From 4f1954eef4f343820dd67933b4f392ebd5cb3b3f Mon Sep 17 00:00:00 2001 From: "chleun.moon" Date: Tue, 23 Aug 2016 17:04:17 +0900 Subject: [PATCH 2/4] Imported Upstream version 1.0.0 Change-Id: I652edb4f76f8f8c30d4ccc8104331c56a4ef7f26 Signed-off-by: cheoleun --- AUTHORS | 1 + COPYING | 22 + ChangeLog | 259 + Dockerfile.android | 109 + INSTALL | 370 + Makefile.am | 43 + Makefile.in | 964 + NEWS | 0 README | 1 + README.rst | 1427 ++ aclocal.m4 | 1613 ++ android-config | 47 + android-make | 33 + compile | 347 + config.guess | 1420 ++ config.h.in | 379 + config.sub | 1799 ++ configure | 25483 +++++++++++++++++++ configure.ac | 758 + contrib/Makefile.am | 44 + contrib/Makefile.in | 531 + contrib/nghttpx-init.in | 173 + contrib/nghttpx-logrotate | 11 + contrib/nghttpx-upstart.conf.in | 8 + contrib/nghttpx.service.in | 10 + depcomp | 791 + doc/Makefile.am | 320 + doc/Makefile.in | 939 + doc/README.rst | 160 + doc/_themes/sphinx_rtd_theme/__init__.py | 17 + doc/_themes/sphinx_rtd_theme/breadcrumbs.html | 23 + doc/_themes/sphinx_rtd_theme/footer.html | 36 + doc/_themes/sphinx_rtd_theme/layout.html | 181 + doc/_themes/sphinx_rtd_theme/layout_old.html | 205 + doc/_themes/sphinx_rtd_theme/search.html | 50 + doc/_themes/sphinx_rtd_theme/searchbox.html | 9 + .../sphinx_rtd_theme/static/css/badge_only.css | 2 + doc/_themes/sphinx_rtd_theme/static/css/theme.css | 5 + .../sphinx_rtd_theme/static/fonts/FontAwesome.otf | Bin 0 -> 62856 bytes .../static/fonts/fontawesome-webfont.eot | Bin 0 -> 38205 bytes .../static/fonts/fontawesome-webfont.svg | 414 + .../static/fonts/fontawesome-webfont.ttf | Bin 0 -> 80652 bytes .../static/fonts/fontawesome-webfont.woff | Bin 0 -> 44432 bytes doc/_themes/sphinx_rtd_theme/static/js/theme.js | 113 + doc/_themes/sphinx_rtd_theme/theme.conf | 9 + doc/_themes/sphinx_rtd_theme/versions.html | 37 + doc/apiref.rst | 104 + doc/asio_http2.h.rst.in | 5 + doc/asio_http2_client.h.rst.in | 5 + doc/asio_http2_server.h.rst.in | 5 + doc/bash_completion/h2load | 19 + doc/bash_completion/nghttp | 19 + doc/bash_completion/nghttpd | 19 + doc/bash_completion/nghttpx | 19 + doc/building-android-binary.rst.in | 1 + doc/conf.py.in | 253 + doc/contribute.rst.in | 1 + doc/enums.rst | 446 + doc/h2load-howto.rst.in | 1 + doc/h2load.1 | 273 + doc/h2load.1.rst | 204 + doc/index.rst.in | 1 + doc/libnghttp2_asio.rst.in | 1 + doc/macros.rst | 86 + doc/mkapiref.py | 277 + doc/nghttp.1 | 289 + doc/nghttp.1.rst | 233 + doc/nghttp2.h.rst.in | 4 + doc/nghttp2_check_header_name.rst | 17 + doc/nghttp2_check_header_value.rst | 15 + doc/nghttp2_hd_deflate_bound.rst | 14 + doc/nghttp2_hd_deflate_change_table_size.rst | 31 + doc/nghttp2_hd_deflate_del.rst | 13 + doc/nghttp2_hd_deflate_hd.rst | 35 + doc/nghttp2_hd_deflate_new.rst | 24 + doc/nghttp2_hd_deflate_new2.rst | 23 + doc/nghttp2_hd_inflate_change_table_size.rst | 23 + doc/nghttp2_hd_inflate_del.rst | 13 + doc/nghttp2_hd_inflate_end_headers.rst | 16 + doc/nghttp2_hd_inflate_hd.rst | 85 + doc/nghttp2_hd_inflate_new.rst | 21 + doc/nghttp2_hd_inflate_new2.rst | 23 + doc/nghttp2_is_fatal.rst | 14 + doc/nghttp2_nv_compare_name.rst | 17 + doc/nghttp2_option_del.rst | 14 + doc/nghttp2_option_new.rst | 22 + doc/nghttp2_option_set_no_auto_window_update.rst | 18 + doc/nghttp2_option_set_no_http_messaging.rst | 18 + doc/nghttp2_option_set_no_recv_client_magic.rst | 26 + ...ttp2_option_set_peer_max_concurrent_streams.rst | 23 + doc/nghttp2_pack_settings_payload.rst | 29 + doc/nghttp2_priority_spec_check_default.rst | 13 + doc/nghttp2_priority_spec_default_init.rst | 15 + doc/nghttp2_priority_spec_init.rst | 18 + doc/nghttp2_select_next_protocol.rst | 66 + doc/nghttp2_session_callbacks_del.rst | 14 + doc/nghttp2_session_callbacks_new.rst | 25 + ...on_callbacks_set_before_frame_send_callback.rst | 13 + ...lbacks_set_data_source_read_length_callback.rst | 14 + ...ssion_callbacks_set_on_begin_frame_callback.rst | 13 + ...ion_callbacks_set_on_begin_headers_callback.rst | 14 + ...n_callbacks_set_on_data_chunk_recv_callback.rst | 14 + ...on_callbacks_set_on_frame_not_send_callback.rst | 14 + ...ession_callbacks_set_on_frame_recv_callback.rst | 14 + ...ession_callbacks_set_on_frame_send_callback.rst | 13 + ...p2_session_callbacks_set_on_header_callback.rst | 14 + ...allbacks_set_on_invalid_frame_recv_callback.rst | 15 + ...sion_callbacks_set_on_stream_close_callback.rst | 13 + ...nghttp2_session_callbacks_set_recv_callback.rst | 16 + ...ssion_callbacks_set_select_padding_callback.rst | 15 + ...nghttp2_session_callbacks_set_send_callback.rst | 16 + ...p2_session_callbacks_set_send_data_callback.rst | 15 + doc/nghttp2_session_client_new.rst | 29 + doc/nghttp2_session_client_new2.rst | 29 + doc/nghttp2_session_client_new3.rst | 29 + doc/nghttp2_session_consume.rst | 31 + doc/nghttp2_session_consume_connection.rst | 24 + doc/nghttp2_session_consume_stream.rst | 26 + doc/nghttp2_session_del.rst | 14 + ...tp2_session_get_effective_local_window_size.rst | 18 + ...ttp2_session_get_effective_recv_data_length.rst | 22 + doc/nghttp2_session_get_last_proc_stream_id.rst | 19 + doc/nghttp2_session_get_next_stream_id.rst | 15 + doc/nghttp2_session_get_outbound_queue_size.rst | 14 + doc/nghttp2_session_get_remote_settings.rst | 15 + doc/nghttp2_session_get_remote_window_size.rst | 15 + ...sion_get_stream_effective_local_window_size.rst | 18 + ...ssion_get_stream_effective_recv_data_length.rst | 22 + doc/nghttp2_session_get_stream_local_close.rst | 14 + doc/nghttp2_session_get_stream_remote_close.rst | 14 + ...http2_session_get_stream_remote_window_size.rst | 22 + doc/nghttp2_session_get_stream_user_data.rst | 20 + doc/nghttp2_session_mem_recv.rst | 42 + doc/nghttp2_session_mem_send.rst | 38 + doc/nghttp2_session_recv.rst | 72 + doc/nghttp2_session_resume_data.rst | 22 + doc/nghttp2_session_send.rst | 55 + doc/nghttp2_session_server_new.rst | 29 + doc/nghttp2_session_server_new2.rst | 29 + doc/nghttp2_session_server_new3.rst | 29 + doc/nghttp2_session_set_next_stream_id.rst | 24 + doc/nghttp2_session_set_stream_user_data.rst | 26 + doc/nghttp2_session_terminate_session.rst | 34 + doc/nghttp2_session_terminate_session2.rst | 34 + doc/nghttp2_session_upgrade.rst | 44 + doc/nghttp2_session_want_read.rst | 18 + doc/nghttp2_session_want_write.rst | 18 + doc/nghttp2_strerror.rst | 14 + doc/nghttp2_submit_data.rst | 42 + doc/nghttp2_submit_goaway.rst | 53 + doc/nghttp2_submit_headers.rst | 80 + doc/nghttp2_submit_ping.rst | 29 + doc/nghttp2_submit_priority.rst | 37 + doc/nghttp2_submit_push_promise.rst | 65 + doc/nghttp2_submit_request.rst | 68 + doc/nghttp2_submit_response.rst | 57 + doc/nghttp2_submit_rst_stream.rst | 27 + doc/nghttp2_submit_settings.rst | 41 + doc/nghttp2_submit_shutdown_notice.rst | 43 + doc/nghttp2_submit_trailer.rst | 52 + doc/nghttp2_submit_window_update.rst | 43 + doc/nghttp2_version.rst | 17 + doc/nghttp2ver.h.rst.in | 4 + doc/nghttpd.1 | 194 + doc/nghttpd.1.rst | 151 + doc/nghttpx-howto.rst.in | 1 + doc/nghttpx.1 | 899 + doc/nghttpx.1.rst | 811 + doc/package_README.rst.in | 1 + doc/programmers-guide.rst | 105 + doc/python-apiref.rst.in | 1 + doc/sources/building-android-binary.rst | 135 + doc/sources/contribute.rst | 57 + doc/sources/h2load-howto.rst | 91 + doc/sources/index.rst | 53 + doc/sources/libnghttp2_asio.rst | 433 + doc/sources/nghttpx-howto.rst | 303 + doc/sources/python-apiref.rst | 437 + doc/sources/tutorial-client.rst | 439 + doc/sources/tutorial-hpack.rst | 118 + doc/sources/tutorial-server.rst | 550 + doc/tutorial-client.rst.in | 6 + doc/tutorial-hpack.rst.in | 6 + doc/tutorial-server.rst.in | 6 + doc/types.rst | 914 + examples/Makefile.am | 95 + examples/Makefile.in | 926 + examples/asio-cl.cc | 96 + examples/asio-cl2.cc | 134 + examples/asio-sv.cc | 149 + examples/asio-sv2.cc | 125 + examples/client.c | 703 + examples/deflate.c | 206 + examples/libevent-client.c | 561 + examples/libevent-server.c | 734 + examples/tiny-nghttpd.c | 1342 + install-sh | 527 + integration-tests/Makefile.am | 43 + integration-tests/Makefile.in | 537 + integration-tests/alt-server.crt | 21 + integration-tests/alt-server.key | 28 + integration-tests/config.go.in | 5 + integration-tests/nghttpx_http1_test.go | 506 + integration-tests/nghttpx_http2_test.go | 813 + integration-tests/nghttpx_spdy_test.go | 232 + integration-tests/server.crt | 21 + integration-tests/server.key | 28 + integration-tests/server_tester.go | 683 + integration-tests/setenv | 6 + integration-tests/setenv.in | 6 + lib/Makefile.am | 67 + lib/Makefile.in | 930 + lib/Makefile.msvc | 249 + lib/includes/Makefile.am | 23 + lib/includes/Makefile.in | 636 + lib/includes/nghttp2/nghttp2.h | 3860 +++ lib/includes/nghttp2/nghttp2ver.h | 42 + lib/includes/nghttp2/nghttp2ver.h.in | 42 + lib/libnghttp2.pc.in | 33 + lib/nghttp2_buf.c | 494 + lib/nghttp2_buf.h | 385 + lib/nghttp2_callbacks.c | 129 + lib/nghttp2_callbacks.h | 112 + lib/nghttp2_frame.c | 888 + lib/nghttp2_frame.h | 527 + lib/nghttp2_hd.c | 2343 ++ lib/nghttp2_hd.h | 421 + lib/nghttp2_hd_huffman.c | 227 + lib/nghttp2_hd_huffman.h | 74 + lib/nghttp2_hd_huffman_data.c | 5152 ++++ lib/nghttp2_helper.c | 463 + lib/nghttp2_helper.h | 114 + lib/nghttp2_http.c | 459 + lib/nghttp2_http.h | 98 + lib/nghttp2_int.h | 58 + lib/nghttp2_map.c | 190 + lib/nghttp2_map.h | 144 + lib/nghttp2_mem.c | 61 + lib/nghttp2_mem.h | 44 + lib/nghttp2_net.h | 91 + lib/nghttp2_npn.c | 57 + lib/nghttp2_npn.h | 34 + lib/nghttp2_option.c | 58 + lib/nghttp2_option.h | 91 + lib/nghttp2_outbound_item.c | 105 + lib/nghttp2_outbound_item.h | 157 + lib/nghttp2_pq.c | 146 + lib/nghttp2_pq.h | 121 + lib/nghttp2_priority_spec.c | 44 + lib/nghttp2_priority_spec.h | 34 + lib/nghttp2_queue.c | 85 + lib/nghttp2_queue.h | 49 + lib/nghttp2_session.c | 6612 +++++ lib/nghttp2_session.h | 762 + lib/nghttp2_stream.c | 1010 + lib/nghttp2_stream.h | 486 + lib/nghttp2_submit.c | 479 + lib/nghttp2_submit.h | 34 + lib/nghttp2_version.c | 38 + ltmain.sh | 9661 +++++++ m4/ax_boost_asio.m4 | 110 + m4/ax_boost_base.m4 | 275 + m4/ax_boost_system.m4 | 120 + m4/ax_boost_thread.m4 | 149 + m4/ax_check_compile_flag.m4 | 74 + m4/ax_cxx_compile_stdcxx_11.m4 | 133 + m4/ax_have_epoll.m4 | 104 + m4/ax_python_devel.m4 | 344 + m4/libtool.m4 | 7997 ++++++ m4/libxml2.m4 | 188 + m4/ltoptions.m4 | 384 + m4/ltsugar.m4 | 123 + m4/ltversion.m4 | 23 + m4/lt~obsolete.m4 | 98 + missing | 215 + nghttpx.conf.sample | 29 + proxy.pac.sample | 6 + python/Makefile.am | 48 + python/Makefile.in | 541 + python/cnghttp2.pxd | 345 + python/nghttp2.pyx | 1591 ++ python/setup.py.in | 49 + src/HtmlParser.cc | 187 + src/HtmlParser.h | 94 + src/HttpServer.cc | 1889 ++ src/HttpServer.h | 202 + src/Makefile.am | 216 + src/Makefile.in | 2137 ++ src/app_helper.cc | 505 + src/app_helper.h | 95 + src/asio_client_request.cc | 67 + src/asio_client_request_impl.cc | 110 + src/asio_client_request_impl.h | 93 + src/asio_client_response.cc | 53 + src/asio_client_response_impl.cc | 57 + src/asio_client_response_impl.h | 69 + src/asio_client_session.cc | 98 + src/asio_client_session_impl.cc | 625 + src/asio_client_session_impl.h | 123 + src/asio_client_session_tcp_impl.cc | 69 + src/asio_client_session_tcp_impl.h | 60 + src/asio_client_session_tls_impl.cc | 85 + src/asio_client_session_tls_impl.h | 63 + src/asio_client_stream.cc | 59 + src/asio_client_stream.h | 68 + src/asio_client_tls_context.cc | 64 + src/asio_client_tls_context.h | 32 + src/asio_common.cc | 148 + src/asio_common.h | 65 + src/asio_io_service_pool.cc | 97 + src/asio_io_service_pool.h | 91 + src/asio_server.cc | 165 + src/asio_server.h | 105 + src/asio_server_connection.h | 169 + src/asio_server_http2.cc | 84 + src/asio_server_http2_handler.cc | 462 + src/asio_server_http2_handler.h | 167 + src/asio_server_http2_impl.cc | 66 + src/asio_server_http2_impl.h | 67 + src/asio_server_request.cc | 55 + src/asio_server_request_handler.cc | 84 + src/asio_server_request_handler.h | 32 + src/asio_server_request_impl.cc | 59 + src/asio_server_request_impl.h | 69 + src/asio_server_response.cc | 75 + src/asio_server_response_impl.cc | 163 + src/asio_server_response_impl.h | 92 + src/asio_server_serve_mux.cc | 138 + src/asio_server_serve_mux.h | 64 + src/asio_server_stream.cc | 55 + src/asio_server_stream.h | 59 + src/asio_server_tls_context.cc | 85 + src/asio_server_tls_context.h | 32 + src/base64.h | 170 + src/buffer.h | 68 + src/buffer_test.cc | 78 + src/buffer_test.h | 38 + src/comp_helper.c | 91 + src/comp_helper.h | 47 + src/deflatehd.cc | 450 + src/h2load.cc | 1528 ++ src/h2load.h | 249 + src/h2load_http2_session.cc | 260 + src/h2load_http2_session.h | 53 + src/h2load_session.h | 57 + src/h2load_spdy_session.cc | 270 + src/h2load_spdy_session.h | 57 + src/http2.cc | 1118 + src/http2.h | 296 + src/http2_test.cc | 826 + src/http2_test.h | 51 + src/includes/Makefile.am | 27 + src/includes/Makefile.in | 640 + src/includes/nghttp2/asio_http2.h | 139 + src/includes/nghttp2/asio_http2_client.h | 195 + src/includes/nghttp2/asio_http2_server.h | 229 + src/inflatehd.cc | 277 + src/libnghttp2_asio.pc.in | 33 + src/memchunk.h | 260 + src/memchunk_test.cc | 199 + src/memchunk_test.h | 42 + src/nghttp.cc | 2652 ++ src/nghttp.h | 272 + src/nghttp2_config.h | 38 + src/nghttp2_gzip.c | 92 + src/nghttp2_gzip.h | 122 + src/nghttp2_gzip_test.c | 115 + src/nghttp2_gzip_test.h | 42 + src/nghttpd.cc | 381 + src/shrpx-unittest.cc | 178 + src/shrpx.cc | 2165 ++ src/shrpx.h | 49 + src/shrpx_accept_handler.cc | 114 + src/shrpx_accept_handler.h | 53 + src/shrpx_client_handler.cc | 793 + src/shrpx_client_handler.h | 154 + src/shrpx_config.cc | 1406 + src/shrpx_config.h | 409 + src/shrpx_config_test.cc | 175 + src/shrpx_config_test.h | 41 + src/shrpx_connect_blocker.cc | 65 + src/shrpx_connect_blocker.h | 56 + src/shrpx_connection.cc | 333 + src/shrpx_connection.h | 109 + src/shrpx_connection_handler.cc | 539 + src/shrpx_connection_handler.h | 138 + src/shrpx_downstream.cc | 1218 + src/shrpx_downstream.h | 456 + src/shrpx_downstream_connection.cc | 52 + src/shrpx_downstream_connection.h | 77 + src/shrpx_downstream_connection_pool.cc | 60 + src/shrpx_downstream_connection_pool.h | 52 + src/shrpx_downstream_queue.cc | 167 + src/shrpx_downstream_queue.h | 105 + src/shrpx_downstream_test.cc | 160 + src/shrpx_downstream_test.h | 44 + src/shrpx_error.h | 43 + src/shrpx_http.cc | 103 + src/shrpx_http.h | 53 + src/shrpx_http2_downstream_connection.cc | 569 + src/shrpx_http2_downstream_connection.h | 86 + src/shrpx_http2_session.cc | 1747 ++ src/shrpx_http2_session.h | 219 + src/shrpx_http2_upstream.cc | 1661 ++ src/shrpx_http2_upstream.h | 121 + src/shrpx_http_downstream_connection.cc | 844 + src/shrpx_http_downstream_connection.h | 78 + src/shrpx_https_upstream.cc | 983 + src/shrpx_https_upstream.h | 91 + src/shrpx_io_control.cc | 66 + src/shrpx_io_control.h | 57 + src/shrpx_log.cc | 343 + src/shrpx_log.h | 151 + src/shrpx_log_config.cc | 69 + src/shrpx_log_config.h | 53 + src/shrpx_rate_limit.cc | 109 + src/shrpx_rate_limit.h | 65 + src/shrpx_spdy_upstream.cc | 1103 + src/shrpx_spdy_upstream.h | 94 + src/shrpx_ssl.cc | 1028 + src/shrpx_ssl.h | 177 + src/shrpx_ssl_test.cc | 119 + src/shrpx_ssl_test.h | 39 + src/shrpx_upstream.h | 69 + src/shrpx_worker.cc | 242 + src/shrpx_worker.h | 142 + src/ssl.cc | 83 + src/ssl.h | 45 + src/template.h | 150 + src/timegm.c | 70 + src/timegm.h | 50 + src/util.cc | 1161 + src/util.h | 626 + src/util_test.cc | 370 + src/util_test.h | 59 + test-driver | 139 + tests/Makefile.am | 82 + tests/Makefile.in | 1253 + tests/failmalloc.c | 77 + tests/failmalloc_test.c | 509 + tests/failmalloc_test.h | 33 + tests/main.c | 368 + tests/malloc_wrapper.c | 75 + tests/malloc_wrapper.h | 61 + tests/nghttp2_buf_test.c | 342 + tests/nghttp2_buf_test.h | 38 + tests/nghttp2_frame_test.c | 561 + tests/nghttp2_frame_test.h | 40 + tests/nghttp2_hd_test.c | 1257 + tests/nghttp2_hd_test.h | 47 + tests/nghttp2_helper_test.c | 169 + tests/nghttp2_helper_test.h | 32 + tests/nghttp2_map_test.c | 176 + tests/nghttp2_map_test.h | 32 + tests/nghttp2_npn_test.c | 72 + tests/nghttp2_npn_test.h | 30 + tests/nghttp2_pq_test.c | 123 + tests/nghttp2_pq_test.h | 31 + tests/nghttp2_queue_test.c | 48 + tests/nghttp2_queue_test.h | 30 + tests/nghttp2_session_test.c | 8125 ++++++ tests/nghttp2_session_test.h | 138 + tests/nghttp2_stream_test.c | 29 + tests/nghttp2_stream_test.h | 28 + tests/nghttp2_test_helper.c | 303 + tests/nghttp2_test_helper.h | 112 + tests/testdata/Makefile.am | 23 + tests/testdata/Makefile.in | 512 + tests/testdata/cacert.pem | 14 + tests/testdata/index.html | 1 + tests/testdata/privkey.pem | 9 + third-party/Makefile.am | 32 + third-party/Makefile.in | 751 + third-party/h2o/fetch-ocsp-response | 150 + third-party/http-parser/http_parser.c | 2418 ++ third-party/http-parser/http_parser.h | 335 + 476 files changed, 171766 insertions(+) create mode 100644 AUTHORS create mode 100644 COPYING create mode 100644 ChangeLog create mode 100644 Dockerfile.android create mode 100644 INSTALL create mode 100644 Makefile.am create mode 100644 Makefile.in create mode 100644 NEWS create mode 100644 README create mode 100644 README.rst create mode 100644 aclocal.m4 create mode 100755 android-config create mode 100755 android-make create mode 100755 compile create mode 100755 config.guess create mode 100644 config.h.in create mode 100755 config.sub create mode 100755 configure create mode 100644 configure.ac create mode 100644 contrib/Makefile.am create mode 100644 contrib/Makefile.in create mode 100644 contrib/nghttpx-init.in create mode 100644 contrib/nghttpx-logrotate create mode 100644 contrib/nghttpx-upstart.conf.in create mode 100644 contrib/nghttpx.service.in create mode 100755 depcomp create mode 100644 doc/Makefile.am create mode 100644 doc/Makefile.in create mode 100644 doc/README.rst create mode 100644 doc/_themes/sphinx_rtd_theme/__init__.py create mode 100644 doc/_themes/sphinx_rtd_theme/breadcrumbs.html create mode 100644 doc/_themes/sphinx_rtd_theme/footer.html create mode 100644 doc/_themes/sphinx_rtd_theme/layout.html create mode 100644 doc/_themes/sphinx_rtd_theme/layout_old.html create mode 100644 doc/_themes/sphinx_rtd_theme/search.html create mode 100644 doc/_themes/sphinx_rtd_theme/searchbox.html create mode 100644 doc/_themes/sphinx_rtd_theme/static/css/badge_only.css create mode 100644 doc/_themes/sphinx_rtd_theme/static/css/theme.css create mode 100644 doc/_themes/sphinx_rtd_theme/static/fonts/FontAwesome.otf create mode 100644 doc/_themes/sphinx_rtd_theme/static/fonts/fontawesome-webfont.eot create mode 100644 doc/_themes/sphinx_rtd_theme/static/fonts/fontawesome-webfont.svg create mode 100644 doc/_themes/sphinx_rtd_theme/static/fonts/fontawesome-webfont.ttf create mode 100644 doc/_themes/sphinx_rtd_theme/static/fonts/fontawesome-webfont.woff create mode 100644 doc/_themes/sphinx_rtd_theme/static/js/theme.js create mode 100644 doc/_themes/sphinx_rtd_theme/theme.conf create mode 100644 doc/_themes/sphinx_rtd_theme/versions.html create mode 100644 doc/apiref.rst create mode 100644 doc/asio_http2.h.rst.in create mode 100644 doc/asio_http2_client.h.rst.in create mode 100644 doc/asio_http2_server.h.rst.in create mode 100644 doc/bash_completion/h2load create mode 100644 doc/bash_completion/nghttp create mode 100644 doc/bash_completion/nghttpd create mode 100644 doc/bash_completion/nghttpx create mode 100644 doc/building-android-binary.rst.in create mode 100644 doc/conf.py.in create mode 100644 doc/contribute.rst.in create mode 100644 doc/enums.rst create mode 100644 doc/h2load-howto.rst.in create mode 100644 doc/h2load.1 create mode 100644 doc/h2load.1.rst create mode 100644 doc/index.rst.in create mode 100644 doc/libnghttp2_asio.rst.in create mode 100644 doc/macros.rst create mode 100755 doc/mkapiref.py create mode 100644 doc/nghttp.1 create mode 100644 doc/nghttp.1.rst create mode 100644 doc/nghttp2.h.rst.in create mode 100644 doc/nghttp2_check_header_name.rst create mode 100644 doc/nghttp2_check_header_value.rst create mode 100644 doc/nghttp2_hd_deflate_bound.rst create mode 100644 doc/nghttp2_hd_deflate_change_table_size.rst create mode 100644 doc/nghttp2_hd_deflate_del.rst create mode 100644 doc/nghttp2_hd_deflate_hd.rst create mode 100644 doc/nghttp2_hd_deflate_new.rst create mode 100644 doc/nghttp2_hd_deflate_new2.rst create mode 100644 doc/nghttp2_hd_inflate_change_table_size.rst create mode 100644 doc/nghttp2_hd_inflate_del.rst create mode 100644 doc/nghttp2_hd_inflate_end_headers.rst create mode 100644 doc/nghttp2_hd_inflate_hd.rst create mode 100644 doc/nghttp2_hd_inflate_new.rst create mode 100644 doc/nghttp2_hd_inflate_new2.rst create mode 100644 doc/nghttp2_is_fatal.rst create mode 100644 doc/nghttp2_nv_compare_name.rst create mode 100644 doc/nghttp2_option_del.rst create mode 100644 doc/nghttp2_option_new.rst create mode 100644 doc/nghttp2_option_set_no_auto_window_update.rst create mode 100644 doc/nghttp2_option_set_no_http_messaging.rst create mode 100644 doc/nghttp2_option_set_no_recv_client_magic.rst create mode 100644 doc/nghttp2_option_set_peer_max_concurrent_streams.rst create mode 100644 doc/nghttp2_pack_settings_payload.rst create mode 100644 doc/nghttp2_priority_spec_check_default.rst create mode 100644 doc/nghttp2_priority_spec_default_init.rst create mode 100644 doc/nghttp2_priority_spec_init.rst create mode 100644 doc/nghttp2_select_next_protocol.rst create mode 100644 doc/nghttp2_session_callbacks_del.rst create mode 100644 doc/nghttp2_session_callbacks_new.rst create mode 100644 doc/nghttp2_session_callbacks_set_before_frame_send_callback.rst create mode 100644 doc/nghttp2_session_callbacks_set_data_source_read_length_callback.rst create mode 100644 doc/nghttp2_session_callbacks_set_on_begin_frame_callback.rst create mode 100644 doc/nghttp2_session_callbacks_set_on_begin_headers_callback.rst create mode 100644 doc/nghttp2_session_callbacks_set_on_data_chunk_recv_callback.rst create mode 100644 doc/nghttp2_session_callbacks_set_on_frame_not_send_callback.rst create mode 100644 doc/nghttp2_session_callbacks_set_on_frame_recv_callback.rst create mode 100644 doc/nghttp2_session_callbacks_set_on_frame_send_callback.rst create mode 100644 doc/nghttp2_session_callbacks_set_on_header_callback.rst create mode 100644 doc/nghttp2_session_callbacks_set_on_invalid_frame_recv_callback.rst create mode 100644 doc/nghttp2_session_callbacks_set_on_stream_close_callback.rst create mode 100644 doc/nghttp2_session_callbacks_set_recv_callback.rst create mode 100644 doc/nghttp2_session_callbacks_set_select_padding_callback.rst create mode 100644 doc/nghttp2_session_callbacks_set_send_callback.rst create mode 100644 doc/nghttp2_session_callbacks_set_send_data_callback.rst create mode 100644 doc/nghttp2_session_client_new.rst create mode 100644 doc/nghttp2_session_client_new2.rst create mode 100644 doc/nghttp2_session_client_new3.rst create mode 100644 doc/nghttp2_session_consume.rst create mode 100644 doc/nghttp2_session_consume_connection.rst create mode 100644 doc/nghttp2_session_consume_stream.rst create mode 100644 doc/nghttp2_session_del.rst create mode 100644 doc/nghttp2_session_get_effective_local_window_size.rst create mode 100644 doc/nghttp2_session_get_effective_recv_data_length.rst create mode 100644 doc/nghttp2_session_get_last_proc_stream_id.rst create mode 100644 doc/nghttp2_session_get_next_stream_id.rst create mode 100644 doc/nghttp2_session_get_outbound_queue_size.rst create mode 100644 doc/nghttp2_session_get_remote_settings.rst create mode 100644 doc/nghttp2_session_get_remote_window_size.rst create mode 100644 doc/nghttp2_session_get_stream_effective_local_window_size.rst create mode 100644 doc/nghttp2_session_get_stream_effective_recv_data_length.rst create mode 100644 doc/nghttp2_session_get_stream_local_close.rst create mode 100644 doc/nghttp2_session_get_stream_remote_close.rst create mode 100644 doc/nghttp2_session_get_stream_remote_window_size.rst create mode 100644 doc/nghttp2_session_get_stream_user_data.rst create mode 100644 doc/nghttp2_session_mem_recv.rst create mode 100644 doc/nghttp2_session_mem_send.rst create mode 100644 doc/nghttp2_session_recv.rst create mode 100644 doc/nghttp2_session_resume_data.rst create mode 100644 doc/nghttp2_session_send.rst create mode 100644 doc/nghttp2_session_server_new.rst create mode 100644 doc/nghttp2_session_server_new2.rst create mode 100644 doc/nghttp2_session_server_new3.rst create mode 100644 doc/nghttp2_session_set_next_stream_id.rst create mode 100644 doc/nghttp2_session_set_stream_user_data.rst create mode 100644 doc/nghttp2_session_terminate_session.rst create mode 100644 doc/nghttp2_session_terminate_session2.rst create mode 100644 doc/nghttp2_session_upgrade.rst create mode 100644 doc/nghttp2_session_want_read.rst create mode 100644 doc/nghttp2_session_want_write.rst create mode 100644 doc/nghttp2_strerror.rst create mode 100644 doc/nghttp2_submit_data.rst create mode 100644 doc/nghttp2_submit_goaway.rst create mode 100644 doc/nghttp2_submit_headers.rst create mode 100644 doc/nghttp2_submit_ping.rst create mode 100644 doc/nghttp2_submit_priority.rst create mode 100644 doc/nghttp2_submit_push_promise.rst create mode 100644 doc/nghttp2_submit_request.rst create mode 100644 doc/nghttp2_submit_response.rst create mode 100644 doc/nghttp2_submit_rst_stream.rst create mode 100644 doc/nghttp2_submit_settings.rst create mode 100644 doc/nghttp2_submit_shutdown_notice.rst create mode 100644 doc/nghttp2_submit_trailer.rst create mode 100644 doc/nghttp2_submit_window_update.rst create mode 100644 doc/nghttp2_version.rst create mode 100644 doc/nghttp2ver.h.rst.in create mode 100644 doc/nghttpd.1 create mode 100644 doc/nghttpd.1.rst create mode 100644 doc/nghttpx-howto.rst.in create mode 100644 doc/nghttpx.1 create mode 100644 doc/nghttpx.1.rst create mode 100644 doc/package_README.rst.in create mode 100644 doc/programmers-guide.rst create mode 100644 doc/python-apiref.rst.in create mode 100644 doc/sources/building-android-binary.rst create mode 100644 doc/sources/contribute.rst create mode 100644 doc/sources/h2load-howto.rst create mode 100644 doc/sources/index.rst create mode 100644 doc/sources/libnghttp2_asio.rst create mode 100644 doc/sources/nghttpx-howto.rst create mode 100644 doc/sources/python-apiref.rst create mode 100644 doc/sources/tutorial-client.rst create mode 100644 doc/sources/tutorial-hpack.rst create mode 100644 doc/sources/tutorial-server.rst create mode 100644 doc/tutorial-client.rst.in create mode 100644 doc/tutorial-hpack.rst.in create mode 100644 doc/tutorial-server.rst.in create mode 100644 doc/types.rst create mode 100644 examples/Makefile.am create mode 100644 examples/Makefile.in create mode 100644 examples/asio-cl.cc create mode 100644 examples/asio-cl2.cc create mode 100644 examples/asio-sv.cc create mode 100644 examples/asio-sv2.cc create mode 100644 examples/client.c create mode 100644 examples/deflate.c create mode 100644 examples/libevent-client.c create mode 100644 examples/libevent-server.c create mode 100644 examples/tiny-nghttpd.c create mode 100755 install-sh create mode 100644 integration-tests/Makefile.am create mode 100644 integration-tests/Makefile.in create mode 100644 integration-tests/alt-server.crt create mode 100644 integration-tests/alt-server.key create mode 100644 integration-tests/config.go.in create mode 100644 integration-tests/nghttpx_http1_test.go create mode 100644 integration-tests/nghttpx_http2_test.go create mode 100644 integration-tests/nghttpx_spdy_test.go create mode 100644 integration-tests/server.crt create mode 100644 integration-tests/server.key create mode 100644 integration-tests/server_tester.go create mode 100644 integration-tests/setenv create mode 100644 integration-tests/setenv.in create mode 100644 lib/Makefile.am create mode 100644 lib/Makefile.in create mode 100644 lib/Makefile.msvc create mode 100644 lib/includes/Makefile.am create mode 100644 lib/includes/Makefile.in create mode 100644 lib/includes/nghttp2/nghttp2.h create mode 100644 lib/includes/nghttp2/nghttp2ver.h create mode 100644 lib/includes/nghttp2/nghttp2ver.h.in create mode 100644 lib/libnghttp2.pc.in create mode 100644 lib/nghttp2_buf.c create mode 100644 lib/nghttp2_buf.h create mode 100644 lib/nghttp2_callbacks.c create mode 100644 lib/nghttp2_callbacks.h create mode 100644 lib/nghttp2_frame.c create mode 100644 lib/nghttp2_frame.h create mode 100644 lib/nghttp2_hd.c create mode 100644 lib/nghttp2_hd.h create mode 100644 lib/nghttp2_hd_huffman.c create mode 100644 lib/nghttp2_hd_huffman.h create mode 100644 lib/nghttp2_hd_huffman_data.c create mode 100644 lib/nghttp2_helper.c create mode 100644 lib/nghttp2_helper.h create mode 100644 lib/nghttp2_http.c create mode 100644 lib/nghttp2_http.h create mode 100644 lib/nghttp2_int.h create mode 100644 lib/nghttp2_map.c create mode 100644 lib/nghttp2_map.h create mode 100644 lib/nghttp2_mem.c create mode 100644 lib/nghttp2_mem.h create mode 100644 lib/nghttp2_net.h create mode 100644 lib/nghttp2_npn.c create mode 100644 lib/nghttp2_npn.h create mode 100644 lib/nghttp2_option.c create mode 100644 lib/nghttp2_option.h create mode 100644 lib/nghttp2_outbound_item.c create mode 100644 lib/nghttp2_outbound_item.h create mode 100644 lib/nghttp2_pq.c create mode 100644 lib/nghttp2_pq.h create mode 100644 lib/nghttp2_priority_spec.c create mode 100644 lib/nghttp2_priority_spec.h create mode 100644 lib/nghttp2_queue.c create mode 100644 lib/nghttp2_queue.h create mode 100644 lib/nghttp2_session.c create mode 100644 lib/nghttp2_session.h create mode 100644 lib/nghttp2_stream.c create mode 100644 lib/nghttp2_stream.h create mode 100644 lib/nghttp2_submit.c create mode 100644 lib/nghttp2_submit.h create mode 100644 lib/nghttp2_version.c create mode 100644 ltmain.sh create mode 100644 m4/ax_boost_asio.m4 create mode 100644 m4/ax_boost_base.m4 create mode 100644 m4/ax_boost_system.m4 create mode 100644 m4/ax_boost_thread.m4 create mode 100644 m4/ax_check_compile_flag.m4 create mode 100644 m4/ax_cxx_compile_stdcxx_11.m4 create mode 100644 m4/ax_have_epoll.m4 create mode 100644 m4/ax_python_devel.m4 create mode 100644 m4/libtool.m4 create mode 100644 m4/libxml2.m4 create mode 100644 m4/ltoptions.m4 create mode 100644 m4/ltsugar.m4 create mode 100644 m4/ltversion.m4 create mode 100644 m4/lt~obsolete.m4 create mode 100755 missing create mode 100644 nghttpx.conf.sample create mode 100644 proxy.pac.sample create mode 100644 python/Makefile.am create mode 100644 python/Makefile.in create mode 100644 python/cnghttp2.pxd create mode 100644 python/nghttp2.pyx create mode 100644 python/setup.py.in create mode 100644 src/HtmlParser.cc create mode 100644 src/HtmlParser.h create mode 100644 src/HttpServer.cc create mode 100644 src/HttpServer.h create mode 100644 src/Makefile.am create mode 100644 src/Makefile.in create mode 100644 src/app_helper.cc create mode 100644 src/app_helper.h create mode 100644 src/asio_client_request.cc create mode 100644 src/asio_client_request_impl.cc create mode 100644 src/asio_client_request_impl.h create mode 100644 src/asio_client_response.cc create mode 100644 src/asio_client_response_impl.cc create mode 100644 src/asio_client_response_impl.h create mode 100644 src/asio_client_session.cc create mode 100644 src/asio_client_session_impl.cc create mode 100644 src/asio_client_session_impl.h create mode 100644 src/asio_client_session_tcp_impl.cc create mode 100644 src/asio_client_session_tcp_impl.h create mode 100644 src/asio_client_session_tls_impl.cc create mode 100644 src/asio_client_session_tls_impl.h create mode 100644 src/asio_client_stream.cc create mode 100644 src/asio_client_stream.h create mode 100644 src/asio_client_tls_context.cc create mode 100644 src/asio_client_tls_context.h create mode 100644 src/asio_common.cc create mode 100644 src/asio_common.h create mode 100644 src/asio_io_service_pool.cc create mode 100644 src/asio_io_service_pool.h create mode 100644 src/asio_server.cc create mode 100644 src/asio_server.h create mode 100644 src/asio_server_connection.h create mode 100644 src/asio_server_http2.cc create mode 100644 src/asio_server_http2_handler.cc create mode 100644 src/asio_server_http2_handler.h create mode 100644 src/asio_server_http2_impl.cc create mode 100644 src/asio_server_http2_impl.h create mode 100644 src/asio_server_request.cc create mode 100644 src/asio_server_request_handler.cc create mode 100644 src/asio_server_request_handler.h create mode 100644 src/asio_server_request_impl.cc create mode 100644 src/asio_server_request_impl.h create mode 100644 src/asio_server_response.cc create mode 100644 src/asio_server_response_impl.cc create mode 100644 src/asio_server_response_impl.h create mode 100644 src/asio_server_serve_mux.cc create mode 100644 src/asio_server_serve_mux.h create mode 100644 src/asio_server_stream.cc create mode 100644 src/asio_server_stream.h create mode 100644 src/asio_server_tls_context.cc create mode 100644 src/asio_server_tls_context.h create mode 100644 src/base64.h create mode 100644 src/buffer.h create mode 100644 src/buffer_test.cc create mode 100644 src/buffer_test.h create mode 100644 src/comp_helper.c create mode 100644 src/comp_helper.h create mode 100644 src/deflatehd.cc create mode 100644 src/h2load.cc create mode 100644 src/h2load.h create mode 100644 src/h2load_http2_session.cc create mode 100644 src/h2load_http2_session.h create mode 100644 src/h2load_session.h create mode 100644 src/h2load_spdy_session.cc create mode 100644 src/h2load_spdy_session.h create mode 100644 src/http2.cc create mode 100644 src/http2.h create mode 100644 src/http2_test.cc create mode 100644 src/http2_test.h create mode 100644 src/includes/Makefile.am create mode 100644 src/includes/Makefile.in create mode 100644 src/includes/nghttp2/asio_http2.h create mode 100644 src/includes/nghttp2/asio_http2_client.h create mode 100644 src/includes/nghttp2/asio_http2_server.h create mode 100644 src/inflatehd.cc create mode 100644 src/libnghttp2_asio.pc.in create mode 100644 src/memchunk.h create mode 100644 src/memchunk_test.cc create mode 100644 src/memchunk_test.h create mode 100644 src/nghttp.cc create mode 100644 src/nghttp.h create mode 100644 src/nghttp2_config.h create mode 100644 src/nghttp2_gzip.c create mode 100644 src/nghttp2_gzip.h create mode 100644 src/nghttp2_gzip_test.c create mode 100644 src/nghttp2_gzip_test.h create mode 100644 src/nghttpd.cc create mode 100644 src/shrpx-unittest.cc create mode 100644 src/shrpx.cc create mode 100644 src/shrpx.h create mode 100644 src/shrpx_accept_handler.cc create mode 100644 src/shrpx_accept_handler.h create mode 100644 src/shrpx_client_handler.cc create mode 100644 src/shrpx_client_handler.h create mode 100644 src/shrpx_config.cc create mode 100644 src/shrpx_config.h create mode 100644 src/shrpx_config_test.cc create mode 100644 src/shrpx_config_test.h create mode 100644 src/shrpx_connect_blocker.cc create mode 100644 src/shrpx_connect_blocker.h create mode 100644 src/shrpx_connection.cc create mode 100644 src/shrpx_connection.h create mode 100644 src/shrpx_connection_handler.cc create mode 100644 src/shrpx_connection_handler.h create mode 100644 src/shrpx_downstream.cc create mode 100644 src/shrpx_downstream.h create mode 100644 src/shrpx_downstream_connection.cc create mode 100644 src/shrpx_downstream_connection.h create mode 100644 src/shrpx_downstream_connection_pool.cc create mode 100644 src/shrpx_downstream_connection_pool.h create mode 100644 src/shrpx_downstream_queue.cc create mode 100644 src/shrpx_downstream_queue.h create mode 100644 src/shrpx_downstream_test.cc create mode 100644 src/shrpx_downstream_test.h create mode 100644 src/shrpx_error.h create mode 100644 src/shrpx_http.cc create mode 100644 src/shrpx_http.h create mode 100644 src/shrpx_http2_downstream_connection.cc create mode 100644 src/shrpx_http2_downstream_connection.h create mode 100644 src/shrpx_http2_session.cc create mode 100644 src/shrpx_http2_session.h create mode 100644 src/shrpx_http2_upstream.cc create mode 100644 src/shrpx_http2_upstream.h create mode 100644 src/shrpx_http_downstream_connection.cc create mode 100644 src/shrpx_http_downstream_connection.h create mode 100644 src/shrpx_https_upstream.cc create mode 100644 src/shrpx_https_upstream.h create mode 100644 src/shrpx_io_control.cc create mode 100644 src/shrpx_io_control.h create mode 100644 src/shrpx_log.cc create mode 100644 src/shrpx_log.h create mode 100644 src/shrpx_log_config.cc create mode 100644 src/shrpx_log_config.h create mode 100644 src/shrpx_rate_limit.cc create mode 100644 src/shrpx_rate_limit.h create mode 100644 src/shrpx_spdy_upstream.cc create mode 100644 src/shrpx_spdy_upstream.h create mode 100644 src/shrpx_ssl.cc create mode 100644 src/shrpx_ssl.h create mode 100644 src/shrpx_ssl_test.cc create mode 100644 src/shrpx_ssl_test.h create mode 100644 src/shrpx_upstream.h create mode 100644 src/shrpx_worker.cc create mode 100644 src/shrpx_worker.h create mode 100644 src/ssl.cc create mode 100644 src/ssl.h create mode 100644 src/template.h create mode 100644 src/timegm.c create mode 100644 src/timegm.h create mode 100644 src/util.cc create mode 100644 src/util.h create mode 100644 src/util_test.cc create mode 100644 src/util_test.h create mode 100755 test-driver create mode 100644 tests/Makefile.am create mode 100644 tests/Makefile.in create mode 100644 tests/failmalloc.c create mode 100644 tests/failmalloc_test.c create mode 100644 tests/failmalloc_test.h create mode 100644 tests/main.c create mode 100644 tests/malloc_wrapper.c create mode 100644 tests/malloc_wrapper.h create mode 100644 tests/nghttp2_buf_test.c create mode 100644 tests/nghttp2_buf_test.h create mode 100644 tests/nghttp2_frame_test.c create mode 100644 tests/nghttp2_frame_test.h create mode 100644 tests/nghttp2_hd_test.c create mode 100644 tests/nghttp2_hd_test.h create mode 100644 tests/nghttp2_helper_test.c create mode 100644 tests/nghttp2_helper_test.h create mode 100644 tests/nghttp2_map_test.c create mode 100644 tests/nghttp2_map_test.h create mode 100644 tests/nghttp2_npn_test.c create mode 100644 tests/nghttp2_npn_test.h create mode 100644 tests/nghttp2_pq_test.c create mode 100644 tests/nghttp2_pq_test.h create mode 100644 tests/nghttp2_queue_test.c create mode 100644 tests/nghttp2_queue_test.h create mode 100644 tests/nghttp2_session_test.c create mode 100644 tests/nghttp2_session_test.h create mode 100644 tests/nghttp2_stream_test.c create mode 100644 tests/nghttp2_stream_test.h create mode 100644 tests/nghttp2_test_helper.c create mode 100644 tests/nghttp2_test_helper.h create mode 100644 tests/testdata/Makefile.am create mode 100644 tests/testdata/Makefile.in create mode 100644 tests/testdata/cacert.pem create mode 100644 tests/testdata/index.html create mode 100644 tests/testdata/privkey.pem create mode 100644 third-party/Makefile.am create mode 100644 third-party/Makefile.in create mode 100755 third-party/h2o/fetch-ocsp-response create mode 100644 third-party/http-parser/http_parser.c create mode 100644 third-party/http-parser/http_parser.h diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 0000000..95bc954 --- /dev/null +++ b/AUTHORS @@ -0,0 +1 @@ +Tatsuhiro Tsujikawa diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..a43e837 --- /dev/null +++ b/COPYING @@ -0,0 +1,22 @@ +The MIT License + +Copyright (c) 2012, 2014, 2015 Tatsuhiro Tsujikawa + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 0000000..9711d5d --- /dev/null +++ b/ChangeLog @@ -0,0 +1,259 @@ +commit 553d741f036b3b5f47b0f296e4663fe1de8af6ec +Author: Tatsuhiro Tsujikawa +AuthorDate: 2015-05-16 +Commit: Tatsuhiro Tsujikawa +CommitDate: 2015-05-16 + + Fix migration version number + +commit 6bd728b3c2a6faccad3e4f69c75ca792f3a39e85 +Author: Tatsuhiro Tsujikawa +AuthorDate: 2015-05-16 +Commit: Tatsuhiro Tsujikawa +CommitDate: 2015-05-16 + + Update man pages + +commit a99085891a9378165f68211225d94e3008d742f5 +Author: Tatsuhiro Tsujikawa +AuthorDate: 2015-05-16 +Commit: Tatsuhiro Tsujikawa +CommitDate: 2015-05-16 + + Bump up version number to 1.0.0, LT revision to 14:0:0 + +commit 68d3724fad57db135015de05bdd169706c9e6983 +Author: Tatsuhiro Tsujikawa +AuthorDate: 2015-05-15 +Commit: Tatsuhiro Tsujikawa +CommitDate: 2015-05-15 + + Update README.rst + +commit fe39ec869733920f3f79e9ebdeb7850fbc9ffd2b +Author: Tatsuhiro Tsujikawa +AuthorDate: 2015-05-15 +Commit: Tatsuhiro Tsujikawa +CommitDate: 2015-05-15 + + doc: Update Resources + +commit c896118747e298aaed4ddc86da1169c02bdfc734 +Author: Tatsuhiro Tsujikawa +AuthorDate: 2015-05-15 +Commit: Tatsuhiro Tsujikawa +CommitDate: 2015-05-15 + + Fix required spdylay version + +commit b89140c311e7a3438d29637b44dea09bf20c8e79 +Merge: 92a20c7 a869c39 +Author: Tatsuhiro Tsujikawa +AuthorDate: 2015-05-15 +Commit: Tatsuhiro Tsujikawa +CommitDate: 2015-05-15 + + Merge branch 'v1.0.0' + +commit a869c39a2c2c7ec0db4e8db477b2dfed5f3eaa0f +Author: Tatsuhiro Tsujikawa +AuthorDate: 2015-05-15 +Commit: Tatsuhiro Tsujikawa +CommitDate: 2015-05-15 + + Bump up version number to 1.0.0-DEV + +commit 0b27f005e0c23931a82e612b024f3ace66546924 +Merge: 64b1aae 92a20c7 +Author: Tatsuhiro Tsujikawa +AuthorDate: 2015-05-15 +Commit: Tatsuhiro Tsujikawa +CommitDate: 2015-05-15 + + Merge branch 'master' into v1.0.0 + + Conflicts: + src/HttpServer.cc + +commit 64b1aae56748c7b6816409aee649817ee56aa1e8 +Author: Tatsuhiro Tsujikawa +AuthorDate: 2015-05-08 +Commit: Tatsuhiro Tsujikawa +CommitDate: 2015-05-08 + + integration: Fix TestH2H1Upgrade test failure + +commit e63d6e490aa9fab1cf8240bfd5c3133df241ced0 +Merge: de47350 7f60de0 +Author: Tatsuhiro Tsujikawa +AuthorDate: 2015-05-08 +Commit: Tatsuhiro Tsujikawa +CommitDate: 2015-05-08 + + Merge branch 'master' into v1.0.0 + + Conflicts: + lib/nghttp2_option.h + lib/nghttp2_session.h + src/HttpServer.cc + +commit de4735092a2970f9f5d93de617dc0faf2db9952d +Author: Tatsuhiro Tsujikawa +AuthorDate: 2015-04-28 +Commit: Tatsuhiro Tsujikawa +CommitDate: 2015-04-28 + + Fix doc formatting + +commit 1c4df1832b179802052b9785001381855e03981c +Author: Tatsuhiro Tsujikawa +AuthorDate: 2015-04-28 +Commit: Tatsuhiro Tsujikawa +CommitDate: 2015-04-28 + + Update doc, mainly for RFC numbers + +commit 1ad1fe600574e6fb5760b5ea66ac69472bf5e4d5 +Merge: db4a684 f05a483 +Author: Tatsuhiro Tsujikawa +AuthorDate: 2015-04-28 +Commit: Tatsuhiro Tsujikawa +CommitDate: 2015-04-28 + + Merge branch 'master' into v1.0.0 + +commit db4a68454a07d304ed92cfed9df8401b6b89196f +Merge: 5937b4b 6b0b8ea +Author: Tatsuhiro Tsujikawa +AuthorDate: 2015-04-24 +Commit: Tatsuhiro Tsujikawa +CommitDate: 2015-04-24 + + Merge branch 'master' into v1.0.0 + + Conflicts: + lib/includes/nghttp2/nghttp2.h + +commit 5937b4b6f73380e8a0c22932c118e265de2c30c3 +Merge: 90bfea7 787d401 +Author: Tatsuhiro Tsujikawa +AuthorDate: 2015-04-19 +Commit: Tatsuhiro Tsujikawa +CommitDate: 2015-04-19 + + Merge branch 'master' into v1.0.0 + +commit 90bfea77e098d54429445ee8cf8eee6854ccc767 +Author: Tatsuhiro Tsujikawa +AuthorDate: 2015-04-08 +Commit: Tatsuhiro Tsujikawa +CommitDate: 2015-04-08 + + doc: Remove nghttp2_submit_altsvc.rst + +commit cf0576253f93a096ea79610f5ef6349fa3e6ac9f +Merge: 59e3783 4aca2f0 +Author: Tatsuhiro Tsujikawa +AuthorDate: 2015-04-08 +Commit: Tatsuhiro Tsujikawa +CommitDate: 2015-04-08 + + Merge branch 'master' into v1.0.0 + +commit 59e3783f3f34cf8663b51fd98fdae14993aa1cb8 +Author: Tatsuhiro Tsujikawa +AuthorDate: 2015-04-07 +Commit: Tatsuhiro Tsujikawa +CommitDate: 2015-04-07 + + Update manual entry + +commit 084e4487edee1a0cd9c503a074519888ab347af4 +Author: Tatsuhiro Tsujikawa +AuthorDate: 2015-04-05 +Commit: Tatsuhiro Tsujikawa +CommitDate: 2015-04-05 + + Add migration section + +commit 24897aa50decdde2bf360f5805bc2cfb722f4cc8 +Author: Tatsuhiro Tsujikawa +AuthorDate: 2015-04-05 +Commit: Tatsuhiro Tsujikawa +CommitDate: 2015-04-05 + + Update README.rst + +commit 3e50ef439d914dbf6c611fc4afc8925c9e79d6c7 +Author: Tatsuhiro Tsujikawa +AuthorDate: 2015-04-05 +Commit: Tatsuhiro Tsujikawa +CommitDate: 2015-04-05 + + Announce h2, final HTTP/2 ALPN identifier + +commit 87602e5d7296b37ed3f2864db1965d139657aba6 +Author: Tatsuhiro Tsujikawa +AuthorDate: 2015-04-05 +Commit: Tatsuhiro Tsujikawa +CommitDate: 2015-04-05 + + Use NGHTTP2_PROTOCOL_ERROR for NGHTTP2_ERR_HTTP_{HEADER,MESSAGING} + +commit d0c27d52296a0adb6f5614cb58408c8617f39aab +Author: Tatsuhiro Tsujikawa +AuthorDate: 2015-04-05 +Commit: Tatsuhiro Tsujikawa +CommitDate: 2015-04-05 + + Send 24 bytes client magic byte string by library + + Previously nghttp2_session_send() and nghttp2_session_mem_send() did + not send 24 bytes client magic byte string (MAGIC). We made + nghttp2_session_recv() and nghttp2_session_mem_recv() process MAGIC by + default, so it is natural to make library send MAGIC as well. This + commit makes nghttp2_session_send() and nghttp2_session_mem_send() + send MAGIC. This commit also replace "connection preface" with + "client magic", since we call MAGIC as "connection preface" but it is + just a part of connection preface. NGHTTP2_CLIENT_CONNECTION_PREFACE + macro was replaced with NGHTTP2_CLIENT_MAGIC. The already deprecated + NGHTTP2_CLIENT_CONNECTION_HEADER macro was removed permanently. + nghttp2_option_set_no_recv_client_preface() was renamed as + nghttp2_option_set_no_recv_client_magic(). NGHTTP2_ERR_BAD_PREFACE + was renamed as NGHTTP2_ERR_BAD_CLIENT_MAGIC. + +commit ebf214c8fc45ea96f60a5edb7737b96f1d59e3ee +Author: Tatsuhiro Tsujikawa +AuthorDate: 2015-03-23 +Commit: Tatsuhiro Tsujikawa +CommitDate: 2015-04-05 + + nghttp2_on_invalid_frame_recv_callback should have lib_error_code as param + + nghttp2_error_code is HTTP/2 standard error code and is too coarse to + know what's going on. + +commit 250ea53e4b968bfc2a3dd0e301a1878fdfa41efb +Author: Tatsuhiro Tsujikawa +AuthorDate: 2015-03-23 +Commit: Tatsuhiro Tsujikawa +CommitDate: 2015-04-05 + + Deal with 24 bytes client connection preface by default + + Since HTTP/2 spec requires for client to send connection preface, it + is reasonable to make this option enabled by default. It is still a + use case to disable this, so replace this option with + nghttp2_option_set_no_recv_client_preface(). + +commit 01af6ea70c466b8ace7f22c25737418babddb8b5 +Author: Tatsuhiro Tsujikawa +AuthorDate: 2015-03-22 +Commit: Tatsuhiro Tsujikawa +CommitDate: 2015-04-05 + + Remove ALTSVC related code + + HTTP/2 and HPACK are going to be published as RFC, but ALTSVC is still + in draft state. To make our API stable, it would be better to remove + ALTSVC API for 1.0.0 release. diff --git a/Dockerfile.android b/Dockerfile.android new file mode 100644 index 0000000..87990f0 --- /dev/null +++ b/Dockerfile.android @@ -0,0 +1,109 @@ +# vim: ft=dockerfile: +# Dockerfile to build nghttp2 android binary +# +# $ sudo docker build -t nghttp2-android - < Dockerfile.android +# +# After successful build, android binaries are located under +# /root/build/nghttp2. You can copy the binary using docker cp. For +# example, to copy nghttpx binary to host file system location +# /path/to/dest, do this: +# +# $ sudo docker run -v /path/to/dest:/out nghttp2-android cp /root/build/nghttp2/src/nghttpx /out + +FROM ubuntu:trusty + +MAINTAINER Tatsuhiro Tsujikawa + +ENV ANDROID_HOME /root/android +ENV PREFIX $ANDROID_HOME/usr/local +ENV TOOLCHAIN $ANDROID_HOME/toolchain +ENV PATH $TOOLCHAIN/bin:$PATH + +# It would be better to use nearest ubuntu archive mirror for faster +# downloads. +# RUN sed -ie 's/archive\.ubuntu/jp.archive.ubuntu/g' /etc/apt/sources.list + +RUN apt-get update +# genisoimage, libc6-i386 and lib32stdc++6 are required to decompress ndk. +RUN apt-get install -y make binutils autoconf automake autotools-dev libtool \ + pkg-config git curl dpkg-dev libxml2-dev \ + genisoimage libc6-i386 lib32stdc++6 + +WORKDIR /root/build +RUN curl -L -O http://dl.google.com/android/ndk/android-ndk-r10c-linux-x86_64.bin && \ + chmod a+x android-ndk-r10c-linux-x86_64.bin && \ + ./android-ndk-r10c-linux-x86_64.bin && \ + rm android-ndk-r10c-linux-x86_64.bin + +WORKDIR /root/build/android-ndk-r10c +RUN /bin/bash build/tools/make-standalone-toolchain.sh \ + --install-dir=$ANDROID_HOME/toolchain \ + --toolchain=arm-linux-androideabi-4.9 --llvm-version=3.5 \ + --system=linux-x86_64 + +WORKDIR /root/build +RUN git clone https://github.com/tatsuhiro-t/spdylay +WORKDIR /root/build/spdylay +RUN autoreconf -i && \ + ./configure \ + --disable-shared \ + --host=arm-linux-androideabi \ + --build=`dpkg-architecture -qDEB_BUILD_GNU_TYPE` \ + --prefix=$PREFIX \ + --without-libxml2 \ + --disable-src \ + --disable-examples \ + CPPFLAGS="-I$PREFIX/include" \ + PKG_CONFIG_LIBDIR="$PREFIX/lib/pkgconfig" \ + LDFLAGS="-L$PREFIX/lib" && \ + make install + +WORKDIR /root/build +RUN curl -L -O https://www.openssl.org/source/openssl-1.0.2a.tar.gz && \ + tar xf openssl-1.0.2a.tar.gz && \ + rm openssl-1.0.2a.tar.gz + +WORKDIR /root/build/openssl-1.0.2a +RUN export CROSS_COMPILE=$TOOLCHAIN/bin/arm-linux-androideabi- && \ + ./Configure --prefix=$PREFIX android && \ + make && make install_sw + +WORKDIR /root/build +RUN curl -L -O http://dist.schmorp.de/libev/libev-4.19.tar.gz && \ + curl -L -O https://gist.github.com/tatsuhiro-t/48c45f08950f587180ed/raw/80a8f003b5d1091eae497c5995bbaa68096e739b/libev-4.19-android.patch && \ + tar xf libev-4.19.tar.gz && \ + rm libev-4.19.tar.gz + +WORKDIR /root/build/libev-4.19 +RUN patch -p1 < ../libev-4.19-android.patch && \ + ./configure \ + --host=arm-linux-androideabi \ + --build=`dpkg-architecture -qDEB_BUILD_GNU_TYPE` \ + --prefix=$PREFIX \ + --disable-shared \ + --enable-static \ + CPPFLAGS=-I$PREFIX/include \ + LDFLAGS=-L$PREFIX/lib && \ + make install + +WORKDIR /root/build +RUN git clone https://github.com/tatsuhiro-t/nghttp2 +WORKDIR /root/build/nghttp2 +RUN autoreconf -i && \ + ./configure \ + --disable-shared \ + --host=arm-linux-androideabi \ + --build=`dpkg-architecture -qDEB_BUILD_GNU_TYPE` \ + --with-xml-prefix="$PREFIX" \ + --without-libxml2 \ + --disable-python-bindings \ + --disable-examples \ + --disable-threads \ + LIBSPDYLAY_CFLAGS=-I$PREFIX/usr/local/include \ + LIBSPDYLAY_LIBS="-L$PREFIX/usr/local/lib -lspdylay" \ + CPPFLAGS="-fPIE -I$PREFIX/include" \ + CXXFLAGS="-fno-strict-aliasing" \ + PKG_CONFIG_LIBDIR="$PREFIX/lib/pkgconfig" \ + LDFLAGS="-fPIE -pie -L$PREFIX/lib" && \ + make && \ + arm-linux-androideabi-strip src/nghttpx src/nghttpd src/nghttp diff --git a/INSTALL b/INSTALL new file mode 100644 index 0000000..2099840 --- /dev/null +++ b/INSTALL @@ -0,0 +1,370 @@ +Installation Instructions +************************* + +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 `' 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/Makefile.am b/Makefile.am new file mode 100644 index 0000000..1058e02 --- /dev/null +++ b/Makefile.am @@ -0,0 +1,43 @@ +# nghttp2 - HTTP/2 C Library + +# Copyright (c) 2012 Tatsuhiro Tsujikawa + +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: + +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. + +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +SUBDIRS = lib third-party src examples python tests integration-tests \ + doc contrib + +ACLOCAL_AMFLAGS = -I m4 + +dist_doc_DATA = README.rst + +EXTRA_DIST = nghttpx.conf.sample proxy.pac.sample android-config android-make \ + Dockerfile.android + +.PHONY: clang-format + +# Format source files using clang-format. Don't format source files +# under third-party directory since we are not responsible for thier +# coding style. +clang-format: + CLANGFORMAT=`git config --get clangformat.binary`; \ + test -z $${CLANGFORMAT} && CLANGFORMAT="clang-format"; \ + $${CLANGFORMAT} -i lib/*.{c,h} lib/includes/nghttp2/*.h \ + src/*.{c,cc,h} src/includes/nghttp2/*.h examples/*.{c,cc} \ + tests/*.{c,h} diff --git a/Makefile.in b/Makefile.in new file mode 100644 index 0000000..0b92de8 --- /dev/null +++ b/Makefile.in @@ -0,0 +1,964 @@ +# Makefile.in generated by automake 1.14.1 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2013 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +# nghttp2 - HTTP/2 C Library + +# Copyright (c) 2012 Tatsuhiro Tsujikawa + +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: + +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. + +VPATH = @srcdir@ +am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)' +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +target_triplet = @target@ +subdir = . +DIST_COMMON = INSTALL NEWS README AUTHORS ChangeLog \ + $(srcdir)/Makefile.in $(srcdir)/Makefile.am \ + $(top_srcdir)/configure $(am__configure_deps) \ + $(srcdir)/config.h.in \ + $(top_srcdir)/lib/includes/nghttp2/nghttp2ver.h.in \ + $(dist_doc_DATA) COPYING compile config.guess config.sub \ + install-sh missing ltmain.sh +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/ax_boost_asio.m4 \ + $(top_srcdir)/m4/ax_boost_base.m4 \ + $(top_srcdir)/m4/ax_boost_system.m4 \ + $(top_srcdir)/m4/ax_boost_thread.m4 \ + $(top_srcdir)/m4/ax_check_compile_flag.m4 \ + $(top_srcdir)/m4/ax_cxx_compile_stdcxx_11.m4 \ + $(top_srcdir)/m4/ax_have_epoll.m4 \ + $(top_srcdir)/m4/ax_python_devel.m4 \ + $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/libxml2.m4 \ + $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ + $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +am__CONFIG_DISTCLEAN_FILES = config.status config.cache config.log \ + configure.lineno config.status.lineno +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = config.h +CONFIG_CLEAN_FILES = lib/includes/nghttp2/nghttp2ver.h +CONFIG_CLEAN_VPATH_FILES = +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +SOURCES = +DIST_SOURCES = +RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \ + ctags-recursive dvi-recursive html-recursive info-recursive \ + install-data-recursive install-dvi-recursive \ + install-exec-recursive install-html-recursive \ + install-info-recursive install-pdf-recursive \ + install-ps-recursive install-recursive installcheck-recursive \ + installdirs-recursive pdf-recursive ps-recursive \ + tags-recursive uninstall-recursive +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +am__uninstall_files_from_dir = { \ + test -z "$$files" \ + || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ + || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ + $(am__cd) "$$dir" && rm -f $$files; }; \ + } +am__installdirs = "$(DESTDIR)$(docdir)" +DATA = $(dist_doc_DATA) +RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \ + distclean-recursive maintainer-clean-recursive +am__recursive_targets = \ + $(RECURSIVE_TARGETS) \ + $(RECURSIVE_CLEAN_TARGETS) \ + $(am__extra_recursive_targets) +AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \ + cscope distdir dist dist-all distcheck +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) \ + $(LISP)config.h.in +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +ETAGS = etags +CTAGS = ctags +CSCOPE = cscope +DIST_SUBDIRS = $(SUBDIRS) +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +distdir = $(PACKAGE)-$(VERSION) +top_distdir = $(distdir) +am__remove_distdir = \ + if test -d "$(distdir)"; then \ + find "$(distdir)" -type d ! -perm -200 -exec chmod u+w {} ';' \ + && rm -rf "$(distdir)" \ + || { sleep 5 && rm -rf "$(distdir)"; }; \ + else :; fi +am__post_remove_distdir = $(am__remove_distdir) +am__relativize = \ + dir0=`pwd`; \ + sed_first='s,^\([^/]*\)/.*$$,\1,'; \ + sed_rest='s,^[^/]*/*,,'; \ + sed_last='s,^.*/\([^/]*\)$$,\1,'; \ + sed_butlast='s,/*[^/]*$$,,'; \ + while test -n "$$dir1"; do \ + first=`echo "$$dir1" | sed -e "$$sed_first"`; \ + if test "$$first" != "."; then \ + if test "$$first" = ".."; then \ + dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \ + dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \ + else \ + first2=`echo "$$dir2" | sed -e "$$sed_first"`; \ + if test "$$first2" = "$$first"; then \ + dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \ + else \ + dir2="../$$dir2"; \ + fi; \ + dir0="$$dir0"/"$$first"; \ + fi; \ + fi; \ + dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \ + done; \ + reldir="$$dir2" +DIST_ARCHIVES = $(distdir).tar.gz +GZIP_ENV = --best +DIST_TARGETS = dist-gzip +distuninstallcheck_listfiles = find . -type f -print +am__distuninstallcheck_listfiles = $(distuninstallcheck_listfiles) \ + | sed 's|^\./|$(prefix)/|' | grep -v '$(infodir)/dir$$' +distcleancheck_listfiles = find . -type f -print +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +APPLDFLAGS = @APPLDFLAGS@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +BOOST_ASIO_LIB = @BOOST_ASIO_LIB@ +BOOST_CPPFLAGS = @BOOST_CPPFLAGS@ +BOOST_LDFLAGS = @BOOST_LDFLAGS@ +BOOST_SYSTEM_LIB = @BOOST_SYSTEM_LIB@ +BOOST_THREAD_LIB = @BOOST_THREAD_LIB@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CUNIT_CFLAGS = @CUNIT_CFLAGS@ +CUNIT_LIBS = @CUNIT_LIBS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +CYTHON = @CYTHON@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DLLTOOL = @DLLTOOL@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +GREP = @GREP@ +HAVE_CXX11 = @HAVE_CXX11@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +JANSSON_CFLAGS = @JANSSON_CFLAGS@ +JANSSON_LIBS = @JANSSON_LIBS@ +JEMALLOC_LIBS = @JEMALLOC_LIBS@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBEVENT_OPENSSL_CFLAGS = @LIBEVENT_OPENSSL_CFLAGS@ +LIBEVENT_OPENSSL_LIBS = @LIBEVENT_OPENSSL_LIBS@ +LIBEV_CFLAGS = @LIBEV_CFLAGS@ +LIBEV_LIBS = @LIBEV_LIBS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBSPDYLAY_CFLAGS = @LIBSPDYLAY_CFLAGS@ +LIBSPDYLAY_LIBS = @LIBSPDYLAY_LIBS@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +LT_AGE = @LT_AGE@ +LT_CURRENT = @LT_CURRENT@ +LT_REVISION = @LT_REVISION@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MKDIR_P = @MKDIR_P@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OPENSSL_CFLAGS = @OPENSSL_CFLAGS@ +OPENSSL_LIBS = @OPENSSL_LIBS@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PACKAGE_VERSION_NUM = @PACKAGE_VERSION_NUM@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PKG_CONFIG = @PKG_CONFIG@ +PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ +PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ +PYTHON = @PYTHON@ +PYTHON_CPPFLAGS = @PYTHON_CPPFLAGS@ +PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@ +PYTHON_EXTRA_LDFLAGS = @PYTHON_EXTRA_LDFLAGS@ +PYTHON_EXTRA_LIBS = @PYTHON_EXTRA_LIBS@ +PYTHON_LDFLAGS = @PYTHON_LDFLAGS@ +PYTHON_PLATFORM = @PYTHON_PLATFORM@ +PYTHON_PREFIX = @PYTHON_PREFIX@ +PYTHON_SITE_PKG = @PYTHON_SITE_PKG@ +PYTHON_VERSION = @PYTHON_VERSION@ +RANLIB = @RANLIB@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +TESTLDADD = @TESTLDADD@ +VERSION = @VERSION@ +WARNCFLAGS = @WARNCFLAGS@ +XML2_CONFIG = @XML2_CONFIG@ +XML_CPPFLAGS = @XML_CPPFLAGS@ +XML_LIBS = @XML_LIBS@ +ZLIB_CFLAGS = @ZLIB_CFLAGS@ +ZLIB_LIBS = @ZLIB_LIBS@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +pkgpyexecdir = @pkgpyexecdir@ +pkgpythondir = @pkgpythondir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +pyexecdir = @pyexecdir@ +pythondir = @pythondir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target = @target@ +target_alias = @target_alias@ +target_cpu = @target_cpu@ +target_os = @target_os@ +target_vendor = @target_vendor@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ + +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +SUBDIRS = lib third-party src examples python tests integration-tests \ + doc contrib + +ACLOCAL_AMFLAGS = -I m4 +dist_doc_DATA = README.rst +EXTRA_DIST = nghttpx.conf.sample proxy.pac.sample android-config android-make \ + Dockerfile.android + +all: config.h + $(MAKE) $(AM_MAKEFLAGS) all-recursive + +.SUFFIXES: +am--refresh: Makefile + @: +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + echo ' cd $(srcdir) && $(AUTOMAKE) --gnu'; \ + $(am__cd) $(srcdir) && $(AUTOMAKE) --gnu \ + && exit 0; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + echo ' $(SHELL) ./config.status'; \ + $(SHELL) ./config.status;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + $(SHELL) ./config.status --recheck + +$(top_srcdir)/configure: $(am__configure_deps) + $(am__cd) $(srcdir) && $(AUTOCONF) +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + $(am__cd) $(srcdir) && $(ACLOCAL) $(ACLOCAL_AMFLAGS) +$(am__aclocal_m4_deps): + +config.h: stamp-h1 + @test -f $@ || rm -f stamp-h1 + @test -f $@ || $(MAKE) $(AM_MAKEFLAGS) stamp-h1 + +stamp-h1: $(srcdir)/config.h.in $(top_builddir)/config.status + @rm -f stamp-h1 + cd $(top_builddir) && $(SHELL) ./config.status config.h +$(srcdir)/config.h.in: $(am__configure_deps) + ($(am__cd) $(top_srcdir) && $(AUTOHEADER)) + rm -f stamp-h1 + touch $@ + +distclean-hdr: + -rm -f config.h stamp-h1 +lib/includes/nghttp2/nghttp2ver.h: $(top_builddir)/config.status $(top_srcdir)/lib/includes/nghttp2/nghttp2ver.h.in + cd $(top_builddir) && $(SHELL) ./config.status $@ + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +distclean-libtool: + -rm -f libtool config.lt +install-dist_docDATA: $(dist_doc_DATA) + @$(NORMAL_INSTALL) + @list='$(dist_doc_DATA)'; test -n "$(docdir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(docdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(docdir)" || exit 1; \ + fi; \ + for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + echo "$$d$$p"; \ + done | $(am__base_list) | \ + while read files; do \ + echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(docdir)'"; \ + $(INSTALL_DATA) $$files "$(DESTDIR)$(docdir)" || exit $$?; \ + done + +uninstall-dist_docDATA: + @$(NORMAL_UNINSTALL) + @list='$(dist_doc_DATA)'; test -n "$(docdir)" || list=; \ + files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ + dir='$(DESTDIR)$(docdir)'; $(am__uninstall_files_from_dir) + +# This directory's subdirectories are mostly independent; you can cd +# into them and run 'make' without going through this Makefile. +# To change the values of 'make' variables: instead of editing Makefiles, +# (1) if the variable is set in 'config.status', edit 'config.status' +# (which will cause the Makefiles to be regenerated when you run 'make'); +# (2) otherwise, pass the desired values on the 'make' command line. +$(am__recursive_targets): + @fail=; \ + if $(am__make_keepgoing); then \ + failcom='fail=yes'; \ + else \ + failcom='exit 1'; \ + fi; \ + dot_seen=no; \ + target=`echo $@ | sed s/-recursive//`; \ + case "$@" in \ + distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ + *) list='$(SUBDIRS)' ;; \ + esac; \ + for subdir in $$list; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + dot_seen=yes; \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || eval $$failcom; \ + done; \ + if test "$$dot_seen" = "no"; then \ + $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ + fi; test -z "$$fail" + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-recursive +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \ + include_option=--etags-include; \ + empty_fix=.; \ + else \ + include_option=--include; \ + empty_fix=; \ + fi; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + test ! -f $$subdir/TAGS || \ + set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \ + fi; \ + done; \ + $(am__define_uniq_tagged_files); \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: ctags-recursive + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" +cscope: cscope.files + test ! -s cscope.files \ + || $(CSCOPE) -b -q $(AM_CSCOPEFLAGS) $(CSCOPEFLAGS) -i cscope.files $(CSCOPE_ARGS) +clean-cscope: + -rm -f cscope.files +cscope.files: clean-cscope cscopelist +cscopelist: cscopelist-recursive + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + -rm -f cscope.out cscope.in.out cscope.po.out cscope.files + +distdir: $(DISTFILES) + $(am__remove_distdir) + test -d "$(distdir)" || mkdir "$(distdir)" + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done + @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + $(am__make_dryrun) \ + || test -d "$(distdir)/$$subdir" \ + || $(MKDIR_P) "$(distdir)/$$subdir" \ + || exit 1; \ + dir1=$$subdir; dir2="$(distdir)/$$subdir"; \ + $(am__relativize); \ + new_distdir=$$reldir; \ + dir1=$$subdir; dir2="$(top_distdir)"; \ + $(am__relativize); \ + new_top_distdir=$$reldir; \ + echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \ + echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \ + ($(am__cd) $$subdir && \ + $(MAKE) $(AM_MAKEFLAGS) \ + top_distdir="$$new_top_distdir" \ + distdir="$$new_distdir" \ + am__remove_distdir=: \ + am__skip_length_check=: \ + am__skip_mode_fix=: \ + distdir) \ + || exit 1; \ + fi; \ + done + -test -n "$(am__skip_mode_fix)" \ + || find "$(distdir)" -type d ! -perm -755 \ + -exec chmod u+rwx,go+rx {} \; -o \ + ! -type d ! -perm -444 -links 1 -exec chmod a+r {} \; -o \ + ! -type d ! -perm -400 -exec chmod a+r {} \; -o \ + ! -type d ! -perm -444 -exec $(install_sh) -c -m a+r {} {} \; \ + || chmod -R a+r "$(distdir)" +dist-gzip: distdir + tardir=$(distdir) && $(am__tar) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).tar.gz + $(am__post_remove_distdir) + +dist-bzip2: distdir + tardir=$(distdir) && $(am__tar) | BZIP2=$${BZIP2--9} bzip2 -c >$(distdir).tar.bz2 + $(am__post_remove_distdir) + +dist-lzip: distdir + tardir=$(distdir) && $(am__tar) | lzip -c $${LZIP_OPT--9} >$(distdir).tar.lz + $(am__post_remove_distdir) + +dist-xz: distdir + tardir=$(distdir) && $(am__tar) | XZ_OPT=$${XZ_OPT--e} xz -c >$(distdir).tar.xz + $(am__post_remove_distdir) + +dist-tarZ: distdir + @echo WARNING: "Support for shar distribution archives is" \ + "deprecated." >&2 + @echo WARNING: "It will be removed altogether in Automake 2.0" >&2 + tardir=$(distdir) && $(am__tar) | compress -c >$(distdir).tar.Z + $(am__post_remove_distdir) + +dist-shar: distdir + @echo WARNING: "Support for distribution archives compressed with" \ + "legacy program 'compress' is deprecated." >&2 + @echo WARNING: "It will be removed altogether in Automake 2.0" >&2 + shar $(distdir) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).shar.gz + $(am__post_remove_distdir) + +dist-zip: distdir + -rm -f $(distdir).zip + zip -rq $(distdir).zip $(distdir) + $(am__post_remove_distdir) + +dist dist-all: + $(MAKE) $(AM_MAKEFLAGS) $(DIST_TARGETS) am__post_remove_distdir='@:' + $(am__post_remove_distdir) + +# This target untars the dist file and tries a VPATH configuration. Then +# it guarantees that the distribution is self-contained by making another +# tarfile. +distcheck: dist + case '$(DIST_ARCHIVES)' in \ + *.tar.gz*) \ + GZIP=$(GZIP_ENV) gzip -dc $(distdir).tar.gz | $(am__untar) ;;\ + *.tar.bz2*) \ + bzip2 -dc $(distdir).tar.bz2 | $(am__untar) ;;\ + *.tar.lz*) \ + lzip -dc $(distdir).tar.lz | $(am__untar) ;;\ + *.tar.xz*) \ + xz -dc $(distdir).tar.xz | $(am__untar) ;;\ + *.tar.Z*) \ + uncompress -c $(distdir).tar.Z | $(am__untar) ;;\ + *.shar.gz*) \ + GZIP=$(GZIP_ENV) gzip -dc $(distdir).shar.gz | unshar ;;\ + *.zip*) \ + unzip $(distdir).zip ;;\ + esac + chmod -R a-w $(distdir) + chmod u+w $(distdir) + mkdir $(distdir)/_build $(distdir)/_inst + chmod a-w $(distdir) + test -d $(distdir)/_build || exit 0; \ + dc_install_base=`$(am__cd) $(distdir)/_inst && pwd | sed -e 's,^[^:\\/]:[\\/],/,'` \ + && dc_destdir="$${TMPDIR-/tmp}/am-dc-$$$$/" \ + && am__cwd=`pwd` \ + && $(am__cd) $(distdir)/_build \ + && ../configure \ + $(AM_DISTCHECK_CONFIGURE_FLAGS) \ + $(DISTCHECK_CONFIGURE_FLAGS) \ + --srcdir=.. --prefix="$$dc_install_base" \ + && $(MAKE) $(AM_MAKEFLAGS) \ + && $(MAKE) $(AM_MAKEFLAGS) dvi \ + && $(MAKE) $(AM_MAKEFLAGS) check \ + && $(MAKE) $(AM_MAKEFLAGS) install \ + && $(MAKE) $(AM_MAKEFLAGS) installcheck \ + && $(MAKE) $(AM_MAKEFLAGS) uninstall \ + && $(MAKE) $(AM_MAKEFLAGS) distuninstallcheck_dir="$$dc_install_base" \ + distuninstallcheck \ + && chmod -R a-w "$$dc_install_base" \ + && ({ \ + (cd ../.. && umask 077 && mkdir "$$dc_destdir") \ + && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" install \ + && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" uninstall \ + && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" \ + distuninstallcheck_dir="$$dc_destdir" distuninstallcheck; \ + } || { rm -rf "$$dc_destdir"; exit 1; }) \ + && rm -rf "$$dc_destdir" \ + && $(MAKE) $(AM_MAKEFLAGS) dist \ + && rm -rf $(DIST_ARCHIVES) \ + && $(MAKE) $(AM_MAKEFLAGS) distcleancheck \ + && cd "$$am__cwd" \ + || exit 1 + $(am__post_remove_distdir) + @(echo "$(distdir) archives ready for distribution: "; \ + list='$(DIST_ARCHIVES)'; for i in $$list; do echo $$i; done) | \ + sed -e 1h -e 1s/./=/g -e 1p -e 1x -e '$$p' -e '$$x' +distuninstallcheck: + @test -n '$(distuninstallcheck_dir)' || { \ + echo 'ERROR: trying to run $@ with an empty' \ + '$$(distuninstallcheck_dir)' >&2; \ + exit 1; \ + }; \ + $(am__cd) '$(distuninstallcheck_dir)' || { \ + echo 'ERROR: cannot chdir into $(distuninstallcheck_dir)' >&2; \ + exit 1; \ + }; \ + test `$(am__distuninstallcheck_listfiles) | wc -l` -eq 0 \ + || { echo "ERROR: files left after uninstall:" ; \ + if test -n "$(DESTDIR)"; then \ + echo " (check DESTDIR support)"; \ + fi ; \ + $(distuninstallcheck_listfiles) ; \ + exit 1; } >&2 +distcleancheck: distclean + @if test '$(srcdir)' = . ; then \ + echo "ERROR: distcleancheck can only run from a VPATH build" ; \ + exit 1 ; \ + fi + @test `$(distcleancheck_listfiles) | wc -l` -eq 0 \ + || { echo "ERROR: files left in build directory after distclean:" ; \ + $(distcleancheck_listfiles) ; \ + exit 1; } >&2 +check-am: all-am +check: check-recursive +all-am: Makefile $(DATA) config.h +installdirs: installdirs-recursive +installdirs-am: + for dir in "$(DESTDIR)$(docdir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: install-recursive +install-exec: install-exec-recursive +install-data: install-data-recursive +uninstall: uninstall-recursive + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-recursive +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-recursive + +clean-am: clean-generic clean-libtool mostlyclean-am + +distclean: distclean-recursive + -rm -f $(am__CONFIG_DISTCLEAN_FILES) + -rm -f Makefile +distclean-am: clean-am distclean-generic distclean-hdr \ + distclean-libtool distclean-tags + +dvi: dvi-recursive + +dvi-am: + +html: html-recursive + +html-am: + +info: info-recursive + +info-am: + +install-data-am: install-dist_docDATA + +install-dvi: install-dvi-recursive + +install-dvi-am: + +install-exec-am: + +install-html: install-html-recursive + +install-html-am: + +install-info: install-info-recursive + +install-info-am: + +install-man: + +install-pdf: install-pdf-recursive + +install-pdf-am: + +install-ps: install-ps-recursive + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-recursive + -rm -f $(am__CONFIG_DISTCLEAN_FILES) + -rm -rf $(top_srcdir)/autom4te.cache + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-recursive + +mostlyclean-am: mostlyclean-generic mostlyclean-libtool + +pdf: pdf-recursive + +pdf-am: + +ps: ps-recursive + +ps-am: + +uninstall-am: uninstall-dist_docDATA + +.MAKE: $(am__recursive_targets) all install-am install-strip + +.PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am \ + am--refresh check check-am clean clean-cscope clean-generic \ + clean-libtool cscope cscopelist-am ctags ctags-am dist \ + dist-all dist-bzip2 dist-gzip dist-lzip dist-shar dist-tarZ \ + dist-xz dist-zip distcheck distclean distclean-generic \ + distclean-hdr distclean-libtool distclean-tags distcleancheck \ + distdir distuninstallcheck dvi dvi-am html html-am info \ + info-am install install-am install-data install-data-am \ + install-dist_docDATA install-dvi install-dvi-am install-exec \ + install-exec-am install-html install-html-am install-info \ + install-info-am install-man install-pdf install-pdf-am \ + install-ps install-ps-am install-strip installcheck \ + installcheck-am installdirs installdirs-am maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-generic \ + mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \ + uninstall-am uninstall-dist_docDATA + + +.PHONY: clang-format + +# Format source files using clang-format. Don't format source files +# under third-party directory since we are not responsible for thier +# coding style. +clang-format: + CLANGFORMAT=`git config --get clangformat.binary`; \ + test -z $${CLANGFORMAT} && CLANGFORMAT="clang-format"; \ + $${CLANGFORMAT} -i lib/*.{c,h} lib/includes/nghttp2/*.h \ + src/*.{c,cc,h} src/includes/nghttp2/*.h examples/*.{c,cc} \ + tests/*.{c,h} + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/NEWS b/NEWS new file mode 100644 index 0000000..e69de29 diff --git a/README b/README new file mode 100644 index 0000000..5ccc0ea --- /dev/null +++ b/README @@ -0,0 +1 @@ +See README.rst diff --git a/README.rst b/README.rst new file mode 100644 index 0000000..82e8ecc --- /dev/null +++ b/README.rst @@ -0,0 +1,1427 @@ +nghttp2 - HTTP/2 C Library +========================== + +This is an implementation of the Hypertext Transfer Protocol version 2 +in C. + +The framing layer of HTTP/2 is implemented as a reusable C +library. On top of that, we have implemented an HTTP/2 client, server +and proxy. We have also developed load test and benchmarking tools for +HTTP/2 and SPDY. + +An HPACK encoder and decoder are available as a public API. + +An experimental high level C++ library is also available. + +We have Python bindings of this library, but we do not have full +code coverage yet. + +Development Status +------------------ + +We have implemented `RFC 7540 `_ +HTTP/2 and `RFC 7541 `_ HPACK - +Header Compression for HTTP/2 + +The nghttp2 code base was forked from the spdylay +(https://github.com/tatsuhiro-t/spdylay) project. + +Public Test Server +------------------ + +The following endpoints are available to try out our nghttp2 +implementation. + +* https://nghttp2.org/ (TLS + ALPN/NPN) + + This endpoint supports ``h2``, ``h2-16``, ``h2-14``, ``spdy/3.1`` + and ``http/1.1`` via ALPN/NPN and requires TLSv1.2 for HTTP/2 + connection. + +* http://nghttp2.org/ (HTTP Upgrade and HTTP/2 Direct) + + ``h2c`` and ``http/1.1``. + +Requirements +------------ + +The following package is required to build the libnghttp2 library: + +* pkg-config >= 0.20 + +To build and run the unit test programs, the following package is +required: + +* cunit >= 2.1 + +To build the documentation, you need to install: + +* sphinx (http://sphinx-doc.org/) + +To build and run the application programs (``nghttp``, ``nghttpd`` and +``nghttpx``) in the ``src`` directory, the following packages are +required: + +* OpenSSL >= 1.0.1 +* libev >= 4.15 +* zlib >= 1.2.3 + +ALPN support requires OpenSSL >= 1.0.2 (released 22 January 2015). + +To enable the SPDY protocol in the application program ``nghttpx`` and +``h2load``, the following package is required: + +* spdylay >= 1.3.2 + +To enable ``-a`` option (getting linked assets from the downloaded +resource) in ``nghttp``, the following package is required: + +* libxml2 >= 2.7.7 + +The HPACK tools require the following package: + +* jansson >= 2.5 + +To build sources under the examples directory, libevent is required: + +* libevent-openssl >= 2.0.8 + +To mitigate heap fragmentation in long running server programs +(``nghttpd`` and ``nghttpx``), jemalloc is recommended: + +* jemalloc + +libnghttp2_asio C++ library requires the following packages: + +* libboost-dev >= 1.54.0 +* libboost-thread-dev >= 1.54.0 + +The Python bindings require the following packages: + +* cython >= 0.19 +* python >= 2.7 + +If you are using Ubuntu 14.04 LTS (trusty), run the following to install the needed packages:: + + sudo apt-get install make binutils autoconf automake autotools-dev libtool pkg-config \ + zlib1g-dev libcunit1-dev libssl-dev libxml2-dev libev-dev libevent-dev libjansson-dev \ + libjemalloc-dev cython python3.4-dev + +spdylay is not packaged in Ubuntu, so you need to build it yourself: +http://tatsuhiro-t.github.io/spdylay/ + +Building 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 + +To compile the source code, gcc >= 4.8.3 or clang >= 3.4 is required. + +.. note:: + + Mac OS X users may need the ``--disable-threads`` configure option to + disable multi-threading in nghttpd, nghttpx and h2load to prevent + them from crashing. A patch is welcome to make multi threading work + on Mac OS X platform. + +Notes for building on Windows (Mingw/Cygwin) +-------------------------------------------- + +Under Mingw environment, you can only compile the library, it's +``libnghttp2-X.dll`` and ``libnghttp2.a``. + +If you want to compile the applications(``h2load``, ``nghttp``, +``nghttpx``, ``nghttpd``), you need to use the Cygwin environment. + +Under Cygwin environment, to compile the applications you need to +compile and install the libev first. + +Secondly, you need to undefine the macro ``__STRICT_ANSI__``, if you +not, the functions ``fdopen``, ``fileno`` and ``strptime`` will not +available. + +the sample command like this:: + + $ export CFLAGS="-U__STRICT_ANSI__ -I$libev_PREFIX/include -L$libev_PREFIX/lib" + $ export CXXFLAGS=$CFLAGS + $ ./configure + $ make + +If you want to compile the applications under ``examples/``, you need +to remove or rename the ``event.h`` from libev's installation, because +it conflicts with libevent's installation. + +Building the documentation +-------------------------- + +.. note:: + + Documentation is still incomplete. + +To build the documentation, run:: + + $ make html + +The documents will be generated under ``doc/manual/html/``. + +The generated documents will not be installed with ``make install``. + +The online documentation is available at +https://nghttp2.org/documentation/ + +Unit tests +---------- + +Unit tests are done by simply running ``make check``. + +Integration tests +----------------- + +We have the integration tests for the nghttpx proxy server. The tests are +written in the `Go programming language `_ and uses +its testing framework. We depend on the following libraries: + +* https://github.com/bradfitz/http2 +* https://github.com/tatsuhiro-t/go-nghttp2 +* https://golang.org/x/net/spdy + +To download the above packages, after settings ``GOPATH``, run the +following command under ``integration-tests`` directory:: + + $ make itprep + +To run the tests, run the following command under +``integration-tests`` directory:: + + $ make it + +Inside the tests, we use port 3009 to run the test subject server. + +Migration from v0.7.15 or earlier +--------------------------------- + +nghttp2 v1.0.0 introduced several backward incompatible changes. In +this section, we describe these changes and how to migrate to v1.0.0. + +ALPN protocol ID is now ``h2`` and ``h2c`` +++++++++++++++++++++++++++++++++++++++++++ + +Previously we announced ``h2-14`` and ``h2c-14``. v1.0.0 implements +final protocol version, and we changed ALPN ID to ``h2`` and ``h2c``. +The macros ``NGHTTP2_PROTO_VERSION_ID``, +``NGHTTP2_PROTO_VERSION_ID_LEN``, +``NGHTTP2_CLEARTEXT_PROTO_VERSION_ID``, and +``NGHTTP2_CLEARTEXT_PROTO_VERSION_ID_LEN`` have been updated to +reflect this change. + +Basically, existing applications do not have to do anything, just +recompiling is enough for this change. + +Use word "client magic" where we use "client connection preface" +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +We use "client connection preface" to mean first 24 bytes of client +connection preface. This is technically not correct, since client +connection preface is composed of 24 bytes client magic byte string +followed by SETTINGS frame. For clarification, we call "client magic" +for this 24 bytes byte string and updated API. + +* ``NGHTTP2_CLIENT_CONNECTION_PREFACE`` was replaced with + ``NGHTTP2_CLIENT_MAGIC``. +* ``NGHTTP2_CLIENT_CONNECTION_PREFACE_LEN`` was replaced with + ``NGHTTP2_CLIENT_MAGIC_LEN``. +* ``NGHTTP2_BAD_PREFACE`` was renamed as ``NGHTTP2_BAD_CLIENT_MAGIC`` + +The alreay deprecated ``NGHTTP2_CLIENT_CONNECTION_HEADER`` and +``NGHTTP2_CLIENT_CONNECTION_HEADER_LEN`` were removed. + +If application uses these macros, just replace old ones with new ones. +Since v1.0.0, client magic is sent by library (see next subsection), +so client application may just remove these macro use. + +Client magic is sent by library ++++++++++++++++++++++++++++++++ + +Previously nghttp2 library did not send client magic, which is first +24 bytes byte string of client connection preface, and client +applications have to send it by themselves. Since v1.0.0, client +magic is sent by library via first call of ``nghttp2_session_send()`` +or ``nghttp2_session_mem_send()``. + +The client applications which send client magic must remove the +relevant code. + +Remove HTTP Alternative Services (Alt-Svc) related code ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +Alt-Svc specification is not finalized yet. To make our API stable, +we have decided to remove all Alt-Svc related API from nghttp2. + +* ``NGHTTP2_EXT_ALTSVC`` was removed. +* ``nghttp2_ext_altsvc`` was removed. + +We have already removed the functionality of Alt-Svc in v0.7 series +and they have been essentially noop. The application using these +macro and struct, remove those lines. + +Use nghttp2_error in nghttp2_on_invalid_frame_recv_callback ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +Previously ``nghttp2_on_invalid_frame_recv_cb_called`` took the +``error_code``, defined in ``nghttp2_error_code``, as parameter. But +they are not detailed enough to debug. Therefore, we decided to use +more detailed ``nghttp2_error`` values instead. + +The application using this callback should update the callback +signature. If it treats ``error_code`` as HTTP/2 error code, update +the code so that it is treated as ``nghttp2_error``. + +Receive client magic by default ++++++++++++++++++++++++++++++++ + +Previously nghttp2 did not process client magic (24 bytes byte +string). To make it deal with it, we had to use +``nghttp2_option_set_recv_client_preface()``. Since v1.0.0, nghttp2 +processes client magic by default and +``nghttp2_option_set_recv_client_preface()`` was removed. + +Some application may want to disable this behaviour, so we added +``nghttp2_option_set_no_recv_client_magic()`` to achieve this. + +The application using ``nghttp2_option_set_recv_client_preface()`` +with nonzero value, just remove it. + +The application using ``nghttp2_option_set_recv_client_preface()`` +with zero value or not using it must use +``nghttp2_option_set_no_recv_client_magic()`` with nonzero value. + +Client, Server and Proxy programs +--------------------------------- + +The ``src`` directory contains the HTTP/2 client, server and proxy programs. + +nghttp - client ++++++++++++++++ + +``nghttp`` is a HTTP/2 client. It can connect to the HTTP/2 server +with prior knowledge, HTTP Upgrade and NPN/ALPN TLS extension. + +It has verbose output mode for framing information. Here is sample +output from ``nghttp`` client:: + + $ nghttp -nv https://nghttp2.org + [ 0.067] Connected + The negotiated protocol: h2 + [ 0.135] send SETTINGS frame + (niv=2) + [SETTINGS_MAX_CONCURRENT_STREAMS(0x03):100] + [SETTINGS_INITIAL_WINDOW_SIZE(0x04):65535] + [ 0.135] send PRIORITY frame + (dep_stream_id=0, weight=201, exclusive=0) + [ 0.135] send PRIORITY frame + (dep_stream_id=0, weight=101, exclusive=0) + [ 0.135] send PRIORITY frame + (dep_stream_id=0, weight=1, exclusive=0) + [ 0.135] send PRIORITY frame + (dep_stream_id=7, weight=1, exclusive=0) + [ 0.135] send PRIORITY frame + (dep_stream_id=3, weight=1, exclusive=0) + [ 0.135] send HEADERS frame + ; END_STREAM | END_HEADERS | PRIORITY + (padlen=0, dep_stream_id=11, weight=16, exclusive=0) + ; Open new stream + :method: GET + :path: / + :scheme: https + :authority: nghttp2.org + accept: */* + accept-encoding: gzip, deflate + user-agent: nghttp2/1.0.0-DEV + [ 0.135] recv SETTINGS frame + (niv=2) + [SETTINGS_MAX_CONCURRENT_STREAMS(0x03):100] + [SETTINGS_INITIAL_WINDOW_SIZE(0x04):65535] + [ 0.135] send SETTINGS frame + ; ACK + (niv=0) + [ 0.165] recv SETTINGS frame + ; ACK + (niv=0) + [ 0.166] recv (stream_id=13) :status: 200 + [ 0.166] recv (stream_id=13) date: Fri, 15 May 2015 14:45:22 GMT + [ 0.166] recv (stream_id=13) content-type: text/html + [ 0.166] recv (stream_id=13) last-modified: Fri, 15 May 2015 14:20:46 GMT + [ 0.166] recv (stream_id=13) etag: W/"555600be-1a7f" + [ 0.166] recv (stream_id=13) link: ; rel=preload; as=stylesheet + [ 0.166] recv (stream_id=13) content-encoding: gzip + [ 0.166] recv (stream_id=13) server: nghttpx nghttp2/1.0.0-DEV + [ 0.166] recv (stream_id=13) via: 1.1 nghttpx + [ 0.166] recv (stream_id=13) strict-transport-security: max-age=31536000 + [ 0.166] recv HEADERS frame + ; END_HEADERS + (padlen=0) + ; First response header + [ 0.166] recv (stream_id=13) :method: GET + [ 0.166] recv (stream_id=13) :scheme: https + [ 0.166] recv (stream_id=13) :path: /stylesheets/screen.css + [ 0.166] recv (stream_id=13) :authority: nghttp2.org + [ 0.166] recv (stream_id=13) accept-encoding: gzip, deflate + [ 0.166] recv (stream_id=13) user-agent: nghttp2/1.0.0-DEV + [ 0.166] recv PUSH_PROMISE frame + ; END_HEADERS + (padlen=0, promised_stream_id=2) + [ 0.166] recv DATA frame + ; END_STREAM + [ 0.167] recv (stream_id=2) :status: 200 + [ 0.167] recv (stream_id=2) date: Fri, 15 May 2015 14:45:22 GMT + [ 0.167] recv (stream_id=2) content-type: text/css + [ 0.167] recv (stream_id=2) last-modified: Fri, 15 May 2015 14:20:46 GMT + [ 0.167] recv (stream_id=2) etag: W/"555600be-9845" + [ 0.167] recv (stream_id=2) content-encoding: gzip + [ 0.167] recv (stream_id=2) server: nghttpx nghttp2/1.0.0-DEV + [ 0.167] recv (stream_id=2) via: 1.1 nghttpx + [ 0.167] recv (stream_id=2) strict-transport-security: max-age=31536000 + [ 0.167] recv HEADERS frame + ; END_HEADERS + (padlen=0) + ; First push response header + [ 0.196] recv DATA frame + ; END_STREAM + [ 0.196] send GOAWAY frame + (last_stream_id=2, error_code=NO_ERROR(0x00), opaque_data(0)=[]) + +The HTTP Upgrade is performed like so:: + + $ nghttp -nvu http://nghttp2.org + [ 0.137] Connected + [ 0.137] HTTP Upgrade request + GET / HTTP/1.1 + Host: nghttp2.org + Connection: Upgrade, HTTP2-Settings + Upgrade: h2c + HTTP2-Settings: AAMAAABkAAQAAP__ + Accept: */* + User-Agent: nghttp2/1.0.0-DEV + + + [ 0.156] HTTP Upgrade response + HTTP/1.1 101 Switching Protocols + Connection: Upgrade + Upgrade: h2c + + + [ 0.156] HTTP Upgrade success + [ 0.157] send SETTINGS frame + (niv=2) + [SETTINGS_MAX_CONCURRENT_STREAMS(0x03):100] + [SETTINGS_INITIAL_WINDOW_SIZE(0x04):65535] + [ 0.157] send PRIORITY frame + (dep_stream_id=0, weight=201, exclusive=0) + [ 0.157] send PRIORITY frame + (dep_stream_id=0, weight=101, exclusive=0) + [ 0.157] send PRIORITY frame + (dep_stream_id=0, weight=1, exclusive=0) + [ 0.157] send PRIORITY frame + (dep_stream_id=7, weight=1, exclusive=0) + [ 0.157] send PRIORITY frame + (dep_stream_id=3, weight=1, exclusive=0) + [ 0.157] send PRIORITY frame + (dep_stream_id=11, weight=16, exclusive=0) + [ 0.157] recv SETTINGS frame + (niv=2) + [SETTINGS_MAX_CONCURRENT_STREAMS(0x03):100] + [SETTINGS_INITIAL_WINDOW_SIZE(0x04):65535] + [ 0.157] recv (stream_id=1) :status: 200 + [ 0.157] recv (stream_id=1) date: Fri, 15 May 2015 14:46:08 GMT + [ 0.157] recv (stream_id=1) content-type: text/html + [ 0.157] recv (stream_id=1) content-length: 6783 + [ 0.157] recv (stream_id=1) last-modified: Fri, 15 May 2015 14:20:46 GMT + [ 0.157] recv (stream_id=1) etag: "555600be-1a7f" + [ 0.157] recv (stream_id=1) link: ; rel=preload; as=stylesheet + [ 0.157] recv (stream_id=1) accept-ranges: bytes + [ 0.157] recv (stream_id=1) server: nghttpx nghttp2/1.0.0-DEV + [ 0.157] recv (stream_id=1) via: 1.1 nghttpx + [ 0.157] recv HEADERS frame + ; END_HEADERS + (padlen=0) + ; First response header + [ 0.157] recv (stream_id=1) :method: GET + [ 0.157] recv (stream_id=1) :scheme: http + [ 0.157] recv (stream_id=1) :path: /stylesheets/screen.css + [ 0.157] recv (stream_id=1) host: nghttp2.org + [ 0.157] recv (stream_id=1) user-agent: nghttp2/1.0.0-DEV + [ 0.157] recv PUSH_PROMISE frame + ; END_HEADERS + (padlen=0, promised_stream_id=2) + [ 0.157] send SETTINGS frame + ; ACK + (niv=0) + [ 0.161] recv DATA frame + ; END_STREAM + [ 0.162] recv (stream_id=2) :status: 200 + [ 0.162] recv (stream_id=2) date: Fri, 15 May 2015 14:46:08 GMT + [ 0.162] recv (stream_id=2) content-type: text/css + [ 0.162] recv (stream_id=2) content-length: 38981 + [ 0.162] recv (stream_id=2) last-modified: Fri, 15 May 2015 14:20:46 GMT + [ 0.162] recv (stream_id=2) etag: "555600be-9845" + [ 0.162] recv (stream_id=2) accept-ranges: bytes + [ 0.162] recv (stream_id=2) server: nghttpx nghttp2/1.0.0-DEV + [ 0.162] recv (stream_id=2) via: 1.1 nghttpx + [ 0.162] recv HEADERS frame + ; END_HEADERS + (padlen=0) + ; First push response header + [ 0.191] recv DATA frame + [ 0.215] recv DATA frame + [ 0.215] send WINDOW_UPDATE frame + (window_size_increment=33322) + [ 0.238] send WINDOW_UPDATE frame + (window_size_increment=33549) + [ 0.238] recv DATA frame + ; END_STREAM + [ 0.238] recv SETTINGS frame + ; ACK + (niv=0) + [ 0.238] send GOAWAY frame + (last_stream_id=2, error_code=NO_ERROR(0x00), opaque_data(0)=[]) + +Using the ``-s`` option, ``nghttp`` prints out some timing information for +requests, sorted by completion time:: + + $ nghttp -nas https://nghttp2.org/ + ***** Statistics ***** + + Request timing: + responseEnd: the time when last byte of response was received + relative to connectEnd + requestStart: the time just before first byte of request was sent + relative to connectEnd. If '*' is shown, this was + pushed by server. + process: responseEnd - requestStart + code: HTTP status code + size: number of bytes received as response body without + inflation. + URI: request URI + + see http://www.w3.org/TR/resource-timing/#processing-model + + sorted by 'complete' + + id responseEnd requestStart process code size request path + 13 +37.19ms +280us 36.91ms 200 2K / + 2 +72.65ms * +36.38ms 36.26ms 200 8K /stylesheets/screen.css + 17 +77.43ms +38.67ms 38.75ms 200 3K /javascripts/octopress.js + 15 +78.12ms +38.66ms 39.46ms 200 3K /javascripts/modernizr-2.0.js + +Using the ``-r`` option, ``nghttp`` writes more detailed timing data to +the given file in HAR format. + +nghttpd - server +++++++++++++++++ + +``nghttpd`` is a multi-threaded static web server. + +By default, it uses SSL/TLS connection. Use ``--no-tls`` option to +disable it. + +``nghttpd`` only accepts HTTP/2 connections via NPN/ALPN or direct +HTTP/2 connections. No HTTP Upgrade is supported. + +The ``-p`` option allows users to configure server push. + +Just like ``nghttp``, it has a verbose output mode for framing +information. Here is sample output from ``nghttpd``:: + + $ nghttpd --no-tls -v 8080 + IPv4: listen 0.0.0.0:8080 + IPv6: listen :::8080 + [id=1] [ 1.521] send SETTINGS frame + (niv=1) + [SETTINGS_MAX_CONCURRENT_STREAMS(0x03):100] + [id=1] [ 1.521] recv SETTINGS frame + (niv=2) + [SETTINGS_MAX_CONCURRENT_STREAMS(0x03):100] + [SETTINGS_INITIAL_WINDOW_SIZE(0x04):65535] + [id=1] [ 1.521] recv SETTINGS frame + ; ACK + (niv=0) + [id=1] [ 1.521] recv PRIORITY frame + (dep_stream_id=0, weight=201, exclusive=0) + [id=1] [ 1.521] recv PRIORITY frame + (dep_stream_id=0, weight=101, exclusive=0) + [id=1] [ 1.521] recv PRIORITY frame + (dep_stream_id=0, weight=1, exclusive=0) + [id=1] [ 1.521] recv PRIORITY frame + (dep_stream_id=7, weight=1, exclusive=0) + [id=1] [ 1.521] recv PRIORITY frame + (dep_stream_id=3, weight=1, exclusive=0) + [id=1] [ 1.521] recv (stream_id=13) :method: GET + [id=1] [ 1.521] recv (stream_id=13) :path: / + [id=1] [ 1.521] recv (stream_id=13) :scheme: http + [id=1] [ 1.521] recv (stream_id=13) :authority: localhost:8080 + [id=1] [ 1.521] recv (stream_id=13) accept: */* + [id=1] [ 1.521] recv (stream_id=13) accept-encoding: gzip, deflate + [id=1] [ 1.521] recv (stream_id=13) user-agent: nghttp2/1.0.0-DEV + [id=1] [ 1.521] recv HEADERS frame + ; END_STREAM | END_HEADERS | PRIORITY + (padlen=0, dep_stream_id=11, weight=16, exclusive=0) + ; Open new stream + [id=1] [ 1.521] send SETTINGS frame + ; ACK + (niv=0) + [id=1] [ 1.521] send HEADERS frame + ; END_HEADERS + (padlen=0) + ; First response header + :status: 200 + server: nghttpd nghttp2/1.0.0-DEV + content-length: 10 + cache-control: max-age=3600 + date: Fri, 15 May 2015 14:49:04 GMT + last-modified: Tue, 30 Sep 2014 12:40:52 GMT + [id=1] [ 1.522] send DATA frame + ; END_STREAM + [id=1] [ 1.522] stream_id=13 closed + [id=1] [ 1.522] recv GOAWAY frame + (last_stream_id=0, error_code=NO_ERROR(0x00), opaque_data(0)=[]) + [id=1] [ 1.522] closed + +nghttpx - proxy ++++++++++++++++ + +``nghttpx`` is a multi-threaded reverse proxy for HTTP/2, SPDY and +HTTP/1.1, and powers http://nghttp2.org and supports HTTP/2 server +push. + +``nghttpx`` implements `important performance-oriented features +`_ in TLS, such as +session IDs, session tickets (with automatic key rotation), OCSP +stapling, dynamic record sizing, ALPN/NPN, forward secrecy and SPDY & +HTTP/2. + +``nghttpx`` has several operational modes: + +================== ============================ ============== ============= +Mode option Frontend Backend Note +================== ============================ ============== ============= +default mode HTTP/2, SPDY, HTTP/1.1 (TLS) HTTP/1.1 Reverse proxy +``--http2-proxy`` HTTP/2, SPDY, HTTP/1.1 (TLS) HTTP/1.1 SPDY proxy +``--http2-bridge`` HTTP/2, SPDY, HTTP/1.1 (TLS) HTTP/2 (TLS) +``--client`` HTTP/2, HTTP/1.1 HTTP/2 (TLS) +``--client-proxy`` HTTP/2, HTTP/1.1 HTTP/2 (TLS) Forward proxy +================== ============================ ============== ============= + +The interesting mode at the moment is the default mode. It works like +a reverse proxy and listens for HTTP/2, SPDY and HTTP/1.1 and can be +deployed as a SSL/TLS terminator for existing web server. + +The default mode, ``--http2-proxy`` and ``--http2-bridge`` modes use +SSL/TLS in the frontend connection by default. To disable SSL/TLS, +use the ``--frontend-no-tls`` option. If that option is used, SPDY is +disabled in the frontend and incoming HTTP/1.1 connections can be +upgraded to HTTP/2 through HTTP Upgrade. + +The ``--http2-bridge``, ``--client`` and ``--client-proxy`` modes use +SSL/TLS in the backend connection by default. To disable SSL/TLS, use +the ``--backend-no-tls`` option. + +``nghttpx`` supports a configuration file. See the ``--conf`` option and +sample configuration file ``nghttpx.conf.sample``. + +In the default mode, (without any of ``--http2-proxy``, +``--http2-bridge``, ``--client-proxy`` and ``--client`` options), +``nghttpx`` works as reverse proxy to the backend server:: + + Client <-- (HTTP/2, SPDY, HTTP/1.1) --> nghttpx <-- (HTTP/1.1) --> Web Server + [reverse proxy] + +With the ``--http2-proxy`` option, it works as a so called secure proxy (aka +SPDY proxy):: + + Client <-- (HTTP/2, SPDY, HTTP/1.1) --> nghttpx <-- (HTTP/1.1) --> Proxy + [secure proxy] (e.g., Squid, ATS) + +The ``Client`` in the above example needs to be configured to use +``nghttpx`` as secure proxy. + +At the time of this writing, Chrome is the only browser which supports +secure proxy. One way to configure Chrome to use a secure proxy is +to create a proxy.pac script like this: + +.. code-block:: javascript + + function FindProxyForURL(url, host) { + return "HTTPS SERVERADDR:PORT"; + } + +``SERVERADDR`` and ``PORT`` is the hostname/address and port of the +machine nghttpx is running on. Please note that Chrome requires a valid +certificate for secure proxy. + +Then run Chrome with the following arguments:: + + $ google-chrome --proxy-pac-url=file:///path/to/proxy.pac --use-npn + +With ``--http2-bridge``, it accepts HTTP/2, SPDY and HTTP/1.1 +connections and communicates with the backend in HTTP/2:: + + Client <-- (HTTP/2, SPDY, HTTP/1.1) --> nghttpx <-- (HTTP/2) --> Web or HTTP/2 Proxy etc + (e.g., nghttpx -s) + +With ``--client-proxy``, it works as a forward proxy and expects +that the backend is an HTTP/2 proxy:: + + Client <-- (HTTP/2, HTTP/1.1) --> nghttpx <-- (HTTP/2) --> HTTP/2 Proxy + [forward proxy] (e.g., nghttpx -s) + +The ``Client`` needs to be configured to use nghttpx as a forward +proxy. The frontend HTTP/1.1 connection can be upgraded to HTTP/2 +through HTTP Upgrade. With the above configuration, one can use +HTTP/1.1 client to access and test their HTTP/2 servers. + +With ``--client``, it works as a reverse proxy and expects that +the backend is an HTTP/2 Web server:: + + Client <-- (HTTP/2, HTTP/1.1) --> nghttpx <-- (HTTP/2) --> Web Server + [reverse proxy] + +The frontend HTTP/1.1 connection can be upgraded to HTTP/2 +through HTTP Upgrade. + +For the operation modes which talk to the backend in HTTP/2 over +SSL/TLS, the backend connections can be tunneled through an HTTP proxy. +The proxy is specified using ``--backend-http-proxy-uri``. The +following figure illustrates the example of the ``--http2-bridge`` and +``--backend-http-proxy-uri`` options to talk to the outside HTTP/2 +proxy through an HTTP proxy:: + + Client <-- (HTTP/2, SPDY, HTTP/1.1) --> nghttpx <-- (HTTP/2) -- + + --===================---> HTTP/2 Proxy + (HTTP proxy tunnel) (e.g., nghttpx -s) + +Benchmarking tool +----------------- + +The ``h2load`` program is a benchmarking tool for HTTP/2 and SPDY. +The SPDY support is enabled if the program was built with the spdylay +library. The UI of ``h2load`` is heavily inspired by ``weighttp`` +(https://github.com/lighttpd/weighttp). The typical usage is as +follows:: + + $ h2load -n100000 -c100 -m100 https://localhost:8443/ + starting benchmark... + spawning thread #0: 100 concurrent clients, 100000 total requests + Protocol: TLSv1.2 + Cipher: ECDHE-RSA-AES128-GCM-SHA256 + Server Temp Key: ECDH P-256 256 bits + progress: 10% done + progress: 20% done + progress: 30% done + progress: 40% done + progress: 50% done + progress: 60% done + progress: 70% done + progress: 80% done + progress: 90% done + progress: 100% done + + finished in 771.26ms, 129658 req/s, 4.71MB/s + requests: 100000 total, 100000 started, 100000 done, 100000 succeeded, 0 failed, 0 errored + status codes: 100000 2xx, 0 3xx, 0 4xx, 0 5xx + traffic: 3812300 bytes total, 1009900 bytes headers, 1000000 bytes data + min max mean sd +/- sd + time for request: 25.12ms 124.55ms 51.07ms 15.36ms 84.87% + time for connect: 208.94ms 254.67ms 241.38ms 7.95ms 63.00% + time to 1st byte: 209.11ms 254.80ms 241.51ms 7.94ms 63.00% + +The above example issued total 100,000 requests, using 100 concurrent +clients (in other words, 100 HTTP/2 sessions), and a maximum of 100 streams +per client. With the ``-t`` option, ``h2load`` will use multiple native +threads to avoid saturating a single core on client side. + +.. warning:: + + **Don't use this tool against publicly available servers.** That is + considered a DOS attack. Please only use it against your private + servers. + +HPACK tools +----------- + +The ``src`` directory contains the HPACK tools. The ``deflatehd`` program is a +command-line header compression tool. The ``inflatehd`` program is a +command-line header decompression tool. Both tools read input from +stdin and write output to stdout. Errors are written to stderr. +They take JSON as input and output. We (mostly) use the same JSON data +format described at https://github.com/http2jp/hpack-test-case. + +deflatehd - header compressor ++++++++++++++++++++++++++++++ + +The ``deflatehd`` program reads JSON data or HTTP/1-style header fields from +stdin and outputs compressed header block in JSON. + +For the JSON input, the root JSON object must include a ``cases`` key. +Its value has to include the sequence of input header set. They share +the same compression context and are processed in the order they +appear. Each item in the sequence is a JSON object and it must +include a ``headers`` key. Its value is an array of JSON objects, +which includes exactly one name/value pair. + +Example: + +.. code-block:: json + + { + "cases": + [ + { + "headers": [ + { ":method": "GET" }, + { ":path": "/" } + ] + }, + { + "headers": [ + { ":method": "POST" }, + { ":path": "/" } + ] + } + ] + } + + +With the ``-t`` option, the program can accept more familiar HTTP/1 style +header field blocks. Each header set is delimited by an empty line: + +Example:: + + :method: GET + :scheme: https + :path: / + + :method: POST + user-agent: nghttp2 + +The output is in JSON object. It should include a ``cases`` key and its +value is an array of JSON objects, which has at least the following keys: + +seq + The index of header set in the input. + +input_length + The sum of the length of the name/value pairs in the input. + +output_length + The length of the compressed header block. + +percentage_of_original_size + ``input_length`` / ``output_length`` * 100 + +wire + The compressed header block as a hex string. + +headers + The input header set. + +header_table_size + The header table size adjusted before deflating the header set. + +Examples: + +.. code-block:: json + + { + "cases": + [ + { + "seq": 0, + "input_length": 66, + "output_length": 20, + "percentage_of_original_size": 30.303030303030305, + "wire": "01881f3468e5891afcbf83868a3d856659c62e3f", + "headers": [ + { + ":authority": "example.org" + }, + { + ":method": "GET" + }, + { + ":path": "/" + }, + { + ":scheme": "https" + }, + { + "user-agent": "nghttp2" + } + ], + "header_table_size": 4096 + } + , + { + "seq": 1, + "input_length": 74, + "output_length": 10, + "percentage_of_original_size": 13.513513513513514, + "wire": "88448504252dd5918485", + "headers": [ + { + ":authority": "example.org" + }, + { + ":method": "POST" + }, + { + ":path": "/account" + }, + { + ":scheme": "https" + }, + { + "user-agent": "nghttp2" + } + ], + "header_table_size": 4096 + } + ] + } + + +The output can be used as the input for ``inflatehd`` and +``deflatehd``. + +With the ``-d`` option, the extra ``header_table`` key is added and its +associated value includes the state of dynamic header table after the +corresponding header set was processed. The value includes at least +the following keys: + +entries + The entry in the header table. If ``referenced`` is ``true``, it + is in the reference set. The ``size`` includes the overhead (32 + bytes). The ``index`` corresponds to the index of header table. + The ``name`` is the header field name and the ``value`` is the + header field value. + +size + The sum of the spaces entries occupied, this includes the + entry overhead. + +max_size + The maximum header table size. + +deflate_size + The sum of the spaces entries occupied within + ``max_deflate_size``. + +max_deflate_size + The maximum header table size the encoder uses. This can be smaller + than ``max_size``. In this case, the encoder only uses up to first + ``max_deflate_size`` buffer. Since the header table size is still + ``max_size``, the encoder has to keep track of entries outside the + ``max_deflate_size`` but inside the ``max_size`` and make sure + that they are no longer referenced. + +Example: + +.. code-block:: json + + { + "cases": + [ + { + "seq": 0, + "input_length": 66, + "output_length": 20, + "percentage_of_original_size": 30.303030303030305, + "wire": "01881f3468e5891afcbf83868a3d856659c62e3f", + "headers": [ + { + ":authority": "example.org" + }, + { + ":method": "GET" + }, + { + ":path": "/" + }, + { + ":scheme": "https" + }, + { + "user-agent": "nghttp2" + } + ], + "header_table_size": 4096, + "header_table": { + "entries": [ + { + "index": 1, + "name": "user-agent", + "value": "nghttp2", + "referenced": true, + "size": 49 + }, + { + "index": 2, + "name": ":scheme", + "value": "https", + "referenced": true, + "size": 44 + }, + { + "index": 3, + "name": ":path", + "value": "/", + "referenced": true, + "size": 38 + }, + { + "index": 4, + "name": ":method", + "value": "GET", + "referenced": true, + "size": 42 + }, + { + "index": 5, + "name": ":authority", + "value": "example.org", + "referenced": true, + "size": 53 + } + ], + "size": 226, + "max_size": 4096, + "deflate_size": 226, + "max_deflate_size": 4096 + } + } + , + { + "seq": 1, + "input_length": 74, + "output_length": 10, + "percentage_of_original_size": 13.513513513513514, + "wire": "88448504252dd5918485", + "headers": [ + { + ":authority": "example.org" + }, + { + ":method": "POST" + }, + { + ":path": "/account" + }, + { + ":scheme": "https" + }, + { + "user-agent": "nghttp2" + } + ], + "header_table_size": 4096, + "header_table": { + "entries": [ + { + "index": 1, + "name": ":method", + "value": "POST", + "referenced": true, + "size": 43 + }, + { + "index": 2, + "name": "user-agent", + "value": "nghttp2", + "referenced": true, + "size": 49 + }, + { + "index": 3, + "name": ":scheme", + "value": "https", + "referenced": true, + "size": 44 + }, + { + "index": 4, + "name": ":path", + "value": "/", + "referenced": false, + "size": 38 + }, + { + "index": 5, + "name": ":method", + "value": "GET", + "referenced": false, + "size": 42 + }, + { + "index": 6, + "name": ":authority", + "value": "example.org", + "referenced": true, + "size": 53 + } + ], + "size": 269, + "max_size": 4096, + "deflate_size": 269, + "max_deflate_size": 4096 + } + } + ] + } + +inflatehd - header decompressor ++++++++++++++++++++++++++++++++ + +The ``inflatehd`` program reads JSON data from stdin and outputs decompressed +name/value pairs in JSON. + +The root JSON object must include the ``cases`` key. Its value has to +include the sequence of compressed header blocks. They share the same +compression context and are processed in the order they appear. Each +item in the sequence is a JSON object and it must have at least a +``wire`` key. Its value is a compressed header block as a hex string. + +Example: + +.. code-block:: json + + { + "cases": + [ + { "wire": "8285" }, + { "wire": "8583" } + ] + } + +The output is a JSON object. It should include a ``cases`` key and its +value is an array of JSON objects, which has at least following keys: + +seq + The index of the header set in the input. + +headers + A JSON array that includes decompressed name/value pairs. + +wire + The compressed header block as a hex string. + +header_table_size + The header table size adjusted before inflating compressed header + block. + +Example: + +.. code-block:: json + + { + "cases": + [ + { + "seq": 0, + "wire": "01881f3468e5891afcbf83868a3d856659c62e3f", + "headers": [ + { + ":authority": "example.org" + }, + { + ":method": "GET" + }, + { + ":path": "/" + }, + { + ":scheme": "https" + }, + { + "user-agent": "nghttp2" + } + ], + "header_table_size": 4096 + } + , + { + "seq": 1, + "wire": "88448504252dd5918485", + "headers": [ + { + ":method": "POST" + }, + { + ":path": "/account" + }, + { + "user-agent": "nghttp2" + }, + { + ":scheme": "https" + }, + { + ":authority": "example.org" + } + ], + "header_table_size": 4096 + } + ] + } + +The output can be used as the input for ``deflatehd`` and +``inflatehd``. + +With the ``-d`` option, the extra ``header_table`` key is added and its +associated value includes the state of the dynamic header table after the +corresponding header set was processed. The format is the same as +``deflatehd``. + +libnghttp2_asio: High level HTTP/2 C++ library +---------------------------------------------- + +libnghttp2_asio is C++ library built on top of libnghttp2 and provides +high level abstraction API to build HTTP/2 applications. It depends +on the Boost::ASIO library and OpenSSL. Currently libnghttp2_asio +provides both client and server APIs. + +libnghttp2_asio is not built by default. Use the ``--enable-asio-lib`` +configure flag to build libnghttp2_asio. The required Boost libraries +are: + +* Boost::Asio +* Boost::System +* Boost::Thread + +The server API is designed to build an HTTP/2 server very easily to utilize +C++11 anonymous functions and closures. The bare minimum example of +an HTTP/2 server looks like this: + +.. code-block:: cpp + + #include + + using namespace nghttp2::asio_http2; + using namespace nghttp2::asio_http2::server; + + int main(int argc, char *argv[]) { + boost::system::error_code ec; + http2 server; + + server.handle("/", [](const request &req, const response &res) { + res.write_head(200); + res.end("hello, world\n"); + }); + + if (server.listen_and_serve(ec, "localhost", "3000")) { + std::cerr << "error: " << ec.message() << std::endl; + } + } + +Here is sample code to use the client API: + +.. code-block:: cpp + + #include + + #include + + using boost::asio::ip::tcp; + + using namespace nghttp2::asio_http2; + using namespace nghttp2::asio_http2::client; + + int main(int argc, char *argv[]) { + boost::system::error_code ec; + boost::asio::io_service io_service; + + // connect to localhost:3000 + session sess(io_service, "localhost", "3000"); + + sess.on_connect([&sess](tcp::resolver::iterator endpoint_it) { + boost::system::error_code ec; + + auto req = sess.submit(ec, "GET", "http://localhost:3000/"); + + req->on_response([](const response &res) { + // print status code and response header fields. + std::cerr << "HTTP/2 " << res.status_code() << std::endl; + for (auto &kv : res.header()) { + std::cerr << kv.first << ": " << kv.second.value << "\n"; + } + std::cerr << std::endl; + + res.on_data([](const uint8_t *data, std::size_t len) { + std::cerr.write(reinterpret_cast(data), len); + std::cerr << std::endl; + }); + }); + + req->on_close([&sess](uint32_t error_code) { + // shutdown session after first request was done. + sess.shutdown(); + }); + }); + + sess.on_error([](const boost::system::error_code &ec) { + std::cerr << "error: " << ec.message() << std::endl; + }); + + io_service.run(); + } + +For more details, see the documentation of libnghttp2_asio. + +Python bindings +--------------- + +The ``python`` directory contains nghttp2 Python bindings. The +bindings currently provide HPACK compressor and decompressor classes +and an HTTP/2 server. + +The extension module is called ``nghttp2``. + +``make`` will build the bindings and target Python version is +determined by the ``configure`` script. If the detected Python version is not +what you expect, specify a path to Python executable in a ``PYTHON`` +variable as an argument to configure script (e.g., ``./configure +PYTHON=/usr/bin/python3.4``). + +The following example code illustrates basic usage of the HPACK compressor +and decompressor in Python: + +.. code-block:: python + + import binascii + import nghttp2 + + deflater = nghttp2.HDDeflater() + inflater = nghttp2.HDInflater() + + data = deflater.deflate([(b'foo', b'bar'), + (b'baz', b'buz')]) + print(binascii.b2a_hex(data)) + + hdrs = inflater.inflate(data) + print(hdrs) + +The ``nghttp2.HTTP2Server`` class builds on top of the asyncio event +loop. On construction, *RequestHandlerClass* must be given, which +must be a subclass of ``nghttp2.BaseRequestHandler`` class. + +The ``BaseRequestHandler`` class is used to handle the HTTP/2 stream. +By default, it does nothing. It must be subclassed to handle each +event callback method. + +The first callback method invoked is ``on_headers()``. It is called +when HEADERS frame, which includes the request header fields, has arrived. + +If the request has a request body, ``on_data(data)`` is invoked for each +chunk of received data. + +Once the entire request is received, ``on_request_done()`` is invoked. + +When the stream is closed, ``on_close(error_code)`` is called. + +The application can send a response using ``send_response()`` method. +It can be used in ``on_headers()``, ``on_data()`` or +``on_request_done()``. + +The application can push resources using the ``push()`` method. It must be +used before the ``send_response()`` call. + +The following instance variables are available: + +client_address + Contains a tuple of the form (host, port) referring to the + client's address. + +stream_id + Stream ID of this stream. + +scheme + Scheme of the request URI. This is a value of :scheme header + field. + +method + Method of this stream. This is a value of :method header field. + +host + This is a value of :authority or host header field. + +path + This is a value of :path header field. + +The following example illustrates the HTTP2Server and +BaseRequestHandler usage: + +.. code-block:: python + + #!/usr/bin/env python + + import io, ssl + import nghttp2 + + class Handler(nghttp2.BaseRequestHandler): + + def on_headers(self): + self.push(path='/css/bootstrap.css', + request_headers = [('content-length', '3')], + status=200, + body='foo') + + self.push(path='/js/bootstrap.js', + method='GET', + request_headers = [('content-length', '10')], + status=200, + body='foobarbuzz') + + self.send_response(status=200, + headers = [('content-type', 'text/plain')], + body=io.BytesIO(b'nghttp2-python FTW')) + + ctx = ssl.SSLContext(ssl.PROTOCOL_SSLv23) + ctx.options = ssl.OP_ALL | ssl.OP_NO_SSLv2 + ctx.load_cert_chain('server.crt', 'server.key') + + # give None to ssl to make the server non-SSL/TLS + server = nghttp2.HTTP2Server(('127.0.0.1', 8443), Handler, ssl=ctx) + server.serve_forever() + +Contribution +------------ + +[This text was composed based on 1.2. License section of curl/libcurl +project.] + +When contributing with code, you agree to put your changes and new +code under the same license nghttp2 is already using unless stated and +agreed otherwise. + +When changing existing source code, do not alter the copyright of +the original file(s). The copyright will still be owned by the +original creator(s) or those who have been assigned copyright by the +original author(s). + +By submitting a patch to the nghttp2 project, you (or your employer, as +the case may be) agree to assign the copyright of your submission to us. +.. the above really needs to be reworded to pass legal muster. +We will credit you for your +changes as far as possible, to give credit but also to keep a trace +back to who made what changes. Please always provide us with your +full real name when contributing! + +See `Contribution Guidelines +`_ for more +details. diff --git a/aclocal.m4 b/aclocal.m4 new file mode 100644 index 0000000..44bdb74 --- /dev/null +++ b/aclocal.m4 @@ -0,0 +1,1613 @@ +# generated automatically by aclocal 1.14.1 -*- Autoconf -*- + +# Copyright (C) 1996-2013 Free Software Foundation, Inc. + +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +m4_ifndef([AC_CONFIG_MACRO_DIRS], [m4_defun([_AM_CONFIG_MACRO_DIRS], [])m4_defun([AC_CONFIG_MACRO_DIRS], [_AM_CONFIG_MACRO_DIRS($@)])]) +m4_ifndef([AC_AUTOCONF_VERSION], + [m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl +m4_if(m4_defn([AC_AUTOCONF_VERSION]), [2.69],, +[m4_warning([this file was generated for autoconf 2.69. +You have another version of autoconf. It may work, but is not guaranteed to. +If you have problems, you may need to regenerate the build system entirely. +To do so, use the procedure documented by the package, typically 'autoreconf'.])]) + +# pkg.m4 - Macros to locate and utilise pkg-config. -*- Autoconf -*- +# serial 1 (pkg-config-0.24) +# +# Copyright © 2004 Scott James Remnant . +# +# 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 2 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, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +# PKG_PROG_PKG_CONFIG([MIN-VERSION]) +# ---------------------------------- +AC_DEFUN([PKG_PROG_PKG_CONFIG], +[m4_pattern_forbid([^_?PKG_[A-Z_]+$]) +m4_pattern_allow([^PKG_CONFIG(_(PATH|LIBDIR|SYSROOT_DIR|ALLOW_SYSTEM_(CFLAGS|LIBS)))?$]) +m4_pattern_allow([^PKG_CONFIG_(DISABLE_UNINSTALLED|TOP_BUILD_DIR|DEBUG_SPEW)$]) +AC_ARG_VAR([PKG_CONFIG], [path to pkg-config utility]) +AC_ARG_VAR([PKG_CONFIG_PATH], [directories to add to pkg-config's search path]) +AC_ARG_VAR([PKG_CONFIG_LIBDIR], [path overriding pkg-config's built-in search path]) + +if test "x$ac_cv_env_PKG_CONFIG_set" != "xset"; then + AC_PATH_TOOL([PKG_CONFIG], [pkg-config]) +fi +if test -n "$PKG_CONFIG"; then + _pkg_min_version=m4_default([$1], [0.9.0]) + AC_MSG_CHECKING([pkg-config is at least version $_pkg_min_version]) + if $PKG_CONFIG --atleast-pkgconfig-version $_pkg_min_version; then + AC_MSG_RESULT([yes]) + else + AC_MSG_RESULT([no]) + PKG_CONFIG="" + fi +fi[]dnl +])# PKG_PROG_PKG_CONFIG + +# PKG_CHECK_EXISTS(MODULES, [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) +# +# Check to see whether a particular set of modules exists. Similar +# to PKG_CHECK_MODULES(), but does not set variables or print errors. +# +# Please remember that m4 expands AC_REQUIRE([PKG_PROG_PKG_CONFIG]) +# only at the first occurence in configure.ac, so if the first place +# it's called might be skipped (such as if it is within an "if", you +# have to call PKG_CHECK_EXISTS manually +# -------------------------------------------------------------- +AC_DEFUN([PKG_CHECK_EXISTS], +[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl +if test -n "$PKG_CONFIG" && \ + AC_RUN_LOG([$PKG_CONFIG --exists --print-errors "$1"]); then + m4_default([$2], [:]) +m4_ifvaln([$3], [else + $3])dnl +fi]) + +# _PKG_CONFIG([VARIABLE], [COMMAND], [MODULES]) +# --------------------------------------------- +m4_define([_PKG_CONFIG], +[if test -n "$$1"; then + pkg_cv_[]$1="$$1" + elif test -n "$PKG_CONFIG"; then + PKG_CHECK_EXISTS([$3], + [pkg_cv_[]$1=`$PKG_CONFIG --[]$2 "$3" 2>/dev/null` + test "x$?" != "x0" && pkg_failed=yes ], + [pkg_failed=yes]) + else + pkg_failed=untried +fi[]dnl +])# _PKG_CONFIG + +# _PKG_SHORT_ERRORS_SUPPORTED +# ----------------------------- +AC_DEFUN([_PKG_SHORT_ERRORS_SUPPORTED], +[AC_REQUIRE([PKG_PROG_PKG_CONFIG]) +if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then + _pkg_short_errors_supported=yes +else + _pkg_short_errors_supported=no +fi[]dnl +])# _PKG_SHORT_ERRORS_SUPPORTED + + +# PKG_CHECK_MODULES(VARIABLE-PREFIX, MODULES, [ACTION-IF-FOUND], +# [ACTION-IF-NOT-FOUND]) +# +# +# Note that if there is a possibility the first call to +# PKG_CHECK_MODULES might not happen, you should be sure to include an +# explicit call to PKG_PROG_PKG_CONFIG in your configure.ac +# +# +# -------------------------------------------------------------- +AC_DEFUN([PKG_CHECK_MODULES], +[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl +AC_ARG_VAR([$1][_CFLAGS], [C compiler flags for $1, overriding pkg-config])dnl +AC_ARG_VAR([$1][_LIBS], [linker flags for $1, overriding pkg-config])dnl + +pkg_failed=no +AC_MSG_CHECKING([for $1]) + +_PKG_CONFIG([$1][_CFLAGS], [cflags], [$2]) +_PKG_CONFIG([$1][_LIBS], [libs], [$2]) + +m4_define([_PKG_TEXT], [Alternatively, you may set the environment variables $1[]_CFLAGS +and $1[]_LIBS to avoid the need to call pkg-config. +See the pkg-config man page for more details.]) + +if test $pkg_failed = yes; then + AC_MSG_RESULT([no]) + _PKG_SHORT_ERRORS_SUPPORTED + if test $_pkg_short_errors_supported = yes; then + $1[]_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "$2" 2>&1` + else + $1[]_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "$2" 2>&1` + fi + # Put the nasty error message in config.log where it belongs + echo "$$1[]_PKG_ERRORS" >&AS_MESSAGE_LOG_FD + + m4_default([$4], [AC_MSG_ERROR( +[Package requirements ($2) were not met: + +$$1_PKG_ERRORS + +Consider adjusting the PKG_CONFIG_PATH environment variable if you +installed software in a non-standard prefix. + +_PKG_TEXT])[]dnl + ]) +elif test $pkg_failed = untried; then + AC_MSG_RESULT([no]) + m4_default([$4], [AC_MSG_FAILURE( +[The pkg-config script could not be found or is too old. Make sure it +is in your PATH or set the PKG_CONFIG environment variable to the full +path to pkg-config. + +_PKG_TEXT + +To get pkg-config, see .])[]dnl + ]) +else + $1[]_CFLAGS=$pkg_cv_[]$1[]_CFLAGS + $1[]_LIBS=$pkg_cv_[]$1[]_LIBS + AC_MSG_RESULT([yes]) + $3 +fi[]dnl +])# PKG_CHECK_MODULES + + +# PKG_INSTALLDIR(DIRECTORY) +# ------------------------- +# Substitutes the variable pkgconfigdir as the location where a module +# should install pkg-config .pc files. By default the directory is +# $libdir/pkgconfig, but the default can be changed by passing +# DIRECTORY. The user can override through the --with-pkgconfigdir +# parameter. +AC_DEFUN([PKG_INSTALLDIR], +[m4_pushdef([pkg_default], [m4_default([$1], ['${libdir}/pkgconfig'])]) +m4_pushdef([pkg_description], + [pkg-config installation directory @<:@]pkg_default[@:>@]) +AC_ARG_WITH([pkgconfigdir], + [AS_HELP_STRING([--with-pkgconfigdir], pkg_description)],, + [with_pkgconfigdir=]pkg_default) +AC_SUBST([pkgconfigdir], [$with_pkgconfigdir]) +m4_popdef([pkg_default]) +m4_popdef([pkg_description]) +]) dnl PKG_INSTALLDIR + + +# PKG_NOARCH_INSTALLDIR(DIRECTORY) +# ------------------------- +# Substitutes the variable noarch_pkgconfigdir as the location where a +# module should install arch-independent pkg-config .pc files. By +# default the directory is $datadir/pkgconfig, but the default can be +# changed by passing DIRECTORY. The user can override through the +# --with-noarch-pkgconfigdir parameter. +AC_DEFUN([PKG_NOARCH_INSTALLDIR], +[m4_pushdef([pkg_default], [m4_default([$1], ['${datadir}/pkgconfig'])]) +m4_pushdef([pkg_description], + [pkg-config arch-independent installation directory @<:@]pkg_default[@:>@]) +AC_ARG_WITH([noarch-pkgconfigdir], + [AS_HELP_STRING([--with-noarch-pkgconfigdir], pkg_description)],, + [with_noarch_pkgconfigdir=]pkg_default) +AC_SUBST([noarch_pkgconfigdir], [$with_noarch_pkgconfigdir]) +m4_popdef([pkg_default]) +m4_popdef([pkg_description]) +]) dnl PKG_NOARCH_INSTALLDIR + + +# PKG_CHECK_VAR(VARIABLE, MODULE, CONFIG-VARIABLE, +# [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) +# ------------------------------------------- +# Retrieves the value of the pkg-config variable for the given module. +AC_DEFUN([PKG_CHECK_VAR], +[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl +AC_ARG_VAR([$1], [value of $3 for $2, overriding pkg-config])dnl + +_PKG_CONFIG([$1], [variable="][$3]["], [$2]) +AS_VAR_COPY([$1], [pkg_cv_][$1]) + +AS_VAR_IF([$1], [""], [$5], [$4])dnl +])# PKG_CHECK_VAR + +# Copyright (C) 2002-2013 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# AM_AUTOMAKE_VERSION(VERSION) +# ---------------------------- +# Automake X.Y traces this macro to ensure aclocal.m4 has been +# generated from the m4 files accompanying Automake X.Y. +# (This private macro should not be called outside this file.) +AC_DEFUN([AM_AUTOMAKE_VERSION], +[am__api_version='1.14' +dnl Some users find AM_AUTOMAKE_VERSION and mistake it for a way to +dnl require some minimum version. Point them to the right macro. +m4_if([$1], [1.14.1], [], + [AC_FATAL([Do not call $0, use AM_INIT_AUTOMAKE([$1]).])])dnl +]) + +# _AM_AUTOCONF_VERSION(VERSION) +# ----------------------------- +# aclocal traces this macro to find the Autoconf version. +# This is a private macro too. Using m4_define simplifies +# the logic in aclocal, which can simply ignore this definition. +m4_define([_AM_AUTOCONF_VERSION], []) + +# AM_SET_CURRENT_AUTOMAKE_VERSION +# ------------------------------- +# Call AM_AUTOMAKE_VERSION and AM_AUTOMAKE_VERSION so they can be traced. +# This function is AC_REQUIREd by AM_INIT_AUTOMAKE. +AC_DEFUN([AM_SET_CURRENT_AUTOMAKE_VERSION], +[AM_AUTOMAKE_VERSION([1.14.1])dnl +m4_ifndef([AC_AUTOCONF_VERSION], + [m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl +_AM_AUTOCONF_VERSION(m4_defn([AC_AUTOCONF_VERSION]))]) + +# AM_AUX_DIR_EXPAND -*- Autoconf -*- + +# Copyright (C) 2001-2013 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# For projects using AC_CONFIG_AUX_DIR([foo]), Autoconf sets +# $ac_aux_dir to '$srcdir/foo'. In other projects, it is set to +# '$srcdir', '$srcdir/..', or '$srcdir/../..'. +# +# Of course, Automake must honor this variable whenever it calls a +# tool from the auxiliary directory. The problem is that $srcdir (and +# therefore $ac_aux_dir as well) can be either absolute or relative, +# depending on how configure is run. This is pretty annoying, since +# it makes $ac_aux_dir quite unusable in subdirectories: in the top +# source directory, any form will work fine, but in subdirectories a +# relative path needs to be adjusted first. +# +# $ac_aux_dir/missing +# fails when called from a subdirectory if $ac_aux_dir is relative +# $top_srcdir/$ac_aux_dir/missing +# fails if $ac_aux_dir is absolute, +# fails when called from a subdirectory in a VPATH build with +# a relative $ac_aux_dir +# +# The reason of the latter failure is that $top_srcdir and $ac_aux_dir +# are both prefixed by $srcdir. In an in-source build this is usually +# harmless because $srcdir is '.', but things will broke when you +# start a VPATH build or use an absolute $srcdir. +# +# So we could use something similar to $top_srcdir/$ac_aux_dir/missing, +# iff we strip the leading $srcdir from $ac_aux_dir. That would be: +# am_aux_dir='\$(top_srcdir)/'`expr "$ac_aux_dir" : "$srcdir//*\(.*\)"` +# and then we would define $MISSING as +# MISSING="\${SHELL} $am_aux_dir/missing" +# This will work as long as MISSING is not called from configure, because +# unfortunately $(top_srcdir) has no meaning in configure. +# However there are other variables, like CC, which are often used in +# configure, and could therefore not use this "fixed" $ac_aux_dir. +# +# Another solution, used here, is to always expand $ac_aux_dir to an +# absolute PATH. The drawback is that using absolute paths prevent a +# configured tree to be moved without reconfiguration. + +AC_DEFUN([AM_AUX_DIR_EXPAND], +[AC_REQUIRE([AC_CONFIG_AUX_DIR_DEFAULT])dnl +# Expand $ac_aux_dir to an absolute path. +am_aux_dir=`cd "$ac_aux_dir" && pwd` +]) + +# AM_CONDITIONAL -*- Autoconf -*- + +# Copyright (C) 1997-2013 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# AM_CONDITIONAL(NAME, SHELL-CONDITION) +# ------------------------------------- +# Define a conditional. +AC_DEFUN([AM_CONDITIONAL], +[AC_PREREQ([2.52])dnl + m4_if([$1], [TRUE], [AC_FATAL([$0: invalid condition: $1])], + [$1], [FALSE], [AC_FATAL([$0: invalid condition: $1])])dnl +AC_SUBST([$1_TRUE])dnl +AC_SUBST([$1_FALSE])dnl +_AM_SUBST_NOTMAKE([$1_TRUE])dnl +_AM_SUBST_NOTMAKE([$1_FALSE])dnl +m4_define([_AM_COND_VALUE_$1], [$2])dnl +if $2; then + $1_TRUE= + $1_FALSE='#' +else + $1_TRUE='#' + $1_FALSE= +fi +AC_CONFIG_COMMANDS_PRE( +[if test -z "${$1_TRUE}" && test -z "${$1_FALSE}"; then + AC_MSG_ERROR([[conditional "$1" was never defined. +Usually this means the macro was only invoked conditionally.]]) +fi])]) + +# Copyright (C) 1999-2013 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + + +# There are a few dirty hacks below to avoid letting 'AC_PROG_CC' be +# written in clear, in which case automake, when reading aclocal.m4, +# will think it sees a *use*, and therefore will trigger all it's +# C support machinery. Also note that it means that autoscan, seeing +# CC etc. in the Makefile, will ask for an AC_PROG_CC use... + + +# _AM_DEPENDENCIES(NAME) +# ---------------------- +# See how the compiler implements dependency checking. +# NAME is "CC", "CXX", "OBJC", "OBJCXX", "UPC", or "GJC". +# We try a few techniques and use that to set a single cache variable. +# +# We don't AC_REQUIRE the corresponding AC_PROG_CC since the latter was +# modified to invoke _AM_DEPENDENCIES(CC); we would have a circular +# dependency, and given that the user is not expected to run this macro, +# just rely on AC_PROG_CC. +AC_DEFUN([_AM_DEPENDENCIES], +[AC_REQUIRE([AM_SET_DEPDIR])dnl +AC_REQUIRE([AM_OUTPUT_DEPENDENCY_COMMANDS])dnl +AC_REQUIRE([AM_MAKE_INCLUDE])dnl +AC_REQUIRE([AM_DEP_TRACK])dnl + +m4_if([$1], [CC], [depcc="$CC" am_compiler_list=], + [$1], [CXX], [depcc="$CXX" am_compiler_list=], + [$1], [OBJC], [depcc="$OBJC" am_compiler_list='gcc3 gcc'], + [$1], [OBJCXX], [depcc="$OBJCXX" am_compiler_list='gcc3 gcc'], + [$1], [UPC], [depcc="$UPC" am_compiler_list=], + [$1], [GCJ], [depcc="$GCJ" am_compiler_list='gcc3 gcc'], + [depcc="$$1" am_compiler_list=]) + +AC_CACHE_CHECK([dependency style of $depcc], + [am_cv_$1_dependencies_compiler_type], +[if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then + # We make a subdir and do the tests there. Otherwise we can end up + # making bogus files that we don't know about and never remove. For + # instance it was reported that on HP-UX the gcc test will end up + # making a dummy file named 'D' -- because '-MD' means "put the output + # in D". + rm -rf conftest.dir + mkdir conftest.dir + # Copy depcomp to subdir because otherwise we won't find it if we're + # using a relative directory. + cp "$am_depcomp" conftest.dir + cd conftest.dir + # We will build objects and dependencies in a subdirectory because + # it helps to detect inapplicable dependency modes. For instance + # both Tru64's cc and ICC support -MD to output dependencies as a + # side effect of compilation, but ICC will put the dependencies in + # the current directory while Tru64 will put them in the object + # directory. + mkdir sub + + am_cv_$1_dependencies_compiler_type=none + if test "$am_compiler_list" = ""; then + am_compiler_list=`sed -n ['s/^#*\([a-zA-Z0-9]*\))$/\1/p'] < ./depcomp` + fi + am__universal=false + m4_case([$1], [CC], + [case " $depcc " in #( + *\ -arch\ *\ -arch\ *) am__universal=true ;; + esac], + [CXX], + [case " $depcc " in #( + *\ -arch\ *\ -arch\ *) am__universal=true ;; + esac]) + + for depmode in $am_compiler_list; do + # Setup a source with many dependencies, because some compilers + # like to wrap large dependency lists on column 80 (with \), and + # we should not choose a depcomp mode which is confused by this. + # + # We need to recreate these files for each test, as the compiler may + # overwrite some of them when testing with obscure command lines. + # This happens at least with the AIX C compiler. + : > sub/conftest.c + for i in 1 2 3 4 5 6; do + echo '#include "conftst'$i'.h"' >> sub/conftest.c + # Using ": > sub/conftst$i.h" creates only sub/conftst1.h with + # Solaris 10 /bin/sh. + echo '/* dummy */' > sub/conftst$i.h + done + echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf + + # We check with '-c' and '-o' for the sake of the "dashmstdout" + # mode. It turns out that the SunPro C++ compiler does not properly + # handle '-M -o', and we need to detect this. Also, some Intel + # versions had trouble with output in subdirs. + am__obj=sub/conftest.${OBJEXT-o} + am__minus_obj="-o $am__obj" + case $depmode in + gcc) + # This depmode causes a compiler race in universal mode. + test "$am__universal" = false || continue + ;; + nosideeffect) + # After this tag, mechanisms are not by side-effect, so they'll + # only be used when explicitly requested. + if test "x$enable_dependency_tracking" = xyes; then + continue + else + break + fi + ;; + msvc7 | msvc7msys | msvisualcpp | msvcmsys) + # This compiler won't grok '-c -o', but also, the minuso test has + # not run yet. These depmodes are late enough in the game, and + # so weak that their functioning should not be impacted. + am__obj=conftest.${OBJEXT-o} + am__minus_obj= + ;; + none) break ;; + esac + if depmode=$depmode \ + source=sub/conftest.c object=$am__obj \ + depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \ + $SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \ + >/dev/null 2>conftest.err && + grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 && + grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 && + grep $am__obj sub/conftest.Po > /dev/null 2>&1 && + ${MAKE-make} -s -f confmf > /dev/null 2>&1; then + # icc doesn't choke on unknown options, it will just issue warnings + # or remarks (even with -Werror). So we grep stderr for any message + # that says an option was ignored or not supported. + # When given -MP, icc 7.0 and 7.1 complain thusly: + # icc: Command line warning: ignoring option '-M'; no argument required + # The diagnosis changed in icc 8.0: + # icc: Command line remark: option '-MP' not supported + if (grep 'ignoring option' conftest.err || + grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else + am_cv_$1_dependencies_compiler_type=$depmode + break + fi + fi + done + + cd .. + rm -rf conftest.dir +else + am_cv_$1_dependencies_compiler_type=none +fi +]) +AC_SUBST([$1DEPMODE], [depmode=$am_cv_$1_dependencies_compiler_type]) +AM_CONDITIONAL([am__fastdep$1], [ + test "x$enable_dependency_tracking" != xno \ + && test "$am_cv_$1_dependencies_compiler_type" = gcc3]) +]) + + +# AM_SET_DEPDIR +# ------------- +# Choose a directory name for dependency files. +# This macro is AC_REQUIREd in _AM_DEPENDENCIES. +AC_DEFUN([AM_SET_DEPDIR], +[AC_REQUIRE([AM_SET_LEADING_DOT])dnl +AC_SUBST([DEPDIR], ["${am__leading_dot}deps"])dnl +]) + + +# AM_DEP_TRACK +# ------------ +AC_DEFUN([AM_DEP_TRACK], +[AC_ARG_ENABLE([dependency-tracking], [dnl +AS_HELP_STRING( + [--enable-dependency-tracking], + [do not reject slow dependency extractors]) +AS_HELP_STRING( + [--disable-dependency-tracking], + [speeds up one-time build])]) +if test "x$enable_dependency_tracking" != xno; then + am_depcomp="$ac_aux_dir/depcomp" + AMDEPBACKSLASH='\' + am__nodep='_no' +fi +AM_CONDITIONAL([AMDEP], [test "x$enable_dependency_tracking" != xno]) +AC_SUBST([AMDEPBACKSLASH])dnl +_AM_SUBST_NOTMAKE([AMDEPBACKSLASH])dnl +AC_SUBST([am__nodep])dnl +_AM_SUBST_NOTMAKE([am__nodep])dnl +]) + +# Generate code to set up dependency tracking. -*- Autoconf -*- + +# Copyright (C) 1999-2013 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + + +# _AM_OUTPUT_DEPENDENCY_COMMANDS +# ------------------------------ +AC_DEFUN([_AM_OUTPUT_DEPENDENCY_COMMANDS], +[{ + # Older Autoconf quotes --file arguments for eval, but not when files + # are listed without --file. Let's play safe and only enable the eval + # if we detect the quoting. + case $CONFIG_FILES in + *\'*) eval set x "$CONFIG_FILES" ;; + *) set x $CONFIG_FILES ;; + esac + shift + for mf + do + # Strip MF so we end up with the name of the file. + mf=`echo "$mf" | sed -e 's/:.*$//'` + # Check whether this is an Automake generated Makefile or not. + # We used to match only the files named 'Makefile.in', but + # some people rename them; so instead we look at the file content. + # Grep'ing the first line is not enough: some people post-process + # each Makefile.in and add a new line on top of each file to say so. + # Grep'ing the whole file is not good either: AIX grep has a line + # limit of 2048, but all sed's we know have understand at least 4000. + if sed -n 's,^#.*generated by automake.*,X,p' "$mf" | grep X >/dev/null 2>&1; then + dirpart=`AS_DIRNAME("$mf")` + else + continue + fi + # Extract the definition of DEPDIR, am__include, and am__quote + # from the Makefile without running 'make'. + DEPDIR=`sed -n 's/^DEPDIR = //p' < "$mf"` + test -z "$DEPDIR" && continue + am__include=`sed -n 's/^am__include = //p' < "$mf"` + test -z "$am__include" && continue + am__quote=`sed -n 's/^am__quote = //p' < "$mf"` + # Find all dependency output files, they are included files with + # $(DEPDIR) in their names. We invoke sed twice because it is the + # simplest approach to changing $(DEPDIR) to its actual value in the + # expansion. + for file in `sed -n " + s/^$am__include $am__quote\(.*(DEPDIR).*\)$am__quote"'$/\1/p' <"$mf" | \ + sed -e 's/\$(DEPDIR)/'"$DEPDIR"'/g'`; do + # Make sure the directory exists. + test -f "$dirpart/$file" && continue + fdir=`AS_DIRNAME(["$file"])` + AS_MKDIR_P([$dirpart/$fdir]) + # echo "creating $dirpart/$file" + echo '# dummy' > "$dirpart/$file" + done + done +} +])# _AM_OUTPUT_DEPENDENCY_COMMANDS + + +# AM_OUTPUT_DEPENDENCY_COMMANDS +# ----------------------------- +# This macro should only be invoked once -- use via AC_REQUIRE. +# +# This code is only required when automatic dependency tracking +# is enabled. FIXME. This creates each '.P' file that we will +# need in order to bootstrap the dependency handling code. +AC_DEFUN([AM_OUTPUT_DEPENDENCY_COMMANDS], +[AC_CONFIG_COMMANDS([depfiles], + [test x"$AMDEP_TRUE" != x"" || _AM_OUTPUT_DEPENDENCY_COMMANDS], + [AMDEP_TRUE="$AMDEP_TRUE" ac_aux_dir="$ac_aux_dir"]) +]) + +# Do all the work for Automake. -*- Autoconf -*- + +# Copyright (C) 1996-2013 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This macro actually does too much. Some checks are only needed if +# your package does certain things. But this isn't really a big deal. + +dnl Redefine AC_PROG_CC to automatically invoke _AM_PROG_CC_C_O. +m4_define([AC_PROG_CC], +m4_defn([AC_PROG_CC]) +[_AM_PROG_CC_C_O +]) + +# AM_INIT_AUTOMAKE(PACKAGE, VERSION, [NO-DEFINE]) +# AM_INIT_AUTOMAKE([OPTIONS]) +# ----------------------------------------------- +# The call with PACKAGE and VERSION arguments is the old style +# call (pre autoconf-2.50), which is being phased out. PACKAGE +# and VERSION should now be passed to AC_INIT and removed from +# the call to AM_INIT_AUTOMAKE. +# We support both call styles for the transition. After +# the next Automake release, Autoconf can make the AC_INIT +# arguments mandatory, and then we can depend on a new Autoconf +# release and drop the old call support. +AC_DEFUN([AM_INIT_AUTOMAKE], +[AC_PREREQ([2.65])dnl +dnl Autoconf wants to disallow AM_ names. We explicitly allow +dnl the ones we care about. +m4_pattern_allow([^AM_[A-Z]+FLAGS$])dnl +AC_REQUIRE([AM_SET_CURRENT_AUTOMAKE_VERSION])dnl +AC_REQUIRE([AC_PROG_INSTALL])dnl +if test "`cd $srcdir && pwd`" != "`pwd`"; then + # Use -I$(srcdir) only when $(srcdir) != ., so that make's output + # is not polluted with repeated "-I." + AC_SUBST([am__isrc], [' -I$(srcdir)'])_AM_SUBST_NOTMAKE([am__isrc])dnl + # test to see if srcdir already configured + if test -f $srcdir/config.status; then + AC_MSG_ERROR([source directory already configured; run "make distclean" there first]) + fi +fi + +# test whether we have cygpath +if test -z "$CYGPATH_W"; then + if (cygpath --version) >/dev/null 2>/dev/null; then + CYGPATH_W='cygpath -w' + else + CYGPATH_W=echo + fi +fi +AC_SUBST([CYGPATH_W]) + +# Define the identity of the package. +dnl Distinguish between old-style and new-style calls. +m4_ifval([$2], +[AC_DIAGNOSE([obsolete], + [$0: two- and three-arguments forms are deprecated.]) +m4_ifval([$3], [_AM_SET_OPTION([no-define])])dnl + AC_SUBST([PACKAGE], [$1])dnl + AC_SUBST([VERSION], [$2])], +[_AM_SET_OPTIONS([$1])dnl +dnl Diagnose old-style AC_INIT with new-style AM_AUTOMAKE_INIT. +m4_if( + m4_ifdef([AC_PACKAGE_NAME], [ok]):m4_ifdef([AC_PACKAGE_VERSION], [ok]), + [ok:ok],, + [m4_fatal([AC_INIT should be called with package and version arguments])])dnl + AC_SUBST([PACKAGE], ['AC_PACKAGE_TARNAME'])dnl + AC_SUBST([VERSION], ['AC_PACKAGE_VERSION'])])dnl + +_AM_IF_OPTION([no-define],, +[AC_DEFINE_UNQUOTED([PACKAGE], ["$PACKAGE"], [Name of package]) + AC_DEFINE_UNQUOTED([VERSION], ["$VERSION"], [Version number of package])])dnl + +# Some tools Automake needs. +AC_REQUIRE([AM_SANITY_CHECK])dnl +AC_REQUIRE([AC_ARG_PROGRAM])dnl +AM_MISSING_PROG([ACLOCAL], [aclocal-${am__api_version}]) +AM_MISSING_PROG([AUTOCONF], [autoconf]) +AM_MISSING_PROG([AUTOMAKE], [automake-${am__api_version}]) +AM_MISSING_PROG([AUTOHEADER], [autoheader]) +AM_MISSING_PROG([MAKEINFO], [makeinfo]) +AC_REQUIRE([AM_PROG_INSTALL_SH])dnl +AC_REQUIRE([AM_PROG_INSTALL_STRIP])dnl +AC_REQUIRE([AC_PROG_MKDIR_P])dnl +# For better backward compatibility. To be removed once Automake 1.9.x +# dies out for good. For more background, see: +# +# +AC_SUBST([mkdir_p], ['$(MKDIR_P)']) +# We need awk for the "check" target. The system "awk" is bad on +# some platforms. +AC_REQUIRE([AC_PROG_AWK])dnl +AC_REQUIRE([AC_PROG_MAKE_SET])dnl +AC_REQUIRE([AM_SET_LEADING_DOT])dnl +_AM_IF_OPTION([tar-ustar], [_AM_PROG_TAR([ustar])], + [_AM_IF_OPTION([tar-pax], [_AM_PROG_TAR([pax])], + [_AM_PROG_TAR([v7])])]) +_AM_IF_OPTION([no-dependencies],, +[AC_PROVIDE_IFELSE([AC_PROG_CC], + [_AM_DEPENDENCIES([CC])], + [m4_define([AC_PROG_CC], + m4_defn([AC_PROG_CC])[_AM_DEPENDENCIES([CC])])])dnl +AC_PROVIDE_IFELSE([AC_PROG_CXX], + [_AM_DEPENDENCIES([CXX])], + [m4_define([AC_PROG_CXX], + m4_defn([AC_PROG_CXX])[_AM_DEPENDENCIES([CXX])])])dnl +AC_PROVIDE_IFELSE([AC_PROG_OBJC], + [_AM_DEPENDENCIES([OBJC])], + [m4_define([AC_PROG_OBJC], + m4_defn([AC_PROG_OBJC])[_AM_DEPENDENCIES([OBJC])])])dnl +AC_PROVIDE_IFELSE([AC_PROG_OBJCXX], + [_AM_DEPENDENCIES([OBJCXX])], + [m4_define([AC_PROG_OBJCXX], + m4_defn([AC_PROG_OBJCXX])[_AM_DEPENDENCIES([OBJCXX])])])dnl +]) +AC_REQUIRE([AM_SILENT_RULES])dnl +dnl The testsuite driver may need to know about EXEEXT, so add the +dnl 'am__EXEEXT' conditional if _AM_COMPILER_EXEEXT was seen. This +dnl macro is hooked onto _AC_COMPILER_EXEEXT early, see below. +AC_CONFIG_COMMANDS_PRE(dnl +[m4_provide_if([_AM_COMPILER_EXEEXT], + [AM_CONDITIONAL([am__EXEEXT], [test -n "$EXEEXT"])])])dnl + +# POSIX will say in a future version that running "rm -f" with no argument +# is OK; and we want to be able to make that assumption in our Makefile +# recipes. So use an aggressive probe to check that the usage we want is +# actually supported "in the wild" to an acceptable degree. +# See automake bug#10828. +# To make any issue more visible, cause the running configure to be aborted +# by default if the 'rm' program in use doesn't match our expectations; the +# user can still override this though. +if rm -f && rm -fr && rm -rf; then : OK; else + cat >&2 <<'END' +Oops! + +Your 'rm' program seems unable to run without file operands specified +on the command line, even when the '-f' option is present. This is contrary +to the behaviour of most rm programs out there, and not conforming with +the upcoming POSIX standard: + +Please tell bug-automake@gnu.org about your system, including the value +of your $PATH and any error possibly output before this message. This +can help us improve future automake versions. + +END + if test x"$ACCEPT_INFERIOR_RM_PROGRAM" = x"yes"; then + echo 'Configuration will proceed anyway, since you have set the' >&2 + echo 'ACCEPT_INFERIOR_RM_PROGRAM variable to "yes"' >&2 + echo >&2 + else + cat >&2 <<'END' +Aborting the configuration process, to ensure you take notice of the issue. + +You can download and install GNU coreutils to get an 'rm' implementation +that behaves properly: . + +If you want to complete the configuration process using your problematic +'rm' anyway, export the environment variable ACCEPT_INFERIOR_RM_PROGRAM +to "yes", and re-run configure. + +END + AC_MSG_ERROR([Your 'rm' program is bad, sorry.]) + fi +fi +]) + +dnl Hook into '_AC_COMPILER_EXEEXT' early to learn its expansion. Do not +dnl add the conditional right here, as _AC_COMPILER_EXEEXT may be further +dnl mangled by Autoconf and run in a shell conditional statement. +m4_define([_AC_COMPILER_EXEEXT], +m4_defn([_AC_COMPILER_EXEEXT])[m4_provide([_AM_COMPILER_EXEEXT])]) + +# When config.status generates a header, we must update the stamp-h file. +# This file resides in the same directory as the config header +# that is generated. The stamp files are numbered to have different names. + +# Autoconf calls _AC_AM_CONFIG_HEADER_HOOK (when defined) in the +# loop where config.status creates the headers, so we can generate +# our stamp files there. +AC_DEFUN([_AC_AM_CONFIG_HEADER_HOOK], +[# Compute $1's index in $config_headers. +_am_arg=$1 +_am_stamp_count=1 +for _am_header in $config_headers :; do + case $_am_header in + $_am_arg | $_am_arg:* ) + break ;; + * ) + _am_stamp_count=`expr $_am_stamp_count + 1` ;; + esac +done +echo "timestamp for $_am_arg" >`AS_DIRNAME(["$_am_arg"])`/stamp-h[]$_am_stamp_count]) + +# Copyright (C) 2001-2013 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# AM_PROG_INSTALL_SH +# ------------------ +# Define $install_sh. +AC_DEFUN([AM_PROG_INSTALL_SH], +[AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl +if test x"${install_sh}" != xset; then + case $am_aux_dir in + *\ * | *\ *) + install_sh="\${SHELL} '$am_aux_dir/install-sh'" ;; + *) + install_sh="\${SHELL} $am_aux_dir/install-sh" + esac +fi +AC_SUBST([install_sh])]) + +# Copyright (C) 2003-2013 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# Check whether the underlying file-system supports filenames +# with a leading dot. For instance MS-DOS doesn't. +AC_DEFUN([AM_SET_LEADING_DOT], +[rm -rf .tst 2>/dev/null +mkdir .tst 2>/dev/null +if test -d .tst; then + am__leading_dot=. +else + am__leading_dot=_ +fi +rmdir .tst 2>/dev/null +AC_SUBST([am__leading_dot])]) + +# Check to see how 'make' treats includes. -*- Autoconf -*- + +# Copyright (C) 2001-2013 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# AM_MAKE_INCLUDE() +# ----------------- +# Check to see how make treats includes. +AC_DEFUN([AM_MAKE_INCLUDE], +[am_make=${MAKE-make} +cat > confinc << 'END' +am__doit: + @echo this is the am__doit target +.PHONY: am__doit +END +# If we don't find an include directive, just comment out the code. +AC_MSG_CHECKING([for style of include used by $am_make]) +am__include="#" +am__quote= +_am_result=none +# First try GNU make style include. +echo "include confinc" > confmf +# Ignore all kinds of additional output from 'make'. +case `$am_make -s -f confmf 2> /dev/null` in #( +*the\ am__doit\ target*) + am__include=include + am__quote= + _am_result=GNU + ;; +esac +# Now try BSD make style include. +if test "$am__include" = "#"; then + echo '.include "confinc"' > confmf + case `$am_make -s -f confmf 2> /dev/null` in #( + *the\ am__doit\ target*) + am__include=.include + am__quote="\"" + _am_result=BSD + ;; + esac +fi +AC_SUBST([am__include]) +AC_SUBST([am__quote]) +AC_MSG_RESULT([$_am_result]) +rm -f confinc confmf +]) + +# Fake the existence of programs that GNU maintainers use. -*- Autoconf -*- + +# Copyright (C) 1997-2013 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# AM_MISSING_PROG(NAME, PROGRAM) +# ------------------------------ +AC_DEFUN([AM_MISSING_PROG], +[AC_REQUIRE([AM_MISSING_HAS_RUN]) +$1=${$1-"${am_missing_run}$2"} +AC_SUBST($1)]) + +# AM_MISSING_HAS_RUN +# ------------------ +# Define MISSING if not defined so far and test if it is modern enough. +# If it is, set am_missing_run to use it, otherwise, to nothing. +AC_DEFUN([AM_MISSING_HAS_RUN], +[AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl +AC_REQUIRE_AUX_FILE([missing])dnl +if test x"${MISSING+set}" != xset; then + case $am_aux_dir in + *\ * | *\ *) + MISSING="\${SHELL} \"$am_aux_dir/missing\"" ;; + *) + MISSING="\${SHELL} $am_aux_dir/missing" ;; + esac +fi +# Use eval to expand $SHELL +if eval "$MISSING --is-lightweight"; then + am_missing_run="$MISSING " +else + am_missing_run= + AC_MSG_WARN(['missing' script is too old or missing]) +fi +]) + +# Helper functions for option handling. -*- Autoconf -*- + +# Copyright (C) 2001-2013 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# _AM_MANGLE_OPTION(NAME) +# ----------------------- +AC_DEFUN([_AM_MANGLE_OPTION], +[[_AM_OPTION_]m4_bpatsubst($1, [[^a-zA-Z0-9_]], [_])]) + +# _AM_SET_OPTION(NAME) +# -------------------- +# Set option NAME. Presently that only means defining a flag for this option. +AC_DEFUN([_AM_SET_OPTION], +[m4_define(_AM_MANGLE_OPTION([$1]), [1])]) + +# _AM_SET_OPTIONS(OPTIONS) +# ------------------------ +# OPTIONS is a space-separated list of Automake options. +AC_DEFUN([_AM_SET_OPTIONS], +[m4_foreach_w([_AM_Option], [$1], [_AM_SET_OPTION(_AM_Option)])]) + +# _AM_IF_OPTION(OPTION, IF-SET, [IF-NOT-SET]) +# ------------------------------------------- +# Execute IF-SET if OPTION is set, IF-NOT-SET otherwise. +AC_DEFUN([_AM_IF_OPTION], +[m4_ifset(_AM_MANGLE_OPTION([$1]), [$2], [$3])]) + +# Copyright (C) 1999-2013 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# _AM_PROG_CC_C_O +# --------------- +# Like AC_PROG_CC_C_O, but changed for automake. We rewrite AC_PROG_CC +# to automatically call this. +AC_DEFUN([_AM_PROG_CC_C_O], +[AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl +AC_REQUIRE_AUX_FILE([compile])dnl +AC_LANG_PUSH([C])dnl +AC_CACHE_CHECK( + [whether $CC understands -c and -o together], + [am_cv_prog_cc_c_o], + [AC_LANG_CONFTEST([AC_LANG_PROGRAM([])]) + # Make sure it works both with $CC and with simple cc. + # Following AC_PROG_CC_C_O, we do the test twice because some + # compilers refuse to overwrite an existing .o file with -o, + # though they will create one. + am_cv_prog_cc_c_o=yes + for am_i in 1 2; do + if AM_RUN_LOG([$CC -c conftest.$ac_ext -o conftest2.$ac_objext]) \ + && test -f conftest2.$ac_objext; then + : OK + else + am_cv_prog_cc_c_o=no + break + fi + done + rm -f core conftest* + unset am_i]) +if test "$am_cv_prog_cc_c_o" != yes; then + # Losing compiler, so override with the script. + # FIXME: It is wrong to rewrite CC. + # But if we don't then we get into trouble of one sort or another. + # A longer-term fix would be to have automake use am__CC in this case, + # and then we could set am__CC="\$(top_srcdir)/compile \$(CC)" + CC="$am_aux_dir/compile $CC" +fi +AC_LANG_POP([C])]) + +# For backward compatibility. +AC_DEFUN_ONCE([AM_PROG_CC_C_O], [AC_REQUIRE([AC_PROG_CC])]) + +# Copyright (C) 1999-2013 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + + +# AM_PATH_PYTHON([MINIMUM-VERSION], [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) +# --------------------------------------------------------------------------- +# Adds support for distributing Python modules and packages. To +# install modules, copy them to $(pythondir), using the python_PYTHON +# automake variable. To install a package with the same name as the +# automake package, install to $(pkgpythondir), or use the +# pkgpython_PYTHON automake variable. +# +# The variables $(pyexecdir) and $(pkgpyexecdir) are provided as +# locations to install python extension modules (shared libraries). +# Another macro is required to find the appropriate flags to compile +# extension modules. +# +# If your package is configured with a different prefix to python, +# users will have to add the install directory to the PYTHONPATH +# environment variable, or create a .pth file (see the python +# documentation for details). +# +# If the MINIMUM-VERSION argument is passed, AM_PATH_PYTHON will +# cause an error if the version of python installed on the system +# doesn't meet the requirement. MINIMUM-VERSION should consist of +# numbers and dots only. +AC_DEFUN([AM_PATH_PYTHON], + [ + dnl Find a Python interpreter. Python versions prior to 2.0 are not + dnl supported. (2.0 was released on October 16, 2000). + m4_define_default([_AM_PYTHON_INTERPRETER_LIST], +[python python2 python3 python3.3 python3.2 python3.1 python3.0 python2.7 dnl + python2.6 python2.5 python2.4 python2.3 python2.2 python2.1 python2.0]) + + AC_ARG_VAR([PYTHON], [the Python interpreter]) + + m4_if([$1],[],[ + dnl No version check is needed. + # Find any Python interpreter. + if test -z "$PYTHON"; then + AC_PATH_PROGS([PYTHON], _AM_PYTHON_INTERPRETER_LIST, :) + fi + am_display_PYTHON=python + ], [ + dnl A version check is needed. + if test -n "$PYTHON"; then + # If the user set $PYTHON, use it and don't search something else. + AC_MSG_CHECKING([whether $PYTHON version is >= $1]) + AM_PYTHON_CHECK_VERSION([$PYTHON], [$1], + [AC_MSG_RESULT([yes])], + [AC_MSG_RESULT([no]) + AC_MSG_ERROR([Python interpreter is too old])]) + am_display_PYTHON=$PYTHON + else + # Otherwise, try each interpreter until we find one that satisfies + # VERSION. + AC_CACHE_CHECK([for a Python interpreter with version >= $1], + [am_cv_pathless_PYTHON],[ + for am_cv_pathless_PYTHON in _AM_PYTHON_INTERPRETER_LIST none; do + test "$am_cv_pathless_PYTHON" = none && break + AM_PYTHON_CHECK_VERSION([$am_cv_pathless_PYTHON], [$1], [break]) + done]) + # Set $PYTHON to the absolute path of $am_cv_pathless_PYTHON. + if test "$am_cv_pathless_PYTHON" = none; then + PYTHON=: + else + AC_PATH_PROG([PYTHON], [$am_cv_pathless_PYTHON]) + fi + am_display_PYTHON=$am_cv_pathless_PYTHON + fi + ]) + + if test "$PYTHON" = :; then + dnl Run any user-specified action, or abort. + m4_default([$3], [AC_MSG_ERROR([no suitable Python interpreter found])]) + else + + dnl Query Python for its version number. Getting [:3] seems to be + dnl the best way to do this; it's what "site.py" does in the standard + dnl library. + + AC_CACHE_CHECK([for $am_display_PYTHON version], [am_cv_python_version], + [am_cv_python_version=`$PYTHON -c "import sys; sys.stdout.write(sys.version[[:3]])"`]) + AC_SUBST([PYTHON_VERSION], [$am_cv_python_version]) + + dnl Use the values of $prefix and $exec_prefix for the corresponding + dnl values of PYTHON_PREFIX and PYTHON_EXEC_PREFIX. These are made + dnl distinct variables so they can be overridden if need be. However, + dnl general consensus is that you shouldn't need this ability. + + AC_SUBST([PYTHON_PREFIX], ['${prefix}']) + AC_SUBST([PYTHON_EXEC_PREFIX], ['${exec_prefix}']) + + dnl At times (like when building shared libraries) you may want + dnl to know which OS platform Python thinks this is. + + AC_CACHE_CHECK([for $am_display_PYTHON platform], [am_cv_python_platform], + [am_cv_python_platform=`$PYTHON -c "import sys; sys.stdout.write(sys.platform)"`]) + AC_SUBST([PYTHON_PLATFORM], [$am_cv_python_platform]) + + # Just factor out some code duplication. + am_python_setup_sysconfig="\ +import sys +# Prefer sysconfig over distutils.sysconfig, for better compatibility +# with python 3.x. See automake bug#10227. +try: + import sysconfig +except ImportError: + can_use_sysconfig = 0 +else: + can_use_sysconfig = 1 +# Can't use sysconfig in CPython 2.7, since it's broken in virtualenvs: +# +try: + from platform import python_implementation + if python_implementation() == 'CPython' and sys.version[[:3]] == '2.7': + can_use_sysconfig = 0 +except ImportError: + pass" + + dnl Set up 4 directories: + + dnl pythondir -- where to install python scripts. This is the + dnl site-packages directory, not the python standard library + dnl directory like in previous automake betas. This behavior + dnl is more consistent with lispdir.m4 for example. + dnl Query distutils for this directory. + AC_CACHE_CHECK([for $am_display_PYTHON script directory], + [am_cv_python_pythondir], + [if test "x$prefix" = xNONE + then + am_py_prefix=$ac_default_prefix + else + am_py_prefix=$prefix + fi + am_cv_python_pythondir=`$PYTHON -c " +$am_python_setup_sysconfig +if can_use_sysconfig: + sitedir = sysconfig.get_path('purelib', vars={'base':'$am_py_prefix'}) +else: + from distutils import sysconfig + sitedir = sysconfig.get_python_lib(0, 0, prefix='$am_py_prefix') +sys.stdout.write(sitedir)"` + case $am_cv_python_pythondir in + $am_py_prefix*) + am__strip_prefix=`echo "$am_py_prefix" | sed 's|.|.|g'` + am_cv_python_pythondir=`echo "$am_cv_python_pythondir" | sed "s,^$am__strip_prefix,$PYTHON_PREFIX,"` + ;; + *) + case $am_py_prefix in + /usr|/System*) ;; + *) + am_cv_python_pythondir=$PYTHON_PREFIX/lib/python$PYTHON_VERSION/site-packages + ;; + esac + ;; + esac + ]) + AC_SUBST([pythondir], [$am_cv_python_pythondir]) + + dnl pkgpythondir -- $PACKAGE directory under pythondir. Was + dnl PYTHON_SITE_PACKAGE in previous betas, but this naming is + dnl more consistent with the rest of automake. + + AC_SUBST([pkgpythondir], [\${pythondir}/$PACKAGE]) + + dnl pyexecdir -- directory for installing python extension modules + dnl (shared libraries) + dnl Query distutils for this directory. + AC_CACHE_CHECK([for $am_display_PYTHON extension module directory], + [am_cv_python_pyexecdir], + [if test "x$exec_prefix" = xNONE + then + am_py_exec_prefix=$am_py_prefix + else + am_py_exec_prefix=$exec_prefix + fi + am_cv_python_pyexecdir=`$PYTHON -c " +$am_python_setup_sysconfig +if can_use_sysconfig: + sitedir = sysconfig.get_path('platlib', vars={'platbase':'$am_py_prefix'}) +else: + from distutils import sysconfig + sitedir = sysconfig.get_python_lib(1, 0, prefix='$am_py_prefix') +sys.stdout.write(sitedir)"` + case $am_cv_python_pyexecdir in + $am_py_exec_prefix*) + am__strip_prefix=`echo "$am_py_exec_prefix" | sed 's|.|.|g'` + am_cv_python_pyexecdir=`echo "$am_cv_python_pyexecdir" | sed "s,^$am__strip_prefix,$PYTHON_EXEC_PREFIX,"` + ;; + *) + case $am_py_exec_prefix in + /usr|/System*) ;; + *) + am_cv_python_pyexecdir=$PYTHON_EXEC_PREFIX/lib/python$PYTHON_VERSION/site-packages + ;; + esac + ;; + esac + ]) + AC_SUBST([pyexecdir], [$am_cv_python_pyexecdir]) + + dnl pkgpyexecdir -- $(pyexecdir)/$(PACKAGE) + + AC_SUBST([pkgpyexecdir], [\${pyexecdir}/$PACKAGE]) + + dnl Run any user-specified action. + $2 + fi + +]) + + +# AM_PYTHON_CHECK_VERSION(PROG, VERSION, [ACTION-IF-TRUE], [ACTION-IF-FALSE]) +# --------------------------------------------------------------------------- +# Run ACTION-IF-TRUE if the Python interpreter PROG has version >= VERSION. +# Run ACTION-IF-FALSE otherwise. +# This test uses sys.hexversion instead of the string equivalent (first +# word of sys.version), in order to cope with versions such as 2.2c1. +# This supports Python 2.0 or higher. (2.0 was released on October 16, 2000). +AC_DEFUN([AM_PYTHON_CHECK_VERSION], + [prog="import sys +# split strings by '.' and convert to numeric. Append some zeros +# because we need at least 4 digits for the hex conversion. +# map returns an iterator in Python 3.0 and a list in 2.x +minver = list(map(int, '$2'.split('.'))) + [[0, 0, 0]] +minverhex = 0 +# xrange is not present in Python 3.0 and range returns an iterator +for i in list(range(0, 4)): minverhex = (minverhex << 8) + minver[[i]] +sys.exit(sys.hexversion < minverhex)" + AS_IF([AM_RUN_LOG([$1 -c "$prog"])], [$3], [$4])]) + +# Copyright (C) 2001-2013 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# AM_RUN_LOG(COMMAND) +# ------------------- +# Run COMMAND, save the exit status in ac_status, and log it. +# (This has been adapted from Autoconf's _AC_RUN_LOG macro.) +AC_DEFUN([AM_RUN_LOG], +[{ echo "$as_me:$LINENO: $1" >&AS_MESSAGE_LOG_FD + ($1) >&AS_MESSAGE_LOG_FD 2>&AS_MESSAGE_LOG_FD + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&AS_MESSAGE_LOG_FD + (exit $ac_status); }]) + +# Check to make sure that the build environment is sane. -*- Autoconf -*- + +# Copyright (C) 1996-2013 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# AM_SANITY_CHECK +# --------------- +AC_DEFUN([AM_SANITY_CHECK], +[AC_MSG_CHECKING([whether build environment is sane]) +# Reject unsafe characters in $srcdir or the absolute working directory +# name. Accept space and tab only in the latter. +am_lf=' +' +case `pwd` in + *[[\\\"\#\$\&\'\`$am_lf]]*) + AC_MSG_ERROR([unsafe absolute working directory name]);; +esac +case $srcdir in + *[[\\\"\#\$\&\'\`$am_lf\ \ ]]*) + AC_MSG_ERROR([unsafe srcdir value: '$srcdir']);; +esac + +# Do 'set' in a subshell so we don't clobber the current shell's +# arguments. Must try -L first in case configure is actually a +# symlink; some systems play weird games with the mod time of symlinks +# (eg FreeBSD returns the mod time of the symlink's containing +# directory). +if ( + am_has_slept=no + for am_try in 1 2; do + echo "timestamp, slept: $am_has_slept" > conftest.file + set X `ls -Lt "$srcdir/configure" conftest.file 2> /dev/null` + if test "$[*]" = "X"; then + # -L didn't work. + set X `ls -t "$srcdir/configure" conftest.file` + fi + if test "$[*]" != "X $srcdir/configure conftest.file" \ + && test "$[*]" != "X conftest.file $srcdir/configure"; then + + # If neither matched, then we have a broken ls. This can happen + # if, for instance, CONFIG_SHELL is bash and it inherits a + # broken ls alias from the environment. This has actually + # happened. Such a system could not be considered "sane". + AC_MSG_ERROR([ls -t appears to fail. Make sure there is not a broken + alias in your environment]) + fi + if test "$[2]" = conftest.file || test $am_try -eq 2; then + break + fi + # Just in case. + sleep 1 + am_has_slept=yes + done + test "$[2]" = conftest.file + ) +then + # Ok. + : +else + AC_MSG_ERROR([newly created file is older than distributed files! +Check your system clock]) +fi +AC_MSG_RESULT([yes]) +# If we didn't sleep, we still need to ensure time stamps of config.status and +# generated files are strictly newer. +am_sleep_pid= +if grep 'slept: no' conftest.file >/dev/null 2>&1; then + ( sleep 1 ) & + am_sleep_pid=$! +fi +AC_CONFIG_COMMANDS_PRE( + [AC_MSG_CHECKING([that generated files are newer than configure]) + if test -n "$am_sleep_pid"; then + # Hide warnings about reused PIDs. + wait $am_sleep_pid 2>/dev/null + fi + AC_MSG_RESULT([done])]) +rm -f conftest.file +]) + +# Copyright (C) 2009-2013 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# AM_SILENT_RULES([DEFAULT]) +# -------------------------- +# Enable less verbose build rules; with the default set to DEFAULT +# ("yes" being less verbose, "no" or empty being verbose). +AC_DEFUN([AM_SILENT_RULES], +[AC_ARG_ENABLE([silent-rules], [dnl +AS_HELP_STRING( + [--enable-silent-rules], + [less verbose build output (undo: "make V=1")]) +AS_HELP_STRING( + [--disable-silent-rules], + [verbose build output (undo: "make V=0")])dnl +]) +case $enable_silent_rules in @%:@ ((( + yes) AM_DEFAULT_VERBOSITY=0;; + no) AM_DEFAULT_VERBOSITY=1;; + *) AM_DEFAULT_VERBOSITY=m4_if([$1], [yes], [0], [1]);; +esac +dnl +dnl A few 'make' implementations (e.g., NonStop OS and NextStep) +dnl do not support nested variable expansions. +dnl See automake bug#9928 and bug#10237. +am_make=${MAKE-make} +AC_CACHE_CHECK([whether $am_make supports nested variables], + [am_cv_make_support_nested_variables], + [if AS_ECHO([['TRUE=$(BAR$(V)) +BAR0=false +BAR1=true +V=1 +am__doit: + @$(TRUE) +.PHONY: am__doit']]) | $am_make -f - >/dev/null 2>&1; then + am_cv_make_support_nested_variables=yes +else + am_cv_make_support_nested_variables=no +fi]) +if test $am_cv_make_support_nested_variables = yes; then + dnl Using '$V' instead of '$(V)' breaks IRIX make. + AM_V='$(V)' + AM_DEFAULT_V='$(AM_DEFAULT_VERBOSITY)' +else + AM_V=$AM_DEFAULT_VERBOSITY + AM_DEFAULT_V=$AM_DEFAULT_VERBOSITY +fi +AC_SUBST([AM_V])dnl +AM_SUBST_NOTMAKE([AM_V])dnl +AC_SUBST([AM_DEFAULT_V])dnl +AM_SUBST_NOTMAKE([AM_DEFAULT_V])dnl +AC_SUBST([AM_DEFAULT_VERBOSITY])dnl +AM_BACKSLASH='\' +AC_SUBST([AM_BACKSLASH])dnl +_AM_SUBST_NOTMAKE([AM_BACKSLASH])dnl +]) + +# Copyright (C) 2001-2013 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# AM_PROG_INSTALL_STRIP +# --------------------- +# One issue with vendor 'install' (even GNU) is that you can't +# specify the program used to strip binaries. This is especially +# annoying in cross-compiling environments, where the build's strip +# is unlikely to handle the host's binaries. +# Fortunately install-sh will honor a STRIPPROG variable, so we +# always use install-sh in "make install-strip", and initialize +# STRIPPROG with the value of the STRIP variable (set by the user). +AC_DEFUN([AM_PROG_INSTALL_STRIP], +[AC_REQUIRE([AM_PROG_INSTALL_SH])dnl +# Installed binaries are usually stripped using 'strip' when the user +# run "make install-strip". However 'strip' might not be the right +# tool to use in cross-compilation environments, therefore Automake +# will honor the 'STRIP' environment variable to overrule this program. +dnl Don't test for $cross_compiling = yes, because it might be 'maybe'. +if test "$cross_compiling" != no; then + AC_CHECK_TOOL([STRIP], [strip], :) +fi +INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s" +AC_SUBST([INSTALL_STRIP_PROGRAM])]) + +# Copyright (C) 2006-2013 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# _AM_SUBST_NOTMAKE(VARIABLE) +# --------------------------- +# Prevent Automake from outputting VARIABLE = @VARIABLE@ in Makefile.in. +# This macro is traced by Automake. +AC_DEFUN([_AM_SUBST_NOTMAKE]) + +# AM_SUBST_NOTMAKE(VARIABLE) +# -------------------------- +# Public sister of _AM_SUBST_NOTMAKE. +AC_DEFUN([AM_SUBST_NOTMAKE], [_AM_SUBST_NOTMAKE($@)]) + +# Check how to create a tarball. -*- Autoconf -*- + +# Copyright (C) 2004-2013 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# _AM_PROG_TAR(FORMAT) +# -------------------- +# Check how to create a tarball in format FORMAT. +# FORMAT should be one of 'v7', 'ustar', or 'pax'. +# +# Substitute a variable $(am__tar) that is a command +# writing to stdout a FORMAT-tarball containing the directory +# $tardir. +# tardir=directory && $(am__tar) > result.tar +# +# Substitute a variable $(am__untar) that extract such +# a tarball read from stdin. +# $(am__untar) < result.tar +# +AC_DEFUN([_AM_PROG_TAR], +[# Always define AMTAR for backward compatibility. Yes, it's still used +# in the wild :-( We should find a proper way to deprecate it ... +AC_SUBST([AMTAR], ['$${TAR-tar}']) + +# We'll loop over all known methods to create a tar archive until one works. +_am_tools='gnutar m4_if([$1], [ustar], [plaintar]) pax cpio none' + +m4_if([$1], [v7], + [am__tar='$${TAR-tar} chof - "$$tardir"' am__untar='$${TAR-tar} xf -'], + + [m4_case([$1], + [ustar], + [# The POSIX 1988 'ustar' format is defined with fixed-size fields. + # There is notably a 21 bits limit for the UID and the GID. In fact, + # the 'pax' utility can hang on bigger UID/GID (see automake bug#8343 + # and bug#13588). + am_max_uid=2097151 # 2^21 - 1 + am_max_gid=$am_max_uid + # The $UID and $GID variables are not portable, so we need to resort + # to the POSIX-mandated id(1) utility. Errors in the 'id' calls + # below are definitely unexpected, so allow the users to see them + # (that is, avoid stderr redirection). + am_uid=`id -u || echo unknown` + am_gid=`id -g || echo unknown` + AC_MSG_CHECKING([whether UID '$am_uid' is supported by ustar format]) + if test $am_uid -le $am_max_uid; then + AC_MSG_RESULT([yes]) + else + AC_MSG_RESULT([no]) + _am_tools=none + fi + AC_MSG_CHECKING([whether GID '$am_gid' is supported by ustar format]) + if test $am_gid -le $am_max_gid; then + AC_MSG_RESULT([yes]) + else + AC_MSG_RESULT([no]) + _am_tools=none + fi], + + [pax], + [], + + [m4_fatal([Unknown tar format])]) + + AC_MSG_CHECKING([how to create a $1 tar archive]) + + # Go ahead even if we have the value already cached. We do so because we + # need to set the values for the 'am__tar' and 'am__untar' variables. + _am_tools=${am_cv_prog_tar_$1-$_am_tools} + + for _am_tool in $_am_tools; do + case $_am_tool in + gnutar) + for _am_tar in tar gnutar gtar; do + AM_RUN_LOG([$_am_tar --version]) && break + done + am__tar="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$$tardir"' + am__tar_="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$tardir"' + am__untar="$_am_tar -xf -" + ;; + plaintar) + # Must skip GNU tar: if it does not support --format= it doesn't create + # ustar tarball either. + (tar --version) >/dev/null 2>&1 && continue + am__tar='tar chf - "$$tardir"' + am__tar_='tar chf - "$tardir"' + am__untar='tar xf -' + ;; + pax) + am__tar='pax -L -x $1 -w "$$tardir"' + am__tar_='pax -L -x $1 -w "$tardir"' + am__untar='pax -r' + ;; + cpio) + am__tar='find "$$tardir" -print | cpio -o -H $1 -L' + am__tar_='find "$tardir" -print | cpio -o -H $1 -L' + am__untar='cpio -i -H $1 -d' + ;; + none) + am__tar=false + am__tar_=false + am__untar=false + ;; + esac + + # If the value was cached, stop now. We just wanted to have am__tar + # and am__untar set. + test -n "${am_cv_prog_tar_$1}" && break + + # tar/untar a dummy directory, and stop if the command works. + rm -rf conftest.dir + mkdir conftest.dir + echo GrepMe > conftest.dir/file + AM_RUN_LOG([tardir=conftest.dir && eval $am__tar_ >conftest.tar]) + rm -rf conftest.dir + if test -s conftest.tar; then + AM_RUN_LOG([$am__untar /dev/null 2>&1 && break + fi + done + rm -rf conftest.dir + + AC_CACHE_VAL([am_cv_prog_tar_$1], [am_cv_prog_tar_$1=$_am_tool]) + AC_MSG_RESULT([$am_cv_prog_tar_$1])]) + +AC_SUBST([am__tar]) +AC_SUBST([am__untar]) +]) # _AM_PROG_TAR + +m4_include([m4/ax_boost_asio.m4]) +m4_include([m4/ax_boost_base.m4]) +m4_include([m4/ax_boost_system.m4]) +m4_include([m4/ax_boost_thread.m4]) +m4_include([m4/ax_check_compile_flag.m4]) +m4_include([m4/ax_cxx_compile_stdcxx_11.m4]) +m4_include([m4/ax_have_epoll.m4]) +m4_include([m4/ax_python_devel.m4]) +m4_include([m4/libtool.m4]) +m4_include([m4/libxml2.m4]) +m4_include([m4/ltoptions.m4]) +m4_include([m4/ltsugar.m4]) +m4_include([m4/ltversion.m4]) +m4_include([m4/lt~obsolete.m4]) diff --git a/android-config b/android-config new file mode 100755 index 0000000..9828822 --- /dev/null +++ b/android-config @@ -0,0 +1,47 @@ +#!/bin/sh +# +# nghttp2 - HTTP/2 C Library +# +# Copyright (c) 2013 Tatsuhiro Tsujikawa +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +if [ -z "$ANDROID_HOME" ]; then + echo 'No $ANDROID_HOME specified.' + exit 1 +fi +PREFIX=$ANDROID_HOME/usr/local +TOOLCHAIN=$ANDROID_HOME/toolchain +PATH=$TOOLCHAIN/bin:$PATH + +./configure \ + --disable-shared \ + --host=arm-linux-androideabi \ + --build=`dpkg-architecture -qDEB_BUILD_GNU_TYPE` \ + --with-xml-prefix="$PREFIX" \ + --without-libxml2 \ + --disable-python-bindings \ + --disable-examples \ + --enable-werror \ + CC=clang \ + CXX=clang++ \ + CPPFLAGS="-fPIE -I$PREFIX/include" \ + PKG_CONFIG_LIBDIR="$PREFIX/lib/pkgconfig" \ + LDFLAGS="-fPIE -pie -L$PREFIX/lib" diff --git a/android-make b/android-make new file mode 100755 index 0000000..375045a --- /dev/null +++ b/android-make @@ -0,0 +1,33 @@ +#!/bin/sh +# +# nghttp2 - HTTP/2 C Library +# +# Copyright (c) 2013 Tatsuhiro Tsujikawa +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +if [ -z "$ANDROID_HOME" ]; then + echo 'No $ANDROID_HOME specified.' + exit 1 +fi +TOOLCHAIN=$ANDROID_HOME/toolchain +PATH=$TOOLCHAIN/bin:$PATH + +make "$@" diff --git a/compile b/compile new file mode 100755 index 0000000..531136b --- /dev/null +++ b/compile @@ -0,0 +1,347 @@ +#! /bin/sh +# Wrapper for compilers which do not understand '-c -o'. + +scriptversion=2012-10-14.11; # UTC + +# Copyright (C) 1999-2013 Free Software Foundation, Inc. +# Written by Tom Tromey . +# +# 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 2, 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 . + +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +# This file is maintained in Automake, please report +# bugs to or send patches to +# . + +nl=' +' + +# We need space, tab and new line, in precisely that order. Quoting is +# there to prevent tools from complaining about whitespace usage. +IFS=" "" $nl" + +file_conv= + +# func_file_conv build_file lazy +# Convert a $build file to $host form and store it in $file +# Currently only supports Windows hosts. If the determined conversion +# type is listed in (the comma separated) LAZY, no conversion will +# take place. +func_file_conv () +{ + file=$1 + case $file in + / | /[!/]*) # absolute file, and not a UNC file + if test -z "$file_conv"; then + # lazily determine how to convert abs files + case `uname -s` in + MINGW*) + file_conv=mingw + ;; + CYGWIN*) + file_conv=cygwin + ;; + *) + file_conv=wine + ;; + esac + fi + case $file_conv/,$2, in + *,$file_conv,*) + ;; + mingw/*) + file=`cmd //C echo "$file " | sed -e 's/"\(.*\) " *$/\1/'` + ;; + cygwin/*) + file=`cygpath -m "$file" || echo "$file"` + ;; + wine/*) + file=`winepath -w "$file" || echo "$file"` + ;; + esac + ;; + esac +} + +# func_cl_dashL linkdir +# Make cl look for libraries in LINKDIR +func_cl_dashL () +{ + func_file_conv "$1" + if test -z "$lib_path"; then + lib_path=$file + else + lib_path="$lib_path;$file" + fi + linker_opts="$linker_opts -LIBPATH:$file" +} + +# func_cl_dashl library +# Do a library search-path lookup for cl +func_cl_dashl () +{ + lib=$1 + found=no + save_IFS=$IFS + IFS=';' + for dir in $lib_path $LIB + do + IFS=$save_IFS + if $shared && test -f "$dir/$lib.dll.lib"; then + found=yes + lib=$dir/$lib.dll.lib + break + fi + if test -f "$dir/$lib.lib"; then + found=yes + lib=$dir/$lib.lib + break + fi + if test -f "$dir/lib$lib.a"; then + found=yes + lib=$dir/lib$lib.a + break + fi + done + IFS=$save_IFS + + if test "$found" != yes; then + lib=$lib.lib + fi +} + +# func_cl_wrapper cl arg... +# Adjust compile command to suit cl +func_cl_wrapper () +{ + # Assume a capable shell + lib_path= + shared=: + linker_opts= + for arg + do + if test -n "$eat"; then + eat= + else + case $1 in + -o) + # configure might choose to run compile as 'compile cc -o foo foo.c'. + eat=1 + case $2 in + *.o | *.[oO][bB][jJ]) + func_file_conv "$2" + set x "$@" -Fo"$file" + shift + ;; + *) + func_file_conv "$2" + set x "$@" -Fe"$file" + shift + ;; + esac + ;; + -I) + eat=1 + func_file_conv "$2" mingw + set x "$@" -I"$file" + shift + ;; + -I*) + func_file_conv "${1#-I}" mingw + set x "$@" -I"$file" + shift + ;; + -l) + eat=1 + func_cl_dashl "$2" + set x "$@" "$lib" + shift + ;; + -l*) + func_cl_dashl "${1#-l}" + set x "$@" "$lib" + shift + ;; + -L) + eat=1 + func_cl_dashL "$2" + ;; + -L*) + func_cl_dashL "${1#-L}" + ;; + -static) + shared=false + ;; + -Wl,*) + arg=${1#-Wl,} + save_ifs="$IFS"; IFS=',' + for flag in $arg; do + IFS="$save_ifs" + linker_opts="$linker_opts $flag" + done + IFS="$save_ifs" + ;; + -Xlinker) + eat=1 + linker_opts="$linker_opts $2" + ;; + -*) + set x "$@" "$1" + shift + ;; + *.cc | *.CC | *.cxx | *.CXX | *.[cC]++) + func_file_conv "$1" + set x "$@" -Tp"$file" + shift + ;; + *.c | *.cpp | *.CPP | *.lib | *.LIB | *.Lib | *.OBJ | *.obj | *.[oO]) + func_file_conv "$1" mingw + set x "$@" "$file" + shift + ;; + *) + set x "$@" "$1" + shift + ;; + esac + fi + shift + done + if test -n "$linker_opts"; then + linker_opts="-link$linker_opts" + fi + exec "$@" $linker_opts + exit 1 +} + +eat= + +case $1 in + '') + echo "$0: No command. Try '$0 --help' for more information." 1>&2 + exit 1; + ;; + -h | --h*) + cat <<\EOF +Usage: compile [--help] [--version] PROGRAM [ARGS] + +Wrapper for compilers which do not understand '-c -o'. +Remove '-o dest.o' from ARGS, run PROGRAM with the remaining +arguments, and rename the output as expected. + +If you are trying to build a whole package this is not the +right script to run: please start by reading the file 'INSTALL'. + +Report bugs to . +EOF + exit $? + ;; + -v | --v*) + echo "compile $scriptversion" + exit $? + ;; + cl | *[/\\]cl | cl.exe | *[/\\]cl.exe ) + func_cl_wrapper "$@" # Doesn't return... + ;; +esac + +ofile= +cfile= + +for arg +do + if test -n "$eat"; then + eat= + else + case $1 in + -o) + # configure might choose to run compile as 'compile cc -o foo foo.c'. + # So we strip '-o arg' only if arg is an object. + eat=1 + case $2 in + *.o | *.obj) + ofile=$2 + ;; + *) + set x "$@" -o "$2" + shift + ;; + esac + ;; + *.c) + cfile=$1 + set x "$@" "$1" + shift + ;; + *) + set x "$@" "$1" + shift + ;; + esac + fi + shift +done + +if test -z "$ofile" || test -z "$cfile"; then + # If no '-o' option was seen then we might have been invoked from a + # pattern rule where we don't need one. That is ok -- this is a + # normal compilation that the losing compiler can handle. If no + # '.c' file was seen then we are probably linking. That is also + # ok. + exec "$@" +fi + +# Name of file we expect compiler to create. +cofile=`echo "$cfile" | sed 's|^.*[\\/]||; s|^[a-zA-Z]:||; s/\.c$/.o/'` + +# Create the lock directory. +# Note: use '[/\\:.-]' here to ensure that we don't use the same name +# that we are using for the .o file. Also, base the name on the expected +# object file name, since that is what matters with a parallel build. +lockdir=`echo "$cofile" | sed -e 's|[/\\:.-]|_|g'`.d +while true; do + if mkdir "$lockdir" >/dev/null 2>&1; then + break + fi + sleep 1 +done +# FIXME: race condition here if user kills between mkdir and trap. +trap "rmdir '$lockdir'; exit 1" 1 2 15 + +# Run the compile. +"$@" +ret=$? + +if test -f "$cofile"; then + test "$cofile" = "$ofile" || mv "$cofile" "$ofile" +elif test -f "${cofile}bj"; then + test "${cofile}bj" = "$ofile" || mv "${cofile}bj" "$ofile" +fi + +rmdir "$lockdir" +exit $ret + +# Local Variables: +# mode: shell-script +# sh-indentation: 2 +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "scriptversion=" +# time-stamp-format: "%:y-%02m-%02d.%02H" +# time-stamp-time-zone: "UTC" +# time-stamp-end: "; # UTC" +# End: diff --git a/config.guess b/config.guess new file mode 100755 index 0000000..1f5c50c --- /dev/null +++ b/config.guess @@ -0,0 +1,1420 @@ +#! /bin/sh +# Attempt to guess a canonical system name. +# Copyright 1992-2014 Free Software Foundation, Inc. + +timestamp='2014-03-23' + +# This file 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 . +# +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that +# program. This Exception is an additional permission under section 7 +# of the GNU General Public License, version 3 ("GPLv3"). +# +# Originally written by Per Bothner. +# +# You can get the latest version of this script from: +# http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess;hb=HEAD +# +# Please send patches with a ChangeLog entry to config-patches@gnu.org. + + +me=`echo "$0" | sed -e 's,.*/,,'` + +usage="\ +Usage: $0 [OPTION] + +Output the configuration name of the system \`$me' is run on. + +Operation modes: + -h, --help print this help, then exit + -t, --time-stamp print date of last modification, then exit + -v, --version print version number, then exit + +Report bugs and patches to ." + +version="\ +GNU config.guess ($timestamp) + +Originally written by Per Bothner. +Copyright 1992-2014 Free Software Foundation, Inc. + +This is free software; see the source for copying conditions. There is NO +warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." + +help=" +Try \`$me --help' for more information." + +# Parse command line +while test $# -gt 0 ; do + case $1 in + --time-stamp | --time* | -t ) + echo "$timestamp" ; exit ;; + --version | -v ) + echo "$version" ; exit ;; + --help | --h* | -h ) + echo "$usage"; exit ;; + -- ) # Stop option processing + shift; break ;; + - ) # Use stdin as input. + break ;; + -* ) + echo "$me: invalid option $1$help" >&2 + exit 1 ;; + * ) + break ;; + esac +done + +if test $# != 0; then + echo "$me: too many arguments$help" >&2 + exit 1 +fi + +trap 'exit 1' 1 2 15 + +# CC_FOR_BUILD -- compiler used by this script. Note that the use of a +# compiler to aid in system detection is discouraged as it requires +# temporary files to be created and, as you can see below, it is a +# headache to deal with in a portable fashion. + +# Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still +# use `HOST_CC' if defined, but it is deprecated. + +# Portable tmp directory creation inspired by the Autoconf team. + +set_cc_for_build=' +trap "exitcode=\$?; (rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null) && exit \$exitcode" 0 ; +trap "rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null; exit 1" 1 2 13 15 ; +: ${TMPDIR=/tmp} ; + { tmp=`(umask 077 && mktemp -d "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" ; } || + { test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir $tmp) ; } || + { tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir $tmp) && echo "Warning: creating insecure temp directory" >&2 ; } || + { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; } ; +dummy=$tmp/dummy ; +tmpfiles="$dummy.c $dummy.o $dummy.rel $dummy" ; +case $CC_FOR_BUILD,$HOST_CC,$CC in + ,,) echo "int x;" > $dummy.c ; + for c in cc gcc c89 c99 ; do + if ($c -c -o $dummy.o $dummy.c) >/dev/null 2>&1 ; then + CC_FOR_BUILD="$c"; break ; + fi ; + done ; + if test x"$CC_FOR_BUILD" = x ; then + CC_FOR_BUILD=no_compiler_found ; + fi + ;; + ,,*) CC_FOR_BUILD=$CC ;; + ,*,*) CC_FOR_BUILD=$HOST_CC ;; +esac ; set_cc_for_build= ;' + +# This is needed to find uname on a Pyramid OSx when run in the BSD universe. +# (ghazi@noc.rutgers.edu 1994-08-24) +if (test -f /.attbin/uname) >/dev/null 2>&1 ; then + PATH=$PATH:/.attbin ; export PATH +fi + +UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown +UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown +UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown +UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown + +case "${UNAME_SYSTEM}" in +Linux|GNU|GNU/*) + # If the system lacks a compiler, then just pick glibc. + # We could probably try harder. + LIBC=gnu + + eval $set_cc_for_build + cat <<-EOF > $dummy.c + #include + #if defined(__UCLIBC__) + LIBC=uclibc + #elif defined(__dietlibc__) + LIBC=dietlibc + #else + LIBC=gnu + #endif + EOF + eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^LIBC' | sed 's, ,,g'` + ;; +esac + +# Note: order is significant - the case branches are not exclusive. + +case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in + *:NetBSD:*:*) + # NetBSD (nbsd) targets should (where applicable) match one or + # more of the tuples: *-*-netbsdelf*, *-*-netbsdaout*, + # *-*-netbsdecoff* and *-*-netbsd*. For targets that recently + # switched to ELF, *-*-netbsd* would select the old + # object file format. This provides both forward + # compatibility and a consistent mechanism for selecting the + # object file format. + # + # Note: NetBSD doesn't particularly care about the vendor + # portion of the name. We always set it to "unknown". + sysctl="sysctl -n hw.machine_arch" + UNAME_MACHINE_ARCH=`(/sbin/$sysctl 2>/dev/null || \ + /usr/sbin/$sysctl 2>/dev/null || echo unknown)` + case "${UNAME_MACHINE_ARCH}" in + armeb) machine=armeb-unknown ;; + arm*) machine=arm-unknown ;; + sh3el) machine=shl-unknown ;; + sh3eb) machine=sh-unknown ;; + sh5el) machine=sh5le-unknown ;; + *) machine=${UNAME_MACHINE_ARCH}-unknown ;; + esac + # The Operating System including object format, if it has switched + # to ELF recently, or will in the future. + case "${UNAME_MACHINE_ARCH}" in + arm*|i386|m68k|ns32k|sh3*|sparc|vax) + eval $set_cc_for_build + if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \ + | grep -q __ELF__ + then + # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout). + # Return netbsd for either. FIX? + os=netbsd + else + os=netbsdelf + fi + ;; + *) + os=netbsd + ;; + esac + # The OS release + # Debian GNU/NetBSD machines have a different userland, and + # thus, need a distinct triplet. However, they do not need + # kernel version information, so it can be replaced with a + # suitable tag, in the style of linux-gnu. + case "${UNAME_VERSION}" in + Debian*) + release='-gnu' + ;; + *) + release=`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'` + ;; + esac + # Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM: + # contains redundant information, the shorter form: + # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used. + echo "${machine}-${os}${release}" + exit ;; + *:Bitrig:*:*) + UNAME_MACHINE_ARCH=`arch | sed 's/Bitrig.//'` + echo ${UNAME_MACHINE_ARCH}-unknown-bitrig${UNAME_RELEASE} + exit ;; + *:OpenBSD:*:*) + UNAME_MACHINE_ARCH=`arch | sed 's/OpenBSD.//'` + echo ${UNAME_MACHINE_ARCH}-unknown-openbsd${UNAME_RELEASE} + exit ;; + *:ekkoBSD:*:*) + echo ${UNAME_MACHINE}-unknown-ekkobsd${UNAME_RELEASE} + exit ;; + *:SolidBSD:*:*) + echo ${UNAME_MACHINE}-unknown-solidbsd${UNAME_RELEASE} + exit ;; + macppc:MirBSD:*:*) + echo powerpc-unknown-mirbsd${UNAME_RELEASE} + exit ;; + *:MirBSD:*:*) + echo ${UNAME_MACHINE}-unknown-mirbsd${UNAME_RELEASE} + exit ;; + alpha:OSF1:*:*) + case $UNAME_RELEASE in + *4.0) + UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'` + ;; + *5.*) + UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $4}'` + ;; + esac + # According to Compaq, /usr/sbin/psrinfo has been available on + # OSF/1 and Tru64 systems produced since 1995. I hope that + # covers most systems running today. This code pipes the CPU + # types through head -n 1, so we only detect the type of CPU 0. + ALPHA_CPU_TYPE=`/usr/sbin/psrinfo -v | sed -n -e 's/^ The alpha \(.*\) processor.*$/\1/p' | head -n 1` + case "$ALPHA_CPU_TYPE" in + "EV4 (21064)") + UNAME_MACHINE="alpha" ;; + "EV4.5 (21064)") + UNAME_MACHINE="alpha" ;; + "LCA4 (21066/21068)") + UNAME_MACHINE="alpha" ;; + "EV5 (21164)") + UNAME_MACHINE="alphaev5" ;; + "EV5.6 (21164A)") + UNAME_MACHINE="alphaev56" ;; + "EV5.6 (21164PC)") + UNAME_MACHINE="alphapca56" ;; + "EV5.7 (21164PC)") + UNAME_MACHINE="alphapca57" ;; + "EV6 (21264)") + UNAME_MACHINE="alphaev6" ;; + "EV6.7 (21264A)") + UNAME_MACHINE="alphaev67" ;; + "EV6.8CB (21264C)") + UNAME_MACHINE="alphaev68" ;; + "EV6.8AL (21264B)") + UNAME_MACHINE="alphaev68" ;; + "EV6.8CX (21264D)") + UNAME_MACHINE="alphaev68" ;; + "EV6.9A (21264/EV69A)") + UNAME_MACHINE="alphaev69" ;; + "EV7 (21364)") + UNAME_MACHINE="alphaev7" ;; + "EV7.9 (21364A)") + UNAME_MACHINE="alphaev79" ;; + esac + # A Pn.n version is a patched version. + # A Vn.n version is a released version. + # A Tn.n version is a released field test version. + # A Xn.n version is an unreleased experimental baselevel. + # 1.2 uses "1.2" for uname -r. + echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[PVTX]//' | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` + # Reset EXIT trap before exiting to avoid spurious non-zero exit code. + exitcode=$? + trap '' 0 + exit $exitcode ;; + Alpha\ *:Windows_NT*:*) + # How do we know it's Interix rather than the generic POSIX subsystem? + # Should we change UNAME_MACHINE based on the output of uname instead + # of the specific Alpha model? + echo alpha-pc-interix + exit ;; + 21064:Windows_NT:50:3) + echo alpha-dec-winnt3.5 + exit ;; + Amiga*:UNIX_System_V:4.0:*) + echo m68k-unknown-sysv4 + exit ;; + *:[Aa]miga[Oo][Ss]:*:*) + echo ${UNAME_MACHINE}-unknown-amigaos + exit ;; + *:[Mm]orph[Oo][Ss]:*:*) + echo ${UNAME_MACHINE}-unknown-morphos + exit ;; + *:OS/390:*:*) + echo i370-ibm-openedition + exit ;; + *:z/VM:*:*) + echo s390-ibm-zvmoe + exit ;; + *:OS400:*:*) + echo powerpc-ibm-os400 + exit ;; + arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*) + echo arm-acorn-riscix${UNAME_RELEASE} + exit ;; + arm*:riscos:*:*|arm*:RISCOS:*:*) + echo arm-unknown-riscos + exit ;; + SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*) + echo hppa1.1-hitachi-hiuxmpp + exit ;; + Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*) + # akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE. + if test "`(/bin/universe) 2>/dev/null`" = att ; then + echo pyramid-pyramid-sysv3 + else + echo pyramid-pyramid-bsd + fi + exit ;; + NILE*:*:*:dcosx) + echo pyramid-pyramid-svr4 + exit ;; + DRS?6000:unix:4.0:6*) + echo sparc-icl-nx6 + exit ;; + DRS?6000:UNIX_SV:4.2*:7* | DRS?6000:isis:4.2*:7*) + case `/usr/bin/uname -p` in + sparc) echo sparc-icl-nx7; exit ;; + esac ;; + s390x:SunOS:*:*) + echo ${UNAME_MACHINE}-ibm-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit ;; + sun4H:SunOS:5.*:*) + echo sparc-hal-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit ;; + sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*) + echo sparc-sun-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit ;; + i86pc:AuroraUX:5.*:* | i86xen:AuroraUX:5.*:*) + echo i386-pc-auroraux${UNAME_RELEASE} + exit ;; + i86pc:SunOS:5.*:* | i86xen:SunOS:5.*:*) + eval $set_cc_for_build + SUN_ARCH="i386" + # If there is a compiler, see if it is configured for 64-bit objects. + # Note that the Sun cc does not turn __LP64__ into 1 like gcc does. + # This test works for both compilers. + if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then + if (echo '#ifdef __amd64'; echo IS_64BIT_ARCH; echo '#endif') | \ + (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \ + grep IS_64BIT_ARCH >/dev/null + then + SUN_ARCH="x86_64" + fi + fi + echo ${SUN_ARCH}-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit ;; + sun4*:SunOS:6*:*) + # According to config.sub, this is the proper way to canonicalize + # SunOS6. Hard to guess exactly what SunOS6 will be like, but + # it's likely to be more like Solaris than SunOS4. + echo sparc-sun-solaris3`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit ;; + sun4*:SunOS:*:*) + case "`/usr/bin/arch -k`" in + Series*|S4*) + UNAME_RELEASE=`uname -v` + ;; + esac + # Japanese Language versions have a version number like `4.1.3-JL'. + echo sparc-sun-sunos`echo ${UNAME_RELEASE}|sed -e 's/-/_/'` + exit ;; + sun3*:SunOS:*:*) + echo m68k-sun-sunos${UNAME_RELEASE} + exit ;; + sun*:*:4.2BSD:*) + UNAME_RELEASE=`(sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null` + test "x${UNAME_RELEASE}" = "x" && UNAME_RELEASE=3 + case "`/bin/arch`" in + sun3) + echo m68k-sun-sunos${UNAME_RELEASE} + ;; + sun4) + echo sparc-sun-sunos${UNAME_RELEASE} + ;; + esac + exit ;; + aushp:SunOS:*:*) + echo sparc-auspex-sunos${UNAME_RELEASE} + exit ;; + # The situation for MiNT is a little confusing. The machine name + # can be virtually everything (everything which is not + # "atarist" or "atariste" at least should have a processor + # > m68000). The system name ranges from "MiNT" over "FreeMiNT" + # to the lowercase version "mint" (or "freemint"). Finally + # the system name "TOS" denotes a system which is actually not + # MiNT. But MiNT is downward compatible to TOS, so this should + # be no problem. + atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*) + echo m68k-atari-mint${UNAME_RELEASE} + exit ;; + atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*) + echo m68k-atari-mint${UNAME_RELEASE} + exit ;; + *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*) + echo m68k-atari-mint${UNAME_RELEASE} + exit ;; + milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*) + echo m68k-milan-mint${UNAME_RELEASE} + exit ;; + hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*) + echo m68k-hades-mint${UNAME_RELEASE} + exit ;; + *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*) + echo m68k-unknown-mint${UNAME_RELEASE} + exit ;; + m68k:machten:*:*) + echo m68k-apple-machten${UNAME_RELEASE} + exit ;; + powerpc:machten:*:*) + echo powerpc-apple-machten${UNAME_RELEASE} + exit ;; + RISC*:Mach:*:*) + echo mips-dec-mach_bsd4.3 + exit ;; + RISC*:ULTRIX:*:*) + echo mips-dec-ultrix${UNAME_RELEASE} + exit ;; + VAX*:ULTRIX*:*:*) + echo vax-dec-ultrix${UNAME_RELEASE} + exit ;; + 2020:CLIX:*:* | 2430:CLIX:*:*) + echo clipper-intergraph-clix${UNAME_RELEASE} + exit ;; + mips:*:*:UMIPS | mips:*:*:RISCos) + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c +#ifdef __cplusplus +#include /* for printf() prototype */ + int main (int argc, char *argv[]) { +#else + int main (argc, argv) int argc; char *argv[]; { +#endif + #if defined (host_mips) && defined (MIPSEB) + #if defined (SYSTYPE_SYSV) + printf ("mips-mips-riscos%ssysv\n", argv[1]); exit (0); + #endif + #if defined (SYSTYPE_SVR4) + printf ("mips-mips-riscos%ssvr4\n", argv[1]); exit (0); + #endif + #if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD) + printf ("mips-mips-riscos%sbsd\n", argv[1]); exit (0); + #endif + #endif + exit (-1); + } +EOF + $CC_FOR_BUILD -o $dummy $dummy.c && + dummyarg=`echo "${UNAME_RELEASE}" | sed -n 's/\([0-9]*\).*/\1/p'` && + SYSTEM_NAME=`$dummy $dummyarg` && + { echo "$SYSTEM_NAME"; exit; } + echo mips-mips-riscos${UNAME_RELEASE} + exit ;; + Motorola:PowerMAX_OS:*:*) + echo powerpc-motorola-powermax + exit ;; + Motorola:*:4.3:PL8-*) + echo powerpc-harris-powermax + exit ;; + Night_Hawk:*:*:PowerMAX_OS | Synergy:PowerMAX_OS:*:*) + echo powerpc-harris-powermax + exit ;; + Night_Hawk:Power_UNIX:*:*) + echo powerpc-harris-powerunix + exit ;; + m88k:CX/UX:7*:*) + echo m88k-harris-cxux7 + exit ;; + m88k:*:4*:R4*) + echo m88k-motorola-sysv4 + exit ;; + m88k:*:3*:R3*) + echo m88k-motorola-sysv3 + exit ;; + AViiON:dgux:*:*) + # DG/UX returns AViiON for all architectures + UNAME_PROCESSOR=`/usr/bin/uname -p` + if [ $UNAME_PROCESSOR = mc88100 ] || [ $UNAME_PROCESSOR = mc88110 ] + then + if [ ${TARGET_BINARY_INTERFACE}x = m88kdguxelfx ] || \ + [ ${TARGET_BINARY_INTERFACE}x = x ] + then + echo m88k-dg-dgux${UNAME_RELEASE} + else + echo m88k-dg-dguxbcs${UNAME_RELEASE} + fi + else + echo i586-dg-dgux${UNAME_RELEASE} + fi + exit ;; + M88*:DolphinOS:*:*) # DolphinOS (SVR3) + echo m88k-dolphin-sysv3 + exit ;; + M88*:*:R3*:*) + # Delta 88k system running SVR3 + echo m88k-motorola-sysv3 + exit ;; + XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3) + echo m88k-tektronix-sysv3 + exit ;; + Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD) + echo m68k-tektronix-bsd + exit ;; + *:IRIX*:*:*) + echo mips-sgi-irix`echo ${UNAME_RELEASE}|sed -e 's/-/_/g'` + exit ;; + ????????:AIX?:[12].1:2) # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX. + echo romp-ibm-aix # uname -m gives an 8 hex-code CPU id + exit ;; # Note that: echo "'`uname -s`'" gives 'AIX ' + i*86:AIX:*:*) + echo i386-ibm-aix + exit ;; + ia64:AIX:*:*) + if [ -x /usr/bin/oslevel ] ; then + IBM_REV=`/usr/bin/oslevel` + else + IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE} + fi + echo ${UNAME_MACHINE}-ibm-aix${IBM_REV} + exit ;; + *:AIX:2:3) + if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + #include + + main() + { + if (!__power_pc()) + exit(1); + puts("powerpc-ibm-aix3.2.5"); + exit(0); + } +EOF + if $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy` + then + echo "$SYSTEM_NAME" + else + echo rs6000-ibm-aix3.2.5 + fi + elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then + echo rs6000-ibm-aix3.2.4 + else + echo rs6000-ibm-aix3.2 + fi + exit ;; + *:AIX:*:[4567]) + IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }'` + if /usr/sbin/lsattr -El ${IBM_CPU_ID} | grep ' POWER' >/dev/null 2>&1; then + IBM_ARCH=rs6000 + else + IBM_ARCH=powerpc + fi + if [ -x /usr/bin/oslevel ] ; then + IBM_REV=`/usr/bin/oslevel` + else + IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE} + fi + echo ${IBM_ARCH}-ibm-aix${IBM_REV} + exit ;; + *:AIX:*:*) + echo rs6000-ibm-aix + exit ;; + ibmrt:4.4BSD:*|romp-ibm:BSD:*) + echo romp-ibm-bsd4.4 + exit ;; + ibmrt:*BSD:*|romp-ibm:BSD:*) # covers RT/PC BSD and + echo romp-ibm-bsd${UNAME_RELEASE} # 4.3 with uname added to + exit ;; # report: romp-ibm BSD 4.3 + *:BOSX:*:*) + echo rs6000-bull-bosx + exit ;; + DPX/2?00:B.O.S.:*:*) + echo m68k-bull-sysv3 + exit ;; + 9000/[34]??:4.3bsd:1.*:*) + echo m68k-hp-bsd + exit ;; + hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*) + echo m68k-hp-bsd4.4 + exit ;; + 9000/[34678]??:HP-UX:*:*) + HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'` + case "${UNAME_MACHINE}" in + 9000/31? ) HP_ARCH=m68000 ;; + 9000/[34]?? ) HP_ARCH=m68k ;; + 9000/[678][0-9][0-9]) + if [ -x /usr/bin/getconf ]; then + sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null` + sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null` + case "${sc_cpu_version}" in + 523) HP_ARCH="hppa1.0" ;; # CPU_PA_RISC1_0 + 528) HP_ARCH="hppa1.1" ;; # CPU_PA_RISC1_1 + 532) # CPU_PA_RISC2_0 + case "${sc_kernel_bits}" in + 32) HP_ARCH="hppa2.0n" ;; + 64) HP_ARCH="hppa2.0w" ;; + '') HP_ARCH="hppa2.0" ;; # HP-UX 10.20 + esac ;; + esac + fi + if [ "${HP_ARCH}" = "" ]; then + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + + #define _HPUX_SOURCE + #include + #include + + int main () + { + #if defined(_SC_KERNEL_BITS) + long bits = sysconf(_SC_KERNEL_BITS); + #endif + long cpu = sysconf (_SC_CPU_VERSION); + + switch (cpu) + { + case CPU_PA_RISC1_0: puts ("hppa1.0"); break; + case CPU_PA_RISC1_1: puts ("hppa1.1"); break; + case CPU_PA_RISC2_0: + #if defined(_SC_KERNEL_BITS) + switch (bits) + { + case 64: puts ("hppa2.0w"); break; + case 32: puts ("hppa2.0n"); break; + default: puts ("hppa2.0"); break; + } break; + #else /* !defined(_SC_KERNEL_BITS) */ + puts ("hppa2.0"); break; + #endif + default: puts ("hppa1.0"); break; + } + exit (0); + } +EOF + (CCOPTS= $CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null) && HP_ARCH=`$dummy` + test -z "$HP_ARCH" && HP_ARCH=hppa + fi ;; + esac + if [ ${HP_ARCH} = "hppa2.0w" ] + then + eval $set_cc_for_build + + # hppa2.0w-hp-hpux* has a 64-bit kernel and a compiler generating + # 32-bit code. hppa64-hp-hpux* has the same kernel and a compiler + # generating 64-bit code. GNU and HP use different nomenclature: + # + # $ CC_FOR_BUILD=cc ./config.guess + # => hppa2.0w-hp-hpux11.23 + # $ CC_FOR_BUILD="cc +DA2.0w" ./config.guess + # => hppa64-hp-hpux11.23 + + if echo __LP64__ | (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | + grep -q __LP64__ + then + HP_ARCH="hppa2.0w" + else + HP_ARCH="hppa64" + fi + fi + echo ${HP_ARCH}-hp-hpux${HPUX_REV} + exit ;; + ia64:HP-UX:*:*) + HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'` + echo ia64-hp-hpux${HPUX_REV} + exit ;; + 3050*:HI-UX:*:*) + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + #include + int + main () + { + long cpu = sysconf (_SC_CPU_VERSION); + /* The order matters, because CPU_IS_HP_MC68K erroneously returns + true for CPU_PA_RISC1_0. CPU_IS_PA_RISC returns correct + results, however. */ + if (CPU_IS_PA_RISC (cpu)) + { + switch (cpu) + { + case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break; + case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break; + case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break; + default: puts ("hppa-hitachi-hiuxwe2"); break; + } + } + else if (CPU_IS_HP_MC68K (cpu)) + puts ("m68k-hitachi-hiuxwe2"); + else puts ("unknown-hitachi-hiuxwe2"); + exit (0); + } +EOF + $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy` && + { echo "$SYSTEM_NAME"; exit; } + echo unknown-hitachi-hiuxwe2 + exit ;; + 9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:* ) + echo hppa1.1-hp-bsd + exit ;; + 9000/8??:4.3bsd:*:*) + echo hppa1.0-hp-bsd + exit ;; + *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*) + echo hppa1.0-hp-mpeix + exit ;; + hp7??:OSF1:*:* | hp8?[79]:OSF1:*:* ) + echo hppa1.1-hp-osf + exit ;; + hp8??:OSF1:*:*) + echo hppa1.0-hp-osf + exit ;; + i*86:OSF1:*:*) + if [ -x /usr/sbin/sysversion ] ; then + echo ${UNAME_MACHINE}-unknown-osf1mk + else + echo ${UNAME_MACHINE}-unknown-osf1 + fi + exit ;; + parisc*:Lites*:*:*) + echo hppa1.1-hp-lites + exit ;; + C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*) + echo c1-convex-bsd + exit ;; + C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*) + if getsysinfo -f scalar_acc + then echo c32-convex-bsd + else echo c2-convex-bsd + fi + exit ;; + C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*) + echo c34-convex-bsd + exit ;; + C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*) + echo c38-convex-bsd + exit ;; + C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*) + echo c4-convex-bsd + exit ;; + CRAY*Y-MP:*:*:*) + echo ymp-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit ;; + CRAY*[A-Z]90:*:*:*) + echo ${UNAME_MACHINE}-cray-unicos${UNAME_RELEASE} \ + | sed -e 's/CRAY.*\([A-Z]90\)/\1/' \ + -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \ + -e 's/\.[^.]*$/.X/' + exit ;; + CRAY*TS:*:*:*) + echo t90-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit ;; + CRAY*T3E:*:*:*) + echo alphaev5-cray-unicosmk${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit ;; + CRAY*SV1:*:*:*) + echo sv1-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit ;; + *:UNICOS/mp:*:*) + echo craynv-cray-unicosmp${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit ;; + F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*) + FUJITSU_PROC=`uname -m | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` + FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'` + FUJITSU_REL=`echo ${UNAME_RELEASE} | sed -e 's/ /_/'` + echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" + exit ;; + 5000:UNIX_System_V:4.*:*) + FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'` + FUJITSU_REL=`echo ${UNAME_RELEASE} | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/ /_/'` + echo "sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" + exit ;; + i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*) + echo ${UNAME_MACHINE}-pc-bsdi${UNAME_RELEASE} + exit ;; + sparc*:BSD/OS:*:*) + echo sparc-unknown-bsdi${UNAME_RELEASE} + exit ;; + *:BSD/OS:*:*) + echo ${UNAME_MACHINE}-unknown-bsdi${UNAME_RELEASE} + exit ;; + *:FreeBSD:*:*) + UNAME_PROCESSOR=`/usr/bin/uname -p` + case ${UNAME_PROCESSOR} in + amd64) + echo x86_64-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;; + *) + echo ${UNAME_PROCESSOR}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;; + esac + exit ;; + i*:CYGWIN*:*) + echo ${UNAME_MACHINE}-pc-cygwin + exit ;; + *:MINGW64*:*) + echo ${UNAME_MACHINE}-pc-mingw64 + exit ;; + *:MINGW*:*) + echo ${UNAME_MACHINE}-pc-mingw32 + exit ;; + *:MSYS*:*) + echo ${UNAME_MACHINE}-pc-msys + exit ;; + i*:windows32*:*) + # uname -m includes "-pc" on this system. + echo ${UNAME_MACHINE}-mingw32 + exit ;; + i*:PW*:*) + echo ${UNAME_MACHINE}-pc-pw32 + exit ;; + *:Interix*:*) + case ${UNAME_MACHINE} in + x86) + echo i586-pc-interix${UNAME_RELEASE} + exit ;; + authenticamd | genuineintel | EM64T) + echo x86_64-unknown-interix${UNAME_RELEASE} + exit ;; + IA64) + echo ia64-unknown-interix${UNAME_RELEASE} + exit ;; + esac ;; + [345]86:Windows_95:* | [345]86:Windows_98:* | [345]86:Windows_NT:*) + echo i${UNAME_MACHINE}-pc-mks + exit ;; + 8664:Windows_NT:*) + echo x86_64-pc-mks + exit ;; + i*:Windows_NT*:* | Pentium*:Windows_NT*:*) + # How do we know it's Interix rather than the generic POSIX subsystem? + # It also conflicts with pre-2.0 versions of AT&T UWIN. Should we + # UNAME_MACHINE based on the output of uname instead of i386? + echo i586-pc-interix + exit ;; + i*:UWIN*:*) + echo ${UNAME_MACHINE}-pc-uwin + exit ;; + amd64:CYGWIN*:*:* | x86_64:CYGWIN*:*:*) + echo x86_64-unknown-cygwin + exit ;; + p*:CYGWIN*:*) + echo powerpcle-unknown-cygwin + exit ;; + prep*:SunOS:5.*:*) + echo powerpcle-unknown-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit ;; + *:GNU:*:*) + # the GNU system + echo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-unknown-${LIBC}`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'` + exit ;; + *:GNU/*:*:*) + # other systems with GNU libc and userland + echo ${UNAME_MACHINE}-unknown-`echo ${UNAME_SYSTEM} | sed 's,^[^/]*/,,' | tr '[A-Z]' '[a-z]'``echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`-${LIBC} + exit ;; + i*86:Minix:*:*) + echo ${UNAME_MACHINE}-pc-minix + exit ;; + aarch64:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + aarch64_be:Linux:*:*) + UNAME_MACHINE=aarch64_be + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + alpha:Linux:*:*) + case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in + EV5) UNAME_MACHINE=alphaev5 ;; + EV56) UNAME_MACHINE=alphaev56 ;; + PCA56) UNAME_MACHINE=alphapca56 ;; + PCA57) UNAME_MACHINE=alphapca56 ;; + EV6) UNAME_MACHINE=alphaev6 ;; + EV67) UNAME_MACHINE=alphaev67 ;; + EV68*) UNAME_MACHINE=alphaev68 ;; + esac + objdump --private-headers /bin/sh | grep -q ld.so.1 + if test "$?" = 0 ; then LIBC="gnulibc1" ; fi + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + arc:Linux:*:* | arceb:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + arm*:Linux:*:*) + eval $set_cc_for_build + if echo __ARM_EABI__ | $CC_FOR_BUILD -E - 2>/dev/null \ + | grep -q __ARM_EABI__ + then + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + else + if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \ + | grep -q __ARM_PCS_VFP + then + echo ${UNAME_MACHINE}-unknown-linux-${LIBC}eabi + else + echo ${UNAME_MACHINE}-unknown-linux-${LIBC}eabihf + fi + fi + exit ;; + avr32*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + cris:Linux:*:*) + echo ${UNAME_MACHINE}-axis-linux-${LIBC} + exit ;; + crisv32:Linux:*:*) + echo ${UNAME_MACHINE}-axis-linux-${LIBC} + exit ;; + frv:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + hexagon:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + i*86:Linux:*:*) + echo ${UNAME_MACHINE}-pc-linux-${LIBC} + exit ;; + ia64:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + m32r*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + m68*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + mips:Linux:*:* | mips64:Linux:*:*) + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + #undef CPU + #undef ${UNAME_MACHINE} + #undef ${UNAME_MACHINE}el + #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL) + CPU=${UNAME_MACHINE}el + #else + #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB) + CPU=${UNAME_MACHINE} + #else + CPU= + #endif + #endif +EOF + eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^CPU'` + test x"${CPU}" != x && { echo "${CPU}-unknown-linux-${LIBC}"; exit; } + ;; + openrisc*:Linux:*:*) + echo or1k-unknown-linux-${LIBC} + exit ;; + or32:Linux:*:* | or1k*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + padre:Linux:*:*) + echo sparc-unknown-linux-${LIBC} + exit ;; + parisc64:Linux:*:* | hppa64:Linux:*:*) + echo hppa64-unknown-linux-${LIBC} + exit ;; + parisc:Linux:*:* | hppa:Linux:*:*) + # Look for CPU level + case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in + PA7*) echo hppa1.1-unknown-linux-${LIBC} ;; + PA8*) echo hppa2.0-unknown-linux-${LIBC} ;; + *) echo hppa-unknown-linux-${LIBC} ;; + esac + exit ;; + ppc64:Linux:*:*) + echo powerpc64-unknown-linux-${LIBC} + exit ;; + ppc:Linux:*:*) + echo powerpc-unknown-linux-${LIBC} + exit ;; + ppc64le:Linux:*:*) + echo powerpc64le-unknown-linux-${LIBC} + exit ;; + ppcle:Linux:*:*) + echo powerpcle-unknown-linux-${LIBC} + exit ;; + s390:Linux:*:* | s390x:Linux:*:*) + echo ${UNAME_MACHINE}-ibm-linux-${LIBC} + exit ;; + sh64*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + sh*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + sparc:Linux:*:* | sparc64:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + tile*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + vax:Linux:*:*) + echo ${UNAME_MACHINE}-dec-linux-${LIBC} + exit ;; + x86_64:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + xtensa*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + i*86:DYNIX/ptx:4*:*) + # ptx 4.0 does uname -s correctly, with DYNIX/ptx in there. + # earlier versions are messed up and put the nodename in both + # sysname and nodename. + echo i386-sequent-sysv4 + exit ;; + i*86:UNIX_SV:4.2MP:2.*) + # Unixware is an offshoot of SVR4, but it has its own version + # number series starting with 2... + # I am not positive that other SVR4 systems won't match this, + # I just have to hope. -- rms. + # Use sysv4.2uw... so that sysv4* matches it. + echo ${UNAME_MACHINE}-pc-sysv4.2uw${UNAME_VERSION} + exit ;; + i*86:OS/2:*:*) + # If we were able to find `uname', then EMX Unix compatibility + # is probably installed. + echo ${UNAME_MACHINE}-pc-os2-emx + exit ;; + i*86:XTS-300:*:STOP) + echo ${UNAME_MACHINE}-unknown-stop + exit ;; + i*86:atheos:*:*) + echo ${UNAME_MACHINE}-unknown-atheos + exit ;; + i*86:syllable:*:*) + echo ${UNAME_MACHINE}-pc-syllable + exit ;; + i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.[02]*:*) + echo i386-unknown-lynxos${UNAME_RELEASE} + exit ;; + i*86:*DOS:*:*) + echo ${UNAME_MACHINE}-pc-msdosdjgpp + exit ;; + i*86:*:4.*:* | i*86:SYSTEM_V:4.*:*) + UNAME_REL=`echo ${UNAME_RELEASE} | sed 's/\/MP$//'` + if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then + echo ${UNAME_MACHINE}-univel-sysv${UNAME_REL} + else + echo ${UNAME_MACHINE}-pc-sysv${UNAME_REL} + fi + exit ;; + i*86:*:5:[678]*) + # UnixWare 7.x, OpenUNIX and OpenServer 6. + case `/bin/uname -X | grep "^Machine"` in + *486*) UNAME_MACHINE=i486 ;; + *Pentium) UNAME_MACHINE=i586 ;; + *Pent*|*Celeron) UNAME_MACHINE=i686 ;; + esac + echo ${UNAME_MACHINE}-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION} + exit ;; + i*86:*:3.2:*) + if test -f /usr/options/cb.name; then + UNAME_REL=`sed -n 's/.*Version //p' /dev/null >/dev/null ; then + UNAME_REL=`(/bin/uname -X|grep Release|sed -e 's/.*= //')` + (/bin/uname -X|grep i80486 >/dev/null) && UNAME_MACHINE=i486 + (/bin/uname -X|grep '^Machine.*Pentium' >/dev/null) \ + && UNAME_MACHINE=i586 + (/bin/uname -X|grep '^Machine.*Pent *II' >/dev/null) \ + && UNAME_MACHINE=i686 + (/bin/uname -X|grep '^Machine.*Pentium Pro' >/dev/null) \ + && UNAME_MACHINE=i686 + echo ${UNAME_MACHINE}-pc-sco$UNAME_REL + else + echo ${UNAME_MACHINE}-pc-sysv32 + fi + exit ;; + pc:*:*:*) + # Left here for compatibility: + # uname -m prints for DJGPP always 'pc', but it prints nothing about + # the processor, so we play safe by assuming i586. + # Note: whatever this is, it MUST be the same as what config.sub + # prints for the "djgpp" host, or else GDB configury will decide that + # this is a cross-build. + echo i586-pc-msdosdjgpp + exit ;; + Intel:Mach:3*:*) + echo i386-pc-mach3 + exit ;; + paragon:*:*:*) + echo i860-intel-osf1 + exit ;; + i860:*:4.*:*) # i860-SVR4 + if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then + echo i860-stardent-sysv${UNAME_RELEASE} # Stardent Vistra i860-SVR4 + else # Add other i860-SVR4 vendors below as they are discovered. + echo i860-unknown-sysv${UNAME_RELEASE} # Unknown i860-SVR4 + fi + exit ;; + mini*:CTIX:SYS*5:*) + # "miniframe" + echo m68010-convergent-sysv + exit ;; + mc68k:UNIX:SYSTEM5:3.51m) + echo m68k-convergent-sysv + exit ;; + M680?0:D-NIX:5.3:*) + echo m68k-diab-dnix + exit ;; + M68*:*:R3V[5678]*:*) + test -r /sysV68 && { echo 'm68k-motorola-sysv'; exit; } ;; + 3[345]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 3[34]??/*:*:4.0:3.0 | 4400:*:4.0:3.0 | 4850:*:4.0:3.0 | SKA40:*:4.0:3.0 | SDS2:*:4.0:3.0 | SHG2:*:4.0:3.0 | S7501*:*:4.0:3.0) + OS_REL='' + test -r /etc/.relid \ + && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` + /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ + && { echo i486-ncr-sysv4.3${OS_REL}; exit; } + /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ + && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;; + 3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*) + /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ + && { echo i486-ncr-sysv4; exit; } ;; + NCR*:*:4.2:* | MPRAS*:*:4.2:*) + OS_REL='.3' + test -r /etc/.relid \ + && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` + /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ + && { echo i486-ncr-sysv4.3${OS_REL}; exit; } + /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ + && { echo i586-ncr-sysv4.3${OS_REL}; exit; } + /bin/uname -p 2>/dev/null | /bin/grep pteron >/dev/null \ + && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;; + m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*) + echo m68k-unknown-lynxos${UNAME_RELEASE} + exit ;; + mc68030:UNIX_System_V:4.*:*) + echo m68k-atari-sysv4 + exit ;; + TSUNAMI:LynxOS:2.*:*) + echo sparc-unknown-lynxos${UNAME_RELEASE} + exit ;; + rs6000:LynxOS:2.*:*) + echo rs6000-unknown-lynxos${UNAME_RELEASE} + exit ;; + PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.[02]*:*) + echo powerpc-unknown-lynxos${UNAME_RELEASE} + exit ;; + SM[BE]S:UNIX_SV:*:*) + echo mips-dde-sysv${UNAME_RELEASE} + exit ;; + RM*:ReliantUNIX-*:*:*) + echo mips-sni-sysv4 + exit ;; + RM*:SINIX-*:*:*) + echo mips-sni-sysv4 + exit ;; + *:SINIX-*:*:*) + if uname -p 2>/dev/null >/dev/null ; then + UNAME_MACHINE=`(uname -p) 2>/dev/null` + echo ${UNAME_MACHINE}-sni-sysv4 + else + echo ns32k-sni-sysv + fi + exit ;; + PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort + # says + echo i586-unisys-sysv4 + exit ;; + *:UNIX_System_V:4*:FTX*) + # From Gerald Hewes . + # How about differentiating between stratus architectures? -djm + echo hppa1.1-stratus-sysv4 + exit ;; + *:*:*:FTX*) + # From seanf@swdc.stratus.com. + echo i860-stratus-sysv4 + exit ;; + i*86:VOS:*:*) + # From Paul.Green@stratus.com. + echo ${UNAME_MACHINE}-stratus-vos + exit ;; + *:VOS:*:*) + # From Paul.Green@stratus.com. + echo hppa1.1-stratus-vos + exit ;; + mc68*:A/UX:*:*) + echo m68k-apple-aux${UNAME_RELEASE} + exit ;; + news*:NEWS-OS:6*:*) + echo mips-sony-newsos6 + exit ;; + R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*) + if [ -d /usr/nec ]; then + echo mips-nec-sysv${UNAME_RELEASE} + else + echo mips-unknown-sysv${UNAME_RELEASE} + fi + exit ;; + BeBox:BeOS:*:*) # BeOS running on hardware made by Be, PPC only. + echo powerpc-be-beos + exit ;; + BeMac:BeOS:*:*) # BeOS running on Mac or Mac clone, PPC only. + echo powerpc-apple-beos + exit ;; + BePC:BeOS:*:*) # BeOS running on Intel PC compatible. + echo i586-pc-beos + exit ;; + BePC:Haiku:*:*) # Haiku running on Intel PC compatible. + echo i586-pc-haiku + exit ;; + x86_64:Haiku:*:*) + echo x86_64-unknown-haiku + exit ;; + SX-4:SUPER-UX:*:*) + echo sx4-nec-superux${UNAME_RELEASE} + exit ;; + SX-5:SUPER-UX:*:*) + echo sx5-nec-superux${UNAME_RELEASE} + exit ;; + SX-6:SUPER-UX:*:*) + echo sx6-nec-superux${UNAME_RELEASE} + exit ;; + SX-7:SUPER-UX:*:*) + echo sx7-nec-superux${UNAME_RELEASE} + exit ;; + SX-8:SUPER-UX:*:*) + echo sx8-nec-superux${UNAME_RELEASE} + exit ;; + SX-8R:SUPER-UX:*:*) + echo sx8r-nec-superux${UNAME_RELEASE} + exit ;; + Power*:Rhapsody:*:*) + echo powerpc-apple-rhapsody${UNAME_RELEASE} + exit ;; + *:Rhapsody:*:*) + echo ${UNAME_MACHINE}-apple-rhapsody${UNAME_RELEASE} + exit ;; + *:Darwin:*:*) + UNAME_PROCESSOR=`uname -p` || UNAME_PROCESSOR=unknown + eval $set_cc_for_build + if test "$UNAME_PROCESSOR" = unknown ; then + UNAME_PROCESSOR=powerpc + fi + if test `echo "$UNAME_RELEASE" | sed -e 's/\..*//'` -le 10 ; then + if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then + if (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \ + (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \ + grep IS_64BIT_ARCH >/dev/null + then + case $UNAME_PROCESSOR in + i386) UNAME_PROCESSOR=x86_64 ;; + powerpc) UNAME_PROCESSOR=powerpc64 ;; + esac + fi + fi + elif test "$UNAME_PROCESSOR" = i386 ; then + # Avoid executing cc on OS X 10.9, as it ships with a stub + # that puts up a graphical alert prompting to install + # developer tools. Any system running Mac OS X 10.7 or + # later (Darwin 11 and later) is required to have a 64-bit + # processor. This is not true of the ARM version of Darwin + # that Apple uses in portable devices. + UNAME_PROCESSOR=x86_64 + fi + echo ${UNAME_PROCESSOR}-apple-darwin${UNAME_RELEASE} + exit ;; + *:procnto*:*:* | *:QNX:[0123456789]*:*) + UNAME_PROCESSOR=`uname -p` + if test "$UNAME_PROCESSOR" = "x86"; then + UNAME_PROCESSOR=i386 + UNAME_MACHINE=pc + fi + echo ${UNAME_PROCESSOR}-${UNAME_MACHINE}-nto-qnx${UNAME_RELEASE} + exit ;; + *:QNX:*:4*) + echo i386-pc-qnx + exit ;; + NEO-?:NONSTOP_KERNEL:*:*) + echo neo-tandem-nsk${UNAME_RELEASE} + exit ;; + NSE-*:NONSTOP_KERNEL:*:*) + echo nse-tandem-nsk${UNAME_RELEASE} + exit ;; + NSR-?:NONSTOP_KERNEL:*:*) + echo nsr-tandem-nsk${UNAME_RELEASE} + exit ;; + *:NonStop-UX:*:*) + echo mips-compaq-nonstopux + exit ;; + BS2000:POSIX*:*:*) + echo bs2000-siemens-sysv + exit ;; + DS/*:UNIX_System_V:*:*) + echo ${UNAME_MACHINE}-${UNAME_SYSTEM}-${UNAME_RELEASE} + exit ;; + *:Plan9:*:*) + # "uname -m" is not consistent, so use $cputype instead. 386 + # is converted to i386 for consistency with other x86 + # operating systems. + if test "$cputype" = "386"; then + UNAME_MACHINE=i386 + else + UNAME_MACHINE="$cputype" + fi + echo ${UNAME_MACHINE}-unknown-plan9 + exit ;; + *:TOPS-10:*:*) + echo pdp10-unknown-tops10 + exit ;; + *:TENEX:*:*) + echo pdp10-unknown-tenex + exit ;; + KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*) + echo pdp10-dec-tops20 + exit ;; + XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*) + echo pdp10-xkl-tops20 + exit ;; + *:TOPS-20:*:*) + echo pdp10-unknown-tops20 + exit ;; + *:ITS:*:*) + echo pdp10-unknown-its + exit ;; + SEI:*:*:SEIUX) + echo mips-sei-seiux${UNAME_RELEASE} + exit ;; + *:DragonFly:*:*) + echo ${UNAME_MACHINE}-unknown-dragonfly`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` + exit ;; + *:*VMS:*:*) + UNAME_MACHINE=`(uname -p) 2>/dev/null` + case "${UNAME_MACHINE}" in + A*) echo alpha-dec-vms ; exit ;; + I*) echo ia64-dec-vms ; exit ;; + V*) echo vax-dec-vms ; exit ;; + esac ;; + *:XENIX:*:SysV) + echo i386-pc-xenix + exit ;; + i*86:skyos:*:*) + echo ${UNAME_MACHINE}-pc-skyos`echo ${UNAME_RELEASE}` | sed -e 's/ .*$//' + exit ;; + i*86:rdos:*:*) + echo ${UNAME_MACHINE}-pc-rdos + exit ;; + i*86:AROS:*:*) + echo ${UNAME_MACHINE}-pc-aros + exit ;; + x86_64:VMkernel:*:*) + echo ${UNAME_MACHINE}-unknown-esx + exit ;; +esac + +cat >&2 < in order to provide the needed +information to handle your system. + +config.guess timestamp = $timestamp + +uname -m = `(uname -m) 2>/dev/null || echo unknown` +uname -r = `(uname -r) 2>/dev/null || echo unknown` +uname -s = `(uname -s) 2>/dev/null || echo unknown` +uname -v = `(uname -v) 2>/dev/null || echo unknown` + +/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null` +/bin/uname -X = `(/bin/uname -X) 2>/dev/null` + +hostinfo = `(hostinfo) 2>/dev/null` +/bin/universe = `(/bin/universe) 2>/dev/null` +/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null` +/bin/arch = `(/bin/arch) 2>/dev/null` +/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null` +/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null` + +UNAME_MACHINE = ${UNAME_MACHINE} +UNAME_RELEASE = ${UNAME_RELEASE} +UNAME_SYSTEM = ${UNAME_SYSTEM} +UNAME_VERSION = ${UNAME_VERSION} +EOF + +exit 1 + +# Local variables: +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "timestamp='" +# time-stamp-format: "%:y-%02m-%02d" +# time-stamp-end: "'" +# End: diff --git a/config.h.in b/config.h.in new file mode 100644 index 0000000..f88e213 --- /dev/null +++ b/config.h.in @@ -0,0 +1,379 @@ +/* config.h.in. Generated from configure.ac by autoheader. */ + +/* Define if building universal (internal helper macro) */ +#undef AC_APPLE_UNIVERSAL_BUILD + +/* Define to 1 to enable debug output. */ +#undef DEBUGBUILD + +/* Define to 1 if you have the `accept4' function. */ +#undef HAVE_ACCEPT4 + +/* Define to 1 if you have the header file. */ +#undef HAVE_ARPA_INET_H + +/* define if the Boost library is available */ +#undef HAVE_BOOST + +/* define if the Boost::ASIO library is available */ +#undef HAVE_BOOST_ASIO + +/* define if the Boost::System library is available */ +#undef HAVE_BOOST_SYSTEM + +/* define if the Boost::Thread library is available */ +#undef HAVE_BOOST_THREAD + +/* Define to 1 if your system has a working `chown' function. */ +#undef HAVE_CHOWN + +/* Define to 1 if you have the `clock_gettime`. */ +#undef HAVE_CLOCK_GETTIME + +/* define if the compiler supports basic C++11 syntax */ +#undef HAVE_CXX11 + +/* Define to 1 if you have the declaration of `strerror_r', and to 0 if you + don't. */ +#undef HAVE_DECL_STRERROR_R + +/* Define to 1 if you have the header file. */ +#undef HAVE_DLFCN_H + +/* Define to 1 if you have the `dup2' function. */ +#undef HAVE_DUP2 + +/* Define to 1 if you have the header file. */ +#undef HAVE_FCNTL_H + +/* Define to 1 if you have the `fork' function. */ +#undef HAVE_FORK + +/* Define to 1 if you have the `getcwd' function. */ +#undef HAVE_GETCWD + +/* Define to 1 if you have the `getpwnam' function. */ +#undef HAVE_GETPWNAM + +/* Define to 1 if you have the header file. */ +#undef HAVE_INTTYPES_H + +/* Define to 1 if you have `libjansson` library. */ +#undef HAVE_JANSSON + +/* Define to 1 if you have `libxml2` library. */ +#undef HAVE_LIBXML2 + +/* Define to 1 if you have the header file. */ +#undef HAVE_LIMITS_H + +/* Define to 1 if you have the `localtime_r' function. */ +#undef HAVE_LOCALTIME_R + +/* Define to 1 if your system has a GNU libc compatible `malloc' function, and + to 0 otherwise. */ +#undef HAVE_MALLOC + +/* Define to 1 if you have the `memchr' function. */ +#undef HAVE_MEMCHR + +/* Define to 1 if you have the `memmove' function. */ +#undef HAVE_MEMMOVE + +/* Define to 1 if you have the header file. */ +#undef HAVE_MEMORY_H + +/* Define to 1 if you have the `memset' function. */ +#undef HAVE_MEMSET + +/* Define to 1 if you have the header file. */ +#undef HAVE_NETDB_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_NETINET_IN_H + +/* Define to 1 if the system has the type `ptrdiff_t'. */ +#undef HAVE_PTRDIFF_T + +/* Define to 1 if you have the header file. */ +#undef HAVE_PWD_H + +/* If available, contains the Python version number currently in use. */ +#undef HAVE_PYTHON + +/* Define to 1 if you have the `socket' function. */ +#undef HAVE_SOCKET + +/* Define to 1 if you have `spdylay` library. */ +#undef HAVE_SPDYLAY + +/* Define to 1 if you have the `sqrt' function. */ +#undef HAVE_SQRT + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDDEF_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDINT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDLIB_H + +/* Define to 1 if you have the `std::future`. */ +#undef HAVE_STD_FUTURE + +/* Define to 1 if you have the `std::map::emplace`. */ +#undef HAVE_STD_MAP_EMPLACE + +/* Define to 1 if you have the `std::chrono::steady_clock`. */ +#undef HAVE_STEADY_CLOCK + +/* Define to 1 if you have the `strchr' function. */ +#undef HAVE_STRCHR + +/* Define to 1 if you have the `strdup' function. */ +#undef HAVE_STRDUP + +/* Define to 1 if you have the `strerror' function. */ +#undef HAVE_STRERROR + +/* Define to 1 if you have the `strerror_r' function. */ +#undef HAVE_STRERROR_R + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRINGS_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRING_H + +/* Define to 1 if you have the `strndup' function. */ +#undef HAVE_STRNDUP + +/* Define to 1 if you have the `strstr' function. */ +#undef HAVE_STRSTR + +/* Define to 1 if you have the `strtol' function. */ +#undef HAVE_STRTOL + +/* Define to 1 if you have the `strtoul' function. */ +#undef HAVE_STRTOUL + +/* Define to 1 if you have `struct tm.tm_gmtoff` member. */ +#undef HAVE_STRUCT_TM_TM_GMTOFF + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYSLOG_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_SOCKET_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_STAT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_TIME_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_TYPES_H + +/* Define to 1 if you have the `timegm' function. */ +#undef HAVE_TIMEGM + +/* Define to 1 if you have the header file. */ +#undef HAVE_TIME_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_UNISTD_H + +/* Define to 1 if you have the `vfork' function. */ +#undef HAVE_VFORK + +/* Define to 1 if you have the header file. */ +#undef HAVE_VFORK_H + +/* Define to 1 if `fork' works. */ +#undef HAVE_WORKING_FORK + +/* Define to 1 if `vfork' works. */ +#undef HAVE_WORKING_VFORK + +/* Define to 1 if you have the `_Exit' function. */ +#undef HAVE__EXIT + +/* Define to the sub-directory in which libtool stores uninstalled libraries. + */ +#undef LT_OBJDIR + +/* Define to 1 if assertions should be disabled. */ +#undef NDEBUG + +/* Define to 1 if you want to disable threads. */ +#undef NOTHREADS + +/* Name of package */ +#undef PACKAGE + +/* Define to the address where bug reports for this package should be sent. */ +#undef PACKAGE_BUGREPORT + +/* Define to the full name of this package. */ +#undef PACKAGE_NAME + +/* Define to the full name and version of this package. */ +#undef PACKAGE_STRING + +/* Define to the one symbol short name of this package. */ +#undef PACKAGE_TARNAME + +/* Define to the home page for this package. */ +#undef PACKAGE_URL + +/* Define to the version of this package. */ +#undef PACKAGE_VERSION + +/* The size of `int *', as computed by sizeof. */ +#undef SIZEOF_INT_P + +/* Define to 1 if you have the ANSI C header files. */ +#undef STDC_HEADERS + +/* Define to 1 if strerror_r returns char *. */ +#undef STRERROR_R_CHAR_P + +/* Enable extensions on AIX 3, Interix. */ +#ifndef _ALL_SOURCE +# undef _ALL_SOURCE +#endif +/* Enable GNU extensions on systems that have them. */ +#ifndef _GNU_SOURCE +# undef _GNU_SOURCE +#endif +/* Enable threading extensions on Solaris. */ +#ifndef _POSIX_PTHREAD_SEMANTICS +# undef _POSIX_PTHREAD_SEMANTICS +#endif +/* Enable extensions on HP NonStop. */ +#ifndef _TANDEM_SOURCE +# undef _TANDEM_SOURCE +#endif +/* Enable general extensions on Solaris. */ +#ifndef __EXTENSIONS__ +# undef __EXTENSIONS__ +#endif + + +/* Version number of package */ +#undef VERSION + +/* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most + significant byte first (like Motorola and SPARC, unlike Intel). */ +#if defined AC_APPLE_UNIVERSAL_BUILD +# if defined __BIG_ENDIAN__ +# define WORDS_BIGENDIAN 1 +# endif +#else +# ifndef WORDS_BIGENDIAN +# undef WORDS_BIGENDIAN +# endif +#endif + +/* Enable large inode numbers on Mac OS X 10.5. */ +#ifndef _DARWIN_USE_64_BIT_INODE +# define _DARWIN_USE_64_BIT_INODE 1 +#endif + +/* Number of bits in a file offset, on hosts where this is settable. */ +#undef _FILE_OFFSET_BITS + +/* Define for large files, on AIX-style hosts. */ +#undef _LARGE_FILES + +/* Define to 1 if on MINIX. */ +#undef _MINIX + +/* Define to 2 if the system does not provide POSIX.1 features except with + this defined. */ +#undef _POSIX_1_SOURCE + +/* Define to 1 if you need to in order for `stat' and other things to work. */ +#undef _POSIX_SOURCE + +/* Define for Solaris 2.5.1 so the uint32_t typedef from , + , or is not used. If the typedef were allowed, the + #define below would cause a syntax error. */ +#undef _UINT32_T + +/* Define for Solaris 2.5.1 so the uint64_t typedef from , + , or is not used. If the typedef were allowed, the + #define below would cause a syntax error. */ +#undef _UINT64_T + +/* Define for Solaris 2.5.1 so the uint8_t typedef from , + , or is not used. If the typedef were allowed, the + #define below would cause a syntax error. */ +#undef _UINT8_T + +/* Hint to the compiler that a function parameters is not used */ +#undef _U_ + +/* Define to `int' if doesn't define. */ +#undef gid_t + +/* Define to `__inline__' or `__inline' if that's what the C compiler + calls it, or to nothing if 'inline' is not supported under any name. */ +#ifndef __cplusplus +#undef inline +#endif + +/* Define to the type of a signed integer type of width exactly 16 bits if + such a type exists and the standard includes do not define it. */ +#undef int16_t + +/* Define to the type of a signed integer type of width exactly 32 bits if + such a type exists and the standard includes do not define it. */ +#undef int32_t + +/* Define to the type of a signed integer type of width exactly 64 bits if + such a type exists and the standard includes do not define it. */ +#undef int64_t + +/* Define to the type of a signed integer type of width exactly 8 bits if such + a type exists and the standard includes do not define it. */ +#undef int8_t + +/* Define to rpl_malloc if the replacement function should be used. */ +#undef malloc + +/* Define to `long int' if does not define. */ +#undef off_t + +/* Define to `int' if does not define. */ +#undef pid_t + +/* Define to `unsigned int' if does not define. */ +#undef size_t + +/* Define to `int' if does not define. */ +#undef ssize_t + +/* Define to `int' if doesn't define. */ +#undef uid_t + +/* Define to the type of an unsigned integer type of width exactly 16 bits if + such a type exists and the standard includes do not define it. */ +#undef uint16_t + +/* Define to the type of an unsigned integer type of width exactly 32 bits if + such a type exists and the standard includes do not define it. */ +#undef uint32_t + +/* Define to the type of an unsigned integer type of width exactly 64 bits if + such a type exists and the standard includes do not define it. */ +#undef uint64_t + +/* Define to the type of an unsigned integer type of width exactly 8 bits if + such a type exists and the standard includes do not define it. */ +#undef uint8_t + +/* Define as `fork' if `vfork' does not work. */ +#undef vfork diff --git a/config.sub b/config.sub new file mode 100755 index 0000000..bba4efb --- /dev/null +++ b/config.sub @@ -0,0 +1,1799 @@ +#! /bin/sh +# Configuration validation subroutine script. +# Copyright 1992-2014 Free Software Foundation, Inc. + +timestamp='2014-09-11' + +# This file 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 . +# +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that +# program. This Exception is an additional permission under section 7 +# of the GNU General Public License, version 3 ("GPLv3"). + + +# Please send patches with a ChangeLog entry to config-patches@gnu.org. +# +# Configuration subroutine to validate and canonicalize a configuration type. +# Supply the specified configuration type as an argument. +# If it is invalid, we print an error message on stderr and exit with code 1. +# Otherwise, we print the canonical config type on stdout and succeed. + +# You can get the latest version of this script from: +# http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub;hb=HEAD + +# This file is supposed to be the same for all GNU packages +# and recognize all the CPU types, system types and aliases +# that are meaningful with *any* GNU software. +# Each package is responsible for reporting which valid configurations +# it does not support. The user should be able to distinguish +# a failure to support a valid configuration from a meaningless +# configuration. + +# The goal of this file is to map all the various variations of a given +# machine specification into a single specification in the form: +# CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM +# or in some cases, the newer four-part form: +# CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM +# It is wrong to echo any other type of specification. + +me=`echo "$0" | sed -e 's,.*/,,'` + +usage="\ +Usage: $0 [OPTION] CPU-MFR-OPSYS + $0 [OPTION] ALIAS + +Canonicalize a configuration name. + +Operation modes: + -h, --help print this help, then exit + -t, --time-stamp print date of last modification, then exit + -v, --version print version number, then exit + +Report bugs and patches to ." + +version="\ +GNU config.sub ($timestamp) + +Copyright 1992-2014 Free Software Foundation, Inc. + +This is free software; see the source for copying conditions. There is NO +warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." + +help=" +Try \`$me --help' for more information." + +# Parse command line +while test $# -gt 0 ; do + case $1 in + --time-stamp | --time* | -t ) + echo "$timestamp" ; exit ;; + --version | -v ) + echo "$version" ; exit ;; + --help | --h* | -h ) + echo "$usage"; exit ;; + -- ) # Stop option processing + shift; break ;; + - ) # Use stdin as input. + break ;; + -* ) + echo "$me: invalid option $1$help" + exit 1 ;; + + *local*) + # First pass through any local machine types. + echo $1 + exit ;; + + * ) + break ;; + esac +done + +case $# in + 0) echo "$me: missing argument$help" >&2 + exit 1;; + 1) ;; + *) echo "$me: too many arguments$help" >&2 + exit 1;; +esac + +# Separate what the user gave into CPU-COMPANY and OS or KERNEL-OS (if any). +# Here we must recognize all the valid KERNEL-OS combinations. +maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'` +case $maybe_os in + nto-qnx* | linux-gnu* | linux-android* | linux-dietlibc | linux-newlib* | \ + linux-musl* | linux-uclibc* | uclinux-uclibc* | uclinux-gnu* | kfreebsd*-gnu* | \ + knetbsd*-gnu* | netbsd*-gnu* | \ + kopensolaris*-gnu* | \ + storm-chaos* | os2-emx* | rtmk-nova*) + os=-$maybe_os + basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'` + ;; + android-linux) + os=-linux-android + basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'`-unknown + ;; + *) + basic_machine=`echo $1 | sed 's/-[^-]*$//'` + if [ $basic_machine != $1 ] + then os=`echo $1 | sed 's/.*-/-/'` + else os=; fi + ;; +esac + +### Let's recognize common machines as not being operating systems so +### that things like config.sub decstation-3100 work. We also +### recognize some manufacturers as not being operating systems, so we +### can provide default operating systems below. +case $os in + -sun*os*) + # Prevent following clause from handling this invalid input. + ;; + -dec* | -mips* | -sequent* | -encore* | -pc532* | -sgi* | -sony* | \ + -att* | -7300* | -3300* | -delta* | -motorola* | -sun[234]* | \ + -unicom* | -ibm* | -next | -hp | -isi* | -apollo | -altos* | \ + -convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\ + -c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \ + -harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp | \ + -apple | -axis | -knuth | -cray | -microblaze*) + os= + basic_machine=$1 + ;; + -bluegene*) + os=-cnk + ;; + -sim | -cisco | -oki | -wec | -winbond) + os= + basic_machine=$1 + ;; + -scout) + ;; + -wrs) + os=-vxworks + basic_machine=$1 + ;; + -chorusos*) + os=-chorusos + basic_machine=$1 + ;; + -chorusrdb) + os=-chorusrdb + basic_machine=$1 + ;; + -hiux*) + os=-hiuxwe2 + ;; + -sco6) + os=-sco5v6 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco5) + os=-sco3.2v5 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco4) + os=-sco3.2v4 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco3.2.[4-9]*) + os=`echo $os | sed -e 's/sco3.2./sco3.2v/'` + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco3.2v[4-9]*) + # Don't forget version if it is 3.2v4 or newer. + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco5v6*) + # Don't forget version if it is 3.2v4 or newer. + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco*) + os=-sco3.2v2 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -udk*) + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -isc) + os=-isc2.2 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -clix*) + basic_machine=clipper-intergraph + ;; + -isc*) + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -lynx*178) + os=-lynxos178 + ;; + -lynx*5) + os=-lynxos5 + ;; + -lynx*) + os=-lynxos + ;; + -ptx*) + basic_machine=`echo $1 | sed -e 's/86-.*/86-sequent/'` + ;; + -windowsnt*) + os=`echo $os | sed -e 's/windowsnt/winnt/'` + ;; + -psos*) + os=-psos + ;; + -mint | -mint[0-9]*) + basic_machine=m68k-atari + os=-mint + ;; +esac + +# Decode aliases for certain CPU-COMPANY combinations. +case $basic_machine in + # Recognize the basic CPU types without company name. + # Some are omitted here because they have special meanings below. + 1750a | 580 \ + | a29k \ + | aarch64 | aarch64_be \ + | alpha | alphaev[4-8] | alphaev56 | alphaev6[78] | alphapca5[67] \ + | alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] | alpha64pca5[67] \ + | am33_2.0 \ + | arc | arceb \ + | arm | arm[bl]e | arme[lb] | armv[2-8] | armv[3-8][lb] | armv7[arm] \ + | avr | avr32 \ + | be32 | be64 \ + | bfin \ + | c4x | c8051 | clipper \ + | d10v | d30v | dlx | dsp16xx \ + | epiphany \ + | fido | fr30 | frv \ + | h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \ + | hexagon \ + | i370 | i860 | i960 | ia64 \ + | ip2k | iq2000 \ + | k1om \ + | le32 | le64 \ + | lm32 \ + | m32c | m32r | m32rle | m68000 | m68k | m88k \ + | maxq | mb | microblaze | microblazeel | mcore | mep | metag \ + | mips | mipsbe | mipseb | mipsel | mipsle \ + | mips16 \ + | mips64 | mips64el \ + | mips64octeon | mips64octeonel \ + | mips64orion | mips64orionel \ + | mips64r5900 | mips64r5900el \ + | mips64vr | mips64vrel \ + | mips64vr4100 | mips64vr4100el \ + | mips64vr4300 | mips64vr4300el \ + | mips64vr5000 | mips64vr5000el \ + | mips64vr5900 | mips64vr5900el \ + | mipsisa32 | mipsisa32el \ + | mipsisa32r2 | mipsisa32r2el \ + | mipsisa32r6 | mipsisa32r6el \ + | mipsisa64 | mipsisa64el \ + | mipsisa64r2 | mipsisa64r2el \ + | mipsisa64r6 | mipsisa64r6el \ + | mipsisa64sb1 | mipsisa64sb1el \ + | mipsisa64sr71k | mipsisa64sr71kel \ + | mipsr5900 | mipsr5900el \ + | mipstx39 | mipstx39el \ + | mn10200 | mn10300 \ + | moxie \ + | mt \ + | msp430 \ + | nds32 | nds32le | nds32be \ + | nios | nios2 | nios2eb | nios2el \ + | ns16k | ns32k \ + | open8 | or1k | or1knd | or32 \ + | pdp10 | pdp11 | pj | pjl \ + | powerpc | powerpc64 | powerpc64le | powerpcle \ + | pyramid \ + | riscv32 | riscv64 \ + | rl78 | rx \ + | score \ + | sh | sh[1234] | sh[24]a | sh[24]aeb | sh[23]e | sh[34]eb | sheb | shbe | shle | sh[1234]le | sh3ele \ + | sh64 | sh64le \ + | sparc | sparc64 | sparc64b | sparc64v | sparc86x | sparclet | sparclite \ + | sparcv8 | sparcv9 | sparcv9b | sparcv9v \ + | spu \ + | tahoe | tic4x | tic54x | tic55x | tic6x | tic80 | tron \ + | ubicom32 \ + | v850 | v850e | v850e1 | v850e2 | v850es | v850e2v3 \ + | we32k \ + | x86 | xc16x | xstormy16 | xtensa \ + | z8k | z80) + basic_machine=$basic_machine-unknown + ;; + c54x) + basic_machine=tic54x-unknown + ;; + c55x) + basic_machine=tic55x-unknown + ;; + c6x) + basic_machine=tic6x-unknown + ;; + m6811 | m68hc11 | m6812 | m68hc12 | m68hcs12x | nvptx | picochip) + basic_machine=$basic_machine-unknown + os=-none + ;; + m88110 | m680[12346]0 | m683?2 | m68360 | m5200 | v70 | w65 | z8k) + ;; + ms1) + basic_machine=mt-unknown + ;; + + strongarm | thumb | xscale) + basic_machine=arm-unknown + ;; + xgate) + basic_machine=$basic_machine-unknown + os=-none + ;; + xscaleeb) + basic_machine=armeb-unknown + ;; + + xscaleel) + basic_machine=armel-unknown + ;; + + # We use `pc' rather than `unknown' + # because (1) that's what they normally are, and + # (2) the word "unknown" tends to confuse beginning users. + i*86 | x86_64) + basic_machine=$basic_machine-pc + ;; + # Object if more than one company name word. + *-*-*) + echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2 + exit 1 + ;; + # Recognize the basic CPU types with company name. + 580-* \ + | a29k-* \ + | aarch64-* | aarch64_be-* \ + | alpha-* | alphaev[4-8]-* | alphaev56-* | alphaev6[78]-* \ + | alpha64-* | alpha64ev[4-8]-* | alpha64ev56-* | alpha64ev6[78]-* \ + | alphapca5[67]-* | alpha64pca5[67]-* | arc-* | arceb-* \ + | arm-* | armbe-* | armle-* | armeb-* | armv*-* \ + | avr-* | avr32-* \ + | be32-* | be64-* \ + | bfin-* | bs2000-* \ + | c[123]* | c30-* | [cjt]90-* | c4x-* \ + | c8051-* | clipper-* | craynv-* | cydra-* \ + | d10v-* | d30v-* | dlx-* \ + | elxsi-* \ + | f30[01]-* | f700-* | fido-* | fr30-* | frv-* | fx80-* \ + | h8300-* | h8500-* \ + | hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \ + | hexagon-* \ + | i*86-* | i860-* | i960-* | ia64-* \ + | ip2k-* | iq2000-* \ + | k1om-* \ + | le32-* | le64-* \ + | lm32-* \ + | m32c-* | m32r-* | m32rle-* \ + | m68000-* | m680[012346]0-* | m68360-* | m683?2-* | m68k-* \ + | m88110-* | m88k-* | maxq-* | mcore-* | metag-* \ + | microblaze-* | microblazeel-* \ + | mips-* | mipsbe-* | mipseb-* | mipsel-* | mipsle-* \ + | mips16-* \ + | mips64-* | mips64el-* \ + | mips64octeon-* | mips64octeonel-* \ + | mips64orion-* | mips64orionel-* \ + | mips64r5900-* | mips64r5900el-* \ + | mips64vr-* | mips64vrel-* \ + | mips64vr4100-* | mips64vr4100el-* \ + | mips64vr4300-* | mips64vr4300el-* \ + | mips64vr5000-* | mips64vr5000el-* \ + | mips64vr5900-* | mips64vr5900el-* \ + | mipsisa32-* | mipsisa32el-* \ + | mipsisa32r2-* | mipsisa32r2el-* \ + | mipsisa32r6-* | mipsisa32r6el-* \ + | mipsisa64-* | mipsisa64el-* \ + | mipsisa64r2-* | mipsisa64r2el-* \ + | mipsisa64r6-* | mipsisa64r6el-* \ + | mipsisa64sb1-* | mipsisa64sb1el-* \ + | mipsisa64sr71k-* | mipsisa64sr71kel-* \ + | mipsr5900-* | mipsr5900el-* \ + | mipstx39-* | mipstx39el-* \ + | mmix-* \ + | mt-* \ + | msp430-* \ + | nds32-* | nds32le-* | nds32be-* \ + | nios-* | nios2-* | nios2eb-* | nios2el-* \ + | none-* | np1-* | ns16k-* | ns32k-* \ + | open8-* \ + | or1k*-* \ + | orion-* \ + | pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \ + | powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* \ + | pyramid-* \ + | rl78-* | romp-* | rs6000-* | rx-* \ + | sh-* | sh[1234]-* | sh[24]a-* | sh[24]aeb-* | sh[23]e-* | sh[34]eb-* | sheb-* | shbe-* \ + | shle-* | sh[1234]le-* | sh3ele-* | sh64-* | sh64le-* \ + | sparc-* | sparc64-* | sparc64b-* | sparc64v-* | sparc86x-* | sparclet-* \ + | sparclite-* \ + | sparcv8-* | sparcv9-* | sparcv9b-* | sparcv9v-* | sv1-* | sx?-* \ + | tahoe-* \ + | tic30-* | tic4x-* | tic54x-* | tic55x-* | tic6x-* | tic80-* \ + | tile*-* \ + | tron-* \ + | ubicom32-* \ + | v850-* | v850e-* | v850e1-* | v850es-* | v850e2-* | v850e2v3-* \ + | vax-* \ + | we32k-* \ + | x86-* | x86_64-* | xc16x-* | xps100-* \ + | xstormy16-* | xtensa*-* \ + | ymp-* \ + | z8k-* | z80-*) + ;; + # Recognize the basic CPU types without company name, with glob match. + xtensa*) + basic_machine=$basic_machine-unknown + ;; + # Recognize the various machine names and aliases which stand + # for a CPU type and a company and sometimes even an OS. + 386bsd) + basic_machine=i386-unknown + os=-bsd + ;; + 3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc) + basic_machine=m68000-att + ;; + 3b*) + basic_machine=we32k-att + ;; + a29khif) + basic_machine=a29k-amd + os=-udi + ;; + abacus) + basic_machine=abacus-unknown + ;; + adobe68k) + basic_machine=m68010-adobe + os=-scout + ;; + alliant | fx80) + basic_machine=fx80-alliant + ;; + altos | altos3068) + basic_machine=m68k-altos + ;; + am29k) + basic_machine=a29k-none + os=-bsd + ;; + amd64) + basic_machine=x86_64-pc + ;; + amd64-*) + basic_machine=x86_64-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + amdahl) + basic_machine=580-amdahl + os=-sysv + ;; + amiga | amiga-*) + basic_machine=m68k-unknown + ;; + amigaos | amigados) + basic_machine=m68k-unknown + os=-amigaos + ;; + amigaunix | amix) + basic_machine=m68k-unknown + os=-sysv4 + ;; + apollo68) + basic_machine=m68k-apollo + os=-sysv + ;; + apollo68bsd) + basic_machine=m68k-apollo + os=-bsd + ;; + aros) + basic_machine=i386-pc + os=-aros + ;; + aux) + basic_machine=m68k-apple + os=-aux + ;; + balance) + basic_machine=ns32k-sequent + os=-dynix + ;; + blackfin) + basic_machine=bfin-unknown + os=-linux + ;; + blackfin-*) + basic_machine=bfin-`echo $basic_machine | sed 's/^[^-]*-//'` + os=-linux + ;; + bluegene*) + basic_machine=powerpc-ibm + os=-cnk + ;; + c54x-*) + basic_machine=tic54x-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + c55x-*) + basic_machine=tic55x-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + c6x-*) + basic_machine=tic6x-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + c90) + basic_machine=c90-cray + os=-unicos + ;; + cegcc) + basic_machine=arm-unknown + os=-cegcc + ;; + convex-c1) + basic_machine=c1-convex + os=-bsd + ;; + convex-c2) + basic_machine=c2-convex + os=-bsd + ;; + convex-c32) + basic_machine=c32-convex + os=-bsd + ;; + convex-c34) + basic_machine=c34-convex + os=-bsd + ;; + convex-c38) + basic_machine=c38-convex + os=-bsd + ;; + cray | j90) + basic_machine=j90-cray + os=-unicos + ;; + craynv) + basic_machine=craynv-cray + os=-unicosmp + ;; + cr16 | cr16-*) + basic_machine=cr16-unknown + os=-elf + ;; + crds | unos) + basic_machine=m68k-crds + ;; + crisv32 | crisv32-* | etraxfs*) + basic_machine=crisv32-axis + ;; + cris | cris-* | etrax*) + basic_machine=cris-axis + ;; + crx) + basic_machine=crx-unknown + os=-elf + ;; + da30 | da30-*) + basic_machine=m68k-da30 + ;; + decstation | decstation-3100 | pmax | pmax-* | pmin | dec3100 | decstatn) + basic_machine=mips-dec + ;; + decsystem10* | dec10*) + basic_machine=pdp10-dec + os=-tops10 + ;; + decsystem20* | dec20*) + basic_machine=pdp10-dec + os=-tops20 + ;; + delta | 3300 | motorola-3300 | motorola-delta \ + | 3300-motorola | delta-motorola) + basic_machine=m68k-motorola + ;; + delta88) + basic_machine=m88k-motorola + os=-sysv3 + ;; + dicos) + basic_machine=i686-pc + os=-dicos + ;; + djgpp) + basic_machine=i586-pc + os=-msdosdjgpp + ;; + dpx20 | dpx20-*) + basic_machine=rs6000-bull + os=-bosx + ;; + dpx2* | dpx2*-bull) + basic_machine=m68k-bull + os=-sysv3 + ;; + ebmon29k) + basic_machine=a29k-amd + os=-ebmon + ;; + elxsi) + basic_machine=elxsi-elxsi + os=-bsd + ;; + encore | umax | mmax) + basic_machine=ns32k-encore + ;; + es1800 | OSE68k | ose68k | ose | OSE) + basic_machine=m68k-ericsson + os=-ose + ;; + fx2800) + basic_machine=i860-alliant + ;; + genix) + basic_machine=ns32k-ns + ;; + gmicro) + basic_machine=tron-gmicro + os=-sysv + ;; + go32) + basic_machine=i386-pc + os=-go32 + ;; + h3050r* | hiux*) + basic_machine=hppa1.1-hitachi + os=-hiuxwe2 + ;; + h8300hms) + basic_machine=h8300-hitachi + os=-hms + ;; + h8300xray) + basic_machine=h8300-hitachi + os=-xray + ;; + h8500hms) + basic_machine=h8500-hitachi + os=-hms + ;; + harris) + basic_machine=m88k-harris + os=-sysv3 + ;; + hp300-*) + basic_machine=m68k-hp + ;; + hp300bsd) + basic_machine=m68k-hp + os=-bsd + ;; + hp300hpux) + basic_machine=m68k-hp + os=-hpux + ;; + hp3k9[0-9][0-9] | hp9[0-9][0-9]) + basic_machine=hppa1.0-hp + ;; + hp9k2[0-9][0-9] | hp9k31[0-9]) + basic_machine=m68000-hp + ;; + hp9k3[2-9][0-9]) + basic_machine=m68k-hp + ;; + hp9k6[0-9][0-9] | hp6[0-9][0-9]) + basic_machine=hppa1.0-hp + ;; + hp9k7[0-79][0-9] | hp7[0-79][0-9]) + basic_machine=hppa1.1-hp + ;; + hp9k78[0-9] | hp78[0-9]) + # FIXME: really hppa2.0-hp + basic_machine=hppa1.1-hp + ;; + hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893) + # FIXME: really hppa2.0-hp + basic_machine=hppa1.1-hp + ;; + hp9k8[0-9][13679] | hp8[0-9][13679]) + basic_machine=hppa1.1-hp + ;; + hp9k8[0-9][0-9] | hp8[0-9][0-9]) + basic_machine=hppa1.0-hp + ;; + hppa-next) + os=-nextstep3 + ;; + hppaosf) + basic_machine=hppa1.1-hp + os=-osf + ;; + hppro) + basic_machine=hppa1.1-hp + os=-proelf + ;; + i370-ibm* | ibm*) + basic_machine=i370-ibm + ;; + i*86v32) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-sysv32 + ;; + i*86v4*) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-sysv4 + ;; + i*86v) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-sysv + ;; + i*86sol2) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-solaris2 + ;; + i386mach) + basic_machine=i386-mach + os=-mach + ;; + i386-vsta | vsta) + basic_machine=i386-unknown + os=-vsta + ;; + iris | iris4d) + basic_machine=mips-sgi + case $os in + -irix*) + ;; + *) + os=-irix4 + ;; + esac + ;; + isi68 | isi) + basic_machine=m68k-isi + os=-sysv + ;; + m68knommu) + basic_machine=m68k-unknown + os=-linux + ;; + m68knommu-*) + basic_machine=m68k-`echo $basic_machine | sed 's/^[^-]*-//'` + os=-linux + ;; + m88k-omron*) + basic_machine=m88k-omron + ;; + magnum | m3230) + basic_machine=mips-mips + os=-sysv + ;; + merlin) + basic_machine=ns32k-utek + os=-sysv + ;; + microblaze*) + basic_machine=microblaze-xilinx + ;; + mingw64) + basic_machine=x86_64-pc + os=-mingw64 + ;; + mingw32) + basic_machine=i686-pc + os=-mingw32 + ;; + mingw32ce) + basic_machine=arm-unknown + os=-mingw32ce + ;; + miniframe) + basic_machine=m68000-convergent + ;; + *mint | -mint[0-9]* | *MiNT | *MiNT[0-9]*) + basic_machine=m68k-atari + os=-mint + ;; + mips3*-*) + basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'` + ;; + mips3*) + basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`-unknown + ;; + monitor) + basic_machine=m68k-rom68k + os=-coff + ;; + morphos) + basic_machine=powerpc-unknown + os=-morphos + ;; + moxiebox) + basic_machine=moxie-unknown + os=-moxiebox + ;; + msdos) + basic_machine=i386-pc + os=-msdos + ;; + ms1-*) + basic_machine=`echo $basic_machine | sed -e 's/ms1-/mt-/'` + ;; + msys) + basic_machine=i686-pc + os=-msys + ;; + mvs) + basic_machine=i370-ibm + os=-mvs + ;; + nacl) + basic_machine=le32-unknown + os=-nacl + ;; + ncr3000) + basic_machine=i486-ncr + os=-sysv4 + ;; + netbsd386) + basic_machine=i386-unknown + os=-netbsd + ;; + netwinder) + basic_machine=armv4l-rebel + os=-linux + ;; + news | news700 | news800 | news900) + basic_machine=m68k-sony + os=-newsos + ;; + news1000) + basic_machine=m68030-sony + os=-newsos + ;; + news-3600 | risc-news) + basic_machine=mips-sony + os=-newsos + ;; + necv70) + basic_machine=v70-nec + os=-sysv + ;; + next | m*-next ) + basic_machine=m68k-next + case $os in + -nextstep* ) + ;; + -ns2*) + os=-nextstep2 + ;; + *) + os=-nextstep3 + ;; + esac + ;; + nh3000) + basic_machine=m68k-harris + os=-cxux + ;; + nh[45]000) + basic_machine=m88k-harris + os=-cxux + ;; + nindy960) + basic_machine=i960-intel + os=-nindy + ;; + mon960) + basic_machine=i960-intel + os=-mon960 + ;; + nonstopux) + basic_machine=mips-compaq + os=-nonstopux + ;; + np1) + basic_machine=np1-gould + ;; + neo-tandem) + basic_machine=neo-tandem + ;; + nse-tandem) + basic_machine=nse-tandem + ;; + nsr-tandem) + basic_machine=nsr-tandem + ;; + op50n-* | op60c-*) + basic_machine=hppa1.1-oki + os=-proelf + ;; + openrisc | openrisc-*) + basic_machine=or32-unknown + ;; + os400) + basic_machine=powerpc-ibm + os=-os400 + ;; + OSE68000 | ose68000) + basic_machine=m68000-ericsson + os=-ose + ;; + os68k) + basic_machine=m68k-none + os=-os68k + ;; + pa-hitachi) + basic_machine=hppa1.1-hitachi + os=-hiuxwe2 + ;; + paragon) + basic_machine=i860-intel + os=-osf + ;; + parisc) + basic_machine=hppa-unknown + os=-linux + ;; + parisc-*) + basic_machine=hppa-`echo $basic_machine | sed 's/^[^-]*-//'` + os=-linux + ;; + pbd) + basic_machine=sparc-tti + ;; + pbb) + basic_machine=m68k-tti + ;; + pc532 | pc532-*) + basic_machine=ns32k-pc532 + ;; + pc98) + basic_machine=i386-pc + ;; + pc98-*) + basic_machine=i386-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pentium | p5 | k5 | k6 | nexgen | viac3) + basic_machine=i586-pc + ;; + pentiumpro | p6 | 6x86 | athlon | athlon_*) + basic_machine=i686-pc + ;; + pentiumii | pentium2 | pentiumiii | pentium3) + basic_machine=i686-pc + ;; + pentium4) + basic_machine=i786-pc + ;; + pentium-* | p5-* | k5-* | k6-* | nexgen-* | viac3-*) + basic_machine=i586-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pentiumpro-* | p6-* | 6x86-* | athlon-*) + basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pentiumii-* | pentium2-* | pentiumiii-* | pentium3-*) + basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pentium4-*) + basic_machine=i786-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pn) + basic_machine=pn-gould + ;; + power) basic_machine=power-ibm + ;; + ppc | ppcbe) basic_machine=powerpc-unknown + ;; + ppc-* | ppcbe-*) + basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + ppcle | powerpclittle | ppc-le | powerpc-little) + basic_machine=powerpcle-unknown + ;; + ppcle-* | powerpclittle-*) + basic_machine=powerpcle-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + ppc64) basic_machine=powerpc64-unknown + ;; + ppc64-*) basic_machine=powerpc64-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + ppc64le | powerpc64little | ppc64-le | powerpc64-little) + basic_machine=powerpc64le-unknown + ;; + ppc64le-* | powerpc64little-*) + basic_machine=powerpc64le-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + ps2) + basic_machine=i386-ibm + ;; + pw32) + basic_machine=i586-unknown + os=-pw32 + ;; + rdos | rdos64) + basic_machine=x86_64-pc + os=-rdos + ;; + rdos32) + basic_machine=i386-pc + os=-rdos + ;; + rom68k) + basic_machine=m68k-rom68k + os=-coff + ;; + rm[46]00) + basic_machine=mips-siemens + ;; + rtpc | rtpc-*) + basic_machine=romp-ibm + ;; + s390 | s390-*) + basic_machine=s390-ibm + ;; + s390x | s390x-*) + basic_machine=s390x-ibm + ;; + sa29200) + basic_machine=a29k-amd + os=-udi + ;; + sb1) + basic_machine=mipsisa64sb1-unknown + ;; + sb1el) + basic_machine=mipsisa64sb1el-unknown + ;; + sde) + basic_machine=mipsisa32-sde + os=-elf + ;; + sei) + basic_machine=mips-sei + os=-seiux + ;; + sequent) + basic_machine=i386-sequent + ;; + sh) + basic_machine=sh-hitachi + os=-hms + ;; + sh5el) + basic_machine=sh5le-unknown + ;; + sh64) + basic_machine=sh64-unknown + ;; + sparclite-wrs | simso-wrs) + basic_machine=sparclite-wrs + os=-vxworks + ;; + sps7) + basic_machine=m68k-bull + os=-sysv2 + ;; + spur) + basic_machine=spur-unknown + ;; + st2000) + basic_machine=m68k-tandem + ;; + stratus) + basic_machine=i860-stratus + os=-sysv4 + ;; + strongarm-* | thumb-*) + basic_machine=arm-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + sun2) + basic_machine=m68000-sun + ;; + sun2os3) + basic_machine=m68000-sun + os=-sunos3 + ;; + sun2os4) + basic_machine=m68000-sun + os=-sunos4 + ;; + sun3os3) + basic_machine=m68k-sun + os=-sunos3 + ;; + sun3os4) + basic_machine=m68k-sun + os=-sunos4 + ;; + sun4os3) + basic_machine=sparc-sun + os=-sunos3 + ;; + sun4os4) + basic_machine=sparc-sun + os=-sunos4 + ;; + sun4sol2) + basic_machine=sparc-sun + os=-solaris2 + ;; + sun3 | sun3-*) + basic_machine=m68k-sun + ;; + sun4) + basic_machine=sparc-sun + ;; + sun386 | sun386i | roadrunner) + basic_machine=i386-sun + ;; + sv1) + basic_machine=sv1-cray + os=-unicos + ;; + symmetry) + basic_machine=i386-sequent + os=-dynix + ;; + t3e) + basic_machine=alphaev5-cray + os=-unicos + ;; + t90) + basic_machine=t90-cray + os=-unicos + ;; + tile*) + basic_machine=$basic_machine-unknown + os=-linux-gnu + ;; + tx39) + basic_machine=mipstx39-unknown + ;; + tx39el) + basic_machine=mipstx39el-unknown + ;; + toad1) + basic_machine=pdp10-xkl + os=-tops20 + ;; + tower | tower-32) + basic_machine=m68k-ncr + ;; + tpf) + basic_machine=s390x-ibm + os=-tpf + ;; + udi29k) + basic_machine=a29k-amd + os=-udi + ;; + ultra3) + basic_machine=a29k-nyu + os=-sym1 + ;; + v810 | necv810) + basic_machine=v810-nec + os=-none + ;; + vaxv) + basic_machine=vax-dec + os=-sysv + ;; + vms) + basic_machine=vax-dec + os=-vms + ;; + vpp*|vx|vx-*) + basic_machine=f301-fujitsu + ;; + vxworks960) + basic_machine=i960-wrs + os=-vxworks + ;; + vxworks68) + basic_machine=m68k-wrs + os=-vxworks + ;; + vxworks29k) + basic_machine=a29k-wrs + os=-vxworks + ;; + w65*) + basic_machine=w65-wdc + os=-none + ;; + w89k-*) + basic_machine=hppa1.1-winbond + os=-proelf + ;; + xbox) + basic_machine=i686-pc + os=-mingw32 + ;; + xps | xps100) + basic_machine=xps100-honeywell + ;; + xscale-* | xscalee[bl]-*) + basic_machine=`echo $basic_machine | sed 's/^xscale/arm/'` + ;; + ymp) + basic_machine=ymp-cray + os=-unicos + ;; + z8k-*-coff) + basic_machine=z8k-unknown + os=-sim + ;; + z80-*-coff) + basic_machine=z80-unknown + os=-sim + ;; + none) + basic_machine=none-none + os=-none + ;; + +# Here we handle the default manufacturer of certain CPU types. It is in +# some cases the only manufacturer, in others, it is the most popular. + w89k) + basic_machine=hppa1.1-winbond + ;; + op50n) + basic_machine=hppa1.1-oki + ;; + op60c) + basic_machine=hppa1.1-oki + ;; + romp) + basic_machine=romp-ibm + ;; + mmix) + basic_machine=mmix-knuth + ;; + rs6000) + basic_machine=rs6000-ibm + ;; + vax) + basic_machine=vax-dec + ;; + pdp10) + # there are many clones, so DEC is not a safe bet + basic_machine=pdp10-unknown + ;; + pdp11) + basic_machine=pdp11-dec + ;; + we32k) + basic_machine=we32k-att + ;; + sh[1234] | sh[24]a | sh[24]aeb | sh[34]eb | sh[1234]le | sh[23]ele) + basic_machine=sh-unknown + ;; + sparc | sparcv8 | sparcv9 | sparcv9b | sparcv9v) + basic_machine=sparc-sun + ;; + cydra) + basic_machine=cydra-cydrome + ;; + orion) + basic_machine=orion-highlevel + ;; + orion105) + basic_machine=clipper-highlevel + ;; + mac | mpw | mac-mpw) + basic_machine=m68k-apple + ;; + pmac | pmac-mpw) + basic_machine=powerpc-apple + ;; + *-unknown) + # Make sure to match an already-canonicalized machine name. + ;; + *) + echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2 + exit 1 + ;; +esac + +# Here we canonicalize certain aliases for manufacturers. +case $basic_machine in + *-digital*) + basic_machine=`echo $basic_machine | sed 's/digital.*/dec/'` + ;; + *-commodore*) + basic_machine=`echo $basic_machine | sed 's/commodore.*/cbm/'` + ;; + *) + ;; +esac + +# Decode manufacturer-specific aliases for certain operating systems. + +if [ x"$os" != x"" ] +then +case $os in + # First match some system type aliases + # that might get confused with valid system types. + # -solaris* is a basic system type, with this one exception. + -auroraux) + os=-auroraux + ;; + -solaris1 | -solaris1.*) + os=`echo $os | sed -e 's|solaris1|sunos4|'` + ;; + -solaris) + os=-solaris2 + ;; + -svr4*) + os=-sysv4 + ;; + -unixware*) + os=-sysv4.2uw + ;; + -gnu/linux*) + os=`echo $os | sed -e 's|gnu/linux|linux-gnu|'` + ;; + # First accept the basic system types. + # The portable systems comes first. + # Each alternative MUST END IN A *, to match a version number. + # -sysv* is not here because it comes later, after sysvr4. + -gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \ + | -*vms* | -sco* | -esix* | -isc* | -aix* | -cnk* | -sunos | -sunos[34]*\ + | -hpux* | -unos* | -osf* | -luna* | -dgux* | -auroraux* | -solaris* \ + | -sym* | -kopensolaris* | -plan9* \ + | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \ + | -aos* | -aros* \ + | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \ + | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \ + | -hiux* | -386bsd* | -knetbsd* | -mirbsd* | -netbsd* \ + | -bitrig* | -openbsd* | -solidbsd* \ + | -ekkobsd* | -kfreebsd* | -freebsd* | -riscix* | -lynxos* \ + | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \ + | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \ + | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \ + | -chorusos* | -chorusrdb* | -cegcc* \ + | -cygwin* | -msys* | -pe* | -psos* | -moss* | -proelf* | -rtems* \ + | -mingw32* | -mingw64* | -linux-gnu* | -linux-android* \ + | -linux-newlib* | -linux-musl* | -linux-uclibc* \ + | -uxpv* | -beos* | -mpeix* | -udk* | -moxiebox* \ + | -interix* | -uwin* | -mks* | -rhapsody* | -darwin* | -opened* \ + | -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \ + | -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* \ + | -os2* | -vos* | -palmos* | -uclinux* | -nucleus* \ + | -morphos* | -superux* | -rtmk* | -rtmk-nova* | -windiss* \ + | -powermax* | -dnix* | -nx6 | -nx7 | -sei* | -dragonfly* \ + | -skyos* | -haiku* | -rdos* | -toppers* | -drops* | -es* | -tirtos*) + # Remember, each alternative MUST END IN *, to match a version number. + ;; + -qnx*) + case $basic_machine in + x86-* | i*86-*) + ;; + *) + os=-nto$os + ;; + esac + ;; + -nto-qnx*) + ;; + -nto*) + os=`echo $os | sed -e 's|nto|nto-qnx|'` + ;; + -sim | -es1800* | -hms* | -xray | -os68k* | -none* | -v88r* \ + | -windows* | -osx | -abug | -netware* | -os9* | -beos* | -haiku* \ + | -macos* | -mpw* | -magic* | -mmixware* | -mon960* | -lnews*) + ;; + -mac*) + os=`echo $os | sed -e 's|mac|macos|'` + ;; + -linux-dietlibc) + os=-linux-dietlibc + ;; + -linux*) + os=`echo $os | sed -e 's|linux|linux-gnu|'` + ;; + -sunos5*) + os=`echo $os | sed -e 's|sunos5|solaris2|'` + ;; + -sunos6*) + os=`echo $os | sed -e 's|sunos6|solaris3|'` + ;; + -opened*) + os=-openedition + ;; + -os400*) + os=-os400 + ;; + -wince*) + os=-wince + ;; + -osfrose*) + os=-osfrose + ;; + -osf*) + os=-osf + ;; + -utek*) + os=-bsd + ;; + -dynix*) + os=-bsd + ;; + -acis*) + os=-aos + ;; + -atheos*) + os=-atheos + ;; + -syllable*) + os=-syllable + ;; + -386bsd) + os=-bsd + ;; + -ctix* | -uts*) + os=-sysv + ;; + -nova*) + os=-rtmk-nova + ;; + -ns2 ) + os=-nextstep2 + ;; + -nsk*) + os=-nsk + ;; + # Preserve the version number of sinix5. + -sinix5.*) + os=`echo $os | sed -e 's|sinix|sysv|'` + ;; + -sinix*) + os=-sysv4 + ;; + -tpf*) + os=-tpf + ;; + -triton*) + os=-sysv3 + ;; + -oss*) + os=-sysv3 + ;; + -svr4) + os=-sysv4 + ;; + -svr3) + os=-sysv3 + ;; + -sysvr4) + os=-sysv4 + ;; + # This must come after -sysvr4. + -sysv*) + ;; + -ose*) + os=-ose + ;; + -es1800*) + os=-ose + ;; + -xenix) + os=-xenix + ;; + -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*) + os=-mint + ;; + -aros*) + os=-aros + ;; + -zvmoe) + os=-zvmoe + ;; + -dicos*) + os=-dicos + ;; + -nacl*) + ;; + -none) + ;; + *) + # Get rid of the `-' at the beginning of $os. + os=`echo $os | sed 's/[^-]*-//'` + echo Invalid configuration \`$1\': system \`$os\' not recognized 1>&2 + exit 1 + ;; +esac +else + +# Here we handle the default operating systems that come with various machines. +# The value should be what the vendor currently ships out the door with their +# machine or put another way, the most popular os provided with the machine. + +# Note that if you're going to try to match "-MANUFACTURER" here (say, +# "-sun"), then you have to tell the case statement up towards the top +# that MANUFACTURER isn't an operating system. Otherwise, code above +# will signal an error saying that MANUFACTURER isn't an operating +# system, and we'll never get to this point. + +case $basic_machine in + score-*) + os=-elf + ;; + spu-*) + os=-elf + ;; + *-acorn) + os=-riscix1.2 + ;; + arm*-rebel) + os=-linux + ;; + arm*-semi) + os=-aout + ;; + c4x-* | tic4x-*) + os=-coff + ;; + c8051-*) + os=-elf + ;; + hexagon-*) + os=-elf + ;; + tic54x-*) + os=-coff + ;; + tic55x-*) + os=-coff + ;; + tic6x-*) + os=-coff + ;; + # This must come before the *-dec entry. + pdp10-*) + os=-tops20 + ;; + pdp11-*) + os=-none + ;; + *-dec | vax-*) + os=-ultrix4.2 + ;; + m68*-apollo) + os=-domain + ;; + i386-sun) + os=-sunos4.0.2 + ;; + m68000-sun) + os=-sunos3 + ;; + m68*-cisco) + os=-aout + ;; + mep-*) + os=-elf + ;; + mips*-cisco) + os=-elf + ;; + mips*-*) + os=-elf + ;; + or32-*) + os=-coff + ;; + *-tti) # must be before sparc entry or we get the wrong os. + os=-sysv3 + ;; + sparc-* | *-sun) + os=-sunos4.1.1 + ;; + *-be) + os=-beos + ;; + *-haiku) + os=-haiku + ;; + *-ibm) + os=-aix + ;; + *-knuth) + os=-mmixware + ;; + *-wec) + os=-proelf + ;; + *-winbond) + os=-proelf + ;; + *-oki) + os=-proelf + ;; + *-hp) + os=-hpux + ;; + *-hitachi) + os=-hiux + ;; + i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent) + os=-sysv + ;; + *-cbm) + os=-amigaos + ;; + *-dg) + os=-dgux + ;; + *-dolphin) + os=-sysv3 + ;; + m68k-ccur) + os=-rtu + ;; + m88k-omron*) + os=-luna + ;; + *-next ) + os=-nextstep + ;; + *-sequent) + os=-ptx + ;; + *-crds) + os=-unos + ;; + *-ns) + os=-genix + ;; + i370-*) + os=-mvs + ;; + *-next) + os=-nextstep3 + ;; + *-gould) + os=-sysv + ;; + *-highlevel) + os=-bsd + ;; + *-encore) + os=-bsd + ;; + *-sgi) + os=-irix + ;; + *-siemens) + os=-sysv4 + ;; + *-masscomp) + os=-rtu + ;; + f30[01]-fujitsu | f700-fujitsu) + os=-uxpv + ;; + *-rom68k) + os=-coff + ;; + *-*bug) + os=-coff + ;; + *-apple) + os=-macos + ;; + *-atari*) + os=-mint + ;; + *) + os=-none + ;; +esac +fi + +# Here we handle the case where we know the os, and the CPU type, but not the +# manufacturer. We pick the logical manufacturer. +vendor=unknown +case $basic_machine in + *-unknown) + case $os in + -riscix*) + vendor=acorn + ;; + -sunos*) + vendor=sun + ;; + -cnk*|-aix*) + vendor=ibm + ;; + -beos*) + vendor=be + ;; + -hpux*) + vendor=hp + ;; + -mpeix*) + vendor=hp + ;; + -hiux*) + vendor=hitachi + ;; + -unos*) + vendor=crds + ;; + -dgux*) + vendor=dg + ;; + -luna*) + vendor=omron + ;; + -genix*) + vendor=ns + ;; + -mvs* | -opened*) + vendor=ibm + ;; + -os400*) + vendor=ibm + ;; + -ptx*) + vendor=sequent + ;; + -tpf*) + vendor=ibm + ;; + -vxsim* | -vxworks* | -windiss*) + vendor=wrs + ;; + -aux*) + vendor=apple + ;; + -hms*) + vendor=hitachi + ;; + -mpw* | -macos*) + vendor=apple + ;; + -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*) + vendor=atari + ;; + -vos*) + vendor=stratus + ;; + esac + basic_machine=`echo $basic_machine | sed "s/unknown/$vendor/"` + ;; +esac + +echo $basic_machine$os +exit + +# Local variables: +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "timestamp='" +# time-stamp-format: "%:y-%02m-%02d" +# time-stamp-end: "'" +# End: diff --git a/configure b/configure new file mode 100755 index 0000000..2d46036 --- /dev/null +++ b/configure @@ -0,0 +1,25483 @@ +#! /bin/sh +# Guess values for system-dependent variables and create Makefiles. +# Generated by GNU Autoconf 2.69 for nghttp2 1.0.0. +# +# Report bugs to . +# +# +# Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc. +# +# +# This configure script is free software; the Free Software Foundation +# gives unlimited permission to copy, distribute and modify it. +## -------------------- ## +## M4sh Initialization. ## +## -------------------- ## + +# Be more Bourne compatible +DUALCASE=1; export DUALCASE # for MKS sh +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : + emulate sh + NULLCMD=: + # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' + setopt NO_GLOB_SUBST +else + case `(set -o) 2>/dev/null` in #( + *posix*) : + set -o posix ;; #( + *) : + ;; +esac +fi + + +as_nl=' +' +export as_nl +# Printing a long string crashes Solaris 7 /usr/bin/printf. +as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' +as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo +as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo +# Prefer a ksh shell builtin over an external printf program on Solaris, +# but without wasting forks for bash or zsh. +if test -z "$BASH_VERSION$ZSH_VERSION" \ + && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then + as_echo='print -r --' + as_echo_n='print -rn --' +elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then + as_echo='printf %s\n' + as_echo_n='printf %s' +else + if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then + as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' + as_echo_n='/usr/ucb/echo -n' + else + as_echo_body='eval expr "X$1" : "X\\(.*\\)"' + as_echo_n_body='eval + arg=$1; + case $arg in #( + *"$as_nl"*) + expr "X$arg" : "X\\(.*\\)$as_nl"; + arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; + esac; + expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" + ' + export as_echo_n_body + as_echo_n='sh -c $as_echo_n_body as_echo' + fi + export as_echo_body + as_echo='sh -c $as_echo_body as_echo' +fi + +# The user is always right. +if test "${PATH_SEPARATOR+set}" != set; then + PATH_SEPARATOR=: + (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { + (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || + PATH_SEPARATOR=';' + } +fi + + +# IFS +# We need space, tab and new line, in precisely that order. Quoting is +# there to prevent editors from complaining about space-tab. +# (If _AS_PATH_WALK were called with IFS unset, it would disable word +# splitting by setting IFS to empty value.) +IFS=" "" $as_nl" + +# Find who we are. Look in the path if we contain no directory separator. +as_myself= +case $0 in #(( + *[\\/]* ) as_myself=$0 ;; + *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break + done +IFS=$as_save_IFS + + ;; +esac +# We did not find ourselves, most probably we were run as `sh COMMAND' +# in which case we are not to be found in the path. +if test "x$as_myself" = x; then + as_myself=$0 +fi +if test ! -f "$as_myself"; then + $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 + exit 1 +fi + +# Unset variables that we do not need and which cause bugs (e.g. in +# pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1" +# suppresses any "Segmentation fault" message there. '((' could +# trigger a bug in pdksh 5.2.14. +for as_var in BASH_ENV ENV MAIL MAILPATH +do eval test x\${$as_var+set} = xset \ + && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : +done +PS1='$ ' +PS2='> ' +PS4='+ ' + +# NLS nuisances. +LC_ALL=C +export LC_ALL +LANGUAGE=C +export LANGUAGE + +# CDPATH. +(unset CDPATH) >/dev/null 2>&1 && unset CDPATH + +# Use a proper internal environment variable to ensure we don't fall + # into an infinite loop, continuously re-executing ourselves. + if test x"${_as_can_reexec}" != xno && test "x$CONFIG_SHELL" != x; then + _as_can_reexec=no; export _as_can_reexec; + # We cannot yet assume a decent shell, so we have to provide a +# neutralization value for shells without unset; and this also +# works around shells that cannot unset nonexistent variables. +# Preserve -v and -x to the replacement shell. +BASH_ENV=/dev/null +ENV=/dev/null +(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV +case $- in # (((( + *v*x* | *x*v* ) as_opts=-vx ;; + *v* ) as_opts=-v ;; + *x* ) as_opts=-x ;; + * ) as_opts= ;; +esac +exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} +# Admittedly, this is quite paranoid, since all the known shells bail +# out after a failed `exec'. +$as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2 +as_fn_exit 255 + fi + # We don't want this to propagate to other subprocesses. + { _as_can_reexec=; unset _as_can_reexec;} +if test "x$CONFIG_SHELL" = x; then + as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then : + emulate sh + NULLCMD=: + # Pre-4.2 versions of Zsh do word splitting on \${1+\"\$@\"}, which + # is contrary to our usage. Disable this feature. + alias -g '\${1+\"\$@\"}'='\"\$@\"' + setopt NO_GLOB_SUBST +else + case \`(set -o) 2>/dev/null\` in #( + *posix*) : + set -o posix ;; #( + *) : + ;; +esac +fi +" + as_required="as_fn_return () { (exit \$1); } +as_fn_success () { as_fn_return 0; } +as_fn_failure () { as_fn_return 1; } +as_fn_ret_success () { return 0; } +as_fn_ret_failure () { return 1; } + +exitcode=0 +as_fn_success || { exitcode=1; echo as_fn_success failed.; } +as_fn_failure && { exitcode=1; echo as_fn_failure succeeded.; } +as_fn_ret_success || { exitcode=1; echo as_fn_ret_success failed.; } +as_fn_ret_failure && { exitcode=1; echo as_fn_ret_failure succeeded.; } +if ( set x; as_fn_ret_success y && test x = \"\$1\" ); then : + +else + exitcode=1; echo positional parameters were not saved. +fi +test x\$exitcode = x0 || exit 1 +test -x / || exit 1" + as_suggested=" as_lineno_1=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_1a=\$LINENO + as_lineno_2=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_2a=\$LINENO + eval 'test \"x\$as_lineno_1'\$as_run'\" != \"x\$as_lineno_2'\$as_run'\" && + test \"x\`expr \$as_lineno_1'\$as_run' + 1\`\" = \"x\$as_lineno_2'\$as_run'\"' || exit 1 +test \$(( 1 + 1 )) = 2 || exit 1 + + test -n \"\${ZSH_VERSION+set}\${BASH_VERSION+set}\" || ( + ECHO='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' + ECHO=\$ECHO\$ECHO\$ECHO\$ECHO\$ECHO + ECHO=\$ECHO\$ECHO\$ECHO\$ECHO\$ECHO\$ECHO + PATH=/empty FPATH=/empty; export PATH FPATH + test \"X\`printf %s \$ECHO\`\" = \"X\$ECHO\" \\ + || test \"X\`print -r -- \$ECHO\`\" = \"X\$ECHO\" ) || exit 1" + if (eval "$as_required") 2>/dev/null; then : + as_have_required=yes +else + as_have_required=no +fi + if test x$as_have_required = xyes && (eval "$as_suggested") 2>/dev/null; then : + +else + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +as_found=false +for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + as_found=: + case $as_dir in #( + /*) + for as_base in sh bash ksh sh5; do + # Try only shells that exist, to save several forks. + as_shell=$as_dir/$as_base + if { test -f "$as_shell" || test -f "$as_shell.exe"; } && + { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$as_shell"; } 2>/dev/null; then : + CONFIG_SHELL=$as_shell as_have_required=yes + if { $as_echo "$as_bourne_compatible""$as_suggested" | as_run=a "$as_shell"; } 2>/dev/null; then : + break 2 +fi +fi + done;; + esac + as_found=false +done +$as_found || { if { test -f "$SHELL" || test -f "$SHELL.exe"; } && + { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$SHELL"; } 2>/dev/null; then : + CONFIG_SHELL=$SHELL as_have_required=yes +fi; } +IFS=$as_save_IFS + + + if test "x$CONFIG_SHELL" != x; then : + export CONFIG_SHELL + # We cannot yet assume a decent shell, so we have to provide a +# neutralization value for shells without unset; and this also +# works around shells that cannot unset nonexistent variables. +# Preserve -v and -x to the replacement shell. +BASH_ENV=/dev/null +ENV=/dev/null +(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV +case $- in # (((( + *v*x* | *x*v* ) as_opts=-vx ;; + *v* ) as_opts=-v ;; + *x* ) as_opts=-x ;; + * ) as_opts= ;; +esac +exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} +# Admittedly, this is quite paranoid, since all the known shells bail +# out after a failed `exec'. +$as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2 +exit 255 +fi + + if test x$as_have_required = xno; then : + $as_echo "$0: This script requires a shell more modern than all" + $as_echo "$0: the shells that I found on your system." + if test x${ZSH_VERSION+set} = xset ; then + $as_echo "$0: In particular, zsh $ZSH_VERSION has bugs and should" + $as_echo "$0: be upgraded to zsh 4.3.4 or later." + else + $as_echo "$0: Please tell bug-autoconf@gnu.org and +$0: t-tujikawa@users.sourceforge.net about your system, +$0: including any error possibly output before this +$0: message. Then install a modern shell, or manually run +$0: the script under such a shell if you do have one." + fi + exit 1 +fi +fi +fi +SHELL=${CONFIG_SHELL-/bin/sh} +export SHELL +# Unset more variables known to interfere with behavior of common tools. +CLICOLOR_FORCE= GREP_OPTIONS= +unset CLICOLOR_FORCE GREP_OPTIONS + +## --------------------- ## +## M4sh Shell Functions. ## +## --------------------- ## +# as_fn_unset VAR +# --------------- +# Portably unset VAR. +as_fn_unset () +{ + { eval $1=; unset $1;} +} +as_unset=as_fn_unset + +# as_fn_set_status STATUS +# ----------------------- +# Set $? to STATUS, without forking. +as_fn_set_status () +{ + return $1 +} # as_fn_set_status + +# as_fn_exit STATUS +# ----------------- +# Exit the shell with STATUS, even in a "trap 0" or "set -e" context. +as_fn_exit () +{ + set +e + as_fn_set_status $1 + exit $1 +} # as_fn_exit + +# as_fn_mkdir_p +# ------------- +# Create "$as_dir" as a directory, including parents if necessary. +as_fn_mkdir_p () +{ + + case $as_dir in #( + -*) as_dir=./$as_dir;; + esac + test -d "$as_dir" || eval $as_mkdir_p || { + as_dirs= + while :; do + case $as_dir in #( + *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( + *) as_qdir=$as_dir;; + esac + as_dirs="'$as_qdir' $as_dirs" + as_dir=`$as_dirname -- "$as_dir" || +$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_dir" : 'X\(//\)[^/]' \| \ + X"$as_dir" : 'X\(//\)$' \| \ + X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$as_dir" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + test -d "$as_dir" && break + done + test -z "$as_dirs" || eval "mkdir $as_dirs" + } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" + + +} # as_fn_mkdir_p + +# as_fn_executable_p FILE +# ----------------------- +# Test if FILE is an executable regular file. +as_fn_executable_p () +{ + test -f "$1" && test -x "$1" +} # as_fn_executable_p +# as_fn_append VAR VALUE +# ---------------------- +# Append the text in VALUE to the end of the definition contained in VAR. Take +# advantage of any shell optimizations that allow amortized linear growth over +# repeated appends, instead of the typical quadratic growth present in naive +# implementations. +if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then : + eval 'as_fn_append () + { + eval $1+=\$2 + }' +else + as_fn_append () + { + eval $1=\$$1\$2 + } +fi # as_fn_append + +# as_fn_arith ARG... +# ------------------ +# Perform arithmetic evaluation on the ARGs, and store the result in the +# global $as_val. Take advantage of shells that can avoid forks. The arguments +# must be portable across $(()) and expr. +if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then : + eval 'as_fn_arith () + { + as_val=$(( $* )) + }' +else + as_fn_arith () + { + as_val=`expr "$@" || test $? -eq 1` + } +fi # as_fn_arith + + +# as_fn_error STATUS ERROR [LINENO LOG_FD] +# ---------------------------------------- +# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are +# provided, also output the error to LOG_FD, referencing LINENO. Then exit the +# script with STATUS, using 1 if that was 0. +as_fn_error () +{ + as_status=$1; test $as_status -eq 0 && as_status=1 + if test "$4"; then + as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 + fi + $as_echo "$as_me: error: $2" >&2 + as_fn_exit $as_status +} # as_fn_error + +if expr a : '\(a\)' >/dev/null 2>&1 && + test "X`expr 00001 : '.*\(...\)'`" = X001; then + as_expr=expr +else + as_expr=false +fi + +if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then + as_basename=basename +else + as_basename=false +fi + +if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then + as_dirname=dirname +else + as_dirname=false +fi + +as_me=`$as_basename -- "$0" || +$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X/"$0" | + sed '/^.*\/\([^/][^/]*\)\/*$/{ + s//\1/ + q + } + /^X\/\(\/\/\)$/{ + s//\1/ + q + } + /^X\/\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + +# Avoid depending upon Character Ranges. +as_cr_letters='abcdefghijklmnopqrstuvwxyz' +as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' +as_cr_Letters=$as_cr_letters$as_cr_LETTERS +as_cr_digits='0123456789' +as_cr_alnum=$as_cr_Letters$as_cr_digits + + + as_lineno_1=$LINENO as_lineno_1a=$LINENO + as_lineno_2=$LINENO as_lineno_2a=$LINENO + eval 'test "x$as_lineno_1'$as_run'" != "x$as_lineno_2'$as_run'" && + test "x`expr $as_lineno_1'$as_run' + 1`" = "x$as_lineno_2'$as_run'"' || { + # Blame Lee E. McMahon (1931-1989) for sed's syntax. :-) + sed -n ' + p + /[$]LINENO/= + ' <$as_myself | + sed ' + s/[$]LINENO.*/&-/ + t lineno + b + :lineno + N + :loop + s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/ + t loop + s/-\n.*// + ' >$as_me.lineno && + chmod +x "$as_me.lineno" || + { $as_echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2; as_fn_exit 1; } + + # If we had to re-execute with $CONFIG_SHELL, we're ensured to have + # already done that, so ensure we don't try to do so again and fall + # in an infinite loop. This has already happened in practice. + _as_can_reexec=no; export _as_can_reexec + # Don't try to exec as it changes $[0], causing all sort of problems + # (the dirname of $[0] is not the place where we might find the + # original and so on. Autoconf is especially sensitive to this). + . "./$as_me.lineno" + # Exit status is that of the last command. + exit +} + +ECHO_C= ECHO_N= ECHO_T= +case `echo -n x` in #((((( +-n*) + case `echo 'xy\c'` in + *c*) ECHO_T=' ';; # ECHO_T is single tab character. + xy) ECHO_C='\c';; + *) echo `echo ksh88 bug on AIX 6.1` > /dev/null + ECHO_T=' ';; + esac;; +*) + ECHO_N='-n';; +esac + +rm -f conf$$ conf$$.exe conf$$.file +if test -d conf$$.dir; then + rm -f conf$$.dir/conf$$.file +else + rm -f conf$$.dir + mkdir conf$$.dir 2>/dev/null +fi +if (echo >conf$$.file) 2>/dev/null; then + if ln -s conf$$.file conf$$ 2>/dev/null; then + as_ln_s='ln -s' + # ... but there are two gotchas: + # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. + # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. + # In both cases, we have to default to `cp -pR'. + ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || + as_ln_s='cp -pR' + elif ln conf$$.file conf$$ 2>/dev/null; then + as_ln_s=ln + else + as_ln_s='cp -pR' + fi +else + as_ln_s='cp -pR' +fi +rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file +rmdir conf$$.dir 2>/dev/null + +if mkdir -p . 2>/dev/null; then + as_mkdir_p='mkdir -p "$as_dir"' +else + test -d ./-p && rmdir ./-p + as_mkdir_p=false +fi + +as_test_x='test -x' +as_executable_p=as_fn_executable_p + +# Sed expression to map a string onto a valid CPP name. +as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" + +# Sed expression to map a string onto a valid variable name. +as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" + +SHELL=${CONFIG_SHELL-/bin/sh} + + +test -n "$DJDIR" || exec 7<&0 &1 + +# Name of the host. +# hostname on some systems (SVR3.2, old GNU/Linux) returns a bogus exit status, +# so uname gets run too. +ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q` + +# +# Initializations. +# +ac_default_prefix=/usr/local +ac_clean_files= +ac_config_libobj_dir=. +LIBOBJS= +cross_compiling=no +subdirs= +MFLAGS= +MAKEFLAGS= + +# Identity of this package. +PACKAGE_NAME='nghttp2' +PACKAGE_TARNAME='nghttp2' +PACKAGE_VERSION='1.0.0' +PACKAGE_STRING='nghttp2 1.0.0' +PACKAGE_BUGREPORT='t-tujikawa@users.sourceforge.net' +PACKAGE_URL='' + +# Factoring default headers for most tests. +ac_includes_default="\ +#include +#ifdef HAVE_SYS_TYPES_H +# include +#endif +#ifdef HAVE_SYS_STAT_H +# include +#endif +#ifdef STDC_HEADERS +# include +# include +#else +# ifdef HAVE_STDLIB_H +# include +# endif +#endif +#ifdef HAVE_STRING_H +# if !defined STDC_HEADERS && defined HAVE_MEMORY_H +# include +# endif +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif +#ifdef HAVE_INTTYPES_H +# include +#endif +#ifdef HAVE_STDINT_H +# include +#endif +#ifdef HAVE_UNISTD_H +# include +#endif" + +ac_subst_vars='am__EXEEXT_FALSE +am__EXEEXT_TRUE +LTLIBOBJS +APPLDFLAGS +TESTLDADD +WARNCFLAGS +ENABLE_TINY_NGHTTPD_FALSE +ENABLE_TINY_NGHTTPD_TRUE +LIBOBJS +ENABLE_FAILMALLOC_FALSE +ENABLE_FAILMALLOC_TRUE +HAVE_CYTHON_FALSE +HAVE_CYTHON_TRUE +ENABLE_PYTHON_BINDINGS_FALSE +ENABLE_PYTHON_BINDINGS_TRUE +ENABLE_EXAMPLES_FALSE +ENABLE_EXAMPLES_TRUE +ENABLE_ASIO_LIB_FALSE +ENABLE_ASIO_LIB_TRUE +ENABLE_HPACK_TOOLS_FALSE +ENABLE_HPACK_TOOLS_TRUE +ENABLE_APP_FALSE +ENABLE_APP_TRUE +BOOST_THREAD_LIB +BOOST_SYSTEM_LIB +BOOST_ASIO_LIB +BOOST_LDFLAGS +BOOST_CPPFLAGS +HAVE_SPDYLAY_FALSE +HAVE_SPDYLAY_TRUE +LIBSPDYLAY_LIBS +LIBSPDYLAY_CFLAGS +JEMALLOC_LIBS +HAVE_LIBXML2_FALSE +HAVE_LIBXML2_TRUE +XML_LIBS +XML_CPPFLAGS +XML2_CONFIG +JANSSON_LIBS +JANSSON_CFLAGS +LIBEVENT_OPENSSL_LIBS +LIBEVENT_OPENSSL_CFLAGS +OPENSSL_LIBS +OPENSSL_CFLAGS +LIBEV_CFLAGS +LIBEV_LIBS +HAVE_CUNIT_FALSE +HAVE_CUNIT_TRUE +CUNIT_LIBS +CUNIT_CFLAGS +ZLIB_LIBS +ZLIB_CFLAGS +HAVE_CXX11 +PYTHON_EXTRA_LDFLAGS +PYTHON_EXTRA_LIBS +PYTHON_SITE_PKG +PYTHON_LDFLAGS +PYTHON_CPPFLAGS +pkgpyexecdir +pyexecdir +pkgpythondir +pythondir +PYTHON_PLATFORM +PYTHON_EXEC_PREFIX +PYTHON_PREFIX +PYTHON_VERSION +PYTHON +PKG_CONFIG_LIBDIR +PKG_CONFIG_PATH +PKG_CONFIG +am__fastdepCXX_FALSE +am__fastdepCXX_TRUE +CXXDEPMODE +CXXCPP +ac_ct_CXX +CXXFLAGS +CXX +CYTHON +PACKAGE_VERSION_NUM +LT_AGE +LT_REVISION +LT_CURRENT +AM_BACKSLASH +AM_DEFAULT_VERBOSITY +AM_DEFAULT_V +AM_V +am__fastdepCC_FALSE +am__fastdepCC_TRUE +CCDEPMODE +am__nodep +AMDEPBACKSLASH +AMDEP_FALSE +AMDEP_TRUE +am__quote +am__include +DEPDIR +am__untar +am__tar +AMTAR +am__leading_dot +SET_MAKE +mkdir_p +MKDIR_P +INSTALL_STRIP_PROGRAM +install_sh +MAKEINFO +AUTOHEADER +AUTOMAKE +AUTOCONF +ACLOCAL +VERSION +PACKAGE +CYGPATH_W +am__isrc +INSTALL_DATA +INSTALL_SCRIPT +INSTALL_PROGRAM +target_os +target_vendor +target_cpu +target +OTOOL64 +OTOOL +LIPO +NMEDIT +DSYMUTIL +MANIFEST_TOOL +AWK +RANLIB +STRIP +ac_ct_AR +AR +DLLTOOL +OBJDUMP +LN_S +NM +ac_ct_DUMPBIN +DUMPBIN +LD +FGREP +SED +host_os +host_vendor +host_cpu +host +build_os +build_vendor +build_cpu +build +LIBTOOL +EGREP +GREP +CPP +OBJEXT +EXEEXT +ac_ct_CC +CPPFLAGS +LDFLAGS +CFLAGS +CC +target_alias +host_alias +build_alias +LIBS +ECHO_T +ECHO_N +ECHO_C +DEFS +mandir +localedir +libdir +psdir +pdfdir +dvidir +htmldir +infodir +docdir +oldincludedir +includedir +localstatedir +sharedstatedir +sysconfdir +datadir +datarootdir +libexecdir +sbindir +bindir +program_transform_name +prefix +exec_prefix +PACKAGE_URL +PACKAGE_BUGREPORT +PACKAGE_STRING +PACKAGE_VERSION +PACKAGE_TARNAME +PACKAGE_NAME +PATH_SEPARATOR +SHELL' +ac_subst_files='' +ac_user_opts=' +enable_option_checking +enable_shared +enable_static +with_pic +enable_fast_install +with_gnu_ld +with_sysroot +enable_libtool_lock +enable_dependency_tracking +enable_silent_rules +enable_werror +enable_debug +enable_threads +enable_app +enable_hpack_tools +enable_asio_lib +enable_examples +enable_python_bindings +enable_failmalloc +with_libxml2 +with_jemalloc +with_spdylay +with_cython +with_xml_prefix +with_xml_exec_prefix +enable_xmltest +with_boost +with_boost_libdir +with_boost_asio +with_boost_system +with_boost_thread +enable_assert +enable_largefile +' + ac_precious_vars='build_alias +host_alias +target_alias +CC +CFLAGS +LDFLAGS +LIBS +CPPFLAGS +CPP +CYTHON +CXX +CXXFLAGS +CCC +CXXCPP +PKG_CONFIG +PKG_CONFIG_PATH +PKG_CONFIG_LIBDIR +PYTHON +PYTHON_VERSION +ZLIB_CFLAGS +ZLIB_LIBS +CUNIT_CFLAGS +CUNIT_LIBS +OPENSSL_CFLAGS +OPENSSL_LIBS +LIBEVENT_OPENSSL_CFLAGS +LIBEVENT_OPENSSL_LIBS +JANSSON_CFLAGS +JANSSON_LIBS +LIBSPDYLAY_CFLAGS +LIBSPDYLAY_LIBS' + + +# Initialize some variables set by options. +ac_init_help= +ac_init_version=false +ac_unrecognized_opts= +ac_unrecognized_sep= +# The variables have the same names as the options, with +# dashes changed to underlines. +cache_file=/dev/null +exec_prefix=NONE +no_create= +no_recursion= +prefix=NONE +program_prefix=NONE +program_suffix=NONE +program_transform_name=s,x,x, +silent= +site= +srcdir= +verbose= +x_includes=NONE +x_libraries=NONE + +# Installation directory options. +# These are left unexpanded so users can "make install exec_prefix=/foo" +# and all the variables that are supposed to be based on exec_prefix +# by default will actually change. +# Use braces instead of parens because sh, perl, etc. also accept them. +# (The list follows the same order as the GNU Coding Standards.) +bindir='${exec_prefix}/bin' +sbindir='${exec_prefix}/sbin' +libexecdir='${exec_prefix}/libexec' +datarootdir='${prefix}/share' +datadir='${datarootdir}' +sysconfdir='${prefix}/etc' +sharedstatedir='${prefix}/com' +localstatedir='${prefix}/var' +includedir='${prefix}/include' +oldincludedir='/usr/include' +docdir='${datarootdir}/doc/${PACKAGE_TARNAME}' +infodir='${datarootdir}/info' +htmldir='${docdir}' +dvidir='${docdir}' +pdfdir='${docdir}' +psdir='${docdir}' +libdir='${exec_prefix}/lib' +localedir='${datarootdir}/locale' +mandir='${datarootdir}/man' + +ac_prev= +ac_dashdash= +for ac_option +do + # If the previous option needs an argument, assign it. + if test -n "$ac_prev"; then + eval $ac_prev=\$ac_option + ac_prev= + continue + fi + + case $ac_option in + *=?*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;; + *=) ac_optarg= ;; + *) ac_optarg=yes ;; + esac + + # Accept the important Cygnus configure options, so we can diagnose typos. + + case $ac_dashdash$ac_option in + --) + ac_dashdash=yes ;; + + -bindir | --bindir | --bindi | --bind | --bin | --bi) + ac_prev=bindir ;; + -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*) + bindir=$ac_optarg ;; + + -build | --build | --buil | --bui | --bu) + ac_prev=build_alias ;; + -build=* | --build=* | --buil=* | --bui=* | --bu=*) + build_alias=$ac_optarg ;; + + -cache-file | --cache-file | --cache-fil | --cache-fi \ + | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) + ac_prev=cache_file ;; + -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \ + | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*) + cache_file=$ac_optarg ;; + + --config-cache | -C) + cache_file=config.cache ;; + + -datadir | --datadir | --datadi | --datad) + ac_prev=datadir ;; + -datadir=* | --datadir=* | --datadi=* | --datad=*) + datadir=$ac_optarg ;; + + -datarootdir | --datarootdir | --datarootdi | --datarootd | --dataroot \ + | --dataroo | --dataro | --datar) + ac_prev=datarootdir ;; + -datarootdir=* | --datarootdir=* | --datarootdi=* | --datarootd=* \ + | --dataroot=* | --dataroo=* | --dataro=* | --datar=*) + datarootdir=$ac_optarg ;; + + -disable-* | --disable-*) + ac_useropt=`expr "x$ac_option" : 'x-*disable-\(.*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + as_fn_error $? "invalid feature name: $ac_useropt" + ac_useropt_orig=$ac_useropt + ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"enable_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--disable-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval enable_$ac_useropt=no ;; + + -docdir | --docdir | --docdi | --doc | --do) + ac_prev=docdir ;; + -docdir=* | --docdir=* | --docdi=* | --doc=* | --do=*) + docdir=$ac_optarg ;; + + -dvidir | --dvidir | --dvidi | --dvid | --dvi | --dv) + ac_prev=dvidir ;; + -dvidir=* | --dvidir=* | --dvidi=* | --dvid=* | --dvi=* | --dv=*) + dvidir=$ac_optarg ;; + + -enable-* | --enable-*) + ac_useropt=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + as_fn_error $? "invalid feature name: $ac_useropt" + ac_useropt_orig=$ac_useropt + ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"enable_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--enable-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval enable_$ac_useropt=\$ac_optarg ;; + + -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \ + | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \ + | --exec | --exe | --ex) + ac_prev=exec_prefix ;; + -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \ + | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \ + | --exec=* | --exe=* | --ex=*) + exec_prefix=$ac_optarg ;; + + -gas | --gas | --ga | --g) + # Obsolete; use --with-gas. + with_gas=yes ;; + + -help | --help | --hel | --he | -h) + ac_init_help=long ;; + -help=r* | --help=r* | --hel=r* | --he=r* | -hr*) + ac_init_help=recursive ;; + -help=s* | --help=s* | --hel=s* | --he=s* | -hs*) + ac_init_help=short ;; + + -host | --host | --hos | --ho) + ac_prev=host_alias ;; + -host=* | --host=* | --hos=* | --ho=*) + host_alias=$ac_optarg ;; + + -htmldir | --htmldir | --htmldi | --htmld | --html | --htm | --ht) + ac_prev=htmldir ;; + -htmldir=* | --htmldir=* | --htmldi=* | --htmld=* | --html=* | --htm=* \ + | --ht=*) + htmldir=$ac_optarg ;; + + -includedir | --includedir | --includedi | --included | --include \ + | --includ | --inclu | --incl | --inc) + ac_prev=includedir ;; + -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \ + | --includ=* | --inclu=* | --incl=* | --inc=*) + includedir=$ac_optarg ;; + + -infodir | --infodir | --infodi | --infod | --info | --inf) + ac_prev=infodir ;; + -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*) + infodir=$ac_optarg ;; + + -libdir | --libdir | --libdi | --libd) + ac_prev=libdir ;; + -libdir=* | --libdir=* | --libdi=* | --libd=*) + libdir=$ac_optarg ;; + + -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \ + | --libexe | --libex | --libe) + ac_prev=libexecdir ;; + -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \ + | --libexe=* | --libex=* | --libe=*) + libexecdir=$ac_optarg ;; + + -localedir | --localedir | --localedi | --localed | --locale) + ac_prev=localedir ;; + -localedir=* | --localedir=* | --localedi=* | --localed=* | --locale=*) + localedir=$ac_optarg ;; + + -localstatedir | --localstatedir | --localstatedi | --localstated \ + | --localstate | --localstat | --localsta | --localst | --locals) + ac_prev=localstatedir ;; + -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \ + | --localstate=* | --localstat=* | --localsta=* | --localst=* | --locals=*) + localstatedir=$ac_optarg ;; + + -mandir | --mandir | --mandi | --mand | --man | --ma | --m) + ac_prev=mandir ;; + -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*) + mandir=$ac_optarg ;; + + -nfp | --nfp | --nf) + # Obsolete; use --without-fp. + with_fp=no ;; + + -no-create | --no-create | --no-creat | --no-crea | --no-cre \ + | --no-cr | --no-c | -n) + no_create=yes ;; + + -no-recursion | --no-recursion | --no-recursio | --no-recursi \ + | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) + no_recursion=yes ;; + + -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \ + | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \ + | --oldin | --oldi | --old | --ol | --o) + ac_prev=oldincludedir ;; + -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \ + | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \ + | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*) + oldincludedir=$ac_optarg ;; + + -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) + ac_prev=prefix ;; + -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) + prefix=$ac_optarg ;; + + -program-prefix | --program-prefix | --program-prefi | --program-pref \ + | --program-pre | --program-pr | --program-p) + ac_prev=program_prefix ;; + -program-prefix=* | --program-prefix=* | --program-prefi=* \ + | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*) + program_prefix=$ac_optarg ;; + + -program-suffix | --program-suffix | --program-suffi | --program-suff \ + | --program-suf | --program-su | --program-s) + ac_prev=program_suffix ;; + -program-suffix=* | --program-suffix=* | --program-suffi=* \ + | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*) + program_suffix=$ac_optarg ;; + + -program-transform-name | --program-transform-name \ + | --program-transform-nam | --program-transform-na \ + | --program-transform-n | --program-transform- \ + | --program-transform | --program-transfor \ + | --program-transfo | --program-transf \ + | --program-trans | --program-tran \ + | --progr-tra | --program-tr | --program-t) + ac_prev=program_transform_name ;; + -program-transform-name=* | --program-transform-name=* \ + | --program-transform-nam=* | --program-transform-na=* \ + | --program-transform-n=* | --program-transform-=* \ + | --program-transform=* | --program-transfor=* \ + | --program-transfo=* | --program-transf=* \ + | --program-trans=* | --program-tran=* \ + | --progr-tra=* | --program-tr=* | --program-t=*) + program_transform_name=$ac_optarg ;; + + -pdfdir | --pdfdir | --pdfdi | --pdfd | --pdf | --pd) + ac_prev=pdfdir ;; + -pdfdir=* | --pdfdir=* | --pdfdi=* | --pdfd=* | --pdf=* | --pd=*) + pdfdir=$ac_optarg ;; + + -psdir | --psdir | --psdi | --psd | --ps) + ac_prev=psdir ;; + -psdir=* | --psdir=* | --psdi=* | --psd=* | --ps=*) + psdir=$ac_optarg ;; + + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil) + silent=yes ;; + + -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) + ac_prev=sbindir ;; + -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ + | --sbi=* | --sb=*) + sbindir=$ac_optarg ;; + + -sharedstatedir | --sharedstatedir | --sharedstatedi \ + | --sharedstated | --sharedstate | --sharedstat | --sharedsta \ + | --sharedst | --shareds | --shared | --share | --shar \ + | --sha | --sh) + ac_prev=sharedstatedir ;; + -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \ + | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \ + | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \ + | --sha=* | --sh=*) + sharedstatedir=$ac_optarg ;; + + -site | --site | --sit) + ac_prev=site ;; + -site=* | --site=* | --sit=*) + site=$ac_optarg ;; + + -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) + ac_prev=srcdir ;; + -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) + srcdir=$ac_optarg ;; + + -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \ + | --syscon | --sysco | --sysc | --sys | --sy) + ac_prev=sysconfdir ;; + -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \ + | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*) + sysconfdir=$ac_optarg ;; + + -target | --target | --targe | --targ | --tar | --ta | --t) + ac_prev=target_alias ;; + -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*) + target_alias=$ac_optarg ;; + + -v | -verbose | --verbose | --verbos | --verbo | --verb) + verbose=yes ;; + + -version | --version | --versio | --versi | --vers | -V) + ac_init_version=: ;; + + -with-* | --with-*) + ac_useropt=`expr "x$ac_option" : 'x-*with-\([^=]*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + as_fn_error $? "invalid package name: $ac_useropt" + ac_useropt_orig=$ac_useropt + ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"with_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--with-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval with_$ac_useropt=\$ac_optarg ;; + + -without-* | --without-*) + ac_useropt=`expr "x$ac_option" : 'x-*without-\(.*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + as_fn_error $? "invalid package name: $ac_useropt" + ac_useropt_orig=$ac_useropt + ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"with_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--without-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval with_$ac_useropt=no ;; + + --x) + # Obsolete; use --with-x. + with_x=yes ;; + + -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \ + | --x-incl | --x-inc | --x-in | --x-i) + ac_prev=x_includes ;; + -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \ + | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*) + x_includes=$ac_optarg ;; + + -x-libraries | --x-libraries | --x-librarie | --x-librari \ + | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l) + ac_prev=x_libraries ;; + -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \ + | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) + x_libraries=$ac_optarg ;; + + -*) as_fn_error $? "unrecognized option: \`$ac_option' +Try \`$0 --help' for more information" + ;; + + *=*) + ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='` + # Reject names that are not valid shell variable names. + case $ac_envvar in #( + '' | [0-9]* | *[!_$as_cr_alnum]* ) + as_fn_error $? "invalid variable name: \`$ac_envvar'" ;; + esac + eval $ac_envvar=\$ac_optarg + export $ac_envvar ;; + + *) + # FIXME: should be removed in autoconf 3.0. + $as_echo "$as_me: WARNING: you should use --build, --host, --target" >&2 + expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null && + $as_echo "$as_me: WARNING: invalid host type: $ac_option" >&2 + : "${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}" + ;; + + esac +done + +if test -n "$ac_prev"; then + ac_option=--`echo $ac_prev | sed 's/_/-/g'` + as_fn_error $? "missing argument to $ac_option" +fi + +if test -n "$ac_unrecognized_opts"; then + case $enable_option_checking in + no) ;; + fatal) as_fn_error $? "unrecognized options: $ac_unrecognized_opts" ;; + *) $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2 ;; + esac +fi + +# Check all directory arguments for consistency. +for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \ + datadir sysconfdir sharedstatedir localstatedir includedir \ + oldincludedir docdir infodir htmldir dvidir pdfdir psdir \ + libdir localedir mandir +do + eval ac_val=\$$ac_var + # Remove trailing slashes. + case $ac_val in + */ ) + ac_val=`expr "X$ac_val" : 'X\(.*[^/]\)' \| "X$ac_val" : 'X\(.*\)'` + eval $ac_var=\$ac_val;; + esac + # Be sure to have absolute directory names. + case $ac_val in + [\\/$]* | ?:[\\/]* ) continue;; + NONE | '' ) case $ac_var in *prefix ) continue;; esac;; + esac + as_fn_error $? "expected an absolute directory name for --$ac_var: $ac_val" +done + +# There might be people who depend on the old broken behavior: `$host' +# used to hold the argument of --host etc. +# FIXME: To remove some day. +build=$build_alias +host=$host_alias +target=$target_alias + +# FIXME: To remove some day. +if test "x$host_alias" != x; then + if test "x$build_alias" = x; then + cross_compiling=maybe + elif test "x$build_alias" != "x$host_alias"; then + cross_compiling=yes + fi +fi + +ac_tool_prefix= +test -n "$host_alias" && ac_tool_prefix=$host_alias- + +test "$silent" = yes && exec 6>/dev/null + + +ac_pwd=`pwd` && test -n "$ac_pwd" && +ac_ls_di=`ls -di .` && +ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` || + as_fn_error $? "working directory cannot be determined" +test "X$ac_ls_di" = "X$ac_pwd_ls_di" || + as_fn_error $? "pwd does not report name of working directory" + + +# Find the source files, if location was not specified. +if test -z "$srcdir"; then + ac_srcdir_defaulted=yes + # Try the directory containing this script, then the parent directory. + ac_confdir=`$as_dirname -- "$as_myself" || +$as_expr X"$as_myself" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_myself" : 'X\(//\)[^/]' \| \ + X"$as_myself" : 'X\(//\)$' \| \ + X"$as_myself" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$as_myself" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + srcdir=$ac_confdir + if test ! -r "$srcdir/$ac_unique_file"; then + srcdir=.. + fi +else + ac_srcdir_defaulted=no +fi +if test ! -r "$srcdir/$ac_unique_file"; then + test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .." + as_fn_error $? "cannot find sources ($ac_unique_file) in $srcdir" +fi +ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work" +ac_abs_confdir=`( + cd "$srcdir" && test -r "./$ac_unique_file" || as_fn_error $? "$ac_msg" + pwd)` +# When building in place, set srcdir=. +if test "$ac_abs_confdir" = "$ac_pwd"; then + srcdir=. +fi +# Remove unnecessary trailing slashes from srcdir. +# Double slashes in file names in object file debugging info +# mess up M-x gdb in Emacs. +case $srcdir in +*/) srcdir=`expr "X$srcdir" : 'X\(.*[^/]\)' \| "X$srcdir" : 'X\(.*\)'`;; +esac +for ac_var in $ac_precious_vars; do + eval ac_env_${ac_var}_set=\${${ac_var}+set} + eval ac_env_${ac_var}_value=\$${ac_var} + eval ac_cv_env_${ac_var}_set=\${${ac_var}+set} + eval ac_cv_env_${ac_var}_value=\$${ac_var} +done + +# +# Report the --help message. +# +if test "$ac_init_help" = "long"; then + # Omit some internal or obsolete options to make the list less imposing. + # This message is too long to be a string in the A/UX 3.1 sh. + cat <<_ACEOF +\`configure' configures nghttp2 1.0.0 to adapt to many kinds of systems. + +Usage: $0 [OPTION]... [VAR=VALUE]... + +To assign environment variables (e.g., CC, CFLAGS...), specify them as +VAR=VALUE. See below for descriptions of some of the useful variables. + +Defaults for the options are specified in brackets. + +Configuration: + -h, --help display this help and exit + --help=short display options specific to this package + --help=recursive display the short help of all the included packages + -V, --version display version information and exit + -q, --quiet, --silent do not print \`checking ...' messages + --cache-file=FILE cache test results in FILE [disabled] + -C, --config-cache alias for \`--cache-file=config.cache' + -n, --no-create do not create output files + --srcdir=DIR find the sources in DIR [configure dir or \`..'] + +Installation directories: + --prefix=PREFIX install architecture-independent files in PREFIX + [$ac_default_prefix] + --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX + [PREFIX] + +By default, \`make install' will install all the files in +\`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc. You can specify +an installation prefix other than \`$ac_default_prefix' using \`--prefix', +for instance \`--prefix=\$HOME'. + +For better control, use the options below. + +Fine tuning of the installation directories: + --bindir=DIR user executables [EPREFIX/bin] + --sbindir=DIR system admin executables [EPREFIX/sbin] + --libexecdir=DIR program executables [EPREFIX/libexec] + --sysconfdir=DIR read-only single-machine data [PREFIX/etc] + --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] + --localstatedir=DIR modifiable single-machine data [PREFIX/var] + --libdir=DIR object code libraries [EPREFIX/lib] + --includedir=DIR C header files [PREFIX/include] + --oldincludedir=DIR C header files for non-gcc [/usr/include] + --datarootdir=DIR read-only arch.-independent data root [PREFIX/share] + --datadir=DIR read-only architecture-independent data [DATAROOTDIR] + --infodir=DIR info documentation [DATAROOTDIR/info] + --localedir=DIR locale-dependent data [DATAROOTDIR/locale] + --mandir=DIR man documentation [DATAROOTDIR/man] + --docdir=DIR documentation root [DATAROOTDIR/doc/nghttp2] + --htmldir=DIR html documentation [DOCDIR] + --dvidir=DIR dvi documentation [DOCDIR] + --pdfdir=DIR pdf documentation [DOCDIR] + --psdir=DIR ps documentation [DOCDIR] +_ACEOF + + cat <<\_ACEOF + +Program names: + --program-prefix=PREFIX prepend PREFIX to installed program names + --program-suffix=SUFFIX append SUFFIX to installed program names + --program-transform-name=PROGRAM run sed PROGRAM on installed program names + +System types: + --build=BUILD configure for building on BUILD [guessed] + --host=HOST cross-compile to build programs to run on HOST [BUILD] + --target=TARGET configure for building compilers for TARGET [HOST] +_ACEOF +fi + +if test -n "$ac_init_help"; then + case $ac_init_help in + short | recursive ) echo "Configuration of nghttp2 1.0.0:";; + esac + cat <<\_ACEOF + +Optional Features: + --disable-option-checking ignore unrecognized --enable/--with options + --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no) + --enable-FEATURE[=ARG] include FEATURE [ARG=yes] + --enable-shared[=PKGS] build shared libraries [default=yes] + --enable-static[=PKGS] build static libraries [default=yes] + --enable-fast-install[=PKGS] + optimize for fast installation [default=yes] + --disable-libtool-lock avoid locking (might break parallel builds) + --enable-dependency-tracking + do not reject slow dependency extractors + --disable-dependency-tracking + speeds up one-time build + --enable-silent-rules less verbose build output (undo: "make V=1") + --disable-silent-rules verbose build output (undo: "make V=0") + --enable-werror Turn on compile time warnings + --enable-debug Turn on debug output + --disable-threads Turn off threading in apps + --enable-app Build applications (nghttp, nghttpd and nghttpx) + [default=check] + --enable-hpack-tools Build HPACK tools [default=check] + --enable-asio-lib Build C++ libnghttp2_asio library [default=no] + --enable-examples Build examples [default=check] + --enable-python-bindings + Build Python bindings [default=check] + --disable-failmalloc Do not build failmalloc test program + --disable-xmltest Do not try to compile and run a test LIBXML program + --disable-assert turn off assertions + --disable-largefile omit support for large files + +Optional Packages: + --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] + --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no) + --with-pic[=PKGS] try to use only PIC/non-PIC objects [default=use + both] + --with-gnu-ld assume the C compiler uses GNU ld [default=no] + --with-sysroot=DIR Search for dependent libraries within DIR + (or the compiler's sysroot if not specified). + --with-libxml2 Use libxml2 [default=check] + --with-jemalloc Use jemalloc [default=check] + --with-spdylay Use spdylay [default=check] + --with-cython=PATH Use cython in given PATH + --with-xml-prefix=PFX Prefix where libxml is installed (optional) + --with-xml-exec-prefix=PFX Exec prefix where libxml is installed (optional) + --with-boost[=ARG] use Boost library from a standard location + (ARG=yes), from the specified location (ARG=), + or disable it (ARG=no) [ARG=yes] + --with-boost-libdir=LIB_DIR + Force given directory for boost libraries. Note that + this will override library path detection, so use + this parameter only if default library detection + fails and you know exactly where your boost + libraries are located. + --with-boost-asio[=special-lib] + use the ASIO library from boost - it is possible to + specify a certain library for the linker e.g. + --with-boost-asio=boost_system-gcc41-mt-1_34 + --with-boost-system[=special-lib] + use the System library from boost - it is possible + to specify a certain library for the linker e.g. + --with-boost-system=boost_system-gcc-mt + --with-boost-thread[=special-lib] + use the Thread library from boost - it is possible + to specify a certain library for the linker e.g. + --with-boost-thread=boost_thread-gcc-mt + +Some influential environment variables: + CC C compiler command + CFLAGS C compiler flags + LDFLAGS linker flags, e.g. -L if you have libraries in a + nonstandard directory + LIBS libraries to pass to the linker, e.g. -l + CPPFLAGS (Objective) C/C++ preprocessor flags, e.g. -I if + you have headers in a nonstandard directory + CPP C preprocessor + CYTHON the Cython executable + CXX C++ compiler command + CXXFLAGS C++ compiler flags + CXXCPP C++ preprocessor + PKG_CONFIG path to pkg-config utility + PKG_CONFIG_PATH + directories to add to pkg-config's search path + PKG_CONFIG_LIBDIR + path overriding pkg-config's built-in search path + PYTHON the Python interpreter + PYTHON_VERSION + The installed Python version to use, for example '2.3'. This + string will be appended to the Python interpreter canonical + name. + ZLIB_CFLAGS C compiler flags for ZLIB, overriding pkg-config + ZLIB_LIBS linker flags for ZLIB, overriding pkg-config + CUNIT_CFLAGS + C compiler flags for CUNIT, overriding pkg-config + CUNIT_LIBS linker flags for CUNIT, overriding pkg-config + OPENSSL_CFLAGS + C compiler flags for OPENSSL, overriding pkg-config + OPENSSL_LIBS + linker flags for OPENSSL, overriding pkg-config + LIBEVENT_OPENSSL_CFLAGS + C compiler flags for LIBEVENT_OPENSSL, overriding pkg-config + LIBEVENT_OPENSSL_LIBS + linker flags for LIBEVENT_OPENSSL, overriding pkg-config + JANSSON_CFLAGS + C compiler flags for JANSSON, overriding pkg-config + JANSSON_LIBS + linker flags for JANSSON, overriding pkg-config + LIBSPDYLAY_CFLAGS + C compiler flags for LIBSPDYLAY, overriding pkg-config + LIBSPDYLAY_LIBS + linker flags for LIBSPDYLAY, overriding pkg-config + +Use these variables to override the choices made by `configure' or to help +it to find libraries and programs with nonstandard names/locations. + +Report bugs to . +_ACEOF +ac_status=$? +fi + +if test "$ac_init_help" = "recursive"; then + # If there are subdirs, report their specific --help. + for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue + test -d "$ac_dir" || + { cd "$srcdir" && ac_pwd=`pwd` && srcdir=. && test -d "$ac_dir"; } || + continue + ac_builddir=. + +case "$ac_dir" in +.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; +*) + ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` + # A ".." for each directory in $ac_dir_suffix. + ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` + case $ac_top_builddir_sub in + "") ac_top_builddir_sub=. ac_top_build_prefix= ;; + *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; + esac ;; +esac +ac_abs_top_builddir=$ac_pwd +ac_abs_builddir=$ac_pwd$ac_dir_suffix +# for backward compatibility: +ac_top_builddir=$ac_top_build_prefix + +case $srcdir in + .) # We are building in place. + ac_srcdir=. + ac_top_srcdir=$ac_top_builddir_sub + ac_abs_top_srcdir=$ac_pwd ;; + [\\/]* | ?:[\\/]* ) # Absolute name. + ac_srcdir=$srcdir$ac_dir_suffix; + ac_top_srcdir=$srcdir + ac_abs_top_srcdir=$srcdir ;; + *) # Relative name. + ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix + ac_top_srcdir=$ac_top_build_prefix$srcdir + ac_abs_top_srcdir=$ac_pwd/$srcdir ;; +esac +ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix + + cd "$ac_dir" || { ac_status=$?; continue; } + # Check for guested configure. + if test -f "$ac_srcdir/configure.gnu"; then + echo && + $SHELL "$ac_srcdir/configure.gnu" --help=recursive + elif test -f "$ac_srcdir/configure"; then + echo && + $SHELL "$ac_srcdir/configure" --help=recursive + else + $as_echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2 + fi || ac_status=$? + cd "$ac_pwd" || { ac_status=$?; break; } + done +fi + +test -n "$ac_init_help" && exit $ac_status +if $ac_init_version; then + cat <<\_ACEOF +nghttp2 configure 1.0.0 +generated by GNU Autoconf 2.69 + +Copyright (C) 2012 Free Software Foundation, Inc. +This configure script is free software; the Free Software Foundation +gives unlimited permission to copy, distribute and modify it. +_ACEOF + exit +fi + +## ------------------------ ## +## Autoconf initialization. ## +## ------------------------ ## + +# ac_fn_c_try_compile LINENO +# -------------------------- +# Try to compile conftest.$ac_ext, and return whether this succeeded. +ac_fn_c_try_compile () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + rm -f conftest.$ac_objext + if { { ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_compile") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + grep -v '^ *+' conftest.err >conftest.er1 + cat conftest.er1 >&5 + mv -f conftest.er1 conftest.err + fi + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then : + ac_retval=0 +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=1 +fi + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval + +} # ac_fn_c_try_compile + +# ac_fn_c_try_cpp LINENO +# ---------------------- +# Try to preprocess conftest.$ac_ext, and return whether this succeeded. +ac_fn_c_try_cpp () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + if { { ac_try="$ac_cpp conftest.$ac_ext" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_cpp conftest.$ac_ext") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + grep -v '^ *+' conftest.err >conftest.er1 + cat conftest.er1 >&5 + mv -f conftest.er1 conftest.err + fi + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } > conftest.i && { + test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || + test ! -s conftest.err + }; then : + ac_retval=0 +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=1 +fi + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval + +} # ac_fn_c_try_cpp + +# ac_fn_c_check_header_mongrel LINENO HEADER VAR INCLUDES +# ------------------------------------------------------- +# Tests whether HEADER exists, giving a warning if it cannot be compiled using +# the include files in INCLUDES and setting the cache variable VAR +# accordingly. +ac_fn_c_check_header_mongrel () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + if eval \${$3+:} false; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 +$as_echo_n "checking for $2... " >&6; } +if eval \${$3+:} false; then : + $as_echo_n "(cached) " >&6 +fi +eval ac_res=\$$3 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } +else + # Is the header compilable? +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 usability" >&5 +$as_echo_n "checking $2 usability... " >&6; } +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +#include <$2> +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_header_compiler=yes +else + ac_header_compiler=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_compiler" >&5 +$as_echo "$ac_header_compiler" >&6; } + +# Is the header present? +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 presence" >&5 +$as_echo_n "checking $2 presence... " >&6; } +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include <$2> +_ACEOF +if ac_fn_c_try_cpp "$LINENO"; then : + ac_header_preproc=yes +else + ac_header_preproc=no +fi +rm -f conftest.err conftest.i conftest.$ac_ext +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_preproc" >&5 +$as_echo "$ac_header_preproc" >&6; } + +# So? What about this header? +case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in #(( + yes:no: ) + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&5 +$as_echo "$as_me: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5 +$as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;} + ;; + no:yes:* ) + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: present but cannot be compiled" >&5 +$as_echo "$as_me: WARNING: $2: present but cannot be compiled" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: check for missing prerequisite headers?" >&5 +$as_echo "$as_me: WARNING: $2: check for missing prerequisite headers?" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: see the Autoconf documentation" >&5 +$as_echo "$as_me: WARNING: $2: see the Autoconf documentation" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&5 +$as_echo "$as_me: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5 +$as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;} +( $as_echo "## ----------------------------------------------- ## +## Report this to t-tujikawa@users.sourceforge.net ## +## ----------------------------------------------- ##" + ) | sed "s/^/$as_me: WARNING: /" >&2 + ;; +esac + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 +$as_echo_n "checking for $2... " >&6; } +if eval \${$3+:} false; then : + $as_echo_n "(cached) " >&6 +else + eval "$3=\$ac_header_compiler" +fi +eval ac_res=\$$3 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } +fi + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + +} # ac_fn_c_check_header_mongrel + +# ac_fn_c_try_run LINENO +# ---------------------- +# Try to link conftest.$ac_ext, and return whether this succeeded. Assumes +# that executables *can* be run. +ac_fn_c_try_run () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + if { { ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && { ac_try='./conftest$ac_exeext' + { { case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_try") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; }; then : + ac_retval=0 +else + $as_echo "$as_me: program exited with status $ac_status" >&5 + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=$ac_status +fi + rm -rf conftest.dSYM conftest_ipa8_conftest.oo + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval + +} # ac_fn_c_try_run + +# ac_fn_c_check_header_compile LINENO HEADER VAR INCLUDES +# ------------------------------------------------------- +# Tests whether HEADER exists and can be compiled using the include files in +# INCLUDES, setting the cache variable VAR accordingly. +ac_fn_c_check_header_compile () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 +$as_echo_n "checking for $2... " >&6; } +if eval \${$3+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +#include <$2> +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + eval "$3=yes" +else + eval "$3=no" +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +eval ac_res=\$$3 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + +} # ac_fn_c_check_header_compile + +# ac_fn_c_try_link LINENO +# ----------------------- +# Try to link conftest.$ac_ext, and return whether this succeeded. +ac_fn_c_try_link () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + rm -f conftest.$ac_objext conftest$ac_exeext + if { { ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + grep -v '^ *+' conftest.err >conftest.er1 + cat conftest.er1 >&5 + mv -f conftest.er1 conftest.err + fi + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest$ac_exeext && { + test "$cross_compiling" = yes || + test -x conftest$ac_exeext + }; then : + ac_retval=0 +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=1 +fi + # Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information + # created by the PGI compiler (conftest_ipa8_conftest.oo), as it would + # interfere with the next link command; also delete a directory that is + # left behind by Apple's compiler. We do this before executing the actions. + rm -rf conftest.dSYM conftest_ipa8_conftest.oo + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval + +} # ac_fn_c_try_link + +# ac_fn_c_check_func LINENO FUNC VAR +# ---------------------------------- +# Tests whether FUNC exists, setting the cache variable VAR accordingly +ac_fn_c_check_func () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 +$as_echo_n "checking for $2... " >&6; } +if eval \${$3+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +/* Define $2 to an innocuous variant, in case declares $2. + For example, HP-UX 11i declares gettimeofday. */ +#define $2 innocuous_$2 + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $2 (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#endif + +#undef $2 + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char $2 (); +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined __stub_$2 || defined __stub___$2 +choke me +#endif + +int +main () +{ +return $2 (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + eval "$3=yes" +else + eval "$3=no" +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +eval ac_res=\$$3 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + +} # ac_fn_c_check_func + +# ac_fn_cxx_try_compile LINENO +# ---------------------------- +# Try to compile conftest.$ac_ext, and return whether this succeeded. +ac_fn_cxx_try_compile () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + rm -f conftest.$ac_objext + if { { ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_compile") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + grep -v '^ *+' conftest.err >conftest.er1 + cat conftest.er1 >&5 + mv -f conftest.er1 conftest.err + fi + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && { + test -z "$ac_cxx_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then : + ac_retval=0 +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=1 +fi + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval + +} # ac_fn_cxx_try_compile + +# ac_fn_cxx_try_cpp LINENO +# ------------------------ +# Try to preprocess conftest.$ac_ext, and return whether this succeeded. +ac_fn_cxx_try_cpp () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + if { { ac_try="$ac_cpp conftest.$ac_ext" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_cpp conftest.$ac_ext") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + grep -v '^ *+' conftest.err >conftest.er1 + cat conftest.er1 >&5 + mv -f conftest.er1 conftest.err + fi + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } > conftest.i && { + test -z "$ac_cxx_preproc_warn_flag$ac_cxx_werror_flag" || + test ! -s conftest.err + }; then : + ac_retval=0 +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=1 +fi + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval + +} # ac_fn_cxx_try_cpp + +# ac_fn_cxx_try_link LINENO +# ------------------------- +# Try to link conftest.$ac_ext, and return whether this succeeded. +ac_fn_cxx_try_link () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + rm -f conftest.$ac_objext conftest$ac_exeext + if { { ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + grep -v '^ *+' conftest.err >conftest.er1 + cat conftest.er1 >&5 + mv -f conftest.er1 conftest.err + fi + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && { + test -z "$ac_cxx_werror_flag" || + test ! -s conftest.err + } && test -s conftest$ac_exeext && { + test "$cross_compiling" = yes || + test -x conftest$ac_exeext + }; then : + ac_retval=0 +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=1 +fi + # Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information + # created by the PGI compiler (conftest_ipa8_conftest.oo), as it would + # interfere with the next link command; also delete a directory that is + # left behind by Apple's compiler. We do this before executing the actions. + rm -rf conftest.dSYM conftest_ipa8_conftest.oo + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval + +} # ac_fn_cxx_try_link + +# ac_fn_c_check_type LINENO TYPE VAR INCLUDES +# ------------------------------------------- +# Tests whether TYPE exists after having included INCLUDES, setting cache +# variable VAR accordingly. +ac_fn_c_check_type () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 +$as_echo_n "checking for $2... " >&6; } +if eval \${$3+:} false; then : + $as_echo_n "(cached) " >&6 +else + eval "$3=no" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +int +main () +{ +if (sizeof ($2)) + return 0; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +int +main () +{ +if (sizeof (($2))) + return 0; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + +else + eval "$3=yes" +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +eval ac_res=\$$3 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + +} # ac_fn_c_check_type + +# ac_fn_c_find_uintX_t LINENO BITS VAR +# ------------------------------------ +# Finds an unsigned integer type with width BITS, setting cache variable VAR +# accordingly. +ac_fn_c_find_uintX_t () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for uint$2_t" >&5 +$as_echo_n "checking for uint$2_t... " >&6; } +if eval \${$3+:} false; then : + $as_echo_n "(cached) " >&6 +else + eval "$3=no" + # Order is important - never check a type that is potentially smaller + # than half of the expected target width. + for ac_type in uint$2_t 'unsigned int' 'unsigned long int' \ + 'unsigned long long int' 'unsigned short int' 'unsigned char'; do + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +static int test_array [1 - 2 * !((($ac_type) -1 >> ($2 / 2 - 1)) >> ($2 / 2 - 1) == 3)]; +test_array [0] = 0; +return test_array [0]; + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + case $ac_type in #( + uint$2_t) : + eval "$3=yes" ;; #( + *) : + eval "$3=\$ac_type" ;; +esac +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + if eval test \"x\$"$3"\" = x"no"; then : + +else + break +fi + done +fi +eval ac_res=\$$3 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + +} # ac_fn_c_find_uintX_t + +# ac_fn_c_find_intX_t LINENO BITS VAR +# ----------------------------------- +# Finds a signed integer type with width BITS, setting cache variable VAR +# accordingly. +ac_fn_c_find_intX_t () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for int$2_t" >&5 +$as_echo_n "checking for int$2_t... " >&6; } +if eval \${$3+:} false; then : + $as_echo_n "(cached) " >&6 +else + eval "$3=no" + # Order is important - never check a type that is potentially smaller + # than half of the expected target width. + for ac_type in int$2_t 'int' 'long int' \ + 'long long int' 'short int' 'signed char'; do + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$ac_includes_default + enum { N = $2 / 2 - 1 }; +int +main () +{ +static int test_array [1 - 2 * !(0 < ($ac_type) ((((($ac_type) 1 << N) << N) - 1) * 2 + 1))]; +test_array [0] = 0; +return test_array [0]; + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$ac_includes_default + enum { N = $2 / 2 - 1 }; +int +main () +{ +static int test_array [1 - 2 * !(($ac_type) ((((($ac_type) 1 << N) << N) - 1) * 2 + 1) + < ($ac_type) ((((($ac_type) 1 << N) << N) - 1) * 2 + 2))]; +test_array [0] = 0; +return test_array [0]; + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + +else + case $ac_type in #( + int$2_t) : + eval "$3=yes" ;; #( + *) : + eval "$3=\$ac_type" ;; +esac +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + if eval test \"x\$"$3"\" = x"no"; then : + +else + break +fi + done +fi +eval ac_res=\$$3 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + +} # ac_fn_c_find_intX_t + +# ac_fn_c_check_member LINENO AGGR MEMBER VAR INCLUDES +# ---------------------------------------------------- +# Tries to find if the field MEMBER exists in type AGGR, after including +# INCLUDES, setting cache variable VAR accordingly. +ac_fn_c_check_member () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2.$3" >&5 +$as_echo_n "checking for $2.$3... " >&6; } +if eval \${$4+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$5 +int +main () +{ +static $2 ac_aggr; +if (ac_aggr.$3) +return 0; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + eval "$4=yes" +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$5 +int +main () +{ +static $2 ac_aggr; +if (sizeof ac_aggr.$3) +return 0; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + eval "$4=yes" +else + eval "$4=no" +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +eval ac_res=\$$4 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + +} # ac_fn_c_check_member + +# ac_fn_c_compute_int LINENO EXPR VAR INCLUDES +# -------------------------------------------- +# Tries to find the compile-time value of EXPR in a program that includes +# INCLUDES, setting VAR accordingly. Returns whether the value could be +# computed +ac_fn_c_compute_int () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + if test "$cross_compiling" = yes; then + # Depending upon the size, compute the lo and hi bounds. +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +int +main () +{ +static int test_array [1 - 2 * !(($2) >= 0)]; +test_array [0] = 0; +return test_array [0]; + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_lo=0 ac_mid=0 + while :; do + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +int +main () +{ +static int test_array [1 - 2 * !(($2) <= $ac_mid)]; +test_array [0] = 0; +return test_array [0]; + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_hi=$ac_mid; break +else + as_fn_arith $ac_mid + 1 && ac_lo=$as_val + if test $ac_lo -le $ac_mid; then + ac_lo= ac_hi= + break + fi + as_fn_arith 2 '*' $ac_mid + 1 && ac_mid=$as_val +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + done +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +int +main () +{ +static int test_array [1 - 2 * !(($2) < 0)]; +test_array [0] = 0; +return test_array [0]; + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_hi=-1 ac_mid=-1 + while :; do + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +int +main () +{ +static int test_array [1 - 2 * !(($2) >= $ac_mid)]; +test_array [0] = 0; +return test_array [0]; + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_lo=$ac_mid; break +else + as_fn_arith '(' $ac_mid ')' - 1 && ac_hi=$as_val + if test $ac_mid -le $ac_hi; then + ac_lo= ac_hi= + break + fi + as_fn_arith 2 '*' $ac_mid && ac_mid=$as_val +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + done +else + ac_lo= ac_hi= +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +# Binary search between lo and hi bounds. +while test "x$ac_lo" != "x$ac_hi"; do + as_fn_arith '(' $ac_hi - $ac_lo ')' / 2 + $ac_lo && ac_mid=$as_val + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +int +main () +{ +static int test_array [1 - 2 * !(($2) <= $ac_mid)]; +test_array [0] = 0; +return test_array [0]; + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_hi=$ac_mid +else + as_fn_arith '(' $ac_mid ')' + 1 && ac_lo=$as_val +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +done +case $ac_lo in #(( +?*) eval "$3=\$ac_lo"; ac_retval=0 ;; +'') ac_retval=1 ;; +esac + else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +static long int longval () { return $2; } +static unsigned long int ulongval () { return $2; } +#include +#include +int +main () +{ + + FILE *f = fopen ("conftest.val", "w"); + if (! f) + return 1; + if (($2) < 0) + { + long int i = longval (); + if (i != ($2)) + return 1; + fprintf (f, "%ld", i); + } + else + { + unsigned long int i = ulongval (); + if (i != ($2)) + return 1; + fprintf (f, "%lu", i); + } + /* Do not output a trailing newline, as this causes \r\n confusion + on some platforms. */ + return ferror (f) || fclose (f) != 0; + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + echo >>conftest.val; read $3 &5 +$as_echo_n "checking whether $as_decl_name is declared... " >&6; } +if eval \${$3+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +int +main () +{ +#ifndef $as_decl_name +#ifdef __cplusplus + (void) $as_decl_use; +#else + (void) $as_decl_name; +#endif +#endif + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + eval "$3=yes" +else + eval "$3=no" +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +eval ac_res=\$$3 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + +} # ac_fn_c_check_decl +cat >config.log <<_ACEOF +This file contains any messages produced by compilers while +running configure, to aid debugging if configure makes a mistake. + +It was created by nghttp2 $as_me 1.0.0, which was +generated by GNU Autoconf 2.69. Invocation command line was + + $ $0 $@ + +_ACEOF +exec 5>>config.log +{ +cat <<_ASUNAME +## --------- ## +## Platform. ## +## --------- ## + +hostname = `(hostname || uname -n) 2>/dev/null | sed 1q` +uname -m = `(uname -m) 2>/dev/null || echo unknown` +uname -r = `(uname -r) 2>/dev/null || echo unknown` +uname -s = `(uname -s) 2>/dev/null || echo unknown` +uname -v = `(uname -v) 2>/dev/null || echo unknown` + +/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown` +/bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown` + +/bin/arch = `(/bin/arch) 2>/dev/null || echo unknown` +/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown` +/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown` +/usr/bin/hostinfo = `(/usr/bin/hostinfo) 2>/dev/null || echo unknown` +/bin/machine = `(/bin/machine) 2>/dev/null || echo unknown` +/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown` +/bin/universe = `(/bin/universe) 2>/dev/null || echo unknown` + +_ASUNAME + +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + $as_echo "PATH: $as_dir" + done +IFS=$as_save_IFS + +} >&5 + +cat >&5 <<_ACEOF + + +## ----------- ## +## Core tests. ## +## ----------- ## + +_ACEOF + + +# Keep a trace of the command line. +# Strip out --no-create and --no-recursion so they do not pile up. +# Strip out --silent because we don't want to record it for future runs. +# Also quote any args containing shell meta-characters. +# Make two passes to allow for proper duplicate-argument suppression. +ac_configure_args= +ac_configure_args0= +ac_configure_args1= +ac_must_keep_next=false +for ac_pass in 1 2 +do + for ac_arg + do + case $ac_arg in + -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;; + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil) + continue ;; + *\'*) + ac_arg=`$as_echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; + esac + case $ac_pass in + 1) as_fn_append ac_configure_args0 " '$ac_arg'" ;; + 2) + as_fn_append ac_configure_args1 " '$ac_arg'" + if test $ac_must_keep_next = true; then + ac_must_keep_next=false # Got value, back to normal. + else + case $ac_arg in + *=* | --config-cache | -C | -disable-* | --disable-* \ + | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \ + | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \ + | -with-* | --with-* | -without-* | --without-* | --x) + case "$ac_configure_args0 " in + "$ac_configure_args1"*" '$ac_arg' "* ) continue ;; + esac + ;; + -* ) ac_must_keep_next=true ;; + esac + fi + as_fn_append ac_configure_args " '$ac_arg'" + ;; + esac + done +done +{ ac_configure_args0=; unset ac_configure_args0;} +{ ac_configure_args1=; unset ac_configure_args1;} + +# When interrupted or exit'd, cleanup temporary files, and complete +# config.log. We remove comments because anyway the quotes in there +# would cause problems or look ugly. +# WARNING: Use '\'' to represent an apostrophe within the trap. +# WARNING: Do not start the trap code with a newline, due to a FreeBSD 4.0 bug. +trap 'exit_status=$? + # Save into config.log some information that might help in debugging. + { + echo + + $as_echo "## ---------------- ## +## Cache variables. ## +## ---------------- ##" + echo + # The following way of writing the cache mishandles newlines in values, +( + for ac_var in `(set) 2>&1 | sed -n '\''s/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'\''`; do + eval ac_val=\$$ac_var + case $ac_val in #( + *${as_nl}*) + case $ac_var in #( + *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 +$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; + esac + case $ac_var in #( + _ | IFS | as_nl) ;; #( + BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( + *) { eval $ac_var=; unset $ac_var;} ;; + esac ;; + esac + done + (set) 2>&1 | + case $as_nl`(ac_space='\'' '\''; set) 2>&1` in #( + *${as_nl}ac_space=\ *) + sed -n \ + "s/'\''/'\''\\\\'\'''\''/g; + s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\''\\2'\''/p" + ;; #( + *) + sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" + ;; + esac | + sort +) + echo + + $as_echo "## ----------------- ## +## Output variables. ## +## ----------------- ##" + echo + for ac_var in $ac_subst_vars + do + eval ac_val=\$$ac_var + case $ac_val in + *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; + esac + $as_echo "$ac_var='\''$ac_val'\''" + done | sort + echo + + if test -n "$ac_subst_files"; then + $as_echo "## ------------------- ## +## File substitutions. ## +## ------------------- ##" + echo + for ac_var in $ac_subst_files + do + eval ac_val=\$$ac_var + case $ac_val in + *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; + esac + $as_echo "$ac_var='\''$ac_val'\''" + done | sort + echo + fi + + if test -s confdefs.h; then + $as_echo "## ----------- ## +## confdefs.h. ## +## ----------- ##" + echo + cat confdefs.h + echo + fi + test "$ac_signal" != 0 && + $as_echo "$as_me: caught signal $ac_signal" + $as_echo "$as_me: exit $exit_status" + } >&5 + rm -f core *.core core.conftest.* && + rm -f -r conftest* confdefs* conf$$* $ac_clean_files && + exit $exit_status +' 0 +for ac_signal in 1 2 13 15; do + trap 'ac_signal='$ac_signal'; as_fn_exit 1' $ac_signal +done +ac_signal=0 + +# confdefs.h avoids OS command line length limits that DEFS can exceed. +rm -f -r conftest* confdefs.h + +$as_echo "/* confdefs.h */" > confdefs.h + +# Predefined preprocessor variables. + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_NAME "$PACKAGE_NAME" +_ACEOF + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_TARNAME "$PACKAGE_TARNAME" +_ACEOF + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_VERSION "$PACKAGE_VERSION" +_ACEOF + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_STRING "$PACKAGE_STRING" +_ACEOF + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT" +_ACEOF + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_URL "$PACKAGE_URL" +_ACEOF + + +# Let the site file select an alternate cache file if it wants to. +# Prefer an explicitly selected file to automatically selected ones. +ac_site_file1=NONE +ac_site_file2=NONE +if test -n "$CONFIG_SITE"; then + # We do not want a PATH search for config.site. + case $CONFIG_SITE in #(( + -*) ac_site_file1=./$CONFIG_SITE;; + */*) ac_site_file1=$CONFIG_SITE;; + *) ac_site_file1=./$CONFIG_SITE;; + esac +elif test "x$prefix" != xNONE; then + ac_site_file1=$prefix/share/config.site + ac_site_file2=$prefix/etc/config.site +else + ac_site_file1=$ac_default_prefix/share/config.site + ac_site_file2=$ac_default_prefix/etc/config.site +fi +for ac_site_file in "$ac_site_file1" "$ac_site_file2" +do + test "x$ac_site_file" = xNONE && continue + if test /dev/null != "$ac_site_file" && test -r "$ac_site_file"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: loading site script $ac_site_file" >&5 +$as_echo "$as_me: loading site script $ac_site_file" >&6;} + sed 's/^/| /' "$ac_site_file" >&5 + . "$ac_site_file" \ + || { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "failed to load site script $ac_site_file +See \`config.log' for more details" "$LINENO" 5; } + fi +done + +if test -r "$cache_file"; then + # Some versions of bash will fail to source /dev/null (special files + # actually), so we avoid doing that. DJGPP emulates it as a regular file. + if test /dev/null != "$cache_file" && test -f "$cache_file"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: loading cache $cache_file" >&5 +$as_echo "$as_me: loading cache $cache_file" >&6;} + case $cache_file in + [\\/]* | ?:[\\/]* ) . "$cache_file";; + *) . "./$cache_file";; + esac + fi +else + { $as_echo "$as_me:${as_lineno-$LINENO}: creating cache $cache_file" >&5 +$as_echo "$as_me: creating cache $cache_file" >&6;} + >$cache_file +fi + +# Check that the precious variables saved in the cache have kept the same +# value. +ac_cache_corrupted=false +for ac_var in $ac_precious_vars; do + eval ac_old_set=\$ac_cv_env_${ac_var}_set + eval ac_new_set=\$ac_env_${ac_var}_set + eval ac_old_val=\$ac_cv_env_${ac_var}_value + eval ac_new_val=\$ac_env_${ac_var}_value + case $ac_old_set,$ac_new_set in + set,) + { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5 +$as_echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;} + ac_cache_corrupted=: ;; + ,set) + { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was not set in the previous run" >&5 +$as_echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;} + ac_cache_corrupted=: ;; + ,);; + *) + if test "x$ac_old_val" != "x$ac_new_val"; then + # differences in whitespace do not lead to failure. + ac_old_val_w=`echo x $ac_old_val` + ac_new_val_w=`echo x $ac_new_val` + if test "$ac_old_val_w" != "$ac_new_val_w"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' has changed since the previous run:" >&5 +$as_echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;} + ac_cache_corrupted=: + else + { $as_echo "$as_me:${as_lineno-$LINENO}: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&5 +$as_echo "$as_me: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&2;} + eval $ac_var=\$ac_old_val + fi + { $as_echo "$as_me:${as_lineno-$LINENO}: former value: \`$ac_old_val'" >&5 +$as_echo "$as_me: former value: \`$ac_old_val'" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: current value: \`$ac_new_val'" >&5 +$as_echo "$as_me: current value: \`$ac_new_val'" >&2;} + fi;; + esac + # Pass precious variables to config.status. + if test "$ac_new_set" = set; then + case $ac_new_val in + *\'*) ac_arg=$ac_var=`$as_echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;; + *) ac_arg=$ac_var=$ac_new_val ;; + esac + case " $ac_configure_args " in + *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy. + *) as_fn_append ac_configure_args " '$ac_arg'" ;; + esac + fi +done +if $ac_cache_corrupted; then + { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: error: changes in the environment can compromise the build" >&5 +$as_echo "$as_me: error: changes in the environment can compromise the build" >&2;} + as_fn_error $? "run \`make distclean' and/or \`rm $cache_file' and start over" "$LINENO" 5 +fi +## -------------------- ## +## Main body of script. ## +## -------------------- ## + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + +ac_aux_dir= +for ac_dir in "$srcdir" "$srcdir/.." "$srcdir/../.."; do + if test -f "$ac_dir/install-sh"; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/install-sh -c" + break + elif test -f "$ac_dir/install.sh"; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/install.sh -c" + break + elif test -f "$ac_dir/shtool"; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/shtool install -c" + break + fi +done +if test -z "$ac_aux_dir"; then + as_fn_error $? "cannot find install-sh, install.sh, or shtool in \"$srcdir\" \"$srcdir/..\" \"$srcdir/../..\"" "$LINENO" 5 +fi + +# These three variables are undocumented and unsupported, +# and are intended to be withdrawn in a future Autoconf release. +# They can cause serious problems if a builder's source tree is in a directory +# whose full name contains unusual characters. +ac_config_guess="$SHELL $ac_aux_dir/config.guess" # Please don't use this var. +ac_config_sub="$SHELL $ac_aux_dir/config.sub" # Please don't use this var. +ac_configure="$SHELL $ac_aux_dir/configure" # Please don't use this var. + + +# Expand $ac_aux_dir to an absolute path. +am_aux_dir=`cd "$ac_aux_dir" && pwd` + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args. +set dummy ${ac_tool_prefix}gcc; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="${ac_tool_prefix}gcc" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_CC"; then + ac_ct_CC=$CC + # Extract the first word of "gcc", so it can be a program name with args. +set dummy gcc; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_CC="gcc" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 +$as_echo "$ac_ct_CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_CC" = x; then + CC="" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + CC=$ac_ct_CC + fi +else + CC="$ac_cv_prog_CC" +fi + +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args. +set dummy ${ac_tool_prefix}cc; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="${ac_tool_prefix}cc" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + fi +fi +if test -z "$CC"; then + # Extract the first word of "cc", so it can be a program name with args. +set dummy cc; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else + ac_prog_rejected=no +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then + ac_prog_rejected=yes + continue + fi + ac_cv_prog_CC="cc" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +if test $ac_prog_rejected = yes; then + # We found a bogon in the path, so make sure we never use it. + set dummy $ac_cv_prog_CC + shift + if test $# != 0; then + # We chose a different compiler from the bogus one. + # However, it has the same basename, so the bogon will be chosen + # first if we set CC to just the basename; use the full file name. + shift + ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@" + fi +fi +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + for ac_prog in cl.exe + do + # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. +set dummy $ac_tool_prefix$ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="$ac_tool_prefix$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$CC" && break + done +fi +if test -z "$CC"; then + ac_ct_CC=$CC + for ac_prog in cl.exe +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_CC="$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 +$as_echo "$ac_ct_CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$ac_ct_CC" && break +done + + if test "x$ac_ct_CC" = x; then + CC="" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + CC=$ac_ct_CC + fi +fi + +fi + + +test -z "$CC" && { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "no acceptable C compiler found in \$PATH +See \`config.log' for more details" "$LINENO" 5; } + +# Provide some information about the compiler. +$as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5 +set X $ac_compile +ac_compiler=$2 +for ac_option in --version -v -V -qversion; do + { { ac_try="$ac_compiler $ac_option >&5" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_compiler $ac_option >&5") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + sed '10a\ +... rest of stderr output deleted ... + 10q' conftest.err >conftest.er1 + cat conftest.er1 >&5 + fi + rm -f conftest.er1 conftest.err + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } +done + +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +ac_clean_files_save=$ac_clean_files +ac_clean_files="$ac_clean_files a.out a.out.dSYM a.exe b.out" +# Try to create an executable without -o first, disregard a.out. +# It will help us diagnose broken compilers, and finding out an intuition +# of exeext. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C compiler works" >&5 +$as_echo_n "checking whether the C compiler works... " >&6; } +ac_link_default=`$as_echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'` + +# The possible output files: +ac_files="a.out conftest.exe conftest a.exe a_out.exe b.out conftest.*" + +ac_rmfiles= +for ac_file in $ac_files +do + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; + * ) ac_rmfiles="$ac_rmfiles $ac_file";; + esac +done +rm -f $ac_rmfiles + +if { { ac_try="$ac_link_default" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link_default") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then : + # Autoconf-2.13 could set the ac_cv_exeext variable to `no'. +# So ignore a value of `no', otherwise this would lead to `EXEEXT = no' +# in a Makefile. We should not override ac_cv_exeext if it was cached, +# so that the user can short-circuit this test for compilers unknown to +# Autoconf. +for ac_file in $ac_files '' +do + test -f "$ac_file" || continue + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) + ;; + [ab].out ) + # We found the default executable, but exeext='' is most + # certainly right. + break;; + *.* ) + if test "${ac_cv_exeext+set}" = set && test "$ac_cv_exeext" != no; + then :; else + ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` + fi + # We set ac_cv_exeext here because the later test for it is not + # safe: cross compilers may not add the suffix if given an `-o' + # argument, so we may need to know it at that point already. + # Even if this section looks crufty: it has the advantage of + # actually working. + break;; + * ) + break;; + esac +done +test "$ac_cv_exeext" = no && ac_cv_exeext= + +else + ac_file='' +fi +if test -z "$ac_file"; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +$as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error 77 "C compiler cannot create executables +See \`config.log' for more details" "$LINENO" 5; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler default output file name" >&5 +$as_echo_n "checking for C compiler default output file name... " >&6; } +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_file" >&5 +$as_echo "$ac_file" >&6; } +ac_exeext=$ac_cv_exeext + +rm -f -r a.out a.out.dSYM a.exe conftest$ac_cv_exeext b.out +ac_clean_files=$ac_clean_files_save +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of executables" >&5 +$as_echo_n "checking for suffix of executables... " >&6; } +if { { ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then : + # If both `conftest.exe' and `conftest' are `present' (well, observable) +# catch `conftest.exe'. For instance with Cygwin, `ls conftest' will +# work properly (i.e., refer to `conftest.exe'), while it won't with +# `rm'. +for ac_file in conftest.exe conftest conftest.*; do + test -f "$ac_file" || continue + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; + *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` + break;; + * ) break;; + esac +done +else + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "cannot compute suffix of executables: cannot compile and link +See \`config.log' for more details" "$LINENO" 5; } +fi +rm -f conftest conftest$ac_cv_exeext +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_exeext" >&5 +$as_echo "$ac_cv_exeext" >&6; } + +rm -f conftest.$ac_ext +EXEEXT=$ac_cv_exeext +ac_exeext=$EXEEXT +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +int +main () +{ +FILE *f = fopen ("conftest.out", "w"); + return ferror (f) || fclose (f) != 0; + + ; + return 0; +} +_ACEOF +ac_clean_files="$ac_clean_files conftest.out" +# Check that the compiler produces executables we can run. If not, either +# the compiler is broken, or we cross compile. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are cross compiling" >&5 +$as_echo_n "checking whether we are cross compiling... " >&6; } +if test "$cross_compiling" != yes; then + { { ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } + if { ac_try='./conftest$ac_cv_exeext' + { { case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_try") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; }; then + cross_compiling=no + else + if test "$cross_compiling" = maybe; then + cross_compiling=yes + else + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "cannot run C compiled programs. +If you meant to cross compile, use \`--host'. +See \`config.log' for more details" "$LINENO" 5; } + fi + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $cross_compiling" >&5 +$as_echo "$cross_compiling" >&6; } + +rm -f conftest.$ac_ext conftest$ac_cv_exeext conftest.out +ac_clean_files=$ac_clean_files_save +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of object files" >&5 +$as_echo_n "checking for suffix of object files... " >&6; } +if ${ac_cv_objext+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.o conftest.obj +if { { ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_compile") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then : + for ac_file in conftest.o conftest.obj conftest.*; do + test -f "$ac_file" || continue; + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM ) ;; + *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'` + break;; + esac +done +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "cannot compute suffix of object files: cannot compile +See \`config.log' for more details" "$LINENO" 5; } +fi +rm -f conftest.$ac_cv_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_objext" >&5 +$as_echo "$ac_cv_objext" >&6; } +OBJEXT=$ac_cv_objext +ac_objext=$OBJEXT +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C compiler" >&5 +$as_echo_n "checking whether we are using the GNU C compiler... " >&6; } +if ${ac_cv_c_compiler_gnu+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ +#ifndef __GNUC__ + choke me +#endif + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_compiler_gnu=yes +else + ac_compiler_gnu=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +ac_cv_c_compiler_gnu=$ac_compiler_gnu + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5 +$as_echo "$ac_cv_c_compiler_gnu" >&6; } +if test $ac_compiler_gnu = yes; then + GCC=yes +else + GCC= +fi +ac_test_CFLAGS=${CFLAGS+set} +ac_save_CFLAGS=$CFLAGS +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5 +$as_echo_n "checking whether $CC accepts -g... " >&6; } +if ${ac_cv_prog_cc_g+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_save_c_werror_flag=$ac_c_werror_flag + ac_c_werror_flag=yes + ac_cv_prog_cc_g=no + CFLAGS="-g" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_prog_cc_g=yes +else + CFLAGS="" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + +else + ac_c_werror_flag=$ac_save_c_werror_flag + CFLAGS="-g" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_prog_cc_g=yes +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + ac_c_werror_flag=$ac_save_c_werror_flag +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5 +$as_echo "$ac_cv_prog_cc_g" >&6; } +if test "$ac_test_CFLAGS" = set; then + CFLAGS=$ac_save_CFLAGS +elif test $ac_cv_prog_cc_g = yes; then + if test "$GCC" = yes; then + CFLAGS="-g -O2" + else + CFLAGS="-g" + fi +else + if test "$GCC" = yes; then + CFLAGS="-O2" + else + CFLAGS= + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C89" >&5 +$as_echo_n "checking for $CC option to accept ISO C89... " >&6; } +if ${ac_cv_prog_cc_c89+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_cv_prog_cc_c89=no +ac_save_CC=$CC +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +#include +struct stat; +/* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */ +struct buf { int x; }; +FILE * (*rcsopen) (struct buf *, struct stat *, int); +static char *e (p, i) + char **p; + int i; +{ + return p[i]; +} +static char *f (char * (*g) (char **, int), char **p, ...) +{ + char *s; + va_list v; + va_start (v,p); + s = g (p, va_arg (v,int)); + va_end (v); + return s; +} + +/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has + function prototypes and stuff, but not '\xHH' hex character constants. + These don't provoke an error unfortunately, instead are silently treated + as 'x'. The following induces an error, until -std is added to get + proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an + array size at least. It's necessary to write '\x00'==0 to get something + that's true only with -std. */ +int osf4_cc_array ['\x00' == 0 ? 1 : -1]; + +/* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters + inside strings and character constants. */ +#define FOO(x) 'x' +int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1]; + +int test (int i, double x); +struct s1 {int (*f) (int a);}; +struct s2 {int (*f) (double a);}; +int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int); +int argc; +char **argv; +int +main () +{ +return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]; + ; + return 0; +} +_ACEOF +for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \ + -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" +do + CC="$ac_save_CC $ac_arg" + if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_prog_cc_c89=$ac_arg +fi +rm -f core conftest.err conftest.$ac_objext + test "x$ac_cv_prog_cc_c89" != "xno" && break +done +rm -f conftest.$ac_ext +CC=$ac_save_CC + +fi +# AC_CACHE_VAL +case "x$ac_cv_prog_cc_c89" in + x) + { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 +$as_echo "none needed" >&6; } ;; + xno) + { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 +$as_echo "unsupported" >&6; } ;; + *) + CC="$CC $ac_cv_prog_cc_c89" + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5 +$as_echo "$ac_cv_prog_cc_c89" >&6; } ;; +esac +if test "x$ac_cv_prog_cc_c89" != xno; then : + +fi + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC understands -c and -o together" >&5 +$as_echo_n "checking whether $CC understands -c and -o together... " >&6; } +if ${am_cv_prog_cc_c_o+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF + # Make sure it works both with $CC and with simple cc. + # Following AC_PROG_CC_C_O, we do the test twice because some + # compilers refuse to overwrite an existing .o file with -o, + # though they will create one. + am_cv_prog_cc_c_o=yes + for am_i in 1 2; do + if { echo "$as_me:$LINENO: $CC -c conftest.$ac_ext -o conftest2.$ac_objext" >&5 + ($CC -c conftest.$ac_ext -o conftest2.$ac_objext) >&5 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } \ + && test -f conftest2.$ac_objext; then + : OK + else + am_cv_prog_cc_c_o=no + break + fi + done + rm -f core conftest* + unset am_i +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_prog_cc_c_o" >&5 +$as_echo "$am_cv_prog_cc_c_o" >&6; } +if test "$am_cv_prog_cc_c_o" != yes; then + # Losing compiler, so override with the script. + # FIXME: It is wrong to rewrite CC. + # But if we don't then we get into trouble of one sort or another. + # A longer-term fix would be to have automake use am__CC in this case, + # and then we could set am__CC="\$(top_srcdir)/compile \$(CC)" + CC="$am_aux_dir/compile $CC" +fi +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to run the C preprocessor" >&5 +$as_echo_n "checking how to run the C preprocessor... " >&6; } +# On Suns, sometimes $CPP names a directory. +if test -n "$CPP" && test -d "$CPP"; then + CPP= +fi +if test -z "$CPP"; then + if ${ac_cv_prog_CPP+:} false; then : + $as_echo_n "(cached) " >&6 +else + # Double quotes because CPP needs to be expanded + for CPP in "$CC -E" "$CC -E -traditional-cpp" "/lib/cpp" + do + ac_preproc_ok=false +for ac_c_preproc_warn_flag in '' yes +do + # Use a header file that comes with gcc, so configuring glibc + # with a fresh cross-compiler works. + # Prefer to if __STDC__ is defined, since + # exists even on freestanding compilers. + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. "Syntax error" is here to catch this case. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#ifdef __STDC__ +# include +#else +# include +#endif + Syntax error +_ACEOF +if ac_fn_c_try_cpp "$LINENO"; then : + +else + # Broken: fails on valid input. +continue +fi +rm -f conftest.err conftest.i conftest.$ac_ext + + # OK, works on sane cases. Now check whether nonexistent headers + # can be detected and how. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +_ACEOF +if ac_fn_c_try_cpp "$LINENO"; then : + # Broken: success on invalid input. +continue +else + # Passes both tests. +ac_preproc_ok=: +break +fi +rm -f conftest.err conftest.i conftest.$ac_ext + +done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f conftest.i conftest.err conftest.$ac_ext +if $ac_preproc_ok; then : + break +fi + + done + ac_cv_prog_CPP=$CPP + +fi + CPP=$ac_cv_prog_CPP +else + ac_cv_prog_CPP=$CPP +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $CPP" >&5 +$as_echo "$CPP" >&6; } +ac_preproc_ok=false +for ac_c_preproc_warn_flag in '' yes +do + # Use a header file that comes with gcc, so configuring glibc + # with a fresh cross-compiler works. + # Prefer to if __STDC__ is defined, since + # exists even on freestanding compilers. + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. "Syntax error" is here to catch this case. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#ifdef __STDC__ +# include +#else +# include +#endif + Syntax error +_ACEOF +if ac_fn_c_try_cpp "$LINENO"; then : + +else + # Broken: fails on valid input. +continue +fi +rm -f conftest.err conftest.i conftest.$ac_ext + + # OK, works on sane cases. Now check whether nonexistent headers + # can be detected and how. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +_ACEOF +if ac_fn_c_try_cpp "$LINENO"; then : + # Broken: success on invalid input. +continue +else + # Passes both tests. +ac_preproc_ok=: +break +fi +rm -f conftest.err conftest.i conftest.$ac_ext + +done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f conftest.i conftest.err conftest.$ac_ext +if $ac_preproc_ok; then : + +else + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "C preprocessor \"$CPP\" fails sanity check +See \`config.log' for more details" "$LINENO" 5; } +fi + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for grep that handles long lines and -e" >&5 +$as_echo_n "checking for grep that handles long lines and -e... " >&6; } +if ${ac_cv_path_GREP+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -z "$GREP"; then + ac_path_GREP_found=false + # Loop through the user's path and test for each of PROGNAME-LIST + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_prog in grep ggrep; do + for ac_exec_ext in '' $ac_executable_extensions; do + ac_path_GREP="$as_dir/$ac_prog$ac_exec_ext" + as_fn_executable_p "$ac_path_GREP" || continue +# Check for GNU ac_path_GREP and select it if it is found. + # Check for GNU $ac_path_GREP +case `"$ac_path_GREP" --version 2>&1` in +*GNU*) + ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_found=:;; +*) + ac_count=0 + $as_echo_n 0123456789 >"conftest.in" + while : + do + cat "conftest.in" "conftest.in" >"conftest.tmp" + mv "conftest.tmp" "conftest.in" + cp "conftest.in" "conftest.nl" + $as_echo 'GREP' >> "conftest.nl" + "$ac_path_GREP" -e 'GREP$' -e '-(cannot match)-' < "conftest.nl" >"conftest.out" 2>/dev/null || break + diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break + as_fn_arith $ac_count + 1 && ac_count=$as_val + if test $ac_count -gt ${ac_path_GREP_max-0}; then + # Best one so far, save it but keep looking for a better one + ac_cv_path_GREP="$ac_path_GREP" + ac_path_GREP_max=$ac_count + fi + # 10*(2^10) chars as input seems more than enough + test $ac_count -gt 10 && break + done + rm -f conftest.in conftest.tmp conftest.nl conftest.out;; +esac + + $ac_path_GREP_found && break 3 + done + done + done +IFS=$as_save_IFS + if test -z "$ac_cv_path_GREP"; then + as_fn_error $? "no acceptable grep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 + fi +else + ac_cv_path_GREP=$GREP +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_GREP" >&5 +$as_echo "$ac_cv_path_GREP" >&6; } + GREP="$ac_cv_path_GREP" + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for egrep" >&5 +$as_echo_n "checking for egrep... " >&6; } +if ${ac_cv_path_EGREP+:} false; then : + $as_echo_n "(cached) " >&6 +else + if echo a | $GREP -E '(a|b)' >/dev/null 2>&1 + then ac_cv_path_EGREP="$GREP -E" + else + if test -z "$EGREP"; then + ac_path_EGREP_found=false + # Loop through the user's path and test for each of PROGNAME-LIST + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_prog in egrep; do + for ac_exec_ext in '' $ac_executable_extensions; do + ac_path_EGREP="$as_dir/$ac_prog$ac_exec_ext" + as_fn_executable_p "$ac_path_EGREP" || continue +# Check for GNU ac_path_EGREP and select it if it is found. + # Check for GNU $ac_path_EGREP +case `"$ac_path_EGREP" --version 2>&1` in +*GNU*) + ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_found=:;; +*) + ac_count=0 + $as_echo_n 0123456789 >"conftest.in" + while : + do + cat "conftest.in" "conftest.in" >"conftest.tmp" + mv "conftest.tmp" "conftest.in" + cp "conftest.in" "conftest.nl" + $as_echo 'EGREP' >> "conftest.nl" + "$ac_path_EGREP" 'EGREP$' < "conftest.nl" >"conftest.out" 2>/dev/null || break + diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break + as_fn_arith $ac_count + 1 && ac_count=$as_val + if test $ac_count -gt ${ac_path_EGREP_max-0}; then + # Best one so far, save it but keep looking for a better one + ac_cv_path_EGREP="$ac_path_EGREP" + ac_path_EGREP_max=$ac_count + fi + # 10*(2^10) chars as input seems more than enough + test $ac_count -gt 10 && break + done + rm -f conftest.in conftest.tmp conftest.nl conftest.out;; +esac + + $ac_path_EGREP_found && break 3 + done + done + done +IFS=$as_save_IFS + if test -z "$ac_cv_path_EGREP"; then + as_fn_error $? "no acceptable egrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 + fi +else + ac_cv_path_EGREP=$EGREP +fi + + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_EGREP" >&5 +$as_echo "$ac_cv_path_EGREP" >&6; } + EGREP="$ac_cv_path_EGREP" + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for ANSI C header files" >&5 +$as_echo_n "checking for ANSI C header files... " >&6; } +if ${ac_cv_header_stdc+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +#include +#include +#include + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_header_stdc=yes +else + ac_cv_header_stdc=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + +if test $ac_cv_header_stdc = yes; then + # SunOS 4.x string.h does not declare mem*, contrary to ANSI. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "memchr" >/dev/null 2>&1; then : + +else + ac_cv_header_stdc=no +fi +rm -f conftest* + +fi + +if test $ac_cv_header_stdc = yes; then + # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "free" >/dev/null 2>&1; then : + +else + ac_cv_header_stdc=no +fi +rm -f conftest* + +fi + +if test $ac_cv_header_stdc = yes; then + # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi. + if test "$cross_compiling" = yes; then : + : +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +#include +#if ((' ' & 0x0FF) == 0x020) +# define ISLOWER(c) ('a' <= (c) && (c) <= 'z') +# define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c)) +#else +# define ISLOWER(c) \ + (('a' <= (c) && (c) <= 'i') \ + || ('j' <= (c) && (c) <= 'r') \ + || ('s' <= (c) && (c) <= 'z')) +# define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c)) +#endif + +#define XOR(e, f) (((e) && !(f)) || (!(e) && (f))) +int +main () +{ + int i; + for (i = 0; i < 256; i++) + if (XOR (islower (i), ISLOWER (i)) + || toupper (i) != TOUPPER (i)) + return 2; + return 0; +} +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + +else + ac_cv_header_stdc=no +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +fi + +fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_stdc" >&5 +$as_echo "$ac_cv_header_stdc" >&6; } +if test $ac_cv_header_stdc = yes; then + +$as_echo "#define STDC_HEADERS 1" >>confdefs.h + +fi + +# On IRIX 5.3, sys/types and inttypes.h are conflicting. +for ac_header in sys/types.h sys/stat.h stdlib.h string.h memory.h strings.h \ + inttypes.h stdint.h unistd.h +do : + as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` +ac_fn_c_check_header_compile "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default +" +if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : + cat >>confdefs.h <<_ACEOF +#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + + + + ac_fn_c_check_header_mongrel "$LINENO" "minix/config.h" "ac_cv_header_minix_config_h" "$ac_includes_default" +if test "x$ac_cv_header_minix_config_h" = xyes; then : + MINIX=yes +else + MINIX= +fi + + + if test "$MINIX" = yes; then + +$as_echo "#define _POSIX_SOURCE 1" >>confdefs.h + + +$as_echo "#define _POSIX_1_SOURCE 2" >>confdefs.h + + +$as_echo "#define _MINIX 1" >>confdefs.h + + fi + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether it is safe to define __EXTENSIONS__" >&5 +$as_echo_n "checking whether it is safe to define __EXTENSIONS__... " >&6; } +if ${ac_cv_safe_to_define___extensions__+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +# define __EXTENSIONS__ 1 + $ac_includes_default +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_safe_to_define___extensions__=yes +else + ac_cv_safe_to_define___extensions__=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_safe_to_define___extensions__" >&5 +$as_echo "$ac_cv_safe_to_define___extensions__" >&6; } + test $ac_cv_safe_to_define___extensions__ = yes && + $as_echo "#define __EXTENSIONS__ 1" >>confdefs.h + + $as_echo "#define _ALL_SOURCE 1" >>confdefs.h + + $as_echo "#define _GNU_SOURCE 1" >>confdefs.h + + $as_echo "#define _POSIX_PTHREAD_SEMANTICS 1" >>confdefs.h + + $as_echo "#define _TANDEM_SOURCE 1" >>confdefs.h + + + + +case `pwd` in + *\ * | *\ *) + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Libtool does not cope well with whitespace in \`pwd\`" >&5 +$as_echo "$as_me: WARNING: Libtool does not cope well with whitespace in \`pwd\`" >&2;} ;; +esac + + + +macro_version='2.4.2' +macro_revision='1.3337' + + + + + + + + + + + + + +ltmain="$ac_aux_dir/ltmain.sh" + +# Make sure we can run config.sub. +$SHELL "$ac_aux_dir/config.sub" sun4 >/dev/null 2>&1 || + as_fn_error $? "cannot run $SHELL $ac_aux_dir/config.sub" "$LINENO" 5 + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking build system type" >&5 +$as_echo_n "checking build system type... " >&6; } +if ${ac_cv_build+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_build_alias=$build_alias +test "x$ac_build_alias" = x && + ac_build_alias=`$SHELL "$ac_aux_dir/config.guess"` +test "x$ac_build_alias" = x && + as_fn_error $? "cannot guess build type; you must specify one" "$LINENO" 5 +ac_cv_build=`$SHELL "$ac_aux_dir/config.sub" $ac_build_alias` || + as_fn_error $? "$SHELL $ac_aux_dir/config.sub $ac_build_alias failed" "$LINENO" 5 + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_build" >&5 +$as_echo "$ac_cv_build" >&6; } +case $ac_cv_build in +*-*-*) ;; +*) as_fn_error $? "invalid value of canonical build" "$LINENO" 5;; +esac +build=$ac_cv_build +ac_save_IFS=$IFS; IFS='-' +set x $ac_cv_build +shift +build_cpu=$1 +build_vendor=$2 +shift; shift +# Remember, the first character of IFS is used to create $*, +# except with old shells: +build_os=$* +IFS=$ac_save_IFS +case $build_os in *\ *) build_os=`echo "$build_os" | sed 's/ /-/g'`;; esac + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking host system type" >&5 +$as_echo_n "checking host system type... " >&6; } +if ${ac_cv_host+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test "x$host_alias" = x; then + ac_cv_host=$ac_cv_build +else + ac_cv_host=`$SHELL "$ac_aux_dir/config.sub" $host_alias` || + as_fn_error $? "$SHELL $ac_aux_dir/config.sub $host_alias failed" "$LINENO" 5 +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_host" >&5 +$as_echo "$ac_cv_host" >&6; } +case $ac_cv_host in +*-*-*) ;; +*) as_fn_error $? "invalid value of canonical host" "$LINENO" 5;; +esac +host=$ac_cv_host +ac_save_IFS=$IFS; IFS='-' +set x $ac_cv_host +shift +host_cpu=$1 +host_vendor=$2 +shift; shift +# Remember, the first character of IFS is used to create $*, +# except with old shells: +host_os=$* +IFS=$ac_save_IFS +case $host_os in *\ *) host_os=`echo "$host_os" | sed 's/ /-/g'`;; esac + + +# Backslashify metacharacters that are still active within +# double-quoted strings. +sed_quote_subst='s/\(["`$\\]\)/\\\1/g' + +# Same as above, but do not quote variable references. +double_quote_subst='s/\(["`\\]\)/\\\1/g' + +# Sed substitution to delay expansion of an escaped shell variable in a +# double_quote_subst'ed string. +delay_variable_subst='s/\\\\\\\\\\\$/\\\\\\$/g' + +# Sed substitution to delay expansion of an escaped single quote. +delay_single_quote_subst='s/'\''/'\'\\\\\\\'\''/g' + +# Sed substitution to avoid accidental globbing in evaled expressions +no_glob_subst='s/\*/\\\*/g' + +ECHO='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' +ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO +ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO$ECHO + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to print strings" >&5 +$as_echo_n "checking how to print strings... " >&6; } +# Test print first, because it will be a builtin if present. +if test "X`( print -r -- -n ) 2>/dev/null`" = X-n && \ + test "X`print -r -- $ECHO 2>/dev/null`" = "X$ECHO"; then + ECHO='print -r --' +elif test "X`printf %s $ECHO 2>/dev/null`" = "X$ECHO"; then + ECHO='printf %s\n' +else + # Use this function as a fallback that always works. + func_fallback_echo () + { + eval 'cat <<_LTECHO_EOF +$1 +_LTECHO_EOF' + } + ECHO='func_fallback_echo' +fi + +# func_echo_all arg... +# Invoke $ECHO with all args, space-separated. +func_echo_all () +{ + $ECHO "" +} + +case "$ECHO" in + printf*) { $as_echo "$as_me:${as_lineno-$LINENO}: result: printf" >&5 +$as_echo "printf" >&6; } ;; + print*) { $as_echo "$as_me:${as_lineno-$LINENO}: result: print -r" >&5 +$as_echo "print -r" >&6; } ;; + *) { $as_echo "$as_me:${as_lineno-$LINENO}: result: cat" >&5 +$as_echo "cat" >&6; } ;; +esac + + + + + + + + + + + + + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for a sed that does not truncate output" >&5 +$as_echo_n "checking for a sed that does not truncate output... " >&6; } +if ${ac_cv_path_SED+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_script=s/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb/ + for ac_i in 1 2 3 4 5 6 7; do + ac_script="$ac_script$as_nl$ac_script" + done + echo "$ac_script" 2>/dev/null | sed 99q >conftest.sed + { ac_script=; unset ac_script;} + if test -z "$SED"; then + ac_path_SED_found=false + # Loop through the user's path and test for each of PROGNAME-LIST + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_prog in sed gsed; do + for ac_exec_ext in '' $ac_executable_extensions; do + ac_path_SED="$as_dir/$ac_prog$ac_exec_ext" + as_fn_executable_p "$ac_path_SED" || continue +# Check for GNU ac_path_SED and select it if it is found. + # Check for GNU $ac_path_SED +case `"$ac_path_SED" --version 2>&1` in +*GNU*) + ac_cv_path_SED="$ac_path_SED" ac_path_SED_found=:;; +*) + ac_count=0 + $as_echo_n 0123456789 >"conftest.in" + while : + do + cat "conftest.in" "conftest.in" >"conftest.tmp" + mv "conftest.tmp" "conftest.in" + cp "conftest.in" "conftest.nl" + $as_echo '' >> "conftest.nl" + "$ac_path_SED" -f conftest.sed < "conftest.nl" >"conftest.out" 2>/dev/null || break + diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break + as_fn_arith $ac_count + 1 && ac_count=$as_val + if test $ac_count -gt ${ac_path_SED_max-0}; then + # Best one so far, save it but keep looking for a better one + ac_cv_path_SED="$ac_path_SED" + ac_path_SED_max=$ac_count + fi + # 10*(2^10) chars as input seems more than enough + test $ac_count -gt 10 && break + done + rm -f conftest.in conftest.tmp conftest.nl conftest.out;; +esac + + $ac_path_SED_found && break 3 + done + done + done +IFS=$as_save_IFS + if test -z "$ac_cv_path_SED"; then + as_fn_error $? "no acceptable sed could be found in \$PATH" "$LINENO" 5 + fi +else + ac_cv_path_SED=$SED +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_SED" >&5 +$as_echo "$ac_cv_path_SED" >&6; } + SED="$ac_cv_path_SED" + rm -f conftest.sed + +test -z "$SED" && SED=sed +Xsed="$SED -e 1s/^X//" + + + + + + + + + + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for fgrep" >&5 +$as_echo_n "checking for fgrep... " >&6; } +if ${ac_cv_path_FGREP+:} false; then : + $as_echo_n "(cached) " >&6 +else + if echo 'ab*c' | $GREP -F 'ab*c' >/dev/null 2>&1 + then ac_cv_path_FGREP="$GREP -F" + else + if test -z "$FGREP"; then + ac_path_FGREP_found=false + # Loop through the user's path and test for each of PROGNAME-LIST + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_prog in fgrep; do + for ac_exec_ext in '' $ac_executable_extensions; do + ac_path_FGREP="$as_dir/$ac_prog$ac_exec_ext" + as_fn_executable_p "$ac_path_FGREP" || continue +# Check for GNU ac_path_FGREP and select it if it is found. + # Check for GNU $ac_path_FGREP +case `"$ac_path_FGREP" --version 2>&1` in +*GNU*) + ac_cv_path_FGREP="$ac_path_FGREP" ac_path_FGREP_found=:;; +*) + ac_count=0 + $as_echo_n 0123456789 >"conftest.in" + while : + do + cat "conftest.in" "conftest.in" >"conftest.tmp" + mv "conftest.tmp" "conftest.in" + cp "conftest.in" "conftest.nl" + $as_echo 'FGREP' >> "conftest.nl" + "$ac_path_FGREP" FGREP < "conftest.nl" >"conftest.out" 2>/dev/null || break + diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break + as_fn_arith $ac_count + 1 && ac_count=$as_val + if test $ac_count -gt ${ac_path_FGREP_max-0}; then + # Best one so far, save it but keep looking for a better one + ac_cv_path_FGREP="$ac_path_FGREP" + ac_path_FGREP_max=$ac_count + fi + # 10*(2^10) chars as input seems more than enough + test $ac_count -gt 10 && break + done + rm -f conftest.in conftest.tmp conftest.nl conftest.out;; +esac + + $ac_path_FGREP_found && break 3 + done + done + done +IFS=$as_save_IFS + if test -z "$ac_cv_path_FGREP"; then + as_fn_error $? "no acceptable fgrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 + fi +else + ac_cv_path_FGREP=$FGREP +fi + + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_FGREP" >&5 +$as_echo "$ac_cv_path_FGREP" >&6; } + FGREP="$ac_cv_path_FGREP" + + +test -z "$GREP" && GREP=grep + + + + + + + + + + + + + + + + + + + +# Check whether --with-gnu-ld was given. +if test "${with_gnu_ld+set}" = set; then : + withval=$with_gnu_ld; test "$withval" = no || with_gnu_ld=yes +else + with_gnu_ld=no +fi + +ac_prog=ld +if test "$GCC" = yes; then + # Check if gcc -print-prog-name=ld gives a path. + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ld used by $CC" >&5 +$as_echo_n "checking for ld used by $CC... " >&6; } + case $host in + *-*-mingw*) + # gcc leaves a trailing carriage return which upsets mingw + ac_prog=`($CC -print-prog-name=ld) 2>&5 | tr -d '\015'` ;; + *) + ac_prog=`($CC -print-prog-name=ld) 2>&5` ;; + esac + case $ac_prog in + # Accept absolute paths. + [\\/]* | ?:[\\/]*) + re_direlt='/[^/][^/]*/\.\./' + # Canonicalize the pathname of ld + ac_prog=`$ECHO "$ac_prog"| $SED 's%\\\\%/%g'` + while $ECHO "$ac_prog" | $GREP "$re_direlt" > /dev/null 2>&1; do + ac_prog=`$ECHO $ac_prog| $SED "s%$re_direlt%/%"` + done + test -z "$LD" && LD="$ac_prog" + ;; + "") + # If it fails, then pretend we aren't using GCC. + ac_prog=ld + ;; + *) + # If it is relative, then search for the first ld in PATH. + with_gnu_ld=unknown + ;; + esac +elif test "$with_gnu_ld" = yes; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for GNU ld" >&5 +$as_echo_n "checking for GNU ld... " >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for non-GNU ld" >&5 +$as_echo_n "checking for non-GNU ld... " >&6; } +fi +if ${lt_cv_path_LD+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -z "$LD"; then + lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR + for ac_dir in $PATH; do + IFS="$lt_save_ifs" + test -z "$ac_dir" && ac_dir=. + if test -f "$ac_dir/$ac_prog" || test -f "$ac_dir/$ac_prog$ac_exeext"; then + lt_cv_path_LD="$ac_dir/$ac_prog" + # Check to see if the program is GNU ld. I'd rather use --version, + # but apparently some variants of GNU ld only accept -v. + # Break only if it was the GNU/non-GNU ld that we prefer. + case `"$lt_cv_path_LD" -v 2>&1 &5 +$as_echo "$LD" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi +test -z "$LD" && as_fn_error $? "no acceptable ld found in \$PATH" "$LINENO" 5 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if the linker ($LD) is GNU ld" >&5 +$as_echo_n "checking if the linker ($LD) is GNU ld... " >&6; } +if ${lt_cv_prog_gnu_ld+:} false; then : + $as_echo_n "(cached) " >&6 +else + # I'd rather use --version here, but apparently some GNU lds only accept -v. +case `$LD -v 2>&1 &5 +$as_echo "$lt_cv_prog_gnu_ld" >&6; } +with_gnu_ld=$lt_cv_prog_gnu_ld + + + + + + + + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for BSD- or MS-compatible name lister (nm)" >&5 +$as_echo_n "checking for BSD- or MS-compatible name lister (nm)... " >&6; } +if ${lt_cv_path_NM+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$NM"; then + # Let the user override the test. + lt_cv_path_NM="$NM" +else + lt_nm_to_check="${ac_tool_prefix}nm" + if test -n "$ac_tool_prefix" && test "$build" = "$host"; then + lt_nm_to_check="$lt_nm_to_check nm" + fi + for lt_tmp_nm in $lt_nm_to_check; do + lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR + for ac_dir in $PATH /usr/ccs/bin/elf /usr/ccs/bin /usr/ucb /bin; do + IFS="$lt_save_ifs" + test -z "$ac_dir" && ac_dir=. + tmp_nm="$ac_dir/$lt_tmp_nm" + if test -f "$tmp_nm" || test -f "$tmp_nm$ac_exeext" ; then + # Check to see if the nm accepts a BSD-compat flag. + # Adding the `sed 1q' prevents false positives on HP-UX, which says: + # nm: unknown option "B" ignored + # Tru64's nm complains that /dev/null is an invalid object file + case `"$tmp_nm" -B /dev/null 2>&1 | sed '1q'` in + */dev/null* | *'Invalid file or object type'*) + lt_cv_path_NM="$tmp_nm -B" + break + ;; + *) + case `"$tmp_nm" -p /dev/null 2>&1 | sed '1q'` in + */dev/null*) + lt_cv_path_NM="$tmp_nm -p" + break + ;; + *) + lt_cv_path_NM=${lt_cv_path_NM="$tmp_nm"} # keep the first match, but + continue # so that we can try to find one that supports BSD flags + ;; + esac + ;; + esac + fi + done + IFS="$lt_save_ifs" + done + : ${lt_cv_path_NM=no} +fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_path_NM" >&5 +$as_echo "$lt_cv_path_NM" >&6; } +if test "$lt_cv_path_NM" != "no"; then + NM="$lt_cv_path_NM" +else + # Didn't find any BSD compatible name lister, look for dumpbin. + if test -n "$DUMPBIN"; then : + # Let the user override the test. + else + if test -n "$ac_tool_prefix"; then + for ac_prog in dumpbin "link -dump" + do + # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. +set dummy $ac_tool_prefix$ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_DUMPBIN+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$DUMPBIN"; then + ac_cv_prog_DUMPBIN="$DUMPBIN" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_DUMPBIN="$ac_tool_prefix$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +DUMPBIN=$ac_cv_prog_DUMPBIN +if test -n "$DUMPBIN"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $DUMPBIN" >&5 +$as_echo "$DUMPBIN" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$DUMPBIN" && break + done +fi +if test -z "$DUMPBIN"; then + ac_ct_DUMPBIN=$DUMPBIN + for ac_prog in dumpbin "link -dump" +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_DUMPBIN+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_DUMPBIN"; then + ac_cv_prog_ac_ct_DUMPBIN="$ac_ct_DUMPBIN" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_DUMPBIN="$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_DUMPBIN=$ac_cv_prog_ac_ct_DUMPBIN +if test -n "$ac_ct_DUMPBIN"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_DUMPBIN" >&5 +$as_echo "$ac_ct_DUMPBIN" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$ac_ct_DUMPBIN" && break +done + + if test "x$ac_ct_DUMPBIN" = x; then + DUMPBIN=":" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + DUMPBIN=$ac_ct_DUMPBIN + fi +fi + + case `$DUMPBIN -symbols /dev/null 2>&1 | sed '1q'` in + *COFF*) + DUMPBIN="$DUMPBIN -symbols" + ;; + *) + DUMPBIN=: + ;; + esac + fi + + if test "$DUMPBIN" != ":"; then + NM="$DUMPBIN" + fi +fi +test -z "$NM" && NM=nm + + + + + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking the name lister ($NM) interface" >&5 +$as_echo_n "checking the name lister ($NM) interface... " >&6; } +if ${lt_cv_nm_interface+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_nm_interface="BSD nm" + echo "int some_variable = 0;" > conftest.$ac_ext + (eval echo "\"\$as_me:$LINENO: $ac_compile\"" >&5) + (eval "$ac_compile" 2>conftest.err) + cat conftest.err >&5 + (eval echo "\"\$as_me:$LINENO: $NM \\\"conftest.$ac_objext\\\"\"" >&5) + (eval "$NM \"conftest.$ac_objext\"" 2>conftest.err > conftest.out) + cat conftest.err >&5 + (eval echo "\"\$as_me:$LINENO: output\"" >&5) + cat conftest.out >&5 + if $GREP 'External.*some_variable' conftest.out > /dev/null; then + lt_cv_nm_interface="MS dumpbin" + fi + rm -f conftest* +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_nm_interface" >&5 +$as_echo "$lt_cv_nm_interface" >&6; } + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ln -s works" >&5 +$as_echo_n "checking whether ln -s works... " >&6; } +LN_S=$as_ln_s +if test "$LN_S" = "ln -s"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no, using $LN_S" >&5 +$as_echo "no, using $LN_S" >&6; } +fi + +# find the maximum length of command line arguments +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking the maximum length of command line arguments" >&5 +$as_echo_n "checking the maximum length of command line arguments... " >&6; } +if ${lt_cv_sys_max_cmd_len+:} false; then : + $as_echo_n "(cached) " >&6 +else + i=0 + teststring="ABCD" + + case $build_os in + msdosdjgpp*) + # On DJGPP, this test can blow up pretty badly due to problems in libc + # (any single argument exceeding 2000 bytes causes a buffer overrun + # during glob expansion). Even if it were fixed, the result of this + # check would be larger than it should be. + lt_cv_sys_max_cmd_len=12288; # 12K is about right + ;; + + gnu*) + # Under GNU Hurd, this test is not required because there is + # no limit to the length of command line arguments. + # Libtool will interpret -1 as no limit whatsoever + lt_cv_sys_max_cmd_len=-1; + ;; + + cygwin* | mingw* | cegcc*) + # On Win9x/ME, this test blows up -- it succeeds, but takes + # about 5 minutes as the teststring grows exponentially. + # Worse, since 9x/ME are not pre-emptively multitasking, + # you end up with a "frozen" computer, even though with patience + # the test eventually succeeds (with a max line length of 256k). + # Instead, let's just punt: use the minimum linelength reported by + # all of the supported platforms: 8192 (on NT/2K/XP). + lt_cv_sys_max_cmd_len=8192; + ;; + + mint*) + # On MiNT this can take a long time and run out of memory. + lt_cv_sys_max_cmd_len=8192; + ;; + + amigaos*) + # On AmigaOS with pdksh, this test takes hours, literally. + # So we just punt and use a minimum line length of 8192. + lt_cv_sys_max_cmd_len=8192; + ;; + + netbsd* | freebsd* | openbsd* | darwin* | dragonfly*) + # This has been around since 386BSD, at least. Likely further. + if test -x /sbin/sysctl; then + lt_cv_sys_max_cmd_len=`/sbin/sysctl -n kern.argmax` + elif test -x /usr/sbin/sysctl; then + lt_cv_sys_max_cmd_len=`/usr/sbin/sysctl -n kern.argmax` + else + lt_cv_sys_max_cmd_len=65536 # usable default for all BSDs + fi + # And add a safety zone + lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4` + lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3` + ;; + + interix*) + # We know the value 262144 and hardcode it with a safety zone (like BSD) + lt_cv_sys_max_cmd_len=196608 + ;; + + os2*) + # The test takes a long time on OS/2. + lt_cv_sys_max_cmd_len=8192 + ;; + + osf*) + # Dr. Hans Ekkehard Plesser reports seeing a kernel panic running configure + # due to this test when exec_disable_arg_limit is 1 on Tru64. It is not + # nice to cause kernel panics so lets avoid the loop below. + # First set a reasonable default. + lt_cv_sys_max_cmd_len=16384 + # + if test -x /sbin/sysconfig; then + case `/sbin/sysconfig -q proc exec_disable_arg_limit` in + *1*) lt_cv_sys_max_cmd_len=-1 ;; + esac + fi + ;; + sco3.2v5*) + lt_cv_sys_max_cmd_len=102400 + ;; + sysv5* | sco5v6* | sysv4.2uw2*) + kargmax=`grep ARG_MAX /etc/conf/cf.d/stune 2>/dev/null` + if test -n "$kargmax"; then + lt_cv_sys_max_cmd_len=`echo $kargmax | sed 's/.*[ ]//'` + else + lt_cv_sys_max_cmd_len=32768 + fi + ;; + *) + lt_cv_sys_max_cmd_len=`(getconf ARG_MAX) 2> /dev/null` + if test -n "$lt_cv_sys_max_cmd_len" && \ + test undefined != "$lt_cv_sys_max_cmd_len"; then + lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4` + lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3` + else + # Make teststring a little bigger before we do anything with it. + # a 1K string should be a reasonable start. + for i in 1 2 3 4 5 6 7 8 ; do + teststring=$teststring$teststring + done + SHELL=${SHELL-${CONFIG_SHELL-/bin/sh}} + # If test is not a shell built-in, we'll probably end up computing a + # maximum length that is only half of the actual maximum length, but + # we can't tell. + while { test "X"`env echo "$teststring$teststring" 2>/dev/null` \ + = "X$teststring$teststring"; } >/dev/null 2>&1 && + test $i != 17 # 1/2 MB should be enough + do + i=`expr $i + 1` + teststring=$teststring$teststring + done + # Only check the string length outside the loop. + lt_cv_sys_max_cmd_len=`expr "X$teststring" : ".*" 2>&1` + teststring= + # Add a significant safety factor because C++ compilers can tack on + # massive amounts of additional arguments before passing them to the + # linker. It appears as though 1/2 is a usable value. + lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 2` + fi + ;; + esac + +fi + +if test -n $lt_cv_sys_max_cmd_len ; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_sys_max_cmd_len" >&5 +$as_echo "$lt_cv_sys_max_cmd_len" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: none" >&5 +$as_echo "none" >&6; } +fi +max_cmd_len=$lt_cv_sys_max_cmd_len + + + + + + +: ${CP="cp -f"} +: ${MV="mv -f"} +: ${RM="rm -f"} + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the shell understands some XSI constructs" >&5 +$as_echo_n "checking whether the shell understands some XSI constructs... " >&6; } +# Try some XSI features +xsi_shell=no +( _lt_dummy="a/b/c" + test "${_lt_dummy##*/},${_lt_dummy%/*},${_lt_dummy#??}"${_lt_dummy%"$_lt_dummy"}, \ + = c,a/b,b/c, \ + && eval 'test $(( 1 + 1 )) -eq 2 \ + && test "${#_lt_dummy}" -eq 5' ) >/dev/null 2>&1 \ + && xsi_shell=yes +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $xsi_shell" >&5 +$as_echo "$xsi_shell" >&6; } + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the shell understands \"+=\"" >&5 +$as_echo_n "checking whether the shell understands \"+=\"... " >&6; } +lt_shell_append=no +( foo=bar; set foo baz; eval "$1+=\$2" && test "$foo" = barbaz ) \ + >/dev/null 2>&1 \ + && lt_shell_append=yes +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_shell_append" >&5 +$as_echo "$lt_shell_append" >&6; } + + +if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then + lt_unset=unset +else + lt_unset=false +fi + + + + + +# test EBCDIC or ASCII +case `echo X|tr X '\101'` in + A) # ASCII based system + # \n is not interpreted correctly by Solaris 8 /usr/ucb/tr + lt_SP2NL='tr \040 \012' + lt_NL2SP='tr \015\012 \040\040' + ;; + *) # EBCDIC based system + lt_SP2NL='tr \100 \n' + lt_NL2SP='tr \r\n \100\100' + ;; +esac + + + + + + + + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to convert $build file names to $host format" >&5 +$as_echo_n "checking how to convert $build file names to $host format... " >&6; } +if ${lt_cv_to_host_file_cmd+:} false; then : + $as_echo_n "(cached) " >&6 +else + case $host in + *-*-mingw* ) + case $build in + *-*-mingw* ) # actually msys + lt_cv_to_host_file_cmd=func_convert_file_msys_to_w32 + ;; + *-*-cygwin* ) + lt_cv_to_host_file_cmd=func_convert_file_cygwin_to_w32 + ;; + * ) # otherwise, assume *nix + lt_cv_to_host_file_cmd=func_convert_file_nix_to_w32 + ;; + esac + ;; + *-*-cygwin* ) + case $build in + *-*-mingw* ) # actually msys + lt_cv_to_host_file_cmd=func_convert_file_msys_to_cygwin + ;; + *-*-cygwin* ) + lt_cv_to_host_file_cmd=func_convert_file_noop + ;; + * ) # otherwise, assume *nix + lt_cv_to_host_file_cmd=func_convert_file_nix_to_cygwin + ;; + esac + ;; + * ) # unhandled hosts (and "normal" native builds) + lt_cv_to_host_file_cmd=func_convert_file_noop + ;; +esac + +fi + +to_host_file_cmd=$lt_cv_to_host_file_cmd +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_to_host_file_cmd" >&5 +$as_echo "$lt_cv_to_host_file_cmd" >&6; } + + + + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to convert $build file names to toolchain format" >&5 +$as_echo_n "checking how to convert $build file names to toolchain format... " >&6; } +if ${lt_cv_to_tool_file_cmd+:} false; then : + $as_echo_n "(cached) " >&6 +else + #assume ordinary cross tools, or native build. +lt_cv_to_tool_file_cmd=func_convert_file_noop +case $host in + *-*-mingw* ) + case $build in + *-*-mingw* ) # actually msys + lt_cv_to_tool_file_cmd=func_convert_file_msys_to_w32 + ;; + esac + ;; +esac + +fi + +to_tool_file_cmd=$lt_cv_to_tool_file_cmd +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_to_tool_file_cmd" >&5 +$as_echo "$lt_cv_to_tool_file_cmd" >&6; } + + + + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $LD option to reload object files" >&5 +$as_echo_n "checking for $LD option to reload object files... " >&6; } +if ${lt_cv_ld_reload_flag+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_ld_reload_flag='-r' +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_ld_reload_flag" >&5 +$as_echo "$lt_cv_ld_reload_flag" >&6; } +reload_flag=$lt_cv_ld_reload_flag +case $reload_flag in +"" | " "*) ;; +*) reload_flag=" $reload_flag" ;; +esac +reload_cmds='$LD$reload_flag -o $output$reload_objs' +case $host_os in + cygwin* | mingw* | pw32* | cegcc*) + if test "$GCC" != yes; then + reload_cmds=false + fi + ;; + darwin*) + if test "$GCC" = yes; then + reload_cmds='$LTCC $LTCFLAGS -nostdlib ${wl}-r -o $output$reload_objs' + else + reload_cmds='$LD$reload_flag -o $output$reload_objs' + fi + ;; +esac + + + + + + + + + +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}objdump", so it can be a program name with args. +set dummy ${ac_tool_prefix}objdump; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_OBJDUMP+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$OBJDUMP"; then + ac_cv_prog_OBJDUMP="$OBJDUMP" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_OBJDUMP="${ac_tool_prefix}objdump" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +OBJDUMP=$ac_cv_prog_OBJDUMP +if test -n "$OBJDUMP"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $OBJDUMP" >&5 +$as_echo "$OBJDUMP" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_OBJDUMP"; then + ac_ct_OBJDUMP=$OBJDUMP + # Extract the first word of "objdump", so it can be a program name with args. +set dummy objdump; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_OBJDUMP+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_OBJDUMP"; then + ac_cv_prog_ac_ct_OBJDUMP="$ac_ct_OBJDUMP" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_OBJDUMP="objdump" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_OBJDUMP=$ac_cv_prog_ac_ct_OBJDUMP +if test -n "$ac_ct_OBJDUMP"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_OBJDUMP" >&5 +$as_echo "$ac_ct_OBJDUMP" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_OBJDUMP" = x; then + OBJDUMP="false" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + OBJDUMP=$ac_ct_OBJDUMP + fi +else + OBJDUMP="$ac_cv_prog_OBJDUMP" +fi + +test -z "$OBJDUMP" && OBJDUMP=objdump + + + + + + + + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to recognize dependent libraries" >&5 +$as_echo_n "checking how to recognize dependent libraries... " >&6; } +if ${lt_cv_deplibs_check_method+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_file_magic_cmd='$MAGIC_CMD' +lt_cv_file_magic_test_file= +lt_cv_deplibs_check_method='unknown' +# Need to set the preceding variable on all platforms that support +# interlibrary dependencies. +# 'none' -- dependencies not supported. +# `unknown' -- same as none, but documents that we really don't know. +# 'pass_all' -- all dependencies passed with no checks. +# 'test_compile' -- check by making test program. +# 'file_magic [[regex]]' -- check by looking for files in library path +# which responds to the $file_magic_cmd with a given extended regex. +# If you have `file' or equivalent on your system and you're not sure +# whether `pass_all' will *always* work, you probably want this one. + +case $host_os in +aix[4-9]*) + lt_cv_deplibs_check_method=pass_all + ;; + +beos*) + lt_cv_deplibs_check_method=pass_all + ;; + +bsdi[45]*) + lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [ML]SB (shared object|dynamic lib)' + lt_cv_file_magic_cmd='/usr/bin/file -L' + lt_cv_file_magic_test_file=/shlib/libc.so + ;; + +cygwin*) + # func_win32_libid is a shell function defined in ltmain.sh + lt_cv_deplibs_check_method='file_magic ^x86 archive import|^x86 DLL' + lt_cv_file_magic_cmd='func_win32_libid' + ;; + +mingw* | pw32*) + # Base MSYS/MinGW do not provide the 'file' command needed by + # func_win32_libid shell function, so use a weaker test based on 'objdump', + # unless we find 'file', for example because we are cross-compiling. + # func_win32_libid assumes BSD nm, so disallow it if using MS dumpbin. + if ( test "$lt_cv_nm_interface" = "BSD nm" && file / ) >/dev/null 2>&1; then + lt_cv_deplibs_check_method='file_magic ^x86 archive import|^x86 DLL' + lt_cv_file_magic_cmd='func_win32_libid' + else + # Keep this pattern in sync with the one in func_win32_libid. + lt_cv_deplibs_check_method='file_magic file format (pei*-i386(.*architecture: i386)?|pe-arm-wince|pe-x86-64)' + lt_cv_file_magic_cmd='$OBJDUMP -f' + fi + ;; + +cegcc*) + # use the weaker test based on 'objdump'. See mingw*. + lt_cv_deplibs_check_method='file_magic file format pe-arm-.*little(.*architecture: arm)?' + lt_cv_file_magic_cmd='$OBJDUMP -f' + ;; + +darwin* | rhapsody*) + lt_cv_deplibs_check_method=pass_all + ;; + +freebsd* | dragonfly*) + if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then + case $host_cpu in + i*86 ) + # Not sure whether the presence of OpenBSD here was a mistake. + # Let's accept both of them until this is cleared up. + lt_cv_deplibs_check_method='file_magic (FreeBSD|OpenBSD|DragonFly)/i[3-9]86 (compact )?demand paged shared library' + lt_cv_file_magic_cmd=/usr/bin/file + lt_cv_file_magic_test_file=`echo /usr/lib/libc.so.*` + ;; + esac + else + lt_cv_deplibs_check_method=pass_all + fi + ;; + +haiku*) + lt_cv_deplibs_check_method=pass_all + ;; + +hpux10.20* | hpux11*) + lt_cv_file_magic_cmd=/usr/bin/file + case $host_cpu in + ia64*) + lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|ELF-[0-9][0-9]) shared object file - IA64' + lt_cv_file_magic_test_file=/usr/lib/hpux32/libc.so + ;; + hppa*64*) + lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|ELF[ -][0-9][0-9])(-bit)?( [LM]SB)? shared object( file)?[, -]* PA-RISC [0-9]\.[0-9]' + lt_cv_file_magic_test_file=/usr/lib/pa20_64/libc.sl + ;; + *) + lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|PA-RISC[0-9]\.[0-9]) shared library' + lt_cv_file_magic_test_file=/usr/lib/libc.sl + ;; + esac + ;; + +interix[3-9]*) + # PIC code is broken on Interix 3.x, that's why |\.a not |_pic\.a here + lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so|\.a)$' + ;; + +irix5* | irix6* | nonstopux*) + case $LD in + *-32|*"-32 ") libmagic=32-bit;; + *-n32|*"-n32 ") libmagic=N32;; + *-64|*"-64 ") libmagic=64-bit;; + *) libmagic=never-match;; + esac + lt_cv_deplibs_check_method=pass_all + ;; + +# This must be glibc/ELF. +linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) + lt_cv_deplibs_check_method=pass_all + ;; + +netbsd* | netbsdelf*-gnu) + if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then + lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so\.[0-9]+\.[0-9]+|_pic\.a)$' + else + lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so|_pic\.a)$' + fi + ;; + +newos6*) + lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [ML]SB (executable|dynamic lib)' + lt_cv_file_magic_cmd=/usr/bin/file + lt_cv_file_magic_test_file=/usr/lib/libnls.so + ;; + +*nto* | *qnx*) + lt_cv_deplibs_check_method=pass_all + ;; + +openbsd*) + if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then + lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so\.[0-9]+\.[0-9]+|\.so|_pic\.a)$' + else + lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so\.[0-9]+\.[0-9]+|_pic\.a)$' + fi + ;; + +osf3* | osf4* | osf5*) + lt_cv_deplibs_check_method=pass_all + ;; + +rdos*) + lt_cv_deplibs_check_method=pass_all + ;; + +solaris*) + lt_cv_deplibs_check_method=pass_all + ;; + +sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*) + lt_cv_deplibs_check_method=pass_all + ;; + +sysv4 | sysv4.3*) + case $host_vendor in + motorola) + lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [ML]SB (shared object|dynamic lib) M[0-9][0-9]* Version [0-9]' + lt_cv_file_magic_test_file=`echo /usr/lib/libc.so*` + ;; + ncr) + lt_cv_deplibs_check_method=pass_all + ;; + sequent) + lt_cv_file_magic_cmd='/bin/file' + lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [LM]SB (shared object|dynamic lib )' + ;; + sni) + lt_cv_file_magic_cmd='/bin/file' + lt_cv_deplibs_check_method="file_magic ELF [0-9][0-9]*-bit [LM]SB dynamic lib" + lt_cv_file_magic_test_file=/lib/libc.so + ;; + siemens) + lt_cv_deplibs_check_method=pass_all + ;; + pc) + lt_cv_deplibs_check_method=pass_all + ;; + esac + ;; + +tpf*) + lt_cv_deplibs_check_method=pass_all + ;; +esac + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_deplibs_check_method" >&5 +$as_echo "$lt_cv_deplibs_check_method" >&6; } + +file_magic_glob= +want_nocaseglob=no +if test "$build" = "$host"; then + case $host_os in + mingw* | pw32*) + if ( shopt | grep nocaseglob ) >/dev/null 2>&1; then + want_nocaseglob=yes + else + file_magic_glob=`echo aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ | $SED -e "s/\(..\)/s\/[\1]\/[\1]\/g;/g"` + fi + ;; + esac +fi + +file_magic_cmd=$lt_cv_file_magic_cmd +deplibs_check_method=$lt_cv_deplibs_check_method +test -z "$deplibs_check_method" && deplibs_check_method=unknown + + + + + + + + + + + + + + + + + + + + + + +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}dlltool", so it can be a program name with args. +set dummy ${ac_tool_prefix}dlltool; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_DLLTOOL+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$DLLTOOL"; then + ac_cv_prog_DLLTOOL="$DLLTOOL" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_DLLTOOL="${ac_tool_prefix}dlltool" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +DLLTOOL=$ac_cv_prog_DLLTOOL +if test -n "$DLLTOOL"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $DLLTOOL" >&5 +$as_echo "$DLLTOOL" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_DLLTOOL"; then + ac_ct_DLLTOOL=$DLLTOOL + # Extract the first word of "dlltool", so it can be a program name with args. +set dummy dlltool; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_DLLTOOL+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_DLLTOOL"; then + ac_cv_prog_ac_ct_DLLTOOL="$ac_ct_DLLTOOL" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_DLLTOOL="dlltool" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_DLLTOOL=$ac_cv_prog_ac_ct_DLLTOOL +if test -n "$ac_ct_DLLTOOL"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_DLLTOOL" >&5 +$as_echo "$ac_ct_DLLTOOL" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_DLLTOOL" = x; then + DLLTOOL="false" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + DLLTOOL=$ac_ct_DLLTOOL + fi +else + DLLTOOL="$ac_cv_prog_DLLTOOL" +fi + +test -z "$DLLTOOL" && DLLTOOL=dlltool + + + + + + + + + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to associate runtime and link libraries" >&5 +$as_echo_n "checking how to associate runtime and link libraries... " >&6; } +if ${lt_cv_sharedlib_from_linklib_cmd+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_sharedlib_from_linklib_cmd='unknown' + +case $host_os in +cygwin* | mingw* | pw32* | cegcc*) + # two different shell functions defined in ltmain.sh + # decide which to use based on capabilities of $DLLTOOL + case `$DLLTOOL --help 2>&1` in + *--identify-strict*) + lt_cv_sharedlib_from_linklib_cmd=func_cygming_dll_for_implib + ;; + *) + lt_cv_sharedlib_from_linklib_cmd=func_cygming_dll_for_implib_fallback + ;; + esac + ;; +*) + # fallback: assume linklib IS sharedlib + lt_cv_sharedlib_from_linklib_cmd="$ECHO" + ;; +esac + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_sharedlib_from_linklib_cmd" >&5 +$as_echo "$lt_cv_sharedlib_from_linklib_cmd" >&6; } +sharedlib_from_linklib_cmd=$lt_cv_sharedlib_from_linklib_cmd +test -z "$sharedlib_from_linklib_cmd" && sharedlib_from_linklib_cmd=$ECHO + + + + + + + +if test -n "$ac_tool_prefix"; then + for ac_prog in ar + do + # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. +set dummy $ac_tool_prefix$ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_AR+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$AR"; then + ac_cv_prog_AR="$AR" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_AR="$ac_tool_prefix$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +AR=$ac_cv_prog_AR +if test -n "$AR"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $AR" >&5 +$as_echo "$AR" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$AR" && break + done +fi +if test -z "$AR"; then + ac_ct_AR=$AR + for ac_prog in ar +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_AR+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_AR"; then + ac_cv_prog_ac_ct_AR="$ac_ct_AR" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_AR="$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_AR=$ac_cv_prog_ac_ct_AR +if test -n "$ac_ct_AR"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_AR" >&5 +$as_echo "$ac_ct_AR" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$ac_ct_AR" && break +done + + if test "x$ac_ct_AR" = x; then + AR="false" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + AR=$ac_ct_AR + fi +fi + +: ${AR=ar} +: ${AR_FLAGS=cru} + + + + + + + + + + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for archiver @FILE support" >&5 +$as_echo_n "checking for archiver @FILE support... " >&6; } +if ${lt_cv_ar_at_file+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_ar_at_file=no + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + echo conftest.$ac_objext > conftest.lst + lt_ar_try='$AR $AR_FLAGS libconftest.a @conftest.lst >&5' + { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$lt_ar_try\""; } >&5 + (eval $lt_ar_try) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } + if test "$ac_status" -eq 0; then + # Ensure the archiver fails upon bogus file names. + rm -f conftest.$ac_objext libconftest.a + { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$lt_ar_try\""; } >&5 + (eval $lt_ar_try) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } + if test "$ac_status" -ne 0; then + lt_cv_ar_at_file=@ + fi + fi + rm -f conftest.* libconftest.a + +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_ar_at_file" >&5 +$as_echo "$lt_cv_ar_at_file" >&6; } + +if test "x$lt_cv_ar_at_file" = xno; then + archiver_list_spec= +else + archiver_list_spec=$lt_cv_ar_at_file +fi + + + + + + + +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}strip", so it can be a program name with args. +set dummy ${ac_tool_prefix}strip; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_STRIP+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$STRIP"; then + ac_cv_prog_STRIP="$STRIP" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_STRIP="${ac_tool_prefix}strip" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +STRIP=$ac_cv_prog_STRIP +if test -n "$STRIP"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $STRIP" >&5 +$as_echo "$STRIP" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_STRIP"; then + ac_ct_STRIP=$STRIP + # Extract the first word of "strip", so it can be a program name with args. +set dummy strip; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_STRIP+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_STRIP"; then + ac_cv_prog_ac_ct_STRIP="$ac_ct_STRIP" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_STRIP="strip" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_STRIP=$ac_cv_prog_ac_ct_STRIP +if test -n "$ac_ct_STRIP"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_STRIP" >&5 +$as_echo "$ac_ct_STRIP" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_STRIP" = x; then + STRIP=":" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + STRIP=$ac_ct_STRIP + fi +else + STRIP="$ac_cv_prog_STRIP" +fi + +test -z "$STRIP" && STRIP=: + + + + + + +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}ranlib", so it can be a program name with args. +set dummy ${ac_tool_prefix}ranlib; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_RANLIB+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$RANLIB"; then + ac_cv_prog_RANLIB="$RANLIB" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_RANLIB="${ac_tool_prefix}ranlib" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +RANLIB=$ac_cv_prog_RANLIB +if test -n "$RANLIB"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $RANLIB" >&5 +$as_echo "$RANLIB" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_RANLIB"; then + ac_ct_RANLIB=$RANLIB + # Extract the first word of "ranlib", so it can be a program name with args. +set dummy ranlib; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_RANLIB+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_RANLIB"; then + ac_cv_prog_ac_ct_RANLIB="$ac_ct_RANLIB" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_RANLIB="ranlib" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_RANLIB=$ac_cv_prog_ac_ct_RANLIB +if test -n "$ac_ct_RANLIB"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_RANLIB" >&5 +$as_echo "$ac_ct_RANLIB" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_RANLIB" = x; then + RANLIB=":" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + RANLIB=$ac_ct_RANLIB + fi +else + RANLIB="$ac_cv_prog_RANLIB" +fi + +test -z "$RANLIB" && RANLIB=: + + + + + + +# Determine commands to create old-style static archives. +old_archive_cmds='$AR $AR_FLAGS $oldlib$oldobjs' +old_postinstall_cmds='chmod 644 $oldlib' +old_postuninstall_cmds= + +if test -n "$RANLIB"; then + case $host_os in + openbsd*) + old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB -t \$tool_oldlib" + ;; + *) + old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB \$tool_oldlib" + ;; + esac + old_archive_cmds="$old_archive_cmds~\$RANLIB \$tool_oldlib" +fi + +case $host_os in + darwin*) + lock_old_archive_extraction=yes ;; + *) + lock_old_archive_extraction=no ;; +esac + + + + + + + + + + + + + + + + + + + + + +for ac_prog in gawk mawk nawk awk +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_AWK+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$AWK"; then + ac_cv_prog_AWK="$AWK" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_AWK="$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +AWK=$ac_cv_prog_AWK +if test -n "$AWK"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $AWK" >&5 +$as_echo "$AWK" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$AWK" && break +done + + + + + + + + + + + + + + + + + + + +# If no C compiler was specified, use CC. +LTCC=${LTCC-"$CC"} + +# If no C compiler flags were specified, use CFLAGS. +LTCFLAGS=${LTCFLAGS-"$CFLAGS"} + +# Allow CC to be a program name with arguments. +compiler=$CC + + +# Check for command to grab the raw symbol name followed by C symbol from nm. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking command to parse $NM output from $compiler object" >&5 +$as_echo_n "checking command to parse $NM output from $compiler object... " >&6; } +if ${lt_cv_sys_global_symbol_pipe+:} false; then : + $as_echo_n "(cached) " >&6 +else + +# These are sane defaults that work on at least a few old systems. +# [They come from Ultrix. What could be older than Ultrix?!! ;)] + +# Character class describing NM global symbol codes. +symcode='[BCDEGRST]' + +# Regexp to match symbols that can be accessed directly from C. +sympat='\([_A-Za-z][_A-Za-z0-9]*\)' + +# Define system-specific variables. +case $host_os in +aix*) + symcode='[BCDT]' + ;; +cygwin* | mingw* | pw32* | cegcc*) + symcode='[ABCDGISTW]' + ;; +hpux*) + if test "$host_cpu" = ia64; then + symcode='[ABCDEGRST]' + fi + ;; +irix* | nonstopux*) + symcode='[BCDEGRST]' + ;; +osf*) + symcode='[BCDEGQRST]' + ;; +solaris*) + symcode='[BDRT]' + ;; +sco3.2v5*) + symcode='[DT]' + ;; +sysv4.2uw2*) + symcode='[DT]' + ;; +sysv5* | sco5v6* | unixware* | OpenUNIX*) + symcode='[ABDT]' + ;; +sysv4) + symcode='[DFNSTU]' + ;; +esac + +# If we're using GNU nm, then use its standard symbol codes. +case `$NM -V 2>&1` in +*GNU* | *'with BFD'*) + symcode='[ABCDGIRSTW]' ;; +esac + +# Transform an extracted symbol line into a proper C declaration. +# Some systems (esp. on ia64) link data and code symbols differently, +# so use this general approach. +lt_cv_sys_global_symbol_to_cdecl="sed -n -e 's/^T .* \(.*\)$/extern int \1();/p' -e 's/^$symcode* .* \(.*\)$/extern char \1;/p'" + +# Transform an extracted symbol line into symbol name and symbol address +lt_cv_sys_global_symbol_to_c_name_address="sed -n -e 's/^: \([^ ]*\)[ ]*$/ {\\\"\1\\\", (void *) 0},/p' -e 's/^$symcode* \([^ ]*\) \([^ ]*\)$/ {\"\2\", (void *) \&\2},/p'" +lt_cv_sys_global_symbol_to_c_name_address_lib_prefix="sed -n -e 's/^: \([^ ]*\)[ ]*$/ {\\\"\1\\\", (void *) 0},/p' -e 's/^$symcode* \([^ ]*\) \(lib[^ ]*\)$/ {\"\2\", (void *) \&\2},/p' -e 's/^$symcode* \([^ ]*\) \([^ ]*\)$/ {\"lib\2\", (void *) \&\2},/p'" + +# Handle CRLF in mingw tool chain +opt_cr= +case $build_os in +mingw*) + opt_cr=`$ECHO 'x\{0,1\}' | tr x '\015'` # option cr in regexp + ;; +esac + +# Try without a prefix underscore, then with it. +for ac_symprfx in "" "_"; do + + # Transform symcode, sympat, and symprfx into a raw symbol and a C symbol. + symxfrm="\\1 $ac_symprfx\\2 \\2" + + # Write the raw and C identifiers. + if test "$lt_cv_nm_interface" = "MS dumpbin"; then + # Fake it for dumpbin and say T for any non-static function + # and D for any global variable. + # Also find C++ and __fastcall symbols from MSVC++, + # which start with @ or ?. + lt_cv_sys_global_symbol_pipe="$AWK '"\ +" {last_section=section; section=\$ 3};"\ +" /^COFF SYMBOL TABLE/{for(i in hide) delete hide[i]};"\ +" /Section length .*#relocs.*(pick any)/{hide[last_section]=1};"\ +" \$ 0!~/External *\|/{next};"\ +" / 0+ UNDEF /{next}; / UNDEF \([^|]\)*()/{next};"\ +" {if(hide[section]) next};"\ +" {f=0}; \$ 0~/\(\).*\|/{f=1}; {printf f ? \"T \" : \"D \"};"\ +" {split(\$ 0, a, /\||\r/); split(a[2], s)};"\ +" s[1]~/^[@?]/{print s[1], s[1]; next};"\ +" s[1]~prfx {split(s[1],t,\"@\"); print t[1], substr(t[1],length(prfx))}"\ +" ' prfx=^$ac_symprfx" + else + lt_cv_sys_global_symbol_pipe="sed -n -e 's/^.*[ ]\($symcode$symcode*\)[ ][ ]*$ac_symprfx$sympat$opt_cr$/$symxfrm/p'" + fi + lt_cv_sys_global_symbol_pipe="$lt_cv_sys_global_symbol_pipe | sed '/ __gnu_lto/d'" + + # Check to see that the pipe works correctly. + pipe_works=no + + rm -f conftest* + cat > conftest.$ac_ext <<_LT_EOF +#ifdef __cplusplus +extern "C" { +#endif +char nm_test_var; +void nm_test_func(void); +void nm_test_func(void){} +#ifdef __cplusplus +} +#endif +int main(){nm_test_var='a';nm_test_func();return(0);} +_LT_EOF + + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then + # Now try to grab the symbols. + nlist=conftest.nm + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$NM conftest.$ac_objext \| "$lt_cv_sys_global_symbol_pipe" \> $nlist\""; } >&5 + (eval $NM conftest.$ac_objext \| "$lt_cv_sys_global_symbol_pipe" \> $nlist) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && test -s "$nlist"; then + # Try sorting and uniquifying the output. + if sort "$nlist" | uniq > "$nlist"T; then + mv -f "$nlist"T "$nlist" + else + rm -f "$nlist"T + fi + + # Make sure that we snagged all the symbols we need. + if $GREP ' nm_test_var$' "$nlist" >/dev/null; then + if $GREP ' nm_test_func$' "$nlist" >/dev/null; then + cat <<_LT_EOF > conftest.$ac_ext +/* Keep this code in sync between libtool.m4, ltmain, lt_system.h, and tests. */ +#if defined(_WIN32) || defined(__CYGWIN__) || defined(_WIN32_WCE) +/* DATA imports from DLLs on WIN32 con't be const, because runtime + relocations are performed -- see ld's documentation on pseudo-relocs. */ +# define LT_DLSYM_CONST +#elif defined(__osf__) +/* This system does not cope well with relocations in const data. */ +# define LT_DLSYM_CONST +#else +# define LT_DLSYM_CONST const +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +_LT_EOF + # Now generate the symbol file. + eval "$lt_cv_sys_global_symbol_to_cdecl"' < "$nlist" | $GREP -v main >> conftest.$ac_ext' + + cat <<_LT_EOF >> conftest.$ac_ext + +/* The mapping between symbol names and symbols. */ +LT_DLSYM_CONST struct { + const char *name; + void *address; +} +lt__PROGRAM__LTX_preloaded_symbols[] = +{ + { "@PROGRAM@", (void *) 0 }, +_LT_EOF + $SED "s/^$symcode$symcode* \(.*\) \(.*\)$/ {\"\2\", (void *) \&\2},/" < "$nlist" | $GREP -v main >> conftest.$ac_ext + cat <<\_LT_EOF >> conftest.$ac_ext + {0, (void *) 0} +}; + +/* This works around a problem in FreeBSD linker */ +#ifdef FREEBSD_WORKAROUND +static const void *lt_preloaded_setup() { + return lt__PROGRAM__LTX_preloaded_symbols; +} +#endif + +#ifdef __cplusplus +} +#endif +_LT_EOF + # Now try linking the two files. + mv conftest.$ac_objext conftstm.$ac_objext + lt_globsym_save_LIBS=$LIBS + lt_globsym_save_CFLAGS=$CFLAGS + LIBS="conftstm.$ac_objext" + CFLAGS="$CFLAGS$lt_prog_compiler_no_builtin_flag" + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_link\""; } >&5 + (eval $ac_link) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && test -s conftest${ac_exeext}; then + pipe_works=yes + fi + LIBS=$lt_globsym_save_LIBS + CFLAGS=$lt_globsym_save_CFLAGS + else + echo "cannot find nm_test_func in $nlist" >&5 + fi + else + echo "cannot find nm_test_var in $nlist" >&5 + fi + else + echo "cannot run $lt_cv_sys_global_symbol_pipe" >&5 + fi + else + echo "$progname: failed program was:" >&5 + cat conftest.$ac_ext >&5 + fi + rm -rf conftest* conftst* + + # Do not use the global_symbol_pipe unless it works. + if test "$pipe_works" = yes; then + break + else + lt_cv_sys_global_symbol_pipe= + fi +done + +fi + +if test -z "$lt_cv_sys_global_symbol_pipe"; then + lt_cv_sys_global_symbol_to_cdecl= +fi +if test -z "$lt_cv_sys_global_symbol_pipe$lt_cv_sys_global_symbol_to_cdecl"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: failed" >&5 +$as_echo "failed" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: ok" >&5 +$as_echo "ok" >&6; } +fi + +# Response file support. +if test "$lt_cv_nm_interface" = "MS dumpbin"; then + nm_file_list_spec='@' +elif $NM --help 2>/dev/null | grep '[@]FILE' >/dev/null; then + nm_file_list_spec='@' +fi + + + + + + + + + + + + + + + + + + + + + + + + + + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for sysroot" >&5 +$as_echo_n "checking for sysroot... " >&6; } + +# Check whether --with-sysroot was given. +if test "${with_sysroot+set}" = set; then : + withval=$with_sysroot; +else + with_sysroot=no +fi + + +lt_sysroot= +case ${with_sysroot} in #( + yes) + if test "$GCC" = yes; then + lt_sysroot=`$CC --print-sysroot 2>/dev/null` + fi + ;; #( + /*) + lt_sysroot=`echo "$with_sysroot" | sed -e "$sed_quote_subst"` + ;; #( + no|'') + ;; #( + *) + { $as_echo "$as_me:${as_lineno-$LINENO}: result: ${with_sysroot}" >&5 +$as_echo "${with_sysroot}" >&6; } + as_fn_error $? "The sysroot must be an absolute path." "$LINENO" 5 + ;; +esac + + { $as_echo "$as_me:${as_lineno-$LINENO}: result: ${lt_sysroot:-no}" >&5 +$as_echo "${lt_sysroot:-no}" >&6; } + + + + + +# Check whether --enable-libtool-lock was given. +if test "${enable_libtool_lock+set}" = set; then : + enableval=$enable_libtool_lock; +fi + +test "x$enable_libtool_lock" != xno && enable_libtool_lock=yes + +# Some flags need to be propagated to the compiler or linker for good +# libtool support. +case $host in +ia64-*-hpux*) + # Find out which ABI we are using. + echo 'int i;' > conftest.$ac_ext + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then + case `/usr/bin/file conftest.$ac_objext` in + *ELF-32*) + HPUX_IA64_MODE="32" + ;; + *ELF-64*) + HPUX_IA64_MODE="64" + ;; + esac + fi + rm -rf conftest* + ;; +*-*-irix6*) + # Find out which ABI we are using. + echo '#line '$LINENO' "configure"' > conftest.$ac_ext + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then + if test "$lt_cv_prog_gnu_ld" = yes; then + case `/usr/bin/file conftest.$ac_objext` in + *32-bit*) + LD="${LD-ld} -melf32bsmip" + ;; + *N32*) + LD="${LD-ld} -melf32bmipn32" + ;; + *64-bit*) + LD="${LD-ld} -melf64bmip" + ;; + esac + else + case `/usr/bin/file conftest.$ac_objext` in + *32-bit*) + LD="${LD-ld} -32" + ;; + *N32*) + LD="${LD-ld} -n32" + ;; + *64-bit*) + LD="${LD-ld} -64" + ;; + esac + fi + fi + rm -rf conftest* + ;; + +x86_64-*kfreebsd*-gnu|x86_64-*linux*|powerpc*-*linux*| \ +s390*-*linux*|s390*-*tpf*|sparc*-*linux*) + # Find out which ABI we are using. + echo 'int i;' > conftest.$ac_ext + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then + case `/usr/bin/file conftest.o` in + *32-bit*) + case $host in + x86_64-*kfreebsd*-gnu) + LD="${LD-ld} -m elf_i386_fbsd" + ;; + x86_64-*linux*) + case `/usr/bin/file conftest.o` in + *x86-64*) + LD="${LD-ld} -m elf32_x86_64" + ;; + *) + LD="${LD-ld} -m elf_i386" + ;; + esac + ;; + powerpc64le-*) + LD="${LD-ld} -m elf32lppclinux" + ;; + powerpc64-*) + LD="${LD-ld} -m elf32ppclinux" + ;; + s390x-*linux*) + LD="${LD-ld} -m elf_s390" + ;; + sparc64-*linux*) + LD="${LD-ld} -m elf32_sparc" + ;; + esac + ;; + *64-bit*) + case $host in + x86_64-*kfreebsd*-gnu) + LD="${LD-ld} -m elf_x86_64_fbsd" + ;; + x86_64-*linux*) + LD="${LD-ld} -m elf_x86_64" + ;; + powerpcle-*) + LD="${LD-ld} -m elf64lppc" + ;; + powerpc-*) + LD="${LD-ld} -m elf64ppc" + ;; + s390*-*linux*|s390*-*tpf*) + LD="${LD-ld} -m elf64_s390" + ;; + sparc*-*linux*) + LD="${LD-ld} -m elf64_sparc" + ;; + esac + ;; + esac + fi + rm -rf conftest* + ;; + +*-*-sco3.2v5*) + # On SCO OpenServer 5, we need -belf to get full-featured binaries. + SAVE_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS -belf" + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C compiler needs -belf" >&5 +$as_echo_n "checking whether the C compiler needs -belf... " >&6; } +if ${lt_cv_cc_needs_belf+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + lt_cv_cc_needs_belf=yes +else + lt_cv_cc_needs_belf=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_cc_needs_belf" >&5 +$as_echo "$lt_cv_cc_needs_belf" >&6; } + if test x"$lt_cv_cc_needs_belf" != x"yes"; then + # this is probably gcc 2.8.0, egcs 1.0 or newer; no need for -belf + CFLAGS="$SAVE_CFLAGS" + fi + ;; +*-*solaris*) + # Find out which ABI we are using. + echo 'int i;' > conftest.$ac_ext + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then + case `/usr/bin/file conftest.o` in + *64-bit*) + case $lt_cv_prog_gnu_ld in + yes*) + case $host in + i?86-*-solaris*) + LD="${LD-ld} -m elf_x86_64" + ;; + sparc*-*-solaris*) + LD="${LD-ld} -m elf64_sparc" + ;; + esac + # GNU ld 2.21 introduced _sol2 emulations. Use them if available. + if ${LD-ld} -V | grep _sol2 >/dev/null 2>&1; then + LD="${LD-ld}_sol2" + fi + ;; + *) + if ${LD-ld} -64 -r -o conftest2.o conftest.o >/dev/null 2>&1; then + LD="${LD-ld} -64" + fi + ;; + esac + ;; + esac + fi + rm -rf conftest* + ;; +esac + +need_locks="$enable_libtool_lock" + +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}mt", so it can be a program name with args. +set dummy ${ac_tool_prefix}mt; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_MANIFEST_TOOL+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$MANIFEST_TOOL"; then + ac_cv_prog_MANIFEST_TOOL="$MANIFEST_TOOL" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_MANIFEST_TOOL="${ac_tool_prefix}mt" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +MANIFEST_TOOL=$ac_cv_prog_MANIFEST_TOOL +if test -n "$MANIFEST_TOOL"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $MANIFEST_TOOL" >&5 +$as_echo "$MANIFEST_TOOL" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_MANIFEST_TOOL"; then + ac_ct_MANIFEST_TOOL=$MANIFEST_TOOL + # Extract the first word of "mt", so it can be a program name with args. +set dummy mt; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_MANIFEST_TOOL+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_MANIFEST_TOOL"; then + ac_cv_prog_ac_ct_MANIFEST_TOOL="$ac_ct_MANIFEST_TOOL" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_MANIFEST_TOOL="mt" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_MANIFEST_TOOL=$ac_cv_prog_ac_ct_MANIFEST_TOOL +if test -n "$ac_ct_MANIFEST_TOOL"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_MANIFEST_TOOL" >&5 +$as_echo "$ac_ct_MANIFEST_TOOL" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_MANIFEST_TOOL" = x; then + MANIFEST_TOOL=":" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + MANIFEST_TOOL=$ac_ct_MANIFEST_TOOL + fi +else + MANIFEST_TOOL="$ac_cv_prog_MANIFEST_TOOL" +fi + +test -z "$MANIFEST_TOOL" && MANIFEST_TOOL=mt +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if $MANIFEST_TOOL is a manifest tool" >&5 +$as_echo_n "checking if $MANIFEST_TOOL is a manifest tool... " >&6; } +if ${lt_cv_path_mainfest_tool+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_path_mainfest_tool=no + echo "$as_me:$LINENO: $MANIFEST_TOOL '-?'" >&5 + $MANIFEST_TOOL '-?' 2>conftest.err > conftest.out + cat conftest.err >&5 + if $GREP 'Manifest Tool' conftest.out > /dev/null; then + lt_cv_path_mainfest_tool=yes + fi + rm -f conftest* +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_path_mainfest_tool" >&5 +$as_echo "$lt_cv_path_mainfest_tool" >&6; } +if test "x$lt_cv_path_mainfest_tool" != xyes; then + MANIFEST_TOOL=: +fi + + + + + + + case $host_os in + rhapsody* | darwin*) + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}dsymutil", so it can be a program name with args. +set dummy ${ac_tool_prefix}dsymutil; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_DSYMUTIL+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$DSYMUTIL"; then + ac_cv_prog_DSYMUTIL="$DSYMUTIL" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_DSYMUTIL="${ac_tool_prefix}dsymutil" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +DSYMUTIL=$ac_cv_prog_DSYMUTIL +if test -n "$DSYMUTIL"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $DSYMUTIL" >&5 +$as_echo "$DSYMUTIL" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_DSYMUTIL"; then + ac_ct_DSYMUTIL=$DSYMUTIL + # Extract the first word of "dsymutil", so it can be a program name with args. +set dummy dsymutil; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_DSYMUTIL+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_DSYMUTIL"; then + ac_cv_prog_ac_ct_DSYMUTIL="$ac_ct_DSYMUTIL" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_DSYMUTIL="dsymutil" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_DSYMUTIL=$ac_cv_prog_ac_ct_DSYMUTIL +if test -n "$ac_ct_DSYMUTIL"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_DSYMUTIL" >&5 +$as_echo "$ac_ct_DSYMUTIL" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_DSYMUTIL" = x; then + DSYMUTIL=":" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + DSYMUTIL=$ac_ct_DSYMUTIL + fi +else + DSYMUTIL="$ac_cv_prog_DSYMUTIL" +fi + + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}nmedit", so it can be a program name with args. +set dummy ${ac_tool_prefix}nmedit; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_NMEDIT+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$NMEDIT"; then + ac_cv_prog_NMEDIT="$NMEDIT" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_NMEDIT="${ac_tool_prefix}nmedit" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +NMEDIT=$ac_cv_prog_NMEDIT +if test -n "$NMEDIT"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $NMEDIT" >&5 +$as_echo "$NMEDIT" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_NMEDIT"; then + ac_ct_NMEDIT=$NMEDIT + # Extract the first word of "nmedit", so it can be a program name with args. +set dummy nmedit; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_NMEDIT+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_NMEDIT"; then + ac_cv_prog_ac_ct_NMEDIT="$ac_ct_NMEDIT" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_NMEDIT="nmedit" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_NMEDIT=$ac_cv_prog_ac_ct_NMEDIT +if test -n "$ac_ct_NMEDIT"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_NMEDIT" >&5 +$as_echo "$ac_ct_NMEDIT" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_NMEDIT" = x; then + NMEDIT=":" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + NMEDIT=$ac_ct_NMEDIT + fi +else + NMEDIT="$ac_cv_prog_NMEDIT" +fi + + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}lipo", so it can be a program name with args. +set dummy ${ac_tool_prefix}lipo; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_LIPO+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$LIPO"; then + ac_cv_prog_LIPO="$LIPO" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_LIPO="${ac_tool_prefix}lipo" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +LIPO=$ac_cv_prog_LIPO +if test -n "$LIPO"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $LIPO" >&5 +$as_echo "$LIPO" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_LIPO"; then + ac_ct_LIPO=$LIPO + # Extract the first word of "lipo", so it can be a program name with args. +set dummy lipo; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_LIPO+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_LIPO"; then + ac_cv_prog_ac_ct_LIPO="$ac_ct_LIPO" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_LIPO="lipo" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_LIPO=$ac_cv_prog_ac_ct_LIPO +if test -n "$ac_ct_LIPO"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_LIPO" >&5 +$as_echo "$ac_ct_LIPO" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_LIPO" = x; then + LIPO=":" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + LIPO=$ac_ct_LIPO + fi +else + LIPO="$ac_cv_prog_LIPO" +fi + + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}otool", so it can be a program name with args. +set dummy ${ac_tool_prefix}otool; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_OTOOL+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$OTOOL"; then + ac_cv_prog_OTOOL="$OTOOL" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_OTOOL="${ac_tool_prefix}otool" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +OTOOL=$ac_cv_prog_OTOOL +if test -n "$OTOOL"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $OTOOL" >&5 +$as_echo "$OTOOL" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_OTOOL"; then + ac_ct_OTOOL=$OTOOL + # Extract the first word of "otool", so it can be a program name with args. +set dummy otool; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_OTOOL+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_OTOOL"; then + ac_cv_prog_ac_ct_OTOOL="$ac_ct_OTOOL" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_OTOOL="otool" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_OTOOL=$ac_cv_prog_ac_ct_OTOOL +if test -n "$ac_ct_OTOOL"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_OTOOL" >&5 +$as_echo "$ac_ct_OTOOL" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_OTOOL" = x; then + OTOOL=":" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + OTOOL=$ac_ct_OTOOL + fi +else + OTOOL="$ac_cv_prog_OTOOL" +fi + + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}otool64", so it can be a program name with args. +set dummy ${ac_tool_prefix}otool64; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_OTOOL64+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$OTOOL64"; then + ac_cv_prog_OTOOL64="$OTOOL64" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_OTOOL64="${ac_tool_prefix}otool64" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +OTOOL64=$ac_cv_prog_OTOOL64 +if test -n "$OTOOL64"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $OTOOL64" >&5 +$as_echo "$OTOOL64" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_OTOOL64"; then + ac_ct_OTOOL64=$OTOOL64 + # Extract the first word of "otool64", so it can be a program name with args. +set dummy otool64; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_OTOOL64+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_OTOOL64"; then + ac_cv_prog_ac_ct_OTOOL64="$ac_ct_OTOOL64" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_OTOOL64="otool64" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_OTOOL64=$ac_cv_prog_ac_ct_OTOOL64 +if test -n "$ac_ct_OTOOL64"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_OTOOL64" >&5 +$as_echo "$ac_ct_OTOOL64" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_OTOOL64" = x; then + OTOOL64=":" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + OTOOL64=$ac_ct_OTOOL64 + fi +else + OTOOL64="$ac_cv_prog_OTOOL64" +fi + + + + + + + + + + + + + + + + + + + + + + + + + + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for -single_module linker flag" >&5 +$as_echo_n "checking for -single_module linker flag... " >&6; } +if ${lt_cv_apple_cc_single_mod+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_apple_cc_single_mod=no + if test -z "${LT_MULTI_MODULE}"; then + # By default we will add the -single_module flag. You can override + # by either setting the environment variable LT_MULTI_MODULE + # non-empty at configure time, or by adding -multi_module to the + # link flags. + rm -rf libconftest.dylib* + echo "int foo(void){return 1;}" > conftest.c + echo "$LTCC $LTCFLAGS $LDFLAGS -o libconftest.dylib \ +-dynamiclib -Wl,-single_module conftest.c" >&5 + $LTCC $LTCFLAGS $LDFLAGS -o libconftest.dylib \ + -dynamiclib -Wl,-single_module conftest.c 2>conftest.err + _lt_result=$? + # If there is a non-empty error log, and "single_module" + # appears in it, assume the flag caused a linker warning + if test -s conftest.err && $GREP single_module conftest.err; then + cat conftest.err >&5 + # Otherwise, if the output was created with a 0 exit code from + # the compiler, it worked. + elif test -f libconftest.dylib && test $_lt_result -eq 0; then + lt_cv_apple_cc_single_mod=yes + else + cat conftest.err >&5 + fi + rm -rf libconftest.dylib* + rm -f conftest.* + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_apple_cc_single_mod" >&5 +$as_echo "$lt_cv_apple_cc_single_mod" >&6; } + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for -exported_symbols_list linker flag" >&5 +$as_echo_n "checking for -exported_symbols_list linker flag... " >&6; } +if ${lt_cv_ld_exported_symbols_list+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_ld_exported_symbols_list=no + save_LDFLAGS=$LDFLAGS + echo "_main" > conftest.sym + LDFLAGS="$LDFLAGS -Wl,-exported_symbols_list,conftest.sym" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + lt_cv_ld_exported_symbols_list=yes +else + lt_cv_ld_exported_symbols_list=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + LDFLAGS="$save_LDFLAGS" + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_ld_exported_symbols_list" >&5 +$as_echo "$lt_cv_ld_exported_symbols_list" >&6; } + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for -force_load linker flag" >&5 +$as_echo_n "checking for -force_load linker flag... " >&6; } +if ${lt_cv_ld_force_load+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_ld_force_load=no + cat > conftest.c << _LT_EOF +int forced_loaded() { return 2;} +_LT_EOF + echo "$LTCC $LTCFLAGS -c -o conftest.o conftest.c" >&5 + $LTCC $LTCFLAGS -c -o conftest.o conftest.c 2>&5 + echo "$AR cru libconftest.a conftest.o" >&5 + $AR cru libconftest.a conftest.o 2>&5 + echo "$RANLIB libconftest.a" >&5 + $RANLIB libconftest.a 2>&5 + cat > conftest.c << _LT_EOF +int main() { return 0;} +_LT_EOF + echo "$LTCC $LTCFLAGS $LDFLAGS -o conftest conftest.c -Wl,-force_load,./libconftest.a" >&5 + $LTCC $LTCFLAGS $LDFLAGS -o conftest conftest.c -Wl,-force_load,./libconftest.a 2>conftest.err + _lt_result=$? + if test -s conftest.err && $GREP force_load conftest.err; then + cat conftest.err >&5 + elif test -f conftest && test $_lt_result -eq 0 && $GREP forced_load conftest >/dev/null 2>&1 ; then + lt_cv_ld_force_load=yes + else + cat conftest.err >&5 + fi + rm -f conftest.err libconftest.a conftest conftest.c + rm -rf conftest.dSYM + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_ld_force_load" >&5 +$as_echo "$lt_cv_ld_force_load" >&6; } + case $host_os in + rhapsody* | darwin1.[012]) + _lt_dar_allow_undefined='${wl}-undefined ${wl}suppress' ;; + darwin1.*) + _lt_dar_allow_undefined='${wl}-flat_namespace ${wl}-undefined ${wl}suppress' ;; + darwin*) # darwin 5.x on + # if running on 10.5 or later, the deployment target defaults + # to the OS version, if on x86, and 10.4, the deployment + # target defaults to 10.4. Don't you love it? + case ${MACOSX_DEPLOYMENT_TARGET-10.0},$host in + 10.0,*86*-darwin8*|10.0,*-darwin[91]*) + _lt_dar_allow_undefined='${wl}-undefined ${wl}dynamic_lookup' ;; + 10.[012]*) + _lt_dar_allow_undefined='${wl}-flat_namespace ${wl}-undefined ${wl}suppress' ;; + 10.*) + _lt_dar_allow_undefined='${wl}-undefined ${wl}dynamic_lookup' ;; + esac + ;; + esac + if test "$lt_cv_apple_cc_single_mod" = "yes"; then + _lt_dar_single_mod='$single_module' + fi + if test "$lt_cv_ld_exported_symbols_list" = "yes"; then + _lt_dar_export_syms=' ${wl}-exported_symbols_list,$output_objdir/${libname}-symbols.expsym' + else + _lt_dar_export_syms='~$NMEDIT -s $output_objdir/${libname}-symbols.expsym ${lib}' + fi + if test "$DSYMUTIL" != ":" && test "$lt_cv_ld_force_load" = "no"; then + _lt_dsymutil='~$DSYMUTIL $lib || :' + else + _lt_dsymutil= + fi + ;; + esac + +for ac_header in dlfcn.h +do : + ac_fn_c_check_header_compile "$LINENO" "dlfcn.h" "ac_cv_header_dlfcn_h" "$ac_includes_default +" +if test "x$ac_cv_header_dlfcn_h" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_DLFCN_H 1 +_ACEOF + +fi + +done + + + + + +# Set options + + + + enable_dlopen=no + + + enable_win32_dll=no + + + # Check whether --enable-shared was given. +if test "${enable_shared+set}" = set; then : + enableval=$enable_shared; p=${PACKAGE-default} + case $enableval in + yes) enable_shared=yes ;; + no) enable_shared=no ;; + *) + enable_shared=no + # Look at the argument we got. We use all the common list separators. + lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR," + for pkg in $enableval; do + IFS="$lt_save_ifs" + if test "X$pkg" = "X$p"; then + enable_shared=yes + fi + done + IFS="$lt_save_ifs" + ;; + esac +else + enable_shared=yes +fi + + + + + + + + + + # Check whether --enable-static was given. +if test "${enable_static+set}" = set; then : + enableval=$enable_static; p=${PACKAGE-default} + case $enableval in + yes) enable_static=yes ;; + no) enable_static=no ;; + *) + enable_static=no + # Look at the argument we got. We use all the common list separators. + lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR," + for pkg in $enableval; do + IFS="$lt_save_ifs" + if test "X$pkg" = "X$p"; then + enable_static=yes + fi + done + IFS="$lt_save_ifs" + ;; + esac +else + enable_static=yes +fi + + + + + + + + + + +# Check whether --with-pic was given. +if test "${with_pic+set}" = set; then : + withval=$with_pic; lt_p=${PACKAGE-default} + case $withval in + yes|no) pic_mode=$withval ;; + *) + pic_mode=default + # Look at the argument we got. We use all the common list separators. + lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR," + for lt_pkg in $withval; do + IFS="$lt_save_ifs" + if test "X$lt_pkg" = "X$lt_p"; then + pic_mode=yes + fi + done + IFS="$lt_save_ifs" + ;; + esac +else + pic_mode=default +fi + + +test -z "$pic_mode" && pic_mode=default + + + + + + + + # Check whether --enable-fast-install was given. +if test "${enable_fast_install+set}" = set; then : + enableval=$enable_fast_install; p=${PACKAGE-default} + case $enableval in + yes) enable_fast_install=yes ;; + no) enable_fast_install=no ;; + *) + enable_fast_install=no + # Look at the argument we got. We use all the common list separators. + lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR," + for pkg in $enableval; do + IFS="$lt_save_ifs" + if test "X$pkg" = "X$p"; then + enable_fast_install=yes + fi + done + IFS="$lt_save_ifs" + ;; + esac +else + enable_fast_install=yes +fi + + + + + + + + + + + +# This can be used to rebuild libtool when needed +LIBTOOL_DEPS="$ltmain" + +# Always use our own libtool. +LIBTOOL='$(SHELL) $(top_builddir)/libtool' + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +test -z "$LN_S" && LN_S="ln -s" + + + + + + + + + + + + + + +if test -n "${ZSH_VERSION+set}" ; then + setopt NO_GLOB_SUBST +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for objdir" >&5 +$as_echo_n "checking for objdir... " >&6; } +if ${lt_cv_objdir+:} false; then : + $as_echo_n "(cached) " >&6 +else + rm -f .libs 2>/dev/null +mkdir .libs 2>/dev/null +if test -d .libs; then + lt_cv_objdir=.libs +else + # MS-DOS does not allow filenames that begin with a dot. + lt_cv_objdir=_libs +fi +rmdir .libs 2>/dev/null +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_objdir" >&5 +$as_echo "$lt_cv_objdir" >&6; } +objdir=$lt_cv_objdir + + + + + +cat >>confdefs.h <<_ACEOF +#define LT_OBJDIR "$lt_cv_objdir/" +_ACEOF + + + + +case $host_os in +aix3*) + # AIX sometimes has problems with the GCC collect2 program. For some + # reason, if we set the COLLECT_NAMES environment variable, the problems + # vanish in a puff of smoke. + if test "X${COLLECT_NAMES+set}" != Xset; then + COLLECT_NAMES= + export COLLECT_NAMES + fi + ;; +esac + +# Global variables: +ofile=libtool +can_build_shared=yes + +# All known linkers require a `.a' archive for static linking (except MSVC, +# which needs '.lib'). +libext=a + +with_gnu_ld="$lt_cv_prog_gnu_ld" + +old_CC="$CC" +old_CFLAGS="$CFLAGS" + +# Set sane defaults for various variables +test -z "$CC" && CC=cc +test -z "$LTCC" && LTCC=$CC +test -z "$LTCFLAGS" && LTCFLAGS=$CFLAGS +test -z "$LD" && LD=ld +test -z "$ac_objext" && ac_objext=o + +for cc_temp in $compiler""; do + case $cc_temp in + compile | *[\\/]compile | ccache | *[\\/]ccache ) ;; + distcc | *[\\/]distcc | purify | *[\\/]purify ) ;; + \-*) ;; + *) break;; + esac +done +cc_basename=`$ECHO "$cc_temp" | $SED "s%.*/%%; s%^$host_alias-%%"` + + +# Only perform the check for file, if the check method requires it +test -z "$MAGIC_CMD" && MAGIC_CMD=file +case $deplibs_check_method in +file_magic*) + if test "$file_magic_cmd" = '$MAGIC_CMD'; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ${ac_tool_prefix}file" >&5 +$as_echo_n "checking for ${ac_tool_prefix}file... " >&6; } +if ${lt_cv_path_MAGIC_CMD+:} false; then : + $as_echo_n "(cached) " >&6 +else + case $MAGIC_CMD in +[\\/*] | ?:[\\/]*) + lt_cv_path_MAGIC_CMD="$MAGIC_CMD" # Let the user override the test with a path. + ;; +*) + lt_save_MAGIC_CMD="$MAGIC_CMD" + lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR + ac_dummy="/usr/bin$PATH_SEPARATOR$PATH" + for ac_dir in $ac_dummy; do + IFS="$lt_save_ifs" + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/${ac_tool_prefix}file; then + lt_cv_path_MAGIC_CMD="$ac_dir/${ac_tool_prefix}file" + if test -n "$file_magic_test_file"; then + case $deplibs_check_method in + "file_magic "*) + file_magic_regex=`expr "$deplibs_check_method" : "file_magic \(.*\)"` + MAGIC_CMD="$lt_cv_path_MAGIC_CMD" + if eval $file_magic_cmd \$file_magic_test_file 2> /dev/null | + $EGREP "$file_magic_regex" > /dev/null; then + : + else + cat <<_LT_EOF 1>&2 + +*** Warning: the command libtool uses to detect shared libraries, +*** $file_magic_cmd, produces output that libtool cannot recognize. +*** The result is that libtool may fail to recognize shared libraries +*** as such. This will affect the creation of libtool libraries that +*** depend on shared libraries, but programs linked with such libtool +*** libraries will work regardless of this problem. Nevertheless, you +*** may want to report the problem to your system manager and/or to +*** bug-libtool@gnu.org + +_LT_EOF + fi ;; + esac + fi + break + fi + done + IFS="$lt_save_ifs" + MAGIC_CMD="$lt_save_MAGIC_CMD" + ;; +esac +fi + +MAGIC_CMD="$lt_cv_path_MAGIC_CMD" +if test -n "$MAGIC_CMD"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $MAGIC_CMD" >&5 +$as_echo "$MAGIC_CMD" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + + + +if test -z "$lt_cv_path_MAGIC_CMD"; then + if test -n "$ac_tool_prefix"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for file" >&5 +$as_echo_n "checking for file... " >&6; } +if ${lt_cv_path_MAGIC_CMD+:} false; then : + $as_echo_n "(cached) " >&6 +else + case $MAGIC_CMD in +[\\/*] | ?:[\\/]*) + lt_cv_path_MAGIC_CMD="$MAGIC_CMD" # Let the user override the test with a path. + ;; +*) + lt_save_MAGIC_CMD="$MAGIC_CMD" + lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR + ac_dummy="/usr/bin$PATH_SEPARATOR$PATH" + for ac_dir in $ac_dummy; do + IFS="$lt_save_ifs" + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/file; then + lt_cv_path_MAGIC_CMD="$ac_dir/file" + if test -n "$file_magic_test_file"; then + case $deplibs_check_method in + "file_magic "*) + file_magic_regex=`expr "$deplibs_check_method" : "file_magic \(.*\)"` + MAGIC_CMD="$lt_cv_path_MAGIC_CMD" + if eval $file_magic_cmd \$file_magic_test_file 2> /dev/null | + $EGREP "$file_magic_regex" > /dev/null; then + : + else + cat <<_LT_EOF 1>&2 + +*** Warning: the command libtool uses to detect shared libraries, +*** $file_magic_cmd, produces output that libtool cannot recognize. +*** The result is that libtool may fail to recognize shared libraries +*** as such. This will affect the creation of libtool libraries that +*** depend on shared libraries, but programs linked with such libtool +*** libraries will work regardless of this problem. Nevertheless, you +*** may want to report the problem to your system manager and/or to +*** bug-libtool@gnu.org + +_LT_EOF + fi ;; + esac + fi + break + fi + done + IFS="$lt_save_ifs" + MAGIC_CMD="$lt_save_MAGIC_CMD" + ;; +esac +fi + +MAGIC_CMD="$lt_cv_path_MAGIC_CMD" +if test -n "$MAGIC_CMD"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $MAGIC_CMD" >&5 +$as_echo "$MAGIC_CMD" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + else + MAGIC_CMD=: + fi +fi + + fi + ;; +esac + +# Use C for the default configuration in the libtool script + +lt_save_CC="$CC" +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + +# Source file extension for C test sources. +ac_ext=c + +# Object file extension for compiled C test sources. +objext=o +objext=$objext + +# Code to be used in simple compile tests +lt_simple_compile_test_code="int some_variable = 0;" + +# Code to be used in simple link tests +lt_simple_link_test_code='int main(){return(0);}' + + + + + + + +# If no C compiler was specified, use CC. +LTCC=${LTCC-"$CC"} + +# If no C compiler flags were specified, use CFLAGS. +LTCFLAGS=${LTCFLAGS-"$CFLAGS"} + +# Allow CC to be a program name with arguments. +compiler=$CC + +# Save the default compiler, since it gets overwritten when the other +# tags are being tested, and _LT_TAGVAR(compiler, []) is a NOP. +compiler_DEFAULT=$CC + +# save warnings/boilerplate of simple test code +ac_outfile=conftest.$ac_objext +echo "$lt_simple_compile_test_code" >conftest.$ac_ext +eval "$ac_compile" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err +_lt_compiler_boilerplate=`cat conftest.err` +$RM conftest* + +ac_outfile=conftest.$ac_objext +echo "$lt_simple_link_test_code" >conftest.$ac_ext +eval "$ac_link" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err +_lt_linker_boilerplate=`cat conftest.err` +$RM -r conftest* + + +## CAVEAT EMPTOR: +## There is no encapsulation within the following macros, do not change +## the running order or otherwise move them around unless you know exactly +## what you are doing... +if test -n "$compiler"; then + +lt_prog_compiler_no_builtin_flag= + +if test "$GCC" = yes; then + case $cc_basename in + nvcc*) + lt_prog_compiler_no_builtin_flag=' -Xcompiler -fno-builtin' ;; + *) + lt_prog_compiler_no_builtin_flag=' -fno-builtin' ;; + esac + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler supports -fno-rtti -fno-exceptions" >&5 +$as_echo_n "checking if $compiler supports -fno-rtti -fno-exceptions... " >&6; } +if ${lt_cv_prog_compiler_rtti_exceptions+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_prog_compiler_rtti_exceptions=no + ac_outfile=conftest.$ac_objext + echo "$lt_simple_compile_test_code" > conftest.$ac_ext + lt_compiler_flag="-fno-rtti -fno-exceptions" + # Insert the option either (1) after the last *FLAGS variable, or + # (2) before a word containing "conftest.", or (3) at the end. + # Note that $ac_compile itself does not contain backslashes and begins + # with a dollar sign (not a hyphen), so the echo should work correctly. + # The option is referenced via a variable to avoid confusing sed. + lt_compile=`echo "$ac_compile" | $SED \ + -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ + -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ + -e 's:$: $lt_compiler_flag:'` + (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5) + (eval "$lt_compile" 2>conftest.err) + ac_status=$? + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + if (exit $ac_status) && test -s "$ac_outfile"; then + # The compiler can only warn and ignore the option if not recognized + # So say no if there are warnings other than the usual output. + $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' >conftest.exp + $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 + if test ! -s conftest.er2 || diff conftest.exp conftest.er2 >/dev/null; then + lt_cv_prog_compiler_rtti_exceptions=yes + fi + fi + $RM conftest* + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_rtti_exceptions" >&5 +$as_echo "$lt_cv_prog_compiler_rtti_exceptions" >&6; } + +if test x"$lt_cv_prog_compiler_rtti_exceptions" = xyes; then + lt_prog_compiler_no_builtin_flag="$lt_prog_compiler_no_builtin_flag -fno-rtti -fno-exceptions" +else + : +fi + +fi + + + + + + + lt_prog_compiler_wl= +lt_prog_compiler_pic= +lt_prog_compiler_static= + + + if test "$GCC" = yes; then + lt_prog_compiler_wl='-Wl,' + lt_prog_compiler_static='-static' + + case $host_os in + aix*) + # All AIX code is PIC. + if test "$host_cpu" = ia64; then + # AIX 5 now supports IA64 processor + lt_prog_compiler_static='-Bstatic' + fi + ;; + + amigaos*) + case $host_cpu in + powerpc) + # see comment about AmigaOS4 .so support + lt_prog_compiler_pic='-fPIC' + ;; + m68k) + # FIXME: we need at least 68020 code to build shared libraries, but + # adding the `-m68020' flag to GCC prevents building anything better, + # like `-m68040'. + lt_prog_compiler_pic='-m68020 -resident32 -malways-restore-a4' + ;; + esac + ;; + + beos* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*) + # PIC is the default for these OSes. + ;; + + mingw* | cygwin* | pw32* | os2* | cegcc*) + # This hack is so that the source file can tell whether it is being + # built for inclusion in a dll (and should export symbols for example). + # Although the cygwin gcc ignores -fPIC, still need this for old-style + # (--disable-auto-import) libraries + lt_prog_compiler_pic='-DDLL_EXPORT' + ;; + + darwin* | rhapsody*) + # PIC is the default on this platform + # Common symbols not allowed in MH_DYLIB files + lt_prog_compiler_pic='-fno-common' + ;; + + haiku*) + # PIC is the default for Haiku. + # The "-static" flag exists, but is broken. + lt_prog_compiler_static= + ;; + + hpux*) + # PIC is the default for 64-bit PA HP-UX, but not for 32-bit + # PA HP-UX. On IA64 HP-UX, PIC is the default but the pic flag + # sets the default TLS model and affects inlining. + case $host_cpu in + hppa*64*) + # +Z the default + ;; + *) + lt_prog_compiler_pic='-fPIC' + ;; + esac + ;; + + interix[3-9]*) + # Interix 3.x gcc -fpic/-fPIC options generate broken code. + # Instead, we relocate shared libraries at runtime. + ;; + + msdosdjgpp*) + # Just because we use GCC doesn't mean we suddenly get shared libraries + # on systems that don't support them. + lt_prog_compiler_can_build_shared=no + enable_shared=no + ;; + + *nto* | *qnx*) + # QNX uses GNU C++, but need to define -shared option too, otherwise + # it will coredump. + lt_prog_compiler_pic='-fPIC -shared' + ;; + + sysv4*MP*) + if test -d /usr/nec; then + lt_prog_compiler_pic=-Kconform_pic + fi + ;; + + *) + lt_prog_compiler_pic='-fPIC' + ;; + esac + + case $cc_basename in + nvcc*) # Cuda Compiler Driver 2.2 + lt_prog_compiler_wl='-Xlinker ' + if test -n "$lt_prog_compiler_pic"; then + lt_prog_compiler_pic="-Xcompiler $lt_prog_compiler_pic" + fi + ;; + esac + else + # PORTME Check for flag to pass linker flags through the system compiler. + case $host_os in + aix*) + lt_prog_compiler_wl='-Wl,' + if test "$host_cpu" = ia64; then + # AIX 5 now supports IA64 processor + lt_prog_compiler_static='-Bstatic' + else + lt_prog_compiler_static='-bnso -bI:/lib/syscalls.exp' + fi + ;; + + mingw* | cygwin* | pw32* | os2* | cegcc*) + # This hack is so that the source file can tell whether it is being + # built for inclusion in a dll (and should export symbols for example). + lt_prog_compiler_pic='-DDLL_EXPORT' + ;; + + hpux9* | hpux10* | hpux11*) + lt_prog_compiler_wl='-Wl,' + # PIC is the default for IA64 HP-UX and 64-bit HP-UX, but + # not for PA HP-UX. + case $host_cpu in + hppa*64*|ia64*) + # +Z the default + ;; + *) + lt_prog_compiler_pic='+Z' + ;; + esac + # Is there a better lt_prog_compiler_static that works with the bundled CC? + lt_prog_compiler_static='${wl}-a ${wl}archive' + ;; + + irix5* | irix6* | nonstopux*) + lt_prog_compiler_wl='-Wl,' + # PIC (with -KPIC) is the default. + lt_prog_compiler_static='-non_shared' + ;; + + linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) + case $cc_basename in + # old Intel for x86_64 which still supported -KPIC. + ecc*) + lt_prog_compiler_wl='-Wl,' + lt_prog_compiler_pic='-KPIC' + lt_prog_compiler_static='-static' + ;; + # icc used to be incompatible with GCC. + # ICC 10 doesn't accept -KPIC any more. + icc* | ifort*) + lt_prog_compiler_wl='-Wl,' + lt_prog_compiler_pic='-fPIC' + lt_prog_compiler_static='-static' + ;; + # Lahey Fortran 8.1. + lf95*) + lt_prog_compiler_wl='-Wl,' + lt_prog_compiler_pic='--shared' + lt_prog_compiler_static='--static' + ;; + nagfor*) + # NAG Fortran compiler + lt_prog_compiler_wl='-Wl,-Wl,,' + lt_prog_compiler_pic='-PIC' + lt_prog_compiler_static='-Bstatic' + ;; + pgcc* | pgf77* | pgf90* | pgf95* | pgfortran*) + # Portland Group compilers (*not* the Pentium gcc compiler, + # which looks to be a dead project) + lt_prog_compiler_wl='-Wl,' + lt_prog_compiler_pic='-fpic' + lt_prog_compiler_static='-Bstatic' + ;; + ccc*) + lt_prog_compiler_wl='-Wl,' + # All Alpha code is PIC. + lt_prog_compiler_static='-non_shared' + ;; + xl* | bgxl* | bgf* | mpixl*) + # IBM XL C 8.0/Fortran 10.1, 11.1 on PPC and BlueGene + lt_prog_compiler_wl='-Wl,' + lt_prog_compiler_pic='-qpic' + lt_prog_compiler_static='-qstaticlink' + ;; + *) + case `$CC -V 2>&1 | sed 5q` in + *Sun\ Ceres\ Fortran* | *Sun*Fortran*\ [1-7].* | *Sun*Fortran*\ 8.[0-3]*) + # Sun Fortran 8.3 passes all unrecognized flags to the linker + lt_prog_compiler_pic='-KPIC' + lt_prog_compiler_static='-Bstatic' + lt_prog_compiler_wl='' + ;; + *Sun\ F* | *Sun*Fortran*) + lt_prog_compiler_pic='-KPIC' + lt_prog_compiler_static='-Bstatic' + lt_prog_compiler_wl='-Qoption ld ' + ;; + *Sun\ C*) + # Sun C 5.9 + lt_prog_compiler_pic='-KPIC' + lt_prog_compiler_static='-Bstatic' + lt_prog_compiler_wl='-Wl,' + ;; + *Intel*\ [CF]*Compiler*) + lt_prog_compiler_wl='-Wl,' + lt_prog_compiler_pic='-fPIC' + lt_prog_compiler_static='-static' + ;; + *Portland\ Group*) + lt_prog_compiler_wl='-Wl,' + lt_prog_compiler_pic='-fpic' + lt_prog_compiler_static='-Bstatic' + ;; + esac + ;; + esac + ;; + + newsos6) + lt_prog_compiler_pic='-KPIC' + lt_prog_compiler_static='-Bstatic' + ;; + + *nto* | *qnx*) + # QNX uses GNU C++, but need to define -shared option too, otherwise + # it will coredump. + lt_prog_compiler_pic='-fPIC -shared' + ;; + + osf3* | osf4* | osf5*) + lt_prog_compiler_wl='-Wl,' + # All OSF/1 code is PIC. + lt_prog_compiler_static='-non_shared' + ;; + + rdos*) + lt_prog_compiler_static='-non_shared' + ;; + + solaris*) + lt_prog_compiler_pic='-KPIC' + lt_prog_compiler_static='-Bstatic' + case $cc_basename in + f77* | f90* | f95* | sunf77* | sunf90* | sunf95*) + lt_prog_compiler_wl='-Qoption ld ';; + *) + lt_prog_compiler_wl='-Wl,';; + esac + ;; + + sunos4*) + lt_prog_compiler_wl='-Qoption ld ' + lt_prog_compiler_pic='-PIC' + lt_prog_compiler_static='-Bstatic' + ;; + + sysv4 | sysv4.2uw2* | sysv4.3*) + lt_prog_compiler_wl='-Wl,' + lt_prog_compiler_pic='-KPIC' + lt_prog_compiler_static='-Bstatic' + ;; + + sysv4*MP*) + if test -d /usr/nec ;then + lt_prog_compiler_pic='-Kconform_pic' + lt_prog_compiler_static='-Bstatic' + fi + ;; + + sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*) + lt_prog_compiler_wl='-Wl,' + lt_prog_compiler_pic='-KPIC' + lt_prog_compiler_static='-Bstatic' + ;; + + unicos*) + lt_prog_compiler_wl='-Wl,' + lt_prog_compiler_can_build_shared=no + ;; + + uts4*) + lt_prog_compiler_pic='-pic' + lt_prog_compiler_static='-Bstatic' + ;; + + *) + lt_prog_compiler_can_build_shared=no + ;; + esac + fi + +case $host_os in + # For platforms which do not support PIC, -DPIC is meaningless: + *djgpp*) + lt_prog_compiler_pic= + ;; + *) + lt_prog_compiler_pic="$lt_prog_compiler_pic -DPIC" + ;; +esac + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $compiler option to produce PIC" >&5 +$as_echo_n "checking for $compiler option to produce PIC... " >&6; } +if ${lt_cv_prog_compiler_pic+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_prog_compiler_pic=$lt_prog_compiler_pic +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_pic" >&5 +$as_echo "$lt_cv_prog_compiler_pic" >&6; } +lt_prog_compiler_pic=$lt_cv_prog_compiler_pic + +# +# Check to make sure the PIC flag actually works. +# +if test -n "$lt_prog_compiler_pic"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler PIC flag $lt_prog_compiler_pic works" >&5 +$as_echo_n "checking if $compiler PIC flag $lt_prog_compiler_pic works... " >&6; } +if ${lt_cv_prog_compiler_pic_works+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_prog_compiler_pic_works=no + ac_outfile=conftest.$ac_objext + echo "$lt_simple_compile_test_code" > conftest.$ac_ext + lt_compiler_flag="$lt_prog_compiler_pic -DPIC" + # Insert the option either (1) after the last *FLAGS variable, or + # (2) before a word containing "conftest.", or (3) at the end. + # Note that $ac_compile itself does not contain backslashes and begins + # with a dollar sign (not a hyphen), so the echo should work correctly. + # The option is referenced via a variable to avoid confusing sed. + lt_compile=`echo "$ac_compile" | $SED \ + -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ + -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ + -e 's:$: $lt_compiler_flag:'` + (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5) + (eval "$lt_compile" 2>conftest.err) + ac_status=$? + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + if (exit $ac_status) && test -s "$ac_outfile"; then + # The compiler can only warn and ignore the option if not recognized + # So say no if there are warnings other than the usual output. + $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' >conftest.exp + $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 + if test ! -s conftest.er2 || diff conftest.exp conftest.er2 >/dev/null; then + lt_cv_prog_compiler_pic_works=yes + fi + fi + $RM conftest* + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_pic_works" >&5 +$as_echo "$lt_cv_prog_compiler_pic_works" >&6; } + +if test x"$lt_cv_prog_compiler_pic_works" = xyes; then + case $lt_prog_compiler_pic in + "" | " "*) ;; + *) lt_prog_compiler_pic=" $lt_prog_compiler_pic" ;; + esac +else + lt_prog_compiler_pic= + lt_prog_compiler_can_build_shared=no +fi + +fi + + + + + + + + + + + +# +# Check to make sure the static flag actually works. +# +wl=$lt_prog_compiler_wl eval lt_tmp_static_flag=\"$lt_prog_compiler_static\" +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler static flag $lt_tmp_static_flag works" >&5 +$as_echo_n "checking if $compiler static flag $lt_tmp_static_flag works... " >&6; } +if ${lt_cv_prog_compiler_static_works+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_prog_compiler_static_works=no + save_LDFLAGS="$LDFLAGS" + LDFLAGS="$LDFLAGS $lt_tmp_static_flag" + echo "$lt_simple_link_test_code" > conftest.$ac_ext + if (eval $ac_link 2>conftest.err) && test -s conftest$ac_exeext; then + # The linker can only warn and ignore the option if not recognized + # So say no if there are warnings + if test -s conftest.err; then + # Append any errors to the config.log. + cat conftest.err 1>&5 + $ECHO "$_lt_linker_boilerplate" | $SED '/^$/d' > conftest.exp + $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 + if diff conftest.exp conftest.er2 >/dev/null; then + lt_cv_prog_compiler_static_works=yes + fi + else + lt_cv_prog_compiler_static_works=yes + fi + fi + $RM -r conftest* + LDFLAGS="$save_LDFLAGS" + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_static_works" >&5 +$as_echo "$lt_cv_prog_compiler_static_works" >&6; } + +if test x"$lt_cv_prog_compiler_static_works" = xyes; then + : +else + lt_prog_compiler_static= +fi + + + + + + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler supports -c -o file.$ac_objext" >&5 +$as_echo_n "checking if $compiler supports -c -o file.$ac_objext... " >&6; } +if ${lt_cv_prog_compiler_c_o+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_prog_compiler_c_o=no + $RM -r conftest 2>/dev/null + mkdir conftest + cd conftest + mkdir out + echo "$lt_simple_compile_test_code" > conftest.$ac_ext + + lt_compiler_flag="-o out/conftest2.$ac_objext" + # Insert the option either (1) after the last *FLAGS variable, or + # (2) before a word containing "conftest.", or (3) at the end. + # Note that $ac_compile itself does not contain backslashes and begins + # with a dollar sign (not a hyphen), so the echo should work correctly. + lt_compile=`echo "$ac_compile" | $SED \ + -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ + -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ + -e 's:$: $lt_compiler_flag:'` + (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5) + (eval "$lt_compile" 2>out/conftest.err) + ac_status=$? + cat out/conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + if (exit $ac_status) && test -s out/conftest2.$ac_objext + then + # The compiler can only warn and ignore the option if not recognized + # So say no if there are warnings + $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' > out/conftest.exp + $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2 + if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then + lt_cv_prog_compiler_c_o=yes + fi + fi + chmod u+w . 2>&5 + $RM conftest* + # SGI C++ compiler will create directory out/ii_files/ for + # template instantiation + test -d out/ii_files && $RM out/ii_files/* && rmdir out/ii_files + $RM out/* && rmdir out + cd .. + $RM -r conftest + $RM conftest* + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_c_o" >&5 +$as_echo "$lt_cv_prog_compiler_c_o" >&6; } + + + + + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler supports -c -o file.$ac_objext" >&5 +$as_echo_n "checking if $compiler supports -c -o file.$ac_objext... " >&6; } +if ${lt_cv_prog_compiler_c_o+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_prog_compiler_c_o=no + $RM -r conftest 2>/dev/null + mkdir conftest + cd conftest + mkdir out + echo "$lt_simple_compile_test_code" > conftest.$ac_ext + + lt_compiler_flag="-o out/conftest2.$ac_objext" + # Insert the option either (1) after the last *FLAGS variable, or + # (2) before a word containing "conftest.", or (3) at the end. + # Note that $ac_compile itself does not contain backslashes and begins + # with a dollar sign (not a hyphen), so the echo should work correctly. + lt_compile=`echo "$ac_compile" | $SED \ + -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ + -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ + -e 's:$: $lt_compiler_flag:'` + (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5) + (eval "$lt_compile" 2>out/conftest.err) + ac_status=$? + cat out/conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + if (exit $ac_status) && test -s out/conftest2.$ac_objext + then + # The compiler can only warn and ignore the option if not recognized + # So say no if there are warnings + $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' > out/conftest.exp + $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2 + if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then + lt_cv_prog_compiler_c_o=yes + fi + fi + chmod u+w . 2>&5 + $RM conftest* + # SGI C++ compiler will create directory out/ii_files/ for + # template instantiation + test -d out/ii_files && $RM out/ii_files/* && rmdir out/ii_files + $RM out/* && rmdir out + cd .. + $RM -r conftest + $RM conftest* + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_c_o" >&5 +$as_echo "$lt_cv_prog_compiler_c_o" >&6; } + + + + +hard_links="nottested" +if test "$lt_cv_prog_compiler_c_o" = no && test "$need_locks" != no; then + # do not overwrite the value of need_locks provided by the user + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if we can lock with hard links" >&5 +$as_echo_n "checking if we can lock with hard links... " >&6; } + hard_links=yes + $RM conftest* + ln conftest.a conftest.b 2>/dev/null && hard_links=no + touch conftest.a + ln conftest.a conftest.b 2>&5 || hard_links=no + ln conftest.a conftest.b 2>/dev/null && hard_links=no + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $hard_links" >&5 +$as_echo "$hard_links" >&6; } + if test "$hard_links" = no; then + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: \`$CC' does not support \`-c -o', so \`make -j' may be unsafe" >&5 +$as_echo "$as_me: WARNING: \`$CC' does not support \`-c -o', so \`make -j' may be unsafe" >&2;} + need_locks=warn + fi +else + need_locks=no +fi + + + + + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the $compiler linker ($LD) supports shared libraries" >&5 +$as_echo_n "checking whether the $compiler linker ($LD) supports shared libraries... " >&6; } + + runpath_var= + allow_undefined_flag= + always_export_symbols=no + archive_cmds= + archive_expsym_cmds= + compiler_needs_object=no + enable_shared_with_static_runtimes=no + export_dynamic_flag_spec= + export_symbols_cmds='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' + hardcode_automatic=no + hardcode_direct=no + hardcode_direct_absolute=no + hardcode_libdir_flag_spec= + hardcode_libdir_separator= + hardcode_minus_L=no + hardcode_shlibpath_var=unsupported + inherit_rpath=no + link_all_deplibs=unknown + module_cmds= + module_expsym_cmds= + old_archive_from_new_cmds= + old_archive_from_expsyms_cmds= + thread_safe_flag_spec= + whole_archive_flag_spec= + # include_expsyms should be a list of space-separated symbols to be *always* + # included in the symbol list + include_expsyms= + # exclude_expsyms can be an extended regexp of symbols to exclude + # it will be wrapped by ` (' and `)$', so one must not match beginning or + # end of line. Example: `a|bc|.*d.*' will exclude the symbols `a' and `bc', + # as well as any symbol that contains `d'. + exclude_expsyms='_GLOBAL_OFFSET_TABLE_|_GLOBAL__F[ID]_.*' + # Although _GLOBAL_OFFSET_TABLE_ is a valid symbol C name, most a.out + # platforms (ab)use it in PIC code, but their linkers get confused if + # the symbol is explicitly referenced. Since portable code cannot + # rely on this symbol name, it's probably fine to never include it in + # preloaded symbol tables. + # Exclude shared library initialization/finalization symbols. + extract_expsyms_cmds= + + case $host_os in + cygwin* | mingw* | pw32* | cegcc*) + # FIXME: the MSVC++ port hasn't been tested in a loooong time + # When not using gcc, we currently assume that we are using + # Microsoft Visual C++. + if test "$GCC" != yes; then + with_gnu_ld=no + fi + ;; + interix*) + # we just hope/assume this is gcc and not c89 (= MSVC++) + with_gnu_ld=yes + ;; + openbsd*) + with_gnu_ld=no + ;; + linux* | k*bsd*-gnu | gnu*) + link_all_deplibs=no + ;; + esac + + ld_shlibs=yes + + # On some targets, GNU ld is compatible enough with the native linker + # that we're better off using the native interface for both. + lt_use_gnu_ld_interface=no + if test "$with_gnu_ld" = yes; then + case $host_os in + aix*) + # The AIX port of GNU ld has always aspired to compatibility + # with the native linker. However, as the warning in the GNU ld + # block says, versions before 2.19.5* couldn't really create working + # shared libraries, regardless of the interface used. + case `$LD -v 2>&1` in + *\ \(GNU\ Binutils\)\ 2.19.5*) ;; + *\ \(GNU\ Binutils\)\ 2.[2-9]*) ;; + *\ \(GNU\ Binutils\)\ [3-9]*) ;; + *) + lt_use_gnu_ld_interface=yes + ;; + esac + ;; + *) + lt_use_gnu_ld_interface=yes + ;; + esac + fi + + if test "$lt_use_gnu_ld_interface" = yes; then + # If archive_cmds runs LD, not CC, wlarc should be empty + wlarc='${wl}' + + # Set some defaults for GNU ld with shared library support. These + # are reset later if shared libraries are not supported. Putting them + # here allows them to be overridden if necessary. + runpath_var=LD_RUN_PATH + hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' + export_dynamic_flag_spec='${wl}--export-dynamic' + # ancient GNU ld didn't support --whole-archive et. al. + if $LD --help 2>&1 | $GREP 'no-whole-archive' > /dev/null; then + whole_archive_flag_spec="$wlarc"'--whole-archive$convenience '"$wlarc"'--no-whole-archive' + else + whole_archive_flag_spec= + fi + supports_anon_versioning=no + case `$LD -v 2>&1` in + *GNU\ gold*) supports_anon_versioning=yes ;; + *\ [01].* | *\ 2.[0-9].* | *\ 2.10.*) ;; # catch versions < 2.11 + *\ 2.11.93.0.2\ *) supports_anon_versioning=yes ;; # RH7.3 ... + *\ 2.11.92.0.12\ *) supports_anon_versioning=yes ;; # Mandrake 8.2 ... + *\ 2.11.*) ;; # other 2.11 versions + *) supports_anon_versioning=yes ;; + esac + + # See if GNU ld supports shared libraries. + case $host_os in + aix[3-9]*) + # On AIX/PPC, the GNU linker is very broken + if test "$host_cpu" != ia64; then + ld_shlibs=no + cat <<_LT_EOF 1>&2 + +*** Warning: the GNU linker, at least up to release 2.19, is reported +*** to be unable to reliably create shared libraries on AIX. +*** Therefore, libtool is disabling shared libraries support. If you +*** really care for shared libraries, you may want to install binutils +*** 2.20 or above, or modify your PATH so that a non-GNU linker is found. +*** You will then need to restart the configuration process. + +_LT_EOF + fi + ;; + + amigaos*) + case $host_cpu in + powerpc) + # see comment about AmigaOS4 .so support + archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + archive_expsym_cmds='' + ;; + m68k) + archive_cmds='$RM $output_objdir/a2ixlibrary.data~$ECHO "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$ECHO "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$ECHO "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$ECHO "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)' + hardcode_libdir_flag_spec='-L$libdir' + hardcode_minus_L=yes + ;; + esac + ;; + + beos*) + if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then + allow_undefined_flag=unsupported + # Joseph Beckenbach says some releases of gcc + # support --undefined. This deserves some investigation. FIXME + archive_cmds='$CC -nostart $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + else + ld_shlibs=no + fi + ;; + + cygwin* | mingw* | pw32* | cegcc*) + # _LT_TAGVAR(hardcode_libdir_flag_spec, ) is actually meaningless, + # as there is no search path for DLLs. + hardcode_libdir_flag_spec='-L$libdir' + export_dynamic_flag_spec='${wl}--export-all-symbols' + allow_undefined_flag=unsupported + always_export_symbols=no + enable_shared_with_static_runtimes=yes + export_symbols_cmds='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[BCDGRS][ ]/s/.*[ ]\([^ ]*\)/\1 DATA/;s/^.*[ ]__nm__\([^ ]*\)[ ][^ ]*/\1 DATA/;/^I[ ]/d;/^[AITW][ ]/s/.* //'\'' | sort | uniq > $export_symbols' + exclude_expsyms='[_]+GLOBAL_OFFSET_TABLE_|[_]+GLOBAL__[FID]_.*|[_]+head_[A-Za-z0-9_]+_dll|[A-Za-z0-9_]+_dll_iname' + + if $LD --help 2>&1 | $GREP 'auto-import' > /dev/null; then + archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' + # If the export-symbols file already is a .def file (1st line + # is EXPORTS), use it as is; otherwise, prepend... + archive_expsym_cmds='if test "x`$SED 1q $export_symbols`" = xEXPORTS; then + cp $export_symbols $output_objdir/$soname.def; + else + echo EXPORTS > $output_objdir/$soname.def; + cat $export_symbols >> $output_objdir/$soname.def; + fi~ + $CC -shared $output_objdir/$soname.def $libobjs $deplibs $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' + else + ld_shlibs=no + fi + ;; + + haiku*) + archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + link_all_deplibs=yes + ;; + + interix[3-9]*) + hardcode_direct=no + hardcode_shlibpath_var=no + hardcode_libdir_flag_spec='${wl}-rpath,$libdir' + export_dynamic_flag_spec='${wl}-E' + # Hack: On Interix 3.x, we cannot compile PIC because of a broken gcc. + # Instead, shared libraries are loaded at an image base (0x10000000 by + # default) and relocated if they conflict, which is a slow very memory + # consuming and fragmenting process. To avoid this, we pick a random, + # 256 KiB-aligned image base between 0x50000000 and 0x6FFC0000 at link + # time. Moving up from 0x10000000 also allows more sbrk(2) space. + archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' + archive_expsym_cmds='sed "s,^,_," $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--retain-symbols-file,$output_objdir/$soname.expsym ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' + ;; + + gnu* | linux* | tpf* | k*bsd*-gnu | kopensolaris*-gnu) + tmp_diet=no + if test "$host_os" = linux-dietlibc; then + case $cc_basename in + diet\ *) tmp_diet=yes;; # linux-dietlibc with static linking (!diet-dyn) + esac + fi + if $LD --help 2>&1 | $EGREP ': supported targets:.* elf' > /dev/null \ + && test "$tmp_diet" = no + then + tmp_addflag=' $pic_flag' + tmp_sharedflag='-shared' + case $cc_basename,$host_cpu in + pgcc*) # Portland Group C compiler + whole_archive_flag_spec='${wl}--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` ${wl}--no-whole-archive' + tmp_addflag=' $pic_flag' + ;; + pgf77* | pgf90* | pgf95* | pgfortran*) + # Portland Group f77 and f90 compilers + whole_archive_flag_spec='${wl}--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` ${wl}--no-whole-archive' + tmp_addflag=' $pic_flag -Mnomain' ;; + ecc*,ia64* | icc*,ia64*) # Intel C compiler on ia64 + tmp_addflag=' -i_dynamic' ;; + efc*,ia64* | ifort*,ia64*) # Intel Fortran compiler on ia64 + tmp_addflag=' -i_dynamic -nofor_main' ;; + ifc* | ifort*) # Intel Fortran compiler + tmp_addflag=' -nofor_main' ;; + lf95*) # Lahey Fortran 8.1 + whole_archive_flag_spec= + tmp_sharedflag='--shared' ;; + xl[cC]* | bgxl[cC]* | mpixl[cC]*) # IBM XL C 8.0 on PPC (deal with xlf below) + tmp_sharedflag='-qmkshrobj' + tmp_addflag= ;; + nvcc*) # Cuda Compiler Driver 2.2 + whole_archive_flag_spec='${wl}--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` ${wl}--no-whole-archive' + compiler_needs_object=yes + ;; + esac + case `$CC -V 2>&1 | sed 5q` in + *Sun\ C*) # Sun C 5.9 + whole_archive_flag_spec='${wl}--whole-archive`new_convenience=; for conv in $convenience\"\"; do test -z \"$conv\" || new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` ${wl}--no-whole-archive' + compiler_needs_object=yes + tmp_sharedflag='-G' ;; + *Sun\ F*) # Sun Fortran 8.3 + tmp_sharedflag='-G' ;; + esac + archive_cmds='$CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + + if test "x$supports_anon_versioning" = xyes; then + archive_expsym_cmds='echo "{ global:" > $output_objdir/$libname.ver~ + cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ + echo "local: *; };" >> $output_objdir/$libname.ver~ + $CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-version-script ${wl}$output_objdir/$libname.ver -o $lib' + fi + + case $cc_basename in + xlf* | bgf* | bgxlf* | mpixlf*) + # IBM XL Fortran 10.1 on PPC cannot create shared libs itself + whole_archive_flag_spec='--whole-archive$convenience --no-whole-archive' + hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' + archive_cmds='$LD -shared $libobjs $deplibs $linker_flags -soname $soname -o $lib' + if test "x$supports_anon_versioning" = xyes; then + archive_expsym_cmds='echo "{ global:" > $output_objdir/$libname.ver~ + cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ + echo "local: *; };" >> $output_objdir/$libname.ver~ + $LD -shared $libobjs $deplibs $linker_flags -soname $soname -version-script $output_objdir/$libname.ver -o $lib' + fi + ;; + esac + else + ld_shlibs=no + fi + ;; + + netbsd* | netbsdelf*-gnu) + if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then + archive_cmds='$LD -Bshareable $libobjs $deplibs $linker_flags -o $lib' + wlarc= + else + archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + archive_expsym_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' + fi + ;; + + solaris*) + if $LD -v 2>&1 | $GREP 'BFD 2\.8' > /dev/null; then + ld_shlibs=no + cat <<_LT_EOF 1>&2 + +*** Warning: The releases 2.8.* of the GNU linker cannot reliably +*** create shared libraries on Solaris systems. Therefore, libtool +*** is disabling shared libraries support. We urge you to upgrade GNU +*** binutils to release 2.9.1 or newer. Another option is to modify +*** your PATH or compiler configuration so that the native linker is +*** used, and then restart. + +_LT_EOF + elif $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then + archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + archive_expsym_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' + else + ld_shlibs=no + fi + ;; + + sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX*) + case `$LD -v 2>&1` in + *\ [01].* | *\ 2.[0-9].* | *\ 2.1[0-5].*) + ld_shlibs=no + cat <<_LT_EOF 1>&2 + +*** Warning: Releases of the GNU linker prior to 2.16.91.0.3 can not +*** reliably create shared libraries on SCO systems. Therefore, libtool +*** is disabling shared libraries support. We urge you to upgrade GNU +*** binutils to release 2.16.91.0.3 or newer. Another option is to modify +*** your PATH or compiler configuration so that the native linker is +*** used, and then restart. + +_LT_EOF + ;; + *) + # For security reasons, it is highly recommended that you always + # use absolute paths for naming shared libraries, and exclude the + # DT_RUNPATH tag from executables and libraries. But doing so + # requires that you compile everything twice, which is a pain. + if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then + hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' + archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + archive_expsym_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' + else + ld_shlibs=no + fi + ;; + esac + ;; + + sunos4*) + archive_cmds='$LD -assert pure-text -Bshareable -o $lib $libobjs $deplibs $linker_flags' + wlarc= + hardcode_direct=yes + hardcode_shlibpath_var=no + ;; + + *) + if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then + archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + archive_expsym_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' + else + ld_shlibs=no + fi + ;; + esac + + if test "$ld_shlibs" = no; then + runpath_var= + hardcode_libdir_flag_spec= + export_dynamic_flag_spec= + whole_archive_flag_spec= + fi + else + # PORTME fill in a description of your system's linker (not GNU ld) + case $host_os in + aix3*) + allow_undefined_flag=unsupported + always_export_symbols=yes + archive_expsym_cmds='$LD -o $output_objdir/$soname $libobjs $deplibs $linker_flags -bE:$export_symbols -T512 -H512 -bM:SRE~$AR $AR_FLAGS $lib $output_objdir/$soname' + # Note: this linker hardcodes the directories in LIBPATH if there + # are no directories specified by -L. + hardcode_minus_L=yes + if test "$GCC" = yes && test -z "$lt_prog_compiler_static"; then + # Neither direct hardcoding nor static linking is supported with a + # broken collect2. + hardcode_direct=unsupported + fi + ;; + + aix[4-9]*) + if test "$host_cpu" = ia64; then + # On IA64, the linker does run time linking by default, so we don't + # have to do anything special. + aix_use_runtimelinking=no + exp_sym_flag='-Bexport' + no_entry_flag="" + else + # If we're using GNU nm, then we don't want the "-C" option. + # -C means demangle to AIX nm, but means don't demangle with GNU nm + # Also, AIX nm treats weak defined symbols like other global + # defined symbols, whereas GNU nm marks them as "W". + if $NM -V 2>&1 | $GREP 'GNU' > /dev/null; then + export_symbols_cmds='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W")) && (substr(\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols' + else + export_symbols_cmds='$NM -BCpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B")) && (substr(\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols' + fi + aix_use_runtimelinking=no + + # Test if we are trying to use run time linking or normal + # AIX style linking. If -brtl is somewhere in LDFLAGS, we + # need to do runtime linking. + case $host_os in aix4.[23]|aix4.[23].*|aix[5-9]*) + for ld_flag in $LDFLAGS; do + if (test $ld_flag = "-brtl" || test $ld_flag = "-Wl,-brtl"); then + aix_use_runtimelinking=yes + break + fi + done + ;; + esac + + exp_sym_flag='-bexport' + no_entry_flag='-bnoentry' + fi + + # When large executables or shared objects are built, AIX ld can + # have problems creating the table of contents. If linking a library + # or program results in "error TOC overflow" add -mminimal-toc to + # CXXFLAGS/CFLAGS for g++/gcc. In the cases where that is not + # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS. + + archive_cmds='' + hardcode_direct=yes + hardcode_direct_absolute=yes + hardcode_libdir_separator=':' + link_all_deplibs=yes + file_list_spec='${wl}-f,' + + if test "$GCC" = yes; then + case $host_os in aix4.[012]|aix4.[012].*) + # We only want to do this on AIX 4.2 and lower, the check + # below for broken collect2 doesn't work under 4.3+ + collect2name=`${CC} -print-prog-name=collect2` + if test -f "$collect2name" && + strings "$collect2name" | $GREP resolve_lib_name >/dev/null + then + # We have reworked collect2 + : + else + # We have old collect2 + hardcode_direct=unsupported + # It fails to find uninstalled libraries when the uninstalled + # path is not listed in the libpath. Setting hardcode_minus_L + # to unsupported forces relinking + hardcode_minus_L=yes + hardcode_libdir_flag_spec='-L$libdir' + hardcode_libdir_separator= + fi + ;; + esac + shared_flag='-shared' + if test "$aix_use_runtimelinking" = yes; then + shared_flag="$shared_flag "'${wl}-G' + fi + link_all_deplibs=no + else + # not using gcc + if test "$host_cpu" = ia64; then + # VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release + # chokes on -Wl,-G. The following line is correct: + shared_flag='-G' + else + if test "$aix_use_runtimelinking" = yes; then + shared_flag='${wl}-G' + else + shared_flag='${wl}-bM:SRE' + fi + fi + fi + + export_dynamic_flag_spec='${wl}-bexpall' + # It seems that -bexpall does not export symbols beginning with + # underscore (_), so it is better to generate a list of symbols to export. + always_export_symbols=yes + if test "$aix_use_runtimelinking" = yes; then + # Warning - without using the other runtime loading flags (-brtl), + # -berok will link without error, but may produce a broken library. + allow_undefined_flag='-berok' + # Determine the default libpath from the value encoded in an + # empty executable. + if test "${lt_cv_aix_libpath+set}" = set; then + aix_libpath=$lt_cv_aix_libpath +else + if ${lt_cv_aix_libpath_+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + + lt_aix_libpath_sed=' + /Import File Strings/,/^$/ { + /^0/ { + s/^0 *\([^ ]*\) *$/\1/ + p + } + }' + lt_cv_aix_libpath_=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` + # Check for a 64-bit object if we didn't find anything. + if test -z "$lt_cv_aix_libpath_"; then + lt_cv_aix_libpath_=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` + fi +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + if test -z "$lt_cv_aix_libpath_"; then + lt_cv_aix_libpath_="/usr/lib:/lib" + fi + +fi + + aix_libpath=$lt_cv_aix_libpath_ +fi + + hardcode_libdir_flag_spec='${wl}-blibpath:$libdir:'"$aix_libpath" + archive_expsym_cmds='$CC -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags `if test "x${allow_undefined_flag}" != "x"; then func_echo_all "${wl}${allow_undefined_flag}"; else :; fi` '"\${wl}$exp_sym_flag:\$export_symbols $shared_flag" + else + if test "$host_cpu" = ia64; then + hardcode_libdir_flag_spec='${wl}-R $libdir:/usr/lib:/lib' + allow_undefined_flag="-z nodefs" + archive_expsym_cmds="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags ${wl}${allow_undefined_flag} '"\${wl}$exp_sym_flag:\$export_symbols" + else + # Determine the default libpath from the value encoded in an + # empty executable. + if test "${lt_cv_aix_libpath+set}" = set; then + aix_libpath=$lt_cv_aix_libpath +else + if ${lt_cv_aix_libpath_+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + + lt_aix_libpath_sed=' + /Import File Strings/,/^$/ { + /^0/ { + s/^0 *\([^ ]*\) *$/\1/ + p + } + }' + lt_cv_aix_libpath_=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` + # Check for a 64-bit object if we didn't find anything. + if test -z "$lt_cv_aix_libpath_"; then + lt_cv_aix_libpath_=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` + fi +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + if test -z "$lt_cv_aix_libpath_"; then + lt_cv_aix_libpath_="/usr/lib:/lib" + fi + +fi + + aix_libpath=$lt_cv_aix_libpath_ +fi + + hardcode_libdir_flag_spec='${wl}-blibpath:$libdir:'"$aix_libpath" + # Warning - without using the other run time loading flags, + # -berok will link without error, but may produce a broken library. + no_undefined_flag=' ${wl}-bernotok' + allow_undefined_flag=' ${wl}-berok' + if test "$with_gnu_ld" = yes; then + # We only use this code for GNU lds that support --whole-archive. + whole_archive_flag_spec='${wl}--whole-archive$convenience ${wl}--no-whole-archive' + else + # Exported symbols can be pulled into shared objects from archives + whole_archive_flag_spec='$convenience' + fi + archive_cmds_need_lc=yes + # This is similar to how AIX traditionally builds its shared libraries. + archive_expsym_cmds="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs ${wl}-bnoentry $compiler_flags ${wl}-bE:$export_symbols${allow_undefined_flag}~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$soname' + fi + fi + ;; + + amigaos*) + case $host_cpu in + powerpc) + # see comment about AmigaOS4 .so support + archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + archive_expsym_cmds='' + ;; + m68k) + archive_cmds='$RM $output_objdir/a2ixlibrary.data~$ECHO "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$ECHO "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$ECHO "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$ECHO "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)' + hardcode_libdir_flag_spec='-L$libdir' + hardcode_minus_L=yes + ;; + esac + ;; + + bsdi[45]*) + export_dynamic_flag_spec=-rdynamic + ;; + + cygwin* | mingw* | pw32* | cegcc*) + # When not using gcc, we currently assume that we are using + # Microsoft Visual C++. + # hardcode_libdir_flag_spec is actually meaningless, as there is + # no search path for DLLs. + case $cc_basename in + cl*) + # Native MSVC + hardcode_libdir_flag_spec=' ' + allow_undefined_flag=unsupported + always_export_symbols=yes + file_list_spec='@' + # Tell ltmain to make .lib files, not .a files. + libext=lib + # Tell ltmain to make .dll files, not .so files. + shrext_cmds=".dll" + # FIXME: Setting linknames here is a bad hack. + archive_cmds='$CC -o $output_objdir/$soname $libobjs $compiler_flags $deplibs -Wl,-dll~linknames=' + archive_expsym_cmds='if test "x`$SED 1q $export_symbols`" = xEXPORTS; then + sed -n -e 's/\\\\\\\(.*\\\\\\\)/-link\\\ -EXPORT:\\\\\\\1/' -e '1\\\!p' < $export_symbols > $output_objdir/$soname.exp; + else + sed -e 's/\\\\\\\(.*\\\\\\\)/-link\\\ -EXPORT:\\\\\\\1/' < $export_symbols > $output_objdir/$soname.exp; + fi~ + $CC -o $tool_output_objdir$soname $libobjs $compiler_flags $deplibs "@$tool_output_objdir$soname.exp" -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~ + linknames=' + # The linker will not automatically build a static lib if we build a DLL. + # _LT_TAGVAR(old_archive_from_new_cmds, )='true' + enable_shared_with_static_runtimes=yes + exclude_expsyms='_NULL_IMPORT_DESCRIPTOR|_IMPORT_DESCRIPTOR_.*' + export_symbols_cmds='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[BCDGRS][ ]/s/.*[ ]\([^ ]*\)/\1,DATA/'\'' | $SED -e '\''/^[AITW][ ]/s/.*[ ]//'\'' | sort | uniq > $export_symbols' + # Don't use ranlib + old_postinstall_cmds='chmod 644 $oldlib' + postlink_cmds='lt_outputfile="@OUTPUT@"~ + lt_tool_outputfile="@TOOL_OUTPUT@"~ + case $lt_outputfile in + *.exe|*.EXE) ;; + *) + lt_outputfile="$lt_outputfile.exe" + lt_tool_outputfile="$lt_tool_outputfile.exe" + ;; + esac~ + if test "$MANIFEST_TOOL" != ":" && test -f "$lt_outputfile.manifest"; then + $MANIFEST_TOOL -manifest "$lt_tool_outputfile.manifest" -outputresource:"$lt_tool_outputfile" || exit 1; + $RM "$lt_outputfile.manifest"; + fi' + ;; + *) + # Assume MSVC wrapper + hardcode_libdir_flag_spec=' ' + allow_undefined_flag=unsupported + # Tell ltmain to make .lib files, not .a files. + libext=lib + # Tell ltmain to make .dll files, not .so files. + shrext_cmds=".dll" + # FIXME: Setting linknames here is a bad hack. + archive_cmds='$CC -o $lib $libobjs $compiler_flags `func_echo_all "$deplibs" | $SED '\''s/ -lc$//'\''` -link -dll~linknames=' + # The linker will automatically build a .lib file if we build a DLL. + old_archive_from_new_cmds='true' + # FIXME: Should let the user specify the lib program. + old_archive_cmds='lib -OUT:$oldlib$oldobjs$old_deplibs' + enable_shared_with_static_runtimes=yes + ;; + esac + ;; + + darwin* | rhapsody*) + + + archive_cmds_need_lc=no + hardcode_direct=no + hardcode_automatic=yes + hardcode_shlibpath_var=unsupported + if test "$lt_cv_ld_force_load" = "yes"; then + whole_archive_flag_spec='`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience ${wl}-force_load,$conv\"; done; func_echo_all \"$new_convenience\"`' + + else + whole_archive_flag_spec='' + fi + link_all_deplibs=yes + allow_undefined_flag="$_lt_dar_allow_undefined" + case $cc_basename in + ifort*) _lt_dar_can_shared=yes ;; + *) _lt_dar_can_shared=$GCC ;; + esac + if test "$_lt_dar_can_shared" = "yes"; then + output_verbose_link_cmd=func_echo_all + archive_cmds="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod${_lt_dsymutil}" + module_cmds="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dsymutil}" + archive_expsym_cmds="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring ${_lt_dar_single_mod}${_lt_dar_export_syms}${_lt_dsymutil}" + module_expsym_cmds="sed -e 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dar_export_syms}${_lt_dsymutil}" + + else + ld_shlibs=no + fi + + ;; + + dgux*) + archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + hardcode_libdir_flag_spec='-L$libdir' + hardcode_shlibpath_var=no + ;; + + # FreeBSD 2.2.[012] allows us to include c++rt0.o to get C++ constructor + # support. Future versions do this automatically, but an explicit c++rt0.o + # does not break anything, and helps significantly (at the cost of a little + # extra space). + freebsd2.2*) + archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags /usr/lib/c++rt0.o' + hardcode_libdir_flag_spec='-R$libdir' + hardcode_direct=yes + hardcode_shlibpath_var=no + ;; + + # Unfortunately, older versions of FreeBSD 2 do not have this feature. + freebsd2.*) + archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' + hardcode_direct=yes + hardcode_minus_L=yes + hardcode_shlibpath_var=no + ;; + + # FreeBSD 3 and greater uses gcc -shared to do shared libraries. + freebsd* | dragonfly*) + archive_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' + hardcode_libdir_flag_spec='-R$libdir' + hardcode_direct=yes + hardcode_shlibpath_var=no + ;; + + hpux9*) + if test "$GCC" = yes; then + archive_cmds='$RM $output_objdir/$soname~$CC -shared $pic_flag ${wl}+b ${wl}$install_libdir -o $output_objdir/$soname $libobjs $deplibs $compiler_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib' + else + archive_cmds='$RM $output_objdir/$soname~$LD -b +b $install_libdir -o $output_objdir/$soname $libobjs $deplibs $linker_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib' + fi + hardcode_libdir_flag_spec='${wl}+b ${wl}$libdir' + hardcode_libdir_separator=: + hardcode_direct=yes + + # hardcode_minus_L: Not really in the search PATH, + # but as the default location of the library. + hardcode_minus_L=yes + export_dynamic_flag_spec='${wl}-E' + ;; + + hpux10*) + if test "$GCC" = yes && test "$with_gnu_ld" = no; then + archive_cmds='$CC -shared $pic_flag ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags' + else + archive_cmds='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags' + fi + if test "$with_gnu_ld" = no; then + hardcode_libdir_flag_spec='${wl}+b ${wl}$libdir' + hardcode_libdir_separator=: + hardcode_direct=yes + hardcode_direct_absolute=yes + export_dynamic_flag_spec='${wl}-E' + # hardcode_minus_L: Not really in the search PATH, + # but as the default location of the library. + hardcode_minus_L=yes + fi + ;; + + hpux11*) + if test "$GCC" = yes && test "$with_gnu_ld" = no; then + case $host_cpu in + hppa*64*) + archive_cmds='$CC -shared ${wl}+h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags' + ;; + ia64*) + archive_cmds='$CC -shared $pic_flag ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags' + ;; + *) + archive_cmds='$CC -shared $pic_flag ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags' + ;; + esac + else + case $host_cpu in + hppa*64*) + archive_cmds='$CC -b ${wl}+h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags' + ;; + ia64*) + archive_cmds='$CC -b ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags' + ;; + *) + + # Older versions of the 11.00 compiler do not understand -b yet + # (HP92453-01 A.11.01.20 doesn't, HP92453-01 B.11.X.35175-35176.GP does) + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $CC understands -b" >&5 +$as_echo_n "checking if $CC understands -b... " >&6; } +if ${lt_cv_prog_compiler__b+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_prog_compiler__b=no + save_LDFLAGS="$LDFLAGS" + LDFLAGS="$LDFLAGS -b" + echo "$lt_simple_link_test_code" > conftest.$ac_ext + if (eval $ac_link 2>conftest.err) && test -s conftest$ac_exeext; then + # The linker can only warn and ignore the option if not recognized + # So say no if there are warnings + if test -s conftest.err; then + # Append any errors to the config.log. + cat conftest.err 1>&5 + $ECHO "$_lt_linker_boilerplate" | $SED '/^$/d' > conftest.exp + $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 + if diff conftest.exp conftest.er2 >/dev/null; then + lt_cv_prog_compiler__b=yes + fi + else + lt_cv_prog_compiler__b=yes + fi + fi + $RM -r conftest* + LDFLAGS="$save_LDFLAGS" + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler__b" >&5 +$as_echo "$lt_cv_prog_compiler__b" >&6; } + +if test x"$lt_cv_prog_compiler__b" = xyes; then + archive_cmds='$CC -b ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags' +else + archive_cmds='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags' +fi + + ;; + esac + fi + if test "$with_gnu_ld" = no; then + hardcode_libdir_flag_spec='${wl}+b ${wl}$libdir' + hardcode_libdir_separator=: + + case $host_cpu in + hppa*64*|ia64*) + hardcode_direct=no + hardcode_shlibpath_var=no + ;; + *) + hardcode_direct=yes + hardcode_direct_absolute=yes + export_dynamic_flag_spec='${wl}-E' + + # hardcode_minus_L: Not really in the search PATH, + # but as the default location of the library. + hardcode_minus_L=yes + ;; + esac + fi + ;; + + irix5* | irix6* | nonstopux*) + if test "$GCC" = yes; then + archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' + # Try to use the -exported_symbol ld option, if it does not + # work, assume that -exports_file does not work either and + # implicitly export all symbols. + # This should be the same for all languages, so no per-tag cache variable. + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the $host_os linker accepts -exported_symbol" >&5 +$as_echo_n "checking whether the $host_os linker accepts -exported_symbol... " >&6; } +if ${lt_cv_irix_exported_symbol+:} false; then : + $as_echo_n "(cached) " >&6 +else + save_LDFLAGS="$LDFLAGS" + LDFLAGS="$LDFLAGS -shared ${wl}-exported_symbol ${wl}foo ${wl}-update_registry ${wl}/dev/null" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +int foo (void) { return 0; } +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + lt_cv_irix_exported_symbol=yes +else + lt_cv_irix_exported_symbol=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + LDFLAGS="$save_LDFLAGS" +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_irix_exported_symbol" >&5 +$as_echo "$lt_cv_irix_exported_symbol" >&6; } + if test "$lt_cv_irix_exported_symbol" = yes; then + archive_expsym_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations ${wl}-exports_file ${wl}$export_symbols -o $lib' + fi + else + archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib' + archive_expsym_cmds='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -exports_file $export_symbols -o $lib' + fi + archive_cmds_need_lc='no' + hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' + hardcode_libdir_separator=: + inherit_rpath=yes + link_all_deplibs=yes + ;; + + netbsd* | netbsdelf*-gnu) + if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then + archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' # a.out + else + archive_cmds='$LD -shared -o $lib $libobjs $deplibs $linker_flags' # ELF + fi + hardcode_libdir_flag_spec='-R$libdir' + hardcode_direct=yes + hardcode_shlibpath_var=no + ;; + + newsos6) + archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + hardcode_direct=yes + hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' + hardcode_libdir_separator=: + hardcode_shlibpath_var=no + ;; + + *nto* | *qnx*) + ;; + + openbsd*) + if test -f /usr/libexec/ld.so; then + hardcode_direct=yes + hardcode_shlibpath_var=no + hardcode_direct_absolute=yes + if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then + archive_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' + archive_expsym_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags ${wl}-retain-symbols-file,$export_symbols' + hardcode_libdir_flag_spec='${wl}-rpath,$libdir' + export_dynamic_flag_spec='${wl}-E' + else + case $host_os in + openbsd[01].* | openbsd2.[0-7] | openbsd2.[0-7].*) + archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' + hardcode_libdir_flag_spec='-R$libdir' + ;; + *) + archive_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' + hardcode_libdir_flag_spec='${wl}-rpath,$libdir' + ;; + esac + fi + else + ld_shlibs=no + fi + ;; + + os2*) + hardcode_libdir_flag_spec='-L$libdir' + hardcode_minus_L=yes + allow_undefined_flag=unsupported + archive_cmds='$ECHO "LIBRARY $libname INITINSTANCE" > $output_objdir/$libname.def~$ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~echo DATA >> $output_objdir/$libname.def~echo " SINGLE NONSHARED" >> $output_objdir/$libname.def~echo EXPORTS >> $output_objdir/$libname.def~emxexp $libobjs >> $output_objdir/$libname.def~$CC -Zdll -Zcrtdll -o $lib $libobjs $deplibs $compiler_flags $output_objdir/$libname.def' + old_archive_from_new_cmds='emximp -o $output_objdir/$libname.a $output_objdir/$libname.def' + ;; + + osf3*) + if test "$GCC" = yes; then + allow_undefined_flag=' ${wl}-expect_unresolved ${wl}\*' + archive_cmds='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' + else + allow_undefined_flag=' -expect_unresolved \*' + archive_cmds='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib' + fi + archive_cmds_need_lc='no' + hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' + hardcode_libdir_separator=: + ;; + + osf4* | osf5*) # as osf3* with the addition of -msym flag + if test "$GCC" = yes; then + allow_undefined_flag=' ${wl}-expect_unresolved ${wl}\*' + archive_cmds='$CC -shared${allow_undefined_flag} $pic_flag $libobjs $deplibs $compiler_flags ${wl}-msym ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' + hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' + else + allow_undefined_flag=' -expect_unresolved \*' + archive_cmds='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags -msym -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib' + archive_expsym_cmds='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done; printf "%s\\n" "-hidden">> $lib.exp~ + $CC -shared${allow_undefined_flag} ${wl}-input ${wl}$lib.exp $compiler_flags $libobjs $deplibs -soname $soname `test -n "$verstring" && $ECHO "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib~$RM $lib.exp' + + # Both c and cxx compiler support -rpath directly + hardcode_libdir_flag_spec='-rpath $libdir' + fi + archive_cmds_need_lc='no' + hardcode_libdir_separator=: + ;; + + solaris*) + no_undefined_flag=' -z defs' + if test "$GCC" = yes; then + wlarc='${wl}' + archive_cmds='$CC -shared $pic_flag ${wl}-z ${wl}text ${wl}-h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags' + archive_expsym_cmds='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ + $CC -shared $pic_flag ${wl}-z ${wl}text ${wl}-M ${wl}$lib.exp ${wl}-h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp' + else + case `$CC -V 2>&1` in + *"Compilers 5.0"*) + wlarc='' + archive_cmds='$LD -G${allow_undefined_flag} -h $soname -o $lib $libobjs $deplibs $linker_flags' + archive_expsym_cmds='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ + $LD -G${allow_undefined_flag} -M $lib.exp -h $soname -o $lib $libobjs $deplibs $linker_flags~$RM $lib.exp' + ;; + *) + wlarc='${wl}' + archive_cmds='$CC -G${allow_undefined_flag} -h $soname -o $lib $libobjs $deplibs $compiler_flags' + archive_expsym_cmds='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ + $CC -G${allow_undefined_flag} -M $lib.exp -h $soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp' + ;; + esac + fi + hardcode_libdir_flag_spec='-R$libdir' + hardcode_shlibpath_var=no + case $host_os in + solaris2.[0-5] | solaris2.[0-5].*) ;; + *) + # The compiler driver will combine and reorder linker options, + # but understands `-z linker_flag'. GCC discards it without `$wl', + # but is careful enough not to reorder. + # Supported since Solaris 2.6 (maybe 2.5.1?) + if test "$GCC" = yes; then + whole_archive_flag_spec='${wl}-z ${wl}allextract$convenience ${wl}-z ${wl}defaultextract' + else + whole_archive_flag_spec='-z allextract$convenience -z defaultextract' + fi + ;; + esac + link_all_deplibs=yes + ;; + + sunos4*) + if test "x$host_vendor" = xsequent; then + # Use $CC to link under sequent, because it throws in some extra .o + # files that make .init and .fini sections work. + archive_cmds='$CC -G ${wl}-h $soname -o $lib $libobjs $deplibs $compiler_flags' + else + archive_cmds='$LD -assert pure-text -Bstatic -o $lib $libobjs $deplibs $linker_flags' + fi + hardcode_libdir_flag_spec='-L$libdir' + hardcode_direct=yes + hardcode_minus_L=yes + hardcode_shlibpath_var=no + ;; + + sysv4) + case $host_vendor in + sni) + archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + hardcode_direct=yes # is this really true??? + ;; + siemens) + ## LD is ld it makes a PLAMLIB + ## CC just makes a GrossModule. + archive_cmds='$LD -G -o $lib $libobjs $deplibs $linker_flags' + reload_cmds='$CC -r -o $output$reload_objs' + hardcode_direct=no + ;; + motorola) + archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + hardcode_direct=no #Motorola manual says yes, but my tests say they lie + ;; + esac + runpath_var='LD_RUN_PATH' + hardcode_shlibpath_var=no + ;; + + sysv4.3*) + archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + hardcode_shlibpath_var=no + export_dynamic_flag_spec='-Bexport' + ;; + + sysv4*MP*) + if test -d /usr/nec; then + archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + hardcode_shlibpath_var=no + runpath_var=LD_RUN_PATH + hardcode_runpath_var=yes + ld_shlibs=yes + fi + ;; + + sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[01].[10]* | unixware7* | sco3.2v5.0.[024]*) + no_undefined_flag='${wl}-z,text' + archive_cmds_need_lc=no + hardcode_shlibpath_var=no + runpath_var='LD_RUN_PATH' + + if test "$GCC" = yes; then + archive_cmds='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + archive_expsym_cmds='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + else + archive_cmds='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + archive_expsym_cmds='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + fi + ;; + + sysv5* | sco3.2v5* | sco5v6*) + # Note: We can NOT use -z defs as we might desire, because we do not + # link with -lc, and that would cause any symbols used from libc to + # always be unresolved, which means just about no library would + # ever link correctly. If we're not using GNU ld we use -z text + # though, which does catch some bad symbols but isn't as heavy-handed + # as -z defs. + no_undefined_flag='${wl}-z,text' + allow_undefined_flag='${wl}-z,nodefs' + archive_cmds_need_lc=no + hardcode_shlibpath_var=no + hardcode_libdir_flag_spec='${wl}-R,$libdir' + hardcode_libdir_separator=':' + link_all_deplibs=yes + export_dynamic_flag_spec='${wl}-Bexport' + runpath_var='LD_RUN_PATH' + + if test "$GCC" = yes; then + archive_cmds='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + archive_expsym_cmds='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + else + archive_cmds='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + archive_expsym_cmds='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + fi + ;; + + uts4*) + archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + hardcode_libdir_flag_spec='-L$libdir' + hardcode_shlibpath_var=no + ;; + + *) + ld_shlibs=no + ;; + esac + + if test x$host_vendor = xsni; then + case $host in + sysv4 | sysv4.2uw2* | sysv4.3* | sysv5*) + export_dynamic_flag_spec='${wl}-Blargedynsym' + ;; + esac + fi + fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ld_shlibs" >&5 +$as_echo "$ld_shlibs" >&6; } +test "$ld_shlibs" = no && can_build_shared=no + +with_gnu_ld=$with_gnu_ld + + + + + + + + + + + + + + + +# +# Do we need to explicitly link libc? +# +case "x$archive_cmds_need_lc" in +x|xyes) + # Assume -lc should be added + archive_cmds_need_lc=yes + + if test "$enable_shared" = yes && test "$GCC" = yes; then + case $archive_cmds in + *'~'*) + # FIXME: we may have to deal with multi-command sequences. + ;; + '$CC '*) + # Test whether the compiler implicitly links with -lc since on some + # systems, -lgcc has to come before -lc. If gcc already passes -lc + # to ld, don't add -lc before -lgcc. + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether -lc should be explicitly linked in" >&5 +$as_echo_n "checking whether -lc should be explicitly linked in... " >&6; } +if ${lt_cv_archive_cmds_need_lc+:} false; then : + $as_echo_n "(cached) " >&6 +else + $RM conftest* + echo "$lt_simple_compile_test_code" > conftest.$ac_ext + + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } 2>conftest.err; then + soname=conftest + lib=conftest + libobjs=conftest.$ac_objext + deplibs= + wl=$lt_prog_compiler_wl + pic_flag=$lt_prog_compiler_pic + compiler_flags=-v + linker_flags=-v + verstring= + output_objdir=. + libname=conftest + lt_save_allow_undefined_flag=$allow_undefined_flag + allow_undefined_flag= + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$archive_cmds 2\>\&1 \| $GREP \" -lc \" \>/dev/null 2\>\&1\""; } >&5 + (eval $archive_cmds 2\>\&1 \| $GREP \" -lc \" \>/dev/null 2\>\&1) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } + then + lt_cv_archive_cmds_need_lc=no + else + lt_cv_archive_cmds_need_lc=yes + fi + allow_undefined_flag=$lt_save_allow_undefined_flag + else + cat conftest.err 1>&5 + fi + $RM conftest* + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_archive_cmds_need_lc" >&5 +$as_echo "$lt_cv_archive_cmds_need_lc" >&6; } + archive_cmds_need_lc=$lt_cv_archive_cmds_need_lc + ;; + esac + fi + ;; +esac + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking dynamic linker characteristics" >&5 +$as_echo_n "checking dynamic linker characteristics... " >&6; } + +if test "$GCC" = yes; then + case $host_os in + darwin*) lt_awk_arg="/^libraries:/,/LR/" ;; + *) lt_awk_arg="/^libraries:/" ;; + esac + case $host_os in + mingw* | cegcc*) lt_sed_strip_eq="s,=\([A-Za-z]:\),\1,g" ;; + *) lt_sed_strip_eq="s,=/,/,g" ;; + esac + lt_search_path_spec=`$CC -print-search-dirs | awk $lt_awk_arg | $SED -e "s/^libraries://" -e $lt_sed_strip_eq` + case $lt_search_path_spec in + *\;*) + # if the path contains ";" then we assume it to be the separator + # otherwise default to the standard path separator (i.e. ":") - it is + # assumed that no part of a normal pathname contains ";" but that should + # okay in the real world where ";" in dirpaths is itself problematic. + lt_search_path_spec=`$ECHO "$lt_search_path_spec" | $SED 's/;/ /g'` + ;; + *) + lt_search_path_spec=`$ECHO "$lt_search_path_spec" | $SED "s/$PATH_SEPARATOR/ /g"` + ;; + esac + # Ok, now we have the path, separated by spaces, we can step through it + # and add multilib dir if necessary. + lt_tmp_lt_search_path_spec= + lt_multi_os_dir=`$CC $CPPFLAGS $CFLAGS $LDFLAGS -print-multi-os-directory 2>/dev/null` + for lt_sys_path in $lt_search_path_spec; do + if test -d "$lt_sys_path/$lt_multi_os_dir"; then + lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec $lt_sys_path/$lt_multi_os_dir" + else + test -d "$lt_sys_path" && \ + lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec $lt_sys_path" + fi + done + lt_search_path_spec=`$ECHO "$lt_tmp_lt_search_path_spec" | awk ' +BEGIN {RS=" "; FS="/|\n";} { + lt_foo=""; + lt_count=0; + for (lt_i = NF; lt_i > 0; lt_i--) { + if ($lt_i != "" && $lt_i != ".") { + if ($lt_i == "..") { + lt_count++; + } else { + if (lt_count == 0) { + lt_foo="/" $lt_i lt_foo; + } else { + lt_count--; + } + } + } + } + if (lt_foo != "") { lt_freq[lt_foo]++; } + if (lt_freq[lt_foo] == 1) { print lt_foo; } +}'` + # AWK program above erroneously prepends '/' to C:/dos/paths + # for these hosts. + case $host_os in + mingw* | cegcc*) lt_search_path_spec=`$ECHO "$lt_search_path_spec" |\ + $SED 's,/\([A-Za-z]:\),\1,g'` ;; + esac + sys_lib_search_path_spec=`$ECHO "$lt_search_path_spec" | $lt_NL2SP` +else + sys_lib_search_path_spec="/lib /usr/lib /usr/local/lib" +fi +library_names_spec= +libname_spec='lib$name' +soname_spec= +shrext_cmds=".so" +postinstall_cmds= +postuninstall_cmds= +finish_cmds= +finish_eval= +shlibpath_var= +shlibpath_overrides_runpath=unknown +version_type=none +dynamic_linker="$host_os ld.so" +sys_lib_dlsearch_path_spec="/lib /usr/lib" +need_lib_prefix=unknown +hardcode_into_libs=no + +# when you set need_version to no, make sure it does not cause -set_version +# flags to be left without arguments +need_version=unknown + +case $host_os in +aix3*) + version_type=linux # correct to gnu/linux during the next big refactor + library_names_spec='${libname}${release}${shared_ext}$versuffix $libname.a' + shlibpath_var=LIBPATH + + # AIX 3 has no versioning support, so we append a major version to the name. + soname_spec='${libname}${release}${shared_ext}$major' + ;; + +aix[4-9]*) + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + hardcode_into_libs=yes + if test "$host_cpu" = ia64; then + # AIX 5 supports IA64 + library_names_spec='${libname}${release}${shared_ext}$major ${libname}${release}${shared_ext}$versuffix $libname${shared_ext}' + shlibpath_var=LD_LIBRARY_PATH + else + # With GCC up to 2.95.x, collect2 would create an import file + # for dependence libraries. The import file would start with + # the line `#! .'. This would cause the generated library to + # depend on `.', always an invalid library. This was fixed in + # development snapshots of GCC prior to 3.0. + case $host_os in + aix4 | aix4.[01] | aix4.[01].*) + if { echo '#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 97)' + echo ' yes ' + echo '#endif'; } | ${CC} -E - | $GREP yes > /dev/null; then + : + else + can_build_shared=no + fi + ;; + esac + # AIX (on Power*) has no versioning support, so currently we can not hardcode correct + # soname into executable. Probably we can add versioning support to + # collect2, so additional links can be useful in future. + if test "$aix_use_runtimelinking" = yes; then + # If using run time linking (on AIX 4.2 or later) use lib.so + # instead of lib.a to let people know that these are not + # typical AIX shared libraries. + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + else + # We preserve .a as extension for shared libraries through AIX4.2 + # and later when we are not doing run time linking. + library_names_spec='${libname}${release}.a $libname.a' + soname_spec='${libname}${release}${shared_ext}$major' + fi + shlibpath_var=LIBPATH + fi + ;; + +amigaos*) + case $host_cpu in + powerpc) + # Since July 2007 AmigaOS4 officially supports .so libraries. + # When compiling the executable, add -use-dynld -Lsobjs: to the compileline. + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + ;; + m68k) + library_names_spec='$libname.ixlibrary $libname.a' + # Create ${libname}_ixlibrary.a entries in /sys/libs. + finish_eval='for lib in `ls $libdir/*.ixlibrary 2>/dev/null`; do libname=`func_echo_all "$lib" | $SED '\''s%^.*/\([^/]*\)\.ixlibrary$%\1%'\''`; test $RM /sys/libs/${libname}_ixlibrary.a; $show "cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a"; cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a || exit 1; done' + ;; + esac + ;; + +beos*) + library_names_spec='${libname}${shared_ext}' + dynamic_linker="$host_os ld.so" + shlibpath_var=LIBRARY_PATH + ;; + +bsdi[45]*) + version_type=linux # correct to gnu/linux during the next big refactor + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + finish_cmds='PATH="\$PATH:/sbin" ldconfig $libdir' + shlibpath_var=LD_LIBRARY_PATH + sys_lib_search_path_spec="/shlib /usr/lib /usr/X11/lib /usr/contrib/lib /lib /usr/local/lib" + sys_lib_dlsearch_path_spec="/shlib /usr/lib /usr/local/lib" + # the default ld.so.conf also contains /usr/contrib/lib and + # /usr/X11R6/lib (/usr/X11 is a link to /usr/X11R6), but let us allow + # libtool to hard-code these into programs + ;; + +cygwin* | mingw* | pw32* | cegcc*) + version_type=windows + shrext_cmds=".dll" + need_version=no + need_lib_prefix=no + + case $GCC,$cc_basename in + yes,*) + # gcc + library_names_spec='$libname.dll.a' + # DLL is installed to $(libdir)/../bin by postinstall_cmds + postinstall_cmds='base_file=`basename \${file}`~ + dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\${base_file}'\''i; echo \$dlname'\''`~ + dldir=$destdir/`dirname \$dlpath`~ + test -d \$dldir || mkdir -p \$dldir~ + $install_prog $dir/$dlname \$dldir/$dlname~ + chmod a+x \$dldir/$dlname~ + if test -n '\''$stripme'\'' && test -n '\''$striplib'\''; then + eval '\''$striplib \$dldir/$dlname'\'' || exit \$?; + fi' + postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~ + dlpath=$dir/\$dldll~ + $RM \$dlpath' + shlibpath_overrides_runpath=yes + + case $host_os in + cygwin*) + # Cygwin DLLs use 'cyg' prefix rather than 'lib' + soname_spec='`echo ${libname} | sed -e 's/^lib/cyg/'``echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext}' + + sys_lib_search_path_spec="$sys_lib_search_path_spec /usr/lib/w32api" + ;; + mingw* | cegcc*) + # MinGW DLLs use traditional 'lib' prefix + soname_spec='${libname}`echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext}' + ;; + pw32*) + # pw32 DLLs use 'pw' prefix rather than 'lib' + library_names_spec='`echo ${libname} | sed -e 's/^lib/pw/'``echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext}' + ;; + esac + dynamic_linker='Win32 ld.exe' + ;; + + *,cl*) + # Native MSVC + libname_spec='$name' + soname_spec='${libname}`echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext}' + library_names_spec='${libname}.dll.lib' + + case $build_os in + mingw*) + sys_lib_search_path_spec= + lt_save_ifs=$IFS + IFS=';' + for lt_path in $LIB + do + IFS=$lt_save_ifs + # Let DOS variable expansion print the short 8.3 style file name. + lt_path=`cd "$lt_path" 2>/dev/null && cmd //C "for %i in (".") do @echo %~si"` + sys_lib_search_path_spec="$sys_lib_search_path_spec $lt_path" + done + IFS=$lt_save_ifs + # Convert to MSYS style. + sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | sed -e 's|\\\\|/|g' -e 's| \\([a-zA-Z]\\):| /\\1|g' -e 's|^ ||'` + ;; + cygwin*) + # Convert to unix form, then to dos form, then back to unix form + # but this time dos style (no spaces!) so that the unix form looks + # like /cygdrive/c/PROGRA~1:/cygdr... + sys_lib_search_path_spec=`cygpath --path --unix "$LIB"` + sys_lib_search_path_spec=`cygpath --path --dos "$sys_lib_search_path_spec" 2>/dev/null` + sys_lib_search_path_spec=`cygpath --path --unix "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"` + ;; + *) + sys_lib_search_path_spec="$LIB" + if $ECHO "$sys_lib_search_path_spec" | $GREP ';[c-zC-Z]:/' >/dev/null; then + # It is most probably a Windows format PATH. + sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e 's/;/ /g'` + else + sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"` + fi + # FIXME: find the short name or the path components, as spaces are + # common. (e.g. "Program Files" -> "PROGRA~1") + ;; + esac + + # DLL is installed to $(libdir)/../bin by postinstall_cmds + postinstall_cmds='base_file=`basename \${file}`~ + dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\${base_file}'\''i; echo \$dlname'\''`~ + dldir=$destdir/`dirname \$dlpath`~ + test -d \$dldir || mkdir -p \$dldir~ + $install_prog $dir/$dlname \$dldir/$dlname' + postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~ + dlpath=$dir/\$dldll~ + $RM \$dlpath' + shlibpath_overrides_runpath=yes + dynamic_linker='Win32 link.exe' + ;; + + *) + # Assume MSVC wrapper + library_names_spec='${libname}`echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext} $libname.lib' + dynamic_linker='Win32 ld.exe' + ;; + esac + # FIXME: first we should search . and the directory the executable is in + shlibpath_var=PATH + ;; + +darwin* | rhapsody*) + dynamic_linker="$host_os dyld" + version_type=darwin + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${major}$shared_ext ${libname}$shared_ext' + soname_spec='${libname}${release}${major}$shared_ext' + shlibpath_overrides_runpath=yes + shlibpath_var=DYLD_LIBRARY_PATH + shrext_cmds='`test .$module = .yes && echo .so || echo .dylib`' + + sys_lib_search_path_spec="$sys_lib_search_path_spec /usr/local/lib" + sys_lib_dlsearch_path_spec='/usr/local/lib /lib /usr/lib' + ;; + +dgux*) + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname$shared_ext' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + ;; + +freebsd* | dragonfly*) + # DragonFly does not have aout. When/if they implement a new + # versioning mechanism, adjust this. + if test -x /usr/bin/objformat; then + objformat=`/usr/bin/objformat` + else + case $host_os in + freebsd[23].*) objformat=aout ;; + *) objformat=elf ;; + esac + fi + version_type=freebsd-$objformat + case $version_type in + freebsd-elf*) + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext} $libname${shared_ext}' + need_version=no + need_lib_prefix=no + ;; + freebsd-*) + library_names_spec='${libname}${release}${shared_ext}$versuffix $libname${shared_ext}$versuffix' + need_version=yes + ;; + esac + shlibpath_var=LD_LIBRARY_PATH + case $host_os in + freebsd2.*) + shlibpath_overrides_runpath=yes + ;; + freebsd3.[01]* | freebsdelf3.[01]*) + shlibpath_overrides_runpath=yes + hardcode_into_libs=yes + ;; + freebsd3.[2-9]* | freebsdelf3.[2-9]* | \ + freebsd4.[0-5] | freebsdelf4.[0-5] | freebsd4.1.1 | freebsdelf4.1.1) + shlibpath_overrides_runpath=no + hardcode_into_libs=yes + ;; + *) # from 4.6 on, and DragonFly + shlibpath_overrides_runpath=yes + hardcode_into_libs=yes + ;; + esac + ;; + +haiku*) + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + dynamic_linker="$host_os runtime_loader" + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}${major} ${libname}${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LIBRARY_PATH + shlibpath_overrides_runpath=yes + sys_lib_dlsearch_path_spec='/boot/home/config/lib /boot/common/lib /boot/system/lib' + hardcode_into_libs=yes + ;; + +hpux9* | hpux10* | hpux11*) + # Give a soname corresponding to the major version so that dld.sl refuses to + # link against other versions. + version_type=sunos + need_lib_prefix=no + need_version=no + case $host_cpu in + ia64*) + shrext_cmds='.so' + hardcode_into_libs=yes + dynamic_linker="$host_os dld.so" + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes # Unless +noenvvar is specified. + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + if test "X$HPUX_IA64_MODE" = X32; then + sys_lib_search_path_spec="/usr/lib/hpux32 /usr/local/lib/hpux32 /usr/local/lib" + else + sys_lib_search_path_spec="/usr/lib/hpux64 /usr/local/lib/hpux64" + fi + sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec + ;; + hppa*64*) + shrext_cmds='.sl' + hardcode_into_libs=yes + dynamic_linker="$host_os dld.sl" + shlibpath_var=LD_LIBRARY_PATH # How should we handle SHLIB_PATH + shlibpath_overrides_runpath=yes # Unless +noenvvar is specified. + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + sys_lib_search_path_spec="/usr/lib/pa20_64 /usr/ccs/lib/pa20_64" + sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec + ;; + *) + shrext_cmds='.sl' + dynamic_linker="$host_os dld.sl" + shlibpath_var=SHLIB_PATH + shlibpath_overrides_runpath=no # +s is required to enable SHLIB_PATH + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + ;; + esac + # HP-UX runs *really* slowly unless shared libraries are mode 555, ... + postinstall_cmds='chmod 555 $lib' + # or fails outright, so override atomically: + install_override_mode=555 + ;; + +interix[3-9]*) + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + dynamic_linker='Interix 3.x ld.so.1 (PE, like ELF)' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=no + hardcode_into_libs=yes + ;; + +irix5* | irix6* | nonstopux*) + case $host_os in + nonstopux*) version_type=nonstopux ;; + *) + if test "$lt_cv_prog_gnu_ld" = yes; then + version_type=linux # correct to gnu/linux during the next big refactor + else + version_type=irix + fi ;; + esac + need_lib_prefix=no + need_version=no + soname_spec='${libname}${release}${shared_ext}$major' + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${release}${shared_ext} $libname${shared_ext}' + case $host_os in + irix5* | nonstopux*) + libsuff= shlibsuff= + ;; + *) + case $LD in # libtool.m4 will add one of these switches to LD + *-32|*"-32 "|*-melf32bsmip|*"-melf32bsmip ") + libsuff= shlibsuff= libmagic=32-bit;; + *-n32|*"-n32 "|*-melf32bmipn32|*"-melf32bmipn32 ") + libsuff=32 shlibsuff=N32 libmagic=N32;; + *-64|*"-64 "|*-melf64bmip|*"-melf64bmip ") + libsuff=64 shlibsuff=64 libmagic=64-bit;; + *) libsuff= shlibsuff= libmagic=never-match;; + esac + ;; + esac + shlibpath_var=LD_LIBRARY${shlibsuff}_PATH + shlibpath_overrides_runpath=no + sys_lib_search_path_spec="/usr/lib${libsuff} /lib${libsuff} /usr/local/lib${libsuff}" + sys_lib_dlsearch_path_spec="/usr/lib${libsuff} /lib${libsuff}" + hardcode_into_libs=yes + ;; + +# No shared lib support for Linux oldld, aout, or coff. +linux*oldld* | linux*aout* | linux*coff*) + dynamic_linker=no + ;; + +# This must be glibc/ELF. +linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + finish_cmds='PATH="\$PATH:/sbin" ldconfig -n $libdir' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=no + + # Some binutils ld are patched to set DT_RUNPATH + if ${lt_cv_shlibpath_overrides_runpath+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_shlibpath_overrides_runpath=no + save_LDFLAGS=$LDFLAGS + save_libdir=$libdir + eval "libdir=/foo; wl=\"$lt_prog_compiler_wl\"; \ + LDFLAGS=\"\$LDFLAGS $hardcode_libdir_flag_spec\"" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + if ($OBJDUMP -p conftest$ac_exeext) 2>/dev/null | grep "RUNPATH.*$libdir" >/dev/null; then : + lt_cv_shlibpath_overrides_runpath=yes +fi +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + LDFLAGS=$save_LDFLAGS + libdir=$save_libdir + +fi + + shlibpath_overrides_runpath=$lt_cv_shlibpath_overrides_runpath + + # This implies no fast_install, which is unacceptable. + # Some rework will be needed to allow for fast_install + # before this can be enabled. + hardcode_into_libs=yes + + # Append ld.so.conf contents to the search path + if test -f /etc/ld.so.conf; then + lt_ld_extra=`awk '/^include / { system(sprintf("cd /etc; cat %s 2>/dev/null", \$2)); skip = 1; } { if (!skip) print \$0; skip = 0; }' < /etc/ld.so.conf | $SED -e 's/#.*//;/^[ ]*hwcap[ ]/d;s/[:, ]/ /g;s/=[^=]*$//;s/=[^= ]* / /g;s/"//g;/^$/d' | tr '\n' ' '` + sys_lib_dlsearch_path_spec="/lib /usr/lib $lt_ld_extra" + fi + + # We used to test for /lib/ld.so.1 and disable shared libraries on + # powerpc, because MkLinux only supported shared libraries with the + # GNU dynamic linker. Since this was broken with cross compilers, + # most powerpc-linux boxes support dynamic linking these days and + # people can always --disable-shared, the test was removed, and we + # assume the GNU/Linux dynamic linker is in use. + dynamic_linker='GNU/Linux ld.so' + ;; + +netbsdelf*-gnu) + version_type=linux + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=no + hardcode_into_libs=yes + dynamic_linker='NetBSD ld.elf_so' + ;; + +netbsd*) + version_type=sunos + need_lib_prefix=no + need_version=no + if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix' + finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' + dynamic_linker='NetBSD (a.out) ld.so' + else + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + dynamic_linker='NetBSD ld.elf_so' + fi + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + hardcode_into_libs=yes + ;; + +newsos6) + version_type=linux # correct to gnu/linux during the next big refactor + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + ;; + +*nto* | *qnx*) + version_type=qnx + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=no + hardcode_into_libs=yes + dynamic_linker='ldqnx.so' + ;; + +openbsd*) + version_type=sunos + sys_lib_dlsearch_path_spec="/usr/lib" + need_lib_prefix=no + # Some older versions of OpenBSD (3.3 at least) *do* need versioned libs. + case $host_os in + openbsd3.3 | openbsd3.3.*) need_version=yes ;; + *) need_version=no ;; + esac + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix' + finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' + shlibpath_var=LD_LIBRARY_PATH + if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then + case $host_os in + openbsd2.[89] | openbsd2.[89].*) + shlibpath_overrides_runpath=no + ;; + *) + shlibpath_overrides_runpath=yes + ;; + esac + else + shlibpath_overrides_runpath=yes + fi + ;; + +os2*) + libname_spec='$name' + shrext_cmds=".dll" + need_lib_prefix=no + library_names_spec='$libname${shared_ext} $libname.a' + dynamic_linker='OS/2 ld.exe' + shlibpath_var=LIBPATH + ;; + +osf3* | osf4* | osf5*) + version_type=osf + need_lib_prefix=no + need_version=no + soname_spec='${libname}${release}${shared_ext}$major' + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + shlibpath_var=LD_LIBRARY_PATH + sys_lib_search_path_spec="/usr/shlib /usr/ccs/lib /usr/lib/cmplrs/cc /usr/lib /usr/local/lib /var/shlib" + sys_lib_dlsearch_path_spec="$sys_lib_search_path_spec" + ;; + +rdos*) + dynamic_linker=no + ;; + +solaris*) + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + hardcode_into_libs=yes + # ldd complains unless libraries are executable + postinstall_cmds='chmod +x $lib' + ;; + +sunos4*) + version_type=sunos + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix' + finish_cmds='PATH="\$PATH:/usr/etc" ldconfig $libdir' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + if test "$with_gnu_ld" = yes; then + need_lib_prefix=no + fi + need_version=yes + ;; + +sysv4 | sysv4.3*) + version_type=linux # correct to gnu/linux during the next big refactor + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + case $host_vendor in + sni) + shlibpath_overrides_runpath=no + need_lib_prefix=no + runpath_var=LD_RUN_PATH + ;; + siemens) + need_lib_prefix=no + ;; + motorola) + need_lib_prefix=no + need_version=no + shlibpath_overrides_runpath=no + sys_lib_search_path_spec='/lib /usr/lib /usr/ccs/lib' + ;; + esac + ;; + +sysv4*MP*) + if test -d /usr/nec ;then + version_type=linux # correct to gnu/linux during the next big refactor + library_names_spec='$libname${shared_ext}.$versuffix $libname${shared_ext}.$major $libname${shared_ext}' + soname_spec='$libname${shared_ext}.$major' + shlibpath_var=LD_LIBRARY_PATH + fi + ;; + +sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*) + version_type=freebsd-elf + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext} $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + hardcode_into_libs=yes + if test "$with_gnu_ld" = yes; then + sys_lib_search_path_spec='/usr/local/lib /usr/gnu/lib /usr/ccs/lib /usr/lib /lib' + else + sys_lib_search_path_spec='/usr/ccs/lib /usr/lib' + case $host_os in + sco3.2v5*) + sys_lib_search_path_spec="$sys_lib_search_path_spec /lib" + ;; + esac + fi + sys_lib_dlsearch_path_spec='/usr/lib' + ;; + +tpf*) + # TPF is a cross-target only. Preferred cross-host = GNU/Linux. + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=no + hardcode_into_libs=yes + ;; + +uts4*) + version_type=linux # correct to gnu/linux during the next big refactor + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + ;; + +*) + dynamic_linker=no + ;; +esac +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $dynamic_linker" >&5 +$as_echo "$dynamic_linker" >&6; } +test "$dynamic_linker" = no && can_build_shared=no + +variables_saved_for_relink="PATH $shlibpath_var $runpath_var" +if test "$GCC" = yes; then + variables_saved_for_relink="$variables_saved_for_relink GCC_EXEC_PREFIX COMPILER_PATH LIBRARY_PATH" +fi + +if test "${lt_cv_sys_lib_search_path_spec+set}" = set; then + sys_lib_search_path_spec="$lt_cv_sys_lib_search_path_spec" +fi +if test "${lt_cv_sys_lib_dlsearch_path_spec+set}" = set; then + sys_lib_dlsearch_path_spec="$lt_cv_sys_lib_dlsearch_path_spec" +fi + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to hardcode library paths into programs" >&5 +$as_echo_n "checking how to hardcode library paths into programs... " >&6; } +hardcode_action= +if test -n "$hardcode_libdir_flag_spec" || + test -n "$runpath_var" || + test "X$hardcode_automatic" = "Xyes" ; then + + # We can hardcode non-existent directories. + if test "$hardcode_direct" != no && + # If the only mechanism to avoid hardcoding is shlibpath_var, we + # have to relink, otherwise we might link with an installed library + # when we should be linking with a yet-to-be-installed one + ## test "$_LT_TAGVAR(hardcode_shlibpath_var, )" != no && + test "$hardcode_minus_L" != no; then + # Linking always hardcodes the temporary library directory. + hardcode_action=relink + else + # We can link without hardcoding, and we can hardcode nonexisting dirs. + hardcode_action=immediate + fi +else + # We cannot hardcode anything, or else we can only hardcode existing + # directories. + hardcode_action=unsupported +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $hardcode_action" >&5 +$as_echo "$hardcode_action" >&6; } + +if test "$hardcode_action" = relink || + test "$inherit_rpath" = yes; then + # Fast installation is not supported + enable_fast_install=no +elif test "$shlibpath_overrides_runpath" = yes || + test "$enable_shared" = no; then + # Fast installation is not necessary + enable_fast_install=needless +fi + + + + + + + if test "x$enable_dlopen" != xyes; then + enable_dlopen=unknown + enable_dlopen_self=unknown + enable_dlopen_self_static=unknown +else + lt_cv_dlopen=no + lt_cv_dlopen_libs= + + case $host_os in + beos*) + lt_cv_dlopen="load_add_on" + lt_cv_dlopen_libs= + lt_cv_dlopen_self=yes + ;; + + mingw* | pw32* | cegcc*) + lt_cv_dlopen="LoadLibrary" + lt_cv_dlopen_libs= + ;; + + cygwin*) + lt_cv_dlopen="dlopen" + lt_cv_dlopen_libs= + ;; + + darwin*) + # if libdl is installed we need to link against it + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for dlopen in -ldl" >&5 +$as_echo_n "checking for dlopen in -ldl... " >&6; } +if ${ac_cv_lib_dl_dlopen+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-ldl $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char dlopen (); +int +main () +{ +return dlopen (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_dl_dlopen=yes +else + ac_cv_lib_dl_dlopen=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dl_dlopen" >&5 +$as_echo "$ac_cv_lib_dl_dlopen" >&6; } +if test "x$ac_cv_lib_dl_dlopen" = xyes; then : + lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-ldl" +else + + lt_cv_dlopen="dyld" + lt_cv_dlopen_libs= + lt_cv_dlopen_self=yes + +fi + + ;; + + *) + ac_fn_c_check_func "$LINENO" "shl_load" "ac_cv_func_shl_load" +if test "x$ac_cv_func_shl_load" = xyes; then : + lt_cv_dlopen="shl_load" +else + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for shl_load in -ldld" >&5 +$as_echo_n "checking for shl_load in -ldld... " >&6; } +if ${ac_cv_lib_dld_shl_load+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-ldld $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char shl_load (); +int +main () +{ +return shl_load (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_dld_shl_load=yes +else + ac_cv_lib_dld_shl_load=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dld_shl_load" >&5 +$as_echo "$ac_cv_lib_dld_shl_load" >&6; } +if test "x$ac_cv_lib_dld_shl_load" = xyes; then : + lt_cv_dlopen="shl_load" lt_cv_dlopen_libs="-ldld" +else + ac_fn_c_check_func "$LINENO" "dlopen" "ac_cv_func_dlopen" +if test "x$ac_cv_func_dlopen" = xyes; then : + lt_cv_dlopen="dlopen" +else + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for dlopen in -ldl" >&5 +$as_echo_n "checking for dlopen in -ldl... " >&6; } +if ${ac_cv_lib_dl_dlopen+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-ldl $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char dlopen (); +int +main () +{ +return dlopen (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_dl_dlopen=yes +else + ac_cv_lib_dl_dlopen=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dl_dlopen" >&5 +$as_echo "$ac_cv_lib_dl_dlopen" >&6; } +if test "x$ac_cv_lib_dl_dlopen" = xyes; then : + lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-ldl" +else + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for dlopen in -lsvld" >&5 +$as_echo_n "checking for dlopen in -lsvld... " >&6; } +if ${ac_cv_lib_svld_dlopen+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lsvld $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char dlopen (); +int +main () +{ +return dlopen (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_svld_dlopen=yes +else + ac_cv_lib_svld_dlopen=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_svld_dlopen" >&5 +$as_echo "$ac_cv_lib_svld_dlopen" >&6; } +if test "x$ac_cv_lib_svld_dlopen" = xyes; then : + lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-lsvld" +else + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for dld_link in -ldld" >&5 +$as_echo_n "checking for dld_link in -ldld... " >&6; } +if ${ac_cv_lib_dld_dld_link+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-ldld $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char dld_link (); +int +main () +{ +return dld_link (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_dld_dld_link=yes +else + ac_cv_lib_dld_dld_link=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dld_dld_link" >&5 +$as_echo "$ac_cv_lib_dld_dld_link" >&6; } +if test "x$ac_cv_lib_dld_dld_link" = xyes; then : + lt_cv_dlopen="dld_link" lt_cv_dlopen_libs="-ldld" +fi + + +fi + + +fi + + +fi + + +fi + + +fi + + ;; + esac + + if test "x$lt_cv_dlopen" != xno; then + enable_dlopen=yes + else + enable_dlopen=no + fi + + case $lt_cv_dlopen in + dlopen) + save_CPPFLAGS="$CPPFLAGS" + test "x$ac_cv_header_dlfcn_h" = xyes && CPPFLAGS="$CPPFLAGS -DHAVE_DLFCN_H" + + save_LDFLAGS="$LDFLAGS" + wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $export_dynamic_flag_spec\" + + save_LIBS="$LIBS" + LIBS="$lt_cv_dlopen_libs $LIBS" + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether a program can dlopen itself" >&5 +$as_echo_n "checking whether a program can dlopen itself... " >&6; } +if ${lt_cv_dlopen_self+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test "$cross_compiling" = yes; then : + lt_cv_dlopen_self=cross +else + lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 + lt_status=$lt_dlunknown + cat > conftest.$ac_ext <<_LT_EOF +#line $LINENO "configure" +#include "confdefs.h" + +#if HAVE_DLFCN_H +#include +#endif + +#include + +#ifdef RTLD_GLOBAL +# define LT_DLGLOBAL RTLD_GLOBAL +#else +# ifdef DL_GLOBAL +# define LT_DLGLOBAL DL_GLOBAL +# else +# define LT_DLGLOBAL 0 +# endif +#endif + +/* We may have to define LT_DLLAZY_OR_NOW in the command line if we + find out it does not work in some platform. */ +#ifndef LT_DLLAZY_OR_NOW +# ifdef RTLD_LAZY +# define LT_DLLAZY_OR_NOW RTLD_LAZY +# else +# ifdef DL_LAZY +# define LT_DLLAZY_OR_NOW DL_LAZY +# else +# ifdef RTLD_NOW +# define LT_DLLAZY_OR_NOW RTLD_NOW +# else +# ifdef DL_NOW +# define LT_DLLAZY_OR_NOW DL_NOW +# else +# define LT_DLLAZY_OR_NOW 0 +# endif +# endif +# endif +# endif +#endif + +/* When -fvisbility=hidden is used, assume the code has been annotated + correspondingly for the symbols needed. */ +#if defined(__GNUC__) && (((__GNUC__ == 3) && (__GNUC_MINOR__ >= 3)) || (__GNUC__ > 3)) +int fnord () __attribute__((visibility("default"))); +#endif + +int fnord () { return 42; } +int main () +{ + void *self = dlopen (0, LT_DLGLOBAL|LT_DLLAZY_OR_NOW); + int status = $lt_dlunknown; + + if (self) + { + if (dlsym (self,"fnord")) status = $lt_dlno_uscore; + else + { + if (dlsym( self,"_fnord")) status = $lt_dlneed_uscore; + else puts (dlerror ()); + } + /* dlclose (self); */ + } + else + puts (dlerror ()); + + return status; +} +_LT_EOF + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_link\""; } >&5 + (eval $ac_link) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && test -s conftest${ac_exeext} 2>/dev/null; then + (./conftest; exit; ) >&5 2>/dev/null + lt_status=$? + case x$lt_status in + x$lt_dlno_uscore) lt_cv_dlopen_self=yes ;; + x$lt_dlneed_uscore) lt_cv_dlopen_self=yes ;; + x$lt_dlunknown|x*) lt_cv_dlopen_self=no ;; + esac + else : + # compilation failed + lt_cv_dlopen_self=no + fi +fi +rm -fr conftest* + + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_dlopen_self" >&5 +$as_echo "$lt_cv_dlopen_self" >&6; } + + if test "x$lt_cv_dlopen_self" = xyes; then + wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $lt_prog_compiler_static\" + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether a statically linked program can dlopen itself" >&5 +$as_echo_n "checking whether a statically linked program can dlopen itself... " >&6; } +if ${lt_cv_dlopen_self_static+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test "$cross_compiling" = yes; then : + lt_cv_dlopen_self_static=cross +else + lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 + lt_status=$lt_dlunknown + cat > conftest.$ac_ext <<_LT_EOF +#line $LINENO "configure" +#include "confdefs.h" + +#if HAVE_DLFCN_H +#include +#endif + +#include + +#ifdef RTLD_GLOBAL +# define LT_DLGLOBAL RTLD_GLOBAL +#else +# ifdef DL_GLOBAL +# define LT_DLGLOBAL DL_GLOBAL +# else +# define LT_DLGLOBAL 0 +# endif +#endif + +/* We may have to define LT_DLLAZY_OR_NOW in the command line if we + find out it does not work in some platform. */ +#ifndef LT_DLLAZY_OR_NOW +# ifdef RTLD_LAZY +# define LT_DLLAZY_OR_NOW RTLD_LAZY +# else +# ifdef DL_LAZY +# define LT_DLLAZY_OR_NOW DL_LAZY +# else +# ifdef RTLD_NOW +# define LT_DLLAZY_OR_NOW RTLD_NOW +# else +# ifdef DL_NOW +# define LT_DLLAZY_OR_NOW DL_NOW +# else +# define LT_DLLAZY_OR_NOW 0 +# endif +# endif +# endif +# endif +#endif + +/* When -fvisbility=hidden is used, assume the code has been annotated + correspondingly for the symbols needed. */ +#if defined(__GNUC__) && (((__GNUC__ == 3) && (__GNUC_MINOR__ >= 3)) || (__GNUC__ > 3)) +int fnord () __attribute__((visibility("default"))); +#endif + +int fnord () { return 42; } +int main () +{ + void *self = dlopen (0, LT_DLGLOBAL|LT_DLLAZY_OR_NOW); + int status = $lt_dlunknown; + + if (self) + { + if (dlsym (self,"fnord")) status = $lt_dlno_uscore; + else + { + if (dlsym( self,"_fnord")) status = $lt_dlneed_uscore; + else puts (dlerror ()); + } + /* dlclose (self); */ + } + else + puts (dlerror ()); + + return status; +} +_LT_EOF + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_link\""; } >&5 + (eval $ac_link) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && test -s conftest${ac_exeext} 2>/dev/null; then + (./conftest; exit; ) >&5 2>/dev/null + lt_status=$? + case x$lt_status in + x$lt_dlno_uscore) lt_cv_dlopen_self_static=yes ;; + x$lt_dlneed_uscore) lt_cv_dlopen_self_static=yes ;; + x$lt_dlunknown|x*) lt_cv_dlopen_self_static=no ;; + esac + else : + # compilation failed + lt_cv_dlopen_self_static=no + fi +fi +rm -fr conftest* + + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_dlopen_self_static" >&5 +$as_echo "$lt_cv_dlopen_self_static" >&6; } + fi + + CPPFLAGS="$save_CPPFLAGS" + LDFLAGS="$save_LDFLAGS" + LIBS="$save_LIBS" + ;; + esac + + case $lt_cv_dlopen_self in + yes|no) enable_dlopen_self=$lt_cv_dlopen_self ;; + *) enable_dlopen_self=unknown ;; + esac + + case $lt_cv_dlopen_self_static in + yes|no) enable_dlopen_self_static=$lt_cv_dlopen_self_static ;; + *) enable_dlopen_self_static=unknown ;; + esac +fi + + + + + + + + + + + + + + + + + +striplib= +old_striplib= +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether stripping libraries is possible" >&5 +$as_echo_n "checking whether stripping libraries is possible... " >&6; } +if test -n "$STRIP" && $STRIP -V 2>&1 | $GREP "GNU strip" >/dev/null; then + test -z "$old_striplib" && old_striplib="$STRIP --strip-debug" + test -z "$striplib" && striplib="$STRIP --strip-unneeded" + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } +else +# FIXME - insert some real tests, host_os isn't really good enough + case $host_os in + darwin*) + if test -n "$STRIP" ; then + striplib="$STRIP -x" + old_striplib="$STRIP -S" + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + fi + ;; + *) + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + ;; + esac +fi + + + + + + + + + + + + + # Report which library types will actually be built + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if libtool supports shared libraries" >&5 +$as_echo_n "checking if libtool supports shared libraries... " >&6; } + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $can_build_shared" >&5 +$as_echo "$can_build_shared" >&6; } + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to build shared libraries" >&5 +$as_echo_n "checking whether to build shared libraries... " >&6; } + test "$can_build_shared" = "no" && enable_shared=no + + # On AIX, shared libraries and static libraries use the same namespace, and + # are all built from PIC. + case $host_os in + aix3*) + test "$enable_shared" = yes && enable_static=no + if test -n "$RANLIB"; then + archive_cmds="$archive_cmds~\$RANLIB \$lib" + postinstall_cmds='$RANLIB $lib' + fi + ;; + + aix[4-9]*) + if test "$host_cpu" != ia64 && test "$aix_use_runtimelinking" = no ; then + test "$enable_shared" = yes && enable_static=no + fi + ;; + esac + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $enable_shared" >&5 +$as_echo "$enable_shared" >&6; } + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to build static libraries" >&5 +$as_echo_n "checking whether to build static libraries... " >&6; } + # Make sure either enable_shared or enable_static is yes. + test "$enable_shared" = yes || enable_static=yes + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $enable_static" >&5 +$as_echo "$enable_static" >&6; } + + + + +fi +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + +CC="$lt_save_CC" + + + + + + + + + + + + + + + + ac_config_commands="$ac_config_commands libtool" + + + + +# Only expand once: + + + + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking target system type" >&5 +$as_echo_n "checking target system type... " >&6; } +if ${ac_cv_target+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test "x$target_alias" = x; then + ac_cv_target=$ac_cv_host +else + ac_cv_target=`$SHELL "$ac_aux_dir/config.sub" $target_alias` || + as_fn_error $? "$SHELL $ac_aux_dir/config.sub $target_alias failed" "$LINENO" 5 +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_target" >&5 +$as_echo "$ac_cv_target" >&6; } +case $ac_cv_target in +*-*-*) ;; +*) as_fn_error $? "invalid value of canonical target" "$LINENO" 5;; +esac +target=$ac_cv_target +ac_save_IFS=$IFS; IFS='-' +set x $ac_cv_target +shift +target_cpu=$1 +target_vendor=$2 +shift; shift +# Remember, the first character of IFS is used to create $*, +# except with old shells: +target_os=$* +IFS=$ac_save_IFS +case $target_os in *\ *) target_os=`echo "$target_os" | sed 's/ /-/g'`;; esac + + +# The aliases save the names the user supplied, while $host etc. +# will get canonicalized. +test -n "$target_alias" && + test "$program_prefix$program_suffix$program_transform_name" = \ + NONENONEs,x,x, && + program_prefix=${target_alias}- + +am__api_version='1.14' + +# Find a good install program. We prefer a C program (faster), +# so one script is as good as another. But avoid the broken or +# incompatible versions: +# SysV /etc/install, /usr/sbin/install +# SunOS /usr/etc/install +# IRIX /sbin/install +# AIX /bin/install +# AmigaOS /C/install, which installs bootblocks on floppy discs +# AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag +# AFS /usr/afsws/bin/install, which mishandles nonexistent args +# SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff" +# OS/2's system install, which has a completely different semantic +# ./install, which can be erroneously created by make from ./install.sh. +# Reject install programs that cannot install multiple files. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for a BSD-compatible install" >&5 +$as_echo_n "checking for a BSD-compatible install... " >&6; } +if test -z "$INSTALL"; then +if ${ac_cv_path_install+:} false; then : + $as_echo_n "(cached) " >&6 +else + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + # Account for people who put trailing slashes in PATH elements. +case $as_dir/ in #(( + ./ | .// | /[cC]/* | \ + /etc/* | /usr/sbin/* | /usr/etc/* | /sbin/* | /usr/afsws/bin/* | \ + ?:[\\/]os2[\\/]install[\\/]* | ?:[\\/]OS2[\\/]INSTALL[\\/]* | \ + /usr/ucb/* ) ;; + *) + # OSF1 and SCO ODT 3.0 have their own names for install. + # Don't use installbsd from OSF since it installs stuff as root + # by default. + for ac_prog in ginstall scoinst install; do + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_prog$ac_exec_ext"; then + if test $ac_prog = install && + grep dspmsg "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then + # AIX install. It has an incompatible calling convention. + : + elif test $ac_prog = install && + grep pwplus "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then + # program-specific install script used by HP pwplus--don't use. + : + else + rm -rf conftest.one conftest.two conftest.dir + echo one > conftest.one + echo two > conftest.two + mkdir conftest.dir + if "$as_dir/$ac_prog$ac_exec_ext" -c conftest.one conftest.two "`pwd`/conftest.dir" && + test -s conftest.one && test -s conftest.two && + test -s conftest.dir/conftest.one && + test -s conftest.dir/conftest.two + then + ac_cv_path_install="$as_dir/$ac_prog$ac_exec_ext -c" + break 3 + fi + fi + fi + done + done + ;; +esac + + done +IFS=$as_save_IFS + +rm -rf conftest.one conftest.two conftest.dir + +fi + if test "${ac_cv_path_install+set}" = set; then + INSTALL=$ac_cv_path_install + else + # As a last resort, use the slow shell script. Don't cache a + # value for INSTALL within a source directory, because that will + # break other packages using the cache if that directory is + # removed, or if the value is a relative name. + INSTALL=$ac_install_sh + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $INSTALL" >&5 +$as_echo "$INSTALL" >&6; } + +# Use test -z because SunOS4 sh mishandles braces in ${var-val}. +# It thinks the first close brace ends the variable substitution. +test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}' + +test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL}' + +test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644' + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether build environment is sane" >&5 +$as_echo_n "checking whether build environment is sane... " >&6; } +# Reject unsafe characters in $srcdir or the absolute working directory +# name. Accept space and tab only in the latter. +am_lf=' +' +case `pwd` in + *[\\\"\#\$\&\'\`$am_lf]*) + as_fn_error $? "unsafe absolute working directory name" "$LINENO" 5;; +esac +case $srcdir in + *[\\\"\#\$\&\'\`$am_lf\ \ ]*) + as_fn_error $? "unsafe srcdir value: '$srcdir'" "$LINENO" 5;; +esac + +# Do 'set' in a subshell so we don't clobber the current shell's +# arguments. Must try -L first in case configure is actually a +# symlink; some systems play weird games with the mod time of symlinks +# (eg FreeBSD returns the mod time of the symlink's containing +# directory). +if ( + am_has_slept=no + for am_try in 1 2; do + echo "timestamp, slept: $am_has_slept" > conftest.file + set X `ls -Lt "$srcdir/configure" conftest.file 2> /dev/null` + if test "$*" = "X"; then + # -L didn't work. + set X `ls -t "$srcdir/configure" conftest.file` + fi + if test "$*" != "X $srcdir/configure conftest.file" \ + && test "$*" != "X conftest.file $srcdir/configure"; then + + # If neither matched, then we have a broken ls. This can happen + # if, for instance, CONFIG_SHELL is bash and it inherits a + # broken ls alias from the environment. This has actually + # happened. Such a system could not be considered "sane". + as_fn_error $? "ls -t appears to fail. Make sure there is not a broken + alias in your environment" "$LINENO" 5 + fi + if test "$2" = conftest.file || test $am_try -eq 2; then + break + fi + # Just in case. + sleep 1 + am_has_slept=yes + done + test "$2" = conftest.file + ) +then + # Ok. + : +else + as_fn_error $? "newly created file is older than distributed files! +Check your system clock" "$LINENO" 5 +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } +# If we didn't sleep, we still need to ensure time stamps of config.status and +# generated files are strictly newer. +am_sleep_pid= +if grep 'slept: no' conftest.file >/dev/null 2>&1; then + ( sleep 1 ) & + am_sleep_pid=$! +fi + +rm -f conftest.file + +test "$program_prefix" != NONE && + program_transform_name="s&^&$program_prefix&;$program_transform_name" +# Use a double $ so make ignores it. +test "$program_suffix" != NONE && + program_transform_name="s&\$&$program_suffix&;$program_transform_name" +# Double any \ or $. +# By default was `s,x,x', remove it if useless. +ac_script='s/[\\$]/&&/g;s/;s,x,x,$//' +program_transform_name=`$as_echo "$program_transform_name" | sed "$ac_script"` + +if test x"${MISSING+set}" != xset; then + case $am_aux_dir in + *\ * | *\ *) + MISSING="\${SHELL} \"$am_aux_dir/missing\"" ;; + *) + MISSING="\${SHELL} $am_aux_dir/missing" ;; + esac +fi +# Use eval to expand $SHELL +if eval "$MISSING --is-lightweight"; then + am_missing_run="$MISSING " +else + am_missing_run= + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: 'missing' script is too old or missing" >&5 +$as_echo "$as_me: WARNING: 'missing' script is too old or missing" >&2;} +fi + +if test x"${install_sh}" != xset; then + case $am_aux_dir in + *\ * | *\ *) + install_sh="\${SHELL} '$am_aux_dir/install-sh'" ;; + *) + install_sh="\${SHELL} $am_aux_dir/install-sh" + esac +fi + +# Installed binaries are usually stripped using 'strip' when the user +# run "make install-strip". However 'strip' might not be the right +# tool to use in cross-compilation environments, therefore Automake +# will honor the 'STRIP' environment variable to overrule this program. +if test "$cross_compiling" != no; then + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}strip", so it can be a program name with args. +set dummy ${ac_tool_prefix}strip; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_STRIP+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$STRIP"; then + ac_cv_prog_STRIP="$STRIP" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_STRIP="${ac_tool_prefix}strip" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +STRIP=$ac_cv_prog_STRIP +if test -n "$STRIP"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $STRIP" >&5 +$as_echo "$STRIP" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_STRIP"; then + ac_ct_STRIP=$STRIP + # Extract the first word of "strip", so it can be a program name with args. +set dummy strip; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_STRIP+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_STRIP"; then + ac_cv_prog_ac_ct_STRIP="$ac_ct_STRIP" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_STRIP="strip" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_STRIP=$ac_cv_prog_ac_ct_STRIP +if test -n "$ac_ct_STRIP"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_STRIP" >&5 +$as_echo "$ac_ct_STRIP" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_STRIP" = x; then + STRIP=":" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + STRIP=$ac_ct_STRIP + fi +else + STRIP="$ac_cv_prog_STRIP" +fi + +fi +INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s" + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for a thread-safe mkdir -p" >&5 +$as_echo_n "checking for a thread-safe mkdir -p... " >&6; } +if test -z "$MKDIR_P"; then + if ${ac_cv_path_mkdir+:} false; then : + $as_echo_n "(cached) " >&6 +else + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH$PATH_SEPARATOR/opt/sfw/bin +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_prog in mkdir gmkdir; do + for ac_exec_ext in '' $ac_executable_extensions; do + as_fn_executable_p "$as_dir/$ac_prog$ac_exec_ext" || continue + case `"$as_dir/$ac_prog$ac_exec_ext" --version 2>&1` in #( + 'mkdir (GNU coreutils) '* | \ + 'mkdir (coreutils) '* | \ + 'mkdir (fileutils) '4.1*) + ac_cv_path_mkdir=$as_dir/$ac_prog$ac_exec_ext + break 3;; + esac + done + done + done +IFS=$as_save_IFS + +fi + + test -d ./--version && rmdir ./--version + if test "${ac_cv_path_mkdir+set}" = set; then + MKDIR_P="$ac_cv_path_mkdir -p" + else + # As a last resort, use the slow shell script. Don't cache a + # value for MKDIR_P within a source directory, because that will + # break other packages using the cache if that directory is + # removed, or if the value is a relative name. + MKDIR_P="$ac_install_sh -d" + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $MKDIR_P" >&5 +$as_echo "$MKDIR_P" >&6; } + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ${MAKE-make} sets \$(MAKE)" >&5 +$as_echo_n "checking whether ${MAKE-make} sets \$(MAKE)... " >&6; } +set x ${MAKE-make} +ac_make=`$as_echo "$2" | sed 's/+/p/g; s/[^a-zA-Z0-9_]/_/g'` +if eval \${ac_cv_prog_make_${ac_make}_set+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat >conftest.make <<\_ACEOF +SHELL = /bin/sh +all: + @echo '@@@%%%=$(MAKE)=@@@%%%' +_ACEOF +# GNU make sometimes prints "make[1]: Entering ...", which would confuse us. +case `${MAKE-make} -f conftest.make 2>/dev/null` in + *@@@%%%=?*=@@@%%%*) + eval ac_cv_prog_make_${ac_make}_set=yes;; + *) + eval ac_cv_prog_make_${ac_make}_set=no;; +esac +rm -f conftest.make +fi +if eval test \$ac_cv_prog_make_${ac_make}_set = yes; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + SET_MAKE= +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + SET_MAKE="MAKE=${MAKE-make}" +fi + +rm -rf .tst 2>/dev/null +mkdir .tst 2>/dev/null +if test -d .tst; then + am__leading_dot=. +else + am__leading_dot=_ +fi +rmdir .tst 2>/dev/null + +DEPDIR="${am__leading_dot}deps" + +ac_config_commands="$ac_config_commands depfiles" + + +am_make=${MAKE-make} +cat > confinc << 'END' +am__doit: + @echo this is the am__doit target +.PHONY: am__doit +END +# If we don't find an include directive, just comment out the code. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for style of include used by $am_make" >&5 +$as_echo_n "checking for style of include used by $am_make... " >&6; } +am__include="#" +am__quote= +_am_result=none +# First try GNU make style include. +echo "include confinc" > confmf +# Ignore all kinds of additional output from 'make'. +case `$am_make -s -f confmf 2> /dev/null` in #( +*the\ am__doit\ target*) + am__include=include + am__quote= + _am_result=GNU + ;; +esac +# Now try BSD make style include. +if test "$am__include" = "#"; then + echo '.include "confinc"' > confmf + case `$am_make -s -f confmf 2> /dev/null` in #( + *the\ am__doit\ target*) + am__include=.include + am__quote="\"" + _am_result=BSD + ;; + esac +fi + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $_am_result" >&5 +$as_echo "$_am_result" >&6; } +rm -f confinc confmf + +# Check whether --enable-dependency-tracking was given. +if test "${enable_dependency_tracking+set}" = set; then : + enableval=$enable_dependency_tracking; +fi + +if test "x$enable_dependency_tracking" != xno; then + am_depcomp="$ac_aux_dir/depcomp" + AMDEPBACKSLASH='\' + am__nodep='_no' +fi + if test "x$enable_dependency_tracking" != xno; then + AMDEP_TRUE= + AMDEP_FALSE='#' +else + AMDEP_TRUE='#' + AMDEP_FALSE= +fi + + +# Check whether --enable-silent-rules was given. +if test "${enable_silent_rules+set}" = set; then : + enableval=$enable_silent_rules; +fi + +case $enable_silent_rules in # ((( + yes) AM_DEFAULT_VERBOSITY=0;; + no) AM_DEFAULT_VERBOSITY=1;; + *) AM_DEFAULT_VERBOSITY=1;; +esac +am_make=${MAKE-make} +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $am_make supports nested variables" >&5 +$as_echo_n "checking whether $am_make supports nested variables... " >&6; } +if ${am_cv_make_support_nested_variables+:} false; then : + $as_echo_n "(cached) " >&6 +else + if $as_echo 'TRUE=$(BAR$(V)) +BAR0=false +BAR1=true +V=1 +am__doit: + @$(TRUE) +.PHONY: am__doit' | $am_make -f - >/dev/null 2>&1; then + am_cv_make_support_nested_variables=yes +else + am_cv_make_support_nested_variables=no +fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_make_support_nested_variables" >&5 +$as_echo "$am_cv_make_support_nested_variables" >&6; } +if test $am_cv_make_support_nested_variables = yes; then + AM_V='$(V)' + AM_DEFAULT_V='$(AM_DEFAULT_VERBOSITY)' +else + AM_V=$AM_DEFAULT_VERBOSITY + AM_DEFAULT_V=$AM_DEFAULT_VERBOSITY +fi +AM_BACKSLASH='\' + +if test "`cd $srcdir && pwd`" != "`pwd`"; then + # Use -I$(srcdir) only when $(srcdir) != ., so that make's output + # is not polluted with repeated "-I." + am__isrc=' -I$(srcdir)' + # test to see if srcdir already configured + if test -f $srcdir/config.status; then + as_fn_error $? "source directory already configured; run \"make distclean\" there first" "$LINENO" 5 + fi +fi + +# test whether we have cygpath +if test -z "$CYGPATH_W"; then + if (cygpath --version) >/dev/null 2>/dev/null; then + CYGPATH_W='cygpath -w' + else + CYGPATH_W=echo + fi +fi + + +# Define the identity of the package. + PACKAGE='nghttp2' + VERSION='1.0.0' + + +cat >>confdefs.h <<_ACEOF +#define PACKAGE "$PACKAGE" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define VERSION "$VERSION" +_ACEOF + +# Some tools Automake needs. + +ACLOCAL=${ACLOCAL-"${am_missing_run}aclocal-${am__api_version}"} + + +AUTOCONF=${AUTOCONF-"${am_missing_run}autoconf"} + + +AUTOMAKE=${AUTOMAKE-"${am_missing_run}automake-${am__api_version}"} + + +AUTOHEADER=${AUTOHEADER-"${am_missing_run}autoheader"} + + +MAKEINFO=${MAKEINFO-"${am_missing_run}makeinfo"} + +# For better backward compatibility. To be removed once Automake 1.9.x +# dies out for good. For more background, see: +# +# +mkdir_p='$(MKDIR_P)' + +# We need awk for the "check" target. The system "awk" is bad on +# some platforms. +# Always define AMTAR for backward compatibility. Yes, it's still used +# in the wild :-( We should find a proper way to deprecate it ... +AMTAR='$${TAR-tar}' + + +# We'll loop over all known methods to create a tar archive until one works. +_am_tools='gnutar pax cpio none' + +am__tar='$${TAR-tar} chof - "$$tardir"' am__untar='$${TAR-tar} xf -' + + + + + +depcc="$CC" am_compiler_list= + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking dependency style of $depcc" >&5 +$as_echo_n "checking dependency style of $depcc... " >&6; } +if ${am_cv_CC_dependencies_compiler_type+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then + # We make a subdir and do the tests there. Otherwise we can end up + # making bogus files that we don't know about and never remove. For + # instance it was reported that on HP-UX the gcc test will end up + # making a dummy file named 'D' -- because '-MD' means "put the output + # in D". + rm -rf conftest.dir + mkdir conftest.dir + # Copy depcomp to subdir because otherwise we won't find it if we're + # using a relative directory. + cp "$am_depcomp" conftest.dir + cd conftest.dir + # We will build objects and dependencies in a subdirectory because + # it helps to detect inapplicable dependency modes. For instance + # both Tru64's cc and ICC support -MD to output dependencies as a + # side effect of compilation, but ICC will put the dependencies in + # the current directory while Tru64 will put them in the object + # directory. + mkdir sub + + am_cv_CC_dependencies_compiler_type=none + if test "$am_compiler_list" = ""; then + am_compiler_list=`sed -n 's/^#*\([a-zA-Z0-9]*\))$/\1/p' < ./depcomp` + fi + am__universal=false + case " $depcc " in #( + *\ -arch\ *\ -arch\ *) am__universal=true ;; + esac + + for depmode in $am_compiler_list; do + # Setup a source with many dependencies, because some compilers + # like to wrap large dependency lists on column 80 (with \), and + # we should not choose a depcomp mode which is confused by this. + # + # We need to recreate these files for each test, as the compiler may + # overwrite some of them when testing with obscure command lines. + # This happens at least with the AIX C compiler. + : > sub/conftest.c + for i in 1 2 3 4 5 6; do + echo '#include "conftst'$i'.h"' >> sub/conftest.c + # Using ": > sub/conftst$i.h" creates only sub/conftst1.h with + # Solaris 10 /bin/sh. + echo '/* dummy */' > sub/conftst$i.h + done + echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf + + # We check with '-c' and '-o' for the sake of the "dashmstdout" + # mode. It turns out that the SunPro C++ compiler does not properly + # handle '-M -o', and we need to detect this. Also, some Intel + # versions had trouble with output in subdirs. + am__obj=sub/conftest.${OBJEXT-o} + am__minus_obj="-o $am__obj" + case $depmode in + gcc) + # This depmode causes a compiler race in universal mode. + test "$am__universal" = false || continue + ;; + nosideeffect) + # After this tag, mechanisms are not by side-effect, so they'll + # only be used when explicitly requested. + if test "x$enable_dependency_tracking" = xyes; then + continue + else + break + fi + ;; + msvc7 | msvc7msys | msvisualcpp | msvcmsys) + # This compiler won't grok '-c -o', but also, the minuso test has + # not run yet. These depmodes are late enough in the game, and + # so weak that their functioning should not be impacted. + am__obj=conftest.${OBJEXT-o} + am__minus_obj= + ;; + none) break ;; + esac + if depmode=$depmode \ + source=sub/conftest.c object=$am__obj \ + depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \ + $SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \ + >/dev/null 2>conftest.err && + grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 && + grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 && + grep $am__obj sub/conftest.Po > /dev/null 2>&1 && + ${MAKE-make} -s -f confmf > /dev/null 2>&1; then + # icc doesn't choke on unknown options, it will just issue warnings + # or remarks (even with -Werror). So we grep stderr for any message + # that says an option was ignored or not supported. + # When given -MP, icc 7.0 and 7.1 complain thusly: + # icc: Command line warning: ignoring option '-M'; no argument required + # The diagnosis changed in icc 8.0: + # icc: Command line remark: option '-MP' not supported + if (grep 'ignoring option' conftest.err || + grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else + am_cv_CC_dependencies_compiler_type=$depmode + break + fi + fi + done + + cd .. + rm -rf conftest.dir +else + am_cv_CC_dependencies_compiler_type=none +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_CC_dependencies_compiler_type" >&5 +$as_echo "$am_cv_CC_dependencies_compiler_type" >&6; } +CCDEPMODE=depmode=$am_cv_CC_dependencies_compiler_type + + if + test "x$enable_dependency_tracking" != xno \ + && test "$am_cv_CC_dependencies_compiler_type" = gcc3; then + am__fastdepCC_TRUE= + am__fastdepCC_FALSE='#' +else + am__fastdepCC_TRUE='#' + am__fastdepCC_FALSE= +fi + + + +# POSIX will say in a future version that running "rm -f" with no argument +# is OK; and we want to be able to make that assumption in our Makefile +# recipes. So use an aggressive probe to check that the usage we want is +# actually supported "in the wild" to an acceptable degree. +# See automake bug#10828. +# To make any issue more visible, cause the running configure to be aborted +# by default if the 'rm' program in use doesn't match our expectations; the +# user can still override this though. +if rm -f && rm -fr && rm -rf; then : OK; else + cat >&2 <<'END' +Oops! + +Your 'rm' program seems unable to run without file operands specified +on the command line, even when the '-f' option is present. This is contrary +to the behaviour of most rm programs out there, and not conforming with +the upcoming POSIX standard: + +Please tell bug-automake@gnu.org about your system, including the value +of your $PATH and any error possibly output before this message. This +can help us improve future automake versions. + +END + if test x"$ACCEPT_INFERIOR_RM_PROGRAM" = x"yes"; then + echo 'Configuration will proceed anyway, since you have set the' >&2 + echo 'ACCEPT_INFERIOR_RM_PROGRAM variable to "yes"' >&2 + echo >&2 + else + cat >&2 <<'END' +Aborting the configuration process, to ensure you take notice of the issue. + +You can download and install GNU coreutils to get an 'rm' implementation +that behaves properly: . + +If you want to complete the configuration process using your problematic +'rm' anyway, export the environment variable ACCEPT_INFERIOR_RM_PROGRAM +to "yes", and re-run configure. + +END + as_fn_error $? "Your 'rm' program is bad, sorry." "$LINENO" 5 + fi +fi + +# comment out for now since this requires automake 1.13 or higher and +# travis has older one. +# AM_EXTRA_RECURSIVE_TARGETS([it]) + +# Check whether --enable-silent-rules was given. +if test "${enable_silent_rules+set}" = set; then : + enableval=$enable_silent_rules; +fi + +case $enable_silent_rules in # ((( + yes) AM_DEFAULT_VERBOSITY=0;; + no) AM_DEFAULT_VERBOSITY=1;; + *) AM_DEFAULT_VERBOSITY=0;; +esac +am_make=${MAKE-make} +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $am_make supports nested variables" >&5 +$as_echo_n "checking whether $am_make supports nested variables... " >&6; } +if ${am_cv_make_support_nested_variables+:} false; then : + $as_echo_n "(cached) " >&6 +else + if $as_echo 'TRUE=$(BAR$(V)) +BAR0=false +BAR1=true +V=1 +am__doit: + @$(TRUE) +.PHONY: am__doit' | $am_make -f - >/dev/null 2>&1; then + am_cv_make_support_nested_variables=yes +else + am_cv_make_support_nested_variables=no +fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_make_support_nested_variables" >&5 +$as_echo "$am_cv_make_support_nested_variables" >&6; } +if test $am_cv_make_support_nested_variables = yes; then + AM_V='$(V)' + AM_DEFAULT_V='$(AM_DEFAULT_VERBOSITY)' +else + AM_V=$AM_DEFAULT_VERBOSITY + AM_DEFAULT_V=$AM_DEFAULT_VERBOSITY +fi +AM_BACKSLASH='\' + + + +ac_config_headers="$ac_config_headers config.h" + + +LT_CURRENT=14 + +LT_REVISION=0 + +LT_AGE=0 + + +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"` + + + +# Check whether --enable-werror was given. +if test "${enable_werror+set}" = set; then : + enableval=$enable_werror; werror=$enableval +else + werror=no +fi + + +# Check whether --enable-debug was given. +if test "${enable_debug+set}" = set; then : + enableval=$enable_debug; debug=$enableval +else + debug=no +fi + + +# Check whether --enable-threads was given. +if test "${enable_threads+set}" = set; then : + enableval=$enable_threads; threads=$enableval +else + threads=yes +fi + + +# Check whether --enable-app was given. +if test "${enable_app+set}" = set; then : + enableval=$enable_app; request_app=$enableval +else + request_app=check +fi + + +# Check whether --enable-hpack-tools was given. +if test "${enable_hpack_tools+set}" = set; then : + enableval=$enable_hpack_tools; request_hpack_tools=$enableval +else + request_hpack_tools=check +fi + + +# Check whether --enable-asio-lib was given. +if test "${enable_asio_lib+set}" = set; then : + enableval=$enable_asio_lib; request_asio_lib=$enableval +else + request_asio_lib=no +fi + + +# Check whether --enable-examples was given. +if test "${enable_examples+set}" = set; then : + enableval=$enable_examples; request_examples=$enableval +else + request_examples=check +fi + + +# Check whether --enable-python-bindings was given. +if test "${enable_python_bindings+set}" = set; then : + enableval=$enable_python_bindings; request_python_bindings=$enableval +else + request_python_bindings=check +fi + + +# Check whether --enable-failmalloc was given. +if test "${enable_failmalloc+set}" = set; then : + enableval=$enable_failmalloc; request_failmalloc=$enableval +else + request_failmalloc=yes +fi + + + +# Check whether --with-libxml2 was given. +if test "${with_libxml2+set}" = set; then : + withval=$with_libxml2; request_libxml2=$withval +else + request_libxml2=check +fi + + + +# Check whether --with-jemalloc was given. +if test "${with_jemalloc+set}" = set; then : + withval=$with_jemalloc; request_jemalloc=$withval +else + request_jemalloc=check +fi + + + +# Check whether --with-spdylay was given. +if test "${with_spdylay+set}" = set; then : + withval=$with_spdylay; request_spdylay=$withval +else + request_spdylay=check +fi + + + +# Check whether --with-cython was given. +if test "${with_cython+set}" = set; then : + withval=$with_cython; cython_path=$withval +fi + + + + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args. +set dummy ${ac_tool_prefix}gcc; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="${ac_tool_prefix}gcc" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_CC"; then + ac_ct_CC=$CC + # Extract the first word of "gcc", so it can be a program name with args. +set dummy gcc; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_CC="gcc" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 +$as_echo "$ac_ct_CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_CC" = x; then + CC="" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + CC=$ac_ct_CC + fi +else + CC="$ac_cv_prog_CC" +fi + +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args. +set dummy ${ac_tool_prefix}cc; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="${ac_tool_prefix}cc" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + fi +fi +if test -z "$CC"; then + # Extract the first word of "cc", so it can be a program name with args. +set dummy cc; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else + ac_prog_rejected=no +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then + ac_prog_rejected=yes + continue + fi + ac_cv_prog_CC="cc" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +if test $ac_prog_rejected = yes; then + # We found a bogon in the path, so make sure we never use it. + set dummy $ac_cv_prog_CC + shift + if test $# != 0; then + # We chose a different compiler from the bogus one. + # However, it has the same basename, so the bogon will be chosen + # first if we set CC to just the basename; use the full file name. + shift + ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@" + fi +fi +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + for ac_prog in cl.exe + do + # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. +set dummy $ac_tool_prefix$ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="$ac_tool_prefix$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$CC" && break + done +fi +if test -z "$CC"; then + ac_ct_CC=$CC + for ac_prog in cl.exe +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_CC="$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 +$as_echo "$ac_ct_CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$ac_ct_CC" && break +done + + if test "x$ac_ct_CC" = x; then + CC="" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + CC=$ac_ct_CC + fi +fi + +fi + + +test -z "$CC" && { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "no acceptable C compiler found in \$PATH +See \`config.log' for more details" "$LINENO" 5; } + +# Provide some information about the compiler. +$as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5 +set X $ac_compile +ac_compiler=$2 +for ac_option in --version -v -V -qversion; do + { { ac_try="$ac_compiler $ac_option >&5" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_compiler $ac_option >&5") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + sed '10a\ +... rest of stderr output deleted ... + 10q' conftest.err >conftest.er1 + cat conftest.er1 >&5 + fi + rm -f conftest.er1 conftest.err + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } +done + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C compiler" >&5 +$as_echo_n "checking whether we are using the GNU C compiler... " >&6; } +if ${ac_cv_c_compiler_gnu+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ +#ifndef __GNUC__ + choke me +#endif + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_compiler_gnu=yes +else + ac_compiler_gnu=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +ac_cv_c_compiler_gnu=$ac_compiler_gnu + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5 +$as_echo "$ac_cv_c_compiler_gnu" >&6; } +if test $ac_compiler_gnu = yes; then + GCC=yes +else + GCC= +fi +ac_test_CFLAGS=${CFLAGS+set} +ac_save_CFLAGS=$CFLAGS +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5 +$as_echo_n "checking whether $CC accepts -g... " >&6; } +if ${ac_cv_prog_cc_g+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_save_c_werror_flag=$ac_c_werror_flag + ac_c_werror_flag=yes + ac_cv_prog_cc_g=no + CFLAGS="-g" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_prog_cc_g=yes +else + CFLAGS="" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + +else + ac_c_werror_flag=$ac_save_c_werror_flag + CFLAGS="-g" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_prog_cc_g=yes +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + ac_c_werror_flag=$ac_save_c_werror_flag +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5 +$as_echo "$ac_cv_prog_cc_g" >&6; } +if test "$ac_test_CFLAGS" = set; then + CFLAGS=$ac_save_CFLAGS +elif test $ac_cv_prog_cc_g = yes; then + if test "$GCC" = yes; then + CFLAGS="-g -O2" + else + CFLAGS="-g" + fi +else + if test "$GCC" = yes; then + CFLAGS="-O2" + else + CFLAGS= + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C89" >&5 +$as_echo_n "checking for $CC option to accept ISO C89... " >&6; } +if ${ac_cv_prog_cc_c89+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_cv_prog_cc_c89=no +ac_save_CC=$CC +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +#include +struct stat; +/* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */ +struct buf { int x; }; +FILE * (*rcsopen) (struct buf *, struct stat *, int); +static char *e (p, i) + char **p; + int i; +{ + return p[i]; +} +static char *f (char * (*g) (char **, int), char **p, ...) +{ + char *s; + va_list v; + va_start (v,p); + s = g (p, va_arg (v,int)); + va_end (v); + return s; +} + +/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has + function prototypes and stuff, but not '\xHH' hex character constants. + These don't provoke an error unfortunately, instead are silently treated + as 'x'. The following induces an error, until -std is added to get + proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an + array size at least. It's necessary to write '\x00'==0 to get something + that's true only with -std. */ +int osf4_cc_array ['\x00' == 0 ? 1 : -1]; + +/* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters + inside strings and character constants. */ +#define FOO(x) 'x' +int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1]; + +int test (int i, double x); +struct s1 {int (*f) (int a);}; +struct s2 {int (*f) (double a);}; +int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int); +int argc; +char **argv; +int +main () +{ +return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]; + ; + return 0; +} +_ACEOF +for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \ + -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" +do + CC="$ac_save_CC $ac_arg" + if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_prog_cc_c89=$ac_arg +fi +rm -f core conftest.err conftest.$ac_objext + test "x$ac_cv_prog_cc_c89" != "xno" && break +done +rm -f conftest.$ac_ext +CC=$ac_save_CC + +fi +# AC_CACHE_VAL +case "x$ac_cv_prog_cc_c89" in + x) + { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 +$as_echo "none needed" >&6; } ;; + xno) + { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 +$as_echo "unsupported" >&6; } ;; + *) + CC="$CC $ac_cv_prog_cc_c89" + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5 +$as_echo "$ac_cv_prog_cc_c89" >&6; } ;; +esac +if test "x$ac_cv_prog_cc_c89" != xno; then : + +fi + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC understands -c and -o together" >&5 +$as_echo_n "checking whether $CC understands -c and -o together... " >&6; } +if ${am_cv_prog_cc_c_o+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF + # Make sure it works both with $CC and with simple cc. + # Following AC_PROG_CC_C_O, we do the test twice because some + # compilers refuse to overwrite an existing .o file with -o, + # though they will create one. + am_cv_prog_cc_c_o=yes + for am_i in 1 2; do + if { echo "$as_me:$LINENO: $CC -c conftest.$ac_ext -o conftest2.$ac_objext" >&5 + ($CC -c conftest.$ac_ext -o conftest2.$ac_objext) >&5 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } \ + && test -f conftest2.$ac_objext; then + : OK + else + am_cv_prog_cc_c_o=no + break + fi + done + rm -f core conftest* + unset am_i +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_prog_cc_c_o" >&5 +$as_echo "$am_cv_prog_cc_c_o" >&6; } +if test "$am_cv_prog_cc_c_o" != yes; then + # Losing compiler, so override with the script. + # FIXME: It is wrong to rewrite CC. + # But if we don't then we get into trouble of one sort or another. + # A longer-term fix would be to have automake use am__CC in this case, + # and then we could set am__CC="\$(top_srcdir)/compile \$(CC)" + CC="$am_aux_dir/compile $CC" +fi +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + +ac_ext=cpp +ac_cpp='$CXXCPP $CPPFLAGS' +ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_cxx_compiler_gnu +if test -z "$CXX"; then + if test -n "$CCC"; then + CXX=$CCC + else + if test -n "$ac_tool_prefix"; then + for ac_prog in g++ c++ gpp aCC CC cxx cc++ cl.exe FCC KCC RCC xlC_r xlC + do + # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. +set dummy $ac_tool_prefix$ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_CXX+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$CXX"; then + ac_cv_prog_CXX="$CXX" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CXX="$ac_tool_prefix$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +CXX=$ac_cv_prog_CXX +if test -n "$CXX"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CXX" >&5 +$as_echo "$CXX" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$CXX" && break + done +fi +if test -z "$CXX"; then + ac_ct_CXX=$CXX + for ac_prog in g++ c++ gpp aCC CC cxx cc++ cl.exe FCC KCC RCC xlC_r xlC +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_CXX+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_CXX"; then + ac_cv_prog_ac_ct_CXX="$ac_ct_CXX" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_CXX="$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_CXX=$ac_cv_prog_ac_ct_CXX +if test -n "$ac_ct_CXX"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CXX" >&5 +$as_echo "$ac_ct_CXX" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$ac_ct_CXX" && break +done + + if test "x$ac_ct_CXX" = x; then + CXX="g++" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + CXX=$ac_ct_CXX + fi +fi + + fi +fi +# Provide some information about the compiler. +$as_echo "$as_me:${as_lineno-$LINENO}: checking for C++ compiler version" >&5 +set X $ac_compile +ac_compiler=$2 +for ac_option in --version -v -V -qversion; do + { { ac_try="$ac_compiler $ac_option >&5" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_compiler $ac_option >&5") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + sed '10a\ +... rest of stderr output deleted ... + 10q' conftest.err >conftest.er1 + cat conftest.er1 >&5 + fi + rm -f conftest.er1 conftest.err + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } +done + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C++ compiler" >&5 +$as_echo_n "checking whether we are using the GNU C++ compiler... " >&6; } +if ${ac_cv_cxx_compiler_gnu+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ +#ifndef __GNUC__ + choke me +#endif + + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_compile "$LINENO"; then : + ac_compiler_gnu=yes +else + ac_compiler_gnu=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +ac_cv_cxx_compiler_gnu=$ac_compiler_gnu + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cxx_compiler_gnu" >&5 +$as_echo "$ac_cv_cxx_compiler_gnu" >&6; } +if test $ac_compiler_gnu = yes; then + GXX=yes +else + GXX= +fi +ac_test_CXXFLAGS=${CXXFLAGS+set} +ac_save_CXXFLAGS=$CXXFLAGS +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CXX accepts -g" >&5 +$as_echo_n "checking whether $CXX accepts -g... " >&6; } +if ${ac_cv_prog_cxx_g+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_save_cxx_werror_flag=$ac_cxx_werror_flag + ac_cxx_werror_flag=yes + ac_cv_prog_cxx_g=no + CXXFLAGS="-g" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_compile "$LINENO"; then : + ac_cv_prog_cxx_g=yes +else + CXXFLAGS="" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_compile "$LINENO"; then : + +else + ac_cxx_werror_flag=$ac_save_cxx_werror_flag + CXXFLAGS="-g" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_compile "$LINENO"; then : + ac_cv_prog_cxx_g=yes +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + ac_cxx_werror_flag=$ac_save_cxx_werror_flag +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cxx_g" >&5 +$as_echo "$ac_cv_prog_cxx_g" >&6; } +if test "$ac_test_CXXFLAGS" = set; then + CXXFLAGS=$ac_save_CXXFLAGS +elif test $ac_cv_prog_cxx_g = yes; then + if test "$GXX" = yes; then + CXXFLAGS="-g -O2" + else + CXXFLAGS="-g" + fi +else + if test "$GXX" = yes; then + CXXFLAGS="-O2" + else + CXXFLAGS= + fi +fi +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + + +func_stripname_cnf () +{ + case ${2} in + .*) func_stripname_result=`$ECHO "${3}" | $SED "s%^${1}%%; s%\\\\${2}\$%%"`;; + *) func_stripname_result=`$ECHO "${3}" | $SED "s%^${1}%%; s%${2}\$%%"`;; + esac +} # func_stripname_cnf + + if test -n "$CXX" && ( test "X$CXX" != "Xno" && + ( (test "X$CXX" = "Xg++" && `g++ -v >/dev/null 2>&1` ) || + (test "X$CXX" != "Xg++"))) ; then + ac_ext=cpp +ac_cpp='$CXXCPP $CPPFLAGS' +ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_cxx_compiler_gnu +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to run the C++ preprocessor" >&5 +$as_echo_n "checking how to run the C++ preprocessor... " >&6; } +if test -z "$CXXCPP"; then + if ${ac_cv_prog_CXXCPP+:} false; then : + $as_echo_n "(cached) " >&6 +else + # Double quotes because CXXCPP needs to be expanded + for CXXCPP in "$CXX -E" "/lib/cpp" + do + ac_preproc_ok=false +for ac_cxx_preproc_warn_flag in '' yes +do + # Use a header file that comes with gcc, so configuring glibc + # with a fresh cross-compiler works. + # Prefer to if __STDC__ is defined, since + # exists even on freestanding compilers. + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. "Syntax error" is here to catch this case. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#ifdef __STDC__ +# include +#else +# include +#endif + Syntax error +_ACEOF +if ac_fn_cxx_try_cpp "$LINENO"; then : + +else + # Broken: fails on valid input. +continue +fi +rm -f conftest.err conftest.i conftest.$ac_ext + + # OK, works on sane cases. Now check whether nonexistent headers + # can be detected and how. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +_ACEOF +if ac_fn_cxx_try_cpp "$LINENO"; then : + # Broken: success on invalid input. +continue +else + # Passes both tests. +ac_preproc_ok=: +break +fi +rm -f conftest.err conftest.i conftest.$ac_ext + +done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f conftest.i conftest.err conftest.$ac_ext +if $ac_preproc_ok; then : + break +fi + + done + ac_cv_prog_CXXCPP=$CXXCPP + +fi + CXXCPP=$ac_cv_prog_CXXCPP +else + ac_cv_prog_CXXCPP=$CXXCPP +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $CXXCPP" >&5 +$as_echo "$CXXCPP" >&6; } +ac_preproc_ok=false +for ac_cxx_preproc_warn_flag in '' yes +do + # Use a header file that comes with gcc, so configuring glibc + # with a fresh cross-compiler works. + # Prefer to if __STDC__ is defined, since + # exists even on freestanding compilers. + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. "Syntax error" is here to catch this case. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#ifdef __STDC__ +# include +#else +# include +#endif + Syntax error +_ACEOF +if ac_fn_cxx_try_cpp "$LINENO"; then : + +else + # Broken: fails on valid input. +continue +fi +rm -f conftest.err conftest.i conftest.$ac_ext + + # OK, works on sane cases. Now check whether nonexistent headers + # can be detected and how. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +_ACEOF +if ac_fn_cxx_try_cpp "$LINENO"; then : + # Broken: success on invalid input. +continue +else + # Passes both tests. +ac_preproc_ok=: +break +fi +rm -f conftest.err conftest.i conftest.$ac_ext + +done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f conftest.i conftest.err conftest.$ac_ext +if $ac_preproc_ok; then : + +else + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "C++ preprocessor \"$CXXCPP\" fails sanity check +See \`config.log' for more details" "$LINENO" 5; } +fi + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + +else + _lt_caught_CXX_error=yes +fi + +ac_ext=cpp +ac_cpp='$CXXCPP $CPPFLAGS' +ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_cxx_compiler_gnu + +archive_cmds_need_lc_CXX=no +allow_undefined_flag_CXX= +always_export_symbols_CXX=no +archive_expsym_cmds_CXX= +compiler_needs_object_CXX=no +export_dynamic_flag_spec_CXX= +hardcode_direct_CXX=no +hardcode_direct_absolute_CXX=no +hardcode_libdir_flag_spec_CXX= +hardcode_libdir_separator_CXX= +hardcode_minus_L_CXX=no +hardcode_shlibpath_var_CXX=unsupported +hardcode_automatic_CXX=no +inherit_rpath_CXX=no +module_cmds_CXX= +module_expsym_cmds_CXX= +link_all_deplibs_CXX=unknown +old_archive_cmds_CXX=$old_archive_cmds +reload_flag_CXX=$reload_flag +reload_cmds_CXX=$reload_cmds +no_undefined_flag_CXX= +whole_archive_flag_spec_CXX= +enable_shared_with_static_runtimes_CXX=no + +# Source file extension for C++ test sources. +ac_ext=cpp + +# Object file extension for compiled C++ test sources. +objext=o +objext_CXX=$objext + +# No sense in running all these tests if we already determined that +# the CXX compiler isn't working. Some variables (like enable_shared) +# are currently assumed to apply to all compilers on this platform, +# and will be corrupted by setting them based on a non-working compiler. +if test "$_lt_caught_CXX_error" != yes; then + # Code to be used in simple compile tests + lt_simple_compile_test_code="int some_variable = 0;" + + # Code to be used in simple link tests + lt_simple_link_test_code='int main(int, char *[]) { return(0); }' + + # ltmain only uses $CC for tagged configurations so make sure $CC is set. + + + + + + +# If no C compiler was specified, use CC. +LTCC=${LTCC-"$CC"} + +# If no C compiler flags were specified, use CFLAGS. +LTCFLAGS=${LTCFLAGS-"$CFLAGS"} + +# Allow CC to be a program name with arguments. +compiler=$CC + + + # save warnings/boilerplate of simple test code + ac_outfile=conftest.$ac_objext +echo "$lt_simple_compile_test_code" >conftest.$ac_ext +eval "$ac_compile" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err +_lt_compiler_boilerplate=`cat conftest.err` +$RM conftest* + + ac_outfile=conftest.$ac_objext +echo "$lt_simple_link_test_code" >conftest.$ac_ext +eval "$ac_link" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err +_lt_linker_boilerplate=`cat conftest.err` +$RM -r conftest* + + + # Allow CC to be a program name with arguments. + lt_save_CC=$CC + lt_save_CFLAGS=$CFLAGS + lt_save_LD=$LD + lt_save_GCC=$GCC + GCC=$GXX + lt_save_with_gnu_ld=$with_gnu_ld + lt_save_path_LD=$lt_cv_path_LD + if test -n "${lt_cv_prog_gnu_ldcxx+set}"; then + lt_cv_prog_gnu_ld=$lt_cv_prog_gnu_ldcxx + else + $as_unset lt_cv_prog_gnu_ld + fi + if test -n "${lt_cv_path_LDCXX+set}"; then + lt_cv_path_LD=$lt_cv_path_LDCXX + else + $as_unset lt_cv_path_LD + fi + test -z "${LDCXX+set}" || LD=$LDCXX + CC=${CXX-"c++"} + CFLAGS=$CXXFLAGS + compiler=$CC + compiler_CXX=$CC + for cc_temp in $compiler""; do + case $cc_temp in + compile | *[\\/]compile | ccache | *[\\/]ccache ) ;; + distcc | *[\\/]distcc | purify | *[\\/]purify ) ;; + \-*) ;; + *) break;; + esac +done +cc_basename=`$ECHO "$cc_temp" | $SED "s%.*/%%; s%^$host_alias-%%"` + + + if test -n "$compiler"; then + # We don't want -fno-exception when compiling C++ code, so set the + # no_builtin_flag separately + if test "$GXX" = yes; then + lt_prog_compiler_no_builtin_flag_CXX=' -fno-builtin' + else + lt_prog_compiler_no_builtin_flag_CXX= + fi + + if test "$GXX" = yes; then + # Set up default GNU C++ configuration + + + +# Check whether --with-gnu-ld was given. +if test "${with_gnu_ld+set}" = set; then : + withval=$with_gnu_ld; test "$withval" = no || with_gnu_ld=yes +else + with_gnu_ld=no +fi + +ac_prog=ld +if test "$GCC" = yes; then + # Check if gcc -print-prog-name=ld gives a path. + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ld used by $CC" >&5 +$as_echo_n "checking for ld used by $CC... " >&6; } + case $host in + *-*-mingw*) + # gcc leaves a trailing carriage return which upsets mingw + ac_prog=`($CC -print-prog-name=ld) 2>&5 | tr -d '\015'` ;; + *) + ac_prog=`($CC -print-prog-name=ld) 2>&5` ;; + esac + case $ac_prog in + # Accept absolute paths. + [\\/]* | ?:[\\/]*) + re_direlt='/[^/][^/]*/\.\./' + # Canonicalize the pathname of ld + ac_prog=`$ECHO "$ac_prog"| $SED 's%\\\\%/%g'` + while $ECHO "$ac_prog" | $GREP "$re_direlt" > /dev/null 2>&1; do + ac_prog=`$ECHO $ac_prog| $SED "s%$re_direlt%/%"` + done + test -z "$LD" && LD="$ac_prog" + ;; + "") + # If it fails, then pretend we aren't using GCC. + ac_prog=ld + ;; + *) + # If it is relative, then search for the first ld in PATH. + with_gnu_ld=unknown + ;; + esac +elif test "$with_gnu_ld" = yes; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for GNU ld" >&5 +$as_echo_n "checking for GNU ld... " >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for non-GNU ld" >&5 +$as_echo_n "checking for non-GNU ld... " >&6; } +fi +if ${lt_cv_path_LD+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -z "$LD"; then + lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR + for ac_dir in $PATH; do + IFS="$lt_save_ifs" + test -z "$ac_dir" && ac_dir=. + if test -f "$ac_dir/$ac_prog" || test -f "$ac_dir/$ac_prog$ac_exeext"; then + lt_cv_path_LD="$ac_dir/$ac_prog" + # Check to see if the program is GNU ld. I'd rather use --version, + # but apparently some variants of GNU ld only accept -v. + # Break only if it was the GNU/non-GNU ld that we prefer. + case `"$lt_cv_path_LD" -v 2>&1 &5 +$as_echo "$LD" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi +test -z "$LD" && as_fn_error $? "no acceptable ld found in \$PATH" "$LINENO" 5 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if the linker ($LD) is GNU ld" >&5 +$as_echo_n "checking if the linker ($LD) is GNU ld... " >&6; } +if ${lt_cv_prog_gnu_ld+:} false; then : + $as_echo_n "(cached) " >&6 +else + # I'd rather use --version here, but apparently some GNU lds only accept -v. +case `$LD -v 2>&1 &5 +$as_echo "$lt_cv_prog_gnu_ld" >&6; } +with_gnu_ld=$lt_cv_prog_gnu_ld + + + + + + + + # Check if GNU C++ uses GNU ld as the underlying linker, since the + # archiving commands below assume that GNU ld is being used. + if test "$with_gnu_ld" = yes; then + archive_cmds_CXX='$CC $pic_flag -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib' + archive_expsym_cmds_CXX='$CC $pic_flag -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' + + hardcode_libdir_flag_spec_CXX='${wl}-rpath ${wl}$libdir' + export_dynamic_flag_spec_CXX='${wl}--export-dynamic' + + # If archive_cmds runs LD, not CC, wlarc should be empty + # XXX I think wlarc can be eliminated in ltcf-cxx, but I need to + # investigate it a little bit more. (MM) + wlarc='${wl}' + + # ancient GNU ld didn't support --whole-archive et. al. + if eval "`$CC -print-prog-name=ld` --help 2>&1" | + $GREP 'no-whole-archive' > /dev/null; then + whole_archive_flag_spec_CXX="$wlarc"'--whole-archive$convenience '"$wlarc"'--no-whole-archive' + else + whole_archive_flag_spec_CXX= + fi + else + with_gnu_ld=no + wlarc= + + # A generic and very simple default shared library creation + # command for GNU C++ for the case where it uses the native + # linker, instead of GNU ld. If possible, this setting should + # overridden to take advantage of the native linker features on + # the platform it is being used on. + archive_cmds_CXX='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $lib' + fi + + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"' + + else + GXX=no + with_gnu_ld=no + wlarc= + fi + + # PORTME: fill in a description of your system's C++ link characteristics + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the $compiler linker ($LD) supports shared libraries" >&5 +$as_echo_n "checking whether the $compiler linker ($LD) supports shared libraries... " >&6; } + ld_shlibs_CXX=yes + case $host_os in + aix3*) + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + aix[4-9]*) + if test "$host_cpu" = ia64; then + # On IA64, the linker does run time linking by default, so we don't + # have to do anything special. + aix_use_runtimelinking=no + exp_sym_flag='-Bexport' + no_entry_flag="" + else + aix_use_runtimelinking=no + + # Test if we are trying to use run time linking or normal + # AIX style linking. If -brtl is somewhere in LDFLAGS, we + # need to do runtime linking. + case $host_os in aix4.[23]|aix4.[23].*|aix[5-9]*) + for ld_flag in $LDFLAGS; do + case $ld_flag in + *-brtl*) + aix_use_runtimelinking=yes + break + ;; + esac + done + ;; + esac + + exp_sym_flag='-bexport' + no_entry_flag='-bnoentry' + fi + + # When large executables or shared objects are built, AIX ld can + # have problems creating the table of contents. If linking a library + # or program results in "error TOC overflow" add -mminimal-toc to + # CXXFLAGS/CFLAGS for g++/gcc. In the cases where that is not + # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS. + + archive_cmds_CXX='' + hardcode_direct_CXX=yes + hardcode_direct_absolute_CXX=yes + hardcode_libdir_separator_CXX=':' + link_all_deplibs_CXX=yes + file_list_spec_CXX='${wl}-f,' + + if test "$GXX" = yes; then + case $host_os in aix4.[012]|aix4.[012].*) + # We only want to do this on AIX 4.2 and lower, the check + # below for broken collect2 doesn't work under 4.3+ + collect2name=`${CC} -print-prog-name=collect2` + if test -f "$collect2name" && + strings "$collect2name" | $GREP resolve_lib_name >/dev/null + then + # We have reworked collect2 + : + else + # We have old collect2 + hardcode_direct_CXX=unsupported + # It fails to find uninstalled libraries when the uninstalled + # path is not listed in the libpath. Setting hardcode_minus_L + # to unsupported forces relinking + hardcode_minus_L_CXX=yes + hardcode_libdir_flag_spec_CXX='-L$libdir' + hardcode_libdir_separator_CXX= + fi + esac + shared_flag='-shared' + if test "$aix_use_runtimelinking" = yes; then + shared_flag="$shared_flag "'${wl}-G' + fi + else + # not using gcc + if test "$host_cpu" = ia64; then + # VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release + # chokes on -Wl,-G. The following line is correct: + shared_flag='-G' + else + if test "$aix_use_runtimelinking" = yes; then + shared_flag='${wl}-G' + else + shared_flag='${wl}-bM:SRE' + fi + fi + fi + + export_dynamic_flag_spec_CXX='${wl}-bexpall' + # It seems that -bexpall does not export symbols beginning with + # underscore (_), so it is better to generate a list of symbols to + # export. + always_export_symbols_CXX=yes + if test "$aix_use_runtimelinking" = yes; then + # Warning - without using the other runtime loading flags (-brtl), + # -berok will link without error, but may produce a broken library. + allow_undefined_flag_CXX='-berok' + # Determine the default libpath from the value encoded in an empty + # executable. + if test "${lt_cv_aix_libpath+set}" = set; then + aix_libpath=$lt_cv_aix_libpath +else + if ${lt_cv_aix_libpath__CXX+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_link "$LINENO"; then : + + lt_aix_libpath_sed=' + /Import File Strings/,/^$/ { + /^0/ { + s/^0 *\([^ ]*\) *$/\1/ + p + } + }' + lt_cv_aix_libpath__CXX=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` + # Check for a 64-bit object if we didn't find anything. + if test -z "$lt_cv_aix_libpath__CXX"; then + lt_cv_aix_libpath__CXX=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` + fi +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + if test -z "$lt_cv_aix_libpath__CXX"; then + lt_cv_aix_libpath__CXX="/usr/lib:/lib" + fi + +fi + + aix_libpath=$lt_cv_aix_libpath__CXX +fi + + hardcode_libdir_flag_spec_CXX='${wl}-blibpath:$libdir:'"$aix_libpath" + + archive_expsym_cmds_CXX='$CC -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags `if test "x${allow_undefined_flag}" != "x"; then func_echo_all "${wl}${allow_undefined_flag}"; else :; fi` '"\${wl}$exp_sym_flag:\$export_symbols $shared_flag" + else + if test "$host_cpu" = ia64; then + hardcode_libdir_flag_spec_CXX='${wl}-R $libdir:/usr/lib:/lib' + allow_undefined_flag_CXX="-z nodefs" + archive_expsym_cmds_CXX="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags ${wl}${allow_undefined_flag} '"\${wl}$exp_sym_flag:\$export_symbols" + else + # Determine the default libpath from the value encoded in an + # empty executable. + if test "${lt_cv_aix_libpath+set}" = set; then + aix_libpath=$lt_cv_aix_libpath +else + if ${lt_cv_aix_libpath__CXX+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_link "$LINENO"; then : + + lt_aix_libpath_sed=' + /Import File Strings/,/^$/ { + /^0/ { + s/^0 *\([^ ]*\) *$/\1/ + p + } + }' + lt_cv_aix_libpath__CXX=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` + # Check for a 64-bit object if we didn't find anything. + if test -z "$lt_cv_aix_libpath__CXX"; then + lt_cv_aix_libpath__CXX=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` + fi +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + if test -z "$lt_cv_aix_libpath__CXX"; then + lt_cv_aix_libpath__CXX="/usr/lib:/lib" + fi + +fi + + aix_libpath=$lt_cv_aix_libpath__CXX +fi + + hardcode_libdir_flag_spec_CXX='${wl}-blibpath:$libdir:'"$aix_libpath" + # Warning - without using the other run time loading flags, + # -berok will link without error, but may produce a broken library. + no_undefined_flag_CXX=' ${wl}-bernotok' + allow_undefined_flag_CXX=' ${wl}-berok' + if test "$with_gnu_ld" = yes; then + # We only use this code for GNU lds that support --whole-archive. + whole_archive_flag_spec_CXX='${wl}--whole-archive$convenience ${wl}--no-whole-archive' + else + # Exported symbols can be pulled into shared objects from archives + whole_archive_flag_spec_CXX='$convenience' + fi + archive_cmds_need_lc_CXX=yes + # This is similar to how AIX traditionally builds its shared + # libraries. + archive_expsym_cmds_CXX="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs ${wl}-bnoentry $compiler_flags ${wl}-bE:$export_symbols${allow_undefined_flag}~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$soname' + fi + fi + ;; + + beos*) + if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then + allow_undefined_flag_CXX=unsupported + # Joseph Beckenbach says some releases of gcc + # support --undefined. This deserves some investigation. FIXME + archive_cmds_CXX='$CC -nostart $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + else + ld_shlibs_CXX=no + fi + ;; + + chorus*) + case $cc_basename in + *) + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + esac + ;; + + cygwin* | mingw* | pw32* | cegcc*) + case $GXX,$cc_basename in + ,cl* | no,cl*) + # Native MSVC + # hardcode_libdir_flag_spec is actually meaningless, as there is + # no search path for DLLs. + hardcode_libdir_flag_spec_CXX=' ' + allow_undefined_flag_CXX=unsupported + always_export_symbols_CXX=yes + file_list_spec_CXX='@' + # Tell ltmain to make .lib files, not .a files. + libext=lib + # Tell ltmain to make .dll files, not .so files. + shrext_cmds=".dll" + # FIXME: Setting linknames here is a bad hack. + archive_cmds_CXX='$CC -o $output_objdir/$soname $libobjs $compiler_flags $deplibs -Wl,-dll~linknames=' + archive_expsym_cmds_CXX='if test "x`$SED 1q $export_symbols`" = xEXPORTS; then + $SED -n -e 's/\\\\\\\(.*\\\\\\\)/-link\\\ -EXPORT:\\\\\\\1/' -e '1\\\!p' < $export_symbols > $output_objdir/$soname.exp; + else + $SED -e 's/\\\\\\\(.*\\\\\\\)/-link\\\ -EXPORT:\\\\\\\1/' < $export_symbols > $output_objdir/$soname.exp; + fi~ + $CC -o $tool_output_objdir$soname $libobjs $compiler_flags $deplibs "@$tool_output_objdir$soname.exp" -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~ + linknames=' + # The linker will not automatically build a static lib if we build a DLL. + # _LT_TAGVAR(old_archive_from_new_cmds, CXX)='true' + enable_shared_with_static_runtimes_CXX=yes + # Don't use ranlib + old_postinstall_cmds_CXX='chmod 644 $oldlib' + postlink_cmds_CXX='lt_outputfile="@OUTPUT@"~ + lt_tool_outputfile="@TOOL_OUTPUT@"~ + case $lt_outputfile in + *.exe|*.EXE) ;; + *) + lt_outputfile="$lt_outputfile.exe" + lt_tool_outputfile="$lt_tool_outputfile.exe" + ;; + esac~ + func_to_tool_file "$lt_outputfile"~ + if test "$MANIFEST_TOOL" != ":" && test -f "$lt_outputfile.manifest"; then + $MANIFEST_TOOL -manifest "$lt_tool_outputfile.manifest" -outputresource:"$lt_tool_outputfile" || exit 1; + $RM "$lt_outputfile.manifest"; + fi' + ;; + *) + # g++ + # _LT_TAGVAR(hardcode_libdir_flag_spec, CXX) is actually meaningless, + # as there is no search path for DLLs. + hardcode_libdir_flag_spec_CXX='-L$libdir' + export_dynamic_flag_spec_CXX='${wl}--export-all-symbols' + allow_undefined_flag_CXX=unsupported + always_export_symbols_CXX=no + enable_shared_with_static_runtimes_CXX=yes + + if $LD --help 2>&1 | $GREP 'auto-import' > /dev/null; then + archive_cmds_CXX='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' + # If the export-symbols file already is a .def file (1st line + # is EXPORTS), use it as is; otherwise, prepend... + archive_expsym_cmds_CXX='if test "x`$SED 1q $export_symbols`" = xEXPORTS; then + cp $export_symbols $output_objdir/$soname.def; + else + echo EXPORTS > $output_objdir/$soname.def; + cat $export_symbols >> $output_objdir/$soname.def; + fi~ + $CC -shared -nostdlib $output_objdir/$soname.def $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' + else + ld_shlibs_CXX=no + fi + ;; + esac + ;; + darwin* | rhapsody*) + + + archive_cmds_need_lc_CXX=no + hardcode_direct_CXX=no + hardcode_automatic_CXX=yes + hardcode_shlibpath_var_CXX=unsupported + if test "$lt_cv_ld_force_load" = "yes"; then + whole_archive_flag_spec_CXX='`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience ${wl}-force_load,$conv\"; done; func_echo_all \"$new_convenience\"`' + + else + whole_archive_flag_spec_CXX='' + fi + link_all_deplibs_CXX=yes + allow_undefined_flag_CXX="$_lt_dar_allow_undefined" + case $cc_basename in + ifort*) _lt_dar_can_shared=yes ;; + *) _lt_dar_can_shared=$GCC ;; + esac + if test "$_lt_dar_can_shared" = "yes"; then + output_verbose_link_cmd=func_echo_all + archive_cmds_CXX="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod${_lt_dsymutil}" + module_cmds_CXX="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dsymutil}" + archive_expsym_cmds_CXX="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring ${_lt_dar_single_mod}${_lt_dar_export_syms}${_lt_dsymutil}" + module_expsym_cmds_CXX="sed -e 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dar_export_syms}${_lt_dsymutil}" + if test "$lt_cv_apple_cc_single_mod" != "yes"; then + archive_cmds_CXX="\$CC -r -keep_private_externs -nostdlib -o \${lib}-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \${lib}-master.o \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring${_lt_dsymutil}" + archive_expsym_cmds_CXX="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -r -keep_private_externs -nostdlib -o \${lib}-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \${lib}-master.o \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring${_lt_dar_export_syms}${_lt_dsymutil}" + fi + + else + ld_shlibs_CXX=no + fi + + ;; + + dgux*) + case $cc_basename in + ec++*) + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + ghcx*) + # Green Hills C++ Compiler + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + *) + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + esac + ;; + + freebsd2.*) + # C++ shared libraries reported to be fairly broken before + # switch to ELF + ld_shlibs_CXX=no + ;; + + freebsd-elf*) + archive_cmds_need_lc_CXX=no + ;; + + freebsd* | dragonfly*) + # FreeBSD 3 and later use GNU C++ and GNU ld with standard ELF + # conventions + ld_shlibs_CXX=yes + ;; + + haiku*) + archive_cmds_CXX='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + link_all_deplibs_CXX=yes + ;; + + hpux9*) + hardcode_libdir_flag_spec_CXX='${wl}+b ${wl}$libdir' + hardcode_libdir_separator_CXX=: + export_dynamic_flag_spec_CXX='${wl}-E' + hardcode_direct_CXX=yes + hardcode_minus_L_CXX=yes # Not in the search PATH, + # but as the default + # location of the library. + + case $cc_basename in + CC*) + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + aCC*) + archive_cmds_CXX='$RM $output_objdir/$soname~$CC -b ${wl}+b ${wl}$install_libdir -o $output_objdir/$soname $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib' + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + # + # There doesn't appear to be a way to prevent this compiler from + # explicitly linking system object files so we need to strip them + # from the output so that they don't get included in the library + # dependencies. + output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | $EGREP "\-L"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"' + ;; + *) + if test "$GXX" = yes; then + archive_cmds_CXX='$RM $output_objdir/$soname~$CC -shared -nostdlib $pic_flag ${wl}+b ${wl}$install_libdir -o $output_objdir/$soname $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib' + else + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + fi + ;; + esac + ;; + + hpux10*|hpux11*) + if test $with_gnu_ld = no; then + hardcode_libdir_flag_spec_CXX='${wl}+b ${wl}$libdir' + hardcode_libdir_separator_CXX=: + + case $host_cpu in + hppa*64*|ia64*) + ;; + *) + export_dynamic_flag_spec_CXX='${wl}-E' + ;; + esac + fi + case $host_cpu in + hppa*64*|ia64*) + hardcode_direct_CXX=no + hardcode_shlibpath_var_CXX=no + ;; + *) + hardcode_direct_CXX=yes + hardcode_direct_absolute_CXX=yes + hardcode_minus_L_CXX=yes # Not in the search PATH, + # but as the default + # location of the library. + ;; + esac + + case $cc_basename in + CC*) + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + aCC*) + case $host_cpu in + hppa*64*) + archive_cmds_CXX='$CC -b ${wl}+h ${wl}$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + ;; + ia64*) + archive_cmds_CXX='$CC -b ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + ;; + *) + archive_cmds_CXX='$CC -b ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + ;; + esac + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + # + # There doesn't appear to be a way to prevent this compiler from + # explicitly linking system object files so we need to strip them + # from the output so that they don't get included in the library + # dependencies. + output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | $GREP "\-L"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"' + ;; + *) + if test "$GXX" = yes; then + if test $with_gnu_ld = no; then + case $host_cpu in + hppa*64*) + archive_cmds_CXX='$CC -shared -nostdlib -fPIC ${wl}+h ${wl}$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + ;; + ia64*) + archive_cmds_CXX='$CC -shared -nostdlib $pic_flag ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + ;; + *) + archive_cmds_CXX='$CC -shared -nostdlib $pic_flag ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + ;; + esac + fi + else + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + fi + ;; + esac + ;; + + interix[3-9]*) + hardcode_direct_CXX=no + hardcode_shlibpath_var_CXX=no + hardcode_libdir_flag_spec_CXX='${wl}-rpath,$libdir' + export_dynamic_flag_spec_CXX='${wl}-E' + # Hack: On Interix 3.x, we cannot compile PIC because of a broken gcc. + # Instead, shared libraries are loaded at an image base (0x10000000 by + # default) and relocated if they conflict, which is a slow very memory + # consuming and fragmenting process. To avoid this, we pick a random, + # 256 KiB-aligned image base between 0x50000000 and 0x6FFC0000 at link + # time. Moving up from 0x10000000 also allows more sbrk(2) space. + archive_cmds_CXX='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' + archive_expsym_cmds_CXX='sed "s,^,_," $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--retain-symbols-file,$output_objdir/$soname.expsym ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' + ;; + irix5* | irix6*) + case $cc_basename in + CC*) + # SGI C++ + archive_cmds_CXX='$CC -shared -all -multigot $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib' + + # Archives containing C++ object files must be created using + # "CC -ar", where "CC" is the IRIX C++ compiler. This is + # necessary to make sure instantiated templates are included + # in the archive. + old_archive_cmds_CXX='$CC -ar -WR,-u -o $oldlib $oldobjs' + ;; + *) + if test "$GXX" = yes; then + if test "$with_gnu_ld" = no; then + archive_cmds_CXX='$CC -shared $pic_flag -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' + else + archive_cmds_CXX='$CC -shared $pic_flag -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` -o $lib' + fi + fi + link_all_deplibs_CXX=yes + ;; + esac + hardcode_libdir_flag_spec_CXX='${wl}-rpath ${wl}$libdir' + hardcode_libdir_separator_CXX=: + inherit_rpath_CXX=yes + ;; + + linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) + case $cc_basename in + KCC*) + # Kuck and Associates, Inc. (KAI) C++ Compiler + + # KCC will only create a shared library if the output file + # ends with ".so" (or ".sl" for HP-UX), so rename the library + # to its proper name (with version) after linking. + archive_cmds_CXX='tempext=`echo $shared_ext | $SED -e '\''s/\([^()0-9A-Za-z{}]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\${tempext}\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib; mv \$templib $lib' + archive_expsym_cmds_CXX='tempext=`echo $shared_ext | $SED -e '\''s/\([^()0-9A-Za-z{}]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\${tempext}\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib ${wl}-retain-symbols-file,$export_symbols; mv \$templib $lib' + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + # + # There doesn't appear to be a way to prevent this compiler from + # explicitly linking system object files so we need to strip them + # from the output so that they don't get included in the library + # dependencies. + output_verbose_link_cmd='templist=`$CC $CFLAGS -v conftest.$objext -o libconftest$shared_ext 2>&1 | $GREP "ld"`; rm -f libconftest$shared_ext; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"' + + hardcode_libdir_flag_spec_CXX='${wl}-rpath,$libdir' + export_dynamic_flag_spec_CXX='${wl}--export-dynamic' + + # Archives containing C++ object files must be created using + # "CC -Bstatic", where "CC" is the KAI C++ compiler. + old_archive_cmds_CXX='$CC -Bstatic -o $oldlib $oldobjs' + ;; + icpc* | ecpc* ) + # Intel C++ + with_gnu_ld=yes + # version 8.0 and above of icpc choke on multiply defined symbols + # if we add $predep_objects and $postdep_objects, however 7.1 and + # earlier do not add the objects themselves. + case `$CC -V 2>&1` in + *"Version 7."*) + archive_cmds_CXX='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib' + archive_expsym_cmds_CXX='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' + ;; + *) # Version 8.0 or newer + tmp_idyn= + case $host_cpu in + ia64*) tmp_idyn=' -i_dynamic';; + esac + archive_cmds_CXX='$CC -shared'"$tmp_idyn"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + archive_expsym_cmds_CXX='$CC -shared'"$tmp_idyn"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' + ;; + esac + archive_cmds_need_lc_CXX=no + hardcode_libdir_flag_spec_CXX='${wl}-rpath,$libdir' + export_dynamic_flag_spec_CXX='${wl}--export-dynamic' + whole_archive_flag_spec_CXX='${wl}--whole-archive$convenience ${wl}--no-whole-archive' + ;; + pgCC* | pgcpp*) + # Portland Group C++ compiler + case `$CC -V` in + *pgCC\ [1-5].* | *pgcpp\ [1-5].*) + prelink_cmds_CXX='tpldir=Template.dir~ + rm -rf $tpldir~ + $CC --prelink_objects --instantiation_dir $tpldir $objs $libobjs $compile_deplibs~ + compile_command="$compile_command `find $tpldir -name \*.o | sort | $NL2SP`"' + old_archive_cmds_CXX='tpldir=Template.dir~ + rm -rf $tpldir~ + $CC --prelink_objects --instantiation_dir $tpldir $oldobjs$old_deplibs~ + $AR $AR_FLAGS $oldlib$oldobjs$old_deplibs `find $tpldir -name \*.o | sort | $NL2SP`~ + $RANLIB $oldlib' + archive_cmds_CXX='tpldir=Template.dir~ + rm -rf $tpldir~ + $CC --prelink_objects --instantiation_dir $tpldir $predep_objects $libobjs $deplibs $convenience $postdep_objects~ + $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | sort | $NL2SP` $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname -o $lib' + archive_expsym_cmds_CXX='tpldir=Template.dir~ + rm -rf $tpldir~ + $CC --prelink_objects --instantiation_dir $tpldir $predep_objects $libobjs $deplibs $convenience $postdep_objects~ + $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | sort | $NL2SP` $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname ${wl}-retain-symbols-file ${wl}$export_symbols -o $lib' + ;; + *) # Version 6 and above use weak symbols + archive_cmds_CXX='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname -o $lib' + archive_expsym_cmds_CXX='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname ${wl}-retain-symbols-file ${wl}$export_symbols -o $lib' + ;; + esac + + hardcode_libdir_flag_spec_CXX='${wl}--rpath ${wl}$libdir' + export_dynamic_flag_spec_CXX='${wl}--export-dynamic' + whole_archive_flag_spec_CXX='${wl}--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` ${wl}--no-whole-archive' + ;; + cxx*) + # Compaq C++ + archive_cmds_CXX='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib' + archive_expsym_cmds_CXX='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib ${wl}-retain-symbols-file $wl$export_symbols' + + runpath_var=LD_RUN_PATH + hardcode_libdir_flag_spec_CXX='-rpath $libdir' + hardcode_libdir_separator_CXX=: + + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + # + # There doesn't appear to be a way to prevent this compiler from + # explicitly linking system object files so we need to strip them + # from the output so that they don't get included in the library + # dependencies. + output_verbose_link_cmd='templist=`$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP "ld"`; templist=`func_echo_all "$templist" | $SED "s/\(^.*ld.*\)\( .*ld .*$\)/\1/"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "X$list" | $Xsed' + ;; + xl* | mpixl* | bgxl*) + # IBM XL 8.0 on PPC, with GNU ld + hardcode_libdir_flag_spec_CXX='${wl}-rpath ${wl}$libdir' + export_dynamic_flag_spec_CXX='${wl}--export-dynamic' + archive_cmds_CXX='$CC -qmkshrobj $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + if test "x$supports_anon_versioning" = xyes; then + archive_expsym_cmds_CXX='echo "{ global:" > $output_objdir/$libname.ver~ + cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ + echo "local: *; };" >> $output_objdir/$libname.ver~ + $CC -qmkshrobj $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-version-script ${wl}$output_objdir/$libname.ver -o $lib' + fi + ;; + *) + case `$CC -V 2>&1 | sed 5q` in + *Sun\ C*) + # Sun C++ 5.9 + no_undefined_flag_CXX=' -zdefs' + archive_cmds_CXX='$CC -G${allow_undefined_flag} -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + archive_expsym_cmds_CXX='$CC -G${allow_undefined_flag} -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-retain-symbols-file ${wl}$export_symbols' + hardcode_libdir_flag_spec_CXX='-R$libdir' + whole_archive_flag_spec_CXX='${wl}--whole-archive`new_convenience=; for conv in $convenience\"\"; do test -z \"$conv\" || new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` ${wl}--no-whole-archive' + compiler_needs_object_CXX=yes + + # Not sure whether something based on + # $CC $CFLAGS -v conftest.$objext -o libconftest$shared_ext 2>&1 + # would be better. + output_verbose_link_cmd='func_echo_all' + + # Archives containing C++ object files must be created using + # "CC -xar", where "CC" is the Sun C++ compiler. This is + # necessary to make sure instantiated templates are included + # in the archive. + old_archive_cmds_CXX='$CC -xar -o $oldlib $oldobjs' + ;; + esac + ;; + esac + ;; + + lynxos*) + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + + m88k*) + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + + mvs*) + case $cc_basename in + cxx*) + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + *) + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + esac + ;; + + netbsd*) + if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then + archive_cmds_CXX='$LD -Bshareable -o $lib $predep_objects $libobjs $deplibs $postdep_objects $linker_flags' + wlarc= + hardcode_libdir_flag_spec_CXX='-R$libdir' + hardcode_direct_CXX=yes + hardcode_shlibpath_var_CXX=no + fi + # Workaround some broken pre-1.5 toolchains + output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP conftest.$objext | $SED -e "s:-lgcc -lc -lgcc::"' + ;; + + *nto* | *qnx*) + ld_shlibs_CXX=yes + ;; + + openbsd2*) + # C++ shared libraries are fairly broken + ld_shlibs_CXX=no + ;; + + openbsd*) + if test -f /usr/libexec/ld.so; then + hardcode_direct_CXX=yes + hardcode_shlibpath_var_CXX=no + hardcode_direct_absolute_CXX=yes + archive_cmds_CXX='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $lib' + hardcode_libdir_flag_spec_CXX='${wl}-rpath,$libdir' + if test -z "`echo __ELF__ | $CC -E - | grep __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then + archive_expsym_cmds_CXX='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-retain-symbols-file,$export_symbols -o $lib' + export_dynamic_flag_spec_CXX='${wl}-E' + whole_archive_flag_spec_CXX="$wlarc"'--whole-archive$convenience '"$wlarc"'--no-whole-archive' + fi + output_verbose_link_cmd=func_echo_all + else + ld_shlibs_CXX=no + fi + ;; + + osf3* | osf4* | osf5*) + case $cc_basename in + KCC*) + # Kuck and Associates, Inc. (KAI) C++ Compiler + + # KCC will only create a shared library if the output file + # ends with ".so" (or ".sl" for HP-UX), so rename the library + # to its proper name (with version) after linking. + archive_cmds_CXX='tempext=`echo $shared_ext | $SED -e '\''s/\([^()0-9A-Za-z{}]\)/\\\\\1/g'\''`; templib=`echo "$lib" | $SED -e "s/\${tempext}\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib; mv \$templib $lib' + + hardcode_libdir_flag_spec_CXX='${wl}-rpath,$libdir' + hardcode_libdir_separator_CXX=: + + # Archives containing C++ object files must be created using + # the KAI C++ compiler. + case $host in + osf3*) old_archive_cmds_CXX='$CC -Bstatic -o $oldlib $oldobjs' ;; + *) old_archive_cmds_CXX='$CC -o $oldlib $oldobjs' ;; + esac + ;; + RCC*) + # Rational C++ 2.4.1 + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + cxx*) + case $host in + osf3*) + allow_undefined_flag_CXX=' ${wl}-expect_unresolved ${wl}\*' + archive_cmds_CXX='$CC -shared${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $soname `test -n "$verstring" && func_echo_all "${wl}-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib' + hardcode_libdir_flag_spec_CXX='${wl}-rpath ${wl}$libdir' + ;; + *) + allow_undefined_flag_CXX=' -expect_unresolved \*' + archive_cmds_CXX='$CC -shared${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -msym -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib' + archive_expsym_cmds_CXX='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done~ + echo "-hidden">> $lib.exp~ + $CC -shared$allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -msym -soname $soname ${wl}-input ${wl}$lib.exp `test -n "$verstring" && $ECHO "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib~ + $RM $lib.exp' + hardcode_libdir_flag_spec_CXX='-rpath $libdir' + ;; + esac + + hardcode_libdir_separator_CXX=: + + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + # + # There doesn't appear to be a way to prevent this compiler from + # explicitly linking system object files so we need to strip them + # from the output so that they don't get included in the library + # dependencies. + output_verbose_link_cmd='templist=`$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP "ld" | $GREP -v "ld:"`; templist=`func_echo_all "$templist" | $SED "s/\(^.*ld.*\)\( .*ld.*$\)/\1/"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"' + ;; + *) + if test "$GXX" = yes && test "$with_gnu_ld" = no; then + allow_undefined_flag_CXX=' ${wl}-expect_unresolved ${wl}\*' + case $host in + osf3*) + archive_cmds_CXX='$CC -shared -nostdlib ${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' + ;; + *) + archive_cmds_CXX='$CC -shared $pic_flag -nostdlib ${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-msym ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' + ;; + esac + + hardcode_libdir_flag_spec_CXX='${wl}-rpath ${wl}$libdir' + hardcode_libdir_separator_CXX=: + + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"' + + else + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + fi + ;; + esac + ;; + + psos*) + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + + sunos4*) + case $cc_basename in + CC*) + # Sun C++ 4.x + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + lcc*) + # Lucid + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + *) + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + esac + ;; + + solaris*) + case $cc_basename in + CC* | sunCC*) + # Sun C++ 4.2, 5.x and Centerline C++ + archive_cmds_need_lc_CXX=yes + no_undefined_flag_CXX=' -zdefs' + archive_cmds_CXX='$CC -G${allow_undefined_flag} -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + archive_expsym_cmds_CXX='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ + $CC -G${allow_undefined_flag} ${wl}-M ${wl}$lib.exp -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp' + + hardcode_libdir_flag_spec_CXX='-R$libdir' + hardcode_shlibpath_var_CXX=no + case $host_os in + solaris2.[0-5] | solaris2.[0-5].*) ;; + *) + # The compiler driver will combine and reorder linker options, + # but understands `-z linker_flag'. + # Supported since Solaris 2.6 (maybe 2.5.1?) + whole_archive_flag_spec_CXX='-z allextract$convenience -z defaultextract' + ;; + esac + link_all_deplibs_CXX=yes + + output_verbose_link_cmd='func_echo_all' + + # Archives containing C++ object files must be created using + # "CC -xar", where "CC" is the Sun C++ compiler. This is + # necessary to make sure instantiated templates are included + # in the archive. + old_archive_cmds_CXX='$CC -xar -o $oldlib $oldobjs' + ;; + gcx*) + # Green Hills C++ Compiler + archive_cmds_CXX='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-h $wl$soname -o $lib' + + # The C++ compiler must be used to create the archive. + old_archive_cmds_CXX='$CC $LDFLAGS -archive -o $oldlib $oldobjs' + ;; + *) + # GNU C++ compiler with Solaris linker + if test "$GXX" = yes && test "$with_gnu_ld" = no; then + no_undefined_flag_CXX=' ${wl}-z ${wl}defs' + if $CC --version | $GREP -v '^2\.7' > /dev/null; then + archive_cmds_CXX='$CC -shared $pic_flag -nostdlib $LDFLAGS $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-h $wl$soname -o $lib' + archive_expsym_cmds_CXX='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ + $CC -shared $pic_flag -nostdlib ${wl}-M $wl$lib.exp -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp' + + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"' + else + # g++ 2.7 appears to require `-G' NOT `-shared' on this + # platform. + archive_cmds_CXX='$CC -G -nostdlib $LDFLAGS $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-h $wl$soname -o $lib' + archive_expsym_cmds_CXX='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ + $CC -G -nostdlib ${wl}-M $wl$lib.exp -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp' + + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + output_verbose_link_cmd='$CC -G $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"' + fi + + hardcode_libdir_flag_spec_CXX='${wl}-R $wl$libdir' + case $host_os in + solaris2.[0-5] | solaris2.[0-5].*) ;; + *) + whole_archive_flag_spec_CXX='${wl}-z ${wl}allextract$convenience ${wl}-z ${wl}defaultextract' + ;; + esac + fi + ;; + esac + ;; + + sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[01].[10]* | unixware7* | sco3.2v5.0.[024]*) + no_undefined_flag_CXX='${wl}-z,text' + archive_cmds_need_lc_CXX=no + hardcode_shlibpath_var_CXX=no + runpath_var='LD_RUN_PATH' + + case $cc_basename in + CC*) + archive_cmds_CXX='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + archive_expsym_cmds_CXX='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + ;; + *) + archive_cmds_CXX='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + archive_expsym_cmds_CXX='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + ;; + esac + ;; + + sysv5* | sco3.2v5* | sco5v6*) + # Note: We can NOT use -z defs as we might desire, because we do not + # link with -lc, and that would cause any symbols used from libc to + # always be unresolved, which means just about no library would + # ever link correctly. If we're not using GNU ld we use -z text + # though, which does catch some bad symbols but isn't as heavy-handed + # as -z defs. + no_undefined_flag_CXX='${wl}-z,text' + allow_undefined_flag_CXX='${wl}-z,nodefs' + archive_cmds_need_lc_CXX=no + hardcode_shlibpath_var_CXX=no + hardcode_libdir_flag_spec_CXX='${wl}-R,$libdir' + hardcode_libdir_separator_CXX=':' + link_all_deplibs_CXX=yes + export_dynamic_flag_spec_CXX='${wl}-Bexport' + runpath_var='LD_RUN_PATH' + + case $cc_basename in + CC*) + archive_cmds_CXX='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + archive_expsym_cmds_CXX='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + old_archive_cmds_CXX='$CC -Tprelink_objects $oldobjs~ + '"$old_archive_cmds_CXX" + reload_cmds_CXX='$CC -Tprelink_objects $reload_objs~ + '"$reload_cmds_CXX" + ;; + *) + archive_cmds_CXX='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + archive_expsym_cmds_CXX='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + ;; + esac + ;; + + tandem*) + case $cc_basename in + NCC*) + # NonStop-UX NCC 3.20 + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + *) + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + esac + ;; + + vxworks*) + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + + *) + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + esac + + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ld_shlibs_CXX" >&5 +$as_echo "$ld_shlibs_CXX" >&6; } + test "$ld_shlibs_CXX" = no && can_build_shared=no + + GCC_CXX="$GXX" + LD_CXX="$LD" + + ## CAVEAT EMPTOR: + ## There is no encapsulation within the following macros, do not change + ## the running order or otherwise move them around unless you know exactly + ## what you are doing... + # Dependencies to place before and after the object being linked: +predep_objects_CXX= +postdep_objects_CXX= +predeps_CXX= +postdeps_CXX= +compiler_lib_search_path_CXX= + +cat > conftest.$ac_ext <<_LT_EOF +class Foo +{ +public: + Foo (void) { a = 0; } +private: + int a; +}; +_LT_EOF + + +_lt_libdeps_save_CFLAGS=$CFLAGS +case "$CC $CFLAGS " in #( +*\ -flto*\ *) CFLAGS="$CFLAGS -fno-lto" ;; +*\ -fwhopr*\ *) CFLAGS="$CFLAGS -fno-whopr" ;; +*\ -fuse-linker-plugin*\ *) CFLAGS="$CFLAGS -fno-use-linker-plugin" ;; +esac + +if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then + # Parse the compiler output and extract the necessary + # objects, libraries and library flags. + + # Sentinel used to keep track of whether or not we are before + # the conftest object file. + pre_test_object_deps_done=no + + for p in `eval "$output_verbose_link_cmd"`; do + case ${prev}${p} in + + -L* | -R* | -l*) + # Some compilers place space between "-{L,R}" and the path. + # Remove the space. + if test $p = "-L" || + test $p = "-R"; then + prev=$p + continue + fi + + # Expand the sysroot to ease extracting the directories later. + if test -z "$prev"; then + case $p in + -L*) func_stripname_cnf '-L' '' "$p"; prev=-L; p=$func_stripname_result ;; + -R*) func_stripname_cnf '-R' '' "$p"; prev=-R; p=$func_stripname_result ;; + -l*) func_stripname_cnf '-l' '' "$p"; prev=-l; p=$func_stripname_result ;; + esac + fi + case $p in + =*) func_stripname_cnf '=' '' "$p"; p=$lt_sysroot$func_stripname_result ;; + esac + if test "$pre_test_object_deps_done" = no; then + case ${prev} in + -L | -R) + # Internal compiler library paths should come after those + # provided the user. The postdeps already come after the + # user supplied libs so there is no need to process them. + if test -z "$compiler_lib_search_path_CXX"; then + compiler_lib_search_path_CXX="${prev}${p}" + else + compiler_lib_search_path_CXX="${compiler_lib_search_path_CXX} ${prev}${p}" + fi + ;; + # The "-l" case would never come before the object being + # linked, so don't bother handling this case. + esac + else + if test -z "$postdeps_CXX"; then + postdeps_CXX="${prev}${p}" + else + postdeps_CXX="${postdeps_CXX} ${prev}${p}" + fi + fi + prev= + ;; + + *.lto.$objext) ;; # Ignore GCC LTO objects + *.$objext) + # This assumes that the test object file only shows up + # once in the compiler output. + if test "$p" = "conftest.$objext"; then + pre_test_object_deps_done=yes + continue + fi + + if test "$pre_test_object_deps_done" = no; then + if test -z "$predep_objects_CXX"; then + predep_objects_CXX="$p" + else + predep_objects_CXX="$predep_objects_CXX $p" + fi + else + if test -z "$postdep_objects_CXX"; then + postdep_objects_CXX="$p" + else + postdep_objects_CXX="$postdep_objects_CXX $p" + fi + fi + ;; + + *) ;; # Ignore the rest. + + esac + done + + # Clean up. + rm -f a.out a.exe +else + echo "libtool.m4: error: problem compiling CXX test program" +fi + +$RM -f confest.$objext +CFLAGS=$_lt_libdeps_save_CFLAGS + +# PORTME: override above test on systems where it is broken +case $host_os in +interix[3-9]*) + # Interix 3.5 installs completely hosed .la files for C++, so rather than + # hack all around it, let's just trust "g++" to DTRT. + predep_objects_CXX= + postdep_objects_CXX= + postdeps_CXX= + ;; + +linux*) + case `$CC -V 2>&1 | sed 5q` in + *Sun\ C*) + # Sun C++ 5.9 + + # The more standards-conforming stlport4 library is + # incompatible with the Cstd library. Avoid specifying + # it if it's in CXXFLAGS. Ignore libCrun as + # -library=stlport4 depends on it. + case " $CXX $CXXFLAGS " in + *" -library=stlport4 "*) + solaris_use_stlport4=yes + ;; + esac + + if test "$solaris_use_stlport4" != yes; then + postdeps_CXX='-library=Cstd -library=Crun' + fi + ;; + esac + ;; + +solaris*) + case $cc_basename in + CC* | sunCC*) + # The more standards-conforming stlport4 library is + # incompatible with the Cstd library. Avoid specifying + # it if it's in CXXFLAGS. Ignore libCrun as + # -library=stlport4 depends on it. + case " $CXX $CXXFLAGS " in + *" -library=stlport4 "*) + solaris_use_stlport4=yes + ;; + esac + + # Adding this requires a known-good setup of shared libraries for + # Sun compiler versions before 5.6, else PIC objects from an old + # archive will be linked into the output, leading to subtle bugs. + if test "$solaris_use_stlport4" != yes; then + postdeps_CXX='-library=Cstd -library=Crun' + fi + ;; + esac + ;; +esac + + +case " $postdeps_CXX " in +*" -lc "*) archive_cmds_need_lc_CXX=no ;; +esac + compiler_lib_search_dirs_CXX= +if test -n "${compiler_lib_search_path_CXX}"; then + compiler_lib_search_dirs_CXX=`echo " ${compiler_lib_search_path_CXX}" | ${SED} -e 's! -L! !g' -e 's!^ !!'` +fi + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + lt_prog_compiler_wl_CXX= +lt_prog_compiler_pic_CXX= +lt_prog_compiler_static_CXX= + + + # C++ specific cases for pic, static, wl, etc. + if test "$GXX" = yes; then + lt_prog_compiler_wl_CXX='-Wl,' + lt_prog_compiler_static_CXX='-static' + + case $host_os in + aix*) + # All AIX code is PIC. + if test "$host_cpu" = ia64; then + # AIX 5 now supports IA64 processor + lt_prog_compiler_static_CXX='-Bstatic' + fi + ;; + + amigaos*) + case $host_cpu in + powerpc) + # see comment about AmigaOS4 .so support + lt_prog_compiler_pic_CXX='-fPIC' + ;; + m68k) + # FIXME: we need at least 68020 code to build shared libraries, but + # adding the `-m68020' flag to GCC prevents building anything better, + # like `-m68040'. + lt_prog_compiler_pic_CXX='-m68020 -resident32 -malways-restore-a4' + ;; + esac + ;; + + beos* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*) + # PIC is the default for these OSes. + ;; + mingw* | cygwin* | os2* | pw32* | cegcc*) + # This hack is so that the source file can tell whether it is being + # built for inclusion in a dll (and should export symbols for example). + # Although the cygwin gcc ignores -fPIC, still need this for old-style + # (--disable-auto-import) libraries + lt_prog_compiler_pic_CXX='-DDLL_EXPORT' + ;; + darwin* | rhapsody*) + # PIC is the default on this platform + # Common symbols not allowed in MH_DYLIB files + lt_prog_compiler_pic_CXX='-fno-common' + ;; + *djgpp*) + # DJGPP does not support shared libraries at all + lt_prog_compiler_pic_CXX= + ;; + haiku*) + # PIC is the default for Haiku. + # The "-static" flag exists, but is broken. + lt_prog_compiler_static_CXX= + ;; + interix[3-9]*) + # Interix 3.x gcc -fpic/-fPIC options generate broken code. + # Instead, we relocate shared libraries at runtime. + ;; + sysv4*MP*) + if test -d /usr/nec; then + lt_prog_compiler_pic_CXX=-Kconform_pic + fi + ;; + hpux*) + # PIC is the default for 64-bit PA HP-UX, but not for 32-bit + # PA HP-UX. On IA64 HP-UX, PIC is the default but the pic flag + # sets the default TLS model and affects inlining. + case $host_cpu in + hppa*64*) + ;; + *) + lt_prog_compiler_pic_CXX='-fPIC' + ;; + esac + ;; + *qnx* | *nto*) + # QNX uses GNU C++, but need to define -shared option too, otherwise + # it will coredump. + lt_prog_compiler_pic_CXX='-fPIC -shared' + ;; + *) + lt_prog_compiler_pic_CXX='-fPIC' + ;; + esac + else + case $host_os in + aix[4-9]*) + # All AIX code is PIC. + if test "$host_cpu" = ia64; then + # AIX 5 now supports IA64 processor + lt_prog_compiler_static_CXX='-Bstatic' + else + lt_prog_compiler_static_CXX='-bnso -bI:/lib/syscalls.exp' + fi + ;; + chorus*) + case $cc_basename in + cxch68*) + # Green Hills C++ Compiler + # _LT_TAGVAR(lt_prog_compiler_static, CXX)="--no_auto_instantiation -u __main -u __premain -u _abort -r $COOL_DIR/lib/libOrb.a $MVME_DIR/lib/CC/libC.a $MVME_DIR/lib/classix/libcx.s.a" + ;; + esac + ;; + mingw* | cygwin* | os2* | pw32* | cegcc*) + # This hack is so that the source file can tell whether it is being + # built for inclusion in a dll (and should export symbols for example). + lt_prog_compiler_pic_CXX='-DDLL_EXPORT' + ;; + dgux*) + case $cc_basename in + ec++*) + lt_prog_compiler_pic_CXX='-KPIC' + ;; + ghcx*) + # Green Hills C++ Compiler + lt_prog_compiler_pic_CXX='-pic' + ;; + *) + ;; + esac + ;; + freebsd* | dragonfly*) + # FreeBSD uses GNU C++ + ;; + hpux9* | hpux10* | hpux11*) + case $cc_basename in + CC*) + lt_prog_compiler_wl_CXX='-Wl,' + lt_prog_compiler_static_CXX='${wl}-a ${wl}archive' + if test "$host_cpu" != ia64; then + lt_prog_compiler_pic_CXX='+Z' + fi + ;; + aCC*) + lt_prog_compiler_wl_CXX='-Wl,' + lt_prog_compiler_static_CXX='${wl}-a ${wl}archive' + case $host_cpu in + hppa*64*|ia64*) + # +Z the default + ;; + *) + lt_prog_compiler_pic_CXX='+Z' + ;; + esac + ;; + *) + ;; + esac + ;; + interix*) + # This is c89, which is MS Visual C++ (no shared libs) + # Anyone wants to do a port? + ;; + irix5* | irix6* | nonstopux*) + case $cc_basename in + CC*) + lt_prog_compiler_wl_CXX='-Wl,' + lt_prog_compiler_static_CXX='-non_shared' + # CC pic flag -KPIC is the default. + ;; + *) + ;; + esac + ;; + linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) + case $cc_basename in + KCC*) + # KAI C++ Compiler + lt_prog_compiler_wl_CXX='--backend -Wl,' + lt_prog_compiler_pic_CXX='-fPIC' + ;; + ecpc* ) + # old Intel C++ for x86_64 which still supported -KPIC. + lt_prog_compiler_wl_CXX='-Wl,' + lt_prog_compiler_pic_CXX='-KPIC' + lt_prog_compiler_static_CXX='-static' + ;; + icpc* ) + # Intel C++, used to be incompatible with GCC. + # ICC 10 doesn't accept -KPIC any more. + lt_prog_compiler_wl_CXX='-Wl,' + lt_prog_compiler_pic_CXX='-fPIC' + lt_prog_compiler_static_CXX='-static' + ;; + pgCC* | pgcpp*) + # Portland Group C++ compiler + lt_prog_compiler_wl_CXX='-Wl,' + lt_prog_compiler_pic_CXX='-fpic' + lt_prog_compiler_static_CXX='-Bstatic' + ;; + cxx*) + # Compaq C++ + # Make sure the PIC flag is empty. It appears that all Alpha + # Linux and Compaq Tru64 Unix objects are PIC. + lt_prog_compiler_pic_CXX= + lt_prog_compiler_static_CXX='-non_shared' + ;; + xlc* | xlC* | bgxl[cC]* | mpixl[cC]*) + # IBM XL 8.0, 9.0 on PPC and BlueGene + lt_prog_compiler_wl_CXX='-Wl,' + lt_prog_compiler_pic_CXX='-qpic' + lt_prog_compiler_static_CXX='-qstaticlink' + ;; + *) + case `$CC -V 2>&1 | sed 5q` in + *Sun\ C*) + # Sun C++ 5.9 + lt_prog_compiler_pic_CXX='-KPIC' + lt_prog_compiler_static_CXX='-Bstatic' + lt_prog_compiler_wl_CXX='-Qoption ld ' + ;; + esac + ;; + esac + ;; + lynxos*) + ;; + m88k*) + ;; + mvs*) + case $cc_basename in + cxx*) + lt_prog_compiler_pic_CXX='-W c,exportall' + ;; + *) + ;; + esac + ;; + netbsd* | netbsdelf*-gnu) + ;; + *qnx* | *nto*) + # QNX uses GNU C++, but need to define -shared option too, otherwise + # it will coredump. + lt_prog_compiler_pic_CXX='-fPIC -shared' + ;; + osf3* | osf4* | osf5*) + case $cc_basename in + KCC*) + lt_prog_compiler_wl_CXX='--backend -Wl,' + ;; + RCC*) + # Rational C++ 2.4.1 + lt_prog_compiler_pic_CXX='-pic' + ;; + cxx*) + # Digital/Compaq C++ + lt_prog_compiler_wl_CXX='-Wl,' + # Make sure the PIC flag is empty. It appears that all Alpha + # Linux and Compaq Tru64 Unix objects are PIC. + lt_prog_compiler_pic_CXX= + lt_prog_compiler_static_CXX='-non_shared' + ;; + *) + ;; + esac + ;; + psos*) + ;; + solaris*) + case $cc_basename in + CC* | sunCC*) + # Sun C++ 4.2, 5.x and Centerline C++ + lt_prog_compiler_pic_CXX='-KPIC' + lt_prog_compiler_static_CXX='-Bstatic' + lt_prog_compiler_wl_CXX='-Qoption ld ' + ;; + gcx*) + # Green Hills C++ Compiler + lt_prog_compiler_pic_CXX='-PIC' + ;; + *) + ;; + esac + ;; + sunos4*) + case $cc_basename in + CC*) + # Sun C++ 4.x + lt_prog_compiler_pic_CXX='-pic' + lt_prog_compiler_static_CXX='-Bstatic' + ;; + lcc*) + # Lucid + lt_prog_compiler_pic_CXX='-pic' + ;; + *) + ;; + esac + ;; + sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*) + case $cc_basename in + CC*) + lt_prog_compiler_wl_CXX='-Wl,' + lt_prog_compiler_pic_CXX='-KPIC' + lt_prog_compiler_static_CXX='-Bstatic' + ;; + esac + ;; + tandem*) + case $cc_basename in + NCC*) + # NonStop-UX NCC 3.20 + lt_prog_compiler_pic_CXX='-KPIC' + ;; + *) + ;; + esac + ;; + vxworks*) + ;; + *) + lt_prog_compiler_can_build_shared_CXX=no + ;; + esac + fi + +case $host_os in + # For platforms which do not support PIC, -DPIC is meaningless: + *djgpp*) + lt_prog_compiler_pic_CXX= + ;; + *) + lt_prog_compiler_pic_CXX="$lt_prog_compiler_pic_CXX -DPIC" + ;; +esac + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $compiler option to produce PIC" >&5 +$as_echo_n "checking for $compiler option to produce PIC... " >&6; } +if ${lt_cv_prog_compiler_pic_CXX+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_prog_compiler_pic_CXX=$lt_prog_compiler_pic_CXX +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_pic_CXX" >&5 +$as_echo "$lt_cv_prog_compiler_pic_CXX" >&6; } +lt_prog_compiler_pic_CXX=$lt_cv_prog_compiler_pic_CXX + +# +# Check to make sure the PIC flag actually works. +# +if test -n "$lt_prog_compiler_pic_CXX"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler PIC flag $lt_prog_compiler_pic_CXX works" >&5 +$as_echo_n "checking if $compiler PIC flag $lt_prog_compiler_pic_CXX works... " >&6; } +if ${lt_cv_prog_compiler_pic_works_CXX+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_prog_compiler_pic_works_CXX=no + ac_outfile=conftest.$ac_objext + echo "$lt_simple_compile_test_code" > conftest.$ac_ext + lt_compiler_flag="$lt_prog_compiler_pic_CXX -DPIC" + # Insert the option either (1) after the last *FLAGS variable, or + # (2) before a word containing "conftest.", or (3) at the end. + # Note that $ac_compile itself does not contain backslashes and begins + # with a dollar sign (not a hyphen), so the echo should work correctly. + # The option is referenced via a variable to avoid confusing sed. + lt_compile=`echo "$ac_compile" | $SED \ + -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ + -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ + -e 's:$: $lt_compiler_flag:'` + (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5) + (eval "$lt_compile" 2>conftest.err) + ac_status=$? + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + if (exit $ac_status) && test -s "$ac_outfile"; then + # The compiler can only warn and ignore the option if not recognized + # So say no if there are warnings other than the usual output. + $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' >conftest.exp + $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 + if test ! -s conftest.er2 || diff conftest.exp conftest.er2 >/dev/null; then + lt_cv_prog_compiler_pic_works_CXX=yes + fi + fi + $RM conftest* + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_pic_works_CXX" >&5 +$as_echo "$lt_cv_prog_compiler_pic_works_CXX" >&6; } + +if test x"$lt_cv_prog_compiler_pic_works_CXX" = xyes; then + case $lt_prog_compiler_pic_CXX in + "" | " "*) ;; + *) lt_prog_compiler_pic_CXX=" $lt_prog_compiler_pic_CXX" ;; + esac +else + lt_prog_compiler_pic_CXX= + lt_prog_compiler_can_build_shared_CXX=no +fi + +fi + + + + + +# +# Check to make sure the static flag actually works. +# +wl=$lt_prog_compiler_wl_CXX eval lt_tmp_static_flag=\"$lt_prog_compiler_static_CXX\" +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler static flag $lt_tmp_static_flag works" >&5 +$as_echo_n "checking if $compiler static flag $lt_tmp_static_flag works... " >&6; } +if ${lt_cv_prog_compiler_static_works_CXX+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_prog_compiler_static_works_CXX=no + save_LDFLAGS="$LDFLAGS" + LDFLAGS="$LDFLAGS $lt_tmp_static_flag" + echo "$lt_simple_link_test_code" > conftest.$ac_ext + if (eval $ac_link 2>conftest.err) && test -s conftest$ac_exeext; then + # The linker can only warn and ignore the option if not recognized + # So say no if there are warnings + if test -s conftest.err; then + # Append any errors to the config.log. + cat conftest.err 1>&5 + $ECHO "$_lt_linker_boilerplate" | $SED '/^$/d' > conftest.exp + $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 + if diff conftest.exp conftest.er2 >/dev/null; then + lt_cv_prog_compiler_static_works_CXX=yes + fi + else + lt_cv_prog_compiler_static_works_CXX=yes + fi + fi + $RM -r conftest* + LDFLAGS="$save_LDFLAGS" + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_static_works_CXX" >&5 +$as_echo "$lt_cv_prog_compiler_static_works_CXX" >&6; } + +if test x"$lt_cv_prog_compiler_static_works_CXX" = xyes; then + : +else + lt_prog_compiler_static_CXX= +fi + + + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler supports -c -o file.$ac_objext" >&5 +$as_echo_n "checking if $compiler supports -c -o file.$ac_objext... " >&6; } +if ${lt_cv_prog_compiler_c_o_CXX+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_prog_compiler_c_o_CXX=no + $RM -r conftest 2>/dev/null + mkdir conftest + cd conftest + mkdir out + echo "$lt_simple_compile_test_code" > conftest.$ac_ext + + lt_compiler_flag="-o out/conftest2.$ac_objext" + # Insert the option either (1) after the last *FLAGS variable, or + # (2) before a word containing "conftest.", or (3) at the end. + # Note that $ac_compile itself does not contain backslashes and begins + # with a dollar sign (not a hyphen), so the echo should work correctly. + lt_compile=`echo "$ac_compile" | $SED \ + -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ + -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ + -e 's:$: $lt_compiler_flag:'` + (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5) + (eval "$lt_compile" 2>out/conftest.err) + ac_status=$? + cat out/conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + if (exit $ac_status) && test -s out/conftest2.$ac_objext + then + # The compiler can only warn and ignore the option if not recognized + # So say no if there are warnings + $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' > out/conftest.exp + $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2 + if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then + lt_cv_prog_compiler_c_o_CXX=yes + fi + fi + chmod u+w . 2>&5 + $RM conftest* + # SGI C++ compiler will create directory out/ii_files/ for + # template instantiation + test -d out/ii_files && $RM out/ii_files/* && rmdir out/ii_files + $RM out/* && rmdir out + cd .. + $RM -r conftest + $RM conftest* + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_c_o_CXX" >&5 +$as_echo "$lt_cv_prog_compiler_c_o_CXX" >&6; } + + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler supports -c -o file.$ac_objext" >&5 +$as_echo_n "checking if $compiler supports -c -o file.$ac_objext... " >&6; } +if ${lt_cv_prog_compiler_c_o_CXX+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_prog_compiler_c_o_CXX=no + $RM -r conftest 2>/dev/null + mkdir conftest + cd conftest + mkdir out + echo "$lt_simple_compile_test_code" > conftest.$ac_ext + + lt_compiler_flag="-o out/conftest2.$ac_objext" + # Insert the option either (1) after the last *FLAGS variable, or + # (2) before a word containing "conftest.", or (3) at the end. + # Note that $ac_compile itself does not contain backslashes and begins + # with a dollar sign (not a hyphen), so the echo should work correctly. + lt_compile=`echo "$ac_compile" | $SED \ + -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ + -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ + -e 's:$: $lt_compiler_flag:'` + (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5) + (eval "$lt_compile" 2>out/conftest.err) + ac_status=$? + cat out/conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + if (exit $ac_status) && test -s out/conftest2.$ac_objext + then + # The compiler can only warn and ignore the option if not recognized + # So say no if there are warnings + $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' > out/conftest.exp + $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2 + if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then + lt_cv_prog_compiler_c_o_CXX=yes + fi + fi + chmod u+w . 2>&5 + $RM conftest* + # SGI C++ compiler will create directory out/ii_files/ for + # template instantiation + test -d out/ii_files && $RM out/ii_files/* && rmdir out/ii_files + $RM out/* && rmdir out + cd .. + $RM -r conftest + $RM conftest* + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_c_o_CXX" >&5 +$as_echo "$lt_cv_prog_compiler_c_o_CXX" >&6; } + + + + +hard_links="nottested" +if test "$lt_cv_prog_compiler_c_o_CXX" = no && test "$need_locks" != no; then + # do not overwrite the value of need_locks provided by the user + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if we can lock with hard links" >&5 +$as_echo_n "checking if we can lock with hard links... " >&6; } + hard_links=yes + $RM conftest* + ln conftest.a conftest.b 2>/dev/null && hard_links=no + touch conftest.a + ln conftest.a conftest.b 2>&5 || hard_links=no + ln conftest.a conftest.b 2>/dev/null && hard_links=no + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $hard_links" >&5 +$as_echo "$hard_links" >&6; } + if test "$hard_links" = no; then + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: \`$CC' does not support \`-c -o', so \`make -j' may be unsafe" >&5 +$as_echo "$as_me: WARNING: \`$CC' does not support \`-c -o', so \`make -j' may be unsafe" >&2;} + need_locks=warn + fi +else + need_locks=no +fi + + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the $compiler linker ($LD) supports shared libraries" >&5 +$as_echo_n "checking whether the $compiler linker ($LD) supports shared libraries... " >&6; } + + export_symbols_cmds_CXX='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' + exclude_expsyms_CXX='_GLOBAL_OFFSET_TABLE_|_GLOBAL__F[ID]_.*' + case $host_os in + aix[4-9]*) + # If we're using GNU nm, then we don't want the "-C" option. + # -C means demangle to AIX nm, but means don't demangle with GNU nm + # Also, AIX nm treats weak defined symbols like other global defined + # symbols, whereas GNU nm marks them as "W". + if $NM -V 2>&1 | $GREP 'GNU' > /dev/null; then + export_symbols_cmds_CXX='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W")) && (substr(\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols' + else + export_symbols_cmds_CXX='$NM -BCpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B")) && (substr(\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols' + fi + ;; + pw32*) + export_symbols_cmds_CXX="$ltdll_cmds" + ;; + cygwin* | mingw* | cegcc*) + case $cc_basename in + cl*) + exclude_expsyms_CXX='_NULL_IMPORT_DESCRIPTOR|_IMPORT_DESCRIPTOR_.*' + ;; + *) + export_symbols_cmds_CXX='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[BCDGRS][ ]/s/.*[ ]\([^ ]*\)/\1 DATA/;s/^.*[ ]__nm__\([^ ]*\)[ ][^ ]*/\1 DATA/;/^I[ ]/d;/^[AITW][ ]/s/.* //'\'' | sort | uniq > $export_symbols' + exclude_expsyms_CXX='[_]+GLOBAL_OFFSET_TABLE_|[_]+GLOBAL__[FID]_.*|[_]+head_[A-Za-z0-9_]+_dll|[A-Za-z0-9_]+_dll_iname' + ;; + esac + ;; + linux* | k*bsd*-gnu | gnu*) + link_all_deplibs_CXX=no + ;; + *) + export_symbols_cmds_CXX='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' + ;; + esac + +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ld_shlibs_CXX" >&5 +$as_echo "$ld_shlibs_CXX" >&6; } +test "$ld_shlibs_CXX" = no && can_build_shared=no + +with_gnu_ld_CXX=$with_gnu_ld + + + + + + +# +# Do we need to explicitly link libc? +# +case "x$archive_cmds_need_lc_CXX" in +x|xyes) + # Assume -lc should be added + archive_cmds_need_lc_CXX=yes + + if test "$enable_shared" = yes && test "$GCC" = yes; then + case $archive_cmds_CXX in + *'~'*) + # FIXME: we may have to deal with multi-command sequences. + ;; + '$CC '*) + # Test whether the compiler implicitly links with -lc since on some + # systems, -lgcc has to come before -lc. If gcc already passes -lc + # to ld, don't add -lc before -lgcc. + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether -lc should be explicitly linked in" >&5 +$as_echo_n "checking whether -lc should be explicitly linked in... " >&6; } +if ${lt_cv_archive_cmds_need_lc_CXX+:} false; then : + $as_echo_n "(cached) " >&6 +else + $RM conftest* + echo "$lt_simple_compile_test_code" > conftest.$ac_ext + + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } 2>conftest.err; then + soname=conftest + lib=conftest + libobjs=conftest.$ac_objext + deplibs= + wl=$lt_prog_compiler_wl_CXX + pic_flag=$lt_prog_compiler_pic_CXX + compiler_flags=-v + linker_flags=-v + verstring= + output_objdir=. + libname=conftest + lt_save_allow_undefined_flag=$allow_undefined_flag_CXX + allow_undefined_flag_CXX= + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$archive_cmds_CXX 2\>\&1 \| $GREP \" -lc \" \>/dev/null 2\>\&1\""; } >&5 + (eval $archive_cmds_CXX 2\>\&1 \| $GREP \" -lc \" \>/dev/null 2\>\&1) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } + then + lt_cv_archive_cmds_need_lc_CXX=no + else + lt_cv_archive_cmds_need_lc_CXX=yes + fi + allow_undefined_flag_CXX=$lt_save_allow_undefined_flag + else + cat conftest.err 1>&5 + fi + $RM conftest* + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_archive_cmds_need_lc_CXX" >&5 +$as_echo "$lt_cv_archive_cmds_need_lc_CXX" >&6; } + archive_cmds_need_lc_CXX=$lt_cv_archive_cmds_need_lc_CXX + ;; + esac + fi + ;; +esac + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking dynamic linker characteristics" >&5 +$as_echo_n "checking dynamic linker characteristics... " >&6; } + +library_names_spec= +libname_spec='lib$name' +soname_spec= +shrext_cmds=".so" +postinstall_cmds= +postuninstall_cmds= +finish_cmds= +finish_eval= +shlibpath_var= +shlibpath_overrides_runpath=unknown +version_type=none +dynamic_linker="$host_os ld.so" +sys_lib_dlsearch_path_spec="/lib /usr/lib" +need_lib_prefix=unknown +hardcode_into_libs=no + +# when you set need_version to no, make sure it does not cause -set_version +# flags to be left without arguments +need_version=unknown + +case $host_os in +aix3*) + version_type=linux # correct to gnu/linux during the next big refactor + library_names_spec='${libname}${release}${shared_ext}$versuffix $libname.a' + shlibpath_var=LIBPATH + + # AIX 3 has no versioning support, so we append a major version to the name. + soname_spec='${libname}${release}${shared_ext}$major' + ;; + +aix[4-9]*) + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + hardcode_into_libs=yes + if test "$host_cpu" = ia64; then + # AIX 5 supports IA64 + library_names_spec='${libname}${release}${shared_ext}$major ${libname}${release}${shared_ext}$versuffix $libname${shared_ext}' + shlibpath_var=LD_LIBRARY_PATH + else + # With GCC up to 2.95.x, collect2 would create an import file + # for dependence libraries. The import file would start with + # the line `#! .'. This would cause the generated library to + # depend on `.', always an invalid library. This was fixed in + # development snapshots of GCC prior to 3.0. + case $host_os in + aix4 | aix4.[01] | aix4.[01].*) + if { echo '#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 97)' + echo ' yes ' + echo '#endif'; } | ${CC} -E - | $GREP yes > /dev/null; then + : + else + can_build_shared=no + fi + ;; + esac + # AIX (on Power*) has no versioning support, so currently we can not hardcode correct + # soname into executable. Probably we can add versioning support to + # collect2, so additional links can be useful in future. + if test "$aix_use_runtimelinking" = yes; then + # If using run time linking (on AIX 4.2 or later) use lib.so + # instead of lib.a to let people know that these are not + # typical AIX shared libraries. + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + else + # We preserve .a as extension for shared libraries through AIX4.2 + # and later when we are not doing run time linking. + library_names_spec='${libname}${release}.a $libname.a' + soname_spec='${libname}${release}${shared_ext}$major' + fi + shlibpath_var=LIBPATH + fi + ;; + +amigaos*) + case $host_cpu in + powerpc) + # Since July 2007 AmigaOS4 officially supports .so libraries. + # When compiling the executable, add -use-dynld -Lsobjs: to the compileline. + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + ;; + m68k) + library_names_spec='$libname.ixlibrary $libname.a' + # Create ${libname}_ixlibrary.a entries in /sys/libs. + finish_eval='for lib in `ls $libdir/*.ixlibrary 2>/dev/null`; do libname=`func_echo_all "$lib" | $SED '\''s%^.*/\([^/]*\)\.ixlibrary$%\1%'\''`; test $RM /sys/libs/${libname}_ixlibrary.a; $show "cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a"; cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a || exit 1; done' + ;; + esac + ;; + +beos*) + library_names_spec='${libname}${shared_ext}' + dynamic_linker="$host_os ld.so" + shlibpath_var=LIBRARY_PATH + ;; + +bsdi[45]*) + version_type=linux # correct to gnu/linux during the next big refactor + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + finish_cmds='PATH="\$PATH:/sbin" ldconfig $libdir' + shlibpath_var=LD_LIBRARY_PATH + sys_lib_search_path_spec="/shlib /usr/lib /usr/X11/lib /usr/contrib/lib /lib /usr/local/lib" + sys_lib_dlsearch_path_spec="/shlib /usr/lib /usr/local/lib" + # the default ld.so.conf also contains /usr/contrib/lib and + # /usr/X11R6/lib (/usr/X11 is a link to /usr/X11R6), but let us allow + # libtool to hard-code these into programs + ;; + +cygwin* | mingw* | pw32* | cegcc*) + version_type=windows + shrext_cmds=".dll" + need_version=no + need_lib_prefix=no + + case $GCC,$cc_basename in + yes,*) + # gcc + library_names_spec='$libname.dll.a' + # DLL is installed to $(libdir)/../bin by postinstall_cmds + postinstall_cmds='base_file=`basename \${file}`~ + dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\${base_file}'\''i; echo \$dlname'\''`~ + dldir=$destdir/`dirname \$dlpath`~ + test -d \$dldir || mkdir -p \$dldir~ + $install_prog $dir/$dlname \$dldir/$dlname~ + chmod a+x \$dldir/$dlname~ + if test -n '\''$stripme'\'' && test -n '\''$striplib'\''; then + eval '\''$striplib \$dldir/$dlname'\'' || exit \$?; + fi' + postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~ + dlpath=$dir/\$dldll~ + $RM \$dlpath' + shlibpath_overrides_runpath=yes + + case $host_os in + cygwin*) + # Cygwin DLLs use 'cyg' prefix rather than 'lib' + soname_spec='`echo ${libname} | sed -e 's/^lib/cyg/'``echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext}' + + ;; + mingw* | cegcc*) + # MinGW DLLs use traditional 'lib' prefix + soname_spec='${libname}`echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext}' + ;; + pw32*) + # pw32 DLLs use 'pw' prefix rather than 'lib' + library_names_spec='`echo ${libname} | sed -e 's/^lib/pw/'``echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext}' + ;; + esac + dynamic_linker='Win32 ld.exe' + ;; + + *,cl*) + # Native MSVC + libname_spec='$name' + soname_spec='${libname}`echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext}' + library_names_spec='${libname}.dll.lib' + + case $build_os in + mingw*) + sys_lib_search_path_spec= + lt_save_ifs=$IFS + IFS=';' + for lt_path in $LIB + do + IFS=$lt_save_ifs + # Let DOS variable expansion print the short 8.3 style file name. + lt_path=`cd "$lt_path" 2>/dev/null && cmd //C "for %i in (".") do @echo %~si"` + sys_lib_search_path_spec="$sys_lib_search_path_spec $lt_path" + done + IFS=$lt_save_ifs + # Convert to MSYS style. + sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | sed -e 's|\\\\|/|g' -e 's| \\([a-zA-Z]\\):| /\\1|g' -e 's|^ ||'` + ;; + cygwin*) + # Convert to unix form, then to dos form, then back to unix form + # but this time dos style (no spaces!) so that the unix form looks + # like /cygdrive/c/PROGRA~1:/cygdr... + sys_lib_search_path_spec=`cygpath --path --unix "$LIB"` + sys_lib_search_path_spec=`cygpath --path --dos "$sys_lib_search_path_spec" 2>/dev/null` + sys_lib_search_path_spec=`cygpath --path --unix "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"` + ;; + *) + sys_lib_search_path_spec="$LIB" + if $ECHO "$sys_lib_search_path_spec" | $GREP ';[c-zC-Z]:/' >/dev/null; then + # It is most probably a Windows format PATH. + sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e 's/;/ /g'` + else + sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"` + fi + # FIXME: find the short name or the path components, as spaces are + # common. (e.g. "Program Files" -> "PROGRA~1") + ;; + esac + + # DLL is installed to $(libdir)/../bin by postinstall_cmds + postinstall_cmds='base_file=`basename \${file}`~ + dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\${base_file}'\''i; echo \$dlname'\''`~ + dldir=$destdir/`dirname \$dlpath`~ + test -d \$dldir || mkdir -p \$dldir~ + $install_prog $dir/$dlname \$dldir/$dlname' + postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~ + dlpath=$dir/\$dldll~ + $RM \$dlpath' + shlibpath_overrides_runpath=yes + dynamic_linker='Win32 link.exe' + ;; + + *) + # Assume MSVC wrapper + library_names_spec='${libname}`echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext} $libname.lib' + dynamic_linker='Win32 ld.exe' + ;; + esac + # FIXME: first we should search . and the directory the executable is in + shlibpath_var=PATH + ;; + +darwin* | rhapsody*) + dynamic_linker="$host_os dyld" + version_type=darwin + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${major}$shared_ext ${libname}$shared_ext' + soname_spec='${libname}${release}${major}$shared_ext' + shlibpath_overrides_runpath=yes + shlibpath_var=DYLD_LIBRARY_PATH + shrext_cmds='`test .$module = .yes && echo .so || echo .dylib`' + + sys_lib_dlsearch_path_spec='/usr/local/lib /lib /usr/lib' + ;; + +dgux*) + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname$shared_ext' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + ;; + +freebsd* | dragonfly*) + # DragonFly does not have aout. When/if they implement a new + # versioning mechanism, adjust this. + if test -x /usr/bin/objformat; then + objformat=`/usr/bin/objformat` + else + case $host_os in + freebsd[23].*) objformat=aout ;; + *) objformat=elf ;; + esac + fi + version_type=freebsd-$objformat + case $version_type in + freebsd-elf*) + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext} $libname${shared_ext}' + need_version=no + need_lib_prefix=no + ;; + freebsd-*) + library_names_spec='${libname}${release}${shared_ext}$versuffix $libname${shared_ext}$versuffix' + need_version=yes + ;; + esac + shlibpath_var=LD_LIBRARY_PATH + case $host_os in + freebsd2.*) + shlibpath_overrides_runpath=yes + ;; + freebsd3.[01]* | freebsdelf3.[01]*) + shlibpath_overrides_runpath=yes + hardcode_into_libs=yes + ;; + freebsd3.[2-9]* | freebsdelf3.[2-9]* | \ + freebsd4.[0-5] | freebsdelf4.[0-5] | freebsd4.1.1 | freebsdelf4.1.1) + shlibpath_overrides_runpath=no + hardcode_into_libs=yes + ;; + *) # from 4.6 on, and DragonFly + shlibpath_overrides_runpath=yes + hardcode_into_libs=yes + ;; + esac + ;; + +haiku*) + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + dynamic_linker="$host_os runtime_loader" + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}${major} ${libname}${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LIBRARY_PATH + shlibpath_overrides_runpath=yes + sys_lib_dlsearch_path_spec='/boot/home/config/lib /boot/common/lib /boot/system/lib' + hardcode_into_libs=yes + ;; + +hpux9* | hpux10* | hpux11*) + # Give a soname corresponding to the major version so that dld.sl refuses to + # link against other versions. + version_type=sunos + need_lib_prefix=no + need_version=no + case $host_cpu in + ia64*) + shrext_cmds='.so' + hardcode_into_libs=yes + dynamic_linker="$host_os dld.so" + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes # Unless +noenvvar is specified. + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + if test "X$HPUX_IA64_MODE" = X32; then + sys_lib_search_path_spec="/usr/lib/hpux32 /usr/local/lib/hpux32 /usr/local/lib" + else + sys_lib_search_path_spec="/usr/lib/hpux64 /usr/local/lib/hpux64" + fi + sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec + ;; + hppa*64*) + shrext_cmds='.sl' + hardcode_into_libs=yes + dynamic_linker="$host_os dld.sl" + shlibpath_var=LD_LIBRARY_PATH # How should we handle SHLIB_PATH + shlibpath_overrides_runpath=yes # Unless +noenvvar is specified. + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + sys_lib_search_path_spec="/usr/lib/pa20_64 /usr/ccs/lib/pa20_64" + sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec + ;; + *) + shrext_cmds='.sl' + dynamic_linker="$host_os dld.sl" + shlibpath_var=SHLIB_PATH + shlibpath_overrides_runpath=no # +s is required to enable SHLIB_PATH + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + ;; + esac + # HP-UX runs *really* slowly unless shared libraries are mode 555, ... + postinstall_cmds='chmod 555 $lib' + # or fails outright, so override atomically: + install_override_mode=555 + ;; + +interix[3-9]*) + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + dynamic_linker='Interix 3.x ld.so.1 (PE, like ELF)' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=no + hardcode_into_libs=yes + ;; + +irix5* | irix6* | nonstopux*) + case $host_os in + nonstopux*) version_type=nonstopux ;; + *) + if test "$lt_cv_prog_gnu_ld" = yes; then + version_type=linux # correct to gnu/linux during the next big refactor + else + version_type=irix + fi ;; + esac + need_lib_prefix=no + need_version=no + soname_spec='${libname}${release}${shared_ext}$major' + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${release}${shared_ext} $libname${shared_ext}' + case $host_os in + irix5* | nonstopux*) + libsuff= shlibsuff= + ;; + *) + case $LD in # libtool.m4 will add one of these switches to LD + *-32|*"-32 "|*-melf32bsmip|*"-melf32bsmip ") + libsuff= shlibsuff= libmagic=32-bit;; + *-n32|*"-n32 "|*-melf32bmipn32|*"-melf32bmipn32 ") + libsuff=32 shlibsuff=N32 libmagic=N32;; + *-64|*"-64 "|*-melf64bmip|*"-melf64bmip ") + libsuff=64 shlibsuff=64 libmagic=64-bit;; + *) libsuff= shlibsuff= libmagic=never-match;; + esac + ;; + esac + shlibpath_var=LD_LIBRARY${shlibsuff}_PATH + shlibpath_overrides_runpath=no + sys_lib_search_path_spec="/usr/lib${libsuff} /lib${libsuff} /usr/local/lib${libsuff}" + sys_lib_dlsearch_path_spec="/usr/lib${libsuff} /lib${libsuff}" + hardcode_into_libs=yes + ;; + +# No shared lib support for Linux oldld, aout, or coff. +linux*oldld* | linux*aout* | linux*coff*) + dynamic_linker=no + ;; + +# This must be glibc/ELF. +linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + finish_cmds='PATH="\$PATH:/sbin" ldconfig -n $libdir' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=no + + # Some binutils ld are patched to set DT_RUNPATH + if ${lt_cv_shlibpath_overrides_runpath+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_shlibpath_overrides_runpath=no + save_LDFLAGS=$LDFLAGS + save_libdir=$libdir + eval "libdir=/foo; wl=\"$lt_prog_compiler_wl_CXX\"; \ + LDFLAGS=\"\$LDFLAGS $hardcode_libdir_flag_spec_CXX\"" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_link "$LINENO"; then : + if ($OBJDUMP -p conftest$ac_exeext) 2>/dev/null | grep "RUNPATH.*$libdir" >/dev/null; then : + lt_cv_shlibpath_overrides_runpath=yes +fi +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + LDFLAGS=$save_LDFLAGS + libdir=$save_libdir + +fi + + shlibpath_overrides_runpath=$lt_cv_shlibpath_overrides_runpath + + # This implies no fast_install, which is unacceptable. + # Some rework will be needed to allow for fast_install + # before this can be enabled. + hardcode_into_libs=yes + + # Append ld.so.conf contents to the search path + if test -f /etc/ld.so.conf; then + lt_ld_extra=`awk '/^include / { system(sprintf("cd /etc; cat %s 2>/dev/null", \$2)); skip = 1; } { if (!skip) print \$0; skip = 0; }' < /etc/ld.so.conf | $SED -e 's/#.*//;/^[ ]*hwcap[ ]/d;s/[:, ]/ /g;s/=[^=]*$//;s/=[^= ]* / /g;s/"//g;/^$/d' | tr '\n' ' '` + sys_lib_dlsearch_path_spec="/lib /usr/lib $lt_ld_extra" + fi + + # We used to test for /lib/ld.so.1 and disable shared libraries on + # powerpc, because MkLinux only supported shared libraries with the + # GNU dynamic linker. Since this was broken with cross compilers, + # most powerpc-linux boxes support dynamic linking these days and + # people can always --disable-shared, the test was removed, and we + # assume the GNU/Linux dynamic linker is in use. + dynamic_linker='GNU/Linux ld.so' + ;; + +netbsdelf*-gnu) + version_type=linux + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=no + hardcode_into_libs=yes + dynamic_linker='NetBSD ld.elf_so' + ;; + +netbsd*) + version_type=sunos + need_lib_prefix=no + need_version=no + if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix' + finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' + dynamic_linker='NetBSD (a.out) ld.so' + else + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + dynamic_linker='NetBSD ld.elf_so' + fi + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + hardcode_into_libs=yes + ;; + +newsos6) + version_type=linux # correct to gnu/linux during the next big refactor + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + ;; + +*nto* | *qnx*) + version_type=qnx + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=no + hardcode_into_libs=yes + dynamic_linker='ldqnx.so' + ;; + +openbsd*) + version_type=sunos + sys_lib_dlsearch_path_spec="/usr/lib" + need_lib_prefix=no + # Some older versions of OpenBSD (3.3 at least) *do* need versioned libs. + case $host_os in + openbsd3.3 | openbsd3.3.*) need_version=yes ;; + *) need_version=no ;; + esac + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix' + finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' + shlibpath_var=LD_LIBRARY_PATH + if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then + case $host_os in + openbsd2.[89] | openbsd2.[89].*) + shlibpath_overrides_runpath=no + ;; + *) + shlibpath_overrides_runpath=yes + ;; + esac + else + shlibpath_overrides_runpath=yes + fi + ;; + +os2*) + libname_spec='$name' + shrext_cmds=".dll" + need_lib_prefix=no + library_names_spec='$libname${shared_ext} $libname.a' + dynamic_linker='OS/2 ld.exe' + shlibpath_var=LIBPATH + ;; + +osf3* | osf4* | osf5*) + version_type=osf + need_lib_prefix=no + need_version=no + soname_spec='${libname}${release}${shared_ext}$major' + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + shlibpath_var=LD_LIBRARY_PATH + sys_lib_search_path_spec="/usr/shlib /usr/ccs/lib /usr/lib/cmplrs/cc /usr/lib /usr/local/lib /var/shlib" + sys_lib_dlsearch_path_spec="$sys_lib_search_path_spec" + ;; + +rdos*) + dynamic_linker=no + ;; + +solaris*) + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + hardcode_into_libs=yes + # ldd complains unless libraries are executable + postinstall_cmds='chmod +x $lib' + ;; + +sunos4*) + version_type=sunos + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix' + finish_cmds='PATH="\$PATH:/usr/etc" ldconfig $libdir' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + if test "$with_gnu_ld" = yes; then + need_lib_prefix=no + fi + need_version=yes + ;; + +sysv4 | sysv4.3*) + version_type=linux # correct to gnu/linux during the next big refactor + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + case $host_vendor in + sni) + shlibpath_overrides_runpath=no + need_lib_prefix=no + runpath_var=LD_RUN_PATH + ;; + siemens) + need_lib_prefix=no + ;; + motorola) + need_lib_prefix=no + need_version=no + shlibpath_overrides_runpath=no + sys_lib_search_path_spec='/lib /usr/lib /usr/ccs/lib' + ;; + esac + ;; + +sysv4*MP*) + if test -d /usr/nec ;then + version_type=linux # correct to gnu/linux during the next big refactor + library_names_spec='$libname${shared_ext}.$versuffix $libname${shared_ext}.$major $libname${shared_ext}' + soname_spec='$libname${shared_ext}.$major' + shlibpath_var=LD_LIBRARY_PATH + fi + ;; + +sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*) + version_type=freebsd-elf + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext} $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + hardcode_into_libs=yes + if test "$with_gnu_ld" = yes; then + sys_lib_search_path_spec='/usr/local/lib /usr/gnu/lib /usr/ccs/lib /usr/lib /lib' + else + sys_lib_search_path_spec='/usr/ccs/lib /usr/lib' + case $host_os in + sco3.2v5*) + sys_lib_search_path_spec="$sys_lib_search_path_spec /lib" + ;; + esac + fi + sys_lib_dlsearch_path_spec='/usr/lib' + ;; + +tpf*) + # TPF is a cross-target only. Preferred cross-host = GNU/Linux. + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=no + hardcode_into_libs=yes + ;; + +uts4*) + version_type=linux # correct to gnu/linux during the next big refactor + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + ;; + +*) + dynamic_linker=no + ;; +esac +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $dynamic_linker" >&5 +$as_echo "$dynamic_linker" >&6; } +test "$dynamic_linker" = no && can_build_shared=no + +variables_saved_for_relink="PATH $shlibpath_var $runpath_var" +if test "$GCC" = yes; then + variables_saved_for_relink="$variables_saved_for_relink GCC_EXEC_PREFIX COMPILER_PATH LIBRARY_PATH" +fi + +if test "${lt_cv_sys_lib_search_path_spec+set}" = set; then + sys_lib_search_path_spec="$lt_cv_sys_lib_search_path_spec" +fi +if test "${lt_cv_sys_lib_dlsearch_path_spec+set}" = set; then + sys_lib_dlsearch_path_spec="$lt_cv_sys_lib_dlsearch_path_spec" +fi + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to hardcode library paths into programs" >&5 +$as_echo_n "checking how to hardcode library paths into programs... " >&6; } +hardcode_action_CXX= +if test -n "$hardcode_libdir_flag_spec_CXX" || + test -n "$runpath_var_CXX" || + test "X$hardcode_automatic_CXX" = "Xyes" ; then + + # We can hardcode non-existent directories. + if test "$hardcode_direct_CXX" != no && + # If the only mechanism to avoid hardcoding is shlibpath_var, we + # have to relink, otherwise we might link with an installed library + # when we should be linking with a yet-to-be-installed one + ## test "$_LT_TAGVAR(hardcode_shlibpath_var, CXX)" != no && + test "$hardcode_minus_L_CXX" != no; then + # Linking always hardcodes the temporary library directory. + hardcode_action_CXX=relink + else + # We can link without hardcoding, and we can hardcode nonexisting dirs. + hardcode_action_CXX=immediate + fi +else + # We cannot hardcode anything, or else we can only hardcode existing + # directories. + hardcode_action_CXX=unsupported +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $hardcode_action_CXX" >&5 +$as_echo "$hardcode_action_CXX" >&6; } + +if test "$hardcode_action_CXX" = relink || + test "$inherit_rpath_CXX" = yes; then + # Fast installation is not supported + enable_fast_install=no +elif test "$shlibpath_overrides_runpath" = yes || + test "$enable_shared" = no; then + # Fast installation is not necessary + enable_fast_install=needless +fi + + + + + + + + fi # test -n "$compiler" + + CC=$lt_save_CC + CFLAGS=$lt_save_CFLAGS + LDCXX=$LD + LD=$lt_save_LD + GCC=$lt_save_GCC + with_gnu_ld=$lt_save_with_gnu_ld + lt_cv_path_LDCXX=$lt_cv_path_LD + lt_cv_path_LD=$lt_save_path_LD + lt_cv_prog_gnu_ldcxx=$lt_cv_prog_gnu_ld + lt_cv_prog_gnu_ld=$lt_save_with_gnu_ld +fi # test "$_lt_caught_CXX_error" != yes + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + +depcc="$CXX" am_compiler_list= + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking dependency style of $depcc" >&5 +$as_echo_n "checking dependency style of $depcc... " >&6; } +if ${am_cv_CXX_dependencies_compiler_type+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then + # We make a subdir and do the tests there. Otherwise we can end up + # making bogus files that we don't know about and never remove. For + # instance it was reported that on HP-UX the gcc test will end up + # making a dummy file named 'D' -- because '-MD' means "put the output + # in D". + rm -rf conftest.dir + mkdir conftest.dir + # Copy depcomp to subdir because otherwise we won't find it if we're + # using a relative directory. + cp "$am_depcomp" conftest.dir + cd conftest.dir + # We will build objects and dependencies in a subdirectory because + # it helps to detect inapplicable dependency modes. For instance + # both Tru64's cc and ICC support -MD to output dependencies as a + # side effect of compilation, but ICC will put the dependencies in + # the current directory while Tru64 will put them in the object + # directory. + mkdir sub + + am_cv_CXX_dependencies_compiler_type=none + if test "$am_compiler_list" = ""; then + am_compiler_list=`sed -n 's/^#*\([a-zA-Z0-9]*\))$/\1/p' < ./depcomp` + fi + am__universal=false + case " $depcc " in #( + *\ -arch\ *\ -arch\ *) am__universal=true ;; + esac + + for depmode in $am_compiler_list; do + # Setup a source with many dependencies, because some compilers + # like to wrap large dependency lists on column 80 (with \), and + # we should not choose a depcomp mode which is confused by this. + # + # We need to recreate these files for each test, as the compiler may + # overwrite some of them when testing with obscure command lines. + # This happens at least with the AIX C compiler. + : > sub/conftest.c + for i in 1 2 3 4 5 6; do + echo '#include "conftst'$i'.h"' >> sub/conftest.c + # Using ": > sub/conftst$i.h" creates only sub/conftst1.h with + # Solaris 10 /bin/sh. + echo '/* dummy */' > sub/conftst$i.h + done + echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf + + # We check with '-c' and '-o' for the sake of the "dashmstdout" + # mode. It turns out that the SunPro C++ compiler does not properly + # handle '-M -o', and we need to detect this. Also, some Intel + # versions had trouble with output in subdirs. + am__obj=sub/conftest.${OBJEXT-o} + am__minus_obj="-o $am__obj" + case $depmode in + gcc) + # This depmode causes a compiler race in universal mode. + test "$am__universal" = false || continue + ;; + nosideeffect) + # After this tag, mechanisms are not by side-effect, so they'll + # only be used when explicitly requested. + if test "x$enable_dependency_tracking" = xyes; then + continue + else + break + fi + ;; + msvc7 | msvc7msys | msvisualcpp | msvcmsys) + # This compiler won't grok '-c -o', but also, the minuso test has + # not run yet. These depmodes are late enough in the game, and + # so weak that their functioning should not be impacted. + am__obj=conftest.${OBJEXT-o} + am__minus_obj= + ;; + none) break ;; + esac + if depmode=$depmode \ + source=sub/conftest.c object=$am__obj \ + depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \ + $SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \ + >/dev/null 2>conftest.err && + grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 && + grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 && + grep $am__obj sub/conftest.Po > /dev/null 2>&1 && + ${MAKE-make} -s -f confmf > /dev/null 2>&1; then + # icc doesn't choke on unknown options, it will just issue warnings + # or remarks (even with -Werror). So we grep stderr for any message + # that says an option was ignored or not supported. + # When given -MP, icc 7.0 and 7.1 complain thusly: + # icc: Command line warning: ignoring option '-M'; no argument required + # The diagnosis changed in icc 8.0: + # icc: Command line remark: option '-MP' not supported + if (grep 'ignoring option' conftest.err || + grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else + am_cv_CXX_dependencies_compiler_type=$depmode + break + fi + fi + done + + cd .. + rm -rf conftest.dir +else + am_cv_CXX_dependencies_compiler_type=none +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_CXX_dependencies_compiler_type" >&5 +$as_echo "$am_cv_CXX_dependencies_compiler_type" >&6; } +CXXDEPMODE=depmode=$am_cv_CXX_dependencies_compiler_type + + if + test "x$enable_dependency_tracking" != xno \ + && test "$am_cv_CXX_dependencies_compiler_type" = gcc3; then + am__fastdepCXX_TRUE= + am__fastdepCXX_FALSE='#' +else + am__fastdepCXX_TRUE='#' + am__fastdepCXX_FALSE= +fi + + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to run the C preprocessor" >&5 +$as_echo_n "checking how to run the C preprocessor... " >&6; } +# On Suns, sometimes $CPP names a directory. +if test -n "$CPP" && test -d "$CPP"; then + CPP= +fi +if test -z "$CPP"; then + if ${ac_cv_prog_CPP+:} false; then : + $as_echo_n "(cached) " >&6 +else + # Double quotes because CPP needs to be expanded + for CPP in "$CC -E" "$CC -E -traditional-cpp" "/lib/cpp" + do + ac_preproc_ok=false +for ac_c_preproc_warn_flag in '' yes +do + # Use a header file that comes with gcc, so configuring glibc + # with a fresh cross-compiler works. + # Prefer to if __STDC__ is defined, since + # exists even on freestanding compilers. + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. "Syntax error" is here to catch this case. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#ifdef __STDC__ +# include +#else +# include +#endif + Syntax error +_ACEOF +if ac_fn_c_try_cpp "$LINENO"; then : + +else + # Broken: fails on valid input. +continue +fi +rm -f conftest.err conftest.i conftest.$ac_ext + + # OK, works on sane cases. Now check whether nonexistent headers + # can be detected and how. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +_ACEOF +if ac_fn_c_try_cpp "$LINENO"; then : + # Broken: success on invalid input. +continue +else + # Passes both tests. +ac_preproc_ok=: +break +fi +rm -f conftest.err conftest.i conftest.$ac_ext + +done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f conftest.i conftest.err conftest.$ac_ext +if $ac_preproc_ok; then : + break +fi + + done + ac_cv_prog_CPP=$CPP + +fi + CPP=$ac_cv_prog_CPP +else + ac_cv_prog_CPP=$CPP +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $CPP" >&5 +$as_echo "$CPP" >&6; } +ac_preproc_ok=false +for ac_c_preproc_warn_flag in '' yes +do + # Use a header file that comes with gcc, so configuring glibc + # with a fresh cross-compiler works. + # Prefer to if __STDC__ is defined, since + # exists even on freestanding compilers. + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. "Syntax error" is here to catch this case. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#ifdef __STDC__ +# include +#else +# include +#endif + Syntax error +_ACEOF +if ac_fn_c_try_cpp "$LINENO"; then : + +else + # Broken: fails on valid input. +continue +fi +rm -f conftest.err conftest.i conftest.$ac_ext + + # OK, works on sane cases. Now check whether nonexistent headers + # can be detected and how. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +_ACEOF +if ac_fn_c_try_cpp "$LINENO"; then : + # Broken: success on invalid input. +continue +else + # Passes both tests. +ac_preproc_ok=: +break +fi +rm -f conftest.err conftest.i conftest.$ac_ext + +done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f conftest.i conftest.err conftest.$ac_ext +if $ac_preproc_ok; then : + +else + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "C preprocessor \"$CPP\" fails sanity check +See \`config.log' for more details" "$LINENO" 5; } +fi + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ln -s works" >&5 +$as_echo_n "checking whether ln -s works... " >&6; } +LN_S=$as_ln_s +if test "$LN_S" = "ln -s"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no, using $LN_S" >&5 +$as_echo "no, using $LN_S" >&6; } +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ${MAKE-make} sets \$(MAKE)" >&5 +$as_echo_n "checking whether ${MAKE-make} sets \$(MAKE)... " >&6; } +set x ${MAKE-make} +ac_make=`$as_echo "$2" | sed 's/+/p/g; s/[^a-zA-Z0-9_]/_/g'` +if eval \${ac_cv_prog_make_${ac_make}_set+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat >conftest.make <<\_ACEOF +SHELL = /bin/sh +all: + @echo '@@@%%%=$(MAKE)=@@@%%%' +_ACEOF +# GNU make sometimes prints "make[1]: Entering ...", which would confuse us. +case `${MAKE-make} -f conftest.make 2>/dev/null` in + *@@@%%%=?*=@@@%%%*) + eval ac_cv_prog_make_${ac_make}_set=yes;; + *) + eval ac_cv_prog_make_${ac_make}_set=no;; +esac +rm -f conftest.make +fi +if eval test \$ac_cv_prog_make_${ac_make}_set = yes; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + SET_MAKE= +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + SET_MAKE="MAKE=${MAKE-make}" +fi + + + + + + + + + + +if test "x$ac_cv_env_PKG_CONFIG_set" != "xset"; then + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}pkg-config", so it can be a program name with args. +set dummy ${ac_tool_prefix}pkg-config; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_path_PKG_CONFIG+:} false; then : + $as_echo_n "(cached) " >&6 +else + case $PKG_CONFIG in + [\\/]* | ?:[\\/]*) + ac_cv_path_PKG_CONFIG="$PKG_CONFIG" # Let the user override the test with a path. + ;; + *) + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_path_PKG_CONFIG="$as_dir/$ac_word$ac_exec_ext" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + + ;; +esac +fi +PKG_CONFIG=$ac_cv_path_PKG_CONFIG +if test -n "$PKG_CONFIG"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PKG_CONFIG" >&5 +$as_echo "$PKG_CONFIG" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_path_PKG_CONFIG"; then + ac_pt_PKG_CONFIG=$PKG_CONFIG + # Extract the first word of "pkg-config", so it can be a program name with args. +set dummy pkg-config; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_path_ac_pt_PKG_CONFIG+:} false; then : + $as_echo_n "(cached) " >&6 +else + case $ac_pt_PKG_CONFIG in + [\\/]* | ?:[\\/]*) + ac_cv_path_ac_pt_PKG_CONFIG="$ac_pt_PKG_CONFIG" # Let the user override the test with a path. + ;; + *) + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_path_ac_pt_PKG_CONFIG="$as_dir/$ac_word$ac_exec_ext" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + + ;; +esac +fi +ac_pt_PKG_CONFIG=$ac_cv_path_ac_pt_PKG_CONFIG +if test -n "$ac_pt_PKG_CONFIG"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_pt_PKG_CONFIG" >&5 +$as_echo "$ac_pt_PKG_CONFIG" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_pt_PKG_CONFIG" = x; then + PKG_CONFIG="" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + PKG_CONFIG=$ac_pt_PKG_CONFIG + fi +else + PKG_CONFIG="$ac_cv_path_PKG_CONFIG" +fi + +fi +if test -n "$PKG_CONFIG"; then + _pkg_min_version=0.20 + { $as_echo "$as_me:${as_lineno-$LINENO}: checking pkg-config is at least version $_pkg_min_version" >&5 +$as_echo_n "checking pkg-config is at least version $_pkg_min_version... " >&6; } + if $PKG_CONFIG --atleast-pkgconfig-version $_pkg_min_version; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + PKG_CONFIG="" + fi +fi + +if test "x$request_python_bindings" != "xno"; then + + + + + + + if test -n "$PYTHON"; then + # If the user set $PYTHON, use it and don't search something else. + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $PYTHON version is >= 2.7" >&5 +$as_echo_n "checking whether $PYTHON version is >= 2.7... " >&6; } + prog="import sys +# split strings by '.' and convert to numeric. Append some zeros +# because we need at least 4 digits for the hex conversion. +# map returns an iterator in Python 3.0 and a list in 2.x +minver = list(map(int, '2.7'.split('.'))) + [0, 0, 0] +minverhex = 0 +# xrange is not present in Python 3.0 and range returns an iterator +for i in list(range(0, 4)): minverhex = (minverhex << 8) + minver[i] +sys.exit(sys.hexversion < minverhex)" + if { echo "$as_me:$LINENO: $PYTHON -c "$prog"" >&5 + ($PYTHON -c "$prog") >&5 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + as_fn_error $? "Python interpreter is too old" "$LINENO" 5 +fi + am_display_PYTHON=$PYTHON + else + # Otherwise, try each interpreter until we find one that satisfies + # VERSION. + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for a Python interpreter with version >= 2.7" >&5 +$as_echo_n "checking for a Python interpreter with version >= 2.7... " >&6; } +if ${am_cv_pathless_PYTHON+:} false; then : + $as_echo_n "(cached) " >&6 +else + + for am_cv_pathless_PYTHON in python python2 python3 python3.3 python3.2 python3.1 python3.0 python2.7 python2.6 python2.5 python2.4 python2.3 python2.2 python2.1 python2.0 none; do + test "$am_cv_pathless_PYTHON" = none && break + prog="import sys +# split strings by '.' and convert to numeric. Append some zeros +# because we need at least 4 digits for the hex conversion. +# map returns an iterator in Python 3.0 and a list in 2.x +minver = list(map(int, '2.7'.split('.'))) + [0, 0, 0] +minverhex = 0 +# xrange is not present in Python 3.0 and range returns an iterator +for i in list(range(0, 4)): minverhex = (minverhex << 8) + minver[i] +sys.exit(sys.hexversion < minverhex)" + if { echo "$as_me:$LINENO: $am_cv_pathless_PYTHON -c "$prog"" >&5 + ($am_cv_pathless_PYTHON -c "$prog") >&5 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; then : + break +fi + done +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_pathless_PYTHON" >&5 +$as_echo "$am_cv_pathless_PYTHON" >&6; } + # Set $PYTHON to the absolute path of $am_cv_pathless_PYTHON. + if test "$am_cv_pathless_PYTHON" = none; then + PYTHON=: + else + # Extract the first word of "$am_cv_pathless_PYTHON", so it can be a program name with args. +set dummy $am_cv_pathless_PYTHON; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_path_PYTHON+:} false; then : + $as_echo_n "(cached) " >&6 +else + case $PYTHON in + [\\/]* | ?:[\\/]*) + ac_cv_path_PYTHON="$PYTHON" # Let the user override the test with a path. + ;; + *) + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_path_PYTHON="$as_dir/$ac_word$ac_exec_ext" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + + ;; +esac +fi +PYTHON=$ac_cv_path_PYTHON +if test -n "$PYTHON"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PYTHON" >&5 +$as_echo "$PYTHON" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + fi + am_display_PYTHON=$am_cv_pathless_PYTHON + fi + + + if test "$PYTHON" = :; then + : + else + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $am_display_PYTHON version" >&5 +$as_echo_n "checking for $am_display_PYTHON version... " >&6; } +if ${am_cv_python_version+:} false; then : + $as_echo_n "(cached) " >&6 +else + am_cv_python_version=`$PYTHON -c "import sys; sys.stdout.write(sys.version[:3])"` +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_python_version" >&5 +$as_echo "$am_cv_python_version" >&6; } + PYTHON_VERSION=$am_cv_python_version + + + + PYTHON_PREFIX='${prefix}' + + PYTHON_EXEC_PREFIX='${exec_prefix}' + + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $am_display_PYTHON platform" >&5 +$as_echo_n "checking for $am_display_PYTHON platform... " >&6; } +if ${am_cv_python_platform+:} false; then : + $as_echo_n "(cached) " >&6 +else + am_cv_python_platform=`$PYTHON -c "import sys; sys.stdout.write(sys.platform)"` +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_python_platform" >&5 +$as_echo "$am_cv_python_platform" >&6; } + PYTHON_PLATFORM=$am_cv_python_platform + + + # Just factor out some code duplication. + am_python_setup_sysconfig="\ +import sys +# Prefer sysconfig over distutils.sysconfig, for better compatibility +# with python 3.x. See automake bug#10227. +try: + import sysconfig +except ImportError: + can_use_sysconfig = 0 +else: + can_use_sysconfig = 1 +# Can't use sysconfig in CPython 2.7, since it's broken in virtualenvs: +# +try: + from platform import python_implementation + if python_implementation() == 'CPython' and sys.version[:3] == '2.7': + can_use_sysconfig = 0 +except ImportError: + pass" + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $am_display_PYTHON script directory" >&5 +$as_echo_n "checking for $am_display_PYTHON script directory... " >&6; } +if ${am_cv_python_pythondir+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test "x$prefix" = xNONE + then + am_py_prefix=$ac_default_prefix + else + am_py_prefix=$prefix + fi + am_cv_python_pythondir=`$PYTHON -c " +$am_python_setup_sysconfig +if can_use_sysconfig: + sitedir = sysconfig.get_path('purelib', vars={'base':'$am_py_prefix'}) +else: + from distutils import sysconfig + sitedir = sysconfig.get_python_lib(0, 0, prefix='$am_py_prefix') +sys.stdout.write(sitedir)"` + case $am_cv_python_pythondir in + $am_py_prefix*) + am__strip_prefix=`echo "$am_py_prefix" | sed 's|.|.|g'` + am_cv_python_pythondir=`echo "$am_cv_python_pythondir" | sed "s,^$am__strip_prefix,$PYTHON_PREFIX,"` + ;; + *) + case $am_py_prefix in + /usr|/System*) ;; + *) + am_cv_python_pythondir=$PYTHON_PREFIX/lib/python$PYTHON_VERSION/site-packages + ;; + esac + ;; + esac + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_python_pythondir" >&5 +$as_echo "$am_cv_python_pythondir" >&6; } + pythondir=$am_cv_python_pythondir + + + + pkgpythondir=\${pythondir}/$PACKAGE + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $am_display_PYTHON extension module directory" >&5 +$as_echo_n "checking for $am_display_PYTHON extension module directory... " >&6; } +if ${am_cv_python_pyexecdir+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test "x$exec_prefix" = xNONE + then + am_py_exec_prefix=$am_py_prefix + else + am_py_exec_prefix=$exec_prefix + fi + am_cv_python_pyexecdir=`$PYTHON -c " +$am_python_setup_sysconfig +if can_use_sysconfig: + sitedir = sysconfig.get_path('platlib', vars={'platbase':'$am_py_prefix'}) +else: + from distutils import sysconfig + sitedir = sysconfig.get_python_lib(1, 0, prefix='$am_py_prefix') +sys.stdout.write(sitedir)"` + case $am_cv_python_pyexecdir in + $am_py_exec_prefix*) + am__strip_prefix=`echo "$am_py_exec_prefix" | sed 's|.|.|g'` + am_cv_python_pyexecdir=`echo "$am_cv_python_pyexecdir" | sed "s,^$am__strip_prefix,$PYTHON_EXEC_PREFIX,"` + ;; + *) + case $am_py_exec_prefix in + /usr|/System*) ;; + *) + am_cv_python_pyexecdir=$PYTHON_EXEC_PREFIX/lib/python$PYTHON_VERSION/site-packages + ;; + esac + ;; + esac + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_python_pyexecdir" >&5 +$as_echo "$am_cv_python_pyexecdir" >&6; } + pyexecdir=$am_cv_python_pyexecdir + + + + pkgpyexecdir=\${pyexecdir}/$PACKAGE + + + + fi + + + + # + # Allow the use of a (user set) custom python version + # + + + # Extract the first word of "python[$PYTHON_VERSION]", so it can be a program name with args. +set dummy python$PYTHON_VERSION; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_path_PYTHON+:} false; then : + $as_echo_n "(cached) " >&6 +else + case $PYTHON in + [\\/]* | ?:[\\/]*) + ac_cv_path_PYTHON="$PYTHON" # Let the user override the test with a path. + ;; + *) + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_path_PYTHON="$as_dir/$ac_word$ac_exec_ext" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + + ;; +esac +fi +PYTHON=$ac_cv_path_PYTHON +if test -n "$PYTHON"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PYTHON" >&5 +$as_echo "$PYTHON" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + if test -z "$PYTHON"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Cannot find python$PYTHON_VERSION in your system path" >&5 +$as_echo "$as_me: WARNING: Cannot find python$PYTHON_VERSION in your system path" >&2;} + PYTHON_VERSION="" + no_python_devel=yes + fi + +if test -z "$no_python_devel"; then : + + # + # Check for a version of Python >= 2.1.0 + # + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for a version of Python >= '2.1.0'" >&5 +$as_echo_n "checking for a version of Python >= '2.1.0'... " >&6; } + ac_supports_python_ver=`$PYTHON -c "import sys; \ + ver = sys.version.split ()[0]; \ + print (ver >= '2.1.0')"` + if test "$ac_supports_python_ver" != "True"; then + if test -z "$PYTHON_NOVERSIONCHECK"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: +This version of the AC_PYTHON_DEVEL macro +doesn't work properly with versions of Python before +2.1.0. You may need to re-run configure, setting the +variables PYTHON_CPPFLAGS, PYTHON_LDFLAGS, PYTHON_SITE_PKG, +PYTHON_EXTRA_LIBS and PYTHON_EXTRA_LDFLAGS by hand. +Moreover, to disable this check, set PYTHON_NOVERSIONCHECK +to something else than an empty string. +" >&5 +$as_echo "$as_me: WARNING: +This version of the AC_PYTHON_DEVEL macro +doesn't work properly with versions of Python before +2.1.0. You may need to re-run configure, setting the +variables PYTHON_CPPFLAGS, PYTHON_LDFLAGS, PYTHON_SITE_PKG, +PYTHON_EXTRA_LIBS and PYTHON_EXTRA_LDFLAGS by hand. +Moreover, to disable this check, set PYTHON_NOVERSIONCHECK +to something else than an empty string. +" >&2;} + no_python_devel=yes + else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: skip at user request" >&5 +$as_echo "skip at user request" >&6; } + fi + else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + fi + +fi # AS_IF + +if test -z "$no_python_devel"; then : + + # + # if the macro parameter ``version'' is set, honour it + # + if test -n ">= '2.7'"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for a version of Python >= '2.7'" >&5 +$as_echo_n "checking for a version of Python >= '2.7'... " >&6; } + ac_supports_python_ver=`$PYTHON -c "import sys; \ + ver = sys.version.split ()[0]; \ + print (ver >= '2.7')"` + if test "$ac_supports_python_ver" = "True"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: this package requires Python >= '2.7'. +If you have it installed, but it isn't the default Python +interpreter in your system path, please pass the PYTHON_VERSION +variable to configure. See \`\`configure --help'' for reference. +" >&5 +$as_echo "$as_me: WARNING: this package requires Python >= '2.7'. +If you have it installed, but it isn't the default Python +interpreter in your system path, please pass the PYTHON_VERSION +variable to configure. See \`\`configure --help'' for reference. +" >&2;} + PYTHON_VERSION="" + no_python_devel=yes + fi + fi + +fi # AS_IF + +if test -z "$no_python_devel"; then : + + # + # Check if you have distutils, else fail + # + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for the distutils Python package" >&5 +$as_echo_n "checking for the distutils Python package... " >&6; } + ac_distutils_result=`$PYTHON -c "import distutils" 2>&1` + if test -z "$ac_distutils_result"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cannot import Python module \"distutils\". +Please check your Python installation. The error was: +$ac_distutils_result" >&5 +$as_echo "$as_me: WARNING: cannot import Python module \"distutils\". +Please check your Python installation. The error was: +$ac_distutils_result" >&2;} + PYTHON_VERSION="" + no_python_devel=yes + fi + +fi # AS_IF + +if test -z "$no_python_devel"; then : + + # + # Check for Python include path + # + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for Python include path" >&5 +$as_echo_n "checking for Python include path... " >&6; } + if test -z "$PYTHON_CPPFLAGS"; then + python_path=`$PYTHON -c "import distutils.sysconfig; \ + print (distutils.sysconfig.get_python_inc ());"` + plat_python_path=`$PYTHON -c "import distutils.sysconfig; \ + print (distutils.sysconfig.get_python_inc (plat_specific=1));"` + if test -n "${python_path}"; then + if test "${plat_python_path}" != "${python_path}"; then + python_path="-I$python_path -I$plat_python_path" + else + python_path="-I$python_path" + fi + fi + PYTHON_CPPFLAGS=$python_path + fi + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PYTHON_CPPFLAGS" >&5 +$as_echo "$PYTHON_CPPFLAGS" >&6; } + + + # + # Check for Python library path + # + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for Python library path" >&5 +$as_echo_n "checking for Python library path... " >&6; } + if test -z "$PYTHON_LDFLAGS"; then + # (makes two attempts to ensure we've got a version number + # from the interpreter) + ac_python_version=`cat<>confdefs.h <<_ACEOF +#define HAVE_PYTHON "$ac_python_version" +_ACEOF + + + # First, the library directory: + ac_python_libdir=`cat<&5 +$as_echo "$as_me: WARNING: + Cannot determine location of your Python DSO. Please check it was installed with + dynamic libraries enabled, or try setting PYTHON_LDFLAGS by hand. + " >&2;} + no_python_devel=yes + fi + fi + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PYTHON_LDFLAGS" >&5 +$as_echo "$PYTHON_LDFLAGS" >&6; } + + +fi # AS_IF + +if test -z "$no_python_devel"; then : + + # + # Check for site packages + # + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for Python site-packages path" >&5 +$as_echo_n "checking for Python site-packages path... " >&6; } + if test -z "$PYTHON_SITE_PKG"; then + PYTHON_SITE_PKG=`$PYTHON -c "import distutils.sysconfig; \ + print (distutils.sysconfig.get_python_lib(0,0));"` + fi + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PYTHON_SITE_PKG" >&5 +$as_echo "$PYTHON_SITE_PKG" >&6; } + + + # + # libraries which must be linked in when embedding + # + { $as_echo "$as_me:${as_lineno-$LINENO}: checking python extra libraries" >&5 +$as_echo_n "checking python extra libraries... " >&6; } + if test -z "$PYTHON_EXTRA_LIBS"; then + PYTHON_EXTRA_LIBS=`$PYTHON -c "import distutils.sysconfig; \ + conf = distutils.sysconfig.get_config_var; \ + print (conf('LIBS'))"` + fi + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PYTHON_EXTRA_LIBS" >&5 +$as_echo "$PYTHON_EXTRA_LIBS" >&6; } + + + # + # linking flags needed when embedding + # + { $as_echo "$as_me:${as_lineno-$LINENO}: checking python extra linking flags" >&5 +$as_echo_n "checking python extra linking flags... " >&6; } + if test -z "$PYTHON_EXTRA_LDFLAGS"; then + PYTHON_EXTRA_LDFLAGS=`$PYTHON -c "import distutils.sysconfig; \ + conf = distutils.sysconfig.get_config_var; \ + print (conf('LINKFORSHARED'))"` + fi + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PYTHON_EXTRA_LDFLAGS" >&5 +$as_echo "$PYTHON_EXTRA_LDFLAGS" >&6; } + + + # + # final check to see if everything compiles alright + # + { $as_echo "$as_me:${as_lineno-$LINENO}: checking consistency of all components of python development environment" >&5 +$as_echo_n "checking consistency of all components of python development environment... " >&6; } + # save current global flags + ac_save_LIBS="$LIBS" + ac_save_CPPFLAGS="$CPPFLAGS" + LIBS="$ac_save_LIBS $PYTHON_LDFLAGS $PYTHON_EXTRA_LDFLAGS $PYTHON_EXTRA_LIBS" + CPPFLAGS="$ac_save_CPPFLAGS $PYTHON_CPPFLAGS" + ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + + #include +int +main () +{ +Py_Initialize(); + ; + return 0; +} + +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + pythonexists=yes +else + pythonexists=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + # turn back to default flags + CPPFLAGS="$ac_save_CPPFLAGS" + LIBS="$ac_save_LIBS" + + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $pythonexists" >&5 +$as_echo "$pythonexists" >&6; } + + if test ! "x$pythonexists" = "xyes"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: + Could not link test program to Python. Maybe the main Python library has been + installed in some non-standard library path. If so, pass it to configure, + via the LDFLAGS environment variable. + Example: ./configure LDFLAGS=\"-L/usr/non-standard-path/python/lib\" + ============================================================================ + ERROR! + You probably have to install the development version of the Python package + for your distribution. The exact name of this package varies among them. + ============================================================================ + " >&5 +$as_echo "$as_me: WARNING: + Could not link test program to Python. Maybe the main Python library has been + installed in some non-standard library path. If so, pass it to configure, + via the LDFLAGS environment variable. + Example: ./configure LDFLAGS=\"-L/usr/non-standard-path/python/lib\" + ============================================================================ + ERROR! + You probably have to install the development version of the Python package + for your distribution. The exact name of this package varies among them. + ============================================================================ + " >&2;} + PYTHON_VERSION="" + no_python_devel=yes + fi + + # + # all done! + # + +fi # AS_IF + +if test -z "$no_python_devel"; then : + have_python_dev=yes +else + have_python_dev=no +fi + + +fi + +if test "x${cython_path}" = "x"; then + for ac_prog in cython.py cython +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_CYTHON+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$CYTHON"; then + ac_cv_prog_CYTHON="$CYTHON" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CYTHON="$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +CYTHON=$ac_cv_prog_CYTHON +if test -n "$CYTHON"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CYTHON" >&5 +$as_echo "$CYTHON" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$CYTHON" && break +done + +else + CYTHON=${cython_path} + +fi + +# +# If we're running GCC or clang define _U_ to be "__attribute__((unused))" +# so we can use _U_ to flag unused function parameters and not get warnings +# about them. Otherwise, define _U_ to be an empty string so that _U_ used +# to flag an unused function parameters will compile with other compilers. +# +# XXX - similar hints for other compilers? +# +if test "x$GCC" = "xyes" -o "x$CC" = "xclang" ; then + +$as_echo "#define _U_ __attribute__((unused))" >>confdefs.h + +else + +$as_echo "#define _U_ /**/" >>confdefs.h + +fi + + ax_cxx_compile_cxx11_required=falsednl + ac_ext=cpp +ac_cpp='$CXXCPP $CPPFLAGS' +ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_cxx_compiler_gnu + ac_success=no + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CXX supports C++11 features by default" >&5 +$as_echo_n "checking whether $CXX supports C++11 features by default... " >&6; } +if ${ax_cv_cxx_compile_cxx11+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + + template + struct check + { + static_assert(sizeof(int) <= sizeof(T), "not big enough"); + }; + + typedef check> right_angle_brackets; + + int a; + decltype(a) b; + + typedef check check_type; + check_type c; + check_type&& cr = static_cast(c); + + auto d = a; + +_ACEOF +if ac_fn_cxx_try_compile "$LINENO"; then : + ax_cv_cxx_compile_cxx11=yes +else + ax_cv_cxx_compile_cxx11=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_cxx_compile_cxx11" >&5 +$as_echo "$ax_cv_cxx_compile_cxx11" >&6; } + if test x$ax_cv_cxx_compile_cxx11 = xyes; then + ac_success=yes + fi + + + + if test x$ac_success = xno; then + for switch in -std=c++11 -std=c++0x; do + cachevar=`$as_echo "ax_cv_cxx_compile_cxx11_$switch" | $as_tr_sh` + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CXX supports C++11 features with $switch" >&5 +$as_echo_n "checking whether $CXX supports C++11 features with $switch... " >&6; } +if eval \${$cachevar+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_save_CXXFLAGS="$CXXFLAGS" + CXXFLAGS="$CXXFLAGS $switch" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + + template + struct check + { + static_assert(sizeof(int) <= sizeof(T), "not big enough"); + }; + + typedef check> right_angle_brackets; + + int a; + decltype(a) b; + + typedef check check_type; + check_type c; + check_type&& cr = static_cast(c); + + auto d = a; + +_ACEOF +if ac_fn_cxx_try_compile "$LINENO"; then : + eval $cachevar=yes +else + eval $cachevar=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + CXXFLAGS="$ac_save_CXXFLAGS" +fi +eval ac_res=\$$cachevar + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } + if eval test x\$$cachevar = xyes; then + CXXFLAGS="$CXXFLAGS $switch" + ac_success=yes + break + fi + done + fi + ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + if test x$ax_cxx_compile_cxx11_required = xtrue; then + if test x$ac_success = xno; then + as_fn_error $? "*** A compiler with support for C++11 language features is required." "$LINENO" 5 + fi + else + if test x$ac_success = xno; then + HAVE_CXX11=0 + { $as_echo "$as_me:${as_lineno-$LINENO}: No compiler with C++11 support was found" >&5 +$as_echo "$as_me: No compiler with C++11 support was found" >&6;} + else + HAVE_CXX11=1 + +$as_echo "#define HAVE_CXX11 1" >>confdefs.h + + fi + + + fi + + +ac_ext=cpp +ac_cpp='$CXXCPP $CPPFLAGS' +ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_cxx_compiler_gnu + + +# Check that std::chrono::steady_clock is available. In particular, +# gcc 4.6 does not have one, but has monotonic_clock which is the old +# name existed in the pre-standard draft. If steady_clock is not +# available, don't define HAVE_STEADY_CLOCK and replace steady_clock +# with monotonic_clock. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether std::chrono::steady_clock is available" >&5 +$as_echo_n "checking whether std::chrono::steady_clock is available... " >&6; } +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#include + +int +main () +{ + +auto tp = std::chrono::steady_clock::now(); + + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_compile "$LINENO"; then : + +$as_echo "#define HAVE_STEADY_CLOCK 1" >>confdefs.h + + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + +# Check that std::future is available. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether std::future is available" >&5 +$as_echo_n "checking whether std::future is available... " >&6; } +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#include +#include + +int +main () +{ + +std::vector> v; + + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_compile "$LINENO"; then : + +$as_echo "#define HAVE_STD_FUTURE 1" >>confdefs.h + + have_std_future=yes + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } +else + have_std_future=no + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + +# Check that std::map::emplace is available for g++-4.7. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether std::map::emplace is available" >&5 +$as_echo_n "checking whether std::map::emplace is available... " >&6; } +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#include + +int +main () +{ + +std::map().emplace(1, 2); + + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_compile "$LINENO"; then : + +$as_echo "#define HAVE_STD_MAP_EMPLACE 1" >>confdefs.h + + have_std_map_emplace=yes + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } +else + have_std_map_emplace=no + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + +# Checks for libraries. + +# Additional libraries required for tests. +TESTLDADD= + +# Additional libraries required for programs under src directory. +APPLDFLAGS= + +LIBS_OLD=$LIBS +# Search for dlsym function, which is used in tests. Linux needs -ldl, +# but netbsd does not need it. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing dlsym" >&5 +$as_echo_n "checking for library containing dlsym... " >&6; } +if ${ac_cv_search_dlsym+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_func_search_save_LIBS=$LIBS +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char dlsym (); +int +main () +{ +return dlsym (); + ; + return 0; +} +_ACEOF +for ac_lib in '' dl; do + if test -z "$ac_lib"; then + ac_res="none required" + else + ac_res=-l$ac_lib + LIBS="-l$ac_lib $ac_func_search_save_LIBS" + fi + if ac_fn_c_try_link "$LINENO"; then : + ac_cv_search_dlsym=$ac_res +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext + if ${ac_cv_search_dlsym+:} false; then : + break +fi +done +if ${ac_cv_search_dlsym+:} false; then : + +else + ac_cv_search_dlsym=no +fi +rm conftest.$ac_ext +LIBS=$ac_func_search_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_dlsym" >&5 +$as_echo "$ac_cv_search_dlsym" >&6; } +ac_res=$ac_cv_search_dlsym +if test "$ac_res" != no; then : + test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" + +fi + +TESTLDADD="$LIBS $TESTLDADD" +LIBS=$LIBS_OLD + +LIBS_OLD=$LIBS +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing clock_gettime" >&5 +$as_echo_n "checking for library containing clock_gettime... " >&6; } +if ${ac_cv_search_clock_gettime+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_func_search_save_LIBS=$LIBS +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char clock_gettime (); +int +main () +{ +return clock_gettime (); + ; + return 0; +} +_ACEOF +for ac_lib in '' rt; do + if test -z "$ac_lib"; then + ac_res="none required" + else + ac_res=-l$ac_lib + LIBS="-l$ac_lib $ac_func_search_save_LIBS" + fi + if ac_fn_c_try_link "$LINENO"; then : + ac_cv_search_clock_gettime=$ac_res +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext + if ${ac_cv_search_clock_gettime+:} false; then : + break +fi +done +if ${ac_cv_search_clock_gettime+:} false; then : + +else + ac_cv_search_clock_gettime=no +fi +rm conftest.$ac_ext +LIBS=$ac_func_search_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_clock_gettime" >&5 +$as_echo "$ac_cv_search_clock_gettime" >&6; } +ac_res=$ac_cv_search_clock_gettime +if test "$ac_res" != no; then : + test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" + +$as_echo "#define HAVE_CLOCK_GETTIME 1" >>confdefs.h + +fi + +APPLDFLAGS="$LIBS $APPLDFLAGS" +LIBS=$LIBS_OLD + +case "$host" in + *android*) + android_build=yes + # android does not need -pthread, but needs followng 3 libs for C++ + APPLDFLAGS="$APPLDFLAGS -lstdc++ -latomic -lsupc++" + ;; + *) + PTHREAD_LDFLAGS="-pthread" + APPLDFLAGS="$APPLDFLAGS $PTHREAD_LDFLAGS" + ;; +esac + +# zlib +if test "x$android_build" = "xyes"; then + # Use zlib provided by NDK + APPLDFLAGS="-lz $APPLDFLAGS" + have_zlib=yes +else + +pkg_failed=no +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for ZLIB" >&5 +$as_echo_n "checking for ZLIB... " >&6; } + +if test -n "$ZLIB_CFLAGS"; then + pkg_cv_ZLIB_CFLAGS="$ZLIB_CFLAGS" + elif test -n "$PKG_CONFIG"; then + if test -n "$PKG_CONFIG" && \ + { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"zlib >= 1.2.3\""; } >&5 + ($PKG_CONFIG --exists --print-errors "zlib >= 1.2.3") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then + pkg_cv_ZLIB_CFLAGS=`$PKG_CONFIG --cflags "zlib >= 1.2.3" 2>/dev/null` + test "x$?" != "x0" && pkg_failed=yes +else + pkg_failed=yes +fi + else + pkg_failed=untried +fi +if test -n "$ZLIB_LIBS"; then + pkg_cv_ZLIB_LIBS="$ZLIB_LIBS" + elif test -n "$PKG_CONFIG"; then + if test -n "$PKG_CONFIG" && \ + { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"zlib >= 1.2.3\""; } >&5 + ($PKG_CONFIG --exists --print-errors "zlib >= 1.2.3") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then + pkg_cv_ZLIB_LIBS=`$PKG_CONFIG --libs "zlib >= 1.2.3" 2>/dev/null` + test "x$?" != "x0" && pkg_failed=yes +else + pkg_failed=yes +fi + else + pkg_failed=untried +fi + + + +if test $pkg_failed = yes; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + +if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then + _pkg_short_errors_supported=yes +else + _pkg_short_errors_supported=no +fi + if test $_pkg_short_errors_supported = yes; then + ZLIB_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "zlib >= 1.2.3" 2>&1` + else + ZLIB_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "zlib >= 1.2.3" 2>&1` + fi + # Put the nasty error message in config.log where it belongs + echo "$ZLIB_PKG_ERRORS" >&5 + + have_zlib=no +elif test $pkg_failed = untried; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + have_zlib=no +else + ZLIB_CFLAGS=$pkg_cv_ZLIB_CFLAGS + ZLIB_LIBS=$pkg_cv_ZLIB_LIBS + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + have_zlib=yes +fi + + if test "x${have_zlib}" = "xno"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: $ZLIB_PKG_ERRORS" >&5 +$as_echo "$as_me: $ZLIB_PKG_ERRORS" >&6;} + fi +fi + +# cunit + +pkg_failed=no +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for CUNIT" >&5 +$as_echo_n "checking for CUNIT... " >&6; } + +if test -n "$CUNIT_CFLAGS"; then + pkg_cv_CUNIT_CFLAGS="$CUNIT_CFLAGS" + elif test -n "$PKG_CONFIG"; then + if test -n "$PKG_CONFIG" && \ + { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"cunit >= 2.1\""; } >&5 + ($PKG_CONFIG --exists --print-errors "cunit >= 2.1") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then + pkg_cv_CUNIT_CFLAGS=`$PKG_CONFIG --cflags "cunit >= 2.1" 2>/dev/null` + test "x$?" != "x0" && pkg_failed=yes +else + pkg_failed=yes +fi + else + pkg_failed=untried +fi +if test -n "$CUNIT_LIBS"; then + pkg_cv_CUNIT_LIBS="$CUNIT_LIBS" + elif test -n "$PKG_CONFIG"; then + if test -n "$PKG_CONFIG" && \ + { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"cunit >= 2.1\""; } >&5 + ($PKG_CONFIG --exists --print-errors "cunit >= 2.1") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then + pkg_cv_CUNIT_LIBS=`$PKG_CONFIG --libs "cunit >= 2.1" 2>/dev/null` + test "x$?" != "x0" && pkg_failed=yes +else + pkg_failed=yes +fi + else + pkg_failed=untried +fi + + + +if test $pkg_failed = yes; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + +if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then + _pkg_short_errors_supported=yes +else + _pkg_short_errors_supported=no +fi + if test $_pkg_short_errors_supported = yes; then + CUNIT_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "cunit >= 2.1" 2>&1` + else + CUNIT_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "cunit >= 2.1" 2>&1` + fi + # Put the nasty error message in config.log where it belongs + echo "$CUNIT_PKG_ERRORS" >&5 + + have_cunit=no +elif test $pkg_failed = untried; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + have_cunit=no +else + CUNIT_CFLAGS=$pkg_cv_CUNIT_CFLAGS + CUNIT_LIBS=$pkg_cv_CUNIT_LIBS + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + have_cunit=yes +fi +# If pkg-config does not find cunit, check it using AC_CHECK_LIB. We +# do this because Debian (Ubuntu) lacks pkg-config file for cunit. +if test "x${have_cunit}" = "xno"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: ${CUNIT_PKG_ERRORS}" >&5 +$as_echo "$as_me: WARNING: ${CUNIT_PKG_ERRORS}" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for CU_initialize_registry in -lcunit" >&5 +$as_echo_n "checking for CU_initialize_registry in -lcunit... " >&6; } +if ${ac_cv_lib_cunit_CU_initialize_registry+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lcunit $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char CU_initialize_registry (); +int +main () +{ +return CU_initialize_registry (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_cunit_CU_initialize_registry=yes +else + ac_cv_lib_cunit_CU_initialize_registry=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_cunit_CU_initialize_registry" >&5 +$as_echo "$ac_cv_lib_cunit_CU_initialize_registry" >&6; } +if test "x$ac_cv_lib_cunit_CU_initialize_registry" = xyes; then : + have_cunit=yes +else + have_cunit=no +fi + + if test "x${have_cunit}" = "xyes"; then + CUNIT_LIBS="-lcunit" + CUNIT_CFLAGS="" + + + fi +fi +if test "x${have_cunit}" = "xyes"; then + # cunit in Mac OS X requires ncurses. Note that in Mac OS X, test + # program can be built without -lncurses, but it emits runtime + # error. + case "${build}" in + *-apple-darwin*) + CUNIT_LIBS="$CUNIT_LIBS -lncurses" + + ;; + esac +fi + + if test "x${have_cunit}" = "xyes" ; then + HAVE_CUNIT_TRUE= + HAVE_CUNIT_FALSE='#' +else + HAVE_CUNIT_TRUE='#' + HAVE_CUNIT_FALSE= +fi + + +# libev (for src) +# libev does not have pkg-config file. Check it in an old way. +LIBS_OLD=$LIBS +# android requires -lm for floor +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for ev_time in -lev" >&5 +$as_echo_n "checking for ev_time in -lev... " >&6; } +if ${ac_cv_lib_ev_ev_time+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lev -lm $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char ev_time (); +int +main () +{ +return ev_time (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_ev_ev_time=yes +else + ac_cv_lib_ev_ev_time=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_ev_ev_time" >&5 +$as_echo "$ac_cv_lib_ev_ev_time" >&6; } +if test "x$ac_cv_lib_ev_ev_time" = xyes; then : + have_libev=yes +else + have_libev=no +fi + +if test "x${have_libev}" = "xyes"; then + ac_fn_c_check_header_mongrel "$LINENO" "ev.h" "ac_cv_header_ev_h" "$ac_includes_default" +if test "x$ac_cv_header_ev_h" = xyes; then : + have_libev=yes +else + have_libev=no +fi + + + if test "x${have_libev}" = "xyes"; then + LIBEV_LIBS=-lev + LIBEV_CFLAGS= + + + fi +fi +LIBS=$LIBS_OLD + +# openssl (for src) + +pkg_failed=no +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for OPENSSL" >&5 +$as_echo_n "checking for OPENSSL... " >&6; } + +if test -n "$OPENSSL_CFLAGS"; then + pkg_cv_OPENSSL_CFLAGS="$OPENSSL_CFLAGS" + elif test -n "$PKG_CONFIG"; then + if test -n "$PKG_CONFIG" && \ + { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"openssl >= 1.0.1\""; } >&5 + ($PKG_CONFIG --exists --print-errors "openssl >= 1.0.1") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then + pkg_cv_OPENSSL_CFLAGS=`$PKG_CONFIG --cflags "openssl >= 1.0.1" 2>/dev/null` + test "x$?" != "x0" && pkg_failed=yes +else + pkg_failed=yes +fi + else + pkg_failed=untried +fi +if test -n "$OPENSSL_LIBS"; then + pkg_cv_OPENSSL_LIBS="$OPENSSL_LIBS" + elif test -n "$PKG_CONFIG"; then + if test -n "$PKG_CONFIG" && \ + { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"openssl >= 1.0.1\""; } >&5 + ($PKG_CONFIG --exists --print-errors "openssl >= 1.0.1") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then + pkg_cv_OPENSSL_LIBS=`$PKG_CONFIG --libs "openssl >= 1.0.1" 2>/dev/null` + test "x$?" != "x0" && pkg_failed=yes +else + pkg_failed=yes +fi + else + pkg_failed=untried +fi + + + +if test $pkg_failed = yes; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + +if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then + _pkg_short_errors_supported=yes +else + _pkg_short_errors_supported=no +fi + if test $_pkg_short_errors_supported = yes; then + OPENSSL_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "openssl >= 1.0.1" 2>&1` + else + OPENSSL_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "openssl >= 1.0.1" 2>&1` + fi + # Put the nasty error message in config.log where it belongs + echo "$OPENSSL_PKG_ERRORS" >&5 + + have_openssl=no +elif test $pkg_failed = untried; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + have_openssl=no +else + OPENSSL_CFLAGS=$pkg_cv_OPENSSL_CFLAGS + OPENSSL_LIBS=$pkg_cv_OPENSSL_LIBS + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + have_openssl=yes +fi +if test "x${have_openssl}" = "xno"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: $OPENSSL_PKG_ERRORS" >&5 +$as_echo "$as_me: $OPENSSL_PKG_ERRORS" >&6;} +fi + +# libevent_openssl (for examples) +# 2.0.8 is required because we use evconnlistener_set_error_cb() + +pkg_failed=no +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for LIBEVENT_OPENSSL" >&5 +$as_echo_n "checking for LIBEVENT_OPENSSL... " >&6; } + +if test -n "$LIBEVENT_OPENSSL_CFLAGS"; then + pkg_cv_LIBEVENT_OPENSSL_CFLAGS="$LIBEVENT_OPENSSL_CFLAGS" + elif test -n "$PKG_CONFIG"; then + if test -n "$PKG_CONFIG" && \ + { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libevent_openssl >= 2.0.8\""; } >&5 + ($PKG_CONFIG --exists --print-errors "libevent_openssl >= 2.0.8") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then + pkg_cv_LIBEVENT_OPENSSL_CFLAGS=`$PKG_CONFIG --cflags "libevent_openssl >= 2.0.8" 2>/dev/null` + test "x$?" != "x0" && pkg_failed=yes +else + pkg_failed=yes +fi + else + pkg_failed=untried +fi +if test -n "$LIBEVENT_OPENSSL_LIBS"; then + pkg_cv_LIBEVENT_OPENSSL_LIBS="$LIBEVENT_OPENSSL_LIBS" + elif test -n "$PKG_CONFIG"; then + if test -n "$PKG_CONFIG" && \ + { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libevent_openssl >= 2.0.8\""; } >&5 + ($PKG_CONFIG --exists --print-errors "libevent_openssl >= 2.0.8") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then + pkg_cv_LIBEVENT_OPENSSL_LIBS=`$PKG_CONFIG --libs "libevent_openssl >= 2.0.8" 2>/dev/null` + test "x$?" != "x0" && pkg_failed=yes +else + pkg_failed=yes +fi + else + pkg_failed=untried +fi + + + +if test $pkg_failed = yes; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + +if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then + _pkg_short_errors_supported=yes +else + _pkg_short_errors_supported=no +fi + if test $_pkg_short_errors_supported = yes; then + LIBEVENT_OPENSSL_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "libevent_openssl >= 2.0.8" 2>&1` + else + LIBEVENT_OPENSSL_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "libevent_openssl >= 2.0.8" 2>&1` + fi + # Put the nasty error message in config.log where it belongs + echo "$LIBEVENT_OPENSSL_PKG_ERRORS" >&5 + + have_libevent_openssl=no +elif test $pkg_failed = untried; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + have_libevent_openssl=no +else + LIBEVENT_OPENSSL_CFLAGS=$pkg_cv_LIBEVENT_OPENSSL_CFLAGS + LIBEVENT_OPENSSL_LIBS=$pkg_cv_LIBEVENT_OPENSSL_LIBS + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + have_libevent_openssl=yes +fi +if test "x${have_libevent_openssl}" = "xno"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: $LIBEVENT_OPENSSL_PKG_ERRORS" >&5 +$as_echo "$as_me: $LIBEVENT_OPENSSL_PKG_ERRORS" >&6;} +fi + +# jansson (for src/nghttp, src/deflatehd and src/inflatehd) + +pkg_failed=no +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for JANSSON" >&5 +$as_echo_n "checking for JANSSON... " >&6; } + +if test -n "$JANSSON_CFLAGS"; then + pkg_cv_JANSSON_CFLAGS="$JANSSON_CFLAGS" + elif test -n "$PKG_CONFIG"; then + if test -n "$PKG_CONFIG" && \ + { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"jansson >= 2.5\""; } >&5 + ($PKG_CONFIG --exists --print-errors "jansson >= 2.5") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then + pkg_cv_JANSSON_CFLAGS=`$PKG_CONFIG --cflags "jansson >= 2.5" 2>/dev/null` + test "x$?" != "x0" && pkg_failed=yes +else + pkg_failed=yes +fi + else + pkg_failed=untried +fi +if test -n "$JANSSON_LIBS"; then + pkg_cv_JANSSON_LIBS="$JANSSON_LIBS" + elif test -n "$PKG_CONFIG"; then + if test -n "$PKG_CONFIG" && \ + { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"jansson >= 2.5\""; } >&5 + ($PKG_CONFIG --exists --print-errors "jansson >= 2.5") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then + pkg_cv_JANSSON_LIBS=`$PKG_CONFIG --libs "jansson >= 2.5" 2>/dev/null` + test "x$?" != "x0" && pkg_failed=yes +else + pkg_failed=yes +fi + else + pkg_failed=untried +fi + + + +if test $pkg_failed = yes; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + +if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then + _pkg_short_errors_supported=yes +else + _pkg_short_errors_supported=no +fi + if test $_pkg_short_errors_supported = yes; then + JANSSON_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "jansson >= 2.5" 2>&1` + else + JANSSON_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "jansson >= 2.5" 2>&1` + fi + # Put the nasty error message in config.log where it belongs + echo "$JANSSON_PKG_ERRORS" >&5 + + have_jansson=no +elif test $pkg_failed = untried; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + have_jansson=no +else + JANSSON_CFLAGS=$pkg_cv_JANSSON_CFLAGS + JANSSON_LIBS=$pkg_cv_JANSSON_LIBS + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + have_jansson=yes +fi +if test "x${have_jansson}" == "xyes"; then + +$as_echo "#define HAVE_JANSSON 1" >>confdefs.h + +else + { $as_echo "$as_me:${as_lineno-$LINENO}: $JANSSON_PKG_ERRORS" >&5 +$as_echo "$as_me: $JANSSON_PKG_ERRORS" >&6;} +fi + +# libxml2 (for src/nghttp) +have_libxml2=no +if test "x${request_libxml2}" != "xno"; then + + +# Check whether --with-xml-prefix was given. +if test "${with_xml_prefix+set}" = set; then : + withval=$with_xml_prefix; xml_config_prefix="$withval" +else + xml_config_prefix="" +fi + + +# Check whether --with-xml-exec-prefix was given. +if test "${with_xml_exec_prefix+set}" = set; then : + withval=$with_xml_exec_prefix; xml_config_exec_prefix="$withval" +else + xml_config_exec_prefix="" +fi + +# Check whether --enable-xmltest was given. +if test "${enable_xmltest+set}" = set; then : + enableval=$enable_xmltest; +else + enable_xmltest=yes +fi + + + if test x$xml_config_exec_prefix != x ; then + xml_config_args="$xml_config_args" + if test x${XML2_CONFIG+set} != xset ; then + XML2_CONFIG=$xml_config_exec_prefix/bin/xml2-config + fi + fi + if test x$xml_config_prefix != x ; then + xml_config_args="$xml_config_args --prefix=$xml_config_prefix" + if test x${XML2_CONFIG+set} != xset ; then + XML2_CONFIG=$xml_config_prefix/bin/xml2-config + fi + fi + + # Extract the first word of "xml2-config", so it can be a program name with args. +set dummy xml2-config; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_path_XML2_CONFIG+:} false; then : + $as_echo_n "(cached) " >&6 +else + case $XML2_CONFIG in + [\\/]* | ?:[\\/]*) + ac_cv_path_XML2_CONFIG="$XML2_CONFIG" # Let the user override the test with a path. + ;; + *) + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_path_XML2_CONFIG="$as_dir/$ac_word$ac_exec_ext" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + + test -z "$ac_cv_path_XML2_CONFIG" && ac_cv_path_XML2_CONFIG="no" + ;; +esac +fi +XML2_CONFIG=$ac_cv_path_XML2_CONFIG +if test -n "$XML2_CONFIG"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $XML2_CONFIG" >&5 +$as_echo "$XML2_CONFIG" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + min_xml_version=2.7.7 + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for libxml - version >= $min_xml_version" >&5 +$as_echo_n "checking for libxml - version >= $min_xml_version... " >&6; } + no_xml="" + if test "$XML2_CONFIG" = "no" ; then + no_xml=yes + else + XML_CPPFLAGS=`$XML2_CONFIG $xml_config_args --cflags` + XML_LIBS=`$XML2_CONFIG $xml_config_args --libs` + xml_config_major_version=`$XML2_CONFIG $xml_config_args --version | \ + sed 's/\([0-9]*\).\([0-9]*\).\([0-9]*\)/\1/'` + xml_config_minor_version=`$XML2_CONFIG $xml_config_args --version | \ + sed 's/\([0-9]*\).\([0-9]*\).\([0-9]*\)/\2/'` + xml_config_micro_version=`$XML2_CONFIG $xml_config_args --version | \ + sed 's/\([0-9]*\).\([0-9]*\).\([0-9]*\)/\3/'` + if test "x$enable_xmltest" = "xyes" ; then + ac_save_CPPFLAGS="$CPPFLAGS" + ac_save_LIBS="$LIBS" + CPPFLAGS="$CPPFLAGS $XML_CPPFLAGS" + LIBS="$XML_LIBS $LIBS" + rm -f conf.xmltest + if test "$cross_compiling" = yes; then : + echo $ac_n "cross compiling; assumed OK... $ac_c" +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#include +#include +#include +#include + +int +main() +{ + int xml_major_version, xml_minor_version, xml_micro_version; + int major, minor, micro; + char *tmp_version; + + system("touch conf.xmltest"); + + /* Capture xml2-config output via autoconf/configure variables */ + /* HP/UX 9 (%@#!) writes to sscanf strings */ + tmp_version = (char *)strdup("$min_xml_version"); + if (sscanf(tmp_version, "%d.%d.%d", &major, &minor, µ) != 3) { + printf("%s, bad version string from xml2-config\n", "$min_xml_version"); + exit(1); + } + free(tmp_version); + + /* Capture the version information from the header files */ + tmp_version = (char *)strdup(LIBXML_DOTTED_VERSION); + if (sscanf(tmp_version, "%d.%d.%d", &xml_major_version, &xml_minor_version, &xml_micro_version) != 3) { + printf("%s, bad version string from libxml includes\n", "LIBXML_DOTTED_VERSION"); + exit(1); + } + free(tmp_version); + + /* Compare xml2-config output to the libxml headers */ + if ((xml_major_version != $xml_config_major_version) || + (xml_minor_version != $xml_config_minor_version) || + (xml_micro_version != $xml_config_micro_version)) + { + printf("*** libxml header files (version %d.%d.%d) do not match\n", + xml_major_version, xml_minor_version, xml_micro_version); + printf("*** xml2-config (version %d.%d.%d)\n", + $xml_config_major_version, $xml_config_minor_version, $xml_config_micro_version); + return 1; + } +/* Compare the headers to the library to make sure we match */ + /* Less than ideal -- doesn't provide us with return value feedback, + * only exits if there's a serious mismatch between header and library. + */ + LIBXML_TEST_VERSION; + + /* Test that the library is greater than our minimum version */ + if ((xml_major_version > major) || + ((xml_major_version == major) && (xml_minor_version > minor)) || + ((xml_major_version == major) && (xml_minor_version == minor) && + (xml_micro_version >= micro))) + { + return 0; + } + else + { + printf("\n*** An old version of libxml (%d.%d.%d) was found.\n", + xml_major_version, xml_minor_version, xml_micro_version); + printf("*** You need a version of libxml newer than %d.%d.%d. The latest version of\n", + major, minor, micro); + printf("*** libxml is always available from ftp://ftp.xmlsoft.org.\n"); + printf("***\n"); + printf("*** If you have already installed a sufficiently new version, this error\n"); + printf("*** probably means that the wrong copy of the xml2-config shell script is\n"); + printf("*** being found. The easiest way to fix this is to remove the old version\n"); + printf("*** of LIBXML, but you can also set the XML2_CONFIG environment to point to the\n"); + printf("*** correct copy of xml2-config. (In this case, you will have to\n"); + printf("*** modify your LD_LIBRARY_PATH enviroment variable, or edit /etc/ld.so.conf\n"); + printf("*** so that the correct libraries are found at run-time))\n"); + } + return 1; +} + +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + +else + no_xml=yes +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +fi + + CPPFLAGS="$ac_save_CPPFLAGS" + LIBS="$ac_save_LIBS" + fi + fi + + if test "x$no_xml" = x ; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes (version $xml_config_major_version.$xml_config_minor_version.$xml_config_micro_version)" >&5 +$as_echo "yes (version $xml_config_major_version.$xml_config_minor_version.$xml_config_micro_version)" >&6; } + have_libxml2=yes + else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + if test "$XML2_CONFIG" = "no" ; then + echo "*** The xml2-config script installed by LIBXML could not be found" + echo "*** If libxml was installed in PREFIX, make sure PREFIX/bin is in" + echo "*** your path, or set the XML2_CONFIG environment variable to the" + echo "*** full path to xml2-config." + else + if test -f conf.xmltest ; then + : + else + echo "*** Could not run libxml test program, checking why..." + CPPFLAGS="$CPPFLAGS $XML_CPPFLAGS" + LIBS="$LIBS $XML_LIBS" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#include +#include + +int +main () +{ + LIBXML_TEST_VERSION; return 0; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + echo "*** The test program compiled, but did not run. This usually means" + echo "*** that the run-time linker is not finding LIBXML or finding the wrong" + echo "*** version of LIBXML. If it is not finding LIBXML, you'll need to set your" + echo "*** LD_LIBRARY_PATH environment variable, or edit /etc/ld.so.conf to point" + echo "*** to the installed location Also, make sure you have run ldconfig if that" + echo "*** is required on your system" + echo "***" + echo "*** If you have an old version installed, it is best to remove it, although" + echo "*** you may also be able to get things to work by modifying LD_LIBRARY_PATH" +else + echo "*** The test program failed to compile or link. See the file config.log for the" + echo "*** exact error that occured. This usually means LIBXML was incorrectly installed" + echo "*** or that you have moved LIBXML since it was installed. In the latter case, you" + echo "*** may want to edit the xml2-config script: $XML2_CONFIG" +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + CPPFLAGS="$ac_save_CPPFLAGS" + LIBS="$ac_save_LIBS" + fi + fi + + XML_CPPFLAGS="" + XML_LIBS="" + have_libxml2=no + fi + + + rm -f conf.xmltest + + if test "x${have_libxml2}" = "xyes"; then + +$as_echo "#define HAVE_LIBXML2 1" >>confdefs.h + + fi +fi + +if test "x${request_libxml2}" = "xyes" && + test "x${have_libxml2}" != "xyes"; then + as_fn_error $? "libxml2 was requested (--with-libxml2) but not found" "$LINENO" 5 +fi + + if test "x${have_libxml2}" = "xyes" ; then + HAVE_LIBXML2_TRUE= + HAVE_LIBXML2_FALSE='#' +else + HAVE_LIBXML2_TRUE='#' + HAVE_LIBXML2_FALSE= +fi + + +# jemalloc +have_jemalloc=no +if test "x${request_jemalloc}" != "xno"; then + LIBS_OLD=$LIBS + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing malloc_stats_print" >&5 +$as_echo_n "checking for library containing malloc_stats_print... " >&6; } +if ${ac_cv_search_malloc_stats_print+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_func_search_save_LIBS=$LIBS +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char malloc_stats_print (); +int +main () +{ +return malloc_stats_print (); + ; + return 0; +} +_ACEOF +for ac_lib in '' jemalloc; do + if test -z "$ac_lib"; then + ac_res="none required" + else + ac_res=-l$ac_lib + LIBS="-l$ac_lib $PTHREAD_LDFLAGS $ac_func_search_save_LIBS" + fi + if ac_fn_c_try_link "$LINENO"; then : + ac_cv_search_malloc_stats_print=$ac_res +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext + if ${ac_cv_search_malloc_stats_print+:} false; then : + break +fi +done +if ${ac_cv_search_malloc_stats_print+:} false; then : + +else + ac_cv_search_malloc_stats_print=no +fi +rm conftest.$ac_ext +LIBS=$ac_func_search_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_malloc_stats_print" >&5 +$as_echo "$ac_cv_search_malloc_stats_print" >&6; } +ac_res=$ac_cv_search_malloc_stats_print +if test "$ac_res" != no; then : + test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" + have_jemalloc=yes +fi + + LIBS=$LIBS_OLD + + if test "x${have_jemalloc}" = "xyes"; then + jemalloc_libs=${ac_cv_search_malloc_stats_print} + else + # On Darwin, malloc_stats_print is je_malloc_stats_print + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing je_malloc_stats_print" >&5 +$as_echo_n "checking for library containing je_malloc_stats_print... " >&6; } +if ${ac_cv_search_je_malloc_stats_print+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_func_search_save_LIBS=$LIBS +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char je_malloc_stats_print (); +int +main () +{ +return je_malloc_stats_print (); + ; + return 0; +} +_ACEOF +for ac_lib in '' jemalloc; do + if test -z "$ac_lib"; then + ac_res="none required" + else + ac_res=-l$ac_lib + LIBS="-l$ac_lib $PTHREAD_LDFLAGS $ac_func_search_save_LIBS" + fi + if ac_fn_c_try_link "$LINENO"; then : + ac_cv_search_je_malloc_stats_print=$ac_res +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext + if ${ac_cv_search_je_malloc_stats_print+:} false; then : + break +fi +done +if ${ac_cv_search_je_malloc_stats_print+:} false; then : + +else + ac_cv_search_je_malloc_stats_print=no +fi +rm conftest.$ac_ext +LIBS=$ac_func_search_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_je_malloc_stats_print" >&5 +$as_echo "$ac_cv_search_je_malloc_stats_print" >&6; } +ac_res=$ac_cv_search_je_malloc_stats_print +if test "$ac_res" != no; then : + test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" + have_jemalloc=yes +fi + + LIBS=$LIBS_OLD + + if test "x${have_jemalloc}" = "xyes"; then + jemalloc_libs=${ac_cv_search_je_malloc_stats_print} + fi + fi + + if test "x${have_jemalloc}" = "xyes" && + test "x${jemalloc_libs}" != "xnone required"; then + JEMALLOC_LIBS=${jemalloc_libs} + + fi +fi + +if test "x${request_jemalloc}" = "xyes" && + test "x${have_jemalloc}" != "xyes"; then + as_fn_error $? "jemalloc was requested (--with-jemalloc) but not found" "$LINENO" 5 +fi + +# spdylay (for src/nghttpx and src/h2load) +have_spdylay=no +if test "x${request_spdylay}" != "xno"; then + +pkg_failed=no +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for LIBSPDYLAY" >&5 +$as_echo_n "checking for LIBSPDYLAY... " >&6; } + +if test -n "$LIBSPDYLAY_CFLAGS"; then + pkg_cv_LIBSPDYLAY_CFLAGS="$LIBSPDYLAY_CFLAGS" + elif test -n "$PKG_CONFIG"; then + if test -n "$PKG_CONFIG" && \ + { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libspdylay >= 1.3.2\""; } >&5 + ($PKG_CONFIG --exists --print-errors "libspdylay >= 1.3.2") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then + pkg_cv_LIBSPDYLAY_CFLAGS=`$PKG_CONFIG --cflags "libspdylay >= 1.3.2" 2>/dev/null` + test "x$?" != "x0" && pkg_failed=yes +else + pkg_failed=yes +fi + else + pkg_failed=untried +fi +if test -n "$LIBSPDYLAY_LIBS"; then + pkg_cv_LIBSPDYLAY_LIBS="$LIBSPDYLAY_LIBS" + elif test -n "$PKG_CONFIG"; then + if test -n "$PKG_CONFIG" && \ + { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libspdylay >= 1.3.2\""; } >&5 + ($PKG_CONFIG --exists --print-errors "libspdylay >= 1.3.2") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then + pkg_cv_LIBSPDYLAY_LIBS=`$PKG_CONFIG --libs "libspdylay >= 1.3.2" 2>/dev/null` + test "x$?" != "x0" && pkg_failed=yes +else + pkg_failed=yes +fi + else + pkg_failed=untried +fi + + + +if test $pkg_failed = yes; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + +if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then + _pkg_short_errors_supported=yes +else + _pkg_short_errors_supported=no +fi + if test $_pkg_short_errors_supported = yes; then + LIBSPDYLAY_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "libspdylay >= 1.3.2" 2>&1` + else + LIBSPDYLAY_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "libspdylay >= 1.3.2" 2>&1` + fi + # Put the nasty error message in config.log where it belongs + echo "$LIBSPDYLAY_PKG_ERRORS" >&5 + + have_spdylay=no +elif test $pkg_failed = untried; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + have_spdylay=no +else + LIBSPDYLAY_CFLAGS=$pkg_cv_LIBSPDYLAY_CFLAGS + LIBSPDYLAY_LIBS=$pkg_cv_LIBSPDYLAY_LIBS + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + have_spdylay=yes +fi + if test "x${have_spdylay}" = "xyes"; then + +$as_echo "#define HAVE_SPDYLAY 1" >>confdefs.h + + else + { $as_echo "$as_me:${as_lineno-$LINENO}: $LIBSPDYLAY_PKG_ERRORS" >&5 +$as_echo "$as_me: $LIBSPDYLAY_PKG_ERRORS" >&6;} + { $as_echo "$as_me:${as_lineno-$LINENO}: The SPDY support in nghttpx and h2load will be disabled." >&5 +$as_echo "$as_me: The SPDY support in nghttpx and h2load will be disabled." >&6;} + fi +fi + +if test "x${request_spdylay}" = "xyes" && + test "x${have_spdylay}" != "xyes"; then + as_fn_error $? "spdylay was requested (--with-spdylay) but not found" "$LINENO" 5 +fi + + if test "x${have_spdylay}" = "xyes" ; then + HAVE_SPDYLAY_TRUE= + HAVE_SPDYLAY_FALSE='#' +else + HAVE_SPDYLAY_TRUE='#' + HAVE_SPDYLAY_FALSE= +fi + + +# Check Boost Asio library +have_asio_lib=no + +if test "x${request_asio_lib}" = "xyes"; then + + +# Check whether --with-boost was given. +if test "${with_boost+set}" = set; then : + withval=$with_boost; + if test "$withval" = "no"; then + want_boost="no" + elif test "$withval" = "yes"; then + want_boost="yes" + ac_boost_path="" + else + want_boost="yes" + ac_boost_path="$withval" + fi + +else + want_boost="yes" +fi + + + + +# Check whether --with-boost-libdir was given. +if test "${with_boost_libdir+set}" = set; then : + withval=$with_boost_libdir; + if test -d "$withval" + then + ac_boost_lib_path="$withval" + else + as_fn_error $? "--with-boost-libdir expected directory name" "$LINENO" 5 + fi + +else + ac_boost_lib_path="" + +fi + + +if test "x$want_boost" = "xyes"; then + boost_lib_version_req=1.54.0 + boost_lib_version_req_shorten=`expr $boost_lib_version_req : '\([0-9]*\.[0-9]*\)'` + boost_lib_version_req_major=`expr $boost_lib_version_req : '\([0-9]*\)'` + boost_lib_version_req_minor=`expr $boost_lib_version_req : '[0-9]*\.\([0-9]*\)'` + boost_lib_version_req_sub_minor=`expr $boost_lib_version_req : '[0-9]*\.[0-9]*\.\([0-9]*\)'` + if test "x$boost_lib_version_req_sub_minor" = "x" ; then + boost_lib_version_req_sub_minor="0" + fi + WANT_BOOST_VERSION=`expr $boost_lib_version_req_major \* 100000 \+ $boost_lib_version_req_minor \* 100 \+ $boost_lib_version_req_sub_minor` + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for boostlib >= $boost_lib_version_req" >&5 +$as_echo_n "checking for boostlib >= $boost_lib_version_req... " >&6; } + succeeded=no + + libsubdirs="lib" + ax_arch=`uname -m` + case $ax_arch in + x86_64) + libsubdirs="lib64 libx32 lib lib64" + ;; + ppc64|s390x|sparc64|aarch64|ppc64le) + libsubdirs="lib64 lib lib64 ppc64le" + ;; + esac + + + libsubdirs="lib/${host_cpu}-${host_os} $libsubdirs" + + case ${host_cpu} in + i?86) + libsubdirs="lib/i386-${host_os} $libsubdirs" + ;; + esac + + if test "$ac_boost_path" != ""; then + BOOST_CPPFLAGS="-I$ac_boost_path/include" + for ac_boost_path_tmp in $libsubdirs; do + if test -d "$ac_boost_path"/"$ac_boost_path_tmp" ; then + BOOST_LDFLAGS="-L$ac_boost_path/$ac_boost_path_tmp" + break + fi + done + elif test "$cross_compiling" != yes; then + for ac_boost_path_tmp in /usr /usr/local /opt /opt/local ; do + if test -d "$ac_boost_path_tmp/include/boost" && test -r "$ac_boost_path_tmp/include/boost"; then + for libsubdir in $libsubdirs ; do + if ls "$ac_boost_path_tmp/$libsubdir/libboost_"* >/dev/null 2>&1 ; then break; fi + done + BOOST_LDFLAGS="-L$ac_boost_path_tmp/$libsubdir" + BOOST_CPPFLAGS="-I$ac_boost_path_tmp/include" + break; + fi + done + fi + + if test "$ac_boost_lib_path" != ""; then + BOOST_LDFLAGS="-L$ac_boost_lib_path" + fi + + CPPFLAGS_SAVED="$CPPFLAGS" + CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS" + export CPPFLAGS + + LDFLAGS_SAVED="$LDFLAGS" + LDFLAGS="$LDFLAGS $BOOST_LDFLAGS" + export LDFLAGS + + + ac_ext=cpp +ac_cpp='$CXXCPP $CPPFLAGS' +ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_cxx_compiler_gnu + + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + + #include + +int +main () +{ + + #if BOOST_VERSION >= $WANT_BOOST_VERSION + // Everything is okay + #else + # error Boost version is too old + #endif + + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_compile "$LINENO"; then : + + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + succeeded=yes + found_system=yes + +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + + + + if test "x$succeeded" != "xyes"; then + _version=0 + if test "$ac_boost_path" != ""; then + if test -d "$ac_boost_path" && test -r "$ac_boost_path"; then + for i in `ls -d $ac_boost_path/include/boost-* 2>/dev/null`; do + _version_tmp=`echo $i | sed "s#$ac_boost_path##" | sed 's/\/include\/boost-//' | sed 's/_/./'` + V_CHECK=`expr $_version_tmp \> $_version` + if test "$V_CHECK" = "1" ; then + _version=$_version_tmp + fi + VERSION_UNDERSCORE=`echo $_version | sed 's/\./_/'` + BOOST_CPPFLAGS="-I$ac_boost_path/include/boost-$VERSION_UNDERSCORE" + done + fi + else + if test "$cross_compiling" != yes; then + for ac_boost_path in /usr /usr/local /opt /opt/local ; do + if test -d "$ac_boost_path" && test -r "$ac_boost_path"; then + for i in `ls -d $ac_boost_path/include/boost-* 2>/dev/null`; do + _version_tmp=`echo $i | sed "s#$ac_boost_path##" | sed 's/\/include\/boost-//' | sed 's/_/./'` + V_CHECK=`expr $_version_tmp \> $_version` + if test "$V_CHECK" = "1" ; then + _version=$_version_tmp + best_path=$ac_boost_path + fi + done + fi + done + + VERSION_UNDERSCORE=`echo $_version | sed 's/\./_/'` + BOOST_CPPFLAGS="-I$best_path/include/boost-$VERSION_UNDERSCORE" + if test "$ac_boost_lib_path" = ""; then + for libsubdir in $libsubdirs ; do + if ls "$best_path/$libsubdir/libboost_"* >/dev/null 2>&1 ; then break; fi + done + BOOST_LDFLAGS="-L$best_path/$libsubdir" + fi + fi + + if test "x$BOOST_ROOT" != "x"; then + for libsubdir in $libsubdirs ; do + if ls "$BOOST_ROOT/stage/$libsubdir/libboost_"* >/dev/null 2>&1 ; then break; fi + done + if test -d "$BOOST_ROOT" && test -r "$BOOST_ROOT" && test -d "$BOOST_ROOT/stage/$libsubdir" && test -r "$BOOST_ROOT/stage/$libsubdir"; then + version_dir=`expr //$BOOST_ROOT : '.*/\(.*\)'` + stage_version=`echo $version_dir | sed 's/boost_//' | sed 's/_/./g'` + stage_version_shorten=`expr $stage_version : '\([0-9]*\.[0-9]*\)'` + V_CHECK=`expr $stage_version_shorten \>\= $_version` + if test "$V_CHECK" = "1" -a "$ac_boost_lib_path" = "" ; then + { $as_echo "$as_me:${as_lineno-$LINENO}: We will use a staged boost library from $BOOST_ROOT" >&5 +$as_echo "$as_me: We will use a staged boost library from $BOOST_ROOT" >&6;} + BOOST_CPPFLAGS="-I$BOOST_ROOT" + BOOST_LDFLAGS="-L$BOOST_ROOT/stage/$libsubdir" + fi + fi + fi + fi + + CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS" + export CPPFLAGS + LDFLAGS="$LDFLAGS $BOOST_LDFLAGS" + export LDFLAGS + + ac_ext=cpp +ac_cpp='$CXXCPP $CPPFLAGS' +ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_cxx_compiler_gnu + + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + + #include + +int +main () +{ + + #if BOOST_VERSION >= $WANT_BOOST_VERSION + // Everything is okay + #else + # error Boost version is too old + #endif + + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_compile "$LINENO"; then : + + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + succeeded=yes + found_system=yes + +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + fi + + if test "$succeeded" != "yes" ; then + if test "$_version" = "0" ; then + { $as_echo "$as_me:${as_lineno-$LINENO}: We could not detect the boost libraries (version $boost_lib_version_req_shorten or higher). If you have a staged boost library (still not installed) please specify \$BOOST_ROOT in your environment and do not give a PATH to --with-boost option. If you are sure you have boost installed, then check your version number looking in . See http://randspringer.de/boost for more documentation." >&5 +$as_echo "$as_me: We could not detect the boost libraries (version $boost_lib_version_req_shorten or higher). If you have a staged boost library (still not installed) please specify \$BOOST_ROOT in your environment and do not give a PATH to --with-boost option. If you are sure you have boost installed, then check your version number looking in . See http://randspringer.de/boost for more documentation." >&6;} + else + { $as_echo "$as_me:${as_lineno-$LINENO}: Your boost libraries seems to old (version $_version)." >&5 +$as_echo "$as_me: Your boost libraries seems to old (version $_version)." >&6;} + fi + # execute ACTION-IF-NOT-FOUND (if present): + have_boost_base=no + else + + + +$as_echo "#define HAVE_BOOST /**/" >>confdefs.h + + # execute ACTION-IF-FOUND (if present): + have_boost_base=yes + fi + + CPPFLAGS="$CPPFLAGS_SAVED" + LDFLAGS="$LDFLAGS_SAVED" +fi + + + + if test "x${have_boost_base}" = "xyes"; then + + +# Check whether --with-boost-asio was given. +if test "${with_boost_asio+set}" = set; then : + withval=$with_boost_asio; + if test "$withval" = "no"; then + want_boost="no" + elif test "$withval" = "yes"; then + want_boost="yes" + ax_boost_user_asio_lib="" + else + want_boost="yes" + ax_boost_user_asio_lib="$withval" + fi + +else + want_boost="yes" + +fi + + + if test "x$want_boost" = "xyes"; then + + CPPFLAGS_SAVED="$CPPFLAGS" + CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS" + export CPPFLAGS + + LDFLAGS_SAVED="$LDFLAGS" + LDFLAGS="$LDFLAGS $BOOST_LDFLAGS" + export LDFLAGS + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the Boost::ASIO library is available" >&5 +$as_echo_n "checking whether the Boost::ASIO library is available... " >&6; } +if ${ax_cv_boost_asio+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_ext=cpp +ac_cpp='$CXXCPP $CPPFLAGS' +ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_cxx_compiler_gnu + + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + #include + +int +main () +{ + + + boost::asio::io_service io; + boost::system::error_code timer_result; + boost::asio::deadline_timer t(io); + t.cancel(); + io.run_one(); + return 0; + + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_compile "$LINENO"; then : + ax_cv_boost_asio=yes +else + ax_cv_boost_asio=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_boost_asio" >&5 +$as_echo "$ax_cv_boost_asio" >&6; } + if test "x$ax_cv_boost_asio" = "xyes"; then + +$as_echo "#define HAVE_BOOST_ASIO /**/" >>confdefs.h + + BN=boost_system + BOOSTLIBDIR=`echo $BOOST_LDFLAGS | sed -e 's/[^\/]*//'` + if test "x$ax_boost_user_asio_lib" = "x"; then + for ax_lib in `ls $BOOSTLIBDIR/libboost_system*.so* $BOOSTLIBDIR/libboost_system*.dylib* $BOOSTLIBDIR/libboost_system*.a* 2>/dev/null | sed 's,.*/,,' | sed -e 's;^lib\(boost_system.*\)\.so.*$;\1;' -e 's;^lib\(boost_system.*\)\.dylib.*$;\1;' -e 's;^lib\(boost_system.*\)\.a.*$;\1;' ` ; do + as_ac_Lib=`$as_echo "ac_cv_lib_$ax_lib''_main" | $as_tr_sh` +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for main in -l$ax_lib" >&5 +$as_echo_n "checking for main in -l$ax_lib... " >&6; } +if eval \${$as_ac_Lib+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-l$ax_lib $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + + +int +main () +{ +return main (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + eval "$as_ac_Lib=yes" +else + eval "$as_ac_Lib=no" +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +eval ac_res=\$$as_ac_Lib + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } +if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : + BOOST_ASIO_LIB="-l$ax_lib" link_thread="yes" break +else + link_thread="no" +fi + + done + else + for ax_lib in $ax_boost_user_asio_lib $BN-$ax_boost_user_asio_lib; do + as_ac_Lib=`$as_echo "ac_cv_lib_$ax_lib''_main" | $as_tr_sh` +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for main in -l$ax_lib" >&5 +$as_echo_n "checking for main in -l$ax_lib... " >&6; } +if eval \${$as_ac_Lib+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-l$ax_lib $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + + +int +main () +{ +return main (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + eval "$as_ac_Lib=yes" +else + eval "$as_ac_Lib=no" +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +eval ac_res=\$$as_ac_Lib + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } +if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : + BOOST_ASIO_LIB="-l$ax_lib" link_asio="yes" break +else + link_asio="no" +fi + + done + + fi + if test "x$ax_lib" = "x"; then + as_fn_error $? "Could not find a version of the library!" "$LINENO" 5 + fi + if test "x$link_asio" = "xno"; then + as_fn_error $? "Could not link against $ax_lib !" "$LINENO" 5 + fi + fi + + CPPFLAGS="$CPPFLAGS_SAVED" + LDFLAGS="$LDFLAGS_SAVED" + fi + + + +# Check whether --with-boost-system was given. +if test "${with_boost_system+set}" = set; then : + withval=$with_boost_system; + if test "$withval" = "no"; then + want_boost="no" + elif test "$withval" = "yes"; then + want_boost="yes" + ax_boost_user_system_lib="" + else + want_boost="yes" + ax_boost_user_system_lib="$withval" + fi + +else + want_boost="yes" + +fi + + + if test "x$want_boost" = "xyes"; then + + + CPPFLAGS_SAVED="$CPPFLAGS" + CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS" + export CPPFLAGS + + LDFLAGS_SAVED="$LDFLAGS" + LDFLAGS="$LDFLAGS $BOOST_LDFLAGS" + export LDFLAGS + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the Boost::System library is available" >&5 +$as_echo_n "checking whether the Boost::System library is available... " >&6; } +if ${ax_cv_boost_system+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_ext=cpp +ac_cpp='$CXXCPP $CPPFLAGS' +ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_cxx_compiler_gnu + + CXXFLAGS_SAVE=$CXXFLAGS + + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +int +main () +{ +boost::system::system_category + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_compile "$LINENO"; then : + ax_cv_boost_system=yes +else + ax_cv_boost_system=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + CXXFLAGS=$CXXFLAGS_SAVE + ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_boost_system" >&5 +$as_echo "$ax_cv_boost_system" >&6; } + if test "x$ax_cv_boost_system" = "xyes"; then + + + +$as_echo "#define HAVE_BOOST_SYSTEM /**/" >>confdefs.h + + BOOSTLIBDIR=`echo $BOOST_LDFLAGS | sed -e 's/[^\/]*//'` + + LDFLAGS_SAVE=$LDFLAGS + if test "x$ax_boost_user_system_lib" = "x"; then + for libextension in `ls -r $BOOSTLIBDIR/libboost_system* 2>/dev/null | sed 's,.*/lib,,' | sed 's,\..*,,'` ; do + ax_lib=${libextension} + as_ac_Lib=`$as_echo "ac_cv_lib_$ax_lib''_exit" | $as_tr_sh` +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for exit in -l$ax_lib" >&5 +$as_echo_n "checking for exit in -l$ax_lib... " >&6; } +if eval \${$as_ac_Lib+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-l$ax_lib $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char exit (); +int +main () +{ +return exit (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + eval "$as_ac_Lib=yes" +else + eval "$as_ac_Lib=no" +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +eval ac_res=\$$as_ac_Lib + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } +if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : + BOOST_SYSTEM_LIB="-l$ax_lib"; link_system="yes"; break +else + link_system="no" +fi + + done + if test "x$link_system" != "xyes"; then + for libextension in `ls -r $BOOSTLIBDIR/boost_system* 2>/dev/null | sed 's,.*/,,' | sed -e 's,\..*,,'` ; do + ax_lib=${libextension} + as_ac_Lib=`$as_echo "ac_cv_lib_$ax_lib''_exit" | $as_tr_sh` +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for exit in -l$ax_lib" >&5 +$as_echo_n "checking for exit in -l$ax_lib... " >&6; } +if eval \${$as_ac_Lib+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-l$ax_lib $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char exit (); +int +main () +{ +return exit (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + eval "$as_ac_Lib=yes" +else + eval "$as_ac_Lib=no" +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +eval ac_res=\$$as_ac_Lib + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } +if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : + BOOST_SYSTEM_LIB="-l$ax_lib"; link_system="yes"; break +else + link_system="no" +fi + + done + fi + + else + for ax_lib in $ax_boost_user_system_lib boost_system-$ax_boost_user_system_lib; do + as_ac_Lib=`$as_echo "ac_cv_lib_$ax_lib''_exit" | $as_tr_sh` +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for exit in -l$ax_lib" >&5 +$as_echo_n "checking for exit in -l$ax_lib... " >&6; } +if eval \${$as_ac_Lib+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-l$ax_lib $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char exit (); +int +main () +{ +return exit (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + eval "$as_ac_Lib=yes" +else + eval "$as_ac_Lib=no" +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +eval ac_res=\$$as_ac_Lib + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } +if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : + BOOST_SYSTEM_LIB="-l$ax_lib"; link_system="yes"; break +else + link_system="no" +fi + + done + + fi + if test "x$ax_lib" = "x"; then + as_fn_error $? "Could not find a version of the library!" "$LINENO" 5 + fi + if test "x$link_system" = "xno"; then + as_fn_error $? "Could not link against $ax_lib !" "$LINENO" 5 + fi + fi + + CPPFLAGS="$CPPFLAGS_SAVED" + LDFLAGS="$LDFLAGS_SAVED" + fi + + + +# Check whether --with-boost-thread was given. +if test "${with_boost_thread+set}" = set; then : + withval=$with_boost_thread; + if test "$withval" = "no"; then + want_boost="no" + elif test "$withval" = "yes"; then + want_boost="yes" + ax_boost_user_thread_lib="" + else + want_boost="yes" + ax_boost_user_thread_lib="$withval" + fi + +else + want_boost="yes" + +fi + + + if test "x$want_boost" = "xyes"; then + + + CPPFLAGS_SAVED="$CPPFLAGS" + CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS" + export CPPFLAGS + + LDFLAGS_SAVED="$LDFLAGS" + LDFLAGS="$LDFLAGS $BOOST_LDFLAGS" + export LDFLAGS + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the Boost::Thread library is available" >&5 +$as_echo_n "checking whether the Boost::Thread library is available... " >&6; } +if ${ax_cv_boost_thread+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_ext=cpp +ac_cpp='$CXXCPP $CPPFLAGS' +ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_cxx_compiler_gnu + + CXXFLAGS_SAVE=$CXXFLAGS + + if test "x$host_os" = "xsolaris" ; then + CXXFLAGS="-pthreads $CXXFLAGS" + elif test "x$host_os" = "xmingw32" ; then + CXXFLAGS="-mthreads $CXXFLAGS" + else + CXXFLAGS="-pthread $CXXFLAGS" + fi + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +int +main () +{ +boost::thread_group thrds; + return 0; + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_compile "$LINENO"; then : + ax_cv_boost_thread=yes +else + ax_cv_boost_thread=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + CXXFLAGS=$CXXFLAGS_SAVE + ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_boost_thread" >&5 +$as_echo "$ax_cv_boost_thread" >&6; } + if test "x$ax_cv_boost_thread" = "xyes"; then + if test "x$host_os" = "xsolaris" ; then + BOOST_CPPFLAGS="-pthreads $BOOST_CPPFLAGS" + elif test "x$host_os" = "xmingw32" ; then + BOOST_CPPFLAGS="-mthreads $BOOST_CPPFLAGS" + else + BOOST_CPPFLAGS="-pthread $BOOST_CPPFLAGS" + fi + + + + +$as_echo "#define HAVE_BOOST_THREAD /**/" >>confdefs.h + + BOOSTLIBDIR=`echo $BOOST_LDFLAGS | sed -e 's/[^\/]*//'` + + LDFLAGS_SAVE=$LDFLAGS + case "x$host_os" in + *bsd* ) + LDFLAGS="-pthread $LDFLAGS" + break; + ;; + esac + if test "x$ax_boost_user_thread_lib" = "x"; then + for libextension in `ls -r $BOOSTLIBDIR/libboost_thread* 2>/dev/null | sed 's,.*/lib,,' | sed 's,\..*,,'`; do + ax_lib=${libextension} + as_ac_Lib=`$as_echo "ac_cv_lib_$ax_lib''_exit" | $as_tr_sh` +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for exit in -l$ax_lib" >&5 +$as_echo_n "checking for exit in -l$ax_lib... " >&6; } +if eval \${$as_ac_Lib+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-l$ax_lib $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char exit (); +int +main () +{ +return exit (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + eval "$as_ac_Lib=yes" +else + eval "$as_ac_Lib=no" +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +eval ac_res=\$$as_ac_Lib + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } +if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : + BOOST_THREAD_LIB="-l$ax_lib"; link_thread="yes"; break +else + link_thread="no" +fi + + done + if test "x$link_thread" != "xyes"; then + for libextension in `ls -r $BOOSTLIBDIR/boost_thread* 2>/dev/null | sed 's,.*/,,' | sed 's,\..*,,'`; do + ax_lib=${libextension} + as_ac_Lib=`$as_echo "ac_cv_lib_$ax_lib''_exit" | $as_tr_sh` +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for exit in -l$ax_lib" >&5 +$as_echo_n "checking for exit in -l$ax_lib... " >&6; } +if eval \${$as_ac_Lib+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-l$ax_lib $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char exit (); +int +main () +{ +return exit (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + eval "$as_ac_Lib=yes" +else + eval "$as_ac_Lib=no" +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +eval ac_res=\$$as_ac_Lib + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } +if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : + BOOST_THREAD_LIB="-l$ax_lib"; link_thread="yes"; break +else + link_thread="no" +fi + + done + fi + + else + for ax_lib in $ax_boost_user_thread_lib boost_thread-$ax_boost_user_thread_lib; do + as_ac_Lib=`$as_echo "ac_cv_lib_$ax_lib''_exit" | $as_tr_sh` +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for exit in -l$ax_lib" >&5 +$as_echo_n "checking for exit in -l$ax_lib... " >&6; } +if eval \${$as_ac_Lib+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-l$ax_lib $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char exit (); +int +main () +{ +return exit (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + eval "$as_ac_Lib=yes" +else + eval "$as_ac_Lib=no" +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +eval ac_res=\$$as_ac_Lib + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } +if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : + BOOST_THREAD_LIB="-l$ax_lib"; link_thread="yes"; break +else + link_thread="no" +fi + + done + + fi + if test "x$ax_lib" = "x"; then + as_fn_error $? "Could not find a version of the library!" "$LINENO" 5 + fi + if test "x$link_thread" = "xno"; then + as_fn_error $? "Could not link against $ax_lib !" "$LINENO" 5 + else + case "x$host_os" in + *bsd* ) + BOOST_LDFLAGS="-pthread $BOOST_LDFLAGS" + break; + ;; + esac + + fi + fi + + CPPFLAGS="$CPPFLAGS_SAVED" + LDFLAGS="$LDFLAGS_SAVED" + fi + + + if test "x${ax_cv_boost_asio}" = "xyes" && + test "x${ax_cv_boost_system}" = "xyes" && + test "x${ax_cv_boost_thread}" = "xyes"; then + have_asio_lib=yes + fi + fi +fi + +# The nghttp, nghttpd and nghttpx under src depend on zlib, OpenSSL +# and libev +enable_app=no +if test "x${request_app}" != "xno" && + test "x${have_zlib}" = "xyes" && + test "x${have_openssl}" = "xyes" && + test "x${have_libev}" = "xyes"; then + enable_app=yes +fi + +if test "x${request_app}" = "xyes" && + test "x${enable_app}" != "xyes"; then + as_fn_error $? "applications were requested (--enable-app) but dependencies are not met." "$LINENO" 5 +fi + + if test "x${enable_app}" = "xyes" ; then + ENABLE_APP_TRUE= + ENABLE_APP_FALSE='#' +else + ENABLE_APP_TRUE='#' + ENABLE_APP_FALSE= +fi + + +enable_hpack_tools=no +# HPACK tools requires jansson +if test "x${request_hpack_tools}" != "xno" && + test "x${have_jansson}" = "xyes"; then + enable_hpack_tools=yes +fi + +if test "x${request_hpack_tools}" = "xyes" && + test "x${enable_hpack_tools}" != "xyes"; then + as_fn_error $? "HPACK tools were requested (--enable-hpack-tools) but dependencies are not met." "$LINENO" 5 +fi + + if test "x${enable_hpack_tools}" = "xyes" ; then + ENABLE_HPACK_TOOLS_TRUE= + ENABLE_HPACK_TOOLS_FALSE='#' +else + ENABLE_HPACK_TOOLS_TRUE='#' + ENABLE_HPACK_TOOLS_FALSE= +fi + + +# C++ library libnghttp2_asio + +enable_asio_lib=no +if test "x${request_asio_lib}" != "xno" && + test "x${have_asio_lib}" = "xyes"; then + enable_asio_lib=yes +fi + + if test "x${enable_asio_lib}" = "xyes" ; then + ENABLE_ASIO_LIB_TRUE= + ENABLE_ASIO_LIB_FALSE='#' +else + ENABLE_ASIO_LIB_TRUE='#' + ENABLE_ASIO_LIB_FALSE= +fi + + +# The example programs depend on OpenSSL and libevent_openssl +enable_examples=no +if test "x${request_examples}" != "xno" && + test "x${have_openssl}" = "xyes" && + test "x${have_libevent_openssl}" = "xyes"; then + enable_examples=yes +fi + +if test "x${request_examples}" = "xyes" && + test "x${enable_examples}" != "xyes"; then + as_fn_error $? "examples were requested (--enable-examples) but dependencies are not met." "$LINENO" 5 +fi + + if test "x${enable_examples}" = "xyes" ; then + ENABLE_EXAMPLES_TRUE= + ENABLE_EXAMPLES_FALSE='#' +else + ENABLE_EXAMPLES_TRUE='#' + ENABLE_EXAMPLES_FALSE= +fi + + +# Python bindings +enable_python_bindings=no +if test "x${request_python_bindings}" != "xno" && + test "x${CYTHON}" != "x" && + test "x${PYTHON}" != "x:" && + test "x${have_python_dev}" = "xyes"; then + enable_python_bindings=yes +fi + +if test "x${request_python_bindings}" = "xyes" && + test "x${enable_python_bindings}" != "xyes"; then + as_fn_error $? "python bindings were requested (--enable-python-bindings) but dependencies are not met." "$LINENO" 5 +fi + + if test "x${enable_python_bindings}" = "xyes"; then + ENABLE_PYTHON_BINDINGS_TRUE= + ENABLE_PYTHON_BINDINGS_FALSE='#' +else + ENABLE_PYTHON_BINDINGS_TRUE='#' + ENABLE_PYTHON_BINDINGS_FALSE= +fi + + +# Produce cython conditional, so that we can distribute generated C +# source + if test "x${CYTHON}" != "x"; then + HAVE_CYTHON_TRUE= + HAVE_CYTHON_FALSE='#' +else + HAVE_CYTHON_TRUE='#' + HAVE_CYTHON_FALSE= +fi + + +# failmalloc tests +enable_failmalloc=no +if test "x${request_failmalloc}" = "xyes"; then + enable_failmalloc=yes +fi + + if test "x${enable_failmalloc}" = "xyes" ; then + ENABLE_FAILMALLOC_TRUE= + ENABLE_FAILMALLOC_FALSE='#' +else + ENABLE_FAILMALLOC_TRUE='#' + ENABLE_FAILMALLOC_FALSE= +fi + + +# Checks for header files. + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to enable assertions" >&5 +$as_echo_n "checking whether to enable assertions... " >&6; } + # Check whether --enable-assert was given. +if test "${enable_assert+set}" = set; then : + enableval=$enable_assert; ac_enable_assert=$enableval + if test "x$enableval" = xno; then : + +$as_echo "#define NDEBUG 1" >>confdefs.h + +elif test "x$enableval" != xyes; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: invalid argument supplied to --enable-assert" >&5 +$as_echo "$as_me: WARNING: invalid argument supplied to --enable-assert" >&2;} + ac_enable_assert=yes +fi +else + ac_enable_assert=yes +fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_enable_assert" >&5 +$as_echo "$ac_enable_assert" >&6; } + +for ac_header in \ + arpa/inet.h \ + fcntl.h \ + inttypes.h \ + limits.h \ + netdb.h \ + netinet/in.h \ + pwd.h \ + stddef.h \ + stdint.h \ + stdlib.h \ + string.h \ + sys/socket.h \ + sys/time.h \ + syslog.h \ + time.h \ + unistd.h \ + +do : + as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` +ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default" +if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : + cat >>confdefs.h <<_ACEOF +#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + + +# Checks for typedefs, structures, and compiler characteristics. +ac_fn_c_check_type "$LINENO" "size_t" "ac_cv_type_size_t" "$ac_includes_default" +if test "x$ac_cv_type_size_t" = xyes; then : + +else + +cat >>confdefs.h <<_ACEOF +#define size_t unsigned int +_ACEOF + +fi + +ac_fn_c_check_type "$LINENO" "ssize_t" "ac_cv_type_ssize_t" "$ac_includes_default" +if test "x$ac_cv_type_ssize_t" = xyes; then : + +else + +cat >>confdefs.h <<_ACEOF +#define ssize_t int +_ACEOF + +fi + +ac_fn_c_find_uintX_t "$LINENO" "8" "ac_cv_c_uint8_t" +case $ac_cv_c_uint8_t in #( + no|yes) ;; #( + *) + +$as_echo "#define _UINT8_T 1" >>confdefs.h + + +cat >>confdefs.h <<_ACEOF +#define uint8_t $ac_cv_c_uint8_t +_ACEOF +;; + esac + +ac_fn_c_find_uintX_t "$LINENO" "16" "ac_cv_c_uint16_t" +case $ac_cv_c_uint16_t in #( + no|yes) ;; #( + *) + + +cat >>confdefs.h <<_ACEOF +#define uint16_t $ac_cv_c_uint16_t +_ACEOF +;; + esac + +ac_fn_c_find_uintX_t "$LINENO" "32" "ac_cv_c_uint32_t" +case $ac_cv_c_uint32_t in #( + no|yes) ;; #( + *) + +$as_echo "#define _UINT32_T 1" >>confdefs.h + + +cat >>confdefs.h <<_ACEOF +#define uint32_t $ac_cv_c_uint32_t +_ACEOF +;; + esac + +ac_fn_c_find_uintX_t "$LINENO" "64" "ac_cv_c_uint64_t" +case $ac_cv_c_uint64_t in #( + no|yes) ;; #( + *) + +$as_echo "#define _UINT64_T 1" >>confdefs.h + + +cat >>confdefs.h <<_ACEOF +#define uint64_t $ac_cv_c_uint64_t +_ACEOF +;; + esac + +ac_fn_c_find_intX_t "$LINENO" "8" "ac_cv_c_int8_t" +case $ac_cv_c_int8_t in #( + no|yes) ;; #( + *) + +cat >>confdefs.h <<_ACEOF +#define int8_t $ac_cv_c_int8_t +_ACEOF +;; +esac + +ac_fn_c_find_intX_t "$LINENO" "16" "ac_cv_c_int16_t" +case $ac_cv_c_int16_t in #( + no|yes) ;; #( + *) + +cat >>confdefs.h <<_ACEOF +#define int16_t $ac_cv_c_int16_t +_ACEOF +;; +esac + +ac_fn_c_find_intX_t "$LINENO" "32" "ac_cv_c_int32_t" +case $ac_cv_c_int32_t in #( + no|yes) ;; #( + *) + +cat >>confdefs.h <<_ACEOF +#define int32_t $ac_cv_c_int32_t +_ACEOF +;; +esac + +ac_fn_c_find_intX_t "$LINENO" "64" "ac_cv_c_int64_t" +case $ac_cv_c_int64_t in #( + no|yes) ;; #( + *) + +cat >>confdefs.h <<_ACEOF +#define int64_t $ac_cv_c_int64_t +_ACEOF +;; +esac + +ac_fn_c_check_type "$LINENO" "off_t" "ac_cv_type_off_t" "$ac_includes_default" +if test "x$ac_cv_type_off_t" = xyes; then : + +else + +cat >>confdefs.h <<_ACEOF +#define off_t long int +_ACEOF + +fi + +ac_fn_c_check_type "$LINENO" "pid_t" "ac_cv_type_pid_t" "$ac_includes_default" +if test "x$ac_cv_type_pid_t" = xyes; then : + +else + +cat >>confdefs.h <<_ACEOF +#define pid_t int +_ACEOF + +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for uid_t in sys/types.h" >&5 +$as_echo_n "checking for uid_t in sys/types.h... " >&6; } +if ${ac_cv_type_uid_t+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "uid_t" >/dev/null 2>&1; then : + ac_cv_type_uid_t=yes +else + ac_cv_type_uid_t=no +fi +rm -f conftest* + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_type_uid_t" >&5 +$as_echo "$ac_cv_type_uid_t" >&6; } +if test $ac_cv_type_uid_t = no; then + +$as_echo "#define uid_t int" >>confdefs.h + + +$as_echo "#define gid_t int" >>confdefs.h + +fi + +ac_fn_c_check_type "$LINENO" "ptrdiff_t" "ac_cv_type_ptrdiff_t" "$ac_includes_default" +if test "x$ac_cv_type_ptrdiff_t" = xyes; then : + +cat >>confdefs.h <<_ACEOF +#define HAVE_PTRDIFF_T 1 +_ACEOF + + +fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether byte ordering is bigendian" >&5 +$as_echo_n "checking whether byte ordering is bigendian... " >&6; } +if ${ac_cv_c_bigendian+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_cv_c_bigendian=unknown + # See if we're dealing with a universal compiler. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#ifndef __APPLE_CC__ + not a universal capable compiler + #endif + typedef int dummy; + +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + + # Check for potential -arch flags. It is not universal unless + # there are at least two -arch flags with different values. + ac_arch= + ac_prev= + for ac_word in $CC $CFLAGS $CPPFLAGS $LDFLAGS; do + if test -n "$ac_prev"; then + case $ac_word in + i?86 | x86_64 | ppc | ppc64) + if test -z "$ac_arch" || test "$ac_arch" = "$ac_word"; then + ac_arch=$ac_word + else + ac_cv_c_bigendian=universal + break + fi + ;; + esac + ac_prev= + elif test "x$ac_word" = "x-arch"; then + ac_prev=arch + fi + done +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + if test $ac_cv_c_bigendian = unknown; then + # See if sys/param.h defines the BYTE_ORDER macro. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include + #include + +int +main () +{ +#if ! (defined BYTE_ORDER && defined BIG_ENDIAN \ + && defined LITTLE_ENDIAN && BYTE_ORDER && BIG_ENDIAN \ + && LITTLE_ENDIAN) + bogus endian macros + #endif + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + # It does; now see whether it defined to BIG_ENDIAN or not. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include + #include + +int +main () +{ +#if BYTE_ORDER != BIG_ENDIAN + not big endian + #endif + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_c_bigendian=yes +else + ac_cv_c_bigendian=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + fi + if test $ac_cv_c_bigendian = unknown; then + # See if defines _LITTLE_ENDIAN or _BIG_ENDIAN (e.g., Solaris). + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include + +int +main () +{ +#if ! (defined _LITTLE_ENDIAN || defined _BIG_ENDIAN) + bogus endian macros + #endif + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + # It does; now see whether it defined to _BIG_ENDIAN or not. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include + +int +main () +{ +#ifndef _BIG_ENDIAN + not big endian + #endif + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_c_bigendian=yes +else + ac_cv_c_bigendian=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + fi + if test $ac_cv_c_bigendian = unknown; then + # Compile a test program. + if test "$cross_compiling" = yes; then : + # Try to guess by grepping values from an object file. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +short int ascii_mm[] = + { 0x4249, 0x4765, 0x6E44, 0x6961, 0x6E53, 0x7953, 0 }; + short int ascii_ii[] = + { 0x694C, 0x5454, 0x656C, 0x6E45, 0x6944, 0x6E61, 0 }; + int use_ascii (int i) { + return ascii_mm[i] + ascii_ii[i]; + } + short int ebcdic_ii[] = + { 0x89D3, 0xE3E3, 0x8593, 0x95C5, 0x89C4, 0x9581, 0 }; + short int ebcdic_mm[] = + { 0xC2C9, 0xC785, 0x95C4, 0x8981, 0x95E2, 0xA8E2, 0 }; + int use_ebcdic (int i) { + return ebcdic_mm[i] + ebcdic_ii[i]; + } + extern int foo; + +int +main () +{ +return use_ascii (foo) == use_ebcdic (foo); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + if grep BIGenDianSyS conftest.$ac_objext >/dev/null; then + ac_cv_c_bigendian=yes + fi + if grep LiTTleEnDian conftest.$ac_objext >/dev/null ; then + if test "$ac_cv_c_bigendian" = unknown; then + ac_cv_c_bigendian=no + else + # finding both strings is unlikely to happen, but who knows? + ac_cv_c_bigendian=unknown + fi + fi +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ + + /* Are we little or big endian? From Harbison&Steele. */ + union + { + long int l; + char c[sizeof (long int)]; + } u; + u.l = 1; + return u.c[sizeof (long int) - 1] == 1; + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + ac_cv_c_bigendian=no +else + ac_cv_c_bigendian=yes +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +fi + + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_bigendian" >&5 +$as_echo "$ac_cv_c_bigendian" >&6; } + case $ac_cv_c_bigendian in #( + yes) + $as_echo "#define WORDS_BIGENDIAN 1" >>confdefs.h +;; #( + no) + ;; #( + universal) + +$as_echo "#define AC_APPLE_UNIVERSAL_BUILD 1" >>confdefs.h + + ;; #( + *) + as_fn_error $? "unknown endianness + presetting ac_cv_c_bigendian=no (or yes) will help" "$LINENO" 5 ;; + esac + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for inline" >&5 +$as_echo_n "checking for inline... " >&6; } +if ${ac_cv_c_inline+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_cv_c_inline=no +for ac_kw in inline __inline__ __inline; do + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#ifndef __cplusplus +typedef int foo_t; +static $ac_kw foo_t static_foo () {return 0; } +$ac_kw foo_t foo () {return 0; } +#endif + +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_c_inline=$ac_kw +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + test "$ac_cv_c_inline" != no && break +done + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_inline" >&5 +$as_echo "$ac_cv_c_inline" >&6; } + +case $ac_cv_c_inline in + inline | yes) ;; + *) + case $ac_cv_c_inline in + no) ac_val=;; + *) ac_val=$ac_cv_c_inline;; + esac + cat >>confdefs.h <<_ACEOF +#ifndef __cplusplus +#define inline $ac_val +#endif +_ACEOF + ;; +esac + +# Check whether --enable-largefile was given. +if test "${enable_largefile+set}" = set; then : + enableval=$enable_largefile; +fi + +if test "$enable_largefile" != no; then + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for special C compiler options needed for large files" >&5 +$as_echo_n "checking for special C compiler options needed for large files... " >&6; } +if ${ac_cv_sys_largefile_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_cv_sys_largefile_CC=no + if test "$GCC" != yes; then + ac_save_CC=$CC + while :; do + # IRIX 6.2 and later do not support large files by default, + # so use the C compiler's -n32 option if that helps. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include + /* Check that off_t can represent 2**63 - 1 correctly. + We can't simply define LARGE_OFF_T to be 9223372036854775807, + since some C++ compilers masquerading as C compilers + incorrectly reject 9223372036854775807. */ +#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31)) + int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 + && LARGE_OFF_T % 2147483647 == 1) + ? 1 : -1]; +int +main () +{ + + ; + return 0; +} +_ACEOF + if ac_fn_c_try_compile "$LINENO"; then : + break +fi +rm -f core conftest.err conftest.$ac_objext + CC="$CC -n32" + if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_sys_largefile_CC=' -n32'; break +fi +rm -f core conftest.err conftest.$ac_objext + break + done + CC=$ac_save_CC + rm -f conftest.$ac_ext + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sys_largefile_CC" >&5 +$as_echo "$ac_cv_sys_largefile_CC" >&6; } + if test "$ac_cv_sys_largefile_CC" != no; then + CC=$CC$ac_cv_sys_largefile_CC + fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for _FILE_OFFSET_BITS value needed for large files" >&5 +$as_echo_n "checking for _FILE_OFFSET_BITS value needed for large files... " >&6; } +if ${ac_cv_sys_file_offset_bits+:} false; then : + $as_echo_n "(cached) " >&6 +else + while :; do + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include + /* Check that off_t can represent 2**63 - 1 correctly. + We can't simply define LARGE_OFF_T to be 9223372036854775807, + since some C++ compilers masquerading as C compilers + incorrectly reject 9223372036854775807. */ +#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31)) + int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 + && LARGE_OFF_T % 2147483647 == 1) + ? 1 : -1]; +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_sys_file_offset_bits=no; break +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#define _FILE_OFFSET_BITS 64 +#include + /* Check that off_t can represent 2**63 - 1 correctly. + We can't simply define LARGE_OFF_T to be 9223372036854775807, + since some C++ compilers masquerading as C compilers + incorrectly reject 9223372036854775807. */ +#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31)) + int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 + && LARGE_OFF_T % 2147483647 == 1) + ? 1 : -1]; +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_sys_file_offset_bits=64; break +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + ac_cv_sys_file_offset_bits=unknown + break +done +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sys_file_offset_bits" >&5 +$as_echo "$ac_cv_sys_file_offset_bits" >&6; } +case $ac_cv_sys_file_offset_bits in #( + no | unknown) ;; + *) +cat >>confdefs.h <<_ACEOF +#define _FILE_OFFSET_BITS $ac_cv_sys_file_offset_bits +_ACEOF +;; +esac +rm -rf conftest* + if test $ac_cv_sys_file_offset_bits = unknown; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for _LARGE_FILES value needed for large files" >&5 +$as_echo_n "checking for _LARGE_FILES value needed for large files... " >&6; } +if ${ac_cv_sys_large_files+:} false; then : + $as_echo_n "(cached) " >&6 +else + while :; do + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include + /* Check that off_t can represent 2**63 - 1 correctly. + We can't simply define LARGE_OFF_T to be 9223372036854775807, + since some C++ compilers masquerading as C compilers + incorrectly reject 9223372036854775807. */ +#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31)) + int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 + && LARGE_OFF_T % 2147483647 == 1) + ? 1 : -1]; +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_sys_large_files=no; break +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#define _LARGE_FILES 1 +#include + /* Check that off_t can represent 2**63 - 1 correctly. + We can't simply define LARGE_OFF_T to be 9223372036854775807, + since some C++ compilers masquerading as C compilers + incorrectly reject 9223372036854775807. */ +#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31)) + int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 + && LARGE_OFF_T % 2147483647 == 1) + ? 1 : -1]; +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_sys_large_files=1; break +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + ac_cv_sys_large_files=unknown + break +done +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sys_large_files" >&5 +$as_echo "$ac_cv_sys_large_files" >&6; } +case $ac_cv_sys_large_files in #( + no | unknown) ;; + *) +cat >>confdefs.h <<_ACEOF +#define _LARGE_FILES $ac_cv_sys_large_files +_ACEOF +;; +esac +rm -rf conftest* + fi + + +fi + + +ac_fn_c_check_member "$LINENO" "struct tm" "tm_gmtoff" "ac_cv_member_struct_tm_tm_gmtoff" "#include +" +if test "x$ac_cv_member_struct_tm_tm_gmtoff" = xyes; then : + have_struct_tm_tm_gmtoff=yes +else + have_struct_tm_tm_gmtoff=no +fi + + +if test "x$have_struct_tm_tm_gmtoff" = "xyes"; then + +$as_echo "#define HAVE_STRUCT_TM_TM_GMTOFF 1" >>confdefs.h + +fi + +# Check size of pointer to decide we need 8 bytes alignment +# adjustment. +# The cast to long int works around a bug in the HP C Compiler +# version HP92453-01 B.11.11.23709.GP, which incorrectly rejects +# declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'. +# This bug is HP SR number 8606223364. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking size of int *" >&5 +$as_echo_n "checking size of int *... " >&6; } +if ${ac_cv_sizeof_int_p+:} false; then : + $as_echo_n "(cached) " >&6 +else + if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (int *))" "ac_cv_sizeof_int_p" "$ac_includes_default"; then : + +else + if test "$ac_cv_type_int_p" = yes; then + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error 77 "cannot compute sizeof (int *) +See \`config.log' for more details" "$LINENO" 5; } + else + ac_cv_sizeof_int_p=0 + fi +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_int_p" >&5 +$as_echo "$ac_cv_sizeof_int_p" >&6; } + + + +cat >>confdefs.h <<_ACEOF +#define SIZEOF_INT_P $ac_cv_sizeof_int_p +_ACEOF + + + +# Checks for library functions. +if test "x$cross_compiling" != "xyes"; then + for ac_header in stdlib.h +do : + ac_fn_c_check_header_mongrel "$LINENO" "stdlib.h" "ac_cv_header_stdlib_h" "$ac_includes_default" +if test "x$ac_cv_header_stdlib_h" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_STDLIB_H 1 +_ACEOF + +fi + +done + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for GNU libc compatible malloc" >&5 +$as_echo_n "checking for GNU libc compatible malloc... " >&6; } +if ${ac_cv_func_malloc_0_nonnull+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test "$cross_compiling" = yes; then : + ac_cv_func_malloc_0_nonnull=no +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#if defined STDC_HEADERS || defined HAVE_STDLIB_H +# include +#else +char *malloc (); +#endif + +int +main () +{ +return ! malloc (0); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + ac_cv_func_malloc_0_nonnull=yes +else + ac_cv_func_malloc_0_nonnull=no +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_malloc_0_nonnull" >&5 +$as_echo "$ac_cv_func_malloc_0_nonnull" >&6; } +if test $ac_cv_func_malloc_0_nonnull = yes; then : + +$as_echo "#define HAVE_MALLOC 1" >>confdefs.h + +else + $as_echo "#define HAVE_MALLOC 0" >>confdefs.h + + case " $LIBOBJS " in + *" malloc.$ac_objext "* ) ;; + *) LIBOBJS="$LIBOBJS malloc.$ac_objext" + ;; +esac + + +$as_echo "#define malloc rpl_malloc" >>confdefs.h + +fi + + +fi + +for ac_header in unistd.h +do : + ac_fn_c_check_header_mongrel "$LINENO" "unistd.h" "ac_cv_header_unistd_h" "$ac_includes_default" +if test "x$ac_cv_header_unistd_h" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_UNISTD_H 1 +_ACEOF + +fi + +done + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for working chown" >&5 +$as_echo_n "checking for working chown... " >&6; } +if ${ac_cv_func_chown_works+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test "$cross_compiling" = yes; then : + ac_cv_func_chown_works=no +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$ac_includes_default +#include + +int +main () +{ + char *f = "conftest.chown"; + struct stat before, after; + + if (creat (f, 0600) < 0) + return 1; + if (stat (f, &before) < 0) + return 1; + if (chown (f, (uid_t) -1, (gid_t) -1) == -1) + return 1; + if (stat (f, &after) < 0) + return 1; + return ! (before.st_uid == after.st_uid && before.st_gid == after.st_gid); + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + ac_cv_func_chown_works=yes +else + ac_cv_func_chown_works=no +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +fi + +rm -f conftest.chown + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_chown_works" >&5 +$as_echo "$ac_cv_func_chown_works" >&6; } +if test $ac_cv_func_chown_works = yes; then + +$as_echo "#define HAVE_CHOWN 1" >>confdefs.h + +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for error_at_line" >&5 +$as_echo_n "checking for error_at_line... " >&6; } +if ${ac_cv_lib_error_at_line+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +int +main () +{ +error_at_line (0, 0, "", 0, "an error occurred"); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_error_at_line=yes +else + ac_cv_lib_error_at_line=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_error_at_line" >&5 +$as_echo "$ac_cv_lib_error_at_line" >&6; } +if test $ac_cv_lib_error_at_line = no; then + case " $LIBOBJS " in + *" error.$ac_objext "* ) ;; + *) LIBOBJS="$LIBOBJS error.$ac_objext" + ;; +esac + +fi + +for ac_header in vfork.h +do : + ac_fn_c_check_header_mongrel "$LINENO" "vfork.h" "ac_cv_header_vfork_h" "$ac_includes_default" +if test "x$ac_cv_header_vfork_h" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_VFORK_H 1 +_ACEOF + +fi + +done + +for ac_func in fork vfork +do : + as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` +ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" +if eval test \"x\$"$as_ac_var"\" = x"yes"; then : + cat >>confdefs.h <<_ACEOF +#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + +fi +done + +if test "x$ac_cv_func_fork" = xyes; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for working fork" >&5 +$as_echo_n "checking for working fork... " >&6; } +if ${ac_cv_func_fork_works+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test "$cross_compiling" = yes; then : + ac_cv_func_fork_works=cross +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ + + /* By Ruediger Kuhlmann. */ + return fork () < 0; + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + ac_cv_func_fork_works=yes +else + ac_cv_func_fork_works=no +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_fork_works" >&5 +$as_echo "$ac_cv_func_fork_works" >&6; } + +else + ac_cv_func_fork_works=$ac_cv_func_fork +fi +if test "x$ac_cv_func_fork_works" = xcross; then + case $host in + *-*-amigaos* | *-*-msdosdjgpp*) + # Override, as these systems have only a dummy fork() stub + ac_cv_func_fork_works=no + ;; + *) + ac_cv_func_fork_works=yes + ;; + esac + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: result $ac_cv_func_fork_works guessed because of cross compilation" >&5 +$as_echo "$as_me: WARNING: result $ac_cv_func_fork_works guessed because of cross compilation" >&2;} +fi +ac_cv_func_vfork_works=$ac_cv_func_vfork +if test "x$ac_cv_func_vfork" = xyes; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for working vfork" >&5 +$as_echo_n "checking for working vfork... " >&6; } +if ${ac_cv_func_vfork_works+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test "$cross_compiling" = yes; then : + ac_cv_func_vfork_works=cross +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +/* Thanks to Paul Eggert for this test. */ +$ac_includes_default +#include +#ifdef HAVE_VFORK_H +# include +#endif +/* On some sparc systems, changes by the child to local and incoming + argument registers are propagated back to the parent. The compiler + is told about this with #include , but some compilers + (e.g. gcc -O) don't grok . Test for this by using a + static variable whose address is put into a register that is + clobbered by the vfork. */ +static void +#ifdef __cplusplus +sparc_address_test (int arg) +# else +sparc_address_test (arg) int arg; +#endif +{ + static pid_t child; + if (!child) { + child = vfork (); + if (child < 0) { + perror ("vfork"); + _exit(2); + } + if (!child) { + arg = getpid(); + write(-1, "", 0); + _exit (arg); + } + } +} + +int +main () +{ + pid_t parent = getpid (); + pid_t child; + + sparc_address_test (0); + + child = vfork (); + + if (child == 0) { + /* Here is another test for sparc vfork register problems. This + test uses lots of local variables, at least as many local + variables as main has allocated so far including compiler + temporaries. 4 locals are enough for gcc 1.40.3 on a Solaris + 4.1.3 sparc, but we use 8 to be safe. A buggy compiler should + reuse the register of parent for one of the local variables, + since it will think that parent can't possibly be used any more + in this routine. Assigning to the local variable will thus + munge parent in the parent process. */ + pid_t + p = getpid(), p1 = getpid(), p2 = getpid(), p3 = getpid(), + p4 = getpid(), p5 = getpid(), p6 = getpid(), p7 = getpid(); + /* Convince the compiler that p..p7 are live; otherwise, it might + use the same hardware register for all 8 local variables. */ + if (p != p1 || p != p2 || p != p3 || p != p4 + || p != p5 || p != p6 || p != p7) + _exit(1); + + /* On some systems (e.g. IRIX 3.3), vfork doesn't separate parent + from child file descriptors. If the child closes a descriptor + before it execs or exits, this munges the parent's descriptor + as well. Test for this by closing stdout in the child. */ + _exit(close(fileno(stdout)) != 0); + } else { + int status; + struct stat st; + + while (wait(&status) != child) + ; + return ( + /* Was there some problem with vforking? */ + child < 0 + + /* Did the child fail? (This shouldn't happen.) */ + || status + + /* Did the vfork/compiler bug occur? */ + || parent != getpid() + + /* Did the file descriptor bug occur? */ + || fstat(fileno(stdout), &st) != 0 + ); + } +} +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + ac_cv_func_vfork_works=yes +else + ac_cv_func_vfork_works=no +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_vfork_works" >&5 +$as_echo "$ac_cv_func_vfork_works" >&6; } + +fi; +if test "x$ac_cv_func_fork_works" = xcross; then + ac_cv_func_vfork_works=$ac_cv_func_vfork + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: result $ac_cv_func_vfork_works guessed because of cross compilation" >&5 +$as_echo "$as_me: WARNING: result $ac_cv_func_vfork_works guessed because of cross compilation" >&2;} +fi + +if test "x$ac_cv_func_vfork_works" = xyes; then + +$as_echo "#define HAVE_WORKING_VFORK 1" >>confdefs.h + +else + +$as_echo "#define vfork fork" >>confdefs.h + +fi +if test "x$ac_cv_func_fork_works" = xyes; then + +$as_echo "#define HAVE_WORKING_FORK 1" >>confdefs.h + +fi + +# Don't check realloc, since LeakSanitizer detects memory leak during check +# AC_FUNC_REALLOC +ac_fn_c_check_decl "$LINENO" "strerror_r" "ac_cv_have_decl_strerror_r" "$ac_includes_default" +if test "x$ac_cv_have_decl_strerror_r" = xyes; then : + ac_have_decl=1 +else + ac_have_decl=0 +fi + +cat >>confdefs.h <<_ACEOF +#define HAVE_DECL_STRERROR_R $ac_have_decl +_ACEOF + +for ac_func in strerror_r +do : + ac_fn_c_check_func "$LINENO" "strerror_r" "ac_cv_func_strerror_r" +if test "x$ac_cv_func_strerror_r" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_STRERROR_R 1 +_ACEOF + +fi +done + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether strerror_r returns char *" >&5 +$as_echo_n "checking whether strerror_r returns char *... " >&6; } +if ${ac_cv_func_strerror_r_char_p+:} false; then : + $as_echo_n "(cached) " >&6 +else + + ac_cv_func_strerror_r_char_p=no + if test $ac_cv_have_decl_strerror_r = yes; then + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ + + char buf[100]; + char x = *strerror_r (0, buf, sizeof buf); + char *p = strerror_r (0, buf, sizeof buf); + return !p || x; + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_func_strerror_r_char_p=yes +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + else + # strerror_r is not declared. Choose between + # systems that have relatively inaccessible declarations for the + # function. BeOS and DEC UNIX 4.0 fall in this category, but the + # former has a strerror_r that returns char*, while the latter + # has a strerror_r that returns `int'. + # This test should segfault on the DEC system. + if test "$cross_compiling" = yes; then : + : +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$ac_includes_default + extern char *strerror_r (); +int +main () +{ +char buf[100]; + char x = *strerror_r (0, buf, sizeof buf); + return ! isalpha (x); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + ac_cv_func_strerror_r_char_p=yes +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +fi + + fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_strerror_r_char_p" >&5 +$as_echo "$ac_cv_func_strerror_r_char_p" >&6; } +if test $ac_cv_func_strerror_r_char_p = yes; then + +$as_echo "#define STRERROR_R_CHAR_P 1" >>confdefs.h + +fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for working strnlen" >&5 +$as_echo_n "checking for working strnlen... " >&6; } +if ${ac_cv_func_strnlen_working+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test "$cross_compiling" = yes; then : + # Guess no on AIX systems, yes otherwise. + case "$host_os" in + aix*) ac_cv_func_strnlen_working=no;; + *) ac_cv_func_strnlen_working=yes;; + esac +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ + +#define S "foobar" +#define S_LEN (sizeof S - 1) + + /* At least one implementation is buggy: that of AIX 4.3 would + give strnlen (S, 1) == 3. */ + + int i; + for (i = 0; i < S_LEN + 1; ++i) + { + int expected = i <= S_LEN ? i : S_LEN; + if (strnlen (S, i) != expected) + return 1; + } + return 0; + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + ac_cv_func_strnlen_working=yes +else + ac_cv_func_strnlen_working=no +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_strnlen_working" >&5 +$as_echo "$ac_cv_func_strnlen_working" >&6; } +test $ac_cv_func_strnlen_working = no && case " $LIBOBJS " in + *" strnlen.$ac_objext "* ) ;; + *) LIBOBJS="$LIBOBJS strnlen.$ac_objext" + ;; +esac + + + +for ac_func in \ + _Exit \ + accept4 \ + dup2 \ + getcwd \ + getpwnam \ + localtime_r \ + memchr \ + memmove \ + memset \ + socket \ + sqrt \ + strchr \ + strdup \ + strerror \ + strndup \ + strstr \ + strtol \ + strtoul \ + timegm \ + +do : + as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` +ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" +if eval test \"x\$"$as_ac_var"\" = x"yes"; then : + cat >>confdefs.h <<_ACEOF +#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + +fi +done + + +# timerfd_create was added in linux kernel 2.6.25 + +ac_fn_c_check_func "$LINENO" "timerfd_create" "ac_cv_func_timerfd_create" +if test "x$ac_cv_func_timerfd_create" = xyes; then : + have_timerfd_create=yes +else + have_timerfd_create=no +fi + + + +# Checks for epoll availability, primarily for examples/tiny-nghttpd + ax_have_epoll_cppflags="${CPPFLAGS}" + ac_fn_c_check_header_mongrel "$LINENO" "linux/version.h" "ac_cv_header_linux_version_h" "$ac_includes_default" +if test "x$ac_cv_header_linux_version_h" = xyes; then : + CPPFLAGS="${CPPFLAGS} -DHAVE_LINUX_VERSION_H" +fi + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for Linux epoll(7) interface" >&5 +$as_echo_n "checking for Linux epoll(7) interface... " >&6; } + if ${ax_cv_have_epoll+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + #include +#ifdef HAVE_LINUX_VERSION_H +# include +# if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,45) +# error linux kernel version is too old to have epoll +# endif +#endif + +int +main () +{ +int fd, rc; +struct epoll_event ev; +fd = epoll_create(128); +rc = epoll_wait(fd, &ev, 1, 0); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ax_cv_have_epoll=yes +else + ax_cv_have_epoll=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi + + CPPFLAGS="${ax_have_epoll_cppflags}" + if test "${ax_cv_have_epoll}" = "yes"; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } +have_epoll=yes +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +have_epoll=no +fi + + + if test "x${have_epoll}" = "xyes" && + test "x${have_timerfd_create}" = "xyes"; then + ENABLE_TINY_NGHTTPD_TRUE= + ENABLE_TINY_NGHTTPD_FALSE='#' +else + ENABLE_TINY_NGHTTPD_TRUE='#' + ENABLE_TINY_NGHTTPD_FALSE= +fi + + +ac_save_CFLAGS=$CFLAGS +CFLAGS= + +if test "x$werror" != "xno"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -Wall" >&5 +$as_echo_n "checking whether C compiler accepts -Wall... " >&6; } +if ${ax_cv_check_cflags___Wall+:} false; then : + $as_echo_n "(cached) " >&6 +else + + ax_check_save_flags=$CFLAGS + CFLAGS="$CFLAGS -Wall" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ax_cv_check_cflags___Wall=yes +else + ax_cv_check_cflags___Wall=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + CFLAGS=$ax_check_save_flags +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags___Wall" >&5 +$as_echo "$ax_cv_check_cflags___Wall" >&6; } +if test x"$ax_cv_check_cflags___Wall" = xyes; then : + CFLAGS="$CFLAGS -Wall" +else + : +fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -Wextra" >&5 +$as_echo_n "checking whether C compiler accepts -Wextra... " >&6; } +if ${ax_cv_check_cflags___Wextra+:} false; then : + $as_echo_n "(cached) " >&6 +else + + ax_check_save_flags=$CFLAGS + CFLAGS="$CFLAGS -Wextra" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ax_cv_check_cflags___Wextra=yes +else + ax_cv_check_cflags___Wextra=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + CFLAGS=$ax_check_save_flags +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags___Wextra" >&5 +$as_echo "$ax_cv_check_cflags___Wextra" >&6; } +if test x"$ax_cv_check_cflags___Wextra" = xyes; then : + CFLAGS="$CFLAGS -Wextra" +else + : +fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -Werror" >&5 +$as_echo_n "checking whether C compiler accepts -Werror... " >&6; } +if ${ax_cv_check_cflags___Werror+:} false; then : + $as_echo_n "(cached) " >&6 +else + + ax_check_save_flags=$CFLAGS + CFLAGS="$CFLAGS -Werror" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ax_cv_check_cflags___Werror=yes +else + ax_cv_check_cflags___Werror=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + CFLAGS=$ax_check_save_flags +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags___Werror" >&5 +$as_echo "$ax_cv_check_cflags___Werror" >&6; } +if test x"$ax_cv_check_cflags___Werror" = xyes; then : + CFLAGS="$CFLAGS -Werror" +else + : +fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -Wmissing-prototypes" >&5 +$as_echo_n "checking whether C compiler accepts -Wmissing-prototypes... " >&6; } +if ${ax_cv_check_cflags___Wmissing_prototypes+:} false; then : + $as_echo_n "(cached) " >&6 +else + + ax_check_save_flags=$CFLAGS + CFLAGS="$CFLAGS -Wmissing-prototypes" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ax_cv_check_cflags___Wmissing_prototypes=yes +else + ax_cv_check_cflags___Wmissing_prototypes=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + CFLAGS=$ax_check_save_flags +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags___Wmissing_prototypes" >&5 +$as_echo "$ax_cv_check_cflags___Wmissing_prototypes" >&6; } +if test x"$ax_cv_check_cflags___Wmissing_prototypes" = xyes; then : + CFLAGS="$CFLAGS -Wmissing-prototypes" +else + : +fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -Wstrict-prototypes" >&5 +$as_echo_n "checking whether C compiler accepts -Wstrict-prototypes... " >&6; } +if ${ax_cv_check_cflags___Wstrict_prototypes+:} false; then : + $as_echo_n "(cached) " >&6 +else + + ax_check_save_flags=$CFLAGS + CFLAGS="$CFLAGS -Wstrict-prototypes" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ax_cv_check_cflags___Wstrict_prototypes=yes +else + ax_cv_check_cflags___Wstrict_prototypes=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + CFLAGS=$ax_check_save_flags +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags___Wstrict_prototypes" >&5 +$as_echo "$ax_cv_check_cflags___Wstrict_prototypes" >&6; } +if test x"$ax_cv_check_cflags___Wstrict_prototypes" = xyes; then : + CFLAGS="$CFLAGS -Wstrict-prototypes" +else + : +fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -Wmissing-declarations" >&5 +$as_echo_n "checking whether C compiler accepts -Wmissing-declarations... " >&6; } +if ${ax_cv_check_cflags___Wmissing_declarations+:} false; then : + $as_echo_n "(cached) " >&6 +else + + ax_check_save_flags=$CFLAGS + CFLAGS="$CFLAGS -Wmissing-declarations" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ax_cv_check_cflags___Wmissing_declarations=yes +else + ax_cv_check_cflags___Wmissing_declarations=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + CFLAGS=$ax_check_save_flags +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags___Wmissing_declarations" >&5 +$as_echo "$ax_cv_check_cflags___Wmissing_declarations" >&6; } +if test x"$ax_cv_check_cflags___Wmissing_declarations" = xyes; then : + CFLAGS="$CFLAGS -Wmissing-declarations" +else + : +fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -Wpointer-arith" >&5 +$as_echo_n "checking whether C compiler accepts -Wpointer-arith... " >&6; } +if ${ax_cv_check_cflags___Wpointer_arith+:} false; then : + $as_echo_n "(cached) " >&6 +else + + ax_check_save_flags=$CFLAGS + CFLAGS="$CFLAGS -Wpointer-arith" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ax_cv_check_cflags___Wpointer_arith=yes +else + ax_cv_check_cflags___Wpointer_arith=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + CFLAGS=$ax_check_save_flags +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags___Wpointer_arith" >&5 +$as_echo "$ax_cv_check_cflags___Wpointer_arith" >&6; } +if test x"$ax_cv_check_cflags___Wpointer_arith" = xyes; then : + CFLAGS="$CFLAGS -Wpointer-arith" +else + : +fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -Wdeclaration-after-statement" >&5 +$as_echo_n "checking whether C compiler accepts -Wdeclaration-after-statement... " >&6; } +if ${ax_cv_check_cflags___Wdeclaration_after_statement+:} false; then : + $as_echo_n "(cached) " >&6 +else + + ax_check_save_flags=$CFLAGS + CFLAGS="$CFLAGS -Wdeclaration-after-statement" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ax_cv_check_cflags___Wdeclaration_after_statement=yes +else + ax_cv_check_cflags___Wdeclaration_after_statement=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + CFLAGS=$ax_check_save_flags +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags___Wdeclaration_after_statement" >&5 +$as_echo "$ax_cv_check_cflags___Wdeclaration_after_statement" >&6; } +if test x"$ax_cv_check_cflags___Wdeclaration_after_statement" = xyes; then : + CFLAGS="$CFLAGS -Wdeclaration-after-statement" +else + : +fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -Wformat-security" >&5 +$as_echo_n "checking whether C compiler accepts -Wformat-security... " >&6; } +if ${ax_cv_check_cflags___Wformat_security+:} false; then : + $as_echo_n "(cached) " >&6 +else + + ax_check_save_flags=$CFLAGS + CFLAGS="$CFLAGS -Wformat-security" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ax_cv_check_cflags___Wformat_security=yes +else + ax_cv_check_cflags___Wformat_security=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + CFLAGS=$ax_check_save_flags +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags___Wformat_security" >&5 +$as_echo "$ax_cv_check_cflags___Wformat_security" >&6; } +if test x"$ax_cv_check_cflags___Wformat_security" = xyes; then : + CFLAGS="$CFLAGS -Wformat-security" +else + : +fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -Wwrite-strings" >&5 +$as_echo_n "checking whether C compiler accepts -Wwrite-strings... " >&6; } +if ${ax_cv_check_cflags___Wwrite_strings+:} false; then : + $as_echo_n "(cached) " >&6 +else + + ax_check_save_flags=$CFLAGS + CFLAGS="$CFLAGS -Wwrite-strings" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ax_cv_check_cflags___Wwrite_strings=yes +else + ax_cv_check_cflags___Wwrite_strings=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + CFLAGS=$ax_check_save_flags +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags___Wwrite_strings" >&5 +$as_echo "$ax_cv_check_cflags___Wwrite_strings" >&6; } +if test x"$ax_cv_check_cflags___Wwrite_strings" = xyes; then : + CFLAGS="$CFLAGS -Wwrite-strings" +else + : +fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -Wshadow" >&5 +$as_echo_n "checking whether C compiler accepts -Wshadow... " >&6; } +if ${ax_cv_check_cflags___Wshadow+:} false; then : + $as_echo_n "(cached) " >&6 +else + + ax_check_save_flags=$CFLAGS + CFLAGS="$CFLAGS -Wshadow" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ax_cv_check_cflags___Wshadow=yes +else + ax_cv_check_cflags___Wshadow=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + CFLAGS=$ax_check_save_flags +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags___Wshadow" >&5 +$as_echo "$ax_cv_check_cflags___Wshadow" >&6; } +if test x"$ax_cv_check_cflags___Wshadow" = xyes; then : + CFLAGS="$CFLAGS -Wshadow" +else + : +fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -Winline" >&5 +$as_echo_n "checking whether C compiler accepts -Winline... " >&6; } +if ${ax_cv_check_cflags___Winline+:} false; then : + $as_echo_n "(cached) " >&6 +else + + ax_check_save_flags=$CFLAGS + CFLAGS="$CFLAGS -Winline" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ax_cv_check_cflags___Winline=yes +else + ax_cv_check_cflags___Winline=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + CFLAGS=$ax_check_save_flags +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags___Winline" >&5 +$as_echo "$ax_cv_check_cflags___Winline" >&6; } +if test x"$ax_cv_check_cflags___Winline" = xyes; then : + CFLAGS="$CFLAGS -Winline" +else + : +fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -Wnested-externs" >&5 +$as_echo_n "checking whether C compiler accepts -Wnested-externs... " >&6; } +if ${ax_cv_check_cflags___Wnested_externs+:} false; then : + $as_echo_n "(cached) " >&6 +else + + ax_check_save_flags=$CFLAGS + CFLAGS="$CFLAGS -Wnested-externs" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ax_cv_check_cflags___Wnested_externs=yes +else + ax_cv_check_cflags___Wnested_externs=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + CFLAGS=$ax_check_save_flags +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags___Wnested_externs" >&5 +$as_echo "$ax_cv_check_cflags___Wnested_externs" >&6; } +if test x"$ax_cv_check_cflags___Wnested_externs" = xyes; then : + CFLAGS="$CFLAGS -Wnested-externs" +else + : +fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -Wfloat-equal" >&5 +$as_echo_n "checking whether C compiler accepts -Wfloat-equal... " >&6; } +if ${ax_cv_check_cflags___Wfloat_equal+:} false; then : + $as_echo_n "(cached) " >&6 +else + + ax_check_save_flags=$CFLAGS + CFLAGS="$CFLAGS -Wfloat-equal" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ax_cv_check_cflags___Wfloat_equal=yes +else + ax_cv_check_cflags___Wfloat_equal=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + CFLAGS=$ax_check_save_flags +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags___Wfloat_equal" >&5 +$as_echo "$ax_cv_check_cflags___Wfloat_equal" >&6; } +if test x"$ax_cv_check_cflags___Wfloat_equal" = xyes; then : + CFLAGS="$CFLAGS -Wfloat-equal" +else + : +fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -Wundef" >&5 +$as_echo_n "checking whether C compiler accepts -Wundef... " >&6; } +if ${ax_cv_check_cflags___Wundef+:} false; then : + $as_echo_n "(cached) " >&6 +else + + ax_check_save_flags=$CFLAGS + CFLAGS="$CFLAGS -Wundef" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ax_cv_check_cflags___Wundef=yes +else + ax_cv_check_cflags___Wundef=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + CFLAGS=$ax_check_save_flags +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags___Wundef" >&5 +$as_echo "$ax_cv_check_cflags___Wundef" >&6; } +if test x"$ax_cv_check_cflags___Wundef" = xyes; then : + CFLAGS="$CFLAGS -Wundef" +else + : +fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -Wendif-labels" >&5 +$as_echo_n "checking whether C compiler accepts -Wendif-labels... " >&6; } +if ${ax_cv_check_cflags___Wendif_labels+:} false; then : + $as_echo_n "(cached) " >&6 +else + + ax_check_save_flags=$CFLAGS + CFLAGS="$CFLAGS -Wendif-labels" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ax_cv_check_cflags___Wendif_labels=yes +else + ax_cv_check_cflags___Wendif_labels=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + CFLAGS=$ax_check_save_flags +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags___Wendif_labels" >&5 +$as_echo "$ax_cv_check_cflags___Wendif_labels" >&6; } +if test x"$ax_cv_check_cflags___Wendif_labels" = xyes; then : + CFLAGS="$CFLAGS -Wendif-labels" +else + : +fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -Wempty-body" >&5 +$as_echo_n "checking whether C compiler accepts -Wempty-body... " >&6; } +if ${ax_cv_check_cflags___Wempty_body+:} false; then : + $as_echo_n "(cached) " >&6 +else + + ax_check_save_flags=$CFLAGS + CFLAGS="$CFLAGS -Wempty-body" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ax_cv_check_cflags___Wempty_body=yes +else + ax_cv_check_cflags___Wempty_body=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + CFLAGS=$ax_check_save_flags +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags___Wempty_body" >&5 +$as_echo "$ax_cv_check_cflags___Wempty_body" >&6; } +if test x"$ax_cv_check_cflags___Wempty_body" = xyes; then : + CFLAGS="$CFLAGS -Wempty-body" +else + : +fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -Wcast-align" >&5 +$as_echo_n "checking whether C compiler accepts -Wcast-align... " >&6; } +if ${ax_cv_check_cflags___Wcast_align+:} false; then : + $as_echo_n "(cached) " >&6 +else + + ax_check_save_flags=$CFLAGS + CFLAGS="$CFLAGS -Wcast-align" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ax_cv_check_cflags___Wcast_align=yes +else + ax_cv_check_cflags___Wcast_align=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + CFLAGS=$ax_check_save_flags +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags___Wcast_align" >&5 +$as_echo "$ax_cv_check_cflags___Wcast_align" >&6; } +if test x"$ax_cv_check_cflags___Wcast_align" = xyes; then : + CFLAGS="$CFLAGS -Wcast-align" +else + : +fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -Wclobbered" >&5 +$as_echo_n "checking whether C compiler accepts -Wclobbered... " >&6; } +if ${ax_cv_check_cflags___Wclobbered+:} false; then : + $as_echo_n "(cached) " >&6 +else + + ax_check_save_flags=$CFLAGS + CFLAGS="$CFLAGS -Wclobbered" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ax_cv_check_cflags___Wclobbered=yes +else + ax_cv_check_cflags___Wclobbered=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + CFLAGS=$ax_check_save_flags +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags___Wclobbered" >&5 +$as_echo "$ax_cv_check_cflags___Wclobbered" >&6; } +if test x"$ax_cv_check_cflags___Wclobbered" = xyes; then : + CFLAGS="$CFLAGS -Wclobbered" +else + : +fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -Wvla" >&5 +$as_echo_n "checking whether C compiler accepts -Wvla... " >&6; } +if ${ax_cv_check_cflags___Wvla+:} false; then : + $as_echo_n "(cached) " >&6 +else + + ax_check_save_flags=$CFLAGS + CFLAGS="$CFLAGS -Wvla" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ax_cv_check_cflags___Wvla=yes +else + ax_cv_check_cflags___Wvla=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + CFLAGS=$ax_check_save_flags +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags___Wvla" >&5 +$as_echo "$ax_cv_check_cflags___Wvla" >&6; } +if test x"$ax_cv_check_cflags___Wvla" = xyes; then : + CFLAGS="$CFLAGS -Wvla" +else + : +fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -Wpragmas" >&5 +$as_echo_n "checking whether C compiler accepts -Wpragmas... " >&6; } +if ${ax_cv_check_cflags___Wpragmas+:} false; then : + $as_echo_n "(cached) " >&6 +else + + ax_check_save_flags=$CFLAGS + CFLAGS="$CFLAGS -Wpragmas" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ax_cv_check_cflags___Wpragmas=yes +else + ax_cv_check_cflags___Wpragmas=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + CFLAGS=$ax_check_save_flags +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags___Wpragmas" >&5 +$as_echo "$ax_cv_check_cflags___Wpragmas" >&6; } +if test x"$ax_cv_check_cflags___Wpragmas" = xyes; then : + CFLAGS="$CFLAGS -Wpragmas" +else + : +fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -Wunreachable-code" >&5 +$as_echo_n "checking whether C compiler accepts -Wunreachable-code... " >&6; } +if ${ax_cv_check_cflags___Wunreachable_code+:} false; then : + $as_echo_n "(cached) " >&6 +else + + ax_check_save_flags=$CFLAGS + CFLAGS="$CFLAGS -Wunreachable-code" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ax_cv_check_cflags___Wunreachable_code=yes +else + ax_cv_check_cflags___Wunreachable_code=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + CFLAGS=$ax_check_save_flags +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags___Wunreachable_code" >&5 +$as_echo "$ax_cv_check_cflags___Wunreachable_code" >&6; } +if test x"$ax_cv_check_cflags___Wunreachable_code" = xyes; then : + CFLAGS="$CFLAGS -Wunreachable-code" +else + : +fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -Waddress" >&5 +$as_echo_n "checking whether C compiler accepts -Waddress... " >&6; } +if ${ax_cv_check_cflags___Waddress+:} false; then : + $as_echo_n "(cached) " >&6 +else + + ax_check_save_flags=$CFLAGS + CFLAGS="$CFLAGS -Waddress" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ax_cv_check_cflags___Waddress=yes +else + ax_cv_check_cflags___Waddress=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + CFLAGS=$ax_check_save_flags +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags___Waddress" >&5 +$as_echo "$ax_cv_check_cflags___Waddress" >&6; } +if test x"$ax_cv_check_cflags___Waddress" = xyes; then : + CFLAGS="$CFLAGS -Waddress" +else + : +fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -Wattributes" >&5 +$as_echo_n "checking whether C compiler accepts -Wattributes... " >&6; } +if ${ax_cv_check_cflags___Wattributes+:} false; then : + $as_echo_n "(cached) " >&6 +else + + ax_check_save_flags=$CFLAGS + CFLAGS="$CFLAGS -Wattributes" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ax_cv_check_cflags___Wattributes=yes +else + ax_cv_check_cflags___Wattributes=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + CFLAGS=$ax_check_save_flags +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags___Wattributes" >&5 +$as_echo "$ax_cv_check_cflags___Wattributes" >&6; } +if test x"$ax_cv_check_cflags___Wattributes" = xyes; then : + CFLAGS="$CFLAGS -Wattributes" +else + : +fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -Wdiv-by-zero" >&5 +$as_echo_n "checking whether C compiler accepts -Wdiv-by-zero... " >&6; } +if ${ax_cv_check_cflags___Wdiv_by_zero+:} false; then : + $as_echo_n "(cached) " >&6 +else + + ax_check_save_flags=$CFLAGS + CFLAGS="$CFLAGS -Wdiv-by-zero" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ax_cv_check_cflags___Wdiv_by_zero=yes +else + ax_cv_check_cflags___Wdiv_by_zero=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + CFLAGS=$ax_check_save_flags +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags___Wdiv_by_zero" >&5 +$as_echo "$ax_cv_check_cflags___Wdiv_by_zero" >&6; } +if test x"$ax_cv_check_cflags___Wdiv_by_zero" = xyes; then : + CFLAGS="$CFLAGS -Wdiv-by-zero" +else + : +fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -Wshorten-64-to-32" >&5 +$as_echo_n "checking whether C compiler accepts -Wshorten-64-to-32... " >&6; } +if ${ax_cv_check_cflags___Wshorten_64_to_32+:} false; then : + $as_echo_n "(cached) " >&6 +else + + ax_check_save_flags=$CFLAGS + CFLAGS="$CFLAGS -Wshorten-64-to-32" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ax_cv_check_cflags___Wshorten_64_to_32=yes +else + ax_cv_check_cflags___Wshorten_64_to_32=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + CFLAGS=$ax_check_save_flags +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags___Wshorten_64_to_32" >&5 +$as_echo "$ax_cv_check_cflags___Wshorten_64_to_32" >&6; } +if test x"$ax_cv_check_cflags___Wshorten_64_to_32" = xyes; then : + CFLAGS="$CFLAGS -Wshorten-64-to-32" +else + : +fi + + + # Only work with Clang for the moment + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -Wheader-guard" >&5 +$as_echo_n "checking whether C compiler accepts -Wheader-guard... " >&6; } +if ${ax_cv_check_cflags___Wheader_guard+:} false; then : + $as_echo_n "(cached) " >&6 +else + + ax_check_save_flags=$CFLAGS + CFLAGS="$CFLAGS -Wheader-guard" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ax_cv_check_cflags___Wheader_guard=yes +else + ax_cv_check_cflags___Wheader_guard=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + CFLAGS=$ax_check_save_flags +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags___Wheader_guard" >&5 +$as_echo "$ax_cv_check_cflags___Wheader_guard" >&6; } +if test x"$ax_cv_check_cflags___Wheader_guard" = xyes; then : + CFLAGS="$CFLAGS -Wheader-guard" +else + : +fi + +fi + +WARNCFLAGS=$CFLAGS +CFLAGS=$ac_save_CFLAGS + + + +if test "x$debug" != "xno"; then + +$as_echo "#define DEBUGBUILD 1" >>confdefs.h + +fi + +enable_threads=yes +# Some platform does not have working std::future. We disable +# threading for those platforms. +if test "x$threads" != "xyes" || + test "x$have_std_future" != "xyes"; then + enable_threads=no + +$as_echo "#define NOTHREADS 1" >>confdefs.h + +fi + + + + +ac_config_files="$ac_config_files Makefile lib/Makefile lib/libnghttp2.pc lib/includes/Makefile lib/includes/nghttp2/nghttp2ver.h tests/Makefile tests/testdata/Makefile third-party/Makefile src/Makefile src/includes/Makefile src/libnghttp2_asio.pc examples/Makefile python/Makefile python/setup.py integration-tests/Makefile integration-tests/config.go integration-tests/setenv doc/Makefile doc/conf.py doc/index.rst doc/package_README.rst doc/tutorial-client.rst doc/tutorial-server.rst doc/tutorial-hpack.rst doc/nghttpx-howto.rst doc/h2load-howto.rst doc/libnghttp2_asio.rst doc/python-apiref.rst doc/building-android-binary.rst doc/nghttp2.h.rst doc/nghttp2ver.h.rst doc/asio_http2.h.rst doc/asio_http2_server.h.rst doc/asio_http2_client.h.rst doc/contribute.rst contrib/Makefile" + +cat >confcache <<\_ACEOF +# This file is a shell script that caches the results of configure +# tests run on this system so they can be shared between configure +# scripts and configure runs, see configure's option --config-cache. +# It is not useful on other systems. If it contains results you don't +# want to keep, you may remove or edit it. +# +# config.status only pays attention to the cache file if you give it +# the --recheck option to rerun configure. +# +# `ac_cv_env_foo' variables (set or unset) will be overridden when +# loading this file, other *unset* `ac_cv_foo' will be assigned the +# following values. + +_ACEOF + +# The following way of writing the cache mishandles newlines in values, +# but we know of no workaround that is simple, portable, and efficient. +# So, we kill variables containing newlines. +# Ultrix sh set writes to stderr and can't be redirected directly, +# and sets the high bit in the cache file unless we assign to the vars. +( + for ac_var in `(set) 2>&1 | sed -n 's/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'`; do + eval ac_val=\$$ac_var + case $ac_val in #( + *${as_nl}*) + case $ac_var in #( + *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 +$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; + esac + case $ac_var in #( + _ | IFS | as_nl) ;; #( + BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( + *) { eval $ac_var=; unset $ac_var;} ;; + esac ;; + esac + done + + (set) 2>&1 | + case $as_nl`(ac_space=' '; set) 2>&1` in #( + *${as_nl}ac_space=\ *) + # `set' does not quote correctly, so add quotes: double-quote + # substitution turns \\\\ into \\, and sed turns \\ into \. + sed -n \ + "s/'/'\\\\''/g; + s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p" + ;; #( + *) + # `set' quotes correctly as required by POSIX, so do not add quotes. + sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" + ;; + esac | + sort +) | + sed ' + /^ac_cv_env_/b end + t clear + :clear + s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/ + t end + s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/ + :end' >>confcache +if diff "$cache_file" confcache >/dev/null 2>&1; then :; else + if test -w "$cache_file"; then + if test "x$cache_file" != "x/dev/null"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: updating cache $cache_file" >&5 +$as_echo "$as_me: updating cache $cache_file" >&6;} + if test ! -f "$cache_file" || test -h "$cache_file"; then + cat confcache >"$cache_file" + else + case $cache_file in #( + */* | ?:*) + mv -f confcache "$cache_file"$$ && + mv -f "$cache_file"$$ "$cache_file" ;; #( + *) + mv -f confcache "$cache_file" ;; + esac + fi + fi + else + { $as_echo "$as_me:${as_lineno-$LINENO}: not updating unwritable cache $cache_file" >&5 +$as_echo "$as_me: not updating unwritable cache $cache_file" >&6;} + fi +fi +rm -f confcache + +test "x$prefix" = xNONE && prefix=$ac_default_prefix +# Let make expand exec_prefix. +test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' + +DEFS=-DHAVE_CONFIG_H + +ac_libobjs= +ac_ltlibobjs= +U= +for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue + # 1. Remove the extension, and $U if already installed. + ac_script='s/\$U\././;s/\.o$//;s/\.obj$//' + ac_i=`$as_echo "$ac_i" | sed "$ac_script"` + # 2. Prepend LIBOBJDIR. When used with automake>=1.10 LIBOBJDIR + # will be set to the directory where LIBOBJS objects are built. + as_fn_append ac_libobjs " \${LIBOBJDIR}$ac_i\$U.$ac_objext" + as_fn_append ac_ltlibobjs " \${LIBOBJDIR}$ac_i"'$U.lo' +done +LIBOBJS=$ac_libobjs + +LTLIBOBJS=$ac_ltlibobjs + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking that generated files are newer than configure" >&5 +$as_echo_n "checking that generated files are newer than configure... " >&6; } + if test -n "$am_sleep_pid"; then + # Hide warnings about reused PIDs. + wait $am_sleep_pid 2>/dev/null + fi + { $as_echo "$as_me:${as_lineno-$LINENO}: result: done" >&5 +$as_echo "done" >&6; } +if test -z "${AMDEP_TRUE}" && test -z "${AMDEP_FALSE}"; then + as_fn_error $? "conditional \"AMDEP\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${am__fastdepCC_TRUE}" && test -z "${am__fastdepCC_FALSE}"; then + as_fn_error $? "conditional \"am__fastdepCC\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi + if test -n "$EXEEXT"; then + am__EXEEXT_TRUE= + am__EXEEXT_FALSE='#' +else + am__EXEEXT_TRUE='#' + am__EXEEXT_FALSE= +fi + +if test -z "${am__fastdepCXX_TRUE}" && test -z "${am__fastdepCXX_FALSE}"; then + as_fn_error $? "conditional \"am__fastdepCXX\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${HAVE_CUNIT_TRUE}" && test -z "${HAVE_CUNIT_FALSE}"; then + as_fn_error $? "conditional \"HAVE_CUNIT\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${HAVE_LIBXML2_TRUE}" && test -z "${HAVE_LIBXML2_FALSE}"; then + as_fn_error $? "conditional \"HAVE_LIBXML2\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${HAVE_SPDYLAY_TRUE}" && test -z "${HAVE_SPDYLAY_FALSE}"; then + as_fn_error $? "conditional \"HAVE_SPDYLAY\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${ENABLE_APP_TRUE}" && test -z "${ENABLE_APP_FALSE}"; then + as_fn_error $? "conditional \"ENABLE_APP\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${ENABLE_HPACK_TOOLS_TRUE}" && test -z "${ENABLE_HPACK_TOOLS_FALSE}"; then + as_fn_error $? "conditional \"ENABLE_HPACK_TOOLS\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${ENABLE_ASIO_LIB_TRUE}" && test -z "${ENABLE_ASIO_LIB_FALSE}"; then + as_fn_error $? "conditional \"ENABLE_ASIO_LIB\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${ENABLE_EXAMPLES_TRUE}" && test -z "${ENABLE_EXAMPLES_FALSE}"; then + as_fn_error $? "conditional \"ENABLE_EXAMPLES\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${ENABLE_PYTHON_BINDINGS_TRUE}" && test -z "${ENABLE_PYTHON_BINDINGS_FALSE}"; then + as_fn_error $? "conditional \"ENABLE_PYTHON_BINDINGS\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${HAVE_CYTHON_TRUE}" && test -z "${HAVE_CYTHON_FALSE}"; then + as_fn_error $? "conditional \"HAVE_CYTHON\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${ENABLE_FAILMALLOC_TRUE}" && test -z "${ENABLE_FAILMALLOC_FALSE}"; then + as_fn_error $? "conditional \"ENABLE_FAILMALLOC\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi + +if test -z "${ENABLE_TINY_NGHTTPD_TRUE}" && test -z "${ENABLE_TINY_NGHTTPD_FALSE}"; then + as_fn_error $? "conditional \"ENABLE_TINY_NGHTTPD\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi + +: "${CONFIG_STATUS=./config.status}" +ac_write_fail=0 +ac_clean_files_save=$ac_clean_files +ac_clean_files="$ac_clean_files $CONFIG_STATUS" +{ $as_echo "$as_me:${as_lineno-$LINENO}: creating $CONFIG_STATUS" >&5 +$as_echo "$as_me: creating $CONFIG_STATUS" >&6;} +as_write_fail=0 +cat >$CONFIG_STATUS <<_ASEOF || as_write_fail=1 +#! $SHELL +# Generated by $as_me. +# Run this file to recreate the current configuration. +# Compiler output produced by configure, useful for debugging +# configure, is in config.log if it exists. + +debug=false +ac_cs_recheck=false +ac_cs_silent=false + +SHELL=\${CONFIG_SHELL-$SHELL} +export SHELL +_ASEOF +cat >>$CONFIG_STATUS <<\_ASEOF || as_write_fail=1 +## -------------------- ## +## M4sh Initialization. ## +## -------------------- ## + +# Be more Bourne compatible +DUALCASE=1; export DUALCASE # for MKS sh +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : + emulate sh + NULLCMD=: + # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' + setopt NO_GLOB_SUBST +else + case `(set -o) 2>/dev/null` in #( + *posix*) : + set -o posix ;; #( + *) : + ;; +esac +fi + + +as_nl=' +' +export as_nl +# Printing a long string crashes Solaris 7 /usr/bin/printf. +as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' +as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo +as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo +# Prefer a ksh shell builtin over an external printf program on Solaris, +# but without wasting forks for bash or zsh. +if test -z "$BASH_VERSION$ZSH_VERSION" \ + && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then + as_echo='print -r --' + as_echo_n='print -rn --' +elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then + as_echo='printf %s\n' + as_echo_n='printf %s' +else + if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then + as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' + as_echo_n='/usr/ucb/echo -n' + else + as_echo_body='eval expr "X$1" : "X\\(.*\\)"' + as_echo_n_body='eval + arg=$1; + case $arg in #( + *"$as_nl"*) + expr "X$arg" : "X\\(.*\\)$as_nl"; + arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; + esac; + expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" + ' + export as_echo_n_body + as_echo_n='sh -c $as_echo_n_body as_echo' + fi + export as_echo_body + as_echo='sh -c $as_echo_body as_echo' +fi + +# The user is always right. +if test "${PATH_SEPARATOR+set}" != set; then + PATH_SEPARATOR=: + (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { + (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || + PATH_SEPARATOR=';' + } +fi + + +# IFS +# We need space, tab and new line, in precisely that order. Quoting is +# there to prevent editors from complaining about space-tab. +# (If _AS_PATH_WALK were called with IFS unset, it would disable word +# splitting by setting IFS to empty value.) +IFS=" "" $as_nl" + +# Find who we are. Look in the path if we contain no directory separator. +as_myself= +case $0 in #(( + *[\\/]* ) as_myself=$0 ;; + *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break + done +IFS=$as_save_IFS + + ;; +esac +# We did not find ourselves, most probably we were run as `sh COMMAND' +# in which case we are not to be found in the path. +if test "x$as_myself" = x; then + as_myself=$0 +fi +if test ! -f "$as_myself"; then + $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 + exit 1 +fi + +# Unset variables that we do not need and which cause bugs (e.g. in +# pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1" +# suppresses any "Segmentation fault" message there. '((' could +# trigger a bug in pdksh 5.2.14. +for as_var in BASH_ENV ENV MAIL MAILPATH +do eval test x\${$as_var+set} = xset \ + && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : +done +PS1='$ ' +PS2='> ' +PS4='+ ' + +# NLS nuisances. +LC_ALL=C +export LC_ALL +LANGUAGE=C +export LANGUAGE + +# CDPATH. +(unset CDPATH) >/dev/null 2>&1 && unset CDPATH + + +# as_fn_error STATUS ERROR [LINENO LOG_FD] +# ---------------------------------------- +# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are +# provided, also output the error to LOG_FD, referencing LINENO. Then exit the +# script with STATUS, using 1 if that was 0. +as_fn_error () +{ + as_status=$1; test $as_status -eq 0 && as_status=1 + if test "$4"; then + as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 + fi + $as_echo "$as_me: error: $2" >&2 + as_fn_exit $as_status +} # as_fn_error + + +# as_fn_set_status STATUS +# ----------------------- +# Set $? to STATUS, without forking. +as_fn_set_status () +{ + return $1 +} # as_fn_set_status + +# as_fn_exit STATUS +# ----------------- +# Exit the shell with STATUS, even in a "trap 0" or "set -e" context. +as_fn_exit () +{ + set +e + as_fn_set_status $1 + exit $1 +} # as_fn_exit + +# as_fn_unset VAR +# --------------- +# Portably unset VAR. +as_fn_unset () +{ + { eval $1=; unset $1;} +} +as_unset=as_fn_unset +# as_fn_append VAR VALUE +# ---------------------- +# Append the text in VALUE to the end of the definition contained in VAR. Take +# advantage of any shell optimizations that allow amortized linear growth over +# repeated appends, instead of the typical quadratic growth present in naive +# implementations. +if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then : + eval 'as_fn_append () + { + eval $1+=\$2 + }' +else + as_fn_append () + { + eval $1=\$$1\$2 + } +fi # as_fn_append + +# as_fn_arith ARG... +# ------------------ +# Perform arithmetic evaluation on the ARGs, and store the result in the +# global $as_val. Take advantage of shells that can avoid forks. The arguments +# must be portable across $(()) and expr. +if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then : + eval 'as_fn_arith () + { + as_val=$(( $* )) + }' +else + as_fn_arith () + { + as_val=`expr "$@" || test $? -eq 1` + } +fi # as_fn_arith + + +if expr a : '\(a\)' >/dev/null 2>&1 && + test "X`expr 00001 : '.*\(...\)'`" = X001; then + as_expr=expr +else + as_expr=false +fi + +if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then + as_basename=basename +else + as_basename=false +fi + +if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then + as_dirname=dirname +else + as_dirname=false +fi + +as_me=`$as_basename -- "$0" || +$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X/"$0" | + sed '/^.*\/\([^/][^/]*\)\/*$/{ + s//\1/ + q + } + /^X\/\(\/\/\)$/{ + s//\1/ + q + } + /^X\/\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + +# Avoid depending upon Character Ranges. +as_cr_letters='abcdefghijklmnopqrstuvwxyz' +as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' +as_cr_Letters=$as_cr_letters$as_cr_LETTERS +as_cr_digits='0123456789' +as_cr_alnum=$as_cr_Letters$as_cr_digits + +ECHO_C= ECHO_N= ECHO_T= +case `echo -n x` in #((((( +-n*) + case `echo 'xy\c'` in + *c*) ECHO_T=' ';; # ECHO_T is single tab character. + xy) ECHO_C='\c';; + *) echo `echo ksh88 bug on AIX 6.1` > /dev/null + ECHO_T=' ';; + esac;; +*) + ECHO_N='-n';; +esac + +rm -f conf$$ conf$$.exe conf$$.file +if test -d conf$$.dir; then + rm -f conf$$.dir/conf$$.file +else + rm -f conf$$.dir + mkdir conf$$.dir 2>/dev/null +fi +if (echo >conf$$.file) 2>/dev/null; then + if ln -s conf$$.file conf$$ 2>/dev/null; then + as_ln_s='ln -s' + # ... but there are two gotchas: + # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. + # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. + # In both cases, we have to default to `cp -pR'. + ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || + as_ln_s='cp -pR' + elif ln conf$$.file conf$$ 2>/dev/null; then + as_ln_s=ln + else + as_ln_s='cp -pR' + fi +else + as_ln_s='cp -pR' +fi +rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file +rmdir conf$$.dir 2>/dev/null + + +# as_fn_mkdir_p +# ------------- +# Create "$as_dir" as a directory, including parents if necessary. +as_fn_mkdir_p () +{ + + case $as_dir in #( + -*) as_dir=./$as_dir;; + esac + test -d "$as_dir" || eval $as_mkdir_p || { + as_dirs= + while :; do + case $as_dir in #( + *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( + *) as_qdir=$as_dir;; + esac + as_dirs="'$as_qdir' $as_dirs" + as_dir=`$as_dirname -- "$as_dir" || +$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_dir" : 'X\(//\)[^/]' \| \ + X"$as_dir" : 'X\(//\)$' \| \ + X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$as_dir" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + test -d "$as_dir" && break + done + test -z "$as_dirs" || eval "mkdir $as_dirs" + } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" + + +} # as_fn_mkdir_p +if mkdir -p . 2>/dev/null; then + as_mkdir_p='mkdir -p "$as_dir"' +else + test -d ./-p && rmdir ./-p + as_mkdir_p=false +fi + + +# as_fn_executable_p FILE +# ----------------------- +# Test if FILE is an executable regular file. +as_fn_executable_p () +{ + test -f "$1" && test -x "$1" +} # as_fn_executable_p +as_test_x='test -x' +as_executable_p=as_fn_executable_p + +# Sed expression to map a string onto a valid CPP name. +as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" + +# Sed expression to map a string onto a valid variable name. +as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" + + +exec 6>&1 +## ----------------------------------- ## +## Main body of $CONFIG_STATUS script. ## +## ----------------------------------- ## +_ASEOF +test $as_write_fail = 0 && chmod +x $CONFIG_STATUS || ac_write_fail=1 + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +# Save the log message, to keep $0 and so on meaningful, and to +# report actual input values of CONFIG_FILES etc. instead of their +# values after options handling. +ac_log=" +This file was extended by nghttp2 $as_me 1.0.0, which was +generated by GNU Autoconf 2.69. Invocation command line was + + CONFIG_FILES = $CONFIG_FILES + CONFIG_HEADERS = $CONFIG_HEADERS + CONFIG_LINKS = $CONFIG_LINKS + CONFIG_COMMANDS = $CONFIG_COMMANDS + $ $0 $@ + +on `(hostname || uname -n) 2>/dev/null | sed 1q` +" + +_ACEOF + +case $ac_config_files in *" +"*) set x $ac_config_files; shift; ac_config_files=$*;; +esac + +case $ac_config_headers in *" +"*) set x $ac_config_headers; shift; ac_config_headers=$*;; +esac + + +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +# Files that config.status was made for. +config_files="$ac_config_files" +config_headers="$ac_config_headers" +config_commands="$ac_config_commands" + +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +ac_cs_usage="\ +\`$as_me' instantiates files and other configuration actions +from templates according to the current configuration. Unless the files +and actions are specified as TAGs, all are instantiated by default. + +Usage: $0 [OPTION]... [TAG]... + + -h, --help print this help, then exit + -V, --version print version number and configuration settings, then exit + --config print configuration, then exit + -q, --quiet, --silent + do not print progress messages + -d, --debug don't remove temporary files + --recheck update $as_me by reconfiguring in the same conditions + --file=FILE[:TEMPLATE] + instantiate the configuration file FILE + --header=FILE[:TEMPLATE] + instantiate the configuration header FILE + +Configuration files: +$config_files + +Configuration headers: +$config_headers + +Configuration commands: +$config_commands + +Report bugs to ." + +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" +ac_cs_version="\\ +nghttp2 config.status 1.0.0 +configured by $0, generated by GNU Autoconf 2.69, + with options \\"\$ac_cs_config\\" + +Copyright (C) 2012 Free Software Foundation, Inc. +This config.status script is free software; the Free Software Foundation +gives unlimited permission to copy, distribute and modify it." + +ac_pwd='$ac_pwd' +srcdir='$srcdir' +INSTALL='$INSTALL' +MKDIR_P='$MKDIR_P' +AWK='$AWK' +test -n "\$AWK" || AWK=awk +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +# The default lists apply if the user does not specify any file. +ac_need_defaults=: +while test $# != 0 +do + case $1 in + --*=?*) + ac_option=`expr "X$1" : 'X\([^=]*\)='` + ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'` + ac_shift=: + ;; + --*=) + ac_option=`expr "X$1" : 'X\([^=]*\)='` + ac_optarg= + ac_shift=: + ;; + *) + ac_option=$1 + ac_optarg=$2 + ac_shift=shift + ;; + esac + + case $ac_option in + # Handling of the options. + -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) + ac_cs_recheck=: ;; + --version | --versio | --versi | --vers | --ver | --ve | --v | -V ) + $as_echo "$ac_cs_version"; exit ;; + --config | --confi | --conf | --con | --co | --c ) + $as_echo "$ac_cs_config"; exit ;; + --debug | --debu | --deb | --de | --d | -d ) + debug=: ;; + --file | --fil | --fi | --f ) + $ac_shift + case $ac_optarg in + *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; + '') as_fn_error $? "missing file argument" ;; + esac + as_fn_append CONFIG_FILES " '$ac_optarg'" + ac_need_defaults=false;; + --header | --heade | --head | --hea ) + $ac_shift + case $ac_optarg in + *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; + esac + as_fn_append CONFIG_HEADERS " '$ac_optarg'" + ac_need_defaults=false;; + --he | --h) + # Conflict between --help and --header + as_fn_error $? "ambiguous option: \`$1' +Try \`$0 --help' for more information.";; + --help | --hel | -h ) + $as_echo "$ac_cs_usage"; exit ;; + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil | --si | --s) + ac_cs_silent=: ;; + + # This is an error. + -*) as_fn_error $? "unrecognized option: \`$1' +Try \`$0 --help' for more information." ;; + + *) as_fn_append ac_config_targets " $1" + ac_need_defaults=false ;; + + esac + shift +done + +ac_configure_extra_args= + +if $ac_cs_silent; then + exec 6>/dev/null + ac_configure_extra_args="$ac_configure_extra_args --silent" +fi + +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +if \$ac_cs_recheck; then + set X $SHELL '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion + shift + \$as_echo "running CONFIG_SHELL=$SHELL \$*" >&6 + CONFIG_SHELL='$SHELL' + export CONFIG_SHELL + exec "\$@" +fi + +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +exec 5>>config.log +{ + echo + sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX +## Running $as_me. ## +_ASBOX + $as_echo "$ac_log" +} >&5 + +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +# +# INIT-COMMANDS +# + + +# The HP-UX ksh and POSIX shell print the target directory to stdout +# if CDPATH is set. +(unset CDPATH) >/dev/null 2>&1 && unset CDPATH + +sed_quote_subst='$sed_quote_subst' +double_quote_subst='$double_quote_subst' +delay_variable_subst='$delay_variable_subst' +macro_version='`$ECHO "$macro_version" | $SED "$delay_single_quote_subst"`' +macro_revision='`$ECHO "$macro_revision" | $SED "$delay_single_quote_subst"`' +enable_shared='`$ECHO "$enable_shared" | $SED "$delay_single_quote_subst"`' +enable_static='`$ECHO "$enable_static" | $SED "$delay_single_quote_subst"`' +pic_mode='`$ECHO "$pic_mode" | $SED "$delay_single_quote_subst"`' +enable_fast_install='`$ECHO "$enable_fast_install" | $SED "$delay_single_quote_subst"`' +SHELL='`$ECHO "$SHELL" | $SED "$delay_single_quote_subst"`' +ECHO='`$ECHO "$ECHO" | $SED "$delay_single_quote_subst"`' +PATH_SEPARATOR='`$ECHO "$PATH_SEPARATOR" | $SED "$delay_single_quote_subst"`' +host_alias='`$ECHO "$host_alias" | $SED "$delay_single_quote_subst"`' +host='`$ECHO "$host" | $SED "$delay_single_quote_subst"`' +host_os='`$ECHO "$host_os" | $SED "$delay_single_quote_subst"`' +build_alias='`$ECHO "$build_alias" | $SED "$delay_single_quote_subst"`' +build='`$ECHO "$build" | $SED "$delay_single_quote_subst"`' +build_os='`$ECHO "$build_os" | $SED "$delay_single_quote_subst"`' +SED='`$ECHO "$SED" | $SED "$delay_single_quote_subst"`' +Xsed='`$ECHO "$Xsed" | $SED "$delay_single_quote_subst"`' +GREP='`$ECHO "$GREP" | $SED "$delay_single_quote_subst"`' +EGREP='`$ECHO "$EGREP" | $SED "$delay_single_quote_subst"`' +FGREP='`$ECHO "$FGREP" | $SED "$delay_single_quote_subst"`' +LD='`$ECHO "$LD" | $SED "$delay_single_quote_subst"`' +NM='`$ECHO "$NM" | $SED "$delay_single_quote_subst"`' +LN_S='`$ECHO "$LN_S" | $SED "$delay_single_quote_subst"`' +max_cmd_len='`$ECHO "$max_cmd_len" | $SED "$delay_single_quote_subst"`' +ac_objext='`$ECHO "$ac_objext" | $SED "$delay_single_quote_subst"`' +exeext='`$ECHO "$exeext" | $SED "$delay_single_quote_subst"`' +lt_unset='`$ECHO "$lt_unset" | $SED "$delay_single_quote_subst"`' +lt_SP2NL='`$ECHO "$lt_SP2NL" | $SED "$delay_single_quote_subst"`' +lt_NL2SP='`$ECHO "$lt_NL2SP" | $SED "$delay_single_quote_subst"`' +lt_cv_to_host_file_cmd='`$ECHO "$lt_cv_to_host_file_cmd" | $SED "$delay_single_quote_subst"`' +lt_cv_to_tool_file_cmd='`$ECHO "$lt_cv_to_tool_file_cmd" | $SED "$delay_single_quote_subst"`' +reload_flag='`$ECHO "$reload_flag" | $SED "$delay_single_quote_subst"`' +reload_cmds='`$ECHO "$reload_cmds" | $SED "$delay_single_quote_subst"`' +OBJDUMP='`$ECHO "$OBJDUMP" | $SED "$delay_single_quote_subst"`' +deplibs_check_method='`$ECHO "$deplibs_check_method" | $SED "$delay_single_quote_subst"`' +file_magic_cmd='`$ECHO "$file_magic_cmd" | $SED "$delay_single_quote_subst"`' +file_magic_glob='`$ECHO "$file_magic_glob" | $SED "$delay_single_quote_subst"`' +want_nocaseglob='`$ECHO "$want_nocaseglob" | $SED "$delay_single_quote_subst"`' +DLLTOOL='`$ECHO "$DLLTOOL" | $SED "$delay_single_quote_subst"`' +sharedlib_from_linklib_cmd='`$ECHO "$sharedlib_from_linklib_cmd" | $SED "$delay_single_quote_subst"`' +AR='`$ECHO "$AR" | $SED "$delay_single_quote_subst"`' +AR_FLAGS='`$ECHO "$AR_FLAGS" | $SED "$delay_single_quote_subst"`' +archiver_list_spec='`$ECHO "$archiver_list_spec" | $SED "$delay_single_quote_subst"`' +STRIP='`$ECHO "$STRIP" | $SED "$delay_single_quote_subst"`' +RANLIB='`$ECHO "$RANLIB" | $SED "$delay_single_quote_subst"`' +old_postinstall_cmds='`$ECHO "$old_postinstall_cmds" | $SED "$delay_single_quote_subst"`' +old_postuninstall_cmds='`$ECHO "$old_postuninstall_cmds" | $SED "$delay_single_quote_subst"`' +old_archive_cmds='`$ECHO "$old_archive_cmds" | $SED "$delay_single_quote_subst"`' +lock_old_archive_extraction='`$ECHO "$lock_old_archive_extraction" | $SED "$delay_single_quote_subst"`' +CC='`$ECHO "$CC" | $SED "$delay_single_quote_subst"`' +CFLAGS='`$ECHO "$CFLAGS" | $SED "$delay_single_quote_subst"`' +compiler='`$ECHO "$compiler" | $SED "$delay_single_quote_subst"`' +GCC='`$ECHO "$GCC" | $SED "$delay_single_quote_subst"`' +lt_cv_sys_global_symbol_pipe='`$ECHO "$lt_cv_sys_global_symbol_pipe" | $SED "$delay_single_quote_subst"`' +lt_cv_sys_global_symbol_to_cdecl='`$ECHO "$lt_cv_sys_global_symbol_to_cdecl" | $SED "$delay_single_quote_subst"`' +lt_cv_sys_global_symbol_to_c_name_address='`$ECHO "$lt_cv_sys_global_symbol_to_c_name_address" | $SED "$delay_single_quote_subst"`' +lt_cv_sys_global_symbol_to_c_name_address_lib_prefix='`$ECHO "$lt_cv_sys_global_symbol_to_c_name_address_lib_prefix" | $SED "$delay_single_quote_subst"`' +nm_file_list_spec='`$ECHO "$nm_file_list_spec" | $SED "$delay_single_quote_subst"`' +lt_sysroot='`$ECHO "$lt_sysroot" | $SED "$delay_single_quote_subst"`' +objdir='`$ECHO "$objdir" | $SED "$delay_single_quote_subst"`' +MAGIC_CMD='`$ECHO "$MAGIC_CMD" | $SED "$delay_single_quote_subst"`' +lt_prog_compiler_no_builtin_flag='`$ECHO "$lt_prog_compiler_no_builtin_flag" | $SED "$delay_single_quote_subst"`' +lt_prog_compiler_pic='`$ECHO "$lt_prog_compiler_pic" | $SED "$delay_single_quote_subst"`' +lt_prog_compiler_wl='`$ECHO "$lt_prog_compiler_wl" | $SED "$delay_single_quote_subst"`' +lt_prog_compiler_static='`$ECHO "$lt_prog_compiler_static" | $SED "$delay_single_quote_subst"`' +lt_cv_prog_compiler_c_o='`$ECHO "$lt_cv_prog_compiler_c_o" | $SED "$delay_single_quote_subst"`' +need_locks='`$ECHO "$need_locks" | $SED "$delay_single_quote_subst"`' +MANIFEST_TOOL='`$ECHO "$MANIFEST_TOOL" | $SED "$delay_single_quote_subst"`' +DSYMUTIL='`$ECHO "$DSYMUTIL" | $SED "$delay_single_quote_subst"`' +NMEDIT='`$ECHO "$NMEDIT" | $SED "$delay_single_quote_subst"`' +LIPO='`$ECHO "$LIPO" | $SED "$delay_single_quote_subst"`' +OTOOL='`$ECHO "$OTOOL" | $SED "$delay_single_quote_subst"`' +OTOOL64='`$ECHO "$OTOOL64" | $SED "$delay_single_quote_subst"`' +libext='`$ECHO "$libext" | $SED "$delay_single_quote_subst"`' +shrext_cmds='`$ECHO "$shrext_cmds" | $SED "$delay_single_quote_subst"`' +extract_expsyms_cmds='`$ECHO "$extract_expsyms_cmds" | $SED "$delay_single_quote_subst"`' +archive_cmds_need_lc='`$ECHO "$archive_cmds_need_lc" | $SED "$delay_single_quote_subst"`' +enable_shared_with_static_runtimes='`$ECHO "$enable_shared_with_static_runtimes" | $SED "$delay_single_quote_subst"`' +export_dynamic_flag_spec='`$ECHO "$export_dynamic_flag_spec" | $SED "$delay_single_quote_subst"`' +whole_archive_flag_spec='`$ECHO "$whole_archive_flag_spec" | $SED "$delay_single_quote_subst"`' +compiler_needs_object='`$ECHO "$compiler_needs_object" | $SED "$delay_single_quote_subst"`' +old_archive_from_new_cmds='`$ECHO "$old_archive_from_new_cmds" | $SED "$delay_single_quote_subst"`' +old_archive_from_expsyms_cmds='`$ECHO "$old_archive_from_expsyms_cmds" | $SED "$delay_single_quote_subst"`' +archive_cmds='`$ECHO "$archive_cmds" | $SED "$delay_single_quote_subst"`' +archive_expsym_cmds='`$ECHO "$archive_expsym_cmds" | $SED "$delay_single_quote_subst"`' +module_cmds='`$ECHO "$module_cmds" | $SED "$delay_single_quote_subst"`' +module_expsym_cmds='`$ECHO "$module_expsym_cmds" | $SED "$delay_single_quote_subst"`' +with_gnu_ld='`$ECHO "$with_gnu_ld" | $SED "$delay_single_quote_subst"`' +allow_undefined_flag='`$ECHO "$allow_undefined_flag" | $SED "$delay_single_quote_subst"`' +no_undefined_flag='`$ECHO "$no_undefined_flag" | $SED "$delay_single_quote_subst"`' +hardcode_libdir_flag_spec='`$ECHO "$hardcode_libdir_flag_spec" | $SED "$delay_single_quote_subst"`' +hardcode_libdir_separator='`$ECHO "$hardcode_libdir_separator" | $SED "$delay_single_quote_subst"`' +hardcode_direct='`$ECHO "$hardcode_direct" | $SED "$delay_single_quote_subst"`' +hardcode_direct_absolute='`$ECHO "$hardcode_direct_absolute" | $SED "$delay_single_quote_subst"`' +hardcode_minus_L='`$ECHO "$hardcode_minus_L" | $SED "$delay_single_quote_subst"`' +hardcode_shlibpath_var='`$ECHO "$hardcode_shlibpath_var" | $SED "$delay_single_quote_subst"`' +hardcode_automatic='`$ECHO "$hardcode_automatic" | $SED "$delay_single_quote_subst"`' +inherit_rpath='`$ECHO "$inherit_rpath" | $SED "$delay_single_quote_subst"`' +link_all_deplibs='`$ECHO "$link_all_deplibs" | $SED "$delay_single_quote_subst"`' +always_export_symbols='`$ECHO "$always_export_symbols" | $SED "$delay_single_quote_subst"`' +export_symbols_cmds='`$ECHO "$export_symbols_cmds" | $SED "$delay_single_quote_subst"`' +exclude_expsyms='`$ECHO "$exclude_expsyms" | $SED "$delay_single_quote_subst"`' +include_expsyms='`$ECHO "$include_expsyms" | $SED "$delay_single_quote_subst"`' +prelink_cmds='`$ECHO "$prelink_cmds" | $SED "$delay_single_quote_subst"`' +postlink_cmds='`$ECHO "$postlink_cmds" | $SED "$delay_single_quote_subst"`' +file_list_spec='`$ECHO "$file_list_spec" | $SED "$delay_single_quote_subst"`' +variables_saved_for_relink='`$ECHO "$variables_saved_for_relink" | $SED "$delay_single_quote_subst"`' +need_lib_prefix='`$ECHO "$need_lib_prefix" | $SED "$delay_single_quote_subst"`' +need_version='`$ECHO "$need_version" | $SED "$delay_single_quote_subst"`' +version_type='`$ECHO "$version_type" | $SED "$delay_single_quote_subst"`' +runpath_var='`$ECHO "$runpath_var" | $SED "$delay_single_quote_subst"`' +shlibpath_var='`$ECHO "$shlibpath_var" | $SED "$delay_single_quote_subst"`' +shlibpath_overrides_runpath='`$ECHO "$shlibpath_overrides_runpath" | $SED "$delay_single_quote_subst"`' +libname_spec='`$ECHO "$libname_spec" | $SED "$delay_single_quote_subst"`' +library_names_spec='`$ECHO "$library_names_spec" | $SED "$delay_single_quote_subst"`' +soname_spec='`$ECHO "$soname_spec" | $SED "$delay_single_quote_subst"`' +install_override_mode='`$ECHO "$install_override_mode" | $SED "$delay_single_quote_subst"`' +postinstall_cmds='`$ECHO "$postinstall_cmds" | $SED "$delay_single_quote_subst"`' +postuninstall_cmds='`$ECHO "$postuninstall_cmds" | $SED "$delay_single_quote_subst"`' +finish_cmds='`$ECHO "$finish_cmds" | $SED "$delay_single_quote_subst"`' +finish_eval='`$ECHO "$finish_eval" | $SED "$delay_single_quote_subst"`' +hardcode_into_libs='`$ECHO "$hardcode_into_libs" | $SED "$delay_single_quote_subst"`' +sys_lib_search_path_spec='`$ECHO "$sys_lib_search_path_spec" | $SED "$delay_single_quote_subst"`' +sys_lib_dlsearch_path_spec='`$ECHO "$sys_lib_dlsearch_path_spec" | $SED "$delay_single_quote_subst"`' +hardcode_action='`$ECHO "$hardcode_action" | $SED "$delay_single_quote_subst"`' +enable_dlopen='`$ECHO "$enable_dlopen" | $SED "$delay_single_quote_subst"`' +enable_dlopen_self='`$ECHO "$enable_dlopen_self" | $SED "$delay_single_quote_subst"`' +enable_dlopen_self_static='`$ECHO "$enable_dlopen_self_static" | $SED "$delay_single_quote_subst"`' +old_striplib='`$ECHO "$old_striplib" | $SED "$delay_single_quote_subst"`' +striplib='`$ECHO "$striplib" | $SED "$delay_single_quote_subst"`' +compiler_lib_search_dirs='`$ECHO "$compiler_lib_search_dirs" | $SED "$delay_single_quote_subst"`' +predep_objects='`$ECHO "$predep_objects" | $SED "$delay_single_quote_subst"`' +postdep_objects='`$ECHO "$postdep_objects" | $SED "$delay_single_quote_subst"`' +predeps='`$ECHO "$predeps" | $SED "$delay_single_quote_subst"`' +postdeps='`$ECHO "$postdeps" | $SED "$delay_single_quote_subst"`' +compiler_lib_search_path='`$ECHO "$compiler_lib_search_path" | $SED "$delay_single_quote_subst"`' +LD_CXX='`$ECHO "$LD_CXX" | $SED "$delay_single_quote_subst"`' +reload_flag_CXX='`$ECHO "$reload_flag_CXX" | $SED "$delay_single_quote_subst"`' +reload_cmds_CXX='`$ECHO "$reload_cmds_CXX" | $SED "$delay_single_quote_subst"`' +old_archive_cmds_CXX='`$ECHO "$old_archive_cmds_CXX" | $SED "$delay_single_quote_subst"`' +compiler_CXX='`$ECHO "$compiler_CXX" | $SED "$delay_single_quote_subst"`' +GCC_CXX='`$ECHO "$GCC_CXX" | $SED "$delay_single_quote_subst"`' +lt_prog_compiler_no_builtin_flag_CXX='`$ECHO "$lt_prog_compiler_no_builtin_flag_CXX" | $SED "$delay_single_quote_subst"`' +lt_prog_compiler_pic_CXX='`$ECHO "$lt_prog_compiler_pic_CXX" | $SED "$delay_single_quote_subst"`' +lt_prog_compiler_wl_CXX='`$ECHO "$lt_prog_compiler_wl_CXX" | $SED "$delay_single_quote_subst"`' +lt_prog_compiler_static_CXX='`$ECHO "$lt_prog_compiler_static_CXX" | $SED "$delay_single_quote_subst"`' +lt_cv_prog_compiler_c_o_CXX='`$ECHO "$lt_cv_prog_compiler_c_o_CXX" | $SED "$delay_single_quote_subst"`' +archive_cmds_need_lc_CXX='`$ECHO "$archive_cmds_need_lc_CXX" | $SED "$delay_single_quote_subst"`' +enable_shared_with_static_runtimes_CXX='`$ECHO "$enable_shared_with_static_runtimes_CXX" | $SED "$delay_single_quote_subst"`' +export_dynamic_flag_spec_CXX='`$ECHO "$export_dynamic_flag_spec_CXX" | $SED "$delay_single_quote_subst"`' +whole_archive_flag_spec_CXX='`$ECHO "$whole_archive_flag_spec_CXX" | $SED "$delay_single_quote_subst"`' +compiler_needs_object_CXX='`$ECHO "$compiler_needs_object_CXX" | $SED "$delay_single_quote_subst"`' +old_archive_from_new_cmds_CXX='`$ECHO "$old_archive_from_new_cmds_CXX" | $SED "$delay_single_quote_subst"`' +old_archive_from_expsyms_cmds_CXX='`$ECHO "$old_archive_from_expsyms_cmds_CXX" | $SED "$delay_single_quote_subst"`' +archive_cmds_CXX='`$ECHO "$archive_cmds_CXX" | $SED "$delay_single_quote_subst"`' +archive_expsym_cmds_CXX='`$ECHO "$archive_expsym_cmds_CXX" | $SED "$delay_single_quote_subst"`' +module_cmds_CXX='`$ECHO "$module_cmds_CXX" | $SED "$delay_single_quote_subst"`' +module_expsym_cmds_CXX='`$ECHO "$module_expsym_cmds_CXX" | $SED "$delay_single_quote_subst"`' +with_gnu_ld_CXX='`$ECHO "$with_gnu_ld_CXX" | $SED "$delay_single_quote_subst"`' +allow_undefined_flag_CXX='`$ECHO "$allow_undefined_flag_CXX" | $SED "$delay_single_quote_subst"`' +no_undefined_flag_CXX='`$ECHO "$no_undefined_flag_CXX" | $SED "$delay_single_quote_subst"`' +hardcode_libdir_flag_spec_CXX='`$ECHO "$hardcode_libdir_flag_spec_CXX" | $SED "$delay_single_quote_subst"`' +hardcode_libdir_separator_CXX='`$ECHO "$hardcode_libdir_separator_CXX" | $SED "$delay_single_quote_subst"`' +hardcode_direct_CXX='`$ECHO "$hardcode_direct_CXX" | $SED "$delay_single_quote_subst"`' +hardcode_direct_absolute_CXX='`$ECHO "$hardcode_direct_absolute_CXX" | $SED "$delay_single_quote_subst"`' +hardcode_minus_L_CXX='`$ECHO "$hardcode_minus_L_CXX" | $SED "$delay_single_quote_subst"`' +hardcode_shlibpath_var_CXX='`$ECHO "$hardcode_shlibpath_var_CXX" | $SED "$delay_single_quote_subst"`' +hardcode_automatic_CXX='`$ECHO "$hardcode_automatic_CXX" | $SED "$delay_single_quote_subst"`' +inherit_rpath_CXX='`$ECHO "$inherit_rpath_CXX" | $SED "$delay_single_quote_subst"`' +link_all_deplibs_CXX='`$ECHO "$link_all_deplibs_CXX" | $SED "$delay_single_quote_subst"`' +always_export_symbols_CXX='`$ECHO "$always_export_symbols_CXX" | $SED "$delay_single_quote_subst"`' +export_symbols_cmds_CXX='`$ECHO "$export_symbols_cmds_CXX" | $SED "$delay_single_quote_subst"`' +exclude_expsyms_CXX='`$ECHO "$exclude_expsyms_CXX" | $SED "$delay_single_quote_subst"`' +include_expsyms_CXX='`$ECHO "$include_expsyms_CXX" | $SED "$delay_single_quote_subst"`' +prelink_cmds_CXX='`$ECHO "$prelink_cmds_CXX" | $SED "$delay_single_quote_subst"`' +postlink_cmds_CXX='`$ECHO "$postlink_cmds_CXX" | $SED "$delay_single_quote_subst"`' +file_list_spec_CXX='`$ECHO "$file_list_spec_CXX" | $SED "$delay_single_quote_subst"`' +hardcode_action_CXX='`$ECHO "$hardcode_action_CXX" | $SED "$delay_single_quote_subst"`' +compiler_lib_search_dirs_CXX='`$ECHO "$compiler_lib_search_dirs_CXX" | $SED "$delay_single_quote_subst"`' +predep_objects_CXX='`$ECHO "$predep_objects_CXX" | $SED "$delay_single_quote_subst"`' +postdep_objects_CXX='`$ECHO "$postdep_objects_CXX" | $SED "$delay_single_quote_subst"`' +predeps_CXX='`$ECHO "$predeps_CXX" | $SED "$delay_single_quote_subst"`' +postdeps_CXX='`$ECHO "$postdeps_CXX" | $SED "$delay_single_quote_subst"`' +compiler_lib_search_path_CXX='`$ECHO "$compiler_lib_search_path_CXX" | $SED "$delay_single_quote_subst"`' + +LTCC='$LTCC' +LTCFLAGS='$LTCFLAGS' +compiler='$compiler_DEFAULT' + +# A function that is used when there is no print builtin or printf. +func_fallback_echo () +{ + eval 'cat <<_LTECHO_EOF +\$1 +_LTECHO_EOF' +} + +# Quote evaled strings. +for var in SHELL \ +ECHO \ +PATH_SEPARATOR \ +SED \ +GREP \ +EGREP \ +FGREP \ +LD \ +NM \ +LN_S \ +lt_SP2NL \ +lt_NL2SP \ +reload_flag \ +OBJDUMP \ +deplibs_check_method \ +file_magic_cmd \ +file_magic_glob \ +want_nocaseglob \ +DLLTOOL \ +sharedlib_from_linklib_cmd \ +AR \ +AR_FLAGS \ +archiver_list_spec \ +STRIP \ +RANLIB \ +CC \ +CFLAGS \ +compiler \ +lt_cv_sys_global_symbol_pipe \ +lt_cv_sys_global_symbol_to_cdecl \ +lt_cv_sys_global_symbol_to_c_name_address \ +lt_cv_sys_global_symbol_to_c_name_address_lib_prefix \ +nm_file_list_spec \ +lt_prog_compiler_no_builtin_flag \ +lt_prog_compiler_pic \ +lt_prog_compiler_wl \ +lt_prog_compiler_static \ +lt_cv_prog_compiler_c_o \ +need_locks \ +MANIFEST_TOOL \ +DSYMUTIL \ +NMEDIT \ +LIPO \ +OTOOL \ +OTOOL64 \ +shrext_cmds \ +export_dynamic_flag_spec \ +whole_archive_flag_spec \ +compiler_needs_object \ +with_gnu_ld \ +allow_undefined_flag \ +no_undefined_flag \ +hardcode_libdir_flag_spec \ +hardcode_libdir_separator \ +exclude_expsyms \ +include_expsyms \ +file_list_spec \ +variables_saved_for_relink \ +libname_spec \ +library_names_spec \ +soname_spec \ +install_override_mode \ +finish_eval \ +old_striplib \ +striplib \ +compiler_lib_search_dirs \ +predep_objects \ +postdep_objects \ +predeps \ +postdeps \ +compiler_lib_search_path \ +LD_CXX \ +reload_flag_CXX \ +compiler_CXX \ +lt_prog_compiler_no_builtin_flag_CXX \ +lt_prog_compiler_pic_CXX \ +lt_prog_compiler_wl_CXX \ +lt_prog_compiler_static_CXX \ +lt_cv_prog_compiler_c_o_CXX \ +export_dynamic_flag_spec_CXX \ +whole_archive_flag_spec_CXX \ +compiler_needs_object_CXX \ +with_gnu_ld_CXX \ +allow_undefined_flag_CXX \ +no_undefined_flag_CXX \ +hardcode_libdir_flag_spec_CXX \ +hardcode_libdir_separator_CXX \ +exclude_expsyms_CXX \ +include_expsyms_CXX \ +file_list_spec_CXX \ +compiler_lib_search_dirs_CXX \ +predep_objects_CXX \ +postdep_objects_CXX \ +predeps_CXX \ +postdeps_CXX \ +compiler_lib_search_path_CXX; do + case \`eval \\\\\$ECHO \\\\""\\\\\$\$var"\\\\"\` in + *[\\\\\\\`\\"\\\$]*) + eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"\\\$\$var\\" | \\\$SED \\"\\\$sed_quote_subst\\"\\\`\\\\\\"" + ;; + *) + eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\"" + ;; + esac +done + +# Double-quote double-evaled strings. +for var in reload_cmds \ +old_postinstall_cmds \ +old_postuninstall_cmds \ +old_archive_cmds \ +extract_expsyms_cmds \ +old_archive_from_new_cmds \ +old_archive_from_expsyms_cmds \ +archive_cmds \ +archive_expsym_cmds \ +module_cmds \ +module_expsym_cmds \ +export_symbols_cmds \ +prelink_cmds \ +postlink_cmds \ +postinstall_cmds \ +postuninstall_cmds \ +finish_cmds \ +sys_lib_search_path_spec \ +sys_lib_dlsearch_path_spec \ +reload_cmds_CXX \ +old_archive_cmds_CXX \ +old_archive_from_new_cmds_CXX \ +old_archive_from_expsyms_cmds_CXX \ +archive_cmds_CXX \ +archive_expsym_cmds_CXX \ +module_cmds_CXX \ +module_expsym_cmds_CXX \ +export_symbols_cmds_CXX \ +prelink_cmds_CXX \ +postlink_cmds_CXX; do + case \`eval \\\\\$ECHO \\\\""\\\\\$\$var"\\\\"\` in + *[\\\\\\\`\\"\\\$]*) + eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"\\\$\$var\\" | \\\$SED -e \\"\\\$double_quote_subst\\" -e \\"\\\$sed_quote_subst\\" -e \\"\\\$delay_variable_subst\\"\\\`\\\\\\"" + ;; + *) + eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\"" + ;; + esac +done + +ac_aux_dir='$ac_aux_dir' +xsi_shell='$xsi_shell' +lt_shell_append='$lt_shell_append' + +# See if we are running on zsh, and set the options which allow our +# commands through without removal of \ escapes INIT. +if test -n "\${ZSH_VERSION+set}" ; then + setopt NO_GLOB_SUBST +fi + + + PACKAGE='$PACKAGE' + VERSION='$VERSION' + TIMESTAMP='$TIMESTAMP' + RM='$RM' + ofile='$ofile' + + + + + +AMDEP_TRUE="$AMDEP_TRUE" ac_aux_dir="$ac_aux_dir" + +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 + +# Handling of arguments. +for ac_config_target in $ac_config_targets +do + case $ac_config_target in + "libtool") CONFIG_COMMANDS="$CONFIG_COMMANDS libtool" ;; + "depfiles") CONFIG_COMMANDS="$CONFIG_COMMANDS depfiles" ;; + "config.h") CONFIG_HEADERS="$CONFIG_HEADERS config.h" ;; + "Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;; + "lib/Makefile") CONFIG_FILES="$CONFIG_FILES lib/Makefile" ;; + "lib/libnghttp2.pc") CONFIG_FILES="$CONFIG_FILES lib/libnghttp2.pc" ;; + "lib/includes/Makefile") CONFIG_FILES="$CONFIG_FILES lib/includes/Makefile" ;; + "lib/includes/nghttp2/nghttp2ver.h") CONFIG_FILES="$CONFIG_FILES lib/includes/nghttp2/nghttp2ver.h" ;; + "tests/Makefile") CONFIG_FILES="$CONFIG_FILES tests/Makefile" ;; + "tests/testdata/Makefile") CONFIG_FILES="$CONFIG_FILES tests/testdata/Makefile" ;; + "third-party/Makefile") CONFIG_FILES="$CONFIG_FILES third-party/Makefile" ;; + "src/Makefile") CONFIG_FILES="$CONFIG_FILES src/Makefile" ;; + "src/includes/Makefile") CONFIG_FILES="$CONFIG_FILES src/includes/Makefile" ;; + "src/libnghttp2_asio.pc") CONFIG_FILES="$CONFIG_FILES src/libnghttp2_asio.pc" ;; + "examples/Makefile") CONFIG_FILES="$CONFIG_FILES examples/Makefile" ;; + "python/Makefile") CONFIG_FILES="$CONFIG_FILES python/Makefile" ;; + "python/setup.py") CONFIG_FILES="$CONFIG_FILES python/setup.py" ;; + "integration-tests/Makefile") CONFIG_FILES="$CONFIG_FILES integration-tests/Makefile" ;; + "integration-tests/config.go") CONFIG_FILES="$CONFIG_FILES integration-tests/config.go" ;; + "integration-tests/setenv") CONFIG_FILES="$CONFIG_FILES integration-tests/setenv" ;; + "doc/Makefile") CONFIG_FILES="$CONFIG_FILES doc/Makefile" ;; + "doc/conf.py") CONFIG_FILES="$CONFIG_FILES doc/conf.py" ;; + "doc/index.rst") CONFIG_FILES="$CONFIG_FILES doc/index.rst" ;; + "doc/package_README.rst") CONFIG_FILES="$CONFIG_FILES doc/package_README.rst" ;; + "doc/tutorial-client.rst") CONFIG_FILES="$CONFIG_FILES doc/tutorial-client.rst" ;; + "doc/tutorial-server.rst") CONFIG_FILES="$CONFIG_FILES doc/tutorial-server.rst" ;; + "doc/tutorial-hpack.rst") CONFIG_FILES="$CONFIG_FILES doc/tutorial-hpack.rst" ;; + "doc/nghttpx-howto.rst") CONFIG_FILES="$CONFIG_FILES doc/nghttpx-howto.rst" ;; + "doc/h2load-howto.rst") CONFIG_FILES="$CONFIG_FILES doc/h2load-howto.rst" ;; + "doc/libnghttp2_asio.rst") CONFIG_FILES="$CONFIG_FILES doc/libnghttp2_asio.rst" ;; + "doc/python-apiref.rst") CONFIG_FILES="$CONFIG_FILES doc/python-apiref.rst" ;; + "doc/building-android-binary.rst") CONFIG_FILES="$CONFIG_FILES doc/building-android-binary.rst" ;; + "doc/nghttp2.h.rst") CONFIG_FILES="$CONFIG_FILES doc/nghttp2.h.rst" ;; + "doc/nghttp2ver.h.rst") CONFIG_FILES="$CONFIG_FILES doc/nghttp2ver.h.rst" ;; + "doc/asio_http2.h.rst") CONFIG_FILES="$CONFIG_FILES doc/asio_http2.h.rst" ;; + "doc/asio_http2_server.h.rst") CONFIG_FILES="$CONFIG_FILES doc/asio_http2_server.h.rst" ;; + "doc/asio_http2_client.h.rst") CONFIG_FILES="$CONFIG_FILES doc/asio_http2_client.h.rst" ;; + "doc/contribute.rst") CONFIG_FILES="$CONFIG_FILES doc/contribute.rst" ;; + "contrib/Makefile") CONFIG_FILES="$CONFIG_FILES contrib/Makefile" ;; + + *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;; + esac +done + + +# If the user did not use the arguments to specify the items to instantiate, +# then the envvar interface is used. Set only those that are not. +# We use the long form for the default assignment because of an extremely +# bizarre bug on SunOS 4.1.3. +if $ac_need_defaults; then + test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files + test "${CONFIG_HEADERS+set}" = set || CONFIG_HEADERS=$config_headers + test "${CONFIG_COMMANDS+set}" = set || CONFIG_COMMANDS=$config_commands +fi + +# Have a temporary directory for convenience. Make it in the build tree +# simply because there is no reason against having it here, and in addition, +# creating and moving files from /tmp can sometimes cause problems. +# Hook for its removal unless debugging. +# Note that there is a small window in which the directory will not be cleaned: +# after its creation but before its name has been assigned to `$tmp'. +$debug || +{ + tmp= ac_tmp= + trap 'exit_status=$? + : "${ac_tmp:=$tmp}" + { test ! -d "$ac_tmp" || rm -fr "$ac_tmp"; } && exit $exit_status +' 0 + trap 'as_fn_exit 1' 1 2 13 15 +} +# Create a (secure) tmp directory for tmp files. + +{ + tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` && + test -d "$tmp" +} || +{ + tmp=./conf$$-$RANDOM + (umask 077 && mkdir "$tmp") +} || as_fn_error $? "cannot create a temporary directory in ." "$LINENO" 5 +ac_tmp=$tmp + +# Set up the scripts for CONFIG_FILES section. +# No need to generate them if there are no CONFIG_FILES. +# This happens for instance with `./config.status config.h'. +if test -n "$CONFIG_FILES"; then + + +ac_cr=`echo X | tr X '\015'` +# On cygwin, bash can eat \r inside `` if the user requested igncr. +# But we know of no other shell where ac_cr would be empty at this +# point, so we can use a bashism as a fallback. +if test "x$ac_cr" = x; then + eval ac_cr=\$\'\\r\' +fi +ac_cs_awk_cr=`$AWK 'BEGIN { print "a\rb" }' /dev/null` +if test "$ac_cs_awk_cr" = "a${ac_cr}b"; then + ac_cs_awk_cr='\\r' +else + ac_cs_awk_cr=$ac_cr +fi + +echo 'BEGIN {' >"$ac_tmp/subs1.awk" && +_ACEOF + + +{ + echo "cat >conf$$subs.awk <<_ACEOF" && + echo "$ac_subst_vars" | sed 's/.*/&!$&$ac_delim/' && + echo "_ACEOF" +} >conf$$subs.sh || + as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 +ac_delim_num=`echo "$ac_subst_vars" | grep -c '^'` +ac_delim='%!_!# ' +for ac_last_try in false false false false false :; do + . ./conf$$subs.sh || + as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 + + ac_delim_n=`sed -n "s/.*$ac_delim\$/X/p" conf$$subs.awk | grep -c X` + if test $ac_delim_n = $ac_delim_num; then + break + elif $ac_last_try; then + as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 + else + ac_delim="$ac_delim!$ac_delim _$ac_delim!! " + fi +done +rm -f conf$$subs.sh + +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +cat >>"\$ac_tmp/subs1.awk" <<\\_ACAWK && +_ACEOF +sed -n ' +h +s/^/S["/; s/!.*/"]=/ +p +g +s/^[^!]*!// +:repl +t repl +s/'"$ac_delim"'$// +t delim +:nl +h +s/\(.\{148\}\)..*/\1/ +t more1 +s/["\\]/\\&/g; s/^/"/; s/$/\\n"\\/ +p +n +b repl +:more1 +s/["\\]/\\&/g; s/^/"/; s/$/"\\/ +p +g +s/.\{148\}// +t nl +:delim +h +s/\(.\{148\}\)..*/\1/ +t more2 +s/["\\]/\\&/g; s/^/"/; s/$/"/ +p +b +:more2 +s/["\\]/\\&/g; s/^/"/; s/$/"\\/ +p +g +s/.\{148\}// +t delim +' >$CONFIG_STATUS || ac_write_fail=1 +rm -f conf$$subs.awk +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +_ACAWK +cat >>"\$ac_tmp/subs1.awk" <<_ACAWK && + for (key in S) S_is_set[key] = 1 + FS = "" + +} +{ + line = $ 0 + nfields = split(line, field, "@") + substed = 0 + len = length(field[1]) + for (i = 2; i < nfields; i++) { + key = field[i] + keylen = length(key) + if (S_is_set[key]) { + value = S[key] + line = substr(line, 1, len) "" value "" substr(line, len + keylen + 3) + len += length(value) + length(field[++i]) + substed = 1 + } else + len += 1 + keylen + } + + print line +} + +_ACAWK +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +if sed "s/$ac_cr//" < /dev/null > /dev/null 2>&1; then + sed "s/$ac_cr\$//; s/$ac_cr/$ac_cs_awk_cr/g" +else + cat +fi < "$ac_tmp/subs1.awk" > "$ac_tmp/subs.awk" \ + || as_fn_error $? "could not setup config files machinery" "$LINENO" 5 +_ACEOF + +# VPATH may cause trouble with some makes, so we remove sole $(srcdir), +# ${srcdir} and @srcdir@ entries from VPATH if srcdir is ".", strip leading and +# trailing colons and then remove the whole line if VPATH becomes empty +# (actually we leave an empty line to preserve line numbers). +if test "x$srcdir" = x.; then + ac_vpsub='/^[ ]*VPATH[ ]*=[ ]*/{ +h +s/// +s/^/:/ +s/[ ]*$/:/ +s/:\$(srcdir):/:/g +s/:\${srcdir}:/:/g +s/:@srcdir@:/:/g +s/^:*// +s/:*$// +x +s/\(=[ ]*\).*/\1/ +G +s/\n// +s/^[^=]*=[ ]*$// +}' +fi + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +fi # test -n "$CONFIG_FILES" + +# Set up the scripts for CONFIG_HEADERS section. +# No need to generate them if there are no CONFIG_HEADERS. +# This happens for instance with `./config.status Makefile'. +if test -n "$CONFIG_HEADERS"; then +cat >"$ac_tmp/defines.awk" <<\_ACAWK || +BEGIN { +_ACEOF + +# Transform confdefs.h into an awk script `defines.awk', embedded as +# here-document in config.status, that substitutes the proper values into +# config.h.in to produce config.h. + +# Create a delimiter string that does not exist in confdefs.h, to ease +# handling of long lines. +ac_delim='%!_!# ' +for ac_last_try in false false :; do + ac_tt=`sed -n "/$ac_delim/p" confdefs.h` + if test -z "$ac_tt"; then + break + elif $ac_last_try; then + as_fn_error $? "could not make $CONFIG_HEADERS" "$LINENO" 5 + else + ac_delim="$ac_delim!$ac_delim _$ac_delim!! " + fi +done + +# For the awk script, D is an array of macro values keyed by name, +# likewise P contains macro parameters if any. Preserve backslash +# newline sequences. + +ac_word_re=[_$as_cr_Letters][_$as_cr_alnum]* +sed -n ' +s/.\{148\}/&'"$ac_delim"'/g +t rset +:rset +s/^[ ]*#[ ]*define[ ][ ]*/ / +t def +d +:def +s/\\$// +t bsnl +s/["\\]/\\&/g +s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\ +D["\1"]=" \3"/p +s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2"/p +d +:bsnl +s/["\\]/\\&/g +s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\ +D["\1"]=" \3\\\\\\n"\\/p +t cont +s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2\\\\\\n"\\/p +t cont +d +:cont +n +s/.\{148\}/&'"$ac_delim"'/g +t clear +:clear +s/\\$// +t bsnlc +s/["\\]/\\&/g; s/^/"/; s/$/"/p +d +:bsnlc +s/["\\]/\\&/g; s/^/"/; s/$/\\\\\\n"\\/p +b cont +' >$CONFIG_STATUS || ac_write_fail=1 + +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 + for (key in D) D_is_set[key] = 1 + FS = "" +} +/^[\t ]*#[\t ]*(define|undef)[\t ]+$ac_word_re([\t (]|\$)/ { + line = \$ 0 + split(line, arg, " ") + if (arg[1] == "#") { + defundef = arg[2] + mac1 = arg[3] + } else { + defundef = substr(arg[1], 2) + mac1 = arg[2] + } + split(mac1, mac2, "(") #) + macro = mac2[1] + prefix = substr(line, 1, index(line, defundef) - 1) + if (D_is_set[macro]) { + # Preserve the white space surrounding the "#". + print prefix "define", macro P[macro] D[macro] + next + } else { + # Replace #undef with comments. This is necessary, for example, + # in the case of _POSIX_SOURCE, which is predefined and required + # on some systems where configure will not decide to define it. + if (defundef == "undef") { + print "/*", prefix defundef, macro, "*/" + next + } + } +} +{ print } +_ACAWK +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 + as_fn_error $? "could not setup config headers machinery" "$LINENO" 5 +fi # test -n "$CONFIG_HEADERS" + + +eval set X " :F $CONFIG_FILES :H $CONFIG_HEADERS :C $CONFIG_COMMANDS" +shift +for ac_tag +do + case $ac_tag in + :[FHLC]) ac_mode=$ac_tag; continue;; + esac + case $ac_mode$ac_tag in + :[FHL]*:*);; + :L* | :C*:*) as_fn_error $? "invalid tag \`$ac_tag'" "$LINENO" 5;; + :[FH]-) ac_tag=-:-;; + :[FH]*) ac_tag=$ac_tag:$ac_tag.in;; + esac + ac_save_IFS=$IFS + IFS=: + set x $ac_tag + IFS=$ac_save_IFS + shift + ac_file=$1 + shift + + case $ac_mode in + :L) ac_source=$1;; + :[FH]) + ac_file_inputs= + for ac_f + do + case $ac_f in + -) ac_f="$ac_tmp/stdin";; + *) # Look for the file first in the build tree, then in the source tree + # (if the path is not absolute). The absolute path cannot be DOS-style, + # because $ac_f cannot contain `:'. + test -f "$ac_f" || + case $ac_f in + [\\/$]*) false;; + *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";; + esac || + as_fn_error 1 "cannot find input file: \`$ac_f'" "$LINENO" 5;; + esac + case $ac_f in *\'*) ac_f=`$as_echo "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac + as_fn_append ac_file_inputs " '$ac_f'" + done + + # Let's still pretend it is `configure' which instantiates (i.e., don't + # use $as_me), people would be surprised to read: + # /* config.h. Generated by config.status. */ + configure_input='Generated from '` + $as_echo "$*" | sed 's|^[^:]*/||;s|:[^:]*/|, |g' + `' by configure.' + if test x"$ac_file" != x-; then + configure_input="$ac_file. $configure_input" + { $as_echo "$as_me:${as_lineno-$LINENO}: creating $ac_file" >&5 +$as_echo "$as_me: creating $ac_file" >&6;} + fi + # Neutralize special characters interpreted by sed in replacement strings. + case $configure_input in #( + *\&* | *\|* | *\\* ) + ac_sed_conf_input=`$as_echo "$configure_input" | + sed 's/[\\\\&|]/\\\\&/g'`;; #( + *) ac_sed_conf_input=$configure_input;; + esac + + case $ac_tag in + *:-:* | *:-) cat >"$ac_tmp/stdin" \ + || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;; + esac + ;; + esac + + ac_dir=`$as_dirname -- "$ac_file" || +$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$ac_file" : 'X\(//\)[^/]' \| \ + X"$ac_file" : 'X\(//\)$' \| \ + X"$ac_file" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$ac_file" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + as_dir="$ac_dir"; as_fn_mkdir_p + ac_builddir=. + +case "$ac_dir" in +.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; +*) + ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` + # A ".." for each directory in $ac_dir_suffix. + ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` + case $ac_top_builddir_sub in + "") ac_top_builddir_sub=. ac_top_build_prefix= ;; + *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; + esac ;; +esac +ac_abs_top_builddir=$ac_pwd +ac_abs_builddir=$ac_pwd$ac_dir_suffix +# for backward compatibility: +ac_top_builddir=$ac_top_build_prefix + +case $srcdir in + .) # We are building in place. + ac_srcdir=. + ac_top_srcdir=$ac_top_builddir_sub + ac_abs_top_srcdir=$ac_pwd ;; + [\\/]* | ?:[\\/]* ) # Absolute name. + ac_srcdir=$srcdir$ac_dir_suffix; + ac_top_srcdir=$srcdir + ac_abs_top_srcdir=$srcdir ;; + *) # Relative name. + ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix + ac_top_srcdir=$ac_top_build_prefix$srcdir + ac_abs_top_srcdir=$ac_pwd/$srcdir ;; +esac +ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix + + + case $ac_mode in + :F) + # + # CONFIG_FILE + # + + case $INSTALL in + [\\/$]* | ?:[\\/]* ) ac_INSTALL=$INSTALL ;; + *) ac_INSTALL=$ac_top_build_prefix$INSTALL ;; + esac + ac_MKDIR_P=$MKDIR_P + case $MKDIR_P in + [\\/$]* | ?:[\\/]* ) ;; + */*) ac_MKDIR_P=$ac_top_build_prefix$MKDIR_P ;; + esac +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +# If the template does not know about datarootdir, expand it. +# FIXME: This hack should be removed a few years after 2.60. +ac_datarootdir_hack=; ac_datarootdir_seen= +ac_sed_dataroot=' +/datarootdir/ { + p + q +} +/@datadir@/p +/@docdir@/p +/@infodir@/p +/@localedir@/p +/@mandir@/p' +case `eval "sed -n \"\$ac_sed_dataroot\" $ac_file_inputs"` in +*datarootdir*) ac_datarootdir_seen=yes;; +*@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*) + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5 +$as_echo "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;} +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 + ac_datarootdir_hack=' + s&@datadir@&$datadir&g + s&@docdir@&$docdir&g + s&@infodir@&$infodir&g + s&@localedir@&$localedir&g + s&@mandir@&$mandir&g + s&\\\${datarootdir}&$datarootdir&g' ;; +esac +_ACEOF + +# Neutralize VPATH when `$srcdir' = `.'. +# Shell code in configure.ac might set extrasub. +# FIXME: do we really want to maintain this feature? +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +ac_sed_extra="$ac_vpsub +$extrasub +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +:t +/@[a-zA-Z_][a-zA-Z_0-9]*@/!b +s|@configure_input@|$ac_sed_conf_input|;t t +s&@top_builddir@&$ac_top_builddir_sub&;t t +s&@top_build_prefix@&$ac_top_build_prefix&;t t +s&@srcdir@&$ac_srcdir&;t t +s&@abs_srcdir@&$ac_abs_srcdir&;t t +s&@top_srcdir@&$ac_top_srcdir&;t t +s&@abs_top_srcdir@&$ac_abs_top_srcdir&;t t +s&@builddir@&$ac_builddir&;t t +s&@abs_builddir@&$ac_abs_builddir&;t t +s&@abs_top_builddir@&$ac_abs_top_builddir&;t t +s&@INSTALL@&$ac_INSTALL&;t t +s&@MKDIR_P@&$ac_MKDIR_P&;t t +$ac_datarootdir_hack +" +eval sed \"\$ac_sed_extra\" "$ac_file_inputs" | $AWK -f "$ac_tmp/subs.awk" \ + >$ac_tmp/out || as_fn_error $? "could not create $ac_file" "$LINENO" 5 + +test -z "$ac_datarootdir_hack$ac_datarootdir_seen" && + { ac_out=`sed -n '/\${datarootdir}/p' "$ac_tmp/out"`; test -n "$ac_out"; } && + { ac_out=`sed -n '/^[ ]*datarootdir[ ]*:*=/p' \ + "$ac_tmp/out"`; test -z "$ac_out"; } && + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file contains a reference to the variable \`datarootdir' +which seems to be undefined. Please make sure it is defined" >&5 +$as_echo "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir' +which seems to be undefined. Please make sure it is defined" >&2;} + + rm -f "$ac_tmp/stdin" + case $ac_file in + -) cat "$ac_tmp/out" && rm -f "$ac_tmp/out";; + *) rm -f "$ac_file" && mv "$ac_tmp/out" "$ac_file";; + esac \ + || as_fn_error $? "could not create $ac_file" "$LINENO" 5 + ;; + :H) + # + # CONFIG_HEADER + # + if test x"$ac_file" != x-; then + { + $as_echo "/* $configure_input */" \ + && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" + } >"$ac_tmp/config.h" \ + || as_fn_error $? "could not create $ac_file" "$LINENO" 5 + if diff "$ac_file" "$ac_tmp/config.h" >/dev/null 2>&1; then + { $as_echo "$as_me:${as_lineno-$LINENO}: $ac_file is unchanged" >&5 +$as_echo "$as_me: $ac_file is unchanged" >&6;} + else + rm -f "$ac_file" + mv "$ac_tmp/config.h" "$ac_file" \ + || as_fn_error $? "could not create $ac_file" "$LINENO" 5 + fi + else + $as_echo "/* $configure_input */" \ + && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" \ + || as_fn_error $? "could not create -" "$LINENO" 5 + fi +# Compute "$ac_file"'s index in $config_headers. +_am_arg="$ac_file" +_am_stamp_count=1 +for _am_header in $config_headers :; do + case $_am_header in + $_am_arg | $_am_arg:* ) + break ;; + * ) + _am_stamp_count=`expr $_am_stamp_count + 1` ;; + esac +done +echo "timestamp for $_am_arg" >`$as_dirname -- "$_am_arg" || +$as_expr X"$_am_arg" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$_am_arg" : 'X\(//\)[^/]' \| \ + X"$_am_arg" : 'X\(//\)$' \| \ + X"$_am_arg" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$_am_arg" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'`/stamp-h$_am_stamp_count + ;; + + :C) { $as_echo "$as_me:${as_lineno-$LINENO}: executing $ac_file commands" >&5 +$as_echo "$as_me: executing $ac_file commands" >&6;} + ;; + esac + + + case $ac_file$ac_mode in + "libtool":C) + + # See if we are running on zsh, and set the options which allow our + # commands through without removal of \ escapes. + if test -n "${ZSH_VERSION+set}" ; then + setopt NO_GLOB_SUBST + fi + + cfgfile="${ofile}T" + trap "$RM \"$cfgfile\"; exit 1" 1 2 15 + $RM "$cfgfile" + + cat <<_LT_EOF >> "$cfgfile" +#! $SHELL + +# `$ECHO "$ofile" | sed 's%^.*/%%'` - Provide generalized library-building support services. +# Generated automatically by $as_me ($PACKAGE$TIMESTAMP) $VERSION +# Libtool was configured on host `(hostname || uname -n) 2>/dev/null | sed 1q`: +# NOTE: Changes made to this file will be lost: look at ltmain.sh. +# +# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2003, 2004, 2005, +# 2006, 2007, 2008, 2009, 2010, 2011 Free Software +# Foundation, Inc. +# Written by Gordon Matzigkeit, 1996 +# +# This file is part of GNU Libtool. +# +# GNU Libtool 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 2 of +# the License, or (at your option) any later version. +# +# As a special exception to the GNU General Public License, +# if you distribute this file as part of a program or library that +# is built using GNU Libtool, you may include this file under the +# same distribution terms that you use for the rest of that program. +# +# GNU Libtool 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 GNU Libtool; see the file COPYING. If not, a copy +# can be downloaded from http://www.gnu.org/licenses/gpl.html, or +# obtained by writing to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + +# The names of the tagged configurations supported by this script. +available_tags="CXX " + +# ### BEGIN LIBTOOL CONFIG + +# Which release of libtool.m4 was used? +macro_version=$macro_version +macro_revision=$macro_revision + +# Whether or not to build shared libraries. +build_libtool_libs=$enable_shared + +# Whether or not to build static libraries. +build_old_libs=$enable_static + +# What type of objects to build. +pic_mode=$pic_mode + +# Whether or not to optimize for fast installation. +fast_install=$enable_fast_install + +# Shell to use when invoking shell scripts. +SHELL=$lt_SHELL + +# An echo program that protects backslashes. +ECHO=$lt_ECHO + +# The PATH separator for the build system. +PATH_SEPARATOR=$lt_PATH_SEPARATOR + +# The host system. +host_alias=$host_alias +host=$host +host_os=$host_os + +# The build system. +build_alias=$build_alias +build=$build +build_os=$build_os + +# A sed program that does not truncate output. +SED=$lt_SED + +# Sed that helps us avoid accidentally triggering echo(1) options like -n. +Xsed="\$SED -e 1s/^X//" + +# A grep program that handles long lines. +GREP=$lt_GREP + +# An ERE matcher. +EGREP=$lt_EGREP + +# A literal string matcher. +FGREP=$lt_FGREP + +# A BSD- or MS-compatible name lister. +NM=$lt_NM + +# Whether we need soft or hard links. +LN_S=$lt_LN_S + +# What is the maximum length of a command? +max_cmd_len=$max_cmd_len + +# Object file suffix (normally "o"). +objext=$ac_objext + +# Executable file suffix (normally ""). +exeext=$exeext + +# whether the shell understands "unset". +lt_unset=$lt_unset + +# turn spaces into newlines. +SP2NL=$lt_lt_SP2NL + +# turn newlines into spaces. +NL2SP=$lt_lt_NL2SP + +# convert \$build file names to \$host format. +to_host_file_cmd=$lt_cv_to_host_file_cmd + +# convert \$build files to toolchain format. +to_tool_file_cmd=$lt_cv_to_tool_file_cmd + +# An object symbol dumper. +OBJDUMP=$lt_OBJDUMP + +# Method to check whether dependent libraries are shared objects. +deplibs_check_method=$lt_deplibs_check_method + +# Command to use when deplibs_check_method = "file_magic". +file_magic_cmd=$lt_file_magic_cmd + +# How to find potential files when deplibs_check_method = "file_magic". +file_magic_glob=$lt_file_magic_glob + +# Find potential files using nocaseglob when deplibs_check_method = "file_magic". +want_nocaseglob=$lt_want_nocaseglob + +# DLL creation program. +DLLTOOL=$lt_DLLTOOL + +# Command to associate shared and link libraries. +sharedlib_from_linklib_cmd=$lt_sharedlib_from_linklib_cmd + +# The archiver. +AR=$lt_AR + +# Flags to create an archive. +AR_FLAGS=$lt_AR_FLAGS + +# How to feed a file listing to the archiver. +archiver_list_spec=$lt_archiver_list_spec + +# A symbol stripping program. +STRIP=$lt_STRIP + +# Commands used to install an old-style archive. +RANLIB=$lt_RANLIB +old_postinstall_cmds=$lt_old_postinstall_cmds +old_postuninstall_cmds=$lt_old_postuninstall_cmds + +# Whether to use a lock for old archive extraction. +lock_old_archive_extraction=$lock_old_archive_extraction + +# A C compiler. +LTCC=$lt_CC + +# LTCC compiler flags. +LTCFLAGS=$lt_CFLAGS + +# Take the output of nm and produce a listing of raw symbols and C names. +global_symbol_pipe=$lt_lt_cv_sys_global_symbol_pipe + +# Transform the output of nm in a proper C declaration. +global_symbol_to_cdecl=$lt_lt_cv_sys_global_symbol_to_cdecl + +# Transform the output of nm in a C name address pair. +global_symbol_to_c_name_address=$lt_lt_cv_sys_global_symbol_to_c_name_address + +# Transform the output of nm in a C name address pair when lib prefix is needed. +global_symbol_to_c_name_address_lib_prefix=$lt_lt_cv_sys_global_symbol_to_c_name_address_lib_prefix + +# Specify filename containing input files for \$NM. +nm_file_list_spec=$lt_nm_file_list_spec + +# The root where to search for dependent libraries,and in which our libraries should be installed. +lt_sysroot=$lt_sysroot + +# The name of the directory that contains temporary libtool files. +objdir=$objdir + +# Used to examine libraries when file_magic_cmd begins with "file". +MAGIC_CMD=$MAGIC_CMD + +# Must we lock files when doing compilation? +need_locks=$lt_need_locks + +# Manifest tool. +MANIFEST_TOOL=$lt_MANIFEST_TOOL + +# Tool to manipulate archived DWARF debug symbol files on Mac OS X. +DSYMUTIL=$lt_DSYMUTIL + +# Tool to change global to local symbols on Mac OS X. +NMEDIT=$lt_NMEDIT + +# Tool to manipulate fat objects and archives on Mac OS X. +LIPO=$lt_LIPO + +# ldd/readelf like tool for Mach-O binaries on Mac OS X. +OTOOL=$lt_OTOOL + +# ldd/readelf like tool for 64 bit Mach-O binaries on Mac OS X 10.4. +OTOOL64=$lt_OTOOL64 + +# Old archive suffix (normally "a"). +libext=$libext + +# Shared library suffix (normally ".so"). +shrext_cmds=$lt_shrext_cmds + +# The commands to extract the exported symbol list from a shared archive. +extract_expsyms_cmds=$lt_extract_expsyms_cmds + +# Variables whose values should be saved in libtool wrapper scripts and +# restored at link time. +variables_saved_for_relink=$lt_variables_saved_for_relink + +# Do we need the "lib" prefix for modules? +need_lib_prefix=$need_lib_prefix + +# Do we need a version for libraries? +need_version=$need_version + +# Library versioning type. +version_type=$version_type + +# Shared library runtime path variable. +runpath_var=$runpath_var + +# Shared library path variable. +shlibpath_var=$shlibpath_var + +# Is shlibpath searched before the hard-coded library search path? +shlibpath_overrides_runpath=$shlibpath_overrides_runpath + +# Format of library name prefix. +libname_spec=$lt_libname_spec + +# List of archive names. First name is the real one, the rest are links. +# The last name is the one that the linker finds with -lNAME +library_names_spec=$lt_library_names_spec + +# The coded name of the library, if different from the real name. +soname_spec=$lt_soname_spec + +# Permission mode override for installation of shared libraries. +install_override_mode=$lt_install_override_mode + +# Command to use after installation of a shared archive. +postinstall_cmds=$lt_postinstall_cmds + +# Command to use after uninstallation of a shared archive. +postuninstall_cmds=$lt_postuninstall_cmds + +# Commands used to finish a libtool library installation in a directory. +finish_cmds=$lt_finish_cmds + +# As "finish_cmds", except a single script fragment to be evaled but +# not shown. +finish_eval=$lt_finish_eval + +# Whether we should hardcode library paths into libraries. +hardcode_into_libs=$hardcode_into_libs + +# Compile-time system search path for libraries. +sys_lib_search_path_spec=$lt_sys_lib_search_path_spec + +# Run-time system search path for libraries. +sys_lib_dlsearch_path_spec=$lt_sys_lib_dlsearch_path_spec + +# Whether dlopen is supported. +dlopen_support=$enable_dlopen + +# Whether dlopen of programs is supported. +dlopen_self=$enable_dlopen_self + +# Whether dlopen of statically linked programs is supported. +dlopen_self_static=$enable_dlopen_self_static + +# Commands to strip libraries. +old_striplib=$lt_old_striplib +striplib=$lt_striplib + + +# The linker used to build libraries. +LD=$lt_LD + +# How to create reloadable object files. +reload_flag=$lt_reload_flag +reload_cmds=$lt_reload_cmds + +# Commands used to build an old-style archive. +old_archive_cmds=$lt_old_archive_cmds + +# A language specific compiler. +CC=$lt_compiler + +# Is the compiler the GNU compiler? +with_gcc=$GCC + +# Compiler flag to turn off builtin functions. +no_builtin_flag=$lt_lt_prog_compiler_no_builtin_flag + +# Additional compiler flags for building library objects. +pic_flag=$lt_lt_prog_compiler_pic + +# How to pass a linker flag through the compiler. +wl=$lt_lt_prog_compiler_wl + +# Compiler flag to prevent dynamic linking. +link_static_flag=$lt_lt_prog_compiler_static + +# Does compiler simultaneously support -c and -o options? +compiler_c_o=$lt_lt_cv_prog_compiler_c_o + +# Whether or not to add -lc for building shared libraries. +build_libtool_need_lc=$archive_cmds_need_lc + +# Whether or not to disallow shared libs when runtime libs are static. +allow_libtool_libs_with_static_runtimes=$enable_shared_with_static_runtimes + +# Compiler flag to allow reflexive dlopens. +export_dynamic_flag_spec=$lt_export_dynamic_flag_spec + +# Compiler flag to generate shared objects directly from archives. +whole_archive_flag_spec=$lt_whole_archive_flag_spec + +# Whether the compiler copes with passing no objects directly. +compiler_needs_object=$lt_compiler_needs_object + +# Create an old-style archive from a shared archive. +old_archive_from_new_cmds=$lt_old_archive_from_new_cmds + +# Create a temporary old-style archive to link instead of a shared archive. +old_archive_from_expsyms_cmds=$lt_old_archive_from_expsyms_cmds + +# Commands used to build a shared archive. +archive_cmds=$lt_archive_cmds +archive_expsym_cmds=$lt_archive_expsym_cmds + +# Commands used to build a loadable module if different from building +# a shared archive. +module_cmds=$lt_module_cmds +module_expsym_cmds=$lt_module_expsym_cmds + +# Whether we are building with GNU ld or not. +with_gnu_ld=$lt_with_gnu_ld + +# Flag that allows shared libraries with undefined symbols to be built. +allow_undefined_flag=$lt_allow_undefined_flag + +# Flag that enforces no undefined symbols. +no_undefined_flag=$lt_no_undefined_flag + +# Flag to hardcode \$libdir into a binary during linking. +# This must work even if \$libdir does not exist +hardcode_libdir_flag_spec=$lt_hardcode_libdir_flag_spec + +# Whether we need a single "-rpath" flag with a separated argument. +hardcode_libdir_separator=$lt_hardcode_libdir_separator + +# Set to "yes" if using DIR/libNAME\${shared_ext} during linking hardcodes +# DIR into the resulting binary. +hardcode_direct=$hardcode_direct + +# Set to "yes" if using DIR/libNAME\${shared_ext} during linking hardcodes +# DIR into the resulting binary and the resulting library dependency is +# "absolute",i.e impossible to change by setting \${shlibpath_var} if the +# library is relocated. +hardcode_direct_absolute=$hardcode_direct_absolute + +# Set to "yes" if using the -LDIR flag during linking hardcodes DIR +# into the resulting binary. +hardcode_minus_L=$hardcode_minus_L + +# Set to "yes" if using SHLIBPATH_VAR=DIR during linking hardcodes DIR +# into the resulting binary. +hardcode_shlibpath_var=$hardcode_shlibpath_var + +# Set to "yes" if building a shared library automatically hardcodes DIR +# into the library and all subsequent libraries and executables linked +# against it. +hardcode_automatic=$hardcode_automatic + +# Set to yes if linker adds runtime paths of dependent libraries +# to runtime path list. +inherit_rpath=$inherit_rpath + +# Whether libtool must link a program against all its dependency libraries. +link_all_deplibs=$link_all_deplibs + +# Set to "yes" if exported symbols are required. +always_export_symbols=$always_export_symbols + +# The commands to list exported symbols. +export_symbols_cmds=$lt_export_symbols_cmds + +# Symbols that should not be listed in the preloaded symbols. +exclude_expsyms=$lt_exclude_expsyms + +# Symbols that must always be exported. +include_expsyms=$lt_include_expsyms + +# Commands necessary for linking programs (against libraries) with templates. +prelink_cmds=$lt_prelink_cmds + +# Commands necessary for finishing linking programs. +postlink_cmds=$lt_postlink_cmds + +# Specify filename containing input files. +file_list_spec=$lt_file_list_spec + +# How to hardcode a shared library path into an executable. +hardcode_action=$hardcode_action + +# The directories searched by this compiler when creating a shared library. +compiler_lib_search_dirs=$lt_compiler_lib_search_dirs + +# Dependencies to place before and after the objects being linked to +# create a shared library. +predep_objects=$lt_predep_objects +postdep_objects=$lt_postdep_objects +predeps=$lt_predeps +postdeps=$lt_postdeps + +# The library search path used internally by the compiler when linking +# a shared library. +compiler_lib_search_path=$lt_compiler_lib_search_path + +# ### END LIBTOOL CONFIG + +_LT_EOF + + case $host_os in + aix3*) + cat <<\_LT_EOF >> "$cfgfile" +# AIX sometimes has problems with the GCC collect2 program. For some +# reason, if we set the COLLECT_NAMES environment variable, the problems +# vanish in a puff of smoke. +if test "X${COLLECT_NAMES+set}" != Xset; then + COLLECT_NAMES= + export COLLECT_NAMES +fi +_LT_EOF + ;; + esac + + +ltmain="$ac_aux_dir/ltmain.sh" + + + # We use sed instead of cat because bash on DJGPP gets confused if + # if finds mixed CR/LF and LF-only lines. Since sed operates in + # text mode, it properly converts lines to CR/LF. This bash problem + # is reportedly fixed, but why not run on old versions too? + sed '$q' "$ltmain" >> "$cfgfile" \ + || (rm -f "$cfgfile"; exit 1) + + if test x"$xsi_shell" = xyes; then + sed -e '/^func_dirname ()$/,/^} # func_dirname /c\ +func_dirname ()\ +{\ +\ case ${1} in\ +\ */*) func_dirname_result="${1%/*}${2}" ;;\ +\ * ) func_dirname_result="${3}" ;;\ +\ esac\ +} # Extended-shell func_dirname implementation' "$cfgfile" > $cfgfile.tmp \ + && mv -f "$cfgfile.tmp" "$cfgfile" \ + || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") +test 0 -eq $? || _lt_function_replace_fail=: + + + sed -e '/^func_basename ()$/,/^} # func_basename /c\ +func_basename ()\ +{\ +\ func_basename_result="${1##*/}"\ +} # Extended-shell func_basename implementation' "$cfgfile" > $cfgfile.tmp \ + && mv -f "$cfgfile.tmp" "$cfgfile" \ + || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") +test 0 -eq $? || _lt_function_replace_fail=: + + + sed -e '/^func_dirname_and_basename ()$/,/^} # func_dirname_and_basename /c\ +func_dirname_and_basename ()\ +{\ +\ case ${1} in\ +\ */*) func_dirname_result="${1%/*}${2}" ;;\ +\ * ) func_dirname_result="${3}" ;;\ +\ esac\ +\ func_basename_result="${1##*/}"\ +} # Extended-shell func_dirname_and_basename implementation' "$cfgfile" > $cfgfile.tmp \ + && mv -f "$cfgfile.tmp" "$cfgfile" \ + || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") +test 0 -eq $? || _lt_function_replace_fail=: + + + sed -e '/^func_stripname ()$/,/^} # func_stripname /c\ +func_stripname ()\ +{\ +\ # pdksh 5.2.14 does not do ${X%$Y} correctly if both X and Y are\ +\ # positional parameters, so assign one to ordinary parameter first.\ +\ func_stripname_result=${3}\ +\ func_stripname_result=${func_stripname_result#"${1}"}\ +\ func_stripname_result=${func_stripname_result%"${2}"}\ +} # Extended-shell func_stripname implementation' "$cfgfile" > $cfgfile.tmp \ + && mv -f "$cfgfile.tmp" "$cfgfile" \ + || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") +test 0 -eq $? || _lt_function_replace_fail=: + + + sed -e '/^func_split_long_opt ()$/,/^} # func_split_long_opt /c\ +func_split_long_opt ()\ +{\ +\ func_split_long_opt_name=${1%%=*}\ +\ func_split_long_opt_arg=${1#*=}\ +} # Extended-shell func_split_long_opt implementation' "$cfgfile" > $cfgfile.tmp \ + && mv -f "$cfgfile.tmp" "$cfgfile" \ + || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") +test 0 -eq $? || _lt_function_replace_fail=: + + + sed -e '/^func_split_short_opt ()$/,/^} # func_split_short_opt /c\ +func_split_short_opt ()\ +{\ +\ func_split_short_opt_arg=${1#??}\ +\ func_split_short_opt_name=${1%"$func_split_short_opt_arg"}\ +} # Extended-shell func_split_short_opt implementation' "$cfgfile" > $cfgfile.tmp \ + && mv -f "$cfgfile.tmp" "$cfgfile" \ + || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") +test 0 -eq $? || _lt_function_replace_fail=: + + + sed -e '/^func_lo2o ()$/,/^} # func_lo2o /c\ +func_lo2o ()\ +{\ +\ case ${1} in\ +\ *.lo) func_lo2o_result=${1%.lo}.${objext} ;;\ +\ *) func_lo2o_result=${1} ;;\ +\ esac\ +} # Extended-shell func_lo2o implementation' "$cfgfile" > $cfgfile.tmp \ + && mv -f "$cfgfile.tmp" "$cfgfile" \ + || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") +test 0 -eq $? || _lt_function_replace_fail=: + + + sed -e '/^func_xform ()$/,/^} # func_xform /c\ +func_xform ()\ +{\ + func_xform_result=${1%.*}.lo\ +} # Extended-shell func_xform implementation' "$cfgfile" > $cfgfile.tmp \ + && mv -f "$cfgfile.tmp" "$cfgfile" \ + || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") +test 0 -eq $? || _lt_function_replace_fail=: + + + sed -e '/^func_arith ()$/,/^} # func_arith /c\ +func_arith ()\ +{\ + func_arith_result=$(( $* ))\ +} # Extended-shell func_arith implementation' "$cfgfile" > $cfgfile.tmp \ + && mv -f "$cfgfile.tmp" "$cfgfile" \ + || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") +test 0 -eq $? || _lt_function_replace_fail=: + + + sed -e '/^func_len ()$/,/^} # func_len /c\ +func_len ()\ +{\ + func_len_result=${#1}\ +} # Extended-shell func_len implementation' "$cfgfile" > $cfgfile.tmp \ + && mv -f "$cfgfile.tmp" "$cfgfile" \ + || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") +test 0 -eq $? || _lt_function_replace_fail=: + +fi + +if test x"$lt_shell_append" = xyes; then + sed -e '/^func_append ()$/,/^} # func_append /c\ +func_append ()\ +{\ + eval "${1}+=\\${2}"\ +} # Extended-shell func_append implementation' "$cfgfile" > $cfgfile.tmp \ + && mv -f "$cfgfile.tmp" "$cfgfile" \ + || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") +test 0 -eq $? || _lt_function_replace_fail=: + + + sed -e '/^func_append_quoted ()$/,/^} # func_append_quoted /c\ +func_append_quoted ()\ +{\ +\ func_quote_for_eval "${2}"\ +\ eval "${1}+=\\\\ \\$func_quote_for_eval_result"\ +} # Extended-shell func_append_quoted implementation' "$cfgfile" > $cfgfile.tmp \ + && mv -f "$cfgfile.tmp" "$cfgfile" \ + || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") +test 0 -eq $? || _lt_function_replace_fail=: + + + # Save a `func_append' function call where possible by direct use of '+=' + sed -e 's%func_append \([a-zA-Z_]\{1,\}\) "%\1+="%g' $cfgfile > $cfgfile.tmp \ + && mv -f "$cfgfile.tmp" "$cfgfile" \ + || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") + test 0 -eq $? || _lt_function_replace_fail=: +else + # Save a `func_append' function call even when '+=' is not available + sed -e 's%func_append \([a-zA-Z_]\{1,\}\) "%\1="$\1%g' $cfgfile > $cfgfile.tmp \ + && mv -f "$cfgfile.tmp" "$cfgfile" \ + || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") + test 0 -eq $? || _lt_function_replace_fail=: +fi + +if test x"$_lt_function_replace_fail" = x":"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Unable to substitute extended shell functions in $ofile" >&5 +$as_echo "$as_me: WARNING: Unable to substitute extended shell functions in $ofile" >&2;} +fi + + + mv -f "$cfgfile" "$ofile" || + (rm -f "$ofile" && cp "$cfgfile" "$ofile" && rm -f "$cfgfile") + chmod +x "$ofile" + + + cat <<_LT_EOF >> "$ofile" + +# ### BEGIN LIBTOOL TAG CONFIG: CXX + +# The linker used to build libraries. +LD=$lt_LD_CXX + +# How to create reloadable object files. +reload_flag=$lt_reload_flag_CXX +reload_cmds=$lt_reload_cmds_CXX + +# Commands used to build an old-style archive. +old_archive_cmds=$lt_old_archive_cmds_CXX + +# A language specific compiler. +CC=$lt_compiler_CXX + +# Is the compiler the GNU compiler? +with_gcc=$GCC_CXX + +# Compiler flag to turn off builtin functions. +no_builtin_flag=$lt_lt_prog_compiler_no_builtin_flag_CXX + +# Additional compiler flags for building library objects. +pic_flag=$lt_lt_prog_compiler_pic_CXX + +# How to pass a linker flag through the compiler. +wl=$lt_lt_prog_compiler_wl_CXX + +# Compiler flag to prevent dynamic linking. +link_static_flag=$lt_lt_prog_compiler_static_CXX + +# Does compiler simultaneously support -c and -o options? +compiler_c_o=$lt_lt_cv_prog_compiler_c_o_CXX + +# Whether or not to add -lc for building shared libraries. +build_libtool_need_lc=$archive_cmds_need_lc_CXX + +# Whether or not to disallow shared libs when runtime libs are static. +allow_libtool_libs_with_static_runtimes=$enable_shared_with_static_runtimes_CXX + +# Compiler flag to allow reflexive dlopens. +export_dynamic_flag_spec=$lt_export_dynamic_flag_spec_CXX + +# Compiler flag to generate shared objects directly from archives. +whole_archive_flag_spec=$lt_whole_archive_flag_spec_CXX + +# Whether the compiler copes with passing no objects directly. +compiler_needs_object=$lt_compiler_needs_object_CXX + +# Create an old-style archive from a shared archive. +old_archive_from_new_cmds=$lt_old_archive_from_new_cmds_CXX + +# Create a temporary old-style archive to link instead of a shared archive. +old_archive_from_expsyms_cmds=$lt_old_archive_from_expsyms_cmds_CXX + +# Commands used to build a shared archive. +archive_cmds=$lt_archive_cmds_CXX +archive_expsym_cmds=$lt_archive_expsym_cmds_CXX + +# Commands used to build a loadable module if different from building +# a shared archive. +module_cmds=$lt_module_cmds_CXX +module_expsym_cmds=$lt_module_expsym_cmds_CXX + +# Whether we are building with GNU ld or not. +with_gnu_ld=$lt_with_gnu_ld_CXX + +# Flag that allows shared libraries with undefined symbols to be built. +allow_undefined_flag=$lt_allow_undefined_flag_CXX + +# Flag that enforces no undefined symbols. +no_undefined_flag=$lt_no_undefined_flag_CXX + +# Flag to hardcode \$libdir into a binary during linking. +# This must work even if \$libdir does not exist +hardcode_libdir_flag_spec=$lt_hardcode_libdir_flag_spec_CXX + +# Whether we need a single "-rpath" flag with a separated argument. +hardcode_libdir_separator=$lt_hardcode_libdir_separator_CXX + +# Set to "yes" if using DIR/libNAME\${shared_ext} during linking hardcodes +# DIR into the resulting binary. +hardcode_direct=$hardcode_direct_CXX + +# Set to "yes" if using DIR/libNAME\${shared_ext} during linking hardcodes +# DIR into the resulting binary and the resulting library dependency is +# "absolute",i.e impossible to change by setting \${shlibpath_var} if the +# library is relocated. +hardcode_direct_absolute=$hardcode_direct_absolute_CXX + +# Set to "yes" if using the -LDIR flag during linking hardcodes DIR +# into the resulting binary. +hardcode_minus_L=$hardcode_minus_L_CXX + +# Set to "yes" if using SHLIBPATH_VAR=DIR during linking hardcodes DIR +# into the resulting binary. +hardcode_shlibpath_var=$hardcode_shlibpath_var_CXX + +# Set to "yes" if building a shared library automatically hardcodes DIR +# into the library and all subsequent libraries and executables linked +# against it. +hardcode_automatic=$hardcode_automatic_CXX + +# Set to yes if linker adds runtime paths of dependent libraries +# to runtime path list. +inherit_rpath=$inherit_rpath_CXX + +# Whether libtool must link a program against all its dependency libraries. +link_all_deplibs=$link_all_deplibs_CXX + +# Set to "yes" if exported symbols are required. +always_export_symbols=$always_export_symbols_CXX + +# The commands to list exported symbols. +export_symbols_cmds=$lt_export_symbols_cmds_CXX + +# Symbols that should not be listed in the preloaded symbols. +exclude_expsyms=$lt_exclude_expsyms_CXX + +# Symbols that must always be exported. +include_expsyms=$lt_include_expsyms_CXX + +# Commands necessary for linking programs (against libraries) with templates. +prelink_cmds=$lt_prelink_cmds_CXX + +# Commands necessary for finishing linking programs. +postlink_cmds=$lt_postlink_cmds_CXX + +# Specify filename containing input files. +file_list_spec=$lt_file_list_spec_CXX + +# How to hardcode a shared library path into an executable. +hardcode_action=$hardcode_action_CXX + +# The directories searched by this compiler when creating a shared library. +compiler_lib_search_dirs=$lt_compiler_lib_search_dirs_CXX + +# Dependencies to place before and after the objects being linked to +# create a shared library. +predep_objects=$lt_predep_objects_CXX +postdep_objects=$lt_postdep_objects_CXX +predeps=$lt_predeps_CXX +postdeps=$lt_postdeps_CXX + +# The library search path used internally by the compiler when linking +# a shared library. +compiler_lib_search_path=$lt_compiler_lib_search_path_CXX + +# ### END LIBTOOL TAG CONFIG: CXX +_LT_EOF + + ;; + "depfiles":C) test x"$AMDEP_TRUE" != x"" || { + # Older Autoconf quotes --file arguments for eval, but not when files + # are listed without --file. Let's play safe and only enable the eval + # if we detect the quoting. + case $CONFIG_FILES in + *\'*) eval set x "$CONFIG_FILES" ;; + *) set x $CONFIG_FILES ;; + esac + shift + for mf + do + # Strip MF so we end up with the name of the file. + mf=`echo "$mf" | sed -e 's/:.*$//'` + # Check whether this is an Automake generated Makefile or not. + # We used to match only the files named 'Makefile.in', but + # some people rename them; so instead we look at the file content. + # Grep'ing the first line is not enough: some people post-process + # each Makefile.in and add a new line on top of each file to say so. + # Grep'ing the whole file is not good either: AIX grep has a line + # limit of 2048, but all sed's we know have understand at least 4000. + if sed -n 's,^#.*generated by automake.*,X,p' "$mf" | grep X >/dev/null 2>&1; then + dirpart=`$as_dirname -- "$mf" || +$as_expr X"$mf" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$mf" : 'X\(//\)[^/]' \| \ + X"$mf" : 'X\(//\)$' \| \ + X"$mf" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$mf" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + else + continue + fi + # Extract the definition of DEPDIR, am__include, and am__quote + # from the Makefile without running 'make'. + DEPDIR=`sed -n 's/^DEPDIR = //p' < "$mf"` + test -z "$DEPDIR" && continue + am__include=`sed -n 's/^am__include = //p' < "$mf"` + test -z "$am__include" && continue + am__quote=`sed -n 's/^am__quote = //p' < "$mf"` + # Find all dependency output files, they are included files with + # $(DEPDIR) in their names. We invoke sed twice because it is the + # simplest approach to changing $(DEPDIR) to its actual value in the + # expansion. + for file in `sed -n " + s/^$am__include $am__quote\(.*(DEPDIR).*\)$am__quote"'$/\1/p' <"$mf" | \ + sed -e 's/\$(DEPDIR)/'"$DEPDIR"'/g'`; do + # Make sure the directory exists. + test -f "$dirpart/$file" && continue + fdir=`$as_dirname -- "$file" || +$as_expr X"$file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$file" : 'X\(//\)[^/]' \| \ + X"$file" : 'X\(//\)$' \| \ + X"$file" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$file" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + as_dir=$dirpart/$fdir; as_fn_mkdir_p + # echo "creating $dirpart/$file" + echo '# dummy' > "$dirpart/$file" + done + done +} + ;; + + esac +done # for ac_tag + + +as_fn_exit 0 +_ACEOF +ac_clean_files=$ac_clean_files_save + +test $ac_write_fail = 0 || + as_fn_error $? "write failure creating $CONFIG_STATUS" "$LINENO" 5 + + +# configure is writing to config.log, and then calls config.status. +# config.status does its own redirection, appending to config.log. +# Unfortunately, on DOS this fails, as config.log is still kept open +# by configure, so config.status won't be able to write to it; its +# output is simply discarded. So we exec the FD to /dev/null, +# effectively closing config.log, so it can be properly (re)opened and +# appended to by config.status. When coming back to configure, we +# need to make the FD available again. +if test "$no_create" != yes; then + ac_cs_success=: + ac_config_status_args= + test "$silent" = yes && + ac_config_status_args="$ac_config_status_args --quiet" + exec 5>/dev/null + $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false + exec 5>>config.log + # Use ||, not &&, to avoid exiting from the if with $? = 1, which + # would make configure fail if this is the last instruction. + $ac_cs_success || as_fn_exit 1 +fi +if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: unrecognized options: $ac_unrecognized_opts" >&5 +$as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;} +fi + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: summary of build options: + + Version: ${VERSION} shared $LT_CURRENT:$LT_REVISION:$LT_AGE + Host type: ${host} + Install prefix: ${prefix} + C compiler: ${CC} + CFLAGS: ${CFLAGS} + WARNCFLAGS: ${WARNCFLAGS} + LDFLAGS: ${LDFLAGS} + LIBS: ${LIBS} + CPPFLAGS: ${CPPFLAGS} + C preprocessor: ${CPP} + C++ compiler: ${CXX} + CXXFLAGS: ${CXXFLAGS} + CXXCPP: ${CXXCPP} + Library types: Shared=${enable_shared}, Static=${enable_static} + Python: + Python: ${PYTHON} + PYTHON_VERSION: ${PYTHON_VERSION} + pyexecdir: ${pyexecdir} + Python-dev: ${have_python_dev} + PYTHON_CPPFLAGS:${PYTHON_CPPFLAGS} + PYTHON_LDFLAGS: ${PYTHON_LDFLAGS} + Cython: ${CYTHON} + Test: + CUnit: ${have_cunit} + Failmalloc: ${enable_failmalloc} + Libs: + OpenSSL: ${have_openssl} + Libxml2: ${have_libxml2} + Libev: ${have_libev} + Libevent(SSL): ${have_libevent_openssl} + Spdylay: ${have_spdylay} + Jansson: ${have_jansson} + Jemalloc: ${have_jemalloc} + Boost CPPFLAGS: ${BOOST_CPPFLAGS} + Boost LDFLAGS: ${BOOST_LDFLAGS} + Boost::ASIO: ${BOOST_ASIO_LIB} + Boost::System: ${BOOST_SYSTEM_LIB} + Boost::Thread: ${BOOST_THREAD_LIB} + Features: + Applications: ${enable_app} + HPACK tools: ${enable_hpack_tools} + Libnghttp2_asio:${enable_asio_lib} + Examples: ${enable_examples} + Python bindings:${enable_python_bindings} + Threading: ${enable_threads} +" >&5 +$as_echo "$as_me: summary of build options: + + Version: ${VERSION} shared $LT_CURRENT:$LT_REVISION:$LT_AGE + Host type: ${host} + Install prefix: ${prefix} + C compiler: ${CC} + CFLAGS: ${CFLAGS} + WARNCFLAGS: ${WARNCFLAGS} + LDFLAGS: ${LDFLAGS} + LIBS: ${LIBS} + CPPFLAGS: ${CPPFLAGS} + C preprocessor: ${CPP} + C++ compiler: ${CXX} + CXXFLAGS: ${CXXFLAGS} + CXXCPP: ${CXXCPP} + Library types: Shared=${enable_shared}, Static=${enable_static} + Python: + Python: ${PYTHON} + PYTHON_VERSION: ${PYTHON_VERSION} + pyexecdir: ${pyexecdir} + Python-dev: ${have_python_dev} + PYTHON_CPPFLAGS:${PYTHON_CPPFLAGS} + PYTHON_LDFLAGS: ${PYTHON_LDFLAGS} + Cython: ${CYTHON} + Test: + CUnit: ${have_cunit} + Failmalloc: ${enable_failmalloc} + Libs: + OpenSSL: ${have_openssl} + Libxml2: ${have_libxml2} + Libev: ${have_libev} + Libevent(SSL): ${have_libevent_openssl} + Spdylay: ${have_spdylay} + Jansson: ${have_jansson} + Jemalloc: ${have_jemalloc} + Boost CPPFLAGS: ${BOOST_CPPFLAGS} + Boost LDFLAGS: ${BOOST_LDFLAGS} + Boost::ASIO: ${BOOST_ASIO_LIB} + Boost::System: ${BOOST_SYSTEM_LIB} + Boost::Thread: ${BOOST_THREAD_LIB} + Features: + Applications: ${enable_app} + HPACK tools: ${enable_hpack_tools} + Libnghttp2_asio:${enable_asio_lib} + Examples: ${enable_examples} + Python bindings:${enable_python_bindings} + Threading: ${enable_threads} +" >&6;} diff --git a/configure.ac b/configure.ac new file mode 100644 index 0000000..94d099d --- /dev/null +++ b/configure.ac @@ -0,0 +1,758 @@ +dnl nghttp2 - HTTP/2 C Library + +dnl Copyright (c) 2012, 2013, 2014, 2015 Tatsuhiro Tsujikawa + +dnl Permission is hereby granted, free of charge, to any person obtaining +dnl a copy of this software and associated documentation files (the +dnl "Software"), to deal in the Software without restriction, including +dnl without limitation the rights to use, copy, modify, merge, publish, +dnl distribute, sublicense, and/or sell copies of the Software, and to +dnl permit persons to whom the Software is furnished to do so, subject to +dnl the following conditions: + +dnl The above copyright notice and this permission notice shall be +dnl included in all copies or substantial portions of the Software. + +dnl THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +dnl EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +dnl MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +dnl NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +dnl LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +dnl OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +dnl WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +dnl Do not change user variables! +dnl http://www.gnu.org/software/automake/manual/html_node/Flag-Variables-Ordering.html + +AC_PREREQ(2.61) +AC_INIT([nghttp2], [1.0.0], [t-tujikawa@users.sourceforge.net]) +AC_USE_SYSTEM_EXTENSIONS + +LT_PREREQ([2.2.6]) +LT_INIT() + +AC_CANONICAL_BUILD +AC_CANONICAL_HOST +AC_CANONICAL_TARGET + +AM_INIT_AUTOMAKE([subdir-objects]) +# comment out for now since this requires automake 1.13 or higher and +# travis has older one. +# AM_EXTRA_RECURSIVE_TARGETS([it]) + +m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) + +AC_CONFIG_MACRO_DIR([m4]) +AC_CONFIG_HEADERS([config.h]) + +dnl See versioning rule: +dnl http://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html +AC_SUBST(LT_CURRENT, 14) +AC_SUBST(LT_REVISION, 0) +AC_SUBST(LT_AGE, 0) + +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) + +dnl Checks for command-line options +AC_ARG_ENABLE([werror], + [AS_HELP_STRING([--enable-werror], + [Turn on compile time warnings])], + [werror=$enableval], [werror=no]) + +AC_ARG_ENABLE([debug], + [AS_HELP_STRING([--enable-debug], + [Turn on debug output])], + [debug=$enableval], [debug=no]) + +AC_ARG_ENABLE([threads], + [AS_HELP_STRING([--disable-threads], + [Turn off threading in apps])], + [threads=$enableval], [threads=yes]) + +AC_ARG_ENABLE([app], + [AS_HELP_STRING([--enable-app], + [Build applications (nghttp, nghttpd and nghttpx) [default=check]])], + [request_app=$enableval], [request_app=check]) + +AC_ARG_ENABLE([hpack-tools], + [AS_HELP_STRING([--enable-hpack-tools], + [Build HPACK tools [default=check]])], + [request_hpack_tools=$enableval], [request_hpack_tools=check]) + +AC_ARG_ENABLE([asio-lib], + [AS_HELP_STRING([--enable-asio-lib], + [Build C++ libnghttp2_asio library [default=no]])], + [request_asio_lib=$enableval], [request_asio_lib=no]) + +AC_ARG_ENABLE([examples], + [AS_HELP_STRING([--enable-examples], + [Build examples [default=check]])], + [request_examples=$enableval], [request_examples=check]) + +AC_ARG_ENABLE([python-bindings], + [AS_HELP_STRING([--enable-python-bindings], + [Build Python bindings [default=check]])], + [request_python_bindings=$enableval], [request_python_bindings=check]) + +AC_ARG_ENABLE([failmalloc], + [AS_HELP_STRING([--disable-failmalloc], + [Do not build failmalloc test program])], + [request_failmalloc=$enableval], [request_failmalloc=yes]) + +AC_ARG_WITH([libxml2], + [AS_HELP_STRING([--with-libxml2], + [Use libxml2 [default=check]])], + [request_libxml2=$withval], [request_libxml2=check]) + +AC_ARG_WITH([jemalloc], + [AS_HELP_STRING([--with-jemalloc], + [Use jemalloc [default=check]])], + [request_jemalloc=$withval], [request_jemalloc=check]) + +AC_ARG_WITH([spdylay], + [AS_HELP_STRING([--with-spdylay], + [Use spdylay [default=check]])], + [request_spdylay=$withval], [request_spdylay=check]) + +AC_ARG_WITH([cython], + [AS_HELP_STRING([--with-cython=PATH], + [Use cython in given PATH])], + [cython_path=$withval], []) + +dnl Define variables +AC_ARG_VAR([CYTHON], [the Cython executable]) + +dnl Checks for programs +AC_PROG_CC +AC_PROG_CXX +AC_PROG_CPP +AC_PROG_INSTALL +AC_PROG_LN_S +AC_PROG_MAKE_SET +AC_PROG_MKDIR_P + +PKG_PROG_PKG_CONFIG([0.20]) + +if [test "x$request_python_bindings" != "xno"]; then + AM_PATH_PYTHON([2.7],, [:]) + AX_PYTHON_DEVEL([>= '2.7']) +fi + +if test "x${cython_path}" = "x"; then + AC_CHECK_PROGS([CYTHON], [cython.py cython]) +else + CYTHON=${cython_path} + AC_SUBST([CYTHON]) +fi + +# +# If we're running GCC or clang define _U_ to be "__attribute__((unused))" +# so we can use _U_ to flag unused function parameters and not get warnings +# about them. Otherwise, define _U_ to be an empty string so that _U_ used +# to flag an unused function parameters will compile with other compilers. +# +# XXX - similar hints for other compilers? +# +if test "x$GCC" = "xyes" -o "x$CC" = "xclang" ; then + AC_DEFINE([_U_], [__attribute__((unused))], [Hint to the compiler that a function parameters is not used]) +else + AC_DEFINE([_U_], , [Hint to the compiler that a function parameters is not used]) +fi + +AX_CXX_COMPILE_STDCXX_11([noext], [optional]) + +AC_LANG_PUSH(C++) + +# Check that std::chrono::steady_clock is available. In particular, +# gcc 4.6 does not have one, but has monotonic_clock which is the old +# name existed in the pre-standard draft. If steady_clock is not +# available, don't define HAVE_STEADY_CLOCK and replace steady_clock +# with monotonic_clock. +AC_MSG_CHECKING([whether std::chrono::steady_clock is available]) +AC_COMPILE_IFELSE([AC_LANG_PROGRAM( +[[ +#include +]], +[[ +auto tp = std::chrono::steady_clock::now(); +]])], + [AC_DEFINE([HAVE_STEADY_CLOCK], [1], + [Define to 1 if you have the `std::chrono::steady_clock`.]) + AC_MSG_RESULT([yes])], + [AC_MSG_RESULT([no])]) + +# Check that std::future is available. +AC_MSG_CHECKING([whether std::future is available]) +AC_COMPILE_IFELSE([AC_LANG_PROGRAM( +[[ +#include +#include +]], +[[ +std::vector> v; +]])], + [AC_DEFINE([HAVE_STD_FUTURE], [1], + [Define to 1 if you have the `std::future`.]) + have_std_future=yes + AC_MSG_RESULT([yes])], + [have_std_future=no + AC_MSG_RESULT([no])]) + +# Check that std::map::emplace is available for g++-4.7. +AC_MSG_CHECKING([whether std::map::emplace is available]) +AC_COMPILE_IFELSE([AC_LANG_PROGRAM( +[[ +#include +]], +[[ +std::map().emplace(1, 2); +]])], + [AC_DEFINE([HAVE_STD_MAP_EMPLACE], [1], + [Define to 1 if you have the `std::map::emplace`.]) + have_std_map_emplace=yes + AC_MSG_RESULT([yes])], + [have_std_map_emplace=no + AC_MSG_RESULT([no])]) + +AC_LANG_POP() + +# Checks for libraries. + +# Additional libraries required for tests. +TESTLDADD= + +# Additional libraries required for programs under src directory. +APPLDFLAGS= + +LIBS_OLD=$LIBS +# Search for dlsym function, which is used in tests. Linux needs -ldl, +# but netbsd does not need it. +AC_SEARCH_LIBS([dlsym], [dl]) +TESTLDADD="$LIBS $TESTLDADD" +LIBS=$LIBS_OLD + +LIBS_OLD=$LIBS +AC_SEARCH_LIBS([clock_gettime], [rt], + [AC_DEFINE([HAVE_CLOCK_GETTIME], [1], + [Define to 1 if you have the `clock_gettime`.])]) +APPLDFLAGS="$LIBS $APPLDFLAGS" +LIBS=$LIBS_OLD + +case "$host" in + *android*) + android_build=yes + # android does not need -pthread, but needs followng 3 libs for C++ + APPLDFLAGS="$APPLDFLAGS -lstdc++ -latomic -lsupc++" + ;; + *) + PTHREAD_LDFLAGS="-pthread" + APPLDFLAGS="$APPLDFLAGS $PTHREAD_LDFLAGS" + ;; +esac + +# zlib +if test "x$android_build" = "xyes"; then + # Use zlib provided by NDK + APPLDFLAGS="-lz $APPLDFLAGS" + have_zlib=yes +else + PKG_CHECK_MODULES([ZLIB], [zlib >= 1.2.3], [have_zlib=yes], [have_zlib=no]) + + if test "x${have_zlib}" = "xno"; then + AC_MSG_NOTICE($ZLIB_PKG_ERRORS) + fi +fi + +# cunit +PKG_CHECK_MODULES([CUNIT], [cunit >= 2.1], [have_cunit=yes], [have_cunit=no]) +# If pkg-config does not find cunit, check it using AC_CHECK_LIB. We +# do this because Debian (Ubuntu) lacks pkg-config file for cunit. +if test "x${have_cunit}" = "xno"; then + AC_MSG_WARN([${CUNIT_PKG_ERRORS}]) + AC_CHECK_LIB([cunit], [CU_initialize_registry], + [have_cunit=yes], [have_cunit=no]) + if test "x${have_cunit}" = "xyes"; then + CUNIT_LIBS="-lcunit" + CUNIT_CFLAGS="" + AC_SUBST([CUNIT_LIBS]) + AC_SUBST([CUNIT_CFLAGS]) + fi +fi +if test "x${have_cunit}" = "xyes"; then + # cunit in Mac OS X requires ncurses. Note that in Mac OS X, test + # program can be built without -lncurses, but it emits runtime + # error. + case "${build}" in + *-apple-darwin*) + CUNIT_LIBS="$CUNIT_LIBS -lncurses" + AC_SUBST([CUNIT_LIBS]) + ;; + esac +fi + +AM_CONDITIONAL([HAVE_CUNIT], [ test "x${have_cunit}" = "xyes" ]) + +# libev (for src) +# libev does not have pkg-config file. Check it in an old way. +LIBS_OLD=$LIBS +# android requires -lm for floor +AC_CHECK_LIB([ev], [ev_time], [have_libev=yes], [have_libev=no], [-lm]) +if test "x${have_libev}" = "xyes"; then + AC_CHECK_HEADER([ev.h], [have_libev=yes], [have_libev=no]) + if test "x${have_libev}" = "xyes"; then + LIBEV_LIBS=-lev + LIBEV_CFLAGS= + AC_SUBST([LIBEV_LIBS]) + AC_SUBST([LIBEV_CFLAGS]) + fi +fi +LIBS=$LIBS_OLD + +# openssl (for src) +PKG_CHECK_MODULES([OPENSSL], [openssl >= 1.0.1], + [have_openssl=yes], [have_openssl=no]) +if test "x${have_openssl}" = "xno"; then + AC_MSG_NOTICE($OPENSSL_PKG_ERRORS) +fi + +# libevent_openssl (for examples) +# 2.0.8 is required because we use evconnlistener_set_error_cb() +PKG_CHECK_MODULES([LIBEVENT_OPENSSL], [libevent_openssl >= 2.0.8], + [have_libevent_openssl=yes], [have_libevent_openssl=no]) +if test "x${have_libevent_openssl}" = "xno"; then + AC_MSG_NOTICE($LIBEVENT_OPENSSL_PKG_ERRORS) +fi + +# jansson (for src/nghttp, src/deflatehd and src/inflatehd) +PKG_CHECK_MODULES([JANSSON], [jansson >= 2.5], + [have_jansson=yes], [have_jansson=no]) +if test "x${have_jansson}" == "xyes"; then + AC_DEFINE([HAVE_JANSSON], [1], + [Define to 1 if you have `libjansson` library.]) +else + AC_MSG_NOTICE($JANSSON_PKG_ERRORS) +fi + +# libxml2 (for src/nghttp) +have_libxml2=no +if test "x${request_libxml2}" != "xno"; then + AM_PATH_XML2(2.7.7, [have_libxml2=yes], [have_libxml2=no]) + if test "x${have_libxml2}" = "xyes"; then + AC_DEFINE([HAVE_LIBXML2], [1], [Define to 1 if you have `libxml2` library.]) + fi +fi + +if test "x${request_libxml2}" = "xyes" && + test "x${have_libxml2}" != "xyes"; then + AC_MSG_ERROR([libxml2 was requested (--with-libxml2) but not found]) +fi + +AM_CONDITIONAL([HAVE_LIBXML2], [ test "x${have_libxml2}" = "xyes" ]) + +# jemalloc +have_jemalloc=no +if test "x${request_jemalloc}" != "xno"; then + LIBS_OLD=$LIBS + AC_SEARCH_LIBS([malloc_stats_print], [jemalloc], [have_jemalloc=yes], [], + [$PTHREAD_LDFLAGS]) + LIBS=$LIBS_OLD + + if test "x${have_jemalloc}" = "xyes"; then + jemalloc_libs=${ac_cv_search_malloc_stats_print} + else + # On Darwin, malloc_stats_print is je_malloc_stats_print + AC_SEARCH_LIBS([je_malloc_stats_print], [jemalloc], [have_jemalloc=yes], [], + [$PTHREAD_LDFLAGS]) + LIBS=$LIBS_OLD + + if test "x${have_jemalloc}" = "xyes"; then + jemalloc_libs=${ac_cv_search_je_malloc_stats_print} + fi + fi + + if test "x${have_jemalloc}" = "xyes" && + test "x${jemalloc_libs}" != "xnone required"; then + JEMALLOC_LIBS=${jemalloc_libs} + AC_SUBST([JEMALLOC_LIBS]) + fi +fi + +if test "x${request_jemalloc}" = "xyes" && + test "x${have_jemalloc}" != "xyes"; then + AC_MSG_ERROR([jemalloc was requested (--with-jemalloc) but not found]) +fi + +# spdylay (for src/nghttpx and src/h2load) +have_spdylay=no +if test "x${request_spdylay}" != "xno"; then + PKG_CHECK_MODULES([LIBSPDYLAY], [libspdylay >= 1.3.2], + [have_spdylay=yes], [have_spdylay=no]) + if test "x${have_spdylay}" = "xyes"; then + AC_DEFINE([HAVE_SPDYLAY], [1], [Define to 1 if you have `spdylay` library.]) + else + AC_MSG_NOTICE($LIBSPDYLAY_PKG_ERRORS) + AC_MSG_NOTICE([The SPDY support in nghttpx and h2load will be disabled.]) + fi +fi + +if test "x${request_spdylay}" = "xyes" && + test "x${have_spdylay}" != "xyes"; then + AC_MSG_ERROR([spdylay was requested (--with-spdylay) but not found]) +fi + +AM_CONDITIONAL([HAVE_SPDYLAY], [ test "x${have_spdylay}" = "xyes" ]) + +# Check Boost Asio library +have_asio_lib=no + +if test "x${request_asio_lib}" = "xyes"; then + AX_BOOST_BASE([1.54.0], [have_boost_base=yes], [have_boost_base=no]) + + if test "x${have_boost_base}" = "xyes"; then + AX_BOOST_ASIO() + AX_BOOST_SYSTEM() + AX_BOOST_THREAD() + + if test "x${ax_cv_boost_asio}" = "xyes" && + test "x${ax_cv_boost_system}" = "xyes" && + test "x${ax_cv_boost_thread}" = "xyes"; then + have_asio_lib=yes + fi + fi +fi + +# The nghttp, nghttpd and nghttpx under src depend on zlib, OpenSSL +# and libev +enable_app=no +if test "x${request_app}" != "xno" && + test "x${have_zlib}" = "xyes" && + test "x${have_openssl}" = "xyes" && + test "x${have_libev}" = "xyes"; then + enable_app=yes +fi + +if test "x${request_app}" = "xyes" && + test "x${enable_app}" != "xyes"; then + AC_MSG_ERROR([applications were requested (--enable-app) but dependencies are not met.]) +fi + +AM_CONDITIONAL([ENABLE_APP], [ test "x${enable_app}" = "xyes" ]) + +enable_hpack_tools=no +# HPACK tools requires jansson +if test "x${request_hpack_tools}" != "xno" && + test "x${have_jansson}" = "xyes"; then + enable_hpack_tools=yes +fi + +if test "x${request_hpack_tools}" = "xyes" && + test "x${enable_hpack_tools}" != "xyes"; then + AC_MSG_ERROR([HPACK tools were requested (--enable-hpack-tools) but dependencies are not met.]) +fi + +AM_CONDITIONAL([ENABLE_HPACK_TOOLS], [ test "x${enable_hpack_tools}" = "xyes" ]) + +# C++ library libnghttp2_asio + +enable_asio_lib=no +if test "x${request_asio_lib}" != "xno" && + test "x${have_asio_lib}" = "xyes"; then + enable_asio_lib=yes +fi + +AM_CONDITIONAL([ENABLE_ASIO_LIB], [ test "x${enable_asio_lib}" = "xyes" ]) + +# The example programs depend on OpenSSL and libevent_openssl +enable_examples=no +if test "x${request_examples}" != "xno" && + test "x${have_openssl}" = "xyes" && + test "x${have_libevent_openssl}" = "xyes"; then + enable_examples=yes +fi + +if test "x${request_examples}" = "xyes" && + test "x${enable_examples}" != "xyes"; then + AC_MSG_ERROR([examples were requested (--enable-examples) but dependencies are not met.]) +fi + +AM_CONDITIONAL([ENABLE_EXAMPLES], [ test "x${enable_examples}" = "xyes" ]) + +# Python bindings +enable_python_bindings=no +if test "x${request_python_bindings}" != "xno" && + test "x${CYTHON}" != "x" && + test "x${PYTHON}" != "x:" && + test "x${have_python_dev}" = "xyes"; then + enable_python_bindings=yes +fi + +if test "x${request_python_bindings}" = "xyes" && + test "x${enable_python_bindings}" != "xyes"; then + AC_MSG_ERROR([python bindings were requested (--enable-python-bindings) but dependencies are not met.]) +fi + +AM_CONDITIONAL([ENABLE_PYTHON_BINDINGS], + [test "x${enable_python_bindings}" = "xyes"]) + +# Produce cython conditional, so that we can distribute generated C +# source +AM_CONDITIONAL([HAVE_CYTHON], [test "x${CYTHON}" != "x"]) + +# failmalloc tests +enable_failmalloc=no +if test "x${request_failmalloc}" = "xyes"; then + enable_failmalloc=yes +fi + +AM_CONDITIONAL([ENABLE_FAILMALLOC], [ test "x${enable_failmalloc}" = "xyes" ]) + +# Checks for header files. +AC_HEADER_ASSERT +AC_CHECK_HEADERS([ \ + arpa/inet.h \ + fcntl.h \ + inttypes.h \ + limits.h \ + netdb.h \ + netinet/in.h \ + pwd.h \ + stddef.h \ + stdint.h \ + stdlib.h \ + string.h \ + sys/socket.h \ + sys/time.h \ + syslog.h \ + time.h \ + unistd.h \ +]) + +# Checks for typedefs, structures, and compiler characteristics. +AC_TYPE_SIZE_T +AC_TYPE_SSIZE_T +AC_TYPE_UINT8_T +AC_TYPE_UINT16_T +AC_TYPE_UINT32_T +AC_TYPE_UINT64_T +AC_TYPE_INT8_T +AC_TYPE_INT16_T +AC_TYPE_INT32_T +AC_TYPE_INT64_T +AC_TYPE_OFF_T +AC_TYPE_PID_T +AC_TYPE_UID_T +AC_CHECK_TYPES([ptrdiff_t]) +AC_C_BIGENDIAN +AC_C_INLINE +AC_SYS_LARGEFILE + +AC_CHECK_MEMBER([struct tm.tm_gmtoff], [have_struct_tm_tm_gmtoff=yes], + [have_struct_tm_tm_gmtoff=no], [[#include ]]) + +if test "x$have_struct_tm_tm_gmtoff" = "xyes"; then + AC_DEFINE([HAVE_STRUCT_TM_TM_GMTOFF], [1], + [Define to 1 if you have `struct tm.tm_gmtoff` member.]) +fi + +# Check size of pointer to decide we need 8 bytes alignment +# adjustment. +AC_CHECK_SIZEOF([int *]) + +# Checks for library functions. +if test "x$cross_compiling" != "xyes"; then + AC_FUNC_MALLOC +fi + +AC_FUNC_CHOWN +AC_FUNC_ERROR_AT_LINE +AC_FUNC_FORK +# Don't check realloc, since LeakSanitizer detects memory leak during check +# AC_FUNC_REALLOC +AC_FUNC_STRERROR_R +AC_FUNC_STRNLEN + +AC_CHECK_FUNCS([ \ + _Exit \ + accept4 \ + dup2 \ + getcwd \ + getpwnam \ + localtime_r \ + memchr \ + memmove \ + memset \ + socket \ + sqrt \ + strchr \ + strdup \ + strerror \ + strndup \ + strstr \ + strtol \ + strtoul \ + timegm \ +]) + +# timerfd_create was added in linux kernel 2.6.25 + +AC_CHECK_FUNC([timerfd_create], + [have_timerfd_create=yes], [have_timerfd_create=no]) + + +# Checks for epoll availability, primarily for examples/tiny-nghttpd +AX_HAVE_EPOLL([have_epoll=yes], [have_epoll=no]) + +AM_CONDITIONAL([ENABLE_TINY_NGHTTPD], + [ test "x${have_epoll}" = "xyes" && + test "x${have_timerfd_create}" = "xyes"]) + +ac_save_CFLAGS=$CFLAGS +CFLAGS= + +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"]) + 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"]) + 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"]) + + # Only work with Clang for the moment + AX_CHECK_COMPILE_FLAG([-Wheader-guard], [CFLAGS="$CFLAGS -Wheader-guard"]) +fi + +WARNCFLAGS=$CFLAGS +CFLAGS=$ac_save_CFLAGS + +AC_SUBST([WARNCFLAGS]) + +if test "x$debug" != "xno"; then + AC_DEFINE([DEBUGBUILD], [1], [Define to 1 to enable debug output.]) +fi + +enable_threads=yes +# Some platform does not have working std::future. We disable +# threading for those platforms. +if test "x$threads" != "xyes" || + test "x$have_std_future" != "xyes"; then + enable_threads=no + AC_DEFINE([NOTHREADS], [1], [Define to 1 if you want to disable threads.]) +fi + +AC_SUBST([TESTLDADD]) +AC_SUBST([APPLDFLAGS]) + +AC_CONFIG_FILES([ + Makefile + lib/Makefile + lib/libnghttp2.pc + lib/includes/Makefile + lib/includes/nghttp2/nghttp2ver.h + tests/Makefile + tests/testdata/Makefile + third-party/Makefile + src/Makefile + src/includes/Makefile + src/libnghttp2_asio.pc + examples/Makefile + python/Makefile + python/setup.py + integration-tests/Makefile + integration-tests/config.go + integration-tests/setenv + doc/Makefile + doc/conf.py + doc/index.rst + doc/package_README.rst + doc/tutorial-client.rst + doc/tutorial-server.rst + doc/tutorial-hpack.rst + doc/nghttpx-howto.rst + doc/h2load-howto.rst + doc/libnghttp2_asio.rst + doc/python-apiref.rst + doc/building-android-binary.rst + doc/nghttp2.h.rst + doc/nghttp2ver.h.rst + doc/asio_http2.h.rst + doc/asio_http2_server.h.rst + doc/asio_http2_client.h.rst + doc/contribute.rst + contrib/Makefile +]) +AC_OUTPUT + +AC_MSG_NOTICE([summary of build options: + + Version: ${VERSION} shared $LT_CURRENT:$LT_REVISION:$LT_AGE + Host type: ${host} + Install prefix: ${prefix} + C compiler: ${CC} + CFLAGS: ${CFLAGS} + WARNCFLAGS: ${WARNCFLAGS} + LDFLAGS: ${LDFLAGS} + LIBS: ${LIBS} + CPPFLAGS: ${CPPFLAGS} + C preprocessor: ${CPP} + C++ compiler: ${CXX} + CXXFLAGS: ${CXXFLAGS} + CXXCPP: ${CXXCPP} + Library types: Shared=${enable_shared}, Static=${enable_static} + Python: + Python: ${PYTHON} + PYTHON_VERSION: ${PYTHON_VERSION} + pyexecdir: ${pyexecdir} + Python-dev: ${have_python_dev} + PYTHON_CPPFLAGS:${PYTHON_CPPFLAGS} + PYTHON_LDFLAGS: ${PYTHON_LDFLAGS} + Cython: ${CYTHON} + Test: + CUnit: ${have_cunit} + Failmalloc: ${enable_failmalloc} + Libs: + OpenSSL: ${have_openssl} + Libxml2: ${have_libxml2} + Libev: ${have_libev} + Libevent(SSL): ${have_libevent_openssl} + Spdylay: ${have_spdylay} + Jansson: ${have_jansson} + Jemalloc: ${have_jemalloc} + Boost CPPFLAGS: ${BOOST_CPPFLAGS} + Boost LDFLAGS: ${BOOST_LDFLAGS} + Boost::ASIO: ${BOOST_ASIO_LIB} + Boost::System: ${BOOST_SYSTEM_LIB} + Boost::Thread: ${BOOST_THREAD_LIB} + Features: + Applications: ${enable_app} + HPACK tools: ${enable_hpack_tools} + Libnghttp2_asio:${enable_asio_lib} + Examples: ${enable_examples} + Python bindings:${enable_python_bindings} + Threading: ${enable_threads} +]) diff --git a/contrib/Makefile.am b/contrib/Makefile.am new file mode 100644 index 0000000..07a3bf8 --- /dev/null +++ b/contrib/Makefile.am @@ -0,0 +1,44 @@ +# nghttp2 - HTTP/2 C Library + +# Copyright (c) 2014 Tatsuhiro Tsujikawa + +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: + +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. + +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +configfiles = nghttpx-init nghttpx.service nghttpx-upstart.conf + +EXTRA_DIST = $(configfiles:%=%.in) nghttpx-logrotate + +edit = sed -e 's|@bindir[@]|$(bindir)|g' + +nghttpx-init: %: $(srcdir)/%.in + rm -f $@ $@.tmp + $(edit) $< > $@.tmp + chmod +x $@.tmp + mv $@.tmp $@ + +nghttpx.service nghttpx-upstart.conf: %: $(srcdir)/%.in + $(edit) $< > $@ + +$(configfiles): Makefile + +all-local: $(configfiles) + +clean-local: + -rm -f nghttpx-init.tmp $(configfiles) diff --git a/contrib/Makefile.in b/contrib/Makefile.in new file mode 100644 index 0000000..357b6e3 --- /dev/null +++ b/contrib/Makefile.in @@ -0,0 +1,531 @@ +# Makefile.in generated by automake 1.14.1 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2013 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +# nghttp2 - HTTP/2 C Library + +# Copyright (c) 2014 Tatsuhiro Tsujikawa + +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: + +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. + +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +VPATH = @srcdir@ +am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)' +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +target_triplet = @target@ +subdir = contrib +DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/ax_boost_asio.m4 \ + $(top_srcdir)/m4/ax_boost_base.m4 \ + $(top_srcdir)/m4/ax_boost_system.m4 \ + $(top_srcdir)/m4/ax_boost_thread.m4 \ + $(top_srcdir)/m4/ax_check_compile_flag.m4 \ + $(top_srcdir)/m4/ax_cxx_compile_stdcxx_11.m4 \ + $(top_srcdir)/m4/ax_have_epoll.m4 \ + $(top_srcdir)/m4/ax_python_devel.m4 \ + $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/libxml2.m4 \ + $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ + $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +SOURCES = +DIST_SOURCES = +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +APPLDFLAGS = @APPLDFLAGS@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +BOOST_ASIO_LIB = @BOOST_ASIO_LIB@ +BOOST_CPPFLAGS = @BOOST_CPPFLAGS@ +BOOST_LDFLAGS = @BOOST_LDFLAGS@ +BOOST_SYSTEM_LIB = @BOOST_SYSTEM_LIB@ +BOOST_THREAD_LIB = @BOOST_THREAD_LIB@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CUNIT_CFLAGS = @CUNIT_CFLAGS@ +CUNIT_LIBS = @CUNIT_LIBS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +CYTHON = @CYTHON@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DLLTOOL = @DLLTOOL@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +GREP = @GREP@ +HAVE_CXX11 = @HAVE_CXX11@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +JANSSON_CFLAGS = @JANSSON_CFLAGS@ +JANSSON_LIBS = @JANSSON_LIBS@ +JEMALLOC_LIBS = @JEMALLOC_LIBS@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBEVENT_OPENSSL_CFLAGS = @LIBEVENT_OPENSSL_CFLAGS@ +LIBEVENT_OPENSSL_LIBS = @LIBEVENT_OPENSSL_LIBS@ +LIBEV_CFLAGS = @LIBEV_CFLAGS@ +LIBEV_LIBS = @LIBEV_LIBS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBSPDYLAY_CFLAGS = @LIBSPDYLAY_CFLAGS@ +LIBSPDYLAY_LIBS = @LIBSPDYLAY_LIBS@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +LT_AGE = @LT_AGE@ +LT_CURRENT = @LT_CURRENT@ +LT_REVISION = @LT_REVISION@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MKDIR_P = @MKDIR_P@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OPENSSL_CFLAGS = @OPENSSL_CFLAGS@ +OPENSSL_LIBS = @OPENSSL_LIBS@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PACKAGE_VERSION_NUM = @PACKAGE_VERSION_NUM@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PKG_CONFIG = @PKG_CONFIG@ +PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ +PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ +PYTHON = @PYTHON@ +PYTHON_CPPFLAGS = @PYTHON_CPPFLAGS@ +PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@ +PYTHON_EXTRA_LDFLAGS = @PYTHON_EXTRA_LDFLAGS@ +PYTHON_EXTRA_LIBS = @PYTHON_EXTRA_LIBS@ +PYTHON_LDFLAGS = @PYTHON_LDFLAGS@ +PYTHON_PLATFORM = @PYTHON_PLATFORM@ +PYTHON_PREFIX = @PYTHON_PREFIX@ +PYTHON_SITE_PKG = @PYTHON_SITE_PKG@ +PYTHON_VERSION = @PYTHON_VERSION@ +RANLIB = @RANLIB@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +TESTLDADD = @TESTLDADD@ +VERSION = @VERSION@ +WARNCFLAGS = @WARNCFLAGS@ +XML2_CONFIG = @XML2_CONFIG@ +XML_CPPFLAGS = @XML_CPPFLAGS@ +XML_LIBS = @XML_LIBS@ +ZLIB_CFLAGS = @ZLIB_CFLAGS@ +ZLIB_LIBS = @ZLIB_LIBS@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +pkgpyexecdir = @pkgpyexecdir@ +pkgpythondir = @pkgpythondir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +pyexecdir = @pyexecdir@ +pythondir = @pythondir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target = @target@ +target_alias = @target_alias@ +target_cpu = @target_cpu@ +target_os = @target_os@ +target_vendor = @target_vendor@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +configfiles = nghttpx-init nghttpx.service nghttpx-upstart.conf +EXTRA_DIST = $(configfiles:%=%.in) nghttpx-logrotate +edit = sed -e 's|@bindir[@]|$(bindir)|g' +all: all-am + +.SUFFIXES: +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu contrib/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu contrib/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs +tags TAGS: + +ctags CTAGS: + +cscope cscopelist: + + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile all-local +installdirs: +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic clean-libtool clean-local mostlyclean-am + +distclean: distclean-am + -rm -f Makefile +distclean-am: clean-am distclean-generic + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-generic mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: + +.MAKE: install-am install-strip + +.PHONY: all all-am all-local check check-am clean clean-generic \ + clean-libtool clean-local cscopelist-am ctags-am distclean \ + distclean-generic distclean-libtool distdir dvi dvi-am html \ + html-am info info-am install install-am install-data \ + install-data-am install-dvi install-dvi-am install-exec \ + install-exec-am install-html install-html-am install-info \ + install-info-am install-man install-pdf install-pdf-am \ + install-ps install-ps-am install-strip installcheck \ + installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-generic \ + mostlyclean-libtool pdf pdf-am ps ps-am tags-am uninstall \ + uninstall-am + + +nghttpx-init: %: $(srcdir)/%.in + rm -f $@ $@.tmp + $(edit) $< > $@.tmp + chmod +x $@.tmp + mv $@.tmp $@ + +nghttpx.service nghttpx-upstart.conf: %: $(srcdir)/%.in + $(edit) $< > $@ + +$(configfiles): Makefile + +all-local: $(configfiles) + +clean-local: + -rm -f nghttpx-init.tmp $(configfiles) + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/contrib/nghttpx-init.in b/contrib/nghttpx-init.in new file mode 100644 index 0000000..a1620a9 --- /dev/null +++ b/contrib/nghttpx-init.in @@ -0,0 +1,173 @@ +#! /bin/sh +### BEGIN INIT INFO +# Provides: nghttpx +# Required-Start: $remote_fs $syslog +# Required-Stop: $remote_fs $syslog +# Default-Start: 2 3 4 5 +# Default-Stop: 0 1 6 +# Short-Description: nghttpx initscript +# Description: nghttpx initscript +### END INIT INFO + +# Author: Tatsuhiro Tsujikawa +# +# Do NOT "set -e" + +# PATH should only include /usr/* if it runs after the mountnfs.sh script +PATH=/sbin:/usr/sbin:/bin:/usr/bin:/usr/local/bin +DESC="HTTP/2 reverse proxy" +NAME=nghttpx +# Depending on the configuration, binary may be located under @sbindir@ +DAEMON=@bindir@/$NAME +PIDFILE=/var/run/$NAME.pid +DAEMON_ARGS="--conf /etc/nghttpx/nghttpx.conf --pid-file=$PIDFILE" +SCRIPTNAME=/etc/init.d/$NAME + +# Exit if the package is not installed +[ -x "$DAEMON" ] || exit 0 + +# Read configuration variable file if it is present +[ -r /etc/default/$NAME ] && . /etc/default/$NAME + +# Load the VERBOSE setting and other rcS variables +. /lib/init/vars.sh + +# Define LSB log_* functions. +# Depend on lsb-base (>= 3.2-14) to ensure that this file is present +# and status_of_proc is working. +. /lib/lsb/init-functions + +# +# Function that starts the daemon/service +# +do_start() +{ + # Return + # 0 if daemon has been started + # 1 if daemon was already running + # 2 if daemon could not be started + start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON --test > /dev/null \ + || return 1 + start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON -- \ + $DAEMON_ARGS \ + || return 2 + # Add code here, if necessary, that waits for the process to be ready + # to handle requests from services started subsequently which depend + # on this one. As a last resort, sleep for some time. +} + +# +# Function that stops the daemon/service +# +do_stop() +{ + # Return + # 0 if daemon has been stopped + # 1 if daemon was already stopped + # 2 if daemon could not be stopped + # other if a failure occurred + start-stop-daemon --stop --quiet --retry=TERM/30/KILL/5 --pidfile $PIDFILE + RETVAL="$?" + [ "$RETVAL" = 2 ] && return 2 + + # Wait for children to finish too if this is a daemon that forks + # and if the daemon is only ever run from this initscript. + # If the above conditions are not satisfied then add some other code + # that waits for the process to drop all resources that could be + # needed by services started subsequently. A last resort is to + # sleep for some time. + #start-stop-daemon --stop --quiet --oknodo --retry=0/30/KILL/5 --exec $DAEMON + #[ "$?" = 2 ] && return 2 + # Many daemons don't delete their pidfiles when they exit. + rm -f $PIDFILE + return "$RETVAL" +} + +# +# Function that sends a SIGHUP to the daemon/service +# +do_reload() { + # + # If the daemon can reload its configuration without + # restarting (for example, when it is sent a SIGHUP), + # then implement that here. + # + start-stop-daemon --stop --signal 1 --quiet --pidfile $PIDFILE --name $NAME + return 0 +} + +case "$1" in + start) + [ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC" "$NAME" + do_start + case "$?" in + 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;; + 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;; + esac + ;; + stop) + [ "$VERBOSE" != no ] && log_daemon_msg "Stopping $DESC" "$NAME" + do_stop + case "$?" in + 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;; + 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;; + esac + ;; + status) + status_of_proc "$DAEMON" "$NAME" && exit 0 || exit $? + ;; + upgrade) + log_daemon_msg "Upgrade $DESC" "$NAME" + pid=`pidofproc -p $PIDFILE $NAME` + case "$?" in + 0) echo "Sending USR2 signal to $pid" + kill -USR2 $pid + echo "Waiting for new binary..." + sleep 5 + echo "Sending QUIT signal to $pid" + kill -QUIT $pid + log_end_msg 0 + ;; + *) echo "pidofproc() failed" + log_end_msg 1 + ;; + esac + ;; + #reload|force-reload) + # + # If do_reload() is not implemented then leave this commented out + # and leave 'force-reload' as an alias for 'restart'. + # + #log_daemon_msg "Reloading $DESC" "$NAME" + #do_reload + #log_end_msg $? + #;; + restart|force-reload) + # + # If the "reload" option is implemented then remove the + # 'force-reload' alias + # + log_daemon_msg "Restarting $DESC" "$NAME" + do_stop + case "$?" in + 0|1) + do_start + case "$?" in + 0) log_end_msg 0 ;; + 1) log_end_msg 1 ;; # Old process is still running + *) log_end_msg 1 ;; # Failed to start + esac + ;; + *) + # Failed to stop + log_end_msg 1 + ;; + esac + ;; + *) + echo "Usage: $SCRIPTNAME {start|stop|status|restart|force-reload|upgrade}" >&2 + exit 3 + ;; +esac + +: diff --git a/contrib/nghttpx-logrotate b/contrib/nghttpx-logrotate new file mode 100644 index 0000000..f1002bd --- /dev/null +++ b/contrib/nghttpx-logrotate @@ -0,0 +1,11 @@ +/var/log/nghttpx/*.log { + weekly + rotate 52 + missingok + compress + delaycompress + notifempty + postrotate + killall -USR1 nghttpx 2> /dev/null || true + endscript +} diff --git a/contrib/nghttpx-upstart.conf.in b/contrib/nghttpx-upstart.conf.in new file mode 100644 index 0000000..0b79916 --- /dev/null +++ b/contrib/nghttpx-upstart.conf.in @@ -0,0 +1,8 @@ +# vim: ft=upstart: + +description "HTTP/2 reverse proxy" + +start on runlevel [2] +stop on runlevel [016] + +exec @bindir@/nghttpx diff --git a/contrib/nghttpx.service.in b/contrib/nghttpx.service.in new file mode 100644 index 0000000..9c7f851 --- /dev/null +++ b/contrib/nghttpx.service.in @@ -0,0 +1,10 @@ +[Unit] +Description=HTTP/2 experimental proxy +After=network.target + +[Service] +Type=simple +ExecStart=@bindir@/nghttpx --errorlog-syslog + +[Install] +WantedBy=multi-user.target diff --git a/depcomp b/depcomp new file mode 100755 index 0000000..4ebd5b3 --- /dev/null +++ b/depcomp @@ -0,0 +1,791 @@ +#! /bin/sh +# depcomp - compile a program generating dependencies as side-effects + +scriptversion=2013-05-30.07; # UTC + +# Copyright (C) 1999-2013 Free Software Foundation, Inc. + +# 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 2, 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 . + +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +# Originally written by Alexandre Oliva . + +case $1 in + '') + echo "$0: No command. Try '$0 --help' for more information." 1>&2 + exit 1; + ;; + -h | --h*) + cat <<\EOF +Usage: depcomp [--help] [--version] PROGRAM [ARGS] + +Run PROGRAMS ARGS to compile a file, generating dependencies +as side-effects. + +Environment variables: + depmode Dependency tracking mode. + source Source file read by 'PROGRAMS ARGS'. + object Object file output by 'PROGRAMS ARGS'. + DEPDIR directory where to store dependencies. + depfile Dependency file to output. + tmpdepfile Temporary file to use when outputting dependencies. + libtool Whether libtool is used (yes/no). + +Report bugs to . +EOF + exit $? + ;; + -v | --v*) + echo "depcomp $scriptversion" + exit $? + ;; +esac + +# Get the directory component of the given path, and save it in the +# global variables '$dir'. Note that this directory component will +# be either empty or ending with a '/' character. This is deliberate. +set_dir_from () +{ + case $1 in + */*) dir=`echo "$1" | sed -e 's|/[^/]*$|/|'`;; + *) dir=;; + esac +} + +# Get the suffix-stripped basename of the given path, and save it the +# global variable '$base'. +set_base_from () +{ + base=`echo "$1" | sed -e 's|^.*/||' -e 's/\.[^.]*$//'` +} + +# If no dependency file was actually created by the compiler invocation, +# we still have to create a dummy depfile, to avoid errors with the +# Makefile "include basename.Plo" scheme. +make_dummy_depfile () +{ + echo "#dummy" > "$depfile" +} + +# Factor out some common post-processing of the generated depfile. +# Requires the auxiliary global variable '$tmpdepfile' to be set. +aix_post_process_depfile () +{ + # If the compiler actually managed to produce a dependency file, + # post-process it. + if test -f "$tmpdepfile"; then + # Each line is of the form 'foo.o: dependency.h'. + # Do two passes, one to just change these to + # $object: dependency.h + # and one to simply output + # dependency.h: + # which is needed to avoid the deleted-header problem. + { sed -e "s,^.*\.[$lower]*:,$object:," < "$tmpdepfile" + sed -e "s,^.*\.[$lower]*:[$tab ]*,," -e 's,$,:,' < "$tmpdepfile" + } > "$depfile" + rm -f "$tmpdepfile" + else + make_dummy_depfile + fi +} + +# A tabulation character. +tab=' ' +# A newline character. +nl=' +' +# Character ranges might be problematic outside the C locale. +# These definitions help. +upper=ABCDEFGHIJKLMNOPQRSTUVWXYZ +lower=abcdefghijklmnopqrstuvwxyz +digits=0123456789 +alpha=${upper}${lower} + +if test -z "$depmode" || test -z "$source" || test -z "$object"; then + echo "depcomp: Variables source, object and depmode must be set" 1>&2 + exit 1 +fi + +# Dependencies for sub/bar.o or sub/bar.obj go into sub/.deps/bar.Po. +depfile=${depfile-`echo "$object" | + sed 's|[^\\/]*$|'${DEPDIR-.deps}'/&|;s|\.\([^.]*\)$|.P\1|;s|Pobj$|Po|'`} +tmpdepfile=${tmpdepfile-`echo "$depfile" | sed 's/\.\([^.]*\)$/.T\1/'`} + +rm -f "$tmpdepfile" + +# Avoid interferences from the environment. +gccflag= dashmflag= + +# Some modes work just like other modes, but use different flags. We +# parameterize here, but still list the modes in the big case below, +# to make depend.m4 easier to write. Note that we *cannot* use a case +# here, because this file can only contain one case statement. +if test "$depmode" = hp; then + # HP compiler uses -M and no extra arg. + gccflag=-M + depmode=gcc +fi + +if test "$depmode" = dashXmstdout; then + # This is just like dashmstdout with a different argument. + dashmflag=-xM + depmode=dashmstdout +fi + +cygpath_u="cygpath -u -f -" +if test "$depmode" = msvcmsys; then + # This is just like msvisualcpp but w/o cygpath translation. + # Just convert the backslash-escaped backslashes to single forward + # slashes to satisfy depend.m4 + cygpath_u='sed s,\\\\,/,g' + depmode=msvisualcpp +fi + +if test "$depmode" = msvc7msys; then + # This is just like msvc7 but w/o cygpath translation. + # Just convert the backslash-escaped backslashes to single forward + # slashes to satisfy depend.m4 + cygpath_u='sed s,\\\\,/,g' + depmode=msvc7 +fi + +if test "$depmode" = xlc; then + # IBM C/C++ Compilers xlc/xlC can output gcc-like dependency information. + gccflag=-qmakedep=gcc,-MF + depmode=gcc +fi + +case "$depmode" in +gcc3) +## gcc 3 implements dependency tracking that does exactly what +## we want. Yay! Note: for some reason libtool 1.4 doesn't like +## it if -MD -MP comes after the -MF stuff. Hmm. +## Unfortunately, FreeBSD c89 acceptance of flags depends upon +## the command line argument order; so add the flags where they +## appear in depend2.am. Note that the slowdown incurred here +## affects only configure: in makefiles, %FASTDEP% shortcuts this. + for arg + do + case $arg in + -c) set fnord "$@" -MT "$object" -MD -MP -MF "$tmpdepfile" "$arg" ;; + *) set fnord "$@" "$arg" ;; + esac + shift # fnord + shift # $arg + done + "$@" + stat=$? + if test $stat -ne 0; then + rm -f "$tmpdepfile" + exit $stat + fi + mv "$tmpdepfile" "$depfile" + ;; + +gcc) +## Note that this doesn't just cater to obsosete pre-3.x GCC compilers. +## but also to in-use compilers like IMB xlc/xlC and the HP C compiler. +## (see the conditional assignment to $gccflag above). +## There are various ways to get dependency output from gcc. Here's +## why we pick this rather obscure method: +## - Don't want to use -MD because we'd like the dependencies to end +## up in a subdir. Having to rename by hand is ugly. +## (We might end up doing this anyway to support other compilers.) +## - The DEPENDENCIES_OUTPUT environment variable makes gcc act like +## -MM, not -M (despite what the docs say). Also, it might not be +## supported by the other compilers which use the 'gcc' depmode. +## - Using -M directly means running the compiler twice (even worse +## than renaming). + if test -z "$gccflag"; then + gccflag=-MD, + fi + "$@" -Wp,"$gccflag$tmpdepfile" + stat=$? + if test $stat -ne 0; then + rm -f "$tmpdepfile" + exit $stat + fi + rm -f "$depfile" + echo "$object : \\" > "$depfile" + # The second -e expression handles DOS-style file names with drive + # letters. + sed -e 's/^[^:]*: / /' \ + -e 's/^['$alpha']:\/[^:]*: / /' < "$tmpdepfile" >> "$depfile" +## This next piece of magic avoids the "deleted header file" problem. +## The problem is that when a header file which appears in a .P file +## is deleted, the dependency causes make to die (because there is +## typically no way to rebuild the header). We avoid this by adding +## dummy dependencies for each header file. Too bad gcc doesn't do +## this for us directly. +## Some versions of gcc put a space before the ':'. On the theory +## that the space means something, we add a space to the output as +## well. hp depmode also adds that space, but also prefixes the VPATH +## to the object. Take care to not repeat it in the output. +## Some versions of the HPUX 10.20 sed can't process this invocation +## correctly. Breaking it into two sed invocations is a workaround. + tr ' ' "$nl" < "$tmpdepfile" \ + | sed -e 's/^\\$//' -e '/^$/d' -e "s|.*$object$||" -e '/:$/d' \ + | sed -e 's/$/ :/' >> "$depfile" + rm -f "$tmpdepfile" + ;; + +hp) + # This case exists only to let depend.m4 do its work. It works by + # looking at the text of this script. This case will never be run, + # since it is checked for above. + exit 1 + ;; + +sgi) + if test "$libtool" = yes; then + "$@" "-Wp,-MDupdate,$tmpdepfile" + else + "$@" -MDupdate "$tmpdepfile" + fi + stat=$? + if test $stat -ne 0; then + rm -f "$tmpdepfile" + exit $stat + fi + rm -f "$depfile" + + if test -f "$tmpdepfile"; then # yes, the sourcefile depend on other files + echo "$object : \\" > "$depfile" + # Clip off the initial element (the dependent). Don't try to be + # clever and replace this with sed code, as IRIX sed won't handle + # lines with more than a fixed number of characters (4096 in + # IRIX 6.2 sed, 8192 in IRIX 6.5). We also remove comment lines; + # the IRIX cc adds comments like '#:fec' to the end of the + # dependency line. + tr ' ' "$nl" < "$tmpdepfile" \ + | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' \ + | tr "$nl" ' ' >> "$depfile" + echo >> "$depfile" + # The second pass generates a dummy entry for each header file. + tr ' ' "$nl" < "$tmpdepfile" \ + | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' -e 's/$/:/' \ + >> "$depfile" + else + make_dummy_depfile + fi + rm -f "$tmpdepfile" + ;; + +xlc) + # This case exists only to let depend.m4 do its work. It works by + # looking at the text of this script. This case will never be run, + # since it is checked for above. + exit 1 + ;; + +aix) + # The C for AIX Compiler uses -M and outputs the dependencies + # in a .u file. In older versions, this file always lives in the + # current directory. Also, the AIX compiler puts '$object:' at the + # start of each line; $object doesn't have directory information. + # Version 6 uses the directory in both cases. + set_dir_from "$object" + set_base_from "$object" + if test "$libtool" = yes; then + tmpdepfile1=$dir$base.u + tmpdepfile2=$base.u + tmpdepfile3=$dir.libs/$base.u + "$@" -Wc,-M + else + tmpdepfile1=$dir$base.u + tmpdepfile2=$dir$base.u + tmpdepfile3=$dir$base.u + "$@" -M + fi + stat=$? + if test $stat -ne 0; then + rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" + exit $stat + fi + + for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" + do + test -f "$tmpdepfile" && break + done + aix_post_process_depfile + ;; + +tcc) + # tcc (Tiny C Compiler) understand '-MD -MF file' since version 0.9.26 + # FIXME: That version still under development at the moment of writing. + # Make that this statement remains true also for stable, released + # versions. + # It will wrap lines (doesn't matter whether long or short) with a + # trailing '\', as in: + # + # foo.o : \ + # foo.c \ + # foo.h \ + # + # It will put a trailing '\' even on the last line, and will use leading + # spaces rather than leading tabs (at least since its commit 0394caf7 + # "Emit spaces for -MD"). + "$@" -MD -MF "$tmpdepfile" + stat=$? + if test $stat -ne 0; then + rm -f "$tmpdepfile" + exit $stat + fi + rm -f "$depfile" + # Each non-empty line is of the form 'foo.o : \' or ' dep.h \'. + # We have to change lines of the first kind to '$object: \'. + sed -e "s|.*:|$object :|" < "$tmpdepfile" > "$depfile" + # And for each line of the second kind, we have to emit a 'dep.h:' + # dummy dependency, to avoid the deleted-header problem. + sed -n -e 's|^ *\(.*\) *\\$|\1:|p' < "$tmpdepfile" >> "$depfile" + rm -f "$tmpdepfile" + ;; + +## The order of this option in the case statement is important, since the +## shell code in configure will try each of these formats in the order +## listed in this file. A plain '-MD' option would be understood by many +## compilers, so we must ensure this comes after the gcc and icc options. +pgcc) + # Portland's C compiler understands '-MD'. + # Will always output deps to 'file.d' where file is the root name of the + # source file under compilation, even if file resides in a subdirectory. + # The object file name does not affect the name of the '.d' file. + # pgcc 10.2 will output + # foo.o: sub/foo.c sub/foo.h + # and will wrap long lines using '\' : + # foo.o: sub/foo.c ... \ + # sub/foo.h ... \ + # ... + set_dir_from "$object" + # Use the source, not the object, to determine the base name, since + # that's sadly what pgcc will do too. + set_base_from "$source" + tmpdepfile=$base.d + + # For projects that build the same source file twice into different object + # files, the pgcc approach of using the *source* file root name can cause + # problems in parallel builds. Use a locking strategy to avoid stomping on + # the same $tmpdepfile. + lockdir=$base.d-lock + trap " + echo '$0: caught signal, cleaning up...' >&2 + rmdir '$lockdir' + exit 1 + " 1 2 13 15 + numtries=100 + i=$numtries + while test $i -gt 0; do + # mkdir is a portable test-and-set. + if mkdir "$lockdir" 2>/dev/null; then + # This process acquired the lock. + "$@" -MD + stat=$? + # Release the lock. + rmdir "$lockdir" + break + else + # If the lock is being held by a different process, wait + # until the winning process is done or we timeout. + while test -d "$lockdir" && test $i -gt 0; do + sleep 1 + i=`expr $i - 1` + done + fi + i=`expr $i - 1` + done + trap - 1 2 13 15 + if test $i -le 0; then + echo "$0: failed to acquire lock after $numtries attempts" >&2 + echo "$0: check lockdir '$lockdir'" >&2 + exit 1 + fi + + if test $stat -ne 0; then + rm -f "$tmpdepfile" + exit $stat + fi + rm -f "$depfile" + # Each line is of the form `foo.o: dependent.h', + # or `foo.o: dep1.h dep2.h \', or ` dep3.h dep4.h \'. + # Do two passes, one to just change these to + # `$object: dependent.h' and one to simply `dependent.h:'. + sed "s,^[^:]*:,$object :," < "$tmpdepfile" > "$depfile" + # Some versions of the HPUX 10.20 sed can't process this invocation + # correctly. Breaking it into two sed invocations is a workaround. + sed 's,^[^:]*: \(.*\)$,\1,;s/^\\$//;/^$/d;/:$/d' < "$tmpdepfile" \ + | sed -e 's/$/ :/' >> "$depfile" + rm -f "$tmpdepfile" + ;; + +hp2) + # The "hp" stanza above does not work with aCC (C++) and HP's ia64 + # compilers, which have integrated preprocessors. The correct option + # to use with these is +Maked; it writes dependencies to a file named + # 'foo.d', which lands next to the object file, wherever that + # happens to be. + # Much of this is similar to the tru64 case; see comments there. + set_dir_from "$object" + set_base_from "$object" + if test "$libtool" = yes; then + tmpdepfile1=$dir$base.d + tmpdepfile2=$dir.libs/$base.d + "$@" -Wc,+Maked + else + tmpdepfile1=$dir$base.d + tmpdepfile2=$dir$base.d + "$@" +Maked + fi + stat=$? + if test $stat -ne 0; then + rm -f "$tmpdepfile1" "$tmpdepfile2" + exit $stat + fi + + for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" + do + test -f "$tmpdepfile" && break + done + if test -f "$tmpdepfile"; then + sed -e "s,^.*\.[$lower]*:,$object:," "$tmpdepfile" > "$depfile" + # Add 'dependent.h:' lines. + sed -ne '2,${ + s/^ *// + s/ \\*$// + s/$/:/ + p + }' "$tmpdepfile" >> "$depfile" + else + make_dummy_depfile + fi + rm -f "$tmpdepfile" "$tmpdepfile2" + ;; + +tru64) + # The Tru64 compiler uses -MD to generate dependencies as a side + # effect. 'cc -MD -o foo.o ...' puts the dependencies into 'foo.o.d'. + # At least on Alpha/Redhat 6.1, Compaq CCC V6.2-504 seems to put + # dependencies in 'foo.d' instead, so we check for that too. + # Subdirectories are respected. + set_dir_from "$object" + set_base_from "$object" + + if test "$libtool" = yes; then + # Libtool generates 2 separate objects for the 2 libraries. These + # two compilations output dependencies in $dir.libs/$base.o.d and + # in $dir$base.o.d. We have to check for both files, because + # one of the two compilations can be disabled. We should prefer + # $dir$base.o.d over $dir.libs/$base.o.d because the latter is + # automatically cleaned when .libs/ is deleted, while ignoring + # the former would cause a distcleancheck panic. + tmpdepfile1=$dir$base.o.d # libtool 1.5 + tmpdepfile2=$dir.libs/$base.o.d # Likewise. + tmpdepfile3=$dir.libs/$base.d # Compaq CCC V6.2-504 + "$@" -Wc,-MD + else + tmpdepfile1=$dir$base.d + tmpdepfile2=$dir$base.d + tmpdepfile3=$dir$base.d + "$@" -MD + fi + + stat=$? + if test $stat -ne 0; then + rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" + exit $stat + fi + + for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" + do + test -f "$tmpdepfile" && break + done + # Same post-processing that is required for AIX mode. + aix_post_process_depfile + ;; + +msvc7) + if test "$libtool" = yes; then + showIncludes=-Wc,-showIncludes + else + showIncludes=-showIncludes + fi + "$@" $showIncludes > "$tmpdepfile" + stat=$? + grep -v '^Note: including file: ' "$tmpdepfile" + if test $stat -ne 0; then + rm -f "$tmpdepfile" + exit $stat + fi + rm -f "$depfile" + echo "$object : \\" > "$depfile" + # The first sed program below extracts the file names and escapes + # backslashes for cygpath. The second sed program outputs the file + # name when reading, but also accumulates all include files in the + # hold buffer in order to output them again at the end. This only + # works with sed implementations that can handle large buffers. + sed < "$tmpdepfile" -n ' +/^Note: including file: *\(.*\)/ { + s//\1/ + s/\\/\\\\/g + p +}' | $cygpath_u | sort -u | sed -n ' +s/ /\\ /g +s/\(.*\)/'"$tab"'\1 \\/p +s/.\(.*\) \\/\1:/ +H +$ { + s/.*/'"$tab"'/ + G + p +}' >> "$depfile" + echo >> "$depfile" # make sure the fragment doesn't end with a backslash + rm -f "$tmpdepfile" + ;; + +msvc7msys) + # This case exists only to let depend.m4 do its work. It works by + # looking at the text of this script. This case will never be run, + # since it is checked for above. + exit 1 + ;; + +#nosideeffect) + # This comment above is used by automake to tell side-effect + # dependency tracking mechanisms from slower ones. + +dashmstdout) + # Important note: in order to support this mode, a compiler *must* + # always write the preprocessed file to stdout, regardless of -o. + "$@" || exit $? + + # Remove the call to Libtool. + if test "$libtool" = yes; then + while test "X$1" != 'X--mode=compile'; do + shift + done + shift + fi + + # Remove '-o $object'. + IFS=" " + for arg + do + case $arg in + -o) + shift + ;; + $object) + shift + ;; + *) + set fnord "$@" "$arg" + shift # fnord + shift # $arg + ;; + esac + done + + test -z "$dashmflag" && dashmflag=-M + # Require at least two characters before searching for ':' + # in the target name. This is to cope with DOS-style filenames: + # a dependency such as 'c:/foo/bar' could be seen as target 'c' otherwise. + "$@" $dashmflag | + sed "s|^[$tab ]*[^:$tab ][^:][^:]*:[$tab ]*|$object: |" > "$tmpdepfile" + rm -f "$depfile" + cat < "$tmpdepfile" > "$depfile" + # Some versions of the HPUX 10.20 sed can't process this sed invocation + # correctly. Breaking it into two sed invocations is a workaround. + tr ' ' "$nl" < "$tmpdepfile" \ + | sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' \ + | sed -e 's/$/ :/' >> "$depfile" + rm -f "$tmpdepfile" + ;; + +dashXmstdout) + # This case only exists to satisfy depend.m4. It is never actually + # run, as this mode is specially recognized in the preamble. + exit 1 + ;; + +makedepend) + "$@" || exit $? + # Remove any Libtool call + if test "$libtool" = yes; then + while test "X$1" != 'X--mode=compile'; do + shift + done + shift + fi + # X makedepend + shift + cleared=no eat=no + for arg + do + case $cleared in + no) + set ""; shift + cleared=yes ;; + esac + if test $eat = yes; then + eat=no + continue + fi + case "$arg" in + -D*|-I*) + set fnord "$@" "$arg"; shift ;; + # Strip any option that makedepend may not understand. Remove + # the object too, otherwise makedepend will parse it as a source file. + -arch) + eat=yes ;; + -*|$object) + ;; + *) + set fnord "$@" "$arg"; shift ;; + esac + done + obj_suffix=`echo "$object" | sed 's/^.*\././'` + touch "$tmpdepfile" + ${MAKEDEPEND-makedepend} -o"$obj_suffix" -f"$tmpdepfile" "$@" + rm -f "$depfile" + # makedepend may prepend the VPATH from the source file name to the object. + # No need to regex-escape $object, excess matching of '.' is harmless. + sed "s|^.*\($object *:\)|\1|" "$tmpdepfile" > "$depfile" + # Some versions of the HPUX 10.20 sed can't process the last invocation + # correctly. Breaking it into two sed invocations is a workaround. + sed '1,2d' "$tmpdepfile" \ + | tr ' ' "$nl" \ + | sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' \ + | sed -e 's/$/ :/' >> "$depfile" + rm -f "$tmpdepfile" "$tmpdepfile".bak + ;; + +cpp) + # Important note: in order to support this mode, a compiler *must* + # always write the preprocessed file to stdout. + "$@" || exit $? + + # Remove the call to Libtool. + if test "$libtool" = yes; then + while test "X$1" != 'X--mode=compile'; do + shift + done + shift + fi + + # Remove '-o $object'. + IFS=" " + for arg + do + case $arg in + -o) + shift + ;; + $object) + shift + ;; + *) + set fnord "$@" "$arg" + shift # fnord + shift # $arg + ;; + esac + done + + "$@" -E \ + | sed -n -e '/^# [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' \ + -e '/^#line [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' \ + | sed '$ s: \\$::' > "$tmpdepfile" + rm -f "$depfile" + echo "$object : \\" > "$depfile" + cat < "$tmpdepfile" >> "$depfile" + sed < "$tmpdepfile" '/^$/d;s/^ //;s/ \\$//;s/$/ :/' >> "$depfile" + rm -f "$tmpdepfile" + ;; + +msvisualcpp) + # Important note: in order to support this mode, a compiler *must* + # always write the preprocessed file to stdout. + "$@" || exit $? + + # Remove the call to Libtool. + if test "$libtool" = yes; then + while test "X$1" != 'X--mode=compile'; do + shift + done + shift + fi + + IFS=" " + for arg + do + case "$arg" in + -o) + shift + ;; + $object) + shift + ;; + "-Gm"|"/Gm"|"-Gi"|"/Gi"|"-ZI"|"/ZI") + set fnord "$@" + shift + shift + ;; + *) + set fnord "$@" "$arg" + shift + shift + ;; + esac + done + "$@" -E 2>/dev/null | + sed -n '/^#line [0-9][0-9]* "\([^"]*\)"/ s::\1:p' | $cygpath_u | sort -u > "$tmpdepfile" + rm -f "$depfile" + echo "$object : \\" > "$depfile" + sed < "$tmpdepfile" -n -e 's% %\\ %g' -e '/^\(.*\)$/ s::'"$tab"'\1 \\:p' >> "$depfile" + echo "$tab" >> "$depfile" + sed < "$tmpdepfile" -n -e 's% %\\ %g' -e '/^\(.*\)$/ s::\1\::p' >> "$depfile" + rm -f "$tmpdepfile" + ;; + +msvcmsys) + # This case exists only to let depend.m4 do its work. It works by + # looking at the text of this script. This case will never be run, + # since it is checked for above. + exit 1 + ;; + +none) + exec "$@" + ;; + +*) + echo "Unknown depmode $depmode" 1>&2 + exit 1 + ;; +esac + +exit 0 + +# Local Variables: +# mode: shell-script +# sh-indentation: 2 +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "scriptversion=" +# time-stamp-format: "%:y-%02m-%02d.%02H" +# time-stamp-time-zone: "UTC" +# time-stamp-end: "; # UTC" +# End: diff --git a/doc/Makefile.am b/doc/Makefile.am new file mode 100644 index 0000000..0eefa44 --- /dev/null +++ b/doc/Makefile.am @@ -0,0 +1,320 @@ +# nghttp2 - HTTP/2 C Library + +# Copyright (c) 2012 Tatsuhiro Tsujikawa + +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: + +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. + +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +man_MANS = nghttp.1 nghttpd.1 nghttpx.1 h2load.1 + +APIDOCS= \ + apiref.rst \ + macros.rst \ + enums.rst \ + types.rst \ + nghttp2_check_header_name.rst \ + nghttp2_check_header_value.rst \ + nghttp2_hd_deflate_bound.rst \ + nghttp2_hd_deflate_change_table_size.rst \ + nghttp2_hd_deflate_del.rst \ + nghttp2_hd_deflate_hd.rst \ + nghttp2_hd_deflate_new.rst \ + nghttp2_hd_deflate_new2.rst \ + nghttp2_hd_inflate_change_table_size.rst \ + nghttp2_hd_inflate_del.rst \ + nghttp2_hd_inflate_end_headers.rst \ + nghttp2_hd_inflate_hd.rst \ + nghttp2_hd_inflate_new.rst \ + nghttp2_hd_inflate_new2.rst \ + nghttp2_is_fatal.rst \ + nghttp2_nv_compare_name.rst \ + nghttp2_option_del.rst \ + nghttp2_option_new.rst \ + nghttp2_option_set_no_auto_window_update.rst \ + nghttp2_option_set_no_http_messaging.rst \ + nghttp2_option_set_peer_max_concurrent_streams.rst \ + nghttp2_option_set_no_recv_client_magic.rst \ + nghttp2_pack_settings_payload.rst \ + nghttp2_priority_spec_check_default.rst \ + nghttp2_priority_spec_default_init.rst \ + nghttp2_priority_spec_init.rst \ + nghttp2_select_next_protocol.rst \ + nghttp2_session_callbacks_del.rst \ + nghttp2_session_callbacks_new.rst \ + nghttp2_session_callbacks_set_before_frame_send_callback.rst \ + nghttp2_session_callbacks_set_data_source_read_length_callback.rst \ + nghttp2_session_callbacks_set_on_begin_frame_callback.rst \ + nghttp2_session_callbacks_set_on_begin_headers_callback.rst \ + nghttp2_session_callbacks_set_on_data_chunk_recv_callback.rst \ + nghttp2_session_callbacks_set_on_frame_not_send_callback.rst \ + nghttp2_session_callbacks_set_on_frame_recv_callback.rst \ + nghttp2_session_callbacks_set_on_frame_send_callback.rst \ + nghttp2_session_callbacks_set_on_header_callback.rst \ + nghttp2_session_callbacks_set_on_invalid_frame_recv_callback.rst \ + nghttp2_session_callbacks_set_on_stream_close_callback.rst \ + nghttp2_session_callbacks_set_recv_callback.rst \ + nghttp2_session_callbacks_set_select_padding_callback.rst \ + nghttp2_session_callbacks_set_send_callback.rst \ + nghttp2_session_callbacks_set_send_data_callback.rst \ + nghttp2_session_client_new.rst \ + nghttp2_session_client_new2.rst \ + nghttp2_session_client_new3.rst \ + nghttp2_session_consume.rst \ + nghttp2_session_consume_connection.rst \ + nghttp2_session_consume_stream.rst \ + nghttp2_session_del.rst \ + nghttp2_session_get_effective_local_window_size.rst \ + nghttp2_session_get_effective_recv_data_length.rst \ + nghttp2_session_get_last_proc_stream_id.rst \ + nghttp2_session_get_next_stream_id.rst \ + nghttp2_session_get_outbound_queue_size.rst \ + nghttp2_session_get_remote_settings.rst \ + nghttp2_session_get_remote_window_size.rst \ + nghttp2_session_get_stream_effective_local_window_size.rst \ + nghttp2_session_get_stream_effective_recv_data_length.rst \ + nghttp2_session_get_stream_local_close.rst \ + nghttp2_session_get_stream_remote_close.rst \ + nghttp2_session_get_stream_remote_window_size.rst \ + nghttp2_session_get_stream_user_data.rst \ + nghttp2_session_mem_recv.rst \ + nghttp2_session_mem_send.rst \ + nghttp2_session_recv.rst \ + nghttp2_session_resume_data.rst \ + nghttp2_session_send.rst \ + nghttp2_session_server_new.rst \ + nghttp2_session_server_new2.rst \ + nghttp2_session_server_new3.rst \ + nghttp2_session_set_next_stream_id.rst \ + nghttp2_session_set_stream_user_data.rst \ + nghttp2_session_terminate_session.rst \ + nghttp2_session_terminate_session2.rst \ + nghttp2_session_upgrade.rst \ + nghttp2_session_want_read.rst \ + nghttp2_session_want_write.rst \ + nghttp2_strerror.rst \ + nghttp2_submit_data.rst \ + nghttp2_submit_goaway.rst \ + nghttp2_submit_headers.rst \ + nghttp2_submit_ping.rst \ + nghttp2_submit_priority.rst \ + nghttp2_submit_push_promise.rst \ + nghttp2_submit_request.rst \ + nghttp2_submit_response.rst \ + nghttp2_submit_rst_stream.rst \ + nghttp2_submit_settings.rst \ + nghttp2_submit_shutdown_notice.rst \ + nghttp2_submit_trailer.rst \ + nghttp2_submit_window_update.rst \ + nghttp2_version.rst + +EXTRA_DIST = \ + mkapiref.py \ + README.rst \ + programmers-guide.rst \ + $(APIDOCS) \ + nghttp.1.rst \ + nghttpd.1.rst \ + nghttpx.1.rst \ + h2load.1.rst \ + sources/index.rst \ + sources/tutorial-client.rst \ + sources/tutorial-server.rst \ + sources/tutorial-hpack.rst \ + sources/nghttpx-howto.rst \ + sources/h2load-howto.rst \ + sources/libnghttp2_asio.rst \ + sources/python-apiref.rst \ + sources/building-android-binary.rst \ + sources/contribute.rst \ + _themes/sphinx_rtd_theme/__init__.py \ + _themes/sphinx_rtd_theme/breadcrumbs.html \ + _themes/sphinx_rtd_theme/footer.html \ + _themes/sphinx_rtd_theme/layout.html \ + _themes/sphinx_rtd_theme/layout_old.html \ + _themes/sphinx_rtd_theme/search.html \ + _themes/sphinx_rtd_theme/searchbox.html \ + _themes/sphinx_rtd_theme/static/css/badge_only.css \ + _themes/sphinx_rtd_theme/static/css/theme.css \ + _themes/sphinx_rtd_theme/static/fonts/FontAwesome.otf \ + _themes/sphinx_rtd_theme/static/fonts/fontawesome-webfont.eot \ + _themes/sphinx_rtd_theme/static/fonts/fontawesome-webfont.svg \ + _themes/sphinx_rtd_theme/static/fonts/fontawesome-webfont.ttf \ + _themes/sphinx_rtd_theme/static/fonts/fontawesome-webfont.woff \ + _themes/sphinx_rtd_theme/static/js/theme.js \ + _themes/sphinx_rtd_theme/theme.conf \ + _themes/sphinx_rtd_theme/versions.html \ + $(man_MANS) \ + bash_completion/nghttp \ + bash_completion/nghttpd \ + bash_completion/nghttpx \ + bash_completion/h2load + +# Makefile for Sphinx documentation +# + +# You can set these variables from the command line. +SPHINXOPTS = +SPHINXBUILD = sphinx-build +PAPER = +BUILDDIR = manual + +# Internal variables. +PAPEROPT_a4 = -D latex_paper_size=a4 +PAPEROPT_letter = -D latex_paper_size=letter +ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . + +.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest + +help: + @echo "Please use \`make ' where is one of" + @echo " html to make standalone HTML files" + @echo " dirhtml to make HTML files named index.html in directories" + @echo " singlehtml to make a single large HTML file" + @echo " pickle to make pickle files" + @echo " json to make JSON files" + @echo " htmlhelp to make HTML files and a HTML help project" + @echo " qthelp to make HTML files and a qthelp project" + @echo " devhelp to make HTML files and a Devhelp project" + @echo " epub to make an epub" + @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" + @echo " latexpdf to make LaTeX files and run them through pdflatex" + @echo " text to make text files" + @echo " man to make manual pages" + @echo " changes to make an overview of all changed/added/deprecated items" + @echo " linkcheck to check all external links for integrity" + @echo " doctest to run all doctests embedded in the documentation (if enabled)" + +apiref.rst macros.rst enums.rst types.rst: \ + $(top_builddir)/lib/includes/nghttp2/nghttp2ver.h \ + $(top_builddir)/lib/includes/nghttp2/nghttp2.h + $(PYTHON) $(top_srcdir)/doc/mkapiref.py \ + $@ macros.rst enums.rst types.rst . $^ + +# Inspired by +# http://www.gnu.org/savannah-checkouts/gnu/automake/manual/html_node/Multiple-Outputs.html +apidoc.stamp: $(top_builddir)/lib/includes/nghttp2/nghttp2ver.h \ + $(top_builddir)/lib/includes/nghttp2/nghttp2.h + @rm -f apidoc.tmp + @touch apidoc.tmp + $(PYTHON) $(top_srcdir)/doc/mkapiref.py \ + $@ macros.rst enums.rst types.rst . $^ + @mv -f apidoc.tmp $@ +$(APIDOC): apidoc.stamp +## Recover from the removal of $@ + @if test -f $@; then :; else \ + rm -f apidoc.stamp; \ + $(MAKE) $(AM_MAKEFLAGS) apidoc.stamp; \ + fi + +clean-local: + -rm $(APIDOCS) + -rm -rf $(BUILDDIR)/* + +html-local: apiref.rst + $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." + +dirhtml: + $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." + +singlehtml: + $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml + @echo + @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." + +pickle: + $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle + @echo + @echo "Build finished; now you can process the pickle files." + +json: + $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json + @echo + @echo "Build finished; now you can process the JSON files." + +htmlhelp: + $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp + @echo + @echo "Build finished; now you can run HTML Help Workshop with the" \ + ".hhp project file in $(BUILDDIR)/htmlhelp." + +qthelp: + $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp + @echo + @echo "Build finished; now you can run "qcollectiongenerator" with the" \ + ".qhcp project file in $(BUILDDIR)/qthelp, like this:" + @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/nghttp2.qhcp" + @echo "To view the help file:" + @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/nghttp2.qhc" + +devhelp: + $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp + @echo + @echo "Build finished." + @echo "To view the help file:" + @echo "# mkdir -p $$HOME/.local/share/devhelp/nghttp2" + @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/nghttp2" + @echo "# devhelp" + +epub: + $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub + @echo + @echo "Build finished. The epub file is in $(BUILDDIR)/epub." + +latex: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo + @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." + @echo "Run \`make' in that directory to run these through (pdf)latex" \ + "(use \`make latexpdf' here to do that automatically)." + +latexpdf: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo "Running LaTeX files through pdflatex..." + $(MAKE) -C $(BUILDDIR)/latex all-pdf + @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." + +text: + $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text + @echo + @echo "Build finished. The text files are in $(BUILDDIR)/text." + +man: apiref.rst + $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man + @echo + @echo "Build finished. The manual pages are in $(BUILDDIR)/man." + +changes: + $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes + @echo + @echo "The overview file is in $(BUILDDIR)/changes." + +linkcheck: + $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck + @echo + @echo "Link check complete; look for any errors in the above output " \ + "or in $(BUILDDIR)/linkcheck/output.txt." + +doctest: + $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest + @echo "Testing of doctests in the sources finished, look at the " \ + "results in $(BUILDDIR)/doctest/output.txt." diff --git a/doc/Makefile.in b/doc/Makefile.in new file mode 100644 index 0000000..0f53eb8 --- /dev/null +++ b/doc/Makefile.in @@ -0,0 +1,939 @@ +# Makefile.in generated by automake 1.14.1 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2013 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +# nghttp2 - HTTP/2 C Library + +# Copyright (c) 2012 Tatsuhiro Tsujikawa + +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: + +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. + +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +VPATH = @srcdir@ +am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)' +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +target_triplet = @target@ +subdir = doc +DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \ + $(srcdir)/conf.py.in $(srcdir)/index.rst.in \ + $(srcdir)/package_README.rst.in \ + $(srcdir)/tutorial-client.rst.in \ + $(srcdir)/tutorial-server.rst.in \ + $(srcdir)/tutorial-hpack.rst.in $(srcdir)/nghttpx-howto.rst.in \ + $(srcdir)/h2load-howto.rst.in $(srcdir)/libnghttp2_asio.rst.in \ + $(srcdir)/python-apiref.rst.in \ + $(srcdir)/building-android-binary.rst.in \ + $(srcdir)/nghttp2.h.rst.in $(srcdir)/nghttp2ver.h.rst.in \ + $(srcdir)/asio_http2.h.rst.in \ + $(srcdir)/asio_http2_server.h.rst.in \ + $(srcdir)/asio_http2_client.h.rst.in \ + $(srcdir)/contribute.rst.in +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/ax_boost_asio.m4 \ + $(top_srcdir)/m4/ax_boost_base.m4 \ + $(top_srcdir)/m4/ax_boost_system.m4 \ + $(top_srcdir)/m4/ax_boost_thread.m4 \ + $(top_srcdir)/m4/ax_check_compile_flag.m4 \ + $(top_srcdir)/m4/ax_cxx_compile_stdcxx_11.m4 \ + $(top_srcdir)/m4/ax_have_epoll.m4 \ + $(top_srcdir)/m4/ax_python_devel.m4 \ + $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/libxml2.m4 \ + $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ + $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = conf.py index.rst package_README.rst \ + tutorial-client.rst tutorial-server.rst tutorial-hpack.rst \ + nghttpx-howto.rst h2load-howto.rst libnghttp2_asio.rst \ + python-apiref.rst building-android-binary.rst nghttp2.h.rst \ + nghttp2ver.h.rst asio_http2.h.rst asio_http2_server.h.rst \ + asio_http2_client.h.rst contribute.rst +CONFIG_CLEAN_VPATH_FILES = +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +SOURCES = +DIST_SOURCES = +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +am__uninstall_files_from_dir = { \ + test -z "$$files" \ + || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ + || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ + $(am__cd) "$$dir" && rm -f $$files; }; \ + } +man1dir = $(mandir)/man1 +am__installdirs = "$(DESTDIR)$(man1dir)" +NROFF = nroff +MANS = $(man_MANS) +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +APPLDFLAGS = @APPLDFLAGS@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +BOOST_ASIO_LIB = @BOOST_ASIO_LIB@ +BOOST_CPPFLAGS = @BOOST_CPPFLAGS@ +BOOST_LDFLAGS = @BOOST_LDFLAGS@ +BOOST_SYSTEM_LIB = @BOOST_SYSTEM_LIB@ +BOOST_THREAD_LIB = @BOOST_THREAD_LIB@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CUNIT_CFLAGS = @CUNIT_CFLAGS@ +CUNIT_LIBS = @CUNIT_LIBS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +CYTHON = @CYTHON@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DLLTOOL = @DLLTOOL@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +GREP = @GREP@ +HAVE_CXX11 = @HAVE_CXX11@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +JANSSON_CFLAGS = @JANSSON_CFLAGS@ +JANSSON_LIBS = @JANSSON_LIBS@ +JEMALLOC_LIBS = @JEMALLOC_LIBS@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBEVENT_OPENSSL_CFLAGS = @LIBEVENT_OPENSSL_CFLAGS@ +LIBEVENT_OPENSSL_LIBS = @LIBEVENT_OPENSSL_LIBS@ +LIBEV_CFLAGS = @LIBEV_CFLAGS@ +LIBEV_LIBS = @LIBEV_LIBS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBSPDYLAY_CFLAGS = @LIBSPDYLAY_CFLAGS@ +LIBSPDYLAY_LIBS = @LIBSPDYLAY_LIBS@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +LT_AGE = @LT_AGE@ +LT_CURRENT = @LT_CURRENT@ +LT_REVISION = @LT_REVISION@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MKDIR_P = @MKDIR_P@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OPENSSL_CFLAGS = @OPENSSL_CFLAGS@ +OPENSSL_LIBS = @OPENSSL_LIBS@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PACKAGE_VERSION_NUM = @PACKAGE_VERSION_NUM@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PKG_CONFIG = @PKG_CONFIG@ +PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ +PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ +PYTHON = @PYTHON@ +PYTHON_CPPFLAGS = @PYTHON_CPPFLAGS@ +PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@ +PYTHON_EXTRA_LDFLAGS = @PYTHON_EXTRA_LDFLAGS@ +PYTHON_EXTRA_LIBS = @PYTHON_EXTRA_LIBS@ +PYTHON_LDFLAGS = @PYTHON_LDFLAGS@ +PYTHON_PLATFORM = @PYTHON_PLATFORM@ +PYTHON_PREFIX = @PYTHON_PREFIX@ +PYTHON_SITE_PKG = @PYTHON_SITE_PKG@ +PYTHON_VERSION = @PYTHON_VERSION@ +RANLIB = @RANLIB@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +TESTLDADD = @TESTLDADD@ +VERSION = @VERSION@ +WARNCFLAGS = @WARNCFLAGS@ +XML2_CONFIG = @XML2_CONFIG@ +XML_CPPFLAGS = @XML_CPPFLAGS@ +XML_LIBS = @XML_LIBS@ +ZLIB_CFLAGS = @ZLIB_CFLAGS@ +ZLIB_LIBS = @ZLIB_LIBS@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +pkgpyexecdir = @pkgpyexecdir@ +pkgpythondir = @pkgpythondir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +pyexecdir = @pyexecdir@ +pythondir = @pythondir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target = @target@ +target_alias = @target_alias@ +target_cpu = @target_cpu@ +target_os = @target_os@ +target_vendor = @target_vendor@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +man_MANS = nghttp.1 nghttpd.1 nghttpx.1 h2load.1 +APIDOCS = \ + apiref.rst \ + macros.rst \ + enums.rst \ + types.rst \ + nghttp2_check_header_name.rst \ + nghttp2_check_header_value.rst \ + nghttp2_hd_deflate_bound.rst \ + nghttp2_hd_deflate_change_table_size.rst \ + nghttp2_hd_deflate_del.rst \ + nghttp2_hd_deflate_hd.rst \ + nghttp2_hd_deflate_new.rst \ + nghttp2_hd_deflate_new2.rst \ + nghttp2_hd_inflate_change_table_size.rst \ + nghttp2_hd_inflate_del.rst \ + nghttp2_hd_inflate_end_headers.rst \ + nghttp2_hd_inflate_hd.rst \ + nghttp2_hd_inflate_new.rst \ + nghttp2_hd_inflate_new2.rst \ + nghttp2_is_fatal.rst \ + nghttp2_nv_compare_name.rst \ + nghttp2_option_del.rst \ + nghttp2_option_new.rst \ + nghttp2_option_set_no_auto_window_update.rst \ + nghttp2_option_set_no_http_messaging.rst \ + nghttp2_option_set_peer_max_concurrent_streams.rst \ + nghttp2_option_set_no_recv_client_magic.rst \ + nghttp2_pack_settings_payload.rst \ + nghttp2_priority_spec_check_default.rst \ + nghttp2_priority_spec_default_init.rst \ + nghttp2_priority_spec_init.rst \ + nghttp2_select_next_protocol.rst \ + nghttp2_session_callbacks_del.rst \ + nghttp2_session_callbacks_new.rst \ + nghttp2_session_callbacks_set_before_frame_send_callback.rst \ + nghttp2_session_callbacks_set_data_source_read_length_callback.rst \ + nghttp2_session_callbacks_set_on_begin_frame_callback.rst \ + nghttp2_session_callbacks_set_on_begin_headers_callback.rst \ + nghttp2_session_callbacks_set_on_data_chunk_recv_callback.rst \ + nghttp2_session_callbacks_set_on_frame_not_send_callback.rst \ + nghttp2_session_callbacks_set_on_frame_recv_callback.rst \ + nghttp2_session_callbacks_set_on_frame_send_callback.rst \ + nghttp2_session_callbacks_set_on_header_callback.rst \ + nghttp2_session_callbacks_set_on_invalid_frame_recv_callback.rst \ + nghttp2_session_callbacks_set_on_stream_close_callback.rst \ + nghttp2_session_callbacks_set_recv_callback.rst \ + nghttp2_session_callbacks_set_select_padding_callback.rst \ + nghttp2_session_callbacks_set_send_callback.rst \ + nghttp2_session_callbacks_set_send_data_callback.rst \ + nghttp2_session_client_new.rst \ + nghttp2_session_client_new2.rst \ + nghttp2_session_client_new3.rst \ + nghttp2_session_consume.rst \ + nghttp2_session_consume_connection.rst \ + nghttp2_session_consume_stream.rst \ + nghttp2_session_del.rst \ + nghttp2_session_get_effective_local_window_size.rst \ + nghttp2_session_get_effective_recv_data_length.rst \ + nghttp2_session_get_last_proc_stream_id.rst \ + nghttp2_session_get_next_stream_id.rst \ + nghttp2_session_get_outbound_queue_size.rst \ + nghttp2_session_get_remote_settings.rst \ + nghttp2_session_get_remote_window_size.rst \ + nghttp2_session_get_stream_effective_local_window_size.rst \ + nghttp2_session_get_stream_effective_recv_data_length.rst \ + nghttp2_session_get_stream_local_close.rst \ + nghttp2_session_get_stream_remote_close.rst \ + nghttp2_session_get_stream_remote_window_size.rst \ + nghttp2_session_get_stream_user_data.rst \ + nghttp2_session_mem_recv.rst \ + nghttp2_session_mem_send.rst \ + nghttp2_session_recv.rst \ + nghttp2_session_resume_data.rst \ + nghttp2_session_send.rst \ + nghttp2_session_server_new.rst \ + nghttp2_session_server_new2.rst \ + nghttp2_session_server_new3.rst \ + nghttp2_session_set_next_stream_id.rst \ + nghttp2_session_set_stream_user_data.rst \ + nghttp2_session_terminate_session.rst \ + nghttp2_session_terminate_session2.rst \ + nghttp2_session_upgrade.rst \ + nghttp2_session_want_read.rst \ + nghttp2_session_want_write.rst \ + nghttp2_strerror.rst \ + nghttp2_submit_data.rst \ + nghttp2_submit_goaway.rst \ + nghttp2_submit_headers.rst \ + nghttp2_submit_ping.rst \ + nghttp2_submit_priority.rst \ + nghttp2_submit_push_promise.rst \ + nghttp2_submit_request.rst \ + nghttp2_submit_response.rst \ + nghttp2_submit_rst_stream.rst \ + nghttp2_submit_settings.rst \ + nghttp2_submit_shutdown_notice.rst \ + nghttp2_submit_trailer.rst \ + nghttp2_submit_window_update.rst \ + nghttp2_version.rst + +EXTRA_DIST = \ + mkapiref.py \ + README.rst \ + programmers-guide.rst \ + $(APIDOCS) \ + nghttp.1.rst \ + nghttpd.1.rst \ + nghttpx.1.rst \ + h2load.1.rst \ + sources/index.rst \ + sources/tutorial-client.rst \ + sources/tutorial-server.rst \ + sources/tutorial-hpack.rst \ + sources/nghttpx-howto.rst \ + sources/h2load-howto.rst \ + sources/libnghttp2_asio.rst \ + sources/python-apiref.rst \ + sources/building-android-binary.rst \ + sources/contribute.rst \ + _themes/sphinx_rtd_theme/__init__.py \ + _themes/sphinx_rtd_theme/breadcrumbs.html \ + _themes/sphinx_rtd_theme/footer.html \ + _themes/sphinx_rtd_theme/layout.html \ + _themes/sphinx_rtd_theme/layout_old.html \ + _themes/sphinx_rtd_theme/search.html \ + _themes/sphinx_rtd_theme/searchbox.html \ + _themes/sphinx_rtd_theme/static/css/badge_only.css \ + _themes/sphinx_rtd_theme/static/css/theme.css \ + _themes/sphinx_rtd_theme/static/fonts/FontAwesome.otf \ + _themes/sphinx_rtd_theme/static/fonts/fontawesome-webfont.eot \ + _themes/sphinx_rtd_theme/static/fonts/fontawesome-webfont.svg \ + _themes/sphinx_rtd_theme/static/fonts/fontawesome-webfont.ttf \ + _themes/sphinx_rtd_theme/static/fonts/fontawesome-webfont.woff \ + _themes/sphinx_rtd_theme/static/js/theme.js \ + _themes/sphinx_rtd_theme/theme.conf \ + _themes/sphinx_rtd_theme/versions.html \ + $(man_MANS) \ + bash_completion/nghttp \ + bash_completion/nghttpd \ + bash_completion/nghttpx \ + bash_completion/h2load + + +# Makefile for Sphinx documentation +# + +# You can set these variables from the command line. +SPHINXOPTS = +SPHINXBUILD = sphinx-build +PAPER = +BUILDDIR = manual + +# Internal variables. +PAPEROPT_a4 = -D latex_paper_size=a4 +PAPEROPT_letter = -D latex_paper_size=letter +ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . +all: all-am + +.SUFFIXES: +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu doc/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu doc/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): +conf.py: $(top_builddir)/config.status $(srcdir)/conf.py.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +index.rst: $(top_builddir)/config.status $(srcdir)/index.rst.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +package_README.rst: $(top_builddir)/config.status $(srcdir)/package_README.rst.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +tutorial-client.rst: $(top_builddir)/config.status $(srcdir)/tutorial-client.rst.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +tutorial-server.rst: $(top_builddir)/config.status $(srcdir)/tutorial-server.rst.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +tutorial-hpack.rst: $(top_builddir)/config.status $(srcdir)/tutorial-hpack.rst.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +nghttpx-howto.rst: $(top_builddir)/config.status $(srcdir)/nghttpx-howto.rst.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +h2load-howto.rst: $(top_builddir)/config.status $(srcdir)/h2load-howto.rst.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +libnghttp2_asio.rst: $(top_builddir)/config.status $(srcdir)/libnghttp2_asio.rst.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +python-apiref.rst: $(top_builddir)/config.status $(srcdir)/python-apiref.rst.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +building-android-binary.rst: $(top_builddir)/config.status $(srcdir)/building-android-binary.rst.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +nghttp2.h.rst: $(top_builddir)/config.status $(srcdir)/nghttp2.h.rst.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +nghttp2ver.h.rst: $(top_builddir)/config.status $(srcdir)/nghttp2ver.h.rst.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +asio_http2.h.rst: $(top_builddir)/config.status $(srcdir)/asio_http2.h.rst.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +asio_http2_server.h.rst: $(top_builddir)/config.status $(srcdir)/asio_http2_server.h.rst.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +asio_http2_client.h.rst: $(top_builddir)/config.status $(srcdir)/asio_http2_client.h.rst.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +contribute.rst: $(top_builddir)/config.status $(srcdir)/contribute.rst.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs +install-man1: $(man_MANS) + @$(NORMAL_INSTALL) + @list1=''; \ + list2='$(man_MANS)'; \ + test -n "$(man1dir)" \ + && test -n "`echo $$list1$$list2`" \ + || exit 0; \ + echo " $(MKDIR_P) '$(DESTDIR)$(man1dir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(man1dir)" || exit 1; \ + { for i in $$list1; do echo "$$i"; done; \ + if test -n "$$list2"; then \ + for i in $$list2; do echo "$$i"; done \ + | sed -n '/\.1[a-z]*$$/p'; \ + fi; \ + } | while read p; do \ + if test -f $$p; then d=; else d="$(srcdir)/"; fi; \ + echo "$$d$$p"; echo "$$p"; \ + done | \ + sed -e 'n;s,.*/,,;p;h;s,.*\.,,;s,^[^1][0-9a-z]*$$,1,;x' \ + -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,' | \ + sed 'N;N;s,\n, ,g' | { \ + list=; while read file base inst; do \ + if test "$$base" = "$$inst"; then list="$$list $$file"; else \ + echo " $(INSTALL_DATA) '$$file' '$(DESTDIR)$(man1dir)/$$inst'"; \ + $(INSTALL_DATA) "$$file" "$(DESTDIR)$(man1dir)/$$inst" || exit $$?; \ + fi; \ + done; \ + for i in $$list; do echo "$$i"; done | $(am__base_list) | \ + while read files; do \ + test -z "$$files" || { \ + echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(man1dir)'"; \ + $(INSTALL_DATA) $$files "$(DESTDIR)$(man1dir)" || exit $$?; }; \ + done; } + +uninstall-man1: + @$(NORMAL_UNINSTALL) + @list=''; test -n "$(man1dir)" || exit 0; \ + files=`{ for i in $$list; do echo "$$i"; done; \ + l2='$(man_MANS)'; for i in $$l2; do echo "$$i"; done | \ + sed -n '/\.1[a-z]*$$/p'; \ + } | sed -e 's,.*/,,;h;s,.*\.,,;s,^[^1][0-9a-z]*$$,1,;x' \ + -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,'`; \ + dir='$(DESTDIR)$(man1dir)'; $(am__uninstall_files_from_dir) +tags TAGS: + +ctags CTAGS: + +cscope cscopelist: + + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(MANS) +installdirs: + for dir in "$(DESTDIR)$(man1dir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic clean-libtool clean-local mostlyclean-am + +distclean: distclean-am + -rm -f Makefile +distclean-am: clean-am distclean-generic + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: html-local + +info: info-am + +info-am: + +install-data-am: install-man + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: install-man1 + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-generic mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-man + +uninstall-man: uninstall-man1 + +.MAKE: install-am install-strip + +.PHONY: all all-am check check-am clean clean-generic clean-libtool \ + clean-local cscopelist-am ctags-am distclean distclean-generic \ + distclean-libtool distdir dvi dvi-am html html-am html-local \ + info info-am install install-am install-data install-data-am \ + install-dvi install-dvi-am install-exec install-exec-am \ + install-html install-html-am install-info install-info-am \ + install-man install-man1 install-pdf install-pdf-am install-ps \ + install-ps-am install-strip installcheck installcheck-am \ + installdirs maintainer-clean maintainer-clean-generic \ + mostlyclean mostlyclean-generic mostlyclean-libtool pdf pdf-am \ + ps ps-am tags-am uninstall uninstall-am uninstall-man \ + uninstall-man1 + + +.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest + +help: + @echo "Please use \`make ' where is one of" + @echo " html to make standalone HTML files" + @echo " dirhtml to make HTML files named index.html in directories" + @echo " singlehtml to make a single large HTML file" + @echo " pickle to make pickle files" + @echo " json to make JSON files" + @echo " htmlhelp to make HTML files and a HTML help project" + @echo " qthelp to make HTML files and a qthelp project" + @echo " devhelp to make HTML files and a Devhelp project" + @echo " epub to make an epub" + @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" + @echo " latexpdf to make LaTeX files and run them through pdflatex" + @echo " text to make text files" + @echo " man to make manual pages" + @echo " changes to make an overview of all changed/added/deprecated items" + @echo " linkcheck to check all external links for integrity" + @echo " doctest to run all doctests embedded in the documentation (if enabled)" + +apiref.rst macros.rst enums.rst types.rst: \ + $(top_builddir)/lib/includes/nghttp2/nghttp2ver.h \ + $(top_builddir)/lib/includes/nghttp2/nghttp2.h + $(PYTHON) $(top_srcdir)/doc/mkapiref.py \ + $@ macros.rst enums.rst types.rst . $^ + +# Inspired by +# http://www.gnu.org/savannah-checkouts/gnu/automake/manual/html_node/Multiple-Outputs.html +apidoc.stamp: $(top_builddir)/lib/includes/nghttp2/nghttp2ver.h \ + $(top_builddir)/lib/includes/nghttp2/nghttp2.h + @rm -f apidoc.tmp + @touch apidoc.tmp + $(PYTHON) $(top_srcdir)/doc/mkapiref.py \ + $@ macros.rst enums.rst types.rst . $^ + @mv -f apidoc.tmp $@ +$(APIDOC): apidoc.stamp + @if test -f $@; then :; else \ + rm -f apidoc.stamp; \ + $(MAKE) $(AM_MAKEFLAGS) apidoc.stamp; \ + fi + +clean-local: + -rm $(APIDOCS) + -rm -rf $(BUILDDIR)/* + +html-local: apiref.rst + $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." + +dirhtml: + $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." + +singlehtml: + $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml + @echo + @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." + +pickle: + $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle + @echo + @echo "Build finished; now you can process the pickle files." + +json: + $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json + @echo + @echo "Build finished; now you can process the JSON files." + +htmlhelp: + $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp + @echo + @echo "Build finished; now you can run HTML Help Workshop with the" \ + ".hhp project file in $(BUILDDIR)/htmlhelp." + +qthelp: + $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp + @echo + @echo "Build finished; now you can run "qcollectiongenerator" with the" \ + ".qhcp project file in $(BUILDDIR)/qthelp, like this:" + @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/nghttp2.qhcp" + @echo "To view the help file:" + @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/nghttp2.qhc" + +devhelp: + $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp + @echo + @echo "Build finished." + @echo "To view the help file:" + @echo "# mkdir -p $$HOME/.local/share/devhelp/nghttp2" + @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/nghttp2" + @echo "# devhelp" + +epub: + $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub + @echo + @echo "Build finished. The epub file is in $(BUILDDIR)/epub." + +latex: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo + @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." + @echo "Run \`make' in that directory to run these through (pdf)latex" \ + "(use \`make latexpdf' here to do that automatically)." + +latexpdf: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo "Running LaTeX files through pdflatex..." + $(MAKE) -C $(BUILDDIR)/latex all-pdf + @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." + +text: + $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text + @echo + @echo "Build finished. The text files are in $(BUILDDIR)/text." + +man: apiref.rst + $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man + @echo + @echo "Build finished. The manual pages are in $(BUILDDIR)/man." + +changes: + $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes + @echo + @echo "The overview file is in $(BUILDDIR)/changes." + +linkcheck: + $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck + @echo + @echo "Link check complete; look for any errors in the above output " \ + "or in $(BUILDDIR)/linkcheck/output.txt." + +doctest: + $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest + @echo "Testing of doctests in the sources finished, look at the " \ + "results in $(BUILDDIR)/doctest/output.txt." + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/doc/README.rst b/doc/README.rst new file mode 100644 index 0000000..35bfe7e --- /dev/null +++ b/doc/README.rst @@ -0,0 +1,160 @@ +nghttp2 Documentation +===================== + +The documentation of nghttp2 is generated using Sphinx. This +directory contains the source files to be processed by Sphinx. The +source file for API reference is generated using a script called +``mkapiref.py`` from the nghttp2 C source code. + +Generating API reference +------------------------ + +As described earlier, we use ``mkapiref.py`` to generate rst formatted +text of API reference from C source code. The ``mkapiref.py`` is not +so flexible and it requires that C source code is formatted in rather +strict rules. + +To generate API reference, just run ``make html``. It runs +``mkapiref.py`` and then run Sphinx to build the entire document. + +The ``mkapiref.py`` reads C source code and searches the comment block +starts with ``/**``. In other words, it only processes the comment +block starting ``/**``. The comment block must end with ``*/``. The +``mkapiref.py`` requires that which type of the object this comment +block refers to. To specify the type of the object, the next line +must contain the so-caled action keyword. Currently, the following +action keywords are supported: ``@function``, ``@functypedef``, +``@enum``, ``@struct`` and ``@union``. The following sections +describes each action keyword. + +@function +######### + +``@function`` is used to refer to the function. The comment block is +used for the document for the function. After the script sees the end +of the comment block, it consumes the lines as the function +declaration until the line which ends with ``;`` is encountered. + +In Sphinx doc, usually the function argument is formatted like +``*this*``. But in C, ``*`` is used for dereferencing a pointer and +we must escape ``*`` with a back slash. To avoid this, we format the +argument like ``|this|``. The ``mkapiref.py`` translates it with +``*this*``, as escaping ``*`` inside ``|`` and ``|`` as necessary. +Note that this shadows the substitution feature of Sphinx. + +The example follows:: + + /** + * @function + * + * Submits PING frame to the |session|. + */ + int nghttp2_submit_ping(nghttp2_session *session); + + +@functypedef +############ + +``@functypedef`` is used to refer to the typedef of the function +pointer. The formatting rule is pretty much the same with +``@function``, but this outputs ``type`` domain, rather than +``function`` domain. + +The example follows:: + + /** + * @functypedef + * + * Callback function invoked when |session| wants to send data to + * remote peer. + */ + typedef ssize_t (*nghttp2_send_callback) + (nghttp2_session *session, + const uint8_t *data, size_t length, int flags, void *user_data); + +@enum +##### + +``@enum`` is used to refer to the enum. Currently, only enum typedefs +are supported. The comment block is used for the document for the +enum type itself. To document each values, put comment block starting +with the line ``/**`` and ending with the ``*/`` just before the enum +value. When the line starts with ``}`` is encountered, the +``mkapiref.py`` extracts strings next to ``}`` as the name of enum. + +At the time of this writing, Sphinx does not support enum type. So we +use ``type`` domain for enum it self and ``macro`` domain for each +value. To refer to the enum value, use ``:enum:`` pseudo role. The +``mkapiref.py`` replaces it with ``:macro:``. By doing this, when +Sphinx will support enum officially, we can replace ``:enum:`` with +the official role easily. + +The example follows:: + + /** + * @enum + * Error codes used in the nghttp2 library. + */ + typedef enum { + /** + * Invalid argument passed. + */ + NGHTTP2_ERR_INVALID_ARGUMENT = -501, + /** + * Zlib error. + */ + NGHTTP2_ERR_ZLIB = -502, + } nghttp2_error; + +@struct +####### + +``@struct`` is used to refer to the struct. Currently, only struct +typedefs are supported. The comment block is used for the document for +the struct type itself.To document each member, put comment block +starting with the line ``/**`` and ending with the ``*/`` just before +the member. When the line starts with ``}`` is encountered, the +``mkapiref.py`` extracts strings next to ``}`` as the name of struct. +The block-less typedef is also supported. In this case, typedef +declaration must be all in one line and the ``mkapiref.py`` uses last +word as the name of struct. + +Some examples follow:: + + /** + * @struct + * The control frame header. + */ + typedef struct { + /** + * SPDY protocol version. + */ + uint16_t version; + /** + * The type of this control frame. + */ + uint16_t type; + /** + * The control frame flags. + */ + uint8_t flags; + /** + * The length field of this control frame. + */ + int32_t length; + } nghttp2_ctrl_hd; + + /** + * @struct + * + * The primary structure to hold the resources needed for a SPDY + * session. The details of this structure is hidden from the public + * API. + */ + typedef struct nghttp2_session nghttp2_session; + +@union +###### + +``@union`` is used to refer to the union. Currently, ``@union`` is an +alias of ``@struct``. diff --git a/doc/_themes/sphinx_rtd_theme/__init__.py b/doc/_themes/sphinx_rtd_theme/__init__.py new file mode 100644 index 0000000..95ddc52 --- /dev/null +++ b/doc/_themes/sphinx_rtd_theme/__init__.py @@ -0,0 +1,17 @@ +"""Sphinx ReadTheDocs theme. + +From https://github.com/ryan-roemer/sphinx-bootstrap-theme. + +""" +import os + +VERSION = (0, 1, 8) + +__version__ = ".".join(str(v) for v in VERSION) +__version_full__ = __version__ + + +def get_html_theme_path(): + """Return list of HTML theme paths.""" + cur_dir = os.path.abspath(os.path.dirname(os.path.dirname(__file__))) + return cur_dir diff --git a/doc/_themes/sphinx_rtd_theme/breadcrumbs.html b/doc/_themes/sphinx_rtd_theme/breadcrumbs.html new file mode 100644 index 0000000..0028421 --- /dev/null +++ b/doc/_themes/sphinx_rtd_theme/breadcrumbs.html @@ -0,0 +1,23 @@ +
+ +
+
diff --git a/doc/_themes/sphinx_rtd_theme/footer.html b/doc/_themes/sphinx_rtd_theme/footer.html new file mode 100644 index 0000000..6347a44 --- /dev/null +++ b/doc/_themes/sphinx_rtd_theme/footer.html @@ -0,0 +1,36 @@ +
+ {% if next or prev %} + + {% endif %} + +
+ +
+

+ {%- if show_copyright %} + {%- if hasdoc('copyright') %} + {% trans path=pathto('copyright'), copyright=copyright|e %}© Copyright {{ copyright }}.{% endtrans %} + {%- else %} + {% trans copyright=copyright|e %}© Copyright {{ copyright }}.{% endtrans %} + {%- endif %} + {%- endif %} + + {%- if last_updated %} + {% trans last_updated=last_updated|e %}Last updated on {{ last_updated }}.{% endtrans %} + {%- endif %} +

+
+ + {%- if show_sphinx %} + {% trans %}Built with Sphinx using a theme provided by Read the Docs{% endtrans %}. + {%- endif %} + +
+ diff --git a/doc/_themes/sphinx_rtd_theme/layout.html b/doc/_themes/sphinx_rtd_theme/layout.html new file mode 100644 index 0000000..9481d8b --- /dev/null +++ b/doc/_themes/sphinx_rtd_theme/layout.html @@ -0,0 +1,181 @@ +{# TEMPLATE VAR SETTINGS #} +{%- set url_root = pathto('', 1) %} +{%- if url_root == '#' %}{% set url_root = '' %}{% endif %} +{%- if not embedded and docstitle %} + {%- set titlesuffix = " — "|safe + docstitle|e %} +{%- else %} + {%- set titlesuffix = "" %} +{%- endif %} + + + + + + + {{ metatags }} + + {% block htmltitle %} + {{ title|striptags|e }}{{ titlesuffix }} + {% endblock %} + + {# FAVICON #} + {% if favicon %} + + {% endif %} + + {# CSS #} + + {# OPENSEARCH #} + {% if not embedded %} + {% if use_opensearch %} + + {% endif %} + + {% endif %} + + {# RTD hosts this file, so just load on non RTD builds #} + {% if not READTHEDOCS %} + + {% endif %} + + {% for cssfile in css_files %} + + {% endfor %} + + {% for cssfile in extra_css_files %} + + {% endfor %} + + {%- block linktags %} + {%- if hasdoc('about') %} + + {%- endif %} + {%- if hasdoc('genindex') %} + + {%- endif %} + {%- if hasdoc('search') %} + + {%- endif %} + {%- if hasdoc('copyright') %} + + {%- endif %} + + {%- if parents %} + + {%- endif %} + {%- if next %} + + {%- endif %} + {%- if prev %} + + {%- endif %} + {%- endblock %} + {%- block extrahead %} {% endblock %} + + {# Keep modernizr in head - http://modernizr.com/docs/#installing #} + + + + + + +
+ + {# SIDE NAV, TOGGLES ON MOBILE #} + + +
+ + {# MOBILE NAV, TRIGGLES SIDE NAV ON TOGGLE #} + + + + {# PAGE CONTENT #} +
+
+ {% include "breadcrumbs.html" %} +
+ {% block body %}{% endblock %} +
+ {% include "footer.html" %} +
+
+ +
+ +
+ {% include "versions.html" %} + + {% if not embedded %} + + + {%- for scriptfile in script_files %} + + {%- endfor %} + + {% endif %} + + {# RTD hosts this file, so just load on non RTD builds #} + {% if not READTHEDOCS %} + + {% endif %} + + {# STICKY NAVIGATION #} + {% if theme_sticky_navigation %} + + {% endif %} + + {%- block footer %} {% endblock %} + + + diff --git a/doc/_themes/sphinx_rtd_theme/layout_old.html b/doc/_themes/sphinx_rtd_theme/layout_old.html new file mode 100644 index 0000000..deb8df2 --- /dev/null +++ b/doc/_themes/sphinx_rtd_theme/layout_old.html @@ -0,0 +1,205 @@ +{# + basic/layout.html + ~~~~~~~~~~~~~~~~~ + + Master layout template for Sphinx themes. + + :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS. + :license: BSD, see LICENSE for details. +#} +{%- block doctype -%} + +{%- endblock %} +{%- set reldelim1 = reldelim1 is not defined and ' »' or reldelim1 %} +{%- set reldelim2 = reldelim2 is not defined and ' |' or reldelim2 %} +{%- set render_sidebar = (not embedded) and (not theme_nosidebar|tobool) and + (sidebars != []) %} +{%- set url_root = pathto('', 1) %} +{# XXX necessary? #} +{%- if url_root == '#' %}{% set url_root = '' %}{% endif %} +{%- if not embedded and docstitle %} + {%- set titlesuffix = " — "|safe + docstitle|e %} +{%- else %} + {%- set titlesuffix = "" %} +{%- endif %} + +{%- macro relbar() %} + +{%- endmacro %} + +{%- macro sidebar() %} + {%- if render_sidebar %} +
+
+ {%- block sidebarlogo %} + {%- if logo %} + + {%- endif %} + {%- endblock %} + {%- if sidebars != None %} + {#- new style sidebar: explicitly include/exclude templates #} + {%- for sidebartemplate in sidebars %} + {%- include sidebartemplate %} + {%- endfor %} + {%- else %} + {#- old style sidebars: using blocks -- should be deprecated #} + {%- block sidebartoc %} + {%- include "localtoc.html" %} + {%- endblock %} + {%- block sidebarrel %} + {%- include "relations.html" %} + {%- endblock %} + {%- block sidebarsourcelink %} + {%- include "sourcelink.html" %} + {%- endblock %} + {%- if customsidebar %} + {%- include customsidebar %} + {%- endif %} + {%- block sidebarsearch %} + {%- include "searchbox.html" %} + {%- endblock %} + {%- endif %} +
+
+ {%- endif %} +{%- endmacro %} + +{%- macro script() %} + + {%- for scriptfile in script_files %} + + {%- endfor %} +{%- endmacro %} + +{%- macro css() %} + + + {%- for cssfile in css_files %} + + {%- endfor %} +{%- endmacro %} + + + + + {{ metatags }} + {%- block htmltitle %} + {{ title|striptags|e }}{{ titlesuffix }} + {%- endblock %} + {{ css() }} + {%- if not embedded %} + {{ script() }} + {%- if use_opensearch %} + + {%- endif %} + {%- if favicon %} + + {%- endif %} + {%- endif %} +{%- block linktags %} + {%- if hasdoc('about') %} + + {%- endif %} + {%- if hasdoc('genindex') %} + + {%- endif %} + {%- if hasdoc('search') %} + + {%- endif %} + {%- if hasdoc('copyright') %} + + {%- endif %} + + {%- if parents %} + + {%- endif %} + {%- if next %} + + {%- endif %} + {%- if prev %} + + {%- endif %} +{%- endblock %} +{%- block extrahead %} {% endblock %} + + +{%- block header %}{% endblock %} + +{%- block relbar1 %}{{ relbar() }}{% endblock %} + +{%- block content %} + {%- block sidebar1 %} {# possible location for sidebar #} {% endblock %} + +
+ {%- block document %} +
+ {%- if render_sidebar %} +
+ {%- endif %} +
+ {% block body %} {% endblock %} +
+ {%- if render_sidebar %} +
+ {%- endif %} +
+ {%- endblock %} + + {%- block sidebar2 %}{{ sidebar() }}{% endblock %} +
+
+{%- endblock %} + +{%- block relbar2 %}{{ relbar() }}{% endblock %} + +{%- block footer %} + +

asdf asdf asdf asdf 22

+{%- endblock %} + + + diff --git a/doc/_themes/sphinx_rtd_theme/search.html b/doc/_themes/sphinx_rtd_theme/search.html new file mode 100644 index 0000000..e3aa9b5 --- /dev/null +++ b/doc/_themes/sphinx_rtd_theme/search.html @@ -0,0 +1,50 @@ +{# + basic/search.html + ~~~~~~~~~~~~~~~~~ + + Template for the search page. + + :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS. + :license: BSD, see LICENSE for details. +#} +{%- extends "layout.html" %} +{% set title = _('Search') %} +{% set script_files = script_files + ['_static/searchtools.js'] %} +{% block footer %} + + {# this is used when loading the search index using $.ajax fails, + such as on Chrome for documents on localhost #} + + {{ super() }} +{% endblock %} +{% block body %} + + + {% if search_performed %} +

{{ _('Search Results') }}

+ {% if not search_results %} +

{{ _('Your search did not match any documents. Please make sure that all words are spelled correctly and that you\'ve selected enough categories.') }}

+ {% endif %} + {% endif %} +
+ {% if search_results %} +
    + {% for href, caption, context in search_results %} +
  • + {{ caption }} +

    {{ context|e }}

    +
  • + {% endfor %} +
+ {% endif %} +
+{% endblock %} diff --git a/doc/_themes/sphinx_rtd_theme/searchbox.html b/doc/_themes/sphinx_rtd_theme/searchbox.html new file mode 100644 index 0000000..35ad52c --- /dev/null +++ b/doc/_themes/sphinx_rtd_theme/searchbox.html @@ -0,0 +1,9 @@ +{%- if builder != 'singlehtml' %} +
+
+ + + +
+
+{%- endif %} diff --git a/doc/_themes/sphinx_rtd_theme/static/css/badge_only.css b/doc/_themes/sphinx_rtd_theme/static/css/badge_only.css new file mode 100644 index 0000000..7e17fb1 --- /dev/null +++ b/doc/_themes/sphinx_rtd_theme/static/css/badge_only.css @@ -0,0 +1,2 @@ +.fa:before{-webkit-font-smoothing:antialiased}.clearfix{*zoom:1}.clearfix:before,.clearfix:after{display:table;content:""}.clearfix:after{clear:both}@font-face{font-family:FontAwesome;font-weight:normal;font-style:normal;src:url("../font/fontawesome_webfont.eot");src:url("../font/fontawesome_webfont.eot?#iefix") format("embedded-opentype"),url("../font/fontawesome_webfont.woff") format("woff"),url("../font/fontawesome_webfont.ttf") format("truetype"),url("../font/fontawesome_webfont.svg#FontAwesome") format("svg")}.fa:before{display:inline-block;font-family:FontAwesome;font-style:normal;font-weight:normal;line-height:1;text-decoration:inherit}a .fa{display:inline-block;text-decoration:inherit}li .fa{display:inline-block}li .fa-large:before,li .fa-large:before{width:1.875em}ul.fas{list-style-type:none;margin-left:2em;text-indent:-0.8em}ul.fas li .fa{width:0.8em}ul.fas li .fa-large:before,ul.fas li .fa-large:before{vertical-align:baseline}.fa-book:before{content:""}.icon-book:before{content:""}.fa-caret-down:before{content:""}.icon-caret-down:before{content:""}.fa-caret-up:before{content:""}.icon-caret-up:before{content:""}.fa-caret-left:before{content:""}.icon-caret-left:before{content:""}.fa-caret-right:before{content:""}.icon-caret-right:before{content:""}.rst-versions{position:fixed;bottom:0;left:0;width:300px;color:#fcfcfc;background:#1f1d1d;border-top:solid 10px #343131;font-family:"Lato","proxima-nova","Helvetica Neue",Arial,sans-serif;z-index:400}.rst-versions a{color:#2980B9;text-decoration:none}.rst-versions .rst-badge-small{display:none}.rst-versions .rst-current-version{padding:12px;background-color:#272525;display:block;text-align:right;font-size:90%;cursor:pointer;color:#27AE60;*zoom:1}.rst-versions .rst-current-version:before,.rst-versions .rst-current-version:after{display:table;content:""}.rst-versions .rst-current-version:after{clear:both}.rst-versions .rst-current-version .fa{color:#fcfcfc}.rst-versions .rst-current-version .fa-book{float:left}.rst-versions .rst-current-version .icon-book{float:left}.rst-versions .rst-current-version.rst-out-of-date{background-color:#E74C3C;color:#fff}.rst-versions .rst-current-version.rst-active-old-version{background-color:#F1C40F;color:#000}.rst-versions.shift-up .rst-other-versions{display:block}.rst-versions .rst-other-versions{font-size:90%;padding:12px;color:gray;display:none}.rst-versions .rst-other-versions hr{display:block;height:1px;border:0;margin:20px 0;padding:0;border-top:solid 1px #413d3d}.rst-versions .rst-other-versions dd{display:inline-block;margin:0}.rst-versions .rst-other-versions dd a{display:inline-block;padding:6px;color:#fcfcfc}.rst-versions.rst-badge{width:auto;bottom:20px;right:20px;left:auto;border:none;max-width:300px}.rst-versions.rst-badge .icon-book{float:none}.rst-versions.rst-badge .fa-book{float:none}.rst-versions.rst-badge.shift-up .rst-current-version{text-align:right}.rst-versions.rst-badge.shift-up .rst-current-version .fa-book{float:left}.rst-versions.rst-badge.shift-up .rst-current-version .icon-book{float:left}.rst-versions.rst-badge .rst-current-version{width:auto;height:30px;line-height:30px;padding:0 6px;display:block;text-align:center}@media screen and (max-width: 768px){.rst-versions{width:85%;display:none}.rst-versions.shift{display:block}img{width:100%;height:auto}} +/*# sourceMappingURL=badge_only.css.map */ diff --git a/doc/_themes/sphinx_rtd_theme/static/css/theme.css b/doc/_themes/sphinx_rtd_theme/static/css/theme.css new file mode 100644 index 0000000..57b98fe --- /dev/null +++ b/doc/_themes/sphinx_rtd_theme/static/css/theme.css @@ -0,0 +1,5 @@ +*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}article,aside,details,figcaption,figure,footer,header,hgroup,nav,section{display:block}audio,canvas,video{display:inline-block;*display:inline;*zoom:1}audio:not([controls]){display:none}[hidden]{display:none}*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}html{font-size:100%;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%}body{margin:0}a:hover,a:active{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:bold}blockquote{margin:0}dfn{font-style:italic}ins{background:#ff9;color:#000;text-decoration:none}mark{background:#ff0;color:#000;font-style:italic;font-weight:bold}pre,code,.rst-content tt,.rst-content code,kbd,samp{font-family:monospace,serif;_font-family:"courier new",monospace;font-size:1em}pre{white-space:pre}q{quotes:none}q:before,q:after{content:"";content:none}small{font-size:85%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-0.5em}sub{bottom:-0.25em}ul,ol,dl{margin:0;padding:0;list-style:none;list-style-image:none}li{list-style:none}dd{margin:0}img{border:0;-ms-interpolation-mode:bicubic;vertical-align:middle;max-width:100%}svg:not(:root){overflow:hidden}figure{margin:0}form{margin:0}fieldset{border:0;margin:0;padding:0}label{cursor:pointer}legend{border:0;*margin-left:-7px;padding:0;white-space:normal}button,input,select,textarea{font-size:100%;margin:0;vertical-align:baseline;*vertical-align:middle}button,input{line-height:normal}button,input[type="button"],input[type="reset"],input[type="submit"]{cursor:pointer;-webkit-appearance:button;*overflow:visible}button[disabled],input[disabled]{cursor:default}input[type="checkbox"],input[type="radio"]{box-sizing:border-box;padding:0;*width:13px;*height:13px}input[type="search"]{-webkit-appearance:textfield;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box}input[type="search"]::-webkit-search-decoration,input[type="search"]::-webkit-search-cancel-button{-webkit-appearance:none}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}textarea{overflow:auto;vertical-align:top;resize:vertical}table{border-collapse:collapse;border-spacing:0}td{vertical-align:top}.chromeframe{margin:0.2em 0;background:#ccc;color:#000;padding:0.2em 0}.ir{display:block;border:0;text-indent:-999em;overflow:hidden;background-color:transparent;background-repeat:no-repeat;text-align:left;direction:ltr;*line-height:0}.ir br{display:none}.hidden{display:none !important;visibility:hidden}.visuallyhidden{border:0;clip:rect(0 0 0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.visuallyhidden.focusable:active,.visuallyhidden.focusable:focus{clip:auto;height:auto;margin:0;overflow:visible;position:static;width:auto}.invisible{visibility:hidden}.relative{position:relative}big,small{font-size:100%}@media print{html,body,section{background:none !important}*{box-shadow:none !important;text-shadow:none !important;filter:none !important;-ms-filter:none !important}a,a:visited{text-decoration:underline}.ir a:after,a[href^="javascript:"]:after,a[href^="#"]:after{content:""}pre,blockquote{page-break-inside:avoid}thead{display:table-header-group}tr,img{page-break-inside:avoid}img{max-width:100% !important}@page{margin:0.5cm}p,h2,.rst-content p.caption,h3{orphans:3;widows:3}h2,.rst-content p.caption,h3{page-break-after:avoid}}.fa:before,.wy-menu-vertical li span.toctree-expand:before,.wy-menu-vertical li.on a span.toctree-expand:before,.wy-menu-vertical li.current>a span.toctree-expand:before,.rst-content .admonition-title:before,.rst-content h1 .headerlink:before,.rst-content h2 .headerlink:before,.rst-content p.caption .headerlink:before,.rst-content h3 .headerlink:before,.rst-content h4 .headerlink:before,.rst-content h5 .headerlink:before,.rst-content h6 .headerlink:before,.rst-content dl dt .headerlink:before,.rst-content tt.download span:first-child:before,.rst-content code.download span:first-child:before,.icon:before,.wy-dropdown .caret:before,.wy-inline-validate.wy-inline-validate-success .wy-input-context:before,.wy-inline-validate.wy-inline-validate-danger .wy-input-context:before,.wy-inline-validate.wy-inline-validate-warning .wy-input-context:before,.wy-inline-validate.wy-inline-validate-info .wy-input-context:before,.wy-alert,.rst-content .note,.rst-content .attention,.rst-content .caution,.rst-content .danger,.rst-content .error,.rst-content .hint,.rst-content .important,.rst-content .tip,.rst-content .warning,.rst-content .seealso,.rst-content .admonition-todo,.btn,input[type="text"],input[type="password"],input[type="email"],input[type="url"],input[type="date"],input[type="month"],input[type="time"],input[type="datetime"],input[type="datetime-local"],input[type="week"],input[type="number"],input[type="search"],input[type="tel"],input[type="color"],select,textarea,.wy-menu-vertical li.on a,.wy-menu-vertical li.current>a,.wy-side-nav-search>a,.wy-side-nav-search .wy-dropdown>a,.wy-nav-top a{-webkit-font-smoothing:antialiased}.clearfix{*zoom:1}.clearfix:before,.clearfix:after{display:table;content:""}.clearfix:after{clear:both}/*! + * Font Awesome 4.2.0 by @davegandy - http://fontawesome.io - @fontawesome + * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) + */@font-face{font-family:'FontAwesome';src:url("../fonts/fontawesome-webfont.eot?v=4.2.0");src:url("../fonts/fontawesome-webfont.eot?#iefix&v=4.2.0") format("embedded-opentype"),url("../fonts/fontawesome-webfont.woff?v=4.2.0") format("woff"),url("../fonts/fontawesome-webfont.ttf?v=4.2.0") format("truetype"),url("../fonts/fontawesome-webfont.svg?v=4.2.0#fontawesomeregular") format("svg");font-weight:normal;font-style:normal}.fa,.wy-menu-vertical li span.toctree-expand,.wy-menu-vertical li.on a span.toctree-expand,.wy-menu-vertical li.current>a span.toctree-expand,.rst-content .admonition-title,.rst-content h1 .headerlink,.rst-content h2 .headerlink,.rst-content p.caption .headerlink,.rst-content h3 .headerlink,.rst-content h4 .headerlink,.rst-content h5 .headerlink,.rst-content h6 .headerlink,.rst-content dl dt .headerlink,.rst-content tt.download span:first-child,.rst-content code.download span:first-child,.icon{display:inline-block;font:normal normal normal 14px/1 FontAwesome;font-size:inherit;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.fa-lg{font-size:1.33333em;line-height:0.75em;vertical-align:-15%}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-fw{width:1.28571em;text-align:center}.fa-ul{padding-left:0;margin-left:2.14286em;list-style-type:none}.fa-ul>li{position:relative}.fa-li{position:absolute;left:-2.14286em;width:2.14286em;top:0.14286em;text-align:center}.fa-li.fa-lg{left:-1.85714em}.fa-border{padding:.2em .25em .15em;border:solid 0.08em #eee;border-radius:.1em}.pull-right{float:right}.pull-left{float:left}.fa.pull-left,.wy-menu-vertical li span.pull-left.toctree-expand,.wy-menu-vertical li.on a span.pull-left.toctree-expand,.wy-menu-vertical li.current>a span.pull-left.toctree-expand,.rst-content .pull-left.admonition-title,.rst-content h1 .pull-left.headerlink,.rst-content h2 .pull-left.headerlink,.rst-content p.caption .pull-left.headerlink,.rst-content h3 .pull-left.headerlink,.rst-content h4 .pull-left.headerlink,.rst-content h5 .pull-left.headerlink,.rst-content h6 .pull-left.headerlink,.rst-content dl dt .pull-left.headerlink,.rst-content tt.download span.pull-left:first-child,.rst-content code.download span.pull-left:first-child,.pull-left.icon{margin-right:.3em}.fa.pull-right,.wy-menu-vertical li span.pull-right.toctree-expand,.wy-menu-vertical li.on a span.pull-right.toctree-expand,.wy-menu-vertical li.current>a span.pull-right.toctree-expand,.rst-content .pull-right.admonition-title,.rst-content h1 .pull-right.headerlink,.rst-content h2 .pull-right.headerlink,.rst-content p.caption .pull-right.headerlink,.rst-content h3 .pull-right.headerlink,.rst-content h4 .pull-right.headerlink,.rst-content h5 .pull-right.headerlink,.rst-content h6 .pull-right.headerlink,.rst-content dl dt .pull-right.headerlink,.rst-content tt.download span.pull-right:first-child,.rst-content code.download span.pull-right:first-child,.pull-right.icon{margin-left:.3em}.fa-spin{-webkit-animation:fa-spin 2s infinite linear;animation:fa-spin 2s infinite linear}@-webkit-keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.fa-rotate-90{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=1);-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=2);-webkit-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=3);-webkit-transform:rotate(270deg);-ms-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=0);-webkit-transform:scale(-1, 1);-ms-transform:scale(-1, 1);transform:scale(-1, 1)}.fa-flip-vertical{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=2);-webkit-transform:scale(1, -1);-ms-transform:scale(1, -1);transform:scale(1, -1)}:root .fa-rotate-90,:root .fa-rotate-180,:root .fa-rotate-270,:root .fa-flip-horizontal,:root .fa-flip-vertical{filter:none}.fa-stack{position:relative;display:inline-block;width:2em;height:2em;line-height:2em;vertical-align:middle}.fa-stack-1x,.fa-stack-2x{position:absolute;left:0;width:100%;text-align:center}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-glass:before{content:""}.fa-music:before{content:""}.fa-search:before,.icon-search:before{content:""}.fa-envelope-o:before{content:""}.fa-heart:before{content:""}.fa-star:before{content:""}.fa-star-o:before{content:""}.fa-user:before{content:""}.fa-film:before{content:""}.fa-th-large:before{content:""}.fa-th:before{content:""}.fa-th-list:before{content:""}.fa-check:before{content:""}.fa-remove:before,.fa-close:before,.fa-times:before{content:""}.fa-search-plus:before{content:""}.fa-search-minus:before{content:""}.fa-power-off:before{content:""}.fa-signal:before{content:""}.fa-gear:before,.fa-cog:before{content:""}.fa-trash-o:before{content:""}.fa-home:before,.icon-home:before{content:""}.fa-file-o:before{content:""}.fa-clock-o:before{content:""}.fa-road:before{content:""}.fa-download:before,.rst-content tt.download span:first-child:before,.rst-content code.download span:first-child:before{content:""}.fa-arrow-circle-o-down:before{content:""}.fa-arrow-circle-o-up:before{content:""}.fa-inbox:before{content:""}.fa-play-circle-o:before{content:""}.fa-rotate-right:before,.fa-repeat:before{content:""}.fa-refresh:before{content:""}.fa-list-alt:before{content:""}.fa-lock:before{content:""}.fa-flag:before{content:""}.fa-headphones:before{content:""}.fa-volume-off:before{content:""}.fa-volume-down:before{content:""}.fa-volume-up:before{content:""}.fa-qrcode:before{content:""}.fa-barcode:before{content:""}.fa-tag:before{content:""}.fa-tags:before{content:""}.fa-book:before,.icon-book:before{content:""}.fa-bookmark:before{content:""}.fa-print:before{content:""}.fa-camera:before{content:""}.fa-font:before{content:""}.fa-bold:before{content:""}.fa-italic:before{content:""}.fa-text-height:before{content:""}.fa-text-width:before{content:""}.fa-align-left:before{content:""}.fa-align-center:before{content:""}.fa-align-right:before{content:""}.fa-align-justify:before{content:""}.fa-list:before{content:""}.fa-dedent:before,.fa-outdent:before{content:""}.fa-indent:before{content:""}.fa-video-camera:before{content:""}.fa-photo:before,.fa-image:before,.fa-picture-o:before{content:""}.fa-pencil:before{content:""}.fa-map-marker:before{content:""}.fa-adjust:before{content:""}.fa-tint:before{content:""}.fa-edit:before,.fa-pencil-square-o:before{content:""}.fa-share-square-o:before{content:""}.fa-check-square-o:before{content:""}.fa-arrows:before{content:""}.fa-step-backward:before{content:""}.fa-fast-backward:before{content:""}.fa-backward:before{content:""}.fa-play:before{content:""}.fa-pause:before{content:""}.fa-stop:before{content:""}.fa-forward:before{content:""}.fa-fast-forward:before{content:""}.fa-step-forward:before{content:""}.fa-eject:before{content:""}.fa-chevron-left:before{content:""}.fa-chevron-right:before{content:""}.fa-plus-circle:before{content:""}.fa-minus-circle:before{content:""}.fa-times-circle:before,.wy-inline-validate.wy-inline-validate-danger .wy-input-context:before{content:""}.fa-check-circle:before,.wy-inline-validate.wy-inline-validate-success .wy-input-context:before{content:""}.fa-question-circle:before{content:""}.fa-info-circle:before{content:""}.fa-crosshairs:before{content:""}.fa-times-circle-o:before{content:""}.fa-check-circle-o:before{content:""}.fa-ban:before{content:""}.fa-arrow-left:before{content:""}.fa-arrow-right:before{content:""}.fa-arrow-up:before{content:""}.fa-arrow-down:before{content:""}.fa-mail-forward:before,.fa-share:before{content:""}.fa-expand:before{content:""}.fa-compress:before{content:""}.fa-plus:before{content:""}.fa-minus:before{content:""}.fa-asterisk:before{content:""}.fa-exclamation-circle:before,.wy-inline-validate.wy-inline-validate-warning .wy-input-context:before,.wy-inline-validate.wy-inline-validate-info .wy-input-context:before,.rst-content .admonition-title:before{content:""}.fa-gift:before{content:""}.fa-leaf:before{content:""}.fa-fire:before,.icon-fire:before{content:""}.fa-eye:before{content:""}.fa-eye-slash:before{content:""}.fa-warning:before,.fa-exclamation-triangle:before{content:""}.fa-plane:before{content:""}.fa-calendar:before{content:""}.fa-random:before{content:""}.fa-comment:before{content:""}.fa-magnet:before{content:""}.fa-chevron-up:before{content:""}.fa-chevron-down:before{content:""}.fa-retweet:before{content:""}.fa-shopping-cart:before{content:""}.fa-folder:before{content:""}.fa-folder-open:before{content:""}.fa-arrows-v:before{content:""}.fa-arrows-h:before{content:""}.fa-bar-chart-o:before,.fa-bar-chart:before{content:""}.fa-twitter-square:before{content:""}.fa-facebook-square:before{content:""}.fa-camera-retro:before{content:""}.fa-key:before{content:""}.fa-gears:before,.fa-cogs:before{content:""}.fa-comments:before{content:""}.fa-thumbs-o-up:before{content:""}.fa-thumbs-o-down:before{content:""}.fa-star-half:before{content:""}.fa-heart-o:before{content:""}.fa-sign-out:before{content:""}.fa-linkedin-square:before{content:""}.fa-thumb-tack:before{content:""}.fa-external-link:before{content:""}.fa-sign-in:before{content:""}.fa-trophy:before{content:""}.fa-github-square:before{content:""}.fa-upload:before{content:""}.fa-lemon-o:before{content:""}.fa-phone:before{content:""}.fa-square-o:before{content:""}.fa-bookmark-o:before{content:""}.fa-phone-square:before{content:""}.fa-twitter:before{content:""}.fa-facebook:before{content:""}.fa-github:before,.icon-github:before{content:""}.fa-unlock:before{content:""}.fa-credit-card:before{content:""}.fa-rss:before{content:""}.fa-hdd-o:before{content:""}.fa-bullhorn:before{content:""}.fa-bell:before{content:""}.fa-certificate:before{content:""}.fa-hand-o-right:before{content:""}.fa-hand-o-left:before{content:""}.fa-hand-o-up:before{content:""}.fa-hand-o-down:before{content:""}.fa-arrow-circle-left:before,.icon-circle-arrow-left:before{content:""}.fa-arrow-circle-right:before,.icon-circle-arrow-right:before{content:""}.fa-arrow-circle-up:before{content:""}.fa-arrow-circle-down:before{content:""}.fa-globe:before{content:""}.fa-wrench:before{content:""}.fa-tasks:before{content:""}.fa-filter:before{content:""}.fa-briefcase:before{content:""}.fa-arrows-alt:before{content:""}.fa-group:before,.fa-users:before{content:""}.fa-chain:before,.fa-link:before,.icon-link:before{content:""}.fa-cloud:before{content:""}.fa-flask:before{content:""}.fa-cut:before,.fa-scissors:before{content:""}.fa-copy:before,.fa-files-o:before{content:""}.fa-paperclip:before{content:""}.fa-save:before,.fa-floppy-o:before{content:""}.fa-square:before{content:""}.fa-navicon:before,.fa-reorder:before,.fa-bars:before{content:""}.fa-list-ul:before{content:""}.fa-list-ol:before{content:""}.fa-strikethrough:before{content:""}.fa-underline:before{content:""}.fa-table:before{content:""}.fa-magic:before{content:""}.fa-truck:before{content:""}.fa-pinterest:before{content:""}.fa-pinterest-square:before{content:""}.fa-google-plus-square:before{content:""}.fa-google-plus:before{content:""}.fa-money:before{content:""}.fa-caret-down:before,.wy-dropdown .caret:before,.icon-caret-down:before{content:""}.fa-caret-up:before{content:""}.fa-caret-left:before{content:""}.fa-caret-right:before{content:""}.fa-columns:before{content:""}.fa-unsorted:before,.fa-sort:before{content:""}.fa-sort-down:before,.fa-sort-desc:before{content:""}.fa-sort-up:before,.fa-sort-asc:before{content:""}.fa-envelope:before{content:""}.fa-linkedin:before{content:""}.fa-rotate-left:before,.fa-undo:before{content:""}.fa-legal:before,.fa-gavel:before{content:""}.fa-dashboard:before,.fa-tachometer:before{content:""}.fa-comment-o:before{content:""}.fa-comments-o:before{content:""}.fa-flash:before,.fa-bolt:before{content:""}.fa-sitemap:before{content:""}.fa-umbrella:before{content:""}.fa-paste:before,.fa-clipboard:before{content:""}.fa-lightbulb-o:before{content:""}.fa-exchange:before{content:""}.fa-cloud-download:before{content:""}.fa-cloud-upload:before{content:""}.fa-user-md:before{content:""}.fa-stethoscope:before{content:""}.fa-suitcase:before{content:""}.fa-bell-o:before{content:""}.fa-coffee:before{content:""}.fa-cutlery:before{content:""}.fa-file-text-o:before{content:""}.fa-building-o:before{content:""}.fa-hospital-o:before{content:""}.fa-ambulance:before{content:""}.fa-medkit:before{content:""}.fa-fighter-jet:before{content:""}.fa-beer:before{content:""}.fa-h-square:before{content:""}.fa-plus-square:before{content:""}.fa-angle-double-left:before{content:""}.fa-angle-double-right:before{content:""}.fa-angle-double-up:before{content:""}.fa-angle-double-down:before{content:""}.fa-angle-left:before{content:""}.fa-angle-right:before{content:""}.fa-angle-up:before{content:""}.fa-angle-down:before{content:""}.fa-desktop:before{content:""}.fa-laptop:before{content:""}.fa-tablet:before{content:""}.fa-mobile-phone:before,.fa-mobile:before{content:""}.fa-circle-o:before{content:""}.fa-quote-left:before{content:""}.fa-quote-right:before{content:""}.fa-spinner:before{content:""}.fa-circle:before{content:""}.fa-mail-reply:before,.fa-reply:before{content:""}.fa-github-alt:before{content:""}.fa-folder-o:before{content:""}.fa-folder-open-o:before{content:""}.fa-smile-o:before{content:""}.fa-frown-o:before{content:""}.fa-meh-o:before{content:""}.fa-gamepad:before{content:""}.fa-keyboard-o:before{content:""}.fa-flag-o:before{content:""}.fa-flag-checkered:before{content:""}.fa-terminal:before{content:""}.fa-code:before{content:""}.fa-mail-reply-all:before,.fa-reply-all:before{content:""}.fa-star-half-empty:before,.fa-star-half-full:before,.fa-star-half-o:before{content:""}.fa-location-arrow:before{content:""}.fa-crop:before{content:""}.fa-code-fork:before{content:""}.fa-unlink:before,.fa-chain-broken:before{content:""}.fa-question:before{content:""}.fa-info:before{content:""}.fa-exclamation:before{content:""}.fa-superscript:before{content:""}.fa-subscript:before{content:""}.fa-eraser:before{content:""}.fa-puzzle-piece:before{content:""}.fa-microphone:before{content:""}.fa-microphone-slash:before{content:""}.fa-shield:before{content:""}.fa-calendar-o:before{content:""}.fa-fire-extinguisher:before{content:""}.fa-rocket:before{content:""}.fa-maxcdn:before{content:""}.fa-chevron-circle-left:before{content:""}.fa-chevron-circle-right:before{content:""}.fa-chevron-circle-up:before{content:""}.fa-chevron-circle-down:before{content:""}.fa-html5:before{content:""}.fa-css3:before{content:""}.fa-anchor:before{content:""}.fa-unlock-alt:before{content:""}.fa-bullseye:before{content:""}.fa-ellipsis-h:before{content:""}.fa-ellipsis-v:before{content:""}.fa-rss-square:before{content:""}.fa-play-circle:before{content:""}.fa-ticket:before{content:""}.fa-minus-square:before{content:""}.fa-minus-square-o:before,.wy-menu-vertical li.on a span.toctree-expand:before,.wy-menu-vertical li.current>a span.toctree-expand:before{content:""}.fa-level-up:before{content:""}.fa-level-down:before{content:""}.fa-check-square:before{content:""}.fa-pencil-square:before{content:""}.fa-external-link-square:before{content:""}.fa-share-square:before{content:""}.fa-compass:before{content:""}.fa-toggle-down:before,.fa-caret-square-o-down:before{content:""}.fa-toggle-up:before,.fa-caret-square-o-up:before{content:""}.fa-toggle-right:before,.fa-caret-square-o-right:before{content:""}.fa-euro:before,.fa-eur:before{content:""}.fa-gbp:before{content:""}.fa-dollar:before,.fa-usd:before{content:""}.fa-rupee:before,.fa-inr:before{content:""}.fa-cny:before,.fa-rmb:before,.fa-yen:before,.fa-jpy:before{content:""}.fa-ruble:before,.fa-rouble:before,.fa-rub:before{content:""}.fa-won:before,.fa-krw:before{content:""}.fa-bitcoin:before,.fa-btc:before{content:""}.fa-file:before{content:""}.fa-file-text:before{content:""}.fa-sort-alpha-asc:before{content:""}.fa-sort-alpha-desc:before{content:""}.fa-sort-amount-asc:before{content:""}.fa-sort-amount-desc:before{content:""}.fa-sort-numeric-asc:before{content:""}.fa-sort-numeric-desc:before{content:""}.fa-thumbs-up:before{content:""}.fa-thumbs-down:before{content:""}.fa-youtube-square:before{content:""}.fa-youtube:before{content:""}.fa-xing:before{content:""}.fa-xing-square:before{content:""}.fa-youtube-play:before{content:""}.fa-dropbox:before{content:""}.fa-stack-overflow:before{content:""}.fa-instagram:before{content:""}.fa-flickr:before{content:""}.fa-adn:before{content:""}.fa-bitbucket:before,.icon-bitbucket:before{content:""}.fa-bitbucket-square:before{content:""}.fa-tumblr:before{content:""}.fa-tumblr-square:before{content:""}.fa-long-arrow-down:before{content:""}.fa-long-arrow-up:before{content:""}.fa-long-arrow-left:before{content:""}.fa-long-arrow-right:before{content:""}.fa-apple:before{content:""}.fa-windows:before{content:""}.fa-android:before{content:""}.fa-linux:before{content:""}.fa-dribbble:before{content:""}.fa-skype:before{content:""}.fa-foursquare:before{content:""}.fa-trello:before{content:""}.fa-female:before{content:""}.fa-male:before{content:""}.fa-gittip:before{content:""}.fa-sun-o:before{content:""}.fa-moon-o:before{content:""}.fa-archive:before{content:""}.fa-bug:before{content:""}.fa-vk:before{content:""}.fa-weibo:before{content:""}.fa-renren:before{content:""}.fa-pagelines:before{content:""}.fa-stack-exchange:before{content:""}.fa-arrow-circle-o-right:before{content:""}.fa-arrow-circle-o-left:before{content:""}.fa-toggle-left:before,.fa-caret-square-o-left:before{content:""}.fa-dot-circle-o:before{content:""}.fa-wheelchair:before{content:""}.fa-vimeo-square:before{content:""}.fa-turkish-lira:before,.fa-try:before{content:""}.fa-plus-square-o:before,.wy-menu-vertical li span.toctree-expand:before{content:""}.fa-space-shuttle:before{content:""}.fa-slack:before{content:""}.fa-envelope-square:before{content:""}.fa-wordpress:before{content:""}.fa-openid:before{content:""}.fa-institution:before,.fa-bank:before,.fa-university:before{content:""}.fa-mortar-board:before,.fa-graduation-cap:before{content:""}.fa-yahoo:before{content:""}.fa-google:before{content:""}.fa-reddit:before{content:""}.fa-reddit-square:before{content:""}.fa-stumbleupon-circle:before{content:""}.fa-stumbleupon:before{content:""}.fa-delicious:before{content:""}.fa-digg:before{content:""}.fa-pied-piper:before{content:""}.fa-pied-piper-alt:before{content:""}.fa-drupal:before{content:""}.fa-joomla:before{content:""}.fa-language:before{content:""}.fa-fax:before{content:""}.fa-building:before{content:""}.fa-child:before{content:""}.fa-paw:before{content:""}.fa-spoon:before{content:""}.fa-cube:before{content:""}.fa-cubes:before{content:""}.fa-behance:before{content:""}.fa-behance-square:before{content:""}.fa-steam:before{content:""}.fa-steam-square:before{content:""}.fa-recycle:before{content:""}.fa-automobile:before,.fa-car:before{content:""}.fa-cab:before,.fa-taxi:before{content:""}.fa-tree:before{content:""}.fa-spotify:before{content:""}.fa-deviantart:before{content:""}.fa-soundcloud:before{content:""}.fa-database:before{content:""}.fa-file-pdf-o:before{content:""}.fa-file-word-o:before{content:""}.fa-file-excel-o:before{content:""}.fa-file-powerpoint-o:before{content:""}.fa-file-photo-o:before,.fa-file-picture-o:before,.fa-file-image-o:before{content:""}.fa-file-zip-o:before,.fa-file-archive-o:before{content:""}.fa-file-sound-o:before,.fa-file-audio-o:before{content:""}.fa-file-movie-o:before,.fa-file-video-o:before{content:""}.fa-file-code-o:before{content:""}.fa-vine:before{content:""}.fa-codepen:before{content:""}.fa-jsfiddle:before{content:""}.fa-life-bouy:before,.fa-life-buoy:before,.fa-life-saver:before,.fa-support:before,.fa-life-ring:before{content:""}.fa-circle-o-notch:before{content:""}.fa-ra:before,.fa-rebel:before{content:""}.fa-ge:before,.fa-empire:before{content:""}.fa-git-square:before{content:""}.fa-git:before{content:""}.fa-hacker-news:before{content:""}.fa-tencent-weibo:before{content:""}.fa-qq:before{content:""}.fa-wechat:before,.fa-weixin:before{content:""}.fa-send:before,.fa-paper-plane:before{content:""}.fa-send-o:before,.fa-paper-plane-o:before{content:""}.fa-history:before{content:""}.fa-circle-thin:before{content:""}.fa-header:before{content:""}.fa-paragraph:before{content:""}.fa-sliders:before{content:""}.fa-share-alt:before{content:""}.fa-share-alt-square:before{content:""}.fa-bomb:before{content:""}.fa-soccer-ball-o:before,.fa-futbol-o:before{content:""}.fa-tty:before{content:""}.fa-binoculars:before{content:""}.fa-plug:before{content:""}.fa-slideshare:before{content:""}.fa-twitch:before{content:""}.fa-yelp:before{content:""}.fa-newspaper-o:before{content:""}.fa-wifi:before{content:""}.fa-calculator:before{content:""}.fa-paypal:before{content:""}.fa-google-wallet:before{content:""}.fa-cc-visa:before{content:""}.fa-cc-mastercard:before{content:""}.fa-cc-discover:before{content:""}.fa-cc-amex:before{content:""}.fa-cc-paypal:before{content:""}.fa-cc-stripe:before{content:""}.fa-bell-slash:before{content:""}.fa-bell-slash-o:before{content:""}.fa-trash:before{content:""}.fa-copyright:before{content:""}.fa-at:before{content:""}.fa-eyedropper:before{content:""}.fa-paint-brush:before{content:""}.fa-birthday-cake:before{content:""}.fa-area-chart:before{content:""}.fa-pie-chart:before{content:""}.fa-line-chart:before{content:""}.fa-lastfm:before{content:""}.fa-lastfm-square:before{content:""}.fa-toggle-off:before{content:""}.fa-toggle-on:before{content:""}.fa-bicycle:before{content:""}.fa-bus:before{content:""}.fa-ioxhost:before{content:""}.fa-angellist:before{content:""}.fa-cc:before{content:""}.fa-shekel:before,.fa-sheqel:before,.fa-ils:before{content:""}.fa-meanpath:before{content:""}.fa,.wy-menu-vertical li span.toctree-expand,.wy-menu-vertical li.on a span.toctree-expand,.wy-menu-vertical li.current>a span.toctree-expand,.rst-content .admonition-title,.rst-content h1 .headerlink,.rst-content h2 .headerlink,.rst-content p.caption .headerlink,.rst-content h3 .headerlink,.rst-content h4 .headerlink,.rst-content h5 .headerlink,.rst-content h6 .headerlink,.rst-content dl dt .headerlink,.rst-content tt.download span:first-child,.rst-content code.download span:first-child,.icon,.wy-dropdown .caret,.wy-inline-validate.wy-inline-validate-success .wy-input-context,.wy-inline-validate.wy-inline-validate-danger .wy-input-context,.wy-inline-validate.wy-inline-validate-warning .wy-input-context,.wy-inline-validate.wy-inline-validate-info .wy-input-context{font-family:inherit}.fa:before,.wy-menu-vertical li span.toctree-expand:before,.wy-menu-vertical li.on a span.toctree-expand:before,.wy-menu-vertical li.current>a span.toctree-expand:before,.rst-content .admonition-title:before,.rst-content h1 .headerlink:before,.rst-content h2 .headerlink:before,.rst-content p.caption .headerlink:before,.rst-content h3 .headerlink:before,.rst-content h4 .headerlink:before,.rst-content h5 .headerlink:before,.rst-content h6 .headerlink:before,.rst-content dl dt .headerlink:before,.rst-content tt.download span:first-child:before,.rst-content code.download span:first-child:before,.icon:before,.wy-dropdown .caret:before,.wy-inline-validate.wy-inline-validate-success .wy-input-context:before,.wy-inline-validate.wy-inline-validate-danger .wy-input-context:before,.wy-inline-validate.wy-inline-validate-warning .wy-input-context:before,.wy-inline-validate.wy-inline-validate-info .wy-input-context:before{font-family:"FontAwesome";display:inline-block;font-style:normal;font-weight:normal;line-height:1;text-decoration:inherit}a .fa,a .wy-menu-vertical li span.toctree-expand,.wy-menu-vertical li a span.toctree-expand,.wy-menu-vertical li.on a span.toctree-expand,.wy-menu-vertical li.current>a span.toctree-expand,a .rst-content .admonition-title,.rst-content a .admonition-title,a .rst-content h1 .headerlink,.rst-content h1 a .headerlink,a .rst-content h2 .headerlink,.rst-content h2 a .headerlink,a .rst-content p.caption .headerlink,.rst-content p.caption a .headerlink,a .rst-content h3 .headerlink,.rst-content h3 a .headerlink,a .rst-content h4 .headerlink,.rst-content h4 a .headerlink,a .rst-content h5 .headerlink,.rst-content h5 a .headerlink,a .rst-content h6 .headerlink,.rst-content h6 a .headerlink,a .rst-content dl dt .headerlink,.rst-content dl dt a .headerlink,a .rst-content tt.download span:first-child,.rst-content tt.download a span:first-child,a .rst-content code.download span:first-child,.rst-content code.download a span:first-child,a .icon{display:inline-block;text-decoration:inherit}.btn .fa,.btn .wy-menu-vertical li span.toctree-expand,.wy-menu-vertical li .btn span.toctree-expand,.btn .wy-menu-vertical li.on a span.toctree-expand,.wy-menu-vertical li.on a .btn span.toctree-expand,.btn .wy-menu-vertical li.current>a span.toctree-expand,.wy-menu-vertical li.current>a .btn span.toctree-expand,.btn .rst-content .admonition-title,.rst-content .btn .admonition-title,.btn .rst-content h1 .headerlink,.rst-content h1 .btn .headerlink,.btn .rst-content h2 .headerlink,.rst-content h2 .btn .headerlink,.btn .rst-content p.caption .headerlink,.rst-content p.caption .btn .headerlink,.btn .rst-content h3 .headerlink,.rst-content h3 .btn .headerlink,.btn .rst-content h4 .headerlink,.rst-content h4 .btn .headerlink,.btn .rst-content h5 .headerlink,.rst-content h5 .btn .headerlink,.btn .rst-content h6 .headerlink,.rst-content h6 .btn .headerlink,.btn .rst-content dl dt .headerlink,.rst-content dl dt .btn .headerlink,.btn .rst-content tt.download span:first-child,.rst-content tt.download .btn span:first-child,.btn .rst-content code.download span:first-child,.rst-content code.download .btn span:first-child,.btn .icon,.nav .fa,.nav .wy-menu-vertical li span.toctree-expand,.wy-menu-vertical li .nav span.toctree-expand,.nav .wy-menu-vertical li.on a span.toctree-expand,.wy-menu-vertical li.on a .nav span.toctree-expand,.nav .wy-menu-vertical li.current>a span.toctree-expand,.wy-menu-vertical li.current>a .nav span.toctree-expand,.nav .rst-content .admonition-title,.rst-content .nav .admonition-title,.nav .rst-content h1 .headerlink,.rst-content h1 .nav .headerlink,.nav .rst-content h2 .headerlink,.rst-content h2 .nav .headerlink,.nav .rst-content p.caption .headerlink,.rst-content p.caption .nav .headerlink,.nav .rst-content h3 .headerlink,.rst-content h3 .nav .headerlink,.nav .rst-content h4 .headerlink,.rst-content h4 .nav .headerlink,.nav .rst-content h5 .headerlink,.rst-content h5 .nav .headerlink,.nav .rst-content h6 .headerlink,.rst-content h6 .nav .headerlink,.nav .rst-content dl dt .headerlink,.rst-content dl dt .nav .headerlink,.nav .rst-content tt.download span:first-child,.rst-content tt.download .nav span:first-child,.nav .rst-content code.download span:first-child,.rst-content code.download .nav span:first-child,.nav .icon{display:inline}.btn .fa.fa-large,.btn .wy-menu-vertical li span.fa-large.toctree-expand,.wy-menu-vertical li .btn span.fa-large.toctree-expand,.btn .rst-content .fa-large.admonition-title,.rst-content .btn .fa-large.admonition-title,.btn .rst-content h1 .fa-large.headerlink,.rst-content h1 .btn .fa-large.headerlink,.btn .rst-content h2 .fa-large.headerlink,.rst-content h2 .btn .fa-large.headerlink,.btn .rst-content p.caption .fa-large.headerlink,.rst-content p.caption .btn .fa-large.headerlink,.btn .rst-content h3 .fa-large.headerlink,.rst-content h3 .btn .fa-large.headerlink,.btn .rst-content h4 .fa-large.headerlink,.rst-content h4 .btn .fa-large.headerlink,.btn .rst-content h5 .fa-large.headerlink,.rst-content h5 .btn .fa-large.headerlink,.btn .rst-content h6 .fa-large.headerlink,.rst-content h6 .btn .fa-large.headerlink,.btn .rst-content dl dt .fa-large.headerlink,.rst-content dl dt .btn .fa-large.headerlink,.btn .rst-content tt.download span.fa-large:first-child,.rst-content tt.download .btn span.fa-large:first-child,.btn .rst-content code.download span.fa-large:first-child,.rst-content code.download .btn span.fa-large:first-child,.btn .fa-large.icon,.nav .fa.fa-large,.nav .wy-menu-vertical li span.fa-large.toctree-expand,.wy-menu-vertical li .nav span.fa-large.toctree-expand,.nav .rst-content .fa-large.admonition-title,.rst-content .nav .fa-large.admonition-title,.nav .rst-content h1 .fa-large.headerlink,.rst-content h1 .nav .fa-large.headerlink,.nav .rst-content h2 .fa-large.headerlink,.rst-content h2 .nav .fa-large.headerlink,.nav .rst-content p.caption .fa-large.headerlink,.rst-content p.caption .nav .fa-large.headerlink,.nav .rst-content h3 .fa-large.headerlink,.rst-content h3 .nav .fa-large.headerlink,.nav .rst-content h4 .fa-large.headerlink,.rst-content h4 .nav .fa-large.headerlink,.nav .rst-content h5 .fa-large.headerlink,.rst-content h5 .nav .fa-large.headerlink,.nav .rst-content h6 .fa-large.headerlink,.rst-content h6 .nav .fa-large.headerlink,.nav .rst-content dl dt .fa-large.headerlink,.rst-content dl dt .nav .fa-large.headerlink,.nav .rst-content tt.download span.fa-large:first-child,.rst-content tt.download .nav span.fa-large:first-child,.nav .rst-content code.download span.fa-large:first-child,.rst-content code.download .nav span.fa-large:first-child,.nav .fa-large.icon{line-height:0.9em}.btn .fa.fa-spin,.btn .wy-menu-vertical li span.fa-spin.toctree-expand,.wy-menu-vertical li .btn span.fa-spin.toctree-expand,.btn .rst-content .fa-spin.admonition-title,.rst-content .btn .fa-spin.admonition-title,.btn .rst-content h1 .fa-spin.headerlink,.rst-content h1 .btn .fa-spin.headerlink,.btn .rst-content h2 .fa-spin.headerlink,.rst-content h2 .btn .fa-spin.headerlink,.btn .rst-content p.caption .fa-spin.headerlink,.rst-content p.caption .btn .fa-spin.headerlink,.btn .rst-content h3 .fa-spin.headerlink,.rst-content h3 .btn .fa-spin.headerlink,.btn .rst-content h4 .fa-spin.headerlink,.rst-content h4 .btn .fa-spin.headerlink,.btn .rst-content h5 .fa-spin.headerlink,.rst-content h5 .btn .fa-spin.headerlink,.btn .rst-content h6 .fa-spin.headerlink,.rst-content h6 .btn .fa-spin.headerlink,.btn .rst-content dl dt .fa-spin.headerlink,.rst-content dl dt .btn .fa-spin.headerlink,.btn .rst-content tt.download span.fa-spin:first-child,.rst-content tt.download .btn span.fa-spin:first-child,.btn .rst-content code.download span.fa-spin:first-child,.rst-content code.download .btn span.fa-spin:first-child,.btn .fa-spin.icon,.nav .fa.fa-spin,.nav .wy-menu-vertical li span.fa-spin.toctree-expand,.wy-menu-vertical li .nav span.fa-spin.toctree-expand,.nav .rst-content .fa-spin.admonition-title,.rst-content .nav .fa-spin.admonition-title,.nav .rst-content h1 .fa-spin.headerlink,.rst-content h1 .nav .fa-spin.headerlink,.nav .rst-content h2 .fa-spin.headerlink,.rst-content h2 .nav .fa-spin.headerlink,.nav .rst-content p.caption .fa-spin.headerlink,.rst-content p.caption .nav .fa-spin.headerlink,.nav .rst-content h3 .fa-spin.headerlink,.rst-content h3 .nav .fa-spin.headerlink,.nav .rst-content h4 .fa-spin.headerlink,.rst-content h4 .nav .fa-spin.headerlink,.nav .rst-content h5 .fa-spin.headerlink,.rst-content h5 .nav .fa-spin.headerlink,.nav .rst-content h6 .fa-spin.headerlink,.rst-content h6 .nav .fa-spin.headerlink,.nav .rst-content dl dt .fa-spin.headerlink,.rst-content dl dt .nav .fa-spin.headerlink,.nav .rst-content tt.download span.fa-spin:first-child,.rst-content tt.download .nav span.fa-spin:first-child,.nav .rst-content code.download span.fa-spin:first-child,.rst-content code.download .nav span.fa-spin:first-child,.nav .fa-spin.icon{display:inline-block}.btn.fa:before,.wy-menu-vertical li span.btn.toctree-expand:before,.rst-content .btn.admonition-title:before,.rst-content h1 .btn.headerlink:before,.rst-content h2 .btn.headerlink:before,.rst-content p.caption .btn.headerlink:before,.rst-content h3 .btn.headerlink:before,.rst-content h4 .btn.headerlink:before,.rst-content h5 .btn.headerlink:before,.rst-content h6 .btn.headerlink:before,.rst-content dl dt .btn.headerlink:before,.rst-content tt.download span.btn:first-child:before,.rst-content code.download span.btn:first-child:before,.btn.icon:before{opacity:0.5;-webkit-transition:opacity 0.05s ease-in;-moz-transition:opacity 0.05s ease-in;transition:opacity 0.05s ease-in}.btn.fa:hover:before,.wy-menu-vertical li span.btn.toctree-expand:hover:before,.rst-content .btn.admonition-title:hover:before,.rst-content h1 .btn.headerlink:hover:before,.rst-content h2 .btn.headerlink:hover:before,.rst-content p.caption .btn.headerlink:hover:before,.rst-content h3 .btn.headerlink:hover:before,.rst-content h4 .btn.headerlink:hover:before,.rst-content h5 .btn.headerlink:hover:before,.rst-content h6 .btn.headerlink:hover:before,.rst-content dl dt .btn.headerlink:hover:before,.rst-content tt.download span.btn:first-child:hover:before,.rst-content code.download span.btn:first-child:hover:before,.btn.icon:hover:before{opacity:1}.btn-mini .fa:before,.btn-mini .wy-menu-vertical li span.toctree-expand:before,.wy-menu-vertical li .btn-mini span.toctree-expand:before,.btn-mini .rst-content .admonition-title:before,.rst-content .btn-mini .admonition-title:before,.btn-mini .rst-content h1 .headerlink:before,.rst-content h1 .btn-mini .headerlink:before,.btn-mini .rst-content h2 .headerlink:before,.rst-content h2 .btn-mini .headerlink:before,.btn-mini .rst-content p.caption .headerlink:before,.rst-content p.caption .btn-mini .headerlink:before,.btn-mini .rst-content h3 .headerlink:before,.rst-content h3 .btn-mini .headerlink:before,.btn-mini .rst-content h4 .headerlink:before,.rst-content h4 .btn-mini .headerlink:before,.btn-mini .rst-content h5 .headerlink:before,.rst-content h5 .btn-mini .headerlink:before,.btn-mini .rst-content h6 .headerlink:before,.rst-content h6 .btn-mini .headerlink:before,.btn-mini .rst-content dl dt .headerlink:before,.rst-content dl dt .btn-mini .headerlink:before,.btn-mini .rst-content tt.download span:first-child:before,.rst-content tt.download .btn-mini span:first-child:before,.btn-mini .rst-content code.download span:first-child:before,.rst-content code.download .btn-mini span:first-child:before,.btn-mini .icon:before{font-size:14px;vertical-align:-15%}.wy-alert,.rst-content .note,.rst-content .attention,.rst-content .caution,.rst-content .danger,.rst-content .error,.rst-content .hint,.rst-content .important,.rst-content .tip,.rst-content .warning,.rst-content .seealso,.rst-content .admonition-todo{padding:12px;line-height:24px;margin-bottom:24px;background:#e7f2fa}.wy-alert-title,.rst-content .admonition-title{color:#fff;font-weight:bold;display:block;color:#fff;background:#6ab0de;margin:-12px;padding:6px 12px;margin-bottom:12px}.wy-alert.wy-alert-danger,.rst-content .wy-alert-danger.note,.rst-content .wy-alert-danger.attention,.rst-content .wy-alert-danger.caution,.rst-content .danger,.rst-content .error,.rst-content .wy-alert-danger.hint,.rst-content .wy-alert-danger.important,.rst-content .wy-alert-danger.tip,.rst-content .wy-alert-danger.warning,.rst-content .wy-alert-danger.seealso,.rst-content .wy-alert-danger.admonition-todo{background:#fdf3f2}.wy-alert.wy-alert-danger .wy-alert-title,.rst-content .wy-alert-danger.note .wy-alert-title,.rst-content .wy-alert-danger.attention .wy-alert-title,.rst-content .wy-alert-danger.caution .wy-alert-title,.rst-content .danger .wy-alert-title,.rst-content .error .wy-alert-title,.rst-content .wy-alert-danger.hint .wy-alert-title,.rst-content .wy-alert-danger.important .wy-alert-title,.rst-content .wy-alert-danger.tip .wy-alert-title,.rst-content .wy-alert-danger.warning .wy-alert-title,.rst-content .wy-alert-danger.seealso .wy-alert-title,.rst-content .wy-alert-danger.admonition-todo .wy-alert-title,.wy-alert.wy-alert-danger .rst-content .admonition-title,.rst-content .wy-alert.wy-alert-danger .admonition-title,.rst-content .wy-alert-danger.note .admonition-title,.rst-content .wy-alert-danger.attention .admonition-title,.rst-content .wy-alert-danger.caution .admonition-title,.rst-content .danger .admonition-title,.rst-content .error .admonition-title,.rst-content .wy-alert-danger.hint .admonition-title,.rst-content .wy-alert-danger.important .admonition-title,.rst-content .wy-alert-danger.tip .admonition-title,.rst-content .wy-alert-danger.warning .admonition-title,.rst-content .wy-alert-danger.seealso .admonition-title,.rst-content .wy-alert-danger.admonition-todo .admonition-title{background:#f29f97}.wy-alert.wy-alert-warning,.rst-content .wy-alert-warning.note,.rst-content .attention,.rst-content .caution,.rst-content .wy-alert-warning.danger,.rst-content .wy-alert-warning.error,.rst-content .wy-alert-warning.hint,.rst-content .wy-alert-warning.important,.rst-content .wy-alert-warning.tip,.rst-content .warning,.rst-content .wy-alert-warning.seealso,.rst-content .admonition-todo{background:#ffedcc}.wy-alert.wy-alert-warning .wy-alert-title,.rst-content .wy-alert-warning.note .wy-alert-title,.rst-content .attention .wy-alert-title,.rst-content .caution .wy-alert-title,.rst-content .wy-alert-warning.danger .wy-alert-title,.rst-content .wy-alert-warning.error .wy-alert-title,.rst-content .wy-alert-warning.hint .wy-alert-title,.rst-content .wy-alert-warning.important .wy-alert-title,.rst-content .wy-alert-warning.tip .wy-alert-title,.rst-content .warning .wy-alert-title,.rst-content .wy-alert-warning.seealso .wy-alert-title,.rst-content .admonition-todo .wy-alert-title,.wy-alert.wy-alert-warning .rst-content .admonition-title,.rst-content .wy-alert.wy-alert-warning .admonition-title,.rst-content .wy-alert-warning.note .admonition-title,.rst-content .attention .admonition-title,.rst-content .caution .admonition-title,.rst-content .wy-alert-warning.danger .admonition-title,.rst-content .wy-alert-warning.error .admonition-title,.rst-content .wy-alert-warning.hint .admonition-title,.rst-content .wy-alert-warning.important .admonition-title,.rst-content .wy-alert-warning.tip .admonition-title,.rst-content .warning .admonition-title,.rst-content .wy-alert-warning.seealso .admonition-title,.rst-content .admonition-todo .admonition-title{background:#f0b37e}.wy-alert.wy-alert-info,.rst-content .note,.rst-content .wy-alert-info.attention,.rst-content .wy-alert-info.caution,.rst-content .wy-alert-info.danger,.rst-content .wy-alert-info.error,.rst-content .wy-alert-info.hint,.rst-content .wy-alert-info.important,.rst-content .wy-alert-info.tip,.rst-content .wy-alert-info.warning,.rst-content .seealso,.rst-content .wy-alert-info.admonition-todo{background:#e7f2fa}.wy-alert.wy-alert-info .wy-alert-title,.rst-content .note .wy-alert-title,.rst-content .wy-alert-info.attention .wy-alert-title,.rst-content .wy-alert-info.caution .wy-alert-title,.rst-content .wy-alert-info.danger .wy-alert-title,.rst-content .wy-alert-info.error .wy-alert-title,.rst-content .wy-alert-info.hint .wy-alert-title,.rst-content .wy-alert-info.important .wy-alert-title,.rst-content .wy-alert-info.tip .wy-alert-title,.rst-content .wy-alert-info.warning .wy-alert-title,.rst-content .seealso .wy-alert-title,.rst-content .wy-alert-info.admonition-todo .wy-alert-title,.wy-alert.wy-alert-info .rst-content .admonition-title,.rst-content .wy-alert.wy-alert-info .admonition-title,.rst-content .note .admonition-title,.rst-content .wy-alert-info.attention .admonition-title,.rst-content .wy-alert-info.caution .admonition-title,.rst-content .wy-alert-info.danger .admonition-title,.rst-content .wy-alert-info.error .admonition-title,.rst-content .wy-alert-info.hint .admonition-title,.rst-content .wy-alert-info.important .admonition-title,.rst-content .wy-alert-info.tip .admonition-title,.rst-content .wy-alert-info.warning .admonition-title,.rst-content .seealso .admonition-title,.rst-content .wy-alert-info.admonition-todo .admonition-title{background:#6ab0de}.wy-alert.wy-alert-success,.rst-content .wy-alert-success.note,.rst-content .wy-alert-success.attention,.rst-content .wy-alert-success.caution,.rst-content .wy-alert-success.danger,.rst-content .wy-alert-success.error,.rst-content .hint,.rst-content .important,.rst-content .tip,.rst-content .wy-alert-success.warning,.rst-content .wy-alert-success.seealso,.rst-content .wy-alert-success.admonition-todo{background:#dbfaf4}.wy-alert.wy-alert-success .wy-alert-title,.rst-content .wy-alert-success.note .wy-alert-title,.rst-content .wy-alert-success.attention .wy-alert-title,.rst-content .wy-alert-success.caution .wy-alert-title,.rst-content .wy-alert-success.danger .wy-alert-title,.rst-content .wy-alert-success.error .wy-alert-title,.rst-content .hint .wy-alert-title,.rst-content .important .wy-alert-title,.rst-content .tip .wy-alert-title,.rst-content .wy-alert-success.warning .wy-alert-title,.rst-content .wy-alert-success.seealso .wy-alert-title,.rst-content .wy-alert-success.admonition-todo .wy-alert-title,.wy-alert.wy-alert-success .rst-content .admonition-title,.rst-content .wy-alert.wy-alert-success .admonition-title,.rst-content .wy-alert-success.note .admonition-title,.rst-content .wy-alert-success.attention .admonition-title,.rst-content .wy-alert-success.caution .admonition-title,.rst-content .wy-alert-success.danger .admonition-title,.rst-content .wy-alert-success.error .admonition-title,.rst-content .hint .admonition-title,.rst-content .important .admonition-title,.rst-content .tip .admonition-title,.rst-content .wy-alert-success.warning .admonition-title,.rst-content .wy-alert-success.seealso .admonition-title,.rst-content .wy-alert-success.admonition-todo .admonition-title{background:#1abc9c}.wy-alert.wy-alert-neutral,.rst-content .wy-alert-neutral.note,.rst-content .wy-alert-neutral.attention,.rst-content .wy-alert-neutral.caution,.rst-content .wy-alert-neutral.danger,.rst-content .wy-alert-neutral.error,.rst-content .wy-alert-neutral.hint,.rst-content .wy-alert-neutral.important,.rst-content .wy-alert-neutral.tip,.rst-content .wy-alert-neutral.warning,.rst-content .wy-alert-neutral.seealso,.rst-content .wy-alert-neutral.admonition-todo{background:#f3f6f6}.wy-alert.wy-alert-neutral .wy-alert-title,.rst-content .wy-alert-neutral.note .wy-alert-title,.rst-content .wy-alert-neutral.attention .wy-alert-title,.rst-content .wy-alert-neutral.caution .wy-alert-title,.rst-content .wy-alert-neutral.danger .wy-alert-title,.rst-content .wy-alert-neutral.error .wy-alert-title,.rst-content .wy-alert-neutral.hint .wy-alert-title,.rst-content .wy-alert-neutral.important .wy-alert-title,.rst-content .wy-alert-neutral.tip .wy-alert-title,.rst-content .wy-alert-neutral.warning .wy-alert-title,.rst-content .wy-alert-neutral.seealso .wy-alert-title,.rst-content .wy-alert-neutral.admonition-todo .wy-alert-title,.wy-alert.wy-alert-neutral .rst-content .admonition-title,.rst-content .wy-alert.wy-alert-neutral .admonition-title,.rst-content .wy-alert-neutral.note .admonition-title,.rst-content .wy-alert-neutral.attention .admonition-title,.rst-content .wy-alert-neutral.caution .admonition-title,.rst-content .wy-alert-neutral.danger .admonition-title,.rst-content .wy-alert-neutral.error .admonition-title,.rst-content .wy-alert-neutral.hint .admonition-title,.rst-content .wy-alert-neutral.important .admonition-title,.rst-content .wy-alert-neutral.tip .admonition-title,.rst-content .wy-alert-neutral.warning .admonition-title,.rst-content .wy-alert-neutral.seealso .admonition-title,.rst-content .wy-alert-neutral.admonition-todo .admonition-title{color:#404040;background:#e1e4e5}.wy-alert.wy-alert-neutral a,.rst-content .wy-alert-neutral.note a,.rst-content .wy-alert-neutral.attention a,.rst-content .wy-alert-neutral.caution a,.rst-content .wy-alert-neutral.danger a,.rst-content .wy-alert-neutral.error a,.rst-content .wy-alert-neutral.hint a,.rst-content .wy-alert-neutral.important a,.rst-content .wy-alert-neutral.tip a,.rst-content .wy-alert-neutral.warning a,.rst-content .wy-alert-neutral.seealso a,.rst-content .wy-alert-neutral.admonition-todo a{color:#2980B9}.wy-alert p:last-child,.rst-content .note p:last-child,.rst-content .attention p:last-child,.rst-content .caution p:last-child,.rst-content .danger p:last-child,.rst-content .error p:last-child,.rst-content .hint p:last-child,.rst-content .important p:last-child,.rst-content .tip p:last-child,.rst-content .warning p:last-child,.rst-content .seealso p:last-child,.rst-content .admonition-todo p:last-child{margin-bottom:0}.wy-tray-container{position:fixed;bottom:0px;left:0;z-index:600}.wy-tray-container li{display:block;width:300px;background:transparent;color:#fff;text-align:center;box-shadow:0 5px 5px 0 rgba(0,0,0,0.1);padding:0 24px;min-width:20%;opacity:0;height:0;line-height:56px;overflow:hidden;-webkit-transition:all 0.3s ease-in;-moz-transition:all 0.3s ease-in;transition:all 0.3s ease-in}.wy-tray-container li.wy-tray-item-success{background:#27AE60}.wy-tray-container li.wy-tray-item-info{background:#2980B9}.wy-tray-container li.wy-tray-item-warning{background:#E67E22}.wy-tray-container li.wy-tray-item-danger{background:#E74C3C}.wy-tray-container li.on{opacity:1;height:56px}@media screen and (max-width: 768px){.wy-tray-container{bottom:auto;top:0;width:100%}.wy-tray-container li{width:100%}}button{font-size:100%;margin:0;vertical-align:baseline;*vertical-align:middle;cursor:pointer;line-height:normal;-webkit-appearance:button;*overflow:visible}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}button[disabled]{cursor:default}.btn{display:inline-block;border-radius:2px;line-height:normal;white-space:nowrap;text-align:center;cursor:pointer;font-size:100%;padding:6px 12px 8px 12px;color:#fff;border:1px solid rgba(0,0,0,0.1);background-color:#27AE60;text-decoration:none;font-weight:normal;font-family:"Lato","proxima-nova","Helvetica Neue",Arial,sans-serif;box-shadow:0px 1px 2px -1px rgba(255,255,255,0.5) inset,0px -2px 0px 0px rgba(0,0,0,0.1) inset;outline-none:false;vertical-align:middle;*display:inline;zoom:1;-webkit-user-drag:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-webkit-transition:all 0.1s linear;-moz-transition:all 0.1s linear;transition:all 0.1s linear}.btn-hover{background:#2e8ece;color:#fff}.btn:hover{background:#2cc36b;color:#fff}.btn:focus{background:#2cc36b;outline:0}.btn:active{box-shadow:0px -1px 0px 0px rgba(0,0,0,0.05) inset,0px 2px 0px 0px rgba(0,0,0,0.1) inset;padding:8px 12px 6px 12px}.btn:visited{color:#fff}.btn:disabled{background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);filter:alpha(opacity=40);opacity:0.4;cursor:not-allowed;box-shadow:none}.btn-disabled{background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);filter:alpha(opacity=40);opacity:0.4;cursor:not-allowed;box-shadow:none}.btn-disabled:hover,.btn-disabled:focus,.btn-disabled:active{background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);filter:alpha(opacity=40);opacity:0.4;cursor:not-allowed;box-shadow:none}.btn::-moz-focus-inner{padding:0;border:0}.btn-small{font-size:80%}.btn-info{background-color:#2980B9 !important}.btn-info:hover{background-color:#2e8ece !important}.btn-neutral{background-color:#f3f6f6 !important;color:#404040 !important}.btn-neutral:hover{background-color:#e5ebeb !important;color:#404040}.btn-neutral:visited{color:#404040 !important}.btn-success{background-color:#27AE60 !important}.btn-success:hover{background-color:#295 !important}.btn-danger{background-color:#E74C3C !important}.btn-danger:hover{background-color:#ea6153 !important}.btn-warning{background-color:#E67E22 !important}.btn-warning:hover{background-color:#e98b39 !important}.btn-invert{background-color:#222}.btn-invert:hover{background-color:#2f2f2f !important}.btn-link{background-color:transparent !important;color:#2980B9;box-shadow:none;border-color:transparent !important}.btn-link:hover{background-color:transparent !important;color:#409ad5 !important;box-shadow:none}.btn-link:active{background-color:transparent !important;color:#409ad5 !important;box-shadow:none}.btn-link:visited{color:#9B59B6}.wy-btn-group .btn,.wy-control .btn{vertical-align:middle}.wy-btn-group{margin-bottom:24px;*zoom:1}.wy-btn-group:before,.wy-btn-group:after{display:table;content:""}.wy-btn-group:after{clear:both}.wy-dropdown{position:relative;display:inline-block}.wy-dropdown-active .wy-dropdown-menu{display:block}.wy-dropdown-menu{position:absolute;left:0;display:none;float:left;top:100%;min-width:100%;background:#fcfcfc;z-index:100;border:solid 1px #cfd7dd;box-shadow:0 2px 2px 0 rgba(0,0,0,0.1);padding:12px}.wy-dropdown-menu>dd>a{display:block;clear:both;color:#404040;white-space:nowrap;font-size:90%;padding:0 12px;cursor:pointer}.wy-dropdown-menu>dd>a:hover{background:#2980B9;color:#fff}.wy-dropdown-menu>dd.divider{border-top:solid 1px #cfd7dd;margin:6px 0}.wy-dropdown-menu>dd.search{padding-bottom:12px}.wy-dropdown-menu>dd.search input[type="search"]{width:100%}.wy-dropdown-menu>dd.call-to-action{background:#e3e3e3;text-transform:uppercase;font-weight:500;font-size:80%}.wy-dropdown-menu>dd.call-to-action:hover{background:#e3e3e3}.wy-dropdown-menu>dd.call-to-action .btn{color:#fff}.wy-dropdown.wy-dropdown-up .wy-dropdown-menu{bottom:100%;top:auto;left:auto;right:0}.wy-dropdown.wy-dropdown-bubble .wy-dropdown-menu{background:#fcfcfc;margin-top:2px}.wy-dropdown.wy-dropdown-bubble .wy-dropdown-menu a{padding:6px 12px}.wy-dropdown.wy-dropdown-bubble .wy-dropdown-menu a:hover{background:#2980B9;color:#fff}.wy-dropdown.wy-dropdown-left .wy-dropdown-menu{right:0;left:auto;text-align:right}.wy-dropdown-arrow:before{content:" ";border-bottom:5px solid #f5f5f5;border-left:5px solid transparent;border-right:5px solid transparent;position:absolute;display:block;top:-4px;left:50%;margin-left:-3px}.wy-dropdown-arrow.wy-dropdown-arrow-left:before{left:11px}.wy-form-stacked select{display:block}.wy-form-aligned input,.wy-form-aligned textarea,.wy-form-aligned select,.wy-form-aligned .wy-help-inline,.wy-form-aligned label{display:inline-block;*display:inline;*zoom:1;vertical-align:middle}.wy-form-aligned .wy-control-group>label{display:inline-block;vertical-align:middle;width:10em;margin:6px 12px 0 0;float:left}.wy-form-aligned .wy-control{float:left}.wy-form-aligned .wy-control label{display:block}.wy-form-aligned .wy-control select{margin-top:6px}fieldset{border:0;margin:0;padding:0}legend{display:block;width:100%;border:0;padding:0;white-space:normal;margin-bottom:24px;font-size:150%;*margin-left:-7px}label{display:block;margin:0 0 0.3125em 0;color:#333;font-size:90%}input,select,textarea{font-size:100%;margin:0;vertical-align:baseline;*vertical-align:middle}.wy-control-group{margin-bottom:24px;*zoom:1;max-width:68em;margin-left:auto;margin-right:auto;*zoom:1}.wy-control-group:before,.wy-control-group:after{display:table;content:""}.wy-control-group:after{clear:both}.wy-control-group:before,.wy-control-group:after{display:table;content:""}.wy-control-group:after{clear:both}.wy-control-group.wy-control-group-required>label:after{content:" *";color:#E74C3C}.wy-control-group .wy-form-full,.wy-control-group .wy-form-halves,.wy-control-group .wy-form-thirds{padding-bottom:12px}.wy-control-group .wy-form-full select,.wy-control-group .wy-form-halves select,.wy-control-group .wy-form-thirds select{width:100%}.wy-control-group .wy-form-full input[type="text"],.wy-control-group .wy-form-full input[type="password"],.wy-control-group .wy-form-full input[type="email"],.wy-control-group .wy-form-full input[type="url"],.wy-control-group .wy-form-full input[type="date"],.wy-control-group .wy-form-full input[type="month"],.wy-control-group .wy-form-full input[type="time"],.wy-control-group .wy-form-full input[type="datetime"],.wy-control-group .wy-form-full input[type="datetime-local"],.wy-control-group .wy-form-full input[type="week"],.wy-control-group .wy-form-full input[type="number"],.wy-control-group .wy-form-full input[type="search"],.wy-control-group .wy-form-full input[type="tel"],.wy-control-group .wy-form-full input[type="color"],.wy-control-group .wy-form-halves input[type="text"],.wy-control-group .wy-form-halves input[type="password"],.wy-control-group .wy-form-halves input[type="email"],.wy-control-group .wy-form-halves input[type="url"],.wy-control-group .wy-form-halves input[type="date"],.wy-control-group .wy-form-halves input[type="month"],.wy-control-group .wy-form-halves input[type="time"],.wy-control-group .wy-form-halves input[type="datetime"],.wy-control-group .wy-form-halves input[type="datetime-local"],.wy-control-group .wy-form-halves input[type="week"],.wy-control-group .wy-form-halves input[type="number"],.wy-control-group .wy-form-halves input[type="search"],.wy-control-group .wy-form-halves input[type="tel"],.wy-control-group .wy-form-halves input[type="color"],.wy-control-group .wy-form-thirds input[type="text"],.wy-control-group .wy-form-thirds input[type="password"],.wy-control-group .wy-form-thirds input[type="email"],.wy-control-group .wy-form-thirds input[type="url"],.wy-control-group .wy-form-thirds input[type="date"],.wy-control-group .wy-form-thirds input[type="month"],.wy-control-group .wy-form-thirds input[type="time"],.wy-control-group .wy-form-thirds input[type="datetime"],.wy-control-group .wy-form-thirds input[type="datetime-local"],.wy-control-group .wy-form-thirds input[type="week"],.wy-control-group .wy-form-thirds input[type="number"],.wy-control-group .wy-form-thirds input[type="search"],.wy-control-group .wy-form-thirds input[type="tel"],.wy-control-group .wy-form-thirds input[type="color"]{width:100%}.wy-control-group .wy-form-full{float:left;display:block;margin-right:2.35765%;width:100%;margin-right:0}.wy-control-group .wy-form-full:last-child{margin-right:0}.wy-control-group .wy-form-halves{float:left;display:block;margin-right:2.35765%;width:48.82117%}.wy-control-group .wy-form-halves:last-child{margin-right:0}.wy-control-group .wy-form-halves:nth-of-type(2n){margin-right:0}.wy-control-group .wy-form-halves:nth-of-type(2n+1){clear:left}.wy-control-group .wy-form-thirds{float:left;display:block;margin-right:2.35765%;width:31.76157%}.wy-control-group .wy-form-thirds:last-child{margin-right:0}.wy-control-group .wy-form-thirds:nth-of-type(3n){margin-right:0}.wy-control-group .wy-form-thirds:nth-of-type(3n+1){clear:left}.wy-control-group.wy-control-group-no-input .wy-control{margin:6px 0 0 0;font-size:90%}.wy-control-no-input{display:inline-block;margin:6px 0 0 0;font-size:90%}.wy-control-group.fluid-input input[type="text"],.wy-control-group.fluid-input input[type="password"],.wy-control-group.fluid-input input[type="email"],.wy-control-group.fluid-input input[type="url"],.wy-control-group.fluid-input input[type="date"],.wy-control-group.fluid-input input[type="month"],.wy-control-group.fluid-input input[type="time"],.wy-control-group.fluid-input input[type="datetime"],.wy-control-group.fluid-input input[type="datetime-local"],.wy-control-group.fluid-input input[type="week"],.wy-control-group.fluid-input input[type="number"],.wy-control-group.fluid-input input[type="search"],.wy-control-group.fluid-input input[type="tel"],.wy-control-group.fluid-input input[type="color"]{width:100%}.wy-form-message-inline{display:inline-block;padding-left:0.3em;color:#666;vertical-align:middle;font-size:90%}.wy-form-message{display:block;color:#999;font-size:70%;margin-top:0.3125em;font-style:italic}.wy-form-message p{font-size:inherit;font-style:italic;margin-bottom:6px}.wy-form-message p:last-child{margin-bottom:0}input{line-height:normal}input[type="button"],input[type="reset"],input[type="submit"]{-webkit-appearance:button;cursor:pointer;font-family:"Lato","proxima-nova","Helvetica Neue",Arial,sans-serif;*overflow:visible}input[type="text"],input[type="password"],input[type="email"],input[type="url"],input[type="date"],input[type="month"],input[type="time"],input[type="datetime"],input[type="datetime-local"],input[type="week"],input[type="number"],input[type="search"],input[type="tel"],input[type="color"]{-webkit-appearance:none;padding:6px;display:inline-block;border:1px solid #ccc;font-size:80%;font-family:"Lato","proxima-nova","Helvetica Neue",Arial,sans-serif;box-shadow:inset 0 1px 3px #ddd;border-radius:0;-webkit-transition:border 0.3s linear;-moz-transition:border 0.3s linear;transition:border 0.3s linear}input[type="datetime-local"]{padding:0.34375em 0.625em}input[disabled]{cursor:default}input[type="checkbox"],input[type="radio"]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;padding:0;margin-right:0.3125em;*height:13px;*width:13px}input[type="search"]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}input[type="search"]::-webkit-search-cancel-button,input[type="search"]::-webkit-search-decoration{-webkit-appearance:none}input[type="text"]:focus,input[type="password"]:focus,input[type="email"]:focus,input[type="url"]:focus,input[type="date"]:focus,input[type="month"]:focus,input[type="time"]:focus,input[type="datetime"]:focus,input[type="datetime-local"]:focus,input[type="week"]:focus,input[type="number"]:focus,input[type="search"]:focus,input[type="tel"]:focus,input[type="color"]:focus{outline:0;outline:thin dotted \9;border-color:#333}input.no-focus:focus{border-color:#ccc !important}input[type="file"]:focus,input[type="radio"]:focus,input[type="checkbox"]:focus{outline:thin dotted #333;outline:1px auto #129FEA}input[type="text"][disabled],input[type="password"][disabled],input[type="email"][disabled],input[type="url"][disabled],input[type="date"][disabled],input[type="month"][disabled],input[type="time"][disabled],input[type="datetime"][disabled],input[type="datetime-local"][disabled],input[type="week"][disabled],input[type="number"][disabled],input[type="search"][disabled],input[type="tel"][disabled],input[type="color"][disabled]{cursor:not-allowed;background-color:#fafafa}input:focus:invalid,textarea:focus:invalid,select:focus:invalid{color:#E74C3C;border:1px solid #E74C3C}input:focus:invalid:focus,textarea:focus:invalid:focus,select:focus:invalid:focus{border-color:#E74C3C}input[type="file"]:focus:invalid:focus,input[type="radio"]:focus:invalid:focus,input[type="checkbox"]:focus:invalid:focus{outline-color:#E74C3C}input.wy-input-large{padding:12px;font-size:100%}textarea{overflow:auto;vertical-align:top;width:100%;font-family:"Lato","proxima-nova","Helvetica Neue",Arial,sans-serif}select,textarea{padding:0.5em 0.625em;display:inline-block;border:1px solid #ccc;font-size:80%;box-shadow:inset 0 1px 3px #ddd;-webkit-transition:border 0.3s linear;-moz-transition:border 0.3s linear;transition:border 0.3s linear}select{border:1px solid #ccc;background-color:#fff}select[multiple]{height:auto}select:focus,textarea:focus{outline:0}select[disabled],textarea[disabled],input[readonly],select[readonly],textarea[readonly]{cursor:not-allowed;background-color:#fafafa}input[type="radio"][disabled],input[type="checkbox"][disabled]{cursor:not-allowed}.wy-checkbox,.wy-radio{margin:6px 0;color:#404040;display:block}.wy-checkbox input,.wy-radio input{vertical-align:baseline}.wy-form-message-inline{display:inline-block;*display:inline;*zoom:1;vertical-align:middle}.wy-input-prefix,.wy-input-suffix{white-space:nowrap;padding:6px}.wy-input-prefix .wy-input-context,.wy-input-suffix .wy-input-context{line-height:27px;padding:0 8px;display:inline-block;font-size:80%;background-color:#f3f6f6;border:solid 1px #ccc;color:#999}.wy-input-suffix .wy-input-context{border-left:0}.wy-input-prefix .wy-input-context{border-right:0}.wy-switch{width:36px;height:12px;margin:12px 0;position:relative;border-radius:4px;background:#ccc;cursor:pointer;-webkit-transition:all 0.2s ease-in-out;-moz-transition:all 0.2s ease-in-out;transition:all 0.2s ease-in-out}.wy-switch:before{position:absolute;content:"";display:block;width:18px;height:18px;border-radius:4px;background:#999;left:-3px;top:-3px;-webkit-transition:all 0.2s ease-in-out;-moz-transition:all 0.2s ease-in-out;transition:all 0.2s ease-in-out}.wy-switch:after{content:"false";position:absolute;left:48px;display:block;font-size:12px;color:#ccc}.wy-switch.active{background:#1e8449}.wy-switch.active:before{left:24px;background:#27AE60}.wy-switch.active:after{content:"true"}.wy-switch.disabled,.wy-switch.active.disabled{cursor:not-allowed}.wy-control-group.wy-control-group-error .wy-form-message,.wy-control-group.wy-control-group-error>label{color:#E74C3C}.wy-control-group.wy-control-group-error input[type="text"],.wy-control-group.wy-control-group-error input[type="password"],.wy-control-group.wy-control-group-error input[type="email"],.wy-control-group.wy-control-group-error input[type="url"],.wy-control-group.wy-control-group-error input[type="date"],.wy-control-group.wy-control-group-error input[type="month"],.wy-control-group.wy-control-group-error input[type="time"],.wy-control-group.wy-control-group-error input[type="datetime"],.wy-control-group.wy-control-group-error input[type="datetime-local"],.wy-control-group.wy-control-group-error input[type="week"],.wy-control-group.wy-control-group-error input[type="number"],.wy-control-group.wy-control-group-error input[type="search"],.wy-control-group.wy-control-group-error input[type="tel"],.wy-control-group.wy-control-group-error input[type="color"]{border:solid 1px #E74C3C}.wy-control-group.wy-control-group-error textarea{border:solid 1px #E74C3C}.wy-inline-validate{white-space:nowrap}.wy-inline-validate .wy-input-context{padding:0.5em 0.625em;display:inline-block;font-size:80%}.wy-inline-validate.wy-inline-validate-success .wy-input-context{color:#27AE60}.wy-inline-validate.wy-inline-validate-danger .wy-input-context{color:#E74C3C}.wy-inline-validate.wy-inline-validate-warning .wy-input-context{color:#E67E22}.wy-inline-validate.wy-inline-validate-info .wy-input-context{color:#2980B9}.rotate-90{-webkit-transform:rotate(90deg);-moz-transform:rotate(90deg);-ms-transform:rotate(90deg);-o-transform:rotate(90deg);transform:rotate(90deg)}.rotate-180{-webkit-transform:rotate(180deg);-moz-transform:rotate(180deg);-ms-transform:rotate(180deg);-o-transform:rotate(180deg);transform:rotate(180deg)}.rotate-270{-webkit-transform:rotate(270deg);-moz-transform:rotate(270deg);-ms-transform:rotate(270deg);-o-transform:rotate(270deg);transform:rotate(270deg)}.mirror{-webkit-transform:scaleX(-1);-moz-transform:scaleX(-1);-ms-transform:scaleX(-1);-o-transform:scaleX(-1);transform:scaleX(-1)}.mirror.rotate-90{-webkit-transform:scaleX(-1) rotate(90deg);-moz-transform:scaleX(-1) rotate(90deg);-ms-transform:scaleX(-1) rotate(90deg);-o-transform:scaleX(-1) rotate(90deg);transform:scaleX(-1) rotate(90deg)}.mirror.rotate-180{-webkit-transform:scaleX(-1) rotate(180deg);-moz-transform:scaleX(-1) rotate(180deg);-ms-transform:scaleX(-1) rotate(180deg);-o-transform:scaleX(-1) rotate(180deg);transform:scaleX(-1) rotate(180deg)}.mirror.rotate-270{-webkit-transform:scaleX(-1) rotate(270deg);-moz-transform:scaleX(-1) rotate(270deg);-ms-transform:scaleX(-1) rotate(270deg);-o-transform:scaleX(-1) rotate(270deg);transform:scaleX(-1) rotate(270deg)}@media only screen and (max-width: 480px){.wy-form button[type="submit"]{margin:0.7em 0 0}.wy-form input[type="text"],.wy-form input[type="password"],.wy-form input[type="email"],.wy-form input[type="url"],.wy-form input[type="date"],.wy-form input[type="month"],.wy-form input[type="time"],.wy-form input[type="datetime"],.wy-form input[type="datetime-local"],.wy-form input[type="week"],.wy-form input[type="number"],.wy-form input[type="search"],.wy-form input[type="tel"],.wy-form input[type="color"]{margin-bottom:0.3em;display:block}.wy-form label{margin-bottom:0.3em;display:block}.wy-form input[type="password"],.wy-form input[type="email"],.wy-form input[type="url"],.wy-form input[type="date"],.wy-form input[type="month"],.wy-form input[type="time"],.wy-form input[type="datetime"],.wy-form input[type="datetime-local"],.wy-form input[type="week"],.wy-form input[type="number"],.wy-form input[type="search"],.wy-form input[type="tel"],.wy-form input[type="color"]{margin-bottom:0}.wy-form-aligned .wy-control-group label{margin-bottom:0.3em;text-align:left;display:block;width:100%}.wy-form-aligned .wy-control{margin:1.5em 0 0 0}.wy-form .wy-help-inline,.wy-form-message-inline,.wy-form-message{display:block;font-size:80%;padding:6px 0}}@media screen and (max-width: 768px){.tablet-hide{display:none}}@media screen and (max-width: 480px){.mobile-hide{display:none}}.float-left{float:left}.float-right{float:right}.full-width{width:100%}.wy-table,.rst-content table.docutils,.rst-content table.field-list{border-collapse:collapse;border-spacing:0;empty-cells:show;margin-bottom:24px}.wy-table caption,.rst-content table.docutils caption,.rst-content table.field-list caption{color:#000;font:italic 85%/1 arial,sans-serif;padding:1em 0;text-align:center}.wy-table td,.rst-content table.docutils td,.rst-content table.field-list td,.wy-table th,.rst-content table.docutils th,.rst-content table.field-list th{font-size:90%;margin:0;overflow:visible;padding:8px 16px}.wy-table td:first-child,.rst-content table.docutils td:first-child,.rst-content table.field-list td:first-child,.wy-table th:first-child,.rst-content table.docutils th:first-child,.rst-content table.field-list th:first-child{border-left-width:0}.wy-table thead,.rst-content table.docutils thead,.rst-content table.field-list thead{color:#000;text-align:left;vertical-align:bottom;white-space:nowrap}.wy-table thead th,.rst-content table.docutils thead th,.rst-content table.field-list thead th{font-weight:bold;border-bottom:solid 2px #e1e4e5}.wy-table td,.rst-content table.docutils td,.rst-content table.field-list td{background-color:transparent;vertical-align:middle}.wy-table td p,.rst-content table.docutils td p,.rst-content table.field-list td p{line-height:18px}.wy-table td p:last-child,.rst-content table.docutils td p:last-child,.rst-content table.field-list td p:last-child{margin-bottom:0}.wy-table .wy-table-cell-min,.rst-content table.docutils .wy-table-cell-min,.rst-content table.field-list .wy-table-cell-min{width:1%;padding-right:0}.wy-table .wy-table-cell-min input[type=checkbox],.rst-content table.docutils .wy-table-cell-min input[type=checkbox],.rst-content table.field-list .wy-table-cell-min input[type=checkbox],.wy-table .wy-table-cell-min input[type=checkbox],.rst-content table.docutils .wy-table-cell-min input[type=checkbox],.rst-content table.field-list .wy-table-cell-min input[type=checkbox]{margin:0}.wy-table-secondary{color:gray;font-size:90%}.wy-table-tertiary{color:gray;font-size:80%}.wy-table-odd td,.wy-table-striped tr:nth-child(2n-1) td,.rst-content table.docutils:not(.field-list) tr:nth-child(2n-1) td{background-color:#f3f6f6}.wy-table-backed{background-color:#f3f6f6}.wy-table-bordered-all,.rst-content table.docutils{border:1px solid #e1e4e5}.wy-table-bordered-all td,.rst-content table.docutils td{border-bottom:1px solid #e1e4e5;border-left:1px solid #e1e4e5}.wy-table-bordered-all tbody>tr:last-child td,.rst-content table.docutils tbody>tr:last-child td{border-bottom-width:0}.wy-table-bordered{border:1px solid #e1e4e5}.wy-table-bordered-rows td{border-bottom:1px solid #e1e4e5}.wy-table-bordered-rows tbody>tr:last-child td{border-bottom-width:0}.wy-table-horizontal tbody>tr:last-child td{border-bottom-width:0}.wy-table-horizontal td,.wy-table-horizontal th{border-width:0 0 1px 0;border-bottom:1px solid #e1e4e5}.wy-table-horizontal tbody>tr:last-child td{border-bottom-width:0}.wy-table-responsive{margin-bottom:24px;max-width:100%;overflow:auto}.wy-table-responsive table{margin-bottom:0 !important}.wy-table-responsive table td,.wy-table-responsive table th{white-space:nowrap}a{color:#2980B9;text-decoration:none;cursor:pointer}a:hover{color:#3091d1}a:visited{color:#9B59B6}html{height:100%;overflow-x:hidden}body{font-family:"Lato","proxima-nova","Helvetica Neue",Arial,sans-serif;font-weight:normal;color:#404040;min-height:100%;overflow-x:hidden;background:#edf0f2}.wy-text-left{text-align:left}.wy-text-center{text-align:center}.wy-text-right{text-align:right}.wy-text-large{font-size:120%}.wy-text-normal{font-size:100%}.wy-text-small,small{font-size:80%}.wy-text-strike{text-decoration:line-through}.wy-text-warning{color:#E67E22 !important}a.wy-text-warning:hover{color:#eb9950 !important}.wy-text-info{color:#2980B9 !important}a.wy-text-info:hover{color:#409ad5 !important}.wy-text-success{color:#27AE60 !important}a.wy-text-success:hover{color:#36d278 !important}.wy-text-danger{color:#E74C3C !important}a.wy-text-danger:hover{color:#ed7669 !important}.wy-text-neutral{color:#404040 !important}a.wy-text-neutral:hover{color:#595959 !important}h1,h2,.rst-content p.caption,h3,h4,h5,h6,legend{margin-top:0;font-weight:700;font-family:"Roboto Slab","ff-tisa-web-pro","Georgia",Arial,sans-serif}p{line-height:24px;margin:0;font-size:16px;margin-bottom:24px}h1{font-size:175%}h2,.rst-content p.caption{font-size:150%}h3{font-size:125%}h4{font-size:115%}h5{font-size:110%}h6{font-size:100%}hr{display:block;height:1px;border:0;border-top:1px solid #e1e4e5;margin:24px 0;padding:0}code,.rst-content tt,.rst-content code{white-space:nowrap;max-width:100%;background:#fff;border:solid 1px #e1e4e5;font-size:75%;padding:0 5px;font-family:Consolas,"Andale Mono WT","Andale Mono","Lucida Console","Lucida Sans Typewriter","DejaVu Sans Mono","Bitstream Vera Sans Mono","Liberation Mono","Nimbus Mono L",Monaco,"Courier New",Courier,monospace;color:#E74C3C;overflow-x:auto}code.code-large,.rst-content tt.code-large{font-size:90%}.wy-plain-list-disc,.rst-content .section ul,.rst-content .toctree-wrapper ul,article ul{list-style:disc;line-height:24px;margin-bottom:24px}.wy-plain-list-disc li,.rst-content .section ul li,.rst-content .toctree-wrapper ul li,article ul li{list-style:disc;margin-left:24px}.wy-plain-list-disc li p:last-child,.rst-content .section ul li p:last-child,.rst-content .toctree-wrapper ul li p:last-child,article ul li p:last-child{margin-bottom:0}.wy-plain-list-disc li ul,.rst-content .section ul li ul,.rst-content .toctree-wrapper ul li ul,article ul li ul{margin-bottom:0}.wy-plain-list-disc li li,.rst-content .section ul li li,.rst-content .toctree-wrapper ul li li,article ul li li{list-style:circle}.wy-plain-list-disc li li li,.rst-content .section ul li li li,.rst-content .toctree-wrapper ul li li li,article ul li li li{list-style:square}.wy-plain-list-disc li ol li,.rst-content .section ul li ol li,.rst-content .toctree-wrapper ul li ol li,article ul li ol li{list-style:decimal}.wy-plain-list-decimal,.rst-content .section ol,.rst-content ol.arabic,article ol{list-style:decimal;line-height:24px;margin-bottom:24px}.wy-plain-list-decimal li,.rst-content .section ol li,.rst-content ol.arabic li,article ol li{list-style:decimal;margin-left:24px}.wy-plain-list-decimal li p:last-child,.rst-content .section ol li p:last-child,.rst-content ol.arabic li p:last-child,article ol li p:last-child{margin-bottom:0}.wy-plain-list-decimal li ul,.rst-content .section ol li ul,.rst-content ol.arabic li ul,article ol li ul{margin-bottom:0}.wy-plain-list-decimal li ul li,.rst-content .section ol li ul li,.rst-content ol.arabic li ul li,article ol li ul li{list-style:disc}.codeblock-example{border:1px solid #e1e4e5;border-bottom:none;padding:24px;padding-top:48px;font-weight:500;background:#fff;position:relative}.codeblock-example:after{content:"Example";position:absolute;top:0px;left:0px;background:#9B59B6;color:#fff;padding:6px 12px}.codeblock-example.prettyprint-example-only{border:1px solid #e1e4e5;margin-bottom:24px}.codeblock,pre.literal-block,.rst-content .literal-block,.rst-content pre.literal-block,div[class^='highlight']{border:1px solid #e1e4e5;padding:0px;overflow-x:auto;background:#fff;margin:1px 0 24px 0}.codeblock div[class^='highlight'],pre.literal-block div[class^='highlight'],.rst-content .literal-block div[class^='highlight'],div[class^='highlight'] div[class^='highlight']{border:none;background:none;margin:0}div[class^='highlight'] td.code{width:100%}.linenodiv pre{border-right:solid 1px #e6e9ea;margin:0;padding:12px 12px;font-family:Consolas,"Andale Mono WT","Andale Mono","Lucida Console","Lucida Sans Typewriter","DejaVu Sans Mono","Bitstream Vera Sans Mono","Liberation Mono","Nimbus Mono L",Monaco,"Courier New",Courier,monospace;font-size:12px;line-height:1.5;color:#d9d9d9}div[class^='highlight'] pre{white-space:pre;margin:0;padding:12px 12px;font-family:Consolas,"Andale Mono WT","Andale Mono","Lucida Console","Lucida Sans Typewriter","DejaVu Sans Mono","Bitstream Vera Sans Mono","Liberation Mono","Nimbus Mono L",Monaco,"Courier New",Courier,monospace;font-size:12px;line-height:1.5;display:block;overflow:auto;color:#404040}@media print{.codeblock,pre.literal-block,.rst-content .literal-block,.rst-content pre.literal-block,div[class^='highlight'],div[class^='highlight'] pre{white-space:pre-wrap}}.hll{background-color:#ffc;margin:0 -12px;padding:0 12px;display:block}.c{color:#998;font-style:italic}.err{color:#a61717;background-color:#e3d2d2}.k{font-weight:bold}.o{font-weight:bold}.cm{color:#998;font-style:italic}.cp{color:#999;font-weight:bold}.c1{color:#998;font-style:italic}.cs{color:#999;font-weight:bold;font-style:italic}.gd{color:#000;background-color:#fdd}.gd .x{color:#000;background-color:#faa}.ge{font-style:italic}.gr{color:#a00}.gh{color:#999}.gi{color:#000;background-color:#dfd}.gi .x{color:#000;background-color:#afa}.go{color:#888}.gp{color:#555}.gs{font-weight:bold}.gu{color:purple;font-weight:bold}.gt{color:#a00}.kc{font-weight:bold}.kd{font-weight:bold}.kn{font-weight:bold}.kp{font-weight:bold}.kr{font-weight:bold}.kt{color:#458;font-weight:bold}.m{color:#099}.s{color:#d14}.n{color:#333}.na{color:teal}.nb{color:#0086b3}.nc{color:#458;font-weight:bold}.no{color:teal}.ni{color:purple}.ne{color:#900;font-weight:bold}.nf{color:#900;font-weight:bold}.nn{color:#555}.nt{color:navy}.nv{color:teal}.ow{font-weight:bold}.w{color:#bbb}.mf{color:#099}.mh{color:#099}.mi{color:#099}.mo{color:#099}.sb{color:#d14}.sc{color:#d14}.sd{color:#d14}.s2{color:#d14}.se{color:#d14}.sh{color:#d14}.si{color:#d14}.sx{color:#d14}.sr{color:#009926}.s1{color:#d14}.ss{color:#990073}.bp{color:#999}.vc{color:teal}.vg{color:teal}.vi{color:teal}.il{color:#099}.gc{color:#999;background-color:#EAF2F5}.wy-breadcrumbs li{display:inline-block}.wy-breadcrumbs li.wy-breadcrumbs-aside{float:right}.wy-breadcrumbs li a{display:inline-block;padding:5px}.wy-breadcrumbs li a:first-child{padding-left:0}.wy-breadcrumbs-extra{margin-bottom:0;color:#b3b3b3;font-size:80%;display:inline-block}@media screen and (max-width: 480px){.wy-breadcrumbs-extra{display:none}.wy-breadcrumbs li.wy-breadcrumbs-aside{display:none}}@media print{.wy-breadcrumbs li.wy-breadcrumbs-aside{display:none}}.wy-affix{position:fixed;top:1.618em}.wy-menu a:hover{text-decoration:none}.wy-menu-horiz{*zoom:1}.wy-menu-horiz:before,.wy-menu-horiz:after{display:table;content:""}.wy-menu-horiz:after{clear:both}.wy-menu-horiz ul,.wy-menu-horiz li{display:inline-block}.wy-menu-horiz li:hover{background:rgba(255,255,255,0.1)}.wy-menu-horiz li.divide-left{border-left:solid 1px #404040}.wy-menu-horiz li.divide-right{border-right:solid 1px #404040}.wy-menu-horiz a{height:32px;display:inline-block;line-height:32px;padding:0 16px}.wy-menu-vertical header,.wy-menu-vertical p.caption{height:32px;display:inline-block;line-height:32px;padding:0 1.618em;margin-bottom:0;display:block;font-weight:bold;text-transform:uppercase;font-size:80%;color:#555;white-space:nowrap}.wy-menu-vertical ul{margin-bottom:0}.wy-menu-vertical li.divide-top{border-top:solid 1px #404040}.wy-menu-vertical li.divide-bottom{border-bottom:solid 1px #404040}.wy-menu-vertical li.current{background:#e3e3e3}.wy-menu-vertical li.current a{color:gray;border-right:solid 1px #c9c9c9;padding:0.4045em 2.427em}.wy-menu-vertical li.current a:hover{background:#d6d6d6}.wy-menu-vertical li code,.wy-menu-vertical li .rst-content tt,.rst-content .wy-menu-vertical li tt{border:none;background:inherit;color:inherit;padding-left:0;padding-right:0}.wy-menu-vertical li span.toctree-expand{display:block;float:left;margin-left:-1.2em;font-size:0.8em;line-height:1.6em;color:#4d4d4d}.wy-menu-vertical li.on a,.wy-menu-vertical li.current>a{color:#404040;padding:0.4045em 1.618em;font-weight:bold;position:relative;background:#fcfcfc;border:none;border-bottom:solid 1px #c9c9c9;border-top:solid 1px #c9c9c9;padding-left:1.618em -4px}.wy-menu-vertical li.on a:hover,.wy-menu-vertical li.current>a:hover{background:#fcfcfc}.wy-menu-vertical li.on a:hover span.toctree-expand,.wy-menu-vertical li.current>a:hover span.toctree-expand{color:gray}.wy-menu-vertical li.on a span.toctree-expand,.wy-menu-vertical li.current>a span.toctree-expand{display:block;font-size:0.8em;line-height:1.6em;color:#333}.wy-menu-vertical li.toctree-l1.current li.toctree-l2>ul,.wy-menu-vertical li.toctree-l2.current li.toctree-l3>ul{display:none}.wy-menu-vertical li.toctree-l1.current li.toctree-l2.current>ul,.wy-menu-vertical li.toctree-l2.current li.toctree-l3.current>ul{display:block}.wy-menu-vertical li.toctree-l2.current>a{background:#c9c9c9;padding:0.4045em 2.427em}.wy-menu-vertical li.toctree-l2.current li.toctree-l3>a{display:block;background:#c9c9c9;padding:0.4045em 4.045em}.wy-menu-vertical li.toctree-l2 a:hover span.toctree-expand{color:gray}.wy-menu-vertical li.toctree-l2 span.toctree-expand{color:#a3a3a3}.wy-menu-vertical li.toctree-l3{font-size:0.9em}.wy-menu-vertical li.toctree-l3.current>a{background:#bdbdbd;padding:0.4045em 4.045em}.wy-menu-vertical li.toctree-l3.current li.toctree-l4>a{display:block;background:#bdbdbd;padding:0.4045em 5.663em;border-top:none;border-bottom:none}.wy-menu-vertical li.toctree-l3 a:hover span.toctree-expand{color:gray}.wy-menu-vertical li.toctree-l3 span.toctree-expand{color:#969696}.wy-menu-vertical li.toctree-l4{font-size:0.9em}.wy-menu-vertical li.current ul{display:block}.wy-menu-vertical li ul{margin-bottom:0;display:none}.wy-menu-vertical .local-toc li ul{display:block}.wy-menu-vertical li ul li a{margin-bottom:0;color:#b3b3b3;font-weight:normal}.wy-menu-vertical a{display:inline-block;line-height:18px;padding:0.4045em 1.618em;display:block;position:relative;font-size:90%;color:#b3b3b3}.wy-menu-vertical a:hover{background-color:#4e4a4a;cursor:pointer}.wy-menu-vertical a:hover span.toctree-expand{color:#b3b3b3}.wy-menu-vertical a:active{background-color:#2980B9;cursor:pointer;color:#fff}.wy-menu-vertical a:active span.toctree-expand{color:#fff}.wy-side-nav-search{z-index:200;background-color:#2980B9;text-align:center;padding:0.809em;display:block;color:#fcfcfc;margin-bottom:0.809em}.wy-side-nav-search input[type=text]{width:100%;border-radius:50px;padding:6px 12px;border-color:#2472a4}.wy-side-nav-search img{display:block;margin:auto auto 0.809em auto;height:45px;width:45px;background-color:#2980B9;padding:5px;border-radius:100%}.wy-side-nav-search>a,.wy-side-nav-search .wy-dropdown>a{color:#fcfcfc;font-size:100%;font-weight:bold;display:inline-block;padding:4px 6px;margin-bottom:0.809em}.wy-side-nav-search>a:hover,.wy-side-nav-search .wy-dropdown>a:hover{background:rgba(255,255,255,0.1)}.wy-side-nav-search>a img.logo,.wy-side-nav-search .wy-dropdown>a img.logo{display:block;margin:0 auto;height:auto;width:auto;border-radius:0;max-width:100%;background:transparent}.wy-side-nav-search>a.icon img.logo,.wy-side-nav-search .wy-dropdown>a.icon img.logo{margin-top:0.85em}.wy-nav .wy-menu-vertical header{color:#2980B9}.wy-nav .wy-menu-vertical a{color:#b3b3b3}.wy-nav .wy-menu-vertical a:hover{background-color:#2980B9;color:#fff}[data-menu-wrap]{-webkit-transition:all 0.2s ease-in;-moz-transition:all 0.2s ease-in;transition:all 0.2s ease-in;position:absolute;opacity:1;width:100%;opacity:0}[data-menu-wrap].move-center{left:0;right:auto;opacity:1}[data-menu-wrap].move-left{right:auto;left:-100%;opacity:0}[data-menu-wrap].move-right{right:-100%;left:auto;opacity:0}.wy-body-for-nav{background:left repeat-y #fcfcfc;background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyRpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoTWFjaW50b3NoKSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDoxOERBMTRGRDBFMUUxMUUzODUwMkJCOThDMEVFNURFMCIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDoxOERBMTRGRTBFMUUxMUUzODUwMkJCOThDMEVFNURFMCI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOjE4REExNEZCMEUxRTExRTM4NTAyQkI5OEMwRUU1REUwIiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOjE4REExNEZDMEUxRTExRTM4NTAyQkI5OEMwRUU1REUwIi8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+EwrlwAAAAA5JREFUeNpiMDU0BAgwAAE2AJgB9BnaAAAAAElFTkSuQmCC);background-size:300px 1px}.wy-grid-for-nav{position:absolute;width:100%;height:100%}.wy-nav-side{position:fixed;top:0;bottom:0;left:0;padding-bottom:2em;width:300px;overflow-x:hidden;overflow-y:scroll;min-height:100%;background:#343131;z-index:200}.wy-nav-top{display:none;background:#2980B9;color:#fff;padding:0.4045em 0.809em;position:relative;line-height:50px;text-align:center;font-size:100%;*zoom:1}.wy-nav-top:before,.wy-nav-top:after{display:table;content:""}.wy-nav-top:after{clear:both}.wy-nav-top a{color:#fff;font-weight:bold}.wy-nav-top img{margin-right:12px;height:45px;width:45px;background-color:#2980B9;padding:5px;border-radius:100%}.wy-nav-top i{font-size:30px;float:left;cursor:pointer}.wy-nav-content-wrap{margin-left:300px;background:#fcfcfc;min-height:100%}.wy-nav-content{padding:1.618em 3.236em;height:100%;max-width:800px;margin:auto}.wy-body-mask{position:fixed;width:100%;height:100%;background:rgba(0,0,0,0.2);display:none;z-index:499}.wy-body-mask.on{display:block}footer{color:#999}footer p{margin-bottom:12px}.rst-footer-buttons{*zoom:1}.rst-footer-buttons:before,.rst-footer-buttons:after{display:table;content:""}.rst-footer-buttons:after{clear:both}#search-results .search li{margin-bottom:24px;border-bottom:solid 1px #e1e4e5;padding-bottom:24px}#search-results .search li:first-child{border-top:solid 1px #e1e4e5;padding-top:24px}#search-results .search li a{font-size:120%;margin-bottom:12px;display:inline-block}#search-results .context{color:gray;font-size:90%}@media screen and (max-width: 768px){.wy-body-for-nav{background:#fcfcfc}.wy-nav-top{display:block}.wy-nav-side{left:-300px}.wy-nav-side.shift{width:85%;left:0}.wy-nav-content-wrap{margin-left:0}.wy-nav-content-wrap .wy-nav-content{padding:1.618em}.wy-nav-content-wrap.shift{position:fixed;min-width:100%;left:85%;top:0;height:100%;overflow:hidden}}@media screen and (min-width: 1400px){.wy-nav-content-wrap{background:rgba(0,0,0,0.05)}.wy-nav-content{margin:0;background:#fcfcfc}}@media print{.rst-versions,footer,.wy-nav-side{display:none}.wy-nav-content-wrap{margin-left:0}}.rst-versions{position:fixed;bottom:0;left:0;width:300px;color:#fcfcfc;background:#1f1d1d;border-top:solid 10px #343131;font-family:"Lato","proxima-nova","Helvetica Neue",Arial,sans-serif;z-index:400}.rst-versions a{color:#2980B9;text-decoration:none}.rst-versions .rst-badge-small{display:none}.rst-versions .rst-current-version{padding:12px;background-color:#272525;display:block;text-align:right;font-size:90%;cursor:pointer;color:#27AE60;*zoom:1}.rst-versions .rst-current-version:before,.rst-versions .rst-current-version:after{display:table;content:""}.rst-versions .rst-current-version:after{clear:both}.rst-versions .rst-current-version .fa,.rst-versions .rst-current-version .wy-menu-vertical li span.toctree-expand,.wy-menu-vertical li .rst-versions .rst-current-version span.toctree-expand,.rst-versions .rst-current-version .rst-content .admonition-title,.rst-content .rst-versions .rst-current-version .admonition-title,.rst-versions .rst-current-version .rst-content h1 .headerlink,.rst-content h1 .rst-versions .rst-current-version .headerlink,.rst-versions .rst-current-version .rst-content h2 .headerlink,.rst-content h2 .rst-versions .rst-current-version .headerlink,.rst-versions .rst-current-version .rst-content p.caption .headerlink,.rst-content p.caption .rst-versions .rst-current-version .headerlink,.rst-versions .rst-current-version .rst-content h3 .headerlink,.rst-content h3 .rst-versions .rst-current-version .headerlink,.rst-versions .rst-current-version .rst-content h4 .headerlink,.rst-content h4 .rst-versions .rst-current-version .headerlink,.rst-versions .rst-current-version .rst-content h5 .headerlink,.rst-content h5 .rst-versions .rst-current-version .headerlink,.rst-versions .rst-current-version .rst-content h6 .headerlink,.rst-content h6 .rst-versions .rst-current-version .headerlink,.rst-versions .rst-current-version .rst-content dl dt .headerlink,.rst-content dl dt .rst-versions .rst-current-version .headerlink,.rst-versions .rst-current-version .rst-content tt.download span:first-child,.rst-content tt.download .rst-versions .rst-current-version span:first-child,.rst-versions .rst-current-version .rst-content code.download span:first-child,.rst-content code.download .rst-versions .rst-current-version span:first-child,.rst-versions .rst-current-version .icon{color:#fcfcfc}.rst-versions .rst-current-version .fa-book,.rst-versions .rst-current-version .icon-book{float:left}.rst-versions .rst-current-version .icon-book{float:left}.rst-versions .rst-current-version.rst-out-of-date{background-color:#E74C3C;color:#fff}.rst-versions .rst-current-version.rst-active-old-version{background-color:#F1C40F;color:#000}.rst-versions.shift-up .rst-other-versions{display:block}.rst-versions .rst-other-versions{font-size:90%;padding:12px;color:gray;display:none}.rst-versions .rst-other-versions hr{display:block;height:1px;border:0;margin:20px 0;padding:0;border-top:solid 1px #413d3d}.rst-versions .rst-other-versions dd{display:inline-block;margin:0}.rst-versions .rst-other-versions dd a{display:inline-block;padding:6px;color:#fcfcfc}.rst-versions.rst-badge{width:auto;bottom:20px;right:20px;left:auto;border:none;max-width:300px}.rst-versions.rst-badge .icon-book{float:none}.rst-versions.rst-badge .fa-book,.rst-versions.rst-badge .icon-book{float:none}.rst-versions.rst-badge.shift-up .rst-current-version{text-align:right}.rst-versions.rst-badge.shift-up .rst-current-version .fa-book,.rst-versions.rst-badge.shift-up .rst-current-version .icon-book{float:left}.rst-versions.rst-badge.shift-up .rst-current-version .icon-book{float:left}.rst-versions.rst-badge .rst-current-version{width:auto;height:30px;line-height:30px;padding:0 6px;display:block;text-align:center}@media screen and (max-width: 768px){.rst-versions{width:85%;display:none}.rst-versions.shift{display:block}img{width:100%;height:auto}}.rst-content img{max-width:100%;height:auto !important}.rst-content div.figure{margin-bottom:24px}.rst-content div.figure.align-center{text-align:center}.rst-content .section>img,.rst-content .section>a>img{margin-bottom:24px}.rst-content blockquote{margin-left:24px;line-height:24px;margin-bottom:24px}.rst-content .note .last,.rst-content .attention .last,.rst-content .caution .last,.rst-content .danger .last,.rst-content .error .last,.rst-content .hint .last,.rst-content .important .last,.rst-content .tip .last,.rst-content .warning .last,.rst-content .seealso .last,.rst-content .admonition-todo .last{margin-bottom:0}.rst-content .admonition-title:before{margin-right:4px}.rst-content .admonition table{border-color:rgba(0,0,0,0.1)}.rst-content .admonition table td,.rst-content .admonition table th{background:transparent !important;border-color:rgba(0,0,0,0.1) !important}.rst-content .section ol.loweralpha,.rst-content .section ol.loweralpha li{list-style:lower-alpha}.rst-content .section ol.upperalpha,.rst-content .section ol.upperalpha li{list-style:upper-alpha}.rst-content .section ol p,.rst-content .section ul p{margin-bottom:12px}.rst-content .line-block{margin-left:24px}.rst-content .topic-title{font-weight:bold;margin-bottom:12px}.rst-content .toc-backref{color:#404040}.rst-content .align-right{float:right;margin:0px 0px 24px 24px}.rst-content .align-left{float:left;margin:0px 24px 24px 0px}.rst-content .align-center{margin:auto;display:block}.rst-content h1 .headerlink,.rst-content h2 .headerlink,.rst-content p.caption .headerlink,.rst-content h3 .headerlink,.rst-content h4 .headerlink,.rst-content h5 .headerlink,.rst-content h6 .headerlink,.rst-content dl dt .headerlink,.rst-content p.caption .headerlink{display:none;visibility:hidden;font-size:14px}.rst-content h1 .headerlink:after,.rst-content h2 .headerlink:after,.rst-content p.caption .headerlink:after,.rst-content h3 .headerlink:after,.rst-content h4 .headerlink:after,.rst-content h5 .headerlink:after,.rst-content h6 .headerlink:after,.rst-content dl dt .headerlink:after,.rst-content p.caption .headerlink:after{visibility:visible;content:"";font-family:FontAwesome;display:inline-block}.rst-content h1:hover .headerlink,.rst-content h2:hover .headerlink,.rst-content p.caption:hover .headerlink,.rst-content h3:hover .headerlink,.rst-content h4:hover .headerlink,.rst-content h5:hover .headerlink,.rst-content h6:hover .headerlink,.rst-content dl dt:hover .headerlink,.rst-content p.caption:hover .headerlink{display:inline-block}.rst-content .sidebar{float:right;width:40%;display:block;margin:0 0 24px 24px;padding:24px;background:#f3f6f6;border:solid 1px #e1e4e5}.rst-content .sidebar p,.rst-content .sidebar ul,.rst-content .sidebar dl{font-size:90%}.rst-content .sidebar .last{margin-bottom:0}.rst-content .sidebar .sidebar-title{display:block;font-family:"Roboto Slab","ff-tisa-web-pro","Georgia",Arial,sans-serif;font-weight:bold;background:#e1e4e5;padding:6px 12px;margin:-24px;margin-bottom:24px;font-size:100%}.rst-content .highlighted{background:#F1C40F;display:inline-block;font-weight:bold;padding:0 6px}.rst-content .footnote-reference,.rst-content .citation-reference{vertical-align:super;font-size:90%}.rst-content table.docutils.citation,.rst-content table.docutils.footnote{background:none;border:none;color:#999}.rst-content table.docutils.citation td,.rst-content table.docutils.citation tr,.rst-content table.docutils.footnote td,.rst-content table.docutils.footnote tr{border:none;background-color:transparent !important;white-space:normal}.rst-content table.docutils.citation td.label,.rst-content table.docutils.footnote td.label{padding-left:0;padding-right:0;vertical-align:top}.rst-content table.field-list{border:none}.rst-content table.field-list td{border:none;padding-top:5px}.rst-content table.field-list td>strong{display:inline-block;margin-top:3px}.rst-content table.field-list .field-name{padding-right:10px;text-align:left;white-space:nowrap}.rst-content table.field-list .field-body{text-align:left;padding-left:0}.rst-content tt,.rst-content tt,.rst-content code{color:#000}.rst-content tt big,.rst-content tt em,.rst-content tt big,.rst-content code big,.rst-content tt em,.rst-content code em{font-size:100% !important;line-height:normal}.rst-content tt .xref,a .rst-content tt,.rst-content tt .xref,.rst-content code .xref,a .rst-content tt,a .rst-content code{font-weight:bold}.rst-content a tt,.rst-content a tt,.rst-content a code{color:#2980B9}.rst-content dl{margin-bottom:24px}.rst-content dl dt{font-weight:bold}.rst-content dl p,.rst-content dl table,.rst-content dl ul,.rst-content dl ol{margin-bottom:12px !important}.rst-content dl dd{margin:0 0 12px 24px}.rst-content dl:not(.docutils){margin-bottom:24px}.rst-content dl:not(.docutils) dt{display:inline-block;margin:6px 0;font-size:90%;line-height:normal;background:#e7f2fa;color:#2980B9;border-top:solid 3px #6ab0de;padding:6px;position:relative}.rst-content dl:not(.docutils) dt:before{color:#6ab0de}.rst-content dl:not(.docutils) dt .headerlink{color:#404040;font-size:100% !important}.rst-content dl:not(.docutils) dl dt{margin-bottom:6px;border:none;border-left:solid 3px #ccc;background:#f0f0f0;color:gray}.rst-content dl:not(.docutils) dl dt .headerlink{color:#404040;font-size:100% !important}.rst-content dl:not(.docutils) dt:first-child{margin-top:0}.rst-content dl:not(.docutils) tt,.rst-content dl:not(.docutils) tt,.rst-content dl:not(.docutils) code{font-weight:bold}.rst-content dl:not(.docutils) tt.descname,.rst-content dl:not(.docutils) tt.descclassname,.rst-content dl:not(.docutils) tt.descname,.rst-content dl:not(.docutils) code.descname,.rst-content dl:not(.docutils) tt.descclassname,.rst-content dl:not(.docutils) code.descclassname{background-color:transparent;border:none;padding:0;font-size:100% !important}.rst-content dl:not(.docutils) tt.descname,.rst-content dl:not(.docutils) tt.descname,.rst-content dl:not(.docutils) code.descname{font-weight:bold}.rst-content dl:not(.docutils) .optional{display:inline-block;padding:0 4px;color:#000;font-weight:bold}.rst-content dl:not(.docutils) .property{display:inline-block;padding-right:8px}.rst-content .viewcode-link,.rst-content .viewcode-back{display:inline-block;color:#27AE60;font-size:80%;padding-left:24px}.rst-content .viewcode-back{display:block;float:right}.rst-content p.rubric{margin-bottom:12px;font-weight:bold}.rst-content tt.download,.rst-content code.download{background:inherit;padding:inherit;font-family:inherit;font-size:inherit;color:inherit;border:inherit;white-space:inherit}.rst-content tt.download span:first-child:before,.rst-content code.download span:first-child:before{margin-right:4px}@media screen and (max-width: 480px){.rst-content .sidebar{width:100%}}span[id*='MathJax-Span']{color:#404040}.math{text-align:center}@font-face{font-family:"Inconsolata";font-style:normal;font-weight:400;src:local("Inconsolata"),url(../fonts/Inconsolata.ttf) format("truetype")}@font-face{font-family:"Inconsolata";font-style:normal;font-weight:700;src:local("Inconsolata Bold"),local("Inconsolata-Bold"),url(../fonts/Inconsolata-Bold.ttf) format("truetype")}@font-face{font-family:"Lato";font-style:normal;font-weight:400;src:local("Lato Regular"),local("Lato-Regular"),url(../fonts/Lato-Regular.ttf) format("truetype")}@font-face{font-family:"Lato";font-style:normal;font-weight:700;src:local("Lato Bold"),local("Lato-Bold"),url(../fonts/Lato-Bold.ttf) format("truetype")}@font-face{font-family:"Roboto Slab";font-style:normal;font-weight:400;src:local("Roboto Slab Regular"),local("RobotoSlab-Regular"),url(../fonts/RobotoSlab-Regular.ttf) format("truetype")}@font-face{font-family:"Roboto Slab";font-style:normal;font-weight:700;src:local("Roboto Slab Bold"),local("RobotoSlab-Bold"),url(../fonts/RobotoSlab-Bold.ttf) format("truetype")} +/*# sourceMappingURL=theme.css.map */ diff --git a/doc/_themes/sphinx_rtd_theme/static/fonts/FontAwesome.otf b/doc/_themes/sphinx_rtd_theme/static/fonts/FontAwesome.otf new file mode 100644 index 0000000000000000000000000000000000000000..8b0f54e47e1d356dcf1496942a50e228e0f1ee14 GIT binary patch literal 62856 zcmcfp2Y3_5)&LBzEbU6(wGF`%u_do$I-wUs=poc3^xzP>t859|l91%ydy%{4ZewH9 zLNU#OK%5)jlp7M#adH#VlN(Y~MSVYG)7F`Dsts8mQIv>+ztD)dFw+9OVG%`1 zdML`ns?&x=Qnp|IfM+dm&(}ePcdqmf37+Ghm#p%f+FVKQ2*chjkzF#ZB~9w-bef!xGBr6D7h{6UGOP@t%*!8rhr zqTX&D_txFJckW8F88SgJDOYWQiq1}9HpST zU`<34PZ)C!_3}_&M2)6kC53tq%16Wv<;B!kk^fL$a$g&o8ZTNrRL|U3FQqy}Aw%^t z%FjbIl=r0M9>Z`rYKq77t>{++@-k0@oM~*1+}p2(7`Q4V*n=HYq=vsI?g5v}-nP z3|{}}ibb1(*R0;YdDD}@+q7nj-e?F6nlWp}oWMD=X3yOms||yGW^I(#9B4HL0`>*2 zG{Pq6qjlCmi#Eba+D94TAv}p9V_D5%k=nR0b4*~E)oRv<#|upiMk~z0GGmR=Yz-V5 ze^pq5HgIj2Au?HKwVD>qoJsnJx#u=RZ=|+Tk5lVmJ2z1#N=q3aw}vu8YK7c-N>4=y zwHEjdq-Iky;2wVdD3u7c7HAy@>636rQ}I+R6-Jq%%_eFi6$}s_rB+ajpcD*stEugP zo136*FtrWZo1wQ}7%h+r0@$R$MYWppE&yKBVk^ODoieQIXI-PMCWPv3^jr9p7*cDDu9q6%xx{?3;;b@n3omixrmwx*YNmZf9p3xm@i;8 zp?TpJjUB@J0D^@;Vq@WEgcj}}s2gf=U*-SLs=qz||El20$!O-RlsfnS_J9)6lK^rf z@F|+|fem;DctSVzuQ6lCs>g=*`}C{(m-TP#-`gM6ukSbXXY`l%AL#GuKiB_u|L6U` z^xwJVb4z_|(yht2X53nKYvZlGw+y#3Zk69U@CS95u-8E9*x%q${UiIw^e^w<+#lK> z-M_Ej)SuN~+27uOroXrU-Tp88`)^UVM&1epcn{s0b!+*p&9_2tnQmp>swD94ennAt zcir7`_tDR9d~W}I%Sf-0+(^%nvXRn}u#+RjBRxinMp7g0j<_@8_K4p{{5Im&i2f13 zj`+pr(-A+9_-Vw=5kHRjVZ`?%z8i6aJ1^|@`u}w?=l`!y{JYkcahKF7zYy(4XAHaLAh7>kswf;WDJ8 zodnW*&mk}LA4ATyzs;HS z&jMIk)X1SUY8WQ8mk8qz!5gX{ac?|#KNXah-`{R{t;jx;+arrw4mTM?C=b`)g9B|K zKbe$=Z!xqbc>xxr!#G3cIJ_43-sk>0XiMsaXE3e+56S@N-W&nebhy1GS=0t{!`!CB zeXl$`20SDCO)=z#yl@A)%foXM<_FJ&aY(!S?qN9ajLc&>wDpF%>BD`=97%ujZX|^{ zkUJb;(Bvllh3Ak$Tkm1o9O@S+z@h#=rtsbrEayd0}DguL&kx00m+ja=Bpt$)C)Jj(+GE#@N5{qN_YooPx`~Xe7HP3 z{%{$_+eqqQIN>I3Ngv^P)=&zdhx-v8M)G7X!|w&{r;s|*7v>g7Gy(!cXqP3lRov@8 zR1fWh=MwT9Zqok0{>Y@@?`{gwSN{7?L`gvE7m2*?lX6LUm1893w2Pdz9?n{^!(W2e zdWpaFl9b@u0BLprBcj#q)KgjW@7iqlGG5Yvz*k2E1b+8G7f(?i1&vA9XxDLyUk5nmBs6~80?xA;He-^DJ8RN^C1NybWMO6ExxOV&s>OP-SKlxQUu zNxCEtRJdwMgQQb(MDmQ}tmIiqujCEMHOY0!HkBMipnS7>{u``WKCv$?i#JtM9$^4u7g87d5nYqQ>kup*r>4Q>U zI$1hRI!8KRx>mYFs*@&5bEW0dI%&J~sPvTdy!1usRp|%PFQwl}f0q6xb;-PBD%k|t zY}tI-V%aj;YS{+aQ?dwIjLaxYk`>BoWsR~9*)iEk*+tn)va7OpWS_{smHjSrdP+V0 zJk_4#J?D9@_1xwe?HTK7@=Wl|@+|Uf_B`o%#`BWri=J_T=4`v|*&UBhl-L)Zv5p0%+J>@(~s_AL7X`wDx7eUJT&{SSMK z9pETV%t<)~r{X4Z^SBk<7A}m7;^H_fm&|2x`CJ88%QbUt++pq*cal5LUErSMUf^El zUgJLCKIVSme)FQdBwi!E`Us0Q z%p9T98WOazMw1pS4`!>y8fGSUh&Ik-O^&x{%~AT;IIAusHq0EYwdzPtZ?PI<%-T3( zf;Poyj0@2lgv1zcHAY2Q^wEZ}*a%}ZXpR=04ir-WpbZI&wOaLYTC*`MGSZl6h=r8Y z4d>%cq(*NDHzt{4!;(WH^yY|Ityyc*hFL*fHES(8GA!v5YmA7AiVce8e_;!6kC&7Z?Hyy8O0n%G}drq zY^2^A7ORi2YLl!XIxW$Sg>0fe(yD_8(T0#%Z4_w&Inczd&{N0@YP37MFWzF+MkX06M(8q>71~9GMQF*2ge2%AwMG*R7f)W-5CO{_W(pxQ1Gtd{5P-01VNw=dm{|+^ z6%j+0-eT37Lc+r$ViLp5kx^l=IKzeEl&qvF4E7NA%LH2ey@o@10m4vTyAQN~fSq7A zx?gWNFHF`H8*d3AI~%7r4CUPWFH{<1gk*m_30u(tfF`iWB#nqQTC}hv2E8F#m?SuDFTQn3UEkkc8@TWC!-F{GC^ww z>q*$~q;*EKK82V{VgW}(B4CfL)4q56 z4)D)xH0hF~^)O1fFcUYy3iJruY7hufKutIFVd8R^gr`Ecp*I_TDL24)U$r5ORbRg-pCjNXR?8@hRjlg!)^B z(D!dOu%iM74)q`)qGOHW+C($Zqs|&;iLn3^gGC89>$Oo4U_&EF=f-R>g=zQ41JxU% z^ai~(IaX`22o=$0BPn|0z*CK8 zK%DqkW2^;?Z85-a0Z6ni9$1JOKmq#-j|FR7G;j-Zd_)ZF6-)}K?p{V%Lg*B4TBUeba0p4h(`{lkhnUa;!S@mlEwb3uRAAna%X|R34lqnNUbFX_%$pF{0bXxjWdRmGt^CFZcG*MWq&*% zpD-JDPJjsSWiSA$4WFQ~!(L z(g@%$q;&`!M=`(;0H;FcJiPEeUTy)bGXu%#O;$^MxH}UvXTe-kd`b#g8@(3xP*30x znc%M+5eqCjy*4&-n6xnX2oC%!5s^Uj?t@SuO@S=#uW(bx z{WX6b2|^FDjXG;w?7RqzWiB8Wa4|QJBTGftngtFZz*C@qy(Q$Y1K?iO@DUL*ch+1% z9wK1j&>$1McLEb&Zk8+5#cF{jf&aTxfx3yPAYib-S%s<1oju2WfRYkWB~Tuak9)I+ z(-1(skh!xT*2bHo!{JN-dNJ<8yjM5m zG60rH7zk-~uZGNixK`kLe=CruA#>*j!96b-j;Z)?t?(j4`6Spia^GJE{4Ojx680Zt zNWe8%t069;H$XAk92OS^LR}2VREDV856=$Q!%mO|6<}C_6UCa{zd}W<5upDiblg`Y z4Cvl7f*bc0-6U;-JxByu&zNWdaxxqBk$}(fNs-__0UlzBNj3priZ@%}*dQl4?7A@u zxFO-}z(C>X2fTOs4u7+;J0*%HiJsMQxqoBiu59bC{I)* zIwpEv)GK;ZbY1kl=qJ%1q5%)ugY$R_l;6D`VIDej?~k_t(Uq#ab(*CcOB-jjSFxlRYtLG(g8nl{qO zbOHT5{ZCLqIVOM^&rD@zGV_^TOav3dn3%)Nr_5K(_smbsZ;XR+Nxh{3(y`L%(je&q z=^E)esaBdKO_%0LE2WLn1JX|EJJNqkKa+kfy&=6R{Z;m$EI>A1Hd!`RHd8iFwn+Af zOe@pN;$&u7o$Qe8lVqKiD_fkJ-=Jui1W386V`Pb1S)E zZZ{Xs={O@7&!utMTpf3Udy%`wead~q-Q@bYKfGjKDz6z{L0&7o9`}0EYlm03m(I)J zmEe`?mG4#O)#laVb=0fN>w?#dUN3vS=Jl4>2VS3feeLyw*Uw(Rc{#l9deh#V_egJz z_ayH*-iy4Kd2jIE?ESR2*4ylzxhxHlZ~0u+4bSNe2Avwqk&^$DHRv=KS#CD3;S~8SQm|;x zN%uXOg<%H!6sOWpT07MECb~&~iaal%Kr~kA@W=0ly z{t+$Uxdi~XHN7!e%}J9R(_7UXGlAu{@LgPTdU`T9mC4D=%h61g=2Yj|)i)V?b+ui? zE#uW(1@DS-MfI`{o?I@T&abi;)~M_?7x@=n*uipt?Z;r>c-GlBp66Pcnp(J_b~W~k zJU4;W8IE;z9Xr-_5FpZ3`8gH2s@$By{Co|!66RIRN3*C1^>ST?V>+@U!LTF2up`?- zL$|?lw4^nqr~{nKnUu7&6b%lRrZlCsr~{Z@h76@~^htykcl!R`V4$yrCB3Hbq$wn746_@NOa-3Klzp2l^gn2VQjbAuo0?#JQLL z$Mz}bSE*b<%<3&$R%={A(pBfD{9}jO88R43TRRf@j!umu(~;H5a&uR%M853YmDj$} zIQyjET)Xy-no~>!4446Ue9XYDW$(ym^9NXsBiI!j&bBmH*VjYd5uCtsQXS7>`8HO> zDbN}`0?ouLy46Rz8=vn%p8Uqm@ezB}D0m6pght^=)w6thX?kgz2G3qG5zoOZl-P#$ z;62Eu9_V9|U>i5{jy^LBsJUYYou6NrldH_F$f?R#6Z}L^@PMpQjwrgSs={8Q zoOChE&E(fDVqJZ+_^S(9K%?|z4Qv@&$Gd6owP0l%>_y%&IxVx)7#jOLcGPC4#d!g42=Yrv!#JYwQRKph}ax;`_tIz`20);H(1 zsJH++i<8d1wvyoE7px2R-tQK>V~5{WU|KHT4=~~?>;J-zTfD!37u?D8Q>s%Z8#$yy z%h5wD_x>xdywB+ughWP$WMyPzRwT*3=TpiXGn-0FZKbMbDvnhisqR1g!-dcPCCh&K zU-?&5z+T@$$>=nPF5$IkC4LdF#0#)`=@RwFOYj1u#w%4&w-#zI;XGu*dusADPKoOm z8YZ0Itm0}4+W;2`1!=edNfwuq23(9Y^AiBwidZ$*g5O$1LZ$6+E(!Uc|#A>nDKry|{>zcC#+K%kF13+aeB` z9VD9p6UpVd$^V7B9CH{zE9`mIIchS3J(9JvNG|5m;2dy7E#^4~49g)Y8pA2@Lg!dK zg2BOf!)Nnef3=~Zrna)izq+0-OJ%Z4GBT8|Rd_LG9C|4SxZ~=3jfW$p9$pYw$y_dg z$>JhlV>uJMiW^X%#R@E9a470Q>roqx9zaWQErSDbk~yp(uQ0DT&%cNvuP5iE^LQ+u z26PNWna=x2;dpDwYtF2PX<;eXb5R_ zZZpZ*jjdH0&h{xRQ82^3_v)+fai0dznTkb#fpNA>TZj!$wMBp(y(a5G+OcF=O-IX7 zI1yn7^P5|gEmh6+^=fi-zRxzcYPfTi=c-TFqDL>HS)ZW?kxW)_xu>W{<;ZnRKUuRK|0& z{yIfL1XJ`OLv>qeQ+d6Ac^h59pu}O!d{)1 zv*gVuu9H;FWrMuddxQ0v#UA3Pz#$I+SM%g3Mhc$GgAw6?7&+-zJQ9zbG>QEFIth(L zBY*uBja2)zlewX3ESktVZS|5(mkM&oHz$Xv$b>E&ZkH^c3ZkKeyP{@`J>81Zl|K725KKL~og7cTUw&+r2C zUk9>oB)d(Z#5JNP*mUmDq4TywX6_8%+DKj@yYsN}P;F;x zs~Sy06X}*#uDQ7i4t1y4@e^&gBNN(#@|4_eym;lN^{dj7Q_?EUGMmj-qU3N8NR(vr zL5@U0AW!DyaDfW~n7L>qoU7ycb%~=uC}_($bO;~RAg|+gl_}Tm%SPM9pFM`C+p(U`f$Ogj39`p#D49F9Oe2B)Y(1=eW zw)bneg>cL|gV(T-@p*5{tE=Jcu_#{Qxp*GXIvt3kkYHpQ3rMZzl>31_u>s6-4t1k$ z+%4rq9}T342VUdi$!t^dQ!_JRmu7%?geCz#$k7y78#|!3og3_v;<;Rny}YW5!%{qk zYr=}g#4>emYj$g9vy8LVs?h8`L_|TiBLNz~6T}mIn`7Q#x%%eXmYM^ywlbt>Y*KQW ztPgGNM5|#@Lho##(bo(L9oRr~qe#cANDc%f=kjIw`MHHTDlBJG(mA{ekB4g&=UR+@ z#y>k2b08anAWukZCeRZa(ch0ofCOX(Es0wN+K`%qt+#QuZ7_-y0m}#2?n`dsD*wD% zU9TxGD=jNm!ZzETgs?z(%&2dH6S29assTs?*$2o*DW}7G$(=zkCn=n0K=g91j%PTP zO^O&KdH%vD8V)3XPz7L>;2B8w07~qv;%G|;IoyGV`0yOvTG|Z!pBsQ#a448*<@V{7 zdf2gEhBIedl9SbV5}wF0Z(rH8R)gfF3J%|GPxzE<#INuQA;=Fuj>54gr^1)E;a_nA zo)4mW8(@oc8NVA2@UCNk;D%})%w{#z2H@ok=K_g?v+@cKVge`%egi3pAfR$7s)V8% zDeAC@I!=iS?|Kv_iSmi9WFEB;;){P5Rf%dKM4(>OC~6j+5}g+P=`qz~g~xw9Zi~l? z6U67mcO<+dT5?YEC%uhsrC(z|gAE zO*vJ0Soy8esY(oZgqQLER6n4etX{4*s1K;GsNYi~jhAMuW{;*_b1QI4;QGKH$2>CT zA7i<(=f?Sr+dQskyn1}e_?r{PPpF*GHsRt#zlr~zR50n=$@LGNnX+igA5%|F+cqs@ z+S}6~n7(}aZ!^p@%4hsObLz||W*(ijYF6oN$QX$5KDr7zAHmywn^DlpJ_O|_m=Lh-A{Et-MyoGSNERokiok) zBnhB3NFqWKByj{Ii5OXtL=iv-I)VcRzH|jku>?yL&Y*4VU{JsS#rOmaeBcup%p(vg z?BW3W4M&OsA3!q@+*i8Vuj{V(uR|WXD@)op>iqEmJe@|bq0uaUO$x21Z|quaWJ_xUXAmZ_~hhx4bGFsw0wse^@d)0B zL-DjAP%gua%Yc&7*ptG~HMb>n%yYV^Ir+quNu8Y~X zOsAO}fxX6IZ{=QTe4}1~-O+ORpvERWcIMrGol^hUixhq6Nu^Kwy$j!Uz@hXT4-9Ss z-^eat$rCh}7lHN*%g%HL&}$Su8|+c)fPpL~YD3OWLx-U)QRDO)^r8pth-2Z11unc6 zgng%-ae6tu=(e_wW5-~S1W_f(E39}MY+<0HH}t}`?3|LK9Q9xyw$l+A#;7pmon0@m z&K*)1ESq+ndV%!`g!5xSUcduLyEub)22bZfY4K@?Qx%R1r~Nu#$Db%*0|u7If<;f- zZs~|Wl!(S*4>TT2kOs?S>p%Q{+3%`Sh&B5C`;XrEP=ho`23o%ajYA%X+By!lcghCs z(t*>G`3tf5iS25v9E+7>u>TlY=(eddSF1{x5@z+(?=Ec9VE;d`68_zm&3^yMUl5~Q z0Git}{%n4T8P1e5L>?Gep2ptkLk#cJzMcm|(|{by6<_nIywA5V(E)G8Gcom+3bm`G z563%p(Fbx;4q8>~c*j#Xi_WWWENE06tM5GgA^R;KAldIYrnu%>=<-IpTt0YLpJO5Z z7ka_5=ykNkF$!&QjdCo4<9+{Y{}-4YM?Pfn-Sr?2iLE?(P=OM*pd0w2DX66fl@N?-1iD^%I(}!F>Y{#DE3uA#DGd2hEe5<#MzbG*8eJ9rAVS*a7>X z{S`8p!61R*K0CV=3?EN|rl+Y>-AblM$u#nWsCFL|0B zfQG|)pZ4~I6JVA_-Cz?4mQ3W`hJitlTLhF*gLObK6@qDS+lA0x(4E2J0agpr&cu^; zCO{MD_+OBcSu~yntMX9y*I=$xBgAa|S3PuJ@wbLP?TrDFLn7oI!1w?W6b|fFfXJWR zs>T5*;3zvdesBW5jGjNr;s6}*4v+5OI|y>`@(7+gbxs`u84}+uPY@vw00iu76xufo z;xcky3)%Z&;>+Yhm+!$8%J?!scS9CB;mhtZ2z){+m9XdqJo!a-xeFw$i9EJ~O~`HB z##U^V3ifpbIY!5;!OjkR*D9R>68VYgd@_*MUtkE$$-fkUxcc07c}E{~7;XvDpX)Cb|1|XFuvZq>JsB#)PveQe{;jxBiN^8{5K0jUrRqVzDg~18#Ciz@>FQUv zymy! z&*Od810Fl&u{>a&NYRqnoKmjF>yBohOh1`&!vECeGZ#-?l2ulhSKE~}#We+0>ac&U zetlbytST=DEOI$HMPT2?V*?FMarLpa{zkN(ZYfS}NLFDp%px@Hdbg?*+HWKXULd8 zkEK16c|6zUdZ=x9l%!V#N--vs)1Y?7`7@ zUn0ko6}wEv0^s#bf$8Y;nt{g#G6c;O9Rxkp~37xp$cQT7Cj!TNVhT`^& zI&4Hw_&KKS_Q{rzgsVT3nbUxjS!=s=ByFFeTQM)>Kqhz5aopk1G=ntHm(bZMG8dQ$BhNn1}_Fh1}7Nti)0c zsT@ogRyZ#PtP12$h;{@IwrJG15JZTZim@zu2-s#H3a(^DF9b*f!~-`SXB4TWX_;v% zT*RcM)i;-FDx{sz1Pp>3(E_#;_tAw?r_B|uIG=Ss?X=o8Z{QexDBE<7`o%{7?Ua9oUL)qyK{_Ai_VIOP#S7N&Z?ckpe>SiZNU9u zm_q=i4bJZ5(sVGj!PB!f7mo=XL{82L5inMgk&7V{T*SK~8Nwgw=%`(Z+g00lwVjUA zU=<3WUD{k?Dq6tekKu^y$hJ1`S7AGt=)v}92iHh2woB0rmiQX{&w_)RM|6e?WpRxG1qwgX1Z!msyPF7Ub7d7P6Vlc}3fyKQX z{8za}`FR?A4PT@4^9plwl!99goGkcu9*=ILU}-~rO?{;X|K@0ah;2_8fQ@>SAE*Hu zm0Ehb1*Q3A1^#G9oZ@s=Z~7@U&T;h6C(|Pi z>r_B2x`_Sz(lt28)kCN2v$jPmT?xPQJ9rqtDh3Y{nDII?+Y{^5u5Q$qRByH=X89*( zW+qsbz#re{>&mNY!JH4q<+i%|_71QcjvmY20Be`s_Y9ba=Ca)^9*q@#$RFGQTd(6C zD%WBR767mVjOD@V9ovsqp^2K>2HSzmI?N+AtVd2c@Vk*_I(IXT8ZbX?y>VB zUjx`hNA3vvLF4-_R%7+suyd>U8$5c5_dOFpf9J3&TGE@)C^juSC%r(E5|OF3M9T2A z8F=ALyha5M-v?g!X1a!$w-VTSu>AxDq`vRwfu|HHXh4~0-SQeQgF!}1ZYz~VPn9c zflBaRv=`n3Qn*Usc#Ek45eF0^LSR7lb6Mh?HnDpSg`cyk1F(JR%Ob?7Vgyf{qpy_(zgvuS>Vj=cLo{pa z>7>`QufDBBFQFGv3;F@B7jX-I>9Oo}NgLE_GwF{*7W7V4osfp`C!~n`D{ zw)N2Ge`)&ziIhHfGEX#uH_&MpKf(LB?vesIuAl_mzgzL^#-FF3QCH;Vl;)~*24l45 z5hQEJ5XpdL?T;vL1Qt`RP}9%>a6BA^|X!|NjdB_-jxI_CZ_l=Idxa zYiv&H$kZH3Ka|;-Ec<2Ut6=@}QDUDhSUP#7+LCO}G^NX|nW;%eh5%56KxP0ZU4iv*KA7w1xTwa7;q_g#*D8$PI$hF$~8E;@fbZi2er?M%mste&UVe zXw>l^U;pv=3AlcEd7Zho235`~JX|gRb zKMD8VG5SSkg(gI)?#yI@*VMn7sL4H8YOkr6)!UoP8&pmwgM1I4LNhLF(2)Uk4S`SY@Fxs`Oc(;0h69>rvKnWwBS-<;xgEr(x6DibxmxA2GpmIW%yoQloTB&TirQB-&)3iy;JKCM^{C2fZQ!-8vmGcos@_>` zs?06jUahZ9ZjxoybQv>rMOIl>wlW*yIdawc z1=gI%9Q>fsugF}o-=uuC4DGI?OOHNR`nu}nH;VJ$(-gdSwdhq6NdZ#d`u?6~~Z{9B`t z1-wD7iVv{1TrJ$)^S%f-D(W5jPFReasvb;xyJU+{ge@XLF!sW1Y>t#pxHf&n1 zT#>nH|1Pz8XL!_BlgzYrRr(xN=QBka^;w~<(os*A)DqVV3{f`x~wu*<2rlCTY(;`{I>jL zIg(cYQuReK+EM8DP0?Fb7i+$1ey6Rcv#0a&>5I>wJl%P&@mbk{muvs|59Qaf*EhbW z_U+#I{v1%Pj(mLjABWnTWxgjboH*Xqepc3gw(i1Z<%PWN^t0;pv+-Sq_cH?QCUG% zdPQ{U<|=F`!^+a9%Ut<>^NXIy4^bDT=A~pM$7FvlUt%w-s(;S!0?Is#=3GHno8CWo>lpI)FKe$jT79zST+OkX zwj*_?YR}i6x1XsyQCHPo(E_mQ%IeFS(o1y3!G*H?$*YP&RM{3=S)>NP*O)ZkUffX9 zT;l&u;qy61(`3n|nI*aE+#T^)mAc-5XO|S1md4@P{+a8x;&v0(YMUovWmkUrJ&Pu zXoQi+mlzyVO8Y8*2502splvA@57<9pE;b(RGHHC@z@yN7Q&))11UB+fcs{K&H5xCf zKDlFG%!H&Hbw@N1lr{f|?xO7oSi+$#0O~rDel$eo146*S?V*`hq6(0H%NP%`pACJIXr6*_&%wUIKAOx$>g;p&(WnhH6fYKMq71sza*elGHFyzT zNPIVF5n6Pb9n8$&3wSgMoXv3B$C6Mh1fewGk~#e>zp;A#;b65xG}uIkv|TbiuX_H{ zk&Epb2jy&{55H9X#uX)4CZOX@#Zq2#rw<$&plbvIOi;aXCP=0bJUn3c-RxUQ+%1X* z{>fL~SNpafs_Cq6Q#Z8rzSI7;tgaj)tW-6%1zF{q_Q!hHHYCdG6KgDHrSE2tnfv2@ z*#3!n`zLrG>Rg06WEV2S+hbHQ5ecCgnnkz+d`6wy7t4G@cPx&bJ`uY72A&*2kiR() z6bXoV6U+i~@qib)t=M{V>dOo`ML-S4(`fXOqhDdqDM`!8!N1|({Bm;AN^(==Jist4j@u&|VHkfH@Du$@Qy2AQ$ zyS=B!4Apu-Qm z??=AR!Q1>cw5nx=g{6hW@|2gSS+|amKUv#qsXH{+_oKfB=iXcIlJfGBa)=elxEVFOi~iUHd&I=pcASXucdT%& zI1%%L?ZgRx=S$9)Xz&P5Vg--jbHH8UD3D7bnD#I%oeT0z8Q3~q@{90U0|W>Iq7TOh z1NXBNgAP&M96-(t7<7ax5CV`lsF`;0Kr{)mF%V-31dg>2)dn!v5Y0Px-e3)^bLR_u zAk-tD0EPi=Wb4oq5)tMOdh~ZfmOf-|vv(;;YY^!I0+^8?SJRo`dC@ukP#kZu9gS@X z7R zCS-&8Ac`H_`5nyExf3wSe-KjId?+zTryShb!;;qltDAkOl@Z$Z084;cCoF^bIV@Ee zi3{;N-Umb2864mq;zq|m6=t(Nu}cM>#x8r?A+v@+MLw**Gn*WdKniw(tq8euTdsi8Zq0W~rrMOat z%m0Qa9T0xxB&|C-8&94BV}cy@fj6lSv`8TpH^P5~fbH1MJPwr1O5YI>fq5L>0N%zO zpw)L380LDgt&xsGhe10dgc}3xt5^u(a<_ofE8Q_ik&>4J5mvKj)0vr&g(IvQf*&EM z=Wz@dRD$rSN=YG=v%iJN&b$_g?5u8v$WA1*LC~f?kA!H=1=V$Z2@4m*i z!)jf11|vI|n8CTKI0gr=6lqxSh(fRxsD;zUZFwYAz1w8iX;p%+pFb`A>8H=%KcT*I z^vK~Cl@~X6uZ!LX%cM?9PfXsuNtT-rdYCFNudJd#gZ+NZs4Z-@H~OP-Um>6O(8DSS zoDRl3UI$DI2g5tT@K!iGt*{MN6a;gygZes?bp@Y!A_yRcap%RV1Aj6_&7Kx;2d?wJhEtaB~olpbt#z|334}xAjCm}zo^*y)xKLutVI8W?{JDyFB1Q@ zZ_8I|ht9Q2;aCbEKK)ESZ-CDnes(Q&ErZV-ejfVF;b+G(wNC)OE>Uz9__G-Nz3=RO zZ6z2L7<36;qB{jz2UcO}R4@MkgsPa&d5c9es2Nn#RuU84VO2XdgMo>XE1Z^x!2y&xJLkH-3zbN3m%kH8KljihAJNb-ug>0nsnuBd*6X?d6;)zd+r*T zW2CS(mmnq)+H`6@{E%?I6J&tp0rb`DATh%L%b^w|O)E&6u#ND-5T68qh?oB|I~X|p z2@cFJ@H7ifZHSfthPe--wSjaqP6Yd#K)hyrfmUFjYbnTCJU^_5+x3N53hR# z%hh$(x|pT}S$1`GUZbk5zWG3NVQWdVrl`BPyIbklk4}H?SP7qr0PoF%gUtaaGMsqM zLWgx1?>y+dy%z!%qyh8|Q3L#d1ncPA3r`1b?*eB7@SU5^Ai{UTK*kTiV-(5hX({SM zd~#Y-s|GzOZEb1-=Sncs(wLU4DMm9C=_P4d;9uOpB&F3gYEqmc8a&F?73#_=d%0bO zOpM)LR8XaQxY8$jL6_Ykc&_$lHY{ri9Qr?lgOz-=rM)PkfMXZbcU8L&C61U zPD*?Y2U(X+x>f4h?fglZc;v8 z4XQz@C<#qQf2!cj1MkmH#g|cl&Gf^j-P?oJ;GFSuJ$4<3t(D<3({U9}#P2J0<+>`p zx+3xLwwx_^=b~}Sgz9{Iih9qH1F>&>{Td2=L3RG-`qbw&u{VB6y{SUe(A4wqAe9D; z`f9Wr?Y)Yw${Ma#zj>8d_#v(fJp@s(pg{&fWG{s1xT8FPC^iG04cu0s8#oI-dO3!C z)ukmxrS$QQT{BkW8dtF1<*URuP!?W^j$vPQNohq19dkwZ{d=g!5q!$w3*la{n*$Ow zUgQWyI(rdKs&+03P}IdMxon^wJ+EegJG^7B0Xxyc%CLKZ^bQ;6Uhr6Dl5U z*PMIqT+i`;$Qlk-w;v`8L*z602~b(lJVNvDvqSXW2=x9Z55$h2lomT!MMg4@`|!bbNtJ)t8(lGj!JyO57)!Bt(Pt>F0vKDH>o6MXX+Gi=;uJYQV7SX zDF7jBiywIBDywp93TsRJOKtE~7}!oUH*Z3GK79S*zYT3e^>CeVRgw<&V*iqIh%Zr9 zSC>^(g0^$Bwx+V7sNNq3IoG3kXx`16S5eTqtNx(10=0Et1*sM6Fn;`rt0#cl1;ImD zSRpS5K1Zw^3dHeOM zu@muwpA$d5brnd044QhC_)A~aod2Qw`&c>N|F)9h5%!0F8W~ zOX7qE><;<;HLE}y1wH9Hs3Sy80@-H}q@3Y{UXUS<^Hw5*49O3md?gc|=`UFU{A{4D zfsjB9Qhx~vM5zLGEd^u)kVD*p1(97&Lo5)Q4r>Qeb258EQC(D1Sf$265MffCpAA7} zu0Bx7gPCP)Q$bU99Yk<~t)Ve9xh6@Kl$@ImT2Y@%PG@Hoq@^K<+=iYnHXFSjIS=0spgd563i}N>f zk6XpVsBFQsxjg;O?JtUpi3k7a-Q)VbjFxT zvu)6pLrfF{lxH+gg0LQH5P-V>h`o9|_GVmVuA$1Ut2S;}6C%w{$x2C4(R#2LTireA zGXTz?AH*3;N=>Ee2jA~L^BMn|dECX&Z;-VqG#0AMi!9bMen9!STMt!W*k*AJ@r}uQ zOwxJ#0$W;D`|_L0>bXB)X}$J3c{4?dR8nb)ib(I>Bhm|}!`AHMjyMjLHP^%~-Mo6` zw)brZ^7oZWu@o)zM-Yj0asEV>kgepk&VHgHWG&VNHI`!fX8XTrvGZR*G;ak; z_W2{SfrA;dl|CgNoxWurPdk&P60(Nu^~V4|r@17&e~&0W^3bDNU~(%E9)-op%uY-c z!!*o*9Hxl@^o{X&85^7#&^;#N47#r>34Hv6m?MO%%Dp&A&K~$gK==z0Z!KOreIzYJ zA#wr=C8jcPn25upDggj}Cvm6@vF=Xfc`&lY418P3?p#c^TJ*y6+{M}Iawy-Ig>1DK zY~u>H*|&zM-k0?pe*4j*+qWO>+>w@4$0gOJ?bxYe?;qVB-jj3QZPzMy(gsqpp^5YA zFX&!-O}Fjd=*mbQYb6XH(N}FJ(GedN384c>e;Q10bUcFbZU6}(KwzBws*Q6FYaiCZ zZ#>h|a>fHt=4mJiy?OObZ6j8`8bz?L28{2 zw?jE)-rUJk=AOM;r}^|8;JYqI*Z+LN$?fbzkl5X$ltsyf3BcYCtWMdHv^{aV?~eVu z_U_y-&9MQ@s@g$iq|>$<&YF(d2q6oj0kB)y(C~t={B60uI#4%?j0yP(YC21tkd&N| z!6z;?Xbnq3Q^JzN5~<{SpB&GQAwU;D7aGMQZ2-R`&61Xr&NZyxwPDBF#4vqW>NfgX zxDR65@rf!rQ<9LESY+hLz;MUbg3zK+-;i~|8$#AgK|X~5LkN-i*M)PyeIgfQ&ov|Y zKxE(5B-QHcQhlqzLP;5J54mbj=OuLx1%qt?^bw&`B{My_)@>-2gp*gR(Pz9{PZ%WcbGeJfMYUJa}R{xq( z!4Wm+0@+>hv3$}5nLGtwdB2d)!dJ|$Z2BieX4oF0#rORpS2BDwoUT1t*y&<5l|L z6PbO#Ve63PCayBPXnBxIzSa7(#u8(Wjs~D}bToL~v?1%ZN$GZW z!(kqL9+nsmT)E>$aPm%m1+I3V)#N2Ly7HrVueeoKd$91>F;#VDO?nmAaHRC?IaN1U zZ&vTC^W|P??H8 zt(!nK+>8$!$*cVzZrvGPA673t_b$aqj8zAT<+D#>a3p8$?kzvX?;}qU@g5?BC5kU9 zNte%;U|{64t-UaPaW-@T5p?cToA-<*J~B<&ohWw)w!cW5@;|KTS&P zdM@^C&=Jm7WvQuF;Sk3XkA)rN%thJ7MXHv_mUYKCt3-bAB$=I!*|QU!uBKhZbP#=E z{Sx{zpByqec&nOX;AWqEGK|~B`?q~EWY@agEBCD0xAy$>Ep+Iw{iNP-%OAfs{d|!=I z%ex;^FJ#^vx*H}$k2uZ0HJ)?}>4_CsabMZA&Jc#Ys@R)F(Rw9Lnly(JKiTo73>MNq zq;8P#^nSs+0)*yGh>sxm?VNs(q>+3~)5-AR<@jg7zvM1>+fC`5PU709ONw3o%D0y+ z7|mswByTJ^_0cCMPF%l!bkVeIUby+#Unxi=_cmXCea8A#Yhts;gSNn2s#9Pz3USvXoF>* z1qz5+X8?tr|2n`1gQ*WEI3#r%uqSZ+d-PuzdxCevO7{WvelUFa4`d{OX2>D4?1)DchD@fD zkx%dkAp|kmQ5vKI{Ml#3kIgO2u;~m?lEMpM-UP%pX}gRT#qSnQ+qz-D6$q_np!we% z#v?kG2bBWvH=AG#w*FfNQ__W`u+YjV21KEFU3k~oQ%RRJQ(xlui|RfS2y{pT?e^Yl zoa-{#q3lO}fkjxdhI{XB1CWzLfSViu(}yU&meJ<>;tZL)HC{G=GR2dFGCGgM(hcOp zc<#XBrr@#!>B(h9OJ=BM1i{H1Fk=7*NWK%0{1(am0WAXt1hurZ6dgNxgexm*+I8T# zlzdnWQp*O$sKYg~>3mgubySt5{$3Fhd@G5fmb|miIhNGRb505zc}JO(V|1k3puUlv zVK8KvQ|##wWHRMgrSb{-)fbf+_Ed`@!;qN;Vuv*?H#5f~&5~GivT_Y}>8uM%b55o; z-2&{m$(U)(uo!Ha)=Zn(Y?0OnDswC*yTN9#rXh)#k(r%lO}85C#+)1}!T?>BW?Q-) z$N&gO7?C!&r8$gJd2c<)gch?+dfA|~r&?1?TuPcDJ&%jV_J>m7EhjX#&CG}$0P zV@ffmr)Q^Sg970&18-w9*`%(;t~pG_3l3q!?yMtxnd!T?G&{m;R=oLg7VQ$ITGp7= z0HX<~kKqLViyF`ZX25vy#L&qLUWauretq((&qI0l`2SD>mMinB4LhRCn7V~eVN$Fu zP8}EPK`3b5+K*vxxV7R}@zhr)XmR%Is!M9}cy4h%WV1ykvRAQnh@pe{fv& z4*p=(dxuqWYvqlw>o-&+{ZrCN-X*Vc=MP?M_+-0u_wDcZ{HT^2{IRNumXT-n?|1B1 z=UB5$IlSCH!4a1o75#4VyDL-+@C;qngg&E|n?r_%!H$Fxa>!;Y#Q zJ9
    g6hQci^?554dATb{-)j(lvyL)qjwGIrcmNyA&2j9QlLX#>zGk0YGw8Y0t7} z+PSpKrBzXR^BU&X&u^5LYzx}8W!6yo_5yY2rrM%#o=*P_5TfpV$aHB!P1v68r^wsi zT~yTvH^kL(o6l@H7j!ncBI0PIU5a>aR+@U_l(_iK{L;vv`C;!$gXTofeoHlI-^ltA zT-B`Yb9QUn=r{!HR+Diroen%7dND$}<<__Be^h^bp}gTdf2j6ML*-FvabwA+ds(pZ zfy~tgkh^zYV6#uF7?F{H%UG1<8ZSdFz){i9u6Ud{1>I7Ua+C0nKW(N#L#O8VmTb*iYcu)G-VbL#WM zVB#}Tnp{>JQ?dU;^5Q{tb#;WkoZk^g`b@ONNX>?@cw$|lV z&JBAfW_sGk2aaE^xi)jdl+Z~D(#vy3?jNKE2l!>$n@$b0gjsPmDvM|;F6?1sv2^RQ zIPGi|?RvKFzvprb%}a_`)ksZQMw5yTAzf$>(l?k(3k}H#QAb9ZEm3?k?uKUuk(V;1 z0kjJRW^{l$G%VY)jeiZi*l`QV47KnB`AX0W7+4Y>~o`MOdo|%T7~g ztikuX2)V9J2nk6(w;zD`)Jvp^Mu}>^E~ZbSS; z*Zo|tkcpTS>s^~L9X82BTR}R4cv3St*PGj)R#a0_X1e$m*diS>$m?OMsKW65c8;8T z2qltca@XV1dl(1Eoof*~XJi8x{H;z{FSP9exv)nilVk%B2LX|SCB|DoZk;N_`j5Ha zfm4p+ZCKVh;WeoWp z!RedSOtNVSZX+jr6)3EAuWfXHB@Hz1 z*tT1Z%x77N9dMLF)@rHLlYr?8v#Bd{f!E2LX(Zsj_iYzfEdpHoG0XPApRP0j%oYmH zH372)r{QV58!G6OWQY(cDz%mumZ_c9;s(E!38L{r&g!da&(FCyXaHh zTSq6V+pEPB-a39%*a-$kimsk%@VZH>T5DAQEB)a1F&9uXUySp`T0k{@LV^lE`2 z)43IDw=N!0st66~CZ0kgZqupf=+wI-NWS?J>DKd`AvZoHk~h9?2HX3Y1LW5basVP9 zQ)yo**yCs^M#IQ5Nb|UVQ_>=`oZ5(p+IL7vwS?Gr5E~-s_*B}>pE|w<1xf*0YgcA) zb+^h|zWy3{CmmLekB({(b8c4RO;#JZO1@Pg9MStcc@vM`bLbNKZ5zFcKtUEbn>}!p zZGeE@CEuw?1bqojhSYJ^d`n@WYLZO8n}rw>Es0jd(eU;o`W^ijy-SPeHf|?YHBcUY z)exx$>suGuI|zWULPQ5 zbC$6U(!zYx@m+ZgR#f1G@P}<;3-h&yRYcXMlR3+L7SdU1o=tqqqPM5j+R3bwK1b*r zTUdEiU7Bxg`gVI+Ir1)?57IN7D50=CwOnnpXJ^~^T6;x>t@a3+<3naGME9|wFZ*d} zwF}8CA2R1it*xTMUh8Y~{4{B|)9fZ5g4hilQ#msrtNTrC5pzoQab;fOx*LftZPakKsXgDT($l>er~IP`$3R?+c;=JLVI z1J`U^Bi$S_ZTK?gH^FH_7yfoXFF)82agksD$D=KztGZQI*;IJI@}88uA%@nc6z-8f z&wl1HB8TrijVRaR_cE(h9`ZU)Kc*b{p2ZNI8;4W}8t*dcC_(EXhsv|dEoI#5YTenx zsv28OK_w^O`g&kP^nnjl4MiVR*0AxII_LbAPcB~g7-E`YdF1Pt2Yg5rs{7X(Zf!qC zMY;m6Kv$qEifCN8Z$7x-8rmP{Gw&kZa0ST8=C{0gFle| zICm8pPgQEhS_q(TthBExUc+O2aIMH-yl~)+Nh$kX_>Gp;g=;G}NYP;~* zEaC8zOa>91Zz8H*jAQmxTSL=B{HoWhEVq`3j^3St>Nh80zDn|K)IayU%^FdLA`hx?}fepwKVnEe6z~QsH)z!SEtlSJ~ z$L9`@rw}qxSe0ZZ?E;f?u94fn1iwd}5N|Rj@NzO|L*?4S)fSvu3Gv4ONTGAbVL)UE zVz_0J;x()6E7kOk0N60YsEUkV_2XRrgJ6v5MkzYe7;<~sG8Ju>u%5nx=sX((KqW6X zJ*c|K?fawt5$WoQPW;bH1;di#y$@)YrIV1;kJTEJ}_u) z^m6s)mBkg?JU@AF6T54s&A#|ChY@*a`T(j>4+y$;YdaAgt1jTH3#tpMicU7-E@_sw zwtRo}k*Yx=|D?&OK*%B|6xm<}E=lxPfoPLg3Koi|I5P6v=niqTW1OA}YTNLTi@3Pq z!DSVGiT8Rc*ojLFcL;vzvf1T9JAemRW@W%KrRN}jqujjEH*af_w`GD! zLeWhkmhC`eN@d85;c?QJO>>Spt9L=(xV;sbuabP_HIL-T` zC2wooCJCsBb3KFN>7F(FNn0GrJWYBNxzRy1Ao~`Vm6sMD#;yUR^Pr-vx<5;^t9Fw< zI15L}l*a2fQ>s4LQRg^Pk$WPtf=C_mo3HHFuhz)F#S_`?E>q^)kyOga&vaxYrby+# z;A4ov=A;=x&dA6}sf!Pci8V`eO=0obsuV*~R$5A`K0i7>Cp}STPfo~Biip)0Cudmo z$>}+e)=SGUXBQ+}Oj3g}Bg3G!Ch8MXQj=44shP%@*rc$AG--C$W>YqAPO@%_EKIhh z@5s#0EHGuI79_?S^YwPAr+a!^9Ng!4z21^pnvt5DWXd!o13qs{%-b3pZT6xJ;U2$c+|=1hQhFf@a#}&RNS@GeU3Vl8w=o zIr*lH%*;$6$AWqWc~JfQB5#5|kBoKt4C zLEIt9o(T-WI!k%AJ-0R^*MN2g9M|Wk7wF@Y?WV>QL!#7Xu{v_q4wE@D$50ejb1cUg zW8V#AlRYy(JdqtZV~;*RIXfZ>Qpa)SiShVk+HQSHat1K=2?^2Jv1Yp|LTAii+5*N@ zW3pLqNG`QHwxpRVEu~o%Y2Fr!43)Ura%|<9He*40cA`a}6JHosnrksvK?)Sxytqf7 zYELQ4&CAU%w^)myV;YoMs>&<0m_~T{??CX!>wb7{u-r6zd;(%Q zb;&X5_$@|Tjy)&G?l725`BgR(epg~ndQM7yW=@LK4so*Tbi1)U-xM#+$uV29RoMx) zxKcB;Aft_$TzX2pImM7^3Xim8CKg9##o}rMjWaDZBNaa{Gs6&LFy)!8`MIpaxQXe= z$DNfXt0^yAWhyDnHx=V%Vq~n+;(~(wf_zJLW|5&Lt2U!1JH6D51T;>z)sAG49XyXb zTV-`YLS9l>Vxc}KH=`gox1=mTs>D!gu%#F3Gjb~I=4@$sPOiQ%xhT0R%@~zuv}Hmi zJ|iCyu-E$2ZqukHoZ0wEe&V3cm44zt&~92LX`DX7>q`3KiI>_Ikr&(FXn(_pW$+&% zPp8p1$2rG|oZW2*U~mEk`G&}0v*+il3ep|PcCLBWz^X~= zbeR{?1gV0#WITwLQ!n%R4F%1OK-O4fojrUR7aT~IEJWV$u>)yb7AEy171>LcO(cr; zR%N)%>FC<=2O$xv&}nW!#3s(K>sKAJ8E{a=Oe!PUo$TX|m6S8NaajjR#~CXTl7-~I zr8AHgvNAm`rpg7Em>HJ}Kde{7a4Z1_cPiRJs1AU-Cp4{F8vxyH4{+Hu*oC<7W#?0xT2I0<9ZouT}fIhTo|C$-CFTB zU0irFpRBWPg-e02eSp})1OGvj+tbBr-x`k+NQeFdNE9_7QP{mC3Ol4p*_On!7xu*K ziyHE(jJ@z-&3L{+!%TgGMFyda%v3IM9OOSc^v;;7m92wuD|`>1YSFcj?|)ELnX4>S zT>Pq)sVk_u*R4o3m0M`-Xxio8vR`?k5`X;ly+eOkq^>jVFFaAw3Pcp0r_1qpp74QC z()zPM3GfJM1^mf$v>rq7y?r8L=59q0g4Z-cdBZ|#0iBENHG-VwcZcs z)1hR(d{QTQN+&;26TEgZUL%T)2}=o6gGo>ZtkxQ`mMOm0)~a?DR99ATn;UnmJFb31 zCV!#R@pU^kH*%E~)%iQ2Xqy~U#*=k)ov17(FMOM-eZF&nGB`;W8O1ej-nxIWnt82@ z_it_7%tuD)l0!P$$Fb=;vhKD9NzT6;Swq*dMxdJOlD98Vei`za_B6+~5}jHwao2eD z*oi^&wfwLNH=?g>*KQ_%`$LuPx>02)`435k8r&|i!pVE%qzRGfK4EGlRqgevv-)QHB|hY+pxxPGe?c%I{Mj z(5J3QPmSoe>s9rT@u7?6^Ya#kjJLnx=zXOx={!Zc;MRlSd+IaC^D7SWHdaw0ophVz zBTwx_yG=?-PfJTr@vT_7IDfwS)xNy3IsRFGx zr7EUS>PMG5`zXV=tw~y;me+KeHKk(zES`4yWc_a!&q!UM=*KW(r&8@5RxxPFhRTPz!2)P|SfE{$Sk_HUeR+pNao|~HMn`t&? z8!aihJ_w?Th=_3j;U3Ls*ST9oLYo`J$m`^5D-?k&Ilg2H;e=B6Kuk>3u?F)oPAi*| zVID(ErQ?m~wfsSopSUtn16rkc-I7?{I-cBsr#c7IZ-98=#4Q^(@a}TX#EKZz2_XS^t=*Mfh+Lt0|b$SfxsYJDFlGY6(B(i zPQ~LkCDS_qEKE)Yd%u#fHRyRFclCf&h=n}gIS0KqVHGPNa$NE8WPtL{hFkAk;*huf zN_1e|g6jEd`qc2@^eJt%_P{z`7~~!V8Y`5v)Rkw?R^mC`#=8dzgGBKq$(2>A{X2K; ztEx(gFG1+i{S_n>Y8Po$Bi?yu#Dayj`_^;qrOq%y?$5UhrJ|XaZmqwg2KDe6 zJO=YXLO{X>CqO`|kw5{0-Nfv{)E@*mw~#YIS{Z{hN!E^K&mBM&?0$D+yaf*+TvD+= zE}@7gyXkIGVPff;Xw_qd#O-h)a7wk_xGBPjPh*u0Qg+BhG?K;+nFvhnBE~_3{3hd= zx!U|SSq|Af$eSY`s#R*SSJ#d|z*#$FEl~~VFN-yIMFk=B254^bHbmEpWULknV70Ec zUH{7$PHosfw__I{>5OU7(eD?cc(9W=%JEk5pnJoka`Mb3K(L=C@|WA>)Ahm&Bb8TH zo_MQ-`-wbSIyvo0!(cGXmNmi}fym;e^y7@lMmX^%$HFRytD^W5I(XkHvnXWE#+fK)l}dg;M^M9u|=N`R9ecJtfHd z%CC+uFRduf$5fFd9&H*uTIDa6D<BsB~lLv|aP6mKD*Lng_kV z@{n}pp@_prRp+XX9@@|CKXkF;3-#AmgJ+%RcW>M?ZFip{qtCbL1s0K|#0>Do`-Y1t z*SWM4X$R8kCf3X;S(z&>n5ea{SJR2~#nmH*@{Fl69;N5<3YZ$7pc zo#amz9;-eE!QZ{xYpNR?t9KVSNq1Z+y!x4{(O3`UIWh;C6bxe5v3o;)9Db)eN*f$< zMv|_h{*;^L3y%1SdMa-kk0zApr1^2S$+WwQ-j=*<9h| z{ik^Hl=|me`BklaYt@BaN1Kl9+t*xouyj{ZbKY@09va91soatvbW1JEQkiOv6@{vD zTcN|jS*_cxAJ}(h??43)DLjZghst3r&8X#K%`m%~#4J-HZ^6B>pdhn2tIQs#UZW_8VjT<+r(+%4s}GyoysBgnvww{23nm_@wD$26ukXAae*n|i z?wYOi|C6!2{`41-K|P@3o>aimrDQ3BNO3ksw`BPyKbH&tBMg;}P!-bj1xXxPN|!Rr zKOIy`8*Fwz5$;zph?F*PE&W`F$-Lt-fbM;iv&rJwOo)~}U!aRGki}&21(7q%J>s~m zJ<>V!xQ7m`0X(hy_Z@SyoWQ!eF9Y(@q1+|Ou@ze^99cvbi7b|4TaKCx70Z7G3?1sS zj{BI*8IJfdD7_vg_r_&WVPOc)BH6!Gq}Aq)ovea(@x-t4j`1yGZ>~k*eLnV8^5-5j zL5p(;83RNq1O1p`FZLr=#9ZePYZqiMKS5-xn$*x|IOD184~x!8vx+Z$O9U?LXjUtr zJmQaT-TZX-!gr>;`;x9dH!AwV+h40mpI^vqvJHs?F{nywXaW+uljy>?Dwfx8;EQ6- z>4vC`gw(){L_-wFt9GgX!6m>=G0Y}7EX6`65YZOUK#+n?)3G#yX1)H#q2t@Qcj=Ur zz${hVoXvAWR!Ad1{Y?Lb+7sLR(%FxUB0V5!&=-$v>^;jvyJR^~;5KH6(@&@TS#_6n z{2S87g&)oO3?1+K;kP%gG%lJsb!9Kz0B$roeqBvo{ux02tz-;bk>?>z9Sgr|Jk`Ec zv0@iG9%oL2v8=)@7u%~X44i$K{Gr_Ze(D!^kV3b{%$a5Pj}W>TLSREi+|z+V9Zm`XGsJRsdT*M=Y9`QpK> zGvpy0%tpYX>9{W*C<9C$!EYJTYomDNxjK=7O=OH(cw0=>GoV^1E(|Wrsf?ChnbAl) z4+a-1JOaH|k`s$*qe`2&aNAOFFaeOEj=Mtj1rmFKATL9vT!#%fb36t-f-K!nW=@Bx zQv&>z6dH;^;I3tzR*ez9o%Z9k*h+ipG=bF}Rldk|7Nbh=fDuZhe0GM;K&{ z^yG2ahCW1BLCSD7Eg{eKy@c;8kmuO+mM}JcOz5qBRmaeR5iX}l?y=!TCcPi# zIi#V5W<0gYuAXIISed#89JTv+(`=N)g~jW`BgcL1gFa|PMC{fA+|E#52%k)c$U!2m zw+&D;x?U z3M~MeY_bNN{Z^s%E+8oLG)%j|!QNmFoh5tx7Yp2UZV>=zRJdB9M(NhNwU`mpFe4%u z!z4_Bg6r5U3!4e8uqh6(a!{}j!N>&035-k#uX*r&_~nSmyr2O}DWFG^#?|Ho?NSd{ z0-ERUHt3-%9=G9Vf>FT4$1#7yj_H`d+mkSlN8Lq>^Vl>$3rYhsSU=f&blUr+lXV(a zj!x5nU*`N+8N3-KSHoZ)i!iB(L0*(eXO8SOo_6-=pwrI1zPL1!rz6QTbSyIFqlsuk zZQ#z}Mrr#V1cqF#UGGf#EC9&%31a_+Bl`{hjf$==<52;w6B&YkkbacD`yqMiwHqEi z_8a7>yN5o+*Dx}N;C2~II!W(b{N^{7&~lC-g>(#gxqCVJ#`%EUl!uasu3k#|&Es(L zjkwZJ^ny~}^s{No=Tw9{dE&(W1Fw!pki?uNCX&y-_{qfkb+xnyE6G_%2)#suIe93Z z`bOVrt9W^n8R4dz;;fuO8IOB#S>&d0OtQ571FM0^$+x-cD{xy8WPm zRS&UL`4zC81!$v!96bh^{rO{oD(uMtSEIZLm_fKnAu;N|6|cbuV6n+Foe$s- z;41f_<_8AcUtkw89`yPxaiO6+yL-T%?2aNm)`CJ+p`jqf!3FQC+Im=BSDjZ@&hOoQ zWbY}JS6kdYP#B0f3@R6?7i?U%F_4dmPDW9r6+0q!1#^xRD7mN;lME>+J@^~_O_YL6 zN}?*!n&e2~b_GZ5SfSpggYX`|F>u+&1s&y&1m9u`p9CDp`meG)~ldk&6wMNxjX$$d;XJj0_!;fat`|IxL^gvNVqzJ zcBD+0;Eqs!`0nmek)uOdn{Y^;zv(cewU+ z`PJ?BeFBb&=)_-M0UWBIiqs=YlPCmm%nVWf%}nF6Bp!0we)=cKY5W~cgtaWL0(?%h zdKXh=V#^BbGub^%b6Ol5OF=2B^dJ<6bz?I9aM5C`V+p@7Z{?P#gvi9mB;P&X_CF({ ziq9uLB2THX4wM45@*!fsT>N#R|9R(SKe|=<1o1x`l_~zBj(jNlyX0M5Pea%q zSAi{2osnTOW$;e zA38W$(7_S<|3;UzA2mc4MpmWynygk+j=HQQuQ-<%n*6$^+lw*4y!Mmodsj~Z2%hU~7(MqZv0H7{yh2A3EY|j?h2UECq zK)~g+9M-#BGeI)8EKKc`%B4Nvu3^Z)~t&kkHb_ySnqx|fM@3xdHpDF=o83~iTjuUeH@myN#+!^;#!S^Fjl+(_1b6D(seRw5 zf4WH|vO;wcQORzc|4IGR4ZJN<7vk+ry#40X`UU6sbh{lix%n6KIbiTRv05rYxKMba4FSlTw?mw!(f}m(7FkOITv{(| zZ3g5(+5=!W9*Bq+ z04Z+6qX5@=?aRA|UK!8HU025c;GgR+4T+5j+N=t9=t^R_xY!h3xN380@QxTRHNg-Y zr;`6L{rHx1+}yfz>o2P>pWAn?jz4$2{zD{$Qj7QXh0NOs(lKyVf8K8_! zh=4S+w$AE+ z*!Xa;>f|WN;lWs7X4BY;R z)!Ub;Jw=|YtL*vZyt~g&GNF$|UtX0~t@a`Xm#q$67r~?XYyTEJEHKdNz_1?2GmfhJ^ib)KLJIiLyuCzkL( zNJ1tz%g!(R$I_4<46OoeLv98Vp<>1+C<7d33X+eB}u=hC$Vq&FDtl4!uQ5EAy})F6=!V^wt0GqI6g8gRupETL01|9su9kc>Vt>5EXVy`rPy zlCwhc#r6}eH&jf|89ZbMQX=52G-E#<7J;4Y672$jH&vWR-#sN2Tn++KO1pN2hA~ng z!2X)%?>CPX?q((GEuc^A($1B2wlHl)qWfF9-O=K$1n#XnJ;Pg6dIn>smvW3TkGmVY zwhqIj3lqXqdiwvm(f`lauV9u$W2kQR6=J%Hm?%2Iy8y_T(VLlj;e>k;1NVaU_Pp$S zhET$!PZU3Sfq!Jde|H=NY3bxaAlkP#f93HOf)IPwzAlrei5iH5xe0E@%JC5T?*qFC zuriYZ0ARO63Sa>IsRWr^2KV}DnLJ~P;Ap^rLvKJV53NV009CDMGom8!j5>LH1^_kO z5zicfD2!JXf-Oy$jO5NrL}Nz&9gWGh0o!V2(HI~3pC_$3`8l?1DH)2>$?PClWC~}1 zQT7ocuJE3kmDn2^X6$;RtstXsTIz|;{CUz7o(T(!TDnPv%VuZD9xM`K+7q-Q1pDz2 z+fbI>6R7dNCMYxjwF;-hyI^7j9q=4$Fg*m^XMM!nAmF(2KlLBU@UDuzf}yDExE=A) zV?~dk2bu;kMh=;9+}{7VB?H(k*(xDz?3N6|n+6YkJgWhdr6b7mKhZXHX9CXhM*IO- zGApZrHn(uJt%2%VL^B{tgjxOynWh;4(!F>_Pz$m)@*8+bwL~WxAPx$GJZ3`>QKU+! zHe7TNHgLEol`4XQs$>m8B6;I|F%G5^L2Wt!dt+V{-$!dxnFLdt2=8?*q^&^&p^2=9 zEDuN?7fp8!D=&bsi2}Z6{Kl+t>dDZXLO3Ic zDnxD_dul-hqm@l^s8~xjaruv+h7On|idw)tm2~rvD6~qbxwX0-*zj$cO96ZsZAEYr z?=3B-APkOqRl4mh}C`aJ4t|L63P4s+* zm2)^+>pEQ4?eSlpV+z-COqWiHy7yCL|2#;?28Gzb)BgXhAUW1_R-~Mj@=528E!n^X z`AC&;o%Ns%Jz#H7dEPpkad21%I!%XWs!b*|16I%I1v6ml{rAX@UvBS*x^CMLvgM968Z7RT?Z(? z)39>CJbpwLj@8206k{}9aN|$H&=Taf+R>0p3meqiIx2W0Afi>?dGoVjsQu%OFFRYy zG>?a5>+stE`N)wIf1@FWfstEn5Zk}Fx(6dp*0Yfsh|k- z*3LrWi_LEAn<7~td_Jc(5K4?ID`m^DY^UM2t3{ICi7`c&bhuvw0J@OJ3iw9(_4Jmp zV`j`4Gp1$6*PJ}_`iCuF^TK4R^?;@Sma~`)eUbP6ZiKhhzalmy6TB!HCQ^34Ra4XM{ht}1@Se6s2py`KSES^ zm&9_PItlXCdtY~NTVq_4xrR5zWyHj(q6^|GitP40J6Bu@`Rr;bqH&+1W`sZH8mjmS zc8(7ARd;}eP@o2**{b{!gWBUu$m92*=V{||n#s|zVhGeVegGQvt3M)8I`X5Iq?8Z& z)DtH%PpVIzu;iZL9UomT_z2(ph+rxz!RW|jCF!%4@B@g5D?8;ldscNV_FCX4939-} ztwHn|zH0EmyjRt|dg;Ua@b~DmeXh`<>cDBS6DFwUIp&sWxdF86T7a(msA!jb`poe@ z9D?;4L8&99YEnr4s)HJ^4}a`oK9NBf&r1}Bc?t6Zw-f3WV(wrj6|^Fu1%cbarTq%` z6za~cTFB%6!D6QU-*iPVzv3dqCB^31Ht*7D^bn682@jR=DTyh14pMM`iB<x=hnsaCE0*CbGEzC%fAM6_0vSa8o>|uwn#20$?zrMD|Mo80PKz^b0<1{ z39k<<-?UrbsNY+jzgzleu4u!Z3>9yOpzY`Jh_o|Evk*YESoYzOoy3BF$k~ccye6aCT8%s!73dX^rqou+ zbTauNqF9RG{60J^#ZnE1N(=AmAhP!}V4XNHamu4Tvdl3WPJZa>*?E(B7Ny3gf2%;_ z>!GOYtUh9s1 zC4bxi?2*vbtO;NiUz=G&b*QY3`F4PWA#30gqPRASY-63qmjN0q+5u*byl1CQ?QQ?H zp|j1qVSC4h-W?8Wcb27p`Zfe@iI|@v_zzf7yijdyni(L zBmt7pEkWGdxl1X3*IWLGlP4~(TeB~MRY3C86q0|#Y9Jkf`zMpX`?E~`O*HCbMX=gN z^2Cod1*}3A>5Sf7#8;L1MO8H{3gGGN3#SW(!9-z40t4OMi%Y3dNuN)qFR!4|1yV8- zg|E+&SB{cy`O+$xFrq7c-aubkL}jz2WUhofb&>QvPrBQr6!lD7-D{ux(!gL_ekf1o zND^}rt%)}2SqQN`e~J!BPX}X`gh|Y$CD|ovGT`2VxkSPjrWYCtGo*0miE0fQ_VEvg zr1Tw$Fuv>H#dO#>s@f+dizVr`b;j)&4S9DumyHK`>{)n1W&b@CY#`**kI3Z77>u7~ zPX?l6806F0K)iQR)-eoBo*FWc;_xm4g5;4JSBrbaRM}(rSuXIg6!$BV>>x9x;np_rZomuJ=XN^fV z#JZpMb3O7wEti;5!=+fC5<^*@wN!Z8PxOqBvv)fm=>cNE7GbN4pJ+N3G~keyD&0MW zp7m(Er|^>KiV3qq1AwM6WCJLcuW_I$LlmHu?kty*Vv~mCK+-jqaEosZ{Ec?qP2UQk zb*6YnLa{*#$?PnPx**?{Z{_WU$V8kc>r|-M>esbe_(HjKdBNKkfG@pD#?Gl1xfV$v z{e5lM?2nR(ut-D}6(|qBpYYyn2P(SycuKl%PlzpwQD;eFViH0Vc^ctf<~B{5oszKn z{Z+m~C;I1bccy4%TFJJ0b$(G!ZZR(`AbNq7e@!h0y+K`HQg<+oA1-8)zsR4We_(uL z{JPdC3u_I#qROR(o}7DfvJt2~cp>eIZHWoN_7L9?du`M%Cd<_-4z38>nZ~i`t5sc7 zRalkJI{{E)+Uc))%^%?urZ`x#cSY{Il6J)*&ufWrsyzTj7j@3NVvC}9;O1>!H*>P8=k4Jhd8DiBF3oG? z>Lfp(s3F6Sp;j+`^Vb&AF7@v3!P08yL<#{d0({`_uyDYlBj5e~P9CQhW{@(wjJ&bt zbIip;Glr&B45f{t1RyJ*10mPz{kr~!{(l+#*#h8Mza!tpmPQvw75K)0n7y6u=m5?F zfxB_zjO>kjeQ6y&PK_yuDvU0T^~Dj$zv-P0VCt8jJwc_OKDFz!FIDb#=O(56*-l9n ziRH1S^xx!;j~5C%?#(ASSnYz~H^-^Q?RxVRaIoLe?@D9K6DyKf%Vi{uZYSGsYijc9 z)O9r;EN>k?Ni7pOpBwo$)#iQ$JBB7NcRH3IJUllabj3ll>QA4#dbvbH`UY_ElfmF8I@XvbXNs#Oio% z+8VMco8Qsy5N*od6#{j0hj`DfoqO<+(;)(yXp9g{x^IM#%YAT!{6zC{*8wFVKP#^- z(#X%=0YK|ZWFR$?M49si=f9P-`xqK8E&_M`Rs~5@5#K(yXzvlTf;Qil?JnD=KKa3> zMZEkhc~cf`PT(w|A|YSg4RM|BShL3_mxhJCzLq)PQvMv&s z_Zi)V2r@$+iZyh)vTg3qRKiiYw*OT1rY%)9IzFU6{os45oB1~jZ*b;3`*}-_)GU!V zr6Z*)-bN+r$rE?n1l*Q%fh3BGbRK@bchCN)I)^rX)=pJzir5ma<3hHqOkb@YH7dVw zG@opq1C3s(JQSXli6ug~LStEGIsW-3-ngm1sebREZD&1SQ(aZR=Su(6M6M!|pU<`Z zetQn>%+YSNOAviZHR|)NSO55}!rZ)d2crH#O;e z{`T+8!DN*`tavCwk>+ki6mhLal8y?H9$8q}Y=|U6ujME_u}sn&#O32M1P%zv0}ud^ zO6}>%-s1%@|Hy^m8IQ>vW>i?ZKESH}%G!RN)ChN!DSOlR?S}-1r^)ffZ*G5^`|UT8 z>w)k9OWLTLJ`WL~8-)LTT4Xmz`8?DRJF)wGy6WqYTPf0f7La6JNtaEWQr<9&gECsu z?xwVT>c5YPkd*|Wmv)i+dE%oa-QK0L?)ot+_yjN)TOutht&S`mYFwIX~0 zERce}=s%Jh^UkQ{i$kTX9Jm(IQmDc?SiF!$UL6wmDB(6Ouhnx1ix?dMDCa)=a&5kF zo0JQq;Km?-gxIK$CwwUU!}{z3%!)$ka_BTTosZ$|!a|+_!?<}VAZ8lc417V4wNF0r z0LNA%hI$VT-S1AC?<1s!DPGTv`EK?@$)(#LQWa<;+ zRrIvjQDKELqu1{Z$_ptD>ho-q#+8EmaGXG7e5E7_#R zH6f-w*1n2MsF$j}*;|SM5h_3lp2GUxXBYPniZAi`iA9;fRtyk5(PD*Mjl3z>mgC4{ zj;RjJh|Uf815|P)U>O}t4;HLuWm#NN46@zx$51o1aP#KQd3*L`_rIcil1<4-&oHS0 zpR^=%T%NvVhL5-84(x?&3r}|5V&L8pbZ4gCl9Zd`ix3%dLXd&80n&{cGzy|~*lc;( zdA=3Gzph^R==`~}zL1AXxeLtKEf|?l8=gtNMzm1;HN8%*%WwIKKXv9PcMzWt;ydOS z=`UmHzs`Uf;s+5f@+$qBa2m2-%>KS1-n%O)vXn22v<9VaqEp*jeaOGXz$m=#%z@1S zc`78WEKug}Nr1c5xR(k`ed=Wbd-_)Mu(wZ(hF+i-d{8~|LW{;%s1ka5sH=bP=3MRB z4LbDoOa$(N55*rCS`Qz7i>;Tsm$IEYAHqKGXuSIXB4|b2L4OA`_1n-^_~3@d_1HCD z**-#CjDibJAMp}*Go^h+rVI&v{A&cM7m+u`h2WbnUPzXltRm4Ow;*0Fzn_-k4_WM z?RY);qK97_)hYQh#nJ9rh;=8t#BSfD52a>G@P{u&mZ0=b4U9Mdc@~Y9T3SD zJ?SgI=+a{81l6qdF|)VY#ED6%Ne14KWJz=+|N4s05J>7y97dOhN}XyrrUN{6542>Y z_=|%lZvF&1N|bEiiBVsyVka&*Y7N{80pk@DQ?xK1VL8$t3_-o&#BJ2>&Ah z`kss0TjWOmQ-L)XC=<-jm65pl|5>=!)r{m&yRJ!dLh~w84CA2Ghcc5rlj4)XmS82TfOjq4jZxk4LPgYsVjm*t^2Xd+3IPJ$FIO5AOaSuPU=s zGE&lszoxL%#K%LGXcQSmR~JiTvlEHG%;v~(n8@W=RN*z1(#ui-YI@m7-KJrOBDRAt z3}Wa%xQDSF60n2aZpkwVrLn>&_oz}gG)v!e&G(1$@M?6py+w)36$#{IeWo7V8;doW zk19yQ{OD9jstYPB3b=~=T2x#{LcZ0fLSF!Si7qKJO3y0Yuk;h=(f7!E-A}Puamh7f=X>x0-E*QbBg;7l=8i{cg* zbsds+tw`FzkVY6mp`3-62sbm`w^k4C?lQg~$q)%RTP!-;#bt4gQs!4>Y>z8PYC+)> zzH>=dcnE}O6+Us%nW1?R&~~UwsKqVQu7HsVhHV-W>j6}onrs4$$yaYJNGm|0@=#Lyn%RprcsWuT0BL zFrre|L3$9Cx{L{+@}?G<9S(Ak97Lrqb5W`tvX|{sm9!aoJ)v2^6Kcn`w0J(ad$+0S zQdZLjUsn06X+ze`4S0Eo9P-HP?s3I>Fy@|ToJ~L%w#Dgm;9#OI7Aq2GD}ePa6y~eFW21sytS`L845#YH6+aO=)N(P(OTc8Kk z=PYS_cwQV3WDuXGvwH?loyAWY6;1o^qUq*@)PzKX)Rbc(G2H+L;({!^HyqpS2~Q(v4)cM<^+X6w ztyLm-WK|;e=@8w){xni2SO=8nsg)_PX)V&MEkRHS20c_`fo_Jhp&y!+(n| z+GdW_`$p&!Bf?d%AHxeHs`Ol?zRp};gte*Fr?eoiyix@fa2<@m$Ee}s(k_+ZpXRZa zrR>mEcKb!c9H$n~2Sh%)E5FZ*F=@4mQ~& zCjCApJ%1o$uYMAntu8f`=H-;WPloxJb4`v6y8%)Gsb*<*#_+0MYOvQFbQWzK%J+jR zrFgLBW3h2l*81!q>DwUmP?5yL==n)ZKlm1??m6T`HF@^O2H@0+t&Wn65~*i)*-ST+ z5ENBdBq&K70!OHCIg~`o<6Tyv7nbJ{V);=ln{T^^O62j_?A$jp@?x2co+ClxhhKa` zM8DmhX3FMl1{7q>c4RXY*zZK{lUHaePs*2C(*g1ZzDZ5(C{HnpM)Nd$Ao-VuzBpL( zlUv@Ob+bQ2%;zAchS&)MPkch`56H4MV(a4C0Ps3Vr|WLecdl~urPH+A2ai-g+_?-~ zR)6xGKMtFlj=?kMW#`(gjvJ)U|LN;Hpqse1u4Qb^3>uphdx$MrBUB-BLeP!Oi$MD|wul29* zUjj>-raLot&OP^>v-kEaD#-!udsYF0^8M)MI*!aoQ&p&JNCNbC5leS&N4@@7`i7Dg z5bZ>=Xg+wP-Xe;PW0X`rc+DutK@1{FV~!}1M1t!vH#I9WeHb{OQd5lamXyK_OdbZ2 z?2KJo7b$pf4osB-R zx054D(-nV!IrJuOnb(s$L|z2((f2!jIy8=nGZZf(!}%&hokD28<#aw057I?)XP=f| ztw449NVC zmpBpSm5<5HyJVIVu(dj8`)>m)$|R`F*W~Eeia&9&j@~6lrz`$qD{%JZ-0d2(7#6E=vv?r zw7AM1eV_fLUz&;AFNhd`s4yq*#}I^IG2IQ>TVMJLOXPW&Ju5$~-nG}Hp+^8}GUS>-Q*OvqIfk<_*(pI= zREE49D$f&x=u)}+QnHab)Sla}qQ$Jc0Szc*a^LPW99Gc+`~togGsId-7JXDlvMR}% zm%gLJ+c@{P?{&TZMKbZ?=w8R$0$oKvuN^9q2kc+ubFiOk=G(&r;0_zAr-XK{oo}!jAQr;d4`CK>{uiu3 zKhi;-Iiu)toKQcm7^+5b+*gY3JK(yWrpQUvB<0BSSgZB6f+VtCiu*l}AE^Nb@wpA0 z8~vZ%agFz2Z!H$DOcG~P0f%rLD_)%EReH%(L?*bPgh`Y zyeS=^dx{+gc(S?l6m|RIaD7Ml@3)(M2Y1Gy2xdT1n*(F+D@f#B*ss1rq<*qR5!}7C z2&DyB+cN~4-G?*q&0R!w^nF|Gps7XbectlMEmC2Egg=ItghTlWyFx;D?+R^hZ)^LVy_WM|DeoA_LaHrMh+DR% z`0AFYtk5mnu_GubaLX?L%`3)GJ|LUhlN}nmN7*Z|yZ412%oW>mFGhbD#RVXxtJ+A0 zsw$YVV~t^@!n!4h+a;@8q21O0)LqTE&BhYtEgP zLQpgNYLB3717AXD4{1jGLwD_N4rxaNbC(I1LE5K(Ws6@O`G*OpU@8z&pNtRzF6>QyG5p+l)^V*r(D-iTTj zy*rl+%nc5O>ZZW%X$}RU=ArCIls~qj-T&a0{XvI!SeKQour4q0J-U^PgpI_tx${-< z`SABNx>~&@t(7DDn7_We_m@#~I{JKI2ZDyEIV6KF5$^2Wi>Iy;kB{vcKVeoMLZ*EB z{gq7*NLQ3Prh^nUKHr2sqTT`W`7%WzK zWt_3dSX!%etm*z#IH;?Pj?%{kqE>?qw8YoeSSt>S_I-{sNTq+eT!m}z42iVa&< zrgMoB9>ze`FyeSGqiW5{q76rr&vP-~7#`e(l;yX^2UTB-whJeYo;Pu2kcR_)M-4_v zyeATG&AE&dTS}L6Rj(K(OvTo{S=}0e`oBi}+4T0r_ad()9*;ksc%1u;IZfA`0#5W6 zLpC_vgdOR@K+HzOh9~0$!)*<5nxv}q76gO`vWJUWN^$O$jkbfT1C7ZMRhrV+q7a<> zKo(-3uEG&EI4mMDLKU58u1wctmE=@l;&S|B+Q7Q^<75ejH26_EBOF7Ot<+LerXlSg zI~dl!h@8Vj$PA3@s~2t&=GLu;hOszRbm8qzeGW!ZIYO1tX5 zL&ioMbjEBkDX$2V<;tqk=4y?7zCxgYT}13|)!v}WL&2I2le)*; zXWg06G8)Xbx9qPxplWM~4X|p8V)FL*E0O;u4=h56AtonP%!x^h(UVr$slDx*AHg{AthzA?nDvqnV+TsHnHI)(OovW3@KyJ4unx?Z;m#&DN#YIq;T*R0;^cu<<=rfI=2d$j-(TY21Tr?ihHvz#^ z0fPCap$2kscZx5culk&8ATCCbIkC#e@!l>DVIeJ_Ps-(knHt~PH)?%b$5$^fLr%2* zH&V|MH~UaIsiEHrr&ABd;v6G(SNN+o?T!zO(8NZh?pUpaGriipqbghsY-o$`QXOxr zIM|@6YA_$cmAOa07bZBKV?ttLlb|M-UR;_ZS%8unrQLagLu7a5M;0cE5$2kd7S(}+ z)o-_J{8)FntmXl7Tu7sMGm!YRKkV)n47o-?_d3Lyl(_m`Dw+n3luY=i>3U;QQ8K*g zR?l3J{^zQw$>EotY)m%kz4Rt4WF$!%(^i4`CtMf%QcHzF+5HY=ZY&wP!Xy>VV0I-& zX_GY$>*HbZ!3HIcKz`_T5~HnEk?qp1rPe}Ak;Y^(l&0J0eLMBcH5iR5dqdBRA{&-j zyij};hfxj@fyka)Boc9w?h?U}o=pAd4`O_3Qf!zcA*o9%EJj?WIM-sb;K}*b6Kyq! zh*Je+T5_$0m|zx~3rbYv4W_v?E&){?&(m;2F52p1&kzdJ4EjvHV_fepPqYt=yf#Oe zNsnb|UTK-BS#as!U_z3r%7J__fU&iRFR(p9J-60G9Oy^{SHrRl4a}rL&?0 z#cm!*h8oD&ARvsQewlq^oRw>!5j4s`flk)qJ%UDP#_8tFiyFo4r5Xb!Z9~E4jQ9Oi zBi4@kY~Dj17eOLO6zU>Wm^nll8c2lZq4l#HHNSAJM1y0Kp~y5yeL&%K*{XK75AVJv z&uxZG?z6Rjk$6o zYfqNcPj7j<+!q|uAs)~=dn!36x2Mu`0x)&w$s^ifPa-$uj-+mID@)(73TCOUubRP3 zc))(f;8wf!Od+mNSRyK+cTKLGj$ymk8091bH;cMD9zUL9e@xwawMGW_t4;KF3Bo6% zp-qVu-9i!_-Tl@Q8yPL{eb)Y*u!9coew8jg3_d4Eg}p_XLkHUbMICp@Ksn9pUI^{O zsrI3cFUhlaQz-ZoR%_RAXPZWC4K6i!kAz4>8DB(Xv+&`<{)0mf2W77a60K zq@NHN78WQzKEnitH67G+dy~Oz^0xF%o0Kr(d+2r`vMb0QvYnW_(z}v7F(o!Iz1}Q6 zWZx%X#xGJO0P=G{S*ipCe>%o1CCJlX1&OedP8UI^?htkc1??2+TxMs`{tgY9&UWnI z-+{qxE$hx>x&y0lfQRSl=#(13@MF#BoE0(O=O@ggt;je$4OCX-j zzi?!6&s#!aTk+w@{i{Eo);hb6hF+!##WXri?kTud?_5atUq?F$0L{+DDi z`jw6R_63>x1^J!WoV)LLj~9xU&E2?W|B8CU59gY=6D`+vtWKdRV@{bR28`?eO+4U_TyVVO23dsWXZ%S z_n*=WMIW1vb#ZU^CJWK?OUC+arNVqVF^vvs^s!B@-*!Fj6W#TcYlS7AB_774EhwFwb)au}T$ikzo_llP!W|Gk`>93ir=I_Vs|ykaIz~& zs5Aa7RqJQPEeT%}zBX|4mVhn0)`TvL;b<_K<7j6W6ungzAeII+?e5sqvG;iR8PM6B z`5^V0>Vxwp8`x+{F4SJx&yh@a?VLFgvsIgSSZV?_5oK}JsSTXIG3(rYrCkI=MutOX z_XJCo2LVcf_#q=oh`X>}yD5HqDwn!_OQyeS^~NIGcFlH>v4%8+*2gsInmAo^28Lbx zNKn8{W4p=@*R(brXl^`E)lq%e_HNMy4iCsNRPijPP4on_s9;M`tXLFlORUmy35_l3 z2UO?JR~mkvJEMD$;Em? zkWfI5S;{tyRGW(nOeT^1Y4<3$3g(W$*Gz%rjI!Fp{snYhTVA#wM z>7NddG<}Yg?MNxKrrR(s;D=D1CD{NiYqJ(3N`?x@5f~7_Vgzw%DGwuUqGfDpR$ZY8 z5O|J0)!{+^@szL(smdSKPtXi@5BjGi&6ZPA=v7i!WVI=AXqUT^@Ue6>?UpYx<{!D#D z>htTbQ~p#PIA*OotEoM6!g@s2c}gF3K@)xPxbC3p?za%__*QfNyCdH;e9k#sy#0)q? zQl9LdV{Z}+y>lFA*zP&wqKBo!Fz1 z_|dCU&nkUPm zHNB_l8^TI||5X~tTz2Jg|8wWMj-M0lbJ_R(kFOGYx?+XLqkG3QZ@#K;RoFi?ct6@;hcZh z%2ocGR*Fwr`J@2|ki5IO^PQTQN95ZI`^k@wRTH*4uR5tLecy?i#LDN3Pzwp{)v$*@ z-#4GwyWi3o*zwV~P468nZ#&;!3ky6gwTwJh<6gDogP*&{^mGe*^K!HnBWF#o%&XQI z*zb}AOM$*RBpJ*Bm4(JwOFl>ca=a=OgA6eYmvZg{WtU`Gs}lUuRs|dLYs~vO_kOZxW#%T^ z0b{FiUv_0$L3*JsH6c9E@3qL+(-x*KEeh<=*<#{zva>TwQ>`(ayKDj@D-SK(yfeo5 z`(D$Y56}en{@jpHE*F`v2DL;sQ1Or5N8&5B=G2;~6N#TRy$i25D=UucYe&?Ot5eI4 zS@-GBn2zC4K67Q3+nuIDYO*sx3!kERkdN8Y|iOGgDIyKm#(wE$+_e zOV^6ajrE0=_QoH!6X)%>w8x@aQY^>AE=(z1%2mExvMX#NSDtE-QkwPowLE{G`-`l)RXNjVEgAICsuTCc|yw z`pINaw~whxDc6@46~uD%brL9K>$CEdIb~_3$XVe~d08eKrm!_Bxslu%1c)+q+WgF% z*z}CJ)FORxnYqAiVd}oDBc>+nnU?aFwUv8JJ1=K*?#o?d zvfr|*e{U_U$*;YX@Jrm^zGV8WZ#Z|IOy;uq+O|vK-$i|za=qXa)4lcNnc3&px6i45 zJ(t@NkdYXwq1+n@6Z3}Ujmf9|tV5GGES#`q)ryrN)OqLVn6-N%vlr*a8aGswUVZN_ z^+bi%CY((Dj_*JuGd6l{`t?Jn`mKWyyC>o9Uhj~a51Y3^kQ`=1MWH{v@>O?7kA?aSv{(C2kBpPPrs z><{TPxBL7x7yG?G5)iDdBrXW-xp;#v!o~f|9&@{}XV%o%36iMAi|2l%jK%=TwoDO~ zqfK_`%^8$N5TC1lpy?fSqh$q0eeHhkKbC%LP9bje6~J9Laos-j zh7e4b4yBXmh>_`scayiKqMU5^0kU*OX%^ReygN?7?9HG789PMF?cdQCg`Dj1bO<%P zg#6hy5Oq$|+qjaG?-iX^xg#@2#`?YpfB}hg#0hCe8u>1b4&mI_W?HjKGObCiiLHtI zNy)$dCS&vRexNRA>Cim-5=UIpF#%Xg(tBo0nbJ`}G5e5@x;w~ws9$rj*n!$>AmXQ*yee|_igU@g<1~Lo%E^$uWcD&TS4sX&gN1v+U#|N|w45-VI;FIG zfqw0(!)xu@4E+Z2wvD2G@7Z@yxOBpr65BeIhsxTU8bwTO-Q4$tk zNwzh^qM-)+OLF4b#Uk|bP##vfFQFA&)s89MooA#eMPF+qia2fGKh)2fyKj;i3K6v$ zN5RuDh4odOK6>=DNdCV3co++OrG3X`#}4U3&#=p=g?qZ1c6R@L1|?eEr6gIPf7pY= z4(%oU?;m@8_x@K~j;`b4%A2CzQ@z(*TUo9-dh)BI->&*&(O$}j1#tF>i||a;0NT&| z8zGS!&y(06lGQ)BAM%!;Mm~mKhp@dBfAJ0l`|Ei9_gz{pk`}s8K)o0epL7v1dLj{P zG?|T-Y>QX61&sdrwCj;4xxiX7!SgRdf+0_zMZ3m%N*kw?hZ<56yyzloq+Jj71^S%S z46n2dbR8wVz|yWUQk7b^-YZ*ggn9###768!jTvdVx_rG?zP>o!oK3pMcw%E@T#GYA za|X(A3rN>PG=mx?rT0t=XqO9%K^lVJBVFSxS(ZGr$qVHM7K;+iB3+NBOktqLuS&~brtUyYxo%28vd(`5XI67K&m4fLT}bPf7?ZFy)e=a`g8 zKcn9}CMyZJ3{R0Jl!?}p=TI?+{^8db`a)rhBwGvy0!g-b403ZjQJ4r|1BheCqS|FN z_;E)nE_=&$sITq;AA=+sw;FT|01X#POn*|k^QKfq?1O2}7W+-08?@kFGyHZ1!E3yp zQFxI73M5Wn^X$FLP-)Qsg;zv`VS7Uak(MqtjG!Kv1O1JZ6GOSWNo|m)+C*ctVbHyZ z^wQbNGRstw%p2mYOF_|YAf6aQ7mLWDN%;9WpzL!sXuzns4ji0n{2utzcX}SV-t>?> zhq)M#kXlrZlLg@I8;U9pyyLY102_%zuQs~J(2`d4yf+(K=KhH{o77_z3`s|(0D;<> zBag~YNJYpqJ~b^$+(_M)4K+Z*hlZ?4i7w^V@3;K~hUML@_r}(VK}Fia8OCA8DY@&x ziW;%2ET<~_Xlh*$XK2_~Fj2J9ytP3F<&NluZ6nAw&amQ-O^Cjy)g)MP^tjUS0uelC zO*!(diLnMlVnXR24XBhP?$|=CCy_LXn933MV%avxD`8Q2W$pnnhm5~bXHZ_N{hq%1 zXfbEFx$dl0B<2D+Q5lyK7lSg>y!R7~Fhe;oszDC8CX%eiBc>n|-+7eS$qlHP`Uldz zC;{6JJsOFJ?lsav)X&=o{Y=(cKP4;e0YvdHBD#~i-P1^+5aRC}<6pC>Ch2rbSM#xp z)m^dg#FVWL_2(WEH$fk#O^YjvE6%L4R9Y%(iz_oK(@in@IF8I6BwQrQ*D$FW(Lo2d z)5!~b>9fAn{UYcphWf)tMBVQOGWXMq#2Z4fNS0U8HHh*qrYD0r_d*|fG6bg2^B8{feKug`Fv+3+na6?{F(v$XQ=^{I4Pat=IOpOv>>vj=VUVgcL zc08Krvo}2^sA#WgZ);2|1a8P(1KUyDnbI8898|nDWheYNeNe@o{rdg-0~MD24Yw1% zTlpbEXh0-GhUo?R+PidWAT|m}i`-QlP#kb5@=!5h>d7;^zZw6{OSRN}7j?#J-LC4L z?J4ak9n~f6+>h#$WI_;R`4nWJFWkQPYE;5p%sLqyABEwddY~=7?66J%}M`j&OOj zr7o!+!)Tv+0fv&kyhC!&Hu5E6J03m%Ci`%|9`w8*B)|SLu+|f4z@mvWro6Z;KE`k^W?%EV(n(oV`O$)#v(FOQzOxM{>fS9l&RK|TP1&flv#^A(+&EEu(fn;r z=bDxP|)vi%~c?1(jy`9cr@oihO(rpybjAhveZ+VeFm+#p!lWi6Ba<0{>fK$93>1hPBJ&ybFv|_7iAMo7Vu9gpxkCu;@zbaoKUm{;AUoYP*-!9)RkC7+Jb@HR~ z6Y}%&JMu^JKjoC^$1sd5^DHxlna0d!<}(|aEzC}44->+aGF41H)52V2t}r*5JIpW4 z-^{;RXSP2(fOThIVAbr4?09w#yM$fKZeq8wfov!n&1SMKY%6=3z0Tfe|6m_;E?j@k zjT^_!_1ppO2zQnHn!CsS*1v!MXZtJrPwxLl|K9$igk*OiZDg0B41%uR4M8eEsEoc z4;5c2epe91W4<5XpI7li_>uf%{uSPbU%{{D*YSaTI3L4j@g}~Uujlvi2l-C^1b>#l z#9!ra@}KkH@b~%Ok!56DJzR&nj&+^n`m*aB*VkSBT-Uj7a^3E_+cn%Z#x>DZ@0#gq za5cM@x;D8UaXsdG%JrP<2d-CKue;uH{m%7Q*MD3eyScdacN^g5?k2hobsOn6-fgCcW? z4*U|uFI`ly;}rZHg@%7gA@V@v@{`pX9?(?*KQo!1hlNjBh#=MuYs`aMt6Mh$)G)e{ z_!Zddc=3NFF?(6TV@4$MHemV55+M0O6NvCXND3Pi5{f@S!M}=@55|F>(ql;67$VQi z#!1Xl^UQb!Uzt>TU4d$2UKp##Dt&#%d<%t1>tQZx{BLkVj9+r!N$ftC#*&Md1z0@SVTqPo zBWx;O2v@`?`l#@DiAzW1rOU!IixfN(7wb;WR7=bS;QYnk>FSdw*Wr9$QuD%8HIGVg zzX@u7ySi1)6Z1AKl$w<4iKpeJb=x<6lFTWd3hn4 z>BzdySVtDCE?qEL-q(D+{Na}0!<*7uGWTVb=aktBD&bTWR3(?_Y&(u^J{;1(fQ=oL>;jKrJbIpPgPlAuom4vF+5@{#;d zz-#+g1NZ=j&>m2(4j{vjnvw85e2rlV(mqn}>Ot?4W>35j^f&|+t%-@*8A+PW@uAx` zf#jdCo0_*Bix)FW%d*Q<`&&wC>NIv=`z*V!W0pL+u0^+BRaTZ~D%BjTXzuJ%ddvH> zU7?+uD@0RLVt%4(04^K}+%L345^1Zcy+GQLi~sU8R6r80y~d(z_ECh^H~r)#NT-SeByTpYRC$hc-aOlNgM6$v_1C zMhpfMHh7+Yq70;+FVyU77W0^)vA!dyt12YiR1~ApL5DB3U%}-a`1MC6lh<(#wSQuWY2FSV7-l;U-+ucq*b%7`6Ib9V%j16n zY5$>qi$%+r6gAJzQ}gH4-80IiyI-#8o}pLp=M=p&%5f6q>fRZt3SQ#9;z3tGT1^M@ zE)x__?9h{m^%GmvCsD!UQhV@_geZa}1WyNp+E2ifH3j$2TQ1|*t7{ef1jS=NW}|xP z>wu>$hmnE89A70}BVXD8I__hjJ_cL3l>wJ{zNOq;ZYuH0FM?QL5tq;8rRS%c((AXP zbe$H;iIL&ADPV1%FkZnq;o1E3f(%ne_xj%To(&Pz<~AkIW*23dGEBYeyVrY0G{mH$ zKYg-}2xM#R?ugS*Ljf<2-n8R>liY z_&A=@abPFUlw?CpNiz2YqFG#=FLAvhASTCMEW;0&e@ByGk9Y_Tn9)BbsX+rmBw7aI z$&k)gv@DyvtK{Po(o<6SM73o1cyE`}*y;a$@e(WmXN+?8!qo~XD9N)hiSb3y`QQ;1 z7o{6%=AkgczpgM)KVDu}S9;(eu3ScjX5G4pI2$yLc;nZpStp&L6lg*01%LihKV>D4 zh|tGj;8<{e#Wg&Jn-zRSgW*U+1Mg%oDV%w5k{F0Tk1$TJxfM<%Q7HhS=X11Ku^CAb zi3{Tw$L-LCCnQC2sjM+vo?%Qi>O^auk*`+_cuMC>eP%qB`5~_<`0a{bK%Sth(hbCu z#Fxw;+tm*KIdIUm@9N{A6M5;;kp?k|W{Yo3sMA`+NK0sDTx{Q?qUHxrojMq&&hjM| zV_vbw1mQAOHmgg`%!H(DiWiUzjq-{1FIjYAQ1hS*z&k}Bme*--sI`%r*L>uXykxhA z@2G9;dymaO?^9D1S};U;^KbX8`sUQm9@$?LT%b1vU0qK#)oQpPu~1 z5|RI~k7R)Vk29nh(3~XK9zUIOL4~x(n6D0A)&LciUy#S~li2FGlJGF4gu$0CZBt2O zb)|Tp<%O{dJ{kSlmdqbaI-sq`Bc{RA)W>?v$HrxbTC`q~mb!#S6(6GB1M2{jQCzr4UfpzM)b|+6>&*Cd1m?Nq*mP8KhpWgynDUB>cpQJ1IYRZU^PR|GtCpH>Dv$ z%T7=LWV%r;aRl&Ar@fhh2n;Ueg}G>lN1>WG6%`blVN2i@*+8U|Y~zJhemj>7wHweybP}6B8X5@%KxfR$ED_&C1m@@Wa#tpo&|RQIu{=FH0?@8WQVC zTX=%e;B}(;gs9JwK`4%x+xMj6p5dl?@N8R*IU+*D!_s5MyqE_eGrk~6!?Wlp!@x5& zAFpBHOyvb>)LF3N{Iy2)weu?$i2VCl>wSsNR6!>rnm)I4sfMR)Zfv%E=N7|y)ol7) zeqI^|hcOZ*ecz83!uH+39kYQOD4G8+3EES}GhBSFlffum38#9=2qCl5XH$5xSkb~e zcTrY2r{En(-OxDNPGA0uzDQmkhYgn~c%ynid>SMYP|b2v0!n9O!}{Xcf%%0X_k;)h za-TDrl6Z5RHJ;NI6=(3iC)a%-B@Qq&RrGOX-(-2^frK3@0;=11^m`Z>X( zY&S$i@C`655t3m+StJj!Bv5Sgabg3;UJz9>u$}N#se$_QtTb>x;gy6moZ={ko&aAu z(1$8idxA~2aB0Zwd(DJR!(AvgS&KB*3Ug&i1$XNVFX0-x5o_inB%hC$cdB`b)}#>T zY#tySuseKd1;|HOiK^Qqn)n_dq0iDd}CdCJ>1^9%;wli9$KqfXst#{TC2RU9ge!hU{l5u$GDu^pCUj~|^P&2$(s({I(A>l|zdeA5g^DojrwX{*0oX5K$ oMn#JeYGU{pbzL1U=O}QIp<2eKYy*8l(j literal 0 HcmV?d00001 diff --git a/doc/_themes/sphinx_rtd_theme/static/fonts/fontawesome-webfont.eot b/doc/_themes/sphinx_rtd_theme/static/fonts/fontawesome-webfont.eot new file mode 100644 index 0000000000000000000000000000000000000000..7c79c6a6bc9a128a2a8eaffbe49a4338625fdbc2 GIT binary patch literal 38205 zcmZ^IWlSYp%;vqo1upLH?(XjH?(XhB4DRmk?(Q(SyX)W#I)m#B?7N%&@gNzPg3A9y|F{1i{C~vS%_!vmy8pvq0i*!V z04IP4KosB&umrgOcXRyD0su$=wg0R&z!TsAFa@~%hfn~t{zKgUi?RJbIV1oM026@a zKV<`u{HH7cRsj2daa8}Gnk4^EMF2odUHbodF(eRY6Og71NK*#{I$+FQ#4RkN>Xu5t zDV|CZ0erHH%7mJ7f9C(hMgfc`(&`gnuuiqhEZtN@Gm6qm9jtBTu`bUstuVt`VE1U^ zQeRP-GNx@G1O+8HnNjpn78T|1$sHu=pO{n+?Hbd%?rXh*b{x)ZZ9Ey*heliTM$ph9 zeSOvxJI7sn2z_VOStQwpj}H7Y+@M&VY|#ngtbu=`HY)^$pT2Bh?F%Qz)A!hd^bxco z(ph?3k$*g}cpvrc9fcXhjj;5WPot~Co6>e-hv7*v=?ht4ZzfafOKSl*nvanjGNp%5 zqVHEAb0A25 ztDEMbuMI$uR5*rQ;Ex2f;9~>x3rZo2m^kwR6UQRPZz@Czx8NQJM6qF(2xu!inpqCE zp&p-KF}@yM;D2@511uFKw|p7`rR5E%Q=P-zPeXA1Ktriy6is`S1oMudP6;lGGo*>+ z8#MeQ*S6fE;37Z&V&V2oyeT_l1gp@&a)ah*E|M@ELRv^E70jhArQEOCVR(XrnfK5q zp=6hd;d{^XAPeI<#-L-CBvNu5_(Jtd*&!2*tS%|-yzds5)A{0f(w};Y^KBe@AdynU zQL37Co!%Eq%0_)~bcR`#k94J}qgc4SSR@Ul!8_*tW{Z3Z>U6}ivNUHWn8P$)EbfkT z@k>R%?c7o_o;AP3>Pi=p)K`@mYLKBdm&H(%0ai{ls$|XAptE5F3tx6U{?(i@T>GA3 z^_!F+A*NF}bxUB`5ssZLyE(_w@^Dbsgs-6_CGq92Gx|oi!cA-HhDACy{4K)xs|&hF z>LTWj1(w}4LTGz@)0q87y$|wm>pEPvgpR{F10WY$v~2DYt@t>2Z4;zPN_He3aPb@z ziE0^tt>sf2&yu8qR?@PaDB@HEgBHaU>ZnpXEB^D(;d~K@`H3P(?)J@Vn z@CfT^4qS#V(v@+Tim_UUz_Xd-$p=1fq8#h)@{UE|bVYBR`b>ehNCJ;D5bU7L26}ay zF9bjM0OWm1Ao>6*BK&HtwoOBWueI2fo{G7Y(GD|!_MzfV9ur=<&-+oRNRfybM70FE ziI3L556BV<%TDstB!_UPon6HAw*b{&kueNsC+=#&J+)243^;t8PopRU4eb)@)UjTC z%|J@gDtLqz=z5jdArpDBF8$;L=m(uEBXxr?n&v3{9kTU@&#yiW%YPB)RIU}%aSn`6 z$@EM;F;6}0Oe=&L&gfL&?rfC)Kx@IRPdd3jy;|W(cPJI&mJ)b22%#Jh)6+MBXi}{R zv^IAae*Q9Ff|}Y>L3KPUWC=0h^@i;U8!M>_cS{w^1mL3n#)V zzLDJBVg}IArNIql9*}a_j5k%x5~ySF{kx7~rG&ilzkAtDE&P%=41?qbzUVW>mJ;wI zG5?8dPhnkm~3cU8v`qiyh&L1E1^VPh=!%X+Uo>1c96Q;$2#!T1Ajyyr?xG>dq*93%MpnA#<7B$B#7=HPXzf=n$eqoJt`+9|FBhvLb+Wa z4m8GHx>=pcMvH?ROyEX%6zNvTMAD1qZ;AsG_0HNgMRs*xMPr|7Ah1x>6n>WIU!Rbx zAYDQVirff^+o%FmVd0B_;=cS=Pb5fBM{XhmuA5{$CX^gd>K>tNd;Lue-*M39)i8u$ zvloM|Alu~~`DW*t3*x9MP(pP*a$yx_Za4IsuM$&kOP znIjBTyD&_q?33=(F8vwuz4}#@VC5b=BR^1qta#WB)w-2XWN|LD`9AlpS}&US6%rj_ zR)6|i3w@-sbdLY*wIZzMyd+h(eZ#``O&@Bi9YU38yi!ozx7p}(2j2!@LD^z z=Hq^=#||B`(#WvR3+)d*sr80BN|Ky6Jt`#Qjwg11 zG(HT7qi~b5*RMzyF*&HHxNqS2WkJBe>I_J0^)kQLmlNmelxf#>?%GJIl_lQcfQhMcCHR zpjs9>tRLYo;~E98pm1*t7SyL+0x}cVhI- z>CT#lG-N@6SO=jawi;8;(_?PT(9ie_1fvY;Jk2=I_w!E z!Y^R`3t#8*m?I|Ud>4es$FXWl2HUO$%~7*kxDsbkG4Q&Gd8^ez857WVF=K{GnKur# zV9TxY3P)fpjfiFra;dkVwPR>95jhb+kD|;*iA+l2Oqxik?B99KpfozgmzxwxSylWb zg)%DWt{5oQP7NgLljJDmH3}IPvoJ+PtxxycCnYT&69cDw>&}In&F09a^uTC0WeDa( zEL8Nxmcz5q4LfwxV%sU0hvQRh+z2C;vEp+E2B3SEF-f|#6-mSx*mK)c0$fDM7kPz8 z?`_-7=l0}C#Zht53SIt`Y4vfg!7WuL-bBA!&v`K(@{u2PXiuNAgvs0jjDCI?mYq<; z@mZQ{ZtFKytujvz#Oopf6!|7kA*r+I0ob}^W8~7^gRdfY+9S_F(zSHB!HwR(Y{(zI z-ibb7)VpopINsALOXkwt^<)cm?aV--LZ?;j*$ezC^n=3iBOB=!JGQ8>rYy~O6p6Wf zY~=*?XKaLp<&Qo6W*RX!e1xBb&9_ct3YV5z_iE#2JViml)_rvMZsp2wS_7iXxJvew%gf;mkQY%&1+`Gi*e*2*B>O@GO()_#LH6z(C{)jcjQ~2H z)FMk)q>Sp8;Wk^A>(}J1pqse|RN~jF+6{lt1bbson9)wiI+YmW7Np-sVNxH|T&AA! zBI7Xjs!)N);7)_r(h`BeuV_SgPbsHm*uRBUVktIpforWVBjVz-avd%1F&mvltBvF? zfNt|pMlEQ@*r7Zr@j1anSI{yWHPQ$!*)ikAEYb7Vw$0#qFN1VR2OI)KFA*m1z+qk`Qy*pW{`d{N@Nn-0){$edMYF#Lln)aUBU%x zpbeNn0tProp-?4C-fLh&EA7jUs3uXR>mE(WMi;sRvb?M`LI&#S!`abZ>*?LAUzBEv z;)Sf?7eJk&T&RX^Zw74e7XPe{@Ple&hu)^v@rLAWVA)heayJ-&0YhI9ste5a#M@pF z()}*Gekga)6xf{ah%_;p~T z+j{vjFu{}Ns1UWUeQeT)f!3d>d;a(X|5DX!wu&XZ9eRYc!uzZQ6r{8oI2ArhVA%G? zHyb=YT19dD63$YpPa%n8ND7_Z+Jr5NQ>dEfM3VIVW%dBxo*UEF9g+=Z` z3D|>we0$`qMMT%+#&?bKsMuGo8^3qSNM2?u$wL0_nc8UkL68&{gP*hNYcXSBRb%cB?pVTSk*kfIOciI=QQrZ1JZwiYyN9#?{qgO7Q!32 zgX+p(BAS0u%GTgED?@bG%^)gzHm;AuU5;tPf-`#gsCDOP-I(3&c+iFWwqT)~_?WRs z0IY9YJeXjU!Nm%OqKuR|k8Mk;_D%MBlM=Kp?lshdEZwvMKMFR{C5D4la_j_TyeaQ~ zdSvtTk@H$=sJHwFks8_|tO%{fojwPmtKj`Q1zQ>HauCfT53_ze)l zTG-M87<=xxy| zDdO)&IMC;(lZM18FVB?v=R|Rw@)!k9^%zF2N_oFCDrd~Y_ws}mz~dKX%-kV41cU}} zQ~qUWCv|=_P_%uplL?G&6J|d>Wk_c3gKFN@F)jA%#ii3cI4UcpfE7lu4V5L?>N`$! zk)h#WZ(15(Finwk1ceGKs3lJx3!EAjUatNdO{TJTR0f@n1S1an1=2=8TU1Ml9{F^EsNZr(g5=z%U97>sgM zril2uR`W@#-Wt5t4Bn5Yz{|T;kcFdy!DE^@u598ty3OaS54s~Hb)tkY7zz6}Z_G@k z&5BO9g?I?$$5+Ud9=`SC0y?M!A2=yUZ(a`GKLJ%Ec-W*#J(z zal~$;zmv0W6y8{yxu3p}rN~roYmS7RdYm}J=#D391J6{cb%T#4)$PQp>Q8-uV-c7&nmY~uoMX$~7PY5dy=uY?@pM1GFC@wI|v|Qrw-=$Sf4{wk5&4_=sF>gnp z*P({nvArrS(l#^E8wXB^60 zjj8eIprA~2PY#gR{Q)B%m?ITG#X@32;je#;)B6g}9@Lo{@=*J&tl^#@&d70hV zqvdqNZSrNvD`pj@qo;n?u+SB3dYiht9J6DcMtae}KQt|F%fb$wYUmT-k7u?}UG8yl z)Fn}2q?zp*uBGX@u7bNWI76Nt7RMm)!sbX2Hz;8bW%E3gv$UWV_F%`6i4Cp7qpcfJ zDggycgt){-@q3Xf(|fbVc=5I>92_~)!?urM`!cFbfKnO~Et7=kL&!+Ci3&hjX#21i zKFjJr(e$x^2(e2@eFplc?uR%6Bo=N#WU7i-P3r}$20vvC5=maef9!lE`8^MhF~c2C zpe=9m1d%QT;koR$`WI=uIaOv;*&wjp4F`WIs*eFc#p^<+tI9=knDS`Y5Hk`w5F|r_ z4?}k75;f>g@CXGS58Xp^u#Y!M9~*|c8HAWY>=({SS*)Ox9&@4z<~uD-@;AQcA~6`) znp0N7D_`!W=)@bxJMyWUz#U*pQ{cN0!i%$t+J2M;9RU6#E3;dfkcw9t9*NT*lcI1S zbVTz`ZG|Ev(sHZt5`F5KoNfAh|<`q^eO8loN$OjJIl2#PXtQA)~wGv&f^-Al_TjJ58Pa+M5kmz-NhD0 z>XD-aM~}AOprfr!hqfUw;f(eLw$1NUyo!L*Yc&h>8ZR3PcRsr zpYsNmhGRf-y508v%`$L8SaCUt#Le-|`Pk(FB`->6b$q*QiU>;5;ZO^-`(W`&3^SQ( zkqH=nN4>YBjf+!y{$c`$oM{CvIf05nmqxq36o*w@|2|2@sQgRAPEnrIYoiG6NcTuA zi20@ezU2fusTA{G1B8BuLkp+2=rSrPB@K@xP~VI_i<*3sk11&W&=Hk2t3r5-zDpV6 z#dQ?z6_e_cU_h5fCw*a;JR+eAljWPV_Vci#Oh=B8idNeaXLW~$1j{iF5rJu`*b1F% zh*c0OefvNb3TPm=QtqJnS&kg0IhUac=EH`4_JOdO2>dyQq`rdoW9z5}NrSU|aEVe@ z!0U9?EzH~X@v58!f-M3vXUndSwO;G6qI#e7_sY;FZ`~pD{4qHs6Dq@w0jvTvuB-~N z8+2+lf)Uo1oXzp{W-SR*n2#9tSW9am$`FVl_l@Qnkpcu$B>@qN%5&yQ1Sw+BnKemL zRfpwW%f=D?SAe7)%1{97X=s}IQA|YiL6S9K$N>{4hvtXo3ypJsGLwUJwmpXvvPb`i zPkFFE0I#G&1qC%RlILTgZcE(q9+YC<%6We|>5Vf%t>CBZCH(2j~p;r3-+a*1_ko zbDXT3(;;8uXXy6+1Dk)LQsHjW_wQy>RZ=1Ndb*^$3dPZD;?iXgYVT4mXTRmuV@H@d z+u^8>gmn-Ztx&?PG9OW)by86jFo4ZHASsxOGZ=Hk?0FLtV$3cds2baN$3E4A#Cl31p{Ux18pUuLY!{ z4`cJ3-aWj(HRT`W2eeMg9XCNOM0LZ3*_F@?(ptb*MXl6wMq(2O8`(E*p^_64!N@mh zN}T6Iy|eL?DEPiQ3hfe{h(y80^dA*EwBR9&WeP}~^-1)Q!~NsxR;~NduFokawu-+X zBk?;o@e$fU1Ti{AzikyOdXzd22eX9kBS`pQkdEjn{K^EqmgG`{$d@+XqZ9O6SY_gu zVF`tjkVmDrsCq}^dc~hYd`tGM!y0j&M8QMw%5XSu{5J^=s>#z|3VD@{Gx!}uptysk zT-+YXFP4p2TEnMWl(`?Zi-2;tKPjKmJ|@->q=`h8(^8lcI;rt9Vh4rL1X0bU&<>to zQ6;sD%}9Rgx_URn9|V~;>{Y$#W1I~`l^ZP`I}3}K2ERDD$UwHe2|PEk(Z?gSX5)<+ zdUVERMQ8fU8wU?*Omoc^6-f@ZzMlOCCI4JZ6pFU7w%(&U3w2ffD{wNRM)kBsFp1D~ z$hptcdV!tgO9it8id@_=mRh|S1`n@*{P87e8yPYawPY3Ej4zfgPmjpJt2xkQ)}yWE z8!BwmbeSH$?$nPCXocC}BuHU>8G_#JzpON-o8dHDrRT}GC=zG4n-7RYj5gxvKZ=Te zSOn$?;)Y`Oh+*oP4+?!cN|V?jhT*7k+1UwXf3vmw_`8RK38Xw0v`a;iv1{x~`@aLM%hM*qtStGVzXCYf`q* z_(Exk=MfFjEUpAv%V>G@&>gR|FJndsyiouJU(}m+h$7w~k3( zW%y9pi}!Z98ob(Mvpx~OfountwA-jxjjOYhbyE7{fri?p4n@6qdH^jr7&38fVczz`O5|rS zdy!`@=)KgM`o`*xTGX6Xu3ZvA3j2C&@tIF-vj3*NrQ~{bnX;X!<-Ae3z#`X$V(A?- zR>Eba34!GF`jUademjbn#TO6DETFmI1 zzS4Ag!l8Mt{T_^WuF)6(;xNHm4}e?OJGCJrNUFcL`Kh&jmc&pBdHbLT;X{(%Yck+$ z9rjdgp4HO5J=y1e6o0fXPkuh0x`e&vK^jbN zLp|T>34R?^3!C<1=U?}@-t=y2v*M`L27Wk8BFOxfx|1;Xni@||$FAh)b)?sBW> zzw>aD<;V80(-5HXqbXyvg-F(qA6|AbNFJ@SK>r2 z1KK76v~3*m5M?RO@~rZr4@<>T$Pxjuw=^e(_#E?V8&W8b5hz8G9Og?S%wxe24~VR& z0*ZpRTVmJdRbj=qb<5uLm(abvLXYTU9@-jw)?ms&mfc8AE!QY0D)J>g-lmy@O#5rY z6WLsH{weaGczE8jONV{}7m$23_L)sEBHTLA?Zbb6s1(3*q~4x|K72BGM_9-U=s9sU39y!~V5p@k##Z1v$ zRm8R`n7%GrkuQ9-DMesZFZqp1B@nB$^Rq%jm}XzRNYPx9EK!;LbE>VkX}0H7VYmtx zJjuxDl_{Gm<0co4N93{5g1C}PR|$ebo?XxyrGGPoPNS1T35K!QkOYXJjNv~{hQ<}) zj=PwUzrPmNOe$M3S>%bIQ{zQ?gB@@uBh3V44xG940Al0GE|aM6Jr(w5h1=03lZIFbBq;fVp3GD+(ARJ!+=|3t4d~)LXIZ2?0`BfXcHj8 zbFHKWn9noh6O;9%f2%6a{o=6@ySg)Fj7Dl80r{ry(Q=;~OrOv@ysCr@xCg4Q?h) z0>WslwOatjzulyT&7q=aiqW`VEU)869Tu$`L`7jXD3k3&LeBAPXqa?S`Pd|7 z2qFA79}#)cd|QZvZPO?h+Y&M#*`{8bO5oYngy#14(vLt|k0Chlj3L@1ZEP_ANPmHY|$QXQ!wD`4GueT7t zb9DaP`^6}`7+hfI+Lt3byh=*|2RmW|5RYL%|k;X#f~6nsc z*CEiAl#o!);6?bZ&&7Cuw=)?`YsI9rCORFy;ceZau=(}DK+fzi?8WFD6_MBMG$ml= zMsh-4ss&nJ$hgT~NSX41@Jwctel6t^3f!aS7D~w?`X92Uy{}4vADR1Y?ObuRR)4U} z2pv1}O4qjvl5YamQNHtoGN&HSZttO^zz9Oa6hS-=n2);DK{SzE6Q+vde1;^FCjSC9$*dy_*- zJ%hTbBmFU~CdErX%Nyeb$#OsI&ESCeA;@k@I4(q&7^1U1`s(G-VP}*LfJS{r7`{#t z3XBp#j3T)A zE{aoA15z}9lo-8(YRQ(SblP(l(>v_To=WdGwoOA(@uxpNPV2il0IpNJ2f3e-`Bpo!hL?RGM5E3eh8=8p>5^l_lXR9EPYY1}o z(k*0k1kU9Jyl--}Xw&XwA1P8^Q?cdv!cZY&l&Kq>B9GCGmdj4wHT^9dwMXYPap)$` zHcW`T%JL;fA%H>*c_mB?l#JLN?qHDW%PHjlUn{q>GpoUxp}-?hslNMUVKQVajYo`7 z>$&QaAbR9@gn)v*X_q1S^FTc3n^;^>(C45_gJ;x8ksNA!J8?Eww{X(y5t1#x)f`Qv z$afQ#`DUDiAP+HE#XzFQfSdoe-ssF`yXbms&A6+g4ZQu2BGnb5t5;(%?va?q$&kRJ6O8P9QtkTz$f0HLozGu3sL1T)XQ$jv*TKZZcy0*t| zK_TQs!%2>%4P>HGk!Wh`(xKdSBv*e;=wIYw7-Vd3f_575 z(1=MApsGiLJ4hjLR@)szko>7!=Mo)iqa96vMJ&dRf?a3#D;$evQ z{_YY+Q+@rn5PCc^9*jnFAMTfUSH-g22#!1STP2Pao1A(Ln%MXc8bY?jv~j`xipY2wT{IOb13X&AJk-5nTR+wl5td2i1=+j94+tN z#ltppQ4jMkmI!9MfaNY_6h(w`qsE!^;@090RmQ!EZH8N8Qs0vKiosb!dcr~y0z;3Y zc?m2$yi;?v#SgG}?w`?N$lDPxJUGnrqzyF6ECSA6iHE zMmXjfI#M|SwM2gyozz_z3C})%JT?s!dVF)l`84z(f|d!j{UQ}Ap@rBDEw3W{Itg{I zNJZsRdQPFi!zloCuI^&>(+Blj{~CtNs_W>xFkZX125*_wJ98t$i=ehjc`5@(yd(2u zT?>W>QqvI(U(%#Yz#1J9RBWcyAngI(;j%jXs@elcsgk zjas-ld1lL{O~fH~9q|_tC9}!DV`;gM=*! z8ip;mpc5sz9uI7RwZ8;>dJ+ele$aWeoXuWdAdG)CWRFuFEcP@LxmdwxSkc?z&}UJ_ z08WXvLj!wjn}~#TCX9NPIc`2z*W@bg%&xvOIewG`y0STb1mq~gp%uS^6(Q2#as80L z|18VSW315517}JcsqYkA`{6di;aW;2wkA=R*}KLiI|h=(ZGMB;EvE)S-hI2->&k0% z9XqG;&yK?V5qPfiI~0EURzMh8%w+%yGtpQbwTJUzWxcJ04&k#-5q-L>x4-B58gbL6 z2xm7dvGamFUVE4Zr@ae^f-=YsOjlm-GtAO}f{z+x7G{VW%aDvWBS9C{t6kOzj6H0^ z8YEmZmqmb$bHtEg+s8(GP#b=%AwIf3^lBpJg*Iv)ludv@gk@!u2{OHFA6|f=Fq7aj zD+OB~lm_FIcUcWY;}m@2*m(lKDEH|8!o1JKb|~q19`#wLQ_GD~ON#)q2!G}Hvt*)$ zd9t^xsn0=5lknsVSWEoU0229mEB7LcH>W7Vgsl%_@8?~uWwUD} z`XxhMRw~@(gYFi7+syt*GUAJxp0gKYG=_J&X?gwDFQyc*lF^iqR$g!<7wKhv-j6q& zzvr-n4l-w3hE0T=>}pxf__W3O`L&E&t$3^wrU9$^^ zTq~O8NYqYbldSWw*?>enK`TBbRn4&WcxtJ4QS?lHx}AtuYG_I?@`rj4X*rCV_~hukuD?XojV7i&{J2ZIr-*=BAMJ&k0JU9NIq# zkz0mMp78F9fe^?!Lg>!&0Zv9yf1mgsQlc6Q2-;;B1cw%=UqR+R=4DvR@&Cl2mBVKp z^$`k`%+4)*RPDpZ+$`m!LPH4&7pOZJ^plAKLhYLIT;iCK$q`45h2sKPP+o4cvJ{4+ zpZ%hK0QCWZEa(A+(-JPhPI>g+A@NBZ4C1@Z-ovz)*y?$kP0pSY@G|23zIIL@AFT2F zs-71oJ&Y}5MHOWGq@sArAoRIn$v&m}RBSsfUX8-fT)OITeMh~nx83g&vx-Oqcgs|* z0bOZp(4vsA!q{KcO(H5w3TQmzrO>)0VYDJ+$~Uf)iS6H$2*$^fsf}xz&Yd&Y5X0HZ zjHgQtaD};It7$bx3Z?b+Fq}>o!)(VO$Jw!?$W@^;heX|Rh=zOW3}!StFr>yb+lI=g zJcd3Yp$`6a*px@(a0;3x=(&u1`w?jX71o9Wt9FhHFEp(_D{=3x62uA}6M*ayf6r`9 z{auu7q^{SrEDhaj2Rnth^rvap#Bh}zQhGPu7Cg6vIMx20KW7#nSo9ih-fDL||8rD| z?F30se51-f=q|`|T*15_ITLh-woarjY*hr4YRGl)Q{BK8@AEZqf4Nti}!Cu+IxrT8t+nm2+GO*-^Y=+7-}W$WHpXp&=F_>|8~SXJ;k>(5GYwS}>~9;4YWl$R5|{36(|VO1 zwA-mm_p+urSKUi)o32KYVnVxTZ^R6m7W2CBzih2-%sCYD18CZgOx?(EU;#>TVzC z00(zo?At;%HQ60Bfd^w)H!PbA>p26=*O9x30bYiwULWM8Z1)w>k0~~hV*-x2hl`^5 zwvGQLmgWW69OCf}RVH|!GS^Kqj3uFc*8R z>e>_(uv`W0+l#JF-(pIhARC;Vf_Ng2GxaJ;u7u6$exj3mrNpQ&j8R5-_%w#@_dyFn zvfSFh;%61eB05sSi z`Yhwg!&_DQtF z@0MJfCj_nYMS;n0llhGVkt;VYD^)vdca2fi&Jxmb>Q(!TcrtN+d|{4d!pqNB58zvq zN6-gHE(cK#CVr}E+uMbADdD5Fx1CzLaF1G$h-i^8M~qM+U23HtrBU;fPGThCE3r#% zopji+n%!Bnw33WI6yuFBU6F8W<0iVBzZHiZWi_U8T>yt@>h4K-BC1D$QCEsYhW~%%K(pj127tbyQhk7Ay!gYzjdO6Jt%k64wTo!kNfR0(2(dmneO zNT(;B$nIq^p)NRYG&JB=)I$JLR%< zzmjY5$0?7q491IWEL@6lbW(tFH3cm-iZR96WL+7riuoI&%Wvc%f~Rk&UVc2OqyLh0 zt)zq%Ry*TI#p1L$g8ypa{k};(6X(P$bCI95$H>}a^Py)5qYzY!9`U4vuN1P2rcC?$ zlVNL5_VeCzjsC-y)gptp;v=bE95bAGZY=oqD|OdI`#wjEs&x1K_?Vh-aSb&0BW~pF zs_jI6Q42NGbW9u1-kcK!^Cb(GHYHzs2!5ZWm;*f(d>Rf96ldZ=5^gw|n50nHT?n#+ zm;B|@@%4;pV=36ej{7<&-t{k{6hYExI-_M{D1Igphg@gvS5->f7_GdMA|ZD`{{(7& znEZjFK$xuM77w{$+D~*8T*P3WT1s#b5Q4u3&1k}6%e}2$Kk#&_wV}x|e-b-#^-6Fz zYTo-I_g zT!2Be5zcJp=#oOI`tRcwDTDphmGbYOy+Sz4xg5n@({V^nWI{v3uHv~MNTwqAD3yoo zXuN)7AcX>t?kRET5$a=B0h5q9xBQG;s!LDHZ2bYy^Icm_ej+o+SP5`$Jv1f%z~3yf zP$(J&Gv_JQaf`vy|1lauI~cJY`u7{0h;ONdWBoh;0Zu|S9*(5HDdOq;z-DAQ83$ua z$3$3P{qZ%b;Tr8TR6eMpX;~)9WQyE7>E&uHhlxf)j?>=2#ILCvT8Y37Yr(th(MYRWZ!h1J(B(s@fbpan5 zN!;*SXL=%wfQf*u8edjrRe}VIxd)(`@`S8pv<^cB3GPr~O5j%vV+_XR*J?o$HB+kn z4Y9}N78Xe-Kgh_5F}hK3)kB?}_`hl5D_2M)#Dg!nVO|fcgZS;a%r)26Q2> z5s+VrrE-t79bfCeEzP8gG@&>rv>9OLf`*wCd+8eHPnwf^d1b6*BBP#@uy{NcJURbR zn?^PGElmeWUbqANIGDFOsRx{weXt5hSaGCZ5!UuYo_#03-SBZvVyOHi@C7fKc={u! zy4obhWSV$($=o?lSk|VBEosrdiomxzXx0$?t32;oPxD`smBja5{XM|GkytzG7HB+i zI+_xONpRW*Wd-t^I!(3t7vo7RQW9G!Ly6#|(XcAj8qJ;fwg=fURXgNm3T~Jf)b?{AxFghlwu)YxhxEJiZS)NI7FL&!Il2W z_|u~DS1!2t%?WR4WaN05$M-KE7P>R_b}bE5?Q~_J7SKG$*`2s}@rt`P6VF%tDnv(# zFb5Oy28(nbPf?AV@MPu!z;Cr6lx{K#EY5&jGQ`6&(#r#JWGyDOXM1CKL7XH!)0WSWHc&>o0D5 zS0bJEzjr@awn>pb_vpmH0}$;w3^y;zi#CF!#oTN1wYo5-P zBKPi8elw+db`nlW#MhUR`Gybz1|~kx)*uH6Wzad z+4w^?sTHI3FOWV(vrBcNKzGJ*RG`C3rwb)b3H zG2>8)%R{9^uPtgBJe49tAcmer5+`{{ckMtKLJJ}L`+>$>9w!FziW(a1tEOp!jk`8- ziUe|c5+g``wWAGqkR+FCJMleG!nIX)1Exf!WgJwMv=+^n(5_Xq)Sv@`bj(;%W)Gzc z@2ZB@YYM(l#Z<}C#p@me^!LN74(|KfT%uUcU|}+(B_v$!tp1Ij*ivQ!BtjAZ7^_ZW zOr<@(=633BJO%nWl+>z3PW^{!OSd>f(E@ozDI;uR>SxQS=K;IGAvIp9NAeyXR&TQA zszK87!&H|)M~H~41*VL%r0>+ZHg4H8u5s|WOK6Tf0x0}ee<|?ixzaq?qNg0;gBD_S zA(=kCH%5uabf_=}GKd!2$Hm|v=pM*BBGu$WN8UeUKFk(Gu)XRKFBbyA5bdb9su7m6 z&HoE9K+nHtmRW0-n>^F2HS2=1!7d-&=XPeK!D&joa2^FQ1^fOmsnrrI8pg#BK6(W`PW8j-?^%>Y%1# zJ?EQ-4xVGt)JO^*IJ8ZpC%76145J*l%rM_c)PW==CPc^UnFSlp1Zig~W&`_FpnF1Xi-ZmVYk(M)eBG z?*xE7f!3hW&5p7p?Q*68}WEeih55*V?c8|1V$59nxh+M6$Er*@mi zJXApP#GbfKPF`P$tQWePqVvkuTI#?in8t{3n!IC%v?}j4r2w!9kASC#R=ij+*9OHG z#-mmxq*0CxB=RJDD0w~`DJD0d)6Y1526{m8RLF~s$q&f?Eg3~%@3_}Mp{;>m*~d5x zoZNOGoqVK!^*FDEN9}TgK*FJ@=_DSdb4rO|99j7}i zg2nv#36Zvh+*I&0=IS9z8w?l?ItCn>+5A{|YTrTa@BDjBwGKeFmbB{yd@O+>t25QCl;N0D7+GD{+rcr@YAL>3O#8Ao8#IgKqSs++?_8G5&SD8{oeu=_d^ zPQH8nD;}21YI&})RXV>w;%I=wYD<|FyXHY^?LKFo-x=#7y?7wKIv3- z^qm1Qe@X)2nhgT%=@9hxADhYWm^{Tc@-FZ!qeoY1fk_A4>jqT()5WL8QpDkH*#t3V z^q6CIQ=9(-bT*R}(w0_YQ)=so&l84Kl+Z5n_IM4D?fNXDU3A8N-eIYMzQd4^ov#`b z=OMNrM+ovoct55A6Xn^vCn>bwjWsr@k4zjGJVJ*ReuHoK9v2Q2k`mb`A}H-Rl?HqUD-6VE}d{ zKiY)If#boCCP?xG(~-F)BEZ^#M6w8VRAdwTF}}APoU|_`X>tS2)FX#}h+&5MjMjD_ zNb#H_>vxTmnK@S6zz3gUX{Kpb!u(?ki2ZQLB(z3*C~FZY%k+?>R6`9}a17CzKq3IY z6og`t1{o-1@G2?dYR}K$O(bYXbAjQ}KI5~Pqd(1cX102Xv!a@YQ0^N~#8EJ8PR60Z&V|tu8sG~O zUg01sgSE;DQ>mer!Ua2@c@G^BO&6vD@JGmi z&U46(LZ0n^Cm*K{l&cM()za{B2i_ zza!H;u&@;2AN1^9oaU4d1gFo9wWGCeFu5eYJeffpbny^_WC#XJ0Az(?c(*5u!ww*2 z>4*TRoV`h4lCeIr_;@H>rQhFv7}IeGP#9+H$ufm90V#rx)8afQ7Sk}Jj=ZAuQdNny zrWg}qxG6*Hz%)puO@?vnTI;SMggHx7pQ*lXs2EJt0_EYo7q10Uj)2(Y7Mn$zM0 z2;K!2GTt_#I{tVG*R7UlY{@JXLCXhHjyR5jquHnq%~}aRseT#fK(n8n7gEsrC|t9Y zeQwgw{od@g)ecMG4f=c`u!$W98mz;RR17*_1`sMe6pt1vuof<`Rq6V{GN8pd>>HUc#MOtPD5%F% zRl!K!W7Fk2A||J}`DHS*>7KUI?Vov+c2P`yJ4_5MQ4$6eKwPqOdmn zV5adY8IlxSSb6$&EFypH8%8qJNf`X8ODmSwVUgNf07D@1u`==`G1{lR)nCn*?Uaze z8ERJpU?O{DDgeEP3u+nP(dnk&8#Nh(@(X06EOCgvgMvge;pb%p$82x+-$;n}lc5hp zpG$z+hc#3mp?-|6fOKsTDN`FHP^?NB*PUqO*%1{BycWECs%9*x09AB^as8SPBrK=W2-Zg zeLhUvw{SegHUv^P*pRj|RI9YJEHbq?Ik3&E3*mcMp;4|kJ_Bkh?XXo*kz9jEw%|O> zAdP*cBGgJ0uz2SQmQ0E}jenNSVxtW1dv@lN9q4kNGh`W~&}NT9s@F#3veFQcWS1y` zA_lDmAZ+3-4aow?Kq??1S3;p;E5vHNBm@9?+>D8%mIOHPL?$WL5dLlAqP=Q83Q;yu zS{b-J7yI6|9OiA4X@erlLErB|?E4i*3?#}l>`N$&p8gV=Pvqr?ED=fjrWz>1E z6FUJJmx8-a{V8)|W_~tK!M1E{FWA%5M5f8uw@Dd8EY07aYO(d)}rCQOWY65heABPXqQErYW-2fDnrkO ztE2rPTq!g!0x0Atth5e&kuT<(yv#_BF(!)`^SNmJ#{k`<*_prG*ZZNUVx-d-uMkDp zqEKQI!9SFjt0+Qtg)D(CiD&TKLOfrp4g}VXzzU~20OcdVBM3yKcE_5dW@g&?l+>7{ zIv^^qF0z7I(G0j-EA8yVXg&h}`xcAvUJz~!1AmeAS2x5(3a!zyC&<5RnWQK-hqOd_ zc&(bTi8g`G!B9S3vE>@j!HHKS)Cp5?@`OBIP{t;Eh`m;7d7&DDdR06-zI@Q&Zv-Q6 z{oV+P!PH+yFCt{2@6g%lc(b9)+5om{bif=Jxh)rOjZS!2`BEG>Gcw_ZNM5K%vaD(tF!1aj%Rtq_uY^j?pqW2L}L|!!!mNkhB4gzT$Kjv@yA= zJwzG=JTL{22aiBJS5s73{;d*vfJdsGM)K*(8akWp3Y}5?>v&b&zt{&0_g|ruU3^hPfd@fw*3_UfnMaL&{H+@!#6amQ70ET-< zu|Ypz1`Fs?6q8c@vmF*bieE)i2%3jEB6eIxnYLdXs1Ypzl<5;IWn&Y#J>jBb*0aw# zs58CR#-X+&j1K(EE-YHLf{8VZe`mqWH?1F!a9p_HrTLM<2Dz}*rq39~1`Q$QRL-C%0vP5VD zRJBqG!^prX8%vOQ8Rl>)Y*PKEMEU0X1_6a1L<0{AEQ-YAIDy89oQcuUb}=VR@rBu8 zxS^a4jNSU>db0Cx46A4zlb0|pv~5w4(c?Y5GGSaDXCX!{au9dzE*%e(k-{o;TUrAT z?EJxOx1|o@G_ipNNf%>syK^T4yFdxqVnuN^N4mazcURzTMGoA%!Qlgre8$qF+&32E zmkbg_VtL~+4@!v(%fsYHoQpl|MfFJc(u-m!lnD4mQvMeM{-EE5VUY#LUo|A1)_fqy z4e46XLQ%odYP%q#{E9P%MIfveEH?7bM{63%dxtUDP6Pti6c6&Ic?%n#Vdik-WhiVY zI1v_rMF!~t6aU1NDHo8)**-``MT3o*Cj=*f;-8UE;caqdzezL2pO{6hFHn3kOji;( z4EIkc;b@F){zhYjuyu&-O=+d7{`fV5Vs^gS}r zSlnz8Ufy^}Z1`vtnigWm!4?Xime#mJM~<5aKp>h-1zL~HA9X?et-KMkR!ZBBSEup} z<0}P0xUD5UK^yKajIh)6%pnU3$6^cnUjs^(WJkRmGGqQn|94Rz9JC3vPHbpaH}2+m z;UNGc>@|wGTc zn*CC)q?r!38f)2vsgP0}p({#+tte3(dAODUxSkY_Xp6WM(ycQlk>? zi90?Q2y`8f__Bj69I2m_C6sx+$`Ci73zahi4QQ#f7PvCCC--9`@nmIR8rm3^al&0+?ciPZVSfYtY_kBWwX) zp6!T*Elqhf2}~d$8UgO(P0b9H5-m$5i?4DAMEqWaKU51A8=pheK>-U2!brk25D-jZ zlt!DGCN4@pZHe4wRFY$vCjp@%m`2U*lR~5YgMq$kDT+Gx%+D)Pl*Kww`z8%2&`4$& z;gM`8E+{mJ79N7i?emDeL75VTddW}~l79wxVj=@)O1g*oiONH*B7l$$y;QYF{U(f> zbN(Gh22oA$&m}bHx+8Rjz-V4F>1U-sch#wX4$9!Kzf5y?qR6C`%nZ>}i}kNDb=8MW z&@a*la2TgL*_*dnu}`!`tjs3A4frq7=1b0>#>CJTQ;TuLj;|$=Zs#f^#Eso-jzS$n z_#5!N4U<;jYQLfw*}|AGJSzorKs?F-nS@Mo2Cgtjfd;|)WyyXl#t9AVro(Ji)cy#C zI*Tm3cyJh71DShm3fl-!FhCYgK3#Ij0GMny<3MrthIShbB%$A#=jA#HrY>sg)ScIG z>%2(!sh#7(gR&Kv>OZ1q8Sy~2k{-pOw?&-2w*&!cc>&HmLJI@LA&hvKQ3rw;t$`5v zDM*QOIQTChL~kTeu@e*oe=}fE4M$fJA?WR$j+b2PnAyXL(~Vfi`fRoplMeQJ8|Z48UpB~H_8y!d!9pe^6HHD1aUz1_pVYE?jJ+3wcV#7-iw5}o<8 z&AS4Hqy}IF1q{@n(RIvtR6r~&ga8N*@PIlq++i^l|0TDP=;Hq{UyzJ1OVA?6n0 z4QlwkniuXNq0ABZ=3(Ppe^{zWhR61~>Ga27j`Gh254B8-5?STtj!x0X&@q<+fDe)I zaFC3whx5$L`U8{1!ImV2V7Ukv0HLU&fWmrCtO=I2{4MEXZUW% z>9&DLp7LW-HLm7|q{-=nhk~AF6Uzu9Nc$}fQ7bZ)bmUmWU$Hcst&8(uYZeln08gBQ zNRYG0F+E}(L%f@lr$~e7laWe?ngZ6Ds&l|Oe4)ol>_v$V8oJi=6}sJ`EHD946S7pG zs{9ZZr*dt~6UahCj`Op3_JBwW-Q3Bx z|2mRHEuG2CBLVydoBRbJs&_OEv%Wc{5qVaKF18Lc)8n72VHMq4pd}P_Ao+qtQk-mH7em4XOK1+uveEcxLlJ9YyE+iI{!6(Zpc#W~ z%a(LBj{H92-)(`>k@G)^M(jDoLS`@#rbmtnbE)AMo)UTE9rs6T`Fo>R8Tt4bvx`{1(3U}|7q1)xk?AJ;`EsNSj zoot2O!X5_KVP^7>_5!!0H|+N7rH!CY!%5`+ELrOV^?*o~@zJcQuwG06Z&tI-HhTsc z{HWxvNl%VcCoL?if#}y70(3J$`vO8uHU5v75-j7>4w`m>&<7C{nO$X@v(ftV+O*RF)vL#5k^C_^Q%7jjvhR_`)>;Vm+FN|}p z)gymTb9zD5+%icdKC_YHs{l#h9$}Xif)Na9*4p^K@+qRX%9X%h#k+0}fpO6S!m_)2 zx#?$Kec=qO+g5YPdDNb+U4OQ6C0grZf2?JpM}Vk?5ugl9v4p9TqU(R zwehj_SZigl-5|e(BU4I7ot2wHR*M82NJvq#Hemw_Xa!TNSl3#@p-SQx!!Bh?;U2=7 z@7dSC57Ir9kjC3}RhAS{@d#5;1lAS-%N7?X#!ObJ0Q*{#tTKA}X@K(n=oZ40Z8w8j z-H`WFqR5_0%?P&?uV7fD7Ec!bHO2o|x_Vq&66q%du~yNeGg0!a>Cm6Um`808R+Vy0 zFcc69fue?5SA_LF0IxD)W+9-i;G^-Xx(;_@LU#@?kqaCzaFYoyp+cfr&4F^A(ku%? z6b?(lBjCjpw!f^kq;XMRRB{s&WiuQZ@C8d=aq;rB*j0$LOJL}5oV3T`iqZx-PFA*P zxGk`xy)Z(el4?S)0Ki~l*Ubb&k>#cW)6$Ia&5IF?khaEE(;Y?*!LU^}UtLKUw4t{* zc+q~-)bHIzLx@az>jYuL!j~kJaFKFvUR#Ptw#H8#MwEttL32Z4mJ-=K$}Y6L{*L7k zErl;};dP94!}>%8k|o{K%71cf!xyuL{1}bwW}&^qar3-BZKY%;;+f`ci;jQ$4CR^l z)Ya4}O@PFoWsHJW0C{#(t!RP_t`>p?-61{8QJO*~IGFe&CZ%I2zxRnz7+UWuaody- ze6`-on7{<}gW(jCawHQDlYK0-p<`#B58DL+Yl5)ZFcFHK=g5%Ihx58Q$b(o&9%6mCUc^N6v-aAsc ze7TH23DIau58oINcMYJz$zY9a#lDJxq(}hYYA@{%ZE*XTH3u+jmi# z*(?MSVWH2l(OGhB7(Znaj)rjuOi=dh)PIZ^c9TOu0Qv^LFaWl;!T@^PSg={7;ipP- zuK66IeGU`|=NLR{fJD)xb|)=a$8Q!APZ)r&Pl{eK&4c3FoiAJ}IC^goa(@a&XJ$y* zBU3yIMiVK^+^WzU*d{~CS!Q>^d|;i%U>&AFX#fjR(mdSox5_4DWD2m!X!?IkdWbo5U6=| zVPgD^i0w!^S(2L$NHLC>Y%%^q&e@Fk)Muh17!6Urj6@{4C=bT4U_BON11L58s4?PX zF>gdjJ+lvaLS<2FIbxZE+8HVvQCQu*xjBXz&tUJk*c!DIxB28dyFa)SVJTL3D*E5qWqDE7Z`i`Zd*P#PzBqVkyZ z5q%lpV%R|9YCX->J21*3l(8x(<>|n|+n(5AL8=bd1Ry}5wzdQOPW?S;wSfddz=AO+ z!7U^Bjn3$aR_-W+pLpTYsJ*&TzW2{|A>&*in$F9@WI@OArgp_)KHSg33^s( z5~`f2W7b3(+uN`9F+<@5e(Z;3i8qzYNWT|_tjG`ta71e>%F+7AVNV<6Y1}AA&v=Qvs%_gNXx=;*d6MyF0m?T?Un#o31OYwfPZID zZzNh_l4ob41SEtA6oCx7@U6ZIRZ^n0mlJ+8srg`Hxk>aaN5?3Sa|R2;Fj)4moM}UZ zEINtcya{S%&jwoJHO-jj#smn)wjD|WBYNOQlC58nohb2jW;kgbrh(W-)7%G?UyuRK zq#$@)8N|iVL4v!PW4=H@SyOn2@C5{mEGbK_y07%OMkOEMw_}S1z9K~+0eY|#i8L&r z`O$RIAgy_)#!?I{oEbyMwk#>y%Ly`D_c7-lEIxv6s@cGjum~#fakjfVOI#U6$FnS# z9LblHni{IC@p|&viO{*&-8yhv3?c^*I5y;d!(m?ftBs~fM6gn*^zmpW!m?BIcZ98y zTqmBGxINDRj1|tUYb{rhbEx^-$3jOeD1p&73z1b@8nXhKR@@6Nk?lHQ;uBp!ZM%lR zX)|>lLL}?SKA$WH=y@juIcC&!NIHkhOSXnQF*6fAANb7#OM0K-N#muPPZKP~#BHNVp!*5$Nou5LQxB$Zth)w9_gP8MVrYqkOc0 zkHJ$*X%k9xA2m3onQgoigKInz1YaP>Q0Z%VmU+=VfXd_X^0KA0ut4QcWJ^5hJ`6ua zuCpX!n_L+Hpv)nsrl<;kD+}s7la&>tnX#9|>Eg-?JD66St-s=I(J>+j%4L(%SpzF; zS>fk{L`;%*6VFrQ3Ob9LtAU*f7iP)Dxg*8$LpW0nngO&4DGN6Ga zz4D*cG5Y9&*aaW$)`_wl00W@7hzU=vjJ^jKrN|OdB_=|R$)IErcOzU3PXGzP91Hvi z1Hl^^bMsoP8b8*4*}h*`t?5K5o9(L2m_g(;hR6-;>4-nw1Y$essv5)r@mv=#!+mVN zy369O0e5E`5Do^y)Vq4weGDxy==KBE3$&*InScmzgD^d?bg~3>CN7J|hGT#TVq6_H>LXckc$bjRTuVCLUusB6cyzAmf)Ai!_ z#NL7-QejN*Es8S0`o8uSvn&U&yki0>-hGK8%rLOTKyd0wIP}F1=VeljySB4p zAC4tj&8X^{G3FU9TSGOf;e}0Tv1%pb3~bca5GaMH!j^hyKwv2Kkoa#D z;0KmE9^Cr~I>STVp^-DAxC0TX-;T}}5|Tj*&`S6NN=L#tauE?ESk}Y5B?#=6kBD_1 z?hI+lp^#}^Q@oV0SQ}71VqQ0ZWKiZx2cPjU$b?FL&64ep_D%dLZb(=#sQzpHc3_4q zOhFO*A~K*YaSpn7Q^k2$pduQ{R0s?AbcoR~WCYX27hsSq3kKuCmN9KIkwi;E^UrCo z6naP;$%&f&33H(+k6xX;W_o;%+j1sjpg`HqnUg@1&UA@RUDky%TBv-aSXR#SThC9Z zqE0FlL_fE&{ra&uWBs~jX6h&ozJOS-)u3kQ#;1c@bDs8CKdCQ!N)GOMNgPylAM5tB^Tg+x(7axuJy z94GC-zN&g^t1IzBVrkMB9GRjbPOmR0msE+i@AmGVDVox*h+UJysK8Q6=M6dl39=$S zs98&3*h(IP@Y3j|uAJ-d52&RW5E-^N#YWVn{i{27&cWY1_5isF1~i1p&!Ps62gUYd zyxX*Z73$wL|Fz8)_&gFPC#22_m*i9$rLK1YI6@mD*C{G-FlpZYw;i0twe}~AGSfQw z!C0U7L)gp|46XKQ2ep-=RAnwz&dX%Kk=HGRLSn&OW)TMJsy_rj{=1K*&{WXgo*Gc2 zn_nd;t5X*425l}ot30tixWqiA1b!O>c$yy8v)-dFG&L_|65kx4v;YrKVbDI5MHG^R z3el>MOrP7Pj_VrxAhHnyw9!6MCYp9Y1WKWQNh1Zq!Na3sjangyjt@GKro}*W!(I9< zGoj<@=PAKtkg`gB0Ul92Sa+2KJcXg)VL`sCP+QUac}1(GXjdOh0|Rh6EcQPvaEBBi z96an|jEZcYCz24@lz{N2E9Mw#5P;LjI&F=`q~&C7<<)zftjMP@-ieh?ELQcxyhY}# znQ;OSr;t7=q*m{7x~Y88brlsasSa|N%ZuqZnvZIfWvI|-gru{fY0`zn1&Uy9_%Flv zaahF3-!VeC_alhq|Hd7K$NqU#`$(ja5uK6goYrYc9T*cpY^LA_d#(g-s}_hO33!{W zu<;{BC^|VSP^6c|Mx%YvyHsRkzATp8cR(dvA_PUU;>Z~!pgDpzIf!)KvnNFQg2ht9 zM5x*Ffz4G3I?7qoSRr`TivVfRJHd zoJFkEZXfR_Xa$IP;eqzNtvG}ta$SJG&5q4E9gjFE`b*4zE`c%F9HiNZg=JB9(&1{0 zWyr5e$4?g5fi3p+E_BhcYfTh#xGL@-T5T6GH2&F@G&x9)s}12;tzbIaBnvJ$ICaP& ze^nu_1xDfs08>W02FLy635_!IVp;=mhx=QG(k_I zyz44f$^wBYtxB;?Q+L5tvdZh$lFC%@zB?seOIsPAd)7I%!%cw$0D5N!$csEp_%82T z7%1q7K9@w$*S3fTfD8*O_c9H!4uLR$?~8yH_N?EHi{OZ9Y6u7tNkB8xFye@Hy(f;E zy1z0c!an5ClOL9O*+xdH(g?FVCq4%2v4P>XWh({1DkWn~aTXvyP$$oZ`H1u^3@5_j z^`+Zb)|k^Jk!jyz6cunPNEhJ+e^=0dy~U?z$w;8q^|o69JE4ZgJ?kzX4v3@%!{UG6 zu8jx)Li+`<$4Jr70=lW!pVL;v42Vv@+hYx8p4PZTGK!^yK|7RV37)0~2@DJZdm(_Y zWJlV3VBKqk^aw#!Y6ZVl`Rw8zfFUKIMW*0MAmsXzCsH;$_L7IkIfemz5C8}r{r$5D zd{=>IW55BM`8323BGh@z_Wg;tF$51pm=?>I1e?->(hQ|5Q~@HSp6wiM@!z_77*y4n>&`>+j z06xsW@8mRfTozfzz zZ2VlioyxFOLUDBtNoW9stu=ZI4!wsq5=5lHqz<%jQa%WSQ`Dh2B7$2V*<%y{Bqxpr zSK58v zG`SZEQ=|FhA?yJWAsF#gP|xxo3%&nV;a#u9ktlmGOm__!Pz{@VFc|zlsp0ySPu9M? zeaA(C1_wjnsTOhtF-JbpXI+W;8kXGymUz#ppCbUharZ^hLiJ|XU6AwdX=E@`DCkYi z3=}IaC6LkaY~Mqf;N}WLQnyNY<~v!EXk*v|JTf7ph3gU?8Z$A`?Ib|sGDwT&^;jYf z@DX@RLt?)HeKs6-^j?MdWop25`Z*SF_ySTGf+sOT6k#+1Cdoz0C2SltLr1lF;7$^= z?_{OrkFfcWGFgmd(*g@hxl6Gk{Q-XpIj0_6N=__4;69cAsXC+(FRCEY!m+F99IQ-h z1HkwQFlgL2WujwMNFk-Q3r2G;=5^fQHnrRd1G`-$qwpTjGsy}kBbxZ1Dr*#^Ql3RQ ztw$2#r?j~|sOZDDgb;a??gQuu9g9|#=*5hMt?@;l<|9ZCj1 zEcQqS#+J4WAnm_GsU-apwifKKT0X_oO;%S{=_oixDKMnfR#Oy=sa^o1lAjj6pe#zD z(w>71(70IF1Ps95E?yfF;RSSxE~(cug}_ChZD73;>RsK;YhLDP99uish%65nL|wUk z?wifwh;p@{U>OP2NYG0V_h`krC&UzFK53YewW4tCLz~K}yAe7vj9t&o30)KecRGszp2)O(re$IL+ zTFc*{gB=R3l0c!5`xArP0!JG*7)Xp)xg(CFiId6ztZ9+lf*m;#X?Sd+9!5^XepPlm z*BBRwM;+;Lnu&1cW$STl2=-bVP+bvO?VH`;75SKt@9gK zP=cW+lc`mCkoPcV_vszRmD@ex;T!wypI}$sw zSGkxS?#QQ--pnkXWY5NRFV5JZXxqG^`-*(f^#8A^j*cg=Q%EwvQ`n(iguOCU;vEN- zU@zIu0Stu`e?$pkytDqWx9in z*8g$Cq2g$-73Ta+OPoY!HRt5%7`zn?w&ua|(q`eHe*@sk&k`J?f3S72vLk}OA5cI5 zg*}x#yD71X0Gc@0j*;{@`>Ay{JS;HKi`ejso$^(&<{_@iN#8Q2QNO{J1{d~yo_1Pt>@V3Of?LefzId^#%f zyI?dh=n-Xd$mZBb8^9jWI4Ic0Yprv6TnmL0!a^CP#1Dv;TJIV0?1yu8+3rAtP#o?tr>?)Kz|DPY8472R0<|)qKOh0N-uY? zS&<-XyFRE!FFIs42kXNOVLG+K5iKBhV;cT%dqH%71kDgp)& zsgH%$$>utLqrN0_%%VK`;T9?hB)#ddsz`*2dmc9sm|w;-jCV@k;dgQ5m`sG9am$^N zZD7LSP||v>+9wG9AU6Z}%(dV<5jE4cLHkZ%)wx3X&AUmByS}`;)eFW@-42@?xiAs$ zUD#%yNQ&~RHEfPg1B)$?mBQw74TAIh`(0_S0jCS01)VNl+_IwgHLH@%qQh~!1 z0m1J#M%#181prie;{Iw`tcURn`FnB)u=|+MfosUgz+FYVBR`nS(3$e`9#cn0$fCW-{J- zKV70+l`gtvv@?pyCR?*Lt6sBYMFG-59y7P=SB=e znfRUiJj{hf^3dX+Nh}7xaD@Sn6Ca&T(u;o*fYu$urJ>lL!}}XwE0sQaf0?B>Lyt2} zVy#S4W}<1IVC(V+brX(#pBBmxQVOkZ=N~UORTS^?L5OVy4q>5yH34u8o5L4QqBNrX z!^UL!N5JFLNH!*Ei|~J=ECL)M_I!Sm2%9@WW|fvo&?u1v;jBW>IiM{R?6#etr_OVI zIQU&g6E1zW?kwuekEum?T%FjO7V1Q*h_LxLugHDNzqf$Q$Ae5xLa)JzWGHe{CZCQR zy1M;5&tk?0$|yGqfA>VKQl`K!O_QSX`$k4-0vCsQb9_!QwD9RjUu6!ie^~`!zxDX+ zf`K`#*U1MwJ(tgaiC~Ts6ug;b&hl+0412lNDn~fqdp!GdQ=2xB48v0l#V=e z-Zzy}H!z6qYkF0QIkQl*QW0Hwl;>%)y%oUdn#@N04uw9;0I2{h>Kksto%Gz=xnhgB z(YeZSjkYBO3BdYSv<0h};;DWjja)bq&Nr`_1N|zs3hw- zBNC#^WvvX>*R>2&{Jngq>f=lOCRO2GkFp!K7B#3-DVb;Dqk;iwzE<{dn~!|EcjC445>}()P{b< zz^8$<1M&7iz-aM5WDn6INCyA~X0J`n1P*oSK4CzvaFP42tD@&CoV$h|wupoLVU1mn zM$rgRiW7j@v+q{ib}?Hy6%sR)N!DCD2d>M=Vw8qZwpj7u_l8XhK(`7YN%?hUOcx5z3~@%eZ%$4vBxE_@q%u#}-1&pb$uV$*w=4)7;V|ZE5$An? z{9I;)2{=%L3P7i6YKN9$XLEdik#MMHU1S`PDU>vzxV1ANl`#~+Z7z948>~;zO@QH~ zQz`Ok=3%}-%mDYofnd6^5xE}vgClw1%oVuSe(y4S6ro{UJSJtz&cq9*;l328SEN0J ziREB3u>~nC3&n$^XmHnHao*#Xk3C>C6drl7{t7X8TVMt$0>gh7W2y;UfzHci5^E{A zAjoDwhU<$3Nf$+sDx)#@<{^$4RrO=IWjOsz6tKiD`|7ptclbNuMTurBxGQk;8EI=7 zP{QGVgCKjDSi>VyS%65N60zB!ZF-~Khd}XW<;qT)1{FR!9p&*4P%4py_sRs4A)>S^ zE@m-VKUc z!OHht{0<^eb_VU1#JXr9c77(D7hEdo+{6e*O$7S@*M{{GUMNIvWD$AqQ z&=#rOB=m@f09RTZ$vHXq+2f3{Tg&lO6GQca64!0=Aw5UE$l1pJSEU4%g$TpG9kKHIqV!5 zgeI`@2h{R>Z3Njj-G~4Lv*!?(VmAOFbH2j73`2+{U>f<1lxjT|;a-gfDPi=*#Pf9ldF&jevss!IsT^wf9EB1|385PE*HNG`qdf@G z1_m(bjwjzQW&azHfE|co3j-|^%=7{`4EHyFl}=C>HYA&4^3g?+i*I=b%s}}^8mB;l zh_!__{Zdy3=!|9@UW4(FrDYKrMZC?tZl~{q+CodO8-*y(hRh4hOK$GguBQ!f+tM?Z z`M3v{_ok4+;-Zr=Dzi1bPOQ39yGDpO^@@jVf$N6EX1)nkqCTNH#!vSt^@eyqAre-M z#C&S)u>XXeEKi}tDL~`T#6OgH#$g>>YhBZsNLr<9Zb0yh+-2C&Ar_5e3SJ_h#+$_= zmV4BVq4~PWPuncYsg;H|!n}|+cpyoIM774v zO^--5^f&-+{-;gsBT{H`)h7P&H7s@2!yT4Rk%lk|bb(1`V2F2t#L9DrR)aF&m)D{6 z*h~Y;W8X>Q8#;~v^rqD_q#p-Jx8Jb1!bs+VfewgnX`Rp0clH>+LJJEFLX&Z(9s?%% zQRO$<@Xc-+H6Ui1JKUym+-IFW&|OG!B#+gRl#z+)cx(k3OdM@aCyS$}OF$98TO?6_ z#;Mk^JQGrumPEUJ6Voflg1Q%H&UF7YFA3A78q?qTf2xXD*gn#OI_j0tEiU?!{O$}O zWj`g-VXyO9eZ8}k^C`V$c2(JQ={2~wt0nNC44eFvtO}(PCTm!q6}7$mWRE} zw!{JyaK*sQQc$>zr+Mk(A*dC%a}1f|g@+12-H$_gG3_80Sk-6uWY=;5|z`tFl0=f;#mvlGQ?zli^lD$F? z4C6mPY;}ZO!ghjx((8e3Wq!ob4Yvh2R}FF`%K4=VT-FoBtPwG{hl2|uJp#RTG!5kW z+dn9haS~>!qX0{xE@(jLur?H9`H5?dL0zIZT95I@J1-Z}>(q$Z-$R zgTrU<6Z)YW0)Efkr~;NL?7bK7rD#f~3iaa2oGV2|W;?|ByTi?Q;H6Cd((zGs?*{Q$ zqusfyzr098LnDxsBq(-oE~!X4oI|J+S_lteX$SyxV)05`L(MJShk!f)Sei_c$fz4y z{0hOQ7YeMa{Jn~oa2_EA+plYBfq@8;)`abAB-7HW7eP?IAoLL(fuVIJCMeTG?!4r$ zget<&RS@b5FuU`@EB3j}r(n-kLq%22p>bUgVaz?qKk9fOVu{EP-u}7yzJftMZiGg= zPDo7C9UVkE+XcDe_-clr*6u6RVmP3E0t<~wRJf#q-DHzwFhIG)Wx8ni@k30GP*DM|iyK_C#|&%$4$fe|X^3MP=RDL7}@U9SPeHP^N^^sb+1 zp9V2PcFt(@!BR_4!3Eksgk+W$yxv`LRVFeUHfV$v|Gz$m8G+0Y;KMtL7$C8sD&6A^ z8tt3^oyl$j9a`u{^a%e3wlpLpx}o~xJo6k3IAsLJ;0rFHy+=p7$G=cTy<>2ZLJ%Vw zh&s^MSO%6!AovQlBxTyI1!)bagEXAh#COP3Ga5GgI0E|EQKd9qYk8pG@EJMB5F#Ii z(?Zz7?-n5H1*R4AMOltZkSDu<`T+(YBfTzV(scN>_RL@AQ2z|k%$yh<9O^O%+V8H$p^x5B!&fqwM6W5HnQtZ%KgZtYJ;%-J0K`*@RNKb6 za)5XeBeyWXQX7bMpeB$(j!NVcJUvC$v^lklNjy;sn*rn15LkysA=j$g(w$pEBSLVkBB%Y88T_Bl_`FrHJ77>&`7rX90BsbvmY4IU3Ik@&d# z%V0^5Ss$(ec@&20WsU~UsdY+9r8`n&L4}b7D_!|ZNIF?#uzG?vZ&9QH2taFUa;U!) zpOopLPK<+Q2gz_+$(3+r(Is<7@|e>CBxI;{!w8eo0cxTh{@wKG1UN$!2ns5)0UiL` zS^ZJ)5peyp?GBBBF*FkE7F|35xS~-n6BFO}dnnw4UWgx2sQ|l$#kyW0O)N#s;Uh*| zBq}TXPIUZqvNQ-;&gm}{CS;h{G9Rz~#K^@VmI~y?PW@S+Bsvi^Q1QsarV|4NkOenG z+EwQX+zdIWNy2FjLjxNE0_x~>##mpRZP38KfcC8+Dk+IlBLT!>3HlPDT^PRuv#vR5 z;W~d@MG}Ja(g*~_Y`}dqie{ADK#J>}C)kdxy%WoW_3lEWpJ9`UK1P&|j*Pj2GCp zWO8?>j97(h8LiI1Fdak=rg+nF*6O7Q*-Lrtn}jy=mm??!+jXvgS}lbgqg!qHo(L5q zGnw$|r3yz`YrF|Ad6pj8!nvd{nc@)iIy2xJ3fg)d z;X;~y_gH9gr0i!OO-bO5xJUadI~D@^(*)GM85dI6=x`j^3T)idi0ST+0ZHy8e!Uew zAAn&6zXu95(GS12jO_}Eh>tLc_}5U3-GD4k6Y``J#UQCk{HX;)60)9Z53kunrzrXk z#FWflWssd;p@KC%(t9ig7xte~4F-jBIEQ>Q%xYxLyW(aav*v!r)YQuY6DY8U#_N@j z!q^OtWE{nwF}tm>Bko_+iRyxQ#u>ftBx#bmPU@1G*XHG4((<1qwqs3)v|2=Z93W^B>lK@N%1DWH4 zh-s>K6QbdX`{5=`X|U0dH8iO2L!8lTwZ5@G8LRCq07R^VY0X_96LH$gDf*#fC7 z*>*NZ#d$6hNI@Vnr~2GoDt(H}Td9 z#W+(W!}0*A3t{vR__%C4|h><<(a9k0mV89;2~y0GLbaWqfqb&Wdz+2 z3KG|Q9N3(hLI)18PI36QP$0m+oB}7zoK=gipwZ35Mh;wUPl5W9?igb(VyT3ff#^g0x^$1zxXFf!HQkK zS{puhkV&Ig{Nc*%cR(7`rnp9-8`s!kd}3fgASbXLHq zzATe?n}agP1VU6Md0b$;cBXcE9cL zVR4aVL`QsTXbZup5SGk+Wr>#~gv45ic1M~gy+@flV56X0T5vuO>3d#i*x44r;fBGWnXCgZ3w))l+TvRFz}E-@;kRK zoigNz#0I2Hp_bTx1F_l5jZz64O~lS1P(WMWYSqKy^>86z9$jj&NP;0v^krWlV2lDa zP)$LNhM)yw-Z@FZ&jhPn_K}kk7NtaQTMLI*fkKFk*aH0la&yH3TI*q9T~3T_;;Z1Y z+t*=2kKrg5fZVHPu=(nkezaBSUU)z>3|Fc`_?=El@VefO=oo!#-O*%@N=lG=0J@+x zqR5msA@8Z}2t#rRsTFu+X>W@II`HJr3KsRvHSa8Cte4vW%zrVOWb$(gIya=L&F$o8 zC!W)pomoa``&sOPNNy)jWAuZ?Rn%oh!j=Lkb>4hg*+KkM6IiJPh%is>)uF2#S2@}I zC)f9Fwm<%b41e=g!jkwC>*Hj*LPdKyL|oQ*K~DOA6erODf?pG%!i`9Ev{G_4KG-z55hx3fZ+5}ux zFll&T+^*}r;D#@5E_TJGY{}FywEI5_<gk-VGiT)19+e5*NrCbeBIB}VH$^_t0a~>~ zjTLN?6QB}6UB2u@JG%2%H!9(dsA_mf^+gn0)Jdgh;*=@P?aGNXsLTneKH&8AIwx8} zPiEIK;(Xd9%UyTw%bNqwQp9dR@lAY=E=_w>b_JZYYy?BicG)gTXLb^MH(wyr(xVwiY5GrR^@E#4%k`@6b9;KCHZZ z%L?u_GUh+{HCeE#LOvoSNMb+~aAnpUfvf!mZfG}eWeau!ARQ1TjWEb8dkAp39Vj~U zv@iG5SJew&N^U1T(A+vFra=^5vu2PrEM!F6TUH}CoL6JJZcM2#mC?`?XOy`@g)wL5 zKteUGP|MIw*v4}(AQ()W033j#<$fR)qHJ+JC5vlZwg>X zD_$6PGfZir)_HHmiaBCg4}{=Z6jOaWzLqhEi4eguCgSCnrqG0wgwkGg8&Y13uzZDN z#*>x?-GL|;`zd%;0YvDoArwX`WKaa#Rx8dVrbIP~RV6UPt-Cnt>|lp53j8Tr@fshj z@l7;VkOrIjJ`Gw^xsa&sS_)x;0c)Qi5k%+ds3yD$Bf#3c>MM?6fiA+19}qV*hiFgG zt0D4Fz=E)~Kg6+=(-{WUX(TkALind7oaCB#Yea=&TcAKDj@j5}@WE42@&fFrUg&=Y zymO9hZh!_3`Jm&_bFz{+Ym%+~jJE}KoP&fWh9{OYUVA&h0L%n|X^!?3kRZeNcv|ZN z?lr6BvY@e{w^7Zst)uFD>Kop?J#{8%t0xUE8)5DgL{V`|a-epGv(n-Pq*F|(>>0NK z>f%sQQiXmM7F7W&B(Rd8P8lYmaS23{uO+NYkda|K6kBPt}dP~TV`5-bc z2sk3(hh$&~q!HdAbcAFdkXRhNJgjhlc~JNf)FY_IE*O|*V9OD?15Jj2400KoH0WjV zp9Z28gk1q~1j!ICB)~&(kO2Y$H3-uWTpXk`NMvC7Ln4MJ40Ippe!-$cfQ2v#LKDm= z&`_YDK@);zg4PDO3WOC1Ens|rssL&N><9P?;5C3LK(zsD0=@?T2pj$Xj{m!S>;D7& z|L{IieNpqEupdodiF~W@|1tRQ@muAWsJ?#vX!z*%yTG4P{5E=f;iJZ7(0Ajn@T#4z4zC7QD2%3Ff)Ocg-i0?QXz&0ASR~&F~(D z4+FO)zwl+Ru{)gF&e(R9ye*gahqMOOdS_{`p&TZbN3} zO4>MqZ5rdExMe&rj;N5jxiq|QdR&K4@n$r5YVhF7^ggha6Y%&gcSaJzeSVDx4g+gLDYO6l@O(c_MRFWi2fFL0*d2lr) z8n#&-XQxbsNQp1-1>ZE|25lV(ItxN336wT|AOUA~<$G#-Lm;EUflWQ2PaKt!V0)2@ zjJ^F|+4&{1156y1XVhq>2He_=DqEeIy1hpzgCD+R&0^9)0J$9*>C2In3%|&ElmRjaUw6#F0}I9dQeSkV z^RzLX`Af@FJ2@Woj(}VlLHkjbhA`x+CcA>^#@fP__w;dyboTg56DwFGCb^;j5X8cR zLI{`Gb#h_5wKMp3fnJO4ppzx@>y2a(Io#{*0K_;QW;p`_@ys!fAt{OENE;VuFUsbC z40h0pe4(G)dKLkoLJvYaa^3p$CM(sf4-6kw&$s8>k>#d3MdQwty-GY+EW*B82yv!H z8Fn=-o&)#nl90Ts0VOSU&X&>=kMHhvbI0fY{(po}wG&vZJ1Jm_MJ znZg=Dkqpd@MdosKGVTZb?tb%;6?47t(q~qaF@Efi<-zN6t1FL;l|p`+*eXW$PP8xU zwWe{O_Xtuc+^SR3q|qm4G$l~R@qD`i7bMI(4}Xz8p=K+^y_=BS%Lg9Q6@x9R42G{_ z3ujo$F#cfmIf!D-V!92kt)M)q0D%-tAve2&X~N~C(5xJOS!o9sX5A#7=E-d828}6u zEb|K&T5zgCoJb4p$9EH%f$C+G{LUH~tv){r`^C=p-iX<)ZyiuM4Ejlj;Qv_AJ(c<1^(u_O? z!9h&{iHbJXecG1W(?@=BXRrQfFq_r>Ns)O5dSc{+eKeE=LOWeoQOS>{1I3Ae^qV~& zMVyz(&kg>Lss1J>_F3JQ!_(JMF8oZMFC>f!8((o%fP?>WM~N{K#TOxx2Vhi)P6SnG z)VYfB8mattOu)u&z%DmUTfB(}1hry-W*%Yg>w+FF)KGK#rMv?{gx4!L8ZvRY&?8aA z;?n6XbgqHq_MOB=vo=uJ@dBJizk1;t-NhFZbHOU^dIl=QTGU~9L~Nxz!`v4c?YE}^ z4+HBd(|2gGF>P2X@V2WdAP`hl5OzNW-tpn--;vOvJ>heyF11A#Oo;gW?0Uow;-T@b z87P-Fkc% z~9spB&5E0V2-wEC_4B>(&?nod9X8@&nMmf`& zo$*$@gQu^K+>qXKi|&%C5CBQn7X`%)XlLO0#_N}~Ut#AR2aZTmd*lP))3~cX>ZY-5 z)zaJ>3=Mgmg{PR(r*IL{;-cKyzQcsI%^R(R*z=GO28L`>2+IhR4ekE+4 zM+Gjxzqe4kWU~R-5>VMZT-3ZM(po&(PI(v(&1dv(86XaN;BvHm}^fU38+P=hf%-Z4PrXG}u{ z^{g=)0^+lVS>{0*NjXNV8&_q+Y)FC5rw3J)qxWAWsHWI1Q7czoL5fLjuNaLok>pJ0 zQivnSZfgD;R3V$T#E<_`Og=^fL87?6@mL~$cPHC8+zk`RkkHzqC2ee!6OOT25}?Au z8lo5|NxX-eBv?+_Jl(h9D~;e6g@3JwzU4b}rUS0FtbaUHZZ$m{NtvL!ESZJHISL z#$q3276qW>>e0K9BC6Lm!PDcC*mJ>96;}jV-`)zxB`?jOs*Xw=t0)s{mG?QRw~8qt zfu=rKWTTDPq=!y;1b*tE3H@nBXu_aSH~}ouMp}xlRsiQy|?8 z+=eFuOFpAznJa$ z9HP}Oq&hZZjUr$CB~(eAM!iJ*;=b?Yrx6h>^|H)MP==A9VPv1#j0hS{CaVQ1a0U*_ zOPt|Q3|tBH4>cTq2$K@~xI!3~L_nbiL8%UpJy?`vZOB>f8|q^o(U}ch?lcb}gFn9* z1|~O!l8`0`5O(Y2Oh~*GnI51ZmY26LDazLJ5qc&Ez{Mb8VGH2izKeuw*Z=?k00000 E0QL`y%>V!Z literal 0 HcmV?d00001 diff --git a/doc/_themes/sphinx_rtd_theme/static/fonts/fontawesome-webfont.svg b/doc/_themes/sphinx_rtd_theme/static/fonts/fontawesome-webfont.svg new file mode 100644 index 0000000..45fdf33 --- /dev/null +++ b/doc/_themes/sphinx_rtd_theme/static/fonts/fontawesome-webfont.svg @@ -0,0 +1,414 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/_themes/sphinx_rtd_theme/static/fonts/fontawesome-webfont.ttf b/doc/_themes/sphinx_rtd_theme/static/fonts/fontawesome-webfont.ttf new file mode 100644 index 0000000000000000000000000000000000000000..e89738de5eaf8fca33a2f2cdc5cb4929caa62b71 GIT binary patch literal 80652 zcmd4434B!5y$62Jx!dgfl1wJaOp=*N2qchXlCUL1*hxS(1pzUj2!bdoh~hR1qKGRh zwYF;1y3o}w_SLrdruJ!H7kRd|tG>S2R@?Wq7TP{rA#?eEf9K95lK|TG|33fEKg+%6 z+hTSaAdmL)uWh^R%I%Bq{=#vIHGE2vyyxxQ zu>PXwf4+35#HOMTl7@fkt@MNGkN*dqzrXxudarck;ms?=9TzfXbVcIGGxh+E^d!f> ztp1kWBdO@h9ZDcN>E)O$)*L%OUQ<(5(?2L3bseob+I4i% z(X~e}J$l2@yN*6`^z%o*bo9v4Umbn#sBz47tm;_Pv94o_j;%d*>9HG*-F57d|CLTs zlc>gL3N=cjYLt$8j>eB>jxIjhe{|c??9qFU4jg^^^s&K$J;*W3T~FTeWV|2+Pm&&ML33QxpS<_UX3 zo}ee-@q2t8ugBw&J>0`QlKZ6FaOd4a?i23g?ho95bN|)-zJuoA|NMsm7K+s}nqB%Y z{lQI|ivK_S=vvsKmRk#edAb%6i2hSQfN{*f8@=C#{(3MdvZPB=N8B5iy>ag#%Ndz% zd|;azJHAbmj*E8`hfQQA(J-EOQqrDKvr;880iAi{Eunx`8?Q;WwYSE-ESYZWVy*F( zDyBWrn7@r>BFSWAC`(6{$=}vkS07fh;rcptPAzWdrDR(Yf3n1{ZmbPgSS%G{s_+g8 z?`TBE8*uTOCf?S?TU)|jb#%6^y@R#4wuCfk)~1cCHg1}Q(}asx@ZVV6;lsib{$)h;3&X! zv#^nE>r1k8t{W+F*LfUs0DkxY35 zA&hmqcN%Y!F$Y>O5DtZ_l&QR>OYUgz=wcmSb8^yNnjQ>PHkL5{@qN#TZq2kl zV*Di$^E=g?)6Z1RVL6_0`tSSJtJ;*Bj-~)(fu@d{DcY;wYCkW#w&!@JXYJY^HP^E? zCQEfyNA@&MoHS`-XZ2cas^9s{_6MI-Cq)uIUm`L|ee%J^d;3q| zxwSnC)nU#t^(_m0Cn*@xCMAs)wp8(Omy8LeF_j-`^X2cc)%HzmHU_(Hx@>V>-Qvq` z>KZiO%HNyy@l}?(^Dn$><{N)&oS&(y%gk^5+Z+G+R{j~Y?$2TF2BjKgP>~{l@+5#xb#STNuZ8r?=WCN#*;G43z#WbeP}pXPs)z27Nc6N(s* z7!KVTtaQBluA?%jx!7OW`ifw}I-h-~p~09u-%4wQ;KqEnm7v$k5_U|!oKTDHICC?U z%UO%D>hNJ>6>FK#cCl;NcSO4y&fF{>U=3aD2IJ-~<7dX|?|etL6`R@eA+4k~0kR8WvKfSYMJobh>0d z!tvr{#Gs=xQsl%)QZ6lGj9fo`gtklOnC+PFB5q~+|H?r@3FXkQznBmY53W~ekX>W(B9tH3|SwvWJ~1XLheJ)N0I z(>o?V_Wu8Me(d|W)LC!j>N`8@S%!`yX`U_3UsHzz6Au-Z2`g~&4=#RcvTJE15t5HKCG3gq~ zrQNE0NeW>%!QQ27HO-7A+qxMxD=QAwOuIFjAAehPar8FhU^GezmgM(PUjEZ!aVvTo z+f4ar)c6Iz7iCcIr6=E0eaZm|+(=!(&9s`76^CY2-C-SFe<+|^nd%cY8^1JuY1YJ& zNEP13l7-rTiL2s0XS!=XLA99lj7d|~VsD&Yr5kF;8J`tNS3NtP z3km=mX{w2Vehi0vgtJWyPIUIJBgSuye>Z-6WY=Q{8ZWMnxyP;FvgG!|uO7aA$(Hrw z+_CD-;|@HQ&-QKV!ynInl1lD6!lIx2D(l%Ab2W~;IJV%Y*K9&@JhkbXpDu`9Jg(6d z+iJYP7vu#V=X4}m3WTqqe@p2FDIs8{2q`V01X>50LF_ODG-LDB`qKNS2O{^EnaD-4lj8PxQryhw9Ovnz(^f)Ef8uU z2*Uc*F(U!YNG;Z=rsJ1-f#sUgX(1$2M8Sf-$E7Al%LWLdqj6bc7WX_~h3j9O9*_O&uJZbsHf!YGkkdK3@Lg87({WRsC>(L4Fb~li4zjJka)fxa zJ<+n#5wRuivR)E)-_{cKI=|)#Zn4_0Xty~X_TcLBmPr*n=oDp}nkFxCIBd?kyKP%a z3)^)xWl9 z2=r7xK?qCFaWA6%eUW<(OS^n>tOSf)XGrI(tU^jX@g7V5_k36_LmfzD;9cZ2Bt60U(mW+|v56fMdYE1^I$# zYn;WCDXavVH)nd^#bB7oM%}kFw5ay^Kq2z{plQ z*kp&z*ff+Sx=PK|ch*OZe~qcIBxv>_<;k*S^aT##S!CCW3BP%kt1v!dz`J42aRDEB3Q^9 zD21}(34VTQ(IZF1Jhn)Zz6j{i3uu>ET5e**HtBLu3lZPM0<{ndq;MH6#$^pcf*PO; zMvz-W$VC(*%z=WTFr*hN%2>epb!UK;F`wfv4j+HNDW7rrSOAxeqqrVmK4(7D6k(59 z>H=&TuDEgKDHL&|2wN7Yv#`e^JgPA4Vt%KQQyd--xMIJPNp#^Pj`Q2Qlz>0#cjjo8 zb50~ryxS#YuAmFBly%H=0lx0*)XAQmQFc zVkB8gwmsEZe;gBw3IE}(Q$9K6HufsO;~U;;BjaoL8JTLYcN~)dnc$I_H0~)Ok20lF zEH*-E-`3fATPOE6R2mt-pXDkWQY&S}~TyokXyw@6buLX;*ub6eMzw9v-7(QKA+|L8-TdVjzepa!yjpUdH3-BzoS z^RN#-q^Xcm5ON2MJ89*!I0RmDT*l@V565YbFRc3xzln{*{*Zi$V6!2au+0Bx*H7*XCt+j>rd*JFSa16?@c(S!c!QKzj4ghXs#(BNfx8MKW zBJs8JwfVZoW#4CImaWG3K089H-N*b}ZU%&_l97od>r+*??<+P0u+n#%g zsAHWhdSusS8*aiP8m2FSuj{0_Xk|d>QoN=P1j~p30GtQ5SzQ}+72XTOe%Vit(OY{CQQmf*S4a-!rCL=&B z(CJbN?hlE3G6w2QX%r&SuPF&0CF^DV!xjJeG^zaQE{7S&Sbe7~`Fyx7${c(L58e zQHg&n=5!keg~5Y?YTC|+Ni!3LPbVIMqgMshgqEEacs{gm38lO<&kG^fB@*scroW@{W9O-ROG z?Ki$`92a<4V+*lVm4Oqq!r4Ns(=2x7h2|P0c!?=lQP+gi*9Iv8O(X`OOKxkDF*?Ne zobDYgd-fcgJCZD`sVSrXWW;TobD9?$z6W_|Am$cJq`G6!Mus~mfQn}2SD_BIBt{9=O676JNwgjI2{$qRA*qp zvSkYbovCER>AZt|+W4^(V4Bja^`^ROZ@>N8x+WyW%^&~$qtIa-G4fN@WF!@+bhkh8 zwI|x$m4OtXf9h9_Hsi+CxKkHaoJx6QHS@3*=2;ynM>brCBC90_4WiIPkRH+w+RqOe zN(FF1EwlrzVyy;i(|-KN@y|g0(=VMF60C3?yj!}~TkDMnThnx%epwbjau%!?u^sde zS&;zAY~an5J+Sao@ENtSReJH*(HOgzJIJ)h-SLtH00GoIooB1?3c{;3Nd zItcmYsr^Vn(q;B#D)b#vYpu7{|Nr8@8$Yqw+Un|u@z>RLLv?kx_zn@U-bhFpUq!UIUk>Ec_WYcV*tuLL-w-b>i$yiSh=vxZ!f`sbB z-=>;v02>IL2n8amC4Bu+tzcQvxVok)_R|ElFqg}#JPB|&a9k?c0rhlyvZITWpoS78Q5&7WEiJ5reQ7B^2Lk}GYoL%= zdn%+7>()ZDog}I(uyQ4NZDW1N_=Eq-8ABTu-W@FqX$*TJcLcTYc#EuZIVuOoDNI+C zI>q0tFbn6dkY@2Z{egH2Qe!9oV8P;$@m}5B^M*cAVYl1Lu9iPh*=}Lub)G!&2gTvy z{mybFh(vw>iA|?mQEDd78@ej9V#}hL)08Hcr9!g@Ds0IuNn5?eUZd4*tFbnz&RR9H zBWbC%S^^P^BN0!PhnOZ?w=EdDYUgaXr(#ZZM1DO~>#m~xQcw#9Q43}gLkhU~n2-ZN zSIk-+8nHbWxKEwL8t%nvp~o20mvgBjMit)x|{(&v217kK;Gm%Ge*DDkEd}3 zEcC!xm-842CmxLU*PoOw7i%S}X9dq3hdfu3$P5EU7$6d8bf|e|%Z9~Ok|{^`$n)Pj zbm+Z9@*t5+$Fp=CZ1rzQb1A*S-a;nkyjT2|&-h^`Q0)lX6-|y- zd2IoUi~3Kv3m6l4zz+$=258kmIHE^D78r%v8a=4{12SEsE6Br81A-H=yVLljW!mAz zZ!?>~I$A&okdQ`<6<~_!8j=WO#3+Sdi03dcjeVKjpH3tjrYu|h^nwZ|^TwVpeCh1v zpJ`hJI}?`wEuRox*yL5LTveEj*?p~5%N0oAuA89xRMrq!uySK#dh&$v<1*cm>%O>Z zO=Ym9XTkiNmu`P)`A_5S*wT4(F1w;K@(28nZKh;Nq5U>8jB7UBSrvR=yRd(vYP`*;+HPhnDTHj9A0I9 zUwx&cqSImVx$JtSCuC{Z7`6G?^i)mH{qZ@BE4tRvo=G?yR%Lu>da}{Mn7+e%c4ZViB0LPC|dWSDQ?y(zK%Ro0605Cgn)Hvx}3u07gM+AOX_w zkpve4C?F}UF31K#B34<&_qDw-vEY2y_hr!QjHD)jLV?bWz1 za6@1U{(bSqi%T==jTI_t<;-KTFcx_@ec_at-z_(uUAC~DyA{sWb*Tr9uNWV{uPIfo z+dPWJHbKSg*(@$4q(rQ7Ptp;r%^hQ(?YewTNKu(qVYg1aDDIC`cv-_aCwLp zzmL_AXI7`3hCXU58T#XYKJA3l> zv2a47oQfj}bB~LhhNHNbrF#mFIgz3RyXYg5{~xv6G>w$e7}0LgC>2Lx6(n*T$N%eg zkF|yPsQl>hE*4my+5|EWAjXcl7&dJ%nBi$iu?x{ z2ftGj%|0QHinvmm9w{RalF0@=9;Ji-BYRfTUkOT$Q~OxZF_@NeWa$HlDaDXu`|weD z)=wQ25=a-Cs2=)9yU343sRq+51u4TSMuiR~ojH9{&~~Dal923rLE_K^7Wz~a8B{Ww z&TvSVQjk&kjID=u<}*7F9oorrI}fq@d=(C7iiA<)ysDqw_f+xDp`A~%1AY}62U7+I zJ_z)c4!@QvsR`EvAJpCg_ASjYkl>ra5eYsTFHVL_xFce_d3M{twrvB-w&Pir8Q|b# zJ`f$%GU(}jrPh{;hYD`X!%RLWin5sBd4h^L6+99f}e!kWQ(MMn=A)U zAjLaUdayOf+CarI@Hn7s!Q!KRUdVeHI03TS2(c}z-&vjISA}eP{?|H=yh?9p14B8Z zUwtR>l+piGU3)tDP6DO2WaWVnm9mAX)c1`3p&T3FgXzRmY~aac@_!&z5qz1Tv31DS zMoCm$z(-h9LclJY#vtrq+_>M>s!2{I zYjl@PtYN67JwZBoGJlc58$jk$C5K^&5nz>}sIJr~dK83K0HP*H>|Qfg8m}$UE|H?nvgB=pa{W}siM-Fvh3iT%GguL@o^=lx>; z6V@Be^{V|1{nP+slcg?c9$ID2rj*27hB}ykG-wld0`d&8Fzg@i{<-` zL1oPvV{i>@@g9t_epJ)h&vV1|NQK~+4u zhQ-!IQ42X9(Y%r_0IOI3=q_E|S>6$+z zRy|qvcj=_bArOavE}&+MU6f8b{gH*8Hf>w6cfM%E;}8D9$coiJU>v@3=L9)yQ9L$V zX!5vPJy<(+(Pg(kw|M|4BjRUSKd&|N#eVvo6>6kLDfaTGew(w*W3jR~j4bfQxZLi2 z#5K?ckHqy#+;;WeUAdxtjswo~89U-m~%dGnMrGy#Pjk^B_V zmR$w8Wcg{@LX#uvigl>K^jWfHYOmA7YJe zI{s=n9uKP%!+c%7${C2Lxk$i?R2{*T*jEHkO?G!Cg*J>MOpPj0FU6f+*dItV&g76V z1b)pJ&Z!wP(E#rzjwNY&55X=l5!R#o)VENrBjrccGxDs4XEAo+;jV=ttEC~7{vmN(Hc`<9+{#fpHLj)Nd9eTcO~l4NgU1bOrQL!VpqQp zib+yUYF})TFh>{Clp6kaemgWrcOVVJ5D~Q z^rB8sKjecYq+-~LVDp})?U-e;_|57^a!dOlcUVjWQBca@2J(2{ZyU8X`l3 z!ZKqBCZ5TXguooG(a*5PF(lMTyU2d2(5_-@PHjVp@6l=BYJ$lrZz=76qtMm1H8T=; zL)Zn0K6KS|1i=Ogr#OaMVYNs06d3hV8d164|J-wa|0;h)gc6YoBu~A$=ZzS1s)}zl0NU8}YaCa@jC(V+kyrbM#+k?(iPn;jyOUHEk1n>nCMH%%UO0z z>j#QY`}pTq9$fm9GT()oV^&#NTRhnmitd5??kC*r}T6#G;# zT{4>ua-y&#TH0ZnA=XK;L!+!AC74DR4QTuOh2bC?SJFX#O5+DyJ}yy7B#fLm`Q*Eh zF_YgK+uo5i(hMI&X~g#gMiv-qQ}zODLySC{h&;4W71rlt+aHv#vZ#wET>Bzi;ca&u1rSmPQ3G&xc}HYiM#26F&DUrAx`u3aCK}v z5XBiDFVsi4Yh=C%cTL3z2uCAvAX#O!28fAe3N0efEC^aMGBB5Io|*; znm#!N-*Pp!BJbKaaM^bcoHJC;|9tC{V5ij>OsjqaADrKikrhxvC#!sg?|y7=-hJ+h z1KA#I_y(psW-K8JT^i~i=~ohErf-5MqY3uB9yQZHd2 zvjZa~Xp3ZD8@!%alE$wWbO-JULWg8MMCtqzV+|Kq%teyO5p!I#pgnWsn^55C(m=2- zc&&s31%G#_6ye;};fuGT2`1lW5MwsD{u3X+e0^7~s(RfXhwgC8H>Mxw-yH;Z#wB>& z`%#L>5l40V**gX{bj;Fft?q!=8o^Fk`P6szvipbKFk7%?rwBtNM2*2;N z&8GHYeSp@@0(J;^#d;j(7lv2JFaTl1RM?0Z{hjqWI5G4KuZ97UVXzgE$y@i7tD=12 zT^#R{O_6XaY>I zy0Q0#)#3Ig+TkVzzd}|0UQ?E8H^PXK&+) zOL6<-#w)_ZyY=IEnDis^28kc{4fX92q8$_?LW8qXYst__)tzbG_lR*${^0d6!=uONX5J;|nf-!1;nR z;Aa={tq#p%(H!~vY;JI`5@f>Qp(NlYC%k*B$?74I_QJLiviuMzi+0vZL^FH<;r2qr zb8Cy~r-q?6ndySL5uA8v{a|qk(va@Lkaobx)kSmBI-~R3H$)mSllep!x+h^|kYM?>=wK^lWze7D}H+0pF!brYsPI zmJ3$apq9uww+rYAb{>=fIg39EKmqTa$Y+f=ezOaUzARX=Hn5NBUybl&pvidW^`8#j zf4loY*wftDRarGI;N=!s?pn|l<<=D+dtqzGSHAqE2U50Fpe9w8>W+D2*iv0^=+?;y6u&ad)|$TZN008T^SNbfDq%}` z!`3x>whKNF>jv^OH>^@6@(ZNtFn2F#qXGiyrouwdsRDzCQ&kG-ltwgcC#6Ye_4l7O zX{N$f-LY>~hnee<&D?;{A<#kbFWPh7vU&4XxAtclYgoShrq8Y~URir{;R+2o=rOw`ynAzQsbu|GY)=^OFN;>mcZ!a(H*m zl+Fg^cfe||twYm&W80aacA6VEAOpqB7ROtJ7c0s7{osYbwWA#Qx&XvrY1RQkn>Q|6 zu^xSSn(rIw1-q49Y^>Ql$>wwH@{GUx*vdfQzRXUduRN7Uv*#g zJIv!<=W)Q7hue&a``>C|?@!n>rzW%HvoGxNz4y&8U%4&wC9oPacOKx=qXM4d1X0-a zKLRJoFe@FlDg}-OMVWU@qh6w3BEioP=-Z6|I)(Xwx=JWE z8X376kOPuHLlCBjbXbK#M(rP;>3eKI^=5U4BD*!?zm0rab@p3b+-*HPWarF=w8md# zvZ1(OFP3$A_{RtOa%z8DuJ5t@Jin`7W3rPC8Tl8zu6`@G4;|J$PRBYcOT#KDY=IYY z)~P-^(3c^pAjN6ISe|NoO%~*2b$ym}CFFl`({em9<_syfuqYSThlMu3e8!`ERRiZnEi zMP$Jc5#>1f%D2H?2YMl9o^VB!WU&lY2fq~-8LZDFXYwY7KrAnja($5jo!gQVAv zZSGvv*4NV0Hl<=}p$K_k7u^e~$VqA9qG{vGVoj9|GpDaO@9J4*9b+yQpHiyVJU5|Z zUPGl2lMK0_{?0-DonuVaUE!Lh>8bO+BJN{DguAA^vsj>NT6a^|)}B>YFFvO=E*>6r z#Vn3-!@43p4A3EwrXWbbnrJF;STdDPwkK&1R68gfLl?uQsp!&C3!KaK52%x zLXlNwgU_NqG1yR6Wqc3<> zX3R4ldkN$@#175VmNt!RS~{)S%u>K3auYXm6bxx3$8*{58ZSKe9P9b6C;_NVh7=`4 zj1ZpS7mXAxeT)VU;<$pz<`P{_!7K{Odzd(O@dmU)eAILyQ)mUZN;_K`=7elaJYN3f@5 z0o&xm4S7;s!3skuoXKlZSF7N+rh`~5z!4z5Lq^vHGgzgBaffH2xbNL8e_x!wA1goc zF4NUA`9XrCAt{m!CHNPAAb?8pl)LSU&Xg}kl4;>vBA)4$bB0uwkay{oWj4=5GN+HY zT4yP82a---bts`HX)S^l&tfe=*Dw~&q57mqd3)BJ$gJ73XAQ%V53JcE59CE&&e7Ev zOi7D#x&rn1rEw!o^AX@&xu@3x|%IUO3Bou zjYC7ZwMV8KUr<@$#WB2mUUjXpy>)J+s=Ailfis&jaQ-}FyQX-RlE#p1N8&l`h0w^s z3I;#~@E~+6q+!6!1ZE`S0hI9^1dUi~rRrPC7Sy%MFWV?!S&23m>sRP;@c@1>ek`L) za?X4gy@N11KzEb|8DMM59fZF4v=xqMgG*iy(!bC+ybB$I|0c~HOntCJ_XS1*?35_xct%NR#)2>jcL0W$O{82u=(lp6e? zog*^kiBbmb({!kWb>iqClK~k^rzE7yuv-UW0liA65afU0gi`Hefe?YFX3Q#|F?;%& z71yda{rarR)y?S(=U0ZDk>HkD+wYB(-T(P*|8~cQN#ME1!JIDRZfYw5gVIxFYBJ6sl}dnsEbubsQ|6Ni@jtP>a?dFs%p_WOl2qN7$|owN|! z*9Kd~SdZQT)Qa%S)t#4q;lVw-cQcLMU)m79`Sq=nQm@~0=kC|@xA1G(`=xKw#hgl* zQ;M5Zf%m1LH|Rnuh=VNQTG|Wv1D4Zq$&-v}o=}X^avb2Mmxclm0wsCC=jvJOi~2h2 zU4MeN@WI!H4pJ;rC0mG7IP@m@0cJI6=-)E=>$Gfd`nUw+AIL=0z5Gj2-`XCcGwM4n zB6Q8ri&H}FSVPY}CB5Ejv zaXMM@)1;GB5-8n=Z5~%(3RHAety1I+Ow9ZZ;}(;t8J*>CulHJ0HH~ur8_`AM>ZAE} z&mMl_l^0mcz!R_RW*79!O*OIgUZ+i4y!_nB^0P2eTRg78kB7zCki6?-HBIzz{kTO@ z{^;&ko)};)FTC=^;b)D9`{hOid-1NfX$zOG>Ou3xT61Hq9R(iuVqR{P4ofEr{i4`J zX8+JLki&&(BB>SFgMxPoupc%l5H({176Bmw+e1|JcZVy&$P|MW;T@=v#)?KR1tdf7 z5iyX!d4OI4)kqsC#jXs6fpg$82Xh>hhanckEC2k%a#lc*d=TNRu)UZ^BkQt$!XB*Y z)b;RAzuk6aqTcS%!(X@iSh%L)D&1+f-J{#OJYmO!HrH^`(A8A5rm?iB#X&_K)7)V@ zit_9O4qvOXi(C3!fk433XW_e)R-fa62b|tkMd|7++-Pmkl&h6iuk(R_w0t2X(@8Z|;YOPb5vwvXF_=jxVQDy%lwqR{wc8S~nQ zi`uOYOVw5SDxd3;rcp&beW8gpVeZWj-r;dqlwV%1$aB{QIS;O#D=WxWxIMU08KxWX zXFm_O<~Hy-bT3@#mXH23PZ9hI94u(;gpfyhC>TbHz>(l4i5RCOXd=-A#qPzz)IoMs zX#{D)i$kl8(Tc4DtYYm_xT9|x-}u*aR$cc{U5jk@b1(y3m0<``=cx?ZuDk1-Y&N@r z&F0hYy3Q7?^whyIg8VK~EZ}IVd+54V=NQMnJEiI|R=@rFz2Tb<%KMG~d3T>@WxW*~ zE$kUJMVGO8CWDFkvUxw+x&PgL`||s){^7i``b03PG2B!%O_yCBrd#V*diE%*majRw zcVX|`pAOUW*dBHGD{dW$nuAqZ8*c;hN!AW?SRe(^QxY?xUtO@Nq}xbzV2RK&p??j5 zg)vAYBtAJAfh_^uOD<@n426vX=&3g4sYNZuK!2t`QkG~4btuX5@pTO;#658)Dx1R- z)gSM^CZ|@_`qBY+tT8*ungo^m**ojb>;J~J+e5}6AzbFG+c0HPSvc94YF)l}&ctUo zJ@^z=o#ffpg;Tyib^Y4NRkt*TXQ?f*bZwn4pVf4?#mnbE9jWrnUl41VT|V8**3_N5 zAYQj{W-zp2;r_=aG}iZ~c{bf!w!1f7e$Ae7i5a)=IPZc70T)D{0=WTC>ySVp{=h!qkX`Q5q$w(Sf?HcBtUOu}ewqU-eDsuMH z`P^%9>smhRtE)}NTGUzL##^q6tX)6#`%@OSY<%#7^RAjTdqyI@e%U#}mW8|FM@ger zKYsip`_zRSLcy5}>*5QD#yj~rIinJv4{Ga_;K_1kY_Mc?@c2uo21hPkmlW@LGHOF` z2EqNqc^3&8lo8k~z@ng4Nsvk~SBM3zWgBPqui13h z!x;FPdMQJ^S_oq6k(tH>n->Zuuv2)IETkU9EDskmwQfAind(MFEHdGw=vaj;NmW=3 zD9EeX6nVg(A0(5?j9_hYq>796E3sh2X_~{s#+)*1d-4$Vz>U$)TVRehNQ$wT$zZb> z$oKqU!6sh7x(w$GARxE3WmM!9;#~glyWhRf z=4_uocQTtgkI(+IP>PqVuodSu6j zp8OqbPtsRA>0y3lDeXr%T2hFfx0Ag-^rJ*dz)XrFmqEaQC{I{~DVfF*aNsTQhr~2` zfq@1=-QkaeS2dQka<79`sC~vIk>tY{&|W6ON48z?Fdtx$yugekgQM|zFte2oZv}fR z8M*c)E}8Ku4e2FJHrhid6nHd6F&f4a;$;7UsUJ3WF4~t;IgmQ0+@VCLIbz++MFVKU zOv`OE7F-r{`)q!@soUgtJc}tLqe$LwLWm4XUKA`^F_X&0CoeTnMm#4}ob(*2I7Qnr z*AQ?@8FWLepi^MbI^3r=h?y|8?dSyX{5XV-2Wk_SLdxktkX?CbCpqH_m}R0TkQACQ zTe!CK5V3Hl14Y(K?i|CA%X22=T1>DOI5{hLa19!<`51X1SuCtXIv&umGX)X(9~(E> zMPN%7b~v;Ig>*`wWFX(Bg0PAJ1rRGZYxcbbC#A#6w@*q7?mV1bcIPXXk4q;jr_b!& z;d2dPN_OYwze-=J)5S%m6^SIL3``Mnud1utnK&A&DMAJ3+X7-q!c3xG7xi*aY4gZg|#;U zlD0d6KQu&xfPH)lCh# zMKzmM$Nw(Hja|bt4Ik<7PT?^HU+Q@I(9S`RH)Ly@yn5Y?hO-hAqMK96^IksBlfI&I zeB!Kz%(~T+>#f0wJu|}osewSyqd9av)M&FgyXMWLU>u>)ps-vA^81?AVYlEv?a;M| zsy9O`tgEuxpxf*a>e_cWG&uRH9+>CbxooqP$z1*-p$%>cdjGg?f>zdk*6y>fIeYcx z*7~xtNW>nSV7+`bF5JAhy-ceE)!Nt)t5;;J%cZKe&Tu%{?1X!A@@6>{mf=i+7J$hW zemQ`-92UIWT<^sggT?b`xj_}laN0Xajsq+(EC7vz`6yV%LtjaB3nSX4G}_>2f)`9@ z()0_0>@yt+tR8S^w1lvy;s{*t>p<*Z z!AhBB#e+b$MC%EavRM|72^a$ze51?muvu(2#p+)anD+arjT>in?wiqnTowzoCL#VuNe)gP2552f++V7_L`vOZA*tmjV1RfuM zdHnv0s_2ABcy%b@W7dh`vQYb^`TzaLo9YJ|!YjsChN|l({EP+mKWTj9M928b%FE`L ztqj*c)^OQRj(l~-)ai>R+BPf?uL|3|URy}3f0)Ju^h&{&0-9*xDD)l!VNz*Od!~r2 zAc7WKok`b`G?K;#ga)KBRru}%@sE_`lbE?Kb|$QR<5%9 z^w!Rn@)Z>>-B)W*#@uqHYx2y=Ha*Dt{%s$xaaCA-oh{P>uF7#r`Q$nNIhxGsD^`@Z zbhhd~dzD-}@hs-eE?jS2T%BpHShIFR&>nzSm4D9Ua%EhlD=@94(`T)4)$o1)*2jXn z4RyOJWp^xTuk}H0V&Z&ZGh*7_kKUV3ad1=mNBm6I{;KGCL)(lh755nOD;g+z9nnG| z_%dUzXhIeQQCmlt`9C!H3Pfb=>2uFzPdm;Sg+)4%WCzba+t{qG`tW!x0=@+RG)q;Tx{ps|lRu?R^fi>%c_!Z%1ou-)@~{~s`kaj@M*sd*~ zc|Pm=#7~VMebzYkW^Ln}&tCjgbv)WQZrgpc7WFI|e+^sxvgPpJJNmcwCoVou*|dJP zD|)k$fA3$m-mBcsuV1Iy!(ZH?B<1mUEnC_9z?W^wy1j=l3QoSV+h(qdpO0e5|xWW4_Sit>MUpNdrc-gvzbj`s-9o-i(3 zh-e@`{^xg{i)3G!x{%#_;)kXw5uql5p9H;=K*rqNX>$hkD*_yn^TY^`A^bA6Y!YTt zNr<3?1&;Yq0#LRh_Kut@`VCMFpIm2sN%X_#DKrn>31BM7&fU;zk(9L&?>4`XqHj#mxYMseX72QVfMY+CvMj4YY(63d$K}C6r~iZm zr{R7CjPhschv>WlUZ!s;A-eCdhc2igB2X}mSkFR=Hx+grh&itg-{Df-$UO(F4}8pY z*yY=}-&c8Sc^wZK-*~GWR#XvnfYn`o#jV`Q1HS0pkpy#m35K%Q|E#<=;ETwRPyg4~ zzwuM%5njB;OVL0uUj7!F9pZK6w^sVR&Regz+<4>hia?;Y{AX-8tNfCaCCcvxv*G;d zH@+-1e=*DZ{cgxJw56C<1GTW?}m&l3+@XpkAMc^tne=-T)-_ZhV9Pd^bBb)df zd&OYjRSl!{xwbx9WPNRqv0pIl$rl4YKM`tvU*N?jjpK&U@4~YYG?}4ZFL)WawS!ov zV>8iVphW0QVb$qK7WU?`1EOkT4#=3#JceO3Nz4L0jpx<=+pBDj`fsKk)s+ojpJ;1v z=+%K+Z;g&?uuc4WLuIui{mpuZt?KqMr5Y-4y|uDobQzu<^B51&WA=uT%Ev`VSKVN9 zRPWzkWw(tgBjzP5U`U62VbfUIqcH3v7Z&r^l%|31DwRDJG^e6Fgl>fE_-b#>Oyn_D$|ZY(zMg_o8bE=U|%FQD#Y7avmMLh5+S z;ZIF1h#X_KFf0mPWqd}hv%aReJ9+&RA$C=%;4v^cy{vKO^!?+5nI%igC+D-7OsT-J zFMaWYU6V~|%WGV}4&KXqkI1Ml7FeS%h$my{05mS+`>O%P+7^CfCxNHU_7D z>V+HcdX};2a$Grd@y8zA#I6cGaecD8xu)J(JA;?GDuQKU8;hlTvpieYGA=I58eftL zfx?a_!_#LrE=x}iEQCGouqd)DcJ|Ut#^h}%US_&?>g-S4q4r%A3Qq2N@ZyaRPMfuB zZ*8V)X|Q8~j6wAJtuTxz$ZCaLTfml590>}Y04bIZ=0?*A(Gs4;sEVNs{lz}7)I zUKmgCNKn-Y{fN*@f*3&#Fx4f~+S7`5KNv>hhBBGFn0Bjrx=C-EY>J<0&LQFw9C2Z; z+h@>Rw=cNn)-iJ}#LiP^^9&$yUIB0|${E16mgMKkI(fPn+WagNRIBt42h{>#W7x#L zXUb=)1rF(eH4fq_Bn~G()R$7UO+pjUDyUV_C}0S(R&R}qCWhdj z*iq{Fr>dfEvoVHE$dBJIG?i^$&75PKwgE-a`a)wOBMn7qV~nHR2p?8xR|=aI+9euB zgEj2kDn80Es$I&dJs*Amb+9Bwc25bkTT6!G6 zI{i~=sIyQluMMH@j&=yJLWm?QN@(Gv3(PW0)lik~NTC`Mc2MjgRUPKNFc{hpe2KMGTN4M0Mq{Zl7$q%OlR~e$WNHmHn(mOr zq`1mLAp1Z?gwU>zwq!@BL%bYVkJ{Mzrw-0@KS02|i9RWBIV8)@#wQkj^SZ#jQC0iX7Hsm&?_{R*=3X9F*Rozj&&d*i5&ee#Df(Wo$?NepMIka+wHwLXAQe{NflsU6% z+zxRIBNcg#jyPUWzB?3zI>jf3WSQxWnp;;nj0ekA89h^N+-}hkc@jTv9e!mluM)%; zbs2`+3Td=zg=AW-mUV>h3~{e4`e~y7{DULJWhZV z$Ix5LWYw+$yj2?_apDWI9Lg3Aky~NUU`60ftD;%`vgT5CuhW7!nL&*!G)8L3U9MWJ zPN!96_~?`tripbs6t`N2v9ytsgAXsTVuZqgyK?5XxR?W>H&xw=DACNOFwCnGP}Fk8 zDl>)a77Qqc+Z{m@tjwjW9;+g2nnROa7|F$VAi$DUmD3=fPeSJa>)<86A-6XIG$z-Fn_bf<X~j}>pSeswiai#x7;04^a=|o zHdzXu3~D!k_twGB!iup-<%>wx!n(HuDjeATlAIHvY9Un}`;FJJc|{`9 z-^eP`5K?4)M{evN9gQ)Ivh+8UDT=wU1GBf!lmQtmso=k_g?xr&l!&KZ3_Az9*8E0P zi+U}-`{WnV=3tR(`03+Msx(gd1-|R#&qqX{Imr*3ZT1Iz{{}+=eG!d^m^rdjB)d}@ zhv6|Gg(Yc-5b`RBcykb*k*rxTX9aa6^#76}DUg)W_p?cD%^=e2hYDQ!00MXh&pi5I z3G44!t4i6tWW-GI$p8@?0~mrqGDd}bo&*j9YpI__JtHg*t=Pz5=w`NuBnsrA174Bj zAoLZJYFr@J5w>!s6rAJ=Rv~d9ei09fyQ*wF%r3YGod%I3J`{A1@v!mmJv2b1fr9qw z9(DmP_#+NSJ-UFHS>9?~!b9Q7|;*yG03lx9S&g z2w#aT#@!2P_+)8@v`ku!t_wS^w1>1bU}!)Hfrk-&9rN|-g4Jm8E7m9lmnE|A5eBz- zmKRF!C6901yL8)iTJP0UXZEPd=+9l-dKT}!ZSUe9Tj6upLuQ;j`J93^sT|+7bnnK; zm#956r(WHwU1u5#azNpdMQq);#&Du?f8KS5Ph+bs!p797E_@+7|LCG6*Qz`AS0=)Z zCdBjmI$D>Co8tS9>Me{SF zN22wq%KM_xS1TIEmXdEg`@UsYU$gAUvXv{(*>&~uSC@~;;}eIdJtkK>BIWM-PTg-u z8g{M!Q4u*1<-bQFT5%wnLZOQ4(S`DF9$j`|+1dZG?CNXJS-BE5kIvG%z*@}$cU54F z1YAHpAOwLxqYCxS6bI_rHy=Hb1G>CxJ4eL7M;Mzrr+@RohMS&Y*+<`mW8IA#nxI7`cA~EsZ zB0@lmq&3oJ>1t`ObO&yc#1>XDDv%tR-ePrQje|G`4N4jDr3v(wtYAU4(j_8a+ex)6 zsBQWJXkpTUEL70BNfOp!r)h1GK}%E41v~=NWkfweB~&y1@Dzf0!i*WUAl*T4m7fy) zIJ<bgFWYnPZRf1A>+6^9Ik0S&)wyez(>iO}fjvvt>uN*e z+57I@vuwSNl9o&Pmt0jd^0O{|Znre2adYkAvU3nxxuN)Ov@(KDXfy1?z@_Owo|qeFgb>z;9S;=l){ z*y{q8=7{V8S;YQ3#xogX$>sePsI@&x#K>jXgSX4rG_VN)f6=~Cji?X_Sb^Y+5+p(& z**FA(#%DgDj~0lyy%jMx5F64@n+QR#*h_{pn!x|00m={3mmnB@3WB`;XHCl*KVgm7 zVsZR8HqFSA$3K_q<)52L1s6=$eikcya{>>e4&!U}KQVs7KV$sF_!PdKH$ZOQ_!5p( z-#_#>C2QsYZA?;5?oqE(uOod2c`X6lOu?h+tR(WL2##0X*y-ktwOq^2@i&K`mRHNMSxQTG)~ zS5D`%FZ|e!M=q2tSAO!*UtOMm+~)91xAF5A9^8C!-_T#XmuHrC^Vwy|%2C;m4gEiK{lgY8LcUti zW04jM6b(hIrcKn;^qA49KP*2w?p`q@oth;ycU&APof9cKu(wZ_q{VSE2U;^DnfkO8 z^gEzvik@S>!VV3&_^8$uHEv_CkBx|2&=Zm$#kK+UXsKrHxT!)MeX+E_t3pS}?h&W_ z01V*Fxs-o1_6i$`bd702pWL+W)xW~}Yns#ttbK`e9ngVTHA48BZqrkcKBOTT5g)LE zddeS+3!y6sBx`UNLVvzaYCzjYcn4rdyRuUK-&WPDEpeB(v#Dz{oYp|NY~{7mn{3C&AtI6|43)`Tu!rgp-*)z4*b^gHU3 zi?5yLs{l{=KY(m8KR9{7|DU06X@Cnq#sM0b@sRo831Zd6+f((G}2m25mpZIv36j}4j( z;C=Nq(4g@E8s1cNzlZRAGc8BzL@rXqqENp@K`qic>gu|&5uIobG}rDcTrg*AenUPJ zniI{)VZ~5_UGPkp^bfra@_w(r&L)I^kP0?6IokinDX1=M@ z)?IMu{%zZvTRb*fKcvzFhupsB+hh9Y2r0a}cxS?e<~qsHpj78{-N{vTg3y<&XhxL~NFa@zFmU3ak= z$8(BK?8)>E+}_FeMa6wK6k17W0?SmC_w#zy5m3%ib+?Z?AKfvaV(w zp81BXm$8}InMH{X2Tt9Q#)WV~9tcB^Q9}r~F;>KVq)G502hIW(@e-wgk>D(Q>Dw%_ z4rpg3juR(fH+a$EP-|#^;^pPb^Yih?c0T`nb2I+L->0vnzL`D{zssL}tB#(g=riiT;) zg!eRU!GI}(9~hZd_ybdHN?I);B)R*${0d8c)2#ooUah#pv*|jgC1i?;C2XscFoAw0Y5=wuX+8! zTOPc6UCUI9E`nIW)&)5$?9!`pCL8-~ZqW&zJE`zHv2j;_dU*3oyBm9UUD?t5&7di$ z9SgmF%Q?6F=H9&zeY~(Gylrtob^GS|Q>x_diR+fIoqyr}UfFd6V#W~PpQ)V#l_OV1 zrE+u?HiR#!92sSaF_i|0kxP}%_v*{sYnqS!dE%u{ukAgy>zvYAGt6$upw`%{e{uiK z_wQfZOqKJ*t6Jv!miz3_&|^F<0i56^iwYl$HL%zp=iRkq%DA3OuV`O&XHadhl-a$` z)w|VpmA%|qWY00^<==gH%j$=MQTN{#o>#LpG1j~K-1fDtLGcZQDU`*^I%af~ zRkV+F*a2@ zlYQqRbxTeMJGyd5?cCnp%ANyrc3+vF3T}UJ%DnbXQzle5cvfJL|~-hkLbp`M02S`iMdZr((3Y9evH-jHK2a+cexH1<$k@5Xs`leX+m zG_C8dzc|#guKnCq-m!_LHRmnd%Z}~eKWSz~dwWGFo=C()*WN1sSJRG5yPG4y{zv;s7K452_o-6#ymjR42ds~zQd zO>VwvMv0kpt|c>eAKpEqMA-=?YY(4H5>1klhd+e+88j^F*J8_(J*@xgu82z>c>mgi zJ7><^c~IHOCCE382V}k#6DO1O2<0{c@dE8)2}va;5xD{%KqYQX!La}`lbnF%ADgHj ziJioA_^}h-`?W;&__G)&BH_T{SuWh9Q5gs%We{KBH)F%N9|@h|b;`2|RZ>Vw{JSLg zku1(1266@hi||q9LsBC9Jv@Oj%8X|d%Ckd}LL8w%NboYlX#-DFI8UbVKzU54@E_;D zhhlYryANDzXem4qY@z)g-4lKA|3u1#3jm$a12@oYUO-Bo>;rm_)N?ZF90{R7ylX!& z%&A?V!5i7CkOoO49cm|D-r-`7YPR2IwZs|PkbeiC`^vs!*)O7YKpTqaJ6^`G=sWbg z(w>>Vf;Usag$L2NAdyk>e?;``4su8rH1jPEdaM?-ny33@rEVxLxrsu&Yhv|AHPg& z9DJYHG0|TY{nv_;%Brf$l1qOdV+&>-tdUP9w3T^94o6X5r8e=AujIzInZ4b-&mV`s z>v|kn!9StI2m_!bf}9+|C66>zplpx|-1d;e2Dce^nAQOgJ6C?1En}3b&Xm=6RnxwxbjUsJ z2bM)xiPIW1M52SAL6mWNSXXFpUn^o4xZVuCizi=&29j$k6^K|rDwVoTENq9-OW^`q`_Mk ziAUB05TC4ur3~M)z+{5=*$h#<+vw5jNd;MK##fC2d>^)0$t~bB_}1ySqEu(Nb@wS% zDe4j<4i|g{pBtnLqKvj=^?@^BhQZD3nX|3}JO*M!$rlD|Vl-nx&D@dk7GyR)24Ycr zt%HL7$#a|o1Tmws`}}-Opt?ePesj0Y)ph#;m#s`#&VNZM;6pz7adJ}>Vb zrg@rPa^0u$Q#7uLE}#KG7d*87!CQ#rbArv+Vr-M_UQ}m`5<)u04FQIM9T`wLpyHiR6ePH9uQ>%NH z%x+sB)#$GI8*}{aC&S=kZu=Rq#U5p`haXO_54;X8(6*J?wHT^HZIpW9OAr~@mt!%2 z?-v&%aq-5_CtLEI=&@j*C zEHGGlpLpeo53c^(SHL!${Nk$-8!o;0b@SXo)qOB5y&dB4_GD;iiR`>|T3&1A5NQAqrVQ@)sSb{in6v}%w; z7jq-#7E3Tdc9XZhb}Q_4Ggr>c1@9?d204?MTNm>RtwKC`&C^x{^@`qys=ymmJ?G-b`H=HsMU4Q76d3-LJjVW zIxTdX;t7_f^hki`aCW~UYB!&WDv{fN;CX;xo>YSL-vV^A7`~;j7@@Z_hA7}gqo3SX zS_{CKqI>#Skl#<6)CIVIehPgI*9FCdL1rhj73)C{h=jsd^1L-RAT2CK-*M#yaTOfm z7|o9*o#M+}+;Zuyf$tu9PhuGrhLKB1CBWmLsoP0v;(zeg!y$zlA)|AGA*CUhFc7?S4q%t`D!ldH>{nx)E|oN{wpg{!N(%T>{4F3-uSl$x8$S1-Qd zneRVy!(tJQ;51iM<88s|wUc+wDleb4bMpDKjAh2#Zn)t#>}H*R$EK?3TdH&GB7s1p zHqYy;s4lCmEvv5ZdGl)NT3v4Smg!ZS?pX2grt#x9JH+b;BuyGJuxc)&V^oP%f#DKti~TMtPKgC4pFD#B*e+D0d zmYLq<_W3<;*XNsIpMUfq?DNxG3&=h{s*GqlCCwrrZ-#u7A#G!PfiXN=8R;`8C;4U+A(-|$01{+vA5IHI1%=+ zN#k<%v5EU~)*cQb=qU)*9p6uAf}YQy>x3=CDEFsbTmS?JGPP^Rfde}_cOTxe#9G_= zvTJ1v@X5MbR=QqpE$HnnXiXemyEw0eW_d~8VnX2ZR{Y|=k^ z_gx^Wp)H8-Nv7KZy3Gv#29O=C-30*a7T9LF+N;{jO=9S|LL_qSR6kl;(qkM235Qb{pzL8ZmeAT*`^r`AXlt}529YAF z+Ld9%`5ev-@VGz>B;pL{SZRIgn4#VwAks^a!|@{42vGxvcA#B|L*5FHCR~1;J)KgV*D`=XsnQpsTdad4%C3J0>d`> z_^5LzOVcZRh_bly94Bdsmyao0#U;?(RDw(|86=v_@nBL?kAO70kMp8vgmqkN&rAl+W~;;gX%WkpM{t z6oxFz4Vtu(UovN&QTz^AeF@tnnmanF#=BSQkLTEFh-I|W)NgR;SNlpclrJ6YvX4#}ro z8JjEt>IgbYUf%ypWArOV)ZmR$GDsvicrwYymDsPikM;C$2D+cN{J4C0`Vig~sy0CD zPa=&Gq1c(5VYeEJOF$on$;VWiVb7er`_g@g-c%evnlMf>y$L3pFTDz{!M6&xhQ(H~ zL#LhW(pcZ}%dkURbU#MKj|wc+w6!mT`{wQf1GHWZ9U=nU-=DEfCy5OBoi92Q{yxPj z!ylbSCTT(YW0N6ulHJS5ogqcwV z&qu;1`#M$sT3jBNhR#q$*h`4}OLERe>Oa}vH_ZJ7agmWH#Tjbz@s~1%;Jz6CRNADJ zP4aed&_&*k}kB9L;+<$O24wD4k!dQ)04Ok9slF9GNeFF*k zcN3`jd-@WIzW$zIFxlUq3AZ)2nZP260oKFR2pdWS@jv7$i$2Ku27>)ToiFLr zVL!n7g18D^H`s_QCE(!_XQmYc+LH;6!ad}E?8W~W<%dZ;YgV}w z70pnQU>H}Te$!+Ug;OTh=yJ*ZO4;Ze_?A*Ce12rfgapc>lxp+?LgUDS3E-h;i2syo zfQ>(fBvefQAu}V-4X9_*nJx-j4Ap=&lq(Qh_XZBC4F-8TyP6$1VgutLrd|1(oA#XiXWc#waFCwugwTx5zJby1j0Wl}zOHNL>V#oj=<&U9Ir zp;UpYg2Gc)OR5OHfND1SGL>tF>KjsxGlizwGwt9yo45YUs5uCq*sF1eJyU4{vp=pSg<}f+wRamPUl?Nd;5Db!1!ygR>Qv+l)*1+a01Vzq) z4H7pY&LDTY$m|v~5gki&SF{`HD{w0+rGg%s>kBDg8leV&=0dE?2r4`R0t|wO%7%-) zti%HH!hso7SJ#3lyJ}b;eVV_u{bV0dMEU1W;`8dBJ_VAhPuys;^&!3%c5wj(QqXb5 zo?(Txb8v1C@i{$MrKng~W>CN+)&eaed0=?VSPyAcIK9<|i=B=sVc$lw6>0%9wFVp; zhOzZlajnsSq9Gon!iqm1;grbR1sH0i6Y(mZ_hZrx7FAIx zKogz))C7HOER;5|r;v@McKR|73-u}K?9=*taYis09OO4hv?aQgS$~Wuk4hD^Fk3zg zBKb8pHU^7;(+G>5c$55V%4^HB+n$!aSL(}3l>5EYz!30_^qNkwYgp5V*40*lgnaVh zrX`q`Iyxs+OnQMk^9`bEW0#!l+DImQEOLmbT6?&mc%W;e2<_1se-ILMd1IH*Po{pp zJRV*P=2yA>4A-g1r5tX5LKs@cw-ks!NlZQevtZ8iP0sd z2R3${aX4Vy1VyD7q%~LZ(o`cRv%iu`jAi$73#)5;ULc-c`F~UgBQ=6ckw*=&zvI{ z+UcS0)T{JRySSJhTHV9rDh5B`Str@$eDqR%Sk@TjKBAdX$^AUDhnuMQZDv6HUQIs> z9-imOWiAm0BT^ef=^7_DM8bGSLu6JRm^5pGaB){%CR&jb*Jib=)#29Vn{K;f`2aaq zsgTQEMagr8pWYK^eczVS11fQ40 zyr+3q1-(BgKde<143rp|{IZU{WcVUS5$vGq&lfQ#T16*}U9kOENMz39mMul^O=@w9 zXMnCUr)6GC4sC?nh7O-QaM76CCp|Lh*3yd(B$gk#a?S&Dt~|6nG0+m-f8!4iFP)jZ z|G-siL#NwdyluQbeTz}m;9;v_a zP4NleYHgHnj!%HLpFbPix3sUSB1rAZcvf<6z56qP^efdl)#xu zoB=3Q*(!vfMX==yp!7p&amjz=!pP6$pG9;&e@>+?Xa58Hb97^?eX@a1bpc{I{;_GR z9{xxk{OI9T*fZ&)huwU5K9H@_2e-@Q|G@?H=VC~Y`RvJIewpx>MGa&_v%)YQ)$aoOQ);M zK~)9)|FmvKcqxN=E%D$aIJ-PWt8Of3GHrQI8$_Zxuex*I}nb zQ_y<;H8dg_f2@oGsmP{+9WM-0Oz;+=YB2#th{KY!IH23eIusJ=A(!6CZ@$@o=|9SX3zi2DzN8bFE_?N%l>~g9b%+<~ce_6Q9z zLB2-vnp(|fiEUF3gm0X&0#{Rw6ctli@bZ+6Z}R!by{X$BH;XYP?Q0 z%9mVyV^igp&4zbTtS5!2uPW{QN^f3fAkdhHbUlQCoDaZ|L!At>0wBtv-kXyx<{ zDq#o_#J^JL6;tm>CGEv(gC~&c_k;}&ms(}E1sqnb^sSSsu%HfmghZgM7*1DOrv-{# z@Wqrn8+@?EO@np+h9kbjmR*lnZlV zx|o|fDkU=po58*jmI`t1zc5Pm`p*a8*QLU(zr|lq|L{Fx4;Jst>F0Vq?*7-{QJO4V ze&RlYd_JJ){$I}-8h`}XJ zz7?KTMAq6eVW4w=a&B2IB-z@s^sa7Y{rKr6F*`r?@u#F``ED}b_S7!Uk>9;6T3XyX z!Jo6ZmIQTN5^IN#Wvd@pV3CsMS?P-zc^y^&l?72DQQ#b%3xuC-;6#Wf(Ns|s$R3xM zgjKF@sP+JIdx&9FlVXxjwHP6XL6b<{`}LH31qfeJB}^1^PfKnh1m;461t{xTui$cU z`qgUENDh6JJ#$KBFq@3BR}DGf5Pm6IRO9z$saqyZq_v~ zb;~F6Cuy)C=D;=i@iZO~o9Py=%X&@fAIhuQEvHmQ-_Qq{{*;Q31q7O6NYrEnGY{}I zP<wD4m;$J15AMqV$M(8_|yWS+rb=ZI3fAtPu(cef{XYA@^{>8lr&PRtXJMQ z;$sR;=)pu8#Jsce*fc&jGLr%NIHG9et4B&KK1CpxkSGZuo@g5<-VS7I7KDBuI2s?{ zu;zl;q_WtUdYoC^duBFOpW8CNG(6etFq!W)t98)jb=|XP4)bLm@ClRax|^B<9`C#y zdqKomKKI6Ops}(fk(YChO}ERCZ)S$p-dj*$E^iAor}HVd7Wuf)NKqzlW*UQCC2a@X znX`VTi%@cMy)U$CT(?F^y>Wo6!>DWhT;{-r;W9r?^+%;u{UnLdhRU!Un|zdk^uMQh zGC2{uL1l`GQDs?GWxqZ@m&NF7F_z0BWQ~om-~hdwHj*Z#qGOS^oNB3nx4uqQNVp*p zcbL!%!UTx~kPN37j)yp)Lrq2u1*^(nB$b%4i0}UP{2)5HJ7Yhz~e| zdV}>2Sx&z2+||fGBe-!z)a6{u*sf<^5k5@GqEtKcoSC&vV`?fao;Ci++%*?oRW)tV z^m_4w`|lqt(VN^Z---KKnAsk9Pl^J2(^T@_1M+9`uZ8XQXy|TgENu>TDdSB|c?!insMEx+Qz!M=>m+{7I{hsrOXA2nb*;bfstGGrPL;l* zO22tEP|i-TQTv*X#?Ba32tYQFw=To{5ka|C5kfffkm`kx04$>*M;Lfwl63+3?s3g$ zR%6a!GTN9@McZsR7I7@%I7x6hQoL|l?x3n{Od<9X_OvdlPQA_j9eZ(t!OqdZ;ftVk z1HuX{K6%s*1&Z_ZgG!eh>l%1!R*qCLauNHpj)fdN*kd2|I)$%kYyX zxp>x?DdnA!3xmvKEWE6@qGeuqOnCk5c^BnJ@+%@;%MR-!dNYtRg@TB9cv)AZ0@p8^ z-?bih&1*?~P{{!P>I;{Zd&X6DmCjkho}NuV?Tpy86sa*x@#9eyQ3S4jR|V6@ zvYP~j)AFuBmainBzWc#9Gp@em%lhpKC@yX`HuXYZyzq=-##Ck z^iGl>)~i=^C{8Ux0@-M; zZ=3q8_;^aS;K98+=S=Zy0e9=4GH2)B2Nx)W5Z@ynNi~Fb5hi-*h4eFc<)tvcr|6r0Qou5{qQ8d=5+2 z@ywIl45h}lhm3YT$`&Rm&-_J zT2LYdxsv!JgqV4XqJmVRc!P`IHUZC8loLkFDbl*Mk>ieS^mNi8nPUTiaa?IyLe zVf>ng9GEC9tiobs{UU&jO=@L$_sIP=y_WR|4&y5C<68y?Xrzn5wGZZRsBD@V(uK9A zYM&uEZTtjBNg35GRA6)nJpc`+x)q%Ya(-J23;0mo0BHz48-Jm~#US556Kl@rwLM+TJD&p8uVu<`Us#N-ZWDf}z1l;&b%JCe5BQ zYaTHHwY@tcKTjZ!L){yshpc9JyyjL^_O`4)3xF6Rw~IxHvm&wV02;G=mt1L zA7q*z-ZM%=j4FdzepWH+~Hh68Nu+sCw^XA7qY^}srSEqJb|56j*sRE-RI73=B-s^mpI1f&srlt6cX;4&{f_^EL{KTQGabEI<2!#br0& z{{N{}bDL1%2W+yLx$vNa8Q;F$ zYce2TDR=_#yd$PR<2u#_Hl2-gp8jo_iajks@JL_83|Lpa$LS%-EQ zURM=apCoJ8))mjyGyAJ5PO;=Ddj=0xMWry(BbASBzHTV7M5k*MzQT8ll#-PA85(+U zKO>yBk{Bhxh6277kgFX-VN5+7Ha)NTh%z zJsvoJ(^Mut7~fFQXmf)1;`$n}3#3!8CvqI(ykcFDT)g^=ivn^#UJ6HJJ3a}Oma)&Q z2e6ydGI;mYpp5sjWI;3{B#r$R7nr@_ek1z>#~A#&dS8{69IH z<77A!S7pz%k8qE|is2sR=G&d(mD#gtnC@#p-Q9{O9P?_)@ti{<@b*L64dRl(5Q90% zmQzSyz;3#=wxNf;VX@2a*v%F@Fnr~cLQoz^4T#C5xw*IIcI7S=`mzhg9=Wx)r-A*4 znI5s2>5)`I2r|q~c|hn{iYIQ(&0X4)UDE7!${}B9ihD*^Yc)W>PIGP?pyPC!MIPgF zkb~r>K2#b)@EmjmOy=0AVc)|BfSo@k?;!5uEryNHUOp3{E;jFSTzNV1_Yn5p4& z0`ZS~7mi4)MZp>rSR<>%V3r%|3tGc9MB zRe2<3@d2ew8VnrgC`vK9m82aGuiWo!cgp=v!4q&yh_e+?~~wsDa#{`WsnE(@%)6X15aq-BXGG z1P{{#iUb?H75Qf1B@!F5K1DP6NSjz4ApJ?Zi+jjKs)oOumau=x7!uNWl|xcA=MyfJ z1k&vFh_8i3lTj_1oxT7%!1VyWmcOOn-<6DY9k zeyN(hY111-pE@A>knZJWD>wunbO7?Mu`gfdC@RQxBVCNyZ2I#Nlbh1cAe9pG=rHv= zPV*+SbKF>mWwXWc22*+Qee)4A$s)ZHGRY)20y$u_KhkM3SvMN3+pb2+7&Tsifmf5E=#u-pSB!S(VDbmw6V`^%i>y%xtG9{&90 zBNO!M+@kL3zj9dinw|0$$M7JE%2c($ws`|G({h}^)HcL&lIJ3N0GUe0QlD{*ctD#~ z=uo=)Azc&Df2jMY8t`@`_ea2@X~Z{va>QZTZ+5m{+SQq(wp&+gZC1UoX-_0F`_lYK zS8ZLad}d|)n2H?x^LIJT`z?-f>pGep8oOz>&T27>-ul*sCCe_hmqeyjRK^>6>L99Pm zDGZg^G!EAxEAm%~j&PoLL8reg76>B^thX}SI(|{Q&-S3tTG0l)0f08+p+pVfzGL8m zl@5exCSZHWvQ=~+X7XqWW$6M?)J#@ zsc+a_POCG_X7@)xfU?0B!rThb(&fxfw)9@>2#4twt1D*Q^c7t9g|KwME%>AAfDtlCg zO?6mSo1OC=mR_?{Xt&vH4tZg8p>L6$-Rrbj?5XcL&Ak@Ke5ZLeFgKnyJBgPeVG?x! z3=s}#iAJy#5C+1b;gSsv#vy7#ct+{z#2q{&=N?F=FlVq0sh8wO*uSZrWUbSDf5t35 zKvxD3P9JzlT>a8cIl=ChcmLN#qn+1q;bxS5o5ev21X3ZOY&sxZ+Tf9$r@9a$!x?tM zqzed3M6`u!Vqv-fpj+jFA|r}?#E4Dc0sQe>_iBAdeA;inen0j`yU_O<)%CH^ zb+o%+G4hbvuJ)_XVXM#6`gZ%Y%h?6zs{L2n3`hn+()V%^pE? zUJ9Z#vQnsFzhFm`$sk5)>Q@`SZj^ntux;|dxuB*W&Uj*c; z1jKy+hgP?0=mbjxPFgk6^^TjjZ8d9aW^TP~&h1?#w>u^~Un*#N^Y{a}QrL zY5l}Xk96uJ8wA3^Gd1iGV+Eb}GB)_R@Y$fYpy|BST}2H=IVO!DKgvY4$>xV6#}}cR zkQZ418PsSDDCpjT3WZPSW81F8L=LNDAZox&6$#nN)DQoS40uBjA)|S+IH#I5REw&? z0a7jyHUp&%NwSo+T7Ico;nnziNv5izdGnQ6=2_~X5#K&L%mh1gsropzq756u!FR9= z&r(#BwGg(AU6@J+$SUosIha2+kPG5rEfyK1N=y4caIr`+TySX#rqMV<#4)8>z+A#W z3Aq`V3OC&tN798jCZ4v2_RboobpLlIn9FN96S&_mhSV0$e}$O%*#+&$3O( z^@rqcCdUUC3-$8#8mrNwcYpDQJTR^DpOw?(cPGAo&-+sEZ!2w*ixrwq=4SwzpkY(@ z&_p@W=eXi8=LmL(9yrrZ!AqwXtkWGDMmso+J{Jbg+|^PrTVsF`kV;bD3E1L9PS6SK z=O?FB`~=&cGu3(+j6Ro8o8bz` z!85mp&^M~iBU)ovvl1Mt;N~+m1=~FI`&k=+k9qa0>ABuP-n|iW)_{5oT;titd<2d- zq12QRqv-h8?Aeum_jj@CK-m;Rw`?bOZF>lU1;&h@R^FPKwh z(`h$pCG)n0-rVcYUvubtLgnVo>~XD6Z8Mo2jSHSjZ62EMLv^p`p3TE`|8hDvs(Q{Z zYmTo`_t&!P_v0^V2q|6plMkJ#_JgCVsjfL=d(iq$a(e>nJLy+}1E}=6;)pRCT^hpx z=}3_8jB=i7w1ksPdCp*OK_^260(ihys6vn#keR(_b;AGGv7} zsMCQ|rV?|{+}uwu!8?V(P%s8AENCkWPH$;w85h|&VY*Nd@B>33;ukK@i3q~x#KMrH zIZ_fUYj!!^1=YpP`M&7%vOp<oB$@JDx<&+A))0Jz~>h*p{ zsI#iqms1q=hcBJ6@XmJo^r9;gjry3?Zm$rDVPj+*8g6=!5aBbr96hWnUc}0@ zU}UUB?v-m*-&8%J`VmG+8~|rpH)ec2z|;!e@Bu>(fp8o+Yw@&kt|qOPw__l1gB@-m zwve<3bVV`ZK@Q*!tpGGZP*`<+ZCx$pUZUWRYF10m%F$4eBZWe}1``Gl`DmPhZP&&q z!!_PjgTheU9=B&G3ONGN;IRo1tB_@kU(5*d83z#YmOMKQ19{K3x2Im{nu;_89kEDA zuW3iZ9G8c+X-#9op^lDV(HN8Vq#&9C@!CAMD{oc6eMO;9!{o~o3Bm0&w3l9m)Pf&f zRW{z>asdYXY9V?xAi!NI^EuOM;xlzYZP+-Kh1_{nH37FfP*auXKGxB}p`|-CM!cPU zo~{1-%U#uo_IS9krsji*@?v)X#NF}@#pSuSC@Ylz;S;O{%(vlCt-EAQ5&P)w;u81M z`aFxrQ5+34UEUOkMspjdkFW7FliMgZ+*wm|XKhOS&fKylwbiO_DqDE;@p+}qblhAz z4-t;VKmM_Isdsh#PcPonm=}%aHS%4cnQfN;TwoJ?4C!nm4mg_Wvb9Bgb^tHw&sZyl z$Hx+2*X&YVt-3??7?;1XCQwL-8q8m9b)<%{ZS6IoGjvO)^WqpCaT-r`k$9L77=)ys z*0Jb$3^xc^)jU(LRukky1ksr^DuR53uo@AaPI;1QoSCslj0#aDFM#t;AEDyQF|Wtt zjj=iBoHN+CPJU_4N)}waI3LN2*EgxZW9#6nJ!c8XTE&xrSVw0p zH!n6}G6WDI)wf`Q@C(0XQRA~I|FeyY&3+s=JtMr&j|cs$cC55iMsn9qVo&ErCUit| zbE6#-BDrkVl6ZB6S+|6VjzB&u`p*szEBAC(RCFHh?oR!LeJo#D;ueE!y}YB!7isB! zVT!+@?l-A5W9#b!bImn|q6rIE&x+L4L}neuE*=Qz#UH&fVZs{|Qwu-b+SH|SyER=+ z8$YIFt;?mwv1Eb4`|r#;^}ykVr-bJ2e(wx*gtKmvYJUy9Qw9K7Rwy-)z7lrwT&jZm<+%7|kvAf~R?ER$J zFaFGEOnu6_j0S_}lM-F&BfKE!BO@L2~kRm+3yHr?;CCn&h(cM6Rr`>&b&ZHvWR zB+fR4Q!zmfg&{bzx0&#twyQ=?7e!A3T?F|u!>XuKEC?C1CGsNCItkQqK9(ux1_fEB zM>C=eRQa;1pfD7&SrO_EMZ93O+SX3`{owB3Pg-ZQScUYtxF>zSWU8GdTncvfBk*qr>xZF1t-VNG9xeqd> z31h`^tC8gy?uao;78$YwNh#t~;}0%gNDLlvA}f4fszrQ?oxCZ`c8Gn0zlMb_)iy_X zIF_3KGvT}$sUz$dyKbkvNoe13^N#(uuv^%YR7V))8Au%#)-D=r@(a&FCd{mfiroyFVNeqCU>qrZxaLwe8j*-c2 zvKWvIYsh&NJw|=*kwufdU4*PdBuG5=+@aM56s@W zb+&ZT?5!6HSG9HSerqSQ_II|WF7}7R?8z@4d+dwHgd6Y69Wy5PK0Nf%@aUNR zBPar~gR&sOs~JlGRNP<&Drg>I4Z!qqf)guJgZm^$V{l}@TqfZ zI5q)N7(!7Fy*TBCs4qec5rDWWb=%^xyxeHfl==;p7niq96QvuMF1h4A*W|J)`5pPA z(u#y5e`$U5dvCYJmoCs*&1FRke(}QUib-=4uAHF8@du%Pz^$ z>vfe?T0@~fH>}s@nzSUUah%Bs_?rJ3=KW(eiaVpvfS$_>tQrI=Yr`FZ;kZ&H& z?nDcseFe&#SqDznS&N*-AXHX{8Tm)o@C-NUqOL1mKA4@P2u*^3Xf}z1KC*GFElOfs9NMI zn8O;~evR4%%~g)e>C?h+rPk)8L~SfbTDw+by1ij`pkjq{{955BaZi1yEnq6Ny2j>r zUi-5mb*-z=*yYMyVs=H{@K>uIo(1qqK*OnK!ta~bB+w~jw}tYXcuvlBy3>3vH4=Ey zI0h-RHYmWQ#`sqq!o)6)I{>& zvV#bodyRQ{Rbx9ZgVDLPrFCXU>p1pdc9ULqtifx~&0oP{$5{BBapOvgz2B18&nzt| zinv@Bv!p()O~g|PA%&ra=mS+c-@<5>neds-EZ<`=TMY7DW}V(OphTiUNV3UE#6~7< zPNy_L%A1oxyoG!-R614X(fEZd8m0(n%gaK$(28O?}+`?G7v zra%2o(xH*{X-GQ+-3a(4O+OW3RH=l$XbM0wW>*0Xgm?1(R&PRkMtQ_wdRURv6D|}H zLZNWC#6NQh3%^5#2a~Lf1R8cAkS>pUQ*7Sl$*Ls_#<$F#U32TrH*VVa$mBJ>h2_gv zP1@dFTRST}{($^$UVd9$U8F;tHuZ6aq=Ibxu3gUugP}s4sQ>Zap@aGPg@xmb5*;<& zn|8h^UD7gbT3emNsJVIlx-p^+ZrekC@t6}L)^sD*a#&I$a7m!(d1Ws=lv+T4n&jX% za*+}oscqeeX#78^3xs%T`{2jBgqy_+2j3U&Lj8$mVTP%9<84;>|I`EfZ3(VdlQ)*e zC8hUjWpz{7JcRCpQAKx>o)Y3ES}GbRBTn2-L5k$14rhS60`eIGb;BT~6 z(CZC)*zusp6Z8(AENO09(A+G|N|aA)UeJ7?xwNF2O|3`>kFHA&u1Kz*q&1nflb5}@ zY_isD(z3(!dvi%?vy|th_bC5<(Oe?WDQ#{pWsjCLJ5#GF5`UtzKPlTpg>XB&x&DQ1 z+g_;OYu0K^`$|gonKW8+>gLQ-rAbur|yq$=ZoR~y3#^aB=%C-|g?SZg@QjkuR%X<@ z9cDAL6y|s&$z_aLn>0F&Cnu6?Fgn0%*mFF#bq=N+v z8wwe`O_{;6z@G1O$AdM6db2|?!RwblTkl7!l>*!cL`qHz;|PgS_0ez6rSh|v%T)D=1c4!uS2L>)Gl)6j5EaZ}5b_*i2s z7z&9NX0iHh0qK0^WExb3Sw*8+BhO(vz+CAJ0<#&A!3*6j$hSLu)|`MX&rql>Rgb;U zzw=|k9&NfPDDn=>RKkY=Qt5#o>1o(yY-@Ow^c7n+Hp`{ zjVrL06$qkH&+?p}d{$Br71LGX4bUt@MTW&65WyYUx3QFGndTT|oXl<&h z@OA2JIzg@1*4nI-qdHARPKP&-IkyJgYZm(*k)Tm5vHJzMurRCZM>?dC77ef>3buNQ zIR=b&9X$JBuMUXnzX=+hU}a{rMl!3RY%qyTI`NVz$LsOHbJ!s{rv_|Vhd$4PVT?}7 z4dyV`Y{sxQ*^S3#%p-3qoN8jjnT=^3)N_ zy!wf|#!pg*s=_&_R*um)b&{!|CO=@rBA3B|OCqj32n|IAkV0BvQCJRnF)D`1a2|t} zON_>(5UtQ&B}FhO3CKiH9fhK}l|h|Rrv^!)6UiBk(Nmo60DB3(Id#ZLmVslFR3*y= z!B%(E?yJJqXFuH6;tt9`l@GH;UDY=pxHKA(9IG$hd7wYYD#W+n_{qXC8*Uo>I~H_d z)^lG>pS5?(gi9thTi+88F}ekhSkfwhUH8PiovV7G5{Q zcv!fxs`Xs0W#_w#7vIs{X)!bPFW5ig#LlYM~ue%Ondf@LQPFGVK5yDu$0Q2 zb7znQxJ7j64927rNwNc}vF(>s#NQ9nmR%<#>4e)$Ma%F_Q8X{-rJ?jv55WHd2r%5r z12-SHlLiy_Dj$+6Fo2wKcmi>grV=xaX3xaRkn=}P-k-`p*CR@(y`rz89kv+#=jDIO zt0`^(IO>$uEV+6LaGd0xz5lUy?|(3Of|RoP`{eVj4uD#JN~wVX`ssIA*&X}jhf5oZ z^L#A1Zk?R;i9PhdUZt#%EeDXvhP-OQp;FsG+jPb~%&us&O!*`gViywtd*pvO2IwY$ zEad@S8ZkkcNPwB&Gq{nLAy?!>u?K z0@x^zw^GjNJq3PnD88}C>V!dgSW-4>K^%3cxh?6zc8D>=+?lEi&gii zt#;EFUzlz9l~pUhnoP>C@~imOX8z&}6Yuk+`um7;aA1V0B1FrGlxaBCLsrTN&%nwv zuh$iE)|j9$$l(?zz{UBvuHk9ZjUS+v=-p0JI?9vEh#uUu_#g>~+ z9I9~?Sc);H6@9T{GcKjxfaf1qdWNb;YZ*q{kflTx>V&W=dj{i|6Dpd{8f=Ac^VmA3 z8cfh7Zsla(9)`ofOcqqZQ+=8q=mXl}o2J63FNMHMl#qr2kUKF=083Dr9;AS1f$I{% z{UM42@jEmeLKqZjFdYVYFzC_r0P&*ZH5i)f951R}iT34VlQrj0X|hQ;ul4_`q6(R&HjxqyI1yQva2L&u&tVUoq#0+?C@u`5(4><-(Yfw69 zM)MgY7ZOL19zyU&Ah&3Dd5`+W%rw~x>1rsWDOzjI#D7EHj)J{%2hL6 zQDg6v;&!vCP%n6#M!&#JYI{Mbv37CP*jiXwpcf>6>5|so9R@4RJNPH4t$K1FRh@cB z^SOE&^vy)|DiM*o23BxYWJnH%w1eu-W1?9RFJA=tjV2?)$l)YI92>=@ zI&extAX4bUF`K-3Efl>9FbVRiuWbGgJjqzpE~ph`F9q5A7h99z#=R<_23WXl>EN@ zUvKTXCix&+Jav4zq_J2vnrnVpQC=>nEe6xLrJY;nB_F(UYT^cq3By2WYH8bIwg6<#(YQuf)_rLM zzK$}q^_cN>-x#%dR!?e6!0)II%z3JFLfoM#XsFcq0bns~ci0TAh!Z}(DhlC`L2#$6 z^$75%B*aC?NDN|WN2H^4!NV^+|L}ny7lwZ<-;sLd7+k!i__0?~PqL!>3%k1)esS>N z7wQ%{Fesn5;#bV~T{hvDsS^2vU#(zA2HBtUe<@>%LT5<2s7s)KK_nith{U35R8WUt z^#wh)2v8^h0aozV(XpD2)lf3UE7XwoB@09wkf>IyK^B_I8ah;85?s{XyP|tmv(3Iq zKJuCqDOQfM(p5#1yB95AFgLXMrTv@Ra^iliXHw^~ISUfynu(V!U(iw$@~8ol5SY|Z zYl+rOxuCg7t#QGo3AxBpS+{7}<()#TW#;^O)0^yeZ?(oZt!w+%>)3a?wzdRCOMZ^Q z@Sgl{=8xvEw~kvJI&<07-E%8l;hEFR_VzJR5bb#lQ@2dawL8Z&wY61QZI?{ZxF$^9 zxak|6Ia9jMSu}TI9efFv__f})cw>R!oq5@umV5{1k9gx%T5nTDRH%a8%nkqHzryxO zUf3=ko5Z;+3Z#Qt4r(|%{YBs^rZ6wkU$@L2Cl97RnY~5&<;jxF-RMMf>bHYgs8rClzow^(gBx zJF|h|PmAb+)*4}pNHNOVC=;lXfmA;ArKJ^z>_wS4P_8E(F6L++el!mtsiJotLDZL&koA%;!_`kmrnBt0xYObF z6~0_^F8Fe{st#1Z%ULpTX^wiV13>-COsED**bl=NE-u?zfMH z#mLsxp;cFw=9ZOu^Ylg$+P=!bxQTW572BL9cSn`o2x?(3Dsq>!l+G*MyS?}7kybl# z@BGT~F40+1Kfg*_F}-%lOn0!tH+%eQ=;k8-x3a5&v!lA|bME`x_p!T4^PK=oNJ9uA zY<82)hZHtp2}wvoNMlGs!ppq(?t5?Y=FLpzW50l~4IiaIDMri>u|-5gtcW!#(we3b z5h)_piY?-=h_PaeNU^rH@{7U$xihob1*|{c?wxz?x#ymH?z!ilduQg(On(+DsR!m| zvI_(*9-cGxqLsy^pFPrBnNyfPeaj>F;3XXkPmkZ5#$7r1XxxMtOO0s*NK6yS@RUxS zuD~B)p|oNm9PZ*i2d4-8^hPE%JqD)q@h59>`+i1p?5k&vf9;X>sozedb8W?$-;d*| z?Lg8{$DEn?c1jo>r=-G)lV3Y?{Hxf%TvU>w@P&;TzoVqy6Tx>raPIfPeTpAie~;mO8eXHHKb*@F z(Eji_kp2JX6WSl5SDb#<6Wd`wVDH4?8{K-TQQ@m+ zLS?IRY3i}F;_uj2pl75 zClU7|W+4OzMtv1JxRn2tGcyuK8(vLzQ~JZVj6V8c>NRG_K`5?Sq3f>$4Yj_BPe;0 z7vV-#dm`G2`Dwg^E;**HKnOnArk|1SS9vH0UMo}`A@3sBqv{&dc`Lmiz_>;X>^O){3BW5ywLa2(5ma&wXHpGX($ zhi!m^7}NR@xDJ($@#B0z19%aqP&F}J*hn4L0^o=C*TC|3luLdKOu1YfiG}g5-{g6jv|=T$m@&o zs6WABB9D)PS28mWAbI81ze`xF2P@cxGT8if&BNPG@*h z0G`uH#9Rl{f5dMF_LKd8|IXF6X-BkIXdOB96!v9amROKDoZOInIr(1dvee_L)9D@Q z=Q6d->Fkc|k?b378`_>|JA=0s-k*Cdza;-qVW2Qvc(K@5+*^FCeW3k`ju{=BJ09=c z)p>X4sVR%6d~xc))Tci-JZ;sq2d2F{ebe;EW^A2ta%RuW+RS4!e==*qtZlO%oZUJ5 zzS%#WvwzP0bG|hf`u16c)=+=7{@ty;pq$a zUwH3@#}_SLba>I@i{8Fy{zbbkdUA1L@w&y2U);XLTJl}omYlY9&C(-F-@UZ|(z`Bw zvwNWX$z_L@o$4`r-sqj$yS?|N<#U!_zWn&|pR8E5;`4o4-_E`#SI%E~3|FDwSbg*A z7uU>KQ(p6>Pn@{C{c`j2qnE#N#r7*+?Kk@$>VIYJv30Z74X-xZv@ zZdd27y}O>+^`qVWyASMsVE2jL-`mr@=g^+xHzaT9yWz+U@9f>V*WdfhzP^3K`%dxS zjoWTKQJPmew15Bp*Y(5tv*pF*d&{p?u$ijzeD!Gc9oa3b^5t4ztyX)t-d{gff2*;z zaoi{vYm8CjE5_*qmmM$<9BCGs1I@>qZ<$NXhs~%;)OyWcVq5kz zj&L?RuN+)*@F_R#Hr%JZJ>Iu`;qUTa3AP3=4{jZNX=u~XH->kNR7dxYK012(rp-4U zx#{(r*W7H~{Kzc>x4eC5;i17pj~sgO(2s6C_twE%A0At9_=mS0xqaI0qqjeI$DBKE zyyM|Jr`=h-^NCMS{q(DMeetgEerEJDU%ESe_ujjoxckj}`tN!A-dXpKe)tcghwy(? z%*NR~|AfK-r}ZO*zoPaihB_s25e@f0dDt^d7-KyVEO38xLj)(Z`M5(G(%@848;;-< zo;rOvg3~DbYy@Y({nZH0YO`oGg4?udbR>fDjRtx=f?v?^{k91Hy4Fo^;=3ao@s`Uj z?OLoLC7uiK($;G>Vjs|ET;r=KtcPP4t|Kf(i1XLtYb8?iK;1&T9ifi5hMSs>uR*K_ zzpdI1a9E2g(rb{~0o+yi?$kEG+f^#8Wipqp5AfLut}f~@luTXt#?Vr&Tir?Sg8sT8 zP4E9A&o)RRAxkK^3%I6ub)jW8+Tv>sq`Pn~VWZ_EsKtQ%4b^TgQvnp$S_6$cp$w-( z4f(+9cpgYX2i)!^sC1NMyn#F2!2~WAN-yyeYRq|eslI3xVu+O@&LySvwp-*h^?!q6xN^co7xCY1NIQAkw zt5ddQ{N5kc_Jq*nBOOH=uh7?UeOS9syGOfQ`>e({SCV+pK8;;iS>B$5{h{yyfvuHNWp}Ba?Hoq$WJnEwJX+GXsy@0RL(uK5$E~3SB zG2VrD2`>F!O5NDm)r0ff<@^)_zDTi(R?`~1$n7%v1a87zLH)EAbI_GEKv&Uv>;cJLv$;R(WmGz-A1?59dsvs zn(iWeewOZ`d+D=uAAOGQr(eMH1HVWQ&@a(Z?7V-FewiMkU!l*_7wBR7ReFSejUJ_6 zr^o0w@RG>i#8-oUi@r#|O;6JA&{Oog^d7VIM`WN~heV^W9s0liEAPCumoz$YSp zOh2Ljq@U7%(R+mV4A6hm8G0Y{KXz*2T6R*TL|SA7UI!_1c(F-A6a}vMicaiznkqgf zritldhM1|%7qi4{F-Oc5^TauLrsF)(CC(S~#RX!4__$aoE)d1fAg&VY#nobi*eEuMYs6-; zMQjz<~XMc8cr8F0ote5jTjvVxPECl*E3ai?a4jQ4v)kMNQO2L*T7+ z*c@Prmav2^9C1*%!V|s-#Gn`w!(v2?ikrmE;udj8+$zSzr^I1#o48%vp*@fZETg-7 zZ8yg~-Q97#EK2u8ac>kakKz?k+!w_wqj*&mua4riVcfGmj8~}mD%6vzo4V(vT7hR& z(w@}aN+T<+L225KOf``9lb)};IX;wR%kf8&fhXN$%`jV8zfm%Ew=RX>$S`bpzOb8V zSGMdynHjb1R>`okDz*bZVb^MD&!}6vnW)(Hl<(?ZBiXQ9G7E09q?>-yH(E03+IqE6 zwTCPd0Hd>UA{{u4OBq(#9?mVuWpr0S@R1aSdo@5-F%pE znYrwJJPBcX0D|>C6-mX zX}!t}p<&1=tA?NQ8oDb}m4<|dxWkH`FP&0ZuQZ2rw_2>}P+^?P#z2ylo^o^;0Sv=- zGBw*}@`56d6N*!mNXY}T;ulcQplgRMFUASggf_Emu4Pyem=BFep)+<<#l?ex zgi64KiQ5dTW{1VRiYuk%HEh2a6$`DR4Fy9eSJtf<)LqveQku+%ppqgR!hw?u0c8)H_@==0C=!gU#l&)`}#wk&{VY|jC%vU$tVDY62?7}bjLxvB#3>D8t z#%8Zlh0x+lsNA&^O*xXpX!f#^$X?NJ1g)}H3LI8kN0ef5Io+llNkcbldF5R~pOWDY zg^MVfhSh{|hCQ5d0e3%3CeV>OivF|0HycN!!4x`7(Xp&f+YfvZWG@Ih8e zjrY7V@vx%yc<_eFoFY(#Gf{)Haa+?N=X3x!RB7g6Vi+{6;A+D4yhNi~&6Z&eP@a`6 zOVi9(SgkcE)|a^ky0H{mw*q;*XA~4TZ7ODkObLy%bk-uLPQoY#9g|RjGr176fe*LK zGCkyC%r{cL?lrwMJSue7R(1_ptLUE0vE_#2Bvp6qz=2z_nkg7$P)(Pm4iAy21U|ab z8Ob@iqwL3UlAb;&bKEsCdk zTe8|T{Ctf?LM;a*M3< zf~sIPgxRAi{!E&wO0S7&BW>yqN6JwALd!05yVPhbME0)iEq5@m{ZO=g2!{QP)>;-C z6Vj$I`#$>j8{~9O4m&(V0it)&fsUsZAStf}K~go$5LTik8<{$0 zcSo;g;pUWGWO*&Y#o861Tnp^FnuU%rd+8=dP*t`mfk0+&}oBi3yY$@+znO zEXWI;wAV1CS#6Ienoyc4JVlk@USUIl;WeO97tT)d#4}u}!a+r|w(gT%B;25!Xu3m*vR~n4vTPe4vz^Khl}8|= z)6mNpk)__A)l4}z6F?W*k<4x#5}-16yR1L8T@442@X)z@CNu^v#TACdA`t||;-DUMaCk_l9+ qx{Kk=rVu5YQ9XR<GPS>b$X_& zr@E%wRZdI{1Qg`ERKc?6xc~A0WB<2^i7Cl^2Z(%A-2Y_45ThzCA}aRH^uB$9 zZxMnHfc%hCWMKYgf4_bHZ|OyVd7v9w>)U;^-fxkDfPgv7S$2Y(>N|cju!HXysQ(p` zsg=9QH@g46Jsf$-2G#R*$WrR zL!siQ#}&N%w0_klvWRwyOkEG73-*c8@-muo+C7K=Bo3EnwJa2(a7H43$lf1EY>~q! z3mwbDz*EeaKAD%~!kO0Da<=BcLYl9Y|AkDJC@+d9(`X+~b8i5nitUFHth3Kob^|K4b^+um zCzkfUZBhJvn6ir5@{`bg_*ZV3kqLJlv+x=L&aJNfHpm5oTk-ekfPQ^}Ai4oNyP&<4 z4wo2xW*l46c-}VDn{&eVe+u%qqksC#~wFzVQ80u_cqNWek zbBc>7*?S&wJP1z?ZJE|9HFP$>!(E>9#}Ap1>aQYQ5{}2y3E|wz7&jtHxVVwn=%hQY z;qjf|^^)n)ldPiv0xXz?KE!&$l;lHOUw3+jrV$bPMc!^m7S$1Rb@bVn8fpmcJZb(dkg+ z@wt!x9qkVViWH;cz*ZTCEDchhtu|2t*sFa#t3yk{U5eg*0j@NXFmdy2gmq4a;U4d| zw+Ti^aFMFVRuw{sgP`21@$TBW+f}ke)6b9Z<4V}1tn9->HAsph=1duR5}waeP+aCN z1b`;+bQy!4; zWAS1tVL8em;&*91yvo~$NY~6YK5>+OOFn+brPzsWhB3F&7ys+#>6ZD2yZHTs%Ji0= zjCppcIO<-@cdXvbX^m{?~DK#d`OOh>+l3d&lcz&JI$C>^4TZZGWx^seZ;RM^z0S&l$GBd=)kwB*_S zSXrWfaCYlS=$YSNz+arKAJVqi*_9oqUFIN|rWr%9cE`qOEaNL{q%rE%+s zn2dxp#y2Aq;f!?q{U%gOA|zcRnZLcxrJ*5oaG}C#G4(h2+({}3sph5Z2uOp-=!o*B zvEA_9ALloGI)X^c)m(a2E5LtrP?2Evl#}0E5>wYM+8hc2bEEL!HNWYx0kza0h|D9(I|EO;H%cx zz&r5VY7r(XD=R9tV1|ifO!Y1NrEH(yW88w{M_K~^&I-Dz{p6S&w#WDnvMCUSFP)>nOjbYLi|+d@eZ-Z0-%(Fmv3*onRo_phiTs z*<<^mNoMQ!%PQ@?Uhq?_e$0(YE&Eh_s4zh9olq|UZWT^@hGr3?9#o~~Zhw0Bgzl_y z%H`~0d!wFfltQ z$ewvMz({&pSbm{NXgKFsWu{mPKwAiCyhT80(2RL^sx&hTQo!9G_w7YIwv87L z&EL*@oRfq;GY+a+UUK-Waj8`cl^LSY%|AanbldO`&1_#UL?&Gbxjnim(w8aUAjIVq zu|-rOsAxqMq2V8p-K$xe5QHuvgte({1?@P|@VYDdm^F`yM)nTT>aVON_|Km*Ei~*E zr@%m~S~`bi^{S;B==r(ZDUmxOG?I6IGIODeHC|I zJ&$?qS=jo=;M8<93Vp@EsFe-9Yj<>r(oDS@Oi%cI4b899W&FS2lSCq36kv`XNT#5( zpf0w(hgHuqXm0Enj+ok?MKGml&6~4ty}XBn1~e9Zt0uln;j9wIc@smE2+wNneD<2`b!F@FG2KIL~R0*pnjCX3Y1jQ$Li(HUa|jkS+am1C+1#x zVak2~*An~Ocr8A&@`1ozi)qJ~=ZadctMC>cv$s5bg<#t0V8Hnxwhu4orpP2nrw00Uc zlYMcu%$^icmD1$$?a0GpmcTTGc8mkzC2wJS)DQ{I^2LK?l9dLSJjWY_aZ77^Zz*tt zc4P(+XwBGLj^^Qs$q4Kwi9Fe1^twrXJU4_y z#19xYv^)I`6b6c2=B4QPH|!#FW)RF#+X?IEmFkxV6yY9Jo)t254Ib5j-xd|M@^K>p zxg_qYevP4}x&G$P+7BmmPUzK>x*Y8cT$IJ)0OZEv6lcKx7ITe;!eNi8Ee2>Mm(bCd zf|k4xm{7R)G^I9h_679;JFu?6N{Uh~ANmG@OJP+ELg9t+M@ZSF!DzJQ!Fex8d_Y&n z3ekTwY)0P~TY!#Z*Jkz}?@7n(D14NQZgbF`@P4|;rA5b5qL}R)XmJ=&7IoFWtBg!F zt}M*`RwZyV3Lp8!`&(U(8?F^E4?+HzS}?N<|JsUoIF|MKRHlKS@7%=gXW#x$@qlDU zlT3~3zFji_>C|5oU9G!)Dn87QfE}zYS4WCZWO2o=WJP7lMGmsu-jiZ2^vXp$`C#x? z>dW%K;p=gOm-#PUPkl-6N+NdDF?csf5y-%Tda7O1YRB@LcON{EcN#?Tz}) zWAI#6CM@^ZQ5t;+1YQz~&;iilU}`7hA%AE{pOIohR7Y{bqXdOjmRt>M&UWQ~Vcy(G z)t#ez39hKek_g*xGi{VwY|GE{^B@1Fxn7LNt+~0WHlZ+4a1()LoIberY?m~&=G4-B zcXnOET5IJVC(3i<*C3XWkJ}7sC|D>MR4Rd1{B+;i4%%ocroOwg=sGW%aBgmY92bTR23baR4$iRyZ*1Y=A z|M>#^7&ln6VZ&qe-zB~j*ToWEx&n1xhlkoFE;;nN9TwS11}8(aolu8i+A=6re%zE% z6ry<61v-u$o!cWT@3Y9;5NSdL!Uh$D)<#;-Nx1JYt;-9_j>GZ{wJY>Fw)c$%sjc5u zexe>U(gArOn|f?IbY$jE`;$uW)t(<3p1$1u%6|6EQlPZpgns>a6?`}J`lDx zZ~k4=6Cni(G}dT)Z9SChi0~HSpJ+M_6h%9BQP<30U^z^H^7Rr2`~=ilT4eg?>r457 zLZULx-&4J#p8j_|`%#_bfr2ST@uS!S3QJ&|mzRWv+|@AOa8j77Z{MwpQHkp6I-xb( z_v_|_bY`QVkzciuol;93a`vQ zs^MiHr->$DQ-p`P6~Q3&^mI)f-sHTTwV<$ofW6QE&t%rJs>fj2s)=g}mtnhsk-I*p zc~%VR)-`5C{`@usmN<*JbqT4Z!Vmu#eX$bGP=W;MLOHBA@t=0Jtvf;`-hddU4t}=k zSK%YgWd*P%yD|r}+iO>C0|=gN+t&UV^9u$*$X1`T@$b2dMTn*aVkCBEr=R{#J>v@E zbRlOsdb8t{)^VkO2TK8aqnVj?e``bll#StP?Job(v`beo8&wSH*ys%dKLUMqC}4PC zU%kpgcOkmYTg_iktGxflzP(=`NtiO7tF%TChCz^MW;~tW-8_>&E-`JYM8n;sXeX-? zVKk@vSKZ4V+pZn_$B;L>aUUtV<@A8(he74E_I0&&)`~{Nb$hDX$S=&N4%^*KI-^VV zN$WRG>wc0ZwDBwR*e#R6^+C?U8ziJGm-yTt?qoyaSIC*4ZR@m0?QZ!CO-6^~WYyCm z8>V#|fSd&%8$m{yQFsT-`*Ka2HfmtFEXK=S3_pzeC0P}xX5<@6wTI@>oGpKP-BJe% z)JH>4UQy%uvZ3@Mjas0_wnwcn&k<%9tcihE2Pp7k|Ne&!TjFH`M@mZsUn~&437G!W%z(AAI(q~1`EakbK07<{iGOlA)ML4}J-oG5fWt9w)YWD1x%#l@ z{Iwi29pO{FP0>B{c=Ae(FA7Z}1Y;2S{O=bi$H-?@{~^;PiK-l2|VRp-*vxy!A<(dM`QNPyViJ12&Wy%n%&V|>03~VFw9YCiaPALOch&Q z_Sf+HlkGG4DYzM>{*71uF7m2BFdpH}--V8$WO8LN+A}QFO48--nJf4Z?XsFaIqKv2 zV8e&LktQ{1Imj~E5$%6-cWnTvClrBbk^uoHQi(CLQ&Uo<+zn|B@~SmT6ZfQOznPqq zTS}9bnnHgsIb#8&k|#Xh_CT4?{H$Muv2j8RnX5Z2L?YsKoI5#eV_Q$2zC_We3g#X= zC|BHD-;*lnLrczI9~f4dLqYcL*b5Gw+xho%vhGj*GB}FuMz_)Zzs)=A$94#K{!eAO zL5$K|I*q)&#cM|aqU5Xaya5~#*VEqONEoj(J-_27yNne)DN-Q|Yfll)Qo6|IQ=b;q zNgTSYUBfRpR}DD9=gMYwk&k@jkKunh*(vv3qmit>m?Lbb8PNN0f#bQU&WUQv+`$-B z1T$o{h0h!X_aLr0^6&5q9T-G4sQKl_A|u*jv}e%^NHIhMQNo`CpTisGJbw#3Wli_( zx4we*8a7aDxTEM|-irl=W4U zo@ZTrZh6F`I~@ZF@+cSTc)g=Zm!{17i#RIA_FfF%jeJg^WTY?%fZXHrx6hsK!~H=l zHvHKk;kW}>wrSBhahlN$gCvqdYjH?p%vu5!{Z_w-r+BV<*2zfFQK8qNx_n1X6s$>u zQ6~zqxWRHMLdQ^EhK?}=c+IL1U5X-_Z1&QegVztgU>EO8WEirqWhd{+EYf)~a@=TeOSqCgDZeKe;1KeHv;S1$F3%t3$6ssViVjB>yc&f9=GcMRY z!>x#FTAOw}*Y0dGo1Cx0e*%I9n4oo&IBSXBA<9$=avYwP3#!EvBjM)A@7y0m7f3UNp(@Q9L-?jk@MC*ca za)TGEoDh_~W0540;KZk2>x9wZ3(T?WZ*6Lw=F8*8a4U{H1sPIFX336^8PJI#5P5;@E1hu7-Q@pkx!tLSdB2wSzf zyBFmixHW$o47%2X`R=H`T!$6RrYEZd(U;(m=BFpk;-E*~+A?FOJ24Vlm2->Ne>WUE zSK9l?a3p=Rf20haZOOpi%OhCL6rf~@bY-0{ zxcKfP9A-1jZo4ZF;@1!LaT5oohBZp*JEsxN$-o)o0?=5aJv7TqG3Bnupkka9El=*! za+>50^vO2!iG?T|x7?@V=vHy!123AsIi)3!7>nk0Y!lfCU*C+!0m$ui`VOmj%H~d`w$yZxFsI;3Z8v9|2&wx3J1jhEa$ts1jZdApJKqFL^;fH4 z*M%w)tma4khE+iV8R?njIXpXfo!Vg#M@yhEOdc=VU8ESwMI(e3v8}TFL?Eb&|m{K!{Ucg{@(mQf;V3>w2T4#* zAEt+k)eRJ}gfqF}n>*2x>ha&=r4h-=r%=Q%129#WsN~1uk4T2Ppmo(W@Y_Vk*iQ+^ z9f?)c1Q}3cXNmih-lp|p-CAPk5LTOE&2%s~43FZ}fV-Z>M*DIuwcD`MrbDh+5usH$ zr}rU^G|<}zg_VkseUd0|i}<{jP(xu~5bP4aIfH!RYt{1L&(&>;EW5K^r_U?SE$EJ+ zx9g3=39XGM&;+SCDHPU`G_;7()Yk81^HD;p0`70Bod!noMTae_%&!<=RfO2T7ln>A zIojV4Oaw0kW-a@MuOlrT9*q?vuiN;iUli8-O>c(HFT!sAsJ3NzB{y;a4gw6{@^0`F z4J;VGA>saK!$}h2c<;yzY7^=wi6YikE9T>qZ5mnq`Ps3CI-akDVWnf&g}1~+`b*d^ znbBNa#R_>GCTt?JMhzw84}w~JsY3+vn13 zj^9Tp7>-$r9Veq#1~yM|Bps6aPspt!>ZZ-4lq}_IMCEof`-iC{9RvXZP5g57Pm~U~Pt5$1zovU{%mi^zw!`_V;rZ~V3ioY? z7?+xP1upW+&=6%FNUY5oK?aOS@jP*Z2_iI}uMYh!A)95{Uh$NAI%8*xE#0GT48P0`L;pO2L*9U*c z*=IzuX@##EkH^~8Y3B;zD*6yh0~c`zNkfW`!-S${i2cM(S!+TDjs zIi|HnX6Bv3up*wc^6j^nlw#a-8)GqaSca$^#UWzJYJsTF%HkR^O?gE}rfxxUj@|P; z?0R`mn|CGZLgplF*`j`&9rQ^}a9x9+7LACEG<1c91CC%Rl+(u>^IQXJ8i_K>7)pAy zv{Ge>a_a3|EL*DTxPQllq`|3X`~$cUFUbL>0@v_L}9+ z^~Svk=y*7LSu1;imj@*3ztdAAunHDWT#g#OLuUvzQEI)GSmRhVihHUlGPe+zF=(|k;PwrEOd zBvUSPFVblcER<6&Y6=UMv>cejqse}Fu(;*6Cs>+hB<_>y7+O9_He~P=CaPJzA~VGV z$4HT*eb&No5^b}uk7%BU7P$I@PEn3$PX-TOY|WTn^BC5~R9=z}7M`NtqBSGgB(YCf zY=0Pem~>xvr_z2z_wdK0E9v0W>0}hv>BLU&O5&bEvw}e0Y6m=U( zdM^gqaBpy)UkOFrbR&_`y`hx_gQR7sdFa)UX$sPIc(#sC%w~yTvf!n${aMB7%=n7? zHgPt_*ki&$-CFv5Tq38-gCp=0E4hP>9VwzOBb@;QCsYS(NJD}siSnvn;q(Eq6WVsx z)t5I~e}4s}tLC7TU7qw{RylYhI<}f45su60Fs~6@F5G@z2mfZc zPpC~{a?CyV&}glU`lU#rW4wy14PLojJYiWQ-&>PBPMCIOq5sN4(fZfVEo-It5kO>( z-0cP+c5NZy;sk=hGun25?MzXw?2Nl7RTBt5yf?w6X(yOadjZaX;{9 z&eGWy=Dx4J5J{naM2Z=u+ZCTy&ik=?;4n39C#Y1&XrfTYliB&nzt5`j?2v2EUqi?4 zXW5A8Tkl*)@)mmw#GaOhN?fO-Z6VB1Me6m92vF z!H!j>Qb&j6K2qbyI7;y6T&?&-93O)4q?XwY(%nACKdVU3*6fp+*ZnD%JGN)aVkx~T zzYjA=%u@?RcO_F8`;m-TXF$(pDjSa0s9N{wMvXUunti~`5a=1=5N>GPo;@huZ7Blw-Kq0(b4S{JP+f3PgUE{qHl{~6mn+njuxTv9vj zrM}(Cn_6U}Y*#zKYEaaeV(zsk!L&ilA3I(GAe0@cA-Iipk`{NOtO+sT?is4X$I5j? zE;$*+x>C=*(aAq8eQ#DC6rNO`ceN#h_V;!Uj*n*EES8tDFj^?#Z!=Vs6G6jc?@(u7 ze?Fg&i6w|8Y!cQiVJ^AG-pb6P5RGI{88{h8sQh5OCGAV7|}0x%8|ZtpsoZ0Vr^u3RfP?`l_m(qr|C`chpN*<7A4R#7tAsY)7P ze(o8b(g^jk@{#LK8u^+7q^}KsD%{3T<{l1S?rjfE+&{`JMVA4m4lc;eN6{|H+az&> zuF@LU(BH80t5MZ8V$k)fDq~?lCXc8v09z02tRoo~76 z*!*;*C-|lZErNu~3hNchWdjtr!!6(;dV?W#4Wwse6P=XvPTc^Hduzw&G?!7vrH^T( z5qmKj=U!afFIB)dxcR0h%^7iDZ5qmx#e!dRn0^Z3^IIVtOwR_9pM{Uaikq@NC<6?` z&u`ZZBfsL!1A5fL%J>l}tC+JSqqrw{K1H&8b!5oQK=w+@@r8i*bRC_C2{qhw5D^nW zh!pnJ;SX#T`J7tIw(83E#P|;HH8UE@DTnG2zk}{ZMNP)^Vkd_@(K4#MMuINK?J=eU zlhBOH+>fVSq zO<(JrTlS@q^juk4-D=-yk?@AOC02tM87gk`I$m$Fv^XE%ZLXKXcAGor#SEF4h#&S!P5*RR`0exopuGp@Ue$7luUpBn5xa#G?)#Bl@1h7*%(#8 z`>}yaCVLD4wxk;R=Z;JXMMaghD8BB;ocenKfKo)np*y$hF@&$R(_+IJM;r3jXK>7* zb`?;w=F{O|OVbLn>#;dG`}J4DgdiO6c0=KaT%;xc?S<%Cjqhc}6Io&)O=hX&J>b%d z7hT|ZROSj>%aILdsiNht({eHLWm^Qj6>7=>zyV*kOD~Dm!HALNH~JCP*uAlUrPbYP_9W6wc%2qIF+rB7sE#5OZ%Z0|Rs22~}tK1kE1ui5v{9OA)(+fv0bZ)7tE$ z@uwq%n(Mlsv-;-B$a(i}cw=WS{if^DxM;*OMaVx8nF<%3uOOMj*eH%fA*t3Mc&>iq zjUlP}*=}I2-dPOvWB5N@*fF^WG9}?1oiO}yZQR%3y1NuUZ*Vr-b5);kLTm#&cF|iq zo)fp7r&ivhKKUxN--D{x8%1vU=zWeJ`<7wy!n1#NXCBM>Bw$JMJXR4F3Rbjb9!Cr?&_bN`Q^gC5O!ott+R%cPpCO zVs46N7O{2py?O%}>IZ2}+%r9m%EXl#V!A*j9z$VRHwE#ATM-Oo>-l=8De{X6)Pr6% zh8^(2N@_6gtl1dFemr>#EDWl3>d#7O&#YMNJv8NWxcHz>xs!0`$sHUN7ItYhD*L*2Pt zWDaQST>!q7(`_rr+42rMbLH55cUhy|%=fg^aNpLj|9MXzP=XXxx=Qs#iqGpHT8?&7 z6!OQ}G@>JZ=stZ+0hmO~iy6jc5)xy-yB4h$c#NwJ+m1gRCD}9&c@aR6VVoe@Y@t46 zu$#l1e0^Dk7;;|LYA4L9!JR;l#!%=H-0Hpli_WnNRZI`}1|!!3padFbEi5*>se_!- z$;nE`adT69GCE=6*CGl0nhQ6dV>W6;$+$f!4g2eF6UGbKNv`H@Fs^xdkT3uaVNa=y z<<{CN(S#t`tEs0%!+%_h@H5Q(zSOEEb%tFC+wBJX!bNe5n4gt5wt!*{`lEW!Xzjdy z@xgq<826Y?GJ1r(GY_b%zm@p7U+%O9ZC?kiK~3hspk&<9n-G%A4kjGC00X=c;rOY4 z#q0eK7k+LNc$0dDP+S%WPD96u0sZ2)$W+Xfv%Q*fz7F*YD}3(}z?Dpw60k#=j0o`& zl}8FCNN)T)3NO+pjx6sdjB;PVNSYrya*ptQy1s-jLgERQ*32H10+YH8GRaxf>;CS9;>dp6+duUCX~A^mJqr&MvJ39p$&%X_BjC zgVm1gi9G(*d17rKP+5dSL03~s4)W1vON_ACdjP`KEu!-vOZT!TyDGBYVjw;k%tlNm z?H8dtp{pThq&; zQKo;LPJ(;9^zV*G7TzU`xh`CoDoefMcRx{gcs!oR$6TbUKktA8K;p~YV`rJT=4$k+ zsVbUwpc4a|Tj6Q)w$yO!uvcO1SKi}=qMYD1qBDk}1>qI)4@9y+%ADuUy27QkaW4a# zltqU72AoTjDAUYeKxImvoFf`kXKrVhj%EdN`pB06y@+N@;5!{RzE)DBCouxJ*Q z1lz_Frhk_*Zi*!v&zZ7Iahel}8Pf%_N>|E#GG4-ej$AzK>s{Wq z2x3@14@^cA#%E|&chd@$?Gb)r zu!%HgjRkf868>Q`z%hx6tK3pwJ6?|6_x9JKUo>%4d3$0GEp$)B>$2|NZB1;_2Y+Q55ay(j^PTTI%pHkj? z=n<&$@z#9Z7<#~unCY_Kn(pvsd-5@Vd$L*Q1vkGsBIyuM+d$J@^$zr{U0&tHYPr{L zD%MGI&EA}IH|JQ4|I}6qnC$>tzQw`3`do}tmfd$EG;E8GwCovgMP7qicb<>5Ca|Yi z!;&*I%6bY4o{s48a@*eOBJAs0f+y0{?J^VFTk5dcezUk0b3pIZ)y~i|UJu!`R8p)? zI;WD4RbKp6Ogn`x6~gJsOS#4;cy=TVW#iC91+w`UcfM39bZ~9W%sXa`H3~n!SvtsT zOm_F=T&V%EgX^_R>(+v5JBNR`=-$kP2B8)m9eg5?)cv<2w%;@B-of` z(1h*SaZCdov3EU_Ch6wD$#xLg3pMvtWTfdhKEBi!^Wk3L1s&6olVndKi$=Xu8eK&Y z;0J$;w_68rvD3=)bjsH?VIUQ%i5S%UKayDHyqwf_w&gdMH6K3GX^gg zUIv=E-B5e?zwZN{8lIS@qkeY|c&>>&I%FKhPl%pJrLE-`=xqXndUGQjs!GO{P^pvh zk^q71UYX$Kf%=iMR%CPm17mq*YlbT>wQe1-=JDI@vB~3~XtyDNX1JZTe1WFUrDv)H zo(-yrt<7@DHriz~=83Hm8QGiQ4Ehv0@l+o5OhnjvSXNZ)(wTMMZIFlDQ)%| z=!E!pZxd66Rbe=Am6Qo%JjPf)p?UM}YyJolDk#3JqEMp*QY|7e_QQnmH@G!B!z}qa`UmNVmA?Z@k`~PA z@O~4A&a&r0Rr~QkNZw0*275Gdn}+o>3)e-M_x>mwp$#0&e_$TxRxXjHPxDYH@Y!MV zuo?$y1ZqyGA8Q16Rmc=YCr?JN=2smrxRD^Qjmi zXwdWMIHIM4O~0q`yfrS{xqmwu4{n=q4$&UA3xO z&oAYXNy}Zs#_}2RFGSEEp zE`VO_(PKBHgWnTM8=rLf2K5Umfp|(us$Qrf?)V9-+qM#GTN&5pEDD_vMqQRT$t#3M z0(S>~DBWvtRFUv@Hwxq6kHf!M7|3K-BGqJJSWB%22>!0@o?55>^tw)hU_!Dl)^67O z?Gwxtt#*ZJ6O+w#KdH>a2ZY)b==-_JYbh4Ru@x^-4eZJN7^4euUgsgr!OeWwU&~;B zrSGX5;*q<6DkhOPWnvg(4+x<3>Bp>P&_TIK)m^{*3qQw_9GD;AxS2f_(8AB#Ra7S+ z^Y8RCz3bx?Nb|%ta z9y79_M3F+Qe5f5QS)`z-pR@q!7ks5x-@%-pv}*wk)G{|ECA85<*nV@Y+gw*6X!sHE zD5B`3VXZalk#4}ok1L0Drj{A2SK5SRq^5&62d`*K`;ASdfR)bmwJ`>l{zETY_%RE%KV!$b;9cUhOO$ zUfZu!Z+r=-!wEiW<`q6laNnNpk?&mR3d%D3gq^6-*|3m9n11l&{cH=6^gQ3INb!A4 z+nXr7T+b;Q&d*9ni^EUwgWuzym#}Y3oiHR@atrQ2`_s>E8V91=7F0pHV7n=i{nxC) zOd2dvV}#nB>I!Nxzg1Y_hmRUv^dBN|69zn(dun=4(jS}r5%l-f8mXp+x^a6Y{#L|z zROt|?kiT89{X-cs#mCzx+xfsO}H^+UK`i=@#P!c|kTtFDOfRT2Uy{wvGV9PaN`{`EqZ~eI=^PA6nF7A|(5?HQ zkgnEOG+ThTz3I_N$Wh~^R)YN!mJSAT>Ka6D>Rr9oAJ!nYMMsk;yaoBplHy_fg(3yu zuDQsAS2r<)RpnLEC?P-320<@{bl?3PsgFn$k9mIu`-Md?u3G?8VpFR)c+PgBTCdBG zp-a|F7F&;LSaCPSQ4`h}t5>YiRB4cvXeDJ`QaH)4eyf3pw}o4=u-u9TY2?seE!Loo zS<98TW0C%xhcPD7O|GTgnTVA7M^oBMIx%8{Vb1R{#AQM;@q5<^28&hYH8GqdS#drv zG%y`nl=p!!hVds`G)lHVcHnYaf>}FJ_>cGGiQejWF}u9fWVsW%F}#3=gFg?o*VB)d zgU5oGq?Vr60xrCo>+JQO33I$5sMHinfoq90ar8qKk^9v?|^E-ahz(2~neOa1OT#p4KDp|p?ZTL$#XuHFw(=Bw6 ze94Q3l@ng|gxJD18tHFR@AQ1%;m#MXp-WSDUR=-q?Eb{H+3TFMA3Vbn5HO`=mmp=G zy;DlWPRYq4OUXJ|!pOPWW+rb+@za8qVMJ_D47R-d5G?6ViPx`|J%A@AyF|&ID~nnk zGnax5oie{7q&1BbN?Yi@K6P`PyMaC*hirbKKJt~VlHR(sWXK9`7zw_6+Jcz|Ac`D$ zrl7i#W7?7_&~n$CnRjlo=wZRjX1X%%<$a`htos$Q`LZr1;QSC{^4X0#fMNT%D292g z%Fy-I#;5I@UWCw^%pf01h!wUesgvqrsog8Ed8~aM#?`laRds7*Li;J;+tqE~I@V#L z(N#jk{h_+k{=jsZw!dcn@Q^}Vt$uFp)p{DQ+j$?w)zFdBOp~GNzT%D^B77?mg&3Jq zl*=73X#iH#@iTdNu1kpWr=~%(9dbwRh6FeNBJ>tWO~z}!tPmUDVCTfaR;RtNHuFmD zWUD!2&BsIIBNPE6*P)TA_+>hG#YJT5o*<5{Z5EenF>#0fjwhtVs)nhPi;GiR<-?TF z zk;~TA673(NkVaj(KBc!w@05^onf3r){p@)dSXW+z5Lp53b?WLjJ5O4}&eE6r=G3#l zy9na&jq-~fNu=eZP^F3@M#1VeV%Q;f01*?feWPUTUCiQz{OtlxQ)i&@(#7sf8_RFn z_zl(qN&8!`sG8}DRNz9@oyZ(9k0j>gd*tGkRe2Q9bZcMCsT=#ykBxk8cCY4Gdpwh0 zy*~CL>-Yx0fm$;?pN@TKAG7GRipAf5#Ct~Cv$1(>jow@A%?Hzd978^HCH=@W`nU%) z=`da;>@~y%Ys6noaF$BJ1F^cNy>H*x^%%cTvmR3HCGw~F(nf>cj$+TE&m+X8ZH>5w zj_*JJ5geh<&LG^&-3>MYy%*rG^(k7ws@ z*_b@N#vePW%*V5wbBnJ{$8pss)61p$TJkZ175bmw=WhhQp5(Ib+)Sf5pivxQ6zlO6_a z7r&o1Wltfm8fboXwM*@ zalz;j)vkuSndmtIF_CJE`<2E-gZiOYt@q>xMD!(Jvbu1Sx=WwA z+IJPe(23K1LI1ChdzPLb+7YUrTh|UD7TbSc@KLI|%C=5xH=IrpE}O*9w5la8YxEcv zeV4%MfIM-lweSDZN}B#iA|}#o+Oyfopn2|)Z#cSB_!yEau@Ar{XjGwJSbJMrd(RH* zAS%aCl37VG!#y5G2!6MZW&nf_F#W~qK{Oc_V4Mvrb7rR zaD`}!x$m4bqEVR%Kr?fL zq~QKRCFhO|PIXCZy;8|fbQPb;0^ECu@y=7uu3o+kH$<#({Lu|yC37Xi_2_&M#UP_vB*vzllRG-w1(FRoe6UqPn$t=7S42cMJGFvl+IRP=vyce0b_H5T?##eWt=$YhyyWe?nneKNYaUvqieyUY8aa+3$I)Ln>|D*~Jl z<4Ewq^?;t%9c#%ZRkJOfdR#GGrmDn)lZPgl@3BQD-x5QuuO@^qO-Ns^AG7mEQ3$gEkR)fL~Y3alDY;Pl&n}w-3HeGCb3d2QZUKx?qr>rf; z#Mg1qkMigkZBD4a+RR%=l<)8--dW2Ay=cvslI70vs?8_vtv%oGOZ za4iqRHSUYxDXJ{^+AIq+nny0%+*4Va-JLEbOgR(EEVz*Kn7CJIWsW$3PvO~GMqkz{ZqoU~wYPiMoO9t$Le-2q60_uwD`;<&V<9s)7P^2IFSOJ!r$Yj5Ci>kRS? zPk+I@I?EQ?J*F!&@WN_3l@|$AMNNKAHmq#klK$c#K#A762^-MdahNGs8T4H5k4hfJ zRWPh_TyaB(Dt@~o)m@mw-E$A4opDDRKp5)UbktNSHf;wal=;EX)RVithHKI5U~dv5 zEML6jw9DXf&g^HeIX?T}A-YbjHweU^tM5+J@7g2bmDlz3R~UO)12l!)NlQ-yRiGMp zl-KgM(YRCBbT&Tc8~|79hF07`a5K_oQXg^~Jc#OAq%MpdrgVS?BsR+;jG5TP5jf3Ffl+ zOXvV|59xBeeytPE*WLESN^7lfpZl;gQiB5O_KeD~>}Xn}3brqixTGo$F-0t~XP>gN zT4z2ra&~LS;HK_HtZg-6rY82HZlf}7Xl+%L`{MrxHbBY0^g>0um3@>UI$m$`q@GtQ z1M9?AoyS`1oT4wqQ?;v&4Oc}-Q&;G8d4V-+oJ|s{&pAoYoorN2Zr8bEvpfk5a3?-Y zAI${6CN&fE53C?}^pxyAdgGKG(F;;M;gVBvDN!bDDU};%#^hwAisVc@kz`Ra(m-wx zJt1h6gu9)UP&0G%Op)o2rtX0>y|#;ZnEX8+yPizK!%|4zxD{v(VOnH{7RazY4>epT zd1OjsQbH@v*pgIaMb-=PWg=C<7$xkuwZKq3!ZyaZ8cC_?Ak{6+n+1 zmLiOwlFjG_tUCf&5sQsb!!4BSLZ5VJqMxA3>T#5y^<*ZZxi;_VGUc$qbH}N*RA{lvE1e=RDr0^|+ z#V_zaUX*15k|^*dRgjHdNsQKpBuO^&gg1g&<|8)IA{Z4_wDLx?QRK}wg8~k_0gR%- z!21=oPOg(gFew&dm54>b8b#5-%Rxn`afpHdykO;9+a*b~ldwUwN-}mxCW6gsuuBKe zkVS#;icx|VmGBm@124I|FmJqhwX%+;tfp`IU;A?pxf<$~aij@!p=HeBri%52Z z(IbfxAr`ZX7wZg)*&*8ea#SUvNhYFC#Dp$`wZSR!ga}3=0U)mL5qS%a69J<{OlDOE zdPN?VEh@cyHw%O|9)}U+7Re@yM6BU!MIL)5D#T=v4M6|dWJLk1LvTy7065%6SrkR1 zS(d~GUM9TYAr78*S`<5PHu4T)^Ei&abT_Z^P6=eAohOQ5l4Lqn1l%^!Y&1zC!Nnx< zHltOr5S%-r5`mZ1IwIKZaFU{s_B=R1F@tQ7B!fykfMDSPy9Ggt;Lsauc+n&xc#Dcc z0B~Fhh>`$;T@s82A{qtBsPd9klpPj>T`;&MBG54sJ+@lWV6<3_B3Ny_{0WR%2+B>9cFnbADN)m$rx zZh^K{V75zTOrBBf^dB6bv=IksuT! z1R$;iU*co2wurxSoZ5~0cGcYX$_X)RjEu)*_yl>)+xFJ&x>C-p>!#W5+N<9Y z@4d=sbCm8C{)owA7cyDrBbz<}wg#xCq>Bz`7e*HohSN$zcUDmP=PuJN< zy@b*sDF06J4cCc&fupFumKV5D`cW=wLjNOKW@P61@ozL&W^++96mL%Dq4c+i^!HUF z$9R+;xng#XD*m!>M0JQ)IT|#TS(`h-shUbZ{v>kE!f%@DHMQtthUPfc2XDe(>YEZ{ zb}8A+Q8~pn_MMWdF$lTKHlQNz5c~eX#Op{xzZ}2`rEjXxYis&Z^q~`2_6OX?J{Zzj zb}-bpQRMPPP7CVnlVRGmVH^Ug0Fv+9s2c;{SZxz$A;%dBWfi!`z6fMwCs3Kul%dKw za{1#$x(zEE1|{_Ipcz@L$ZHS4Id@^F%O485OM5_j;4V5qrH=sJ1?OOZ>NA@g>3tMS z1Lt5S_64niFU~A-@qd^+Um!6d7d6O5bI}y6ZkB@9EvmX4BFF5TJGdF#Ol}Uhl3UNX z;*>zK>)eDaB0@0v*Q-n1xbj!5nF$9b-@^oMF)t~lAj=;)fB%Z@S4;g@%%0mP3gbU_ zt@JJ1fAjujeM;$b*Q2_fJbraanv@T1U$OuEN0y6yb7x=CFI}w*3lfCFN|;-$6h5Gdlcr2mJ|5RM#**QStS6R~}q>`hTvx z;;Pka*J8=zy(OEIl+Rqp?*9-jxU|j)Pylo zE%X=&K_cylINahtJLhjbp5HpZ6aJYio4Shoa@yP4yW|JjyRQ7&Gp@Vt489ibED3S# zn5V6TFE+&BPHjg_-*%uR%P4b8xeeS_?h0-{ciWh)e-Rjuk?nB|Ik%RUI>XtMOpuky zG=|x?W7yR$!?vkVZE4aegE6CH`|iGZ^*WQhX~n*SE9V(4d-hn2^Hv_*w_=kl zHnp67;O>1ZH_4dNa54F+)nT{f10wG~zM-{a`G#|sB=lG7@{ZQTl5;ocFR%`Utf%>S ztB82guZGA7?wG^WyuDTM@k9CIzrI3DL_Z{b+NG{&#GXTxZ*QLfGuj7lPp?|K>Z*Y| z(yJOQ#>I<`mWEa7I|gQ7m^f`!>W;zo86fn*UW1&oN20D=hWRfz3j1W@kAyWD@XDU?i4Dj{SYjDa{@DC8QM1+f1&+?d|vy7_8I7+x;*r26~HwPjs8o>>psTU7EbIF zuNJRnR+(L8ttj1sMoFN(q~!pmFC2{d-4oJ_S3kJxrgKOCx#P8m9=wd4sdU>dO7W4? z&f9u$fH(B6$gS!vKI045$7|t!rN?eowDWo|U9q;C%s=-NyB<83H(d7Vhkm!C_=sY* zcPr$q!9!aw7#RI$@2cF2UNXNXULUN}&cnDK1@7-&yW&zTY|}V-II1f>U;nlTlYwL3 zjTzIgcO=U!uZg;#;w0Z11^OW%j?d>^iuNa^-KO8b<#D)q9BwUNrJ;*q$Jp&0&xXIo z-^e~nl()`MpjL5}73`05y2S>VM+9 z)i-O$@{JBlctA1ya=wX+^l$o1MpKKUBluo87wkgSpY|?ScLAd6k za)Hk-`!)q@yFCn>yqR!;1RLeAP zZQZQd$(bt`cC2j8)^=&%(Z|f{RQb!#Ij8B7MzbR}aGiFcc1!npEP`a)^?eHEA> z5E#>yNiw>TR;s;W1FC$&4z|kW03WLQf(pZam;wmJo6}ic>c?BMxke?aB&IO@0h9cL z@A|#%`)>rHV^`lLipeUPS6MsKYxi6_Z*E`TFXnHV6?+>#B{zB7V~dt8UUt=`%Ws=$ zGf=wmJX^pfMy9v)%wC-9ADrH{JWTRq-`vYZrk}n3sr+@SIT~MfRhP34Y0CRL*Uz4{ zcJbV~J+4-N%?U1%zGQQDMx?df>Gn3-%?7LG!uCKsHjRXr#0@iJQMaeg*VR35)#Cap zzUVph)=7=G>4s@ppE|O#*DdJ-;&GS0#-sOE?{TX>WHvz1@_MpkpPQlSJ*sDHcLaLYENxz%vX zxmL33#epl3)}NkOEZKO2RdU;W@g@D+E;{(cuH9YT9=oGfTjOz^}1 zuzzBGC+j?x?dUNn;wty}7>%1c?xUxyc2jbf$sUMQw5(!V5bmfrwJ|4eoh(PQ3u7U^g09FvhQlnW z*h8Qj5hd-ZN)9s?#8Z7){Su<|^-CS4q~FdC00Yso9XCTU3-p0cu6Z;@m$XM zw81kMhQE@SdEnhcm;T_|Swq+CpS$J3pgAbFOI}y^x=;M(GkZVx&YJGXt}`0`Z*%Vf zA4hTbjql91>t*+v?xfT8Q$1Na-JQBl#g^qNcN-g7*v6I%xMPFcVH=E1GX{)lu^Bd2)ZIb^@v#%vMgOaynb(GPq9+38qe!&#@{i%qyEt z{B6RvCs*~K*l}L@^r>1iqhdK@&8zp_eBZuRO}KKFNOkiZ+Y+1cDSR2pOF)v~W%E6c z1nWTXzh>WgX?K0!wkz6~-{E3ax(cIJY?*)ft-CM3|C4!5p3U=$tJ~JknpiC@S$3N& zJyQ9(C03-@gsBx+w&5`@4NlduI+cLqiLV)zT$GIy>0BN;Qx{J%3}HgWvHQVr3`a&~ zjb((z(~X31_#>6Hck!(b+j$rF$6Q9P+E^+2j0GyC^rw$+S@EDNVE$y@1>r^Uan=>* zx36k((QiDkMXCr^bWH822(`C`BGsHhsb=@>lO`W{Ys%d_ap_M}IO&^8)Cb(_7gn}; zbdd3AJVsA}&m9Dl_-WwBm$1zR9pLz~OKWHK_gD2Dn7Q*xXUetZf$rJu>$}I-G&+6p z#tEAa-4NnbtWFi5x_IZq4{Yhf5kln789oYmz9^(B(Hy)M%@MUB1r|f_+r~uQEs(BF zhb-Wb<0$Rsy*Ry&9B1*2>n5#+=?&zV>~x5BEQ+K*+(Z%FMD!Y^s=(+ID~;8h(H-qy zH#^$3ac8`7b#H8|yLol{`OB^2;)}u;%-aJ_?AzBhE!5r~a!2Cvi2Ir&(tkHzx~;d# z?@HW#)08;FsbGoo=C^)&buY6f(@I_Dpxak~nn&Ydpw3s<+tj(b*;x?jrSELow{zx! zzN-HIS+$qK*6EdZ&!4n$LSw7XUK6Tm?pj(uaM>PH)%c4#nkU82ueQQj?Ha4Wp6&+oO_}@SR?FH~F>ZtgwO9qwk_nwFZ;j%lB_9%lJt2r%p$6$&MtO9@X+UOo?Woxf zbG#-t+%&aJi*2rDQ+FQTIkik)z_L|`PbKh}#3T-X9I$^&tT8+WJx=t20|x1Sls1!fLogOlF&Ije;uujhE)rrV`aH5O zf}~iR!6ip3HATneYi0g(Ihg>1qzn-pge1m6NCFZ^BFcgP^0jd)0WpS%Hp@1ghFic^ zkKBWpc>aCF499c=#+ke_%V39A0OO?0^0RO{Pp0sJ^mB*j>J(8_*iGU@{g@+jwA?WO z`%(#!y(pD{eKMVRRu*6qrv|j5i|IR+7y+SxW!EGl5Wb|V{y{LYzI;iybk!nNTX}QTibR)ab9tL;q4c1q z<>FaW*<{;dx?$)866tTR4*Y9rSygp)RoS*b2f^Iw2gA~-IA2xd69ivT6(9f9R(50S zwEkZ5&L2f%{Th--Se{1Qu*hM{IJS~_J4h@R#yb}bRlsfbl9WwwzVswm3|7pBGncLS z(K68TlWTj!Y7(o;w!0^QJ5*0rMb*lYClLvH#npr(7tlI}?tTrl)*>IEpQ+%i7w z45!`(*Ml#{jXUTXS6BSk;amWTm%Spr zf5$`8Z!hA3V!ujn;Je@4(*Nv%88Z$%+rQ+A3H$TB7Q0si@y0tq;VX2Z^n&#ME0^7{ zS5=@mpoFT${pj@9&{bXS2lBicmtVN{vR6s4{XUsMCQ(W1R|)jB)BtK$T+)-fDluzsBze*lSo0(6e;V z#G#W6ssOq`ZBZ(T6;X?BrFNj3D$vc%5IqJxYxJq8RAZdF^E6eC>Jp@~cp!3YHDAXT+0O7|gHi8*xS^S`Zj`*(YYKmBEw+AY%&wwY>QHLe5bW;xBCK zHJEyCJ76+Yz$N5JN(LW->GQ6>R`h;%rB}QbBW{5;V9FQQ0U2osrYWP3f}QqCox?8e zW~VkyJy6m!wP}M+KI28Q*esuylurG*sOVk5J&A8}-51gmnQ=kJ1+(D!k3vE$k_$0x zJ|C44^L&G|01eU)3I+&4%BgX1& zqkzP|0C#{7!5vKE>QDBsdvQ`t-@+NKYXY3&>Q8|1$**(ZVrJtQ*kTWZ;IU&l`wSWr z(b%>uzZTg#)CTZdI13^JI6D>t5{>Bv(ks%x?p)P(f!9-55t%mmR-n4`&eRVu2E)m7 zAT_WJ-wUDPIwsNo*z%c2>gr~j#A21M|FM@I`*8m!=YVZE_072v8@6qI9gPp*G(~Sm zW0+g^QOnMmn8?bGn{;9T8YO5y`sC@&f;#oSwun&~jm-1XDn=n_1@X8fcJ>&! zM!|^mZ%wvS+X^6CXrN0j1ZusFuGa|#MukeMUIO!ZO6Cl=6(fbvZ4Qqlj2?3zacX;q z6Md8;aWsu|$WwJCa_VBAL=kKCm|Ih7p}b8J983BjMi(rp%TIeuCNpP`u~j=InYkA4 zO-`vz*5zcAB+~S!Qw!2^Q6~H!qwpA`HL?X3tCU>EO@<@wz=%yUnaMZ@Q3}r**j)z9 z0S`}ZM<A*)YFa zqt=R`k~$6M{PY^29lX~KQdC(*84innE_Jg1$dP_5!qiNgRs%cL0j;PCg(fwre4Nq9 z`BY7l^4CKlm8fOmQ^0st&y9aQ0O1=;AY6ilQYPzjQcyM|LB)`6=9c|T?ooy$cQz-y zc{qU!@odmYvc*0LDS??JQ^e8>lc)|9D3{)XRL&7qSHhq*vmVa{3GC(o1HhHVvrS!u z&YzPa?|eXZVPLnDR*&X`zN}nHcxwz)3AKp$ZAqHC>{rFfm}pAJ`DG^JxwM9(#1;@U z;po3C&IZ<+Nun5ebD2LJYab!11B8R3U0hR(%T=><^1%4D`wr||JHAs@s!C|z*Cx=i zGqIwwv5BcFD5%u7hD<%ZJ*H5rwz8n0ifL-BT(RJWr+)g>4GU;ul@8UQySb*+PTW4d zvU2+Ni5E^+SEz5j;f7n$V)})*udkl6v8FKUcR2jDMOIs=rlPjCq9$as7S-Z?(ZZUI zQ>xeBzVz7owzl=h$oMbg{if`s|q06`+|laVe#AF2iVuR`ZxcE~tJu@s>@187Oi?pfH%3~nLeQHqdU zTv1q`(U3= z0DZ&ux?;oSAD@= zFkx@Os>80jo;uf*{wZWRz7YUMrReN$@T;X{I>hCV#J#`c(gO!B?c8~I<3fFH=ZmIg z%{}YZ^)xRtz1ULR-(TDkKfG!|Q5pWY%Ze6Y{EggJ=N6But+=*K)Gyq4cqje)bg)Y{ zhh1)qsX0k6hSVRUiE;TbsY;p-mAJ&n7lGcTD=OzH5PO;Y_HatFSw2D}iJELmM_0WJ zaedD_0XwHMHhFPMfV=o4P@F7w<8^P7QN`H<@7#lT)pw!Rq2+*#c*_#AwE5_J?;YK1 z`u#xy(c$zVDNc|sCYH@Z0^0C7A?7kW_c}IM~;r4Gd1p9>2R_<7*EUd9`bfc1%X@c=%|yHkKlvl66<>6@t$wL z;Hkr_PEo54^YQnN#`iA5sGHdEa+Dr7uue*(lIYQl67?e&ZX-B|*~4-e?Uhu!ECKM@ z3|qMyk#1s<@mq$kv)MDf`Mj`Q^@Nb1zAGQ10cZ74WIq}jPVU8_hio#HK%c_USGeQT zYV>hH8Md~M1SbxRT>qAEc|bH`)2_WI19FZoo8i(cp{ml@yu%#1k&%ww?9A@QEUrN? zMtlM$Qc4lOOa_T2vp$68Tr$7oh|H}jjr40x5uVjg$r;269HUTISOWU8uCOn&YpFvt zg{OHbQKSL&8kN*Pl*o%uc!5mpraa92(SEZ>sGm`PGtG)!IgD^Bw|+Wroj$|<)BhLGhiBM7 zyv!hRDuL@pfU~H4=J~;FP5(K%;(7a0{~TlIKmQM&DE;%SCHwA13`jaC3uJkr&)A}P zmT%@M>QB^H|M$O=|4A>+4pn*mwE$!|4!n`!kyXtgY#xoNA9iOolK&&U`}_93(^#`b zBb$sD3^IrE%9BXnFVi}+5KnYe z_Csf2 zV}<-LHLBEc84TPt>OOcChOj#)~X?ZxcahJn+Xc+XZU}Fz!PCkY1%zy1>AoE9p|$5;g@|4uS!f5^HvGSA&U0700
    V$fDV|Iw z-#ZH8@kAo&8X6qN(~8+vauls2VmxK&6M~O83OR_xEJ{?4GZ$vqTJvKqld>-g({5yZ zQg}d+aKr=sA0y&0N0jUP@W+l-E-5LOEh#@sE>(PF$z%fAxLms77r=&*IN+7kRQjJx z7)f!ZSVPr=oSQMt$IFbh6K+)1sO%~!q*8%5&`OO;C2axw!GSS%A17;M5BiZ$*&=OG zjlEmuazo|%&rG?fTpW)wL%EL1HO5Xj3qM@G?|$?Ia#QdID%V)M;Z(V-WNSazpDuAo zHTG^?uBp_uOqiK9ti6udyQbH z7slF&%5}!-jR)gpd5^eM8FuGfZ$cd@efF?^Lw`DUW0CO< z^$j>Hd(ZFP3C{Gk$vvk6Efc0^$@ly>ULd&WOz#BWvl88NW3HUvv+?Q5Gc;$~uPn=r zRWhFHXdVQUGplXawtz_97=lfQ!*~!=X3>XZ6lF>zFbX>YGXRsEBW)b6aADX4IvG0s5>sZmuo|SX_=VFgY zV_N(u-2z%#Zmb-B-g06b7?drNJw-C{joCo5W2p0LD$Jl_=S=P&;L@j0r`WK(^o0Q(Z3C5IKRtzxnfznlS04*>PKd z>}{z%K={em^tQxucw7^D?Ay>{)pXE~wjeP=5t?Q8z zJ?pT`p3G+PRfp?J27A`gi8CC4alCt74@_cLKbiUtuR_AFeEJyssWHo~gL!HWlJ&?u zollK)_7iAoRKeEufCMi084fVXRD5KK0V(kr_EUKnv`I=y8L5J-C%uhWn$t$pYh7_C+bU;?Rl}hhR*GXFEt3B#)5( zI<$56?5(qlZAhas}%!{evS#;{97qv0-Eui-TYy^&?TElbwldixSgj4M$h z))~UC;YHID_Z_%umAmCCM|jOW zt8cvfroAigSsiv<1^RntcXrMm{<-ADmk&V zWm(&{*FHTubN;5~(`S2KGp8-zG;hYh@bAcq-$Htv!(Yi+M_ZYJ38~(xc+P!{iD^fX zG7Um4Gl;XlK&=eOhgz6``+}(79T{0Lq^PnvHmCe@5s$ak z!hIDvl`L6km;NY3n0U#e0uT^RU5#y{G7cjyG@vRDvh^Y959NnCP9?MDMw(nQdY(lO z&-a!WOE=pL-il(d+VaFet}4esV`TgfTN;+Ydf_?YzD^QH9u}La9 z7DndQ0+W{?`&1hG^w@H=1k9($J{U>n{_>?a-E=9s0lH1k(xp9io1qH4nn%u+lJI5A zbGJdm^N8{8(0tBLH?11J8i!l&grw2-qYI=-Jp zgc%W^kp~N ziT?%F2@MCR93o!O(W+_qW?c5UGb{)RpTQsdsj(kgSKrtF9SVzwIBJVf# z#i(7<7#ryYkQeFy(f~QnfOBgx1=|pL5RHFj5jvi>%~_~2YA%+}GO<0pk>nZ>+ygMe z1(^2qWitP8peU0?#)y%y)l4=V8r%~P?4Q}X?Ec>4AAEH(cEQqEtgxbf>#2*pMZ^hK z-GKuht5K;_cj<$>2QZ-zBD#qr}X9&8x&Y(lUL_<7S3-_Dnvj0z-uy>HwRi` z;yMj$5KK6)DN}bA_24q9hMGWaz~3Rqo1-H6MeD%`8Y-2jIn1O|Rx_#>I*96Ow*3EU z7CL_7#g`v{=*_q3kN$qMNo4D^HDbtK;jOS(?c(wit3^{;_15DL?5}j+bn2o1QCmS< z(s1E3ec;jO6_-4_R;qh?Q{^D1qzgG4FLG*zq5s?vQF14Zkbice;<+;L+5fB|u`LP7 zCB$Cf!+Bw&>;)FnNEa;Z9?O8BVk!mQ5b=)Ec+@H#+iD_J=4BP)K3sYFMt&CaDS3W9 zl8pFK<}`~*iDq<6n1(?DF!c49#e^%zvaYG%c&Oq)?3(P@AR0f*a-ILVBjfJ9k> z&LfN4MWsP$qbPD(PkE$}Q zgaZjPAVo0&5|Y40)(M!q0g&!!cOGp7ElnEmm2~r5)?zhUrB z#C+q}A(=C#2oQspoH&&k=gfHQLt-%-N$&tIqNU3J;nT9pT3Z1JJNG4KRn#Jtw6-F> zh%Sq@O(_c+$)=55!aPkD6UlF1?Sca7ypWzI=0>EC_5EEdiwd)N@_EbMAC0LZECcbta4B*30Mi_35;wu$smZ4!_cUJqxWN& zdGJRPn1N=yj zna!UAqhqGy#==7BGr?;HJ+o7{d@g;S1`7fL+9y4l#sdP=%<#Ir+oZmfZw+oaO{s0! z2Lk13iu46Q7U8^P<3V!%z*Y}PcMt(q3aj>f*SQtx0QP*Y6Xq<9xbaF0ONY@-aQl8G8fq3#At70 zlfz=2U0^Ksi*yHgGSUuv9X@EGNz+Ik6W~OVE!q%TF@mAtEj7 z)ImCs&QZ_5y|WMm@n#Sd0zdY~`hjZ@AH+Wlmm(+91n>=yS`;g>t0@o04e^`37`?!Y zA(7mXut<9&ZUX2Kj?Q%hOy&&*WwslVYZH#pmw$8Arl4u1N`Jc~C7yp~ zKQLVl&1es;D7XfI9Z$amKTb(BQ#EZ#XL>iP(}eF+C-%&BqQ7UIK1oRoJ-kjmYc9TO{L*EUm~&L=53e{X!RQ*b zuk2{(4EB)v0Hkm2VrBe1%8%pDE!gxzdO(28UD!IB06i&6dX)Q0uPzu$1R7FQpw)oZ zX|ztGb%GnnL_CuVhp38D4_Y#4DcktoA>(JijQK^-z%f3q*~9CgjAot9r6%;_^4wVk zJV8&yh%rB~aElYNGYQy)G6@sNn6bqWV~5DZKu9TAFuk<9veSRD3s}^iUHzfv+1^s` zni;b%ar&Jhf6wB>O21MIAcVz!`taf&e+ccrWKPc-bk^+V_=i=1Wr59GQE92K?kS(S z5Ii{pAKD%~5@eC6p^DV|J1e_Or!QDIv%IIe-cniNwLu0#02pe-rRkE?N1P*`mX^hs z1mUv_lkbn>%~{fQ5;Pv5@YhJJ>y#_Kj%NWEnFU-HCL#Ud4+K^*ZDRn`AEZBElK}yZ zL@TGMlhQXQam*|oPrNHVW7{hSNA9(Ou6N}jLdK&cs6WdkYVXODdm;YC5wS>?*+^nk zJMe6dZkR2O63CJ7JZkj3LXN6Hkk7|(u$cTn26YGe3vpTnvr@X{s_m3i=t?`j z1zw^%;2K_%jcu0slRR=P1NtsSqe;gS(#tHiIun=TTYCSV>{z;g)6R%NQ>ZaSc5d3g zv_lSRfpM5Pb$#okr|Cyi)Z7R5Y@gX}=Q)nIchB6u=YhHMK$y!rPvc#9@px!;8{Pg9 z5e}obM`Zb=g}dw;YEd+qe1|^29Aphm<<>D_$9IHrG11$OS@h%u+JhvvBybT>5F*p% ztxr2e+)yme{vqsn^6wPVZZwf|2a&8dB^ML!Ps3FDLpVK2=Ag=yI~KvY_36(V=aOZE zn%(H2pTOThIU1b)kw&3mXeqANou<~_AWwEXmbx0(bv2t9V~Ig)HELL~u5D#qLGRvP z9SG^vAW1XmDpr2yeNxh(MkGS&MRpCBKNj_22h#u%PJ!)~$7XCW zL7kM~l^S(i%g&Mhm-GqE>6CG!W>94S+xmJ=g4ux8nHX701&ME^n;-A#lddqR1{o!O zX(muG2PosB2_$sTv|+|it`oETM6b&_2B6(yG>AG2TDs96?Iw8L-0Sy9k3FU>bksfY zlJwY1(tqLKTbZE?f85wq22Z6}I$q~;4|UPc;6Kncqr3ZO!((0WfJ6CX(ORTcWw7@- zl0lO1-l4BuE{f92AS{Z@u@=`Lir`mbExdAsCG%Q*6ok=vwIaTvK|UG2eMY=^`T6M4 z!8E|WRhb5}&woCA89h$E9l9+DOD~gx&=W>JAD0RjO)lok=sbMIxtO z8^lSzhmrKK80uLVV#h18;fP;!2Z5Vr{md%E&^1+XndSNCw2xT8Dh8~mNp06lb!;M$ z`f2JH^sz@$AHN@oTqAwF3@nAN6X31ymfU?e>A#xOaqhpfe$)QO>AJE37ndUhPM}`uYejXyYa5Oz${SuvvgY-c$tG_PTsdF zk3&^}L#-4Xg{$iX);v`?Pw6y=GoEZ?3y5XFcj=@&DlIoD7_I93Ez)|aR$9O1e5H<2 zn9zvXXHh8h%R0WgSr)DvCLDhA@Pr0=^PJOM{MPT1`EA=#0-)U;#aGJ|Lmk1&Qnl zI)e{3N<(DN6)&BrD69u#`x036I!_L$)Sx&&`cclp_k0K@YJmwI7l8Vm+q6cL z_BK%b(T|t2K&2vk`PZd;UeXFGCH?Zqn8=*p&M|_~gAC<_Y>4O*qgWpv!(mj#ZkNko zFzQD!0i%VyvxYFj>-k${Qy z%W5$pMWHG6ob()630I*38FQ(m4x@2nDj|CO!)o9AYrjc2^X2mkQ|JjLE+veX6!ZTa6wFkXmk?^G3vr0Uda-lLrS8X zN=dsBJyJ^Q)B{?jlBGo5&|Q;U61p!)6bJk;p-$>d;&55OmnRE=U``eo^%)+A%hR)a z<$tEd0W1?O&wq=b!sTgM0G%VBe49vLng2d><35K*c60ijT6r9JP9PCT`zdK7NRu<^ zN5{e4bfmVf54@o>O79xAIwSBJrBl!)4W|2DcI8s=+sP9bQeF2W4O~+R9Tycg0DF$Q%!kCfSE&_L-`dDrV zXgMf2G}_>ZZr=xx5)mvd!sn5eL+6RC5tikbBv%eU&Tm#`2Av|{(Xq0LA{GroOl~Z1 zjVurSDdzmM5D38z_8|e9G#Cwfk(gXTzmi`jB7f5VL}ltjBa+p^>4A>-dZ=Jlqz=Tgt5J%u zcq5^kxJX$H+#w6$sGyuxUd4uHf(ym8Vh1DrnwQq7Sw<_`9OwmzA4_+)F2)Vi4(SeD zs3jfXg2CmB)Jl#nr!88B(VGe!#k!p@)POe)N)>Hm9g>Zv!Haq%A=sdxmUfJLahKpL zE;Jh$R;$(g?Wo3#X=gZ=Wf=(AcSY@btyn)!&~4BOZve`Qp07QMU9x~?Xc{KgX*9YG zc7LZvqhF`iZ{ANc=t2Nlo=@xJ^bl%~)?DQ5a7(_7%z~YNI7JKdhmjB*cLp5Un6c#0 zL#W9+b%Ln9U@@-g;;(=9%weP=tWavTDz>bza!x;}Cdp#2f*%OFyU~lhUb+FFc^GxE zU7~i6PWa2QKkrZ!sCKCVRI-J>-YIVjx;9x-RPaQWMpt1;4NvU;~*8x z1_;Np0!$zyhlkx6Ezx4d-kIHk?tbf=58elSI+eowOM_B+1>*s z4Y+7D`TjntG9E+PVA*n=aPSG!W72H~LC}D;FDbRVwBp>Ef({*6FKVyA=c3i-Spoqf zM4|@aS*P6IG%-OMS|r=uWRar=BSs_jRV3?ZTn%TsnK{?tOdMSJ5b6{p4-vTJH`rMy^M_!_;fJuUGg;ty+==!xHY&RGTf;2BM z&o;!d`k?Lyr{h|ehz z_>>fs21z>wXtcc;^$gJ~T1?j3s2Fow-Ql1Y??6hByhGLzY0_h8FD)}+)7jGI#zQ*u zUfklarG=-n1_vJd=i!W_lK}vmywW=^aM#t|3E=3oyJw(1Yu(b@1dsf!dwAPX8~>x% z??X$q5e~eD>+^{FI=r}O0jp9O_S@O>z={ia+fEz51YC4JYu|5Bsn~^U@hLZW9!F!w z98iwbX9hEtJ(Nf!Qb?7S-a;E_*YQNcg?ee~h|LE3(XUPg`-!YATb99my;ftBj(~of z{HxLGrTfz-VEwl4G{t;~+A&N`Bsf79Oyr_tc(XU+37Wk|5BiK^ND4BB170HzO0?F* zB4KkhjDDOnT^nLN1UR&&g~J&>l-(vw6kjM_Tca>= zD(#fDZ^qrX%`CZX`epsiuRANcn&#I`S11|+oz-ojYNyy$;A^VsE^p)6Mo)W1W56fS zi6^HN9=^J3&4elobNUn*qE3US!r%}9#hv#6F!VM2YKSjxydZU_ug+JX;h^*|pjnN< z?g@c!++nv>#Q`9_jHU;L&RQJG^CKALoXBAr(r9w_yD?%D5;wEp4VdGjNTO%ffVvu* z8XC-CGhno)1W4&?q!(&rSuKk>QH{Twb7GmF>Dgz7nE+##Y9Om-0bOqO;xiN#mDO{a z;&yNtjonAJQ!`OJgfWGYmq(KfkTH=mYLPsd5N(OYgj~^9fTN@x`7mCJVUfA-#}hS}vX4o9p^|=%qaLIrwy-5hTnY|h=}bKh)@ziQ+)X2VxE02v z>p8tzr!;@_hBP?2>Yr7UrS~R$aQ6pH{~xOij0t!&r<@r;CWB~V`*2;q8xXGe=sai? zlu8=V8~?T-^_fCYLkPFfm#i7e|-~(vx$AJ`>H-&AV-&oty-B~js^@B51`ZIf7&*t$h zA)64?8~lOU7aE{>M#ZWt4_>tG9;Z}(AAr0RSd4?PR3Hf#Wo@;26>(FzT7pGj??M%6t=BAat{Kl?a0qI%-ln&W%a z{k8o1{qigg!K5pH>cO#UKQywMYZJ) z{myNza7}5hYp(aN8$SgWJM85E`0eoW0zZTs;`7`>lfNuj(PR?M#Wf{OPFr9~g@?15 zbQ`EFzk8hIi#gJmh}oAnQZx5k%tXtDRvg?ypoK9>F_h_+(@lcgqmjm3Z{&|Rov9&K z#=!b%(%%_{jur$HQ0m=P-66YZDpd1IrCo4$R`=Tqd;z<6+thh?v>T`Ru821%gLsJ`V zocWO;i2g-b^p|$dh0|tvBb$!>L8oA`5L*w-rVN`68W2f9YZ368P3Y{}Xf5Vm!U-2O zpq9|*xm^S)Gz~=QBK-`B?R?NnfGN#kOvp-Nu#m(g8{{yEhA~|ZZ@L_#40E>>84U(w z(bMhispoqpO#?sf2>RVht{niK$pTt=O{v%2(c$uyYWP!-);J=yMP^gca)mhWtE5k)Pp_(IQ<+Svw(|Wju)iFwr?lry4o9XbT)bC33AoKg)nSL(>V|1KZj| zwdS%?ANcgHk}~s?$|9XbC@s|Y=AakkpAQs9F;&Z z+%}884m4i=4ULz%{;`l+O6{QbQ@2x(5d9k?2BLS(BB7_Y#vjJmw#Kk~jMtKRc@fk* zBIM=yBVN*Bnn8Hfi;ZC>9uL~AAxynI=OSGM!*`=z;UYZ*glTkl3}hS@Gks6)XSnbA z$LOK-i$SZ!Vhw_s=bbmyuv&UyO<31zI~=Z+r@VK-P!s%P(D~tMV7F z>H<#|`p0(!3JU`rR}`@R@XFnVEKh zHPWTkHh**P^WFBk=pRxm$HiifS=zA5H-6rV>HcuoKm9mbL>vw!{fjrokAGuAYTn12 z8hbdind@m>_ZeR2O(q_#GdgL#^beq)bYR77>Dvj9%s^KMdLHS)H<>AEV=aDL7#xsp za6?Nu*dfP8Vt(I$Q6kRV2b`=K$HbaoMiIu=UUSCS0-^x#gmYA1I|84ZO{x?CcWKm0 z>*pnQ`nPIz>I=}LR;etXm)WG_0t5xYe^}@X1!+>qgE<7yE7a>N!7_t+=sb|R)nwFH z!i!z>b(J|j1Uxp0gtrbOj$%6w_6(S5&WfX}Vu0)c7C^S5L4d??>nNwnPIK|of`V7< zcuuKQ7@jE>=@@VPiBps=L~69j^|Zh%l+qBmRq>}`#%CJ5>rrcrzX#HfbULk%o}uxk zf>3gMk>U*A0q{Q!SB=J-p=6wKf)havcUuCVNhbM}`!eR-0J+|b!BL$ORqS!Q4SJIf zQqT$Ydc&%&KM(EvbJuEvP7l-D^zQWb!bwIDHwi)@l?Vt56^I{BuDQ3Zdzqr3K(Va5 z?cO!RHz^s1ic7Kwh~E>lEf=Ftn=u1(kdGjJ9{rD*l^Uc>e^8LdRP+ZX6aSwub@?We~t7f!u{@F(+3JMGn@22^Ly#9 z(rZ8`eJTAz`Z*|~cS=8(z69e49zDhGB=L0mY-zkWBA1N-BX4#GFL1k*Dc_R5SeqICYa3TuKiN{T?Q@sn(hBSTHr`xA20gsiWWoxNf_&9=2b4^QHT4 z0k?pKsSYnH&tU2>Ts6P#a2t5zsY6eJ&!r=~K|gpo_0$|V@uO6i9X^xiV=<>O;wUtd z;Gk7Z7mmgsZ(1&(vXWyiJyVYPi;a|~X6`d3-r4=U^r7imubrtZ@Ja8VNbEXsVpjsZ zUQ+aMQ3?5Zc+-qi2WD*AG=sTh#-@wmRjr*n-`WoJ$<E!4^`mQNHl>%(kp}T@zm4-P(4-- zZx4Gp`$HtB;|#4h_`zR1> z1xSo=0#4)zHh~}QX7CZr3la0NI97tLQf!U{iwXn2?$}!0ua>k0Rm5@=#oGE{Zk1|4wUU(OiXITj87g>hmi?T{GjR0v9Lz1;z%=oZ*Ch4qH*~9+GbR z=8)d3WqGLdn(a!u$W!NY?l=jyfzsQX3;^ESI>lw2InyX;8jY(rR1{u1eqlnPI07$o zc$JE(YF_2B7kZU^QK3TN9TMypc66J@RnbO;$rJJRJ!eqfbQ9;Pqo2M{vN>xDjXML5 zb(*45N3F8vg>4T_v{yQvdUZ(f&kId4wGjSK`CTcFgqI zA1u{kp&m)PVr?`KL<5x`5Dr7!uu;qzz;e9Y)=nDjXRr<+j1stdX8OuOd2se5#r(ai zXc()UaQ%~}j$p;@4^#v?%-WF0`KveFzM48UtG`R?zgxrF^;LI%`?$xc-={Q|ulv39 zkG;Kt@-U;Y_&A{81ntVl0e!+&T+ECECBwX5x0Q!1rj>#<+T4DzW>H7=d{gmE&|tQ6 ztjWaj1t!tPBY~ae3sN*6EMQix;xxC_&2WU4ifyaluOpV2yVarb=uP9Co!9)<$JUxW z>K;?!Laixa25L|nj^7FsDlJo*;?X>ewb2_PoMYh1KcVUTCY?4|)3JHu z@+njMR?e8#)L^zexG)|M2HAwP{U6dLSNZ(b;wfK_Gm4Ians79_8an>qjK-!;8w114 zA4xwYLRhN2GGC-QY&7MlHAndpm(HIX_7|ztK#)GWM_p7@J+5uP-aH{!m&ot-Q?VH<@%=h8@)=^yxTEp{|AzZY*P~(C{mR zR=QiI)v2UAwF;#vjje~2B!iStsX)RYiVU&+pUT8$P%yMo-yJN~GNO2j1VS@|0RuocmlB3FuM?noicXPxW)R>r`0rL3c!H;J2}TqO4i10D z5*?{QnrDjUlIeTO{@vlo@t9F2iHk6zRB#V!iXZ3{`Bgv-l#Od&kJ>XpG6vJ#3Jb?x z4-F$}=@!3dqG8G0p&-M#Dih#YO%`^2aQ5Yi>VE5;j(tAbD)@anKF>GXKoeDRKO@A~b( zVlHc*Jh?S0sJWZhtS+SuG^5GqW24cWu9n%7{YJuMlwQIIQ*-ejml)cNL!_XP+T05( z;r~iq1S6>}L!a${H`5mneE{zyypjZ?mEB2V77LN&Hx=m|6jc)?^A?j{vhwUEcXAo_ zkt8EFWA&0K^FiWk!%2!bN*zap7UOULoMg?DFC_he)L6i~F00jL0ViD+i_1E6s;sGT zZc`I8JzhDvX>QYjrt-2TFewy=53f!PElsTH;x$@+;^H?KPvo^49vsHUo65?Ym?A5_ zkNp4DrZQ<}c~et4c(|-dOf3(^|BAQ%D*whq@HTLB?D@@`pO5X)@|`8nwl@gl|Gmc>oVgzz3>97x5A!kUEZbb5@f#gt{>%tmiQQ4<5yMl1OB& zv2Y~ulT5udo)c(1RREda1I-=*d8Re zka~h1X~8$Bi2^6Yg#iTAgeI^*yp9ga4T0~En}7)75mG>OHz&=T@I7$>v6YM1z5@6l zv3j9e$K+WvOkiO6^tl%N5SrW;wGeL9^o`T)>}26BY9+&p>>@_5vMFfkc7|bTn&&yj z$N&fdr02vKB;F!1R|!;;yf*hdw>ns?2Wq8R&}xCsQ($2jlRBtx)8$^!yC(Q&3Bg-mO5ExXn0>5r3 z-6q)d1r9@z%EOnl<1RLtTJPRe0-4IoLcykDK?7Q5I(-&%n@2%A0jQ}3bbEoQ=b1R` zEHNu-#ZJAFX88Jc0P2hN6~&NND?yQHae^`*qt|JyKxbzaR=pZPBhV;~N*#wvLUYB8 z$RMedVf0o2GzL+xWR#F)8IIP{i^XWt3XC|(Vc-R2 zkp*>Q^pXl)1pqW@QMc9@)z*1x!#KZBsbN%t$J6aLv9wlS#@RF$wZ2nlRB{Ch&ZVQd zirTiI@u#(uJW89vQiK`4mq$BI*VnH5)p^^>&7jCpcC>Txmh~$eUz=CmRRW>Mj~ZPe zYKmCDZgyo@bFO<&+TY~5d%Sd6&XufK#h~JMu$b=mo0(N z5WQ*VRbKtmAMb58yQJSphr#@wni~&n3-}pf#n$Zyk}eRU-+ANL^Ges=H1rQNp~LCV zd^2VGo{i%#>uS=!PagtGQ^({T;|oNnqcq-nzH#%UeEgD*pU~$$z6S0^o*w#0THBkB>H)CC`VC0Zl=? zzPm6|##vGKqLIeH!WYKEEljsx3)PEtk`P@5Fmr9VhLE}DJ=$sZ=R6dW_%Vc zP$ry0e?Cmm7L(2Q7`2VD2pF@CxjEP{e`eoHg*O^$`5tuZ$ z>Ckx=S5I4bMs-7}h=u*z3Ee z_V1QAq*Hh!+Xf7g?VDtblng?NRf(sv477ly7=%e6tO?D##7$L=m4GxxNije_?2D-r zwYNl4Cn6CzIdV7xl+uQiW%Z4vTg%G8VW*!fYzo5FFtU5APL~Q8O$-z?(n_7~Qf-B9 z2)5|UAeFrq{Y0d%rS&JvN-r&GY$(HwhfFD4O-ByH=B@fNeJY>_Py>$W%XC}y`XSh= zA7+0b@y7m95sv4;|HOV@A|r#rv_~|%H4w0WM_e8(`b{##pE^Vlf^tYarNm!K>vAUr zvb=vR#SRjLM%l{~q`hX*LgIghk&@KL#E6$pGn0{=Y1HhQTp1kv5ia^`<=4u9J=q=_ z2(>5e0p-_~e=Q1^)ENNPy#gdwbOXvD_3inOJ$wEG43^ZDgE@Pp3-y9MAbo+Ufq@}l z7xduvz0$Grx{@LrNUUBhC2VvbzF?1BRtA^VPa;^;!malVOS#RmSY}jRPhGryQ9JoV z>+5=8qGz2nNJ>M;C7BbhZ)hDU$!pR$yrd6G1P>1k^sHM4Ue1*xWB+pFxb+rnBFHef zK_o_5tiF6h4-0w?#-gf{xy?3TQ=`w;JhwDdWHd1IM+_<-gFjd%^%dKZgi=yc=mGZP zzDbtr#uyhWkUsGydm8nlZfrv(;077MG2^fQhq#^;h~I!GLf~ScJP>ZJFbeLu3lDvF()I- zf_LFMJ;3#`NvfTiNHW;Uk;02dLfj2>40cI+La-`BGuR5!gb0nm7{uR4F+tNwgXsV_ zPQd5-0`|d<*F;f>3cq4a@%AO-65$KG8+H1pOocX4q>aCAkYO>7i-B74I6dXKSQ`+J z589;(sl-o!>L>8L+Q6|buZy*!C_c{`N?mpgq~-_)wYpc$1|eel>xKbbv4DJ`d>iSH zkhC+V8cQ9Sll_b`VlXW+1xELY{03zj%)TuH4%acFNf!fR9Eet_jASxE_D@czq5#$tXtpnJuhjbAngFvev=`H*Y>v3D@G>x&? z7{_wLwKYf)QIrKvQ?|Its0Td52;Pldhu5EPD^PjY^k3V=(Tu(f2pS8^ z8Wg5ly`d;tUQ(!qoS;;(P{(rxOAnO4~YYHdV=W z1Ax2MU|~5C$(RhSHrK2!ENYrxUC083uc5!Yq+P4=D4|7E+ab`f#$tCv?Sg>1#Zy(R zgp9p>VN3s|Dm_gD^dGW%rOb`{Aon#pnNpEauZo&Ot)zCLFEXnKV;)?xij+=k1|JhO zt3L#MNPoj0V=U_PBV8Abj5seS3<6Qlt)qe!Qe6-htYM|K6V zLMyA~@Q2vFI?ZemI%jNBD7CsG-ssdhPgMTb+SN0vs$O5Ub}`Zn2c*-7{v!QJryKy_ z&|iQb1STE)xs;MVkpBCv-B%|b01GCyRWh7T&v94(E>u|wS)EE#zo>K5>;h3yZbbz% z&2P1pF|6Iz1m?^O2bDEZyQ0w7((=%}!f~47!fjs;c_!#}cDHA|%W=Eb!Ln*?v5r;u zF7NYso>_eUB1h4QroNjd=&YX}k{8!?UcaZmrDMxeYc>KV@xYan;y36ts2jk>=GKi` zof`G1hLvz}@3uPhbX11cJ}r8>t(4VH?@MiT*o7L$%qKd>M+C08u8Oly&i4mypp=w| z`OyiVE7GqqYrP5bn1t8|3_KbvjTS~=E;{!7bH@(+(&PQ5bbIQh6ZZih6FKox>T%$^ z&(qsG@0)`MzhRpt$B=Zv(zk)_Ct&>VQf1PIZ!ZN$hrr*QzmtBF#zv;t%Q%W!jqNQo z7Ew8hCkPp6Jk~+%N&x8disE$^ud~G<8VRvT+h=r0wLwD^wuk8Or_AA1_A=M}-u|V% z)0+&&_0rMTM7v!)4$7DNCic!>GIy4H!wdU1v=&6{yrrvi@yxmLN^ZigC3Bm@ZVSt3 z6ppUCT3sOAeNmH-wT81z?%A^GI`HG3P0cP^ z=PXdE-j}`w_CNu6>!eOlXe%b|oKk&{Z=6vt4W&Mxv61=Rsj|%9#u@aq85@D4ea;r? zpFq21PCJ-znmP?8qMvIzI%aR#k|%2xAZe*Oom(>|ZKvf7iBU`{?21(OO_hu$4-}ZIQwWm`KWNlvSN--T)-UlC}!>)IBQ`C(?tZWmW%rI&hs8UO&zEcs`QL%~TX;Q4*01OJp%Co?WRh7EG;VG@@nDtr#KG z#NGwbZFb{KDUm+Cyg_>HCwE9+-~Rf8#>)-?{+XR`ZHA79)0EawV*FexvH9sfsL;)g zw)ggT`oVqDN(1;j z+C$-`c8%FQb>M0c27zH7D3Ilw=)@WxWMq{t8w}J6BKhl?R460@6(JdtHD^|gQ7V0q zNjxi^{Mmp`c$?-_O0D&y%u>*yonVXJZk4vA7bgKj_QK@Pq?6AII=HkQa4JK>s^~gD zyY?N{P)}@PO?d0l^D`?_ffks4ilcIK`Pbew>a#hW>LXVsJE&znYTq*_8;=@sOq@#; z={`9Rr0<*=+M~`VcRE|fHue7jDoYD$004N}V_;-pU|?ZjXo@RJkLS1f%D~Oe00QUc zW`)D(|Ns9pus5)QxEu^jAPN9Cg$rB&004N}V_;-pU}N}qmw|!3;Xe?tH!uK2kO5;K z0I6LEeE@jcg;cRl12GKsT`m_1IMIcLE)`;6XcwS}@qPfdj!1|PKuCyzP7zn5ugFYzITwTLGqsUul~03g?(GI z$Nvn^x|r_)-_XCSO{+dM*h6>eWewk3wb=*uYlgFXwsW!`?@s5i?!;@H#-=g%hhvaf z8cNdU8*<&++t|&1TT_KNm%!Jd-1eZCbC!&d^qr3*cWcXy&v~Etq88bC(d033+1s4k zf(LUyxoCJuH5v1^Qe*XLf9@+Jl5a~kl_C@U{B0r(8#HJ~G2{_N;1iZoDGhkn}5)14*olpEb$m@Oe z7GBPD_ElHqefpq!-0K*}=F8OX-u*y2YP`-7(W58n*+^Fm=(lJU<~;+Z+=HgCdLMW5 zkb9ry4R#FSQ|DRjPTOLhym^OUKNrb$n1#66*f$ln7kg%9oK@|$^7{vZ16004N} zV_;wqBLm7Y1TaiuxWeefSircBiGj(6S%tZY#e?M>%P&?N)@7`J*h1Kju&1&A;RxZF z#PNXBgL4JvKdvCI30$|hb+~8oxbRf)oZ>a(jp1Fw=fbywUyR>}f0;mpK$pNHK`p^m zLM}qvgeycWM5c&*5cLvWBIYM{K-@??O?;F1HwhJq0Eror0+M}_Kco_*CP-bAW|LNu z4wEjCULyTUMoPv@_Xd}DVQnbDXdUeY%)rH9jbWYPBcmLn2gX9iLB?lHq)hBg_LzJ# zwJ@Dy#$Xm^w#Hn^e3M0h#RJP4%TrcjR!LSHZ1>sm+2z6FPkDM8tU7XjsM7g|ko#s~LcE#PreUpcr$2w0p&qbaGJnwn_@sjfL@oMmz=e5UM z#5=}&osXB#312PWeZD{ZGW_27yZN68kO;^M*ca#$xGC^mkWo-p(1~E9kTYQ%VUxms zh5Lk8gdd3zh=_?;5%DF`Au=m+O60!C7f}XLby0hwS)$FNCq=)D35zL-*%50NTM_#R z1mgnY_QlJ@*Ciw*+)HdqJd~uB)RS~8nI$tRB z7FGSJ_Nks!eXqum8x&?Ko>b}&=)tA-JYfx$W)I6z0q@}9mNUKz9 zTshx$_qHC1o+?ZT0KC^I-vD^pV_;-p zV4TJz$soc20!%>62!sp_4q!e502Y`53;=lAb&$_a!axwlzZLvLjGhef*cju%1Gd!@ zH$+hr1cC&;7NpWBf6`VIAHxUm;K2v+q&JT~fzRRB=~lpKHoNnincZ(@2fzxRk%CHR z0NC6yD`e@#Jcm^rYffPUP0eX+;a>ARHu0o+fp1?mFH-$e^Agt8gXRp@)T8EQY^xW| zZ^)_-&F?VP7tU~kG7MBPL57)Yn*%w!k}1*~V$6)kx?TBq^rlTps=BoP)EoC_LLuW0E*b4fzt@a8jE17u;y)%T zecDh@G~gdfq8h2pc78yGk<>XN^{GCVzC!ky#|~Fg-MaGnVFenLC;7x zl3FKNGE=}D$8ngMnVFd!W@d1h6Q{bRS$N65-R`PVLv{79U%e$N>7U1!OIMZt&kr6^ zO^HfnQ0e~CJ*B%#_mv(*85LAfLmdq?(Lx&?bTNX_(!HgJN)KQRa)K7RTXuoPZOt1t;NToPtwv8cxRDFxN~h83bOxPCXVKYo4xLNq(fM=%T}T(v z#dHZ>N|({)bOl{WSJBmU4P8sukwMp!Nml7mvdJMqJ?fK79&M!o`4mt{k|NqhF(s5z zM)R~li?l?`bOYT;H_^>>3*Ab$(d~2x-AQ+q9pDX&!MZYEQCr``!Y2Ba7`&9eBnIzR9OFX-l2s5_bh6v|{FC$TPSx+lT zYQ`IwO9mlUeuSR3=A)9=w4=NS@wFh z#OsHqU$$kxn#N}0R$Li~2CpUz(@!g@7l=wMO{e3?h0td~nHxi;mPM+odZ8s3+mUZB z8MYVOzTiD0VW#z1^kR{?4dsen(3ke0((}!Jix1;Ot_(%enwNeS2!s7;7oysrS;$#b z+ZNl>5p~PdeK|Gz75+;qmXw2rY63GJRHN7n)0%AtA~q{M8K(T*cWPd0`kviR#bRo> z!t1+fOUnzMle#Vb)(;I|^wLf)+9FIv+|HF)4e#di)+|ZA-cm)KrR{|dkIUy3vK~9q zGi{-wX3TqzkoCy3(<~OXNQAcMw*oUVl&>PLnT}eJBg}pZ$4je;YsR8#yMiO6F07lR zA~Gz~9xRx#)9slY!lBj}3KbRfYGg797#K3D_hhW>9X))g=#>hkDz*wc?eISHvCL22 z9V+?=&B)IZLjj`|cwr&7a}a5{E(f~rZp#FRgy$)(>4iO+PfP4rh%j+w+AXH#sA%%U zTxwZnI26q|mJ8aCb}ni!8o8WB#dnPe9U_Gzb|>+ch0)7=zf;IbVEX=;ShRgJFjw5F z^t~R#PMAH;kytdu5(ABIqp1Yjmx<_bR6;N8>)}<7XDAxB>5I@Y<63NnjtuIy34FexmyaGrYDt?Dw$o!2ia6h_T`0yuq8tvOEw=70%|QQMjCRQ#T8&gnd8A`jYfvao2xB7Am6MwaASDZTE22E3l)d78Dg9? zD!@)TPLi_ga8fWDICx>j629NIRako**i^J!zQzLGT2yGOYblFziwekij!0t_ksH=o z^a7*nOj)#kl3Ip2Tw0>G5OdDE)znM|NsSqm57V?_PxNdv5iNz>JWs0qSY}a0#j?s6 z$())cOlF9(ouz!05l6+0G=99Ol9=_`BR2jUU%`~6cgC<`i`@`uwvLflQkM*VO^J!K%puNUW?E=nf zWM>F%T~V0hQ^sp5m|Gi+?U?W0WJYApYx&9vgJEGcm>2k-`(i|g*ceu@POj!it*cUM z1Wudhrmjpl_@a?yUaD@ap+Kc}tl3rWx?= zW@w9AAe@1hwtLDY-es#`*9F%BH>auIL{E%6GP4wvLKSh1zjc-zf9p()zjeAgS8H{C zd(Fhga7Jr&Xx$OXfXhbBHzU<)proBZTIyUn8#@KQHQrj=GMN@j=VE@(eA+PN!{lSD zT>br}RzU?En6b4KsA*^o4Jy4Q79*8~`R(!rM)|mE60jrH9;a4V4uo6pGuK6?(_os@ zxM--igc>=b1x+oCW~ae1=IUko74>3hYKM53Kf1zq1pzUchg>qS_?GN6UtFmV%(xniN5;)ipu6Y2Z&+ z>?E10F*cbpTRE#1AZBLb>bM=_-HQ@0SyPb4S8T(gRWYU}rkeWcr`E5rk^LQ6eL3iI zom0LxHhjTJuV9!98nO9z{fyAGu2aI8+Bn(DOTMlMoc5g7s ul li.current').removeClass('current'); + parent_li.toggleClass('current'); +} + +$(document).ready(function() { + // Shift nav in mobile when clicking the menu. + $(document).on('click', "[data-toggle='wy-nav-top']", function() { + $("[data-toggle='wy-nav-shift']").toggleClass("shift"); + $("[data-toggle='rst-versions']").toggleClass("shift"); + }); + // Nav menu link click operations + $(document).on('click', ".wy-menu-vertical .current ul li a", function() { + var target = $(this); + // Close menu when you click a link. + $("[data-toggle='wy-nav-shift']").removeClass("shift"); + $("[data-toggle='rst-versions']").toggleClass("shift"); + // Handle dynamic display of l3 and l4 nav lists + toggleCurrent(target); + if (typeof(window.SphinxRtdTheme) != 'undefined') { + window.SphinxRtdTheme.StickyNav.hashChange(); + } + }); + $(document).on('click', "[data-toggle='rst-current-version']", function() { + $("[data-toggle='rst-versions']").toggleClass("shift-up"); + }); + // Make tables responsive + $("table.docutils:not(.field-list)").wrap("
    "); + + // Add expand links to all parents of nested ul + $('.wy-menu-vertical ul').siblings('a').each(function () { + var link = $(this); + expand = $(''); + expand.on('click', function (ev) { + toggleCurrent(link); + ev.stopPropagation(); + return false; + }); + link.prepend(expand); + }); +}); + +// Sphinx theme state +window.SphinxRtdTheme = (function (jquery) { + var stickyNav = (function () { + var navBar, + win, + winScroll = false, + linkScroll = false, + winPosition = 0, + enable = function () { + init(); + reset(); + win.on('hashchange', reset); + + // Set scrolling + win.on('scroll', function () { + if (!linkScroll) { + winScroll = true; + } + }); + setInterval(function () { + if (winScroll) { + winScroll = false; + var newWinPosition = win.scrollTop(), + navPosition = navBar.scrollTop(), + newNavPosition = navPosition + (newWinPosition - winPosition); + navBar.scrollTop(newNavPosition); + winPosition = newWinPosition; + } + }, 25); + }, + init = function () { + navBar = jquery('nav.wy-nav-side:first'); + win = jquery(window); + }, + reset = function () { + // Get anchor from URL and open up nested nav + var anchor = encodeURI(window.location.hash); + if (anchor) { + try { + var link = $('.wy-menu-vertical') + .find('[href="' + anchor + '"]'); + $('.wy-menu-vertical li.toctree-l1 li.current') + .removeClass('current'); + link.closest('li.toctree-l2').addClass('current'); + link.closest('li.toctree-l3').addClass('current'); + link.closest('li.toctree-l4').addClass('current'); + } + catch (err) { + console.log("Error expanding nav for anchor", err); + } + } + }, + hashChange = function () { + linkScroll = true; + win.one('hashchange', function () { + linkScroll = false; + }); + }; + jquery(init); + return { + enable: enable, + hashChange: hashChange + }; + }()); + return { + StickyNav: stickyNav + }; +}($)); diff --git a/doc/_themes/sphinx_rtd_theme/theme.conf b/doc/_themes/sphinx_rtd_theme/theme.conf new file mode 100644 index 0000000..b71548b --- /dev/null +++ b/doc/_themes/sphinx_rtd_theme/theme.conf @@ -0,0 +1,9 @@ +[theme] +inherit = basic +stylesheet = css/theme.css + +[options] +typekit_id = hiw1hhg +analytics_id = +sticky_navigation = False +logo_only = diff --git a/doc/_themes/sphinx_rtd_theme/versions.html b/doc/_themes/sphinx_rtd_theme/versions.html new file mode 100644 index 0000000..8b3eb79 --- /dev/null +++ b/doc/_themes/sphinx_rtd_theme/versions.html @@ -0,0 +1,37 @@ +{% if READTHEDOCS %} +{# Add rst-badge after rst-versions for small badge style. #} +
    + + Read the Docs + v: {{ current_version }} + + +
    +
    +
    Versions
    + {% for slug, url in versions %} +
    {{ slug }}
    + {% endfor %} +
    +
    +
    Downloads
    + {% for type, url in downloads %} +
    {{ type }}
    + {% endfor %} +
    +
    +
    On Read the Docs
    +
    + Project Home +
    +
    + Builds +
    +
    +
    + Free document hosting provided by Read the Docs. + +
    +
    +{% endif %} + diff --git a/doc/apiref.rst b/doc/apiref.rst new file mode 100644 index 0000000..ab66564 --- /dev/null +++ b/doc/apiref.rst @@ -0,0 +1,104 @@ + +API Reference +============= + +.. toctree:: + :maxdepth: 1 + + macros + enums + types + nghttp2_check_header_name + nghttp2_check_header_value + nghttp2_hd_deflate_bound + nghttp2_hd_deflate_change_table_size + nghttp2_hd_deflate_del + nghttp2_hd_deflate_hd + nghttp2_hd_deflate_new + nghttp2_hd_deflate_new2 + nghttp2_hd_inflate_change_table_size + nghttp2_hd_inflate_del + nghttp2_hd_inflate_end_headers + nghttp2_hd_inflate_hd + nghttp2_hd_inflate_new + nghttp2_hd_inflate_new2 + nghttp2_is_fatal + nghttp2_nv_compare_name + nghttp2_option_del + nghttp2_option_new + nghttp2_option_set_no_auto_window_update + nghttp2_option_set_no_http_messaging + nghttp2_option_set_no_recv_client_magic + nghttp2_option_set_peer_max_concurrent_streams + nghttp2_pack_settings_payload + nghttp2_priority_spec_check_default + nghttp2_priority_spec_default_init + nghttp2_priority_spec_init + nghttp2_select_next_protocol + nghttp2_session_callbacks_del + nghttp2_session_callbacks_new + nghttp2_session_callbacks_set_before_frame_send_callback + nghttp2_session_callbacks_set_data_source_read_length_callback + nghttp2_session_callbacks_set_on_begin_frame_callback + nghttp2_session_callbacks_set_on_begin_headers_callback + nghttp2_session_callbacks_set_on_data_chunk_recv_callback + nghttp2_session_callbacks_set_on_frame_not_send_callback + nghttp2_session_callbacks_set_on_frame_recv_callback + nghttp2_session_callbacks_set_on_frame_send_callback + nghttp2_session_callbacks_set_on_header_callback + nghttp2_session_callbacks_set_on_invalid_frame_recv_callback + nghttp2_session_callbacks_set_on_stream_close_callback + nghttp2_session_callbacks_set_recv_callback + nghttp2_session_callbacks_set_select_padding_callback + nghttp2_session_callbacks_set_send_callback + nghttp2_session_callbacks_set_send_data_callback + nghttp2_session_client_new + nghttp2_session_client_new2 + nghttp2_session_client_new3 + nghttp2_session_consume + nghttp2_session_consume_connection + nghttp2_session_consume_stream + nghttp2_session_del + nghttp2_session_get_effective_local_window_size + nghttp2_session_get_effective_recv_data_length + nghttp2_session_get_last_proc_stream_id + nghttp2_session_get_next_stream_id + nghttp2_session_get_outbound_queue_size + nghttp2_session_get_remote_settings + nghttp2_session_get_remote_window_size + nghttp2_session_get_stream_effective_local_window_size + nghttp2_session_get_stream_effective_recv_data_length + nghttp2_session_get_stream_local_close + nghttp2_session_get_stream_remote_close + nghttp2_session_get_stream_remote_window_size + nghttp2_session_get_stream_user_data + nghttp2_session_mem_recv + nghttp2_session_mem_send + nghttp2_session_recv + nghttp2_session_resume_data + nghttp2_session_send + nghttp2_session_server_new + nghttp2_session_server_new2 + nghttp2_session_server_new3 + nghttp2_session_set_next_stream_id + nghttp2_session_set_stream_user_data + nghttp2_session_terminate_session + nghttp2_session_terminate_session2 + nghttp2_session_upgrade + nghttp2_session_want_read + nghttp2_session_want_write + nghttp2_strerror + nghttp2_submit_data + nghttp2_submit_goaway + nghttp2_submit_headers + nghttp2_submit_ping + nghttp2_submit_priority + nghttp2_submit_push_promise + nghttp2_submit_request + nghttp2_submit_response + nghttp2_submit_rst_stream + nghttp2_submit_settings + nghttp2_submit_shutdown_notice + nghttp2_submit_trailer + nghttp2_submit_window_update + nghttp2_version diff --git a/doc/asio_http2.h.rst.in b/doc/asio_http2.h.rst.in new file mode 100644 index 0000000..645ed48 --- /dev/null +++ b/doc/asio_http2.h.rst.in @@ -0,0 +1,5 @@ +asio_http2.h +============ + +.. literalinclude:: @top_srcdir@/src/includes/nghttp2/asio_http2.h + :language: cpp diff --git a/doc/asio_http2_client.h.rst.in b/doc/asio_http2_client.h.rst.in new file mode 100644 index 0000000..756f00f --- /dev/null +++ b/doc/asio_http2_client.h.rst.in @@ -0,0 +1,5 @@ +asio_http2_client.h +=================== + +.. literalinclude:: @top_srcdir@/src/includes/nghttp2/asio_http2_client.h + :language: cpp diff --git a/doc/asio_http2_server.h.rst.in b/doc/asio_http2_server.h.rst.in new file mode 100644 index 0000000..e7c1434 --- /dev/null +++ b/doc/asio_http2_server.h.rst.in @@ -0,0 +1,5 @@ +asio_http2_server.h +=================== + +.. literalinclude:: @top_srcdir@/src/includes/nghttp2/asio_http2_server.h + :language: cpp diff --git a/doc/bash_completion/h2load b/doc/bash_completion/h2load new file mode 100644 index 0000000..2a4bf98 --- /dev/null +++ b/doc/bash_completion/h2load @@ -0,0 +1,19 @@ +_h2load() +{ + local cur prev split=false + COMPREPLY=() + COMP_WORDBREAKS=${COMP_WORDBREAKS//=} + + cmd=${COMP_WORDS[0]} + _get_comp_words_by_ref cur prev + case $cur in + -*) + COMPREPLY=( $( compgen -W '--threads --connection-window-bits --input-file --help --requests --data --verbose --version --window-bits --clients --no-tls-proto --header --max-concurrent-streams ' -- "$cur" ) ) + ;; + *) + _filedir + return 0 + esac + return 0 +} +complete -F _h2load h2load diff --git a/doc/bash_completion/nghttp b/doc/bash_completion/nghttp new file mode 100644 index 0000000..6bb7a3e --- /dev/null +++ b/doc/bash_completion/nghttp @@ -0,0 +1,19 @@ +_nghttp() +{ + local cur prev split=false + COMPREPLY=() + COMP_WORDBREAKS=${COMP_WORDBREAKS//=} + + cmd=${COMP_WORDS[0]} + _get_comp_words_by_ref cur prev + case $cur in + -*) + COMPREPLY=( $( compgen -W '--no-push --verbose --no-dep --get-assets --har --header-table-size --multiply --padding --hexdump --continuation --connection-window-bits --peer-max-concurrent-streams --timeout --data --no-content-length --version --color --cert --upgrade --remote-name --trailer --weight --help --key --null-out --window-bits --stat --header ' -- "$cur" ) ) + ;; + *) + _filedir + return 0 + esac + return 0 +} +complete -F _nghttp nghttp diff --git a/doc/bash_completion/nghttpd b/doc/bash_completion/nghttpd new file mode 100644 index 0000000..6de3d26 --- /dev/null +++ b/doc/bash_completion/nghttpd @@ -0,0 +1,19 @@ +_nghttpd() +{ + local cur prev split=false + COMPREPLY=() + COMP_WORDBREAKS=${COMP_WORDBREAKS//=} + + cmd=${COMP_WORDS[0]} + _get_comp_words_by_ref cur prev + case $cur in + -*) + COMPREPLY=( $( compgen -W '--error-gzip --push --header-table-size --trailer --htdocs --address --padding --verbose --version --help --hexdump --dh-param-file --daemon --verify-client --echo-upload --workers --no-tls --color --early-response --max-concurrent-streams ' -- "$cur" ) ) + ;; + *) + _filedir + return 0 + esac + return 0 +} +complete -F _nghttpd nghttpd diff --git a/doc/bash_completion/nghttpx b/doc/bash_completion/nghttpx new file mode 100644 index 0000000..7bbb77c --- /dev/null +++ b/doc/bash_completion/nghttpx @@ -0,0 +1,19 @@ +_nghttpx() +{ + local cur prev split=false + COMPREPLY=() + COMP_WORDBREAKS=${COMP_WORDBREAKS//=} + + cmd=${COMP_WORDS[0]} + _get_comp_words_by_ref cur prev + case $cur in + -*) + COMPREPLY=( $( compgen -W '--worker-read-rate --frontend-no-tls --frontend-http2-dump-response-header --backend-http1-connections-per-frontend --tls-ticket-key-file --verify-client-cacert --backend-request-buffer --backend-http2-connection-window-bits --conf --worker-write-burst --npn-list --fetch-ocsp-response-file --stream-read-timeout --accesslog-syslog --frontend-http2-read-timeout --listener-disable-timeout --frontend-http2-connection-window-bits --ciphers --strip-incoming-x-forwarded-for --daemon --backend-keep-alive-timeout --backend-http-proxy-uri --backend-http1-connections-per-host --rlimit-nofile --no-via --ocsp-update-interval --backend-write-timeout --client --http2-no-cookie-crumbling --worker-read-burst --client-proxy --http2-bridge --accesslog-format --errorlog-syslog --errorlog-file --http2-max-concurrent-streams --frontend-write-timeout --read-burst --backend-ipv4 --backend-ipv6 --backend --insecure --log-level --tls-proto-list --backend-http2-connections-per-worker --dh-param-file --worker-frontend-connections --header-field-buffer --no-server-push --no-location-rewrite --no-ocsp --backend-response-buffer --workers --frontend-http2-window-bits --no-host-rewrite --worker-write-rate --backend-tls-sni-field --subcert --help --frontend-frame-debug --pid-file --frontend-http2-dump-request-header --private-key-passwd-file --write-rate --altsvc --user --add-x-forwarded-for --syslog-facility --frontend-read-timeout --backlog --write-burst --backend-http2-window-bits --padding --stream-write-timeout --cacert --version --verify-client --backend-read-timeout --frontend --accesslog-file --http2-proxy --max-header-fields --backend-no-tls --client-private-key-file --client-cert-file --add-response-header --read-rate ' -- "$cur" ) ) + ;; + *) + _filedir + return 0 + esac + return 0 +} +complete -F _nghttpx nghttpx diff --git a/doc/building-android-binary.rst.in b/doc/building-android-binary.rst.in new file mode 100644 index 0000000..2c77c98 --- /dev/null +++ b/doc/building-android-binary.rst.in @@ -0,0 +1 @@ +.. include:: @top_srcdir@/doc/sources/building-android-binary.rst diff --git a/doc/conf.py.in b/doc/conf.py.in new file mode 100644 index 0000000..0e572cf --- /dev/null +++ b/doc/conf.py.in @@ -0,0 +1,253 @@ +# -*- coding: utf-8 -*- +# nghttp2 - HTTP/2 C Library + +# Copyright (c) 2012 Tatsuhiro Tsujikawa + +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: + +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. + +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +# +# nghttp2 documentation build configuration file, created by +# sphinx-quickstart on Sun Mar 11 22:57:49 2012. +# +# This file is execfile()d with the current directory set to its containing dir. +# +# Note that not all possible configuration values are present in this +# autogenerated file. +# +# All configuration values have a default; values that are commented out +# serve to show the default. + +import sys, os + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +#sys.path.insert(0, os.path.abspath('.')) + +# -- General configuration ----------------------------------------------------- + +# If your documentation needs a minimal Sphinx version, state it here. +#needs_sphinx = '1.0' + +# Add any Sphinx extension module names here, as strings. They can be extensions +# coming with Sphinx (named 'sphinx.ext.*') or your custom ones. +extensions = [] + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['@top_srcdir@/_templates'] + +# The suffix of source filenames. +source_suffix = '.rst' + +# The encoding of source files. +#source_encoding = 'utf-8-sig' + +# The master toctree document. +master_doc = 'index' + +# General information about the project. +project = u'nghttp2' +copyright = u'2012, 2015, Tatsuhiro Tsujikawa' + +# The version info for the project you're documenting, acts as replacement for +# |version| and |release|, also used in various other places throughout the +# built documents. +# +# The short X.Y version. +version = '@PACKAGE_VERSION@' +# The full version, including alpha/beta/rc tags. +release = '@PACKAGE_VERSION@' + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +#language = None + +# There are two options for replacing |today|: either, you set today to some +# non-false value, then it is used: +#today = '' +# Else, today_fmt is used as the format for a strftime call. +#today_fmt = '%B %d, %Y' + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +exclude_patterns = ['manual', 'README.rst', '*-header.rst', 'sources'] + +# The reST default role (used for this markup: `text`) to use for all documents. +default_role = 'c:func' +primary_domain = 'c' + +# If true, '()' will be appended to :func: etc. cross-reference text. +#add_function_parentheses = True + +# If true, the current module name will be prepended to all description +# unit titles (such as .. function::). +#add_module_names = True + +# If true, sectionauthor and moduleauthor directives will be shown in the +# output. They are ignored by default. +#show_authors = False + +# The default language to highlight source code in. The default is 'python'. +highlight_language = 'c' + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = 'sphinx' + +# A list of ignored prefixes for module index sorting. +#modindex_common_prefix = [] + + +# -- Options for HTML output --------------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +html_theme = 'sphinx_rtd_theme' + +# Theme options are theme-specific and customize the look and feel of a theme +# further. For a list of options available for each theme, see the +# documentation. +#html_theme_options = {} + +# Add any paths that contain custom themes here, relative to this directory. +html_theme_path = ['@top_srcdir@/doc/_themes'] + +# The name for this set of Sphinx documents. If None, it defaults to +# " v documentation". +#html_title = None + +# A shorter title for the navigation bar. Default is the same as html_title. +#html_short_title = None + +# The name of an image file (relative to this directory) to place at the top +# of the sidebar. +#html_logo = None + +# The name of an image file (within the static path) to use as favicon of the +# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 +# pixels large. +#html_favicon = None + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +#html_static_path = [] + +# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, +# using the given strftime format. +#html_last_updated_fmt = '%b %d, %Y' + +# If true, SmartyPants will be used to convert quotes and dashes to +# typographically correct entities. +html_use_smartypants = False + +# Custom sidebar templates, maps document names to template names. +html_sidebars = { + '**': ['menu.html', 'localtoc.html', 'relations.html', 'sourcelink.html', + 'searchbox.html'] + } + +# Additional templates that should be rendered to pages, maps page names to +# template names. +#html_additional_pages = {} + +# If false, no module index is generated. +#html_domain_indices = True + +# If false, no index is generated. +#html_use_index = True + +# If true, the index is split into individual pages for each letter. +#html_split_index = False + +# If true, links to the reST sources are added to the pages. +html_show_sourcelink = False +html_copy_source = False + +# If true, "Created using Sphinx" is shown in the HTML footer. Default is True. +#html_show_sphinx = True + +# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. +#html_show_copyright = True + +# If true, an OpenSearch description file will be output, and all pages will +# contain a tag referring to it. The value of this option must be the +# base URL from which the finished HTML is served. +#html_use_opensearch = '' + +# This is the file name suffix for HTML files (e.g. ".xhtml"). +#html_file_suffix = None + +# Output file base name for HTML help builder. +htmlhelp_basename = 'nghttp2doc' + + +# -- Options for LaTeX output -------------------------------------------------- + +# The paper size ('letter' or 'a4'). +#latex_paper_size = 'letter' + +# The font size ('10pt', '11pt' or '12pt'). +#latex_font_size = '10pt' + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, author, documentclass [howto/manual]). +latex_documents = [ + ('index', 'nghttp2.tex', u'nghttp2 Documentation', + u'Tatsuhiro Tsujikawa', 'manual'), +] + +# The name of an image file (relative to this directory) to place at the top of +# the title page. +#latex_logo = None + +# For "manual" documents, if this is true, then toplevel headings are parts, +# not chapters. +#latex_use_parts = False + +# If true, show page references after internal links. +#latex_show_pagerefs = False + +# If true, show URL addresses after external links. +#latex_show_urls = False + +# Additional stuff for the LaTeX preamble. +#latex_preamble = '' + +# Documents to append as an appendix to all manuals. +#latex_appendices = [] + +# If false, no module index is generated. +#latex_domain_indices = True + + +# -- Options for manual page output -------------------------------------------- + +# One entry per manual page. List of tuples +# (source start file, name, description, authors, manual section). +man_pages = [ + ('nghttp.1', 'nghttp', u'HTTP/2 experimental client', + [u'Tatsuhiro Tsujikawa'], 1), + ('nghttpd.1', 'nghttpd', u'HTTP/2 experimental server', + [u'Tatsuhiro Tsujikawa'], 1), + ('nghttpx.1', 'nghttpx', u'HTTP/2 experimental proxy', + [u'Tatsuhiro Tsujikawa'], 1), + ('h2load.1', 'h2load', u'HTTP/2 benchmarking tool', + [u'Tatsuhiro Tsujikawa'], 1) +] diff --git a/doc/contribute.rst.in b/doc/contribute.rst.in new file mode 100644 index 0000000..6a229d4 --- /dev/null +++ b/doc/contribute.rst.in @@ -0,0 +1 @@ +.. include:: @top_srcdir@/doc/sources/contribute.rst diff --git a/doc/enums.rst b/doc/enums.rst new file mode 100644 index 0000000..27041c9 --- /dev/null +++ b/doc/enums.rst @@ -0,0 +1,446 @@ + +Enums +===== +.. type:: nghttp2_error + + + Error codes used in this library. The code range is [-999, -500], + inclusive. The following values are defined: + + .. macro:: NGHTTP2_ERR_INVALID_ARGUMENT + + (``-501``) + Invalid argument passed. + .. macro:: NGHTTP2_ERR_BUFFER_ERROR + + (``-502``) + Out of buffer space. + .. macro:: NGHTTP2_ERR_UNSUPPORTED_VERSION + + (``-503``) + The specified protocol version is not supported. + .. macro:: NGHTTP2_ERR_WOULDBLOCK + + (``-504``) + Used as a return value from :type:`nghttp2_send_callback`, + :type:`nghttp2_recv_callback` and + :type:`nghttp2_send_data_callback` to indicate that the operation + would block. + .. macro:: NGHTTP2_ERR_PROTO + + (``-505``) + General protocol error + .. macro:: NGHTTP2_ERR_INVALID_FRAME + + (``-506``) + The frame is invalid. + .. macro:: NGHTTP2_ERR_EOF + + (``-507``) + The peer performed a shutdown on the connection. + .. macro:: NGHTTP2_ERR_DEFERRED + + (``-508``) + Used as a return value from + :func:`nghttp2_data_source_read_callback` to indicate that data + transfer is postponed. See + :func:`nghttp2_data_source_read_callback` for details. + .. macro:: NGHTTP2_ERR_STREAM_ID_NOT_AVAILABLE + + (``-509``) + Stream ID has reached the maximum value. Therefore no stream ID + is available. + .. macro:: NGHTTP2_ERR_STREAM_CLOSED + + (``-510``) + The stream is already closed; or the stream ID is invalid. + .. macro:: NGHTTP2_ERR_STREAM_CLOSING + + (``-511``) + RST_STREAM has been added to the outbound queue. The stream is + in closing state. + .. macro:: NGHTTP2_ERR_STREAM_SHUT_WR + + (``-512``) + The transmission is not allowed for this stream (e.g., a frame + with END_STREAM flag set has already sent). + .. macro:: NGHTTP2_ERR_INVALID_STREAM_ID + + (``-513``) + The stream ID is invalid. + .. macro:: NGHTTP2_ERR_INVALID_STREAM_STATE + + (``-514``) + The state of the stream is not valid (e.g., DATA cannot be sent + to the stream if response HEADERS has not been sent). + .. macro:: NGHTTP2_ERR_DEFERRED_DATA_EXIST + + (``-515``) + Another DATA frame has already been deferred. + .. macro:: NGHTTP2_ERR_START_STREAM_NOT_ALLOWED + + (``-516``) + Starting new stream is not allowed (e.g., GOAWAY has been sent + and/or received). + .. macro:: NGHTTP2_ERR_GOAWAY_ALREADY_SENT + + (``-517``) + GOAWAY has already been sent. + .. macro:: NGHTTP2_ERR_INVALID_HEADER_BLOCK + + (``-518``) + The received frame contains the invalid header block (e.g., There + are duplicate header names; or the header names are not encoded + in US-ASCII character set and not lower cased; or the header name + is zero-length string; or the header value contains multiple + in-sequence NUL bytes). + .. macro:: NGHTTP2_ERR_INVALID_STATE + + (``-519``) + Indicates that the context is not suitable to perform the + requested operation. + .. macro:: NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE + + (``-521``) + The user callback function failed due to the temporal error. + .. macro:: NGHTTP2_ERR_FRAME_SIZE_ERROR + + (``-522``) + The length of the frame is invalid, either too large or too small. + .. macro:: NGHTTP2_ERR_HEADER_COMP + + (``-523``) + Header block inflate/deflate error. + .. macro:: NGHTTP2_ERR_FLOW_CONTROL + + (``-524``) + Flow control error + .. macro:: NGHTTP2_ERR_INSUFF_BUFSIZE + + (``-525``) + Insufficient buffer size given to function. + .. macro:: NGHTTP2_ERR_PAUSE + + (``-526``) + Callback was paused by the application + .. macro:: NGHTTP2_ERR_TOO_MANY_INFLIGHT_SETTINGS + + (``-527``) + There are too many in-flight SETTING frame and no more + transmission of SETTINGS is allowed. + .. macro:: NGHTTP2_ERR_PUSH_DISABLED + + (``-528``) + The server push is disabled. + .. macro:: NGHTTP2_ERR_DATA_EXIST + + (``-529``) + DATA frame for a given stream has been already submitted and has + not been fully processed yet. + .. macro:: NGHTTP2_ERR_SESSION_CLOSING + + (``-530``) + The current session is closing due to a connection error or + `nghttp2_session_terminate_session()` is called. + .. macro:: NGHTTP2_ERR_HTTP_HEADER + + (``-531``) + Invalid HTTP header field was received and stream is going to be + closed. + .. macro:: NGHTTP2_ERR_HTTP_MESSAGING + + (``-532``) + Violation in HTTP messaging rule. + .. macro:: NGHTTP2_ERR_REFUSED_STREAM + + (``-533``) + Stream was refused. + .. macro:: NGHTTP2_ERR_INTERNAL + + (``-534``) + Unexpected internal error, but recovered. + .. macro:: NGHTTP2_ERR_FATAL + + (``-900``) + The errors < :macro:`NGHTTP2_ERR_FATAL` mean that the library is + under unexpected condition and processing was terminated (e.g., + out of memory). If application receives this error code, it must + stop using that :type:`nghttp2_session` object and only allowed + operation for that object is deallocate it using + `nghttp2_session_del()`. + .. macro:: NGHTTP2_ERR_NOMEM + + (``-901``) + Out of memory. This is a fatal error. + .. macro:: NGHTTP2_ERR_CALLBACK_FAILURE + + (``-902``) + The user callback function failed. This is a fatal error. + .. macro:: NGHTTP2_ERR_BAD_CLIENT_MAGIC + + (``-903``) + Invalid client magic (see :macro:`NGHTTP2_CLIENT_MAGIC`) was + received and further processing is not possible. + +.. type:: nghttp2_nv_flag + + + The flags for header field name/value pair. + + .. macro:: NGHTTP2_NV_FLAG_NONE + + (``0``) + No flag set. + .. macro:: NGHTTP2_NV_FLAG_NO_INDEX + + (``0x01``) + Indicates that this name/value pair must not be indexed ("Literal + Header Field never Indexed" representation must be used in HPACK + encoding). Other implementation calls this bit as "sensitive". + +.. type:: nghttp2_frame_type + + + The frame types in HTTP/2 specification. + + .. macro:: NGHTTP2_DATA + + (``0``) + The DATA frame. + .. macro:: NGHTTP2_HEADERS + + (``0x01``) + The HEADERS frame. + .. macro:: NGHTTP2_PRIORITY + + (``0x02``) + The PRIORITY frame. + .. macro:: NGHTTP2_RST_STREAM + + (``0x03``) + The RST_STREAM frame. + .. macro:: NGHTTP2_SETTINGS + + (``0x04``) + The SETTINGS frame. + .. macro:: NGHTTP2_PUSH_PROMISE + + (``0x05``) + The PUSH_PROMISE frame. + .. macro:: NGHTTP2_PING + + (``0x06``) + The PING frame. + .. macro:: NGHTTP2_GOAWAY + + (``0x07``) + The GOAWAY frame. + .. macro:: NGHTTP2_WINDOW_UPDATE + + (``0x08``) + The WINDOW_UPDATE frame. + .. macro:: NGHTTP2_CONTINUATION + + (``0x09``) + The CONTINUATION frame. This frame type won't be passed to any + callbacks because the library processes this frame type and its + preceding HEADERS/PUSH_PROMISE as a single frame. + +.. type:: nghttp2_flag + + + The flags for HTTP/2 frames. This enum defines all flags for all + frames. + + .. macro:: NGHTTP2_FLAG_NONE + + (``0``) + No flag set. + .. macro:: NGHTTP2_FLAG_END_STREAM + + (``0x01``) + The END_STREAM flag. + .. macro:: NGHTTP2_FLAG_END_HEADERS + + (``0x04``) + The END_HEADERS flag. + .. macro:: NGHTTP2_FLAG_ACK + + (``0x01``) + The ACK flag. + .. macro:: NGHTTP2_FLAG_PADDED + + (``0x08``) + The PADDED flag. + .. macro:: NGHTTP2_FLAG_PRIORITY + + (``0x20``) + The PRIORITY flag. + +.. type:: nghttp2_settings_id + + The SETTINGS ID. + + .. macro:: NGHTTP2_SETTINGS_HEADER_TABLE_SIZE + + (``0x01``) + SETTINGS_HEADER_TABLE_SIZE + .. macro:: NGHTTP2_SETTINGS_ENABLE_PUSH + + (``0x02``) + SETTINGS_ENABLE_PUSH + .. macro:: NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS + + (``0x03``) + SETTINGS_MAX_CONCURRENT_STREAMS + .. macro:: NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE + + (``0x04``) + SETTINGS_INITIAL_WINDOW_SIZE + .. macro:: NGHTTP2_SETTINGS_MAX_FRAME_SIZE + + (``0x05``) + SETTINGS_MAX_FRAME_SIZE + .. macro:: NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE + + (``0x06``) + SETTINGS_MAX_HEADER_LIST_SIZE + +.. type:: nghttp2_error_code + + The status codes for the RST_STREAM and GOAWAY frames. + + .. macro:: NGHTTP2_NO_ERROR + + (``0x00``) + No errors. + .. macro:: NGHTTP2_PROTOCOL_ERROR + + (``0x01``) + PROTOCOL_ERROR + .. macro:: NGHTTP2_INTERNAL_ERROR + + (``0x02``) + INTERNAL_ERROR + .. macro:: NGHTTP2_FLOW_CONTROL_ERROR + + (``0x03``) + FLOW_CONTROL_ERROR + .. macro:: NGHTTP2_SETTINGS_TIMEOUT + + (``0x04``) + SETTINGS_TIMEOUT + .. macro:: NGHTTP2_STREAM_CLOSED + + (``0x05``) + STREAM_CLOSED + .. macro:: NGHTTP2_FRAME_SIZE_ERROR + + (``0x06``) + FRAME_SIZE_ERROR + .. macro:: NGHTTP2_REFUSED_STREAM + + (``0x07``) + REFUSED_STREAM + .. macro:: NGHTTP2_CANCEL + + (``0x08``) + CANCEL + .. macro:: NGHTTP2_COMPRESSION_ERROR + + (``0x09``) + COMPRESSION_ERROR + .. macro:: NGHTTP2_CONNECT_ERROR + + (``0x0a``) + CONNECT_ERROR + .. macro:: NGHTTP2_ENHANCE_YOUR_CALM + + (``0x0b``) + ENHANCE_YOUR_CALM + .. macro:: NGHTTP2_INADEQUATE_SECURITY + + (``0x0c``) + INADEQUATE_SECURITY + .. macro:: NGHTTP2_HTTP_1_1_REQUIRED + + (``0x0d``) + HTTP_1_1_REQUIRED + +.. type:: nghttp2_data_flag + + + The flags used to set in *data_flags* output parameter in + :type:`nghttp2_data_source_read_callback`. + + .. macro:: NGHTTP2_DATA_FLAG_NONE + + (``0``) + No flag set. + .. macro:: NGHTTP2_DATA_FLAG_EOF + + (``0x01``) + Indicates EOF was sensed. + .. macro:: NGHTTP2_DATA_FLAG_NO_END_STREAM + + (``0x02``) + Indicates that END_STREAM flag must not be set even if + NGHTTP2_DATA_FLAG_EOF is set. Usually this flag is used to send + trailer header fields with `nghttp2_submit_request()` or + `nghttp2_submit_response()`. + .. macro:: NGHTTP2_DATA_FLAG_NO_COPY + + (``0x04``) + Indicates that application will send complete DATA frame in + :type:`nghttp2_send_data_callback`. + +.. type:: nghttp2_headers_category + + + The category of HEADERS, which indicates the role of the frame. In + HTTP/2 spec, request, response, push response and other arbitrary + headers (e.g., trailers) are all called just HEADERS. To give the + application the role of incoming HEADERS frame, we define several + categories. + + .. macro:: NGHTTP2_HCAT_REQUEST + + (``0``) + The HEADERS frame is opening new stream, which is analogous to + SYN_STREAM in SPDY. + .. macro:: NGHTTP2_HCAT_RESPONSE + + (``1``) + The HEADERS frame is the first response headers, which is + analogous to SYN_REPLY in SPDY. + .. macro:: NGHTTP2_HCAT_PUSH_RESPONSE + + (``2``) + The HEADERS frame is the first headers sent against reserved + stream. + .. macro:: NGHTTP2_HCAT_HEADERS + + (``3``) + The HEADERS frame which does not apply for the above categories, + which is analogous to HEADERS in SPDY. If non-final response + (e.g., status 1xx) is used, final response HEADERS frame will be + categorized here. + +.. type:: nghttp2_hd_inflate_flag + + + The flags for header inflation. + + .. macro:: NGHTTP2_HD_INFLATE_NONE + + (``0``) + No flag set. + .. macro:: NGHTTP2_HD_INFLATE_FINAL + + (``0x01``) + Indicates all headers were inflated. + .. macro:: NGHTTP2_HD_INFLATE_EMIT + + (``0x02``) + Indicates a header was emitted. + diff --git a/doc/h2load-howto.rst.in b/doc/h2load-howto.rst.in new file mode 100644 index 0000000..252069d --- /dev/null +++ b/doc/h2load-howto.rst.in @@ -0,0 +1 @@ +.. include:: @top_srcdir@/doc/sources/h2load-howto.rst diff --git a/doc/h2load.1 b/doc/h2load.1 new file mode 100644 index 0000000..beeb863 --- /dev/null +++ b/doc/h2load.1 @@ -0,0 +1,273 @@ +.\" Man page generated from reStructuredText. +. +.TH "H2LOAD" "1" "May 16, 2015" "1.0.0" "nghttp2" +.SH NAME +h2load \- HTTP/2 benchmarking tool +. +.nr rst2man-indent-level 0 +. +.de1 rstReportMargin +\\$1 \\n[an-margin] +level \\n[rst2man-indent-level] +level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] +- +\\n[rst2man-indent0] +\\n[rst2man-indent1] +\\n[rst2man-indent2] +.. +.de1 INDENT +.\" .rstReportMargin pre: +. RS \\$1 +. nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] +. nr rst2man-indent-level +1 +.\" .rstReportMargin post: +.. +.de UNINDENT +. RE +.\" indent \\n[an-margin] +.\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] +.nr rst2man-indent-level -1 +.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] +.in \\n[rst2man-indent\\n[rst2man-indent-level]]u +.. +.SH SYNOPSIS +.sp +\fBh2load\fP [OPTIONS]... [URI]... +.SH DESCRIPTION +.sp +benchmarking tool for HTTP/2 and SPDY server +.INDENT 0.0 +.TP +.B +Specify URI to access. Multiple URIs can be specified. +URIs are used in this order for each client. All URIs +are used, then first URI is used and then 2nd URI, and +so on. The scheme, host and port in the subsequent +URIs, if present, are ignored. Those in the first URI +are used solely. +.UNINDENT +.SH OPTIONS +.INDENT 0.0 +.TP +.B \-n, \-\-requests= +Number of requests. +.sp +Default: \fB1\fP +.UNINDENT +.INDENT 0.0 +.TP +.B \-c, \-\-clients= +Number of concurrent clients. +.sp +Default: \fB1\fP +.UNINDENT +.INDENT 0.0 +.TP +.B \-t, \-\-threads= +Number of native threads. +.sp +Default: \fB1\fP +.UNINDENT +.INDENT 0.0 +.TP +.B \-i, \-\-input\-file= +Path of a file with multiple URIs are separated by EOLs. +This option will disable URIs getting from command\-line. +If \(aq\-\(aq is given as , URIs will be read from stdin. +URIs are used in this order for each client. All URIs +are used, then first URI is used and then 2nd URI, and +so on. The scheme, host and port in the subsequent +URIs, if present, are ignored. Those in the first URI +are used solely. +.UNINDENT +.INDENT 0.0 +.TP +.B \-m, \-\-max\-concurrent\-streams=(auto|) +Max concurrent streams to issue per session. If "auto" +is given, the number of given URIs is used. +.sp +Default: \fBauto\fP +.UNINDENT +.INDENT 0.0 +.TP +.B \-w, \-\-window\-bits= +Sets the stream level initial window size to (2**)\-1. +For SPDY, 2** is used instead. +.sp +Default: \fB30\fP +.UNINDENT +.INDENT 0.0 +.TP +.B \-W, \-\-connection\-window\-bits= +Sets the connection level initial window size to +(2**)\-1. For SPDY, if is strictly less than 16, +this option is ignored. Otherwise 2** is used for +SPDY. +.sp +Default: \fB30\fP +.UNINDENT +.INDENT 0.0 +.TP +.B \-H, \-\-header=
    +Add/Override a header to the requests. +.UNINDENT +.INDENT 0.0 +.TP +.B \-p, \-\-no\-tls\-proto= +Specify ALPN identifier of the protocol to be used when +accessing http URI without SSL/TLS. +Available protocols: spdy/2, spdy/3, spdy/3.1 and h2c +.sp +Default: \fBh2c\fP +.UNINDENT +.INDENT 0.0 +.TP +.B \-d, \-\-data= +Post FILE to server. The request method is changed to +POST. +.UNINDENT +.INDENT 0.0 +.TP +.B \-v, \-\-verbose +Output debug information. +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-version +Display version information and exit. +.UNINDENT +.INDENT 0.0 +.TP +.B \-h, \-\-help +Display this help and exit. +.UNINDENT +.SH OUTPUT +.INDENT 0.0 +.TP +.B requests +.INDENT 7.0 +.TP +.B total +The number of requests h2load was instructed to make. +.TP +.B started +The number of requests h2load has started. +.TP +.B done +The number of requests completed. +.TP +.B succeeded +The number of requests completed successfully. Only HTTP status +code 2xx or3xx are considered as success. +.TP +.B failed +The number of requests failed, including HTTP level failures +(non\-successful HTTP status code). +.TP +.B errored +The number of requests failed, except for HTTP level failures. +status code. This is the subset of the number reported in +\fBfailed\fP and most likely the network level failures or stream +was reset by RST_STREAM. +.UNINDENT +.TP +.B status codes +The number of status code h2load received. +.TP +.B traffic +.INDENT 7.0 +.TP +.B total +The number of bytes received from the server "on the wire". If +requests were made via TLS, this value is the number of decrpyted +bytes. +.TP +.B headers +The number of response header bytes from the server without +decompression. For HTTP/2, this is the sum of the payload of +HEADERS frame. For SPDY, this is the sum of the payload of +SYN_REPLY frame. +.TP +.B data +The number of response body bytes received from the server. +.UNINDENT +.TP +.B time for request +.INDENT 7.0 +.TP +.B min +The minimum time taken for request and response. +.TP +.B max +The maximum time taken for request and response. +.TP +.B mean +The mean time taken for request and response. +.TP +.B sd +The standard deviation of the time taken for request and response. +.TP +.B +/\- sd +The fraction of the number of requests within standard deviation +range (mean +/\- sd) against total number of successful requests. +.UNINDENT +.TP +.B time for connect +.INDENT 7.0 +.TP +.B min +The minimum time taken to connect to a server. +.TP +.B max +The maximum time taken to connect to a server. +.TP +.B mean +The mean time taken to connect to a server. +.TP +.B sd +The standard deviation of the time taken to connect to a server. +.TP +.B +/\- sd +The fraction of the number of connections within standard +deviation range (mean +/\- sd) against total number of successful +connections. +.UNINDENT +.TP +.B time for 1st byte (of (decrypted in case of TLS) application data) +.INDENT 7.0 +.TP +.B min +The minimum time taken to get 1st byte from a server. +.TP +.B max +The maximum time taken to get 1st byte from a server. +.TP +.B mean +The mean time taken to get 1st byte from a server. +.TP +.B sd +The standard deviation of the time taken to get 1st byte from a +server. +.TP +.B +/\- sd +The fraction of the number of connections within standard +deviation range (mean +/\- sd) against total number of successful +connections. +.UNINDENT +.UNINDENT +.SH FLOW CONTROL +.sp +h2load sets large flow control window by default, and effectively +disables flow control to avoid under utilization of server +performance. To set smaller flow control window, use \fI\%\-w\fP and +\fI\%\-W\fP options. For example, use \fB\-w16 \-W16\fP to set default +window size described in HTTP/2 and SPDY protocol specification. +.SH SEE ALSO +.sp +\fInghttp(1)\fP, \fInghttpd(1)\fP, \fInghttpx(1)\fP +.SH AUTHOR +Tatsuhiro Tsujikawa +.SH COPYRIGHT +2012, 2015, Tatsuhiro Tsujikawa +.\" Generated by docutils manpage writer. +. diff --git a/doc/h2load.1.rst b/doc/h2load.1.rst new file mode 100644 index 0000000..21e5a42 --- /dev/null +++ b/doc/h2load.1.rst @@ -0,0 +1,204 @@ + +.. GENERATED by help2rst.py. DO NOT EDIT DIRECTLY. + +.. program:: h2load + +h2load(1) +========= + +SYNOPSIS +-------- + +**h2load** [OPTIONS]... [URI]... + +DESCRIPTION +----------- + +benchmarking tool for HTTP/2 and SPDY server + +.. describe:: + + Specify URI to access. Multiple URIs can be specified. + URIs are used in this order for each client. All URIs + are used, then first URI is used and then 2nd URI, and + so on. The scheme, host and port in the subsequent + URIs, if present, are ignored. Those in the first URI + are used solely. + +OPTIONS +------- + +.. option:: -n, --requests= + + Number of requests. + + Default: ``1`` + +.. option:: -c, --clients= + + Number of concurrent clients. + + Default: ``1`` + +.. option:: -t, --threads= + + Number of native threads. + + Default: ``1`` + +.. option:: -i, --input-file= + + Path of a file with multiple URIs are separated by EOLs. + This option will disable URIs getting from command-line. + If '-' is given as , URIs will be read from stdin. + URIs are used in this order for each client. All URIs + are used, then first URI is used and then 2nd URI, and + so on. The scheme, host and port in the subsequent + URIs, if present, are ignored. Those in the first URI + are used solely. + +.. option:: -m, --max-concurrent-streams=(auto|) + + Max concurrent streams to issue per session. If "auto" + is given, the number of given URIs is used. + + Default: ``auto`` + +.. option:: -w, --window-bits= + + Sets the stream level initial window size to (2\*\*)-1. + For SPDY, 2** is used instead. + + Default: ``30`` + +.. option:: -W, --connection-window-bits= + + Sets the connection level initial window size to + (2**)-1. For SPDY, if is strictly less than 16, + this option is ignored. Otherwise 2\*\* is used for + SPDY. + + Default: ``30`` + +.. option:: -H, --header=
    + + Add/Override a header to the requests. + +.. option:: -p, --no-tls-proto= + + Specify ALPN identifier of the protocol to be used when + accessing http URI without SSL/TLS. + Available protocols: spdy/2, spdy/3, spdy/3.1 and h2c + + Default: ``h2c`` + +.. option:: -d, --data= + + Post FILE to server. The request method is changed to + POST. + +.. option:: -v, --verbose + + Output debug information. + +.. option:: --version + + Display version information and exit. + +.. option:: -h, --help + + Display this help and exit. + +OUTPUT +------ + +requests + total + The number of requests h2load was instructed to make. + started + The number of requests h2load has started. + done + The number of requests completed. + succeeded + The number of requests completed successfully. Only HTTP status + code 2xx or3xx are considered as success. + failed + The number of requests failed, including HTTP level failures + (non-successful HTTP status code). + errored + The number of requests failed, except for HTTP level failures. + status code. This is the subset of the number reported in + ``failed`` and most likely the network level failures or stream + was reset by RST_STREAM. + +status codes + The number of status code h2load received. + +traffic + total + The number of bytes received from the server "on the wire". If + requests were made via TLS, this value is the number of decrpyted + bytes. + headers + The number of response header bytes from the server without + decompression. For HTTP/2, this is the sum of the payload of + HEADERS frame. For SPDY, this is the sum of the payload of + SYN_REPLY frame. + data + The number of response body bytes received from the server. + +time for request + min + The minimum time taken for request and response. + max + The maximum time taken for request and response. + mean + The mean time taken for request and response. + sd + The standard deviation of the time taken for request and response. + +/- sd + The fraction of the number of requests within standard deviation + range (mean +/- sd) against total number of successful requests. + +time for connect + min + The minimum time taken to connect to a server. + max + The maximum time taken to connect to a server. + mean + The mean time taken to connect to a server. + sd + The standard deviation of the time taken to connect to a server. + +/- sd + The fraction of the number of connections within standard + deviation range (mean +/- sd) against total number of successful + connections. + +time for 1st byte (of (decrypted in case of TLS) application data) + min + The minimum time taken to get 1st byte from a server. + max + The maximum time taken to get 1st byte from a server. + mean + The mean time taken to get 1st byte from a server. + sd + The standard deviation of the time taken to get 1st byte from a + server. + +/- sd + The fraction of the number of connections within standard + deviation range (mean +/- sd) against total number of successful + connections. + +FLOW CONTROL +------------ + +h2load sets large flow control window by default, and effectively +disables flow control to avoid under utilization of server +performance. To set smaller flow control window, use :option:`-w` and +:option:`-W` options. For example, use ``-w16 -W16`` to set default +window size described in HTTP/2 and SPDY protocol specification. + +SEE ALSO +-------- + +:manpage:`nghttp(1)`, :manpage:`nghttpd(1)`, :manpage:`nghttpx(1)` diff --git a/doc/index.rst.in b/doc/index.rst.in new file mode 100644 index 0000000..2a49369 --- /dev/null +++ b/doc/index.rst.in @@ -0,0 +1 @@ +.. include:: @top_srcdir@/doc/sources/index.rst diff --git a/doc/libnghttp2_asio.rst.in b/doc/libnghttp2_asio.rst.in new file mode 100644 index 0000000..38254e1 --- /dev/null +++ b/doc/libnghttp2_asio.rst.in @@ -0,0 +1 @@ +.. include:: @top_srcdir@/doc/sources/libnghttp2_asio.rst diff --git a/doc/macros.rst b/doc/macros.rst new file mode 100644 index 0000000..ee9ec33 --- /dev/null +++ b/doc/macros.rst @@ -0,0 +1,86 @@ + +Macros +====== +.. macro:: NGHTTP2_VERSION + + Version number of the nghttp2 library release +.. macro:: NGHTTP2_VERSION_NUM + + Numerical representation of the version number of the nghttp2 library + 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. +.. macro:: NGHTTP2_PROTO_VERSION_ID + + + The protocol version identification string of this library + supports. This identifier is used if HTTP/2 is used over TLS. +.. macro:: NGHTTP2_PROTO_VERSION_ID_LEN + + + The length of :macro:`NGHTTP2_PROTO_VERSION_ID`. +.. macro:: NGHTTP2_PROTO_ALPN + + + The seriazlied form of ALPN protocol identifier this library + supports. Notice that first byte is the length of following + protocol identifier. This is the same wire format of `TLS ALPN + extension `_. This is useful + to process incoming ALPN tokens in wire format. +.. macro:: NGHTTP2_PROTO_ALPN_LEN + + + The length of :macro:`NGHTTP2_PROTO_ALPN`. +.. macro:: NGHTTP2_CLEARTEXT_PROTO_VERSION_ID + + + The protocol version identification string of this library + supports. This identifier is used if HTTP/2 is used over cleartext + TCP. +.. macro:: NGHTTP2_CLEARTEXT_PROTO_VERSION_ID_LEN + + + The length of :macro:`NGHTTP2_CLEARTEXT_PROTO_VERSION_ID`. +.. macro:: NGHTTP2_VERSION_AGE + + + The age of :type:`nghttp2_info` +.. macro:: NGHTTP2_DEFAULT_WEIGHT + + + The default weight of stream dependency. +.. macro:: NGHTTP2_MAX_WEIGHT + + + The maximum weight of stream dependency. +.. macro:: NGHTTP2_MIN_WEIGHT + + + The minimum weight of stream dependency. +.. macro:: NGHTTP2_MAX_WINDOW_SIZE + + + The maximum window size +.. macro:: NGHTTP2_INITIAL_WINDOW_SIZE + + + The initial window size for stream level flow control. +.. macro:: NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE + + + The initial window size for connection level flow control. +.. macro:: NGHTTP2_DEFAULT_HEADER_TABLE_SIZE + + + The default header table size. +.. macro:: NGHTTP2_CLIENT_MAGIC + + + The client magic string, which is the first 24 bytes byte string of + client connection preface. +.. macro:: NGHTTP2_CLIENT_MAGIC_LEN + + + The length of :macro:`NGHTTP2_CLIENT_MAGIC`. +.. macro:: NGHTTP2_INITIAL_MAX_CONCURRENT_STREAMS + + Default maximum concurrent streams. diff --git a/doc/mkapiref.py b/doc/mkapiref.py new file mode 100755 index 0000000..23ab9cc --- /dev/null +++ b/doc/mkapiref.py @@ -0,0 +1,277 @@ +#!/usr/bin/env python +# nghttp2 - HTTP/2 C Library + +# Copyright (c) 2012 Tatsuhiro Tsujikawa + +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: + +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. + +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +# Generates API reference from C source code. +from __future__ import print_function # At least python 2.6 is required +import re, sys, argparse, os.path + +class FunctionDoc: + def __init__(self, name, content, domain): + self.name = name + self.content = content + self.domain = domain + if self.domain == 'function': + self.funcname = re.search(r'(nghttp2_[^ )]+)\(', self.name).group(1) + + def write(self, out): + out.write('.. {}:: {}\n'.format(self.domain, self.name)) + out.write('\n') + for line in self.content: + out.write(' {}\n'.format(line)) + +class StructDoc: + def __init__(self, name, content, members, member_domain): + self.name = name + self.content = content + self.members = members + self.member_domain = member_domain + + def write(self, out): + if self.name: + out.write('.. type:: {}\n'.format(self.name)) + out.write('\n') + for line in self.content: + out.write(' {}\n'.format(line)) + out.write('\n') + for name, content in self.members: + out.write(' .. {}:: {}\n'.format(self.member_domain, name)) + out.write('\n') + for line in content: + out.write(' {}\n'.format(line)) + out.write('\n') + +class MacroDoc: + def __init__(self, name, content): + self.name = name + self.content = content + + def write(self, out): + out.write('''.. macro:: {}\n'''.format(self.name)) + out.write('\n') + for line in self.content: + out.write(' {}\n'.format(line)) + +def make_api_ref(infiles): + macros = [] + enums = [] + types = [] + functions = [] + for infile in infiles: + while True: + line = infile.readline() + if not line: + break + elif line == '/**\n': + line = infile.readline() + doctype = line.split()[1] + if doctype == '@function': + functions.append(process_function('function', infile)) + elif doctype == '@functypedef': + types.append(process_function('type', infile)) + elif doctype == '@struct' or doctype == '@union': + types.append(process_struct(infile)) + elif doctype == '@enum': + enums.append(process_enum(infile)) + elif doctype == '@macro': + macros.append(process_macro(infile)) + return macros, enums, types, functions + + alldocs = [('Macros', macros), + ('Enums', enums), + ('Types (structs, unions and typedefs)', types), + ('Functions', functions)] + +def output( + indexfile, macrosfile, enumsfile, typesfile, funcsdir, + macros, enums, types, functions): + indexfile.write(''' +API Reference +============= + +.. toctree:: + :maxdepth: 1 + + macros + enums + types +''') + + for doc in functions: + indexfile.write(' {}\n'.format(doc.funcname)) + + macrosfile.write(''' +Macros +====== +''') + for doc in macros: + doc.write(macrosfile) + + enumsfile.write(''' +Enums +===== +''') + for doc in enums: + doc.write(enumsfile) + + typesfile.write(''' +Types (structs, unions and typedefs) +==================================== +''') + for doc in types: + doc.write(typesfile) + + for doc in functions: + with open(os.path.join(funcsdir, doc.funcname + '.rst'), 'w') as f: + f.write(''' +{funcname} +{secul} + +Synopsis +-------- + +*#include * + +'''.format(funcname=doc.funcname, secul='='*len(doc.funcname))) + doc.write(f) + +def process_macro(infile): + content = read_content(infile) + line = infile.readline() + macro_name = line.split()[1] + return MacroDoc(macro_name, content) + +def process_enum(infile): + members = [] + enum_name = None + content = read_content(infile) + while True: + line = infile.readline() + if not line: + break + elif re.match(r'\s*/\*\*\n', line): + member_content = read_content(infile) + line = infile.readline() + items = line.split() + member_name = items[0] + if len(items) >= 3: + member_content.insert(0, '(``{}``) '\ + .format(' '.join(items[2:]).rstrip(','))) + members.append((member_name, member_content)) + elif line.startswith('}'): + enum_name = line.rstrip().split()[1] + enum_name = re.sub(r';$', '', enum_name) + break + return StructDoc(enum_name, content, members, 'macro') + +def process_struct(infile): + members = [] + struct_name = None + content = read_content(infile) + while True: + line = infile.readline() + if not line: + break + elif re.match(r'\s*/\*\*\n', line): + member_content = read_content(infile) + line = infile.readline() + member_name = line.rstrip().rstrip(';') + members.append((member_name, member_content)) + elif line.startswith('}') or\ + (line.startswith('typedef ') and line.endswith(';\n')): + if line.startswith('}'): + index = 1 + else: + index = 3 + struct_name = line.rstrip().split()[index] + struct_name = re.sub(r';$', '', struct_name) + break + return StructDoc(struct_name, content, members, 'member') + +def process_function(domain, infile): + content = read_content(infile) + func_proto = [] + while True: + line = infile.readline() + if not line: + break + elif line == '\n': + break + else: + func_proto.append(line) + func_proto = ''.join(func_proto) + func_proto = re.sub(r';\n$', '', func_proto) + func_proto = re.sub(r'\s+', ' ', func_proto) + func_proto = re.sub(r'NGHTTP2_EXTERN ', '', func_proto) + return FunctionDoc(func_proto, content, domain) + +def read_content(infile): + content = [] + while True: + line = infile.readline() + if not line: + break + if re.match(r'\s*\*/\n', line): + break + else: + content.append(transform_content(line.rstrip())) + return content + +def arg_repl(matchobj): + return '*{}*'.format(matchobj.group(1).replace('*', '\\*')) + +def transform_content(content): + content = re.sub(r'^\s+\* ?', '', content) + content = re.sub(r'\|([^\s|]+)\|', arg_repl, content) + content = re.sub(r':enum:', ':macro:', content) + return content + +if __name__ == '__main__': + parser = argparse.ArgumentParser(description="Generate API reference") + parser.add_argument('index', type=argparse.FileType('w'), + help='index output file') + parser.add_argument('macros', type=argparse.FileType('w'), + help='macros section output file. The filename should be macros.rst') + parser.add_argument('enums', type=argparse.FileType('w'), + help='enums section output file. The filename should be enums.rst') + parser.add_argument('types', type=argparse.FileType('w'), + help='types section output file. The filename should be types.rst') + parser.add_argument('funcsdir', + help='functions doc output dir') + parser.add_argument('files', nargs='+', type=argparse.FileType('r'), + help='source file') + args = parser.parse_args() + macros = [] + enums = [] + types = [] + funcs = [] + for infile in args.files: + m, e, t, f = make_api_ref(args.files) + macros.extend(m) + enums.extend(e) + types.extend(t) + funcs.extend(f) + funcs.sort(key=lambda x: x.funcname) + output( + args.index, args.macros, args.enums, args.types, args.funcsdir, + macros, enums, types, funcs) diff --git a/doc/nghttp.1 b/doc/nghttp.1 new file mode 100644 index 0000000..c29360f --- /dev/null +++ b/doc/nghttp.1 @@ -0,0 +1,289 @@ +.\" Man page generated from reStructuredText. +. +.TH "NGHTTP" "1" "May 16, 2015" "1.0.0" "nghttp2" +.SH NAME +nghttp \- HTTP/2 experimental client +. +.nr rst2man-indent-level 0 +. +.de1 rstReportMargin +\\$1 \\n[an-margin] +level \\n[rst2man-indent-level] +level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] +- +\\n[rst2man-indent0] +\\n[rst2man-indent1] +\\n[rst2man-indent2] +.. +.de1 INDENT +.\" .rstReportMargin pre: +. RS \\$1 +. nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] +. nr rst2man-indent-level +1 +.\" .rstReportMargin post: +.. +.de UNINDENT +. RE +.\" indent \\n[an-margin] +.\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] +.nr rst2man-indent-level -1 +.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] +.in \\n[rst2man-indent\\n[rst2man-indent-level]]u +.. +.SH SYNOPSIS +.sp +\fBnghttp\fP [OPTIONS]... ... +.SH DESCRIPTION +.sp +HTTP/2 experimental client +.INDENT 0.0 +.TP +.B +Specify URI to access. +.UNINDENT +.SH OPTIONS +.INDENT 0.0 +.TP +.B \-v, \-\-verbose +Print debug information such as reception and +transmission of frames and name/value pairs. Specifying +this option multiple times increases verbosity. +.UNINDENT +.INDENT 0.0 +.TP +.B \-n, \-\-null\-out +Discard downloaded data. +.UNINDENT +.INDENT 0.0 +.TP +.B \-O, \-\-remote\-name +Save download data in the current directory. The +filename is dereived from URI. If URI ends with \(aq\fI/\fP\(aq, +\(aqindex.html\(aq is used as a filename. Not implemented +yet. +.UNINDENT +.INDENT 0.0 +.TP +.B \-t, \-\-timeout= +Timeout each request after . Set 0 to disable +timeout. +.UNINDENT +.INDENT 0.0 +.TP +.B \-w, \-\-window\-bits= +Sets the stream level initial window size to 2**\-1. +.UNINDENT +.INDENT 0.0 +.TP +.B \-W, \-\-connection\-window\-bits= +Sets the connection level initial window size to +2**\-1. +.UNINDENT +.INDENT 0.0 +.TP +.B \-a, \-\-get\-assets +Download assets such as stylesheets, images and script +files linked from the downloaded resource. Only links +whose origins are the same with the linking resource +will be downloaded. nghttp prioritizes resources using +HTTP/2 dependency based priority. The priority order, +from highest to lowest, is html itself, css, javascript +and images. +.UNINDENT +.INDENT 0.0 +.TP +.B \-s, \-\-stat +Print statistics. +.UNINDENT +.INDENT 0.0 +.TP +.B \-H, \-\-header=
    +Add a header to the requests. Example: \fI\%\-H\fP\(aq:method: PUT\(aq +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-trailer=
    +Add a trailer header to the requests.
    must not +include pseudo header field (header field name starting +with \(aq:\(aq). To send trailer, one must use \fI\%\-d\fP option to +send request body. Example: \fI\%\-\-trailer\fP \(aqfoo: bar\(aq. +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-cert= +Use the specified client certificate file. The file +must be in PEM format. +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-key= +Use the client private key file. The file must be in +PEM format. +.UNINDENT +.INDENT 0.0 +.TP +.B \-d, \-\-data= +Post FILE to server. If \(aq\-\(aq is given, data will be read +from stdin. +.UNINDENT +.INDENT 0.0 +.TP +.B \-m, \-\-multiply= +Request each URI times. By default, same URI is not +requested twice. This option disables it too. +.UNINDENT +.INDENT 0.0 +.TP +.B \-u, \-\-upgrade +Perform HTTP Upgrade for HTTP/2. This option is ignored +if the request URI has https scheme. If \fI\%\-d\fP is used, the +HTTP upgrade request is performed with OPTIONS method. +.UNINDENT +.INDENT 0.0 +.TP +.B \-p, \-\-weight= +Sets priority group weight. The valid value range is +[1, 256], inclusive. +.sp +Default: \fB16\fP +.UNINDENT +.INDENT 0.0 +.TP +.B \-M, \-\-peer\-max\-concurrent\-streams= +Use as SETTINGS_MAX_CONCURRENT_STREAMS value of +remote endpoint as if it is received in SETTINGS frame. +The default is large enough as it is seen as unlimited. +.UNINDENT +.INDENT 0.0 +.TP +.B \-c, \-\-header\-table\-size= +Specify decoder header table size. +.UNINDENT +.INDENT 0.0 +.TP +.B \-b, \-\-padding= +Add at most bytes to a frame payload as padding. +Specify 0 to disable padding. +.UNINDENT +.INDENT 0.0 +.TP +.B \-r, \-\-har= +Output HTTP transactions in HAR format. If \(aq\-\(aq +is given, data is written to stdout. +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-color +Force colored log output. +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-continuation +Send large header to test CONTINUATION. +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-no\-content\-length +Don\(aqt send content\-length header field. +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-no\-dep +Don\(aqt send dependency based priority hint to server. +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-hexdump +Display the incoming traffic in hexadecimal (Canonical +hex+ASCII display). If SSL/TLS is used, decrypted data +are used. +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-no\-push +Disable server push. +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-version +Display version information and exit. +.UNINDENT +.INDENT 0.0 +.TP +.B \-h, \-\-help +Display this help and exit. +.UNINDENT +.sp +The argument is an integer and an optional unit (e.g., 10K is +10 * 1024). Units are K, M and G (powers of 1024). +.sp +The argument is an integer and an optional unit (e.g., 1s +is 1 second and 500ms is 500 milliseconds). Units are h, m, s or ms +(hours, minutes, seconds and milliseconds, respectively). If a unit +is omitted, a second is used as unit. +.SH DEPENDENCY BASED PRIORITY +.sp +nghttp sends priority hints to server by default unless +\fI\%\-\-no\-dep\fP is used. nghttp mimics the way Firefox employs to +manages dependency using idle streams. We follows the behaviour of +Firefox Nightly as of April, 2015, and nghttp\(aqs behaviour is very +static and could be different from Firefox in detail. But reproducing +the same behaviour of Firefox is not our goal. The goal is provide +the easy way to test out the dependency priority in server +implementation. +.sp +When connection is established, nghttp sends 5 PRIORITY frames to idle +streams 3, 5, 7, 9 and 11 to create "anchor" nodes in dependency +tree: +.INDENT 0.0 +.INDENT 3.5 +.sp +.nf +.ft C + +\-\-\-\-\-+ + |id=0 | + +\-\-\-\-\-+ + ^ ^ ^ + w=201 / | \e w=1 + / | \e + / w=101| \e + +\-\-\-\-\-+ +\-\-\-\-\-+ +\-\-\-\-\-+ + |id=3 | |id=5 | |id=7 | + +\-\-\-\-\-+ +\-\-\-\-\-+ +\-\-\-\-\-+ + ^ ^ +w=1 | w=1 | + | | + +\-\-\-\-\-+ +\-\-\-\-\-+ + |id=11| |id=9 | + +\-\-\-\-\-+ +\-\-\-\-\-+ +.ft P +.fi +.UNINDENT +.UNINDENT +.sp +In the above figure, \fBid\fP means stream ID, and \fBw\fP means weight. +The stream 0 is non\-existence stream, and forms the root of the tree. +The stream 7 and 9 are not used for now. +.sp +The URIs given in the command\-line depend on stream 11 with the weight +given in \fI\%\-p\fP option, which defaults to 16. +.sp +If \fI\%\-a\fP option is used, nghttp parses the resource pointed by +URI given in command\-line as html, and extracts resource links from +it. When requesting those resources, nghttp uses dependency according +to its resource type. +.sp +For CSS, and Javascript files inside "head" element, they depend on +stream 3 with the weight 2. The Javascript files outside "head" +element depend on stream 5 with the weight 2. The mages depend on +stream 11 with the weight 12. The other resources (e.g., icon) depend +on stream 11 with the weight 2. +.SH SEE ALSO +.sp +\fInghttpd(1)\fP, \fInghttpx(1)\fP, \fIh2load(1)\fP +.SH AUTHOR +Tatsuhiro Tsujikawa +.SH COPYRIGHT +2012, 2015, Tatsuhiro Tsujikawa +.\" Generated by docutils manpage writer. +. diff --git a/doc/nghttp.1.rst b/doc/nghttp.1.rst new file mode 100644 index 0000000..7f2449b --- /dev/null +++ b/doc/nghttp.1.rst @@ -0,0 +1,233 @@ + +.. GENERATED by help2rst.py. DO NOT EDIT DIRECTLY. + +.. program:: nghttp + +nghttp(1) +========= + +SYNOPSIS +-------- + +**nghttp** [OPTIONS]... ... + +DESCRIPTION +----------- + +HTTP/2 experimental client + +.. describe:: + + Specify URI to access. + +OPTIONS +------- + +.. option:: -v, --verbose + + Print debug information such as reception and + transmission of frames and name/value pairs. Specifying + this option multiple times increases verbosity. + +.. option:: -n, --null-out + + Discard downloaded data. + +.. option:: -O, --remote-name + + Save download data in the current directory. The + filename is dereived from URI. If URI ends with '*/*', + 'index.html' is used as a filename. Not implemented + yet. + +.. option:: -t, --timeout= + + Timeout each request after . Set 0 to disable + timeout. + +.. option:: -w, --window-bits= + + Sets the stream level initial window size to 2\*\*-1. + +.. option:: -W, --connection-window-bits= + + Sets the connection level initial window size to + 2\*\*-1. + +.. option:: -a, --get-assets + + Download assets such as stylesheets, images and script + files linked from the downloaded resource. Only links + whose origins are the same with the linking resource + will be downloaded. nghttp prioritizes resources using + HTTP/2 dependency based priority. The priority order, + from highest to lowest, is html itself, css, javascript + and images. + +.. option:: -s, --stat + + Print statistics. + +.. option:: -H, --header=
    + + Add a header to the requests. Example: :option:`-H`\':method: PUT' + +.. option:: --trailer=
    + + Add a trailer header to the requests.
    must not + include pseudo header field (header field name starting + with ':'). To send trailer, one must use :option:`-d` option to + send request body. Example: :option:`--trailer` 'foo: bar'. + +.. option:: --cert= + + Use the specified client certificate file. The file + must be in PEM format. + +.. option:: --key= + + Use the client private key file. The file must be in + PEM format. + +.. option:: -d, --data= + + Post FILE to server. If '-' is given, data will be read + from stdin. + +.. option:: -m, --multiply= + + Request each URI times. By default, same URI is not + requested twice. This option disables it too. + +.. option:: -u, --upgrade + + Perform HTTP Upgrade for HTTP/2. This option is ignored + if the request URI has https scheme. If :option:`-d` is used, the + HTTP upgrade request is performed with OPTIONS method. + +.. option:: -p, --weight= + + Sets priority group weight. The valid value range is + [1, 256], inclusive. + + Default: ``16`` + +.. option:: -M, --peer-max-concurrent-streams= + + Use as SETTINGS_MAX_CONCURRENT_STREAMS value of + remote endpoint as if it is received in SETTINGS frame. + The default is large enough as it is seen as unlimited. + +.. option:: -c, --header-table-size= + + Specify decoder header table size. + +.. option:: -b, --padding= + + Add at most bytes to a frame payload as padding. + Specify 0 to disable padding. + +.. option:: -r, --har= + + Output HTTP transactions in HAR format. If '-' + is given, data is written to stdout. + +.. option:: --color + + Force colored log output. + +.. option:: --continuation + + Send large header to test CONTINUATION. + +.. option:: --no-content-length + + Don't send content-length header field. + +.. option:: --no-dep + + Don't send dependency based priority hint to server. + +.. option:: --hexdump + + Display the incoming traffic in hexadecimal (Canonical + hex+ASCII display). If SSL/TLS is used, decrypted data + are used. + +.. option:: --no-push + + Disable server push. + +.. option:: --version + + Display version information and exit. + +.. option:: -h, --help + + Display this help and exit. + + + +The argument is an integer and an optional unit (e.g., 10K is +10 * 1024). Units are K, M and G (powers of 1024). + +The argument is an integer and an optional unit (e.g., 1s +is 1 second and 500ms is 500 milliseconds). Units are h, m, s or ms +(hours, minutes, seconds and milliseconds, respectively). If a unit +is omitted, a second is used as unit. + +DEPENDENCY BASED PRIORITY +------------------------- + +nghttp sends priority hints to server by default unless +:option:`--no-dep` is used. nghttp mimics the way Firefox employs to +manages dependency using idle streams. We follows the behaviour of +Firefox Nightly as of April, 2015, and nghttp's behaviour is very +static and could be different from Firefox in detail. But reproducing +the same behaviour of Firefox is not our goal. The goal is provide +the easy way to test out the dependency priority in server +implementation. + +When connection is established, nghttp sends 5 PRIORITY frames to idle +streams 3, 5, 7, 9 and 11 to create "anchor" nodes in dependency +tree:: + + +-----+ + |id=0 | + +-----+ + ^ ^ ^ + w=201 / | \ w=1 + / | \ + / w=101| \ + +-----+ +-----+ +-----+ + |id=3 | |id=5 | |id=7 | + +-----+ +-----+ +-----+ + ^ ^ + w=1 | w=1 | + | | + +-----+ +-----+ + |id=11| |id=9 | + +-----+ +-----+ + +In the above figure, ``id`` means stream ID, and ``w`` means weight. +The stream 0 is non-existence stream, and forms the root of the tree. +The stream 7 and 9 are not used for now. + +The URIs given in the command-line depend on stream 11 with the weight +given in :option:`-p` option, which defaults to 16. + +If :option:`-a` option is used, nghttp parses the resource pointed by +URI given in command-line as html, and extracts resource links from +it. When requesting those resources, nghttp uses dependency according +to its resource type. + +For CSS, and Javascript files inside "head" element, they depend on +stream 3 with the weight 2. The Javascript files outside "head" +element depend on stream 5 with the weight 2. The mages depend on +stream 11 with the weight 12. The other resources (e.g., icon) depend +on stream 11 with the weight 2. + +SEE ALSO +-------- + +:manpage:`nghttpd(1)`, :manpage:`nghttpx(1)`, :manpage:`h2load(1)` diff --git a/doc/nghttp2.h.rst.in b/doc/nghttp2.h.rst.in new file mode 100644 index 0000000..29e641d --- /dev/null +++ b/doc/nghttp2.h.rst.in @@ -0,0 +1,4 @@ +nghttp2.h +========= + +.. literalinclude:: @top_srcdir@/lib/includes/nghttp2/nghttp2.h diff --git a/doc/nghttp2_check_header_name.rst b/doc/nghttp2_check_header_name.rst new file mode 100644 index 0000000..2fba2c5 --- /dev/null +++ b/doc/nghttp2_check_header_name.rst @@ -0,0 +1,17 @@ + +nghttp2_check_header_name +========================= + +Synopsis +-------- + +*#include * + +.. function:: int nghttp2_check_header_name(const uint8_t *name, size_t len) + + + Returns nonzero if HTTP header field name *name* of length *len* is + valid according to http://tools.ietf.org/html/rfc7230#section-3.2 + + Because this is a header field name in HTTP2, the upper cased alphabet + is treated as error. diff --git a/doc/nghttp2_check_header_value.rst b/doc/nghttp2_check_header_value.rst new file mode 100644 index 0000000..e6f792e --- /dev/null +++ b/doc/nghttp2_check_header_value.rst @@ -0,0 +1,15 @@ + +nghttp2_check_header_value +========================== + +Synopsis +-------- + +*#include * + +.. function:: int nghttp2_check_header_value(const uint8_t *value, size_t len) + + + Returns nonzero if HTTP header field value *value* of length *len* + is valid according to + http://tools.ietf.org/html/rfc7230#section-3.2 diff --git a/doc/nghttp2_hd_deflate_bound.rst b/doc/nghttp2_hd_deflate_bound.rst new file mode 100644 index 0000000..27eaed3 --- /dev/null +++ b/doc/nghttp2_hd_deflate_bound.rst @@ -0,0 +1,14 @@ + +nghttp2_hd_deflate_bound +======================== + +Synopsis +-------- + +*#include * + +.. function:: size_t nghttp2_hd_deflate_bound(nghttp2_hd_deflater *deflater, const nghttp2_nv *nva, size_t nvlen) + + + Returns an upper bound on the compressed size after deflation of + *nva* of length *nvlen*. diff --git a/doc/nghttp2_hd_deflate_change_table_size.rst b/doc/nghttp2_hd_deflate_change_table_size.rst new file mode 100644 index 0000000..d0bec79 --- /dev/null +++ b/doc/nghttp2_hd_deflate_change_table_size.rst @@ -0,0 +1,31 @@ + +nghttp2_hd_deflate_change_table_size +==================================== + +Synopsis +-------- + +*#include * + +.. function:: int nghttp2_hd_deflate_change_table_size(nghttp2_hd_deflater *deflater, size_t settings_hd_table_bufsize_max) + + + Changes header table size of the *deflater* to + *settings_hd_table_bufsize_max* bytes. This may trigger eviction + in the dynamic table. + + The *settings_hd_table_bufsize_max* should be the value received in + SETTINGS_HEADER_TABLE_SIZE. + + The deflater never uses more memory than + ``deflate_hd_table_bufsize_max`` bytes specified in + `nghttp2_hd_deflate_new()`. Therefore, if + *settings_hd_table_bufsize_max* > ``deflate_hd_table_bufsize_max``, + resulting maximum table size becomes + ``deflate_hd_table_bufsize_max``. + + This function returns 0 if it succeeds, or one of the following + negative error codes: + + :macro:`NGHTTP2_ERR_NOMEM` + Out of memory. diff --git a/doc/nghttp2_hd_deflate_del.rst b/doc/nghttp2_hd_deflate_del.rst new file mode 100644 index 0000000..ea81a35 --- /dev/null +++ b/doc/nghttp2_hd_deflate_del.rst @@ -0,0 +1,13 @@ + +nghttp2_hd_deflate_del +====================== + +Synopsis +-------- + +*#include * + +.. function:: void nghttp2_hd_deflate_del(nghttp2_hd_deflater *deflater) + + + Deallocates any resources allocated for *deflater*. diff --git a/doc/nghttp2_hd_deflate_hd.rst b/doc/nghttp2_hd_deflate_hd.rst new file mode 100644 index 0000000..4c80bfc --- /dev/null +++ b/doc/nghttp2_hd_deflate_hd.rst @@ -0,0 +1,35 @@ + +nghttp2_hd_deflate_hd +===================== + +Synopsis +-------- + +*#include * + +.. function:: ssize_t nghttp2_hd_deflate_hd(nghttp2_hd_deflater *deflater, uint8_t *buf, size_t buflen, const nghttp2_nv *nva, size_t nvlen) + + + Deflates the *nva*, which has the *nvlen* name/value pairs, into + the *buf* of length *buflen*. + + If *buf* is not large enough to store the deflated header block, + this function fails with :macro:`NGHTTP2_ERR_INSUFF_BUFSIZE`. The + caller should use `nghttp2_hd_deflate_bound()` to know the upper + bound of buffer size required to deflate given header name/value + pairs. + + Once this function fails, subsequent call of this function always + returns :macro:`NGHTTP2_ERR_HEADER_COMP`. + + After this function returns, it is safe to delete the *nva*. + + This function returns 0 if it succeeds, or one of the following + negative error codes: + + :macro:`NGHTTP2_ERR_NOMEM` + Out of memory. + :macro:`NGHTTP2_ERR_HEADER_COMP` + Deflation process has failed. + :macro:`NGHTTP2_ERR_INSUFF_BUFSIZE` + The provided *buflen* size is too small to hold the output. diff --git a/doc/nghttp2_hd_deflate_new.rst b/doc/nghttp2_hd_deflate_new.rst new file mode 100644 index 0000000..146113f --- /dev/null +++ b/doc/nghttp2_hd_deflate_new.rst @@ -0,0 +1,24 @@ + +nghttp2_hd_deflate_new +====================== + +Synopsis +-------- + +*#include * + +.. function:: int nghttp2_hd_deflate_new(nghttp2_hd_deflater **deflater_ptr, size_t deflate_hd_table_bufsize_max) + + + Initializes *\*deflater_ptr* for deflating name/values pairs. + + The *deflate_hd_table_bufsize_max* is the upper bound of header + table size the deflater will use. + + If this function fails, *\*deflater_ptr* is left untouched. + + This function returns 0 if it succeeds, or one of the following + negative error codes: + + :macro:`NGHTTP2_ERR_NOMEM` + Out of memory. diff --git a/doc/nghttp2_hd_deflate_new2.rst b/doc/nghttp2_hd_deflate_new2.rst new file mode 100644 index 0000000..f128f3e --- /dev/null +++ b/doc/nghttp2_hd_deflate_new2.rst @@ -0,0 +1,23 @@ + +nghttp2_hd_deflate_new2 +======================= + +Synopsis +-------- + +*#include * + +.. function:: int nghttp2_hd_deflate_new2(nghttp2_hd_deflater **deflater_ptr, size_t deflate_hd_table_bufsize_max, nghttp2_mem *mem) + + + Like `nghttp2_hd_deflate_new()`, but with additional custom memory + allocator specified in the *mem*. + + The *mem* can be ``NULL`` and the call is equivalent to + `nghttp2_hd_deflate_new()`. + + This function does not take ownership *mem*. The application is + responsible for freeing *mem*. + + The library code does not refer to *mem* pointer after this + function returns, so the application can safely free it. diff --git a/doc/nghttp2_hd_inflate_change_table_size.rst b/doc/nghttp2_hd_inflate_change_table_size.rst new file mode 100644 index 0000000..e859207 --- /dev/null +++ b/doc/nghttp2_hd_inflate_change_table_size.rst @@ -0,0 +1,23 @@ + +nghttp2_hd_inflate_change_table_size +==================================== + +Synopsis +-------- + +*#include * + +.. function:: int nghttp2_hd_inflate_change_table_size(nghttp2_hd_inflater *inflater, size_t settings_hd_table_bufsize_max) + + + Changes header table size in the *inflater*. This may trigger + eviction in the dynamic table. + + The *settings_hd_table_bufsize_max* should be the value transmitted + in SETTINGS_HEADER_TABLE_SIZE. + + This function returns 0 if it succeeds, or one of the following + negative error codes: + + :macro:`NGHTTP2_ERR_NOMEM` + Out of memory. diff --git a/doc/nghttp2_hd_inflate_del.rst b/doc/nghttp2_hd_inflate_del.rst new file mode 100644 index 0000000..bdf51bb --- /dev/null +++ b/doc/nghttp2_hd_inflate_del.rst @@ -0,0 +1,13 @@ + +nghttp2_hd_inflate_del +====================== + +Synopsis +-------- + +*#include * + +.. function:: void nghttp2_hd_inflate_del(nghttp2_hd_inflater *inflater) + + + Deallocates any resources allocated for *inflater*. diff --git a/doc/nghttp2_hd_inflate_end_headers.rst b/doc/nghttp2_hd_inflate_end_headers.rst new file mode 100644 index 0000000..63ea728 --- /dev/null +++ b/doc/nghttp2_hd_inflate_end_headers.rst @@ -0,0 +1,16 @@ + +nghttp2_hd_inflate_end_headers +============================== + +Synopsis +-------- + +*#include * + +.. function:: int nghttp2_hd_inflate_end_headers(nghttp2_hd_inflater *inflater) + + + Signals the end of decompression for one header block. + + This function returns 0 if it succeeds. Currently this function + always succeeds. diff --git a/doc/nghttp2_hd_inflate_hd.rst b/doc/nghttp2_hd_inflate_hd.rst new file mode 100644 index 0000000..216cdfe --- /dev/null +++ b/doc/nghttp2_hd_inflate_hd.rst @@ -0,0 +1,85 @@ + +nghttp2_hd_inflate_hd +===================== + +Synopsis +-------- + +*#include * + +.. function:: ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_inflater *inflater, nghttp2_nv *nv_out, int *inflate_flags, uint8_t *in, size_t inlen, int in_final) + + + Inflates name/value block stored in *in* with length *inlen*. This + function performs decompression. For each successful emission of + header name/value pair, :macro:`NGHTTP2_HD_INFLATE_EMIT` is set in + *\*inflate_flags* and name/value pair is assigned to the *nv_out* + and the function returns. The caller must not free the members of + *nv_out*. + + The *nv_out* may include pointers to the memory region in the *in*. + The caller must retain the *in* while the *nv_out* is used. + + The application should call this function repeatedly until the + ``(*inflate_flags) & NGHTTP2_HD_INFLATE_FINAL`` is nonzero and + return value is non-negative. This means the all input values are + processed successfully. Then the application must call + `nghttp2_hd_inflate_end_headers()` to prepare for the next header + block input. + + The caller can feed complete compressed header block. It also can + feed it in several chunks. The caller must set *in_final* to + nonzero if the given input is the last block of the compressed + header. + + This function returns the number of bytes processed if it succeeds, + or one of the following negative error codes: + + :macro:`NGHTTP2_ERR_NOMEM` + Out of memory. + :macro:`NGHTTP2_ERR_HEADER_COMP` + Inflation process has failed. + :macro:`NGHTTP2_ERR_BUFFER_ERROR` + The heder field name or value is too large. + + Example follows:: + + int inflate_header_block(nghttp2_hd_inflater *hd_inflater, + uint8_t *in, size_t inlen, int final) + { + ssize_t rv; + + for(;;) { + nghttp2_nv nv; + int inflate_flags = 0; + + rv = nghttp2_hd_inflate_hd(hd_inflater, &nv, &inflate_flags, + in, inlen, final); + + if(rv < 0) { + fprintf(stderr, "inflate failed with error code %zd", rv); + return -1; + } + + in += rv; + inlen -= rv; + + if(inflate_flags & NGHTTP2_HD_INFLATE_EMIT) { + fwrite(nv.name, nv.namelen, 1, stderr); + fprintf(stderr, ": "); + fwrite(nv.value, nv.valuelen, 1, stderr); + fprintf(stderr, "\n"); + } + if(inflate_flags & NGHTTP2_HD_INFLATE_FINAL) { + nghttp2_hd_inflate_end_headers(hd_inflater); + break; + } + if((inflate_flags & NGHTTP2_HD_INFLATE_EMIT) == 0 && + inlen == 0) { + break; + } + } + + return 0; + } + diff --git a/doc/nghttp2_hd_inflate_new.rst b/doc/nghttp2_hd_inflate_new.rst new file mode 100644 index 0000000..cfd2212 --- /dev/null +++ b/doc/nghttp2_hd_inflate_new.rst @@ -0,0 +1,21 @@ + +nghttp2_hd_inflate_new +====================== + +Synopsis +-------- + +*#include * + +.. function:: int nghttp2_hd_inflate_new(nghttp2_hd_inflater **inflater_ptr) + + + Initializes *\*inflater_ptr* for inflating name/values pairs. + + If this function fails, *\*inflater_ptr* is left untouched. + + This function returns 0 if it succeeds, or one of the following + negative error codes: + + :macro:`NGHTTP2_ERR_NOMEM` + Out of memory. diff --git a/doc/nghttp2_hd_inflate_new2.rst b/doc/nghttp2_hd_inflate_new2.rst new file mode 100644 index 0000000..f052019 --- /dev/null +++ b/doc/nghttp2_hd_inflate_new2.rst @@ -0,0 +1,23 @@ + +nghttp2_hd_inflate_new2 +======================= + +Synopsis +-------- + +*#include * + +.. function:: int nghttp2_hd_inflate_new2(nghttp2_hd_inflater **inflater_ptr, nghttp2_mem *mem) + + + Like `nghttp2_hd_inflate_new()`, but with additional custom memory + allocator specified in the *mem*. + + The *mem* can be ``NULL`` and the call is equivalent to + `nghttp2_hd_inflate_new()`. + + This function does not take ownership *mem*. The application is + responsible for freeing *mem*. + + The library code does not refer to *mem* pointer after this + function returns, so the application can safely free it. diff --git a/doc/nghttp2_is_fatal.rst b/doc/nghttp2_is_fatal.rst new file mode 100644 index 0000000..b548219 --- /dev/null +++ b/doc/nghttp2_is_fatal.rst @@ -0,0 +1,14 @@ + +nghttp2_is_fatal +================ + +Synopsis +-------- + +*#include * + +.. function:: int nghttp2_is_fatal(int lib_error_code) + + + Returns nonzero if the :type:`nghttp2_error` library error code + *lib_error* is fatal. diff --git a/doc/nghttp2_nv_compare_name.rst b/doc/nghttp2_nv_compare_name.rst new file mode 100644 index 0000000..9fa4c1b --- /dev/null +++ b/doc/nghttp2_nv_compare_name.rst @@ -0,0 +1,17 @@ + +nghttp2_nv_compare_name +======================= + +Synopsis +-------- + +*#include * + +.. function:: int nghttp2_nv_compare_name(const nghttp2_nv *lhs, const nghttp2_nv *rhs) + + + Compares ``lhs->name`` of length ``lhs->namelen`` bytes and + ``rhs->name`` of length ``rhs->namelen`` bytes. Returns negative + integer if ``lhs->name`` is found to be less than ``rhs->name``; or + returns positive integer if ``lhs->name`` is found to be greater + than ``rhs->name``; or returns 0 otherwise. diff --git a/doc/nghttp2_option_del.rst b/doc/nghttp2_option_del.rst new file mode 100644 index 0000000..b816b71 --- /dev/null +++ b/doc/nghttp2_option_del.rst @@ -0,0 +1,14 @@ + +nghttp2_option_del +================== + +Synopsis +-------- + +*#include * + +.. function:: void nghttp2_option_del(nghttp2_option *option) + + + Frees any resources allocated for *option*. If *option* is + ``NULL``, this function does nothing. diff --git a/doc/nghttp2_option_new.rst b/doc/nghttp2_option_new.rst new file mode 100644 index 0000000..4dcfb64 --- /dev/null +++ b/doc/nghttp2_option_new.rst @@ -0,0 +1,22 @@ + +nghttp2_option_new +================== + +Synopsis +-------- + +*#include * + +.. function:: int nghttp2_option_new(nghttp2_option **option_ptr) + + + Initializes *\*option_ptr* with default values. + + When the application finished using this object, it can use + `nghttp2_option_del()` to free its memory. + + This function returns 0 if it succeeds, or one of the following + negative error codes: + + :macro:`NGHTTP2_ERR_NOMEM` + Out of memory. diff --git a/doc/nghttp2_option_set_no_auto_window_update.rst b/doc/nghttp2_option_set_no_auto_window_update.rst new file mode 100644 index 0000000..737b6ce --- /dev/null +++ b/doc/nghttp2_option_set_no_auto_window_update.rst @@ -0,0 +1,18 @@ + +nghttp2_option_set_no_auto_window_update +======================================== + +Synopsis +-------- + +*#include * + +.. function:: void nghttp2_option_set_no_auto_window_update(nghttp2_option *option, int val) + + + This option prevents the library from sending WINDOW_UPDATE for a + connection automatically. If this option is set to nonzero, the + library won't send WINDOW_UPDATE for DATA until application calls + `nghttp2_session_consume()` to indicate the consumed amount of + data. Don't use `nghttp2_submit_window_update()` for this purpose. + By default, this option is set to zero. diff --git a/doc/nghttp2_option_set_no_http_messaging.rst b/doc/nghttp2_option_set_no_http_messaging.rst new file mode 100644 index 0000000..602fe3b --- /dev/null +++ b/doc/nghttp2_option_set_no_http_messaging.rst @@ -0,0 +1,18 @@ + +nghttp2_option_set_no_http_messaging +==================================== + +Synopsis +-------- + +*#include * + +.. function:: void nghttp2_option_set_no_http_messaging(nghttp2_option *option, int val) + + + By default, nghttp2 library enforces subset of HTTP Messaging rules + described in `HTTP/2 specification, section 8 + `_. See + :ref:`http-messaging` section for details. For those applications + who use nghttp2 library as non-HTTP use, give nonzero to *val* to + disable this enforcement. diff --git a/doc/nghttp2_option_set_no_recv_client_magic.rst b/doc/nghttp2_option_set_no_recv_client_magic.rst new file mode 100644 index 0000000..e45fe54 --- /dev/null +++ b/doc/nghttp2_option_set_no_recv_client_magic.rst @@ -0,0 +1,26 @@ + +nghttp2_option_set_no_recv_client_magic +======================================= + +Synopsis +-------- + +*#include * + +.. function:: void nghttp2_option_set_no_recv_client_magic(nghttp2_option *option, int val) + + + By default, nghttp2 library, if configured as server, requires + first 24 bytes of client magic byte string (MAGIC). In most cases, + this will simplify the implementation of server. But sometimes + server may want to detect the application protocol based on first + few bytes on clear text communication. + + If this option is used with nonzero *val*, nghttp2 library does not + handle MAGIC. It still checks following SETTINGS frame. This + means that applications should deal with MAGIC by themselves. + + If this option is not used or used with zero value, if MAGIC does + not match :macro:`NGHTTP2_CLIENT_MAGIC`, `nghttp2_session_recv()` + and `nghttp2_session_mem_recv()` will return error + :macro:`NGHTTP2_ERR_BAD_CLIENT_MAGIC`, which is fatal error. diff --git a/doc/nghttp2_option_set_peer_max_concurrent_streams.rst b/doc/nghttp2_option_set_peer_max_concurrent_streams.rst new file mode 100644 index 0000000..b15fae3 --- /dev/null +++ b/doc/nghttp2_option_set_peer_max_concurrent_streams.rst @@ -0,0 +1,23 @@ + +nghttp2_option_set_peer_max_concurrent_streams +============================================== + +Synopsis +-------- + +*#include * + +.. function:: void nghttp2_option_set_peer_max_concurrent_streams(nghttp2_option *option, uint32_t val) + + + This option sets the SETTINGS_MAX_CONCURRENT_STREAMS value of + remote endpoint as if it is received in SETTINGS frame. Without + specifying this option, before the local endpoint receives + SETTINGS_MAX_CONCURRENT_STREAMS in SETTINGS frame from remote + endpoint, SETTINGS_MAX_CONCURRENT_STREAMS is unlimited. This may + cause problem if local endpoint submits lots of requests initially + and sending them at once to the remote peer may lead to the + rejection of some requests. Specifying this option to the sensible + value, say 100, may avoid this kind of issue. This value will be + overwritten if the local endpoint receives + SETTINGS_MAX_CONCURRENT_STREAMS from the remote endpoint. diff --git a/doc/nghttp2_pack_settings_payload.rst b/doc/nghttp2_pack_settings_payload.rst new file mode 100644 index 0000000..a4ce9fd --- /dev/null +++ b/doc/nghttp2_pack_settings_payload.rst @@ -0,0 +1,29 @@ + +nghttp2_pack_settings_payload +============================= + +Synopsis +-------- + +*#include * + +.. function:: ssize_t nghttp2_pack_settings_payload(uint8_t *buf, size_t buflen, const nghttp2_settings_entry *iv, size_t niv) + + + Serializes the SETTINGS values *iv* in the *buf*. The size of the + *buf* is specified by *buflen*. The number of entries in the *iv* + array is given by *niv*. The required space in *buf* for the *niv* + entries is ``8*niv`` bytes and if the given buffer is too small, an + error is returned. This function is used mainly for creating a + SETTINGS payload to be sent with the ``HTTP2-Settings`` header + field in an HTTP Upgrade request. The data written in *buf* is NOT + base64url encoded and the application is responsible for encoding. + + This function returns the number of bytes written in *buf*, or one + of the following negative error codes: + + :macro:`NGHTTP2_ERR_INVALID_ARGUMENT` + The *iv* contains duplicate settings ID or invalid value. + + :macro:`NGHTTP2_ERR_INSUFF_BUFSIZE` + The provided *buflen* size is too small to hold the output. diff --git a/doc/nghttp2_priority_spec_check_default.rst b/doc/nghttp2_priority_spec_check_default.rst new file mode 100644 index 0000000..12a2765 --- /dev/null +++ b/doc/nghttp2_priority_spec_check_default.rst @@ -0,0 +1,13 @@ + +nghttp2_priority_spec_check_default +=================================== + +Synopsis +-------- + +*#include * + +.. function:: int nghttp2_priority_spec_check_default(const nghttp2_priority_spec *pri_spec) + + + Returns nonzero if the *pri_spec* is filled with default values. diff --git a/doc/nghttp2_priority_spec_default_init.rst b/doc/nghttp2_priority_spec_default_init.rst new file mode 100644 index 0000000..66f03a3 --- /dev/null +++ b/doc/nghttp2_priority_spec_default_init.rst @@ -0,0 +1,15 @@ + +nghttp2_priority_spec_default_init +================================== + +Synopsis +-------- + +*#include * + +.. function:: void nghttp2_priority_spec_default_init(nghttp2_priority_spec *pri_spec) + + + Initializes *pri_spec* with the default values. The default values + are: stream_id = 0, weight = :macro:`NGHTTP2_DEFAULT_WEIGHT` and + exclusive = 0. diff --git a/doc/nghttp2_priority_spec_init.rst b/doc/nghttp2_priority_spec_init.rst new file mode 100644 index 0000000..e6d3757 --- /dev/null +++ b/doc/nghttp2_priority_spec_init.rst @@ -0,0 +1,18 @@ + +nghttp2_priority_spec_init +========================== + +Synopsis +-------- + +*#include * + +.. function:: void nghttp2_priority_spec_init(nghttp2_priority_spec *pri_spec, int32_t stream_id, int32_t weight, int exclusive) + + + Initializes *pri_spec* with the *stream_id* of the stream to depend + on with *weight* and its exclusive flag. If *exclusive* is + nonzero, exclusive flag is set. + + The *weight* must be in [:macro:`NGHTTP2_MIN_WEIGHT`, + :macro:`NGHTTP2_MAX_WEIGHT`], inclusive. diff --git a/doc/nghttp2_select_next_protocol.rst b/doc/nghttp2_select_next_protocol.rst new file mode 100644 index 0000000..5ace810 --- /dev/null +++ b/doc/nghttp2_select_next_protocol.rst @@ -0,0 +1,66 @@ + +nghttp2_select_next_protocol +============================ + +Synopsis +-------- + +*#include * + +.. function:: int nghttp2_select_next_protocol(unsigned char **out, unsigned char *outlen, const unsigned char *in, unsigned int inlen) + + + A helper function for dealing with NPN in client side or ALPN in + server side. The *in* contains peer's protocol list in preferable + order. The format of *in* is length-prefixed and not + null-terminated. For example, ``h2`` and + ``http/1.1`` stored in *in* like this:: + + in[0] = 2 + in[1..2] = "h2" + in[3] = 8 + in[4..11] = "http/1.1" + inlen = 12 + + The selection algorithm is as follows: + + 1. If peer's list contains HTTP/2 protocol the library supports, + it is selected and returns 1. The following step is not taken. + + 2. If peer's list contains ``http/1.1``, this function selects + ``http/1.1`` and returns 0. The following step is not taken. + + 3. This function selects nothing and returns -1 (So called + non-overlap case). In this case, *out* and *outlen* are left + untouched. + + Selecting ``h2`` means that ``h2`` is written into *\*out* and its + length (which is 2) is assigned to *\*outlen*. + + For ALPN, refer to https://tools.ietf.org/html/rfc7301 + + See http://technotes.googlecode.com/git/nextprotoneg.html for more + details about NPN. + + For NPN, to use this method you should do something like:: + + static int select_next_proto_cb(SSL* ssl, + unsigned char **out, + unsigned char *outlen, + const unsigned char *in, + unsigned int inlen, + void *arg) + { + int rv; + rv = nghttp2_select_next_protocol(out, outlen, in, inlen); + if (rv == -1) { + return SSL_TLSEXT_ERR_NOACK; + } + if (rv == 1) { + ((MyType*)arg)->http2_selected = 1; + } + return SSL_TLSEXT_ERR_OK; + } + ... + SSL_CTX_set_next_proto_select_cb(ssl_ctx, select_next_proto_cb, my_obj); + diff --git a/doc/nghttp2_session_callbacks_del.rst b/doc/nghttp2_session_callbacks_del.rst new file mode 100644 index 0000000..2372355 --- /dev/null +++ b/doc/nghttp2_session_callbacks_del.rst @@ -0,0 +1,14 @@ + +nghttp2_session_callbacks_del +============================= + +Synopsis +-------- + +*#include * + +.. function:: void nghttp2_session_callbacks_del(nghttp2_session_callbacks *callbacks) + + + Frees any resources allocated for *callbacks*. If *callbacks* is + ``NULL``, this function does nothing. diff --git a/doc/nghttp2_session_callbacks_new.rst b/doc/nghttp2_session_callbacks_new.rst new file mode 100644 index 0000000..809db59 --- /dev/null +++ b/doc/nghttp2_session_callbacks_new.rst @@ -0,0 +1,25 @@ + +nghttp2_session_callbacks_new +============================= + +Synopsis +-------- + +*#include * + +.. function:: int nghttp2_session_callbacks_new(nghttp2_session_callbacks **callbacks_ptr) + + + Initializes *\*callbacks_ptr* with NULL values. + + The initialized object can be used when initializing multiple + :type:`nghttp2_session` objects. + + When the application finished using this object, it can use + `nghttp2_session_callbacks_del()` to free its memory. + + This function returns 0 if it succeeds, or one of the following + negative error codes: + + :macro:`NGHTTP2_ERR_NOMEM` + Out of memory. diff --git a/doc/nghttp2_session_callbacks_set_before_frame_send_callback.rst b/doc/nghttp2_session_callbacks_set_before_frame_send_callback.rst new file mode 100644 index 0000000..0f52c10 --- /dev/null +++ b/doc/nghttp2_session_callbacks_set_before_frame_send_callback.rst @@ -0,0 +1,13 @@ + +nghttp2_session_callbacks_set_before_frame_send_callback +======================================================== + +Synopsis +-------- + +*#include * + +.. function:: void nghttp2_session_callbacks_set_before_frame_send_callback( nghttp2_session_callbacks *cbs, nghttp2_before_frame_send_callback before_frame_send_callback) + + + Sets callback function invoked before a non-DATA frame is sent. diff --git a/doc/nghttp2_session_callbacks_set_data_source_read_length_callback.rst b/doc/nghttp2_session_callbacks_set_data_source_read_length_callback.rst new file mode 100644 index 0000000..e2819fa --- /dev/null +++ b/doc/nghttp2_session_callbacks_set_data_source_read_length_callback.rst @@ -0,0 +1,14 @@ + +nghttp2_session_callbacks_set_data_source_read_length_callback +============================================================== + +Synopsis +-------- + +*#include * + +.. function:: void nghttp2_session_callbacks_set_data_source_read_length_callback( nghttp2_session_callbacks *cbs, nghttp2_data_source_read_length_callback data_source_read_length_callback) + + + Sets callback function determine the length allowed in + :type:`nghttp2_data_source_read_callback`. diff --git a/doc/nghttp2_session_callbacks_set_on_begin_frame_callback.rst b/doc/nghttp2_session_callbacks_set_on_begin_frame_callback.rst new file mode 100644 index 0000000..95f0b72 --- /dev/null +++ b/doc/nghttp2_session_callbacks_set_on_begin_frame_callback.rst @@ -0,0 +1,13 @@ + +nghttp2_session_callbacks_set_on_begin_frame_callback +===================================================== + +Synopsis +-------- + +*#include * + +.. function:: void nghttp2_session_callbacks_set_on_begin_frame_callback( nghttp2_session_callbacks *cbs, nghttp2_on_begin_frame_callback on_begin_frame_callback) + + + Sets callback function invoked when a frame header is received. diff --git a/doc/nghttp2_session_callbacks_set_on_begin_headers_callback.rst b/doc/nghttp2_session_callbacks_set_on_begin_headers_callback.rst new file mode 100644 index 0000000..970b1fd --- /dev/null +++ b/doc/nghttp2_session_callbacks_set_on_begin_headers_callback.rst @@ -0,0 +1,14 @@ + +nghttp2_session_callbacks_set_on_begin_headers_callback +======================================================= + +Synopsis +-------- + +*#include * + +.. function:: void nghttp2_session_callbacks_set_on_begin_headers_callback( nghttp2_session_callbacks *cbs, nghttp2_on_begin_headers_callback on_begin_headers_callback) + + + Sets callback function invoked when the reception of header block + in HEADERS or PUSH_PROMISE is started. diff --git a/doc/nghttp2_session_callbacks_set_on_data_chunk_recv_callback.rst b/doc/nghttp2_session_callbacks_set_on_data_chunk_recv_callback.rst new file mode 100644 index 0000000..04fa3f6 --- /dev/null +++ b/doc/nghttp2_session_callbacks_set_on_data_chunk_recv_callback.rst @@ -0,0 +1,14 @@ + +nghttp2_session_callbacks_set_on_data_chunk_recv_callback +========================================================= + +Synopsis +-------- + +*#include * + +.. function:: void nghttp2_session_callbacks_set_on_data_chunk_recv_callback( nghttp2_session_callbacks *cbs, nghttp2_on_data_chunk_recv_callback on_data_chunk_recv_callback) + + + Sets callback function invoked when a chunk of data in DATA frame + is received. diff --git a/doc/nghttp2_session_callbacks_set_on_frame_not_send_callback.rst b/doc/nghttp2_session_callbacks_set_on_frame_not_send_callback.rst new file mode 100644 index 0000000..73e9fb1 --- /dev/null +++ b/doc/nghttp2_session_callbacks_set_on_frame_not_send_callback.rst @@ -0,0 +1,14 @@ + +nghttp2_session_callbacks_set_on_frame_not_send_callback +======================================================== + +Synopsis +-------- + +*#include * + +.. function:: void nghttp2_session_callbacks_set_on_frame_not_send_callback( nghttp2_session_callbacks *cbs, nghttp2_on_frame_not_send_callback on_frame_not_send_callback) + + + Sets callback function invoked when a non-DATA frame is not sent + because of an error. diff --git a/doc/nghttp2_session_callbacks_set_on_frame_recv_callback.rst b/doc/nghttp2_session_callbacks_set_on_frame_recv_callback.rst new file mode 100644 index 0000000..736ae53 --- /dev/null +++ b/doc/nghttp2_session_callbacks_set_on_frame_recv_callback.rst @@ -0,0 +1,14 @@ + +nghttp2_session_callbacks_set_on_frame_recv_callback +==================================================== + +Synopsis +-------- + +*#include * + +.. function:: void nghttp2_session_callbacks_set_on_frame_recv_callback( nghttp2_session_callbacks *cbs, nghttp2_on_frame_recv_callback on_frame_recv_callback) + + + Sets callback function invoked by `nghttp2_session_recv()` and + `nghttp2_session_mem_recv()` when a frame is received. diff --git a/doc/nghttp2_session_callbacks_set_on_frame_send_callback.rst b/doc/nghttp2_session_callbacks_set_on_frame_send_callback.rst new file mode 100644 index 0000000..7a02ee5 --- /dev/null +++ b/doc/nghttp2_session_callbacks_set_on_frame_send_callback.rst @@ -0,0 +1,13 @@ + +nghttp2_session_callbacks_set_on_frame_send_callback +==================================================== + +Synopsis +-------- + +*#include * + +.. function:: void nghttp2_session_callbacks_set_on_frame_send_callback( nghttp2_session_callbacks *cbs, nghttp2_on_frame_send_callback on_frame_send_callback) + + + Sets callback function invoked after a frame is sent. diff --git a/doc/nghttp2_session_callbacks_set_on_header_callback.rst b/doc/nghttp2_session_callbacks_set_on_header_callback.rst new file mode 100644 index 0000000..c843aeb --- /dev/null +++ b/doc/nghttp2_session_callbacks_set_on_header_callback.rst @@ -0,0 +1,14 @@ + +nghttp2_session_callbacks_set_on_header_callback +================================================ + +Synopsis +-------- + +*#include * + +.. function:: void nghttp2_session_callbacks_set_on_header_callback( nghttp2_session_callbacks *cbs, nghttp2_on_header_callback on_header_callback) + + + Sets callback function invoked when a header name/value pair is + received. diff --git a/doc/nghttp2_session_callbacks_set_on_invalid_frame_recv_callback.rst b/doc/nghttp2_session_callbacks_set_on_invalid_frame_recv_callback.rst new file mode 100644 index 0000000..97e4851 --- /dev/null +++ b/doc/nghttp2_session_callbacks_set_on_invalid_frame_recv_callback.rst @@ -0,0 +1,15 @@ + +nghttp2_session_callbacks_set_on_invalid_frame_recv_callback +============================================================ + +Synopsis +-------- + +*#include * + +.. function:: void nghttp2_session_callbacks_set_on_invalid_frame_recv_callback( nghttp2_session_callbacks *cbs, nghttp2_on_invalid_frame_recv_callback on_invalid_frame_recv_callback) + + + Sets callback function invoked by `nghttp2_session_recv()` and + `nghttp2_session_mem_recv()` when an invalid non-DATA frame is + received. diff --git a/doc/nghttp2_session_callbacks_set_on_stream_close_callback.rst b/doc/nghttp2_session_callbacks_set_on_stream_close_callback.rst new file mode 100644 index 0000000..9349a1c --- /dev/null +++ b/doc/nghttp2_session_callbacks_set_on_stream_close_callback.rst @@ -0,0 +1,13 @@ + +nghttp2_session_callbacks_set_on_stream_close_callback +====================================================== + +Synopsis +-------- + +*#include * + +.. function:: void nghttp2_session_callbacks_set_on_stream_close_callback( nghttp2_session_callbacks *cbs, nghttp2_on_stream_close_callback on_stream_close_callback) + + + Sets callback function invoked when the stream is closed. diff --git a/doc/nghttp2_session_callbacks_set_recv_callback.rst b/doc/nghttp2_session_callbacks_set_recv_callback.rst new file mode 100644 index 0000000..89b6788 --- /dev/null +++ b/doc/nghttp2_session_callbacks_set_recv_callback.rst @@ -0,0 +1,16 @@ + +nghttp2_session_callbacks_set_recv_callback +=========================================== + +Synopsis +-------- + +*#include * + +.. function:: void nghttp2_session_callbacks_set_recv_callback( nghttp2_session_callbacks *cbs, nghttp2_recv_callback recv_callback) + + + Sets callback function invoked when the a session wants to receive + data from the remote peer. This callback is not necessary if the + application uses solely `nghttp2_session_mem_recv()` to process + received data. diff --git a/doc/nghttp2_session_callbacks_set_select_padding_callback.rst b/doc/nghttp2_session_callbacks_set_select_padding_callback.rst new file mode 100644 index 0000000..17837c2 --- /dev/null +++ b/doc/nghttp2_session_callbacks_set_select_padding_callback.rst @@ -0,0 +1,15 @@ + +nghttp2_session_callbacks_set_select_padding_callback +===================================================== + +Synopsis +-------- + +*#include * + +.. function:: void nghttp2_session_callbacks_set_select_padding_callback( nghttp2_session_callbacks *cbs, nghttp2_select_padding_callback select_padding_callback) + + + Sets callback function invoked when the library asks application + how many padding bytes are required for the transmission of the + given frame. diff --git a/doc/nghttp2_session_callbacks_set_send_callback.rst b/doc/nghttp2_session_callbacks_set_send_callback.rst new file mode 100644 index 0000000..bd7d2b9 --- /dev/null +++ b/doc/nghttp2_session_callbacks_set_send_callback.rst @@ -0,0 +1,16 @@ + +nghttp2_session_callbacks_set_send_callback +=========================================== + +Synopsis +-------- + +*#include * + +.. function:: void nghttp2_session_callbacks_set_send_callback( nghttp2_session_callbacks *cbs, nghttp2_send_callback send_callback) + + + Sets callback function invoked when a session wants to send data to + the remote peer. This callback is not necessary if the application + uses solely `nghttp2_session_mem_send()` to serialize data to + transmit. diff --git a/doc/nghttp2_session_callbacks_set_send_data_callback.rst b/doc/nghttp2_session_callbacks_set_send_data_callback.rst new file mode 100644 index 0000000..5678cfc --- /dev/null +++ b/doc/nghttp2_session_callbacks_set_send_data_callback.rst @@ -0,0 +1,15 @@ + +nghttp2_session_callbacks_set_send_data_callback +================================================ + +Synopsis +-------- + +*#include * + +.. function:: void nghttp2_session_callbacks_set_send_data_callback( nghttp2_session_callbacks *cbs, nghttp2_send_data_callback send_data_callback) + + + Sets callback function invoked when + :macro:`NGHTTP2_DATA_FLAG_NO_COPY` is used in + :type:`nghttp2_data_source_read_callback` to avoid data copy. diff --git a/doc/nghttp2_session_client_new.rst b/doc/nghttp2_session_client_new.rst new file mode 100644 index 0000000..6f0c261 --- /dev/null +++ b/doc/nghttp2_session_client_new.rst @@ -0,0 +1,29 @@ + +nghttp2_session_client_new +========================== + +Synopsis +-------- + +*#include * + +.. function:: int nghttp2_session_client_new(nghttp2_session **session_ptr, const nghttp2_session_callbacks *callbacks, void *user_data) + + + Initializes *\*session_ptr* for client use. The all members of + *callbacks* are copied to *\*session_ptr*. Therefore *\*session_ptr* + does not store *callbacks*. The *user_data* is an arbitrary user + supplied data, which will be passed to the callback functions. + + The :type:`nghttp2_send_callback` must be specified. If the + application code uses `nghttp2_session_recv()`, the + :type:`nghttp2_recv_callback` must be specified. The other members + of *callbacks* can be ``NULL``. + + If this function fails, *\*session_ptr* is left untouched. + + This function returns 0 if it succeeds, or one of the following + negative error codes: + + :macro:`NGHTTP2_ERR_NOMEM` + Out of memory. diff --git a/doc/nghttp2_session_client_new2.rst b/doc/nghttp2_session_client_new2.rst new file mode 100644 index 0000000..863d429 --- /dev/null +++ b/doc/nghttp2_session_client_new2.rst @@ -0,0 +1,29 @@ + +nghttp2_session_client_new2 +=========================== + +Synopsis +-------- + +*#include * + +.. function:: int nghttp2_session_client_new2(nghttp2_session **session_ptr, const nghttp2_session_callbacks *callbacks, void *user_data, const nghttp2_option *option) + + + Like `nghttp2_session_client_new()`, but with additional options + specified in the *option*. + + The *option* can be ``NULL`` and the call is equivalent to + `nghttp2_session_client_new()`. + + This function does not take ownership *option*. The application is + responsible for freeing *option* if it finishes using the object. + + The library code does not refer to *option* after this function + returns. + + This function returns 0 if it succeeds, or one of the following + negative error codes: + + :macro:`NGHTTP2_ERR_NOMEM` + Out of memory. diff --git a/doc/nghttp2_session_client_new3.rst b/doc/nghttp2_session_client_new3.rst new file mode 100644 index 0000000..22bef5a --- /dev/null +++ b/doc/nghttp2_session_client_new3.rst @@ -0,0 +1,29 @@ + +nghttp2_session_client_new3 +=========================== + +Synopsis +-------- + +*#include * + +.. function:: int nghttp2_session_client_new3( nghttp2_session **session_ptr, const nghttp2_session_callbacks *callbacks, void *user_data, const nghttp2_option *option, nghttp2_mem *mem) + + + Like `nghttp2_session_client_new2()`, but with additional custom + memory allocator specified in the *mem*. + + The *mem* can be ``NULL`` and the call is equivalent to + `nghttp2_session_client_new2()`. + + This function does not take ownership *mem*. The application is + responsible for freeing *mem*. + + The library code does not refer to *mem* pointer after this + function returns, so the application can safely free it. + + This function returns 0 if it succeeds, or one of the following + negative error codes: + + :macro:`NGHTTP2_ERR_NOMEM` + Out of memory. diff --git a/doc/nghttp2_session_consume.rst b/doc/nghttp2_session_consume.rst new file mode 100644 index 0000000..c4d08f2 --- /dev/null +++ b/doc/nghttp2_session_consume.rst @@ -0,0 +1,31 @@ + +nghttp2_session_consume +======================= + +Synopsis +-------- + +*#include * + +.. function:: int nghttp2_session_consume(nghttp2_session *session, int32_t stream_id, size_t size) + + + Tells the *session* that *size* bytes for a stream denoted by + *stream_id* were consumed by application and are ready to + WINDOW_UPDATE. The consumed bytes are counted towards both + connection and stream level WINDOW_UPDATE (see + `nghttp2_session_consume_connection()` and + `nghttp2_session_consume_stream()` to update consumption + independently). This function is intended to be used without + automatic window update (see + `nghttp2_option_set_no_auto_window_update()`). + + This function returns 0 if it succeeds, or one of the following + negative error codes: + + :macro:`NGHTTP2_ERR_NOMEM` + Out of memory. + :macro:`NGHTTP2_ERR_INVALID_ARGUMENT` + The *stream_id* is 0. + :macro:`NGHTTP2_ERR_INVALID_STATE` + Automatic WINDOW_UPDATE is not disabled. diff --git a/doc/nghttp2_session_consume_connection.rst b/doc/nghttp2_session_consume_connection.rst new file mode 100644 index 0000000..c263cd6 --- /dev/null +++ b/doc/nghttp2_session_consume_connection.rst @@ -0,0 +1,24 @@ + +nghttp2_session_consume_connection +================================== + +Synopsis +-------- + +*#include * + +.. function:: int nghttp2_session_consume_connection(nghttp2_session *session, size_t size) + + + Like `nghttp2_session_consume()`, but this only tells library that + *size* bytes were consumed only for connection level. Note that + HTTP/2 maintains connection and stream level flow control windows + independently. + + This function returns 0 if it succeeds, or one of the following + negative error codes: + + :macro:`NGHTTP2_ERR_NOMEM` + Out of memory. + :macro:`NGHTTP2_ERR_INVALID_STATE` + Automatic WINDOW_UPDATE is not disabled. diff --git a/doc/nghttp2_session_consume_stream.rst b/doc/nghttp2_session_consume_stream.rst new file mode 100644 index 0000000..694ee50 --- /dev/null +++ b/doc/nghttp2_session_consume_stream.rst @@ -0,0 +1,26 @@ + +nghttp2_session_consume_stream +============================== + +Synopsis +-------- + +*#include * + +.. function:: int nghttp2_session_consume_stream(nghttp2_session *session, int32_t stream_id, size_t size) + + + Like `nghttp2_session_consume()`, but this only tells library that + *size* bytes were consumed only for stream denoted by *stream_id*. + Note that HTTP/2 maintains connection and stream level flow control + windows independently. + + This function returns 0 if it succeeds, or one of the following + negative error codes: + + :macro:`NGHTTP2_ERR_NOMEM` + Out of memory. + :macro:`NGHTTP2_ERR_INVALID_ARGUMENT` + The *stream_id* is 0. + :macro:`NGHTTP2_ERR_INVALID_STATE` + Automatic WINDOW_UPDATE is not disabled. diff --git a/doc/nghttp2_session_del.rst b/doc/nghttp2_session_del.rst new file mode 100644 index 0000000..1c00d7c --- /dev/null +++ b/doc/nghttp2_session_del.rst @@ -0,0 +1,14 @@ + +nghttp2_session_del +=================== + +Synopsis +-------- + +*#include * + +.. function:: void nghttp2_session_del(nghttp2_session *session) + + + Frees any resources allocated for *session*. If *session* is + ``NULL``, this function does nothing. diff --git a/doc/nghttp2_session_get_effective_local_window_size.rst b/doc/nghttp2_session_get_effective_local_window_size.rst new file mode 100644 index 0000000..60efcba --- /dev/null +++ b/doc/nghttp2_session_get_effective_local_window_size.rst @@ -0,0 +1,18 @@ + +nghttp2_session_get_effective_local_window_size +=============================================== + +Synopsis +-------- + +*#include * + +.. function:: int32_t nghttp2_session_get_effective_local_window_size(nghttp2_session *session) + + + Returns the local (receive) window size for a connection. The + local window size can be adjusted by + `nghttp2_submit_window_update()`. This function takes into account + that and returns effective window size. + + This function returns -1 if it fails. diff --git a/doc/nghttp2_session_get_effective_recv_data_length.rst b/doc/nghttp2_session_get_effective_recv_data_length.rst new file mode 100644 index 0000000..be4e97a --- /dev/null +++ b/doc/nghttp2_session_get_effective_recv_data_length.rst @@ -0,0 +1,22 @@ + +nghttp2_session_get_effective_recv_data_length +============================================== + +Synopsis +-------- + +*#include * + +.. function:: int32_t nghttp2_session_get_effective_recv_data_length(nghttp2_session *session) + + + Returns the number of DATA payload in bytes received without + WINDOW_UPDATE transmission for a connection. The local (receive) + window size can be adjusted by `nghttp2_submit_window_update()`. + This function takes into account that and returns effective data + length. In particular, if the local window size is reduced by + submitting negative window_size_increment with + `nghttp2_submit_window_update()`, this function returns the number + of bytes less than actually received. + + This function returns -1 if it fails. diff --git a/doc/nghttp2_session_get_last_proc_stream_id.rst b/doc/nghttp2_session_get_last_proc_stream_id.rst new file mode 100644 index 0000000..726fffd --- /dev/null +++ b/doc/nghttp2_session_get_last_proc_stream_id.rst @@ -0,0 +1,19 @@ + +nghttp2_session_get_last_proc_stream_id +======================================= + +Synopsis +-------- + +*#include * + +.. function:: int32_t nghttp2_session_get_last_proc_stream_id(nghttp2_session *session) + + + Returns the last stream ID of a stream for which + :type:`nghttp2_on_frame_recv_callback` was invoked most recently. + The returned value can be used as last_stream_id parameter for + `nghttp2_submit_goaway()` and + `nghttp2_session_terminate_session2()`. + + This function always succeeds. diff --git a/doc/nghttp2_session_get_next_stream_id.rst b/doc/nghttp2_session_get_next_stream_id.rst new file mode 100644 index 0000000..a68401c --- /dev/null +++ b/doc/nghttp2_session_get_next_stream_id.rst @@ -0,0 +1,15 @@ + +nghttp2_session_get_next_stream_id +================================== + +Synopsis +-------- + +*#include * + +.. function:: uint32_t nghttp2_session_get_next_stream_id(nghttp2_session *session) + + + Returns the next outgoing stream ID. Notice that return type is + uint32_t. If we run out of stream ID for this session, this + function returns 1 << 31. diff --git a/doc/nghttp2_session_get_outbound_queue_size.rst b/doc/nghttp2_session_get_outbound_queue_size.rst new file mode 100644 index 0000000..8816ee7 --- /dev/null +++ b/doc/nghttp2_session_get_outbound_queue_size.rst @@ -0,0 +1,14 @@ + +nghttp2_session_get_outbound_queue_size +======================================= + +Synopsis +-------- + +*#include * + +.. function:: size_t nghttp2_session_get_outbound_queue_size(nghttp2_session *session) + + + Returns the number of frames in the outbound queue. This does not + include the deferred DATA frames. diff --git a/doc/nghttp2_session_get_remote_settings.rst b/doc/nghttp2_session_get_remote_settings.rst new file mode 100644 index 0000000..6e9ad91 --- /dev/null +++ b/doc/nghttp2_session_get_remote_settings.rst @@ -0,0 +1,15 @@ + +nghttp2_session_get_remote_settings +=================================== + +Synopsis +-------- + +*#include * + +.. function:: uint32_t nghttp2_session_get_remote_settings(nghttp2_session *session, nghttp2_settings_id id) + + + Returns the value of SETTINGS *id* notified by a remote endpoint. + The *id* must be one of values defined in + :macro:`nghttp2_settings_id`. diff --git a/doc/nghttp2_session_get_remote_window_size.rst b/doc/nghttp2_session_get_remote_window_size.rst new file mode 100644 index 0000000..cd810c2 --- /dev/null +++ b/doc/nghttp2_session_get_remote_window_size.rst @@ -0,0 +1,15 @@ + +nghttp2_session_get_remote_window_size +====================================== + +Synopsis +-------- + +*#include * + +.. function:: int32_t nghttp2_session_get_remote_window_size(nghttp2_session *session) + + + Returns the remote window size for a connection. + + This function always succeeds. diff --git a/doc/nghttp2_session_get_stream_effective_local_window_size.rst b/doc/nghttp2_session_get_stream_effective_local_window_size.rst new file mode 100644 index 0000000..387f7a6 --- /dev/null +++ b/doc/nghttp2_session_get_stream_effective_local_window_size.rst @@ -0,0 +1,18 @@ + +nghttp2_session_get_stream_effective_local_window_size +====================================================== + +Synopsis +-------- + +*#include * + +.. function:: int32_t nghttp2_session_get_stream_effective_local_window_size( nghttp2_session *session, int32_t stream_id) + + + Returns the local (receive) window size for the stream *stream_id*. + The local window size can be adjusted by + `nghttp2_submit_window_update()`. This function takes into account + that and returns effective window size. + + This function returns -1 if it fails. diff --git a/doc/nghttp2_session_get_stream_effective_recv_data_length.rst b/doc/nghttp2_session_get_stream_effective_recv_data_length.rst new file mode 100644 index 0000000..8e01b2f --- /dev/null +++ b/doc/nghttp2_session_get_stream_effective_recv_data_length.rst @@ -0,0 +1,22 @@ + +nghttp2_session_get_stream_effective_recv_data_length +===================================================== + +Synopsis +-------- + +*#include * + +.. function:: int32_t nghttp2_session_get_stream_effective_recv_data_length( nghttp2_session *session, int32_t stream_id) + + + Returns the number of DATA payload in bytes received without + WINDOW_UPDATE transmission for the stream *stream_id*. The local + (receive) window size can be adjusted by + `nghttp2_submit_window_update()`. This function takes into account + that and returns effective data length. In particular, if the + local window size is reduced by submitting negative + window_size_increment with `nghttp2_submit_window_update()`, this + function returns the number of bytes less than actually received. + + This function returns -1 if it fails. diff --git a/doc/nghttp2_session_get_stream_local_close.rst b/doc/nghttp2_session_get_stream_local_close.rst new file mode 100644 index 0000000..cc01d8d --- /dev/null +++ b/doc/nghttp2_session_get_stream_local_close.rst @@ -0,0 +1,14 @@ + +nghttp2_session_get_stream_local_close +====================================== + +Synopsis +-------- + +*#include * + +.. function:: int nghttp2_session_get_stream_local_close(nghttp2_session *session, int32_t stream_id) + + + Returns 1 if local peer half closed the given stream *stream_id*. + Returns 0 if it did not. Returns -1 if no such stream exists. diff --git a/doc/nghttp2_session_get_stream_remote_close.rst b/doc/nghttp2_session_get_stream_remote_close.rst new file mode 100644 index 0000000..a0a540c --- /dev/null +++ b/doc/nghttp2_session_get_stream_remote_close.rst @@ -0,0 +1,14 @@ + +nghttp2_session_get_stream_remote_close +======================================= + +Synopsis +-------- + +*#include * + +.. function:: int nghttp2_session_get_stream_remote_close(nghttp2_session *session, int32_t stream_id) + + + Returns 1 if remote peer half closed the given stream *stream_id*. + Returns 0 if it did not. Returns -1 if no such stream exists. diff --git a/doc/nghttp2_session_get_stream_remote_window_size.rst b/doc/nghttp2_session_get_stream_remote_window_size.rst new file mode 100644 index 0000000..5404162 --- /dev/null +++ b/doc/nghttp2_session_get_stream_remote_window_size.rst @@ -0,0 +1,22 @@ + +nghttp2_session_get_stream_remote_window_size +============================================= + +Synopsis +-------- + +*#include * + +.. function:: int32_t nghttp2_session_get_stream_remote_window_size(nghttp2_session *session, int32_t stream_id) + + + Returns the remote window size for a given stream *stream_id*. + + This is the amount of flow-controlled payload (e.g., DATA) that the + local endpoint can send without stream level WINDOW_UPDATE. There + is also connection level flow control, so the effective size of + payload that the local endpoint can actually send is + min(`nghttp2_session_get_stream_remote_window_size()`, + `nghttp2_session_get_remote_window_size()`). + + This function returns -1 if it fails. diff --git a/doc/nghttp2_session_get_stream_user_data.rst b/doc/nghttp2_session_get_stream_user_data.rst new file mode 100644 index 0000000..94f18c1 --- /dev/null +++ b/doc/nghttp2_session_get_stream_user_data.rst @@ -0,0 +1,20 @@ + +nghttp2_session_get_stream_user_data +==================================== + +Synopsis +-------- + +*#include * + +.. function:: void * nghttp2_session_get_stream_user_data(nghttp2_session *session, int32_t stream_id) + + + Returns stream_user_data for the stream *stream_id*. The + stream_user_data is provided by `nghttp2_submit_request()`, + `nghttp2_submit_headers()` or + `nghttp2_session_set_stream_user_data()`. Unless it is set using + `nghttp2_session_set_stream_user_data()`, if the stream is + initiated by the remote endpoint, stream_user_data is always + ``NULL``. If the stream does not exist, this function returns + ``NULL``. diff --git a/doc/nghttp2_session_mem_recv.rst b/doc/nghttp2_session_mem_recv.rst new file mode 100644 index 0000000..994db42 --- /dev/null +++ b/doc/nghttp2_session_mem_recv.rst @@ -0,0 +1,42 @@ + +nghttp2_session_mem_recv +======================== + +Synopsis +-------- + +*#include * + +.. function:: ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in, size_t inlen) + + + Processes data *in* as an input from the remote endpoint. The + *inlen* indicates the number of bytes in the *in*. + + This function behaves like `nghttp2_session_recv()` except that it + does not use :type:`nghttp2_recv_callback` to receive data; the + *in* is the only data for the invocation of this function. If all + bytes are processed, this function returns. The other callbacks + are called in the same way as they are in `nghttp2_session_recv()`. + + In the current implementation, this function always tries to + processes all input data unless either an error occurs or + :macro:`NGHTTP2_ERR_PAUSE` is returned from + :type:`nghttp2_on_header_callback` or + :type:`nghttp2_on_data_chunk_recv_callback`. If + :macro:`NGHTTP2_ERR_PAUSE` is used, the return value includes the + number of bytes which was used to produce the data or frame for the + callback. + + This function returns the number of processed bytes, or one of the + following negative error codes: + + :macro:`NGHTTP2_ERR_NOMEM` + Out of memory. + :macro:`NGHTTP2_ERR_CALLBACK_FAILURE` + The callback function failed. + :macro:`NGHTTP2_ERR_BAD_CLIENT_MAGIC` + Invalid client magic was detected. This error only returns + when *session* was configured as server and + `nghttp2_option_set_no_recv_client_magic()` is not used with + nonzero value. diff --git a/doc/nghttp2_session_mem_send.rst b/doc/nghttp2_session_mem_send.rst new file mode 100644 index 0000000..979b637 --- /dev/null +++ b/doc/nghttp2_session_mem_send.rst @@ -0,0 +1,38 @@ + +nghttp2_session_mem_send +======================== + +Synopsis +-------- + +*#include * + +.. function:: ssize_t nghttp2_session_mem_send(nghttp2_session *session, const uint8_t **data_ptr) + + + Returns the serialized data to send. + + This function behaves like `nghttp2_session_send()` except that it + does not use :type:`nghttp2_send_callback` to transmit data. + Instead, it assigns the pointer to the serialized data to the + *\*data_ptr* and returns its length. The other callbacks are called + in the same way as they are in `nghttp2_session_send()`. + + If no data is available to send, this function returns 0. + + This function may not return all serialized data in one invocation. + To get all data, call this function repeatedly until it returns 0 + or one of negative error codes. + + The assigned *\*data_ptr* is valid until the next call of + `nghttp2_session_mem_send()` or `nghttp2_session_send()`. + + The caller must send all data before sending the next chunk of + data. + + This function returns the length of the data pointed by the + *\*data_ptr* if it succeeds, or one of the following negative error + codes: + + :macro:`NGHTTP2_ERR_NOMEM` + Out of memory. diff --git a/doc/nghttp2_session_recv.rst b/doc/nghttp2_session_recv.rst new file mode 100644 index 0000000..61da799 --- /dev/null +++ b/doc/nghttp2_session_recv.rst @@ -0,0 +1,72 @@ + +nghttp2_session_recv +==================== + +Synopsis +-------- + +*#include * + +.. function:: int nghttp2_session_recv(nghttp2_session *session) + + + Receives frames from the remote peer. + + This function receives as many frames as possible until the user + callback :type:`nghttp2_recv_callback` returns + :macro:`NGHTTP2_ERR_WOULDBLOCK`. This function calls several + callback functions which are passed when initializing the + *session*. Here is the simple time chart which tells when each + callback is invoked: + + 1. :type:`nghttp2_recv_callback` is invoked one or more times to + receive frame header. + + 2. When frame header is received, + :type:`nghttp2_on_begin_frame_callback` is invoked. + + 3. If the frame is DATA frame: + + 1. :type:`nghttp2_recv_callback` is invoked to receive DATA + payload. For each chunk of data, + :type:`nghttp2_on_data_chunk_recv_callback` is invoked. + + 2. If one DATA frame is completely received, + :type:`nghttp2_on_frame_recv_callback` is invoked. If the + reception of the frame triggers the closure of the stream, + :type:`nghttp2_on_stream_close_callback` is invoked. + + 4. If the frame is the control frame: + + 1. :type:`nghttp2_recv_callback` is invoked one or more times to + receive whole frame. + + 2. If the received frame is valid, then following actions are + taken. If the frame is either HEADERS or PUSH_PROMISE, + :type:`nghttp2_on_begin_headers_callback` is invoked. Then + :type:`nghttp2_on_header_callback` is invoked for each header + name/value pair. After all name/value pairs are emitted + successfully, :type:`nghttp2_on_frame_recv_callback` is + invoked. For other frames, + :type:`nghttp2_on_frame_recv_callback` is invoked. If the + reception of the frame triggers the closure of the stream, + :type:`nghttp2_on_stream_close_callback` is invoked. + + 3. If the received frame is unpacked but is interpreted as + invalid, :type:`nghttp2_on_invalid_frame_recv_callback` is + invoked. + + This function returns 0 if it succeeds, or one of the following + negative error codes: + + :macro:`NGHTTP2_ERR_EOF` + The remote peer did shutdown on the connection. + :macro:`NGHTTP2_ERR_NOMEM` + Out of memory. + :macro:`NGHTTP2_ERR_CALLBACK_FAILURE` + The callback function failed. + :macro:`NGHTTP2_ERR_BAD_CLIENT_MAGIC` + Invalid client magic was detected. This error only returns + when *session* was configured as server and + `nghttp2_option_set_no_recv_client_magic()` is not used with + nonzero value. diff --git a/doc/nghttp2_session_resume_data.rst b/doc/nghttp2_session_resume_data.rst new file mode 100644 index 0000000..daa9b8f --- /dev/null +++ b/doc/nghttp2_session_resume_data.rst @@ -0,0 +1,22 @@ + +nghttp2_session_resume_data +=========================== + +Synopsis +-------- + +*#include * + +.. function:: int nghttp2_session_resume_data(nghttp2_session *session, int32_t stream_id) + + + Puts back previously deferred DATA frame in the stream *stream_id* + to the outbound queue. + + This function returns 0 if it succeeds, or one of the following + negative error codes: + + :macro:`NGHTTP2_ERR_INVALID_ARGUMENT` + The stream does not exist; or no deferred data exist. + :macro:`NGHTTP2_ERR_NOMEM` + Out of memory. diff --git a/doc/nghttp2_session_send.rst b/doc/nghttp2_session_send.rst new file mode 100644 index 0000000..1f40bab --- /dev/null +++ b/doc/nghttp2_session_send.rst @@ -0,0 +1,55 @@ + +nghttp2_session_send +==================== + +Synopsis +-------- + +*#include * + +.. function:: int nghttp2_session_send(nghttp2_session *session) + + + Sends pending frames to the remote peer. + + This function retrieves the highest prioritized frame from the + outbound queue and sends it to the remote peer. It does this as + many as possible until the user callback + :type:`nghttp2_send_callback` returns + :macro:`NGHTTP2_ERR_WOULDBLOCK` or the outbound queue becomes empty. + This function calls several callback functions which are passed + when initializing the *session*. Here is the simple time chart + which tells when each callback is invoked: + + 1. Get the next frame to send from outbound queue. + + 2. Prepare transmission of the frame. + + 3. If the control frame cannot be sent because some preconditions + are not met (e.g., request HEADERS cannot be sent after GOAWAY), + :type:`nghttp2_on_frame_not_send_callback` is invoked. Abort + the following steps. + + 4. If the frame is HEADERS, PUSH_PROMISE or DATA, + :type:`nghttp2_select_padding_callback` is invoked. + + 5. If the frame is request HEADERS, the stream is opened here. + + 6. :type:`nghttp2_before_frame_send_callback` is invoked. + + 7. :type:`nghttp2_send_callback` is invoked one or more times to + send the frame. + + 8. :type:`nghttp2_on_frame_send_callback` is invoked. + + 9. If the transmission of the frame triggers closure of the stream, + the stream is closed and + :type:`nghttp2_on_stream_close_callback` is invoked. + + This function returns 0 if it succeeds, or one of the following + negative error codes: + + :macro:`NGHTTP2_ERR_NOMEM` + Out of memory. + :macro:`NGHTTP2_ERR_CALLBACK_FAILURE` + The callback function failed. diff --git a/doc/nghttp2_session_server_new.rst b/doc/nghttp2_session_server_new.rst new file mode 100644 index 0000000..7350180 --- /dev/null +++ b/doc/nghttp2_session_server_new.rst @@ -0,0 +1,29 @@ + +nghttp2_session_server_new +========================== + +Synopsis +-------- + +*#include * + +.. function:: int nghttp2_session_server_new(nghttp2_session **session_ptr, const nghttp2_session_callbacks *callbacks, void *user_data) + + + Initializes *\*session_ptr* for server use. The all members of + *callbacks* are copied to *\*session_ptr*. Therefore *\*session_ptr* + does not store *callbacks*. The *user_data* is an arbitrary user + supplied data, which will be passed to the callback functions. + + The :type:`nghttp2_send_callback` must be specified. If the + application code uses `nghttp2_session_recv()`, the + :type:`nghttp2_recv_callback` must be specified. The other members + of *callbacks* can be ``NULL``. + + If this function fails, *\*session_ptr* is left untouched. + + This function returns 0 if it succeeds, or one of the following + negative error codes: + + :macro:`NGHTTP2_ERR_NOMEM` + Out of memory. diff --git a/doc/nghttp2_session_server_new2.rst b/doc/nghttp2_session_server_new2.rst new file mode 100644 index 0000000..482e651 --- /dev/null +++ b/doc/nghttp2_session_server_new2.rst @@ -0,0 +1,29 @@ + +nghttp2_session_server_new2 +=========================== + +Synopsis +-------- + +*#include * + +.. function:: int nghttp2_session_server_new2(nghttp2_session **session_ptr, const nghttp2_session_callbacks *callbacks, void *user_data, const nghttp2_option *option) + + + Like `nghttp2_session_server_new()`, but with additional options + specified in the *option*. + + The *option* can be ``NULL`` and the call is equivalent to + `nghttp2_session_server_new()`. + + This function does not take ownership *option*. The application is + responsible for freeing *option* if it finishes using the object. + + The library code does not refer to *option* after this function + returns. + + This function returns 0 if it succeeds, or one of the following + negative error codes: + + :macro:`NGHTTP2_ERR_NOMEM` + Out of memory. diff --git a/doc/nghttp2_session_server_new3.rst b/doc/nghttp2_session_server_new3.rst new file mode 100644 index 0000000..35d1e39 --- /dev/null +++ b/doc/nghttp2_session_server_new3.rst @@ -0,0 +1,29 @@ + +nghttp2_session_server_new3 +=========================== + +Synopsis +-------- + +*#include * + +.. function:: int nghttp2_session_server_new3( nghttp2_session **session_ptr, const nghttp2_session_callbacks *callbacks, void *user_data, const nghttp2_option *option, nghttp2_mem *mem) + + + Like `nghttp2_session_server_new2()`, but with additional custom + memory allocator specified in the *mem*. + + The *mem* can be ``NULL`` and the call is equivalent to + `nghttp2_session_server_new2()`. + + This function does not take ownership *mem*. The application is + responsible for freeing *mem*. + + The library code does not refer to *mem* pointer after this + function returns, so the application can safely free it. + + This function returns 0 if it succeeds, or one of the following + negative error codes: + + :macro:`NGHTTP2_ERR_NOMEM` + Out of memory. diff --git a/doc/nghttp2_session_set_next_stream_id.rst b/doc/nghttp2_session_set_next_stream_id.rst new file mode 100644 index 0000000..5f2efa9 --- /dev/null +++ b/doc/nghttp2_session_set_next_stream_id.rst @@ -0,0 +1,24 @@ + +nghttp2_session_set_next_stream_id +================================== + +Synopsis +-------- + +*#include * + +.. function:: int nghttp2_session_set_next_stream_id(nghttp2_session *session, int32_t next_stream_id) + + + Tells the *session* that next stream ID is *next_stream_id*. The + *next_stream_id* must be equal or greater than the value returned + by `nghttp2_session_get_next_stream_id()`. + + This function returns 0 if it succeeds, or one of the following + negative error codes: + + :macro:`NGHTTP2_ERR_INVALID_ARGUMENT` + The *next_stream_id* is strictly less than the value + `nghttp2_session_get_next_stream_id()` returns; or + *next_stream_id* is invalid (e.g., even integer for client, or + odd integer for server). diff --git a/doc/nghttp2_session_set_stream_user_data.rst b/doc/nghttp2_session_set_stream_user_data.rst new file mode 100644 index 0000000..6700650 --- /dev/null +++ b/doc/nghttp2_session_set_stream_user_data.rst @@ -0,0 +1,26 @@ + +nghttp2_session_set_stream_user_data +==================================== + +Synopsis +-------- + +*#include * + +.. function:: int nghttp2_session_set_stream_user_data(nghttp2_session *session, int32_t stream_id, void *stream_user_data) + + + Sets the *stream_user_data* to the stream denoted by the + *stream_id*. If a stream user data is already set to the stream, + it is replaced with the *stream_user_data*. It is valid to specify + ``NULL`` in the *stream_user_data*, which nullifies the associated + data pointer. + + It is valid to set the *stream_user_data* to the stream reserved by + PUSH_PROMISE frame. + + This function returns 0 if it succeeds, or one of following + negative error codes: + + :macro:`NGHTTP2_ERR_INVALID_ARGUMENT` + The stream does not exist diff --git a/doc/nghttp2_session_terminate_session.rst b/doc/nghttp2_session_terminate_session.rst new file mode 100644 index 0000000..2b85319 --- /dev/null +++ b/doc/nghttp2_session_terminate_session.rst @@ -0,0 +1,34 @@ + +nghttp2_session_terminate_session +================================= + +Synopsis +-------- + +*#include * + +.. function:: int nghttp2_session_terminate_session(nghttp2_session *session, uint32_t error_code) + + + Signals the session so that the connection should be terminated. + + The last stream ID is the minimum value between the stream ID of a + stream for which :type:`nghttp2_on_frame_recv_callback` was called + most recently and the last stream ID we have sent to the peer + previously. + + The *error_code* is the error code of this GOAWAY frame. The + pre-defined error code is one of :macro:`nghttp2_error_code`. + + After the transmission, both `nghttp2_session_want_read()` and + `nghttp2_session_want_write()` return 0. + + This function should be called when the connection should be + terminated after sending GOAWAY. If the remaining streams should + be processed after GOAWAY, use `nghttp2_submit_goaway()` instead. + + This function returns 0 if it succeeds, or one of the following + negative error codes: + + :macro:`NGHTTP2_ERR_NOMEM` + Out of memory. diff --git a/doc/nghttp2_session_terminate_session2.rst b/doc/nghttp2_session_terminate_session2.rst new file mode 100644 index 0000000..fd31508 --- /dev/null +++ b/doc/nghttp2_session_terminate_session2.rst @@ -0,0 +1,34 @@ + +nghttp2_session_terminate_session2 +================================== + +Synopsis +-------- + +*#include * + +.. function:: int nghttp2_session_terminate_session2(nghttp2_session *session, int32_t last_stream_id, uint32_t error_code) + + + Signals the session so that the connection should be terminated. + + This function behaves like `nghttp2_session_terminate_session()`, + but the last stream ID can be specified by the application for fine + grained control of stream. The HTTP/2 specification does not allow + last_stream_id to be increased. So the actual value sent as + last_stream_id is the minimum value between the given + *last_stream_id* and the last_stream_id we have previously sent to + the peer. + + The *last_stream_id* is peer's stream ID or 0. So if *session* is + initialized as client, *last_stream_id* must be even or 0. If + *session* is initialized as server, *last_stream_id* must be odd or + 0. + + This function returns 0 if it succeeds, or one of the following + negative error codes: + + :macro:`NGHTTP2_ERR_NOMEM` + Out of memory. + :macro:`NGHTTP2_ERR_INVALID_ARGUMENT` + The *last_stream_id* is invalid. diff --git a/doc/nghttp2_session_upgrade.rst b/doc/nghttp2_session_upgrade.rst new file mode 100644 index 0000000..f750e7c --- /dev/null +++ b/doc/nghttp2_session_upgrade.rst @@ -0,0 +1,44 @@ + +nghttp2_session_upgrade +======================= + +Synopsis +-------- + +*#include * + +.. function:: int nghttp2_session_upgrade(nghttp2_session *session, const uint8_t *settings_payload, size_t settings_payloadlen, void *stream_user_data) + + + Performs post-process of HTTP Upgrade request. This function can + be called from both client and server, but the behavior is very + different in each other. + + If called from client side, the *settings_payload* must be the + value sent in ``HTTP2-Settings`` header field and must be decoded + by base64url decoder. The *settings_payloadlen* is the length of + *settings_payload*. The *settings_payload* is unpacked and its + setting values will be submitted using `nghttp2_submit_settings()`. + This means that the client application code does not need to submit + SETTINGS by itself. The stream with stream ID=1 is opened and the + *stream_user_data* is used for its stream_user_data. The opened + stream becomes half-closed (local) state. + + If called from server side, the *settings_payload* must be the + value received in ``HTTP2-Settings`` header field and must be + decoded by base64url decoder. The *settings_payloadlen* is the + length of *settings_payload*. It is treated as if the SETTINGS + frame with that payload is received. Thus, callback functions for + the reception of SETTINGS frame will be invoked. The stream with + stream ID=1 is opened. The *stream_user_data* is ignored. The + opened stream becomes half-closed (remote). + + This function returns 0 if it succeeds, or one of the following + negative error codes: + + :macro:`NGHTTP2_ERR_NOMEM` + Out of memory. + :macro:`NGHTTP2_ERR_INVALID_ARGUMENT` + The *settings_payload* is badly formed. + :macro:`NGHTTP2_ERR_PROTO` + The stream ID 1 is already used or closed; or is not available. diff --git a/doc/nghttp2_session_want_read.rst b/doc/nghttp2_session_want_read.rst new file mode 100644 index 0000000..4a2af49 --- /dev/null +++ b/doc/nghttp2_session_want_read.rst @@ -0,0 +1,18 @@ + +nghttp2_session_want_read +========================= + +Synopsis +-------- + +*#include * + +.. function:: int nghttp2_session_want_read(nghttp2_session *session) + + + Returns nonzero value if *session* wants to receive data from the + remote peer. + + If both `nghttp2_session_want_read()` and + `nghttp2_session_want_write()` return 0, the application should + drop the connection. diff --git a/doc/nghttp2_session_want_write.rst b/doc/nghttp2_session_want_write.rst new file mode 100644 index 0000000..96f27fc --- /dev/null +++ b/doc/nghttp2_session_want_write.rst @@ -0,0 +1,18 @@ + +nghttp2_session_want_write +========================== + +Synopsis +-------- + +*#include * + +.. function:: int nghttp2_session_want_write(nghttp2_session *session) + + + Returns nonzero value if *session* wants to send data to the remote + peer. + + If both `nghttp2_session_want_read()` and + `nghttp2_session_want_write()` return 0, the application should + drop the connection. diff --git a/doc/nghttp2_strerror.rst b/doc/nghttp2_strerror.rst new file mode 100644 index 0000000..479593e --- /dev/null +++ b/doc/nghttp2_strerror.rst @@ -0,0 +1,14 @@ + +nghttp2_strerror +================ + +Synopsis +-------- + +*#include * + +.. function:: const char *nghttp2_strerror(int lib_error_code) + + + Returns string describing the *lib_error_code*. The + *lib_error_code* must be one of the :macro:`nghttp2_error`. diff --git a/doc/nghttp2_submit_data.rst b/doc/nghttp2_submit_data.rst new file mode 100644 index 0000000..d0c5579 --- /dev/null +++ b/doc/nghttp2_submit_data.rst @@ -0,0 +1,42 @@ + +nghttp2_submit_data +=================== + +Synopsis +-------- + +*#include * + +.. function:: int nghttp2_submit_data(nghttp2_session *session, uint8_t flags, int32_t stream_id, const nghttp2_data_provider *data_prd) + + + Submits one or more DATA frames to the stream *stream_id*. The + data to be sent are provided by *data_prd*. If *flags* contains + :macro:`NGHTTP2_FLAG_END_STREAM`, the last DATA frame has END_STREAM + flag set. + + This function does not take ownership of the *data_prd*. The + function copies the members of the *data_prd*. + + This function returns 0 if it succeeds, or one of the following + negative error codes: + + :macro:`NGHTTP2_ERR_NOMEM` + Out of memory. + :macro:`NGHTTP2_ERR_DATA_EXIST` + DATA has been already submitted and not fully processed yet. + :macro:`NGHTTP2_ERR_INVALID_ARGUMENT` + The *stream_id* is 0. + :macro:`NGHTTP2_ERR_STREAM_CLOSED` + The stream was alreay closed; or the *stream_id* is invalid. + + .. note:: + + Currently, only one data is allowed for a stream at a time. + Submitting data more than once before first data is finished + results in :macro:`NGHTTP2_ERR_DATA_EXIST` error code. The + earliest callback which tells that previous data is done is + :type:`nghttp2_on_frame_send_callback`. In side that callback, + new data can be submitted using `nghttp2_submit_data()`. Of + course, all data except for last one must not have + :macro:`NGHTTP2_FLAG_END_STREAM` flag set in *flags*. diff --git a/doc/nghttp2_submit_goaway.rst b/doc/nghttp2_submit_goaway.rst new file mode 100644 index 0000000..93a99f1 --- /dev/null +++ b/doc/nghttp2_submit_goaway.rst @@ -0,0 +1,53 @@ + +nghttp2_submit_goaway +===================== + +Synopsis +-------- + +*#include * + +.. function:: int nghttp2_submit_goaway(nghttp2_session *session, uint8_t flags, int32_t last_stream_id, uint32_t error_code, const uint8_t *opaque_data, size_t opaque_data_len) + + + Submits GOAWAY frame with the last stream ID *last_stream_id* and + the error code *error_code*. + + The pre-defined error code is one of :macro:`nghttp2_error_code`. + + The *flags* is currently ignored and should be + :macro:`NGHTTP2_FLAG_NONE`. + + The *last_stream_id* is peer's stream ID or 0. So if *session* is + initialized as client, *last_stream_id* must be even or 0. If + *session* is initialized as server, *last_stream_id* must be odd or + 0. + + The HTTP/2 specification says last_stream_id must not be increased + from the value previously sent. So the actual value sent as + last_stream_id is the minimum value between the given + *last_stream_id* and the last_stream_id previously sent to the + peer. + + If the *opaque_data* is not ``NULL`` and *opaque_data_len* is not + zero, those data will be sent as additional debug data. The + library makes a copy of the memory region pointed by *opaque_data* + with the length *opaque_data_len*, so the caller does not need to + keep this memory after the return of this function. If the + *opaque_data_len* is 0, the *opaque_data* could be ``NULL``. + + After successful transmission of GOAWAY, following things happen. + All incoming streams having strictly more than *last_stream_id* are + closed. All incoming HEADERS which starts new stream are simply + ignored. After all active streams are handled, both + `nghttp2_session_want_read()` and `nghttp2_session_want_write()` + return 0 and the application can close session. + + This function returns 0 if it succeeds, or one of the following + negative error codes: + + :macro:`NGHTTP2_ERR_NOMEM` + Out of memory. + :macro:`NGHTTP2_ERR_INVALID_ARGUMENT` + The *opaque_data_len* is too large; the *last_stream_id* is + invalid. diff --git a/doc/nghttp2_submit_headers.rst b/doc/nghttp2_submit_headers.rst new file mode 100644 index 0000000..f328bad --- /dev/null +++ b/doc/nghttp2_submit_headers.rst @@ -0,0 +1,80 @@ + +nghttp2_submit_headers +====================== + +Synopsis +-------- + +*#include * + +.. function:: int32_t nghttp2_submit_headers(nghttp2_session *session, uint8_t flags, int32_t stream_id, const nghttp2_priority_spec *pri_spec, const nghttp2_nv *nva, size_t nvlen, void *stream_user_data) + + + Submits HEADERS frame. The *flags* is bitwise OR of the + following values: + + * :macro:`NGHTTP2_FLAG_END_STREAM` + + If *flags* includes :macro:`NGHTTP2_FLAG_END_STREAM`, this frame has + END_STREAM flag set. + + The library handles the CONTINUATION frame internally and it + correctly sets END_HEADERS to the last sequence of the PUSH_PROMISE + or CONTINUATION frame. + + If the *stream_id* is -1, this frame is assumed as request (i.e., + request HEADERS frame which opens new stream). In this case, the + assigned stream ID will be returned. Otherwise, specify stream ID + in *stream_id*. + + The *pri_spec* is priority specification of this request. ``NULL`` + means the default priority (see + `nghttp2_priority_spec_default_init()`). To specify the priority, + use `nghttp2_priority_spec_init()`. If *pri_spec* is not ``NULL``, + this function will copy its data members. + + The ``pri_spec->weight`` must be in [:macro:`NGHTTP2_MIN_WEIGHT`, + :macro:`NGHTTP2_MAX_WEIGHT`], inclusive. If ``pri_spec->weight`` is + strictly less than :macro:`NGHTTP2_MIN_WEIGHT`, it becomes + :macro:`NGHTTP2_MIN_WEIGHT`. If it is strictly greater than + :macro:`NGHTTP2_MAX_WEIGHT`, it becomes :macro:`NGHTTP2_MAX_WEIGHT`. + + The *nva* is an array of name/value pair :type:`nghttp2_nv` with + *nvlen* elements. The application is responsible to include + required pseudo-header fields (header field whose name starts with + ":") in *nva* and must place pseudo-headers before regular header + fields. + + This function creates copies of all name/value pairs in *nva*. It + also lower-cases all names in *nva*. The order of elements in + *nva* is preserved. + + The *stream_user_data* is a pointer to an arbitrary data which is + associated to the stream this frame will open. Therefore it is + only used if this frame opens streams, in other words, it changes + stream state from idle or reserved to open. + + This function is low-level in a sense that the application code can + specify flags directly. For usual HTTP request, + `nghttp2_submit_request()` is useful. + + This function returns newly assigned stream ID if it succeeds and + *stream_id* is -1. Otherwise, this function returns 0 if it + succeeds, or one of the following negative error codes: + + :macro:`NGHTTP2_ERR_NOMEM` + Out of memory. + :macro:`NGHTTP2_ERR_STREAM_ID_NOT_AVAILABLE` + No stream ID is available because maximum stream ID was + reached. + :macro:`NGHTTP2_ERR_INVALID_ARGUMENT` + The *stream_id* is 0. + + .. warning:: + + This function returns assigned stream ID if it succeeds and + *stream_id* is -1. But that stream is not opened yet. The + application must not submit frame to that stream ID before + :type:`nghttp2_before_frame_send_callback` is called for this + frame. + diff --git a/doc/nghttp2_submit_ping.rst b/doc/nghttp2_submit_ping.rst new file mode 100644 index 0000000..7c6ec8f --- /dev/null +++ b/doc/nghttp2_submit_ping.rst @@ -0,0 +1,29 @@ + +nghttp2_submit_ping +=================== + +Synopsis +-------- + +*#include * + +.. function:: int nghttp2_submit_ping(nghttp2_session *session, uint8_t flags, const uint8_t *opaque_data) + + + Submits PING frame. You don't have to send PING back when you + received PING frame. The library automatically submits PING frame + in this case. + + The *flags* is currently ignored and should be + :macro:`NGHTTP2_FLAG_NONE`. + + If the *opaque_data* is non ``NULL``, then it should point to the 8 + bytes array of memory to specify opaque data to send with PING + frame. If the *opaque_data* is ``NULL``, zero-cleared 8 bytes will + be sent as opaque data. + + This function returns 0 if it succeeds, or one of the following + negative error codes: + + :macro:`NGHTTP2_ERR_NOMEM` + Out of memory. diff --git a/doc/nghttp2_submit_priority.rst b/doc/nghttp2_submit_priority.rst new file mode 100644 index 0000000..2fda48a --- /dev/null +++ b/doc/nghttp2_submit_priority.rst @@ -0,0 +1,37 @@ + +nghttp2_submit_priority +======================= + +Synopsis +-------- + +*#include * + +.. function:: int nghttp2_submit_priority(nghttp2_session *session, uint8_t flags, int32_t stream_id, const nghttp2_priority_spec *pri_spec) + + + Submits PRIORITY frame to change the priority of stream *stream_id* + to the priority specification *pri_spec*. + + The *flags* is currently ignored and should be + :macro:`NGHTTP2_FLAG_NONE`. + + The *pri_spec* is priority specification of this request. ``NULL`` + is not allowed for this function. To specify the priority, use + `nghttp2_priority_spec_init()`. This function will copy its data + members. + + The ``pri_spec->weight`` must be in [:macro:`NGHTTP2_MIN_WEIGHT`, + :macro:`NGHTTP2_MAX_WEIGHT`], inclusive. If ``pri_spec->weight`` is + strictly less than :macro:`NGHTTP2_MIN_WEIGHT`, it becomes + :macro:`NGHTTP2_MIN_WEIGHT`. If it is strictly greater than + :macro:`NGHTTP2_MAX_WEIGHT`, it becomes :macro:`NGHTTP2_MAX_WEIGHT`. + + This function returns 0 if it succeeds, or one of the following + negative error codes: + + :macro:`NGHTTP2_ERR_NOMEM` + Out of memory. + :macro:`NGHTTP2_ERR_INVALID_ARGUMENT` + The *stream_id* is 0; or the *pri_spec* is NULL; or trying to + depend on itself. diff --git a/doc/nghttp2_submit_push_promise.rst b/doc/nghttp2_submit_push_promise.rst new file mode 100644 index 0000000..8d66933 --- /dev/null +++ b/doc/nghttp2_submit_push_promise.rst @@ -0,0 +1,65 @@ + +nghttp2_submit_push_promise +=========================== + +Synopsis +-------- + +*#include * + +.. function:: int32_t nghttp2_submit_push_promise(nghttp2_session *session, uint8_t flags, int32_t stream_id, const nghttp2_nv *nva, size_t nvlen, void *promised_stream_user_data) + + + Submits PUSH_PROMISE frame. + + The *flags* is currently ignored. The library handles the + CONTINUATION frame internally and it correctly sets END_HEADERS to + the last sequence of the PUSH_PROMISE or CONTINUATION frame. + + The *stream_id* must be client initiated stream ID. + + The *nva* is an array of name/value pair :type:`nghttp2_nv` with + *nvlen* elements. The application is responsible to include + required pseudo-header fields (header field whose name starts with + ":") in *nva* and must place pseudo-headers before regular header + fields. + + This function creates copies of all name/value pairs in *nva*. It + also lower-cases all names in *nva*. The order of elements in + *nva* is preserved. + + The *promised_stream_user_data* is a pointer to an arbitrary data + which is associated to the promised stream this frame will open and + make it in reserved state. It is available using + `nghttp2_session_get_stream_user_data()`. The application can + access it in :type:`nghttp2_before_frame_send_callback` and + :type:`nghttp2_on_frame_send_callback` of this frame. + + The client side is not allowed to use this function. + + To submit response headers and data, use + `nghttp2_submit_response()`. + + This function returns assigned promised stream ID if it succeeds, + or one of the following negative error codes: + + :macro:`NGHTTP2_ERR_NOMEM` + Out of memory. + :macro:`NGHTTP2_ERR_PROTO` + This function was invoked when *session* is initialized as + client. + :macro:`NGHTTP2_ERR_STREAM_ID_NOT_AVAILABLE` + No stream ID is available because maximum stream ID was + reached. + :macro:`NGHTTP2_ERR_INVALID_ARGUMENT` + The *stream_id* is 0; The *stream_id* does not designate stream + that peer initiated. + + .. warning:: + + This function returns assigned promised stream ID if it succeeds. + But that stream is not opened yet. The application must not + submit frame to that stream ID before + :type:`nghttp2_before_frame_send_callback` is called for this + frame. + diff --git a/doc/nghttp2_submit_request.rst b/doc/nghttp2_submit_request.rst new file mode 100644 index 0000000..a4b36dd --- /dev/null +++ b/doc/nghttp2_submit_request.rst @@ -0,0 +1,68 @@ + +nghttp2_submit_request +====================== + +Synopsis +-------- + +*#include * + +.. function:: int32_t nghttp2_submit_request(nghttp2_session *session, const nghttp2_priority_spec *pri_spec, const nghttp2_nv *nva, size_t nvlen, const nghttp2_data_provider *data_prd, void *stream_user_data) + + + Submits HEADERS frame and optionally one or more DATA frames. + + The *pri_spec* is priority specification of this request. ``NULL`` + means the default priority (see + `nghttp2_priority_spec_default_init()`). To specify the priority, + use `nghttp2_priority_spec_init()`. If *pri_spec* is not ``NULL``, + this function will copy its data members. + + The ``pri_spec->weight`` must be in [:macro:`NGHTTP2_MIN_WEIGHT`, + :macro:`NGHTTP2_MAX_WEIGHT`], inclusive. If ``pri_spec->weight`` is + strictly less than :macro:`NGHTTP2_MIN_WEIGHT`, it becomes + :macro:`NGHTTP2_MIN_WEIGHT`. If it is strictly greater than + :macro:`NGHTTP2_MAX_WEIGHT`, it becomes :macro:`NGHTTP2_MAX_WEIGHT`. + + The *nva* is an array of name/value pair :type:`nghttp2_nv` with + *nvlen* elements. The application is responsible to include + required pseudo-header fields (header field whose name starts with + ":") in *nva* and must place pseudo-headers before regular header + fields. + + This function creates copies of all name/value pairs in *nva*. It + also lower-cases all names in *nva*. The order of elements in + *nva* is preserved. + + HTTP/2 specification has requirement about header fields in the + request HEADERS. See the specification for more details. + + If *data_prd* is not ``NULL``, it provides data which will be sent + in subsequent DATA frames. In this case, a method that allows + request message bodies + (https://tools.ietf.org/html/rfc7231#section-4) must be specified + with ``:method`` key in *nva* (e.g. ``POST``). This function does + not take ownership of the *data_prd*. The function copies the + members of the *data_prd*. If *data_prd* is ``NULL``, HEADERS have + END_STREAM set. The *stream_user_data* is data associated to the + stream opened by this request and can be an arbitrary pointer, + which can be retrieved later by + `nghttp2_session_get_stream_user_data()`. + + This function returns assigned stream ID if it succeeds, or one of + the following negative error codes: + + :macro:`NGHTTP2_ERR_NOMEM` + Out of memory. + :macro:`NGHTTP2_ERR_STREAM_ID_NOT_AVAILABLE` + No stream ID is available because maximum stream ID was + reached. + + .. warning:: + + This function returns assigned stream ID if it succeeds. But + that stream is not opened yet. The application must not submit + frame to that stream ID before + :type:`nghttp2_before_frame_send_callback` is called for this + frame. + diff --git a/doc/nghttp2_submit_response.rst b/doc/nghttp2_submit_response.rst new file mode 100644 index 0000000..f01d892 --- /dev/null +++ b/doc/nghttp2_submit_response.rst @@ -0,0 +1,57 @@ + +nghttp2_submit_response +======================= + +Synopsis +-------- + +*#include * + +.. function:: int nghttp2_submit_response(nghttp2_session *session, int32_t stream_id, const nghttp2_nv *nva, size_t nvlen, const nghttp2_data_provider *data_prd) + + + Submits response HEADERS frame and optionally one or more DATA + frames against the stream *stream_id*. + + The *nva* is an array of name/value pair :type:`nghttp2_nv` with + *nvlen* elements. The application is responsible to include + required pseudo-header fields (header field whose name starts with + ":") in *nva* and must place pseudo-headers before regular header + fields. + + This function creates copies of all name/value pairs in *nva*. It + also lower-cases all names in *nva*. The order of elements in + *nva* is preserved. + + HTTP/2 specification has requirement about header fields in the + response HEADERS. See the specification for more details. + + If *data_prd* is not ``NULL``, it provides data which will be sent + in subsequent DATA frames. This function does not take ownership + of the *data_prd*. The function copies the members of the + *data_prd*. If *data_prd* is ``NULL``, HEADERS will have + END_STREAM flag set. + + This method can be used as normal HTTP response and push response. + When pushing a resource using this function, the *session* must be + configured using `nghttp2_session_server_new()` or its variants and + the target stream denoted by the *stream_id* must be reserved using + `nghttp2_submit_push_promise()`. + + To send non-final response headers (e.g., HTTP status 101), don't + use this function because this function half-closes the outbound + stream. Instead, use `nghttp2_submit_headers()` for this purpose. + + This function returns 0 if it succeeds, or one of the following + negative error codes: + + :macro:`NGHTTP2_ERR_NOMEM` + Out of memory. + :macro:`NGHTTP2_ERR_INVALID_ARGUMENT` + The *stream_id* is 0. + + .. warning:: + + Calling this function twice for the same stream ID may lead to + program crash. It is generally considered to a programming error + to commit response twice. diff --git a/doc/nghttp2_submit_rst_stream.rst b/doc/nghttp2_submit_rst_stream.rst new file mode 100644 index 0000000..a179424 --- /dev/null +++ b/doc/nghttp2_submit_rst_stream.rst @@ -0,0 +1,27 @@ + +nghttp2_submit_rst_stream +========================= + +Synopsis +-------- + +*#include * + +.. function:: int nghttp2_submit_rst_stream(nghttp2_session *session, uint8_t flags, int32_t stream_id, uint32_t error_code) + + + Submits RST_STREAM frame to cancel/reject the stream *stream_id* + with the error code *error_code*. + + The pre-defined error code is one of :macro:`nghttp2_error_code`. + + The *flags* is currently ignored and should be + :macro:`NGHTTP2_FLAG_NONE`. + + This function returns 0 if it succeeds, or one of the following + negative error codes: + + :macro:`NGHTTP2_ERR_NOMEM` + Out of memory. + :macro:`NGHTTP2_ERR_INVALID_ARGUMENT` + The *stream_id* is 0. diff --git a/doc/nghttp2_submit_settings.rst b/doc/nghttp2_submit_settings.rst new file mode 100644 index 0000000..dc4df55 --- /dev/null +++ b/doc/nghttp2_submit_settings.rst @@ -0,0 +1,41 @@ + +nghttp2_submit_settings +======================= + +Synopsis +-------- + +*#include * + +.. function:: int nghttp2_submit_settings(nghttp2_session *session, uint8_t flags, const nghttp2_settings_entry *iv, size_t niv) + + + Stores local settings and submits SETTINGS frame. The *iv* is the + pointer to the array of :type:`nghttp2_settings_entry`. The *niv* + indicates the number of :type:`nghttp2_settings_entry`. + + The *flags* is currently ignored and should be + :macro:`NGHTTP2_FLAG_NONE`. + + This function does not take ownership of the *iv*. This function + copies all the elements in the *iv*. + + While updating individual stream's local window size, if the window + size becomes strictly larger than NGHTTP2_MAX_WINDOW_SIZE, + RST_STREAM is issued against such a stream. + + SETTINGS with :macro:`NGHTTP2_FLAG_ACK` is automatically submitted + by the library and application could not send it at its will. + + This function returns 0 if it succeeds, or one of the following + negative error codes: + + :macro:`NGHTTP2_ERR_INVALID_ARGUMENT` + The *iv* contains invalid value (e.g., initial window size + strictly greater than (1 << 31) - 1. + :macro:`NGHTTP2_ERR_TOO_MANY_INFLIGHT_SETTINGS` + There is already another in-flight SETTINGS. Note that the + current implementation only allows 1 in-flight SETTINGS frame + without ACK flag set. + :macro:`NGHTTP2_ERR_NOMEM` + Out of memory. diff --git a/doc/nghttp2_submit_shutdown_notice.rst b/doc/nghttp2_submit_shutdown_notice.rst new file mode 100644 index 0000000..d6c28b8 --- /dev/null +++ b/doc/nghttp2_submit_shutdown_notice.rst @@ -0,0 +1,43 @@ + +nghttp2_submit_shutdown_notice +============================== + +Synopsis +-------- + +*#include * + +.. function:: int nghttp2_submit_shutdown_notice(nghttp2_session *session) + + + Signals to the client that the server started graceful shutdown + procedure. + + This function is only usable for server. If this function is + called with client side session, this function returns + :macro:`NGHTTP2_ERR_INVALID_STATE`. + + To gracefully shutdown HTTP/2 session, server should call this + function to send GOAWAY with last_stream_id (1u << 31) - 1. And + after some delay (e.g., 1 RTT), send another GOAWAY with the stream + ID that the server has some processing using + `nghttp2_submit_goaway()`. See also + `nghttp2_session_get_last_proc_stream_id()`. + + Unlike `nghttp2_submit_goaway()`, this function just sends GOAWAY + and does nothing more. This is a mere indication to the client + that session shutdown is imminent. The application should call + `nghttp2_submit_goaway()` with appropriate last_stream_id after + this call. + + If one or more GOAWAY frame have been already sent by either + `nghttp2_submit_goaway()` or `nghttp2_session_terminate_session()`, + this function has no effect. + + This function returns 0 if it succeeds, or one of the following + negative error codes: + + :macro:`NGHTTP2_ERR_NOMEM` + Out of memory. + :macro:`NGHTTP2_ERR_INVALID_STATE` + The *session* is initialized as client. diff --git a/doc/nghttp2_submit_trailer.rst b/doc/nghttp2_submit_trailer.rst new file mode 100644 index 0000000..420c6b9 --- /dev/null +++ b/doc/nghttp2_submit_trailer.rst @@ -0,0 +1,52 @@ + +nghttp2_submit_trailer +====================== + +Synopsis +-------- + +*#include * + +.. function:: int nghttp2_submit_trailer(nghttp2_session *session, int32_t stream_id, const nghttp2_nv *nva, size_t nvlen) + + + Submits trailer HEADERS against the stream *stream_id*. + + The *nva* is an array of name/value pair :type:`nghttp2_nv` with + *nvlen* elements. The application is responsible not to include + required pseudo-header fields (header field whose name starts with + ":") in *nva*. + + This function creates copies of all name/value pairs in *nva*. It + also lower-cases all names in *nva*. The order of elements in + *nva* is preserved. + + For server, trailer must be followed by response HEADERS or + response DATA. The library does not check that response HEADERS + has already sent and if `nghttp2_submit_trailer()` is called before + any response HEADERS submission (usually by + `nghttp2_submit_response()`), the content of *nva* will be sent as + reponse headers, which will result in error. + + This function has the same effect with `nghttp2_submit_headers()`, + with flags = :macro:`NGHTTP2_FLAG_END_HEADERS` and both pri_spec and + stream_user_data to NULL. + + To submit trailer after `nghttp2_submit_response()` is called, the + application has to specify :type:`nghttp2_data_provider` to + `nghttp2_submit_response()`. In side + :type:`nghttp2_data_source_read_callback`, when setting + :macro:`NGHTTP2_DATA_FLAG_EOF`, also set + :macro:`NGHTTP2_DATA_FLAG_NO_END_STREAM`. After that, the + application can send trailer using `nghttp2_submit_trailer()`. + `nghttp2_submit_trailer()` can be used inside + :type:`nghttp2_data_source_read_callback`. + + This function returns 0 if it succeeds and *stream_id* is -1. + Otherwise, this function returns 0 if it succeeds, or one of the + following negative error codes: + + :macro:`NGHTTP2_ERR_NOMEM` + Out of memory. + :macro:`NGHTTP2_ERR_INVALID_ARGUMENT` + The *stream_id* is 0. diff --git a/doc/nghttp2_submit_window_update.rst b/doc/nghttp2_submit_window_update.rst new file mode 100644 index 0000000..4410444 --- /dev/null +++ b/doc/nghttp2_submit_window_update.rst @@ -0,0 +1,43 @@ + +nghttp2_submit_window_update +============================ + +Synopsis +-------- + +*#include * + +.. function:: int nghttp2_submit_window_update(nghttp2_session *session, uint8_t flags, int32_t stream_id, int32_t window_size_increment) + + + Submits WINDOW_UPDATE frame. + + The *flags* is currently ignored and should be + :macro:`NGHTTP2_FLAG_NONE`. + + The *stream_id* is the stream ID to send this WINDOW_UPDATE. To + send connection level WINDOW_UPDATE, specify 0 to *stream_id*. + + If the *window_size_increment* is positive, the WINDOW_UPDATE with + that value as window_size_increment is queued. If the + *window_size_increment* is larger than the received bytes from the + remote endpoint, the local window size is increased by that + difference. + + If the *window_size_increment* is negative, the local window size + is decreased by -*window_size_increment*. If automatic + WINDOW_UPDATE is enabled + (`nghttp2_option_set_no_auto_window_update()`), and the library + decided that the WINDOW_UPDATE should be submitted, then + WINDOW_UPDATE is queued with the current received bytes count. + + If the *window_size_increment* is 0, the function does nothing and + returns 0. + + This function returns 0 if it succeeds, or one of the following + negative error codes: + + :macro:`NGHTTP2_ERR_FLOW_CONTROL` + The local window size overflow or gets negative. + :macro:`NGHTTP2_ERR_NOMEM` + Out of memory. diff --git a/doc/nghttp2_version.rst b/doc/nghttp2_version.rst new file mode 100644 index 0000000..3f16c54 --- /dev/null +++ b/doc/nghttp2_version.rst @@ -0,0 +1,17 @@ + +nghttp2_version +=============== + +Synopsis +-------- + +*#include * + +.. function:: nghttp2_info *nghttp2_version(int least_version) + + + Returns a pointer to a nghttp2_info struct with version information + about the run-time library in use. The *least_version* argument + can be set to a 24 bit numerical value for the least accepted + version number and if the condition is not met, this function will + return a ``NULL``. Pass in 0 to skip the version checking. diff --git a/doc/nghttp2ver.h.rst.in b/doc/nghttp2ver.h.rst.in new file mode 100644 index 0000000..c6aa779 --- /dev/null +++ b/doc/nghttp2ver.h.rst.in @@ -0,0 +1,4 @@ +nghttp2ver.h +============ + +.. literalinclude:: @top_builddir@/lib/includes/nghttp2/nghttp2ver.h diff --git a/doc/nghttpd.1 b/doc/nghttpd.1 new file mode 100644 index 0000000..24e7bb9 --- /dev/null +++ b/doc/nghttpd.1 @@ -0,0 +1,194 @@ +.\" Man page generated from reStructuredText. +. +.TH "NGHTTPD" "1" "May 16, 2015" "1.0.0" "nghttp2" +.SH NAME +nghttpd \- HTTP/2 experimental server +. +.nr rst2man-indent-level 0 +. +.de1 rstReportMargin +\\$1 \\n[an-margin] +level \\n[rst2man-indent-level] +level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] +- +\\n[rst2man-indent0] +\\n[rst2man-indent1] +\\n[rst2man-indent2] +.. +.de1 INDENT +.\" .rstReportMargin pre: +. RS \\$1 +. nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] +. nr rst2man-indent-level +1 +.\" .rstReportMargin post: +.. +.de UNINDENT +. RE +.\" indent \\n[an-margin] +.\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] +.nr rst2man-indent-level -1 +.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] +.in \\n[rst2man-indent\\n[rst2man-indent-level]]u +.. +.SH SYNOPSIS +.sp +\fBnghttpd\fP [OPTION]... [ ] +.SH DESCRIPTION +.sp +HTTP/2 experimental server +.INDENT 0.0 +.TP +.B +Specify listening port number. +.UNINDENT +.INDENT 0.0 +.TP +.B +Set path to server\(aqs private key. Required unless +\fI\%\-\-no\-tls\fP is specified. +.UNINDENT +.INDENT 0.0 +.TP +.B +Set path to server\(aqs certificate. Required unless +\fI\%\-\-no\-tls\fP is specified. +.UNINDENT +.SH OPTIONS +.INDENT 0.0 +.TP +.B \-a, \-\-address= +The address to bind to. If not specified the default IP +address determined by getaddrinfo is used. +.UNINDENT +.INDENT 0.0 +.TP +.B \-D, \-\-daemon +Run in a background. If \fI\%\-D\fP is used, the current working +directory is changed to \(aq\fI/\fP\(aq. Therefore if this option +is used, \fI\%\-d\fP option must be specified. +.UNINDENT +.INDENT 0.0 +.TP +.B \-V, \-\-verify\-client +The server sends a client certificate request. If the +client did not return a certificate, the handshake is +terminated. Currently, this option just requests a +client certificate and does not verify it. +.UNINDENT +.INDENT 0.0 +.TP +.B \-d, \-\-htdocs= +Specify document root. If this option is not specified, +the document root is the current working directory. +.UNINDENT +.INDENT 0.0 +.TP +.B \-v, \-\-verbose +Print debug information such as reception/ transmission +of frames and name/value pairs. +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-no\-tls +Disable SSL/TLS. +.UNINDENT +.INDENT 0.0 +.TP +.B \-c, \-\-header\-table\-size= +Specify decoder header table size. +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-color +Force colored log output. +.UNINDENT +.INDENT 0.0 +.TP +.B \-p, \-\-push== +Push resources s when is requested. +This option can be used repeatedly to specify multiple +push configurations. and s are +relative to document root. See \fI\%\-\-htdocs\fP option. +Example: \fI\%\-p\fP/=/foo.png \fI\%\-p\fP/doc=/bar.css +.UNINDENT +.INDENT 0.0 +.TP +.B \-b, \-\-padding= +Add at most bytes to a frame payload as padding. +Specify 0 to disable padding. +.UNINDENT +.INDENT 0.0 +.TP +.B \-m, \-\-max\-concurrent\-streams= +Set the maximum number of the concurrent streams in one +HTTP/2 session. +.sp +Default: \fB100\fP +.UNINDENT +.INDENT 0.0 +.TP +.B \-n, \-\-workers= +Set the number of worker threads. +.sp +Default: \fB1\fP +.UNINDENT +.INDENT 0.0 +.TP +.B \-e, \-\-error\-gzip +Make error response gzipped. +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-dh\-param\-file= +Path to file that contains DH parameters in PEM format. +Without this option, DHE cipher suites are not +available. +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-early\-response +Start sending response when request HEADERS is received, +rather than complete request is received. +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-trailer=
    +Add a trailer header to a response.
    must not +include pseudo header field (header field name starting +with \(aq:\(aq). The trailer is sent only if a response has +body part. Example: \fI\%\-\-trailer\fP \(aqfoo: bar\(aq. +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-hexdump +Display the incoming traffic in hexadecimal (Canonical +hex+ASCII display). If SSL/TLS is used, decrypted data +are used. +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-echo\-upload +Send back uploaded content if method is POST or PUT. +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-version +Display version information and exit. +.UNINDENT +.INDENT 0.0 +.TP +.B \-h, \-\-help +Display this help and exit. +.UNINDENT +.sp +The argument is an integer and an optional unit (e.g., 10K is +10 * 1024). Units are K, M and G (powers of 1024). +.SH SEE ALSO +.sp +\fInghttp(1)\fP, \fInghttpx(1)\fP, \fIh2load(1)\fP +.SH AUTHOR +Tatsuhiro Tsujikawa +.SH COPYRIGHT +2012, 2015, Tatsuhiro Tsujikawa +.\" Generated by docutils manpage writer. +. diff --git a/doc/nghttpd.1.rst b/doc/nghttpd.1.rst new file mode 100644 index 0000000..e9ac30d --- /dev/null +++ b/doc/nghttpd.1.rst @@ -0,0 +1,151 @@ + +.. GENERATED by help2rst.py. DO NOT EDIT DIRECTLY. + +.. program:: nghttpd + +nghttpd(1) +========== + +SYNOPSIS +-------- + +**nghttpd** [OPTION]... [ ] + +DESCRIPTION +----------- + +HTTP/2 experimental server + +.. describe:: + + Specify listening port number. + +.. describe:: + + + Set path to server's private key. Required unless + :option:`--no-tls` is specified. + +.. describe:: + + Set path to server's certificate. Required unless + :option:`--no-tls` is specified. + +OPTIONS +------- + +.. option:: -a, --address= + + The address to bind to. If not specified the default IP + address determined by getaddrinfo is used. + +.. option:: -D, --daemon + + Run in a background. If :option:`-D` is used, the current working + directory is changed to '*/*'. Therefore if this option + is used, :option:`-d` option must be specified. + +.. option:: -V, --verify-client + + The server sends a client certificate request. If the + client did not return a certificate, the handshake is + terminated. Currently, this option just requests a + client certificate and does not verify it. + +.. option:: -d, --htdocs= + + Specify document root. If this option is not specified, + the document root is the current working directory. + +.. option:: -v, --verbose + + Print debug information such as reception/ transmission + of frames and name/value pairs. + +.. option:: --no-tls + + Disable SSL/TLS. + +.. option:: -c, --header-table-size= + + Specify decoder header table size. + +.. option:: --color + + Force colored log output. + +.. option:: -p, --push== + + Push resources s when is requested. + This option can be used repeatedly to specify multiple + push configurations. and s are + relative to document root. See :option:`--htdocs` option. + Example: :option:`-p`\/=/foo.png :option:`-p`\/doc=/bar.css + +.. option:: -b, --padding= + + Add at most bytes to a frame payload as padding. + Specify 0 to disable padding. + +.. option:: -m, --max-concurrent-streams= + + Set the maximum number of the concurrent streams in one + HTTP/2 session. + + Default: ``100`` + +.. option:: -n, --workers= + + Set the number of worker threads. + + Default: ``1`` + +.. option:: -e, --error-gzip + + Make error response gzipped. + +.. option:: --dh-param-file= + + Path to file that contains DH parameters in PEM format. + Without this option, DHE cipher suites are not + available. + +.. option:: --early-response + + Start sending response when request HEADERS is received, + rather than complete request is received. + +.. option:: --trailer=
    + + Add a trailer header to a response.
    must not + include pseudo header field (header field name starting + with ':'). The trailer is sent only if a response has + body part. Example: :option:`--trailer` 'foo: bar'. + +.. option:: --hexdump + + Display the incoming traffic in hexadecimal (Canonical + hex+ASCII display). If SSL/TLS is used, decrypted data + are used. + +.. option:: --echo-upload + + Send back uploaded content if method is POST or PUT. + +.. option:: --version + + Display version information and exit. + +.. option:: -h, --help + + Display this help and exit. + + + +The argument is an integer and an optional unit (e.g., 10K is +10 * 1024). Units are K, M and G (powers of 1024). + +SEE ALSO +-------- + +:manpage:`nghttp(1)`, :manpage:`nghttpx(1)`, :manpage:`h2load(1)` diff --git a/doc/nghttpx-howto.rst.in b/doc/nghttpx-howto.rst.in new file mode 100644 index 0000000..082ce51 --- /dev/null +++ b/doc/nghttpx-howto.rst.in @@ -0,0 +1 @@ +.. include:: @top_srcdir@/doc/sources/nghttpx-howto.rst diff --git a/doc/nghttpx.1 b/doc/nghttpx.1 new file mode 100644 index 0000000..e42ec76 --- /dev/null +++ b/doc/nghttpx.1 @@ -0,0 +1,899 @@ +.\" Man page generated from reStructuredText. +. +.TH "NGHTTPX" "1" "May 16, 2015" "1.0.0" "nghttp2" +.SH NAME +nghttpx \- HTTP/2 experimental proxy +. +.nr rst2man-indent-level 0 +. +.de1 rstReportMargin +\\$1 \\n[an-margin] +level \\n[rst2man-indent-level] +level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] +- +\\n[rst2man-indent0] +\\n[rst2man-indent1] +\\n[rst2man-indent2] +.. +.de1 INDENT +.\" .rstReportMargin pre: +. RS \\$1 +. nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] +. nr rst2man-indent-level +1 +.\" .rstReportMargin post: +.. +.de UNINDENT +. RE +.\" indent \\n[an-margin] +.\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] +.nr rst2man-indent-level -1 +.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] +.in \\n[rst2man-indent\\n[rst2man-indent-level]]u +.. +.SH SYNOPSIS +.sp +\fBnghttpx\fP [OPTIONS]... [ ] +.SH DESCRIPTION +.sp +A reverse proxy for HTTP/2, HTTP/1 and SPDY. +.INDENT 0.0 +.TP +.B +Set path to server\(aqs private key. Required unless \fI\%\-p\fP, +\fI\%\-\-client\fP or \fI\%\-\-frontend\-no\-tls\fP are given. +.UNINDENT +.INDENT 0.0 +.TP +.B +Set path to server\(aqs certificate. Required unless \fI\%\-p\fP, +\fI\%\-\-client\fP or \fI\%\-\-frontend\-no\-tls\fP are given. To make OCSP +stapling work, this must be absolute path. +.UNINDENT +.SH OPTIONS +.sp +The options are categorized into several groups. +.SS Connections +.INDENT 0.0 +.TP +.B \-b, \-\-backend= +Set backend host and port. The multiple backend +addresses are accepted by repeating this option. UNIX +domain socket can be specified by prefixing path name +with "unix:" (e.g., unix:/var/run/backend.sock) +.sp +Default: \fB127.0.0.1,80\fP +.UNINDENT +.INDENT 0.0 +.TP +.B \-f, \-\-frontend= +Set frontend host and port. If is \(aq*\(aq, it +assumes all addresses including both IPv4 and IPv6. +UNIX domain socket can be specified by prefixing path +name with "unix:" (e.g., unix:/var/run/nghttpx.sock) +.sp +Default: \fB*,3000\fP +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-backlog= +Set listen backlog size. +.sp +Default: \fB512\fP +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-backend\-ipv4 +Resolve backend hostname to IPv4 address only. +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-backend\-ipv6 +Resolve backend hostname to IPv6 address only. +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-backend\-http\-proxy\-uri= +Specify proxy URI in the form +\fI\%http:/\fP/[:@]:. If a proxy +requires authentication, specify and . +Note that they must be properly percent\-encoded. This +proxy is used when the backend connection is HTTP/2. +First, make a CONNECT request to the proxy and it +connects to the backend on behalf of nghttpx. This +forms tunnel. After that, nghttpx performs SSL/TLS +handshake with the downstream through the tunnel. The +timeouts when connecting and making CONNECT request can +be specified by \fI\%\-\-backend\-read\-timeout\fP and +\fI\%\-\-backend\-write\-timeout\fP options. +.UNINDENT +.SS Performance +.INDENT 0.0 +.TP +.B \-n, \-\-workers= +Set the number of worker threads. +.sp +Default: \fB1\fP +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-read\-rate= +Set maximum average read rate on frontend connection. +Setting 0 to this option means read rate is unlimited. +.sp +Default: \fB0\fP +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-read\-burst= +Set maximum read burst size on frontend connection. +Setting 0 to this option means read burst size is +unlimited. +.sp +Default: \fB0\fP +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-write\-rate= +Set maximum average write rate on frontend connection. +Setting 0 to this option means write rate is unlimited. +.sp +Default: \fB0\fP +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-write\-burst= +Set maximum write burst size on frontend connection. +Setting 0 to this option means write burst size is +unlimited. +.sp +Default: \fB0\fP +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-worker\-read\-rate= +Set maximum average read rate on frontend connection per +worker. Setting 0 to this option means read rate is +unlimited. Not implemented yet. +.sp +Default: \fB0\fP +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-worker\-read\-burst= +Set maximum read burst size on frontend connection per +worker. Setting 0 to this option means read burst size +is unlimited. Not implemented yet. +.sp +Default: \fB0\fP +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-worker\-write\-rate= +Set maximum average write rate on frontend connection +per worker. Setting 0 to this option means write rate +is unlimited. Not implemented yet. +.sp +Default: \fB0\fP +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-worker\-write\-burst= +Set maximum write burst size on frontend connection per +worker. Setting 0 to this option means write burst size +is unlimited. Not implemented yet. +.sp +Default: \fB0\fP +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-worker\-frontend\-connections= +Set maximum number of simultaneous connections frontend +accepts. Setting 0 means unlimited. +.sp +Default: \fB0\fP +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-backend\-http2\-connections\-per\-worker= +Set maximum number of HTTP/2 connections per worker. +The default value is 0, which means the number of +backend addresses specified by \fI\%\-b\fP option. +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-backend\-http1\-connections\-per\-host= +Set maximum number of backend concurrent HTTP/1 +connections per host. This option is meaningful when \fI\%\-s\fP +option is used. To limit the number of connections per +frontend for default mode, use +\fI\%\-\-backend\-http1\-connections\-per\-frontend\fP\&. +.sp +Default: \fB8\fP +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-backend\-http1\-connections\-per\-frontend= +Set maximum number of backend concurrent HTTP/1 +connections per frontend. This option is only used for +default mode. 0 means unlimited. To limit the number +of connections per host for HTTP/2 or SPDY proxy mode +(\-s option), use \fI\%\-\-backend\-http1\-connections\-per\-host\fP\&. +.sp +Default: \fB0\fP +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-rlimit\-nofile= +Set maximum number of open files (RLIMIT_NOFILE) to . +If 0 is given, nghttpx does not set the limit. +.sp +Default: \fB0\fP +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-backend\-request\-buffer= +Set buffer size used to store backend request. +.sp +Default: \fB16K\fP +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-backend\-response\-buffer= +Set buffer size used to store backend response. +.sp +Default: \fB16K\fP +.UNINDENT +.SS Timeout +.INDENT 0.0 +.TP +.B \-\-frontend\-http2\-read\-timeout= +Specify read timeout for HTTP/2 and SPDY frontend +connection. +.sp +Default: \fB3m\fP +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-frontend\-read\-timeout= +Specify read timeout for HTTP/1.1 frontend connection. +.sp +Default: \fB3m\fP +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-frontend\-write\-timeout= +Specify write timeout for all frontend connections. +.sp +Default: \fB30s\fP +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-stream\-read\-timeout= +Specify read timeout for HTTP/2 and SPDY streams. 0 +means no timeout. +.sp +Default: \fB0\fP +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-stream\-write\-timeout= +Specify write timeout for HTTP/2 and SPDY streams. 0 +means no timeout. +.sp +Default: \fB0\fP +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-backend\-read\-timeout= +Specify read timeout for backend connection. +.sp +Default: \fB3m\fP +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-backend\-write\-timeout= +Specify write timeout for backend connection. +.sp +Default: \fB30s\fP +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-backend\-keep\-alive\-timeout= +Specify keep\-alive timeout for backend connection. +.sp +Default: \fB2s\fP +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-listener\-disable\-timeout= +After accepting connection failed, connection listener +is disabled for a given amount of time. Specifying 0 +disables this feature. +.sp +Default: \fB0\fP +.UNINDENT +.SS SSL/TLS +.INDENT 0.0 +.TP +.B \-\-ciphers= +Set allowed cipher list. The format of the string is +described in OpenSSL ciphers(1). +.UNINDENT +.INDENT 0.0 +.TP +.B \-k, \-\-insecure +Don\(aqt verify backend server\(aqs certificate if \fI\%\-p\fP, +\fI\%\-\-client\fP or \fI\%\-\-http2\-bridge\fP are given and +\fI\%\-\-backend\-no\-tls\fP is not given. +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-cacert= +Set path to trusted CA certificate file if \fI\%\-p\fP, \fI\%\-\-client\fP +or \fI\%\-\-http2\-bridge\fP are given and \fI\%\-\-backend\-no\-tls\fP is not +given. The file must be in PEM format. It can contain +multiple certificates. If the linked OpenSSL is +configured to load system wide certificates, they are +loaded at startup regardless of this option. +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-private\-key\-passwd\-file= +Path to file that contains password for the server\(aqs +private key. If none is given and the private key is +password protected it\(aqll be requested interactively. +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-subcert=: +Specify additional certificate and private key file. +nghttpx will choose certificates based on the hostname +indicated by client using TLS SNI extension. This +option can be used multiple times. To make OCSP +stapling work, must be absolute path. +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-backend\-tls\-sni\-field= +Explicitly set the content of the TLS SNI extension. +This will default to the backend HOST name. +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-dh\-param\-file= +Path to file that contains DH parameters in PEM format. +Without this option, DHE cipher suites are not +available. +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-npn\-list= +Comma delimited list of ALPN protocol identifier sorted +in the order of preference. That means most desirable +protocol comes first. This is used in both ALPN and +NPN. The parameter must be delimited by a single comma +only and any white spaces are treated as a part of +protocol string. +.sp +Default: \fBh2,h2\-16,h2\-14,spdy/3.1,http/1.1\fP +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-verify\-client +Require and verify client certificate. +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-verify\-client\-cacert= +Path to file that contains CA certificates to verify +client certificate. The file must be in PEM format. It +can contain multiple certificates. +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-client\-private\-key\-file= +Path to file that contains client private key used in +backend client authentication. +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-client\-cert\-file= +Path to file that contains client certificate used in +backend client authentication. +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-tls\-proto\-list= +Comma delimited list of SSL/TLS protocol to be enabled. +The following protocols are available: TLSv1.2, TLSv1.1 +and TLSv1.0. The name matching is done in +case\-insensitive manner. The parameter must be +delimited by a single comma only and any white spaces +are treated as a part of protocol string. +.sp +Default: \fBTLSv1.2,TLSv1.1\fP +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-tls\-ticket\-key\-file= +Path to file that contains 48 bytes random data to +construct TLS session ticket parameters. This options +can be used repeatedly to specify multiple ticket +parameters. If several files are given, only the first +key is used to encrypt TLS session tickets. Other keys +are accepted but server will issue new session ticket +with first key. This allows session key rotation. +Please note that key rotation does not occur +automatically. User should rearrange files or change +options values and restart nghttpx gracefully. If +opening or reading given file fails, all loaded keys are +discarded and it is treated as if none of this option is +given. If this option is not given or an error occurred +while opening or reading a file, key is generated +automatically and renewed every 12hrs. At most 2 keys +are stored in memory. +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-fetch\-ocsp\-response\-file= +Path to fetch\-ocsp\-response script file. It should be +absolute path. +.sp +Default: \fB/usr/local/share/nghttp2/fetch\-ocsp\-response\fP +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-ocsp\-update\-interval= +Set interval to update OCSP response cache. +.sp +Default: \fB4h\fP +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-no\-ocsp +Disable OCSP stapling. +.UNINDENT +.SS HTTP/2 and SPDY +.INDENT 0.0 +.TP +.B \-c, \-\-http2\-max\-concurrent\-streams= +Set the maximum number of the concurrent streams in one +HTTP/2 and SPDY session. +.sp +Default: \fB100\fP +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-frontend\-http2\-window\-bits= +Sets the per\-stream initial window size of HTTP/2 SPDY +frontend connection. For HTTP/2, the size is 2**\-1. +For SPDY, the size is 2**. +.sp +Default: \fB16\fP +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-frontend\-http2\-connection\-window\-bits= +Sets the per\-connection window size of HTTP/2 and SPDY +frontend connection. For HTTP/2, the size is +2**\-1. For SPDY, the size is 2**. +.sp +Default: \fB16\fP +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-frontend\-no\-tls +Disable SSL/TLS on frontend connections. +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-backend\-http2\-window\-bits= +Sets the initial window size of HTTP/2 backend +connection to 2**\-1. +.sp +Default: \fB16\fP +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-backend\-http2\-connection\-window\-bits= +Sets the per\-connection window size of HTTP/2 backend +connection to 2**\-1. +.sp +Default: \fB16\fP +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-backend\-no\-tls +Disable SSL/TLS on backend connections. +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-http2\-no\-cookie\-crumbling +Don\(aqt crumble cookie header field. +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-padding= +Add at most bytes to a HTTP/2 frame payload as +padding. Specify 0 to disable padding. This option is +meant for debugging purpose and not intended to enhance +protocol security. +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-no\-server\-push +Disable HTTP/2 server push. Server push is only +supported by default mode and HTTP/2 frontend. SPDY +frontend does not support server push. +.UNINDENT +.SS Mode +.INDENT 0.0 +.TP +.B (default mode) +Accept HTTP/2, SPDY and HTTP/1.1 over SSL/TLS. If +\fI\%\-\-frontend\-no\-tls\fP is used, accept HTTP/2 and HTTP/1.1. +The incoming HTTP/1.1 connection can be upgraded to +HTTP/2 through HTTP Upgrade. The protocol to the +backend is HTTP/1.1. +.UNINDENT +.INDENT 0.0 +.TP +.B \-s, \-\-http2\-proxy +Like default mode, but enable secure proxy mode. +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-http2\-bridge +Like default mode, but communicate with the backend in +HTTP/2 over SSL/TLS. Thus the incoming all connections +are converted to HTTP/2 connection and relayed to the +backend. See \fI\%\-\-backend\-http\-proxy\-uri\fP option if you are +behind the proxy and want to connect to the outside +HTTP/2 proxy. +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-client +Accept HTTP/2 and HTTP/1.1 without SSL/TLS. The +incoming HTTP/1.1 connection can be upgraded to HTTP/2 +connection through HTTP Upgrade. The protocol to the +backend is HTTP/2. To use nghttpx as a forward proxy, +use \fI\%\-p\fP option instead. +.UNINDENT +.INDENT 0.0 +.TP +.B \-p, \-\-client\-proxy +Like \fI\%\-\-client\fP option, but it also requires the request +path from frontend must be an absolute URI, suitable for +use as a forward proxy. +.UNINDENT +.SS Logging +.INDENT 0.0 +.TP +.B \-L, \-\-log\-level= +Set the severity level of log output. must be +one of INFO, NOTICE, WARN, ERROR and FATAL. +.sp +Default: \fBNOTICE\fP +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-accesslog\-file= +Set path to write access log. To reopen file, send USR1 +signal to nghttpx. +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-accesslog\-syslog +Send access log to syslog. If this option is used, +\fI\%\-\-accesslog\-file\fP option is ignored. +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-accesslog\-format= +Specify format string for access log. The default +format is combined format. The following variables are +available: +.INDENT 7.0 +.IP \(bu 2 +$remote_addr: client IP address. +.IP \(bu 2 +$time_local: local time in Common Log format. +.IP \(bu 2 +$time_iso8601: local time in ISO 8601 format. +.IP \(bu 2 +$request: HTTP request line. +.IP \(bu 2 +$status: HTTP response status code. +.IP \(bu 2 +$body_bytes_sent: the number of bytes sent to client +as response body. +.IP \(bu 2 +$http_: value of HTTP request header where +\(aq_\(aq in is replaced with \(aq\-\(aq. +.IP \(bu 2 +$remote_port: client port. +.IP \(bu 2 +$server_port: server port. +.IP \(bu 2 +$request_time: request processing time in seconds with +milliseconds resolution. +.IP \(bu 2 +$pid: PID of the running process. +.IP \(bu 2 +$alpn: ALPN identifier of the protocol which generates +the response. For HTTP/1, ALPN is always http/1.1, +regardless of minor version. +.UNINDENT +.sp +Default: \fB$remote_addr \- \- [$time_local] "$request" $status $body_bytes_sent "$http_referer" "$http_user_agent"\fP +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-errorlog\-file= +Set path to write error log. To reopen file, send USR1 +signal to nghttpx. stderr will be redirected to the +error log file unless \fI\%\-\-errorlog\-syslog\fP is used. +.sp +Default: \fB/dev/stderr\fP +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-errorlog\-syslog +Send error log to syslog. If this option is used, +\fI\%\-\-errorlog\-file\fP option is ignored. +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-syslog\-facility= +Set syslog facility to . +.sp +Default: \fBdaemon\fP +.UNINDENT +.SS HTTP +.INDENT 0.0 +.TP +.B \-\-add\-x\-forwarded\-for +Append X\-Forwarded\-For header field to the downstream +request. +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-strip\-incoming\-x\-forwarded\-for +Strip X\-Forwarded\-For header field from inbound client +requests. +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-no\-via +Don\(aqt append to Via header field. If Via header field +is received, it is left unaltered. +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-no\-location\-rewrite +Don\(aqt rewrite location header field on \fI\%\-\-http2\-bridge\fP, +\fI\%\-\-client\fP and default mode. For \fI\%\-\-http2\-proxy\fP and +\fI\%\-\-client\-proxy\fP mode, location header field will not be +altered regardless of this option. +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-no\-host\-rewrite +Don\(aqt rewrite host and :authority header fields on +\fI\%\-\-http2\-bridge\fP, \fI\%\-\-client\fP and default mode. For +\fI\%\-\-http2\-proxy\fP and \fI\%\-\-client\-proxy\fP mode, these headers +will not be altered regardless of this option. +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-altsvc= +Specify protocol ID, port, host and origin of +alternative service. and are optional. +They are advertised in alt\-svc header field only in +HTTP/1.1 frontend. This option can be used multiple +times to specify multiple alternative services. +Example: \fI\%\-\-altsvc\fP=h2,443 +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-add\-response\-header=
    +Specify additional header field to add to response +header set. This option just appends header field and +won\(aqt replace anything already set. This option can be +used several times to specify multiple header fields. +Example: \fI\%\-\-add\-response\-header\fP="foo: bar" +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-header\-field\-buffer= +Set maximum buffer size for incoming HTTP header field +list. This is the sum of header name and value in +bytes. +.sp +Default: \fB64K\fP +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-max\-header\-fields= +Set maximum number of incoming HTTP header fields, which +appear in one request or response header field list. +.sp +Default: \fB100\fP +.UNINDENT +.SS Debug +.INDENT 0.0 +.TP +.B \-\-frontend\-http2\-dump\-request\-header= +Dumps request headers received by HTTP/2 frontend to the +file denoted in . The output is done in HTTP/1 +header field format and each header block is followed by +an empty line. This option is not thread safe and MUST +NOT be used with option \fI\%\-n\fP, where >= 2. +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-frontend\-http2\-dump\-response\-header= +Dumps response headers sent from HTTP/2 frontend to the +file denoted in . The output is done in HTTP/1 +header field format and each header block is followed by +an empty line. This option is not thread safe and MUST +NOT be used with option \fI\%\-n\fP, where >= 2. +.UNINDENT +.INDENT 0.0 +.TP +.B \-o, \-\-frontend\-frame\-debug +Print HTTP/2 frames in frontend to stderr. This option +is not thread safe and MUST NOT be used with option +\fI\%\-n\fP=N, where N >= 2. +.UNINDENT +.SS Process +.INDENT 0.0 +.TP +.B \-D, \-\-daemon +Run in a background. If \fI\%\-D\fP is used, the current working +directory is changed to \(aq\fI/\fP\(aq. +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-pid\-file= +Set path to save PID of this program. +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-user= +Run this program as . This option is intended to +be used to drop root privileges. +.UNINDENT +.SS Misc +.INDENT 0.0 +.TP +.B \-\-conf= +Load configuration from . +.sp +Default: \fB/etc/nghttpx/nghttpx.conf\fP +.UNINDENT +.INDENT 0.0 +.TP +.B \-v, \-\-version +Print version and exit. +.UNINDENT +.INDENT 0.0 +.TP +.B \-h, \-\-help +Print this help and exit. +.UNINDENT +.sp +The argument is an integer and an optional unit (e.g., 10K is +10 * 1024). Units are K, M and G (powers of 1024). +.sp +The argument is an integer and an optional unit (e.g., 1s +is 1 second and 500ms is 500 milliseconds). Units are h, m, s or ms +(hours, minutes, seconds and milliseconds, respectively). If a unit +is omitted, a second is used as unit. +.SH FILES +.INDENT 0.0 +.TP +.B \fI/etc/nghttpx/nghttpx.conf\fP +The default configuration file path nghttpx searches at startup. +The configuration file path can be changed using \fI\%\-\-conf\fP +option. +.sp +Those lines which are staring \fB#\fP are treated as comment. +.sp +The option name in the configuration file is the long command\-line +option name with leading \fB\-\-\fP stripped (e.g., \fBfrontend\fP). Put +\fB=\fP between option name and value. Don\(aqt put extra leading or +trailing spaces. +.sp +The options which do not take argument in the command\-line \fItake\fP +argument in the configuration file. Specify \fByes\fP as an argument +(e.g., \fBhttp2\-proxy=yes\fP). If other string is given, it is +ignored. +.sp +To specify private key and certificate file which are given as +positional arguments in command\-line, use \fBprivate\-key\-file\fP and +\fBcertificate\-file\fP\&. +.sp +\fI\%\-\-conf\fP option cannot be used in the configuration file and +will be ignored if specified. +.UNINDENT +.SH SIGNALS +.INDENT 0.0 +.TP +.B SIGQUIT +Shutdown gracefully. First accept pending connections and stop +accepting connection. After all connections are handled, nghttpx +exits. +.TP +.B SIGUSR1 +Reopen log files. +.TP +.B SIGUSR2 +Fork and execute nghttpx. It will execute the binary in the same +path with same command\-line arguments and environment variables. +After new process comes up, sending SIGQUIT to the original process +to perform hot swapping. +.UNINDENT +.SH SERVER PUSH +.sp +nghttpx supports HTTP/2 server push in default mode. nghttpx looks +for Link header field (\fI\%RFC 5988\fP) in response headers from +backend server and extracts URI\-reference with parameter +\fBrel=preload\fP (see \fI\%preload\fP) +and pushes those URIs to the frontend client. Here is a sample Link +header field to initiate server push: +.INDENT 0.0 +.INDENT 3.5 +.sp +.nf +.ft C +Link: ; rel=preload +Link: ; rel=preload +.ft P +.fi +.UNINDENT +.UNINDENT +.sp +Currently, the following restrictions are applied for server push: +.INDENT 0.0 +.IP 1. 3 +URI\-reference must not contain authority. If it exists, it is not +pushed. \fB/fonts/font.woff\fP and \fBcss/theme.css\fP are eligible to +be pushed. \fBhttps://example.org/fonts/font.woff\fP and +\fB//example.org/css/theme.css\fP are not. +.IP 2. 3 +The associated stream must have method "GET" or "POST". The +associated stream\(aqs status code must be 200. +.UNINDENT +.sp +These limitations may be loosened in the future release. +.SH UNIX DOMAIN SOCKET +.sp +nghttpx supports UNIX domain socket with a filename for both frontend +and backend connections. +.sp +Please note that current nghttpx implementation does not delete a +socket with a filename. And on start up, if nghttpx detects that the +specified socket already exists in the file system, nghttpx first +deletes it. However, if SIGUSR2 is used to execute new binary and +both old and new configurations use same filename, new binary does not +delete the socket and continues to use it. +.SH OCSP STAPLING +.sp +OCSP query is done using external perl script \fBfetch\-ocsp\-response\fP, +which has been developed as part of h2o project +(\fI\%https://github.com/h2o/h2o\fP). +.sp +The script file is usually installed under +\fB$(prefix)/share/nghttp2/\fP directory. The actual path to script can +be customized using \fI\%\-\-fetch\-ocsp\-response\-file\fP option. +.SH SEE ALSO +.sp +\fInghttp(1)\fP, \fInghttpd(1)\fP, \fIh2load(1)\fP +.SH AUTHOR +Tatsuhiro Tsujikawa +.SH COPYRIGHT +2012, 2015, Tatsuhiro Tsujikawa +.\" Generated by docutils manpage writer. +. diff --git a/doc/nghttpx.1.rst b/doc/nghttpx.1.rst new file mode 100644 index 0000000..ad3f915 --- /dev/null +++ b/doc/nghttpx.1.rst @@ -0,0 +1,811 @@ + +.. GENERATED by help2rst.py. DO NOT EDIT DIRECTLY. + +.. program:: nghttpx + +nghttpx(1) +========== + +SYNOPSIS +-------- + +**nghttpx** [OPTIONS]... [ ] + +DESCRIPTION +----------- + +A reverse proxy for HTTP/2, HTTP/1 and SPDY. + +.. describe:: + + + Set path to server's private key. Required unless :option:`-p`\, + :option:`--client` or :option:`\--frontend-no-tls` are given. + +.. describe:: + + Set path to server's certificate. Required unless :option:`-p`\, + :option:`--client` or :option:`\--frontend-no-tls` are given. To make OCSP + stapling work, this must be absolute path. + + +OPTIONS +------- + +The options are categorized into several groups. + +Connections +~~~~~~~~~~~ + +.. option:: -b, --backend= + + Set backend host and port. The multiple backend + addresses are accepted by repeating this option. UNIX + domain socket can be specified by prefixing path name + with "unix:" (e.g., unix:/var/run/backend.sock) + + Default: ``127.0.0.1,80`` + +.. option:: -f, --frontend= + + Set frontend host and port. If is '\*', it + assumes all addresses including both IPv4 and IPv6. + UNIX domain socket can be specified by prefixing path + name with "unix:" (e.g., unix:/var/run/nghttpx.sock) + + Default: ``*,3000`` + +.. option:: --backlog= + + Set listen backlog size. + + Default: ``512`` + +.. option:: --backend-ipv4 + + Resolve backend hostname to IPv4 address only. + +.. option:: --backend-ipv6 + + Resolve backend hostname to IPv6 address only. + +.. option:: --backend-http-proxy-uri= + + Specify proxy URI in the form + http://[:@]:. If a proxy + requires authentication, specify and . + Note that they must be properly percent-encoded. This + proxy is used when the backend connection is HTTP/2. + First, make a CONNECT request to the proxy and it + connects to the backend on behalf of nghttpx. This + forms tunnel. After that, nghttpx performs SSL/TLS + handshake with the downstream through the tunnel. The + timeouts when connecting and making CONNECT request can + be specified by :option:`--backend-read-timeout` and + :option:`--backend-write-timeout` options. + + +Performance +~~~~~~~~~~~ + +.. option:: -n, --workers= + + Set the number of worker threads. + + Default: ``1`` + +.. option:: --read-rate= + + Set maximum average read rate on frontend connection. + Setting 0 to this option means read rate is unlimited. + + Default: ``0`` + +.. option:: --read-burst= + + Set maximum read burst size on frontend connection. + Setting 0 to this option means read burst size is + unlimited. + + Default: ``0`` + +.. option:: --write-rate= + + Set maximum average write rate on frontend connection. + Setting 0 to this option means write rate is unlimited. + + Default: ``0`` + +.. option:: --write-burst= + + Set maximum write burst size on frontend connection. + Setting 0 to this option means write burst size is + unlimited. + + Default: ``0`` + +.. option:: --worker-read-rate= + + Set maximum average read rate on frontend connection per + worker. Setting 0 to this option means read rate is + unlimited. Not implemented yet. + + Default: ``0`` + +.. option:: --worker-read-burst= + + Set maximum read burst size on frontend connection per + worker. Setting 0 to this option means read burst size + is unlimited. Not implemented yet. + + Default: ``0`` + +.. option:: --worker-write-rate= + + Set maximum average write rate on frontend connection + per worker. Setting 0 to this option means write rate + is unlimited. Not implemented yet. + + Default: ``0`` + +.. option:: --worker-write-burst= + + Set maximum write burst size on frontend connection per + worker. Setting 0 to this option means write burst size + is unlimited. Not implemented yet. + + Default: ``0`` + +.. option:: --worker-frontend-connections= + + Set maximum number of simultaneous connections frontend + accepts. Setting 0 means unlimited. + + Default: ``0`` + +.. option:: --backend-http2-connections-per-worker= + + Set maximum number of HTTP/2 connections per worker. + The default value is 0, which means the number of + backend addresses specified by :option:`-b` option. + +.. option:: --backend-http1-connections-per-host= + + Set maximum number of backend concurrent HTTP/1 + connections per host. This option is meaningful when :option:`-s` + option is used. To limit the number of connections per + frontend for default mode, use + :option:`--backend-http1-connections-per-frontend`\. + + Default: ``8`` + +.. option:: --backend-http1-connections-per-frontend= + + Set maximum number of backend concurrent HTTP/1 + connections per frontend. This option is only used for + default mode. 0 means unlimited. To limit the number + of connections per host for HTTP/2 or SPDY proxy mode + (-s option), use :option:`--backend-http1-connections-per-host`\. + + Default: ``0`` + +.. option:: --rlimit-nofile= + + Set maximum number of open files (RLIMIT_NOFILE) to . + If 0 is given, nghttpx does not set the limit. + + Default: ``0`` + +.. option:: --backend-request-buffer= + + Set buffer size used to store backend request. + + Default: ``16K`` + +.. option:: --backend-response-buffer= + + Set buffer size used to store backend response. + + Default: ``16K`` + + +Timeout +~~~~~~~ + +.. option:: --frontend-http2-read-timeout= + + Specify read timeout for HTTP/2 and SPDY frontend + connection. + + Default: ``3m`` + +.. option:: --frontend-read-timeout= + + Specify read timeout for HTTP/1.1 frontend connection. + + Default: ``3m`` + +.. option:: --frontend-write-timeout= + + Specify write timeout for all frontend connections. + + Default: ``30s`` + +.. option:: --stream-read-timeout= + + Specify read timeout for HTTP/2 and SPDY streams. 0 + means no timeout. + + Default: ``0`` + +.. option:: --stream-write-timeout= + + Specify write timeout for HTTP/2 and SPDY streams. 0 + means no timeout. + + Default: ``0`` + +.. option:: --backend-read-timeout= + + Specify read timeout for backend connection. + + Default: ``3m`` + +.. option:: --backend-write-timeout= + + Specify write timeout for backend connection. + + Default: ``30s`` + +.. option:: --backend-keep-alive-timeout= + + Specify keep-alive timeout for backend connection. + + Default: ``2s`` + +.. option:: --listener-disable-timeout= + + After accepting connection failed, connection listener + is disabled for a given amount of time. Specifying 0 + disables this feature. + + Default: ``0`` + + +SSL/TLS +~~~~~~~ + +.. option:: --ciphers= + + Set allowed cipher list. The format of the string is + described in OpenSSL ciphers(1). + +.. option:: -k, --insecure + + Don't verify backend server's certificate if :option:`-p`\, + :option:`--client` or :option:`\--http2-bridge` are given and + :option:`--backend-no-tls` is not given. + +.. option:: --cacert= + + Set path to trusted CA certificate file if :option:`-p`\, :option:`--client` + or :option:`--http2-bridge` are given and :option:`\--backend-no-tls` is not + given. The file must be in PEM format. It can contain + multiple certificates. If the linked OpenSSL is + configured to load system wide certificates, they are + loaded at startup regardless of this option. + +.. option:: --private-key-passwd-file= + + Path to file that contains password for the server's + private key. If none is given and the private key is + password protected it'll be requested interactively. + +.. option:: --subcert=: + + Specify additional certificate and private key file. + nghttpx will choose certificates based on the hostname + indicated by client using TLS SNI extension. This + option can be used multiple times. To make OCSP + stapling work, must be absolute path. + +.. option:: --backend-tls-sni-field= + + Explicitly set the content of the TLS SNI extension. + This will default to the backend HOST name. + +.. option:: --dh-param-file= + + Path to file that contains DH parameters in PEM format. + Without this option, DHE cipher suites are not + available. + +.. option:: --npn-list= + + Comma delimited list of ALPN protocol identifier sorted + in the order of preference. That means most desirable + protocol comes first. This is used in both ALPN and + NPN. The parameter must be delimited by a single comma + only and any white spaces are treated as a part of + protocol string. + + Default: ``h2,h2-16,h2-14,spdy/3.1,http/1.1`` + +.. option:: --verify-client + + Require and verify client certificate. + +.. option:: --verify-client-cacert= + + Path to file that contains CA certificates to verify + client certificate. The file must be in PEM format. It + can contain multiple certificates. + +.. option:: --client-private-key-file= + + Path to file that contains client private key used in + backend client authentication. + +.. option:: --client-cert-file= + + Path to file that contains client certificate used in + backend client authentication. + +.. option:: --tls-proto-list= + + Comma delimited list of SSL/TLS protocol to be enabled. + The following protocols are available: TLSv1.2, TLSv1.1 + and TLSv1.0. The name matching is done in + case-insensitive manner. The parameter must be + delimited by a single comma only and any white spaces + are treated as a part of protocol string. + + Default: ``TLSv1.2,TLSv1.1`` + +.. option:: --tls-ticket-key-file= + + Path to file that contains 48 bytes random data to + construct TLS session ticket parameters. This options + can be used repeatedly to specify multiple ticket + parameters. If several files are given, only the first + key is used to encrypt TLS session tickets. Other keys + are accepted but server will issue new session ticket + with first key. This allows session key rotation. + Please note that key rotation does not occur + automatically. User should rearrange files or change + options values and restart nghttpx gracefully. If + opening or reading given file fails, all loaded keys are + discarded and it is treated as if none of this option is + given. If this option is not given or an error occurred + while opening or reading a file, key is generated + automatically and renewed every 12hrs. At most 2 keys + are stored in memory. + +.. option:: --fetch-ocsp-response-file= + + Path to fetch-ocsp-response script file. It should be + absolute path. + + Default: ``/usr/local/share/nghttp2/fetch-ocsp-response`` + +.. option:: --ocsp-update-interval= + + Set interval to update OCSP response cache. + + Default: ``4h`` + +.. option:: --no-ocsp + + Disable OCSP stapling. + + +HTTP/2 and SPDY +~~~~~~~~~~~~~~~ + +.. option:: -c, --http2-max-concurrent-streams= + + Set the maximum number of the concurrent streams in one + HTTP/2 and SPDY session. + + Default: ``100`` + +.. option:: --frontend-http2-window-bits= + + Sets the per-stream initial window size of HTTP/2 SPDY + frontend connection. For HTTP/2, the size is 2\*\*-1. + For SPDY, the size is 2\*\*. + + Default: ``16`` + +.. option:: --frontend-http2-connection-window-bits= + + Sets the per-connection window size of HTTP/2 and SPDY + frontend connection. For HTTP/2, the size is + 2**-1. For SPDY, the size is 2\*\*. + + Default: ``16`` + +.. option:: --frontend-no-tls + + Disable SSL/TLS on frontend connections. + +.. option:: --backend-http2-window-bits= + + Sets the initial window size of HTTP/2 backend + connection to 2\*\*-1. + + Default: ``16`` + +.. option:: --backend-http2-connection-window-bits= + + Sets the per-connection window size of HTTP/2 backend + connection to 2\*\*-1. + + Default: ``16`` + +.. option:: --backend-no-tls + + Disable SSL/TLS on backend connections. + +.. option:: --http2-no-cookie-crumbling + + Don't crumble cookie header field. + +.. option:: --padding= + + Add at most bytes to a HTTP/2 frame payload as + padding. Specify 0 to disable padding. This option is + meant for debugging purpose and not intended to enhance + protocol security. + +.. option:: --no-server-push + + Disable HTTP/2 server push. Server push is only + supported by default mode and HTTP/2 frontend. SPDY + frontend does not support server push. + + +Mode +~~~~ + +.. describe:: (default mode) + + + Accept HTTP/2, SPDY and HTTP/1.1 over SSL/TLS. If + :option:`--frontend-no-tls` is used, accept HTTP/2 and HTTP/1.1. + The incoming HTTP/1.1 connection can be upgraded to + HTTP/2 through HTTP Upgrade. The protocol to the + backend is HTTP/1.1. + +.. option:: -s, --http2-proxy + + Like default mode, but enable secure proxy mode. + +.. option:: --http2-bridge + + Like default mode, but communicate with the backend in + HTTP/2 over SSL/TLS. Thus the incoming all connections + are converted to HTTP/2 connection and relayed to the + backend. See :option:`--backend-http-proxy-uri` option if you are + behind the proxy and want to connect to the outside + HTTP/2 proxy. + +.. option:: --client + + Accept HTTP/2 and HTTP/1.1 without SSL/TLS. The + incoming HTTP/1.1 connection can be upgraded to HTTP/2 + connection through HTTP Upgrade. The protocol to the + backend is HTTP/2. To use nghttpx as a forward proxy, + use :option:`-p` option instead. + +.. option:: -p, --client-proxy + + Like :option:`--client` option, but it also requires the request + path from frontend must be an absolute URI, suitable for + use as a forward proxy. + + +Logging +~~~~~~~ + +.. option:: -L, --log-level= + + Set the severity level of log output. must be + one of INFO, NOTICE, WARN, ERROR and FATAL. + + Default: ``NOTICE`` + +.. option:: --accesslog-file= + + Set path to write access log. To reopen file, send USR1 + signal to nghttpx. + +.. option:: --accesslog-syslog + + Send access log to syslog. If this option is used, + :option:`--accesslog-file` option is ignored. + +.. option:: --accesslog-format= + + Specify format string for access log. The default + format is combined format. The following variables are + available: + + * $remote_addr: client IP address. + * $time_local: local time in Common Log format. + * $time_iso8601: local time in ISO 8601 format. + * $request: HTTP request line. + * $status: HTTP response status code. + * $body_bytes_sent: the number of bytes sent to client + as response body. + * $http_: value of HTTP request header where + '_' in is replaced with '-'. + * $remote_port: client port. + * $server_port: server port. + * $request_time: request processing time in seconds with + milliseconds resolution. + * $pid: PID of the running process. + * $alpn: ALPN identifier of the protocol which generates + the response. For HTTP/1, ALPN is always http/1.1, + regardless of minor version. + + + Default: ``$remote_addr - - [$time_local] "$request" $status $body_bytes_sent "$http_referer" "$http_user_agent"`` + +.. option:: --errorlog-file= + + Set path to write error log. To reopen file, send USR1 + signal to nghttpx. stderr will be redirected to the + error log file unless :option:`--errorlog-syslog` is used. + + Default: ``/dev/stderr`` + +.. option:: --errorlog-syslog + + Send error log to syslog. If this option is used, + :option:`--errorlog-file` option is ignored. + +.. option:: --syslog-facility= + + Set syslog facility to . + + Default: ``daemon`` + + +HTTP +~~~~ + +.. option:: --add-x-forwarded-for + + Append X-Forwarded-For header field to the downstream + request. + +.. option:: --strip-incoming-x-forwarded-for + + Strip X-Forwarded-For header field from inbound client + requests. + +.. option:: --no-via + + Don't append to Via header field. If Via header field + is received, it is left unaltered. + +.. option:: --no-location-rewrite + + Don't rewrite location header field on :option:`--http2-bridge`\, + :option:`--client` and default mode. For :option:`\--http2-proxy` and + :option:`--client-proxy` mode, location header field will not be + altered regardless of this option. + +.. option:: --no-host-rewrite + + Don't rewrite host and :authority header fields on + :option:`--http2-bridge`\, :option:`--client` and default mode. For + :option:`--http2-proxy` and :option:`\--client-proxy` mode, these headers + will not be altered regardless of this option. + +.. option:: --altsvc= + + Specify protocol ID, port, host and origin of + alternative service. and are optional. + They are advertised in alt-svc header field only in + HTTP/1.1 frontend. This option can be used multiple + times to specify multiple alternative services. + Example: :option:`--altsvc`\=h2,443 + +.. option:: --add-response-header=
    + + Specify additional header field to add to response + header set. This option just appends header field and + won't replace anything already set. This option can be + used several times to specify multiple header fields. + Example: :option:`--add-response-header`\="foo: bar" + +.. option:: --header-field-buffer= + + Set maximum buffer size for incoming HTTP header field + list. This is the sum of header name and value in + bytes. + + Default: ``64K`` + +.. option:: --max-header-fields= + + Set maximum number of incoming HTTP header fields, which + appear in one request or response header field list. + + Default: ``100`` + + +Debug +~~~~~ + +.. option:: --frontend-http2-dump-request-header= + + Dumps request headers received by HTTP/2 frontend to the + file denoted in . The output is done in HTTP/1 + header field format and each header block is followed by + an empty line. This option is not thread safe and MUST + NOT be used with option :option:`-n`\, where >= 2. + +.. option:: --frontend-http2-dump-response-header= + + Dumps response headers sent from HTTP/2 frontend to the + file denoted in . The output is done in HTTP/1 + header field format and each header block is followed by + an empty line. This option is not thread safe and MUST + NOT be used with option :option:`-n`\, where >= 2. + +.. option:: -o, --frontend-frame-debug + + Print HTTP/2 frames in frontend to stderr. This option + is not thread safe and MUST NOT be used with option + :option:`-n`\=N, where N >= 2. + + +Process +~~~~~~~ + +.. option:: -D, --daemon + + Run in a background. If :option:`-D` is used, the current working + directory is changed to '*/*'. + +.. option:: --pid-file= + + Set path to save PID of this program. + +.. option:: --user= + + Run this program as . This option is intended to + be used to drop root privileges. + + +Misc +~~~~ + +.. option:: --conf= + + Load configuration from . + + Default: ``/etc/nghttpx/nghttpx.conf`` + +.. option:: -v, --version + + Print version and exit. + +.. option:: -h, --help + + Print this help and exit. + + + +The argument is an integer and an optional unit (e.g., 10K is +10 * 1024). Units are K, M and G (powers of 1024). + +The argument is an integer and an optional unit (e.g., 1s +is 1 second and 500ms is 500 milliseconds). Units are h, m, s or ms +(hours, minutes, seconds and milliseconds, respectively). If a unit +is omitted, a second is used as unit. + +FILES +----- + +*/etc/nghttpx/nghttpx.conf* + The default configuration file path nghttpx searches at startup. + The configuration file path can be changed using :option:`--conf` + option. + + Those lines which are staring ``#`` are treated as comment. + + The option name in the configuration file is the long command-line + option name with leading ``--`` stripped (e.g., ``frontend``). Put + ``=`` between option name and value. Don't put extra leading or + trailing spaces. + + The options which do not take argument in the command-line *take* + argument in the configuration file. Specify ``yes`` as an argument + (e.g., ``http2-proxy=yes``). If other string is given, it is + ignored. + + To specify private key and certificate file which are given as + positional arguments in command-line, use ``private-key-file`` and + ``certificate-file``. + + :option:`--conf` option cannot be used in the configuration file and + will be ignored if specified. + +SIGNALS +------- + +SIGQUIT + Shutdown gracefully. First accept pending connections and stop + accepting connection. After all connections are handled, nghttpx + exits. + +SIGUSR1 + Reopen log files. + +SIGUSR2 + Fork and execute nghttpx. It will execute the binary in the same + path with same command-line arguments and environment variables. + After new process comes up, sending SIGQUIT to the original process + to perform hot swapping. + +SERVER PUSH +----------- + +nghttpx supports HTTP/2 server push in default mode. nghttpx looks +for Link header field (`RFC 5988 +`_) in response headers from +backend server and extracts URI-reference with parameter +``rel=preload`` (see `preload +`_) +and pushes those URIs to the frontend client. Here is a sample Link +header field to initiate server push: + +.. code-block:: http + + Link: ; rel=preload + Link: ; rel=preload + +Currently, the following restrictions are applied for server push: + +1. URI-reference must not contain authority. If it exists, it is not + pushed. ``/fonts/font.woff`` and ``css/theme.css`` are eligible to + be pushed. ``https://example.org/fonts/font.woff`` and + ``//example.org/css/theme.css`` are not. + +2. The associated stream must have method "GET" or "POST". The + associated stream's status code must be 200. + +These limitations may be loosened in the future release. + +UNIX DOMAIN SOCKET +------------------ + +nghttpx supports UNIX domain socket with a filename for both frontend +and backend connections. + +Please note that current nghttpx implementation does not delete a +socket with a filename. And on start up, if nghttpx detects that the +specified socket already exists in the file system, nghttpx first +deletes it. However, if SIGUSR2 is used to execute new binary and +both old and new configurations use same filename, new binary does not +delete the socket and continues to use it. + +OCSP STAPLING +------------- + +OCSP query is done using external perl script ``fetch-ocsp-response``, +which has been developed as part of h2o project +(https://github.com/h2o/h2o). + +The script file is usually installed under +``$(prefix)/share/nghttp2/`` directory. The actual path to script can +be customized using :option:`--fetch-ocsp-response-file` option. + +SEE ALSO +-------- + +:manpage:`nghttp(1)`, :manpage:`nghttpd(1)`, :manpage:`h2load(1)` diff --git a/doc/package_README.rst.in b/doc/package_README.rst.in new file mode 100644 index 0000000..dfa6b2d --- /dev/null +++ b/doc/package_README.rst.in @@ -0,0 +1 @@ +.. include:: @top_srcdir@/README.rst diff --git a/doc/programmers-guide.rst b/doc/programmers-guide.rst new file mode 100644 index 0000000..94c72e9 --- /dev/null +++ b/doc/programmers-guide.rst @@ -0,0 +1,105 @@ +Programmers' Guide +================== + +Includes +-------- + +To use the public APIs, include ``nghttp2/nghttp2.h``:: + + #include + +The header files are also available online: :doc:`nghttp2.h` and +:doc:`nghttp2ver.h`. + +Remarks +------- + +Do not call `nghttp2_session_send()`, `nghttp2_session_mem_send()`, +`nghttp2_session_recv()` or `nghttp2_session_mem_recv()` from the +nghttp2 callback functions directly or indirectly. It will lead to the +crash. You can submit requests or frames in the callbacks then call +these functions outside the callbacks. + +`nghttp2_session_send()` and `nghttp2_session_mem_send()` send first +24 bytes of client magic string (MAGIC) +(:macro:`NGHTTP2_CLIENT_MAGIC`) on client configuration. The +applications are responsible to send SETTINGS frame as part of +connection preface using `nghttp2_submit_settings()`. Similarly, +`nghttp2_session_recv()` and `nghttp2_session_mem_recv()` consume +MAGIC on server configuration unless +`nghttp2_option_set_no_recv_client_magic()` is used with nonzero +option value. + +.. _http-messaging: + +HTTP Messaging +-------------- + +By default, nghttp2 library checks HTTP messaging rules described in +`HTTP/2 specification, section 8 +`_. +Everything described in that section is not validated however. We +briefly describe what the library does in this area. In the following +description, without loss of generality we omit CONTINUATION frame +since they must follow HEADERS frame and are processed atomically. In +other words, they are just one big HEADERS frame. To disable these +validations, use `nghttp2_option_set_no_http_messaging()`. + +For HTTP request, including those carried by PUSH_PROMISE, HTTP +message starts with one HEADERS frame containing request headers. It +is followed by zero or more DATA frames containing request body, which +is followed by zero or one HEADERS containing trailer headers. The +request headers must include ":scheme", ":method" and ":path" pseudo +header fields unless ":method" is not "CONNECT". ":authority" is +optional, but nghttp2 requires either ":authority" or "Host" header +field must be present. If ":method" is "CONNECT", the request headers +must include ":method" and ":authority" and must omit ":scheme" and +":path". + +For HTTP response, HTTP message starts with zero or more HEADERS +frames containing non-final response (status code 1xx). They are +followed by one HEADERS frame containing final response headers +(non-1xx). It is followed by zero or more DATA frames containing +response body, which is followed by zero or one HEADERS containing +trailer headers. The non-final and final response headers must +contain ":status" pseudo header field containing 3 digits only. + +All request and response headers must include exactly one valid value +for each pseudo header field. Additionally nghttp2 requires all +request headers must not include more than one "Host" header field. + +HTTP/2 prohibits connection-specific header fields. The following +header fields must not appear: "Connection", "Keep-Alive", +"Proxy-Connection", "Transfer-Encoding" and "Upgrade". Additionally, +"TE" header field must not include any value other than "trailers". + +Each header field name and value must obey the field-name and +field-value production rules described in `RFC 7230, section +3.2. `_. +Additionally, all field name must be lower cased. While the pseudo +header fields must satisfy these rules, we just ignore illegal regular +headers (this means that these header fields are not passed to +application callback). This is because these illegal header fields +are floating around in existing internet and resetting stream just +because of this may break many web sites. This is especially true if +we forward to or translate from HTTP/1 traffic. + +For "http" or "https" URIs, ":path" pseudo header fields must start +with "/". The only exception is OPTIONS request, in that case, "*" is +allowed in ":path" pseudo header field to represent system-wide +OPTIONS request. + +With the above validations, nghttp2 library guarantees that header +field name passed to `nghttp2_on_header_callback()` is not empty. +Also required pseudo headers are all present and not empty. + +nghttp2 enforces "Content-Length" validation as well. All request or +response headers must not contain more than one "Content-Length" +header field. If "Content-Length" header field is present, it must be +parsed as 64 bit signed integer. The sum of data length in the +following DATA frames must match with the number in "Content-Length" +header field if it is present (this does not include padding bytes). + +Any deviation results in stream error of type PROTOCOL_ERROR. If +error is found in PUSH_PROMISE frame, stream error is raised against +promised stream. diff --git a/doc/python-apiref.rst.in b/doc/python-apiref.rst.in new file mode 100644 index 0000000..5fd40de --- /dev/null +++ b/doc/python-apiref.rst.in @@ -0,0 +1 @@ +.. include:: @top_srcdir@/doc/sources/python-apiref.rst diff --git a/doc/sources/building-android-binary.rst b/doc/sources/building-android-binary.rst new file mode 100644 index 0000000..99aec39 --- /dev/null +++ b/doc/sources/building-android-binary.rst @@ -0,0 +1,135 @@ +Building Android binary +======================= + +In this article, we briefly describe how to build Android binary using +`Android NDK `_ +cross-compiler on Debian Linux. + +The easiest way to build android binary is use Dockerfile.android. +See Dockerfile.android for more details. If you cannot use +Dockerfile.android for whatever reason, continue to read the rest of +this article. + +We offer ``android-config`` and ``android-make`` scripts to make the +build easier. To make these script work, NDK toolchain must be +installed in the following way. First, let us introduce +``ANDROID_HOME`` environment variable. We need to install toolchain +under ``$ANDROID_HOME/toolchain``. An user can freely choose the path +for ``ANDROID_HOME``. For example, to install toolchain under +``$ANDROID_HOME/toolchain``, do this in the the directory where NDK is +unpacked:: + + $ build/tools/make-standalone-toolchain.sh \ + --install-dir=$ANDROID_HOME/toolchain \ + --toolchain=arm-linux-androideabi-4.9 \ + --llvm-version=3.5 \ + --platform=android-16 + +The additional flag ``--system=linux-x86_64`` may be required if you +are using x86_64 system. + +The platform level is not important here because we don't use Android +specific C/C++ API. + +The dependent libraries, such as OpenSSL and libev should be built +with the toolchain and installed under ``$ANDROID_HOME/usr/local``. +We recommend to build these libraries as static library to make the +deployment easier. libxml2 support is currently disabled. + +We use zlib which comes with Android NDK, so we don't have to build it +by ourselves. + +If SPDY support is required for nghttpx and h2load, build and install +spdylay as well. + +Before running ``android-config`` and ``android-make``, +``ANDROID_HOME`` environment variable must be set to point to the +correct path. Also add ``$ANDROID_HOME/toolchain/bin`` to ``PATH``:: + + $ export PATH=$PATH:$ANDROID_HOME/toolchain/bin + +To configure OpenSSL, use the following script: + +.. code-block:: sh + + #!/bin/sh + + if [ -z "$ANDROID_HOME" ]; then + echo 'No $ANDROID_HOME specified.' + exit 1 + fi + PREFIX=$ANDROID_HOME/usr/local + TOOLCHAIN=$ANDROID_HOME/toolchain + PATH=$TOOLCHAIN/bin:$PATH + + export CROSS_COMPILE=$TOOLCHAIN/bin/arm-linux-androideabi- + ./Configure --prefix=$PREFIX android + +And run ``make install`` to build and install. + +We cannot compile libev without modification. Apply `this patch +`_ before +configuring libev. This patch is for libev-4.19. After applying the +patch, to configure libev, use the following script: + +.. code-block:: sh + + #!/bin/sh + + if [ -z "$ANDROID_HOME" ]; then + echo 'No $ANDROID_HOME specified.' + exit 1 + fi + PREFIX=$ANDROID_HOME/usr/local + TOOLCHAIN=$ANDROID_HOME/toolchain + PATH=$TOOLCHAIN/bin:$PATH + + ./configure \ + --host=arm-linux-androideabi \ + --build=`dpkg-architecture -qDEB_BUILD_GNU_TYPE` \ + --prefix=$PREFIX \ + --disable-shared \ + --enable-static \ + CPPFLAGS=-I$PREFIX/include \ + LDFLAGS=-L$PREFIX/lib + +And run ``make install`` to build and install. + +To configure spdylay, use the following script: + +.. code-block:: sh + + if [ -z "$ANDROID_HOME" ]; then + echo 'No $ANDROID_HOME specified.' + exit 1 + fi + PREFIX=$ANDROID_HOME/usr/local + TOOLCHAIN=$ANDROID_HOME/toolchain + PATH=$TOOLCHAIN/bin:$PATH + + ./configure \ + --disable-shared \ + --host=arm-linux-androideabi \ + --build=`dpkg-architecture -qDEB_BUILD_GNU_TYPE` \ + --prefix=$PREFIX \ + --without-libxml2 \ + --disable-src \ + --disable-examples \ + CPPFLAGS="-I$PREFIX/include" \ + PKG_CONFIG_LIBDIR="$PREFIX/lib/pkgconfig" \ + LDFLAGS="-L$PREFIX/lib" + +And run ``make install`` to build and install. After spdylay +installation, edit $ANDROID_HOME/usr/local/lib/pkgconfig/libspdylay.pc +and remove the following line:: + + Requires.private: zlib + +After prerequisite libraries are prepared, run ``android-config`` and +then ``android-make`` to compile nghttp2 source files. + +If all went well, application binaries, such as nghttpx, are created +under src directory. Strip debugging information from the binary +using the following command:: + + $ arm-linux-androideabi-strip src/nghttpx diff --git a/doc/sources/contribute.rst b/doc/sources/contribute.rst new file mode 100644 index 0000000..14a11e2 --- /dev/null +++ b/doc/sources/contribute.rst @@ -0,0 +1,57 @@ +Contribution Guidelines +======================= + +[This text was composed based on 1.2. License section of curl/libcurl +project.] + +When contributing with code, you agree to put your changes and new +code under the same license nghttp2 is already using unless stated and +agreed otherwise. + +When changing existing source code, you do not alter the copyright of +the original file(s). The copyright will still be owned by the +original creator(s) or those who have been assigned copyright by the +original author(s). + +By submitting a patch to the nghttp2 project, you are assumed to have +the right to the code and to be allowed by your employer or whatever +to hand over that patch/code to us. We will credit you for your +changes as far as possible, to give credit but also to keep a trace +back to who made what changes. Please always provide us with your +full real name when contributing! + +Coding style +------------ + +We use clang-format to format source code consistently. The +clang-format configuration file .clang-format is located at the root +directory. Since clang-format produces slightly different results +between versions, we currently use clang-format which comes with +clang-3.5. + +To detect any violation to the coding style, we recommend to setup git +pre-commit hook to check coding style of the changes you introduced. +The pre-commit file is located at the root directory. Copy it under +.git/hooks and make sure that it is executable. The pre-commit script +uses clang-format-diff.py to detect any style errors. If it is not in +your PATH or it exists under different name (e.g., +clang-format-diff-3.5 in debian), either add it to PATH variable or +add git option ``clangformatdiff.binary`` to point to the script. + +For emacs users, integrating clang-format to emacs is very easy. +clang-format.el should come with clang distribution. If it is not +found, download it from `here +`_. +And add these lines to your .emacs file: + +.. code-block:: lisp + + ;; From + ;; https://code.google.com/p/chromium/wiki/Emacs#Use_Google's_C++_style! + (load "//clang-format.el") + (add-hook 'c-mode-common-hook + (function (lambda () (local-set-key (kbd "TAB") + 'clang-format-region)))) + +You can find other editor integration in +http://clang.llvm.org/docs/ClangFormat.html. diff --git a/doc/sources/h2load-howto.rst b/doc/sources/h2load-howto.rst new file mode 100644 index 0000000..7c07194 --- /dev/null +++ b/doc/sources/h2load-howto.rst @@ -0,0 +1,91 @@ +h2load - HTTP/2 benchmarking tool - HOW-TO +========================================== + +h2load is benchmarking tool for HTTP/2. If built with +spdylay (http://tatsuhiro-t.github.io/spdylay/) library, it also +supports SPDY protocol. It supports SSL/TLS and clear text for both +HTTP/2 and SPDY. + +Basic Usage +----------- + +In order to set benchmark settings, specify following 3 options. + +``-n`` + The number of total requests. Default: 1 + +``-c`` + The number of concurrent clients. Default: 1 + +``-m`` + The max concurrent streams to issue per client. + If ``auto`` is given, the number of given URIs is used. + Default: ``auto`` + +Here is a command-line to perform benchmark to URI \https://localhost +using total 100000 requests, 100 concurrent clients and 10 max +concurrent streams:: + + $ h2load -n100000 -c100 -m10 https://localhost + +The benchmarking result looks like this:: + + finished in 0 sec, 385 millisec and 851 microsec, 2591 req/s, 1689 kbytes/s + requests: 1000 total, 1000 started, 1000 done, 1000 succeeded, 0 failed, 0 errored + status codes: 1000 2xx, 0 3xx, 0 4xx, 0 5xx + traffic: 667500 bytes total, 28700 bytes headers, 612000 bytes data + +The number of ``failed`` is the number of requests returned with non +2xx status. The number of ``error`` is the number of ``failed`` plus +the number of requests which failed with connection error. + +The number of ``total`` in ``traffic`` is the received application +data. If SSL/TLS is used, this number is calculated after decryption. +The number of ``headers`` is the sum of payload size of response +HEADERS (or SYN_REPLY for SPDY). This number comes before +decompressing header block. The number of ``data`` is the sum of +response body. + +Flow Control +------------ + +HTTP/2 and SPDY/3 or later employ flow control and it may affect +benchmarking results. By default, h2load uses large enough flow +control window, which effectively disables flow control. To adjust +receiver flow control window size, there are following options: + +``-w`` + Sets the stream level initial window size to + (2**)-1. For SPDY, 2** is used instead. + +``-W`` + Sets the connection level initial window size to + (2**)-1. For SPDY, if is strictly less + than 16, this option is ignored. Otherwise + 2** is used for SPDY. + +Multi-Threading +--------------- + +Sometimes benchmarking client itself becomes a bottleneck. To remedy +this situation, use ``-t`` option to specify the number of native +thread to use. + +``-t`` + The number of native threads. Default: 1 + +Selecting protocol for clear text +--------------------------------- + +By default, if \http:// URI is given, HTTP/2 protocol is used. To +change the protocol to use for clear text, use ``-p`` option. + +Multiple URIs +------------- + +If multiple URIs are specified, they are used in round robin manner. + +.. note:: + + Please note that h2load uses scheme, host and port in the first URI + and ignores those parts in the rest of the URIs. diff --git a/doc/sources/index.rst b/doc/sources/index.rst new file mode 100644 index 0000000..14a469f --- /dev/null +++ b/doc/sources/index.rst @@ -0,0 +1,53 @@ +.. nghttp2 documentation master file, created by + sphinx-quickstart on Sun Mar 11 22:57:49 2012. + You can adapt this file completely to your liking, but it should at least + contain the root `toctree` directive. + +nghttp2 - HTTP/2 C Library +============================ + +This is an experimental implementation of Hypertext Transfer Protocol +version 2. + +The project is hosted at `github.com/tatsuhiro-t/nghttp2 `_. + +Contents: + +.. toctree:: + :maxdepth: 2 + + package_README + contribute + building-android-binary + tutorial-client + tutorial-server + tutorial-hpack + nghttp.1 + nghttpd.1 + nghttpx.1 + h2load.1 + nghttpx-howto + h2load-howto + programmers-guide + apiref + libnghttp2_asio + python-apiref + nghttp2.h + nghttp2ver.h + asio_http2_server.h + asio_http2_client.h + asio_http2.h + Source + Issues + nghttp2.org + +Released Versions +================= + +https://github.com/tatsuhiro-t/nghttp2/releases + +Resources +--------- + +* HTTP/2 https://tools.ietf.org/html/rfc7540 +* HPACK https://tools.ietf.org/html/rfc7541 diff --git a/doc/sources/libnghttp2_asio.rst b/doc/sources/libnghttp2_asio.rst new file mode 100644 index 0000000..76999f9 --- /dev/null +++ b/doc/sources/libnghttp2_asio.rst @@ -0,0 +1,433 @@ +libnghttp2_asio: High level HTTP/2 C++ library +============================================== + +libnghttp2_asio is C++ library built on top of libnghttp2 and provides +high level abstraction API to build HTTP/2 applications. It depends +on Boost::ASIO library and OpenSSL. Currently libnghttp2_asio +provides server and client side API. + +libnghttp2_asio is not built by default. Use ``--enable-asio-lib`` +configure flag to build libnghttp2_asio. The required Boost libraries +are: + +* Boost::Asio +* Boost::System +* Boost::Thread + +We have 3 header files for this library: + +* :doc:`asio_http2_server.h` +* :doc:`asio_http2_client.h` +* :doc:`asio_http2.h` + +asio_http2.h is included from the other two files. + +To build a program with libnghttp2_asio, link to the following +libraries:: + + -lnghttp2_asio -lboost_system + +If ``boost::asio::ssl`` is used in application code, OpenSSL is also +required in link line:: + + -lnghttp2_asio -lboost_system -lssl -lcrypto + +Server API +---------- + +To use server API, first include following header file: + +.. code-block:: cpp + + #include + +Also take a look at that header file :doc:`asio_http2_server.h`. + +Server API is designed to build HTTP/2 server very easily to utilize +C++11 anonymous function and closure. The bare minimum example of +HTTP/2 server looks like this: + +.. code-block:: cpp + + using namespace nghttp2::asio_http2; + using namespace nghttp2::asio_http2::server; + + int main(int argc, char *argv[]) { + boost::system::error_code ec; + http2 server; + + server.handle("/", [](const request &req, const response &res) { + res.write_head(200); + res.end("hello, world\n"); + }); + + if (server.listen_and_serve(ec, "localhost", "3000")) { + std::cerr << "error: " << ec.message() << std::endl; + } + } + +First we instantiate ``nghttp2::asio_http2::server::http2`` object. +``nghttp2::asio_http2::server::http2::handle`` function registers +pattern and its handler function. In this example, we register "/" as +pattern, which matches all requests. Then call +``nghttp2::asio_http2::server::http2::listen_and_serve`` function with +address and port to listen to. + +The ``req`` and ``res`` represent HTTP request and response +respectively. ``nghttp2::asio_http2_::server::response::write_head`` +constructs HTTP response header fields. The first argument is HTTP +status code, in the above example, which is 200. The second argument, +which is omitted in the above example, is additional header fields to +send. + +``nghttp2::asio_http2::server::response::end`` sends response body. +In the above example, we send string "hello, world". + +The life time of req and res object ends after the callback set by +``nghttp2::asio_http2::server::response::on_close`` function. +Application must not use those objects after this call. + +Serving static files and enabling SSL/TLS ++++++++++++++++++++++++++++++++++++++++++ + +In this example, we serve a couple of static files and also enable +SSL/TLS. + +.. code-block:: cpp + + #include + + using namespace nghttp2::asio_http2; + using namespace nghttp2::asio_http2::server; + + int main(int argc, char *argv[]) { + boost::system::error_code ec; + boost::asio::ssl::context tls(boost::asio::ssl::context::sslv23); + + tls.use_private_key_file("server.key", boost::asio::ssl::context::pem); + tls.use_certificate_chain_file("server.crt"); + + configure_tls_context_easy(ec, tls); + + http2 server; + + server.handle("/index.html", [](const request &req, const response &res) { + res.write_head(200); + res.end(file_generator("index.html")); + }); + + if (server.listen_and_serve(ec, tls, "localhost", "3000")) { + std::cerr << "error: " << ec.message() << std::endl; + } + } + +We first create ``boost::asio::ssl::context`` object and set path to +private key file and certificate file. +``nghttp2::asio_http2::server::configure_tls_context_easy`` function +configures SSL/TLS context object for HTTP/2 server use, including NPN +callbacks. + +In the above example, if request path is "/index.html", we serve +index.html file in the current working directory. +``nghttp2::asio_http2::server::response::end`` has overload to take +function of type ``nghttp2::asio_http2::generator_cb`` and application +pass its implementation to generate response body. For the +convenience, libnghttp2_asio library provides +``nghttp2::asio_http2::file_generator`` function to generate function +to server static file. If other resource is requested, server +automatically responds with 404 status code. + +Server push ++++++++++++ + +Server push is also supported. + +.. code-block:: cpp + + #include + + using namespace nghttp2::asio_http2; + using namespace nghttp2::asio_http2::server; + + int main(int argc, char *argv[]) { + boost::system::error_code ec; + boost::asio::ssl::context tls(boost::asio::ssl::context::sslv23); + + tls.use_private_key_file("server.key", boost::asio::ssl::context::pem); + tls.use_certificate_chain_file("server.crt"); + + configure_tls_context_easy(ec, tls); + + http2 server; + + std::string style_css = "h1 { color: green; }"; + + server.handle("/", [&style_css](const request &req, const response &res) { + boost::system::error_code ec; + auto push = res.push(ec, "GET", "/style.css"); + push->write_head(200); + push->end(style_css); + + res.write_head(200); + res.end(R"( + + HTTP/2 FTW + +

    This should be green

    + + )"); + }); + + server.handle("/style.css", + [&style_css](const request &req, const response &res) { + res.write_head(200); + res.end(style_css); + }); + + if (server.listen_and_serve(ec, tls, "localhost", "3000")) { + std::cerr << "error: " << ec.message() << std::endl; + } + } + +When client requested any resource other than "/style.css", we push +"/style.css". To push resource, call +``nghttp2::asio_http2::server::response::push`` function with desired +method and path. It returns another response object and use its +functions to send push response. + +Enable multi-threading +++++++++++++++++++++++ + +Enabling multi-threading is very easy. Just call +``nghttp2::asio_http2::server::http2::num_threads`` function with the +desired number of threads: + +.. code-block:: cpp + + http2 server; + + // Use 4 native threads + server.num_threads(4); + +Client API +---------- + +To use client API, first include following header file: + +.. code-block:: cpp + + #include + +Also take a look at that header file :doc:`asio_http2_client.h`. + +Here is the sample client code to access HTTP/2 server and print out +response header fields and response body to the console screen: + +.. code-block:: cpp + + #include + + #include + + using boost::asio::ip::tcp; + + using namespace nghttp2::asio_http2; + using namespace nghttp2::asio_http2::client; + + int main(int argc, char *argv[]) { + boost::system::error_code ec; + boost::asio::io_service io_service; + + // connect to localhost:3000 + session sess(io_service, "localhost", "3000"); + + sess.on_connect([&sess](tcp::resolver::iterator endpoint_it) { + boost::system::error_code ec; + + auto req = sess.submit(ec, "GET", "http://localhost:3000/"); + + req->on_response([](const response &res) { + // print status code and response header fields. + std::cerr << "HTTP/2 " << res.status_code() << std::endl; + for (auto &kv : res.header()) { + std::cerr << kv.first << ": " << kv.second.value << "\n"; + } + std::cerr << std::endl; + + res.on_data([](const uint8_t *data, std::size_t len) { + std::cerr.write(reinterpret_cast(data), len); + std::cerr << std::endl; + }); + }); + + req->on_close([&sess](uint32_t error_code) { + // shutdown session after first request was done. + sess.shutdown(); + }); + }); + + sess.on_error([](const boost::system::error_code &ec) { + std::cerr << "error: " << ec.message() << std::endl; + }); + + io_service.run(); + } + +``nghttp2::asio_http2::client::session`` object takes +``boost::asio::io_service`` object and remote server address. When +connection is made, the callback function passed to +``nghttp2::asio_http2::client::on_connect`` is invoked with connected +address as its parameter. After this callback call, use +``nghttp2::asio_http2::session::submit`` to send request to the +server. You can submit multiple requests at once without waiting for +the completion of previous request. + +The life time of req and res object ends after the callback set by +``nghttp2::asio_http2::server::request::on_close`` function. +Application must not use those objects after this call. + +Normally, client does not stop even after all requests are done unless +connection is lost. To stop client, call +``nghttp2::asio_http2::server::session::shutdown()``. + +Recieve server push and enable SSL/TLS +++++++++++++++++++++++++++++++++++++++ + +.. code-block:: cpp + + #include + + #include + + using boost::asio::ip::tcp; + + using namespace nghttp2::asio_http2; + using namespace nghttp2::asio_http2::client; + + int main(int argc, char *argv[]) { + boost::system::error_code ec; + boost::asio::io_service io_service; + + boost::asio::ssl::context tls(boost::asio::ssl::context::sslv23); + tls.set_default_verify_paths(); + // disabled to make development easier... + // tls_ctx.set_verify_mode(boost::asio::ssl::verify_peer); + configure_tls_context(ec, tls); + + // connect to localhost:3000 + session sess(io_service, tls, "localhost", "3000"); + + sess.on_connect([&sess](tcp::resolver::iterator endpoint_it) { + boost::system::error_code ec; + + auto req = sess.submit(ec, "GET", "http://localhost:3000/"); + + req->on_response([&sess](const response &res) { + std::cerr << "response received!" << std::endl; + res.on_data([&sess](const uint8_t *data, std::size_t len) { + std::cerr.write(reinterpret_cast(data), len); + std::cerr << std::endl; + }); + }); + + req->on_push([](const request &push) { + std::cerr << "push request received!" << std::endl; + push.on_response([](const response &res) { + std::cerr << "push response received!" << std::endl; + res.on_data([](const uint8_t *data, std::size_t len) { + std::cerr.write(reinterpret_cast(data), len); + std::cerr << std::endl; + }); + }); + }); + }); + + sess.on_error([](const boost::system::error_code &ec) { + std::cerr << "error: " << ec.message() << std::endl; + }); + + io_service.run(); + } + +The above sample code demonstrates how to enable SSL/TLS and receive +server push. Currently, +``nghttp2::asio_http2::client::configure_tls_context`` function setups +NPN callbacks for SSL/TLS context for HTTP/2 use. + +To receive server push, use +``nghttp2::asio_http2::client::request::on_push`` function to set +callback function which is invoked when server push request is +arrived. The callback function takes +``nghttp2::asio_http2::client::request`` object, which contains the +pushed request. To get server push response, set callback using +``nghttp2::asio_http2::client::request::on_response``. + +As stated in the previous section, client does not stop automatically +as long as HTTP/2 session is fine and connection is alive. We don't +call ``nghttp2::asio_http2::client::session::shutdown`` in this +example, so the program does not terminate after all responses are +received. Hit Ctrl-C to terminate the program. + +Multiple concurrent requests +++++++++++++++++++++++++++++ + +.. code-block:: cpp + + #include + + #include + + using boost::asio::ip::tcp; + + using namespace nghttp2::asio_http2; + using namespace nghttp2::asio_http2::client; + + int main(int argc, char *argv[]) { + boost::system::error_code ec; + boost::asio::io_service io_service; + + // connect to localhost:3000 + session sess(io_service, "localhost", "3000"); + + sess.on_connect([&sess](tcp::resolver::iterator endpoint_it) { + boost::system::error_code ec; + + auto printer = [](const response &res) { + res.on_data([](const uint8_t *data, std::size_t len) { + std::cerr.write(reinterpret_cast(data), len); + std::cerr << std::endl; + }); + }; + + std::size_t num = 3; + auto count = std::make_shared(num); + + for (std::size_t i = 0; i < num; ++i) { + auto req = sess.submit(ec, "GET", + "http://localhost:3000/" + std::to_string(i + 1)); + + req->on_response(printer); + req->on_close([&sess, count](uint32_t error_code) { + if (--*count == 0) { + // shutdown session after |num| requests were done. + sess.shutdown(); + } + }); + } + }); + + sess.on_error([](const boost::system::error_code &ec) { + std::cerr << "error: " << ec.message() << std::endl; + }); + + io_service.run(); + } + +Here is the sample to send 3 requests at once. Depending on the +server settings, these requests are processed out-of-order. In this +example, we have a trick to shutdown session after all requests were +done. We made ``count`` object which is shared pointer to int and is +initialized to 3. On each request closure (the invocation of the +callback set by ``nghttp2::asio_http2::client::request::on_close``), +we decrement the count. If count becomes 0, we are sure that all +requests have been done and initiate shutdown. diff --git a/doc/sources/nghttpx-howto.rst b/doc/sources/nghttpx-howto.rst new file mode 100644 index 0000000..1f7d87e --- /dev/null +++ b/doc/sources/nghttpx-howto.rst @@ -0,0 +1,303 @@ +nghttpx - HTTP/2 proxy - HOW-TO +=============================== + +nghttpx is a proxy translating protocols between HTTP/2 and other +protocols (e.g., HTTP/1, SPDY). It operates in several modes and each +mode may require additional programs to work with. This article +describes each operation mode and explains the intended use-cases. It +also covers some useful options later. + +Default mode +------------ + +If nghttpx is invoked without any ``-s``, ``-p`` and ``--client``, it +operates in default mode. In this mode, nghttpx frontend listens for +HTTP/2 requests and translates them to HTTP/1 requests. Thus it works +as reverse proxy (gateway) for HTTP/2 clients to HTTP/1 web server. +HTTP/1 requests are also supported in frontend as a fallback. If +nghttpx is linked with spdylay library and frontend connection is +SSL/TLS, the frontend also supports SPDY protocol. + +By default, this mode's frontend connection is encrypted using +SSL/TLS. So server's private key and certificate must be supplied to +the command line (or through configuration file). In this case, the +frontend protocol selection will is done via ALPN or NPN. + +With ``--frontend-no-tls`` option, user can turn off SSL/TLS in +frontend connection. In this case, SPDY protocol is not available +even if spdylay library is liked to nghttpx. HTTP/2 and HTTP/1 are +available on the frontend and a HTTP/1 connection can be upgraded to +HTTP/2 using HTTP Upgrade. Starting HTTP/2 connection by sending +HTTP/2 connection preface is also supported. + +The backend is supposed to be HTTP/1 Web server. For example, to make +nghttpx listen to encrypted HTTP/2 requests at port 8443, and a +backend HTTP/1 web server is configured to listen to HTTP/1 request at +port 8080 in the same host, run nghttpx command-line like this:: + + $ nghttpx -f0.0.0.0,8443 -b127.0.0.1,8080 /path/to/server.key /path/to/server.crt + +Then HTTP/2 enabled client can access to the nghttpx in HTTP/2. For +example, you can send GET request to the server using nghttp:: + + $ nghttp -nv https://localhost:8443/ + +HTTP/2 proxy mode +----------------- + +If nghttpx is invoked with ``-s`` option, it operates in HTTP/2 proxy +mode. The supported protocols in frontend and backend connections are +the same in `default mode`_. The difference is that this mode acts +like forward proxy and assumes the backend is HTTP/1 proxy server +(e.g., squid, traffic server). So HTTP/1 request must include +absolute URI in request line. + +By default, frontend connection is encrypted. So this mode is also +called secure proxy. If nghttpx is linked with spdylay, it supports +SPDY protocols and it works as so called SPDY proxy. + +With ``--frontend-no-tls`` option, SSL/TLS is turned off in frontend +connection, so the connection gets insecure. + +The backend must be HTTP/1 proxy server. nghttpx supports multiple +backend server addresses. It translates incoming requests to HTTP/1 +request to backend server. The backend server performs real proxy +work for each request, for example, dispatching requests to the origin +server and caching contents. + +For example, to make nghttpx listen to encrypted HTTP/2 requests at +port 8443, and a backend HTTP/1 proxy server is configured to listen +to HTTP/1 request at port 8080 in the same host, run nghttpx +command-line like this:: + + $ nghttpx -s -f'*,8443' -b127.0.0.1,8080 /path/to/server.key /path/to/server.crt + +At the time of this writing, Firefox nightly supports HTTP/2 proxy. +Chromium can use nghttpx as secure (SPDY) proxy and will support +HTTP/2 proxy in the near future. + +To make Firefox nightly or Chromium use nghttpx as HTTP/2 or SPDY +proxy, user has to create proxy.pac script file like this: + +.. code-block:: javascript + + function FindProxyForURL(url, host) { + return "HTTPS SERVERADDR:PORT"; + } + +``SERVERADDR`` and ``PORT`` is the hostname/address and port of the +machine nghttpx is running. Please note that both Firefox nightly and +Chromium require valid certificate for secure proxy. + +For Firefox nightly, open Preference window and select Advanced then +click Network tab. Clicking Connection Settings button will show the +dialog. Select "Automatic proxy configuration URL" and enter the path +to proxy.pac file, something like this:: + + file:///path/to/proxy.pac + +For Chromium, use following command-line:: + + $ google-chrome --proxy-pac-url=file:///path/to/proxy.pac --use-npn + +As HTTP/1 proxy server, Squid may work as out-of-box. Traffic server +requires to be configured as forward proxy. Here is the minimum +configuration items to edit:: + + CONFIG proxy.config.reverse_proxy.enabled INT 0 + CONFIG proxy.config.url_remap.remap_required INT 0 + +Consult Traffic server `documentation +`_ +to know how to configure traffic server as forward proxy and its +security implications. + +Client mode +----------- + +If nghttpx is invoked with ``--client`` option, it operates in client +mode. In this mode, nghttpx listens for plain, unencrypted HTTP/2 and +HTTP/1 requests and translates them to encrypted HTTP/2 requests to +the backend. User cannot enable SSL/TLS in frontend connection. + +HTTP/1 frontend connection can be upgraded to HTTP/2 using HTTP +Upgrade. To disable SSL/TLS in backend connection, use +``--backend-no-tls`` option. + +By default, the number of backend HTTP/2 connections per worker +(thread) is determined by number of ``-b`` option. To adjust this +value, use ``--backend-http2-connections-per-worker`` option. + +The backend server is supporsed to be a HTTP/2 web server (e.g., +nghttpd). The one use-case of this mode is utilize existing HTTP/1 +clients to test HTTP/2 deployment. Suppose that HTTP/2 web server +listens to port 80 without encryption. Then run nghttpx as client +mode to access to that web server:: + + $ nghttpx --client -f127.0.0.1,8080 -b127.0.0.1,80 --backend-no-tls + +.. note:: + + You may need ``-k`` option if HTTP/2 server enables SSL/TLS and + its certificate is self-signed. But please note that it is + insecure. + +Then you can use curl to access HTTP/2 server via nghttpx:: + + $ curl http://localhost:8080/ + +Client proxy mode +----------------- + +If nghttpx is invoked with ``-p`` option, it operates in client proxy +mode. This mode behaves like `client mode`_, but it works like +forward proxy. So HTTP/1 request must include absolute URI in request +line. + +HTTP/1 frontend connection can be upgraded to HTTP/2 using HTTP +Upgrade. To disable SSL/TLS in backend connection, use +``--backend-no-tls`` option. + +By default, the number of backend HTTP/2 connections per worker +(thread) is determined by number of ``-b`` option. To adjust this +value, use ``--backend-http2-connections-per-worker`` option. + +The backend server must be a HTTP/2 proxy. You can use nghttpx in +`HTTP/2 proxy mode`_ as backend server. The one use-case of this mode +is utilize existing HTTP/1 clients to test HTTP/2 connections between +2 proxies. The another use-case is use this mode to aggregate local +HTTP/1 connections to one HTTP/2 backend encrypted connection. This +makes HTTP/1 clients which does not support secure proxy can use +secure HTTP/2 proxy via nghttpx client mode. + +Suppose that HTTP/2 proxy listens to port 8443, just like we saw in +`HTTP/2 proxy mode`_. To run nghttpx in client proxy mode to access +that server, invoke nghttpx like this:: + + $ nghttpx -p -f127.0.0.1,8080 -b127.0.0.1,8443 + +.. note:: + + You may need ``-k`` option if HTTP/2 server's certificate is + self-signed. But please note that it is insecure. + +Then you can use curl to issue HTTP request via HTTP/2 proxy:: + + $ curl --http-proxy=http://localhost:8080 http://www.google.com/ + +You can configure web browser to use localhost:8080 as forward +proxy. + +HTTP/2 bridge mode +------------------ + +If nghttpx is invoked with ``--http2-bridge`` option, it operates in +HTTP/2 bridge mode. The supported protocols in frontend connections +are the same in `default mode`_. The protocol in backend is HTTP/2 +only. + +With ``--frontend-no-tls`` option, SSL/TLS is turned off in frontend +connection, so the connection gets insecure. To disable SSL/TLS in +backend connection, use ``--backend-no-tls`` option. + +By default, the number of backend HTTP/2 connections per worker +(thread) is determined by number of ``-b`` option. To adjust this +value, use ``--backend-http2-connections-per-worker`` option. + +The backend server is supporsed to be a HTTP/2 web server or HTTP/2 +proxy. If backend server is HTTP/2 proxy, use +``--no-location-rewrite`` and ``--no-host-rewrite`` options to disable +rewriting location, host and :authority header field. + +The use-case of this mode is aggregate the incoming connections to one +HTTP/2 connection. One backend HTTP/2 connection is created per +worker (thread). + +Disable SSL/TLS +--------------- + +In `default mode`_, `HTTP/2 proxy mode`_ and `HTTP/2 bridge mode`_, +frontend connections are encrypted with SSL/TLS by default. To turn +off SSL/TLS, use ``--frontend-no-tls`` option. If this option is +used, the private key and certificate are not required to run nghttpx. + +In `client mode`_, `client proxy mode`_ and `HTTP/2 bridge mode`_, +backend connections are encrypted with SSL/TLS by default. To turn +off SSL/TLS, use ``--backend-no-tls`` option. + +Specifying additional CA certificate +------------------------------------ + +By default, nghttpx tries to read CA certificate from system. But +depending on the system you use, this may fail or is not supported. +To specify CA certificate manually, use ``--cacert`` option. The +specified file must be PEM format and can contain multiple +certificates. + +By default, nghttpx validates server's certificate. If you want to +turn off this validation, knowing this is really insecure and what you +are doing, you can use ``-k`` option to disable certificate +validation. + +Read/write rate limit +--------------------- + +nghttpx supports transfer rate limiting on frontend connections. You +can do rate limit per frontend connection for reading and writing +individually. + +To perform rate limit for reading, use ``--read-rate`` and +``--read-burst`` options. For writing, use ``--write-rate`` and +``--write-burst``. + +Please note that rate limit is performed on top of TCP and nothing to +do with HTTP/2 flow control. + +Rewriting location header field +------------------------------- + +nghttpx automatically rewrites location response header field if the +following all conditions satisfy: + +* URI in location header field is not absolute URI or is not https URI. +* URI in location header field includes non empty host component. +* host (without port) in URI in location header field must match the + host appearing in :authority or host header field. + +When rewrite happens, URI scheme and port are replaced with the ones +used in frontend, and host is replaced with which appears in +:authority or host request header field. :authority header field has +precedence. If the above conditions are not met with the host value +in :authority header field, rewrite is retried with the value in host +header field. + +Hot swapping +------------ + +nghttpx supports hot swapping using signals. The hot swapping in +nghttpx is multi step process. First send USR2 signal to nghttpx +process. It will do fork and execute new executable, using same +command-line arguments and environment variables. At this point, both +current and new processes can accept requests. To gracefully shutdown +current process, send QUIT signal to current nghttpx process. When +all existing frontend connections are done, the current process will +exit. At this point, only new nghttpx process exists and serves +incoming requests. + +Re-opening log files +-------------------- + +When rotating log files, it is desirable to re-open log files after +log rotation daemon renamed existing log files. To tell nghttpx to +re-open log files, send USR1 signal to nghttpx process. It will +re-open files specified by ``--accesslog-file`` and +``--errorlog-file`` options. + +Multiple backend addresses +-------------------------- + +nghttpx supports multiple backend addresses. To specify them, just +use ``-b`` option repeatedly. For example, to use backend1:8080 and +backend2:8080, use command-line like this: ``-bbackend1,8080 +-bbackend2,8080``. For HTTP/2 backend, see also +``--backend-http2-connections-per-worker`` option. diff --git a/doc/sources/python-apiref.rst b/doc/sources/python-apiref.rst new file mode 100644 index 0000000..d9d8494 --- /dev/null +++ b/doc/sources/python-apiref.rst @@ -0,0 +1,437 @@ +Python API Reference +==================== + +.. py:module:: nghttp2 + +nghttp2 offers some high level Python API to C library. The bindings +currently provide HPACK compressor and decompressor classes and HTTP/2 +server class. + +The extension module is called ``nghttp2``. + +``make`` will build the bindings. The target Python version is +determined by configure script. If the detected Python version is not +what you expect, specify a path to Python executable in ``PYTHON`` +variable as an argument to configure script (e.g., ``./configure +PYTHON=/usr/bin/python3.4``). + +HPACK API +--------- + +.. py:class:: HDDeflater(hd_table_bufsize_max=DEFLATE_MAX_HEADER_TABLE_SIZE) + + This class is used to perform header compression. The + *hd_table_bufsize_max* limits the usage of header table in the + given amount of bytes. The default value is + :py:data:`DEFLATE_MAX_HEADER_TABLE_SIZE`. This is necessary + because the deflater and inflater share the same amount of header + table and the inflater decides that number. The deflater may not + want to use all header table size because of limited memory + availability. In that case, *hd_table_bufsize_max* can be used to + cap the upper limit of table size whatever the header table size is + chosen by the inflater. + + .. py:method:: deflate(headers) + + Deflates the *headers*. The *headers* must be sequence of tuple + of name/value pair, which are byte strings (not unicode string). + + This method returns the deflated header block in byte string. + Raises the exception if any error occurs. + + .. py:method:: set_no_refset(no_refset) + + Tells the deflater not to use reference set if *no_refset* is + evaluated to ``True``. If that happens, on each subsequent + invocation of :py:meth:`deflate()`, deflater will clear up + refersent set. + + .. py:method:: change_table_size(hd_table_bufsize_max) + + Changes header table size to *hd_table_bufsize_max* byte. if + *hd_table_bufsize_max* is strictly larger than + ``hd_table_bufsize_max`` given in constructor, + ``hd_table_bufsize_max`` is used as header table size instead. + + Raises the exception if any error occurs. + + .. py:method:: get_hd_table() + + Returns copy of current dynamic header table. + +The following example shows how to deflate header name/value pairs: + +.. code-block:: python + + import binascii, nghttp2 + + deflater = nghttp2.HDDeflater() + + res = deflater.deflate([(b'foo', b'bar'), + (b'baz', b'buz')]) + + print(binascii.b2a_hex(res)) + + +.. py:class:: HDInflater() + + This class is used to perform header decompression. + + .. py:method:: inflate(data) + + Inflates the deflated header block *data*. The *data* must be + byte string. + + Raises the exception if any error occurs. + + .. py:method:: change_table_size(hd_table_bufsize_max) + + Changes header table size to *hd_table_bufsize_max* byte. + + Raises the exception if any error occurs. + + .. py:method:: get_hd_table() + + Returns copy of current dynamic header table. + +The following example shows how to inflate deflated header block: + +.. code-block:: python + + deflater = nghttp2.HDDeflater() + + data = deflater.deflate([(b'foo', b'bar'), + (b'baz', b'buz')]) + + inflater = nghttp2.HDInflater() + + hdrs = inflater.inflate(data) + + print(hdrs) + + +.. py:function:: print_hd_table(hdtable) + + Convenient function to print *hdtable* to the standard output. The + *hdtable* is the one retrieved by + :py:meth:`HDDeflater.get_hd_table()` or + :py:meth:`HDInflater.get_hd_table()`. This function does not work + if header name/value cannot be decoded using UTF-8 encoding. + + In output, ``s=N`` means the entry occupies ``N`` bytes in header + table. If ``r=y``, then the entry is in the reference set. + +.. py:data:: DEFAULT_HEADER_TABLE_SIZE + + The default header table size, which is 4096 as per HTTP/2 + specification. + +.. py:data:: DEFLATE_MAX_HEADER_TABLE_SIZE + + The default header table size for deflater. The initial value + is 4096. + +HTTP/2 servers +-------------- + +.. note:: + + We use :py:mod:`asyncio` for HTTP/2 server classes. Therefore, + Python 3.4 or later is required to use these objects. To + explicitly configure nghttp2 build to use Python 3.4, specify the + ``PYTHON`` variable to the path to Python 3.4 executable when + invoking configure script like this:: + + $ ./configure PYTHON=/usr/bin/python3.4 + +.. py:class:: HTTP2Server(address, RequestHandlerClass, ssl=None) + + This class builds on top of the :py:mod:`asyncio` event loop. On + construction, *RequestHandlerClass* must be given, which must be a + subclass of :py:class:`BaseRequestHandler` class. + + The *address* must be a tuple of hostname/IP address and port to + bind. If hostname/IP address is ``None``, all interfaces are + assumed. + + To enable SSL/TLS, specify instance of :py:class:`ssl.SSLContext` + in *ssl*. Before passing *ssl* to + :py:func:`BaseEventLoop.create_server`, ALPN protocol identifiers + are set using :py:meth:`ssl.SSLContext.set_npn_protocols`. + + To disable SSL/TLS, omit *ssl* or specify ``None``. + + .. py:method:: serve_forever() + + Runs server and processes incoming requests forever. + +.. py:class:: BaseRequestHandler(http2, stream_id) + + The class is used to handle the single HTTP/2 stream. By default, + it does not nothing. It must be subclassed to handle each event + callback method. + + The first callback method invoked is :py:meth:`on_headers()`. It is + called when HEADERS frame, which includes request header fields, is + arrived. + + If request has request body, :py:meth:`on_data()` is invoked for + each chunk of received data chunk. + + When whole request is received, :py:meth:`on_request_done()` is + invoked. + + When stream is closed, :py:meth:`on_close()` is called. + + The application can send response using :py:meth:`send_response()` + method. It can be used in :py:meth:`on_headers()`, + :py:meth:`on_data()` or :py:meth:`on_request_done()`. + + The application can push resource using :py:meth:`push()` method. + It must be used before :py:meth:`send_response()` call. + + A :py:class:`BaseRequestHandler` has the following instance + variables: + + .. py:attribute:: client_address + + Contains a tuple of the form ``(host, port)`` referring to the + client's address. + + .. py:attribute:: stream_id + + Stream ID of this stream + + .. py:attribute:: scheme + + Scheme of the request URI. This is a value of ``:scheme`` + header field. + + .. py:attribute:: method + + Method of this stream. This is a value of ``:method`` header + field. + + .. py:attribute:: host + + This is a value of ``:authority`` or ``host`` header field. + + .. py:attribute:: path + + This is a value of ``:path`` header field. + + .. py:attribute:: headers + + Request header fields. + + A :py:class:`BaseRequestHandler` has the following methods: + + .. py:method:: on_headers() + + Called when request HEADERS is arrived. By default, this method + does nothing. + + .. py:method:: on_data(data) + + Called when a chunk of request body *data* is arrived. This + method will be called multiple times until all data are + received. By default, this method does nothing. + + .. py:method:: on_request_done() + + Called when whole request was received. By default, this method + does nothing. + + .. py:method:: on_close(error_code) + + Called when stream is about to close. The *error_code* + indicates the reason of closure. If it is ``0``, the stream is + going to close without error. + + .. py:method:: send_response(status=200, headers=None, body=None) + + Send response. The *status* is HTTP status code. The *headers* + is additional response headers. The *:status* header field will + be appended by the library. The *body* is the response body. + It could be ``None`` if response body is empty. Or it must be + instance of either ``str``, ``bytes``, :py:class:`io.IOBase` or + callable, called body generator, which takes one parameter, + size. The body generator generates response body. It can pause + generation of response so that it can wait for slow backend data + generation. When invoked, it should return tuple, byte string + at most size length and flag. The flag is either + :py:data:`DATA_OK`, :py:data:`DATA_EOF` or + :py:data:`DATA_DEFERRED`. For non-empty byte string and it is + not the last chunk of response, :py:data:`DATA_OK` must be + returned as flag. If this is the last chunk of the response + (byte string could be ``None``), :py:data:`DATA_EOF` must be + returned as flag. If there is no data available right now, but + additional data are anticipated, return tuple (``None``, + :py:data:`DATA_DEFERRED`). When data arrived, call + :py:meth:`resume()` and restart response body transmission. + + Only the body generator can pause response body generation; + instance of :py:class:`io.IOBase` must not block. + + If instance of ``str`` is specified as *body*, it will be + encoded using UTF-8. + + The *headers* is a list of tuple of the form ``(name, + value)``. The ``name`` and ``value`` can be either byte string + or unicode string. In the latter case, they will be encoded + using UTF-8. + + Raises the exception if any error occurs. + + .. py:method:: push(path, method='GET', request_headers=None, status=200, headers=None, body=None) + + Push a specified resource. The *path* is a path portion of + request URI for this resource. The *method* is a method to + access this resource. The *request_headers* is additional + request headers to access this resource. The ``:scheme``, + ``:method``, ``:authority`` and ``:path`` are appended by the + library. The ``:scheme`` and ``:authority`` are inherited from + request header fields of the associated stream. + + The *status* is HTTP status code. The *headers* is additional + response headers. The ``:status`` header field is appended by + the library. The *body* is the response body. It has the same + semantics of *body* parameter of :py:meth:`send_response()`. + + The headers and request_headers are a list of tuple of the form + ``(name, value)``. The ``name`` and ``value`` can be either byte + string or unicode string. In the latter case, they will be + encoded using UTF-8. + + Returns an instance of ``RequestHandlerClass`` specified in + :py:class:`HTTP2Server` constructor for the pushed resource. + + Raises the exception if any error occurs. + + .. py:method:: resume() + + Signals the restarting of response body transmission paused by + ``DATA_DEFERRED`` from the body generator (see + :py:meth:`send_response()` about the body generator). It is not + an error calling this method while response body transmission is + not paused. + +.. py:data:: DATA_OK + + ``DATA_OK`` indicates non empty data is generated from body generator. + +.. py:data:: DATA_EOF + + ``DATA_EOF`` indicates the end of response body. + +.. py:data:: DATA_DEFERRED + + ``DATA_DEFERRED`` indicates that data are not available right now + and response should be paused. + +The following example illustrates :py:class:`HTTP2Server` and +:py:class:`BaseRequestHandler` usage: + +.. code-block:: python + + #!/usr/bin/env python + + import io, ssl + + import nghttp2 + + class Handler(nghttp2.BaseRequestHandler): + + def on_headers(self): + self.push(path='/css/style.css', + request_headers = [('content-type', 'text/css')], + status=200, + body='body{margin:0;}') + + self.send_response(status=200, + headers = [('content-type', 'text/plain')], + body=io.BytesIO(b'nghttp2-python FTW')) + + ctx = ssl.SSLContext(ssl.PROTOCOL_SSLv23) + ctx.options = ssl.OP_ALL | ssl.OP_NO_SSLv2 | ssl.OP_NO_SSLv3 + ctx.load_cert_chain('server.crt', 'server.key') + + # give None to ssl to make the server non-SSL/TLS + server = nghttp2.HTTP2Server(('127.0.0.1', 8443), Handler, ssl=ctx) + server.serve_forever() + +The following example illustrates HTTP/2 server using asynchronous +response body generation. This is simplified reverse proxy: + +.. code-block:: python + + #!/usr/bin/env python + + import ssl + import os + import urllib + import asyncio + import io + + import nghttp2 + + @asyncio.coroutine + def get_http_header(handler, url): + url = urllib.parse.urlsplit(url) + ssl = url.scheme == 'https' + if url.port == None: + if url.scheme == 'https': + port = 443 + else: + port = 80 + else: + port = url.port + + connect = asyncio.open_connection(url.hostname, port, ssl=ssl) + reader, writer = yield from connect + req = 'GET {path} HTTP/1.0\r\n\r\n'.format(path=url.path or '/') + writer.write(req.encode('utf-8')) + # skip response header fields + while True: + line = yield from reader.readline() + line = line.rstrip() + if not line: + break + # read body + while True: + b = yield from reader.read(4096) + if not b: + break + handler.buf.write(b) + writer.close() + handler.buf.seek(0) + handler.eof = True + handler.resume() + + class Body: + def __init__(self, handler): + self.handler = handler + self.handler.eof = False + self.handler.buf = io.BytesIO() + + def generate(self, n): + buf = self.handler.buf + data = buf.read1(n) + if not data and not self.handler.eof: + return None, nghttp2.DATA_DEFERRED + return data, nghttp2.DATA_EOF if self.handler.eof else nghttp2.DATA_OK + + class Handler(nghttp2.BaseRequestHandler): + + def on_headers(self): + body = Body(self) + asyncio.async(get_http_header( + self, 'http://localhost' + self.path.decode('utf-8'))) + self.send_response(status=200, body=body.generate) + + ctx = ssl.SSLContext(ssl.PROTOCOL_SSLv23) + ctx.options = ssl.OP_ALL | ssl.OP_NO_SSLv2 | ssl.OP_NO_SSLv3 + ctx.load_cert_chain('server.crt', 'server.key') + + server = nghttp2.HTTP2Server(('127.0.0.1', 8443), Handler, ssl=ctx) + server.serve_forever() diff --git a/doc/sources/tutorial-client.rst b/doc/sources/tutorial-client.rst new file mode 100644 index 0000000..6ce1238 --- /dev/null +++ b/doc/sources/tutorial-client.rst @@ -0,0 +1,439 @@ +Tutorial: HTTP/2 client +========================= + +In this tutorial, we are going to write very primitive HTTP/2 +client. The complete source code, `libevent-client.c`_, is attached at +the end of this page. It also resides in examples directory in the +archive or repository. + +This simple client takes 1 argument, HTTPS URI, and retrieves the +resource denoted by the URI. Its synopsis is like this:: + + $ libevent-client HTTPS_URI + +We use libevent in this tutorial to handle networking I/O. Please +note that nghttp2 itself does not depend on libevent. + +First we do some setup routine for libevent and OpenSSL library in +function ``main()`` and ``run()``, which is not so relevant to nghttp2 +library use. The one thing you should look at is setup NPN callback. +The NPN callback is used for the client to select the next application +protocol over the SSL/TLS transport. In this tutorial, we use +`nghttp2_select_next_protocol()` function to select the HTTP/2 +protocol the library supports:: + + static int select_next_proto_cb(SSL *ssl _U_, unsigned char **out, + unsigned char *outlen, const unsigned char *in, + unsigned int inlen, void *arg _U_) { + if (nghttp2_select_next_protocol(out, outlen, in, inlen) <= 0) { + errx(1, "Server did not advertise " NGHTTP2_PROTO_VERSION_ID); + } + return SSL_TLSEXT_ERR_OK; + } + +The callback is set to the SSL_CTX object using +``SSL_CTX_set_next_proto_select_cb()`` function:: + + static SSL_CTX *create_ssl_ctx(void) { + SSL_CTX *ssl_ctx; + ssl_ctx = SSL_CTX_new(SSLv23_client_method()); + if (!ssl_ctx) { + errx(1, "Could not create SSL/TLS context: %s", + ERR_error_string(ERR_get_error(), NULL)); + } + SSL_CTX_set_options(ssl_ctx, + SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | + SSL_OP_NO_COMPRESSION | + SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION); + SSL_CTX_set_next_proto_select_cb(ssl_ctx, select_next_proto_cb, NULL); + return ssl_ctx; + } + +We use ``http2_session_data`` structure to store the data related to +the HTTP/2 session:: + + typedef struct { + nghttp2_session *session; + struct evdns_base *dnsbase; + struct bufferevent *bev; + http2_stream_data *stream_data; + } http2_session_data; + +Since this program only handles 1 URI, it uses only 1 stream. We store +its stream specific data in ``http2_stream_data`` structure and the +``stream_data`` points to it. The ``struct http2_stream_data`` is +defined as follows:: + + typedef struct { + /* The NULL-terminated URI string to retrieve. */ + const char *uri; + /* Parsed result of the |uri| */ + struct http_parser_url *u; + /* The authority portion of the |uri|, not NULL-terminated */ + char *authority; + /* The path portion of the |uri|, including query, not + NULL-terminated */ + char *path; + /* The length of the |authority| */ + size_t authoritylen; + /* The length of the |path| */ + size_t pathlen; + /* The stream ID of this stream */ + int32_t stream_id; + } http2_stream_data; + +We creates and initializes these structures in +``create_http2_session_data()`` and ``create_http2_stream_data()`` +respectively. + +Then we call function ``initiate_connection()`` to start connecting to +the remote server:: + + static void initiate_connection(struct event_base *evbase, SSL_CTX *ssl_ctx, + const char *host, uint16_t port, + http2_session_data *session_data) { + int rv; + struct bufferevent *bev; + SSL *ssl; + + ssl = create_ssl(ssl_ctx); + bev = bufferevent_openssl_socket_new( + evbase, -1, ssl, BUFFEREVENT_SSL_CONNECTING, + BEV_OPT_DEFER_CALLBACKS | BEV_OPT_CLOSE_ON_FREE); + bufferevent_setcb(bev, readcb, writecb, eventcb, session_data); + rv = bufferevent_socket_connect_hostname(bev, session_data->dnsbase, + AF_UNSPEC, host, port); + + if (rv != 0) { + errx(1, "Could not connect to the remote host %s", host); + } + session_data->bev = bev; + } + +We set 3 callbacks for the bufferevent: ``reacb``, ``writecb`` and +``eventcb``. + +The ``eventcb()`` is invoked by libevent event loop when an event +(e.g., connection has been established, timeout, etc) happens on the +underlying network socket:: + + static void eventcb(struct bufferevent *bev, short events, void *ptr) { + http2_session_data *session_data = (http2_session_data *)ptr; + if (events & BEV_EVENT_CONNECTED) { + int fd = bufferevent_getfd(bev); + int val = 1; + fprintf(stderr, "Connected\n"); + setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char *)&val, sizeof(val)); + initialize_nghttp2_session(session_data); + send_client_connection_header(session_data); + submit_request(session_data); + if (session_send(session_data) != 0) { + delete_http2_session_data(session_data); + } + return; + } + if (events & BEV_EVENT_EOF) { + warnx("Disconnected from the remote host"); + } else if (events & BEV_EVENT_ERROR) { + warnx("Network error"); + } else if (events & BEV_EVENT_TIMEOUT) { + warnx("Timeout"); + } + delete_http2_session_data(session_data); + } + +For ``BEV_EVENT_EOF``, ``BEV_EVENT_ERROR`` and ``BEV_EVENT_TIMEOUT`` +event, we just simply tear down the connection. The +``BEV_EVENT_CONNECTED`` event is invoked when SSL/TLS handshake is +finished successfully. We first initialize nghttp2 session object in +``initialize_nghttp2_session()`` function:: + + static void initialize_nghttp2_session(http2_session_data *session_data) { + nghttp2_session_callbacks *callbacks; + + nghttp2_session_callbacks_new(&callbacks); + + nghttp2_session_callbacks_set_send_callback(callbacks, send_callback); + + nghttp2_session_callbacks_set_on_frame_recv_callback(callbacks, + on_frame_recv_callback); + + nghttp2_session_callbacks_set_on_data_chunk_recv_callback( + callbacks, on_data_chunk_recv_callback); + + nghttp2_session_callbacks_set_on_stream_close_callback( + callbacks, on_stream_close_callback); + + nghttp2_session_callbacks_set_on_header_callback(callbacks, + on_header_callback); + + nghttp2_session_callbacks_set_on_begin_headers_callback( + callbacks, on_begin_headers_callback); + + nghttp2_session_client_new(&session_data->session, callbacks, session_data); + + nghttp2_session_callbacks_del(callbacks); + } + +Since we are creating client, we use `nghttp2_session_client_new()` to +initialize nghttp2 session object. We setup 7 callbacks for the +nghttp2 session. We'll explain these callbacks later. + +The `delete_http2_session_data()` destroys ``session_data`` and frees +its bufferevent, so it closes underlying connection as well. It also +calls `nghttp2_session_del()` to delete nghttp2 session object. + +We begin HTTP/2 communication by sending client connection preface, +which is 24 bytes magic byte string (:macro:`NGHTTP2_CLIENT_MAGIC`) +followed by SETTINGS frame. First 24 bytes magic string is +automatically sent by nghttp2 library. We send SETTINGS frame in +``send_client_connection_header()``:: + + static void send_client_connection_header(http2_session_data *session_data) { + nghttp2_settings_entry iv[1] = { + {NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100}}; + int rv; + + /* client 24 bytes magic string will be sent by nghttp2 library */ + rv = nghttp2_submit_settings(session_data->session, NGHTTP2_FLAG_NONE, iv, + ARRLEN(iv)); + if (rv != 0) { + errx(1, "Could not submit SETTINGS: %s", nghttp2_strerror(rv)); + } + } + +Here we specify SETTINGS_MAX_CONCURRENT_STREAMS to 100, which is +really not needed for this tiny example program, but we are +demonstrating the use of SETTINGS frame. To queue the SETTINGS frame +for the transmission, we use `nghttp2_submit_settings()`. Note that +`nghttp2_submit_settings()` function only queues the frame and not +actually send it. All ``nghttp2_submit_*()`` family functions have +this property. To actually send the frame, `nghttp2_session_send()` is +used, which is described about later. + +After the transmission of client connection header, we enqueue HTTP +request in ``submit_request()`` function:: + + static void submit_request(http2_session_data *session_data) { + int32_t stream_id; + http2_stream_data *stream_data = session_data->stream_data; + const char *uri = stream_data->uri; + const struct http_parser_url *u = stream_data->u; + nghttp2_nv hdrs[] = { + MAKE_NV2(":method", "GET"), + MAKE_NV(":scheme", &uri[u->field_data[UF_SCHEMA].off], + u->field_data[UF_SCHEMA].len), + MAKE_NV(":authority", stream_data->authority, stream_data->authoritylen), + MAKE_NV(":path", stream_data->path, stream_data->pathlen)}; + fprintf(stderr, "Request headers:\n"); + print_headers(stderr, hdrs, ARRLEN(hdrs)); + stream_id = nghttp2_submit_request(session_data->session, NULL, hdrs, + ARRLEN(hdrs), NULL, stream_data); + if (stream_id < 0) { + errx(1, "Could not submit HTTP request: %s", nghttp2_strerror(stream_id)); + } + + stream_data->stream_id = stream_id; + } + +We build HTTP request header fields in ``hdrs`` which is an array of +:type:`nghttp2_nv`. There are 4 header fields to be sent: ``:method``, +``:scheme``, ``:authority`` and ``:path``. To queue this HTTP request, +we use `nghttp2_submit_request()` function. The `stream_data` is +passed in *stream_user_data* parameter. It is used in nghttp2 +callbacks which we'll describe about later. +`nghttp2_submit_request()` returns the newly assigned stream ID for +this request. + +The next bufferevent callback is ``readcb()``, which is invoked when +data is available to read in the bufferevent input buffer:: + + static void readcb(struct bufferevent *bev, void *ptr) { + http2_session_data *session_data = (http2_session_data *)ptr; + ssize_t readlen; + struct evbuffer *input = bufferevent_get_input(bev); + size_t datalen = evbuffer_get_length(input); + unsigned char *data = evbuffer_pullup(input, -1); + + readlen = nghttp2_session_mem_recv(session_data->session, data, datalen); + if (readlen < 0) { + warnx("Fatal error: %s", nghttp2_strerror((int)readlen)); + delete_http2_session_data(session_data); + return; + } + if (evbuffer_drain(input, readlen) != 0) { + warnx("Fatal error: evbuffer_drain failed"); + delete_http2_session_data(session_data); + return; + } + if (session_send(session_data) != 0) { + delete_http2_session_data(session_data); + return; + } + } + +In this function, we feed all unprocessed, received data to nghttp2 +session object using `nghttp2_session_mem_recv()` function. The +`nghttp2_session_mem_recv()` processes the received data and may +invoke nghttp2 callbacks and also queue frames. Since there may be +pending frames, we call ``session_send()`` function to send those +frames. The ``session_send()`` function is defined as follows:: + + static int session_send(http2_session_data *session_data) { + int rv; + + rv = nghttp2_session_send(session_data->session); + if (rv != 0) { + warnx("Fatal error: %s", nghttp2_strerror(rv)); + return -1; + } + return 0; + } + +The `nghttp2_session_send()` function serializes the frame into wire +format and call ``send_callback()`` function of type +:type:`nghttp2_send_callback`. The ``send_callback()`` is defined as +follows:: + + static ssize_t send_callback(nghttp2_session *session _U_, const uint8_t *data, + size_t length, int flags _U_, void *user_data) { + http2_session_data *session_data = (http2_session_data *)user_data; + struct bufferevent *bev = session_data->bev; + bufferevent_write(bev, data, length); + return length; + } + +Since we use bufferevent to abstract network I/O, we just write the +data to the bufferevent object. Note that `nghttp2_session_send()` +continues to write all frames queued so far. If we were writing the +data to the non-blocking socket directly using ``write()`` system call +in the ``send_callback()``, we will surely get ``EAGAIN`` or +``EWOULDBLOCK`` since the socket has limited send buffer. If that +happens, we can return :macro:`NGHTTP2_ERR_WOULDBLOCK` to signal the +nghttp2 library to stop sending further data. But writing to the +bufferevent, we have to regulate the amount data to be buffered by +ourselves to avoid possible huge memory consumption. In this example +client, we do not limit anything. To see how to regulate the amount of +buffered data, see the ``send_callback()`` in the server tutorial. + +The third bufferevent callback is ``writecb()``, which is invoked when +all data written in the bufferevent output buffer have been sent:: + + static void writecb(struct bufferevent *bev _U_, void *ptr) { + http2_session_data *session_data = (http2_session_data *)ptr; + if (nghttp2_session_want_read(session_data->session) == 0 && + nghttp2_session_want_write(session_data->session) == 0 && + evbuffer_get_length(bufferevent_get_output(session_data->bev)) == 0) { + delete_http2_session_data(session_data); + } + } + +As described earlier, we just write off all data in `send_callback()`, +we have no data to write in this function. All we have to do is check +we have to drop connection or not. The nghttp2 session object keeps +track of reception and transmission of GOAWAY frame and other error +conditions as well. Using these information, nghttp2 session object +will tell whether the connection should be dropped or not. More +specifically, both `nghttp2_session_want_read()` and +`nghttp2_session_want_write()` return 0, we have no business in the +connection. But since we are using bufferevent and its deferred +callback option, the bufferevent output buffer may contain the pending +data when the ``writecb()`` is called. To handle this situation, we +also check whether the output buffer is empty or not. If these +conditions are met, we drop connection. + +We have already described about nghttp2 callback ``send_callback()``. +Let's describe remaining nghttp2 callbacks we setup in +``initialize_nghttp2_setup()`` function. + +Each request header name/value pair is emitted via +``on_header_callback`` function:: + + static int on_header_callback(nghttp2_session *session _U_, + const nghttp2_frame *frame, const uint8_t *name, + size_t namelen, const uint8_t *value, + size_t valuelen, uint8_t flags _U_, + void *user_data) { + http2_session_data *session_data = (http2_session_data *)user_data; + switch (frame->hd.type) { + case NGHTTP2_HEADERS: + if (frame->headers.cat == NGHTTP2_HCAT_RESPONSE && + session_data->stream_data->stream_id == frame->hd.stream_id) { + /* Print response headers for the initiated request. */ + print_header(stderr, name, namelen, value, valuelen); + break; + } + } + return 0; + } + +In this tutorial, we just print the name/value pair. + +After all name/value pairs are emitted for a frame, +``on_frame_recv_callback`` function is called:: + + static int on_frame_recv_callback(nghttp2_session *session _U_, + const nghttp2_frame *frame, void *user_data) { + http2_session_data *session_data = (http2_session_data *)user_data; + switch (frame->hd.type) { + case NGHTTP2_HEADERS: + if (frame->headers.cat == NGHTTP2_HCAT_RESPONSE && + session_data->stream_data->stream_id == frame->hd.stream_id) { + fprintf(stderr, "All headers received\n"); + } + break; + } + return 0; + } + +In this tutorial, we are just interested in the HTTP response +HEADERS. We check the frame type and its category (it should be +:macro:`NGHTTP2_HCAT_RESPONSE` for HTTP response HEADERS). Also check +its stream ID. + +The ``on_data_chunk_recv_callback()`` function is invoked when a chunk +of data is received from the remote peer:: + + static int on_data_chunk_recv_callback(nghttp2_session *session _U_, + uint8_t flags _U_, int32_t stream_id, + const uint8_t *data, size_t len, + void *user_data) { + http2_session_data *session_data = (http2_session_data *)user_data; + if (session_data->stream_data->stream_id == stream_id) { + fwrite(data, len, 1, stdout); + } + return 0; + } + +In our case, a chunk of data is response body. After checking stream +ID, we just write the received data to the stdout. Note that the +output in the terminal may be corrupted if the response body contains +some binary data. + +The ``on_stream_close_callback()`` function is invoked when the stream +is about to close:: + + static int on_stream_close_callback(nghttp2_session *session, int32_t stream_id, + nghttp2_error_code error_code, + void *user_data) { + http2_session_data *session_data = (http2_session_data *)user_data; + int rv; + + if (session_data->stream_data->stream_id == stream_id) { + fprintf(stderr, "Stream %d closed with error_code=%d\n", stream_id, + error_code); + rv = nghttp2_session_terminate_session(session, NGHTTP2_NO_ERROR); + if (rv != 0) { + return NGHTTP2_ERR_CALLBACK_FAILURE; + } + } + return 0; + } + +If the stream ID matches the one we initiated, it means that its +stream is going to be closed. Since we have finished to get the +resource we want (or the stream was reset by RST_STREAM from the +remote peer), we call `nghttp2_session_terminate_session()` to +commencing the closure of the HTTP/2 session gracefully. If you have +some data associated for the stream to be closed, you may delete it +here. diff --git a/doc/sources/tutorial-hpack.rst b/doc/sources/tutorial-hpack.rst new file mode 100644 index 0000000..0b4d59f --- /dev/null +++ b/doc/sources/tutorial-hpack.rst @@ -0,0 +1,118 @@ +Tutorial: HPACK API +=================== + +In this tutorial, we describe basic use of HPACK API in nghttp2 +library. We briefly describe APIs for deflating and inflating header +fields. The example of using these APIs are presented as complete +source code `deflate.c`_. + +Deflating (encoding) headers +---------------------------- + +First we need to initialize :type:`nghttp2_hd_deflater` object using +`nghttp2_hd_deflate_new()` function:: + + int nghttp2_hd_deflate_new(nghttp2_hd_deflater **deflater_ptr, + size_t deflate_hd_table_bufsize_max); + +This function allocates :type:`nghttp2_hd_deflater` object and +initializes it and assigns its pointer to ``*deflater_ptr`` passed by +parameter. The *deflate_hd_table_bufsize_max* is the upper bound of +header table size the deflater will use. This will limit the memory +usage in deflater object for dynamic header table. If you doubt, just +specify 4096 here, which is the default upper bound of dynamic header +table buffer size. + +To encode header fields, `nghttp2_hd_deflate_hd()` function:: + + ssize_t nghttp2_hd_deflate_hd(nghttp2_hd_deflater *deflater, + uint8_t *buf, size_t buflen, + const nghttp2_nv *nva, size_t nvlen); + +The *deflater* is the deflater object initialized by +`nghttp2_hd_deflate_new()` function described above. The *buf* is a +pointer to buffer to store encoded byte string. The *buflen* is +capacity of *buf*. The *nva* is a pointer to :type:`nghttp2_nv`, +which is an array of header fields to deflate. The *nvlen* is the +number of header fields which *nva* contains. + +It is important to initialize and assign all members of +:type:`nghttp2_nv`. If a header field should not be inserted in +dynamic header table for a security reason, set +:macro:`NGHTTP2_NV_FLAG_NO_INDEX` flag in :member:`nghttp2_nv.flags`. + +`nghttp2_hd_deflate_hd()` processes all headers given in *nva*. The +*nva* must include all request or response header fields to be sent in +one HEADERS (or optionally following (multiple) CONTINUATION +frame(s)). The *buf* must have enough space to store the encoded +result. Otherwise, the function will fail. To estimate the upper +bound of encoded result, use `nghttp2_hd_deflate_bound()` function:: + + size_t nghttp2_hd_deflate_bound(nghttp2_hd_deflater *deflater, + const nghttp2_nv *nva, size_t nvlen); + +Pass this function with the same parameters *deflater*, *nva* and +*nvlen* which will be passed to `nghttp2_hd_deflate_hd()`. + +The subsequent call of `nghttp2_hd_deflate_hd()` will use current +encoder state and perform differential encoding which is the +fundamental compression gain for HPACK. + +Once `nghttp2_hd_deflate_hd()` fails, it cannot be undone and its +further call with the same deflater object shall fail. So it is very +important to use `nghttp2_hd_deflate_bound()` to know the required +size of buffer. + +To delete :type:`nghttp2_hd_deflater` object, use `nghttp2_hd_deflate_del()` +function. + +Inflating (decoding) headers +---------------------------- + +We use :type:`nghttp2_hd_inflater` object to inflate compressed header +data. To initialize the object, use `nghttp2_hd_inflate_new()`:: + + int nghttp2_hd_inflate_new(nghttp2_hd_inflater **inflater_ptr); + +To inflate header data, use `nghttp2_hd_inflate_hd()` function:: + + ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_inflater *inflater, + nghttp2_nv *nv_out, int *inflate_flags, + uint8_t *in, size_t inlen, int in_final); + +The *inflater* is the inflater object initialized above. The *nv_out* +is a pointer to :type:`nghttp2_nv` to store the result. The *in* is a +pointer to input data and *inlen* is its length. The caller is not +required to specify whole deflated header data to *in* at once. It +can call this function multiple times for portion of the data in +streaming way. If *in_final* is nonzero, it tells the function that +the passed data is the final sequence of deflated header data. The +*inflate_flags* is output parameter and successful call of this +function stores a set of flags in it. It will be described later. + +This function returns when each header field is inflated. When this +happens, the function sets :macro:`NGHTTP2_HD_INFLATE_EMIT` flag to +*inflate_flag* parameter and header field is stored in *nv_out*. The +return value indicates the number of data read from *in* to processed +so far. It may be less than *inlen*. The caller should call the +function repeatedly until all data are processed by adjusting *in* and +*inlen* with the processed bytes. + +If *in_final* is nonzero and all given data was processed, the +function sets :macro:`NGHTTP2_HD_INFLATE_FINAL` flag to +*inflate_flag*. If the caller sees this flag set, call +`nghttp2_hd_inflate_end_headers()` function. + +If *in_final* is zero and :macro:`NGHTTP2_HD_INFLATE_EMIT` flag is not +set, it indicates that all given data was processed. The caller is +required to pass subsequent data. + +It is important to note that the function may produce one or more +header fields even if *inlen* is 0 when *in_final* is nonzero, due to +differential encoding. + +The example use of `nghttp2_hd_inflate_hd()` is shown in +`inflate_header_block()` function in `deflate.c`_. + +To delete :type:`nghttp2_hd_inflater` object, use `nghttp2_hd_inflate_del()` +function. diff --git a/doc/sources/tutorial-server.rst b/doc/sources/tutorial-server.rst new file mode 100644 index 0000000..8f5b583 --- /dev/null +++ b/doc/sources/tutorial-server.rst @@ -0,0 +1,550 @@ +Tutorial: HTTP/2 server +========================= + +In this tutorial, we are going to write single-threaded, event-based +HTTP/2 web server, which supports HTTPS only. It can handle +concurrent multiple requests, but only the GET method is supported. The +complete source code, `libevent-server.c`_, is attached at the end of +this page. It also resides in examples directory in the archive or +repository. + +This simple server takes 3 arguments, a port number to listen to, a path to +your SSL/TLS private key file and a path to your certificate file. Its +synopsis is like this:: + + $ libevent-server PORT /path/to/server.key /path/to/server.crt + +We use libevent in this tutorial to handle networking I/O. Please +note that nghttp2 itself does not depend on libevent. + +First we create a setup routine for libevent and OpenSSL in the functions +``main()`` and ``run()``. One thing in there you should look at, is the setup +of the NPN callback. The NPN callback is used for the server to advertise +which application protocols the server supports to a client. In this example +program, when creating ``SSL_CTX`` object, we store the application protocol +name in the wire format of NPN in a statically allocated buffer. This is safe +because we only create one ``SSL_CTX`` object in the program's entire life +time:: + + static unsigned char next_proto_list[256]; + static size_t next_proto_list_len; + + static int next_proto_cb(SSL *s _U_, const unsigned char **data, + unsigned int *len, void *arg _U_) { + *data = next_proto_list; + *len = (unsigned int)next_proto_list_len; + return SSL_TLSEXT_ERR_OK; + } + + static SSL_CTX *create_ssl_ctx(const char *key_file, const char *cert_file) { + SSL_CTX *ssl_ctx; + EC_KEY *ecdh; + + ssl_ctx = SSL_CTX_new(SSLv23_server_method()); + + ... + + next_proto_list[0] = NGHTTP2_PROTO_VERSION_ID_LEN; + memcpy(&next_proto_list[1], NGHTTP2_PROTO_VERSION_ID, + NGHTTP2_PROTO_VERSION_ID_LEN); + next_proto_list_len = 1 + NGHTTP2_PROTO_VERSION_ID_LEN; + + SSL_CTX_set_next_protos_advertised_cb(ssl_ctx, next_proto_cb, NULL); + return ssl_ctx; + } + +The wire format of NPN is a sequence of length prefixed string. Exactly one +byte is used to specify the length of each protocol identifier. In this +tutorial, we advertise the specific HTTP/2 protocol version the current +nghttp2 library supports. The nghttp2 library exports its identifier in +:macro:`NGHTTP2_PROTO_VERSION_ID`. The ``next_proto_cb()`` function is the +server-side NPN callback. In the OpenSSL implementation, we just assign the +pointer to the NPN buffers we filled in earlier. The NPN callback function is +set to the ``SSL_CTX`` object using +``SSL_CTX_set_next_protos_advertised_cb()``. + +We use the ``app_content`` structure to store application-wide data:: + + struct app_context { + SSL_CTX *ssl_ctx; + struct event_base *evbase; + }; + +We use the ``http2_session_data`` structure to store session-level +(which corresponds to one HTTP/2 connection) data:: + + typedef struct http2_session_data { + struct http2_stream_data root; + struct bufferevent *bev; + app_context *app_ctx; + nghttp2_session *session; + char *client_addr; + } http2_session_data; + +We use the ``http2_stream_data`` structure to store stream-level data:: + + typedef struct http2_stream_data { + struct http2_stream_data *prev, *next; + char *request_path; + int32_t stream_id; + int fd; + } http2_stream_data; + +A single HTTP/2 session can have multiple streams. We manage these +multiple streams with a doubly linked list. The first element of this +list is pointed to by the ``root->next`` in ``http2_session_data``. +Initially, ``root->next`` is ``NULL``. We use libevent's bufferevent +structure to perform network I/O. Note that the bufferevent object is +kept in ``http2_session_data`` and not in ``http2_stream_data``. This +is because ``http2_stream_data`` is just a logical stream multiplexed +over the single connection managed by bufferevent in +``http2_session_data``. + +We first create a listener object to accept incoming connections. We use +libevent's ``struct evconnlistener`` for this purpose:: + + static void start_listen(struct event_base *evbase, const char *service, + app_context *app_ctx) { + int rv; + struct addrinfo hints; + struct addrinfo *res, *rp; + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_PASSIVE; + #ifdef AI_ADDRCONFIG + hints.ai_flags |= AI_ADDRCONFIG; + #endif /* AI_ADDRCONFIG */ + + rv = getaddrinfo(NULL, service, &hints, &res); + if (rv != 0) { + errx(1, NULL); + } + for (rp = res; rp; rp = rp->ai_next) { + struct evconnlistener *listener; + listener = evconnlistener_new_bind( + evbase, acceptcb, app_ctx, LEV_OPT_CLOSE_ON_FREE | LEV_OPT_REUSEABLE, + 16, rp->ai_addr, rp->ai_addrlen); + if (listener) { + freeaddrinfo(res); + + return; + } + } + errx(1, "Could not start listener"); + } + +We specify the ``acceptcb`` callback which is called when a new connection is +accepted:: + + static void acceptcb(struct evconnlistener *listener _U_, int fd, + struct sockaddr *addr, int addrlen, void *arg) { + app_context *app_ctx = (app_context *)arg; + http2_session_data *session_data; + + session_data = create_http2_session_data(app_ctx, fd, addr, addrlen); + + bufferevent_setcb(session_data->bev, readcb, writecb, eventcb, session_data); + } + +Here we create the ``http2_session_data`` object. The bufferevent for +this connection is also initialized at this time. We specify three +callbacks for the bufferevent: ``readcb``, ``writecb`` and +``eventcb``. + +The ``eventcb()`` callback is invoked by the libevent event loop when an event +(e.g., connection has been established, timeout, etc) happens on the +underlying network socket:: + + static void eventcb(struct bufferevent *bev _U_, short events, void *ptr) { + http2_session_data *session_data = (http2_session_data *)ptr; + if (events & BEV_EVENT_CONNECTED) { + fprintf(stderr, "%s connected\n", session_data->client_addr); + + initialize_nghttp2_session(session_data); + + if (send_server_connection_header(session_data) != 0) { + delete_http2_session_data(session_data); + return; + } + + return; + } + if (events & BEV_EVENT_EOF) { + fprintf(stderr, "%s EOF\n", session_data->client_addr); + } else if (events & BEV_EVENT_ERROR) { + fprintf(stderr, "%s network error\n", session_data->client_addr); + } else if (events & BEV_EVENT_TIMEOUT) { + fprintf(stderr, "%s timeout\n", session_data->client_addr); + } + delete_http2_session_data(session_data); + } + +For the ``BEV_EVENT_EOF``, ``BEV_EVENT_ERROR`` and +``BEV_EVENT_TIMEOUT`` events, we just simply tear down the connection. +The ``delete_http2_session_data()`` function destroys the +``http2_session_data`` object and thus also its bufferevent member. +As a result, the underlying connection is closed. The +``BEV_EVENT_CONNECTED`` event is invoked when SSL/TLS handshake is +finished successfully. Now we are ready to start the HTTP/2 +communication. + +We initialize a nghttp2 session object which is done in +``initialize_nghttp2_session()``:: + + static void initialize_nghttp2_session(http2_session_data *session_data) { + nghttp2_session_callbacks *callbacks; + + nghttp2_session_callbacks_new(&callbacks); + + nghttp2_session_callbacks_set_send_callback(callbacks, send_callback); + + nghttp2_session_callbacks_set_on_frame_recv_callback(callbacks, + on_frame_recv_callback); + + nghttp2_session_callbacks_set_on_stream_close_callback( + callbacks, on_stream_close_callback); + + nghttp2_session_callbacks_set_on_header_callback(callbacks, + on_header_callback); + + nghttp2_session_callbacks_set_on_begin_headers_callback( + callbacks, on_begin_headers_callback); + + nghttp2_session_server_new(&session_data->session, callbacks, session_data); + + nghttp2_session_callbacks_del(callbacks); + } + +Since we are creating a server and uses options, the nghttp2 session +object is created using `nghttp2_session_server_new2()` function. We +registers five callbacks for nghttp2 session object. We'll talk about +these callbacks later. + +After initialization of the nghttp2 session object, we are going to send +a server connection header in ``send_server_connection_header()``:: + + static int send_server_connection_header(http2_session_data *session_data) { + nghttp2_settings_entry iv[1] = { + {NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100}}; + int rv; + + rv = nghttp2_submit_settings(session_data->session, NGHTTP2_FLAG_NONE, iv, + ARRLEN(iv)); + if (rv != 0) { + warnx("Fatal error: %s", nghttp2_strerror(rv)); + return -1; + } + return 0; + } + +The server connection header is a SETTINGS frame. We specify +SETTINGS_MAX_CONCURRENT_STREAMS to 100 in the SETTINGS frame. To queue +the SETTINGS frame for the transmission, we use +`nghttp2_submit_settings()`. Note that `nghttp2_submit_settings()` +function only queues the frame and it does not actually send it. All +functions in the ``nghttp2_submit_*()`` family have this property. To +actually send the frame, `nghttp2_session_send()` should be used, as +described later. + +Since bufferevent may buffer more than the first 24 bytes from the client, we +have to process them here since libevent won't invoke callback functions for +this pending data. To process the received data, we call the +``session_recv()`` function:: + + static int session_recv(http2_session_data *session_data) { + ssize_t readlen; + struct evbuffer *input = bufferevent_get_input(session_data->bev); + size_t datalen = evbuffer_get_length(input); + unsigned char *data = evbuffer_pullup(input, -1); + + readlen = nghttp2_session_mem_recv(session_data->session, data, datalen); + if (readlen < 0) { + warnx("Fatal error: %s", nghttp2_strerror((int)readlen)); + return -1; + } + if (evbuffer_drain(input, readlen) != 0) { + warnx("Fatal error: evbuffer_drain failed"); + return -1; + } + if (session_send(session_data) != 0) { + return -1; + } + return 0; + } + +In this function, we feed all unprocessed but already received data to the +nghttp2 session object using the `nghttp2_session_mem_recv()` function. The +`nghttp2_session_mem_recv()` function processes the data and may invoke the +nghttp2 callbacks and also queue outgoing frames. Since there may be pending +outgoing frames, we call ``session_send()`` function to send off those +frames. The ``session_send()`` function is defined as follows:: + + static int session_send(http2_session_data *session_data) { + int rv; + rv = nghttp2_session_send(session_data->session); + if (rv != 0) { + warnx("Fatal error: %s", nghttp2_strerror(rv)); + return -1; + } + return 0; + } + +The `nghttp2_session_send()` function serializes the frame into wire +format and calls ``send_callback()`` of type +:type:`nghttp2_send_callback`. The ``send_callback()`` is defined as +follows:: + + static ssize_t send_callback(nghttp2_session *session _U_, const uint8_t *data, + size_t length, int flags _U_, void *user_data) { + http2_session_data *session_data = (http2_session_data *)user_data; + struct bufferevent *bev = session_data->bev; + /* Avoid excessive buffering in server side. */ + if (evbuffer_get_length(bufferevent_get_output(session_data->bev)) >= + OUTPUT_WOULDBLOCK_THRESHOLD) { + return NGHTTP2_ERR_WOULDBLOCK; + } + bufferevent_write(bev, data, length); + return length; + } + +Since we use bufferevent to abstract network I/O, we just write the +data to the bufferevent object. Note that `nghttp2_session_send()` +continues to write all frames queued so far. If we were writing the +data to a non-blocking socket directly using ``write()`` system call +in the ``send_callback()``, we would surely get ``EAGAIN`` or +``EWOULDBLOCK`` back since the socket has limited send buffer. If that +happens, we can return :macro:`NGHTTP2_ERR_WOULDBLOCK` to signal the +nghttp2 library to stop sending further data. But when writing to the +bufferevent, we have to regulate the amount data to get buffered +ourselves to avoid using huge amounts of memory. To achieve this, we +check the size of the output buffer and if it reaches more than or +equal to ``OUTPUT_WOULDBLOCK_THRESHOLD`` bytes, we stop writing data +and return :macro:`NGHTTP2_ERR_WOULDBLOCK` to tell the library to stop +calling send_callback. + +The next bufferevent callback is ``readcb()``, which is invoked when +data is available to read in the bufferevent input buffer:: + + static void readcb(struct bufferevent *bev _U_, void *ptr) { + http2_session_data *session_data = (http2_session_data *)ptr; + if (session_recv(session_data) != 0) { + delete_http2_session_data(session_data); + return; + } + } + +In this function, we just call ``session_recv()`` to process incoming +data. + +The third bufferevent callback is ``writecb()``, which is invoked when all +data in the bufferevent output buffer has been sent:: + + static void writecb(struct bufferevent *bev, void *ptr) { + http2_session_data *session_data = (http2_session_data *)ptr; + if (evbuffer_get_length(bufferevent_get_output(bev)) > 0) { + return; + } + if (nghttp2_session_want_read(session_data->session) == 0 && + nghttp2_session_want_write(session_data->session) == 0) { + delete_http2_session_data(session_data); + return; + } + if (session_send(session_data) != 0) { + delete_http2_session_data(session_data); + return; + } + } + +First we check whether we should drop the connection or not. The nghttp2 +session object keeps track of reception and transmission of GOAWAY frames and +other error conditions as well. Using this information, the nghttp2 session +object will tell whether the connection should be dropped or not. More +specifically, if both `nghttp2_session_want_read()` and +`nghttp2_session_want_write()` return 0, we have no business left in the +connection. But since we are using bufferevent and its deferred callback +option, the bufferevent output buffer may contain pending data when the +``writecb()`` is called. To handle this, we check whether the output buffer is +empty or not. If all these conditions are met, we drop connection. + +Otherwise, we call ``session_send()`` to process the pending output +data. Remember that in ``send_callback()``, we must not write all data to +bufferevent to avoid excessive buffering. We continue processing pending data +when the output buffer becomes empty. + +We have already described the nghttp2 callback ``send_callback()``. Let's +learn about the remaining nghttp2 callbacks we setup in +``initialize_nghttp2_setup()`` function. + +The ``on_begin_headers_callback()`` function is invoked when the reception of +a header block in HEADERS or PUSH_PROMISE frame is started:: + + static int on_begin_headers_callback(nghttp2_session *session, + const nghttp2_frame *frame, + void *user_data) { + http2_session_data *session_data = (http2_session_data *)user_data; + http2_stream_data *stream_data; + + if (frame->hd.type != NGHTTP2_HEADERS || + frame->headers.cat != NGHTTP2_HCAT_REQUEST) { + return 0; + } + stream_data = create_http2_stream_data(session_data, frame->hd.stream_id); + nghttp2_session_set_stream_user_data(session, frame->hd.stream_id, + stream_data); + return 0; + } + +We are only interested in the HEADERS frame in this function. Since the +HEADERS frame has several roles in the HTTP/2 protocol, we check that it is a +request HEADERS, which opens new stream. If the frame is a request HEADERS, we +create a ``http2_stream_data`` object to store the stream related data. We +associate the created ``http2_stream_data`` object with the stream in the +nghttp2 session object using `nghttp2_set_stream_user_data()` to get the +object without searching through the doubly linked list. + +In this example server, we want to serve files relative to the current working +directory in which the program was invoked. Each header name/value pair is +emitted via ``on_header_callback`` function, which is called after +``on_begin_headers_callback()``:: + + static int on_header_callback(nghttp2_session *session, + const nghttp2_frame *frame, const uint8_t *name, + size_t namelen, const uint8_t *value, + size_t valuelen, uint8_t flags _U_, + void *user_data _U_) { + http2_stream_data *stream_data; + const char PATH[] = ":path"; + switch (frame->hd.type) { + case NGHTTP2_HEADERS: + if (frame->headers.cat != NGHTTP2_HCAT_REQUEST) { + break; + } + stream_data = + nghttp2_session_get_stream_user_data(session, frame->hd.stream_id); + if (!stream_data || stream_data->request_path) { + break; + } + if (namelen == sizeof(PATH) - 1 && memcmp(PATH, name, namelen) == 0) { + size_t j; + for (j = 0; j < valuelen && value[j] != '?'; ++j) + ; + stream_data->request_path = percent_decode(value, j); + } + break; + } + return 0; + } + +We search for the ``:path`` header field among the request headers and store +the requested path in the ``http2_stream_data`` object. In this example +program, we ignore ``:method`` header field and always treat the request as a +GET request. + +The ``on_frame_recv_callback()`` function is invoked when a frame is +fully received:: + + static int on_frame_recv_callback(nghttp2_session *session, + const nghttp2_frame *frame, void *user_data) { + http2_session_data *session_data = (http2_session_data *)user_data; + http2_stream_data *stream_data; + switch (frame->hd.type) { + case NGHTTP2_DATA: + case NGHTTP2_HEADERS: + /* Check that the client request has finished */ + if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) { + stream_data = + nghttp2_session_get_stream_user_data(session, frame->hd.stream_id); + /* For DATA and HEADERS frame, this callback may be called after + on_stream_close_callback. Check that stream still alive. */ + if (!stream_data) { + return 0; + } + return on_request_recv(session, session_data, stream_data); + } + break; + default: + break; + } + return 0; + } + +First we retrieve the ``http2_stream_data`` object associated with the stream +in ``on_begin_headers_callback()``. It is done using +`nghttp2_session_get_stream_user_data()`. If the requested path cannot be +served for some reason (e.g., file is not found), we send a 404 response, +which is done in ``error_reply()``. Otherwise, we open the requested file and +send its content. We send the header field ``:status`` as a single response +header. + +Sending the content of the file is done in ``send_response()`` function:: + + static int send_response(nghttp2_session *session, int32_t stream_id, + nghttp2_nv *nva, size_t nvlen, int fd) { + int rv; + nghttp2_data_provider data_prd; + data_prd.source.fd = fd; + data_prd.read_callback = file_read_callback; + + rv = nghttp2_submit_response(session, stream_id, nva, nvlen, &data_prd); + if (rv != 0) { + warnx("Fatal error: %s", nghttp2_strerror(rv)); + return -1; + } + return 0; + } + +The nghttp2 library uses the :type:`nghttp2_data_provider` structure to +send entity body to the remote peer. The ``source`` member of this +structure is a union and it can be either void pointer or int which is +intended to be used as file descriptor. In this example server, we use +the file descriptor. We also set the ``file_read_callback()`` callback +function to read the contents of the file:: + + static ssize_t file_read_callback(nghttp2_session *session _U_, + int32_t stream_id _U_, uint8_t *buf, + size_t length, uint32_t *data_flags, + nghttp2_data_source *source, + void *user_data _U_) { + int fd = source->fd; + ssize_t r; + while ((r = read(fd, buf, length)) == -1 && errno == EINTR) + ; + if (r == -1) { + return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; + } + if (r == 0) { + *data_flags |= NGHTTP2_DATA_FLAG_EOF; + } + return r; + } + +If an error happens while reading the file, we return +:macro:`NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE`. This tells the +library to send RST_STREAM to the stream. When all data has been read, set +the :macro:`NGHTTP2_DATA_FLAG_EOF` flag to ``*data_flags`` to tell the +nghttp2 library that we have finished reading the file. + +The `nghttp2_submit_response()` function is used to send the response to the +remote peer. + +The ``on_stream_close_callback()`` function is invoked when the stream +is about to close:: + + static int on_stream_close_callback(nghttp2_session *session, int32_t stream_id, + uint32_t error_code _U_, void *user_data) { + http2_session_data *session_data = (http2_session_data *)user_data; + http2_stream_data *stream_data; + + stream_data = nghttp2_session_get_stream_user_data(session, stream_id); + if (!stream_data) { + return 0; + } + remove_stream(session_data, stream_data); + delete_http2_stream_data(stream_data); + return 0; + } + +We destroy the ``http2_stream_data`` object in this function since the stream +is about to close and we no longer use that object. diff --git a/doc/tutorial-client.rst.in b/doc/tutorial-client.rst.in new file mode 100644 index 0000000..4f7fcfc --- /dev/null +++ b/doc/tutorial-client.rst.in @@ -0,0 +1,6 @@ +.. include:: @top_srcdir@/doc/sources/tutorial-client.rst + +libevent-client.c +----------------- + +.. literalinclude:: @top_srcdir@/examples/libevent-client.c diff --git a/doc/tutorial-hpack.rst.in b/doc/tutorial-hpack.rst.in new file mode 100644 index 0000000..832dedf --- /dev/null +++ b/doc/tutorial-hpack.rst.in @@ -0,0 +1,6 @@ +.. include:: @top_srcdir@/doc/sources/tutorial-hpack.rst + +deflate.c +--------- + +.. literalinclude:: @top_srcdir@/examples/deflate.c diff --git a/doc/tutorial-server.rst.in b/doc/tutorial-server.rst.in new file mode 100644 index 0000000..1972266 --- /dev/null +++ b/doc/tutorial-server.rst.in @@ -0,0 +1,6 @@ +.. include:: @top_srcdir@/doc/sources/tutorial-server.rst + +libevent-server.c +----------------- + +.. literalinclude:: @top_srcdir@/examples/libevent-server.c diff --git a/doc/types.rst b/doc/types.rst new file mode 100644 index 0000000..acb2ef1 --- /dev/null +++ b/doc/types.rst @@ -0,0 +1,914 @@ + +Types (structs, unions and typedefs) +==================================== +.. type:: nghttp2_session + + + The primary structure to hold the resources needed for a HTTP/2 + session. The details of this structure are intentionally hidden + from the public API. + + +.. type:: nghttp2_info + + + This struct is what `nghttp2_version()` returns. It holds + information about the particular nghttp2 version. + + .. member:: int age + + Age of this struct. This instance of nghttp2 sets it to + :macro:`NGHTTP2_VERSION_AGE` but a future version may bump it and + add more struct fields at the bottom + .. member:: int version_num + + the :macro:`NGHTTP2_VERSION_NUM` number (since age ==1) + .. member:: const char *version_str + + points to the :macro:`NGHTTP2_VERSION` string (since age ==1) + .. member:: const char *proto_str + + points to the :macro:`NGHTTP2_PROTO_VERSION_ID` string this + instance implements (since age ==1) + +.. type:: nghttp2_nv + + + The name/value pair, which mainly used to represent header fields. + + .. member:: uint8_t *name + + The *name* byte string. If this struct is presented from library + (e.g., :type:`nghttp2_on_frame_recv_callback`), *name* is + guaranteed to be NULL-terminated. When application is + constructing this struct, *name* is not required to be + NULL-terminated. + .. member:: uint8_t *value + + The *value* byte string. If this struct is presented from + library (e.g., :type:`nghttp2_on_frame_recv_callback`), *value* + is guaranteed to be NULL-terminated. When application is + constructing this struct, *value* is not required to be + NULL-terminated. + .. member:: size_t namelen + + The length of the *name*, excluding terminating NULL. + .. member:: size_t valuelen + + The length of the *value*, excluding terminating NULL. + .. member:: uint8_t flags + + Bitwise OR of one or more of :type:`nghttp2_nv_flag`. + +.. type:: nghttp2_frame_hd + + The frame header. + + .. member:: size_t length + + The length field of this frame, excluding frame header. + .. member:: int32_t stream_id + + The stream identifier (aka, stream ID) + .. member:: uint8_t type + + The type of this frame. See `nghttp2_frame_type`. + .. member:: uint8_t flags + + The flags. + .. member:: uint8_t reserved + + Reserved bit in frame header. Currently, this is always set to 0 + and application should not expect something useful in here. + +.. type:: nghttp2_data_source + + + This union represents the some kind of data source passed to + :type:`nghttp2_data_source_read_callback`. + + .. member:: int fd + + The integer field, suitable for a file descriptor. + .. member:: void *ptr + + The pointer to an arbitrary object. + +.. type:: typedef ssize_t (*nghttp2_data_source_read_callback)( nghttp2_session *session, int32_t stream_id, uint8_t *buf, size_t length, uint32_t *data_flags, nghttp2_data_source *source, void *user_data) + + + Callback function invoked when the library wants to read data from + the *source*. The read data is sent in the stream *stream_id*. + The implementation of this function must read at most *length* + bytes of data from *source* (or possibly other places) and store + them in *buf* and return number of data stored in *buf*. If EOF is + reached, set :macro:`NGHTTP2_DATA_FLAG_EOF` flag in *\*data_flags*. + + Sometime it is desirable to avoid copying data into *buf* and let + application to send data directly. To achieve this, set + :macro:`NGHTTP2_DATA_FLAG_NO_COPY` to *\*data_flags* (and possibly + other flags, just like when we do copy), and return the number of + bytes to send without copying data into *buf*. The library, seeing + :macro:`NGHTTP2_DATA_FLAG_NO_COPY`, will invoke + :type:`nghttp2_send_data_callback`. The application must send + complete DATA frame in that callback. + + If this callback is set by `nghttp2_submit_request()`, + `nghttp2_submit_response()` or `nghttp2_submit_headers()` and + `nghttp2_submit_data()` with flag parameter + :macro:`NGHTTP2_FLAG_END_STREAM` set, and + :macro:`NGHTTP2_DATA_FLAG_EOF` flag is set to *\*data_flags*, DATA + frame will have END_STREAM flag set. Usually, this is expected + behaviour and all are fine. One exception is send trailer header + fields. You cannot send trailers after sending frame with + END_STREAM set. To avoid this problem, one can set + :macro:`NGHTTP2_DATA_FLAG_NO_END_STREAM` along with + :macro:`NGHTTP2_DATA_FLAG_EOF` to signal the library not to set + END_STREAM in DATA frame. Then application can use + `nghttp2_submit_trailer()` to send trailers. + `nghttp2_submit_trailer()` can be called inside this callback. + + If the application wants to postpone DATA frames (e.g., + asynchronous I/O, or reading data blocks for long time), it is + achieved by returning :macro:`NGHTTP2_ERR_DEFERRED` without reading + any data in this invocation. The library removes DATA frame from + the outgoing queue temporarily. To move back deferred DATA frame + to outgoing queue, call `nghttp2_session_resume_data()`. In case + of error, there are 2 choices. Returning + :macro:`NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE` will close the stream + by issuing RST_STREAM with :macro:`NGHTTP2_INTERNAL_ERROR`. If a + different error code is desirable, use + `nghttp2_submit_rst_stream()` with a desired error code and then + return :macro:`NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE`. Returning + :macro:`NGHTTP2_ERR_CALLBACK_FAILURE` will signal the entire session + failure. +.. type:: nghttp2_data_provider + + + This struct represents the data source and the way to read a chunk + of data from it. + + .. member:: nghttp2_data_source source + + The data source. + .. member:: nghttp2_data_source_read_callback read_callback + + The callback function to read a chunk of data from the *source*. + +.. type:: nghttp2_data + + + The DATA frame. The received data is delivered via + :type:`nghttp2_on_data_chunk_recv_callback`. + + .. member:: size_t padlen + + The length of the padding in this frame. This includes PAD_HIGH + and PAD_LOW. + +.. type:: nghttp2_priority_spec + + + The structure to specify stream dependency. + + .. member:: int32_t stream_id + + The stream ID of the stream to depend on. Specifying 0 makes + stream not depend any other stream. + .. member:: int32_t weight + + The weight of this dependency. + .. member:: uint8_t exclusive + + nonzero means exclusive dependency + +.. type:: nghttp2_headers + + + The HEADERS frame. It has the following members: + + .. member:: nghttp2_frame_hd hd + + The frame header. + .. member:: size_t padlen + + The length of the padding in this frame. This includes PAD_HIGH + and PAD_LOW. + .. member:: nghttp2_priority_spec pri_spec + + The priority specification + .. member:: nghttp2_nv *nva + + The name/value pairs. + .. member:: size_t nvlen + + The number of name/value pairs in *nva*. + .. member:: nghttp2_headers_category cat + + The category of this HEADERS frame. + +.. type:: nghttp2_priority + + + The PRIORITY frame. It has the following members: + + .. member:: nghttp2_frame_hd hd + + The frame header. + .. member:: nghttp2_priority_spec pri_spec + + The priority specification. + +.. type:: nghttp2_rst_stream + + + The RST_STREAM frame. It has the following members: + + .. member:: nghttp2_frame_hd hd + + The frame header. + .. member:: uint32_t error_code + + The error code. See :type:`nghttp2_error_code`. + +.. type:: nghttp2_settings_entry + + + The SETTINGS ID/Value pair. It has the following members: + + .. member:: int32_t settings_id + + The SETTINGS ID. See :type:`nghttp2_settings_id`. + .. member:: uint32_t value + + The value of this entry. + +.. type:: nghttp2_settings + + + The SETTINGS frame. It has the following members: + + .. member:: nghttp2_frame_hd hd + + The frame header. + .. member:: size_t niv + + The number of SETTINGS ID/Value pairs in *iv*. + .. member:: nghttp2_settings_entry *iv + + The pointer to the array of SETTINGS ID/Value pair. + +.. type:: nghttp2_push_promise + + + The PUSH_PROMISE frame. It has the following members: + + .. member:: nghttp2_frame_hd hd + + The frame header. + .. member:: size_t padlen + + The length of the padding in this frame. This includes PAD_HIGH + and PAD_LOW. + .. member:: nghttp2_nv *nva + + The name/value pairs. + .. member:: size_t nvlen + + The number of name/value pairs in *nva*. + .. member:: int32_t promised_stream_id + + The promised stream ID + .. member:: uint8_t reserved + + Reserved bit. Currently this is always set to 0 and application + should not expect something useful in here. + +.. type:: nghttp2_ping + + + The PING frame. It has the following members: + + .. member:: nghttp2_frame_hd hd + + The frame header. + .. member:: uint8_t opaque_data[8] + + The opaque data + +.. type:: nghttp2_goaway + + + The GOAWAY frame. It has the following members: + + .. member:: nghttp2_frame_hd hd + + The frame header. + .. member:: int32_t last_stream_id + + The last stream stream ID. + .. member:: uint32_t error_code + + The error code. See :type:`nghttp2_error_code`. + .. member:: uint8_t *opaque_data + + The additional debug data + .. member:: size_t opaque_data_len + + The length of *opaque_data* member. + .. member:: uint8_t reserved + + Reserved bit. Currently this is always set to 0 and application + should not expect something useful in here. + +.. type:: nghttp2_window_update + + + The WINDOW_UPDATE frame. It has the following members: + + .. member:: nghttp2_frame_hd hd + + The frame header. + .. member:: int32_t window_size_increment + + The window size increment. + .. member:: uint8_t reserved + + Reserved bit. Currently this is always set to 0 and application + should not expect something useful in here. + +.. type:: nghttp2_extension + + + The extension frame. It has following members: + + .. member:: nghttp2_frame_hd hd + + The frame header. + .. member:: void *payload + + The pointer to extension payload. The exact pointer type is + determined by hd.type. + + Currently, no extension is supported. This is a place holder for + the future extensions. + +.. type:: nghttp2_frame + + + This union includes all frames to pass them to various function + calls as nghttp2_frame type. The CONTINUATION frame is omitted + from here because the library deals with it internally. + + .. member:: nghttp2_frame_hd hd + + The frame header, which is convenient to inspect frame header. + .. member:: nghttp2_data data + + The DATA frame. + .. member:: nghttp2_headers headers + + The HEADERS frame. + .. member:: nghttp2_priority priority + + The PRIORITY frame. + .. member:: nghttp2_rst_stream rst_stream + + The RST_STREAM frame. + .. member:: nghttp2_settings settings + + The SETTINGS frame. + .. member:: nghttp2_push_promise push_promise + + The PUSH_PROMISE frame. + .. member:: nghttp2_ping ping + + The PING frame. + .. member:: nghttp2_goaway goaway + + The GOAWAY frame. + .. member:: nghttp2_window_update window_update + + The WINDOW_UPDATE frame. + .. member:: nghttp2_extension ext + + The extension frame. + +.. type:: typedef ssize_t (*nghttp2_send_callback)(nghttp2_session *session, const uint8_t *data, size_t length, int flags, void *user_data) + + + Callback function invoked when *session* wants to send data to the + remote peer. The implementation of this function must send at most + *length* bytes of data stored in *data*. The *flags* is currently + not used and always 0. It must return the number of bytes sent if + it succeeds. If it cannot send any single byte without blocking, + it must return :macro:`NGHTTP2_ERR_WOULDBLOCK`. For other errors, + it must return :macro:`NGHTTP2_ERR_CALLBACK_FAILURE`. The + *user_data* pointer is the third argument passed in to the call to + `nghttp2_session_client_new()` or `nghttp2_session_server_new()`. + + This callback is required if the application uses + `nghttp2_session_send()` to send data to the remote endpoint. If + the application uses solely `nghttp2_session_mem_send()` instead, + this callback function is unnecessary. + + To set this callback to :type:`nghttp2_session_callbacks`, use + `nghttp2_session_callbacks_set_send_callback()`. +.. type:: typedef int (*nghttp2_send_data_callback)(nghttp2_session *session, nghttp2_frame *frame, const uint8_t *framehd, size_t length, nghttp2_data_source *source, void *user_data) + + + Callback function invoked when :macro:`NGHTTP2_DATA_FLAG_NO_COPY` is + used in :type:`nghttp2_data_source_read_callback` to send complete + DATA frame. + + The *frame* is a DATA frame to send. The *framehd* is the + serialized frame header (9 bytes). The *length* is the length of + application data to send (this does not include padding). The + *source* is the same pointer passed to + :type:`nghttp2_data_source_read_callback`. + + The application first must send frame header *framehd* of length 9 + bytes. If ``frame->padlen > 0``, send 1 byte of value + ``frame->padlen - 1``. Then send exactly *length* bytes of + application data. Finally, if ``frame->padlen > 0``, send + ``frame->padlen - 1`` bytes of zero (they are padding). + + The application has to send complete DATA frame in this callback. + If all data were written successfully, return 0. + + If it cannot send it all, just return + :macro:`NGHTTP2_ERR_WOULDBLOCK`; the library will call this callback + with the same parameters later (It is recommended to send complete + DATA frame at once in this function to deal with error; if partial + frame data has already sent, it is impossible to send another data + in that state, and all we can do is tear down connection). If + application decided to reset this stream, return + :macro:`NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE`, then the library + will send RST_STREAM with INTERNAL_ERROR as error code. The + application can also return :macro:`NGHTTP2_ERR_CALLBACK_FAILURE`, + which will result in connection closure. Returning any other value + is treated as :macro:`NGHTTP2_ERR_CALLBACK_FAILURE` is returned. +.. type:: typedef ssize_t (*nghttp2_recv_callback)(nghttp2_session *session, uint8_t *buf, size_t length, int flags, void *user_data) + + + Callback function invoked when *session* wants to receive data from + the remote peer. The implementation of this function must read at + most *length* bytes of data and store it in *buf*. The *flags* is + currently not used and always 0. It must return the number of + bytes written in *buf* if it succeeds. If it cannot read any + single byte without blocking, it must return + :macro:`NGHTTP2_ERR_WOULDBLOCK`. If it gets EOF before it reads any + single byte, it must return :macro:`NGHTTP2_ERR_EOF`. For other + errors, it must return :macro:`NGHTTP2_ERR_CALLBACK_FAILURE`. + Returning 0 is treated as :macro:`NGHTTP2_ERR_WOULDBLOCK`. The + *user_data* pointer is the third argument passed in to the call to + `nghttp2_session_client_new()` or `nghttp2_session_server_new()`. + + This callback is required if the application uses + `nghttp2_session_recv()` to receive data from the remote endpoint. + If the application uses solely `nghttp2_session_mem_recv()` + instead, this callback function is unnecessary. + + To set this callback to :type:`nghttp2_session_callbacks`, use + `nghttp2_session_callbacks_set_recv_callback()`. +.. type:: typedef int (*nghttp2_on_frame_recv_callback)(nghttp2_session *session, const nghttp2_frame *frame, void *user_data) + + + Callback function invoked by `nghttp2_session_recv()` and + `nghttp2_session_mem_recv()` when a frame is received. The + *user_data* pointer is the third argument passed in to the call to + `nghttp2_session_client_new()` or `nghttp2_session_server_new()`. + + If frame is HEADERS or PUSH_PROMISE, the ``nva`` and ``nvlen`` + member of their data structure are always ``NULL`` and 0 + respectively. The header name/value pairs are emitted via + :type:`nghttp2_on_header_callback`. + + For HEADERS, PUSH_PROMISE and DATA frames, this callback may be + called after stream is closed (see + :type:`nghttp2_on_stream_close_callback`). The application should + check that stream is still alive using its own stream management or + :func:`nghttp2_session_get_stream_user_data()`. + + Only HEADERS and DATA frame can signal the end of incoming data. + If ``frame->hd.flags & NGHTTP2_FLAG_END_STREAM`` is nonzero, the + *frame* is the last frame from the remote peer in this stream. + + This callback won't be called for CONTINUATION frames. + HEADERS/PUSH_PROMISE + CONTINUATIONs are treated as single frame. + + The implementation of this function must return 0 if it succeeds. + If nonzero value is returned, it is treated as fatal error and + `nghttp2_session_recv()` and `nghttp2_session_mem_recv()` functions + immediately return :macro:`NGHTTP2_ERR_CALLBACK_FAILURE`. + + To set this callback to :type:`nghttp2_session_callbacks`, use + `nghttp2_session_callbacks_set_on_frame_recv_callback()`. +.. type:: typedef int (*nghttp2_on_invalid_frame_recv_callback)( nghttp2_session *session, const nghttp2_frame *frame, int lib_error_code, void *user_data) + + + Callback function invoked by `nghttp2_session_recv()` and + `nghttp2_session_mem_recv()` when an invalid non-DATA frame is + received. The error is indicated by the *lib_error_code*, which is + one of the values defined in :type:`nghttp2_error`. When this + callback function is invoked, the library automatically submits + either RST_STREAM or GOAWAY frame. The *user_data* pointer is the + third argument passed in to the call to + `nghttp2_session_client_new()` or `nghttp2_session_server_new()`. + + If frame is HEADERS or PUSH_PROMISE, the ``nva`` and ``nvlen`` + member of their data structure are always ``NULL`` and 0 + respectively. + + The implementation of this function must return 0 if it succeeds. + If nonzero is returned, it is treated as fatal error and + `nghttp2_session_recv()` and `nghttp2_session_mem_recv()` functions + immediately return :macro:`NGHTTP2_ERR_CALLBACK_FAILURE`. + + To set this callback to :type:`nghttp2_session_callbacks`, use + `nghttp2_session_callbacks_set_on_invalid_frame_recv_callback()`. +.. type:: typedef int (*nghttp2_on_data_chunk_recv_callback)(nghttp2_session *session, uint8_t flags, int32_t stream_id, const uint8_t *data, size_t len, void *user_data) + + + Callback function invoked when a chunk of data in DATA frame is + received. The *stream_id* is the stream ID this DATA frame belongs + to. The *flags* is the flags of DATA frame which this data chunk + is contained. ``(flags & NGHTTP2_FLAG_END_STREAM) != 0`` does not + necessarily mean this chunk of data is the last one in the stream. + You should use :type:`nghttp2_on_frame_recv_callback` to know all + data frames are received. The *user_data* pointer is the third + argument passed in to the call to `nghttp2_session_client_new()` or + `nghttp2_session_server_new()`. + + If the application uses `nghttp2_session_mem_recv()`, it can return + :macro:`NGHTTP2_ERR_PAUSE` to make `nghttp2_session_mem_recv()` + return without processing further input bytes. The memory by + pointed by the *data* is retained until + `nghttp2_session_mem_recv()` or `nghttp2_session_recv()` is called. + The application must retain the input bytes which was used to + produce the *data* parameter, because it may refer to the memory + region included in the input bytes. + + The implementation of this function must return 0 if it succeeds. + If nonzero is returned, it is treated as fatal error, and + `nghttp2_session_recv()` and `nghttp2_session_mem_recv()` functions + immediately return :macro:`NGHTTP2_ERR_CALLBACK_FAILURE`. + + To set this callback to :type:`nghttp2_session_callbacks`, use + `nghttp2_session_callbacks_set_on_data_chunk_recv_callback()`. +.. type:: typedef int (*nghttp2_before_frame_send_callback)(nghttp2_session *session, const nghttp2_frame *frame, void *user_data) + + + Callback function invoked just before the non-DATA frame *frame* is + sent. The *user_data* pointer is the third argument passed in to + the call to `nghttp2_session_client_new()` or + `nghttp2_session_server_new()`. + + The implementation of this function must return 0 if it succeeds. + If nonzero is returned, it is treated as fatal error and + `nghttp2_session_send()` and `nghttp2_session_mem_send()` functions + immediately return :macro:`NGHTTP2_ERR_CALLBACK_FAILURE`. + + To set this callback to :type:`nghttp2_session_callbacks`, use + `nghttp2_session_callbacks_set_before_frame_send_callback()`. +.. type:: typedef int (*nghttp2_on_frame_send_callback)(nghttp2_session *session, const nghttp2_frame *frame, void *user_data) + + + Callback function invoked after the frame *frame* is sent. The + *user_data* pointer is the third argument passed in to the call to + `nghttp2_session_client_new()` or `nghttp2_session_server_new()`. + + The implementation of this function must return 0 if it succeeds. + If nonzero is returned, it is treated as fatal error and + `nghttp2_session_send()` and `nghttp2_session_mem_send()` functions + immediately return :macro:`NGHTTP2_ERR_CALLBACK_FAILURE`. + + To set this callback to :type:`nghttp2_session_callbacks`, use + `nghttp2_session_callbacks_set_on_frame_send_callback()`. +.. type:: typedef int (*nghttp2_on_frame_not_send_callback)(nghttp2_session *session, const nghttp2_frame *frame, int lib_error_code, void *user_data) + + + Callback function invoked after the non-DATA frame *frame* is not + sent because of the error. The error is indicated by the + *lib_error_code*, which is one of the values defined in + :type:`nghttp2_error`. The *user_data* pointer is the third + argument passed in to the call to `nghttp2_session_client_new()` or + `nghttp2_session_server_new()`. + + The implementation of this function must return 0 if it succeeds. + If nonzero is returned, it is treated as fatal error and + `nghttp2_session_send()` and `nghttp2_session_mem_send()` functions + immediately return :macro:`NGHTTP2_ERR_CALLBACK_FAILURE`. + + `nghttp2_session_get_stream_user_data()` can be used to get + associated data. + + To set this callback to :type:`nghttp2_session_callbacks`, use + `nghttp2_session_callbacks_set_on_frame_not_send_callback()`. +.. type:: typedef int (*nghttp2_on_stream_close_callback)(nghttp2_session *session, int32_t stream_id, uint32_t error_code, void *user_data) + + + Callback function invoked when the stream *stream_id* is closed. + The reason of closure is indicated by the *error_code*. The + *error_code* is usually one of :macro:`nghttp2_error_code`, but that + is not guaranteed. The stream_user_data, which was specified in + `nghttp2_submit_request()` or `nghttp2_submit_headers()`, is still + available in this function. The *user_data* pointer is the third + argument passed in to the call to `nghttp2_session_client_new()` or + `nghttp2_session_server_new()`. + + This function is also called for a stream in reserved state. + + The implementation of this function must return 0 if it succeeds. + If nonzero is returned, it is treated as fatal error and + `nghttp2_session_recv()`, `nghttp2_session_mem_recv()`, + `nghttp2_session_send()`, and `nghttp2_session_mem_send()` + functions immediately return :macro:`NGHTTP2_ERR_CALLBACK_FAILURE`. + + To set this callback to :type:`nghttp2_session_callbacks`, use + `nghttp2_session_callbacks_set_on_stream_close_callback()`. +.. type:: typedef int (*nghttp2_on_begin_headers_callback)(nghttp2_session *session, const nghttp2_frame *frame, void *user_data) + + + Callback function invoked when the reception of header block in + HEADERS or PUSH_PROMISE is started. Each header name/value pair + will be emitted by :type:`nghttp2_on_header_callback`. + + The ``frame->hd.flags`` may not have + :macro:`NGHTTP2_FLAG_END_HEADERS` flag set, which indicates that one + or more CONTINUATION frames are involved. But the application does + not need to care about that because the header name/value pairs are + emitted transparently regardless of CONTINUATION frames. + + The server applications probably create an object to store + information about new stream if ``frame->hd.type == + NGHTTP2_HEADERS`` and ``frame->headers.cat == + NGHTTP2_HCAT_REQUEST``. If *session* is configured as server side, + ``frame->headers.cat`` is either ``NGHTTP2_HCAT_REQUEST`` + containing request headers or ``NGHTTP2_HCAT_HEADERS`` containing + trailer headers and never get PUSH_PROMISE in this callback. + + For the client applications, ``frame->hd.type`` is either + ``NGHTTP2_HEADERS`` or ``NGHTTP2_PUSH_PROMISE``. In case of + ``NGHTTP2_HEADERS``, ``frame->headers.cat == + NGHTTP2_HCAT_RESPONSE`` means that it is the first response + headers, but it may be non-final response which is indicated by 1xx + status code. In this case, there may be zero or more HEADERS frame + with ``frame->headers.cat == NGHTTP2_HCAT_HEADERS`` which has + non-final response code and finally client gets exactly one HEADERS + frame with ``frame->headers.cat == NGHTTP2_HCAT_HEADERS`` + containing final response headers (non-1xx status code). The + trailer headers also has ``frame->headers.cat == + NGHTTP2_HCAT_HEADERS`` which does not contain any status code. + + Returning :macro:`NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE` will close + the stream (promised stream if frame is PUSH_PROMISE) by issuing + RST_STREAM with :macro:`NGHTTP2_INTERNAL_ERROR`. In this case, + :type:`nghttp2_on_header_callback` and + :type:`nghttp2_on_frame_recv_callback` will not be invoked. If a + different error code is desirable, use + `nghttp2_submit_rst_stream()` with a desired error code and then + return :macro:`NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE`. Again, use + ``frame->push_promise.promised_stream_id`` as stream_id parameter + in `nghttp2_submit_rst_stream()` if frame is PUSH_PROMISE. + + The implementation of this function must return 0 if it succeeds. + It can return :macro:`NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE` to + reset the stream (promised stream if frame is PUSH_PROMISE). For + critical errors, it must return + :macro:`NGHTTP2_ERR_CALLBACK_FAILURE`. If the other value is + returned, it is treated as if :macro:`NGHTTP2_ERR_CALLBACK_FAILURE` + is returned. If :macro:`NGHTTP2_ERR_CALLBACK_FAILURE` is returned, + `nghttp2_session_mem_recv()` function will immediately return + :macro:`NGHTTP2_ERR_CALLBACK_FAILURE`. + + To set this callback to :type:`nghttp2_session_callbacks`, use + `nghttp2_session_callbacks_set_on_begin_headers_callback()`. +.. type:: typedef int (*nghttp2_on_header_callback)(nghttp2_session *session, const nghttp2_frame *frame, const uint8_t *name, size_t namelen, const uint8_t *value, size_t valuelen, uint8_t flags, void *user_data) + + + Callback function invoked when a header name/value pair is received + for the *frame*. The *name* of length *namelen* is header name. + The *value* of length *valuelen* is header value. The *flags* is + bitwise OR of one or more of :type:`nghttp2_nv_flag`. + + If :macro:`NGHTTP2_NV_FLAG_NO_INDEX` is set in *flags*, the receiver + must not index this name/value pair when forwarding it to the next + hop. More specifically, "Literal Header Field never Indexed" + representation must be used in HPACK encoding. + + When this callback is invoked, ``frame->hd.type`` is either + :macro:`NGHTTP2_HEADERS` or :macro:`NGHTTP2_PUSH_PROMISE`. After all + header name/value pairs are processed with this callback, and no + error has been detected, :type:`nghttp2_on_frame_recv_callback` + will be invoked. If there is an error in decompression, + :type:`nghttp2_on_frame_recv_callback` for the *frame* will not be + invoked. + + Both *name* and *value* are guaranteed to be NULL-terminated. The + *namelen* and *valuelen* do not include terminal NULL. If + `nghttp2_option_set_no_http_messaging()` is used with nonzero + value, NULL character may be included in *name* or *value* before + terminating NULL. + + Please note that unless `nghttp2_option_set_no_http_messaging()` is + used, nghttp2 library does perform validation against the *name* + and the *value* using `nghttp2_check_header_name()` and + `nghttp2_check_header_value()`. In addition to this, nghttp2 + performs vaidation based on HTTP Messaging rule, which is briefly + explained in :ref:`http-messaging` section. + + If the application uses `nghttp2_session_mem_recv()`, it can return + :macro:`NGHTTP2_ERR_PAUSE` to make `nghttp2_session_mem_recv()` + return without processing further input bytes. The memory pointed + by *frame*, *name* and *value* parameters are retained until + `nghttp2_session_mem_recv()` or `nghttp2_session_recv()` is called. + The application must retain the input bytes which was used to + produce these parameters, because it may refer to the memory region + included in the input bytes. + + Returning :macro:`NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE` will close + the stream (promised stream if frame is PUSH_PROMISE) by issuing + RST_STREAM with :macro:`NGHTTP2_INTERNAL_ERROR`. In this case, + :type:`nghttp2_on_header_callback` and + :type:`nghttp2_on_frame_recv_callback` will not be invoked. If a + different error code is desirable, use + `nghttp2_submit_rst_stream()` with a desired error code and then + return :macro:`NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE`. Again, use + ``frame->push_promise.promised_stream_id`` as stream_id parameter + in `nghttp2_submit_rst_stream()` if frame is PUSH_PROMISE. + + The implementation of this function must return 0 if it succeeds. + It may return :macro:`NGHTTP2_ERR_PAUSE` or + :macro:`NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE`. For other critical + failures, it must return :macro:`NGHTTP2_ERR_CALLBACK_FAILURE`. If + the other nonzero value is returned, it is treated as + :macro:`NGHTTP2_ERR_CALLBACK_FAILURE`. If + :macro:`NGHTTP2_ERR_CALLBACK_FAILURE` is returned, + `nghttp2_session_recv()` and `nghttp2_session_mem_recv()` functions + immediately return :macro:`NGHTTP2_ERR_CALLBACK_FAILURE`. + + To set this callback to :type:`nghttp2_session_callbacks`, use + `nghttp2_session_callbacks_set_on_header_callback()`. +.. type:: typedef ssize_t (*nghttp2_select_padding_callback)(nghttp2_session *session, const nghttp2_frame *frame, size_t max_payloadlen, void *user_data) + + + Callback function invoked when the library asks application how + many padding bytes are required for the transmission of the + *frame*. The application must choose the total length of payload + including padded bytes in range [frame->hd.length, max_payloadlen], + inclusive. Choosing number not in this range will be treated as + :macro:`NGHTTP2_ERR_CALLBACK_FAILURE`. Returning + ``frame->hd.length`` means no padding is added. Returning + :macro:`NGHTTP2_ERR_CALLBACK_FAILURE` will make + `nghttp2_session_send()` and `nghttp2_session_mem_send()` functions + immediately return :macro:`NGHTTP2_ERR_CALLBACK_FAILURE`. + + To set this callback to :type:`nghttp2_session_callbacks`, use + `nghttp2_session_callbacks_set_select_padding_callback()`. +.. type:: typedef ssize_t (*nghttp2_data_source_read_length_callback)( nghttp2_session *session, uint8_t frame_type, int32_t stream_id, int32_t session_remote_window_size, int32_t stream_remote_window_size, uint32_t remote_max_frame_size, void *user_data) + + + Callback function invoked when library wants to get max length of + data to send data to the remote peer. The implementation of this + function should return a value in the following range. [1, + min(*session_remote_window_size*, *stream_remote_window_size*, + *remote_max_frame_size*)]. If a value greater than this range is + returned than the max allow value will be used. Returning a value + smaller than this range is treated as + :macro:`NGHTTP2_ERR_CALLBACK_FAILURE`. The *frame_type* is provided + for future extensibility and identifies the type of frame (see + :type:`nghttp2_frame_type`) for which to get the length for. + Currently supported frame types are: :macro:`NGHTTP2_DATA`. + + This callback can be used to control the length in bytes for which + :type:`nghttp2_data_source_read_callback` is allowed to send to the + remote endpoint. This callback is optional. Returning + :macro:`NGHTTP2_ERR_CALLBACK_FAILURE` will signal the entire session + failure. + + To set this callback to :type:`nghttp2_session_callbacks`, use + `nghttp2_session_callbacks_set_data_source_read_length_callback()`. +.. type:: typedef int (*nghttp2_on_begin_frame_callback)(nghttp2_session *session, const nghttp2_frame_hd *hd, void *user_data) + + + Callback function invoked when a frame header is received. The + *hd* points to received frame header. + + Unlike :type:`nghttp2_on_frame_recv_callback`, this callback will + also be called when frame header of CONTINUATION frame is received. + + If both :type:`nghttp2_on_begin_frame_callback` and + :type:`nghttp2_on_begin_headers_callback` are set and HEADERS or + PUSH_PROMISE is received, :type:`nghttp2_on_begin_frame_callback` + will be called first. + + The implementation of this function must return 0 if it succeeds. + If nonzero value is returned, it is treated as fatal error and + `nghttp2_session_recv()` and `nghttp2_session_mem_recv()` functions + immediately return :macro:`NGHTTP2_ERR_CALLBACK_FAILURE`. + + To set this callback to :type:`nghttp2_session_callbacks`, use + `nghttp2_session_callbacks_set_on_begin_frame_callback()`. +.. type:: nghttp2_session_callbacks + + + Callback functions for :type:`nghttp2_session`. The details of + this structure are intentionally hidden from the public API. + + +.. type:: typedef void *(*nghttp2_malloc)(size_t size, void *mem_user_data) + + + Custom memory allocator to replace malloc(). The *mem_user_data* + is the mem_user_data member of :type:`nghttp2_mem` structure. +.. type:: typedef void (*nghttp2_free)(void *ptr, void *mem_user_data) + + + Custom memory allocator to replace free(). The *mem_user_data* is + the mem_user_data member of :type:`nghttp2_mem` structure. +.. type:: typedef void *(*nghttp2_calloc)(size_t nmemb, size_t size, void *mem_user_data) + + + Custom memory allocator to replace calloc(). The *mem_user_data* + is the mem_user_data member of :type:`nghttp2_mem` structure. +.. type:: typedef void *(*nghttp2_realloc)(void *ptr, size_t size, void *mem_user_data) + + + Custom memory allocator to replace realloc(). The *mem_user_data* + is the mem_user_data member of :type:`nghttp2_mem` structure. +.. type:: nghttp2_mem + + + Custom memory allocator functions and user defined pointer. The + *mem_user_data* member is passed to each allocator function. This + can be used, for example, to achieve per-session memory pool. + + In the following example code, ``my_malloc``, ``my_free``, + ``my_calloc`` and ``my_realloc`` are the replacement of the + standard allocators ``malloc``, ``free``, ``calloc`` and + ``realloc`` respectively:: + + void *my_malloc_cb(size_t size, void *mem_user_data) { + return my_malloc(size); + } + + void my_free_cb(void *ptr, void *mem_user_data) { my_free(ptr); } + + void *my_calloc_cb(size_t nmemb, size_t size, void *mem_user_data) { + return my_calloc(nmemb, size); + } + + void *my_realloc_cb(void *ptr, size_t size, void *mem_user_data) { + return my_realloc(ptr, size); + } + + void session_new() { + nghttp2_session *session; + nghttp2_session_callbacks *callbacks; + nghttp2_mem mem = {NULL, my_malloc_cb, my_free_cb, my_calloc_cb, + my_realloc_cb}; + + ... + + nghttp2_session_client_new3(&session, callbacks, NULL, NULL, &mem); + + ... + } + + .. member:: void *mem_user_data + + An arbitrary user supplied data. This is passed to each + allocator function. + .. member:: nghttp2_malloc malloc + + Custom allocator function to replace malloc(). + .. member:: nghttp2_free free + + Custom allocator function to replace free(). + .. member:: nghttp2_calloc calloc + + Custom allocator function to replace calloc(). + .. member:: nghttp2_realloc realloc + + Custom allocator function to replace realloc(). + +.. type:: nghttp2_option + + + Configuration options for :type:`nghttp2_session`. The details of + this structure are intentionally hidden from the public API. + + +.. type:: nghttp2_hd_deflater + + + HPACK deflater object. + + +.. type:: nghttp2_hd_inflater + + + HPACK inflater object. + + diff --git a/examples/Makefile.am b/examples/Makefile.am new file mode 100644 index 0000000..5bcf2d1 --- /dev/null +++ b/examples/Makefile.am @@ -0,0 +1,95 @@ +# nghttp2 - HTTP/2 C Library + +# Copyright (c) 2012 Tatsuhiro Tsujikawa + +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: + +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. + +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +if ENABLE_EXAMPLES + +AM_CFLAGS = $(WARNCFLAGS) +AM_CPPFLAGS = \ + -Wall \ + -I$(top_srcdir)/lib/includes \ + -I$(top_builddir)/lib/includes \ + -I$(top_srcdir)/src/includes \ + -I$(top_srcdir)/third-party \ + @LIBEVENT_OPENSSL_CFLAGS@ \ + @OPENSSL_CFLAGS@ \ + @DEFS@ +LDADD = $(top_builddir)/lib/libnghttp2.la \ + $(top_builddir)/third-party/libhttp-parser.la \ + @LIBEVENT_OPENSSL_LIBS@ \ + @OPENSSL_LIBS@ + +noinst_PROGRAMS = client libevent-client libevent-server deflate + +client_SOURCES = client.c + +libevent_client_SOURCES = libevent-client.c + +libevent_server_SOURCES = libevent-server.c + +deflate_SOURCES = deflate.c + +if ENABLE_TINY_NGHTTPD + +noinst_PROGRAMS += tiny-nghttpd + +tiny_nghttpd_SOURCES = tiny-nghttpd.c + +endif # ENABLE_TINY_NGHTTPD + +if ENABLE_ASIO_LIB + +noinst_PROGRAMS += asio-sv asio-sv2 asio-cl asio-cl2 + +# AM_CPPFLAGS must be placed first, so that header file (e.g., +# nghttp2/nghttp2.h) in this package is used rather than installed +# one. +ASIOCPPFLAGS = ${AM_CPPFLAGS} ${BOOST_CPPFLAGS} +ASIOLDADD = $(top_builddir)/lib/libnghttp2.la \ + $(top_builddir)/src/libnghttp2_asio.la @JEMALLOC_LIBS@ \ + $(top_builddir)/third-party/libhttp-parser.la \ + ${BOOST_LDFLAGS} \ + ${BOOST_ASIO_LIB} \ + ${BOOST_THREAD_LIB} \ + ${BOOST_SYSTEM_LIB} \ + @OPENSSL_LIBS@ \ + @APPLDFLAGS@ + +asio_sv_SOURCES = asio-sv.cc +asio_sv_CPPFLAGS = ${ASIOCPPFLAGS} +asio_sv_LDADD = ${ASIOLDADD} + +asio_sv2_SOURCES = asio-sv2.cc +asio_sv2_CPPFLAGS = ${ASIOCPPFLAGS} +asio_sv2_LDADD = ${ASIOLDADD} + +asio_cl_SOURCES = asio-cl.cc +asio_cl_CPPFLAGS = ${ASIOCPPFLAGS} +asio_cl_LDADD = ${ASIOLDADD} + +asio_cl2_SOURCES = asio-cl2.cc +asio_cl2_CPPFLAGS = ${ASIOCPPFLAGS} +asio_cl2_LDADD = ${ASIOLDADD} + +endif # ENABLE_ASIO_LIB + +endif # ENABLE_EXAMPLES diff --git a/examples/Makefile.in b/examples/Makefile.in new file mode 100644 index 0000000..9b55d0a --- /dev/null +++ b/examples/Makefile.in @@ -0,0 +1,926 @@ +# Makefile.in generated by automake 1.14.1 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2013 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +# nghttp2 - HTTP/2 C Library + +# Copyright (c) 2012 Tatsuhiro Tsujikawa + +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: + +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. + +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +VPATH = @srcdir@ +am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)' +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +target_triplet = @target@ +@ENABLE_EXAMPLES_TRUE@noinst_PROGRAMS = client$(EXEEXT) \ +@ENABLE_EXAMPLES_TRUE@ libevent-client$(EXEEXT) \ +@ENABLE_EXAMPLES_TRUE@ libevent-server$(EXEEXT) \ +@ENABLE_EXAMPLES_TRUE@ deflate$(EXEEXT) $(am__EXEEXT_1) \ +@ENABLE_EXAMPLES_TRUE@ $(am__EXEEXT_2) +@ENABLE_EXAMPLES_TRUE@@ENABLE_TINY_NGHTTPD_TRUE@am__append_1 = tiny-nghttpd +@ENABLE_ASIO_LIB_TRUE@@ENABLE_EXAMPLES_TRUE@am__append_2 = asio-sv asio-sv2 asio-cl asio-cl2 +subdir = examples +DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \ + $(top_srcdir)/depcomp +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/ax_boost_asio.m4 \ + $(top_srcdir)/m4/ax_boost_base.m4 \ + $(top_srcdir)/m4/ax_boost_system.m4 \ + $(top_srcdir)/m4/ax_boost_thread.m4 \ + $(top_srcdir)/m4/ax_check_compile_flag.m4 \ + $(top_srcdir)/m4/ax_cxx_compile_stdcxx_11.m4 \ + $(top_srcdir)/m4/ax_have_epoll.m4 \ + $(top_srcdir)/m4/ax_python_devel.m4 \ + $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/libxml2.m4 \ + $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ + $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +@ENABLE_EXAMPLES_TRUE@@ENABLE_TINY_NGHTTPD_TRUE@am__EXEEXT_1 = tiny-nghttpd$(EXEEXT) +@ENABLE_ASIO_LIB_TRUE@@ENABLE_EXAMPLES_TRUE@am__EXEEXT_2 = \ +@ENABLE_ASIO_LIB_TRUE@@ENABLE_EXAMPLES_TRUE@ asio-sv$(EXEEXT) \ +@ENABLE_ASIO_LIB_TRUE@@ENABLE_EXAMPLES_TRUE@ asio-sv2$(EXEEXT) \ +@ENABLE_ASIO_LIB_TRUE@@ENABLE_EXAMPLES_TRUE@ asio-cl$(EXEEXT) \ +@ENABLE_ASIO_LIB_TRUE@@ENABLE_EXAMPLES_TRUE@ asio-cl2$(EXEEXT) +PROGRAMS = $(noinst_PROGRAMS) +am__asio_cl_SOURCES_DIST = asio-cl.cc +@ENABLE_ASIO_LIB_TRUE@@ENABLE_EXAMPLES_TRUE@am_asio_cl_OBJECTS = asio_cl-asio-cl.$(OBJEXT) +asio_cl_OBJECTS = $(am_asio_cl_OBJECTS) +am__DEPENDENCIES_1 = +@ENABLE_ASIO_LIB_TRUE@@ENABLE_EXAMPLES_TRUE@am__DEPENDENCIES_2 = $(top_builddir)/lib/libnghttp2.la \ +@ENABLE_ASIO_LIB_TRUE@@ENABLE_EXAMPLES_TRUE@ $(top_builddir)/src/libnghttp2_asio.la \ +@ENABLE_ASIO_LIB_TRUE@@ENABLE_EXAMPLES_TRUE@ $(top_builddir)/third-party/libhttp-parser.la \ +@ENABLE_ASIO_LIB_TRUE@@ENABLE_EXAMPLES_TRUE@ $(am__DEPENDENCIES_1) \ +@ENABLE_ASIO_LIB_TRUE@@ENABLE_EXAMPLES_TRUE@ $(am__DEPENDENCIES_1) \ +@ENABLE_ASIO_LIB_TRUE@@ENABLE_EXAMPLES_TRUE@ $(am__DEPENDENCIES_1) \ +@ENABLE_ASIO_LIB_TRUE@@ENABLE_EXAMPLES_TRUE@ $(am__DEPENDENCIES_1) +@ENABLE_ASIO_LIB_TRUE@@ENABLE_EXAMPLES_TRUE@asio_cl_DEPENDENCIES = $(am__DEPENDENCIES_2) +AM_V_lt = $(am__v_lt_@AM_V@) +am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) +am__v_lt_0 = --silent +am__v_lt_1 = +am__asio_cl2_SOURCES_DIST = asio-cl2.cc +@ENABLE_ASIO_LIB_TRUE@@ENABLE_EXAMPLES_TRUE@am_asio_cl2_OBJECTS = asio_cl2-asio-cl2.$(OBJEXT) +asio_cl2_OBJECTS = $(am_asio_cl2_OBJECTS) +@ENABLE_ASIO_LIB_TRUE@@ENABLE_EXAMPLES_TRUE@asio_cl2_DEPENDENCIES = $(am__DEPENDENCIES_2) +am__asio_sv_SOURCES_DIST = asio-sv.cc +@ENABLE_ASIO_LIB_TRUE@@ENABLE_EXAMPLES_TRUE@am_asio_sv_OBJECTS = asio_sv-asio-sv.$(OBJEXT) +asio_sv_OBJECTS = $(am_asio_sv_OBJECTS) +@ENABLE_ASIO_LIB_TRUE@@ENABLE_EXAMPLES_TRUE@asio_sv_DEPENDENCIES = $(am__DEPENDENCIES_2) +am__asio_sv2_SOURCES_DIST = asio-sv2.cc +@ENABLE_ASIO_LIB_TRUE@@ENABLE_EXAMPLES_TRUE@am_asio_sv2_OBJECTS = asio_sv2-asio-sv2.$(OBJEXT) +asio_sv2_OBJECTS = $(am_asio_sv2_OBJECTS) +@ENABLE_ASIO_LIB_TRUE@@ENABLE_EXAMPLES_TRUE@asio_sv2_DEPENDENCIES = $(am__DEPENDENCIES_2) +am__client_SOURCES_DIST = client.c +@ENABLE_EXAMPLES_TRUE@am_client_OBJECTS = client.$(OBJEXT) +client_OBJECTS = $(am_client_OBJECTS) +client_LDADD = $(LDADD) +@ENABLE_EXAMPLES_TRUE@client_DEPENDENCIES = \ +@ENABLE_EXAMPLES_TRUE@ $(top_builddir)/lib/libnghttp2.la \ +@ENABLE_EXAMPLES_TRUE@ $(top_builddir)/third-party/libhttp-parser.la +am__deflate_SOURCES_DIST = deflate.c +@ENABLE_EXAMPLES_TRUE@am_deflate_OBJECTS = deflate.$(OBJEXT) +deflate_OBJECTS = $(am_deflate_OBJECTS) +deflate_LDADD = $(LDADD) +@ENABLE_EXAMPLES_TRUE@deflate_DEPENDENCIES = \ +@ENABLE_EXAMPLES_TRUE@ $(top_builddir)/lib/libnghttp2.la \ +@ENABLE_EXAMPLES_TRUE@ $(top_builddir)/third-party/libhttp-parser.la +am__libevent_client_SOURCES_DIST = libevent-client.c +@ENABLE_EXAMPLES_TRUE@am_libevent_client_OBJECTS = \ +@ENABLE_EXAMPLES_TRUE@ libevent-client.$(OBJEXT) +libevent_client_OBJECTS = $(am_libevent_client_OBJECTS) +libevent_client_LDADD = $(LDADD) +@ENABLE_EXAMPLES_TRUE@libevent_client_DEPENDENCIES = \ +@ENABLE_EXAMPLES_TRUE@ $(top_builddir)/lib/libnghttp2.la \ +@ENABLE_EXAMPLES_TRUE@ $(top_builddir)/third-party/libhttp-parser.la +am__libevent_server_SOURCES_DIST = libevent-server.c +@ENABLE_EXAMPLES_TRUE@am_libevent_server_OBJECTS = \ +@ENABLE_EXAMPLES_TRUE@ libevent-server.$(OBJEXT) +libevent_server_OBJECTS = $(am_libevent_server_OBJECTS) +libevent_server_LDADD = $(LDADD) +@ENABLE_EXAMPLES_TRUE@libevent_server_DEPENDENCIES = \ +@ENABLE_EXAMPLES_TRUE@ $(top_builddir)/lib/libnghttp2.la \ +@ENABLE_EXAMPLES_TRUE@ $(top_builddir)/third-party/libhttp-parser.la +am__tiny_nghttpd_SOURCES_DIST = tiny-nghttpd.c +@ENABLE_EXAMPLES_TRUE@@ENABLE_TINY_NGHTTPD_TRUE@am_tiny_nghttpd_OBJECTS = tiny-nghttpd.$(OBJEXT) +tiny_nghttpd_OBJECTS = $(am_tiny_nghttpd_OBJECTS) +tiny_nghttpd_LDADD = $(LDADD) +@ENABLE_EXAMPLES_TRUE@tiny_nghttpd_DEPENDENCIES = \ +@ENABLE_EXAMPLES_TRUE@ $(top_builddir)/lib/libnghttp2.la \ +@ENABLE_EXAMPLES_TRUE@ $(top_builddir)/third-party/libhttp-parser.la +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__depfiles_maybe = depfiles +am__mv = mv -f +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CFLAGS) $(CFLAGS) +AM_V_CC = $(am__v_CC_@AM_V@) +am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) +am__v_CC_0 = @echo " CC " $@; +am__v_CC_1 = +CCLD = $(CC) +LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CCLD = $(am__v_CCLD_@AM_V@) +am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) +am__v_CCLD_0 = @echo " CCLD " $@; +am__v_CCLD_1 = +CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) +LTCXXCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CXXFLAGS) $(CXXFLAGS) +AM_V_CXX = $(am__v_CXX_@AM_V@) +am__v_CXX_ = $(am__v_CXX_@AM_DEFAULT_V@) +am__v_CXX_0 = @echo " CXX " $@; +am__v_CXX_1 = +CXXLD = $(CXX) +CXXLINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CXXLD) $(AM_CXXFLAGS) \ + $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CXXLD = $(am__v_CXXLD_@AM_V@) +am__v_CXXLD_ = $(am__v_CXXLD_@AM_DEFAULT_V@) +am__v_CXXLD_0 = @echo " CXXLD " $@; +am__v_CXXLD_1 = +SOURCES = $(asio_cl_SOURCES) $(asio_cl2_SOURCES) $(asio_sv_SOURCES) \ + $(asio_sv2_SOURCES) $(client_SOURCES) $(deflate_SOURCES) \ + $(libevent_client_SOURCES) $(libevent_server_SOURCES) \ + $(tiny_nghttpd_SOURCES) +DIST_SOURCES = $(am__asio_cl_SOURCES_DIST) \ + $(am__asio_cl2_SOURCES_DIST) $(am__asio_sv_SOURCES_DIST) \ + $(am__asio_sv2_SOURCES_DIST) $(am__client_SOURCES_DIST) \ + $(am__deflate_SOURCES_DIST) \ + $(am__libevent_client_SOURCES_DIST) \ + $(am__libevent_server_SOURCES_DIST) \ + $(am__tiny_nghttpd_SOURCES_DIST) +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +ETAGS = etags +CTAGS = ctags +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +APPLDFLAGS = @APPLDFLAGS@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +BOOST_ASIO_LIB = @BOOST_ASIO_LIB@ +BOOST_CPPFLAGS = @BOOST_CPPFLAGS@ +BOOST_LDFLAGS = @BOOST_LDFLAGS@ +BOOST_SYSTEM_LIB = @BOOST_SYSTEM_LIB@ +BOOST_THREAD_LIB = @BOOST_THREAD_LIB@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CUNIT_CFLAGS = @CUNIT_CFLAGS@ +CUNIT_LIBS = @CUNIT_LIBS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +CYTHON = @CYTHON@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DLLTOOL = @DLLTOOL@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +GREP = @GREP@ +HAVE_CXX11 = @HAVE_CXX11@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +JANSSON_CFLAGS = @JANSSON_CFLAGS@ +JANSSON_LIBS = @JANSSON_LIBS@ +JEMALLOC_LIBS = @JEMALLOC_LIBS@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBEVENT_OPENSSL_CFLAGS = @LIBEVENT_OPENSSL_CFLAGS@ +LIBEVENT_OPENSSL_LIBS = @LIBEVENT_OPENSSL_LIBS@ +LIBEV_CFLAGS = @LIBEV_CFLAGS@ +LIBEV_LIBS = @LIBEV_LIBS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBSPDYLAY_CFLAGS = @LIBSPDYLAY_CFLAGS@ +LIBSPDYLAY_LIBS = @LIBSPDYLAY_LIBS@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +LT_AGE = @LT_AGE@ +LT_CURRENT = @LT_CURRENT@ +LT_REVISION = @LT_REVISION@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MKDIR_P = @MKDIR_P@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OPENSSL_CFLAGS = @OPENSSL_CFLAGS@ +OPENSSL_LIBS = @OPENSSL_LIBS@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PACKAGE_VERSION_NUM = @PACKAGE_VERSION_NUM@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PKG_CONFIG = @PKG_CONFIG@ +PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ +PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ +PYTHON = @PYTHON@ +PYTHON_CPPFLAGS = @PYTHON_CPPFLAGS@ +PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@ +PYTHON_EXTRA_LDFLAGS = @PYTHON_EXTRA_LDFLAGS@ +PYTHON_EXTRA_LIBS = @PYTHON_EXTRA_LIBS@ +PYTHON_LDFLAGS = @PYTHON_LDFLAGS@ +PYTHON_PLATFORM = @PYTHON_PLATFORM@ +PYTHON_PREFIX = @PYTHON_PREFIX@ +PYTHON_SITE_PKG = @PYTHON_SITE_PKG@ +PYTHON_VERSION = @PYTHON_VERSION@ +RANLIB = @RANLIB@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +TESTLDADD = @TESTLDADD@ +VERSION = @VERSION@ +WARNCFLAGS = @WARNCFLAGS@ +XML2_CONFIG = @XML2_CONFIG@ +XML_CPPFLAGS = @XML_CPPFLAGS@ +XML_LIBS = @XML_LIBS@ +ZLIB_CFLAGS = @ZLIB_CFLAGS@ +ZLIB_LIBS = @ZLIB_LIBS@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +pkgpyexecdir = @pkgpyexecdir@ +pkgpythondir = @pkgpythondir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +pyexecdir = @pyexecdir@ +pythondir = @pythondir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target = @target@ +target_alias = @target_alias@ +target_cpu = @target_cpu@ +target_os = @target_os@ +target_vendor = @target_vendor@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +@ENABLE_EXAMPLES_TRUE@AM_CFLAGS = $(WARNCFLAGS) +@ENABLE_EXAMPLES_TRUE@AM_CPPFLAGS = \ +@ENABLE_EXAMPLES_TRUE@ -Wall \ +@ENABLE_EXAMPLES_TRUE@ -I$(top_srcdir)/lib/includes \ +@ENABLE_EXAMPLES_TRUE@ -I$(top_builddir)/lib/includes \ +@ENABLE_EXAMPLES_TRUE@ -I$(top_srcdir)/src/includes \ +@ENABLE_EXAMPLES_TRUE@ -I$(top_srcdir)/third-party \ +@ENABLE_EXAMPLES_TRUE@ @LIBEVENT_OPENSSL_CFLAGS@ \ +@ENABLE_EXAMPLES_TRUE@ @OPENSSL_CFLAGS@ \ +@ENABLE_EXAMPLES_TRUE@ @DEFS@ + +@ENABLE_EXAMPLES_TRUE@LDADD = $(top_builddir)/lib/libnghttp2.la \ +@ENABLE_EXAMPLES_TRUE@ $(top_builddir)/third-party/libhttp-parser.la \ +@ENABLE_EXAMPLES_TRUE@ @LIBEVENT_OPENSSL_LIBS@ \ +@ENABLE_EXAMPLES_TRUE@ @OPENSSL_LIBS@ + +@ENABLE_EXAMPLES_TRUE@client_SOURCES = client.c +@ENABLE_EXAMPLES_TRUE@libevent_client_SOURCES = libevent-client.c +@ENABLE_EXAMPLES_TRUE@libevent_server_SOURCES = libevent-server.c +@ENABLE_EXAMPLES_TRUE@deflate_SOURCES = deflate.c +@ENABLE_EXAMPLES_TRUE@@ENABLE_TINY_NGHTTPD_TRUE@tiny_nghttpd_SOURCES = tiny-nghttpd.c + +# AM_CPPFLAGS must be placed first, so that header file (e.g., +# nghttp2/nghttp2.h) in this package is used rather than installed +# one. +@ENABLE_ASIO_LIB_TRUE@@ENABLE_EXAMPLES_TRUE@ASIOCPPFLAGS = ${AM_CPPFLAGS} ${BOOST_CPPFLAGS} +@ENABLE_ASIO_LIB_TRUE@@ENABLE_EXAMPLES_TRUE@ASIOLDADD = $(top_builddir)/lib/libnghttp2.la \ +@ENABLE_ASIO_LIB_TRUE@@ENABLE_EXAMPLES_TRUE@ $(top_builddir)/src/libnghttp2_asio.la @JEMALLOC_LIBS@ \ +@ENABLE_ASIO_LIB_TRUE@@ENABLE_EXAMPLES_TRUE@ $(top_builddir)/third-party/libhttp-parser.la \ +@ENABLE_ASIO_LIB_TRUE@@ENABLE_EXAMPLES_TRUE@ ${BOOST_LDFLAGS} \ +@ENABLE_ASIO_LIB_TRUE@@ENABLE_EXAMPLES_TRUE@ ${BOOST_ASIO_LIB} \ +@ENABLE_ASIO_LIB_TRUE@@ENABLE_EXAMPLES_TRUE@ ${BOOST_THREAD_LIB} \ +@ENABLE_ASIO_LIB_TRUE@@ENABLE_EXAMPLES_TRUE@ ${BOOST_SYSTEM_LIB} \ +@ENABLE_ASIO_LIB_TRUE@@ENABLE_EXAMPLES_TRUE@ @OPENSSL_LIBS@ \ +@ENABLE_ASIO_LIB_TRUE@@ENABLE_EXAMPLES_TRUE@ @APPLDFLAGS@ + +@ENABLE_ASIO_LIB_TRUE@@ENABLE_EXAMPLES_TRUE@asio_sv_SOURCES = asio-sv.cc +@ENABLE_ASIO_LIB_TRUE@@ENABLE_EXAMPLES_TRUE@asio_sv_CPPFLAGS = ${ASIOCPPFLAGS} +@ENABLE_ASIO_LIB_TRUE@@ENABLE_EXAMPLES_TRUE@asio_sv_LDADD = ${ASIOLDADD} +@ENABLE_ASIO_LIB_TRUE@@ENABLE_EXAMPLES_TRUE@asio_sv2_SOURCES = asio-sv2.cc +@ENABLE_ASIO_LIB_TRUE@@ENABLE_EXAMPLES_TRUE@asio_sv2_CPPFLAGS = ${ASIOCPPFLAGS} +@ENABLE_ASIO_LIB_TRUE@@ENABLE_EXAMPLES_TRUE@asio_sv2_LDADD = ${ASIOLDADD} +@ENABLE_ASIO_LIB_TRUE@@ENABLE_EXAMPLES_TRUE@asio_cl_SOURCES = asio-cl.cc +@ENABLE_ASIO_LIB_TRUE@@ENABLE_EXAMPLES_TRUE@asio_cl_CPPFLAGS = ${ASIOCPPFLAGS} +@ENABLE_ASIO_LIB_TRUE@@ENABLE_EXAMPLES_TRUE@asio_cl_LDADD = ${ASIOLDADD} +@ENABLE_ASIO_LIB_TRUE@@ENABLE_EXAMPLES_TRUE@asio_cl2_SOURCES = asio-cl2.cc +@ENABLE_ASIO_LIB_TRUE@@ENABLE_EXAMPLES_TRUE@asio_cl2_CPPFLAGS = ${ASIOCPPFLAGS} +@ENABLE_ASIO_LIB_TRUE@@ENABLE_EXAMPLES_TRUE@asio_cl2_LDADD = ${ASIOLDADD} +all: all-am + +.SUFFIXES: +.SUFFIXES: .c .cc .lo .o .obj +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu examples/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu examples/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): + +clean-noinstPROGRAMS: + @list='$(noinst_PROGRAMS)'; test -n "$$list" || exit 0; \ + echo " rm -f" $$list; \ + rm -f $$list || exit $$?; \ + test -n "$(EXEEXT)" || exit 0; \ + list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ + echo " rm -f" $$list; \ + rm -f $$list + +asio-cl$(EXEEXT): $(asio_cl_OBJECTS) $(asio_cl_DEPENDENCIES) $(EXTRA_asio_cl_DEPENDENCIES) + @rm -f asio-cl$(EXEEXT) + $(AM_V_CXXLD)$(CXXLINK) $(asio_cl_OBJECTS) $(asio_cl_LDADD) $(LIBS) + +asio-cl2$(EXEEXT): $(asio_cl2_OBJECTS) $(asio_cl2_DEPENDENCIES) $(EXTRA_asio_cl2_DEPENDENCIES) + @rm -f asio-cl2$(EXEEXT) + $(AM_V_CXXLD)$(CXXLINK) $(asio_cl2_OBJECTS) $(asio_cl2_LDADD) $(LIBS) + +asio-sv$(EXEEXT): $(asio_sv_OBJECTS) $(asio_sv_DEPENDENCIES) $(EXTRA_asio_sv_DEPENDENCIES) + @rm -f asio-sv$(EXEEXT) + $(AM_V_CXXLD)$(CXXLINK) $(asio_sv_OBJECTS) $(asio_sv_LDADD) $(LIBS) + +asio-sv2$(EXEEXT): $(asio_sv2_OBJECTS) $(asio_sv2_DEPENDENCIES) $(EXTRA_asio_sv2_DEPENDENCIES) + @rm -f asio-sv2$(EXEEXT) + $(AM_V_CXXLD)$(CXXLINK) $(asio_sv2_OBJECTS) $(asio_sv2_LDADD) $(LIBS) + +client$(EXEEXT): $(client_OBJECTS) $(client_DEPENDENCIES) $(EXTRA_client_DEPENDENCIES) + @rm -f client$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(client_OBJECTS) $(client_LDADD) $(LIBS) + +deflate$(EXEEXT): $(deflate_OBJECTS) $(deflate_DEPENDENCIES) $(EXTRA_deflate_DEPENDENCIES) + @rm -f deflate$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(deflate_OBJECTS) $(deflate_LDADD) $(LIBS) + +libevent-client$(EXEEXT): $(libevent_client_OBJECTS) $(libevent_client_DEPENDENCIES) $(EXTRA_libevent_client_DEPENDENCIES) + @rm -f libevent-client$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(libevent_client_OBJECTS) $(libevent_client_LDADD) $(LIBS) + +libevent-server$(EXEEXT): $(libevent_server_OBJECTS) $(libevent_server_DEPENDENCIES) $(EXTRA_libevent_server_DEPENDENCIES) + @rm -f libevent-server$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(libevent_server_OBJECTS) $(libevent_server_LDADD) $(LIBS) + +tiny-nghttpd$(EXEEXT): $(tiny_nghttpd_OBJECTS) $(tiny_nghttpd_DEPENDENCIES) $(EXTRA_tiny_nghttpd_DEPENDENCIES) + @rm -f tiny-nghttpd$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(tiny_nghttpd_OBJECTS) $(tiny_nghttpd_LDADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asio_cl-asio-cl.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asio_cl2-asio-cl2.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asio_sv-asio-sv.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asio_sv2-asio-sv2.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/client.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/deflate.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libevent-client.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libevent-server.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tiny-nghttpd.Po@am__quote@ + +.c.o: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\ +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< + +.c.obj: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\ +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.c.lo: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\ +@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< + +.cc.o: +@am__fastdepCXX_TRUE@ $(AM_V_CXX)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\ +@am__fastdepCXX_TRUE@ $(CXXCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCXX_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ $< + +.cc.obj: +@am__fastdepCXX_TRUE@ $(AM_V_CXX)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\ +@am__fastdepCXX_TRUE@ $(CXXCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\ +@am__fastdepCXX_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.cc.lo: +@am__fastdepCXX_TRUE@ $(AM_V_CXX)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\ +@am__fastdepCXX_TRUE@ $(LTCXXCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCXX_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LTCXXCOMPILE) -c -o $@ $< + +asio_cl-asio-cl.o: asio-cl.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(asio_cl_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT asio_cl-asio-cl.o -MD -MP -MF $(DEPDIR)/asio_cl-asio-cl.Tpo -c -o asio_cl-asio-cl.o `test -f 'asio-cl.cc' || echo '$(srcdir)/'`asio-cl.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/asio_cl-asio-cl.Tpo $(DEPDIR)/asio_cl-asio-cl.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='asio-cl.cc' object='asio_cl-asio-cl.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(asio_cl_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o asio_cl-asio-cl.o `test -f 'asio-cl.cc' || echo '$(srcdir)/'`asio-cl.cc + +asio_cl-asio-cl.obj: asio-cl.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(asio_cl_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT asio_cl-asio-cl.obj -MD -MP -MF $(DEPDIR)/asio_cl-asio-cl.Tpo -c -o asio_cl-asio-cl.obj `if test -f 'asio-cl.cc'; then $(CYGPATH_W) 'asio-cl.cc'; else $(CYGPATH_W) '$(srcdir)/asio-cl.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/asio_cl-asio-cl.Tpo $(DEPDIR)/asio_cl-asio-cl.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='asio-cl.cc' object='asio_cl-asio-cl.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(asio_cl_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o asio_cl-asio-cl.obj `if test -f 'asio-cl.cc'; then $(CYGPATH_W) 'asio-cl.cc'; else $(CYGPATH_W) '$(srcdir)/asio-cl.cc'; fi` + +asio_cl2-asio-cl2.o: asio-cl2.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(asio_cl2_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT asio_cl2-asio-cl2.o -MD -MP -MF $(DEPDIR)/asio_cl2-asio-cl2.Tpo -c -o asio_cl2-asio-cl2.o `test -f 'asio-cl2.cc' || echo '$(srcdir)/'`asio-cl2.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/asio_cl2-asio-cl2.Tpo $(DEPDIR)/asio_cl2-asio-cl2.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='asio-cl2.cc' object='asio_cl2-asio-cl2.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(asio_cl2_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o asio_cl2-asio-cl2.o `test -f 'asio-cl2.cc' || echo '$(srcdir)/'`asio-cl2.cc + +asio_cl2-asio-cl2.obj: asio-cl2.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(asio_cl2_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT asio_cl2-asio-cl2.obj -MD -MP -MF $(DEPDIR)/asio_cl2-asio-cl2.Tpo -c -o asio_cl2-asio-cl2.obj `if test -f 'asio-cl2.cc'; then $(CYGPATH_W) 'asio-cl2.cc'; else $(CYGPATH_W) '$(srcdir)/asio-cl2.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/asio_cl2-asio-cl2.Tpo $(DEPDIR)/asio_cl2-asio-cl2.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='asio-cl2.cc' object='asio_cl2-asio-cl2.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(asio_cl2_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o asio_cl2-asio-cl2.obj `if test -f 'asio-cl2.cc'; then $(CYGPATH_W) 'asio-cl2.cc'; else $(CYGPATH_W) '$(srcdir)/asio-cl2.cc'; fi` + +asio_sv-asio-sv.o: asio-sv.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(asio_sv_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT asio_sv-asio-sv.o -MD -MP -MF $(DEPDIR)/asio_sv-asio-sv.Tpo -c -o asio_sv-asio-sv.o `test -f 'asio-sv.cc' || echo '$(srcdir)/'`asio-sv.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/asio_sv-asio-sv.Tpo $(DEPDIR)/asio_sv-asio-sv.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='asio-sv.cc' object='asio_sv-asio-sv.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(asio_sv_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o asio_sv-asio-sv.o `test -f 'asio-sv.cc' || echo '$(srcdir)/'`asio-sv.cc + +asio_sv-asio-sv.obj: asio-sv.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(asio_sv_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT asio_sv-asio-sv.obj -MD -MP -MF $(DEPDIR)/asio_sv-asio-sv.Tpo -c -o asio_sv-asio-sv.obj `if test -f 'asio-sv.cc'; then $(CYGPATH_W) 'asio-sv.cc'; else $(CYGPATH_W) '$(srcdir)/asio-sv.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/asio_sv-asio-sv.Tpo $(DEPDIR)/asio_sv-asio-sv.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='asio-sv.cc' object='asio_sv-asio-sv.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(asio_sv_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o asio_sv-asio-sv.obj `if test -f 'asio-sv.cc'; then $(CYGPATH_W) 'asio-sv.cc'; else $(CYGPATH_W) '$(srcdir)/asio-sv.cc'; fi` + +asio_sv2-asio-sv2.o: asio-sv2.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(asio_sv2_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT asio_sv2-asio-sv2.o -MD -MP -MF $(DEPDIR)/asio_sv2-asio-sv2.Tpo -c -o asio_sv2-asio-sv2.o `test -f 'asio-sv2.cc' || echo '$(srcdir)/'`asio-sv2.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/asio_sv2-asio-sv2.Tpo $(DEPDIR)/asio_sv2-asio-sv2.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='asio-sv2.cc' object='asio_sv2-asio-sv2.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(asio_sv2_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o asio_sv2-asio-sv2.o `test -f 'asio-sv2.cc' || echo '$(srcdir)/'`asio-sv2.cc + +asio_sv2-asio-sv2.obj: asio-sv2.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(asio_sv2_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT asio_sv2-asio-sv2.obj -MD -MP -MF $(DEPDIR)/asio_sv2-asio-sv2.Tpo -c -o asio_sv2-asio-sv2.obj `if test -f 'asio-sv2.cc'; then $(CYGPATH_W) 'asio-sv2.cc'; else $(CYGPATH_W) '$(srcdir)/asio-sv2.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/asio_sv2-asio-sv2.Tpo $(DEPDIR)/asio_sv2-asio-sv2.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='asio-sv2.cc' object='asio_sv2-asio-sv2.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(asio_sv2_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o asio_sv2-asio-sv2.obj `if test -f 'asio-sv2.cc'; then $(CYGPATH_W) 'asio-sv2.cc'; else $(CYGPATH_W) '$(srcdir)/asio-sv2.cc'; fi` + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-am +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + $(am__define_uniq_tagged_files); \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: ctags-am + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" +cscopelist: cscopelist-am + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(PROGRAMS) +installdirs: +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic clean-libtool clean-noinstPROGRAMS \ + mostlyclean-am + +distclean: distclean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: + +.MAKE: install-am install-strip + +.PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ + clean-libtool clean-noinstPROGRAMS cscopelist-am ctags \ + ctags-am distclean distclean-compile distclean-generic \ + distclean-libtool distclean-tags distdir dvi dvi-am html \ + html-am info info-am install install-am install-data \ + install-data-am install-dvi install-dvi-am install-exec \ + install-exec-am install-html install-html-am install-info \ + install-info-am install-man install-pdf install-pdf-am \ + install-ps install-ps-am install-strip installcheck \ + installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-compile \ + mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ + tags tags-am uninstall uninstall-am + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/examples/asio-cl.cc b/examples/asio-cl.cc new file mode 100644 index 0000000..c7d7bb1 --- /dev/null +++ b/examples/asio-cl.cc @@ -0,0 +1,96 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2015 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include + +#include + +using boost::asio::ip::tcp; + +using namespace nghttp2::asio_http2; +using namespace nghttp2::asio_http2::client; + +int main(int argc, char *argv[]) { + try { + if (argc < 2) { + std::cerr << "Usage: asio-cl URI" << std::endl; + return 1; + } + boost::system::error_code ec; + boost::asio::io_service io_service; + + std::string uri = argv[1]; + std::string scheme, host, service; + + if (host_service_from_uri(ec, scheme, host, service, uri)) { + std::cerr << "error: bad URI: " << ec.message() << std::endl; + return 1; + } + + boost::asio::ssl::context tls_ctx(boost::asio::ssl::context::sslv23); + tls_ctx.set_default_verify_paths(); + // disabled to make development easier... + // tls_ctx.set_verify_mode(boost::asio::ssl::verify_peer); + configure_tls_context(ec, tls_ctx); + + auto sess = scheme == "https" ? session(io_service, tls_ctx, host, service) + : session(io_service, host, service); + + sess.on_connect([&sess, &uri](tcp::resolver::iterator endpoint_it) { + boost::system::error_code ec; + auto req = sess.submit(ec, "GET", uri); + + if (ec) { + std::cerr << "error: " << ec.message() << std::endl; + return; + } + + req->on_response([&sess](const response &res) { + std::cerr << "HTTP/2 " << res.status_code() << std::endl; + for (auto &kv : res.header()) { + std::cerr << kv.first << ": " << kv.second.value << "\n"; + } + std::cerr << std::endl; + + res.on_data([&sess](const uint8_t *data, std::size_t len) { + std::cerr.write(reinterpret_cast(data), len); + std::cerr << std::endl; + }); + }); + + req->on_close([&sess](uint32_t error_code) { sess.shutdown(); }); + }); + + sess.on_error([](const boost::system::error_code &ec) { + std::cerr << "error: " << ec.message() << std::endl; + }); + + io_service.run(); + } catch (std::exception &e) { + std::cerr << "exception: " << e.what() << "\n"; + } + + return 0; +} diff --git a/examples/asio-cl2.cc b/examples/asio-cl2.cc new file mode 100644 index 0000000..2a8ed71 --- /dev/null +++ b/examples/asio-cl2.cc @@ -0,0 +1,134 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2015 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include +#include + +#include + +using boost::asio::ip::tcp; + +using namespace nghttp2::asio_http2; +using namespace nghttp2::asio_http2::client; + +void print_header(const header_map &h) { + for (auto &kv : h) { + std::cerr << kv.first << ": " << kv.second.value << "\n"; + } + std::cerr << std::endl; +} + +void print_header(const response &res) { + std::cerr << "HTTP/2 " << res.status_code() << "\n"; + print_header(res.header()); +} + +void print_header(const request &req) { + auto &uri = req.uri(); + std::cerr << req.method() << " " << uri.scheme << "://" << uri.host + << uri.path; + if (!uri.raw_query.empty()) { + std::cerr << "?" << uri.raw_query; + } + std::cerr << " HTTP/2\n"; + print_header(req.header()); +} + +int main(int argc, char *argv[]) { + try { + if (argc < 2) { + std::cerr << "Usage: asio-cl URI" << std::endl; + return 1; + } + boost::system::error_code ec; + boost::asio::io_service io_service; + + std::string uri = argv[1]; + std::string scheme, host, service; + + if (host_service_from_uri(ec, scheme, host, service, uri)) { + std::cerr << "error: bad URI: " << ec.message() << std::endl; + return 1; + } + + boost::asio::ssl::context tls_ctx(boost::asio::ssl::context::sslv23); + tls_ctx.set_default_verify_paths(); + // disabled to make development easier... + // tls_ctx.set_verify_mode(boost::asio::ssl::verify_peer); + configure_tls_context(ec, tls_ctx); + + auto sess = scheme == "https" ? session(io_service, tls_ctx, host, service) + : session(io_service, host, service); + + sess.on_connect([&sess, &uri](tcp::resolver::iterator endpoint_it) { + std::cerr << "connected to " << (*endpoint_it).endpoint() << std::endl; + boost::system::error_code ec; + auto req = sess.submit(ec, "GET", uri, {{"cookie", {"foo=bar", true}}}); + if (ec) { + std::cerr << "error: " << ec.message() << std::endl; + return; + } + + req->on_response([&sess, req](const response &res) { + std::cerr << "response header was received" << std::endl; + print_header(res); + + res.on_data([&sess](const uint8_t *data, std::size_t len) { + std::cerr.write(reinterpret_cast(data), len); + std::cerr << std::endl; + }); + }); + + req->on_close([&sess](uint32_t error_code) { + std::cerr << "request done with error_code=" << error_code << std::endl; + }); + + req->on_push([](const request &push_req) { + std::cerr << "push request was received" << std::endl; + + print_header(push_req); + + push_req.on_response([](const response &res) { + std::cerr << "push response header was received" << std::endl; + + res.on_data([](const uint8_t *data, std::size_t len) { + std::cerr.write(reinterpret_cast(data), len); + std::cerr << std::endl; + }); + }); + }); + }); + + sess.on_error([](const boost::system::error_code &ec) { + std::cerr << "error: " << ec.message() << std::endl; + }); + + io_service.run(); + } catch (std::exception &e) { + std::cerr << "exception: " << e.what() << "\n"; + } + + return 0; +} diff --git a/examples/asio-sv.cc b/examples/asio-sv.cc new file mode 100644 index 0000000..47a1d84 --- /dev/null +++ b/examples/asio-sv.cc @@ -0,0 +1,149 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2014 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +// We wrote this code based on the original code which has the +// following license: +// +// main.cpp +// ~~~~~~~~ +// +// Copyright (c) 2003-2013 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#include +#include + +#include + +using namespace nghttp2::asio_http2; +using namespace nghttp2::asio_http2::server; + +int main(int argc, char *argv[]) { + try { + // Check command line arguments. + if (argc < 4) { + std::cerr + << "Usage: asio-sv
    [ " + << "]\n"; + return 1; + } + + boost::system::error_code ec; + + std::string addr = argv[1]; + std::string port = argv[2]; + std::size_t num_threads = std::stoi(argv[3]); + + http2 server; + + server.num_threads(num_threads); + + server.handle("/", [](const request &req, const response &res) { + res.write_head(200, {{"foo", {"bar"}}}); + res.end("hello, world\n"); + }); + server.handle("/secret/", [](const request &req, const response &res) { + res.write_head(200); + res.end("under construction!\n"); + }); + server.handle("/push", [](const request &req, const response &res) { + boost::system::error_code ec; + auto push = res.push(ec, "GET", "/push/1"); + if (!ec) { + push->write_head(200); + push->end("server push FTW!\n"); + } + + res.write_head(200); + res.end("you'll receive server push!\n"); + }); + server.handle("/delay", [](const request &req, const response &res) { + res.write_head(200); + + auto timer = std::make_shared( + res.io_service(), boost::posix_time::seconds(3)); + auto closed = std::make_shared(); + + res.on_close([timer, closed](uint32_t error_code) { + timer->cancel(); + *closed = true; + }); + + timer->async_wait([&res, closed](const boost::system::error_code &ec) { + if (ec || *closed) { + return; + } + + res.end("finally!\n"); + }); + }); + server.handle("/trailer", [](const request &req, const response &res) { + // send trailer part. + res.write_head(200, {{"trailers", {"digest"}}}); + + std::string body = "nghttp2 FTW!\n"; + auto left = std::make_shared(body.size()); + + res.end([&res, body, left](uint8_t *dst, std::size_t len, + uint32_t *data_flags) { + auto n = std::min(len, *left); + std::copy_n(body.c_str() + (body.size() - *left), n, dst); + *left -= n; + if (*left == 0) { + *data_flags |= + NGHTTP2_DATA_FLAG_EOF | NGHTTP2_DATA_FLAG_NO_END_STREAM; + // RFC 3230 Instance Digests in HTTP. The digest value is + // SHA-256 message digest of body. + res.write_trailer( + {{"digest", + {"SHA-256=qqXqskW7F3ueBSvmZRCiSwl2ym4HRO0M/pvQCBlSDis="}}}); + } + return n; + }); + }); + + if (argc >= 6) { + boost::asio::ssl::context tls(boost::asio::ssl::context::sslv23); + tls.use_private_key_file(argv[4], boost::asio::ssl::context::pem); + tls.use_certificate_chain_file(argv[5]); + + configure_tls_context_easy(ec, tls); + + if (server.listen_and_serve(ec, tls, addr, port)) { + std::cerr << "error: " << ec.message() << std::endl; + } + } else { + if (server.listen_and_serve(ec, addr, port)) { + std::cerr << "error: " << ec.message() << std::endl; + } + } + } catch (std::exception &e) { + std::cerr << "exception: " << e.what() << "\n"; + } + + return 0; +} diff --git a/examples/asio-sv2.cc b/examples/asio-sv2.cc new file mode 100644 index 0000000..8d2580e --- /dev/null +++ b/examples/asio-sv2.cc @@ -0,0 +1,125 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2014 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +// We wrote this code based on the original code which has the +// following license: +// +// main.cpp +// ~~~~~~~~ +// +// Copyright (c) 2003-2013 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif // HAVE_UNISTD_H +#ifdef HAVE_FCNTL_H +#include +#endif // HAVE_FCNTL_H +#include +#include + +#include + +using namespace nghttp2::asio_http2; +using namespace nghttp2::asio_http2::server; + +int main(int argc, char *argv[]) { + try { + // Check command line arguments. + if (argc < 5) { + std::cerr << "Usage: asio-sv2
    " + << "[ ]\n"; + return 1; + } + + boost::system::error_code ec; + + std::string addr = argv[1]; + std::string port = argv[2]; + std::size_t num_threads = std::stoi(argv[3]); + std::string docroot = argv[4]; + + http2 server; + + server.num_threads(num_threads); + + server.handle("/", [&docroot](const request &req, const response &res) { + auto path = percent_decode(req.uri().path); + if (!check_path(path)) { + res.write_head(404); + res.end(); + return; + } + + if (path == "/") { + path = "/index.html"; + } + + path = docroot + path; + auto fd = open(path.c_str(), O_RDONLY); + if (fd == -1) { + res.write_head(404); + res.end(); + return; + } + + auto header = header_map(); + + struct stat stbuf; + if (stat(path.c_str(), &stbuf) == 0) { + header.emplace("content-length", + header_value{std::to_string(stbuf.st_size)}); + header.emplace("last-modified", + header_value{http_date(stbuf.st_mtime)}); + } + res.write_head(200, std::move(header)); + res.end(file_generator_from_fd(fd)); + }); + + if (argc >= 7) { + boost::asio::ssl::context tls(boost::asio::ssl::context::sslv23); + tls.use_private_key_file(argv[5], boost::asio::ssl::context::pem); + tls.use_certificate_chain_file(argv[6]); + + configure_tls_context_easy(ec, tls); + + if (server.listen_and_serve(ec, tls, addr, port)) { + std::cerr << "error: " << ec.message() << std::endl; + } + } else { + if (server.listen_and_serve(ec, addr, port)) { + std::cerr << "error: " << ec.message() << std::endl; + } + } + } catch (std::exception &e) { + std::cerr << "exception: " << e.what() << "\n"; + } + + return 0; +} diff --git a/examples/client.c b/examples/client.c new file mode 100644 index 0000000..4b4deee --- /dev/null +++ b/examples/client.c @@ -0,0 +1,703 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2013 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +/* + * This program is written to show how to use nghttp2 API in C and + * intentionally made simple. + */ +#ifdef HAVE_CONFIG_H +#include +#endif /* HAVE_CONFIG_H */ + +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif /* HAVE_UNISTD_H */ +#ifdef HAVE_FCNTL_H +#include +#endif /* HAVE_FCNTL_H */ +#include +#ifdef HAVE_SYS_SOCKET_H +#include +#endif /* HAVE_SYS_SOCKET_H */ +#ifdef HAVE_NETDB_H +#include +#endif /* HAVE_NETDB_H */ +#ifdef HAVE_NETINET_IN_H +#include +#endif /* HAVE_NETINET_IN_H */ +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +enum { IO_NONE, WANT_READ, WANT_WRITE }; + +#define MAKE_NV(NAME, VALUE) \ + { \ + (uint8_t *) NAME, (uint8_t *)VALUE, sizeof(NAME) - 1, sizeof(VALUE) - 1, \ + NGHTTP2_NV_FLAG_NONE \ + } + +#define MAKE_NV_CS(NAME, VALUE) \ + { \ + (uint8_t *) NAME, (uint8_t *)VALUE, sizeof(NAME) - 1, strlen(VALUE), \ + NGHTTP2_NV_FLAG_NONE \ + } + +struct Connection { + SSL *ssl; + nghttp2_session *session; + /* WANT_READ if SSL/TLS connection needs more input; or WANT_WRITE + if it needs more output; or IO_NONE. This is necessary because + SSL/TLS re-negotiation is possible at any time. nghttp2 API + offers similar functions like nghttp2_session_want_read() and + nghttp2_session_want_write() but they do not take into account + SSL/TSL connection. */ + int want_io; +}; + +struct Request { + char *host; + /* In this program, path contains query component as well. */ + char *path; + /* This is the concatenation of host and port with ":" in + between. */ + char *hostport; + /* Stream ID for this request. */ + int32_t stream_id; + uint16_t port; +}; + +struct URI { + const char *host; + /* In this program, path contains query component as well. */ + const char *path; + size_t pathlen; + const char *hostport; + size_t hostlen; + size_t hostportlen; + uint16_t port; +}; + +/* + * Returns copy of string |s| with the length |len|. The returned + * string is NULL-terminated. + */ +static char *strcopy(const char *s, size_t len) { + char *dst; + dst = malloc(len + 1); + memcpy(dst, s, len); + dst[len] = '\0'; + return dst; +} + +/* + * Prints error message |msg| and exit. + */ +static void die(const char *msg) { + fprintf(stderr, "FATAL: %s\n", msg); + exit(EXIT_FAILURE); +} + +/* + * Prints error containing the function name |func| and message |msg| + * and exit. + */ +static void dief(const char *func, const char *msg) { + fprintf(stderr, "FATAL: %s: %s\n", func, msg); + exit(EXIT_FAILURE); +} + +/* + * Prints error containing the function name |func| and error code + * |error_code| and exit. + */ +static void diec(const char *func, int error_code) { + fprintf(stderr, "FATAL: %s: error_code=%d, msg=%s\n", func, error_code, + nghttp2_strerror(error_code)); + exit(EXIT_FAILURE); +} + +/* + * The implementation of nghttp2_send_callback type. Here we write + * |data| with size |length| to the network and return the number of + * bytes actually written. See the documentation of + * nghttp2_send_callback for the details. + */ +static ssize_t send_callback(nghttp2_session *session _U_, const uint8_t *data, + size_t length, int flags _U_, void *user_data) { + struct Connection *connection; + int rv; + connection = (struct Connection *)user_data; + connection->want_io = IO_NONE; + ERR_clear_error(); + rv = SSL_write(connection->ssl, data, (int)length); + if (rv <= 0) { + int err = SSL_get_error(connection->ssl, rv); + if (err == SSL_ERROR_WANT_WRITE || err == SSL_ERROR_WANT_READ) { + connection->want_io = + (err == SSL_ERROR_WANT_READ ? WANT_READ : WANT_WRITE); + rv = NGHTTP2_ERR_WOULDBLOCK; + } else { + rv = NGHTTP2_ERR_CALLBACK_FAILURE; + } + } + return rv; +} + +/* + * The implementation of nghttp2_recv_callback type. Here we read data + * from the network and write them in |buf|. The capacity of |buf| is + * |length| bytes. Returns the number of bytes stored in |buf|. See + * the documentation of nghttp2_recv_callback for the details. + */ +static ssize_t recv_callback(nghttp2_session *session _U_, uint8_t *buf, + size_t length, int flags _U_, void *user_data) { + struct Connection *connection; + int rv; + connection = (struct Connection *)user_data; + connection->want_io = IO_NONE; + ERR_clear_error(); + rv = SSL_read(connection->ssl, buf, (int)length); + if (rv < 0) { + int err = SSL_get_error(connection->ssl, rv); + if (err == SSL_ERROR_WANT_WRITE || err == SSL_ERROR_WANT_READ) { + connection->want_io = + (err == SSL_ERROR_WANT_READ ? WANT_READ : WANT_WRITE); + rv = NGHTTP2_ERR_WOULDBLOCK; + } else { + rv = NGHTTP2_ERR_CALLBACK_FAILURE; + } + } else if (rv == 0) { + rv = NGHTTP2_ERR_EOF; + } + return rv; +} + +static int on_frame_send_callback(nghttp2_session *session, + const nghttp2_frame *frame, + void *user_data _U_) { + size_t i; + switch (frame->hd.type) { + case NGHTTP2_HEADERS: + if (nghttp2_session_get_stream_user_data(session, frame->hd.stream_id)) { + const nghttp2_nv *nva = frame->headers.nva; + printf("[INFO] C ----------------------------> S (HEADERS)\n"); + for (i = 0; i < frame->headers.nvlen; ++i) { + fwrite(nva[i].name, nva[i].namelen, 1, stdout); + printf(": "); + fwrite(nva[i].value, nva[i].valuelen, 1, stdout); + printf("\n"); + } + } + break; + case NGHTTP2_RST_STREAM: + printf("[INFO] C ----------------------------> S (RST_STREAM)\n"); + break; + case NGHTTP2_GOAWAY: + printf("[INFO] C ----------------------------> S (GOAWAY)\n"); + break; + } + return 0; +} + +static int on_frame_recv_callback(nghttp2_session *session, + const nghttp2_frame *frame, + void *user_data _U_) { + size_t i; + switch (frame->hd.type) { + case NGHTTP2_HEADERS: + if (frame->headers.cat == NGHTTP2_HCAT_RESPONSE) { + const nghttp2_nv *nva = frame->headers.nva; + struct Request *req; + req = nghttp2_session_get_stream_user_data(session, frame->hd.stream_id); + if (req) { + printf("[INFO] C <---------------------------- S (HEADERS)\n"); + for (i = 0; i < frame->headers.nvlen; ++i) { + fwrite(nva[i].name, nva[i].namelen, 1, stdout); + printf(": "); + fwrite(nva[i].value, nva[i].valuelen, 1, stdout); + printf("\n"); + } + } + } + break; + case NGHTTP2_RST_STREAM: + printf("[INFO] C <---------------------------- S (RST_STREAM)\n"); + break; + case NGHTTP2_GOAWAY: + printf("[INFO] C <---------------------------- S (GOAWAY)\n"); + break; + } + return 0; +} + +/* + * The implementation of nghttp2_on_stream_close_callback type. We use + * this function to know the response is fully received. Since we just + * fetch 1 resource in this program, after reception of the response, + * we submit GOAWAY and close the session. + */ +static int on_stream_close_callback(nghttp2_session *session, int32_t stream_id, + uint32_t error_code _U_, + void *user_data _U_) { + struct Request *req; + req = nghttp2_session_get_stream_user_data(session, stream_id); + if (req) { + int rv; + rv = nghttp2_session_terminate_session(session, NGHTTP2_NO_ERROR); + + if (rv != 0) { + diec("nghttp2_session_terminate_session", rv); + } + } + return 0; +} + +#define MAX_OUTLEN 4096 + +/* + * The implementation of nghttp2_on_data_chunk_recv_callback type. We + * use this function to print the received response body. + */ +static int on_data_chunk_recv_callback(nghttp2_session *session, + uint8_t flags _U_, int32_t stream_id, + const uint8_t *data, size_t len, + void *user_data _U_) { + struct Request *req; + req = nghttp2_session_get_stream_user_data(session, stream_id); + if (req) { + printf("[INFO] C <---------------------------- S (DATA chunk)\n" + "%lu bytes\n", + (unsigned long int)len); + fwrite(data, 1, len, stdout); + printf("\n"); + } + return 0; +} + +/* + * Setup callback functions. nghttp2 API offers many callback + * functions, but most of them are optional. The send_callback is + * always required. Since we use nghttp2_session_recv(), the + * recv_callback is also required. + */ +static void setup_nghttp2_callbacks(nghttp2_session_callbacks *callbacks) { + nghttp2_session_callbacks_set_send_callback(callbacks, send_callback); + + nghttp2_session_callbacks_set_recv_callback(callbacks, recv_callback); + + nghttp2_session_callbacks_set_on_frame_send_callback(callbacks, + on_frame_send_callback); + + nghttp2_session_callbacks_set_on_frame_recv_callback(callbacks, + on_frame_recv_callback); + + nghttp2_session_callbacks_set_on_stream_close_callback( + callbacks, on_stream_close_callback); + + nghttp2_session_callbacks_set_on_data_chunk_recv_callback( + callbacks, on_data_chunk_recv_callback); +} + +/* + * Callback function for TLS NPN. Since this program only supports + * HTTP/2 protocol, if server does not offer HTTP/2 the nghttp2 + * library supports, we terminate program. + */ +static int select_next_proto_cb(SSL *ssl _U_, unsigned char **out, + unsigned char *outlen, const unsigned char *in, + unsigned int inlen, void *arg _U_) { + int rv; + /* nghttp2_select_next_protocol() selects HTTP/2 protocol the + nghttp2 library supports. */ + rv = nghttp2_select_next_protocol(out, outlen, in, inlen); + if (rv <= 0) { + die("Server did not advertise HTTP/2 protocol"); + } + return SSL_TLSEXT_ERR_OK; +} + +/* + * Setup SSL/TLS context. + */ +static void init_ssl_ctx(SSL_CTX *ssl_ctx) { + /* Disable SSLv2 and enable all workarounds for buggy servers */ + SSL_CTX_set_options(ssl_ctx, SSL_OP_ALL | SSL_OP_NO_SSLv2); + SSL_CTX_set_mode(ssl_ctx, SSL_MODE_AUTO_RETRY); + SSL_CTX_set_mode(ssl_ctx, SSL_MODE_RELEASE_BUFFERS); + /* Set NPN callback */ + SSL_CTX_set_next_proto_select_cb(ssl_ctx, select_next_proto_cb, NULL); +} + +static void ssl_handshake(SSL *ssl, int fd) { + int rv; + if (SSL_set_fd(ssl, fd) == 0) { + dief("SSL_set_fd", ERR_error_string(ERR_get_error(), NULL)); + } + ERR_clear_error(); + rv = SSL_connect(ssl); + if (rv <= 0) { + dief("SSL_connect", ERR_error_string(ERR_get_error(), NULL)); + } +} + +/* + * Connects to the host |host| and port |port|. This function returns + * the file descriptor of the client socket. + */ +static int connect_to(const char *host, uint16_t port) { + struct addrinfo hints; + int fd = -1; + int rv; + char service[NI_MAXSERV]; + struct addrinfo *res, *rp; + snprintf(service, sizeof(service), "%u", port); + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + rv = getaddrinfo(host, service, &hints, &res); + if (rv != 0) { + dief("getaddrinfo", gai_strerror(rv)); + } + for (rp = res; rp; rp = rp->ai_next) { + fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); + if (fd == -1) { + continue; + } + while ((rv = connect(fd, rp->ai_addr, rp->ai_addrlen)) == -1 && + errno == EINTR) + ; + if (rv == 0) { + break; + } + close(fd); + fd = -1; + } + freeaddrinfo(res); + return fd; +} + +static void make_non_block(int fd) { + int flags, rv; + while ((flags = fcntl(fd, F_GETFL, 0)) == -1 && errno == EINTR) + ; + if (flags == -1) { + dief("fcntl", strerror(errno)); + } + while ((rv = fcntl(fd, F_SETFL, flags | O_NONBLOCK)) == -1 && errno == EINTR) + ; + if (rv == -1) { + dief("fcntl", strerror(errno)); + } +} + +static void set_tcp_nodelay(int fd) { + int val = 1; + int rv; + rv = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &val, (socklen_t)sizeof(val)); + if (rv == -1) { + dief("setsockopt", strerror(errno)); + } +} + +/* + * Update |pollfd| based on the state of |connection|. + */ +static void ctl_poll(struct pollfd *pollfd, struct Connection *connection) { + pollfd->events = 0; + if (nghttp2_session_want_read(connection->session) || + connection->want_io == WANT_READ) { + pollfd->events |= POLLIN; + } + if (nghttp2_session_want_write(connection->session) || + connection->want_io == WANT_WRITE) { + pollfd->events |= POLLOUT; + } +} + +/* + * Submits the request |req| to the connection |connection|. This + * function does not send packets; just append the request to the + * internal queue in |connection->session|. + */ +static void submit_request(struct Connection *connection, struct Request *req) { + int32_t stream_id; + /* Make sure that the last item is NULL */ + const nghttp2_nv nva[] = {MAKE_NV(":method", "GET"), + MAKE_NV_CS(":path", req->path), + MAKE_NV(":scheme", "https"), + MAKE_NV_CS(":authority", req->hostport), + MAKE_NV("accept", "*/*"), + MAKE_NV("user-agent", "nghttp2/" NGHTTP2_VERSION)}; + + stream_id = nghttp2_submit_request(connection->session, NULL, nva, + sizeof(nva) / sizeof(nva[0]), NULL, req); + + if (stream_id < 0) { + diec("nghttp2_submit_request", stream_id); + } + + req->stream_id = stream_id; + printf("[INFO] Stream ID = %d\n", stream_id); +} + +/* + * Performs the network I/O. + */ +static void exec_io(struct Connection *connection) { + int rv; + rv = nghttp2_session_recv(connection->session); + if (rv != 0) { + diec("nghttp2_session_recv", rv); + } + rv = nghttp2_session_send(connection->session); + if (rv != 0) { + diec("nghttp2_session_send", rv); + } +} + +static void request_init(struct Request *req, const struct URI *uri) { + req->host = strcopy(uri->host, uri->hostlen); + req->port = uri->port; + req->path = strcopy(uri->path, uri->pathlen); + req->hostport = strcopy(uri->hostport, uri->hostportlen); + req->stream_id = -1; +} + +static void request_free(struct Request *req) { + free(req->host); + free(req->path); + free(req->hostport); +} + +/* + * Fetches the resource denoted by |uri|. + */ +static void fetch_uri(const struct URI *uri) { + nghttp2_session_callbacks *callbacks; + int fd; + SSL_CTX *ssl_ctx; + SSL *ssl; + struct Request req; + struct Connection connection; + int rv; + nfds_t npollfds = 1; + struct pollfd pollfds[1]; + + request_init(&req, uri); + + /* Establish connection and setup SSL */ + fd = connect_to(req.host, req.port); + if (fd == -1) { + die("Could not open file descriptor"); + } + ssl_ctx = SSL_CTX_new(SSLv23_client_method()); + if (ssl_ctx == NULL) { + dief("SSL_CTX_new", ERR_error_string(ERR_get_error(), NULL)); + } + init_ssl_ctx(ssl_ctx); + ssl = SSL_new(ssl_ctx); + if (ssl == NULL) { + dief("SSL_new", ERR_error_string(ERR_get_error(), NULL)); + } + /* To simplify the program, we perform SSL/TLS handshake in blocking + I/O. */ + ssl_handshake(ssl, fd); + + connection.ssl = ssl; + connection.want_io = IO_NONE; + + /* Here make file descriptor non-block */ + make_non_block(fd); + set_tcp_nodelay(fd); + + printf("[INFO] SSL/TLS handshake completed\n"); + + rv = nghttp2_session_callbacks_new(&callbacks); + + if (rv != 0) { + diec("nghttp2_session_callbacks_new", rv); + } + + setup_nghttp2_callbacks(callbacks); + + rv = nghttp2_session_client_new(&connection.session, callbacks, &connection); + + nghttp2_session_callbacks_del(callbacks); + + if (rv != 0) { + diec("nghttp2_session_client_new", rv); + } + + nghttp2_submit_settings(connection.session, NGHTTP2_FLAG_NONE, NULL, 0); + + /* Submit the HTTP request to the outbound queue. */ + submit_request(&connection, &req); + + pollfds[0].fd = fd; + ctl_poll(pollfds, &connection); + + /* Event loop */ + while (nghttp2_session_want_read(connection.session) || + nghttp2_session_want_write(connection.session)) { + int nfds = poll(pollfds, npollfds, -1); + if (nfds == -1) { + dief("poll", strerror(errno)); + } + if (pollfds[0].revents & (POLLIN | POLLOUT)) { + exec_io(&connection); + } + if ((pollfds[0].revents & POLLHUP) || (pollfds[0].revents & POLLERR)) { + die("Connection error"); + } + ctl_poll(pollfds, &connection); + } + + /* Resource cleanup */ + nghttp2_session_del(connection.session); + SSL_shutdown(ssl); + SSL_free(ssl); + SSL_CTX_free(ssl_ctx); + shutdown(fd, SHUT_WR); + close(fd); + request_free(&req); +} + +static int parse_uri(struct URI *res, const char *uri) { + /* We only interested in https */ + size_t len, i, offset; + int ipv6addr = 0; + memset(res, 0, sizeof(struct URI)); + len = strlen(uri); + if (len < 9 || memcmp("https://", uri, 8) != 0) { + return -1; + } + offset = 8; + res->host = res->hostport = &uri[offset]; + res->hostlen = 0; + if (uri[offset] == '[') { + /* IPv6 literal address */ + ++offset; + ++res->host; + ipv6addr = 1; + for (i = offset; i < len; ++i) { + if (uri[i] == ']') { + res->hostlen = i - offset; + offset = i + 1; + break; + } + } + } else { + const char delims[] = ":/?#"; + for (i = offset; i < len; ++i) { + if (strchr(delims, uri[i]) != NULL) { + break; + } + } + res->hostlen = i - offset; + offset = i; + } + if (res->hostlen == 0) { + return -1; + } + /* Assuming https */ + res->port = 443; + if (offset < len) { + if (uri[offset] == ':') { + /* port */ + const char delims[] = "/?#"; + int port = 0; + ++offset; + for (i = offset; i < len; ++i) { + if (strchr(delims, uri[i]) != NULL) { + break; + } + if ('0' <= uri[i] && uri[i] <= '9') { + port *= 10; + port += uri[i] - '0'; + if (port > 65535) { + return -1; + } + } else { + return -1; + } + } + if (port == 0) { + return -1; + } + offset = i; + res->port = port; + } + } + res->hostportlen = uri + offset + ipv6addr - res->host; + for (i = offset; i < len; ++i) { + if (uri[i] == '#') { + break; + } + } + if (i - offset == 0) { + res->path = "/"; + res->pathlen = 1; + } else { + res->path = &uri[offset]; + res->pathlen = i - offset; + } + return 0; +} + +int main(int argc, char **argv) { + struct URI uri; + struct sigaction act; + int rv; + + if (argc < 2) { + die("Specify a https URI"); + } + + memset(&act, 0, sizeof(struct sigaction)); + act.sa_handler = SIG_IGN; + sigaction(SIGPIPE, &act, 0); + + OPENSSL_config(NULL); + OpenSSL_add_all_algorithms(); + SSL_load_error_strings(); + SSL_library_init(); + + rv = parse_uri(&uri, argv[1]); + if (rv != 0) { + die("parse_uri failed"); + } + fetch_uri(&uri); + return EXIT_SUCCESS; +} diff --git a/examples/deflate.c b/examples/deflate.c new file mode 100644 index 0000000..d0e7d3f --- /dev/null +++ b/examples/deflate.c @@ -0,0 +1,206 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2014 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifdef HAVE_CONFIG_H +#include +#endif /* !HAVE_CONFIG_H */ + +#include +#include + +#include + +#define MAKE_NV(K, V) \ + { \ + (uint8_t *) K, (uint8_t *)V, sizeof(K) - 1, sizeof(V) - 1, \ + NGHTTP2_NV_FLAG_NONE \ + } + +static void deflate(nghttp2_hd_deflater *deflater, + nghttp2_hd_inflater *inflater, const nghttp2_nv *const nva, + size_t nvlen); + +static int inflate_header_block(nghttp2_hd_inflater *inflater, uint8_t *in, + size_t inlen, int final); + +int main(int argc _U_, char **argv _U_) { + int rv; + nghttp2_hd_deflater *deflater; + nghttp2_hd_inflater *inflater; + /* Define 1st header set. This is looks like a HTTP request. */ + nghttp2_nv nva1[] = { + MAKE_NV(":scheme", "https"), MAKE_NV(":authority", "example.org"), + MAKE_NV(":path", "/"), MAKE_NV("user-agent", "libnghttp2"), + MAKE_NV("accept-encoding", "gzip, deflate")}; + /* Define 2nd header set */ + nghttp2_nv nva2[] = {MAKE_NV(":scheme", "https"), + MAKE_NV(":authority", "example.org"), + MAKE_NV(":path", "/stylesheet/style.css"), + MAKE_NV("user-agent", "libnghttp2"), + MAKE_NV("accept-encoding", "gzip, deflate"), + MAKE_NV("referer", "https://example.org")}; + + rv = nghttp2_hd_deflate_new(&deflater, 4096); + + if (rv != 0) { + fprintf(stderr, "nghttp2_hd_deflate_init failed with error: %s\n", + nghttp2_strerror(rv)); + exit(EXIT_FAILURE); + } + + rv = nghttp2_hd_inflate_new(&inflater); + + if (rv != 0) { + fprintf(stderr, "nghttp2_hd_inflate_init failed with error: %s\n", + nghttp2_strerror(rv)); + exit(EXIT_FAILURE); + } + + /* Encode and decode 1st header set */ + deflate(deflater, inflater, nva1, sizeof(nva1) / sizeof(nva1[0])); + + /* Encode and decode 2nd header set, using differential encoding + using state after encoding 1st header set. */ + deflate(deflater, inflater, nva2, sizeof(nva2) / sizeof(nva2[0])); + + nghttp2_hd_inflate_del(inflater); + nghttp2_hd_deflate_del(deflater); + + return 0; +} + +static void deflate(nghttp2_hd_deflater *deflater, + nghttp2_hd_inflater *inflater, const nghttp2_nv *const nva, + size_t nvlen) { + ssize_t rv; + uint8_t *buf; + size_t buflen; + size_t outlen; + size_t i; + size_t sum; + + sum = 0; + + for (i = 0; i < nvlen; ++i) { + sum += nva[i].namelen + nva[i].valuelen; + } + + printf("Input (%zu byte(s)):\n\n", sum); + + for (i = 0; i < nvlen; ++i) { + fwrite(nva[i].name, nva[i].namelen, 1, stdout); + printf(": "); + fwrite(nva[i].value, nva[i].valuelen, 1, stdout); + printf("\n"); + } + + buflen = nghttp2_hd_deflate_bound(deflater, nva, nvlen); + buf = malloc(buflen); + + rv = nghttp2_hd_deflate_hd(deflater, buf, buflen, nva, nvlen); + + if (rv < 0) { + fprintf(stderr, "nghttp2_hd_deflate_hd() failed with error: %s\n", + nghttp2_strerror((int)rv)); + + free(buf); + + exit(EXIT_FAILURE); + } + + outlen = rv; + + printf("\nDeflate (%zu byte(s), ratio %.02f):\n\n", outlen, + sum == 0 ? 0 : (double)outlen / sum); + + for (i = 0; i < outlen; ++i) { + if ((i & 0x0fu) == 0) { + printf("%08zX: ", i); + } + + printf("%02X ", buf[i]); + + if (((i + 1) & 0x0fu) == 0) { + printf("\n"); + } + } + + printf("\n\nInflate:\n\n"); + + /* We pass 1 to final parameter, because buf contains whole deflated + header data. */ + rv = inflate_header_block(inflater, buf, outlen, 1); + + if (rv != 0) { + free(buf); + + exit(EXIT_FAILURE); + } + + printf("\n-----------------------------------------------------------" + "--------------------\n"); + + free(buf); +} + +int inflate_header_block(nghttp2_hd_inflater *inflater, uint8_t *in, + size_t inlen, int final) { + ssize_t rv; + + for (;;) { + nghttp2_nv nv; + int inflate_flags = 0; + size_t proclen; + + rv = nghttp2_hd_inflate_hd(inflater, &nv, &inflate_flags, in, inlen, final); + + if (rv < 0) { + fprintf(stderr, "inflate failed with error code %zd", rv); + return -1; + } + + proclen = rv; + + in += proclen; + inlen -= proclen; + + if (inflate_flags & NGHTTP2_HD_INFLATE_EMIT) { + fwrite(nv.name, nv.namelen, 1, stderr); + fprintf(stderr, ": "); + fwrite(nv.value, nv.valuelen, 1, stderr); + fprintf(stderr, "\n"); + } + + if (inflate_flags & NGHTTP2_HD_INFLATE_FINAL) { + nghttp2_hd_inflate_end_headers(inflater); + break; + } + + if ((inflate_flags & NGHTTP2_HD_INFLATE_EMIT) == 0 && inlen == 0) { + break; + } + } + + return 0; +} diff --git a/examples/libevent-client.c b/examples/libevent-client.c new file mode 100644 index 0000000..17ed94b --- /dev/null +++ b/examples/libevent-client.c @@ -0,0 +1,561 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2013 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifdef HAVE_CONFIG_H +#include +#endif /* HAVE_CONFIG_H */ + +#include +#ifdef HAVE_UNISTD_H +#include +#endif /* HAVE_UNISTD_H */ +#ifdef HAVE_SYS_SOCKET_H +#include +#endif /* HAVE_SYS_SOCKET_H */ +#ifdef HAVE_NETINET_IN_H +#include +#endif /* HAVE_NETINET_IN_H */ +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include "http-parser/http_parser.h" + +#define ARRLEN(x) (sizeof(x) / sizeof(x[0])) + +typedef struct { + /* The NULL-terminated URI string to retrieve. */ + const char *uri; + /* Parsed result of the |uri| */ + struct http_parser_url *u; + /* The authority portion of the |uri|, not NULL-terminated */ + char *authority; + /* The path portion of the |uri|, including query, not + NULL-terminated */ + char *path; + /* The length of the |authority| */ + size_t authoritylen; + /* The length of the |path| */ + size_t pathlen; + /* The stream ID of this stream */ + int32_t stream_id; +} http2_stream_data; + +typedef struct { + nghttp2_session *session; + struct evdns_base *dnsbase; + struct bufferevent *bev; + http2_stream_data *stream_data; +} http2_session_data; + +static http2_stream_data *create_http2_stream_data(const char *uri, + struct http_parser_url *u) { + /* MAX 5 digits (max 65535) + 1 ':' + 1 NULL (because of snprintf) */ + size_t extra = 7; + http2_stream_data *stream_data = malloc(sizeof(http2_stream_data)); + + stream_data->uri = uri; + stream_data->u = u; + stream_data->stream_id = -1; + + stream_data->authoritylen = u->field_data[UF_HOST].len; + stream_data->authority = malloc(stream_data->authoritylen + extra); + memcpy(stream_data->authority, &uri[u->field_data[UF_HOST].off], + u->field_data[UF_HOST].len); + if (u->field_set & (1 << UF_PORT)) { + stream_data->authoritylen += + snprintf(stream_data->authority + u->field_data[UF_HOST].len, extra, + ":%u", u->port); + } + + stream_data->pathlen = 0; + if (u->field_set & (1 << UF_PATH)) { + stream_data->pathlen = u->field_data[UF_PATH].len; + } + if (u->field_set & (1 << UF_QUERY)) { + /* +1 for '?' character */ + stream_data->pathlen += u->field_data[UF_QUERY].len + 1; + } + if (stream_data->pathlen > 0) { + stream_data->path = malloc(stream_data->pathlen); + if (u->field_set & (1 << UF_PATH)) { + memcpy(stream_data->path, &uri[u->field_data[UF_PATH].off], + u->field_data[UF_PATH].len); + } + if (u->field_set & (1 << UF_QUERY)) { + memcpy(stream_data->path + u->field_data[UF_PATH].len + 1, + &uri[u->field_data[UF_QUERY].off], u->field_data[UF_QUERY].len); + } + } else { + stream_data->path = NULL; + } + return stream_data; +} + +static void delete_http2_stream_data(http2_stream_data *stream_data) { + free(stream_data->path); + free(stream_data->authority); + free(stream_data); +} + +/* Initializes |session_data| */ +static http2_session_data * +create_http2_session_data(struct event_base *evbase) { + http2_session_data *session_data = malloc(sizeof(http2_session_data)); + + memset(session_data, 0, sizeof(http2_session_data)); + session_data->dnsbase = evdns_base_new(evbase, 1); + return session_data; +} + +static void delete_http2_session_data(http2_session_data *session_data) { + SSL *ssl = bufferevent_openssl_get_ssl(session_data->bev); + + if (ssl) { + SSL_shutdown(ssl); + } + bufferevent_free(session_data->bev); + session_data->bev = NULL; + evdns_base_free(session_data->dnsbase, 1); + session_data->dnsbase = NULL; + nghttp2_session_del(session_data->session); + session_data->session = NULL; + if (session_data->stream_data) { + delete_http2_stream_data(session_data->stream_data); + session_data->stream_data = NULL; + } + free(session_data); +} + +static void print_header(FILE *f, const uint8_t *name, size_t namelen, + const uint8_t *value, size_t valuelen) { + fwrite(name, namelen, 1, f); + fprintf(f, ": "); + fwrite(value, valuelen, 1, f); + fprintf(f, "\n"); +} + +/* Print HTTP headers to |f|. Please note that this function does not + take into account that header name and value are sequence of + octets, therefore they may contain non-printable characters. */ +static void print_headers(FILE *f, nghttp2_nv *nva, size_t nvlen) { + size_t i; + for (i = 0; i < nvlen; ++i) { + print_header(f, nva[i].name, nva[i].namelen, nva[i].value, nva[i].valuelen); + } + fprintf(f, "\n"); +} + +/* nghttp2_send_callback. Here we transmit the |data|, |length| bytes, + to the network. Because we are using libevent bufferevent, we just + write those bytes into bufferevent buffer. */ +static ssize_t send_callback(nghttp2_session *session _U_, const uint8_t *data, + size_t length, int flags _U_, void *user_data) { + http2_session_data *session_data = (http2_session_data *)user_data; + struct bufferevent *bev = session_data->bev; + bufferevent_write(bev, data, length); + return length; +} + +/* nghttp2_on_header_callback: Called when nghttp2 library emits + single header name/value pair. */ +static int on_header_callback(nghttp2_session *session _U_, + const nghttp2_frame *frame, const uint8_t *name, + size_t namelen, const uint8_t *value, + size_t valuelen, uint8_t flags _U_, + void *user_data) { + http2_session_data *session_data = (http2_session_data *)user_data; + switch (frame->hd.type) { + case NGHTTP2_HEADERS: + if (frame->headers.cat == NGHTTP2_HCAT_RESPONSE && + session_data->stream_data->stream_id == frame->hd.stream_id) { + /* Print response headers for the initiated request. */ + print_header(stderr, name, namelen, value, valuelen); + break; + } + } + return 0; +} + +/* nghttp2_on_begin_headers_callback: Called when nghttp2 library gets + started to receive header block. */ +static int on_begin_headers_callback(nghttp2_session *session _U_, + const nghttp2_frame *frame, + void *user_data) { + http2_session_data *session_data = (http2_session_data *)user_data; + switch (frame->hd.type) { + case NGHTTP2_HEADERS: + if (frame->headers.cat == NGHTTP2_HCAT_RESPONSE && + session_data->stream_data->stream_id == frame->hd.stream_id) { + fprintf(stderr, "Response headers for stream ID=%d:\n", + frame->hd.stream_id); + } + break; + } + return 0; +} + +/* nghttp2_on_frame_recv_callback: Called when nghttp2 library + received a complete frame from the remote peer. */ +static int on_frame_recv_callback(nghttp2_session *session _U_, + const nghttp2_frame *frame, void *user_data) { + http2_session_data *session_data = (http2_session_data *)user_data; + switch (frame->hd.type) { + case NGHTTP2_HEADERS: + if (frame->headers.cat == NGHTTP2_HCAT_RESPONSE && + session_data->stream_data->stream_id == frame->hd.stream_id) { + fprintf(stderr, "All headers received\n"); + } + break; + } + return 0; +} + +/* nghttp2_on_data_chunk_recv_callback: Called when DATA frame is + received from the remote peer. In this implementation, if the frame + is meant to the stream we initiated, print the received data in + stdout, so that the user can redirect its output to the file + easily. */ +static int on_data_chunk_recv_callback(nghttp2_session *session _U_, + uint8_t flags _U_, int32_t stream_id, + const uint8_t *data, size_t len, + void *user_data) { + http2_session_data *session_data = (http2_session_data *)user_data; + if (session_data->stream_data->stream_id == stream_id) { + fwrite(data, len, 1, stdout); + } + return 0; +} + +/* nghttp2_on_stream_close_callback: Called when a stream is about to + closed. This example program only deals with 1 HTTP request (1 + stream), if it is closed, we send GOAWAY and tear down the + session */ +static int on_stream_close_callback(nghttp2_session *session, int32_t stream_id, + uint32_t error_code, void *user_data) { + http2_session_data *session_data = (http2_session_data *)user_data; + int rv; + + if (session_data->stream_data->stream_id == stream_id) { + fprintf(stderr, "Stream %d closed with error_code=%d\n", stream_id, + error_code); + rv = nghttp2_session_terminate_session(session, NGHTTP2_NO_ERROR); + if (rv != 0) { + return NGHTTP2_ERR_CALLBACK_FAILURE; + } + } + return 0; +} + +/* NPN TLS extension client callback. We check that server advertised + the HTTP/2 protocol the nghttp2 library supports. If not, exit + the program. */ +static int select_next_proto_cb(SSL *ssl _U_, unsigned char **out, + unsigned char *outlen, const unsigned char *in, + unsigned int inlen, void *arg _U_) { + if (nghttp2_select_next_protocol(out, outlen, in, inlen) <= 0) { + errx(1, "Server did not advertise " NGHTTP2_PROTO_VERSION_ID); + } + return SSL_TLSEXT_ERR_OK; +} + +/* Create SSL_CTX. */ +static SSL_CTX *create_ssl_ctx(void) { + SSL_CTX *ssl_ctx; + ssl_ctx = SSL_CTX_new(SSLv23_client_method()); + if (!ssl_ctx) { + errx(1, "Could not create SSL/TLS context: %s", + ERR_error_string(ERR_get_error(), NULL)); + } + SSL_CTX_set_options(ssl_ctx, + SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | + SSL_OP_NO_COMPRESSION | + SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION); + SSL_CTX_set_next_proto_select_cb(ssl_ctx, select_next_proto_cb, NULL); + return ssl_ctx; +} + +/* Create SSL object */ +static SSL *create_ssl(SSL_CTX *ssl_ctx) { + SSL *ssl; + ssl = SSL_new(ssl_ctx); + if (!ssl) { + errx(1, "Could not create SSL/TLS session object: %s", + ERR_error_string(ERR_get_error(), NULL)); + } + return ssl; +} + +static void initialize_nghttp2_session(http2_session_data *session_data) { + nghttp2_session_callbacks *callbacks; + + nghttp2_session_callbacks_new(&callbacks); + + nghttp2_session_callbacks_set_send_callback(callbacks, send_callback); + + nghttp2_session_callbacks_set_on_frame_recv_callback(callbacks, + on_frame_recv_callback); + + nghttp2_session_callbacks_set_on_data_chunk_recv_callback( + callbacks, on_data_chunk_recv_callback); + + nghttp2_session_callbacks_set_on_stream_close_callback( + callbacks, on_stream_close_callback); + + nghttp2_session_callbacks_set_on_header_callback(callbacks, + on_header_callback); + + nghttp2_session_callbacks_set_on_begin_headers_callback( + callbacks, on_begin_headers_callback); + + nghttp2_session_client_new(&session_data->session, callbacks, session_data); + + nghttp2_session_callbacks_del(callbacks); +} + +static void send_client_connection_header(http2_session_data *session_data) { + nghttp2_settings_entry iv[1] = { + {NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100}}; + int rv; + + /* client 24 bytes magic string will be sent by nghttp2 library */ + rv = nghttp2_submit_settings(session_data->session, NGHTTP2_FLAG_NONE, iv, + ARRLEN(iv)); + if (rv != 0) { + errx(1, "Could not submit SETTINGS: %s", nghttp2_strerror(rv)); + } +} + +#define MAKE_NV(NAME, VALUE, VALUELEN) \ + { \ + (uint8_t *) NAME, (uint8_t *)VALUE, sizeof(NAME) - 1, VALUELEN, \ + NGHTTP2_NV_FLAG_NONE \ + } + +#define MAKE_NV2(NAME, VALUE) \ + { \ + (uint8_t *) NAME, (uint8_t *)VALUE, sizeof(NAME) - 1, sizeof(VALUE) - 1, \ + NGHTTP2_NV_FLAG_NONE \ + } + +/* Send HTTP request to the remote peer */ +static void submit_request(http2_session_data *session_data) { + int32_t stream_id; + http2_stream_data *stream_data = session_data->stream_data; + const char *uri = stream_data->uri; + const struct http_parser_url *u = stream_data->u; + nghttp2_nv hdrs[] = { + MAKE_NV2(":method", "GET"), + MAKE_NV(":scheme", &uri[u->field_data[UF_SCHEMA].off], + u->field_data[UF_SCHEMA].len), + MAKE_NV(":authority", stream_data->authority, stream_data->authoritylen), + MAKE_NV(":path", stream_data->path, stream_data->pathlen)}; + fprintf(stderr, "Request headers:\n"); + print_headers(stderr, hdrs, ARRLEN(hdrs)); + stream_id = nghttp2_submit_request(session_data->session, NULL, hdrs, + ARRLEN(hdrs), NULL, stream_data); + if (stream_id < 0) { + errx(1, "Could not submit HTTP request: %s", nghttp2_strerror(stream_id)); + } + + stream_data->stream_id = stream_id; +} + +/* Serialize the frame and send (or buffer) the data to + bufferevent. */ +static int session_send(http2_session_data *session_data) { + int rv; + + rv = nghttp2_session_send(session_data->session); + if (rv != 0) { + warnx("Fatal error: %s", nghttp2_strerror(rv)); + return -1; + } + return 0; +} + +/* readcb for bufferevent. Here we get the data from the input buffer + of bufferevent and feed them to nghttp2 library. This may invoke + nghttp2 callbacks. It may also queues the frame in nghttp2 session + context. To send them, we call session_send() in the end. */ +static void readcb(struct bufferevent *bev, void *ptr) { + http2_session_data *session_data = (http2_session_data *)ptr; + ssize_t readlen; + struct evbuffer *input = bufferevent_get_input(bev); + size_t datalen = evbuffer_get_length(input); + unsigned char *data = evbuffer_pullup(input, -1); + + readlen = nghttp2_session_mem_recv(session_data->session, data, datalen); + if (readlen < 0) { + warnx("Fatal error: %s", nghttp2_strerror((int)readlen)); + delete_http2_session_data(session_data); + return; + } + if (evbuffer_drain(input, readlen) != 0) { + warnx("Fatal error: evbuffer_drain failed"); + delete_http2_session_data(session_data); + return; + } + if (session_send(session_data) != 0) { + delete_http2_session_data(session_data); + return; + } +} + +/* writecb for bufferevent. To greaceful shutdown after sending or + receiving GOAWAY, we check the some conditions on the nghttp2 + library and output buffer of bufferevent. If it indicates we have + no business to this session, tear down the connection. */ +static void writecb(struct bufferevent *bev _U_, void *ptr) { + http2_session_data *session_data = (http2_session_data *)ptr; + if (nghttp2_session_want_read(session_data->session) == 0 && + nghttp2_session_want_write(session_data->session) == 0 && + evbuffer_get_length(bufferevent_get_output(session_data->bev)) == 0) { + delete_http2_session_data(session_data); + } +} + +/* eventcb for bufferevent. For the purpose of simplicity and + readability of the example program, we omitted the certificate and + peer verification. After SSL/TLS handshake is over, initialize + nghttp2 library session, and send client connection header. Then + send HTTP request. */ +static void eventcb(struct bufferevent *bev, short events, void *ptr) { + http2_session_data *session_data = (http2_session_data *)ptr; + if (events & BEV_EVENT_CONNECTED) { + int fd = bufferevent_getfd(bev); + int val = 1; + fprintf(stderr, "Connected\n"); + setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char *)&val, sizeof(val)); + initialize_nghttp2_session(session_data); + send_client_connection_header(session_data); + submit_request(session_data); + if (session_send(session_data) != 0) { + delete_http2_session_data(session_data); + } + return; + } + if (events & BEV_EVENT_EOF) { + warnx("Disconnected from the remote host"); + } else if (events & BEV_EVENT_ERROR) { + warnx("Network error"); + } else if (events & BEV_EVENT_TIMEOUT) { + warnx("Timeout"); + } + delete_http2_session_data(session_data); +} + +/* Start connecting to the remote peer |host:port| */ +static void initiate_connection(struct event_base *evbase, SSL_CTX *ssl_ctx, + const char *host, uint16_t port, + http2_session_data *session_data) { + int rv; + struct bufferevent *bev; + SSL *ssl; + + ssl = create_ssl(ssl_ctx); + bev = bufferevent_openssl_socket_new( + evbase, -1, ssl, BUFFEREVENT_SSL_CONNECTING, + BEV_OPT_DEFER_CALLBACKS | BEV_OPT_CLOSE_ON_FREE); + bufferevent_setcb(bev, readcb, writecb, eventcb, session_data); + rv = bufferevent_socket_connect_hostname(bev, session_data->dnsbase, + AF_UNSPEC, host, port); + + if (rv != 0) { + errx(1, "Could not connect to the remote host %s", host); + } + session_data->bev = bev; +} + +/* Get resource denoted by the |uri|. The debug and error messages are + printed in stderr, while the response body is printed in stdout. */ +static void run(const char *uri) { + struct http_parser_url u; + char *host; + uint16_t port; + int rv; + SSL_CTX *ssl_ctx; + struct event_base *evbase; + http2_session_data *session_data; + + /* Parse the |uri| and stores its components in |u| */ + rv = http_parser_parse_url(uri, strlen(uri), 0, &u); + if (rv != 0) { + errx(1, "Could not parse URI %s", uri); + } + host = strndup(&uri[u.field_data[UF_HOST].off], u.field_data[UF_HOST].len); + if (!(u.field_set & (1 << UF_PORT))) { + port = 443; + } else { + port = u.port; + } + + ssl_ctx = create_ssl_ctx(); + + evbase = event_base_new(); + + session_data = create_http2_session_data(evbase); + session_data->stream_data = create_http2_stream_data(uri, &u); + + initiate_connection(evbase, ssl_ctx, host, port, session_data); + free(host); + host = NULL; + + event_base_loop(evbase, 0); + + event_base_free(evbase); + SSL_CTX_free(ssl_ctx); +} + +int main(int argc, char **argv) { + struct sigaction act; + + if (argc < 2) { + fprintf(stderr, "Usage: libevent-client HTTPS_URI\n"); + exit(EXIT_FAILURE); + } + + memset(&act, 0, sizeof(struct sigaction)); + act.sa_handler = SIG_IGN; + sigaction(SIGPIPE, &act, NULL); + + OPENSSL_config(NULL); + OpenSSL_add_all_algorithms(); + SSL_load_error_strings(); + SSL_library_init(); + + run(argv[1]); + return 0; +} diff --git a/examples/libevent-server.c b/examples/libevent-server.c new file mode 100644 index 0000000..412c6d0 --- /dev/null +++ b/examples/libevent-server.c @@ -0,0 +1,734 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2013 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifdef HAVE_CONFIG_H +#include +#endif /* HAVE_CONFIG_H */ + +#include +#ifdef HAVE_SYS_SOCKET_H +#include +#endif /* HAVE_SYS_SOCKET_H */ +#ifdef HAVE_NETDB_H +#include +#endif /* HAVE_NETDB_H */ +#include +#ifdef HAVE_UNISTD_H +#include +#endif /* HAVE_UNISTD_H */ +#include +#ifdef HAVE_FCNTL_H +#include +#endif /* HAVE_FCNTL_H */ +#include +#ifdef HAVE_NETINET_IN_H +#include +#endif /* HAVE_NETINET_IN_H */ +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include + +#define OUTPUT_WOULDBLOCK_THRESHOLD (1 << 16) + +#define ARRLEN(x) (sizeof(x) / sizeof(x[0])) + +#define MAKE_NV(NAME, VALUE) \ + { \ + (uint8_t *) NAME, (uint8_t *)VALUE, sizeof(NAME) - 1, sizeof(VALUE) - 1, \ + NGHTTP2_NV_FLAG_NONE \ + } + +struct app_context; +typedef struct app_context app_context; + +typedef struct http2_stream_data { + struct http2_stream_data *prev, *next; + char *request_path; + int32_t stream_id; + int fd; +} http2_stream_data; + +typedef struct http2_session_data { + struct http2_stream_data root; + struct bufferevent *bev; + app_context *app_ctx; + nghttp2_session *session; + char *client_addr; +} http2_session_data; + +struct app_context { + SSL_CTX *ssl_ctx; + struct event_base *evbase; +}; + +static unsigned char next_proto_list[256]; +static size_t next_proto_list_len; + +static int next_proto_cb(SSL *s _U_, const unsigned char **data, + unsigned int *len, void *arg _U_) { + *data = next_proto_list; + *len = (unsigned int)next_proto_list_len; + return SSL_TLSEXT_ERR_OK; +} + +/* Create SSL_CTX. */ +static SSL_CTX *create_ssl_ctx(const char *key_file, const char *cert_file) { + SSL_CTX *ssl_ctx; + EC_KEY *ecdh; + + ssl_ctx = SSL_CTX_new(SSLv23_server_method()); + if (!ssl_ctx) { + errx(1, "Could not create SSL/TLS context: %s", + ERR_error_string(ERR_get_error(), NULL)); + } + SSL_CTX_set_options(ssl_ctx, + SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | + SSL_OP_NO_COMPRESSION | + SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION); + + ecdh = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1); + if (!ecdh) { + errx(1, "EC_KEY_new_by_curv_name failed: %s", + ERR_error_string(ERR_get_error(), NULL)); + } + SSL_CTX_set_tmp_ecdh(ssl_ctx, ecdh); + EC_KEY_free(ecdh); + + if (SSL_CTX_use_PrivateKey_file(ssl_ctx, key_file, SSL_FILETYPE_PEM) != 1) { + errx(1, "Could not read private key file %s", key_file); + } + if (SSL_CTX_use_certificate_chain_file(ssl_ctx, cert_file) != 1) { + errx(1, "Could not read certificate file %s", cert_file); + } + + next_proto_list[0] = NGHTTP2_PROTO_VERSION_ID_LEN; + memcpy(&next_proto_list[1], NGHTTP2_PROTO_VERSION_ID, + NGHTTP2_PROTO_VERSION_ID_LEN); + next_proto_list_len = 1 + NGHTTP2_PROTO_VERSION_ID_LEN; + + SSL_CTX_set_next_protos_advertised_cb(ssl_ctx, next_proto_cb, NULL); + return ssl_ctx; +} + +/* Create SSL object */ +static SSL *create_ssl(SSL_CTX *ssl_ctx) { + SSL *ssl; + ssl = SSL_new(ssl_ctx); + if (!ssl) { + errx(1, "Could not create SSL/TLS session object: %s", + ERR_error_string(ERR_get_error(), NULL)); + } + return ssl; +} + +static void add_stream(http2_session_data *session_data, + http2_stream_data *stream_data) { + stream_data->next = session_data->root.next; + session_data->root.next = stream_data; + stream_data->prev = &session_data->root; + if (stream_data->next) { + stream_data->next->prev = stream_data; + } +} + +static void remove_stream(http2_session_data *session_data _U_, + http2_stream_data *stream_data) { + stream_data->prev->next = stream_data->next; + if (stream_data->next) { + stream_data->next->prev = stream_data->prev; + } +} + +static http2_stream_data * +create_http2_stream_data(http2_session_data *session_data, int32_t stream_id) { + http2_stream_data *stream_data; + stream_data = malloc(sizeof(http2_stream_data)); + memset(stream_data, 0, sizeof(http2_stream_data)); + stream_data->stream_id = stream_id; + stream_data->fd = -1; + + add_stream(session_data, stream_data); + return stream_data; +} + +static void delete_http2_stream_data(http2_stream_data *stream_data) { + if (stream_data->fd != -1) { + close(stream_data->fd); + } + free(stream_data->request_path); + free(stream_data); +} + +static http2_session_data *create_http2_session_data(app_context *app_ctx, + int fd, + struct sockaddr *addr, + int addrlen) { + int rv; + http2_session_data *session_data; + SSL *ssl; + char host[NI_MAXHOST]; + int val = 1; + + ssl = create_ssl(app_ctx->ssl_ctx); + session_data = malloc(sizeof(http2_session_data)); + memset(session_data, 0, sizeof(http2_session_data)); + session_data->app_ctx = app_ctx; + setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char *)&val, sizeof(val)); + session_data->bev = bufferevent_openssl_socket_new( + app_ctx->evbase, fd, ssl, BUFFEREVENT_SSL_ACCEPTING, + BEV_OPT_CLOSE_ON_FREE | BEV_OPT_DEFER_CALLBACKS); + rv = getnameinfo(addr, addrlen, host, sizeof(host), NULL, 0, NI_NUMERICHOST); + if (rv != 0) { + session_data->client_addr = strdup("(unknown)"); + } else { + session_data->client_addr = strdup(host); + } + + return session_data; +} + +static void delete_http2_session_data(http2_session_data *session_data) { + http2_stream_data *stream_data; + SSL *ssl = bufferevent_openssl_get_ssl(session_data->bev); + fprintf(stderr, "%s disconnected\n", session_data->client_addr); + if (ssl) { + SSL_shutdown(ssl); + } + bufferevent_free(session_data->bev); + nghttp2_session_del(session_data->session); + for (stream_data = session_data->root.next; stream_data;) { + http2_stream_data *next = stream_data->next; + delete_http2_stream_data(stream_data); + stream_data = next; + } + free(session_data->client_addr); + free(session_data); +} + +/* Serialize the frame and send (or buffer) the data to + bufferevent. */ +static int session_send(http2_session_data *session_data) { + int rv; + rv = nghttp2_session_send(session_data->session); + if (rv != 0) { + warnx("Fatal error: %s", nghttp2_strerror(rv)); + return -1; + } + return 0; +} + +/* Read the data in the bufferevent and feed them into nghttp2 library + function. Invocation of nghttp2_session_mem_recv() may make + additional pending frames, so call session_send() at the end of the + function. */ +static int session_recv(http2_session_data *session_data) { + ssize_t readlen; + struct evbuffer *input = bufferevent_get_input(session_data->bev); + size_t datalen = evbuffer_get_length(input); + unsigned char *data = evbuffer_pullup(input, -1); + + readlen = nghttp2_session_mem_recv(session_data->session, data, datalen); + if (readlen < 0) { + warnx("Fatal error: %s", nghttp2_strerror((int)readlen)); + return -1; + } + if (evbuffer_drain(input, readlen) != 0) { + warnx("Fatal error: evbuffer_drain failed"); + return -1; + } + if (session_send(session_data) != 0) { + return -1; + } + return 0; +} + +static ssize_t send_callback(nghttp2_session *session _U_, const uint8_t *data, + size_t length, int flags _U_, void *user_data) { + http2_session_data *session_data = (http2_session_data *)user_data; + struct bufferevent *bev = session_data->bev; + /* Avoid excessive buffering in server side. */ + if (evbuffer_get_length(bufferevent_get_output(session_data->bev)) >= + OUTPUT_WOULDBLOCK_THRESHOLD) { + return NGHTTP2_ERR_WOULDBLOCK; + } + bufferevent_write(bev, data, length); + return length; +} + +/* Returns nonzero if the string |s| ends with the substring |sub| */ +static int ends_with(const char *s, const char *sub) { + size_t slen = strlen(s); + size_t sublen = strlen(sub); + if (slen < sublen) { + return 0; + } + return memcmp(s + slen - sublen, sub, sublen) == 0; +} + +/* Returns int value of hex string character |c| */ +static uint8_t hex_to_uint(uint8_t c) { + if ('0' <= c && c <= '9') { + return c - '0'; + } + if ('A' <= c && c <= 'F') { + return c - 'A' + 10; + } + if ('a' <= c && c <= 'f') { + return c - 'a' + 10; + } + return 0; +} + +/* Decodes percent-encoded byte string |value| with length |valuelen| + and returns the decoded byte string in allocated buffer. The return + value is NULL terminated. The caller must free the returned + string. */ +static char *percent_decode(const uint8_t *value, size_t valuelen) { + char *res; + + res = malloc(valuelen + 1); + if (valuelen > 3) { + size_t i, j; + for (i = 0, j = 0; i < valuelen - 2;) { + if (value[i] != '%' || !isxdigit(value[i + 1]) || + !isxdigit(value[i + 2])) { + res[j++] = value[i++]; + continue; + } + res[j++] = (hex_to_uint(value[i + 1]) << 4) + hex_to_uint(value[i + 2]); + i += 3; + } + memcpy(&res[j], &value[i], 2); + res[j + 2] = '\0'; + } else { + memcpy(res, value, valuelen); + res[valuelen] = '\0'; + } + return res; +} + +static ssize_t file_read_callback(nghttp2_session *session _U_, + int32_t stream_id _U_, uint8_t *buf, + size_t length, uint32_t *data_flags, + nghttp2_data_source *source, + void *user_data _U_) { + int fd = source->fd; + ssize_t r; + while ((r = read(fd, buf, length)) == -1 && errno == EINTR) + ; + if (r == -1) { + return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; + } + if (r == 0) { + *data_flags |= NGHTTP2_DATA_FLAG_EOF; + } + return r; +} + +static int send_response(nghttp2_session *session, int32_t stream_id, + nghttp2_nv *nva, size_t nvlen, int fd) { + int rv; + nghttp2_data_provider data_prd; + data_prd.source.fd = fd; + data_prd.read_callback = file_read_callback; + + rv = nghttp2_submit_response(session, stream_id, nva, nvlen, &data_prd); + if (rv != 0) { + warnx("Fatal error: %s", nghttp2_strerror(rv)); + return -1; + } + return 0; +} + +const char ERROR_HTML[] = "404" + "

    404 Not Found

    "; + +static int error_reply(nghttp2_session *session, + http2_stream_data *stream_data) { + int rv; + ssize_t writelen; + int pipefd[2]; + nghttp2_nv hdrs[] = {MAKE_NV(":status", "404")}; + + rv = pipe(pipefd); + if (rv != 0) { + warn("Could not create pipe"); + rv = nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, + stream_data->stream_id, + NGHTTP2_INTERNAL_ERROR); + if (rv != 0) { + warnx("Fatal error: %s", nghttp2_strerror(rv)); + return -1; + } + return 0; + } + + writelen = write(pipefd[1], ERROR_HTML, sizeof(ERROR_HTML) - 1); + close(pipefd[1]); + + if (writelen != sizeof(ERROR_HTML) - 1) { + close(pipefd[0]); + return -1; + } + + stream_data->fd = pipefd[0]; + + if (send_response(session, stream_data->stream_id, hdrs, ARRLEN(hdrs), + pipefd[0]) != 0) { + close(pipefd[0]); + return -1; + } + return 0; +} + +/* nghttp2_on_header_callback: Called when nghttp2 library emits + single header name/value pair. */ +static int on_header_callback(nghttp2_session *session, + const nghttp2_frame *frame, const uint8_t *name, + size_t namelen, const uint8_t *value, + size_t valuelen, uint8_t flags _U_, + void *user_data _U_) { + http2_stream_data *stream_data; + const char PATH[] = ":path"; + switch (frame->hd.type) { + case NGHTTP2_HEADERS: + if (frame->headers.cat != NGHTTP2_HCAT_REQUEST) { + break; + } + stream_data = + nghttp2_session_get_stream_user_data(session, frame->hd.stream_id); + if (!stream_data || stream_data->request_path) { + break; + } + if (namelen == sizeof(PATH) - 1 && memcmp(PATH, name, namelen) == 0) { + size_t j; + for (j = 0; j < valuelen && value[j] != '?'; ++j) + ; + stream_data->request_path = percent_decode(value, j); + } + break; + } + return 0; +} + +static int on_begin_headers_callback(nghttp2_session *session, + const nghttp2_frame *frame, + void *user_data) { + http2_session_data *session_data = (http2_session_data *)user_data; + http2_stream_data *stream_data; + + if (frame->hd.type != NGHTTP2_HEADERS || + frame->headers.cat != NGHTTP2_HCAT_REQUEST) { + return 0; + } + stream_data = create_http2_stream_data(session_data, frame->hd.stream_id); + nghttp2_session_set_stream_user_data(session, frame->hd.stream_id, + stream_data); + return 0; +} + +/* Minimum check for directory traversal. Returns nonzero if it is + safe. */ +static int check_path(const char *path) { + /* We don't like '\' in url. */ + return path[0] && path[0] == '/' && strchr(path, '\\') == NULL && + strstr(path, "/../") == NULL && strstr(path, "/./") == NULL && + !ends_with(path, "/..") && !ends_with(path, "/."); +} + +static int on_request_recv(nghttp2_session *session, + http2_session_data *session_data, + http2_stream_data *stream_data) { + int fd; + nghttp2_nv hdrs[] = {MAKE_NV(":status", "200")}; + char *rel_path; + + if (!stream_data->request_path) { + if (error_reply(session, stream_data) != 0) { + return NGHTTP2_ERR_CALLBACK_FAILURE; + } + return 0; + } + fprintf(stderr, "%s GET %s\n", session_data->client_addr, + stream_data->request_path); + if (!check_path(stream_data->request_path)) { + if (error_reply(session, stream_data) != 0) { + return NGHTTP2_ERR_CALLBACK_FAILURE; + } + return 0; + } + for (rel_path = stream_data->request_path; *rel_path == '/'; ++rel_path) + ; + fd = open(rel_path, O_RDONLY); + if (fd == -1) { + if (error_reply(session, stream_data) != 0) { + return NGHTTP2_ERR_CALLBACK_FAILURE; + } + return 0; + } + stream_data->fd = fd; + + if (send_response(session, stream_data->stream_id, hdrs, ARRLEN(hdrs), fd) != + 0) { + close(fd); + return NGHTTP2_ERR_CALLBACK_FAILURE; + } + return 0; +} + +static int on_frame_recv_callback(nghttp2_session *session, + const nghttp2_frame *frame, void *user_data) { + http2_session_data *session_data = (http2_session_data *)user_data; + http2_stream_data *stream_data; + switch (frame->hd.type) { + case NGHTTP2_DATA: + case NGHTTP2_HEADERS: + /* Check that the client request has finished */ + if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) { + stream_data = + nghttp2_session_get_stream_user_data(session, frame->hd.stream_id); + /* For DATA and HEADERS frame, this callback may be called after + on_stream_close_callback. Check that stream still alive. */ + if (!stream_data) { + return 0; + } + return on_request_recv(session, session_data, stream_data); + } + break; + default: + break; + } + return 0; +} + +static int on_stream_close_callback(nghttp2_session *session, int32_t stream_id, + uint32_t error_code _U_, void *user_data) { + http2_session_data *session_data = (http2_session_data *)user_data; + http2_stream_data *stream_data; + + stream_data = nghttp2_session_get_stream_user_data(session, stream_id); + if (!stream_data) { + return 0; + } + remove_stream(session_data, stream_data); + delete_http2_stream_data(stream_data); + return 0; +} + +static void initialize_nghttp2_session(http2_session_data *session_data) { + nghttp2_session_callbacks *callbacks; + + nghttp2_session_callbacks_new(&callbacks); + + nghttp2_session_callbacks_set_send_callback(callbacks, send_callback); + + nghttp2_session_callbacks_set_on_frame_recv_callback(callbacks, + on_frame_recv_callback); + + nghttp2_session_callbacks_set_on_stream_close_callback( + callbacks, on_stream_close_callback); + + nghttp2_session_callbacks_set_on_header_callback(callbacks, + on_header_callback); + + nghttp2_session_callbacks_set_on_begin_headers_callback( + callbacks, on_begin_headers_callback); + + nghttp2_session_server_new(&session_data->session, callbacks, session_data); + + nghttp2_session_callbacks_del(callbacks); +} + +/* Send HTTP/2 client connection header, which includes 24 bytes + magic octets and SETTINGS frame */ +static int send_server_connection_header(http2_session_data *session_data) { + nghttp2_settings_entry iv[1] = { + {NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100}}; + int rv; + + rv = nghttp2_submit_settings(session_data->session, NGHTTP2_FLAG_NONE, iv, + ARRLEN(iv)); + if (rv != 0) { + warnx("Fatal error: %s", nghttp2_strerror(rv)); + return -1; + } + return 0; +} + +/* readcb for bufferevent after client connection header was + checked. */ +static void readcb(struct bufferevent *bev _U_, void *ptr) { + http2_session_data *session_data = (http2_session_data *)ptr; + if (session_recv(session_data) != 0) { + delete_http2_session_data(session_data); + return; + } +} + +/* writecb for bufferevent. To greaceful shutdown after sending or + receiving GOAWAY, we check the some conditions on the nghttp2 + library and output buffer of bufferevent. If it indicates we have + no business to this session, tear down the connection. If the + connection is not going to shutdown, we call session_send() to + process pending data in the output buffer. This is necessary + because we have a threshold on the buffer size to avoid too much + buffering. See send_callback(). */ +static void writecb(struct bufferevent *bev, void *ptr) { + http2_session_data *session_data = (http2_session_data *)ptr; + if (evbuffer_get_length(bufferevent_get_output(bev)) > 0) { + return; + } + if (nghttp2_session_want_read(session_data->session) == 0 && + nghttp2_session_want_write(session_data->session) == 0) { + delete_http2_session_data(session_data); + return; + } + if (session_send(session_data) != 0) { + delete_http2_session_data(session_data); + return; + } +} + +/* eventcb for bufferevent */ +static void eventcb(struct bufferevent *bev _U_, short events, void *ptr) { + http2_session_data *session_data = (http2_session_data *)ptr; + if (events & BEV_EVENT_CONNECTED) { + fprintf(stderr, "%s connected\n", session_data->client_addr); + + initialize_nghttp2_session(session_data); + + if (send_server_connection_header(session_data) != 0) { + delete_http2_session_data(session_data); + return; + } + + return; + } + if (events & BEV_EVENT_EOF) { + fprintf(stderr, "%s EOF\n", session_data->client_addr); + } else if (events & BEV_EVENT_ERROR) { + fprintf(stderr, "%s network error\n", session_data->client_addr); + } else if (events & BEV_EVENT_TIMEOUT) { + fprintf(stderr, "%s timeout\n", session_data->client_addr); + } + delete_http2_session_data(session_data); +} + +/* callback for evconnlistener */ +static void acceptcb(struct evconnlistener *listener _U_, int fd, + struct sockaddr *addr, int addrlen, void *arg) { + app_context *app_ctx = (app_context *)arg; + http2_session_data *session_data; + + session_data = create_http2_session_data(app_ctx, fd, addr, addrlen); + + bufferevent_setcb(session_data->bev, readcb, writecb, eventcb, session_data); +} + +static void start_listen(struct event_base *evbase, const char *service, + app_context *app_ctx) { + int rv; + struct addrinfo hints; + struct addrinfo *res, *rp; + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_PASSIVE; +#ifdef AI_ADDRCONFIG + hints.ai_flags |= AI_ADDRCONFIG; +#endif /* AI_ADDRCONFIG */ + + rv = getaddrinfo(NULL, service, &hints, &res); + if (rv != 0) { + errx(1, NULL); + } + for (rp = res; rp; rp = rp->ai_next) { + struct evconnlistener *listener; + listener = evconnlistener_new_bind( + evbase, acceptcb, app_ctx, LEV_OPT_CLOSE_ON_FREE | LEV_OPT_REUSEABLE, + 16, rp->ai_addr, rp->ai_addrlen); + if (listener) { + freeaddrinfo(res); + + return; + } + } + errx(1, "Could not start listener"); +} + +static void initialize_app_context(app_context *app_ctx, SSL_CTX *ssl_ctx, + struct event_base *evbase) { + memset(app_ctx, 0, sizeof(app_context)); + app_ctx->ssl_ctx = ssl_ctx; + app_ctx->evbase = evbase; +} + +static void run(const char *service, const char *key_file, + const char *cert_file) { + SSL_CTX *ssl_ctx; + app_context app_ctx; + struct event_base *evbase; + + ssl_ctx = create_ssl_ctx(key_file, cert_file); + evbase = event_base_new(); + initialize_app_context(&app_ctx, ssl_ctx, evbase); + start_listen(evbase, service, &app_ctx); + + event_base_loop(evbase, 0); + + event_base_free(evbase); + SSL_CTX_free(ssl_ctx); +} + +int main(int argc, char **argv) { + struct sigaction act; + + if (argc < 4) { + fprintf(stderr, "Usage: libevent-server PORT KEY_FILE CERT_FILE\n"); + exit(EXIT_FAILURE); + } + + memset(&act, 0, sizeof(struct sigaction)); + act.sa_handler = SIG_IGN; + sigaction(SIGPIPE, &act, NULL); + + OPENSSL_config(NULL); + OpenSSL_add_all_algorithms(); + SSL_load_error_strings(); + SSL_library_init(); + + run(argv[1], argv[2], argv[3]); + return 0; +} diff --git a/examples/tiny-nghttpd.c b/examples/tiny-nghttpd.c new file mode 100644 index 0000000..3410e9d --- /dev/null +++ b/examples/tiny-nghttpd.c @@ -0,0 +1,1342 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2014 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +/* + * This program is intended to measure library performance, avoiding + * overhead of underlying I/O library (e.g., libevent, Boost ASIO). + */ +#define _GNU_SOURCE + +#ifdef HAVE_CONFIG_H +#include +#endif /* HAVE_CONFIG_H */ + +#include +#ifdef HAVE_SYS_SOCKET_H +#include +#endif /* HAVE_SYS_SOCKET_H */ +#include +#ifdef HAVE_FCNTL_H +#include +#endif /* HAVE_FCNTL_H */ +#ifdef HAVE_NETDB_H +#include +#endif /* HAVE_NETDB_H */ +#ifdef HAVE_NETINET_IN_H +#include +#endif /* HAVE_NETINET_IN_H */ +#include +#ifdef HAVE_UNISTD_H +#include +#endif /* HAVE_UNISTD_H */ +#include +#ifdef HAVE_TIME_H +#include +#endif /* HAVE_TIME_H */ +#include +#include +#include +#include +#include +#include +#include + +#include + +#define SERVER_NAME "tiny-nghttpd nghttp2/" NGHTTP2_VERSION + +#define MAKE_NV(name, value) \ + { \ + (uint8_t *)(name), (uint8_t *)(value), sizeof((name)) - 1, \ + sizeof((value)) - 1, NGHTTP2_NV_FLAG_NONE \ + } + +#define MAKE_NV2(name, value, valuelen) \ + { \ + (uint8_t *)(name), (uint8_t *)(value), sizeof((name)) - 1, (valuelen), \ + NGHTTP2_NV_FLAG_NONE \ + } + +#define array_size(a) (sizeof((a)) / sizeof((a)[0])) + +/* Returns the length of remaning data in buffer */ +#define io_buf_len(iobuf) ((iobuf)->last - (iobuf)->pos) +/* Returns the space buffer can still accept */ +#define io_buf_left(iobuf) ((iobuf)->end - (iobuf)->last) + +typedef struct { + /* beginning of buffer */ + uint8_t *begin; + /* one byte beyond the end of buffer */ + uint8_t *end; + /* next read/write position of buffer */ + uint8_t *pos; + /* one byte beyond last data of buffer */ + uint8_t *last; +} io_buf; + +typedef struct { + /* epoll fd */ + int epfd; +} io_loop; + +typedef struct stream { + struct stream *prev, *next; + /* mandatory header fields */ + char *method; + char *scheme; + char *authority; + char *path; + char *host; + /* region of response body in rawscrbuf */ + uint8_t *res_begin, *res_end; + /* io_buf wrapping rawscrbuf */ + io_buf scrbuf; + int64_t fileleft; + /* length of mandatory header fields */ + size_t methodlen; + size_t schemelen; + size_t authoritylen; + size_t pathlen; + size_t hostlen; + /* stream ID of this stream */ + int32_t stream_id; + /* fd for reading file */ + int filefd; + /* scratch buffer for this stream */ + uint8_t rawscrbuf[4096]; +} stream; + +typedef struct { int (*handler)(io_loop *, uint32_t, void *); } evhandle; + +typedef struct { + evhandle evhn; + nghttp2_session *session; + /* list of stream */ + stream strm_head; + /* pending library output */ + const uint8_t *cache; + /* io_buf wrapping rawoutbuf */ + io_buf buf; + /* length of cache */ + size_t cachelen; + /* client fd */ + int fd; + /* output buffer */ + uint8_t rawoutbuf[65536]; +} connection; + +typedef struct { + evhandle evhn; + /* listening fd */ + int fd; +} server; + +typedef struct { + evhandle evhn; + /* timerfd */ + int fd; +} timer; + +/* document root */ +const char *docroot; +/* length of docroot */ +size_t docrootlen; + +nghttp2_session_callbacks *shared_callbacks; + +static int handle_accept(io_loop *loop, uint32_t events, void *ptr); +static int handle_connection(io_loop *loop, uint32_t events, void *ptr); +static int handle_timer(io_loop *loop, uint32_t events, void *ptr); + +static void io_buf_init(io_buf *buf, uint8_t *underlying, size_t len) { + buf->begin = buf->pos = buf->last = underlying; + buf->end = underlying + len; +} + +static void io_buf_add(io_buf *buf, const void *src, size_t len) { + memcpy(buf->last, src, len); + buf->last += len; +} + +static char *io_buf_add_str(io_buf *buf, const void *src, size_t len) { + uint8_t *start = buf->last; + + memcpy(buf->last, src, len); + buf->last += len; + *buf->last++ = '\0'; + + return (char *)start; +} + +static int memeq(const void *a, const void *b, size_t n) { + return memcmp(a, b, n) == 0; +} + +#define streq(A, B, N) ((sizeof((A)) - 1) == (N) && memeq((A), (B), (N))) + +typedef enum { + NGHTTP2_TOKEN__AUTHORITY, + NGHTTP2_TOKEN__METHOD, + NGHTTP2_TOKEN__PATH, + NGHTTP2_TOKEN__SCHEME, + NGHTTP2_TOKEN_HOST +} nghttp2_token; + +/* Inspired by h2o header lookup. https://github.com/h2o/h2o */ +static int lookup_token(const uint8_t *name, size_t namelen) { + switch (namelen) { + case 5: + switch (name[namelen - 1]) { + case 'h': + if (streq(":pat", name, 4)) { + return NGHTTP2_TOKEN__PATH; + } + break; + } + break; + case 7: + switch (name[namelen - 1]) { + case 'd': + if (streq(":metho", name, 6)) { + return NGHTTP2_TOKEN__METHOD; + } + break; + case 'e': + if (streq(":schem", name, 6)) { + return NGHTTP2_TOKEN__SCHEME; + } + break; + } + break; + case 10: + switch (name[namelen - 1]) { + case 'y': + if (streq(":authorit", name, 9)) { + return NGHTTP2_TOKEN__AUTHORITY; + } + break; + } + break; + } + return -1; +} + +static char *cpydig(char *buf, int n, size_t len) { + char *p; + + p = buf + len - 1; + do { + *p-- = (n % 10) + '0'; + n /= 10; + } while (p >= buf); + + return buf + len; +} + +static const char *MONTH[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; +static const char *DAY_OF_WEEK[] = {"Sun", "Mon", "Tue", "Wed", + "Thu", "Fri", "Sat"}; + +static size_t http_date(char *buf, time_t t) { + struct tm tms; + char *p = buf; + + if (gmtime_r(&t, &tms) == NULL) { + return 0; + } + + /* Sat, 27 Sep 2014 06:31:15 GMT */ + + memcpy(p, DAY_OF_WEEK[tms.tm_wday], 3); + p += 3; + *p++ = ','; + *p++ = ' '; + p = cpydig(p, tms.tm_mday, 2); + *p++ = ' '; + memcpy(p, MONTH[tms.tm_mon], 3); + p += 3; + *p++ = ' '; + p = cpydig(p, tms.tm_year + 1900, 4); + *p++ = ' '; + p = cpydig(p, tms.tm_hour, 2); + *p++ = ':'; + p = cpydig(p, tms.tm_min, 2); + *p++ = ':'; + p = cpydig(p, tms.tm_sec, 2); + memcpy(p, " GMT", 4); + p += 4; + + return p - buf; +} + +static char date[29]; +static char datelen; + +static void update_date() { datelen = http_date(date, time(NULL)); } + +static size_t utos(char *buf, size_t len, uint64_t n) { + size_t nwrite = 0; + uint64_t t = n; + + if (len == 0) { + return 0; + } + + if (n == 0) { + buf[0] = '0'; + return 1; + } + + for (; t; t /= 10, ++nwrite) + ; + + if (nwrite > len) { + return 0; + } + + buf += nwrite - 1; + do { + *buf-- = (n % 10) + '0'; + n /= 10; + } while (n); + + return nwrite; +} + +static void print_errno(const char *prefix, int errnum) { + char buf[1024]; + char *errmsg; + + errmsg = strerror_r(errnum, buf, sizeof(buf)); + + fprintf(stderr, "%s: %s\n", prefix, errmsg); +} + +#define list_insert(head, elem) \ + do { \ + (elem)->prev = (head); \ + (elem)->next = (head)->next; \ + \ + if ((head)->next) { \ + (head)->next->prev = (elem); \ + } \ + (head)->next = (elem); \ + } while (0) + +#define list_remove(elem) \ + do { \ + (elem)->prev->next = (elem)->next; \ + if ((elem)->next) { \ + (elem)->next->prev = (elem)->prev; \ + } \ + } while (0) + +static stream *stream_new(int32_t stream_id, connection *conn) { + stream *strm; + + strm = malloc(sizeof(stream)); + + strm->prev = strm->next = NULL; + strm->method = NULL; + strm->scheme = NULL; + strm->authority = NULL; + strm->path = NULL; + strm->host = NULL; + strm->res_begin = NULL; + strm->res_end = NULL; + strm->methodlen = 0; + strm->schemelen = 0; + strm->authoritylen = 0; + strm->pathlen = 0; + strm->hostlen = 0; + strm->stream_id = stream_id; + strm->filefd = -1; + strm->fileleft = 0; + + list_insert(&conn->strm_head, strm); + + io_buf_init(&strm->scrbuf, strm->rawscrbuf, sizeof(strm->rawscrbuf)); + + return strm; +} + +static void stream_del(stream *strm) { + list_remove(strm); + + if (strm->filefd != -1) { + close(strm->filefd); + } + + free(strm); +} + +static connection *connection_new(int fd) { + connection *conn; + int rv; + + conn = malloc(sizeof(connection)); + + rv = nghttp2_session_server_new(&conn->session, shared_callbacks, conn); + + if (rv != 0) { + goto cleanup; + } + + conn->fd = fd; + conn->cache = NULL; + conn->cachelen = 0; + io_buf_init(&conn->buf, conn->rawoutbuf, sizeof(conn->rawoutbuf)); + conn->evhn.handler = handle_connection; + conn->strm_head.next = NULL; + + return conn; + +cleanup: + free(conn); + return NULL; +} + +static void connection_del(connection *conn) { + stream *strm; + + nghttp2_session_del(conn->session); + shutdown(conn->fd, SHUT_WR); + close(conn->fd); + + strm = conn->strm_head.next; + while (strm) { + stream *next_strm = strm->next; + + stream_del(strm); + strm = next_strm; + } + + free(conn); +} + +static int connection_start(connection *conn) { + nghttp2_settings_entry iv = {NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100}; + int rv; + + rv = nghttp2_submit_settings(conn->session, NGHTTP2_FLAG_NONE, &iv, 1); + + if (rv != 0) { + return -1; + } + + return 0; +} + +static int server_init(server *serv, const char *node, const char *service) { + int rv; + struct addrinfo hints; + struct addrinfo *res, *rp; + int fd; + int on = 1; + socklen_t optlen = sizeof(on); + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = 0; + hints.ai_flags = AI_PASSIVE | AI_ADDRCONFIG; + + rv = getaddrinfo(node, service, &hints, &res); + + if (rv != 0) { + fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv)); + return -1; + } + + for (rp = res; rp; rp = rp->ai_next) { + fd = + socket(rp->ai_family, rp->ai_socktype | SOCK_NONBLOCK, rp->ai_protocol); + + if (fd == -1) { + continue; + } + + rv = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, optlen); + + if (rv == -1) { + print_errno("setsockopt", errno); + } + + if (bind(fd, rp->ai_addr, rp->ai_addrlen) != 0) { + close(fd); + continue; + } + + if (listen(fd, 65536) != 0) { + close(fd); + continue; + } + + break; + } + + freeaddrinfo(res); + + if (!rp) { + fprintf(stderr, "No address to bind\n"); + return -1; + } + + serv->fd = fd; + serv->evhn.handler = handle_accept; + + return 0; +} + +static int timer_init(timer *tmr) { + int fd; + struct itimerspec timerval = {{1, 0}, {1, 0}}; + + fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK); + if (fd == -1) { + print_errno("timerfd_create", errno); + return -1; + } + + if (timerfd_settime(fd, 0, &timerval, NULL) != 0) { + print_errno("timerfd_settime", errno); + return -1; + } + + tmr->fd = fd; + tmr->evhn.handler = handle_timer; + + return 0; +} + +static int io_loop_init(io_loop *loop) { + int epfd; + + epfd = epoll_create1(0); + + if (epfd == -1) { + print_errno("epoll_create", errno); + return -1; + } + + loop->epfd = epfd; + + return 0; +} + +static int io_loop_ctl(io_loop *loop, int op, int fd, uint32_t events, + void *ptr) { + int rv; + struct epoll_event ev; + + ev.events = events; + ev.data.ptr = ptr; + + rv = epoll_ctl(loop->epfd, op, fd, &ev); + + if (rv != 0) { + print_errno("epoll_ctl", errno); + return -1; + } + + return 0; +} + +static int io_loop_add(io_loop *loop, int fd, uint32_t events, void *ptr) { + return io_loop_ctl(loop, EPOLL_CTL_ADD, fd, events, ptr); +} + +static int io_loop_mod(io_loop *loop, int fd, uint32_t events, void *ptr) { + return io_loop_ctl(loop, EPOLL_CTL_MOD, fd, events, ptr); +} + +static int io_loop_run(io_loop *loop, server *serv _U_) { +#define NUM_EVENTS 1024 + struct epoll_event events[NUM_EVENTS]; + + for (;;) { + int nev; + evhandle *evhn; + struct epoll_event *ev, *end; + + while ((nev = epoll_wait(loop->epfd, events, NUM_EVENTS, -1)) == -1 && + errno == EINTR) + ; + + if (nev == -1) { + print_errno("epoll_wait", errno); + return -1; + } + + for (ev = events, end = events + nev; ev != end; ++ev) { + evhn = ev->data.ptr; + evhn->handler(loop, ev->events, ev->data.ptr); + } + } +} + +static int handle_timer(io_loop *loop _U_, uint32_t events _U_, void *ptr) { + timer *tmr = ptr; + int64_t buf; + ssize_t nread; + + while ((nread = read(tmr->fd, &buf, sizeof(buf))) == -1 && errno == EINTR) + ; + + assert(nread == sizeof(buf)); + + update_date(); + + return 0; +} + +static int handle_accept(io_loop *loop, uint32_t events _U_, void *ptr) { + int acfd; + server *serv = ptr; + int on = 1; + socklen_t optlen = sizeof(on); + int rv; + + for (;;) { + connection *conn; + + while ((acfd = accept4(serv->fd, NULL, NULL, SOCK_NONBLOCK)) == -1 && + errno == EINTR) + ; + + if (acfd == -1) { + switch (errno) { + case ENETDOWN: + case EPROTO: + case ENOPROTOOPT: + case EHOSTDOWN: + case ENONET: + case EHOSTUNREACH: + case EOPNOTSUPP: + case ENETUNREACH: + continue; + } + return 0; + } + + rv = setsockopt(acfd, IPPROTO_TCP, TCP_NODELAY, &on, optlen); + + if (rv == -1) { + print_errno("setsockopt", errno); + } + + conn = connection_new(acfd); + + if (conn == NULL) { + close(acfd); + continue; + } + + if (connection_start(conn) != 0 || + io_loop_add(loop, acfd, EPOLLIN | EPOLLOUT, conn) != 0) { + connection_del(conn); + } + } +} + +static void stream_error(connection *conn, int32_t stream_id, + uint32_t error_code) { + nghttp2_submit_rst_stream(conn->session, NGHTTP2_FLAG_NONE, stream_id, + error_code); +} + +static int send_data_callback(nghttp2_session *session _U_, + nghttp2_frame *frame, const uint8_t *framehd, + size_t length, nghttp2_data_source *source, + void *user_data) { + connection *conn = user_data; + uint8_t *p = conn->buf.last; + stream *strm = source->ptr; + + /* We never use padding in this program */ + assert(frame->data.padlen == 0); + + if ((size_t)io_buf_left(&conn->buf) < 9 + frame->hd.length) { + return NGHTTP2_ERR_WOULDBLOCK; + } + + memcpy(p, framehd, 9); + p += 9; + + while (length) { + ssize_t nread; + while ((nread = read(strm->filefd, p, length)) == -1 && errno == EINTR) + ; + if (nread == -1) { + return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; + } + + length -= nread; + p += nread; + } + + conn->buf.last = p; + + return 0; +} + +static ssize_t fd_read_callback(nghttp2_session *session _U_, + int32_t stream_id _U_, uint8_t *buf _U_, + size_t length, uint32_t *data_flags, + nghttp2_data_source *source, + void *user_data _U_) { + stream *strm = source->ptr; + ssize_t nread = + (int64_t)length < strm->fileleft ? (int64_t)length : strm->fileleft; + + *data_flags |= NGHTTP2_DATA_FLAG_NO_COPY; + + strm->fileleft -= nread; + if (nread == 0 || strm->fileleft == 0) { + if (strm->fileleft != 0) { + return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; + } + *data_flags |= NGHTTP2_DATA_FLAG_EOF; + } + return nread; +} + +static ssize_t resbuf_read_callback(nghttp2_session *session _U_, + int32_t stream_id _U_, uint8_t *buf, + size_t length, uint32_t *data_flags, + nghttp2_data_source *source, + void *user_data _U_) { + stream *strm = source->ptr; + size_t left = strm->res_end - strm->res_begin; + size_t nwrite = length < left ? length : left; + + memcpy(buf, strm->res_begin, nwrite); + strm->res_begin += nwrite; + + if (strm->res_begin == strm->res_end) { + *data_flags |= NGHTTP2_DATA_FLAG_EOF; + } + + return nwrite; +} + +static int hex_digit(char c) { + return ('0' <= c && c <= '9') || ('A' <= c && c <= 'F') || + ('a' <= c && c <= 'f'); +} + +static unsigned int hex_to_uint(char c) { + if (c <= '9') { + return c - '0'; + } + + if (c <= 'F') { + return c - 'A' + 10; + } + + return c - 'a' + 10; +} + +static void percent_decode(io_buf *buf, const char *s) { + for (; *s; ++s) { + if (*s == '?' || *s == '#') { + break; + } + + if (*s == '%' && hex_digit(*(s + 1)) && hex_digit(*(s + 2))) { + *buf->last++ = (hex_to_uint(*(s + 1)) << 4) + hex_to_uint(*(s + 2)); + s += 2; + continue; + } + + *buf->last++ = *s; + } +} + +static int check_path(const char *path, size_t len) { + return path[0] == '/' && strchr(path, '\\') == NULL && + strstr(path, "/../") == NULL && strstr(path, "/./") == NULL && + (len < 3 || memcmp(path + len - 3, "/..", 3) != 0) && + (len < 2 || memcmp(path + len - 2, "/.", 2) != 0); +} + +static int make_path(io_buf *pathbuf, const char *req, size_t reqlen _U_) { + uint8_t *p; + + if (req[0] != '/') { + return -1; + } + + if (docrootlen + strlen(req) + sizeof("index.html") > + (size_t)io_buf_left(pathbuf)) { + return -1; + } + + io_buf_add(pathbuf, docroot, docrootlen); + + p = pathbuf->last; + + percent_decode(pathbuf, req); + + if (*(pathbuf->last - 1) == '/') { + io_buf_add(pathbuf, "index.html", sizeof("index.html") - 1); + } + + *pathbuf->last++ = '\0'; + + if (!check_path((const char *)p, pathbuf->last - 1 - p)) { + + return -1; + } + + return 0; +} + +static int status_response(stream *strm, connection *conn, + const char *status_code) { + int rv; + size_t status_codelen = strlen(status_code); + char contentlength[19]; + size_t contentlengthlen; + size_t reslen; + nghttp2_data_provider prd, *prdptr; + nghttp2_nv nva[5] = { + MAKE_NV(":status", ""), MAKE_NV("server", SERVER_NAME), + MAKE_NV2("date", date, datelen), MAKE_NV("content-length", ""), + }; + size_t nvlen = 3; + + nva[0].value = (uint8_t *)status_code; + nva[0].valuelen = strlen(status_code); + +#define BODY1 "" +#define BODY2 "

    " +#define BODY3 "

    " + + reslen = sizeof(BODY1) - 1 + sizeof(BODY2) - 1 + sizeof(BODY3) - 1 + + status_codelen * 2; + + if ((size_t)io_buf_left(&strm->scrbuf) < reslen) { + contentlength[0] = '0'; + contentlengthlen = 1; + prdptr = NULL; + } else { + contentlengthlen = utos(contentlength, sizeof(contentlength), reslen); + + strm->res_begin = strm->scrbuf.last; + + io_buf_add(&strm->scrbuf, BODY1, sizeof(BODY1) - 1); + io_buf_add(&strm->scrbuf, status_code, strlen(status_code)); + io_buf_add(&strm->scrbuf, BODY2, sizeof(BODY2) - 1); + io_buf_add(&strm->scrbuf, status_code, strlen(status_code)); + io_buf_add(&strm->scrbuf, BODY3, sizeof(BODY3) - 1); + + strm->res_end = strm->scrbuf.last; + prdptr = &prd; + } + + nva[nvlen].value = (uint8_t *)contentlength; + nva[nvlen].valuelen = contentlengthlen; + + ++nvlen; + + prd.source.ptr = strm; + prd.read_callback = resbuf_read_callback; + + rv = nghttp2_submit_response(conn->session, strm->stream_id, nva, nvlen, + prdptr); + if (rv != 0) { + return -1; + } + + return 0; +} + +static int redirect_response(stream *strm, connection *conn) { + int rv; + size_t locationlen; + nghttp2_nv nva[5] = { + MAKE_NV(":status", "301"), MAKE_NV("server", SERVER_NAME), + MAKE_NV2("date", date, datelen), MAKE_NV("content-length", "0"), + MAKE_NV("location", ""), + }; + + /* + 1 for trailing '/' */ + locationlen = strm->schemelen + 3 + strm->hostlen + strm->pathlen + 1; + if ((size_t)io_buf_left(&strm->scrbuf) < locationlen) { + return -1; + } + + nva[4].value = strm->scrbuf.last; + nva[4].valuelen = locationlen; + + io_buf_add(&strm->scrbuf, strm->scheme, strm->schemelen); + io_buf_add(&strm->scrbuf, "://", 3); + io_buf_add(&strm->scrbuf, strm->host, strm->hostlen); + io_buf_add(&strm->scrbuf, strm->path, strm->pathlen); + *strm->scrbuf.last++ = '/'; + + rv = nghttp2_submit_response(conn->session, strm->stream_id, nva, + array_size(nva), NULL); + + if (rv != 0) { + return -1; + } + + return 0; +} + +static int process_request(stream *strm, connection *conn) { + int fd; + struct stat stbuf; + int rv; + nghttp2_data_provider prd; + char lastmod[32]; + size_t lastmodlen; + char contentlength[19]; + size_t contentlengthlen; + char path[1024]; + io_buf pathbuf; + nghttp2_nv nva[5] = { + MAKE_NV(":status", "200"), MAKE_NV("server", SERVER_NAME), + MAKE_NV2("date", date, datelen), MAKE_NV("content-length", ""), + }; + size_t nvlen = 3; + + io_buf_init(&pathbuf, (uint8_t *)path, sizeof(path)); + + rv = make_path(&pathbuf, strm->path, strm->pathlen); + + if (rv != 0) { + return status_response(strm, conn, "400"); + } + + fd = open(path, O_RDONLY); + + if (fd == -1) { + return status_response(strm, conn, "404"); + } + + strm->filefd = fd; + + rv = fstat(fd, &stbuf); + + if (rv == -1) { + return status_response(strm, conn, "404"); + } + + if (stbuf.st_mode & S_IFDIR) { + return redirect_response(strm, conn); + } + + prd.source.ptr = strm; + prd.read_callback = fd_read_callback; + + strm->fileleft = stbuf.st_size; + + lastmodlen = http_date(lastmod, stbuf.st_mtim.tv_sec); + contentlengthlen = utos(contentlength, sizeof(contentlength), stbuf.st_size); + + nva[nvlen].value = (uint8_t *)contentlength; + nva[nvlen].valuelen = contentlengthlen; + + ++nvlen; + + if (lastmodlen) { + nva[nvlen].name = (uint8_t *)"last-modified"; + nva[nvlen].namelen = sizeof("last-modified") - 1; + nva[nvlen].value = (uint8_t *)lastmod; + nva[nvlen].valuelen = lastmodlen; + nva[nvlen].flags = NGHTTP2_NV_FLAG_NONE; + + ++nvlen; + } + + rv = + nghttp2_submit_response(conn->session, strm->stream_id, nva, nvlen, &prd); + if (rv != 0) { + return -1; + } + + return 0; +} + +static int on_begin_headers_callback(nghttp2_session *session, + const nghttp2_frame *frame, + void *user_data) { + connection *conn = user_data; + stream *strm; + + if (frame->hd.type != NGHTTP2_HEADERS || + frame->headers.cat != NGHTTP2_HCAT_REQUEST) { + return 0; + } + + strm = stream_new(frame->hd.stream_id, conn); + + if (!strm) { + nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, frame->hd.stream_id, + NGHTTP2_INTERNAL_ERROR); + return 0; + } + + nghttp2_session_set_stream_user_data(session, frame->hd.stream_id, strm); + + return 0; +} + +static int on_header_callback(nghttp2_session *session, + const nghttp2_frame *frame, const uint8_t *name, + size_t namelen, const uint8_t *value, + size_t valuelen, uint8_t flags _U_, + void *user_data _U_) { + stream *strm; + + if (frame->hd.type != NGHTTP2_HEADERS || + frame->headers.cat != NGHTTP2_HCAT_REQUEST) { + return 0; + } + + strm = nghttp2_session_get_stream_user_data(session, frame->hd.stream_id); + + if (!strm) { + return 0; + } + + switch (lookup_token(name, namelen)) { + case NGHTTP2_TOKEN__METHOD: + strm->method = io_buf_add_str(&strm->scrbuf, value, valuelen); + if (!strm->method) { + return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; + } + strm->methodlen = valuelen; + break; + case NGHTTP2_TOKEN__SCHEME: + strm->scheme = io_buf_add_str(&strm->scrbuf, value, valuelen); + if (!strm->scheme) { + return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; + } + strm->schemelen = valuelen; + break; + case NGHTTP2_TOKEN__AUTHORITY: + strm->authority = io_buf_add_str(&strm->scrbuf, value, valuelen); + if (!strm->authority) { + return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; + } + strm->authoritylen = valuelen; + break; + case NGHTTP2_TOKEN__PATH: + strm->path = io_buf_add_str(&strm->scrbuf, value, valuelen); + if (!strm->path) { + return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; + } + strm->pathlen = valuelen; + break; + case NGHTTP2_TOKEN_HOST: + strm->host = io_buf_add_str(&strm->scrbuf, value, valuelen); + if (!strm->host) { + return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; + } + strm->hostlen = valuelen; + break; + } + + return 0; +} + +static int on_frame_recv_callback(nghttp2_session *session, + const nghttp2_frame *frame, void *user_data) { + connection *conn = user_data; + stream *strm; + + if (frame->hd.type != NGHTTP2_HEADERS || + frame->headers.cat != NGHTTP2_HCAT_REQUEST) { + return 0; + } + + strm = nghttp2_session_get_stream_user_data(session, frame->hd.stream_id); + + if (!strm) { + return 0; + } + + if (!strm->host) { + strm->host = strm->authority; + strm->hostlen = strm->authoritylen; + } + + if (process_request(strm, conn) != 0) { + stream_error(conn, strm->stream_id, NGHTTP2_INTERNAL_ERROR); + return 0; + } + + return 0; +} + +static int on_stream_close_callback(nghttp2_session *session, int32_t stream_id, + uint32_t error_code _U_, + void *user_data _U_) { + stream *strm; + + strm = nghttp2_session_get_stream_user_data(session, stream_id); + + if (!strm) { + return 0; + } + + stream_del(strm); + + return 0; +} + +static int on_frame_not_send_callback(nghttp2_session *session _U_, + const nghttp2_frame *frame, + int lib_error_code _U_, void *user_data) { + connection *conn = user_data; + + if (frame->hd.type != NGHTTP2_HEADERS) { + return 0; + } + + /* Issue RST_STREAM so that stream does not hang around. */ + nghttp2_submit_rst_stream(conn->session, NGHTTP2_FLAG_NONE, + frame->hd.stream_id, NGHTTP2_INTERNAL_ERROR); + + return 0; +} + +static int do_read(connection *conn) { + uint8_t buf[32768]; + + for (;;) { + ssize_t nread; + ssize_t nproc; + + while ((nread = read(conn->fd, buf, sizeof(buf))) == -1 && errno == EINTR) + ; + if (nread == -1) { + if (errno == EAGAIN || errno == EWOULDBLOCK) { + return 0; + } + + return -1; + } + + if (nread == 0) { + return -1; + } + + nproc = nghttp2_session_mem_recv(conn->session, buf, nread); + + if (nproc < 0) { + return -1; + } + } +} + +static int do_write(connection *conn) { + for (;;) { + if (io_buf_len(&conn->buf)) { + ssize_t nwrite; + while ((nwrite = write(conn->fd, conn->buf.pos, + io_buf_len(&conn->buf))) == -1 && + errno == EINTR) + ; + if (nwrite == -1) { + if (errno == EAGAIN || errno == EWOULDBLOCK) { + return 0; + } + return -1; + } + + conn->buf.pos += nwrite; + + if (io_buf_len(&conn->buf)) { + return 0; + } + + io_buf_init(&conn->buf, conn->rawoutbuf, sizeof(conn->rawoutbuf)); + } + + if (conn->cache) { + io_buf_add(&conn->buf, conn->cache, conn->cachelen); + conn->cache = NULL; + conn->cachelen = 0; + } + + for (;;) { + ssize_t n; + const uint8_t *b; + + n = nghttp2_session_mem_send(conn->session, &b); + + if (n < 0) { + return -1; + } + + if (n == 0) { + if (io_buf_len(&conn->buf) == 0) { + return 0; + } + break; + } + + if (io_buf_left(&conn->buf) < n) { + conn->cache = b; + conn->cachelen = n; + break; + } + + io_buf_add(&conn->buf, b, n); + } + } +} + +static int handle_connection(io_loop *loop, uint32_t events, void *ptr) { + connection *conn = ptr; + int rv; + uint32_t nextev = 0; + + if (events & (EPOLLHUP | EPOLLERR)) { + goto cleanup; + } + + if (events & EPOLLIN) { + rv = do_read(conn); + + if (rv != 0) { + goto cleanup; + } + } + + rv = do_write(conn); + + if (rv != 0) { + goto cleanup; + } + + if (nghttp2_session_want_read(conn->session)) { + nextev |= EPOLLIN; + } + + if (io_buf_len(&conn->buf) || nghttp2_session_want_write(conn->session)) { + nextev |= EPOLLOUT; + } + + if (!nextev) { + goto cleanup; + } + + io_loop_mod(loop, conn->fd, nextev, conn); + + return 0; + +cleanup: + connection_del(conn); + + return 0; +} + +int main(int argc, char **argv) { + int rv; + server serv; + timer tmr; + io_loop loop; + struct sigaction act; + const char *address; + const char *service; + + if (argc < 4) { + fprintf(stderr, "Usage: tiny-nghttpd
    \n"); + exit(EXIT_FAILURE); + } + + address = argv[1]; + service = argv[2]; + docroot = argv[3]; + docrootlen = strlen(docroot); + + memset(&act, 0, sizeof(act)); + act.sa_handler = SIG_IGN; + sigaction(SIGPIPE, &act, NULL); + + rv = server_init(&serv, address, service); + + if (rv != 0) { + exit(EXIT_FAILURE); + } + + rv = timer_init(&tmr); + + if (rv != 0) { + exit(EXIT_FAILURE); + } + + rv = io_loop_init(&loop); + + if (rv != 0) { + exit(EXIT_FAILURE); + } + + rv = nghttp2_session_callbacks_new(&shared_callbacks); + if (rv != 0) { + fprintf(stderr, "nghttp2_session_callbacks_new: %s", nghttp2_strerror(rv)); + exit(EXIT_FAILURE); + } + + nghttp2_session_callbacks_set_on_begin_headers_callback( + shared_callbacks, on_begin_headers_callback); + nghttp2_session_callbacks_set_on_header_callback(shared_callbacks, + on_header_callback); + nghttp2_session_callbacks_set_on_frame_recv_callback(shared_callbacks, + on_frame_recv_callback); + nghttp2_session_callbacks_set_on_stream_close_callback( + shared_callbacks, on_stream_close_callback); + nghttp2_session_callbacks_set_on_frame_not_send_callback( + shared_callbacks, on_frame_not_send_callback); + nghttp2_session_callbacks_set_send_data_callback(shared_callbacks, + send_data_callback); + + rv = io_loop_add(&loop, serv.fd, EPOLLIN, &serv); + + if (rv != 0) { + exit(EXIT_FAILURE); + } + + rv = io_loop_add(&loop, tmr.fd, EPOLLIN, &tmr); + + if (rv != 0) { + exit(EXIT_FAILURE); + } + + update_date(); + + io_loop_run(&loop, &serv); + + nghttp2_session_callbacks_del(shared_callbacks); + + return 0; +} diff --git a/install-sh b/install-sh new file mode 100755 index 0000000..377bb86 --- /dev/null +++ b/install-sh @@ -0,0 +1,527 @@ +#!/bin/sh +# install - install a program, script, or datafile + +scriptversion=2011-11-20.07; # UTC + +# This originates from X11R5 (mit/util/scripts/install.sh), which was +# later released in X11R6 (xc/config/util/install.sh) with the +# following copyright and license. +# +# Copyright (C) 1994 X Consortium +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +# AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC- +# TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# +# Except as contained in this notice, the name of the X Consortium shall not +# be used in advertising or otherwise to promote the sale, use or other deal- +# ings in this Software without prior written authorization from the X Consor- +# tium. +# +# +# FSF changes to this file are in the public domain. +# +# Calling this script install-sh is preferred over install.sh, to prevent +# 'make' implicit rules from creating a file called install from it +# when there is no Makefile. +# +# This script is compatible with the BSD install script, but was written +# from scratch. + +nl=' +' +IFS=" "" $nl" + +# set DOITPROG to echo to test this script + +# Don't use :- since 4.3BSD and earlier shells don't like it. +doit=${DOITPROG-} +if test -z "$doit"; then + doit_exec=exec +else + doit_exec=$doit +fi + +# Put in absolute file names if you don't have them in your path; +# or use environment vars. + +chgrpprog=${CHGRPPROG-chgrp} +chmodprog=${CHMODPROG-chmod} +chownprog=${CHOWNPROG-chown} +cmpprog=${CMPPROG-cmp} +cpprog=${CPPROG-cp} +mkdirprog=${MKDIRPROG-mkdir} +mvprog=${MVPROG-mv} +rmprog=${RMPROG-rm} +stripprog=${STRIPPROG-strip} + +posix_glob='?' +initialize_posix_glob=' + test "$posix_glob" != "?" || { + if (set -f) 2>/dev/null; then + posix_glob= + else + posix_glob=: + fi + } +' + +posix_mkdir= + +# Desired mode of installed file. +mode=0755 + +chgrpcmd= +chmodcmd=$chmodprog +chowncmd= +mvcmd=$mvprog +rmcmd="$rmprog -f" +stripcmd= + +src= +dst= +dir_arg= +dst_arg= + +copy_on_change=false +no_target_directory= + +usage="\ +Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE + or: $0 [OPTION]... SRCFILES... DIRECTORY + or: $0 [OPTION]... -t DIRECTORY SRCFILES... + or: $0 [OPTION]... -d DIRECTORIES... + +In the 1st form, copy SRCFILE to DSTFILE. +In the 2nd and 3rd, copy all SRCFILES to DIRECTORY. +In the 4th, create DIRECTORIES. + +Options: + --help display this help and exit. + --version display version info and exit. + + -c (ignored) + -C install only if different (preserve the last data modification time) + -d create directories instead of installing files. + -g GROUP $chgrpprog installed files to GROUP. + -m MODE $chmodprog installed files to MODE. + -o USER $chownprog installed files to USER. + -s $stripprog installed files. + -t DIRECTORY install into DIRECTORY. + -T report an error if DSTFILE is a directory. + +Environment variables override the default commands: + CHGRPPROG CHMODPROG CHOWNPROG CMPPROG CPPROG MKDIRPROG MVPROG + RMPROG STRIPPROG +" + +while test $# -ne 0; do + case $1 in + -c) ;; + + -C) copy_on_change=true;; + + -d) dir_arg=true;; + + -g) chgrpcmd="$chgrpprog $2" + shift;; + + --help) echo "$usage"; exit $?;; + + -m) mode=$2 + case $mode in + *' '* | *' '* | *' +'* | *'*'* | *'?'* | *'['*) + echo "$0: invalid mode: $mode" >&2 + exit 1;; + esac + shift;; + + -o) chowncmd="$chownprog $2" + shift;; + + -s) stripcmd=$stripprog;; + + -t) dst_arg=$2 + # Protect names problematic for 'test' and other utilities. + case $dst_arg in + -* | [=\(\)!]) dst_arg=./$dst_arg;; + esac + shift;; + + -T) no_target_directory=true;; + + --version) echo "$0 $scriptversion"; exit $?;; + + --) shift + break;; + + -*) echo "$0: invalid option: $1" >&2 + exit 1;; + + *) break;; + esac + shift +done + +if test $# -ne 0 && test -z "$dir_arg$dst_arg"; then + # When -d is used, all remaining arguments are directories to create. + # When -t is used, the destination is already specified. + # Otherwise, the last argument is the destination. Remove it from $@. + for arg + do + if test -n "$dst_arg"; then + # $@ is not empty: it contains at least $arg. + set fnord "$@" "$dst_arg" + shift # fnord + fi + shift # arg + dst_arg=$arg + # Protect names problematic for 'test' and other utilities. + case $dst_arg in + -* | [=\(\)!]) dst_arg=./$dst_arg;; + esac + done +fi + +if test $# -eq 0; then + if test -z "$dir_arg"; then + echo "$0: no input file specified." >&2 + exit 1 + fi + # It's OK to call 'install-sh -d' without argument. + # This can happen when creating conditional directories. + exit 0 +fi + +if test -z "$dir_arg"; then + do_exit='(exit $ret); exit $ret' + trap "ret=129; $do_exit" 1 + trap "ret=130; $do_exit" 2 + trap "ret=141; $do_exit" 13 + trap "ret=143; $do_exit" 15 + + # Set umask so as not to create temps with too-generous modes. + # However, 'strip' requires both read and write access to temps. + case $mode in + # Optimize common cases. + *644) cp_umask=133;; + *755) cp_umask=22;; + + *[0-7]) + if test -z "$stripcmd"; then + u_plus_rw= + else + u_plus_rw='% 200' + fi + cp_umask=`expr '(' 777 - $mode % 1000 ')' $u_plus_rw`;; + *) + if test -z "$stripcmd"; then + u_plus_rw= + else + u_plus_rw=,u+rw + fi + cp_umask=$mode$u_plus_rw;; + esac +fi + +for src +do + # Protect names problematic for 'test' and other utilities. + case $src in + -* | [=\(\)!]) src=./$src;; + esac + + if test -n "$dir_arg"; then + dst=$src + dstdir=$dst + test -d "$dstdir" + dstdir_status=$? + else + + # Waiting for this to be detected by the "$cpprog $src $dsttmp" command + # might cause directories to be created, which would be especially bad + # if $src (and thus $dsttmp) contains '*'. + if test ! -f "$src" && test ! -d "$src"; then + echo "$0: $src does not exist." >&2 + exit 1 + fi + + if test -z "$dst_arg"; then + echo "$0: no destination specified." >&2 + exit 1 + fi + dst=$dst_arg + + # If destination is a directory, append the input filename; won't work + # if double slashes aren't ignored. + if test -d "$dst"; then + if test -n "$no_target_directory"; then + echo "$0: $dst_arg: Is a directory" >&2 + exit 1 + fi + dstdir=$dst + dst=$dstdir/`basename "$src"` + dstdir_status=0 + else + # Prefer dirname, but fall back on a substitute if dirname fails. + dstdir=` + (dirname "$dst") 2>/dev/null || + expr X"$dst" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$dst" : 'X\(//\)[^/]' \| \ + X"$dst" : 'X\(//\)$' \| \ + X"$dst" : 'X\(/\)' \| . 2>/dev/null || + echo X"$dst" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q' + ` + + test -d "$dstdir" + dstdir_status=$? + fi + fi + + obsolete_mkdir_used=false + + if test $dstdir_status != 0; then + case $posix_mkdir in + '') + # Create intermediate dirs using mode 755 as modified by the umask. + # This is like FreeBSD 'install' as of 1997-10-28. + umask=`umask` + case $stripcmd.$umask in + # Optimize common cases. + *[2367][2367]) mkdir_umask=$umask;; + .*0[02][02] | .[02][02] | .[02]) mkdir_umask=22;; + + *[0-7]) + mkdir_umask=`expr $umask + 22 \ + - $umask % 100 % 40 + $umask % 20 \ + - $umask % 10 % 4 + $umask % 2 + `;; + *) mkdir_umask=$umask,go-w;; + esac + + # With -d, create the new directory with the user-specified mode. + # Otherwise, rely on $mkdir_umask. + if test -n "$dir_arg"; then + mkdir_mode=-m$mode + else + mkdir_mode= + fi + + posix_mkdir=false + case $umask in + *[123567][0-7][0-7]) + # POSIX mkdir -p sets u+wx bits regardless of umask, which + # is incompatible with FreeBSD 'install' when (umask & 300) != 0. + ;; + *) + tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$ + trap 'ret=$?; rmdir "$tmpdir/d" "$tmpdir" 2>/dev/null; exit $ret' 0 + + if (umask $mkdir_umask && + exec $mkdirprog $mkdir_mode -p -- "$tmpdir/d") >/dev/null 2>&1 + then + if test -z "$dir_arg" || { + # Check for POSIX incompatibilities with -m. + # HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or + # other-writable bit of parent directory when it shouldn't. + # FreeBSD 6.1 mkdir -m -p sets mode of existing directory. + ls_ld_tmpdir=`ls -ld "$tmpdir"` + case $ls_ld_tmpdir in + d????-?r-*) different_mode=700;; + d????-?--*) different_mode=755;; + *) false;; + esac && + $mkdirprog -m$different_mode -p -- "$tmpdir" && { + ls_ld_tmpdir_1=`ls -ld "$tmpdir"` + test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1" + } + } + then posix_mkdir=: + fi + rmdir "$tmpdir/d" "$tmpdir" + else + # Remove any dirs left behind by ancient mkdir implementations. + rmdir ./$mkdir_mode ./-p ./-- 2>/dev/null + fi + trap '' 0;; + esac;; + esac + + if + $posix_mkdir && ( + umask $mkdir_umask && + $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir" + ) + then : + else + + # The umask is ridiculous, or mkdir does not conform to POSIX, + # or it failed possibly due to a race condition. Create the + # directory the slow way, step by step, checking for races as we go. + + case $dstdir in + /*) prefix='/';; + [-=\(\)!]*) prefix='./';; + *) prefix='';; + esac + + eval "$initialize_posix_glob" + + oIFS=$IFS + IFS=/ + $posix_glob set -f + set fnord $dstdir + shift + $posix_glob set +f + IFS=$oIFS + + prefixes= + + for d + do + test X"$d" = X && continue + + prefix=$prefix$d + if test -d "$prefix"; then + prefixes= + else + if $posix_mkdir; then + (umask=$mkdir_umask && + $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir") && break + # Don't fail if two instances are running concurrently. + test -d "$prefix" || exit 1 + else + case $prefix in + *\'*) qprefix=`echo "$prefix" | sed "s/'/'\\\\\\\\''/g"`;; + *) qprefix=$prefix;; + esac + prefixes="$prefixes '$qprefix'" + fi + fi + prefix=$prefix/ + done + + if test -n "$prefixes"; then + # Don't fail if two instances are running concurrently. + (umask $mkdir_umask && + eval "\$doit_exec \$mkdirprog $prefixes") || + test -d "$dstdir" || exit 1 + obsolete_mkdir_used=true + fi + fi + fi + + if test -n "$dir_arg"; then + { test -z "$chowncmd" || $doit $chowncmd "$dst"; } && + { test -z "$chgrpcmd" || $doit $chgrpcmd "$dst"; } && + { test "$obsolete_mkdir_used$chowncmd$chgrpcmd" = false || + test -z "$chmodcmd" || $doit $chmodcmd $mode "$dst"; } || exit 1 + else + + # Make a couple of temp file names in the proper directory. + dsttmp=$dstdir/_inst.$$_ + rmtmp=$dstdir/_rm.$$_ + + # Trap to clean up those temp files at exit. + trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0 + + # Copy the file name to the temp name. + (umask $cp_umask && $doit_exec $cpprog "$src" "$dsttmp") && + + # and set any options; do chmod last to preserve setuid bits. + # + # If any of these fail, we abort the whole thing. If we want to + # ignore errors from any of these, just make sure not to ignore + # errors from the above "$doit $cpprog $src $dsttmp" command. + # + { test -z "$chowncmd" || $doit $chowncmd "$dsttmp"; } && + { test -z "$chgrpcmd" || $doit $chgrpcmd "$dsttmp"; } && + { test -z "$stripcmd" || $doit $stripcmd "$dsttmp"; } && + { test -z "$chmodcmd" || $doit $chmodcmd $mode "$dsttmp"; } && + + # If -C, don't bother to copy if it wouldn't change the file. + if $copy_on_change && + old=`LC_ALL=C ls -dlL "$dst" 2>/dev/null` && + new=`LC_ALL=C ls -dlL "$dsttmp" 2>/dev/null` && + + eval "$initialize_posix_glob" && + $posix_glob set -f && + set X $old && old=:$2:$4:$5:$6 && + set X $new && new=:$2:$4:$5:$6 && + $posix_glob set +f && + + test "$old" = "$new" && + $cmpprog "$dst" "$dsttmp" >/dev/null 2>&1 + then + rm -f "$dsttmp" + else + # Rename the file to the real destination. + $doit $mvcmd -f "$dsttmp" "$dst" 2>/dev/null || + + # The rename failed, perhaps because mv can't rename something else + # to itself, or perhaps because mv is so ancient that it does not + # support -f. + { + # Now remove or move aside any old file at destination location. + # We try this two ways since rm can't unlink itself on some + # systems and the destination file might be busy for other + # reasons. In this case, the final cleanup might fail but the new + # file should still install successfully. + { + test ! -f "$dst" || + $doit $rmcmd -f "$dst" 2>/dev/null || + { $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null && + { $doit $rmcmd -f "$rmtmp" 2>/dev/null; :; } + } || + { echo "$0: cannot unlink or rename $dst" >&2 + (exit 1); exit 1 + } + } && + + # Now rename the file to the real destination. + $doit $mvcmd "$dsttmp" "$dst" + } + fi || exit 1 + + trap '' 0 + fi +done + +# Local variables: +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "scriptversion=" +# time-stamp-format: "%:y-%02m-%02d.%02H" +# time-stamp-time-zone: "UTC" +# time-stamp-end: "; # UTC" +# End: diff --git a/integration-tests/Makefile.am b/integration-tests/Makefile.am new file mode 100644 index 0000000..f03cd78 --- /dev/null +++ b/integration-tests/Makefile.am @@ -0,0 +1,43 @@ +# nghttp2 - HTTP/2 C Library + +# Copyright (c) 2015 Tatsuhiro Tsujikawa + +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: + +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. + +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +EXTRA_DIST = \ + nghttpx_http1_test.go \ + nghttpx_http2_test.go \ + nghttpx_spdy_test.go \ + server_tester.go \ + server.key \ + server.crt \ + alt-server.key \ + alt-server.crt \ + setenv + +.PHONY: itprep it + +itprep: + go get -d -v github.com/bradfitz/http2 + go get -d -v github.com/tatsuhiro-t/go-nghttp2 + go get -d -v golang.org/x/net/spdy + +it: + sh setenv go test -v diff --git a/integration-tests/Makefile.in b/integration-tests/Makefile.in new file mode 100644 index 0000000..1110f9a --- /dev/null +++ b/integration-tests/Makefile.in @@ -0,0 +1,537 @@ +# Makefile.in generated by automake 1.14.1 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2013 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +# nghttp2 - HTTP/2 C Library + +# Copyright (c) 2015 Tatsuhiro Tsujikawa + +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: + +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. + +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +VPATH = @srcdir@ +am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)' +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +target_triplet = @target@ +subdir = integration-tests +DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \ + $(srcdir)/config.go.in $(srcdir)/setenv.in +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/ax_boost_asio.m4 \ + $(top_srcdir)/m4/ax_boost_base.m4 \ + $(top_srcdir)/m4/ax_boost_system.m4 \ + $(top_srcdir)/m4/ax_boost_thread.m4 \ + $(top_srcdir)/m4/ax_check_compile_flag.m4 \ + $(top_srcdir)/m4/ax_cxx_compile_stdcxx_11.m4 \ + $(top_srcdir)/m4/ax_have_epoll.m4 \ + $(top_srcdir)/m4/ax_python_devel.m4 \ + $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/libxml2.m4 \ + $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ + $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = config.go setenv +CONFIG_CLEAN_VPATH_FILES = +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +SOURCES = +DIST_SOURCES = +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +APPLDFLAGS = @APPLDFLAGS@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +BOOST_ASIO_LIB = @BOOST_ASIO_LIB@ +BOOST_CPPFLAGS = @BOOST_CPPFLAGS@ +BOOST_LDFLAGS = @BOOST_LDFLAGS@ +BOOST_SYSTEM_LIB = @BOOST_SYSTEM_LIB@ +BOOST_THREAD_LIB = @BOOST_THREAD_LIB@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CUNIT_CFLAGS = @CUNIT_CFLAGS@ +CUNIT_LIBS = @CUNIT_LIBS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +CYTHON = @CYTHON@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DLLTOOL = @DLLTOOL@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +GREP = @GREP@ +HAVE_CXX11 = @HAVE_CXX11@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +JANSSON_CFLAGS = @JANSSON_CFLAGS@ +JANSSON_LIBS = @JANSSON_LIBS@ +JEMALLOC_LIBS = @JEMALLOC_LIBS@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBEVENT_OPENSSL_CFLAGS = @LIBEVENT_OPENSSL_CFLAGS@ +LIBEVENT_OPENSSL_LIBS = @LIBEVENT_OPENSSL_LIBS@ +LIBEV_CFLAGS = @LIBEV_CFLAGS@ +LIBEV_LIBS = @LIBEV_LIBS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBSPDYLAY_CFLAGS = @LIBSPDYLAY_CFLAGS@ +LIBSPDYLAY_LIBS = @LIBSPDYLAY_LIBS@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +LT_AGE = @LT_AGE@ +LT_CURRENT = @LT_CURRENT@ +LT_REVISION = @LT_REVISION@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MKDIR_P = @MKDIR_P@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OPENSSL_CFLAGS = @OPENSSL_CFLAGS@ +OPENSSL_LIBS = @OPENSSL_LIBS@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PACKAGE_VERSION_NUM = @PACKAGE_VERSION_NUM@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PKG_CONFIG = @PKG_CONFIG@ +PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ +PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ +PYTHON = @PYTHON@ +PYTHON_CPPFLAGS = @PYTHON_CPPFLAGS@ +PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@ +PYTHON_EXTRA_LDFLAGS = @PYTHON_EXTRA_LDFLAGS@ +PYTHON_EXTRA_LIBS = @PYTHON_EXTRA_LIBS@ +PYTHON_LDFLAGS = @PYTHON_LDFLAGS@ +PYTHON_PLATFORM = @PYTHON_PLATFORM@ +PYTHON_PREFIX = @PYTHON_PREFIX@ +PYTHON_SITE_PKG = @PYTHON_SITE_PKG@ +PYTHON_VERSION = @PYTHON_VERSION@ +RANLIB = @RANLIB@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +TESTLDADD = @TESTLDADD@ +VERSION = @VERSION@ +WARNCFLAGS = @WARNCFLAGS@ +XML2_CONFIG = @XML2_CONFIG@ +XML_CPPFLAGS = @XML_CPPFLAGS@ +XML_LIBS = @XML_LIBS@ +ZLIB_CFLAGS = @ZLIB_CFLAGS@ +ZLIB_LIBS = @ZLIB_LIBS@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +pkgpyexecdir = @pkgpyexecdir@ +pkgpythondir = @pkgpythondir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +pyexecdir = @pyexecdir@ +pythondir = @pythondir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target = @target@ +target_alias = @target_alias@ +target_cpu = @target_cpu@ +target_os = @target_os@ +target_vendor = @target_vendor@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +EXTRA_DIST = \ + nghttpx_http1_test.go \ + nghttpx_http2_test.go \ + nghttpx_spdy_test.go \ + server_tester.go \ + server.key \ + server.crt \ + alt-server.key \ + alt-server.crt \ + setenv + +all: all-am + +.SUFFIXES: +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu integration-tests/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu integration-tests/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): +config.go: $(top_builddir)/config.status $(srcdir)/config.go.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +setenv: $(top_builddir)/config.status $(srcdir)/setenv.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs +tags TAGS: + +ctags CTAGS: + +cscope cscopelist: + + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile +installdirs: +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic clean-libtool mostlyclean-am + +distclean: distclean-am + -rm -f Makefile +distclean-am: clean-am distclean-generic + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-generic mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: + +.MAKE: install-am install-strip + +.PHONY: all all-am check check-am clean clean-generic clean-libtool \ + cscopelist-am ctags-am distclean distclean-generic \ + distclean-libtool distdir dvi dvi-am html html-am info info-am \ + install install-am install-data install-data-am install-dvi \ + install-dvi-am install-exec install-exec-am install-html \ + install-html-am install-info install-info-am install-man \ + install-pdf install-pdf-am install-ps install-ps-am \ + install-strip installcheck installcheck-am installdirs \ + maintainer-clean maintainer-clean-generic mostlyclean \ + mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ + tags-am uninstall uninstall-am + + +.PHONY: itprep it + +itprep: + go get -d -v github.com/bradfitz/http2 + go get -d -v github.com/tatsuhiro-t/go-nghttp2 + go get -d -v golang.org/x/net/spdy + +it: + sh setenv go test -v + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/integration-tests/alt-server.crt b/integration-tests/alt-server.crt new file mode 100644 index 0000000..f003eb1 --- /dev/null +++ b/integration-tests/alt-server.crt @@ -0,0 +1,21 @@ +-----BEGIN CERTIFICATE----- +MIIDhzCCAm+gAwIBAgIJANfuEldiquMNMA0GCSqGSIb3DQEBCwUAMFoxCzAJBgNV +BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX +aWRnaXRzIFB0eSBMdGQxEzARBgNVBAMMCmFsdC1kb21haW4wHhcNMTUwMTI1MDYy +NTQxWhcNMjUwMTIyMDYyNTQxWjBaMQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29t +ZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMRMwEQYD +VQQDDAphbHQtZG9tYWluMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA +0IwhDOGDipGrJQ9IoRSzPdkU/Ii4aJgGKHlXminym42X0VI3IW61RLvOHRlHVmVH +JQjFuDo2x+y81t9NlDg3HGUbSpzOzpm6StiutB7c4hreT5G4r0YKya1ugiemN0+p +qjIPJWm2jVnf448eZvUKRKEQ9W0MLZjiNjVGKrKlwo7fIlXg4N3+YixLYffAT1NV +d1T6V5jzlbruj15gK2nGjMQ9D1h1t9vTbTxY+mtk72aX0Y64IE6pPBWLFSSH8ozU +idDoL3AZwz2Jker+ALKK8CM4uho/RPpyW1C06HH+HLdH2MqEjDOROde/Nzxm668O +gK/JWGIEyUqYiUXx0yhFxwIDAQABo1AwTjAdBgNVHQ4EFgQU/Y0GDN2uPjbyePcu +95ZvYEK/gHIwHwYDVR0jBBgwFoAU/Y0GDN2uPjbyePcu95ZvYEK/gHIwDAYDVR0T +BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAodD6LVCzL3wfsZ6TxTzf9TfgIdbj +ilL3SEMT/xnfTXT3SLYScTRqQIAI29Y7dOLMq89p4hY2wmeUEhBUAz+y9G2JVr8o +6EbxXrQpWgNJogELqoNnMdrDxB5RsmDDKEJ/rLjDfSkjWbK7B2PZsqVTDgjekCFw +u6FqTIjn/O1O/L5tjwxwxjHmQod/maFCvXoDOVBuwdHnkp298tqlvsHfHO8m++Wj ++XYB8plMIjpeTh9v4w9Jc4QZ59lK/3Tt4qaENeQrMEubKSY/Zen7L2bzhk+cChWT +GSGz9uNXieoZaH79D0wnyZaSZ5Ds4ActMevnGg3iYXuzuFqx8Pungn74Vg== +-----END CERTIFICATE----- diff --git a/integration-tests/alt-server.key b/integration-tests/alt-server.key new file mode 100644 index 0000000..a977663 --- /dev/null +++ b/integration-tests/alt-server.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEwAIBADANBgkqhkiG9w0BAQEFAASCBKowggSmAgEAAoIBAQDQjCEM4YOKkasl +D0ihFLM92RT8iLhomAYoeVeaKfKbjZfRUjchbrVEu84dGUdWZUclCMW4OjbH7LzW +302UODccZRtKnM7OmbpK2K60HtziGt5PkbivRgrJrW6CJ6Y3T6mqMg8labaNWd/j +jx5m9QpEoRD1bQwtmOI2NUYqsqXCjt8iVeDg3f5iLEth98BPU1V3VPpXmPOVuu6P +XmAracaMxD0PWHW329NtPFj6a2TvZpfRjrggTqk8FYsVJIfyjNSJ0OgvcBnDPYmR +6v4AsorwIzi6Gj9E+nJbULTocf4ct0fYyoSMM5E51783PGbrrw6Ar8lYYgTJSpiJ +RfHTKEXHAgMBAAECggEBALTrjFSXY72YB+h7rN+JjMIwDIPUvF6I3HbKZhQpJf6K +xNVkRM2tNHavku0tm/S4ohLf3F+pqRKiL2Udjjjy1+S7VgTRqpwTQ0lhV5aNW8SP +2KMg4R61XfB+k+s4KHu9kYxEJ12mqydPe+r3o0FgfYryTDsOYk1AX6b1aqzqFOGF +7GaqLALSbKU59tcJJ1SZNBbpIKFUrAT9nZt9dW02/foqP5bzUk43Yjw48xmLwegc +bMXXcpZhNZSktltvwRw7Q4Foc9kuRlMdTAnAD9PnMCcZwicS/YeVVF6Rz4fGviKv +7/kPHQ7g4YpFktVDzuZ5xw6GDVFeJ6uGMVUX8+EePvkCgYEA+/nrcn82nFHCxm8Q +0iiUhi/AoXjZg+O5Ytaje9O/YNoX+c4ywe13h0+TXKH79O0KfTwXeJyDgPZbAIFV +9oURellRYUzKDafnBHis2f+Ywn6GqHL5e2X30ZxIp1GK46pcvne1YuvJhgGmiVay +vd7sRx09OKU124dG22rIFCis6asCgYEA0+CsA6LrEwQ/aPJYASY3VHNO/WoAOnPg +Cwsg+02XWsPEwP//lNmpanz8TUm2URS063ZK8bx7t3ejvDgBdsRwwjiMlDp7XTUU +3Zk+mhCV2qkMi02aKemvz29bDhmh5JoH7W3IwsXtJYO0yZDYrDR3ioiKRccioPoE +b/Nq781sEFUCgYEA4xqx9xRpaCLY5nicNI6WrwrDF8YQZisNn+PMnYKP7v8itOgA +H4GkRbSXINpueKZc2dsbXH3UmJtyEdaAYBw3UIrIKmZHhl9afFE3mZQhXssjGxfl +fC6/WZD+eq+n+uJFjPXf6jSSAdHjA828dB1D4CSeVTuyexZF6uUnR+QRVNkCgYEA +i+pb7XLSpZYygY03zFp+Q0h6KyKqz+7hTqmkuA8/GfMZpRHop1UtaWLsAeXhfZ2c +87kEOKptUHSzLYIWhWWnyLorK1+LQ7vf8Y5XJso5C1KDNCKk4XSuYt94U9FddWa6 +QXI0F1s5BYL6Cfma++0R2+va08Vy+rbf40XtojoXWJkCgYEA0hMQSCvok7is27nQ +G80KXfmghU2eEB7zif3T00/fwJycxEbmnNeof+SKmhdY4ZgqTscfOxlQPflV/eqB +xs4GnFDDeM0F8KH0BimOXxr7sJPFCg22PCCQQcRtM/KoU+ip/kNmTfwrsC0xMFPU +HD8M1JCZF2eLMekXXP3cB0U4sUs= +-----END PRIVATE KEY----- diff --git a/integration-tests/config.go.in b/integration-tests/config.go.in new file mode 100644 index 0000000..0a6fd6b --- /dev/null +++ b/integration-tests/config.go.in @@ -0,0 +1,5 @@ +package nghttp2 + +const ( + buildDir = "@top_builddir@" +) diff --git a/integration-tests/nghttpx_http1_test.go b/integration-tests/nghttpx_http1_test.go new file mode 100644 index 0000000..c8d5870 --- /dev/null +++ b/integration-tests/nghttpx_http1_test.go @@ -0,0 +1,506 @@ +package nghttp2 + +import ( + "bufio" + "fmt" + "github.com/bradfitz/http2/hpack" + "io" + "net/http" + "syscall" + "testing" +) + +// TestH1H1PlainGET tests whether simple HTTP/1 GET request works. +func TestH1H1PlainGET(t *testing.T) { + st := newServerTester(nil, t, noopHandler) + defer st.Close() + + res, err := st.http1(requestParam{ + name: "TestH1H1PlainGET", + }) + if err != nil { + t.Fatalf("Error st.http1() = %v", err) + } + + want := 200 + if got := res.status; got != want { + t.Errorf("status = %v; want %v", got, want) + } +} + +// TestH1H1PlainGETClose tests whether simple HTTP/1 GET request with +// Connetion: close request header field works. +func TestH1H1PlainGETClose(t *testing.T) { + st := newServerTester(nil, t, noopHandler) + defer st.Close() + + res, err := st.http1(requestParam{ + name: "TestH1H1PlainGETClose", + header: []hpack.HeaderField{ + pair("Connection", "close"), + }, + }) + if err != nil { + t.Fatalf("Error st.http1() = %v", err) + } + + want := 200 + if got := res.status; got != want { + t.Errorf("status = %v; want %v", got, want) + } +} + +// TestH1H1MultipleRequestCL tests that server rejects request which +// contains multiple Content-Length header fields. +func TestH1H1MultipleRequestCL(t *testing.T) { + st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) { + t.Errorf("server should not forward bad request") + }) + defer st.Close() + + if _, err := io.WriteString(st.conn, fmt.Sprintf(`GET / HTTP/1.1 +Host: %v +Test-Case: TestH1H1MultipleRequestCL +Content-Length: 0 +Content-Length: 0 + +`, st.authority)); err != nil { + t.Fatalf("Error io.WriteString() = %v", err) + } + + resp, err := http.ReadResponse(bufio.NewReader(st.conn), nil) + if err != nil { + t.Fatalf("Error http.ReadResponse() = %v", err) + } + + want := 400 + if got := resp.StatusCode; got != want { + t.Errorf("status: %v; want %v", got, want) + } +} + +// TestH1H1ConnectFailure tests that server handles the situation that +// connection attempt to HTTP/1 backend failed. +func TestH1H1ConnectFailure(t *testing.T) { + st := newServerTester(nil, t, noopHandler) + defer st.Close() + + // shutdown backend server to simulate backend connect failure + st.ts.Close() + + res, err := st.http1(requestParam{ + name: "TestH1H1ConnectFailure", + }) + if err != nil { + t.Fatalf("Error st.http1() = %v", err) + } + want := 503 + if got := res.status; got != want { + t.Errorf("status: %v; want %v", got, want) + } +} + +// TestH1H1GracefulShutdown tests graceful shutdown. +func TestH1H1GracefulShutdown(t *testing.T) { + st := newServerTester(nil, t, noopHandler) + defer st.Close() + + res, err := st.http1(requestParam{ + name: "TestH1H1GracefulShutdown-1", + }) + if err != nil { + t.Fatalf("Error st.http1() = %v", err) + } + + if got, want := res.status, 200; got != want { + t.Errorf("status: %v; want %v", got, want) + } + + st.cmd.Process.Signal(syscall.SIGQUIT) + + res, err = st.http1(requestParam{ + name: "TestH1H1GracefulShutdown-2", + }) + if err != nil { + t.Fatalf("Error st.http1() = %v", err) + } + + if got, want := res.status, 200; got != want { + t.Errorf("status: %v; want %v", got, want) + } + + if got, want := res.connClose, true; got != want { + t.Errorf("res.connClose: %v; want %v", got, want) + } + + want := io.EOF + if _, err := st.conn.Read(nil); err == nil || err != want { + t.Errorf("st.conn.Read(): %v; want %v", err, want) + } +} + +// TestH1H1HostRewrite tests that server rewrites Host header field +func TestH1H1HostRewrite(t *testing.T) { + st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) { + w.Header().Add("request-host", r.Host) + }) + defer st.Close() + + res, err := st.http1(requestParam{ + name: "TestH1H1HostRewrite", + }) + if err != nil { + t.Fatalf("Error st.http1() = %v", err) + } + if got, want := res.status, 200; got != want { + t.Errorf("status: %v; want %v", got, want) + } + if got, want := res.header.Get("request-host"), st.backendHost; got != want { + t.Errorf("request-host: %v; want %v", got, want) + } +} + +// TestH1H1HTTP10 tests that server can accept HTTP/1.0 request +// without Host header field +func TestH1H1HTTP10(t *testing.T) { + st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) { + w.Header().Add("request-host", r.Host) + }) + defer st.Close() + + if _, err := io.WriteString(st.conn, "GET / HTTP/1.0\r\nTest-Case: TestH1H1HTTP10\r\n\r\n"); err != nil { + t.Fatalf("Error io.WriteString() = %v", err) + } + + resp, err := http.ReadResponse(bufio.NewReader(st.conn), nil) + if err != nil { + t.Fatalf("Error http.ReadResponse() = %v", err) + } + + if got, want := resp.StatusCode, 200; got != want { + t.Errorf("status: %v; want %v", got, want) + } + if got, want := resp.Header.Get("request-host"), st.backendHost; got != want { + t.Errorf("request-host: %v; want %v", got, want) + } +} + +// TestH1H1HTTP10NoHostRewrite tests that server generates host header +// field using actual backend server even if --no-http-rewrite is +// used. +func TestH1H1HTTP10NoHostRewrite(t *testing.T) { + st := newServerTester([]string{"--no-host-rewrite"}, t, func(w http.ResponseWriter, r *http.Request) { + w.Header().Add("request-host", r.Host) + }) + defer st.Close() + + if _, err := io.WriteString(st.conn, "GET / HTTP/1.0\r\nTest-Case: TestH1H1HTTP10NoHostRewrite\r\n\r\n"); err != nil { + t.Fatalf("Error io.WriteString() = %v", err) + } + + resp, err := http.ReadResponse(bufio.NewReader(st.conn), nil) + if err != nil { + t.Fatalf("Error http.ReadResponse() = %v", err) + } + + if got, want := resp.StatusCode, 200; got != want { + t.Errorf("status: %v; want %v", got, want) + } + if got, want := resp.Header.Get("request-host"), st.backendHost; got != want { + t.Errorf("request-host: %v; want %v", got, want) + } +} + +// TestH1H1RequestTrailer tests request trailer part is forwarded to +// backend. +func TestH1H1RequestTrailer(t *testing.T) { + st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) { + buf := make([]byte, 4096) + for { + _, err := r.Body.Read(buf) + if err == io.EOF { + break + } + if err != nil { + t.Fatalf("r.Body.Read() = %v", err) + } + } + if got, want := r.Trailer.Get("foo"), "bar"; got != want { + t.Errorf("r.Trailer.Get(foo): %v; want %v", got, want) + } + }) + defer st.Close() + + res, err := st.http1(requestParam{ + name: "TestH1H1RequestTrailer", + body: []byte("1"), + trailer: []hpack.HeaderField{ + pair("foo", "bar"), + }, + }) + if err != nil { + t.Fatalf("Error st.http1() = %v", err) + } + if got, want := res.status, 200; got != want { + t.Errorf("res.status: %v; want %v", got, want) + } +} + +// TestH1H1HeaderFieldBufferPath tests that request with request path +// larger than configured buffer size is rejected. +func TestH1H1HeaderFieldBufferPath(t *testing.T) { + // The value 100 is chosen so that sum of header fields bytes + // does not exceed it. We use > 100 bytes URI to exceed this + // limit. + st := newServerTester([]string{"--header-field-buffer=100"}, t, func(w http.ResponseWriter, r *http.Request) { + t.Fatal("execution path should not be here") + }) + defer st.Close() + + res, err := st.http1(requestParam{ + name: "TestH1H1HeaderFieldBufferPath", + path: "/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", + }) + if err != nil { + t.Fatalf("Error st.http1() = %v", err) + } + if got, want := res.status, 431; got != want { + t.Errorf("status: %v; want %v", got, want) + } +} + +// TestH1H1HeaderFieldBuffer tests that request with header fields +// larger than configured buffer size is rejected. +func TestH1H1HeaderFieldBuffer(t *testing.T) { + st := newServerTester([]string{"--header-field-buffer=10"}, t, func(w http.ResponseWriter, r *http.Request) { + t.Fatal("execution path should not be here") + }) + defer st.Close() + + res, err := st.http1(requestParam{ + name: "TestH1H1HeaderFieldBuffer", + }) + if err != nil { + t.Fatalf("Error st.http1() = %v", err) + } + if got, want := res.status, 431; got != want { + t.Errorf("status: %v; want %v", got, want) + } +} + +// TestH1H1HeaderFields tests that request with header fields more +// than configured number is rejected. +func TestH1H1HeaderFields(t *testing.T) { + st := newServerTester([]string{"--max-header-fields=1"}, t, func(w http.ResponseWriter, r *http.Request) { + t.Fatal("execution path should not be here") + }) + defer st.Close() + + res, err := st.http1(requestParam{ + name: "TestH1H1HeaderFields", + header: []hpack.HeaderField{ + // Add extra header field to ensure that + // header field limit exceeds + pair("Connection", "close"), + }, + }) + if err != nil { + t.Fatalf("Error st.http1() = %v", err) + } + if got, want := res.status, 431; got != want { + t.Errorf("status: %v; want %v", got, want) + } +} + +// TestH1H2ConnectFailure tests that server handles the situation that +// connection attempt to HTTP/2 backend failed. +func TestH1H2ConnectFailure(t *testing.T) { + st := newServerTester([]string{"--http2-bridge"}, t, noopHandler) + defer st.Close() + + // simulate backend connect attempt failure + st.ts.Close() + + res, err := st.http1(requestParam{ + name: "TestH1H2ConnectFailure", + }) + if err != nil { + t.Fatalf("Error st.http1() = %v", err) + } + want := 503 + if got := res.status; got != want { + t.Errorf("status: %v; want %v", got, want) + } +} + +// TestH1H2NoHost tests that server rejects request without Host +// header field for HTTP/2 backend. +func TestH1H2NoHost(t *testing.T) { + st := newServerTester([]string{"--http2-bridge"}, t, func(w http.ResponseWriter, r *http.Request) { + t.Errorf("server should not forward bad request") + }) + defer st.Close() + + // without Host header field, we expect 400 response + if _, err := io.WriteString(st.conn, "GET / HTTP/1.1\r\nTest-Case: TestH1H2NoHost\r\n\r\n"); err != nil { + t.Fatalf("Error io.WriteString() = %v", err) + } + + resp, err := http.ReadResponse(bufio.NewReader(st.conn), nil) + if err != nil { + t.Fatalf("Error http.ReadResponse() = %v", err) + } + + want := 400 + if got := resp.StatusCode; got != want { + t.Errorf("status: %v; want %v", got, want) + } +} + +// TestH1H2HTTP10 tests that server can accept HTTP/1.0 request +// without Host header field +func TestH1H2HTTP10(t *testing.T) { + st := newServerTester([]string{"--http2-bridge"}, t, func(w http.ResponseWriter, r *http.Request) { + w.Header().Add("request-host", r.Host) + }) + defer st.Close() + + if _, err := io.WriteString(st.conn, "GET / HTTP/1.0\r\nTest-Case: TestH1H2HTTP10\r\n\r\n"); err != nil { + t.Fatalf("Error io.WriteString() = %v", err) + } + + resp, err := http.ReadResponse(bufio.NewReader(st.conn), nil) + if err != nil { + t.Fatalf("Error http.ReadResponse() = %v", err) + } + + if got, want := resp.StatusCode, 200; got != want { + t.Errorf("status: %v; want %v", got, want) + } + if got, want := resp.Header.Get("request-host"), st.backendHost; got != want { + t.Errorf("request-host: %v; want %v", got, want) + } +} + +// TestH1H2HTTP10NoHostRewrite tests that server generates host header +// field using actual backend server even if --no-http-rewrite is +// used. +func TestH1H2HTTP10NoHostRewrite(t *testing.T) { + st := newServerTester([]string{"--http2-bridge", "--no-host-rewrite"}, t, func(w http.ResponseWriter, r *http.Request) { + w.Header().Add("request-host", r.Host) + }) + defer st.Close() + + if _, err := io.WriteString(st.conn, "GET / HTTP/1.0\r\nTest-Case: TestH1H2HTTP10NoHostRewrite\r\n\r\n"); err != nil { + t.Fatalf("Error io.WriteString() = %v", err) + } + + resp, err := http.ReadResponse(bufio.NewReader(st.conn), nil) + if err != nil { + t.Fatalf("Error http.ReadResponse() = %v", err) + } + + if got, want := resp.StatusCode, 200; got != want { + t.Errorf("status: %v; want %v", got, want) + } + if got, want := resp.Header.Get("request-host"), st.backendHost; got != want { + t.Errorf("request-host: %v; want %v", got, want) + } +} + +// TestH1H2CrumbleCookie tests that Cookies are crumbled and assembled +// when forwarding to HTTP/2 backend link. go-nghttp2 server +// concatenates crumbled Cookies automatically, so this test is not +// much effective now. +func TestH1H2CrumbleCookie(t *testing.T) { + st := newServerTester([]string{"--http2-bridge"}, t, func(w http.ResponseWriter, r *http.Request) { + if got, want := r.Header.Get("Cookie"), "alpha; bravo; charlie"; got != want { + t.Errorf("Cookie: %v; want %v", got, want) + } + }) + defer st.Close() + + res, err := st.http1(requestParam{ + name: "TestH1H2CrumbleCookie", + header: []hpack.HeaderField{ + pair("Cookie", "alpha; bravo; charlie"), + }, + }) + if err != nil { + t.Fatalf("Error st.http1() = %v", err) + } + if got, want := res.status, 200; got != want { + t.Errorf("status: %v; want %v", got, want) + } +} + +// TestH1H2GenerateVia tests that server generates Via header field to and +// from backend server. +func TestH1H2GenerateVia(t *testing.T) { + st := newServerTester([]string{"--http2-bridge"}, t, func(w http.ResponseWriter, r *http.Request) { + if got, want := r.Header.Get("Via"), "1.1 nghttpx"; got != want { + t.Errorf("Via: %v; want %v", got, want) + } + }) + defer st.Close() + + res, err := st.http1(requestParam{ + name: "TestH1H2GenerateVia", + }) + if err != nil { + t.Fatalf("Error st.http1() = %v", err) + } + if got, want := res.header.Get("Via"), "2 nghttpx"; got != want { + t.Errorf("Via: %v; want %v", got, want) + } +} + +// TestH1H2AppendVia tests that server adds value to existing Via +// header field to and from backend server. +func TestH1H2AppendVia(t *testing.T) { + st := newServerTester([]string{"--http2-bridge"}, t, func(w http.ResponseWriter, r *http.Request) { + if got, want := r.Header.Get("Via"), "foo, 1.1 nghttpx"; got != want { + t.Errorf("Via: %v; want %v", got, want) + } + w.Header().Add("Via", "bar") + }) + defer st.Close() + + res, err := st.http1(requestParam{ + name: "TestH1H2AppendVia", + header: []hpack.HeaderField{ + pair("via", "foo"), + }, + }) + if err != nil { + t.Fatalf("Error st.http1() = %v", err) + } + if got, want := res.header.Get("Via"), "bar, 2 nghttpx"; got != want { + t.Errorf("Via: %v; want %v", got, want) + } +} + +// TestH1H2NoVia tests that server does not add value to existing Via +// header field to and from backend server. +func TestH1H2NoVia(t *testing.T) { + st := newServerTester([]string{"--http2-bridge", "--no-via"}, t, func(w http.ResponseWriter, r *http.Request) { + if got, want := r.Header.Get("Via"), "foo"; got != want { + t.Errorf("Via: %v; want %v", got, want) + } + w.Header().Add("Via", "bar") + }) + defer st.Close() + + res, err := st.http1(requestParam{ + name: "TestH1H2NoVia", + header: []hpack.HeaderField{ + pair("via", "foo"), + }, + }) + if err != nil { + t.Fatalf("Error st.http1() = %v", err) + } + if got, want := res.header.Get("Via"), "bar"; got != want { + t.Errorf("Via: %v; want %v", got, want) + } +} diff --git a/integration-tests/nghttpx_http2_test.go b/integration-tests/nghttpx_http2_test.go new file mode 100644 index 0000000..953e9d4 --- /dev/null +++ b/integration-tests/nghttpx_http2_test.go @@ -0,0 +1,813 @@ +package nghttp2 + +import ( + "crypto/tls" + "fmt" + "github.com/bradfitz/http2" + "github.com/bradfitz/http2/hpack" + "io" + "io/ioutil" + "net/http" + "strings" + "syscall" + "testing" +) + +// TestH2H1PlainGET tests whether simple HTTP/2 GET request works. +func TestH2H1PlainGET(t *testing.T) { + st := newServerTester(nil, t, noopHandler) + defer st.Close() + + res, err := st.http2(requestParam{ + name: "TestH2H1PlainGET", + }) + if err != nil { + t.Fatalf("Error st.http2() = %v", err) + } + + want := 200 + if res.status != want { + t.Errorf("status = %v; want %v", res.status, want) + } +} + +// TestH2H1AddXff tests that server generates X-Forwarded-For header +// field when forwarding request to backend. +func TestH2H1AddXff(t *testing.T) { + st := newServerTester([]string{"--add-x-forwarded-for"}, t, func(w http.ResponseWriter, r *http.Request) { + xff := r.Header.Get("X-Forwarded-For") + want := "127.0.0.1" + if xff != want { + t.Errorf("X-Forwarded-For = %v; want %v", xff, want) + } + }) + defer st.Close() + + _, err := st.http2(requestParam{ + name: "TestH2H1AddXff", + }) + if err != nil { + t.Fatalf("Error st.http2() = %v", err) + } +} + +// TestH2H1AddXff2 tests that server appends X-Forwarded-For header +// field to existing one when forwarding request to backend. +func TestH2H1AddXff2(t *testing.T) { + st := newServerTester([]string{"--add-x-forwarded-for"}, t, func(w http.ResponseWriter, r *http.Request) { + xff := r.Header.Get("X-Forwarded-For") + want := "host, 127.0.0.1" + if xff != want { + t.Errorf("X-Forwarded-For = %v; want %v", xff, want) + } + }) + defer st.Close() + + _, err := st.http2(requestParam{ + name: "TestH2H1AddXff2", + header: []hpack.HeaderField{ + pair("x-forwarded-for", "host"), + }, + }) + if err != nil { + t.Fatalf("Error st.http2() = %v", err) + } +} + +// TestH2H1StripXff tests that --strip-incoming-x-forwarded-for +// option. +func TestH2H1StripXff(t *testing.T) { + st := newServerTester([]string{"--strip-incoming-x-forwarded-for"}, t, func(w http.ResponseWriter, r *http.Request) { + if xff, found := r.Header["X-Forwarded-For"]; found { + t.Errorf("X-Forwarded-For = %v; want nothing", xff) + } + }) + defer st.Close() + + _, err := st.http2(requestParam{ + name: "TestH2H1StripXff1", + header: []hpack.HeaderField{ + pair("x-forwarded-for", "host"), + }, + }) + if err != nil { + t.Fatalf("Error st.http2() = %v", err) + } +} + +// TestH2H1StripAddXff tests that --strip-incoming-x-forwarded-for and +// --add-x-forwarded-for options. +func TestH2H1StripAddXff(t *testing.T) { + args := []string{ + "--strip-incoming-x-forwarded-for", + "--add-x-forwarded-for", + } + st := newServerTester(args, t, func(w http.ResponseWriter, r *http.Request) { + xff := r.Header.Get("X-Forwarded-For") + want := "127.0.0.1" + if xff != want { + t.Errorf("X-Forwarded-For = %v; want %v", xff, want) + } + }) + defer st.Close() + + _, err := st.http2(requestParam{ + name: "TestH2H1StripAddXff", + header: []hpack.HeaderField{ + pair("x-forwarded-for", "host"), + }, + }) + if err != nil { + t.Fatalf("Error st.http2() = %v", err) + } +} + +// TestH2H1GenerateVia tests that server generates Via header field to and +// from backend server. +func TestH2H1GenerateVia(t *testing.T) { + st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) { + if got, want := r.Header.Get("Via"), "2 nghttpx"; got != want { + t.Errorf("Via: %v; want %v", got, want) + } + }) + defer st.Close() + + res, err := st.http2(requestParam{ + name: "TestH2H1GenerateVia", + }) + if err != nil { + t.Fatalf("Error st.http2() = %v", err) + } + if got, want := res.header.Get("Via"), "1.1 nghttpx"; got != want { + t.Errorf("Via: %v; want %v", got, want) + } +} + +// TestH2H1AppendVia tests that server adds value to existing Via +// header field to and from backend server. +func TestH2H1AppendVia(t *testing.T) { + st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) { + if got, want := r.Header.Get("Via"), "foo, 2 nghttpx"; got != want { + t.Errorf("Via: %v; want %v", got, want) + } + w.Header().Add("Via", "bar") + }) + defer st.Close() + + res, err := st.http2(requestParam{ + name: "TestH2H1AppendVia", + header: []hpack.HeaderField{ + pair("via", "foo"), + }, + }) + if err != nil { + t.Fatalf("Error st.http2() = %v", err) + } + if got, want := res.header.Get("Via"), "bar, 1.1 nghttpx"; got != want { + t.Errorf("Via: %v; want %v", got, want) + } +} + +// TestH2H1NoVia tests that server does not add value to existing Via +// header field to and from backend server. +func TestH2H1NoVia(t *testing.T) { + st := newServerTester([]string{"--no-via"}, t, func(w http.ResponseWriter, r *http.Request) { + if got, want := r.Header.Get("Via"), "foo"; got != want { + t.Errorf("Via: %v; want %v", got, want) + } + w.Header().Add("Via", "bar") + }) + defer st.Close() + + res, err := st.http2(requestParam{ + name: "TestH2H1NoVia", + header: []hpack.HeaderField{ + pair("via", "foo"), + }, + }) + if err != nil { + t.Fatalf("Error st.http2() = %v", err) + } + if got, want := res.header.Get("Via"), "bar"; got != want { + t.Errorf("Via: %v; want %v", got, want) + } +} + +// TestH2H1HostRewrite tests that server rewrites host header field +func TestH2H1HostRewrite(t *testing.T) { + st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) { + w.Header().Add("request-host", r.Host) + }) + defer st.Close() + + res, err := st.http2(requestParam{ + name: "TestH2H1HostRewrite", + }) + if err != nil { + t.Fatalf("Error st.http2() = %v", err) + } + if got, want := res.status, 200; got != want { + t.Errorf("status: %v; want %v", got, want) + } + if got, want := res.header.Get("request-host"), st.backendHost; got != want { + t.Errorf("request-host: %v; want %v", got, want) + } +} + +// TestH2H1NoHostRewrite tests that server does not rewrite host +// header field +func TestH2H1NoHostRewrite(t *testing.T) { + st := newServerTester([]string{"--no-host-rewrite"}, t, func(w http.ResponseWriter, r *http.Request) { + w.Header().Add("request-host", r.Host) + }) + defer st.Close() + + res, err := st.http2(requestParam{ + name: "TestH2H1NoHostRewrite", + }) + if err != nil { + t.Fatalf("Error st.http2() = %v", err) + } + if got, want := res.status, 200; got != want { + t.Errorf("status: %v; want %v", got, want) + } + if got, want := res.header.Get("request-host"), st.frontendHost; got != want { + t.Errorf("request-host: %v; want %v", got, want) + } +} + +// TestH2H1BadRequestCL tests that server rejects request whose +// content-length header field value does not match its request body +// size. +func TestH2H1BadRequestCL(t *testing.T) { + st := newServerTester(nil, t, noopHandler) + defer st.Close() + + // we set content-length: 1024, but the actual request body is + // 3 bytes. + res, err := st.http2(requestParam{ + name: "TestH2H1BadRequestCL", + method: "POST", + header: []hpack.HeaderField{ + pair("content-length", "1024"), + }, + body: []byte("foo"), + }) + if err != nil { + t.Fatalf("Error st.http2() = %v", err) + } + + want := http2.ErrCodeProtocol + if res.errCode != want { + t.Errorf("res.errCode = %v; want %v", res.errCode, want) + } +} + +// TestH2H1BadResponseCL tests that server returns error when +// content-length response header field value does not match its +// response body size. +func TestH2H1BadResponseCL(t *testing.T) { + st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) { + // we set content-length: 1024, but only send 3 bytes. + w.Header().Add("Content-Length", "1024") + w.Write([]byte("foo")) + }) + defer st.Close() + + res, err := st.http2(requestParam{ + name: "TestH2H1BadResponseCL", + }) + if err != nil { + t.Fatalf("Error st.http2() = %v", err) + } + + want := http2.ErrCodeProtocol + if res.errCode != want { + t.Errorf("res.errCode = %v; want %v", res.errCode, want) + } +} + +// TestH2H1LocationRewrite tests location header field rewriting +// works. +func TestH2H1LocationRewrite(t *testing.T) { + st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) { + // TODO we cannot get st.ts's port number here.. 8443 + // is just a place holder. We ignore it on rewrite. + w.Header().Add("Location", "http://127.0.0.1:8443/p/q?a=b#fragment") + }) + defer st.Close() + + res, err := st.http2(requestParam{ + name: "TestH2H1LocationRewrite", + }) + if err != nil { + t.Fatalf("Error st.http2() = %v", err) + } + + want := fmt.Sprintf("http://127.0.0.1:%v/p/q?a=b#fragment", serverPort) + if got := res.header.Get("Location"); got != want { + t.Errorf("Location: %v; want %v", got, want) + } +} + +// TestH2H1ChunkedRequestBody tests that chunked request body works. +func TestH2H1ChunkedRequestBody(t *testing.T) { + st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) { + want := "[chunked]" + if got := fmt.Sprint(r.TransferEncoding); got != want { + t.Errorf("Transfer-Encoding: %v; want %v", got, want) + } + body, err := ioutil.ReadAll(r.Body) + if err != nil { + t.Fatalf("Error reading r.body: %v", err) + } + want = "foo" + if got := string(body); got != want { + t.Errorf("body: %v; want %v", got, want) + } + }) + defer st.Close() + + _, err := st.http2(requestParam{ + name: "TestH2H1ChunkedRequestBody", + method: "POST", + body: []byte("foo"), + }) + if err != nil { + t.Fatalf("Error st.http2() = %v", err) + } +} + +// TestH2H1MultipleRequestCL tests that server rejects request with +// multiple Content-Length request header fields. +func TestH2H1MultipleRequestCL(t *testing.T) { + st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) { + t.Errorf("server should not forward bad request") + }) + defer st.Close() + + res, err := st.http2(requestParam{ + name: "TestH2H1MultipleRequestCL", + header: []hpack.HeaderField{ + pair("content-length", "1"), + pair("content-length", "1"), + }, + }) + if err != nil { + t.Fatalf("Error st.http2() = %v", err) + } + if got, want := res.errCode, http2.ErrCodeProtocol; got != want { + t.Errorf("res.errCode: %v; want %v", got, want) + } +} + +// TestH2H1InvalidRequestCL tests that server rejects request with +// Content-Length which cannot be parsed as a number. +func TestH2H1InvalidRequestCL(t *testing.T) { + st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) { + t.Errorf("server should not forward bad request") + }) + defer st.Close() + + res, err := st.http2(requestParam{ + name: "TestH2H1InvalidRequestCL", + header: []hpack.HeaderField{ + pair("content-length", ""), + }, + }) + if err != nil { + t.Fatalf("Error st.http2() = %v", err) + } + if got, want := res.errCode, http2.ErrCodeProtocol; got != want { + t.Errorf("res.errCode: %v; want %v", got, want) + } +} + +// TestH2H1ConnectFailure tests that server handles the situation that +// connection attempt to HTTP/1 backend failed. +func TestH2H1ConnectFailure(t *testing.T) { + st := newServerTester(nil, t, noopHandler) + defer st.Close() + + // shutdown backend server to simulate backend connect failure + st.ts.Close() + + res, err := st.http2(requestParam{ + name: "TestH2H1ConnectFailure", + }) + if err != nil { + t.Fatalf("Error st.http2() = %v", err) + } + want := 503 + if got := res.status; got != want { + t.Errorf("status: %v; want %v", got, want) + } +} + +// TestH2H1AssembleCookies tests that crumbled cookies in HTTP/2 +// request is assembled into 1 when forwarding to HTTP/1 backend link. +func TestH2H1AssembleCookies(t *testing.T) { + st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) { + if got, want := r.Header.Get("Cookie"), "alpha; bravo; charlie"; got != want { + t.Errorf("Cookie: %v; want %v", got, want) + } + }) + defer st.Close() + + res, err := st.http2(requestParam{ + name: "TestH2H1AssembleCookies", + header: []hpack.HeaderField{ + pair("cookie", "alpha"), + pair("cookie", "bravo"), + pair("cookie", "charlie"), + }, + }) + if err != nil { + t.Fatalf("Error st.http2() = %v", err) + } + if got, want := res.status, 200; got != want { + t.Errorf("status: %v; want %v", got, want) + } +} + +// TestH2H1TETrailers tests that server accepts TE request header +// field if it has trailers only. +func TestH2H1TETrailers(t *testing.T) { + st := newServerTester(nil, t, noopHandler) + defer st.Close() + + res, err := st.http2(requestParam{ + name: "TestH2H1TETrailers", + header: []hpack.HeaderField{ + pair("te", "trailers"), + }, + }) + if err != nil { + t.Fatalf("Error st.http2() = %v", err) + } + if got, want := res.status, 200; got != want { + t.Errorf("status: %v; want %v", got, want) + } +} + +// TestH2H1TEGzip tests that server resets stream if TE request header +// field contains gzip. +func TestH2H1TEGzip(t *testing.T) { + st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) { + t.Error("server should not forward bad request") + }) + defer st.Close() + + res, err := st.http2(requestParam{ + name: "TestH2H1TEGzip", + header: []hpack.HeaderField{ + pair("te", "gzip"), + }, + }) + if err != nil { + t.Fatalf("Error st.http2() = %v", err) + } + if got, want := res.errCode, http2.ErrCodeProtocol; got != want { + t.Errorf("res.errCode = %v; want %v", res.errCode, want) + } +} + +// TestH2H1SNI tests server's TLS SNI extension feature. It must +// choose appropriate certificate depending on the indicated +// server_name from client. +func TestH2H1SNI(t *testing.T) { + st := newServerTesterTLSConfig([]string{"--subcert=" + testDir + "/alt-server.key:" + testDir + "/alt-server.crt"}, t, noopHandler, &tls.Config{ + ServerName: "alt-domain", + }) + defer st.Close() + + tlsConn := st.conn.(*tls.Conn) + connState := tlsConn.ConnectionState() + cert := connState.PeerCertificates[0] + + if got, want := cert.Subject.CommonName, "alt-domain"; got != want { + t.Errorf("CommonName: %v; want %v", got, want) + } +} + +// TestH2H1ServerPush tests server push using Link header field from +// backend server. +func TestH2H1ServerPush(t *testing.T) { + st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) { + // only resources marked as rel=preload are pushed + if !strings.HasPrefix(r.URL.Path, "/css/") { + w.Header().Add("Link", "; rel=preload, , ; rel=preload") + } + }) + defer st.Close() + + res, err := st.http2(requestParam{ + name: "TestH2H1ServerPush", + }) + if err != nil { + t.Fatalf("Error st.http2() = %v", err) + } + if got, want := res.status, 200; got != want { + t.Errorf("res.status: %v; want %v", got, want) + } + if got, want := len(res.pushResponse), 2; got != want { + t.Fatalf("len(res.pushResponse): %v; want %v", got, want) + } + mainCSS := res.pushResponse[0] + if got, want := mainCSS.status, 200; got != want { + t.Errorf("mainCSS.status: %v; want %v", got, want) + } + themeCSS := res.pushResponse[1] + if got, want := themeCSS.status, 200; got != want { + t.Errorf("themeCSS.status: %v; want %v", got, want) + } +} + +// TestH2H1RequestTrailer tests request trailer part is forwarded to +// backend. +func TestH2H1RequestTrailer(t *testing.T) { + st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) { + buf := make([]byte, 4096) + for { + _, err := r.Body.Read(buf) + if err == io.EOF { + break + } + if err != nil { + t.Fatalf("r.Body.Read() = %v", err) + } + } + if got, want := r.Trailer.Get("foo"), "bar"; got != want { + t.Errorf("r.Trailer.Get(foo): %v; want %v", got, want) + } + }) + defer st.Close() + + res, err := st.http2(requestParam{ + name: "TestH2H1RequestTrailer", + body: []byte("1"), + trailer: []hpack.HeaderField{ + pair("foo", "bar"), + }, + }) + if err != nil { + t.Fatalf("Error st.http2() = %v", err) + } + if got, want := res.status, 200; got != want { + t.Errorf("res.status: %v; want %v", got, want) + } +} + +// TestH2H1HeaderFieldBuffer tests that request with header fields +// larger than configured buffer size is rejected. +func TestH2H1HeaderFieldBuffer(t *testing.T) { + st := newServerTester([]string{"--header-field-buffer=10"}, t, func(w http.ResponseWriter, r *http.Request) { + t.Fatal("execution path should not be here") + }) + defer st.Close() + + res, err := st.http2(requestParam{ + name: "TestH2H1HeaderFieldBuffer", + }) + if err != nil { + t.Fatalf("Error st.http2() = %v", err) + } + if got, want := res.status, 431; got != want { + t.Errorf("status: %v; want %v", got, want) + } +} + +// TestH2H1HeaderFields tests that request with header fields more +// than configured number is rejected. +func TestH2H1HeaderFields(t *testing.T) { + st := newServerTester([]string{"--max-header-fields=1"}, t, func(w http.ResponseWriter, r *http.Request) { + t.Fatal("execution path should not be here") + }) + defer st.Close() + + res, err := st.http2(requestParam{ + name: "TestH2H1HeaderFields", + // we have at least 4 pseudo-header fields sent, and + // that ensures that buffer limit exceeds. + }) + if err != nil { + t.Fatalf("Error st.http2() = %v", err) + } + if got, want := res.status, 431; got != want { + t.Errorf("status: %v; want %v", got, want) + } +} + +// TestH2H1Upgrade tests HTTP Upgrade to HTTP/2 +func TestH2H1Upgrade(t *testing.T) { + st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) {}) + defer st.Close() + + res, err := st.http1(requestParam{ + name: "TestH2H1Upgrade", + header: []hpack.HeaderField{ + pair("Connection", "Upgrade, HTTP2-Settings"), + pair("Upgrade", "h2c"), + pair("HTTP2-Settings", "AAMAAABkAAQAAP__"), + }, + }) + + if err != nil { + t.Fatalf("Error st.http1() = %v", err) + } + + if got, want := res.status, 101; got != want { + t.Errorf("res.status: %v; want %v", got, want) + } + + res, err = st.http2(requestParam{ + httpUpgrade: true, + }) + if err != nil { + t.Fatalf("Error st.http2() = %v", err) + } + if got, want := res.status, 200; got != want { + t.Errorf("res.status: %v; want %v", got, want) + } +} + +// TestH2H1GracefulShutdown tests graceful shutdown. +func TestH2H1GracefulShutdown(t *testing.T) { + st := newServerTester(nil, t, noopHandler) + defer st.Close() + + fmt.Fprint(st.conn, "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n") + if err := st.fr.WriteSettings(); err != nil { + t.Fatalf("st.fr.WriteSettings(): %v", err) + } + + header := []hpack.HeaderField{ + pair(":method", "GET"), + pair(":scheme", "http"), + pair(":authority", st.authority), + pair(":path", "/"), + } + + for _, h := range header { + _ = st.enc.WriteField(h) + } + + if err := st.fr.WriteHeaders(http2.HeadersFrameParam{ + StreamID: 1, + EndStream: false, + EndHeaders: true, + BlockFragment: st.headerBlkBuf.Bytes(), + }); err != nil { + t.Fatalf("st.fr.WriteHeaders(): %v", err) + } + + // send SIGQUIT signal to nghttpx to perform graceful shutdown + st.cmd.Process.Signal(syscall.SIGQUIT) + + // after signal, finish request body + if err := st.fr.WriteData(1, true, nil); err != nil { + t.Fatalf("st.fr.WriteData(): %v", err) + } + + numGoAway := 0 + + for { + fr, err := st.readFrame() + if err != nil { + if err == io.EOF { + want := 2 + if got := numGoAway; got != want { + t.Fatalf("numGoAway: %v; want %v", got, want) + } + return + } + t.Fatalf("st.readFrame(): %v", err) + } + switch f := fr.(type) { + case *http2.GoAwayFrame: + numGoAway += 1 + want := http2.ErrCodeNo + if got := f.ErrCode; got != want { + t.Fatalf("f.ErrCode(%v): %v; want %v", numGoAway, got, want) + } + switch numGoAway { + case 1: + want := (uint32(1) << 31) - 1 + if got := f.LastStreamID; got != want { + t.Fatalf("f.LastStreamID(%v): %v; want %v", numGoAway, got, want) + } + case 2: + want := uint32(1) + if got := f.LastStreamID; got != want { + t.Fatalf("f.LastStreamID(%v): %v; want %v", numGoAway, got, want) + } + case 3: + t.Fatalf("too many GOAWAYs received") + } + } + } +} + +// TestH2H2MultipleResponseCL tests that server returns error if +// multiple Content-Length response header fields are received. +func TestH2H2MultipleResponseCL(t *testing.T) { + st := newServerTester([]string{"--http2-bridge"}, t, func(w http.ResponseWriter, r *http.Request) { + w.Header().Add("content-length", "1") + w.Header().Add("content-length", "1") + }) + defer st.Close() + + res, err := st.http2(requestParam{ + name: "TestH2H2MultipleResponseCL", + }) + if err != nil { + t.Fatalf("Error st.http2() = %v", err) + } + if got, want := res.errCode, http2.ErrCodeInternal; got != want { + t.Errorf("res.errCode: %v; want %v", got, want) + } +} + +// TestH2H2InvalidResponseCL tests that server returns error if +// Content-Length response header field value cannot be parsed as a +// number. +func TestH2H2InvalidResponseCL(t *testing.T) { + st := newServerTester([]string{"--http2-bridge"}, t, func(w http.ResponseWriter, r *http.Request) { + w.Header().Add("content-length", "") + }) + defer st.Close() + + res, err := st.http2(requestParam{ + name: "TestH2H2InvalidResponseCL", + }) + if err != nil { + t.Fatalf("Error st.http2() = %v", err) + } + if got, want := res.errCode, http2.ErrCodeInternal; got != want { + t.Errorf("res.errCode: %v; want %v", got, want) + } +} + +// TestH2H2ConnectFailure tests that server handles the situation that +// connection attempt to HTTP/2 backend failed. +func TestH2H2ConnectFailure(t *testing.T) { + st := newServerTester([]string{"--http2-bridge"}, t, noopHandler) + defer st.Close() + + // simulate backend connect attempt failure + st.ts.Close() + + res, err := st.http2(requestParam{ + name: "TestH2H2ConnectFailure", + }) + if err != nil { + t.Fatalf("Error st.http2() = %v", err) + } + want := 503 + if got := res.status; got != want { + t.Errorf("status: %v; want %v", got, want) + } +} + +// TestH2H2HostRewrite tests that server rewrites host header field +func TestH2H2HostRewrite(t *testing.T) { + st := newServerTester([]string{"--http2-bridge"}, t, func(w http.ResponseWriter, r *http.Request) { + w.Header().Add("request-host", r.Host) + }) + defer st.Close() + + res, err := st.http2(requestParam{ + name: "TestH2H2HostRewrite", + }) + if err != nil { + t.Fatalf("Error st.http2() = %v", err) + } + if got, want := res.status, 200; got != want { + t.Errorf("status: %v; want %v", got, want) + } + if got, want := res.header.Get("request-host"), st.backendHost; got != want { + t.Errorf("request-host: %v; want %v", got, want) + } +} + +// TestH2H2NoHostRewrite tests that server does not rewrite host +// header field +func TestH2H2NoHostRewrite(t *testing.T) { + st := newServerTester([]string{"--http2-bridge", "--no-host-rewrite"}, t, func(w http.ResponseWriter, r *http.Request) { + w.Header().Add("request-host", r.Host) + }) + defer st.Close() + + res, err := st.http2(requestParam{ + name: "TestH2H2NoHostRewrite", + }) + if err != nil { + t.Fatalf("Error st.http2() = %v", err) + } + if got, want := res.status, 200; got != want { + t.Errorf("status: %v; want %v", got, want) + } + if got, want := res.header.Get("request-host"), st.frontendHost; got != want { + t.Errorf("request-host: %v; want %v", got, want) + } +} diff --git a/integration-tests/nghttpx_spdy_test.go b/integration-tests/nghttpx_spdy_test.go new file mode 100644 index 0000000..232e1da --- /dev/null +++ b/integration-tests/nghttpx_spdy_test.go @@ -0,0 +1,232 @@ +package nghttp2 + +import ( + "github.com/bradfitz/http2/hpack" + "golang.org/x/net/spdy" + "net/http" + "testing" +) + +// TestS3H1PlainGET tests whether simple SPDY GET request works. +func TestS3H1PlainGET(t *testing.T) { + st := newServerTesterTLS([]string{"--npn-list=spdy/3.1"}, t, noopHandler) + defer st.Close() + + res, err := st.spdy(requestParam{ + name: "TestS3H1PlainGET", + }) + if err != nil { + t.Fatalf("Error st.spdy() = %v", err) + } + + want := 200 + if got := res.status; got != want { + t.Errorf("status = %v; want %v", got, want) + } +} + +// TestS3H1BadRequestCL tests that server rejects request whose +// content-length header field value does not match its request body +// size. +func TestS3H1BadRequestCL(t *testing.T) { + st := newServerTesterTLS([]string{"--npn-list=spdy/3.1"}, t, noopHandler) + defer st.Close() + + // we set content-length: 1024, but the actual request body is + // 3 bytes. + res, err := st.spdy(requestParam{ + name: "TestS3H1BadRequestCL", + method: "POST", + header: []hpack.HeaderField{ + pair("content-length", "1024"), + }, + body: []byte("foo"), + }) + if err != nil { + t.Fatalf("Error st.spdy() = %v", err) + } + + want := spdy.ProtocolError + if got := res.spdyRstErrCode; got != want { + t.Errorf("res.spdyRstErrCode = %v; want %v", got, want) + } +} + +// TestS3H1MultipleRequestCL tests that server rejects request with +// multiple Content-Length request header fields. +func TestS3H1MultipleRequestCL(t *testing.T) { + st := newServerTesterTLS([]string{"--npn-list=spdy/3.1"}, t, func(w http.ResponseWriter, r *http.Request) { + t.Errorf("server should not forward bad request") + }) + defer st.Close() + + res, err := st.spdy(requestParam{ + name: "TestS3H1MultipleRequestCL", + header: []hpack.HeaderField{ + pair("content-length", "1"), + pair("content-length", "1"), + }, + }) + if err != nil { + t.Fatalf("Error st.spdy() = %v", err) + } + want := 400 + if got := res.status; got != want { + t.Errorf("status: %v; want %v", got, want) + } +} + +// TestS3H1InvalidRequestCL tests that server rejects request with +// Content-Length which cannot be parsed as a number. +func TestS3H1InvalidRequestCL(t *testing.T) { + st := newServerTesterTLS([]string{"--npn-list=spdy/3.1"}, t, func(w http.ResponseWriter, r *http.Request) { + t.Errorf("server should not forward bad request") + }) + defer st.Close() + + res, err := st.spdy(requestParam{ + name: "TestS3H1InvalidRequestCL", + header: []hpack.HeaderField{ + pair("content-length", ""), + }, + }) + if err != nil { + t.Fatalf("Error st.spdy() = %v", err) + } + want := 400 + if got := res.status; got != want { + t.Errorf("status: %v; want %v", got, want) + } +} + +// TestS3H1GenerateVia tests that server generates Via header field to and +// from backend server. +func TestS3H1GenerateVia(t *testing.T) { + st := newServerTesterTLS([]string{"--npn-list=spdy/3.1"}, t, func(w http.ResponseWriter, r *http.Request) { + if got, want := r.Header.Get("Via"), "1.1 nghttpx"; got != want { + t.Errorf("Via: %v; want %v", got, want) + } + }) + defer st.Close() + + res, err := st.spdy(requestParam{ + name: "TestS3H1GenerateVia", + }) + if err != nil { + t.Fatalf("Error st.spdy() = %v", err) + } + if got, want := res.header.Get("Via"), "1.1 nghttpx"; got != want { + t.Errorf("Via: %v; want %v", got, want) + } +} + +// TestS3H1AppendVia tests that server adds value to existing Via +// header field to and from backend server. +func TestS3H1AppendVia(t *testing.T) { + st := newServerTesterTLS([]string{"--npn-list=spdy/3.1"}, t, func(w http.ResponseWriter, r *http.Request) { + if got, want := r.Header.Get("Via"), "foo, 1.1 nghttpx"; got != want { + t.Errorf("Via: %v; want %v", got, want) + } + w.Header().Add("Via", "bar") + }) + defer st.Close() + + res, err := st.spdy(requestParam{ + name: "TestS3H1AppendVia", + header: []hpack.HeaderField{ + pair("via", "foo"), + }, + }) + if err != nil { + t.Fatalf("Error st.spdy() = %v", err) + } + if got, want := res.header.Get("Via"), "bar, 1.1 nghttpx"; got != want { + t.Errorf("Via: %v; want %v", got, want) + } +} + +// TestS3H1NoVia tests that server does not add value to existing Via +// header field to and from backend server. +func TestS3H1NoVia(t *testing.T) { + st := newServerTesterTLS([]string{"--npn-list=spdy/3.1", "--no-via"}, t, func(w http.ResponseWriter, r *http.Request) { + if got, want := r.Header.Get("Via"), "foo"; got != want { + t.Errorf("Via: %v; want %v", got, want) + } + w.Header().Add("Via", "bar") + }) + defer st.Close() + + res, err := st.spdy(requestParam{ + name: "TestS3H1NoVia", + header: []hpack.HeaderField{ + pair("via", "foo"), + }, + }) + if err != nil { + t.Fatalf("Error st.spdy() = %v", err) + } + if got, want := res.header.Get("Via"), "bar"; got != want { + t.Errorf("Via: %v; want %v", got, want) + } +} + +// TestS3H1HeaderFieldBuffer tests that request with header fields +// larger than configured buffer size is rejected. +func TestS3H1HeaderFieldBuffer(t *testing.T) { + st := newServerTesterTLS([]string{"--npn-list=spdy/3.1", "--header-field-buffer=10"}, t, func(w http.ResponseWriter, r *http.Request) { + t.Fatal("execution path should not be here") + }) + defer st.Close() + + res, err := st.spdy(requestParam{ + name: "TestS3H1HeaderFieldBuffer", + }) + if err != nil { + t.Fatalf("Error st.spdy() = %v", err) + } + if got, want := res.spdyRstErrCode, spdy.InternalError; got != want { + t.Errorf("res.spdyRstErrCode: %v; want %v", got, want) + } +} + +// TestS3H1HeaderFields tests that request with header fields more +// than configured number is rejected. +func TestS3H1HeaderFields(t *testing.T) { + st := newServerTesterTLS([]string{"--npn-list=spdy/3.1", "--max-header-fields=1"}, t, func(w http.ResponseWriter, r *http.Request) { + t.Fatal("execution path should not be here") + }) + defer st.Close() + + res, err := st.spdy(requestParam{ + name: "TestS3H1HeaderFields", + // we have at least 5 pseudo-header fields sent, and + // that ensures that buffer limit exceeds. + }) + if err != nil { + t.Fatalf("Error st.spdy() = %v", err) + } + if got, want := res.spdyRstErrCode, spdy.InternalError; got != want { + t.Errorf("res.spdyRstErrCode: %v; want %v", got, want) + } +} + +// TestS3H2ConnectFailure tests that server handles the situation that +// connection attempt to HTTP/2 backend failed. +func TestS3H2ConnectFailure(t *testing.T) { + st := newServerTesterTLS([]string{"--npn-list=spdy/3.1", "--http2-bridge"}, t, noopHandler) + defer st.Close() + + // simulate backend connect attempt failure + st.ts.Close() + + res, err := st.spdy(requestParam{ + name: "TestS3H2ConnectFailure", + }) + if err != nil { + t.Fatalf("Error st.spdy() = %v", err) + } + want := 503 + if got := res.status; got != want { + t.Errorf("status: %v; want %v", got, want) + } +} diff --git a/integration-tests/server.crt b/integration-tests/server.crt new file mode 100644 index 0000000..c50fdaa --- /dev/null +++ b/integration-tests/server.crt @@ -0,0 +1,21 @@ +-----BEGIN CERTIFICATE----- +MIIDhTCCAm2gAwIBAgIJAOvIx8xIxgyOMA0GCSqGSIb3DQEBCwUAMFkxCzAJBgNV +BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX +aWRnaXRzIFB0eSBMdGQxEjAQBgNVBAMMCTEyNy4wLjAuMTAeFw0xNTAxMjMxMjI0 +MjdaFw0yNTAxMjAxMjI0MjdaMFkxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21l +LVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQxEjAQBgNV +BAMMCTEyNy4wLjAuMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMuI +QZRI/iBaxPTjTWGemt8tCEfzZWxuIW3hY/gIhwJDfH2SbourBh1s9vqcqhBq5vmo +kdfVQXAnNLjIG1uhWmcHuNnKrE5hU82N6i9RsmuM5TQRvhsamHri4G+EXJMu9GqF +Mso8g7MWpRSGKf+8gfjAVNwfCHFiu8oBcMmy3l54MFHgRLSveAMhiPB0e3Xlnpr5 +2bS/oGTx5ynwPgBpEn2FrpT4Z/aLCLzJ/ysgNH8BXEh7n/v7xM3vd5grqB039rd5 +JoxlWvp+4XpzKp5upaqmOcVUq4pDSFUQ3w6C+v33Z3OK6Qaon7GMxLv3Us3b7PZ3 +1CLoWJR2o3OSnUfO/gUCAwEAAaNQME4wHQYDVR0OBBYEFLc5JWPUUVx4GJesogMV +w2Rz0L3yMB8GA1UdIwQYMBaAFLc5JWPUUVx4GJesogMVw2Rz0L3yMAwGA1UdEwQF +MAMBAf8wDQYJKoZIhvcNAQELBQADggEBAAP/cJWpM+GEjmVYHFacKTdbXBMox2Xn +QY2NLm00WPOGvKnO7czMFfX/pEmiq71kD45rLLfbaJP205QpxqiAIvhFhuq50Co7 +sTDtwcDTPLX9H7Ugjt4sTMPiwC14uVXFfoT/J46zMjXwP00qKyfszc2tkIgHfrTl +h4M1hkdfmMximir/Ii7TdYYJ3oGS8tdcYb6D4DZwAljKmxF6iUOwFCUgpTmqDBT5 +irXY8D27DzuNN5Pg07rwAlwXLCzrJE10UtO4MmRVXwpzmoaRQD4/tna6bZzdetvs +gPdGP6W1o0q85gullieMJWeKyQA/wasoE7fypn4pHAdTZm/vH+v7GHg= +-----END CERTIFICATE----- diff --git a/integration-tests/server.key b/integration-tests/server.key new file mode 100644 index 0000000..0fc02bb --- /dev/null +++ b/integration-tests/server.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDLiEGUSP4gWsT0 +401hnprfLQhH82VsbiFt4WP4CIcCQ3x9km6LqwYdbPb6nKoQaub5qJHX1UFwJzS4 +yBtboVpnB7jZyqxOYVPNjeovUbJrjOU0Eb4bGph64uBvhFyTLvRqhTLKPIOzFqUU +hin/vIH4wFTcHwhxYrvKAXDJst5eeDBR4ES0r3gDIYjwdHt15Z6a+dm0v6Bk8ecp +8D4AaRJ9ha6U+Gf2iwi8yf8rIDR/AVxIe5/7+8TN73eYK6gdN/a3eSaMZVr6fuF6 +cyqebqWqpjnFVKuKQ0hVEN8Ogvr992dziukGqJ+xjMS791LN2+z2d9Qi6FiUdqNz +kp1Hzv4FAgMBAAECggEACG26GYP0Ui6wHVwUZkiFLVzWDPS9bIIbDEvbMfhYbvWQ +gDrCLTKF7E4I5FP8jvV+XzRl5cRFE3nsKwLObzr9XWrqcsp73DsXl1mbKx58/ws0 +qrVZZBHz4pLmrHeUxduZ75dYhRuAcLgtWe48awTJdR2x5fO7C8cE89afbxrjLpJE +tVyiw6vVB0GfWTZodxtAFMTX1KVm4bTngXfg0NF1FBNHAX3Cm6t4YCE41hKSc0IQ +Jr3C4e9uj8poze1B17k79bGB8HNMbbc8Ws0sdbxi5xnY+HUA/mYQrmGXo8sdqiYC +EYCMqPm3iJrCmmpHukGf2Vt9k1aLlJ+lxOclSwFO+QKBgQDoRmoprfdmU20LyxYH +eVeVqggqmhNohwnuhIvOAyrWGUkbDsssqx2Vv82z0WHAAkwEvQ984UzaYWCCL3m3 ++JzpF2dz6aKhXIaYnXBlk3STMGUCDT5ysPvsin9z/unzkffh3vrbDBARGFYWG18x +eUyTDOVVeTZNHUJXGjRyiftCkwKBgQDgUkR6dHU4ciSt7Y0UkyAgtZ7POR41T05L +bcxbjJeqm6qlj+oP9WUk7JxeSEFUbrMiROABLPPqTwmGo4xrDRx/e7WrqN6QBKC+ +Y8CfalrKRb0np60x7Mxx0kbmHp5cwv9QDKznKViOYSgKxFrOFZyMAEXQdZ3FvjXF +OQWrw86kBwKBgQDXuxa9MWO3uUJtkqkaNfw/+FVvY/0kt09lJdxHci+l/IQmyl2w +Vhm7TRK7sXvtfvSl7gblgMgFiC2/nGKbmR/7ag5e3R98aVhlhMywuvyp/GfEORLI +KVNChfwMezVFUUx+j8BEFHcTuZuzGqcWZ0fUyER0V4k0pDlKdv9BZqBkWwKBgCdP +o3qGQCilMDJex/OMGPxCd9M+4kFbZZAobMC6cbXPU+dxwgYL7i67XGfVZ8WBJNlj +kpICK7irIzM6JBh6krzwlBTCIkbA2N6kopQNUl3SPOTfKKXwJp/nxs77HKuK7K09 +m2tjPoatFhRU9sjY1rdeMN3oTr7hp5CpfonsZaEvAoGAEPsZcDd4N9ap5bgaeDy9 +NOfLsIyaxT5k6moRIiy83QPihvCuECP16+r6M5tiSfgt/PtCimdjhRiqXzIHNRhh +Nfsv13vUtZgt8cYXuTdI4a8feKI7Q4876ME8Qp3WM5/UNZWq6/sWCuZFqbXUhqM0 +mwNEi5Zddzf8VsSL2gCraQg= +-----END PRIVATE KEY----- diff --git a/integration-tests/server_tester.go b/integration-tests/server_tester.go new file mode 100644 index 0000000..87e593b --- /dev/null +++ b/integration-tests/server_tester.go @@ -0,0 +1,683 @@ +package nghttp2 + +import ( + "bufio" + "bytes" + "crypto/tls" + "errors" + "fmt" + "github.com/bradfitz/http2" + "github.com/bradfitz/http2/hpack" + "github.com/tatsuhiro-t/go-nghttp2" + "golang.org/x/net/spdy" + "io" + "io/ioutil" + "net" + "net/http" + "net/http/httptest" + "net/url" + "os/exec" + "sort" + "strconv" + "strings" + "testing" + "time" +) + +const ( + serverBin = buildDir + "/src/nghttpx" + serverPort = 3009 + testDir = buildDir + "/integration-tests" +) + +func pair(name, value string) hpack.HeaderField { + return hpack.HeaderField{ + Name: name, + Value: value, + } +} + +type serverTester struct { + args []string // command-line arguments + cmd *exec.Cmd // test frontend server process, which is test subject + url string // test frontend server URL + t *testing.T + ts *httptest.Server // backend server + frontendHost string // frontend server host + backendHost string // backend server host + conn net.Conn // connection to frontend server + h2PrefaceSent bool // HTTP/2 preface was sent in conn + nextStreamID uint32 // next stream ID + fr *http2.Framer // HTTP/2 framer + spdyFr *spdy.Framer // SPDY/3.1 framer + headerBlkBuf bytes.Buffer // buffer to store encoded header block + enc *hpack.Encoder // HTTP/2 HPACK encoder + header http.Header // received header fields + dec *hpack.Decoder // HTTP/2 HPACK decoder + authority string // server's host:port + frCh chan http2.Frame // used for incoming HTTP/2 frame + spdyFrCh chan spdy.Frame // used for incoming SPDY frame + errCh chan error +} + +// newServerTester creates test context for plain TCP frontend +// connection. +func newServerTester(args []string, t *testing.T, handler http.HandlerFunc) *serverTester { + return newServerTesterInternal(args, t, handler, false, nil) +} + +// newServerTester creates test context for TLS frontend connection. +func newServerTesterTLS(args []string, t *testing.T, handler http.HandlerFunc) *serverTester { + return newServerTesterInternal(args, t, handler, true, nil) +} + +// newServerTester creates test context for TLS frontend connection +// with given clientConfig +func newServerTesterTLSConfig(args []string, t *testing.T, handler http.HandlerFunc, clientConfig *tls.Config) *serverTester { + return newServerTesterInternal(args, t, handler, true, clientConfig) +} + +// newServerTesterInternal creates test context. If frontendTLS is +// true, set up TLS frontend connection. +func newServerTesterInternal(args []string, t *testing.T, handler http.HandlerFunc, frontendTLS bool, clientConfig *tls.Config) *serverTester { + ts := httptest.NewUnstartedServer(handler) + + backendTLS := false + for _, k := range args { + switch k { + case "--http2-bridge": + backendTLS = true + } + } + if backendTLS { + nghttp2.ConfigureServer(ts.Config, &nghttp2.Server{}) + // According to httptest/server.go, we have to set + // NextProtos separately for ts.TLS. NextProtos set + // in nghttp2.ConfigureServer is effectively ignored. + ts.TLS = new(tls.Config) + ts.TLS.NextProtos = append(ts.TLS.NextProtos, "h2-14") + ts.StartTLS() + args = append(args, "-k") + } else { + ts.Start() + } + scheme := "http" + if frontendTLS { + scheme = "https" + args = append(args, testDir+"/server.key", testDir+"/server.crt") + } else { + args = append(args, "--frontend-no-tls") + } + + backendURL, err := url.Parse(ts.URL) + if err != nil { + t.Fatalf("Error parsing URL from httptest.Server: %v", err) + } + + // URL.Host looks like "127.0.0.1:8080", but we want + // "127.0.0.1,8080" + b := "-b" + strings.Replace(backendURL.Host, ":", ",", -1) + args = append(args, fmt.Sprintf("-f127.0.0.1,%v", serverPort), b, + "--errorlog-file="+testDir+"/log.txt", "-LINFO") + + authority := fmt.Sprintf("127.0.0.1:%v", serverPort) + + st := &serverTester{ + cmd: exec.Command(serverBin, args...), + t: t, + ts: ts, + url: fmt.Sprintf("%v://%v", scheme, authority), + frontendHost: fmt.Sprintf("127.0.0.1:%v", serverPort), + backendHost: backendURL.Host, + nextStreamID: 1, + authority: authority, + frCh: make(chan http2.Frame), + spdyFrCh: make(chan spdy.Frame), + errCh: make(chan error), + } + + if err := st.cmd.Start(); err != nil { + st.t.Fatalf("Error starting %v: %v", serverBin, err) + } + + retry := 0 + for { + var conn net.Conn + var err error + if frontendTLS { + var tlsConfig *tls.Config + if clientConfig == nil { + tlsConfig = new(tls.Config) + } else { + tlsConfig = clientConfig + } + tlsConfig.InsecureSkipVerify = true + tlsConfig.NextProtos = []string{"h2-14", "spdy/3.1"} + conn, err = tls.Dial("tcp", authority, tlsConfig) + } else { + conn, err = net.Dial("tcp", authority) + } + if err != nil { + retry += 1 + if retry >= 100 { + st.Close() + st.t.Fatalf("Error server is not responding too long; server command-line arguments may be invalid") + } + time.Sleep(150 * time.Millisecond) + continue + } + if frontendTLS { + tlsConn := conn.(*tls.Conn) + cs := tlsConn.ConnectionState() + if !cs.NegotiatedProtocolIsMutual { + st.Close() + st.t.Fatalf("Error negotiated next protocol is not mutual") + } + } + st.conn = conn + break + } + + st.fr = http2.NewFramer(st.conn, st.conn) + spdyFr, err := spdy.NewFramer(st.conn, st.conn) + if err != nil { + st.Close() + st.t.Fatalf("Error spdy.NewFramer: %v", err) + } + st.spdyFr = spdyFr + st.enc = hpack.NewEncoder(&st.headerBlkBuf) + st.dec = hpack.NewDecoder(4096, func(f hpack.HeaderField) { + st.header.Add(f.Name, f.Value) + }) + + return st +} + +func (st *serverTester) Close() { + if st.conn != nil { + st.conn.Close() + } + if st.cmd != nil { + st.cmd.Process.Kill() + st.cmd.Wait() + } + if st.ts != nil { + st.ts.Close() + } +} + +func (st *serverTester) readFrame() (http2.Frame, error) { + go func() { + f, err := st.fr.ReadFrame() + if err != nil { + st.errCh <- err + return + } + st.frCh <- f + }() + + select { + case f := <-st.frCh: + return f, nil + case err := <-st.errCh: + return nil, err + case <-time.After(5 * time.Second): + return nil, errors.New("timeout waiting for frame") + } +} + +func (st *serverTester) readSpdyFrame() (spdy.Frame, error) { + go func() { + f, err := st.spdyFr.ReadFrame() + if err != nil { + st.errCh <- err + return + } + st.spdyFrCh <- f + }() + + select { + case f := <-st.spdyFrCh: + return f, nil + case err := <-st.errCh: + return nil, err + case <-time.After(2 * time.Second): + return nil, errors.New("timeout waiting for frame") + } +} + +type requestParam struct { + name string // name for this request to identify the request in log easily + streamID uint32 // stream ID, automatically assigned if 0 + method string // method, defaults to GET + scheme string // scheme, defaults to http + authority string // authority, defaults to backend server address + path string // path, defaults to / + header []hpack.HeaderField // additional request header fields + body []byte // request body + trailer []hpack.HeaderField // trailer part + httpUpgrade bool // true if upgraded to HTTP/2 through HTTP Upgrade +} + +// wrapper for request body to set trailer part +type chunkedBodyReader struct { + trailer []hpack.HeaderField + trailerWritten bool + body io.Reader + req *http.Request +} + +func (cbr *chunkedBodyReader) Read(p []byte) (n int, err error) { + // document says that we have to set http.Request.Trailer + // after request was sent and before body returns EOF. + if !cbr.trailerWritten { + cbr.trailerWritten = true + for _, h := range cbr.trailer { + cbr.req.Trailer.Set(h.Name, h.Value) + } + } + return cbr.body.Read(p) +} + +func (st *serverTester) http1(rp requestParam) (*serverResponse, error) { + method := "GET" + if rp.method != "" { + method = rp.method + } + + var body io.Reader + var cbr *chunkedBodyReader + if rp.body != nil { + body = bytes.NewBuffer(rp.body) + if len(rp.trailer) != 0 { + cbr = &chunkedBodyReader{ + trailer: rp.trailer, + body: body, + } + body = cbr + } + } + + reqURL := st.url + + if rp.path != "" { + u, err := url.Parse(st.url) + if err != nil { + st.t.Fatalf("Error parsing URL from st.url %v: %v", st.url, err) + } + u.Path = rp.path + reqURL = u.String() + } + + req, err := http.NewRequest(method, reqURL, body) + if err != nil { + return nil, err + } + for _, h := range rp.header { + req.Header.Add(h.Name, h.Value) + } + req.Header.Add("Test-Case", rp.name) + if cbr != nil { + cbr.req = req + // this makes request use chunked encoding + req.ContentLength = -1 + req.Trailer = make(http.Header) + for _, h := range cbr.trailer { + req.Trailer.Set(h.Name, "") + } + } + if err := req.Write(st.conn); err != nil { + return nil, err + } + resp, err := http.ReadResponse(bufio.NewReader(st.conn), req) + if err != nil { + return nil, err + } + respBody, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, err + } + resp.Body.Close() + + res := &serverResponse{ + status: resp.StatusCode, + header: resp.Header, + body: respBody, + connClose: resp.Close, + } + + return res, nil +} + +func (st *serverTester) spdy(rp requestParam) (*serverResponse, error) { + res := &serverResponse{} + + var id spdy.StreamId + if rp.streamID != 0 { + id = spdy.StreamId(rp.streamID) + if id >= spdy.StreamId(st.nextStreamID) && id%2 == 1 { + st.nextStreamID = uint32(id) + 2 + } + } else { + id = spdy.StreamId(st.nextStreamID) + st.nextStreamID += 2 + } + + method := "GET" + if rp.method != "" { + method = rp.method + } + + scheme := "http" + if rp.scheme != "" { + scheme = rp.scheme + } + + host := st.authority + if rp.authority != "" { + host = rp.authority + } + + path := "/" + if rp.path != "" { + path = rp.path + } + + header := make(http.Header) + header.Add(":method", method) + header.Add(":scheme", scheme) + header.Add(":host", host) + header.Add(":path", path) + header.Add(":version", "HTTP/1.1") + header.Add("test-case", rp.name) + for _, h := range rp.header { + header.Add(h.Name, h.Value) + } + + var synStreamFlags spdy.ControlFlags + if len(rp.body) == 0 { + synStreamFlags = spdy.ControlFlagFin + } + if err := st.spdyFr.WriteFrame(&spdy.SynStreamFrame{ + CFHeader: spdy.ControlFrameHeader{ + Flags: synStreamFlags, + }, + StreamId: id, + Headers: header, + }); err != nil { + return nil, err + } + + if len(rp.body) != 0 { + if err := st.spdyFr.WriteFrame(&spdy.DataFrame{ + StreamId: id, + Flags: spdy.DataFlagFin, + Data: rp.body, + }); err != nil { + return nil, err + } + } + +loop: + for { + fr, err := st.readSpdyFrame() + if err != nil { + return res, err + } + switch f := fr.(type) { + case *spdy.SynReplyFrame: + if f.StreamId != id { + break + } + res.header = cloneHeader(f.Headers) + if _, err := fmt.Sscan(res.header.Get(":status"), &res.status); err != nil { + return res, fmt.Errorf("Error parsing status code: %v", err) + } + if f.CFHeader.Flags&spdy.ControlFlagFin != 0 { + break loop + } + case *spdy.DataFrame: + if f.StreamId != id { + break + } + res.body = append(res.body, f.Data...) + if f.Flags&spdy.DataFlagFin != 0 { + break loop + } + case *spdy.RstStreamFrame: + if f.StreamId != id { + break + } + res.spdyRstErrCode = f.Status + break loop + case *spdy.GoAwayFrame: + if f.Status == spdy.GoAwayOK { + break + } + res.spdyGoAwayErrCode = f.Status + break loop + } + } + return res, nil +} + +func (st *serverTester) http2(rp requestParam) (*serverResponse, error) { + st.headerBlkBuf.Reset() + st.header = make(http.Header) + + var id uint32 + if rp.streamID != 0 { + id = rp.streamID + if id >= st.nextStreamID && id%2 == 1 { + st.nextStreamID = id + 2 + } + } else { + id = st.nextStreamID + st.nextStreamID += 2 + } + + if !st.h2PrefaceSent { + st.h2PrefaceSent = true + fmt.Fprint(st.conn, "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n") + if err := st.fr.WriteSettings(); err != nil { + return nil, err + } + } + + res := &serverResponse{ + streamID: id, + } + + streams := make(map[uint32]*serverResponse) + streams[id] = res + + if !rp.httpUpgrade { + method := "GET" + if rp.method != "" { + method = rp.method + } + _ = st.enc.WriteField(pair(":method", method)) + + scheme := "http" + if rp.scheme != "" { + scheme = rp.scheme + } + _ = st.enc.WriteField(pair(":scheme", scheme)) + + authority := st.authority + if rp.authority != "" { + authority = rp.authority + } + _ = st.enc.WriteField(pair(":authority", authority)) + + path := "/" + if rp.path != "" { + path = rp.path + } + _ = st.enc.WriteField(pair(":path", path)) + + _ = st.enc.WriteField(pair("test-case", rp.name)) + + for _, h := range rp.header { + _ = st.enc.WriteField(h) + } + + err := st.fr.WriteHeaders(http2.HeadersFrameParam{ + StreamID: id, + EndStream: len(rp.body) == 0 && len(rp.trailer) == 0, + EndHeaders: true, + BlockFragment: st.headerBlkBuf.Bytes(), + }) + if err != nil { + return nil, err + } + + if len(rp.body) != 0 { + // TODO we assume rp.body fits in 1 frame + if err := st.fr.WriteData(id, len(rp.trailer) == 0, rp.body); err != nil { + return nil, err + } + } + + if len(rp.trailer) != 0 { + st.headerBlkBuf.Reset() + for _, h := range rp.trailer { + _ = st.enc.WriteField(h) + } + err := st.fr.WriteHeaders(http2.HeadersFrameParam{ + StreamID: id, + EndStream: true, + EndHeaders: true, + BlockFragment: st.headerBlkBuf.Bytes(), + }) + if err != nil { + return nil, err + } + } + } +loop: + for { + fr, err := st.readFrame() + if err != nil { + return res, err + } + switch f := fr.(type) { + case *http2.HeadersFrame: + _, err := st.dec.Write(f.HeaderBlockFragment()) + if err != nil { + return res, err + } + sr, ok := streams[f.FrameHeader.StreamID] + if !ok { + st.header = make(http.Header) + break + } + sr.header = cloneHeader(st.header) + var status int + status, err = strconv.Atoi(sr.header.Get(":status")) + if err != nil { + return res, fmt.Errorf("Error parsing status code: %v", err) + } + sr.status = status + if f.StreamEnded() { + if streamEnded(res, streams, sr) { + break loop + } + } + case *http2.PushPromiseFrame: + _, err := st.dec.Write(f.HeaderBlockFragment()) + if err != nil { + return res, err + } + sr := &serverResponse{ + streamID: f.PromiseID, + reqHeader: cloneHeader(st.header), + } + streams[sr.streamID] = sr + case *http2.DataFrame: + sr, ok := streams[f.FrameHeader.StreamID] + if !ok { + break + } + sr.body = append(sr.body, f.Data()...) + if f.StreamEnded() { + if streamEnded(res, streams, sr) { + break loop + } + } + case *http2.RSTStreamFrame: + sr, ok := streams[f.FrameHeader.StreamID] + if !ok { + break + } + sr.errCode = f.ErrCode + if streamEnded(res, streams, sr) { + break loop + } + case *http2.GoAwayFrame: + if f.ErrCode == http2.ErrCodeNo { + break + } + res.errCode = f.ErrCode + res.connErr = true + break loop + case *http2.SettingsFrame: + if f.IsAck() { + break + } + if err := st.fr.WriteSettingsAck(); err != nil { + return res, err + } + } + } + sort.Sort(ByStreamID(res.pushResponse)) + return res, nil +} + +func streamEnded(mainSr *serverResponse, streams map[uint32]*serverResponse, sr *serverResponse) bool { + delete(streams, sr.streamID) + if mainSr.streamID != sr.streamID { + mainSr.pushResponse = append(mainSr.pushResponse, sr) + } + return len(streams) == 0 +} + +type serverResponse struct { + status int // HTTP status code + header http.Header // response header fields + body []byte // response body + streamID uint32 // stream ID in HTTP/2 + errCode http2.ErrCode // error code received in HTTP/2 RST_STREAM or GOAWAY + connErr bool // true if HTTP/2 connection error + spdyGoAwayErrCode spdy.GoAwayStatus // status code received in SPDY RST_STREAM + spdyRstErrCode spdy.RstStreamStatus // status code received in SPDY GOAWAY + connClose bool // Conection: close is included in response header in HTTP/1 test + reqHeader http.Header // http request header, currently only sotres pushed request header + pushResponse []*serverResponse // pushed response +} + +type ByStreamID []*serverResponse + +func (b ByStreamID) Len() int { + return len(b) +} + +func (b ByStreamID) Swap(i, j int) { + b[i], b[j] = b[j], b[i] +} + +func (b ByStreamID) Less(i, j int) bool { + return b[i].streamID < b[j].streamID +} + +func cloneHeader(h http.Header) http.Header { + h2 := make(http.Header, len(h)) + for k, vv := range h { + vv2 := make([]string, len(vv)) + copy(vv2, vv) + h2[k] = vv2 + } + return h2 +} + +func noopHandler(w http.ResponseWriter, r *http.Request) {} diff --git a/integration-tests/setenv b/integration-tests/setenv new file mode 100644 index 0000000..56879b9 --- /dev/null +++ b/integration-tests/setenv @@ -0,0 +1,6 @@ +#!/bin/sh -e + +export CGO_CFLAGS="-I/mnt/nghttp2/lib/includes -I/mnt/nghttp2/lib/includes" +export CGO_LDFLAGS="-L/mnt/nghttp2/lib/.libs" +export LD_LIBRARY_PATH="/mnt/nghttp2/lib/.libs" +"$@" diff --git a/integration-tests/setenv.in b/integration-tests/setenv.in new file mode 100644 index 0000000..2c2d3c9 --- /dev/null +++ b/integration-tests/setenv.in @@ -0,0 +1,6 @@ +#!/bin/sh -e + +export CGO_CFLAGS="-I@abs_top_srcdir@/lib/includes -I@abs_top_builddir@/lib/includes" +export CGO_LDFLAGS="-L@abs_top_builddir@/lib/.libs" +export LD_LIBRARY_PATH="@abs_top_builddir@/lib/.libs" +"$@" diff --git a/lib/Makefile.am b/lib/Makefile.am new file mode 100644 index 0000000..6d2393e --- /dev/null +++ b/lib/Makefile.am @@ -0,0 +1,67 @@ +# nghttp2 - HTTP/2 C Library + +# Copyright (c) 2012, 2013 Tatsuhiro Tsujikawa + +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: + +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. + +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +SUBDIRS = includes + +EXTRA_DIST = Makefile.msvc + +AM_CFLAGS = $(WARNCFLAGS) +AM_CPPFLAGS = -I$(srcdir)/includes -I$(builddir)/includes @DEFS@ + +pkgconfigdir = $(libdir)/pkgconfig +pkgconfig_DATA = libnghttp2.pc +DISTCLEANFILES = $(pkgconfig_DATA) + +lib_LTLIBRARIES = libnghttp2.la + +OBJECTS = nghttp2_pq.c nghttp2_map.c nghttp2_queue.c \ + nghttp2_frame.c \ + nghttp2_buf.c \ + nghttp2_stream.c nghttp2_outbound_item.c \ + nghttp2_session.c nghttp2_submit.c \ + nghttp2_helper.c \ + nghttp2_npn.c \ + nghttp2_hd.c nghttp2_hd_huffman.c nghttp2_hd_huffman_data.c \ + nghttp2_version.c \ + nghttp2_priority_spec.c \ + nghttp2_option.c \ + nghttp2_callbacks.c \ + nghttp2_mem.c \ + nghttp2_http.c + +HFILES = nghttp2_pq.h nghttp2_int.h nghttp2_map.h nghttp2_queue.h \ + nghttp2_frame.h \ + nghttp2_buf.h \ + nghttp2_session.h nghttp2_helper.h nghttp2_stream.h nghttp2_int.h \ + nghttp2_npn.h \ + nghttp2_submit.h nghttp2_outbound_item.h \ + nghttp2_net.h \ + nghttp2_hd.h nghttp2_hd_huffman.h \ + nghttp2_priority_spec.h \ + nghttp2_option.h \ + nghttp2_callbacks.h \ + nghttp2_mem.h \ + nghttp2_http.h + +libnghttp2_la_SOURCES = $(HFILES) $(OBJECTS) +libnghttp2_la_LDFLAGS = -no-undefined \ + -version-info $(LT_CURRENT):$(LT_REVISION):$(LT_AGE) diff --git a/lib/Makefile.in b/lib/Makefile.in new file mode 100644 index 0000000..dd1cc37 --- /dev/null +++ b/lib/Makefile.in @@ -0,0 +1,930 @@ +# Makefile.in generated by automake 1.14.1 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2013 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +# nghttp2 - HTTP/2 C Library + +# Copyright (c) 2012, 2013 Tatsuhiro Tsujikawa + +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: + +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. + + +VPATH = @srcdir@ +am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)' +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +target_triplet = @target@ +subdir = lib +DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \ + $(srcdir)/libnghttp2.pc.in $(top_srcdir)/depcomp +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/ax_boost_asio.m4 \ + $(top_srcdir)/m4/ax_boost_base.m4 \ + $(top_srcdir)/m4/ax_boost_system.m4 \ + $(top_srcdir)/m4/ax_boost_thread.m4 \ + $(top_srcdir)/m4/ax_check_compile_flag.m4 \ + $(top_srcdir)/m4/ax_cxx_compile_stdcxx_11.m4 \ + $(top_srcdir)/m4/ax_have_epoll.m4 \ + $(top_srcdir)/m4/ax_python_devel.m4 \ + $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/libxml2.m4 \ + $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ + $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = libnghttp2.pc +CONFIG_CLEAN_VPATH_FILES = +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +am__uninstall_files_from_dir = { \ + test -z "$$files" \ + || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ + || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ + $(am__cd) "$$dir" && rm -f $$files; }; \ + } +am__installdirs = "$(DESTDIR)$(libdir)" "$(DESTDIR)$(pkgconfigdir)" +LTLIBRARIES = $(lib_LTLIBRARIES) +libnghttp2_la_LIBADD = +am__objects_1 = +am__objects_2 = nghttp2_pq.lo nghttp2_map.lo nghttp2_queue.lo \ + nghttp2_frame.lo nghttp2_buf.lo nghttp2_stream.lo \ + nghttp2_outbound_item.lo nghttp2_session.lo nghttp2_submit.lo \ + nghttp2_helper.lo nghttp2_npn.lo nghttp2_hd.lo \ + nghttp2_hd_huffman.lo nghttp2_hd_huffman_data.lo \ + nghttp2_version.lo nghttp2_priority_spec.lo nghttp2_option.lo \ + nghttp2_callbacks.lo nghttp2_mem.lo nghttp2_http.lo +am_libnghttp2_la_OBJECTS = $(am__objects_1) $(am__objects_2) +libnghttp2_la_OBJECTS = $(am_libnghttp2_la_OBJECTS) +AM_V_lt = $(am__v_lt_@AM_V@) +am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) +am__v_lt_0 = --silent +am__v_lt_1 = +libnghttp2_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(libnghttp2_la_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__depfiles_maybe = depfiles +am__mv = mv -f +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CFLAGS) $(CFLAGS) +AM_V_CC = $(am__v_CC_@AM_V@) +am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) +am__v_CC_0 = @echo " CC " $@; +am__v_CC_1 = +CCLD = $(CC) +LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CCLD = $(am__v_CCLD_@AM_V@) +am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) +am__v_CCLD_0 = @echo " CCLD " $@; +am__v_CCLD_1 = +SOURCES = $(libnghttp2_la_SOURCES) +DIST_SOURCES = $(libnghttp2_la_SOURCES) +RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \ + ctags-recursive dvi-recursive html-recursive info-recursive \ + install-data-recursive install-dvi-recursive \ + install-exec-recursive install-html-recursive \ + install-info-recursive install-pdf-recursive \ + install-ps-recursive install-recursive installcheck-recursive \ + installdirs-recursive pdf-recursive ps-recursive \ + tags-recursive uninstall-recursive +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +DATA = $(pkgconfig_DATA) +RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \ + distclean-recursive maintainer-clean-recursive +am__recursive_targets = \ + $(RECURSIVE_TARGETS) \ + $(RECURSIVE_CLEAN_TARGETS) \ + $(am__extra_recursive_targets) +AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \ + distdir +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +ETAGS = etags +CTAGS = ctags +DIST_SUBDIRS = $(SUBDIRS) +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +am__relativize = \ + dir0=`pwd`; \ + sed_first='s,^\([^/]*\)/.*$$,\1,'; \ + sed_rest='s,^[^/]*/*,,'; \ + sed_last='s,^.*/\([^/]*\)$$,\1,'; \ + sed_butlast='s,/*[^/]*$$,,'; \ + while test -n "$$dir1"; do \ + first=`echo "$$dir1" | sed -e "$$sed_first"`; \ + if test "$$first" != "."; then \ + if test "$$first" = ".."; then \ + dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \ + dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \ + else \ + first2=`echo "$$dir2" | sed -e "$$sed_first"`; \ + if test "$$first2" = "$$first"; then \ + dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \ + else \ + dir2="../$$dir2"; \ + fi; \ + dir0="$$dir0"/"$$first"; \ + fi; \ + fi; \ + dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \ + done; \ + reldir="$$dir2" +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +APPLDFLAGS = @APPLDFLAGS@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +BOOST_ASIO_LIB = @BOOST_ASIO_LIB@ +BOOST_CPPFLAGS = @BOOST_CPPFLAGS@ +BOOST_LDFLAGS = @BOOST_LDFLAGS@ +BOOST_SYSTEM_LIB = @BOOST_SYSTEM_LIB@ +BOOST_THREAD_LIB = @BOOST_THREAD_LIB@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CUNIT_CFLAGS = @CUNIT_CFLAGS@ +CUNIT_LIBS = @CUNIT_LIBS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +CYTHON = @CYTHON@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DLLTOOL = @DLLTOOL@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +GREP = @GREP@ +HAVE_CXX11 = @HAVE_CXX11@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +JANSSON_CFLAGS = @JANSSON_CFLAGS@ +JANSSON_LIBS = @JANSSON_LIBS@ +JEMALLOC_LIBS = @JEMALLOC_LIBS@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBEVENT_OPENSSL_CFLAGS = @LIBEVENT_OPENSSL_CFLAGS@ +LIBEVENT_OPENSSL_LIBS = @LIBEVENT_OPENSSL_LIBS@ +LIBEV_CFLAGS = @LIBEV_CFLAGS@ +LIBEV_LIBS = @LIBEV_LIBS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBSPDYLAY_CFLAGS = @LIBSPDYLAY_CFLAGS@ +LIBSPDYLAY_LIBS = @LIBSPDYLAY_LIBS@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +LT_AGE = @LT_AGE@ +LT_CURRENT = @LT_CURRENT@ +LT_REVISION = @LT_REVISION@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MKDIR_P = @MKDIR_P@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OPENSSL_CFLAGS = @OPENSSL_CFLAGS@ +OPENSSL_LIBS = @OPENSSL_LIBS@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PACKAGE_VERSION_NUM = @PACKAGE_VERSION_NUM@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PKG_CONFIG = @PKG_CONFIG@ +PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ +PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ +PYTHON = @PYTHON@ +PYTHON_CPPFLAGS = @PYTHON_CPPFLAGS@ +PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@ +PYTHON_EXTRA_LDFLAGS = @PYTHON_EXTRA_LDFLAGS@ +PYTHON_EXTRA_LIBS = @PYTHON_EXTRA_LIBS@ +PYTHON_LDFLAGS = @PYTHON_LDFLAGS@ +PYTHON_PLATFORM = @PYTHON_PLATFORM@ +PYTHON_PREFIX = @PYTHON_PREFIX@ +PYTHON_SITE_PKG = @PYTHON_SITE_PKG@ +PYTHON_VERSION = @PYTHON_VERSION@ +RANLIB = @RANLIB@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +TESTLDADD = @TESTLDADD@ +VERSION = @VERSION@ +WARNCFLAGS = @WARNCFLAGS@ +XML2_CONFIG = @XML2_CONFIG@ +XML_CPPFLAGS = @XML_CPPFLAGS@ +XML_LIBS = @XML_LIBS@ +ZLIB_CFLAGS = @ZLIB_CFLAGS@ +ZLIB_LIBS = @ZLIB_LIBS@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +pkgpyexecdir = @pkgpyexecdir@ +pkgpythondir = @pkgpythondir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +pyexecdir = @pyexecdir@ +pythondir = @pythondir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target = @target@ +target_alias = @target_alias@ +target_cpu = @target_cpu@ +target_os = @target_os@ +target_vendor = @target_vendor@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ + +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +SUBDIRS = includes +EXTRA_DIST = Makefile.msvc +AM_CFLAGS = $(WARNCFLAGS) +AM_CPPFLAGS = -I$(srcdir)/includes -I$(builddir)/includes @DEFS@ +pkgconfigdir = $(libdir)/pkgconfig +pkgconfig_DATA = libnghttp2.pc +DISTCLEANFILES = $(pkgconfig_DATA) +lib_LTLIBRARIES = libnghttp2.la +OBJECTS = nghttp2_pq.c nghttp2_map.c nghttp2_queue.c \ + nghttp2_frame.c \ + nghttp2_buf.c \ + nghttp2_stream.c nghttp2_outbound_item.c \ + nghttp2_session.c nghttp2_submit.c \ + nghttp2_helper.c \ + nghttp2_npn.c \ + nghttp2_hd.c nghttp2_hd_huffman.c nghttp2_hd_huffman_data.c \ + nghttp2_version.c \ + nghttp2_priority_spec.c \ + nghttp2_option.c \ + nghttp2_callbacks.c \ + nghttp2_mem.c \ + nghttp2_http.c + +HFILES = nghttp2_pq.h nghttp2_int.h nghttp2_map.h nghttp2_queue.h \ + nghttp2_frame.h \ + nghttp2_buf.h \ + nghttp2_session.h nghttp2_helper.h nghttp2_stream.h nghttp2_int.h \ + nghttp2_npn.h \ + nghttp2_submit.h nghttp2_outbound_item.h \ + nghttp2_net.h \ + nghttp2_hd.h nghttp2_hd_huffman.h \ + nghttp2_priority_spec.h \ + nghttp2_option.h \ + nghttp2_callbacks.h \ + nghttp2_mem.h \ + nghttp2_http.h + +libnghttp2_la_SOURCES = $(HFILES) $(OBJECTS) +libnghttp2_la_LDFLAGS = -no-undefined \ + -version-info $(LT_CURRENT):$(LT_REVISION):$(LT_AGE) + +all: all-recursive + +.SUFFIXES: +.SUFFIXES: .c .lo .o .obj +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu lib/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu lib/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): +libnghttp2.pc: $(top_builddir)/config.status $(srcdir)/libnghttp2.pc.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ + +install-libLTLIBRARIES: $(lib_LTLIBRARIES) + @$(NORMAL_INSTALL) + @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \ + list2=; for p in $$list; do \ + if test -f $$p; then \ + list2="$$list2 $$p"; \ + else :; fi; \ + done; \ + test -z "$$list2" || { \ + echo " $(MKDIR_P) '$(DESTDIR)$(libdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(libdir)" || exit 1; \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(libdir)'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(libdir)"; \ + } + +uninstall-libLTLIBRARIES: + @$(NORMAL_UNINSTALL) + @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \ + for p in $$list; do \ + $(am__strip_dir) \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$f'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$f"; \ + done + +clean-libLTLIBRARIES: + -test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES) + @list='$(lib_LTLIBRARIES)'; \ + locs=`for p in $$list; do echo $$p; done | \ + sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ + sort -u`; \ + test -z "$$locs" || { \ + echo rm -f $${locs}; \ + rm -f $${locs}; \ + } + +libnghttp2.la: $(libnghttp2_la_OBJECTS) $(libnghttp2_la_DEPENDENCIES) $(EXTRA_libnghttp2_la_DEPENDENCIES) + $(AM_V_CCLD)$(libnghttp2_la_LINK) -rpath $(libdir) $(libnghttp2_la_OBJECTS) $(libnghttp2_la_LIBADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_buf.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_callbacks.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_frame.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_hd.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_hd_huffman.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_hd_huffman_data.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_helper.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_http.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_map.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_mem.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_npn.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_option.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_outbound_item.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_pq.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_priority_spec.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_queue.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_session.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_stream.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_submit.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_version.Plo@am__quote@ + +.c.o: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\ +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< + +.c.obj: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\ +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.c.lo: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\ +@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs +install-pkgconfigDATA: $(pkgconfig_DATA) + @$(NORMAL_INSTALL) + @list='$(pkgconfig_DATA)'; test -n "$(pkgconfigdir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(pkgconfigdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(pkgconfigdir)" || exit 1; \ + fi; \ + for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + echo "$$d$$p"; \ + done | $(am__base_list) | \ + while read files; do \ + echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(pkgconfigdir)'"; \ + $(INSTALL_DATA) $$files "$(DESTDIR)$(pkgconfigdir)" || exit $$?; \ + done + +uninstall-pkgconfigDATA: + @$(NORMAL_UNINSTALL) + @list='$(pkgconfig_DATA)'; test -n "$(pkgconfigdir)" || list=; \ + files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ + dir='$(DESTDIR)$(pkgconfigdir)'; $(am__uninstall_files_from_dir) + +# This directory's subdirectories are mostly independent; you can cd +# into them and run 'make' without going through this Makefile. +# To change the values of 'make' variables: instead of editing Makefiles, +# (1) if the variable is set in 'config.status', edit 'config.status' +# (which will cause the Makefiles to be regenerated when you run 'make'); +# (2) otherwise, pass the desired values on the 'make' command line. +$(am__recursive_targets): + @fail=; \ + if $(am__make_keepgoing); then \ + failcom='fail=yes'; \ + else \ + failcom='exit 1'; \ + fi; \ + dot_seen=no; \ + target=`echo $@ | sed s/-recursive//`; \ + case "$@" in \ + distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ + *) list='$(SUBDIRS)' ;; \ + esac; \ + for subdir in $$list; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + dot_seen=yes; \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || eval $$failcom; \ + done; \ + if test "$$dot_seen" = "no"; then \ + $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ + fi; test -z "$$fail" + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-recursive +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \ + include_option=--etags-include; \ + empty_fix=.; \ + else \ + include_option=--include; \ + empty_fix=; \ + fi; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + test ! -f $$subdir/TAGS || \ + set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \ + fi; \ + done; \ + $(am__define_uniq_tagged_files); \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: ctags-recursive + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" +cscopelist: cscopelist-recursive + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done + @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + $(am__make_dryrun) \ + || test -d "$(distdir)/$$subdir" \ + || $(MKDIR_P) "$(distdir)/$$subdir" \ + || exit 1; \ + dir1=$$subdir; dir2="$(distdir)/$$subdir"; \ + $(am__relativize); \ + new_distdir=$$reldir; \ + dir1=$$subdir; dir2="$(top_distdir)"; \ + $(am__relativize); \ + new_top_distdir=$$reldir; \ + echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \ + echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \ + ($(am__cd) $$subdir && \ + $(MAKE) $(AM_MAKEFLAGS) \ + top_distdir="$$new_top_distdir" \ + distdir="$$new_distdir" \ + am__remove_distdir=: \ + am__skip_length_check=: \ + am__skip_mode_fix=: \ + distdir) \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-recursive +all-am: Makefile $(LTLIBRARIES) $(DATA) +installdirs: installdirs-recursive +installdirs-am: + for dir in "$(DESTDIR)$(libdir)" "$(DESTDIR)$(pkgconfigdir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: install-recursive +install-exec: install-exec-recursive +install-data: install-data-recursive +uninstall: uninstall-recursive + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-recursive +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + -test -z "$(DISTCLEANFILES)" || rm -f $(DISTCLEANFILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-recursive + +clean-am: clean-generic clean-libLTLIBRARIES clean-libtool \ + mostlyclean-am + +distclean: distclean-recursive + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-recursive + +dvi-am: + +html: html-recursive + +html-am: + +info: info-recursive + +info-am: + +install-data-am: install-pkgconfigDATA + +install-dvi: install-dvi-recursive + +install-dvi-am: + +install-exec-am: install-libLTLIBRARIES + +install-html: install-html-recursive + +install-html-am: + +install-info: install-info-recursive + +install-info-am: + +install-man: + +install-pdf: install-pdf-recursive + +install-pdf-am: + +install-ps: install-ps-recursive + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-recursive + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-recursive + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-recursive + +pdf-am: + +ps: ps-recursive + +ps-am: + +uninstall-am: uninstall-libLTLIBRARIES uninstall-pkgconfigDATA + +.MAKE: $(am__recursive_targets) install-am install-strip + +.PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am check \ + check-am clean clean-generic clean-libLTLIBRARIES \ + clean-libtool cscopelist-am ctags ctags-am distclean \ + distclean-compile distclean-generic distclean-libtool \ + distclean-tags distdir dvi dvi-am html html-am info info-am \ + install install-am install-data install-data-am install-dvi \ + install-dvi-am install-exec install-exec-am install-html \ + install-html-am install-info install-info-am \ + install-libLTLIBRARIES install-man install-pdf install-pdf-am \ + install-pkgconfigDATA install-ps install-ps-am install-strip \ + installcheck installcheck-am installdirs installdirs-am \ + maintainer-clean maintainer-clean-generic mostlyclean \ + mostlyclean-compile mostlyclean-generic mostlyclean-libtool \ + pdf pdf-am ps ps-am tags tags-am uninstall uninstall-am \ + uninstall-libLTLIBRARIES uninstall-pkgconfigDATA + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/lib/Makefile.msvc b/lib/Makefile.msvc new file mode 100644 index 0000000..edc6d74 --- /dev/null +++ b/lib/Makefile.msvc @@ -0,0 +1,249 @@ +# +# GNU Makefile for nghttp2 / MSVC. +# +# By G. Vanem 2013 +# Updated 3/2015 by Remo Eichenberger @remoe +# The MIT License apply. +# + +# +# Choose your weapons: +# Set 'USE_CYTHON=1' to build and install the 'nghttp2.pyd' Python extension. +# +USE_CYTHON = 1 + +_VERSION := $(shell grep AC_INIT ../configure.ac | cut -d'[' -f3 | sed -e 's/-DEV], //g') +_VERSION := $(subst ., ,$(_VERSION)) +VER_MAJOR = $(word 1,$(_VERSION)) +VER_MINOR = $(word 2,$(_VERSION)) +VER_MICRO = $(word 3,$(_VERSION)) +VERSION = $(VER_MAJOR).$(VER_MINOR).$(VER_MICRO) +VERSION_NUM = ($(VER_MAJOR) << 16) + ($(VER_MINOR) << 8) + $(VER_MICRO) + +GENERATED = 'Generated by $(realpath Makefile.MSVC)' + +# +# Where to copy nghttp2.dll + lib + headers to. +# Note: 'make install' is not in default targets. Do it explicitly. +# +VC_ROOT = $(realpath $(VCINSTALLDIR)) +INSTALL_BIN = $(VC_ROOT)/bin +INSTALL_LIB = $(VC_ROOT)/lib +INSTALL_HDR = $(VC_ROOT)/include + +# +# Build for DEBUG-model and RELEASE at the same time. +# +TARGETS = nghttp2.lib nghttp2.dll nghttp2_imp.lib \ + nghttp2d.lib nghttp2d.dll nghttp2d_imp.lib + +EXT_LIBS = + +OBJ_DIR = MSVC_obj + +NGHTTP2_PDB_R = $(OBJ_DIR)/nghttp2.pdb +NGHTTP2_PDB_D = $(OBJ_DIR)/nghttp2d.pdb + +CC = cl +CFLAGS = -I./includes -Dssize_t=long -D_U_="" + +CFLAGS_R = -nologo -MD -W3 -Zi -Fd./$(NGHTTP2_PDB_R) +CFLAGS_D = -nologo -MDd -W3 -Zi -Fd./$(NGHTTP2_PDB_D) \ + -Ot -D_DEBUG -GF -RTCs -RTCu # -RTCc -GS + +LDFLAGS = -nologo -machine:x64 -map -debug -incremental:no # -verbose + +NGHTTP2_SRC = nghttp2_pq.c \ + nghttp2_map.c \ + nghttp2_queue.c \ + nghttp2_frame.c \ + nghttp2_buf.c \ + nghttp2_stream.c \ + nghttp2_outbound_item.c \ + nghttp2_session.c \ + nghttp2_submit.c \ + nghttp2_helper.c \ + nghttp2_npn.c \ + nghttp2_hd.c \ + nghttp2_hd_huffman.c \ + nghttp2_hd_huffman_data.c \ + nghttp2_version.c \ + nghttp2_priority_spec.c \ + nghttp2_option.c \ + nghttp2_callbacks.c \ + nghttp2_mem.c \ + nghttp2_http.c + +NGHTTP2_OBJ_R = $(addprefix $(OBJ_DIR)/r_, $(notdir $(NGHTTP2_SRC:.c=.obj))) +NGHTTP2_OBJ_D = $(addprefix $(OBJ_DIR)/d_, $(notdir $(NGHTTP2_SRC:.c=.obj))) + +all: intro $(OBJ_DIR) $(TARGETS) + @echo 'Welcome to NgHTTP2 (release + debug).' + @echo 'Do a "make -f Makefile.MSVC install" at own risk!' + +intro: + @echo 'Building NgHTTP (MSVC) ver. "$(VERSION)".' + +test_ver: + @echo '$$(VERSION): "$(VERSION)".' + @echo '$$(_VERSION): "$(_VERSION)".' + @echo '$$(VER_MAJOR): "$(VER_MAJOR)".' + @echo '$$(VER_MINOR): "$(VER_MINOR)".' + @echo '$$(VER_MICRO): "$(VER_MICRO)".' + +$(OBJ_DIR): + - mkdir $(OBJ_DIR) + +install: includes/nghttp2/nghttp2.h includes/nghttp2/nghttp2ver.h \ + nghttp2.dll nghttp2.lib nghttp2_imp.lib \ + nghttp2d.dll nghttp2d.lib nghttp2d_imp.lib \ + copy_headers_and_libs install_nghttp2_pyd_$(USE_CYTHON) + +# +# This MUST be done before using the 'install_nghttp2_pyd_1' rule. +# +copy_headers_and_libs: + - mkdir $(INSTALL_HDR)/nghttp2 + cp --update $(addprefix includes/nghttp2/, nghttp2.h nghttp2ver.h) $(INSTALL_HDR)/nghttp2 + cp --update nghttp2.dll nghttp2d.dll $(NGHTTP2_PDB_R) $(NGHTTP2_PDB_D) $(INSTALL_BIN) + cp --update nghttp2.lib nghttp2d.lib nghttp2_imp.lib nghttp2d_imp.lib $(INSTALL_LIB) + @echo + +nghttp2.lib: $(NGHTTP2_OBJ_R) + lib -nologo -out:$@ $^ + @echo + +nghttp2d.lib: $(NGHTTP2_OBJ_D) + lib -nologo -out:$@ $^ + @echo + +nghttp2.dll nghttp2_imp.lib: $(NGHTTP2_OBJ_R) $(OBJ_DIR)/r_nghttp2.res + link $(LDFLAGS) -dll -out:nghttp2.dll -implib:nghttp2_imp.lib \ + $(NGHTTP2_OBJ_R) $(OBJ_DIR)/r_nghttp2.res $(EXT_LIBS) + @echo + +nghttp2d.dll nghttp2d_imp.lib: $(NGHTTP2_OBJ_D) $(OBJ_DIR)/d_nghttp2.res + link $(LDFLAGS) -dll -out:nghttp2d.dll -implib:nghttp2d_imp.lib \ + $(NGHTTP2_OBJ_D) $(OBJ_DIR)/d_nghttp2.res $(EXT_LIBS) + @echo + +install_nghttp2_pyd_0: ; + +install_nghttp2_pyd_1: $(addprefix ../python/, setup.py.in nghttp2.pyx) + cd ../python ; \ + echo '# $(GENERATED). DO NOT EDIT.' > setup.py ; \ + sed -e 's/@top_srcdir@/../' \ + -e 's/@top_builddir@/../' \ + -e 's/@PACKAGE_VERSION@/$(VERSION)/' setup.py.in >> setup.py ; \ + cython -v nghttp2.pyx ; \ + python setup.py install + +clean_nghttp2_pyd_0: ; + +clean_nghttp2_pyd_1: + cd ../python ; \ + rm -f setup.py nghttp2.c ; \ + rm -fR build/* + +$(OBJ_DIR)/r_%.obj: %.c + $(CC) $(CFLAGS_R) $(CFLAGS) -Fo$@ -c $< + @echo + +$(OBJ_DIR)/d_%.obj: %.c + $(CC) $(CFLAGS_D) $(CFLAGS) -Fo$@ -c $< + @echo + +$(OBJ_DIR)/r_nghttp2.res: nghttp2.rc + rc -nologo -D_RELEASE -Fo $@ $< + @echo + +$(OBJ_DIR)/d_nghttp2.res: nghttp2.rc + rc -nologo -D_DEBUG -Fo $@ $< + @echo + +includes/nghttp2/nghttp2ver.h: includes/nghttp2/nghttp2ver.h.in + sed < includes/nghttp2/nghttp2ver.h.in \ + -e 's/@PACKAGE_VERSION@/$(VERSION)/g' \ + -e 's/@PACKAGE_VERSION_NUM@/($(VERSION_NUM))/g' > $@ + touch --reference=includes/nghttp2/nghttp2ver.h.in $@ + + +define RES_FILE + #include + + VS_VERSION_INFO VERSIONINFO + FILEVERSION $(VER_MAJOR), $(VER_MINOR), $(VER_MICRO), 0 + PRODUCTVERSION $(VER_MAJOR), $(VER_MINOR), $(VER_MICRO), 0 + FILEFLAGSMASK 0x3fL + FILEOS 0x40004L + FILETYPE 0x2L + FILESUBTYPE 0x0L + #ifdef _DEBUG + #define VER_STR "$(VERSION).0 (MSVC debug)" + #define DBG "d" + FILEFLAGS 0x1L + #else + #define VER_STR "$(VERSION).0 (MSVC release)" + #define DBG "" + FILEFLAGS 0x0L + #endif + BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "CompanyName", "http://tatsuhiro-t.github.io/nghttp2/" + VALUE "FileDescription", "nghttp2; HTTP/2 C library" + VALUE "FileVersion", VER_STR + VALUE "InternalName", "nghttp2" DBG + VALUE "LegalCopyright", "The MIT License" + VALUE "LegalTrademarks", "" + VALUE "OriginalFilename", "nghttp2" DBG ".dll" + VALUE "ProductName", "NGHTTP2." + VALUE "ProductVersion", VER_STR + VALUE "PrivateBuild", "The privat build of ." + VALUE "SpecialBuild", "" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END + END +endef + +export RES_FILE + +nghttp2.rc: Makefile.MSVC + @echo 'Generating $@...' + @echo ' /* $(GENERATED). DO NOT EDIT.' > $@ + @echo ' */' >> $@ + @echo "$$RES_FILE" >> $@ + +clean: + rm -f $(OBJ_DIR)/* nghttp2_imp.exp nghttp2_imp.exp \ + nghttp2.map nghttp2d.map nghttp2.rc includes/nghttp2/nghttp2ver.h + @echo + +vclean realclean: clean clean_nghttp2_pyd_$(USE_CYTHON) + rm -f $(TARGETS) nghttp2.pdb nghttp2d.pdb nghttp2_imp.exp nghttp2d_imp.exp .depend.MSVC + - rmdir $(OBJ_DIR) + +# +# Use gcc to generated the dependencies. No MSVC specific args please! +# +REPLACE_R = 's/\(.*\)\.o: /\n$$(OBJ_DIR)\/r_\1.obj: /' +REPLACE_D = 's/\(.*\)\.o: /\n$$(OBJ_DIR)\/d_\1.obj: /' + +depend: includes/nghttp2/nghttp2ver.h + @echo '# $(GENERATED). DO NOT EDIT.' > .depend.MSVC + gcc -MM $(CFLAGS) $(NGHTTP2_SRC) >> .depend.tmp + @echo '#' >> .depend.MSVC + @echo '# Release lib objects:' >> .depend.MSVC + sed -e $(REPLACE_R) .depend.tmp >> .depend.MSVC + @echo '#' >> .depend.MSVC + @echo '# Debug lib objects:' >> .depend.MSVC + sed -e $(REPLACE_D) .depend.tmp >> .depend.MSVC + rm -f .depend.tmp + +-include .depend.MSVC diff --git a/lib/includes/Makefile.am b/lib/includes/Makefile.am new file mode 100644 index 0000000..80af63c --- /dev/null +++ b/lib/includes/Makefile.am @@ -0,0 +1,23 @@ +# nghttp2 - HTTP/2 C Library + +# Copyright (c) 2012 Tatsuhiro Tsujikawa + +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: + +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. + +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +nobase_include_HEADERS = nghttp2/nghttp2.h nghttp2/nghttp2ver.h diff --git a/lib/includes/Makefile.in b/lib/includes/Makefile.in new file mode 100644 index 0000000..f2d93d8 --- /dev/null +++ b/lib/includes/Makefile.in @@ -0,0 +1,636 @@ +# Makefile.in generated by automake 1.14.1 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2013 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +# nghttp2 - HTTP/2 C Library + +# Copyright (c) 2012 Tatsuhiro Tsujikawa + +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: + +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. + +VPATH = @srcdir@ +am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)' +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +target_triplet = @target@ +subdir = lib/includes +DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \ + $(nobase_include_HEADERS) +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/ax_boost_asio.m4 \ + $(top_srcdir)/m4/ax_boost_base.m4 \ + $(top_srcdir)/m4/ax_boost_system.m4 \ + $(top_srcdir)/m4/ax_boost_thread.m4 \ + $(top_srcdir)/m4/ax_check_compile_flag.m4 \ + $(top_srcdir)/m4/ax_cxx_compile_stdcxx_11.m4 \ + $(top_srcdir)/m4/ax_have_epoll.m4 \ + $(top_srcdir)/m4/ax_python_devel.m4 \ + $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/libxml2.m4 \ + $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ + $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +SOURCES = +DIST_SOURCES = +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +am__uninstall_files_from_dir = { \ + test -z "$$files" \ + || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ + || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ + $(am__cd) "$$dir" && rm -f $$files; }; \ + } +am__installdirs = "$(DESTDIR)$(includedir)" +HEADERS = $(nobase_include_HEADERS) +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +ETAGS = etags +CTAGS = ctags +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +APPLDFLAGS = @APPLDFLAGS@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +BOOST_ASIO_LIB = @BOOST_ASIO_LIB@ +BOOST_CPPFLAGS = @BOOST_CPPFLAGS@ +BOOST_LDFLAGS = @BOOST_LDFLAGS@ +BOOST_SYSTEM_LIB = @BOOST_SYSTEM_LIB@ +BOOST_THREAD_LIB = @BOOST_THREAD_LIB@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CUNIT_CFLAGS = @CUNIT_CFLAGS@ +CUNIT_LIBS = @CUNIT_LIBS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +CYTHON = @CYTHON@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DLLTOOL = @DLLTOOL@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +GREP = @GREP@ +HAVE_CXX11 = @HAVE_CXX11@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +JANSSON_CFLAGS = @JANSSON_CFLAGS@ +JANSSON_LIBS = @JANSSON_LIBS@ +JEMALLOC_LIBS = @JEMALLOC_LIBS@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBEVENT_OPENSSL_CFLAGS = @LIBEVENT_OPENSSL_CFLAGS@ +LIBEVENT_OPENSSL_LIBS = @LIBEVENT_OPENSSL_LIBS@ +LIBEV_CFLAGS = @LIBEV_CFLAGS@ +LIBEV_LIBS = @LIBEV_LIBS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBSPDYLAY_CFLAGS = @LIBSPDYLAY_CFLAGS@ +LIBSPDYLAY_LIBS = @LIBSPDYLAY_LIBS@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +LT_AGE = @LT_AGE@ +LT_CURRENT = @LT_CURRENT@ +LT_REVISION = @LT_REVISION@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MKDIR_P = @MKDIR_P@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OPENSSL_CFLAGS = @OPENSSL_CFLAGS@ +OPENSSL_LIBS = @OPENSSL_LIBS@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PACKAGE_VERSION_NUM = @PACKAGE_VERSION_NUM@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PKG_CONFIG = @PKG_CONFIG@ +PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ +PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ +PYTHON = @PYTHON@ +PYTHON_CPPFLAGS = @PYTHON_CPPFLAGS@ +PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@ +PYTHON_EXTRA_LDFLAGS = @PYTHON_EXTRA_LDFLAGS@ +PYTHON_EXTRA_LIBS = @PYTHON_EXTRA_LIBS@ +PYTHON_LDFLAGS = @PYTHON_LDFLAGS@ +PYTHON_PLATFORM = @PYTHON_PLATFORM@ +PYTHON_PREFIX = @PYTHON_PREFIX@ +PYTHON_SITE_PKG = @PYTHON_SITE_PKG@ +PYTHON_VERSION = @PYTHON_VERSION@ +RANLIB = @RANLIB@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +TESTLDADD = @TESTLDADD@ +VERSION = @VERSION@ +WARNCFLAGS = @WARNCFLAGS@ +XML2_CONFIG = @XML2_CONFIG@ +XML_CPPFLAGS = @XML_CPPFLAGS@ +XML_LIBS = @XML_LIBS@ +ZLIB_CFLAGS = @ZLIB_CFLAGS@ +ZLIB_LIBS = @ZLIB_LIBS@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +pkgpyexecdir = @pkgpyexecdir@ +pkgpythondir = @pkgpythondir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +pyexecdir = @pyexecdir@ +pythondir = @pythondir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target = @target@ +target_alias = @target_alias@ +target_cpu = @target_cpu@ +target_os = @target_os@ +target_vendor = @target_vendor@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ + +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +nobase_include_HEADERS = nghttp2/nghttp2.h nghttp2/nghttp2ver.h +all: all-am + +.SUFFIXES: +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu lib/includes/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu lib/includes/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs +install-nobase_includeHEADERS: $(nobase_include_HEADERS) + @$(NORMAL_INSTALL) + @list='$(nobase_include_HEADERS)'; test -n "$(includedir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(includedir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(includedir)" || exit 1; \ + fi; \ + $(am__nobase_list) | while read dir files; do \ + xfiles=; for file in $$files; do \ + if test -f "$$file"; then xfiles="$$xfiles $$file"; \ + else xfiles="$$xfiles $(srcdir)/$$file"; fi; done; \ + test -z "$$xfiles" || { \ + test "x$$dir" = x. || { \ + echo " $(MKDIR_P) '$(DESTDIR)$(includedir)/$$dir'"; \ + $(MKDIR_P) "$(DESTDIR)$(includedir)/$$dir"; }; \ + echo " $(INSTALL_HEADER) $$xfiles '$(DESTDIR)$(includedir)/$$dir'"; \ + $(INSTALL_HEADER) $$xfiles "$(DESTDIR)$(includedir)/$$dir" || exit $$?; }; \ + done + +uninstall-nobase_includeHEADERS: + @$(NORMAL_UNINSTALL) + @list='$(nobase_include_HEADERS)'; test -n "$(includedir)" || list=; \ + $(am__nobase_strip_setup); files=`$(am__nobase_strip)`; \ + dir='$(DESTDIR)$(includedir)'; $(am__uninstall_files_from_dir) + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-am +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + $(am__define_uniq_tagged_files); \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: ctags-am + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" +cscopelist: cscopelist-am + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(HEADERS) +installdirs: + for dir in "$(DESTDIR)$(includedir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic clean-libtool mostlyclean-am + +distclean: distclean-am + -rm -f Makefile +distclean-am: clean-am distclean-generic distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: install-nobase_includeHEADERS + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-generic mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-nobase_includeHEADERS + +.MAKE: install-am install-strip + +.PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ + clean-libtool cscopelist-am ctags ctags-am distclean \ + distclean-generic distclean-libtool distclean-tags distdir dvi \ + dvi-am html html-am info info-am install install-am \ + install-data install-data-am install-dvi install-dvi-am \ + install-exec install-exec-am install-html install-html-am \ + install-info install-info-am install-man \ + install-nobase_includeHEADERS install-pdf install-pdf-am \ + install-ps install-ps-am install-strip installcheck \ + installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-generic \ + mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \ + uninstall-am uninstall-nobase_includeHEADERS + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/lib/includes/nghttp2/nghttp2.h b/lib/includes/nghttp2/nghttp2.h new file mode 100644 index 0000000..5f5fdab --- /dev/null +++ b/lib/includes/nghttp2/nghttp2.h @@ -0,0 +1,3860 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2013, 2014 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGHTTP2_H +#define NGHTTP2_H + +/* Define WIN32 when build target is Win32 API (borrowed from + libcurl) */ +#if (defined(_WIN32) || defined(__WIN32__)) && !defined(WIN32) +#define WIN32 +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include + +#include + +#ifdef NGHTTP2_STATICLIB +#define NGHTTP2_EXTERN +#elif defined(WIN32) +#define NGHTTP2_EXTERN __declspec(dllexport) +#else /* !defined(WIN32) */ +#define NGHTTP2_EXTERN +#endif /* !defined(WIN32) */ + +/** + * @macro + * + * The protocol version identification string of this library + * supports. This identifier is used if HTTP/2 is used over TLS. + */ +#define NGHTTP2_PROTO_VERSION_ID "h2" +/** + * @macro + * + * The length of :macro:`NGHTTP2_PROTO_VERSION_ID`. + */ +#define NGHTTP2_PROTO_VERSION_ID_LEN 2 + +/** + * @macro + * + * The seriazlied form of ALPN protocol identifier this library + * supports. Notice that first byte is the length of following + * protocol identifier. This is the same wire format of `TLS ALPN + * extension `_. This is useful + * to process incoming ALPN tokens in wire format. + */ +#define NGHTTP2_PROTO_ALPN "\x2h2" + +/** + * @macro + * + * The length of :macro:`NGHTTP2_PROTO_ALPN`. + */ +#define NGHTTP2_PROTO_ALPN_LEN (sizeof(NGHTTP2_PROTO_ALPN) - 1) + +/** + * @macro + * + * The protocol version identification string of this library + * supports. This identifier is used if HTTP/2 is used over cleartext + * TCP. + */ +#define NGHTTP2_CLEARTEXT_PROTO_VERSION_ID "h2c" + +/** + * @macro + * + * The length of :macro:`NGHTTP2_CLEARTEXT_PROTO_VERSION_ID`. + */ +#define NGHTTP2_CLEARTEXT_PROTO_VERSION_ID_LEN 3 + +struct nghttp2_session; +/** + * @struct + * + * The primary structure to hold the resources needed for a HTTP/2 + * session. The details of this structure are intentionally hidden + * from the public API. + */ +typedef struct nghttp2_session nghttp2_session; + +/** + * @macro + * + * The age of :type:`nghttp2_info` + */ +#define NGHTTP2_VERSION_AGE 1 + +/** + * @struct + * + * This struct is what `nghttp2_version()` returns. It holds + * information about the particular nghttp2 version. + */ +typedef struct { + /** + * Age of this struct. This instance of nghttp2 sets it to + * :macro:`NGHTTP2_VERSION_AGE` but a future version may bump it and + * add more struct fields at the bottom + */ + int age; + /** + * the :macro:`NGHTTP2_VERSION_NUM` number (since age ==1) + */ + int version_num; + /** + * points to the :macro:`NGHTTP2_VERSION` string (since age ==1) + */ + const char *version_str; + /** + * points to the :macro:`NGHTTP2_PROTO_VERSION_ID` string this + * instance implements (since age ==1) + */ + const char *proto_str; + /* -------- the above fields all exist when age == 1 */ +} nghttp2_info; + +/** + * @macro + * + * The default weight of stream dependency. + */ +#define NGHTTP2_DEFAULT_WEIGHT 16 + +/** + * @macro + * + * The maximum weight of stream dependency. + */ +#define NGHTTP2_MAX_WEIGHT 256 + +/** + * @macro + * + * The minimum weight of stream dependency. + */ +#define NGHTTP2_MIN_WEIGHT 1 + +/** + * @macro + * + * The maximum window size + */ +#define NGHTTP2_MAX_WINDOW_SIZE ((int32_t)((1U << 31) - 1)) + +/** + * @macro + * + * The initial window size for stream level flow control. + */ +#define NGHTTP2_INITIAL_WINDOW_SIZE ((1 << 16) - 1) +/** + * @macro + * + * The initial window size for connection level flow control. + */ +#define NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE ((1 << 16) - 1) + +/** + * @macro + * + * The default header table size. + */ +#define NGHTTP2_DEFAULT_HEADER_TABLE_SIZE (1 << 12) + +/** + * @macro + * + * The client magic string, which is the first 24 bytes byte string of + * client connection preface. + */ +#define NGHTTP2_CLIENT_MAGIC "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n" + +/** + * @macro + * + * The length of :macro:`NGHTTP2_CLIENT_MAGIC`. + */ +#define NGHTTP2_CLIENT_MAGIC_LEN 24 + +/** + * @enum + * + * Error codes used in this library. The code range is [-999, -500], + * inclusive. The following values are defined: + */ +typedef enum { + /** + * Invalid argument passed. + */ + NGHTTP2_ERR_INVALID_ARGUMENT = -501, + /** + * Out of buffer space. + */ + NGHTTP2_ERR_BUFFER_ERROR = -502, + /** + * The specified protocol version is not supported. + */ + NGHTTP2_ERR_UNSUPPORTED_VERSION = -503, + /** + * Used as a return value from :type:`nghttp2_send_callback`, + * :type:`nghttp2_recv_callback` and + * :type:`nghttp2_send_data_callback` to indicate that the operation + * would block. + */ + NGHTTP2_ERR_WOULDBLOCK = -504, + /** + * General protocol error + */ + NGHTTP2_ERR_PROTO = -505, + /** + * The frame is invalid. + */ + NGHTTP2_ERR_INVALID_FRAME = -506, + /** + * The peer performed a shutdown on the connection. + */ + NGHTTP2_ERR_EOF = -507, + /** + * Used as a return value from + * :func:`nghttp2_data_source_read_callback` to indicate that data + * transfer is postponed. See + * :func:`nghttp2_data_source_read_callback` for details. + */ + NGHTTP2_ERR_DEFERRED = -508, + /** + * Stream ID has reached the maximum value. Therefore no stream ID + * is available. + */ + NGHTTP2_ERR_STREAM_ID_NOT_AVAILABLE = -509, + /** + * The stream is already closed; or the stream ID is invalid. + */ + NGHTTP2_ERR_STREAM_CLOSED = -510, + /** + * RST_STREAM has been added to the outbound queue. The stream is + * in closing state. + */ + NGHTTP2_ERR_STREAM_CLOSING = -511, + /** + * The transmission is not allowed for this stream (e.g., a frame + * with END_STREAM flag set has already sent). + */ + NGHTTP2_ERR_STREAM_SHUT_WR = -512, + /** + * The stream ID is invalid. + */ + NGHTTP2_ERR_INVALID_STREAM_ID = -513, + /** + * The state of the stream is not valid (e.g., DATA cannot be sent + * to the stream if response HEADERS has not been sent). + */ + NGHTTP2_ERR_INVALID_STREAM_STATE = -514, + /** + * Another DATA frame has already been deferred. + */ + NGHTTP2_ERR_DEFERRED_DATA_EXIST = -515, + /** + * Starting new stream is not allowed (e.g., GOAWAY has been sent + * and/or received). + */ + NGHTTP2_ERR_START_STREAM_NOT_ALLOWED = -516, + /** + * GOAWAY has already been sent. + */ + NGHTTP2_ERR_GOAWAY_ALREADY_SENT = -517, + /** + * The received frame contains the invalid header block (e.g., There + * are duplicate header names; or the header names are not encoded + * in US-ASCII character set and not lower cased; or the header name + * is zero-length string; or the header value contains multiple + * in-sequence NUL bytes). + */ + NGHTTP2_ERR_INVALID_HEADER_BLOCK = -518, + /** + * Indicates that the context is not suitable to perform the + * requested operation. + */ + NGHTTP2_ERR_INVALID_STATE = -519, + /** + * The user callback function failed due to the temporal error. + */ + NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE = -521, + /** + * The length of the frame is invalid, either too large or too small. + */ + NGHTTP2_ERR_FRAME_SIZE_ERROR = -522, + /** + * Header block inflate/deflate error. + */ + NGHTTP2_ERR_HEADER_COMP = -523, + /** + * Flow control error + */ + NGHTTP2_ERR_FLOW_CONTROL = -524, + /** + * Insufficient buffer size given to function. + */ + NGHTTP2_ERR_INSUFF_BUFSIZE = -525, + /** + * Callback was paused by the application + */ + NGHTTP2_ERR_PAUSE = -526, + /** + * There are too many in-flight SETTING frame and no more + * transmission of SETTINGS is allowed. + */ + NGHTTP2_ERR_TOO_MANY_INFLIGHT_SETTINGS = -527, + /** + * The server push is disabled. + */ + NGHTTP2_ERR_PUSH_DISABLED = -528, + /** + * DATA frame for a given stream has been already submitted and has + * not been fully processed yet. + */ + NGHTTP2_ERR_DATA_EXIST = -529, + /** + * The current session is closing due to a connection error or + * `nghttp2_session_terminate_session()` is called. + */ + NGHTTP2_ERR_SESSION_CLOSING = -530, + /** + * Invalid HTTP header field was received and stream is going to be + * closed. + */ + NGHTTP2_ERR_HTTP_HEADER = -531, + /** + * Violation in HTTP messaging rule. + */ + NGHTTP2_ERR_HTTP_MESSAGING = -532, + /** + * Stream was refused. + */ + NGHTTP2_ERR_REFUSED_STREAM = -533, + /** + * Unexpected internal error, but recovered. + */ + NGHTTP2_ERR_INTERNAL = -534, + /** + * The errors < :enum:`NGHTTP2_ERR_FATAL` mean that the library is + * under unexpected condition and processing was terminated (e.g., + * out of memory). If application receives this error code, it must + * stop using that :type:`nghttp2_session` object and only allowed + * operation for that object is deallocate it using + * `nghttp2_session_del()`. + */ + NGHTTP2_ERR_FATAL = -900, + /** + * Out of memory. This is a fatal error. + */ + NGHTTP2_ERR_NOMEM = -901, + /** + * The user callback function failed. This is a fatal error. + */ + NGHTTP2_ERR_CALLBACK_FAILURE = -902, + /** + * Invalid client magic (see :macro:`NGHTTP2_CLIENT_MAGIC`) was + * received and further processing is not possible. + */ + NGHTTP2_ERR_BAD_CLIENT_MAGIC = -903 +} nghttp2_error; + +/** + * @enum + * + * The flags for header field name/value pair. + */ +typedef enum { + /** + * No flag set. + */ + NGHTTP2_NV_FLAG_NONE = 0, + /** + * Indicates that this name/value pair must not be indexed ("Literal + * Header Field never Indexed" representation must be used in HPACK + * encoding). Other implementation calls this bit as "sensitive". + */ + NGHTTP2_NV_FLAG_NO_INDEX = 0x01 +} nghttp2_nv_flag; + +/** + * @struct + * + * The name/value pair, which mainly used to represent header fields. + */ +typedef struct { + /** + * The |name| byte string. If this struct is presented from library + * (e.g., :type:`nghttp2_on_frame_recv_callback`), |name| is + * guaranteed to be NULL-terminated. When application is + * constructing this struct, |name| is not required to be + * NULL-terminated. + */ + uint8_t *name; + /** + * The |value| byte string. If this struct is presented from + * library (e.g., :type:`nghttp2_on_frame_recv_callback`), |value| + * is guaranteed to be NULL-terminated. When application is + * constructing this struct, |value| is not required to be + * NULL-terminated. + */ + uint8_t *value; + /** + * The length of the |name|, excluding terminating NULL. + */ + size_t namelen; + /** + * The length of the |value|, excluding terminating NULL. + */ + size_t valuelen; + /** + * Bitwise OR of one or more of :type:`nghttp2_nv_flag`. + */ + uint8_t flags; +} nghttp2_nv; + +/** + * @enum + * + * The frame types in HTTP/2 specification. + */ +typedef enum { + /** + * The DATA frame. + */ + NGHTTP2_DATA = 0, + /** + * The HEADERS frame. + */ + NGHTTP2_HEADERS = 0x01, + /** + * The PRIORITY frame. + */ + NGHTTP2_PRIORITY = 0x02, + /** + * The RST_STREAM frame. + */ + NGHTTP2_RST_STREAM = 0x03, + /** + * The SETTINGS frame. + */ + NGHTTP2_SETTINGS = 0x04, + /** + * The PUSH_PROMISE frame. + */ + NGHTTP2_PUSH_PROMISE = 0x05, + /** + * The PING frame. + */ + NGHTTP2_PING = 0x06, + /** + * The GOAWAY frame. + */ + NGHTTP2_GOAWAY = 0x07, + /** + * The WINDOW_UPDATE frame. + */ + NGHTTP2_WINDOW_UPDATE = 0x08, + /** + * The CONTINUATION frame. This frame type won't be passed to any + * callbacks because the library processes this frame type and its + * preceding HEADERS/PUSH_PROMISE as a single frame. + */ + NGHTTP2_CONTINUATION = 0x09 +} nghttp2_frame_type; + +/** + * @enum + * + * The flags for HTTP/2 frames. This enum defines all flags for all + * frames. + */ +typedef enum { + /** + * No flag set. + */ + NGHTTP2_FLAG_NONE = 0, + /** + * The END_STREAM flag. + */ + NGHTTP2_FLAG_END_STREAM = 0x01, + /** + * The END_HEADERS flag. + */ + NGHTTP2_FLAG_END_HEADERS = 0x04, + /** + * The ACK flag. + */ + NGHTTP2_FLAG_ACK = 0x01, + /** + * The PADDED flag. + */ + NGHTTP2_FLAG_PADDED = 0x08, + /** + * The PRIORITY flag. + */ + NGHTTP2_FLAG_PRIORITY = 0x20 +} nghttp2_flag; + +/** + * @enum + * The SETTINGS ID. + */ +typedef enum { + /** + * SETTINGS_HEADER_TABLE_SIZE + */ + NGHTTP2_SETTINGS_HEADER_TABLE_SIZE = 0x01, + /** + * SETTINGS_ENABLE_PUSH + */ + NGHTTP2_SETTINGS_ENABLE_PUSH = 0x02, + /** + * SETTINGS_MAX_CONCURRENT_STREAMS + */ + NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS = 0x03, + /** + * SETTINGS_INITIAL_WINDOW_SIZE + */ + NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE = 0x04, + /** + * SETTINGS_MAX_FRAME_SIZE + */ + NGHTTP2_SETTINGS_MAX_FRAME_SIZE = 0x05, + /** + * SETTINGS_MAX_HEADER_LIST_SIZE + */ + NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE = 0x06 +} nghttp2_settings_id; +/* Note: If we add SETTINGS, update the capacity of + NGHTTP2_INBOUND_NUM_IV as well */ + +/** + * @macro + * Default maximum concurrent streams. + */ +#define NGHTTP2_INITIAL_MAX_CONCURRENT_STREAMS ((1U << 31) - 1) + +/** + * @enum + * The status codes for the RST_STREAM and GOAWAY frames. + */ +typedef enum { + /** + * No errors. + */ + NGHTTP2_NO_ERROR = 0x00, + /** + * PROTOCOL_ERROR + */ + NGHTTP2_PROTOCOL_ERROR = 0x01, + /** + * INTERNAL_ERROR + */ + NGHTTP2_INTERNAL_ERROR = 0x02, + /** + * FLOW_CONTROL_ERROR + */ + NGHTTP2_FLOW_CONTROL_ERROR = 0x03, + /** + * SETTINGS_TIMEOUT + */ + NGHTTP2_SETTINGS_TIMEOUT = 0x04, + /** + * STREAM_CLOSED + */ + NGHTTP2_STREAM_CLOSED = 0x05, + /** + * FRAME_SIZE_ERROR + */ + NGHTTP2_FRAME_SIZE_ERROR = 0x06, + /** + * REFUSED_STREAM + */ + NGHTTP2_REFUSED_STREAM = 0x07, + /** + * CANCEL + */ + NGHTTP2_CANCEL = 0x08, + /** + * COMPRESSION_ERROR + */ + NGHTTP2_COMPRESSION_ERROR = 0x09, + /** + * CONNECT_ERROR + */ + NGHTTP2_CONNECT_ERROR = 0x0a, + /** + * ENHANCE_YOUR_CALM + */ + NGHTTP2_ENHANCE_YOUR_CALM = 0x0b, + /** + * INADEQUATE_SECURITY + */ + NGHTTP2_INADEQUATE_SECURITY = 0x0c, + /** + * HTTP_1_1_REQUIRED + */ + NGHTTP2_HTTP_1_1_REQUIRED = 0x0d +} nghttp2_error_code; + +/** + * @struct + * The frame header. + */ +typedef struct { + /** + * The length field of this frame, excluding frame header. + */ + size_t length; + /** + * The stream identifier (aka, stream ID) + */ + int32_t stream_id; + /** + * The type of this frame. See `nghttp2_frame_type`. + */ + uint8_t type; + /** + * The flags. + */ + uint8_t flags; + /** + * Reserved bit in frame header. Currently, this is always set to 0 + * and application should not expect something useful in here. + */ + uint8_t reserved; +} nghttp2_frame_hd; + +/** + * @union + * + * This union represents the some kind of data source passed to + * :type:`nghttp2_data_source_read_callback`. + */ +typedef union { + /** + * The integer field, suitable for a file descriptor. + */ + int fd; + /** + * The pointer to an arbitrary object. + */ + void *ptr; +} nghttp2_data_source; + +/** + * @enum + * + * The flags used to set in |data_flags| output parameter in + * :type:`nghttp2_data_source_read_callback`. + */ +typedef enum { + /** + * No flag set. + */ + NGHTTP2_DATA_FLAG_NONE = 0, + /** + * Indicates EOF was sensed. + */ + NGHTTP2_DATA_FLAG_EOF = 0x01, + /** + * Indicates that END_STREAM flag must not be set even if + * NGHTTP2_DATA_FLAG_EOF is set. Usually this flag is used to send + * trailer header fields with `nghttp2_submit_request()` or + * `nghttp2_submit_response()`. + */ + NGHTTP2_DATA_FLAG_NO_END_STREAM = 0x02, + /** + * Indicates that application will send complete DATA frame in + * :type:`nghttp2_send_data_callback`. + */ + NGHTTP2_DATA_FLAG_NO_COPY = 0x04 +} nghttp2_data_flag; + +/** + * @functypedef + * + * Callback function invoked when the library wants to read data from + * the |source|. The read data is sent in the stream |stream_id|. + * The implementation of this function must read at most |length| + * bytes of data from |source| (or possibly other places) and store + * them in |buf| and return number of data stored in |buf|. If EOF is + * reached, set :enum:`NGHTTP2_DATA_FLAG_EOF` flag in |*data_flags|. + * + * Sometime it is desirable to avoid copying data into |buf| and let + * application to send data directly. To achieve this, set + * :enum:`NGHTTP2_DATA_FLAG_NO_COPY` to |*data_flags| (and possibly + * other flags, just like when we do copy), and return the number of + * bytes to send without copying data into |buf|. The library, seeing + * :enum:`NGHTTP2_DATA_FLAG_NO_COPY`, will invoke + * :type:`nghttp2_send_data_callback`. The application must send + * complete DATA frame in that callback. + * + * If this callback is set by `nghttp2_submit_request()`, + * `nghttp2_submit_response()` or `nghttp2_submit_headers()` and + * `nghttp2_submit_data()` with flag parameter + * :enum:`NGHTTP2_FLAG_END_STREAM` set, and + * :enum:`NGHTTP2_DATA_FLAG_EOF` flag is set to |*data_flags|, DATA + * frame will have END_STREAM flag set. Usually, this is expected + * behaviour and all are fine. One exception is send trailer header + * fields. You cannot send trailers after sending frame with + * END_STREAM set. To avoid this problem, one can set + * :enum:`NGHTTP2_DATA_FLAG_NO_END_STREAM` along with + * :enum:`NGHTTP2_DATA_FLAG_EOF` to signal the library not to set + * END_STREAM in DATA frame. Then application can use + * `nghttp2_submit_trailer()` to send trailers. + * `nghttp2_submit_trailer()` can be called inside this callback. + * + * If the application wants to postpone DATA frames (e.g., + * asynchronous I/O, or reading data blocks for long time), it is + * achieved by returning :enum:`NGHTTP2_ERR_DEFERRED` without reading + * any data in this invocation. The library removes DATA frame from + * the outgoing queue temporarily. To move back deferred DATA frame + * to outgoing queue, call `nghttp2_session_resume_data()`. In case + * of error, there are 2 choices. Returning + * :enum:`NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE` will close the stream + * by issuing RST_STREAM with :enum:`NGHTTP2_INTERNAL_ERROR`. If a + * different error code is desirable, use + * `nghttp2_submit_rst_stream()` with a desired error code and then + * return :enum:`NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE`. Returning + * :enum:`NGHTTP2_ERR_CALLBACK_FAILURE` will signal the entire session + * failure. + */ +typedef ssize_t (*nghttp2_data_source_read_callback)( + nghttp2_session *session, int32_t stream_id, uint8_t *buf, size_t length, + uint32_t *data_flags, nghttp2_data_source *source, void *user_data); + +/** + * @struct + * + * This struct represents the data source and the way to read a chunk + * of data from it. + */ +typedef struct { + /** + * The data source. + */ + nghttp2_data_source source; + /** + * The callback function to read a chunk of data from the |source|. + */ + nghttp2_data_source_read_callback read_callback; +} nghttp2_data_provider; + +/** + * @struct + * + * The DATA frame. The received data is delivered via + * :type:`nghttp2_on_data_chunk_recv_callback`. + */ +typedef struct { + nghttp2_frame_hd hd; + /** + * The length of the padding in this frame. This includes PAD_HIGH + * and PAD_LOW. + */ + size_t padlen; +} nghttp2_data; + +/** + * @enum + * + * The category of HEADERS, which indicates the role of the frame. In + * HTTP/2 spec, request, response, push response and other arbitrary + * headers (e.g., trailers) are all called just HEADERS. To give the + * application the role of incoming HEADERS frame, we define several + * categories. + */ +typedef enum { + /** + * The HEADERS frame is opening new stream, which is analogous to + * SYN_STREAM in SPDY. + */ + NGHTTP2_HCAT_REQUEST = 0, + /** + * The HEADERS frame is the first response headers, which is + * analogous to SYN_REPLY in SPDY. + */ + NGHTTP2_HCAT_RESPONSE = 1, + /** + * The HEADERS frame is the first headers sent against reserved + * stream. + */ + NGHTTP2_HCAT_PUSH_RESPONSE = 2, + /** + * The HEADERS frame which does not apply for the above categories, + * which is analogous to HEADERS in SPDY. If non-final response + * (e.g., status 1xx) is used, final response HEADERS frame will be + * categorized here. + */ + NGHTTP2_HCAT_HEADERS = 3 +} nghttp2_headers_category; + +/** + * @struct + * + * The structure to specify stream dependency. + */ +typedef struct { + /** + * The stream ID of the stream to depend on. Specifying 0 makes + * stream not depend any other stream. + */ + int32_t stream_id; + /** + * The weight of this dependency. + */ + int32_t weight; + /** + * nonzero means exclusive dependency + */ + uint8_t exclusive; +} nghttp2_priority_spec; + +/** + * @struct + * + * The HEADERS frame. It has the following members: + */ +typedef struct { + /** + * The frame header. + */ + nghttp2_frame_hd hd; + /** + * The length of the padding in this frame. This includes PAD_HIGH + * and PAD_LOW. + */ + size_t padlen; + /** + * The priority specification + */ + nghttp2_priority_spec pri_spec; + /** + * The name/value pairs. + */ + nghttp2_nv *nva; + /** + * The number of name/value pairs in |nva|. + */ + size_t nvlen; + /** + * The category of this HEADERS frame. + */ + nghttp2_headers_category cat; +} nghttp2_headers; + +/** + * @struct + * + * The PRIORITY frame. It has the following members: + */ +typedef struct { + /** + * The frame header. + */ + nghttp2_frame_hd hd; + /** + * The priority specification. + */ + nghttp2_priority_spec pri_spec; +} nghttp2_priority; + +/** + * @struct + * + * The RST_STREAM frame. It has the following members: + */ +typedef struct { + /** + * The frame header. + */ + nghttp2_frame_hd hd; + /** + * The error code. See :type:`nghttp2_error_code`. + */ + uint32_t error_code; +} nghttp2_rst_stream; + +/** + * @struct + * + * The SETTINGS ID/Value pair. It has the following members: + */ +typedef struct { + /** + * The SETTINGS ID. See :type:`nghttp2_settings_id`. + */ + int32_t settings_id; + /** + * The value of this entry. + */ + uint32_t value; +} nghttp2_settings_entry; + +/** + * @struct + * + * The SETTINGS frame. It has the following members: + */ +typedef struct { + /** + * The frame header. + */ + nghttp2_frame_hd hd; + /** + * The number of SETTINGS ID/Value pairs in |iv|. + */ + size_t niv; + /** + * The pointer to the array of SETTINGS ID/Value pair. + */ + nghttp2_settings_entry *iv; +} nghttp2_settings; + +/** + * @struct + * + * The PUSH_PROMISE frame. It has the following members: + */ +typedef struct { + /** + * The frame header. + */ + nghttp2_frame_hd hd; + /** + * The length of the padding in this frame. This includes PAD_HIGH + * and PAD_LOW. + */ + size_t padlen; + /** + * The name/value pairs. + */ + nghttp2_nv *nva; + /** + * The number of name/value pairs in |nva|. + */ + size_t nvlen; + /** + * The promised stream ID + */ + int32_t promised_stream_id; + /** + * Reserved bit. Currently this is always set to 0 and application + * should not expect something useful in here. + */ + uint8_t reserved; +} nghttp2_push_promise; + +/** + * @struct + * + * The PING frame. It has the following members: + */ +typedef struct { + /** + * The frame header. + */ + nghttp2_frame_hd hd; + /** + * The opaque data + */ + uint8_t opaque_data[8]; +} nghttp2_ping; + +/** + * @struct + * + * The GOAWAY frame. It has the following members: + */ +typedef struct { + /** + * The frame header. + */ + nghttp2_frame_hd hd; + /** + * The last stream stream ID. + */ + int32_t last_stream_id; + /** + * The error code. See :type:`nghttp2_error_code`. + */ + uint32_t error_code; + /** + * The additional debug data + */ + uint8_t *opaque_data; + /** + * The length of |opaque_data| member. + */ + size_t opaque_data_len; + /** + * Reserved bit. Currently this is always set to 0 and application + * should not expect something useful in here. + */ + uint8_t reserved; +} nghttp2_goaway; + +/** + * @struct + * + * The WINDOW_UPDATE frame. It has the following members: + */ +typedef struct { + /** + * The frame header. + */ + nghttp2_frame_hd hd; + /** + * The window size increment. + */ + int32_t window_size_increment; + /** + * Reserved bit. Currently this is always set to 0 and application + * should not expect something useful in here. + */ + uint8_t reserved; +} nghttp2_window_update; + +/** + * @struct + * + * The extension frame. It has following members: + */ +typedef struct { + /** + * The frame header. + */ + nghttp2_frame_hd hd; + /** + * The pointer to extension payload. The exact pointer type is + * determined by hd.type. + * + * Currently, no extension is supported. This is a place holder for + * the future extensions. + */ + void *payload; +} nghttp2_extension; + +/** + * @union + * + * This union includes all frames to pass them to various function + * calls as nghttp2_frame type. The CONTINUATION frame is omitted + * from here because the library deals with it internally. + */ +typedef union { + /** + * The frame header, which is convenient to inspect frame header. + */ + nghttp2_frame_hd hd; + /** + * The DATA frame. + */ + nghttp2_data data; + /** + * The HEADERS frame. + */ + nghttp2_headers headers; + /** + * The PRIORITY frame. + */ + nghttp2_priority priority; + /** + * The RST_STREAM frame. + */ + nghttp2_rst_stream rst_stream; + /** + * The SETTINGS frame. + */ + nghttp2_settings settings; + /** + * The PUSH_PROMISE frame. + */ + nghttp2_push_promise push_promise; + /** + * The PING frame. + */ + nghttp2_ping ping; + /** + * The GOAWAY frame. + */ + nghttp2_goaway goaway; + /** + * The WINDOW_UPDATE frame. + */ + nghttp2_window_update window_update; + /** + * The extension frame. + */ + nghttp2_extension ext; +} nghttp2_frame; + +/** + * @functypedef + * + * Callback function invoked when |session| wants to send data to the + * remote peer. The implementation of this function must send at most + * |length| bytes of data stored in |data|. The |flags| is currently + * not used and always 0. It must return the number of bytes sent if + * it succeeds. If it cannot send any single byte without blocking, + * it must return :enum:`NGHTTP2_ERR_WOULDBLOCK`. For other errors, + * it must return :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`. The + * |user_data| pointer is the third argument passed in to the call to + * `nghttp2_session_client_new()` or `nghttp2_session_server_new()`. + * + * This callback is required if the application uses + * `nghttp2_session_send()` to send data to the remote endpoint. If + * the application uses solely `nghttp2_session_mem_send()` instead, + * this callback function is unnecessary. + * + * To set this callback to :type:`nghttp2_session_callbacks`, use + * `nghttp2_session_callbacks_set_send_callback()`. + */ +typedef ssize_t (*nghttp2_send_callback)(nghttp2_session *session, + const uint8_t *data, size_t length, + int flags, void *user_data); + +/** + * @functypedef + * + * Callback function invoked when :enum:`NGHTTP2_DATA_FLAG_NO_COPY` is + * used in :type:`nghttp2_data_source_read_callback` to send complete + * DATA frame. + * + * The |frame| is a DATA frame to send. The |framehd| is the + * serialized frame header (9 bytes). The |length| is the length of + * application data to send (this does not include padding). The + * |source| is the same pointer passed to + * :type:`nghttp2_data_source_read_callback`. + * + * The application first must send frame header |framehd| of length 9 + * bytes. If ``frame->padlen > 0``, send 1 byte of value + * ``frame->padlen - 1``. Then send exactly |length| bytes of + * application data. Finally, if ``frame->padlen > 0``, send + * ``frame->padlen - 1`` bytes of zero (they are padding). + * + * The application has to send complete DATA frame in this callback. + * If all data were written successfully, return 0. + * + * If it cannot send it all, just return + * :enum:`NGHTTP2_ERR_WOULDBLOCK`; the library will call this callback + * with the same parameters later (It is recommended to send complete + * DATA frame at once in this function to deal with error; if partial + * frame data has already sent, it is impossible to send another data + * in that state, and all we can do is tear down connection). If + * application decided to reset this stream, return + * :enum:`NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE`, then the library + * will send RST_STREAM with INTERNAL_ERROR as error code. The + * application can also return :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`, + * which will result in connection closure. Returning any other value + * is treated as :enum:`NGHTTP2_ERR_CALLBACK_FAILURE` is returned. + */ +typedef int (*nghttp2_send_data_callback)(nghttp2_session *session, + nghttp2_frame *frame, + const uint8_t *framehd, size_t length, + nghttp2_data_source *source, + void *user_data); + +/** + * @functypedef + * + * Callback function invoked when |session| wants to receive data from + * the remote peer. The implementation of this function must read at + * most |length| bytes of data and store it in |buf|. The |flags| is + * currently not used and always 0. It must return the number of + * bytes written in |buf| if it succeeds. If it cannot read any + * single byte without blocking, it must return + * :enum:`NGHTTP2_ERR_WOULDBLOCK`. If it gets EOF before it reads any + * single byte, it must return :enum:`NGHTTP2_ERR_EOF`. For other + * errors, it must return :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`. + * Returning 0 is treated as :enum:`NGHTTP2_ERR_WOULDBLOCK`. The + * |user_data| pointer is the third argument passed in to the call to + * `nghttp2_session_client_new()` or `nghttp2_session_server_new()`. + * + * This callback is required if the application uses + * `nghttp2_session_recv()` to receive data from the remote endpoint. + * If the application uses solely `nghttp2_session_mem_recv()` + * instead, this callback function is unnecessary. + * + * To set this callback to :type:`nghttp2_session_callbacks`, use + * `nghttp2_session_callbacks_set_recv_callback()`. + */ +typedef ssize_t (*nghttp2_recv_callback)(nghttp2_session *session, uint8_t *buf, + size_t length, int flags, + void *user_data); + +/** + * @functypedef + * + * Callback function invoked by `nghttp2_session_recv()` and + * `nghttp2_session_mem_recv()` when a frame is received. The + * |user_data| pointer is the third argument passed in to the call to + * `nghttp2_session_client_new()` or `nghttp2_session_server_new()`. + * + * If frame is HEADERS or PUSH_PROMISE, the ``nva`` and ``nvlen`` + * member of their data structure are always ``NULL`` and 0 + * respectively. The header name/value pairs are emitted via + * :type:`nghttp2_on_header_callback`. + * + * For HEADERS, PUSH_PROMISE and DATA frames, this callback may be + * called after stream is closed (see + * :type:`nghttp2_on_stream_close_callback`). The application should + * check that stream is still alive using its own stream management or + * :func:`nghttp2_session_get_stream_user_data()`. + * + * Only HEADERS and DATA frame can signal the end of incoming data. + * If ``frame->hd.flags & NGHTTP2_FLAG_END_STREAM`` is nonzero, the + * |frame| is the last frame from the remote peer in this stream. + * + * This callback won't be called for CONTINUATION frames. + * HEADERS/PUSH_PROMISE + CONTINUATIONs are treated as single frame. + * + * The implementation of this function must return 0 if it succeeds. + * If nonzero value is returned, it is treated as fatal error and + * `nghttp2_session_recv()` and `nghttp2_session_mem_recv()` functions + * immediately return :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`. + * + * To set this callback to :type:`nghttp2_session_callbacks`, use + * `nghttp2_session_callbacks_set_on_frame_recv_callback()`. + */ +typedef int (*nghttp2_on_frame_recv_callback)(nghttp2_session *session, + const nghttp2_frame *frame, + void *user_data); + +/** + * @functypedef + * + * Callback function invoked by `nghttp2_session_recv()` and + * `nghttp2_session_mem_recv()` when an invalid non-DATA frame is + * received. The error is indicated by the |lib_error_code|, which is + * one of the values defined in :type:`nghttp2_error`. When this + * callback function is invoked, the library automatically submits + * either RST_STREAM or GOAWAY frame. The |user_data| pointer is the + * third argument passed in to the call to + * `nghttp2_session_client_new()` or `nghttp2_session_server_new()`. + * + * If frame is HEADERS or PUSH_PROMISE, the ``nva`` and ``nvlen`` + * member of their data structure are always ``NULL`` and 0 + * respectively. + * + * The implementation of this function must return 0 if it succeeds. + * If nonzero is returned, it is treated as fatal error and + * `nghttp2_session_recv()` and `nghttp2_session_mem_recv()` functions + * immediately return :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`. + * + * To set this callback to :type:`nghttp2_session_callbacks`, use + * `nghttp2_session_callbacks_set_on_invalid_frame_recv_callback()`. + */ +typedef int (*nghttp2_on_invalid_frame_recv_callback)( + nghttp2_session *session, const nghttp2_frame *frame, int lib_error_code, + void *user_data); + +/** + * @functypedef + * + * Callback function invoked when a chunk of data in DATA frame is + * received. The |stream_id| is the stream ID this DATA frame belongs + * to. The |flags| is the flags of DATA frame which this data chunk + * is contained. ``(flags & NGHTTP2_FLAG_END_STREAM) != 0`` does not + * necessarily mean this chunk of data is the last one in the stream. + * You should use :type:`nghttp2_on_frame_recv_callback` to know all + * data frames are received. The |user_data| pointer is the third + * argument passed in to the call to `nghttp2_session_client_new()` or + * `nghttp2_session_server_new()`. + * + * If the application uses `nghttp2_session_mem_recv()`, it can return + * :enum:`NGHTTP2_ERR_PAUSE` to make `nghttp2_session_mem_recv()` + * return without processing further input bytes. The memory by + * pointed by the |data| is retained until + * `nghttp2_session_mem_recv()` or `nghttp2_session_recv()` is called. + * The application must retain the input bytes which was used to + * produce the |data| parameter, because it may refer to the memory + * region included in the input bytes. + * + * The implementation of this function must return 0 if it succeeds. + * If nonzero is returned, it is treated as fatal error, and + * `nghttp2_session_recv()` and `nghttp2_session_mem_recv()` functions + * immediately return :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`. + * + * To set this callback to :type:`nghttp2_session_callbacks`, use + * `nghttp2_session_callbacks_set_on_data_chunk_recv_callback()`. + */ +typedef int (*nghttp2_on_data_chunk_recv_callback)(nghttp2_session *session, + uint8_t flags, + int32_t stream_id, + const uint8_t *data, + size_t len, void *user_data); + +/** + * @functypedef + * + * Callback function invoked just before the non-DATA frame |frame| is + * sent. The |user_data| pointer is the third argument passed in to + * the call to `nghttp2_session_client_new()` or + * `nghttp2_session_server_new()`. + * + * The implementation of this function must return 0 if it succeeds. + * If nonzero is returned, it is treated as fatal error and + * `nghttp2_session_send()` and `nghttp2_session_mem_send()` functions + * immediately return :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`. + * + * To set this callback to :type:`nghttp2_session_callbacks`, use + * `nghttp2_session_callbacks_set_before_frame_send_callback()`. + */ +typedef int (*nghttp2_before_frame_send_callback)(nghttp2_session *session, + const nghttp2_frame *frame, + void *user_data); + +/** + * @functypedef + * + * Callback function invoked after the frame |frame| is sent. The + * |user_data| pointer is the third argument passed in to the call to + * `nghttp2_session_client_new()` or `nghttp2_session_server_new()`. + * + * The implementation of this function must return 0 if it succeeds. + * If nonzero is returned, it is treated as fatal error and + * `nghttp2_session_send()` and `nghttp2_session_mem_send()` functions + * immediately return :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`. + * + * To set this callback to :type:`nghttp2_session_callbacks`, use + * `nghttp2_session_callbacks_set_on_frame_send_callback()`. + */ +typedef int (*nghttp2_on_frame_send_callback)(nghttp2_session *session, + const nghttp2_frame *frame, + void *user_data); + +/** + * @functypedef + * + * Callback function invoked after the non-DATA frame |frame| is not + * sent because of the error. The error is indicated by the + * |lib_error_code|, which is one of the values defined in + * :type:`nghttp2_error`. The |user_data| pointer is the third + * argument passed in to the call to `nghttp2_session_client_new()` or + * `nghttp2_session_server_new()`. + * + * The implementation of this function must return 0 if it succeeds. + * If nonzero is returned, it is treated as fatal error and + * `nghttp2_session_send()` and `nghttp2_session_mem_send()` functions + * immediately return :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`. + * + * `nghttp2_session_get_stream_user_data()` can be used to get + * associated data. + * + * To set this callback to :type:`nghttp2_session_callbacks`, use + * `nghttp2_session_callbacks_set_on_frame_not_send_callback()`. + */ +typedef int (*nghttp2_on_frame_not_send_callback)(nghttp2_session *session, + const nghttp2_frame *frame, + int lib_error_code, + void *user_data); + +/** + * @functypedef + * + * Callback function invoked when the stream |stream_id| is closed. + * The reason of closure is indicated by the |error_code|. The + * |error_code| is usually one of :enum:`nghttp2_error_code`, but that + * is not guaranteed. The stream_user_data, which was specified in + * `nghttp2_submit_request()` or `nghttp2_submit_headers()`, is still + * available in this function. The |user_data| pointer is the third + * argument passed in to the call to `nghttp2_session_client_new()` or + * `nghttp2_session_server_new()`. + * + * This function is also called for a stream in reserved state. + * + * The implementation of this function must return 0 if it succeeds. + * If nonzero is returned, it is treated as fatal error and + * `nghttp2_session_recv()`, `nghttp2_session_mem_recv()`, + * `nghttp2_session_send()`, and `nghttp2_session_mem_send()` + * functions immediately return :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`. + * + * To set this callback to :type:`nghttp2_session_callbacks`, use + * `nghttp2_session_callbacks_set_on_stream_close_callback()`. + */ +typedef int (*nghttp2_on_stream_close_callback)(nghttp2_session *session, + int32_t stream_id, + uint32_t error_code, + void *user_data); + +/** + * @functypedef + * + * Callback function invoked when the reception of header block in + * HEADERS or PUSH_PROMISE is started. Each header name/value pair + * will be emitted by :type:`nghttp2_on_header_callback`. + * + * The ``frame->hd.flags`` may not have + * :enum:`NGHTTP2_FLAG_END_HEADERS` flag set, which indicates that one + * or more CONTINUATION frames are involved. But the application does + * not need to care about that because the header name/value pairs are + * emitted transparently regardless of CONTINUATION frames. + * + * The server applications probably create an object to store + * information about new stream if ``frame->hd.type == + * NGHTTP2_HEADERS`` and ``frame->headers.cat == + * NGHTTP2_HCAT_REQUEST``. If |session| is configured as server side, + * ``frame->headers.cat`` is either ``NGHTTP2_HCAT_REQUEST`` + * containing request headers or ``NGHTTP2_HCAT_HEADERS`` containing + * trailer headers and never get PUSH_PROMISE in this callback. + * + * For the client applications, ``frame->hd.type`` is either + * ``NGHTTP2_HEADERS`` or ``NGHTTP2_PUSH_PROMISE``. In case of + * ``NGHTTP2_HEADERS``, ``frame->headers.cat == + * NGHTTP2_HCAT_RESPONSE`` means that it is the first response + * headers, but it may be non-final response which is indicated by 1xx + * status code. In this case, there may be zero or more HEADERS frame + * with ``frame->headers.cat == NGHTTP2_HCAT_HEADERS`` which has + * non-final response code and finally client gets exactly one HEADERS + * frame with ``frame->headers.cat == NGHTTP2_HCAT_HEADERS`` + * containing final response headers (non-1xx status code). The + * trailer headers also has ``frame->headers.cat == + * NGHTTP2_HCAT_HEADERS`` which does not contain any status code. + * + * Returning :enum:`NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE` will close + * the stream (promised stream if frame is PUSH_PROMISE) by issuing + * RST_STREAM with :enum:`NGHTTP2_INTERNAL_ERROR`. In this case, + * :type:`nghttp2_on_header_callback` and + * :type:`nghttp2_on_frame_recv_callback` will not be invoked. If a + * different error code is desirable, use + * `nghttp2_submit_rst_stream()` with a desired error code and then + * return :enum:`NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE`. Again, use + * ``frame->push_promise.promised_stream_id`` as stream_id parameter + * in `nghttp2_submit_rst_stream()` if frame is PUSH_PROMISE. + * + * The implementation of this function must return 0 if it succeeds. + * It can return :enum:`NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE` to + * reset the stream (promised stream if frame is PUSH_PROMISE). For + * critical errors, it must return + * :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`. If the other value is + * returned, it is treated as if :enum:`NGHTTP2_ERR_CALLBACK_FAILURE` + * is returned. If :enum:`NGHTTP2_ERR_CALLBACK_FAILURE` is returned, + * `nghttp2_session_mem_recv()` function will immediately return + * :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`. + * + * To set this callback to :type:`nghttp2_session_callbacks`, use + * `nghttp2_session_callbacks_set_on_begin_headers_callback()`. + */ +typedef int (*nghttp2_on_begin_headers_callback)(nghttp2_session *session, + const nghttp2_frame *frame, + void *user_data); + +/** + * @functypedef + * + * Callback function invoked when a header name/value pair is received + * for the |frame|. The |name| of length |namelen| is header name. + * The |value| of length |valuelen| is header value. The |flags| is + * bitwise OR of one or more of :type:`nghttp2_nv_flag`. + * + * If :enum:`NGHTTP2_NV_FLAG_NO_INDEX` is set in |flags|, the receiver + * must not index this name/value pair when forwarding it to the next + * hop. More specifically, "Literal Header Field never Indexed" + * representation must be used in HPACK encoding. + * + * When this callback is invoked, ``frame->hd.type`` is either + * :enum:`NGHTTP2_HEADERS` or :enum:`NGHTTP2_PUSH_PROMISE`. After all + * header name/value pairs are processed with this callback, and no + * error has been detected, :type:`nghttp2_on_frame_recv_callback` + * will be invoked. If there is an error in decompression, + * :type:`nghttp2_on_frame_recv_callback` for the |frame| will not be + * invoked. + * + * Both |name| and |value| are guaranteed to be NULL-terminated. The + * |namelen| and |valuelen| do not include terminal NULL. If + * `nghttp2_option_set_no_http_messaging()` is used with nonzero + * value, NULL character may be included in |name| or |value| before + * terminating NULL. + * + * Please note that unless `nghttp2_option_set_no_http_messaging()` is + * used, nghttp2 library does perform validation against the |name| + * and the |value| using `nghttp2_check_header_name()` and + * `nghttp2_check_header_value()`. In addition to this, nghttp2 + * performs vaidation based on HTTP Messaging rule, which is briefly + * explained in :ref:`http-messaging` section. + * + * If the application uses `nghttp2_session_mem_recv()`, it can return + * :enum:`NGHTTP2_ERR_PAUSE` to make `nghttp2_session_mem_recv()` + * return without processing further input bytes. The memory pointed + * by |frame|, |name| and |value| parameters are retained until + * `nghttp2_session_mem_recv()` or `nghttp2_session_recv()` is called. + * The application must retain the input bytes which was used to + * produce these parameters, because it may refer to the memory region + * included in the input bytes. + * + * Returning :enum:`NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE` will close + * the stream (promised stream if frame is PUSH_PROMISE) by issuing + * RST_STREAM with :enum:`NGHTTP2_INTERNAL_ERROR`. In this case, + * :type:`nghttp2_on_header_callback` and + * :type:`nghttp2_on_frame_recv_callback` will not be invoked. If a + * different error code is desirable, use + * `nghttp2_submit_rst_stream()` with a desired error code and then + * return :enum:`NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE`. Again, use + * ``frame->push_promise.promised_stream_id`` as stream_id parameter + * in `nghttp2_submit_rst_stream()` if frame is PUSH_PROMISE. + * + * The implementation of this function must return 0 if it succeeds. + * It may return :enum:`NGHTTP2_ERR_PAUSE` or + * :enum:`NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE`. For other critical + * failures, it must return :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`. If + * the other nonzero value is returned, it is treated as + * :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`. If + * :enum:`NGHTTP2_ERR_CALLBACK_FAILURE` is returned, + * `nghttp2_session_recv()` and `nghttp2_session_mem_recv()` functions + * immediately return :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`. + * + * To set this callback to :type:`nghttp2_session_callbacks`, use + * `nghttp2_session_callbacks_set_on_header_callback()`. + */ +typedef int (*nghttp2_on_header_callback)(nghttp2_session *session, + const nghttp2_frame *frame, + const uint8_t *name, size_t namelen, + const uint8_t *value, size_t valuelen, + uint8_t flags, void *user_data); + +/** + * @functypedef + * + * Callback function invoked when the library asks application how + * many padding bytes are required for the transmission of the + * |frame|. The application must choose the total length of payload + * including padded bytes in range [frame->hd.length, max_payloadlen], + * inclusive. Choosing number not in this range will be treated as + * :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`. Returning + * ``frame->hd.length`` means no padding is added. Returning + * :enum:`NGHTTP2_ERR_CALLBACK_FAILURE` will make + * `nghttp2_session_send()` and `nghttp2_session_mem_send()` functions + * immediately return :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`. + * + * To set this callback to :type:`nghttp2_session_callbacks`, use + * `nghttp2_session_callbacks_set_select_padding_callback()`. + */ +typedef ssize_t (*nghttp2_select_padding_callback)(nghttp2_session *session, + const nghttp2_frame *frame, + size_t max_payloadlen, + void *user_data); + +/** + * @functypedef + * + * Callback function invoked when library wants to get max length of + * data to send data to the remote peer. The implementation of this + * function should return a value in the following range. [1, + * min(|session_remote_window_size|, |stream_remote_window_size|, + * |remote_max_frame_size|)]. If a value greater than this range is + * returned than the max allow value will be used. Returning a value + * smaller than this range is treated as + * :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`. The |frame_type| is provided + * for future extensibility and identifies the type of frame (see + * :type:`nghttp2_frame_type`) for which to get the length for. + * Currently supported frame types are: :enum:`NGHTTP2_DATA`. + * + * This callback can be used to control the length in bytes for which + * :type:`nghttp2_data_source_read_callback` is allowed to send to the + * remote endpoint. This callback is optional. Returning + * :enum:`NGHTTP2_ERR_CALLBACK_FAILURE` will signal the entire session + * failure. + * + * To set this callback to :type:`nghttp2_session_callbacks`, use + * `nghttp2_session_callbacks_set_data_source_read_length_callback()`. + */ +typedef ssize_t (*nghttp2_data_source_read_length_callback)( + nghttp2_session *session, uint8_t frame_type, int32_t stream_id, + int32_t session_remote_window_size, int32_t stream_remote_window_size, + uint32_t remote_max_frame_size, void *user_data); + +/** + * @functypedef + * + * Callback function invoked when a frame header is received. The + * |hd| points to received frame header. + * + * Unlike :type:`nghttp2_on_frame_recv_callback`, this callback will + * also be called when frame header of CONTINUATION frame is received. + * + * If both :type:`nghttp2_on_begin_frame_callback` and + * :type:`nghttp2_on_begin_headers_callback` are set and HEADERS or + * PUSH_PROMISE is received, :type:`nghttp2_on_begin_frame_callback` + * will be called first. + * + * The implementation of this function must return 0 if it succeeds. + * If nonzero value is returned, it is treated as fatal error and + * `nghttp2_session_recv()` and `nghttp2_session_mem_recv()` functions + * immediately return :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`. + * + * To set this callback to :type:`nghttp2_session_callbacks`, use + * `nghttp2_session_callbacks_set_on_begin_frame_callback()`. + */ +typedef int (*nghttp2_on_begin_frame_callback)(nghttp2_session *session, + const nghttp2_frame_hd *hd, + void *user_data); + +struct nghttp2_session_callbacks; + +/** + * @struct + * + * Callback functions for :type:`nghttp2_session`. The details of + * this structure are intentionally hidden from the public API. + */ +typedef struct nghttp2_session_callbacks nghttp2_session_callbacks; + +/** + * @function + * + * Initializes |*callbacks_ptr| with NULL values. + * + * The initialized object can be used when initializing multiple + * :type:`nghttp2_session` objects. + * + * When the application finished using this object, it can use + * `nghttp2_session_callbacks_del()` to free its memory. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`NGHTTP2_ERR_NOMEM` + * Out of memory. + */ +NGHTTP2_EXTERN int +nghttp2_session_callbacks_new(nghttp2_session_callbacks **callbacks_ptr); + +/** + * @function + * + * Frees any resources allocated for |callbacks|. If |callbacks| is + * ``NULL``, this function does nothing. + */ +NGHTTP2_EXTERN void +nghttp2_session_callbacks_del(nghttp2_session_callbacks *callbacks); + +/** + * @function + * + * Sets callback function invoked when a session wants to send data to + * the remote peer. This callback is not necessary if the application + * uses solely `nghttp2_session_mem_send()` to serialize data to + * transmit. + */ +NGHTTP2_EXTERN void nghttp2_session_callbacks_set_send_callback( + nghttp2_session_callbacks *cbs, nghttp2_send_callback send_callback); + +/** + * @function + * + * Sets callback function invoked when the a session wants to receive + * data from the remote peer. This callback is not necessary if the + * application uses solely `nghttp2_session_mem_recv()` to process + * received data. + */ +NGHTTP2_EXTERN void nghttp2_session_callbacks_set_recv_callback( + nghttp2_session_callbacks *cbs, nghttp2_recv_callback recv_callback); + +/** + * @function + * + * Sets callback function invoked by `nghttp2_session_recv()` and + * `nghttp2_session_mem_recv()` when a frame is received. + */ +NGHTTP2_EXTERN void nghttp2_session_callbacks_set_on_frame_recv_callback( + nghttp2_session_callbacks *cbs, + nghttp2_on_frame_recv_callback on_frame_recv_callback); + +/** + * @function + * + * Sets callback function invoked by `nghttp2_session_recv()` and + * `nghttp2_session_mem_recv()` when an invalid non-DATA frame is + * received. + */ +NGHTTP2_EXTERN void +nghttp2_session_callbacks_set_on_invalid_frame_recv_callback( + nghttp2_session_callbacks *cbs, + nghttp2_on_invalid_frame_recv_callback on_invalid_frame_recv_callback); + +/** + * @function + * + * Sets callback function invoked when a chunk of data in DATA frame + * is received. + */ +NGHTTP2_EXTERN void nghttp2_session_callbacks_set_on_data_chunk_recv_callback( + nghttp2_session_callbacks *cbs, + nghttp2_on_data_chunk_recv_callback on_data_chunk_recv_callback); + +/** + * @function + * + * Sets callback function invoked before a non-DATA frame is sent. + */ +NGHTTP2_EXTERN void nghttp2_session_callbacks_set_before_frame_send_callback( + nghttp2_session_callbacks *cbs, + nghttp2_before_frame_send_callback before_frame_send_callback); + +/** + * @function + * + * Sets callback function invoked after a frame is sent. + */ +NGHTTP2_EXTERN void nghttp2_session_callbacks_set_on_frame_send_callback( + nghttp2_session_callbacks *cbs, + nghttp2_on_frame_send_callback on_frame_send_callback); + +/** + * @function + * + * Sets callback function invoked when a non-DATA frame is not sent + * because of an error. + */ +NGHTTP2_EXTERN void nghttp2_session_callbacks_set_on_frame_not_send_callback( + nghttp2_session_callbacks *cbs, + nghttp2_on_frame_not_send_callback on_frame_not_send_callback); + +/** + * @function + * + * Sets callback function invoked when the stream is closed. + */ +NGHTTP2_EXTERN void nghttp2_session_callbacks_set_on_stream_close_callback( + nghttp2_session_callbacks *cbs, + nghttp2_on_stream_close_callback on_stream_close_callback); + +/** + * @function + * + * Sets callback function invoked when the reception of header block + * in HEADERS or PUSH_PROMISE is started. + */ +NGHTTP2_EXTERN void nghttp2_session_callbacks_set_on_begin_headers_callback( + nghttp2_session_callbacks *cbs, + nghttp2_on_begin_headers_callback on_begin_headers_callback); + +/** + * @function + * + * Sets callback function invoked when a header name/value pair is + * received. + */ +NGHTTP2_EXTERN void nghttp2_session_callbacks_set_on_header_callback( + nghttp2_session_callbacks *cbs, + nghttp2_on_header_callback on_header_callback); + +/** + * @function + * + * Sets callback function invoked when the library asks application + * how many padding bytes are required for the transmission of the + * given frame. + */ +NGHTTP2_EXTERN void nghttp2_session_callbacks_set_select_padding_callback( + nghttp2_session_callbacks *cbs, + nghttp2_select_padding_callback select_padding_callback); + +/** + * @function + * + * Sets callback function determine the length allowed in + * :type:`nghttp2_data_source_read_callback`. + */ +NGHTTP2_EXTERN void +nghttp2_session_callbacks_set_data_source_read_length_callback( + nghttp2_session_callbacks *cbs, + nghttp2_data_source_read_length_callback data_source_read_length_callback); + +/** + * @function + * + * Sets callback function invoked when a frame header is received. + */ +NGHTTP2_EXTERN void nghttp2_session_callbacks_set_on_begin_frame_callback( + nghttp2_session_callbacks *cbs, + nghttp2_on_begin_frame_callback on_begin_frame_callback); + +/** + * @function + * + * Sets callback function invoked when + * :enum:`NGHTTP2_DATA_FLAG_NO_COPY` is used in + * :type:`nghttp2_data_source_read_callback` to avoid data copy. + */ +NGHTTP2_EXTERN void nghttp2_session_callbacks_set_send_data_callback( + nghttp2_session_callbacks *cbs, + nghttp2_send_data_callback send_data_callback); + +/** + * @functypedef + * + * Custom memory allocator to replace malloc(). The |mem_user_data| + * is the mem_user_data member of :type:`nghttp2_mem` structure. + */ +typedef void *(*nghttp2_malloc)(size_t size, void *mem_user_data); + +/** + * @functypedef + * + * Custom memory allocator to replace free(). The |mem_user_data| is + * the mem_user_data member of :type:`nghttp2_mem` structure. + */ +typedef void (*nghttp2_free)(void *ptr, void *mem_user_data); + +/** + * @functypedef + * + * Custom memory allocator to replace calloc(). The |mem_user_data| + * is the mem_user_data member of :type:`nghttp2_mem` structure. + */ +typedef void *(*nghttp2_calloc)(size_t nmemb, size_t size, void *mem_user_data); + +/** + * @functypedef + * + * Custom memory allocator to replace realloc(). The |mem_user_data| + * is the mem_user_data member of :type:`nghttp2_mem` structure. + */ +typedef void *(*nghttp2_realloc)(void *ptr, size_t size, void *mem_user_data); + +/** + * @struct + * + * Custom memory allocator functions and user defined pointer. The + * |mem_user_data| member is passed to each allocator function. This + * can be used, for example, to achieve per-session memory pool. + * + * In the following example code, ``my_malloc``, ``my_free``, + * ``my_calloc`` and ``my_realloc`` are the replacement of the + * standard allocators ``malloc``, ``free``, ``calloc`` and + * ``realloc`` respectively:: + * + * void *my_malloc_cb(size_t size, void *mem_user_data) { + * return my_malloc(size); + * } + * + * void my_free_cb(void *ptr, void *mem_user_data) { my_free(ptr); } + * + * void *my_calloc_cb(size_t nmemb, size_t size, void *mem_user_data) { + * return my_calloc(nmemb, size); + * } + * + * void *my_realloc_cb(void *ptr, size_t size, void *mem_user_data) { + * return my_realloc(ptr, size); + * } + * + * void session_new() { + * nghttp2_session *session; + * nghttp2_session_callbacks *callbacks; + * nghttp2_mem mem = {NULL, my_malloc_cb, my_free_cb, my_calloc_cb, + * my_realloc_cb}; + * + * ... + * + * nghttp2_session_client_new3(&session, callbacks, NULL, NULL, &mem); + * + * ... + * } + */ +typedef struct { + /** + * An arbitrary user supplied data. This is passed to each + * allocator function. + */ + void *mem_user_data; + /** + * Custom allocator function to replace malloc(). + */ + nghttp2_malloc malloc; + /** + * Custom allocator function to replace free(). + */ + nghttp2_free free; + /** + * Custom allocator function to replace calloc(). + */ + nghttp2_calloc calloc; + /** + * Custom allocator function to replace realloc(). + */ + nghttp2_realloc realloc; +} nghttp2_mem; + +struct nghttp2_option; + +/** + * @struct + * + * Configuration options for :type:`nghttp2_session`. The details of + * this structure are intentionally hidden from the public API. + */ +typedef struct nghttp2_option nghttp2_option; + +/** + * @function + * + * Initializes |*option_ptr| with default values. + * + * When the application finished using this object, it can use + * `nghttp2_option_del()` to free its memory. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`NGHTTP2_ERR_NOMEM` + * Out of memory. + */ +NGHTTP2_EXTERN int nghttp2_option_new(nghttp2_option **option_ptr); + +/** + * @function + * + * Frees any resources allocated for |option|. If |option| is + * ``NULL``, this function does nothing. + */ +NGHTTP2_EXTERN void nghttp2_option_del(nghttp2_option *option); + +/** + * @function + * + * This option prevents the library from sending WINDOW_UPDATE for a + * connection automatically. If this option is set to nonzero, the + * library won't send WINDOW_UPDATE for DATA until application calls + * `nghttp2_session_consume()` to indicate the consumed amount of + * data. Don't use `nghttp2_submit_window_update()` for this purpose. + * By default, this option is set to zero. + */ +NGHTTP2_EXTERN void +nghttp2_option_set_no_auto_window_update(nghttp2_option *option, int val); + +/** + * @function + * + * This option sets the SETTINGS_MAX_CONCURRENT_STREAMS value of + * remote endpoint as if it is received in SETTINGS frame. Without + * specifying this option, before the local endpoint receives + * SETTINGS_MAX_CONCURRENT_STREAMS in SETTINGS frame from remote + * endpoint, SETTINGS_MAX_CONCURRENT_STREAMS is unlimited. This may + * cause problem if local endpoint submits lots of requests initially + * and sending them at once to the remote peer may lead to the + * rejection of some requests. Specifying this option to the sensible + * value, say 100, may avoid this kind of issue. This value will be + * overwritten if the local endpoint receives + * SETTINGS_MAX_CONCURRENT_STREAMS from the remote endpoint. + */ +NGHTTP2_EXTERN void +nghttp2_option_set_peer_max_concurrent_streams(nghttp2_option *option, + uint32_t val); + +/** + * @function + * + * By default, nghttp2 library, if configured as server, requires + * first 24 bytes of client magic byte string (MAGIC). In most cases, + * this will simplify the implementation of server. But sometimes + * server may want to detect the application protocol based on first + * few bytes on clear text communication. + * + * If this option is used with nonzero |val|, nghttp2 library does not + * handle MAGIC. It still checks following SETTINGS frame. This + * means that applications should deal with MAGIC by themselves. + * + * If this option is not used or used with zero value, if MAGIC does + * not match :macro:`NGHTTP2_CLIENT_MAGIC`, `nghttp2_session_recv()` + * and `nghttp2_session_mem_recv()` will return error + * :enum:`NGHTTP2_ERR_BAD_CLIENT_MAGIC`, which is fatal error. + */ +NGHTTP2_EXTERN void +nghttp2_option_set_no_recv_client_magic(nghttp2_option *option, int val); + +/** + * @function + * + * By default, nghttp2 library enforces subset of HTTP Messaging rules + * described in `HTTP/2 specification, section 8 + * `_. See + * :ref:`http-messaging` section for details. For those applications + * who use nghttp2 library as non-HTTP use, give nonzero to |val| to + * disable this enforcement. + */ +NGHTTP2_EXTERN void nghttp2_option_set_no_http_messaging(nghttp2_option *option, + int val); + +/** + * @function + * + * Initializes |*session_ptr| for client use. The all members of + * |callbacks| are copied to |*session_ptr|. Therefore |*session_ptr| + * does not store |callbacks|. The |user_data| is an arbitrary user + * supplied data, which will be passed to the callback functions. + * + * The :type:`nghttp2_send_callback` must be specified. If the + * application code uses `nghttp2_session_recv()`, the + * :type:`nghttp2_recv_callback` must be specified. The other members + * of |callbacks| can be ``NULL``. + * + * If this function fails, |*session_ptr| is left untouched. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`NGHTTP2_ERR_NOMEM` + * Out of memory. + */ +NGHTTP2_EXTERN int +nghttp2_session_client_new(nghttp2_session **session_ptr, + const nghttp2_session_callbacks *callbacks, + void *user_data); + +/** + * @function + * + * Initializes |*session_ptr| for server use. The all members of + * |callbacks| are copied to |*session_ptr|. Therefore |*session_ptr| + * does not store |callbacks|. The |user_data| is an arbitrary user + * supplied data, which will be passed to the callback functions. + * + * The :type:`nghttp2_send_callback` must be specified. If the + * application code uses `nghttp2_session_recv()`, the + * :type:`nghttp2_recv_callback` must be specified. The other members + * of |callbacks| can be ``NULL``. + * + * If this function fails, |*session_ptr| is left untouched. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`NGHTTP2_ERR_NOMEM` + * Out of memory. + */ +NGHTTP2_EXTERN int +nghttp2_session_server_new(nghttp2_session **session_ptr, + const nghttp2_session_callbacks *callbacks, + void *user_data); + +/** + * @function + * + * Like `nghttp2_session_client_new()`, but with additional options + * specified in the |option|. + * + * The |option| can be ``NULL`` and the call is equivalent to + * `nghttp2_session_client_new()`. + * + * This function does not take ownership |option|. The application is + * responsible for freeing |option| if it finishes using the object. + * + * The library code does not refer to |option| after this function + * returns. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`NGHTTP2_ERR_NOMEM` + * Out of memory. + */ +NGHTTP2_EXTERN int +nghttp2_session_client_new2(nghttp2_session **session_ptr, + const nghttp2_session_callbacks *callbacks, + void *user_data, const nghttp2_option *option); + +/** + * @function + * + * Like `nghttp2_session_server_new()`, but with additional options + * specified in the |option|. + * + * The |option| can be ``NULL`` and the call is equivalent to + * `nghttp2_session_server_new()`. + * + * This function does not take ownership |option|. The application is + * responsible for freeing |option| if it finishes using the object. + * + * The library code does not refer to |option| after this function + * returns. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`NGHTTP2_ERR_NOMEM` + * Out of memory. + */ +NGHTTP2_EXTERN int +nghttp2_session_server_new2(nghttp2_session **session_ptr, + const nghttp2_session_callbacks *callbacks, + void *user_data, const nghttp2_option *option); + +/** + * @function + * + * Like `nghttp2_session_client_new2()`, but with additional custom + * memory allocator specified in the |mem|. + * + * The |mem| can be ``NULL`` and the call is equivalent to + * `nghttp2_session_client_new2()`. + * + * This function does not take ownership |mem|. The application is + * responsible for freeing |mem|. + * + * The library code does not refer to |mem| pointer after this + * function returns, so the application can safely free it. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`NGHTTP2_ERR_NOMEM` + * Out of memory. + */ +NGHTTP2_EXTERN int nghttp2_session_client_new3( + nghttp2_session **session_ptr, const nghttp2_session_callbacks *callbacks, + void *user_data, const nghttp2_option *option, nghttp2_mem *mem); + +/** + * @function + * + * Like `nghttp2_session_server_new2()`, but with additional custom + * memory allocator specified in the |mem|. + * + * The |mem| can be ``NULL`` and the call is equivalent to + * `nghttp2_session_server_new2()`. + * + * This function does not take ownership |mem|. The application is + * responsible for freeing |mem|. + * + * The library code does not refer to |mem| pointer after this + * function returns, so the application can safely free it. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`NGHTTP2_ERR_NOMEM` + * Out of memory. + */ +NGHTTP2_EXTERN int nghttp2_session_server_new3( + nghttp2_session **session_ptr, const nghttp2_session_callbacks *callbacks, + void *user_data, const nghttp2_option *option, nghttp2_mem *mem); + +/** + * @function + * + * Frees any resources allocated for |session|. If |session| is + * ``NULL``, this function does nothing. + */ +NGHTTP2_EXTERN void nghttp2_session_del(nghttp2_session *session); + +/** + * @function + * + * Sends pending frames to the remote peer. + * + * This function retrieves the highest prioritized frame from the + * outbound queue and sends it to the remote peer. It does this as + * many as possible until the user callback + * :type:`nghttp2_send_callback` returns + * :enum:`NGHTTP2_ERR_WOULDBLOCK` or the outbound queue becomes empty. + * This function calls several callback functions which are passed + * when initializing the |session|. Here is the simple time chart + * which tells when each callback is invoked: + * + * 1. Get the next frame to send from outbound queue. + * + * 2. Prepare transmission of the frame. + * + * 3. If the control frame cannot be sent because some preconditions + * are not met (e.g., request HEADERS cannot be sent after GOAWAY), + * :type:`nghttp2_on_frame_not_send_callback` is invoked. Abort + * the following steps. + * + * 4. If the frame is HEADERS, PUSH_PROMISE or DATA, + * :type:`nghttp2_select_padding_callback` is invoked. + * + * 5. If the frame is request HEADERS, the stream is opened here. + * + * 6. :type:`nghttp2_before_frame_send_callback` is invoked. + * + * 7. :type:`nghttp2_send_callback` is invoked one or more times to + * send the frame. + * + * 8. :type:`nghttp2_on_frame_send_callback` is invoked. + * + * 9. If the transmission of the frame triggers closure of the stream, + * the stream is closed and + * :type:`nghttp2_on_stream_close_callback` is invoked. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`NGHTTP2_ERR_NOMEM` + * Out of memory. + * :enum:`NGHTTP2_ERR_CALLBACK_FAILURE` + * The callback function failed. + */ +NGHTTP2_EXTERN int nghttp2_session_send(nghttp2_session *session); + +/** + * @function + * + * Returns the serialized data to send. + * + * This function behaves like `nghttp2_session_send()` except that it + * does not use :type:`nghttp2_send_callback` to transmit data. + * Instead, it assigns the pointer to the serialized data to the + * |*data_ptr| and returns its length. The other callbacks are called + * in the same way as they are in `nghttp2_session_send()`. + * + * If no data is available to send, this function returns 0. + * + * This function may not return all serialized data in one invocation. + * To get all data, call this function repeatedly until it returns 0 + * or one of negative error codes. + * + * The assigned |*data_ptr| is valid until the next call of + * `nghttp2_session_mem_send()` or `nghttp2_session_send()`. + * + * The caller must send all data before sending the next chunk of + * data. + * + * This function returns the length of the data pointed by the + * |*data_ptr| if it succeeds, or one of the following negative error + * codes: + * + * :enum:`NGHTTP2_ERR_NOMEM` + * Out of memory. + */ +NGHTTP2_EXTERN ssize_t nghttp2_session_mem_send(nghttp2_session *session, + const uint8_t **data_ptr); + +/** + * @function + * + * Receives frames from the remote peer. + * + * This function receives as many frames as possible until the user + * callback :type:`nghttp2_recv_callback` returns + * :enum:`NGHTTP2_ERR_WOULDBLOCK`. This function calls several + * callback functions which are passed when initializing the + * |session|. Here is the simple time chart which tells when each + * callback is invoked: + * + * 1. :type:`nghttp2_recv_callback` is invoked one or more times to + * receive frame header. + * + * 2. When frame header is received, + * :type:`nghttp2_on_begin_frame_callback` is invoked. + * + * 3. If the frame is DATA frame: + * + * 1. :type:`nghttp2_recv_callback` is invoked to receive DATA + * payload. For each chunk of data, + * :type:`nghttp2_on_data_chunk_recv_callback` is invoked. + * + * 2. If one DATA frame is completely received, + * :type:`nghttp2_on_frame_recv_callback` is invoked. If the + * reception of the frame triggers the closure of the stream, + * :type:`nghttp2_on_stream_close_callback` is invoked. + * + * 4. If the frame is the control frame: + * + * 1. :type:`nghttp2_recv_callback` is invoked one or more times to + * receive whole frame. + * + * 2. If the received frame is valid, then following actions are + * taken. If the frame is either HEADERS or PUSH_PROMISE, + * :type:`nghttp2_on_begin_headers_callback` is invoked. Then + * :type:`nghttp2_on_header_callback` is invoked for each header + * name/value pair. After all name/value pairs are emitted + * successfully, :type:`nghttp2_on_frame_recv_callback` is + * invoked. For other frames, + * :type:`nghttp2_on_frame_recv_callback` is invoked. If the + * reception of the frame triggers the closure of the stream, + * :type:`nghttp2_on_stream_close_callback` is invoked. + * + * 3. If the received frame is unpacked but is interpreted as + * invalid, :type:`nghttp2_on_invalid_frame_recv_callback` is + * invoked. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`NGHTTP2_ERR_EOF` + * The remote peer did shutdown on the connection. + * :enum:`NGHTTP2_ERR_NOMEM` + * Out of memory. + * :enum:`NGHTTP2_ERR_CALLBACK_FAILURE` + * The callback function failed. + * :enum:`NGHTTP2_ERR_BAD_CLIENT_MAGIC` + * Invalid client magic was detected. This error only returns + * when |session| was configured as server and + * `nghttp2_option_set_no_recv_client_magic()` is not used with + * nonzero value. + */ +NGHTTP2_EXTERN int nghttp2_session_recv(nghttp2_session *session); + +/** + * @function + * + * Processes data |in| as an input from the remote endpoint. The + * |inlen| indicates the number of bytes in the |in|. + * + * This function behaves like `nghttp2_session_recv()` except that it + * does not use :type:`nghttp2_recv_callback` to receive data; the + * |in| is the only data for the invocation of this function. If all + * bytes are processed, this function returns. The other callbacks + * are called in the same way as they are in `nghttp2_session_recv()`. + * + * In the current implementation, this function always tries to + * processes all input data unless either an error occurs or + * :enum:`NGHTTP2_ERR_PAUSE` is returned from + * :type:`nghttp2_on_header_callback` or + * :type:`nghttp2_on_data_chunk_recv_callback`. If + * :enum:`NGHTTP2_ERR_PAUSE` is used, the return value includes the + * number of bytes which was used to produce the data or frame for the + * callback. + * + * This function returns the number of processed bytes, or one of the + * following negative error codes: + * + * :enum:`NGHTTP2_ERR_NOMEM` + * Out of memory. + * :enum:`NGHTTP2_ERR_CALLBACK_FAILURE` + * The callback function failed. + * :enum:`NGHTTP2_ERR_BAD_CLIENT_MAGIC` + * Invalid client magic was detected. This error only returns + * when |session| was configured as server and + * `nghttp2_option_set_no_recv_client_magic()` is not used with + * nonzero value. + */ +NGHTTP2_EXTERN ssize_t nghttp2_session_mem_recv(nghttp2_session *session, + const uint8_t *in, + size_t inlen); + +/** + * @function + * + * Puts back previously deferred DATA frame in the stream |stream_id| + * to the outbound queue. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`NGHTTP2_ERR_INVALID_ARGUMENT` + * The stream does not exist; or no deferred data exist. + * :enum:`NGHTTP2_ERR_NOMEM` + * Out of memory. + */ +NGHTTP2_EXTERN int nghttp2_session_resume_data(nghttp2_session *session, + int32_t stream_id); + +/** + * @function + * + * Returns nonzero value if |session| wants to receive data from the + * remote peer. + * + * If both `nghttp2_session_want_read()` and + * `nghttp2_session_want_write()` return 0, the application should + * drop the connection. + */ +NGHTTP2_EXTERN int nghttp2_session_want_read(nghttp2_session *session); + +/** + * @function + * + * Returns nonzero value if |session| wants to send data to the remote + * peer. + * + * If both `nghttp2_session_want_read()` and + * `nghttp2_session_want_write()` return 0, the application should + * drop the connection. + */ +NGHTTP2_EXTERN int nghttp2_session_want_write(nghttp2_session *session); + +/** + * @function + * + * Returns stream_user_data for the stream |stream_id|. The + * stream_user_data is provided by `nghttp2_submit_request()`, + * `nghttp2_submit_headers()` or + * `nghttp2_session_set_stream_user_data()`. Unless it is set using + * `nghttp2_session_set_stream_user_data()`, if the stream is + * initiated by the remote endpoint, stream_user_data is always + * ``NULL``. If the stream does not exist, this function returns + * ``NULL``. + */ +NGHTTP2_EXTERN void * +nghttp2_session_get_stream_user_data(nghttp2_session *session, + int32_t stream_id); + +/** + * @function + * + * Sets the |stream_user_data| to the stream denoted by the + * |stream_id|. If a stream user data is already set to the stream, + * it is replaced with the |stream_user_data|. It is valid to specify + * ``NULL`` in the |stream_user_data|, which nullifies the associated + * data pointer. + * + * It is valid to set the |stream_user_data| to the stream reserved by + * PUSH_PROMISE frame. + * + * This function returns 0 if it succeeds, or one of following + * negative error codes: + * + * :enum:`NGHTTP2_ERR_INVALID_ARGUMENT` + * The stream does not exist + */ +NGHTTP2_EXTERN int +nghttp2_session_set_stream_user_data(nghttp2_session *session, + int32_t stream_id, void *stream_user_data); + +/** + * @function + * + * Returns the number of frames in the outbound queue. This does not + * include the deferred DATA frames. + */ +NGHTTP2_EXTERN size_t + nghttp2_session_get_outbound_queue_size(nghttp2_session *session); + +/** + * @function + * + * Returns the number of DATA payload in bytes received without + * WINDOW_UPDATE transmission for the stream |stream_id|. The local + * (receive) window size can be adjusted by + * `nghttp2_submit_window_update()`. This function takes into account + * that and returns effective data length. In particular, if the + * local window size is reduced by submitting negative + * window_size_increment with `nghttp2_submit_window_update()`, this + * function returns the number of bytes less than actually received. + * + * This function returns -1 if it fails. + */ +NGHTTP2_EXTERN int32_t nghttp2_session_get_stream_effective_recv_data_length( + nghttp2_session *session, int32_t stream_id); + +/** + * @function + * + * Returns the local (receive) window size for the stream |stream_id|. + * The local window size can be adjusted by + * `nghttp2_submit_window_update()`. This function takes into account + * that and returns effective window size. + * + * This function returns -1 if it fails. + */ +NGHTTP2_EXTERN int32_t nghttp2_session_get_stream_effective_local_window_size( + nghttp2_session *session, int32_t stream_id); + +/** + * @function + * + * Returns the number of DATA payload in bytes received without + * WINDOW_UPDATE transmission for a connection. The local (receive) + * window size can be adjusted by `nghttp2_submit_window_update()`. + * This function takes into account that and returns effective data + * length. In particular, if the local window size is reduced by + * submitting negative window_size_increment with + * `nghttp2_submit_window_update()`, this function returns the number + * of bytes less than actually received. + * + * This function returns -1 if it fails. + */ +NGHTTP2_EXTERN int32_t + nghttp2_session_get_effective_recv_data_length(nghttp2_session *session); + +/** + * @function + * + * Returns the local (receive) window size for a connection. The + * local window size can be adjusted by + * `nghttp2_submit_window_update()`. This function takes into account + * that and returns effective window size. + * + * This function returns -1 if it fails. + */ +NGHTTP2_EXTERN int32_t + nghttp2_session_get_effective_local_window_size(nghttp2_session *session); + +/** + * @function + * + * Returns the remote window size for a given stream |stream_id|. + * + * This is the amount of flow-controlled payload (e.g., DATA) that the + * local endpoint can send without stream level WINDOW_UPDATE. There + * is also connection level flow control, so the effective size of + * payload that the local endpoint can actually send is + * min(`nghttp2_session_get_stream_remote_window_size()`, + * `nghttp2_session_get_remote_window_size()`). + * + * This function returns -1 if it fails. + */ +NGHTTP2_EXTERN int32_t + nghttp2_session_get_stream_remote_window_size(nghttp2_session *session, + int32_t stream_id); + +/** + * @function + * + * Returns the remote window size for a connection. + * + * This function always succeeds. + */ +NGHTTP2_EXTERN int32_t + nghttp2_session_get_remote_window_size(nghttp2_session *session); + +/** + * @function + * + * Returns 1 if local peer half closed the given stream |stream_id|. + * Returns 0 if it did not. Returns -1 if no such stream exists. + */ +NGHTTP2_EXTERN int +nghttp2_session_get_stream_local_close(nghttp2_session *session, + int32_t stream_id); + +/** + * @function + * + * Returns 1 if remote peer half closed the given stream |stream_id|. + * Returns 0 if it did not. Returns -1 if no such stream exists. + */ +NGHTTP2_EXTERN int +nghttp2_session_get_stream_remote_close(nghttp2_session *session, + int32_t stream_id); + +/** + * @function + * + * Signals the session so that the connection should be terminated. + * + * The last stream ID is the minimum value between the stream ID of a + * stream for which :type:`nghttp2_on_frame_recv_callback` was called + * most recently and the last stream ID we have sent to the peer + * previously. + * + * The |error_code| is the error code of this GOAWAY frame. The + * pre-defined error code is one of :enum:`nghttp2_error_code`. + * + * After the transmission, both `nghttp2_session_want_read()` and + * `nghttp2_session_want_write()` return 0. + * + * This function should be called when the connection should be + * terminated after sending GOAWAY. If the remaining streams should + * be processed after GOAWAY, use `nghttp2_submit_goaway()` instead. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`NGHTTP2_ERR_NOMEM` + * Out of memory. + */ +NGHTTP2_EXTERN int nghttp2_session_terminate_session(nghttp2_session *session, + uint32_t error_code); + +/** + * @function + * + * Signals the session so that the connection should be terminated. + * + * This function behaves like `nghttp2_session_terminate_session()`, + * but the last stream ID can be specified by the application for fine + * grained control of stream. The HTTP/2 specification does not allow + * last_stream_id to be increased. So the actual value sent as + * last_stream_id is the minimum value between the given + * |last_stream_id| and the last_stream_id we have previously sent to + * the peer. + * + * The |last_stream_id| is peer's stream ID or 0. So if |session| is + * initialized as client, |last_stream_id| must be even or 0. If + * |session| is initialized as server, |last_stream_id| must be odd or + * 0. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`NGHTTP2_ERR_NOMEM` + * Out of memory. + * :enum:`NGHTTP2_ERR_INVALID_ARGUMENT` + * The |last_stream_id| is invalid. + */ +NGHTTP2_EXTERN int nghttp2_session_terminate_session2(nghttp2_session *session, + int32_t last_stream_id, + uint32_t error_code); + +/** + * @function + * + * Signals to the client that the server started graceful shutdown + * procedure. + * + * This function is only usable for server. If this function is + * called with client side session, this function returns + * :enum:`NGHTTP2_ERR_INVALID_STATE`. + * + * To gracefully shutdown HTTP/2 session, server should call this + * function to send GOAWAY with last_stream_id (1u << 31) - 1. And + * after some delay (e.g., 1 RTT), send another GOAWAY with the stream + * ID that the server has some processing using + * `nghttp2_submit_goaway()`. See also + * `nghttp2_session_get_last_proc_stream_id()`. + * + * Unlike `nghttp2_submit_goaway()`, this function just sends GOAWAY + * and does nothing more. This is a mere indication to the client + * that session shutdown is imminent. The application should call + * `nghttp2_submit_goaway()` with appropriate last_stream_id after + * this call. + * + * If one or more GOAWAY frame have been already sent by either + * `nghttp2_submit_goaway()` or `nghttp2_session_terminate_session()`, + * this function has no effect. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`NGHTTP2_ERR_NOMEM` + * Out of memory. + * :enum:`NGHTTP2_ERR_INVALID_STATE` + * The |session| is initialized as client. + */ +NGHTTP2_EXTERN int nghttp2_submit_shutdown_notice(nghttp2_session *session); + +/** + * @function + * + * Returns the value of SETTINGS |id| notified by a remote endpoint. + * The |id| must be one of values defined in + * :enum:`nghttp2_settings_id`. + */ +NGHTTP2_EXTERN uint32_t + nghttp2_session_get_remote_settings(nghttp2_session *session, + nghttp2_settings_id id); + +/** + * @function + * + * Tells the |session| that next stream ID is |next_stream_id|. The + * |next_stream_id| must be equal or greater than the value returned + * by `nghttp2_session_get_next_stream_id()`. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`NGHTTP2_ERR_INVALID_ARGUMENT` + * The |next_stream_id| is strictly less than the value + * `nghttp2_session_get_next_stream_id()` returns; or + * |next_stream_id| is invalid (e.g., even integer for client, or + * odd integer for server). + */ +NGHTTP2_EXTERN int nghttp2_session_set_next_stream_id(nghttp2_session *session, + int32_t next_stream_id); + +/** + * @function + * + * Returns the next outgoing stream ID. Notice that return type is + * uint32_t. If we run out of stream ID for this session, this + * function returns 1 << 31. + */ +NGHTTP2_EXTERN uint32_t + nghttp2_session_get_next_stream_id(nghttp2_session *session); + +/** + * @function + * + * Tells the |session| that |size| bytes for a stream denoted by + * |stream_id| were consumed by application and are ready to + * WINDOW_UPDATE. The consumed bytes are counted towards both + * connection and stream level WINDOW_UPDATE (see + * `nghttp2_session_consume_connection()` and + * `nghttp2_session_consume_stream()` to update consumption + * independently). This function is intended to be used without + * automatic window update (see + * `nghttp2_option_set_no_auto_window_update()`). + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`NGHTTP2_ERR_NOMEM` + * Out of memory. + * :enum:`NGHTTP2_ERR_INVALID_ARGUMENT` + * The |stream_id| is 0. + * :enum:`NGHTTP2_ERR_INVALID_STATE` + * Automatic WINDOW_UPDATE is not disabled. + */ +NGHTTP2_EXTERN int nghttp2_session_consume(nghttp2_session *session, + int32_t stream_id, size_t size); + +/** + * @function + * + * Like `nghttp2_session_consume()`, but this only tells library that + * |size| bytes were consumed only for connection level. Note that + * HTTP/2 maintains connection and stream level flow control windows + * independently. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`NGHTTP2_ERR_NOMEM` + * Out of memory. + * :enum:`NGHTTP2_ERR_INVALID_STATE` + * Automatic WINDOW_UPDATE is not disabled. + */ +NGHTTP2_EXTERN int nghttp2_session_consume_connection(nghttp2_session *session, + size_t size); + +/** + * @function + * + * Like `nghttp2_session_consume()`, but this only tells library that + * |size| bytes were consumed only for stream denoted by |stream_id|. + * Note that HTTP/2 maintains connection and stream level flow control + * windows independently. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`NGHTTP2_ERR_NOMEM` + * Out of memory. + * :enum:`NGHTTP2_ERR_INVALID_ARGUMENT` + * The |stream_id| is 0. + * :enum:`NGHTTP2_ERR_INVALID_STATE` + * Automatic WINDOW_UPDATE is not disabled. + */ +NGHTTP2_EXTERN int nghttp2_session_consume_stream(nghttp2_session *session, + int32_t stream_id, + size_t size); + +/** + * @function + * + * Performs post-process of HTTP Upgrade request. This function can + * be called from both client and server, but the behavior is very + * different in each other. + * + * If called from client side, the |settings_payload| must be the + * value sent in ``HTTP2-Settings`` header field and must be decoded + * by base64url decoder. The |settings_payloadlen| is the length of + * |settings_payload|. The |settings_payload| is unpacked and its + * setting values will be submitted using `nghttp2_submit_settings()`. + * This means that the client application code does not need to submit + * SETTINGS by itself. The stream with stream ID=1 is opened and the + * |stream_user_data| is used for its stream_user_data. The opened + * stream becomes half-closed (local) state. + * + * If called from server side, the |settings_payload| must be the + * value received in ``HTTP2-Settings`` header field and must be + * decoded by base64url decoder. The |settings_payloadlen| is the + * length of |settings_payload|. It is treated as if the SETTINGS + * frame with that payload is received. Thus, callback functions for + * the reception of SETTINGS frame will be invoked. The stream with + * stream ID=1 is opened. The |stream_user_data| is ignored. The + * opened stream becomes half-closed (remote). + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`NGHTTP2_ERR_NOMEM` + * Out of memory. + * :enum:`NGHTTP2_ERR_INVALID_ARGUMENT` + * The |settings_payload| is badly formed. + * :enum:`NGHTTP2_ERR_PROTO` + * The stream ID 1 is already used or closed; or is not available. + */ +NGHTTP2_EXTERN int nghttp2_session_upgrade(nghttp2_session *session, + const uint8_t *settings_payload, + size_t settings_payloadlen, + void *stream_user_data); + +/** + * @function + * + * Serializes the SETTINGS values |iv| in the |buf|. The size of the + * |buf| is specified by |buflen|. The number of entries in the |iv| + * array is given by |niv|. The required space in |buf| for the |niv| + * entries is ``8*niv`` bytes and if the given buffer is too small, an + * error is returned. This function is used mainly for creating a + * SETTINGS payload to be sent with the ``HTTP2-Settings`` header + * field in an HTTP Upgrade request. The data written in |buf| is NOT + * base64url encoded and the application is responsible for encoding. + * + * This function returns the number of bytes written in |buf|, or one + * of the following negative error codes: + * + * :enum:`NGHTTP2_ERR_INVALID_ARGUMENT` + * The |iv| contains duplicate settings ID or invalid value. + * + * :enum:`NGHTTP2_ERR_INSUFF_BUFSIZE` + * The provided |buflen| size is too small to hold the output. + */ +NGHTTP2_EXTERN ssize_t + nghttp2_pack_settings_payload(uint8_t *buf, size_t buflen, + const nghttp2_settings_entry *iv, size_t niv); + +/** + * @function + * + * Returns string describing the |lib_error_code|. The + * |lib_error_code| must be one of the :enum:`nghttp2_error`. + */ +NGHTTP2_EXTERN const char *nghttp2_strerror(int lib_error_code); + +/** + * @function + * + * Initializes |pri_spec| with the |stream_id| of the stream to depend + * on with |weight| and its exclusive flag. If |exclusive| is + * nonzero, exclusive flag is set. + * + * The |weight| must be in [:enum:`NGHTTP2_MIN_WEIGHT`, + * :enum:`NGHTTP2_MAX_WEIGHT`], inclusive. + */ +NGHTTP2_EXTERN void nghttp2_priority_spec_init(nghttp2_priority_spec *pri_spec, + int32_t stream_id, + int32_t weight, int exclusive); + +/** + * @function + * + * Initializes |pri_spec| with the default values. The default values + * are: stream_id = 0, weight = :macro:`NGHTTP2_DEFAULT_WEIGHT` and + * exclusive = 0. + */ +NGHTTP2_EXTERN void +nghttp2_priority_spec_default_init(nghttp2_priority_spec *pri_spec); + +/** + * @function + * + * Returns nonzero if the |pri_spec| is filled with default values. + */ +NGHTTP2_EXTERN int +nghttp2_priority_spec_check_default(const nghttp2_priority_spec *pri_spec); + +/** + * @function + * + * Submits HEADERS frame and optionally one or more DATA frames. + * + * The |pri_spec| is priority specification of this request. ``NULL`` + * means the default priority (see + * `nghttp2_priority_spec_default_init()`). To specify the priority, + * use `nghttp2_priority_spec_init()`. If |pri_spec| is not ``NULL``, + * this function will copy its data members. + * + * The ``pri_spec->weight`` must be in [:enum:`NGHTTP2_MIN_WEIGHT`, + * :enum:`NGHTTP2_MAX_WEIGHT`], inclusive. If ``pri_spec->weight`` is + * strictly less than :enum:`NGHTTP2_MIN_WEIGHT`, it becomes + * :enum:`NGHTTP2_MIN_WEIGHT`. If it is strictly greater than + * :enum:`NGHTTP2_MAX_WEIGHT`, it becomes :enum:`NGHTTP2_MAX_WEIGHT`. + * + * The |nva| is an array of name/value pair :type:`nghttp2_nv` with + * |nvlen| elements. The application is responsible to include + * required pseudo-header fields (header field whose name starts with + * ":") in |nva| and must place pseudo-headers before regular header + * fields. + * + * This function creates copies of all name/value pairs in |nva|. It + * also lower-cases all names in |nva|. The order of elements in + * |nva| is preserved. + * + * HTTP/2 specification has requirement about header fields in the + * request HEADERS. See the specification for more details. + * + * If |data_prd| is not ``NULL``, it provides data which will be sent + * in subsequent DATA frames. In this case, a method that allows + * request message bodies + * (https://tools.ietf.org/html/rfc7231#section-4) must be specified + * with ``:method`` key in |nva| (e.g. ``POST``). This function does + * not take ownership of the |data_prd|. The function copies the + * members of the |data_prd|. If |data_prd| is ``NULL``, HEADERS have + * END_STREAM set. The |stream_user_data| is data associated to the + * stream opened by this request and can be an arbitrary pointer, + * which can be retrieved later by + * `nghttp2_session_get_stream_user_data()`. + * + * This function returns assigned stream ID if it succeeds, or one of + * the following negative error codes: + * + * :enum:`NGHTTP2_ERR_NOMEM` + * Out of memory. + * :enum:`NGHTTP2_ERR_STREAM_ID_NOT_AVAILABLE` + * No stream ID is available because maximum stream ID was + * reached. + * + * .. warning:: + * + * This function returns assigned stream ID if it succeeds. But + * that stream is not opened yet. The application must not submit + * frame to that stream ID before + * :type:`nghttp2_before_frame_send_callback` is called for this + * frame. + * + */ +NGHTTP2_EXTERN int32_t + nghttp2_submit_request(nghttp2_session *session, + const nghttp2_priority_spec *pri_spec, + const nghttp2_nv *nva, size_t nvlen, + const nghttp2_data_provider *data_prd, + void *stream_user_data); + +/** + * @function + * + * Submits response HEADERS frame and optionally one or more DATA + * frames against the stream |stream_id|. + * + * The |nva| is an array of name/value pair :type:`nghttp2_nv` with + * |nvlen| elements. The application is responsible to include + * required pseudo-header fields (header field whose name starts with + * ":") in |nva| and must place pseudo-headers before regular header + * fields. + * + * This function creates copies of all name/value pairs in |nva|. It + * also lower-cases all names in |nva|. The order of elements in + * |nva| is preserved. + * + * HTTP/2 specification has requirement about header fields in the + * response HEADERS. See the specification for more details. + * + * If |data_prd| is not ``NULL``, it provides data which will be sent + * in subsequent DATA frames. This function does not take ownership + * of the |data_prd|. The function copies the members of the + * |data_prd|. If |data_prd| is ``NULL``, HEADERS will have + * END_STREAM flag set. + * + * This method can be used as normal HTTP response and push response. + * When pushing a resource using this function, the |session| must be + * configured using `nghttp2_session_server_new()` or its variants and + * the target stream denoted by the |stream_id| must be reserved using + * `nghttp2_submit_push_promise()`. + * + * To send non-final response headers (e.g., HTTP status 101), don't + * use this function because this function half-closes the outbound + * stream. Instead, use `nghttp2_submit_headers()` for this purpose. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`NGHTTP2_ERR_NOMEM` + * Out of memory. + * :enum:`NGHTTP2_ERR_INVALID_ARGUMENT` + * The |stream_id| is 0. + * + * .. warning:: + * + * Calling this function twice for the same stream ID may lead to + * program crash. It is generally considered to a programming error + * to commit response twice. + */ +NGHTTP2_EXTERN int +nghttp2_submit_response(nghttp2_session *session, int32_t stream_id, + const nghttp2_nv *nva, size_t nvlen, + const nghttp2_data_provider *data_prd); + +/** + * @function + * + * Submits trailer HEADERS against the stream |stream_id|. + * + * The |nva| is an array of name/value pair :type:`nghttp2_nv` with + * |nvlen| elements. The application is responsible not to include + * required pseudo-header fields (header field whose name starts with + * ":") in |nva|. + * + * This function creates copies of all name/value pairs in |nva|. It + * also lower-cases all names in |nva|. The order of elements in + * |nva| is preserved. + * + * For server, trailer must be followed by response HEADERS or + * response DATA. The library does not check that response HEADERS + * has already sent and if `nghttp2_submit_trailer()` is called before + * any response HEADERS submission (usually by + * `nghttp2_submit_response()`), the content of |nva| will be sent as + * reponse headers, which will result in error. + * + * This function has the same effect with `nghttp2_submit_headers()`, + * with flags = :enum:`NGHTTP2_FLAG_END_HEADERS` and both pri_spec and + * stream_user_data to NULL. + * + * To submit trailer after `nghttp2_submit_response()` is called, the + * application has to specify :type:`nghttp2_data_provider` to + * `nghttp2_submit_response()`. In side + * :type:`nghttp2_data_source_read_callback`, when setting + * :enum:`NGHTTP2_DATA_FLAG_EOF`, also set + * :enum:`NGHTTP2_DATA_FLAG_NO_END_STREAM`. After that, the + * application can send trailer using `nghttp2_submit_trailer()`. + * `nghttp2_submit_trailer()` can be used inside + * :type:`nghttp2_data_source_read_callback`. + * + * This function returns 0 if it succeeds and |stream_id| is -1. + * Otherwise, this function returns 0 if it succeeds, or one of the + * following negative error codes: + * + * :enum:`NGHTTP2_ERR_NOMEM` + * Out of memory. + * :enum:`NGHTTP2_ERR_INVALID_ARGUMENT` + * The |stream_id| is 0. + */ +NGHTTP2_EXTERN int nghttp2_submit_trailer(nghttp2_session *session, + int32_t stream_id, + const nghttp2_nv *nva, size_t nvlen); + +/** + * @function + * + * Submits HEADERS frame. The |flags| is bitwise OR of the + * following values: + * + * * :enum:`NGHTTP2_FLAG_END_STREAM` + * + * If |flags| includes :enum:`NGHTTP2_FLAG_END_STREAM`, this frame has + * END_STREAM flag set. + * + * The library handles the CONTINUATION frame internally and it + * correctly sets END_HEADERS to the last sequence of the PUSH_PROMISE + * or CONTINUATION frame. + * + * If the |stream_id| is -1, this frame is assumed as request (i.e., + * request HEADERS frame which opens new stream). In this case, the + * assigned stream ID will be returned. Otherwise, specify stream ID + * in |stream_id|. + * + * The |pri_spec| is priority specification of this request. ``NULL`` + * means the default priority (see + * `nghttp2_priority_spec_default_init()`). To specify the priority, + * use `nghttp2_priority_spec_init()`. If |pri_spec| is not ``NULL``, + * this function will copy its data members. + * + * The ``pri_spec->weight`` must be in [:enum:`NGHTTP2_MIN_WEIGHT`, + * :enum:`NGHTTP2_MAX_WEIGHT`], inclusive. If ``pri_spec->weight`` is + * strictly less than :enum:`NGHTTP2_MIN_WEIGHT`, it becomes + * :enum:`NGHTTP2_MIN_WEIGHT`. If it is strictly greater than + * :enum:`NGHTTP2_MAX_WEIGHT`, it becomes :enum:`NGHTTP2_MAX_WEIGHT`. + * + * The |nva| is an array of name/value pair :type:`nghttp2_nv` with + * |nvlen| elements. The application is responsible to include + * required pseudo-header fields (header field whose name starts with + * ":") in |nva| and must place pseudo-headers before regular header + * fields. + * + * This function creates copies of all name/value pairs in |nva|. It + * also lower-cases all names in |nva|. The order of elements in + * |nva| is preserved. + * + * The |stream_user_data| is a pointer to an arbitrary data which is + * associated to the stream this frame will open. Therefore it is + * only used if this frame opens streams, in other words, it changes + * stream state from idle or reserved to open. + * + * This function is low-level in a sense that the application code can + * specify flags directly. For usual HTTP request, + * `nghttp2_submit_request()` is useful. + * + * This function returns newly assigned stream ID if it succeeds and + * |stream_id| is -1. Otherwise, this function returns 0 if it + * succeeds, or one of the following negative error codes: + * + * :enum:`NGHTTP2_ERR_NOMEM` + * Out of memory. + * :enum:`NGHTTP2_ERR_STREAM_ID_NOT_AVAILABLE` + * No stream ID is available because maximum stream ID was + * reached. + * :enum:`NGHTTP2_ERR_INVALID_ARGUMENT` + * The |stream_id| is 0. + * + * .. warning:: + * + * This function returns assigned stream ID if it succeeds and + * |stream_id| is -1. But that stream is not opened yet. The + * application must not submit frame to that stream ID before + * :type:`nghttp2_before_frame_send_callback` is called for this + * frame. + * + */ +NGHTTP2_EXTERN int32_t + nghttp2_submit_headers(nghttp2_session *session, uint8_t flags, + int32_t stream_id, + const nghttp2_priority_spec *pri_spec, + const nghttp2_nv *nva, size_t nvlen, + void *stream_user_data); + +/** + * @function + * + * Submits one or more DATA frames to the stream |stream_id|. The + * data to be sent are provided by |data_prd|. If |flags| contains + * :enum:`NGHTTP2_FLAG_END_STREAM`, the last DATA frame has END_STREAM + * flag set. + * + * This function does not take ownership of the |data_prd|. The + * function copies the members of the |data_prd|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`NGHTTP2_ERR_NOMEM` + * Out of memory. + * :enum:`NGHTTP2_ERR_DATA_EXIST` + * DATA has been already submitted and not fully processed yet. + * :enum:`NGHTTP2_ERR_INVALID_ARGUMENT` + * The |stream_id| is 0. + * :enum:`NGHTTP2_ERR_STREAM_CLOSED` + * The stream was alreay closed; or the |stream_id| is invalid. + * + * .. note:: + * + * Currently, only one data is allowed for a stream at a time. + * Submitting data more than once before first data is finished + * results in :enum:`NGHTTP2_ERR_DATA_EXIST` error code. The + * earliest callback which tells that previous data is done is + * :type:`nghttp2_on_frame_send_callback`. In side that callback, + * new data can be submitted using `nghttp2_submit_data()`. Of + * course, all data except for last one must not have + * :enum:`NGHTTP2_FLAG_END_STREAM` flag set in |flags|. + */ +NGHTTP2_EXTERN int nghttp2_submit_data(nghttp2_session *session, uint8_t flags, + int32_t stream_id, + const nghttp2_data_provider *data_prd); + +/** + * @function + * + * Submits PRIORITY frame to change the priority of stream |stream_id| + * to the priority specification |pri_spec|. + * + * The |flags| is currently ignored and should be + * :enum:`NGHTTP2_FLAG_NONE`. + * + * The |pri_spec| is priority specification of this request. ``NULL`` + * is not allowed for this function. To specify the priority, use + * `nghttp2_priority_spec_init()`. This function will copy its data + * members. + * + * The ``pri_spec->weight`` must be in [:enum:`NGHTTP2_MIN_WEIGHT`, + * :enum:`NGHTTP2_MAX_WEIGHT`], inclusive. If ``pri_spec->weight`` is + * strictly less than :enum:`NGHTTP2_MIN_WEIGHT`, it becomes + * :enum:`NGHTTP2_MIN_WEIGHT`. If it is strictly greater than + * :enum:`NGHTTP2_MAX_WEIGHT`, it becomes :enum:`NGHTTP2_MAX_WEIGHT`. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`NGHTTP2_ERR_NOMEM` + * Out of memory. + * :enum:`NGHTTP2_ERR_INVALID_ARGUMENT` + * The |stream_id| is 0; or the |pri_spec| is NULL; or trying to + * depend on itself. + */ +NGHTTP2_EXTERN int +nghttp2_submit_priority(nghttp2_session *session, uint8_t flags, + int32_t stream_id, + const nghttp2_priority_spec *pri_spec); + +/** + * @function + * + * Submits RST_STREAM frame to cancel/reject the stream |stream_id| + * with the error code |error_code|. + * + * The pre-defined error code is one of :enum:`nghttp2_error_code`. + * + * The |flags| is currently ignored and should be + * :enum:`NGHTTP2_FLAG_NONE`. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`NGHTTP2_ERR_NOMEM` + * Out of memory. + * :enum:`NGHTTP2_ERR_INVALID_ARGUMENT` + * The |stream_id| is 0. + */ +NGHTTP2_EXTERN int nghttp2_submit_rst_stream(nghttp2_session *session, + uint8_t flags, int32_t stream_id, + uint32_t error_code); + +/** + * @function + * + * Stores local settings and submits SETTINGS frame. The |iv| is the + * pointer to the array of :type:`nghttp2_settings_entry`. The |niv| + * indicates the number of :type:`nghttp2_settings_entry`. + * + * The |flags| is currently ignored and should be + * :enum:`NGHTTP2_FLAG_NONE`. + * + * This function does not take ownership of the |iv|. This function + * copies all the elements in the |iv|. + * + * While updating individual stream's local window size, if the window + * size becomes strictly larger than NGHTTP2_MAX_WINDOW_SIZE, + * RST_STREAM is issued against such a stream. + * + * SETTINGS with :enum:`NGHTTP2_FLAG_ACK` is automatically submitted + * by the library and application could not send it at its will. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`NGHTTP2_ERR_INVALID_ARGUMENT` + * The |iv| contains invalid value (e.g., initial window size + * strictly greater than (1 << 31) - 1. + * :enum:`NGHTTP2_ERR_TOO_MANY_INFLIGHT_SETTINGS` + * There is already another in-flight SETTINGS. Note that the + * current implementation only allows 1 in-flight SETTINGS frame + * without ACK flag set. + * :enum:`NGHTTP2_ERR_NOMEM` + * Out of memory. + */ +NGHTTP2_EXTERN int nghttp2_submit_settings(nghttp2_session *session, + uint8_t flags, + const nghttp2_settings_entry *iv, + size_t niv); + +/** + * @function + * + * Submits PUSH_PROMISE frame. + * + * The |flags| is currently ignored. The library handles the + * CONTINUATION frame internally and it correctly sets END_HEADERS to + * the last sequence of the PUSH_PROMISE or CONTINUATION frame. + * + * The |stream_id| must be client initiated stream ID. + * + * The |nva| is an array of name/value pair :type:`nghttp2_nv` with + * |nvlen| elements. The application is responsible to include + * required pseudo-header fields (header field whose name starts with + * ":") in |nva| and must place pseudo-headers before regular header + * fields. + * + * This function creates copies of all name/value pairs in |nva|. It + * also lower-cases all names in |nva|. The order of elements in + * |nva| is preserved. + * + * The |promised_stream_user_data| is a pointer to an arbitrary data + * which is associated to the promised stream this frame will open and + * make it in reserved state. It is available using + * `nghttp2_session_get_stream_user_data()`. The application can + * access it in :type:`nghttp2_before_frame_send_callback` and + * :type:`nghttp2_on_frame_send_callback` of this frame. + * + * The client side is not allowed to use this function. + * + * To submit response headers and data, use + * `nghttp2_submit_response()`. + * + * This function returns assigned promised stream ID if it succeeds, + * or one of the following negative error codes: + * + * :enum:`NGHTTP2_ERR_NOMEM` + * Out of memory. + * :enum:`NGHTTP2_ERR_PROTO` + * This function was invoked when |session| is initialized as + * client. + * :enum:`NGHTTP2_ERR_STREAM_ID_NOT_AVAILABLE` + * No stream ID is available because maximum stream ID was + * reached. + * :enum:`NGHTTP2_ERR_INVALID_ARGUMENT` + * The |stream_id| is 0; The |stream_id| does not designate stream + * that peer initiated. + * + * .. warning:: + * + * This function returns assigned promised stream ID if it succeeds. + * But that stream is not opened yet. The application must not + * submit frame to that stream ID before + * :type:`nghttp2_before_frame_send_callback` is called for this + * frame. + * + */ +NGHTTP2_EXTERN int32_t + nghttp2_submit_push_promise(nghttp2_session *session, uint8_t flags, + int32_t stream_id, const nghttp2_nv *nva, + size_t nvlen, void *promised_stream_user_data); + +/** + * @function + * + * Submits PING frame. You don't have to send PING back when you + * received PING frame. The library automatically submits PING frame + * in this case. + * + * The |flags| is currently ignored and should be + * :enum:`NGHTTP2_FLAG_NONE`. + * + * If the |opaque_data| is non ``NULL``, then it should point to the 8 + * bytes array of memory to specify opaque data to send with PING + * frame. If the |opaque_data| is ``NULL``, zero-cleared 8 bytes will + * be sent as opaque data. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`NGHTTP2_ERR_NOMEM` + * Out of memory. + */ +NGHTTP2_EXTERN int nghttp2_submit_ping(nghttp2_session *session, uint8_t flags, + const uint8_t *opaque_data); + +/** + * @function + * + * Submits GOAWAY frame with the last stream ID |last_stream_id| and + * the error code |error_code|. + * + * The pre-defined error code is one of :enum:`nghttp2_error_code`. + * + * The |flags| is currently ignored and should be + * :enum:`NGHTTP2_FLAG_NONE`. + * + * The |last_stream_id| is peer's stream ID or 0. So if |session| is + * initialized as client, |last_stream_id| must be even or 0. If + * |session| is initialized as server, |last_stream_id| must be odd or + * 0. + * + * The HTTP/2 specification says last_stream_id must not be increased + * from the value previously sent. So the actual value sent as + * last_stream_id is the minimum value between the given + * |last_stream_id| and the last_stream_id previously sent to the + * peer. + * + * If the |opaque_data| is not ``NULL`` and |opaque_data_len| is not + * zero, those data will be sent as additional debug data. The + * library makes a copy of the memory region pointed by |opaque_data| + * with the length |opaque_data_len|, so the caller does not need to + * keep this memory after the return of this function. If the + * |opaque_data_len| is 0, the |opaque_data| could be ``NULL``. + * + * After successful transmission of GOAWAY, following things happen. + * All incoming streams having strictly more than |last_stream_id| are + * closed. All incoming HEADERS which starts new stream are simply + * ignored. After all active streams are handled, both + * `nghttp2_session_want_read()` and `nghttp2_session_want_write()` + * return 0 and the application can close session. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`NGHTTP2_ERR_NOMEM` + * Out of memory. + * :enum:`NGHTTP2_ERR_INVALID_ARGUMENT` + * The |opaque_data_len| is too large; the |last_stream_id| is + * invalid. + */ +NGHTTP2_EXTERN int nghttp2_submit_goaway(nghttp2_session *session, + uint8_t flags, int32_t last_stream_id, + uint32_t error_code, + const uint8_t *opaque_data, + size_t opaque_data_len); + +/** + * @function + * + * Returns the last stream ID of a stream for which + * :type:`nghttp2_on_frame_recv_callback` was invoked most recently. + * The returned value can be used as last_stream_id parameter for + * `nghttp2_submit_goaway()` and + * `nghttp2_session_terminate_session2()`. + * + * This function always succeeds. + */ +NGHTTP2_EXTERN int32_t + nghttp2_session_get_last_proc_stream_id(nghttp2_session *session); + +/** + * @function + * + * Submits WINDOW_UPDATE frame. + * + * The |flags| is currently ignored and should be + * :enum:`NGHTTP2_FLAG_NONE`. + * + * The |stream_id| is the stream ID to send this WINDOW_UPDATE. To + * send connection level WINDOW_UPDATE, specify 0 to |stream_id|. + * + * If the |window_size_increment| is positive, the WINDOW_UPDATE with + * that value as window_size_increment is queued. If the + * |window_size_increment| is larger than the received bytes from the + * remote endpoint, the local window size is increased by that + * difference. + * + * If the |window_size_increment| is negative, the local window size + * is decreased by -|window_size_increment|. If automatic + * WINDOW_UPDATE is enabled + * (`nghttp2_option_set_no_auto_window_update()`), and the library + * decided that the WINDOW_UPDATE should be submitted, then + * WINDOW_UPDATE is queued with the current received bytes count. + * + * If the |window_size_increment| is 0, the function does nothing and + * returns 0. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`NGHTTP2_ERR_FLOW_CONTROL` + * The local window size overflow or gets negative. + * :enum:`NGHTTP2_ERR_NOMEM` + * Out of memory. + */ +NGHTTP2_EXTERN int nghttp2_submit_window_update(nghttp2_session *session, + uint8_t flags, + int32_t stream_id, + int32_t window_size_increment); + +/** + * @function + * + * Compares ``lhs->name`` of length ``lhs->namelen`` bytes and + * ``rhs->name`` of length ``rhs->namelen`` bytes. Returns negative + * integer if ``lhs->name`` is found to be less than ``rhs->name``; or + * returns positive integer if ``lhs->name`` is found to be greater + * than ``rhs->name``; or returns 0 otherwise. + */ +NGHTTP2_EXTERN int nghttp2_nv_compare_name(const nghttp2_nv *lhs, + const nghttp2_nv *rhs); + +/** + * @function + * + * A helper function for dealing with NPN in client side or ALPN in + * server side. The |in| contains peer's protocol list in preferable + * order. The format of |in| is length-prefixed and not + * null-terminated. For example, ``h2`` and + * ``http/1.1`` stored in |in| like this:: + * + * in[0] = 2 + * in[1..2] = "h2" + * in[3] = 8 + * in[4..11] = "http/1.1" + * inlen = 12 + * + * The selection algorithm is as follows: + * + * 1. If peer's list contains HTTP/2 protocol the library supports, + * it is selected and returns 1. The following step is not taken. + * + * 2. If peer's list contains ``http/1.1``, this function selects + * ``http/1.1`` and returns 0. The following step is not taken. + * + * 3. This function selects nothing and returns -1 (So called + * non-overlap case). In this case, |out| and |outlen| are left + * untouched. + * + * Selecting ``h2`` means that ``h2`` is written into |*out| and its + * length (which is 2) is assigned to |*outlen|. + * + * For ALPN, refer to https://tools.ietf.org/html/rfc7301 + * + * See http://technotes.googlecode.com/git/nextprotoneg.html for more + * details about NPN. + * + * For NPN, to use this method you should do something like:: + * + * static int select_next_proto_cb(SSL* ssl, + * unsigned char **out, + * unsigned char *outlen, + * const unsigned char *in, + * unsigned int inlen, + * void *arg) + * { + * int rv; + * rv = nghttp2_select_next_protocol(out, outlen, in, inlen); + * if (rv == -1) { + * return SSL_TLSEXT_ERR_NOACK; + * } + * if (rv == 1) { + * ((MyType*)arg)->http2_selected = 1; + * } + * return SSL_TLSEXT_ERR_OK; + * } + * ... + * SSL_CTX_set_next_proto_select_cb(ssl_ctx, select_next_proto_cb, my_obj); + * + */ +NGHTTP2_EXTERN int nghttp2_select_next_protocol(unsigned char **out, + unsigned char *outlen, + const unsigned char *in, + unsigned int inlen); + +/** + * @function + * + * Returns a pointer to a nghttp2_info struct with version information + * about the run-time library in use. The |least_version| argument + * can be set to a 24 bit numerical value for the least accepted + * version number and if the condition is not met, this function will + * return a ``NULL``. Pass in 0 to skip the version checking. + */ +NGHTTP2_EXTERN nghttp2_info *nghttp2_version(int least_version); + +/** + * @function + * + * Returns nonzero if the :type:`nghttp2_error` library error code + * |lib_error| is fatal. + */ +NGHTTP2_EXTERN int nghttp2_is_fatal(int lib_error_code); + +/** + * @function + * + * Returns nonzero if HTTP header field name |name| of length |len| is + * valid according to http://tools.ietf.org/html/rfc7230#section-3.2 + * + * Because this is a header field name in HTTP2, the upper cased alphabet + * is treated as error. + */ +NGHTTP2_EXTERN int nghttp2_check_header_name(const uint8_t *name, size_t len); + +/** + * @function + * + * Returns nonzero if HTTP header field value |value| of length |len| + * is valid according to + * http://tools.ietf.org/html/rfc7230#section-3.2 + */ +NGHTTP2_EXTERN int nghttp2_check_header_value(const uint8_t *value, size_t len); + +/* HPACK API */ + +struct nghttp2_hd_deflater; + +/** + * @struct + * + * HPACK deflater object. + */ +typedef struct nghttp2_hd_deflater nghttp2_hd_deflater; + +/** + * @function + * + * Initializes |*deflater_ptr| for deflating name/values pairs. + * + * The |deflate_hd_table_bufsize_max| is the upper bound of header + * table size the deflater will use. + * + * If this function fails, |*deflater_ptr| is left untouched. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`NGHTTP2_ERR_NOMEM` + * Out of memory. + */ +NGHTTP2_EXTERN int nghttp2_hd_deflate_new(nghttp2_hd_deflater **deflater_ptr, + size_t deflate_hd_table_bufsize_max); + +/** + * @function + * + * Like `nghttp2_hd_deflate_new()`, but with additional custom memory + * allocator specified in the |mem|. + * + * The |mem| can be ``NULL`` and the call is equivalent to + * `nghttp2_hd_deflate_new()`. + * + * This function does not take ownership |mem|. The application is + * responsible for freeing |mem|. + * + * The library code does not refer to |mem| pointer after this + * function returns, so the application can safely free it. + */ +NGHTTP2_EXTERN int nghttp2_hd_deflate_new2(nghttp2_hd_deflater **deflater_ptr, + size_t deflate_hd_table_bufsize_max, + nghttp2_mem *mem); + +/** + * @function + * + * Deallocates any resources allocated for |deflater|. + */ +NGHTTP2_EXTERN void nghttp2_hd_deflate_del(nghttp2_hd_deflater *deflater); + +/** + * @function + * + * Changes header table size of the |deflater| to + * |settings_hd_table_bufsize_max| bytes. This may trigger eviction + * in the dynamic table. + * + * The |settings_hd_table_bufsize_max| should be the value received in + * SETTINGS_HEADER_TABLE_SIZE. + * + * The deflater never uses more memory than + * ``deflate_hd_table_bufsize_max`` bytes specified in + * `nghttp2_hd_deflate_new()`. Therefore, if + * |settings_hd_table_bufsize_max| > ``deflate_hd_table_bufsize_max``, + * resulting maximum table size becomes + * ``deflate_hd_table_bufsize_max``. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`NGHTTP2_ERR_NOMEM` + * Out of memory. + */ +NGHTTP2_EXTERN int +nghttp2_hd_deflate_change_table_size(nghttp2_hd_deflater *deflater, + size_t settings_hd_table_bufsize_max); + +/** + * @function + * + * Deflates the |nva|, which has the |nvlen| name/value pairs, into + * the |buf| of length |buflen|. + * + * If |buf| is not large enough to store the deflated header block, + * this function fails with :enum:`NGHTTP2_ERR_INSUFF_BUFSIZE`. The + * caller should use `nghttp2_hd_deflate_bound()` to know the upper + * bound of buffer size required to deflate given header name/value + * pairs. + * + * Once this function fails, subsequent call of this function always + * returns :enum:`NGHTTP2_ERR_HEADER_COMP`. + * + * After this function returns, it is safe to delete the |nva|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`NGHTTP2_ERR_NOMEM` + * Out of memory. + * :enum:`NGHTTP2_ERR_HEADER_COMP` + * Deflation process has failed. + * :enum:`NGHTTP2_ERR_INSUFF_BUFSIZE` + * The provided |buflen| size is too small to hold the output. + */ +NGHTTP2_EXTERN ssize_t + nghttp2_hd_deflate_hd(nghttp2_hd_deflater *deflater, uint8_t *buf, + size_t buflen, const nghttp2_nv *nva, size_t nvlen); + +/** + * @function + * + * Returns an upper bound on the compressed size after deflation of + * |nva| of length |nvlen|. + */ +NGHTTP2_EXTERN size_t nghttp2_hd_deflate_bound(nghttp2_hd_deflater *deflater, + const nghttp2_nv *nva, + size_t nvlen); + +struct nghttp2_hd_inflater; + +/** + * @struct + * + * HPACK inflater object. + */ +typedef struct nghttp2_hd_inflater nghttp2_hd_inflater; + +/** + * @function + * + * Initializes |*inflater_ptr| for inflating name/values pairs. + * + * If this function fails, |*inflater_ptr| is left untouched. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`NGHTTP2_ERR_NOMEM` + * Out of memory. + */ +NGHTTP2_EXTERN int nghttp2_hd_inflate_new(nghttp2_hd_inflater **inflater_ptr); + +/** + * @function + * + * Like `nghttp2_hd_inflate_new()`, but with additional custom memory + * allocator specified in the |mem|. + * + * The |mem| can be ``NULL`` and the call is equivalent to + * `nghttp2_hd_inflate_new()`. + * + * This function does not take ownership |mem|. The application is + * responsible for freeing |mem|. + * + * The library code does not refer to |mem| pointer after this + * function returns, so the application can safely free it. + */ +NGHTTP2_EXTERN int nghttp2_hd_inflate_new2(nghttp2_hd_inflater **inflater_ptr, + nghttp2_mem *mem); + +/** + * @function + * + * Deallocates any resources allocated for |inflater|. + */ +NGHTTP2_EXTERN void nghttp2_hd_inflate_del(nghttp2_hd_inflater *inflater); + +/** + * @function + * + * Changes header table size in the |inflater|. This may trigger + * eviction in the dynamic table. + * + * The |settings_hd_table_bufsize_max| should be the value transmitted + * in SETTINGS_HEADER_TABLE_SIZE. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`NGHTTP2_ERR_NOMEM` + * Out of memory. + */ +NGHTTP2_EXTERN int +nghttp2_hd_inflate_change_table_size(nghttp2_hd_inflater *inflater, + size_t settings_hd_table_bufsize_max); + +/** + * @enum + * + * The flags for header inflation. + */ +typedef enum { + /** + * No flag set. + */ + NGHTTP2_HD_INFLATE_NONE = 0, + /** + * Indicates all headers were inflated. + */ + NGHTTP2_HD_INFLATE_FINAL = 0x01, + /** + * Indicates a header was emitted. + */ + NGHTTP2_HD_INFLATE_EMIT = 0x02 +} nghttp2_hd_inflate_flag; + +/** + * @function + * + * Inflates name/value block stored in |in| with length |inlen|. This + * function performs decompression. For each successful emission of + * header name/value pair, :enum:`NGHTTP2_HD_INFLATE_EMIT` is set in + * |*inflate_flags| and name/value pair is assigned to the |nv_out| + * and the function returns. The caller must not free the members of + * |nv_out|. + * + * The |nv_out| may include pointers to the memory region in the |in|. + * The caller must retain the |in| while the |nv_out| is used. + * + * The application should call this function repeatedly until the + * ``(*inflate_flags) & NGHTTP2_HD_INFLATE_FINAL`` is nonzero and + * return value is non-negative. This means the all input values are + * processed successfully. Then the application must call + * `nghttp2_hd_inflate_end_headers()` to prepare for the next header + * block input. + * + * The caller can feed complete compressed header block. It also can + * feed it in several chunks. The caller must set |in_final| to + * nonzero if the given input is the last block of the compressed + * header. + * + * This function returns the number of bytes processed if it succeeds, + * or one of the following negative error codes: + * + * :enum:`NGHTTP2_ERR_NOMEM` + * Out of memory. + * :enum:`NGHTTP2_ERR_HEADER_COMP` + * Inflation process has failed. + * :enum:`NGHTTP2_ERR_BUFFER_ERROR` + * The heder field name or value is too large. + * + * Example follows:: + * + * int inflate_header_block(nghttp2_hd_inflater *hd_inflater, + * uint8_t *in, size_t inlen, int final) + * { + * ssize_t rv; + * + * for(;;) { + * nghttp2_nv nv; + * int inflate_flags = 0; + * + * rv = nghttp2_hd_inflate_hd(hd_inflater, &nv, &inflate_flags, + * in, inlen, final); + * + * if(rv < 0) { + * fprintf(stderr, "inflate failed with error code %zd", rv); + * return -1; + * } + * + * in += rv; + * inlen -= rv; + * + * if(inflate_flags & NGHTTP2_HD_INFLATE_EMIT) { + * fwrite(nv.name, nv.namelen, 1, stderr); + * fprintf(stderr, ": "); + * fwrite(nv.value, nv.valuelen, 1, stderr); + * fprintf(stderr, "\n"); + * } + * if(inflate_flags & NGHTTP2_HD_INFLATE_FINAL) { + * nghttp2_hd_inflate_end_headers(hd_inflater); + * break; + * } + * if((inflate_flags & NGHTTP2_HD_INFLATE_EMIT) == 0 && + * inlen == 0) { + * break; + * } + * } + * + * return 0; + * } + * + */ +NGHTTP2_EXTERN ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_inflater *inflater, + nghttp2_nv *nv_out, + int *inflate_flags, uint8_t *in, + size_t inlen, int in_final); + +/** + * @function + * + * Signals the end of decompression for one header block. + * + * This function returns 0 if it succeeds. Currently this function + * always succeeds. + */ +NGHTTP2_EXTERN int +nghttp2_hd_inflate_end_headers(nghttp2_hd_inflater *inflater); + +#ifdef __cplusplus +} +#endif + +#endif /* NGHTTP2_H */ diff --git a/lib/includes/nghttp2/nghttp2ver.h b/lib/includes/nghttp2/nghttp2ver.h new file mode 100644 index 0000000..09be018 --- /dev/null +++ b/lib/includes/nghttp2/nghttp2ver.h @@ -0,0 +1,42 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012, 2013 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGHTTP2VER_H +#define NGHTTP2VER_H + +/** + * @macro + * Version number of the nghttp2 library release + */ +#define NGHTTP2_VERSION "1.0.0" + +/** + * @macro + * Numerical representation of the version number of the nghttp2 library + * 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 NGHTTP2_VERSION_NUM 0x010000 + +#endif /* NGHTTP2VER_H */ diff --git a/lib/includes/nghttp2/nghttp2ver.h.in b/lib/includes/nghttp2/nghttp2ver.h.in new file mode 100644 index 0000000..7717a64 --- /dev/null +++ b/lib/includes/nghttp2/nghttp2ver.h.in @@ -0,0 +1,42 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012, 2013 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGHTTP2VER_H +#define NGHTTP2VER_H + +/** + * @macro + * Version number of the nghttp2 library release + */ +#define NGHTTP2_VERSION "@PACKAGE_VERSION@" + +/** + * @macro + * Numerical representation of the version number of the nghttp2 library + * 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 NGHTTP2_VERSION_NUM @PACKAGE_VERSION_NUM@ + +#endif /* NGHTTP2VER_H */ diff --git a/lib/libnghttp2.pc.in b/lib/libnghttp2.pc.in new file mode 100644 index 0000000..da6938f --- /dev/null +++ b/lib/libnghttp2.pc.in @@ -0,0 +1,33 @@ +# nghttp2 - HTTP/2 C Library + +# Copyright (c) 2012, 2013 Tatsuhiro Tsujikawa + +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: + +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. + +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: libnghttp2 +Description: HTTP/2 C library +URL: https://github.com/tatsuhiro-t/nghttp2 +Version: @VERSION@ +Libs: -L${libdir} -lnghttp2 +Cflags: -I${includedir} diff --git a/lib/nghttp2_buf.c b/lib/nghttp2_buf.c new file mode 100644 index 0000000..96454a8 --- /dev/null +++ b/lib/nghttp2_buf.c @@ -0,0 +1,494 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2014 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "nghttp2_buf.h" + +#include + +#include "nghttp2_helper.h" + +void nghttp2_buf_init(nghttp2_buf *buf) { + buf->begin = NULL; + buf->end = NULL; + buf->pos = NULL; + buf->last = NULL; + buf->mark = NULL; +} + +int nghttp2_buf_init2(nghttp2_buf *buf, size_t initial, nghttp2_mem *mem) { + nghttp2_buf_init(buf); + return nghttp2_buf_reserve(buf, initial, mem); +} + +void nghttp2_buf_free(nghttp2_buf *buf, nghttp2_mem *mem) { + if (buf == NULL) { + return; + } + + nghttp2_mem_free(mem, buf->begin); + buf->begin = NULL; +} + +int nghttp2_buf_reserve(nghttp2_buf *buf, size_t new_cap, nghttp2_mem *mem) { + uint8_t *ptr; + size_t cap; + + cap = nghttp2_buf_cap(buf); + + if (cap >= new_cap) { + return 0; + } + + new_cap = nghttp2_max(new_cap, cap * 2); + + ptr = nghttp2_mem_realloc(mem, buf->begin, new_cap); + if (ptr == NULL) { + return NGHTTP2_ERR_NOMEM; + } + + buf->pos = ptr + (buf->pos - buf->begin); + buf->last = ptr + (buf->last - buf->begin); + buf->mark = ptr + (buf->mark - buf->begin); + buf->begin = ptr; + buf->end = ptr + new_cap; + + return 0; +} + +void nghttp2_buf_reset(nghttp2_buf *buf) { + buf->pos = buf->last = buf->mark = buf->begin; +} + +void nghttp2_buf_wrap_init(nghttp2_buf *buf, uint8_t *begin, size_t len) { + buf->begin = buf->pos = buf->last = buf->mark = begin; + buf->end = begin + len; +} + +static int buf_chain_new(nghttp2_buf_chain **chain, size_t chunk_length, + nghttp2_mem *mem) { + int rv; + + *chain = nghttp2_mem_malloc(mem, sizeof(nghttp2_buf_chain)); + if (*chain == NULL) { + return NGHTTP2_ERR_NOMEM; + } + + (*chain)->next = NULL; + + rv = nghttp2_buf_init2(&(*chain)->buf, chunk_length, mem); + if (rv != 0) { + nghttp2_mem_free(mem, *chain); + return NGHTTP2_ERR_NOMEM; + } + + return 0; +} + +static void buf_chain_del(nghttp2_buf_chain *chain, nghttp2_mem *mem) { + nghttp2_buf_free(&chain->buf, mem); + nghttp2_mem_free(mem, chain); +} + +int nghttp2_bufs_init(nghttp2_bufs *bufs, size_t chunk_length, size_t max_chunk, + nghttp2_mem *mem) { + return nghttp2_bufs_init2(bufs, chunk_length, max_chunk, 0, mem); +} + +int nghttp2_bufs_init2(nghttp2_bufs *bufs, size_t chunk_length, + size_t max_chunk, size_t offset, nghttp2_mem *mem) { + return nghttp2_bufs_init3(bufs, chunk_length, max_chunk, max_chunk, offset, + mem); +} + +int nghttp2_bufs_init3(nghttp2_bufs *bufs, size_t chunk_length, + size_t max_chunk, size_t chunk_keep, size_t offset, + nghttp2_mem *mem) { + int rv; + nghttp2_buf_chain *chain; + + if (chunk_keep == 0 || max_chunk < chunk_keep || chunk_length < offset) { + return NGHTTP2_ERR_INVALID_ARGUMENT; + } + + rv = buf_chain_new(&chain, chunk_length, mem); + if (rv != 0) { + return rv; + } + + bufs->mem = mem; + bufs->offset = offset; + + bufs->head = chain; + bufs->cur = bufs->head; + + nghttp2_buf_shift_right(&bufs->cur->buf, offset); + + bufs->chunk_length = chunk_length; + bufs->chunk_used = 1; + bufs->max_chunk = max_chunk; + bufs->chunk_keep = chunk_keep; + + return 0; +} + +int nghttp2_bufs_realloc(nghttp2_bufs *bufs, size_t chunk_length) { + int rv; + nghttp2_buf_chain *chain; + + if (chunk_length < bufs->offset) { + return NGHTTP2_ERR_INVALID_ARGUMENT; + } + + rv = buf_chain_new(&chain, chunk_length, bufs->mem); + if (rv != 0) { + return rv; + } + + nghttp2_bufs_free(bufs); + + bufs->head = chain; + bufs->cur = bufs->head; + + nghttp2_buf_shift_right(&bufs->cur->buf, bufs->offset); + + bufs->chunk_length = chunk_length; + bufs->chunk_used = 1; + + return 0; +} + +void nghttp2_bufs_free(nghttp2_bufs *bufs) { + nghttp2_buf_chain *chain, *next_chain; + + if (bufs == NULL) { + return; + } + + for (chain = bufs->head; chain;) { + next_chain = chain->next; + + buf_chain_del(chain, bufs->mem); + + chain = next_chain; + } + + bufs->head = NULL; +} + +int nghttp2_bufs_wrap_init(nghttp2_bufs *bufs, uint8_t *begin, size_t len, + nghttp2_mem *mem) { + nghttp2_buf_chain *chain; + + chain = nghttp2_mem_malloc(mem, sizeof(nghttp2_buf_chain)); + if (chain == NULL) { + return NGHTTP2_ERR_NOMEM; + } + + chain->next = NULL; + + nghttp2_buf_wrap_init(&chain->buf, begin, len); + + bufs->mem = mem; + bufs->offset = 0; + + bufs->head = chain; + bufs->cur = bufs->head; + + bufs->chunk_length = len; + bufs->chunk_used = 1; + bufs->max_chunk = 1; + bufs->chunk_keep = 1; + + return 0; +} + +void nghttp2_bufs_wrap_free(nghttp2_bufs *bufs) { + if (bufs == NULL) { + return; + } + + nghttp2_mem_free(bufs->mem, bufs->head); + bufs->head = NULL; +} + +void nghttp2_bufs_seek_last_present(nghttp2_bufs *bufs) { + nghttp2_buf_chain *ci; + + for (ci = bufs->cur; ci; ci = ci->next) { + if (nghttp2_buf_len(&ci->buf) == 0) { + return; + } else { + bufs->cur = ci; + } + } +} + +ssize_t nghttp2_bufs_len(nghttp2_bufs *bufs) { + nghttp2_buf_chain *ci; + ssize_t len; + + len = 0; + for (ci = bufs->head; ci; ci = ci->next) { + len += nghttp2_buf_len(&ci->buf); + } + + return len; +} + +static ssize_t bufs_avail(nghttp2_bufs *bufs) { + return (ssize_t)(nghttp2_buf_avail(&bufs->cur->buf) + + (bufs->chunk_length - bufs->offset) * + (bufs->max_chunk - bufs->chunk_used)); +} + +static int bufs_alloc_chain(nghttp2_bufs *bufs) { + int rv; + nghttp2_buf_chain *chain; + + if (bufs->cur->next) { + bufs->cur = bufs->cur->next; + + return 0; + } + + if (bufs->max_chunk == bufs->chunk_used) { + return NGHTTP2_ERR_BUFFER_ERROR; + } + + rv = buf_chain_new(&chain, bufs->chunk_length, bufs->mem); + if (rv != 0) { + return rv; + } + + DEBUGF(fprintf(stderr, + "new buffer %zu bytes allocated for bufs %p, used %zu\n", + bufs->chunk_length, bufs, bufs->chunk_used)); + + ++bufs->chunk_used; + + bufs->cur->next = chain; + bufs->cur = chain; + + nghttp2_buf_shift_right(&bufs->cur->buf, bufs->offset); + + return 0; +} + +int nghttp2_bufs_add(nghttp2_bufs *bufs, const void *data, size_t len) { + int rv; + size_t nwrite; + nghttp2_buf *buf; + const uint8_t *p; + + if (bufs_avail(bufs) < (ssize_t)len) { + return NGHTTP2_ERR_BUFFER_ERROR; + } + + p = data; + + while (len) { + buf = &bufs->cur->buf; + + nwrite = nghttp2_min((size_t)nghttp2_buf_avail(buf), len); + if (nwrite == 0) { + rv = bufs_alloc_chain(bufs); + if (rv != 0) { + return rv; + } + continue; + } + + buf->last = nghttp2_cpymem(buf->last, p, nwrite); + p += nwrite; + len -= nwrite; + } + + return 0; +} + +static int bufs_ensure_addb(nghttp2_bufs *bufs) { + int rv; + nghttp2_buf *buf; + + buf = &bufs->cur->buf; + + if (nghttp2_buf_avail(buf) > 0) { + return 0; + } + + rv = bufs_alloc_chain(bufs); + if (rv != 0) { + return rv; + } + + return 0; +} + +int nghttp2_bufs_addb(nghttp2_bufs *bufs, uint8_t b) { + int rv; + + rv = bufs_ensure_addb(bufs); + if (rv != 0) { + return rv; + } + + *bufs->cur->buf.last++ = b; + + return 0; +} + +int nghttp2_bufs_addb_hold(nghttp2_bufs *bufs, uint8_t b) { + int rv; + + rv = bufs_ensure_addb(bufs); + if (rv != 0) { + return rv; + } + + *bufs->cur->buf.last = b; + + return 0; +} + +int nghttp2_bufs_orb(nghttp2_bufs *bufs, uint8_t b) { + int rv; + + rv = bufs_ensure_addb(bufs); + if (rv != 0) { + return rv; + } + + *bufs->cur->buf.last++ |= b; + + return 0; +} + +int nghttp2_bufs_orb_hold(nghttp2_bufs *bufs, uint8_t b) { + int rv; + + rv = bufs_ensure_addb(bufs); + if (rv != 0) { + return rv; + } + + *bufs->cur->buf.last |= b; + + return 0; +} + +ssize_t nghttp2_bufs_remove(nghttp2_bufs *bufs, uint8_t **out) { + size_t len; + nghttp2_buf_chain *chain; + nghttp2_buf *buf; + uint8_t *res; + nghttp2_buf resbuf; + + len = 0; + + for (chain = bufs->head; chain; chain = chain->next) { + len += nghttp2_buf_len(&chain->buf); + } + + if (len == 0) { + res = NULL; + return 0; + } + + res = nghttp2_mem_malloc(bufs->mem, len); + if (res == NULL) { + return NGHTTP2_ERR_NOMEM; + } + + nghttp2_buf_wrap_init(&resbuf, res, len); + + for (chain = bufs->head; chain; chain = chain->next) { + buf = &chain->buf; + resbuf.last = nghttp2_cpymem(resbuf.last, buf->pos, nghttp2_buf_len(buf)); + } + + *out = res; + + return (ssize_t)len; +} + +size_t nghttp2_bufs_remove_copy(nghttp2_bufs *bufs, uint8_t *out) { + size_t len; + nghttp2_buf_chain *chain; + nghttp2_buf *buf; + nghttp2_buf resbuf; + + len = nghttp2_bufs_len(bufs); + + nghttp2_buf_wrap_init(&resbuf, out, len); + + for (chain = bufs->head; chain; chain = chain->next) { + buf = &chain->buf; + resbuf.last = nghttp2_cpymem(resbuf.last, buf->pos, nghttp2_buf_len(buf)); + } + + return len; +} + +void nghttp2_bufs_reset(nghttp2_bufs *bufs) { + nghttp2_buf_chain *chain, *ci; + size_t k; + + k = bufs->chunk_keep; + + for (ci = bufs->head; ci; ci = ci->next) { + nghttp2_buf_reset(&ci->buf); + nghttp2_buf_shift_right(&ci->buf, bufs->offset); + + if (--k == 0) { + break; + } + } + + if (ci) { + chain = ci->next; + ci->next = NULL; + + for (ci = chain; ci;) { + chain = ci->next; + + buf_chain_del(ci, bufs->mem); + + ci = chain; + } + + bufs->chunk_used = bufs->chunk_keep; + } + + bufs->cur = bufs->head; +} + +int nghttp2_bufs_advance(nghttp2_bufs *bufs) { return bufs_alloc_chain(bufs); } + +int nghttp2_bufs_next_present(nghttp2_bufs *bufs) { + nghttp2_buf_chain *chain; + + chain = bufs->cur->next; + + return chain && nghttp2_buf_len(&chain->buf); +} diff --git a/lib/nghttp2_buf.h b/lib/nghttp2_buf.h new file mode 100644 index 0000000..9e986c6 --- /dev/null +++ b/lib/nghttp2_buf.h @@ -0,0 +1,385 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2014 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGHTTP2_BUF_H +#define NGHTTP2_BUF_H + +#ifdef HAVE_CONFIG_H +#include +#endif /* HAVE_CONFIG_H */ + +#include + +#include "nghttp2_int.h" +#include "nghttp2_mem.h" + +typedef struct { + /* This points to the beginning of the buffer. The effective range + of buffer is [begin, end). */ + uint8_t *begin; + /* This points to the memory one byte beyond the end of the + buffer. */ + uint8_t *end; + /* The position indicator for effective start of the buffer. pos <= + last must be hold. */ + uint8_t *pos; + /* The position indicator for effective one beyond of the end of the + buffer. last <= end must be hold. */ + uint8_t *last; + /* Mark arbitrary position in buffer [begin, end) */ + uint8_t *mark; +} nghttp2_buf; + +#define nghttp2_buf_len(BUF) ((ssize_t)((BUF)->last - (BUF)->pos)) +#define nghttp2_buf_avail(BUF) ((ssize_t)((BUF)->end - (BUF)->last)) +#define nghttp2_buf_mark_avail(BUF) ((ssize_t)((BUF)->mark - (BUF)->last)) +#define nghttp2_buf_cap(BUF) ((ssize_t)((BUF)->end - (BUF)->begin)) + +#define nghttp2_buf_pos_offset(BUF) ((ssize_t)((BUF)->pos - (BUF)->begin)) +#define nghttp2_buf_last_offset(BUF) ((ssize_t)((BUF)->last - (BUF)->begin)) + +#define nghttp2_buf_shift_right(BUF, AMT) \ + do { \ + (BUF)->pos += AMT; \ + (BUF)->last += AMT; \ + } while (0) + +#define nghttp2_buf_shift_left(BUF, AMT) \ + do { \ + (BUF)->pos -= AMT; \ + (BUF)->last -= AMT; \ + } while (0) + +/* + * Initializes the |buf|. No memory is allocated in this function. Use + * nghttp2_buf_reserve() or nghttp2_buf_reserve2() to allocate memory. + */ +void nghttp2_buf_init(nghttp2_buf *buf); + +/* + * Initializes the |buf| and allocates at least |initial| bytes of + * memory. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory + */ +int nghttp2_buf_init2(nghttp2_buf *buf, size_t initial, nghttp2_mem *mem); + +/* + * Frees buffer in |buf|. + */ +void nghttp2_buf_free(nghttp2_buf *buf, nghttp2_mem *mem); + +/* + * Extends buffer so that nghttp2_buf_cap() returns at least + * |new_cap|. If extensions took place, buffer pointers in |buf| will + * change. + * + * This function returns 0 if it succeeds, or one of the followings + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory + */ +int nghttp2_buf_reserve(nghttp2_buf *buf, size_t new_cap, nghttp2_mem *mem); + +/* + * Resets pos, last, mark member of |buf| to buf->begin. + */ +void nghttp2_buf_reset(nghttp2_buf *buf); + +/* + * Initializes |buf| using supplied buffer |begin| of length + * |len|. Semantically, the application should not call *_reserve() or + * nghttp2_free() functions for |buf|. + */ +void nghttp2_buf_wrap_init(nghttp2_buf *buf, uint8_t *begin, size_t len); + +struct nghttp2_buf_chain; + +typedef struct nghttp2_buf_chain nghttp2_buf_chain; + +/* Chains 2 buffers */ +struct nghttp2_buf_chain { + /* Points to the subsequent buffer. NULL if there is no such + buffer. */ + nghttp2_buf_chain *next; + nghttp2_buf buf; +}; + +typedef struct { + /* Points to the first buffer */ + nghttp2_buf_chain *head; + /* Buffer pointer where write occurs. */ + nghttp2_buf_chain *cur; + /* Memory allocator */ + nghttp2_mem *mem; + /* The buffer capacity of each buf */ + size_t chunk_length; + /* The maximum number of nghttp2_buf_chain */ + size_t max_chunk; + /* The number of nghttp2_buf_chain allocated */ + size_t chunk_used; + /* The number of nghttp2_buf_chain to keep on reset */ + size_t chunk_keep; + /* pos offset from begin in each buffers. On initialization and + reset, buf->pos and buf->last are positioned at buf->begin + + offset. */ + size_t offset; +} nghttp2_bufs; + +/* + * This is the same as calling nghttp2_bufs_init2 with the given + * arguments and offset = 0. + */ +int nghttp2_bufs_init(nghttp2_bufs *bufs, size_t chunk_length, size_t max_chunk, + nghttp2_mem *mem); + +/* + * This is the same as calling nghttp2_bufs_init3 with the given + * arguments and chunk_keep = max_chunk. + */ +int nghttp2_bufs_init2(nghttp2_bufs *bufs, size_t chunk_length, + size_t max_chunk, size_t offset, nghttp2_mem *mem); + +/* + * Initializes |bufs|. Each buffer size is given in the + * |chunk_length|. The maximum number of buffers is given in the + * |max_chunk|. On reset, first |chunk_keep| buffers are kept and + * remaining buffers are deleted. Each buffer will have bufs->pos and + * bufs->last shifted to left by |offset| bytes on creation and reset. + * + * This function allocates first buffer. bufs->head and bufs->cur + * will point to the first buffer after this call. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + * NGHTTP2_ERR_INVALID_ARGUMENT + * chunk_keep is 0; or max_chunk < chunk_keep; or offset is too + * long. + */ +int nghttp2_bufs_init3(nghttp2_bufs *bufs, size_t chunk_length, + size_t max_chunk, size_t chunk_keep, size_t offset, + nghttp2_mem *mem); + +/* + * Frees any related resources to the |bufs|. + */ +void nghttp2_bufs_free(nghttp2_bufs *bufs); + +/* + * Initializes |bufs| using supplied buffer |begin| of length |len|. + * The first buffer bufs->head uses buffer |begin|. The buffer size + * is fixed and no allocate extra chunk buffer is allocated. In other + * words, max_chunk = chunk_keep = 1. To free the resource allocated + * for |bufs|, use nghttp2_bufs_wrap_free(). + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + */ +int nghttp2_bufs_wrap_init(nghttp2_bufs *bufs, uint8_t *begin, size_t len, + nghttp2_mem *mem); + +/* + * Frees any related resource to the |bufs|. This function does not + * free supplied buffer provided in nghttp2_bufs_wrap_init(). + */ +void nghttp2_bufs_wrap_free(nghttp2_bufs *bufs); + +/* + * Reallocates internal buffer using |chunk_length|. The max_chunk, + * chunk_keep and offset do not change. After successful allocation + * of new buffer, previous buffers are deallocated without copying + * anything into new buffers. chunk_used is reset to 1. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + * NGHTTP2_ERR_INVALID_ARGUMENT + * chunk_length < offset + */ +int nghttp2_bufs_realloc(nghttp2_bufs *bufs, size_t chunk_length); + +/* + * Appends the |data| of length |len| to the |bufs|. The write starts + * at bufs->cur->buf.last. A new buffers will be allocated to store + * all data. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + * NGHTTP2_ERR_BUFFER_ERROR + * Out of buffer space. + */ +int nghttp2_bufs_add(nghttp2_bufs *bufs, const void *data, size_t len); + +/* + * Appends a single byte |b| to the |bufs|. The write starts at + * bufs->cur->buf.last. A new buffers will be allocated to store all + * data. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + * NGHTTP2_ERR_BUFFER_ERROR + * Out of buffer space. + */ +int nghttp2_bufs_addb(nghttp2_bufs *bufs, uint8_t b); + +/* + * Behaves like nghttp2_bufs_addb(), but this does not update + * buf->last pointer. + */ +int nghttp2_bufs_addb_hold(nghttp2_bufs *bufs, uint8_t b); + +#define nghttp2_bufs_fast_addb(BUFS, B) \ + do { \ + *(BUFS)->cur->buf.last++ = B; \ + } while (0) + +#define nghttp2_bufs_fast_addb_hold(BUFS, B) \ + do { \ + *(BUFS)->cur->buf.last = B; \ + } while (0) + +/* + * Performs bitwise-OR of |b| at bufs->cur->buf.last. A new buffers + * will be allocated if necessary. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + * NGHTTP2_ERR_BUFFER_ERROR + * Out of buffer space. + */ +int nghttp2_bufs_orb(nghttp2_bufs *bufs, uint8_t b); + +/* + * Behaves like nghttp2_bufs_orb(), but does not update buf->last + * pointer. + */ +int nghttp2_bufs_orb_hold(nghttp2_bufs *bufs, uint8_t b); + +#define nghttp2_bufs_fast_orb(BUFS, B) \ + do { \ + *(BUFS)->cur->buf.last++ |= B; \ + } while (0) + +#define nghttp2_bufs_fast_orb_hold(BUFS, B) \ + do { \ + *(BUFS)->cur->buf.last |= B; \ + } while (0) + +/* + * Copies all data stored in |bufs| to the contagious buffer. This + * function allocates the contagious memory to store all data in + * |bufs| and assigns it to |*out|. + * + * The contents of |bufs| is left unchanged. + * + * This function returns the length of copied data and assigns the + * pointer to copied data to |*out| if it succeeds, or one of the + * following negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory + */ +ssize_t nghttp2_bufs_remove(nghttp2_bufs *bufs, uint8_t **out); + +/* + * Copies all data stored in |bufs| to |out|. This function assumes + * that the buffer space pointed by |out| has at least + * nghttp2_bufs(bufs) bytes. + * + * The contents of |bufs| is left unchanged. + * + * This function returns the length of copied data. + */ +size_t nghttp2_bufs_remove_copy(nghttp2_bufs *bufs, uint8_t *out); + +/* + * Resets |bufs| and makes the buffers empty. + */ +void nghttp2_bufs_reset(nghttp2_bufs *bufs); + +/* + * Moves bufs->cur to bufs->cur->next. If resulting bufs->cur is + * NULL, this function allocates new buffers and bufs->cur points to + * it. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory + * NGHTTP2_ERR_BUFFER_ERROR + * Out of buffer space. + */ +int nghttp2_bufs_advance(nghttp2_bufs *bufs); + +/* Sets bufs->cur to bufs->head */ +#define nghttp2_bufs_rewind(BUFS) \ + do { \ + (BUFS)->cur = (BUFS)->head; \ + } while (0) + +/* + * Move bufs->cur, from the current position, using next member, to + * the last buf which has nghttp2_buf_len(buf) > 0 without seeing buf + * which satisfies nghttp2_buf_len(buf) == 0. If + * nghttp2_buf_len(&bufs->cur->buf) == 0 or bufs->cur->next is NULL, + * bufs->cur is unchanged. + */ +void nghttp2_bufs_seek_last_present(nghttp2_bufs *bufs); + +/* + * Returns nonzero if bufs->cur->next is not emtpy. + */ +int nghttp2_bufs_next_present(nghttp2_bufs *bufs); + +#define nghttp2_bufs_cur_avail(BUFS) nghttp2_buf_avail(&(BUFS)->cur->buf) + +/* + * Returns the buffer length of |bufs|. + */ +ssize_t nghttp2_bufs_len(nghttp2_bufs *bufs); + +#endif /* NGHTTP2_BUF_H */ diff --git a/lib/nghttp2_callbacks.c b/lib/nghttp2_callbacks.c new file mode 100644 index 0000000..3c4be9a --- /dev/null +++ b/lib/nghttp2_callbacks.c @@ -0,0 +1,129 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2014 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "nghttp2_callbacks.h" + +#include + +int nghttp2_session_callbacks_new(nghttp2_session_callbacks **callbacks_ptr) { + *callbacks_ptr = calloc(1, sizeof(nghttp2_session_callbacks)); + + if (*callbacks_ptr == NULL) { + return NGHTTP2_ERR_NOMEM; + } + + return 0; +} + +void nghttp2_session_callbacks_del(nghttp2_session_callbacks *callbacks) { + free(callbacks); +} + +void nghttp2_session_callbacks_set_send_callback( + nghttp2_session_callbacks *cbs, nghttp2_send_callback send_callback) { + cbs->send_callback = send_callback; +} + +void nghttp2_session_callbacks_set_recv_callback( + nghttp2_session_callbacks *cbs, nghttp2_recv_callback recv_callback) { + cbs->recv_callback = recv_callback; +} + +void nghttp2_session_callbacks_set_on_frame_recv_callback( + nghttp2_session_callbacks *cbs, + nghttp2_on_frame_recv_callback on_frame_recv_callback) { + cbs->on_frame_recv_callback = on_frame_recv_callback; +} + +void nghttp2_session_callbacks_set_on_invalid_frame_recv_callback( + nghttp2_session_callbacks *cbs, + nghttp2_on_invalid_frame_recv_callback on_invalid_frame_recv_callback) { + cbs->on_invalid_frame_recv_callback = on_invalid_frame_recv_callback; +} + +void nghttp2_session_callbacks_set_on_data_chunk_recv_callback( + nghttp2_session_callbacks *cbs, + nghttp2_on_data_chunk_recv_callback on_data_chunk_recv_callback) { + cbs->on_data_chunk_recv_callback = on_data_chunk_recv_callback; +} + +void nghttp2_session_callbacks_set_before_frame_send_callback( + nghttp2_session_callbacks *cbs, + nghttp2_before_frame_send_callback before_frame_send_callback) { + cbs->before_frame_send_callback = before_frame_send_callback; +} + +void nghttp2_session_callbacks_set_on_frame_send_callback( + nghttp2_session_callbacks *cbs, + nghttp2_on_frame_send_callback on_frame_send_callback) { + cbs->on_frame_send_callback = on_frame_send_callback; +} + +void nghttp2_session_callbacks_set_on_frame_not_send_callback( + nghttp2_session_callbacks *cbs, + nghttp2_on_frame_not_send_callback on_frame_not_send_callback) { + cbs->on_frame_not_send_callback = on_frame_not_send_callback; +} + +void nghttp2_session_callbacks_set_on_stream_close_callback( + nghttp2_session_callbacks *cbs, + nghttp2_on_stream_close_callback on_stream_close_callback) { + cbs->on_stream_close_callback = on_stream_close_callback; +} + +void nghttp2_session_callbacks_set_on_begin_headers_callback( + nghttp2_session_callbacks *cbs, + nghttp2_on_begin_headers_callback on_begin_headers_callback) { + cbs->on_begin_headers_callback = on_begin_headers_callback; +} + +void nghttp2_session_callbacks_set_on_header_callback( + nghttp2_session_callbacks *cbs, + nghttp2_on_header_callback on_header_callback) { + cbs->on_header_callback = on_header_callback; +} + +void nghttp2_session_callbacks_set_select_padding_callback( + nghttp2_session_callbacks *cbs, + nghttp2_select_padding_callback select_padding_callback) { + cbs->select_padding_callback = select_padding_callback; +} + +void nghttp2_session_callbacks_set_data_source_read_length_callback( + nghttp2_session_callbacks *cbs, + nghttp2_data_source_read_length_callback data_source_read_length_callback) { + cbs->read_length_callback = data_source_read_length_callback; +} + +void nghttp2_session_callbacks_set_on_begin_frame_callback( + nghttp2_session_callbacks *cbs, + nghttp2_on_begin_frame_callback on_begin_frame_callback) { + cbs->on_begin_frame_callback = on_begin_frame_callback; +} + +void nghttp2_session_callbacks_set_send_data_callback( + nghttp2_session_callbacks *cbs, + nghttp2_send_data_callback send_data_callback) { + cbs->send_data_callback = send_data_callback; +} diff --git a/lib/nghttp2_callbacks.h b/lib/nghttp2_callbacks.h new file mode 100644 index 0000000..37958ea --- /dev/null +++ b/lib/nghttp2_callbacks.h @@ -0,0 +1,112 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2014 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGHTTP2_CALLBACKS_H +#define NGHTTP2_CALLBACKS_H + +#ifdef HAVE_CONFIG_H +#include +#endif /* HAVE_CONFIG_H */ + +#include + +/* + * Callback functions. + */ +struct nghttp2_session_callbacks { + /** + * Callback function invoked when the session wants to send data to + * the remote peer. This callback is not necessary if the + * application uses solely `nghttp2_session_mem_send()` to serialize + * data to transmit. + */ + nghttp2_send_callback send_callback; + /** + * Callback function invoked when the session wants to receive data + * from the remote peer. This callback is not necessary if the + * application uses solely `nghttp2_session_mem_recv()` to process + * received data. + */ + nghttp2_recv_callback recv_callback; + /** + * Callback function invoked by `nghttp2_session_recv()` when a + * frame is received. + */ + nghttp2_on_frame_recv_callback on_frame_recv_callback; + /** + * Callback function invoked by `nghttp2_session_recv()` when an + * invalid non-DATA frame is received. + */ + nghttp2_on_invalid_frame_recv_callback on_invalid_frame_recv_callback; + /** + * Callback function invoked when a chunk of data in DATA frame is + * received. + */ + nghttp2_on_data_chunk_recv_callback on_data_chunk_recv_callback; + /** + * Callback function invoked before a non-DATA frame is sent. + */ + nghttp2_before_frame_send_callback before_frame_send_callback; + /** + * Callback function invoked after a frame is sent. + */ + nghttp2_on_frame_send_callback on_frame_send_callback; + /** + * The callback function invoked when a non-DATA frame is not sent + * because of an error. + */ + nghttp2_on_frame_not_send_callback on_frame_not_send_callback; + /** + * Callback function invoked when the stream is closed. + */ + nghttp2_on_stream_close_callback on_stream_close_callback; + /** + * Callback function invoked when the reception of header block in + * HEADERS or PUSH_PROMISE is started. + */ + nghttp2_on_begin_headers_callback on_begin_headers_callback; + /** + * Callback function invoked when a header name/value pair is + * received. + */ + nghttp2_on_header_callback on_header_callback; + /** + * Callback function invoked when the library asks application how + * many padding bytes are required for the transmission of the given + * frame. + */ + nghttp2_select_padding_callback select_padding_callback; + /** + * The callback function used to determine the length allowed in + * `nghttp2_data_source_read_callback()` + */ + nghttp2_data_source_read_length_callback read_length_callback; + /** + * Sets callback function invoked when a frame header is received. + */ + nghttp2_on_begin_frame_callback on_begin_frame_callback; + nghttp2_send_data_callback send_data_callback; +}; + +#endif /* NGHTTP2_CALLBACKS_H */ diff --git a/lib/nghttp2_frame.c b/lib/nghttp2_frame.c new file mode 100644 index 0000000..07f93a1 --- /dev/null +++ b/lib/nghttp2_frame.c @@ -0,0 +1,888 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2013 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "nghttp2_frame.h" + +#include +#include +#include +#include + +#include "nghttp2_helper.h" +#include "nghttp2_net.h" +#include "nghttp2_priority_spec.h" + +void nghttp2_frame_pack_frame_hd(uint8_t *buf, const nghttp2_frame_hd *hd) { + nghttp2_put_uint32be(&buf[0], (uint32_t)(hd->length << 8)); + buf[3] = hd->type; + buf[4] = hd->flags; + nghttp2_put_uint32be(&buf[5], hd->stream_id); + /* ignore hd->reserved for now */ +} + +void nghttp2_frame_unpack_frame_hd(nghttp2_frame_hd *hd, const uint8_t *buf) { + hd->length = nghttp2_get_uint32(&buf[0]) >> 8; + hd->type = buf[3]; + hd->flags = buf[4]; + hd->stream_id = nghttp2_get_uint32(&buf[5]) & NGHTTP2_STREAM_ID_MASK; + hd->reserved = 0; +} + +void nghttp2_frame_hd_init(nghttp2_frame_hd *hd, size_t length, uint8_t type, + uint8_t flags, int32_t stream_id) { + hd->length = length; + hd->type = type; + hd->flags = flags; + hd->stream_id = stream_id; + hd->reserved = 0; +} + +void nghttp2_frame_headers_init(nghttp2_headers *frame, uint8_t flags, + int32_t stream_id, nghttp2_headers_category cat, + const nghttp2_priority_spec *pri_spec, + nghttp2_nv *nva, size_t nvlen) { + nghttp2_frame_hd_init(&frame->hd, 0, NGHTTP2_HEADERS, flags, stream_id); + frame->padlen = 0; + frame->nva = nva; + frame->nvlen = nvlen; + frame->cat = cat; + + if (pri_spec) { + frame->pri_spec = *pri_spec; + } else { + nghttp2_priority_spec_default_init(&frame->pri_spec); + } +} + +void nghttp2_frame_headers_free(nghttp2_headers *frame, nghttp2_mem *mem) { + nghttp2_nv_array_del(frame->nva, mem); +} + +void nghttp2_frame_priority_init(nghttp2_priority *frame, int32_t stream_id, + const nghttp2_priority_spec *pri_spec) { + nghttp2_frame_hd_init(&frame->hd, NGHTTP2_PRIORITY_SPECLEN, NGHTTP2_PRIORITY, + NGHTTP2_FLAG_NONE, stream_id); + frame->pri_spec = *pri_spec; +} + +void nghttp2_frame_priority_free(nghttp2_priority *frame _U_) {} + +void nghttp2_frame_rst_stream_init(nghttp2_rst_stream *frame, int32_t stream_id, + uint32_t error_code) { + nghttp2_frame_hd_init(&frame->hd, 4, NGHTTP2_RST_STREAM, NGHTTP2_FLAG_NONE, + stream_id); + frame->error_code = error_code; +} + +void nghttp2_frame_rst_stream_free(nghttp2_rst_stream *frame _U_) {} + +void nghttp2_frame_settings_init(nghttp2_settings *frame, uint8_t flags, + nghttp2_settings_entry *iv, size_t niv) { + nghttp2_frame_hd_init(&frame->hd, niv * NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH, + NGHTTP2_SETTINGS, flags, 0); + frame->niv = niv; + frame->iv = iv; +} + +void nghttp2_frame_settings_free(nghttp2_settings *frame, nghttp2_mem *mem) { + nghttp2_mem_free(mem, frame->iv); +} + +void nghttp2_frame_push_promise_init(nghttp2_push_promise *frame, uint8_t flags, + int32_t stream_id, + int32_t promised_stream_id, + nghttp2_nv *nva, size_t nvlen) { + nghttp2_frame_hd_init(&frame->hd, 0, NGHTTP2_PUSH_PROMISE, flags, stream_id); + frame->padlen = 0; + frame->nva = nva; + frame->nvlen = nvlen; + frame->promised_stream_id = promised_stream_id; + frame->reserved = 0; +} + +void nghttp2_frame_push_promise_free(nghttp2_push_promise *frame, + nghttp2_mem *mem) { + nghttp2_nv_array_del(frame->nva, mem); +} + +void nghttp2_frame_ping_init(nghttp2_ping *frame, uint8_t flags, + const uint8_t *opaque_data) { + nghttp2_frame_hd_init(&frame->hd, 8, NGHTTP2_PING, flags, 0); + if (opaque_data) { + memcpy(frame->opaque_data, opaque_data, sizeof(frame->opaque_data)); + } else { + memset(frame->opaque_data, 0, sizeof(frame->opaque_data)); + } +} + +void nghttp2_frame_ping_free(nghttp2_ping *frame _U_) {} + +void nghttp2_frame_goaway_init(nghttp2_goaway *frame, int32_t last_stream_id, + uint32_t error_code, uint8_t *opaque_data, + size_t opaque_data_len) { + nghttp2_frame_hd_init(&frame->hd, 8 + opaque_data_len, NGHTTP2_GOAWAY, + NGHTTP2_FLAG_NONE, 0); + frame->last_stream_id = last_stream_id; + frame->error_code = error_code; + frame->opaque_data = opaque_data; + frame->opaque_data_len = opaque_data_len; + frame->reserved = 0; +} + +void nghttp2_frame_goaway_free(nghttp2_goaway *frame, nghttp2_mem *mem) { + nghttp2_mem_free(mem, frame->opaque_data); +} + +void nghttp2_frame_window_update_init(nghttp2_window_update *frame, + uint8_t flags, int32_t stream_id, + int32_t window_size_increment) { + nghttp2_frame_hd_init(&frame->hd, 4, NGHTTP2_WINDOW_UPDATE, flags, stream_id); + frame->window_size_increment = window_size_increment; + frame->reserved = 0; +} + +void nghttp2_frame_window_update_free(nghttp2_window_update *frame _U_) {} + +size_t nghttp2_frame_trail_padlen(nghttp2_frame *frame, size_t padlen) { + return padlen - ((frame->hd.flags & NGHTTP2_FLAG_PADDED) > 0); +} + +void nghttp2_frame_data_init(nghttp2_data *frame, uint8_t flags, + int32_t stream_id) { + /* At this moment, the length of DATA frame is unknown */ + nghttp2_frame_hd_init(&frame->hd, 0, NGHTTP2_DATA, flags, stream_id); + frame->padlen = 0; +} + +void nghttp2_frame_data_free(nghttp2_data *frame _U_) {} + +size_t nghttp2_frame_priority_len(uint8_t flags) { + if (flags & NGHTTP2_FLAG_PRIORITY) { + return NGHTTP2_PRIORITY_SPECLEN; + } + + return 0; +} + +size_t nghttp2_frame_headers_payload_nv_offset(nghttp2_headers *frame) { + return nghttp2_frame_priority_len(frame->hd.flags); +} + +/* + * Call this function after payload was serialized, but not before + * changing buf->pos and serializing frame header. + * + * This function assumes bufs->cur points to the last buf chain of the + * frame(s). + * + * This function serializes frame header for HEADERS/PUSH_PROMISE and + * handles their successive CONTINUATION frames. + * + * We don't process any padding here. + */ +static int frame_pack_headers_shared(nghttp2_bufs *bufs, + nghttp2_frame_hd *frame_hd) { + nghttp2_buf *buf; + nghttp2_buf_chain *ci, *ce; + nghttp2_frame_hd hd; + + buf = &bufs->head->buf; + + hd = *frame_hd; + hd.length = nghttp2_buf_len(buf); + + DEBUGF(fprintf(stderr, "send: HEADERS/PUSH_PROMISE, payloadlen=%zu\n", + hd.length)); + + /* We have multiple frame buffers, which means one or more + CONTINUATION frame is involved. Remove END_HEADERS flag from the + first frame. */ + if (bufs->head != bufs->cur) { + hd.flags &= ~NGHTTP2_FLAG_END_HEADERS; + } + + buf->pos -= NGHTTP2_FRAME_HDLEN; + nghttp2_frame_pack_frame_hd(buf->pos, &hd); + + if (bufs->head != bufs->cur) { + /* 2nd and later frames are CONTINUATION frames. */ + hd.type = NGHTTP2_CONTINUATION; + /* We don't have no flags except for last CONTINUATION */ + hd.flags = NGHTTP2_FLAG_NONE; + + ce = bufs->cur; + + for (ci = bufs->head->next; ci != ce; ci = ci->next) { + buf = &ci->buf; + + hd.length = nghttp2_buf_len(buf); + + DEBUGF(fprintf(stderr, "send: int CONTINUATION, payloadlen=%zu\n", + hd.length)); + + buf->pos -= NGHTTP2_FRAME_HDLEN; + nghttp2_frame_pack_frame_hd(buf->pos, &hd); + } + + buf = &ci->buf; + hd.length = nghttp2_buf_len(buf); + /* Set END_HEADERS flag for last CONTINUATION */ + hd.flags = NGHTTP2_FLAG_END_HEADERS; + + DEBUGF(fprintf(stderr, "send: last CONTINUATION, payloadlen=%zu\n", + hd.length)); + + buf->pos -= NGHTTP2_FRAME_HDLEN; + nghttp2_frame_pack_frame_hd(buf->pos, &hd); + } + + return 0; +} + +int nghttp2_frame_pack_headers(nghttp2_bufs *bufs, nghttp2_headers *frame, + nghttp2_hd_deflater *deflater) { + size_t nv_offset; + int rv; + nghttp2_buf *buf; + + assert(bufs->head == bufs->cur); + + nv_offset = nghttp2_frame_headers_payload_nv_offset(frame); + + buf = &bufs->cur->buf; + + buf->pos += nv_offset; + buf->last = buf->pos; + + /* This call will adjust buf->last to the correct position */ + rv = nghttp2_hd_deflate_hd_bufs(deflater, bufs, frame->nva, frame->nvlen); + + if (rv == NGHTTP2_ERR_BUFFER_ERROR) { + rv = NGHTTP2_ERR_HEADER_COMP; + } + + buf->pos -= nv_offset; + + if (rv != 0) { + return rv; + } + + if (frame->hd.flags & NGHTTP2_FLAG_PRIORITY) { + nghttp2_frame_pack_priority_spec(buf->pos, &frame->pri_spec); + } + + frame->padlen = 0; + frame->hd.length = nghttp2_bufs_len(bufs); + + return frame_pack_headers_shared(bufs, &frame->hd); +} + +void nghttp2_frame_pack_priority_spec(uint8_t *buf, + const nghttp2_priority_spec *pri_spec) { + nghttp2_put_uint32be(buf, pri_spec->stream_id); + if (pri_spec->exclusive) { + buf[0] |= 0x80; + } + buf[4] = pri_spec->weight - 1; +} + +void nghttp2_frame_unpack_priority_spec(nghttp2_priority_spec *pri_spec, + uint8_t flags _U_, + const uint8_t *payload, + size_t payloadlen _U_) { + int32_t dep_stream_id; + uint8_t exclusive; + int32_t weight; + + dep_stream_id = nghttp2_get_uint32(payload) & NGHTTP2_STREAM_ID_MASK; + exclusive = (payload[0] & 0x80) > 0; + weight = payload[4] + 1; + + nghttp2_priority_spec_init(pri_spec, dep_stream_id, weight, exclusive); +} + +int nghttp2_frame_unpack_headers_payload(nghttp2_headers *frame, + const uint8_t *payload, + size_t payloadlen) { + if (frame->hd.flags & NGHTTP2_FLAG_PRIORITY) { + nghttp2_frame_unpack_priority_spec(&frame->pri_spec, frame->hd.flags, + payload, payloadlen); + } else { + nghttp2_priority_spec_default_init(&frame->pri_spec); + } + + frame->nva = NULL; + frame->nvlen = 0; + + return 0; +} + +int nghttp2_frame_pack_priority(nghttp2_bufs *bufs, nghttp2_priority *frame) { + nghttp2_buf *buf; + + assert(bufs->head == bufs->cur); + + buf = &bufs->head->buf; + + assert(nghttp2_buf_avail(buf) >= NGHTTP2_PRIORITY_SPECLEN); + + buf->pos -= NGHTTP2_FRAME_HDLEN; + + nghttp2_frame_pack_frame_hd(buf->pos, &frame->hd); + + nghttp2_frame_pack_priority_spec(buf->last, &frame->pri_spec); + + buf->last += NGHTTP2_PRIORITY_SPECLEN; + + return 0; +} + +void nghttp2_frame_unpack_priority_payload(nghttp2_priority *frame, + const uint8_t *payload, + size_t payloadlen) { + nghttp2_frame_unpack_priority_spec(&frame->pri_spec, frame->hd.flags, payload, + payloadlen); +} + +int nghttp2_frame_pack_rst_stream(nghttp2_bufs *bufs, + nghttp2_rst_stream *frame) { + nghttp2_buf *buf; + + assert(bufs->head == bufs->cur); + + buf = &bufs->head->buf; + + assert(nghttp2_buf_avail(buf) >= 4); + + buf->pos -= NGHTTP2_FRAME_HDLEN; + + nghttp2_frame_pack_frame_hd(buf->pos, &frame->hd); + + nghttp2_put_uint32be(buf->last, frame->error_code); + buf->last += 4; + + return 0; +} + +void nghttp2_frame_unpack_rst_stream_payload(nghttp2_rst_stream *frame, + const uint8_t *payload, + size_t payloadlen _U_) { + frame->error_code = nghttp2_get_uint32(payload); +} + +int nghttp2_frame_pack_settings(nghttp2_bufs *bufs, nghttp2_settings *frame) { + nghttp2_buf *buf; + + assert(bufs->head == bufs->cur); + + buf = &bufs->head->buf; + + if (nghttp2_buf_avail(buf) < (ssize_t)frame->hd.length) { + return NGHTTP2_ERR_FRAME_SIZE_ERROR; + } + + buf->pos -= NGHTTP2_FRAME_HDLEN; + + nghttp2_frame_pack_frame_hd(buf->pos, &frame->hd); + + buf->last += + nghttp2_frame_pack_settings_payload(buf->last, frame->iv, frame->niv); + + return 0; +} + +size_t nghttp2_frame_pack_settings_payload(uint8_t *buf, + const nghttp2_settings_entry *iv, + size_t niv) { + size_t i; + for (i = 0; i < niv; ++i, buf += NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH) { + nghttp2_put_uint16be(buf, iv[i].settings_id); + nghttp2_put_uint32be(buf + 2, iv[i].value); + } + return NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH * niv; +} + +int nghttp2_frame_unpack_settings_payload(nghttp2_settings *frame, + nghttp2_settings_entry *iv, + size_t niv, nghttp2_mem *mem) { + size_t payloadlen = niv * sizeof(nghttp2_settings_entry); + + if (niv == 0) { + frame->iv = NULL; + } else { + frame->iv = nghttp2_mem_malloc(mem, payloadlen); + + if (frame->iv == NULL) { + return NGHTTP2_ERR_NOMEM; + } + + memcpy(frame->iv, iv, payloadlen); + } + + frame->niv = niv; + return 0; +} + +void nghttp2_frame_unpack_settings_entry(nghttp2_settings_entry *iv, + const uint8_t *payload) { + iv->settings_id = nghttp2_get_uint16(&payload[0]); + iv->value = nghttp2_get_uint32(&payload[2]); +} + +int nghttp2_frame_unpack_settings_payload2(nghttp2_settings_entry **iv_ptr, + size_t *niv_ptr, + const uint8_t *payload, + size_t payloadlen, + nghttp2_mem *mem) { + size_t i; + + *niv_ptr = payloadlen / NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH; + + if (*niv_ptr == 0) { + *iv_ptr = NULL; + + return 0; + } + + *iv_ptr = + nghttp2_mem_malloc(mem, (*niv_ptr) * sizeof(nghttp2_settings_entry)); + + if (*iv_ptr == NULL) { + return NGHTTP2_ERR_NOMEM; + } + + for (i = 0; i < *niv_ptr; ++i) { + size_t off = i * NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH; + nghttp2_frame_unpack_settings_entry(&(*iv_ptr)[i], &payload[off]); + } + + return 0; +} + +int nghttp2_frame_pack_push_promise(nghttp2_bufs *bufs, + nghttp2_push_promise *frame, + nghttp2_hd_deflater *deflater) { + size_t nv_offset = 4; + int rv; + nghttp2_buf *buf; + + assert(bufs->head == bufs->cur); + + buf = &bufs->cur->buf; + + buf->pos += nv_offset; + buf->last = buf->pos; + + /* This call will adjust buf->last to the correct position */ + rv = nghttp2_hd_deflate_hd_bufs(deflater, bufs, frame->nva, frame->nvlen); + + if (rv == NGHTTP2_ERR_BUFFER_ERROR) { + rv = NGHTTP2_ERR_HEADER_COMP; + } + + buf->pos -= nv_offset; + + if (rv != 0) { + return rv; + } + + nghttp2_put_uint32be(buf->pos, frame->promised_stream_id); + + frame->padlen = 0; + frame->hd.length = nghttp2_bufs_len(bufs); + + return frame_pack_headers_shared(bufs, &frame->hd); +} + +int nghttp2_frame_unpack_push_promise_payload(nghttp2_push_promise *frame, + const uint8_t *payload, + size_t payloadlen _U_) { + frame->promised_stream_id = + nghttp2_get_uint32(payload) & NGHTTP2_STREAM_ID_MASK; + frame->nva = NULL; + frame->nvlen = 0; + return 0; +} + +int nghttp2_frame_pack_ping(nghttp2_bufs *bufs, nghttp2_ping *frame) { + nghttp2_buf *buf; + + assert(bufs->head == bufs->cur); + + buf = &bufs->head->buf; + + assert(nghttp2_buf_avail(buf) >= 8); + + buf->pos -= NGHTTP2_FRAME_HDLEN; + + nghttp2_frame_pack_frame_hd(buf->pos, &frame->hd); + + buf->last = + nghttp2_cpymem(buf->last, frame->opaque_data, sizeof(frame->opaque_data)); + + return 0; +} + +void nghttp2_frame_unpack_ping_payload(nghttp2_ping *frame, + const uint8_t *payload, + size_t payloadlen _U_) { + memcpy(frame->opaque_data, payload, sizeof(frame->opaque_data)); +} + +int nghttp2_frame_pack_goaway(nghttp2_bufs *bufs, nghttp2_goaway *frame) { + int rv; + nghttp2_buf *buf; + + assert(bufs->head == bufs->cur); + + buf = &bufs->head->buf; + + buf->pos -= NGHTTP2_FRAME_HDLEN; + + nghttp2_frame_pack_frame_hd(buf->pos, &frame->hd); + + nghttp2_put_uint32be(buf->last, frame->last_stream_id); + buf->last += 4; + + nghttp2_put_uint32be(buf->last, frame->error_code); + buf->last += 4; + + rv = nghttp2_bufs_add(bufs, frame->opaque_data, frame->opaque_data_len); + + if (rv == NGHTTP2_ERR_BUFFER_ERROR) { + return NGHTTP2_ERR_FRAME_SIZE_ERROR; + } + + if (rv != 0) { + return rv; + } + + return 0; +} + +void nghttp2_frame_unpack_goaway_payload(nghttp2_goaway *frame, + const uint8_t *payload, + size_t payloadlen _U_, + uint8_t *var_gift_payload, + size_t var_gift_payloadlen) { + frame->last_stream_id = nghttp2_get_uint32(payload) & NGHTTP2_STREAM_ID_MASK; + frame->error_code = nghttp2_get_uint32(payload + 4); + + frame->opaque_data = var_gift_payload; + frame->opaque_data_len = var_gift_payloadlen; +} + +int nghttp2_frame_unpack_goaway_payload2(nghttp2_goaway *frame, + const uint8_t *payload, + size_t payloadlen, nghttp2_mem *mem) { + uint8_t *var_gift_payload; + size_t var_gift_payloadlen; + + if (payloadlen > 8) { + var_gift_payloadlen = payloadlen - 8; + } else { + var_gift_payloadlen = 0; + } + + payloadlen -= var_gift_payloadlen; + + if (!var_gift_payloadlen) { + var_gift_payload = NULL; + } else { + var_gift_payload = nghttp2_mem_malloc(mem, var_gift_payloadlen); + + if (var_gift_payload == NULL) { + return NGHTTP2_ERR_NOMEM; + } + + memcpy(var_gift_payload, payload + 8, var_gift_payloadlen); + } + + nghttp2_frame_unpack_goaway_payload(frame, payload, payloadlen, + var_gift_payload, var_gift_payloadlen); + + return 0; +} + +int nghttp2_frame_pack_window_update(nghttp2_bufs *bufs, + nghttp2_window_update *frame) { + nghttp2_buf *buf; + + assert(bufs->head == bufs->cur); + + buf = &bufs->head->buf; + + assert(nghttp2_buf_avail(buf) >= 4); + + buf->pos -= NGHTTP2_FRAME_HDLEN; + + nghttp2_frame_pack_frame_hd(buf->pos, &frame->hd); + + nghttp2_put_uint32be(buf->last, frame->window_size_increment); + buf->last += 4; + + return 0; +} + +void nghttp2_frame_unpack_window_update_payload(nghttp2_window_update *frame, + const uint8_t *payload, + size_t payloadlen _U_) { + frame->window_size_increment = + nghttp2_get_uint32(payload) & NGHTTP2_WINDOW_SIZE_INCREMENT_MASK; +} + +nghttp2_settings_entry *nghttp2_frame_iv_copy(const nghttp2_settings_entry *iv, + size_t niv, nghttp2_mem *mem) { + nghttp2_settings_entry *iv_copy; + size_t len = niv * sizeof(nghttp2_settings_entry); + + if (len == 0) { + return NULL; + } + + iv_copy = nghttp2_mem_malloc(mem, len); + + if (iv_copy == NULL) { + return NULL; + } + + memcpy(iv_copy, iv, len); + + return iv_copy; +} + +int nghttp2_nv_equal(const nghttp2_nv *a, const nghttp2_nv *b) { + return a->namelen == b->namelen && a->valuelen == b->valuelen && + memcmp(a->name, b->name, a->namelen) == 0 && + memcmp(a->value, b->value, a->valuelen) == 0; +} + +void nghttp2_nv_array_del(nghttp2_nv *nva, nghttp2_mem *mem) { + nghttp2_mem_free(mem, nva); +} + +static int bytes_compar(const uint8_t *a, size_t alen, const uint8_t *b, + size_t blen) { + int rv; + + if (alen == blen) { + return memcmp(a, b, alen); + } + + if (alen < blen) { + rv = memcmp(a, b, alen); + + if (rv == 0) { + return -1; + } + + return rv; + } + + rv = memcmp(a, b, blen); + + if (rv == 0) { + return 1; + } + + return rv; +} + +int nghttp2_nv_compare_name(const nghttp2_nv *lhs, const nghttp2_nv *rhs) { + return bytes_compar(lhs->name, lhs->namelen, rhs->name, rhs->namelen); +} + +static int nv_compar(const void *lhs, const void *rhs) { + const nghttp2_nv *a = (const nghttp2_nv *)lhs; + const nghttp2_nv *b = (const nghttp2_nv *)rhs; + int rv; + + rv = bytes_compar(a->name, a->namelen, b->name, b->namelen); + + if (rv == 0) { + return bytes_compar(a->value, a->valuelen, b->value, b->valuelen); + } + + return rv; +} + +void nghttp2_nv_array_sort(nghttp2_nv *nva, size_t nvlen) { + qsort(nva, nvlen, sizeof(nghttp2_nv), nv_compar); +} + +int nghttp2_nv_array_copy(nghttp2_nv **nva_ptr, const nghttp2_nv *nva, + size_t nvlen, nghttp2_mem *mem) { + size_t i; + uint8_t *data; + size_t buflen = 0; + nghttp2_nv *p; + + for (i = 0; i < nvlen; ++i) { + /* + 2 for null-termination */ + buflen += nva[i].namelen + nva[i].valuelen + 2; + } + + if (nvlen == 0) { + *nva_ptr = NULL; + + return 0; + } + + buflen += sizeof(nghttp2_nv) * nvlen; + + *nva_ptr = nghttp2_mem_malloc(mem, buflen); + + if (*nva_ptr == NULL) { + return NGHTTP2_ERR_NOMEM; + } + + p = *nva_ptr; + data = (uint8_t *)(*nva_ptr) + sizeof(nghttp2_nv) * nvlen; + + for (i = 0; i < nvlen; ++i) { + p->flags = nva[i].flags; + + memcpy(data, nva[i].name, nva[i].namelen); + p->name = data; + p->namelen = nva[i].namelen; + data[p->namelen] = '\0'; + nghttp2_downcase(p->name, p->namelen); + data += nva[i].namelen + 1; + memcpy(data, nva[i].value, nva[i].valuelen); + p->value = data; + p->valuelen = nva[i].valuelen; + data[p->valuelen] = '\0'; + data += nva[i].valuelen + 1; + ++p; + } + return 0; +} + +int nghttp2_iv_check(const nghttp2_settings_entry *iv, size_t niv) { + size_t i; + for (i = 0; i < niv; ++i) { + switch (iv[i].settings_id) { + case NGHTTP2_SETTINGS_HEADER_TABLE_SIZE: + if (iv[i].value > NGHTTP2_MAX_HEADER_TABLE_SIZE) { + return 0; + } + break; + case NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS: + break; + case NGHTTP2_SETTINGS_ENABLE_PUSH: + if (iv[i].value != 0 && iv[i].value != 1) { + return 0; + } + break; + case NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE: + if (iv[i].value > (uint32_t)NGHTTP2_MAX_WINDOW_SIZE) { + return 0; + } + break; + case NGHTTP2_SETTINGS_MAX_FRAME_SIZE: + if (iv[i].value < NGHTTP2_MAX_FRAME_SIZE_MIN || + iv[i].value > NGHTTP2_MAX_FRAME_SIZE_MAX) { + return 0; + } + break; + case NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE: + break; + } + } + return 1; +} + +static void frame_set_pad(nghttp2_buf *buf, size_t padlen, int framehd_only) { + size_t trail_padlen; + size_t newlen; + + DEBUGF(fprintf(stderr, "send: padlen=%zu, shift left 1 bytes\n", padlen)); + + memmove(buf->pos - 1, buf->pos, NGHTTP2_FRAME_HDLEN); + + --buf->pos; + + buf->pos[4] |= NGHTTP2_FLAG_PADDED; + + newlen = (nghttp2_get_uint32(buf->pos) >> 8) + padlen; + nghttp2_put_uint32be(buf->pos, (uint32_t)((newlen << 8) + buf->pos[3])); + + if (framehd_only) { + return; + } + + trail_padlen = padlen - 1; + buf->pos[NGHTTP2_FRAME_HDLEN] = trail_padlen; + + /* zero out padding */ + memset(buf->last, 0, trail_padlen); + /* extend buffers trail_padlen bytes, since we ate previous padlen - + trail_padlen byte(s) */ + buf->last += trail_padlen; +} + +int nghttp2_frame_add_pad(nghttp2_bufs *bufs, nghttp2_frame_hd *hd, + size_t padlen, int framehd_only) { + nghttp2_buf *buf; + + if (padlen == 0) { + DEBUGF(fprintf(stderr, "send: padlen = 0, nothing to do\n")); + + return 0; + } + + /* + * We have arranged bufs like this: + * + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | |Frame header | Frame payload... : + * +-+-----------------+-------------------------------------------+ + * | |Frame header | Frame payload... : + * +-+-----------------+-------------------------------------------+ + * | |Frame header | Frame payload... : + * +-+-----------------+-------------------------------------------+ + * + * We arranged padding so that it is included in the first frame + * completely. For padded frame, we are going to adjust buf->pos of + * frame which includes padding and serialize (memmove) frame header + * in the correct position. Also extends buf->last to include + * padding. + */ + + buf = &bufs->head->buf; + + assert(nghttp2_buf_avail(buf) >= (ssize_t)padlen - 1); + + frame_set_pad(buf, padlen, framehd_only); + + hd->length += padlen; + hd->flags |= NGHTTP2_FLAG_PADDED; + + DEBUGF(fprintf(stderr, "send: final payloadlen=%zu, padlen=%zu\n", hd->length, + padlen)); + + return 0; +} diff --git a/lib/nghttp2_frame.h b/lib/nghttp2_frame.h new file mode 100644 index 0000000..7fc336c --- /dev/null +++ b/lib/nghttp2_frame.h @@ -0,0 +1,527 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGHTTP2_FRAME_H +#define NGHTTP2_FRAME_H + +#ifdef HAVE_CONFIG_H +#include +#endif /* HAVE_CONFIG_H */ + +#include +#include "nghttp2_hd.h" +#include "nghttp2_buf.h" + +#define NGHTTP2_STREAM_ID_MASK ((1u << 31) - 1) +#define NGHTTP2_PRI_GROUP_ID_MASK ((1u << 31) - 1) +#define NGHTTP2_PRIORITY_MASK ((1u << 31) - 1) +#define NGHTTP2_WINDOW_SIZE_INCREMENT_MASK ((1u << 31) - 1) +#define NGHTTP2_SETTINGS_ID_MASK ((1 << 24) - 1) + +/* The number of bytes of frame header. */ +#define NGHTTP2_FRAME_HDLEN 9 + +#define NGHTTP2_MAX_FRAME_SIZE_MAX ((1 << 24) - 1) +#define NGHTTP2_MAX_FRAME_SIZE_MIN (1 << 14) + +#define NGHTTP2_MAX_PAYLOADLEN 16384 +/* The one frame buffer length for tranmission. We may use several of + them to support CONTINUATION. To account for Pad Length field, we + allocate extra 1 byte, which saves extra large memcopying. */ +#define NGHTTP2_FRAMEBUF_CHUNKLEN \ + (NGHTTP2_FRAME_HDLEN + 1 + NGHTTP2_MAX_PAYLOADLEN) + +/* Number of inbound buffer */ +#define NGHTTP2_FRAMEBUF_MAX_NUM 5 + +/* The default length of DATA frame payload. */ +#define NGHTTP2_DATA_PAYLOADLEN NGHTTP2_MAX_FRAME_SIZE_MIN + +/* Maximum headers payload length, calculated in compressed form. + This applies to transmission only. */ +#define NGHTTP2_MAX_HEADERSLEN 65536 + +/* The number of bytes for each SETTINGS entry */ +#define NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH 6 + +/* The maximum header table size in SETTINGS_HEADER_TABLE_SIZE */ +#define NGHTTP2_MAX_HEADER_TABLE_SIZE ((1u << 31) - 1) + +/* Length of priority related fields in HEADERS/PRIORITY frames */ +#define NGHTTP2_PRIORITY_SPECLEN 5 + +/* Maximum length of padding in bytes. */ +#define NGHTTP2_MAX_PADLEN 256 + +/* Union of extension frame payload */ +typedef union { int dummy; } nghttp2_ext_frame_payload; + +void nghttp2_frame_pack_frame_hd(uint8_t *buf, const nghttp2_frame_hd *hd); + +void nghttp2_frame_unpack_frame_hd(nghttp2_frame_hd *hd, const uint8_t *buf); + +/** + * Initializes frame header |hd| with given parameters. Reserved bit + * is set to 0. + */ +void nghttp2_frame_hd_init(nghttp2_frame_hd *hd, size_t length, uint8_t type, + uint8_t flags, int32_t stream_id); + +/** + * Returns the number of priority field depending on the |flags|. If + * |flags| has neither NGHTTP2_FLAG_PRIORITY_GROUP nor + * NGHTTP2_FLAG_PRIORITY_DEPENDENCY set, return 0. + */ +size_t nghttp2_frame_priority_len(uint8_t flags); + +/** + * Packs the |pri_spec| in |buf|. This function assumes |buf| has + * enough space for serialization. + */ +void nghttp2_frame_pack_priority_spec(uint8_t *buf, + const nghttp2_priority_spec *pri_spec); + +/** + * Unpacks the priority specification from payload |payload| of length + * |payloadlen| to |pri_spec|. The |flags| is used to determine what + * kind of priority specification is in |payload|. This function + * assumes the |payload| contains whole priority specification. + */ +void nghttp2_frame_unpack_priority_spec(nghttp2_priority_spec *pri_spec, + uint8_t flags, const uint8_t *payload, + size_t payloadlen); + +/* + * Returns the offset from the HEADERS frame payload where the + * compressed header block starts. The frame payload does not include + * frame header. + */ +size_t nghttp2_frame_headers_payload_nv_offset(nghttp2_headers *frame); + +/* + * Packs HEADERS frame |frame| in wire format and store it in |bufs|. + * This function expands |bufs| as necessary to store frame. + * + * The caller must make sure that nghttp2_bufs_reset(bufs) is called + * before calling this function. + * + * frame->hd.length is assigned after length is determined during + * packing process. CONTINUATION frames are also serialized in this + * function. This function does not handle padding. + * + * This function returns 0 if it succeeds, or returns one of the + * following negative error codes: + * + * NGHTTP2_ERR_HEADER_COMP + * The deflate operation failed. + * NGHTTP2_ERR_NOMEM + * Out of memory. + */ +int nghttp2_frame_pack_headers(nghttp2_bufs *bufs, nghttp2_headers *frame, + nghttp2_hd_deflater *deflater); + +/* + * Unpacks HEADERS frame byte sequence into |frame|. This function + * only unapcks bytes that come before name/value header block and + * after possible Pad Length field. + * + * This function always succeeds and returns 0. + */ +int nghttp2_frame_unpack_headers_payload(nghttp2_headers *frame, + const uint8_t *payload, + size_t payloadlen); + +/* + * Packs PRIORITY frame |frame| in wire format and store it in + * |bufs|. + * + * The caller must make sure that nghttp2_bufs_reset(bufs) is called + * before calling this function. + * + * This function always succeeds and returns 0. + */ +int nghttp2_frame_pack_priority(nghttp2_bufs *bufs, nghttp2_priority *frame); + +/* + * Unpacks PRIORITY wire format into |frame|. + */ +void nghttp2_frame_unpack_priority_payload(nghttp2_priority *frame, + const uint8_t *payload, + size_t payloadlen); + +/* + * Packs RST_STREAM frame |frame| in wire frame format and store it in + * |bufs|. + * + * The caller must make sure that nghttp2_bufs_reset(bufs) is called + * before calling this function. + * + * This function always succeeds and returns 0. + */ +int nghttp2_frame_pack_rst_stream(nghttp2_bufs *bufs, + nghttp2_rst_stream *frame); + +/* + * Unpacks RST_STREAM frame byte sequence into |frame|. + */ +void nghttp2_frame_unpack_rst_stream_payload(nghttp2_rst_stream *frame, + const uint8_t *payload, + size_t payloadlen); + +/* + * Packs SETTINGS frame |frame| in wire format and store it in + * |bufs|. + * + * The caller must make sure that nghttp2_bufs_reset(bufs) is called + * before calling this function. + * + * This function returns 0 if it succeeds, or returns one of the + * following negative error codes: + * + * NGHTTP2_ERR_FRAME_SIZE_ERROR + * The length of the frame is too large. + */ +int nghttp2_frame_pack_settings(nghttp2_bufs *bufs, nghttp2_settings *frame); + +/* + * Packs the |iv|, which includes |niv| entries, in the |buf|, + * assuming the |buf| has at least 8 * |niv| bytes. + * + * Returns the number of bytes written into the |buf|. + */ +size_t nghttp2_frame_pack_settings_payload(uint8_t *buf, + const nghttp2_settings_entry *iv, + size_t niv); + +void nghttp2_frame_unpack_settings_entry(nghttp2_settings_entry *iv, + const uint8_t *payload); + +/* + * Makes a copy of |iv| in frame->settings.iv. The |niv| is assigned + * to frame->settings.niv. + * + * This function returns 0 if it succeeds or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + */ +int nghttp2_frame_unpack_settings_payload(nghttp2_settings *frame, + nghttp2_settings_entry *iv, + size_t niv, nghttp2_mem *mem); + +/* + * Unpacks SETTINGS payload into |*iv_ptr|. The number of entries are + * assigned to the |*niv_ptr|. This function allocates enough memory + * to store the result in |*iv_ptr|. The caller is responsible to free + * |*iv_ptr| after its use. + * + * This function returns 0 if it succeeds or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + */ +int nghttp2_frame_unpack_settings_payload2(nghttp2_settings_entry **iv_ptr, + size_t *niv_ptr, + const uint8_t *payload, + size_t payloadlen, nghttp2_mem *mem); + +/* + * Packs PUSH_PROMISE frame |frame| in wire format and store it in + * |bufs|. This function expands |bufs| as necessary to store + * frame. + * + * The caller must make sure that nghttp2_bufs_reset(bufs) is called + * before calling this function. + * + * frame->hd.length is assigned after length is determined during + * packing process. CONTINUATION frames are also serialized in this + * function. This function does not handle padding. + * + * This function returns 0 if it succeeds, or returns one of the + * following negative error codes: + * + * NGHTTP2_ERR_HEADER_COMP + * The deflate operation failed. + * NGHTTP2_ERR_NOMEM + * Out of memory. + */ +int nghttp2_frame_pack_push_promise(nghttp2_bufs *bufs, + nghttp2_push_promise *frame, + nghttp2_hd_deflater *deflater); + +/* + * Unpacks PUSH_PROMISE frame byte sequence into |frame|. This + * function only unapcks bytes that come before name/value header + * block and after possible Pad Length field. + * + * This function returns 0 if it succeeds or one of the following + * negative error codes: + * + * NGHTTP2_ERR_PROTO + * TODO END_HEADERS flag is not set + */ +int nghttp2_frame_unpack_push_promise_payload(nghttp2_push_promise *frame, + const uint8_t *payload, + size_t payloadlen); + +/* + * Packs PING frame |frame| in wire format and store it in + * |bufs|. + * + * The caller must make sure that nghttp2_bufs_reset(bufs) is called + * before calling this function. + * + * This function always succeeds and returns 0. + */ +int nghttp2_frame_pack_ping(nghttp2_bufs *bufs, nghttp2_ping *frame); + +/* + * Unpacks PING wire format into |frame|. + */ +void nghttp2_frame_unpack_ping_payload(nghttp2_ping *frame, + const uint8_t *payload, + size_t payloadlen); + +/* + * Packs GOAWAY frame |frame| in wire format and store it in |bufs|. + * This function expands |bufs| as necessary to store frame. + * + * The caller must make sure that nghttp2_bufs_reset(bufs) is called + * before calling this function. + * + * This function returns 0 if it succeeds or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + * NGHTTP2_ERR_FRAME_SIZE_ERROR + * The length of the frame is too large. + */ +int nghttp2_frame_pack_goaway(nghttp2_bufs *bufs, nghttp2_goaway *frame); + +/* + * Unpacks GOAWAY wire format into |frame|. The |payload| of length + * |payloadlen| contains first 8 bytes of payload. The + * |var_gift_payload| of length |var_gift_payloadlen| contains + * remaining payload and its buffer is gifted to the function and then + * |frame|. The |var_gift_payloadlen| must be freed by + * nghttp2_frame_goaway_free(). + */ +void nghttp2_frame_unpack_goaway_payload(nghttp2_goaway *frame, + const uint8_t *payload, + size_t payloadlen, + uint8_t *var_gift_payload, + size_t var_gift_payloadlen); + +/* + * Unpacks GOAWAY wire format into |frame|. This function only exists + * for unit test. After allocating buffer for debug data, this + * function internally calls nghttp2_frame_unpack_goaway_payload(). + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + */ +int nghttp2_frame_unpack_goaway_payload2(nghttp2_goaway *frame, + const uint8_t *payload, + size_t payloadlen, nghttp2_mem *mem); + +/* + * Packs WINDOW_UPDATE frame |frame| in wire frame format and store it + * in |bufs|. + * + * The caller must make sure that nghttp2_bufs_reset(bufs) is called + * before calling this function. + * + * This function always succeeds and returns 0. + */ +int nghttp2_frame_pack_window_update(nghttp2_bufs *bufs, + nghttp2_window_update *frame); + +/* + * Unpacks WINDOW_UPDATE frame byte sequence into |frame|. + */ +void nghttp2_frame_unpack_window_update_payload(nghttp2_window_update *frame, + const uint8_t *payload, + size_t payloadlen); + +/* + * Initializes HEADERS frame |frame| with given values. |frame| takes + * ownership of |nva|, so caller must not free it. If |stream_id| is + * not assigned yet, it must be -1. + */ +void nghttp2_frame_headers_init(nghttp2_headers *frame, uint8_t flags, + int32_t stream_id, nghttp2_headers_category cat, + const nghttp2_priority_spec *pri_spec, + nghttp2_nv *nva, size_t nvlen); + +void nghttp2_frame_headers_free(nghttp2_headers *frame, nghttp2_mem *mem); + +void nghttp2_frame_priority_init(nghttp2_priority *frame, int32_t stream_id, + const nghttp2_priority_spec *pri_spec); + +void nghttp2_frame_priority_free(nghttp2_priority *frame); + +void nghttp2_frame_rst_stream_init(nghttp2_rst_stream *frame, int32_t stream_id, + uint32_t error_code); + +void nghttp2_frame_rst_stream_free(nghttp2_rst_stream *frame); + +/* + * Initializes PUSH_PROMISE frame |frame| with given values. |frame| + * takes ownership of |nva|, so caller must not free it. + */ +void nghttp2_frame_push_promise_init(nghttp2_push_promise *frame, uint8_t flags, + int32_t stream_id, + int32_t promised_stream_id, + nghttp2_nv *nva, size_t nvlen); + +void nghttp2_frame_push_promise_free(nghttp2_push_promise *frame, + nghttp2_mem *mem); + +/* + * Initializes SETTINGS frame |frame| with given values. |frame| takes + * ownership of |iv|, so caller must not free it. The |flags| are + * bitwise-OR of one or more of nghttp2_settings_flag. + */ +void nghttp2_frame_settings_init(nghttp2_settings *frame, uint8_t flags, + nghttp2_settings_entry *iv, size_t niv); + +void nghttp2_frame_settings_free(nghttp2_settings *frame, nghttp2_mem *mem); + +/* + * Initializes PING frame |frame| with given values. If the + * |opqeue_data| is not NULL, it must point to 8 bytes memory region + * of data. The data pointed by |opaque_data| is copied. It can be + * NULL. In this case, 8 bytes NULL is used. + */ +void nghttp2_frame_ping_init(nghttp2_ping *frame, uint8_t flags, + const uint8_t *opque_data); + +void nghttp2_frame_ping_free(nghttp2_ping *frame); + +/* + * Initializes GOAWAY frame |frame| with given values. On success, + * this function takes ownership of |opaque_data|, so caller must not + * free it. If the |opaque_data_len| is 0, opaque_data could be NULL. + */ +void nghttp2_frame_goaway_init(nghttp2_goaway *frame, int32_t last_stream_id, + uint32_t error_code, uint8_t *opaque_data, + size_t opaque_data_len); + +void nghttp2_frame_goaway_free(nghttp2_goaway *frame, nghttp2_mem *mem); + +void nghttp2_frame_window_update_init(nghttp2_window_update *frame, + uint8_t flags, int32_t stream_id, + int32_t window_size_increment); + +void nghttp2_frame_window_update_free(nghttp2_window_update *frame); + +/* + * Returns the number of padding bytes after payload. The total + * padding length is given in the |padlen|. The returned value does + * not include the Pad Length field. + */ +size_t nghttp2_frame_trail_padlen(nghttp2_frame *frame, size_t padlen); + +void nghttp2_frame_data_init(nghttp2_data *frame, uint8_t flags, + int32_t stream_id); + +void nghttp2_frame_data_free(nghttp2_data *frame); + +/* + * Makes copy of |iv| and return the copy. The |niv| is the number of + * entries in |iv|. This function returns the pointer to the copy if + * it succeeds, or NULL. + */ +nghttp2_settings_entry *nghttp2_frame_iv_copy(const nghttp2_settings_entry *iv, + size_t niv, nghttp2_mem *mem); + +/* + * Sorts the |nva| in ascending order of name and value. If names are + * equivalent, sort them by value. + */ +void nghttp2_nv_array_sort(nghttp2_nv *nva, size_t nvlen); + +/* + * Copies name/value pairs from |nva|, which contains |nvlen| pairs, + * to |*nva_ptr|, which is dynamically allocated so that all items can + * be stored. The resultant name and value in nghttp2_nv are + * guaranteed to be NULL-terminated even if the input is not + * null-terminated. + * + * The |*nva_ptr| must be freed using nghttp2_nv_array_del(). + * + * This function returns 0 if it succeeds or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + */ +int nghttp2_nv_array_copy(nghttp2_nv **nva_ptr, const nghttp2_nv *nva, + size_t nvlen, nghttp2_mem *mem); + +/* + * Returns nonzero if the name/value pair |a| equals to |b|. The name + * is compared in case-sensitive, because we ensure that this function + * is called after the name is lower-cased. + */ +int nghttp2_nv_equal(const nghttp2_nv *a, const nghttp2_nv *b); + +/* + * Frees |nva|. + */ +void nghttp2_nv_array_del(nghttp2_nv *nva, nghttp2_mem *mem); + +/* + * Checks that the |iv|, which includes |niv| entries, does not have + * invalid values. + * + * This function returns nonzero if it succeeds, or 0. + */ +int nghttp2_iv_check(const nghttp2_settings_entry *iv, size_t niv); + +/* + * Sets Pad Length field and flags and adjusts frame header position + * of each buffers in |bufs|. The number of padding is given in the + * |padlen| including Pad Length field. The |hd| is the frame header + * for the serialized data. This function fills zeros padding region + * unless framehd_only is nonzero. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + * NGHTTP2_ERR_FRAME_SIZE_ERROR + * The length of the resulting frame is too large. + */ +int nghttp2_frame_add_pad(nghttp2_bufs *bufs, nghttp2_frame_hd *hd, + size_t padlen, int framehd_only); + +#endif /* NGHTTP2_FRAME_H */ diff --git a/lib/nghttp2_hd.c b/lib/nghttp2_hd.c new file mode 100644 index 0000000..f9444e0 --- /dev/null +++ b/lib/nghttp2_hd.c @@ -0,0 +1,2343 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2013 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "nghttp2_hd.h" + +#include +#include +#include + +#include "nghttp2_helper.h" +#include "nghttp2_int.h" + +/* Make scalar initialization form of nghttp2_hd_entry */ +#define MAKE_STATIC_ENT(N, V, T) \ + { \ + { (uint8_t *)(N), (uint8_t *)(V), sizeof((N)) - 1, sizeof((V)) - 1, 0 } \ + , (T), 1, NGHTTP2_HD_FLAG_NONE \ + } + +/* Generated by mkstatictbl.py */ +/* 3rd parameter is nghttp2_token value for header field name. We use + first enum value if same header names are repeated (e.g., + :status). */ +static nghttp2_hd_entry static_table[] = { + MAKE_STATIC_ENT(":authority", "", 0), + MAKE_STATIC_ENT(":method", "GET", 1), + MAKE_STATIC_ENT(":method", "POST", 1), + MAKE_STATIC_ENT(":path", "/", 3), + MAKE_STATIC_ENT(":path", "/index.html", 3), + MAKE_STATIC_ENT(":scheme", "http", 5), + MAKE_STATIC_ENT(":scheme", "https", 5), + MAKE_STATIC_ENT(":status", "200", 7), + MAKE_STATIC_ENT(":status", "204", 7), + MAKE_STATIC_ENT(":status", "206", 7), + MAKE_STATIC_ENT(":status", "304", 7), + MAKE_STATIC_ENT(":status", "400", 7), + MAKE_STATIC_ENT(":status", "404", 7), + MAKE_STATIC_ENT(":status", "500", 7), + MAKE_STATIC_ENT("accept-charset", "", 14), + MAKE_STATIC_ENT("accept-encoding", "gzip, deflate", 15), + MAKE_STATIC_ENT("accept-language", "", 16), + MAKE_STATIC_ENT("accept-ranges", "", 17), + MAKE_STATIC_ENT("accept", "", 18), + MAKE_STATIC_ENT("access-control-allow-origin", "", 19), + MAKE_STATIC_ENT("age", "", 20), + MAKE_STATIC_ENT("allow", "", 21), + MAKE_STATIC_ENT("authorization", "", 22), + MAKE_STATIC_ENT("cache-control", "", 23), + MAKE_STATIC_ENT("content-disposition", "", 24), + MAKE_STATIC_ENT("content-encoding", "", 25), + MAKE_STATIC_ENT("content-language", "", 26), + MAKE_STATIC_ENT("content-length", "", 27), + MAKE_STATIC_ENT("content-location", "", 28), + MAKE_STATIC_ENT("content-range", "", 29), + MAKE_STATIC_ENT("content-type", "", 30), + MAKE_STATIC_ENT("cookie", "", 31), + MAKE_STATIC_ENT("date", "", 32), + MAKE_STATIC_ENT("etag", "", 33), + MAKE_STATIC_ENT("expect", "", 34), + MAKE_STATIC_ENT("expires", "", 35), + MAKE_STATIC_ENT("from", "", 36), + MAKE_STATIC_ENT("host", "", 37), + MAKE_STATIC_ENT("if-match", "", 38), + MAKE_STATIC_ENT("if-modified-since", "", 39), + MAKE_STATIC_ENT("if-none-match", "", 40), + MAKE_STATIC_ENT("if-range", "", 41), + MAKE_STATIC_ENT("if-unmodified-since", "", 42), + MAKE_STATIC_ENT("last-modified", "", 43), + MAKE_STATIC_ENT("link", "", 44), + MAKE_STATIC_ENT("location", "", 45), + MAKE_STATIC_ENT("max-forwards", "", 46), + MAKE_STATIC_ENT("proxy-authenticate", "", 47), + MAKE_STATIC_ENT("proxy-authorization", "", 48), + MAKE_STATIC_ENT("range", "", 49), + MAKE_STATIC_ENT("referer", "", 50), + MAKE_STATIC_ENT("refresh", "", 51), + MAKE_STATIC_ENT("retry-after", "", 52), + MAKE_STATIC_ENT("server", "", 53), + MAKE_STATIC_ENT("set-cookie", "", 54), + MAKE_STATIC_ENT("strict-transport-security", "", 55), + MAKE_STATIC_ENT("transfer-encoding", "", 56), + MAKE_STATIC_ENT("user-agent", "", 57), + MAKE_STATIC_ENT("vary", "", 58), + MAKE_STATIC_ENT("via", "", 59), + MAKE_STATIC_ENT("www-authenticate", "", 60), +}; + +static int memeq(const void *s1, const void *s2, size_t n) { + return memcmp(s1, s2, n) == 0; +} + +/* + * This function was generated by genlibtokenlookup.py. Inspired by + * h2o header lookup. https://github.com/h2o/h2o + */ +static int lookup_token(const uint8_t *name, size_t namelen) { + switch (namelen) { + case 2: + switch (name[1]) { + case 'e': + if (lstreq("t", name, 1)) { + return NGHTTP2_TOKEN_TE; + } + break; + } + break; + case 3: + switch (name[2]) { + case 'a': + if (lstreq("vi", name, 2)) { + return NGHTTP2_TOKEN_VIA; + } + break; + case 'e': + if (lstreq("ag", name, 2)) { + return NGHTTP2_TOKEN_AGE; + } + break; + } + break; + case 4: + switch (name[3]) { + case 'e': + if (lstreq("dat", name, 3)) { + return NGHTTP2_TOKEN_DATE; + } + break; + case 'g': + if (lstreq("eta", name, 3)) { + return NGHTTP2_TOKEN_ETAG; + } + break; + case 'k': + if (lstreq("lin", name, 3)) { + return NGHTTP2_TOKEN_LINK; + } + break; + case 'm': + if (lstreq("fro", name, 3)) { + return NGHTTP2_TOKEN_FROM; + } + break; + case 't': + if (lstreq("hos", name, 3)) { + return NGHTTP2_TOKEN_HOST; + } + break; + case 'y': + if (lstreq("var", name, 3)) { + return NGHTTP2_TOKEN_VARY; + } + break; + } + break; + case 5: + switch (name[4]) { + case 'e': + if (lstreq("rang", name, 4)) { + return NGHTTP2_TOKEN_RANGE; + } + break; + case 'h': + if (lstreq(":pat", name, 4)) { + return NGHTTP2_TOKEN__PATH; + } + if (lstreq(":pat", name, 4)) { + return NGHTTP2_TOKEN__PATH; + } + break; + case 'w': + if (lstreq("allo", name, 4)) { + return NGHTTP2_TOKEN_ALLOW; + } + break; + } + break; + case 6: + switch (name[5]) { + case 'e': + if (lstreq("cooki", name, 5)) { + return NGHTTP2_TOKEN_COOKIE; + } + break; + case 'r': + if (lstreq("serve", name, 5)) { + return NGHTTP2_TOKEN_SERVER; + } + break; + case 't': + if (lstreq("accep", name, 5)) { + return NGHTTP2_TOKEN_ACCEPT; + } + if (lstreq("expec", name, 5)) { + return NGHTTP2_TOKEN_EXPECT; + } + break; + } + break; + case 7: + switch (name[6]) { + case 'd': + if (lstreq(":metho", name, 6)) { + return NGHTTP2_TOKEN__METHOD; + } + if (lstreq(":metho", name, 6)) { + return NGHTTP2_TOKEN__METHOD; + } + break; + case 'e': + if (lstreq(":schem", name, 6)) { + return NGHTTP2_TOKEN__SCHEME; + } + if (lstreq(":schem", name, 6)) { + return NGHTTP2_TOKEN__SCHEME; + } + if (lstreq("upgrad", name, 6)) { + return NGHTTP2_TOKEN_UPGRADE; + } + break; + case 'h': + if (lstreq("refres", name, 6)) { + return NGHTTP2_TOKEN_REFRESH; + } + break; + case 'r': + if (lstreq("refere", name, 6)) { + return NGHTTP2_TOKEN_REFERER; + } + break; + case 's': + if (lstreq(":statu", name, 6)) { + return NGHTTP2_TOKEN__STATUS; + } + if (lstreq(":statu", name, 6)) { + return NGHTTP2_TOKEN__STATUS; + } + if (lstreq(":statu", name, 6)) { + return NGHTTP2_TOKEN__STATUS; + } + if (lstreq(":statu", name, 6)) { + return NGHTTP2_TOKEN__STATUS; + } + if (lstreq(":statu", name, 6)) { + return NGHTTP2_TOKEN__STATUS; + } + if (lstreq(":statu", name, 6)) { + return NGHTTP2_TOKEN__STATUS; + } + if (lstreq(":statu", name, 6)) { + return NGHTTP2_TOKEN__STATUS; + } + if (lstreq("expire", name, 6)) { + return NGHTTP2_TOKEN_EXPIRES; + } + break; + } + break; + case 8: + switch (name[7]) { + case 'e': + if (lstreq("if-rang", name, 7)) { + return NGHTTP2_TOKEN_IF_RANGE; + } + break; + case 'h': + if (lstreq("if-matc", name, 7)) { + return NGHTTP2_TOKEN_IF_MATCH; + } + break; + case 'n': + if (lstreq("locatio", name, 7)) { + return NGHTTP2_TOKEN_LOCATION; + } + break; + } + break; + case 10: + switch (name[9]) { + case 'e': + if (lstreq("keep-aliv", name, 9)) { + return NGHTTP2_TOKEN_KEEP_ALIVE; + } + if (lstreq("set-cooki", name, 9)) { + return NGHTTP2_TOKEN_SET_COOKIE; + } + break; + case 'n': + if (lstreq("connectio", name, 9)) { + return NGHTTP2_TOKEN_CONNECTION; + } + break; + case 't': + if (lstreq("user-agen", name, 9)) { + return NGHTTP2_TOKEN_USER_AGENT; + } + break; + case 'y': + if (lstreq(":authorit", name, 9)) { + return NGHTTP2_TOKEN__AUTHORITY; + } + break; + } + break; + case 11: + switch (name[10]) { + case 'r': + if (lstreq("retry-afte", name, 10)) { + return NGHTTP2_TOKEN_RETRY_AFTER; + } + break; + } + break; + case 12: + switch (name[11]) { + case 'e': + if (lstreq("content-typ", name, 11)) { + return NGHTTP2_TOKEN_CONTENT_TYPE; + } + break; + case 's': + if (lstreq("max-forward", name, 11)) { + return NGHTTP2_TOKEN_MAX_FORWARDS; + } + break; + } + break; + case 13: + switch (name[12]) { + case 'd': + if (lstreq("last-modifie", name, 12)) { + return NGHTTP2_TOKEN_LAST_MODIFIED; + } + break; + case 'e': + if (lstreq("content-rang", name, 12)) { + return NGHTTP2_TOKEN_CONTENT_RANGE; + } + break; + case 'h': + if (lstreq("if-none-matc", name, 12)) { + return NGHTTP2_TOKEN_IF_NONE_MATCH; + } + break; + case 'l': + if (lstreq("cache-contro", name, 12)) { + return NGHTTP2_TOKEN_CACHE_CONTROL; + } + break; + case 'n': + if (lstreq("authorizatio", name, 12)) { + return NGHTTP2_TOKEN_AUTHORIZATION; + } + break; + case 's': + if (lstreq("accept-range", name, 12)) { + return NGHTTP2_TOKEN_ACCEPT_RANGES; + } + break; + } + break; + case 14: + switch (name[13]) { + case 'h': + if (lstreq("content-lengt", name, 13)) { + return NGHTTP2_TOKEN_CONTENT_LENGTH; + } + break; + case 't': + if (lstreq("accept-charse", name, 13)) { + return NGHTTP2_TOKEN_ACCEPT_CHARSET; + } + break; + } + break; + case 15: + switch (name[14]) { + case 'e': + if (lstreq("accept-languag", name, 14)) { + return NGHTTP2_TOKEN_ACCEPT_LANGUAGE; + } + break; + case 'g': + if (lstreq("accept-encodin", name, 14)) { + return NGHTTP2_TOKEN_ACCEPT_ENCODING; + } + break; + } + break; + case 16: + switch (name[15]) { + case 'e': + if (lstreq("content-languag", name, 15)) { + return NGHTTP2_TOKEN_CONTENT_LANGUAGE; + } + if (lstreq("www-authenticat", name, 15)) { + return NGHTTP2_TOKEN_WWW_AUTHENTICATE; + } + break; + case 'g': + if (lstreq("content-encodin", name, 15)) { + return NGHTTP2_TOKEN_CONTENT_ENCODING; + } + break; + case 'n': + if (lstreq("content-locatio", name, 15)) { + return NGHTTP2_TOKEN_CONTENT_LOCATION; + } + if (lstreq("proxy-connectio", name, 15)) { + return NGHTTP2_TOKEN_PROXY_CONNECTION; + } + break; + } + break; + case 17: + switch (name[16]) { + case 'e': + if (lstreq("if-modified-sinc", name, 16)) { + return NGHTTP2_TOKEN_IF_MODIFIED_SINCE; + } + break; + case 'g': + if (lstreq("transfer-encodin", name, 16)) { + return NGHTTP2_TOKEN_TRANSFER_ENCODING; + } + break; + } + break; + case 18: + switch (name[17]) { + case 'e': + if (lstreq("proxy-authenticat", name, 17)) { + return NGHTTP2_TOKEN_PROXY_AUTHENTICATE; + } + break; + } + break; + case 19: + switch (name[18]) { + case 'e': + if (lstreq("if-unmodified-sinc", name, 18)) { + return NGHTTP2_TOKEN_IF_UNMODIFIED_SINCE; + } + break; + case 'n': + if (lstreq("content-dispositio", name, 18)) { + return NGHTTP2_TOKEN_CONTENT_DISPOSITION; + } + if (lstreq("proxy-authorizatio", name, 18)) { + return NGHTTP2_TOKEN_PROXY_AUTHORIZATION; + } + break; + } + break; + case 25: + switch (name[24]) { + case 'y': + if (lstreq("strict-transport-securit", name, 24)) { + return NGHTTP2_TOKEN_STRICT_TRANSPORT_SECURITY; + } + break; + } + break; + case 27: + switch (name[26]) { + case 'n': + if (lstreq("access-control-allow-origi", name, 26)) { + return NGHTTP2_TOKEN_ACCESS_CONTROL_ALLOW_ORIGIN; + } + break; + } + break; + } + return -1; +} + +int nghttp2_hd_entry_init(nghttp2_hd_entry *ent, uint8_t flags, uint8_t *name, + size_t namelen, uint8_t *value, size_t valuelen, + int token, nghttp2_mem *mem) { + int rv = 0; + + /* Since nghttp2_hd_entry is used for indexing, ent->nv.flags always + NGHTTP2_NV_FLAG_NONE */ + ent->nv.flags = NGHTTP2_NV_FLAG_NONE; + + if ((flags & NGHTTP2_HD_FLAG_NAME_ALLOC) && + (flags & NGHTTP2_HD_FLAG_NAME_GIFT) == 0) { + if (namelen == 0) { + flags &= ~NGHTTP2_HD_FLAG_NAME_ALLOC; + ent->nv.name = (uint8_t *)""; + } else { + /* copy including terminating NULL byte */ + ent->nv.name = nghttp2_memdup(name, namelen + 1, mem); + if (ent->nv.name == NULL) { + rv = NGHTTP2_ERR_NOMEM; + goto fail; + } + } + } else { + ent->nv.name = name; + } + if ((flags & NGHTTP2_HD_FLAG_VALUE_ALLOC) && + (flags & NGHTTP2_HD_FLAG_VALUE_GIFT) == 0) { + if (valuelen == 0) { + flags &= ~NGHTTP2_HD_FLAG_VALUE_ALLOC; + ent->nv.value = (uint8_t *)""; + } else { + /* copy including terminating NULL byte */ + ent->nv.value = nghttp2_memdup(value, valuelen + 1, mem); + if (ent->nv.value == NULL) { + rv = NGHTTP2_ERR_NOMEM; + goto fail2; + } + } + } else { + ent->nv.value = value; + } + ent->nv.namelen = namelen; + ent->nv.valuelen = valuelen; + ent->token = token; + ent->ref = 1; + ent->flags = flags; + + return 0; + +fail2: + if (flags & NGHTTP2_HD_FLAG_NAME_ALLOC) { + nghttp2_mem_free(mem, ent->nv.name); + } +fail: + return rv; +} + +void nghttp2_hd_entry_free(nghttp2_hd_entry *ent, nghttp2_mem *mem) { + assert(ent->ref == 0); + if (ent->flags & NGHTTP2_HD_FLAG_NAME_ALLOC) { + nghttp2_mem_free(mem, ent->nv.name); + } + if (ent->flags & NGHTTP2_HD_FLAG_VALUE_ALLOC) { + nghttp2_mem_free(mem, ent->nv.value); + } +} + +static int hd_ringbuf_init(nghttp2_hd_ringbuf *ringbuf, size_t bufsize, + nghttp2_mem *mem) { + size_t size; + for (size = 1; size < bufsize; size <<= 1) + ; + ringbuf->buffer = nghttp2_mem_malloc(mem, sizeof(nghttp2_hd_entry *) * size); + if (ringbuf->buffer == NULL) { + return NGHTTP2_ERR_NOMEM; + } + ringbuf->mask = size - 1; + ringbuf->first = 0; + ringbuf->len = 0; + return 0; +} + +static nghttp2_hd_entry *hd_ringbuf_get(nghttp2_hd_ringbuf *ringbuf, + size_t idx) { + assert(idx < ringbuf->len); + return ringbuf->buffer[(ringbuf->first + idx) & ringbuf->mask]; +} + +static int hd_ringbuf_reserve(nghttp2_hd_ringbuf *ringbuf, size_t bufsize, + nghttp2_mem *mem) { + size_t i; + size_t size; + nghttp2_hd_entry **buffer; + + if (ringbuf->mask + 1 >= bufsize) { + return 0; + } + for (size = 1; size < bufsize; size <<= 1) + ; + buffer = nghttp2_mem_malloc(mem, sizeof(nghttp2_hd_entry *) * size); + if (buffer == NULL) { + return NGHTTP2_ERR_NOMEM; + } + for (i = 0; i < ringbuf->len; ++i) { + buffer[i] = hd_ringbuf_get(ringbuf, i); + } + nghttp2_mem_free(mem, ringbuf->buffer); + ringbuf->buffer = buffer; + ringbuf->mask = size - 1; + ringbuf->first = 0; + return 0; +} + +static void hd_ringbuf_free(nghttp2_hd_ringbuf *ringbuf, nghttp2_mem *mem) { + size_t i; + if (ringbuf == NULL) { + return; + } + for (i = 0; i < ringbuf->len; ++i) { + nghttp2_hd_entry *ent = hd_ringbuf_get(ringbuf, i); + --ent->ref; + nghttp2_hd_entry_free(ent, mem); + nghttp2_mem_free(mem, ent); + } + nghttp2_mem_free(mem, ringbuf->buffer); +} + +static int hd_ringbuf_push_front(nghttp2_hd_ringbuf *ringbuf, + nghttp2_hd_entry *ent, nghttp2_mem *mem) { + int rv; + + rv = hd_ringbuf_reserve(ringbuf, ringbuf->len + 1, mem); + + if (rv != 0) { + return rv; + } + + ringbuf->buffer[--ringbuf->first & ringbuf->mask] = ent; + ++ringbuf->len; + + return 0; +} + +static void hd_ringbuf_pop_back(nghttp2_hd_ringbuf *ringbuf) { + assert(ringbuf->len > 0); + --ringbuf->len; +} + +static int hd_context_init(nghttp2_hd_context *context, nghttp2_mem *mem) { + int rv; + context->mem = mem; + context->bad = 0; + context->hd_table_bufsize_max = NGHTTP2_HD_DEFAULT_MAX_BUFFER_SIZE; + rv = hd_ringbuf_init(&context->hd_table, context->hd_table_bufsize_max / + NGHTTP2_HD_ENTRY_OVERHEAD, + mem); + if (rv != 0) { + return rv; + } + + context->hd_table_bufsize = 0; + return 0; +} + +static void hd_context_free(nghttp2_hd_context *context) { + hd_ringbuf_free(&context->hd_table, context->mem); +} + +int nghttp2_hd_deflate_init(nghttp2_hd_deflater *deflater, nghttp2_mem *mem) { + return nghttp2_hd_deflate_init2( + deflater, NGHTTP2_HD_DEFAULT_MAX_DEFLATE_BUFFER_SIZE, mem); +} + +int nghttp2_hd_deflate_init2(nghttp2_hd_deflater *deflater, + size_t deflate_hd_table_bufsize_max, + nghttp2_mem *mem) { + int rv; + rv = hd_context_init(&deflater->ctx, mem); + if (rv != 0) { + return rv; + } + + if (deflate_hd_table_bufsize_max < NGHTTP2_HD_DEFAULT_MAX_BUFFER_SIZE) { + deflater->notify_table_size_change = 1; + deflater->ctx.hd_table_bufsize_max = deflate_hd_table_bufsize_max; + } else { + deflater->notify_table_size_change = 0; + } + + deflater->deflate_hd_table_bufsize_max = deflate_hd_table_bufsize_max; + deflater->min_hd_table_bufsize_max = UINT32_MAX; + + return 0; +} + +int nghttp2_hd_inflate_init(nghttp2_hd_inflater *inflater, nghttp2_mem *mem) { + int rv; + + rv = hd_context_init(&inflater->ctx, mem); + if (rv != 0) { + goto fail; + } + + inflater->settings_hd_table_bufsize_max = NGHTTP2_HD_DEFAULT_MAX_BUFFER_SIZE; + + inflater->ent_keep = NULL; + inflater->nv_keep = NULL; + + inflater->opcode = NGHTTP2_HD_OPCODE_NONE; + inflater->state = NGHTTP2_HD_STATE_OPCODE; + + rv = nghttp2_bufs_init3(&inflater->nvbufs, NGHTTP2_HD_MAX_NV / 8, 8, 1, 0, + mem); + + if (rv != 0) { + goto nvbufs_fail; + } + + inflater->huffman_encoded = 0; + inflater->index = 0; + inflater->left = 0; + inflater->shift = 0; + inflater->newnamelen = 0; + inflater->index_required = 0; + inflater->no_index = 0; + + return 0; + +nvbufs_fail: + hd_context_free(&inflater->ctx); +fail: + return rv; +} + +static void hd_inflate_keep_free(nghttp2_hd_inflater *inflater) { + nghttp2_mem *mem; + + mem = inflater->ctx.mem; + if (inflater->ent_keep) { + if (inflater->ent_keep->ref == 0) { + nghttp2_hd_entry_free(inflater->ent_keep, mem); + nghttp2_mem_free(mem, inflater->ent_keep); + } + inflater->ent_keep = NULL; + } + + nghttp2_mem_free(mem, inflater->nv_keep); + inflater->nv_keep = NULL; +} + +void nghttp2_hd_deflate_free(nghttp2_hd_deflater *deflater) { + hd_context_free(&deflater->ctx); +} + +void nghttp2_hd_inflate_free(nghttp2_hd_inflater *inflater) { + hd_inflate_keep_free(inflater); + nghttp2_bufs_free(&inflater->nvbufs); + hd_context_free(&inflater->ctx); +} + +static size_t entry_room(size_t namelen, size_t valuelen) { + return NGHTTP2_HD_ENTRY_OVERHEAD + namelen + valuelen; +} + +static int emit_indexed_header(nghttp2_nv *nv_out, int *token_out, + nghttp2_hd_entry *ent) { + DEBUGF(fprintf(stderr, "inflatehd: header emission: %s: %s\n", ent->nv.name, + ent->nv.value)); + /* ent->ref may be 0. This happens if the encoder emits literal + block larger than header table capacity with indexing. */ + *nv_out = ent->nv; + *token_out = ent->token; + return 0; +} + +static int emit_literal_header(nghttp2_nv *nv_out, int *token_out, + nghttp2_nv *nv) { + DEBUGF(fprintf(stderr, "inflatehd: header emission: %s: %s\n", nv->name, + nv->value)); + *nv_out = *nv; + *token_out = lookup_token(nv->name, nv->namelen); + return 0; +} + +static size_t count_encoded_length(size_t n, size_t prefix) { + size_t k = (1 << prefix) - 1; + size_t len = 0; + + if (n < k) { + return 1; + } + + n -= k; + ++len; + + for (; n >= 128; n >>= 7, ++len) + ; + + return len + 1; +} + +static size_t encode_length(uint8_t *buf, size_t n, size_t prefix) { + size_t k = (1 << prefix) - 1; + uint8_t *begin = buf; + + *buf &= ~k; + + if (n < k) { + *buf |= n; + return 1; + } + + *buf++ |= k; + n -= k; + + for (; n >= 128; n >>= 7) { + *buf++ = (1 << 7) | (n & 0x7f); + } + + *buf++ = (uint8_t)n; + + return (size_t)(buf - begin); +} + +/* + * Decodes |prefix| prefixed integer stored from |in|. The |last| + * represents the 1 beyond the last of the valid contiguous memory + * region from |in|. The decoded integer must be less than or equal + * to UINT32_MAX. + * + * If the |initial| is nonzero, it is used as a initial value, this + * function assumes the |in| starts with intermediate data. + * + * An entire integer is decoded successfully, decoded, the |*final| is + * set to nonzero. + * + * This function stores the decoded integer in |*res| if it succeed, + * including partial decoding (in this case, number of shift to make + * in the next call will be stored in |*shift_ptr|) and returns number + * of bytes processed, or returns -1, indicating decoding error. + */ +static ssize_t decode_length(uint32_t *res, size_t *shift_ptr, int *final, + uint32_t initial, size_t shift, uint8_t *in, + uint8_t *last, size_t prefix) { + uint32_t k = (1 << prefix) - 1; + uint32_t n = initial; + uint8_t *start = in; + + *shift_ptr = 0; + *final = 0; + + if (n == 0) { + if ((*in & k) != k) { + *res = (*in) & k; + *final = 1; + return 1; + } + + n = k; + + if (++in == last) { + *res = n; + return (ssize_t)(in - start); + } + } + + for (; in != last; ++in, shift += 7) { + uint32_t add = *in & 0x7f; + + if ((UINT32_MAX >> shift) < add) { + DEBUGF(fprintf(stderr, "inflate: integer overflow on shift\n")); + return -1; + } + + add <<= shift; + + if (UINT32_MAX - add < n) { + DEBUGF(fprintf(stderr, "inflate: integer overflow on addition\n")); + return -1; + } + + n += add; + + if ((*in & (1 << 7)) == 0) { + break; + } + } + + *shift_ptr = shift; + + if (in == last) { + *res = n; + return (ssize_t)(in - start); + } + + *res = n; + *final = 1; + return (ssize_t)(in + 1 - start); +} + +static int emit_table_size(nghttp2_bufs *bufs, size_t table_size) { + int rv; + uint8_t *bufp; + size_t blocklen; + uint8_t sb[16]; + + DEBUGF(fprintf(stderr, "deflatehd: emit table_size=%zu\n", table_size)); + + blocklen = count_encoded_length(table_size, 5); + + if (sizeof(sb) < blocklen) { + return NGHTTP2_ERR_HEADER_COMP; + } + + bufp = sb; + + *bufp = 0x20u; + + encode_length(bufp, table_size, 5); + + rv = nghttp2_bufs_add(bufs, sb, blocklen); + if (rv != 0) { + return rv; + } + + return 0; +} + +static int emit_indexed_block(nghttp2_bufs *bufs, size_t idx) { + int rv; + size_t blocklen; + uint8_t sb[16]; + uint8_t *bufp; + + blocklen = count_encoded_length(idx + 1, 7); + + DEBUGF(fprintf(stderr, "deflatehd: emit indexed index=%zu, %zu bytes\n", idx, + blocklen)); + + if (sizeof(sb) < blocklen) { + return NGHTTP2_ERR_HEADER_COMP; + } + + bufp = sb; + *bufp = 0x80u; + encode_length(bufp, idx + 1, 7); + + rv = nghttp2_bufs_add(bufs, sb, blocklen); + if (rv != 0) { + return rv; + } + + return 0; +} + +static int emit_string(nghttp2_bufs *bufs, const uint8_t *str, size_t len) { + int rv; + uint8_t sb[16]; + uint8_t *bufp; + size_t blocklen; + size_t enclen; + int huffman = 0; + + enclen = nghttp2_hd_huff_encode_count(str, len); + + if (enclen < len) { + huffman = 1; + } else { + enclen = len; + } + + blocklen = count_encoded_length(enclen, 7); + + DEBUGF(fprintf(stderr, "deflatehd: emit string str=")); + DEBUGF(fwrite(str, len, 1, stderr)); + DEBUGF(fprintf(stderr, ", length=%zu, huffman=%d, encoded_length=%zu\n", len, + huffman, enclen)); + + if (sizeof(sb) < blocklen) { + return NGHTTP2_ERR_HEADER_COMP; + } + + bufp = sb; + *bufp = huffman ? 1 << 7 : 0; + encode_length(bufp, enclen, 7); + + rv = nghttp2_bufs_add(bufs, sb, blocklen); + if (rv != 0) { + return rv; + } + + if (huffman) { + rv = nghttp2_hd_huff_encode(bufs, str, len); + } else { + assert(enclen == len); + rv = nghttp2_bufs_add(bufs, str, len); + } + + return rv; +} + +static uint8_t pack_first_byte(int indexing_mode) { + switch (indexing_mode) { + case NGHTTP2_HD_WITH_INDEXING: + return 0x40u; + case NGHTTP2_HD_WITHOUT_INDEXING: + return 0; + case NGHTTP2_HD_NEVER_INDEXING: + return 0x10u; + default: + assert(0); + } + /* This is required to compile with android NDK r10d + + --enable-werror */ + return 0; +} + +static int emit_indname_block(nghttp2_bufs *bufs, size_t idx, + const nghttp2_nv *nv, int indexing_mode) { + int rv; + uint8_t *bufp; + size_t blocklen; + uint8_t sb[16]; + size_t prefixlen; + + if (indexing_mode == NGHTTP2_HD_WITH_INDEXING) { + prefixlen = 6; + } else { + prefixlen = 4; + } + + DEBUGF(fprintf(stderr, "deflatehd: emit indname index=%zu, valuelen=%zu, " + "indexing_mode=%d\n", + idx, nv->valuelen, indexing_mode)); + + blocklen = count_encoded_length(idx + 1, prefixlen); + + if (sizeof(sb) < blocklen) { + return NGHTTP2_ERR_HEADER_COMP; + } + + bufp = sb; + + *bufp = pack_first_byte(indexing_mode); + + encode_length(bufp, idx + 1, prefixlen); + + rv = nghttp2_bufs_add(bufs, sb, blocklen); + if (rv != 0) { + return rv; + } + + rv = emit_string(bufs, nv->value, nv->valuelen); + if (rv != 0) { + return rv; + } + + return 0; +} + +static int emit_newname_block(nghttp2_bufs *bufs, const nghttp2_nv *nv, + int indexing_mode) { + int rv; + + DEBUGF(fprintf(stderr, "deflatehd: emit newname namelen=%zu, valuelen=%zu, " + "indexing_mode=%d\n", + nv->namelen, nv->valuelen, indexing_mode)); + + rv = nghttp2_bufs_addb(bufs, pack_first_byte(indexing_mode)); + if (rv != 0) { + return rv; + } + + rv = emit_string(bufs, nv->name, nv->namelen); + if (rv != 0) { + return rv; + } + + rv = emit_string(bufs, nv->value, nv->valuelen); + if (rv != 0) { + return rv; + } + + return 0; +} + +static nghttp2_hd_entry *add_hd_table_incremental(nghttp2_hd_context *context, + const nghttp2_nv *nv, + int token, + uint8_t entry_flags) { + int rv; + nghttp2_hd_entry *new_ent; + size_t room; + nghttp2_mem *mem; + + mem = context->mem; + room = entry_room(nv->namelen, nv->valuelen); + + while (context->hd_table_bufsize + room > context->hd_table_bufsize_max && + context->hd_table.len > 0) { + + size_t idx = context->hd_table.len - 1; + nghttp2_hd_entry *ent = hd_ringbuf_get(&context->hd_table, idx); + + context->hd_table_bufsize -= entry_room(ent->nv.namelen, ent->nv.valuelen); + + DEBUGF(fprintf(stderr, "hpack: remove item from header table: %s: %s\n", + ent->nv.name, ent->nv.value)); + + hd_ringbuf_pop_back(&context->hd_table); + if (--ent->ref == 0) { + nghttp2_hd_entry_free(ent, mem); + nghttp2_mem_free(mem, ent); + } + } + + new_ent = nghttp2_mem_malloc(mem, sizeof(nghttp2_hd_entry)); + if (new_ent == NULL) { + return NULL; + } + + rv = nghttp2_hd_entry_init(new_ent, entry_flags, nv->name, nv->namelen, + nv->value, nv->valuelen, token, mem); + if (rv != 0) { + nghttp2_mem_free(mem, new_ent); + return NULL; + } + + if (room > context->hd_table_bufsize_max) { + /* The entry taking more than NGHTTP2_HD_MAX_BUFFER_SIZE is + immediately evicted. */ + --new_ent->ref; + } else { + rv = hd_ringbuf_push_front(&context->hd_table, new_ent, mem); + + if (rv != 0) { + --new_ent->ref; + + if ((entry_flags & NGHTTP2_HD_FLAG_NAME_ALLOC) && + (entry_flags & NGHTTP2_HD_FLAG_NAME_GIFT)) { + /* nv->name are managed by caller. */ + new_ent->nv.name = NULL; + new_ent->nv.namelen = 0; + } + if ((entry_flags & NGHTTP2_HD_FLAG_VALUE_ALLOC) && + (entry_flags & NGHTTP2_HD_FLAG_VALUE_GIFT)) { + /* nv->value are managed by caller. */ + new_ent->nv.value = NULL; + new_ent->nv.valuelen = 0; + } + + nghttp2_hd_entry_free(new_ent, mem); + nghttp2_mem_free(mem, new_ent); + + return NULL; + } + + context->hd_table_bufsize += room; + } + return new_ent; +} + +static int name_eq(const nghttp2_nv *a, const nghttp2_nv *b) { + return a->namelen == b->namelen && memeq(a->name, b->name, a->namelen); +} + +static int value_eq(const nghttp2_nv *a, const nghttp2_nv *b) { + return a->valuelen == b->valuelen && memeq(a->value, b->value, a->valuelen); +} + +typedef struct { + ssize_t index; + /* Nonzero if both name and value are matched. */ + uint8_t name_value_match; +} search_result; + +static search_result search_static_table(const nghttp2_nv *nv, int token, + int indexing_mode) { + search_result res = {token, 0}; + int i; + + if (indexing_mode == NGHTTP2_HD_NEVER_INDEXING) { + return res; + } + + for (i = token; + i <= NGHTTP2_TOKEN_WWW_AUTHENTICATE && static_table[i].token == token; + ++i) { + if (value_eq(&static_table[i].nv, nv)) { + res.index = i; + res.name_value_match = 1; + return res; + } + } + return res; +} + +static search_result search_hd_table(nghttp2_hd_context *context, + const nghttp2_nv *nv, int token, + int indexing_mode) { + search_result res = {-1, 0}; + size_t i; + + if (token >= 0 && token <= NGHTTP2_TOKEN_WWW_AUTHENTICATE) { + res = search_static_table(nv, token, indexing_mode); + if (res.name_value_match) { + return res; + } + } + + for (i = 0; i < context->hd_table.len; ++i) { + nghttp2_hd_entry *ent = hd_ringbuf_get(&context->hd_table, i); + if (ent->token != token || (token == -1 && !name_eq(&ent->nv, nv))) { + continue; + } + + if (res.index == -1) { + res.index = (ssize_t)(i + NGHTTP2_STATIC_TABLE_LENGTH); + } + + if (indexing_mode != NGHTTP2_HD_NEVER_INDEXING && value_eq(&ent->nv, nv)) { + res.index = (ssize_t)(i + NGHTTP2_STATIC_TABLE_LENGTH); + res.name_value_match = 1; + return res; + } + } + + return res; +} + +static void hd_context_shrink_table_size(nghttp2_hd_context *context) { + nghttp2_mem *mem; + + mem = context->mem; + + while (context->hd_table_bufsize > context->hd_table_bufsize_max && + context->hd_table.len > 0) { + size_t idx = context->hd_table.len - 1; + nghttp2_hd_entry *ent = hd_ringbuf_get(&context->hd_table, idx); + context->hd_table_bufsize -= entry_room(ent->nv.namelen, ent->nv.valuelen); + hd_ringbuf_pop_back(&context->hd_table); + if (--ent->ref == 0) { + nghttp2_hd_entry_free(ent, mem); + nghttp2_mem_free(mem, ent); + } + } +} + +int nghttp2_hd_deflate_change_table_size(nghttp2_hd_deflater *deflater, + size_t settings_hd_table_bufsize_max) { + size_t next_bufsize = nghttp2_min(settings_hd_table_bufsize_max, + deflater->deflate_hd_table_bufsize_max); + + deflater->ctx.hd_table_bufsize_max = next_bufsize; + + deflater->min_hd_table_bufsize_max = + nghttp2_min(deflater->min_hd_table_bufsize_max, next_bufsize); + + deflater->notify_table_size_change = 1; + + hd_context_shrink_table_size(&deflater->ctx); + return 0; +} + +int nghttp2_hd_inflate_change_table_size(nghttp2_hd_inflater *inflater, + size_t settings_hd_table_bufsize_max) { + inflater->settings_hd_table_bufsize_max = settings_hd_table_bufsize_max; + inflater->ctx.hd_table_bufsize_max = settings_hd_table_bufsize_max; + hd_context_shrink_table_size(&inflater->ctx); + return 0; +} + +#define INDEX_RANGE_VALID(context, idx) \ + ((idx) < (context)->hd_table.len + NGHTTP2_STATIC_TABLE_LENGTH) + +static size_t get_max_index(nghttp2_hd_context *context) { + return context->hd_table.len + NGHTTP2_STATIC_TABLE_LENGTH - 1; +} + +nghttp2_hd_entry *nghttp2_hd_table_get(nghttp2_hd_context *context, + size_t idx) { + assert(INDEX_RANGE_VALID(context, idx)); + if (idx >= NGHTTP2_STATIC_TABLE_LENGTH) { + return hd_ringbuf_get(&context->hd_table, + idx - NGHTTP2_STATIC_TABLE_LENGTH); + } else { + return &static_table[idx]; + } +} + +static int hd_deflate_decide_indexing(nghttp2_hd_deflater *deflater, + const nghttp2_nv *nv, int token) { + if (token == NGHTTP2_TOKEN__PATH || token == NGHTTP2_TOKEN_AGE || + token == NGHTTP2_TOKEN_CONTENT_LENGTH || token == NGHTTP2_TOKEN_ETAG || + token == NGHTTP2_TOKEN_IF_MODIFIED_SINCE || + token == NGHTTP2_TOKEN_IF_NONE_MATCH || token == NGHTTP2_TOKEN_LOCATION || + token == NGHTTP2_TOKEN_SET_COOKIE || + entry_room(nv->namelen, nv->valuelen) > + deflater->ctx.hd_table_bufsize_max * 3 / 4) { + return NGHTTP2_HD_WITHOUT_INDEXING; + } + + return NGHTTP2_HD_WITH_INDEXING; +} + +static int deflate_nv(nghttp2_hd_deflater *deflater, nghttp2_bufs *bufs, + const nghttp2_nv *nv) { + int rv; + search_result res; + ssize_t idx; + int indexing_mode; + int token; + nghttp2_mem *mem; + + DEBUGF(fprintf(stderr, "deflatehd: deflating %s: %s\n", nv->name, nv->value)); + + mem = deflater->ctx.mem; + + token = lookup_token(nv->name, nv->namelen); + + /* Don't index authorization header field since it may contain low + entropy secret data (e.g., id/password). Also cookie header + field with less than 20 bytes value is also never indexed. This + is the same criteria used in Firefox codebase. */ + indexing_mode = + token == NGHTTP2_TOKEN_AUTHORIZATION || + (token == NGHTTP2_TOKEN_COOKIE && nv->valuelen < 20) || + (nv->flags & NGHTTP2_NV_FLAG_NO_INDEX) + ? NGHTTP2_HD_NEVER_INDEXING + : hd_deflate_decide_indexing(deflater, nv, token); + + res = search_hd_table(&deflater->ctx, nv, token, indexing_mode); + + idx = res.index; + + if (res.name_value_match) { + + DEBUGF(fprintf(stderr, "deflatehd: name/value match index=%zd\n", idx)); + + rv = emit_indexed_block(bufs, idx); + if (rv != 0) { + return rv; + } + + return 0; + } + + if (res.index != -1) { + DEBUGF(fprintf(stderr, "deflatehd: name match index=%zd\n", res.index)); + } + + if (indexing_mode == NGHTTP2_HD_WITH_INDEXING) { + nghttp2_hd_entry *new_ent; + if (idx != -1 && idx < (ssize_t)NGHTTP2_STATIC_TABLE_LENGTH) { + nghttp2_nv nv_indname; + nv_indname = *nv; + nv_indname.name = nghttp2_hd_table_get(&deflater->ctx, idx)->nv.name; + new_ent = add_hd_table_incremental(&deflater->ctx, &nv_indname, token, + NGHTTP2_HD_FLAG_VALUE_ALLOC); + } else { + new_ent = add_hd_table_incremental(&deflater->ctx, nv, token, + NGHTTP2_HD_FLAG_NAME_ALLOC | + NGHTTP2_HD_FLAG_VALUE_ALLOC); + } + if (!new_ent) { + return NGHTTP2_ERR_HEADER_COMP; + } + if (new_ent->ref == 0) { + nghttp2_hd_entry_free(new_ent, mem); + nghttp2_mem_free(mem, new_ent); + } + } + if (idx == -1) { + rv = emit_newname_block(bufs, nv, indexing_mode); + } else { + rv = emit_indname_block(bufs, idx, nv, indexing_mode); + } + if (rv != 0) { + return rv; + } + + return 0; +} + +int nghttp2_hd_deflate_hd_bufs(nghttp2_hd_deflater *deflater, + nghttp2_bufs *bufs, const nghttp2_nv *nv, + size_t nvlen) { + size_t i; + int rv = 0; + + if (deflater->ctx.bad) { + return NGHTTP2_ERR_HEADER_COMP; + } + + if (deflater->notify_table_size_change) { + size_t min_hd_table_bufsize_max; + + min_hd_table_bufsize_max = deflater->min_hd_table_bufsize_max; + + deflater->notify_table_size_change = 0; + deflater->min_hd_table_bufsize_max = UINT32_MAX; + + if (deflater->ctx.hd_table_bufsize_max > min_hd_table_bufsize_max) { + + rv = emit_table_size(bufs, min_hd_table_bufsize_max); + + if (rv != 0) { + goto fail; + } + } + + rv = emit_table_size(bufs, deflater->ctx.hd_table_bufsize_max); + + if (rv != 0) { + goto fail; + } + } + + for (i = 0; i < nvlen; ++i) { + rv = deflate_nv(deflater, bufs, &nv[i]); + if (rv != 0) { + goto fail; + } + } + + DEBUGF( + fprintf(stderr, "deflatehd: all input name/value pairs were deflated\n")); + + return 0; +fail: + DEBUGF(fprintf(stderr, "deflatehd: error return %d\n", rv)); + + deflater->ctx.bad = 1; + return rv; +} + +ssize_t nghttp2_hd_deflate_hd(nghttp2_hd_deflater *deflater, uint8_t *buf, + size_t buflen, const nghttp2_nv *nv, + size_t nvlen) { + nghttp2_bufs bufs; + int rv; + nghttp2_mem *mem; + + mem = deflater->ctx.mem; + + rv = nghttp2_bufs_wrap_init(&bufs, buf, buflen, mem); + + if (rv != 0) { + return rv; + } + + rv = nghttp2_hd_deflate_hd_bufs(deflater, &bufs, nv, nvlen); + + buflen = nghttp2_bufs_len(&bufs); + + nghttp2_bufs_wrap_free(&bufs); + + if (rv == NGHTTP2_ERR_BUFFER_ERROR) { + return NGHTTP2_ERR_INSUFF_BUFSIZE; + } + + if (rv != 0) { + return rv; + } + + return (ssize_t)buflen; +} + +size_t nghttp2_hd_deflate_bound(nghttp2_hd_deflater *deflater _U_, + const nghttp2_nv *nva, size_t nvlen) { + size_t n = 0; + size_t i; + + /* Possible Maximum Header Table Size Change. Encoding (1u << 31) - + 1 using 4 bit prefix requires 6 bytes. We may emit this at most + twice. */ + n += 12; + + /* Use Literal Header Field without indexing - New Name, since it is + most space consuming format. Also we choose the less one between + non-huffman and huffman, so using literal byte count is + sufficient for upper bound. + + Encoding (1u << 31) - 1 using 7 bit prefix requires 6 bytes. We + need 2 of this for |nvlen| header fields. */ + n += 6 * 2 * nvlen; + + for (i = 0; i < nvlen; ++i) { + n += nva[i].namelen + nva[i].valuelen; + } + + return n; +} + +int nghttp2_hd_deflate_new(nghttp2_hd_deflater **deflater_ptr, + size_t deflate_hd_table_bufsize_max) { + return nghttp2_hd_deflate_new2(deflater_ptr, deflate_hd_table_bufsize_max, + NULL); +} + +int nghttp2_hd_deflate_new2(nghttp2_hd_deflater **deflater_ptr, + size_t deflate_hd_table_bufsize_max, + nghttp2_mem *mem) { + int rv; + nghttp2_hd_deflater *deflater; + + if (mem == NULL) { + mem = nghttp2_mem_default(); + } + + deflater = nghttp2_mem_malloc(mem, sizeof(nghttp2_hd_deflater)); + + if (deflater == NULL) { + return NGHTTP2_ERR_NOMEM; + } + + rv = nghttp2_hd_deflate_init2(deflater, deflate_hd_table_bufsize_max, mem); + + if (rv != 0) { + nghttp2_mem_free(mem, deflater); + + return rv; + } + + *deflater_ptr = deflater; + + return 0; +} + +void nghttp2_hd_deflate_del(nghttp2_hd_deflater *deflater) { + nghttp2_mem *mem; + + mem = deflater->ctx.mem; + + nghttp2_hd_deflate_free(deflater); + + nghttp2_mem_free(mem, deflater); +} + +static void hd_inflate_set_huffman_encoded(nghttp2_hd_inflater *inflater, + const uint8_t *in) { + inflater->huffman_encoded = (*in & (1 << 7)) != 0; +} + +/* + * Decodes the integer from the range [in, last). The result is + * assigned to |inflater->left|. If the |inflater->left| is 0, then + * it performs variable integer decoding from scratch. Otherwise, it + * uses the |inflater->left| as the initial value and continues to + * decode assuming that [in, last) begins with intermediary sequence. + * + * This function returns the number of bytes read if it succeeds, or + * one of the following negative error codes: + * + * NGHTTP2_ERR_HEADER_COMP + * Integer decoding failed + */ +static ssize_t hd_inflate_read_len(nghttp2_hd_inflater *inflater, int *rfin, + uint8_t *in, uint8_t *last, size_t prefix, + size_t maxlen) { + ssize_t rv; + uint32_t out; + + *rfin = 0; + + rv = decode_length(&out, &inflater->shift, rfin, (uint32_t)inflater->left, + inflater->shift, in, last, prefix); + + if (rv == -1) { + DEBUGF(fprintf(stderr, "inflatehd: integer decoding failed\n")); + return NGHTTP2_ERR_HEADER_COMP; + } + + if (out > maxlen) { + DEBUGF(fprintf( + stderr, "inflatehd: integer exceeded the maximum value %zu\n", maxlen)); + return NGHTTP2_ERR_HEADER_COMP; + } + + inflater->left = out; + + DEBUGF(fprintf(stderr, "inflatehd: decoded integer is %u\n", out)); + + return rv; +} + +/* + * Reads |inflater->left| bytes from the range [in, last) and performs + * huffman decoding against them and pushes the result into the + * |buffer|. + * + * This function returns the number of bytes read if it succeeds, or + * one of the following negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory + * NGHTTP2_ERR_HEADER_COMP + * Huffman decoding failed + * NGHTTP2_ERR_BUFFER_ERROR + * Out of buffer space. + */ +static ssize_t hd_inflate_read_huff(nghttp2_hd_inflater *inflater, + nghttp2_bufs *bufs, uint8_t *in, + uint8_t *last) { + ssize_t readlen; + int final = 0; + if ((size_t)(last - in) >= inflater->left) { + last = in + inflater->left; + final = 1; + } + readlen = nghttp2_hd_huff_decode(&inflater->huff_decode_ctx, bufs, in, + last - in, final); + + if (readlen < 0) { + DEBUGF(fprintf(stderr, "inflatehd: huffman decoding failed\n")); + return readlen; + } + inflater->left -= (size_t)readlen; + return readlen; +} + +/* + * Reads |inflater->left| bytes from the range [in, last) and copies + * them into the |buffer|. + * + * This function returns the number of bytes read if it succeeds, or + * one of the following negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory + * NGHTTP2_ERR_HEADER_COMP + * Header decompression failed + * NGHTTP2_ERR_BUFFER_ERROR + * Out of buffer space. + */ +static ssize_t hd_inflate_read(nghttp2_hd_inflater *inflater, + nghttp2_bufs *bufs, uint8_t *in, uint8_t *last) { + int rv; + size_t len = nghttp2_min((size_t)(last - in), inflater->left); + rv = nghttp2_bufs_add(bufs, in, len); + if (rv != 0) { + return rv; + } + inflater->left -= len; + return (ssize_t)len; +} + +/* + * Finalize indexed header representation reception. If header is + * emitted, |*nv_out| is filled with that value and 0 is returned. If + * no header is emitted, 1 is returned. + * + * This function returns either 0 or 1 if it succeeds, or one of the + * following negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory + */ +static int hd_inflate_commit_indexed(nghttp2_hd_inflater *inflater, + nghttp2_nv *nv_out, int *token_out) { + nghttp2_hd_entry *ent = nghttp2_hd_table_get(&inflater->ctx, inflater->index); + + emit_indexed_header(nv_out, token_out, ent); + + return 0; +} + +static int hd_inflate_remove_bufs(nghttp2_hd_inflater *inflater, nghttp2_nv *nv, + int value_only) { + ssize_t rv; + size_t buflen; + uint8_t *buf; + nghttp2_buf *pbuf; + + if (inflater->index_required || + inflater->nvbufs.head != inflater->nvbufs.cur) { + + rv = nghttp2_bufs_remove(&inflater->nvbufs, &buf); + + if (rv < 0) { + return NGHTTP2_ERR_NOMEM; + } + + nghttp2_bufs_reset(&inflater->nvbufs); + + buflen = rv; + + if (value_only) { + /* we don't use this value, so no need to NULL-terminate */ + nv->name = NULL; + nv->namelen = 0; + + nv->value = buf; + nv->valuelen = buflen - 1; + } else { + nv->name = buf; + nv->namelen = inflater->newnamelen; + + nv->value = buf + nv->namelen + 1; + nv->valuelen = buflen - nv->namelen - 2; + } + + return 0; + } + + /* If we are not going to store header in header table and + name/value are in first chunk, we just refer them from nv, + instead of mallocing another memory. */ + + pbuf = &inflater->nvbufs.head->buf; + + if (value_only) { + /* we don't use this value, so no need to NULL-terminate */ + nv->name = NULL; + nv->namelen = 0; + + nv->value = pbuf->pos; + nv->valuelen = nghttp2_buf_len(pbuf) - 1; + } else { + nv->name = pbuf->pos; + nv->namelen = inflater->newnamelen; + + nv->value = pbuf->pos + nv->namelen + 1; + nv->valuelen = nghttp2_buf_len(pbuf) - nv->namelen - 2; + } + + /* Resetting does not change the content of first buffer */ + nghttp2_bufs_reset(&inflater->nvbufs); + + return 0; +} + +static int hd_inflate_remove_bufs_with_name(nghttp2_hd_inflater *inflater, + nghttp2_nv *nv, + nghttp2_hd_entry *ent_name) { +#ifndef NDEBUG + size_t rv; +#endif + size_t buflen; + uint8_t *buf; + nghttp2_mem *mem; + + mem = inflater->ctx.mem; + + /* Allocate buffer including name in ent_name, plus terminating + NULL. */ + buflen = ent_name->nv.namelen + 1 + nghttp2_bufs_len(&inflater->nvbufs); + + buf = nghttp2_mem_malloc(mem, buflen); + if (buf == NULL) { + return NGHTTP2_ERR_NOMEM; + } + + /* Copy including terminal NULL */ + memcpy(buf, ent_name->nv.name, ent_name->nv.namelen + 1); +#ifndef NDEBUG + rv = +#endif + nghttp2_bufs_remove_copy(&inflater->nvbufs, + buf + ent_name->nv.namelen + 1); + assert(ent_name->nv.namelen + 1 + rv == buflen); + + nghttp2_bufs_reset(&inflater->nvbufs); + + nv->name = buf; + nv->namelen = ent_name->nv.namelen; + + nv->value = buf + nv->namelen + 1; + nv->valuelen = buflen - nv->namelen - 2; + + return 0; +} + +/* + * Finalize literal header representation - new name- reception. If + * header is emitted, |*nv_out| is filled with that value and 0 is + * returned. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory + */ +static int hd_inflate_commit_newname(nghttp2_hd_inflater *inflater, + nghttp2_nv *nv_out, int *token_out) { + int rv; + nghttp2_nv nv; + nghttp2_mem *mem; + + mem = inflater->ctx.mem; + + rv = hd_inflate_remove_bufs(inflater, &nv, 0 /* name and value */); + if (rv != 0) { + return NGHTTP2_ERR_NOMEM; + } + + if (inflater->no_index) { + nv.flags = NGHTTP2_NV_FLAG_NO_INDEX; + } else { + nv.flags = NGHTTP2_NV_FLAG_NONE; + } + + if (inflater->index_required) { + nghttp2_hd_entry *new_ent; + uint8_t ent_flags; + + /* nv->value points to the middle of the buffer pointed by + nv->name. So we just need to keep track of nv->name for memory + management. */ + ent_flags = NGHTTP2_HD_FLAG_NAME_ALLOC | NGHTTP2_HD_FLAG_NAME_GIFT; + + new_ent = add_hd_table_incremental( + &inflater->ctx, &nv, lookup_token(nv.name, nv.namelen), ent_flags); + + if (new_ent) { + emit_indexed_header(nv_out, token_out, new_ent); + inflater->ent_keep = new_ent; + + return 0; + } + + nghttp2_mem_free(mem, nv.name); + + return NGHTTP2_ERR_NOMEM; + } + + emit_literal_header(nv_out, token_out, &nv); + + if (nv.name != inflater->nvbufs.head->buf.pos) { + inflater->nv_keep = nv.name; + } + + return 0; +} + +/* + * Finalize literal header representation - indexed name- + * reception. If header is emitted, |*nv_out| is filled with that + * value and 0 is returned. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory + */ +static int hd_inflate_commit_indname(nghttp2_hd_inflater *inflater, + nghttp2_nv *nv_out, int *token_out) { + int rv; + nghttp2_nv nv; + nghttp2_hd_entry *ent_name; + nghttp2_mem *mem; + + mem = inflater->ctx.mem; + + if (inflater->no_index) { + nv.flags = NGHTTP2_NV_FLAG_NO_INDEX; + } else { + nv.flags = NGHTTP2_NV_FLAG_NONE; + } + + ent_name = nghttp2_hd_table_get(&inflater->ctx, inflater->index); + + if (inflater->index_required) { + nghttp2_hd_entry *new_ent; + uint8_t ent_flags; + + if (inflater->index < NGHTTP2_STATIC_TABLE_LENGTH) { + /* We don't copy name in static table */ + rv = hd_inflate_remove_bufs(inflater, &nv, 1 /* value only */); + if (rv != 0) { + return NGHTTP2_ERR_NOMEM; + } + nv.name = ent_name->nv.name; + nv.namelen = ent_name->nv.namelen; + + ent_flags = NGHTTP2_HD_FLAG_VALUE_ALLOC | NGHTTP2_HD_FLAG_VALUE_GIFT; + } else { + rv = hd_inflate_remove_bufs_with_name(inflater, &nv, ent_name); + if (rv != 0) { + return NGHTTP2_ERR_NOMEM; + } + /* nv->name and nv->value are in the same buffer. */ + ent_flags = NGHTTP2_HD_FLAG_NAME_ALLOC | NGHTTP2_HD_FLAG_NAME_GIFT; + } + + new_ent = add_hd_table_incremental(&inflater->ctx, &nv, ent_name->token, + ent_flags); + + /* At this point, ent_name might be deleted. */ + + if (new_ent) { + emit_indexed_header(nv_out, token_out, new_ent); + + inflater->ent_keep = new_ent; + + return 0; + } + + nghttp2_mem_free(mem, nv.value); + + return NGHTTP2_ERR_NOMEM; + } + + rv = hd_inflate_remove_bufs(inflater, &nv, 1 /* value only */); + if (rv != 0) { + return NGHTTP2_ERR_NOMEM; + } + + nv.name = ent_name->nv.name; + nv.namelen = ent_name->nv.namelen; + + emit_literal_header(nv_out, token_out, &nv); + + if (nv.value != inflater->nvbufs.head->buf.pos) { + inflater->nv_keep = nv.value; + } + + return 0; +} + +ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_inflater *inflater, nghttp2_nv *nv_out, + int *inflate_flags, uint8_t *in, size_t inlen, + int in_final) { + int token; + + return nghttp2_hd_inflate_hd2(inflater, nv_out, inflate_flags, &token, in, + inlen, in_final); +} + +ssize_t nghttp2_hd_inflate_hd2(nghttp2_hd_inflater *inflater, + nghttp2_nv *nv_out, int *inflate_flags, + int *token_out, uint8_t *in, size_t inlen, + int in_final) { + ssize_t rv = 0; + uint8_t *first = in; + uint8_t *last = in + inlen; + int rfin = 0; + int busy = 0; + + if (inflater->ctx.bad) { + return NGHTTP2_ERR_HEADER_COMP; + } + + DEBUGF(fprintf(stderr, "inflatehd: start state=%d\n", inflater->state)); + hd_inflate_keep_free(inflater); + *token_out = -1; + *inflate_flags = NGHTTP2_HD_INFLATE_NONE; + for (; in != last || busy;) { + busy = 0; + switch (inflater->state) { + case NGHTTP2_HD_STATE_OPCODE: + if ((*in & 0xe0u) == 0x20u) { + DEBUGF(fprintf(stderr, "inflatehd: header table size change\n")); + inflater->opcode = NGHTTP2_HD_OPCODE_INDEXED; + inflater->state = NGHTTP2_HD_STATE_READ_TABLE_SIZE; + } else if (*in & 0x80u) { + DEBUGF(fprintf(stderr, "inflatehd: indexed repr\n")); + inflater->opcode = NGHTTP2_HD_OPCODE_INDEXED; + inflater->state = NGHTTP2_HD_STATE_READ_INDEX; + } else { + if (*in == 0x40u || *in == 0 || *in == 0x10u) { + DEBUGF( + fprintf(stderr, "inflatehd: literal header repr - new name\n")); + inflater->opcode = NGHTTP2_HD_OPCODE_NEWNAME; + inflater->state = NGHTTP2_HD_STATE_NEWNAME_CHECK_NAMELEN; + } else { + DEBUGF(fprintf(stderr, + "inflatehd: literal header repr - indexed name\n")); + inflater->opcode = NGHTTP2_HD_OPCODE_INDNAME; + inflater->state = NGHTTP2_HD_STATE_READ_INDEX; + } + inflater->index_required = (*in & 0x40) != 0; + inflater->no_index = (*in & 0xf0u) == 0x10u; + DEBUGF(fprintf(stderr, "inflatehd: indexing required=%d, no_index=%d\n", + inflater->index_required, inflater->no_index)); + if (inflater->opcode == NGHTTP2_HD_OPCODE_NEWNAME) { + ++in; + } + } + inflater->left = 0; + inflater->shift = 0; + break; + case NGHTTP2_HD_STATE_READ_TABLE_SIZE: + rfin = 0; + rv = hd_inflate_read_len(inflater, &rfin, in, last, 5, + inflater->settings_hd_table_bufsize_max); + if (rv < 0) { + goto fail; + } + in += rv; + if (!rfin) { + goto almost_ok; + } + DEBUGF(fprintf(stderr, "inflatehd: table_size=%zu\n", inflater->left)); + inflater->ctx.hd_table_bufsize_max = inflater->left; + hd_context_shrink_table_size(&inflater->ctx); + inflater->state = NGHTTP2_HD_STATE_OPCODE; + break; + case NGHTTP2_HD_STATE_READ_INDEX: { + size_t prefixlen; + + if (inflater->opcode == NGHTTP2_HD_OPCODE_INDEXED) { + prefixlen = 7; + } else if (inflater->index_required) { + prefixlen = 6; + } else { + prefixlen = 4; + } + + rfin = 0; + rv = hd_inflate_read_len(inflater, &rfin, in, last, prefixlen, + get_max_index(&inflater->ctx) + 1); + if (rv < 0) { + goto fail; + } + + in += rv; + + if (!rfin) { + goto almost_ok; + } + + if (inflater->left == 0) { + rv = NGHTTP2_ERR_HEADER_COMP; + goto fail; + } + + DEBUGF(fprintf(stderr, "inflatehd: index=%zu\n", inflater->left)); + if (inflater->opcode == NGHTTP2_HD_OPCODE_INDEXED) { + inflater->index = inflater->left; + --inflater->index; + + rv = hd_inflate_commit_indexed(inflater, nv_out, token_out); + if (rv < 0) { + goto fail; + } + inflater->state = NGHTTP2_HD_STATE_OPCODE; + /* If rv == 1, no header was emitted */ + if (rv == 0) { + *inflate_flags |= NGHTTP2_HD_INFLATE_EMIT; + return (ssize_t)(in - first); + } + } else { + inflater->index = inflater->left; + --inflater->index; + + inflater->state = NGHTTP2_HD_STATE_CHECK_VALUELEN; + } + break; + } + case NGHTTP2_HD_STATE_NEWNAME_CHECK_NAMELEN: + hd_inflate_set_huffman_encoded(inflater, in); + inflater->state = NGHTTP2_HD_STATE_NEWNAME_READ_NAMELEN; + inflater->left = 0; + inflater->shift = 0; + DEBUGF(fprintf(stderr, "inflatehd: huffman encoded=%d\n", + inflater->huffman_encoded != 0)); + /* Fall through */ + case NGHTTP2_HD_STATE_NEWNAME_READ_NAMELEN: + rfin = 0; + rv = hd_inflate_read_len(inflater, &rfin, in, last, 7, NGHTTP2_HD_MAX_NV); + if (rv < 0) { + goto fail; + } + in += rv; + if (!rfin) { + DEBUGF(fprintf(stderr, + "inflatehd: integer not fully decoded. current=%zu\n", + inflater->left)); + + goto almost_ok; + } + + if (inflater->huffman_encoded) { + nghttp2_hd_huff_decode_context_init(&inflater->huff_decode_ctx); + + inflater->state = NGHTTP2_HD_STATE_NEWNAME_READ_NAMEHUFF; + } else { + inflater->state = NGHTTP2_HD_STATE_NEWNAME_READ_NAME; + } + break; + case NGHTTP2_HD_STATE_NEWNAME_READ_NAMEHUFF: + rv = hd_inflate_read_huff(inflater, &inflater->nvbufs, in, last); + if (rv < 0) { + goto fail; + } + + in += rv; + + DEBUGF(fprintf(stderr, "inflatehd: %zd bytes read\n", rv)); + + if (inflater->left) { + DEBUGF(fprintf(stderr, "inflatehd: still %zu bytes to go\n", + inflater->left)); + + goto almost_ok; + } + + inflater->newnamelen = nghttp2_bufs_len(&inflater->nvbufs); + + rv = nghttp2_bufs_addb(&inflater->nvbufs, '\0'); + if (rv != 0) { + goto fail; + } + + inflater->state = NGHTTP2_HD_STATE_CHECK_VALUELEN; + + break; + case NGHTTP2_HD_STATE_NEWNAME_READ_NAME: + rv = hd_inflate_read(inflater, &inflater->nvbufs, in, last); + if (rv < 0) { + goto fail; + } + + in += rv; + + DEBUGF(fprintf(stderr, "inflatehd: %zd bytes read\n", rv)); + if (inflater->left) { + DEBUGF(fprintf(stderr, "inflatehd: still %zu bytes to go\n", + inflater->left)); + + goto almost_ok; + } + + inflater->newnamelen = nghttp2_bufs_len(&inflater->nvbufs); + + rv = nghttp2_bufs_addb(&inflater->nvbufs, '\0'); + if (rv != 0) { + goto fail; + } + + inflater->state = NGHTTP2_HD_STATE_CHECK_VALUELEN; + + break; + case NGHTTP2_HD_STATE_CHECK_VALUELEN: + hd_inflate_set_huffman_encoded(inflater, in); + inflater->state = NGHTTP2_HD_STATE_READ_VALUELEN; + inflater->left = 0; + inflater->shift = 0; + DEBUGF(fprintf(stderr, "inflatehd: huffman encoded=%d\n", + inflater->huffman_encoded != 0)); + /* Fall through */ + case NGHTTP2_HD_STATE_READ_VALUELEN: + rfin = 0; + rv = hd_inflate_read_len(inflater, &rfin, in, last, 7, NGHTTP2_HD_MAX_NV); + if (rv < 0) { + goto fail; + } + + in += rv; + + if (!rfin) { + goto almost_ok; + } + + DEBUGF(fprintf(stderr, "inflatehd: valuelen=%zu\n", inflater->left)); + + if (inflater->huffman_encoded) { + nghttp2_hd_huff_decode_context_init(&inflater->huff_decode_ctx); + + inflater->state = NGHTTP2_HD_STATE_READ_VALUEHUFF; + } else { + inflater->state = NGHTTP2_HD_STATE_READ_VALUE; + } + + busy = 1; + + break; + case NGHTTP2_HD_STATE_READ_VALUEHUFF: + rv = hd_inflate_read_huff(inflater, &inflater->nvbufs, in, last); + if (rv < 0) { + goto fail; + } + + in += rv; + + DEBUGF(fprintf(stderr, "inflatehd: %zd bytes read\n", rv)); + + if (inflater->left) { + DEBUGF(fprintf(stderr, "inflatehd: still %zu bytes to go\n", + inflater->left)); + + goto almost_ok; + } + + rv = nghttp2_bufs_addb(&inflater->nvbufs, '\0'); + if (rv != 0) { + goto fail; + } + + if (inflater->opcode == NGHTTP2_HD_OPCODE_NEWNAME) { + rv = hd_inflate_commit_newname(inflater, nv_out, token_out); + } else { + rv = hd_inflate_commit_indname(inflater, nv_out, token_out); + } + + if (rv != 0) { + goto fail; + } + + inflater->state = NGHTTP2_HD_STATE_OPCODE; + *inflate_flags |= NGHTTP2_HD_INFLATE_EMIT; + + return (ssize_t)(in - first); + case NGHTTP2_HD_STATE_READ_VALUE: + rv = hd_inflate_read(inflater, &inflater->nvbufs, in, last); + if (rv < 0) { + DEBUGF(fprintf(stderr, "inflatehd: value read failure %zd: %s\n", rv, + nghttp2_strerror((int)rv))); + goto fail; + } + + in += rv; + + DEBUGF(fprintf(stderr, "inflatehd: %zd bytes read\n", rv)); + + if (inflater->left) { + DEBUGF(fprintf(stderr, "inflatehd: still %zu bytes to go\n", + inflater->left)); + goto almost_ok; + } + + rv = nghttp2_bufs_addb(&inflater->nvbufs, '\0'); + if (rv != 0) { + goto fail; + } + + if (inflater->opcode == NGHTTP2_HD_OPCODE_NEWNAME) { + rv = hd_inflate_commit_newname(inflater, nv_out, token_out); + } else { + rv = hd_inflate_commit_indname(inflater, nv_out, token_out); + } + + if (rv != 0) { + goto fail; + } + + inflater->state = NGHTTP2_HD_STATE_OPCODE; + *inflate_flags |= NGHTTP2_HD_INFLATE_EMIT; + + return (ssize_t)(in - first); + } + } + + assert(in == last); + + DEBUGF(fprintf(stderr, "inflatehd: all input bytes were processed\n")); + + if (in_final) { + DEBUGF(fprintf(stderr, "inflatehd: in_final set\n")); + + if (inflater->state != NGHTTP2_HD_STATE_OPCODE) { + DEBUGF(fprintf(stderr, "inflatehd: unacceptable state=%d\n", + inflater->state)); + rv = NGHTTP2_ERR_HEADER_COMP; + + goto fail; + } + *inflate_flags |= NGHTTP2_HD_INFLATE_FINAL; + } + return (ssize_t)(in - first); + +almost_ok: + if (in_final && inflater->state != NGHTTP2_HD_STATE_OPCODE) { + DEBUGF(fprintf(stderr, "inflatehd: input ended prematurely\n")); + + rv = NGHTTP2_ERR_HEADER_COMP; + + goto fail; + } + return (ssize_t)(in - first); + +fail: + DEBUGF(fprintf(stderr, "inflatehd: error return %zd\n", rv)); + + inflater->ctx.bad = 1; + return rv; +} + +int nghttp2_hd_inflate_end_headers(nghttp2_hd_inflater *inflater) { + hd_inflate_keep_free(inflater); + return 0; +} + +int nghttp2_hd_inflate_new(nghttp2_hd_inflater **inflater_ptr) { + return nghttp2_hd_inflate_new2(inflater_ptr, NULL); +} + +int nghttp2_hd_inflate_new2(nghttp2_hd_inflater **inflater_ptr, + nghttp2_mem *mem) { + int rv; + nghttp2_hd_inflater *inflater; + + if (mem == NULL) { + mem = nghttp2_mem_default(); + } + + inflater = nghttp2_mem_malloc(mem, sizeof(nghttp2_hd_inflater)); + + if (inflater == NULL) { + return NGHTTP2_ERR_NOMEM; + } + + rv = nghttp2_hd_inflate_init(inflater, mem); + + if (rv != 0) { + nghttp2_mem_free(mem, inflater); + + return rv; + } + + *inflater_ptr = inflater; + + return 0; +} + +void nghttp2_hd_inflate_del(nghttp2_hd_inflater *inflater) { + nghttp2_mem *mem; + + mem = inflater->ctx.mem; + nghttp2_hd_inflate_free(inflater); + + nghttp2_mem_free(mem, inflater); +} + +int nghttp2_hd_emit_indname_block(nghttp2_bufs *bufs, size_t idx, + nghttp2_nv *nv, int indexing_mode) { + + return emit_indname_block(bufs, idx, nv, indexing_mode); +} + +int nghttp2_hd_emit_newname_block(nghttp2_bufs *bufs, nghttp2_nv *nv, + int indexing_mode) { + return emit_newname_block(bufs, nv, indexing_mode); +} + +int nghttp2_hd_emit_table_size(nghttp2_bufs *bufs, size_t table_size) { + return emit_table_size(bufs, table_size); +} + +ssize_t nghttp2_hd_decode_length(uint32_t *res, size_t *shift_ptr, int *final, + uint32_t initial, size_t shift, uint8_t *in, + uint8_t *last, size_t prefix) { + return decode_length(res, shift_ptr, final, initial, shift, in, last, prefix); +} diff --git a/lib/nghttp2_hd.h b/lib/nghttp2_hd.h new file mode 100644 index 0000000..1b9ca7e --- /dev/null +++ b/lib/nghttp2_hd.h @@ -0,0 +1,421 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2013 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGHTTP2_HD_H +#define NGHTTP2_HD_H + +#ifdef HAVE_CONFIG_H +#include +#endif /* HAVE_CONFIG_H */ + +#include + +#include "nghttp2_hd_huffman.h" +#include "nghttp2_buf.h" +#include "nghttp2_mem.h" + +#define NGHTTP2_HD_DEFAULT_MAX_BUFFER_SIZE NGHTTP2_DEFAULT_HEADER_TABLE_SIZE +#define NGHTTP2_HD_ENTRY_OVERHEAD 32 + +/* The maximum length of one name/value pair. This is the sum of the + length of name and value. This is not specified by the spec. We + just chose the arbitrary size */ +#define NGHTTP2_HD_MAX_NV 65536 + +/* Default size of maximum table buffer size for encoder. Even if + remote decoder notifies larger buffer size for its decoding, + encoder only uses the memory up to this value. */ +#define NGHTTP2_HD_DEFAULT_MAX_DEFLATE_BUFFER_SIZE (1 << 12) + +/* Exported for unit test */ +#define NGHTTP2_STATIC_TABLE_LENGTH 61 + +/* Generated by genlibtokenlookup.py */ +typedef enum { + NGHTTP2_TOKEN__AUTHORITY = 0, + NGHTTP2_TOKEN__METHOD = 1, + NGHTTP2_TOKEN__PATH = 3, + NGHTTP2_TOKEN__SCHEME = 5, + NGHTTP2_TOKEN__STATUS = 7, + NGHTTP2_TOKEN_ACCEPT_CHARSET = 14, + NGHTTP2_TOKEN_ACCEPT_ENCODING = 15, + NGHTTP2_TOKEN_ACCEPT_LANGUAGE = 16, + NGHTTP2_TOKEN_ACCEPT_RANGES = 17, + NGHTTP2_TOKEN_ACCEPT = 18, + NGHTTP2_TOKEN_ACCESS_CONTROL_ALLOW_ORIGIN = 19, + NGHTTP2_TOKEN_AGE = 20, + NGHTTP2_TOKEN_ALLOW = 21, + NGHTTP2_TOKEN_AUTHORIZATION = 22, + NGHTTP2_TOKEN_CACHE_CONTROL = 23, + NGHTTP2_TOKEN_CONTENT_DISPOSITION = 24, + NGHTTP2_TOKEN_CONTENT_ENCODING = 25, + NGHTTP2_TOKEN_CONTENT_LANGUAGE = 26, + NGHTTP2_TOKEN_CONTENT_LENGTH = 27, + NGHTTP2_TOKEN_CONTENT_LOCATION = 28, + NGHTTP2_TOKEN_CONTENT_RANGE = 29, + NGHTTP2_TOKEN_CONTENT_TYPE = 30, + NGHTTP2_TOKEN_COOKIE = 31, + NGHTTP2_TOKEN_DATE = 32, + NGHTTP2_TOKEN_ETAG = 33, + NGHTTP2_TOKEN_EXPECT = 34, + NGHTTP2_TOKEN_EXPIRES = 35, + NGHTTP2_TOKEN_FROM = 36, + NGHTTP2_TOKEN_HOST = 37, + NGHTTP2_TOKEN_IF_MATCH = 38, + NGHTTP2_TOKEN_IF_MODIFIED_SINCE = 39, + NGHTTP2_TOKEN_IF_NONE_MATCH = 40, + NGHTTP2_TOKEN_IF_RANGE = 41, + NGHTTP2_TOKEN_IF_UNMODIFIED_SINCE = 42, + NGHTTP2_TOKEN_LAST_MODIFIED = 43, + NGHTTP2_TOKEN_LINK = 44, + NGHTTP2_TOKEN_LOCATION = 45, + NGHTTP2_TOKEN_MAX_FORWARDS = 46, + NGHTTP2_TOKEN_PROXY_AUTHENTICATE = 47, + NGHTTP2_TOKEN_PROXY_AUTHORIZATION = 48, + NGHTTP2_TOKEN_RANGE = 49, + NGHTTP2_TOKEN_REFERER = 50, + NGHTTP2_TOKEN_REFRESH = 51, + NGHTTP2_TOKEN_RETRY_AFTER = 52, + NGHTTP2_TOKEN_SERVER = 53, + NGHTTP2_TOKEN_SET_COOKIE = 54, + NGHTTP2_TOKEN_STRICT_TRANSPORT_SECURITY = 55, + NGHTTP2_TOKEN_TRANSFER_ENCODING = 56, + NGHTTP2_TOKEN_USER_AGENT = 57, + NGHTTP2_TOKEN_VARY = 58, + NGHTTP2_TOKEN_VIA = 59, + NGHTTP2_TOKEN_WWW_AUTHENTICATE = 60, + NGHTTP2_TOKEN_TE, + NGHTTP2_TOKEN_CONNECTION, + NGHTTP2_TOKEN_KEEP_ALIVE, + NGHTTP2_TOKEN_PROXY_CONNECTION, + NGHTTP2_TOKEN_UPGRADE +} nghttp2_token; + +typedef enum { + NGHTTP2_HD_FLAG_NONE = 0, + /* Indicates name was dynamically allocated and must be freed */ + NGHTTP2_HD_FLAG_NAME_ALLOC = 1, + /* Indicates value was dynamically allocated and must be freed */ + NGHTTP2_HD_FLAG_VALUE_ALLOC = 1 << 1, + /* Indicates that the name was gifted to the entry and no copying + necessary. */ + NGHTTP2_HD_FLAG_NAME_GIFT = 1 << 2, + /* Indicates that the value was gifted to the entry and no copying + necessary. */ + NGHTTP2_HD_FLAG_VALUE_GIFT = 1 << 3 +} nghttp2_hd_flags; + +typedef struct { + nghttp2_nv nv; + /* nghttp2_token value for nv.name. It could be -1 if we have no + token for that header field name. */ + int token; + /* Reference count */ + uint8_t ref; + uint8_t flags; +} nghttp2_hd_entry; + +typedef struct { + nghttp2_hd_entry **buffer; + size_t mask; + size_t first; + size_t len; +} nghttp2_hd_ringbuf; + +typedef enum { + NGHTTP2_HD_OPCODE_NONE, + NGHTTP2_HD_OPCODE_INDEXED, + NGHTTP2_HD_OPCODE_NEWNAME, + NGHTTP2_HD_OPCODE_INDNAME +} nghttp2_hd_opcode; + +typedef enum { + NGHTTP2_HD_STATE_OPCODE, + NGHTTP2_HD_STATE_READ_TABLE_SIZE, + NGHTTP2_HD_STATE_READ_INDEX, + NGHTTP2_HD_STATE_NEWNAME_CHECK_NAMELEN, + NGHTTP2_HD_STATE_NEWNAME_READ_NAMELEN, + NGHTTP2_HD_STATE_NEWNAME_READ_NAMEHUFF, + NGHTTP2_HD_STATE_NEWNAME_READ_NAME, + NGHTTP2_HD_STATE_CHECK_VALUELEN, + NGHTTP2_HD_STATE_READ_VALUELEN, + NGHTTP2_HD_STATE_READ_VALUEHUFF, + NGHTTP2_HD_STATE_READ_VALUE +} nghttp2_hd_inflate_state; + +typedef enum { + NGHTTP2_HD_WITH_INDEXING, + NGHTTP2_HD_WITHOUT_INDEXING, + NGHTTP2_HD_NEVER_INDEXING +} nghttp2_hd_indexing_mode; + +typedef struct { + /* dynamic header table */ + nghttp2_hd_ringbuf hd_table; + /* Memory allocator */ + nghttp2_mem *mem; + /* Abstract buffer size of hd_table as described in the spec. This + is the sum of length of name/value in hd_table + + NGHTTP2_HD_ENTRY_OVERHEAD bytes overhead per each entry. */ + size_t hd_table_bufsize; + /* The effective header table size. */ + size_t hd_table_bufsize_max; + /* If inflate/deflate error occurred, this value is set to 1 and + further invocation of inflate/deflate will fail with + NGHTTP2_ERR_HEADER_COMP. */ + uint8_t bad; +} nghttp2_hd_context; + +struct nghttp2_hd_deflater { + nghttp2_hd_context ctx; + /* The upper limit of the header table size the deflater accepts. */ + size_t deflate_hd_table_bufsize_max; + /* Minimum header table size notified in the next context update */ + size_t min_hd_table_bufsize_max; + /* If nonzero, send header table size using encoding context update + in the next deflate process */ + uint8_t notify_table_size_change; +}; + +struct nghttp2_hd_inflater { + nghttp2_hd_context ctx; + /* header buffer */ + nghttp2_bufs nvbufs; + /* Stores current state of huffman decoding */ + nghttp2_hd_huff_decode_context huff_decode_ctx; + /* Pointer to the nghttp2_hd_entry which is used current header + emission. This is required because in some cases the + ent_keep->ref == 0 and we have to keep track of it. */ + nghttp2_hd_entry *ent_keep; + /* Pointer to the name/value pair buffer which is used in the + current header emission. */ + uint8_t *nv_keep; + /* The number of bytes to read */ + size_t left; + /* The index in indexed repr or indexed name */ + size_t index; + /* The length of new name encoded in literal. For huffman encoded + string, this is the length after it is decoded. */ + size_t newnamelen; + /* The maximum header table size the inflater supports. This is the + same value transmitted in SETTINGS_HEADER_TABLE_SIZE */ + size_t settings_hd_table_bufsize_max; + /* The number of next shift to decode integer */ + size_t shift; + nghttp2_hd_opcode opcode; + nghttp2_hd_inflate_state state; + /* nonzero if string is huffman encoded */ + uint8_t huffman_encoded; + /* nonzero if deflater requires that current entry is indexed */ + uint8_t index_required; + /* nonzero if deflater requires that current entry must not be + indexed */ + uint8_t no_index; +}; + +/* + * Initializes the |ent| members. If NGHTTP2_HD_FLAG_NAME_ALLOC bit + * set in the |flags|, the content pointed by the |name| with length + * |namelen| is copied. Likewise, if NGHTTP2_HD_FLAG_VALUE_ALLOC bit + * set in the |flags|, the content pointed by the |value| with length + * |valuelen| is copied. The |token| is enum number looked up by + * |name|. It could be -1 if we don't have that enum value. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + */ +int nghttp2_hd_entry_init(nghttp2_hd_entry *ent, uint8_t flags, uint8_t *name, + size_t namelen, uint8_t *value, size_t valuelen, + int token, nghttp2_mem *mem); + +void nghttp2_hd_entry_free(nghttp2_hd_entry *ent, nghttp2_mem *mem); + +/* + * Initializes |deflater| for deflating name/values pairs. + * + * The encoder only uses up to + * NGHTTP2_HD_DEFAULT_MAX_DEFLATE_BUFFER_SIZE bytes for header table + * even if the larger value is specified later in + * nghttp2_hd_change_table_size(). + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + */ +int nghttp2_hd_deflate_init(nghttp2_hd_deflater *deflater, nghttp2_mem *mem); + +/* + * Initializes |deflater| for deflating name/values pairs. + * + * The encoder only uses up to |deflate_hd_table_bufsize_max| bytes + * for header table even if the larger value is specified later in + * nghttp2_hd_change_table_size(). + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + */ +int nghttp2_hd_deflate_init2(nghttp2_hd_deflater *deflater, + size_t deflate_hd_table_bufsize_max, + nghttp2_mem *mem); + +/* + * Deallocates any resources allocated for |deflater|. + */ +void nghttp2_hd_deflate_free(nghttp2_hd_deflater *deflater); + +/* + * Deflates the |nva|, which has the |nvlen| name/value pairs, into + * the |bufs|. + * + * This function expands |bufs| as necessary to store the result. If + * buffers is full and the process still requires more space, this + * funtion fails and returns NGHTTP2_ERR_HEADER_COMP. + * + * After this function returns, it is safe to delete the |nva|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + * NGHTTP2_ERR_HEADER_COMP + * Deflation process has failed. + * NGHTTP2_ERR_BUFFER_ERROR + * Out of buffer space. + */ +int nghttp2_hd_deflate_hd_bufs(nghttp2_hd_deflater *deflater, + nghttp2_bufs *bufs, const nghttp2_nv *nva, + size_t nvlen); + +/* + * Initializes |inflater| for inflating name/values pairs. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`NGHTTP2_ERR_NOMEM` + * Out of memory. + */ +int nghttp2_hd_inflate_init(nghttp2_hd_inflater *inflater, nghttp2_mem *mem); + +/* + * Deallocates any resources allocated for |inflater|. + */ +void nghttp2_hd_inflate_free(nghttp2_hd_inflater *inflater); + +/* + * Similar to nghttp2_hd_inflate_hd(), but this takes additional + * output parameter |token|. On successful header emission, it + * contains nghttp2_token value for nv_out->name. It could be -1 if + * we don't have enum value for the name. Other than that return + * values and semantics are the same as nghttp2_hd_inflate_hd(). + */ +ssize_t nghttp2_hd_inflate_hd2(nghttp2_hd_inflater *inflater, + nghttp2_nv *nv_out, int *inflate_flags, + int *token, uint8_t *in, size_t inlen, + int in_final); + +/* For unittesting purpose */ +int nghttp2_hd_emit_indname_block(nghttp2_bufs *bufs, size_t index, + nghttp2_nv *nv, int indexing_mode); + +/* For unittesting purpose */ +int nghttp2_hd_emit_newname_block(nghttp2_bufs *bufs, nghttp2_nv *nv, + int indexing_mode); + +/* For unittesting purpose */ +int nghttp2_hd_emit_table_size(nghttp2_bufs *bufs, size_t table_size); + +/* For unittesting purpose */ +nghttp2_hd_entry *nghttp2_hd_table_get(nghttp2_hd_context *context, + size_t index); + +/* For unittesting purpose */ +ssize_t nghttp2_hd_decode_length(uint32_t *res, size_t *shift_ptr, int *final, + uint32_t initial, size_t shift, uint8_t *in, + uint8_t *last, size_t prefix); + +/* Huffman encoding/decoding functions */ + +/* + * Counts the required bytes to encode |src| with length |len|. + * + * This function returns the number of required bytes to encode given + * data, including padding of prefix of terminal symbol code. This + * function always succeeds. + */ +size_t nghttp2_hd_huff_encode_count(const uint8_t *src, size_t len); + +/* + * Encodes the given data |src| with length |srclen| to the |bufs|. + * This function expands extra buffers in |bufs| if necessary. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + * NGHTTP2_ERR_BUFFER_ERROR + * Out of buffer space. + */ +int nghttp2_hd_huff_encode(nghttp2_bufs *bufs, const uint8_t *src, + size_t srclen); + +void nghttp2_hd_huff_decode_context_init(nghttp2_hd_huff_decode_context *ctx); + +/* + * Decodes the given data |src| with length |srclen|. The |ctx| must + * be initialized by nghttp2_hd_huff_decode_context_init(). The result + * will be added to |dest|. This function may expand |dest| as + * needed. The caller is responsible to release the memory of |dest| + * by calling nghttp2_bufs_free(). + * + * The caller must set the |final| to nonzero if the given input is + * the final block. + * + * This function returns the number of read bytes from the |in|. + * + * If this function fails, it returns one of the following negative + * return codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + * NGHTTP2_ERR_BUFFER_ERROR + * Maximum buffer capacity size exceeded. + * NGHTTP2_ERR_HEADER_COMP + * Decoding process has failed. + */ +ssize_t nghttp2_hd_huff_decode(nghttp2_hd_huff_decode_context *ctx, + nghttp2_bufs *bufs, const uint8_t *src, + size_t srclen, int final); + +#endif /* NGHTTP2_HD_H */ diff --git a/lib/nghttp2_hd_huffman.c b/lib/nghttp2_hd_huffman.c new file mode 100644 index 0000000..4df1cd0 --- /dev/null +++ b/lib/nghttp2_hd_huffman.c @@ -0,0 +1,227 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2013 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "nghttp2_hd_huffman.h" + +#include +#include +#include + +#include "nghttp2_hd.h" + +extern const nghttp2_huff_sym huff_sym_table[]; +extern const nghttp2_huff_decode huff_decode_table[][16]; + +/* + * Encodes huffman code |sym| into |*dest_ptr|, whose least |rembits| + * bits are not filled yet. The |rembits| must be in range [1, 8], + * inclusive. At the end of the process, the |*dest_ptr| is updated + * and points where next output should be placed. The number of + * unfilled bits in the pointed location is returned. + */ +static ssize_t huff_encode_sym(nghttp2_bufs *bufs, size_t *avail_ptr, + size_t rembits, const nghttp2_huff_sym *sym) { + int rv; + size_t nbits = sym->nbits; + uint32_t code = sym->code; + + /* We assume that sym->nbits <= 32 */ + if (rembits > nbits) { + nghttp2_bufs_fast_orb_hold(bufs, code << (rembits - nbits)); + return (ssize_t)(rembits - nbits); + } + + if (rembits == nbits) { + nghttp2_bufs_fast_orb(bufs, code); + --*avail_ptr; + return 8; + } + + nghttp2_bufs_fast_orb(bufs, code >> (nbits - rembits)); + --*avail_ptr; + + nbits -= rembits; + if (nbits & 0x7) { + /* align code to MSB byte boundary */ + code <<= 8 - (nbits & 0x7); + } + + /* we lose at most 3 bytes, but it is not critical in practice */ + if (*avail_ptr < (nbits + 7) / 8) { + rv = nghttp2_bufs_advance(bufs); + if (rv != 0) { + return rv; + } + *avail_ptr = nghttp2_bufs_cur_avail(bufs); + /* we assume that we at least 3 buffer space available */ + assert(*avail_ptr >= 3); + } + + /* fast path, since most code is less than 8 */ + if (nbits < 8) { + nghttp2_bufs_fast_addb_hold(bufs, code); + *avail_ptr = nghttp2_bufs_cur_avail(bufs); + return (ssize_t)(8 - nbits); + } + + /* handle longer code path */ + if (nbits > 24) { + nghttp2_bufs_fast_addb(bufs, code >> 24); + nbits -= 8; + } + + if (nbits > 16) { + nghttp2_bufs_fast_addb(bufs, code >> 16); + nbits -= 8; + } + + if (nbits > 8) { + nghttp2_bufs_fast_addb(bufs, code >> 8); + nbits -= 8; + } + + if (nbits == 8) { + nghttp2_bufs_fast_addb(bufs, code); + *avail_ptr = nghttp2_bufs_cur_avail(bufs); + return 8; + } + + nghttp2_bufs_fast_addb_hold(bufs, code); + *avail_ptr = nghttp2_bufs_cur_avail(bufs); + return (ssize_t)(8 - nbits); +} + +size_t nghttp2_hd_huff_encode_count(const uint8_t *src, size_t len) { + size_t i; + size_t nbits = 0; + + for (i = 0; i < len; ++i) { + nbits += huff_sym_table[src[i]].nbits; + } + /* pad the prefix of EOS (256) */ + return (nbits + 7) / 8; +} + +int nghttp2_hd_huff_encode(nghttp2_bufs *bufs, const uint8_t *src, + size_t srclen) { + int rv; + ssize_t rembits = 8; + size_t i; + size_t avail; + + avail = nghttp2_bufs_cur_avail(bufs); + + for (i = 0; i < srclen; ++i) { + const nghttp2_huff_sym *sym = &huff_sym_table[src[i]]; + if (rembits == 8) { + if (avail) { + nghttp2_bufs_fast_addb_hold(bufs, 0); + } else { + rv = nghttp2_bufs_addb_hold(bufs, 0); + if (rv != 0) { + return rv; + } + avail = nghttp2_bufs_cur_avail(bufs); + } + } + rembits = huff_encode_sym(bufs, &avail, rembits, sym); + if (rembits < 0) { + return (int)rembits; + } + } + /* 256 is special terminal symbol, pad with its prefix */ + if (rembits < 8) { + /* if rembits < 8, we should have at least 1 buffer space + available */ + const nghttp2_huff_sym *sym = &huff_sym_table[256]; + assert(avail); + /* Caution we no longer adjust avail here */ + nghttp2_bufs_fast_orb(bufs, sym->code >> (sym->nbits - rembits)); + } + + return 0; +} + +void nghttp2_hd_huff_decode_context_init(nghttp2_hd_huff_decode_context *ctx) { + ctx->state = 0; + ctx->accept = 1; +} + +/* Use macro to make the code simpler..., but error case is tricky. + We spent most of the CPU in decoding, so we are doing this + thing. */ +#define hd_huff_decode_sym_emit(bufs, sym, avail) \ + do { \ + if ((avail)) { \ + nghttp2_bufs_fast_addb((bufs), (sym)); \ + --(avail); \ + } else { \ + rv = nghttp2_bufs_addb((bufs), (sym)); \ + if (rv != 0) { \ + return rv; \ + } \ + (avail) = nghttp2_bufs_cur_avail((bufs)); \ + } \ + } while (0) + +ssize_t nghttp2_hd_huff_decode(nghttp2_hd_huff_decode_context *ctx, + nghttp2_bufs *bufs, const uint8_t *src, + size_t srclen, int final) { + size_t i; + int rv; + size_t avail; + + avail = nghttp2_bufs_cur_avail(bufs); + + /* We use the decoding algorithm described in + http://graphics.ics.uci.edu/pub/Prefix.pdf */ + for (i = 0; i < srclen; ++i) { + const nghttp2_huff_decode *t; + + t = &huff_decode_table[ctx->state][src[i] >> 4]; + if (t->flags & NGHTTP2_HUFF_FAIL) { + return NGHTTP2_ERR_HEADER_COMP; + } + if (t->flags & NGHTTP2_HUFF_SYM) { + /* this is macro, and may return from this function on error */ + hd_huff_decode_sym_emit(bufs, t->sym, avail); + } + + t = &huff_decode_table[t->state][src[i] & 0xf]; + if (t->flags & NGHTTP2_HUFF_FAIL) { + return NGHTTP2_ERR_HEADER_COMP; + } + if (t->flags & NGHTTP2_HUFF_SYM) { + /* this is macro, and may return from this function on error */ + hd_huff_decode_sym_emit(bufs, t->sym, avail); + } + + ctx->state = t->state; + ctx->accept = (t->flags & NGHTTP2_HUFF_ACCEPTED) != 0; + } + if (final && !ctx->accept) { + return NGHTTP2_ERR_HEADER_COMP; + } + return (ssize_t)i; +} diff --git a/lib/nghttp2_hd_huffman.h b/lib/nghttp2_hd_huffman.h new file mode 100644 index 0000000..714b6b6 --- /dev/null +++ b/lib/nghttp2_hd_huffman.h @@ -0,0 +1,74 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2013 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGHTTP2_HD_HUFFMAN_H +#define NGHTTP2_HD_HUFFMAN_H + +#ifdef HAVE_CONFIG_H +#include +#endif /* HAVE_CONFIG_H */ + +#include + +typedef enum { + /* FSA accepts this state as the end of huffman encoding + sequence. */ + NGHTTP2_HUFF_ACCEPTED = 1, + /* This state emits symbol */ + NGHTTP2_HUFF_SYM = (1 << 1), + /* If state machine reaches this state, decoding fails. */ + NGHTTP2_HUFF_FAIL = (1 << 2) +} nghttp2_huff_decode_flag; + +typedef struct { + /* huffman decoding state, which is actually the node ID of internal + huffman tree. We have 257 leaf nodes, but they are identical to + root node other than emitting a symbol, so we have 256 internal + nodes [1..255], inclusive. */ + uint8_t state; + /* bitwise OR of zero or more of the nghttp2_huff_decode_flag */ + uint8_t flags; + /* symbol if NGHTTP2_HUFF_SYM flag set */ + uint8_t sym; +} nghttp2_huff_decode; + +typedef nghttp2_huff_decode huff_decode_table_type[16]; + +typedef struct { + /* Current huffman decoding state. We stripped leaf nodes, so the + value range is [0..255], inclusive. */ + uint8_t state; + /* nonzero if we can say that the decoding process succeeds at this + state */ + uint8_t accept; +} nghttp2_hd_huff_decode_context; + +typedef struct { + /* The number of bits in this code */ + uint32_t nbits; + /* Huffman code aligned to LSB */ + uint32_t code; +} nghttp2_huff_sym; + +#endif /* NGHTTP2_HD_HUFFMAN_H */ diff --git a/lib/nghttp2_hd_huffman_data.c b/lib/nghttp2_hd_huffman_data.c new file mode 100644 index 0000000..4a4251b --- /dev/null +++ b/lib/nghttp2_hd_huffman_data.c @@ -0,0 +1,5152 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2013 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "nghttp2_hd_huffman.h" + +/* Generated by mkhufftbl.py */ + +const nghttp2_huff_sym huff_sym_table[] = {{13, 0x1ff8u}, + {23, 0x7fffd8u}, + {28, 0xfffffe2u}, + {28, 0xfffffe3u}, + {28, 0xfffffe4u}, + {28, 0xfffffe5u}, + {28, 0xfffffe6u}, + {28, 0xfffffe7u}, + {28, 0xfffffe8u}, + {24, 0xffffeau}, + {30, 0x3ffffffcu}, + {28, 0xfffffe9u}, + {28, 0xfffffeau}, + {30, 0x3ffffffdu}, + {28, 0xfffffebu}, + {28, 0xfffffecu}, + {28, 0xfffffedu}, + {28, 0xfffffeeu}, + {28, 0xfffffefu}, + {28, 0xffffff0u}, + {28, 0xffffff1u}, + {28, 0xffffff2u}, + {30, 0x3ffffffeu}, + {28, 0xffffff3u}, + {28, 0xffffff4u}, + {28, 0xffffff5u}, + {28, 0xffffff6u}, + {28, 0xffffff7u}, + {28, 0xffffff8u}, + {28, 0xffffff9u}, + {28, 0xffffffau}, + {28, 0xffffffbu}, + {6, 0x14u}, + {10, 0x3f8u}, + {10, 0x3f9u}, + {12, 0xffau}, + {13, 0x1ff9u}, + {6, 0x15u}, + {8, 0xf8u}, + {11, 0x7fau}, + {10, 0x3fau}, + {10, 0x3fbu}, + {8, 0xf9u}, + {11, 0x7fbu}, + {8, 0xfau}, + {6, 0x16u}, + {6, 0x17u}, + {6, 0x18u}, + {5, 0x0u}, + {5, 0x1u}, + {5, 0x2u}, + {6, 0x19u}, + {6, 0x1au}, + {6, 0x1bu}, + {6, 0x1cu}, + {6, 0x1du}, + {6, 0x1eu}, + {6, 0x1fu}, + {7, 0x5cu}, + {8, 0xfbu}, + {15, 0x7ffcu}, + {6, 0x20u}, + {12, 0xffbu}, + {10, 0x3fcu}, + {13, 0x1ffau}, + {6, 0x21u}, + {7, 0x5du}, + {7, 0x5eu}, + {7, 0x5fu}, + {7, 0x60u}, + {7, 0x61u}, + {7, 0x62u}, + {7, 0x63u}, + {7, 0x64u}, + {7, 0x65u}, + {7, 0x66u}, + {7, 0x67u}, + {7, 0x68u}, + {7, 0x69u}, + {7, 0x6au}, + {7, 0x6bu}, + {7, 0x6cu}, + {7, 0x6du}, + {7, 0x6eu}, + {7, 0x6fu}, + {7, 0x70u}, + {7, 0x71u}, + {7, 0x72u}, + {8, 0xfcu}, + {7, 0x73u}, + {8, 0xfdu}, + {13, 0x1ffbu}, + {19, 0x7fff0u}, + {13, 0x1ffcu}, + {14, 0x3ffcu}, + {6, 0x22u}, + {15, 0x7ffdu}, + {5, 0x3u}, + {6, 0x23u}, + {5, 0x4u}, + {6, 0x24u}, + {5, 0x5u}, + {6, 0x25u}, + {6, 0x26u}, + {6, 0x27u}, + {5, 0x6u}, + {7, 0x74u}, + {7, 0x75u}, + {6, 0x28u}, + {6, 0x29u}, + {6, 0x2au}, + {5, 0x7u}, + {6, 0x2bu}, + {7, 0x76u}, + {6, 0x2cu}, + {5, 0x8u}, + {5, 0x9u}, + {6, 0x2du}, + {7, 0x77u}, + {7, 0x78u}, + {7, 0x79u}, + {7, 0x7au}, + {7, 0x7bu}, + {15, 0x7ffeu}, + {11, 0x7fcu}, + {14, 0x3ffdu}, + {13, 0x1ffdu}, + {28, 0xffffffcu}, + {20, 0xfffe6u}, + {22, 0x3fffd2u}, + {20, 0xfffe7u}, + {20, 0xfffe8u}, + {22, 0x3fffd3u}, + {22, 0x3fffd4u}, + {22, 0x3fffd5u}, + {23, 0x7fffd9u}, + {22, 0x3fffd6u}, + {23, 0x7fffdau}, + {23, 0x7fffdbu}, + {23, 0x7fffdcu}, + {23, 0x7fffddu}, + {23, 0x7fffdeu}, + {24, 0xffffebu}, + {23, 0x7fffdfu}, + {24, 0xffffecu}, + {24, 0xffffedu}, + {22, 0x3fffd7u}, + {23, 0x7fffe0u}, + {24, 0xffffeeu}, + {23, 0x7fffe1u}, + {23, 0x7fffe2u}, + {23, 0x7fffe3u}, + {23, 0x7fffe4u}, + {21, 0x1fffdcu}, + {22, 0x3fffd8u}, + {23, 0x7fffe5u}, + {22, 0x3fffd9u}, + {23, 0x7fffe6u}, + {23, 0x7fffe7u}, + {24, 0xffffefu}, + {22, 0x3fffdau}, + {21, 0x1fffddu}, + {20, 0xfffe9u}, + {22, 0x3fffdbu}, + {22, 0x3fffdcu}, + {23, 0x7fffe8u}, + {23, 0x7fffe9u}, + {21, 0x1fffdeu}, + {23, 0x7fffeau}, + {22, 0x3fffddu}, + {22, 0x3fffdeu}, + {24, 0xfffff0u}, + {21, 0x1fffdfu}, + {22, 0x3fffdfu}, + {23, 0x7fffebu}, + {23, 0x7fffecu}, + {21, 0x1fffe0u}, + {21, 0x1fffe1u}, + {22, 0x3fffe0u}, + {21, 0x1fffe2u}, + {23, 0x7fffedu}, + {22, 0x3fffe1u}, + {23, 0x7fffeeu}, + {23, 0x7fffefu}, + {20, 0xfffeau}, + {22, 0x3fffe2u}, + {22, 0x3fffe3u}, + {22, 0x3fffe4u}, + {23, 0x7ffff0u}, + {22, 0x3fffe5u}, + {22, 0x3fffe6u}, + {23, 0x7ffff1u}, + {26, 0x3ffffe0u}, + {26, 0x3ffffe1u}, + {20, 0xfffebu}, + {19, 0x7fff1u}, + {22, 0x3fffe7u}, + {23, 0x7ffff2u}, + {22, 0x3fffe8u}, + {25, 0x1ffffecu}, + {26, 0x3ffffe2u}, + {26, 0x3ffffe3u}, + {26, 0x3ffffe4u}, + {27, 0x7ffffdeu}, + {27, 0x7ffffdfu}, + {26, 0x3ffffe5u}, + {24, 0xfffff1u}, + {25, 0x1ffffedu}, + {19, 0x7fff2u}, + {21, 0x1fffe3u}, + {26, 0x3ffffe6u}, + {27, 0x7ffffe0u}, + {27, 0x7ffffe1u}, + {26, 0x3ffffe7u}, + {27, 0x7ffffe2u}, + {24, 0xfffff2u}, + {21, 0x1fffe4u}, + {21, 0x1fffe5u}, + {26, 0x3ffffe8u}, + {26, 0x3ffffe9u}, + {28, 0xffffffdu}, + {27, 0x7ffffe3u}, + {27, 0x7ffffe4u}, + {27, 0x7ffffe5u}, + {20, 0xfffecu}, + {24, 0xfffff3u}, + {20, 0xfffedu}, + {21, 0x1fffe6u}, + {22, 0x3fffe9u}, + {21, 0x1fffe7u}, + {21, 0x1fffe8u}, + {23, 0x7ffff3u}, + {22, 0x3fffeau}, + {22, 0x3fffebu}, + {25, 0x1ffffeeu}, + {25, 0x1ffffefu}, + {24, 0xfffff4u}, + {24, 0xfffff5u}, + {26, 0x3ffffeau}, + {23, 0x7ffff4u}, + {26, 0x3ffffebu}, + {27, 0x7ffffe6u}, + {26, 0x3ffffecu}, + {26, 0x3ffffedu}, + {27, 0x7ffffe7u}, + {27, 0x7ffffe8u}, + {27, 0x7ffffe9u}, + {27, 0x7ffffeau}, + {27, 0x7ffffebu}, + {28, 0xffffffeu}, + {27, 0x7ffffecu}, + {27, 0x7ffffedu}, + {27, 0x7ffffeeu}, + {27, 0x7ffffefu}, + {27, 0x7fffff0u}, + {26, 0x3ffffeeu}, + {30, 0x3fffffffu}}; + +const nghttp2_huff_decode huff_decode_table[][16] = { + /* 0 */ + { + {4, 0x00, 0}, + {5, 0x00, 0}, + {7, 0x00, 0}, + {8, 0x00, 0}, + {11, 0x00, 0}, + {12, 0x00, 0}, + {16, 0x00, 0}, + {19, 0x00, 0}, + {25, 0x00, 0}, + {28, 0x00, 0}, + {32, 0x00, 0}, + {35, 0x00, 0}, + {42, 0x00, 0}, + {49, 0x00, 0}, + {57, 0x00, 0}, + {64, 0x01, 0}, + }, + /* 1 */ + { + {0, 0x03, 48}, + {0, 0x03, 49}, + {0, 0x03, 50}, + {0, 0x03, 97}, + {0, 0x03, 99}, + {0, 0x03, 101}, + {0, 0x03, 105}, + {0, 0x03, 111}, + {0, 0x03, 115}, + {0, 0x03, 116}, + {13, 0x00, 0}, + {14, 0x00, 0}, + {17, 0x00, 0}, + {18, 0x00, 0}, + {20, 0x00, 0}, + {21, 0x00, 0}, + }, + /* 2 */ + { + {1, 0x02, 48}, + {22, 0x03, 48}, + {1, 0x02, 49}, + {22, 0x03, 49}, + {1, 0x02, 50}, + {22, 0x03, 50}, + {1, 0x02, 97}, + {22, 0x03, 97}, + {1, 0x02, 99}, + {22, 0x03, 99}, + {1, 0x02, 101}, + {22, 0x03, 101}, + {1, 0x02, 105}, + {22, 0x03, 105}, + {1, 0x02, 111}, + {22, 0x03, 111}, + }, + /* 3 */ + { + {2, 0x02, 48}, + {9, 0x02, 48}, + {23, 0x02, 48}, + {40, 0x03, 48}, + {2, 0x02, 49}, + {9, 0x02, 49}, + {23, 0x02, 49}, + {40, 0x03, 49}, + {2, 0x02, 50}, + {9, 0x02, 50}, + {23, 0x02, 50}, + {40, 0x03, 50}, + {2, 0x02, 97}, + {9, 0x02, 97}, + {23, 0x02, 97}, + {40, 0x03, 97}, + }, + /* 4 */ + { + {3, 0x02, 48}, + {6, 0x02, 48}, + {10, 0x02, 48}, + {15, 0x02, 48}, + {24, 0x02, 48}, + {31, 0x02, 48}, + {41, 0x02, 48}, + {56, 0x03, 48}, + {3, 0x02, 49}, + {6, 0x02, 49}, + {10, 0x02, 49}, + {15, 0x02, 49}, + {24, 0x02, 49}, + {31, 0x02, 49}, + {41, 0x02, 49}, + {56, 0x03, 49}, + }, + /* 5 */ + { + {3, 0x02, 50}, + {6, 0x02, 50}, + {10, 0x02, 50}, + {15, 0x02, 50}, + {24, 0x02, 50}, + {31, 0x02, 50}, + {41, 0x02, 50}, + {56, 0x03, 50}, + {3, 0x02, 97}, + {6, 0x02, 97}, + {10, 0x02, 97}, + {15, 0x02, 97}, + {24, 0x02, 97}, + {31, 0x02, 97}, + {41, 0x02, 97}, + {56, 0x03, 97}, + }, + /* 6 */ + { + {2, 0x02, 99}, + {9, 0x02, 99}, + {23, 0x02, 99}, + {40, 0x03, 99}, + {2, 0x02, 101}, + {9, 0x02, 101}, + {23, 0x02, 101}, + {40, 0x03, 101}, + {2, 0x02, 105}, + {9, 0x02, 105}, + {23, 0x02, 105}, + {40, 0x03, 105}, + {2, 0x02, 111}, + {9, 0x02, 111}, + {23, 0x02, 111}, + {40, 0x03, 111}, + }, + /* 7 */ + { + {3, 0x02, 99}, + {6, 0x02, 99}, + {10, 0x02, 99}, + {15, 0x02, 99}, + {24, 0x02, 99}, + {31, 0x02, 99}, + {41, 0x02, 99}, + {56, 0x03, 99}, + {3, 0x02, 101}, + {6, 0x02, 101}, + {10, 0x02, 101}, + {15, 0x02, 101}, + {24, 0x02, 101}, + {31, 0x02, 101}, + {41, 0x02, 101}, + {56, 0x03, 101}, + }, + /* 8 */ + { + {3, 0x02, 105}, + {6, 0x02, 105}, + {10, 0x02, 105}, + {15, 0x02, 105}, + {24, 0x02, 105}, + {31, 0x02, 105}, + {41, 0x02, 105}, + {56, 0x03, 105}, + {3, 0x02, 111}, + {6, 0x02, 111}, + {10, 0x02, 111}, + {15, 0x02, 111}, + {24, 0x02, 111}, + {31, 0x02, 111}, + {41, 0x02, 111}, + {56, 0x03, 111}, + }, + /* 9 */ + { + {1, 0x02, 115}, + {22, 0x03, 115}, + {1, 0x02, 116}, + {22, 0x03, 116}, + {0, 0x03, 32}, + {0, 0x03, 37}, + {0, 0x03, 45}, + {0, 0x03, 46}, + {0, 0x03, 47}, + {0, 0x03, 51}, + {0, 0x03, 52}, + {0, 0x03, 53}, + {0, 0x03, 54}, + {0, 0x03, 55}, + {0, 0x03, 56}, + {0, 0x03, 57}, + }, + /* 10 */ + { + {2, 0x02, 115}, + {9, 0x02, 115}, + {23, 0x02, 115}, + {40, 0x03, 115}, + {2, 0x02, 116}, + {9, 0x02, 116}, + {23, 0x02, 116}, + {40, 0x03, 116}, + {1, 0x02, 32}, + {22, 0x03, 32}, + {1, 0x02, 37}, + {22, 0x03, 37}, + {1, 0x02, 45}, + {22, 0x03, 45}, + {1, 0x02, 46}, + {22, 0x03, 46}, + }, + /* 11 */ + { + {3, 0x02, 115}, + {6, 0x02, 115}, + {10, 0x02, 115}, + {15, 0x02, 115}, + {24, 0x02, 115}, + {31, 0x02, 115}, + {41, 0x02, 115}, + {56, 0x03, 115}, + {3, 0x02, 116}, + {6, 0x02, 116}, + {10, 0x02, 116}, + {15, 0x02, 116}, + {24, 0x02, 116}, + {31, 0x02, 116}, + {41, 0x02, 116}, + {56, 0x03, 116}, + }, + /* 12 */ + { + {2, 0x02, 32}, + {9, 0x02, 32}, + {23, 0x02, 32}, + {40, 0x03, 32}, + {2, 0x02, 37}, + {9, 0x02, 37}, + {23, 0x02, 37}, + {40, 0x03, 37}, + {2, 0x02, 45}, + {9, 0x02, 45}, + {23, 0x02, 45}, + {40, 0x03, 45}, + {2, 0x02, 46}, + {9, 0x02, 46}, + {23, 0x02, 46}, + {40, 0x03, 46}, + }, + /* 13 */ + { + {3, 0x02, 32}, + {6, 0x02, 32}, + {10, 0x02, 32}, + {15, 0x02, 32}, + {24, 0x02, 32}, + {31, 0x02, 32}, + {41, 0x02, 32}, + {56, 0x03, 32}, + {3, 0x02, 37}, + {6, 0x02, 37}, + {10, 0x02, 37}, + {15, 0x02, 37}, + {24, 0x02, 37}, + {31, 0x02, 37}, + {41, 0x02, 37}, + {56, 0x03, 37}, + }, + /* 14 */ + { + {3, 0x02, 45}, + {6, 0x02, 45}, + {10, 0x02, 45}, + {15, 0x02, 45}, + {24, 0x02, 45}, + {31, 0x02, 45}, + {41, 0x02, 45}, + {56, 0x03, 45}, + {3, 0x02, 46}, + {6, 0x02, 46}, + {10, 0x02, 46}, + {15, 0x02, 46}, + {24, 0x02, 46}, + {31, 0x02, 46}, + {41, 0x02, 46}, + {56, 0x03, 46}, + }, + /* 15 */ + { + {1, 0x02, 47}, + {22, 0x03, 47}, + {1, 0x02, 51}, + {22, 0x03, 51}, + {1, 0x02, 52}, + {22, 0x03, 52}, + {1, 0x02, 53}, + {22, 0x03, 53}, + {1, 0x02, 54}, + {22, 0x03, 54}, + {1, 0x02, 55}, + {22, 0x03, 55}, + {1, 0x02, 56}, + {22, 0x03, 56}, + {1, 0x02, 57}, + {22, 0x03, 57}, + }, + /* 16 */ + { + {2, 0x02, 47}, + {9, 0x02, 47}, + {23, 0x02, 47}, + {40, 0x03, 47}, + {2, 0x02, 51}, + {9, 0x02, 51}, + {23, 0x02, 51}, + {40, 0x03, 51}, + {2, 0x02, 52}, + {9, 0x02, 52}, + {23, 0x02, 52}, + {40, 0x03, 52}, + {2, 0x02, 53}, + {9, 0x02, 53}, + {23, 0x02, 53}, + {40, 0x03, 53}, + }, + /* 17 */ + { + {3, 0x02, 47}, + {6, 0x02, 47}, + {10, 0x02, 47}, + {15, 0x02, 47}, + {24, 0x02, 47}, + {31, 0x02, 47}, + {41, 0x02, 47}, + {56, 0x03, 47}, + {3, 0x02, 51}, + {6, 0x02, 51}, + {10, 0x02, 51}, + {15, 0x02, 51}, + {24, 0x02, 51}, + {31, 0x02, 51}, + {41, 0x02, 51}, + {56, 0x03, 51}, + }, + /* 18 */ + { + {3, 0x02, 52}, + {6, 0x02, 52}, + {10, 0x02, 52}, + {15, 0x02, 52}, + {24, 0x02, 52}, + {31, 0x02, 52}, + {41, 0x02, 52}, + {56, 0x03, 52}, + {3, 0x02, 53}, + {6, 0x02, 53}, + {10, 0x02, 53}, + {15, 0x02, 53}, + {24, 0x02, 53}, + {31, 0x02, 53}, + {41, 0x02, 53}, + {56, 0x03, 53}, + }, + /* 19 */ + { + {2, 0x02, 54}, + {9, 0x02, 54}, + {23, 0x02, 54}, + {40, 0x03, 54}, + {2, 0x02, 55}, + {9, 0x02, 55}, + {23, 0x02, 55}, + {40, 0x03, 55}, + {2, 0x02, 56}, + {9, 0x02, 56}, + {23, 0x02, 56}, + {40, 0x03, 56}, + {2, 0x02, 57}, + {9, 0x02, 57}, + {23, 0x02, 57}, + {40, 0x03, 57}, + }, + /* 20 */ + { + {3, 0x02, 54}, + {6, 0x02, 54}, + {10, 0x02, 54}, + {15, 0x02, 54}, + {24, 0x02, 54}, + {31, 0x02, 54}, + {41, 0x02, 54}, + {56, 0x03, 54}, + {3, 0x02, 55}, + {6, 0x02, 55}, + {10, 0x02, 55}, + {15, 0x02, 55}, + {24, 0x02, 55}, + {31, 0x02, 55}, + {41, 0x02, 55}, + {56, 0x03, 55}, + }, + /* 21 */ + { + {3, 0x02, 56}, + {6, 0x02, 56}, + {10, 0x02, 56}, + {15, 0x02, 56}, + {24, 0x02, 56}, + {31, 0x02, 56}, + {41, 0x02, 56}, + {56, 0x03, 56}, + {3, 0x02, 57}, + {6, 0x02, 57}, + {10, 0x02, 57}, + {15, 0x02, 57}, + {24, 0x02, 57}, + {31, 0x02, 57}, + {41, 0x02, 57}, + {56, 0x03, 57}, + }, + /* 22 */ + { + {26, 0x00, 0}, + {27, 0x00, 0}, + {29, 0x00, 0}, + {30, 0x00, 0}, + {33, 0x00, 0}, + {34, 0x00, 0}, + {36, 0x00, 0}, + {37, 0x00, 0}, + {43, 0x00, 0}, + {46, 0x00, 0}, + {50, 0x00, 0}, + {53, 0x00, 0}, + {58, 0x00, 0}, + {61, 0x00, 0}, + {65, 0x00, 0}, + {68, 0x01, 0}, + }, + /* 23 */ + { + {0, 0x03, 61}, + {0, 0x03, 65}, + {0, 0x03, 95}, + {0, 0x03, 98}, + {0, 0x03, 100}, + {0, 0x03, 102}, + {0, 0x03, 103}, + {0, 0x03, 104}, + {0, 0x03, 108}, + {0, 0x03, 109}, + {0, 0x03, 110}, + {0, 0x03, 112}, + {0, 0x03, 114}, + {0, 0x03, 117}, + {38, 0x00, 0}, + {39, 0x00, 0}, + }, + /* 24 */ + { + {1, 0x02, 61}, + {22, 0x03, 61}, + {1, 0x02, 65}, + {22, 0x03, 65}, + {1, 0x02, 95}, + {22, 0x03, 95}, + {1, 0x02, 98}, + {22, 0x03, 98}, + {1, 0x02, 100}, + {22, 0x03, 100}, + {1, 0x02, 102}, + {22, 0x03, 102}, + {1, 0x02, 103}, + {22, 0x03, 103}, + {1, 0x02, 104}, + {22, 0x03, 104}, + }, + /* 25 */ + { + {2, 0x02, 61}, + {9, 0x02, 61}, + {23, 0x02, 61}, + {40, 0x03, 61}, + {2, 0x02, 65}, + {9, 0x02, 65}, + {23, 0x02, 65}, + {40, 0x03, 65}, + {2, 0x02, 95}, + {9, 0x02, 95}, + {23, 0x02, 95}, + {40, 0x03, 95}, + {2, 0x02, 98}, + {9, 0x02, 98}, + {23, 0x02, 98}, + {40, 0x03, 98}, + }, + /* 26 */ + { + {3, 0x02, 61}, + {6, 0x02, 61}, + {10, 0x02, 61}, + {15, 0x02, 61}, + {24, 0x02, 61}, + {31, 0x02, 61}, + {41, 0x02, 61}, + {56, 0x03, 61}, + {3, 0x02, 65}, + {6, 0x02, 65}, + {10, 0x02, 65}, + {15, 0x02, 65}, + {24, 0x02, 65}, + {31, 0x02, 65}, + {41, 0x02, 65}, + {56, 0x03, 65}, + }, + /* 27 */ + { + {3, 0x02, 95}, + {6, 0x02, 95}, + {10, 0x02, 95}, + {15, 0x02, 95}, + {24, 0x02, 95}, + {31, 0x02, 95}, + {41, 0x02, 95}, + {56, 0x03, 95}, + {3, 0x02, 98}, + {6, 0x02, 98}, + {10, 0x02, 98}, + {15, 0x02, 98}, + {24, 0x02, 98}, + {31, 0x02, 98}, + {41, 0x02, 98}, + {56, 0x03, 98}, + }, + /* 28 */ + { + {2, 0x02, 100}, + {9, 0x02, 100}, + {23, 0x02, 100}, + {40, 0x03, 100}, + {2, 0x02, 102}, + {9, 0x02, 102}, + {23, 0x02, 102}, + {40, 0x03, 102}, + {2, 0x02, 103}, + {9, 0x02, 103}, + {23, 0x02, 103}, + {40, 0x03, 103}, + {2, 0x02, 104}, + {9, 0x02, 104}, + {23, 0x02, 104}, + {40, 0x03, 104}, + }, + /* 29 */ + { + {3, 0x02, 100}, + {6, 0x02, 100}, + {10, 0x02, 100}, + {15, 0x02, 100}, + {24, 0x02, 100}, + {31, 0x02, 100}, + {41, 0x02, 100}, + {56, 0x03, 100}, + {3, 0x02, 102}, + {6, 0x02, 102}, + {10, 0x02, 102}, + {15, 0x02, 102}, + {24, 0x02, 102}, + {31, 0x02, 102}, + {41, 0x02, 102}, + {56, 0x03, 102}, + }, + /* 30 */ + { + {3, 0x02, 103}, + {6, 0x02, 103}, + {10, 0x02, 103}, + {15, 0x02, 103}, + {24, 0x02, 103}, + {31, 0x02, 103}, + {41, 0x02, 103}, + {56, 0x03, 103}, + {3, 0x02, 104}, + {6, 0x02, 104}, + {10, 0x02, 104}, + {15, 0x02, 104}, + {24, 0x02, 104}, + {31, 0x02, 104}, + {41, 0x02, 104}, + {56, 0x03, 104}, + }, + /* 31 */ + { + {1, 0x02, 108}, + {22, 0x03, 108}, + {1, 0x02, 109}, + {22, 0x03, 109}, + {1, 0x02, 110}, + {22, 0x03, 110}, + {1, 0x02, 112}, + {22, 0x03, 112}, + {1, 0x02, 114}, + {22, 0x03, 114}, + {1, 0x02, 117}, + {22, 0x03, 117}, + {0, 0x03, 58}, + {0, 0x03, 66}, + {0, 0x03, 67}, + {0, 0x03, 68}, + }, + /* 32 */ + { + {2, 0x02, 108}, + {9, 0x02, 108}, + {23, 0x02, 108}, + {40, 0x03, 108}, + {2, 0x02, 109}, + {9, 0x02, 109}, + {23, 0x02, 109}, + {40, 0x03, 109}, + {2, 0x02, 110}, + {9, 0x02, 110}, + {23, 0x02, 110}, + {40, 0x03, 110}, + {2, 0x02, 112}, + {9, 0x02, 112}, + {23, 0x02, 112}, + {40, 0x03, 112}, + }, + /* 33 */ + { + {3, 0x02, 108}, + {6, 0x02, 108}, + {10, 0x02, 108}, + {15, 0x02, 108}, + {24, 0x02, 108}, + {31, 0x02, 108}, + {41, 0x02, 108}, + {56, 0x03, 108}, + {3, 0x02, 109}, + {6, 0x02, 109}, + {10, 0x02, 109}, + {15, 0x02, 109}, + {24, 0x02, 109}, + {31, 0x02, 109}, + {41, 0x02, 109}, + {56, 0x03, 109}, + }, + /* 34 */ + { + {3, 0x02, 110}, + {6, 0x02, 110}, + {10, 0x02, 110}, + {15, 0x02, 110}, + {24, 0x02, 110}, + {31, 0x02, 110}, + {41, 0x02, 110}, + {56, 0x03, 110}, + {3, 0x02, 112}, + {6, 0x02, 112}, + {10, 0x02, 112}, + {15, 0x02, 112}, + {24, 0x02, 112}, + {31, 0x02, 112}, + {41, 0x02, 112}, + {56, 0x03, 112}, + }, + /* 35 */ + { + {2, 0x02, 114}, + {9, 0x02, 114}, + {23, 0x02, 114}, + {40, 0x03, 114}, + {2, 0x02, 117}, + {9, 0x02, 117}, + {23, 0x02, 117}, + {40, 0x03, 117}, + {1, 0x02, 58}, + {22, 0x03, 58}, + {1, 0x02, 66}, + {22, 0x03, 66}, + {1, 0x02, 67}, + {22, 0x03, 67}, + {1, 0x02, 68}, + {22, 0x03, 68}, + }, + /* 36 */ + { + {3, 0x02, 114}, + {6, 0x02, 114}, + {10, 0x02, 114}, + {15, 0x02, 114}, + {24, 0x02, 114}, + {31, 0x02, 114}, + {41, 0x02, 114}, + {56, 0x03, 114}, + {3, 0x02, 117}, + {6, 0x02, 117}, + {10, 0x02, 117}, + {15, 0x02, 117}, + {24, 0x02, 117}, + {31, 0x02, 117}, + {41, 0x02, 117}, + {56, 0x03, 117}, + }, + /* 37 */ + { + {2, 0x02, 58}, + {9, 0x02, 58}, + {23, 0x02, 58}, + {40, 0x03, 58}, + {2, 0x02, 66}, + {9, 0x02, 66}, + {23, 0x02, 66}, + {40, 0x03, 66}, + {2, 0x02, 67}, + {9, 0x02, 67}, + {23, 0x02, 67}, + {40, 0x03, 67}, + {2, 0x02, 68}, + {9, 0x02, 68}, + {23, 0x02, 68}, + {40, 0x03, 68}, + }, + /* 38 */ + { + {3, 0x02, 58}, + {6, 0x02, 58}, + {10, 0x02, 58}, + {15, 0x02, 58}, + {24, 0x02, 58}, + {31, 0x02, 58}, + {41, 0x02, 58}, + {56, 0x03, 58}, + {3, 0x02, 66}, + {6, 0x02, 66}, + {10, 0x02, 66}, + {15, 0x02, 66}, + {24, 0x02, 66}, + {31, 0x02, 66}, + {41, 0x02, 66}, + {56, 0x03, 66}, + }, + /* 39 */ + { + {3, 0x02, 67}, + {6, 0x02, 67}, + {10, 0x02, 67}, + {15, 0x02, 67}, + {24, 0x02, 67}, + {31, 0x02, 67}, + {41, 0x02, 67}, + {56, 0x03, 67}, + {3, 0x02, 68}, + {6, 0x02, 68}, + {10, 0x02, 68}, + {15, 0x02, 68}, + {24, 0x02, 68}, + {31, 0x02, 68}, + {41, 0x02, 68}, + {56, 0x03, 68}, + }, + /* 40 */ + { + {44, 0x00, 0}, + {45, 0x00, 0}, + {47, 0x00, 0}, + {48, 0x00, 0}, + {51, 0x00, 0}, + {52, 0x00, 0}, + {54, 0x00, 0}, + {55, 0x00, 0}, + {59, 0x00, 0}, + {60, 0x00, 0}, + {62, 0x00, 0}, + {63, 0x00, 0}, + {66, 0x00, 0}, + {67, 0x00, 0}, + {69, 0x00, 0}, + {72, 0x01, 0}, + }, + /* 41 */ + { + {0, 0x03, 69}, + {0, 0x03, 70}, + {0, 0x03, 71}, + {0, 0x03, 72}, + {0, 0x03, 73}, + {0, 0x03, 74}, + {0, 0x03, 75}, + {0, 0x03, 76}, + {0, 0x03, 77}, + {0, 0x03, 78}, + {0, 0x03, 79}, + {0, 0x03, 80}, + {0, 0x03, 81}, + {0, 0x03, 82}, + {0, 0x03, 83}, + {0, 0x03, 84}, + }, + /* 42 */ + { + {1, 0x02, 69}, + {22, 0x03, 69}, + {1, 0x02, 70}, + {22, 0x03, 70}, + {1, 0x02, 71}, + {22, 0x03, 71}, + {1, 0x02, 72}, + {22, 0x03, 72}, + {1, 0x02, 73}, + {22, 0x03, 73}, + {1, 0x02, 74}, + {22, 0x03, 74}, + {1, 0x02, 75}, + {22, 0x03, 75}, + {1, 0x02, 76}, + {22, 0x03, 76}, + }, + /* 43 */ + { + {2, 0x02, 69}, + {9, 0x02, 69}, + {23, 0x02, 69}, + {40, 0x03, 69}, + {2, 0x02, 70}, + {9, 0x02, 70}, + {23, 0x02, 70}, + {40, 0x03, 70}, + {2, 0x02, 71}, + {9, 0x02, 71}, + {23, 0x02, 71}, + {40, 0x03, 71}, + {2, 0x02, 72}, + {9, 0x02, 72}, + {23, 0x02, 72}, + {40, 0x03, 72}, + }, + /* 44 */ + { + {3, 0x02, 69}, + {6, 0x02, 69}, + {10, 0x02, 69}, + {15, 0x02, 69}, + {24, 0x02, 69}, + {31, 0x02, 69}, + {41, 0x02, 69}, + {56, 0x03, 69}, + {3, 0x02, 70}, + {6, 0x02, 70}, + {10, 0x02, 70}, + {15, 0x02, 70}, + {24, 0x02, 70}, + {31, 0x02, 70}, + {41, 0x02, 70}, + {56, 0x03, 70}, + }, + /* 45 */ + { + {3, 0x02, 71}, + {6, 0x02, 71}, + {10, 0x02, 71}, + {15, 0x02, 71}, + {24, 0x02, 71}, + {31, 0x02, 71}, + {41, 0x02, 71}, + {56, 0x03, 71}, + {3, 0x02, 72}, + {6, 0x02, 72}, + {10, 0x02, 72}, + {15, 0x02, 72}, + {24, 0x02, 72}, + {31, 0x02, 72}, + {41, 0x02, 72}, + {56, 0x03, 72}, + }, + /* 46 */ + { + {2, 0x02, 73}, + {9, 0x02, 73}, + {23, 0x02, 73}, + {40, 0x03, 73}, + {2, 0x02, 74}, + {9, 0x02, 74}, + {23, 0x02, 74}, + {40, 0x03, 74}, + {2, 0x02, 75}, + {9, 0x02, 75}, + {23, 0x02, 75}, + {40, 0x03, 75}, + {2, 0x02, 76}, + {9, 0x02, 76}, + {23, 0x02, 76}, + {40, 0x03, 76}, + }, + /* 47 */ + { + {3, 0x02, 73}, + {6, 0x02, 73}, + {10, 0x02, 73}, + {15, 0x02, 73}, + {24, 0x02, 73}, + {31, 0x02, 73}, + {41, 0x02, 73}, + {56, 0x03, 73}, + {3, 0x02, 74}, + {6, 0x02, 74}, + {10, 0x02, 74}, + {15, 0x02, 74}, + {24, 0x02, 74}, + {31, 0x02, 74}, + {41, 0x02, 74}, + {56, 0x03, 74}, + }, + /* 48 */ + { + {3, 0x02, 75}, + {6, 0x02, 75}, + {10, 0x02, 75}, + {15, 0x02, 75}, + {24, 0x02, 75}, + {31, 0x02, 75}, + {41, 0x02, 75}, + {56, 0x03, 75}, + {3, 0x02, 76}, + {6, 0x02, 76}, + {10, 0x02, 76}, + {15, 0x02, 76}, + {24, 0x02, 76}, + {31, 0x02, 76}, + {41, 0x02, 76}, + {56, 0x03, 76}, + }, + /* 49 */ + { + {1, 0x02, 77}, + {22, 0x03, 77}, + {1, 0x02, 78}, + {22, 0x03, 78}, + {1, 0x02, 79}, + {22, 0x03, 79}, + {1, 0x02, 80}, + {22, 0x03, 80}, + {1, 0x02, 81}, + {22, 0x03, 81}, + {1, 0x02, 82}, + {22, 0x03, 82}, + {1, 0x02, 83}, + {22, 0x03, 83}, + {1, 0x02, 84}, + {22, 0x03, 84}, + }, + /* 50 */ + { + {2, 0x02, 77}, + {9, 0x02, 77}, + {23, 0x02, 77}, + {40, 0x03, 77}, + {2, 0x02, 78}, + {9, 0x02, 78}, + {23, 0x02, 78}, + {40, 0x03, 78}, + {2, 0x02, 79}, + {9, 0x02, 79}, + {23, 0x02, 79}, + {40, 0x03, 79}, + {2, 0x02, 80}, + {9, 0x02, 80}, + {23, 0x02, 80}, + {40, 0x03, 80}, + }, + /* 51 */ + { + {3, 0x02, 77}, + {6, 0x02, 77}, + {10, 0x02, 77}, + {15, 0x02, 77}, + {24, 0x02, 77}, + {31, 0x02, 77}, + {41, 0x02, 77}, + {56, 0x03, 77}, + {3, 0x02, 78}, + {6, 0x02, 78}, + {10, 0x02, 78}, + {15, 0x02, 78}, + {24, 0x02, 78}, + {31, 0x02, 78}, + {41, 0x02, 78}, + {56, 0x03, 78}, + }, + /* 52 */ + { + {3, 0x02, 79}, + {6, 0x02, 79}, + {10, 0x02, 79}, + {15, 0x02, 79}, + {24, 0x02, 79}, + {31, 0x02, 79}, + {41, 0x02, 79}, + {56, 0x03, 79}, + {3, 0x02, 80}, + {6, 0x02, 80}, + {10, 0x02, 80}, + {15, 0x02, 80}, + {24, 0x02, 80}, + {31, 0x02, 80}, + {41, 0x02, 80}, + {56, 0x03, 80}, + }, + /* 53 */ + { + {2, 0x02, 81}, + {9, 0x02, 81}, + {23, 0x02, 81}, + {40, 0x03, 81}, + {2, 0x02, 82}, + {9, 0x02, 82}, + {23, 0x02, 82}, + {40, 0x03, 82}, + {2, 0x02, 83}, + {9, 0x02, 83}, + {23, 0x02, 83}, + {40, 0x03, 83}, + {2, 0x02, 84}, + {9, 0x02, 84}, + {23, 0x02, 84}, + {40, 0x03, 84}, + }, + /* 54 */ + { + {3, 0x02, 81}, + {6, 0x02, 81}, + {10, 0x02, 81}, + {15, 0x02, 81}, + {24, 0x02, 81}, + {31, 0x02, 81}, + {41, 0x02, 81}, + {56, 0x03, 81}, + {3, 0x02, 82}, + {6, 0x02, 82}, + {10, 0x02, 82}, + {15, 0x02, 82}, + {24, 0x02, 82}, + {31, 0x02, 82}, + {41, 0x02, 82}, + {56, 0x03, 82}, + }, + /* 55 */ + { + {3, 0x02, 83}, + {6, 0x02, 83}, + {10, 0x02, 83}, + {15, 0x02, 83}, + {24, 0x02, 83}, + {31, 0x02, 83}, + {41, 0x02, 83}, + {56, 0x03, 83}, + {3, 0x02, 84}, + {6, 0x02, 84}, + {10, 0x02, 84}, + {15, 0x02, 84}, + {24, 0x02, 84}, + {31, 0x02, 84}, + {41, 0x02, 84}, + {56, 0x03, 84}, + }, + /* 56 */ + { + {0, 0x03, 85}, + {0, 0x03, 86}, + {0, 0x03, 87}, + {0, 0x03, 89}, + {0, 0x03, 106}, + {0, 0x03, 107}, + {0, 0x03, 113}, + {0, 0x03, 118}, + {0, 0x03, 119}, + {0, 0x03, 120}, + {0, 0x03, 121}, + {0, 0x03, 122}, + {70, 0x00, 0}, + {71, 0x00, 0}, + {73, 0x00, 0}, + {74, 0x01, 0}, + }, + /* 57 */ + { + {1, 0x02, 85}, + {22, 0x03, 85}, + {1, 0x02, 86}, + {22, 0x03, 86}, + {1, 0x02, 87}, + {22, 0x03, 87}, + {1, 0x02, 89}, + {22, 0x03, 89}, + {1, 0x02, 106}, + {22, 0x03, 106}, + {1, 0x02, 107}, + {22, 0x03, 107}, + {1, 0x02, 113}, + {22, 0x03, 113}, + {1, 0x02, 118}, + {22, 0x03, 118}, + }, + /* 58 */ + { + {2, 0x02, 85}, + {9, 0x02, 85}, + {23, 0x02, 85}, + {40, 0x03, 85}, + {2, 0x02, 86}, + {9, 0x02, 86}, + {23, 0x02, 86}, + {40, 0x03, 86}, + {2, 0x02, 87}, + {9, 0x02, 87}, + {23, 0x02, 87}, + {40, 0x03, 87}, + {2, 0x02, 89}, + {9, 0x02, 89}, + {23, 0x02, 89}, + {40, 0x03, 89}, + }, + /* 59 */ + { + {3, 0x02, 85}, + {6, 0x02, 85}, + {10, 0x02, 85}, + {15, 0x02, 85}, + {24, 0x02, 85}, + {31, 0x02, 85}, + {41, 0x02, 85}, + {56, 0x03, 85}, + {3, 0x02, 86}, + {6, 0x02, 86}, + {10, 0x02, 86}, + {15, 0x02, 86}, + {24, 0x02, 86}, + {31, 0x02, 86}, + {41, 0x02, 86}, + {56, 0x03, 86}, + }, + /* 60 */ + { + {3, 0x02, 87}, + {6, 0x02, 87}, + {10, 0x02, 87}, + {15, 0x02, 87}, + {24, 0x02, 87}, + {31, 0x02, 87}, + {41, 0x02, 87}, + {56, 0x03, 87}, + {3, 0x02, 89}, + {6, 0x02, 89}, + {10, 0x02, 89}, + {15, 0x02, 89}, + {24, 0x02, 89}, + {31, 0x02, 89}, + {41, 0x02, 89}, + {56, 0x03, 89}, + }, + /* 61 */ + { + {2, 0x02, 106}, + {9, 0x02, 106}, + {23, 0x02, 106}, + {40, 0x03, 106}, + {2, 0x02, 107}, + {9, 0x02, 107}, + {23, 0x02, 107}, + {40, 0x03, 107}, + {2, 0x02, 113}, + {9, 0x02, 113}, + {23, 0x02, 113}, + {40, 0x03, 113}, + {2, 0x02, 118}, + {9, 0x02, 118}, + {23, 0x02, 118}, + {40, 0x03, 118}, + }, + /* 62 */ + { + {3, 0x02, 106}, + {6, 0x02, 106}, + {10, 0x02, 106}, + {15, 0x02, 106}, + {24, 0x02, 106}, + {31, 0x02, 106}, + {41, 0x02, 106}, + {56, 0x03, 106}, + {3, 0x02, 107}, + {6, 0x02, 107}, + {10, 0x02, 107}, + {15, 0x02, 107}, + {24, 0x02, 107}, + {31, 0x02, 107}, + {41, 0x02, 107}, + {56, 0x03, 107}, + }, + /* 63 */ + { + {3, 0x02, 113}, + {6, 0x02, 113}, + {10, 0x02, 113}, + {15, 0x02, 113}, + {24, 0x02, 113}, + {31, 0x02, 113}, + {41, 0x02, 113}, + {56, 0x03, 113}, + {3, 0x02, 118}, + {6, 0x02, 118}, + {10, 0x02, 118}, + {15, 0x02, 118}, + {24, 0x02, 118}, + {31, 0x02, 118}, + {41, 0x02, 118}, + {56, 0x03, 118}, + }, + /* 64 */ + { + {1, 0x02, 119}, + {22, 0x03, 119}, + {1, 0x02, 120}, + {22, 0x03, 120}, + {1, 0x02, 121}, + {22, 0x03, 121}, + {1, 0x02, 122}, + {22, 0x03, 122}, + {0, 0x03, 38}, + {0, 0x03, 42}, + {0, 0x03, 44}, + {0, 0x03, 59}, + {0, 0x03, 88}, + {0, 0x03, 90}, + {75, 0x00, 0}, + {78, 0x00, 0}, + }, + /* 65 */ + { + {2, 0x02, 119}, + {9, 0x02, 119}, + {23, 0x02, 119}, + {40, 0x03, 119}, + {2, 0x02, 120}, + {9, 0x02, 120}, + {23, 0x02, 120}, + {40, 0x03, 120}, + {2, 0x02, 121}, + {9, 0x02, 121}, + {23, 0x02, 121}, + {40, 0x03, 121}, + {2, 0x02, 122}, + {9, 0x02, 122}, + {23, 0x02, 122}, + {40, 0x03, 122}, + }, + /* 66 */ + { + {3, 0x02, 119}, + {6, 0x02, 119}, + {10, 0x02, 119}, + {15, 0x02, 119}, + {24, 0x02, 119}, + {31, 0x02, 119}, + {41, 0x02, 119}, + {56, 0x03, 119}, + {3, 0x02, 120}, + {6, 0x02, 120}, + {10, 0x02, 120}, + {15, 0x02, 120}, + {24, 0x02, 120}, + {31, 0x02, 120}, + {41, 0x02, 120}, + {56, 0x03, 120}, + }, + /* 67 */ + { + {3, 0x02, 121}, + {6, 0x02, 121}, + {10, 0x02, 121}, + {15, 0x02, 121}, + {24, 0x02, 121}, + {31, 0x02, 121}, + {41, 0x02, 121}, + {56, 0x03, 121}, + {3, 0x02, 122}, + {6, 0x02, 122}, + {10, 0x02, 122}, + {15, 0x02, 122}, + {24, 0x02, 122}, + {31, 0x02, 122}, + {41, 0x02, 122}, + {56, 0x03, 122}, + }, + /* 68 */ + { + {1, 0x02, 38}, + {22, 0x03, 38}, + {1, 0x02, 42}, + {22, 0x03, 42}, + {1, 0x02, 44}, + {22, 0x03, 44}, + {1, 0x02, 59}, + {22, 0x03, 59}, + {1, 0x02, 88}, + {22, 0x03, 88}, + {1, 0x02, 90}, + {22, 0x03, 90}, + {76, 0x00, 0}, + {77, 0x00, 0}, + {79, 0x00, 0}, + {81, 0x00, 0}, + }, + /* 69 */ + { + {2, 0x02, 38}, + {9, 0x02, 38}, + {23, 0x02, 38}, + {40, 0x03, 38}, + {2, 0x02, 42}, + {9, 0x02, 42}, + {23, 0x02, 42}, + {40, 0x03, 42}, + {2, 0x02, 44}, + {9, 0x02, 44}, + {23, 0x02, 44}, + {40, 0x03, 44}, + {2, 0x02, 59}, + {9, 0x02, 59}, + {23, 0x02, 59}, + {40, 0x03, 59}, + }, + /* 70 */ + { + {3, 0x02, 38}, + {6, 0x02, 38}, + {10, 0x02, 38}, + {15, 0x02, 38}, + {24, 0x02, 38}, + {31, 0x02, 38}, + {41, 0x02, 38}, + {56, 0x03, 38}, + {3, 0x02, 42}, + {6, 0x02, 42}, + {10, 0x02, 42}, + {15, 0x02, 42}, + {24, 0x02, 42}, + {31, 0x02, 42}, + {41, 0x02, 42}, + {56, 0x03, 42}, + }, + /* 71 */ + { + {3, 0x02, 44}, + {6, 0x02, 44}, + {10, 0x02, 44}, + {15, 0x02, 44}, + {24, 0x02, 44}, + {31, 0x02, 44}, + {41, 0x02, 44}, + {56, 0x03, 44}, + {3, 0x02, 59}, + {6, 0x02, 59}, + {10, 0x02, 59}, + {15, 0x02, 59}, + {24, 0x02, 59}, + {31, 0x02, 59}, + {41, 0x02, 59}, + {56, 0x03, 59}, + }, + /* 72 */ + { + {2, 0x02, 88}, + {9, 0x02, 88}, + {23, 0x02, 88}, + {40, 0x03, 88}, + {2, 0x02, 90}, + {9, 0x02, 90}, + {23, 0x02, 90}, + {40, 0x03, 90}, + {0, 0x03, 33}, + {0, 0x03, 34}, + {0, 0x03, 40}, + {0, 0x03, 41}, + {0, 0x03, 63}, + {80, 0x00, 0}, + {82, 0x00, 0}, + {84, 0x00, 0}, + }, + /* 73 */ + { + {3, 0x02, 88}, + {6, 0x02, 88}, + {10, 0x02, 88}, + {15, 0x02, 88}, + {24, 0x02, 88}, + {31, 0x02, 88}, + {41, 0x02, 88}, + {56, 0x03, 88}, + {3, 0x02, 90}, + {6, 0x02, 90}, + {10, 0x02, 90}, + {15, 0x02, 90}, + {24, 0x02, 90}, + {31, 0x02, 90}, + {41, 0x02, 90}, + {56, 0x03, 90}, + }, + /* 74 */ + { + {1, 0x02, 33}, + {22, 0x03, 33}, + {1, 0x02, 34}, + {22, 0x03, 34}, + {1, 0x02, 40}, + {22, 0x03, 40}, + {1, 0x02, 41}, + {22, 0x03, 41}, + {1, 0x02, 63}, + {22, 0x03, 63}, + {0, 0x03, 39}, + {0, 0x03, 43}, + {0, 0x03, 124}, + {83, 0x00, 0}, + {85, 0x00, 0}, + {88, 0x00, 0}, + }, + /* 75 */ + { + {2, 0x02, 33}, + {9, 0x02, 33}, + {23, 0x02, 33}, + {40, 0x03, 33}, + {2, 0x02, 34}, + {9, 0x02, 34}, + {23, 0x02, 34}, + {40, 0x03, 34}, + {2, 0x02, 40}, + {9, 0x02, 40}, + {23, 0x02, 40}, + {40, 0x03, 40}, + {2, 0x02, 41}, + {9, 0x02, 41}, + {23, 0x02, 41}, + {40, 0x03, 41}, + }, + /* 76 */ + { + {3, 0x02, 33}, + {6, 0x02, 33}, + {10, 0x02, 33}, + {15, 0x02, 33}, + {24, 0x02, 33}, + {31, 0x02, 33}, + {41, 0x02, 33}, + {56, 0x03, 33}, + {3, 0x02, 34}, + {6, 0x02, 34}, + {10, 0x02, 34}, + {15, 0x02, 34}, + {24, 0x02, 34}, + {31, 0x02, 34}, + {41, 0x02, 34}, + {56, 0x03, 34}, + }, + /* 77 */ + { + {3, 0x02, 40}, + {6, 0x02, 40}, + {10, 0x02, 40}, + {15, 0x02, 40}, + {24, 0x02, 40}, + {31, 0x02, 40}, + {41, 0x02, 40}, + {56, 0x03, 40}, + {3, 0x02, 41}, + {6, 0x02, 41}, + {10, 0x02, 41}, + {15, 0x02, 41}, + {24, 0x02, 41}, + {31, 0x02, 41}, + {41, 0x02, 41}, + {56, 0x03, 41}, + }, + /* 78 */ + { + {2, 0x02, 63}, + {9, 0x02, 63}, + {23, 0x02, 63}, + {40, 0x03, 63}, + {1, 0x02, 39}, + {22, 0x03, 39}, + {1, 0x02, 43}, + {22, 0x03, 43}, + {1, 0x02, 124}, + {22, 0x03, 124}, + {0, 0x03, 35}, + {0, 0x03, 62}, + {86, 0x00, 0}, + {87, 0x00, 0}, + {89, 0x00, 0}, + {90, 0x00, 0}, + }, + /* 79 */ + { + {3, 0x02, 63}, + {6, 0x02, 63}, + {10, 0x02, 63}, + {15, 0x02, 63}, + {24, 0x02, 63}, + {31, 0x02, 63}, + {41, 0x02, 63}, + {56, 0x03, 63}, + {2, 0x02, 39}, + {9, 0x02, 39}, + {23, 0x02, 39}, + {40, 0x03, 39}, + {2, 0x02, 43}, + {9, 0x02, 43}, + {23, 0x02, 43}, + {40, 0x03, 43}, + }, + /* 80 */ + { + {3, 0x02, 39}, + {6, 0x02, 39}, + {10, 0x02, 39}, + {15, 0x02, 39}, + {24, 0x02, 39}, + {31, 0x02, 39}, + {41, 0x02, 39}, + {56, 0x03, 39}, + {3, 0x02, 43}, + {6, 0x02, 43}, + {10, 0x02, 43}, + {15, 0x02, 43}, + {24, 0x02, 43}, + {31, 0x02, 43}, + {41, 0x02, 43}, + {56, 0x03, 43}, + }, + /* 81 */ + { + {2, 0x02, 124}, + {9, 0x02, 124}, + {23, 0x02, 124}, + {40, 0x03, 124}, + {1, 0x02, 35}, + {22, 0x03, 35}, + {1, 0x02, 62}, + {22, 0x03, 62}, + {0, 0x03, 0}, + {0, 0x03, 36}, + {0, 0x03, 64}, + {0, 0x03, 91}, + {0, 0x03, 93}, + {0, 0x03, 126}, + {91, 0x00, 0}, + {92, 0x00, 0}, + }, + /* 82 */ + { + {3, 0x02, 124}, + {6, 0x02, 124}, + {10, 0x02, 124}, + {15, 0x02, 124}, + {24, 0x02, 124}, + {31, 0x02, 124}, + {41, 0x02, 124}, + {56, 0x03, 124}, + {2, 0x02, 35}, + {9, 0x02, 35}, + {23, 0x02, 35}, + {40, 0x03, 35}, + {2, 0x02, 62}, + {9, 0x02, 62}, + {23, 0x02, 62}, + {40, 0x03, 62}, + }, + /* 83 */ + { + {3, 0x02, 35}, + {6, 0x02, 35}, + {10, 0x02, 35}, + {15, 0x02, 35}, + {24, 0x02, 35}, + {31, 0x02, 35}, + {41, 0x02, 35}, + {56, 0x03, 35}, + {3, 0x02, 62}, + {6, 0x02, 62}, + {10, 0x02, 62}, + {15, 0x02, 62}, + {24, 0x02, 62}, + {31, 0x02, 62}, + {41, 0x02, 62}, + {56, 0x03, 62}, + }, + /* 84 */ + { + {1, 0x02, 0}, + {22, 0x03, 0}, + {1, 0x02, 36}, + {22, 0x03, 36}, + {1, 0x02, 64}, + {22, 0x03, 64}, + {1, 0x02, 91}, + {22, 0x03, 91}, + {1, 0x02, 93}, + {22, 0x03, 93}, + {1, 0x02, 126}, + {22, 0x03, 126}, + {0, 0x03, 94}, + {0, 0x03, 125}, + {93, 0x00, 0}, + {94, 0x00, 0}, + }, + /* 85 */ + { + {2, 0x02, 0}, + {9, 0x02, 0}, + {23, 0x02, 0}, + {40, 0x03, 0}, + {2, 0x02, 36}, + {9, 0x02, 36}, + {23, 0x02, 36}, + {40, 0x03, 36}, + {2, 0x02, 64}, + {9, 0x02, 64}, + {23, 0x02, 64}, + {40, 0x03, 64}, + {2, 0x02, 91}, + {9, 0x02, 91}, + {23, 0x02, 91}, + {40, 0x03, 91}, + }, + /* 86 */ + { + {3, 0x02, 0}, + {6, 0x02, 0}, + {10, 0x02, 0}, + {15, 0x02, 0}, + {24, 0x02, 0}, + {31, 0x02, 0}, + {41, 0x02, 0}, + {56, 0x03, 0}, + {3, 0x02, 36}, + {6, 0x02, 36}, + {10, 0x02, 36}, + {15, 0x02, 36}, + {24, 0x02, 36}, + {31, 0x02, 36}, + {41, 0x02, 36}, + {56, 0x03, 36}, + }, + /* 87 */ + { + {3, 0x02, 64}, + {6, 0x02, 64}, + {10, 0x02, 64}, + {15, 0x02, 64}, + {24, 0x02, 64}, + {31, 0x02, 64}, + {41, 0x02, 64}, + {56, 0x03, 64}, + {3, 0x02, 91}, + {6, 0x02, 91}, + {10, 0x02, 91}, + {15, 0x02, 91}, + {24, 0x02, 91}, + {31, 0x02, 91}, + {41, 0x02, 91}, + {56, 0x03, 91}, + }, + /* 88 */ + { + {2, 0x02, 93}, + {9, 0x02, 93}, + {23, 0x02, 93}, + {40, 0x03, 93}, + {2, 0x02, 126}, + {9, 0x02, 126}, + {23, 0x02, 126}, + {40, 0x03, 126}, + {1, 0x02, 94}, + {22, 0x03, 94}, + {1, 0x02, 125}, + {22, 0x03, 125}, + {0, 0x03, 60}, + {0, 0x03, 96}, + {0, 0x03, 123}, + {95, 0x00, 0}, + }, + /* 89 */ + { + {3, 0x02, 93}, + {6, 0x02, 93}, + {10, 0x02, 93}, + {15, 0x02, 93}, + {24, 0x02, 93}, + {31, 0x02, 93}, + {41, 0x02, 93}, + {56, 0x03, 93}, + {3, 0x02, 126}, + {6, 0x02, 126}, + {10, 0x02, 126}, + {15, 0x02, 126}, + {24, 0x02, 126}, + {31, 0x02, 126}, + {41, 0x02, 126}, + {56, 0x03, 126}, + }, + /* 90 */ + { + {2, 0x02, 94}, + {9, 0x02, 94}, + {23, 0x02, 94}, + {40, 0x03, 94}, + {2, 0x02, 125}, + {9, 0x02, 125}, + {23, 0x02, 125}, + {40, 0x03, 125}, + {1, 0x02, 60}, + {22, 0x03, 60}, + {1, 0x02, 96}, + {22, 0x03, 96}, + {1, 0x02, 123}, + {22, 0x03, 123}, + {96, 0x00, 0}, + {110, 0x00, 0}, + }, + /* 91 */ + { + {3, 0x02, 94}, + {6, 0x02, 94}, + {10, 0x02, 94}, + {15, 0x02, 94}, + {24, 0x02, 94}, + {31, 0x02, 94}, + {41, 0x02, 94}, + {56, 0x03, 94}, + {3, 0x02, 125}, + {6, 0x02, 125}, + {10, 0x02, 125}, + {15, 0x02, 125}, + {24, 0x02, 125}, + {31, 0x02, 125}, + {41, 0x02, 125}, + {56, 0x03, 125}, + }, + /* 92 */ + { + {2, 0x02, 60}, + {9, 0x02, 60}, + {23, 0x02, 60}, + {40, 0x03, 60}, + {2, 0x02, 96}, + {9, 0x02, 96}, + {23, 0x02, 96}, + {40, 0x03, 96}, + {2, 0x02, 123}, + {9, 0x02, 123}, + {23, 0x02, 123}, + {40, 0x03, 123}, + {97, 0x00, 0}, + {101, 0x00, 0}, + {111, 0x00, 0}, + {133, 0x00, 0}, + }, + /* 93 */ + { + {3, 0x02, 60}, + {6, 0x02, 60}, + {10, 0x02, 60}, + {15, 0x02, 60}, + {24, 0x02, 60}, + {31, 0x02, 60}, + {41, 0x02, 60}, + {56, 0x03, 60}, + {3, 0x02, 96}, + {6, 0x02, 96}, + {10, 0x02, 96}, + {15, 0x02, 96}, + {24, 0x02, 96}, + {31, 0x02, 96}, + {41, 0x02, 96}, + {56, 0x03, 96}, + }, + /* 94 */ + { + {3, 0x02, 123}, + {6, 0x02, 123}, + {10, 0x02, 123}, + {15, 0x02, 123}, + {24, 0x02, 123}, + {31, 0x02, 123}, + {41, 0x02, 123}, + {56, 0x03, 123}, + {98, 0x00, 0}, + {99, 0x00, 0}, + {102, 0x00, 0}, + {105, 0x00, 0}, + {112, 0x00, 0}, + {119, 0x00, 0}, + {134, 0x00, 0}, + {153, 0x00, 0}, + }, + /* 95 */ + { + {0, 0x03, 92}, + {0, 0x03, 195}, + {0, 0x03, 208}, + {100, 0x00, 0}, + {103, 0x00, 0}, + {104, 0x00, 0}, + {106, 0x00, 0}, + {107, 0x00, 0}, + {113, 0x00, 0}, + {116, 0x00, 0}, + {120, 0x00, 0}, + {126, 0x00, 0}, + {135, 0x00, 0}, + {142, 0x00, 0}, + {154, 0x00, 0}, + {169, 0x00, 0}, + }, + /* 96 */ + { + {1, 0x02, 92}, + {22, 0x03, 92}, + {1, 0x02, 195}, + {22, 0x03, 195}, + {1, 0x02, 208}, + {22, 0x03, 208}, + {0, 0x03, 128}, + {0, 0x03, 130}, + {0, 0x03, 131}, + {0, 0x03, 162}, + {0, 0x03, 184}, + {0, 0x03, 194}, + {0, 0x03, 224}, + {0, 0x03, 226}, + {108, 0x00, 0}, + {109, 0x00, 0}, + }, + /* 97 */ + { + {2, 0x02, 92}, + {9, 0x02, 92}, + {23, 0x02, 92}, + {40, 0x03, 92}, + {2, 0x02, 195}, + {9, 0x02, 195}, + {23, 0x02, 195}, + {40, 0x03, 195}, + {2, 0x02, 208}, + {9, 0x02, 208}, + {23, 0x02, 208}, + {40, 0x03, 208}, + {1, 0x02, 128}, + {22, 0x03, 128}, + {1, 0x02, 130}, + {22, 0x03, 130}, + }, + /* 98 */ + { + {3, 0x02, 92}, + {6, 0x02, 92}, + {10, 0x02, 92}, + {15, 0x02, 92}, + {24, 0x02, 92}, + {31, 0x02, 92}, + {41, 0x02, 92}, + {56, 0x03, 92}, + {3, 0x02, 195}, + {6, 0x02, 195}, + {10, 0x02, 195}, + {15, 0x02, 195}, + {24, 0x02, 195}, + {31, 0x02, 195}, + {41, 0x02, 195}, + {56, 0x03, 195}, + }, + /* 99 */ + { + {3, 0x02, 208}, + {6, 0x02, 208}, + {10, 0x02, 208}, + {15, 0x02, 208}, + {24, 0x02, 208}, + {31, 0x02, 208}, + {41, 0x02, 208}, + {56, 0x03, 208}, + {2, 0x02, 128}, + {9, 0x02, 128}, + {23, 0x02, 128}, + {40, 0x03, 128}, + {2, 0x02, 130}, + {9, 0x02, 130}, + {23, 0x02, 130}, + {40, 0x03, 130}, + }, + /* 100 */ + { + {3, 0x02, 128}, + {6, 0x02, 128}, + {10, 0x02, 128}, + {15, 0x02, 128}, + {24, 0x02, 128}, + {31, 0x02, 128}, + {41, 0x02, 128}, + {56, 0x03, 128}, + {3, 0x02, 130}, + {6, 0x02, 130}, + {10, 0x02, 130}, + {15, 0x02, 130}, + {24, 0x02, 130}, + {31, 0x02, 130}, + {41, 0x02, 130}, + {56, 0x03, 130}, + }, + /* 101 */ + { + {1, 0x02, 131}, + {22, 0x03, 131}, + {1, 0x02, 162}, + {22, 0x03, 162}, + {1, 0x02, 184}, + {22, 0x03, 184}, + {1, 0x02, 194}, + {22, 0x03, 194}, + {1, 0x02, 224}, + {22, 0x03, 224}, + {1, 0x02, 226}, + {22, 0x03, 226}, + {0, 0x03, 153}, + {0, 0x03, 161}, + {0, 0x03, 167}, + {0, 0x03, 172}, + }, + /* 102 */ + { + {2, 0x02, 131}, + {9, 0x02, 131}, + {23, 0x02, 131}, + {40, 0x03, 131}, + {2, 0x02, 162}, + {9, 0x02, 162}, + {23, 0x02, 162}, + {40, 0x03, 162}, + {2, 0x02, 184}, + {9, 0x02, 184}, + {23, 0x02, 184}, + {40, 0x03, 184}, + {2, 0x02, 194}, + {9, 0x02, 194}, + {23, 0x02, 194}, + {40, 0x03, 194}, + }, + /* 103 */ + { + {3, 0x02, 131}, + {6, 0x02, 131}, + {10, 0x02, 131}, + {15, 0x02, 131}, + {24, 0x02, 131}, + {31, 0x02, 131}, + {41, 0x02, 131}, + {56, 0x03, 131}, + {3, 0x02, 162}, + {6, 0x02, 162}, + {10, 0x02, 162}, + {15, 0x02, 162}, + {24, 0x02, 162}, + {31, 0x02, 162}, + {41, 0x02, 162}, + {56, 0x03, 162}, + }, + /* 104 */ + { + {3, 0x02, 184}, + {6, 0x02, 184}, + {10, 0x02, 184}, + {15, 0x02, 184}, + {24, 0x02, 184}, + {31, 0x02, 184}, + {41, 0x02, 184}, + {56, 0x03, 184}, + {3, 0x02, 194}, + {6, 0x02, 194}, + {10, 0x02, 194}, + {15, 0x02, 194}, + {24, 0x02, 194}, + {31, 0x02, 194}, + {41, 0x02, 194}, + {56, 0x03, 194}, + }, + /* 105 */ + { + {2, 0x02, 224}, + {9, 0x02, 224}, + {23, 0x02, 224}, + {40, 0x03, 224}, + {2, 0x02, 226}, + {9, 0x02, 226}, + {23, 0x02, 226}, + {40, 0x03, 226}, + {1, 0x02, 153}, + {22, 0x03, 153}, + {1, 0x02, 161}, + {22, 0x03, 161}, + {1, 0x02, 167}, + {22, 0x03, 167}, + {1, 0x02, 172}, + {22, 0x03, 172}, + }, + /* 106 */ + { + {3, 0x02, 224}, + {6, 0x02, 224}, + {10, 0x02, 224}, + {15, 0x02, 224}, + {24, 0x02, 224}, + {31, 0x02, 224}, + {41, 0x02, 224}, + {56, 0x03, 224}, + {3, 0x02, 226}, + {6, 0x02, 226}, + {10, 0x02, 226}, + {15, 0x02, 226}, + {24, 0x02, 226}, + {31, 0x02, 226}, + {41, 0x02, 226}, + {56, 0x03, 226}, + }, + /* 107 */ + { + {2, 0x02, 153}, + {9, 0x02, 153}, + {23, 0x02, 153}, + {40, 0x03, 153}, + {2, 0x02, 161}, + {9, 0x02, 161}, + {23, 0x02, 161}, + {40, 0x03, 161}, + {2, 0x02, 167}, + {9, 0x02, 167}, + {23, 0x02, 167}, + {40, 0x03, 167}, + {2, 0x02, 172}, + {9, 0x02, 172}, + {23, 0x02, 172}, + {40, 0x03, 172}, + }, + /* 108 */ + { + {3, 0x02, 153}, + {6, 0x02, 153}, + {10, 0x02, 153}, + {15, 0x02, 153}, + {24, 0x02, 153}, + {31, 0x02, 153}, + {41, 0x02, 153}, + {56, 0x03, 153}, + {3, 0x02, 161}, + {6, 0x02, 161}, + {10, 0x02, 161}, + {15, 0x02, 161}, + {24, 0x02, 161}, + {31, 0x02, 161}, + {41, 0x02, 161}, + {56, 0x03, 161}, + }, + /* 109 */ + { + {3, 0x02, 167}, + {6, 0x02, 167}, + {10, 0x02, 167}, + {15, 0x02, 167}, + {24, 0x02, 167}, + {31, 0x02, 167}, + {41, 0x02, 167}, + {56, 0x03, 167}, + {3, 0x02, 172}, + {6, 0x02, 172}, + {10, 0x02, 172}, + {15, 0x02, 172}, + {24, 0x02, 172}, + {31, 0x02, 172}, + {41, 0x02, 172}, + {56, 0x03, 172}, + }, + /* 110 */ + { + {114, 0x00, 0}, + {115, 0x00, 0}, + {117, 0x00, 0}, + {118, 0x00, 0}, + {121, 0x00, 0}, + {123, 0x00, 0}, + {127, 0x00, 0}, + {130, 0x00, 0}, + {136, 0x00, 0}, + {139, 0x00, 0}, + {143, 0x00, 0}, + {146, 0x00, 0}, + {155, 0x00, 0}, + {162, 0x00, 0}, + {170, 0x00, 0}, + {180, 0x00, 0}, + }, + /* 111 */ + { + {0, 0x03, 176}, + {0, 0x03, 177}, + {0, 0x03, 179}, + {0, 0x03, 209}, + {0, 0x03, 216}, + {0, 0x03, 217}, + {0, 0x03, 227}, + {0, 0x03, 229}, + {0, 0x03, 230}, + {122, 0x00, 0}, + {124, 0x00, 0}, + {125, 0x00, 0}, + {128, 0x00, 0}, + {129, 0x00, 0}, + {131, 0x00, 0}, + {132, 0x00, 0}, + }, + /* 112 */ + { + {1, 0x02, 176}, + {22, 0x03, 176}, + {1, 0x02, 177}, + {22, 0x03, 177}, + {1, 0x02, 179}, + {22, 0x03, 179}, + {1, 0x02, 209}, + {22, 0x03, 209}, + {1, 0x02, 216}, + {22, 0x03, 216}, + {1, 0x02, 217}, + {22, 0x03, 217}, + {1, 0x02, 227}, + {22, 0x03, 227}, + {1, 0x02, 229}, + {22, 0x03, 229}, + }, + /* 113 */ + { + {2, 0x02, 176}, + {9, 0x02, 176}, + {23, 0x02, 176}, + {40, 0x03, 176}, + {2, 0x02, 177}, + {9, 0x02, 177}, + {23, 0x02, 177}, + {40, 0x03, 177}, + {2, 0x02, 179}, + {9, 0x02, 179}, + {23, 0x02, 179}, + {40, 0x03, 179}, + {2, 0x02, 209}, + {9, 0x02, 209}, + {23, 0x02, 209}, + {40, 0x03, 209}, + }, + /* 114 */ + { + {3, 0x02, 176}, + {6, 0x02, 176}, + {10, 0x02, 176}, + {15, 0x02, 176}, + {24, 0x02, 176}, + {31, 0x02, 176}, + {41, 0x02, 176}, + {56, 0x03, 176}, + {3, 0x02, 177}, + {6, 0x02, 177}, + {10, 0x02, 177}, + {15, 0x02, 177}, + {24, 0x02, 177}, + {31, 0x02, 177}, + {41, 0x02, 177}, + {56, 0x03, 177}, + }, + /* 115 */ + { + {3, 0x02, 179}, + {6, 0x02, 179}, + {10, 0x02, 179}, + {15, 0x02, 179}, + {24, 0x02, 179}, + {31, 0x02, 179}, + {41, 0x02, 179}, + {56, 0x03, 179}, + {3, 0x02, 209}, + {6, 0x02, 209}, + {10, 0x02, 209}, + {15, 0x02, 209}, + {24, 0x02, 209}, + {31, 0x02, 209}, + {41, 0x02, 209}, + {56, 0x03, 209}, + }, + /* 116 */ + { + {2, 0x02, 216}, + {9, 0x02, 216}, + {23, 0x02, 216}, + {40, 0x03, 216}, + {2, 0x02, 217}, + {9, 0x02, 217}, + {23, 0x02, 217}, + {40, 0x03, 217}, + {2, 0x02, 227}, + {9, 0x02, 227}, + {23, 0x02, 227}, + {40, 0x03, 227}, + {2, 0x02, 229}, + {9, 0x02, 229}, + {23, 0x02, 229}, + {40, 0x03, 229}, + }, + /* 117 */ + { + {3, 0x02, 216}, + {6, 0x02, 216}, + {10, 0x02, 216}, + {15, 0x02, 216}, + {24, 0x02, 216}, + {31, 0x02, 216}, + {41, 0x02, 216}, + {56, 0x03, 216}, + {3, 0x02, 217}, + {6, 0x02, 217}, + {10, 0x02, 217}, + {15, 0x02, 217}, + {24, 0x02, 217}, + {31, 0x02, 217}, + {41, 0x02, 217}, + {56, 0x03, 217}, + }, + /* 118 */ + { + {3, 0x02, 227}, + {6, 0x02, 227}, + {10, 0x02, 227}, + {15, 0x02, 227}, + {24, 0x02, 227}, + {31, 0x02, 227}, + {41, 0x02, 227}, + {56, 0x03, 227}, + {3, 0x02, 229}, + {6, 0x02, 229}, + {10, 0x02, 229}, + {15, 0x02, 229}, + {24, 0x02, 229}, + {31, 0x02, 229}, + {41, 0x02, 229}, + {56, 0x03, 229}, + }, + /* 119 */ + { + {1, 0x02, 230}, + {22, 0x03, 230}, + {0, 0x03, 129}, + {0, 0x03, 132}, + {0, 0x03, 133}, + {0, 0x03, 134}, + {0, 0x03, 136}, + {0, 0x03, 146}, + {0, 0x03, 154}, + {0, 0x03, 156}, + {0, 0x03, 160}, + {0, 0x03, 163}, + {0, 0x03, 164}, + {0, 0x03, 169}, + {0, 0x03, 170}, + {0, 0x03, 173}, + }, + /* 120 */ + { + {2, 0x02, 230}, + {9, 0x02, 230}, + {23, 0x02, 230}, + {40, 0x03, 230}, + {1, 0x02, 129}, + {22, 0x03, 129}, + {1, 0x02, 132}, + {22, 0x03, 132}, + {1, 0x02, 133}, + {22, 0x03, 133}, + {1, 0x02, 134}, + {22, 0x03, 134}, + {1, 0x02, 136}, + {22, 0x03, 136}, + {1, 0x02, 146}, + {22, 0x03, 146}, + }, + /* 121 */ + { + {3, 0x02, 230}, + {6, 0x02, 230}, + {10, 0x02, 230}, + {15, 0x02, 230}, + {24, 0x02, 230}, + {31, 0x02, 230}, + {41, 0x02, 230}, + {56, 0x03, 230}, + {2, 0x02, 129}, + {9, 0x02, 129}, + {23, 0x02, 129}, + {40, 0x03, 129}, + {2, 0x02, 132}, + {9, 0x02, 132}, + {23, 0x02, 132}, + {40, 0x03, 132}, + }, + /* 122 */ + { + {3, 0x02, 129}, + {6, 0x02, 129}, + {10, 0x02, 129}, + {15, 0x02, 129}, + {24, 0x02, 129}, + {31, 0x02, 129}, + {41, 0x02, 129}, + {56, 0x03, 129}, + {3, 0x02, 132}, + {6, 0x02, 132}, + {10, 0x02, 132}, + {15, 0x02, 132}, + {24, 0x02, 132}, + {31, 0x02, 132}, + {41, 0x02, 132}, + {56, 0x03, 132}, + }, + /* 123 */ + { + {2, 0x02, 133}, + {9, 0x02, 133}, + {23, 0x02, 133}, + {40, 0x03, 133}, + {2, 0x02, 134}, + {9, 0x02, 134}, + {23, 0x02, 134}, + {40, 0x03, 134}, + {2, 0x02, 136}, + {9, 0x02, 136}, + {23, 0x02, 136}, + {40, 0x03, 136}, + {2, 0x02, 146}, + {9, 0x02, 146}, + {23, 0x02, 146}, + {40, 0x03, 146}, + }, + /* 124 */ + { + {3, 0x02, 133}, + {6, 0x02, 133}, + {10, 0x02, 133}, + {15, 0x02, 133}, + {24, 0x02, 133}, + {31, 0x02, 133}, + {41, 0x02, 133}, + {56, 0x03, 133}, + {3, 0x02, 134}, + {6, 0x02, 134}, + {10, 0x02, 134}, + {15, 0x02, 134}, + {24, 0x02, 134}, + {31, 0x02, 134}, + {41, 0x02, 134}, + {56, 0x03, 134}, + }, + /* 125 */ + { + {3, 0x02, 136}, + {6, 0x02, 136}, + {10, 0x02, 136}, + {15, 0x02, 136}, + {24, 0x02, 136}, + {31, 0x02, 136}, + {41, 0x02, 136}, + {56, 0x03, 136}, + {3, 0x02, 146}, + {6, 0x02, 146}, + {10, 0x02, 146}, + {15, 0x02, 146}, + {24, 0x02, 146}, + {31, 0x02, 146}, + {41, 0x02, 146}, + {56, 0x03, 146}, + }, + /* 126 */ + { + {1, 0x02, 154}, + {22, 0x03, 154}, + {1, 0x02, 156}, + {22, 0x03, 156}, + {1, 0x02, 160}, + {22, 0x03, 160}, + {1, 0x02, 163}, + {22, 0x03, 163}, + {1, 0x02, 164}, + {22, 0x03, 164}, + {1, 0x02, 169}, + {22, 0x03, 169}, + {1, 0x02, 170}, + {22, 0x03, 170}, + {1, 0x02, 173}, + {22, 0x03, 173}, + }, + /* 127 */ + { + {2, 0x02, 154}, + {9, 0x02, 154}, + {23, 0x02, 154}, + {40, 0x03, 154}, + {2, 0x02, 156}, + {9, 0x02, 156}, + {23, 0x02, 156}, + {40, 0x03, 156}, + {2, 0x02, 160}, + {9, 0x02, 160}, + {23, 0x02, 160}, + {40, 0x03, 160}, + {2, 0x02, 163}, + {9, 0x02, 163}, + {23, 0x02, 163}, + {40, 0x03, 163}, + }, + /* 128 */ + { + {3, 0x02, 154}, + {6, 0x02, 154}, + {10, 0x02, 154}, + {15, 0x02, 154}, + {24, 0x02, 154}, + {31, 0x02, 154}, + {41, 0x02, 154}, + {56, 0x03, 154}, + {3, 0x02, 156}, + {6, 0x02, 156}, + {10, 0x02, 156}, + {15, 0x02, 156}, + {24, 0x02, 156}, + {31, 0x02, 156}, + {41, 0x02, 156}, + {56, 0x03, 156}, + }, + /* 129 */ + { + {3, 0x02, 160}, + {6, 0x02, 160}, + {10, 0x02, 160}, + {15, 0x02, 160}, + {24, 0x02, 160}, + {31, 0x02, 160}, + {41, 0x02, 160}, + {56, 0x03, 160}, + {3, 0x02, 163}, + {6, 0x02, 163}, + {10, 0x02, 163}, + {15, 0x02, 163}, + {24, 0x02, 163}, + {31, 0x02, 163}, + {41, 0x02, 163}, + {56, 0x03, 163}, + }, + /* 130 */ + { + {2, 0x02, 164}, + {9, 0x02, 164}, + {23, 0x02, 164}, + {40, 0x03, 164}, + {2, 0x02, 169}, + {9, 0x02, 169}, + {23, 0x02, 169}, + {40, 0x03, 169}, + {2, 0x02, 170}, + {9, 0x02, 170}, + {23, 0x02, 170}, + {40, 0x03, 170}, + {2, 0x02, 173}, + {9, 0x02, 173}, + {23, 0x02, 173}, + {40, 0x03, 173}, + }, + /* 131 */ + { + {3, 0x02, 164}, + {6, 0x02, 164}, + {10, 0x02, 164}, + {15, 0x02, 164}, + {24, 0x02, 164}, + {31, 0x02, 164}, + {41, 0x02, 164}, + {56, 0x03, 164}, + {3, 0x02, 169}, + {6, 0x02, 169}, + {10, 0x02, 169}, + {15, 0x02, 169}, + {24, 0x02, 169}, + {31, 0x02, 169}, + {41, 0x02, 169}, + {56, 0x03, 169}, + }, + /* 132 */ + { + {3, 0x02, 170}, + {6, 0x02, 170}, + {10, 0x02, 170}, + {15, 0x02, 170}, + {24, 0x02, 170}, + {31, 0x02, 170}, + {41, 0x02, 170}, + {56, 0x03, 170}, + {3, 0x02, 173}, + {6, 0x02, 173}, + {10, 0x02, 173}, + {15, 0x02, 173}, + {24, 0x02, 173}, + {31, 0x02, 173}, + {41, 0x02, 173}, + {56, 0x03, 173}, + }, + /* 133 */ + { + {137, 0x00, 0}, + {138, 0x00, 0}, + {140, 0x00, 0}, + {141, 0x00, 0}, + {144, 0x00, 0}, + {145, 0x00, 0}, + {147, 0x00, 0}, + {150, 0x00, 0}, + {156, 0x00, 0}, + {159, 0x00, 0}, + {163, 0x00, 0}, + {166, 0x00, 0}, + {171, 0x00, 0}, + {174, 0x00, 0}, + {181, 0x00, 0}, + {190, 0x00, 0}, + }, + /* 134 */ + { + {0, 0x03, 178}, + {0, 0x03, 181}, + {0, 0x03, 185}, + {0, 0x03, 186}, + {0, 0x03, 187}, + {0, 0x03, 189}, + {0, 0x03, 190}, + {0, 0x03, 196}, + {0, 0x03, 198}, + {0, 0x03, 228}, + {0, 0x03, 232}, + {0, 0x03, 233}, + {148, 0x00, 0}, + {149, 0x00, 0}, + {151, 0x00, 0}, + {152, 0x00, 0}, + }, + /* 135 */ + { + {1, 0x02, 178}, + {22, 0x03, 178}, + {1, 0x02, 181}, + {22, 0x03, 181}, + {1, 0x02, 185}, + {22, 0x03, 185}, + {1, 0x02, 186}, + {22, 0x03, 186}, + {1, 0x02, 187}, + {22, 0x03, 187}, + {1, 0x02, 189}, + {22, 0x03, 189}, + {1, 0x02, 190}, + {22, 0x03, 190}, + {1, 0x02, 196}, + {22, 0x03, 196}, + }, + /* 136 */ + { + {2, 0x02, 178}, + {9, 0x02, 178}, + {23, 0x02, 178}, + {40, 0x03, 178}, + {2, 0x02, 181}, + {9, 0x02, 181}, + {23, 0x02, 181}, + {40, 0x03, 181}, + {2, 0x02, 185}, + {9, 0x02, 185}, + {23, 0x02, 185}, + {40, 0x03, 185}, + {2, 0x02, 186}, + {9, 0x02, 186}, + {23, 0x02, 186}, + {40, 0x03, 186}, + }, + /* 137 */ + { + {3, 0x02, 178}, + {6, 0x02, 178}, + {10, 0x02, 178}, + {15, 0x02, 178}, + {24, 0x02, 178}, + {31, 0x02, 178}, + {41, 0x02, 178}, + {56, 0x03, 178}, + {3, 0x02, 181}, + {6, 0x02, 181}, + {10, 0x02, 181}, + {15, 0x02, 181}, + {24, 0x02, 181}, + {31, 0x02, 181}, + {41, 0x02, 181}, + {56, 0x03, 181}, + }, + /* 138 */ + { + {3, 0x02, 185}, + {6, 0x02, 185}, + {10, 0x02, 185}, + {15, 0x02, 185}, + {24, 0x02, 185}, + {31, 0x02, 185}, + {41, 0x02, 185}, + {56, 0x03, 185}, + {3, 0x02, 186}, + {6, 0x02, 186}, + {10, 0x02, 186}, + {15, 0x02, 186}, + {24, 0x02, 186}, + {31, 0x02, 186}, + {41, 0x02, 186}, + {56, 0x03, 186}, + }, + /* 139 */ + { + {2, 0x02, 187}, + {9, 0x02, 187}, + {23, 0x02, 187}, + {40, 0x03, 187}, + {2, 0x02, 189}, + {9, 0x02, 189}, + {23, 0x02, 189}, + {40, 0x03, 189}, + {2, 0x02, 190}, + {9, 0x02, 190}, + {23, 0x02, 190}, + {40, 0x03, 190}, + {2, 0x02, 196}, + {9, 0x02, 196}, + {23, 0x02, 196}, + {40, 0x03, 196}, + }, + /* 140 */ + { + {3, 0x02, 187}, + {6, 0x02, 187}, + {10, 0x02, 187}, + {15, 0x02, 187}, + {24, 0x02, 187}, + {31, 0x02, 187}, + {41, 0x02, 187}, + {56, 0x03, 187}, + {3, 0x02, 189}, + {6, 0x02, 189}, + {10, 0x02, 189}, + {15, 0x02, 189}, + {24, 0x02, 189}, + {31, 0x02, 189}, + {41, 0x02, 189}, + {56, 0x03, 189}, + }, + /* 141 */ + { + {3, 0x02, 190}, + {6, 0x02, 190}, + {10, 0x02, 190}, + {15, 0x02, 190}, + {24, 0x02, 190}, + {31, 0x02, 190}, + {41, 0x02, 190}, + {56, 0x03, 190}, + {3, 0x02, 196}, + {6, 0x02, 196}, + {10, 0x02, 196}, + {15, 0x02, 196}, + {24, 0x02, 196}, + {31, 0x02, 196}, + {41, 0x02, 196}, + {56, 0x03, 196}, + }, + /* 142 */ + { + {1, 0x02, 198}, + {22, 0x03, 198}, + {1, 0x02, 228}, + {22, 0x03, 228}, + {1, 0x02, 232}, + {22, 0x03, 232}, + {1, 0x02, 233}, + {22, 0x03, 233}, + {0, 0x03, 1}, + {0, 0x03, 135}, + {0, 0x03, 137}, + {0, 0x03, 138}, + {0, 0x03, 139}, + {0, 0x03, 140}, + {0, 0x03, 141}, + {0, 0x03, 143}, + }, + /* 143 */ + { + {2, 0x02, 198}, + {9, 0x02, 198}, + {23, 0x02, 198}, + {40, 0x03, 198}, + {2, 0x02, 228}, + {9, 0x02, 228}, + {23, 0x02, 228}, + {40, 0x03, 228}, + {2, 0x02, 232}, + {9, 0x02, 232}, + {23, 0x02, 232}, + {40, 0x03, 232}, + {2, 0x02, 233}, + {9, 0x02, 233}, + {23, 0x02, 233}, + {40, 0x03, 233}, + }, + /* 144 */ + { + {3, 0x02, 198}, + {6, 0x02, 198}, + {10, 0x02, 198}, + {15, 0x02, 198}, + {24, 0x02, 198}, + {31, 0x02, 198}, + {41, 0x02, 198}, + {56, 0x03, 198}, + {3, 0x02, 228}, + {6, 0x02, 228}, + {10, 0x02, 228}, + {15, 0x02, 228}, + {24, 0x02, 228}, + {31, 0x02, 228}, + {41, 0x02, 228}, + {56, 0x03, 228}, + }, + /* 145 */ + { + {3, 0x02, 232}, + {6, 0x02, 232}, + {10, 0x02, 232}, + {15, 0x02, 232}, + {24, 0x02, 232}, + {31, 0x02, 232}, + {41, 0x02, 232}, + {56, 0x03, 232}, + {3, 0x02, 233}, + {6, 0x02, 233}, + {10, 0x02, 233}, + {15, 0x02, 233}, + {24, 0x02, 233}, + {31, 0x02, 233}, + {41, 0x02, 233}, + {56, 0x03, 233}, + }, + /* 146 */ + { + {1, 0x02, 1}, + {22, 0x03, 1}, + {1, 0x02, 135}, + {22, 0x03, 135}, + {1, 0x02, 137}, + {22, 0x03, 137}, + {1, 0x02, 138}, + {22, 0x03, 138}, + {1, 0x02, 139}, + {22, 0x03, 139}, + {1, 0x02, 140}, + {22, 0x03, 140}, + {1, 0x02, 141}, + {22, 0x03, 141}, + {1, 0x02, 143}, + {22, 0x03, 143}, + }, + /* 147 */ + { + {2, 0x02, 1}, + {9, 0x02, 1}, + {23, 0x02, 1}, + {40, 0x03, 1}, + {2, 0x02, 135}, + {9, 0x02, 135}, + {23, 0x02, 135}, + {40, 0x03, 135}, + {2, 0x02, 137}, + {9, 0x02, 137}, + {23, 0x02, 137}, + {40, 0x03, 137}, + {2, 0x02, 138}, + {9, 0x02, 138}, + {23, 0x02, 138}, + {40, 0x03, 138}, + }, + /* 148 */ + { + {3, 0x02, 1}, + {6, 0x02, 1}, + {10, 0x02, 1}, + {15, 0x02, 1}, + {24, 0x02, 1}, + {31, 0x02, 1}, + {41, 0x02, 1}, + {56, 0x03, 1}, + {3, 0x02, 135}, + {6, 0x02, 135}, + {10, 0x02, 135}, + {15, 0x02, 135}, + {24, 0x02, 135}, + {31, 0x02, 135}, + {41, 0x02, 135}, + {56, 0x03, 135}, + }, + /* 149 */ + { + {3, 0x02, 137}, + {6, 0x02, 137}, + {10, 0x02, 137}, + {15, 0x02, 137}, + {24, 0x02, 137}, + {31, 0x02, 137}, + {41, 0x02, 137}, + {56, 0x03, 137}, + {3, 0x02, 138}, + {6, 0x02, 138}, + {10, 0x02, 138}, + {15, 0x02, 138}, + {24, 0x02, 138}, + {31, 0x02, 138}, + {41, 0x02, 138}, + {56, 0x03, 138}, + }, + /* 150 */ + { + {2, 0x02, 139}, + {9, 0x02, 139}, + {23, 0x02, 139}, + {40, 0x03, 139}, + {2, 0x02, 140}, + {9, 0x02, 140}, + {23, 0x02, 140}, + {40, 0x03, 140}, + {2, 0x02, 141}, + {9, 0x02, 141}, + {23, 0x02, 141}, + {40, 0x03, 141}, + {2, 0x02, 143}, + {9, 0x02, 143}, + {23, 0x02, 143}, + {40, 0x03, 143}, + }, + /* 151 */ + { + {3, 0x02, 139}, + {6, 0x02, 139}, + {10, 0x02, 139}, + {15, 0x02, 139}, + {24, 0x02, 139}, + {31, 0x02, 139}, + {41, 0x02, 139}, + {56, 0x03, 139}, + {3, 0x02, 140}, + {6, 0x02, 140}, + {10, 0x02, 140}, + {15, 0x02, 140}, + {24, 0x02, 140}, + {31, 0x02, 140}, + {41, 0x02, 140}, + {56, 0x03, 140}, + }, + /* 152 */ + { + {3, 0x02, 141}, + {6, 0x02, 141}, + {10, 0x02, 141}, + {15, 0x02, 141}, + {24, 0x02, 141}, + {31, 0x02, 141}, + {41, 0x02, 141}, + {56, 0x03, 141}, + {3, 0x02, 143}, + {6, 0x02, 143}, + {10, 0x02, 143}, + {15, 0x02, 143}, + {24, 0x02, 143}, + {31, 0x02, 143}, + {41, 0x02, 143}, + {56, 0x03, 143}, + }, + /* 153 */ + { + {157, 0x00, 0}, + {158, 0x00, 0}, + {160, 0x00, 0}, + {161, 0x00, 0}, + {164, 0x00, 0}, + {165, 0x00, 0}, + {167, 0x00, 0}, + {168, 0x00, 0}, + {172, 0x00, 0}, + {173, 0x00, 0}, + {175, 0x00, 0}, + {177, 0x00, 0}, + {182, 0x00, 0}, + {185, 0x00, 0}, + {191, 0x00, 0}, + {207, 0x00, 0}, + }, + /* 154 */ + { + {0, 0x03, 147}, + {0, 0x03, 149}, + {0, 0x03, 150}, + {0, 0x03, 151}, + {0, 0x03, 152}, + {0, 0x03, 155}, + {0, 0x03, 157}, + {0, 0x03, 158}, + {0, 0x03, 165}, + {0, 0x03, 166}, + {0, 0x03, 168}, + {0, 0x03, 174}, + {0, 0x03, 175}, + {0, 0x03, 180}, + {0, 0x03, 182}, + {0, 0x03, 183}, + }, + /* 155 */ + { + {1, 0x02, 147}, + {22, 0x03, 147}, + {1, 0x02, 149}, + {22, 0x03, 149}, + {1, 0x02, 150}, + {22, 0x03, 150}, + {1, 0x02, 151}, + {22, 0x03, 151}, + {1, 0x02, 152}, + {22, 0x03, 152}, + {1, 0x02, 155}, + {22, 0x03, 155}, + {1, 0x02, 157}, + {22, 0x03, 157}, + {1, 0x02, 158}, + {22, 0x03, 158}, + }, + /* 156 */ + { + {2, 0x02, 147}, + {9, 0x02, 147}, + {23, 0x02, 147}, + {40, 0x03, 147}, + {2, 0x02, 149}, + {9, 0x02, 149}, + {23, 0x02, 149}, + {40, 0x03, 149}, + {2, 0x02, 150}, + {9, 0x02, 150}, + {23, 0x02, 150}, + {40, 0x03, 150}, + {2, 0x02, 151}, + {9, 0x02, 151}, + {23, 0x02, 151}, + {40, 0x03, 151}, + }, + /* 157 */ + { + {3, 0x02, 147}, + {6, 0x02, 147}, + {10, 0x02, 147}, + {15, 0x02, 147}, + {24, 0x02, 147}, + {31, 0x02, 147}, + {41, 0x02, 147}, + {56, 0x03, 147}, + {3, 0x02, 149}, + {6, 0x02, 149}, + {10, 0x02, 149}, + {15, 0x02, 149}, + {24, 0x02, 149}, + {31, 0x02, 149}, + {41, 0x02, 149}, + {56, 0x03, 149}, + }, + /* 158 */ + { + {3, 0x02, 150}, + {6, 0x02, 150}, + {10, 0x02, 150}, + {15, 0x02, 150}, + {24, 0x02, 150}, + {31, 0x02, 150}, + {41, 0x02, 150}, + {56, 0x03, 150}, + {3, 0x02, 151}, + {6, 0x02, 151}, + {10, 0x02, 151}, + {15, 0x02, 151}, + {24, 0x02, 151}, + {31, 0x02, 151}, + {41, 0x02, 151}, + {56, 0x03, 151}, + }, + /* 159 */ + { + {2, 0x02, 152}, + {9, 0x02, 152}, + {23, 0x02, 152}, + {40, 0x03, 152}, + {2, 0x02, 155}, + {9, 0x02, 155}, + {23, 0x02, 155}, + {40, 0x03, 155}, + {2, 0x02, 157}, + {9, 0x02, 157}, + {23, 0x02, 157}, + {40, 0x03, 157}, + {2, 0x02, 158}, + {9, 0x02, 158}, + {23, 0x02, 158}, + {40, 0x03, 158}, + }, + /* 160 */ + { + {3, 0x02, 152}, + {6, 0x02, 152}, + {10, 0x02, 152}, + {15, 0x02, 152}, + {24, 0x02, 152}, + {31, 0x02, 152}, + {41, 0x02, 152}, + {56, 0x03, 152}, + {3, 0x02, 155}, + {6, 0x02, 155}, + {10, 0x02, 155}, + {15, 0x02, 155}, + {24, 0x02, 155}, + {31, 0x02, 155}, + {41, 0x02, 155}, + {56, 0x03, 155}, + }, + /* 161 */ + { + {3, 0x02, 157}, + {6, 0x02, 157}, + {10, 0x02, 157}, + {15, 0x02, 157}, + {24, 0x02, 157}, + {31, 0x02, 157}, + {41, 0x02, 157}, + {56, 0x03, 157}, + {3, 0x02, 158}, + {6, 0x02, 158}, + {10, 0x02, 158}, + {15, 0x02, 158}, + {24, 0x02, 158}, + {31, 0x02, 158}, + {41, 0x02, 158}, + {56, 0x03, 158}, + }, + /* 162 */ + { + {1, 0x02, 165}, + {22, 0x03, 165}, + {1, 0x02, 166}, + {22, 0x03, 166}, + {1, 0x02, 168}, + {22, 0x03, 168}, + {1, 0x02, 174}, + {22, 0x03, 174}, + {1, 0x02, 175}, + {22, 0x03, 175}, + {1, 0x02, 180}, + {22, 0x03, 180}, + {1, 0x02, 182}, + {22, 0x03, 182}, + {1, 0x02, 183}, + {22, 0x03, 183}, + }, + /* 163 */ + { + {2, 0x02, 165}, + {9, 0x02, 165}, + {23, 0x02, 165}, + {40, 0x03, 165}, + {2, 0x02, 166}, + {9, 0x02, 166}, + {23, 0x02, 166}, + {40, 0x03, 166}, + {2, 0x02, 168}, + {9, 0x02, 168}, + {23, 0x02, 168}, + {40, 0x03, 168}, + {2, 0x02, 174}, + {9, 0x02, 174}, + {23, 0x02, 174}, + {40, 0x03, 174}, + }, + /* 164 */ + { + {3, 0x02, 165}, + {6, 0x02, 165}, + {10, 0x02, 165}, + {15, 0x02, 165}, + {24, 0x02, 165}, + {31, 0x02, 165}, + {41, 0x02, 165}, + {56, 0x03, 165}, + {3, 0x02, 166}, + {6, 0x02, 166}, + {10, 0x02, 166}, + {15, 0x02, 166}, + {24, 0x02, 166}, + {31, 0x02, 166}, + {41, 0x02, 166}, + {56, 0x03, 166}, + }, + /* 165 */ + { + {3, 0x02, 168}, + {6, 0x02, 168}, + {10, 0x02, 168}, + {15, 0x02, 168}, + {24, 0x02, 168}, + {31, 0x02, 168}, + {41, 0x02, 168}, + {56, 0x03, 168}, + {3, 0x02, 174}, + {6, 0x02, 174}, + {10, 0x02, 174}, + {15, 0x02, 174}, + {24, 0x02, 174}, + {31, 0x02, 174}, + {41, 0x02, 174}, + {56, 0x03, 174}, + }, + /* 166 */ + { + {2, 0x02, 175}, + {9, 0x02, 175}, + {23, 0x02, 175}, + {40, 0x03, 175}, + {2, 0x02, 180}, + {9, 0x02, 180}, + {23, 0x02, 180}, + {40, 0x03, 180}, + {2, 0x02, 182}, + {9, 0x02, 182}, + {23, 0x02, 182}, + {40, 0x03, 182}, + {2, 0x02, 183}, + {9, 0x02, 183}, + {23, 0x02, 183}, + {40, 0x03, 183}, + }, + /* 167 */ + { + {3, 0x02, 175}, + {6, 0x02, 175}, + {10, 0x02, 175}, + {15, 0x02, 175}, + {24, 0x02, 175}, + {31, 0x02, 175}, + {41, 0x02, 175}, + {56, 0x03, 175}, + {3, 0x02, 180}, + {6, 0x02, 180}, + {10, 0x02, 180}, + {15, 0x02, 180}, + {24, 0x02, 180}, + {31, 0x02, 180}, + {41, 0x02, 180}, + {56, 0x03, 180}, + }, + /* 168 */ + { + {3, 0x02, 182}, + {6, 0x02, 182}, + {10, 0x02, 182}, + {15, 0x02, 182}, + {24, 0x02, 182}, + {31, 0x02, 182}, + {41, 0x02, 182}, + {56, 0x03, 182}, + {3, 0x02, 183}, + {6, 0x02, 183}, + {10, 0x02, 183}, + {15, 0x02, 183}, + {24, 0x02, 183}, + {31, 0x02, 183}, + {41, 0x02, 183}, + {56, 0x03, 183}, + }, + /* 169 */ + { + {0, 0x03, 188}, + {0, 0x03, 191}, + {0, 0x03, 197}, + {0, 0x03, 231}, + {0, 0x03, 239}, + {176, 0x00, 0}, + {178, 0x00, 0}, + {179, 0x00, 0}, + {183, 0x00, 0}, + {184, 0x00, 0}, + {186, 0x00, 0}, + {187, 0x00, 0}, + {192, 0x00, 0}, + {199, 0x00, 0}, + {208, 0x00, 0}, + {223, 0x00, 0}, + }, + /* 170 */ + { + {1, 0x02, 188}, + {22, 0x03, 188}, + {1, 0x02, 191}, + {22, 0x03, 191}, + {1, 0x02, 197}, + {22, 0x03, 197}, + {1, 0x02, 231}, + {22, 0x03, 231}, + {1, 0x02, 239}, + {22, 0x03, 239}, + {0, 0x03, 9}, + {0, 0x03, 142}, + {0, 0x03, 144}, + {0, 0x03, 145}, + {0, 0x03, 148}, + {0, 0x03, 159}, + }, + /* 171 */ + { + {2, 0x02, 188}, + {9, 0x02, 188}, + {23, 0x02, 188}, + {40, 0x03, 188}, + {2, 0x02, 191}, + {9, 0x02, 191}, + {23, 0x02, 191}, + {40, 0x03, 191}, + {2, 0x02, 197}, + {9, 0x02, 197}, + {23, 0x02, 197}, + {40, 0x03, 197}, + {2, 0x02, 231}, + {9, 0x02, 231}, + {23, 0x02, 231}, + {40, 0x03, 231}, + }, + /* 172 */ + { + {3, 0x02, 188}, + {6, 0x02, 188}, + {10, 0x02, 188}, + {15, 0x02, 188}, + {24, 0x02, 188}, + {31, 0x02, 188}, + {41, 0x02, 188}, + {56, 0x03, 188}, + {3, 0x02, 191}, + {6, 0x02, 191}, + {10, 0x02, 191}, + {15, 0x02, 191}, + {24, 0x02, 191}, + {31, 0x02, 191}, + {41, 0x02, 191}, + {56, 0x03, 191}, + }, + /* 173 */ + { + {3, 0x02, 197}, + {6, 0x02, 197}, + {10, 0x02, 197}, + {15, 0x02, 197}, + {24, 0x02, 197}, + {31, 0x02, 197}, + {41, 0x02, 197}, + {56, 0x03, 197}, + {3, 0x02, 231}, + {6, 0x02, 231}, + {10, 0x02, 231}, + {15, 0x02, 231}, + {24, 0x02, 231}, + {31, 0x02, 231}, + {41, 0x02, 231}, + {56, 0x03, 231}, + }, + /* 174 */ + { + {2, 0x02, 239}, + {9, 0x02, 239}, + {23, 0x02, 239}, + {40, 0x03, 239}, + {1, 0x02, 9}, + {22, 0x03, 9}, + {1, 0x02, 142}, + {22, 0x03, 142}, + {1, 0x02, 144}, + {22, 0x03, 144}, + {1, 0x02, 145}, + {22, 0x03, 145}, + {1, 0x02, 148}, + {22, 0x03, 148}, + {1, 0x02, 159}, + {22, 0x03, 159}, + }, + /* 175 */ + { + {3, 0x02, 239}, + {6, 0x02, 239}, + {10, 0x02, 239}, + {15, 0x02, 239}, + {24, 0x02, 239}, + {31, 0x02, 239}, + {41, 0x02, 239}, + {56, 0x03, 239}, + {2, 0x02, 9}, + {9, 0x02, 9}, + {23, 0x02, 9}, + {40, 0x03, 9}, + {2, 0x02, 142}, + {9, 0x02, 142}, + {23, 0x02, 142}, + {40, 0x03, 142}, + }, + /* 176 */ + { + {3, 0x02, 9}, + {6, 0x02, 9}, + {10, 0x02, 9}, + {15, 0x02, 9}, + {24, 0x02, 9}, + {31, 0x02, 9}, + {41, 0x02, 9}, + {56, 0x03, 9}, + {3, 0x02, 142}, + {6, 0x02, 142}, + {10, 0x02, 142}, + {15, 0x02, 142}, + {24, 0x02, 142}, + {31, 0x02, 142}, + {41, 0x02, 142}, + {56, 0x03, 142}, + }, + /* 177 */ + { + {2, 0x02, 144}, + {9, 0x02, 144}, + {23, 0x02, 144}, + {40, 0x03, 144}, + {2, 0x02, 145}, + {9, 0x02, 145}, + {23, 0x02, 145}, + {40, 0x03, 145}, + {2, 0x02, 148}, + {9, 0x02, 148}, + {23, 0x02, 148}, + {40, 0x03, 148}, + {2, 0x02, 159}, + {9, 0x02, 159}, + {23, 0x02, 159}, + {40, 0x03, 159}, + }, + /* 178 */ + { + {3, 0x02, 144}, + {6, 0x02, 144}, + {10, 0x02, 144}, + {15, 0x02, 144}, + {24, 0x02, 144}, + {31, 0x02, 144}, + {41, 0x02, 144}, + {56, 0x03, 144}, + {3, 0x02, 145}, + {6, 0x02, 145}, + {10, 0x02, 145}, + {15, 0x02, 145}, + {24, 0x02, 145}, + {31, 0x02, 145}, + {41, 0x02, 145}, + {56, 0x03, 145}, + }, + /* 179 */ + { + {3, 0x02, 148}, + {6, 0x02, 148}, + {10, 0x02, 148}, + {15, 0x02, 148}, + {24, 0x02, 148}, + {31, 0x02, 148}, + {41, 0x02, 148}, + {56, 0x03, 148}, + {3, 0x02, 159}, + {6, 0x02, 159}, + {10, 0x02, 159}, + {15, 0x02, 159}, + {24, 0x02, 159}, + {31, 0x02, 159}, + {41, 0x02, 159}, + {56, 0x03, 159}, + }, + /* 180 */ + { + {0, 0x03, 171}, + {0, 0x03, 206}, + {0, 0x03, 215}, + {0, 0x03, 225}, + {0, 0x03, 236}, + {0, 0x03, 237}, + {188, 0x00, 0}, + {189, 0x00, 0}, + {193, 0x00, 0}, + {196, 0x00, 0}, + {200, 0x00, 0}, + {203, 0x00, 0}, + {209, 0x00, 0}, + {216, 0x00, 0}, + {224, 0x00, 0}, + {238, 0x00, 0}, + }, + /* 181 */ + { + {1, 0x02, 171}, + {22, 0x03, 171}, + {1, 0x02, 206}, + {22, 0x03, 206}, + {1, 0x02, 215}, + {22, 0x03, 215}, + {1, 0x02, 225}, + {22, 0x03, 225}, + {1, 0x02, 236}, + {22, 0x03, 236}, + {1, 0x02, 237}, + {22, 0x03, 237}, + {0, 0x03, 199}, + {0, 0x03, 207}, + {0, 0x03, 234}, + {0, 0x03, 235}, + }, + /* 182 */ + { + {2, 0x02, 171}, + {9, 0x02, 171}, + {23, 0x02, 171}, + {40, 0x03, 171}, + {2, 0x02, 206}, + {9, 0x02, 206}, + {23, 0x02, 206}, + {40, 0x03, 206}, + {2, 0x02, 215}, + {9, 0x02, 215}, + {23, 0x02, 215}, + {40, 0x03, 215}, + {2, 0x02, 225}, + {9, 0x02, 225}, + {23, 0x02, 225}, + {40, 0x03, 225}, + }, + /* 183 */ + { + {3, 0x02, 171}, + {6, 0x02, 171}, + {10, 0x02, 171}, + {15, 0x02, 171}, + {24, 0x02, 171}, + {31, 0x02, 171}, + {41, 0x02, 171}, + {56, 0x03, 171}, + {3, 0x02, 206}, + {6, 0x02, 206}, + {10, 0x02, 206}, + {15, 0x02, 206}, + {24, 0x02, 206}, + {31, 0x02, 206}, + {41, 0x02, 206}, + {56, 0x03, 206}, + }, + /* 184 */ + { + {3, 0x02, 215}, + {6, 0x02, 215}, + {10, 0x02, 215}, + {15, 0x02, 215}, + {24, 0x02, 215}, + {31, 0x02, 215}, + {41, 0x02, 215}, + {56, 0x03, 215}, + {3, 0x02, 225}, + {6, 0x02, 225}, + {10, 0x02, 225}, + {15, 0x02, 225}, + {24, 0x02, 225}, + {31, 0x02, 225}, + {41, 0x02, 225}, + {56, 0x03, 225}, + }, + /* 185 */ + { + {2, 0x02, 236}, + {9, 0x02, 236}, + {23, 0x02, 236}, + {40, 0x03, 236}, + {2, 0x02, 237}, + {9, 0x02, 237}, + {23, 0x02, 237}, + {40, 0x03, 237}, + {1, 0x02, 199}, + {22, 0x03, 199}, + {1, 0x02, 207}, + {22, 0x03, 207}, + {1, 0x02, 234}, + {22, 0x03, 234}, + {1, 0x02, 235}, + {22, 0x03, 235}, + }, + /* 186 */ + { + {3, 0x02, 236}, + {6, 0x02, 236}, + {10, 0x02, 236}, + {15, 0x02, 236}, + {24, 0x02, 236}, + {31, 0x02, 236}, + {41, 0x02, 236}, + {56, 0x03, 236}, + {3, 0x02, 237}, + {6, 0x02, 237}, + {10, 0x02, 237}, + {15, 0x02, 237}, + {24, 0x02, 237}, + {31, 0x02, 237}, + {41, 0x02, 237}, + {56, 0x03, 237}, + }, + /* 187 */ + { + {2, 0x02, 199}, + {9, 0x02, 199}, + {23, 0x02, 199}, + {40, 0x03, 199}, + {2, 0x02, 207}, + {9, 0x02, 207}, + {23, 0x02, 207}, + {40, 0x03, 207}, + {2, 0x02, 234}, + {9, 0x02, 234}, + {23, 0x02, 234}, + {40, 0x03, 234}, + {2, 0x02, 235}, + {9, 0x02, 235}, + {23, 0x02, 235}, + {40, 0x03, 235}, + }, + /* 188 */ + { + {3, 0x02, 199}, + {6, 0x02, 199}, + {10, 0x02, 199}, + {15, 0x02, 199}, + {24, 0x02, 199}, + {31, 0x02, 199}, + {41, 0x02, 199}, + {56, 0x03, 199}, + {3, 0x02, 207}, + {6, 0x02, 207}, + {10, 0x02, 207}, + {15, 0x02, 207}, + {24, 0x02, 207}, + {31, 0x02, 207}, + {41, 0x02, 207}, + {56, 0x03, 207}, + }, + /* 189 */ + { + {3, 0x02, 234}, + {6, 0x02, 234}, + {10, 0x02, 234}, + {15, 0x02, 234}, + {24, 0x02, 234}, + {31, 0x02, 234}, + {41, 0x02, 234}, + {56, 0x03, 234}, + {3, 0x02, 235}, + {6, 0x02, 235}, + {10, 0x02, 235}, + {15, 0x02, 235}, + {24, 0x02, 235}, + {31, 0x02, 235}, + {41, 0x02, 235}, + {56, 0x03, 235}, + }, + /* 190 */ + { + {194, 0x00, 0}, + {195, 0x00, 0}, + {197, 0x00, 0}, + {198, 0x00, 0}, + {201, 0x00, 0}, + {202, 0x00, 0}, + {204, 0x00, 0}, + {205, 0x00, 0}, + {210, 0x00, 0}, + {213, 0x00, 0}, + {217, 0x00, 0}, + {220, 0x00, 0}, + {225, 0x00, 0}, + {231, 0x00, 0}, + {239, 0x00, 0}, + {246, 0x00, 0}, + }, + /* 191 */ + { + {0, 0x03, 192}, + {0, 0x03, 193}, + {0, 0x03, 200}, + {0, 0x03, 201}, + {0, 0x03, 202}, + {0, 0x03, 205}, + {0, 0x03, 210}, + {0, 0x03, 213}, + {0, 0x03, 218}, + {0, 0x03, 219}, + {0, 0x03, 238}, + {0, 0x03, 240}, + {0, 0x03, 242}, + {0, 0x03, 243}, + {0, 0x03, 255}, + {206, 0x00, 0}, + }, + /* 192 */ + { + {1, 0x02, 192}, + {22, 0x03, 192}, + {1, 0x02, 193}, + {22, 0x03, 193}, + {1, 0x02, 200}, + {22, 0x03, 200}, + {1, 0x02, 201}, + {22, 0x03, 201}, + {1, 0x02, 202}, + {22, 0x03, 202}, + {1, 0x02, 205}, + {22, 0x03, 205}, + {1, 0x02, 210}, + {22, 0x03, 210}, + {1, 0x02, 213}, + {22, 0x03, 213}, + }, + /* 193 */ + { + {2, 0x02, 192}, + {9, 0x02, 192}, + {23, 0x02, 192}, + {40, 0x03, 192}, + {2, 0x02, 193}, + {9, 0x02, 193}, + {23, 0x02, 193}, + {40, 0x03, 193}, + {2, 0x02, 200}, + {9, 0x02, 200}, + {23, 0x02, 200}, + {40, 0x03, 200}, + {2, 0x02, 201}, + {9, 0x02, 201}, + {23, 0x02, 201}, + {40, 0x03, 201}, + }, + /* 194 */ + { + {3, 0x02, 192}, + {6, 0x02, 192}, + {10, 0x02, 192}, + {15, 0x02, 192}, + {24, 0x02, 192}, + {31, 0x02, 192}, + {41, 0x02, 192}, + {56, 0x03, 192}, + {3, 0x02, 193}, + {6, 0x02, 193}, + {10, 0x02, 193}, + {15, 0x02, 193}, + {24, 0x02, 193}, + {31, 0x02, 193}, + {41, 0x02, 193}, + {56, 0x03, 193}, + }, + /* 195 */ + { + {3, 0x02, 200}, + {6, 0x02, 200}, + {10, 0x02, 200}, + {15, 0x02, 200}, + {24, 0x02, 200}, + {31, 0x02, 200}, + {41, 0x02, 200}, + {56, 0x03, 200}, + {3, 0x02, 201}, + {6, 0x02, 201}, + {10, 0x02, 201}, + {15, 0x02, 201}, + {24, 0x02, 201}, + {31, 0x02, 201}, + {41, 0x02, 201}, + {56, 0x03, 201}, + }, + /* 196 */ + { + {2, 0x02, 202}, + {9, 0x02, 202}, + {23, 0x02, 202}, + {40, 0x03, 202}, + {2, 0x02, 205}, + {9, 0x02, 205}, + {23, 0x02, 205}, + {40, 0x03, 205}, + {2, 0x02, 210}, + {9, 0x02, 210}, + {23, 0x02, 210}, + {40, 0x03, 210}, + {2, 0x02, 213}, + {9, 0x02, 213}, + {23, 0x02, 213}, + {40, 0x03, 213}, + }, + /* 197 */ + { + {3, 0x02, 202}, + {6, 0x02, 202}, + {10, 0x02, 202}, + {15, 0x02, 202}, + {24, 0x02, 202}, + {31, 0x02, 202}, + {41, 0x02, 202}, + {56, 0x03, 202}, + {3, 0x02, 205}, + {6, 0x02, 205}, + {10, 0x02, 205}, + {15, 0x02, 205}, + {24, 0x02, 205}, + {31, 0x02, 205}, + {41, 0x02, 205}, + {56, 0x03, 205}, + }, + /* 198 */ + { + {3, 0x02, 210}, + {6, 0x02, 210}, + {10, 0x02, 210}, + {15, 0x02, 210}, + {24, 0x02, 210}, + {31, 0x02, 210}, + {41, 0x02, 210}, + {56, 0x03, 210}, + {3, 0x02, 213}, + {6, 0x02, 213}, + {10, 0x02, 213}, + {15, 0x02, 213}, + {24, 0x02, 213}, + {31, 0x02, 213}, + {41, 0x02, 213}, + {56, 0x03, 213}, + }, + /* 199 */ + { + {1, 0x02, 218}, + {22, 0x03, 218}, + {1, 0x02, 219}, + {22, 0x03, 219}, + {1, 0x02, 238}, + {22, 0x03, 238}, + {1, 0x02, 240}, + {22, 0x03, 240}, + {1, 0x02, 242}, + {22, 0x03, 242}, + {1, 0x02, 243}, + {22, 0x03, 243}, + {1, 0x02, 255}, + {22, 0x03, 255}, + {0, 0x03, 203}, + {0, 0x03, 204}, + }, + /* 200 */ + { + {2, 0x02, 218}, + {9, 0x02, 218}, + {23, 0x02, 218}, + {40, 0x03, 218}, + {2, 0x02, 219}, + {9, 0x02, 219}, + {23, 0x02, 219}, + {40, 0x03, 219}, + {2, 0x02, 238}, + {9, 0x02, 238}, + {23, 0x02, 238}, + {40, 0x03, 238}, + {2, 0x02, 240}, + {9, 0x02, 240}, + {23, 0x02, 240}, + {40, 0x03, 240}, + }, + /* 201 */ + { + {3, 0x02, 218}, + {6, 0x02, 218}, + {10, 0x02, 218}, + {15, 0x02, 218}, + {24, 0x02, 218}, + {31, 0x02, 218}, + {41, 0x02, 218}, + {56, 0x03, 218}, + {3, 0x02, 219}, + {6, 0x02, 219}, + {10, 0x02, 219}, + {15, 0x02, 219}, + {24, 0x02, 219}, + {31, 0x02, 219}, + {41, 0x02, 219}, + {56, 0x03, 219}, + }, + /* 202 */ + { + {3, 0x02, 238}, + {6, 0x02, 238}, + {10, 0x02, 238}, + {15, 0x02, 238}, + {24, 0x02, 238}, + {31, 0x02, 238}, + {41, 0x02, 238}, + {56, 0x03, 238}, + {3, 0x02, 240}, + {6, 0x02, 240}, + {10, 0x02, 240}, + {15, 0x02, 240}, + {24, 0x02, 240}, + {31, 0x02, 240}, + {41, 0x02, 240}, + {56, 0x03, 240}, + }, + /* 203 */ + { + {2, 0x02, 242}, + {9, 0x02, 242}, + {23, 0x02, 242}, + {40, 0x03, 242}, + {2, 0x02, 243}, + {9, 0x02, 243}, + {23, 0x02, 243}, + {40, 0x03, 243}, + {2, 0x02, 255}, + {9, 0x02, 255}, + {23, 0x02, 255}, + {40, 0x03, 255}, + {1, 0x02, 203}, + {22, 0x03, 203}, + {1, 0x02, 204}, + {22, 0x03, 204}, + }, + /* 204 */ + { + {3, 0x02, 242}, + {6, 0x02, 242}, + {10, 0x02, 242}, + {15, 0x02, 242}, + {24, 0x02, 242}, + {31, 0x02, 242}, + {41, 0x02, 242}, + {56, 0x03, 242}, + {3, 0x02, 243}, + {6, 0x02, 243}, + {10, 0x02, 243}, + {15, 0x02, 243}, + {24, 0x02, 243}, + {31, 0x02, 243}, + {41, 0x02, 243}, + {56, 0x03, 243}, + }, + /* 205 */ + { + {3, 0x02, 255}, + {6, 0x02, 255}, + {10, 0x02, 255}, + {15, 0x02, 255}, + {24, 0x02, 255}, + {31, 0x02, 255}, + {41, 0x02, 255}, + {56, 0x03, 255}, + {2, 0x02, 203}, + {9, 0x02, 203}, + {23, 0x02, 203}, + {40, 0x03, 203}, + {2, 0x02, 204}, + {9, 0x02, 204}, + {23, 0x02, 204}, + {40, 0x03, 204}, + }, + /* 206 */ + { + {3, 0x02, 203}, + {6, 0x02, 203}, + {10, 0x02, 203}, + {15, 0x02, 203}, + {24, 0x02, 203}, + {31, 0x02, 203}, + {41, 0x02, 203}, + {56, 0x03, 203}, + {3, 0x02, 204}, + {6, 0x02, 204}, + {10, 0x02, 204}, + {15, 0x02, 204}, + {24, 0x02, 204}, + {31, 0x02, 204}, + {41, 0x02, 204}, + {56, 0x03, 204}, + }, + /* 207 */ + { + {211, 0x00, 0}, + {212, 0x00, 0}, + {214, 0x00, 0}, + {215, 0x00, 0}, + {218, 0x00, 0}, + {219, 0x00, 0}, + {221, 0x00, 0}, + {222, 0x00, 0}, + {226, 0x00, 0}, + {228, 0x00, 0}, + {232, 0x00, 0}, + {235, 0x00, 0}, + {240, 0x00, 0}, + {243, 0x00, 0}, + {247, 0x00, 0}, + {250, 0x00, 0}, + }, + /* 208 */ + { + {0, 0x03, 211}, + {0, 0x03, 212}, + {0, 0x03, 214}, + {0, 0x03, 221}, + {0, 0x03, 222}, + {0, 0x03, 223}, + {0, 0x03, 241}, + {0, 0x03, 244}, + {0, 0x03, 245}, + {0, 0x03, 246}, + {0, 0x03, 247}, + {0, 0x03, 248}, + {0, 0x03, 250}, + {0, 0x03, 251}, + {0, 0x03, 252}, + {0, 0x03, 253}, + }, + /* 209 */ + { + {1, 0x02, 211}, + {22, 0x03, 211}, + {1, 0x02, 212}, + {22, 0x03, 212}, + {1, 0x02, 214}, + {22, 0x03, 214}, + {1, 0x02, 221}, + {22, 0x03, 221}, + {1, 0x02, 222}, + {22, 0x03, 222}, + {1, 0x02, 223}, + {22, 0x03, 223}, + {1, 0x02, 241}, + {22, 0x03, 241}, + {1, 0x02, 244}, + {22, 0x03, 244}, + }, + /* 210 */ + { + {2, 0x02, 211}, + {9, 0x02, 211}, + {23, 0x02, 211}, + {40, 0x03, 211}, + {2, 0x02, 212}, + {9, 0x02, 212}, + {23, 0x02, 212}, + {40, 0x03, 212}, + {2, 0x02, 214}, + {9, 0x02, 214}, + {23, 0x02, 214}, + {40, 0x03, 214}, + {2, 0x02, 221}, + {9, 0x02, 221}, + {23, 0x02, 221}, + {40, 0x03, 221}, + }, + /* 211 */ + { + {3, 0x02, 211}, + {6, 0x02, 211}, + {10, 0x02, 211}, + {15, 0x02, 211}, + {24, 0x02, 211}, + {31, 0x02, 211}, + {41, 0x02, 211}, + {56, 0x03, 211}, + {3, 0x02, 212}, + {6, 0x02, 212}, + {10, 0x02, 212}, + {15, 0x02, 212}, + {24, 0x02, 212}, + {31, 0x02, 212}, + {41, 0x02, 212}, + {56, 0x03, 212}, + }, + /* 212 */ + { + {3, 0x02, 214}, + {6, 0x02, 214}, + {10, 0x02, 214}, + {15, 0x02, 214}, + {24, 0x02, 214}, + {31, 0x02, 214}, + {41, 0x02, 214}, + {56, 0x03, 214}, + {3, 0x02, 221}, + {6, 0x02, 221}, + {10, 0x02, 221}, + {15, 0x02, 221}, + {24, 0x02, 221}, + {31, 0x02, 221}, + {41, 0x02, 221}, + {56, 0x03, 221}, + }, + /* 213 */ + { + {2, 0x02, 222}, + {9, 0x02, 222}, + {23, 0x02, 222}, + {40, 0x03, 222}, + {2, 0x02, 223}, + {9, 0x02, 223}, + {23, 0x02, 223}, + {40, 0x03, 223}, + {2, 0x02, 241}, + {9, 0x02, 241}, + {23, 0x02, 241}, + {40, 0x03, 241}, + {2, 0x02, 244}, + {9, 0x02, 244}, + {23, 0x02, 244}, + {40, 0x03, 244}, + }, + /* 214 */ + { + {3, 0x02, 222}, + {6, 0x02, 222}, + {10, 0x02, 222}, + {15, 0x02, 222}, + {24, 0x02, 222}, + {31, 0x02, 222}, + {41, 0x02, 222}, + {56, 0x03, 222}, + {3, 0x02, 223}, + {6, 0x02, 223}, + {10, 0x02, 223}, + {15, 0x02, 223}, + {24, 0x02, 223}, + {31, 0x02, 223}, + {41, 0x02, 223}, + {56, 0x03, 223}, + }, + /* 215 */ + { + {3, 0x02, 241}, + {6, 0x02, 241}, + {10, 0x02, 241}, + {15, 0x02, 241}, + {24, 0x02, 241}, + {31, 0x02, 241}, + {41, 0x02, 241}, + {56, 0x03, 241}, + {3, 0x02, 244}, + {6, 0x02, 244}, + {10, 0x02, 244}, + {15, 0x02, 244}, + {24, 0x02, 244}, + {31, 0x02, 244}, + {41, 0x02, 244}, + {56, 0x03, 244}, + }, + /* 216 */ + { + {1, 0x02, 245}, + {22, 0x03, 245}, + {1, 0x02, 246}, + {22, 0x03, 246}, + {1, 0x02, 247}, + {22, 0x03, 247}, + {1, 0x02, 248}, + {22, 0x03, 248}, + {1, 0x02, 250}, + {22, 0x03, 250}, + {1, 0x02, 251}, + {22, 0x03, 251}, + {1, 0x02, 252}, + {22, 0x03, 252}, + {1, 0x02, 253}, + {22, 0x03, 253}, + }, + /* 217 */ + { + {2, 0x02, 245}, + {9, 0x02, 245}, + {23, 0x02, 245}, + {40, 0x03, 245}, + {2, 0x02, 246}, + {9, 0x02, 246}, + {23, 0x02, 246}, + {40, 0x03, 246}, + {2, 0x02, 247}, + {9, 0x02, 247}, + {23, 0x02, 247}, + {40, 0x03, 247}, + {2, 0x02, 248}, + {9, 0x02, 248}, + {23, 0x02, 248}, + {40, 0x03, 248}, + }, + /* 218 */ + { + {3, 0x02, 245}, + {6, 0x02, 245}, + {10, 0x02, 245}, + {15, 0x02, 245}, + {24, 0x02, 245}, + {31, 0x02, 245}, + {41, 0x02, 245}, + {56, 0x03, 245}, + {3, 0x02, 246}, + {6, 0x02, 246}, + {10, 0x02, 246}, + {15, 0x02, 246}, + {24, 0x02, 246}, + {31, 0x02, 246}, + {41, 0x02, 246}, + {56, 0x03, 246}, + }, + /* 219 */ + { + {3, 0x02, 247}, + {6, 0x02, 247}, + {10, 0x02, 247}, + {15, 0x02, 247}, + {24, 0x02, 247}, + {31, 0x02, 247}, + {41, 0x02, 247}, + {56, 0x03, 247}, + {3, 0x02, 248}, + {6, 0x02, 248}, + {10, 0x02, 248}, + {15, 0x02, 248}, + {24, 0x02, 248}, + {31, 0x02, 248}, + {41, 0x02, 248}, + {56, 0x03, 248}, + }, + /* 220 */ + { + {2, 0x02, 250}, + {9, 0x02, 250}, + {23, 0x02, 250}, + {40, 0x03, 250}, + {2, 0x02, 251}, + {9, 0x02, 251}, + {23, 0x02, 251}, + {40, 0x03, 251}, + {2, 0x02, 252}, + {9, 0x02, 252}, + {23, 0x02, 252}, + {40, 0x03, 252}, + {2, 0x02, 253}, + {9, 0x02, 253}, + {23, 0x02, 253}, + {40, 0x03, 253}, + }, + /* 221 */ + { + {3, 0x02, 250}, + {6, 0x02, 250}, + {10, 0x02, 250}, + {15, 0x02, 250}, + {24, 0x02, 250}, + {31, 0x02, 250}, + {41, 0x02, 250}, + {56, 0x03, 250}, + {3, 0x02, 251}, + {6, 0x02, 251}, + {10, 0x02, 251}, + {15, 0x02, 251}, + {24, 0x02, 251}, + {31, 0x02, 251}, + {41, 0x02, 251}, + {56, 0x03, 251}, + }, + /* 222 */ + { + {3, 0x02, 252}, + {6, 0x02, 252}, + {10, 0x02, 252}, + {15, 0x02, 252}, + {24, 0x02, 252}, + {31, 0x02, 252}, + {41, 0x02, 252}, + {56, 0x03, 252}, + {3, 0x02, 253}, + {6, 0x02, 253}, + {10, 0x02, 253}, + {15, 0x02, 253}, + {24, 0x02, 253}, + {31, 0x02, 253}, + {41, 0x02, 253}, + {56, 0x03, 253}, + }, + /* 223 */ + { + {0, 0x03, 254}, + {227, 0x00, 0}, + {229, 0x00, 0}, + {230, 0x00, 0}, + {233, 0x00, 0}, + {234, 0x00, 0}, + {236, 0x00, 0}, + {237, 0x00, 0}, + {241, 0x00, 0}, + {242, 0x00, 0}, + {244, 0x00, 0}, + {245, 0x00, 0}, + {248, 0x00, 0}, + {249, 0x00, 0}, + {251, 0x00, 0}, + {252, 0x00, 0}, + }, + /* 224 */ + { + {1, 0x02, 254}, + {22, 0x03, 254}, + {0, 0x03, 2}, + {0, 0x03, 3}, + {0, 0x03, 4}, + {0, 0x03, 5}, + {0, 0x03, 6}, + {0, 0x03, 7}, + {0, 0x03, 8}, + {0, 0x03, 11}, + {0, 0x03, 12}, + {0, 0x03, 14}, + {0, 0x03, 15}, + {0, 0x03, 16}, + {0, 0x03, 17}, + {0, 0x03, 18}, + }, + /* 225 */ + { + {2, 0x02, 254}, + {9, 0x02, 254}, + {23, 0x02, 254}, + {40, 0x03, 254}, + {1, 0x02, 2}, + {22, 0x03, 2}, + {1, 0x02, 3}, + {22, 0x03, 3}, + {1, 0x02, 4}, + {22, 0x03, 4}, + {1, 0x02, 5}, + {22, 0x03, 5}, + {1, 0x02, 6}, + {22, 0x03, 6}, + {1, 0x02, 7}, + {22, 0x03, 7}, + }, + /* 226 */ + { + {3, 0x02, 254}, + {6, 0x02, 254}, + {10, 0x02, 254}, + {15, 0x02, 254}, + {24, 0x02, 254}, + {31, 0x02, 254}, + {41, 0x02, 254}, + {56, 0x03, 254}, + {2, 0x02, 2}, + {9, 0x02, 2}, + {23, 0x02, 2}, + {40, 0x03, 2}, + {2, 0x02, 3}, + {9, 0x02, 3}, + {23, 0x02, 3}, + {40, 0x03, 3}, + }, + /* 227 */ + { + {3, 0x02, 2}, + {6, 0x02, 2}, + {10, 0x02, 2}, + {15, 0x02, 2}, + {24, 0x02, 2}, + {31, 0x02, 2}, + {41, 0x02, 2}, + {56, 0x03, 2}, + {3, 0x02, 3}, + {6, 0x02, 3}, + {10, 0x02, 3}, + {15, 0x02, 3}, + {24, 0x02, 3}, + {31, 0x02, 3}, + {41, 0x02, 3}, + {56, 0x03, 3}, + }, + /* 228 */ + { + {2, 0x02, 4}, + {9, 0x02, 4}, + {23, 0x02, 4}, + {40, 0x03, 4}, + {2, 0x02, 5}, + {9, 0x02, 5}, + {23, 0x02, 5}, + {40, 0x03, 5}, + {2, 0x02, 6}, + {9, 0x02, 6}, + {23, 0x02, 6}, + {40, 0x03, 6}, + {2, 0x02, 7}, + {9, 0x02, 7}, + {23, 0x02, 7}, + {40, 0x03, 7}, + }, + /* 229 */ + { + {3, 0x02, 4}, + {6, 0x02, 4}, + {10, 0x02, 4}, + {15, 0x02, 4}, + {24, 0x02, 4}, + {31, 0x02, 4}, + {41, 0x02, 4}, + {56, 0x03, 4}, + {3, 0x02, 5}, + {6, 0x02, 5}, + {10, 0x02, 5}, + {15, 0x02, 5}, + {24, 0x02, 5}, + {31, 0x02, 5}, + {41, 0x02, 5}, + {56, 0x03, 5}, + }, + /* 230 */ + { + {3, 0x02, 6}, + {6, 0x02, 6}, + {10, 0x02, 6}, + {15, 0x02, 6}, + {24, 0x02, 6}, + {31, 0x02, 6}, + {41, 0x02, 6}, + {56, 0x03, 6}, + {3, 0x02, 7}, + {6, 0x02, 7}, + {10, 0x02, 7}, + {15, 0x02, 7}, + {24, 0x02, 7}, + {31, 0x02, 7}, + {41, 0x02, 7}, + {56, 0x03, 7}, + }, + /* 231 */ + { + {1, 0x02, 8}, + {22, 0x03, 8}, + {1, 0x02, 11}, + {22, 0x03, 11}, + {1, 0x02, 12}, + {22, 0x03, 12}, + {1, 0x02, 14}, + {22, 0x03, 14}, + {1, 0x02, 15}, + {22, 0x03, 15}, + {1, 0x02, 16}, + {22, 0x03, 16}, + {1, 0x02, 17}, + {22, 0x03, 17}, + {1, 0x02, 18}, + {22, 0x03, 18}, + }, + /* 232 */ + { + {2, 0x02, 8}, + {9, 0x02, 8}, + {23, 0x02, 8}, + {40, 0x03, 8}, + {2, 0x02, 11}, + {9, 0x02, 11}, + {23, 0x02, 11}, + {40, 0x03, 11}, + {2, 0x02, 12}, + {9, 0x02, 12}, + {23, 0x02, 12}, + {40, 0x03, 12}, + {2, 0x02, 14}, + {9, 0x02, 14}, + {23, 0x02, 14}, + {40, 0x03, 14}, + }, + /* 233 */ + { + {3, 0x02, 8}, + {6, 0x02, 8}, + {10, 0x02, 8}, + {15, 0x02, 8}, + {24, 0x02, 8}, + {31, 0x02, 8}, + {41, 0x02, 8}, + {56, 0x03, 8}, + {3, 0x02, 11}, + {6, 0x02, 11}, + {10, 0x02, 11}, + {15, 0x02, 11}, + {24, 0x02, 11}, + {31, 0x02, 11}, + {41, 0x02, 11}, + {56, 0x03, 11}, + }, + /* 234 */ + { + {3, 0x02, 12}, + {6, 0x02, 12}, + {10, 0x02, 12}, + {15, 0x02, 12}, + {24, 0x02, 12}, + {31, 0x02, 12}, + {41, 0x02, 12}, + {56, 0x03, 12}, + {3, 0x02, 14}, + {6, 0x02, 14}, + {10, 0x02, 14}, + {15, 0x02, 14}, + {24, 0x02, 14}, + {31, 0x02, 14}, + {41, 0x02, 14}, + {56, 0x03, 14}, + }, + /* 235 */ + { + {2, 0x02, 15}, + {9, 0x02, 15}, + {23, 0x02, 15}, + {40, 0x03, 15}, + {2, 0x02, 16}, + {9, 0x02, 16}, + {23, 0x02, 16}, + {40, 0x03, 16}, + {2, 0x02, 17}, + {9, 0x02, 17}, + {23, 0x02, 17}, + {40, 0x03, 17}, + {2, 0x02, 18}, + {9, 0x02, 18}, + {23, 0x02, 18}, + {40, 0x03, 18}, + }, + /* 236 */ + { + {3, 0x02, 15}, + {6, 0x02, 15}, + {10, 0x02, 15}, + {15, 0x02, 15}, + {24, 0x02, 15}, + {31, 0x02, 15}, + {41, 0x02, 15}, + {56, 0x03, 15}, + {3, 0x02, 16}, + {6, 0x02, 16}, + {10, 0x02, 16}, + {15, 0x02, 16}, + {24, 0x02, 16}, + {31, 0x02, 16}, + {41, 0x02, 16}, + {56, 0x03, 16}, + }, + /* 237 */ + { + {3, 0x02, 17}, + {6, 0x02, 17}, + {10, 0x02, 17}, + {15, 0x02, 17}, + {24, 0x02, 17}, + {31, 0x02, 17}, + {41, 0x02, 17}, + {56, 0x03, 17}, + {3, 0x02, 18}, + {6, 0x02, 18}, + {10, 0x02, 18}, + {15, 0x02, 18}, + {24, 0x02, 18}, + {31, 0x02, 18}, + {41, 0x02, 18}, + {56, 0x03, 18}, + }, + /* 238 */ + { + {0, 0x03, 19}, + {0, 0x03, 20}, + {0, 0x03, 21}, + {0, 0x03, 23}, + {0, 0x03, 24}, + {0, 0x03, 25}, + {0, 0x03, 26}, + {0, 0x03, 27}, + {0, 0x03, 28}, + {0, 0x03, 29}, + {0, 0x03, 30}, + {0, 0x03, 31}, + {0, 0x03, 127}, + {0, 0x03, 220}, + {0, 0x03, 249}, + {253, 0x00, 0}, + }, + /* 239 */ + { + {1, 0x02, 19}, + {22, 0x03, 19}, + {1, 0x02, 20}, + {22, 0x03, 20}, + {1, 0x02, 21}, + {22, 0x03, 21}, + {1, 0x02, 23}, + {22, 0x03, 23}, + {1, 0x02, 24}, + {22, 0x03, 24}, + {1, 0x02, 25}, + {22, 0x03, 25}, + {1, 0x02, 26}, + {22, 0x03, 26}, + {1, 0x02, 27}, + {22, 0x03, 27}, + }, + /* 240 */ + { + {2, 0x02, 19}, + {9, 0x02, 19}, + {23, 0x02, 19}, + {40, 0x03, 19}, + {2, 0x02, 20}, + {9, 0x02, 20}, + {23, 0x02, 20}, + {40, 0x03, 20}, + {2, 0x02, 21}, + {9, 0x02, 21}, + {23, 0x02, 21}, + {40, 0x03, 21}, + {2, 0x02, 23}, + {9, 0x02, 23}, + {23, 0x02, 23}, + {40, 0x03, 23}, + }, + /* 241 */ + { + {3, 0x02, 19}, + {6, 0x02, 19}, + {10, 0x02, 19}, + {15, 0x02, 19}, + {24, 0x02, 19}, + {31, 0x02, 19}, + {41, 0x02, 19}, + {56, 0x03, 19}, + {3, 0x02, 20}, + {6, 0x02, 20}, + {10, 0x02, 20}, + {15, 0x02, 20}, + {24, 0x02, 20}, + {31, 0x02, 20}, + {41, 0x02, 20}, + {56, 0x03, 20}, + }, + /* 242 */ + { + {3, 0x02, 21}, + {6, 0x02, 21}, + {10, 0x02, 21}, + {15, 0x02, 21}, + {24, 0x02, 21}, + {31, 0x02, 21}, + {41, 0x02, 21}, + {56, 0x03, 21}, + {3, 0x02, 23}, + {6, 0x02, 23}, + {10, 0x02, 23}, + {15, 0x02, 23}, + {24, 0x02, 23}, + {31, 0x02, 23}, + {41, 0x02, 23}, + {56, 0x03, 23}, + }, + /* 243 */ + { + {2, 0x02, 24}, + {9, 0x02, 24}, + {23, 0x02, 24}, + {40, 0x03, 24}, + {2, 0x02, 25}, + {9, 0x02, 25}, + {23, 0x02, 25}, + {40, 0x03, 25}, + {2, 0x02, 26}, + {9, 0x02, 26}, + {23, 0x02, 26}, + {40, 0x03, 26}, + {2, 0x02, 27}, + {9, 0x02, 27}, + {23, 0x02, 27}, + {40, 0x03, 27}, + }, + /* 244 */ + { + {3, 0x02, 24}, + {6, 0x02, 24}, + {10, 0x02, 24}, + {15, 0x02, 24}, + {24, 0x02, 24}, + {31, 0x02, 24}, + {41, 0x02, 24}, + {56, 0x03, 24}, + {3, 0x02, 25}, + {6, 0x02, 25}, + {10, 0x02, 25}, + {15, 0x02, 25}, + {24, 0x02, 25}, + {31, 0x02, 25}, + {41, 0x02, 25}, + {56, 0x03, 25}, + }, + /* 245 */ + { + {3, 0x02, 26}, + {6, 0x02, 26}, + {10, 0x02, 26}, + {15, 0x02, 26}, + {24, 0x02, 26}, + {31, 0x02, 26}, + {41, 0x02, 26}, + {56, 0x03, 26}, + {3, 0x02, 27}, + {6, 0x02, 27}, + {10, 0x02, 27}, + {15, 0x02, 27}, + {24, 0x02, 27}, + {31, 0x02, 27}, + {41, 0x02, 27}, + {56, 0x03, 27}, + }, + /* 246 */ + { + {1, 0x02, 28}, + {22, 0x03, 28}, + {1, 0x02, 29}, + {22, 0x03, 29}, + {1, 0x02, 30}, + {22, 0x03, 30}, + {1, 0x02, 31}, + {22, 0x03, 31}, + {1, 0x02, 127}, + {22, 0x03, 127}, + {1, 0x02, 220}, + {22, 0x03, 220}, + {1, 0x02, 249}, + {22, 0x03, 249}, + {254, 0x00, 0}, + {255, 0x00, 0}, + }, + /* 247 */ + { + {2, 0x02, 28}, + {9, 0x02, 28}, + {23, 0x02, 28}, + {40, 0x03, 28}, + {2, 0x02, 29}, + {9, 0x02, 29}, + {23, 0x02, 29}, + {40, 0x03, 29}, + {2, 0x02, 30}, + {9, 0x02, 30}, + {23, 0x02, 30}, + {40, 0x03, 30}, + {2, 0x02, 31}, + {9, 0x02, 31}, + {23, 0x02, 31}, + {40, 0x03, 31}, + }, + /* 248 */ + { + {3, 0x02, 28}, + {6, 0x02, 28}, + {10, 0x02, 28}, + {15, 0x02, 28}, + {24, 0x02, 28}, + {31, 0x02, 28}, + {41, 0x02, 28}, + {56, 0x03, 28}, + {3, 0x02, 29}, + {6, 0x02, 29}, + {10, 0x02, 29}, + {15, 0x02, 29}, + {24, 0x02, 29}, + {31, 0x02, 29}, + {41, 0x02, 29}, + {56, 0x03, 29}, + }, + /* 249 */ + { + {3, 0x02, 30}, + {6, 0x02, 30}, + {10, 0x02, 30}, + {15, 0x02, 30}, + {24, 0x02, 30}, + {31, 0x02, 30}, + {41, 0x02, 30}, + {56, 0x03, 30}, + {3, 0x02, 31}, + {6, 0x02, 31}, + {10, 0x02, 31}, + {15, 0x02, 31}, + {24, 0x02, 31}, + {31, 0x02, 31}, + {41, 0x02, 31}, + {56, 0x03, 31}, + }, + /* 250 */ + { + {2, 0x02, 127}, + {9, 0x02, 127}, + {23, 0x02, 127}, + {40, 0x03, 127}, + {2, 0x02, 220}, + {9, 0x02, 220}, + {23, 0x02, 220}, + {40, 0x03, 220}, + {2, 0x02, 249}, + {9, 0x02, 249}, + {23, 0x02, 249}, + {40, 0x03, 249}, + {0, 0x03, 10}, + {0, 0x03, 13}, + {0, 0x03, 22}, + {0, 0x04, 0}, + }, + /* 251 */ + { + {3, 0x02, 127}, + {6, 0x02, 127}, + {10, 0x02, 127}, + {15, 0x02, 127}, + {24, 0x02, 127}, + {31, 0x02, 127}, + {41, 0x02, 127}, + {56, 0x03, 127}, + {3, 0x02, 220}, + {6, 0x02, 220}, + {10, 0x02, 220}, + {15, 0x02, 220}, + {24, 0x02, 220}, + {31, 0x02, 220}, + {41, 0x02, 220}, + {56, 0x03, 220}, + }, + /* 252 */ + { + {3, 0x02, 249}, + {6, 0x02, 249}, + {10, 0x02, 249}, + {15, 0x02, 249}, + {24, 0x02, 249}, + {31, 0x02, 249}, + {41, 0x02, 249}, + {56, 0x03, 249}, + {1, 0x02, 10}, + {22, 0x03, 10}, + {1, 0x02, 13}, + {22, 0x03, 13}, + {1, 0x02, 22}, + {22, 0x03, 22}, + {0, 0x04, 0}, + {0, 0x04, 0}, + }, + /* 253 */ + { + {2, 0x02, 10}, + {9, 0x02, 10}, + {23, 0x02, 10}, + {40, 0x03, 10}, + {2, 0x02, 13}, + {9, 0x02, 13}, + {23, 0x02, 13}, + {40, 0x03, 13}, + {2, 0x02, 22}, + {9, 0x02, 22}, + {23, 0x02, 22}, + {40, 0x03, 22}, + {0, 0x04, 0}, + {0, 0x04, 0}, + {0, 0x04, 0}, + {0, 0x04, 0}, + }, + /* 254 */ + { + {3, 0x02, 10}, + {6, 0x02, 10}, + {10, 0x02, 10}, + {15, 0x02, 10}, + {24, 0x02, 10}, + {31, 0x02, 10}, + {41, 0x02, 10}, + {56, 0x03, 10}, + {3, 0x02, 13}, + {6, 0x02, 13}, + {10, 0x02, 13}, + {15, 0x02, 13}, + {24, 0x02, 13}, + {31, 0x02, 13}, + {41, 0x02, 13}, + {56, 0x03, 13}, + }, + /* 255 */ + { + {3, 0x02, 22}, + {6, 0x02, 22}, + {10, 0x02, 22}, + {15, 0x02, 22}, + {24, 0x02, 22}, + {31, 0x02, 22}, + {41, 0x02, 22}, + {56, 0x03, 22}, + {0, 0x04, 0}, + {0, 0x04, 0}, + {0, 0x04, 0}, + {0, 0x04, 0}, + {0, 0x04, 0}, + {0, 0x04, 0}, + {0, 0x04, 0}, + {0, 0x04, 0}, + }, +}; diff --git a/lib/nghttp2_helper.c b/lib/nghttp2_helper.c new file mode 100644 index 0000000..15f329c --- /dev/null +++ b/lib/nghttp2_helper.c @@ -0,0 +1,463 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "nghttp2_helper.h" + +#include +#include + +#include "nghttp2_net.h" + +void nghttp2_put_uint16be(uint8_t *buf, uint16_t n) { + uint16_t x = htons(n); + memcpy(buf, &x, sizeof(uint16_t)); +} + +void nghttp2_put_uint32be(uint8_t *buf, uint32_t n) { + uint32_t x = htonl(n); + memcpy(buf, &x, sizeof(uint32_t)); +} + +uint16_t nghttp2_get_uint16(const uint8_t *data) { + uint16_t n; + memcpy(&n, data, sizeof(uint16_t)); + return ntohs(n); +} + +uint32_t nghttp2_get_uint32(const uint8_t *data) { + uint32_t n; + memcpy(&n, data, sizeof(uint32_t)); + return ntohl(n); +} + +void *nghttp2_memdup(const void *src, size_t n, nghttp2_mem *mem) { + void *dest; + + if (n == 0) { + return NULL; + } + + dest = nghttp2_mem_malloc(mem, n); + if (dest == NULL) { + return NULL; + } + memcpy(dest, src, n); + return dest; +} + +/* Generated by gendowncasetbl.py */ +static const int DOWNCASE_TBL[] = { + 0 /* NUL */, 1 /* SOH */, 2 /* STX */, 3 /* ETX */, + 4 /* EOT */, 5 /* ENQ */, 6 /* ACK */, 7 /* BEL */, + 8 /* BS */, 9 /* HT */, 10 /* LF */, 11 /* VT */, + 12 /* FF */, 13 /* CR */, 14 /* SO */, 15 /* SI */, + 16 /* DLE */, 17 /* DC1 */, 18 /* DC2 */, 19 /* DC3 */, + 20 /* DC4 */, 21 /* NAK */, 22 /* SYN */, 23 /* ETB */, + 24 /* CAN */, 25 /* EM */, 26 /* SUB */, 27 /* ESC */, + 28 /* FS */, 29 /* GS */, 30 /* RS */, 31 /* US */, + 32 /* SPC */, 33 /* ! */, 34 /* " */, 35 /* # */, + 36 /* $ */, 37 /* % */, 38 /* & */, 39 /* ' */, + 40 /* ( */, 41 /* ) */, 42 /* * */, 43 /* + */, + 44 /* , */, 45 /* - */, 46 /* . */, 47 /* / */, + 48 /* 0 */, 49 /* 1 */, 50 /* 2 */, 51 /* 3 */, + 52 /* 4 */, 53 /* 5 */, 54 /* 6 */, 55 /* 7 */, + 56 /* 8 */, 57 /* 9 */, 58 /* : */, 59 /* ; */, + 60 /* < */, 61 /* = */, 62 /* > */, 63 /* ? */, + 64 /* @ */, 97 /* A */, 98 /* B */, 99 /* C */, + 100 /* D */, 101 /* E */, 102 /* F */, 103 /* G */, + 104 /* H */, 105 /* I */, 106 /* J */, 107 /* K */, + 108 /* L */, 109 /* M */, 110 /* N */, 111 /* O */, + 112 /* P */, 113 /* Q */, 114 /* R */, 115 /* S */, + 116 /* T */, 117 /* U */, 118 /* V */, 119 /* W */, + 120 /* X */, 121 /* Y */, 122 /* Z */, 91 /* [ */, + 92 /* \ */, 93 /* ] */, 94 /* ^ */, 95 /* _ */, + 96 /* ` */, 97 /* a */, 98 /* b */, 99 /* c */, + 100 /* d */, 101 /* e */, 102 /* f */, 103 /* g */, + 104 /* h */, 105 /* i */, 106 /* j */, 107 /* k */, + 108 /* l */, 109 /* m */, 110 /* n */, 111 /* o */, + 112 /* p */, 113 /* q */, 114 /* r */, 115 /* s */, + 116 /* t */, 117 /* u */, 118 /* v */, 119 /* w */, + 120 /* x */, 121 /* y */, 122 /* z */, 123 /* { */, + 124 /* | */, 125 /* } */, 126 /* ~ */, 127 /* DEL */, + 128 /* 0x80 */, 129 /* 0x81 */, 130 /* 0x82 */, 131 /* 0x83 */, + 132 /* 0x84 */, 133 /* 0x85 */, 134 /* 0x86 */, 135 /* 0x87 */, + 136 /* 0x88 */, 137 /* 0x89 */, 138 /* 0x8a */, 139 /* 0x8b */, + 140 /* 0x8c */, 141 /* 0x8d */, 142 /* 0x8e */, 143 /* 0x8f */, + 144 /* 0x90 */, 145 /* 0x91 */, 146 /* 0x92 */, 147 /* 0x93 */, + 148 /* 0x94 */, 149 /* 0x95 */, 150 /* 0x96 */, 151 /* 0x97 */, + 152 /* 0x98 */, 153 /* 0x99 */, 154 /* 0x9a */, 155 /* 0x9b */, + 156 /* 0x9c */, 157 /* 0x9d */, 158 /* 0x9e */, 159 /* 0x9f */, + 160 /* 0xa0 */, 161 /* 0xa1 */, 162 /* 0xa2 */, 163 /* 0xa3 */, + 164 /* 0xa4 */, 165 /* 0xa5 */, 166 /* 0xa6 */, 167 /* 0xa7 */, + 168 /* 0xa8 */, 169 /* 0xa9 */, 170 /* 0xaa */, 171 /* 0xab */, + 172 /* 0xac */, 173 /* 0xad */, 174 /* 0xae */, 175 /* 0xaf */, + 176 /* 0xb0 */, 177 /* 0xb1 */, 178 /* 0xb2 */, 179 /* 0xb3 */, + 180 /* 0xb4 */, 181 /* 0xb5 */, 182 /* 0xb6 */, 183 /* 0xb7 */, + 184 /* 0xb8 */, 185 /* 0xb9 */, 186 /* 0xba */, 187 /* 0xbb */, + 188 /* 0xbc */, 189 /* 0xbd */, 190 /* 0xbe */, 191 /* 0xbf */, + 192 /* 0xc0 */, 193 /* 0xc1 */, 194 /* 0xc2 */, 195 /* 0xc3 */, + 196 /* 0xc4 */, 197 /* 0xc5 */, 198 /* 0xc6 */, 199 /* 0xc7 */, + 200 /* 0xc8 */, 201 /* 0xc9 */, 202 /* 0xca */, 203 /* 0xcb */, + 204 /* 0xcc */, 205 /* 0xcd */, 206 /* 0xce */, 207 /* 0xcf */, + 208 /* 0xd0 */, 209 /* 0xd1 */, 210 /* 0xd2 */, 211 /* 0xd3 */, + 212 /* 0xd4 */, 213 /* 0xd5 */, 214 /* 0xd6 */, 215 /* 0xd7 */, + 216 /* 0xd8 */, 217 /* 0xd9 */, 218 /* 0xda */, 219 /* 0xdb */, + 220 /* 0xdc */, 221 /* 0xdd */, 222 /* 0xde */, 223 /* 0xdf */, + 224 /* 0xe0 */, 225 /* 0xe1 */, 226 /* 0xe2 */, 227 /* 0xe3 */, + 228 /* 0xe4 */, 229 /* 0xe5 */, 230 /* 0xe6 */, 231 /* 0xe7 */, + 232 /* 0xe8 */, 233 /* 0xe9 */, 234 /* 0xea */, 235 /* 0xeb */, + 236 /* 0xec */, 237 /* 0xed */, 238 /* 0xee */, 239 /* 0xef */, + 240 /* 0xf0 */, 241 /* 0xf1 */, 242 /* 0xf2 */, 243 /* 0xf3 */, + 244 /* 0xf4 */, 245 /* 0xf5 */, 246 /* 0xf6 */, 247 /* 0xf7 */, + 248 /* 0xf8 */, 249 /* 0xf9 */, 250 /* 0xfa */, 251 /* 0xfb */, + 252 /* 0xfc */, 253 /* 0xfd */, 254 /* 0xfe */, 255 /* 0xff */, +}; + +void nghttp2_downcase(uint8_t *s, size_t len) { + size_t i; + for (i = 0; i < len; ++i) { + s[i] = DOWNCASE_TBL[s[i]]; + } +} + +/* + * local_window_size + * ^ * + * | * recv_window_size + * | * * ^ + * | * * | + * 0+++++++++ + * | * * \ + * | * * | This rage is hidden in flow control. But it must be + * v * * / kept in order to restore it when window size is enlarged. + * recv_reduction + * (+ for negative direction) + * + * recv_window_size could be negative if we decrease + * local_window_size more than recv_window_size: + * + * local_window_size + * ^ * + * | * + * | * + * 0++++++++ + * | * ^ recv_window_size (negative) + * | * | + * v * * + * recv_reduction + */ +int nghttp2_adjust_local_window_size(int32_t *local_window_size_ptr, + int32_t *recv_window_size_ptr, + int32_t *recv_reduction_ptr, + int32_t *delta_ptr) { + if (*delta_ptr > 0) { + int32_t recv_reduction_delta; + int32_t delta; + int32_t new_recv_window_size = + nghttp2_max(0, *recv_window_size_ptr) - *delta_ptr; + + if (new_recv_window_size >= 0) { + *recv_window_size_ptr = new_recv_window_size; + return 0; + } + + delta = -new_recv_window_size; + + /* The delta size is strictly more than received bytes. Increase + local_window_size by that difference |delta|. */ + if (*local_window_size_ptr > NGHTTP2_MAX_WINDOW_SIZE - delta) { + return NGHTTP2_ERR_FLOW_CONTROL; + } + *local_window_size_ptr += delta; + /* If there is recv_reduction due to earlier window_size + reduction, we have to adjust it too. */ + recv_reduction_delta = nghttp2_min(*recv_reduction_ptr, delta); + *recv_reduction_ptr -= recv_reduction_delta; + if (*recv_window_size_ptr < 0) { + *recv_window_size_ptr += recv_reduction_delta; + } else { + /* If *recv_window_size_ptr > 0, then those bytes are going to + be returned to the remote peer (by WINDOW_UPDATE with the + adjusted *delta_ptr), so it is effectively 0 now. We set to + *recv_reduction_delta, because caller does not take into + account it in *delta_ptr. */ + *recv_window_size_ptr = recv_reduction_delta; + } + /* recv_reduction_delta must be paied from *delta_ptr, since it + was added in window size reduction (see below). */ + *delta_ptr -= recv_reduction_delta; + + return 0; + } + + if (*local_window_size_ptr + *delta_ptr < 0 || + *recv_window_size_ptr < INT32_MIN - *delta_ptr || + *recv_reduction_ptr > INT32_MAX + *delta_ptr) { + return NGHTTP2_ERR_FLOW_CONTROL; + } + /* Decreasing local window size. Note that we achieve this without + noticing to the remote peer. To do this, we cut + recv_window_size by -delta. This means that we don't send + WINDOW_UPDATE for -delta bytes. */ + *local_window_size_ptr += *delta_ptr; + *recv_window_size_ptr += *delta_ptr; + *recv_reduction_ptr -= *delta_ptr; + *delta_ptr = 0; + + return 0; +} + +int nghttp2_should_send_window_update(int32_t local_window_size, + int32_t recv_window_size) { + return recv_window_size >= local_window_size / 2; +} + +const char *nghttp2_strerror(int error_code) { + switch (error_code) { + case 0: + return "Success"; + case NGHTTP2_ERR_INVALID_ARGUMENT: + return "Invalid argument"; + case NGHTTP2_ERR_BUFFER_ERROR: + return "Out of buffer space"; + case NGHTTP2_ERR_UNSUPPORTED_VERSION: + return "Unsupported SPDY version"; + case NGHTTP2_ERR_WOULDBLOCK: + return "Operation would block"; + case NGHTTP2_ERR_PROTO: + return "Protocol error"; + case NGHTTP2_ERR_INVALID_FRAME: + return "Invalid frame octets"; + case NGHTTP2_ERR_EOF: + return "EOF"; + case NGHTTP2_ERR_DEFERRED: + return "Data transfer deferred"; + case NGHTTP2_ERR_STREAM_ID_NOT_AVAILABLE: + return "No more Stream ID available"; + case NGHTTP2_ERR_STREAM_CLOSED: + return "Stream was already closed or invalid"; + case NGHTTP2_ERR_STREAM_CLOSING: + return "Stream is closing"; + case NGHTTP2_ERR_STREAM_SHUT_WR: + return "The transmission is not allowed for this stream"; + case NGHTTP2_ERR_INVALID_STREAM_ID: + return "Stream ID is invalid"; + case NGHTTP2_ERR_INVALID_STREAM_STATE: + return "Invalid stream state"; + case NGHTTP2_ERR_DEFERRED_DATA_EXIST: + return "Another DATA frame has already been deferred"; + case NGHTTP2_ERR_START_STREAM_NOT_ALLOWED: + return "request HEADERS is not allowed"; + case NGHTTP2_ERR_GOAWAY_ALREADY_SENT: + return "GOAWAY has already been sent"; + case NGHTTP2_ERR_INVALID_HEADER_BLOCK: + return "Invalid header block"; + case NGHTTP2_ERR_INVALID_STATE: + return "Invalid state"; + case NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE: + return "The user callback function failed due to the temporal error"; + case NGHTTP2_ERR_FRAME_SIZE_ERROR: + return "The length of the frame is invalid"; + case NGHTTP2_ERR_HEADER_COMP: + return "Header compression/decompression error"; + case NGHTTP2_ERR_FLOW_CONTROL: + return "Flow control error"; + case NGHTTP2_ERR_INSUFF_BUFSIZE: + return "Insufficient buffer size given to function"; + case NGHTTP2_ERR_PAUSE: + return "Callback was paused by the application"; + case NGHTTP2_ERR_TOO_MANY_INFLIGHT_SETTINGS: + return "Too many inflight SETTINGS"; + case NGHTTP2_ERR_PUSH_DISABLED: + return "Server push is disabled by peer"; + case NGHTTP2_ERR_DATA_EXIST: + return "DATA frame already exists"; + case NGHTTP2_ERR_SESSION_CLOSING: + return "The current session is closing"; + case NGHTTP2_ERR_HTTP_HEADER: + return "Invalid HTTP header field was received"; + case NGHTTP2_ERR_HTTP_MESSAGING: + return "Violation in HTTP messaging rule"; + case NGHTTP2_ERR_REFUSED_STREAM: + return "Stream was refused"; + case NGHTTP2_ERR_INTERNAL: + return "Internal error"; + case NGHTTP2_ERR_NOMEM: + return "Out of memory"; + case NGHTTP2_ERR_CALLBACK_FAILURE: + return "The user callback function failed"; + case NGHTTP2_ERR_BAD_CLIENT_MAGIC: + return "Received bad clinet magic byte string"; + default: + return "Unknown error code"; + } +} + +/* Generated by gennmchartbl.py */ +static int VALID_HD_NAME_CHARS[] = { + 0 /* NUL */, 0 /* SOH */, 0 /* STX */, 0 /* ETX */, 0 /* EOT */, + 0 /* ENQ */, 0 /* ACK */, 0 /* BEL */, 0 /* BS */, 0 /* HT */, + 0 /* LF */, 0 /* VT */, 0 /* FF */, 0 /* CR */, 0 /* SO */, + 0 /* SI */, 0 /* DLE */, 0 /* DC1 */, 0 /* DC2 */, 0 /* DC3 */, + 0 /* DC4 */, 0 /* NAK */, 0 /* SYN */, 0 /* ETB */, 0 /* CAN */, + 0 /* EM */, 0 /* SUB */, 0 /* ESC */, 0 /* FS */, 0 /* GS */, + 0 /* RS */, 0 /* US */, 0 /* SPC */, 1 /* ! */, 0 /* " */, + 1 /* # */, 1 /* $ */, 1 /* % */, 1 /* & */, 1 /* ' */, + 0 /* ( */, 0 /* ) */, 1 /* * */, 1 /* + */, 0 /* , */, + 1 /* - */, 1 /* . */, 0 /* / */, 1 /* 0 */, 1 /* 1 */, + 1 /* 2 */, 1 /* 3 */, 1 /* 4 */, 1 /* 5 */, 1 /* 6 */, + 1 /* 7 */, 1 /* 8 */, 1 /* 9 */, 0 /* : */, 0 /* ; */, + 0 /* < */, 0 /* = */, 0 /* > */, 0 /* ? */, 0 /* @ */, + 0 /* A */, 0 /* B */, 0 /* C */, 0 /* D */, 0 /* E */, + 0 /* F */, 0 /* G */, 0 /* H */, 0 /* I */, 0 /* J */, + 0 /* K */, 0 /* L */, 0 /* M */, 0 /* N */, 0 /* O */, + 0 /* P */, 0 /* Q */, 0 /* R */, 0 /* S */, 0 /* T */, + 0 /* U */, 0 /* V */, 0 /* W */, 0 /* X */, 0 /* Y */, + 0 /* Z */, 0 /* [ */, 0 /* \ */, 0 /* ] */, 1 /* ^ */, + 1 /* _ */, 1 /* ` */, 1 /* a */, 1 /* b */, 1 /* c */, + 1 /* d */, 1 /* e */, 1 /* f */, 1 /* g */, 1 /* h */, + 1 /* i */, 1 /* j */, 1 /* k */, 1 /* l */, 1 /* m */, + 1 /* n */, 1 /* o */, 1 /* p */, 1 /* q */, 1 /* r */, + 1 /* s */, 1 /* t */, 1 /* u */, 1 /* v */, 1 /* w */, + 1 /* x */, 1 /* y */, 1 /* z */, 0 /* { */, 1 /* | */, + 0 /* } */, 1 /* ~ */, 0 /* DEL */, 0 /* 0x80 */, 0 /* 0x81 */, + 0 /* 0x82 */, 0 /* 0x83 */, 0 /* 0x84 */, 0 /* 0x85 */, 0 /* 0x86 */, + 0 /* 0x87 */, 0 /* 0x88 */, 0 /* 0x89 */, 0 /* 0x8a */, 0 /* 0x8b */, + 0 /* 0x8c */, 0 /* 0x8d */, 0 /* 0x8e */, 0 /* 0x8f */, 0 /* 0x90 */, + 0 /* 0x91 */, 0 /* 0x92 */, 0 /* 0x93 */, 0 /* 0x94 */, 0 /* 0x95 */, + 0 /* 0x96 */, 0 /* 0x97 */, 0 /* 0x98 */, 0 /* 0x99 */, 0 /* 0x9a */, + 0 /* 0x9b */, 0 /* 0x9c */, 0 /* 0x9d */, 0 /* 0x9e */, 0 /* 0x9f */, + 0 /* 0xa0 */, 0 /* 0xa1 */, 0 /* 0xa2 */, 0 /* 0xa3 */, 0 /* 0xa4 */, + 0 /* 0xa5 */, 0 /* 0xa6 */, 0 /* 0xa7 */, 0 /* 0xa8 */, 0 /* 0xa9 */, + 0 /* 0xaa */, 0 /* 0xab */, 0 /* 0xac */, 0 /* 0xad */, 0 /* 0xae */, + 0 /* 0xaf */, 0 /* 0xb0 */, 0 /* 0xb1 */, 0 /* 0xb2 */, 0 /* 0xb3 */, + 0 /* 0xb4 */, 0 /* 0xb5 */, 0 /* 0xb6 */, 0 /* 0xb7 */, 0 /* 0xb8 */, + 0 /* 0xb9 */, 0 /* 0xba */, 0 /* 0xbb */, 0 /* 0xbc */, 0 /* 0xbd */, + 0 /* 0xbe */, 0 /* 0xbf */, 0 /* 0xc0 */, 0 /* 0xc1 */, 0 /* 0xc2 */, + 0 /* 0xc3 */, 0 /* 0xc4 */, 0 /* 0xc5 */, 0 /* 0xc6 */, 0 /* 0xc7 */, + 0 /* 0xc8 */, 0 /* 0xc9 */, 0 /* 0xca */, 0 /* 0xcb */, 0 /* 0xcc */, + 0 /* 0xcd */, 0 /* 0xce */, 0 /* 0xcf */, 0 /* 0xd0 */, 0 /* 0xd1 */, + 0 /* 0xd2 */, 0 /* 0xd3 */, 0 /* 0xd4 */, 0 /* 0xd5 */, 0 /* 0xd6 */, + 0 /* 0xd7 */, 0 /* 0xd8 */, 0 /* 0xd9 */, 0 /* 0xda */, 0 /* 0xdb */, + 0 /* 0xdc */, 0 /* 0xdd */, 0 /* 0xde */, 0 /* 0xdf */, 0 /* 0xe0 */, + 0 /* 0xe1 */, 0 /* 0xe2 */, 0 /* 0xe3 */, 0 /* 0xe4 */, 0 /* 0xe5 */, + 0 /* 0xe6 */, 0 /* 0xe7 */, 0 /* 0xe8 */, 0 /* 0xe9 */, 0 /* 0xea */, + 0 /* 0xeb */, 0 /* 0xec */, 0 /* 0xed */, 0 /* 0xee */, 0 /* 0xef */, + 0 /* 0xf0 */, 0 /* 0xf1 */, 0 /* 0xf2 */, 0 /* 0xf3 */, 0 /* 0xf4 */, + 0 /* 0xf5 */, 0 /* 0xf6 */, 0 /* 0xf7 */, 0 /* 0xf8 */, 0 /* 0xf9 */, + 0 /* 0xfa */, 0 /* 0xfb */, 0 /* 0xfc */, 0 /* 0xfd */, 0 /* 0xfe */, + 0 /* 0xff */ +}; + +int nghttp2_check_header_name(const uint8_t *name, size_t len) { + const uint8_t *last; + if (len == 0) { + return 0; + } + if (*name == ':') { + if (len == 1) { + return 0; + } + ++name; + --len; + } + for (last = name + len; name != last; ++name) { + if (!VALID_HD_NAME_CHARS[*name]) { + return 0; + } + } + return 1; +} + +/* Generated by genvchartbl.py */ +static int VALID_HD_VALUE_CHARS[] = { + 0 /* NUL */, 0 /* SOH */, 0 /* STX */, 0 /* ETX */, 0 /* EOT */, + 0 /* ENQ */, 0 /* ACK */, 0 /* BEL */, 0 /* BS */, 1 /* HT */, + 0 /* LF */, 0 /* VT */, 0 /* FF */, 0 /* CR */, 0 /* SO */, + 0 /* SI */, 0 /* DLE */, 0 /* DC1 */, 0 /* DC2 */, 0 /* DC3 */, + 0 /* DC4 */, 0 /* NAK */, 0 /* SYN */, 0 /* ETB */, 0 /* CAN */, + 0 /* EM */, 0 /* SUB */, 0 /* ESC */, 0 /* FS */, 0 /* GS */, + 0 /* RS */, 0 /* US */, 1 /* SPC */, 1 /* ! */, 1 /* " */, + 1 /* # */, 1 /* $ */, 1 /* % */, 1 /* & */, 1 /* ' */, + 1 /* ( */, 1 /* ) */, 1 /* * */, 1 /* + */, 1 /* , */, + 1 /* - */, 1 /* . */, 1 /* / */, 1 /* 0 */, 1 /* 1 */, + 1 /* 2 */, 1 /* 3 */, 1 /* 4 */, 1 /* 5 */, 1 /* 6 */, + 1 /* 7 */, 1 /* 8 */, 1 /* 9 */, 1 /* : */, 1 /* ; */, + 1 /* < */, 1 /* = */, 1 /* > */, 1 /* ? */, 1 /* @ */, + 1 /* A */, 1 /* B */, 1 /* C */, 1 /* D */, 1 /* E */, + 1 /* F */, 1 /* G */, 1 /* H */, 1 /* I */, 1 /* J */, + 1 /* K */, 1 /* L */, 1 /* M */, 1 /* N */, 1 /* O */, + 1 /* P */, 1 /* Q */, 1 /* R */, 1 /* S */, 1 /* T */, + 1 /* U */, 1 /* V */, 1 /* W */, 1 /* X */, 1 /* Y */, + 1 /* Z */, 1 /* [ */, 1 /* \ */, 1 /* ] */, 1 /* ^ */, + 1 /* _ */, 1 /* ` */, 1 /* a */, 1 /* b */, 1 /* c */, + 1 /* d */, 1 /* e */, 1 /* f */, 1 /* g */, 1 /* h */, + 1 /* i */, 1 /* j */, 1 /* k */, 1 /* l */, 1 /* m */, + 1 /* n */, 1 /* o */, 1 /* p */, 1 /* q */, 1 /* r */, + 1 /* s */, 1 /* t */, 1 /* u */, 1 /* v */, 1 /* w */, + 1 /* x */, 1 /* y */, 1 /* z */, 1 /* { */, 1 /* | */, + 1 /* } */, 1 /* ~ */, 0 /* DEL */, 1 /* 0x80 */, 1 /* 0x81 */, + 1 /* 0x82 */, 1 /* 0x83 */, 1 /* 0x84 */, 1 /* 0x85 */, 1 /* 0x86 */, + 1 /* 0x87 */, 1 /* 0x88 */, 1 /* 0x89 */, 1 /* 0x8a */, 1 /* 0x8b */, + 1 /* 0x8c */, 1 /* 0x8d */, 1 /* 0x8e */, 1 /* 0x8f */, 1 /* 0x90 */, + 1 /* 0x91 */, 1 /* 0x92 */, 1 /* 0x93 */, 1 /* 0x94 */, 1 /* 0x95 */, + 1 /* 0x96 */, 1 /* 0x97 */, 1 /* 0x98 */, 1 /* 0x99 */, 1 /* 0x9a */, + 1 /* 0x9b */, 1 /* 0x9c */, 1 /* 0x9d */, 1 /* 0x9e */, 1 /* 0x9f */, + 1 /* 0xa0 */, 1 /* 0xa1 */, 1 /* 0xa2 */, 1 /* 0xa3 */, 1 /* 0xa4 */, + 1 /* 0xa5 */, 1 /* 0xa6 */, 1 /* 0xa7 */, 1 /* 0xa8 */, 1 /* 0xa9 */, + 1 /* 0xaa */, 1 /* 0xab */, 1 /* 0xac */, 1 /* 0xad */, 1 /* 0xae */, + 1 /* 0xaf */, 1 /* 0xb0 */, 1 /* 0xb1 */, 1 /* 0xb2 */, 1 /* 0xb3 */, + 1 /* 0xb4 */, 1 /* 0xb5 */, 1 /* 0xb6 */, 1 /* 0xb7 */, 1 /* 0xb8 */, + 1 /* 0xb9 */, 1 /* 0xba */, 1 /* 0xbb */, 1 /* 0xbc */, 1 /* 0xbd */, + 1 /* 0xbe */, 1 /* 0xbf */, 1 /* 0xc0 */, 1 /* 0xc1 */, 1 /* 0xc2 */, + 1 /* 0xc3 */, 1 /* 0xc4 */, 1 /* 0xc5 */, 1 /* 0xc6 */, 1 /* 0xc7 */, + 1 /* 0xc8 */, 1 /* 0xc9 */, 1 /* 0xca */, 1 /* 0xcb */, 1 /* 0xcc */, + 1 /* 0xcd */, 1 /* 0xce */, 1 /* 0xcf */, 1 /* 0xd0 */, 1 /* 0xd1 */, + 1 /* 0xd2 */, 1 /* 0xd3 */, 1 /* 0xd4 */, 1 /* 0xd5 */, 1 /* 0xd6 */, + 1 /* 0xd7 */, 1 /* 0xd8 */, 1 /* 0xd9 */, 1 /* 0xda */, 1 /* 0xdb */, + 1 /* 0xdc */, 1 /* 0xdd */, 1 /* 0xde */, 1 /* 0xdf */, 1 /* 0xe0 */, + 1 /* 0xe1 */, 1 /* 0xe2 */, 1 /* 0xe3 */, 1 /* 0xe4 */, 1 /* 0xe5 */, + 1 /* 0xe6 */, 1 /* 0xe7 */, 1 /* 0xe8 */, 1 /* 0xe9 */, 1 /* 0xea */, + 1 /* 0xeb */, 1 /* 0xec */, 1 /* 0xed */, 1 /* 0xee */, 1 /* 0xef */, + 1 /* 0xf0 */, 1 /* 0xf1 */, 1 /* 0xf2 */, 1 /* 0xf3 */, 1 /* 0xf4 */, + 1 /* 0xf5 */, 1 /* 0xf6 */, 1 /* 0xf7 */, 1 /* 0xf8 */, 1 /* 0xf9 */, + 1 /* 0xfa */, 1 /* 0xfb */, 1 /* 0xfc */, 1 /* 0xfd */, 1 /* 0xfe */, + 1 /* 0xff */ +}; + +int nghttp2_check_header_value(const uint8_t *value, size_t len) { + const uint8_t *last; + for (last = value + len; value != last; ++value) { + if (!VALID_HD_VALUE_CHARS[*value]) { + return 0; + } + } + return 1; +} + +uint8_t *nghttp2_cpymem(uint8_t *dest, const void *src, size_t len) { + memcpy(dest, src, len); + + return dest + len; +} diff --git a/lib/nghttp2_helper.h b/lib/nghttp2_helper.h new file mode 100644 index 0000000..0a7f8c8 --- /dev/null +++ b/lib/nghttp2_helper.h @@ -0,0 +1,114 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGHTTP2_HELPER_H +#define NGHTTP2_HELPER_H + +#ifdef HAVE_CONFIG_H +#include +#endif /* HAVE_CONFIG_H */ + +#include + +#include +#include "nghttp2_mem.h" + +#define nghttp2_min(A, B) ((A) < (B) ? (A) : (B)) +#define nghttp2_max(A, B) ((A) > (B) ? (A) : (B)) + +#define lstreq(A, B, N) ((sizeof((A)) - 1) == (N) && memcmp((A), (B), (N)) == 0) + +/* + * Copies 2 byte unsigned integer |n| in host byte order to |buf| in + * network byte order. + */ +void nghttp2_put_uint16be(uint8_t *buf, uint16_t n); + +/* + * Copies 4 byte unsigned integer |n| in host byte order to |buf| in + * network byte order. + */ +void nghttp2_put_uint32be(uint8_t *buf, uint32_t n); + +/* + * Retrieves 2 byte unsigned integer stored in |data| in network byte + * order and returns it in host byte order. + */ +uint16_t nghttp2_get_uint16(const uint8_t *data); + +/* + * Retrieves 4 byte unsigned integer stored in |data| in network byte + * order and returns it in host byte order. + */ +uint32_t nghttp2_get_uint32(const uint8_t *data); + +/* + * Allocates |n| bytes of memory and copy the memory region pointed by + * |src| with the length |n| bytes into it. Returns the allocated memory. + * + * This function returns pointer to allocated memory, or one of the + * following negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + */ +void *nghttp2_memdup(const void *src, size_t n, nghttp2_mem *mem); + +void nghttp2_downcase(uint8_t *s, size_t len); + +/* + * Adjusts |*local_window_size_ptr|, |*recv_window_size_ptr|, + * |*recv_reduction_ptr| with |*delta_ptr| which is the + * WINDOW_UPDATE's window_size_increment sent from local side. If + * |delta| is strictly larger than |*recv_window_size_ptr|, + * |*local_window_size_ptr| is increased by delta - + * *recv_window_size_ptr. If |delta| is negative, + * |*local_window_size_ptr| is decreased by delta. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_FLOW_CONTROL + * local_window_size overflow or gets negative. + */ +int nghttp2_adjust_local_window_size(int32_t *local_window_size_ptr, + int32_t *recv_window_size_ptr, + int32_t *recv_reduction_ptr, + int32_t *delta_ptr); + +/* + * Returns non-zero if the function decided that WINDOW_UPDATE should + * be sent. + */ +int nghttp2_should_send_window_update(int32_t local_window_size, + int32_t recv_window_size); + +/* + * Copies the buffer |src| of length |len| to the destination pointed + * by the |dest|, assuming that the |dest| is at lest |len| bytes long + * . Returns dest + len. + */ +uint8_t *nghttp2_cpymem(uint8_t *dest, const void *src, size_t len); + +#endif /* NGHTTP2_HELPER_H */ diff --git a/lib/nghttp2_http.c b/lib/nghttp2_http.c new file mode 100644 index 0000000..695307e --- /dev/null +++ b/lib/nghttp2_http.c @@ -0,0 +1,459 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2015 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "nghttp2_http.h" + +#include +#include +#include + +#include "nghttp2_hd.h" +#include "nghttp2_helper.h" + +static char downcase(char c) { + return 'A' <= c && c <= 'Z' ? (c - 'A' + 'a') : c; +} + +static int memieq(const void *a, const void *b, size_t n) { + size_t i; + const uint8_t *aa = a, *bb = b; + + for (i = 0; i < n; ++i) { + if (downcase(aa[i]) != downcase(bb[i])) { + return 0; + } + } + return 1; +} + +#define lstrieq(A, B, N) ((sizeof((A)) - 1) == (N) && memieq((A), (B), (N))) + +static int64_t parse_uint(const uint8_t *s, size_t len) { + int64_t n = 0; + size_t i; + if (len == 0) { + return -1; + } + for (i = 0; i < len; ++i) { + if ('0' <= s[i] && s[i] <= '9') { + if (n > INT64_MAX / 10) { + return -1; + } + n *= 10; + if (n > INT64_MAX - (s[i] - '0')) { + return -1; + } + n += s[i] - '0'; + continue; + } + return -1; + } + return n; +} + +static int lws(const uint8_t *s, size_t n) { + size_t i; + for (i = 0; i < n; ++i) { + if (s[i] != ' ' && s[i] != '\t') { + return 0; + } + } + return 1; +} + +static int check_pseudo_header(nghttp2_stream *stream, const nghttp2_nv *nv, + int flag) { + if (stream->http_flags & flag) { + return 0; + } + if (lws(nv->value, nv->valuelen)) { + return 0; + } + stream->http_flags |= flag; + return 1; +} + +static int expect_response_body(nghttp2_stream *stream) { + return (stream->http_flags & NGHTTP2_HTTP_FLAG_METH_HEAD) == 0 && + stream->status_code / 100 != 1 && stream->status_code != 304 && + stream->status_code != 204; +} + +/* For "http" or "https" URIs, OPTIONS request may have "*" in :path + header field to represent system-wide OPTIONS request. Otherwise, + :path header field value must start with "/". This function must + be called after ":method" header field was received. This function + returns nonzero if path is valid.*/ +static int check_path(nghttp2_stream *stream) { + return (stream->http_flags & NGHTTP2_HTTP_FLAG_SCHEME_HTTP) == 0 || + ((stream->http_flags & NGHTTP2_HTTP_FLAG_PATH_REGULAR) || + ((stream->http_flags & NGHTTP2_HTTP_FLAG_METH_OPTIONS) && + (stream->http_flags & NGHTTP2_HTTP_FLAG_PATH_ASTERISK))); +} + +static int http_request_on_header(nghttp2_stream *stream, nghttp2_nv *nv, + int token, int trailer) { + if (nv->name[0] == ':') { + if (trailer || + (stream->http_flags & NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED)) { + return NGHTTP2_ERR_HTTP_HEADER; + } + } + + switch (token) { + case NGHTTP2_TOKEN__AUTHORITY: + if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__AUTHORITY)) { + return NGHTTP2_ERR_HTTP_HEADER; + } + break; + case NGHTTP2_TOKEN__METHOD: + if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__METHOD)) { + return NGHTTP2_ERR_HTTP_HEADER; + } + switch (nv->valuelen) { + case 4: + if (lstreq("HEAD", nv->value, nv->valuelen)) { + stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_HEAD; + } + break; + case 7: + switch (nv->value[6]) { + case 'T': + if (lstreq("CONNECT", nv->value, nv->valuelen)) { + if (stream->stream_id % 2 == 0) { + /* we won't allow CONNECT for push */ + return NGHTTP2_ERR_HTTP_HEADER; + } + stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_CONNECT; + if (stream->http_flags & + (NGHTTP2_HTTP_FLAG__PATH | NGHTTP2_HTTP_FLAG__SCHEME)) { + return NGHTTP2_ERR_HTTP_HEADER; + } + } + break; + case 'S': + if (lstreq("OPTIONS", nv->value, nv->valuelen)) { + stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_OPTIONS; + } + break; + } + break; + } + break; + case NGHTTP2_TOKEN__PATH: + if (stream->http_flags & NGHTTP2_HTTP_FLAG_METH_CONNECT) { + return NGHTTP2_ERR_HTTP_HEADER; + } + if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__PATH)) { + return NGHTTP2_ERR_HTTP_HEADER; + } + if (nv->value[0] == '/') { + stream->http_flags |= NGHTTP2_HTTP_FLAG_PATH_REGULAR; + } else if (nv->valuelen == 1 && nv->value[0] == '*') { + stream->http_flags |= NGHTTP2_HTTP_FLAG_PATH_ASTERISK; + } + break; + case NGHTTP2_TOKEN__SCHEME: + if (stream->http_flags & NGHTTP2_HTTP_FLAG_METH_CONNECT) { + return NGHTTP2_ERR_HTTP_HEADER; + } + if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__SCHEME)) { + return NGHTTP2_ERR_HTTP_HEADER; + } + if ((nv->valuelen == 4 && memieq("http", nv->value, 4)) || + (nv->valuelen == 5 && memieq("https", nv->value, 5))) { + stream->http_flags |= NGHTTP2_HTTP_FLAG_SCHEME_HTTP; + } + break; + case NGHTTP2_TOKEN_HOST: + if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG_HOST)) { + return NGHTTP2_ERR_HTTP_HEADER; + } + break; + case NGHTTP2_TOKEN_CONTENT_LENGTH: { + if (stream->content_length != -1) { + return NGHTTP2_ERR_HTTP_HEADER; + } + stream->content_length = parse_uint(nv->value, nv->valuelen); + if (stream->content_length == -1) { + return NGHTTP2_ERR_HTTP_HEADER; + } + break; + } + /* disallowed header fields */ + case NGHTTP2_TOKEN_CONNECTION: + case NGHTTP2_TOKEN_KEEP_ALIVE: + case NGHTTP2_TOKEN_PROXY_CONNECTION: + case NGHTTP2_TOKEN_TRANSFER_ENCODING: + case NGHTTP2_TOKEN_UPGRADE: + return NGHTTP2_ERR_HTTP_HEADER; + case NGHTTP2_TOKEN_TE: + if (!lstrieq("trailers", nv->value, nv->valuelen)) { + return NGHTTP2_ERR_HTTP_HEADER; + } + break; + default: + if (nv->name[0] == ':') { + return NGHTTP2_ERR_HTTP_HEADER; + } + } + + if (nv->name[0] != ':') { + stream->http_flags |= NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED; + } + + return 0; +} + +static int http_response_on_header(nghttp2_stream *stream, nghttp2_nv *nv, + int token, int trailer) { + if (nv->name[0] == ':') { + if (trailer || + (stream->http_flags & NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED)) { + return NGHTTP2_ERR_HTTP_HEADER; + } + } + + switch (token) { + case NGHTTP2_TOKEN__STATUS: { + if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__STATUS)) { + return NGHTTP2_ERR_HTTP_HEADER; + } + if (nv->valuelen != 3) { + return NGHTTP2_ERR_HTTP_HEADER; + } + stream->status_code = parse_uint(nv->value, nv->valuelen); + if (stream->status_code == -1) { + return NGHTTP2_ERR_HTTP_HEADER; + } + break; + } + case NGHTTP2_TOKEN_CONTENT_LENGTH: { + if (stream->content_length != -1) { + return NGHTTP2_ERR_HTTP_HEADER; + } + stream->content_length = parse_uint(nv->value, nv->valuelen); + if (stream->content_length == -1) { + return NGHTTP2_ERR_HTTP_HEADER; + } + break; + } + /* disallowed header fields */ + case NGHTTP2_TOKEN_CONNECTION: + case NGHTTP2_TOKEN_KEEP_ALIVE: + case NGHTTP2_TOKEN_PROXY_CONNECTION: + case NGHTTP2_TOKEN_TRANSFER_ENCODING: + case NGHTTP2_TOKEN_UPGRADE: + return NGHTTP2_ERR_HTTP_HEADER; + case NGHTTP2_TOKEN_TE: + if (!lstrieq("trailers", nv->value, nv->valuelen)) { + return NGHTTP2_ERR_HTTP_HEADER; + } + break; + default: + if (nv->name[0] == ':') { + return NGHTTP2_ERR_HTTP_HEADER; + } + } + + if (nv->name[0] != ':') { + stream->http_flags |= NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED; + } + + return 0; +} + +int nghttp2_http_on_header(nghttp2_session *session, nghttp2_stream *stream, + nghttp2_frame *frame, nghttp2_nv *nv, int token, + int trailer) { + /* We are strict for pseudo header field. One bad character should + lead to fail. OTOH, we should be a bit forgiving for regular + headers, since existing public internet has so much illegal + headers floating around and if we kill the stream because of + this, we may disrupt many web sites and/or libraries. So we + become conservative here, and just ignore those illegal regular + headers. */ + if (!nghttp2_check_header_name(nv->name, nv->namelen)) { + size_t i; + if (nv->namelen > 0 && nv->name[0] == ':') { + return NGHTTP2_ERR_HTTP_HEADER; + } + /* header field name must be lower-cased without exception */ + for (i = 0; i < nv->namelen; ++i) { + char c = nv->name[i]; + if ('A' <= c && c <= 'Z') { + return NGHTTP2_ERR_HTTP_HEADER; + } + } + /* When ignoring regular headers, we set this flag so that we + still enforce header field ordering rule for pseudo header + fields. */ + stream->http_flags |= NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED; + return NGHTTP2_ERR_IGN_HTTP_HEADER; + } + + if (!nghttp2_check_header_value(nv->value, nv->valuelen)) { + assert(nv->namelen > 0); + if (nv->name[0] == ':') { + return NGHTTP2_ERR_HTTP_HEADER; + } + /* When ignoring regular headers, we set this flag so that we + still enforce header field ordering rule for pseudo header + fields. */ + stream->http_flags |= NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED; + return NGHTTP2_ERR_IGN_HTTP_HEADER; + } + + if (session->server || frame->hd.type == NGHTTP2_PUSH_PROMISE) { + return http_request_on_header(stream, nv, token, trailer); + } + + return http_response_on_header(stream, nv, token, trailer); +} + +int nghttp2_http_on_request_headers(nghttp2_stream *stream, + nghttp2_frame *frame) { + if (stream->http_flags & NGHTTP2_HTTP_FLAG_METH_CONNECT) { + if ((stream->http_flags & NGHTTP2_HTTP_FLAG__AUTHORITY) == 0) { + return -1; + } + stream->content_length = -1; + } else { + if ((stream->http_flags & NGHTTP2_HTTP_FLAG_REQ_HEADERS) != + NGHTTP2_HTTP_FLAG_REQ_HEADERS || + (stream->http_flags & + (NGHTTP2_HTTP_FLAG__AUTHORITY | NGHTTP2_HTTP_FLAG_HOST)) == 0) { + return -1; + } + if (!check_path(stream)) { + return -1; + } + } + + if (frame->hd.type == NGHTTP2_PUSH_PROMISE) { + /* we are going to reuse data fields for upcoming response. Clear + them now, except for method flags. */ + stream->http_flags &= NGHTTP2_HTTP_FLAG_METH_ALL; + stream->content_length = -1; + } + + return 0; +} + +int nghttp2_http_on_response_headers(nghttp2_stream *stream) { + if ((stream->http_flags & NGHTTP2_HTTP_FLAG__STATUS) == 0) { + return -1; + } + + if (stream->status_code / 100 == 1) { + /* non-final response */ + stream->http_flags = (stream->http_flags & NGHTTP2_HTTP_FLAG_METH_ALL) | + NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE; + stream->content_length = -1; + stream->status_code = -1; + return 0; + } + + stream->http_flags &= ~NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE; + + if (!expect_response_body(stream)) { + stream->content_length = 0; + } else if (stream->http_flags & NGHTTP2_HTTP_FLAG_METH_CONNECT) { + stream->content_length = -1; + } + + return 0; +} + +int nghttp2_http_on_trailer_headers(nghttp2_stream *stream _U_, + nghttp2_frame *frame) { + if ((frame->hd.flags & NGHTTP2_FLAG_END_STREAM) == 0) { + return -1; + } + + return 0; +} + +int nghttp2_http_on_remote_end_stream(nghttp2_stream *stream) { + if (stream->http_flags & NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE) { + return -1; + } + + if (stream->content_length != -1 && + stream->content_length != stream->recv_content_length) { + return -1; + } + + return 0; +} + +int nghttp2_http_on_data_chunk(nghttp2_stream *stream, size_t n) { + stream->recv_content_length += n; + + if ((stream->http_flags & NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE) || + (stream->content_length != -1 && + stream->recv_content_length > stream->content_length)) { + return -1; + } + + return 0; +} + +void nghttp2_http_record_request_method(nghttp2_stream *stream, + nghttp2_frame *frame) { + const nghttp2_nv *nva; + size_t nvlen; + size_t i; + + switch (frame->hd.type) { + case NGHTTP2_HEADERS: + nva = frame->headers.nva; + nvlen = frame->headers.nvlen; + break; + case NGHTTP2_PUSH_PROMISE: + nva = frame->push_promise.nva; + nvlen = frame->push_promise.nvlen; + break; + default: + return; + } + + /* TODO we should do this strictly. */ + for (i = 0; i < nvlen; ++i) { + const nghttp2_nv *nv = &nva[i]; + if (!(nv->namelen == 7 && nv->name[6] == 'd' && + memcmp(":metho", nv->name, nv->namelen - 1) == 0)) { + continue; + } + if (lstreq("CONNECT", nv->value, nv->valuelen)) { + stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_CONNECT; + return; + } + if (lstreq("HEAD", nv->value, nv->valuelen)) { + stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_HEAD; + return; + } + return; + } +} diff --git a/lib/nghttp2_http.h b/lib/nghttp2_http.h new file mode 100644 index 0000000..f782058 --- /dev/null +++ b/lib/nghttp2_http.h @@ -0,0 +1,98 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2015 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGHTTP2_HTTP_H +#define NGHTTP2_HTTP_H + +#ifdef HAVE_CONFIG_H +#include +#endif /* HAVE_CONFIG_H */ + +#include +#include "nghttp2_session.h" +#include "nghttp2_stream.h" + +/* + * This function is called when HTTP header field |nv| in |frame| is + * received for |stream|. This function will validate |nv| against + * the current state of stream. The |token| is nghttp2_token value + * for nv->name, or -1 if we don't have enum value for the name. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_HTTP_HEADER + * Invalid HTTP header field was received. + * NGHTTP2_ERR_IGN_HTTP_HEADER + * Invalid HTTP header field was received but it can be treated as + * if it was not received because of compatibility reasons. + */ +int nghttp2_http_on_header(nghttp2_session *session, nghttp2_stream *stream, + nghttp2_frame *frame, nghttp2_nv *nv, int token, + int trailer); + +/* + * This function is called when request header is received. This + * function performs validation and returns 0 if it succeeds, or -1. + */ +int nghttp2_http_on_request_headers(nghttp2_stream *stream, + nghttp2_frame *frame); + +/* + * This function is called when response header is received. This + * function performs validation and returns 0 if it succeeds, or -1. + */ +int nghttp2_http_on_response_headers(nghttp2_stream *stream); + +/* + * This function is called trailer header (for both request and + * response) is received. This function performs validation and + * returns 0 if it succeeds, or -1. + */ +int nghttp2_http_on_trailer_headers(nghttp2_stream *stream, + nghttp2_frame *frame); + +/* + * This function is called when END_STREAM flag is seen in incoming + * frame. This function performs validation and returns 0 if it + * succeeds, or -1. + */ +int nghttp2_http_on_remote_end_stream(nghttp2_stream *stream); + +/* + * This function is called when chunk of data is received. This + * function performs validation and returns 0 if it succeeds, or -1. + */ +int nghttp2_http_on_data_chunk(nghttp2_stream *stream, size_t n); + +/* + * This function inspects header field in |frame| and records its + * method in stream->http_flags. If frame->hd.type is neither + * NGHTTP2_HEADERS nor NGHTTP2_PUSH_PROMISE, this function does + * nothing. + */ +void nghttp2_http_record_request_method(nghttp2_stream *stream, + nghttp2_frame *frame); + +#endif /* NGHTTP2_HTTP_H */ diff --git a/lib/nghttp2_int.h b/lib/nghttp2_int.h new file mode 100644 index 0000000..c26c8e9 --- /dev/null +++ b/lib/nghttp2_int.h @@ -0,0 +1,58 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGHTTP2_INT_H +#define NGHTTP2_INT_H + +#ifdef HAVE_CONFIG_H +#include +#endif /* HAVE_CONFIG_H */ + +/* Macros, types and constants for internal use */ + +#ifdef DEBUGBUILD +#define DEBUGF(x) x +#else +#define DEBUGF(x) \ + do { \ + } while (0) +#endif + +/* "less" function, return nonzero if |lhs| is less than |rhs|. */ +typedef int (*nghttp2_less)(const void *lhs, const void *rhs); + +/* Internal error code. They must be in the range [-499, -100], + inclusive. */ +typedef enum { + NGHTTP2_ERR_CREDENTIAL_PENDING = -101, + NGHTTP2_ERR_IGN_HEADER_BLOCK = -103, + NGHTTP2_ERR_IGN_PAYLOAD = -104, + /* + * Invalid HTTP header field was received but it can be treated as + * if it was not received because of compatibility reasons. + */ + NGHTTP2_ERR_IGN_HTTP_HEADER = -105 +} nghttp2_internal_error; + +#endif /* NGHTTP2_INT_H */ diff --git a/lib/nghttp2_map.c b/lib/nghttp2_map.c new file mode 100644 index 0000000..5a38ee2 --- /dev/null +++ b/lib/nghttp2_map.c @@ -0,0 +1,190 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "nghttp2_map.h" + +#include + +#define INITIAL_TABLE_LENGTH 256 + +int nghttp2_map_init(nghttp2_map *map, nghttp2_mem *mem) { + map->mem = mem; + map->tablelen = INITIAL_TABLE_LENGTH; + map->table = + nghttp2_mem_calloc(mem, map->tablelen, sizeof(nghttp2_map_entry *)); + if (map->table == NULL) { + return NGHTTP2_ERR_NOMEM; + } + + map->size = 0; + + return 0; +} + +void nghttp2_map_free(nghttp2_map *map) { + nghttp2_mem_free(map->mem, map->table); +} + +void nghttp2_map_each_free(nghttp2_map *map, + int (*func)(nghttp2_map_entry *entry, void *ptr), + void *ptr) { + size_t i; + for (i = 0; i < map->tablelen; ++i) { + nghttp2_map_entry *entry; + for (entry = map->table[i]; entry;) { + nghttp2_map_entry *next = entry->next; + func(entry, ptr); + entry = next; + } + map->table[i] = NULL; + } +} + +int nghttp2_map_each(nghttp2_map *map, + int (*func)(nghttp2_map_entry *entry, void *ptr), + void *ptr) { + int rv; + size_t i; + for (i = 0; i < map->tablelen; ++i) { + nghttp2_map_entry *entry; + for (entry = map->table[i]; entry; entry = entry->next) { + rv = func(entry, ptr); + if (rv != 0) { + return rv; + } + } + } + return 0; +} + +void nghttp2_map_entry_init(nghttp2_map_entry *entry, key_type key) { + entry->key = key; + entry->next = NULL; +} + +/* Same hash function in android HashMap source code. */ +/* The |mod| must be power of 2 */ +static int32_t hash(int32_t h, size_t mod) { + h ^= (h >> 20) ^ (h >> 12); + h ^= (h >> 7) ^ (h >> 4); + return h & (mod - 1); +} + +static int insert(nghttp2_map_entry **table, size_t tablelen, + nghttp2_map_entry *entry) { + int32_t h = hash(entry->key, tablelen); + if (table[h] == NULL) { + table[h] = entry; + } else { + nghttp2_map_entry *p; + /* We won't allow duplicated key, so check it out. */ + for (p = table[h]; p; p = p->next) { + if (p->key == entry->key) { + return NGHTTP2_ERR_INVALID_ARGUMENT; + } + } + entry->next = table[h]; + table[h] = entry; + } + return 0; +} + +/* new_tablelen must be power of 2 */ +static int resize(nghttp2_map *map, size_t new_tablelen) { + size_t i; + nghttp2_map_entry **new_table; + + new_table = + nghttp2_mem_calloc(map->mem, new_tablelen, sizeof(nghttp2_map_entry *)); + if (new_table == NULL) { + return NGHTTP2_ERR_NOMEM; + } + + for (i = 0; i < map->tablelen; ++i) { + nghttp2_map_entry *entry; + for (entry = map->table[i]; entry;) { + nghttp2_map_entry *next = entry->next; + entry->next = NULL; + /* This function must succeed */ + insert(new_table, new_tablelen, entry); + entry = next; + } + } + nghttp2_mem_free(map->mem, map->table); + map->tablelen = new_tablelen; + map->table = new_table; + + return 0; +} + +int nghttp2_map_insert(nghttp2_map *map, nghttp2_map_entry *new_entry) { + int rv; + /* Load factor is 0.75 */ + if ((map->size + 1) * 4 > map->tablelen * 3) { + rv = resize(map, map->tablelen * 2); + if (rv != 0) { + return rv; + } + } + rv = insert(map->table, map->tablelen, new_entry); + if (rv != 0) { + return rv; + } + ++map->size; + return 0; +} + +nghttp2_map_entry *nghttp2_map_find(nghttp2_map *map, key_type key) { + int32_t h; + nghttp2_map_entry *entry; + h = hash(key, map->tablelen); + for (entry = map->table[h]; entry; entry = entry->next) { + if (entry->key == key) { + return entry; + } + } + return NULL; +} + +int nghttp2_map_remove(nghttp2_map *map, key_type key) { + int32_t h; + nghttp2_map_entry *entry, *prev; + h = hash(key, map->tablelen); + prev = NULL; + for (entry = map->table[h]; entry; entry = entry->next) { + if (entry->key == key) { + if (prev == NULL) { + map->table[h] = entry->next; + } else { + prev->next = entry->next; + } + --map->size; + return 0; + } + prev = entry; + } + return NGHTTP2_ERR_INVALID_ARGUMENT; +} + +size_t nghttp2_map_size(nghttp2_map *map) { return map->size; } diff --git a/lib/nghttp2_map.h b/lib/nghttp2_map.h new file mode 100644 index 0000000..83f425d --- /dev/null +++ b/lib/nghttp2_map.h @@ -0,0 +1,144 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGHTTP2_MAP_H +#define NGHTTP2_MAP_H + +#ifdef HAVE_CONFIG_H +#include +#endif /* HAVE_CONFIG_H */ + +#include +#include "nghttp2_int.h" +#include "nghttp2_mem.h" + +/* Implementation of unordered map */ + +typedef uint32_t key_type; + +typedef struct nghttp2_map_entry { + struct nghttp2_map_entry *next; + key_type key; +#if SIZEOF_INT_P == 4 + /* we requires 8 bytes aligment */ + int64_t pad; +#endif +} nghttp2_map_entry; + +typedef struct { + nghttp2_map_entry **table; + nghttp2_mem *mem; + size_t tablelen; + size_t size; +} nghttp2_map; + +/* + * Initializes the map |map|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory + */ +int nghttp2_map_init(nghttp2_map *map, nghttp2_mem *mem); + +/* + * Deallocates any resources allocated for |map|. The stored entries + * are not freed by this function. Use nghttp2_map_each_free() to free + * each entries. + */ +void nghttp2_map_free(nghttp2_map *map); + +/* + * Deallocates each entries using |func| function and any resources + * allocated for |map|. The |func| function is responsible for freeing + * given the |entry| object. The |ptr| will be passed to the |func| as + * send argument. The return value of the |func| will be ignored. + */ +void nghttp2_map_each_free(nghttp2_map *map, + int (*func)(nghttp2_map_entry *entry, void *ptr), + void *ptr); + +/* + * Initializes the |entry| with the |key|. All entries to be inserted + * to the map must be initialized with this function. + */ +void nghttp2_map_entry_init(nghttp2_map_entry *entry, key_type key); + +/* + * Inserts the new |entry| with the key |entry->key| to the map |map|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_INVALID_ARGUMENT + * The item associated by |key| already exists. + * NGHTTP2_ERR_NOMEM + * Out of memory + */ +int nghttp2_map_insert(nghttp2_map *map, nghttp2_map_entry *entry); + +/* + * Returns the entry associated by the key |key|. If there is no such + * entry, this function returns NULL. + */ +nghttp2_map_entry *nghttp2_map_find(nghttp2_map *map, key_type key); + +/* + * Removes the entry associated by the key |key| from the |map|. The + * removed entry is not freed by this function. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_INVALID_ARGUMENT + * The entry associated by |key| does not exist. + */ +int nghttp2_map_remove(nghttp2_map *map, key_type key); + +/* + * Returns the number of items stored in the map |map|. + */ +size_t nghttp2_map_size(nghttp2_map *map); + +/* + * Applies the function |func| to each entry in the |map| with the + * optional user supplied pointer |ptr|. + * + * If the |func| returns 0, this function calls the |func| with the + * next entry. If the |func| returns nonzero, it will not call the + * |func| for further entries and return the return value of the + * |func| immediately. Thus, this function returns 0 if all the + * invocations of the |func| return 0, or nonzero value which the last + * invocation of |func| returns. + * + * Don't use this function to free each entry. Use + * nghttp2_map_each_free() instead. + */ +int nghttp2_map_each(nghttp2_map *map, + int (*func)(nghttp2_map_entry *entry, void *ptr), + void *ptr); + +#endif /* NGHTTP2_MAP_H */ diff --git a/lib/nghttp2_mem.c b/lib/nghttp2_mem.c new file mode 100644 index 0000000..e7d5aae --- /dev/null +++ b/lib/nghttp2_mem.c @@ -0,0 +1,61 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2014 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "nghttp2_mem.h" + +static void *default_malloc(size_t size, void *mem_user_data _U_) { + return malloc(size); +} + +static void default_free(void *ptr, void *mem_user_data _U_) { free(ptr); } + +static void *default_calloc(size_t nmemb, size_t size, + void *mem_user_data _U_) { + return calloc(nmemb, size); +} + +static void *default_realloc(void *ptr, size_t size, void *mem_user_data _U_) { + return realloc(ptr, size); +} + +static nghttp2_mem mem_default = {NULL, default_malloc, default_free, + default_calloc, default_realloc}; + +nghttp2_mem *nghttp2_mem_default(void) { return &mem_default; } + +void *nghttp2_mem_malloc(nghttp2_mem *mem, size_t size) { + return mem->malloc(size, mem->mem_user_data); +} + +void nghttp2_mem_free(nghttp2_mem *mem, void *ptr) { + mem->free(ptr, mem->mem_user_data); +} + +void *nghttp2_mem_calloc(nghttp2_mem *mem, size_t nmemb, size_t size) { + return mem->calloc(nmemb, size, mem->mem_user_data); +} + +void *nghttp2_mem_realloc(nghttp2_mem *mem, void *ptr, size_t size) { + return mem->realloc(ptr, size, mem->mem_user_data); +} diff --git a/lib/nghttp2_mem.h b/lib/nghttp2_mem.h new file mode 100644 index 0000000..d1fded4 --- /dev/null +++ b/lib/nghttp2_mem.h @@ -0,0 +1,44 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2014 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGHTTP2_MEM_H +#define NGHTTP2_MEM_H + +#ifdef HAVE_CONFIG_H +#include +#endif /* HAVE_CONFIG_H */ + +#include + +/* The default, system standard memory allocator */ +nghttp2_mem *nghttp2_mem_default(void); + +/* Convenient wrapper functions to call allocator function in + |mem|. */ +void *nghttp2_mem_malloc(nghttp2_mem *mem, size_t size); +void nghttp2_mem_free(nghttp2_mem *mem, void *ptr); +void *nghttp2_mem_calloc(nghttp2_mem *mem, size_t nmemb, size_t size); +void *nghttp2_mem_realloc(nghttp2_mem *mem, void *ptr, size_t size); + +#endif /* NGHTTP2_MEM_H */ diff --git a/lib/nghttp2_net.h b/lib/nghttp2_net.h new file mode 100644 index 0000000..587f418 --- /dev/null +++ b/lib/nghttp2_net.h @@ -0,0 +1,91 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGHTTP2_NET_H +#define NGHTTP2_NET_H + +#ifdef HAVE_CONFIG_H +#include +#endif /* HAVE_CONFIG_H */ + +#ifdef HAVE_ARPA_INET_H +#include +#endif /* HAVE_ARPA_INET_H */ + +#ifdef HAVE_NETINET_IN_H +#include +#endif /* HAVE_NETINET_IN_H */ + +#include + +#if defined(WIN32) +/* Windows requires ws2_32 library for ntonl family functions. We + define inline functions for those function so that we don't have + dependeny on that lib. */ + +#ifdef _MSC_VER +#define STIN static __inline +#else +#define STIN static inline +#endif + +STIN uint32_t htonl(uint32_t hostlong) { + uint32_t res; + unsigned char *p = (unsigned char *)&res; + *p++ = hostlong >> 24; + *p++ = (hostlong >> 16) & 0xffu; + *p++ = (hostlong >> 8) & 0xffu; + *p = hostlong & 0xffu; + return res; +} + +STIN uint16_t htons(uint16_t hostshort) { + uint16_t res; + unsigned char *p = (unsigned char *)&res; + *p++ = hostshort >> 8; + *p = hostshort & 0xffu; + return res; +} + +STIN uint32_t ntohl(uint32_t netlong) { + uint32_t res; + unsigned char *p = (unsigned char *)&netlong; + res = *p++ << 24; + res += *p++ << 16; + res += *p++ << 8; + res += *p; + return res; +} + +STIN uint16_t ntohs(uint16_t netshort) { + uint16_t res; + unsigned char *p = (unsigned char *)&netshort; + res = *p++ << 8; + res += *p; + return res; +} + +#endif /* WIN32 */ + +#endif /* NGHTTP2_NET_H */ diff --git a/lib/nghttp2_npn.c b/lib/nghttp2_npn.c new file mode 100644 index 0000000..a8bdb23 --- /dev/null +++ b/lib/nghttp2_npn.c @@ -0,0 +1,57 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "nghttp2_npn.h" + +#include + +static int select_next_protocol(unsigned char **out, unsigned char *outlen, + const unsigned char *in, unsigned int inlen, + const char *key, unsigned int keylen) { + unsigned int i; + for (i = 0; i + keylen <= inlen; i += in [i] + 1) { + if (memcmp(&in[i], key, keylen) == 0) { + *out = (unsigned char *)&in[i + 1]; + *outlen = in[i]; + return 0; + } + } + return -1; +} + +#define NGHTTP2_HTTP_1_1_ALPN "\x8http/1.1" +#define NGHTTP2_HTTP_1_1_ALPN_LEN (sizeof(NGHTTP2_HTTP_1_1_ALPN) - 1) + +int nghttp2_select_next_protocol(unsigned char **out, unsigned char *outlen, + const unsigned char *in, unsigned int inlen) { + if (select_next_protocol(out, outlen, in, inlen, NGHTTP2_PROTO_ALPN, + NGHTTP2_PROTO_ALPN_LEN) == 0) { + return 1; + } + if (select_next_protocol(out, outlen, in, inlen, NGHTTP2_HTTP_1_1_ALPN, + NGHTTP2_HTTP_1_1_ALPN_LEN) == 0) { + return 0; + } + return -1; +} diff --git a/lib/nghttp2_npn.h b/lib/nghttp2_npn.h new file mode 100644 index 0000000..c4bdedb --- /dev/null +++ b/lib/nghttp2_npn.h @@ -0,0 +1,34 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGHTTP2_NPN_H +#define NGHTTP2_NPN_H + +#ifdef HAVE_CONFIG +#include +#endif /* HAVE_CONFIG */ + +#include + +#endif /* NGHTTP2_NPN_H */ diff --git a/lib/nghttp2_option.c b/lib/nghttp2_option.c new file mode 100644 index 0000000..a45b5e0 --- /dev/null +++ b/lib/nghttp2_option.c @@ -0,0 +1,58 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2014 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "nghttp2_option.h" + +int nghttp2_option_new(nghttp2_option **option_ptr) { + *option_ptr = calloc(1, sizeof(nghttp2_option)); + + if (*option_ptr == NULL) { + return NGHTTP2_ERR_NOMEM; + } + + return 0; +} + +void nghttp2_option_del(nghttp2_option *option) { free(option); } + +void nghttp2_option_set_no_auto_window_update(nghttp2_option *option, int val) { + option->opt_set_mask |= NGHTTP2_OPT_NO_AUTO_WINDOW_UPDATE; + option->no_auto_window_update = val; +} + +void nghttp2_option_set_peer_max_concurrent_streams(nghttp2_option *option, + uint32_t val) { + option->opt_set_mask |= NGHTTP2_OPT_PEER_MAX_CONCURRENT_STREAMS; + option->peer_max_concurrent_streams = val; +} + +void nghttp2_option_set_no_recv_client_magic(nghttp2_option *option, int val) { + option->opt_set_mask |= NGHTTP2_OPT_NO_RECV_CLIENT_MAGIC; + option->no_recv_client_magic = val; +} + +void nghttp2_option_set_no_http_messaging(nghttp2_option *option, int val) { + option->opt_set_mask |= NGHTTP2_OPT_NO_HTTP_MESSAGING; + option->no_http_messaging = val; +} diff --git a/lib/nghttp2_option.h b/lib/nghttp2_option.h new file mode 100644 index 0000000..739bd85 --- /dev/null +++ b/lib/nghttp2_option.h @@ -0,0 +1,91 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2014 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGHTTP2_OPTION_H +#define NGHTTP2_OPTION_H + +#ifdef HAVE_CONFIG_H +#include +#endif /* HAVE_CONFIG_H */ + +#include + +/** + * Configuration options + */ +typedef enum { + /** + * This option prevents the library from sending WINDOW_UPDATE for a + * connection automatically. If this option is set to nonzero, the + * library won't send WINDOW_UPDATE for DATA until application calls + * nghttp2_session_consume() to indicate the amount of consumed + * DATA. By default, this option is set to zero. + */ + NGHTTP2_OPT_NO_AUTO_WINDOW_UPDATE = 1, + /** + * This option sets the SETTINGS_MAX_CONCURRENT_STREAMS value of + * remote endpoint as if it is received in SETTINGS frame. Without + * specifying this option, before the local endpoint receives + * SETTINGS_MAX_CONCURRENT_STREAMS in SETTINGS frame from remote + * endpoint, SETTINGS_MAX_CONCURRENT_STREAMS is unlimited. This may + * cause problem if local endpoint submits lots of requests + * initially and sending them at once to the remote peer may lead to + * the rejection of some requests. Specifying this option to the + * sensible value, say 100, may avoid this kind of issue. This value + * will be overwritten if the local endpoint receives + * SETTINGS_MAX_CONCURRENT_STREAMS from the remote endpoint. + */ + NGHTTP2_OPT_PEER_MAX_CONCURRENT_STREAMS = 1 << 1, + NGHTTP2_OPT_NO_RECV_CLIENT_MAGIC = 1 << 2, + NGHTTP2_OPT_NO_HTTP_MESSAGING = 1 << 3 +} nghttp2_option_flag; + +/** + * Struct to store option values for nghttp2_session. + */ +struct nghttp2_option { + /** + * Bitwise OR of nghttp2_option_flag to determine that which fields + * are specified. + */ + uint32_t opt_set_mask; + /** + * NGHTTP2_OPT_PEER_MAX_CONCURRENT_STREAMS + */ + uint32_t peer_max_concurrent_streams; + /** + * NGHTTP2_OPT_NO_AUTO_WINDOW_UPDATE + */ + uint8_t no_auto_window_update; + /** + * NGHTTP2_OPT_NO_RECV_CLIENT_MAGIC + */ + uint8_t no_recv_client_magic; + /** + * NGHTTP2_OPT_NO_HTTP_MESSAGING + */ + uint8_t no_http_messaging; +}; + +#endif /* NGHTTP2_OPTION_H */ diff --git a/lib/nghttp2_outbound_item.c b/lib/nghttp2_outbound_item.c new file mode 100644 index 0000000..c4ecab9 --- /dev/null +++ b/lib/nghttp2_outbound_item.c @@ -0,0 +1,105 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "nghttp2_outbound_item.h" + +#include +#include + +void nghttp2_outbound_item_init(nghttp2_outbound_item *item) { + item->cycle = 0; + item->qnext = NULL; + item->queued = 0; + + memset(&item->aux_data, 0, sizeof(nghttp2_aux_data)); +} + +void nghttp2_outbound_item_free(nghttp2_outbound_item *item, nghttp2_mem *mem) { + nghttp2_frame *frame; + + if (item == NULL) { + return; + } + + frame = &item->frame; + + switch (frame->hd.type) { + case NGHTTP2_DATA: + nghttp2_frame_data_free(&frame->data); + break; + case NGHTTP2_HEADERS: + nghttp2_frame_headers_free(&frame->headers, mem); + break; + case NGHTTP2_PRIORITY: + nghttp2_frame_priority_free(&frame->priority); + break; + case NGHTTP2_RST_STREAM: + nghttp2_frame_rst_stream_free(&frame->rst_stream); + break; + case NGHTTP2_SETTINGS: + nghttp2_frame_settings_free(&frame->settings, mem); + break; + case NGHTTP2_PUSH_PROMISE: + nghttp2_frame_push_promise_free(&frame->push_promise, mem); + break; + case NGHTTP2_PING: + nghttp2_frame_ping_free(&frame->ping); + break; + case NGHTTP2_GOAWAY: + nghttp2_frame_goaway_free(&frame->goaway, mem); + break; + case NGHTTP2_WINDOW_UPDATE: + nghttp2_frame_window_update_free(&frame->window_update); + break; + } +} + +void nghttp2_outbound_queue_init(nghttp2_outbound_queue *q) { + q->head = q->tail = NULL; + q->n = 0; +} + +void nghttp2_outbound_queue_push(nghttp2_outbound_queue *q, + nghttp2_outbound_item *item) { + if (q->tail) { + q->tail = q->tail->qnext = item; + } else { + q->head = q->tail = item; + } + ++q->n; +} + +void nghttp2_outbound_queue_pop(nghttp2_outbound_queue *q) { + nghttp2_outbound_item *item; + if (!q->head) { + return; + } + item = q->head; + q->head = q->head->qnext; + item->qnext = NULL; + if (!q->head) { + q->tail = NULL; + } + --q->n; +} diff --git a/lib/nghttp2_outbound_item.h b/lib/nghttp2_outbound_item.h new file mode 100644 index 0000000..0be5cb7 --- /dev/null +++ b/lib/nghttp2_outbound_item.h @@ -0,0 +1,157 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGHTTP2_OUTBOUND_ITEM_H +#define NGHTTP2_OUTBOUND_ITEM_H + +#ifdef HAVE_CONFIG_H +#include +#endif /* HAVE_CONFIG_H */ + +#include +#include "nghttp2_frame.h" +#include "nghttp2_mem.h" + +/* struct used for HEADERS and PUSH_PROMISE frame */ +typedef struct { + nghttp2_data_provider data_prd; + void *stream_user_data; + /* error code when request HEADERS is canceled by RST_STREAM while + it is in queue. */ + uint32_t error_code; + /* nonzero if request HEADERS is canceled. The error code is stored + in |error_code|. */ + uint8_t canceled; + /* nonzero if this item should be attached to stream object to make + it under priority control */ + uint8_t attach_stream; +} nghttp2_headers_aux_data; + +/* struct used for DATA frame */ +typedef struct { + /** + * The data to be sent for this DATA frame. + */ + nghttp2_data_provider data_prd; + /** + * The flags of DATA frame. We use separate flags here and + * nghttp2_data frame. The latter contains flags actually sent to + * peer. This |flags| may contain NGHTTP2_FLAG_END_STREAM and only + * when |eof| becomes nonzero, flags in nghttp2_data has + * NGHTTP2_FLAG_END_STREAM set. + */ + uint8_t flags; + /** + * The flag to indicate whether EOF was reached or not. Initially + * |eof| is 0. It becomes 1 after all data were read. + */ + uint8_t eof; + /** + * The flag to indicate that NGHTTP2_DATA_FLAG_NO_COPY is used. + */ + uint8_t no_copy; +} nghttp2_data_aux_data; + +typedef enum { + NGHTTP2_GOAWAY_AUX_NONE = 0x0, + /* indicates that session should be terminated after the + transmission of this frame. */ + NGHTTP2_GOAWAY_AUX_TERM_ON_SEND = 0x1, + /* indicates that this GOAWAY is just a notification for graceful + shutdown. No nghttp2_session.goaway_flags should be updated on + the reaction to this frame. */ + NGHTTP2_GOAWAY_AUX_SHUTDOWN_NOTICE = 0x2 +} nghttp2_goaway_aux_flag; + +/* struct used for GOAWAY frame */ +typedef struct { + /* bitwise-OR of one or more of nghttp2_goaway_aux_flag. */ + uint8_t flags; +} nghttp2_goaway_aux_data; + +/* Additional data which cannot be stored in nghttp2_frame struct */ +typedef union { + nghttp2_data_aux_data data; + nghttp2_headers_aux_data headers; + nghttp2_goaway_aux_data goaway; +} nghttp2_aux_data; + +struct nghttp2_outbound_item; +typedef struct nghttp2_outbound_item nghttp2_outbound_item; + +struct nghttp2_outbound_item { + nghttp2_frame frame; + nghttp2_aux_data aux_data; + /* The priority used in priority comparion. Smaller is served + ealier. For PING, SETTINGS and non-DATA frames (excluding + response HEADERS frame) have dedicated cycle value defined above. + For DATA frame, cycle is computed by taking into account of + effective weight and frame payload length previously sent, so + that the amount of transmission is distributed across streams + proportional to effective weight (inside a tree). */ + uint64_t cycle; + nghttp2_outbound_item *qnext; + /* nonzero if this object is queued. */ + uint8_t queued; +}; + +/* + * Initializes |item|. No memory allocation is done in this function. + * Don't call nghttp2_outbound_item_free() until frame member is + * initialized. + */ +void nghttp2_outbound_item_init(nghttp2_outbound_item *item); + +/* + * Deallocates resource for |item|. If |item| is NULL, this function + * does nothing. + */ +void nghttp2_outbound_item_free(nghttp2_outbound_item *item, nghttp2_mem *mem); + +/* + * queue for nghttp2_outbound_item. + */ +typedef struct { + nghttp2_outbound_item *head, *tail; + /* number of items in this queue. */ + size_t n; +} nghttp2_outbound_queue; + +void nghttp2_outbound_queue_init(nghttp2_outbound_queue *q); + +/* Pushes |item| into |q| */ +void nghttp2_outbound_queue_push(nghttp2_outbound_queue *q, + nghttp2_outbound_item *item); + +/* Pops |item| at the top from |q|. If |q| is empty, nothing + happens. */ +void nghttp2_outbound_queue_pop(nghttp2_outbound_queue *q); + +/* Returns the top item. */ +#define nghttp2_outbound_queue_top(Q) ((Q)->head) + +/* Returns the size of the queue */ +#define nghttp2_outbound_queue_size(Q) ((Q)->n) + +#endif /* NGHTTP2_OUTBOUND_ITEM_H */ diff --git a/lib/nghttp2_pq.c b/lib/nghttp2_pq.c new file mode 100644 index 0000000..f04c189 --- /dev/null +++ b/lib/nghttp2_pq.c @@ -0,0 +1,146 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "nghttp2_pq.h" + +int nghttp2_pq_init(nghttp2_pq *pq, nghttp2_less less, nghttp2_mem *mem) { + pq->mem = mem; + pq->capacity = 128; + pq->q = nghttp2_mem_malloc(mem, pq->capacity * sizeof(void *)); + if (pq->q == NULL) { + return NGHTTP2_ERR_NOMEM; + } + pq->length = 0; + pq->less = less; + return 0; +} + +void nghttp2_pq_free(nghttp2_pq *pq) { + nghttp2_mem_free(pq->mem, pq->q); + pq->q = NULL; +} + +static void swap(nghttp2_pq *pq, size_t i, size_t j) { + void *t = pq->q[i]; + pq->q[i] = pq->q[j]; + pq->q[j] = t; +} + +static void bubble_up(nghttp2_pq *pq, size_t index) { + if (index == 0) { + return; + } else { + size_t parent = (index - 1) / 2; + if (pq->less(pq->q[index], pq->q[parent])) { + swap(pq, parent, index); + bubble_up(pq, parent); + } + } +} + +int nghttp2_pq_push(nghttp2_pq *pq, void *item) { + if (pq->capacity <= pq->length) { + void *nq; + nq = nghttp2_mem_realloc(pq->mem, pq->q, + (pq->capacity * 2) * sizeof(void *)); + if (nq == NULL) { + return NGHTTP2_ERR_NOMEM; + } + pq->capacity *= 2; + pq->q = nq; + } + pq->q[pq->length] = item; + ++pq->length; + bubble_up(pq, pq->length - 1); + return 0; +} + +void *nghttp2_pq_top(nghttp2_pq *pq) { + if (pq->length == 0) { + return NULL; + } else { + return pq->q[0]; + } +} + +static void bubble_down(nghttp2_pq *pq, size_t index) { + size_t lchild = index * 2 + 1; + size_t minindex = index; + size_t i, j; + for (i = 0; i < 2; ++i) { + j = lchild + i; + if (j >= pq->length) { + break; + } + if (pq->less(pq->q[j], pq->q[minindex])) { + minindex = j; + } + } + if (minindex != index) { + swap(pq, index, minindex); + bubble_down(pq, minindex); + } +} + +void nghttp2_pq_pop(nghttp2_pq *pq) { + if (pq->length > 0) { + pq->q[0] = pq->q[pq->length - 1]; + --pq->length; + bubble_down(pq, 0); + } +} + +int nghttp2_pq_empty(nghttp2_pq *pq) { return pq->length == 0; } + +size_t nghttp2_pq_size(nghttp2_pq *pq) { return pq->length; } + +void nghttp2_pq_update(nghttp2_pq *pq, nghttp2_pq_item_cb fun, void *arg) { + size_t i; + int rv = 0; + if (pq->length == 0) { + return; + } + for (i = 0; i < pq->length; ++i) { + rv |= (*fun)(pq->q[i], arg); + } + if (rv) { + for (i = pq->length; i > 0; --i) { + bubble_down(pq, i - 1); + } + } +} + +int nghttp2_pq_each(nghttp2_pq *pq, nghttp2_pq_item_cb fun, void *arg) { + size_t i; + + if (pq->length == 0) { + return 0; + } + for (i = 0; i < pq->length; ++i) { + if ((*fun)(pq->q[i], arg)) { + return 1; + } + } + return 0; +} diff --git a/lib/nghttp2_pq.h b/lib/nghttp2_pq.h new file mode 100644 index 0000000..1775d03 --- /dev/null +++ b/lib/nghttp2_pq.h @@ -0,0 +1,121 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGHTTP2_PQ_H +#define NGHTTP2_PQ_H + +#ifdef HAVE_CONFIG_H +#include +#endif /* HAVE_CONFIG_H */ + +#include +#include "nghttp2_int.h" +#include "nghttp2_mem.h" + +/* Implementation of priority queue */ + +typedef struct { + /* The pointer to the pointer to the item stored */ + void **q; + /* Memory allocator */ + nghttp2_mem *mem; + /* The number of items sotred */ + size_t length; + /* The maximum number of items this pq can store. This is + automatically extended when length is reached to this value. */ + size_t capacity; + /* The less function between items */ + nghttp2_less less; +} nghttp2_pq; + +/* + * Initializes priority queue |pq| with compare function |cmp|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + */ +int nghttp2_pq_init(nghttp2_pq *pq, nghttp2_less less, nghttp2_mem *mem); + +/* + * Deallocates any resources allocated for |pq|. The stored items are + * not freed by this function. + */ +void nghttp2_pq_free(nghttp2_pq *pq); + +/* + * Adds |item| to the priority queue |pq|. + * + * This function returns 0 if it succeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + */ +int nghttp2_pq_push(nghttp2_pq *pq, void *item); + +/* + * Returns item at the top of the queue |pq|. If the queue is empty, + * this function returns NULL. + */ +void *nghttp2_pq_top(nghttp2_pq *pq); + +/* + * Pops item at the top of the queue |pq|. The popped item is not + * freed by this function. + */ +void nghttp2_pq_pop(nghttp2_pq *pq); + +/* + * Returns nonzero if the queue |pq| is empty. + */ +int nghttp2_pq_empty(nghttp2_pq *pq); + +/* + * Returns the number of items in the queue |pq|. + */ +size_t nghttp2_pq_size(nghttp2_pq *pq); + +typedef int (*nghttp2_pq_item_cb)(void *item, void *arg); + +/* + * Updates each item in |pq| using function |fun| and re-construct + * priority queue. The |fun| must return non-zero if it modifies the + * item in a way that it affects ordering in the priority queue. The + * |arg| is passed to the 2nd parameter of |fun|. + */ +void nghttp2_pq_update(nghttp2_pq *pq, nghttp2_pq_item_cb fun, void *arg); + +/* + * Applys |fun| to each item in |pq|. The |arg| is passed as arg + * parameter to callback function. This function must not change the + * ordering key. If the return value from callback is nonzero, this + * function returns 1 immediately without iterating remaining items. + * Otherwise this function returns 0. + */ +int nghttp2_pq_each(nghttp2_pq *pq, nghttp2_pq_item_cb fun, void *arg); + +#endif /* NGHTTP2_PQ_H */ diff --git a/lib/nghttp2_priority_spec.c b/lib/nghttp2_priority_spec.c new file mode 100644 index 0000000..cd254b1 --- /dev/null +++ b/lib/nghttp2_priority_spec.c @@ -0,0 +1,44 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2014 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "nghttp2_priority_spec.h" + +void nghttp2_priority_spec_init(nghttp2_priority_spec *pri_spec, + int32_t stream_id, int32_t weight, + int exclusive) { + pri_spec->stream_id = stream_id; + pri_spec->weight = weight; + pri_spec->exclusive = exclusive != 0; +} + +void nghttp2_priority_spec_default_init(nghttp2_priority_spec *pri_spec) { + pri_spec->stream_id = 0; + pri_spec->weight = NGHTTP2_DEFAULT_WEIGHT; + pri_spec->exclusive = 0; +} + +int nghttp2_priority_spec_check_default(const nghttp2_priority_spec *pri_spec) { + return pri_spec->stream_id == 0 && + pri_spec->weight == NGHTTP2_DEFAULT_WEIGHT && pri_spec->exclusive == 0; +} diff --git a/lib/nghttp2_priority_spec.h b/lib/nghttp2_priority_spec.h new file mode 100644 index 0000000..a325a58 --- /dev/null +++ b/lib/nghttp2_priority_spec.h @@ -0,0 +1,34 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2014 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGHTTP2_PRIORITY_SPEC_H +#define NGHTTP2_PRIORITY_SPEC_H + +#ifdef HAVE_CONFIG_H +#include +#endif /* HAVE_CONFIG_H */ + +#include + +#endif /* NGHTTP2_PRIORITY_SPEC_H */ diff --git a/lib/nghttp2_queue.c b/lib/nghttp2_queue.c new file mode 100644 index 0000000..055eb69 --- /dev/null +++ b/lib/nghttp2_queue.c @@ -0,0 +1,85 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "nghttp2_queue.h" + +#include +#include + +void nghttp2_queue_init(nghttp2_queue *queue) { + queue->front = queue->back = NULL; +} + +void nghttp2_queue_free(nghttp2_queue *queue) { + if (!queue) { + return; + } else { + nghttp2_queue_cell *p = queue->front; + while (p) { + nghttp2_queue_cell *next = p->next; + free(p); + p = next; + } + } +} + +int nghttp2_queue_push(nghttp2_queue *queue, void *data) { + nghttp2_queue_cell *new_cell = + (nghttp2_queue_cell *)malloc(sizeof(nghttp2_queue_cell)); + if (!new_cell) { + return NGHTTP2_ERR_NOMEM; + } + new_cell->data = data; + new_cell->next = NULL; + if (queue->back) { + queue->back->next = new_cell; + queue->back = new_cell; + + } else { + queue->front = queue->back = new_cell; + } + return 0; +} + +void nghttp2_queue_pop(nghttp2_queue *queue) { + nghttp2_queue_cell *front = queue->front; + assert(front); + queue->front = front->next; + if (front == queue->back) { + queue->back = NULL; + } + free(front); +} + +void *nghttp2_queue_front(nghttp2_queue *queue) { + assert(queue->front); + return queue->front->data; +} + +void *nghttp2_queue_back(nghttp2_queue *queue) { + assert(queue->back); + return queue->back->data; +} + +int nghttp2_queue_empty(nghttp2_queue *queue) { return queue->front == NULL; } diff --git a/lib/nghttp2_queue.h b/lib/nghttp2_queue.h new file mode 100644 index 0000000..d872b07 --- /dev/null +++ b/lib/nghttp2_queue.h @@ -0,0 +1,49 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGHTTP2_QUEUE_H +#define NGHTTP2_QUEUE_H + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif /* HAVE_CONFIG_H */ + +#include + +typedef struct nghttp2_queue_cell { + void *data; + struct nghttp2_queue_cell *next; +} nghttp2_queue_cell; + +typedef struct { nghttp2_queue_cell *front, *back; } nghttp2_queue; + +void nghttp2_queue_init(nghttp2_queue *queue); +void nghttp2_queue_free(nghttp2_queue *queue); +int nghttp2_queue_push(nghttp2_queue *queue, void *data); +void nghttp2_queue_pop(nghttp2_queue *queue); +void *nghttp2_queue_front(nghttp2_queue *queue); +void *nghttp2_queue_back(nghttp2_queue *queue); +int nghttp2_queue_empty(nghttp2_queue *queue); + +#endif /* NGHTTP2_QUEUE_H */ diff --git a/lib/nghttp2_session.c b/lib/nghttp2_session.c new file mode 100644 index 0000000..7309aa0 --- /dev/null +++ b/lib/nghttp2_session.c @@ -0,0 +1,6612 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "nghttp2_session.h" + +#include +#include +#include +#include + +#include "nghttp2_helper.h" +#include "nghttp2_net.h" +#include "nghttp2_priority_spec.h" +#include "nghttp2_option.h" +#include "nghttp2_http.h" + +/* + * Returns non-zero if the number of outgoing opened streams is larger + * than or equal to + * remote_settings.max_concurrent_streams. + */ +static int +session_is_outgoing_concurrent_streams_max(nghttp2_session *session) { + return session->remote_settings.max_concurrent_streams <= + session->num_outgoing_streams; +} + +/* + * Returns non-zero if the number of incoming opened streams is larger + * than or equal to + * local_settings.max_concurrent_streams. + */ +static int +session_is_incoming_concurrent_streams_max(nghttp2_session *session) { + return session->local_settings.max_concurrent_streams <= + session->num_incoming_streams; +} + +/* + * Returns non-zero if the number of incoming opened streams is larger + * than or equal to + * session->pending_local_max_concurrent_stream. + */ +static int +session_is_incoming_concurrent_streams_pending_max(nghttp2_session *session) { + return session->pending_local_max_concurrent_stream <= + session->num_incoming_streams; +} + +/* + * Returns non-zero if |lib_error| is non-fatal error. + */ +static int is_non_fatal(int lib_error_code) { + return lib_error_code < 0 && lib_error_code > NGHTTP2_ERR_FATAL; +} + +int nghttp2_is_fatal(int lib_error_code) { + return lib_error_code < NGHTTP2_ERR_FATAL; +} + +static int session_enforce_http_messaging(nghttp2_session *session) { + return (session->opt_flags & NGHTTP2_OPTMASK_NO_HTTP_MESSAGING) == 0; +} + +/* + * Returns nonzero if |frame| is trailer headers. + */ +static int session_trailer_headers(nghttp2_session *session, + nghttp2_stream *stream, + nghttp2_frame *frame) { + if (!stream || frame->hd.type != NGHTTP2_HEADERS) { + return 0; + } + if (session->server) { + return frame->headers.cat == NGHTTP2_HCAT_HEADERS; + } + + return frame->headers.cat == NGHTTP2_HCAT_HEADERS && + (stream->http_flags & NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE) == 0; +} + +/* Returns nonzero if the |stream| is in reserved(remote) state */ +static int state_reserved_remote(nghttp2_session *session, + nghttp2_stream *stream) { + return stream->state == NGHTTP2_STREAM_RESERVED && + !nghttp2_session_is_my_stream_id(session, stream->stream_id); +} + +/* Returns nonzero if the |stream| is in reserved(local) state */ +static int state_reserved_local(nghttp2_session *session, + nghttp2_stream *stream) { + return stream->state == NGHTTP2_STREAM_RESERVED && + nghttp2_session_is_my_stream_id(session, stream->stream_id); +} + +/* + * Checks whether received stream_id is valid. This function returns + * 1 if it succeeds, or 0. + */ +static int session_is_new_peer_stream_id(nghttp2_session *session, + int32_t stream_id) { + return stream_id != 0 && + !nghttp2_session_is_my_stream_id(session, stream_id) && + session->last_recv_stream_id < stream_id; +} + +static int session_detect_idle_stream(nghttp2_session *session, + int32_t stream_id) { + /* Assume that stream object with stream_id does not exist */ + if (nghttp2_session_is_my_stream_id(session, stream_id)) { + if (session->next_stream_id <= (uint32_t)stream_id) { + return 1; + } + return 0; + } + if (session_is_new_peer_stream_id(session, stream_id)) { + return 1; + } + return 0; +} + +static int session_terminate_session(nghttp2_session *session, + int32_t last_stream_id, + uint32_t error_code, const char *reason) { + int rv; + const uint8_t *debug_data; + size_t debug_datalen; + + if (session->goaway_flags & NGHTTP2_GOAWAY_TERM_ON_SEND) { + return 0; + } + + if (reason == NULL) { + debug_data = NULL; + debug_datalen = 0; + } else { + debug_data = (const uint8_t *)reason; + debug_datalen = strlen(reason); + } + + rv = nghttp2_session_add_goaway(session, last_stream_id, error_code, + debug_data, debug_datalen, + NGHTTP2_GOAWAY_AUX_TERM_ON_SEND); + + if (rv != 0) { + return rv; + } + + session->goaway_flags |= NGHTTP2_GOAWAY_TERM_ON_SEND; + + return 0; +} + +int nghttp2_session_terminate_session(nghttp2_session *session, + uint32_t error_code) { + return session_terminate_session(session, session->last_proc_stream_id, + error_code, NULL); +} + +int nghttp2_session_terminate_session2(nghttp2_session *session, + int32_t last_stream_id, + uint32_t error_code) { + return session_terminate_session(session, last_stream_id, error_code, NULL); +} + +int nghttp2_session_terminate_session_with_reason(nghttp2_session *session, + uint32_t error_code, + const char *reason) { + return session_terminate_session(session, session->last_proc_stream_id, + error_code, reason); +} + +int nghttp2_session_is_my_stream_id(nghttp2_session *session, + int32_t stream_id) { + int rem; + if (stream_id == 0) { + return 0; + } + rem = stream_id & 0x1; + if (session->server) { + return rem == 0; + } + return rem == 1; +} + +nghttp2_stream *nghttp2_session_get_stream(nghttp2_session *session, + int32_t stream_id) { + nghttp2_stream *stream; + + stream = (nghttp2_stream *)nghttp2_map_find(&session->streams, stream_id); + + if (stream == NULL || (stream->flags & NGHTTP2_STREAM_FLAG_CLOSED) || + stream->state == NGHTTP2_STREAM_IDLE) { + return NULL; + } + + return stream; +} + +nghttp2_stream *nghttp2_session_get_stream_raw(nghttp2_session *session, + int32_t stream_id) { + return (nghttp2_stream *)nghttp2_map_find(&session->streams, stream_id); +} + +static int outbound_item_less(const void *lhsx, const void *rhsx) { + const nghttp2_outbound_item *lhs, *rhs; + + lhs = (const nghttp2_outbound_item *)lhsx; + rhs = (const nghttp2_outbound_item *)rhsx; + + return (lhs->cycle < rhs->cycle) ? 1 : 0; +} + +static void session_inbound_frame_reset(nghttp2_session *session) { + nghttp2_inbound_frame *iframe = &session->iframe; + nghttp2_mem *mem = &session->mem; + /* A bit risky code, since if this function is called from + nghttp2_session_new(), we rely on the fact that + iframe->frame.hd.type is 0, so that no free is performed. */ + switch (iframe->frame.hd.type) { + case NGHTTP2_HEADERS: + nghttp2_frame_headers_free(&iframe->frame.headers, mem); + break; + case NGHTTP2_PRIORITY: + nghttp2_frame_priority_free(&iframe->frame.priority); + break; + case NGHTTP2_RST_STREAM: + nghttp2_frame_rst_stream_free(&iframe->frame.rst_stream); + break; + case NGHTTP2_SETTINGS: + nghttp2_frame_settings_free(&iframe->frame.settings, mem); + break; + case NGHTTP2_PUSH_PROMISE: + nghttp2_frame_push_promise_free(&iframe->frame.push_promise, mem); + break; + case NGHTTP2_PING: + nghttp2_frame_ping_free(&iframe->frame.ping); + break; + case NGHTTP2_GOAWAY: + nghttp2_frame_goaway_free(&iframe->frame.goaway, mem); + break; + case NGHTTP2_WINDOW_UPDATE: + nghttp2_frame_window_update_free(&iframe->frame.window_update); + break; + } + + memset(&iframe->frame, 0, sizeof(nghttp2_frame)); + memset(&iframe->ext_frame_payload, 0, sizeof(nghttp2_ext_frame_payload)); + + iframe->state = NGHTTP2_IB_READ_HEAD; + + nghttp2_buf_wrap_init(&iframe->sbuf, iframe->raw_sbuf, + sizeof(iframe->raw_sbuf)); + iframe->sbuf.mark += NGHTTP2_FRAME_HDLEN; + + nghttp2_buf_free(&iframe->lbuf, mem); + nghttp2_buf_wrap_init(&iframe->lbuf, NULL, 0); + + iframe->niv = 0; + iframe->payloadleft = 0; + iframe->padlen = 0; + iframe->iv[NGHTTP2_INBOUND_NUM_IV - 1].settings_id = + NGHTTP2_SETTINGS_HEADER_TABLE_SIZE; + iframe->iv[NGHTTP2_INBOUND_NUM_IV - 1].value = UINT32_MAX; +} + +static void init_settings(nghttp2_settings_storage *settings) { + settings->header_table_size = NGHTTP2_HD_DEFAULT_MAX_BUFFER_SIZE; + settings->enable_push = 1; + settings->max_concurrent_streams = NGHTTP2_INITIAL_MAX_CONCURRENT_STREAMS; + settings->initial_window_size = NGHTTP2_INITIAL_WINDOW_SIZE; + settings->max_frame_size = NGHTTP2_MAX_FRAME_SIZE_MIN; + settings->max_header_list_size = UINT32_MAX; +} + +static void active_outbound_item_reset(nghttp2_active_outbound_item *aob, + nghttp2_mem *mem) { + DEBUGF(fprintf(stderr, "send: reset nghttp2_active_outbound_item\n")); + DEBUGF(fprintf(stderr, "send: aob->item = %p\n", aob->item)); + nghttp2_outbound_item_free(aob->item, mem); + nghttp2_mem_free(mem, aob->item); + aob->item = NULL; + nghttp2_bufs_reset(&aob->framebufs); + aob->state = NGHTTP2_OB_POP_ITEM; +} + +/* The global variable for tests where we want to disable strict + preface handling. */ +int nghttp2_enable_strict_preface = 1; + +static int session_new(nghttp2_session **session_ptr, + const nghttp2_session_callbacks *callbacks, + void *user_data, int server, + const nghttp2_option *option, nghttp2_mem *mem) { + int rv; + + if (mem == NULL) { + mem = nghttp2_mem_default(); + } + + *session_ptr = nghttp2_mem_calloc(mem, 1, sizeof(nghttp2_session)); + if (*session_ptr == NULL) { + rv = NGHTTP2_ERR_NOMEM; + goto fail_session; + } + + (*session_ptr)->mem = *mem; + mem = &(*session_ptr)->mem; + + /* next_stream_id is initialized in either + nghttp2_session_client_new2 or nghttp2_session_server_new2 */ + + rv = nghttp2_pq_init(&(*session_ptr)->ob_da_pq, outbound_item_less, mem); + if (rv != 0) { + goto fail_ob_da_pq; + } + + rv = nghttp2_hd_deflate_init(&(*session_ptr)->hd_deflater, mem); + if (rv != 0) { + goto fail_hd_deflater; + } + rv = nghttp2_hd_inflate_init(&(*session_ptr)->hd_inflater, mem); + if (rv != 0) { + goto fail_hd_inflater; + } + rv = nghttp2_map_init(&(*session_ptr)->streams, mem); + if (rv != 0) { + goto fail_map; + } + + nghttp2_stream_roots_init(&(*session_ptr)->roots); + + (*session_ptr)->remote_window_size = NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE; + (*session_ptr)->recv_window_size = 0; + (*session_ptr)->consumed_size = 0; + (*session_ptr)->recv_reduction = 0; + (*session_ptr)->local_window_size = NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE; + + (*session_ptr)->goaway_flags = NGHTTP2_GOAWAY_NONE; + (*session_ptr)->local_last_stream_id = (1u << 31) - 1; + (*session_ptr)->remote_last_stream_id = (1u << 31) - 1; + + (*session_ptr)->inflight_niv = -1; + + (*session_ptr)->pending_local_max_concurrent_stream = + NGHTTP2_INITIAL_MAX_CONCURRENT_STREAMS; + (*session_ptr)->pending_enable_push = 1; + + if (server) { + (*session_ptr)->server = 1; + } + + /* 1 for Pad Field. */ + rv = nghttp2_bufs_init3(&(*session_ptr)->aob.framebufs, + NGHTTP2_FRAMEBUF_CHUNKLEN, NGHTTP2_FRAMEBUF_MAX_NUM, + 1, NGHTTP2_FRAME_HDLEN + 1, mem); + if (rv != 0) { + goto fail_aob_framebuf; + } + + active_outbound_item_reset(&(*session_ptr)->aob, mem); + + init_settings(&(*session_ptr)->remote_settings); + init_settings(&(*session_ptr)->local_settings); + + if (option) { + if ((option->opt_set_mask & NGHTTP2_OPT_NO_AUTO_WINDOW_UPDATE) && + option->no_auto_window_update) { + + (*session_ptr)->opt_flags |= NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE; + } + + if (option->opt_set_mask & NGHTTP2_OPT_PEER_MAX_CONCURRENT_STREAMS) { + + (*session_ptr)->remote_settings.max_concurrent_streams = + option->peer_max_concurrent_streams; + } + + if ((option->opt_set_mask & NGHTTP2_OPT_NO_RECV_CLIENT_MAGIC) && + option->no_recv_client_magic) { + + (*session_ptr)->opt_flags |= NGHTTP2_OPTMASK_NO_RECV_CLIENT_MAGIC; + } + + if ((option->opt_set_mask & NGHTTP2_OPT_NO_HTTP_MESSAGING) && + option->no_http_messaging) { + + (*session_ptr)->opt_flags |= NGHTTP2_OPTMASK_NO_HTTP_MESSAGING; + } + } + + (*session_ptr)->callbacks = *callbacks; + (*session_ptr)->user_data = user_data; + + session_inbound_frame_reset(*session_ptr); + + if (nghttp2_enable_strict_preface) { + nghttp2_inbound_frame *iframe = &(*session_ptr)->iframe; + + if (server && + ((*session_ptr)->opt_flags & NGHTTP2_OPTMASK_NO_RECV_CLIENT_MAGIC) == + 0) { + iframe->state = NGHTTP2_IB_READ_CLIENT_MAGIC; + iframe->payloadleft = NGHTTP2_CLIENT_MAGIC_LEN; + } else { + iframe->state = NGHTTP2_IB_READ_FIRST_SETTINGS; + } + + if (!server) { + (*session_ptr)->aob.state = NGHTTP2_OB_SEND_CLIENT_MAGIC; + nghttp2_bufs_add(&(*session_ptr)->aob.framebufs, NGHTTP2_CLIENT_MAGIC, + NGHTTP2_CLIENT_MAGIC_LEN); + } + } + + return 0; + +fail_aob_framebuf: + nghttp2_map_free(&(*session_ptr)->streams); +fail_map: + nghttp2_hd_inflate_free(&(*session_ptr)->hd_inflater); +fail_hd_inflater: + nghttp2_hd_deflate_free(&(*session_ptr)->hd_deflater); +fail_hd_deflater: + nghttp2_pq_free(&(*session_ptr)->ob_da_pq); +fail_ob_da_pq: + nghttp2_mem_free(mem, *session_ptr); +fail_session: + return rv; +} + +int nghttp2_session_client_new(nghttp2_session **session_ptr, + const nghttp2_session_callbacks *callbacks, + void *user_data) { + return nghttp2_session_client_new3(session_ptr, callbacks, user_data, NULL, + NULL); +} + +int nghttp2_session_client_new2(nghttp2_session **session_ptr, + const nghttp2_session_callbacks *callbacks, + void *user_data, const nghttp2_option *option) { + return nghttp2_session_client_new3(session_ptr, callbacks, user_data, option, + NULL); +} + +int nghttp2_session_client_new3(nghttp2_session **session_ptr, + const nghttp2_session_callbacks *callbacks, + void *user_data, const nghttp2_option *option, + nghttp2_mem *mem) { + int rv; + nghttp2_session *session; + + rv = session_new(&session, callbacks, user_data, 0, option, mem); + + if (rv != 0) { + return rv; + } + /* IDs for use in client */ + session->next_stream_id = 1; + + *session_ptr = session; + + return 0; +} + +int nghttp2_session_server_new(nghttp2_session **session_ptr, + const nghttp2_session_callbacks *callbacks, + void *user_data) { + return nghttp2_session_server_new3(session_ptr, callbacks, user_data, NULL, + NULL); +} + +int nghttp2_session_server_new2(nghttp2_session **session_ptr, + const nghttp2_session_callbacks *callbacks, + void *user_data, const nghttp2_option *option) { + return nghttp2_session_server_new3(session_ptr, callbacks, user_data, option, + NULL); +} + +int nghttp2_session_server_new3(nghttp2_session **session_ptr, + const nghttp2_session_callbacks *callbacks, + void *user_data, const nghttp2_option *option, + nghttp2_mem *mem) { + int rv; + nghttp2_session *session; + + rv = session_new(&session, callbacks, user_data, 1, option, mem); + + if (rv != 0) { + return rv; + } + /* IDs for use in client */ + session->next_stream_id = 2; + + *session_ptr = session; + + return 0; +} + +static int free_streams(nghttp2_map_entry *entry, void *ptr) { + nghttp2_session *session; + nghttp2_stream *stream; + nghttp2_outbound_item *item; + nghttp2_mem *mem; + + session = (nghttp2_session *)ptr; + mem = &session->mem; + stream = (nghttp2_stream *)entry; + item = stream->item; + + if (item && !item->queued && item != session->aob.item) { + nghttp2_outbound_item_free(item, mem); + nghttp2_mem_free(mem, item); + } + + nghttp2_stream_free(stream); + nghttp2_mem_free(mem, stream); + + return 0; +} + +static void ob_pq_free(nghttp2_pq *pq, nghttp2_mem *mem) { + while (!nghttp2_pq_empty(pq)) { + nghttp2_outbound_item *item = (nghttp2_outbound_item *)nghttp2_pq_top(pq); + nghttp2_outbound_item_free(item, mem); + nghttp2_mem_free(mem, item); + nghttp2_pq_pop(pq); + } + nghttp2_pq_free(pq); +} + +static void ob_q_free(nghttp2_outbound_queue *q, nghttp2_mem *mem) { + nghttp2_outbound_item *item, *next; + for (item = q->head; item;) { + next = item->qnext; + nghttp2_outbound_item_free(item, mem); + nghttp2_mem_free(mem, item); + item = next; + } +} + +void nghttp2_session_del(nghttp2_session *session) { + nghttp2_mem *mem; + + if (session == NULL) { + return; + } + + mem = &session->mem; + + nghttp2_mem_free(mem, session->inflight_iv); + + nghttp2_stream_roots_free(&session->roots); + + /* Have to free streams first, so that we can check + stream->item->queued */ + nghttp2_map_each_free(&session->streams, free_streams, session); + nghttp2_map_free(&session->streams); + + ob_q_free(&session->ob_urgent, mem); + ob_q_free(&session->ob_reg, mem); + ob_q_free(&session->ob_syn, mem); + ob_pq_free(&session->ob_da_pq, mem); + active_outbound_item_reset(&session->aob, mem); + session_inbound_frame_reset(session); + nghttp2_hd_deflate_free(&session->hd_deflater); + nghttp2_hd_inflate_free(&session->hd_inflater); + nghttp2_bufs_free(&session->aob.framebufs); + nghttp2_mem_free(mem, session); +} + +int +nghttp2_session_reprioritize_stream(nghttp2_session *session, + nghttp2_stream *stream, + const nghttp2_priority_spec *pri_spec_in) { + int rv; + nghttp2_stream *dep_stream = NULL; + nghttp2_stream *root_stream; + nghttp2_priority_spec pri_spec_default; + const nghttp2_priority_spec *pri_spec = pri_spec_in; + + if (!nghttp2_stream_in_dep_tree(stream)) { + return 0; + } + + if (pri_spec->stream_id == stream->stream_id) { + return nghttp2_session_terminate_session_with_reason( + session, NGHTTP2_PROTOCOL_ERROR, "depend on itself"); + } + + if (pri_spec->stream_id != 0) { + dep_stream = nghttp2_session_get_stream_raw(session, pri_spec->stream_id); + + if (session->server && !dep_stream && + session_detect_idle_stream(session, pri_spec->stream_id)) { + + nghttp2_priority_spec_default_init(&pri_spec_default); + + dep_stream = nghttp2_session_open_stream( + session, pri_spec->stream_id, NGHTTP2_FLAG_NONE, &pri_spec_default, + NGHTTP2_STREAM_IDLE, NULL); + + if (dep_stream == NULL) { + return NGHTTP2_ERR_NOMEM; + } + } else if (!dep_stream || !nghttp2_stream_in_dep_tree(dep_stream)) { + nghttp2_priority_spec_default_init(&pri_spec_default); + pri_spec = &pri_spec_default; + } + } + + if (pri_spec->stream_id == 0) { + nghttp2_stream_dep_remove_subtree(stream); + + /* We have to update weight after removing stream from tree */ + stream->weight = pri_spec->weight; + + if (pri_spec->exclusive && + session->roots.num_streams <= NGHTTP2_MAX_DEP_TREE_LENGTH) { + + rv = nghttp2_stream_dep_all_your_stream_are_belong_to_us(stream, session); + } else { + rv = nghttp2_stream_dep_make_root(stream, session); + } + + return rv; + } + + assert(dep_stream); + + if (nghttp2_stream_dep_subtree_find(stream, dep_stream)) { + DEBUGF(fprintf(stderr, "stream: cycle detected, dep_stream(%p)=%d " + "stream(%p)=%d\n", + dep_stream, dep_stream->stream_id, stream, + stream->stream_id)); + + nghttp2_stream_dep_remove_subtree(dep_stream); + nghttp2_stream_dep_make_root(dep_stream, session); + } + + nghttp2_stream_dep_remove_subtree(stream); + + /* We have to update weight after removing stream from tree */ + stream->weight = pri_spec->weight; + + root_stream = nghttp2_stream_get_dep_root(dep_stream); + + if (root_stream->num_substreams + stream->num_substreams > + NGHTTP2_MAX_DEP_TREE_LENGTH) { + stream->weight = NGHTTP2_DEFAULT_WEIGHT; + + rv = nghttp2_stream_dep_make_root(stream, session); + } else { + if (pri_spec->exclusive) { + rv = nghttp2_stream_dep_insert_subtree(dep_stream, stream, session); + } else { + rv = nghttp2_stream_dep_add_subtree(dep_stream, stream, session); + } + } + + if (rv != 0) { + return rv; + } + + return 0; +} + +int nghttp2_session_add_item(nghttp2_session *session, + nghttp2_outbound_item *item) { + /* TODO Return error if stream is not found for the frame requiring + stream presence. */ + int rv = 0; + nghttp2_stream *stream; + nghttp2_frame *frame; + + frame = &item->frame; + stream = nghttp2_session_get_stream(session, frame->hd.stream_id); + + if (frame->hd.type != NGHTTP2_DATA) { + + switch (frame->hd.type) { + case NGHTTP2_HEADERS: + /* We push request HEADERS and push response HEADERS to + dedicated queue because their transmission is affected by + SETTINGS_MAX_CONCURRENT_STREAMS */ + /* TODO If 2 HEADERS are submitted for reserved stream, then + both of them are queued into ob_syn, which is not + desirable. */ + if (frame->headers.cat == NGHTTP2_HCAT_REQUEST) { + nghttp2_outbound_queue_push(&session->ob_syn, item); + item->queued = 1; + break; + } + + if (stream && (stream->state == NGHTTP2_STREAM_RESERVED || + item->aux_data.headers.attach_stream)) { + rv = nghttp2_stream_attach_item(stream, item, session); + + if (rv != 0) { + return rv; + } + + break; + } + + nghttp2_outbound_queue_push(&session->ob_reg, item); + item->queued = 1; + break; + case NGHTTP2_SETTINGS: + case NGHTTP2_PING: + nghttp2_outbound_queue_push(&session->ob_urgent, item); + item->queued = 1; + break; + case NGHTTP2_RST_STREAM: + if (stream) { + stream->state = NGHTTP2_STREAM_CLOSING; + } + /* fall through */ + default: + nghttp2_outbound_queue_push(&session->ob_reg, item); + item->queued = 1; + } + + return 0; + } + + if (!stream) { + return NGHTTP2_ERR_STREAM_CLOSED; + } + + if (stream->item) { + return NGHTTP2_ERR_DATA_EXIST; + } + + rv = nghttp2_stream_attach_item(stream, item, session); + + if (rv != 0) { + return rv; + } + + return 0; +} + +int nghttp2_session_add_rst_stream(nghttp2_session *session, int32_t stream_id, + uint32_t error_code) { + int rv; + nghttp2_outbound_item *item; + nghttp2_frame *frame; + nghttp2_stream *stream; + nghttp2_mem *mem; + + mem = &session->mem; + stream = nghttp2_session_get_stream(session, stream_id); + if (stream && stream->state == NGHTTP2_STREAM_CLOSING) { + return 0; + } + + /* Cancel pending request HEADERS in ob_syn if this RST_STREAM + refers to that stream. */ + if (!session->server && nghttp2_session_is_my_stream_id(session, stream_id) && + nghttp2_outbound_queue_top(&session->ob_syn)) { + nghttp2_headers_aux_data *aux_data; + nghttp2_frame *headers_frame; + + headers_frame = &nghttp2_outbound_queue_top(&session->ob_syn)->frame; + assert(headers_frame->hd.type == NGHTTP2_HEADERS); + + if (headers_frame->hd.stream_id <= stream_id && + (uint32_t)stream_id < session->next_stream_id) { + + for (item = session->ob_syn.head; item; item = item->qnext) { + aux_data = &item->aux_data.headers; + + if (item->frame.hd.stream_id < stream_id) { + continue; + } + + /* stream_id in ob_syn queue must be strictly increasing. If + we found larger ID, then we can break here. */ + if (item->frame.hd.stream_id > stream_id || aux_data->canceled) { + break; + } + + aux_data->error_code = error_code; + aux_data->canceled = 1; + + return 0; + } + } + } + + item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item)); + if (item == NULL) { + return NGHTTP2_ERR_NOMEM; + } + + nghttp2_outbound_item_init(item); + + frame = &item->frame; + + nghttp2_frame_rst_stream_init(&frame->rst_stream, stream_id, error_code); + rv = nghttp2_session_add_item(session, item); + if (rv != 0) { + nghttp2_frame_rst_stream_free(&frame->rst_stream); + nghttp2_mem_free(mem, item); + return rv; + } + return 0; +} + +nghttp2_stream *nghttp2_session_open_stream(nghttp2_session *session, + int32_t stream_id, uint8_t flags, + nghttp2_priority_spec *pri_spec_in, + nghttp2_stream_state initial_state, + void *stream_user_data) { + int rv; + nghttp2_stream *stream; + nghttp2_stream *dep_stream = NULL; + nghttp2_stream *root_stream; + int stream_alloc = 0; + nghttp2_priority_spec pri_spec_default; + nghttp2_priority_spec *pri_spec = pri_spec_in; + nghttp2_mem *mem; + + mem = &session->mem; + stream = nghttp2_session_get_stream_raw(session, stream_id); + + if (stream) { + assert(stream->state == NGHTTP2_STREAM_IDLE); + assert(nghttp2_stream_in_dep_tree(stream)); + nghttp2_session_detach_idle_stream(session, stream); + nghttp2_stream_dep_remove(stream); + } else { + if (session->server && initial_state != NGHTTP2_STREAM_IDLE && + !nghttp2_session_is_my_stream_id(session, stream_id)) { + + nghttp2_session_adjust_closed_stream(session, 1); + } + + stream = nghttp2_mem_malloc(mem, sizeof(nghttp2_stream)); + if (stream == NULL) { + return NULL; + } + + stream_alloc = 1; + } + + if (pri_spec->stream_id != 0) { + dep_stream = nghttp2_session_get_stream_raw(session, pri_spec->stream_id); + + if (session->server && !dep_stream && + session_detect_idle_stream(session, pri_spec->stream_id)) { + /* Depends on idle stream, which does not exist in memory. + Assign default priority for it. */ + nghttp2_priority_spec_default_init(&pri_spec_default); + + dep_stream = nghttp2_session_open_stream( + session, pri_spec->stream_id, NGHTTP2_FLAG_NONE, &pri_spec_default, + NGHTTP2_STREAM_IDLE, NULL); + + if (dep_stream == NULL) { + if (stream_alloc) { + nghttp2_mem_free(mem, stream); + } + + return NULL; + } + } else if (!dep_stream || !nghttp2_stream_in_dep_tree(dep_stream)) { + /* If dep_stream is not part of dependency tree, stream will get + default priority. */ + nghttp2_priority_spec_default_init(&pri_spec_default); + pri_spec = &pri_spec_default; + } + } + + if (initial_state == NGHTTP2_STREAM_RESERVED) { + flags |= NGHTTP2_STREAM_FLAG_PUSH; + } + + nghttp2_stream_init( + stream, stream_id, flags, initial_state, pri_spec->weight, + &session->roots, session->remote_settings.initial_window_size, + session->local_settings.initial_window_size, stream_user_data); + + if (stream_alloc) { + rv = nghttp2_map_insert(&session->streams, &stream->map_entry); + if (rv != 0) { + nghttp2_mem_free(mem, stream); + return NULL; + } + } + + switch (initial_state) { + case NGHTTP2_STREAM_RESERVED: + if (nghttp2_session_is_my_stream_id(session, stream_id)) { + /* half closed (remote) */ + nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_RD); + } else { + /* half closed (local) */ + nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_WR); + } + /* Reserved stream does not count in the concurrent streams + limit. That is one of the DOS vector. */ + break; + case NGHTTP2_STREAM_IDLE: + /* Idle stream does not count toward the concurrent streams limit. + This is used as anchor node in dependency tree. */ + assert(session->server); + nghttp2_session_keep_idle_stream(session, stream); + break; + default: + if (nghttp2_session_is_my_stream_id(session, stream_id)) { + ++session->num_outgoing_streams; + } else { + ++session->num_incoming_streams; + } + } + + /* We don't have to track dependency of received reserved stream */ + if (stream->shut_flags & NGHTTP2_SHUT_WR) { + return stream; + } + + if (pri_spec->stream_id == 0) { + + ++session->roots.num_streams; + + if (pri_spec->exclusive && + session->roots.num_streams <= NGHTTP2_MAX_DEP_TREE_LENGTH) { + rv = nghttp2_stream_dep_all_your_stream_are_belong_to_us(stream, session); + + /* Since no dpri is changed in dependency tree, the above + function call never fail. */ + assert(rv == 0); + } else { + nghttp2_stream_roots_add(&session->roots, stream); + } + + return stream; + } + + /* TODO Client does not have to track dependencies of streams except + for those which have upload data. Currently, we just track + everything. */ + + assert(dep_stream); + + root_stream = nghttp2_stream_get_dep_root(dep_stream); + + if (root_stream->num_substreams < NGHTTP2_MAX_DEP_TREE_LENGTH) { + if (pri_spec->exclusive) { + nghttp2_stream_dep_insert(dep_stream, stream); + } else { + nghttp2_stream_dep_add(dep_stream, stream); + } + } else { + stream->weight = NGHTTP2_DEFAULT_WEIGHT; + + nghttp2_stream_roots_add(&session->roots, stream); + } + + return stream; +} + +int nghttp2_session_close_stream(nghttp2_session *session, int32_t stream_id, + uint32_t error_code) { + int rv; + nghttp2_stream *stream; + nghttp2_mem *mem; + + mem = &session->mem; + stream = nghttp2_session_get_stream(session, stream_id); + + if (!stream) { + return NGHTTP2_ERR_INVALID_ARGUMENT; + } + + DEBUGF(fprintf(stderr, "stream: stream(%p)=%d close\n", stream, + stream->stream_id)); + + if (stream->item) { + nghttp2_outbound_item *item; + + item = stream->item; + + rv = nghttp2_stream_detach_item(stream, session); + + if (rv != 0) { + return rv; + } + + /* If item is queued, it will be deleted when it is popped + (nghttp2_session_prep_frame() will fail). If session->aob.item + points to this item, let active_outbound_item_reset() + free the item. */ + if (!item->queued && item != session->aob.item) { + nghttp2_outbound_item_free(item, mem); + nghttp2_mem_free(mem, item); + } + } + + /* We call on_stream_close_callback even if stream->state is + NGHTTP2_STREAM_INITIAL. This will happen while sending request + HEADERS, a local endpoint receives RST_STREAM for that stream. It + may be PROTOCOL_ERROR, but without notifying stream closure will + hang the stream in a local endpoint. + */ + + if (session->callbacks.on_stream_close_callback) { + if (session->callbacks.on_stream_close_callback( + session, stream_id, error_code, session->user_data) != 0) { + + return NGHTTP2_ERR_CALLBACK_FAILURE; + } + } + + /* pushed streams which is not opened yet is not counted toward max + concurrent limits */ + if ((stream->flags & NGHTTP2_STREAM_FLAG_PUSH) == 0) { + if (nghttp2_session_is_my_stream_id(session, stream_id)) { + --session->num_outgoing_streams; + } else { + --session->num_incoming_streams; + } + } + + /* Closes both directions just in case they are not closed yet */ + stream->flags |= NGHTTP2_STREAM_FLAG_CLOSED; + + if (session->server && nghttp2_stream_in_dep_tree(stream)) { + /* On server side, retain stream at most MAX_CONCURRENT_STREAMS + combined with the current active incoming streams to make + dependency tree work better. */ + nghttp2_session_keep_closed_stream(session, stream); + } else { + nghttp2_session_destroy_stream(session, stream); + } + + return 0; +} + +void nghttp2_session_destroy_stream(nghttp2_session *session, + nghttp2_stream *stream) { + nghttp2_mem *mem; + + DEBUGF(fprintf(stderr, "stream: destroy closed stream(%p)=%d\n", stream, + stream->stream_id)); + + mem = &session->mem; + + nghttp2_stream_dep_remove(stream); + + nghttp2_map_remove(&session->streams, stream->stream_id); + nghttp2_stream_free(stream); + nghttp2_mem_free(mem, stream); +} + +void nghttp2_session_keep_closed_stream(nghttp2_session *session, + nghttp2_stream *stream) { + DEBUGF(fprintf(stderr, "stream: keep closed stream(%p)=%d, state=%d\n", + stream, stream->stream_id, stream->state)); + + if (session->closed_stream_tail) { + session->closed_stream_tail->closed_next = stream; + stream->closed_prev = session->closed_stream_tail; + } else { + session->closed_stream_head = stream; + } + session->closed_stream_tail = stream; + + ++session->num_closed_streams; + + nghttp2_session_adjust_closed_stream(session, 0); +} + +void nghttp2_session_keep_idle_stream(nghttp2_session *session, + nghttp2_stream *stream) { + DEBUGF(fprintf(stderr, "stream: keep idle stream(%p)=%d, state=%d\n", stream, + stream->stream_id, stream->state)); + + if (session->idle_stream_tail) { + session->idle_stream_tail->closed_next = stream; + stream->closed_prev = session->idle_stream_tail; + } else { + session->idle_stream_head = stream; + } + session->idle_stream_tail = stream; + + ++session->num_idle_streams; + + nghttp2_session_adjust_idle_stream(session); +} + +void nghttp2_session_detach_idle_stream(nghttp2_session *session, + nghttp2_stream *stream) { + nghttp2_stream *prev_stream, *next_stream; + + DEBUGF(fprintf(stderr, "stream: detach idle stream(%p)=%d, state=%d\n", + stream, stream->stream_id, stream->state)); + + prev_stream = stream->closed_prev; + next_stream = stream->closed_next; + + if (prev_stream) { + prev_stream->closed_next = next_stream; + } else { + session->idle_stream_head = next_stream; + } + + if (next_stream) { + next_stream->closed_prev = prev_stream; + } else { + session->idle_stream_tail = prev_stream; + } + + stream->closed_prev = NULL; + stream->closed_next = NULL; + + --session->num_idle_streams; +} + +void nghttp2_session_adjust_closed_stream(nghttp2_session *session, + ssize_t offset) { + size_t num_stream_max; + + num_stream_max = nghttp2_min(session->local_settings.max_concurrent_streams, + session->pending_local_max_concurrent_stream); + + DEBUGF(fprintf(stderr, "stream: adjusting kept closed streams " + "num_closed_streams=%zu, num_incoming_streams=%zu, " + "max_concurrent_streams=%zu\n", + session->num_closed_streams, session->num_incoming_streams, + num_stream_max)); + + while (session->num_closed_streams > 0 && + session->num_closed_streams + session->num_incoming_streams + offset > + num_stream_max) { + nghttp2_stream *head_stream; + + head_stream = session->closed_stream_head; + + assert(head_stream); + + session->closed_stream_head = head_stream->closed_next; + + if (session->closed_stream_head) { + session->closed_stream_head->closed_prev = NULL; + } else { + session->closed_stream_tail = NULL; + } + + nghttp2_session_destroy_stream(session, head_stream); + /* head_stream is now freed */ + --session->num_closed_streams; + } +} + +void nghttp2_session_adjust_idle_stream(nghttp2_session *session) { + size_t max; + + /* Make minimum number of idle streams 2 so that allocating 2 + streams at once is easy. This happens when PRIORITY frame to + idle stream, which depends on idle stream which does not + exist. */ + max = + nghttp2_max(2, nghttp2_min(session->local_settings.max_concurrent_streams, + session->pending_local_max_concurrent_stream)); + + DEBUGF(fprintf(stderr, "stream: adjusting kept idle streams " + "num_idle_streams=%zu, max=%zu\n", + session->num_idle_streams, max)); + + while (session->num_idle_streams > max) { + nghttp2_stream *head; + + head = session->idle_stream_head; + assert(head); + + session->idle_stream_head = head->closed_next; + + if (session->idle_stream_head) { + session->idle_stream_head->closed_prev = NULL; + } else { + session->idle_stream_tail = NULL; + } + + nghttp2_session_destroy_stream(session, head); + /* head is now destroyed */ + --session->num_idle_streams; + } +} + +/* + * Closes stream with stream ID |stream_id| if both transmission and + * reception of the stream were disallowed. The |error_code| indicates + * the reason of the closure. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_INVALID_ARGUMENT + * The stream is not found. + * NGHTTP2_ERR_CALLBACK_FAILURE + * The callback function failed. + */ +int nghttp2_session_close_stream_if_shut_rdwr(nghttp2_session *session, + nghttp2_stream *stream) { + if ((stream->shut_flags & NGHTTP2_SHUT_RDWR) == NGHTTP2_SHUT_RDWR) { + return nghttp2_session_close_stream(session, stream->stream_id, + NGHTTP2_NO_ERROR); + } + return 0; +} + +/* + * This function returns nonzero if session is closing. + */ +static int session_is_closing(nghttp2_session *session) { + return (session->goaway_flags & NGHTTP2_GOAWAY_TERM_ON_SEND) != 0; +} + +/* + * Check that we can send a frame to the |stream|. This function + * returns 0 if we can send a frame to the |frame|, or one of the + * following negative error codes: + * + * NGHTTP2_ERR_STREAM_CLOSED + * The stream is already closed. + * NGHTTP2_ERR_STREAM_SHUT_WR + * The stream is half-closed for transmission. + * NGHTTP2_ERR_SESSION_CLOSING + * This session is closing. + */ +static int session_predicate_for_stream_send(nghttp2_session *session, + nghttp2_stream *stream) { + if (stream == NULL) { + return NGHTTP2_ERR_STREAM_CLOSED; + } + if (session_is_closing(session)) { + return NGHTTP2_ERR_SESSION_CLOSING; + } + if (stream->shut_flags & NGHTTP2_SHUT_WR) { + return NGHTTP2_ERR_STREAM_SHUT_WR; + } + return 0; +} + +/* + * This function checks request HEADERS frame, which opens stream, can + * be sent at this time. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_START_STREAM_NOT_ALLOWED + * New stream cannot be created because of GOAWAY: session is + * going down or received last_stream_id is strictly less than + * frame->hd.stream_id. + * NGHTTP2_ERR_STREAM_CLOSING + * request HEADERS was canceled by RST_STREAM while it is in queue. + */ +static int session_predicate_request_headers_send(nghttp2_session *session, + nghttp2_outbound_item *item) { + if (item->aux_data.headers.canceled) { + return NGHTTP2_ERR_STREAM_CLOSING; + } + /* If we are terminating session (NGHTTP2_GOAWAY_TERM_ON_SEND) or + GOAWAY was received from peer, new request is not allowed. */ + if (session->goaway_flags & + (NGHTTP2_GOAWAY_TERM_ON_SEND | NGHTTP2_GOAWAY_RECV)) { + return NGHTTP2_ERR_START_STREAM_NOT_ALLOWED; + } + return 0; +} + +/* + * This function checks HEADERS, which is the first frame from the + * server, with the |stream| can be sent at this time. The |stream| + * can be NULL. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_STREAM_CLOSED + * The stream is already closed or does not exist. + * NGHTTP2_ERR_STREAM_SHUT_WR + * The transmission is not allowed for this stream (e.g., a frame + * with END_STREAM flag set has already sent) + * NGHTTP2_ERR_INVALID_STREAM_ID + * The stream ID is invalid. + * NGHTTP2_ERR_STREAM_CLOSING + * RST_STREAM was queued for this stream. + * NGHTTP2_ERR_INVALID_STREAM_STATE + * The state of the stream is not valid. + * NGHTTP2_ERR_SESSION_CLOSING + * This session is closing. + */ +static int session_predicate_response_headers_send(nghttp2_session *session, + nghttp2_stream *stream) { + int rv; + rv = session_predicate_for_stream_send(session, stream); + if (rv != 0) { + return rv; + } + assert(stream); + if (nghttp2_session_is_my_stream_id(session, stream->stream_id)) { + return NGHTTP2_ERR_INVALID_STREAM_ID; + } + if (stream->state == NGHTTP2_STREAM_OPENING) { + return 0; + } + if (stream->state == NGHTTP2_STREAM_CLOSING) { + return NGHTTP2_ERR_STREAM_CLOSING; + } + return NGHTTP2_ERR_INVALID_STREAM_STATE; +} + +/* + * This function checks HEADERS for reserved stream can be sent. The + * |stream| must be reserved state and the |session| is server side. + * The |stream| can be NULL. + * + * This function returns 0 if it succeeds, or one of the following + * error codes: + * + * NGHTTP2_ERR_STREAM_CLOSED + * The stream is already closed. + * NGHTTP2_ERR_STREAM_SHUT_WR + * The stream is half-closed for transmission. + * NGHTTP2_ERR_PROTO + * The stream is not reserved state + * NGHTTP2_ERR_STREAM_CLOSED + * RST_STREAM was queued for this stream. + * NGHTTP2_ERR_SESSION_CLOSING + * This session is closing. + */ +static int +session_predicate_push_response_headers_send(nghttp2_session *session, + nghttp2_stream *stream) { + int rv; + /* TODO Should disallow HEADERS if GOAWAY has already been issued? */ + rv = session_predicate_for_stream_send(session, stream); + if (rv != 0) { + return rv; + } + assert(stream); + if (stream->state != NGHTTP2_STREAM_RESERVED) { + return NGHTTP2_ERR_PROTO; + } + if (stream->state == NGHTTP2_STREAM_CLOSING) { + return NGHTTP2_ERR_STREAM_CLOSING; + } + return 0; +} + +/* + * This function checks HEADERS, which is neither stream-opening nor + * first response header, with the |stream| can be sent at this time. + * The |stream| can be NULL. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_STREAM_CLOSED + * The stream is already closed or does not exist. + * NGHTTP2_ERR_STREAM_SHUT_WR + * The transmission is not allowed for this stream (e.g., a frame + * with END_STREAM flag set has already sent) + * NGHTTP2_ERR_STREAM_CLOSING + * RST_STREAM was queued for this stream. + * NGHTTP2_ERR_INVALID_STREAM_STATE + * The state of the stream is not valid. + * NGHTTP2_ERR_SESSION_CLOSING + * This session is closing. + */ +static int session_predicate_headers_send(nghttp2_session *session, + nghttp2_stream *stream) { + int rv; + rv = session_predicate_for_stream_send(session, stream); + if (rv != 0) { + return rv; + } + assert(stream); + if (nghttp2_session_is_my_stream_id(session, stream->stream_id)) { + if (stream->state == NGHTTP2_STREAM_CLOSING) { + return NGHTTP2_ERR_STREAM_CLOSING; + } + return 0; + } + if (stream->state == NGHTTP2_STREAM_OPENED) { + return 0; + } + if (stream->state == NGHTTP2_STREAM_CLOSING) { + return NGHTTP2_ERR_STREAM_CLOSING; + } + return NGHTTP2_ERR_INVALID_STREAM_STATE; +} + +/* + * This function checks PUSH_PROMISE frame |frame| with the |stream| + * can be sent at this time. The |stream| can be NULL. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_START_STREAM_NOT_ALLOWED + * New stream cannot be created because GOAWAY is already sent or + * received. + * NGHTTP2_ERR_PROTO + * The client side attempts to send PUSH_PROMISE, or the server + * sends PUSH_PROMISE for the stream not initiated by the client. + * NGHTTP2_ERR_STREAM_CLOSED + * The stream is already closed or does not exist. + * NGHTTP2_ERR_STREAM_CLOSING + * RST_STREAM was queued for this stream. + * NGHTTP2_ERR_STREAM_SHUT_WR + * The transmission is not allowed for this stream (e.g., a frame + * with END_STREAM flag set has already sent) + * NGHTTP2_ERR_PUSH_DISABLED + * The remote peer disabled reception of PUSH_PROMISE. + * NGHTTP2_ERR_SESSION_CLOSING + * This session is closing. + */ +static int session_predicate_push_promise_send(nghttp2_session *session, + nghttp2_stream *stream) { + int rv; + + if (!session->server) { + return NGHTTP2_ERR_PROTO; + } + + rv = session_predicate_for_stream_send(session, stream); + if (rv != 0) { + return rv; + } + + assert(stream); + + if (session->remote_settings.enable_push == 0) { + return NGHTTP2_ERR_PUSH_DISABLED; + } + if (stream->state == NGHTTP2_STREAM_CLOSING) { + return NGHTTP2_ERR_STREAM_CLOSING; + } + if (session->goaway_flags & NGHTTP2_GOAWAY_RECV) { + return NGHTTP2_ERR_START_STREAM_NOT_ALLOWED; + } + return 0; +} + +/* + * This function checks WINDOW_UPDATE with the stream ID |stream_id| + * can be sent at this time. Note that END_STREAM flag of the previous + * frame does not affect the transmission of the WINDOW_UPDATE frame. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_STREAM_CLOSED + * The stream is already closed or does not exist. + * NGHTTP2_ERR_STREAM_CLOSING + * RST_STREAM was queued for this stream. + * NGHTTP2_ERR_INVALID_STREAM_STATE + * The state of the stream is not valid. + * NGHTTP2_ERR_SESSION_CLOSING + * This session is closing. + */ +static int session_predicate_window_update_send(nghttp2_session *session, + int32_t stream_id) { + nghttp2_stream *stream; + if (stream_id == 0) { + /* Connection-level window update */ + return 0; + } + stream = nghttp2_session_get_stream(session, stream_id); + if (stream == NULL) { + return NGHTTP2_ERR_STREAM_CLOSED; + } + if (session_is_closing(session)) { + return NGHTTP2_ERR_SESSION_CLOSING; + } + if (stream->state == NGHTTP2_STREAM_CLOSING) { + return NGHTTP2_ERR_STREAM_CLOSING; + } + if (state_reserved_local(session, stream)) { + return NGHTTP2_ERR_INVALID_STREAM_STATE; + } + return 0; +} + +/* Take into account settings max frame size and both connection-level + flow control here */ +static ssize_t +nghttp2_session_enforce_flow_control_limits(nghttp2_session *session, + nghttp2_stream *stream, + ssize_t requested_window_size) { + DEBUGF(fprintf(stderr, "send: remote windowsize connection=%d, " + "remote maxframsize=%u, stream(id %d)=%d\n", + session->remote_window_size, + session->remote_settings.max_frame_size, stream->stream_id, + stream->remote_window_size)); + + return nghttp2_min(nghttp2_min(nghttp2_min(requested_window_size, + stream->remote_window_size), + session->remote_window_size), + (int32_t)session->remote_settings.max_frame_size); +} + +/* + * Returns the maximum length of next data read. If the + * connection-level and/or stream-wise flow control are enabled, the + * return value takes into account those current window sizes. The remote + * settings for max frame size is also taken into account. + */ +static size_t nghttp2_session_next_data_read(nghttp2_session *session, + nghttp2_stream *stream) { + ssize_t window_size; + + window_size = nghttp2_session_enforce_flow_control_limits( + session, stream, NGHTTP2_DATA_PAYLOADLEN); + + DEBUGF(fprintf(stderr, "send: available window=%zd\n", window_size)); + + return window_size > 0 ? (size_t)window_size : 0; +} + +/* + * This function checks DATA with the |stream| can be sent at this + * time. The |stream| can be NULL. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_STREAM_CLOSED + * The stream is already closed or does not exist. + * NGHTTP2_ERR_STREAM_SHUT_WR + * The transmission is not allowed for this stream (e.g., a frame + * with END_STREAM flag set has already sent) + * NGHTTP2_ERR_STREAM_CLOSING + * RST_STREAM was queued for this stream. + * NGHTTP2_ERR_INVALID_STREAM_STATE + * The state of the stream is not valid. + * NGHTTP2_ERR_SESSION_CLOSING + * This session is closing. + */ +static int nghttp2_session_predicate_data_send(nghttp2_session *session, + nghttp2_stream *stream) { + int rv; + rv = session_predicate_for_stream_send(session, stream); + if (rv != 0) { + return rv; + } + assert(stream); + if (nghttp2_session_is_my_stream_id(session, stream->stream_id)) { + /* Request body data */ + /* If stream->state is NGHTTP2_STREAM_CLOSING, RST_STREAM was + queued but not yet sent. In this case, we won't send DATA + frames. */ + if (stream->state == NGHTTP2_STREAM_CLOSING) { + return NGHTTP2_ERR_STREAM_CLOSING; + } + if (stream->state == NGHTTP2_STREAM_RESERVED) { + return NGHTTP2_ERR_INVALID_STREAM_STATE; + } + return 0; + } + /* Response body data */ + if (stream->state == NGHTTP2_STREAM_OPENED) { + return 0; + } + if (stream->state == NGHTTP2_STREAM_CLOSING) { + return NGHTTP2_ERR_STREAM_CLOSING; + } + return NGHTTP2_ERR_INVALID_STREAM_STATE; +} + +static ssize_t session_call_select_padding(nghttp2_session *session, + const nghttp2_frame *frame, + size_t max_payloadlen) { + ssize_t rv; + + if (frame->hd.length >= max_payloadlen) { + return frame->hd.length; + } + + if (session->callbacks.select_padding_callback) { + size_t max_paddedlen; + + max_paddedlen = + nghttp2_min(frame->hd.length + NGHTTP2_MAX_PADLEN, max_payloadlen); + + rv = session->callbacks.select_padding_callback( + session, frame, max_paddedlen, session->user_data); + if (rv < (ssize_t)frame->hd.length || rv > (ssize_t)max_paddedlen) { + return NGHTTP2_ERR_CALLBACK_FAILURE; + } + return rv; + } + return frame->hd.length; +} + +/* Add padding to HEADERS or PUSH_PROMISE. We use + frame->headers.padlen in this function to use the fact that + frame->push_promise has also padlen in the same position. */ +static int session_headers_add_pad(nghttp2_session *session, + nghttp2_frame *frame) { + int rv; + ssize_t padded_payloadlen; + nghttp2_active_outbound_item *aob; + nghttp2_bufs *framebufs; + size_t padlen; + size_t max_payloadlen; + + aob = &session->aob; + framebufs = &aob->framebufs; + + max_payloadlen = nghttp2_min(NGHTTP2_MAX_PAYLOADLEN, + frame->hd.length + NGHTTP2_MAX_PADLEN); + + padded_payloadlen = + session_call_select_padding(session, frame, max_payloadlen); + + if (nghttp2_is_fatal((int)padded_payloadlen)) { + return (int)padded_payloadlen; + } + + padlen = padded_payloadlen - frame->hd.length; + + DEBUGF(fprintf(stderr, "send: padding selected: payloadlen=%zd, padlen=%zu\n", + padded_payloadlen, padlen)); + + rv = nghttp2_frame_add_pad(framebufs, &frame->hd, padlen, 0); + + if (rv != 0) { + return rv; + } + + frame->headers.padlen = padlen; + + return 0; +} + +static size_t session_estimate_headers_payload(nghttp2_session *session, + const nghttp2_nv *nva, + size_t nvlen, + size_t additional) { + return nghttp2_hd_deflate_bound(&session->hd_deflater, nva, nvlen) + + additional; +} + +/* + * This function serializes frame for transmission. + * + * This function returns 0 if it succeeds, or one of negative error + * codes, including both fatal and non-fatal ones. + */ +static int session_prep_frame(nghttp2_session *session, + nghttp2_outbound_item *item) { + int rv; + nghttp2_frame *frame; + nghttp2_mem *mem; + + mem = &session->mem; + frame = &item->frame; + + if (frame->hd.type != NGHTTP2_DATA) { + switch (frame->hd.type) { + case NGHTTP2_HEADERS: { + nghttp2_headers_aux_data *aux_data; + size_t estimated_payloadlen; + + aux_data = &item->aux_data.headers; + + estimated_payloadlen = session_estimate_headers_payload( + session, frame->headers.nva, frame->headers.nvlen, + NGHTTP2_PRIORITY_SPECLEN); + + if (estimated_payloadlen > NGHTTP2_MAX_HEADERSLEN) { + return NGHTTP2_ERR_FRAME_SIZE_ERROR; + } + + if (frame->headers.cat == NGHTTP2_HCAT_REQUEST) { + /* initial HEADERS, which opens stream */ + nghttp2_stream *stream; + + stream = nghttp2_session_open_stream( + session, frame->hd.stream_id, NGHTTP2_STREAM_FLAG_NONE, + &frame->headers.pri_spec, NGHTTP2_STREAM_INITIAL, + aux_data->stream_user_data); + + if (stream == NULL) { + return NGHTTP2_ERR_NOMEM; + } + + rv = session_predicate_request_headers_send(session, item); + if (rv != 0) { + return rv; + } + + if (session_enforce_http_messaging(session)) { + nghttp2_http_record_request_method(stream, frame); + } + } else { + nghttp2_stream *stream; + + stream = nghttp2_session_get_stream(session, frame->hd.stream_id); + + if (session_predicate_push_response_headers_send(session, stream) == + 0) { + frame->headers.cat = NGHTTP2_HCAT_PUSH_RESPONSE; + + if (aux_data->stream_user_data) { + stream->stream_user_data = aux_data->stream_user_data; + } + } else if (session_predicate_response_headers_send(session, stream) == + 0) { + frame->headers.cat = NGHTTP2_HCAT_RESPONSE; + } else { + frame->headers.cat = NGHTTP2_HCAT_HEADERS; + + rv = session_predicate_headers_send(session, stream); + + if (rv != 0) { + if (stream && stream->item == item) { + int rv2; + + rv2 = nghttp2_stream_detach_item(stream, session); + + if (nghttp2_is_fatal(rv2)) { + return rv2; + } + } + + return rv; + } + } + } + + rv = nghttp2_frame_pack_headers(&session->aob.framebufs, &frame->headers, + &session->hd_deflater); + + if (rv != 0) { + return rv; + } + + DEBUGF(fprintf(stderr, + "send: before padding, HEADERS serialized in %zd bytes\n", + nghttp2_bufs_len(&session->aob.framebufs))); + + rv = session_headers_add_pad(session, frame); + + if (rv != 0) { + return rv; + } + + DEBUGF(fprintf(stderr, "send: HEADERS finally serialized in %zd bytes\n", + nghttp2_bufs_len(&session->aob.framebufs))); + + break; + } + case NGHTTP2_PRIORITY: { + if (session_is_closing(session)) { + return NGHTTP2_ERR_SESSION_CLOSING; + } + /* PRIORITY frame can be sent at any time and to any stream + ID. */ + nghttp2_frame_pack_priority(&session->aob.framebufs, &frame->priority); + + /* Peer can send PRIORITY frame against idle stream to create + "anchor" in dependency tree. Only client can do this in + nghttp2. In nghttp2, only server retains non-active (closed + or idle) streams in memory, so we don't open stream here. */ + break; + } + case NGHTTP2_RST_STREAM: + if (session_is_closing(session)) { + return NGHTTP2_ERR_SESSION_CLOSING; + } + nghttp2_frame_pack_rst_stream(&session->aob.framebufs, + &frame->rst_stream); + break; + case NGHTTP2_SETTINGS: { + rv = nghttp2_frame_pack_settings(&session->aob.framebufs, + &frame->settings); + if (rv != 0) { + return rv; + } + break; + } + case NGHTTP2_PUSH_PROMISE: { + nghttp2_stream *stream; + nghttp2_headers_aux_data *aux_data; + nghttp2_priority_spec pri_spec; + size_t estimated_payloadlen; + + aux_data = &item->aux_data.headers; + + stream = nghttp2_session_get_stream(session, frame->hd.stream_id); + + /* stream could be NULL if associated stream was already + closed. */ + if (stream) { + nghttp2_priority_spec_init(&pri_spec, stream->stream_id, + NGHTTP2_DEFAULT_WEIGHT, 0); + } else { + nghttp2_priority_spec_default_init(&pri_spec); + } + + if (!nghttp2_session_open_stream( + session, frame->push_promise.promised_stream_id, + NGHTTP2_STREAM_FLAG_NONE, &pri_spec, NGHTTP2_STREAM_RESERVED, + aux_data->stream_user_data)) { + return NGHTTP2_ERR_NOMEM; + } + + estimated_payloadlen = session_estimate_headers_payload( + session, frame->push_promise.nva, frame->push_promise.nvlen, 0); + + if (estimated_payloadlen > NGHTTP2_MAX_HEADERSLEN) { + return NGHTTP2_ERR_FRAME_SIZE_ERROR; + } + + /* predicte should fail if stream is NULL. */ + rv = session_predicate_push_promise_send(session, stream); + if (rv != 0) { + return rv; + } + + assert(stream); + + rv = nghttp2_frame_pack_push_promise( + &session->aob.framebufs, &frame->push_promise, &session->hd_deflater); + if (rv != 0) { + return rv; + } + rv = session_headers_add_pad(session, frame); + if (rv != 0) { + return rv; + } + + break; + } + case NGHTTP2_PING: + if (session_is_closing(session)) { + return NGHTTP2_ERR_SESSION_CLOSING; + } + nghttp2_frame_pack_ping(&session->aob.framebufs, &frame->ping); + break; + case NGHTTP2_WINDOW_UPDATE: { + rv = session_predicate_window_update_send(session, frame->hd.stream_id); + if (rv != 0) { + return rv; + } + nghttp2_frame_pack_window_update(&session->aob.framebufs, + &frame->window_update); + break; + } + case NGHTTP2_GOAWAY: + rv = nghttp2_frame_pack_goaway(&session->aob.framebufs, &frame->goaway); + if (rv != 0) { + return rv; + } + session->local_last_stream_id = frame->goaway.last_stream_id; + + break; + default: + return NGHTTP2_ERR_INVALID_ARGUMENT; + } + return 0; + } else { + size_t next_readmax; + nghttp2_stream *stream; + + stream = nghttp2_session_get_stream(session, frame->hd.stream_id); + + if (stream) { + assert(stream->item == item); + } + + rv = nghttp2_session_predicate_data_send(session, stream); + if (rv != 0) { + if (stream) { + int rv2; + + rv2 = nghttp2_stream_detach_item(stream, session); + + if (nghttp2_is_fatal(rv2)) { + return rv2; + } + } + + return rv; + } + /* Assuming stream is not NULL */ + assert(stream); + next_readmax = nghttp2_session_next_data_read(session, stream); + + if (next_readmax == 0) { + + /* This must be true since we only pop DATA frame item from + queue when session->remote_window_size > 0 */ + assert(session->remote_window_size > 0); + + rv = nghttp2_stream_defer_item( + stream, NGHTTP2_STREAM_FLAG_DEFERRED_FLOW_CONTROL, session); + + if (nghttp2_is_fatal(rv)) { + return rv; + } + + session->aob.item = NULL; + active_outbound_item_reset(&session->aob, mem); + return NGHTTP2_ERR_DEFERRED; + } + + rv = nghttp2_session_pack_data(session, &session->aob.framebufs, + next_readmax, frame, &item->aux_data.data, + stream); + if (rv == NGHTTP2_ERR_DEFERRED) { + rv = nghttp2_stream_defer_item(stream, NGHTTP2_STREAM_FLAG_DEFERRED_USER, + session); + + if (nghttp2_is_fatal(rv)) { + return rv; + } + + session->aob.item = NULL; + active_outbound_item_reset(&session->aob, mem); + return NGHTTP2_ERR_DEFERRED; + } + if (rv == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) { + rv = nghttp2_stream_detach_item(stream, session); + + if (nghttp2_is_fatal(rv)) { + return rv; + } + + rv = nghttp2_session_add_rst_stream(session, frame->hd.stream_id, + NGHTTP2_INTERNAL_ERROR); + if (nghttp2_is_fatal(rv)) { + return rv; + } + return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; + } + if (rv != 0) { + int rv2; + + rv2 = nghttp2_stream_detach_item(stream, session); + + if (nghttp2_is_fatal(rv2)) { + return rv2; + } + + return rv; + } + return 0; + } +} + +nghttp2_outbound_item * +nghttp2_session_get_next_ob_item(nghttp2_session *session) { + if (nghttp2_outbound_queue_top(&session->ob_urgent)) { + return nghttp2_outbound_queue_top(&session->ob_urgent); + } + + if (nghttp2_outbound_queue_top(&session->ob_reg)) { + return nghttp2_outbound_queue_top(&session->ob_reg); + } + + if (!session_is_outgoing_concurrent_streams_max(session)) { + if (nghttp2_outbound_queue_top(&session->ob_syn)) { + return nghttp2_outbound_queue_top(&session->ob_syn); + } + } + + if (session->remote_window_size > 0 && + !nghttp2_pq_empty(&session->ob_da_pq)) { + return nghttp2_pq_top(&session->ob_da_pq); + } + + return NULL; +} + +nghttp2_outbound_item * +nghttp2_session_pop_next_ob_item(nghttp2_session *session) { + nghttp2_outbound_item *item; + + item = nghttp2_outbound_queue_top(&session->ob_urgent); + if (item) { + nghttp2_outbound_queue_pop(&session->ob_urgent); + item->queued = 0; + return item; + } + + item = nghttp2_outbound_queue_top(&session->ob_reg); + if (item) { + nghttp2_outbound_queue_pop(&session->ob_reg); + item->queued = 0; + return item; + } + + if (!session_is_outgoing_concurrent_streams_max(session)) { + item = nghttp2_outbound_queue_top(&session->ob_syn); + if (item) { + nghttp2_outbound_queue_pop(&session->ob_syn); + item->queued = 0; + return item; + } + } + + if (session->remote_window_size > 0 && + !nghttp2_pq_empty(&session->ob_da_pq)) { + item = nghttp2_pq_top(&session->ob_da_pq); + nghttp2_pq_pop(&session->ob_da_pq); + item->queued = 0; + return item; + } + + return NULL; +} + +static int session_call_before_frame_send(nghttp2_session *session, + nghttp2_frame *frame) { + int rv; + if (session->callbacks.before_frame_send_callback) { + rv = session->callbacks.before_frame_send_callback(session, frame, + session->user_data); + if (rv != 0) { + return NGHTTP2_ERR_CALLBACK_FAILURE; + } + } + return 0; +} + +static int session_call_on_frame_send(nghttp2_session *session, + nghttp2_frame *frame) { + int rv; + if (session->callbacks.on_frame_send_callback) { + rv = session->callbacks.on_frame_send_callback(session, frame, + session->user_data); + if (rv != 0) { + return NGHTTP2_ERR_CALLBACK_FAILURE; + } + } + return 0; +} + +static int find_stream_on_goaway_func(nghttp2_map_entry *entry, void *ptr) { + nghttp2_close_stream_on_goaway_arg *arg; + nghttp2_stream *stream; + + arg = (nghttp2_close_stream_on_goaway_arg *)ptr; + stream = (nghttp2_stream *)entry; + + if (nghttp2_session_is_my_stream_id(arg->session, stream->stream_id)) { + if (arg->incoming) { + return 0; + } + } else if (!arg->incoming) { + return 0; + } + + if (stream->state != NGHTTP2_STREAM_IDLE && + (stream->flags & NGHTTP2_STREAM_FLAG_CLOSED) == 0 && + stream->stream_id > arg->last_stream_id) { + /* We are collecting streams to close because we cannot call + nghttp2_session_close_stream() inside nghttp2_map_each(). + Reuse closed_next member.. bad choice? */ + assert(stream->closed_next == NULL); + assert(stream->closed_prev == NULL); + + if (arg->head) { + stream->closed_next = arg->head; + arg->head = stream; + } else { + arg->head = stream; + } + } + + return 0; +} + +/* Closes non-idle and non-closed streams whose stream ID > + last_stream_id. If incoming is nonzero, we are going to close + incoming streams. Otherwise, close outgoing streams. */ +static int session_close_stream_on_goaway(nghttp2_session *session, + int32_t last_stream_id, + int incoming) { + int rv; + nghttp2_stream *stream, *next_stream; + nghttp2_close_stream_on_goaway_arg arg = {session, NULL, last_stream_id, + incoming}; + + rv = nghttp2_map_each(&session->streams, find_stream_on_goaway_func, &arg); + assert(rv == 0); + + stream = arg.head; + while (stream) { + next_stream = stream->closed_next; + stream->closed_next = NULL; + rv = nghttp2_session_close_stream(session, stream->stream_id, + NGHTTP2_REFUSED_STREAM); + + /* stream may be deleted here */ + + stream = next_stream; + + if (nghttp2_is_fatal(rv)) { + /* Clean up closed_next member just in case */ + while (stream) { + next_stream = stream->closed_next; + stream->closed_next = NULL; + stream = next_stream; + } + return rv; + } + } + + return 0; +} + +static void session_outbound_item_schedule(nghttp2_session *session, + nghttp2_outbound_item *item, + int32_t weight) { + /* Schedule next write. Offset proportional to the write size. + Stream with heavier weight is scheduled earlier. */ + size_t delta = item->frame.hd.length * NGHTTP2_MAX_WEIGHT / weight; + + if (session->last_cycle < item->cycle) { + session->last_cycle = item->cycle; + } + + /* We pretend to ignore overflow given that the value range of + item->cycle, which is uint64_t. nghttp2 won't explode even when + overflow occurs, there might be some disturbance of priority. */ + item->cycle = session->last_cycle + delta; +} + +/* + * Called after a frame is sent. This function runs + * on_frame_send_callback and handles stream closure upon END_STREAM + * or RST_STREAM. This function does not reset session->aob. It is a + * responsibility of session_after_frame_sent2. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + * NGHTTP2_ERR_CALLBACK_FAILURE + * The callback function failed. + */ +static int session_after_frame_sent1(nghttp2_session *session) { + int rv; + nghttp2_active_outbound_item *aob = &session->aob; + nghttp2_outbound_item *item = aob->item; + nghttp2_bufs *framebufs = &aob->framebufs; + nghttp2_frame *frame; + + frame = &item->frame; + + if (frame->hd.type != NGHTTP2_DATA) { + + if (frame->hd.type == NGHTTP2_HEADERS || + frame->hd.type == NGHTTP2_PUSH_PROMISE) { + + if (nghttp2_bufs_next_present(framebufs)) { + DEBUGF(fprintf(stderr, "send: CONTINUATION exists, just return\n")); + return 0; + } + } + rv = session_call_on_frame_send(session, frame); + if (nghttp2_is_fatal(rv)) { + return rv; + } + switch (frame->hd.type) { + case NGHTTP2_HEADERS: { + nghttp2_headers_aux_data *aux_data; + nghttp2_stream *stream; + + stream = nghttp2_session_get_stream(session, frame->hd.stream_id); + if (!stream) { + break; + } + + if (stream->item == item) { + rv = nghttp2_stream_detach_item(stream, session); + + if (nghttp2_is_fatal(rv)) { + return rv; + } + } + + switch (frame->headers.cat) { + case NGHTTP2_HCAT_REQUEST: { + stream->state = NGHTTP2_STREAM_OPENING; + if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) { + nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_WR); + } + rv = nghttp2_session_close_stream_if_shut_rdwr(session, stream); + if (nghttp2_is_fatal(rv)) { + return rv; + } + /* We assume aux_data is a pointer to nghttp2_headers_aux_data */ + aux_data = &item->aux_data.headers; + if (aux_data->data_prd.read_callback) { + /* nghttp2_submit_data() makes a copy of aux_data->data_prd */ + rv = nghttp2_submit_data(session, NGHTTP2_FLAG_END_STREAM, + frame->hd.stream_id, &aux_data->data_prd); + if (nghttp2_is_fatal(rv)) { + return rv; + } + /* TODO nghttp2_submit_data() may fail if stream has already + DATA frame item. We might have to handle it here. */ + } + break; + } + case NGHTTP2_HCAT_PUSH_RESPONSE: + stream->flags &= ~NGHTTP2_STREAM_FLAG_PUSH; + ++session->num_outgoing_streams; + /* Fall through */ + case NGHTTP2_HCAT_RESPONSE: + stream->state = NGHTTP2_STREAM_OPENED; + /* Fall through */ + case NGHTTP2_HCAT_HEADERS: + if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) { + nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_WR); + } + rv = nghttp2_session_close_stream_if_shut_rdwr(session, stream); + if (nghttp2_is_fatal(rv)) { + return rv; + } + /* We assume aux_data is a pointer to nghttp2_headers_aux_data */ + aux_data = &item->aux_data.headers; + if (aux_data->data_prd.read_callback) { + rv = nghttp2_submit_data(session, NGHTTP2_FLAG_END_STREAM, + frame->hd.stream_id, &aux_data->data_prd); + if (nghttp2_is_fatal(rv)) { + return rv; + } + /* TODO nghttp2_submit_data() may fail if stream has already + DATA frame item. We might have to handle it here. */ + } + break; + } + break; + } + case NGHTTP2_PRIORITY: { + nghttp2_stream *stream; + + if (session->server) { + break; + } + + stream = nghttp2_session_get_stream_raw(session, frame->hd.stream_id); + + if (!stream) { + break; + } + + rv = nghttp2_session_reprioritize_stream(session, stream, + &frame->priority.pri_spec); + + if (nghttp2_is_fatal(rv)) { + return rv; + } + + break; + } + case NGHTTP2_RST_STREAM: + rv = nghttp2_session_close_stream(session, frame->hd.stream_id, + frame->rst_stream.error_code); + if (nghttp2_is_fatal(rv)) { + return rv; + } + break; + case NGHTTP2_GOAWAY: { + nghttp2_goaway_aux_data *aux_data; + + aux_data = &item->aux_data.goaway; + + if ((aux_data->flags & NGHTTP2_GOAWAY_AUX_SHUTDOWN_NOTICE) == 0) { + + if (aux_data->flags & NGHTTP2_GOAWAY_AUX_TERM_ON_SEND) { + session->goaway_flags |= NGHTTP2_GOAWAY_TERM_SENT; + } + + session->goaway_flags |= NGHTTP2_GOAWAY_SENT; + + rv = session_close_stream_on_goaway(session, + frame->goaway.last_stream_id, 1); + + if (nghttp2_is_fatal(rv)) { + return rv; + } + } + + break; + } + default: + break; + } + + return 0; + } else { + nghttp2_stream *stream; + nghttp2_data_aux_data *aux_data; + + aux_data = &item->aux_data.data; + + stream = nghttp2_session_get_stream(session, frame->hd.stream_id); + /* We update flow control window after a frame was completely + sent. This is possible because we choose payload length not to + exceed the window */ + session->remote_window_size -= frame->hd.length; + if (stream) { + stream->remote_window_size -= frame->hd.length; + } + + if (stream && aux_data->eof) { + rv = nghttp2_stream_detach_item(stream, session); + + if (nghttp2_is_fatal(rv)) { + return rv; + } + + /* Call on_frame_send_callback after + nghttp2_stream_detach_item(), so that application can issue + nghttp2_submit_data() in the callback. */ + if (session->callbacks.on_frame_send_callback) { + rv = session_call_on_frame_send(session, frame); + + if (nghttp2_is_fatal(rv)) { + return rv; + } + } + + if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) { + int stream_closed; + + stream_closed = + (stream->shut_flags & NGHTTP2_SHUT_RDWR) == NGHTTP2_SHUT_RDWR; + + nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_WR); + + rv = nghttp2_session_close_stream_if_shut_rdwr(session, stream); + if (nghttp2_is_fatal(rv)) { + return rv; + } + /* stream may be NULL if it was closed */ + if (stream_closed) { + stream = NULL; + } + } + return 0; + } + + if (session->callbacks.on_frame_send_callback) { + rv = session_call_on_frame_send(session, frame); + + if (nghttp2_is_fatal(rv)) { + return rv; + } + } + + return 0; + } + /* Unreachable */ + assert(0); + return 0; +} + +/* + * Called after a frame is sent and session_after_frame_sent1. This + * function is responsible to reset session->aob. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + * NGHTTP2_ERR_CALLBACK_FAILURE + * The callback function failed. + */ +static int session_after_frame_sent2(nghttp2_session *session) { + int rv; + nghttp2_active_outbound_item *aob = &session->aob; + nghttp2_outbound_item *item = aob->item; + nghttp2_bufs *framebufs = &aob->framebufs; + nghttp2_frame *frame; + nghttp2_mem *mem; + + mem = &session->mem; + frame = &item->frame; + + if (frame->hd.type != NGHTTP2_DATA) { + + if (frame->hd.type == NGHTTP2_HEADERS || + frame->hd.type == NGHTTP2_PUSH_PROMISE) { + + if (nghttp2_bufs_next_present(framebufs)) { + framebufs->cur = framebufs->cur->next; + + DEBUGF(fprintf(stderr, "send: next CONTINUATION frame, %zu bytes\n", + nghttp2_buf_len(&framebufs->cur->buf))); + + return 0; + } + } + + active_outbound_item_reset(&session->aob, mem); + + return 0; + } else { + nghttp2_outbound_item *next_item; + nghttp2_stream *stream; + nghttp2_data_aux_data *aux_data; + + aux_data = &item->aux_data.data; + + /* On EOF, we have already detached data. Please note that + application may issue nghttp2_submit_data() in + on_frame_send_callback (call from session_after_frame_sent1), + which attach data to stream. We don't want to detach it. */ + if (aux_data->eof) { + active_outbound_item_reset(aob, mem); + + return 0; + } + + /* Reset no_copy here because next write may not use this. */ + aux_data->no_copy = 0; + + stream = nghttp2_session_get_stream(session, frame->hd.stream_id); + + /* If session is closed or RST_STREAM was queued, we won't send + further data. */ + if (nghttp2_session_predicate_data_send(session, stream) != 0) { + if (stream) { + rv = nghttp2_stream_detach_item(stream, session); + + if (nghttp2_is_fatal(rv)) { + return rv; + } + } + + active_outbound_item_reset(aob, mem); + + return 0; + } + + /* Assuming stream is not NULL */ + assert(stream); + next_item = nghttp2_session_get_next_ob_item(session); + + /* If priority of this stream is higher or equal to other stream + waiting at the top of the queue, we continue to send this + data. */ + if (stream->dpri == NGHTTP2_STREAM_DPRI_TOP && + (next_item == NULL || (next_item->frame.hd.type == NGHTTP2_DATA && + outbound_item_less(item, next_item)))) { + size_t next_readmax; + + next_readmax = nghttp2_session_next_data_read(session, stream); + + if (next_readmax == 0) { + + if (session->remote_window_size == 0 && + stream->remote_window_size > 0) { + + /* If DATA cannot be sent solely due to connection level + window size, just push item to queue again. We never pop + DATA item while connection level window size is 0. */ + rv = nghttp2_pq_push(&session->ob_da_pq, aob->item); + + if (nghttp2_is_fatal(rv)) { + return rv; + } + + aob->item->queued = 1; + } else { + rv = nghttp2_stream_defer_item( + stream, NGHTTP2_STREAM_FLAG_DEFERRED_FLOW_CONTROL, session); + + if (nghttp2_is_fatal(rv)) { + return rv; + } + } + + aob->item = NULL; + active_outbound_item_reset(aob, mem); + + return 0; + } + + nghttp2_bufs_reset(framebufs); + + rv = nghttp2_session_pack_data(session, framebufs, next_readmax, frame, + aux_data, stream); + if (nghttp2_is_fatal(rv)) { + return rv; + } + if (rv == NGHTTP2_ERR_DEFERRED) { + rv = nghttp2_stream_defer_item( + stream, NGHTTP2_STREAM_FLAG_DEFERRED_USER, session); + + if (nghttp2_is_fatal(rv)) { + return rv; + } + + aob->item = NULL; + active_outbound_item_reset(aob, mem); + + return 0; + } + + if (rv == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) { + /* Stop DATA frame chain and issue RST_STREAM to close the + stream. We don't return + NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE intentionally. */ + rv = nghttp2_session_add_rst_stream(session, frame->hd.stream_id, + NGHTTP2_INTERNAL_ERROR); + + if (nghttp2_is_fatal(rv)) { + return rv; + } + + rv = nghttp2_stream_detach_item(stream, session); + + if (nghttp2_is_fatal(rv)) { + return rv; + } + + active_outbound_item_reset(aob, mem); + + return 0; + } + assert(rv == 0); + + if (aux_data->no_copy) { + aob->state = NGHTTP2_OB_SEND_NO_COPY; + } else { + aob->state = NGHTTP2_OB_SEND_DATA; + } + + return 0; + } + + if (stream->dpri == NGHTTP2_STREAM_DPRI_TOP) { + rv = nghttp2_pq_push(&session->ob_da_pq, aob->item); + + if (nghttp2_is_fatal(rv)) { + return rv; + } + + aob->item->queued = 1; + } + + aob->item = NULL; + active_outbound_item_reset(&session->aob, mem); + return 0; + } + /* Unreachable */ + assert(0); + return 0; +} + +static int session_call_send_data(nghttp2_session *session, + nghttp2_outbound_item *item, + nghttp2_bufs *framebufs) { + int rv; + nghttp2_buf *buf; + size_t length; + nghttp2_frame *frame; + nghttp2_data_aux_data *aux_data; + + buf = &framebufs->cur->buf; + frame = &item->frame; + length = frame->hd.length - frame->data.padlen; + aux_data = &item->aux_data.data; + + rv = session->callbacks.send_data_callback(session, frame, buf->pos, length, + &aux_data->data_prd.source, + session->user_data); + + if (rv == 0 || rv == NGHTTP2_ERR_WOULDBLOCK || + rv == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) { + return rv; + } + + return NGHTTP2_ERR_CALLBACK_FAILURE; +} + +static ssize_t nghttp2_session_mem_send_internal(nghttp2_session *session, + const uint8_t **data_ptr, + int fast_cb) { + int rv; + nghttp2_active_outbound_item *aob; + nghttp2_bufs *framebufs; + nghttp2_mem *mem; + + mem = &session->mem; + aob = &session->aob; + framebufs = &aob->framebufs; + + *data_ptr = NULL; + for (;;) { + switch (aob->state) { + case NGHTTP2_OB_POP_ITEM: { + nghttp2_outbound_item *item; + + item = nghttp2_session_pop_next_ob_item(session); + if (item == NULL) { + return 0; + } + + if (item->frame.hd.type == NGHTTP2_DATA || + item->frame.hd.type == NGHTTP2_HEADERS) { + nghttp2_frame *frame; + nghttp2_stream *stream; + + frame = &item->frame; + stream = nghttp2_session_get_stream(session, frame->hd.stream_id); + + if (stream && item == stream->item && + stream->dpri != NGHTTP2_STREAM_DPRI_TOP) { + /* We have DATA with higher priority in queue within the + same dependency tree. */ + break; + } + } + + rv = session_prep_frame(session, item); + if (rv == NGHTTP2_ERR_DEFERRED) { + DEBUGF(fprintf(stderr, "send: frame transmission deferred\n")); + break; + } + if (rv < 0) { + int32_t opened_stream_id = 0; + uint32_t error_code = NGHTTP2_INTERNAL_ERROR; + + DEBUGF(fprintf(stderr, "send: frame preparation failed with %s\n", + nghttp2_strerror(rv))); + /* TODO If the error comes from compressor, the connection + must be closed. */ + if (item->frame.hd.type != NGHTTP2_DATA && + session->callbacks.on_frame_not_send_callback && is_non_fatal(rv)) { + nghttp2_frame *frame = &item->frame; + /* The library is responsible for the transmission of + WINDOW_UPDATE frame, so we don't call error callback for + it. */ + if (frame->hd.type != NGHTTP2_WINDOW_UPDATE && + session->callbacks.on_frame_not_send_callback( + session, frame, rv, session->user_data) != 0) { + + nghttp2_outbound_item_free(item, mem); + nghttp2_mem_free(mem, item); + + return NGHTTP2_ERR_CALLBACK_FAILURE; + } + } + /* We have to close stream opened by failed request HEADERS + or PUSH_PROMISE. */ + switch (item->frame.hd.type) { + case NGHTTP2_HEADERS: + if (item->frame.headers.cat == NGHTTP2_HCAT_REQUEST) { + opened_stream_id = item->frame.hd.stream_id; + if (item->aux_data.headers.canceled) { + error_code = item->aux_data.headers.error_code; + } + } + break; + case NGHTTP2_PUSH_PROMISE: + opened_stream_id = item->frame.push_promise.promised_stream_id; + break; + } + if (opened_stream_id) { + /* careful not to override rv */ + int rv2; + rv2 = nghttp2_session_close_stream(session, opened_stream_id, + error_code); + + if (nghttp2_is_fatal(rv2)) { + return rv2; + } + } + + nghttp2_outbound_item_free(item, mem); + nghttp2_mem_free(mem, item); + active_outbound_item_reset(aob, mem); + + if (rv == NGHTTP2_ERR_HEADER_COMP) { + /* If header compression error occurred, should terminiate + connection. */ + rv = nghttp2_session_terminate_session(session, + NGHTTP2_INTERNAL_ERROR); + } + if (nghttp2_is_fatal(rv)) { + return rv; + } + break; + } + + aob->item = item; + + nghttp2_bufs_rewind(framebufs); + + if (item->frame.hd.type != NGHTTP2_DATA) { + nghttp2_frame *frame; + + frame = &item->frame; + + DEBUGF(fprintf(stderr, "send: next frame: payloadlen=%zu, type=%u, " + "flags=0x%02x, stream_id=%d\n", + frame->hd.length, frame->hd.type, frame->hd.flags, + frame->hd.stream_id)); + + rv = session_call_before_frame_send(session, frame); + if (nghttp2_is_fatal(rv)) { + return rv; + } + } else { + DEBUGF(fprintf(stderr, "send: next frame: DATA\n")); + + if (item->aux_data.data.no_copy) { + aob->state = NGHTTP2_OB_SEND_NO_COPY; + break; + } + } + + DEBUGF(fprintf(stderr, + "send: start transmitting frame type=%u, length=%zd\n", + framebufs->cur->buf.pos[3], + framebufs->cur->buf.last - framebufs->cur->buf.pos)); + + aob->state = NGHTTP2_OB_SEND_DATA; + + break; + } + case NGHTTP2_OB_SEND_DATA: { + size_t datalen; + nghttp2_buf *buf; + + buf = &framebufs->cur->buf; + + if (buf->pos == buf->last) { + DEBUGF(fprintf(stderr, "send: end transmission of a frame\n")); + + /* Frame has completely sent */ + if (fast_cb) { + rv = session_after_frame_sent2(session); + } else { + rv = session_after_frame_sent1(session); + if (rv < 0) { + /* FATAL */ + assert(nghttp2_is_fatal(rv)); + return rv; + } + rv = session_after_frame_sent2(session); + } + if (rv < 0) { + /* FATAL */ + assert(nghttp2_is_fatal(rv)); + return rv; + } + /* We have already adjusted the next state */ + break; + } + + *data_ptr = buf->pos; + datalen = nghttp2_buf_len(buf); + + /* We increment the offset here. If send_callback does not send + everything, we will adjust it. */ + buf->pos += datalen; + + return datalen; + } + case NGHTTP2_OB_SEND_NO_COPY: { + nghttp2_stream *stream; + nghttp2_frame *frame; + + DEBUGF(fprintf(stderr, "send: no copy DATA\n")); + + frame = &aob->item->frame; + + stream = nghttp2_session_get_stream(session, frame->hd.stream_id); + if (stream == NULL) { + DEBUGF(fprintf( + stderr, + "send: no copy DATA cancelled because stream was closed\n")); + + active_outbound_item_reset(aob, mem); + + break; + } + + rv = session_call_send_data(session, aob->item, framebufs); + if (nghttp2_is_fatal(rv)) { + return rv; + } + + if (rv == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) { + rv = nghttp2_stream_detach_item(stream, session); + + if (nghttp2_is_fatal(rv)) { + return rv; + } + + rv = nghttp2_session_add_rst_stream(session, frame->hd.stream_id, + NGHTTP2_INTERNAL_ERROR); + if (nghttp2_is_fatal(rv)) { + return rv; + } + + active_outbound_item_reset(aob, mem); + + break; + } + + if (rv == NGHTTP2_ERR_WOULDBLOCK) { + return 0; + } + + assert(rv == 0); + + rv = session_after_frame_sent1(session); + if (rv < 0) { + assert(nghttp2_is_fatal(rv)); + return rv; + } + rv = session_after_frame_sent2(session); + if (rv < 0) { + assert(nghttp2_is_fatal(rv)); + return rv; + } + + /* We have already adjusted the next state */ + + break; + } + case NGHTTP2_OB_SEND_CLIENT_MAGIC: { + size_t datalen; + nghttp2_buf *buf; + + buf = &framebufs->cur->buf; + + if (buf->pos == buf->last) { + DEBUGF(fprintf(stderr, "send: end transmission of client magic\n")); + active_outbound_item_reset(aob, mem); + break; + } + + *data_ptr = buf->pos; + datalen = nghttp2_buf_len(buf); + + buf->pos += datalen; + + return datalen; + } + } + } +} + +ssize_t nghttp2_session_mem_send(nghttp2_session *session, + const uint8_t **data_ptr) { + int rv; + ssize_t len; + + len = nghttp2_session_mem_send_internal(session, data_ptr, 1); + if (len <= 0) { + return len; + } + + if (session->aob.item) { + /* We have to call session_after_frame_sent1 here to handle stream + closure upon transmission of frames. Otherwise, END_STREAM may + be reached to client before we call nghttp2_session_mem_send + again and we may get exceeding number of incoming streams. */ + rv = session_after_frame_sent1(session); + if (rv < 0) { + assert(nghttp2_is_fatal(rv)); + return (ssize_t)rv; + } + } + + return len; +} + +int nghttp2_session_send(nghttp2_session *session) { + const uint8_t *data; + ssize_t datalen; + ssize_t sentlen; + nghttp2_bufs *framebufs; + + framebufs = &session->aob.framebufs; + + for (;;) { + datalen = nghttp2_session_mem_send_internal(session, &data, 0); + if (datalen <= 0) { + return (int)datalen; + } + sentlen = session->callbacks.send_callback(session, data, datalen, 0, + session->user_data); + if (sentlen < 0) { + if (sentlen == NGHTTP2_ERR_WOULDBLOCK) { + /* Transmission canceled. Rewind the offset */ + framebufs->cur->buf.pos -= datalen; + + return 0; + } + return NGHTTP2_ERR_CALLBACK_FAILURE; + } + /* Rewind the offset to the amount of unsent bytes */ + framebufs->cur->buf.pos -= datalen - sentlen; + } +} + +static ssize_t session_recv(nghttp2_session *session, uint8_t *buf, + size_t len) { + ssize_t rv; + rv = session->callbacks.recv_callback(session, buf, len, 0, + session->user_data); + if (rv > 0) { + if ((size_t)rv > len) { + return NGHTTP2_ERR_CALLBACK_FAILURE; + } + } else if (rv < 0 && rv != NGHTTP2_ERR_WOULDBLOCK && rv != NGHTTP2_ERR_EOF) { + return NGHTTP2_ERR_CALLBACK_FAILURE; + } + return rv; +} + +static int session_call_on_begin_frame(nghttp2_session *session, + const nghttp2_frame_hd *hd) { + int rv; + + if (session->callbacks.on_begin_frame_callback) { + + rv = session->callbacks.on_begin_frame_callback(session, hd, + session->user_data); + + if (rv != 0) { + return NGHTTP2_ERR_CALLBACK_FAILURE; + } + } + + return 0; +} + +static int session_call_on_frame_received(nghttp2_session *session, + nghttp2_frame *frame) { + int rv; + if (session->callbacks.on_frame_recv_callback) { + rv = session->callbacks.on_frame_recv_callback(session, frame, + session->user_data); + if (rv != 0) { + return NGHTTP2_ERR_CALLBACK_FAILURE; + } + } + return 0; +} + +static int session_call_on_begin_headers(nghttp2_session *session, + nghttp2_frame *frame) { + int rv; + DEBUGF(fprintf(stderr, "recv: call on_begin_headers callback stream_id=%d\n", + frame->hd.stream_id)); + if (session->callbacks.on_begin_headers_callback) { + rv = session->callbacks.on_begin_headers_callback(session, frame, + session->user_data); + if (rv == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) { + return rv; + } + if (rv != 0) { + return NGHTTP2_ERR_CALLBACK_FAILURE; + } + } + return 0; +} + +static int session_call_on_header(nghttp2_session *session, + const nghttp2_frame *frame, + const nghttp2_nv *nv) { + int rv; + if (session->callbacks.on_header_callback) { + rv = session->callbacks.on_header_callback( + session, frame, nv->name, nv->namelen, nv->value, nv->valuelen, + nv->flags, session->user_data); + if (rv == NGHTTP2_ERR_PAUSE || + rv == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) { + return rv; + } + if (rv != 0) { + return NGHTTP2_ERR_CALLBACK_FAILURE; + } + } + return 0; +} + +/* + * Handles frame size error. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + */ +static int session_handle_frame_size_error(nghttp2_session *session, + nghttp2_frame *frame _U_) { + /* TODO Currently no callback is called for this error, because we + call this callback before reading any payload */ + return nghttp2_session_terminate_session(session, NGHTTP2_FRAME_SIZE_ERROR); +} + +static int get_error_code_from_lib_error_code(int lib_error_code) { + switch (lib_error_code) { + case NGHTTP2_ERR_STREAM_CLOSED: + return NGHTTP2_STREAM_CLOSED; + case NGHTTP2_ERR_HEADER_COMP: + return NGHTTP2_COMPRESSION_ERROR; + case NGHTTP2_ERR_FRAME_SIZE_ERROR: + return NGHTTP2_FRAME_SIZE_ERROR; + case NGHTTP2_ERR_FLOW_CONTROL: + return NGHTTP2_FLOW_CONTROL_ERROR; + case NGHTTP2_ERR_REFUSED_STREAM: + return NGHTTP2_REFUSED_STREAM; + case NGHTTP2_ERR_PROTO: + case NGHTTP2_ERR_HTTP_HEADER: + case NGHTTP2_ERR_HTTP_MESSAGING: + return NGHTTP2_PROTOCOL_ERROR; + default: + return NGHTTP2_INTERNAL_ERROR; + } +} + +static int session_handle_invalid_stream2(nghttp2_session *session, + int32_t stream_id, + nghttp2_frame *frame, + int lib_error_code) { + int rv; + rv = nghttp2_session_add_rst_stream( + session, stream_id, get_error_code_from_lib_error_code(lib_error_code)); + if (rv != 0) { + return rv; + } + if (session->callbacks.on_invalid_frame_recv_callback) { + if (session->callbacks.on_invalid_frame_recv_callback( + session, frame, lib_error_code, session->user_data) != 0) { + return NGHTTP2_ERR_CALLBACK_FAILURE; + } + } + return 0; +} + +static int session_handle_invalid_stream(nghttp2_session *session, + nghttp2_frame *frame, + int lib_error_code) { + return session_handle_invalid_stream2(session, frame->hd.stream_id, frame, + lib_error_code); +} + +static int session_inflate_handle_invalid_stream(nghttp2_session *session, + nghttp2_frame *frame, + int lib_error_code) { + int rv; + rv = session_handle_invalid_stream(session, frame, lib_error_code); + if (nghttp2_is_fatal(rv)) { + return rv; + } + return NGHTTP2_ERR_IGN_HEADER_BLOCK; +} + +/* + * Handles invalid frame which causes connection error. + */ +static int session_handle_invalid_connection(nghttp2_session *session, + nghttp2_frame *frame, + int lib_error_code, + const char *reason) { + if (session->callbacks.on_invalid_frame_recv_callback) { + if (session->callbacks.on_invalid_frame_recv_callback( + session, frame, lib_error_code, session->user_data) != 0) { + return NGHTTP2_ERR_CALLBACK_FAILURE; + } + } + return nghttp2_session_terminate_session_with_reason( + session, get_error_code_from_lib_error_code(lib_error_code), reason); +} + +static int session_inflate_handle_invalid_connection(nghttp2_session *session, + nghttp2_frame *frame, + int lib_error_code, + const char *reason) { + int rv; + rv = + session_handle_invalid_connection(session, frame, lib_error_code, reason); + if (nghttp2_is_fatal(rv)) { + return rv; + } + return NGHTTP2_ERR_IGN_HEADER_BLOCK; +} + +/* + * Inflates header block in the memory pointed by |in| with |inlen| + * bytes. If this function returns NGHTTP2_ERR_PAUSE, the caller must + * call this function again, until it returns 0 or one of negative + * error code. If |call_header_cb| is zero, the on_header_callback + * are not invoked and the function never return NGHTTP2_ERR_PAUSE. If + * the given |in| is the last chunk of header block, the |final| must + * be nonzero. If header block is successfully processed (which is + * indicated by the return value 0, NGHTTP2_ERR_PAUSE or + * NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE), the number of processed + * input bytes is assigned to the |*readlen_ptr|. + * + * This function return 0 if it succeeds, or one of the negative error + * codes: + * + * NGHTTP2_ERR_CALLBACK_FAILURE + * The callback function failed. + * NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE + * The callback returns this error code, indicating that this + * stream should be RST_STREAMed. + * NGHTTP2_ERR_NOMEM + * Out of memory. + * NGHTTP2_ERR_PAUSE + * The callback function returned NGHTTP2_ERR_PAUSE + * NGHTTP2_ERR_HEADER_COMP + * Header decompression failed + */ +static int inflate_header_block(nghttp2_session *session, nghttp2_frame *frame, + size_t *readlen_ptr, uint8_t *in, size_t inlen, + int final, int call_header_cb) { + ssize_t proclen; + int rv; + int inflate_flags; + nghttp2_nv nv; + nghttp2_stream *stream; + nghttp2_stream *subject_stream; + int trailer = 0; + int token; + + *readlen_ptr = 0; + stream = nghttp2_session_get_stream(session, frame->hd.stream_id); + + if (frame->hd.type == NGHTTP2_PUSH_PROMISE) { + subject_stream = nghttp2_session_get_stream( + session, frame->push_promise.promised_stream_id); + } else { + subject_stream = stream; + trailer = session_trailer_headers(session, stream, frame); + } + + DEBUGF(fprintf(stderr, "recv: decoding header block %zu bytes\n", inlen)); + for (;;) { + inflate_flags = 0; + proclen = nghttp2_hd_inflate_hd2(&session->hd_inflater, &nv, &inflate_flags, + &token, in, inlen, final); + if (nghttp2_is_fatal((int)proclen)) { + return (int)proclen; + } + if (proclen < 0) { + if (session->iframe.state == NGHTTP2_IB_READ_HEADER_BLOCK) { + if (subject_stream && subject_stream->state != NGHTTP2_STREAM_CLOSING) { + /* Adding RST_STREAM here is very important. It prevents + from invoking subsequent callbacks for the same stream + ID. */ + rv = nghttp2_session_add_rst_stream( + session, subject_stream->stream_id, NGHTTP2_COMPRESSION_ERROR); + + if (nghttp2_is_fatal(rv)) { + return rv; + } + } + } + rv = + nghttp2_session_terminate_session(session, NGHTTP2_COMPRESSION_ERROR); + if (nghttp2_is_fatal(rv)) { + return rv; + } + + return NGHTTP2_ERR_HEADER_COMP; + } + in += proclen; + inlen -= proclen; + *readlen_ptr += proclen; + + DEBUGF(fprintf(stderr, "recv: proclen=%zd\n", proclen)); + + if (call_header_cb && (inflate_flags & NGHTTP2_HD_INFLATE_EMIT)) { + rv = 0; + if (subject_stream && session_enforce_http_messaging(session)) { + rv = nghttp2_http_on_header(session, subject_stream, frame, &nv, token, + trailer); + if (rv == NGHTTP2_ERR_HTTP_HEADER) { + DEBUGF(fprintf( + stderr, "recv: HTTP error: type=%d, id=%d, header %.*s: %.*s\n", + frame->hd.type, subject_stream->stream_id, (int)nv.namelen, + nv.name, (int)nv.valuelen, nv.value)); + + rv = + session_handle_invalid_stream2(session, subject_stream->stream_id, + frame, NGHTTP2_ERR_HTTP_HEADER); + if (nghttp2_is_fatal(rv)) { + return rv; + } + return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; + } + + if (rv == NGHTTP2_ERR_IGN_HTTP_HEADER) { + /* header is ignored */ + DEBUGF(fprintf( + stderr, "recv: HTTP ignored: type=%d, id=%d, header %.*s: %.*s\n", + frame->hd.type, subject_stream->stream_id, (int)nv.namelen, + nv.name, (int)nv.valuelen, nv.value)); + } + } + if (rv == 0) { + rv = session_call_on_header(session, frame, &nv); + /* This handles NGHTTP2_ERR_PAUSE and + NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE as well */ + if (rv != 0) { + return rv; + } + } + } + if (inflate_flags & NGHTTP2_HD_INFLATE_FINAL) { + nghttp2_hd_inflate_end_headers(&session->hd_inflater); + break; + } + if ((inflate_flags & NGHTTP2_HD_INFLATE_EMIT) == 0 && inlen == 0) { + break; + } + } + return 0; +} + +/* + * Decompress header blocks of incoming request HEADERS and also call + * additional callbacks. This function can be called again if this + * function returns NGHTTP2_ERR_PAUSE. + * + * This function returns 0 if it succeeds, or one of negative error + * codes: + * + * NGHTTP2_ERR_CALLBACK_FAILURE + * The callback function failed. + * NGHTTP2_ERR_NOMEM + * Out of memory. + */ +int nghttp2_session_end_request_headers_received(nghttp2_session *session _U_, + nghttp2_frame *frame, + nghttp2_stream *stream) { + if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) { + nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_RD); + } + /* Here we assume that stream is not shutdown in NGHTTP2_SHUT_WR */ + return 0; +} + +/* + * Decompress header blocks of incoming (push-)response HEADERS and + * also call additional callbacks. This function can be called again + * if this function returns NGHTTP2_ERR_PAUSE. + * + * This function returns 0 if it succeeds, or one of negative error + * codes: + * + * NGHTTP2_ERR_CALLBACK_FAILURE + * The callback function failed. + * NGHTTP2_ERR_NOMEM + * Out of memory. + */ +int nghttp2_session_end_response_headers_received(nghttp2_session *session, + nghttp2_frame *frame, + nghttp2_stream *stream) { + int rv; + if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) { + /* This is the last frame of this stream, so disallow + further receptions. */ + nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_RD); + rv = nghttp2_session_close_stream_if_shut_rdwr(session, stream); + if (nghttp2_is_fatal(rv)) { + return rv; + } + } + return 0; +} + +/* + * Decompress header blocks of incoming HEADERS and also call + * additional callbacks. This function can be called again if this + * function returns NGHTTP2_ERR_PAUSE. + * + * This function returns 0 if it succeeds, or one of negative error + * codes: + * + * NGHTTP2_ERR_CALLBACK_FAILURE + * The callback function failed. + * NGHTTP2_ERR_NOMEM + * Out of memory. + */ +int nghttp2_session_end_headers_received(nghttp2_session *session, + nghttp2_frame *frame, + nghttp2_stream *stream) { + int rv; + if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) { + if (!nghttp2_session_is_my_stream_id(session, frame->hd.stream_id)) { + } + nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_RD); + rv = nghttp2_session_close_stream_if_shut_rdwr(session, stream); + if (nghttp2_is_fatal(rv)) { + return rv; + } + } + return 0; +} + +static int session_after_header_block_received(nghttp2_session *session) { + int rv = 0; + int call_cb = 1; + nghttp2_frame *frame = &session->iframe.frame; + nghttp2_stream *stream; + + /* We don't call on_frame_recv_callback if stream has been closed + already or being closed. */ + stream = nghttp2_session_get_stream(session, frame->hd.stream_id); + if (!stream || stream->state == NGHTTP2_STREAM_CLOSING) { + return 0; + } + + if (session_enforce_http_messaging(session)) { + if (frame->hd.type == NGHTTP2_PUSH_PROMISE) { + nghttp2_stream *subject_stream; + + subject_stream = nghttp2_session_get_stream( + session, frame->push_promise.promised_stream_id); + if (subject_stream) { + rv = nghttp2_http_on_request_headers(subject_stream, frame); + } + } else { + assert(frame->hd.type == NGHTTP2_HEADERS); + switch (frame->headers.cat) { + case NGHTTP2_HCAT_REQUEST: + rv = nghttp2_http_on_request_headers(stream, frame); + break; + case NGHTTP2_HCAT_RESPONSE: + case NGHTTP2_HCAT_PUSH_RESPONSE: + rv = nghttp2_http_on_response_headers(stream); + break; + case NGHTTP2_HCAT_HEADERS: + if (stream->http_flags & NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE) { + assert(!session->server); + rv = nghttp2_http_on_response_headers(stream); + } else { + rv = nghttp2_http_on_trailer_headers(stream, frame); + } + break; + default: + assert(0); + } + if (rv == 0 && (frame->hd.flags & NGHTTP2_FLAG_END_STREAM)) { + rv = nghttp2_http_on_remote_end_stream(stream); + } + } + if (rv != 0) { + int32_t stream_id; + + if (frame->hd.type == NGHTTP2_PUSH_PROMISE) { + stream_id = frame->push_promise.promised_stream_id; + } else { + stream_id = frame->hd.stream_id; + } + + call_cb = 0; + + rv = session_handle_invalid_stream2(session, stream_id, frame, + NGHTTP2_ERR_HTTP_MESSAGING); + if (nghttp2_is_fatal(rv)) { + return rv; + } + } + } + + if (call_cb) { + rv = session_call_on_frame_received(session, frame); + if (nghttp2_is_fatal(rv)) { + return rv; + } + } + + if (frame->hd.type != NGHTTP2_HEADERS) { + return 0; + } + + switch (frame->headers.cat) { + case NGHTTP2_HCAT_REQUEST: + return nghttp2_session_end_request_headers_received(session, frame, stream); + case NGHTTP2_HCAT_RESPONSE: + case NGHTTP2_HCAT_PUSH_RESPONSE: + return nghttp2_session_end_response_headers_received(session, frame, + stream); + case NGHTTP2_HCAT_HEADERS: + return nghttp2_session_end_headers_received(session, frame, stream); + default: + assert(0); + } + return 0; +} + +int nghttp2_session_on_request_headers_received(nghttp2_session *session, + nghttp2_frame *frame) { + int rv = 0; + nghttp2_stream *stream; + if (frame->hd.stream_id == 0) { + return session_inflate_handle_invalid_connection( + session, frame, NGHTTP2_ERR_PROTO, "request HEADERS: stream_id == 0"); + } + + /* If client recieves idle stream from server, it is invalid + regardless stream ID is even or odd. This is because client is + not expected to receive request from server. */ + if (!session->server) { + if (session_detect_idle_stream(session, frame->hd.stream_id)) { + return session_inflate_handle_invalid_connection( + session, frame, NGHTTP2_ERR_PROTO, + "request HEADERS: client received request"); + } + + return NGHTTP2_ERR_IGN_HEADER_BLOCK; + } + + if (!session_is_new_peer_stream_id(session, frame->hd.stream_id)) { + /* The spec says if an endpoint receives a HEADERS with invalid + stream ID, it MUST issue connection error with error code + PROTOCOL_ERROR. But we could get trailer HEADERS after we have + sent RST_STREAM to this stream and peer have not received it. + Then connection error is too harsh. It means that we only use + connection error if stream ID refers idle stream. OTherwise we + just ignore HEADERS for now. */ + if (session_detect_idle_stream(session, frame->hd.stream_id)) { + return session_inflate_handle_invalid_connection( + session, frame, NGHTTP2_ERR_PROTO, + "request HEADERS: invalid stream_id"); + } + + return NGHTTP2_ERR_IGN_HEADER_BLOCK; + } + session->last_recv_stream_id = frame->hd.stream_id; + + if (session->goaway_flags & NGHTTP2_GOAWAY_SENT) { + /* We just ignore stream after GOAWAY was queued */ + return NGHTTP2_ERR_IGN_HEADER_BLOCK; + } + + if (session_is_incoming_concurrent_streams_max(session)) { + return session_inflate_handle_invalid_connection( + session, frame, NGHTTP2_ERR_PROTO, + "request HEADERS: max concurrent streams exceeded"); + } + + if (frame->headers.pri_spec.stream_id == frame->hd.stream_id) { + return session_inflate_handle_invalid_connection( + session, frame, NGHTTP2_ERR_PROTO, "request HEADERS: depend on itself"); + } + + if (session_is_incoming_concurrent_streams_pending_max(session)) { + return session_inflate_handle_invalid_stream(session, frame, + NGHTTP2_ERR_REFUSED_STREAM); + } + + stream = nghttp2_session_open_stream( + session, frame->hd.stream_id, NGHTTP2_STREAM_FLAG_NONE, + &frame->headers.pri_spec, NGHTTP2_STREAM_OPENING, NULL); + if (!stream) { + return NGHTTP2_ERR_NOMEM; + } + session->last_proc_stream_id = session->last_recv_stream_id; + rv = session_call_on_begin_headers(session, frame); + if (rv != 0) { + return rv; + } + return 0; +} + +int nghttp2_session_on_response_headers_received(nghttp2_session *session, + nghttp2_frame *frame, + nghttp2_stream *stream) { + int rv; + /* This function is only called if stream->state == + NGHTTP2_STREAM_OPENING and stream_id is local side initiated. */ + assert(stream->state == NGHTTP2_STREAM_OPENING && + nghttp2_session_is_my_stream_id(session, frame->hd.stream_id)); + if (frame->hd.stream_id == 0) { + return session_inflate_handle_invalid_connection( + session, frame, NGHTTP2_ERR_PROTO, "response HEADERS: stream_id == 0"); + } + if (stream->shut_flags & NGHTTP2_SHUT_RD) { + /* half closed (remote): from the spec: + + If an endpoint receives additional frames for a stream that is + in this state it MUST respond with a stream error (Section + 5.4.2) of type STREAM_CLOSED. + */ + return session_inflate_handle_invalid_stream(session, frame, + NGHTTP2_ERR_STREAM_CLOSED); + } + stream->state = NGHTTP2_STREAM_OPENED; + rv = session_call_on_begin_headers(session, frame); + if (rv != 0) { + return rv; + } + return 0; +} + +int nghttp2_session_on_push_response_headers_received(nghttp2_session *session, + nghttp2_frame *frame, + nghttp2_stream *stream) { + int rv = 0; + assert(stream->state == NGHTTP2_STREAM_RESERVED); + if (frame->hd.stream_id == 0) { + return session_inflate_handle_invalid_connection( + session, frame, NGHTTP2_ERR_PROTO, + "push response HEADERS: stream_id == 0"); + } + if (session->goaway_flags) { + /* We don't accept new stream after GOAWAY is sent or received. */ + return NGHTTP2_ERR_IGN_HEADER_BLOCK; + } + + if (session_is_incoming_concurrent_streams_max(session)) { + return session_inflate_handle_invalid_connection( + session, frame, NGHTTP2_ERR_PROTO, + "push response HEADERS: max concurrent streams exceeded"); + } + if (session_is_incoming_concurrent_streams_pending_max(session)) { + return session_inflate_handle_invalid_stream(session, frame, + NGHTTP2_ERR_REFUSED_STREAM); + } + + nghttp2_stream_promise_fulfilled(stream); + ++session->num_incoming_streams; + rv = session_call_on_begin_headers(session, frame); + if (rv != 0) { + return rv; + } + return 0; +} + +int nghttp2_session_on_headers_received(nghttp2_session *session, + nghttp2_frame *frame, + nghttp2_stream *stream) { + int rv = 0; + if (frame->hd.stream_id == 0) { + return session_inflate_handle_invalid_connection( + session, frame, NGHTTP2_ERR_PROTO, "HEADERS: stream_id == 0"); + } + if (stream->state == NGHTTP2_STREAM_RESERVED) { + /* reserved. The valid push response HEADERS is processed by + nghttp2_session_on_push_response_headers_received(). This + generic HEADERS is called invalid cases for HEADERS against + reserved state. */ + return session_inflate_handle_invalid_connection( + session, frame, NGHTTP2_ERR_PROTO, "HEADERS: stream in reserved"); + } + if ((stream->shut_flags & NGHTTP2_SHUT_RD)) { + /* half closed (remote): from the spec: + + If an endpoint receives additional frames for a stream that is + in this state it MUST respond with a stream error (Section + 5.4.2) of type STREAM_CLOSED. + */ + return session_inflate_handle_invalid_stream(session, frame, + NGHTTP2_ERR_STREAM_CLOSED); + } + if (nghttp2_session_is_my_stream_id(session, frame->hd.stream_id)) { + if (stream->state == NGHTTP2_STREAM_OPENED) { + rv = session_call_on_begin_headers(session, frame); + if (rv != 0) { + return rv; + } + return 0; + } else if (stream->state == NGHTTP2_STREAM_CLOSING) { + /* This is race condition. NGHTTP2_STREAM_CLOSING indicates + that we queued RST_STREAM but it has not been sent. It will + eventually sent, so we just ignore this frame. */ + return NGHTTP2_ERR_IGN_HEADER_BLOCK; + } else { + return session_inflate_handle_invalid_stream(session, frame, + NGHTTP2_ERR_PROTO); + } + } + /* If this is remote peer initiated stream, it is OK unless it + has sent END_STREAM frame already. But if stream is in + NGHTTP2_STREAM_CLOSING, we discard the frame. This is a race + condition. */ + if (stream->state != NGHTTP2_STREAM_CLOSING) { + rv = session_call_on_begin_headers(session, frame); + if (rv != 0) { + return rv; + } + return 0; + } + return NGHTTP2_ERR_IGN_HEADER_BLOCK; +} + +static int session_process_headers_frame(nghttp2_session *session) { + int rv; + nghttp2_inbound_frame *iframe = &session->iframe; + nghttp2_frame *frame = &iframe->frame; + nghttp2_stream *stream; + + rv = nghttp2_frame_unpack_headers_payload(&frame->headers, iframe->sbuf.pos, + nghttp2_buf_len(&iframe->sbuf)); + + if (rv != 0) { + return nghttp2_session_terminate_session_with_reason( + session, NGHTTP2_PROTOCOL_ERROR, "HEADERS: could not unpack"); + } + stream = nghttp2_session_get_stream(session, frame->hd.stream_id); + if (!stream) { + frame->headers.cat = NGHTTP2_HCAT_REQUEST; + return nghttp2_session_on_request_headers_received(session, frame); + } + + if (nghttp2_session_is_my_stream_id(session, frame->hd.stream_id)) { + if (stream->state == NGHTTP2_STREAM_OPENING) { + frame->headers.cat = NGHTTP2_HCAT_RESPONSE; + return nghttp2_session_on_response_headers_received(session, frame, + stream); + } + frame->headers.cat = NGHTTP2_HCAT_HEADERS; + return nghttp2_session_on_headers_received(session, frame, stream); + } + if (stream->state == NGHTTP2_STREAM_RESERVED) { + frame->headers.cat = NGHTTP2_HCAT_PUSH_RESPONSE; + return nghttp2_session_on_push_response_headers_received(session, frame, + stream); + } + frame->headers.cat = NGHTTP2_HCAT_HEADERS; + return nghttp2_session_on_headers_received(session, frame, stream); +} + +int nghttp2_session_on_priority_received(nghttp2_session *session, + nghttp2_frame *frame) { + int rv; + nghttp2_stream *stream; + + if (frame->hd.stream_id == 0) { + return session_handle_invalid_connection(session, frame, NGHTTP2_ERR_PROTO, + "PRIORITY: stream_id == 0"); + } + + if (!session->server) { + /* Re-prioritization works only in server */ + return session_call_on_frame_received(session, frame); + } + + stream = nghttp2_session_get_stream_raw(session, frame->hd.stream_id); + + if (!stream) { + /* PRIORITY against idle stream can create anchor node in + dependency tree. */ + if (!session_detect_idle_stream(session, frame->hd.stream_id)) { + return 0; + } + + stream = nghttp2_session_open_stream( + session, frame->hd.stream_id, NGHTTP2_STREAM_FLAG_NONE, + &frame->priority.pri_spec, NGHTTP2_STREAM_IDLE, NULL); + + if (stream == NULL) { + return NGHTTP2_ERR_NOMEM; + } + } else { + rv = nghttp2_session_reprioritize_stream(session, stream, + &frame->priority.pri_spec); + + if (nghttp2_is_fatal(rv)) { + return rv; + } + } + + return session_call_on_frame_received(session, frame); +} + +static int session_process_priority_frame(nghttp2_session *session) { + nghttp2_inbound_frame *iframe = &session->iframe; + nghttp2_frame *frame = &iframe->frame; + + nghttp2_frame_unpack_priority_payload(&frame->priority, iframe->sbuf.pos, + nghttp2_buf_len(&iframe->sbuf)); + + return nghttp2_session_on_priority_received(session, frame); +} + +int nghttp2_session_on_rst_stream_received(nghttp2_session *session, + nghttp2_frame *frame) { + int rv; + nghttp2_stream *stream; + if (frame->hd.stream_id == 0) { + return session_handle_invalid_connection(session, frame, NGHTTP2_ERR_PROTO, + "RST_STREAM: stream_id == 0"); + } + stream = nghttp2_session_get_stream(session, frame->hd.stream_id); + if (!stream) { + if (session_detect_idle_stream(session, frame->hd.stream_id)) { + return session_handle_invalid_connection( + session, frame, NGHTTP2_ERR_PROTO, "RST_STREAM: stream in idle"); + } + } + + rv = session_call_on_frame_received(session, frame); + if (rv != 0) { + return rv; + } + rv = nghttp2_session_close_stream(session, frame->hd.stream_id, + frame->rst_stream.error_code); + if (nghttp2_is_fatal(rv)) { + return rv; + } + return 0; +} + +static int session_process_rst_stream_frame(nghttp2_session *session) { + nghttp2_inbound_frame *iframe = &session->iframe; + nghttp2_frame *frame = &iframe->frame; + + nghttp2_frame_unpack_rst_stream_payload(&frame->rst_stream, iframe->sbuf.pos, + nghttp2_buf_len(&iframe->sbuf)); + + return nghttp2_session_on_rst_stream_received(session, frame); +} + +static int update_remote_initial_window_size_func(nghttp2_map_entry *entry, + void *ptr) { + int rv; + nghttp2_update_window_size_arg *arg; + nghttp2_stream *stream; + + arg = (nghttp2_update_window_size_arg *)ptr; + stream = (nghttp2_stream *)entry; + + rv = nghttp2_stream_update_remote_initial_window_size( + stream, arg->new_window_size, arg->old_window_size); + if (rv != 0) { + return nghttp2_session_terminate_session(arg->session, + NGHTTP2_FLOW_CONTROL_ERROR); + } + + /* If window size gets positive, push deferred DATA frame to + outbound queue. */ + if (stream->remote_window_size > 0 && + nghttp2_stream_check_deferred_by_flow_control(stream)) { + + rv = nghttp2_stream_resume_deferred_item( + stream, NGHTTP2_STREAM_FLAG_DEFERRED_FLOW_CONTROL, arg->session); + + if (nghttp2_is_fatal(rv)) { + return rv; + } + } + return 0; +} + +/* + * Updates the remote initial window size of all active streams. If + * error occurs, all streams may not be updated. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + */ +static int +session_update_remote_initial_window_size(nghttp2_session *session, + int32_t new_initial_window_size) { + nghttp2_update_window_size_arg arg; + + arg.session = session; + arg.new_window_size = new_initial_window_size; + arg.old_window_size = session->remote_settings.initial_window_size; + + return nghttp2_map_each(&session->streams, + update_remote_initial_window_size_func, &arg); +} + +static int update_local_initial_window_size_func(nghttp2_map_entry *entry, + void *ptr) { + int rv; + nghttp2_update_window_size_arg *arg; + nghttp2_stream *stream; + arg = (nghttp2_update_window_size_arg *)ptr; + stream = (nghttp2_stream *)entry; + rv = nghttp2_stream_update_local_initial_window_size( + stream, arg->new_window_size, arg->old_window_size); + if (rv != 0) { + return nghttp2_session_terminate_session(arg->session, + NGHTTP2_FLOW_CONTROL_ERROR); + } + if (!(arg->session->opt_flags & NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE)) { + + if (nghttp2_should_send_window_update(stream->local_window_size, + stream->recv_window_size)) { + + rv = nghttp2_session_add_window_update(arg->session, NGHTTP2_FLAG_NONE, + stream->stream_id, + stream->recv_window_size); + if (rv != 0) { + return rv; + } + stream->recv_window_size = 0; + } + } + return 0; +} + +/* + * Updates the local initial window size of all active streams. If + * error occurs, all streams may not be updated. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + */ +static int +session_update_local_initial_window_size(nghttp2_session *session, + int32_t new_initial_window_size, + int32_t old_initial_window_size) { + nghttp2_update_window_size_arg arg; + arg.session = session; + arg.new_window_size = new_initial_window_size; + arg.old_window_size = old_initial_window_size; + return nghttp2_map_each(&session->streams, + update_local_initial_window_size_func, &arg); +} + +/* + * Apply SETTINGS values |iv| having |niv| elements to the local + * settings. We assumes that all values in |iv| is correct, since we + * validated them in nghttp2_session_add_settings() already. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_HEADER_COMP + * The header table size is out of range + * NGHTTP2_ERR_NOMEM + * Out of memory + */ +int nghttp2_session_update_local_settings(nghttp2_session *session, + nghttp2_settings_entry *iv, + size_t niv) { + int rv; + size_t i; + int32_t new_initial_window_size = -1; + int32_t header_table_size = -1; + uint8_t header_table_size_seen = 0; + /* Use the value last seen. */ + for (i = 0; i < niv; ++i) { + switch (iv[i].settings_id) { + case NGHTTP2_SETTINGS_HEADER_TABLE_SIZE: + header_table_size_seen = 1; + header_table_size = iv[i].value; + break; + case NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE: + new_initial_window_size = iv[i].value; + break; + } + } + if (header_table_size_seen) { + rv = nghttp2_hd_inflate_change_table_size(&session->hd_inflater, + header_table_size); + if (rv != 0) { + return rv; + } + } + if (new_initial_window_size != -1) { + rv = session_update_local_initial_window_size( + session, new_initial_window_size, + session->local_settings.initial_window_size); + if (rv != 0) { + return rv; + } + } + + for (i = 0; i < niv; ++i) { + switch (iv[i].settings_id) { + case NGHTTP2_SETTINGS_HEADER_TABLE_SIZE: + session->local_settings.header_table_size = iv[i].value; + break; + case NGHTTP2_SETTINGS_ENABLE_PUSH: + session->local_settings.enable_push = iv[i].value; + break; + case NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS: + session->local_settings.max_concurrent_streams = iv[i].value; + break; + case NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE: + session->local_settings.initial_window_size = iv[i].value; + break; + case NGHTTP2_SETTINGS_MAX_FRAME_SIZE: + session->local_settings.max_frame_size = iv[i].value; + break; + case NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE: + session->local_settings.max_header_list_size = iv[i].value; + break; + } + } + + session->pending_local_max_concurrent_stream = + NGHTTP2_INITIAL_MAX_CONCURRENT_STREAMS; + session->pending_enable_push = 1; + + return 0; +} + +int nghttp2_session_on_settings_received(nghttp2_session *session, + nghttp2_frame *frame, int noack) { + int rv; + size_t i; + nghttp2_mem *mem; + + mem = &session->mem; + + if (frame->hd.stream_id != 0) { + return session_handle_invalid_connection(session, frame, NGHTTP2_ERR_PROTO, + "SETTINGS: stream_id != 0"); + } + if (frame->hd.flags & NGHTTP2_FLAG_ACK) { + if (frame->settings.niv != 0) { + return session_handle_invalid_connection( + session, frame, NGHTTP2_ERR_FRAME_SIZE_ERROR, + "SETTINGS: ACK and payload != 0"); + } + if (session->inflight_niv == -1) { + return session_handle_invalid_connection( + session, frame, NGHTTP2_ERR_PROTO, "SETTINGS: unexpected ACK"); + } + rv = nghttp2_session_update_local_settings(session, session->inflight_iv, + session->inflight_niv); + nghttp2_mem_free(mem, session->inflight_iv); + session->inflight_iv = NULL; + session->inflight_niv = -1; + if (rv != 0) { + if (nghttp2_is_fatal(rv)) { + return rv; + } + return session_handle_invalid_connection(session, frame, rv, NULL); + } + return session_call_on_frame_received(session, frame); + } + + for (i = 0; i < frame->settings.niv; ++i) { + nghttp2_settings_entry *entry = &frame->settings.iv[i]; + + switch (entry->settings_id) { + case NGHTTP2_SETTINGS_HEADER_TABLE_SIZE: + + if (entry->value > NGHTTP2_MAX_HEADER_TABLE_SIZE) { + return session_handle_invalid_connection( + session, frame, NGHTTP2_ERR_HEADER_COMP, + "SETTINGS: too large SETTINGS_HEADER_TABLE_SIZE"); + } + + rv = nghttp2_hd_deflate_change_table_size(&session->hd_deflater, + entry->value); + if (rv != 0) { + if (nghttp2_is_fatal(rv)) { + return rv; + } else { + return session_handle_invalid_connection( + session, frame, NGHTTP2_ERR_HEADER_COMP, NULL); + } + } + + session->remote_settings.header_table_size = entry->value; + + break; + case NGHTTP2_SETTINGS_ENABLE_PUSH: + + if (entry->value != 0 && entry->value != 1) { + return session_handle_invalid_connection( + session, frame, NGHTTP2_ERR_PROTO, + "SETTINGS: invalid SETTINGS_ENBLE_PUSH"); + } + + if (!session->server && entry->value != 0) { + return session_handle_invalid_connection( + session, frame, NGHTTP2_ERR_PROTO, + "SETTINGS: server attempted to enable push"); + } + + session->remote_settings.enable_push = entry->value; + + break; + case NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS: + + session->remote_settings.max_concurrent_streams = entry->value; + + break; + case NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE: + + /* Update the initial window size of the all active streams */ + /* Check that initial_window_size < (1u << 31) */ + if (entry->value > NGHTTP2_MAX_WINDOW_SIZE) { + return session_handle_invalid_connection( + session, frame, NGHTTP2_ERR_FLOW_CONTROL, + "SETTINGS: too large SETTINGS_INITIAL_WINDOW_SIZE"); + } + + rv = session_update_remote_initial_window_size(session, entry->value); + + if (nghttp2_is_fatal(rv)) { + return rv; + } + + if (rv != 0) { + return session_handle_invalid_connection( + session, frame, NGHTTP2_ERR_FLOW_CONTROL, NULL); + } + + session->remote_settings.initial_window_size = entry->value; + + break; + case NGHTTP2_SETTINGS_MAX_FRAME_SIZE: + + if (entry->value < NGHTTP2_MAX_FRAME_SIZE_MIN || + entry->value > NGHTTP2_MAX_FRAME_SIZE_MAX) { + return session_handle_invalid_connection( + session, frame, NGHTTP2_ERR_PROTO, + "SETTINGS: invalid SETTINGS_MAX_FRAME_SIZE"); + } + + session->remote_settings.max_frame_size = entry->value; + + break; + case NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE: + + session->remote_settings.max_header_list_size = entry->value; + + break; + } + } + + if (!noack && !session_is_closing(session)) { + rv = nghttp2_session_add_settings(session, NGHTTP2_FLAG_ACK, NULL, 0); + + if (rv != 0) { + if (nghttp2_is_fatal(rv)) { + return rv; + } + + return session_handle_invalid_connection(session, frame, + NGHTTP2_ERR_INTERNAL, NULL); + } + } + + return session_call_on_frame_received(session, frame); +} + +static int session_process_settings_frame(nghttp2_session *session) { + int rv; + nghttp2_inbound_frame *iframe = &session->iframe; + nghttp2_frame *frame = &iframe->frame; + size_t i; + nghttp2_settings_entry min_header_size_entry; + nghttp2_mem *mem; + + mem = &session->mem; + min_header_size_entry = iframe->iv[NGHTTP2_INBOUND_NUM_IV - 1]; + + if (min_header_size_entry.value < UINT32_MAX) { + /* If we have less value, then we must have + SETTINGS_HEADER_TABLE_SIZE in i < iframe->niv */ + for (i = 0; i < iframe->niv; ++i) { + if (iframe->iv[i].settings_id == NGHTTP2_SETTINGS_HEADER_TABLE_SIZE) { + break; + } + } + + assert(i < iframe->niv); + + if (min_header_size_entry.value != iframe->iv[i].value) { + iframe->iv[iframe->niv++] = iframe->iv[i]; + iframe->iv[i] = min_header_size_entry; + } + } + + rv = nghttp2_frame_unpack_settings_payload(&frame->settings, iframe->iv, + iframe->niv, mem); + if (rv != 0) { + assert(nghttp2_is_fatal(rv)); + return rv; + } + return nghttp2_session_on_settings_received(session, frame, 0 /* ACK */); +} + +int nghttp2_session_on_push_promise_received(nghttp2_session *session, + nghttp2_frame *frame) { + int rv; + nghttp2_stream *stream; + nghttp2_stream *promised_stream; + nghttp2_priority_spec pri_spec; + + if (frame->hd.stream_id == 0) { + return session_inflate_handle_invalid_connection( + session, frame, NGHTTP2_ERR_PROTO, "PUSH_PROMISE: stream_id == 0"); + } + if (session->server || session->local_settings.enable_push == 0) { + return session_inflate_handle_invalid_connection( + session, frame, NGHTTP2_ERR_PROTO, "PUSH_PROMISE: push disabled"); + } + if (session->goaway_flags) { + /* We just dicard PUSH_PROMISE after GOAWAY is sent or + received. */ + return NGHTTP2_ERR_IGN_HEADER_BLOCK; + } + + if (!nghttp2_session_is_my_stream_id(session, frame->hd.stream_id)) { + return session_inflate_handle_invalid_connection( + session, frame, NGHTTP2_ERR_PROTO, "PUSH_PROMISE: invalid stream_id"); + } + + if (!session_is_new_peer_stream_id(session, + frame->push_promise.promised_stream_id)) { + /* The spec says if an endpoint receives a PUSH_PROMISE with + illegal stream ID is subject to a connection error of type + PROTOCOL_ERROR. */ + return session_inflate_handle_invalid_connection( + session, frame, NGHTTP2_ERR_PROTO, + "PUSH_PROMISE: invalid promised_stream_id"); + } + session->last_recv_stream_id = frame->push_promise.promised_stream_id; + stream = nghttp2_session_get_stream(session, frame->hd.stream_id); + if (!stream || stream->state == NGHTTP2_STREAM_CLOSING || + !session->pending_enable_push) { + if (!stream) { + if (session_detect_idle_stream(session, frame->hd.stream_id)) { + return session_inflate_handle_invalid_connection( + session, frame, NGHTTP2_ERR_PROTO, "PUSH_PROMISE: stream in idle"); + } + } + rv = nghttp2_session_add_rst_stream(session, + frame->push_promise.promised_stream_id, + NGHTTP2_REFUSED_STREAM); + if (rv != 0) { + return rv; + } + return NGHTTP2_ERR_IGN_HEADER_BLOCK; + } + if (stream->shut_flags & NGHTTP2_SHUT_RD) { + if (session->callbacks.on_invalid_frame_recv_callback) { + if (session->callbacks.on_invalid_frame_recv_callback( + session, frame, NGHTTP2_PROTOCOL_ERROR, session->user_data) != + 0) { + return NGHTTP2_ERR_CALLBACK_FAILURE; + } + } + rv = nghttp2_session_add_rst_stream(session, + frame->push_promise.promised_stream_id, + NGHTTP2_PROTOCOL_ERROR); + if (rv != 0) { + return rv; + } + return NGHTTP2_ERR_IGN_HEADER_BLOCK; + } + + /* TODO It is unclear reserved stream dpeneds on associated + stream with or without exclusive flag set */ + nghttp2_priority_spec_init(&pri_spec, stream->stream_id, + NGHTTP2_DEFAULT_WEIGHT, 0); + + promised_stream = nghttp2_session_open_stream( + session, frame->push_promise.promised_stream_id, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec, NGHTTP2_STREAM_RESERVED, NULL); + + if (!promised_stream) { + return NGHTTP2_ERR_NOMEM; + } + + session->last_proc_stream_id = session->last_recv_stream_id; + rv = session_call_on_begin_headers(session, frame); + if (rv != 0) { + return rv; + } + return 0; +} + +static int session_process_push_promise_frame(nghttp2_session *session) { + int rv; + nghttp2_inbound_frame *iframe = &session->iframe; + nghttp2_frame *frame = &iframe->frame; + + rv = nghttp2_frame_unpack_push_promise_payload( + &frame->push_promise, iframe->sbuf.pos, nghttp2_buf_len(&iframe->sbuf)); + + if (rv != 0) { + return nghttp2_session_terminate_session_with_reason( + session, NGHTTP2_PROTOCOL_ERROR, "PUSH_PROMISE: could not unpack"); + } + + return nghttp2_session_on_push_promise_received(session, frame); +} + +int nghttp2_session_on_ping_received(nghttp2_session *session, + nghttp2_frame *frame) { + int rv = 0; + if (frame->hd.stream_id != 0) { + return session_handle_invalid_connection(session, frame, NGHTTP2_ERR_PROTO, + "PING: stream_id != 0"); + } + if ((frame->hd.flags & NGHTTP2_FLAG_ACK) == 0 && + !session_is_closing(session)) { + /* Peer sent ping, so ping it back */ + rv = nghttp2_session_add_ping(session, NGHTTP2_FLAG_ACK, + frame->ping.opaque_data); + if (rv != 0) { + return rv; + } + } + return session_call_on_frame_received(session, frame); +} + +static int session_process_ping_frame(nghttp2_session *session) { + nghttp2_inbound_frame *iframe = &session->iframe; + nghttp2_frame *frame = &iframe->frame; + + nghttp2_frame_unpack_ping_payload(&frame->ping, iframe->sbuf.pos, + nghttp2_buf_len(&iframe->sbuf)); + + return nghttp2_session_on_ping_received(session, frame); +} + +int nghttp2_session_on_goaway_received(nghttp2_session *session, + nghttp2_frame *frame) { + int rv; + + if (frame->hd.stream_id != 0) { + return session_handle_invalid_connection(session, frame, NGHTTP2_ERR_PROTO, + "GOAWAY: stream_id != 0"); + } + /* Spec says Endpoints MUST NOT increase the value they send in the + last stream identifier. */ + if ((frame->goaway.last_stream_id > 0 && + !nghttp2_session_is_my_stream_id(session, + frame->goaway.last_stream_id)) || + session->remote_last_stream_id < frame->goaway.last_stream_id) { + return session_handle_invalid_connection(session, frame, NGHTTP2_ERR_PROTO, + "GOAWAY: invalid last_stream_id"); + } + + session->goaway_flags |= NGHTTP2_GOAWAY_RECV; + + session->remote_last_stream_id = frame->goaway.last_stream_id; + + rv = session_call_on_frame_received(session, frame); + + if (nghttp2_is_fatal(rv)) { + return rv; + } + + return session_close_stream_on_goaway(session, frame->goaway.last_stream_id, + 0); +} + +static int session_process_goaway_frame(nghttp2_session *session) { + nghttp2_inbound_frame *iframe = &session->iframe; + nghttp2_frame *frame = &iframe->frame; + + nghttp2_frame_unpack_goaway_payload( + &frame->goaway, iframe->sbuf.pos, nghttp2_buf_len(&iframe->sbuf), + iframe->lbuf.pos, nghttp2_buf_len(&iframe->lbuf)); + + nghttp2_buf_wrap_init(&iframe->lbuf, NULL, 0); + + return nghttp2_session_on_goaway_received(session, frame); +} + +static int +session_on_connection_window_update_received(nghttp2_session *session, + nghttp2_frame *frame) { + /* Handle connection-level flow control */ + if (frame->window_update.window_size_increment == 0) { + return session_handle_invalid_connection(session, frame, NGHTTP2_ERR_PROTO, + NULL); + } + + if (NGHTTP2_MAX_WINDOW_SIZE - frame->window_update.window_size_increment < + session->remote_window_size) { + return session_handle_invalid_connection(session, frame, + NGHTTP2_ERR_FLOW_CONTROL, NULL); + } + session->remote_window_size += frame->window_update.window_size_increment; + + return session_call_on_frame_received(session, frame); +} + +static int session_on_stream_window_update_received(nghttp2_session *session, + nghttp2_frame *frame) { + int rv; + nghttp2_stream *stream; + stream = nghttp2_session_get_stream(session, frame->hd.stream_id); + if (!stream) { + if (session_detect_idle_stream(session, frame->hd.stream_id)) { + return session_handle_invalid_connection( + session, frame, NGHTTP2_ERR_PROTO, "WINDOW_UPDATE to idle stream"); + } + return 0; + } + if (state_reserved_remote(session, stream)) { + return session_handle_invalid_connection( + session, frame, NGHTTP2_ERR_PROTO, "WINDOW_UPADATE to reserved stream"); + } + if (frame->window_update.window_size_increment == 0) { + return session_handle_invalid_stream(session, frame, NGHTTP2_ERR_PROTO); + } + if (NGHTTP2_MAX_WINDOW_SIZE - frame->window_update.window_size_increment < + stream->remote_window_size) { + return session_handle_invalid_stream(session, frame, + NGHTTP2_ERR_FLOW_CONTROL); + } + stream->remote_window_size += frame->window_update.window_size_increment; + + if (stream->remote_window_size > 0 && + nghttp2_stream_check_deferred_by_flow_control(stream)) { + + rv = nghttp2_stream_resume_deferred_item( + stream, NGHTTP2_STREAM_FLAG_DEFERRED_FLOW_CONTROL, session); + + if (nghttp2_is_fatal(rv)) { + return rv; + } + } + return session_call_on_frame_received(session, frame); +} + +int nghttp2_session_on_window_update_received(nghttp2_session *session, + nghttp2_frame *frame) { + if (frame->hd.stream_id == 0) { + return session_on_connection_window_update_received(session, frame); + } else { + return session_on_stream_window_update_received(session, frame); + } +} + +static int session_process_window_update_frame(nghttp2_session *session) { + nghttp2_inbound_frame *iframe = &session->iframe; + nghttp2_frame *frame = &iframe->frame; + + nghttp2_frame_unpack_window_update_payload( + &frame->window_update, iframe->sbuf.pos, nghttp2_buf_len(&iframe->sbuf)); + + return nghttp2_session_on_window_update_received(session, frame); +} + +int nghttp2_session_on_data_received(nghttp2_session *session, + nghttp2_frame *frame) { + int rv = 0; + int call_cb = 1; + nghttp2_stream *stream; + + /* We don't call on_frame_recv_callback if stream has been closed + already or being closed. */ + stream = nghttp2_session_get_stream(session, frame->hd.stream_id); + if (!stream || stream->state == NGHTTP2_STREAM_CLOSING) { + /* This should be treated as stream error, but it results in lots + of RST_STREAM. So just ignore frame against nonexistent stream + for now. */ + return 0; + } + + if (session_enforce_http_messaging(session) && + (frame->hd.flags & NGHTTP2_FLAG_END_STREAM)) { + if (nghttp2_http_on_remote_end_stream(stream) != 0) { + call_cb = 0; + rv = nghttp2_session_add_rst_stream(session, stream->stream_id, + NGHTTP2_PROTOCOL_ERROR); + if (nghttp2_is_fatal(rv)) { + return rv; + } + } + } + + if (call_cb) { + rv = session_call_on_frame_received(session, frame); + if (nghttp2_is_fatal(rv)) { + return rv; + } + } + + if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) { + nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_RD); + rv = nghttp2_session_close_stream_if_shut_rdwr(session, stream); + if (nghttp2_is_fatal(rv)) { + return rv; + } + } + return 0; +} + +/* For errors, this function only returns FATAL error. */ +static int session_process_data_frame(nghttp2_session *session) { + int rv; + nghttp2_frame *public_data_frame = &session->iframe.frame; + rv = nghttp2_session_on_data_received(session, public_data_frame); + if (nghttp2_is_fatal(rv)) { + return rv; + } + return 0; +} + +/* + * Now we have SETTINGS synchronization, flow control error can be + * detected strictly. If DATA frame is received with length > 0 and + * current received window size + delta length is strictly larger than + * local window size, it is subject to FLOW_CONTROL_ERROR, so return + * -1. Note that local_window_size is calculated after SETTINGS ACK is + * received from peer, so peer must honor this limit. If the resulting + * recv_window_size is strictly larger than NGHTTP2_MAX_WINDOW_SIZE, + * return -1 too. + */ +static int adjust_recv_window_size(int32_t *recv_window_size_ptr, size_t delta, + int32_t local_window_size) { + if (*recv_window_size_ptr > local_window_size - (int32_t)delta || + *recv_window_size_ptr > NGHTTP2_MAX_WINDOW_SIZE - (int32_t)delta) { + return -1; + } + *recv_window_size_ptr += delta; + return 0; +} + +/* + * Accumulates received bytes |delta_size| for stream-level flow + * control and decides whether to send WINDOW_UPDATE to that stream. + * If NGHTTP2_OPT_NO_AUTO_WINDOW_UPDATE is set, WINDOW_UPDATE will not + * be sent. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + */ +static int session_update_recv_stream_window_size(nghttp2_session *session, + nghttp2_stream *stream, + size_t delta_size, + int send_window_update) { + int rv; + rv = adjust_recv_window_size(&stream->recv_window_size, delta_size, + stream->local_window_size); + if (rv != 0) { + return nghttp2_session_add_rst_stream(session, stream->stream_id, + NGHTTP2_FLOW_CONTROL_ERROR); + } + /* We don't have to send WINDOW_UPDATE if the data received is the + last chunk in the incoming stream. */ + if (send_window_update && + !(session->opt_flags & NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE)) { + /* We have to use local_settings here because it is the constraint + the remote endpoint should honor. */ + if (nghttp2_should_send_window_update(stream->local_window_size, + stream->recv_window_size)) { + rv = nghttp2_session_add_window_update(session, NGHTTP2_FLAG_NONE, + stream->stream_id, + stream->recv_window_size); + if (rv == 0) { + stream->recv_window_size = 0; + } else { + return rv; + } + } + } + return 0; +} + +/* + * Accumulates received bytes |delta_size| for connection-level flow + * control and decides whether to send WINDOW_UPDATE to the + * connection. If NGHTTP2_OPT_NO_AUTO_WINDOW_UPDATE is set, + * WINDOW_UPDATE will not be sent. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + */ +static int session_update_recv_connection_window_size(nghttp2_session *session, + size_t delta_size) { + int rv; + rv = adjust_recv_window_size(&session->recv_window_size, delta_size, + session->local_window_size); + if (rv != 0) { + return nghttp2_session_terminate_session(session, + NGHTTP2_FLOW_CONTROL_ERROR); + } + if (!(session->opt_flags & NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE)) { + + if (nghttp2_should_send_window_update(session->local_window_size, + session->recv_window_size)) { + /* Use stream ID 0 to update connection-level flow control + window */ + rv = nghttp2_session_add_window_update(session, NGHTTP2_FLAG_NONE, 0, + session->recv_window_size); + if (rv != 0) { + return rv; + } + + session->recv_window_size = 0; + } + } + return 0; +} + +static int session_update_consumed_size(nghttp2_session *session, + int32_t *consumed_size_ptr, + int32_t *recv_window_size_ptr, + int32_t stream_id, size_t delta_size, + int32_t local_window_size) { + int32_t recv_size; + int rv; + + if ((size_t)*consumed_size_ptr > NGHTTP2_MAX_WINDOW_SIZE - delta_size) { + return nghttp2_session_terminate_session(session, + NGHTTP2_FLOW_CONTROL_ERROR); + } + + *consumed_size_ptr += delta_size; + + /* recv_window_size may be smaller than consumed_size, because it + may be decreased by negative value with + nghttp2_submit_window_update(). */ + recv_size = nghttp2_min(*consumed_size_ptr, *recv_window_size_ptr); + + if (nghttp2_should_send_window_update(local_window_size, recv_size)) { + rv = nghttp2_session_add_window_update(session, NGHTTP2_FLAG_NONE, + stream_id, recv_size); + + if (rv != 0) { + return rv; + } + + *recv_window_size_ptr -= recv_size; + *consumed_size_ptr -= recv_size; + } + + return 0; +} + +static int session_update_stream_consumed_size(nghttp2_session *session, + nghttp2_stream *stream, + size_t delta_size) { + return session_update_consumed_size( + session, &stream->consumed_size, &stream->recv_window_size, + stream->stream_id, delta_size, stream->local_window_size); +} + +static int session_update_connection_consumed_size(nghttp2_session *session, + size_t delta_size) { + return session_update_consumed_size(session, &session->consumed_size, + &session->recv_window_size, 0, delta_size, + session->local_window_size); +} + +/* + * Checks that we can receive the DATA frame for stream, which is + * indicated by |session->iframe.frame.hd.stream_id|. If it is a + * connection error situation, GOAWAY frame will be issued by this + * function. + * + * If the DATA frame is allowed, returns 0. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_IGN_PAYLOAD + * The reception of DATA frame is connection error; or should be + * ignored. + * NGHTTP2_ERR_NOMEM + * Out of memory. + */ +static int session_on_data_received_fail_fast(nghttp2_session *session) { + int rv; + nghttp2_stream *stream; + nghttp2_inbound_frame *iframe; + int32_t stream_id; + const char *failure_reason; + uint32_t error_code = NGHTTP2_PROTOCOL_ERROR; + + iframe = &session->iframe; + stream_id = iframe->frame.hd.stream_id; + + if (stream_id == 0) { + /* The spec says that if a DATA frame is received whose stream ID + is 0, the recipient MUST respond with a connection error of + type PROTOCOL_ERROR. */ + failure_reason = "DATA: stream_id == 0"; + goto fail; + } + stream = nghttp2_session_get_stream(session, stream_id); + if (!stream) { + if (session_detect_idle_stream(session, stream_id)) { + failure_reason = "DATA: stream in idle"; + error_code = NGHTTP2_STREAM_CLOSED; + goto fail; + } + return NGHTTP2_ERR_IGN_PAYLOAD; + } + if (stream->shut_flags & NGHTTP2_SHUT_RD) { + failure_reason = "DATA: stream in half-closed(remote)"; + error_code = NGHTTP2_STREAM_CLOSED; + goto fail; + } + + if (nghttp2_session_is_my_stream_id(session, stream_id)) { + if (stream->state == NGHTTP2_STREAM_CLOSING) { + return NGHTTP2_ERR_IGN_PAYLOAD; + } + if (stream->state != NGHTTP2_STREAM_OPENED) { + failure_reason = "DATA: stream not opened"; + goto fail; + } + return 0; + } + if (stream->state == NGHTTP2_STREAM_RESERVED) { + failure_reason = "DATA: stream in reserved"; + goto fail; + } + if (stream->state == NGHTTP2_STREAM_CLOSING) { + return NGHTTP2_ERR_IGN_PAYLOAD; + } + return 0; +fail: + rv = nghttp2_session_terminate_session_with_reason(session, error_code, + failure_reason); + if (nghttp2_is_fatal(rv)) { + return rv; + } + return NGHTTP2_ERR_IGN_PAYLOAD; +} + +static size_t inbound_frame_payload_readlen(nghttp2_inbound_frame *iframe, + const uint8_t *in, + const uint8_t *last) { + return nghttp2_min((size_t)(last - in), iframe->payloadleft); +} + +/* + * Resets iframe->sbuf and advance its mark pointer by |left| bytes. + */ +static void inbound_frame_set_mark(nghttp2_inbound_frame *iframe, size_t left) { + nghttp2_buf_reset(&iframe->sbuf); + iframe->sbuf.mark += left; +} + +static size_t inbound_frame_buf_read(nghttp2_inbound_frame *iframe, + const uint8_t *in, const uint8_t *last) { + size_t readlen; + + readlen = nghttp2_min(last - in, nghttp2_buf_mark_avail(&iframe->sbuf)); + + iframe->sbuf.last = nghttp2_cpymem(iframe->sbuf.last, in, readlen); + + return readlen; +} + +/* + * Unpacks SETTINGS entry in iframe->sbuf. + */ +static void inbound_frame_set_settings_entry(nghttp2_inbound_frame *iframe) { + nghttp2_settings_entry iv; + size_t i; + + nghttp2_frame_unpack_settings_entry(&iv, iframe->sbuf.pos); + + switch (iv.settings_id) { + case NGHTTP2_SETTINGS_HEADER_TABLE_SIZE: + case NGHTTP2_SETTINGS_ENABLE_PUSH: + case NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS: + case NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE: + case NGHTTP2_SETTINGS_MAX_FRAME_SIZE: + case NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE: + break; + default: + DEBUGF(fprintf(stderr, "recv: ignore unknown settings id=0x%02x\n", + iv.settings_id)); + return; + } + + for (i = 0; i < iframe->niv; ++i) { + if (iframe->iv[i].settings_id == iv.settings_id) { + iframe->iv[i] = iv; + break; + } + } + + if (i == iframe->niv) { + iframe->iv[iframe->niv++] = iv; + } + + if (iv.settings_id == NGHTTP2_SETTINGS_HEADER_TABLE_SIZE && + iv.value < iframe->iv[NGHTTP2_INBOUND_NUM_IV - 1].value) { + + iframe->iv[NGHTTP2_INBOUND_NUM_IV - 1] = iv; + } +} + +/* + * Checks PADDED flags and set iframe->sbuf to read them accordingly. + * If padding is set, this function returns 1. If no padding is set, + * this function returns 0. On error, returns -1. + */ +static int inbound_frame_handle_pad(nghttp2_inbound_frame *iframe, + nghttp2_frame_hd *hd) { + if (hd->flags & NGHTTP2_FLAG_PADDED) { + if (hd->length < 1) { + return -1; + } + inbound_frame_set_mark(iframe, 1); + return 1; + } + DEBUGF(fprintf(stderr, "recv: no padding in payload\n")); + return 0; +} + +/* + * Computes number of padding based on flags. This function returns + * the calculated length if it succeeds, or -1. + */ +static ssize_t inbound_frame_compute_pad(nghttp2_inbound_frame *iframe) { + size_t padlen; + + /* 1 for Pad Length field */ + padlen = iframe->sbuf.pos[0] + 1; + + DEBUGF(fprintf(stderr, "recv: padlen=%zu\n", padlen)); + + /* We cannot use iframe->frame.hd.length because of CONTINUATION */ + if (padlen - 1 > iframe->payloadleft) { + return -1; + } + + iframe->padlen = padlen; + + return padlen; +} + +/* + * This function returns the effective payload length in the data of + * length |readlen| when the remaning payload is |payloadleft|. The + * |payloadleft| does not include |readlen|. If padding was started + * strictly before this data chunk, this function returns -1. + */ +static ssize_t inbound_frame_effective_readlen(nghttp2_inbound_frame *iframe, + size_t payloadleft, + size_t readlen) { + size_t trail_padlen = + nghttp2_frame_trail_padlen(&iframe->frame, iframe->padlen); + + if (trail_padlen > payloadleft) { + size_t padlen; + padlen = trail_padlen - payloadleft; + if (readlen < padlen) { + return -1; + } else { + return readlen - padlen; + } + } + return readlen; +} + +ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in, + size_t inlen) { + const uint8_t *first = in, *last = in + inlen; + nghttp2_inbound_frame *iframe = &session->iframe; + size_t readlen; + ssize_t padlen; + int rv; + int busy = 0; + nghttp2_frame_hd cont_hd; + nghttp2_stream *stream; + size_t pri_fieldlen; + nghttp2_mem *mem; + + DEBUGF(fprintf(stderr, + "recv: connection recv_window_size=%d, local_window=%d\n", + session->recv_window_size, session->local_window_size)); + + mem = &session->mem; + + for (;;) { + switch (iframe->state) { + case NGHTTP2_IB_READ_CLIENT_MAGIC: + readlen = nghttp2_min(inlen, iframe->payloadleft); + + if (memcmp(NGHTTP2_CLIENT_MAGIC + NGHTTP2_CLIENT_MAGIC_LEN - + iframe->payloadleft, + in, readlen) != 0) { + return NGHTTP2_ERR_BAD_CLIENT_MAGIC; + } + + iframe->payloadleft -= readlen; + in += readlen; + + if (iframe->payloadleft == 0) { + session_inbound_frame_reset(session); + iframe->state = NGHTTP2_IB_READ_FIRST_SETTINGS; + } + + break; + case NGHTTP2_IB_READ_FIRST_SETTINGS: + DEBUGF(fprintf(stderr, "recv: [IB_READ_FIRST_SETTINGS]\n")); + + readlen = inbound_frame_buf_read(iframe, in, last); + in += readlen; + + if (nghttp2_buf_mark_avail(&iframe->sbuf)) { + return in - first; + } + + if (iframe->sbuf.pos[3] != NGHTTP2_SETTINGS || + (iframe->sbuf.pos[4] & NGHTTP2_FLAG_ACK)) { + + iframe->state = NGHTTP2_IB_IGN_ALL; + + rv = nghttp2_session_terminate_session_with_reason( + session, NGHTTP2_PROTOCOL_ERROR, "SETTINGS expected"); + + if (nghttp2_is_fatal(rv)) { + return rv; + } + + return inlen; + } + + iframe->state = NGHTTP2_IB_READ_HEAD; + + /* Fall through */ + case NGHTTP2_IB_READ_HEAD: { + int on_begin_frame_called = 0; + + DEBUGF(fprintf(stderr, "recv: [IB_READ_HEAD]\n")); + + readlen = inbound_frame_buf_read(iframe, in, last); + in += readlen; + + if (nghttp2_buf_mark_avail(&iframe->sbuf)) { + return in - first; + } + + nghttp2_frame_unpack_frame_hd(&iframe->frame.hd, iframe->sbuf.pos); + iframe->payloadleft = iframe->frame.hd.length; + + DEBUGF(fprintf(stderr, "recv: payloadlen=%zu, type=%u, flags=0x%02x, " + "stream_id=%d\n", + iframe->frame.hd.length, iframe->frame.hd.type, + iframe->frame.hd.flags, iframe->frame.hd.stream_id)); + + if (iframe->frame.hd.length > session->local_settings.max_frame_size) { + DEBUGF(fprintf(stderr, "recv: length is too large %zu > %u\n", + iframe->frame.hd.length, + session->local_settings.max_frame_size)); + + busy = 1; + + iframe->state = NGHTTP2_IB_IGN_PAYLOAD; + + rv = nghttp2_session_terminate_session_with_reason( + session, NGHTTP2_FRAME_SIZE_ERROR, "too large frame size"); + + if (nghttp2_is_fatal(rv)) { + return rv; + } + + break; + } + + switch (iframe->frame.hd.type) { + case NGHTTP2_DATA: { + DEBUGF(fprintf(stderr, "recv: DATA\n")); + + iframe->frame.hd.flags &= + (NGHTTP2_FLAG_END_STREAM | NGHTTP2_FLAG_PADDED); + /* Check stream is open. If it is not open or closing, + ignore payload. */ + busy = 1; + + rv = session_on_data_received_fail_fast(session); + if (rv == NGHTTP2_ERR_IGN_PAYLOAD) { + DEBUGF(fprintf(stderr, "recv: DATA not allowed stream_id=%d\n", + iframe->frame.hd.stream_id)); + iframe->state = NGHTTP2_IB_IGN_DATA; + break; + } + + if (nghttp2_is_fatal(rv)) { + return rv; + } + + rv = inbound_frame_handle_pad(iframe, &iframe->frame.hd); + if (rv < 0) { + iframe->state = NGHTTP2_IB_IGN_DATA; + rv = nghttp2_session_terminate_session_with_reason( + session, NGHTTP2_PROTOCOL_ERROR, + "DATA: insufficient padding space"); + + if (nghttp2_is_fatal(rv)) { + return rv; + } + break; + } + + if (rv == 1) { + iframe->state = NGHTTP2_IB_READ_PAD_DATA; + break; + } + + iframe->state = NGHTTP2_IB_READ_DATA; + break; + } + case NGHTTP2_HEADERS: + + DEBUGF(fprintf(stderr, "recv: HEADERS\n")); + + iframe->frame.hd.flags &= + (NGHTTP2_FLAG_END_STREAM | NGHTTP2_FLAG_END_HEADERS | + NGHTTP2_FLAG_PADDED | NGHTTP2_FLAG_PRIORITY); + + rv = inbound_frame_handle_pad(iframe, &iframe->frame.hd); + if (rv < 0) { + busy = 1; + + iframe->state = NGHTTP2_IB_IGN_PAYLOAD; + + rv = nghttp2_session_terminate_session_with_reason( + session, NGHTTP2_PROTOCOL_ERROR, + "HEADERS: insufficient padding space"); + if (nghttp2_is_fatal(rv)) { + return rv; + } + break; + } + + if (rv == 1) { + iframe->state = NGHTTP2_IB_READ_NBYTE; + break; + } + + pri_fieldlen = nghttp2_frame_priority_len(iframe->frame.hd.flags); + + if (pri_fieldlen > 0) { + if (iframe->payloadleft < pri_fieldlen) { + busy = 1; + iframe->state = NGHTTP2_IB_FRAME_SIZE_ERROR; + break; + } + + iframe->state = NGHTTP2_IB_READ_NBYTE; + + inbound_frame_set_mark(iframe, pri_fieldlen); + + break; + } + + /* Call on_begin_frame_callback here because + session_process_headers_frame() may call + on_begin_headers_callback */ + rv = session_call_on_begin_frame(session, &iframe->frame.hd); + + if (nghttp2_is_fatal(rv)) { + return rv; + } + + on_begin_frame_called = 1; + + rv = session_process_headers_frame(session); + if (nghttp2_is_fatal(rv)) { + return rv; + } + + busy = 1; + + if (rv == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) { + rv = nghttp2_session_add_rst_stream( + session, iframe->frame.hd.stream_id, NGHTTP2_INTERNAL_ERROR); + if (nghttp2_is_fatal(rv)) { + return rv; + } + iframe->state = NGHTTP2_IB_IGN_HEADER_BLOCK; + break; + } + + if (rv == NGHTTP2_ERR_IGN_HEADER_BLOCK) { + iframe->state = NGHTTP2_IB_IGN_HEADER_BLOCK; + break; + } + + iframe->state = NGHTTP2_IB_READ_HEADER_BLOCK; + + break; + case NGHTTP2_PRIORITY: + DEBUGF(fprintf(stderr, "recv: PRIORITY\n")); + + iframe->frame.hd.flags = NGHTTP2_FLAG_NONE; + + if (iframe->payloadleft != NGHTTP2_PRIORITY_SPECLEN) { + busy = 1; + + iframe->state = NGHTTP2_IB_FRAME_SIZE_ERROR; + + break; + } + + iframe->state = NGHTTP2_IB_READ_NBYTE; + + inbound_frame_set_mark(iframe, NGHTTP2_PRIORITY_SPECLEN); + + break; + case NGHTTP2_RST_STREAM: + case NGHTTP2_WINDOW_UPDATE: +#ifdef DEBUGBUILD + switch (iframe->frame.hd.type) { + case NGHTTP2_RST_STREAM: + DEBUGF(fprintf(stderr, "recv: RST_STREAM\n")); + break; + case NGHTTP2_WINDOW_UPDATE: + DEBUGF(fprintf(stderr, "recv: WINDOW_UPDATE\n")); + break; + } +#endif /* DEBUGBUILD */ + + iframe->frame.hd.flags = NGHTTP2_FLAG_NONE; + + if (iframe->payloadleft != 4) { + busy = 1; + iframe->state = NGHTTP2_IB_FRAME_SIZE_ERROR; + break; + } + + iframe->state = NGHTTP2_IB_READ_NBYTE; + + inbound_frame_set_mark(iframe, 4); + + break; + case NGHTTP2_SETTINGS: + DEBUGF(fprintf(stderr, "recv: SETTINGS\n")); + + iframe->frame.hd.flags &= NGHTTP2_FLAG_ACK; + + if ((iframe->frame.hd.length % NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH) || + ((iframe->frame.hd.flags & NGHTTP2_FLAG_ACK) && + iframe->payloadleft > 0)) { + busy = 1; + iframe->state = NGHTTP2_IB_FRAME_SIZE_ERROR; + break; + } + + iframe->state = NGHTTP2_IB_READ_SETTINGS; + + if (iframe->payloadleft) { + inbound_frame_set_mark(iframe, NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH); + break; + } + + busy = 1; + + inbound_frame_set_mark(iframe, 0); + + break; + case NGHTTP2_PUSH_PROMISE: + DEBUGF(fprintf(stderr, "recv: PUSH_PROMISE\n")); + + iframe->frame.hd.flags &= + (NGHTTP2_FLAG_END_HEADERS | NGHTTP2_FLAG_PADDED); + + rv = inbound_frame_handle_pad(iframe, &iframe->frame.hd); + if (rv < 0) { + busy = 1; + iframe->state = NGHTTP2_IB_IGN_PAYLOAD; + rv = nghttp2_session_terminate_session_with_reason( + session, NGHTTP2_PROTOCOL_ERROR, + "PUSH_PROMISE: insufficient padding space"); + if (nghttp2_is_fatal(rv)) { + return rv; + } + break; + } + + if (rv == 1) { + iframe->state = NGHTTP2_IB_READ_NBYTE; + break; + } + + if (iframe->payloadleft < 4) { + busy = 1; + iframe->state = NGHTTP2_IB_FRAME_SIZE_ERROR; + break; + } + + iframe->state = NGHTTP2_IB_READ_NBYTE; + + inbound_frame_set_mark(iframe, 4); + + break; + case NGHTTP2_PING: + DEBUGF(fprintf(stderr, "recv: PING\n")); + + iframe->frame.hd.flags &= NGHTTP2_FLAG_ACK; + + if (iframe->payloadleft != 8) { + busy = 1; + iframe->state = NGHTTP2_IB_FRAME_SIZE_ERROR; + break; + } + + iframe->state = NGHTTP2_IB_READ_NBYTE; + inbound_frame_set_mark(iframe, 8); + + break; + case NGHTTP2_GOAWAY: + DEBUGF(fprintf(stderr, "recv: GOAWAY\n")); + + iframe->frame.hd.flags = NGHTTP2_FLAG_NONE; + + if (iframe->payloadleft < 8) { + busy = 1; + iframe->state = NGHTTP2_IB_FRAME_SIZE_ERROR; + break; + } + + iframe->state = NGHTTP2_IB_READ_NBYTE; + inbound_frame_set_mark(iframe, 8); + + break; + case NGHTTP2_CONTINUATION: + DEBUGF(fprintf(stderr, "recv: unexpected CONTINUATION\n")); + + /* Receiving CONTINUATION in this state are subject to + connection error of type PROTOCOL_ERROR */ + rv = nghttp2_session_terminate_session_with_reason( + session, NGHTTP2_PROTOCOL_ERROR, "CONTINUATION: unexpected"); + if (nghttp2_is_fatal(rv)) { + return rv; + } + + busy = 1; + + iframe->state = NGHTTP2_IB_IGN_PAYLOAD; + + break; + default: + DEBUGF(fprintf(stderr, "recv: unknown frame\n")); + + /* Silently ignore unknown frame type. */ + + busy = 1; + + iframe->state = NGHTTP2_IB_IGN_PAYLOAD; + + break; + } + + if (!on_begin_frame_called) { + switch (iframe->state) { + case NGHTTP2_IB_IGN_HEADER_BLOCK: + case NGHTTP2_IB_IGN_PAYLOAD: + case NGHTTP2_IB_FRAME_SIZE_ERROR: + case NGHTTP2_IB_IGN_DATA: + break; + default: + rv = session_call_on_begin_frame(session, &iframe->frame.hd); + + if (nghttp2_is_fatal(rv)) { + return rv; + } + } + } + + break; + } + case NGHTTP2_IB_READ_NBYTE: + DEBUGF(fprintf(stderr, "recv: [IB_READ_NBYTE]\n")); + + readlen = inbound_frame_buf_read(iframe, in, last); + in += readlen; + iframe->payloadleft -= readlen; + + DEBUGF(fprintf(stderr, "recv: readlen=%zu, payloadleft=%zu, left=%zd\n", + readlen, iframe->payloadleft, + nghttp2_buf_mark_avail(&iframe->sbuf))); + + if (nghttp2_buf_mark_avail(&iframe->sbuf)) { + return in - first; + } + + switch (iframe->frame.hd.type) { + case NGHTTP2_HEADERS: + if (iframe->padlen == 0 && + (iframe->frame.hd.flags & NGHTTP2_FLAG_PADDED)) { + padlen = inbound_frame_compute_pad(iframe); + if (padlen < 0) { + busy = 1; + rv = nghttp2_session_terminate_session_with_reason( + session, NGHTTP2_PROTOCOL_ERROR, "HEADERS: invalid padding"); + if (nghttp2_is_fatal(rv)) { + return rv; + } + iframe->state = NGHTTP2_IB_IGN_PAYLOAD; + break; + } + iframe->frame.headers.padlen = padlen; + + pri_fieldlen = nghttp2_frame_priority_len(iframe->frame.hd.flags); + + if (pri_fieldlen > 0) { + if (iframe->payloadleft < pri_fieldlen) { + busy = 1; + iframe->state = NGHTTP2_IB_FRAME_SIZE_ERROR; + break; + } + iframe->state = NGHTTP2_IB_READ_NBYTE; + inbound_frame_set_mark(iframe, pri_fieldlen); + break; + } else { + /* Truncate buffers used for padding spec */ + inbound_frame_set_mark(iframe, 0); + } + } + + rv = session_process_headers_frame(session); + if (nghttp2_is_fatal(rv)) { + return rv; + } + + busy = 1; + + if (rv == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) { + rv = nghttp2_session_add_rst_stream( + session, iframe->frame.hd.stream_id, NGHTTP2_INTERNAL_ERROR); + if (nghttp2_is_fatal(rv)) { + return rv; + } + iframe->state = NGHTTP2_IB_IGN_HEADER_BLOCK; + break; + } + + if (rv == NGHTTP2_ERR_IGN_HEADER_BLOCK) { + iframe->state = NGHTTP2_IB_IGN_HEADER_BLOCK; + break; + } + + iframe->state = NGHTTP2_IB_READ_HEADER_BLOCK; + + break; + case NGHTTP2_PRIORITY: + rv = session_process_priority_frame(session); + if (nghttp2_is_fatal(rv)) { + return rv; + } + + session_inbound_frame_reset(session); + + break; + case NGHTTP2_RST_STREAM: + rv = session_process_rst_stream_frame(session); + if (nghttp2_is_fatal(rv)) { + return rv; + } + + session_inbound_frame_reset(session); + + break; + case NGHTTP2_PUSH_PROMISE: + if (iframe->padlen == 0 && + (iframe->frame.hd.flags & NGHTTP2_FLAG_PADDED)) { + padlen = inbound_frame_compute_pad(iframe); + if (padlen < 0) { + busy = 1; + rv = nghttp2_session_terminate_session_with_reason( + session, NGHTTP2_PROTOCOL_ERROR, + "PUSH_PROMISE: invalid padding"); + if (nghttp2_is_fatal(rv)) { + return rv; + } + iframe->state = NGHTTP2_IB_IGN_PAYLOAD; + break; + } + + iframe->frame.push_promise.padlen = padlen; + + if (iframe->payloadleft < 4) { + busy = 1; + iframe->state = NGHTTP2_IB_FRAME_SIZE_ERROR; + break; + } + + iframe->state = NGHTTP2_IB_READ_NBYTE; + + inbound_frame_set_mark(iframe, 4); + + break; + } + + rv = session_process_push_promise_frame(session); + if (nghttp2_is_fatal(rv)) { + return rv; + } + + busy = 1; + + if (rv == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) { + rv = nghttp2_session_add_rst_stream( + session, iframe->frame.push_promise.promised_stream_id, + NGHTTP2_INTERNAL_ERROR); + if (nghttp2_is_fatal(rv)) { + return rv; + } + iframe->state = NGHTTP2_IB_IGN_HEADER_BLOCK; + break; + } + + if (rv == NGHTTP2_ERR_IGN_HEADER_BLOCK) { + iframe->state = NGHTTP2_IB_IGN_HEADER_BLOCK; + break; + } + + iframe->state = NGHTTP2_IB_READ_HEADER_BLOCK; + + break; + case NGHTTP2_PING: + rv = session_process_ping_frame(session); + if (nghttp2_is_fatal(rv)) { + return rv; + } + + session_inbound_frame_reset(session); + + break; + case NGHTTP2_GOAWAY: { + size_t debuglen; + + /* 8 is Last-stream-ID + Error Code */ + debuglen = iframe->frame.hd.length - 8; + + if (debuglen > 0) { + iframe->raw_lbuf = nghttp2_mem_malloc(mem, debuglen); + + if (iframe->raw_lbuf == NULL) { + return NGHTTP2_ERR_NOMEM; + } + + nghttp2_buf_wrap_init(&iframe->lbuf, iframe->raw_lbuf, debuglen); + } + + busy = 1; + + iframe->state = NGHTTP2_IB_READ_GOAWAY_DEBUG; + + break; + } + case NGHTTP2_WINDOW_UPDATE: + rv = session_process_window_update_frame(session); + if (nghttp2_is_fatal(rv)) { + return rv; + } + + session_inbound_frame_reset(session); + + break; + default: + /* This is unknown frame */ + session_inbound_frame_reset(session); + + break; + } + break; + case NGHTTP2_IB_READ_HEADER_BLOCK: + case NGHTTP2_IB_IGN_HEADER_BLOCK: { + ssize_t data_readlen; +#ifdef DEBUGBUILD + if (iframe->state == NGHTTP2_IB_READ_HEADER_BLOCK) { + fprintf(stderr, "recv: [IB_READ_HEADER_BLOCK]\n"); + } else { + fprintf(stderr, "recv: [IB_IGN_HEADER_BLOCK]\n"); + } +#endif /* DEBUGBUILD */ + + readlen = inbound_frame_payload_readlen(iframe, in, last); + + DEBUGF(fprintf(stderr, "recv: readlen=%zu, payloadleft=%zu\n", readlen, + iframe->payloadleft - readlen)); + + data_readlen = inbound_frame_effective_readlen( + iframe, iframe->payloadleft - readlen, readlen); + if (data_readlen >= 0) { + size_t trail_padlen; + size_t hd_proclen = 0; + trail_padlen = + nghttp2_frame_trail_padlen(&iframe->frame, iframe->padlen); + DEBUGF(fprintf(stderr, "recv: block final=%d\n", + (iframe->frame.hd.flags & NGHTTP2_FLAG_END_HEADERS) && + iframe->payloadleft - data_readlen == trail_padlen)); + + rv = inflate_header_block( + session, &iframe->frame, &hd_proclen, (uint8_t *)in, data_readlen, + (iframe->frame.hd.flags & NGHTTP2_FLAG_END_HEADERS) && + iframe->payloadleft - data_readlen == trail_padlen, + iframe->state == NGHTTP2_IB_READ_HEADER_BLOCK); + + if (nghttp2_is_fatal(rv)) { + return rv; + } + + if (rv == NGHTTP2_ERR_PAUSE) { + in += hd_proclen; + iframe->payloadleft -= hd_proclen; + + return in - first; + } + + if (rv == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) { + /* The application says no more headers. We decompress the + rest of the header block but not invoke on_header_callback + and on_frame_recv_callback. */ + in += hd_proclen; + iframe->payloadleft -= hd_proclen; + + /* Use promised stream ID for PUSH_PROMISE */ + rv = nghttp2_session_add_rst_stream( + session, iframe->frame.hd.type == NGHTTP2_PUSH_PROMISE + ? iframe->frame.push_promise.promised_stream_id + : iframe->frame.hd.stream_id, + NGHTTP2_INTERNAL_ERROR); + if (nghttp2_is_fatal(rv)) { + return rv; + } + busy = 1; + iframe->state = NGHTTP2_IB_IGN_HEADER_BLOCK; + break; + } + + in += readlen; + iframe->payloadleft -= readlen; + + if (rv == NGHTTP2_ERR_HEADER_COMP) { + /* GOAWAY is already issued */ + if (iframe->payloadleft == 0) { + session_inbound_frame_reset(session); + } else { + busy = 1; + iframe->state = NGHTTP2_IB_IGN_PAYLOAD; + } + break; + } + } else { + in += readlen; + iframe->payloadleft -= readlen; + } + + if (iframe->payloadleft) { + break; + } + + if ((iframe->frame.hd.flags & NGHTTP2_FLAG_END_HEADERS) == 0) { + + inbound_frame_set_mark(iframe, NGHTTP2_FRAME_HDLEN); + + iframe->padlen = 0; + + if (iframe->state == NGHTTP2_IB_READ_HEADER_BLOCK) { + iframe->state = NGHTTP2_IB_EXPECT_CONTINUATION; + } else { + iframe->state = NGHTTP2_IB_IGN_CONTINUATION; + } + } else { + if (iframe->state == NGHTTP2_IB_READ_HEADER_BLOCK) { + rv = session_after_header_block_received(session); + if (nghttp2_is_fatal(rv)) { + return rv; + } + } + session_inbound_frame_reset(session); + } + break; + } + case NGHTTP2_IB_IGN_PAYLOAD: + DEBUGF(fprintf(stderr, "recv: [IB_IGN_PAYLOAD]\n")); + + readlen = inbound_frame_payload_readlen(iframe, in, last); + iframe->payloadleft -= readlen; + in += readlen; + + DEBUGF(fprintf(stderr, "recv: readlen=%zu, payloadleft=%zu\n", readlen, + iframe->payloadleft)); + + if (iframe->payloadleft) { + break; + } + + switch (iframe->frame.hd.type) { + case NGHTTP2_HEADERS: + case NGHTTP2_PUSH_PROMISE: + case NGHTTP2_CONTINUATION: + /* Mark inflater bad so that we won't perform further decoding */ + session->hd_inflater.ctx.bad = 1; + break; + default: + break; + } + + session_inbound_frame_reset(session); + + break; + case NGHTTP2_IB_FRAME_SIZE_ERROR: + DEBUGF(fprintf(stderr, "recv: [IB_FRAME_SIZE_ERROR]\n")); + + rv = session_handle_frame_size_error(session, &iframe->frame); + if (nghttp2_is_fatal(rv)) { + return rv; + } + + busy = 1; + + iframe->state = NGHTTP2_IB_IGN_PAYLOAD; + + break; + case NGHTTP2_IB_READ_SETTINGS: + DEBUGF(fprintf(stderr, "recv: [IB_READ_SETTINGS]\n")); + + readlen = inbound_frame_buf_read(iframe, in, last); + iframe->payloadleft -= readlen; + in += readlen; + + DEBUGF(fprintf(stderr, "recv: readlen=%zu, payloadleft=%zu\n", readlen, + iframe->payloadleft)); + + if (nghttp2_buf_mark_avail(&iframe->sbuf)) { + break; + } + + if (readlen > 0) { + inbound_frame_set_settings_entry(iframe); + } + if (iframe->payloadleft) { + inbound_frame_set_mark(iframe, NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH); + break; + } + + rv = session_process_settings_frame(session); + + if (nghttp2_is_fatal(rv)) { + return rv; + } + + session_inbound_frame_reset(session); + + break; + case NGHTTP2_IB_READ_GOAWAY_DEBUG: + DEBUGF(fprintf(stderr, "recv: [IB_READ_GOAWAY_DEBUG]\n")); + + readlen = inbound_frame_payload_readlen(iframe, in, last); + + iframe->lbuf.last = nghttp2_cpymem(iframe->lbuf.last, in, readlen); + + iframe->payloadleft -= readlen; + in += readlen; + + DEBUGF(fprintf(stderr, "recv: readlen=%zu, payloadleft=%zu\n", readlen, + iframe->payloadleft)); + + if (iframe->payloadleft) { + assert(nghttp2_buf_avail(&iframe->lbuf) > 0); + + break; + } + + rv = session_process_goaway_frame(session); + + if (nghttp2_is_fatal(rv)) { + return rv; + } + + session_inbound_frame_reset(session); + + break; + case NGHTTP2_IB_EXPECT_CONTINUATION: + case NGHTTP2_IB_IGN_CONTINUATION: +#ifdef DEBUGBUILD + if (iframe->state == NGHTTP2_IB_EXPECT_CONTINUATION) { + fprintf(stderr, "recv: [IB_EXPECT_CONTINUATION]\n"); + } else { + fprintf(stderr, "recv: [IB_IGN_CONTINUATION]\n"); + } +#endif /* DEBUGBUILD */ + + readlen = inbound_frame_buf_read(iframe, in, last); + in += readlen; + + if (nghttp2_buf_mark_avail(&iframe->sbuf)) { + return in - first; + } + + nghttp2_frame_unpack_frame_hd(&cont_hd, iframe->sbuf.pos); + iframe->payloadleft = cont_hd.length; + + DEBUGF(fprintf(stderr, "recv: payloadlen=%zu, type=%u, flags=0x%02x, " + "stream_id=%d\n", + cont_hd.length, cont_hd.type, cont_hd.flags, + cont_hd.stream_id)); + + if (cont_hd.type != NGHTTP2_CONTINUATION || + cont_hd.stream_id != iframe->frame.hd.stream_id) { + DEBUGF(fprintf(stderr, "recv: expected stream_id=%d, type=%d, but " + "got stream_id=%d, type=%d\n", + iframe->frame.hd.stream_id, NGHTTP2_CONTINUATION, + cont_hd.stream_id, cont_hd.type)); + rv = nghttp2_session_terminate_session_with_reason( + session, NGHTTP2_PROTOCOL_ERROR, + "unexpected non-CONTINUATION frame or stream_id is invalid"); + if (nghttp2_is_fatal(rv)) { + return rv; + } + + busy = 1; + + iframe->state = NGHTTP2_IB_IGN_PAYLOAD; + + break; + } + + /* CONTINUATION won't bear NGHTTP2_PADDED flag */ + + iframe->frame.hd.flags |= cont_hd.flags & NGHTTP2_FLAG_END_HEADERS; + iframe->frame.hd.length += cont_hd.length; + + busy = 1; + + if (iframe->state == NGHTTP2_IB_EXPECT_CONTINUATION) { + iframe->state = NGHTTP2_IB_READ_HEADER_BLOCK; + + rv = session_call_on_begin_frame(session, &cont_hd); + + if (nghttp2_is_fatal(rv)) { + return rv; + } + } else { + iframe->state = NGHTTP2_IB_IGN_HEADER_BLOCK; + } + + break; + case NGHTTP2_IB_READ_PAD_DATA: + DEBUGF(fprintf(stderr, "recv: [IB_READ_PAD_DATA]\n")); + + readlen = inbound_frame_buf_read(iframe, in, last); + in += readlen; + iframe->payloadleft -= readlen; + + DEBUGF(fprintf(stderr, "recv: readlen=%zu, payloadleft=%zu, left=%zu\n", + readlen, iframe->payloadleft, + nghttp2_buf_mark_avail(&iframe->sbuf))); + + if (nghttp2_buf_mark_avail(&iframe->sbuf)) { + return in - first; + } + + /* Pad Length field is subject to flow control */ + rv = session_update_recv_connection_window_size(session, readlen); + if (nghttp2_is_fatal(rv)) { + return rv; + } + + /* Pad Length field is consumed immediately */ + rv = + nghttp2_session_consume(session, iframe->frame.hd.stream_id, readlen); + + if (nghttp2_is_fatal(rv)) { + return rv; + } + + stream = nghttp2_session_get_stream(session, iframe->frame.hd.stream_id); + if (stream) { + rv = session_update_recv_stream_window_size( + session, stream, readlen, + iframe->payloadleft || + (iframe->frame.hd.flags & NGHTTP2_FLAG_END_STREAM) == 0); + if (nghttp2_is_fatal(rv)) { + return rv; + } + } + + busy = 1; + + padlen = inbound_frame_compute_pad(iframe); + if (padlen < 0) { + rv = nghttp2_session_terminate_session_with_reason( + session, NGHTTP2_PROTOCOL_ERROR, "DATA: invalid padding"); + if (nghttp2_is_fatal(rv)) { + return rv; + } + iframe->state = NGHTTP2_IB_IGN_DATA; + break; + } + + iframe->frame.data.padlen = padlen; + + iframe->state = NGHTTP2_IB_READ_DATA; + + break; + case NGHTTP2_IB_READ_DATA: + DEBUGF(fprintf(stderr, "recv: [IB_READ_DATA]\n")); + + readlen = inbound_frame_payload_readlen(iframe, in, last); + iframe->payloadleft -= readlen; + in += readlen; + + DEBUGF(fprintf(stderr, "recv: readlen=%zu, payloadleft=%zu\n", readlen, + iframe->payloadleft)); + + if (readlen > 0) { + ssize_t data_readlen; + + rv = session_update_recv_connection_window_size(session, readlen); + if (nghttp2_is_fatal(rv)) { + return rv; + } + + stream = + nghttp2_session_get_stream(session, iframe->frame.hd.stream_id); + if (stream) { + rv = session_update_recv_stream_window_size( + session, stream, readlen, + iframe->payloadleft || + (iframe->frame.hd.flags & NGHTTP2_FLAG_END_STREAM) == 0); + if (nghttp2_is_fatal(rv)) { + return rv; + } + } + + data_readlen = inbound_frame_effective_readlen( + iframe, iframe->payloadleft, readlen); + + padlen = readlen - data_readlen; + + if (padlen > 0) { + /* Padding is considered as "consumed" immediately */ + rv = nghttp2_session_consume(session, iframe->frame.hd.stream_id, + padlen); + + if (nghttp2_is_fatal(rv)) { + return rv; + } + } + + DEBUGF(fprintf(stderr, "recv: data_readlen=%zd\n", data_readlen)); + + if (stream && data_readlen > 0) { + if (session_enforce_http_messaging(session)) { + if (nghttp2_http_on_data_chunk(stream, data_readlen) != 0) { + rv = nghttp2_session_add_rst_stream( + session, iframe->frame.hd.stream_id, NGHTTP2_PROTOCOL_ERROR); + if (nghttp2_is_fatal(rv)) { + return rv; + } + busy = 1; + iframe->state = NGHTTP2_IB_IGN_DATA; + break; + } + } + if (session->callbacks.on_data_chunk_recv_callback) { + rv = session->callbacks.on_data_chunk_recv_callback( + session, iframe->frame.hd.flags, iframe->frame.hd.stream_id, + in - readlen, data_readlen, session->user_data); + if (rv == NGHTTP2_ERR_PAUSE) { + return in - first; + } + + if (nghttp2_is_fatal(rv)) { + return NGHTTP2_ERR_CALLBACK_FAILURE; + } + } + } + } + + if (iframe->payloadleft) { + break; + } + + rv = session_process_data_frame(session); + if (nghttp2_is_fatal(rv)) { + return rv; + } + + session_inbound_frame_reset(session); + + break; + case NGHTTP2_IB_IGN_DATA: + DEBUGF(fprintf(stderr, "recv: [IB_IGN_DATA]\n")); + + readlen = inbound_frame_payload_readlen(iframe, in, last); + iframe->payloadleft -= readlen; + in += readlen; + + DEBUGF(fprintf(stderr, "recv: readlen=%zu, payloadleft=%zu\n", readlen, + iframe->payloadleft)); + + if (readlen > 0) { + /* Update connection-level flow control window for ignored + DATA frame too */ + rv = session_update_recv_connection_window_size(session, readlen); + if (nghttp2_is_fatal(rv)) { + return rv; + } + + if (session->opt_flags & NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE) { + + /* Ignored DATA is considered as "consumed" immediately. */ + rv = session_update_connection_consumed_size(session, readlen); + + if (nghttp2_is_fatal(rv)) { + return rv; + } + } + } + + if (iframe->payloadleft) { + break; + } + + session_inbound_frame_reset(session); + + break; + case NGHTTP2_IB_IGN_ALL: + return inlen; + } + + if (!busy && in == last) { + break; + } + + busy = 0; + } + + assert(in == last); + + return in - first; +} + +int nghttp2_session_recv(nghttp2_session *session) { + uint8_t buf[NGHTTP2_INBOUND_BUFFER_LENGTH]; + while (1) { + ssize_t readlen; + readlen = session_recv(session, buf, sizeof(buf)); + if (readlen > 0) { + ssize_t proclen = nghttp2_session_mem_recv(session, buf, readlen); + if (proclen < 0) { + return (int)proclen; + } + assert(proclen == readlen); + } else if (readlen == 0 || readlen == NGHTTP2_ERR_WOULDBLOCK) { + return 0; + } else if (readlen == NGHTTP2_ERR_EOF) { + return NGHTTP2_ERR_EOF; + } else if (readlen < 0) { + return NGHTTP2_ERR_CALLBACK_FAILURE; + } + } +} + +/* + * Returns the number of active streams, which includes streams in + * reserved state. + */ +static size_t session_get_num_active_streams(nghttp2_session *session) { + return nghttp2_map_size(&session->streams) - session->num_closed_streams - + session->num_idle_streams; +} + +int nghttp2_session_want_read(nghttp2_session *session) { + size_t num_active_streams; + + /* If this flag is set, we don't want to read. The application + should drop the connection. */ + if (session->goaway_flags & NGHTTP2_GOAWAY_TERM_SENT) { + return 0; + } + + num_active_streams = session_get_num_active_streams(session); + + /* Unless termination GOAWAY is sent or received, we always want to + read incoming frames. */ + + if (num_active_streams > 0) { + return 1; + } + + /* If there is no active streams and GOAWAY has been sent or + received, we are done with this session. */ + return (session->goaway_flags & + (NGHTTP2_GOAWAY_SENT | NGHTTP2_GOAWAY_RECV)) == 0; +} + +int nghttp2_session_want_write(nghttp2_session *session) { + /* If these flag is set, we don't want to write any data. The + application should drop the connection. */ + if (session->goaway_flags & NGHTTP2_GOAWAY_TERM_SENT) { + return 0; + } + + /* + * Unless termination GOAWAY is sent or received, we want to write + * frames if there is pending ones. If pending frame is request/push + * response HEADERS and concurrent stream limit is reached, we don't + * want to write them. + */ + + if (session->aob.item == NULL && + nghttp2_outbound_queue_top(&session->ob_urgent) == NULL && + nghttp2_outbound_queue_top(&session->ob_reg) == NULL && + (nghttp2_pq_empty(&session->ob_da_pq) || + session->remote_window_size == 0) && + (nghttp2_outbound_queue_top(&session->ob_syn) == NULL || + session_is_outgoing_concurrent_streams_max(session))) { + return 0; + } + + /* If there is no active streams and GOAWAY has been sent or + received, we are done with this session. */ + return (session->goaway_flags & + (NGHTTP2_GOAWAY_SENT | NGHTTP2_GOAWAY_RECV)) == 0; +} + +int nghttp2_session_add_ping(nghttp2_session *session, uint8_t flags, + const uint8_t *opaque_data) { + int rv; + nghttp2_outbound_item *item; + nghttp2_frame *frame; + nghttp2_mem *mem; + + mem = &session->mem; + item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item)); + if (item == NULL) { + return NGHTTP2_ERR_NOMEM; + } + + nghttp2_outbound_item_init(item); + + frame = &item->frame; + + nghttp2_frame_ping_init(&frame->ping, flags, opaque_data); + + rv = nghttp2_session_add_item(session, item); + + if (rv != 0) { + nghttp2_frame_ping_free(&frame->ping); + nghttp2_mem_free(mem, item); + return rv; + } + return 0; +} + +int nghttp2_session_add_goaway(nghttp2_session *session, int32_t last_stream_id, + uint32_t error_code, const uint8_t *opaque_data, + size_t opaque_data_len, uint8_t aux_flags) { + int rv; + nghttp2_outbound_item *item; + nghttp2_frame *frame; + uint8_t *opaque_data_copy = NULL; + nghttp2_goaway_aux_data *aux_data; + nghttp2_mem *mem; + + mem = &session->mem; + + if (nghttp2_session_is_my_stream_id(session, last_stream_id)) { + return NGHTTP2_ERR_INVALID_ARGUMENT; + } + + if (opaque_data_len) { + if (opaque_data_len + 8 > NGHTTP2_MAX_PAYLOADLEN) { + return NGHTTP2_ERR_INVALID_ARGUMENT; + } + opaque_data_copy = nghttp2_mem_malloc(mem, opaque_data_len); + if (opaque_data_copy == NULL) { + return NGHTTP2_ERR_NOMEM; + } + memcpy(opaque_data_copy, opaque_data, opaque_data_len); + } + + item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item)); + if (item == NULL) { + nghttp2_mem_free(mem, opaque_data_copy); + return NGHTTP2_ERR_NOMEM; + } + + nghttp2_outbound_item_init(item); + + frame = &item->frame; + + /* last_stream_id must not be increased from the value previously + sent */ + last_stream_id = nghttp2_min(last_stream_id, session->local_last_stream_id); + + nghttp2_frame_goaway_init(&frame->goaway, last_stream_id, error_code, + opaque_data_copy, opaque_data_len); + + aux_data = &item->aux_data.goaway; + aux_data->flags = aux_flags; + + rv = nghttp2_session_add_item(session, item); + if (rv != 0) { + nghttp2_frame_goaway_free(&frame->goaway, mem); + nghttp2_mem_free(mem, item); + return rv; + } + return 0; +} + +int nghttp2_session_add_window_update(nghttp2_session *session, uint8_t flags, + int32_t stream_id, + int32_t window_size_increment) { + int rv; + nghttp2_outbound_item *item; + nghttp2_frame *frame; + nghttp2_mem *mem; + + mem = &session->mem; + item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item)); + if (item == NULL) { + return NGHTTP2_ERR_NOMEM; + } + + nghttp2_outbound_item_init(item); + + frame = &item->frame; + + nghttp2_frame_window_update_init(&frame->window_update, flags, stream_id, + window_size_increment); + + rv = nghttp2_session_add_item(session, item); + + if (rv != 0) { + nghttp2_frame_window_update_free(&frame->window_update); + nghttp2_mem_free(mem, item); + return rv; + } + return 0; +} + +int nghttp2_session_add_settings(nghttp2_session *session, uint8_t flags, + const nghttp2_settings_entry *iv, size_t niv) { + nghttp2_outbound_item *item; + nghttp2_frame *frame; + nghttp2_settings_entry *iv_copy; + size_t i; + int rv; + nghttp2_mem *mem; + + mem = &session->mem; + + if (flags & NGHTTP2_FLAG_ACK) { + if (niv != 0) { + return NGHTTP2_ERR_INVALID_ARGUMENT; + } + } else if (session->inflight_niv != -1) { + return NGHTTP2_ERR_TOO_MANY_INFLIGHT_SETTINGS; + } + + if (!nghttp2_iv_check(iv, niv)) { + return NGHTTP2_ERR_INVALID_ARGUMENT; + } + + item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item)); + if (item == NULL) { + return NGHTTP2_ERR_NOMEM; + } + + if (niv > 0) { + iv_copy = nghttp2_frame_iv_copy(iv, niv, mem); + if (iv_copy == NULL) { + nghttp2_mem_free(mem, item); + return NGHTTP2_ERR_NOMEM; + } + } else { + iv_copy = NULL; + } + + if ((flags & NGHTTP2_FLAG_ACK) == 0) { + if (niv > 0) { + session->inflight_iv = nghttp2_frame_iv_copy(iv, niv, mem); + + if (session->inflight_iv == NULL) { + nghttp2_mem_free(mem, iv_copy); + nghttp2_mem_free(mem, item); + return NGHTTP2_ERR_NOMEM; + } + } else { + session->inflight_iv = NULL; + } + + session->inflight_niv = niv; + } + + nghttp2_outbound_item_init(item); + + frame = &item->frame; + + nghttp2_frame_settings_init(&frame->settings, flags, iv_copy, niv); + rv = nghttp2_session_add_item(session, item); + if (rv != 0) { + /* The only expected error is fatal one */ + assert(nghttp2_is_fatal(rv)); + + if ((flags & NGHTTP2_FLAG_ACK) == 0) { + nghttp2_mem_free(mem, session->inflight_iv); + session->inflight_iv = NULL; + session->inflight_niv = -1; + } + + nghttp2_frame_settings_free(&frame->settings, mem); + nghttp2_mem_free(mem, item); + + return rv; + } + + /* Extract NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS and ENABLE_PUSH + here. We use it to refuse the incoming stream and PUSH_PROMISE + with RST_STREAM. */ + + for (i = niv; i > 0; --i) { + if (iv[i - 1].settings_id == NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS) { + session->pending_local_max_concurrent_stream = iv[i - 1].value; + break; + } + } + + for (i = niv; i > 0; --i) { + if (iv[i - 1].settings_id == NGHTTP2_SETTINGS_ENABLE_PUSH) { + session->pending_enable_push = iv[i - 1].value; + break; + } + } + + return 0; +} + +int nghttp2_session_pack_data(nghttp2_session *session, nghttp2_bufs *bufs, + size_t datamax, nghttp2_frame *frame, + nghttp2_data_aux_data *aux_data, + nghttp2_stream *stream) { + int rv; + uint32_t data_flags; + ssize_t payloadlen; + ssize_t padded_payloadlen; + nghttp2_buf *buf; + size_t max_payloadlen; + + assert(bufs->head == bufs->cur); + + buf = &bufs->cur->buf; + + if (session->callbacks.read_length_callback) { + + payloadlen = session->callbacks.read_length_callback( + session, frame->hd.type, stream->stream_id, session->remote_window_size, + stream->remote_window_size, session->remote_settings.max_frame_size, + session->user_data); + + DEBUGF(fprintf(stderr, "send: read_length_callback=%zd\n", payloadlen)); + + payloadlen = nghttp2_session_enforce_flow_control_limits(session, stream, + payloadlen); + + DEBUGF(fprintf(stderr, + "send: read_length_callback after flow control=%zd\n", + payloadlen)); + + if (payloadlen <= 0) { + return NGHTTP2_ERR_CALLBACK_FAILURE; + } + + if (payloadlen > nghttp2_buf_avail(buf)) { + /* Resize the current buffer(s). The reason why we do +1 for + buffer size is for possible padding field. */ + rv = nghttp2_bufs_realloc(&session->aob.framebufs, + NGHTTP2_FRAME_HDLEN + 1 + payloadlen); + + if (rv != 0) { + DEBUGF(fprintf(stderr, "send: realloc buffer failed rv=%d", rv)); + /* If reallocation failed, old buffers are still in tact. So + use safe limit. */ + payloadlen = datamax; + + DEBUGF( + fprintf(stderr, "send: use safe limit payloadlen=%zd", payloadlen)); + } else { + assert(&session->aob.framebufs == bufs); + + buf = &bufs->cur->buf; + } + } + datamax = (size_t)payloadlen; + } + + /* Current max DATA length is less then buffer chunk size */ + assert(nghttp2_buf_avail(buf) >= (ssize_t)datamax); + + data_flags = NGHTTP2_DATA_FLAG_NONE; + payloadlen = aux_data->data_prd.read_callback( + session, frame->hd.stream_id, buf->pos, datamax, &data_flags, + &aux_data->data_prd.source, session->user_data); + + if (payloadlen == NGHTTP2_ERR_DEFERRED || + payloadlen == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) { + DEBUGF(fprintf(stderr, "send: DATA postponed due to %s\n", + nghttp2_strerror((int)payloadlen))); + + return (int)payloadlen; + } + + if (payloadlen < 0 || datamax < (size_t)payloadlen) { + /* This is the error code when callback is failed. */ + return NGHTTP2_ERR_CALLBACK_FAILURE; + } + + buf->last = buf->pos + payloadlen; + buf->pos -= NGHTTP2_FRAME_HDLEN; + + /* Clear flags, because this may contain previous flags of previous + DATA */ + frame->hd.flags = NGHTTP2_FLAG_NONE; + + if (data_flags & NGHTTP2_DATA_FLAG_EOF) { + aux_data->eof = 1; + /* If NGHTTP2_DATA_FLAG_NO_END_STREAM is set, don't set + NGHTTP2_FLAG_END_STREAM */ + if ((aux_data->flags & NGHTTP2_FLAG_END_STREAM) && + (data_flags & NGHTTP2_DATA_FLAG_NO_END_STREAM) == 0) { + frame->hd.flags |= NGHTTP2_FLAG_END_STREAM; + } + } + + if (data_flags & NGHTTP2_DATA_FLAG_NO_COPY) { + if (session->callbacks.send_data_callback == NULL) { + DEBUGF(fprintf( + stderr, + "NGHTTP2_DATA_FLAG_NO_COPY requires send_data_callback set\n")); + + return NGHTTP2_ERR_CALLBACK_FAILURE; + } + aux_data->no_copy = 1; + } + + frame->hd.length = payloadlen; + frame->data.padlen = 0; + + max_payloadlen = nghttp2_min(datamax, frame->hd.length + NGHTTP2_MAX_PADLEN); + + padded_payloadlen = + session_call_select_padding(session, frame, max_payloadlen); + + if (nghttp2_is_fatal((int)padded_payloadlen)) { + return (int)padded_payloadlen; + } + + frame->data.padlen = padded_payloadlen - payloadlen; + + nghttp2_frame_pack_frame_hd(buf->pos, &frame->hd); + + rv = nghttp2_frame_add_pad(bufs, &frame->hd, frame->data.padlen, + aux_data->no_copy); + if (rv != 0) { + return rv; + } + + session_outbound_item_schedule(session, stream->item, + stream->effective_weight); + + return 0; +} + +void *nghttp2_session_get_stream_user_data(nghttp2_session *session, + int32_t stream_id) { + nghttp2_stream *stream; + stream = nghttp2_session_get_stream(session, stream_id); + if (stream) { + return stream->stream_user_data; + } else { + return NULL; + } +} + +int nghttp2_session_set_stream_user_data(nghttp2_session *session, + int32_t stream_id, + void *stream_user_data) { + nghttp2_stream *stream; + stream = nghttp2_session_get_stream(session, stream_id); + if (!stream) { + return NGHTTP2_ERR_INVALID_ARGUMENT; + } + stream->stream_user_data = stream_user_data; + return 0; +} + +int nghttp2_session_resume_data(nghttp2_session *session, int32_t stream_id) { + int rv; + nghttp2_stream *stream; + stream = nghttp2_session_get_stream(session, stream_id); + if (stream == NULL || !nghttp2_stream_check_deferred_item(stream)) { + return NGHTTP2_ERR_INVALID_ARGUMENT; + } + + rv = nghttp2_stream_resume_deferred_item( + stream, NGHTTP2_STREAM_FLAG_DEFERRED_USER, session); + + if (nghttp2_is_fatal(rv)) { + return rv; + } + + return rv; +} + +size_t nghttp2_session_get_outbound_queue_size(nghttp2_session *session) { + return nghttp2_outbound_queue_size(&session->ob_urgent) + + nghttp2_outbound_queue_size(&session->ob_reg) + + nghttp2_outbound_queue_size(&session->ob_syn) + + nghttp2_pq_size(&session->ob_da_pq); +} + +int32_t +nghttp2_session_get_stream_effective_recv_data_length(nghttp2_session *session, + int32_t stream_id) { + nghttp2_stream *stream; + stream = nghttp2_session_get_stream(session, stream_id); + if (stream == NULL) { + return -1; + } + return stream->recv_window_size < 0 ? 0 : stream->recv_window_size; +} + +int32_t +nghttp2_session_get_stream_effective_local_window_size(nghttp2_session *session, + int32_t stream_id) { + nghttp2_stream *stream; + stream = nghttp2_session_get_stream(session, stream_id); + if (stream == NULL) { + return -1; + } + return stream->local_window_size; +} + +int32_t +nghttp2_session_get_effective_recv_data_length(nghttp2_session *session) { + return session->recv_window_size < 0 ? 0 : session->recv_window_size; +} + +int32_t +nghttp2_session_get_effective_local_window_size(nghttp2_session *session) { + return session->local_window_size; +} + +int32_t nghttp2_session_get_stream_remote_window_size(nghttp2_session *session, + int32_t stream_id) { + nghttp2_stream *stream; + + stream = nghttp2_session_get_stream(session, stream_id); + if (stream == NULL) { + return -1; + } + + /* stream->remote_window_size can be negative when + SETTINGS_INITIAL_WINDOW_SIZE is changed. */ + return nghttp2_max(0, stream->remote_window_size); +} + +int32_t nghttp2_session_get_remote_window_size(nghttp2_session *session) { + return session->remote_window_size; +} + +uint32_t nghttp2_session_get_remote_settings(nghttp2_session *session, + nghttp2_settings_id id) { + switch (id) { + case NGHTTP2_SETTINGS_HEADER_TABLE_SIZE: + return session->remote_settings.header_table_size; + case NGHTTP2_SETTINGS_ENABLE_PUSH: + return session->remote_settings.enable_push; + case NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS: + return session->remote_settings.max_concurrent_streams; + case NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE: + return session->remote_settings.initial_window_size; + case NGHTTP2_SETTINGS_MAX_FRAME_SIZE: + return session->remote_settings.max_frame_size; + case NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE: + return session->remote_settings.max_header_list_size; + } + + assert(0); +} + +int nghttp2_session_upgrade(nghttp2_session *session, + const uint8_t *settings_payload, + size_t settings_payloadlen, + void *stream_user_data) { + nghttp2_stream *stream; + nghttp2_frame frame; + nghttp2_settings_entry *iv; + size_t niv; + int rv; + nghttp2_priority_spec pri_spec; + nghttp2_mem *mem; + + mem = &session->mem; + + if ((!session->server && session->next_stream_id != 1) || + (session->server && session->last_recv_stream_id >= 1)) { + return NGHTTP2_ERR_PROTO; + } + if (settings_payloadlen % NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH) { + return NGHTTP2_ERR_INVALID_ARGUMENT; + } + rv = nghttp2_frame_unpack_settings_payload2(&iv, &niv, settings_payload, + settings_payloadlen, mem); + if (rv != 0) { + return rv; + } + + if (session->server) { + nghttp2_frame_hd_init(&frame.hd, settings_payloadlen, NGHTTP2_SETTINGS, + NGHTTP2_FLAG_NONE, 0); + frame.settings.iv = iv; + frame.settings.niv = niv; + rv = nghttp2_session_on_settings_received(session, &frame, 1 /* No ACK */); + } else { + rv = nghttp2_submit_settings(session, NGHTTP2_FLAG_NONE, iv, niv); + } + nghttp2_mem_free(mem, iv); + if (rv != 0) { + return rv; + } + + nghttp2_priority_spec_default_init(&pri_spec); + + stream = nghttp2_session_open_stream( + session, 1, NGHTTP2_STREAM_FLAG_NONE, &pri_spec, NGHTTP2_STREAM_OPENING, + session->server ? NULL : stream_user_data); + if (stream == NULL) { + return NGHTTP2_ERR_NOMEM; + } + if (session->server) { + nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_RD); + session->last_recv_stream_id = 1; + session->last_proc_stream_id = 1; + } else { + nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_WR); + session->next_stream_id += 2; + } + return 0; +} + +int nghttp2_session_get_stream_local_close(nghttp2_session *session, + int32_t stream_id) { + nghttp2_stream *stream; + + stream = nghttp2_session_get_stream(session, stream_id); + + if (!stream) { + return -1; + } + + return (stream->shut_flags & NGHTTP2_SHUT_WR) != 0; +} + +int nghttp2_session_get_stream_remote_close(nghttp2_session *session, + int32_t stream_id) { + nghttp2_stream *stream; + + stream = nghttp2_session_get_stream(session, stream_id); + + if (!stream) { + return -1; + } + + return (stream->shut_flags & NGHTTP2_SHUT_RD) != 0; +} + +int nghttp2_session_consume(nghttp2_session *session, int32_t stream_id, + size_t size) { + int rv; + nghttp2_stream *stream; + + if (stream_id == 0) { + return NGHTTP2_ERR_INVALID_ARGUMENT; + } + + if (!(session->opt_flags & NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE)) { + return NGHTTP2_ERR_INVALID_STATE; + } + + rv = session_update_connection_consumed_size(session, size); + + if (nghttp2_is_fatal(rv)) { + return rv; + } + + stream = nghttp2_session_get_stream(session, stream_id); + + if (!stream) { + return 0; + } + + rv = session_update_stream_consumed_size(session, stream, size); + + if (nghttp2_is_fatal(rv)) { + return rv; + } + + return 0; +} + +int nghttp2_session_consume_connection(nghttp2_session *session, size_t size) { + int rv; + + if (!(session->opt_flags & NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE)) { + return NGHTTP2_ERR_INVALID_STATE; + } + + rv = session_update_connection_consumed_size(session, size); + + if (nghttp2_is_fatal(rv)) { + return rv; + } + + return 0; +} + +int nghttp2_session_consume_stream(nghttp2_session *session, int32_t stream_id, + size_t size) { + int rv; + nghttp2_stream *stream; + + if (stream_id == 0) { + return NGHTTP2_ERR_INVALID_ARGUMENT; + } + + if (!(session->opt_flags & NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE)) { + return NGHTTP2_ERR_INVALID_STATE; + } + + stream = nghttp2_session_get_stream(session, stream_id); + + if (!stream) { + return 0; + } + + rv = session_update_stream_consumed_size(session, stream, size); + + if (nghttp2_is_fatal(rv)) { + return rv; + } + + return 0; +} + +int nghttp2_session_set_next_stream_id(nghttp2_session *session, + int32_t next_stream_id) { + if (next_stream_id <= 0 || + session->next_stream_id > (uint32_t)next_stream_id) { + return NGHTTP2_ERR_INVALID_ARGUMENT; + } + + if (session->server) { + if (next_stream_id % 2) { + return NGHTTP2_ERR_INVALID_ARGUMENT; + } + } else if (next_stream_id % 2 == 0) { + return NGHTTP2_ERR_INVALID_ARGUMENT; + } + + session->next_stream_id = next_stream_id; + return 0; +} + +uint32_t nghttp2_session_get_next_stream_id(nghttp2_session *session) { + return session->next_stream_id; +} + +int32_t nghttp2_session_get_last_proc_stream_id(nghttp2_session *session) { + return session->last_proc_stream_id; +} diff --git a/lib/nghttp2_session.h b/lib/nghttp2_session.h new file mode 100644 index 0000000..41b1608 --- /dev/null +++ b/lib/nghttp2_session.h @@ -0,0 +1,762 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGHTTP2_SESSION_H +#define NGHTTP2_SESSION_H + +#ifdef HAVE_CONFIG_H +#include +#endif /* HAVE_CONFIG_H */ + +#include +#include "nghttp2_pq.h" +#include "nghttp2_map.h" +#include "nghttp2_frame.h" +#include "nghttp2_hd.h" +#include "nghttp2_stream.h" +#include "nghttp2_outbound_item.h" +#include "nghttp2_int.h" +#include "nghttp2_buf.h" +#include "nghttp2_callbacks.h" +#include "nghttp2_mem.h" + +/* + * Option flags. + */ +typedef enum { + NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE = 1 << 0, + NGHTTP2_OPTMASK_NO_RECV_CLIENT_MAGIC = 1 << 1, + NGHTTP2_OPTMASK_NO_HTTP_MESSAGING = 1 << 2 +} nghttp2_optmask; + +typedef enum { + NGHTTP2_OB_POP_ITEM, + NGHTTP2_OB_SEND_DATA, + NGHTTP2_OB_SEND_NO_COPY, + NGHTTP2_OB_SEND_CLIENT_MAGIC +} nghttp2_outbound_state; + +typedef struct { + nghttp2_outbound_item *item; + nghttp2_bufs framebufs; + nghttp2_outbound_state state; +} nghttp2_active_outbound_item; + +/* Buffer length for inbound raw byte stream used in + nghttp2_session_recv(). */ +#define NGHTTP2_INBOUND_BUFFER_LENGTH 16384 + +/* Internal state when receiving incoming frame */ +typedef enum { + /* Receiving frame header */ + NGHTTP2_IB_READ_CLIENT_MAGIC, + NGHTTP2_IB_READ_FIRST_SETTINGS, + NGHTTP2_IB_READ_HEAD, + NGHTTP2_IB_READ_NBYTE, + NGHTTP2_IB_READ_HEADER_BLOCK, + NGHTTP2_IB_IGN_HEADER_BLOCK, + NGHTTP2_IB_IGN_PAYLOAD, + NGHTTP2_IB_FRAME_SIZE_ERROR, + NGHTTP2_IB_READ_SETTINGS, + NGHTTP2_IB_READ_GOAWAY_DEBUG, + NGHTTP2_IB_EXPECT_CONTINUATION, + NGHTTP2_IB_IGN_CONTINUATION, + NGHTTP2_IB_READ_PAD_DATA, + NGHTTP2_IB_READ_DATA, + NGHTTP2_IB_IGN_DATA, + NGHTTP2_IB_IGN_ALL +} nghttp2_inbound_state; + +#define NGHTTP2_INBOUND_NUM_IV 7 + +typedef struct { + nghttp2_frame frame; + /* Storage for extension frame payload. frame->ext.payload points + to this structure to avoid frequent memory allocation. */ + nghttp2_ext_frame_payload ext_frame_payload; + /* The received SETTINGS entry. The protocol says that we only cares + about the defined settings ID. If unknown ID is received, it is + ignored. We use last entry to hold minimum header table size if + same settings are multiple times. */ + nghttp2_settings_entry iv[NGHTTP2_INBOUND_NUM_IV]; + /* buffer pointers to small buffer, raw_sbuf */ + nghttp2_buf sbuf; + /* buffer pointers to large buffer, raw_lbuf */ + nghttp2_buf lbuf; + /* Large buffer, malloced on demand */ + uint8_t *raw_lbuf; + /* The number of entry filled in |iv| */ + size_t niv; + /* How many bytes we still need to receive for current frame */ + size_t payloadleft; + /* padding length for the current frame */ + size_t padlen; + nghttp2_inbound_state state; + /* Small buffer. Currently the largest contiguous chunk to buffer + is frame header. We buffer part of payload, but they are smaller + than frame header. */ + uint8_t raw_sbuf[NGHTTP2_FRAME_HDLEN]; +} nghttp2_inbound_frame; + +typedef struct { + uint32_t header_table_size; + uint32_t enable_push; + uint32_t max_concurrent_streams; + uint32_t initial_window_size; + uint32_t max_frame_size; + uint32_t max_header_list_size; +} nghttp2_settings_storage; + +typedef enum { + NGHTTP2_GOAWAY_NONE = 0, + /* Flag means that connection should be terminated after sending GOAWAY. */ + NGHTTP2_GOAWAY_TERM_ON_SEND = 0x1, + /* Flag means GOAWAY to terminate session has been sent */ + NGHTTP2_GOAWAY_TERM_SENT = 0x2, + /* Flag means GOAWAY was sent */ + NGHTTP2_GOAWAY_SENT = 0x4, + /* Flag means GOAWAY was received */ + NGHTTP2_GOAWAY_RECV = 0x8 +} nghttp2_goaway_flag; + +struct nghttp2_session { + nghttp2_map /* */ streams; + nghttp2_stream_roots roots; + /* Queue for outbound urgent frames (PING and SETTINGS) */ + nghttp2_outbound_queue ob_urgent; + /* Queue for non-DATA frames */ + nghttp2_outbound_queue ob_reg; + /* Queue for outbound stream-creating HEADERS (request or push + response) frame, which are subject to + SETTINGS_MAX_CONCURRENT_STREAMS limit. */ + nghttp2_outbound_queue ob_syn; + /* Queue for DATA frame */ + nghttp2_pq /* */ ob_da_pq; + nghttp2_active_outbound_item aob; + nghttp2_inbound_frame iframe; + nghttp2_hd_deflater hd_deflater; + nghttp2_hd_inflater hd_inflater; + nghttp2_session_callbacks callbacks; + /* Memory allocator */ + nghttp2_mem mem; + /* Base value when we schedule next DATA frame write. This is + updated when one frame was written. */ + uint64_t last_cycle; + void *user_data; + /* Points to the latest closed stream. NULL if there is no closed + stream. Only used when session is initialized as server. */ + nghttp2_stream *closed_stream_head; + /* Points to the oldest closed stream. NULL if there is no closed + stream. Only used when session is initialized as server. */ + nghttp2_stream *closed_stream_tail; + /* Points to the latest idle stream. NULL if there is no idle + stream. Only used when session is initialized as server .*/ + nghttp2_stream *idle_stream_head; + /* Points to the oldest idle stream. NULL if there is no idle + stream. Only used when session is initialized as erver. */ + nghttp2_stream *idle_stream_tail; + /* In-flight SETTINGS values. NULL does not necessarily mean there + is no in-flight SETTINGS. */ + nghttp2_settings_entry *inflight_iv; + /* The number of entries in |inflight_iv|. -1 if there is no + in-flight SETTINGS. */ + ssize_t inflight_niv; + /* The number of outgoing streams. This will be capped by + remote_settings.max_concurrent_streams. */ + size_t num_outgoing_streams; + /* The number of incoming streams. This will be capped by + local_settings.max_concurrent_streams. */ + size_t num_incoming_streams; + /* The number of closed streams still kept in |streams| hash. The + closed streams can be accessed through single linked list + |closed_stream_head|. The current implementation only keeps + incoming streams and session is initialized as server. */ + size_t num_closed_streams; + /* The number of idle streams kept in |streams| hash. The idle + streams can be accessed through doubly linked list + |idle_stream_head|. The current implementation only keeps idle + streams if session is initialized as server. */ + size_t num_idle_streams; + /* The number of bytes allocated for nvbuf */ + size_t nvbuflen; + /* Next Stream ID. Made unsigned int to detect >= (1 << 31). */ + uint32_t next_stream_id; + /* The largest stream ID received so far */ + int32_t last_recv_stream_id; + /* The largest stream ID which has been processed in some way. This + value will be used as last-stream-id when sending GOAWAY + frame. */ + int32_t last_proc_stream_id; + /* Counter of unique ID of PING. Wraps when it exceeds + NGHTTP2_MAX_UNIQUE_ID */ + uint32_t next_unique_id; + /* This is the last-stream-ID we have sent in GOAWAY */ + int32_t local_last_stream_id; + /* This is the value in GOAWAY frame received from remote endpoint. */ + int32_t remote_last_stream_id; + /* Current sender window size. This value is computed against the + current initial window size of remote endpoint. */ + int32_t remote_window_size; + /* Keep track of the number of bytes received without + WINDOW_UPDATE. This could be negative after submitting negative + value to WINDOW_UPDATE. */ + int32_t recv_window_size; + /* The number of bytes consumed by the application and now is + subject to WINDOW_UPDATE. This is only used when auto + WINDOW_UPDATE is turned off. */ + int32_t consumed_size; + /* The amount of recv_window_size cut using submitting negative + value to WINDOW_UPDATE */ + int32_t recv_reduction; + /* window size for local flow control. It is initially set to + NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE and could be + increased/decreased by submitting WINDOW_UPDATE. See + nghttp2_submit_window_update(). */ + int32_t local_window_size; + /* Settings value received from the remote endpoint. We just use ID + as index. The index = 0 is unused. */ + nghttp2_settings_storage remote_settings; + /* Settings value of the local endpoint. */ + nghttp2_settings_storage local_settings; + /* Option flags. This is bitwise-OR of 0 or more of nghttp2_optmask. */ + uint32_t opt_flags; + /* Unacked local SETTINGS_MAX_CONCURRENT_STREAMS value. We use this + to refuse the incoming stream if it exceeds this value. */ + uint32_t pending_local_max_concurrent_stream; + /* Unacked local ENABLE_PUSH value. We use this to refuse + PUSH_PROMISE before SETTINGS ACK is received. */ + uint8_t pending_enable_push; + /* Nonzero if the session is server side. */ + uint8_t server; + /* Flags indicating GOAWAY is sent and/or recieved. The flags are + composed by bitwise OR-ing nghttp2_goaway_flag. */ + uint8_t goaway_flags; +}; + +/* Struct used when updating initial window size of each active + stream. */ +typedef struct { + nghttp2_session *session; + int32_t new_window_size, old_window_size; +} nghttp2_update_window_size_arg; + +typedef struct { + nghttp2_session *session; + /* linked list of streams to close */ + nghttp2_stream *head; + int32_t last_stream_id; + /* nonzero if GOAWAY is sent to peer, which means we are going to + close incoming streams. zero if GOAWAY is received from peer and + we are going to close outgoing streams. */ + int incoming; +} nghttp2_close_stream_on_goaway_arg; + +/* TODO stream timeout etc */ + +/* + * Returns nonzero value if |stream_id| is initiated by local + * endpoint. + */ +int nghttp2_session_is_my_stream_id(nghttp2_session *session, + int32_t stream_id); + +/* + * Adds |item| to the outbound queue in |session|. When this function + * succeeds, it takes ownership of |item|. So caller must not free it + * on success. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + * NGHTTP2_ERR_STREAM_CLOSED + * Stream already closed (DATA frame only) + */ +int nghttp2_session_add_item(nghttp2_session *session, + nghttp2_outbound_item *item); + +/* + * Adds RST_STREAM frame for the stream |stream_id| with the error + * code |error_code|. This is a convenient function built on top of + * nghttp2_session_add_frame() to add RST_STREAM easily. + * + * This function simply returns 0 without adding RST_STREAM frame if + * given stream is in NGHTTP2_STREAM_CLOSING state, because multiple + * RST_STREAM for a stream is redundant. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + */ +int nghttp2_session_add_rst_stream(nghttp2_session *session, int32_t stream_id, + uint32_t error_code); + +/* + * Adds PING frame. This is a convenient functin built on top of + * nghttp2_session_add_frame() to add PING easily. + * + * If the |opaque_data| is not NULL, it must point to 8 bytes memory + * region of data. The data pointed by |opaque_data| is copied. It can + * be NULL. In this case, 8 bytes NULL is used. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + */ +int nghttp2_session_add_ping(nghttp2_session *session, uint8_t flags, + const uint8_t *opaque_data); + +/* + * Adds GOAWAY frame with the last-stream-ID |last_stream_id| and the + * error code |error_code|. This is a convenient function built on top + * of nghttp2_session_add_frame() to add GOAWAY easily. The + * |aux_flags| are bitwise-OR of one or more of + * nghttp2_goaway_aux_flag. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + * NGHTTP2_ERR_INVALID_ARGUMENT + * The |opaque_data_len| is too large. + */ +int nghttp2_session_add_goaway(nghttp2_session *session, int32_t last_stream_id, + uint32_t error_code, const uint8_t *opaque_data, + size_t opaque_data_len, uint8_t aux_flags); + +/* + * Adds WINDOW_UPDATE frame with stream ID |stream_id| and + * window-size-increment |window_size_increment|. This is a convenient + * function built on top of nghttp2_session_add_frame() to add + * WINDOW_UPDATE easily. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + */ +int nghttp2_session_add_window_update(nghttp2_session *session, uint8_t flags, + int32_t stream_id, + int32_t window_size_increment); + +/* + * Adds SETTINGS frame. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + */ +int nghttp2_session_add_settings(nghttp2_session *session, uint8_t flags, + const nghttp2_settings_entry *iv, size_t niv); + +/* + * Creates new stream in |session| with stream ID |stream_id|, + * priority |pri_spec| and flags |flags|. The |flags| is bitwise OR + * of nghttp2_stream_flag. Since this function is called when initial + * HEADERS is sent or received, these flags are taken from it. The + * state of stream is set to |initial_state|. The |stream_user_data| + * is a pointer to the arbitrary user supplied data to be associated + * to this stream. + * + * If |initial_state| is NGHTTP2_STREAM_RESERVED, this function sets + * NGHTTP2_STREAM_FLAG_PUSH flag set. + * + * This function returns a pointer to created new stream object, or + * NULL. + */ +nghttp2_stream *nghttp2_session_open_stream(nghttp2_session *session, + int32_t stream_id, uint8_t flags, + nghttp2_priority_spec *pri_spec, + nghttp2_stream_state initial_state, + void *stream_user_data); + +/* + * Closes stream whose stream ID is |stream_id|. The reason of closure + * is indicated by the |error_code|. When closing the stream, + * on_stream_close_callback will be called. + * + * If the session is initialized as server and |stream| is incoming + * stream, stream is just marked closed and this function calls + * nghttp2_session_keep_closed_stream() with |stream|. Otherwise, + * |stream| will be deleted from memory. + * + * This function returns 0 if it succeeds, or one the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory + * NGHTTP2_ERR_INVALID_ARGUMENT + * The specified stream does not exist. + * NGHTTP2_ERR_CALLBACK_FAILURE + * The callback function failed. + */ +int nghttp2_session_close_stream(nghttp2_session *session, int32_t stream_id, + uint32_t error_code); + +/* + * Deletes |stream| from memory. After this function returns, stream + * cannot be accessed. + * + */ +void nghttp2_session_destroy_stream(nghttp2_session *session, + nghttp2_stream *stream); + +/* + * Tries to keep incoming closed stream |stream|. Due to the + * limitation of maximum number of streams in memory, |stream| is not + * closed and just deleted from memory (see + * nghttp2_session_destroy_stream). + */ +void nghttp2_session_keep_closed_stream(nghttp2_session *session, + nghttp2_stream *stream); + +/* + * Appends |stream| to linked list |session->idle_stream_head|. We + * apply fixed limit for list size. To fit into that limit, one or + * more oldest streams are removed from list as necessary. + */ +void nghttp2_session_keep_idle_stream(nghttp2_session *session, + nghttp2_stream *stream); + +/* + * Detaches |stream| from idle streams linked list. + */ +void nghttp2_session_detach_idle_stream(nghttp2_session *session, + nghttp2_stream *stream); + +/* + * Deletes closed stream to ensure that number of incoming streams + * including active and closed is in the maximum number of allowed + * stream. If |offset| is nonzero, it is decreased from the maximum + * number of allowed stream when comparing number of active and closed + * stream and the maximum number. + */ +void nghttp2_session_adjust_closed_stream(nghttp2_session *session, + ssize_t offset); + +/* + * Deletes idle stream to ensure that number of idle streams is in + * certain limit. + */ +void nghttp2_session_adjust_idle_stream(nghttp2_session *session); + +/* + * If further receptions and transmissions over the stream |stream_id| + * are disallowed, close the stream with error code NGHTTP2_NO_ERROR. + * + * This function returns 0 if it + * succeeds, or one of the following negative error codes: + * + * NGHTTP2_ERR_INVALID_ARGUMENT + * The specified stream does not exist. + */ +int nghttp2_session_close_stream_if_shut_rdwr(nghttp2_session *session, + nghttp2_stream *stream); + +int nghttp2_session_end_request_headers_received(nghttp2_session *session, + nghttp2_frame *frame, + nghttp2_stream *stream); + +int nghttp2_session_end_response_headers_received(nghttp2_session *session, + nghttp2_frame *frame, + nghttp2_stream *stream); + +int nghttp2_session_end_headers_received(nghttp2_session *session, + nghttp2_frame *frame, + nghttp2_stream *stream); + +int nghttp2_session_on_request_headers_received(nghttp2_session *session, + nghttp2_frame *frame); + +int nghttp2_session_on_response_headers_received(nghttp2_session *session, + nghttp2_frame *frame, + nghttp2_stream *stream); + +int nghttp2_session_on_push_response_headers_received(nghttp2_session *session, + nghttp2_frame *frame, + nghttp2_stream *stream); + +/* + * Called when HEADERS is received, assuming |frame| is properly + * initialized. This function does first validate received frame and + * then open stream and call callback functions. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + * NGHTTP2_ERR_IGN_HEADER_BLOCK + * Frame was rejected and header block must be decoded but + * result must be ignored. + * NGHTTP2_ERR_CALLBACK_FAILURE + * The read_callback failed + */ +int nghttp2_session_on_headers_received(nghttp2_session *session, + nghttp2_frame *frame, + nghttp2_stream *stream); + +/* + * Called when PRIORITY is received, assuming |frame| is properly + * initialized. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + * NGHTTP2_ERR_CALLBACK_FAILURE + * The read_callback failed + */ +int nghttp2_session_on_priority_received(nghttp2_session *session, + nghttp2_frame *frame); + +/* + * Called when RST_STREAM is received, assuming |frame| is properly + * initialized. + * + * This function returns 0 if it succeeds, or one the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory + * NGHTTP2_ERR_CALLBACK_FAILURE + * The read_callback failed + */ +int nghttp2_session_on_rst_stream_received(nghttp2_session *session, + nghttp2_frame *frame); + +/* + * Called when SETTINGS is received, assuming |frame| is properly + * initialized. If |noack| is non-zero, SETTINGS with ACK will not be + * submitted. If |frame| has NGHTTP2_FLAG_ACK flag set, no SETTINGS + * with ACK will not be submitted regardless of |noack|. + * + * This function returns 0 if it succeeds, or one the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory + * NGHTTP2_ERR_CALLBACK_FAILURE + * The read_callback failed + */ +int nghttp2_session_on_settings_received(nghttp2_session *session, + nghttp2_frame *frame, int noack); + +/* + * Called when PUSH_PROMISE is received, assuming |frame| is properly + * initialized. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + * NGHTTP2_ERR_IGN_HEADER_BLOCK + * Frame was rejected and header block must be decoded but + * result must be ignored. + * NGHTTP2_ERR_CALLBACK_FAILURE + * The read_callback failed + */ +int nghttp2_session_on_push_promise_received(nghttp2_session *session, + nghttp2_frame *frame); + +/* + * Called when PING is received, assuming |frame| is properly + * initialized. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + * NGHTTP2_ERR_CALLBACK_FAILURE + * The callback function failed. + */ +int nghttp2_session_on_ping_received(nghttp2_session *session, + nghttp2_frame *frame); + +/* + * Called when GOAWAY is received, assuming |frame| is properly + * initialized. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + * NGHTTP2_ERR_CALLBACK_FAILURE + * The callback function failed. + */ +int nghttp2_session_on_goaway_received(nghttp2_session *session, + nghttp2_frame *frame); + +/* + * Called when WINDOW_UPDATE is recieved, assuming |frame| is properly + * initialized. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + * NGHTTP2_ERR_CALLBACK_FAILURE + * The callback function failed. + */ +int nghttp2_session_on_window_update_received(nghttp2_session *session, + nghttp2_frame *frame); + +/* + * Called when DATA is received, assuming |frame| is properly + * initialized. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + * NGHTTP2_ERR_CALLBACK_FAILURE + * The callback function failed. + */ +int nghttp2_session_on_data_received(nghttp2_session *session, + nghttp2_frame *frame); + +/* + * Returns nghttp2_stream* object whose stream ID is |stream_id|. It + * could be NULL if such stream does not exist. This function returns + * NULL if stream is marked as closed. + */ +nghttp2_stream *nghttp2_session_get_stream(nghttp2_session *session, + int32_t stream_id); + +/* + * This function behaves like nghttp2_session_get_stream(), but it + * returns stream object even if it is marked as closed or in + * NGHTTP2_STREAM_IDLE state. + */ +nghttp2_stream *nghttp2_session_get_stream_raw(nghttp2_session *session, + int32_t stream_id); + +/* + * Packs DATA frame |frame| in wire frame format and stores it in + * |bufs|. Payload will be read using |aux_data->data_prd|. The + * length of payload is at most |datamax| bytes. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_DEFERRED + * The DATA frame is postponed. + * NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE + * The read_callback failed (stream error). + * NGHTTP2_ERR_NOMEM + * Out of memory. + * NGHTTP2_ERR_CALLBACK_FAILURE + * The read_callback failed (session error). + */ +int nghttp2_session_pack_data(nghttp2_session *session, nghttp2_bufs *bufs, + size_t datamax, nghttp2_frame *frame, + nghttp2_data_aux_data *aux_data, + nghttp2_stream *stream); + +/* + * Pops and returns next item to send. If there is no such item, + * returns NULL. This function takes into account max concurrent + * streams. That means if session->ob_pq is empty but + * session->ob_ss_pq has item and max concurrent streams is reached, + * then this function returns NULL. + */ +nghttp2_outbound_item * +nghttp2_session_pop_next_ob_item(nghttp2_session *session); + +/* + * Returns next item to send. If there is no such item, this function + * returns NULL. This function takes into account max concurrent + * streams. That means if session->ob_pq is empty but + * session->ob_ss_pq has item and max concurrent streams is reached, + * then this function returns NULL. + */ +nghttp2_outbound_item * +nghttp2_session_get_next_ob_item(nghttp2_session *session); + +/* + * Updates local settings with the |iv|. The number of elements in the + * array pointed by the |iv| is given by the |niv|. This function + * assumes that the all settings_id member in |iv| are in range 1 to + * NGHTTP2_SETTINGS_MAX, inclusive. + * + * While updating individual stream's local window size, if the window + * size becomes strictly larger than NGHTTP2_MAX_WINDOW_SIZE, + * RST_STREAM is issued against such a stream. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory + */ +int nghttp2_session_update_local_settings(nghttp2_session *session, + nghttp2_settings_entry *iv, + size_t niv); + +/* + * Re-prioritize |stream|. The new priority specification is + * |pri_spec|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory + */ +int nghttp2_session_reprioritize_stream(nghttp2_session *session, + nghttp2_stream *stream, + const nghttp2_priority_spec *pri_spec); + +/* + * Terminates current |session| with the |error_code|. The |reason| + * is NULL-terminated debug string. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + * NGHTTP2_ERR_INVALID_ARGUMENT + * The |reason| is too long. + */ +int nghttp2_session_terminate_session_with_reason(nghttp2_session *session, + uint32_t error_code, + const char *reason); + +#endif /* NGHTTP2_SESSION_H */ diff --git a/lib/nghttp2_stream.c b/lib/nghttp2_stream.c new file mode 100644 index 0000000..753932f --- /dev/null +++ b/lib/nghttp2_stream.c @@ -0,0 +1,1010 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "nghttp2_stream.h" + +#include +#include + +#include "nghttp2_session.h" +#include "nghttp2_helper.h" + +void nghttp2_stream_init(nghttp2_stream *stream, int32_t stream_id, + uint8_t flags, nghttp2_stream_state initial_state, + int32_t weight, nghttp2_stream_roots *roots, + int32_t remote_initial_window_size, + int32_t local_initial_window_size, + void *stream_user_data) { + nghttp2_map_entry_init(&stream->map_entry, stream_id); + stream->stream_id = stream_id; + stream->flags = flags; + stream->state = initial_state; + stream->shut_flags = NGHTTP2_SHUT_NONE; + stream->stream_user_data = stream_user_data; + stream->item = NULL; + stream->remote_window_size = remote_initial_window_size; + stream->local_window_size = local_initial_window_size; + stream->recv_window_size = 0; + stream->consumed_size = 0; + stream->recv_reduction = 0; + + stream->dep_prev = NULL; + stream->dep_next = NULL; + stream->sib_prev = NULL; + stream->sib_next = NULL; + + stream->closed_prev = NULL; + stream->closed_next = NULL; + + stream->dpri = NGHTTP2_STREAM_DPRI_NO_ITEM; + stream->num_substreams = 1; + stream->weight = weight; + stream->effective_weight = stream->weight; + stream->sum_dep_weight = 0; + stream->sum_norest_weight = 0; + + stream->roots = roots; + stream->root_prev = NULL; + stream->root_next = NULL; + + stream->http_flags = NGHTTP2_HTTP_FLAG_NONE; + stream->content_length = -1; + stream->recv_content_length = 0; + stream->status_code = -1; +} + +void nghttp2_stream_free(nghttp2_stream *stream _U_) { + /* We don't free stream->item. If it is assigned to aob, then + active_outbound_item_reset() will delete it. If it is queued, + then it is deleted when pq is deleted in nghttp2_session_del(). + Otherwise, nghttp2_session_del() will delete it. */ +} + +void nghttp2_stream_shutdown(nghttp2_stream *stream, nghttp2_shut_flag flag) { + stream->shut_flags |= flag; +} + +static int stream_push_item(nghttp2_stream *stream, nghttp2_session *session) { + /* This is required for Android NDK r10d */ + int rv = 0; + nghttp2_outbound_item *item; + + assert(stream->item); + assert(stream->item->queued == 0); + + item = stream->item; + + /* If item is now sent, don't push it to the queue. Otherwise, we + may push same item twice. */ + if (session->aob.item == item) { + return 0; + } + + switch (item->frame.hd.type) { + case NGHTTP2_DATA: + /* Penalize item by delaying scheduling according to effective + weight. This will delay low priority stream, which is good. + OTOH, this may incur delay for high priority item. Will + see. */ + item->cycle = + session->last_cycle + + NGHTTP2_DATA_PAYLOADLEN * NGHTTP2_MAX_WEIGHT / stream->effective_weight; + + rv = nghttp2_pq_push(&session->ob_da_pq, item); + if (rv != 0) { + return rv; + } + break; + case NGHTTP2_HEADERS: + if (stream->state == NGHTTP2_STREAM_RESERVED) { + nghttp2_outbound_queue_push(&session->ob_syn, item); + } else { + nghttp2_outbound_queue_push(&session->ob_reg, item); + } + break; + default: + /* should not reach here */ + assert(0); + } + + item->queued = 1; + + return 0; +} + +static nghttp2_stream *stream_first_sib(nghttp2_stream *stream) { + for (; stream->sib_prev; stream = stream->sib_prev) + ; + + return stream; +} + +static nghttp2_stream *stream_last_sib(nghttp2_stream *stream) { + for (; stream->sib_next; stream = stream->sib_next) + ; + + return stream; +} + +static nghttp2_stream *stream_update_dep_length(nghttp2_stream *stream, + ssize_t delta) { + stream->num_substreams += delta; + + stream = stream_first_sib(stream); + + if (stream->dep_prev) { + return stream_update_dep_length(stream->dep_prev, delta); + } + + return stream; +} + +int32_t nghttp2_stream_dep_distributed_weight(nghttp2_stream *stream, + int32_t weight) { + weight = stream->weight * weight / stream->sum_dep_weight; + + return nghttp2_max(1, weight); +} + +int32_t nghttp2_stream_dep_distributed_effective_weight(nghttp2_stream *stream, + int32_t weight) { + if (stream->sum_norest_weight == 0) { + return stream->effective_weight; + } + + weight = stream->effective_weight * weight / stream->sum_norest_weight; + + return nghttp2_max(1, weight); +} + +static void stream_update_dep_set_rest(nghttp2_stream *stream); + +/* Updates effective_weight of descendant streams in subtree of + |stream|. We assume that stream->effective_weight is already set + right. */ +static void stream_update_dep_effective_weight(nghttp2_stream *stream) { + nghttp2_stream *si; + + DEBUGF(fprintf(stderr, "stream: update_dep_effective_weight " + "stream(%p)=%d, weight=%d, sum_norest_weight=%d\n", + stream, stream->stream_id, stream->weight, + stream->sum_norest_weight)); + + /* stream->sum_norest_weight == 0 means there is no + NGHTTP2_STREAM_DPRI_TOP under stream */ + if (stream->dpri != NGHTTP2_STREAM_DPRI_NO_ITEM || + stream->sum_norest_weight == 0) { + return; + } + + for (si = stream->dep_next; si; si = si->sib_next) { + if (si->dpri != NGHTTP2_STREAM_DPRI_REST) { + si->effective_weight = + nghttp2_stream_dep_distributed_effective_weight(stream, si->weight); + } + + stream_update_dep_effective_weight(si); + } +} + +static void stream_update_dep_set_rest(nghttp2_stream *stream) { + if (stream == NULL) { + return; + } + + DEBUGF(fprintf(stderr, "stream: stream=%d is rest\n", stream->stream_id)); + + if (stream->dpri == NGHTTP2_STREAM_DPRI_REST) { + return; + } + + if (stream->dpri == NGHTTP2_STREAM_DPRI_TOP) { + stream->dpri = NGHTTP2_STREAM_DPRI_REST; + + stream_update_dep_set_rest(stream->sib_next); + + return; + } + + stream_update_dep_set_rest(stream->sib_next); + stream_update_dep_set_rest(stream->dep_next); +} + +/* + * Performs dfs starting |stream|, search stream which can become + * NGHTTP2_STREAM_DPRI_TOP and set its dpri. + */ +static void stream_update_dep_set_top(nghttp2_stream *stream) { + nghttp2_stream *si; + + if (stream->dpri == NGHTTP2_STREAM_DPRI_TOP) { + return; + } + + if (stream->dpri == NGHTTP2_STREAM_DPRI_REST) { + DEBUGF( + fprintf(stderr, "stream: stream=%d item is top\n", stream->stream_id)); + + stream->dpri = NGHTTP2_STREAM_DPRI_TOP; + + return; + } + + for (si = stream->dep_next; si; si = si->sib_next) { + stream_update_dep_set_top(si); + } +} + +/* + * Performs dfs starting |stream|, and dueue stream whose dpri is + * NGHTTP2_STREAM_DPRI_TOP and has not been queued yet. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + */ +static int stream_update_dep_queue_top(nghttp2_stream *stream, + nghttp2_session *session) { + int rv; + nghttp2_stream *si; + + if (stream->dpri == NGHTTP2_STREAM_DPRI_REST) { + return 0; + } + + if (stream->dpri == NGHTTP2_STREAM_DPRI_TOP) { + if (!stream->item->queued) { + DEBUGF(fprintf(stderr, "stream: stream=%d enqueue\n", stream->stream_id)); + rv = stream_push_item(stream, session); + + if (rv != 0) { + return rv; + } + } + + return 0; + } + + for (si = stream->dep_next; si; si = si->sib_next) { + rv = stream_update_dep_queue_top(si, session); + + if (rv != 0) { + return rv; + } + } + + return 0; +} + +/* + * Updates stream->sum_norest_weight recursively. We have to gather + * effective sum of weight of descendants. If stream->dpri == + * NGHTTP2_STREAM_DPRI_NO_ITEM, we have to go deeper and check that + * any of its descendants has dpri value of NGHTTP2_STREAM_DPRI_TOP. + * If so, we have to add weight of its direct descendants to + * stream->sum_norest_weight. To make this work, this function + * returns 1 if any of its descendants has dpri value of + * NGHTTP2_STREAM_DPRI_TOP, otherwise 0. + */ +static int stream_update_dep_sum_norest_weight(nghttp2_stream *stream) { + nghttp2_stream *si; + int rv; + + stream->sum_norest_weight = 0; + + if (stream->dpri == NGHTTP2_STREAM_DPRI_TOP) { + return 1; + } + + if (stream->dpri == NGHTTP2_STREAM_DPRI_REST) { + return 0; + } + + rv = 0; + + for (si = stream->dep_next; si; si = si->sib_next) { + + if (stream_update_dep_sum_norest_weight(si)) { + rv = 1; + stream->sum_norest_weight += si->weight; + } + } + + return rv; +} + +static int stream_update_dep_on_attach_item(nghttp2_stream *stream, + nghttp2_session *session) { + nghttp2_stream *root_stream; + + stream->dpri = NGHTTP2_STREAM_DPRI_REST; + + stream_update_dep_set_rest(stream->dep_next); + + root_stream = nghttp2_stream_get_dep_root(stream); + + DEBUGF(fprintf(stderr, "root=%p, stream=%p\n", root_stream, stream)); + + stream_update_dep_set_top(root_stream); + + stream_update_dep_sum_norest_weight(root_stream); + stream_update_dep_effective_weight(root_stream); + + return stream_update_dep_queue_top(root_stream, session); +} + +static int stream_update_dep_on_detach_item(nghttp2_stream *stream, + nghttp2_session *session) { + nghttp2_stream *root_stream; + + stream->dpri = NGHTTP2_STREAM_DPRI_NO_ITEM; + + root_stream = nghttp2_stream_get_dep_root(stream); + + stream_update_dep_set_top(root_stream); + + stream_update_dep_sum_norest_weight(root_stream); + stream_update_dep_effective_weight(root_stream); + + return stream_update_dep_queue_top(root_stream, session); +} + +int nghttp2_stream_attach_item(nghttp2_stream *stream, + nghttp2_outbound_item *item, + nghttp2_session *session) { + assert((stream->flags & NGHTTP2_STREAM_FLAG_DEFERRED_ALL) == 0); + assert(stream->item == NULL); + + DEBUGF(fprintf(stderr, "stream: stream=%d attach item=%p\n", + stream->stream_id, item)); + + stream->item = item; + + return stream_update_dep_on_attach_item(stream, session); +} + +int nghttp2_stream_detach_item(nghttp2_stream *stream, + nghttp2_session *session) { + DEBUGF(fprintf(stderr, "stream: stream=%d detach item=%p\n", + stream->stream_id, stream->item)); + + stream->item = NULL; + stream->flags &= ~NGHTTP2_STREAM_FLAG_DEFERRED_ALL; + + return stream_update_dep_on_detach_item(stream, session); +} + +int nghttp2_stream_defer_item(nghttp2_stream *stream, uint8_t flags, + nghttp2_session *session) { + assert(stream->item); + + DEBUGF(fprintf(stderr, "stream: stream=%d defer item=%p cause=%02x\n", + stream->stream_id, stream->item, flags)); + + stream->flags |= flags; + + return stream_update_dep_on_detach_item(stream, session); +} + +int nghttp2_stream_resume_deferred_item(nghttp2_stream *stream, uint8_t flags, + nghttp2_session *session) { + assert(stream->item); + + DEBUGF(fprintf(stderr, "stream: stream=%d resume item=%p flags=%02x\n", + stream->stream_id, stream->item, flags)); + + stream->flags &= ~flags; + + if (stream->flags & NGHTTP2_STREAM_FLAG_DEFERRED_ALL) { + return 0; + } + + return stream_update_dep_on_attach_item(stream, session); +} + +int nghttp2_stream_check_deferred_item(nghttp2_stream *stream) { + return stream->item && (stream->flags & NGHTTP2_STREAM_FLAG_DEFERRED_ALL); +} + +int nghttp2_stream_check_deferred_by_flow_control(nghttp2_stream *stream) { + return stream->item && + (stream->flags & NGHTTP2_STREAM_FLAG_DEFERRED_FLOW_CONTROL); +} + +static int update_initial_window_size(int32_t *window_size_ptr, + int32_t new_initial_window_size, + int32_t old_initial_window_size) { + int64_t new_window_size = (int64_t)(*window_size_ptr) + + new_initial_window_size - old_initial_window_size; + if (INT32_MIN > new_window_size || + new_window_size > NGHTTP2_MAX_WINDOW_SIZE) { + return -1; + } + *window_size_ptr = (int32_t)new_window_size; + return 0; +} + +int nghttp2_stream_update_remote_initial_window_size( + nghttp2_stream *stream, int32_t new_initial_window_size, + int32_t old_initial_window_size) { + return update_initial_window_size(&stream->remote_window_size, + new_initial_window_size, + old_initial_window_size); +} + +int nghttp2_stream_update_local_initial_window_size( + nghttp2_stream *stream, int32_t new_initial_window_size, + int32_t old_initial_window_size) { + return update_initial_window_size(&stream->local_window_size, + new_initial_window_size, + old_initial_window_size); +} + +void nghttp2_stream_promise_fulfilled(nghttp2_stream *stream) { + stream->state = NGHTTP2_STREAM_OPENED; + stream->flags &= ~NGHTTP2_STREAM_FLAG_PUSH; +} + +nghttp2_stream *nghttp2_stream_get_dep_root(nghttp2_stream *stream) { + for (;;) { + if (stream->sib_prev) { + stream = stream->sib_prev; + + continue; + } + + if (stream->dep_prev) { + stream = stream->dep_prev; + + continue; + } + + break; + } + + return stream; +} + +int nghttp2_stream_dep_subtree_find(nghttp2_stream *stream, + nghttp2_stream *target) { + if (stream == NULL) { + return 0; + } + + if (stream == target) { + return 1; + } + + if (nghttp2_stream_dep_subtree_find(stream->sib_next, target)) { + return 1; + } + + return nghttp2_stream_dep_subtree_find(stream->dep_next, target); +} + +void nghttp2_stream_dep_insert(nghttp2_stream *dep_stream, + nghttp2_stream *stream) { + nghttp2_stream *si; + nghttp2_stream *root_stream; + + assert(stream->item == NULL); + + DEBUGF(fprintf(stderr, + "stream: dep_insert dep_stream(%p)=%d, stream(%p)=%d\n", + dep_stream, dep_stream->stream_id, stream, stream->stream_id)); + + stream->sum_dep_weight = dep_stream->sum_dep_weight; + dep_stream->sum_dep_weight = stream->weight; + + if (dep_stream->dep_next) { + for (si = dep_stream->dep_next; si; si = si->sib_next) { + stream->num_substreams += si->num_substreams; + } + + stream->dep_next = dep_stream->dep_next; + stream->dep_next->dep_prev = stream; + } + + dep_stream->dep_next = stream; + stream->dep_prev = dep_stream; + + root_stream = stream_update_dep_length(dep_stream, 1); + + stream_update_dep_sum_norest_weight(root_stream); + stream_update_dep_effective_weight(root_stream); + + ++stream->roots->num_streams; +} + +static void link_dep(nghttp2_stream *dep_stream, nghttp2_stream *stream) { + dep_stream->dep_next = stream; + stream->dep_prev = dep_stream; +} + +static void link_sib(nghttp2_stream *prev_stream, nghttp2_stream *stream) { + prev_stream->sib_next = stream; + stream->sib_prev = prev_stream; +} + +static void insert_link_dep(nghttp2_stream *dep_stream, + nghttp2_stream *stream) { + nghttp2_stream *sib_next; + + assert(stream->sib_prev == NULL); + + sib_next = dep_stream->dep_next; + + link_sib(stream, sib_next); + + sib_next->dep_prev = NULL; + + link_dep(dep_stream, stream); +} + +static void unlink_sib(nghttp2_stream *stream) { + nghttp2_stream *prev, *next, *dep_next; + + prev = stream->sib_prev; + dep_next = stream->dep_next; + + assert(prev); + + if (dep_next) { + /* + * prev--stream(--sib_next--...) + * | + * dep_next + */ + dep_next->dep_prev = NULL; + + link_sib(prev, dep_next); + + if (stream->sib_next) { + link_sib(stream_last_sib(dep_next), stream->sib_next); + } + } else { + /* + * prev--stream(--sib_next--...) + */ + next = stream->sib_next; + + prev->sib_next = next; + + if (next) { + next->sib_prev = prev; + } + } +} + +static void unlink_dep(nghttp2_stream *stream) { + nghttp2_stream *prev, *next, *dep_next; + + prev = stream->dep_prev; + dep_next = stream->dep_next; + + assert(prev); + + if (dep_next) { + /* + * prev + * | + * stream(--sib_next--...) + * | + * dep_next + */ + link_dep(prev, dep_next); + + if (stream->sib_next) { + link_sib(stream_last_sib(dep_next), stream->sib_next); + } + } else if (stream->sib_next) { + /* + * prev + * | + * stream--sib_next + */ + next = stream->sib_next; + + next->sib_prev = NULL; + + link_dep(prev, next); + } else { + prev->dep_next = NULL; + } +} + +void nghttp2_stream_dep_add(nghttp2_stream *dep_stream, + nghttp2_stream *stream) { + nghttp2_stream *root_stream; + + assert(stream->item == NULL); + + DEBUGF(fprintf(stderr, "stream: dep_add dep_stream(%p)=%d, stream(%p)=%d\n", + dep_stream, dep_stream->stream_id, stream, stream->stream_id)); + + root_stream = stream_update_dep_length(dep_stream, 1); + + dep_stream->sum_dep_weight += stream->weight; + + if (dep_stream->dep_next == NULL) { + link_dep(dep_stream, stream); + } else { + insert_link_dep(dep_stream, stream); + } + + stream_update_dep_sum_norest_weight(root_stream); + stream_update_dep_effective_weight(root_stream); + + ++stream->roots->num_streams; +} + +void nghttp2_stream_dep_remove(nghttp2_stream *stream) { + nghttp2_stream *prev, *next, *dep_prev, *si, *root_stream; + int32_t sum_dep_weight_delta; + + root_stream = NULL; + + DEBUGF(fprintf(stderr, "stream: dep_remove stream(%p)=%d\n", stream, + stream->stream_id)); + + /* Distribute weight of |stream| to direct descendants */ + sum_dep_weight_delta = -stream->weight; + + for (si = stream->dep_next; si; si = si->sib_next) { + si->weight = nghttp2_stream_dep_distributed_weight(stream, si->weight); + + sum_dep_weight_delta += si->weight; + } + + prev = stream_first_sib(stream); + + dep_prev = prev->dep_prev; + + if (dep_prev) { + root_stream = stream_update_dep_length(dep_prev, -1); + + dep_prev->sum_dep_weight += sum_dep_weight_delta; + } + + if (stream->sib_prev) { + unlink_sib(stream); + } else if (stream->dep_prev) { + unlink_dep(stream); + } else { + nghttp2_stream_roots_remove(stream->roots, stream); + + /* stream is a root of tree. Removing stream makes its + descendants a root of its own subtree. */ + + for (si = stream->dep_next; si;) { + next = si->sib_next; + + si->dep_prev = NULL; + si->sib_prev = NULL; + si->sib_next = NULL; + + /* We already distributed weight of |stream| to this. */ + si->effective_weight = si->weight; + + nghttp2_stream_roots_add(si->roots, si); + + si = next; + } + } + + if (root_stream) { + stream_update_dep_sum_norest_weight(root_stream); + stream_update_dep_effective_weight(root_stream); + } + + stream->num_substreams = 1; + stream->sum_dep_weight = 0; + + stream->dep_prev = NULL; + stream->dep_next = NULL; + stream->sib_prev = NULL; + stream->sib_next = NULL; + + --stream->roots->num_streams; +} + +int nghttp2_stream_dep_insert_subtree(nghttp2_stream *dep_stream, + nghttp2_stream *stream, + nghttp2_session *session) { + nghttp2_stream *last_sib; + nghttp2_stream *dep_next; + nghttp2_stream *root_stream; + size_t delta_substreams; + + DEBUGF(fprintf(stderr, "stream: dep_insert_subtree dep_stream(%p)=%d " + "stream(%p)=%d\n", + dep_stream, dep_stream->stream_id, stream, stream->stream_id)); + + delta_substreams = stream->num_substreams; + + stream_update_dep_set_rest(stream); + + if (dep_stream->dep_next) { + /* dep_stream->num_substreams includes dep_stream itself */ + stream->num_substreams += dep_stream->num_substreams - 1; + + stream->sum_dep_weight += dep_stream->sum_dep_weight; + dep_stream->sum_dep_weight = stream->weight; + + dep_next = dep_stream->dep_next; + + stream_update_dep_set_rest(dep_next); + + link_dep(dep_stream, stream); + + if (stream->dep_next) { + last_sib = stream_last_sib(stream->dep_next); + + link_sib(last_sib, dep_next); + + dep_next->dep_prev = NULL; + } else { + link_dep(stream, dep_next); + } + } else { + link_dep(dep_stream, stream); + + assert(dep_stream->sum_dep_weight == 0); + dep_stream->sum_dep_weight = stream->weight; + } + + root_stream = stream_update_dep_length(dep_stream, delta_substreams); + + stream_update_dep_set_top(root_stream); + + stream_update_dep_sum_norest_weight(root_stream); + stream_update_dep_effective_weight(root_stream); + + return stream_update_dep_queue_top(root_stream, session); +} + +int nghttp2_stream_dep_add_subtree(nghttp2_stream *dep_stream, + nghttp2_stream *stream, + nghttp2_session *session) { + nghttp2_stream *root_stream; + + DEBUGF(fprintf(stderr, "stream: dep_add_subtree dep_stream(%p)=%d " + "stream(%p)=%d\n", + dep_stream, dep_stream->stream_id, stream, stream->stream_id)); + + stream_update_dep_set_rest(stream); + + if (dep_stream->dep_next) { + dep_stream->sum_dep_weight += stream->weight; + + insert_link_dep(dep_stream, stream); + } else { + link_dep(dep_stream, stream); + + assert(dep_stream->sum_dep_weight == 0); + dep_stream->sum_dep_weight = stream->weight; + } + + root_stream = stream_update_dep_length(dep_stream, stream->num_substreams); + + stream_update_dep_set_top(root_stream); + + stream_update_dep_sum_norest_weight(root_stream); + stream_update_dep_effective_weight(root_stream); + + return stream_update_dep_queue_top(root_stream, session); +} + +void nghttp2_stream_dep_remove_subtree(nghttp2_stream *stream) { + nghttp2_stream *prev, *next, *dep_prev, *root_stream; + + DEBUGF(fprintf(stderr, "stream: dep_remove_subtree stream(%p)=%d\n", stream, + stream->stream_id)); + + if (stream->sib_prev) { + prev = stream->sib_prev; + + prev->sib_next = stream->sib_next; + if (prev->sib_next) { + prev->sib_next->sib_prev = prev; + } + + prev = stream_first_sib(prev); + + dep_prev = prev->dep_prev; + + } else if (stream->dep_prev) { + dep_prev = stream->dep_prev; + next = stream->sib_next; + + dep_prev->dep_next = next; + + if (next) { + next->dep_prev = dep_prev; + + next->sib_prev = NULL; + } + + } else { + nghttp2_stream_roots_remove(stream->roots, stream); + + dep_prev = NULL; + } + + if (dep_prev) { + dep_prev->sum_dep_weight -= stream->weight; + + root_stream = stream_update_dep_length(dep_prev, -stream->num_substreams); + + stream_update_dep_sum_norest_weight(root_stream); + stream_update_dep_effective_weight(root_stream); + } + + stream->sib_prev = NULL; + stream->sib_next = NULL; + stream->dep_prev = NULL; +} + +int nghttp2_stream_dep_make_root(nghttp2_stream *stream, + nghttp2_session *session) { + DEBUGF(fprintf(stderr, "stream: dep_make_root stream(%p)=%d\n", stream, + stream->stream_id)); + + nghttp2_stream_roots_add(stream->roots, stream); + + stream_update_dep_set_rest(stream); + + stream->effective_weight = stream->weight; + + stream_update_dep_set_top(stream); + + stream_update_dep_sum_norest_weight(stream); + stream_update_dep_effective_weight(stream); + + return stream_update_dep_queue_top(stream, session); +} + +int +nghttp2_stream_dep_all_your_stream_are_belong_to_us(nghttp2_stream *stream, + nghttp2_session *session) { + nghttp2_stream *first, *si; + + DEBUGF(fprintf(stderr, "stream: ALL YOUR STREAM ARE BELONG TO US " + "stream(%p)=%d\n", + stream, stream->stream_id)); + + first = stream->roots->head; + + /* stream must not be include in stream->roots->head list */ + assert(first != stream); + + if (first) { + nghttp2_stream *prev; + + prev = first; + + DEBUGF(fprintf(stderr, "stream: root stream(%p)=%d\n", first, + first->stream_id)); + + stream->sum_dep_weight += first->weight; + stream->num_substreams += first->num_substreams; + + for (si = first->root_next; si; si = si->root_next) { + + assert(si != stream); + + DEBUGF( + fprintf(stderr, "stream: root stream(%p)=%d\n", si, si->stream_id)); + + stream->sum_dep_weight += si->weight; + stream->num_substreams += si->num_substreams; + + link_sib(prev, si); + + prev = si; + } + + if (stream->dep_next) { + nghttp2_stream *sib_next; + + sib_next = stream->dep_next; + + sib_next->dep_prev = NULL; + + link_sib(first, sib_next); + link_dep(stream, prev); + } else { + link_dep(stream, first); + } + } + + nghttp2_stream_roots_remove_all(stream->roots); + + return nghttp2_stream_dep_make_root(stream, session); +} + +int nghttp2_stream_in_dep_tree(nghttp2_stream *stream) { + return stream->dep_prev || stream->dep_next || stream->sib_prev || + stream->sib_next || stream->root_next || stream->root_prev || + stream->roots->head == stream; +} + +void nghttp2_stream_roots_init(nghttp2_stream_roots *roots) { + roots->head = NULL; + roots->num_streams = 0; +} + +void nghttp2_stream_roots_free(nghttp2_stream_roots *roots _U_) {} + +void nghttp2_stream_roots_add(nghttp2_stream_roots *roots, + nghttp2_stream *stream) { + if (roots->head) { + stream->root_next = roots->head; + roots->head->root_prev = stream; + } + + roots->head = stream; +} + +void nghttp2_stream_roots_remove(nghttp2_stream_roots *roots, + nghttp2_stream *stream) { + nghttp2_stream *root_prev, *root_next; + + root_prev = stream->root_prev; + root_next = stream->root_next; + + if (root_prev) { + root_prev->root_next = root_next; + + if (root_next) { + root_next->root_prev = root_prev; + } + } else { + if (root_next) { + root_next->root_prev = NULL; + } + + roots->head = root_next; + } + + stream->root_prev = NULL; + stream->root_next = NULL; +} + +void nghttp2_stream_roots_remove_all(nghttp2_stream_roots *roots) { + nghttp2_stream *si, *next; + + for (si = roots->head; si;) { + next = si->root_next; + + si->root_prev = NULL; + si->root_next = NULL; + + si = next; + } + + roots->head = NULL; +} diff --git a/lib/nghttp2_stream.h b/lib/nghttp2_stream.h new file mode 100644 index 0000000..684459b --- /dev/null +++ b/lib/nghttp2_stream.h @@ -0,0 +1,486 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGHTTP2_STREAM_H +#define NGHTTP2_STREAM_H + +#ifdef HAVE_CONFIG_H +#include +#endif /* HAVE_CONFIG_H */ + +#include +#include "nghttp2_outbound_item.h" +#include "nghttp2_map.h" +#include "nghttp2_pq.h" +#include "nghttp2_int.h" + +/* + * Maximum number of streams in one dependency tree. + */ +#define NGHTTP2_MAX_DEP_TREE_LENGTH 100 + +/* + * If local peer is stream initiator: + * NGHTTP2_STREAM_OPENING : upon sending request HEADERS + * NGHTTP2_STREAM_OPENED : upon receiving response HEADERS + * NGHTTP2_STREAM_CLOSING : upon queuing RST_STREAM + * + * If remote peer is stream initiator: + * NGHTTP2_STREAM_OPENING : upon receiving request HEADERS + * NGHTTP2_STREAM_OPENED : upon sending response HEADERS + * NGHTTP2_STREAM_CLOSING : upon queuing RST_STREAM + */ +typedef enum { + /* Initial state */ + NGHTTP2_STREAM_INITIAL, + /* For stream initiator: request HEADERS has been sent, but response + HEADERS has not been received yet. For receiver: request HEADERS + has been received, but it does not send response HEADERS yet. */ + NGHTTP2_STREAM_OPENING, + /* For stream initiator: response HEADERS is received. For receiver: + response HEADERS is sent. */ + NGHTTP2_STREAM_OPENED, + /* RST_STREAM is received, but somehow we need to keep stream in + memory. */ + NGHTTP2_STREAM_CLOSING, + /* PUSH_PROMISE is received or sent */ + NGHTTP2_STREAM_RESERVED, + /* Stream is created in this state if it is used as anchor in + dependency tree. */ + NGHTTP2_STREAM_IDLE +} nghttp2_stream_state; + +typedef enum { + NGHTTP2_SHUT_NONE = 0, + /* Indicates further receptions will be disallowed. */ + NGHTTP2_SHUT_RD = 0x01, + /* Indicates further transmissions will be disallowed. */ + NGHTTP2_SHUT_WR = 0x02, + /* Indicates both further receptions and transmissions will be + disallowed. */ + NGHTTP2_SHUT_RDWR = NGHTTP2_SHUT_RD | NGHTTP2_SHUT_WR +} nghttp2_shut_flag; + +typedef enum { + NGHTTP2_STREAM_FLAG_NONE = 0, + /* Indicates that this stream is pushed stream and not opened + yet. */ + NGHTTP2_STREAM_FLAG_PUSH = 0x01, + /* Indicates that this stream was closed */ + NGHTTP2_STREAM_FLAG_CLOSED = 0x02, + /* Indicates the item is deferred due to flow control. */ + NGHTTP2_STREAM_FLAG_DEFERRED_FLOW_CONTROL = 0x04, + /* Indicates the item is deferred by user callback */ + NGHTTP2_STREAM_FLAG_DEFERRED_USER = 0x08, + /* bitwise OR of NGHTTP2_STREAM_FLAG_DEFERRED_FLOW_CONTROL and + NGHTTP2_STREAM_FLAG_DEFERRED_USER. */ + NGHTTP2_STREAM_FLAG_DEFERRED_ALL = 0x0c + +} nghttp2_stream_flag; + +/* HTTP related flags to enforce HTTP semantics */ +typedef enum { + NGHTTP2_HTTP_FLAG_NONE = 0, + /* header field seen so far */ + NGHTTP2_HTTP_FLAG__AUTHORITY = 1, + NGHTTP2_HTTP_FLAG__PATH = 1 << 1, + NGHTTP2_HTTP_FLAG__METHOD = 1 << 2, + NGHTTP2_HTTP_FLAG__SCHEME = 1 << 3, + /* host is not pseudo header, but we require either host or + :authority */ + NGHTTP2_HTTP_FLAG_HOST = 1 << 4, + NGHTTP2_HTTP_FLAG__STATUS = 1 << 5, + /* required header fields for HTTP request except for CONNECT + method. */ + NGHTTP2_HTTP_FLAG_REQ_HEADERS = NGHTTP2_HTTP_FLAG__METHOD | + NGHTTP2_HTTP_FLAG__PATH | + NGHTTP2_HTTP_FLAG__SCHEME, + NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED = 1 << 6, + /* HTTP method flags */ + NGHTTP2_HTTP_FLAG_METH_CONNECT = 1 << 7, + NGHTTP2_HTTP_FLAG_METH_HEAD = 1 << 8, + NGHTTP2_HTTP_FLAG_METH_OPTIONS = 1 << 9, + NGHTTP2_HTTP_FLAG_METH_ALL = NGHTTP2_HTTP_FLAG_METH_CONNECT | + NGHTTP2_HTTP_FLAG_METH_HEAD | + NGHTTP2_HTTP_FLAG_METH_OPTIONS, + /* :path category */ + /* path starts with "/" */ + NGHTTP2_HTTP_FLAG_PATH_REGULAR = 1 << 10, + /* path "*" */ + NGHTTP2_HTTP_FLAG_PATH_ASTERISK = 1 << 11, + /* scheme */ + /* "http" or "https" scheme */ + NGHTTP2_HTTP_FLAG_SCHEME_HTTP = 1 << 12, + /* set if final response is expected */ + NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE = 1 << 13 +} nghttp2_http_flag; + +typedef enum { + NGHTTP2_STREAM_DPRI_NONE = 0, + NGHTTP2_STREAM_DPRI_NO_ITEM = 0x01, + NGHTTP2_STREAM_DPRI_TOP = 0x02, + NGHTTP2_STREAM_DPRI_REST = 0x04 +} nghttp2_stream_dpri; + +struct nghttp2_stream_roots; + +typedef struct nghttp2_stream_roots nghttp2_stream_roots; + +struct nghttp2_stream; + +typedef struct nghttp2_stream nghttp2_stream; + +struct nghttp2_stream { + /* Intrusive Map */ + nghttp2_map_entry map_entry; + /* Content-Length of request/response body. -1 if unknown. */ + int64_t content_length; + /* Received body so far */ + int64_t recv_content_length; + /* pointers to form dependency tree. If multiple streams depend on + a stream, only one stream (left most) has non-NULL dep_prev which + points to the stream it depends on. The remaining streams are + linked using sib_prev and sib_next. The stream which has + non-NULL dep_prev always NULL sib_prev. The right most stream + has NULL sib_next. If this stream is a root of dependency tree, + dep_prev and sib_prev are NULL. */ + nghttp2_stream *dep_prev, *dep_next; + nghttp2_stream *sib_prev, *sib_next; + /* pointers to track dependency tree root streams. This is + doubly-linked list and first element is pointed by + roots->head. */ + nghttp2_stream *root_prev, *root_next; + /* When stream is kept after closure, it may be kept in doubly + linked list pointed by nghttp2_session closed_stream_head. + closed_next points to the next stream object if it is the element + of the list. */ + nghttp2_stream *closed_prev, *closed_next; + /* pointer to roots, which tracks dependency tree roots */ + nghttp2_stream_roots *roots; + /* The arbitrary data provided by user for this stream. */ + void *stream_user_data; + /* Item to send */ + nghttp2_outbound_item *item; + /* stream ID */ + int32_t stream_id; + /* categorized priority of this stream. Only stream bearing + NGHTTP2_STREAM_DPRI_TOP can send item. */ + nghttp2_stream_dpri dpri; + /* the number of streams in subtree */ + size_t num_substreams; + /* Current remote window size. This value is computed against the + current initial window size of remote endpoint. */ + int32_t remote_window_size; + /* Keep track of the number of bytes received without + WINDOW_UPDATE. This could be negative after submitting negative + value to WINDOW_UPDATE */ + int32_t recv_window_size; + /* The number of bytes consumed by the application and now is + subject to WINDOW_UPDATE. This is only used when auto + WINDOW_UPDATE is turned off. */ + int32_t consumed_size; + /* The amount of recv_window_size cut using submitting negative + value to WINDOW_UPDATE */ + int32_t recv_reduction; + /* window size for local flow control. It is initially set to + NGHTTP2_INITIAL_WINDOW_SIZE and could be increased/decreased by + submitting WINDOW_UPDATE. See nghttp2_submit_window_update(). */ + int32_t local_window_size; + /* weight of this stream */ + int32_t weight; + /* effective weight of this stream in belonging dependency tree */ + int32_t effective_weight; + /* sum of weight (not effective_weight) of direct descendants */ + int32_t sum_dep_weight; + /* sum of weight of direct descendants which have at least one + descendant with dpri == NGHTTP2_STREAM_DPRI_TOP. We use this + value to calculate effective weight. */ + int32_t sum_norest_weight; + nghttp2_stream_state state; + /* status code from remote server */ + int16_t status_code; + /* Bitwise OR of zero or more nghttp2_http_flag values */ + uint16_t http_flags; + /* This is bitwise-OR of 0 or more of nghttp2_stream_flag. */ + uint8_t flags; + /* Bitwise OR of zero or more nghttp2_shut_flag values */ + uint8_t shut_flags; +}; + +void nghttp2_stream_init(nghttp2_stream *stream, int32_t stream_id, + uint8_t flags, nghttp2_stream_state initial_state, + int32_t weight, nghttp2_stream_roots *roots, + int32_t remote_initial_window_size, + int32_t local_initial_window_size, + void *stream_user_data); + +void nghttp2_stream_free(nghttp2_stream *stream); + +/* + * Disallow either further receptions or transmissions, or both. + * |flag| is bitwise OR of one or more of nghttp2_shut_flag. + */ +void nghttp2_stream_shutdown(nghttp2_stream *stream, nghttp2_shut_flag flag); + +/* + * Defer |stream->item|. We won't call this function in the situation + * where |stream->item| == NULL. The |flags| is bitwise OR of zero or + * more of NGHTTP2_STREAM_FLAG_DEFERRED_USER and + * NGHTTP2_STREAM_FLAG_DEFERRED_FLOW_CONTROL. The |flags| indicates + * the reason of this action. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory + */ +int nghttp2_stream_defer_item(nghttp2_stream *stream, uint8_t flags, + nghttp2_session *session); + +/* + * Put back deferred data in this stream to active state. The |flags| + * are one or more of bitwise OR of the following values: + * NGHTTP2_STREAM_FLAG_DEFERRED_USER and + * NGHTTP2_STREAM_FLAG_DEFERRED_FLOW_CONTROL and given masks are + * cleared if they are set. So even if this function is called, if + * one of flag is still set, data does not become active. + */ +int nghttp2_stream_resume_deferred_item(nghttp2_stream *stream, uint8_t flags, + nghttp2_session *session); + +/* + * Returns nonzero if item is deferred by whatever reason. + */ +int nghttp2_stream_check_deferred_item(nghttp2_stream *stream); + +/* + * Returns nonzero if item is deferred by flow control. + */ +int nghttp2_stream_check_deferred_by_flow_control(nghttp2_stream *stream); + +/* + * Updates the remote window size with the new value + * |new_initial_window_size|. The |old_initial_window_size| is used to + * calculate the current window size. + * + * This function returns 0 if it succeeds or -1. The failure is due to + * overflow. + */ +int nghttp2_stream_update_remote_initial_window_size( + nghttp2_stream *stream, int32_t new_initial_window_size, + int32_t old_initial_window_size); + +/* + * Updates the local window size with the new value + * |new_initial_window_size|. The |old_initial_window_size| is used to + * calculate the current window size. + * + * This function returns 0 if it succeeds or -1. The failure is due to + * overflow. + */ +int nghttp2_stream_update_local_initial_window_size( + nghttp2_stream *stream, int32_t new_initial_window_size, + int32_t old_initial_window_size); + +/* + * Call this function if promised stream |stream| is replied with + * HEADERS. This function makes the state of the |stream| to + * NGHTTP2_STREAM_OPENED. + */ +void nghttp2_stream_promise_fulfilled(nghttp2_stream *stream); + +/* + * Returns the stream positioned in root of the dependency tree the + * |stream| belongs to. + */ +nghttp2_stream *nghttp2_stream_get_dep_root(nghttp2_stream *stream); + +/* + * Returns nonzero if |target| is found in subtree of |stream|. + */ +int nghttp2_stream_dep_subtree_find(nghttp2_stream *stream, + nghttp2_stream *target); + +/* + * Computes distributed weight of a stream of the |weight| under the + * |stream| if |stream| is removed from a dependency tree. The result + * is computed using stream->weight rather than + * stream->effective_weight. + */ +int32_t nghttp2_stream_dep_distributed_weight(nghttp2_stream *stream, + int32_t weight); + +/* + * Computes effective weight of a stream of the |weight| under the + * |stream|. The result is computed using stream->effective_weight + * rather than stream->weight. This function is used to determine + * weight in dependency tree. + */ +int32_t nghttp2_stream_dep_distributed_effective_weight(nghttp2_stream *stream, + int32_t weight); + +/* + * Makes the |stream| depend on the |dep_stream|. This dependency is + * exclusive. All existing direct descendants of |dep_stream| become + * the descendants of the |stream|. This function assumes + * |stream->data| is NULL and no dpri members are changed in this + * dependency tree. + */ +void nghttp2_stream_dep_insert(nghttp2_stream *dep_stream, + nghttp2_stream *stream); + +/* + * Makes the |stream| depend on the |dep_stream|. This dependency is + * not exclusive. This function assumes |stream->data| is NULL and no + * dpri members are changed in this dependency tree. + */ +void nghttp2_stream_dep_add(nghttp2_stream *dep_stream, nghttp2_stream *stream); + +/* + * Removes the |stream| from the current dependency tree. This + * function assumes |stream->data| is NULL. + */ +void nghttp2_stream_dep_remove(nghttp2_stream *stream); + +/* + * Attaches |item| to |stream|. Updates dpri members in this + * dependency tree. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory + */ +int nghttp2_stream_attach_item(nghttp2_stream *stream, + nghttp2_outbound_item *item, + nghttp2_session *session); + +/* + * Detaches |stream->item|. Updates dpri members in this dependency + * tree. This function does not free |stream->item|. The caller must + * free it. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory + */ +int nghttp2_stream_detach_item(nghttp2_stream *stream, + nghttp2_session *session); + +/* + * Makes the |stream| depend on the |dep_stream|. This dependency is + * exclusive. Updates dpri members in this dependency tree. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory + */ +int nghttp2_stream_dep_insert_subtree(nghttp2_stream *dep_stream, + nghttp2_stream *stream, + nghttp2_session *session); + +/* + * Makes the |stream| depend on the |dep_stream|. This dependency is + * not exclusive. Updates dpri members in this dependency tree. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory + */ +int nghttp2_stream_dep_add_subtree(nghttp2_stream *dep_stream, + nghttp2_stream *stream, + nghttp2_session *session); + +/* + * Removes subtree whose root stream is |stream|. Removing subtree + * does not change dpri values. The effective_weight of streams in + * removed subtree is not updated. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory + */ +void nghttp2_stream_dep_remove_subtree(nghttp2_stream *stream); + +/* + * Makes the |stream| as root. Updates dpri members in this + * dependency tree. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory + */ +int nghttp2_stream_dep_make_root(nghttp2_stream *stream, + nghttp2_session *session); + +/* + * Makes the |stream| as root and all existing root streams become + * direct children of |stream|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory + */ +int +nghttp2_stream_dep_all_your_stream_are_belong_to_us(nghttp2_stream *stream, + nghttp2_session *session); + +/* + * Returns nonzero if |stream| is in any dependency tree. + */ +int nghttp2_stream_in_dep_tree(nghttp2_stream *stream); + +struct nghttp2_stream_roots { + nghttp2_stream *head; + + int32_t num_streams; +}; + +void nghttp2_stream_roots_init(nghttp2_stream_roots *roots); + +void nghttp2_stream_roots_free(nghttp2_stream_roots *roots); + +void nghttp2_stream_roots_add(nghttp2_stream_roots *roots, + nghttp2_stream *stream); + +void nghttp2_stream_roots_remove(nghttp2_stream_roots *roots, + nghttp2_stream *stream); + +void nghttp2_stream_roots_remove_all(nghttp2_stream_roots *roots); + +#endif /* NGHTTP2_STREAM */ diff --git a/lib/nghttp2_submit.c b/lib/nghttp2_submit.c new file mode 100644 index 0000000..0f6d9f5 --- /dev/null +++ b/lib/nghttp2_submit.c @@ -0,0 +1,479 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012, 2013 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "nghttp2_submit.h" + +#include +#include + +#include "nghttp2_session.h" +#include "nghttp2_frame.h" +#include "nghttp2_helper.h" +#include "nghttp2_priority_spec.h" + +/* This function takes ownership of |nva_copy|. Regardless of the + return value, the caller must not free |nva_copy| after this + function returns. */ +static int32_t submit_headers_shared(nghttp2_session *session, uint8_t flags, + int32_t stream_id, + const nghttp2_priority_spec *pri_spec, + nghttp2_nv *nva_copy, size_t nvlen, + const nghttp2_data_provider *data_prd, + void *stream_user_data, + uint8_t attach_stream) { + int rv; + uint8_t flags_copy; + nghttp2_outbound_item *item = NULL; + nghttp2_frame *frame = NULL; + nghttp2_headers_category hcat; + nghttp2_mem *mem; + + mem = &session->mem; + + if (stream_id == 0) { + rv = NGHTTP2_ERR_INVALID_ARGUMENT; + goto fail; + } + + item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item)); + if (item == NULL) { + rv = NGHTTP2_ERR_NOMEM; + goto fail; + } + + nghttp2_outbound_item_init(item); + + if (data_prd != NULL && data_prd->read_callback != NULL) { + item->aux_data.headers.data_prd = *data_prd; + } + + item->aux_data.headers.stream_user_data = stream_user_data; + item->aux_data.headers.attach_stream = attach_stream; + + flags_copy = (flags & (NGHTTP2_FLAG_END_STREAM | NGHTTP2_FLAG_PRIORITY)) | + NGHTTP2_FLAG_END_HEADERS; + + if (stream_id == -1) { + if (session->next_stream_id > INT32_MAX) { + rv = NGHTTP2_ERR_STREAM_ID_NOT_AVAILABLE; + goto fail; + } + + stream_id = session->next_stream_id; + session->next_stream_id += 2; + + hcat = NGHTTP2_HCAT_REQUEST; + } else { + /* More specific categorization will be done later. */ + hcat = NGHTTP2_HCAT_HEADERS; + } + + frame = &item->frame; + + nghttp2_frame_headers_init(&frame->headers, flags_copy, stream_id, hcat, + pri_spec, nva_copy, nvlen); + + rv = nghttp2_session_add_item(session, item); + + if (rv != 0) { + nghttp2_frame_headers_free(&frame->headers, mem); + goto fail2; + } + + if (hcat == NGHTTP2_HCAT_REQUEST) { + return stream_id; + } + + return 0; + +fail: + /* nghttp2_frame_headers_init() takes ownership of nva_copy. */ + nghttp2_nv_array_del(nva_copy, mem); +fail2: + nghttp2_mem_free(mem, item); + + return rv; +} + +static void adjust_priority_spec_weight(nghttp2_priority_spec *pri_spec) { + if (pri_spec->weight < NGHTTP2_MIN_WEIGHT) { + pri_spec->weight = NGHTTP2_MIN_WEIGHT; + } else if (pri_spec->weight > NGHTTP2_MAX_WEIGHT) { + pri_spec->weight = NGHTTP2_MAX_WEIGHT; + } +} + +static int32_t submit_headers_shared_nva(nghttp2_session *session, + uint8_t flags, int32_t stream_id, + const nghttp2_priority_spec *pri_spec, + const nghttp2_nv *nva, size_t nvlen, + const nghttp2_data_provider *data_prd, + void *stream_user_data, + uint8_t attach_stream) { + int rv; + nghttp2_nv *nva_copy; + nghttp2_priority_spec copy_pri_spec; + nghttp2_mem *mem; + + mem = &session->mem; + + if (pri_spec) { + copy_pri_spec = *pri_spec; + adjust_priority_spec_weight(©_pri_spec); + } else { + nghttp2_priority_spec_default_init(©_pri_spec); + } + + rv = nghttp2_nv_array_copy(&nva_copy, nva, nvlen, mem); + if (rv < 0) { + return rv; + } + + return submit_headers_shared(session, flags, stream_id, ©_pri_spec, + nva_copy, nvlen, data_prd, stream_user_data, + attach_stream); +} + +int32_t nghttp2_submit_trailer(nghttp2_session *session, int32_t stream_id, + const nghttp2_nv *nva, size_t nvlen) { + return submit_headers_shared_nva(session, NGHTTP2_FLAG_END_STREAM, stream_id, + NULL, nva, nvlen, NULL, NULL, 0); +} + +int32_t nghttp2_submit_headers(nghttp2_session *session, uint8_t flags, + int32_t stream_id, + const nghttp2_priority_spec *pri_spec, + const nghttp2_nv *nva, size_t nvlen, + void *stream_user_data) { + flags &= NGHTTP2_FLAG_END_STREAM; + + if (pri_spec && !nghttp2_priority_spec_check_default(pri_spec)) { + flags |= NGHTTP2_FLAG_PRIORITY; + } else { + pri_spec = NULL; + } + + return submit_headers_shared_nva(session, flags, stream_id, pri_spec, nva, + nvlen, NULL, stream_user_data, 0); +} + +int nghttp2_submit_ping(nghttp2_session *session, uint8_t flags _U_, + const uint8_t *opaque_data) { + return nghttp2_session_add_ping(session, NGHTTP2_FLAG_NONE, opaque_data); +} + +int nghttp2_submit_priority(nghttp2_session *session, uint8_t flags _U_, + int32_t stream_id, + const nghttp2_priority_spec *pri_spec) { + int rv; + nghttp2_outbound_item *item; + nghttp2_frame *frame; + nghttp2_priority_spec copy_pri_spec; + nghttp2_mem *mem; + + mem = &session->mem; + + if (stream_id == 0 || pri_spec == NULL) { + return NGHTTP2_ERR_INVALID_ARGUMENT; + } + + if (stream_id == pri_spec->stream_id) { + return NGHTTP2_ERR_INVALID_ARGUMENT; + } + + copy_pri_spec = *pri_spec; + + adjust_priority_spec_weight(©_pri_spec); + + item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item)); + + if (item == NULL) { + return NGHTTP2_ERR_NOMEM; + } + + nghttp2_outbound_item_init(item); + + frame = &item->frame; + + nghttp2_frame_priority_init(&frame->priority, stream_id, ©_pri_spec); + + rv = nghttp2_session_add_item(session, item); + + if (rv != 0) { + nghttp2_frame_priority_free(&frame->priority); + nghttp2_mem_free(mem, item); + + return rv; + } + + return 0; +} + +int nghttp2_submit_rst_stream(nghttp2_session *session, uint8_t flags _U_, + int32_t stream_id, uint32_t error_code) { + if (stream_id == 0) { + return NGHTTP2_ERR_INVALID_ARGUMENT; + } + + return nghttp2_session_add_rst_stream(session, stream_id, error_code); +} + +int nghttp2_submit_goaway(nghttp2_session *session, uint8_t flags _U_, + int32_t last_stream_id, uint32_t error_code, + const uint8_t *opaque_data, size_t opaque_data_len) { + if (session->goaway_flags & NGHTTP2_GOAWAY_TERM_ON_SEND) { + return 0; + } + return nghttp2_session_add_goaway(session, last_stream_id, error_code, + opaque_data, opaque_data_len, + NGHTTP2_GOAWAY_AUX_NONE); +} + +int nghttp2_submit_shutdown_notice(nghttp2_session *session) { + if (!session->server) { + return NGHTTP2_ERR_INVALID_STATE; + } + if (session->goaway_flags) { + return 0; + } + return nghttp2_session_add_goaway(session, (1u << 31) - 1, NGHTTP2_NO_ERROR, + NULL, 0, + NGHTTP2_GOAWAY_AUX_SHUTDOWN_NOTICE); +} + +int nghttp2_submit_settings(nghttp2_session *session, uint8_t flags _U_, + const nghttp2_settings_entry *iv, size_t niv) { + return nghttp2_session_add_settings(session, NGHTTP2_FLAG_NONE, iv, niv); +} + +int32_t nghttp2_submit_push_promise(nghttp2_session *session, uint8_t flags _U_, + int32_t stream_id, const nghttp2_nv *nva, + size_t nvlen, + void *promised_stream_user_data) { + nghttp2_outbound_item *item; + nghttp2_frame *frame; + nghttp2_nv *nva_copy; + uint8_t flags_copy; + int32_t promised_stream_id; + int rv; + nghttp2_mem *mem; + + mem = &session->mem; + + if (stream_id == 0 || nghttp2_session_is_my_stream_id(session, stream_id)) { + return NGHTTP2_ERR_INVALID_ARGUMENT; + } + + if (!session->server) { + return NGHTTP2_ERR_PROTO; + } + + /* All 32bit signed stream IDs are spent. */ + if (session->next_stream_id > INT32_MAX) { + return NGHTTP2_ERR_STREAM_ID_NOT_AVAILABLE; + } + + item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item)); + if (item == NULL) { + return NGHTTP2_ERR_NOMEM; + } + + nghttp2_outbound_item_init(item); + + item->aux_data.headers.stream_user_data = promised_stream_user_data; + + frame = &item->frame; + + rv = nghttp2_nv_array_copy(&nva_copy, nva, nvlen, mem); + if (rv < 0) { + nghttp2_mem_free(mem, item); + return rv; + } + + flags_copy = NGHTTP2_FLAG_END_HEADERS; + + promised_stream_id = session->next_stream_id; + session->next_stream_id += 2; + + nghttp2_frame_push_promise_init(&frame->push_promise, flags_copy, stream_id, + promised_stream_id, nva_copy, nvlen); + + rv = nghttp2_session_add_item(session, item); + + if (rv != 0) { + nghttp2_frame_push_promise_free(&frame->push_promise, mem); + nghttp2_mem_free(mem, item); + + return rv; + } + + return promised_stream_id; +} + +int nghttp2_submit_window_update(nghttp2_session *session, uint8_t flags, + int32_t stream_id, + int32_t window_size_increment) { + int rv; + nghttp2_stream *stream = 0; + if (window_size_increment == 0) { + return 0; + } + flags = 0; + if (stream_id == 0) { + rv = nghttp2_adjust_local_window_size( + &session->local_window_size, &session->recv_window_size, + &session->recv_reduction, &window_size_increment); + if (rv != 0) { + return rv; + } + } else { + stream = nghttp2_session_get_stream(session, stream_id); + if (!stream) { + return 0; + } + + rv = nghttp2_adjust_local_window_size( + &stream->local_window_size, &stream->recv_window_size, + &stream->recv_reduction, &window_size_increment); + if (rv != 0) { + return rv; + } + } + + if (window_size_increment > 0) { + if (stream_id == 0) { + session->consumed_size = + nghttp2_max(0, session->consumed_size - window_size_increment); + } else { + stream->consumed_size = + nghttp2_max(0, stream->consumed_size - window_size_increment); + } + + return nghttp2_session_add_window_update(session, flags, stream_id, + window_size_increment); + } + return 0; +} + +static uint8_t set_request_flags(const nghttp2_priority_spec *pri_spec, + const nghttp2_data_provider *data_prd) { + uint8_t flags = NGHTTP2_FLAG_NONE; + if (data_prd == NULL || data_prd->read_callback == NULL) { + flags |= NGHTTP2_FLAG_END_STREAM; + } + + if (pri_spec) { + flags |= NGHTTP2_FLAG_PRIORITY; + } + + return flags; +} + +int32_t nghttp2_submit_request(nghttp2_session *session, + const nghttp2_priority_spec *pri_spec, + const nghttp2_nv *nva, size_t nvlen, + const nghttp2_data_provider *data_prd, + void *stream_user_data) { + uint8_t flags; + + if (pri_spec && nghttp2_priority_spec_check_default(pri_spec)) { + pri_spec = NULL; + } + + flags = set_request_flags(pri_spec, data_prd); + + return submit_headers_shared_nva(session, flags, -1, pri_spec, nva, nvlen, + data_prd, stream_user_data, 0); +} + +static uint8_t set_response_flags(const nghttp2_data_provider *data_prd) { + uint8_t flags = NGHTTP2_FLAG_NONE; + if (data_prd == NULL || data_prd->read_callback == NULL) { + flags |= NGHTTP2_FLAG_END_STREAM; + } + return flags; +} + +int nghttp2_submit_response(nghttp2_session *session, int32_t stream_id, + const nghttp2_nv *nva, size_t nvlen, + const nghttp2_data_provider *data_prd) { + uint8_t flags = set_response_flags(data_prd); + return submit_headers_shared_nva(session, flags, stream_id, NULL, nva, nvlen, + data_prd, NULL, 1); +} + +int nghttp2_submit_data(nghttp2_session *session, uint8_t flags, + int32_t stream_id, + const nghttp2_data_provider *data_prd) { + int rv; + nghttp2_outbound_item *item; + nghttp2_frame *frame; + nghttp2_data_aux_data *aux_data; + uint8_t nflags = flags & NGHTTP2_FLAG_END_STREAM; + nghttp2_mem *mem; + + mem = &session->mem; + + if (stream_id == 0) { + return NGHTTP2_ERR_INVALID_ARGUMENT; + } + + item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item)); + if (item == NULL) { + return NGHTTP2_ERR_NOMEM; + } + + nghttp2_outbound_item_init(item); + + frame = &item->frame; + aux_data = &item->aux_data.data; + aux_data->data_prd = *data_prd; + aux_data->eof = 0; + aux_data->flags = nflags; + + /* flags are sent on transmission */ + nghttp2_frame_data_init(&frame->data, NGHTTP2_FLAG_NONE, stream_id); + + rv = nghttp2_session_add_item(session, item); + if (rv != 0) { + nghttp2_frame_data_free(&frame->data); + nghttp2_mem_free(mem, item); + return rv; + } + return 0; +} + +ssize_t nghttp2_pack_settings_payload(uint8_t *buf, size_t buflen, + const nghttp2_settings_entry *iv, + size_t niv) { + if (!nghttp2_iv_check(iv, niv)) { + return NGHTTP2_ERR_INVALID_ARGUMENT; + } + + if (buflen < (niv * NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH)) { + return NGHTTP2_ERR_INSUFF_BUFSIZE; + } + + return nghttp2_frame_pack_settings_payload(buf, iv, niv); +} diff --git a/lib/nghttp2_submit.h b/lib/nghttp2_submit.h new file mode 100644 index 0000000..545388c --- /dev/null +++ b/lib/nghttp2_submit.h @@ -0,0 +1,34 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGHTTP2_SUBMIT_H +#define NGHTTP2_SUBMIT_H + +#ifdef HAVE_CONFIG_H +#include +#endif /* HAVE_CONFIG_H */ + +#include + +#endif /* NGHTTP2_SUBMIT_H */ diff --git a/lib/nghttp2_version.c b/lib/nghttp2_version.c new file mode 100644 index 0000000..8c5710d --- /dev/null +++ b/lib/nghttp2_version.c @@ -0,0 +1,38 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012, 2013 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifdef HAVE_CONFIG_H +#include +#endif /* HAVE_CONFIG_H */ + +#include + +static nghttp2_info version = {NGHTTP2_VERSION_AGE, NGHTTP2_VERSION_NUM, + NGHTTP2_VERSION, NGHTTP2_PROTO_VERSION_ID}; + +nghttp2_info *nghttp2_version(int least_version) { + if (least_version > NGHTTP2_VERSION_NUM) + return NULL; + return &version; +} diff --git a/ltmain.sh b/ltmain.sh new file mode 100644 index 0000000..bffda54 --- /dev/null +++ b/ltmain.sh @@ -0,0 +1,9661 @@ + +# libtool (GNU libtool) 2.4.2 +# Written by Gordon Matzigkeit , 1996 + +# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2003, 2004, 2005, 2006, +# 2007, 2008, 2009, 2010, 2011 Free Software Foundation, Inc. +# This is free software; see the source for copying conditions. There is NO +# warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +# GNU Libtool 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 2 of the License, or +# (at your option) any later version. +# +# As a special exception to the GNU General Public License, +# if you distribute this file as part of a program or library that +# is built using GNU Libtool, you may include this file under the +# same distribution terms that you use for the rest of that program. +# +# GNU Libtool 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 GNU Libtool; see the file COPYING. If not, a copy +# can be downloaded from http://www.gnu.org/licenses/gpl.html, +# or obtained by writing to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +# Usage: $progname [OPTION]... [MODE-ARG]... +# +# Provide generalized library-building support services. +# +# --config show all configuration variables +# --debug enable verbose shell tracing +# -n, --dry-run display commands without modifying any files +# --features display basic configuration information and exit +# --mode=MODE use operation mode MODE +# --preserve-dup-deps don't remove duplicate dependency libraries +# --quiet, --silent don't print informational messages +# --no-quiet, --no-silent +# print informational messages (default) +# --no-warn don't display warning messages +# --tag=TAG use configuration variables from tag TAG +# -v, --verbose print more informational messages than default +# --no-verbose don't print the extra informational messages +# --version print version information +# -h, --help, --help-all print short, long, or detailed help message +# +# MODE must be one of the following: +# +# clean remove files from the build directory +# compile compile a source file into a libtool object +# execute automatically set library path, then run a program +# finish complete the installation of libtool libraries +# install install libraries or executables +# link create a library or an executable +# uninstall remove libraries from an installed directory +# +# MODE-ARGS vary depending on the MODE. When passed as first option, +# `--mode=MODE' may be abbreviated as `MODE' or a unique abbreviation of that. +# Try `$progname --help --mode=MODE' for a more detailed description of MODE. +# +# When reporting a bug, please describe a test case to reproduce it and +# include the following information: +# +# host-triplet: $host +# shell: $SHELL +# compiler: $LTCC +# compiler flags: $LTCFLAGS +# linker: $LD (gnu? $with_gnu_ld) +# $progname: (GNU libtool) 2.4.2 Debian-2.4.2-1.11 +# automake: $automake_version +# autoconf: $autoconf_version +# +# Report bugs to . +# GNU libtool home page: . +# General help using GNU software: . + +PROGRAM=libtool +PACKAGE=libtool +VERSION="2.4.2 Debian-2.4.2-1.11" +TIMESTAMP="" +package_revision=1.3337 + +# Be Bourne compatible +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then + emulate sh + NULLCMD=: + # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' + setopt NO_GLOB_SUBST +else + case `(set -o) 2>/dev/null` in *posix*) set -o posix;; esac +fi +BIN_SH=xpg4; export BIN_SH # for Tru64 +DUALCASE=1; export DUALCASE # for MKS sh + +# A function that is used when there is no print builtin or printf. +func_fallback_echo () +{ + eval 'cat <<_LTECHO_EOF +$1 +_LTECHO_EOF' +} + +# NLS nuisances: We save the old values to restore during execute mode. +lt_user_locale= +lt_safe_locale= +for lt_var in LANG LANGUAGE LC_ALL LC_CTYPE LC_COLLATE LC_MESSAGES +do + eval "if test \"\${$lt_var+set}\" = set; then + save_$lt_var=\$$lt_var + $lt_var=C + export $lt_var + lt_user_locale=\"$lt_var=\\\$save_\$lt_var; \$lt_user_locale\" + lt_safe_locale=\"$lt_var=C; \$lt_safe_locale\" + fi" +done +LC_ALL=C +LANGUAGE=C +export LANGUAGE LC_ALL + +$lt_unset CDPATH + + +# Work around backward compatibility issue on IRIX 6.5. On IRIX 6.4+, sh +# is ksh but when the shell is invoked as "sh" and the current value of +# the _XPG environment variable is not equal to 1 (one), the special +# positional parameter $0, within a function call, is the name of the +# function. +progpath="$0" + + + +: ${CP="cp -f"} +test "${ECHO+set}" = set || ECHO=${as_echo-'printf %s\n'} +: ${MAKE="make"} +: ${MKDIR="mkdir"} +: ${MV="mv -f"} +: ${RM="rm -f"} +: ${SHELL="${CONFIG_SHELL-/bin/sh}"} +: ${Xsed="$SED -e 1s/^X//"} + +# Global variables: +EXIT_SUCCESS=0 +EXIT_FAILURE=1 +EXIT_MISMATCH=63 # $? = 63 is used to indicate version mismatch to missing. +EXIT_SKIP=77 # $? = 77 is used to indicate a skipped test to automake. + +exit_status=$EXIT_SUCCESS + +# Make sure IFS has a sensible default +lt_nl=' +' +IFS=" $lt_nl" + +dirname="s,/[^/]*$,," +basename="s,^.*/,," + +# func_dirname file append nondir_replacement +# Compute the dirname of FILE. If nonempty, add APPEND to the result, +# otherwise set result to NONDIR_REPLACEMENT. +func_dirname () +{ + func_dirname_result=`$ECHO "${1}" | $SED "$dirname"` + if test "X$func_dirname_result" = "X${1}"; then + func_dirname_result="${3}" + else + func_dirname_result="$func_dirname_result${2}" + fi +} # func_dirname may be replaced by extended shell implementation + + +# func_basename file +func_basename () +{ + func_basename_result=`$ECHO "${1}" | $SED "$basename"` +} # func_basename may be replaced by extended shell implementation + + +# func_dirname_and_basename file append nondir_replacement +# perform func_basename and func_dirname in a single function +# call: +# dirname: Compute the dirname of FILE. If nonempty, +# add APPEND to the result, otherwise set result +# to NONDIR_REPLACEMENT. +# value returned in "$func_dirname_result" +# basename: Compute filename of FILE. +# value retuned in "$func_basename_result" +# Implementation must be kept synchronized with func_dirname +# and func_basename. For efficiency, we do not delegate to +# those functions but instead duplicate the functionality here. +func_dirname_and_basename () +{ + # Extract subdirectory from the argument. + func_dirname_result=`$ECHO "${1}" | $SED -e "$dirname"` + if test "X$func_dirname_result" = "X${1}"; then + func_dirname_result="${3}" + else + func_dirname_result="$func_dirname_result${2}" + fi + func_basename_result=`$ECHO "${1}" | $SED -e "$basename"` +} # func_dirname_and_basename may be replaced by extended shell implementation + + +# func_stripname prefix suffix name +# strip PREFIX and SUFFIX off of NAME. +# PREFIX and SUFFIX must not contain globbing or regex special +# characters, hashes, percent signs, but SUFFIX may contain a leading +# dot (in which case that matches only a dot). +# func_strip_suffix prefix name +func_stripname () +{ + case ${2} in + .*) func_stripname_result=`$ECHO "${3}" | $SED "s%^${1}%%; s%\\\\${2}\$%%"`;; + *) func_stripname_result=`$ECHO "${3}" | $SED "s%^${1}%%; s%${2}\$%%"`;; + esac +} # func_stripname may be replaced by extended shell implementation + + +# These SED scripts presuppose an absolute path with a trailing slash. +pathcar='s,^/\([^/]*\).*$,\1,' +pathcdr='s,^/[^/]*,,' +removedotparts=':dotsl + s@/\./@/@g + t dotsl + s,/\.$,/,' +collapseslashes='s@/\{1,\}@/@g' +finalslash='s,/*$,/,' + +# func_normal_abspath PATH +# Remove doubled-up and trailing slashes, "." path components, +# and cancel out any ".." path components in PATH after making +# it an absolute path. +# value returned in "$func_normal_abspath_result" +func_normal_abspath () +{ + # Start from root dir and reassemble the path. + func_normal_abspath_result= + func_normal_abspath_tpath=$1 + func_normal_abspath_altnamespace= + case $func_normal_abspath_tpath in + "") + # Empty path, that just means $cwd. + func_stripname '' '/' "`pwd`" + func_normal_abspath_result=$func_stripname_result + return + ;; + # The next three entries are used to spot a run of precisely + # two leading slashes without using negated character classes; + # we take advantage of case's first-match behaviour. + ///*) + # Unusual form of absolute path, do nothing. + ;; + //*) + # Not necessarily an ordinary path; POSIX reserves leading '//' + # and for example Cygwin uses it to access remote file shares + # over CIFS/SMB, so we conserve a leading double slash if found. + func_normal_abspath_altnamespace=/ + ;; + /*) + # Absolute path, do nothing. + ;; + *) + # Relative path, prepend $cwd. + func_normal_abspath_tpath=`pwd`/$func_normal_abspath_tpath + ;; + esac + # Cancel out all the simple stuff to save iterations. We also want + # the path to end with a slash for ease of parsing, so make sure + # there is one (and only one) here. + func_normal_abspath_tpath=`$ECHO "$func_normal_abspath_tpath" | $SED \ + -e "$removedotparts" -e "$collapseslashes" -e "$finalslash"` + while :; do + # Processed it all yet? + if test "$func_normal_abspath_tpath" = / ; then + # If we ascended to the root using ".." the result may be empty now. + if test -z "$func_normal_abspath_result" ; then + func_normal_abspath_result=/ + fi + break + fi + func_normal_abspath_tcomponent=`$ECHO "$func_normal_abspath_tpath" | $SED \ + -e "$pathcar"` + func_normal_abspath_tpath=`$ECHO "$func_normal_abspath_tpath" | $SED \ + -e "$pathcdr"` + # Figure out what to do with it + case $func_normal_abspath_tcomponent in + "") + # Trailing empty path component, ignore it. + ;; + ..) + # Parent dir; strip last assembled component from result. + func_dirname "$func_normal_abspath_result" + func_normal_abspath_result=$func_dirname_result + ;; + *) + # Actual path component, append it. + func_normal_abspath_result=$func_normal_abspath_result/$func_normal_abspath_tcomponent + ;; + esac + done + # Restore leading double-slash if one was found on entry. + func_normal_abspath_result=$func_normal_abspath_altnamespace$func_normal_abspath_result +} + +# func_relative_path SRCDIR DSTDIR +# generates a relative path from SRCDIR to DSTDIR, with a trailing +# slash if non-empty, suitable for immediately appending a filename +# without needing to append a separator. +# value returned in "$func_relative_path_result" +func_relative_path () +{ + func_relative_path_result= + func_normal_abspath "$1" + func_relative_path_tlibdir=$func_normal_abspath_result + func_normal_abspath "$2" + func_relative_path_tbindir=$func_normal_abspath_result + + # Ascend the tree starting from libdir + while :; do + # check if we have found a prefix of bindir + case $func_relative_path_tbindir in + $func_relative_path_tlibdir) + # found an exact match + func_relative_path_tcancelled= + break + ;; + $func_relative_path_tlibdir*) + # found a matching prefix + func_stripname "$func_relative_path_tlibdir" '' "$func_relative_path_tbindir" + func_relative_path_tcancelled=$func_stripname_result + if test -z "$func_relative_path_result"; then + func_relative_path_result=. + fi + break + ;; + *) + func_dirname $func_relative_path_tlibdir + func_relative_path_tlibdir=${func_dirname_result} + if test "x$func_relative_path_tlibdir" = x ; then + # Have to descend all the way to the root! + func_relative_path_result=../$func_relative_path_result + func_relative_path_tcancelled=$func_relative_path_tbindir + break + fi + func_relative_path_result=../$func_relative_path_result + ;; + esac + done + + # Now calculate path; take care to avoid doubling-up slashes. + func_stripname '' '/' "$func_relative_path_result" + func_relative_path_result=$func_stripname_result + func_stripname '/' '/' "$func_relative_path_tcancelled" + if test "x$func_stripname_result" != x ; then + func_relative_path_result=${func_relative_path_result}/${func_stripname_result} + fi + + # Normalisation. If bindir is libdir, return empty string, + # else relative path ending with a slash; either way, target + # file name can be directly appended. + if test ! -z "$func_relative_path_result"; then + func_stripname './' '' "$func_relative_path_result/" + func_relative_path_result=$func_stripname_result + fi +} + +# The name of this program: +func_dirname_and_basename "$progpath" +progname=$func_basename_result + +# Make sure we have an absolute path for reexecution: +case $progpath in + [\\/]*|[A-Za-z]:\\*) ;; + *[\\/]*) + progdir=$func_dirname_result + progdir=`cd "$progdir" && pwd` + progpath="$progdir/$progname" + ;; + *) + save_IFS="$IFS" + IFS=${PATH_SEPARATOR-:} + for progdir in $PATH; do + IFS="$save_IFS" + test -x "$progdir/$progname" && break + done + IFS="$save_IFS" + test -n "$progdir" || progdir=`pwd` + progpath="$progdir/$progname" + ;; +esac + +# Sed substitution that helps us do robust quoting. It backslashifies +# metacharacters that are still active within double-quoted strings. +Xsed="${SED}"' -e 1s/^X//' +sed_quote_subst='s/\([`"$\\]\)/\\\1/g' + +# Same as above, but do not quote variable references. +double_quote_subst='s/\(["`\\]\)/\\\1/g' + +# Sed substitution that turns a string into a regex matching for the +# string literally. +sed_make_literal_regex='s,[].[^$\\*\/],\\&,g' + +# Sed substitution that converts a w32 file name or path +# which contains forward slashes, into one that contains +# (escaped) backslashes. A very naive implementation. +lt_sed_naive_backslashify='s|\\\\*|\\|g;s|/|\\|g;s|\\|\\\\|g' + +# Re-`\' parameter expansions in output of double_quote_subst that were +# `\'-ed in input to the same. If an odd number of `\' preceded a '$' +# in input to double_quote_subst, that '$' was protected from expansion. +# Since each input `\' is now two `\'s, look for any number of runs of +# four `\'s followed by two `\'s and then a '$'. `\' that '$'. +bs='\\' +bs2='\\\\' +bs4='\\\\\\\\' +dollar='\$' +sed_double_backslash="\ + s/$bs4/&\\ +/g + s/^$bs2$dollar/$bs&/ + s/\\([^$bs]\\)$bs2$dollar/\\1$bs2$bs$dollar/g + s/\n//g" + +# Standard options: +opt_dry_run=false +opt_help=false +opt_quiet=false +opt_verbose=false +opt_warning=: + +# func_echo arg... +# Echo program name prefixed message, along with the current mode +# name if it has been set yet. +func_echo () +{ + $ECHO "$progname: ${opt_mode+$opt_mode: }$*" +} + +# func_verbose arg... +# Echo program name prefixed message in verbose mode only. +func_verbose () +{ + $opt_verbose && func_echo ${1+"$@"} + + # A bug in bash halts the script if the last line of a function + # fails when set -e is in force, so we need another command to + # work around that: + : +} + +# func_echo_all arg... +# Invoke $ECHO with all args, space-separated. +func_echo_all () +{ + $ECHO "$*" +} + +# func_error arg... +# Echo program name prefixed message to standard error. +func_error () +{ + $ECHO "$progname: ${opt_mode+$opt_mode: }"${1+"$@"} 1>&2 +} + +# func_warning arg... +# Echo program name prefixed warning message to standard error. +func_warning () +{ + $opt_warning && $ECHO "$progname: ${opt_mode+$opt_mode: }warning: "${1+"$@"} 1>&2 + + # bash bug again: + : +} + +# func_fatal_error arg... +# Echo program name prefixed message to standard error, and exit. +func_fatal_error () +{ + func_error ${1+"$@"} + exit $EXIT_FAILURE +} + +# func_fatal_help arg... +# Echo program name prefixed message to standard error, followed by +# a help hint, and exit. +func_fatal_help () +{ + func_error ${1+"$@"} + func_fatal_error "$help" +} +help="Try \`$progname --help' for more information." ## default + + +# func_grep expression filename +# Check whether EXPRESSION matches any line of FILENAME, without output. +func_grep () +{ + $GREP "$1" "$2" >/dev/null 2>&1 +} + + +# func_mkdir_p directory-path +# Make sure the entire path to DIRECTORY-PATH is available. +func_mkdir_p () +{ + my_directory_path="$1" + my_dir_list= + + if test -n "$my_directory_path" && test "$opt_dry_run" != ":"; then + + # Protect directory names starting with `-' + case $my_directory_path in + -*) my_directory_path="./$my_directory_path" ;; + esac + + # While some portion of DIR does not yet exist... + while test ! -d "$my_directory_path"; do + # ...make a list in topmost first order. Use a colon delimited + # list incase some portion of path contains whitespace. + my_dir_list="$my_directory_path:$my_dir_list" + + # If the last portion added has no slash in it, the list is done + case $my_directory_path in */*) ;; *) break ;; esac + + # ...otherwise throw away the child directory and loop + my_directory_path=`$ECHO "$my_directory_path" | $SED -e "$dirname"` + done + my_dir_list=`$ECHO "$my_dir_list" | $SED 's,:*$,,'` + + save_mkdir_p_IFS="$IFS"; IFS=':' + for my_dir in $my_dir_list; do + IFS="$save_mkdir_p_IFS" + # mkdir can fail with a `File exist' error if two processes + # try to create one of the directories concurrently. Don't + # stop in that case! + $MKDIR "$my_dir" 2>/dev/null || : + done + IFS="$save_mkdir_p_IFS" + + # Bail out if we (or some other process) failed to create a directory. + test -d "$my_directory_path" || \ + func_fatal_error "Failed to create \`$1'" + fi +} + + +# func_mktempdir [string] +# Make a temporary directory that won't clash with other running +# libtool processes, and avoids race conditions if possible. If +# given, STRING is the basename for that directory. +func_mktempdir () +{ + my_template="${TMPDIR-/tmp}/${1-$progname}" + + if test "$opt_dry_run" = ":"; then + # Return a directory name, but don't create it in dry-run mode + my_tmpdir="${my_template}-$$" + else + + # If mktemp works, use that first and foremost + my_tmpdir=`mktemp -d "${my_template}-XXXXXXXX" 2>/dev/null` + + if test ! -d "$my_tmpdir"; then + # Failing that, at least try and use $RANDOM to avoid a race + my_tmpdir="${my_template}-${RANDOM-0}$$" + + save_mktempdir_umask=`umask` + umask 0077 + $MKDIR "$my_tmpdir" + umask $save_mktempdir_umask + fi + + # If we're not in dry-run mode, bomb out on failure + test -d "$my_tmpdir" || \ + func_fatal_error "cannot create temporary directory \`$my_tmpdir'" + fi + + $ECHO "$my_tmpdir" +} + + +# func_quote_for_eval arg +# Aesthetically quote ARG to be evaled later. +# This function returns two values: FUNC_QUOTE_FOR_EVAL_RESULT +# is double-quoted, suitable for a subsequent eval, whereas +# FUNC_QUOTE_FOR_EVAL_UNQUOTED_RESULT has merely all characters +# which are still active within double quotes backslashified. +func_quote_for_eval () +{ + case $1 in + *[\\\`\"\$]*) + func_quote_for_eval_unquoted_result=`$ECHO "$1" | $SED "$sed_quote_subst"` ;; + *) + func_quote_for_eval_unquoted_result="$1" ;; + esac + + case $func_quote_for_eval_unquoted_result in + # Double-quote args containing shell metacharacters to delay + # word splitting, command substitution and and variable + # expansion for a subsequent eval. + # Many Bourne shells cannot handle close brackets correctly + # in scan sets, so we specify it separately. + *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") + func_quote_for_eval_result="\"$func_quote_for_eval_unquoted_result\"" + ;; + *) + func_quote_for_eval_result="$func_quote_for_eval_unquoted_result" + esac +} + + +# func_quote_for_expand arg +# Aesthetically quote ARG to be evaled later; same as above, +# but do not quote variable references. +func_quote_for_expand () +{ + case $1 in + *[\\\`\"]*) + my_arg=`$ECHO "$1" | $SED \ + -e "$double_quote_subst" -e "$sed_double_backslash"` ;; + *) + my_arg="$1" ;; + esac + + case $my_arg in + # Double-quote args containing shell metacharacters to delay + # word splitting and command substitution for a subsequent eval. + # Many Bourne shells cannot handle close brackets correctly + # in scan sets, so we specify it separately. + *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") + my_arg="\"$my_arg\"" + ;; + esac + + func_quote_for_expand_result="$my_arg" +} + + +# func_show_eval cmd [fail_exp] +# Unless opt_silent is true, then output CMD. Then, if opt_dryrun is +# not true, evaluate CMD. If the evaluation of CMD fails, and FAIL_EXP +# is given, then evaluate it. +func_show_eval () +{ + my_cmd="$1" + my_fail_exp="${2-:}" + + ${opt_silent-false} || { + func_quote_for_expand "$my_cmd" + eval "func_echo $func_quote_for_expand_result" + } + + if ${opt_dry_run-false}; then :; else + eval "$my_cmd" + my_status=$? + if test "$my_status" -eq 0; then :; else + eval "(exit $my_status); $my_fail_exp" + fi + fi +} + + +# func_show_eval_locale cmd [fail_exp] +# Unless opt_silent is true, then output CMD. Then, if opt_dryrun is +# not true, evaluate CMD. If the evaluation of CMD fails, and FAIL_EXP +# is given, then evaluate it. Use the saved locale for evaluation. +func_show_eval_locale () +{ + my_cmd="$1" + my_fail_exp="${2-:}" + + ${opt_silent-false} || { + func_quote_for_expand "$my_cmd" + eval "func_echo $func_quote_for_expand_result" + } + + if ${opt_dry_run-false}; then :; else + eval "$lt_user_locale + $my_cmd" + my_status=$? + eval "$lt_safe_locale" + if test "$my_status" -eq 0; then :; else + eval "(exit $my_status); $my_fail_exp" + fi + fi +} + +# func_tr_sh +# Turn $1 into a string suitable for a shell variable name. +# Result is stored in $func_tr_sh_result. All characters +# not in the set a-zA-Z0-9_ are replaced with '_'. Further, +# if $1 begins with a digit, a '_' is prepended as well. +func_tr_sh () +{ + case $1 in + [0-9]* | *[!a-zA-Z0-9_]*) + func_tr_sh_result=`$ECHO "$1" | $SED 's/^\([0-9]\)/_\1/; s/[^a-zA-Z0-9_]/_/g'` + ;; + * ) + func_tr_sh_result=$1 + ;; + esac +} + + +# func_version +# Echo version message to standard output and exit. +func_version () +{ + $opt_debug + + $SED -n '/(C)/!b go + :more + /\./!{ + N + s/\n# / / + b more + } + :go + /^# '$PROGRAM' (GNU /,/# warranty; / { + s/^# // + s/^# *$// + s/\((C)\)[ 0-9,-]*\( [1-9][0-9]*\)/\1\2/ + p + }' < "$progpath" + exit $? +} + +# func_usage +# Echo short help message to standard output and exit. +func_usage () +{ + $opt_debug + + $SED -n '/^# Usage:/,/^# *.*--help/ { + s/^# // + s/^# *$// + s/\$progname/'$progname'/ + p + }' < "$progpath" + echo + $ECHO "run \`$progname --help | more' for full usage" + exit $? +} + +# func_help [NOEXIT] +# Echo long help message to standard output and exit, +# unless 'noexit' is passed as argument. +func_help () +{ + $opt_debug + + $SED -n '/^# Usage:/,/# Report bugs to/ { + :print + s/^# // + s/^# *$// + s*\$progname*'$progname'* + s*\$host*'"$host"'* + s*\$SHELL*'"$SHELL"'* + s*\$LTCC*'"$LTCC"'* + s*\$LTCFLAGS*'"$LTCFLAGS"'* + s*\$LD*'"$LD"'* + s/\$with_gnu_ld/'"$with_gnu_ld"'/ + s/\$automake_version/'"`(${AUTOMAKE-automake} --version) 2>/dev/null |$SED 1q`"'/ + s/\$autoconf_version/'"`(${AUTOCONF-autoconf} --version) 2>/dev/null |$SED 1q`"'/ + p + d + } + /^# .* home page:/b print + /^# General help using/b print + ' < "$progpath" + ret=$? + if test -z "$1"; then + exit $ret + fi +} + +# func_missing_arg argname +# Echo program name prefixed message to standard error and set global +# exit_cmd. +func_missing_arg () +{ + $opt_debug + + func_error "missing argument for $1." + exit_cmd=exit +} + + +# func_split_short_opt shortopt +# Set func_split_short_opt_name and func_split_short_opt_arg shell +# variables after splitting SHORTOPT after the 2nd character. +func_split_short_opt () +{ + my_sed_short_opt='1s/^\(..\).*$/\1/;q' + my_sed_short_rest='1s/^..\(.*\)$/\1/;q' + + func_split_short_opt_name=`$ECHO "$1" | $SED "$my_sed_short_opt"` + func_split_short_opt_arg=`$ECHO "$1" | $SED "$my_sed_short_rest"` +} # func_split_short_opt may be replaced by extended shell implementation + + +# func_split_long_opt longopt +# Set func_split_long_opt_name and func_split_long_opt_arg shell +# variables after splitting LONGOPT at the `=' sign. +func_split_long_opt () +{ + my_sed_long_opt='1s/^\(--[^=]*\)=.*/\1/;q' + my_sed_long_arg='1s/^--[^=]*=//' + + func_split_long_opt_name=`$ECHO "$1" | $SED "$my_sed_long_opt"` + func_split_long_opt_arg=`$ECHO "$1" | $SED "$my_sed_long_arg"` +} # func_split_long_opt may be replaced by extended shell implementation + +exit_cmd=: + + + + + +magic="%%%MAGIC variable%%%" +magic_exe="%%%MAGIC EXE variable%%%" + +# Global variables. +nonopt= +preserve_args= +lo2o="s/\\.lo\$/.${objext}/" +o2lo="s/\\.${objext}\$/.lo/" +extracted_archives= +extracted_serial=0 + +# If this variable is set in any of the actions, the command in it +# will be execed at the end. This prevents here-documents from being +# left over by shells. +exec_cmd= + +# func_append var value +# Append VALUE to the end of shell variable VAR. +func_append () +{ + eval "${1}=\$${1}\${2}" +} # func_append may be replaced by extended shell implementation + +# func_append_quoted var value +# Quote VALUE and append to the end of shell variable VAR, separated +# by a space. +func_append_quoted () +{ + func_quote_for_eval "${2}" + eval "${1}=\$${1}\\ \$func_quote_for_eval_result" +} # func_append_quoted may be replaced by extended shell implementation + + +# func_arith arithmetic-term... +func_arith () +{ + func_arith_result=`expr "${@}"` +} # func_arith may be replaced by extended shell implementation + + +# func_len string +# STRING may not start with a hyphen. +func_len () +{ + func_len_result=`expr "${1}" : ".*" 2>/dev/null || echo $max_cmd_len` +} # func_len may be replaced by extended shell implementation + + +# func_lo2o object +func_lo2o () +{ + func_lo2o_result=`$ECHO "${1}" | $SED "$lo2o"` +} # func_lo2o may be replaced by extended shell implementation + + +# func_xform libobj-or-source +func_xform () +{ + func_xform_result=`$ECHO "${1}" | $SED 's/\.[^.]*$/.lo/'` +} # func_xform may be replaced by extended shell implementation + + +# func_fatal_configuration arg... +# Echo program name prefixed message to standard error, followed by +# a configuration failure hint, and exit. +func_fatal_configuration () +{ + func_error ${1+"$@"} + func_error "See the $PACKAGE documentation for more information." + func_fatal_error "Fatal configuration error." +} + + +# func_config +# Display the configuration for all the tags in this script. +func_config () +{ + re_begincf='^# ### BEGIN LIBTOOL' + re_endcf='^# ### END LIBTOOL' + + # Default configuration. + $SED "1,/$re_begincf CONFIG/d;/$re_endcf CONFIG/,\$d" < "$progpath" + + # Now print the configurations for the tags. + for tagname in $taglist; do + $SED -n "/$re_begincf TAG CONFIG: $tagname\$/,/$re_endcf TAG CONFIG: $tagname\$/p" < "$progpath" + done + + exit $? +} + +# func_features +# Display the features supported by this script. +func_features () +{ + echo "host: $host" + if test "$build_libtool_libs" = yes; then + echo "enable shared libraries" + else + echo "disable shared libraries" + fi + if test "$build_old_libs" = yes; then + echo "enable static libraries" + else + echo "disable static libraries" + fi + + exit $? +} + +# func_enable_tag tagname +# Verify that TAGNAME is valid, and either flag an error and exit, or +# enable the TAGNAME tag. We also add TAGNAME to the global $taglist +# variable here. +func_enable_tag () +{ + # Global variable: + tagname="$1" + + re_begincf="^# ### BEGIN LIBTOOL TAG CONFIG: $tagname\$" + re_endcf="^# ### END LIBTOOL TAG CONFIG: $tagname\$" + sed_extractcf="/$re_begincf/,/$re_endcf/p" + + # Validate tagname. + case $tagname in + *[!-_A-Za-z0-9,/]*) + func_fatal_error "invalid tag name: $tagname" + ;; + esac + + # Don't test for the "default" C tag, as we know it's + # there but not specially marked. + case $tagname in + CC) ;; + *) + if $GREP "$re_begincf" "$progpath" >/dev/null 2>&1; then + taglist="$taglist $tagname" + + # Evaluate the configuration. Be careful to quote the path + # and the sed script, to avoid splitting on whitespace, but + # also don't use non-portable quotes within backquotes within + # quotes we have to do it in 2 steps: + extractedcf=`$SED -n -e "$sed_extractcf" < "$progpath"` + eval "$extractedcf" + else + func_error "ignoring unknown tag $tagname" + fi + ;; + esac +} + +# func_check_version_match +# Ensure that we are using m4 macros, and libtool script from the same +# release of libtool. +func_check_version_match () +{ + if test "$package_revision" != "$macro_revision"; then + if test "$VERSION" != "$macro_version"; then + if test -z "$macro_version"; then + cat >&2 <<_LT_EOF +$progname: Version mismatch error. This is $PACKAGE $VERSION, but the +$progname: definition of this LT_INIT comes from an older release. +$progname: You should recreate aclocal.m4 with macros from $PACKAGE $VERSION +$progname: and run autoconf again. +_LT_EOF + else + cat >&2 <<_LT_EOF +$progname: Version mismatch error. This is $PACKAGE $VERSION, but the +$progname: definition of this LT_INIT comes from $PACKAGE $macro_version. +$progname: You should recreate aclocal.m4 with macros from $PACKAGE $VERSION +$progname: and run autoconf again. +_LT_EOF + fi + else + cat >&2 <<_LT_EOF +$progname: Version mismatch error. This is $PACKAGE $VERSION, revision $package_revision, +$progname: but the definition of this LT_INIT comes from revision $macro_revision. +$progname: You should recreate aclocal.m4 with macros from revision $package_revision +$progname: of $PACKAGE $VERSION and run autoconf again. +_LT_EOF + fi + + exit $EXIT_MISMATCH + fi +} + + +# Shorthand for --mode=foo, only valid as the first argument +case $1 in +clean|clea|cle|cl) + shift; set dummy --mode clean ${1+"$@"}; shift + ;; +compile|compil|compi|comp|com|co|c) + shift; set dummy --mode compile ${1+"$@"}; shift + ;; +execute|execut|execu|exec|exe|ex|e) + shift; set dummy --mode execute ${1+"$@"}; shift + ;; +finish|finis|fini|fin|fi|f) + shift; set dummy --mode finish ${1+"$@"}; shift + ;; +install|instal|insta|inst|ins|in|i) + shift; set dummy --mode install ${1+"$@"}; shift + ;; +link|lin|li|l) + shift; set dummy --mode link ${1+"$@"}; shift + ;; +uninstall|uninstal|uninsta|uninst|unins|unin|uni|un|u) + shift; set dummy --mode uninstall ${1+"$@"}; shift + ;; +esac + + + +# Option defaults: +opt_debug=: +opt_dry_run=false +opt_config=false +opt_preserve_dup_deps=false +opt_features=false +opt_finish=false +opt_help=false +opt_help_all=false +opt_silent=: +opt_warning=: +opt_verbose=: +opt_silent=false +opt_verbose=false + + +# Parse options once, thoroughly. This comes as soon as possible in the +# script to make things like `--version' happen as quickly as we can. +{ + # this just eases exit handling + while test $# -gt 0; do + opt="$1" + shift + case $opt in + --debug|-x) opt_debug='set -x' + func_echo "enabling shell trace mode" + $opt_debug + ;; + --dry-run|--dryrun|-n) + opt_dry_run=: + ;; + --config) + opt_config=: +func_config + ;; + --dlopen|-dlopen) + optarg="$1" + opt_dlopen="${opt_dlopen+$opt_dlopen +}$optarg" + shift + ;; + --preserve-dup-deps) + opt_preserve_dup_deps=: + ;; + --features) + opt_features=: +func_features + ;; + --finish) + opt_finish=: +set dummy --mode finish ${1+"$@"}; shift + ;; + --help) + opt_help=: + ;; + --help-all) + opt_help_all=: +opt_help=': help-all' + ;; + --mode) + test $# = 0 && func_missing_arg $opt && break + optarg="$1" + opt_mode="$optarg" +case $optarg in + # Valid mode arguments: + clean|compile|execute|finish|install|link|relink|uninstall) ;; + + # Catch anything else as an error + *) func_error "invalid argument for $opt" + exit_cmd=exit + break + ;; +esac + shift + ;; + --no-silent|--no-quiet) + opt_silent=false +func_append preserve_args " $opt" + ;; + --no-warning|--no-warn) + opt_warning=false +func_append preserve_args " $opt" + ;; + --no-verbose) + opt_verbose=false +func_append preserve_args " $opt" + ;; + --silent|--quiet) + opt_silent=: +func_append preserve_args " $opt" + opt_verbose=false + ;; + --verbose|-v) + opt_verbose=: +func_append preserve_args " $opt" +opt_silent=false + ;; + --tag) + test $# = 0 && func_missing_arg $opt && break + optarg="$1" + opt_tag="$optarg" +func_append preserve_args " $opt $optarg" +func_enable_tag "$optarg" + shift + ;; + + -\?|-h) func_usage ;; + --help) func_help ;; + --version) func_version ;; + + # Separate optargs to long options: + --*=*) + func_split_long_opt "$opt" + set dummy "$func_split_long_opt_name" "$func_split_long_opt_arg" ${1+"$@"} + shift + ;; + + # Separate non-argument short options: + -\?*|-h*|-n*|-v*) + func_split_short_opt "$opt" + set dummy "$func_split_short_opt_name" "-$func_split_short_opt_arg" ${1+"$@"} + shift + ;; + + --) break ;; + -*) func_fatal_help "unrecognized option \`$opt'" ;; + *) set dummy "$opt" ${1+"$@"}; shift; break ;; + esac + done + + # Validate options: + + # save first non-option argument + if test "$#" -gt 0; then + nonopt="$opt" + shift + fi + + # preserve --debug + test "$opt_debug" = : || func_append preserve_args " --debug" + + case $host in + *cygwin* | *mingw* | *pw32* | *cegcc*) + # don't eliminate duplications in $postdeps and $predeps + opt_duplicate_compiler_generated_deps=: + ;; + *) + opt_duplicate_compiler_generated_deps=$opt_preserve_dup_deps + ;; + esac + + $opt_help || { + # Sanity checks first: + func_check_version_match + + if test "$build_libtool_libs" != yes && test "$build_old_libs" != yes; then + func_fatal_configuration "not configured to build any kind of library" + fi + + # Darwin sucks + eval std_shrext=\"$shrext_cmds\" + + # Only execute mode is allowed to have -dlopen flags. + if test -n "$opt_dlopen" && test "$opt_mode" != execute; then + func_error "unrecognized option \`-dlopen'" + $ECHO "$help" 1>&2 + exit $EXIT_FAILURE + fi + + # Change the help message to a mode-specific one. + generic_help="$help" + help="Try \`$progname --help --mode=$opt_mode' for more information." + } + + + # Bail if the options were screwed + $exit_cmd $EXIT_FAILURE +} + + + + +## ----------- ## +## Main. ## +## ----------- ## + +# func_lalib_p file +# True iff FILE is a libtool `.la' library or `.lo' object file. +# This function is only a basic sanity check; it will hardly flush out +# determined imposters. +func_lalib_p () +{ + test -f "$1" && + $SED -e 4q "$1" 2>/dev/null \ + | $GREP "^# Generated by .*$PACKAGE" > /dev/null 2>&1 +} + +# func_lalib_unsafe_p file +# True iff FILE is a libtool `.la' library or `.lo' object file. +# This function implements the same check as func_lalib_p without +# resorting to external programs. To this end, it redirects stdin and +# closes it afterwards, without saving the original file descriptor. +# As a safety measure, use it only where a negative result would be +# fatal anyway. Works if `file' does not exist. +func_lalib_unsafe_p () +{ + lalib_p=no + if test -f "$1" && test -r "$1" && exec 5<&0 <"$1"; then + for lalib_p_l in 1 2 3 4 + do + read lalib_p_line + case "$lalib_p_line" in + \#\ Generated\ by\ *$PACKAGE* ) lalib_p=yes; break;; + esac + done + exec 0<&5 5<&- + fi + test "$lalib_p" = yes +} + +# func_ltwrapper_script_p file +# True iff FILE is a libtool wrapper script +# This function is only a basic sanity check; it will hardly flush out +# determined imposters. +func_ltwrapper_script_p () +{ + func_lalib_p "$1" +} + +# func_ltwrapper_executable_p file +# True iff FILE is a libtool wrapper executable +# This function is only a basic sanity check; it will hardly flush out +# determined imposters. +func_ltwrapper_executable_p () +{ + func_ltwrapper_exec_suffix= + case $1 in + *.exe) ;; + *) func_ltwrapper_exec_suffix=.exe ;; + esac + $GREP "$magic_exe" "$1$func_ltwrapper_exec_suffix" >/dev/null 2>&1 +} + +# func_ltwrapper_scriptname file +# Assumes file is an ltwrapper_executable +# uses $file to determine the appropriate filename for a +# temporary ltwrapper_script. +func_ltwrapper_scriptname () +{ + func_dirname_and_basename "$1" "" "." + func_stripname '' '.exe' "$func_basename_result" + func_ltwrapper_scriptname_result="$func_dirname_result/$objdir/${func_stripname_result}_ltshwrapper" +} + +# func_ltwrapper_p file +# True iff FILE is a libtool wrapper script or wrapper executable +# This function is only a basic sanity check; it will hardly flush out +# determined imposters. +func_ltwrapper_p () +{ + func_ltwrapper_script_p "$1" || func_ltwrapper_executable_p "$1" +} + + +# func_execute_cmds commands fail_cmd +# Execute tilde-delimited COMMANDS. +# If FAIL_CMD is given, eval that upon failure. +# FAIL_CMD may read-access the current command in variable CMD! +func_execute_cmds () +{ + $opt_debug + save_ifs=$IFS; IFS='~' + for cmd in $1; do + IFS=$save_ifs + eval cmd=\"$cmd\" + func_show_eval "$cmd" "${2-:}" + done + IFS=$save_ifs +} + + +# func_source file +# Source FILE, adding directory component if necessary. +# Note that it is not necessary on cygwin/mingw to append a dot to +# FILE even if both FILE and FILE.exe exist: automatic-append-.exe +# behavior happens only for exec(3), not for open(2)! Also, sourcing +# `FILE.' does not work on cygwin managed mounts. +func_source () +{ + $opt_debug + case $1 in + */* | *\\*) . "$1" ;; + *) . "./$1" ;; + esac +} + + +# func_resolve_sysroot PATH +# Replace a leading = in PATH with a sysroot. Store the result into +# func_resolve_sysroot_result +func_resolve_sysroot () +{ + func_resolve_sysroot_result=$1 + case $func_resolve_sysroot_result in + =*) + func_stripname '=' '' "$func_resolve_sysroot_result" + func_resolve_sysroot_result=$lt_sysroot$func_stripname_result + ;; + esac +} + +# func_replace_sysroot PATH +# If PATH begins with the sysroot, replace it with = and +# store the result into func_replace_sysroot_result. +func_replace_sysroot () +{ + case "$lt_sysroot:$1" in + ?*:"$lt_sysroot"*) + func_stripname "$lt_sysroot" '' "$1" + func_replace_sysroot_result="=$func_stripname_result" + ;; + *) + # Including no sysroot. + func_replace_sysroot_result=$1 + ;; + esac +} + +# func_infer_tag arg +# Infer tagged configuration to use if any are available and +# if one wasn't chosen via the "--tag" command line option. +# Only attempt this if the compiler in the base compile +# command doesn't match the default compiler. +# arg is usually of the form 'gcc ...' +func_infer_tag () +{ + $opt_debug + if test -n "$available_tags" && test -z "$tagname"; then + CC_quoted= + for arg in $CC; do + func_append_quoted CC_quoted "$arg" + done + CC_expanded=`func_echo_all $CC` + CC_quoted_expanded=`func_echo_all $CC_quoted` + case $@ in + # Blanks in the command may have been stripped by the calling shell, + # but not from the CC environment variable when configure was run. + " $CC "* | "$CC "* | " $CC_expanded "* | "$CC_expanded "* | \ + " $CC_quoted"* | "$CC_quoted "* | " $CC_quoted_expanded "* | "$CC_quoted_expanded "*) ;; + # Blanks at the start of $base_compile will cause this to fail + # if we don't check for them as well. + *) + for z in $available_tags; do + if $GREP "^# ### BEGIN LIBTOOL TAG CONFIG: $z$" < "$progpath" > /dev/null; then + # Evaluate the configuration. + eval "`${SED} -n -e '/^# ### BEGIN LIBTOOL TAG CONFIG: '$z'$/,/^# ### END LIBTOOL TAG CONFIG: '$z'$/p' < $progpath`" + CC_quoted= + for arg in $CC; do + # Double-quote args containing other shell metacharacters. + func_append_quoted CC_quoted "$arg" + done + CC_expanded=`func_echo_all $CC` + CC_quoted_expanded=`func_echo_all $CC_quoted` + case "$@ " in + " $CC "* | "$CC "* | " $CC_expanded "* | "$CC_expanded "* | \ + " $CC_quoted"* | "$CC_quoted "* | " $CC_quoted_expanded "* | "$CC_quoted_expanded "*) + # The compiler in the base compile command matches + # the one in the tagged configuration. + # Assume this is the tagged configuration we want. + tagname=$z + break + ;; + esac + fi + done + # If $tagname still isn't set, then no tagged configuration + # was found and let the user know that the "--tag" command + # line option must be used. + if test -z "$tagname"; then + func_echo "unable to infer tagged configuration" + func_fatal_error "specify a tag with \`--tag'" +# else +# func_verbose "using $tagname tagged configuration" + fi + ;; + esac + fi +} + + + +# func_write_libtool_object output_name pic_name nonpic_name +# Create a libtool object file (analogous to a ".la" file), +# but don't create it if we're doing a dry run. +func_write_libtool_object () +{ + write_libobj=${1} + if test "$build_libtool_libs" = yes; then + write_lobj=\'${2}\' + else + write_lobj=none + fi + + if test "$build_old_libs" = yes; then + write_oldobj=\'${3}\' + else + write_oldobj=none + fi + + $opt_dry_run || { + cat >${write_libobj}T </dev/null` + if test "$?" -eq 0 && test -n "${func_convert_core_file_wine_to_w32_tmp}"; then + func_convert_core_file_wine_to_w32_result=`$ECHO "$func_convert_core_file_wine_to_w32_tmp" | + $SED -e "$lt_sed_naive_backslashify"` + else + func_convert_core_file_wine_to_w32_result= + fi + fi +} +# end: func_convert_core_file_wine_to_w32 + + +# func_convert_core_path_wine_to_w32 ARG +# Helper function used by path conversion functions when $build is *nix, and +# $host is mingw, cygwin, or some other w32 environment. Relies on a correctly +# configured wine environment available, with the winepath program in $build's +# $PATH. Assumes ARG has no leading or trailing path separator characters. +# +# ARG is path to be converted from $build format to win32. +# Result is available in $func_convert_core_path_wine_to_w32_result. +# Unconvertible file (directory) names in ARG are skipped; if no directory names +# are convertible, then the result may be empty. +func_convert_core_path_wine_to_w32 () +{ + $opt_debug + # unfortunately, winepath doesn't convert paths, only file names + func_convert_core_path_wine_to_w32_result="" + if test -n "$1"; then + oldIFS=$IFS + IFS=: + for func_convert_core_path_wine_to_w32_f in $1; do + IFS=$oldIFS + func_convert_core_file_wine_to_w32 "$func_convert_core_path_wine_to_w32_f" + if test -n "$func_convert_core_file_wine_to_w32_result" ; then + if test -z "$func_convert_core_path_wine_to_w32_result"; then + func_convert_core_path_wine_to_w32_result="$func_convert_core_file_wine_to_w32_result" + else + func_append func_convert_core_path_wine_to_w32_result ";$func_convert_core_file_wine_to_w32_result" + fi + fi + done + IFS=$oldIFS + fi +} +# end: func_convert_core_path_wine_to_w32 + + +# func_cygpath ARGS... +# Wrapper around calling the cygpath program via LT_CYGPATH. This is used when +# when (1) $build is *nix and Cygwin is hosted via a wine environment; or (2) +# $build is MSYS and $host is Cygwin, or (3) $build is Cygwin. In case (1) or +# (2), returns the Cygwin file name or path in func_cygpath_result (input +# file name or path is assumed to be in w32 format, as previously converted +# from $build's *nix or MSYS format). In case (3), returns the w32 file name +# or path in func_cygpath_result (input file name or path is assumed to be in +# Cygwin format). Returns an empty string on error. +# +# ARGS are passed to cygpath, with the last one being the file name or path to +# be converted. +# +# Specify the absolute *nix (or w32) name to cygpath in the LT_CYGPATH +# environment variable; do not put it in $PATH. +func_cygpath () +{ + $opt_debug + if test -n "$LT_CYGPATH" && test -f "$LT_CYGPATH"; then + func_cygpath_result=`$LT_CYGPATH "$@" 2>/dev/null` + if test "$?" -ne 0; then + # on failure, ensure result is empty + func_cygpath_result= + fi + else + func_cygpath_result= + func_error "LT_CYGPATH is empty or specifies non-existent file: \`$LT_CYGPATH'" + fi +} +#end: func_cygpath + + +# func_convert_core_msys_to_w32 ARG +# Convert file name or path ARG from MSYS format to w32 format. Return +# result in func_convert_core_msys_to_w32_result. +func_convert_core_msys_to_w32 () +{ + $opt_debug + # awkward: cmd appends spaces to result + func_convert_core_msys_to_w32_result=`( cmd //c echo "$1" ) 2>/dev/null | + $SED -e 's/[ ]*$//' -e "$lt_sed_naive_backslashify"` +} +#end: func_convert_core_msys_to_w32 + + +# func_convert_file_check ARG1 ARG2 +# Verify that ARG1 (a file name in $build format) was converted to $host +# format in ARG2. Otherwise, emit an error message, but continue (resetting +# func_to_host_file_result to ARG1). +func_convert_file_check () +{ + $opt_debug + if test -z "$2" && test -n "$1" ; then + func_error "Could not determine host file name corresponding to" + func_error " \`$1'" + func_error "Continuing, but uninstalled executables may not work." + # Fallback: + func_to_host_file_result="$1" + fi +} +# end func_convert_file_check + + +# func_convert_path_check FROM_PATHSEP TO_PATHSEP FROM_PATH TO_PATH +# Verify that FROM_PATH (a path in $build format) was converted to $host +# format in TO_PATH. Otherwise, emit an error message, but continue, resetting +# func_to_host_file_result to a simplistic fallback value (see below). +func_convert_path_check () +{ + $opt_debug + if test -z "$4" && test -n "$3"; then + func_error "Could not determine the host path corresponding to" + func_error " \`$3'" + func_error "Continuing, but uninstalled executables may not work." + # Fallback. This is a deliberately simplistic "conversion" and + # should not be "improved". See libtool.info. + if test "x$1" != "x$2"; then + lt_replace_pathsep_chars="s|$1|$2|g" + func_to_host_path_result=`echo "$3" | + $SED -e "$lt_replace_pathsep_chars"` + else + func_to_host_path_result="$3" + fi + fi +} +# end func_convert_path_check + + +# func_convert_path_front_back_pathsep FRONTPAT BACKPAT REPL ORIG +# Modifies func_to_host_path_result by prepending REPL if ORIG matches FRONTPAT +# and appending REPL if ORIG matches BACKPAT. +func_convert_path_front_back_pathsep () +{ + $opt_debug + case $4 in + $1 ) func_to_host_path_result="$3$func_to_host_path_result" + ;; + esac + case $4 in + $2 ) func_append func_to_host_path_result "$3" + ;; + esac +} +# end func_convert_path_front_back_pathsep + + +################################################## +# $build to $host FILE NAME CONVERSION FUNCTIONS # +################################################## +# invoked via `$to_host_file_cmd ARG' +# +# In each case, ARG is the path to be converted from $build to $host format. +# Result will be available in $func_to_host_file_result. + + +# func_to_host_file ARG +# Converts the file name ARG from $build format to $host format. Return result +# in func_to_host_file_result. +func_to_host_file () +{ + $opt_debug + $to_host_file_cmd "$1" +} +# end func_to_host_file + + +# func_to_tool_file ARG LAZY +# converts the file name ARG from $build format to toolchain format. Return +# result in func_to_tool_file_result. If the conversion in use is listed +# in (the comma separated) LAZY, no conversion takes place. +func_to_tool_file () +{ + $opt_debug + case ,$2, in + *,"$to_tool_file_cmd",*) + func_to_tool_file_result=$1 + ;; + *) + $to_tool_file_cmd "$1" + func_to_tool_file_result=$func_to_host_file_result + ;; + esac +} +# end func_to_tool_file + + +# func_convert_file_noop ARG +# Copy ARG to func_to_host_file_result. +func_convert_file_noop () +{ + func_to_host_file_result="$1" +} +# end func_convert_file_noop + + +# func_convert_file_msys_to_w32 ARG +# Convert file name ARG from (mingw) MSYS to (mingw) w32 format; automatic +# conversion to w32 is not available inside the cwrapper. Returns result in +# func_to_host_file_result. +func_convert_file_msys_to_w32 () +{ + $opt_debug + func_to_host_file_result="$1" + if test -n "$1"; then + func_convert_core_msys_to_w32 "$1" + func_to_host_file_result="$func_convert_core_msys_to_w32_result" + fi + func_convert_file_check "$1" "$func_to_host_file_result" +} +# end func_convert_file_msys_to_w32 + + +# func_convert_file_cygwin_to_w32 ARG +# Convert file name ARG from Cygwin to w32 format. Returns result in +# func_to_host_file_result. +func_convert_file_cygwin_to_w32 () +{ + $opt_debug + func_to_host_file_result="$1" + if test -n "$1"; then + # because $build is cygwin, we call "the" cygpath in $PATH; no need to use + # LT_CYGPATH in this case. + func_to_host_file_result=`cygpath -m "$1"` + fi + func_convert_file_check "$1" "$func_to_host_file_result" +} +# end func_convert_file_cygwin_to_w32 + + +# func_convert_file_nix_to_w32 ARG +# Convert file name ARG from *nix to w32 format. Requires a wine environment +# and a working winepath. Returns result in func_to_host_file_result. +func_convert_file_nix_to_w32 () +{ + $opt_debug + func_to_host_file_result="$1" + if test -n "$1"; then + func_convert_core_file_wine_to_w32 "$1" + func_to_host_file_result="$func_convert_core_file_wine_to_w32_result" + fi + func_convert_file_check "$1" "$func_to_host_file_result" +} +# end func_convert_file_nix_to_w32 + + +# func_convert_file_msys_to_cygwin ARG +# Convert file name ARG from MSYS to Cygwin format. Requires LT_CYGPATH set. +# Returns result in func_to_host_file_result. +func_convert_file_msys_to_cygwin () +{ + $opt_debug + func_to_host_file_result="$1" + if test -n "$1"; then + func_convert_core_msys_to_w32 "$1" + func_cygpath -u "$func_convert_core_msys_to_w32_result" + func_to_host_file_result="$func_cygpath_result" + fi + func_convert_file_check "$1" "$func_to_host_file_result" +} +# end func_convert_file_msys_to_cygwin + + +# func_convert_file_nix_to_cygwin ARG +# Convert file name ARG from *nix to Cygwin format. Requires Cygwin installed +# in a wine environment, working winepath, and LT_CYGPATH set. Returns result +# in func_to_host_file_result. +func_convert_file_nix_to_cygwin () +{ + $opt_debug + func_to_host_file_result="$1" + if test -n "$1"; then + # convert from *nix to w32, then use cygpath to convert from w32 to cygwin. + func_convert_core_file_wine_to_w32 "$1" + func_cygpath -u "$func_convert_core_file_wine_to_w32_result" + func_to_host_file_result="$func_cygpath_result" + fi + func_convert_file_check "$1" "$func_to_host_file_result" +} +# end func_convert_file_nix_to_cygwin + + +############################################# +# $build to $host PATH CONVERSION FUNCTIONS # +############################################# +# invoked via `$to_host_path_cmd ARG' +# +# In each case, ARG is the path to be converted from $build to $host format. +# The result will be available in $func_to_host_path_result. +# +# Path separators are also converted from $build format to $host format. If +# ARG begins or ends with a path separator character, it is preserved (but +# converted to $host format) on output. +# +# All path conversion functions are named using the following convention: +# file name conversion function : func_convert_file_X_to_Y () +# path conversion function : func_convert_path_X_to_Y () +# where, for any given $build/$host combination the 'X_to_Y' value is the +# same. If conversion functions are added for new $build/$host combinations, +# the two new functions must follow this pattern, or func_init_to_host_path_cmd +# will break. + + +# func_init_to_host_path_cmd +# Ensures that function "pointer" variable $to_host_path_cmd is set to the +# appropriate value, based on the value of $to_host_file_cmd. +to_host_path_cmd= +func_init_to_host_path_cmd () +{ + $opt_debug + if test -z "$to_host_path_cmd"; then + func_stripname 'func_convert_file_' '' "$to_host_file_cmd" + to_host_path_cmd="func_convert_path_${func_stripname_result}" + fi +} + + +# func_to_host_path ARG +# Converts the path ARG from $build format to $host format. Return result +# in func_to_host_path_result. +func_to_host_path () +{ + $opt_debug + func_init_to_host_path_cmd + $to_host_path_cmd "$1" +} +# end func_to_host_path + + +# func_convert_path_noop ARG +# Copy ARG to func_to_host_path_result. +func_convert_path_noop () +{ + func_to_host_path_result="$1" +} +# end func_convert_path_noop + + +# func_convert_path_msys_to_w32 ARG +# Convert path ARG from (mingw) MSYS to (mingw) w32 format; automatic +# conversion to w32 is not available inside the cwrapper. Returns result in +# func_to_host_path_result. +func_convert_path_msys_to_w32 () +{ + $opt_debug + func_to_host_path_result="$1" + if test -n "$1"; then + # Remove leading and trailing path separator characters from ARG. MSYS + # behavior is inconsistent here; cygpath turns them into '.;' and ';.'; + # and winepath ignores them completely. + func_stripname : : "$1" + func_to_host_path_tmp1=$func_stripname_result + func_convert_core_msys_to_w32 "$func_to_host_path_tmp1" + func_to_host_path_result="$func_convert_core_msys_to_w32_result" + func_convert_path_check : ";" \ + "$func_to_host_path_tmp1" "$func_to_host_path_result" + func_convert_path_front_back_pathsep ":*" "*:" ";" "$1" + fi +} +# end func_convert_path_msys_to_w32 + + +# func_convert_path_cygwin_to_w32 ARG +# Convert path ARG from Cygwin to w32 format. Returns result in +# func_to_host_file_result. +func_convert_path_cygwin_to_w32 () +{ + $opt_debug + func_to_host_path_result="$1" + if test -n "$1"; then + # See func_convert_path_msys_to_w32: + func_stripname : : "$1" + func_to_host_path_tmp1=$func_stripname_result + func_to_host_path_result=`cygpath -m -p "$func_to_host_path_tmp1"` + func_convert_path_check : ";" \ + "$func_to_host_path_tmp1" "$func_to_host_path_result" + func_convert_path_front_back_pathsep ":*" "*:" ";" "$1" + fi +} +# end func_convert_path_cygwin_to_w32 + + +# func_convert_path_nix_to_w32 ARG +# Convert path ARG from *nix to w32 format. Requires a wine environment and +# a working winepath. Returns result in func_to_host_file_result. +func_convert_path_nix_to_w32 () +{ + $opt_debug + func_to_host_path_result="$1" + if test -n "$1"; then + # See func_convert_path_msys_to_w32: + func_stripname : : "$1" + func_to_host_path_tmp1=$func_stripname_result + func_convert_core_path_wine_to_w32 "$func_to_host_path_tmp1" + func_to_host_path_result="$func_convert_core_path_wine_to_w32_result" + func_convert_path_check : ";" \ + "$func_to_host_path_tmp1" "$func_to_host_path_result" + func_convert_path_front_back_pathsep ":*" "*:" ";" "$1" + fi +} +# end func_convert_path_nix_to_w32 + + +# func_convert_path_msys_to_cygwin ARG +# Convert path ARG from MSYS to Cygwin format. Requires LT_CYGPATH set. +# Returns result in func_to_host_file_result. +func_convert_path_msys_to_cygwin () +{ + $opt_debug + func_to_host_path_result="$1" + if test -n "$1"; then + # See func_convert_path_msys_to_w32: + func_stripname : : "$1" + func_to_host_path_tmp1=$func_stripname_result + func_convert_core_msys_to_w32 "$func_to_host_path_tmp1" + func_cygpath -u -p "$func_convert_core_msys_to_w32_result" + func_to_host_path_result="$func_cygpath_result" + func_convert_path_check : : \ + "$func_to_host_path_tmp1" "$func_to_host_path_result" + func_convert_path_front_back_pathsep ":*" "*:" : "$1" + fi +} +# end func_convert_path_msys_to_cygwin + + +# func_convert_path_nix_to_cygwin ARG +# Convert path ARG from *nix to Cygwin format. Requires Cygwin installed in a +# a wine environment, working winepath, and LT_CYGPATH set. Returns result in +# func_to_host_file_result. +func_convert_path_nix_to_cygwin () +{ + $opt_debug + func_to_host_path_result="$1" + if test -n "$1"; then + # Remove leading and trailing path separator characters from + # ARG. msys behavior is inconsistent here, cygpath turns them + # into '.;' and ';.', and winepath ignores them completely. + func_stripname : : "$1" + func_to_host_path_tmp1=$func_stripname_result + func_convert_core_path_wine_to_w32 "$func_to_host_path_tmp1" + func_cygpath -u -p "$func_convert_core_path_wine_to_w32_result" + func_to_host_path_result="$func_cygpath_result" + func_convert_path_check : : \ + "$func_to_host_path_tmp1" "$func_to_host_path_result" + func_convert_path_front_back_pathsep ":*" "*:" : "$1" + fi +} +# end func_convert_path_nix_to_cygwin + + +# func_mode_compile arg... +func_mode_compile () +{ + $opt_debug + # Get the compilation command and the source file. + base_compile= + srcfile="$nonopt" # always keep a non-empty value in "srcfile" + suppress_opt=yes + suppress_output= + arg_mode=normal + libobj= + later= + pie_flag= + + for arg + do + case $arg_mode in + arg ) + # do not "continue". Instead, add this to base_compile + lastarg="$arg" + arg_mode=normal + ;; + + target ) + libobj="$arg" + arg_mode=normal + continue + ;; + + normal ) + # Accept any command-line options. + case $arg in + -o) + test -n "$libobj" && \ + func_fatal_error "you cannot specify \`-o' more than once" + arg_mode=target + continue + ;; + + -pie | -fpie | -fPIE) + func_append pie_flag " $arg" + continue + ;; + + -shared | -static | -prefer-pic | -prefer-non-pic) + func_append later " $arg" + continue + ;; + + -no-suppress) + suppress_opt=no + continue + ;; + + -Xcompiler) + arg_mode=arg # the next one goes into the "base_compile" arg list + continue # The current "srcfile" will either be retained or + ;; # replaced later. I would guess that would be a bug. + + -Wc,*) + func_stripname '-Wc,' '' "$arg" + args=$func_stripname_result + lastarg= + save_ifs="$IFS"; IFS=',' + for arg in $args; do + IFS="$save_ifs" + func_append_quoted lastarg "$arg" + done + IFS="$save_ifs" + func_stripname ' ' '' "$lastarg" + lastarg=$func_stripname_result + + # Add the arguments to base_compile. + func_append base_compile " $lastarg" + continue + ;; + + *) + # Accept the current argument as the source file. + # The previous "srcfile" becomes the current argument. + # + lastarg="$srcfile" + srcfile="$arg" + ;; + esac # case $arg + ;; + esac # case $arg_mode + + # Aesthetically quote the previous argument. + func_append_quoted base_compile "$lastarg" + done # for arg + + case $arg_mode in + arg) + func_fatal_error "you must specify an argument for -Xcompile" + ;; + target) + func_fatal_error "you must specify a target with \`-o'" + ;; + *) + # Get the name of the library object. + test -z "$libobj" && { + func_basename "$srcfile" + libobj="$func_basename_result" + } + ;; + esac + + # Recognize several different file suffixes. + # If the user specifies -o file.o, it is replaced with file.lo + case $libobj in + *.[cCFSifmso] | \ + *.ada | *.adb | *.ads | *.asm | \ + *.c++ | *.cc | *.ii | *.class | *.cpp | *.cxx | \ + *.[fF][09]? | *.for | *.java | *.go | *.obj | *.sx | *.cu | *.cup) + func_xform "$libobj" + libobj=$func_xform_result + ;; + esac + + case $libobj in + *.lo) func_lo2o "$libobj"; obj=$func_lo2o_result ;; + *) + func_fatal_error "cannot determine name of library object from \`$libobj'" + ;; + esac + + func_infer_tag $base_compile + + for arg in $later; do + case $arg in + -shared) + test "$build_libtool_libs" != yes && \ + func_fatal_configuration "can not build a shared library" + build_old_libs=no + continue + ;; + + -static) + build_libtool_libs=no + build_old_libs=yes + continue + ;; + + -prefer-pic) + pic_mode=yes + continue + ;; + + -prefer-non-pic) + pic_mode=no + continue + ;; + esac + done + + func_quote_for_eval "$libobj" + test "X$libobj" != "X$func_quote_for_eval_result" \ + && $ECHO "X$libobj" | $GREP '[]~#^*{};<>?"'"'"' &()|`$[]' \ + && func_warning "libobj name \`$libobj' may not contain shell special characters." + func_dirname_and_basename "$obj" "/" "" + objname="$func_basename_result" + xdir="$func_dirname_result" + lobj=${xdir}$objdir/$objname + + test -z "$base_compile" && \ + func_fatal_help "you must specify a compilation command" + + # Delete any leftover library objects. + if test "$build_old_libs" = yes; then + removelist="$obj $lobj $libobj ${libobj}T" + else + removelist="$lobj $libobj ${libobj}T" + fi + + # On Cygwin there's no "real" PIC flag so we must build both object types + case $host_os in + cygwin* | mingw* | pw32* | os2* | cegcc*) + pic_mode=default + ;; + esac + if test "$pic_mode" = no && test "$deplibs_check_method" != pass_all; then + # non-PIC code in shared libraries is not supported + pic_mode=default + fi + + # Calculate the filename of the output object if compiler does + # not support -o with -c + if test "$compiler_c_o" = no; then + output_obj=`$ECHO "$srcfile" | $SED 's%^.*/%%; s%\.[^.]*$%%'`.${objext} + lockfile="$output_obj.lock" + else + output_obj= + need_locks=no + lockfile= + fi + + # Lock this critical section if it is needed + # We use this script file to make the link, it avoids creating a new file + if test "$need_locks" = yes; then + until $opt_dry_run || ln "$progpath" "$lockfile" 2>/dev/null; do + func_echo "Waiting for $lockfile to be removed" + sleep 2 + done + elif test "$need_locks" = warn; then + if test -f "$lockfile"; then + $ECHO "\ +*** ERROR, $lockfile exists and contains: +`cat $lockfile 2>/dev/null` + +This indicates that another process is trying to use the same +temporary object file, and libtool could not work around it because +your compiler does not support \`-c' and \`-o' together. If you +repeat this compilation, it may succeed, by chance, but you had better +avoid parallel builds (make -j) in this platform, or get a better +compiler." + + $opt_dry_run || $RM $removelist + exit $EXIT_FAILURE + fi + func_append removelist " $output_obj" + $ECHO "$srcfile" > "$lockfile" + fi + + $opt_dry_run || $RM $removelist + func_append removelist " $lockfile" + trap '$opt_dry_run || $RM $removelist; exit $EXIT_FAILURE' 1 2 15 + + func_to_tool_file "$srcfile" func_convert_file_msys_to_w32 + srcfile=$func_to_tool_file_result + func_quote_for_eval "$srcfile" + qsrcfile=$func_quote_for_eval_result + + # Only build a PIC object if we are building libtool libraries. + if test "$build_libtool_libs" = yes; then + # Without this assignment, base_compile gets emptied. + fbsd_hideous_sh_bug=$base_compile + + if test "$pic_mode" != no; then + command="$base_compile $qsrcfile $pic_flag" + else + # Don't build PIC code + command="$base_compile $qsrcfile" + fi + + func_mkdir_p "$xdir$objdir" + + if test -z "$output_obj"; then + # Place PIC objects in $objdir + func_append command " -o $lobj" + fi + + func_show_eval_locale "$command" \ + 'test -n "$output_obj" && $RM $removelist; exit $EXIT_FAILURE' + + if test "$need_locks" = warn && + test "X`cat $lockfile 2>/dev/null`" != "X$srcfile"; then + $ECHO "\ +*** ERROR, $lockfile contains: +`cat $lockfile 2>/dev/null` + +but it should contain: +$srcfile + +This indicates that another process is trying to use the same +temporary object file, and libtool could not work around it because +your compiler does not support \`-c' and \`-o' together. If you +repeat this compilation, it may succeed, by chance, but you had better +avoid parallel builds (make -j) in this platform, or get a better +compiler." + + $opt_dry_run || $RM $removelist + exit $EXIT_FAILURE + fi + + # Just move the object if needed, then go on to compile the next one + if test -n "$output_obj" && test "X$output_obj" != "X$lobj"; then + func_show_eval '$MV "$output_obj" "$lobj"' \ + 'error=$?; $opt_dry_run || $RM $removelist; exit $error' + fi + + # Allow error messages only from the first compilation. + if test "$suppress_opt" = yes; then + suppress_output=' >/dev/null 2>&1' + fi + fi + + # Only build a position-dependent object if we build old libraries. + if test "$build_old_libs" = yes; then + if test "$pic_mode" != yes; then + # Don't build PIC code + command="$base_compile $qsrcfile$pie_flag" + else + command="$base_compile $qsrcfile $pic_flag" + fi + if test "$compiler_c_o" = yes; then + func_append command " -o $obj" + fi + + # Suppress compiler output if we already did a PIC compilation. + func_append command "$suppress_output" + func_show_eval_locale "$command" \ + '$opt_dry_run || $RM $removelist; exit $EXIT_FAILURE' + + if test "$need_locks" = warn && + test "X`cat $lockfile 2>/dev/null`" != "X$srcfile"; then + $ECHO "\ +*** ERROR, $lockfile contains: +`cat $lockfile 2>/dev/null` + +but it should contain: +$srcfile + +This indicates that another process is trying to use the same +temporary object file, and libtool could not work around it because +your compiler does not support \`-c' and \`-o' together. If you +repeat this compilation, it may succeed, by chance, but you had better +avoid parallel builds (make -j) in this platform, or get a better +compiler." + + $opt_dry_run || $RM $removelist + exit $EXIT_FAILURE + fi + + # Just move the object if needed + if test -n "$output_obj" && test "X$output_obj" != "X$obj"; then + func_show_eval '$MV "$output_obj" "$obj"' \ + 'error=$?; $opt_dry_run || $RM $removelist; exit $error' + fi + fi + + $opt_dry_run || { + func_write_libtool_object "$libobj" "$objdir/$objname" "$objname" + + # Unlock the critical section if it was locked + if test "$need_locks" != no; then + removelist=$lockfile + $RM "$lockfile" + fi + } + + exit $EXIT_SUCCESS +} + +$opt_help || { + test "$opt_mode" = compile && func_mode_compile ${1+"$@"} +} + +func_mode_help () +{ + # We need to display help for each of the modes. + case $opt_mode in + "") + # Generic help is extracted from the usage comments + # at the start of this file. + func_help + ;; + + clean) + $ECHO \ +"Usage: $progname [OPTION]... --mode=clean RM [RM-OPTION]... FILE... + +Remove files from the build directory. + +RM is the name of the program to use to delete files associated with each FILE +(typically \`/bin/rm'). RM-OPTIONS are options (such as \`-f') to be passed +to RM. + +If FILE is a libtool library, object or program, all the files associated +with it are deleted. Otherwise, only FILE itself is deleted using RM." + ;; + + compile) + $ECHO \ +"Usage: $progname [OPTION]... --mode=compile COMPILE-COMMAND... SOURCEFILE + +Compile a source file into a libtool library object. + +This mode accepts the following additional options: + + -o OUTPUT-FILE set the output file name to OUTPUT-FILE + -no-suppress do not suppress compiler output for multiple passes + -prefer-pic try to build PIC objects only + -prefer-non-pic try to build non-PIC objects only + -shared do not build a \`.o' file suitable for static linking + -static only build a \`.o' file suitable for static linking + -Wc,FLAG pass FLAG directly to the compiler + +COMPILE-COMMAND is a command to be used in creating a \`standard' object file +from the given SOURCEFILE. + +The output file name is determined by removing the directory component from +SOURCEFILE, then substituting the C source code suffix \`.c' with the +library object suffix, \`.lo'." + ;; + + execute) + $ECHO \ +"Usage: $progname [OPTION]... --mode=execute COMMAND [ARGS]... + +Automatically set library path, then run a program. + +This mode accepts the following additional options: + + -dlopen FILE add the directory containing FILE to the library path + +This mode sets the library path environment variable according to \`-dlopen' +flags. + +If any of the ARGS are libtool executable wrappers, then they are translated +into their corresponding uninstalled binary, and any of their required library +directories are added to the library path. + +Then, COMMAND is executed, with ARGS as arguments." + ;; + + finish) + $ECHO \ +"Usage: $progname [OPTION]... --mode=finish [LIBDIR]... + +Complete the installation of libtool libraries. + +Each LIBDIR is a directory that contains libtool libraries. + +The commands that this mode executes may require superuser privileges. Use +the \`--dry-run' option if you just want to see what would be executed." + ;; + + install) + $ECHO \ +"Usage: $progname [OPTION]... --mode=install INSTALL-COMMAND... + +Install executables or libraries. + +INSTALL-COMMAND is the installation command. The first component should be +either the \`install' or \`cp' program. + +The following components of INSTALL-COMMAND are treated specially: + + -inst-prefix-dir PREFIX-DIR Use PREFIX-DIR as a staging area for installation + +The rest of the components are interpreted as arguments to that command (only +BSD-compatible install options are recognized)." + ;; + + link) + $ECHO \ +"Usage: $progname [OPTION]... --mode=link LINK-COMMAND... + +Link object files or libraries together to form another library, or to +create an executable program. + +LINK-COMMAND is a command using the C compiler that you would use to create +a program from several object files. + +The following components of LINK-COMMAND are treated specially: + + -all-static do not do any dynamic linking at all + -avoid-version do not add a version suffix if possible + -bindir BINDIR specify path to binaries directory (for systems where + libraries must be found in the PATH setting at runtime) + -dlopen FILE \`-dlpreopen' FILE if it cannot be dlopened at runtime + -dlpreopen FILE link in FILE and add its symbols to lt_preloaded_symbols + -export-dynamic allow symbols from OUTPUT-FILE to be resolved with dlsym(3) + -export-symbols SYMFILE + try to export only the symbols listed in SYMFILE + -export-symbols-regex REGEX + try to export only the symbols matching REGEX + -LLIBDIR search LIBDIR for required installed libraries + -lNAME OUTPUT-FILE requires the installed library libNAME + -module build a library that can dlopened + -no-fast-install disable the fast-install mode + -no-install link a not-installable executable + -no-undefined declare that a library does not refer to external symbols + -o OUTPUT-FILE create OUTPUT-FILE from the specified objects + -objectlist FILE Use a list of object files found in FILE to specify objects + -precious-files-regex REGEX + don't remove output files matching REGEX + -release RELEASE specify package release information + -rpath LIBDIR the created library will eventually be installed in LIBDIR + -R[ ]LIBDIR add LIBDIR to the runtime path of programs and libraries + -shared only do dynamic linking of libtool libraries + -shrext SUFFIX override the standard shared library file extension + -static do not do any dynamic linking of uninstalled libtool libraries + -static-libtool-libs + do not do any dynamic linking of libtool libraries + -version-info CURRENT[:REVISION[:AGE]] + specify library version info [each variable defaults to 0] + -weak LIBNAME declare that the target provides the LIBNAME interface + -Wc,FLAG + -Xcompiler FLAG pass linker-specific FLAG directly to the compiler + -Wl,FLAG + -Xlinker FLAG pass linker-specific FLAG directly to the linker + -XCClinker FLAG pass link-specific FLAG to the compiler driver (CC) + +All other options (arguments beginning with \`-') are ignored. + +Every other argument is treated as a filename. Files ending in \`.la' are +treated as uninstalled libtool libraries, other files are standard or library +object files. + +If the OUTPUT-FILE ends in \`.la', then a libtool library is created, +only library objects (\`.lo' files) may be specified, and \`-rpath' is +required, except when creating a convenience library. + +If OUTPUT-FILE ends in \`.a' or \`.lib', then a standard library is created +using \`ar' and \`ranlib', or on Windows using \`lib'. + +If OUTPUT-FILE ends in \`.lo' or \`.${objext}', then a reloadable object file +is created, otherwise an executable program is created." + ;; + + uninstall) + $ECHO \ +"Usage: $progname [OPTION]... --mode=uninstall RM [RM-OPTION]... FILE... + +Remove libraries from an installation directory. + +RM is the name of the program to use to delete files associated with each FILE +(typically \`/bin/rm'). RM-OPTIONS are options (such as \`-f') to be passed +to RM. + +If FILE is a libtool library, all the files associated with it are deleted. +Otherwise, only FILE itself is deleted using RM." + ;; + + *) + func_fatal_help "invalid operation mode \`$opt_mode'" + ;; + esac + + echo + $ECHO "Try \`$progname --help' for more information about other modes." +} + +# Now that we've collected a possible --mode arg, show help if necessary +if $opt_help; then + if test "$opt_help" = :; then + func_mode_help + else + { + func_help noexit + for opt_mode in compile link execute install finish uninstall clean; do + func_mode_help + done + } | sed -n '1p; 2,$s/^Usage:/ or: /p' + { + func_help noexit + for opt_mode in compile link execute install finish uninstall clean; do + echo + func_mode_help + done + } | + sed '1d + /^When reporting/,/^Report/{ + H + d + } + $x + /information about other modes/d + /more detailed .*MODE/d + s/^Usage:.*--mode=\([^ ]*\) .*/Description of \1 mode:/' + fi + exit $? +fi + + +# func_mode_execute arg... +func_mode_execute () +{ + $opt_debug + # The first argument is the command name. + cmd="$nonopt" + test -z "$cmd" && \ + func_fatal_help "you must specify a COMMAND" + + # Handle -dlopen flags immediately. + for file in $opt_dlopen; do + test -f "$file" \ + || func_fatal_help "\`$file' is not a file" + + dir= + case $file in + *.la) + func_resolve_sysroot "$file" + file=$func_resolve_sysroot_result + + # Check to see that this really is a libtool archive. + func_lalib_unsafe_p "$file" \ + || func_fatal_help "\`$lib' is not a valid libtool archive" + + # Read the libtool library. + dlname= + library_names= + func_source "$file" + + # Skip this library if it cannot be dlopened. + if test -z "$dlname"; then + # Warn if it was a shared library. + test -n "$library_names" && \ + func_warning "\`$file' was not linked with \`-export-dynamic'" + continue + fi + + func_dirname "$file" "" "." + dir="$func_dirname_result" + + if test -f "$dir/$objdir/$dlname"; then + func_append dir "/$objdir" + else + if test ! -f "$dir/$dlname"; then + func_fatal_error "cannot find \`$dlname' in \`$dir' or \`$dir/$objdir'" + fi + fi + ;; + + *.lo) + # Just add the directory containing the .lo file. + func_dirname "$file" "" "." + dir="$func_dirname_result" + ;; + + *) + func_warning "\`-dlopen' is ignored for non-libtool libraries and objects" + continue + ;; + esac + + # Get the absolute pathname. + absdir=`cd "$dir" && pwd` + test -n "$absdir" && dir="$absdir" + + # Now add the directory to shlibpath_var. + if eval "test -z \"\$$shlibpath_var\""; then + eval "$shlibpath_var=\"\$dir\"" + else + eval "$shlibpath_var=\"\$dir:\$$shlibpath_var\"" + fi + done + + # This variable tells wrapper scripts just to set shlibpath_var + # rather than running their programs. + libtool_execute_magic="$magic" + + # Check if any of the arguments is a wrapper script. + args= + for file + do + case $file in + -* | *.la | *.lo ) ;; + *) + # Do a test to see if this is really a libtool program. + if func_ltwrapper_script_p "$file"; then + func_source "$file" + # Transform arg to wrapped name. + file="$progdir/$program" + elif func_ltwrapper_executable_p "$file"; then + func_ltwrapper_scriptname "$file" + func_source "$func_ltwrapper_scriptname_result" + # Transform arg to wrapped name. + file="$progdir/$program" + fi + ;; + esac + # Quote arguments (to preserve shell metacharacters). + func_append_quoted args "$file" + done + + if test "X$opt_dry_run" = Xfalse; then + if test -n "$shlibpath_var"; then + # Export the shlibpath_var. + eval "export $shlibpath_var" + fi + + # Restore saved environment variables + for lt_var in LANG LANGUAGE LC_ALL LC_CTYPE LC_COLLATE LC_MESSAGES + do + eval "if test \"\${save_$lt_var+set}\" = set; then + $lt_var=\$save_$lt_var; export $lt_var + else + $lt_unset $lt_var + fi" + done + + # Now prepare to actually exec the command. + exec_cmd="\$cmd$args" + else + # Display what would be done. + if test -n "$shlibpath_var"; then + eval "\$ECHO \"\$shlibpath_var=\$$shlibpath_var\"" + echo "export $shlibpath_var" + fi + $ECHO "$cmd$args" + exit $EXIT_SUCCESS + fi +} + +test "$opt_mode" = execute && func_mode_execute ${1+"$@"} + + +# func_mode_finish arg... +func_mode_finish () +{ + $opt_debug + libs= + libdirs= + admincmds= + + for opt in "$nonopt" ${1+"$@"} + do + if test -d "$opt"; then + func_append libdirs " $opt" + + elif test -f "$opt"; then + if func_lalib_unsafe_p "$opt"; then + func_append libs " $opt" + else + func_warning "\`$opt' is not a valid libtool archive" + fi + + else + func_fatal_error "invalid argument \`$opt'" + fi + done + + if test -n "$libs"; then + if test -n "$lt_sysroot"; then + sysroot_regex=`$ECHO "$lt_sysroot" | $SED "$sed_make_literal_regex"` + sysroot_cmd="s/\([ ']\)$sysroot_regex/\1/g;" + else + sysroot_cmd= + fi + + # Remove sysroot references + if $opt_dry_run; then + for lib in $libs; do + echo "removing references to $lt_sysroot and \`=' prefixes from $lib" + done + else + tmpdir=`func_mktempdir` + for lib in $libs; do + sed -e "${sysroot_cmd} s/\([ ']-[LR]\)=/\1/g; s/\([ ']\)=/\1/g" $lib \ + > $tmpdir/tmp-la + mv -f $tmpdir/tmp-la $lib + done + ${RM}r "$tmpdir" + fi + fi + + if test -n "$finish_cmds$finish_eval" && test -n "$libdirs"; then + for libdir in $libdirs; do + if test -n "$finish_cmds"; then + # Do each command in the finish commands. + func_execute_cmds "$finish_cmds" 'admincmds="$admincmds +'"$cmd"'"' + fi + if test -n "$finish_eval"; then + # Do the single finish_eval. + eval cmds=\"$finish_eval\" + $opt_dry_run || eval "$cmds" || func_append admincmds " + $cmds" + fi + done + fi + + # Exit here if they wanted silent mode. + $opt_silent && exit $EXIT_SUCCESS + + if test -n "$finish_cmds$finish_eval" && test -n "$libdirs"; then + echo "----------------------------------------------------------------------" + echo "Libraries have been installed in:" + for libdir in $libdirs; do + $ECHO " $libdir" + done + echo + echo "If you ever happen to want to link against installed libraries" + echo "in a given directory, LIBDIR, you must either use libtool, and" + echo "specify the full pathname of the library, or use the \`-LLIBDIR'" + echo "flag during linking and do at least one of the following:" + if test -n "$shlibpath_var"; then + echo " - add LIBDIR to the \`$shlibpath_var' environment variable" + echo " during execution" + fi + if test -n "$runpath_var"; then + echo " - add LIBDIR to the \`$runpath_var' environment variable" + echo " during linking" + fi + if test -n "$hardcode_libdir_flag_spec"; then + libdir=LIBDIR + eval flag=\"$hardcode_libdir_flag_spec\" + + $ECHO " - use the \`$flag' linker flag" + fi + if test -n "$admincmds"; then + $ECHO " - have your system administrator run these commands:$admincmds" + fi + if test -f /etc/ld.so.conf; then + echo " - have your system administrator add LIBDIR to \`/etc/ld.so.conf'" + fi + echo + + echo "See any operating system documentation about shared libraries for" + case $host in + solaris2.[6789]|solaris2.1[0-9]) + echo "more information, such as the ld(1), crle(1) and ld.so(8) manual" + echo "pages." + ;; + *) + echo "more information, such as the ld(1) and ld.so(8) manual pages." + ;; + esac + echo "----------------------------------------------------------------------" + fi + exit $EXIT_SUCCESS +} + +test "$opt_mode" = finish && func_mode_finish ${1+"$@"} + + +# func_mode_install arg... +func_mode_install () +{ + $opt_debug + # There may be an optional sh(1) argument at the beginning of + # install_prog (especially on Windows NT). + if test "$nonopt" = "$SHELL" || test "$nonopt" = /bin/sh || + # Allow the use of GNU shtool's install command. + case $nonopt in *shtool*) :;; *) false;; esac; then + # Aesthetically quote it. + func_quote_for_eval "$nonopt" + install_prog="$func_quote_for_eval_result " + arg=$1 + shift + else + install_prog= + arg=$nonopt + fi + + # The real first argument should be the name of the installation program. + # Aesthetically quote it. + func_quote_for_eval "$arg" + func_append install_prog "$func_quote_for_eval_result" + install_shared_prog=$install_prog + case " $install_prog " in + *[\\\ /]cp\ *) install_cp=: ;; + *) install_cp=false ;; + esac + + # We need to accept at least all the BSD install flags. + dest= + files= + opts= + prev= + install_type= + isdir=no + stripme= + no_mode=: + for arg + do + arg2= + if test -n "$dest"; then + func_append files " $dest" + dest=$arg + continue + fi + + case $arg in + -d) isdir=yes ;; + -f) + if $install_cp; then :; else + prev=$arg + fi + ;; + -g | -m | -o) + prev=$arg + ;; + -s) + stripme=" -s" + continue + ;; + -*) + ;; + *) + # If the previous option needed an argument, then skip it. + if test -n "$prev"; then + if test "x$prev" = x-m && test -n "$install_override_mode"; then + arg2=$install_override_mode + no_mode=false + fi + prev= + else + dest=$arg + continue + fi + ;; + esac + + # Aesthetically quote the argument. + func_quote_for_eval "$arg" + func_append install_prog " $func_quote_for_eval_result" + if test -n "$arg2"; then + func_quote_for_eval "$arg2" + fi + func_append install_shared_prog " $func_quote_for_eval_result" + done + + test -z "$install_prog" && \ + func_fatal_help "you must specify an install program" + + test -n "$prev" && \ + func_fatal_help "the \`$prev' option requires an argument" + + if test -n "$install_override_mode" && $no_mode; then + if $install_cp; then :; else + func_quote_for_eval "$install_override_mode" + func_append install_shared_prog " -m $func_quote_for_eval_result" + fi + fi + + if test -z "$files"; then + if test -z "$dest"; then + func_fatal_help "no file or destination specified" + else + func_fatal_help "you must specify a destination" + fi + fi + + # Strip any trailing slash from the destination. + func_stripname '' '/' "$dest" + dest=$func_stripname_result + + # Check to see that the destination is a directory. + test -d "$dest" && isdir=yes + if test "$isdir" = yes; then + destdir="$dest" + destname= + else + func_dirname_and_basename "$dest" "" "." + destdir="$func_dirname_result" + destname="$func_basename_result" + + # Not a directory, so check to see that there is only one file specified. + set dummy $files; shift + test "$#" -gt 1 && \ + func_fatal_help "\`$dest' is not a directory" + fi + case $destdir in + [\\/]* | [A-Za-z]:[\\/]*) ;; + *) + for file in $files; do + case $file in + *.lo) ;; + *) + func_fatal_help "\`$destdir' must be an absolute directory name" + ;; + esac + done + ;; + esac + + # This variable tells wrapper scripts just to set variables rather + # than running their programs. + libtool_install_magic="$magic" + + staticlibs= + future_libdirs= + current_libdirs= + for file in $files; do + + # Do each installation. + case $file in + *.$libext) + # Do the static libraries later. + func_append staticlibs " $file" + ;; + + *.la) + func_resolve_sysroot "$file" + file=$func_resolve_sysroot_result + + # Check to see that this really is a libtool archive. + func_lalib_unsafe_p "$file" \ + || func_fatal_help "\`$file' is not a valid libtool archive" + + library_names= + old_library= + relink_command= + func_source "$file" + + # Add the libdir to current_libdirs if it is the destination. + if test "X$destdir" = "X$libdir"; then + case "$current_libdirs " in + *" $libdir "*) ;; + *) func_append current_libdirs " $libdir" ;; + esac + else + # Note the libdir as a future libdir. + case "$future_libdirs " in + *" $libdir "*) ;; + *) func_append future_libdirs " $libdir" ;; + esac + fi + + func_dirname "$file" "/" "" + dir="$func_dirname_result" + func_append dir "$objdir" + + if test -n "$relink_command"; then + # Determine the prefix the user has applied to our future dir. + inst_prefix_dir=`$ECHO "$destdir" | $SED -e "s%$libdir\$%%"` + + # Don't allow the user to place us outside of our expected + # location b/c this prevents finding dependent libraries that + # are installed to the same prefix. + # At present, this check doesn't affect windows .dll's that + # are installed into $libdir/../bin (currently, that works fine) + # but it's something to keep an eye on. + test "$inst_prefix_dir" = "$destdir" && \ + func_fatal_error "error: cannot install \`$file' to a directory not ending in $libdir" + + if test -n "$inst_prefix_dir"; then + # Stick the inst_prefix_dir data into the link command. + relink_command=`$ECHO "$relink_command" | $SED "s%@inst_prefix_dir@%-inst-prefix-dir $inst_prefix_dir%"` + else + relink_command=`$ECHO "$relink_command" | $SED "s%@inst_prefix_dir@%%"` + fi + + func_warning "relinking \`$file'" + func_show_eval "$relink_command" \ + 'func_fatal_error "error: relink \`$file'\'' with the above command before installing it"' + fi + + # See the names of the shared library. + set dummy $library_names; shift + if test -n "$1"; then + realname="$1" + shift + + srcname="$realname" + test -n "$relink_command" && srcname="$realname"T + + # Install the shared library and build the symlinks. + func_show_eval "$install_shared_prog $dir/$srcname $destdir/$realname" \ + 'exit $?' + tstripme="$stripme" + case $host_os in + cygwin* | mingw* | pw32* | cegcc*) + case $realname in + *.dll.a) + tstripme="" + ;; + esac + ;; + esac + if test -n "$tstripme" && test -n "$striplib"; then + func_show_eval "$striplib $destdir/$realname" 'exit $?' + fi + + if test "$#" -gt 0; then + # Delete the old symlinks, and create new ones. + # Try `ln -sf' first, because the `ln' binary might depend on + # the symlink we replace! Solaris /bin/ln does not understand -f, + # so we also need to try rm && ln -s. + for linkname + do + test "$linkname" != "$realname" \ + && func_show_eval "(cd $destdir && { $LN_S -f $realname $linkname || { $RM $linkname && $LN_S $realname $linkname; }; })" + done + fi + + # Do each command in the postinstall commands. + lib="$destdir/$realname" + func_execute_cmds "$postinstall_cmds" 'exit $?' + fi + + # Install the pseudo-library for information purposes. + func_basename "$file" + name="$func_basename_result" + instname="$dir/$name"i + func_show_eval "$install_prog $instname $destdir/$name" 'exit $?' + + # Maybe install the static library, too. + test -n "$old_library" && func_append staticlibs " $dir/$old_library" + ;; + + *.lo) + # Install (i.e. copy) a libtool object. + + # Figure out destination file name, if it wasn't already specified. + if test -n "$destname"; then + destfile="$destdir/$destname" + else + func_basename "$file" + destfile="$func_basename_result" + destfile="$destdir/$destfile" + fi + + # Deduce the name of the destination old-style object file. + case $destfile in + *.lo) + func_lo2o "$destfile" + staticdest=$func_lo2o_result + ;; + *.$objext) + staticdest="$destfile" + destfile= + ;; + *) + func_fatal_help "cannot copy a libtool object to \`$destfile'" + ;; + esac + + # Install the libtool object if requested. + test -n "$destfile" && \ + func_show_eval "$install_prog $file $destfile" 'exit $?' + + # Install the old object if enabled. + if test "$build_old_libs" = yes; then + # Deduce the name of the old-style object file. + func_lo2o "$file" + staticobj=$func_lo2o_result + func_show_eval "$install_prog \$staticobj \$staticdest" 'exit $?' + fi + exit $EXIT_SUCCESS + ;; + + *) + # Figure out destination file name, if it wasn't already specified. + if test -n "$destname"; then + destfile="$destdir/$destname" + else + func_basename "$file" + destfile="$func_basename_result" + destfile="$destdir/$destfile" + fi + + # If the file is missing, and there is a .exe on the end, strip it + # because it is most likely a libtool script we actually want to + # install + stripped_ext="" + case $file in + *.exe) + if test ! -f "$file"; then + func_stripname '' '.exe' "$file" + file=$func_stripname_result + stripped_ext=".exe" + fi + ;; + esac + + # Do a test to see if this is really a libtool program. + case $host in + *cygwin* | *mingw*) + if func_ltwrapper_executable_p "$file"; then + func_ltwrapper_scriptname "$file" + wrapper=$func_ltwrapper_scriptname_result + else + func_stripname '' '.exe' "$file" + wrapper=$func_stripname_result + fi + ;; + *) + wrapper=$file + ;; + esac + if func_ltwrapper_script_p "$wrapper"; then + notinst_deplibs= + relink_command= + + func_source "$wrapper" + + # Check the variables that should have been set. + test -z "$generated_by_libtool_version" && \ + func_fatal_error "invalid libtool wrapper script \`$wrapper'" + + finalize=yes + for lib in $notinst_deplibs; do + # Check to see that each library is installed. + libdir= + if test -f "$lib"; then + func_source "$lib" + fi + libfile="$libdir/"`$ECHO "$lib" | $SED 's%^.*/%%g'` ### testsuite: skip nested quoting test + if test -n "$libdir" && test ! -f "$libfile"; then + func_warning "\`$lib' has not been installed in \`$libdir'" + finalize=no + fi + done + + relink_command= + func_source "$wrapper" + + outputname= + if test "$fast_install" = no && test -n "$relink_command"; then + $opt_dry_run || { + if test "$finalize" = yes; then + tmpdir=`func_mktempdir` + func_basename "$file$stripped_ext" + file="$func_basename_result" + outputname="$tmpdir/$file" + # Replace the output file specification. + relink_command=`$ECHO "$relink_command" | $SED 's%@OUTPUT@%'"$outputname"'%g'` + + $opt_silent || { + func_quote_for_expand "$relink_command" + eval "func_echo $func_quote_for_expand_result" + } + if eval "$relink_command"; then : + else + func_error "error: relink \`$file' with the above command before installing it" + $opt_dry_run || ${RM}r "$tmpdir" + continue + fi + file="$outputname" + else + func_warning "cannot relink \`$file'" + fi + } + else + # Install the binary that we compiled earlier. + file=`$ECHO "$file$stripped_ext" | $SED "s%\([^/]*\)$%$objdir/\1%"` + fi + fi + + # remove .exe since cygwin /usr/bin/install will append another + # one anyway + case $install_prog,$host in + */usr/bin/install*,*cygwin*) + case $file:$destfile in + *.exe:*.exe) + # this is ok + ;; + *.exe:*) + destfile=$destfile.exe + ;; + *:*.exe) + func_stripname '' '.exe' "$destfile" + destfile=$func_stripname_result + ;; + esac + ;; + esac + func_show_eval "$install_prog\$stripme \$file \$destfile" 'exit $?' + $opt_dry_run || if test -n "$outputname"; then + ${RM}r "$tmpdir" + fi + ;; + esac + done + + for file in $staticlibs; do + func_basename "$file" + name="$func_basename_result" + + # Set up the ranlib parameters. + oldlib="$destdir/$name" + func_to_tool_file "$oldlib" func_convert_file_msys_to_w32 + tool_oldlib=$func_to_tool_file_result + + func_show_eval "$install_prog \$file \$oldlib" 'exit $?' + + if test -n "$stripme" && test -n "$old_striplib"; then + func_show_eval "$old_striplib $tool_oldlib" 'exit $?' + fi + + # Do each command in the postinstall commands. + func_execute_cmds "$old_postinstall_cmds" 'exit $?' + done + + test -n "$future_libdirs" && \ + func_warning "remember to run \`$progname --finish$future_libdirs'" + + if test -n "$current_libdirs"; then + # Maybe just do a dry run. + $opt_dry_run && current_libdirs=" -n$current_libdirs" + exec_cmd='$SHELL $progpath $preserve_args --finish$current_libdirs' + else + exit $EXIT_SUCCESS + fi +} + +test "$opt_mode" = install && func_mode_install ${1+"$@"} + + +# func_generate_dlsyms outputname originator pic_p +# Extract symbols from dlprefiles and create ${outputname}S.o with +# a dlpreopen symbol table. +func_generate_dlsyms () +{ + $opt_debug + my_outputname="$1" + my_originator="$2" + my_pic_p="${3-no}" + my_prefix=`$ECHO "$my_originator" | sed 's%[^a-zA-Z0-9]%_%g'` + my_dlsyms= + + if test -n "$dlfiles$dlprefiles" || test "$dlself" != no; then + if test -n "$NM" && test -n "$global_symbol_pipe"; then + my_dlsyms="${my_outputname}S.c" + else + func_error "not configured to extract global symbols from dlpreopened files" + fi + fi + + if test -n "$my_dlsyms"; then + case $my_dlsyms in + "") ;; + *.c) + # Discover the nlist of each of the dlfiles. + nlist="$output_objdir/${my_outputname}.nm" + + func_show_eval "$RM $nlist ${nlist}S ${nlist}T" + + # Parse the name list into a source file. + func_verbose "creating $output_objdir/$my_dlsyms" + + $opt_dry_run || $ECHO > "$output_objdir/$my_dlsyms" "\ +/* $my_dlsyms - symbol resolution table for \`$my_outputname' dlsym emulation. */ +/* Generated by $PROGRAM (GNU $PACKAGE$TIMESTAMP) $VERSION */ + +#ifdef __cplusplus +extern \"C\" { +#endif + +#if defined(__GNUC__) && (((__GNUC__ == 4) && (__GNUC_MINOR__ >= 4)) || (__GNUC__ > 4)) +#pragma GCC diagnostic ignored \"-Wstrict-prototypes\" +#endif + +/* Keep this code in sync between libtool.m4, ltmain, lt_system.h, and tests. */ +#if defined(_WIN32) || defined(__CYGWIN__) || defined(_WIN32_WCE) +/* DATA imports from DLLs on WIN32 con't be const, because runtime + relocations are performed -- see ld's documentation on pseudo-relocs. */ +# define LT_DLSYM_CONST +#elif defined(__osf__) +/* This system does not cope well with relocations in const data. */ +# define LT_DLSYM_CONST +#else +# define LT_DLSYM_CONST const +#endif + +/* External symbol declarations for the compiler. */\ +" + + if test "$dlself" = yes; then + func_verbose "generating symbol list for \`$output'" + + $opt_dry_run || echo ': @PROGRAM@ ' > "$nlist" + + # Add our own program objects to the symbol list. + progfiles=`$ECHO "$objs$old_deplibs" | $SP2NL | $SED "$lo2o" | $NL2SP` + for progfile in $progfiles; do + func_to_tool_file "$progfile" func_convert_file_msys_to_w32 + func_verbose "extracting global C symbols from \`$func_to_tool_file_result'" + $opt_dry_run || eval "$NM $func_to_tool_file_result | $global_symbol_pipe >> '$nlist'" + done + + if test -n "$exclude_expsyms"; then + $opt_dry_run || { + eval '$EGREP -v " ($exclude_expsyms)$" "$nlist" > "$nlist"T' + eval '$MV "$nlist"T "$nlist"' + } + fi + + if test -n "$export_symbols_regex"; then + $opt_dry_run || { + eval '$EGREP -e "$export_symbols_regex" "$nlist" > "$nlist"T' + eval '$MV "$nlist"T "$nlist"' + } + fi + + # Prepare the list of exported symbols + if test -z "$export_symbols"; then + export_symbols="$output_objdir/$outputname.exp" + $opt_dry_run || { + $RM $export_symbols + eval "${SED} -n -e '/^: @PROGRAM@ $/d' -e 's/^.* \(.*\)$/\1/p' "'< "$nlist" > "$export_symbols"' + case $host in + *cygwin* | *mingw* | *cegcc* ) + eval "echo EXPORTS "'> "$output_objdir/$outputname.def"' + eval 'cat "$export_symbols" >> "$output_objdir/$outputname.def"' + ;; + esac + } + else + $opt_dry_run || { + eval "${SED} -e 's/\([].[*^$]\)/\\\\\1/g' -e 's/^/ /' -e 's/$/$/'"' < "$export_symbols" > "$output_objdir/$outputname.exp"' + eval '$GREP -f "$output_objdir/$outputname.exp" < "$nlist" > "$nlist"T' + eval '$MV "$nlist"T "$nlist"' + case $host in + *cygwin* | *mingw* | *cegcc* ) + eval "echo EXPORTS "'> "$output_objdir/$outputname.def"' + eval 'cat "$nlist" >> "$output_objdir/$outputname.def"' + ;; + esac + } + fi + fi + + for dlprefile in $dlprefiles; do + func_verbose "extracting global C symbols from \`$dlprefile'" + func_basename "$dlprefile" + name="$func_basename_result" + case $host in + *cygwin* | *mingw* | *cegcc* ) + # if an import library, we need to obtain dlname + if func_win32_import_lib_p "$dlprefile"; then + func_tr_sh "$dlprefile" + eval "curr_lafile=\$libfile_$func_tr_sh_result" + dlprefile_dlbasename="" + if test -n "$curr_lafile" && func_lalib_p "$curr_lafile"; then + # Use subshell, to avoid clobbering current variable values + dlprefile_dlname=`source "$curr_lafile" && echo "$dlname"` + if test -n "$dlprefile_dlname" ; then + func_basename "$dlprefile_dlname" + dlprefile_dlbasename="$func_basename_result" + else + # no lafile. user explicitly requested -dlpreopen . + $sharedlib_from_linklib_cmd "$dlprefile" + dlprefile_dlbasename=$sharedlib_from_linklib_result + fi + fi + $opt_dry_run || { + if test -n "$dlprefile_dlbasename" ; then + eval '$ECHO ": $dlprefile_dlbasename" >> "$nlist"' + else + func_warning "Could not compute DLL name from $name" + eval '$ECHO ": $name " >> "$nlist"' + fi + func_to_tool_file "$dlprefile" func_convert_file_msys_to_w32 + eval "$NM \"$func_to_tool_file_result\" 2>/dev/null | $global_symbol_pipe | + $SED -e '/I __imp/d' -e 's/I __nm_/D /;s/_nm__//' >> '$nlist'" + } + else # not an import lib + $opt_dry_run || { + eval '$ECHO ": $name " >> "$nlist"' + func_to_tool_file "$dlprefile" func_convert_file_msys_to_w32 + eval "$NM \"$func_to_tool_file_result\" 2>/dev/null | $global_symbol_pipe >> '$nlist'" + } + fi + ;; + *) + $opt_dry_run || { + eval '$ECHO ": $name " >> "$nlist"' + func_to_tool_file "$dlprefile" func_convert_file_msys_to_w32 + eval "$NM \"$func_to_tool_file_result\" 2>/dev/null | $global_symbol_pipe >> '$nlist'" + } + ;; + esac + done + + $opt_dry_run || { + # Make sure we have at least an empty file. + test -f "$nlist" || : > "$nlist" + + if test -n "$exclude_expsyms"; then + $EGREP -v " ($exclude_expsyms)$" "$nlist" > "$nlist"T + $MV "$nlist"T "$nlist" + fi + + # Try sorting and uniquifying the output. + if $GREP -v "^: " < "$nlist" | + if sort -k 3 /dev/null 2>&1; then + sort -k 3 + else + sort +2 + fi | + uniq > "$nlist"S; then + : + else + $GREP -v "^: " < "$nlist" > "$nlist"S + fi + + if test -f "$nlist"S; then + eval "$global_symbol_to_cdecl"' < "$nlist"S >> "$output_objdir/$my_dlsyms"' + else + echo '/* NONE */' >> "$output_objdir/$my_dlsyms" + fi + + echo >> "$output_objdir/$my_dlsyms" "\ + +/* The mapping between symbol names and symbols. */ +typedef struct { + const char *name; + void *address; +} lt_dlsymlist; +extern LT_DLSYM_CONST lt_dlsymlist +lt_${my_prefix}_LTX_preloaded_symbols[]; +LT_DLSYM_CONST lt_dlsymlist +lt_${my_prefix}_LTX_preloaded_symbols[] = +{\ + { \"$my_originator\", (void *) 0 }," + + case $need_lib_prefix in + no) + eval "$global_symbol_to_c_name_address" < "$nlist" >> "$output_objdir/$my_dlsyms" + ;; + *) + eval "$global_symbol_to_c_name_address_lib_prefix" < "$nlist" >> "$output_objdir/$my_dlsyms" + ;; + esac + echo >> "$output_objdir/$my_dlsyms" "\ + {0, (void *) 0} +}; + +/* This works around a problem in FreeBSD linker */ +#ifdef FREEBSD_WORKAROUND +static const void *lt_preloaded_setup() { + return lt_${my_prefix}_LTX_preloaded_symbols; +} +#endif + +#ifdef __cplusplus +} +#endif\ +" + } # !$opt_dry_run + + pic_flag_for_symtable= + case "$compile_command " in + *" -static "*) ;; + *) + case $host in + # compiling the symbol table file with pic_flag works around + # a FreeBSD bug that causes programs to crash when -lm is + # linked before any other PIC object. But we must not use + # pic_flag when linking with -static. The problem exists in + # FreeBSD 2.2.6 and is fixed in FreeBSD 3.1. + *-*-freebsd2.*|*-*-freebsd3.0*|*-*-freebsdelf3.0*) + pic_flag_for_symtable=" $pic_flag -DFREEBSD_WORKAROUND" ;; + *-*-hpux*) + pic_flag_for_symtable=" $pic_flag" ;; + *) + if test "X$my_pic_p" != Xno; then + pic_flag_for_symtable=" $pic_flag" + fi + ;; + esac + ;; + esac + symtab_cflags= + for arg in $LTCFLAGS; do + case $arg in + -pie | -fpie | -fPIE) ;; + *) func_append symtab_cflags " $arg" ;; + esac + done + + # Now compile the dynamic symbol file. + func_show_eval '(cd $output_objdir && $LTCC$symtab_cflags -c$no_builtin_flag$pic_flag_for_symtable "$my_dlsyms")' 'exit $?' + + # Clean up the generated files. + func_show_eval '$RM "$output_objdir/$my_dlsyms" "$nlist" "${nlist}S" "${nlist}T"' + + # Transform the symbol file into the correct name. + symfileobj="$output_objdir/${my_outputname}S.$objext" + case $host in + *cygwin* | *mingw* | *cegcc* ) + if test -f "$output_objdir/$my_outputname.def"; then + compile_command=`$ECHO "$compile_command" | $SED "s%@SYMFILE@%$output_objdir/$my_outputname.def $symfileobj%"` + finalize_command=`$ECHO "$finalize_command" | $SED "s%@SYMFILE@%$output_objdir/$my_outputname.def $symfileobj%"` + else + compile_command=`$ECHO "$compile_command" | $SED "s%@SYMFILE@%$symfileobj%"` + finalize_command=`$ECHO "$finalize_command" | $SED "s%@SYMFILE@%$symfileobj%"` + fi + ;; + *) + compile_command=`$ECHO "$compile_command" | $SED "s%@SYMFILE@%$symfileobj%"` + finalize_command=`$ECHO "$finalize_command" | $SED "s%@SYMFILE@%$symfileobj%"` + ;; + esac + ;; + *) + func_fatal_error "unknown suffix for \`$my_dlsyms'" + ;; + esac + else + # We keep going just in case the user didn't refer to + # lt_preloaded_symbols. The linker will fail if global_symbol_pipe + # really was required. + + # Nullify the symbol file. + compile_command=`$ECHO "$compile_command" | $SED "s% @SYMFILE@%%"` + finalize_command=`$ECHO "$finalize_command" | $SED "s% @SYMFILE@%%"` + fi +} + +# func_win32_libid arg +# return the library type of file 'arg' +# +# Need a lot of goo to handle *both* DLLs and import libs +# Has to be a shell function in order to 'eat' the argument +# that is supplied when $file_magic_command is called. +# Despite the name, also deal with 64 bit binaries. +func_win32_libid () +{ + $opt_debug + win32_libid_type="unknown" + win32_fileres=`file -L $1 2>/dev/null` + case $win32_fileres in + *ar\ archive\ import\ library*) # definitely import + win32_libid_type="x86 archive import" + ;; + *ar\ archive*) # could be an import, or static + # Keep the egrep pattern in sync with the one in _LT_CHECK_MAGIC_METHOD. + if eval $OBJDUMP -f $1 | $SED -e '10q' 2>/dev/null | + $EGREP 'file format (pei*-i386(.*architecture: i386)?|pe-arm-wince|pe-x86-64)' >/dev/null; then + func_to_tool_file "$1" func_convert_file_msys_to_w32 + win32_nmres=`eval $NM -f posix -A \"$func_to_tool_file_result\" | + $SED -n -e ' + 1,100{ + / I /{ + s,.*,import, + p + q + } + }'` + case $win32_nmres in + import*) win32_libid_type="x86 archive import";; + *) win32_libid_type="x86 archive static";; + esac + fi + ;; + *DLL*) + win32_libid_type="x86 DLL" + ;; + *executable*) # but shell scripts are "executable" too... + case $win32_fileres in + *MS\ Windows\ PE\ Intel*) + win32_libid_type="x86 DLL" + ;; + esac + ;; + esac + $ECHO "$win32_libid_type" +} + +# func_cygming_dll_for_implib ARG +# +# Platform-specific function to extract the +# name of the DLL associated with the specified +# import library ARG. +# Invoked by eval'ing the libtool variable +# $sharedlib_from_linklib_cmd +# Result is available in the variable +# $sharedlib_from_linklib_result +func_cygming_dll_for_implib () +{ + $opt_debug + sharedlib_from_linklib_result=`$DLLTOOL --identify-strict --identify "$1"` +} + +# func_cygming_dll_for_implib_fallback_core SECTION_NAME LIBNAMEs +# +# The is the core of a fallback implementation of a +# platform-specific function to extract the name of the +# DLL associated with the specified import library LIBNAME. +# +# SECTION_NAME is either .idata$6 or .idata$7, depending +# on the platform and compiler that created the implib. +# +# Echos the name of the DLL associated with the +# specified import library. +func_cygming_dll_for_implib_fallback_core () +{ + $opt_debug + match_literal=`$ECHO "$1" | $SED "$sed_make_literal_regex"` + $OBJDUMP -s --section "$1" "$2" 2>/dev/null | + $SED '/^Contents of section '"$match_literal"':/{ + # Place marker at beginning of archive member dllname section + s/.*/====MARK====/ + p + d + } + # These lines can sometimes be longer than 43 characters, but + # are always uninteresting + /:[ ]*file format pe[i]\{,1\}-/d + /^In archive [^:]*:/d + # Ensure marker is printed + /^====MARK====/p + # Remove all lines with less than 43 characters + /^.\{43\}/!d + # From remaining lines, remove first 43 characters + s/^.\{43\}//' | + $SED -n ' + # Join marker and all lines until next marker into a single line + /^====MARK====/ b para + H + $ b para + b + :para + x + s/\n//g + # Remove the marker + s/^====MARK====// + # Remove trailing dots and whitespace + s/[\. \t]*$// + # Print + /./p' | + # we now have a list, one entry per line, of the stringified + # contents of the appropriate section of all members of the + # archive which possess that section. Heuristic: eliminate + # all those which have a first or second character that is + # a '.' (that is, objdump's representation of an unprintable + # character.) This should work for all archives with less than + # 0x302f exports -- but will fail for DLLs whose name actually + # begins with a literal '.' or a single character followed by + # a '.'. + # + # Of those that remain, print the first one. + $SED -e '/^\./d;/^.\./d;q' +} + +# func_cygming_gnu_implib_p ARG +# This predicate returns with zero status (TRUE) if +# ARG is a GNU/binutils-style import library. Returns +# with nonzero status (FALSE) otherwise. +func_cygming_gnu_implib_p () +{ + $opt_debug + func_to_tool_file "$1" func_convert_file_msys_to_w32 + func_cygming_gnu_implib_tmp=`$NM "$func_to_tool_file_result" | eval "$global_symbol_pipe" | $EGREP ' (_head_[A-Za-z0-9_]+_[ad]l*|[A-Za-z0-9_]+_[ad]l*_iname)$'` + test -n "$func_cygming_gnu_implib_tmp" +} + +# func_cygming_ms_implib_p ARG +# This predicate returns with zero status (TRUE) if +# ARG is an MS-style import library. Returns +# with nonzero status (FALSE) otherwise. +func_cygming_ms_implib_p () +{ + $opt_debug + func_to_tool_file "$1" func_convert_file_msys_to_w32 + func_cygming_ms_implib_tmp=`$NM "$func_to_tool_file_result" | eval "$global_symbol_pipe" | $GREP '_NULL_IMPORT_DESCRIPTOR'` + test -n "$func_cygming_ms_implib_tmp" +} + +# func_cygming_dll_for_implib_fallback ARG +# Platform-specific function to extract the +# name of the DLL associated with the specified +# import library ARG. +# +# This fallback implementation is for use when $DLLTOOL +# does not support the --identify-strict option. +# Invoked by eval'ing the libtool variable +# $sharedlib_from_linklib_cmd +# Result is available in the variable +# $sharedlib_from_linklib_result +func_cygming_dll_for_implib_fallback () +{ + $opt_debug + if func_cygming_gnu_implib_p "$1" ; then + # binutils import library + sharedlib_from_linklib_result=`func_cygming_dll_for_implib_fallback_core '.idata$7' "$1"` + elif func_cygming_ms_implib_p "$1" ; then + # ms-generated import library + sharedlib_from_linklib_result=`func_cygming_dll_for_implib_fallback_core '.idata$6' "$1"` + else + # unknown + sharedlib_from_linklib_result="" + fi +} + + +# func_extract_an_archive dir oldlib +func_extract_an_archive () +{ + $opt_debug + f_ex_an_ar_dir="$1"; shift + f_ex_an_ar_oldlib="$1" + if test "$lock_old_archive_extraction" = yes; then + lockfile=$f_ex_an_ar_oldlib.lock + until $opt_dry_run || ln "$progpath" "$lockfile" 2>/dev/null; do + func_echo "Waiting for $lockfile to be removed" + sleep 2 + done + fi + func_show_eval "(cd \$f_ex_an_ar_dir && $AR x \"\$f_ex_an_ar_oldlib\")" \ + 'stat=$?; rm -f "$lockfile"; exit $stat' + if test "$lock_old_archive_extraction" = yes; then + $opt_dry_run || rm -f "$lockfile" + fi + if ($AR t "$f_ex_an_ar_oldlib" | sort | sort -uc >/dev/null 2>&1); then + : + else + func_fatal_error "object name conflicts in archive: $f_ex_an_ar_dir/$f_ex_an_ar_oldlib" + fi +} + + +# func_extract_archives gentop oldlib ... +func_extract_archives () +{ + $opt_debug + my_gentop="$1"; shift + my_oldlibs=${1+"$@"} + my_oldobjs="" + my_xlib="" + my_xabs="" + my_xdir="" + + for my_xlib in $my_oldlibs; do + # Extract the objects. + case $my_xlib in + [\\/]* | [A-Za-z]:[\\/]*) my_xabs="$my_xlib" ;; + *) my_xabs=`pwd`"/$my_xlib" ;; + esac + func_basename "$my_xlib" + my_xlib="$func_basename_result" + my_xlib_u=$my_xlib + while :; do + case " $extracted_archives " in + *" $my_xlib_u "*) + func_arith $extracted_serial + 1 + extracted_serial=$func_arith_result + my_xlib_u=lt$extracted_serial-$my_xlib ;; + *) break ;; + esac + done + extracted_archives="$extracted_archives $my_xlib_u" + my_xdir="$my_gentop/$my_xlib_u" + + func_mkdir_p "$my_xdir" + + case $host in + *-darwin*) + func_verbose "Extracting $my_xabs" + # Do not bother doing anything if just a dry run + $opt_dry_run || { + darwin_orig_dir=`pwd` + cd $my_xdir || exit $? + darwin_archive=$my_xabs + darwin_curdir=`pwd` + darwin_base_archive=`basename "$darwin_archive"` + darwin_arches=`$LIPO -info "$darwin_archive" 2>/dev/null | $GREP Architectures 2>/dev/null || true` + if test -n "$darwin_arches"; then + darwin_arches=`$ECHO "$darwin_arches" | $SED -e 's/.*are://'` + darwin_arch= + func_verbose "$darwin_base_archive has multiple architectures $darwin_arches" + for darwin_arch in $darwin_arches ; do + func_mkdir_p "unfat-$$/${darwin_base_archive}-${darwin_arch}" + $LIPO -thin $darwin_arch -output "unfat-$$/${darwin_base_archive}-${darwin_arch}/${darwin_base_archive}" "${darwin_archive}" + cd "unfat-$$/${darwin_base_archive}-${darwin_arch}" + func_extract_an_archive "`pwd`" "${darwin_base_archive}" + cd "$darwin_curdir" + $RM "unfat-$$/${darwin_base_archive}-${darwin_arch}/${darwin_base_archive}" + done # $darwin_arches + ## Okay now we've a bunch of thin objects, gotta fatten them up :) + darwin_filelist=`find unfat-$$ -type f -name \*.o -print -o -name \*.lo -print | $SED -e "$basename" | sort -u` + darwin_file= + darwin_files= + for darwin_file in $darwin_filelist; do + darwin_files=`find unfat-$$ -name $darwin_file -print | sort | $NL2SP` + $LIPO -create -output "$darwin_file" $darwin_files + done # $darwin_filelist + $RM -rf unfat-$$ + cd "$darwin_orig_dir" + else + cd $darwin_orig_dir + func_extract_an_archive "$my_xdir" "$my_xabs" + fi # $darwin_arches + } # !$opt_dry_run + ;; + *) + func_extract_an_archive "$my_xdir" "$my_xabs" + ;; + esac + my_oldobjs="$my_oldobjs "`find $my_xdir -name \*.$objext -print -o -name \*.lo -print | sort | $NL2SP` + done + + func_extract_archives_result="$my_oldobjs" +} + + +# func_emit_wrapper [arg=no] +# +# Emit a libtool wrapper script on stdout. +# Don't directly open a file because we may want to +# incorporate the script contents within a cygwin/mingw +# wrapper executable. Must ONLY be called from within +# func_mode_link because it depends on a number of variables +# set therein. +# +# ARG is the value that the WRAPPER_SCRIPT_BELONGS_IN_OBJDIR +# variable will take. If 'yes', then the emitted script +# will assume that the directory in which it is stored is +# the $objdir directory. This is a cygwin/mingw-specific +# behavior. +func_emit_wrapper () +{ + func_emit_wrapper_arg1=${1-no} + + $ECHO "\ +#! $SHELL + +# $output - temporary wrapper script for $objdir/$outputname +# Generated by $PROGRAM (GNU $PACKAGE$TIMESTAMP) $VERSION +# +# The $output program cannot be directly executed until all the libtool +# libraries that it depends on are installed. +# +# This wrapper script should never be moved out of the build directory. +# If it is, it will not operate correctly. + +# Sed substitution that helps us do robust quoting. It backslashifies +# metacharacters that are still active within double-quoted strings. +sed_quote_subst='$sed_quote_subst' + +# Be Bourne compatible +if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then + emulate sh + NULLCMD=: + # Zsh 3.x and 4.x performs word splitting on \${1+\"\$@\"}, which + # is contrary to our usage. Disable this feature. + alias -g '\${1+\"\$@\"}'='\"\$@\"' + setopt NO_GLOB_SUBST +else + case \`(set -o) 2>/dev/null\` in *posix*) set -o posix;; esac +fi +BIN_SH=xpg4; export BIN_SH # for Tru64 +DUALCASE=1; export DUALCASE # for MKS sh + +# The HP-UX ksh and POSIX shell print the target directory to stdout +# if CDPATH is set. +(unset CDPATH) >/dev/null 2>&1 && unset CDPATH + +relink_command=\"$relink_command\" + +# This environment variable determines our operation mode. +if test \"\$libtool_install_magic\" = \"$magic\"; then + # install mode needs the following variables: + generated_by_libtool_version='$macro_version' + notinst_deplibs='$notinst_deplibs' +else + # When we are sourced in execute mode, \$file and \$ECHO are already set. + if test \"\$libtool_execute_magic\" != \"$magic\"; then + file=\"\$0\"" + + qECHO=`$ECHO "$ECHO" | $SED "$sed_quote_subst"` + $ECHO "\ + +# A function that is used when there is no print builtin or printf. +func_fallback_echo () +{ + eval 'cat <<_LTECHO_EOF +\$1 +_LTECHO_EOF' +} + ECHO=\"$qECHO\" + fi + +# Very basic option parsing. These options are (a) specific to +# the libtool wrapper, (b) are identical between the wrapper +# /script/ and the wrapper /executable/ which is used only on +# windows platforms, and (c) all begin with the string "--lt-" +# (application programs are unlikely to have options which match +# this pattern). +# +# There are only two supported options: --lt-debug and +# --lt-dump-script. There is, deliberately, no --lt-help. +# +# The first argument to this parsing function should be the +# script's $0 value, followed by "$@". +lt_option_debug= +func_parse_lt_options () +{ + lt_script_arg0=\$0 + shift + for lt_opt + do + case \"\$lt_opt\" in + --lt-debug) lt_option_debug=1 ;; + --lt-dump-script) + lt_dump_D=\`\$ECHO \"X\$lt_script_arg0\" | $SED -e 's/^X//' -e 's%/[^/]*$%%'\` + test \"X\$lt_dump_D\" = \"X\$lt_script_arg0\" && lt_dump_D=. + lt_dump_F=\`\$ECHO \"X\$lt_script_arg0\" | $SED -e 's/^X//' -e 's%^.*/%%'\` + cat \"\$lt_dump_D/\$lt_dump_F\" + exit 0 + ;; + --lt-*) + \$ECHO \"Unrecognized --lt- option: '\$lt_opt'\" 1>&2 + exit 1 + ;; + esac + done + + # Print the debug banner immediately: + if test -n \"\$lt_option_debug\"; then + echo \"${outputname}:${output}:\${LINENO}: libtool wrapper (GNU $PACKAGE$TIMESTAMP) $VERSION\" 1>&2 + fi +} + +# Used when --lt-debug. Prints its arguments to stdout +# (redirection is the responsibility of the caller) +func_lt_dump_args () +{ + lt_dump_args_N=1; + for lt_arg + do + \$ECHO \"${outputname}:${output}:\${LINENO}: newargv[\$lt_dump_args_N]: \$lt_arg\" + lt_dump_args_N=\`expr \$lt_dump_args_N + 1\` + done +} + +# Core function for launching the target application +func_exec_program_core () +{ +" + case $host in + # Backslashes separate directories on plain windows + *-*-mingw | *-*-os2* | *-cegcc*) + $ECHO "\ + if test -n \"\$lt_option_debug\"; then + \$ECHO \"${outputname}:${output}:\${LINENO}: newargv[0]: \$progdir\\\\\$program\" 1>&2 + func_lt_dump_args \${1+\"\$@\"} 1>&2 + fi + exec \"\$progdir\\\\\$program\" \${1+\"\$@\"} +" + ;; + + *) + $ECHO "\ + if test -n \"\$lt_option_debug\"; then + \$ECHO \"${outputname}:${output}:\${LINENO}: newargv[0]: \$progdir/\$program\" 1>&2 + func_lt_dump_args \${1+\"\$@\"} 1>&2 + fi + exec \"\$progdir/\$program\" \${1+\"\$@\"} +" + ;; + esac + $ECHO "\ + \$ECHO \"\$0: cannot exec \$program \$*\" 1>&2 + exit 1 +} + +# A function to encapsulate launching the target application +# Strips options in the --lt-* namespace from \$@ and +# launches target application with the remaining arguments. +func_exec_program () +{ + case \" \$* \" in + *\\ --lt-*) + for lt_wr_arg + do + case \$lt_wr_arg in + --lt-*) ;; + *) set x \"\$@\" \"\$lt_wr_arg\"; shift;; + esac + shift + done ;; + esac + func_exec_program_core \${1+\"\$@\"} +} + + # Parse options + func_parse_lt_options \"\$0\" \${1+\"\$@\"} + + # Find the directory that this script lives in. + thisdir=\`\$ECHO \"\$file\" | $SED 's%/[^/]*$%%'\` + test \"x\$thisdir\" = \"x\$file\" && thisdir=. + + # Follow symbolic links until we get to the real thisdir. + file=\`ls -ld \"\$file\" | $SED -n 's/.*-> //p'\` + while test -n \"\$file\"; do + destdir=\`\$ECHO \"\$file\" | $SED 's%/[^/]*\$%%'\` + + # If there was a directory component, then change thisdir. + if test \"x\$destdir\" != \"x\$file\"; then + case \"\$destdir\" in + [\\\\/]* | [A-Za-z]:[\\\\/]*) thisdir=\"\$destdir\" ;; + *) thisdir=\"\$thisdir/\$destdir\" ;; + esac + fi + + file=\`\$ECHO \"\$file\" | $SED 's%^.*/%%'\` + file=\`ls -ld \"\$thisdir/\$file\" | $SED -n 's/.*-> //p'\` + done + + # Usually 'no', except on cygwin/mingw when embedded into + # the cwrapper. + WRAPPER_SCRIPT_BELONGS_IN_OBJDIR=$func_emit_wrapper_arg1 + if test \"\$WRAPPER_SCRIPT_BELONGS_IN_OBJDIR\" = \"yes\"; then + # special case for '.' + if test \"\$thisdir\" = \".\"; then + thisdir=\`pwd\` + fi + # remove .libs from thisdir + case \"\$thisdir\" in + *[\\\\/]$objdir ) thisdir=\`\$ECHO \"\$thisdir\" | $SED 's%[\\\\/][^\\\\/]*$%%'\` ;; + $objdir ) thisdir=. ;; + esac + fi + + # Try to get the absolute directory name. + absdir=\`cd \"\$thisdir\" && pwd\` + test -n \"\$absdir\" && thisdir=\"\$absdir\" +" + + if test "$fast_install" = yes; then + $ECHO "\ + program=lt-'$outputname'$exeext + progdir=\"\$thisdir/$objdir\" + + if test ! -f \"\$progdir/\$program\" || + { file=\`ls -1dt \"\$progdir/\$program\" \"\$progdir/../\$program\" 2>/dev/null | ${SED} 1q\`; \\ + test \"X\$file\" != \"X\$progdir/\$program\"; }; then + + file=\"\$\$-\$program\" + + if test ! -d \"\$progdir\"; then + $MKDIR \"\$progdir\" + else + $RM \"\$progdir/\$file\" + fi" + + $ECHO "\ + + # relink executable if necessary + if test -n \"\$relink_command\"; then + if relink_command_output=\`eval \$relink_command 2>&1\`; then : + else + $ECHO \"\$relink_command_output\" >&2 + $RM \"\$progdir/\$file\" + exit 1 + fi + fi + + $MV \"\$progdir/\$file\" \"\$progdir/\$program\" 2>/dev/null || + { $RM \"\$progdir/\$program\"; + $MV \"\$progdir/\$file\" \"\$progdir/\$program\"; } + $RM \"\$progdir/\$file\" + fi" + else + $ECHO "\ + program='$outputname' + progdir=\"\$thisdir/$objdir\" +" + fi + + $ECHO "\ + + if test -f \"\$progdir/\$program\"; then" + + # fixup the dll searchpath if we need to. + # + # Fix the DLL searchpath if we need to. Do this before prepending + # to shlibpath, because on Windows, both are PATH and uninstalled + # libraries must come first. + if test -n "$dllsearchpath"; then + $ECHO "\ + # Add the dll search path components to the executable PATH + PATH=$dllsearchpath:\$PATH +" + fi + + # Export our shlibpath_var if we have one. + if test "$shlibpath_overrides_runpath" = yes && test -n "$shlibpath_var" && test -n "$temp_rpath"; then + $ECHO "\ + # Add our own library path to $shlibpath_var + $shlibpath_var=\"$temp_rpath\$$shlibpath_var\" + + # Some systems cannot cope with colon-terminated $shlibpath_var + # The second colon is a workaround for a bug in BeOS R4 sed + $shlibpath_var=\`\$ECHO \"\$$shlibpath_var\" | $SED 's/::*\$//'\` + + export $shlibpath_var +" + fi + + $ECHO "\ + if test \"\$libtool_execute_magic\" != \"$magic\"; then + # Run the actual program with our arguments. + func_exec_program \${1+\"\$@\"} + fi + else + # The program doesn't exist. + \$ECHO \"\$0: error: \\\`\$progdir/\$program' does not exist\" 1>&2 + \$ECHO \"This script is just a wrapper for \$program.\" 1>&2 + \$ECHO \"See the $PACKAGE documentation for more information.\" 1>&2 + exit 1 + fi +fi\ +" +} + + +# func_emit_cwrapperexe_src +# emit the source code for a wrapper executable on stdout +# Must ONLY be called from within func_mode_link because +# it depends on a number of variable set therein. +func_emit_cwrapperexe_src () +{ + cat < +#include +#ifdef _MSC_VER +# include +# include +# include +#else +# include +# include +# ifdef __CYGWIN__ +# include +# endif +#endif +#include +#include +#include +#include +#include +#include +#include +#include + +/* declarations of non-ANSI functions */ +#if defined(__MINGW32__) +# ifdef __STRICT_ANSI__ +int _putenv (const char *); +# endif +#elif defined(__CYGWIN__) +# ifdef __STRICT_ANSI__ +char *realpath (const char *, char *); +int putenv (char *); +int setenv (const char *, const char *, int); +# endif +/* #elif defined (other platforms) ... */ +#endif + +/* portability defines, excluding path handling macros */ +#if defined(_MSC_VER) +# define setmode _setmode +# define stat _stat +# define chmod _chmod +# define getcwd _getcwd +# define putenv _putenv +# define S_IXUSR _S_IEXEC +# ifndef _INTPTR_T_DEFINED +# define _INTPTR_T_DEFINED +# define intptr_t int +# endif +#elif defined(__MINGW32__) +# define setmode _setmode +# define stat _stat +# define chmod _chmod +# define getcwd _getcwd +# define putenv _putenv +#elif defined(__CYGWIN__) +# define HAVE_SETENV +# define FOPEN_WB "wb" +/* #elif defined (other platforms) ... */ +#endif + +#if defined(PATH_MAX) +# define LT_PATHMAX PATH_MAX +#elif defined(MAXPATHLEN) +# define LT_PATHMAX MAXPATHLEN +#else +# define LT_PATHMAX 1024 +#endif + +#ifndef S_IXOTH +# define S_IXOTH 0 +#endif +#ifndef S_IXGRP +# define S_IXGRP 0 +#endif + +/* path handling portability macros */ +#ifndef DIR_SEPARATOR +# define DIR_SEPARATOR '/' +# define PATH_SEPARATOR ':' +#endif + +#if defined (_WIN32) || defined (__MSDOS__) || defined (__DJGPP__) || \ + defined (__OS2__) +# define HAVE_DOS_BASED_FILE_SYSTEM +# define FOPEN_WB "wb" +# ifndef DIR_SEPARATOR_2 +# define DIR_SEPARATOR_2 '\\' +# endif +# ifndef PATH_SEPARATOR_2 +# define PATH_SEPARATOR_2 ';' +# endif +#endif + +#ifndef DIR_SEPARATOR_2 +# define IS_DIR_SEPARATOR(ch) ((ch) == DIR_SEPARATOR) +#else /* DIR_SEPARATOR_2 */ +# define IS_DIR_SEPARATOR(ch) \ + (((ch) == DIR_SEPARATOR) || ((ch) == DIR_SEPARATOR_2)) +#endif /* DIR_SEPARATOR_2 */ + +#ifndef PATH_SEPARATOR_2 +# define IS_PATH_SEPARATOR(ch) ((ch) == PATH_SEPARATOR) +#else /* PATH_SEPARATOR_2 */ +# define IS_PATH_SEPARATOR(ch) ((ch) == PATH_SEPARATOR_2) +#endif /* PATH_SEPARATOR_2 */ + +#ifndef FOPEN_WB +# define FOPEN_WB "w" +#endif +#ifndef _O_BINARY +# define _O_BINARY 0 +#endif + +#define XMALLOC(type, num) ((type *) xmalloc ((num) * sizeof(type))) +#define XFREE(stale) do { \ + if (stale) { free ((void *) stale); stale = 0; } \ +} while (0) + +#if defined(LT_DEBUGWRAPPER) +static int lt_debug = 1; +#else +static int lt_debug = 0; +#endif + +const char *program_name = "libtool-wrapper"; /* in case xstrdup fails */ + +void *xmalloc (size_t num); +char *xstrdup (const char *string); +const char *base_name (const char *name); +char *find_executable (const char *wrapper); +char *chase_symlinks (const char *pathspec); +int make_executable (const char *path); +int check_executable (const char *path); +char *strendzap (char *str, const char *pat); +void lt_debugprintf (const char *file, int line, const char *fmt, ...); +void lt_fatal (const char *file, int line, const char *message, ...); +static const char *nonnull (const char *s); +static const char *nonempty (const char *s); +void lt_setenv (const char *name, const char *value); +char *lt_extend_str (const char *orig_value, const char *add, int to_end); +void lt_update_exe_path (const char *name, const char *value); +void lt_update_lib_path (const char *name, const char *value); +char **prepare_spawn (char **argv); +void lt_dump_script (FILE *f); +EOF + + cat <= 0) + && (st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))) + return 1; + else + return 0; +} + +int +make_executable (const char *path) +{ + int rval = 0; + struct stat st; + + lt_debugprintf (__FILE__, __LINE__, "(make_executable): %s\n", + nonempty (path)); + if ((!path) || (!*path)) + return 0; + + if (stat (path, &st) >= 0) + { + rval = chmod (path, st.st_mode | S_IXOTH | S_IXGRP | S_IXUSR); + } + return rval; +} + +/* Searches for the full path of the wrapper. Returns + newly allocated full path name if found, NULL otherwise + Does not chase symlinks, even on platforms that support them. +*/ +char * +find_executable (const char *wrapper) +{ + int has_slash = 0; + const char *p; + const char *p_next; + /* static buffer for getcwd */ + char tmp[LT_PATHMAX + 1]; + int tmp_len; + char *concat_name; + + lt_debugprintf (__FILE__, __LINE__, "(find_executable): %s\n", + nonempty (wrapper)); + + if ((wrapper == NULL) || (*wrapper == '\0')) + return NULL; + + /* Absolute path? */ +#if defined (HAVE_DOS_BASED_FILE_SYSTEM) + if (isalpha ((unsigned char) wrapper[0]) && wrapper[1] == ':') + { + concat_name = xstrdup (wrapper); + if (check_executable (concat_name)) + return concat_name; + XFREE (concat_name); + } + else + { +#endif + if (IS_DIR_SEPARATOR (wrapper[0])) + { + concat_name = xstrdup (wrapper); + if (check_executable (concat_name)) + return concat_name; + XFREE (concat_name); + } +#if defined (HAVE_DOS_BASED_FILE_SYSTEM) + } +#endif + + for (p = wrapper; *p; p++) + if (*p == '/') + { + has_slash = 1; + break; + } + if (!has_slash) + { + /* no slashes; search PATH */ + const char *path = getenv ("PATH"); + if (path != NULL) + { + for (p = path; *p; p = p_next) + { + const char *q; + size_t p_len; + for (q = p; *q; q++) + if (IS_PATH_SEPARATOR (*q)) + break; + p_len = q - p; + p_next = (*q == '\0' ? q : q + 1); + if (p_len == 0) + { + /* empty path: current directory */ + if (getcwd (tmp, LT_PATHMAX) == NULL) + lt_fatal (__FILE__, __LINE__, "getcwd failed: %s", + nonnull (strerror (errno))); + tmp_len = strlen (tmp); + concat_name = + XMALLOC (char, tmp_len + 1 + strlen (wrapper) + 1); + memcpy (concat_name, tmp, tmp_len); + concat_name[tmp_len] = '/'; + strcpy (concat_name + tmp_len + 1, wrapper); + } + else + { + concat_name = + XMALLOC (char, p_len + 1 + strlen (wrapper) + 1); + memcpy (concat_name, p, p_len); + concat_name[p_len] = '/'; + strcpy (concat_name + p_len + 1, wrapper); + } + if (check_executable (concat_name)) + return concat_name; + XFREE (concat_name); + } + } + /* not found in PATH; assume curdir */ + } + /* Relative path | not found in path: prepend cwd */ + if (getcwd (tmp, LT_PATHMAX) == NULL) + lt_fatal (__FILE__, __LINE__, "getcwd failed: %s", + nonnull (strerror (errno))); + tmp_len = strlen (tmp); + concat_name = XMALLOC (char, tmp_len + 1 + strlen (wrapper) + 1); + memcpy (concat_name, tmp, tmp_len); + concat_name[tmp_len] = '/'; + strcpy (concat_name + tmp_len + 1, wrapper); + + if (check_executable (concat_name)) + return concat_name; + XFREE (concat_name); + return NULL; +} + +char * +chase_symlinks (const char *pathspec) +{ +#ifndef S_ISLNK + return xstrdup (pathspec); +#else + char buf[LT_PATHMAX]; + struct stat s; + char *tmp_pathspec = xstrdup (pathspec); + char *p; + int has_symlinks = 0; + while (strlen (tmp_pathspec) && !has_symlinks) + { + lt_debugprintf (__FILE__, __LINE__, + "checking path component for symlinks: %s\n", + tmp_pathspec); + if (lstat (tmp_pathspec, &s) == 0) + { + if (S_ISLNK (s.st_mode) != 0) + { + has_symlinks = 1; + break; + } + + /* search backwards for last DIR_SEPARATOR */ + p = tmp_pathspec + strlen (tmp_pathspec) - 1; + while ((p > tmp_pathspec) && (!IS_DIR_SEPARATOR (*p))) + p--; + if ((p == tmp_pathspec) && (!IS_DIR_SEPARATOR (*p))) + { + /* no more DIR_SEPARATORS left */ + break; + } + *p = '\0'; + } + else + { + lt_fatal (__FILE__, __LINE__, + "error accessing file \"%s\": %s", + tmp_pathspec, nonnull (strerror (errno))); + } + } + XFREE (tmp_pathspec); + + if (!has_symlinks) + { + return xstrdup (pathspec); + } + + tmp_pathspec = realpath (pathspec, buf); + if (tmp_pathspec == 0) + { + lt_fatal (__FILE__, __LINE__, + "could not follow symlinks for %s", pathspec); + } + return xstrdup (tmp_pathspec); +#endif +} + +char * +strendzap (char *str, const char *pat) +{ + size_t len, patlen; + + assert (str != NULL); + assert (pat != NULL); + + len = strlen (str); + patlen = strlen (pat); + + if (patlen <= len) + { + str += len - patlen; + if (strcmp (str, pat) == 0) + *str = '\0'; + } + return str; +} + +void +lt_debugprintf (const char *file, int line, const char *fmt, ...) +{ + va_list args; + if (lt_debug) + { + (void) fprintf (stderr, "%s:%s:%d: ", program_name, file, line); + va_start (args, fmt); + (void) vfprintf (stderr, fmt, args); + va_end (args); + } +} + +static void +lt_error_core (int exit_status, const char *file, + int line, const char *mode, + const char *message, va_list ap) +{ + fprintf (stderr, "%s:%s:%d: %s: ", program_name, file, line, mode); + vfprintf (stderr, message, ap); + fprintf (stderr, ".\n"); + + if (exit_status >= 0) + exit (exit_status); +} + +void +lt_fatal (const char *file, int line, const char *message, ...) +{ + va_list ap; + va_start (ap, message); + lt_error_core (EXIT_FAILURE, file, line, "FATAL", message, ap); + va_end (ap); +} + +static const char * +nonnull (const char *s) +{ + return s ? s : "(null)"; +} + +static const char * +nonempty (const char *s) +{ + return (s && !*s) ? "(empty)" : nonnull (s); +} + +void +lt_setenv (const char *name, const char *value) +{ + lt_debugprintf (__FILE__, __LINE__, + "(lt_setenv) setting '%s' to '%s'\n", + nonnull (name), nonnull (value)); + { +#ifdef HAVE_SETENV + /* always make a copy, for consistency with !HAVE_SETENV */ + char *str = xstrdup (value); + setenv (name, str, 1); +#else + int len = strlen (name) + 1 + strlen (value) + 1; + char *str = XMALLOC (char, len); + sprintf (str, "%s=%s", name, value); + if (putenv (str) != EXIT_SUCCESS) + { + XFREE (str); + } +#endif + } +} + +char * +lt_extend_str (const char *orig_value, const char *add, int to_end) +{ + char *new_value; + if (orig_value && *orig_value) + { + int orig_value_len = strlen (orig_value); + int add_len = strlen (add); + new_value = XMALLOC (char, add_len + orig_value_len + 1); + if (to_end) + { + strcpy (new_value, orig_value); + strcpy (new_value + orig_value_len, add); + } + else + { + strcpy (new_value, add); + strcpy (new_value + add_len, orig_value); + } + } + else + { + new_value = xstrdup (add); + } + return new_value; +} + +void +lt_update_exe_path (const char *name, const char *value) +{ + lt_debugprintf (__FILE__, __LINE__, + "(lt_update_exe_path) modifying '%s' by prepending '%s'\n", + nonnull (name), nonnull (value)); + + if (name && *name && value && *value) + { + char *new_value = lt_extend_str (getenv (name), value, 0); + /* some systems can't cope with a ':'-terminated path #' */ + int len = strlen (new_value); + while (((len = strlen (new_value)) > 0) && IS_PATH_SEPARATOR (new_value[len-1])) + { + new_value[len-1] = '\0'; + } + lt_setenv (name, new_value); + XFREE (new_value); + } +} + +void +lt_update_lib_path (const char *name, const char *value) +{ + lt_debugprintf (__FILE__, __LINE__, + "(lt_update_lib_path) modifying '%s' by prepending '%s'\n", + nonnull (name), nonnull (value)); + + if (name && *name && value && *value) + { + char *new_value = lt_extend_str (getenv (name), value, 0); + lt_setenv (name, new_value); + XFREE (new_value); + } +} + +EOF + case $host_os in + mingw*) + cat <<"EOF" + +/* Prepares an argument vector before calling spawn(). + Note that spawn() does not by itself call the command interpreter + (getenv ("COMSPEC") != NULL ? getenv ("COMSPEC") : + ({ OSVERSIONINFO v; v.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); + GetVersionEx(&v); + v.dwPlatformId == VER_PLATFORM_WIN32_NT; + }) ? "cmd.exe" : "command.com"). + Instead it simply concatenates the arguments, separated by ' ', and calls + CreateProcess(). We must quote the arguments since Win32 CreateProcess() + interprets characters like ' ', '\t', '\\', '"' (but not '<' and '>') in a + special way: + - Space and tab are interpreted as delimiters. They are not treated as + delimiters if they are surrounded by double quotes: "...". + - Unescaped double quotes are removed from the input. Their only effect is + that within double quotes, space and tab are treated like normal + characters. + - Backslashes not followed by double quotes are not special. + - But 2*n+1 backslashes followed by a double quote become + n backslashes followed by a double quote (n >= 0): + \" -> " + \\\" -> \" + \\\\\" -> \\" + */ +#define SHELL_SPECIAL_CHARS "\"\\ \001\002\003\004\005\006\007\010\011\012\013\014\015\016\017\020\021\022\023\024\025\026\027\030\031\032\033\034\035\036\037" +#define SHELL_SPACE_CHARS " \001\002\003\004\005\006\007\010\011\012\013\014\015\016\017\020\021\022\023\024\025\026\027\030\031\032\033\034\035\036\037" +char ** +prepare_spawn (char **argv) +{ + size_t argc; + char **new_argv; + size_t i; + + /* Count number of arguments. */ + for (argc = 0; argv[argc] != NULL; argc++) + ; + + /* Allocate new argument vector. */ + new_argv = XMALLOC (char *, argc + 1); + + /* Put quoted arguments into the new argument vector. */ + for (i = 0; i < argc; i++) + { + const char *string = argv[i]; + + if (string[0] == '\0') + new_argv[i] = xstrdup ("\"\""); + else if (strpbrk (string, SHELL_SPECIAL_CHARS) != NULL) + { + int quote_around = (strpbrk (string, SHELL_SPACE_CHARS) != NULL); + size_t length; + unsigned int backslashes; + const char *s; + char *quoted_string; + char *p; + + length = 0; + backslashes = 0; + if (quote_around) + length++; + for (s = string; *s != '\0'; s++) + { + char c = *s; + if (c == '"') + length += backslashes + 1; + length++; + if (c == '\\') + backslashes++; + else + backslashes = 0; + } + if (quote_around) + length += backslashes + 1; + + quoted_string = XMALLOC (char, length + 1); + + p = quoted_string; + backslashes = 0; + if (quote_around) + *p++ = '"'; + for (s = string; *s != '\0'; s++) + { + char c = *s; + if (c == '"') + { + unsigned int j; + for (j = backslashes + 1; j > 0; j--) + *p++ = '\\'; + } + *p++ = c; + if (c == '\\') + backslashes++; + else + backslashes = 0; + } + if (quote_around) + { + unsigned int j; + for (j = backslashes; j > 0; j--) + *p++ = '\\'; + *p++ = '"'; + } + *p = '\0'; + + new_argv[i] = quoted_string; + } + else + new_argv[i] = (char *) string; + } + new_argv[argc] = NULL; + + return new_argv; +} +EOF + ;; + esac + + cat <<"EOF" +void lt_dump_script (FILE* f) +{ +EOF + func_emit_wrapper yes | + $SED -n -e ' +s/^\(.\{79\}\)\(..*\)/\1\ +\2/ +h +s/\([\\"]\)/\\\1/g +s/$/\\n/ +s/\([^\n]*\).*/ fputs ("\1", f);/p +g +D' + cat <<"EOF" +} +EOF +} +# end: func_emit_cwrapperexe_src + +# func_win32_import_lib_p ARG +# True if ARG is an import lib, as indicated by $file_magic_cmd +func_win32_import_lib_p () +{ + $opt_debug + case `eval $file_magic_cmd \"\$1\" 2>/dev/null | $SED -e 10q` in + *import*) : ;; + *) false ;; + esac +} + +# func_mode_link arg... +func_mode_link () +{ + $opt_debug + case $host in + *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-cegcc*) + # It is impossible to link a dll without this setting, and + # we shouldn't force the makefile maintainer to figure out + # which system we are compiling for in order to pass an extra + # flag for every libtool invocation. + # allow_undefined=no + + # FIXME: Unfortunately, there are problems with the above when trying + # to make a dll which has undefined symbols, in which case not + # even a static library is built. For now, we need to specify + # -no-undefined on the libtool link line when we can be certain + # that all symbols are satisfied, otherwise we get a static library. + allow_undefined=yes + ;; + *) + allow_undefined=yes + ;; + esac + libtool_args=$nonopt + base_compile="$nonopt $@" + compile_command=$nonopt + finalize_command=$nonopt + + compile_rpath= + finalize_rpath= + compile_shlibpath= + finalize_shlibpath= + convenience= + old_convenience= + deplibs= + old_deplibs= + compiler_flags= + linker_flags= + dllsearchpath= + lib_search_path=`pwd` + inst_prefix_dir= + new_inherited_linker_flags= + + avoid_version=no + bindir= + dlfiles= + dlprefiles= + dlself=no + export_dynamic=no + export_symbols= + export_symbols_regex= + generated= + libobjs= + ltlibs= + module=no + no_install=no + objs= + non_pic_objects= + precious_files_regex= + prefer_static_libs=no + preload=no + prev= + prevarg= + release= + rpath= + xrpath= + perm_rpath= + temp_rpath= + thread_safe=no + vinfo= + vinfo_number=no + weak_libs= + single_module="${wl}-single_module" + func_infer_tag $base_compile + + # We need to know -static, to get the right output filenames. + for arg + do + case $arg in + -shared) + test "$build_libtool_libs" != yes && \ + func_fatal_configuration "can not build a shared library" + build_old_libs=no + break + ;; + -all-static | -static | -static-libtool-libs) + case $arg in + -all-static) + if test "$build_libtool_libs" = yes && test -z "$link_static_flag"; then + func_warning "complete static linking is impossible in this configuration" + fi + if test -n "$link_static_flag"; then + dlopen_self=$dlopen_self_static + fi + prefer_static_libs=yes + ;; + -static) + if test -z "$pic_flag" && test -n "$link_static_flag"; then + dlopen_self=$dlopen_self_static + fi + prefer_static_libs=built + ;; + -static-libtool-libs) + if test -z "$pic_flag" && test -n "$link_static_flag"; then + dlopen_self=$dlopen_self_static + fi + prefer_static_libs=yes + ;; + esac + build_libtool_libs=no + build_old_libs=yes + break + ;; + esac + done + + # See if our shared archives depend on static archives. + test -n "$old_archive_from_new_cmds" && build_old_libs=yes + + # Go through the arguments, transforming them on the way. + while test "$#" -gt 0; do + arg="$1" + shift + func_quote_for_eval "$arg" + qarg=$func_quote_for_eval_unquoted_result + func_append libtool_args " $func_quote_for_eval_result" + + # If the previous option needs an argument, assign it. + if test -n "$prev"; then + case $prev in + output) + func_append compile_command " @OUTPUT@" + func_append finalize_command " @OUTPUT@" + ;; + esac + + case $prev in + bindir) + bindir="$arg" + prev= + continue + ;; + dlfiles|dlprefiles) + if test "$preload" = no; then + # Add the symbol object into the linking commands. + func_append compile_command " @SYMFILE@" + func_append finalize_command " @SYMFILE@" + preload=yes + fi + case $arg in + *.la | *.lo) ;; # We handle these cases below. + force) + if test "$dlself" = no; then + dlself=needless + export_dynamic=yes + fi + prev= + continue + ;; + self) + if test "$prev" = dlprefiles; then + dlself=yes + elif test "$prev" = dlfiles && test "$dlopen_self" != yes; then + dlself=yes + else + dlself=needless + export_dynamic=yes + fi + prev= + continue + ;; + *) + if test "$prev" = dlfiles; then + func_append dlfiles " $arg" + else + func_append dlprefiles " $arg" + fi + prev= + continue + ;; + esac + ;; + expsyms) + export_symbols="$arg" + test -f "$arg" \ + || func_fatal_error "symbol file \`$arg' does not exist" + prev= + continue + ;; + expsyms_regex) + export_symbols_regex="$arg" + prev= + continue + ;; + framework) + case $host in + *-*-darwin*) + case "$deplibs " in + *" $qarg.ltframework "*) ;; + *) func_append deplibs " $qarg.ltframework" # this is fixed later + ;; + esac + ;; + esac + prev= + continue + ;; + inst_prefix) + inst_prefix_dir="$arg" + prev= + continue + ;; + objectlist) + if test -f "$arg"; then + save_arg=$arg + moreargs= + for fil in `cat "$save_arg"` + do +# func_append moreargs " $fil" + arg=$fil + # A libtool-controlled object. + + # Check to see that this really is a libtool object. + if func_lalib_unsafe_p "$arg"; then + pic_object= + non_pic_object= + + # Read the .lo file + func_source "$arg" + + if test -z "$pic_object" || + test -z "$non_pic_object" || + test "$pic_object" = none && + test "$non_pic_object" = none; then + func_fatal_error "cannot find name of object for \`$arg'" + fi + + # Extract subdirectory from the argument. + func_dirname "$arg" "/" "" + xdir="$func_dirname_result" + + if test "$pic_object" != none; then + # Prepend the subdirectory the object is found in. + pic_object="$xdir$pic_object" + + if test "$prev" = dlfiles; then + if test "$build_libtool_libs" = yes && test "$dlopen_support" = yes; then + func_append dlfiles " $pic_object" + prev= + continue + else + # If libtool objects are unsupported, then we need to preload. + prev=dlprefiles + fi + fi + + # CHECK ME: I think I busted this. -Ossama + if test "$prev" = dlprefiles; then + # Preload the old-style object. + func_append dlprefiles " $pic_object" + prev= + fi + + # A PIC object. + func_append libobjs " $pic_object" + arg="$pic_object" + fi + + # Non-PIC object. + if test "$non_pic_object" != none; then + # Prepend the subdirectory the object is found in. + non_pic_object="$xdir$non_pic_object" + + # A standard non-PIC object + func_append non_pic_objects " $non_pic_object" + if test -z "$pic_object" || test "$pic_object" = none ; then + arg="$non_pic_object" + fi + else + # If the PIC object exists, use it instead. + # $xdir was prepended to $pic_object above. + non_pic_object="$pic_object" + func_append non_pic_objects " $non_pic_object" + fi + else + # Only an error if not doing a dry-run. + if $opt_dry_run; then + # Extract subdirectory from the argument. + func_dirname "$arg" "/" "" + xdir="$func_dirname_result" + + func_lo2o "$arg" + pic_object=$xdir$objdir/$func_lo2o_result + non_pic_object=$xdir$func_lo2o_result + func_append libobjs " $pic_object" + func_append non_pic_objects " $non_pic_object" + else + func_fatal_error "\`$arg' is not a valid libtool object" + fi + fi + done + else + func_fatal_error "link input file \`$arg' does not exist" + fi + arg=$save_arg + prev= + continue + ;; + precious_regex) + precious_files_regex="$arg" + prev= + continue + ;; + release) + release="-$arg" + prev= + continue + ;; + rpath | xrpath) + # We need an absolute path. + case $arg in + [\\/]* | [A-Za-z]:[\\/]*) ;; + *) + func_fatal_error "only absolute run-paths are allowed" + ;; + esac + if test "$prev" = rpath; then + case "$rpath " in + *" $arg "*) ;; + *) func_append rpath " $arg" ;; + esac + else + case "$xrpath " in + *" $arg "*) ;; + *) func_append xrpath " $arg" ;; + esac + fi + prev= + continue + ;; + shrext) + shrext_cmds="$arg" + prev= + continue + ;; + weak) + func_append weak_libs " $arg" + prev= + continue + ;; + xcclinker) + func_append linker_flags " $qarg" + func_append compiler_flags " $qarg" + prev= + func_append compile_command " $qarg" + func_append finalize_command " $qarg" + continue + ;; + xcompiler) + func_append compiler_flags " $qarg" + prev= + func_append compile_command " $qarg" + func_append finalize_command " $qarg" + continue + ;; + xlinker) + func_append linker_flags " $qarg" + func_append compiler_flags " $wl$qarg" + prev= + func_append compile_command " $wl$qarg" + func_append finalize_command " $wl$qarg" + continue + ;; + *) + eval "$prev=\"\$arg\"" + prev= + continue + ;; + esac + fi # test -n "$prev" + + prevarg="$arg" + + case $arg in + -all-static) + if test -n "$link_static_flag"; then + # See comment for -static flag below, for more details. + func_append compile_command " $link_static_flag" + func_append finalize_command " $link_static_flag" + fi + continue + ;; + + -allow-undefined) + # FIXME: remove this flag sometime in the future. + func_fatal_error "\`-allow-undefined' must not be used because it is the default" + ;; + + -avoid-version) + avoid_version=yes + continue + ;; + + -bindir) + prev=bindir + continue + ;; + + -dlopen) + prev=dlfiles + continue + ;; + + -dlpreopen) + prev=dlprefiles + continue + ;; + + -export-dynamic) + export_dynamic=yes + continue + ;; + + -export-symbols | -export-symbols-regex) + if test -n "$export_symbols" || test -n "$export_symbols_regex"; then + func_fatal_error "more than one -exported-symbols argument is not allowed" + fi + if test "X$arg" = "X-export-symbols"; then + prev=expsyms + else + prev=expsyms_regex + fi + continue + ;; + + -framework) + prev=framework + continue + ;; + + -inst-prefix-dir) + prev=inst_prefix + continue + ;; + + # The native IRIX linker understands -LANG:*, -LIST:* and -LNO:* + # so, if we see these flags be careful not to treat them like -L + -L[A-Z][A-Z]*:*) + case $with_gcc/$host in + no/*-*-irix* | /*-*-irix*) + func_append compile_command " $arg" + func_append finalize_command " $arg" + ;; + esac + continue + ;; + + -L*) + func_stripname "-L" '' "$arg" + if test -z "$func_stripname_result"; then + if test "$#" -gt 0; then + func_fatal_error "require no space between \`-L' and \`$1'" + else + func_fatal_error "need path for \`-L' option" + fi + fi + func_resolve_sysroot "$func_stripname_result" + dir=$func_resolve_sysroot_result + # We need an absolute path. + case $dir in + [\\/]* | [A-Za-z]:[\\/]*) ;; + *) + absdir=`cd "$dir" && pwd` + test -z "$absdir" && \ + func_fatal_error "cannot determine absolute directory name of \`$dir'" + dir="$absdir" + ;; + esac + case "$deplibs " in + *" -L$dir "* | *" $arg "*) + # Will only happen for absolute or sysroot arguments + ;; + *) + # Preserve sysroot, but never include relative directories + case $dir in + [\\/]* | [A-Za-z]:[\\/]* | =*) func_append deplibs " $arg" ;; + *) func_append deplibs " -L$dir" ;; + esac + func_append lib_search_path " $dir" + ;; + esac + case $host in + *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-cegcc*) + testbindir=`$ECHO "$dir" | $SED 's*/lib$*/bin*'` + case :$dllsearchpath: in + *":$dir:"*) ;; + ::) dllsearchpath=$dir;; + *) func_append dllsearchpath ":$dir";; + esac + case :$dllsearchpath: in + *":$testbindir:"*) ;; + ::) dllsearchpath=$testbindir;; + *) func_append dllsearchpath ":$testbindir";; + esac + ;; + esac + continue + ;; + + -l*) + if test "X$arg" = "X-lc" || test "X$arg" = "X-lm"; then + case $host in + *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-beos* | *-cegcc* | *-*-haiku*) + # These systems don't actually have a C or math library (as such) + continue + ;; + *-*-os2*) + # These systems don't actually have a C library (as such) + test "X$arg" = "X-lc" && continue + ;; + *-*-openbsd* | *-*-freebsd* | *-*-dragonfly*) + # Do not include libc due to us having libc/libc_r. + test "X$arg" = "X-lc" && continue + ;; + *-*-rhapsody* | *-*-darwin1.[012]) + # Rhapsody C and math libraries are in the System framework + func_append deplibs " System.ltframework" + continue + ;; + *-*-sco3.2v5* | *-*-sco5v6*) + # Causes problems with __ctype + test "X$arg" = "X-lc" && continue + ;; + *-*-sysv4.2uw2* | *-*-sysv5* | *-*-unixware* | *-*-OpenUNIX*) + # Compiler inserts libc in the correct place for threads to work + test "X$arg" = "X-lc" && continue + ;; + esac + elif test "X$arg" = "X-lc_r"; then + case $host in + *-*-openbsd* | *-*-freebsd* | *-*-dragonfly*) + # Do not include libc_r directly, use -pthread flag. + continue + ;; + esac + fi + func_append deplibs " $arg" + continue + ;; + + -module) + module=yes + continue + ;; + + # Tru64 UNIX uses -model [arg] to determine the layout of C++ + # classes, name mangling, and exception handling. + # Darwin uses the -arch flag to determine output architecture. + -model|-arch|-isysroot|--sysroot) + func_append compiler_flags " $arg" + func_append compile_command " $arg" + func_append finalize_command " $arg" + prev=xcompiler + continue + ;; + + -mt|-mthreads|-kthread|-Kthread|-pthread|-pthreads|--thread-safe \ + |-threads|-fopenmp|-openmp|-mp|-xopenmp|-omp|-qsmp=*) + func_append compiler_flags " $arg" + func_append compile_command " $arg" + func_append finalize_command " $arg" + case "$new_inherited_linker_flags " in + *" $arg "*) ;; + * ) func_append new_inherited_linker_flags " $arg" ;; + esac + continue + ;; + + -multi_module) + single_module="${wl}-multi_module" + continue + ;; + + -no-fast-install) + fast_install=no + continue + ;; + + -no-install) + case $host in + *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-*-darwin* | *-cegcc*) + # The PATH hackery in wrapper scripts is required on Windows + # and Darwin in order for the loader to find any dlls it needs. + func_warning "\`-no-install' is ignored for $host" + func_warning "assuming \`-no-fast-install' instead" + fast_install=no + ;; + *) no_install=yes ;; + esac + continue + ;; + + -no-undefined) + allow_undefined=no + continue + ;; + + -objectlist) + prev=objectlist + continue + ;; + + -o) prev=output ;; + + -precious-files-regex) + prev=precious_regex + continue + ;; + + -release) + prev=release + continue + ;; + + -rpath) + prev=rpath + continue + ;; + + -R) + prev=xrpath + continue + ;; + + -R*) + func_stripname '-R' '' "$arg" + dir=$func_stripname_result + # We need an absolute path. + case $dir in + [\\/]* | [A-Za-z]:[\\/]*) ;; + =*) + func_stripname '=' '' "$dir" + dir=$lt_sysroot$func_stripname_result + ;; + *) + func_fatal_error "only absolute run-paths are allowed" + ;; + esac + case "$xrpath " in + *" $dir "*) ;; + *) func_append xrpath " $dir" ;; + esac + continue + ;; + + -shared) + # The effects of -shared are defined in a previous loop. + continue + ;; + + -shrext) + prev=shrext + continue + ;; + + -static | -static-libtool-libs) + # The effects of -static are defined in a previous loop. + # We used to do the same as -all-static on platforms that + # didn't have a PIC flag, but the assumption that the effects + # would be equivalent was wrong. It would break on at least + # Digital Unix and AIX. + continue + ;; + + -thread-safe) + thread_safe=yes + continue + ;; + + -version-info) + prev=vinfo + continue + ;; + + -version-number) + prev=vinfo + vinfo_number=yes + continue + ;; + + -weak) + prev=weak + continue + ;; + + -Wc,*) + func_stripname '-Wc,' '' "$arg" + args=$func_stripname_result + arg= + save_ifs="$IFS"; IFS=',' + for flag in $args; do + IFS="$save_ifs" + func_quote_for_eval "$flag" + func_append arg " $func_quote_for_eval_result" + func_append compiler_flags " $func_quote_for_eval_result" + done + IFS="$save_ifs" + func_stripname ' ' '' "$arg" + arg=$func_stripname_result + ;; + + -Wl,*) + func_stripname '-Wl,' '' "$arg" + args=$func_stripname_result + arg= + save_ifs="$IFS"; IFS=',' + for flag in $args; do + IFS="$save_ifs" + func_quote_for_eval "$flag" + func_append arg " $wl$func_quote_for_eval_result" + func_append compiler_flags " $wl$func_quote_for_eval_result" + func_append linker_flags " $func_quote_for_eval_result" + done + IFS="$save_ifs" + func_stripname ' ' '' "$arg" + arg=$func_stripname_result + ;; + + -Xcompiler) + prev=xcompiler + continue + ;; + + -Xlinker) + prev=xlinker + continue + ;; + + -XCClinker) + prev=xcclinker + continue + ;; + + # -msg_* for osf cc + -msg_*) + func_quote_for_eval "$arg" + arg="$func_quote_for_eval_result" + ;; + + # Flags to be passed through unchanged, with rationale: + # -64, -mips[0-9] enable 64-bit mode for the SGI compiler + # -r[0-9][0-9]* specify processor for the SGI compiler + # -xarch=*, -xtarget=* enable 64-bit mode for the Sun compiler + # +DA*, +DD* enable 64-bit mode for the HP compiler + # -q* compiler args for the IBM compiler + # -m*, -t[45]*, -txscale* architecture-specific flags for GCC + # -F/path path to uninstalled frameworks, gcc on darwin + # -p, -pg, --coverage, -fprofile-* profiling flags for GCC + # @file GCC response files + # -tp=* Portland pgcc target processor selection + # --sysroot=* for sysroot support + # -O*, -flto*, -fwhopr*, -fuse-linker-plugin GCC link-time optimization + -64|-mips[0-9]|-r[0-9][0-9]*|-xarch=*|-xtarget=*|+DA*|+DD*|-q*|-m*| \ + -t[45]*|-txscale*|-p|-pg|--coverage|-fprofile-*|-F*|@*|-tp=*|--sysroot=*| \ + -O*|-flto*|-fwhopr*|-fuse-linker-plugin) + func_quote_for_eval "$arg" + arg="$func_quote_for_eval_result" + func_append compile_command " $arg" + func_append finalize_command " $arg" + func_append compiler_flags " $arg" + continue + ;; + + # Some other compiler flag. + -* | +*) + func_quote_for_eval "$arg" + arg="$func_quote_for_eval_result" + ;; + + *.$objext) + # A standard object. + func_append objs " $arg" + ;; + + *.lo) + # A libtool-controlled object. + + # Check to see that this really is a libtool object. + if func_lalib_unsafe_p "$arg"; then + pic_object= + non_pic_object= + + # Read the .lo file + func_source "$arg" + + if test -z "$pic_object" || + test -z "$non_pic_object" || + test "$pic_object" = none && + test "$non_pic_object" = none; then + func_fatal_error "cannot find name of object for \`$arg'" + fi + + # Extract subdirectory from the argument. + func_dirname "$arg" "/" "" + xdir="$func_dirname_result" + + if test "$pic_object" != none; then + # Prepend the subdirectory the object is found in. + pic_object="$xdir$pic_object" + + if test "$prev" = dlfiles; then + if test "$build_libtool_libs" = yes && test "$dlopen_support" = yes; then + func_append dlfiles " $pic_object" + prev= + continue + else + # If libtool objects are unsupported, then we need to preload. + prev=dlprefiles + fi + fi + + # CHECK ME: I think I busted this. -Ossama + if test "$prev" = dlprefiles; then + # Preload the old-style object. + func_append dlprefiles " $pic_object" + prev= + fi + + # A PIC object. + func_append libobjs " $pic_object" + arg="$pic_object" + fi + + # Non-PIC object. + if test "$non_pic_object" != none; then + # Prepend the subdirectory the object is found in. + non_pic_object="$xdir$non_pic_object" + + # A standard non-PIC object + func_append non_pic_objects " $non_pic_object" + if test -z "$pic_object" || test "$pic_object" = none ; then + arg="$non_pic_object" + fi + else + # If the PIC object exists, use it instead. + # $xdir was prepended to $pic_object above. + non_pic_object="$pic_object" + func_append non_pic_objects " $non_pic_object" + fi + else + # Only an error if not doing a dry-run. + if $opt_dry_run; then + # Extract subdirectory from the argument. + func_dirname "$arg" "/" "" + xdir="$func_dirname_result" + + func_lo2o "$arg" + pic_object=$xdir$objdir/$func_lo2o_result + non_pic_object=$xdir$func_lo2o_result + func_append libobjs " $pic_object" + func_append non_pic_objects " $non_pic_object" + else + func_fatal_error "\`$arg' is not a valid libtool object" + fi + fi + ;; + + *.$libext) + # An archive. + func_append deplibs " $arg" + func_append old_deplibs " $arg" + continue + ;; + + *.la) + # A libtool-controlled library. + + func_resolve_sysroot "$arg" + if test "$prev" = dlfiles; then + # This library was specified with -dlopen. + func_append dlfiles " $func_resolve_sysroot_result" + prev= + elif test "$prev" = dlprefiles; then + # The library was specified with -dlpreopen. + func_append dlprefiles " $func_resolve_sysroot_result" + prev= + else + func_append deplibs " $func_resolve_sysroot_result" + fi + continue + ;; + + # Some other compiler argument. + *) + # Unknown arguments in both finalize_command and compile_command need + # to be aesthetically quoted because they are evaled later. + func_quote_for_eval "$arg" + arg="$func_quote_for_eval_result" + ;; + esac # arg + + # Now actually substitute the argument into the commands. + if test -n "$arg"; then + func_append compile_command " $arg" + func_append finalize_command " $arg" + fi + done # argument parsing loop + + test -n "$prev" && \ + func_fatal_help "the \`$prevarg' option requires an argument" + + if test "$export_dynamic" = yes && test -n "$export_dynamic_flag_spec"; then + eval arg=\"$export_dynamic_flag_spec\" + func_append compile_command " $arg" + func_append finalize_command " $arg" + fi + + oldlibs= + # calculate the name of the file, without its directory + func_basename "$output" + outputname="$func_basename_result" + libobjs_save="$libobjs" + + if test -n "$shlibpath_var"; then + # get the directories listed in $shlibpath_var + eval shlib_search_path=\`\$ECHO \"\${$shlibpath_var}\" \| \$SED \'s/:/ /g\'\` + else + shlib_search_path= + fi + eval sys_lib_search_path=\"$sys_lib_search_path_spec\" + eval sys_lib_dlsearch_path=\"$sys_lib_dlsearch_path_spec\" + + func_dirname "$output" "/" "" + output_objdir="$func_dirname_result$objdir" + func_to_tool_file "$output_objdir/" + tool_output_objdir=$func_to_tool_file_result + # Create the object directory. + func_mkdir_p "$output_objdir" + + # Determine the type of output + case $output in + "") + func_fatal_help "you must specify an output file" + ;; + *.$libext) linkmode=oldlib ;; + *.lo | *.$objext) linkmode=obj ;; + *.la) linkmode=lib ;; + *) linkmode=prog ;; # Anything else should be a program. + esac + + specialdeplibs= + + libs= + # Find all interdependent deplibs by searching for libraries + # that are linked more than once (e.g. -la -lb -la) + for deplib in $deplibs; do + if $opt_preserve_dup_deps ; then + case "$libs " in + *" $deplib "*) func_append specialdeplibs " $deplib" ;; + esac + fi + func_append libs " $deplib" + done + + if test "$linkmode" = lib; then + libs="$predeps $libs $compiler_lib_search_path $postdeps" + + # Compute libraries that are listed more than once in $predeps + # $postdeps and mark them as special (i.e., whose duplicates are + # not to be eliminated). + pre_post_deps= + if $opt_duplicate_compiler_generated_deps; then + for pre_post_dep in $predeps $postdeps; do + case "$pre_post_deps " in + *" $pre_post_dep "*) func_append specialdeplibs " $pre_post_deps" ;; + esac + func_append pre_post_deps " $pre_post_dep" + done + fi + pre_post_deps= + fi + + deplibs= + newdependency_libs= + newlib_search_path= + need_relink=no # whether we're linking any uninstalled libtool libraries + notinst_deplibs= # not-installed libtool libraries + notinst_path= # paths that contain not-installed libtool libraries + + case $linkmode in + lib) + passes="conv dlpreopen link" + for file in $dlfiles $dlprefiles; do + case $file in + *.la) ;; + *) + func_fatal_help "libraries can \`-dlopen' only libtool libraries: $file" + ;; + esac + done + ;; + prog) + compile_deplibs= + finalize_deplibs= + alldeplibs=no + newdlfiles= + newdlprefiles= + passes="conv scan dlopen dlpreopen link" + ;; + *) passes="conv" + ;; + esac + + for pass in $passes; do + # The preopen pass in lib mode reverses $deplibs; put it back here + # so that -L comes before libs that need it for instance... + if test "$linkmode,$pass" = "lib,link"; then + ## FIXME: Find the place where the list is rebuilt in the wrong + ## order, and fix it there properly + tmp_deplibs= + for deplib in $deplibs; do + tmp_deplibs="$deplib $tmp_deplibs" + done + deplibs="$tmp_deplibs" + fi + + if test "$linkmode,$pass" = "lib,link" || + test "$linkmode,$pass" = "prog,scan"; then + libs="$deplibs" + deplibs= + fi + if test "$linkmode" = prog; then + case $pass in + dlopen) libs="$dlfiles" ;; + dlpreopen) libs="$dlprefiles" ;; + link) + libs="$deplibs %DEPLIBS%" + test "X$link_all_deplibs" != Xno && libs="$libs $dependency_libs" + ;; + esac + fi + if test "$linkmode,$pass" = "lib,dlpreopen"; then + # Collect and forward deplibs of preopened libtool libs + for lib in $dlprefiles; do + # Ignore non-libtool-libs + dependency_libs= + func_resolve_sysroot "$lib" + case $lib in + *.la) func_source "$func_resolve_sysroot_result" ;; + esac + + # Collect preopened libtool deplibs, except any this library + # has declared as weak libs + for deplib in $dependency_libs; do + func_basename "$deplib" + deplib_base=$func_basename_result + case " $weak_libs " in + *" $deplib_base "*) ;; + *) func_append deplibs " $deplib" ;; + esac + done + done + libs="$dlprefiles" + fi + if test "$pass" = dlopen; then + # Collect dlpreopened libraries + save_deplibs="$deplibs" + deplibs= + fi + + for deplib in $libs; do + lib= + found=no + case $deplib in + -mt|-mthreads|-kthread|-Kthread|-pthread|-pthreads|--thread-safe \ + |-threads|-fopenmp|-openmp|-mp|-xopenmp|-omp|-qsmp=*) + if test "$linkmode,$pass" = "prog,link"; then + compile_deplibs="$deplib $compile_deplibs" + finalize_deplibs="$deplib $finalize_deplibs" + else + func_append compiler_flags " $deplib" + if test "$linkmode" = lib ; then + case "$new_inherited_linker_flags " in + *" $deplib "*) ;; + * ) func_append new_inherited_linker_flags " $deplib" ;; + esac + fi + fi + continue + ;; + -l*) + if test "$linkmode" != lib && test "$linkmode" != prog; then + func_warning "\`-l' is ignored for archives/objects" + continue + fi + func_stripname '-l' '' "$deplib" + name=$func_stripname_result + if test "$linkmode" = lib; then + searchdirs="$newlib_search_path $lib_search_path $compiler_lib_search_dirs $sys_lib_search_path $shlib_search_path" + else + searchdirs="$newlib_search_path $lib_search_path $sys_lib_search_path $shlib_search_path" + fi + for searchdir in $searchdirs; do + for search_ext in .la $std_shrext .so .a; do + # Search the libtool library + lib="$searchdir/lib${name}${search_ext}" + if test -f "$lib"; then + if test "$search_ext" = ".la"; then + found=yes + else + found=no + fi + break 2 + fi + done + done + if test "$found" != yes; then + # deplib doesn't seem to be a libtool library + if test "$linkmode,$pass" = "prog,link"; then + compile_deplibs="$deplib $compile_deplibs" + finalize_deplibs="$deplib $finalize_deplibs" + else + deplibs="$deplib $deplibs" + test "$linkmode" = lib && newdependency_libs="$deplib $newdependency_libs" + fi + continue + else # deplib is a libtool library + # If $allow_libtool_libs_with_static_runtimes && $deplib is a stdlib, + # We need to do some special things here, and not later. + if test "X$allow_libtool_libs_with_static_runtimes" = "Xyes" ; then + case " $predeps $postdeps " in + *" $deplib "*) + if func_lalib_p "$lib"; then + library_names= + old_library= + func_source "$lib" + for l in $old_library $library_names; do + ll="$l" + done + if test "X$ll" = "X$old_library" ; then # only static version available + found=no + func_dirname "$lib" "" "." + ladir="$func_dirname_result" + lib=$ladir/$old_library + if test "$linkmode,$pass" = "prog,link"; then + compile_deplibs="$deplib $compile_deplibs" + finalize_deplibs="$deplib $finalize_deplibs" + else + deplibs="$deplib $deplibs" + test "$linkmode" = lib && newdependency_libs="$deplib $newdependency_libs" + fi + continue + fi + fi + ;; + *) ;; + esac + fi + fi + ;; # -l + *.ltframework) + if test "$linkmode,$pass" = "prog,link"; then + compile_deplibs="$deplib $compile_deplibs" + finalize_deplibs="$deplib $finalize_deplibs" + else + deplibs="$deplib $deplibs" + if test "$linkmode" = lib ; then + case "$new_inherited_linker_flags " in + *" $deplib "*) ;; + * ) func_append new_inherited_linker_flags " $deplib" ;; + esac + fi + fi + continue + ;; + -L*) + case $linkmode in + lib) + deplibs="$deplib $deplibs" + test "$pass" = conv && continue + newdependency_libs="$deplib $newdependency_libs" + func_stripname '-L' '' "$deplib" + func_resolve_sysroot "$func_stripname_result" + func_append newlib_search_path " $func_resolve_sysroot_result" + ;; + prog) + if test "$pass" = conv; then + deplibs="$deplib $deplibs" + continue + fi + if test "$pass" = scan; then + deplibs="$deplib $deplibs" + else + compile_deplibs="$deplib $compile_deplibs" + finalize_deplibs="$deplib $finalize_deplibs" + fi + func_stripname '-L' '' "$deplib" + func_resolve_sysroot "$func_stripname_result" + func_append newlib_search_path " $func_resolve_sysroot_result" + ;; + *) + func_warning "\`-L' is ignored for archives/objects" + ;; + esac # linkmode + continue + ;; # -L + -R*) + if test "$pass" = link; then + func_stripname '-R' '' "$deplib" + func_resolve_sysroot "$func_stripname_result" + dir=$func_resolve_sysroot_result + # Make sure the xrpath contains only unique directories. + case "$xrpath " in + *" $dir "*) ;; + *) func_append xrpath " $dir" ;; + esac + fi + deplibs="$deplib $deplibs" + continue + ;; + *.la) + func_resolve_sysroot "$deplib" + lib=$func_resolve_sysroot_result + ;; + *.$libext) + if test "$pass" = conv; then + deplibs="$deplib $deplibs" + continue + fi + case $linkmode in + lib) + # Linking convenience modules into shared libraries is allowed, + # but linking other static libraries is non-portable. + case " $dlpreconveniencelibs " in + *" $deplib "*) ;; + *) + valid_a_lib=no + case $deplibs_check_method in + match_pattern*) + set dummy $deplibs_check_method; shift + match_pattern_regex=`expr "$deplibs_check_method" : "$1 \(.*\)"` + if eval "\$ECHO \"$deplib\"" 2>/dev/null | $SED 10q \ + | $EGREP "$match_pattern_regex" > /dev/null; then + valid_a_lib=yes + fi + ;; + pass_all) + valid_a_lib=yes + ;; + esac + if test "$valid_a_lib" != yes; then + echo + $ECHO "*** Warning: Trying to link with static lib archive $deplib." + echo "*** I have the capability to make that library automatically link in when" + echo "*** you link to this library. But I can only do this if you have a" + echo "*** shared version of the library, which you do not appear to have" + echo "*** because the file extensions .$libext of this argument makes me believe" + echo "*** that it is just a static archive that I should not use here." + else + echo + $ECHO "*** Warning: Linking the shared library $output against the" + $ECHO "*** static library $deplib is not portable!" + deplibs="$deplib $deplibs" + fi + ;; + esac + continue + ;; + prog) + if test "$pass" != link; then + deplibs="$deplib $deplibs" + else + compile_deplibs="$deplib $compile_deplibs" + finalize_deplibs="$deplib $finalize_deplibs" + fi + continue + ;; + esac # linkmode + ;; # *.$libext + *.lo | *.$objext) + if test "$pass" = conv; then + deplibs="$deplib $deplibs" + elif test "$linkmode" = prog; then + if test "$pass" = dlpreopen || test "$dlopen_support" != yes || test "$build_libtool_libs" = no; then + # If there is no dlopen support or we're linking statically, + # we need to preload. + func_append newdlprefiles " $deplib" + compile_deplibs="$deplib $compile_deplibs" + finalize_deplibs="$deplib $finalize_deplibs" + else + func_append newdlfiles " $deplib" + fi + fi + continue + ;; + %DEPLIBS%) + alldeplibs=yes + continue + ;; + esac # case $deplib + + if test "$found" = yes || test -f "$lib"; then : + else + func_fatal_error "cannot find the library \`$lib' or unhandled argument \`$deplib'" + fi + + # Check to see that this really is a libtool archive. + func_lalib_unsafe_p "$lib" \ + || func_fatal_error "\`$lib' is not a valid libtool archive" + + func_dirname "$lib" "" "." + ladir="$func_dirname_result" + + dlname= + dlopen= + dlpreopen= + libdir= + library_names= + old_library= + inherited_linker_flags= + # If the library was installed with an old release of libtool, + # it will not redefine variables installed, or shouldnotlink + installed=yes + shouldnotlink=no + avoidtemprpath= + + + # Read the .la file + func_source "$lib" + + # Convert "-framework foo" to "foo.ltframework" + if test -n "$inherited_linker_flags"; then + tmp_inherited_linker_flags=`$ECHO "$inherited_linker_flags" | $SED 's/-framework \([^ $]*\)/\1.ltframework/g'` + for tmp_inherited_linker_flag in $tmp_inherited_linker_flags; do + case " $new_inherited_linker_flags " in + *" $tmp_inherited_linker_flag "*) ;; + *) func_append new_inherited_linker_flags " $tmp_inherited_linker_flag";; + esac + done + fi + dependency_libs=`$ECHO " $dependency_libs" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'` + if test "$linkmode,$pass" = "lib,link" || + test "$linkmode,$pass" = "prog,scan" || + { test "$linkmode" != prog && test "$linkmode" != lib; }; then + test -n "$dlopen" && func_append dlfiles " $dlopen" + test -n "$dlpreopen" && func_append dlprefiles " $dlpreopen" + fi + + if test "$pass" = conv; then + # Only check for convenience libraries + deplibs="$lib $deplibs" + if test -z "$libdir"; then + if test -z "$old_library"; then + func_fatal_error "cannot find name of link library for \`$lib'" + fi + # It is a libtool convenience library, so add in its objects. + func_append convenience " $ladir/$objdir/$old_library" + func_append old_convenience " $ladir/$objdir/$old_library" + tmp_libs= + for deplib in $dependency_libs; do + deplibs="$deplib $deplibs" + if $opt_preserve_dup_deps ; then + case "$tmp_libs " in + *" $deplib "*) func_append specialdeplibs " $deplib" ;; + esac + fi + func_append tmp_libs " $deplib" + done + elif test "$linkmode" != prog && test "$linkmode" != lib; then + func_fatal_error "\`$lib' is not a convenience library" + fi + continue + fi # $pass = conv + + + # Get the name of the library we link against. + linklib= + if test -n "$old_library" && + { test "$prefer_static_libs" = yes || + test "$prefer_static_libs,$installed" = "built,no"; }; then + linklib=$old_library + else + for l in $old_library $library_names; do + linklib="$l" + done + fi + if test -z "$linklib"; then + func_fatal_error "cannot find name of link library for \`$lib'" + fi + + # This library was specified with -dlopen. + if test "$pass" = dlopen; then + if test -z "$libdir"; then + func_fatal_error "cannot -dlopen a convenience library: \`$lib'" + fi + if test -z "$dlname" || + test "$dlopen_support" != yes || + test "$build_libtool_libs" = no; then + # If there is no dlname, no dlopen support or we're linking + # statically, we need to preload. We also need to preload any + # dependent libraries so libltdl's deplib preloader doesn't + # bomb out in the load deplibs phase. + func_append dlprefiles " $lib $dependency_libs" + else + func_append newdlfiles " $lib" + fi + continue + fi # $pass = dlopen + + # We need an absolute path. + case $ladir in + [\\/]* | [A-Za-z]:[\\/]*) abs_ladir="$ladir" ;; + *) + abs_ladir=`cd "$ladir" && pwd` + if test -z "$abs_ladir"; then + func_warning "cannot determine absolute directory name of \`$ladir'" + func_warning "passing it literally to the linker, although it might fail" + abs_ladir="$ladir" + fi + ;; + esac + func_basename "$lib" + laname="$func_basename_result" + + # Find the relevant object directory and library name. + if test "X$installed" = Xyes; then + if test ! -f "$lt_sysroot$libdir/$linklib" && test -f "$abs_ladir/$linklib"; then + func_warning "library \`$lib' was moved." + dir="$ladir" + absdir="$abs_ladir" + libdir="$abs_ladir" + else + dir="$lt_sysroot$libdir" + absdir="$lt_sysroot$libdir" + fi + test "X$hardcode_automatic" = Xyes && avoidtemprpath=yes + else + if test ! -f "$ladir/$objdir/$linklib" && test -f "$abs_ladir/$linklib"; then + dir="$ladir" + absdir="$abs_ladir" + # Remove this search path later + func_append notinst_path " $abs_ladir" + else + dir="$ladir/$objdir" + absdir="$abs_ladir/$objdir" + # Remove this search path later + func_append notinst_path " $abs_ladir" + fi + fi # $installed = yes + func_stripname 'lib' '.la' "$laname" + name=$func_stripname_result + + # This library was specified with -dlpreopen. + if test "$pass" = dlpreopen; then + if test -z "$libdir" && test "$linkmode" = prog; then + func_fatal_error "only libraries may -dlpreopen a convenience library: \`$lib'" + fi + case "$host" in + # special handling for platforms with PE-DLLs. + *cygwin* | *mingw* | *cegcc* ) + # Linker will automatically link against shared library if both + # static and shared are present. Therefore, ensure we extract + # symbols from the import library if a shared library is present + # (otherwise, the dlopen module name will be incorrect). We do + # this by putting the import library name into $newdlprefiles. + # We recover the dlopen module name by 'saving' the la file + # name in a special purpose variable, and (later) extracting the + # dlname from the la file. + if test -n "$dlname"; then + func_tr_sh "$dir/$linklib" + eval "libfile_$func_tr_sh_result=\$abs_ladir/\$laname" + func_append newdlprefiles " $dir/$linklib" + else + func_append newdlprefiles " $dir/$old_library" + # Keep a list of preopened convenience libraries to check + # that they are being used correctly in the link pass. + test -z "$libdir" && \ + func_append dlpreconveniencelibs " $dir/$old_library" + fi + ;; + * ) + # Prefer using a static library (so that no silly _DYNAMIC symbols + # are required to link). + if test -n "$old_library"; then + func_append newdlprefiles " $dir/$old_library" + # Keep a list of preopened convenience libraries to check + # that they are being used correctly in the link pass. + test -z "$libdir" && \ + func_append dlpreconveniencelibs " $dir/$old_library" + # Otherwise, use the dlname, so that lt_dlopen finds it. + elif test -n "$dlname"; then + func_append newdlprefiles " $dir/$dlname" + else + func_append newdlprefiles " $dir/$linklib" + fi + ;; + esac + fi # $pass = dlpreopen + + if test -z "$libdir"; then + # Link the convenience library + if test "$linkmode" = lib; then + deplibs="$dir/$old_library $deplibs" + elif test "$linkmode,$pass" = "prog,link"; then + compile_deplibs="$dir/$old_library $compile_deplibs" + finalize_deplibs="$dir/$old_library $finalize_deplibs" + else + deplibs="$lib $deplibs" # used for prog,scan pass + fi + continue + fi + + + if test "$linkmode" = prog && test "$pass" != link; then + func_append newlib_search_path " $ladir" + deplibs="$lib $deplibs" + + linkalldeplibs=no + if test "$link_all_deplibs" != no || test -z "$library_names" || + test "$build_libtool_libs" = no; then + linkalldeplibs=yes + fi + + tmp_libs= + for deplib in $dependency_libs; do + case $deplib in + -L*) func_stripname '-L' '' "$deplib" + func_resolve_sysroot "$func_stripname_result" + func_append newlib_search_path " $func_resolve_sysroot_result" + ;; + esac + # Need to link against all dependency_libs? + if test "$linkalldeplibs" = yes; then + deplibs="$deplib $deplibs" + else + # Need to hardcode shared library paths + # or/and link against static libraries + newdependency_libs="$deplib $newdependency_libs" + fi + if $opt_preserve_dup_deps ; then + case "$tmp_libs " in + *" $deplib "*) func_append specialdeplibs " $deplib" ;; + esac + fi + func_append tmp_libs " $deplib" + done # for deplib + continue + fi # $linkmode = prog... + + if test "$linkmode,$pass" = "prog,link"; then + if test -n "$library_names" && + { { test "$prefer_static_libs" = no || + test "$prefer_static_libs,$installed" = "built,yes"; } || + test -z "$old_library"; }; then + # We need to hardcode the library path + if test -n "$shlibpath_var" && test -z "$avoidtemprpath" ; then + # Make sure the rpath contains only unique directories. + case "$temp_rpath:" in + *"$absdir:"*) ;; + *) func_append temp_rpath "$absdir:" ;; + esac + fi + + # Hardcode the library path. + # Skip directories that are in the system default run-time + # search path. + case " $sys_lib_dlsearch_path " in + *" $absdir "*) ;; + *) + case "$compile_rpath " in + *" $absdir "*) ;; + *) func_append compile_rpath " $absdir" ;; + esac + ;; + esac + case " $sys_lib_dlsearch_path " in + *" $libdir "*) ;; + *) + case "$finalize_rpath " in + *" $libdir "*) ;; + *) func_append finalize_rpath " $libdir" ;; + esac + ;; + esac + fi # $linkmode,$pass = prog,link... + + if test "$alldeplibs" = yes && + { test "$deplibs_check_method" = pass_all || + { test "$build_libtool_libs" = yes && + test -n "$library_names"; }; }; then + # We only need to search for static libraries + continue + fi + fi + + link_static=no # Whether the deplib will be linked statically + use_static_libs=$prefer_static_libs + if test "$use_static_libs" = built && test "$installed" = yes; then + use_static_libs=no + fi + if test -n "$library_names" && + { test "$use_static_libs" = no || test -z "$old_library"; }; then + case $host in + *cygwin* | *mingw* | *cegcc*) + # No point in relinking DLLs because paths are not encoded + func_append notinst_deplibs " $lib" + need_relink=no + ;; + *) + if test "$installed" = no; then + func_append notinst_deplibs " $lib" + need_relink=yes + fi + ;; + esac + # This is a shared library + + # Warn about portability, can't link against -module's on some + # systems (darwin). Don't bleat about dlopened modules though! + dlopenmodule="" + for dlpremoduletest in $dlprefiles; do + if test "X$dlpremoduletest" = "X$lib"; then + dlopenmodule="$dlpremoduletest" + break + fi + done + if test -z "$dlopenmodule" && test "$shouldnotlink" = yes && test "$pass" = link; then + echo + if test "$linkmode" = prog; then + $ECHO "*** Warning: Linking the executable $output against the loadable module" + else + $ECHO "*** Warning: Linking the shared library $output against the loadable module" + fi + $ECHO "*** $linklib is not portable!" + fi + if test "$linkmode" = lib && + test "$hardcode_into_libs" = yes; then + # Hardcode the library path. + # Skip directories that are in the system default run-time + # search path. + case " $sys_lib_dlsearch_path " in + *" $absdir "*) ;; + *) + case "$compile_rpath " in + *" $absdir "*) ;; + *) func_append compile_rpath " $absdir" ;; + esac + ;; + esac + case " $sys_lib_dlsearch_path " in + *" $libdir "*) ;; + *) + case "$finalize_rpath " in + *" $libdir "*) ;; + *) func_append finalize_rpath " $libdir" ;; + esac + ;; + esac + fi + + if test -n "$old_archive_from_expsyms_cmds"; then + # figure out the soname + set dummy $library_names + shift + realname="$1" + shift + libname=`eval "\\$ECHO \"$libname_spec\""` + # use dlname if we got it. it's perfectly good, no? + if test -n "$dlname"; then + soname="$dlname" + elif test -n "$soname_spec"; then + # bleh windows + case $host in + *cygwin* | mingw* | *cegcc*) + func_arith $current - $age + major=$func_arith_result + versuffix="-$major" + ;; + esac + eval soname=\"$soname_spec\" + else + soname="$realname" + fi + + # Make a new name for the extract_expsyms_cmds to use + soroot="$soname" + func_basename "$soroot" + soname="$func_basename_result" + func_stripname 'lib' '.dll' "$soname" + newlib=libimp-$func_stripname_result.a + + # If the library has no export list, then create one now + if test -f "$output_objdir/$soname-def"; then : + else + func_verbose "extracting exported symbol list from \`$soname'" + func_execute_cmds "$extract_expsyms_cmds" 'exit $?' + fi + + # Create $newlib + if test -f "$output_objdir/$newlib"; then :; else + func_verbose "generating import library for \`$soname'" + func_execute_cmds "$old_archive_from_expsyms_cmds" 'exit $?' + fi + # make sure the library variables are pointing to the new library + dir=$output_objdir + linklib=$newlib + fi # test -n "$old_archive_from_expsyms_cmds" + + if test "$linkmode" = prog || test "$opt_mode" != relink; then + add_shlibpath= + add_dir= + add= + lib_linked=yes + case $hardcode_action in + immediate | unsupported) + if test "$hardcode_direct" = no; then + add="$dir/$linklib" + case $host in + *-*-sco3.2v5.0.[024]*) add_dir="-L$dir" ;; + *-*-sysv4*uw2*) add_dir="-L$dir" ;; + *-*-sysv5OpenUNIX* | *-*-sysv5UnixWare7.[01].[10]* | \ + *-*-unixware7*) add_dir="-L$dir" ;; + *-*-darwin* ) + # if the lib is a (non-dlopened) module then we can not + # link against it, someone is ignoring the earlier warnings + if /usr/bin/file -L $add 2> /dev/null | + $GREP ": [^:]* bundle" >/dev/null ; then + if test "X$dlopenmodule" != "X$lib"; then + $ECHO "*** Warning: lib $linklib is a module, not a shared library" + if test -z "$old_library" ; then + echo + echo "*** And there doesn't seem to be a static archive available" + echo "*** The link will probably fail, sorry" + else + add="$dir/$old_library" + fi + elif test -n "$old_library"; then + add="$dir/$old_library" + fi + fi + esac + elif test "$hardcode_minus_L" = no; then + case $host in + *-*-sunos*) add_shlibpath="$dir" ;; + esac + add_dir="-L$dir" + add="-l$name" + elif test "$hardcode_shlibpath_var" = no; then + add_shlibpath="$dir" + add="-l$name" + else + lib_linked=no + fi + ;; + relink) + if test "$hardcode_direct" = yes && + test "$hardcode_direct_absolute" = no; then + add="$dir/$linklib" + elif test "$hardcode_minus_L" = yes; then + add_dir="-L$absdir" + # Try looking first in the location we're being installed to. + if test -n "$inst_prefix_dir"; then + case $libdir in + [\\/]*) + func_append add_dir " -L$inst_prefix_dir$libdir" + ;; + esac + fi + add="-l$name" + elif test "$hardcode_shlibpath_var" = yes; then + add_shlibpath="$dir" + add="-l$name" + else + lib_linked=no + fi + ;; + *) lib_linked=no ;; + esac + + if test "$lib_linked" != yes; then + func_fatal_configuration "unsupported hardcode properties" + fi + + if test -n "$add_shlibpath"; then + case :$compile_shlibpath: in + *":$add_shlibpath:"*) ;; + *) func_append compile_shlibpath "$add_shlibpath:" ;; + esac + fi + if test "$linkmode" = prog; then + test -n "$add_dir" && compile_deplibs="$add_dir $compile_deplibs" + test -n "$add" && compile_deplibs="$add $compile_deplibs" + else + test -n "$add_dir" && deplibs="$add_dir $deplibs" + test -n "$add" && deplibs="$add $deplibs" + if test "$hardcode_direct" != yes && + test "$hardcode_minus_L" != yes && + test "$hardcode_shlibpath_var" = yes; then + case :$finalize_shlibpath: in + *":$libdir:"*) ;; + *) func_append finalize_shlibpath "$libdir:" ;; + esac + fi + fi + fi + + if test "$linkmode" = prog || test "$opt_mode" = relink; then + add_shlibpath= + add_dir= + add= + # Finalize command for both is simple: just hardcode it. + if test "$hardcode_direct" = yes && + test "$hardcode_direct_absolute" = no; then + add="$libdir/$linklib" + elif test "$hardcode_minus_L" = yes; then + add_dir="-L$libdir" + add="-l$name" + elif test "$hardcode_shlibpath_var" = yes; then + case :$finalize_shlibpath: in + *":$libdir:"*) ;; + *) func_append finalize_shlibpath "$libdir:" ;; + esac + add="-l$name" + elif test "$hardcode_automatic" = yes; then + if test -n "$inst_prefix_dir" && + test -f "$inst_prefix_dir$libdir/$linklib" ; then + add="$inst_prefix_dir$libdir/$linklib" + else + add="$libdir/$linklib" + fi + else + # We cannot seem to hardcode it, guess we'll fake it. + add_dir="-L$libdir" + # Try looking first in the location we're being installed to. + if test -n "$inst_prefix_dir"; then + case $libdir in + [\\/]*) + func_append add_dir " -L$inst_prefix_dir$libdir" + ;; + esac + fi + add="-l$name" + fi + + if test "$linkmode" = prog; then + test -n "$add_dir" && finalize_deplibs="$add_dir $finalize_deplibs" + test -n "$add" && finalize_deplibs="$add $finalize_deplibs" + else + test -n "$add_dir" && deplibs="$add_dir $deplibs" + test -n "$add" && deplibs="$add $deplibs" + fi + fi + elif test "$linkmode" = prog; then + # Here we assume that one of hardcode_direct or hardcode_minus_L + # is not unsupported. This is valid on all known static and + # shared platforms. + if test "$hardcode_direct" != unsupported; then + test -n "$old_library" && linklib="$old_library" + compile_deplibs="$dir/$linklib $compile_deplibs" + finalize_deplibs="$dir/$linklib $finalize_deplibs" + else + compile_deplibs="-l$name -L$dir $compile_deplibs" + finalize_deplibs="-l$name -L$dir $finalize_deplibs" + fi + elif test "$build_libtool_libs" = yes; then + # Not a shared library + if test "$deplibs_check_method" != pass_all; then + # We're trying link a shared library against a static one + # but the system doesn't support it. + + # Just print a warning and add the library to dependency_libs so + # that the program can be linked against the static library. + echo + $ECHO "*** Warning: This system can not link to static lib archive $lib." + echo "*** I have the capability to make that library automatically link in when" + echo "*** you link to this library. But I can only do this if you have a" + echo "*** shared version of the library, which you do not appear to have." + if test "$module" = yes; then + echo "*** But as you try to build a module library, libtool will still create " + echo "*** a static module, that should work as long as the dlopening application" + echo "*** is linked with the -dlopen flag to resolve symbols at runtime." + if test -z "$global_symbol_pipe"; then + echo + echo "*** However, this would only work if libtool was able to extract symbol" + echo "*** lists from a program, using \`nm' or equivalent, but libtool could" + echo "*** not find such a program. So, this module is probably useless." + echo "*** \`nm' from GNU binutils and a full rebuild may help." + fi + if test "$build_old_libs" = no; then + build_libtool_libs=module + build_old_libs=yes + else + build_libtool_libs=no + fi + fi + else + deplibs="$dir/$old_library $deplibs" + link_static=yes + fi + fi # link shared/static library? + + if test "$linkmode" = lib; then + if test -n "$dependency_libs" && + { test "$hardcode_into_libs" != yes || + test "$build_old_libs" = yes || + test "$link_static" = yes; }; then + # Extract -R from dependency_libs + temp_deplibs= + for libdir in $dependency_libs; do + case $libdir in + -R*) func_stripname '-R' '' "$libdir" + temp_xrpath=$func_stripname_result + case " $xrpath " in + *" $temp_xrpath "*) ;; + *) func_append xrpath " $temp_xrpath";; + esac;; + *) func_append temp_deplibs " $libdir";; + esac + done + dependency_libs="$temp_deplibs" + fi + + func_append newlib_search_path " $absdir" + # Link against this library + test "$link_static" = no && newdependency_libs="$abs_ladir/$laname $newdependency_libs" + # ... and its dependency_libs + tmp_libs= + for deplib in $dependency_libs; do + newdependency_libs="$deplib $newdependency_libs" + case $deplib in + -L*) func_stripname '-L' '' "$deplib" + func_resolve_sysroot "$func_stripname_result";; + *) func_resolve_sysroot "$deplib" ;; + esac + if $opt_preserve_dup_deps ; then + case "$tmp_libs " in + *" $func_resolve_sysroot_result "*) + func_append specialdeplibs " $func_resolve_sysroot_result" ;; + esac + fi + func_append tmp_libs " $func_resolve_sysroot_result" + done + + if test "$link_all_deplibs" != no; then + # Add the search paths of all dependency libraries + for deplib in $dependency_libs; do + path= + case $deplib in + -L*) path="$deplib" ;; + *.la) + func_resolve_sysroot "$deplib" + deplib=$func_resolve_sysroot_result + func_dirname "$deplib" "" "." + dir=$func_dirname_result + # We need an absolute path. + case $dir in + [\\/]* | [A-Za-z]:[\\/]*) absdir="$dir" ;; + *) + absdir=`cd "$dir" && pwd` + if test -z "$absdir"; then + func_warning "cannot determine absolute directory name of \`$dir'" + absdir="$dir" + fi + ;; + esac + if $GREP "^installed=no" $deplib > /dev/null; then + case $host in + *-*-darwin*) + depdepl= + eval deplibrary_names=`${SED} -n -e 's/^library_names=\(.*\)$/\1/p' $deplib` + if test -n "$deplibrary_names" ; then + for tmp in $deplibrary_names ; do + depdepl=$tmp + done + if test -f "$absdir/$objdir/$depdepl" ; then + depdepl="$absdir/$objdir/$depdepl" + darwin_install_name=`${OTOOL} -L $depdepl | awk '{if (NR == 2) {print $1;exit}}'` + if test -z "$darwin_install_name"; then + darwin_install_name=`${OTOOL64} -L $depdepl | awk '{if (NR == 2) {print $1;exit}}'` + fi + func_append compiler_flags " ${wl}-dylib_file ${wl}${darwin_install_name}:${depdepl}" + func_append linker_flags " -dylib_file ${darwin_install_name}:${depdepl}" + path= + fi + fi + ;; + *) + path="-L$absdir/$objdir" + ;; + esac + else + eval libdir=`${SED} -n -e 's/^libdir=\(.*\)$/\1/p' $deplib` + test -z "$libdir" && \ + func_fatal_error "\`$deplib' is not a valid libtool archive" + test "$absdir" != "$libdir" && \ + func_warning "\`$deplib' seems to be moved" + + path="-L$absdir" + fi + ;; + esac + case " $deplibs " in + *" $path "*) ;; + *) deplibs="$path $deplibs" ;; + esac + done + fi # link_all_deplibs != no + fi # linkmode = lib + done # for deplib in $libs + if test "$pass" = link; then + if test "$linkmode" = "prog"; then + compile_deplibs="$new_inherited_linker_flags $compile_deplibs" + finalize_deplibs="$new_inherited_linker_flags $finalize_deplibs" + else + compiler_flags="$compiler_flags "`$ECHO " $new_inherited_linker_flags" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'` + fi + fi + dependency_libs="$newdependency_libs" + if test "$pass" = dlpreopen; then + # Link the dlpreopened libraries before other libraries + for deplib in $save_deplibs; do + deplibs="$deplib $deplibs" + done + fi + if test "$pass" != dlopen; then + if test "$pass" != conv; then + # Make sure lib_search_path contains only unique directories. + lib_search_path= + for dir in $newlib_search_path; do + case "$lib_search_path " in + *" $dir "*) ;; + *) func_append lib_search_path " $dir" ;; + esac + done + newlib_search_path= + fi + + if test "$linkmode,$pass" != "prog,link"; then + vars="deplibs" + else + vars="compile_deplibs finalize_deplibs" + fi + for var in $vars dependency_libs; do + # Add libraries to $var in reverse order + eval tmp_libs=\"\$$var\" + new_libs= + for deplib in $tmp_libs; do + # FIXME: Pedantically, this is the right thing to do, so + # that some nasty dependency loop isn't accidentally + # broken: + #new_libs="$deplib $new_libs" + # Pragmatically, this seems to cause very few problems in + # practice: + case $deplib in + -L*) new_libs="$deplib $new_libs" ;; + -R*) ;; + *) + # And here is the reason: when a library appears more + # than once as an explicit dependence of a library, or + # is implicitly linked in more than once by the + # compiler, it is considered special, and multiple + # occurrences thereof are not removed. Compare this + # with having the same library being listed as a + # dependency of multiple other libraries: in this case, + # we know (pedantically, we assume) the library does not + # need to be listed more than once, so we keep only the + # last copy. This is not always right, but it is rare + # enough that we require users that really mean to play + # such unportable linking tricks to link the library + # using -Wl,-lname, so that libtool does not consider it + # for duplicate removal. + case " $specialdeplibs " in + *" $deplib "*) new_libs="$deplib $new_libs" ;; + *) + case " $new_libs " in + *" $deplib "*) ;; + *) new_libs="$deplib $new_libs" ;; + esac + ;; + esac + ;; + esac + done + tmp_libs= + for deplib in $new_libs; do + case $deplib in + -L*) + case " $tmp_libs " in + *" $deplib "*) ;; + *) func_append tmp_libs " $deplib" ;; + esac + ;; + *) func_append tmp_libs " $deplib" ;; + esac + done + eval $var=\"$tmp_libs\" + done # for var + fi + # Last step: remove runtime libs from dependency_libs + # (they stay in deplibs) + tmp_libs= + for i in $dependency_libs ; do + case " $predeps $postdeps $compiler_lib_search_path " in + *" $i "*) + i="" + ;; + esac + if test -n "$i" ; then + func_append tmp_libs " $i" + fi + done + dependency_libs=$tmp_libs + done # for pass + if test "$linkmode" = prog; then + dlfiles="$newdlfiles" + fi + if test "$linkmode" = prog || test "$linkmode" = lib; then + dlprefiles="$newdlprefiles" + fi + + case $linkmode in + oldlib) + if test -n "$dlfiles$dlprefiles" || test "$dlself" != no; then + func_warning "\`-dlopen' is ignored for archives" + fi + + case " $deplibs" in + *\ -l* | *\ -L*) + func_warning "\`-l' and \`-L' are ignored for archives" ;; + esac + + test -n "$rpath" && \ + func_warning "\`-rpath' is ignored for archives" + + test -n "$xrpath" && \ + func_warning "\`-R' is ignored for archives" + + test -n "$vinfo" && \ + func_warning "\`-version-info/-version-number' is ignored for archives" + + test -n "$release" && \ + func_warning "\`-release' is ignored for archives" + + test -n "$export_symbols$export_symbols_regex" && \ + func_warning "\`-export-symbols' is ignored for archives" + + # Now set the variables for building old libraries. + build_libtool_libs=no + oldlibs="$output" + func_append objs "$old_deplibs" + ;; + + lib) + # Make sure we only generate libraries of the form `libNAME.la'. + case $outputname in + lib*) + func_stripname 'lib' '.la' "$outputname" + name=$func_stripname_result + eval shared_ext=\"$shrext_cmds\" + eval libname=\"$libname_spec\" + ;; + *) + test "$module" = no && \ + func_fatal_help "libtool library \`$output' must begin with \`lib'" + + if test "$need_lib_prefix" != no; then + # Add the "lib" prefix for modules if required + func_stripname '' '.la' "$outputname" + name=$func_stripname_result + eval shared_ext=\"$shrext_cmds\" + eval libname=\"$libname_spec\" + else + func_stripname '' '.la' "$outputname" + libname=$func_stripname_result + fi + ;; + esac + + if test -n "$objs"; then + if test "$deplibs_check_method" != pass_all; then + func_fatal_error "cannot build libtool library \`$output' from non-libtool objects on this host:$objs" + else + echo + $ECHO "*** Warning: Linking the shared library $output against the non-libtool" + $ECHO "*** objects $objs is not portable!" + func_append libobjs " $objs" + fi + fi + + test "$dlself" != no && \ + func_warning "\`-dlopen self' is ignored for libtool libraries" + + set dummy $rpath + shift + test "$#" -gt 1 && \ + func_warning "ignoring multiple \`-rpath's for a libtool library" + + install_libdir="$1" + + oldlibs= + if test -z "$rpath"; then + if test "$build_libtool_libs" = yes; then + # Building a libtool convenience library. + # Some compilers have problems with a `.al' extension so + # convenience libraries should have the same extension an + # archive normally would. + oldlibs="$output_objdir/$libname.$libext $oldlibs" + build_libtool_libs=convenience + build_old_libs=yes + fi + + test -n "$vinfo" && \ + func_warning "\`-version-info/-version-number' is ignored for convenience libraries" + + test -n "$release" && \ + func_warning "\`-release' is ignored for convenience libraries" + else + + # Parse the version information argument. + save_ifs="$IFS"; IFS=':' + set dummy $vinfo 0 0 0 + shift + IFS="$save_ifs" + + test -n "$7" && \ + func_fatal_help "too many parameters to \`-version-info'" + + # convert absolute version numbers to libtool ages + # this retains compatibility with .la files and attempts + # to make the code below a bit more comprehensible + + case $vinfo_number in + yes) + number_major="$1" + number_minor="$2" + number_revision="$3" + # + # There are really only two kinds -- those that + # use the current revision as the major version + # and those that subtract age and use age as + # a minor version. But, then there is irix + # which has an extra 1 added just for fun + # + case $version_type in + # correct linux to gnu/linux during the next big refactor + darwin|linux|osf|windows|none) + func_arith $number_major + $number_minor + current=$func_arith_result + age="$number_minor" + revision="$number_revision" + ;; + freebsd-aout|freebsd-elf|qnx|sunos) + current="$number_major" + revision="$number_minor" + age="0" + ;; + irix|nonstopux) + func_arith $number_major + $number_minor + current=$func_arith_result + age="$number_minor" + revision="$number_minor" + lt_irix_increment=no + ;; + *) + func_fatal_configuration "$modename: unknown library version type \`$version_type'" + ;; + esac + ;; + no) + current="$1" + revision="$2" + age="$3" + ;; + esac + + # Check that each of the things are valid numbers. + case $current in + 0|[1-9]|[1-9][0-9]|[1-9][0-9][0-9]|[1-9][0-9][0-9][0-9]|[1-9][0-9][0-9][0-9][0-9]) ;; + *) + func_error "CURRENT \`$current' must be a nonnegative integer" + func_fatal_error "\`$vinfo' is not valid version information" + ;; + esac + + case $revision in + 0|[1-9]|[1-9][0-9]|[1-9][0-9][0-9]|[1-9][0-9][0-9][0-9]|[1-9][0-9][0-9][0-9][0-9]) ;; + *) + func_error "REVISION \`$revision' must be a nonnegative integer" + func_fatal_error "\`$vinfo' is not valid version information" + ;; + esac + + case $age in + 0|[1-9]|[1-9][0-9]|[1-9][0-9][0-9]|[1-9][0-9][0-9][0-9]|[1-9][0-9][0-9][0-9][0-9]) ;; + *) + func_error "AGE \`$age' must be a nonnegative integer" + func_fatal_error "\`$vinfo' is not valid version information" + ;; + esac + + if test "$age" -gt "$current"; then + func_error "AGE \`$age' is greater than the current interface number \`$current'" + func_fatal_error "\`$vinfo' is not valid version information" + fi + + # Calculate the version variables. + major= + versuffix= + verstring= + case $version_type in + none) ;; + + darwin) + # Like Linux, but with the current version available in + # verstring for coding it into the library header + func_arith $current - $age + major=.$func_arith_result + versuffix="$major.$age.$revision" + # Darwin ld doesn't like 0 for these options... + func_arith $current + 1 + minor_current=$func_arith_result + xlcverstring="${wl}-compatibility_version ${wl}$minor_current ${wl}-current_version ${wl}$minor_current.$revision" + verstring="-compatibility_version $minor_current -current_version $minor_current.$revision" + ;; + + freebsd-aout) + major=".$current" + versuffix=".$current.$revision"; + ;; + + freebsd-elf) + major=".$current" + versuffix=".$current" + ;; + + irix | nonstopux) + if test "X$lt_irix_increment" = "Xno"; then + func_arith $current - $age + else + func_arith $current - $age + 1 + fi + major=$func_arith_result + + case $version_type in + nonstopux) verstring_prefix=nonstopux ;; + *) verstring_prefix=sgi ;; + esac + verstring="$verstring_prefix$major.$revision" + + # Add in all the interfaces that we are compatible with. + loop=$revision + while test "$loop" -ne 0; do + func_arith $revision - $loop + iface=$func_arith_result + func_arith $loop - 1 + loop=$func_arith_result + verstring="$verstring_prefix$major.$iface:$verstring" + done + + # Before this point, $major must not contain `.'. + major=.$major + versuffix="$major.$revision" + ;; + + linux) # correct to gnu/linux during the next big refactor + func_arith $current - $age + major=.$func_arith_result + versuffix="$major.$age.$revision" + ;; + + osf) + func_arith $current - $age + major=.$func_arith_result + versuffix=".$current.$age.$revision" + verstring="$current.$age.$revision" + + # Add in all the interfaces that we are compatible with. + loop=$age + while test "$loop" -ne 0; do + func_arith $current - $loop + iface=$func_arith_result + func_arith $loop - 1 + loop=$func_arith_result + verstring="$verstring:${iface}.0" + done + + # Make executables depend on our current version. + func_append verstring ":${current}.0" + ;; + + qnx) + major=".$current" + versuffix=".$current" + ;; + + sunos) + major=".$current" + versuffix=".$current.$revision" + ;; + + windows) + # Use '-' rather than '.', since we only want one + # extension on DOS 8.3 filesystems. + func_arith $current - $age + major=$func_arith_result + versuffix="-$major" + ;; + + *) + func_fatal_configuration "unknown library version type \`$version_type'" + ;; + esac + + # Clear the version info if we defaulted, and they specified a release. + if test -z "$vinfo" && test -n "$release"; then + major= + case $version_type in + darwin) + # we can't check for "0.0" in archive_cmds due to quoting + # problems, so we reset it completely + verstring= + ;; + *) + verstring="0.0" + ;; + esac + if test "$need_version" = no; then + versuffix= + else + versuffix=".0.0" + fi + fi + + # Remove version info from name if versioning should be avoided + if test "$avoid_version" = yes && test "$need_version" = no; then + major= + versuffix= + verstring="" + fi + + # Check to see if the archive will have undefined symbols. + if test "$allow_undefined" = yes; then + if test "$allow_undefined_flag" = unsupported; then + func_warning "undefined symbols not allowed in $host shared libraries" + build_libtool_libs=no + build_old_libs=yes + fi + else + # Don't allow undefined symbols. + allow_undefined_flag="$no_undefined_flag" + fi + + fi + + func_generate_dlsyms "$libname" "$libname" "yes" + func_append libobjs " $symfileobj" + test "X$libobjs" = "X " && libobjs= + + if test "$opt_mode" != relink; then + # Remove our outputs, but don't remove object files since they + # may have been created when compiling PIC objects. + removelist= + tempremovelist=`$ECHO "$output_objdir/*"` + for p in $tempremovelist; do + case $p in + *.$objext | *.gcno) + ;; + $output_objdir/$outputname | $output_objdir/$libname.* | $output_objdir/${libname}${release}.*) + if test "X$precious_files_regex" != "X"; then + if $ECHO "$p" | $EGREP -e "$precious_files_regex" >/dev/null 2>&1 + then + continue + fi + fi + func_append removelist " $p" + ;; + *) ;; + esac + done + test -n "$removelist" && \ + func_show_eval "${RM}r \$removelist" + fi + + # Now set the variables for building old libraries. + if test "$build_old_libs" = yes && test "$build_libtool_libs" != convenience ; then + func_append oldlibs " $output_objdir/$libname.$libext" + + # Transform .lo files to .o files. + oldobjs="$objs "`$ECHO "$libobjs" | $SP2NL | $SED "/\.${libext}$/d; $lo2o" | $NL2SP` + fi + + # Eliminate all temporary directories. + #for path in $notinst_path; do + # lib_search_path=`$ECHO "$lib_search_path " | $SED "s% $path % %g"` + # deplibs=`$ECHO "$deplibs " | $SED "s% -L$path % %g"` + # dependency_libs=`$ECHO "$dependency_libs " | $SED "s% -L$path % %g"` + #done + + if test -n "$xrpath"; then + # If the user specified any rpath flags, then add them. + temp_xrpath= + for libdir in $xrpath; do + func_replace_sysroot "$libdir" + func_append temp_xrpath " -R$func_replace_sysroot_result" + case "$finalize_rpath " in + *" $libdir "*) ;; + *) func_append finalize_rpath " $libdir" ;; + esac + done + if test "$hardcode_into_libs" != yes || test "$build_old_libs" = yes; then + dependency_libs="$temp_xrpath $dependency_libs" + fi + fi + + # Make sure dlfiles contains only unique files that won't be dlpreopened + old_dlfiles="$dlfiles" + dlfiles= + for lib in $old_dlfiles; do + case " $dlprefiles $dlfiles " in + *" $lib "*) ;; + *) func_append dlfiles " $lib" ;; + esac + done + + # Make sure dlprefiles contains only unique files + old_dlprefiles="$dlprefiles" + dlprefiles= + for lib in $old_dlprefiles; do + case "$dlprefiles " in + *" $lib "*) ;; + *) func_append dlprefiles " $lib" ;; + esac + done + + if test "$build_libtool_libs" = yes; then + if test -n "$rpath"; then + case $host in + *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-*-beos* | *-cegcc* | *-*-haiku*) + # these systems don't actually have a c library (as such)! + ;; + *-*-rhapsody* | *-*-darwin1.[012]) + # Rhapsody C library is in the System framework + func_append deplibs " System.ltframework" + ;; + *-*-netbsd*) + # Don't link with libc until the a.out ld.so is fixed. + ;; + *-*-openbsd* | *-*-freebsd* | *-*-dragonfly*) + # Do not include libc due to us having libc/libc_r. + ;; + *-*-sco3.2v5* | *-*-sco5v6*) + # Causes problems with __ctype + ;; + *-*-sysv4.2uw2* | *-*-sysv5* | *-*-unixware* | *-*-OpenUNIX*) + # Compiler inserts libc in the correct place for threads to work + ;; + *) + # Add libc to deplibs on all other systems if necessary. + if test "$build_libtool_need_lc" = "yes"; then + func_append deplibs " -lc" + fi + ;; + esac + fi + + # Transform deplibs into only deplibs that can be linked in shared. + name_save=$name + libname_save=$libname + release_save=$release + versuffix_save=$versuffix + major_save=$major + # I'm not sure if I'm treating the release correctly. I think + # release should show up in the -l (ie -lgmp5) so we don't want to + # add it in twice. Is that correct? + release="" + versuffix="" + major="" + newdeplibs= + droppeddeps=no + case $deplibs_check_method in + pass_all) + # Don't check for shared/static. Everything works. + # This might be a little naive. We might want to check + # whether the library exists or not. But this is on + # osf3 & osf4 and I'm not really sure... Just + # implementing what was already the behavior. + newdeplibs=$deplibs + ;; + test_compile) + # This code stresses the "libraries are programs" paradigm to its + # limits. Maybe even breaks it. We compile a program, linking it + # against the deplibs as a proxy for the library. Then we can check + # whether they linked in statically or dynamically with ldd. + $opt_dry_run || $RM conftest.c + cat > conftest.c </dev/null` + $nocaseglob + else + potential_libs=`ls $i/$libnameglob[.-]* 2>/dev/null` + fi + for potent_lib in $potential_libs; do + # Follow soft links. + if ls -lLd "$potent_lib" 2>/dev/null | + $GREP " -> " >/dev/null; then + continue + fi + # The statement above tries to avoid entering an + # endless loop below, in case of cyclic links. + # We might still enter an endless loop, since a link + # loop can be closed while we follow links, + # but so what? + potlib="$potent_lib" + while test -h "$potlib" 2>/dev/null; do + potliblink=`ls -ld $potlib | ${SED} 's/.* -> //'` + case $potliblink in + [\\/]* | [A-Za-z]:[\\/]*) potlib="$potliblink";; + *) potlib=`$ECHO "$potlib" | $SED 's,[^/]*$,,'`"$potliblink";; + esac + done + if eval $file_magic_cmd \"\$potlib\" 2>/dev/null | + $SED -e 10q | + $EGREP "$file_magic_regex" > /dev/null; then + func_append newdeplibs " $a_deplib" + a_deplib="" + break 2 + fi + done + done + fi + if test -n "$a_deplib" ; then + droppeddeps=yes + echo + $ECHO "*** Warning: linker path does not have real file for library $a_deplib." + echo "*** I have the capability to make that library automatically link in when" + echo "*** you link to this library. But I can only do this if you have a" + echo "*** shared version of the library, which you do not appear to have" + echo "*** because I did check the linker path looking for a file starting" + if test -z "$potlib" ; then + $ECHO "*** with $libname but no candidates were found. (...for file magic test)" + else + $ECHO "*** with $libname and none of the candidates passed a file format test" + $ECHO "*** using a file magic. Last file checked: $potlib" + fi + fi + ;; + *) + # Add a -L argument. + func_append newdeplibs " $a_deplib" + ;; + esac + done # Gone through all deplibs. + ;; + match_pattern*) + set dummy $deplibs_check_method; shift + match_pattern_regex=`expr "$deplibs_check_method" : "$1 \(.*\)"` + for a_deplib in $deplibs; do + case $a_deplib in + -l*) + func_stripname -l '' "$a_deplib" + name=$func_stripname_result + if test "X$allow_libtool_libs_with_static_runtimes" = "Xyes" ; then + case " $predeps $postdeps " in + *" $a_deplib "*) + func_append newdeplibs " $a_deplib" + a_deplib="" + ;; + esac + fi + if test -n "$a_deplib" ; then + libname=`eval "\\$ECHO \"$libname_spec\""` + for i in $lib_search_path $sys_lib_search_path $shlib_search_path; do + potential_libs=`ls $i/$libname[.-]* 2>/dev/null` + for potent_lib in $potential_libs; do + potlib="$potent_lib" # see symlink-check above in file_magic test + if eval "\$ECHO \"$potent_lib\"" 2>/dev/null | $SED 10q | \ + $EGREP "$match_pattern_regex" > /dev/null; then + func_append newdeplibs " $a_deplib" + a_deplib="" + break 2 + fi + done + done + fi + if test -n "$a_deplib" ; then + droppeddeps=yes + echo + $ECHO "*** Warning: linker path does not have real file for library $a_deplib." + echo "*** I have the capability to make that library automatically link in when" + echo "*** you link to this library. But I can only do this if you have a" + echo "*** shared version of the library, which you do not appear to have" + echo "*** because I did check the linker path looking for a file starting" + if test -z "$potlib" ; then + $ECHO "*** with $libname but no candidates were found. (...for regex pattern test)" + else + $ECHO "*** with $libname and none of the candidates passed a file format test" + $ECHO "*** using a regex pattern. Last file checked: $potlib" + fi + fi + ;; + *) + # Add a -L argument. + func_append newdeplibs " $a_deplib" + ;; + esac + done # Gone through all deplibs. + ;; + none | unknown | *) + newdeplibs="" + tmp_deplibs=`$ECHO " $deplibs" | $SED 's/ -lc$//; s/ -[LR][^ ]*//g'` + if test "X$allow_libtool_libs_with_static_runtimes" = "Xyes" ; then + for i in $predeps $postdeps ; do + # can't use Xsed below, because $i might contain '/' + tmp_deplibs=`$ECHO " $tmp_deplibs" | $SED "s,$i,,"` + done + fi + case $tmp_deplibs in + *[!\ \ ]*) + echo + if test "X$deplibs_check_method" = "Xnone"; then + echo "*** Warning: inter-library dependencies are not supported in this platform." + else + echo "*** Warning: inter-library dependencies are not known to be supported." + fi + echo "*** All declared inter-library dependencies are being dropped." + droppeddeps=yes + ;; + esac + ;; + esac + versuffix=$versuffix_save + major=$major_save + release=$release_save + libname=$libname_save + name=$name_save + + case $host in + *-*-rhapsody* | *-*-darwin1.[012]) + # On Rhapsody replace the C library with the System framework + newdeplibs=`$ECHO " $newdeplibs" | $SED 's/ -lc / System.ltframework /'` + ;; + esac + + if test "$droppeddeps" = yes; then + if test "$module" = yes; then + echo + echo "*** Warning: libtool could not satisfy all declared inter-library" + $ECHO "*** dependencies of module $libname. Therefore, libtool will create" + echo "*** a static module, that should work as long as the dlopening" + echo "*** application is linked with the -dlopen flag." + if test -z "$global_symbol_pipe"; then + echo + echo "*** However, this would only work if libtool was able to extract symbol" + echo "*** lists from a program, using \`nm' or equivalent, but libtool could" + echo "*** not find such a program. So, this module is probably useless." + echo "*** \`nm' from GNU binutils and a full rebuild may help." + fi + if test "$build_old_libs" = no; then + oldlibs="$output_objdir/$libname.$libext" + build_libtool_libs=module + build_old_libs=yes + else + build_libtool_libs=no + fi + else + echo "*** The inter-library dependencies that have been dropped here will be" + echo "*** automatically added whenever a program is linked with this library" + echo "*** or is declared to -dlopen it." + + if test "$allow_undefined" = no; then + echo + echo "*** Since this library must not contain undefined symbols," + echo "*** because either the platform does not support them or" + echo "*** it was explicitly requested with -no-undefined," + echo "*** libtool will only create a static version of it." + if test "$build_old_libs" = no; then + oldlibs="$output_objdir/$libname.$libext" + build_libtool_libs=module + build_old_libs=yes + else + build_libtool_libs=no + fi + fi + fi + fi + # Done checking deplibs! + deplibs=$newdeplibs + fi + # Time to change all our "foo.ltframework" stuff back to "-framework foo" + case $host in + *-*-darwin*) + newdeplibs=`$ECHO " $newdeplibs" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'` + new_inherited_linker_flags=`$ECHO " $new_inherited_linker_flags" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'` + deplibs=`$ECHO " $deplibs" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'` + ;; + esac + + # move library search paths that coincide with paths to not yet + # installed libraries to the beginning of the library search list + new_libs= + for path in $notinst_path; do + case " $new_libs " in + *" -L$path/$objdir "*) ;; + *) + case " $deplibs " in + *" -L$path/$objdir "*) + func_append new_libs " -L$path/$objdir" ;; + esac + ;; + esac + done + for deplib in $deplibs; do + case $deplib in + -L*) + case " $new_libs " in + *" $deplib "*) ;; + *) func_append new_libs " $deplib" ;; + esac + ;; + *) func_append new_libs " $deplib" ;; + esac + done + deplibs="$new_libs" + + # All the library-specific variables (install_libdir is set above). + library_names= + old_library= + dlname= + + # Test again, we may have decided not to build it any more + if test "$build_libtool_libs" = yes; then + # Remove ${wl} instances when linking with ld. + # FIXME: should test the right _cmds variable. + case $archive_cmds in + *\$LD\ *) wl= ;; + esac + if test "$hardcode_into_libs" = yes; then + # Hardcode the library paths + hardcode_libdirs= + dep_rpath= + rpath="$finalize_rpath" + test "$opt_mode" != relink && rpath="$compile_rpath$rpath" + for libdir in $rpath; do + if test -n "$hardcode_libdir_flag_spec"; then + if test -n "$hardcode_libdir_separator"; then + func_replace_sysroot "$libdir" + libdir=$func_replace_sysroot_result + if test -z "$hardcode_libdirs"; then + hardcode_libdirs="$libdir" + else + # Just accumulate the unique libdirs. + case $hardcode_libdir_separator$hardcode_libdirs$hardcode_libdir_separator in + *"$hardcode_libdir_separator$libdir$hardcode_libdir_separator"*) + ;; + *) + func_append hardcode_libdirs "$hardcode_libdir_separator$libdir" + ;; + esac + fi + else + eval flag=\"$hardcode_libdir_flag_spec\" + func_append dep_rpath " $flag" + fi + elif test -n "$runpath_var"; then + case "$perm_rpath " in + *" $libdir "*) ;; + *) func_append perm_rpath " $libdir" ;; + esac + fi + done + # Substitute the hardcoded libdirs into the rpath. + if test -n "$hardcode_libdir_separator" && + test -n "$hardcode_libdirs"; then + libdir="$hardcode_libdirs" + eval "dep_rpath=\"$hardcode_libdir_flag_spec\"" + fi + if test -n "$runpath_var" && test -n "$perm_rpath"; then + # We should set the runpath_var. + rpath= + for dir in $perm_rpath; do + func_append rpath "$dir:" + done + eval "$runpath_var='$rpath\$$runpath_var'; export $runpath_var" + fi + test -n "$dep_rpath" && deplibs="$dep_rpath $deplibs" + fi + + shlibpath="$finalize_shlibpath" + test "$opt_mode" != relink && shlibpath="$compile_shlibpath$shlibpath" + if test -n "$shlibpath"; then + eval "$shlibpath_var='$shlibpath\$$shlibpath_var'; export $shlibpath_var" + fi + + # Get the real and link names of the library. + eval shared_ext=\"$shrext_cmds\" + eval library_names=\"$library_names_spec\" + set dummy $library_names + shift + realname="$1" + shift + + if test -n "$soname_spec"; then + eval soname=\"$soname_spec\" + else + soname="$realname" + fi + if test -z "$dlname"; then + dlname=$soname + fi + + lib="$output_objdir/$realname" + linknames= + for link + do + func_append linknames " $link" + done + + # Use standard objects if they are pic + test -z "$pic_flag" && libobjs=`$ECHO "$libobjs" | $SP2NL | $SED "$lo2o" | $NL2SP` + test "X$libobjs" = "X " && libobjs= + + delfiles= + if test -n "$export_symbols" && test -n "$include_expsyms"; then + $opt_dry_run || cp "$export_symbols" "$output_objdir/$libname.uexp" + export_symbols="$output_objdir/$libname.uexp" + func_append delfiles " $export_symbols" + fi + + orig_export_symbols= + case $host_os in + cygwin* | mingw* | cegcc*) + if test -n "$export_symbols" && test -z "$export_symbols_regex"; then + # exporting using user supplied symfile + if test "x`$SED 1q $export_symbols`" != xEXPORTS; then + # and it's NOT already a .def file. Must figure out + # which of the given symbols are data symbols and tag + # them as such. So, trigger use of export_symbols_cmds. + # export_symbols gets reassigned inside the "prepare + # the list of exported symbols" if statement, so the + # include_expsyms logic still works. + orig_export_symbols="$export_symbols" + export_symbols= + always_export_symbols=yes + fi + fi + ;; + esac + + # Prepare the list of exported symbols + if test -z "$export_symbols"; then + if test "$always_export_symbols" = yes || test -n "$export_symbols_regex"; then + func_verbose "generating symbol list for \`$libname.la'" + export_symbols="$output_objdir/$libname.exp" + $opt_dry_run || $RM $export_symbols + cmds=$export_symbols_cmds + save_ifs="$IFS"; IFS='~' + for cmd1 in $cmds; do + IFS="$save_ifs" + # Take the normal branch if the nm_file_list_spec branch + # doesn't work or if tool conversion is not needed. + case $nm_file_list_spec~$to_tool_file_cmd in + *~func_convert_file_noop | *~func_convert_file_msys_to_w32 | ~*) + try_normal_branch=yes + eval cmd=\"$cmd1\" + func_len " $cmd" + len=$func_len_result + ;; + *) + try_normal_branch=no + ;; + esac + if test "$try_normal_branch" = yes \ + && { test "$len" -lt "$max_cmd_len" \ + || test "$max_cmd_len" -le -1; } + then + func_show_eval "$cmd" 'exit $?' + skipped_export=false + elif test -n "$nm_file_list_spec"; then + func_basename "$output" + output_la=$func_basename_result + save_libobjs=$libobjs + save_output=$output + output=${output_objdir}/${output_la}.nm + func_to_tool_file "$output" + libobjs=$nm_file_list_spec$func_to_tool_file_result + func_append delfiles " $output" + func_verbose "creating $NM input file list: $output" + for obj in $save_libobjs; do + func_to_tool_file "$obj" + $ECHO "$func_to_tool_file_result" + done > "$output" + eval cmd=\"$cmd1\" + func_show_eval "$cmd" 'exit $?' + output=$save_output + libobjs=$save_libobjs + skipped_export=false + else + # The command line is too long to execute in one step. + func_verbose "using reloadable object file for export list..." + skipped_export=: + # Break out early, otherwise skipped_export may be + # set to false by a later but shorter cmd. + break + fi + done + IFS="$save_ifs" + if test -n "$export_symbols_regex" && test "X$skipped_export" != "X:"; then + func_show_eval '$EGREP -e "$export_symbols_regex" "$export_symbols" > "${export_symbols}T"' + func_show_eval '$MV "${export_symbols}T" "$export_symbols"' + fi + fi + fi + + if test -n "$export_symbols" && test -n "$include_expsyms"; then + tmp_export_symbols="$export_symbols" + test -n "$orig_export_symbols" && tmp_export_symbols="$orig_export_symbols" + $opt_dry_run || eval '$ECHO "$include_expsyms" | $SP2NL >> "$tmp_export_symbols"' + fi + + if test "X$skipped_export" != "X:" && test -n "$orig_export_symbols"; then + # The given exports_symbols file has to be filtered, so filter it. + func_verbose "filter symbol list for \`$libname.la' to tag DATA exports" + # FIXME: $output_objdir/$libname.filter potentially contains lots of + # 's' commands which not all seds can handle. GNU sed should be fine + # though. Also, the filter scales superlinearly with the number of + # global variables. join(1) would be nice here, but unfortunately + # isn't a blessed tool. + $opt_dry_run || $SED -e '/[ ,]DATA/!d;s,\(.*\)\([ \,].*\),s|^\1$|\1\2|,' < $export_symbols > $output_objdir/$libname.filter + func_append delfiles " $export_symbols $output_objdir/$libname.filter" + export_symbols=$output_objdir/$libname.def + $opt_dry_run || $SED -f $output_objdir/$libname.filter < $orig_export_symbols > $export_symbols + fi + + tmp_deplibs= + for test_deplib in $deplibs; do + case " $convenience " in + *" $test_deplib "*) ;; + *) + func_append tmp_deplibs " $test_deplib" + ;; + esac + done + deplibs="$tmp_deplibs" + + if test -n "$convenience"; then + if test -n "$whole_archive_flag_spec" && + test "$compiler_needs_object" = yes && + test -z "$libobjs"; then + # extract the archives, so we have objects to list. + # TODO: could optimize this to just extract one archive. + whole_archive_flag_spec= + fi + if test -n "$whole_archive_flag_spec"; then + save_libobjs=$libobjs + eval libobjs=\"\$libobjs $whole_archive_flag_spec\" + test "X$libobjs" = "X " && libobjs= + else + gentop="$output_objdir/${outputname}x" + func_append generated " $gentop" + + func_extract_archives $gentop $convenience + func_append libobjs " $func_extract_archives_result" + test "X$libobjs" = "X " && libobjs= + fi + fi + + if test "$thread_safe" = yes && test -n "$thread_safe_flag_spec"; then + eval flag=\"$thread_safe_flag_spec\" + func_append linker_flags " $flag" + fi + + # Make a backup of the uninstalled library when relinking + if test "$opt_mode" = relink; then + $opt_dry_run || eval '(cd $output_objdir && $RM ${realname}U && $MV $realname ${realname}U)' || exit $? + fi + + # Do each of the archive commands. + if test "$module" = yes && test -n "$module_cmds" ; then + if test -n "$export_symbols" && test -n "$module_expsym_cmds"; then + eval test_cmds=\"$module_expsym_cmds\" + cmds=$module_expsym_cmds + else + eval test_cmds=\"$module_cmds\" + cmds=$module_cmds + fi + else + if test -n "$export_symbols" && test -n "$archive_expsym_cmds"; then + eval test_cmds=\"$archive_expsym_cmds\" + cmds=$archive_expsym_cmds + else + eval test_cmds=\"$archive_cmds\" + cmds=$archive_cmds + fi + fi + + if test "X$skipped_export" != "X:" && + func_len " $test_cmds" && + len=$func_len_result && + test "$len" -lt "$max_cmd_len" || test "$max_cmd_len" -le -1; then + : + else + # The command line is too long to link in one step, link piecewise + # or, if using GNU ld and skipped_export is not :, use a linker + # script. + + # Save the value of $output and $libobjs because we want to + # use them later. If we have whole_archive_flag_spec, we + # want to use save_libobjs as it was before + # whole_archive_flag_spec was expanded, because we can't + # assume the linker understands whole_archive_flag_spec. + # This may have to be revisited, in case too many + # convenience libraries get linked in and end up exceeding + # the spec. + if test -z "$convenience" || test -z "$whole_archive_flag_spec"; then + save_libobjs=$libobjs + fi + save_output=$output + func_basename "$output" + output_la=$func_basename_result + + # Clear the reloadable object creation command queue and + # initialize k to one. + test_cmds= + concat_cmds= + objlist= + last_robj= + k=1 + + if test -n "$save_libobjs" && test "X$skipped_export" != "X:" && test "$with_gnu_ld" = yes; then + output=${output_objdir}/${output_la}.lnkscript + func_verbose "creating GNU ld script: $output" + echo 'INPUT (' > $output + for obj in $save_libobjs + do + func_to_tool_file "$obj" + $ECHO "$func_to_tool_file_result" >> $output + done + echo ')' >> $output + func_append delfiles " $output" + func_to_tool_file "$output" + output=$func_to_tool_file_result + elif test -n "$save_libobjs" && test "X$skipped_export" != "X:" && test "X$file_list_spec" != X; then + output=${output_objdir}/${output_la}.lnk + func_verbose "creating linker input file list: $output" + : > $output + set x $save_libobjs + shift + firstobj= + if test "$compiler_needs_object" = yes; then + firstobj="$1 " + shift + fi + for obj + do + func_to_tool_file "$obj" + $ECHO "$func_to_tool_file_result" >> $output + done + func_append delfiles " $output" + func_to_tool_file "$output" + output=$firstobj\"$file_list_spec$func_to_tool_file_result\" + else + if test -n "$save_libobjs"; then + func_verbose "creating reloadable object files..." + output=$output_objdir/$output_la-${k}.$objext + eval test_cmds=\"$reload_cmds\" + func_len " $test_cmds" + len0=$func_len_result + len=$len0 + + # Loop over the list of objects to be linked. + for obj in $save_libobjs + do + func_len " $obj" + func_arith $len + $func_len_result + len=$func_arith_result + if test "X$objlist" = X || + test "$len" -lt "$max_cmd_len"; then + func_append objlist " $obj" + else + # The command $test_cmds is almost too long, add a + # command to the queue. + if test "$k" -eq 1 ; then + # The first file doesn't have a previous command to add. + reload_objs=$objlist + eval concat_cmds=\"$reload_cmds\" + else + # All subsequent reloadable object files will link in + # the last one created. + reload_objs="$objlist $last_robj" + eval concat_cmds=\"\$concat_cmds~$reload_cmds~\$RM $last_robj\" + fi + last_robj=$output_objdir/$output_la-${k}.$objext + func_arith $k + 1 + k=$func_arith_result + output=$output_objdir/$output_la-${k}.$objext + objlist=" $obj" + func_len " $last_robj" + func_arith $len0 + $func_len_result + len=$func_arith_result + fi + done + # Handle the remaining objects by creating one last + # reloadable object file. All subsequent reloadable object + # files will link in the last one created. + test -z "$concat_cmds" || concat_cmds=$concat_cmds~ + reload_objs="$objlist $last_robj" + eval concat_cmds=\"\${concat_cmds}$reload_cmds\" + if test -n "$last_robj"; then + eval concat_cmds=\"\${concat_cmds}~\$RM $last_robj\" + fi + func_append delfiles " $output" + + else + output= + fi + + if ${skipped_export-false}; then + func_verbose "generating symbol list for \`$libname.la'" + export_symbols="$output_objdir/$libname.exp" + $opt_dry_run || $RM $export_symbols + libobjs=$output + # Append the command to create the export file. + test -z "$concat_cmds" || concat_cmds=$concat_cmds~ + eval concat_cmds=\"\$concat_cmds$export_symbols_cmds\" + if test -n "$last_robj"; then + eval concat_cmds=\"\$concat_cmds~\$RM $last_robj\" + fi + fi + + test -n "$save_libobjs" && + func_verbose "creating a temporary reloadable object file: $output" + + # Loop through the commands generated above and execute them. + save_ifs="$IFS"; IFS='~' + for cmd in $concat_cmds; do + IFS="$save_ifs" + $opt_silent || { + func_quote_for_expand "$cmd" + eval "func_echo $func_quote_for_expand_result" + } + $opt_dry_run || eval "$cmd" || { + lt_exit=$? + + # Restore the uninstalled library and exit + if test "$opt_mode" = relink; then + ( cd "$output_objdir" && \ + $RM "${realname}T" && \ + $MV "${realname}U" "$realname" ) + fi + + exit $lt_exit + } + done + IFS="$save_ifs" + + if test -n "$export_symbols_regex" && ${skipped_export-false}; then + func_show_eval '$EGREP -e "$export_symbols_regex" "$export_symbols" > "${export_symbols}T"' + func_show_eval '$MV "${export_symbols}T" "$export_symbols"' + fi + fi + + if ${skipped_export-false}; then + if test -n "$export_symbols" && test -n "$include_expsyms"; then + tmp_export_symbols="$export_symbols" + test -n "$orig_export_symbols" && tmp_export_symbols="$orig_export_symbols" + $opt_dry_run || eval '$ECHO "$include_expsyms" | $SP2NL >> "$tmp_export_symbols"' + fi + + if test -n "$orig_export_symbols"; then + # The given exports_symbols file has to be filtered, so filter it. + func_verbose "filter symbol list for \`$libname.la' to tag DATA exports" + # FIXME: $output_objdir/$libname.filter potentially contains lots of + # 's' commands which not all seds can handle. GNU sed should be fine + # though. Also, the filter scales superlinearly with the number of + # global variables. join(1) would be nice here, but unfortunately + # isn't a blessed tool. + $opt_dry_run || $SED -e '/[ ,]DATA/!d;s,\(.*\)\([ \,].*\),s|^\1$|\1\2|,' < $export_symbols > $output_objdir/$libname.filter + func_append delfiles " $export_symbols $output_objdir/$libname.filter" + export_symbols=$output_objdir/$libname.def + $opt_dry_run || $SED -f $output_objdir/$libname.filter < $orig_export_symbols > $export_symbols + fi + fi + + libobjs=$output + # Restore the value of output. + output=$save_output + + if test -n "$convenience" && test -n "$whole_archive_flag_spec"; then + eval libobjs=\"\$libobjs $whole_archive_flag_spec\" + test "X$libobjs" = "X " && libobjs= + fi + # Expand the library linking commands again to reset the + # value of $libobjs for piecewise linking. + + # Do each of the archive commands. + if test "$module" = yes && test -n "$module_cmds" ; then + if test -n "$export_symbols" && test -n "$module_expsym_cmds"; then + cmds=$module_expsym_cmds + else + cmds=$module_cmds + fi + else + if test -n "$export_symbols" && test -n "$archive_expsym_cmds"; then + cmds=$archive_expsym_cmds + else + cmds=$archive_cmds + fi + fi + fi + + if test -n "$delfiles"; then + # Append the command to remove temporary files to $cmds. + eval cmds=\"\$cmds~\$RM $delfiles\" + fi + + # Add any objects from preloaded convenience libraries + if test -n "$dlprefiles"; then + gentop="$output_objdir/${outputname}x" + func_append generated " $gentop" + + func_extract_archives $gentop $dlprefiles + func_append libobjs " $func_extract_archives_result" + test "X$libobjs" = "X " && libobjs= + fi + + save_ifs="$IFS"; IFS='~' + for cmd in $cmds; do + IFS="$save_ifs" + eval cmd=\"$cmd\" + $opt_silent || { + func_quote_for_expand "$cmd" + eval "func_echo $func_quote_for_expand_result" + } + $opt_dry_run || eval "$cmd" || { + lt_exit=$? + + # Restore the uninstalled library and exit + if test "$opt_mode" = relink; then + ( cd "$output_objdir" && \ + $RM "${realname}T" && \ + $MV "${realname}U" "$realname" ) + fi + + exit $lt_exit + } + done + IFS="$save_ifs" + + # Restore the uninstalled library and exit + if test "$opt_mode" = relink; then + $opt_dry_run || eval '(cd $output_objdir && $RM ${realname}T && $MV $realname ${realname}T && $MV ${realname}U $realname)' || exit $? + + if test -n "$convenience"; then + if test -z "$whole_archive_flag_spec"; then + func_show_eval '${RM}r "$gentop"' + fi + fi + + exit $EXIT_SUCCESS + fi + + # Create links to the real library. + for linkname in $linknames; do + if test "$realname" != "$linkname"; then + func_show_eval '(cd "$output_objdir" && $RM "$linkname" && $LN_S "$realname" "$linkname")' 'exit $?' + fi + done + + # If -module or -export-dynamic was specified, set the dlname. + if test "$module" = yes || test "$export_dynamic" = yes; then + # On all known operating systems, these are identical. + dlname="$soname" + fi + fi + ;; + + obj) + if test -n "$dlfiles$dlprefiles" || test "$dlself" != no; then + func_warning "\`-dlopen' is ignored for objects" + fi + + case " $deplibs" in + *\ -l* | *\ -L*) + func_warning "\`-l' and \`-L' are ignored for objects" ;; + esac + + test -n "$rpath" && \ + func_warning "\`-rpath' is ignored for objects" + + test -n "$xrpath" && \ + func_warning "\`-R' is ignored for objects" + + test -n "$vinfo" && \ + func_warning "\`-version-info' is ignored for objects" + + test -n "$release" && \ + func_warning "\`-release' is ignored for objects" + + case $output in + *.lo) + test -n "$objs$old_deplibs" && \ + func_fatal_error "cannot build library object \`$output' from non-libtool objects" + + libobj=$output + func_lo2o "$libobj" + obj=$func_lo2o_result + ;; + *) + libobj= + obj="$output" + ;; + esac + + # Delete the old objects. + $opt_dry_run || $RM $obj $libobj + + # Objects from convenience libraries. This assumes + # single-version convenience libraries. Whenever we create + # different ones for PIC/non-PIC, this we'll have to duplicate + # the extraction. + reload_conv_objs= + gentop= + # reload_cmds runs $LD directly, so let us get rid of + # -Wl from whole_archive_flag_spec and hope we can get by with + # turning comma into space.. + wl= + + if test -n "$convenience"; then + if test -n "$whole_archive_flag_spec"; then + eval tmp_whole_archive_flags=\"$whole_archive_flag_spec\" + reload_conv_objs=$reload_objs\ `$ECHO "$tmp_whole_archive_flags" | $SED 's|,| |g'` + else + gentop="$output_objdir/${obj}x" + func_append generated " $gentop" + + func_extract_archives $gentop $convenience + reload_conv_objs="$reload_objs $func_extract_archives_result" + fi + fi + + # If we're not building shared, we need to use non_pic_objs + test "$build_libtool_libs" != yes && libobjs="$non_pic_objects" + + # Create the old-style object. + reload_objs="$objs$old_deplibs "`$ECHO "$libobjs" | $SP2NL | $SED "/\.${libext}$/d; /\.lib$/d; $lo2o" | $NL2SP`" $reload_conv_objs" ### testsuite: skip nested quoting test + + output="$obj" + func_execute_cmds "$reload_cmds" 'exit $?' + + # Exit if we aren't doing a library object file. + if test -z "$libobj"; then + if test -n "$gentop"; then + func_show_eval '${RM}r "$gentop"' + fi + + exit $EXIT_SUCCESS + fi + + if test "$build_libtool_libs" != yes; then + if test -n "$gentop"; then + func_show_eval '${RM}r "$gentop"' + fi + + # Create an invalid libtool object if no PIC, so that we don't + # accidentally link it into a program. + # $show "echo timestamp > $libobj" + # $opt_dry_run || eval "echo timestamp > $libobj" || exit $? + exit $EXIT_SUCCESS + fi + + if test -n "$pic_flag" || test "$pic_mode" != default; then + # Only do commands if we really have different PIC objects. + reload_objs="$libobjs $reload_conv_objs" + output="$libobj" + func_execute_cmds "$reload_cmds" 'exit $?' + fi + + if test -n "$gentop"; then + func_show_eval '${RM}r "$gentop"' + fi + + exit $EXIT_SUCCESS + ;; + + prog) + case $host in + *cygwin*) func_stripname '' '.exe' "$output" + output=$func_stripname_result.exe;; + esac + test -n "$vinfo" && \ + func_warning "\`-version-info' is ignored for programs" + + test -n "$release" && \ + func_warning "\`-release' is ignored for programs" + + test "$preload" = yes \ + && test "$dlopen_support" = unknown \ + && test "$dlopen_self" = unknown \ + && test "$dlopen_self_static" = unknown && \ + func_warning "\`LT_INIT([dlopen])' not used. Assuming no dlopen support." + + case $host in + *-*-rhapsody* | *-*-darwin1.[012]) + # On Rhapsody replace the C library is the System framework + compile_deplibs=`$ECHO " $compile_deplibs" | $SED 's/ -lc / System.ltframework /'` + finalize_deplibs=`$ECHO " $finalize_deplibs" | $SED 's/ -lc / System.ltframework /'` + ;; + esac + + case $host in + *-*-darwin*) + # Don't allow lazy linking, it breaks C++ global constructors + # But is supposedly fixed on 10.4 or later (yay!). + if test "$tagname" = CXX ; then + case ${MACOSX_DEPLOYMENT_TARGET-10.0} in + 10.[0123]) + func_append compile_command " ${wl}-bind_at_load" + func_append finalize_command " ${wl}-bind_at_load" + ;; + esac + fi + # Time to change all our "foo.ltframework" stuff back to "-framework foo" + compile_deplibs=`$ECHO " $compile_deplibs" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'` + finalize_deplibs=`$ECHO " $finalize_deplibs" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'` + ;; + esac + + + # move library search paths that coincide with paths to not yet + # installed libraries to the beginning of the library search list + new_libs= + for path in $notinst_path; do + case " $new_libs " in + *" -L$path/$objdir "*) ;; + *) + case " $compile_deplibs " in + *" -L$path/$objdir "*) + func_append new_libs " -L$path/$objdir" ;; + esac + ;; + esac + done + for deplib in $compile_deplibs; do + case $deplib in + -L*) + case " $new_libs " in + *" $deplib "*) ;; + *) func_append new_libs " $deplib" ;; + esac + ;; + *) func_append new_libs " $deplib" ;; + esac + done + compile_deplibs="$new_libs" + + + func_append compile_command " $compile_deplibs" + func_append finalize_command " $finalize_deplibs" + + if test -n "$rpath$xrpath"; then + # If the user specified any rpath flags, then add them. + for libdir in $rpath $xrpath; do + # This is the magic to use -rpath. + case "$finalize_rpath " in + *" $libdir "*) ;; + *) func_append finalize_rpath " $libdir" ;; + esac + done + fi + + # Now hardcode the library paths + rpath= + hardcode_libdirs= + for libdir in $compile_rpath $finalize_rpath; do + if test -n "$hardcode_libdir_flag_spec"; then + if test -n "$hardcode_libdir_separator"; then + if test -z "$hardcode_libdirs"; then + hardcode_libdirs="$libdir" + else + # Just accumulate the unique libdirs. + case $hardcode_libdir_separator$hardcode_libdirs$hardcode_libdir_separator in + *"$hardcode_libdir_separator$libdir$hardcode_libdir_separator"*) + ;; + *) + func_append hardcode_libdirs "$hardcode_libdir_separator$libdir" + ;; + esac + fi + else + eval flag=\"$hardcode_libdir_flag_spec\" + func_append rpath " $flag" + fi + elif test -n "$runpath_var"; then + case "$perm_rpath " in + *" $libdir "*) ;; + *) func_append perm_rpath " $libdir" ;; + esac + fi + case $host in + *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-cegcc*) + testbindir=`${ECHO} "$libdir" | ${SED} -e 's*/lib$*/bin*'` + case :$dllsearchpath: in + *":$libdir:"*) ;; + ::) dllsearchpath=$libdir;; + *) func_append dllsearchpath ":$libdir";; + esac + case :$dllsearchpath: in + *":$testbindir:"*) ;; + ::) dllsearchpath=$testbindir;; + *) func_append dllsearchpath ":$testbindir";; + esac + ;; + esac + done + # Substitute the hardcoded libdirs into the rpath. + if test -n "$hardcode_libdir_separator" && + test -n "$hardcode_libdirs"; then + libdir="$hardcode_libdirs" + eval rpath=\" $hardcode_libdir_flag_spec\" + fi + compile_rpath="$rpath" + + rpath= + hardcode_libdirs= + for libdir in $finalize_rpath; do + if test -n "$hardcode_libdir_flag_spec"; then + if test -n "$hardcode_libdir_separator"; then + if test -z "$hardcode_libdirs"; then + hardcode_libdirs="$libdir" + else + # Just accumulate the unique libdirs. + case $hardcode_libdir_separator$hardcode_libdirs$hardcode_libdir_separator in + *"$hardcode_libdir_separator$libdir$hardcode_libdir_separator"*) + ;; + *) + func_append hardcode_libdirs "$hardcode_libdir_separator$libdir" + ;; + esac + fi + else + eval flag=\"$hardcode_libdir_flag_spec\" + func_append rpath " $flag" + fi + elif test -n "$runpath_var"; then + case "$finalize_perm_rpath " in + *" $libdir "*) ;; + *) func_append finalize_perm_rpath " $libdir" ;; + esac + fi + done + # Substitute the hardcoded libdirs into the rpath. + if test -n "$hardcode_libdir_separator" && + test -n "$hardcode_libdirs"; then + libdir="$hardcode_libdirs" + eval rpath=\" $hardcode_libdir_flag_spec\" + fi + finalize_rpath="$rpath" + + if test -n "$libobjs" && test "$build_old_libs" = yes; then + # Transform all the library objects into standard objects. + compile_command=`$ECHO "$compile_command" | $SP2NL | $SED "$lo2o" | $NL2SP` + finalize_command=`$ECHO "$finalize_command" | $SP2NL | $SED "$lo2o" | $NL2SP` + fi + + func_generate_dlsyms "$outputname" "@PROGRAM@" "no" + + # template prelinking step + if test -n "$prelink_cmds"; then + func_execute_cmds "$prelink_cmds" 'exit $?' + fi + + wrappers_required=yes + case $host in + *cegcc* | *mingw32ce*) + # Disable wrappers for cegcc and mingw32ce hosts, we are cross compiling anyway. + wrappers_required=no + ;; + *cygwin* | *mingw* ) + if test "$build_libtool_libs" != yes; then + wrappers_required=no + fi + ;; + *) + if test "$need_relink" = no || test "$build_libtool_libs" != yes; then + wrappers_required=no + fi + ;; + esac + if test "$wrappers_required" = no; then + # Replace the output file specification. + compile_command=`$ECHO "$compile_command" | $SED 's%@OUTPUT@%'"$output"'%g'` + link_command="$compile_command$compile_rpath" + + # We have no uninstalled library dependencies, so finalize right now. + exit_status=0 + func_show_eval "$link_command" 'exit_status=$?' + + if test -n "$postlink_cmds"; then + func_to_tool_file "$output" + postlink_cmds=`func_echo_all "$postlink_cmds" | $SED -e 's%@OUTPUT@%'"$output"'%g' -e 's%@TOOL_OUTPUT@%'"$func_to_tool_file_result"'%g'` + func_execute_cmds "$postlink_cmds" 'exit $?' + fi + + # Delete the generated files. + if test -f "$output_objdir/${outputname}S.${objext}"; then + func_show_eval '$RM "$output_objdir/${outputname}S.${objext}"' + fi + + exit $exit_status + fi + + if test -n "$compile_shlibpath$finalize_shlibpath"; then + compile_command="$shlibpath_var=\"$compile_shlibpath$finalize_shlibpath\$$shlibpath_var\" $compile_command" + fi + if test -n "$finalize_shlibpath"; then + finalize_command="$shlibpath_var=\"$finalize_shlibpath\$$shlibpath_var\" $finalize_command" + fi + + compile_var= + finalize_var= + if test -n "$runpath_var"; then + if test -n "$perm_rpath"; then + # We should set the runpath_var. + rpath= + for dir in $perm_rpath; do + func_append rpath "$dir:" + done + compile_var="$runpath_var=\"$rpath\$$runpath_var\" " + fi + if test -n "$finalize_perm_rpath"; then + # We should set the runpath_var. + rpath= + for dir in $finalize_perm_rpath; do + func_append rpath "$dir:" + done + finalize_var="$runpath_var=\"$rpath\$$runpath_var\" " + fi + fi + + if test "$no_install" = yes; then + # We don't need to create a wrapper script. + link_command="$compile_var$compile_command$compile_rpath" + # Replace the output file specification. + link_command=`$ECHO "$link_command" | $SED 's%@OUTPUT@%'"$output"'%g'` + # Delete the old output file. + $opt_dry_run || $RM $output + # Link the executable and exit + func_show_eval "$link_command" 'exit $?' + + if test -n "$postlink_cmds"; then + func_to_tool_file "$output" + postlink_cmds=`func_echo_all "$postlink_cmds" | $SED -e 's%@OUTPUT@%'"$output"'%g' -e 's%@TOOL_OUTPUT@%'"$func_to_tool_file_result"'%g'` + func_execute_cmds "$postlink_cmds" 'exit $?' + fi + + exit $EXIT_SUCCESS + fi + + if test "$hardcode_action" = relink; then + # Fast installation is not supported + link_command="$compile_var$compile_command$compile_rpath" + relink_command="$finalize_var$finalize_command$finalize_rpath" + + func_warning "this platform does not like uninstalled shared libraries" + func_warning "\`$output' will be relinked during installation" + else + if test "$fast_install" != no; then + link_command="$finalize_var$compile_command$finalize_rpath" + if test "$fast_install" = yes; then + relink_command=`$ECHO "$compile_var$compile_command$compile_rpath" | $SED 's%@OUTPUT@%\$progdir/\$file%g'` + else + # fast_install is set to needless + relink_command= + fi + else + link_command="$compile_var$compile_command$compile_rpath" + relink_command="$finalize_var$finalize_command$finalize_rpath" + fi + fi + + # Replace the output file specification. + link_command=`$ECHO "$link_command" | $SED 's%@OUTPUT@%'"$output_objdir/$outputname"'%g'` + + # Delete the old output files. + $opt_dry_run || $RM $output $output_objdir/$outputname $output_objdir/lt-$outputname + + func_show_eval "$link_command" 'exit $?' + + if test -n "$postlink_cmds"; then + func_to_tool_file "$output_objdir/$outputname" + postlink_cmds=`func_echo_all "$postlink_cmds" | $SED -e 's%@OUTPUT@%'"$output_objdir/$outputname"'%g' -e 's%@TOOL_OUTPUT@%'"$func_to_tool_file_result"'%g'` + func_execute_cmds "$postlink_cmds" 'exit $?' + fi + + # Now create the wrapper script. + func_verbose "creating $output" + + # Quote the relink command for shipping. + if test -n "$relink_command"; then + # Preserve any variables that may affect compiler behavior + for var in $variables_saved_for_relink; do + if eval test -z \"\${$var+set}\"; then + relink_command="{ test -z \"\${$var+set}\" || $lt_unset $var || { $var=; export $var; }; }; $relink_command" + elif eval var_value=\$$var; test -z "$var_value"; then + relink_command="$var=; export $var; $relink_command" + else + func_quote_for_eval "$var_value" + relink_command="$var=$func_quote_for_eval_result; export $var; $relink_command" + fi + done + relink_command="(cd `pwd`; $relink_command)" + relink_command=`$ECHO "$relink_command" | $SED "$sed_quote_subst"` + fi + + # Only actually do things if not in dry run mode. + $opt_dry_run || { + # win32 will think the script is a binary if it has + # a .exe suffix, so we strip it off here. + case $output in + *.exe) func_stripname '' '.exe' "$output" + output=$func_stripname_result ;; + esac + # test for cygwin because mv fails w/o .exe extensions + case $host in + *cygwin*) + exeext=.exe + func_stripname '' '.exe' "$outputname" + outputname=$func_stripname_result ;; + *) exeext= ;; + esac + case $host in + *cygwin* | *mingw* ) + func_dirname_and_basename "$output" "" "." + output_name=$func_basename_result + output_path=$func_dirname_result + cwrappersource="$output_path/$objdir/lt-$output_name.c" + cwrapper="$output_path/$output_name.exe" + $RM $cwrappersource $cwrapper + trap "$RM $cwrappersource $cwrapper; exit $EXIT_FAILURE" 1 2 15 + + func_emit_cwrapperexe_src > $cwrappersource + + # The wrapper executable is built using the $host compiler, + # because it contains $host paths and files. If cross- + # compiling, it, like the target executable, must be + # executed on the $host or under an emulation environment. + $opt_dry_run || { + $LTCC $LTCFLAGS -o $cwrapper $cwrappersource + $STRIP $cwrapper + } + + # Now, create the wrapper script for func_source use: + func_ltwrapper_scriptname $cwrapper + $RM $func_ltwrapper_scriptname_result + trap "$RM $func_ltwrapper_scriptname_result; exit $EXIT_FAILURE" 1 2 15 + $opt_dry_run || { + # note: this script will not be executed, so do not chmod. + if test "x$build" = "x$host" ; then + $cwrapper --lt-dump-script > $func_ltwrapper_scriptname_result + else + func_emit_wrapper no > $func_ltwrapper_scriptname_result + fi + } + ;; + * ) + $RM $output + trap "$RM $output; exit $EXIT_FAILURE" 1 2 15 + + func_emit_wrapper no > $output + chmod +x $output + ;; + esac + } + exit $EXIT_SUCCESS + ;; + esac + + # See if we need to build an old-fashioned archive. + for oldlib in $oldlibs; do + + if test "$build_libtool_libs" = convenience; then + oldobjs="$libobjs_save $symfileobj" + addlibs="$convenience" + build_libtool_libs=no + else + if test "$build_libtool_libs" = module; then + oldobjs="$libobjs_save" + build_libtool_libs=no + else + oldobjs="$old_deplibs $non_pic_objects" + if test "$preload" = yes && test -f "$symfileobj"; then + func_append oldobjs " $symfileobj" + fi + fi + addlibs="$old_convenience" + fi + + if test -n "$addlibs"; then + gentop="$output_objdir/${outputname}x" + func_append generated " $gentop" + + func_extract_archives $gentop $addlibs + func_append oldobjs " $func_extract_archives_result" + fi + + # Do each command in the archive commands. + if test -n "$old_archive_from_new_cmds" && test "$build_libtool_libs" = yes; then + cmds=$old_archive_from_new_cmds + else + + # Add any objects from preloaded convenience libraries + if test -n "$dlprefiles"; then + gentop="$output_objdir/${outputname}x" + func_append generated " $gentop" + + func_extract_archives $gentop $dlprefiles + func_append oldobjs " $func_extract_archives_result" + fi + + # POSIX demands no paths to be encoded in archives. We have + # to avoid creating archives with duplicate basenames if we + # might have to extract them afterwards, e.g., when creating a + # static archive out of a convenience library, or when linking + # the entirety of a libtool archive into another (currently + # not supported by libtool). + if (for obj in $oldobjs + do + func_basename "$obj" + $ECHO "$func_basename_result" + done | sort | sort -uc >/dev/null 2>&1); then + : + else + echo "copying selected object files to avoid basename conflicts..." + gentop="$output_objdir/${outputname}x" + func_append generated " $gentop" + func_mkdir_p "$gentop" + save_oldobjs=$oldobjs + oldobjs= + counter=1 + for obj in $save_oldobjs + do + func_basename "$obj" + objbase="$func_basename_result" + case " $oldobjs " in + " ") oldobjs=$obj ;; + *[\ /]"$objbase "*) + while :; do + # Make sure we don't pick an alternate name that also + # overlaps. + newobj=lt$counter-$objbase + func_arith $counter + 1 + counter=$func_arith_result + case " $oldobjs " in + *[\ /]"$newobj "*) ;; + *) if test ! -f "$gentop/$newobj"; then break; fi ;; + esac + done + func_show_eval "ln $obj $gentop/$newobj || cp $obj $gentop/$newobj" + func_append oldobjs " $gentop/$newobj" + ;; + *) func_append oldobjs " $obj" ;; + esac + done + fi + func_to_tool_file "$oldlib" func_convert_file_msys_to_w32 + tool_oldlib=$func_to_tool_file_result + eval cmds=\"$old_archive_cmds\" + + func_len " $cmds" + len=$func_len_result + if test "$len" -lt "$max_cmd_len" || test "$max_cmd_len" -le -1; then + cmds=$old_archive_cmds + elif test -n "$archiver_list_spec"; then + func_verbose "using command file archive linking..." + for obj in $oldobjs + do + func_to_tool_file "$obj" + $ECHO "$func_to_tool_file_result" + done > $output_objdir/$libname.libcmd + func_to_tool_file "$output_objdir/$libname.libcmd" + oldobjs=" $archiver_list_spec$func_to_tool_file_result" + cmds=$old_archive_cmds + else + # the command line is too long to link in one step, link in parts + func_verbose "using piecewise archive linking..." + save_RANLIB=$RANLIB + RANLIB=: + objlist= + concat_cmds= + save_oldobjs=$oldobjs + oldobjs= + # Is there a better way of finding the last object in the list? + for obj in $save_oldobjs + do + last_oldobj=$obj + done + eval test_cmds=\"$old_archive_cmds\" + func_len " $test_cmds" + len0=$func_len_result + len=$len0 + for obj in $save_oldobjs + do + func_len " $obj" + func_arith $len + $func_len_result + len=$func_arith_result + func_append objlist " $obj" + if test "$len" -lt "$max_cmd_len"; then + : + else + # the above command should be used before it gets too long + oldobjs=$objlist + if test "$obj" = "$last_oldobj" ; then + RANLIB=$save_RANLIB + fi + test -z "$concat_cmds" || concat_cmds=$concat_cmds~ + eval concat_cmds=\"\${concat_cmds}$old_archive_cmds\" + objlist= + len=$len0 + fi + done + RANLIB=$save_RANLIB + oldobjs=$objlist + if test "X$oldobjs" = "X" ; then + eval cmds=\"\$concat_cmds\" + else + eval cmds=\"\$concat_cmds~\$old_archive_cmds\" + fi + fi + fi + func_execute_cmds "$cmds" 'exit $?' + done + + test -n "$generated" && \ + func_show_eval "${RM}r$generated" + + # Now create the libtool archive. + case $output in + *.la) + old_library= + test "$build_old_libs" = yes && old_library="$libname.$libext" + func_verbose "creating $output" + + # Preserve any variables that may affect compiler behavior + for var in $variables_saved_for_relink; do + if eval test -z \"\${$var+set}\"; then + relink_command="{ test -z \"\${$var+set}\" || $lt_unset $var || { $var=; export $var; }; }; $relink_command" + elif eval var_value=\$$var; test -z "$var_value"; then + relink_command="$var=; export $var; $relink_command" + else + func_quote_for_eval "$var_value" + relink_command="$var=$func_quote_for_eval_result; export $var; $relink_command" + fi + done + # Quote the link command for shipping. + relink_command="(cd `pwd`; $SHELL $progpath $preserve_args --mode=relink $libtool_args @inst_prefix_dir@)" + relink_command=`$ECHO "$relink_command" | $SED "$sed_quote_subst"` + if test "$hardcode_automatic" = yes ; then + relink_command= + fi + + # Only create the output if not a dry run. + $opt_dry_run || { + for installed in no yes; do + if test "$installed" = yes; then + if test -z "$install_libdir"; then + break + fi + output="$output_objdir/$outputname"i + # Replace all uninstalled libtool libraries with the installed ones + newdependency_libs= + for deplib in $dependency_libs; do + case $deplib in + *.la) + func_basename "$deplib" + name="$func_basename_result" + func_resolve_sysroot "$deplib" + eval libdir=`${SED} -n -e 's/^libdir=\(.*\)$/\1/p' $func_resolve_sysroot_result` + test -z "$libdir" && \ + func_fatal_error "\`$deplib' is not a valid libtool archive" + func_append newdependency_libs " ${lt_sysroot:+=}$libdir/$name" + ;; + -L*) + func_stripname -L '' "$deplib" + func_replace_sysroot "$func_stripname_result" + func_append newdependency_libs " -L$func_replace_sysroot_result" + ;; + -R*) + func_stripname -R '' "$deplib" + func_replace_sysroot "$func_stripname_result" + func_append newdependency_libs " -R$func_replace_sysroot_result" + ;; + *) func_append newdependency_libs " $deplib" ;; + esac + done + dependency_libs="$newdependency_libs" + newdlfiles= + + for lib in $dlfiles; do + case $lib in + *.la) + func_basename "$lib" + name="$func_basename_result" + eval libdir=`${SED} -n -e 's/^libdir=\(.*\)$/\1/p' $lib` + test -z "$libdir" && \ + func_fatal_error "\`$lib' is not a valid libtool archive" + func_append newdlfiles " ${lt_sysroot:+=}$libdir/$name" + ;; + *) func_append newdlfiles " $lib" ;; + esac + done + dlfiles="$newdlfiles" + newdlprefiles= + for lib in $dlprefiles; do + case $lib in + *.la) + # Only pass preopened files to the pseudo-archive (for + # eventual linking with the app. that links it) if we + # didn't already link the preopened objects directly into + # the library: + func_basename "$lib" + name="$func_basename_result" + eval libdir=`${SED} -n -e 's/^libdir=\(.*\)$/\1/p' $lib` + test -z "$libdir" && \ + func_fatal_error "\`$lib' is not a valid libtool archive" + func_append newdlprefiles " ${lt_sysroot:+=}$libdir/$name" + ;; + esac + done + dlprefiles="$newdlprefiles" + else + newdlfiles= + for lib in $dlfiles; do + case $lib in + [\\/]* | [A-Za-z]:[\\/]*) abs="$lib" ;; + *) abs=`pwd`"/$lib" ;; + esac + func_append newdlfiles " $abs" + done + dlfiles="$newdlfiles" + newdlprefiles= + for lib in $dlprefiles; do + case $lib in + [\\/]* | [A-Za-z]:[\\/]*) abs="$lib" ;; + *) abs=`pwd`"/$lib" ;; + esac + func_append newdlprefiles " $abs" + done + dlprefiles="$newdlprefiles" + fi + $RM $output + # place dlname in correct position for cygwin + # In fact, it would be nice if we could use this code for all target + # systems that can't hard-code library paths into their executables + # and that have no shared library path variable independent of PATH, + # but it turns out we can't easily determine that from inspecting + # libtool variables, so we have to hard-code the OSs to which it + # applies here; at the moment, that means platforms that use the PE + # object format with DLL files. See the long comment at the top of + # tests/bindir.at for full details. + tdlname=$dlname + case $host,$output,$installed,$module,$dlname in + *cygwin*,*lai,yes,no,*.dll | *mingw*,*lai,yes,no,*.dll | *cegcc*,*lai,yes,no,*.dll) + # If a -bindir argument was supplied, place the dll there. + if test "x$bindir" != x ; + then + func_relative_path "$install_libdir" "$bindir" + tdlname=$func_relative_path_result$dlname + else + # Otherwise fall back on heuristic. + tdlname=../bin/$dlname + fi + ;; + esac + $ECHO > $output "\ +# $outputname - a libtool library file +# Generated by $PROGRAM (GNU $PACKAGE$TIMESTAMP) $VERSION +# +# Please DO NOT delete this file! +# It is necessary for linking the library. + +# The name that we can dlopen(3). +dlname='$tdlname' + +# Names of this library. +library_names='$library_names' + +# The name of the static archive. +old_library='$old_library' + +# Linker flags that can not go in dependency_libs. +inherited_linker_flags='$new_inherited_linker_flags' + +# Libraries that this one depends upon. +dependency_libs='$dependency_libs' + +# Names of additional weak libraries provided by this library +weak_library_names='$weak_libs' + +# Version information for $libname. +current=$current +age=$age +revision=$revision + +# Is this an already installed library? +installed=$installed + +# Should we warn about portability when linking against -modules? +shouldnotlink=$module + +# Files to dlopen/dlpreopen +dlopen='$dlfiles' +dlpreopen='$dlprefiles' + +# Directory that this library needs to be installed in: +libdir='$install_libdir'" + if test "$installed" = no && test "$need_relink" = yes; then + $ECHO >> $output "\ +relink_command=\"$relink_command\"" + fi + done + } + + # Do a symbolic link so that the libtool archive can be found in + # LD_LIBRARY_PATH before the program is installed. + func_show_eval '( cd "$output_objdir" && $RM "$outputname" && $LN_S "../$outputname" "$outputname" )' 'exit $?' + ;; + esac + exit $EXIT_SUCCESS +} + +{ test "$opt_mode" = link || test "$opt_mode" = relink; } && + func_mode_link ${1+"$@"} + + +# func_mode_uninstall arg... +func_mode_uninstall () +{ + $opt_debug + RM="$nonopt" + files= + rmforce= + exit_status=0 + + # This variable tells wrapper scripts just to set variables rather + # than running their programs. + libtool_install_magic="$magic" + + for arg + do + case $arg in + -f) func_append RM " $arg"; rmforce=yes ;; + -*) func_append RM " $arg" ;; + *) func_append files " $arg" ;; + esac + done + + test -z "$RM" && \ + func_fatal_help "you must specify an RM program" + + rmdirs= + + for file in $files; do + func_dirname "$file" "" "." + dir="$func_dirname_result" + if test "X$dir" = X.; then + odir="$objdir" + else + odir="$dir/$objdir" + fi + func_basename "$file" + name="$func_basename_result" + test "$opt_mode" = uninstall && odir="$dir" + + # Remember odir for removal later, being careful to avoid duplicates + if test "$opt_mode" = clean; then + case " $rmdirs " in + *" $odir "*) ;; + *) func_append rmdirs " $odir" ;; + esac + fi + + # Don't error if the file doesn't exist and rm -f was used. + if { test -L "$file"; } >/dev/null 2>&1 || + { test -h "$file"; } >/dev/null 2>&1 || + test -f "$file"; then + : + elif test -d "$file"; then + exit_status=1 + continue + elif test "$rmforce" = yes; then + continue + fi + + rmfiles="$file" + + case $name in + *.la) + # Possibly a libtool archive, so verify it. + if func_lalib_p "$file"; then + func_source $dir/$name + + # Delete the libtool libraries and symlinks. + for n in $library_names; do + func_append rmfiles " $odir/$n" + done + test -n "$old_library" && func_append rmfiles " $odir/$old_library" + + case "$opt_mode" in + clean) + case " $library_names " in + *" $dlname "*) ;; + *) test -n "$dlname" && func_append rmfiles " $odir/$dlname" ;; + esac + test -n "$libdir" && func_append rmfiles " $odir/$name $odir/${name}i" + ;; + uninstall) + if test -n "$library_names"; then + # Do each command in the postuninstall commands. + func_execute_cmds "$postuninstall_cmds" 'test "$rmforce" = yes || exit_status=1' + fi + + if test -n "$old_library"; then + # Do each command in the old_postuninstall commands. + func_execute_cmds "$old_postuninstall_cmds" 'test "$rmforce" = yes || exit_status=1' + fi + # FIXME: should reinstall the best remaining shared library. + ;; + esac + fi + ;; + + *.lo) + # Possibly a libtool object, so verify it. + if func_lalib_p "$file"; then + + # Read the .lo file + func_source $dir/$name + + # Add PIC object to the list of files to remove. + if test -n "$pic_object" && + test "$pic_object" != none; then + func_append rmfiles " $dir/$pic_object" + fi + + # Add non-PIC object to the list of files to remove. + if test -n "$non_pic_object" && + test "$non_pic_object" != none; then + func_append rmfiles " $dir/$non_pic_object" + fi + fi + ;; + + *) + if test "$opt_mode" = clean ; then + noexename=$name + case $file in + *.exe) + func_stripname '' '.exe' "$file" + file=$func_stripname_result + func_stripname '' '.exe' "$name" + noexename=$func_stripname_result + # $file with .exe has already been added to rmfiles, + # add $file without .exe + func_append rmfiles " $file" + ;; + esac + # Do a test to see if this is a libtool program. + if func_ltwrapper_p "$file"; then + if func_ltwrapper_executable_p "$file"; then + func_ltwrapper_scriptname "$file" + relink_command= + func_source $func_ltwrapper_scriptname_result + func_append rmfiles " $func_ltwrapper_scriptname_result" + else + relink_command= + func_source $dir/$noexename + fi + + # note $name still contains .exe if it was in $file originally + # as does the version of $file that was added into $rmfiles + func_append rmfiles " $odir/$name $odir/${name}S.${objext}" + if test "$fast_install" = yes && test -n "$relink_command"; then + func_append rmfiles " $odir/lt-$name" + fi + if test "X$noexename" != "X$name" ; then + func_append rmfiles " $odir/lt-${noexename}.c" + fi + fi + fi + ;; + esac + func_show_eval "$RM $rmfiles" 'exit_status=1' + done + + # Try to remove the ${objdir}s in the directories where we deleted files + for dir in $rmdirs; do + if test -d "$dir"; then + func_show_eval "rmdir $dir >/dev/null 2>&1" + fi + done + + exit $exit_status +} + +{ test "$opt_mode" = uninstall || test "$opt_mode" = clean; } && + func_mode_uninstall ${1+"$@"} + +test -z "$opt_mode" && { + help="$generic_help" + func_fatal_help "you must specify a MODE" +} + +test -z "$exec_cmd" && \ + func_fatal_help "invalid operation mode \`$opt_mode'" + +if test -n "$exec_cmd"; then + eval exec "$exec_cmd" + exit $EXIT_FAILURE +fi + +exit $exit_status + + +# The TAGs below are defined such that we never get into a situation +# in which we disable both kinds of libraries. Given conflicting +# choices, we go for a static library, that is the most portable, +# since we can't tell whether shared libraries were disabled because +# the user asked for that or because the platform doesn't support +# them. This is particularly important on AIX, because we don't +# support having both static and shared libraries enabled at the same +# time on that platform, so we default to a shared-only configuration. +# If a disable-shared tag is given, we'll fallback to a static-only +# configuration. But we'll never go from static-only to shared-only. + +# ### BEGIN LIBTOOL TAG CONFIG: disable-shared +build_libtool_libs=no +build_old_libs=yes +# ### END LIBTOOL TAG CONFIG: disable-shared + +# ### BEGIN LIBTOOL TAG CONFIG: disable-static +build_old_libs=`case $build_libtool_libs in yes) echo no;; *) echo yes;; esac` +# ### END LIBTOOL TAG CONFIG: disable-static + +# Local Variables: +# mode:shell-script +# sh-indentation:2 +# End: +# vi:sw=2 + diff --git a/m4/ax_boost_asio.m4 b/m4/ax_boost_asio.m4 new file mode 100644 index 0000000..b57d487 --- /dev/null +++ b/m4/ax_boost_asio.m4 @@ -0,0 +1,110 @@ +# =========================================================================== +# http://www.gnu.org/software/autoconf-archive/ax_boost_asio.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_BOOST_ASIO +# +# DESCRIPTION +# +# Test for Asio library from the Boost C++ libraries. The macro requires a +# preceding call to AX_BOOST_BASE. Further documentation is available at +# . +# +# This macro calls: +# +# AC_SUBST(BOOST_ASIO_LIB) +# +# And sets: +# +# HAVE_BOOST_ASIO +# +# LICENSE +# +# Copyright (c) 2008 Thomas Porschberg +# Copyright (c) 2008 Pete Greenwell +# +# 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 any +# warranty. + +#serial 16 + +AC_DEFUN([AX_BOOST_ASIO], +[ + AC_ARG_WITH([boost-asio], + AS_HELP_STRING([--with-boost-asio@<:@=special-lib@:>@], + [use the ASIO library from boost - it is possible to specify a certain library for the linker + e.g. --with-boost-asio=boost_system-gcc41-mt-1_34 ]), + [ + if test "$withval" = "no"; then + want_boost="no" + elif test "$withval" = "yes"; then + want_boost="yes" + ax_boost_user_asio_lib="" + else + want_boost="yes" + ax_boost_user_asio_lib="$withval" + fi + ], + [want_boost="yes"] + ) + + if test "x$want_boost" = "xyes"; then + AC_REQUIRE([AC_PROG_CC]) + CPPFLAGS_SAVED="$CPPFLAGS" + CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS" + export CPPFLAGS + + LDFLAGS_SAVED="$LDFLAGS" + LDFLAGS="$LDFLAGS $BOOST_LDFLAGS" + export LDFLAGS + + AC_CACHE_CHECK(whether the Boost::ASIO library is available, + ax_cv_boost_asio, + [AC_LANG_PUSH([C++]) + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ @%:@include + ]], + [[ + + boost::asio::io_service io; + boost::system::error_code timer_result; + boost::asio::deadline_timer t(io); + t.cancel(); + io.run_one(); + return 0; + ]])], + ax_cv_boost_asio=yes, ax_cv_boost_asio=no) + AC_LANG_POP([C++]) + ]) + if test "x$ax_cv_boost_asio" = "xyes"; then + AC_DEFINE(HAVE_BOOST_ASIO,,[define if the Boost::ASIO library is available]) + BN=boost_system + BOOSTLIBDIR=`echo $BOOST_LDFLAGS | sed -e 's/@<:@^\/@:>@*//'` + if test "x$ax_boost_user_asio_lib" = "x"; then + for ax_lib in `ls $BOOSTLIBDIR/libboost_system*.so* $BOOSTLIBDIR/libboost_system*.dylib* $BOOSTLIBDIR/libboost_system*.a* 2>/dev/null | sed 's,.*/,,' | sed -e 's;^lib\(boost_system.*\)\.so.*$;\1;' -e 's;^lib\(boost_system.*\)\.dylib.*$;\1;' -e 's;^lib\(boost_system.*\)\.a.*$;\1;' ` ; do + AC_CHECK_LIB($ax_lib, main, [BOOST_ASIO_LIB="-l$ax_lib" AC_SUBST(BOOST_ASIO_LIB) link_thread="yes" break], + [link_thread="no"]) + done + else + for ax_lib in $ax_boost_user_asio_lib $BN-$ax_boost_user_asio_lib; do + AC_CHECK_LIB($ax_lib, main, + [BOOST_ASIO_LIB="-l$ax_lib" AC_SUBST(BOOST_ASIO_LIB) link_asio="yes" break], + [link_asio="no"]) + done + + fi + if test "x$ax_lib" = "x"; then + AC_MSG_ERROR(Could not find a version of the library!) + fi + if test "x$link_asio" = "xno"; then + AC_MSG_ERROR(Could not link against $ax_lib !) + fi + fi + + CPPFLAGS="$CPPFLAGS_SAVED" + LDFLAGS="$LDFLAGS_SAVED" + fi +]) diff --git a/m4/ax_boost_base.m4 b/m4/ax_boost_base.m4 new file mode 100644 index 0000000..d7c9d0d --- /dev/null +++ b/m4/ax_boost_base.m4 @@ -0,0 +1,275 @@ +# =========================================================================== +# http://www.gnu.org/software/autoconf-archive/ax_boost_base.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_BOOST_BASE([MINIMUM-VERSION], [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) +# +# DESCRIPTION +# +# Test for the Boost C++ libraries of a particular version (or newer) +# +# If no path to the installed boost library is given the macro searchs +# under /usr, /usr/local, /opt and /opt/local and evaluates the +# $BOOST_ROOT environment variable. Further documentation is available at +# . +# +# This macro calls: +# +# AC_SUBST(BOOST_CPPFLAGS) / AC_SUBST(BOOST_LDFLAGS) +# +# And sets: +# +# HAVE_BOOST +# +# LICENSE +# +# Copyright (c) 2008 Thomas Porschberg +# Copyright (c) 2009 Peter Adolphs +# +# 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 any +# warranty. + +#serial 25 + +AC_DEFUN([AX_BOOST_BASE], +[ +AC_ARG_WITH([boost], + [AS_HELP_STRING([--with-boost@<:@=ARG@:>@], + [use Boost library from a standard location (ARG=yes), + from the specified location (ARG=), + or disable it (ARG=no) + @<:@ARG=yes@:>@ ])], + [ + if test "$withval" = "no"; then + want_boost="no" + elif test "$withval" = "yes"; then + want_boost="yes" + ac_boost_path="" + else + want_boost="yes" + ac_boost_path="$withval" + fi + ], + [want_boost="yes"]) + + +AC_ARG_WITH([boost-libdir], + AS_HELP_STRING([--with-boost-libdir=LIB_DIR], + [Force given directory for boost libraries. Note that this will override library path detection, so use this parameter only if default library detection fails and you know exactly where your boost libraries are located.]), + [ + if test -d "$withval" + then + ac_boost_lib_path="$withval" + else + AC_MSG_ERROR(--with-boost-libdir expected directory name) + fi + ], + [ac_boost_lib_path=""] +) + +if test "x$want_boost" = "xyes"; then + boost_lib_version_req=ifelse([$1], ,1.20.0,$1) + boost_lib_version_req_shorten=`expr $boost_lib_version_req : '\([[0-9]]*\.[[0-9]]*\)'` + boost_lib_version_req_major=`expr $boost_lib_version_req : '\([[0-9]]*\)'` + boost_lib_version_req_minor=`expr $boost_lib_version_req : '[[0-9]]*\.\([[0-9]]*\)'` + boost_lib_version_req_sub_minor=`expr $boost_lib_version_req : '[[0-9]]*\.[[0-9]]*\.\([[0-9]]*\)'` + if test "x$boost_lib_version_req_sub_minor" = "x" ; then + boost_lib_version_req_sub_minor="0" + fi + WANT_BOOST_VERSION=`expr $boost_lib_version_req_major \* 100000 \+ $boost_lib_version_req_minor \* 100 \+ $boost_lib_version_req_sub_minor` + AC_MSG_CHECKING(for boostlib >= $boost_lib_version_req) + succeeded=no + + dnl On 64-bit systems check for system libraries in both lib64 and lib. + dnl The former is specified by FHS, but e.g. Debian does not adhere to + dnl this (as it rises problems for generic multi-arch support). + dnl The last entry in the list is chosen by default when no libraries + dnl are found, e.g. when only header-only libraries are installed! + libsubdirs="lib" + ax_arch=`uname -m` + case $ax_arch in + x86_64) + libsubdirs="lib64 libx32 lib lib64" + ;; + ppc64|s390x|sparc64|aarch64|ppc64le) + libsubdirs="lib64 lib lib64 ppc64le" + ;; + esac + + dnl allow for real multi-arch paths e.g. /usr/lib/x86_64-linux-gnu. Give + dnl them priority over the other paths since, if libs are found there, they + dnl are almost assuredly the ones desired. + AC_REQUIRE([AC_CANONICAL_HOST]) + libsubdirs="lib/${host_cpu}-${host_os} $libsubdirs" + + case ${host_cpu} in + i?86) + libsubdirs="lib/i386-${host_os} $libsubdirs" + ;; + esac + + dnl first we check the system location for boost libraries + dnl this location ist chosen if boost libraries are installed with the --layout=system option + dnl or if you install boost with RPM + if test "$ac_boost_path" != ""; then + BOOST_CPPFLAGS="-I$ac_boost_path/include" + for ac_boost_path_tmp in $libsubdirs; do + if test -d "$ac_boost_path"/"$ac_boost_path_tmp" ; then + BOOST_LDFLAGS="-L$ac_boost_path/$ac_boost_path_tmp" + break + fi + done + elif test "$cross_compiling" != yes; then + for ac_boost_path_tmp in /usr /usr/local /opt /opt/local ; do + if test -d "$ac_boost_path_tmp/include/boost" && test -r "$ac_boost_path_tmp/include/boost"; then + for libsubdir in $libsubdirs ; do + if ls "$ac_boost_path_tmp/$libsubdir/libboost_"* >/dev/null 2>&1 ; then break; fi + done + BOOST_LDFLAGS="-L$ac_boost_path_tmp/$libsubdir" + BOOST_CPPFLAGS="-I$ac_boost_path_tmp/include" + break; + fi + done + fi + + dnl overwrite ld flags if we have required special directory with + dnl --with-boost-libdir parameter + if test "$ac_boost_lib_path" != ""; then + BOOST_LDFLAGS="-L$ac_boost_lib_path" + fi + + CPPFLAGS_SAVED="$CPPFLAGS" + CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS" + export CPPFLAGS + + LDFLAGS_SAVED="$LDFLAGS" + LDFLAGS="$LDFLAGS $BOOST_LDFLAGS" + export LDFLAGS + + AC_REQUIRE([AC_PROG_CXX]) + AC_LANG_PUSH(C++) + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ + @%:@include + ]], [[ + #if BOOST_VERSION >= $WANT_BOOST_VERSION + // Everything is okay + #else + # error Boost version is too old + #endif + ]])],[ + AC_MSG_RESULT(yes) + succeeded=yes + found_system=yes + ],[ + ]) + AC_LANG_POP([C++]) + + + + dnl if we found no boost with system layout we search for boost libraries + dnl built and installed without the --layout=system option or for a staged(not installed) version + if test "x$succeeded" != "xyes"; then + _version=0 + if test "$ac_boost_path" != ""; then + if test -d "$ac_boost_path" && test -r "$ac_boost_path"; then + for i in `ls -d $ac_boost_path/include/boost-* 2>/dev/null`; do + _version_tmp=`echo $i | sed "s#$ac_boost_path##" | sed 's/\/include\/boost-//' | sed 's/_/./'` + V_CHECK=`expr $_version_tmp \> $_version` + if test "$V_CHECK" = "1" ; then + _version=$_version_tmp + fi + VERSION_UNDERSCORE=`echo $_version | sed 's/\./_/'` + BOOST_CPPFLAGS="-I$ac_boost_path/include/boost-$VERSION_UNDERSCORE" + done + fi + else + if test "$cross_compiling" != yes; then + for ac_boost_path in /usr /usr/local /opt /opt/local ; do + if test -d "$ac_boost_path" && test -r "$ac_boost_path"; then + for i in `ls -d $ac_boost_path/include/boost-* 2>/dev/null`; do + _version_tmp=`echo $i | sed "s#$ac_boost_path##" | sed 's/\/include\/boost-//' | sed 's/_/./'` + V_CHECK=`expr $_version_tmp \> $_version` + if test "$V_CHECK" = "1" ; then + _version=$_version_tmp + best_path=$ac_boost_path + fi + done + fi + done + + VERSION_UNDERSCORE=`echo $_version | sed 's/\./_/'` + BOOST_CPPFLAGS="-I$best_path/include/boost-$VERSION_UNDERSCORE" + if test "$ac_boost_lib_path" = ""; then + for libsubdir in $libsubdirs ; do + if ls "$best_path/$libsubdir/libboost_"* >/dev/null 2>&1 ; then break; fi + done + BOOST_LDFLAGS="-L$best_path/$libsubdir" + fi + fi + + if test "x$BOOST_ROOT" != "x"; then + for libsubdir in $libsubdirs ; do + if ls "$BOOST_ROOT/stage/$libsubdir/libboost_"* >/dev/null 2>&1 ; then break; fi + done + if test -d "$BOOST_ROOT" && test -r "$BOOST_ROOT" && test -d "$BOOST_ROOT/stage/$libsubdir" && test -r "$BOOST_ROOT/stage/$libsubdir"; then + version_dir=`expr //$BOOST_ROOT : '.*/\(.*\)'` + stage_version=`echo $version_dir | sed 's/boost_//' | sed 's/_/./g'` + stage_version_shorten=`expr $stage_version : '\([[0-9]]*\.[[0-9]]*\)'` + V_CHECK=`expr $stage_version_shorten \>\= $_version` + if test "$V_CHECK" = "1" -a "$ac_boost_lib_path" = "" ; then + AC_MSG_NOTICE(We will use a staged boost library from $BOOST_ROOT) + BOOST_CPPFLAGS="-I$BOOST_ROOT" + BOOST_LDFLAGS="-L$BOOST_ROOT/stage/$libsubdir" + fi + fi + fi + fi + + CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS" + export CPPFLAGS + LDFLAGS="$LDFLAGS $BOOST_LDFLAGS" + export LDFLAGS + + AC_LANG_PUSH(C++) + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ + @%:@include + ]], [[ + #if BOOST_VERSION >= $WANT_BOOST_VERSION + // Everything is okay + #else + # error Boost version is too old + #endif + ]])],[ + AC_MSG_RESULT(yes) + succeeded=yes + found_system=yes + ],[ + ]) + AC_LANG_POP([C++]) + fi + + if test "$succeeded" != "yes" ; then + if test "$_version" = "0" ; then + AC_MSG_NOTICE([[We could not detect the boost libraries (version $boost_lib_version_req_shorten or higher). If you have a staged boost library (still not installed) please specify \$BOOST_ROOT in your environment and do not give a PATH to --with-boost option. If you are sure you have boost installed, then check your version number looking in . See http://randspringer.de/boost for more documentation.]]) + else + AC_MSG_NOTICE([Your boost libraries seems to old (version $_version).]) + fi + # execute ACTION-IF-NOT-FOUND (if present): + ifelse([$3], , :, [$3]) + else + AC_SUBST(BOOST_CPPFLAGS) + AC_SUBST(BOOST_LDFLAGS) + AC_DEFINE(HAVE_BOOST,,[define if the Boost library is available]) + # execute ACTION-IF-FOUND (if present): + ifelse([$2], , :, [$2]) + fi + + CPPFLAGS="$CPPFLAGS_SAVED" + LDFLAGS="$LDFLAGS_SAVED" +fi + +]) diff --git a/m4/ax_boost_system.m4 b/m4/ax_boost_system.m4 new file mode 100644 index 0000000..c4c4555 --- /dev/null +++ b/m4/ax_boost_system.m4 @@ -0,0 +1,120 @@ +# =========================================================================== +# http://www.gnu.org/software/autoconf-archive/ax_boost_system.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_BOOST_SYSTEM +# +# DESCRIPTION +# +# Test for System library from the Boost C++ libraries. The macro requires +# a preceding call to AX_BOOST_BASE. Further documentation is available at +# . +# +# This macro calls: +# +# AC_SUBST(BOOST_SYSTEM_LIB) +# +# And sets: +# +# HAVE_BOOST_SYSTEM +# +# LICENSE +# +# Copyright (c) 2008 Thomas Porschberg +# Copyright (c) 2008 Michael Tindal +# Copyright (c) 2008 Daniel Casimiro +# +# 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 any +# warranty. + +#serial 17 + +AC_DEFUN([AX_BOOST_SYSTEM], +[ + AC_ARG_WITH([boost-system], + AS_HELP_STRING([--with-boost-system@<:@=special-lib@:>@], + [use the System library from boost - it is possible to specify a certain library for the linker + e.g. --with-boost-system=boost_system-gcc-mt ]), + [ + if test "$withval" = "no"; then + want_boost="no" + elif test "$withval" = "yes"; then + want_boost="yes" + ax_boost_user_system_lib="" + else + want_boost="yes" + ax_boost_user_system_lib="$withval" + fi + ], + [want_boost="yes"] + ) + + if test "x$want_boost" = "xyes"; then + AC_REQUIRE([AC_PROG_CC]) + AC_REQUIRE([AC_CANONICAL_BUILD]) + CPPFLAGS_SAVED="$CPPFLAGS" + CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS" + export CPPFLAGS + + LDFLAGS_SAVED="$LDFLAGS" + LDFLAGS="$LDFLAGS $BOOST_LDFLAGS" + export LDFLAGS + + AC_CACHE_CHECK(whether the Boost::System library is available, + ax_cv_boost_system, + [AC_LANG_PUSH([C++]) + CXXFLAGS_SAVE=$CXXFLAGS + + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[@%:@include ]], + [[boost::system::system_category]])], + ax_cv_boost_system=yes, ax_cv_boost_system=no) + CXXFLAGS=$CXXFLAGS_SAVE + AC_LANG_POP([C++]) + ]) + if test "x$ax_cv_boost_system" = "xyes"; then + AC_SUBST(BOOST_CPPFLAGS) + + AC_DEFINE(HAVE_BOOST_SYSTEM,,[define if the Boost::System library is available]) + BOOSTLIBDIR=`echo $BOOST_LDFLAGS | sed -e 's/@<:@^\/@:>@*//'` + + LDFLAGS_SAVE=$LDFLAGS + if test "x$ax_boost_user_system_lib" = "x"; then + for libextension in `ls -r $BOOSTLIBDIR/libboost_system* 2>/dev/null | sed 's,.*/lib,,' | sed 's,\..*,,'` ; do + ax_lib=${libextension} + AC_CHECK_LIB($ax_lib, exit, + [BOOST_SYSTEM_LIB="-l$ax_lib"; AC_SUBST(BOOST_SYSTEM_LIB) link_system="yes"; break], + [link_system="no"]) + done + if test "x$link_system" != "xyes"; then + for libextension in `ls -r $BOOSTLIBDIR/boost_system* 2>/dev/null | sed 's,.*/,,' | sed -e 's,\..*,,'` ; do + ax_lib=${libextension} + AC_CHECK_LIB($ax_lib, exit, + [BOOST_SYSTEM_LIB="-l$ax_lib"; AC_SUBST(BOOST_SYSTEM_LIB) link_system="yes"; break], + [link_system="no"]) + done + fi + + else + for ax_lib in $ax_boost_user_system_lib boost_system-$ax_boost_user_system_lib; do + AC_CHECK_LIB($ax_lib, exit, + [BOOST_SYSTEM_LIB="-l$ax_lib"; AC_SUBST(BOOST_SYSTEM_LIB) link_system="yes"; break], + [link_system="no"]) + done + + fi + if test "x$ax_lib" = "x"; then + AC_MSG_ERROR(Could not find a version of the library!) + fi + if test "x$link_system" = "xno"; then + AC_MSG_ERROR(Could not link against $ax_lib !) + fi + fi + + CPPFLAGS="$CPPFLAGS_SAVED" + LDFLAGS="$LDFLAGS_SAVED" + fi +]) diff --git a/m4/ax_boost_thread.m4 b/m4/ax_boost_thread.m4 new file mode 100644 index 0000000..79e12cd --- /dev/null +++ b/m4/ax_boost_thread.m4 @@ -0,0 +1,149 @@ +# =========================================================================== +# http://www.gnu.org/software/autoconf-archive/ax_boost_thread.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_BOOST_THREAD +# +# DESCRIPTION +# +# Test for Thread library from the Boost C++ libraries. The macro requires +# a preceding call to AX_BOOST_BASE. Further documentation is available at +# . +# +# This macro calls: +# +# AC_SUBST(BOOST_THREAD_LIB) +# +# And sets: +# +# HAVE_BOOST_THREAD +# +# LICENSE +# +# Copyright (c) 2009 Thomas Porschberg +# Copyright (c) 2009 Michael Tindal +# +# 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 any +# warranty. + +#serial 27 + +AC_DEFUN([AX_BOOST_THREAD], +[ + AC_ARG_WITH([boost-thread], + AS_HELP_STRING([--with-boost-thread@<:@=special-lib@:>@], + [use the Thread library from boost - it is possible to specify a certain library for the linker + e.g. --with-boost-thread=boost_thread-gcc-mt ]), + [ + if test "$withval" = "no"; then + want_boost="no" + elif test "$withval" = "yes"; then + want_boost="yes" + ax_boost_user_thread_lib="" + else + want_boost="yes" + ax_boost_user_thread_lib="$withval" + fi + ], + [want_boost="yes"] + ) + + if test "x$want_boost" = "xyes"; then + AC_REQUIRE([AC_PROG_CC]) + AC_REQUIRE([AC_CANONICAL_BUILD]) + CPPFLAGS_SAVED="$CPPFLAGS" + CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS" + export CPPFLAGS + + LDFLAGS_SAVED="$LDFLAGS" + LDFLAGS="$LDFLAGS $BOOST_LDFLAGS" + export LDFLAGS + + AC_CACHE_CHECK(whether the Boost::Thread library is available, + ax_cv_boost_thread, + [AC_LANG_PUSH([C++]) + CXXFLAGS_SAVE=$CXXFLAGS + + if test "x$host_os" = "xsolaris" ; then + CXXFLAGS="-pthreads $CXXFLAGS" + elif test "x$host_os" = "xmingw32" ; then + CXXFLAGS="-mthreads $CXXFLAGS" + else + CXXFLAGS="-pthread $CXXFLAGS" + fi + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[@%:@include ]], + [[boost::thread_group thrds; + return 0;]])], + ax_cv_boost_thread=yes, ax_cv_boost_thread=no) + CXXFLAGS=$CXXFLAGS_SAVE + AC_LANG_POP([C++]) + ]) + if test "x$ax_cv_boost_thread" = "xyes"; then + if test "x$host_os" = "xsolaris" ; then + BOOST_CPPFLAGS="-pthreads $BOOST_CPPFLAGS" + elif test "x$host_os" = "xmingw32" ; then + BOOST_CPPFLAGS="-mthreads $BOOST_CPPFLAGS" + else + BOOST_CPPFLAGS="-pthread $BOOST_CPPFLAGS" + fi + + AC_SUBST(BOOST_CPPFLAGS) + + AC_DEFINE(HAVE_BOOST_THREAD,,[define if the Boost::Thread library is available]) + BOOSTLIBDIR=`echo $BOOST_LDFLAGS | sed -e 's/@<:@^\/@:>@*//'` + + LDFLAGS_SAVE=$LDFLAGS + case "x$host_os" in + *bsd* ) + LDFLAGS="-pthread $LDFLAGS" + break; + ;; + esac + if test "x$ax_boost_user_thread_lib" = "x"; then + for libextension in `ls -r $BOOSTLIBDIR/libboost_thread* 2>/dev/null | sed 's,.*/lib,,' | sed 's,\..*,,'`; do + ax_lib=${libextension} + AC_CHECK_LIB($ax_lib, exit, + [BOOST_THREAD_LIB="-l$ax_lib"; AC_SUBST(BOOST_THREAD_LIB) link_thread="yes"; break], + [link_thread="no"]) + done + if test "x$link_thread" != "xyes"; then + for libextension in `ls -r $BOOSTLIBDIR/boost_thread* 2>/dev/null | sed 's,.*/,,' | sed 's,\..*,,'`; do + ax_lib=${libextension} + AC_CHECK_LIB($ax_lib, exit, + [BOOST_THREAD_LIB="-l$ax_lib"; AC_SUBST(BOOST_THREAD_LIB) link_thread="yes"; break], + [link_thread="no"]) + done + fi + + else + for ax_lib in $ax_boost_user_thread_lib boost_thread-$ax_boost_user_thread_lib; do + AC_CHECK_LIB($ax_lib, exit, + [BOOST_THREAD_LIB="-l$ax_lib"; AC_SUBST(BOOST_THREAD_LIB) link_thread="yes"; break], + [link_thread="no"]) + done + + fi + if test "x$ax_lib" = "x"; then + AC_MSG_ERROR(Could not find a version of the library!) + fi + if test "x$link_thread" = "xno"; then + AC_MSG_ERROR(Could not link against $ax_lib !) + else + case "x$host_os" in + *bsd* ) + BOOST_LDFLAGS="-pthread $BOOST_LDFLAGS" + break; + ;; + esac + + fi + fi + + CPPFLAGS="$CPPFLAGS_SAVED" + LDFLAGS="$LDFLAGS_SAVED" + fi +]) diff --git a/m4/ax_check_compile_flag.m4 b/m4/ax_check_compile_flag.m4 new file mode 100644 index 0000000..51df0c0 --- /dev/null +++ b/m4/ax_check_compile_flag.m4 @@ -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 +# Copyright (c) 2011 Maarten Bosmans +# +# 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 . +# +# 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/m4/ax_cxx_compile_stdcxx_11.m4 b/m4/ax_cxx_compile_stdcxx_11.m4 new file mode 100644 index 0000000..af37acd --- /dev/null +++ b/m4/ax_cxx_compile_stdcxx_11.m4 @@ -0,0 +1,133 @@ +# ============================================================================ +# http://www.gnu.org/software/autoconf-archive/ax_cxx_compile_stdcxx_11.html +# ============================================================================ +# +# SYNOPSIS +# +# AX_CXX_COMPILE_STDCXX_11([ext|noext],[mandatory|optional]) +# +# DESCRIPTION +# +# Check for baseline language coverage in the compiler for the C++11 +# standard; if necessary, add switches to CXXFLAGS to enable support. +# +# The first argument, if specified, indicates whether you insist on an +# extended mode (e.g. -std=gnu++11) or a strict conformance mode (e.g. +# -std=c++11). If neither is specified, you get whatever works, with +# preference for an extended mode. +# +# The second argument, if specified 'mandatory' or if left unspecified, +# indicates that baseline C++11 support is required and that the macro +# should error out if no mode with that support is found. If specified +# 'optional', then configuration proceeds regardless, after defining +# HAVE_CXX11 if and only if a supporting mode is found. +# +# LICENSE +# +# Copyright (c) 2008 Benjamin Kosnik +# Copyright (c) 2012 Zack Weinberg +# Copyright (c) 2013 Roy Stogner +# +# 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 any +# warranty. + +#serial 3 + +m4_define([_AX_CXX_COMPILE_STDCXX_11_testbody], [ + template + struct check + { + static_assert(sizeof(int) <= sizeof(T), "not big enough"); + }; + + typedef check> right_angle_brackets; + + int a; + decltype(a) b; + + typedef check check_type; + check_type c; + check_type&& cr = static_cast(c); + + auto d = a; +]) + +AC_DEFUN([AX_CXX_COMPILE_STDCXX_11], [dnl + m4_if([$1], [], [], + [$1], [ext], [], + [$1], [noext], [], + [m4_fatal([invalid argument `$1' to AX_CXX_COMPILE_STDCXX_11])])dnl + m4_if([$2], [], [ax_cxx_compile_cxx11_required=true], + [$2], [mandatory], [ax_cxx_compile_cxx11_required=true], + [$2], [optional], [ax_cxx_compile_cxx11_required=false], + [m4_fatal([invalid second argument `$2' to AX_CXX_COMPILE_STDCXX_11])])dnl + AC_LANG_PUSH([C++])dnl + ac_success=no + AC_CACHE_CHECK(whether $CXX supports C++11 features by default, + ax_cv_cxx_compile_cxx11, + [AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_11_testbody])], + [ax_cv_cxx_compile_cxx11=yes], + [ax_cv_cxx_compile_cxx11=no])]) + if test x$ax_cv_cxx_compile_cxx11 = xyes; then + ac_success=yes + fi + + m4_if([$1], [noext], [], [dnl + if test x$ac_success = xno; then + for switch in -std=gnu++11 -std=gnu++0x; do + cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx11_$switch]) + AC_CACHE_CHECK(whether $CXX supports C++11 features with $switch, + $cachevar, + [ac_save_CXXFLAGS="$CXXFLAGS" + CXXFLAGS="$CXXFLAGS $switch" + AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_11_testbody])], + [eval $cachevar=yes], + [eval $cachevar=no]) + CXXFLAGS="$ac_save_CXXFLAGS"]) + if eval test x\$$cachevar = xyes; then + CXXFLAGS="$CXXFLAGS $switch" + ac_success=yes + break + fi + done + fi]) + + m4_if([$1], [ext], [], [dnl + if test x$ac_success = xno; then + for switch in -std=c++11 -std=c++0x; do + cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx11_$switch]) + AC_CACHE_CHECK(whether $CXX supports C++11 features with $switch, + $cachevar, + [ac_save_CXXFLAGS="$CXXFLAGS" + CXXFLAGS="$CXXFLAGS $switch" + AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_11_testbody])], + [eval $cachevar=yes], + [eval $cachevar=no]) + CXXFLAGS="$ac_save_CXXFLAGS"]) + if eval test x\$$cachevar = xyes; then + CXXFLAGS="$CXXFLAGS $switch" + ac_success=yes + break + fi + done + fi]) + AC_LANG_POP([C++]) + if test x$ax_cxx_compile_cxx11_required = xtrue; then + if test x$ac_success = xno; then + AC_MSG_ERROR([*** A compiler with support for C++11 language features is required.]) + fi + else + if test x$ac_success = xno; then + HAVE_CXX11=0 + AC_MSG_NOTICE([No compiler with C++11 support was found]) + else + HAVE_CXX11=1 + AC_DEFINE(HAVE_CXX11,1, + [define if the compiler supports basic C++11 syntax]) + fi + + AC_SUBST(HAVE_CXX11) + fi +]) diff --git a/m4/ax_have_epoll.m4 b/m4/ax_have_epoll.m4 new file mode 100644 index 0000000..07ceb49 --- /dev/null +++ b/m4/ax_have_epoll.m4 @@ -0,0 +1,104 @@ +# =========================================================================== +# http://www.gnu.org/software/autoconf-archive/ax_have_epoll.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_HAVE_EPOLL([ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) +# AX_HAVE_EPOLL_PWAIT([ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) +# +# DESCRIPTION +# +# This macro determines whether the system supports the epoll I/O event +# interface. A neat usage example would be: +# +# AX_HAVE_EPOLL( +# [AX_CONFIG_FEATURE_ENABLE(epoll)], +# [AX_CONFIG_FEATURE_DISABLE(epoll)]) +# AX_CONFIG_FEATURE( +# [epoll], [This platform supports epoll(7)], +# [HAVE_EPOLL], [This platform supports epoll(7).]) +# +# The epoll interface was added to the Linux kernel in version 2.5.45, and +# the macro verifies that a kernel newer than this is installed. This +# check is somewhat unreliable if doesn't match the +# running kernel, but it is necessary regardless, because glibc comes with +# stubs for the epoll_create(), epoll_wait(), etc. that allow programs to +# compile and link even if the kernel is too old; the problem would then +# be detected only at runtime. +# +# Linux kernel version 2.6.19 adds the epoll_pwait() call in addition to +# epoll_wait(). The availability of that function can be tested with the +# second macro. Generally speaking, it is safe to assume that +# AX_HAVE_EPOLL would succeed if AX_HAVE_EPOLL_PWAIT has, but not the +# other way round. +# +# LICENSE +# +# Copyright (c) 2008 Peter Simons +# +# 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 any +# warranty. + +#serial 10 + +AC_DEFUN([AX_HAVE_EPOLL], [dnl + ax_have_epoll_cppflags="${CPPFLAGS}" + AC_CHECK_HEADER([linux/version.h], [CPPFLAGS="${CPPFLAGS} -DHAVE_LINUX_VERSION_H"]) + AC_MSG_CHECKING([for Linux epoll(7) interface]) + AC_CACHE_VAL([ax_cv_have_epoll], [dnl + AC_LINK_IFELSE([dnl + AC_LANG_PROGRAM([dnl +#include +#ifdef HAVE_LINUX_VERSION_H +# include +# if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,45) +# error linux kernel version is too old to have epoll +# endif +#endif +], [dnl +int fd, rc; +struct epoll_event ev; +fd = epoll_create(128); +rc = epoll_wait(fd, &ev, 1, 0);])], + [ax_cv_have_epoll=yes], + [ax_cv_have_epoll=no])]) + CPPFLAGS="${ax_have_epoll_cppflags}" + AS_IF([test "${ax_cv_have_epoll}" = "yes"], + [AC_MSG_RESULT([yes]) +$1],[AC_MSG_RESULT([no]) +$2]) +])dnl + +AC_DEFUN([AX_HAVE_EPOLL_PWAIT], [dnl + ax_have_epoll_cppflags="${CPPFLAGS}" + AC_CHECK_HEADER([linux/version.h], + [CPPFLAGS="${CPPFLAGS} -DHAVE_LINUX_VERSION_H"]) + AC_MSG_CHECKING([for Linux epoll(7) interface with signals extension]) + AC_CACHE_VAL([ax_cv_have_epoll_pwait], [dnl + AC_LINK_IFELSE([dnl + AC_LANG_PROGRAM([dnl +#ifdef HAVE_LINUX_VERSION_H +# include +# if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19) +# error linux kernel version is too old to have epoll_pwait +# endif +#endif +#include +#include +], [dnl +int fd, rc; +struct epoll_event ev; +fd = epoll_create(128); +rc = epoll_wait(fd, &ev, 1, 0); +rc = epoll_pwait(fd, &ev, 1, 0, (sigset_t const *)(0));])], + [ax_cv_have_epoll_pwait=yes], + [ax_cv_have_epoll_pwait=no])]) + CPPFLAGS="${ax_have_epoll_cppflags}" + AS_IF([test "${ax_cv_have_epoll_pwait}" = "yes"], + [AC_MSG_RESULT([yes]) +$1],[AC_MSG_RESULT([no]) +$2]) +])dnl diff --git a/m4/ax_python_devel.m4 b/m4/ax_python_devel.m4 new file mode 100644 index 0000000..067fbcf --- /dev/null +++ b/m4/ax_python_devel.m4 @@ -0,0 +1,344 @@ +# =========================================================================== +# http://www.gnu.org/software/autoconf-archive/ax_python_devel.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_PYTHON_DEVEL([version]) +# +# DESCRIPTION +# +# Note: Defines as a precious variable "PYTHON_VERSION". Don't override it +# in your configure.ac. +# +# This macro checks for Python and tries to get the include path to +# 'Python.h'. It provides the $(PYTHON_CPPFLAGS) and $(PYTHON_LDFLAGS) +# output variables. It also exports $(PYTHON_EXTRA_LIBS) and +# $(PYTHON_EXTRA_LDFLAGS) for embedding Python in your code. +# +# You can search for some particular version of Python by passing a +# parameter to this macro, for example ">= '2.3.1'", or "== '2.4'". Please +# note that you *have* to pass also an operator along with the version to +# match, and pay special attention to the single quotes surrounding the +# version number. Don't use "PYTHON_VERSION" for this: that environment +# variable is declared as precious and thus reserved for the end-user. +# +# This macro should work for all versions of Python >= 2.1.0. As an end +# user, you can disable the check for the python version by setting the +# PYTHON_NOVERSIONCHECK environment variable to something else than the +# empty string. +# +# If you need to use this macro for an older Python version, please +# contact the authors. We're always open for feedback. +# +# LICENSE +# +# Copyright (c) 2009 Sebastian Huber +# Copyright (c) 2009 Alan W. Irwin +# Copyright (c) 2009 Rafael Laboissiere +# Copyright (c) 2009 Andrew Collier +# Copyright (c) 2009 Matteo Settenvini +# Copyright (c) 2009 Horst Knorr +# Copyright (c) 2013 Daniel Mullner +# +# 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 . +# +# 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 16 + +AU_ALIAS([AC_PYTHON_DEVEL], [AX_PYTHON_DEVEL]) +AC_DEFUN([AX_PYTHON_DEVEL],[ + # + # Allow the use of a (user set) custom python version + # + AC_ARG_VAR([PYTHON_VERSION],[The installed Python + version to use, for example '2.3'. This string + will be appended to the Python interpreter + canonical name.]) + + AC_PATH_PROG([PYTHON],[python[$PYTHON_VERSION]]) + if test -z "$PYTHON"; then + AC_MSG_WARN([Cannot find python$PYTHON_VERSION in your system path]) + PYTHON_VERSION="" + no_python_devel=yes + fi + +AS_IF([test -z "$no_python_devel"], [ + # + # Check for a version of Python >= 2.1.0 + # + AC_MSG_CHECKING([for a version of Python >= '2.1.0']) + ac_supports_python_ver=`$PYTHON -c "import sys; \ + ver = sys.version.split ()[[0]]; \ + print (ver >= '2.1.0')"` + if test "$ac_supports_python_ver" != "True"; then + if test -z "$PYTHON_NOVERSIONCHECK"; then + AC_MSG_RESULT([no]) + AC_MSG_WARN([ +This version of the AC@&t@_PYTHON_DEVEL macro +doesn't work properly with versions of Python before +2.1.0. You may need to re-run configure, setting the +variables PYTHON_CPPFLAGS, PYTHON_LDFLAGS, PYTHON_SITE_PKG, +PYTHON_EXTRA_LIBS and PYTHON_EXTRA_LDFLAGS by hand. +Moreover, to disable this check, set PYTHON_NOVERSIONCHECK +to something else than an empty string. +]) + no_python_devel=yes + else + AC_MSG_RESULT([skip at user request]) + fi + else + AC_MSG_RESULT([yes]) + fi +]) # AS_IF + +AS_IF([test -z "$no_python_devel"], [ + # + # if the macro parameter ``version'' is set, honour it + # + if test -n "$1"; then + AC_MSG_CHECKING([for a version of Python $1]) + ac_supports_python_ver=`$PYTHON -c "import sys; \ + ver = sys.version.split ()[[0]]; \ + print (ver $1)"` + if test "$ac_supports_python_ver" = "True"; then + AC_MSG_RESULT([yes]) + else + AC_MSG_RESULT([no]) + AC_MSG_WARN([this package requires Python $1. +If you have it installed, but it isn't the default Python +interpreter in your system path, please pass the PYTHON_VERSION +variable to configure. See ``configure --help'' for reference. +]) + PYTHON_VERSION="" + no_python_devel=yes + fi + fi +]) # AS_IF + +AS_IF([test -z "$no_python_devel"], [ + # + # Check if you have distutils, else fail + # + AC_MSG_CHECKING([for the distutils Python package]) + ac_distutils_result=`$PYTHON -c "import distutils" 2>&1` + if test -z "$ac_distutils_result"; then + AC_MSG_RESULT([yes]) + else + AC_MSG_RESULT([no]) + AC_MSG_WARN([cannot import Python module "distutils". +Please check your Python installation. The error was: +$ac_distutils_result]) + PYTHON_VERSION="" + no_python_devel=yes + fi +]) # AS_IF + +AS_IF([test -z "$no_python_devel"], [ + # + # Check for Python include path + # + AC_MSG_CHECKING([for Python include path]) + if test -z "$PYTHON_CPPFLAGS"; then + python_path=`$PYTHON -c "import distutils.sysconfig; \ + print (distutils.sysconfig.get_python_inc ());"` + plat_python_path=`$PYTHON -c "import distutils.sysconfig; \ + print (distutils.sysconfig.get_python_inc (plat_specific=1));"` + if test -n "${python_path}"; then + if test "${plat_python_path}" != "${python_path}"; then + python_path="-I$python_path -I$plat_python_path" + else + python_path="-I$python_path" + fi + fi + PYTHON_CPPFLAGS=$python_path + fi + AC_MSG_RESULT([$PYTHON_CPPFLAGS]) + AC_SUBST([PYTHON_CPPFLAGS]) + + # + # Check for Python library path + # + AC_MSG_CHECKING([for Python library path]) + if test -z "$PYTHON_LDFLAGS"; then + # (makes two attempts to ensure we've got a version number + # from the interpreter) + ac_python_version=`cat<]], + [[Py_Initialize();]]) + ],[pythonexists=yes],[pythonexists=no]) + AC_LANG_POP([C]) + # turn back to default flags + CPPFLAGS="$ac_save_CPPFLAGS" + LIBS="$ac_save_LIBS" + + AC_MSG_RESULT([$pythonexists]) + + if test ! "x$pythonexists" = "xyes"; then + AC_MSG_WARN([ + Could not link test program to Python. Maybe the main Python library has been + installed in some non-standard library path. If so, pass it to configure, + via the LDFLAGS environment variable. + Example: ./configure LDFLAGS="-L/usr/non-standard-path/python/lib" + ============================================================================ + ERROR! + You probably have to install the development version of the Python package + for your distribution. The exact name of this package varies among them. + ============================================================================ + ]) + PYTHON_VERSION="" + no_python_devel=yes + fi + + # + # all done! + # +]) # AS_IF + +AS_IF([test -z "$no_python_devel"], + [have_python_dev=yes], [have_python_dev=no]) + +]) # AS_IF diff --git a/m4/libtool.m4 b/m4/libtool.m4 new file mode 100644 index 0000000..d7c043f --- /dev/null +++ b/m4/libtool.m4 @@ -0,0 +1,7997 @@ +# libtool.m4 - Configure libtool for the host system. -*-Autoconf-*- +# +# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2003, 2004, 2005, +# 2006, 2007, 2008, 2009, 2010, 2011 Free Software +# Foundation, Inc. +# Written by Gordon Matzigkeit, 1996 +# +# This file is free software; the Free Software Foundation gives +# unlimited permission to copy and/or distribute it, with or without +# modifications, as long as this notice is preserved. + +m4_define([_LT_COPYING], [dnl +# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2003, 2004, 2005, +# 2006, 2007, 2008, 2009, 2010, 2011 Free Software +# Foundation, Inc. +# Written by Gordon Matzigkeit, 1996 +# +# This file is part of GNU Libtool. +# +# GNU Libtool 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 2 of +# the License, or (at your option) any later version. +# +# As a special exception to the GNU General Public License, +# if you distribute this file as part of a program or library that +# is built using GNU Libtool, you may include this file under the +# same distribution terms that you use for the rest of that program. +# +# GNU Libtool 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 GNU Libtool; see the file COPYING. If not, a copy +# can be downloaded from http://www.gnu.org/licenses/gpl.html, or +# obtained by writing to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +]) + +# serial 57 LT_INIT + + +# LT_PREREQ(VERSION) +# ------------------ +# Complain and exit if this libtool version is less that VERSION. +m4_defun([LT_PREREQ], +[m4_if(m4_version_compare(m4_defn([LT_PACKAGE_VERSION]), [$1]), -1, + [m4_default([$3], + [m4_fatal([Libtool version $1 or higher is required], + 63)])], + [$2])]) + + +# _LT_CHECK_BUILDDIR +# ------------------ +# Complain if the absolute build directory name contains unusual characters +m4_defun([_LT_CHECK_BUILDDIR], +[case `pwd` in + *\ * | *\ *) + AC_MSG_WARN([Libtool does not cope well with whitespace in `pwd`]) ;; +esac +]) + + +# LT_INIT([OPTIONS]) +# ------------------ +AC_DEFUN([LT_INIT], +[AC_PREREQ([2.58])dnl We use AC_INCLUDES_DEFAULT +AC_REQUIRE([AC_CONFIG_AUX_DIR_DEFAULT])dnl +AC_BEFORE([$0], [LT_LANG])dnl +AC_BEFORE([$0], [LT_OUTPUT])dnl +AC_BEFORE([$0], [LTDL_INIT])dnl +m4_require([_LT_CHECK_BUILDDIR])dnl + +dnl Autoconf doesn't catch unexpanded LT_ macros by default: +m4_pattern_forbid([^_?LT_[A-Z_]+$])dnl +m4_pattern_allow([^(_LT_EOF|LT_DLGLOBAL|LT_DLLAZY_OR_NOW|LT_MULTI_MODULE)$])dnl +dnl aclocal doesn't pull ltoptions.m4, ltsugar.m4, or ltversion.m4 +dnl unless we require an AC_DEFUNed macro: +AC_REQUIRE([LTOPTIONS_VERSION])dnl +AC_REQUIRE([LTSUGAR_VERSION])dnl +AC_REQUIRE([LTVERSION_VERSION])dnl +AC_REQUIRE([LTOBSOLETE_VERSION])dnl +m4_require([_LT_PROG_LTMAIN])dnl + +_LT_SHELL_INIT([SHELL=${CONFIG_SHELL-/bin/sh}]) + +dnl Parse OPTIONS +_LT_SET_OPTIONS([$0], [$1]) + +# This can be used to rebuild libtool when needed +LIBTOOL_DEPS="$ltmain" + +# Always use our own libtool. +LIBTOOL='$(SHELL) $(top_builddir)/libtool' +AC_SUBST(LIBTOOL)dnl + +_LT_SETUP + +# Only expand once: +m4_define([LT_INIT]) +])# LT_INIT + +# Old names: +AU_ALIAS([AC_PROG_LIBTOOL], [LT_INIT]) +AU_ALIAS([AM_PROG_LIBTOOL], [LT_INIT]) +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AC_PROG_LIBTOOL], []) +dnl AC_DEFUN([AM_PROG_LIBTOOL], []) + + +# _LT_CC_BASENAME(CC) +# ------------------- +# Calculate cc_basename. Skip known compiler wrappers and cross-prefix. +m4_defun([_LT_CC_BASENAME], +[for cc_temp in $1""; do + case $cc_temp in + compile | *[[\\/]]compile | ccache | *[[\\/]]ccache ) ;; + distcc | *[[\\/]]distcc | purify | *[[\\/]]purify ) ;; + \-*) ;; + *) break;; + esac +done +cc_basename=`$ECHO "$cc_temp" | $SED "s%.*/%%; s%^$host_alias-%%"` +]) + + +# _LT_FILEUTILS_DEFAULTS +# ---------------------- +# It is okay to use these file commands and assume they have been set +# sensibly after `m4_require([_LT_FILEUTILS_DEFAULTS])'. +m4_defun([_LT_FILEUTILS_DEFAULTS], +[: ${CP="cp -f"} +: ${MV="mv -f"} +: ${RM="rm -f"} +])# _LT_FILEUTILS_DEFAULTS + + +# _LT_SETUP +# --------- +m4_defun([_LT_SETUP], +[AC_REQUIRE([AC_CANONICAL_HOST])dnl +AC_REQUIRE([AC_CANONICAL_BUILD])dnl +AC_REQUIRE([_LT_PREPARE_SED_QUOTE_VARS])dnl +AC_REQUIRE([_LT_PROG_ECHO_BACKSLASH])dnl + +_LT_DECL([], [PATH_SEPARATOR], [1], [The PATH separator for the build system])dnl +dnl +_LT_DECL([], [host_alias], [0], [The host system])dnl +_LT_DECL([], [host], [0])dnl +_LT_DECL([], [host_os], [0])dnl +dnl +_LT_DECL([], [build_alias], [0], [The build system])dnl +_LT_DECL([], [build], [0])dnl +_LT_DECL([], [build_os], [0])dnl +dnl +AC_REQUIRE([AC_PROG_CC])dnl +AC_REQUIRE([LT_PATH_LD])dnl +AC_REQUIRE([LT_PATH_NM])dnl +dnl +AC_REQUIRE([AC_PROG_LN_S])dnl +test -z "$LN_S" && LN_S="ln -s" +_LT_DECL([], [LN_S], [1], [Whether we need soft or hard links])dnl +dnl +AC_REQUIRE([LT_CMD_MAX_LEN])dnl +_LT_DECL([objext], [ac_objext], [0], [Object file suffix (normally "o")])dnl +_LT_DECL([], [exeext], [0], [Executable file suffix (normally "")])dnl +dnl +m4_require([_LT_FILEUTILS_DEFAULTS])dnl +m4_require([_LT_CHECK_SHELL_FEATURES])dnl +m4_require([_LT_PATH_CONVERSION_FUNCTIONS])dnl +m4_require([_LT_CMD_RELOAD])dnl +m4_require([_LT_CHECK_MAGIC_METHOD])dnl +m4_require([_LT_CHECK_SHAREDLIB_FROM_LINKLIB])dnl +m4_require([_LT_CMD_OLD_ARCHIVE])dnl +m4_require([_LT_CMD_GLOBAL_SYMBOLS])dnl +m4_require([_LT_WITH_SYSROOT])dnl + +_LT_CONFIG_LIBTOOL_INIT([ +# See if we are running on zsh, and set the options which allow our +# commands through without removal of \ escapes INIT. +if test -n "\${ZSH_VERSION+set}" ; then + setopt NO_GLOB_SUBST +fi +]) +if test -n "${ZSH_VERSION+set}" ; then + setopt NO_GLOB_SUBST +fi + +_LT_CHECK_OBJDIR + +m4_require([_LT_TAG_COMPILER])dnl + +case $host_os in +aix3*) + # AIX sometimes has problems with the GCC collect2 program. For some + # reason, if we set the COLLECT_NAMES environment variable, the problems + # vanish in a puff of smoke. + if test "X${COLLECT_NAMES+set}" != Xset; then + COLLECT_NAMES= + export COLLECT_NAMES + fi + ;; +esac + +# Global variables: +ofile=libtool +can_build_shared=yes + +# All known linkers require a `.a' archive for static linking (except MSVC, +# which needs '.lib'). +libext=a + +with_gnu_ld="$lt_cv_prog_gnu_ld" + +old_CC="$CC" +old_CFLAGS="$CFLAGS" + +# Set sane defaults for various variables +test -z "$CC" && CC=cc +test -z "$LTCC" && LTCC=$CC +test -z "$LTCFLAGS" && LTCFLAGS=$CFLAGS +test -z "$LD" && LD=ld +test -z "$ac_objext" && ac_objext=o + +_LT_CC_BASENAME([$compiler]) + +# Only perform the check for file, if the check method requires it +test -z "$MAGIC_CMD" && MAGIC_CMD=file +case $deplibs_check_method in +file_magic*) + if test "$file_magic_cmd" = '$MAGIC_CMD'; then + _LT_PATH_MAGIC + fi + ;; +esac + +# Use C for the default configuration in the libtool script +LT_SUPPORTED_TAG([CC]) +_LT_LANG_C_CONFIG +_LT_LANG_DEFAULT_CONFIG +_LT_CONFIG_COMMANDS +])# _LT_SETUP + + +# _LT_PREPARE_SED_QUOTE_VARS +# -------------------------- +# Define a few sed substitution that help us do robust quoting. +m4_defun([_LT_PREPARE_SED_QUOTE_VARS], +[# Backslashify metacharacters that are still active within +# double-quoted strings. +sed_quote_subst='s/\([["`$\\]]\)/\\\1/g' + +# Same as above, but do not quote variable references. +double_quote_subst='s/\([["`\\]]\)/\\\1/g' + +# Sed substitution to delay expansion of an escaped shell variable in a +# double_quote_subst'ed string. +delay_variable_subst='s/\\\\\\\\\\\$/\\\\\\$/g' + +# Sed substitution to delay expansion of an escaped single quote. +delay_single_quote_subst='s/'\''/'\'\\\\\\\'\''/g' + +# Sed substitution to avoid accidental globbing in evaled expressions +no_glob_subst='s/\*/\\\*/g' +]) + +# _LT_PROG_LTMAIN +# --------------- +# Note that this code is called both from `configure', and `config.status' +# now that we use AC_CONFIG_COMMANDS to generate libtool. Notably, +# `config.status' has no value for ac_aux_dir unless we are using Automake, +# so we pass a copy along to make sure it has a sensible value anyway. +m4_defun([_LT_PROG_LTMAIN], +[m4_ifdef([AC_REQUIRE_AUX_FILE], [AC_REQUIRE_AUX_FILE([ltmain.sh])])dnl +_LT_CONFIG_LIBTOOL_INIT([ac_aux_dir='$ac_aux_dir']) +ltmain="$ac_aux_dir/ltmain.sh" +])# _LT_PROG_LTMAIN + + +## ------------------------------------- ## +## Accumulate code for creating libtool. ## +## ------------------------------------- ## + +# So that we can recreate a full libtool script including additional +# tags, we accumulate the chunks of code to send to AC_CONFIG_COMMANDS +# in macros and then make a single call at the end using the `libtool' +# label. + + +# _LT_CONFIG_LIBTOOL_INIT([INIT-COMMANDS]) +# ---------------------------------------- +# Register INIT-COMMANDS to be passed to AC_CONFIG_COMMANDS later. +m4_define([_LT_CONFIG_LIBTOOL_INIT], +[m4_ifval([$1], + [m4_append([_LT_OUTPUT_LIBTOOL_INIT], + [$1 +])])]) + +# Initialize. +m4_define([_LT_OUTPUT_LIBTOOL_INIT]) + + +# _LT_CONFIG_LIBTOOL([COMMANDS]) +# ------------------------------ +# Register COMMANDS to be passed to AC_CONFIG_COMMANDS later. +m4_define([_LT_CONFIG_LIBTOOL], +[m4_ifval([$1], + [m4_append([_LT_OUTPUT_LIBTOOL_COMMANDS], + [$1 +])])]) + +# Initialize. +m4_define([_LT_OUTPUT_LIBTOOL_COMMANDS]) + + +# _LT_CONFIG_SAVE_COMMANDS([COMMANDS], [INIT_COMMANDS]) +# ----------------------------------------------------- +m4_defun([_LT_CONFIG_SAVE_COMMANDS], +[_LT_CONFIG_LIBTOOL([$1]) +_LT_CONFIG_LIBTOOL_INIT([$2]) +]) + + +# _LT_FORMAT_COMMENT([COMMENT]) +# ----------------------------- +# Add leading comment marks to the start of each line, and a trailing +# full-stop to the whole comment if one is not present already. +m4_define([_LT_FORMAT_COMMENT], +[m4_ifval([$1], [ +m4_bpatsubst([m4_bpatsubst([$1], [^ *], [# ])], + [['`$\]], [\\\&])]m4_bmatch([$1], [[!?.]$], [], [.]) +)]) + + + +## ------------------------ ## +## FIXME: Eliminate VARNAME ## +## ------------------------ ## + + +# _LT_DECL([CONFIGNAME], VARNAME, VALUE, [DESCRIPTION], [IS-TAGGED?]) +# ------------------------------------------------------------------- +# CONFIGNAME is the name given to the value in the libtool script. +# VARNAME is the (base) name used in the configure script. +# VALUE may be 0, 1 or 2 for a computed quote escaped value based on +# VARNAME. Any other value will be used directly. +m4_define([_LT_DECL], +[lt_if_append_uniq([lt_decl_varnames], [$2], [, ], + [lt_dict_add_subkey([lt_decl_dict], [$2], [libtool_name], + [m4_ifval([$1], [$1], [$2])]) + lt_dict_add_subkey([lt_decl_dict], [$2], [value], [$3]) + m4_ifval([$4], + [lt_dict_add_subkey([lt_decl_dict], [$2], [description], [$4])]) + lt_dict_add_subkey([lt_decl_dict], [$2], + [tagged?], [m4_ifval([$5], [yes], [no])])]) +]) + + +# _LT_TAGDECL([CONFIGNAME], VARNAME, VALUE, [DESCRIPTION]) +# -------------------------------------------------------- +m4_define([_LT_TAGDECL], [_LT_DECL([$1], [$2], [$3], [$4], [yes])]) + + +# lt_decl_tag_varnames([SEPARATOR], [VARNAME1...]) +# ------------------------------------------------ +m4_define([lt_decl_tag_varnames], +[_lt_decl_filter([tagged?], [yes], $@)]) + + +# _lt_decl_filter(SUBKEY, VALUE, [SEPARATOR], [VARNAME1..]) +# --------------------------------------------------------- +m4_define([_lt_decl_filter], +[m4_case([$#], + [0], [m4_fatal([$0: too few arguments: $#])], + [1], [m4_fatal([$0: too few arguments: $#: $1])], + [2], [lt_dict_filter([lt_decl_dict], [$1], [$2], [], lt_decl_varnames)], + [3], [lt_dict_filter([lt_decl_dict], [$1], [$2], [$3], lt_decl_varnames)], + [lt_dict_filter([lt_decl_dict], $@)])[]dnl +]) + + +# lt_decl_quote_varnames([SEPARATOR], [VARNAME1...]) +# -------------------------------------------------- +m4_define([lt_decl_quote_varnames], +[_lt_decl_filter([value], [1], $@)]) + + +# lt_decl_dquote_varnames([SEPARATOR], [VARNAME1...]) +# --------------------------------------------------- +m4_define([lt_decl_dquote_varnames], +[_lt_decl_filter([value], [2], $@)]) + + +# lt_decl_varnames_tagged([SEPARATOR], [VARNAME1...]) +# --------------------------------------------------- +m4_define([lt_decl_varnames_tagged], +[m4_assert([$# <= 2])dnl +_$0(m4_quote(m4_default([$1], [[, ]])), + m4_ifval([$2], [[$2]], [m4_dquote(lt_decl_tag_varnames)]), + m4_split(m4_normalize(m4_quote(_LT_TAGS)), [ ]))]) +m4_define([_lt_decl_varnames_tagged], +[m4_ifval([$3], [lt_combine([$1], [$2], [_], $3)])]) + + +# lt_decl_all_varnames([SEPARATOR], [VARNAME1...]) +# ------------------------------------------------ +m4_define([lt_decl_all_varnames], +[_$0(m4_quote(m4_default([$1], [[, ]])), + m4_if([$2], [], + m4_quote(lt_decl_varnames), + m4_quote(m4_shift($@))))[]dnl +]) +m4_define([_lt_decl_all_varnames], +[lt_join($@, lt_decl_varnames_tagged([$1], + lt_decl_tag_varnames([[, ]], m4_shift($@))))dnl +]) + + +# _LT_CONFIG_STATUS_DECLARE([VARNAME]) +# ------------------------------------ +# Quote a variable value, and forward it to `config.status' so that its +# declaration there will have the same value as in `configure'. VARNAME +# must have a single quote delimited value for this to work. +m4_define([_LT_CONFIG_STATUS_DECLARE], +[$1='`$ECHO "$][$1" | $SED "$delay_single_quote_subst"`']) + + +# _LT_CONFIG_STATUS_DECLARATIONS +# ------------------------------ +# We delimit libtool config variables with single quotes, so when +# we write them to config.status, we have to be sure to quote all +# embedded single quotes properly. In configure, this macro expands +# each variable declared with _LT_DECL (and _LT_TAGDECL) into: +# +# ='`$ECHO "$" | $SED "$delay_single_quote_subst"`' +m4_defun([_LT_CONFIG_STATUS_DECLARATIONS], +[m4_foreach([_lt_var], m4_quote(lt_decl_all_varnames), + [m4_n([_LT_CONFIG_STATUS_DECLARE(_lt_var)])])]) + + +# _LT_LIBTOOL_TAGS +# ---------------- +# Output comment and list of tags supported by the script +m4_defun([_LT_LIBTOOL_TAGS], +[_LT_FORMAT_COMMENT([The names of the tagged configurations supported by this script])dnl +available_tags="_LT_TAGS"dnl +]) + + +# _LT_LIBTOOL_DECLARE(VARNAME, [TAG]) +# ----------------------------------- +# Extract the dictionary values for VARNAME (optionally with TAG) and +# expand to a commented shell variable setting: +# +# # Some comment about what VAR is for. +# visible_name=$lt_internal_name +m4_define([_LT_LIBTOOL_DECLARE], +[_LT_FORMAT_COMMENT(m4_quote(lt_dict_fetch([lt_decl_dict], [$1], + [description])))[]dnl +m4_pushdef([_libtool_name], + m4_quote(lt_dict_fetch([lt_decl_dict], [$1], [libtool_name])))[]dnl +m4_case(m4_quote(lt_dict_fetch([lt_decl_dict], [$1], [value])), + [0], [_libtool_name=[$]$1], + [1], [_libtool_name=$lt_[]$1], + [2], [_libtool_name=$lt_[]$1], + [_libtool_name=lt_dict_fetch([lt_decl_dict], [$1], [value])])[]dnl +m4_ifval([$2], [_$2])[]m4_popdef([_libtool_name])[]dnl +]) + + +# _LT_LIBTOOL_CONFIG_VARS +# ----------------------- +# Produce commented declarations of non-tagged libtool config variables +# suitable for insertion in the LIBTOOL CONFIG section of the `libtool' +# script. Tagged libtool config variables (even for the LIBTOOL CONFIG +# section) are produced by _LT_LIBTOOL_TAG_VARS. +m4_defun([_LT_LIBTOOL_CONFIG_VARS], +[m4_foreach([_lt_var], + m4_quote(_lt_decl_filter([tagged?], [no], [], lt_decl_varnames)), + [m4_n([_LT_LIBTOOL_DECLARE(_lt_var)])])]) + + +# _LT_LIBTOOL_TAG_VARS(TAG) +# ------------------------- +m4_define([_LT_LIBTOOL_TAG_VARS], +[m4_foreach([_lt_var], m4_quote(lt_decl_tag_varnames), + [m4_n([_LT_LIBTOOL_DECLARE(_lt_var, [$1])])])]) + + +# _LT_TAGVAR(VARNAME, [TAGNAME]) +# ------------------------------ +m4_define([_LT_TAGVAR], [m4_ifval([$2], [$1_$2], [$1])]) + + +# _LT_CONFIG_COMMANDS +# ------------------- +# Send accumulated output to $CONFIG_STATUS. Thanks to the lists of +# variables for single and double quote escaping we saved from calls +# to _LT_DECL, we can put quote escaped variables declarations +# into `config.status', and then the shell code to quote escape them in +# for loops in `config.status'. Finally, any additional code accumulated +# from calls to _LT_CONFIG_LIBTOOL_INIT is expanded. +m4_defun([_LT_CONFIG_COMMANDS], +[AC_PROVIDE_IFELSE([LT_OUTPUT], + dnl If the libtool generation code has been placed in $CONFIG_LT, + dnl instead of duplicating it all over again into config.status, + dnl then we will have config.status run $CONFIG_LT later, so it + dnl needs to know what name is stored there: + [AC_CONFIG_COMMANDS([libtool], + [$SHELL $CONFIG_LT || AS_EXIT(1)], [CONFIG_LT='$CONFIG_LT'])], + dnl If the libtool generation code is destined for config.status, + dnl expand the accumulated commands and init code now: + [AC_CONFIG_COMMANDS([libtool], + [_LT_OUTPUT_LIBTOOL_COMMANDS], [_LT_OUTPUT_LIBTOOL_COMMANDS_INIT])]) +])#_LT_CONFIG_COMMANDS + + +# Initialize. +m4_define([_LT_OUTPUT_LIBTOOL_COMMANDS_INIT], +[ + +# The HP-UX ksh and POSIX shell print the target directory to stdout +# if CDPATH is set. +(unset CDPATH) >/dev/null 2>&1 && unset CDPATH + +sed_quote_subst='$sed_quote_subst' +double_quote_subst='$double_quote_subst' +delay_variable_subst='$delay_variable_subst' +_LT_CONFIG_STATUS_DECLARATIONS +LTCC='$LTCC' +LTCFLAGS='$LTCFLAGS' +compiler='$compiler_DEFAULT' + +# A function that is used when there is no print builtin or printf. +func_fallback_echo () +{ + eval 'cat <<_LTECHO_EOF +\$[]1 +_LTECHO_EOF' +} + +# Quote evaled strings. +for var in lt_decl_all_varnames([[ \ +]], lt_decl_quote_varnames); do + case \`eval \\\\\$ECHO \\\\""\\\\\$\$var"\\\\"\` in + *[[\\\\\\\`\\"\\\$]]*) + eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"\\\$\$var\\" | \\\$SED \\"\\\$sed_quote_subst\\"\\\`\\\\\\"" + ;; + *) + eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\"" + ;; + esac +done + +# Double-quote double-evaled strings. +for var in lt_decl_all_varnames([[ \ +]], lt_decl_dquote_varnames); do + case \`eval \\\\\$ECHO \\\\""\\\\\$\$var"\\\\"\` in + *[[\\\\\\\`\\"\\\$]]*) + eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"\\\$\$var\\" | \\\$SED -e \\"\\\$double_quote_subst\\" -e \\"\\\$sed_quote_subst\\" -e \\"\\\$delay_variable_subst\\"\\\`\\\\\\"" + ;; + *) + eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\"" + ;; + esac +done + +_LT_OUTPUT_LIBTOOL_INIT +]) + +# _LT_GENERATED_FILE_INIT(FILE, [COMMENT]) +# ------------------------------------ +# Generate a child script FILE with all initialization necessary to +# reuse the environment learned by the parent script, and make the +# file executable. If COMMENT is supplied, it is inserted after the +# `#!' sequence but before initialization text begins. After this +# macro, additional text can be appended to FILE to form the body of +# the child script. The macro ends with non-zero status if the +# file could not be fully written (such as if the disk is full). +m4_ifdef([AS_INIT_GENERATED], +[m4_defun([_LT_GENERATED_FILE_INIT],[AS_INIT_GENERATED($@)])], +[m4_defun([_LT_GENERATED_FILE_INIT], +[m4_require([AS_PREPARE])]dnl +[m4_pushdef([AS_MESSAGE_LOG_FD])]dnl +[lt_write_fail=0 +cat >$1 <<_ASEOF || lt_write_fail=1 +#! $SHELL +# Generated by $as_me. +$2 +SHELL=\${CONFIG_SHELL-$SHELL} +export SHELL +_ASEOF +cat >>$1 <<\_ASEOF || lt_write_fail=1 +AS_SHELL_SANITIZE +_AS_PREPARE +exec AS_MESSAGE_FD>&1 +_ASEOF +test $lt_write_fail = 0 && chmod +x $1[]dnl +m4_popdef([AS_MESSAGE_LOG_FD])])])# _LT_GENERATED_FILE_INIT + +# LT_OUTPUT +# --------- +# This macro allows early generation of the libtool script (before +# AC_OUTPUT is called), incase it is used in configure for compilation +# tests. +AC_DEFUN([LT_OUTPUT], +[: ${CONFIG_LT=./config.lt} +AC_MSG_NOTICE([creating $CONFIG_LT]) +_LT_GENERATED_FILE_INIT(["$CONFIG_LT"], +[# Run this file to recreate a libtool stub with the current configuration.]) + +cat >>"$CONFIG_LT" <<\_LTEOF +lt_cl_silent=false +exec AS_MESSAGE_LOG_FD>>config.log +{ + echo + AS_BOX([Running $as_me.]) +} >&AS_MESSAGE_LOG_FD + +lt_cl_help="\ +\`$as_me' creates a local libtool stub from the current configuration, +for use in further configure time tests before the real libtool is +generated. + +Usage: $[0] [[OPTIONS]] + + -h, --help print this help, then exit + -V, --version print version number, then exit + -q, --quiet do not print progress messages + -d, --debug don't remove temporary files + +Report bugs to ." + +lt_cl_version="\ +m4_ifset([AC_PACKAGE_NAME], [AC_PACKAGE_NAME ])config.lt[]dnl +m4_ifset([AC_PACKAGE_VERSION], [ AC_PACKAGE_VERSION]) +configured by $[0], generated by m4_PACKAGE_STRING. + +Copyright (C) 2011 Free Software Foundation, Inc. +This config.lt script is free software; the Free Software Foundation +gives unlimited permision to copy, distribute and modify it." + +while test $[#] != 0 +do + case $[1] in + --version | --v* | -V ) + echo "$lt_cl_version"; exit 0 ;; + --help | --h* | -h ) + echo "$lt_cl_help"; exit 0 ;; + --debug | --d* | -d ) + debug=: ;; + --quiet | --q* | --silent | --s* | -q ) + lt_cl_silent=: ;; + + -*) AC_MSG_ERROR([unrecognized option: $[1] +Try \`$[0] --help' for more information.]) ;; + + *) AC_MSG_ERROR([unrecognized argument: $[1] +Try \`$[0] --help' for more information.]) ;; + esac + shift +done + +if $lt_cl_silent; then + exec AS_MESSAGE_FD>/dev/null +fi +_LTEOF + +cat >>"$CONFIG_LT" <<_LTEOF +_LT_OUTPUT_LIBTOOL_COMMANDS_INIT +_LTEOF + +cat >>"$CONFIG_LT" <<\_LTEOF +AC_MSG_NOTICE([creating $ofile]) +_LT_OUTPUT_LIBTOOL_COMMANDS +AS_EXIT(0) +_LTEOF +chmod +x "$CONFIG_LT" + +# configure is writing to config.log, but config.lt does its own redirection, +# appending to config.log, which fails on DOS, as config.log is still kept +# open by configure. Here we exec the FD to /dev/null, effectively closing +# config.log, so it can be properly (re)opened and appended to by config.lt. +lt_cl_success=: +test "$silent" = yes && + lt_config_lt_args="$lt_config_lt_args --quiet" +exec AS_MESSAGE_LOG_FD>/dev/null +$SHELL "$CONFIG_LT" $lt_config_lt_args || lt_cl_success=false +exec AS_MESSAGE_LOG_FD>>config.log +$lt_cl_success || AS_EXIT(1) +])# LT_OUTPUT + + +# _LT_CONFIG(TAG) +# --------------- +# If TAG is the built-in tag, create an initial libtool script with a +# default configuration from the untagged config vars. Otherwise add code +# to config.status for appending the configuration named by TAG from the +# matching tagged config vars. +m4_defun([_LT_CONFIG], +[m4_require([_LT_FILEUTILS_DEFAULTS])dnl +_LT_CONFIG_SAVE_COMMANDS([ + m4_define([_LT_TAG], m4_if([$1], [], [C], [$1]))dnl + m4_if(_LT_TAG, [C], [ + # See if we are running on zsh, and set the options which allow our + # commands through without removal of \ escapes. + if test -n "${ZSH_VERSION+set}" ; then + setopt NO_GLOB_SUBST + fi + + cfgfile="${ofile}T" + trap "$RM \"$cfgfile\"; exit 1" 1 2 15 + $RM "$cfgfile" + + cat <<_LT_EOF >> "$cfgfile" +#! $SHELL + +# `$ECHO "$ofile" | sed 's%^.*/%%'` - Provide generalized library-building support services. +# Generated automatically by $as_me ($PACKAGE$TIMESTAMP) $VERSION +# Libtool was configured on host `(hostname || uname -n) 2>/dev/null | sed 1q`: +# NOTE: Changes made to this file will be lost: look at ltmain.sh. +# +_LT_COPYING +_LT_LIBTOOL_TAGS + +# ### BEGIN LIBTOOL CONFIG +_LT_LIBTOOL_CONFIG_VARS +_LT_LIBTOOL_TAG_VARS +# ### END LIBTOOL CONFIG + +_LT_EOF + + case $host_os in + aix3*) + cat <<\_LT_EOF >> "$cfgfile" +# AIX sometimes has problems with the GCC collect2 program. For some +# reason, if we set the COLLECT_NAMES environment variable, the problems +# vanish in a puff of smoke. +if test "X${COLLECT_NAMES+set}" != Xset; then + COLLECT_NAMES= + export COLLECT_NAMES +fi +_LT_EOF + ;; + esac + + _LT_PROG_LTMAIN + + # We use sed instead of cat because bash on DJGPP gets confused if + # if finds mixed CR/LF and LF-only lines. Since sed operates in + # text mode, it properly converts lines to CR/LF. This bash problem + # is reportedly fixed, but why not run on old versions too? + sed '$q' "$ltmain" >> "$cfgfile" \ + || (rm -f "$cfgfile"; exit 1) + + _LT_PROG_REPLACE_SHELLFNS + + mv -f "$cfgfile" "$ofile" || + (rm -f "$ofile" && cp "$cfgfile" "$ofile" && rm -f "$cfgfile") + chmod +x "$ofile" +], +[cat <<_LT_EOF >> "$ofile" + +dnl Unfortunately we have to use $1 here, since _LT_TAG is not expanded +dnl in a comment (ie after a #). +# ### BEGIN LIBTOOL TAG CONFIG: $1 +_LT_LIBTOOL_TAG_VARS(_LT_TAG) +# ### END LIBTOOL TAG CONFIG: $1 +_LT_EOF +])dnl /m4_if +], +[m4_if([$1], [], [ + PACKAGE='$PACKAGE' + VERSION='$VERSION' + TIMESTAMP='$TIMESTAMP' + RM='$RM' + ofile='$ofile'], []) +])dnl /_LT_CONFIG_SAVE_COMMANDS +])# _LT_CONFIG + + +# LT_SUPPORTED_TAG(TAG) +# --------------------- +# Trace this macro to discover what tags are supported by the libtool +# --tag option, using: +# autoconf --trace 'LT_SUPPORTED_TAG:$1' +AC_DEFUN([LT_SUPPORTED_TAG], []) + + +# C support is built-in for now +m4_define([_LT_LANG_C_enabled], []) +m4_define([_LT_TAGS], []) + + +# LT_LANG(LANG) +# ------------- +# Enable libtool support for the given language if not already enabled. +AC_DEFUN([LT_LANG], +[AC_BEFORE([$0], [LT_OUTPUT])dnl +m4_case([$1], + [C], [_LT_LANG(C)], + [C++], [_LT_LANG(CXX)], + [Go], [_LT_LANG(GO)], + [Java], [_LT_LANG(GCJ)], + [Fortran 77], [_LT_LANG(F77)], + [Fortran], [_LT_LANG(FC)], + [Windows Resource], [_LT_LANG(RC)], + [m4_ifdef([_LT_LANG_]$1[_CONFIG], + [_LT_LANG($1)], + [m4_fatal([$0: unsupported language: "$1"])])])dnl +])# LT_LANG + + +# _LT_LANG(LANGNAME) +# ------------------ +m4_defun([_LT_LANG], +[m4_ifdef([_LT_LANG_]$1[_enabled], [], + [LT_SUPPORTED_TAG([$1])dnl + m4_append([_LT_TAGS], [$1 ])dnl + m4_define([_LT_LANG_]$1[_enabled], [])dnl + _LT_LANG_$1_CONFIG($1)])dnl +])# _LT_LANG + + +m4_ifndef([AC_PROG_GO], [ +############################################################ +# NOTE: This macro has been submitted for inclusion into # +# GNU Autoconf as AC_PROG_GO. When it is available in # +# a released version of Autoconf we should remove this # +# macro and use it instead. # +############################################################ +m4_defun([AC_PROG_GO], +[AC_LANG_PUSH(Go)dnl +AC_ARG_VAR([GOC], [Go compiler command])dnl +AC_ARG_VAR([GOFLAGS], [Go compiler flags])dnl +_AC_ARG_VAR_LDFLAGS()dnl +AC_CHECK_TOOL(GOC, gccgo) +if test -z "$GOC"; then + if test -n "$ac_tool_prefix"; then + AC_CHECK_PROG(GOC, [${ac_tool_prefix}gccgo], [${ac_tool_prefix}gccgo]) + fi +fi +if test -z "$GOC"; then + AC_CHECK_PROG(GOC, gccgo, gccgo, false) +fi +])#m4_defun +])#m4_ifndef + + +# _LT_LANG_DEFAULT_CONFIG +# ----------------------- +m4_defun([_LT_LANG_DEFAULT_CONFIG], +[AC_PROVIDE_IFELSE([AC_PROG_CXX], + [LT_LANG(CXX)], + [m4_define([AC_PROG_CXX], defn([AC_PROG_CXX])[LT_LANG(CXX)])]) + +AC_PROVIDE_IFELSE([AC_PROG_F77], + [LT_LANG(F77)], + [m4_define([AC_PROG_F77], defn([AC_PROG_F77])[LT_LANG(F77)])]) + +AC_PROVIDE_IFELSE([AC_PROG_FC], + [LT_LANG(FC)], + [m4_define([AC_PROG_FC], defn([AC_PROG_FC])[LT_LANG(FC)])]) + +dnl The call to [A][M_PROG_GCJ] is quoted like that to stop aclocal +dnl pulling things in needlessly. +AC_PROVIDE_IFELSE([AC_PROG_GCJ], + [LT_LANG(GCJ)], + [AC_PROVIDE_IFELSE([A][M_PROG_GCJ], + [LT_LANG(GCJ)], + [AC_PROVIDE_IFELSE([LT_PROG_GCJ], + [LT_LANG(GCJ)], + [m4_ifdef([AC_PROG_GCJ], + [m4_define([AC_PROG_GCJ], defn([AC_PROG_GCJ])[LT_LANG(GCJ)])]) + m4_ifdef([A][M_PROG_GCJ], + [m4_define([A][M_PROG_GCJ], defn([A][M_PROG_GCJ])[LT_LANG(GCJ)])]) + m4_ifdef([LT_PROG_GCJ], + [m4_define([LT_PROG_GCJ], defn([LT_PROG_GCJ])[LT_LANG(GCJ)])])])])]) + +AC_PROVIDE_IFELSE([AC_PROG_GO], + [LT_LANG(GO)], + [m4_define([AC_PROG_GO], defn([AC_PROG_GO])[LT_LANG(GO)])]) + +AC_PROVIDE_IFELSE([LT_PROG_RC], + [LT_LANG(RC)], + [m4_define([LT_PROG_RC], defn([LT_PROG_RC])[LT_LANG(RC)])]) +])# _LT_LANG_DEFAULT_CONFIG + +# Obsolete macros: +AU_DEFUN([AC_LIBTOOL_CXX], [LT_LANG(C++)]) +AU_DEFUN([AC_LIBTOOL_F77], [LT_LANG(Fortran 77)]) +AU_DEFUN([AC_LIBTOOL_FC], [LT_LANG(Fortran)]) +AU_DEFUN([AC_LIBTOOL_GCJ], [LT_LANG(Java)]) +AU_DEFUN([AC_LIBTOOL_RC], [LT_LANG(Windows Resource)]) +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AC_LIBTOOL_CXX], []) +dnl AC_DEFUN([AC_LIBTOOL_F77], []) +dnl AC_DEFUN([AC_LIBTOOL_FC], []) +dnl AC_DEFUN([AC_LIBTOOL_GCJ], []) +dnl AC_DEFUN([AC_LIBTOOL_RC], []) + + +# _LT_TAG_COMPILER +# ---------------- +m4_defun([_LT_TAG_COMPILER], +[AC_REQUIRE([AC_PROG_CC])dnl + +_LT_DECL([LTCC], [CC], [1], [A C compiler])dnl +_LT_DECL([LTCFLAGS], [CFLAGS], [1], [LTCC compiler flags])dnl +_LT_TAGDECL([CC], [compiler], [1], [A language specific compiler])dnl +_LT_TAGDECL([with_gcc], [GCC], [0], [Is the compiler the GNU compiler?])dnl + +# If no C compiler was specified, use CC. +LTCC=${LTCC-"$CC"} + +# If no C compiler flags were specified, use CFLAGS. +LTCFLAGS=${LTCFLAGS-"$CFLAGS"} + +# Allow CC to be a program name with arguments. +compiler=$CC +])# _LT_TAG_COMPILER + + +# _LT_COMPILER_BOILERPLATE +# ------------------------ +# Check for compiler boilerplate output or warnings with +# the simple compiler test code. +m4_defun([_LT_COMPILER_BOILERPLATE], +[m4_require([_LT_DECL_SED])dnl +ac_outfile=conftest.$ac_objext +echo "$lt_simple_compile_test_code" >conftest.$ac_ext +eval "$ac_compile" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err +_lt_compiler_boilerplate=`cat conftest.err` +$RM conftest* +])# _LT_COMPILER_BOILERPLATE + + +# _LT_LINKER_BOILERPLATE +# ---------------------- +# Check for linker boilerplate output or warnings with +# the simple link test code. +m4_defun([_LT_LINKER_BOILERPLATE], +[m4_require([_LT_DECL_SED])dnl +ac_outfile=conftest.$ac_objext +echo "$lt_simple_link_test_code" >conftest.$ac_ext +eval "$ac_link" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err +_lt_linker_boilerplate=`cat conftest.err` +$RM -r conftest* +])# _LT_LINKER_BOILERPLATE + +# _LT_REQUIRED_DARWIN_CHECKS +# ------------------------- +m4_defun_once([_LT_REQUIRED_DARWIN_CHECKS],[ + case $host_os in + rhapsody* | darwin*) + AC_CHECK_TOOL([DSYMUTIL], [dsymutil], [:]) + AC_CHECK_TOOL([NMEDIT], [nmedit], [:]) + AC_CHECK_TOOL([LIPO], [lipo], [:]) + AC_CHECK_TOOL([OTOOL], [otool], [:]) + AC_CHECK_TOOL([OTOOL64], [otool64], [:]) + _LT_DECL([], [DSYMUTIL], [1], + [Tool to manipulate archived DWARF debug symbol files on Mac OS X]) + _LT_DECL([], [NMEDIT], [1], + [Tool to change global to local symbols on Mac OS X]) + _LT_DECL([], [LIPO], [1], + [Tool to manipulate fat objects and archives on Mac OS X]) + _LT_DECL([], [OTOOL], [1], + [ldd/readelf like tool for Mach-O binaries on Mac OS X]) + _LT_DECL([], [OTOOL64], [1], + [ldd/readelf like tool for 64 bit Mach-O binaries on Mac OS X 10.4]) + + AC_CACHE_CHECK([for -single_module linker flag],[lt_cv_apple_cc_single_mod], + [lt_cv_apple_cc_single_mod=no + if test -z "${LT_MULTI_MODULE}"; then + # By default we will add the -single_module flag. You can override + # by either setting the environment variable LT_MULTI_MODULE + # non-empty at configure time, or by adding -multi_module to the + # link flags. + rm -rf libconftest.dylib* + echo "int foo(void){return 1;}" > conftest.c + echo "$LTCC $LTCFLAGS $LDFLAGS -o libconftest.dylib \ +-dynamiclib -Wl,-single_module conftest.c" >&AS_MESSAGE_LOG_FD + $LTCC $LTCFLAGS $LDFLAGS -o libconftest.dylib \ + -dynamiclib -Wl,-single_module conftest.c 2>conftest.err + _lt_result=$? + # If there is a non-empty error log, and "single_module" + # appears in it, assume the flag caused a linker warning + if test -s conftest.err && $GREP single_module conftest.err; then + cat conftest.err >&AS_MESSAGE_LOG_FD + # Otherwise, if the output was created with a 0 exit code from + # the compiler, it worked. + elif test -f libconftest.dylib && test $_lt_result -eq 0; then + lt_cv_apple_cc_single_mod=yes + else + cat conftest.err >&AS_MESSAGE_LOG_FD + fi + rm -rf libconftest.dylib* + rm -f conftest.* + fi]) + + AC_CACHE_CHECK([for -exported_symbols_list linker flag], + [lt_cv_ld_exported_symbols_list], + [lt_cv_ld_exported_symbols_list=no + save_LDFLAGS=$LDFLAGS + echo "_main" > conftest.sym + LDFLAGS="$LDFLAGS -Wl,-exported_symbols_list,conftest.sym" + AC_LINK_IFELSE([AC_LANG_PROGRAM([],[])], + [lt_cv_ld_exported_symbols_list=yes], + [lt_cv_ld_exported_symbols_list=no]) + LDFLAGS="$save_LDFLAGS" + ]) + + AC_CACHE_CHECK([for -force_load linker flag],[lt_cv_ld_force_load], + [lt_cv_ld_force_load=no + cat > conftest.c << _LT_EOF +int forced_loaded() { return 2;} +_LT_EOF + echo "$LTCC $LTCFLAGS -c -o conftest.o conftest.c" >&AS_MESSAGE_LOG_FD + $LTCC $LTCFLAGS -c -o conftest.o conftest.c 2>&AS_MESSAGE_LOG_FD + echo "$AR cru libconftest.a conftest.o" >&AS_MESSAGE_LOG_FD + $AR cru libconftest.a conftest.o 2>&AS_MESSAGE_LOG_FD + echo "$RANLIB libconftest.a" >&AS_MESSAGE_LOG_FD + $RANLIB libconftest.a 2>&AS_MESSAGE_LOG_FD + cat > conftest.c << _LT_EOF +int main() { return 0;} +_LT_EOF + echo "$LTCC $LTCFLAGS $LDFLAGS -o conftest conftest.c -Wl,-force_load,./libconftest.a" >&AS_MESSAGE_LOG_FD + $LTCC $LTCFLAGS $LDFLAGS -o conftest conftest.c -Wl,-force_load,./libconftest.a 2>conftest.err + _lt_result=$? + if test -s conftest.err && $GREP force_load conftest.err; then + cat conftest.err >&AS_MESSAGE_LOG_FD + elif test -f conftest && test $_lt_result -eq 0 && $GREP forced_load conftest >/dev/null 2>&1 ; then + lt_cv_ld_force_load=yes + else + cat conftest.err >&AS_MESSAGE_LOG_FD + fi + rm -f conftest.err libconftest.a conftest conftest.c + rm -rf conftest.dSYM + ]) + case $host_os in + rhapsody* | darwin1.[[012]]) + _lt_dar_allow_undefined='${wl}-undefined ${wl}suppress' ;; + darwin1.*) + _lt_dar_allow_undefined='${wl}-flat_namespace ${wl}-undefined ${wl}suppress' ;; + darwin*) # darwin 5.x on + # if running on 10.5 or later, the deployment target defaults + # to the OS version, if on x86, and 10.4, the deployment + # target defaults to 10.4. Don't you love it? + case ${MACOSX_DEPLOYMENT_TARGET-10.0},$host in + 10.0,*86*-darwin8*|10.0,*-darwin[[91]]*) + _lt_dar_allow_undefined='${wl}-undefined ${wl}dynamic_lookup' ;; + 10.[[012]]*) + _lt_dar_allow_undefined='${wl}-flat_namespace ${wl}-undefined ${wl}suppress' ;; + 10.*) + _lt_dar_allow_undefined='${wl}-undefined ${wl}dynamic_lookup' ;; + esac + ;; + esac + if test "$lt_cv_apple_cc_single_mod" = "yes"; then + _lt_dar_single_mod='$single_module' + fi + if test "$lt_cv_ld_exported_symbols_list" = "yes"; then + _lt_dar_export_syms=' ${wl}-exported_symbols_list,$output_objdir/${libname}-symbols.expsym' + else + _lt_dar_export_syms='~$NMEDIT -s $output_objdir/${libname}-symbols.expsym ${lib}' + fi + if test "$DSYMUTIL" != ":" && test "$lt_cv_ld_force_load" = "no"; then + _lt_dsymutil='~$DSYMUTIL $lib || :' + else + _lt_dsymutil= + fi + ;; + esac +]) + + +# _LT_DARWIN_LINKER_FEATURES([TAG]) +# --------------------------------- +# Checks for linker and compiler features on darwin +m4_defun([_LT_DARWIN_LINKER_FEATURES], +[ + m4_require([_LT_REQUIRED_DARWIN_CHECKS]) + _LT_TAGVAR(archive_cmds_need_lc, $1)=no + _LT_TAGVAR(hardcode_direct, $1)=no + _LT_TAGVAR(hardcode_automatic, $1)=yes + _LT_TAGVAR(hardcode_shlibpath_var, $1)=unsupported + if test "$lt_cv_ld_force_load" = "yes"; then + _LT_TAGVAR(whole_archive_flag_spec, $1)='`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience ${wl}-force_load,$conv\"; done; func_echo_all \"$new_convenience\"`' + m4_case([$1], [F77], [_LT_TAGVAR(compiler_needs_object, $1)=yes], + [FC], [_LT_TAGVAR(compiler_needs_object, $1)=yes]) + else + _LT_TAGVAR(whole_archive_flag_spec, $1)='' + fi + _LT_TAGVAR(link_all_deplibs, $1)=yes + _LT_TAGVAR(allow_undefined_flag, $1)="$_lt_dar_allow_undefined" + case $cc_basename in + ifort*) _lt_dar_can_shared=yes ;; + *) _lt_dar_can_shared=$GCC ;; + esac + if test "$_lt_dar_can_shared" = "yes"; then + output_verbose_link_cmd=func_echo_all + _LT_TAGVAR(archive_cmds, $1)="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod${_lt_dsymutil}" + _LT_TAGVAR(module_cmds, $1)="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dsymutil}" + _LT_TAGVAR(archive_expsym_cmds, $1)="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring ${_lt_dar_single_mod}${_lt_dar_export_syms}${_lt_dsymutil}" + _LT_TAGVAR(module_expsym_cmds, $1)="sed -e 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dar_export_syms}${_lt_dsymutil}" + m4_if([$1], [CXX], +[ if test "$lt_cv_apple_cc_single_mod" != "yes"; then + _LT_TAGVAR(archive_cmds, $1)="\$CC -r -keep_private_externs -nostdlib -o \${lib}-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \${lib}-master.o \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring${_lt_dsymutil}" + _LT_TAGVAR(archive_expsym_cmds, $1)="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -r -keep_private_externs -nostdlib -o \${lib}-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \${lib}-master.o \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring${_lt_dar_export_syms}${_lt_dsymutil}" + fi +],[]) + else + _LT_TAGVAR(ld_shlibs, $1)=no + fi +]) + +# _LT_SYS_MODULE_PATH_AIX([TAGNAME]) +# ---------------------------------- +# Links a minimal program and checks the executable +# for the system default hardcoded library path. In most cases, +# this is /usr/lib:/lib, but when the MPI compilers are used +# the location of the communication and MPI libs are included too. +# If we don't find anything, use the default library path according +# to the aix ld manual. +# Store the results from the different compilers for each TAGNAME. +# Allow to override them for all tags through lt_cv_aix_libpath. +m4_defun([_LT_SYS_MODULE_PATH_AIX], +[m4_require([_LT_DECL_SED])dnl +if test "${lt_cv_aix_libpath+set}" = set; then + aix_libpath=$lt_cv_aix_libpath +else + AC_CACHE_VAL([_LT_TAGVAR([lt_cv_aix_libpath_], [$1])], + [AC_LINK_IFELSE([AC_LANG_PROGRAM],[ + lt_aix_libpath_sed='[ + /Import File Strings/,/^$/ { + /^0/ { + s/^0 *\([^ ]*\) *$/\1/ + p + } + }]' + _LT_TAGVAR([lt_cv_aix_libpath_], [$1])=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` + # Check for a 64-bit object if we didn't find anything. + if test -z "$_LT_TAGVAR([lt_cv_aix_libpath_], [$1])"; then + _LT_TAGVAR([lt_cv_aix_libpath_], [$1])=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` + fi],[]) + if test -z "$_LT_TAGVAR([lt_cv_aix_libpath_], [$1])"; then + _LT_TAGVAR([lt_cv_aix_libpath_], [$1])="/usr/lib:/lib" + fi + ]) + aix_libpath=$_LT_TAGVAR([lt_cv_aix_libpath_], [$1]) +fi +])# _LT_SYS_MODULE_PATH_AIX + + +# _LT_SHELL_INIT(ARG) +# ------------------- +m4_define([_LT_SHELL_INIT], +[m4_divert_text([M4SH-INIT], [$1 +])])# _LT_SHELL_INIT + + + +# _LT_PROG_ECHO_BACKSLASH +# ----------------------- +# Find how we can fake an echo command that does not interpret backslash. +# In particular, with Autoconf 2.60 or later we add some code to the start +# of the generated configure script which will find a shell with a builtin +# printf (which we can use as an echo command). +m4_defun([_LT_PROG_ECHO_BACKSLASH], +[ECHO='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' +ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO +ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO$ECHO + +AC_MSG_CHECKING([how to print strings]) +# Test print first, because it will be a builtin if present. +if test "X`( print -r -- -n ) 2>/dev/null`" = X-n && \ + test "X`print -r -- $ECHO 2>/dev/null`" = "X$ECHO"; then + ECHO='print -r --' +elif test "X`printf %s $ECHO 2>/dev/null`" = "X$ECHO"; then + ECHO='printf %s\n' +else + # Use this function as a fallback that always works. + func_fallback_echo () + { + eval 'cat <<_LTECHO_EOF +$[]1 +_LTECHO_EOF' + } + ECHO='func_fallback_echo' +fi + +# func_echo_all arg... +# Invoke $ECHO with all args, space-separated. +func_echo_all () +{ + $ECHO "$*" +} + +case "$ECHO" in + printf*) AC_MSG_RESULT([printf]) ;; + print*) AC_MSG_RESULT([print -r]) ;; + *) AC_MSG_RESULT([cat]) ;; +esac + +m4_ifdef([_AS_DETECT_SUGGESTED], +[_AS_DETECT_SUGGESTED([ + test -n "${ZSH_VERSION+set}${BASH_VERSION+set}" || ( + ECHO='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' + ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO + ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO$ECHO + PATH=/empty FPATH=/empty; export PATH FPATH + test "X`printf %s $ECHO`" = "X$ECHO" \ + || test "X`print -r -- $ECHO`" = "X$ECHO" )])]) + +_LT_DECL([], [SHELL], [1], [Shell to use when invoking shell scripts]) +_LT_DECL([], [ECHO], [1], [An echo program that protects backslashes]) +])# _LT_PROG_ECHO_BACKSLASH + + +# _LT_WITH_SYSROOT +# ---------------- +AC_DEFUN([_LT_WITH_SYSROOT], +[AC_MSG_CHECKING([for sysroot]) +AC_ARG_WITH([sysroot], +[ --with-sysroot[=DIR] Search for dependent libraries within DIR + (or the compiler's sysroot if not specified).], +[], [with_sysroot=no]) + +dnl lt_sysroot will always be passed unquoted. We quote it here +dnl in case the user passed a directory name. +lt_sysroot= +case ${with_sysroot} in #( + yes) + if test "$GCC" = yes; then + lt_sysroot=`$CC --print-sysroot 2>/dev/null` + fi + ;; #( + /*) + lt_sysroot=`echo "$with_sysroot" | sed -e "$sed_quote_subst"` + ;; #( + no|'') + ;; #( + *) + AC_MSG_RESULT([${with_sysroot}]) + AC_MSG_ERROR([The sysroot must be an absolute path.]) + ;; +esac + + AC_MSG_RESULT([${lt_sysroot:-no}]) +_LT_DECL([], [lt_sysroot], [0], [The root where to search for ]dnl +[dependent libraries, and in which our libraries should be installed.])]) + +# _LT_ENABLE_LOCK +# --------------- +m4_defun([_LT_ENABLE_LOCK], +[AC_ARG_ENABLE([libtool-lock], + [AS_HELP_STRING([--disable-libtool-lock], + [avoid locking (might break parallel builds)])]) +test "x$enable_libtool_lock" != xno && enable_libtool_lock=yes + +# Some flags need to be propagated to the compiler or linker for good +# libtool support. +case $host in +ia64-*-hpux*) + # Find out which ABI we are using. + echo 'int i;' > conftest.$ac_ext + if AC_TRY_EVAL(ac_compile); then + case `/usr/bin/file conftest.$ac_objext` in + *ELF-32*) + HPUX_IA64_MODE="32" + ;; + *ELF-64*) + HPUX_IA64_MODE="64" + ;; + esac + fi + rm -rf conftest* + ;; +*-*-irix6*) + # Find out which ABI we are using. + echo '[#]line '$LINENO' "configure"' > conftest.$ac_ext + if AC_TRY_EVAL(ac_compile); then + if test "$lt_cv_prog_gnu_ld" = yes; then + case `/usr/bin/file conftest.$ac_objext` in + *32-bit*) + LD="${LD-ld} -melf32bsmip" + ;; + *N32*) + LD="${LD-ld} -melf32bmipn32" + ;; + *64-bit*) + LD="${LD-ld} -melf64bmip" + ;; + esac + else + case `/usr/bin/file conftest.$ac_objext` in + *32-bit*) + LD="${LD-ld} -32" + ;; + *N32*) + LD="${LD-ld} -n32" + ;; + *64-bit*) + LD="${LD-ld} -64" + ;; + esac + fi + fi + rm -rf conftest* + ;; + +x86_64-*kfreebsd*-gnu|x86_64-*linux*|powerpc*-*linux*| \ +s390*-*linux*|s390*-*tpf*|sparc*-*linux*) + # Find out which ABI we are using. + echo 'int i;' > conftest.$ac_ext + if AC_TRY_EVAL(ac_compile); then + case `/usr/bin/file conftest.o` in + *32-bit*) + case $host in + x86_64-*kfreebsd*-gnu) + LD="${LD-ld} -m elf_i386_fbsd" + ;; + x86_64-*linux*) + case `/usr/bin/file conftest.o` in + *x86-64*) + LD="${LD-ld} -m elf32_x86_64" + ;; + *) + LD="${LD-ld} -m elf_i386" + ;; + esac + ;; + powerpc64le-*) + LD="${LD-ld} -m elf32lppclinux" + ;; + powerpc64-*) + LD="${LD-ld} -m elf32ppclinux" + ;; + s390x-*linux*) + LD="${LD-ld} -m elf_s390" + ;; + sparc64-*linux*) + LD="${LD-ld} -m elf32_sparc" + ;; + esac + ;; + *64-bit*) + case $host in + x86_64-*kfreebsd*-gnu) + LD="${LD-ld} -m elf_x86_64_fbsd" + ;; + x86_64-*linux*) + LD="${LD-ld} -m elf_x86_64" + ;; + powerpcle-*) + LD="${LD-ld} -m elf64lppc" + ;; + powerpc-*) + LD="${LD-ld} -m elf64ppc" + ;; + s390*-*linux*|s390*-*tpf*) + LD="${LD-ld} -m elf64_s390" + ;; + sparc*-*linux*) + LD="${LD-ld} -m elf64_sparc" + ;; + esac + ;; + esac + fi + rm -rf conftest* + ;; + +*-*-sco3.2v5*) + # On SCO OpenServer 5, we need -belf to get full-featured binaries. + SAVE_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS -belf" + AC_CACHE_CHECK([whether the C compiler needs -belf], lt_cv_cc_needs_belf, + [AC_LANG_PUSH(C) + AC_LINK_IFELSE([AC_LANG_PROGRAM([[]],[[]])],[lt_cv_cc_needs_belf=yes],[lt_cv_cc_needs_belf=no]) + AC_LANG_POP]) + if test x"$lt_cv_cc_needs_belf" != x"yes"; then + # this is probably gcc 2.8.0, egcs 1.0 or newer; no need for -belf + CFLAGS="$SAVE_CFLAGS" + fi + ;; +*-*solaris*) + # Find out which ABI we are using. + echo 'int i;' > conftest.$ac_ext + if AC_TRY_EVAL(ac_compile); then + case `/usr/bin/file conftest.o` in + *64-bit*) + case $lt_cv_prog_gnu_ld in + yes*) + case $host in + i?86-*-solaris*) + LD="${LD-ld} -m elf_x86_64" + ;; + sparc*-*-solaris*) + LD="${LD-ld} -m elf64_sparc" + ;; + esac + # GNU ld 2.21 introduced _sol2 emulations. Use them if available. + if ${LD-ld} -V | grep _sol2 >/dev/null 2>&1; then + LD="${LD-ld}_sol2" + fi + ;; + *) + if ${LD-ld} -64 -r -o conftest2.o conftest.o >/dev/null 2>&1; then + LD="${LD-ld} -64" + fi + ;; + esac + ;; + esac + fi + rm -rf conftest* + ;; +esac + +need_locks="$enable_libtool_lock" +])# _LT_ENABLE_LOCK + + +# _LT_PROG_AR +# ----------- +m4_defun([_LT_PROG_AR], +[AC_CHECK_TOOLS(AR, [ar], false) +: ${AR=ar} +: ${AR_FLAGS=cru} +_LT_DECL([], [AR], [1], [The archiver]) +_LT_DECL([], [AR_FLAGS], [1], [Flags to create an archive]) + +AC_CACHE_CHECK([for archiver @FILE support], [lt_cv_ar_at_file], + [lt_cv_ar_at_file=no + AC_COMPILE_IFELSE([AC_LANG_PROGRAM], + [echo conftest.$ac_objext > conftest.lst + lt_ar_try='$AR $AR_FLAGS libconftest.a @conftest.lst >&AS_MESSAGE_LOG_FD' + AC_TRY_EVAL([lt_ar_try]) + if test "$ac_status" -eq 0; then + # Ensure the archiver fails upon bogus file names. + rm -f conftest.$ac_objext libconftest.a + AC_TRY_EVAL([lt_ar_try]) + if test "$ac_status" -ne 0; then + lt_cv_ar_at_file=@ + fi + fi + rm -f conftest.* libconftest.a + ]) + ]) + +if test "x$lt_cv_ar_at_file" = xno; then + archiver_list_spec= +else + archiver_list_spec=$lt_cv_ar_at_file +fi +_LT_DECL([], [archiver_list_spec], [1], + [How to feed a file listing to the archiver]) +])# _LT_PROG_AR + + +# _LT_CMD_OLD_ARCHIVE +# ------------------- +m4_defun([_LT_CMD_OLD_ARCHIVE], +[_LT_PROG_AR + +AC_CHECK_TOOL(STRIP, strip, :) +test -z "$STRIP" && STRIP=: +_LT_DECL([], [STRIP], [1], [A symbol stripping program]) + +AC_CHECK_TOOL(RANLIB, ranlib, :) +test -z "$RANLIB" && RANLIB=: +_LT_DECL([], [RANLIB], [1], + [Commands used to install an old-style archive]) + +# Determine commands to create old-style static archives. +old_archive_cmds='$AR $AR_FLAGS $oldlib$oldobjs' +old_postinstall_cmds='chmod 644 $oldlib' +old_postuninstall_cmds= + +if test -n "$RANLIB"; then + case $host_os in + openbsd*) + old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB -t \$tool_oldlib" + ;; + *) + old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB \$tool_oldlib" + ;; + esac + old_archive_cmds="$old_archive_cmds~\$RANLIB \$tool_oldlib" +fi + +case $host_os in + darwin*) + lock_old_archive_extraction=yes ;; + *) + lock_old_archive_extraction=no ;; +esac +_LT_DECL([], [old_postinstall_cmds], [2]) +_LT_DECL([], [old_postuninstall_cmds], [2]) +_LT_TAGDECL([], [old_archive_cmds], [2], + [Commands used to build an old-style archive]) +_LT_DECL([], [lock_old_archive_extraction], [0], + [Whether to use a lock for old archive extraction]) +])# _LT_CMD_OLD_ARCHIVE + + +# _LT_COMPILER_OPTION(MESSAGE, VARIABLE-NAME, FLAGS, +# [OUTPUT-FILE], [ACTION-SUCCESS], [ACTION-FAILURE]) +# ---------------------------------------------------------------- +# Check whether the given compiler option works +AC_DEFUN([_LT_COMPILER_OPTION], +[m4_require([_LT_FILEUTILS_DEFAULTS])dnl +m4_require([_LT_DECL_SED])dnl +AC_CACHE_CHECK([$1], [$2], + [$2=no + m4_if([$4], , [ac_outfile=conftest.$ac_objext], [ac_outfile=$4]) + echo "$lt_simple_compile_test_code" > conftest.$ac_ext + lt_compiler_flag="$3" + # Insert the option either (1) after the last *FLAGS variable, or + # (2) before a word containing "conftest.", or (3) at the end. + # Note that $ac_compile itself does not contain backslashes and begins + # with a dollar sign (not a hyphen), so the echo should work correctly. + # The option is referenced via a variable to avoid confusing sed. + lt_compile=`echo "$ac_compile" | $SED \ + -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ + -e 's: [[^ ]]*conftest\.: $lt_compiler_flag&:; t' \ + -e 's:$: $lt_compiler_flag:'` + (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&AS_MESSAGE_LOG_FD) + (eval "$lt_compile" 2>conftest.err) + ac_status=$? + cat conftest.err >&AS_MESSAGE_LOG_FD + echo "$as_me:$LINENO: \$? = $ac_status" >&AS_MESSAGE_LOG_FD + if (exit $ac_status) && test -s "$ac_outfile"; then + # The compiler can only warn and ignore the option if not recognized + # So say no if there are warnings other than the usual output. + $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' >conftest.exp + $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 + if test ! -s conftest.er2 || diff conftest.exp conftest.er2 >/dev/null; then + $2=yes + fi + fi + $RM conftest* +]) + +if test x"[$]$2" = xyes; then + m4_if([$5], , :, [$5]) +else + m4_if([$6], , :, [$6]) +fi +])# _LT_COMPILER_OPTION + +# Old name: +AU_ALIAS([AC_LIBTOOL_COMPILER_OPTION], [_LT_COMPILER_OPTION]) +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AC_LIBTOOL_COMPILER_OPTION], []) + + +# _LT_LINKER_OPTION(MESSAGE, VARIABLE-NAME, FLAGS, +# [ACTION-SUCCESS], [ACTION-FAILURE]) +# ---------------------------------------------------- +# Check whether the given linker option works +AC_DEFUN([_LT_LINKER_OPTION], +[m4_require([_LT_FILEUTILS_DEFAULTS])dnl +m4_require([_LT_DECL_SED])dnl +AC_CACHE_CHECK([$1], [$2], + [$2=no + save_LDFLAGS="$LDFLAGS" + LDFLAGS="$LDFLAGS $3" + echo "$lt_simple_link_test_code" > conftest.$ac_ext + if (eval $ac_link 2>conftest.err) && test -s conftest$ac_exeext; then + # The linker can only warn and ignore the option if not recognized + # So say no if there are warnings + if test -s conftest.err; then + # Append any errors to the config.log. + cat conftest.err 1>&AS_MESSAGE_LOG_FD + $ECHO "$_lt_linker_boilerplate" | $SED '/^$/d' > conftest.exp + $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 + if diff conftest.exp conftest.er2 >/dev/null; then + $2=yes + fi + else + $2=yes + fi + fi + $RM -r conftest* + LDFLAGS="$save_LDFLAGS" +]) + +if test x"[$]$2" = xyes; then + m4_if([$4], , :, [$4]) +else + m4_if([$5], , :, [$5]) +fi +])# _LT_LINKER_OPTION + +# Old name: +AU_ALIAS([AC_LIBTOOL_LINKER_OPTION], [_LT_LINKER_OPTION]) +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AC_LIBTOOL_LINKER_OPTION], []) + + +# LT_CMD_MAX_LEN +#--------------- +AC_DEFUN([LT_CMD_MAX_LEN], +[AC_REQUIRE([AC_CANONICAL_HOST])dnl +# find the maximum length of command line arguments +AC_MSG_CHECKING([the maximum length of command line arguments]) +AC_CACHE_VAL([lt_cv_sys_max_cmd_len], [dnl + i=0 + teststring="ABCD" + + case $build_os in + msdosdjgpp*) + # On DJGPP, this test can blow up pretty badly due to problems in libc + # (any single argument exceeding 2000 bytes causes a buffer overrun + # during glob expansion). Even if it were fixed, the result of this + # check would be larger than it should be. + lt_cv_sys_max_cmd_len=12288; # 12K is about right + ;; + + gnu*) + # Under GNU Hurd, this test is not required because there is + # no limit to the length of command line arguments. + # Libtool will interpret -1 as no limit whatsoever + lt_cv_sys_max_cmd_len=-1; + ;; + + cygwin* | mingw* | cegcc*) + # On Win9x/ME, this test blows up -- it succeeds, but takes + # about 5 minutes as the teststring grows exponentially. + # Worse, since 9x/ME are not pre-emptively multitasking, + # you end up with a "frozen" computer, even though with patience + # the test eventually succeeds (with a max line length of 256k). + # Instead, let's just punt: use the minimum linelength reported by + # all of the supported platforms: 8192 (on NT/2K/XP). + lt_cv_sys_max_cmd_len=8192; + ;; + + mint*) + # On MiNT this can take a long time and run out of memory. + lt_cv_sys_max_cmd_len=8192; + ;; + + amigaos*) + # On AmigaOS with pdksh, this test takes hours, literally. + # So we just punt and use a minimum line length of 8192. + lt_cv_sys_max_cmd_len=8192; + ;; + + netbsd* | freebsd* | openbsd* | darwin* | dragonfly*) + # This has been around since 386BSD, at least. Likely further. + if test -x /sbin/sysctl; then + lt_cv_sys_max_cmd_len=`/sbin/sysctl -n kern.argmax` + elif test -x /usr/sbin/sysctl; then + lt_cv_sys_max_cmd_len=`/usr/sbin/sysctl -n kern.argmax` + else + lt_cv_sys_max_cmd_len=65536 # usable default for all BSDs + fi + # And add a safety zone + lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4` + lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3` + ;; + + interix*) + # We know the value 262144 and hardcode it with a safety zone (like BSD) + lt_cv_sys_max_cmd_len=196608 + ;; + + os2*) + # The test takes a long time on OS/2. + lt_cv_sys_max_cmd_len=8192 + ;; + + osf*) + # Dr. Hans Ekkehard Plesser reports seeing a kernel panic running configure + # due to this test when exec_disable_arg_limit is 1 on Tru64. It is not + # nice to cause kernel panics so lets avoid the loop below. + # First set a reasonable default. + lt_cv_sys_max_cmd_len=16384 + # + if test -x /sbin/sysconfig; then + case `/sbin/sysconfig -q proc exec_disable_arg_limit` in + *1*) lt_cv_sys_max_cmd_len=-1 ;; + esac + fi + ;; + sco3.2v5*) + lt_cv_sys_max_cmd_len=102400 + ;; + sysv5* | sco5v6* | sysv4.2uw2*) + kargmax=`grep ARG_MAX /etc/conf/cf.d/stune 2>/dev/null` + if test -n "$kargmax"; then + lt_cv_sys_max_cmd_len=`echo $kargmax | sed 's/.*[[ ]]//'` + else + lt_cv_sys_max_cmd_len=32768 + fi + ;; + *) + lt_cv_sys_max_cmd_len=`(getconf ARG_MAX) 2> /dev/null` + if test -n "$lt_cv_sys_max_cmd_len" && \ + test undefined != "$lt_cv_sys_max_cmd_len"; then + lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4` + lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3` + else + # Make teststring a little bigger before we do anything with it. + # a 1K string should be a reasonable start. + for i in 1 2 3 4 5 6 7 8 ; do + teststring=$teststring$teststring + done + SHELL=${SHELL-${CONFIG_SHELL-/bin/sh}} + # If test is not a shell built-in, we'll probably end up computing a + # maximum length that is only half of the actual maximum length, but + # we can't tell. + while { test "X"`env echo "$teststring$teststring" 2>/dev/null` \ + = "X$teststring$teststring"; } >/dev/null 2>&1 && + test $i != 17 # 1/2 MB should be enough + do + i=`expr $i + 1` + teststring=$teststring$teststring + done + # Only check the string length outside the loop. + lt_cv_sys_max_cmd_len=`expr "X$teststring" : ".*" 2>&1` + teststring= + # Add a significant safety factor because C++ compilers can tack on + # massive amounts of additional arguments before passing them to the + # linker. It appears as though 1/2 is a usable value. + lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 2` + fi + ;; + esac +]) +if test -n $lt_cv_sys_max_cmd_len ; then + AC_MSG_RESULT($lt_cv_sys_max_cmd_len) +else + AC_MSG_RESULT(none) +fi +max_cmd_len=$lt_cv_sys_max_cmd_len +_LT_DECL([], [max_cmd_len], [0], + [What is the maximum length of a command?]) +])# LT_CMD_MAX_LEN + +# Old name: +AU_ALIAS([AC_LIBTOOL_SYS_MAX_CMD_LEN], [LT_CMD_MAX_LEN]) +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AC_LIBTOOL_SYS_MAX_CMD_LEN], []) + + +# _LT_HEADER_DLFCN +# ---------------- +m4_defun([_LT_HEADER_DLFCN], +[AC_CHECK_HEADERS([dlfcn.h], [], [], [AC_INCLUDES_DEFAULT])dnl +])# _LT_HEADER_DLFCN + + +# _LT_TRY_DLOPEN_SELF (ACTION-IF-TRUE, ACTION-IF-TRUE-W-USCORE, +# ACTION-IF-FALSE, ACTION-IF-CROSS-COMPILING) +# ---------------------------------------------------------------- +m4_defun([_LT_TRY_DLOPEN_SELF], +[m4_require([_LT_HEADER_DLFCN])dnl +if test "$cross_compiling" = yes; then : + [$4] +else + lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 + lt_status=$lt_dlunknown + cat > conftest.$ac_ext <<_LT_EOF +[#line $LINENO "configure" +#include "confdefs.h" + +#if HAVE_DLFCN_H +#include +#endif + +#include + +#ifdef RTLD_GLOBAL +# define LT_DLGLOBAL RTLD_GLOBAL +#else +# ifdef DL_GLOBAL +# define LT_DLGLOBAL DL_GLOBAL +# else +# define LT_DLGLOBAL 0 +# endif +#endif + +/* We may have to define LT_DLLAZY_OR_NOW in the command line if we + find out it does not work in some platform. */ +#ifndef LT_DLLAZY_OR_NOW +# ifdef RTLD_LAZY +# define LT_DLLAZY_OR_NOW RTLD_LAZY +# else +# ifdef DL_LAZY +# define LT_DLLAZY_OR_NOW DL_LAZY +# else +# ifdef RTLD_NOW +# define LT_DLLAZY_OR_NOW RTLD_NOW +# else +# ifdef DL_NOW +# define LT_DLLAZY_OR_NOW DL_NOW +# else +# define LT_DLLAZY_OR_NOW 0 +# endif +# endif +# endif +# endif +#endif + +/* When -fvisbility=hidden is used, assume the code has been annotated + correspondingly for the symbols needed. */ +#if defined(__GNUC__) && (((__GNUC__ == 3) && (__GNUC_MINOR__ >= 3)) || (__GNUC__ > 3)) +int fnord () __attribute__((visibility("default"))); +#endif + +int fnord () { return 42; } +int main () +{ + void *self = dlopen (0, LT_DLGLOBAL|LT_DLLAZY_OR_NOW); + int status = $lt_dlunknown; + + if (self) + { + if (dlsym (self,"fnord")) status = $lt_dlno_uscore; + else + { + if (dlsym( self,"_fnord")) status = $lt_dlneed_uscore; + else puts (dlerror ()); + } + /* dlclose (self); */ + } + else + puts (dlerror ()); + + return status; +}] +_LT_EOF + if AC_TRY_EVAL(ac_link) && test -s conftest${ac_exeext} 2>/dev/null; then + (./conftest; exit; ) >&AS_MESSAGE_LOG_FD 2>/dev/null + lt_status=$? + case x$lt_status in + x$lt_dlno_uscore) $1 ;; + x$lt_dlneed_uscore) $2 ;; + x$lt_dlunknown|x*) $3 ;; + esac + else : + # compilation failed + $3 + fi +fi +rm -fr conftest* +])# _LT_TRY_DLOPEN_SELF + + +# LT_SYS_DLOPEN_SELF +# ------------------ +AC_DEFUN([LT_SYS_DLOPEN_SELF], +[m4_require([_LT_HEADER_DLFCN])dnl +if test "x$enable_dlopen" != xyes; then + enable_dlopen=unknown + enable_dlopen_self=unknown + enable_dlopen_self_static=unknown +else + lt_cv_dlopen=no + lt_cv_dlopen_libs= + + case $host_os in + beos*) + lt_cv_dlopen="load_add_on" + lt_cv_dlopen_libs= + lt_cv_dlopen_self=yes + ;; + + mingw* | pw32* | cegcc*) + lt_cv_dlopen="LoadLibrary" + lt_cv_dlopen_libs= + ;; + + cygwin*) + lt_cv_dlopen="dlopen" + lt_cv_dlopen_libs= + ;; + + darwin*) + # if libdl is installed we need to link against it + AC_CHECK_LIB([dl], [dlopen], + [lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-ldl"],[ + lt_cv_dlopen="dyld" + lt_cv_dlopen_libs= + lt_cv_dlopen_self=yes + ]) + ;; + + *) + AC_CHECK_FUNC([shl_load], + [lt_cv_dlopen="shl_load"], + [AC_CHECK_LIB([dld], [shl_load], + [lt_cv_dlopen="shl_load" lt_cv_dlopen_libs="-ldld"], + [AC_CHECK_FUNC([dlopen], + [lt_cv_dlopen="dlopen"], + [AC_CHECK_LIB([dl], [dlopen], + [lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-ldl"], + [AC_CHECK_LIB([svld], [dlopen], + [lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-lsvld"], + [AC_CHECK_LIB([dld], [dld_link], + [lt_cv_dlopen="dld_link" lt_cv_dlopen_libs="-ldld"]) + ]) + ]) + ]) + ]) + ]) + ;; + esac + + if test "x$lt_cv_dlopen" != xno; then + enable_dlopen=yes + else + enable_dlopen=no + fi + + case $lt_cv_dlopen in + dlopen) + save_CPPFLAGS="$CPPFLAGS" + test "x$ac_cv_header_dlfcn_h" = xyes && CPPFLAGS="$CPPFLAGS -DHAVE_DLFCN_H" + + save_LDFLAGS="$LDFLAGS" + wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $export_dynamic_flag_spec\" + + save_LIBS="$LIBS" + LIBS="$lt_cv_dlopen_libs $LIBS" + + AC_CACHE_CHECK([whether a program can dlopen itself], + lt_cv_dlopen_self, [dnl + _LT_TRY_DLOPEN_SELF( + lt_cv_dlopen_self=yes, lt_cv_dlopen_self=yes, + lt_cv_dlopen_self=no, lt_cv_dlopen_self=cross) + ]) + + if test "x$lt_cv_dlopen_self" = xyes; then + wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $lt_prog_compiler_static\" + AC_CACHE_CHECK([whether a statically linked program can dlopen itself], + lt_cv_dlopen_self_static, [dnl + _LT_TRY_DLOPEN_SELF( + lt_cv_dlopen_self_static=yes, lt_cv_dlopen_self_static=yes, + lt_cv_dlopen_self_static=no, lt_cv_dlopen_self_static=cross) + ]) + fi + + CPPFLAGS="$save_CPPFLAGS" + LDFLAGS="$save_LDFLAGS" + LIBS="$save_LIBS" + ;; + esac + + case $lt_cv_dlopen_self in + yes|no) enable_dlopen_self=$lt_cv_dlopen_self ;; + *) enable_dlopen_self=unknown ;; + esac + + case $lt_cv_dlopen_self_static in + yes|no) enable_dlopen_self_static=$lt_cv_dlopen_self_static ;; + *) enable_dlopen_self_static=unknown ;; + esac +fi +_LT_DECL([dlopen_support], [enable_dlopen], [0], + [Whether dlopen is supported]) +_LT_DECL([dlopen_self], [enable_dlopen_self], [0], + [Whether dlopen of programs is supported]) +_LT_DECL([dlopen_self_static], [enable_dlopen_self_static], [0], + [Whether dlopen of statically linked programs is supported]) +])# LT_SYS_DLOPEN_SELF + +# Old name: +AU_ALIAS([AC_LIBTOOL_DLOPEN_SELF], [LT_SYS_DLOPEN_SELF]) +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AC_LIBTOOL_DLOPEN_SELF], []) + + +# _LT_COMPILER_C_O([TAGNAME]) +# --------------------------- +# Check to see if options -c and -o are simultaneously supported by compiler. +# This macro does not hard code the compiler like AC_PROG_CC_C_O. +m4_defun([_LT_COMPILER_C_O], +[m4_require([_LT_DECL_SED])dnl +m4_require([_LT_FILEUTILS_DEFAULTS])dnl +m4_require([_LT_TAG_COMPILER])dnl +AC_CACHE_CHECK([if $compiler supports -c -o file.$ac_objext], + [_LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)], + [_LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)=no + $RM -r conftest 2>/dev/null + mkdir conftest + cd conftest + mkdir out + echo "$lt_simple_compile_test_code" > conftest.$ac_ext + + lt_compiler_flag="-o out/conftest2.$ac_objext" + # Insert the option either (1) after the last *FLAGS variable, or + # (2) before a word containing "conftest.", or (3) at the end. + # Note that $ac_compile itself does not contain backslashes and begins + # with a dollar sign (not a hyphen), so the echo should work correctly. + lt_compile=`echo "$ac_compile" | $SED \ + -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ + -e 's: [[^ ]]*conftest\.: $lt_compiler_flag&:; t' \ + -e 's:$: $lt_compiler_flag:'` + (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&AS_MESSAGE_LOG_FD) + (eval "$lt_compile" 2>out/conftest.err) + ac_status=$? + cat out/conftest.err >&AS_MESSAGE_LOG_FD + echo "$as_me:$LINENO: \$? = $ac_status" >&AS_MESSAGE_LOG_FD + if (exit $ac_status) && test -s out/conftest2.$ac_objext + then + # The compiler can only warn and ignore the option if not recognized + # So say no if there are warnings + $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' > out/conftest.exp + $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2 + if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then + _LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)=yes + fi + fi + chmod u+w . 2>&AS_MESSAGE_LOG_FD + $RM conftest* + # SGI C++ compiler will create directory out/ii_files/ for + # template instantiation + test -d out/ii_files && $RM out/ii_files/* && rmdir out/ii_files + $RM out/* && rmdir out + cd .. + $RM -r conftest + $RM conftest* +]) +_LT_TAGDECL([compiler_c_o], [lt_cv_prog_compiler_c_o], [1], + [Does compiler simultaneously support -c and -o options?]) +])# _LT_COMPILER_C_O + + +# _LT_COMPILER_FILE_LOCKS([TAGNAME]) +# ---------------------------------- +# Check to see if we can do hard links to lock some files if needed +m4_defun([_LT_COMPILER_FILE_LOCKS], +[m4_require([_LT_ENABLE_LOCK])dnl +m4_require([_LT_FILEUTILS_DEFAULTS])dnl +_LT_COMPILER_C_O([$1]) + +hard_links="nottested" +if test "$_LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)" = no && test "$need_locks" != no; then + # do not overwrite the value of need_locks provided by the user + AC_MSG_CHECKING([if we can lock with hard links]) + hard_links=yes + $RM conftest* + ln conftest.a conftest.b 2>/dev/null && hard_links=no + touch conftest.a + ln conftest.a conftest.b 2>&5 || hard_links=no + ln conftest.a conftest.b 2>/dev/null && hard_links=no + AC_MSG_RESULT([$hard_links]) + if test "$hard_links" = no; then + AC_MSG_WARN([`$CC' does not support `-c -o', so `make -j' may be unsafe]) + need_locks=warn + fi +else + need_locks=no +fi +_LT_DECL([], [need_locks], [1], [Must we lock files when doing compilation?]) +])# _LT_COMPILER_FILE_LOCKS + + +# _LT_CHECK_OBJDIR +# ---------------- +m4_defun([_LT_CHECK_OBJDIR], +[AC_CACHE_CHECK([for objdir], [lt_cv_objdir], +[rm -f .libs 2>/dev/null +mkdir .libs 2>/dev/null +if test -d .libs; then + lt_cv_objdir=.libs +else + # MS-DOS does not allow filenames that begin with a dot. + lt_cv_objdir=_libs +fi +rmdir .libs 2>/dev/null]) +objdir=$lt_cv_objdir +_LT_DECL([], [objdir], [0], + [The name of the directory that contains temporary libtool files])dnl +m4_pattern_allow([LT_OBJDIR])dnl +AC_DEFINE_UNQUOTED(LT_OBJDIR, "$lt_cv_objdir/", + [Define to the sub-directory in which libtool stores uninstalled libraries.]) +])# _LT_CHECK_OBJDIR + + +# _LT_LINKER_HARDCODE_LIBPATH([TAGNAME]) +# -------------------------------------- +# Check hardcoding attributes. +m4_defun([_LT_LINKER_HARDCODE_LIBPATH], +[AC_MSG_CHECKING([how to hardcode library paths into programs]) +_LT_TAGVAR(hardcode_action, $1)= +if test -n "$_LT_TAGVAR(hardcode_libdir_flag_spec, $1)" || + test -n "$_LT_TAGVAR(runpath_var, $1)" || + test "X$_LT_TAGVAR(hardcode_automatic, $1)" = "Xyes" ; then + + # We can hardcode non-existent directories. + if test "$_LT_TAGVAR(hardcode_direct, $1)" != no && + # If the only mechanism to avoid hardcoding is shlibpath_var, we + # have to relink, otherwise we might link with an installed library + # when we should be linking with a yet-to-be-installed one + ## test "$_LT_TAGVAR(hardcode_shlibpath_var, $1)" != no && + test "$_LT_TAGVAR(hardcode_minus_L, $1)" != no; then + # Linking always hardcodes the temporary library directory. + _LT_TAGVAR(hardcode_action, $1)=relink + else + # We can link without hardcoding, and we can hardcode nonexisting dirs. + _LT_TAGVAR(hardcode_action, $1)=immediate + fi +else + # We cannot hardcode anything, or else we can only hardcode existing + # directories. + _LT_TAGVAR(hardcode_action, $1)=unsupported +fi +AC_MSG_RESULT([$_LT_TAGVAR(hardcode_action, $1)]) + +if test "$_LT_TAGVAR(hardcode_action, $1)" = relink || + test "$_LT_TAGVAR(inherit_rpath, $1)" = yes; then + # Fast installation is not supported + enable_fast_install=no +elif test "$shlibpath_overrides_runpath" = yes || + test "$enable_shared" = no; then + # Fast installation is not necessary + enable_fast_install=needless +fi +_LT_TAGDECL([], [hardcode_action], [0], + [How to hardcode a shared library path into an executable]) +])# _LT_LINKER_HARDCODE_LIBPATH + + +# _LT_CMD_STRIPLIB +# ---------------- +m4_defun([_LT_CMD_STRIPLIB], +[m4_require([_LT_DECL_EGREP]) +striplib= +old_striplib= +AC_MSG_CHECKING([whether stripping libraries is possible]) +if test -n "$STRIP" && $STRIP -V 2>&1 | $GREP "GNU strip" >/dev/null; then + test -z "$old_striplib" && old_striplib="$STRIP --strip-debug" + test -z "$striplib" && striplib="$STRIP --strip-unneeded" + AC_MSG_RESULT([yes]) +else +# FIXME - insert some real tests, host_os isn't really good enough + case $host_os in + darwin*) + if test -n "$STRIP" ; then + striplib="$STRIP -x" + old_striplib="$STRIP -S" + AC_MSG_RESULT([yes]) + else + AC_MSG_RESULT([no]) + fi + ;; + *) + AC_MSG_RESULT([no]) + ;; + esac +fi +_LT_DECL([], [old_striplib], [1], [Commands to strip libraries]) +_LT_DECL([], [striplib], [1]) +])# _LT_CMD_STRIPLIB + + +# _LT_SYS_DYNAMIC_LINKER([TAG]) +# ----------------------------- +# PORTME Fill in your ld.so characteristics +m4_defun([_LT_SYS_DYNAMIC_LINKER], +[AC_REQUIRE([AC_CANONICAL_HOST])dnl +m4_require([_LT_DECL_EGREP])dnl +m4_require([_LT_FILEUTILS_DEFAULTS])dnl +m4_require([_LT_DECL_OBJDUMP])dnl +m4_require([_LT_DECL_SED])dnl +m4_require([_LT_CHECK_SHELL_FEATURES])dnl +AC_MSG_CHECKING([dynamic linker characteristics]) +m4_if([$1], + [], [ +if test "$GCC" = yes; then + case $host_os in + darwin*) lt_awk_arg="/^libraries:/,/LR/" ;; + *) lt_awk_arg="/^libraries:/" ;; + esac + case $host_os in + mingw* | cegcc*) lt_sed_strip_eq="s,=\([[A-Za-z]]:\),\1,g" ;; + *) lt_sed_strip_eq="s,=/,/,g" ;; + esac + lt_search_path_spec=`$CC -print-search-dirs | awk $lt_awk_arg | $SED -e "s/^libraries://" -e $lt_sed_strip_eq` + case $lt_search_path_spec in + *\;*) + # if the path contains ";" then we assume it to be the separator + # otherwise default to the standard path separator (i.e. ":") - it is + # assumed that no part of a normal pathname contains ";" but that should + # okay in the real world where ";" in dirpaths is itself problematic. + lt_search_path_spec=`$ECHO "$lt_search_path_spec" | $SED 's/;/ /g'` + ;; + *) + lt_search_path_spec=`$ECHO "$lt_search_path_spec" | $SED "s/$PATH_SEPARATOR/ /g"` + ;; + esac + # Ok, now we have the path, separated by spaces, we can step through it + # and add multilib dir if necessary. + lt_tmp_lt_search_path_spec= + lt_multi_os_dir=`$CC $CPPFLAGS $CFLAGS $LDFLAGS -print-multi-os-directory 2>/dev/null` + for lt_sys_path in $lt_search_path_spec; do + if test -d "$lt_sys_path/$lt_multi_os_dir"; then + lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec $lt_sys_path/$lt_multi_os_dir" + else + test -d "$lt_sys_path" && \ + lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec $lt_sys_path" + fi + done + lt_search_path_spec=`$ECHO "$lt_tmp_lt_search_path_spec" | awk ' +BEGIN {RS=" "; FS="/|\n";} { + lt_foo=""; + lt_count=0; + for (lt_i = NF; lt_i > 0; lt_i--) { + if ($lt_i != "" && $lt_i != ".") { + if ($lt_i == "..") { + lt_count++; + } else { + if (lt_count == 0) { + lt_foo="/" $lt_i lt_foo; + } else { + lt_count--; + } + } + } + } + if (lt_foo != "") { lt_freq[[lt_foo]]++; } + if (lt_freq[[lt_foo]] == 1) { print lt_foo; } +}'` + # AWK program above erroneously prepends '/' to C:/dos/paths + # for these hosts. + case $host_os in + mingw* | cegcc*) lt_search_path_spec=`$ECHO "$lt_search_path_spec" |\ + $SED 's,/\([[A-Za-z]]:\),\1,g'` ;; + esac + sys_lib_search_path_spec=`$ECHO "$lt_search_path_spec" | $lt_NL2SP` +else + sys_lib_search_path_spec="/lib /usr/lib /usr/local/lib" +fi]) +library_names_spec= +libname_spec='lib$name' +soname_spec= +shrext_cmds=".so" +postinstall_cmds= +postuninstall_cmds= +finish_cmds= +finish_eval= +shlibpath_var= +shlibpath_overrides_runpath=unknown +version_type=none +dynamic_linker="$host_os ld.so" +sys_lib_dlsearch_path_spec="/lib /usr/lib" +need_lib_prefix=unknown +hardcode_into_libs=no + +# when you set need_version to no, make sure it does not cause -set_version +# flags to be left without arguments +need_version=unknown + +case $host_os in +aix3*) + version_type=linux # correct to gnu/linux during the next big refactor + library_names_spec='${libname}${release}${shared_ext}$versuffix $libname.a' + shlibpath_var=LIBPATH + + # AIX 3 has no versioning support, so we append a major version to the name. + soname_spec='${libname}${release}${shared_ext}$major' + ;; + +aix[[4-9]]*) + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + hardcode_into_libs=yes + if test "$host_cpu" = ia64; then + # AIX 5 supports IA64 + library_names_spec='${libname}${release}${shared_ext}$major ${libname}${release}${shared_ext}$versuffix $libname${shared_ext}' + shlibpath_var=LD_LIBRARY_PATH + else + # With GCC up to 2.95.x, collect2 would create an import file + # for dependence libraries. The import file would start with + # the line `#! .'. This would cause the generated library to + # depend on `.', always an invalid library. This was fixed in + # development snapshots of GCC prior to 3.0. + case $host_os in + aix4 | aix4.[[01]] | aix4.[[01]].*) + if { echo '#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 97)' + echo ' yes ' + echo '#endif'; } | ${CC} -E - | $GREP yes > /dev/null; then + : + else + can_build_shared=no + fi + ;; + esac + # AIX (on Power*) has no versioning support, so currently we can not hardcode correct + # soname into executable. Probably we can add versioning support to + # collect2, so additional links can be useful in future. + if test "$aix_use_runtimelinking" = yes; then + # If using run time linking (on AIX 4.2 or later) use lib.so + # instead of lib.a to let people know that these are not + # typical AIX shared libraries. + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + else + # We preserve .a as extension for shared libraries through AIX4.2 + # and later when we are not doing run time linking. + library_names_spec='${libname}${release}.a $libname.a' + soname_spec='${libname}${release}${shared_ext}$major' + fi + shlibpath_var=LIBPATH + fi + ;; + +amigaos*) + case $host_cpu in + powerpc) + # Since July 2007 AmigaOS4 officially supports .so libraries. + # When compiling the executable, add -use-dynld -Lsobjs: to the compileline. + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + ;; + m68k) + library_names_spec='$libname.ixlibrary $libname.a' + # Create ${libname}_ixlibrary.a entries in /sys/libs. + finish_eval='for lib in `ls $libdir/*.ixlibrary 2>/dev/null`; do libname=`func_echo_all "$lib" | $SED '\''s%^.*/\([[^/]]*\)\.ixlibrary$%\1%'\''`; test $RM /sys/libs/${libname}_ixlibrary.a; $show "cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a"; cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a || exit 1; done' + ;; + esac + ;; + +beos*) + library_names_spec='${libname}${shared_ext}' + dynamic_linker="$host_os ld.so" + shlibpath_var=LIBRARY_PATH + ;; + +bsdi[[45]]*) + version_type=linux # correct to gnu/linux during the next big refactor + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + finish_cmds='PATH="\$PATH:/sbin" ldconfig $libdir' + shlibpath_var=LD_LIBRARY_PATH + sys_lib_search_path_spec="/shlib /usr/lib /usr/X11/lib /usr/contrib/lib /lib /usr/local/lib" + sys_lib_dlsearch_path_spec="/shlib /usr/lib /usr/local/lib" + # the default ld.so.conf also contains /usr/contrib/lib and + # /usr/X11R6/lib (/usr/X11 is a link to /usr/X11R6), but let us allow + # libtool to hard-code these into programs + ;; + +cygwin* | mingw* | pw32* | cegcc*) + version_type=windows + shrext_cmds=".dll" + need_version=no + need_lib_prefix=no + + case $GCC,$cc_basename in + yes,*) + # gcc + library_names_spec='$libname.dll.a' + # DLL is installed to $(libdir)/../bin by postinstall_cmds + postinstall_cmds='base_file=`basename \${file}`~ + dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\${base_file}'\''i; echo \$dlname'\''`~ + dldir=$destdir/`dirname \$dlpath`~ + test -d \$dldir || mkdir -p \$dldir~ + $install_prog $dir/$dlname \$dldir/$dlname~ + chmod a+x \$dldir/$dlname~ + if test -n '\''$stripme'\'' && test -n '\''$striplib'\''; then + eval '\''$striplib \$dldir/$dlname'\'' || exit \$?; + fi' + postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~ + dlpath=$dir/\$dldll~ + $RM \$dlpath' + shlibpath_overrides_runpath=yes + + case $host_os in + cygwin*) + # Cygwin DLLs use 'cyg' prefix rather than 'lib' + soname_spec='`echo ${libname} | sed -e 's/^lib/cyg/'``echo ${release} | $SED -e 's/[[.]]/-/g'`${versuffix}${shared_ext}' +m4_if([$1], [],[ + sys_lib_search_path_spec="$sys_lib_search_path_spec /usr/lib/w32api"]) + ;; + mingw* | cegcc*) + # MinGW DLLs use traditional 'lib' prefix + soname_spec='${libname}`echo ${release} | $SED -e 's/[[.]]/-/g'`${versuffix}${shared_ext}' + ;; + pw32*) + # pw32 DLLs use 'pw' prefix rather than 'lib' + library_names_spec='`echo ${libname} | sed -e 's/^lib/pw/'``echo ${release} | $SED -e 's/[[.]]/-/g'`${versuffix}${shared_ext}' + ;; + esac + dynamic_linker='Win32 ld.exe' + ;; + + *,cl*) + # Native MSVC + libname_spec='$name' + soname_spec='${libname}`echo ${release} | $SED -e 's/[[.]]/-/g'`${versuffix}${shared_ext}' + library_names_spec='${libname}.dll.lib' + + case $build_os in + mingw*) + sys_lib_search_path_spec= + lt_save_ifs=$IFS + IFS=';' + for lt_path in $LIB + do + IFS=$lt_save_ifs + # Let DOS variable expansion print the short 8.3 style file name. + lt_path=`cd "$lt_path" 2>/dev/null && cmd //C "for %i in (".") do @echo %~si"` + sys_lib_search_path_spec="$sys_lib_search_path_spec $lt_path" + done + IFS=$lt_save_ifs + # Convert to MSYS style. + sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | sed -e 's|\\\\|/|g' -e 's| \\([[a-zA-Z]]\\):| /\\1|g' -e 's|^ ||'` + ;; + cygwin*) + # Convert to unix form, then to dos form, then back to unix form + # but this time dos style (no spaces!) so that the unix form looks + # like /cygdrive/c/PROGRA~1:/cygdr... + sys_lib_search_path_spec=`cygpath --path --unix "$LIB"` + sys_lib_search_path_spec=`cygpath --path --dos "$sys_lib_search_path_spec" 2>/dev/null` + sys_lib_search_path_spec=`cygpath --path --unix "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"` + ;; + *) + sys_lib_search_path_spec="$LIB" + if $ECHO "$sys_lib_search_path_spec" | [$GREP ';[c-zC-Z]:/' >/dev/null]; then + # It is most probably a Windows format PATH. + sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e 's/;/ /g'` + else + sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"` + fi + # FIXME: find the short name or the path components, as spaces are + # common. (e.g. "Program Files" -> "PROGRA~1") + ;; + esac + + # DLL is installed to $(libdir)/../bin by postinstall_cmds + postinstall_cmds='base_file=`basename \${file}`~ + dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\${base_file}'\''i; echo \$dlname'\''`~ + dldir=$destdir/`dirname \$dlpath`~ + test -d \$dldir || mkdir -p \$dldir~ + $install_prog $dir/$dlname \$dldir/$dlname' + postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~ + dlpath=$dir/\$dldll~ + $RM \$dlpath' + shlibpath_overrides_runpath=yes + dynamic_linker='Win32 link.exe' + ;; + + *) + # Assume MSVC wrapper + library_names_spec='${libname}`echo ${release} | $SED -e 's/[[.]]/-/g'`${versuffix}${shared_ext} $libname.lib' + dynamic_linker='Win32 ld.exe' + ;; + esac + # FIXME: first we should search . and the directory the executable is in + shlibpath_var=PATH + ;; + +darwin* | rhapsody*) + dynamic_linker="$host_os dyld" + version_type=darwin + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${major}$shared_ext ${libname}$shared_ext' + soname_spec='${libname}${release}${major}$shared_ext' + shlibpath_overrides_runpath=yes + shlibpath_var=DYLD_LIBRARY_PATH + shrext_cmds='`test .$module = .yes && echo .so || echo .dylib`' +m4_if([$1], [],[ + sys_lib_search_path_spec="$sys_lib_search_path_spec /usr/local/lib"]) + sys_lib_dlsearch_path_spec='/usr/local/lib /lib /usr/lib' + ;; + +dgux*) + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname$shared_ext' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + ;; + +freebsd* | dragonfly*) + # DragonFly does not have aout. When/if they implement a new + # versioning mechanism, adjust this. + if test -x /usr/bin/objformat; then + objformat=`/usr/bin/objformat` + else + case $host_os in + freebsd[[23]].*) objformat=aout ;; + *) objformat=elf ;; + esac + fi + version_type=freebsd-$objformat + case $version_type in + freebsd-elf*) + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext} $libname${shared_ext}' + need_version=no + need_lib_prefix=no + ;; + freebsd-*) + library_names_spec='${libname}${release}${shared_ext}$versuffix $libname${shared_ext}$versuffix' + need_version=yes + ;; + esac + shlibpath_var=LD_LIBRARY_PATH + case $host_os in + freebsd2.*) + shlibpath_overrides_runpath=yes + ;; + freebsd3.[[01]]* | freebsdelf3.[[01]]*) + shlibpath_overrides_runpath=yes + hardcode_into_libs=yes + ;; + freebsd3.[[2-9]]* | freebsdelf3.[[2-9]]* | \ + freebsd4.[[0-5]] | freebsdelf4.[[0-5]] | freebsd4.1.1 | freebsdelf4.1.1) + shlibpath_overrides_runpath=no + hardcode_into_libs=yes + ;; + *) # from 4.6 on, and DragonFly + shlibpath_overrides_runpath=yes + hardcode_into_libs=yes + ;; + esac + ;; + +haiku*) + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + dynamic_linker="$host_os runtime_loader" + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}${major} ${libname}${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LIBRARY_PATH + shlibpath_overrides_runpath=yes + sys_lib_dlsearch_path_spec='/boot/home/config/lib /boot/common/lib /boot/system/lib' + hardcode_into_libs=yes + ;; + +hpux9* | hpux10* | hpux11*) + # Give a soname corresponding to the major version so that dld.sl refuses to + # link against other versions. + version_type=sunos + need_lib_prefix=no + need_version=no + case $host_cpu in + ia64*) + shrext_cmds='.so' + hardcode_into_libs=yes + dynamic_linker="$host_os dld.so" + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes # Unless +noenvvar is specified. + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + if test "X$HPUX_IA64_MODE" = X32; then + sys_lib_search_path_spec="/usr/lib/hpux32 /usr/local/lib/hpux32 /usr/local/lib" + else + sys_lib_search_path_spec="/usr/lib/hpux64 /usr/local/lib/hpux64" + fi + sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec + ;; + hppa*64*) + shrext_cmds='.sl' + hardcode_into_libs=yes + dynamic_linker="$host_os dld.sl" + shlibpath_var=LD_LIBRARY_PATH # How should we handle SHLIB_PATH + shlibpath_overrides_runpath=yes # Unless +noenvvar is specified. + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + sys_lib_search_path_spec="/usr/lib/pa20_64 /usr/ccs/lib/pa20_64" + sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec + ;; + *) + shrext_cmds='.sl' + dynamic_linker="$host_os dld.sl" + shlibpath_var=SHLIB_PATH + shlibpath_overrides_runpath=no # +s is required to enable SHLIB_PATH + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + ;; + esac + # HP-UX runs *really* slowly unless shared libraries are mode 555, ... + postinstall_cmds='chmod 555 $lib' + # or fails outright, so override atomically: + install_override_mode=555 + ;; + +interix[[3-9]]*) + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + dynamic_linker='Interix 3.x ld.so.1 (PE, like ELF)' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=no + hardcode_into_libs=yes + ;; + +irix5* | irix6* | nonstopux*) + case $host_os in + nonstopux*) version_type=nonstopux ;; + *) + if test "$lt_cv_prog_gnu_ld" = yes; then + version_type=linux # correct to gnu/linux during the next big refactor + else + version_type=irix + fi ;; + esac + need_lib_prefix=no + need_version=no + soname_spec='${libname}${release}${shared_ext}$major' + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${release}${shared_ext} $libname${shared_ext}' + case $host_os in + irix5* | nonstopux*) + libsuff= shlibsuff= + ;; + *) + case $LD in # libtool.m4 will add one of these switches to LD + *-32|*"-32 "|*-melf32bsmip|*"-melf32bsmip ") + libsuff= shlibsuff= libmagic=32-bit;; + *-n32|*"-n32 "|*-melf32bmipn32|*"-melf32bmipn32 ") + libsuff=32 shlibsuff=N32 libmagic=N32;; + *-64|*"-64 "|*-melf64bmip|*"-melf64bmip ") + libsuff=64 shlibsuff=64 libmagic=64-bit;; + *) libsuff= shlibsuff= libmagic=never-match;; + esac + ;; + esac + shlibpath_var=LD_LIBRARY${shlibsuff}_PATH + shlibpath_overrides_runpath=no + sys_lib_search_path_spec="/usr/lib${libsuff} /lib${libsuff} /usr/local/lib${libsuff}" + sys_lib_dlsearch_path_spec="/usr/lib${libsuff} /lib${libsuff}" + hardcode_into_libs=yes + ;; + +# No shared lib support for Linux oldld, aout, or coff. +linux*oldld* | linux*aout* | linux*coff*) + dynamic_linker=no + ;; + +# This must be glibc/ELF. +linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + finish_cmds='PATH="\$PATH:/sbin" ldconfig -n $libdir' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=no + + # Some binutils ld are patched to set DT_RUNPATH + AC_CACHE_VAL([lt_cv_shlibpath_overrides_runpath], + [lt_cv_shlibpath_overrides_runpath=no + save_LDFLAGS=$LDFLAGS + save_libdir=$libdir + eval "libdir=/foo; wl=\"$_LT_TAGVAR(lt_prog_compiler_wl, $1)\"; \ + LDFLAGS=\"\$LDFLAGS $_LT_TAGVAR(hardcode_libdir_flag_spec, $1)\"" + AC_LINK_IFELSE([AC_LANG_PROGRAM([],[])], + [AS_IF([ ($OBJDUMP -p conftest$ac_exeext) 2>/dev/null | grep "RUNPATH.*$libdir" >/dev/null], + [lt_cv_shlibpath_overrides_runpath=yes])]) + LDFLAGS=$save_LDFLAGS + libdir=$save_libdir + ]) + shlibpath_overrides_runpath=$lt_cv_shlibpath_overrides_runpath + + # This implies no fast_install, which is unacceptable. + # Some rework will be needed to allow for fast_install + # before this can be enabled. + hardcode_into_libs=yes + + # Append ld.so.conf contents to the search path + if test -f /etc/ld.so.conf; then + lt_ld_extra=`awk '/^include / { system(sprintf("cd /etc; cat %s 2>/dev/null", \[$]2)); skip = 1; } { if (!skip) print \[$]0; skip = 0; }' < /etc/ld.so.conf | $SED -e 's/#.*//;/^[ ]*hwcap[ ]/d;s/[:, ]/ /g;s/=[^=]*$//;s/=[^= ]* / /g;s/"//g;/^$/d' | tr '\n' ' '` + sys_lib_dlsearch_path_spec="/lib /usr/lib $lt_ld_extra" + fi + + # We used to test for /lib/ld.so.1 and disable shared libraries on + # powerpc, because MkLinux only supported shared libraries with the + # GNU dynamic linker. Since this was broken with cross compilers, + # most powerpc-linux boxes support dynamic linking these days and + # people can always --disable-shared, the test was removed, and we + # assume the GNU/Linux dynamic linker is in use. + dynamic_linker='GNU/Linux ld.so' + ;; + +netbsdelf*-gnu) + version_type=linux + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=no + hardcode_into_libs=yes + dynamic_linker='NetBSD ld.elf_so' + ;; + +netbsd*) + version_type=sunos + need_lib_prefix=no + need_version=no + if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix' + finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' + dynamic_linker='NetBSD (a.out) ld.so' + else + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + dynamic_linker='NetBSD ld.elf_so' + fi + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + hardcode_into_libs=yes + ;; + +newsos6) + version_type=linux # correct to gnu/linux during the next big refactor + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + ;; + +*nto* | *qnx*) + version_type=qnx + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=no + hardcode_into_libs=yes + dynamic_linker='ldqnx.so' + ;; + +openbsd*) + version_type=sunos + sys_lib_dlsearch_path_spec="/usr/lib" + need_lib_prefix=no + # Some older versions of OpenBSD (3.3 at least) *do* need versioned libs. + case $host_os in + openbsd3.3 | openbsd3.3.*) need_version=yes ;; + *) need_version=no ;; + esac + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix' + finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' + shlibpath_var=LD_LIBRARY_PATH + if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then + case $host_os in + openbsd2.[[89]] | openbsd2.[[89]].*) + shlibpath_overrides_runpath=no + ;; + *) + shlibpath_overrides_runpath=yes + ;; + esac + else + shlibpath_overrides_runpath=yes + fi + ;; + +os2*) + libname_spec='$name' + shrext_cmds=".dll" + need_lib_prefix=no + library_names_spec='$libname${shared_ext} $libname.a' + dynamic_linker='OS/2 ld.exe' + shlibpath_var=LIBPATH + ;; + +osf3* | osf4* | osf5*) + version_type=osf + need_lib_prefix=no + need_version=no + soname_spec='${libname}${release}${shared_ext}$major' + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + shlibpath_var=LD_LIBRARY_PATH + sys_lib_search_path_spec="/usr/shlib /usr/ccs/lib /usr/lib/cmplrs/cc /usr/lib /usr/local/lib /var/shlib" + sys_lib_dlsearch_path_spec="$sys_lib_search_path_spec" + ;; + +rdos*) + dynamic_linker=no + ;; + +solaris*) + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + hardcode_into_libs=yes + # ldd complains unless libraries are executable + postinstall_cmds='chmod +x $lib' + ;; + +sunos4*) + version_type=sunos + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix' + finish_cmds='PATH="\$PATH:/usr/etc" ldconfig $libdir' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + if test "$with_gnu_ld" = yes; then + need_lib_prefix=no + fi + need_version=yes + ;; + +sysv4 | sysv4.3*) + version_type=linux # correct to gnu/linux during the next big refactor + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + case $host_vendor in + sni) + shlibpath_overrides_runpath=no + need_lib_prefix=no + runpath_var=LD_RUN_PATH + ;; + siemens) + need_lib_prefix=no + ;; + motorola) + need_lib_prefix=no + need_version=no + shlibpath_overrides_runpath=no + sys_lib_search_path_spec='/lib /usr/lib /usr/ccs/lib' + ;; + esac + ;; + +sysv4*MP*) + if test -d /usr/nec ;then + version_type=linux # correct to gnu/linux during the next big refactor + library_names_spec='$libname${shared_ext}.$versuffix $libname${shared_ext}.$major $libname${shared_ext}' + soname_spec='$libname${shared_ext}.$major' + shlibpath_var=LD_LIBRARY_PATH + fi + ;; + +sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*) + version_type=freebsd-elf + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext} $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + hardcode_into_libs=yes + if test "$with_gnu_ld" = yes; then + sys_lib_search_path_spec='/usr/local/lib /usr/gnu/lib /usr/ccs/lib /usr/lib /lib' + else + sys_lib_search_path_spec='/usr/ccs/lib /usr/lib' + case $host_os in + sco3.2v5*) + sys_lib_search_path_spec="$sys_lib_search_path_spec /lib" + ;; + esac + fi + sys_lib_dlsearch_path_spec='/usr/lib' + ;; + +tpf*) + # TPF is a cross-target only. Preferred cross-host = GNU/Linux. + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=no + hardcode_into_libs=yes + ;; + +uts4*) + version_type=linux # correct to gnu/linux during the next big refactor + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + ;; + +*) + dynamic_linker=no + ;; +esac +AC_MSG_RESULT([$dynamic_linker]) +test "$dynamic_linker" = no && can_build_shared=no + +variables_saved_for_relink="PATH $shlibpath_var $runpath_var" +if test "$GCC" = yes; then + variables_saved_for_relink="$variables_saved_for_relink GCC_EXEC_PREFIX COMPILER_PATH LIBRARY_PATH" +fi + +if test "${lt_cv_sys_lib_search_path_spec+set}" = set; then + sys_lib_search_path_spec="$lt_cv_sys_lib_search_path_spec" +fi +if test "${lt_cv_sys_lib_dlsearch_path_spec+set}" = set; then + sys_lib_dlsearch_path_spec="$lt_cv_sys_lib_dlsearch_path_spec" +fi + +_LT_DECL([], [variables_saved_for_relink], [1], + [Variables whose values should be saved in libtool wrapper scripts and + restored at link time]) +_LT_DECL([], [need_lib_prefix], [0], + [Do we need the "lib" prefix for modules?]) +_LT_DECL([], [need_version], [0], [Do we need a version for libraries?]) +_LT_DECL([], [version_type], [0], [Library versioning type]) +_LT_DECL([], [runpath_var], [0], [Shared library runtime path variable]) +_LT_DECL([], [shlibpath_var], [0],[Shared library path variable]) +_LT_DECL([], [shlibpath_overrides_runpath], [0], + [Is shlibpath searched before the hard-coded library search path?]) +_LT_DECL([], [libname_spec], [1], [Format of library name prefix]) +_LT_DECL([], [library_names_spec], [1], + [[List of archive names. First name is the real one, the rest are links. + The last name is the one that the linker finds with -lNAME]]) +_LT_DECL([], [soname_spec], [1], + [[The coded name of the library, if different from the real name]]) +_LT_DECL([], [install_override_mode], [1], + [Permission mode override for installation of shared libraries]) +_LT_DECL([], [postinstall_cmds], [2], + [Command to use after installation of a shared archive]) +_LT_DECL([], [postuninstall_cmds], [2], + [Command to use after uninstallation of a shared archive]) +_LT_DECL([], [finish_cmds], [2], + [Commands used to finish a libtool library installation in a directory]) +_LT_DECL([], [finish_eval], [1], + [[As "finish_cmds", except a single script fragment to be evaled but + not shown]]) +_LT_DECL([], [hardcode_into_libs], [0], + [Whether we should hardcode library paths into libraries]) +_LT_DECL([], [sys_lib_search_path_spec], [2], + [Compile-time system search path for libraries]) +_LT_DECL([], [sys_lib_dlsearch_path_spec], [2], + [Run-time system search path for libraries]) +])# _LT_SYS_DYNAMIC_LINKER + + +# _LT_PATH_TOOL_PREFIX(TOOL) +# -------------------------- +# find a file program which can recognize shared library +AC_DEFUN([_LT_PATH_TOOL_PREFIX], +[m4_require([_LT_DECL_EGREP])dnl +AC_MSG_CHECKING([for $1]) +AC_CACHE_VAL(lt_cv_path_MAGIC_CMD, +[case $MAGIC_CMD in +[[\\/*] | ?:[\\/]*]) + lt_cv_path_MAGIC_CMD="$MAGIC_CMD" # Let the user override the test with a path. + ;; +*) + lt_save_MAGIC_CMD="$MAGIC_CMD" + lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR +dnl $ac_dummy forces splitting on constant user-supplied paths. +dnl POSIX.2 word splitting is done only on the output of word expansions, +dnl not every word. This closes a longstanding sh security hole. + ac_dummy="m4_if([$2], , $PATH, [$2])" + for ac_dir in $ac_dummy; do + IFS="$lt_save_ifs" + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/$1; then + lt_cv_path_MAGIC_CMD="$ac_dir/$1" + if test -n "$file_magic_test_file"; then + case $deplibs_check_method in + "file_magic "*) + file_magic_regex=`expr "$deplibs_check_method" : "file_magic \(.*\)"` + MAGIC_CMD="$lt_cv_path_MAGIC_CMD" + if eval $file_magic_cmd \$file_magic_test_file 2> /dev/null | + $EGREP "$file_magic_regex" > /dev/null; then + : + else + cat <<_LT_EOF 1>&2 + +*** Warning: the command libtool uses to detect shared libraries, +*** $file_magic_cmd, produces output that libtool cannot recognize. +*** The result is that libtool may fail to recognize shared libraries +*** as such. This will affect the creation of libtool libraries that +*** depend on shared libraries, but programs linked with such libtool +*** libraries will work regardless of this problem. Nevertheless, you +*** may want to report the problem to your system manager and/or to +*** bug-libtool@gnu.org + +_LT_EOF + fi ;; + esac + fi + break + fi + done + IFS="$lt_save_ifs" + MAGIC_CMD="$lt_save_MAGIC_CMD" + ;; +esac]) +MAGIC_CMD="$lt_cv_path_MAGIC_CMD" +if test -n "$MAGIC_CMD"; then + AC_MSG_RESULT($MAGIC_CMD) +else + AC_MSG_RESULT(no) +fi +_LT_DECL([], [MAGIC_CMD], [0], + [Used to examine libraries when file_magic_cmd begins with "file"])dnl +])# _LT_PATH_TOOL_PREFIX + +# Old name: +AU_ALIAS([AC_PATH_TOOL_PREFIX], [_LT_PATH_TOOL_PREFIX]) +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AC_PATH_TOOL_PREFIX], []) + + +# _LT_PATH_MAGIC +# -------------- +# find a file program which can recognize a shared library +m4_defun([_LT_PATH_MAGIC], +[_LT_PATH_TOOL_PREFIX(${ac_tool_prefix}file, /usr/bin$PATH_SEPARATOR$PATH) +if test -z "$lt_cv_path_MAGIC_CMD"; then + if test -n "$ac_tool_prefix"; then + _LT_PATH_TOOL_PREFIX(file, /usr/bin$PATH_SEPARATOR$PATH) + else + MAGIC_CMD=: + fi +fi +])# _LT_PATH_MAGIC + + +# LT_PATH_LD +# ---------- +# find the pathname to the GNU or non-GNU linker +AC_DEFUN([LT_PATH_LD], +[AC_REQUIRE([AC_PROG_CC])dnl +AC_REQUIRE([AC_CANONICAL_HOST])dnl +AC_REQUIRE([AC_CANONICAL_BUILD])dnl +m4_require([_LT_DECL_SED])dnl +m4_require([_LT_DECL_EGREP])dnl +m4_require([_LT_PROG_ECHO_BACKSLASH])dnl + +AC_ARG_WITH([gnu-ld], + [AS_HELP_STRING([--with-gnu-ld], + [assume the C compiler uses GNU ld @<:@default=no@:>@])], + [test "$withval" = no || with_gnu_ld=yes], + [with_gnu_ld=no])dnl + +ac_prog=ld +if test "$GCC" = yes; then + # Check if gcc -print-prog-name=ld gives a path. + AC_MSG_CHECKING([for ld used by $CC]) + case $host in + *-*-mingw*) + # gcc leaves a trailing carriage return which upsets mingw + ac_prog=`($CC -print-prog-name=ld) 2>&5 | tr -d '\015'` ;; + *) + ac_prog=`($CC -print-prog-name=ld) 2>&5` ;; + esac + case $ac_prog in + # Accept absolute paths. + [[\\/]]* | ?:[[\\/]]*) + re_direlt='/[[^/]][[^/]]*/\.\./' + # Canonicalize the pathname of ld + ac_prog=`$ECHO "$ac_prog"| $SED 's%\\\\%/%g'` + while $ECHO "$ac_prog" | $GREP "$re_direlt" > /dev/null 2>&1; do + ac_prog=`$ECHO $ac_prog| $SED "s%$re_direlt%/%"` + done + test -z "$LD" && LD="$ac_prog" + ;; + "") + # If it fails, then pretend we aren't using GCC. + ac_prog=ld + ;; + *) + # If it is relative, then search for the first ld in PATH. + with_gnu_ld=unknown + ;; + esac +elif test "$with_gnu_ld" = yes; then + AC_MSG_CHECKING([for GNU ld]) +else + AC_MSG_CHECKING([for non-GNU ld]) +fi +AC_CACHE_VAL(lt_cv_path_LD, +[if test -z "$LD"; then + lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR + for ac_dir in $PATH; do + IFS="$lt_save_ifs" + test -z "$ac_dir" && ac_dir=. + if test -f "$ac_dir/$ac_prog" || test -f "$ac_dir/$ac_prog$ac_exeext"; then + lt_cv_path_LD="$ac_dir/$ac_prog" + # Check to see if the program is GNU ld. I'd rather use --version, + # but apparently some variants of GNU ld only accept -v. + # Break only if it was the GNU/non-GNU ld that we prefer. + case `"$lt_cv_path_LD" -v 2>&1 &1 /dev/null 2>&1; then + lt_cv_deplibs_check_method='file_magic ^x86 archive import|^x86 DLL' + lt_cv_file_magic_cmd='func_win32_libid' + else + # Keep this pattern in sync with the one in func_win32_libid. + lt_cv_deplibs_check_method='file_magic file format (pei*-i386(.*architecture: i386)?|pe-arm-wince|pe-x86-64)' + lt_cv_file_magic_cmd='$OBJDUMP -f' + fi + ;; + +cegcc*) + # use the weaker test based on 'objdump'. See mingw*. + lt_cv_deplibs_check_method='file_magic file format pe-arm-.*little(.*architecture: arm)?' + lt_cv_file_magic_cmd='$OBJDUMP -f' + ;; + +darwin* | rhapsody*) + lt_cv_deplibs_check_method=pass_all + ;; + +freebsd* | dragonfly*) + if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then + case $host_cpu in + i*86 ) + # Not sure whether the presence of OpenBSD here was a mistake. + # Let's accept both of them until this is cleared up. + lt_cv_deplibs_check_method='file_magic (FreeBSD|OpenBSD|DragonFly)/i[[3-9]]86 (compact )?demand paged shared library' + lt_cv_file_magic_cmd=/usr/bin/file + lt_cv_file_magic_test_file=`echo /usr/lib/libc.so.*` + ;; + esac + else + lt_cv_deplibs_check_method=pass_all + fi + ;; + +haiku*) + lt_cv_deplibs_check_method=pass_all + ;; + +hpux10.20* | hpux11*) + lt_cv_file_magic_cmd=/usr/bin/file + case $host_cpu in + ia64*) + lt_cv_deplibs_check_method='file_magic (s[[0-9]][[0-9]][[0-9]]|ELF-[[0-9]][[0-9]]) shared object file - IA64' + lt_cv_file_magic_test_file=/usr/lib/hpux32/libc.so + ;; + hppa*64*) + [lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|ELF[ -][0-9][0-9])(-bit)?( [LM]SB)? shared object( file)?[, -]* PA-RISC [0-9]\.[0-9]'] + lt_cv_file_magic_test_file=/usr/lib/pa20_64/libc.sl + ;; + *) + lt_cv_deplibs_check_method='file_magic (s[[0-9]][[0-9]][[0-9]]|PA-RISC[[0-9]]\.[[0-9]]) shared library' + lt_cv_file_magic_test_file=/usr/lib/libc.sl + ;; + esac + ;; + +interix[[3-9]]*) + # PIC code is broken on Interix 3.x, that's why |\.a not |_pic\.a here + lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so|\.a)$' + ;; + +irix5* | irix6* | nonstopux*) + case $LD in + *-32|*"-32 ") libmagic=32-bit;; + *-n32|*"-n32 ") libmagic=N32;; + *-64|*"-64 ") libmagic=64-bit;; + *) libmagic=never-match;; + esac + lt_cv_deplibs_check_method=pass_all + ;; + +# This must be glibc/ELF. +linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) + lt_cv_deplibs_check_method=pass_all + ;; + +netbsd* | netbsdelf*-gnu) + if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then + lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so\.[[0-9]]+\.[[0-9]]+|_pic\.a)$' + else + lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so|_pic\.a)$' + fi + ;; + +newos6*) + lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[ML]]SB (executable|dynamic lib)' + lt_cv_file_magic_cmd=/usr/bin/file + lt_cv_file_magic_test_file=/usr/lib/libnls.so + ;; + +*nto* | *qnx*) + lt_cv_deplibs_check_method=pass_all + ;; + +openbsd*) + if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then + lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so\.[[0-9]]+\.[[0-9]]+|\.so|_pic\.a)$' + else + lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so\.[[0-9]]+\.[[0-9]]+|_pic\.a)$' + fi + ;; + +osf3* | osf4* | osf5*) + lt_cv_deplibs_check_method=pass_all + ;; + +rdos*) + lt_cv_deplibs_check_method=pass_all + ;; + +solaris*) + lt_cv_deplibs_check_method=pass_all + ;; + +sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*) + lt_cv_deplibs_check_method=pass_all + ;; + +sysv4 | sysv4.3*) + case $host_vendor in + motorola) + lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[ML]]SB (shared object|dynamic lib) M[[0-9]][[0-9]]* Version [[0-9]]' + lt_cv_file_magic_test_file=`echo /usr/lib/libc.so*` + ;; + ncr) + lt_cv_deplibs_check_method=pass_all + ;; + sequent) + lt_cv_file_magic_cmd='/bin/file' + lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[LM]]SB (shared object|dynamic lib )' + ;; + sni) + lt_cv_file_magic_cmd='/bin/file' + lt_cv_deplibs_check_method="file_magic ELF [[0-9]][[0-9]]*-bit [[LM]]SB dynamic lib" + lt_cv_file_magic_test_file=/lib/libc.so + ;; + siemens) + lt_cv_deplibs_check_method=pass_all + ;; + pc) + lt_cv_deplibs_check_method=pass_all + ;; + esac + ;; + +tpf*) + lt_cv_deplibs_check_method=pass_all + ;; +esac +]) + +file_magic_glob= +want_nocaseglob=no +if test "$build" = "$host"; then + case $host_os in + mingw* | pw32*) + if ( shopt | grep nocaseglob ) >/dev/null 2>&1; then + want_nocaseglob=yes + else + file_magic_glob=`echo aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ | $SED -e "s/\(..\)/s\/[[\1]]\/[[\1]]\/g;/g"` + fi + ;; + esac +fi + +file_magic_cmd=$lt_cv_file_magic_cmd +deplibs_check_method=$lt_cv_deplibs_check_method +test -z "$deplibs_check_method" && deplibs_check_method=unknown + +_LT_DECL([], [deplibs_check_method], [1], + [Method to check whether dependent libraries are shared objects]) +_LT_DECL([], [file_magic_cmd], [1], + [Command to use when deplibs_check_method = "file_magic"]) +_LT_DECL([], [file_magic_glob], [1], + [How to find potential files when deplibs_check_method = "file_magic"]) +_LT_DECL([], [want_nocaseglob], [1], + [Find potential files using nocaseglob when deplibs_check_method = "file_magic"]) +])# _LT_CHECK_MAGIC_METHOD + + +# LT_PATH_NM +# ---------- +# find the pathname to a BSD- or MS-compatible name lister +AC_DEFUN([LT_PATH_NM], +[AC_REQUIRE([AC_PROG_CC])dnl +AC_CACHE_CHECK([for BSD- or MS-compatible name lister (nm)], lt_cv_path_NM, +[if test -n "$NM"; then + # Let the user override the test. + lt_cv_path_NM="$NM" +else + lt_nm_to_check="${ac_tool_prefix}nm" + if test -n "$ac_tool_prefix" && test "$build" = "$host"; then + lt_nm_to_check="$lt_nm_to_check nm" + fi + for lt_tmp_nm in $lt_nm_to_check; do + lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR + for ac_dir in $PATH /usr/ccs/bin/elf /usr/ccs/bin /usr/ucb /bin; do + IFS="$lt_save_ifs" + test -z "$ac_dir" && ac_dir=. + tmp_nm="$ac_dir/$lt_tmp_nm" + if test -f "$tmp_nm" || test -f "$tmp_nm$ac_exeext" ; then + # Check to see if the nm accepts a BSD-compat flag. + # Adding the `sed 1q' prevents false positives on HP-UX, which says: + # nm: unknown option "B" ignored + # Tru64's nm complains that /dev/null is an invalid object file + case `"$tmp_nm" -B /dev/null 2>&1 | sed '1q'` in + */dev/null* | *'Invalid file or object type'*) + lt_cv_path_NM="$tmp_nm -B" + break + ;; + *) + case `"$tmp_nm" -p /dev/null 2>&1 | sed '1q'` in + */dev/null*) + lt_cv_path_NM="$tmp_nm -p" + break + ;; + *) + lt_cv_path_NM=${lt_cv_path_NM="$tmp_nm"} # keep the first match, but + continue # so that we can try to find one that supports BSD flags + ;; + esac + ;; + esac + fi + done + IFS="$lt_save_ifs" + done + : ${lt_cv_path_NM=no} +fi]) +if test "$lt_cv_path_NM" != "no"; then + NM="$lt_cv_path_NM" +else + # Didn't find any BSD compatible name lister, look for dumpbin. + if test -n "$DUMPBIN"; then : + # Let the user override the test. + else + AC_CHECK_TOOLS(DUMPBIN, [dumpbin "link -dump"], :) + case `$DUMPBIN -symbols /dev/null 2>&1 | sed '1q'` in + *COFF*) + DUMPBIN="$DUMPBIN -symbols" + ;; + *) + DUMPBIN=: + ;; + esac + fi + AC_SUBST([DUMPBIN]) + if test "$DUMPBIN" != ":"; then + NM="$DUMPBIN" + fi +fi +test -z "$NM" && NM=nm +AC_SUBST([NM]) +_LT_DECL([], [NM], [1], [A BSD- or MS-compatible name lister])dnl + +AC_CACHE_CHECK([the name lister ($NM) interface], [lt_cv_nm_interface], + [lt_cv_nm_interface="BSD nm" + echo "int some_variable = 0;" > conftest.$ac_ext + (eval echo "\"\$as_me:$LINENO: $ac_compile\"" >&AS_MESSAGE_LOG_FD) + (eval "$ac_compile" 2>conftest.err) + cat conftest.err >&AS_MESSAGE_LOG_FD + (eval echo "\"\$as_me:$LINENO: $NM \\\"conftest.$ac_objext\\\"\"" >&AS_MESSAGE_LOG_FD) + (eval "$NM \"conftest.$ac_objext\"" 2>conftest.err > conftest.out) + cat conftest.err >&AS_MESSAGE_LOG_FD + (eval echo "\"\$as_me:$LINENO: output\"" >&AS_MESSAGE_LOG_FD) + cat conftest.out >&AS_MESSAGE_LOG_FD + if $GREP 'External.*some_variable' conftest.out > /dev/null; then + lt_cv_nm_interface="MS dumpbin" + fi + rm -f conftest*]) +])# LT_PATH_NM + +# Old names: +AU_ALIAS([AM_PROG_NM], [LT_PATH_NM]) +AU_ALIAS([AC_PROG_NM], [LT_PATH_NM]) +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AM_PROG_NM], []) +dnl AC_DEFUN([AC_PROG_NM], []) + +# _LT_CHECK_SHAREDLIB_FROM_LINKLIB +# -------------------------------- +# how to determine the name of the shared library +# associated with a specific link library. +# -- PORTME fill in with the dynamic library characteristics +m4_defun([_LT_CHECK_SHAREDLIB_FROM_LINKLIB], +[m4_require([_LT_DECL_EGREP]) +m4_require([_LT_DECL_OBJDUMP]) +m4_require([_LT_DECL_DLLTOOL]) +AC_CACHE_CHECK([how to associate runtime and link libraries], +lt_cv_sharedlib_from_linklib_cmd, +[lt_cv_sharedlib_from_linklib_cmd='unknown' + +case $host_os in +cygwin* | mingw* | pw32* | cegcc*) + # two different shell functions defined in ltmain.sh + # decide which to use based on capabilities of $DLLTOOL + case `$DLLTOOL --help 2>&1` in + *--identify-strict*) + lt_cv_sharedlib_from_linklib_cmd=func_cygming_dll_for_implib + ;; + *) + lt_cv_sharedlib_from_linklib_cmd=func_cygming_dll_for_implib_fallback + ;; + esac + ;; +*) + # fallback: assume linklib IS sharedlib + lt_cv_sharedlib_from_linklib_cmd="$ECHO" + ;; +esac +]) +sharedlib_from_linklib_cmd=$lt_cv_sharedlib_from_linklib_cmd +test -z "$sharedlib_from_linklib_cmd" && sharedlib_from_linklib_cmd=$ECHO + +_LT_DECL([], [sharedlib_from_linklib_cmd], [1], + [Command to associate shared and link libraries]) +])# _LT_CHECK_SHAREDLIB_FROM_LINKLIB + + +# _LT_PATH_MANIFEST_TOOL +# ---------------------- +# locate the manifest tool +m4_defun([_LT_PATH_MANIFEST_TOOL], +[AC_CHECK_TOOL(MANIFEST_TOOL, mt, :) +test -z "$MANIFEST_TOOL" && MANIFEST_TOOL=mt +AC_CACHE_CHECK([if $MANIFEST_TOOL is a manifest tool], [lt_cv_path_mainfest_tool], + [lt_cv_path_mainfest_tool=no + echo "$as_me:$LINENO: $MANIFEST_TOOL '-?'" >&AS_MESSAGE_LOG_FD + $MANIFEST_TOOL '-?' 2>conftest.err > conftest.out + cat conftest.err >&AS_MESSAGE_LOG_FD + if $GREP 'Manifest Tool' conftest.out > /dev/null; then + lt_cv_path_mainfest_tool=yes + fi + rm -f conftest*]) +if test "x$lt_cv_path_mainfest_tool" != xyes; then + MANIFEST_TOOL=: +fi +_LT_DECL([], [MANIFEST_TOOL], [1], [Manifest tool])dnl +])# _LT_PATH_MANIFEST_TOOL + + +# LT_LIB_M +# -------- +# check for math library +AC_DEFUN([LT_LIB_M], +[AC_REQUIRE([AC_CANONICAL_HOST])dnl +LIBM= +case $host in +*-*-beos* | *-*-cegcc* | *-*-cygwin* | *-*-haiku* | *-*-pw32* | *-*-darwin*) + # These system don't have libm, or don't need it + ;; +*-ncr-sysv4.3*) + AC_CHECK_LIB(mw, _mwvalidcheckl, LIBM="-lmw") + AC_CHECK_LIB(m, cos, LIBM="$LIBM -lm") + ;; +*) + AC_CHECK_LIB(m, cos, LIBM="-lm") + ;; +esac +AC_SUBST([LIBM]) +])# LT_LIB_M + +# Old name: +AU_ALIAS([AC_CHECK_LIBM], [LT_LIB_M]) +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AC_CHECK_LIBM], []) + + +# _LT_COMPILER_NO_RTTI([TAGNAME]) +# ------------------------------- +m4_defun([_LT_COMPILER_NO_RTTI], +[m4_require([_LT_TAG_COMPILER])dnl + +_LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)= + +if test "$GCC" = yes; then + case $cc_basename in + nvcc*) + _LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)=' -Xcompiler -fno-builtin' ;; + *) + _LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)=' -fno-builtin' ;; + esac + + _LT_COMPILER_OPTION([if $compiler supports -fno-rtti -fno-exceptions], + lt_cv_prog_compiler_rtti_exceptions, + [-fno-rtti -fno-exceptions], [], + [_LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)="$_LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1) -fno-rtti -fno-exceptions"]) +fi +_LT_TAGDECL([no_builtin_flag], [lt_prog_compiler_no_builtin_flag], [1], + [Compiler flag to turn off builtin functions]) +])# _LT_COMPILER_NO_RTTI + + +# _LT_CMD_GLOBAL_SYMBOLS +# ---------------------- +m4_defun([_LT_CMD_GLOBAL_SYMBOLS], +[AC_REQUIRE([AC_CANONICAL_HOST])dnl +AC_REQUIRE([AC_PROG_CC])dnl +AC_REQUIRE([AC_PROG_AWK])dnl +AC_REQUIRE([LT_PATH_NM])dnl +AC_REQUIRE([LT_PATH_LD])dnl +m4_require([_LT_DECL_SED])dnl +m4_require([_LT_DECL_EGREP])dnl +m4_require([_LT_TAG_COMPILER])dnl + +# Check for command to grab the raw symbol name followed by C symbol from nm. +AC_MSG_CHECKING([command to parse $NM output from $compiler object]) +AC_CACHE_VAL([lt_cv_sys_global_symbol_pipe], +[ +# These are sane defaults that work on at least a few old systems. +# [They come from Ultrix. What could be older than Ultrix?!! ;)] + +# Character class describing NM global symbol codes. +symcode='[[BCDEGRST]]' + +# Regexp to match symbols that can be accessed directly from C. +sympat='\([[_A-Za-z]][[_A-Za-z0-9]]*\)' + +# Define system-specific variables. +case $host_os in +aix*) + symcode='[[BCDT]]' + ;; +cygwin* | mingw* | pw32* | cegcc*) + symcode='[[ABCDGISTW]]' + ;; +hpux*) + if test "$host_cpu" = ia64; then + symcode='[[ABCDEGRST]]' + fi + ;; +irix* | nonstopux*) + symcode='[[BCDEGRST]]' + ;; +osf*) + symcode='[[BCDEGQRST]]' + ;; +solaris*) + symcode='[[BDRT]]' + ;; +sco3.2v5*) + symcode='[[DT]]' + ;; +sysv4.2uw2*) + symcode='[[DT]]' + ;; +sysv5* | sco5v6* | unixware* | OpenUNIX*) + symcode='[[ABDT]]' + ;; +sysv4) + symcode='[[DFNSTU]]' + ;; +esac + +# If we're using GNU nm, then use its standard symbol codes. +case `$NM -V 2>&1` in +*GNU* | *'with BFD'*) + symcode='[[ABCDGIRSTW]]' ;; +esac + +# Transform an extracted symbol line into a proper C declaration. +# Some systems (esp. on ia64) link data and code symbols differently, +# so use this general approach. +lt_cv_sys_global_symbol_to_cdecl="sed -n -e 's/^T .* \(.*\)$/extern int \1();/p' -e 's/^$symcode* .* \(.*\)$/extern char \1;/p'" + +# Transform an extracted symbol line into symbol name and symbol address +lt_cv_sys_global_symbol_to_c_name_address="sed -n -e 's/^: \([[^ ]]*\)[[ ]]*$/ {\\\"\1\\\", (void *) 0},/p' -e 's/^$symcode* \([[^ ]]*\) \([[^ ]]*\)$/ {\"\2\", (void *) \&\2},/p'" +lt_cv_sys_global_symbol_to_c_name_address_lib_prefix="sed -n -e 's/^: \([[^ ]]*\)[[ ]]*$/ {\\\"\1\\\", (void *) 0},/p' -e 's/^$symcode* \([[^ ]]*\) \(lib[[^ ]]*\)$/ {\"\2\", (void *) \&\2},/p' -e 's/^$symcode* \([[^ ]]*\) \([[^ ]]*\)$/ {\"lib\2\", (void *) \&\2},/p'" + +# Handle CRLF in mingw tool chain +opt_cr= +case $build_os in +mingw*) + opt_cr=`$ECHO 'x\{0,1\}' | tr x '\015'` # option cr in regexp + ;; +esac + +# Try without a prefix underscore, then with it. +for ac_symprfx in "" "_"; do + + # Transform symcode, sympat, and symprfx into a raw symbol and a C symbol. + symxfrm="\\1 $ac_symprfx\\2 \\2" + + # Write the raw and C identifiers. + if test "$lt_cv_nm_interface" = "MS dumpbin"; then + # Fake it for dumpbin and say T for any non-static function + # and D for any global variable. + # Also find C++ and __fastcall symbols from MSVC++, + # which start with @ or ?. + lt_cv_sys_global_symbol_pipe="$AWK ['"\ +" {last_section=section; section=\$ 3};"\ +" /^COFF SYMBOL TABLE/{for(i in hide) delete hide[i]};"\ +" /Section length .*#relocs.*(pick any)/{hide[last_section]=1};"\ +" \$ 0!~/External *\|/{next};"\ +" / 0+ UNDEF /{next}; / UNDEF \([^|]\)*()/{next};"\ +" {if(hide[section]) next};"\ +" {f=0}; \$ 0~/\(\).*\|/{f=1}; {printf f ? \"T \" : \"D \"};"\ +" {split(\$ 0, a, /\||\r/); split(a[2], s)};"\ +" s[1]~/^[@?]/{print s[1], s[1]; next};"\ +" s[1]~prfx {split(s[1],t,\"@\"); print t[1], substr(t[1],length(prfx))}"\ +" ' prfx=^$ac_symprfx]" + else + lt_cv_sys_global_symbol_pipe="sed -n -e 's/^.*[[ ]]\($symcode$symcode*\)[[ ]][[ ]]*$ac_symprfx$sympat$opt_cr$/$symxfrm/p'" + fi + lt_cv_sys_global_symbol_pipe="$lt_cv_sys_global_symbol_pipe | sed '/ __gnu_lto/d'" + + # Check to see that the pipe works correctly. + pipe_works=no + + rm -f conftest* + cat > conftest.$ac_ext <<_LT_EOF +#ifdef __cplusplus +extern "C" { +#endif +char nm_test_var; +void nm_test_func(void); +void nm_test_func(void){} +#ifdef __cplusplus +} +#endif +int main(){nm_test_var='a';nm_test_func();return(0);} +_LT_EOF + + if AC_TRY_EVAL(ac_compile); then + # Now try to grab the symbols. + nlist=conftest.nm + if AC_TRY_EVAL(NM conftest.$ac_objext \| "$lt_cv_sys_global_symbol_pipe" \> $nlist) && test -s "$nlist"; then + # Try sorting and uniquifying the output. + if sort "$nlist" | uniq > "$nlist"T; then + mv -f "$nlist"T "$nlist" + else + rm -f "$nlist"T + fi + + # Make sure that we snagged all the symbols we need. + if $GREP ' nm_test_var$' "$nlist" >/dev/null; then + if $GREP ' nm_test_func$' "$nlist" >/dev/null; then + cat <<_LT_EOF > conftest.$ac_ext +/* Keep this code in sync between libtool.m4, ltmain, lt_system.h, and tests. */ +#if defined(_WIN32) || defined(__CYGWIN__) || defined(_WIN32_WCE) +/* DATA imports from DLLs on WIN32 con't be const, because runtime + relocations are performed -- see ld's documentation on pseudo-relocs. */ +# define LT@&t@_DLSYM_CONST +#elif defined(__osf__) +/* This system does not cope well with relocations in const data. */ +# define LT@&t@_DLSYM_CONST +#else +# define LT@&t@_DLSYM_CONST const +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +_LT_EOF + # Now generate the symbol file. + eval "$lt_cv_sys_global_symbol_to_cdecl"' < "$nlist" | $GREP -v main >> conftest.$ac_ext' + + cat <<_LT_EOF >> conftest.$ac_ext + +/* The mapping between symbol names and symbols. */ +LT@&t@_DLSYM_CONST struct { + const char *name; + void *address; +} +lt__PROGRAM__LTX_preloaded_symbols[[]] = +{ + { "@PROGRAM@", (void *) 0 }, +_LT_EOF + $SED "s/^$symcode$symcode* \(.*\) \(.*\)$/ {\"\2\", (void *) \&\2},/" < "$nlist" | $GREP -v main >> conftest.$ac_ext + cat <<\_LT_EOF >> conftest.$ac_ext + {0, (void *) 0} +}; + +/* This works around a problem in FreeBSD linker */ +#ifdef FREEBSD_WORKAROUND +static const void *lt_preloaded_setup() { + return lt__PROGRAM__LTX_preloaded_symbols; +} +#endif + +#ifdef __cplusplus +} +#endif +_LT_EOF + # Now try linking the two files. + mv conftest.$ac_objext conftstm.$ac_objext + lt_globsym_save_LIBS=$LIBS + lt_globsym_save_CFLAGS=$CFLAGS + LIBS="conftstm.$ac_objext" + CFLAGS="$CFLAGS$_LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)" + if AC_TRY_EVAL(ac_link) && test -s conftest${ac_exeext}; then + pipe_works=yes + fi + LIBS=$lt_globsym_save_LIBS + CFLAGS=$lt_globsym_save_CFLAGS + else + echo "cannot find nm_test_func in $nlist" >&AS_MESSAGE_LOG_FD + fi + else + echo "cannot find nm_test_var in $nlist" >&AS_MESSAGE_LOG_FD + fi + else + echo "cannot run $lt_cv_sys_global_symbol_pipe" >&AS_MESSAGE_LOG_FD + fi + else + echo "$progname: failed program was:" >&AS_MESSAGE_LOG_FD + cat conftest.$ac_ext >&5 + fi + rm -rf conftest* conftst* + + # Do not use the global_symbol_pipe unless it works. + if test "$pipe_works" = yes; then + break + else + lt_cv_sys_global_symbol_pipe= + fi +done +]) +if test -z "$lt_cv_sys_global_symbol_pipe"; then + lt_cv_sys_global_symbol_to_cdecl= +fi +if test -z "$lt_cv_sys_global_symbol_pipe$lt_cv_sys_global_symbol_to_cdecl"; then + AC_MSG_RESULT(failed) +else + AC_MSG_RESULT(ok) +fi + +# Response file support. +if test "$lt_cv_nm_interface" = "MS dumpbin"; then + nm_file_list_spec='@' +elif $NM --help 2>/dev/null | grep '[[@]]FILE' >/dev/null; then + nm_file_list_spec='@' +fi + +_LT_DECL([global_symbol_pipe], [lt_cv_sys_global_symbol_pipe], [1], + [Take the output of nm and produce a listing of raw symbols and C names]) +_LT_DECL([global_symbol_to_cdecl], [lt_cv_sys_global_symbol_to_cdecl], [1], + [Transform the output of nm in a proper C declaration]) +_LT_DECL([global_symbol_to_c_name_address], + [lt_cv_sys_global_symbol_to_c_name_address], [1], + [Transform the output of nm in a C name address pair]) +_LT_DECL([global_symbol_to_c_name_address_lib_prefix], + [lt_cv_sys_global_symbol_to_c_name_address_lib_prefix], [1], + [Transform the output of nm in a C name address pair when lib prefix is needed]) +_LT_DECL([], [nm_file_list_spec], [1], + [Specify filename containing input files for $NM]) +]) # _LT_CMD_GLOBAL_SYMBOLS + + +# _LT_COMPILER_PIC([TAGNAME]) +# --------------------------- +m4_defun([_LT_COMPILER_PIC], +[m4_require([_LT_TAG_COMPILER])dnl +_LT_TAGVAR(lt_prog_compiler_wl, $1)= +_LT_TAGVAR(lt_prog_compiler_pic, $1)= +_LT_TAGVAR(lt_prog_compiler_static, $1)= + +m4_if([$1], [CXX], [ + # C++ specific cases for pic, static, wl, etc. + if test "$GXX" = yes; then + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' + + case $host_os in + aix*) + # All AIX code is PIC. + if test "$host_cpu" = ia64; then + # AIX 5 now supports IA64 processor + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + fi + ;; + + amigaos*) + case $host_cpu in + powerpc) + # see comment about AmigaOS4 .so support + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' + ;; + m68k) + # FIXME: we need at least 68020 code to build shared libraries, but + # adding the `-m68020' flag to GCC prevents building anything better, + # like `-m68040'. + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-m68020 -resident32 -malways-restore-a4' + ;; + esac + ;; + + beos* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*) + # PIC is the default for these OSes. + ;; + mingw* | cygwin* | os2* | pw32* | cegcc*) + # This hack is so that the source file can tell whether it is being + # built for inclusion in a dll (and should export symbols for example). + # Although the cygwin gcc ignores -fPIC, still need this for old-style + # (--disable-auto-import) libraries + m4_if([$1], [GCJ], [], + [_LT_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT']) + ;; + darwin* | rhapsody*) + # PIC is the default on this platform + # Common symbols not allowed in MH_DYLIB files + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fno-common' + ;; + *djgpp*) + # DJGPP does not support shared libraries at all + _LT_TAGVAR(lt_prog_compiler_pic, $1)= + ;; + haiku*) + # PIC is the default for Haiku. + # The "-static" flag exists, but is broken. + _LT_TAGVAR(lt_prog_compiler_static, $1)= + ;; + interix[[3-9]]*) + # Interix 3.x gcc -fpic/-fPIC options generate broken code. + # Instead, we relocate shared libraries at runtime. + ;; + sysv4*MP*) + if test -d /usr/nec; then + _LT_TAGVAR(lt_prog_compiler_pic, $1)=-Kconform_pic + fi + ;; + hpux*) + # PIC is the default for 64-bit PA HP-UX, but not for 32-bit + # PA HP-UX. On IA64 HP-UX, PIC is the default but the pic flag + # sets the default TLS model and affects inlining. + case $host_cpu in + hppa*64*) + ;; + *) + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' + ;; + esac + ;; + *qnx* | *nto*) + # QNX uses GNU C++, but need to define -shared option too, otherwise + # it will coredump. + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC -shared' + ;; + *) + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' + ;; + esac + else + case $host_os in + aix[[4-9]]*) + # All AIX code is PIC. + if test "$host_cpu" = ia64; then + # AIX 5 now supports IA64 processor + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + else + _LT_TAGVAR(lt_prog_compiler_static, $1)='-bnso -bI:/lib/syscalls.exp' + fi + ;; + chorus*) + case $cc_basename in + cxch68*) + # Green Hills C++ Compiler + # _LT_TAGVAR(lt_prog_compiler_static, $1)="--no_auto_instantiation -u __main -u __premain -u _abort -r $COOL_DIR/lib/libOrb.a $MVME_DIR/lib/CC/libC.a $MVME_DIR/lib/classix/libcx.s.a" + ;; + esac + ;; + mingw* | cygwin* | os2* | pw32* | cegcc*) + # This hack is so that the source file can tell whether it is being + # built for inclusion in a dll (and should export symbols for example). + m4_if([$1], [GCJ], [], + [_LT_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT']) + ;; + dgux*) + case $cc_basename in + ec++*) + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + ;; + ghcx*) + # Green Hills C++ Compiler + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic' + ;; + *) + ;; + esac + ;; + freebsd* | dragonfly*) + # FreeBSD uses GNU C++ + ;; + hpux9* | hpux10* | hpux11*) + case $cc_basename in + CC*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_static, $1)='${wl}-a ${wl}archive' + if test "$host_cpu" != ia64; then + _LT_TAGVAR(lt_prog_compiler_pic, $1)='+Z' + fi + ;; + aCC*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_static, $1)='${wl}-a ${wl}archive' + case $host_cpu in + hppa*64*|ia64*) + # +Z the default + ;; + *) + _LT_TAGVAR(lt_prog_compiler_pic, $1)='+Z' + ;; + esac + ;; + *) + ;; + esac + ;; + interix*) + # This is c89, which is MS Visual C++ (no shared libs) + # Anyone wants to do a port? + ;; + irix5* | irix6* | nonstopux*) + case $cc_basename in + CC*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' + # CC pic flag -KPIC is the default. + ;; + *) + ;; + esac + ;; + linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) + case $cc_basename in + KCC*) + # KAI C++ Compiler + _LT_TAGVAR(lt_prog_compiler_wl, $1)='--backend -Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' + ;; + ecpc* ) + # old Intel C++ for x86_64 which still supported -KPIC. + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' + ;; + icpc* ) + # Intel C++, used to be incompatible with GCC. + # ICC 10 doesn't accept -KPIC any more. + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' + ;; + pgCC* | pgcpp*) + # Portland Group C++ compiler + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fpic' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + ;; + cxx*) + # Compaq C++ + # Make sure the PIC flag is empty. It appears that all Alpha + # Linux and Compaq Tru64 Unix objects are PIC. + _LT_TAGVAR(lt_prog_compiler_pic, $1)= + _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' + ;; + xlc* | xlC* | bgxl[[cC]]* | mpixl[[cC]]*) + # IBM XL 8.0, 9.0 on PPC and BlueGene + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-qpic' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-qstaticlink' + ;; + *) + case `$CC -V 2>&1 | sed 5q` in + *Sun\ C*) + # Sun C++ 5.9 + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld ' + ;; + esac + ;; + esac + ;; + lynxos*) + ;; + m88k*) + ;; + mvs*) + case $cc_basename in + cxx*) + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-W c,exportall' + ;; + *) + ;; + esac + ;; + netbsd* | netbsdelf*-gnu) + ;; + *qnx* | *nto*) + # QNX uses GNU C++, but need to define -shared option too, otherwise + # it will coredump. + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC -shared' + ;; + osf3* | osf4* | osf5*) + case $cc_basename in + KCC*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='--backend -Wl,' + ;; + RCC*) + # Rational C++ 2.4.1 + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic' + ;; + cxx*) + # Digital/Compaq C++ + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + # Make sure the PIC flag is empty. It appears that all Alpha + # Linux and Compaq Tru64 Unix objects are PIC. + _LT_TAGVAR(lt_prog_compiler_pic, $1)= + _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' + ;; + *) + ;; + esac + ;; + psos*) + ;; + solaris*) + case $cc_basename in + CC* | sunCC*) + # Sun C++ 4.2, 5.x and Centerline C++ + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld ' + ;; + gcx*) + # Green Hills C++ Compiler + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-PIC' + ;; + *) + ;; + esac + ;; + sunos4*) + case $cc_basename in + CC*) + # Sun C++ 4.x + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + ;; + lcc*) + # Lucid + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic' + ;; + *) + ;; + esac + ;; + sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*) + case $cc_basename in + CC*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + ;; + esac + ;; + tandem*) + case $cc_basename in + NCC*) + # NonStop-UX NCC 3.20 + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + ;; + *) + ;; + esac + ;; + vxworks*) + ;; + *) + _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no + ;; + esac + fi +], +[ + if test "$GCC" = yes; then + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' + + case $host_os in + aix*) + # All AIX code is PIC. + if test "$host_cpu" = ia64; then + # AIX 5 now supports IA64 processor + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + fi + ;; + + amigaos*) + case $host_cpu in + powerpc) + # see comment about AmigaOS4 .so support + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' + ;; + m68k) + # FIXME: we need at least 68020 code to build shared libraries, but + # adding the `-m68020' flag to GCC prevents building anything better, + # like `-m68040'. + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-m68020 -resident32 -malways-restore-a4' + ;; + esac + ;; + + beos* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*) + # PIC is the default for these OSes. + ;; + + mingw* | cygwin* | pw32* | os2* | cegcc*) + # This hack is so that the source file can tell whether it is being + # built for inclusion in a dll (and should export symbols for example). + # Although the cygwin gcc ignores -fPIC, still need this for old-style + # (--disable-auto-import) libraries + m4_if([$1], [GCJ], [], + [_LT_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT']) + ;; + + darwin* | rhapsody*) + # PIC is the default on this platform + # Common symbols not allowed in MH_DYLIB files + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fno-common' + ;; + + haiku*) + # PIC is the default for Haiku. + # The "-static" flag exists, but is broken. + _LT_TAGVAR(lt_prog_compiler_static, $1)= + ;; + + hpux*) + # PIC is the default for 64-bit PA HP-UX, but not for 32-bit + # PA HP-UX. On IA64 HP-UX, PIC is the default but the pic flag + # sets the default TLS model and affects inlining. + case $host_cpu in + hppa*64*) + # +Z the default + ;; + *) + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' + ;; + esac + ;; + + interix[[3-9]]*) + # Interix 3.x gcc -fpic/-fPIC options generate broken code. + # Instead, we relocate shared libraries at runtime. + ;; + + msdosdjgpp*) + # Just because we use GCC doesn't mean we suddenly get shared libraries + # on systems that don't support them. + _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no + enable_shared=no + ;; + + *nto* | *qnx*) + # QNX uses GNU C++, but need to define -shared option too, otherwise + # it will coredump. + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC -shared' + ;; + + sysv4*MP*) + if test -d /usr/nec; then + _LT_TAGVAR(lt_prog_compiler_pic, $1)=-Kconform_pic + fi + ;; + + *) + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' + ;; + esac + + case $cc_basename in + nvcc*) # Cuda Compiler Driver 2.2 + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Xlinker ' + if test -n "$_LT_TAGVAR(lt_prog_compiler_pic, $1)"; then + _LT_TAGVAR(lt_prog_compiler_pic, $1)="-Xcompiler $_LT_TAGVAR(lt_prog_compiler_pic, $1)" + fi + ;; + esac + else + # PORTME Check for flag to pass linker flags through the system compiler. + case $host_os in + aix*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + if test "$host_cpu" = ia64; then + # AIX 5 now supports IA64 processor + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + else + _LT_TAGVAR(lt_prog_compiler_static, $1)='-bnso -bI:/lib/syscalls.exp' + fi + ;; + + mingw* | cygwin* | pw32* | os2* | cegcc*) + # This hack is so that the source file can tell whether it is being + # built for inclusion in a dll (and should export symbols for example). + m4_if([$1], [GCJ], [], + [_LT_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT']) + ;; + + hpux9* | hpux10* | hpux11*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + # PIC is the default for IA64 HP-UX and 64-bit HP-UX, but + # not for PA HP-UX. + case $host_cpu in + hppa*64*|ia64*) + # +Z the default + ;; + *) + _LT_TAGVAR(lt_prog_compiler_pic, $1)='+Z' + ;; + esac + # Is there a better lt_prog_compiler_static that works with the bundled CC? + _LT_TAGVAR(lt_prog_compiler_static, $1)='${wl}-a ${wl}archive' + ;; + + irix5* | irix6* | nonstopux*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + # PIC (with -KPIC) is the default. + _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' + ;; + + linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) + case $cc_basename in + # old Intel for x86_64 which still supported -KPIC. + ecc*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' + ;; + # icc used to be incompatible with GCC. + # ICC 10 doesn't accept -KPIC any more. + icc* | ifort*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' + ;; + # Lahey Fortran 8.1. + lf95*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='--shared' + _LT_TAGVAR(lt_prog_compiler_static, $1)='--static' + ;; + nagfor*) + # NAG Fortran compiler + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,-Wl,,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-PIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + ;; + pgcc* | pgf77* | pgf90* | pgf95* | pgfortran*) + # Portland Group compilers (*not* the Pentium gcc compiler, + # which looks to be a dead project) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fpic' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + ;; + ccc*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + # All Alpha code is PIC. + _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' + ;; + xl* | bgxl* | bgf* | mpixl*) + # IBM XL C 8.0/Fortran 10.1, 11.1 on PPC and BlueGene + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-qpic' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-qstaticlink' + ;; + *) + case `$CC -V 2>&1 | sed 5q` in + *Sun\ Ceres\ Fortran* | *Sun*Fortran*\ [[1-7]].* | *Sun*Fortran*\ 8.[[0-3]]*) + # Sun Fortran 8.3 passes all unrecognized flags to the linker + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + _LT_TAGVAR(lt_prog_compiler_wl, $1)='' + ;; + *Sun\ F* | *Sun*Fortran*) + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld ' + ;; + *Sun\ C*) + # Sun C 5.9 + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + ;; + *Intel*\ [[CF]]*Compiler*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' + ;; + *Portland\ Group*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fpic' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + ;; + esac + ;; + esac + ;; + + newsos6) + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + ;; + + *nto* | *qnx*) + # QNX uses GNU C++, but need to define -shared option too, otherwise + # it will coredump. + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC -shared' + ;; + + osf3* | osf4* | osf5*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + # All OSF/1 code is PIC. + _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' + ;; + + rdos*) + _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' + ;; + + solaris*) + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + case $cc_basename in + f77* | f90* | f95* | sunf77* | sunf90* | sunf95*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld ';; + *) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,';; + esac + ;; + + sunos4*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld ' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-PIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + ;; + + sysv4 | sysv4.2uw2* | sysv4.3*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + ;; + + sysv4*MP*) + if test -d /usr/nec ;then + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-Kconform_pic' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + fi + ;; + + sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + ;; + + unicos*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no + ;; + + uts4*) + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + ;; + + *) + _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no + ;; + esac + fi +]) +case $host_os in + # For platforms which do not support PIC, -DPIC is meaningless: + *djgpp*) + _LT_TAGVAR(lt_prog_compiler_pic, $1)= + ;; + *) + _LT_TAGVAR(lt_prog_compiler_pic, $1)="$_LT_TAGVAR(lt_prog_compiler_pic, $1)@&t@m4_if([$1],[],[ -DPIC],[m4_if([$1],[CXX],[ -DPIC],[])])" + ;; +esac + +AC_CACHE_CHECK([for $compiler option to produce PIC], + [_LT_TAGVAR(lt_cv_prog_compiler_pic, $1)], + [_LT_TAGVAR(lt_cv_prog_compiler_pic, $1)=$_LT_TAGVAR(lt_prog_compiler_pic, $1)]) +_LT_TAGVAR(lt_prog_compiler_pic, $1)=$_LT_TAGVAR(lt_cv_prog_compiler_pic, $1) + +# +# Check to make sure the PIC flag actually works. +# +if test -n "$_LT_TAGVAR(lt_prog_compiler_pic, $1)"; then + _LT_COMPILER_OPTION([if $compiler PIC flag $_LT_TAGVAR(lt_prog_compiler_pic, $1) works], + [_LT_TAGVAR(lt_cv_prog_compiler_pic_works, $1)], + [$_LT_TAGVAR(lt_prog_compiler_pic, $1)@&t@m4_if([$1],[],[ -DPIC],[m4_if([$1],[CXX],[ -DPIC],[])])], [], + [case $_LT_TAGVAR(lt_prog_compiler_pic, $1) in + "" | " "*) ;; + *) _LT_TAGVAR(lt_prog_compiler_pic, $1)=" $_LT_TAGVAR(lt_prog_compiler_pic, $1)" ;; + esac], + [_LT_TAGVAR(lt_prog_compiler_pic, $1)= + _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no]) +fi +_LT_TAGDECL([pic_flag], [lt_prog_compiler_pic], [1], + [Additional compiler flags for building library objects]) + +_LT_TAGDECL([wl], [lt_prog_compiler_wl], [1], + [How to pass a linker flag through the compiler]) +# +# Check to make sure the static flag actually works. +# +wl=$_LT_TAGVAR(lt_prog_compiler_wl, $1) eval lt_tmp_static_flag=\"$_LT_TAGVAR(lt_prog_compiler_static, $1)\" +_LT_LINKER_OPTION([if $compiler static flag $lt_tmp_static_flag works], + _LT_TAGVAR(lt_cv_prog_compiler_static_works, $1), + $lt_tmp_static_flag, + [], + [_LT_TAGVAR(lt_prog_compiler_static, $1)=]) +_LT_TAGDECL([link_static_flag], [lt_prog_compiler_static], [1], + [Compiler flag to prevent dynamic linking]) +])# _LT_COMPILER_PIC + + +# _LT_LINKER_SHLIBS([TAGNAME]) +# ---------------------------- +# See if the linker supports building shared libraries. +m4_defun([_LT_LINKER_SHLIBS], +[AC_REQUIRE([LT_PATH_LD])dnl +AC_REQUIRE([LT_PATH_NM])dnl +m4_require([_LT_PATH_MANIFEST_TOOL])dnl +m4_require([_LT_FILEUTILS_DEFAULTS])dnl +m4_require([_LT_DECL_EGREP])dnl +m4_require([_LT_DECL_SED])dnl +m4_require([_LT_CMD_GLOBAL_SYMBOLS])dnl +m4_require([_LT_TAG_COMPILER])dnl +AC_MSG_CHECKING([whether the $compiler linker ($LD) supports shared libraries]) +m4_if([$1], [CXX], [ + _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' + _LT_TAGVAR(exclude_expsyms, $1)=['_GLOBAL_OFFSET_TABLE_|_GLOBAL__F[ID]_.*'] + case $host_os in + aix[[4-9]]*) + # If we're using GNU nm, then we don't want the "-C" option. + # -C means demangle to AIX nm, but means don't demangle with GNU nm + # Also, AIX nm treats weak defined symbols like other global defined + # symbols, whereas GNU nm marks them as "W". + if $NM -V 2>&1 | $GREP 'GNU' > /dev/null; then + _LT_TAGVAR(export_symbols_cmds, $1)='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W")) && ([substr](\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols' + else + _LT_TAGVAR(export_symbols_cmds, $1)='$NM -BCpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B")) && ([substr](\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols' + fi + ;; + pw32*) + _LT_TAGVAR(export_symbols_cmds, $1)="$ltdll_cmds" + ;; + cygwin* | mingw* | cegcc*) + case $cc_basename in + cl*) + _LT_TAGVAR(exclude_expsyms, $1)='_NULL_IMPORT_DESCRIPTOR|_IMPORT_DESCRIPTOR_.*' + ;; + *) + _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[[BCDGRS]][[ ]]/s/.*[[ ]]\([[^ ]]*\)/\1 DATA/;s/^.*[[ ]]__nm__\([[^ ]]*\)[[ ]][[^ ]]*/\1 DATA/;/^I[[ ]]/d;/^[[AITW]][[ ]]/s/.* //'\'' | sort | uniq > $export_symbols' + _LT_TAGVAR(exclude_expsyms, $1)=['[_]+GLOBAL_OFFSET_TABLE_|[_]+GLOBAL__[FID]_.*|[_]+head_[A-Za-z0-9_]+_dll|[A-Za-z0-9_]+_dll_iname'] + ;; + esac + ;; + linux* | k*bsd*-gnu | gnu*) + _LT_TAGVAR(link_all_deplibs, $1)=no + ;; + *) + _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' + ;; + esac +], [ + runpath_var= + _LT_TAGVAR(allow_undefined_flag, $1)= + _LT_TAGVAR(always_export_symbols, $1)=no + _LT_TAGVAR(archive_cmds, $1)= + _LT_TAGVAR(archive_expsym_cmds, $1)= + _LT_TAGVAR(compiler_needs_object, $1)=no + _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=no + _LT_TAGVAR(export_dynamic_flag_spec, $1)= + _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' + _LT_TAGVAR(hardcode_automatic, $1)=no + _LT_TAGVAR(hardcode_direct, $1)=no + _LT_TAGVAR(hardcode_direct_absolute, $1)=no + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)= + _LT_TAGVAR(hardcode_libdir_separator, $1)= + _LT_TAGVAR(hardcode_minus_L, $1)=no + _LT_TAGVAR(hardcode_shlibpath_var, $1)=unsupported + _LT_TAGVAR(inherit_rpath, $1)=no + _LT_TAGVAR(link_all_deplibs, $1)=unknown + _LT_TAGVAR(module_cmds, $1)= + _LT_TAGVAR(module_expsym_cmds, $1)= + _LT_TAGVAR(old_archive_from_new_cmds, $1)= + _LT_TAGVAR(old_archive_from_expsyms_cmds, $1)= + _LT_TAGVAR(thread_safe_flag_spec, $1)= + _LT_TAGVAR(whole_archive_flag_spec, $1)= + # include_expsyms should be a list of space-separated symbols to be *always* + # included in the symbol list + _LT_TAGVAR(include_expsyms, $1)= + # exclude_expsyms can be an extended regexp of symbols to exclude + # it will be wrapped by ` (' and `)$', so one must not match beginning or + # end of line. Example: `a|bc|.*d.*' will exclude the symbols `a' and `bc', + # as well as any symbol that contains `d'. + _LT_TAGVAR(exclude_expsyms, $1)=['_GLOBAL_OFFSET_TABLE_|_GLOBAL__F[ID]_.*'] + # Although _GLOBAL_OFFSET_TABLE_ is a valid symbol C name, most a.out + # platforms (ab)use it in PIC code, but their linkers get confused if + # the symbol is explicitly referenced. Since portable code cannot + # rely on this symbol name, it's probably fine to never include it in + # preloaded symbol tables. + # Exclude shared library initialization/finalization symbols. +dnl Note also adjust exclude_expsyms for C++ above. + extract_expsyms_cmds= + + case $host_os in + cygwin* | mingw* | pw32* | cegcc*) + # FIXME: the MSVC++ port hasn't been tested in a loooong time + # When not using gcc, we currently assume that we are using + # Microsoft Visual C++. + if test "$GCC" != yes; then + with_gnu_ld=no + fi + ;; + interix*) + # we just hope/assume this is gcc and not c89 (= MSVC++) + with_gnu_ld=yes + ;; + openbsd*) + with_gnu_ld=no + ;; + linux* | k*bsd*-gnu | gnu*) + _LT_TAGVAR(link_all_deplibs, $1)=no + ;; + esac + + _LT_TAGVAR(ld_shlibs, $1)=yes + + # On some targets, GNU ld is compatible enough with the native linker + # that we're better off using the native interface for both. + lt_use_gnu_ld_interface=no + if test "$with_gnu_ld" = yes; then + case $host_os in + aix*) + # The AIX port of GNU ld has always aspired to compatibility + # with the native linker. However, as the warning in the GNU ld + # block says, versions before 2.19.5* couldn't really create working + # shared libraries, regardless of the interface used. + case `$LD -v 2>&1` in + *\ \(GNU\ Binutils\)\ 2.19.5*) ;; + *\ \(GNU\ Binutils\)\ 2.[[2-9]]*) ;; + *\ \(GNU\ Binutils\)\ [[3-9]]*) ;; + *) + lt_use_gnu_ld_interface=yes + ;; + esac + ;; + *) + lt_use_gnu_ld_interface=yes + ;; + esac + fi + + if test "$lt_use_gnu_ld_interface" = yes; then + # If archive_cmds runs LD, not CC, wlarc should be empty + wlarc='${wl}' + + # Set some defaults for GNU ld with shared library support. These + # are reset later if shared libraries are not supported. Putting them + # here allows them to be overridden if necessary. + runpath_var=LD_RUN_PATH + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' + _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic' + # ancient GNU ld didn't support --whole-archive et. al. + if $LD --help 2>&1 | $GREP 'no-whole-archive' > /dev/null; then + _LT_TAGVAR(whole_archive_flag_spec, $1)="$wlarc"'--whole-archive$convenience '"$wlarc"'--no-whole-archive' + else + _LT_TAGVAR(whole_archive_flag_spec, $1)= + fi + supports_anon_versioning=no + case `$LD -v 2>&1` in + *GNU\ gold*) supports_anon_versioning=yes ;; + *\ [[01]].* | *\ 2.[[0-9]].* | *\ 2.10.*) ;; # catch versions < 2.11 + *\ 2.11.93.0.2\ *) supports_anon_versioning=yes ;; # RH7.3 ... + *\ 2.11.92.0.12\ *) supports_anon_versioning=yes ;; # Mandrake 8.2 ... + *\ 2.11.*) ;; # other 2.11 versions + *) supports_anon_versioning=yes ;; + esac + + # See if GNU ld supports shared libraries. + case $host_os in + aix[[3-9]]*) + # On AIX/PPC, the GNU linker is very broken + if test "$host_cpu" != ia64; then + _LT_TAGVAR(ld_shlibs, $1)=no + cat <<_LT_EOF 1>&2 + +*** Warning: the GNU linker, at least up to release 2.19, is reported +*** to be unable to reliably create shared libraries on AIX. +*** Therefore, libtool is disabling shared libraries support. If you +*** really care for shared libraries, you may want to install binutils +*** 2.20 or above, or modify your PATH so that a non-GNU linker is found. +*** You will then need to restart the configuration process. + +_LT_EOF + fi + ;; + + amigaos*) + case $host_cpu in + powerpc) + # see comment about AmigaOS4 .so support + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='' + ;; + m68k) + _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/a2ixlibrary.data~$ECHO "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$ECHO "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$ECHO "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$ECHO "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' + _LT_TAGVAR(hardcode_minus_L, $1)=yes + ;; + esac + ;; + + beos*) + if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then + _LT_TAGVAR(allow_undefined_flag, $1)=unsupported + # Joseph Beckenbach says some releases of gcc + # support --undefined. This deserves some investigation. FIXME + _LT_TAGVAR(archive_cmds, $1)='$CC -nostart $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + else + _LT_TAGVAR(ld_shlibs, $1)=no + fi + ;; + + cygwin* | mingw* | pw32* | cegcc*) + # _LT_TAGVAR(hardcode_libdir_flag_spec, $1) is actually meaningless, + # as there is no search path for DLLs. + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' + _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-all-symbols' + _LT_TAGVAR(allow_undefined_flag, $1)=unsupported + _LT_TAGVAR(always_export_symbols, $1)=no + _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes + _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[[BCDGRS]][[ ]]/s/.*[[ ]]\([[^ ]]*\)/\1 DATA/;s/^.*[[ ]]__nm__\([[^ ]]*\)[[ ]][[^ ]]*/\1 DATA/;/^I[[ ]]/d;/^[[AITW]][[ ]]/s/.* //'\'' | sort | uniq > $export_symbols' + _LT_TAGVAR(exclude_expsyms, $1)=['[_]+GLOBAL_OFFSET_TABLE_|[_]+GLOBAL__[FID]_.*|[_]+head_[A-Za-z0-9_]+_dll|[A-Za-z0-9_]+_dll_iname'] + + if $LD --help 2>&1 | $GREP 'auto-import' > /dev/null; then + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' + # If the export-symbols file already is a .def file (1st line + # is EXPORTS), use it as is; otherwise, prepend... + _LT_TAGVAR(archive_expsym_cmds, $1)='if test "x`$SED 1q $export_symbols`" = xEXPORTS; then + cp $export_symbols $output_objdir/$soname.def; + else + echo EXPORTS > $output_objdir/$soname.def; + cat $export_symbols >> $output_objdir/$soname.def; + fi~ + $CC -shared $output_objdir/$soname.def $libobjs $deplibs $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' + else + _LT_TAGVAR(ld_shlibs, $1)=no + fi + ;; + + haiku*) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + _LT_TAGVAR(link_all_deplibs, $1)=yes + ;; + + interix[[3-9]]*) + _LT_TAGVAR(hardcode_direct, $1)=no + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir' + _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' + # Hack: On Interix 3.x, we cannot compile PIC because of a broken gcc. + # Instead, shared libraries are loaded at an image base (0x10000000 by + # default) and relocated if they conflict, which is a slow very memory + # consuming and fragmenting process. To avoid this, we pick a random, + # 256 KiB-aligned image base between 0x50000000 and 0x6FFC0000 at link + # time. Moving up from 0x10000000 also allows more sbrk(2) space. + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='sed "s,^,_," $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--retain-symbols-file,$output_objdir/$soname.expsym ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' + ;; + + gnu* | linux* | tpf* | k*bsd*-gnu | kopensolaris*-gnu) + tmp_diet=no + if test "$host_os" = linux-dietlibc; then + case $cc_basename in + diet\ *) tmp_diet=yes;; # linux-dietlibc with static linking (!diet-dyn) + esac + fi + if $LD --help 2>&1 | $EGREP ': supported targets:.* elf' > /dev/null \ + && test "$tmp_diet" = no + then + tmp_addflag=' $pic_flag' + tmp_sharedflag='-shared' + case $cc_basename,$host_cpu in + pgcc*) # Portland Group C compiler + _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` ${wl}--no-whole-archive' + tmp_addflag=' $pic_flag' + ;; + pgf77* | pgf90* | pgf95* | pgfortran*) + # Portland Group f77 and f90 compilers + _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` ${wl}--no-whole-archive' + tmp_addflag=' $pic_flag -Mnomain' ;; + ecc*,ia64* | icc*,ia64*) # Intel C compiler on ia64 + tmp_addflag=' -i_dynamic' ;; + efc*,ia64* | ifort*,ia64*) # Intel Fortran compiler on ia64 + tmp_addflag=' -i_dynamic -nofor_main' ;; + ifc* | ifort*) # Intel Fortran compiler + tmp_addflag=' -nofor_main' ;; + lf95*) # Lahey Fortran 8.1 + _LT_TAGVAR(whole_archive_flag_spec, $1)= + tmp_sharedflag='--shared' ;; + xl[[cC]]* | bgxl[[cC]]* | mpixl[[cC]]*) # IBM XL C 8.0 on PPC (deal with xlf below) + tmp_sharedflag='-qmkshrobj' + tmp_addflag= ;; + nvcc*) # Cuda Compiler Driver 2.2 + _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` ${wl}--no-whole-archive' + _LT_TAGVAR(compiler_needs_object, $1)=yes + ;; + esac + case `$CC -V 2>&1 | sed 5q` in + *Sun\ C*) # Sun C 5.9 + _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive`new_convenience=; for conv in $convenience\"\"; do test -z \"$conv\" || new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` ${wl}--no-whole-archive' + _LT_TAGVAR(compiler_needs_object, $1)=yes + tmp_sharedflag='-G' ;; + *Sun\ F*) # Sun Fortran 8.3 + tmp_sharedflag='-G' ;; + esac + _LT_TAGVAR(archive_cmds, $1)='$CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + + if test "x$supports_anon_versioning" = xyes; then + _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $output_objdir/$libname.ver~ + cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ + echo "local: *; };" >> $output_objdir/$libname.ver~ + $CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-version-script ${wl}$output_objdir/$libname.ver -o $lib' + fi + + case $cc_basename in + xlf* | bgf* | bgxlf* | mpixlf*) + # IBM XL Fortran 10.1 on PPC cannot create shared libs itself + _LT_TAGVAR(whole_archive_flag_spec, $1)='--whole-archive$convenience --no-whole-archive' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' + _LT_TAGVAR(archive_cmds, $1)='$LD -shared $libobjs $deplibs $linker_flags -soname $soname -o $lib' + if test "x$supports_anon_versioning" = xyes; then + _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $output_objdir/$libname.ver~ + cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ + echo "local: *; };" >> $output_objdir/$libname.ver~ + $LD -shared $libobjs $deplibs $linker_flags -soname $soname -version-script $output_objdir/$libname.ver -o $lib' + fi + ;; + esac + else + _LT_TAGVAR(ld_shlibs, $1)=no + fi + ;; + + netbsd* | netbsdelf*-gnu) + if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then + _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable $libobjs $deplibs $linker_flags -o $lib' + wlarc= + else + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' + fi + ;; + + solaris*) + if $LD -v 2>&1 | $GREP 'BFD 2\.8' > /dev/null; then + _LT_TAGVAR(ld_shlibs, $1)=no + cat <<_LT_EOF 1>&2 + +*** Warning: The releases 2.8.* of the GNU linker cannot reliably +*** create shared libraries on Solaris systems. Therefore, libtool +*** is disabling shared libraries support. We urge you to upgrade GNU +*** binutils to release 2.9.1 or newer. Another option is to modify +*** your PATH or compiler configuration so that the native linker is +*** used, and then restart. + +_LT_EOF + elif $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' + else + _LT_TAGVAR(ld_shlibs, $1)=no + fi + ;; + + sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX*) + case `$LD -v 2>&1` in + *\ [[01]].* | *\ 2.[[0-9]].* | *\ 2.1[[0-5]].*) + _LT_TAGVAR(ld_shlibs, $1)=no + cat <<_LT_EOF 1>&2 + +*** Warning: Releases of the GNU linker prior to 2.16.91.0.3 can not +*** reliably create shared libraries on SCO systems. Therefore, libtool +*** is disabling shared libraries support. We urge you to upgrade GNU +*** binutils to release 2.16.91.0.3 or newer. Another option is to modify +*** your PATH or compiler configuration so that the native linker is +*** used, and then restart. + +_LT_EOF + ;; + *) + # For security reasons, it is highly recommended that you always + # use absolute paths for naming shared libraries, and exclude the + # DT_RUNPATH tag from executables and libraries. But doing so + # requires that you compile everything twice, which is a pain. + if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' + else + _LT_TAGVAR(ld_shlibs, $1)=no + fi + ;; + esac + ;; + + sunos4*) + _LT_TAGVAR(archive_cmds, $1)='$LD -assert pure-text -Bshareable -o $lib $libobjs $deplibs $linker_flags' + wlarc= + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; + + *) + if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' + else + _LT_TAGVAR(ld_shlibs, $1)=no + fi + ;; + esac + + if test "$_LT_TAGVAR(ld_shlibs, $1)" = no; then + runpath_var= + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)= + _LT_TAGVAR(export_dynamic_flag_spec, $1)= + _LT_TAGVAR(whole_archive_flag_spec, $1)= + fi + else + # PORTME fill in a description of your system's linker (not GNU ld) + case $host_os in + aix3*) + _LT_TAGVAR(allow_undefined_flag, $1)=unsupported + _LT_TAGVAR(always_export_symbols, $1)=yes + _LT_TAGVAR(archive_expsym_cmds, $1)='$LD -o $output_objdir/$soname $libobjs $deplibs $linker_flags -bE:$export_symbols -T512 -H512 -bM:SRE~$AR $AR_FLAGS $lib $output_objdir/$soname' + # Note: this linker hardcodes the directories in LIBPATH if there + # are no directories specified by -L. + _LT_TAGVAR(hardcode_minus_L, $1)=yes + if test "$GCC" = yes && test -z "$lt_prog_compiler_static"; then + # Neither direct hardcoding nor static linking is supported with a + # broken collect2. + _LT_TAGVAR(hardcode_direct, $1)=unsupported + fi + ;; + + aix[[4-9]]*) + if test "$host_cpu" = ia64; then + # On IA64, the linker does run time linking by default, so we don't + # have to do anything special. + aix_use_runtimelinking=no + exp_sym_flag='-Bexport' + no_entry_flag="" + else + # If we're using GNU nm, then we don't want the "-C" option. + # -C means demangle to AIX nm, but means don't demangle with GNU nm + # Also, AIX nm treats weak defined symbols like other global + # defined symbols, whereas GNU nm marks them as "W". + if $NM -V 2>&1 | $GREP 'GNU' > /dev/null; then + _LT_TAGVAR(export_symbols_cmds, $1)='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W")) && ([substr](\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols' + else + _LT_TAGVAR(export_symbols_cmds, $1)='$NM -BCpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B")) && ([substr](\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols' + fi + aix_use_runtimelinking=no + + # Test if we are trying to use run time linking or normal + # AIX style linking. If -brtl is somewhere in LDFLAGS, we + # need to do runtime linking. + case $host_os in aix4.[[23]]|aix4.[[23]].*|aix[[5-9]]*) + for ld_flag in $LDFLAGS; do + if (test $ld_flag = "-brtl" || test $ld_flag = "-Wl,-brtl"); then + aix_use_runtimelinking=yes + break + fi + done + ;; + esac + + exp_sym_flag='-bexport' + no_entry_flag='-bnoentry' + fi + + # When large executables or shared objects are built, AIX ld can + # have problems creating the table of contents. If linking a library + # or program results in "error TOC overflow" add -mminimal-toc to + # CXXFLAGS/CFLAGS for g++/gcc. In the cases where that is not + # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS. + + _LT_TAGVAR(archive_cmds, $1)='' + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_direct_absolute, $1)=yes + _LT_TAGVAR(hardcode_libdir_separator, $1)=':' + _LT_TAGVAR(link_all_deplibs, $1)=yes + _LT_TAGVAR(file_list_spec, $1)='${wl}-f,' + + if test "$GCC" = yes; then + case $host_os in aix4.[[012]]|aix4.[[012]].*) + # We only want to do this on AIX 4.2 and lower, the check + # below for broken collect2 doesn't work under 4.3+ + collect2name=`${CC} -print-prog-name=collect2` + if test -f "$collect2name" && + strings "$collect2name" | $GREP resolve_lib_name >/dev/null + then + # We have reworked collect2 + : + else + # We have old collect2 + _LT_TAGVAR(hardcode_direct, $1)=unsupported + # It fails to find uninstalled libraries when the uninstalled + # path is not listed in the libpath. Setting hardcode_minus_L + # to unsupported forces relinking + _LT_TAGVAR(hardcode_minus_L, $1)=yes + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)= + fi + ;; + esac + shared_flag='-shared' + if test "$aix_use_runtimelinking" = yes; then + shared_flag="$shared_flag "'${wl}-G' + fi + _LT_TAGVAR(link_all_deplibs, $1)=no + else + # not using gcc + if test "$host_cpu" = ia64; then + # VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release + # chokes on -Wl,-G. The following line is correct: + shared_flag='-G' + else + if test "$aix_use_runtimelinking" = yes; then + shared_flag='${wl}-G' + else + shared_flag='${wl}-bM:SRE' + fi + fi + fi + + _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-bexpall' + # It seems that -bexpall does not export symbols beginning with + # underscore (_), so it is better to generate a list of symbols to export. + _LT_TAGVAR(always_export_symbols, $1)=yes + if test "$aix_use_runtimelinking" = yes; then + # Warning - without using the other runtime loading flags (-brtl), + # -berok will link without error, but may produce a broken library. + _LT_TAGVAR(allow_undefined_flag, $1)='-berok' + # Determine the default libpath from the value encoded in an + # empty executable. + _LT_SYS_MODULE_PATH_AIX([$1]) + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-blibpath:$libdir:'"$aix_libpath" + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags `if test "x${allow_undefined_flag}" != "x"; then func_echo_all "${wl}${allow_undefined_flag}"; else :; fi` '"\${wl}$exp_sym_flag:\$export_symbols $shared_flag" + else + if test "$host_cpu" = ia64; then + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-R $libdir:/usr/lib:/lib' + _LT_TAGVAR(allow_undefined_flag, $1)="-z nodefs" + _LT_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags ${wl}${allow_undefined_flag} '"\${wl}$exp_sym_flag:\$export_symbols" + else + # Determine the default libpath from the value encoded in an + # empty executable. + _LT_SYS_MODULE_PATH_AIX([$1]) + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-blibpath:$libdir:'"$aix_libpath" + # Warning - without using the other run time loading flags, + # -berok will link without error, but may produce a broken library. + _LT_TAGVAR(no_undefined_flag, $1)=' ${wl}-bernotok' + _LT_TAGVAR(allow_undefined_flag, $1)=' ${wl}-berok' + if test "$with_gnu_ld" = yes; then + # We only use this code for GNU lds that support --whole-archive. + _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive$convenience ${wl}--no-whole-archive' + else + # Exported symbols can be pulled into shared objects from archives + _LT_TAGVAR(whole_archive_flag_spec, $1)='$convenience' + fi + _LT_TAGVAR(archive_cmds_need_lc, $1)=yes + # This is similar to how AIX traditionally builds its shared libraries. + _LT_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs ${wl}-bnoentry $compiler_flags ${wl}-bE:$export_symbols${allow_undefined_flag}~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$soname' + fi + fi + ;; + + amigaos*) + case $host_cpu in + powerpc) + # see comment about AmigaOS4 .so support + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='' + ;; + m68k) + _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/a2ixlibrary.data~$ECHO "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$ECHO "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$ECHO "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$ECHO "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' + _LT_TAGVAR(hardcode_minus_L, $1)=yes + ;; + esac + ;; + + bsdi[[45]]*) + _LT_TAGVAR(export_dynamic_flag_spec, $1)=-rdynamic + ;; + + cygwin* | mingw* | pw32* | cegcc*) + # When not using gcc, we currently assume that we are using + # Microsoft Visual C++. + # hardcode_libdir_flag_spec is actually meaningless, as there is + # no search path for DLLs. + case $cc_basename in + cl*) + # Native MSVC + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)=' ' + _LT_TAGVAR(allow_undefined_flag, $1)=unsupported + _LT_TAGVAR(always_export_symbols, $1)=yes + _LT_TAGVAR(file_list_spec, $1)='@' + # Tell ltmain to make .lib files, not .a files. + libext=lib + # Tell ltmain to make .dll files, not .so files. + shrext_cmds=".dll" + # FIXME: Setting linknames here is a bad hack. + _LT_TAGVAR(archive_cmds, $1)='$CC -o $output_objdir/$soname $libobjs $compiler_flags $deplibs -Wl,-dll~linknames=' + _LT_TAGVAR(archive_expsym_cmds, $1)='if test "x`$SED 1q $export_symbols`" = xEXPORTS; then + sed -n -e 's/\\\\\\\(.*\\\\\\\)/-link\\\ -EXPORT:\\\\\\\1/' -e '1\\\!p' < $export_symbols > $output_objdir/$soname.exp; + else + sed -e 's/\\\\\\\(.*\\\\\\\)/-link\\\ -EXPORT:\\\\\\\1/' < $export_symbols > $output_objdir/$soname.exp; + fi~ + $CC -o $tool_output_objdir$soname $libobjs $compiler_flags $deplibs "@$tool_output_objdir$soname.exp" -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~ + linknames=' + # The linker will not automatically build a static lib if we build a DLL. + # _LT_TAGVAR(old_archive_from_new_cmds, $1)='true' + _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes + _LT_TAGVAR(exclude_expsyms, $1)='_NULL_IMPORT_DESCRIPTOR|_IMPORT_DESCRIPTOR_.*' + _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[[BCDGRS]][[ ]]/s/.*[[ ]]\([[^ ]]*\)/\1,DATA/'\'' | $SED -e '\''/^[[AITW]][[ ]]/s/.*[[ ]]//'\'' | sort | uniq > $export_symbols' + # Don't use ranlib + _LT_TAGVAR(old_postinstall_cmds, $1)='chmod 644 $oldlib' + _LT_TAGVAR(postlink_cmds, $1)='lt_outputfile="@OUTPUT@"~ + lt_tool_outputfile="@TOOL_OUTPUT@"~ + case $lt_outputfile in + *.exe|*.EXE) ;; + *) + lt_outputfile="$lt_outputfile.exe" + lt_tool_outputfile="$lt_tool_outputfile.exe" + ;; + esac~ + if test "$MANIFEST_TOOL" != ":" && test -f "$lt_outputfile.manifest"; then + $MANIFEST_TOOL -manifest "$lt_tool_outputfile.manifest" -outputresource:"$lt_tool_outputfile" || exit 1; + $RM "$lt_outputfile.manifest"; + fi' + ;; + *) + # Assume MSVC wrapper + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)=' ' + _LT_TAGVAR(allow_undefined_flag, $1)=unsupported + # Tell ltmain to make .lib files, not .a files. + libext=lib + # Tell ltmain to make .dll files, not .so files. + shrext_cmds=".dll" + # FIXME: Setting linknames here is a bad hack. + _LT_TAGVAR(archive_cmds, $1)='$CC -o $lib $libobjs $compiler_flags `func_echo_all "$deplibs" | $SED '\''s/ -lc$//'\''` -link -dll~linknames=' + # The linker will automatically build a .lib file if we build a DLL. + _LT_TAGVAR(old_archive_from_new_cmds, $1)='true' + # FIXME: Should let the user specify the lib program. + _LT_TAGVAR(old_archive_cmds, $1)='lib -OUT:$oldlib$oldobjs$old_deplibs' + _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes + ;; + esac + ;; + + darwin* | rhapsody*) + _LT_DARWIN_LINKER_FEATURES($1) + ;; + + dgux*) + _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; + + # FreeBSD 2.2.[012] allows us to include c++rt0.o to get C++ constructor + # support. Future versions do this automatically, but an explicit c++rt0.o + # does not break anything, and helps significantly (at the cost of a little + # extra space). + freebsd2.2*) + _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags /usr/lib/c++rt0.o' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; + + # Unfortunately, older versions of FreeBSD 2 do not have this feature. + freebsd2.*) + _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_minus_L, $1)=yes + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; + + # FreeBSD 3 and greater uses gcc -shared to do shared libraries. + freebsd* | dragonfly*) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; + + hpux9*) + if test "$GCC" = yes; then + _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$CC -shared $pic_flag ${wl}+b ${wl}$install_libdir -o $output_objdir/$soname $libobjs $deplibs $compiler_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib' + else + _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$LD -b +b $install_libdir -o $output_objdir/$soname $libobjs $deplibs $linker_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib' + fi + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}+b ${wl}$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=: + _LT_TAGVAR(hardcode_direct, $1)=yes + + # hardcode_minus_L: Not really in the search PATH, + # but as the default location of the library. + _LT_TAGVAR(hardcode_minus_L, $1)=yes + _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' + ;; + + hpux10*) + if test "$GCC" = yes && test "$with_gnu_ld" = no; then + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags' + else + _LT_TAGVAR(archive_cmds, $1)='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags' + fi + if test "$with_gnu_ld" = no; then + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}+b ${wl}$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=: + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_direct_absolute, $1)=yes + _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' + # hardcode_minus_L: Not really in the search PATH, + # but as the default location of the library. + _LT_TAGVAR(hardcode_minus_L, $1)=yes + fi + ;; + + hpux11*) + if test "$GCC" = yes && test "$with_gnu_ld" = no; then + case $host_cpu in + hppa*64*) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}+h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags' + ;; + ia64*) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags' + ;; + *) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags' + ;; + esac + else + case $host_cpu in + hppa*64*) + _LT_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags' + ;; + ia64*) + _LT_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags' + ;; + *) + m4_if($1, [], [ + # Older versions of the 11.00 compiler do not understand -b yet + # (HP92453-01 A.11.01.20 doesn't, HP92453-01 B.11.X.35175-35176.GP does) + _LT_LINKER_OPTION([if $CC understands -b], + _LT_TAGVAR(lt_cv_prog_compiler__b, $1), [-b], + [_LT_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags'], + [_LT_TAGVAR(archive_cmds, $1)='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags'])], + [_LT_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags']) + ;; + esac + fi + if test "$with_gnu_ld" = no; then + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}+b ${wl}$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=: + + case $host_cpu in + hppa*64*|ia64*) + _LT_TAGVAR(hardcode_direct, $1)=no + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; + *) + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_direct_absolute, $1)=yes + _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' + + # hardcode_minus_L: Not really in the search PATH, + # but as the default location of the library. + _LT_TAGVAR(hardcode_minus_L, $1)=yes + ;; + esac + fi + ;; + + irix5* | irix6* | nonstopux*) + if test "$GCC" = yes; then + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' + # Try to use the -exported_symbol ld option, if it does not + # work, assume that -exports_file does not work either and + # implicitly export all symbols. + # This should be the same for all languages, so no per-tag cache variable. + AC_CACHE_CHECK([whether the $host_os linker accepts -exported_symbol], + [lt_cv_irix_exported_symbol], + [save_LDFLAGS="$LDFLAGS" + LDFLAGS="$LDFLAGS -shared ${wl}-exported_symbol ${wl}foo ${wl}-update_registry ${wl}/dev/null" + AC_LINK_IFELSE( + [AC_LANG_SOURCE( + [AC_LANG_CASE([C], [[int foo (void) { return 0; }]], + [C++], [[int foo (void) { return 0; }]], + [Fortran 77], [[ + subroutine foo + end]], + [Fortran], [[ + subroutine foo + end]])])], + [lt_cv_irix_exported_symbol=yes], + [lt_cv_irix_exported_symbol=no]) + LDFLAGS="$save_LDFLAGS"]) + if test "$lt_cv_irix_exported_symbol" = yes; then + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations ${wl}-exports_file ${wl}$export_symbols -o $lib' + fi + else + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -exports_file $export_symbols -o $lib' + fi + _LT_TAGVAR(archive_cmds_need_lc, $1)='no' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=: + _LT_TAGVAR(inherit_rpath, $1)=yes + _LT_TAGVAR(link_all_deplibs, $1)=yes + ;; + + netbsd* | netbsdelf*-gnu) + if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then + _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' # a.out + else + _LT_TAGVAR(archive_cmds, $1)='$LD -shared -o $lib $libobjs $deplibs $linker_flags' # ELF + fi + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; + + newsos6) + _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=: + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; + + *nto* | *qnx*) + ;; + + openbsd*) + if test -f /usr/libexec/ld.so; then + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + _LT_TAGVAR(hardcode_direct_absolute, $1)=yes + if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags ${wl}-retain-symbols-file,$export_symbols' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir' + _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' + else + case $host_os in + openbsd[[01]].* | openbsd2.[[0-7]] | openbsd2.[[0-7]].*) + _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' + ;; + *) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir' + ;; + esac + fi + else + _LT_TAGVAR(ld_shlibs, $1)=no + fi + ;; + + os2*) + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' + _LT_TAGVAR(hardcode_minus_L, $1)=yes + _LT_TAGVAR(allow_undefined_flag, $1)=unsupported + _LT_TAGVAR(archive_cmds, $1)='$ECHO "LIBRARY $libname INITINSTANCE" > $output_objdir/$libname.def~$ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~echo DATA >> $output_objdir/$libname.def~echo " SINGLE NONSHARED" >> $output_objdir/$libname.def~echo EXPORTS >> $output_objdir/$libname.def~emxexp $libobjs >> $output_objdir/$libname.def~$CC -Zdll -Zcrtdll -o $lib $libobjs $deplibs $compiler_flags $output_objdir/$libname.def' + _LT_TAGVAR(old_archive_from_new_cmds, $1)='emximp -o $output_objdir/$libname.a $output_objdir/$libname.def' + ;; + + osf3*) + if test "$GCC" = yes; then + _LT_TAGVAR(allow_undefined_flag, $1)=' ${wl}-expect_unresolved ${wl}\*' + _LT_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' + else + _LT_TAGVAR(allow_undefined_flag, $1)=' -expect_unresolved \*' + _LT_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib' + fi + _LT_TAGVAR(archive_cmds_need_lc, $1)='no' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=: + ;; + + osf4* | osf5*) # as osf3* with the addition of -msym flag + if test "$GCC" = yes; then + _LT_TAGVAR(allow_undefined_flag, $1)=' ${wl}-expect_unresolved ${wl}\*' + _LT_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $pic_flag $libobjs $deplibs $compiler_flags ${wl}-msym ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' + else + _LT_TAGVAR(allow_undefined_flag, $1)=' -expect_unresolved \*' + _LT_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags -msym -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done; printf "%s\\n" "-hidden">> $lib.exp~ + $CC -shared${allow_undefined_flag} ${wl}-input ${wl}$lib.exp $compiler_flags $libobjs $deplibs -soname $soname `test -n "$verstring" && $ECHO "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib~$RM $lib.exp' + + # Both c and cxx compiler support -rpath directly + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-rpath $libdir' + fi + _LT_TAGVAR(archive_cmds_need_lc, $1)='no' + _LT_TAGVAR(hardcode_libdir_separator, $1)=: + ;; + + solaris*) + _LT_TAGVAR(no_undefined_flag, $1)=' -z defs' + if test "$GCC" = yes; then + wlarc='${wl}' + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag ${wl}-z ${wl}text ${wl}-h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ + $CC -shared $pic_flag ${wl}-z ${wl}text ${wl}-M ${wl}$lib.exp ${wl}-h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp' + else + case `$CC -V 2>&1` in + *"Compilers 5.0"*) + wlarc='' + _LT_TAGVAR(archive_cmds, $1)='$LD -G${allow_undefined_flag} -h $soname -o $lib $libobjs $deplibs $linker_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ + $LD -G${allow_undefined_flag} -M $lib.exp -h $soname -o $lib $libobjs $deplibs $linker_flags~$RM $lib.exp' + ;; + *) + wlarc='${wl}' + _LT_TAGVAR(archive_cmds, $1)='$CC -G${allow_undefined_flag} -h $soname -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ + $CC -G${allow_undefined_flag} -M $lib.exp -h $soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp' + ;; + esac + fi + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + case $host_os in + solaris2.[[0-5]] | solaris2.[[0-5]].*) ;; + *) + # The compiler driver will combine and reorder linker options, + # but understands `-z linker_flag'. GCC discards it without `$wl', + # but is careful enough not to reorder. + # Supported since Solaris 2.6 (maybe 2.5.1?) + if test "$GCC" = yes; then + _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}-z ${wl}allextract$convenience ${wl}-z ${wl}defaultextract' + else + _LT_TAGVAR(whole_archive_flag_spec, $1)='-z allextract$convenience -z defaultextract' + fi + ;; + esac + _LT_TAGVAR(link_all_deplibs, $1)=yes + ;; + + sunos4*) + if test "x$host_vendor" = xsequent; then + # Use $CC to link under sequent, because it throws in some extra .o + # files that make .init and .fini sections work. + _LT_TAGVAR(archive_cmds, $1)='$CC -G ${wl}-h $soname -o $lib $libobjs $deplibs $compiler_flags' + else + _LT_TAGVAR(archive_cmds, $1)='$LD -assert pure-text -Bstatic -o $lib $libobjs $deplibs $linker_flags' + fi + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_minus_L, $1)=yes + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; + + sysv4) + case $host_vendor in + sni) + _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + _LT_TAGVAR(hardcode_direct, $1)=yes # is this really true??? + ;; + siemens) + ## LD is ld it makes a PLAMLIB + ## CC just makes a GrossModule. + _LT_TAGVAR(archive_cmds, $1)='$LD -G -o $lib $libobjs $deplibs $linker_flags' + _LT_TAGVAR(reload_cmds, $1)='$CC -r -o $output$reload_objs' + _LT_TAGVAR(hardcode_direct, $1)=no + ;; + motorola) + _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + _LT_TAGVAR(hardcode_direct, $1)=no #Motorola manual says yes, but my tests say they lie + ;; + esac + runpath_var='LD_RUN_PATH' + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; + + sysv4.3*) + _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + _LT_TAGVAR(export_dynamic_flag_spec, $1)='-Bexport' + ;; + + sysv4*MP*) + if test -d /usr/nec; then + _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + runpath_var=LD_RUN_PATH + hardcode_runpath_var=yes + _LT_TAGVAR(ld_shlibs, $1)=yes + fi + ;; + + sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[[01]].[[10]]* | unixware7* | sco3.2v5.0.[[024]]*) + _LT_TAGVAR(no_undefined_flag, $1)='${wl}-z,text' + _LT_TAGVAR(archive_cmds_need_lc, $1)=no + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + runpath_var='LD_RUN_PATH' + + if test "$GCC" = yes; then + _LT_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + else + _LT_TAGVAR(archive_cmds, $1)='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + fi + ;; + + sysv5* | sco3.2v5* | sco5v6*) + # Note: We can NOT use -z defs as we might desire, because we do not + # link with -lc, and that would cause any symbols used from libc to + # always be unresolved, which means just about no library would + # ever link correctly. If we're not using GNU ld we use -z text + # though, which does catch some bad symbols but isn't as heavy-handed + # as -z defs. + _LT_TAGVAR(no_undefined_flag, $1)='${wl}-z,text' + _LT_TAGVAR(allow_undefined_flag, $1)='${wl}-z,nodefs' + _LT_TAGVAR(archive_cmds_need_lc, $1)=no + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-R,$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=':' + _LT_TAGVAR(link_all_deplibs, $1)=yes + _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-Bexport' + runpath_var='LD_RUN_PATH' + + if test "$GCC" = yes; then + _LT_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + else + _LT_TAGVAR(archive_cmds, $1)='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + fi + ;; + + uts4*) + _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; + + *) + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + esac + + if test x$host_vendor = xsni; then + case $host in + sysv4 | sysv4.2uw2* | sysv4.3* | sysv5*) + _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-Blargedynsym' + ;; + esac + fi + fi +]) +AC_MSG_RESULT([$_LT_TAGVAR(ld_shlibs, $1)]) +test "$_LT_TAGVAR(ld_shlibs, $1)" = no && can_build_shared=no + +_LT_TAGVAR(with_gnu_ld, $1)=$with_gnu_ld + +_LT_DECL([], [libext], [0], [Old archive suffix (normally "a")])dnl +_LT_DECL([], [shrext_cmds], [1], [Shared library suffix (normally ".so")])dnl +_LT_DECL([], [extract_expsyms_cmds], [2], + [The commands to extract the exported symbol list from a shared archive]) + +# +# Do we need to explicitly link libc? +# +case "x$_LT_TAGVAR(archive_cmds_need_lc, $1)" in +x|xyes) + # Assume -lc should be added + _LT_TAGVAR(archive_cmds_need_lc, $1)=yes + + if test "$enable_shared" = yes && test "$GCC" = yes; then + case $_LT_TAGVAR(archive_cmds, $1) in + *'~'*) + # FIXME: we may have to deal with multi-command sequences. + ;; + '$CC '*) + # Test whether the compiler implicitly links with -lc since on some + # systems, -lgcc has to come before -lc. If gcc already passes -lc + # to ld, don't add -lc before -lgcc. + AC_CACHE_CHECK([whether -lc should be explicitly linked in], + [lt_cv_]_LT_TAGVAR(archive_cmds_need_lc, $1), + [$RM conftest* + echo "$lt_simple_compile_test_code" > conftest.$ac_ext + + if AC_TRY_EVAL(ac_compile) 2>conftest.err; then + soname=conftest + lib=conftest + libobjs=conftest.$ac_objext + deplibs= + wl=$_LT_TAGVAR(lt_prog_compiler_wl, $1) + pic_flag=$_LT_TAGVAR(lt_prog_compiler_pic, $1) + compiler_flags=-v + linker_flags=-v + verstring= + output_objdir=. + libname=conftest + lt_save_allow_undefined_flag=$_LT_TAGVAR(allow_undefined_flag, $1) + _LT_TAGVAR(allow_undefined_flag, $1)= + if AC_TRY_EVAL(_LT_TAGVAR(archive_cmds, $1) 2\>\&1 \| $GREP \" -lc \" \>/dev/null 2\>\&1) + then + lt_cv_[]_LT_TAGVAR(archive_cmds_need_lc, $1)=no + else + lt_cv_[]_LT_TAGVAR(archive_cmds_need_lc, $1)=yes + fi + _LT_TAGVAR(allow_undefined_flag, $1)=$lt_save_allow_undefined_flag + else + cat conftest.err 1>&5 + fi + $RM conftest* + ]) + _LT_TAGVAR(archive_cmds_need_lc, $1)=$lt_cv_[]_LT_TAGVAR(archive_cmds_need_lc, $1) + ;; + esac + fi + ;; +esac + +_LT_TAGDECL([build_libtool_need_lc], [archive_cmds_need_lc], [0], + [Whether or not to add -lc for building shared libraries]) +_LT_TAGDECL([allow_libtool_libs_with_static_runtimes], + [enable_shared_with_static_runtimes], [0], + [Whether or not to disallow shared libs when runtime libs are static]) +_LT_TAGDECL([], [export_dynamic_flag_spec], [1], + [Compiler flag to allow reflexive dlopens]) +_LT_TAGDECL([], [whole_archive_flag_spec], [1], + [Compiler flag to generate shared objects directly from archives]) +_LT_TAGDECL([], [compiler_needs_object], [1], + [Whether the compiler copes with passing no objects directly]) +_LT_TAGDECL([], [old_archive_from_new_cmds], [2], + [Create an old-style archive from a shared archive]) +_LT_TAGDECL([], [old_archive_from_expsyms_cmds], [2], + [Create a temporary old-style archive to link instead of a shared archive]) +_LT_TAGDECL([], [archive_cmds], [2], [Commands used to build a shared archive]) +_LT_TAGDECL([], [archive_expsym_cmds], [2]) +_LT_TAGDECL([], [module_cmds], [2], + [Commands used to build a loadable module if different from building + a shared archive.]) +_LT_TAGDECL([], [module_expsym_cmds], [2]) +_LT_TAGDECL([], [with_gnu_ld], [1], + [Whether we are building with GNU ld or not]) +_LT_TAGDECL([], [allow_undefined_flag], [1], + [Flag that allows shared libraries with undefined symbols to be built]) +_LT_TAGDECL([], [no_undefined_flag], [1], + [Flag that enforces no undefined symbols]) +_LT_TAGDECL([], [hardcode_libdir_flag_spec], [1], + [Flag to hardcode $libdir into a binary during linking. + This must work even if $libdir does not exist]) +_LT_TAGDECL([], [hardcode_libdir_separator], [1], + [Whether we need a single "-rpath" flag with a separated argument]) +_LT_TAGDECL([], [hardcode_direct], [0], + [Set to "yes" if using DIR/libNAME${shared_ext} during linking hardcodes + DIR into the resulting binary]) +_LT_TAGDECL([], [hardcode_direct_absolute], [0], + [Set to "yes" if using DIR/libNAME${shared_ext} during linking hardcodes + DIR into the resulting binary and the resulting library dependency is + "absolute", i.e impossible to change by setting ${shlibpath_var} if the + library is relocated]) +_LT_TAGDECL([], [hardcode_minus_L], [0], + [Set to "yes" if using the -LDIR flag during linking hardcodes DIR + into the resulting binary]) +_LT_TAGDECL([], [hardcode_shlibpath_var], [0], + [Set to "yes" if using SHLIBPATH_VAR=DIR during linking hardcodes DIR + into the resulting binary]) +_LT_TAGDECL([], [hardcode_automatic], [0], + [Set to "yes" if building a shared library automatically hardcodes DIR + into the library and all subsequent libraries and executables linked + against it]) +_LT_TAGDECL([], [inherit_rpath], [0], + [Set to yes if linker adds runtime paths of dependent libraries + to runtime path list]) +_LT_TAGDECL([], [link_all_deplibs], [0], + [Whether libtool must link a program against all its dependency libraries]) +_LT_TAGDECL([], [always_export_symbols], [0], + [Set to "yes" if exported symbols are required]) +_LT_TAGDECL([], [export_symbols_cmds], [2], + [The commands to list exported symbols]) +_LT_TAGDECL([], [exclude_expsyms], [1], + [Symbols that should not be listed in the preloaded symbols]) +_LT_TAGDECL([], [include_expsyms], [1], + [Symbols that must always be exported]) +_LT_TAGDECL([], [prelink_cmds], [2], + [Commands necessary for linking programs (against libraries) with templates]) +_LT_TAGDECL([], [postlink_cmds], [2], + [Commands necessary for finishing linking programs]) +_LT_TAGDECL([], [file_list_spec], [1], + [Specify filename containing input files]) +dnl FIXME: Not yet implemented +dnl _LT_TAGDECL([], [thread_safe_flag_spec], [1], +dnl [Compiler flag to generate thread safe objects]) +])# _LT_LINKER_SHLIBS + + +# _LT_LANG_C_CONFIG([TAG]) +# ------------------------ +# Ensure that the configuration variables for a C compiler are suitably +# defined. These variables are subsequently used by _LT_CONFIG to write +# the compiler configuration to `libtool'. +m4_defun([_LT_LANG_C_CONFIG], +[m4_require([_LT_DECL_EGREP])dnl +lt_save_CC="$CC" +AC_LANG_PUSH(C) + +# Source file extension for C test sources. +ac_ext=c + +# Object file extension for compiled C test sources. +objext=o +_LT_TAGVAR(objext, $1)=$objext + +# Code to be used in simple compile tests +lt_simple_compile_test_code="int some_variable = 0;" + +# Code to be used in simple link tests +lt_simple_link_test_code='int main(){return(0);}' + +_LT_TAG_COMPILER +# Save the default compiler, since it gets overwritten when the other +# tags are being tested, and _LT_TAGVAR(compiler, []) is a NOP. +compiler_DEFAULT=$CC + +# save warnings/boilerplate of simple test code +_LT_COMPILER_BOILERPLATE +_LT_LINKER_BOILERPLATE + +## CAVEAT EMPTOR: +## There is no encapsulation within the following macros, do not change +## the running order or otherwise move them around unless you know exactly +## what you are doing... +if test -n "$compiler"; then + _LT_COMPILER_NO_RTTI($1) + _LT_COMPILER_PIC($1) + _LT_COMPILER_C_O($1) + _LT_COMPILER_FILE_LOCKS($1) + _LT_LINKER_SHLIBS($1) + _LT_SYS_DYNAMIC_LINKER($1) + _LT_LINKER_HARDCODE_LIBPATH($1) + LT_SYS_DLOPEN_SELF + _LT_CMD_STRIPLIB + + # Report which library types will actually be built + AC_MSG_CHECKING([if libtool supports shared libraries]) + AC_MSG_RESULT([$can_build_shared]) + + AC_MSG_CHECKING([whether to build shared libraries]) + test "$can_build_shared" = "no" && enable_shared=no + + # On AIX, shared libraries and static libraries use the same namespace, and + # are all built from PIC. + case $host_os in + aix3*) + test "$enable_shared" = yes && enable_static=no + if test -n "$RANLIB"; then + archive_cmds="$archive_cmds~\$RANLIB \$lib" + postinstall_cmds='$RANLIB $lib' + fi + ;; + + aix[[4-9]]*) + if test "$host_cpu" != ia64 && test "$aix_use_runtimelinking" = no ; then + test "$enable_shared" = yes && enable_static=no + fi + ;; + esac + AC_MSG_RESULT([$enable_shared]) + + AC_MSG_CHECKING([whether to build static libraries]) + # Make sure either enable_shared or enable_static is yes. + test "$enable_shared" = yes || enable_static=yes + AC_MSG_RESULT([$enable_static]) + + _LT_CONFIG($1) +fi +AC_LANG_POP +CC="$lt_save_CC" +])# _LT_LANG_C_CONFIG + + +# _LT_LANG_CXX_CONFIG([TAG]) +# -------------------------- +# Ensure that the configuration variables for a C++ compiler are suitably +# defined. These variables are subsequently used by _LT_CONFIG to write +# the compiler configuration to `libtool'. +m4_defun([_LT_LANG_CXX_CONFIG], +[m4_require([_LT_FILEUTILS_DEFAULTS])dnl +m4_require([_LT_DECL_EGREP])dnl +m4_require([_LT_PATH_MANIFEST_TOOL])dnl +if test -n "$CXX" && ( test "X$CXX" != "Xno" && + ( (test "X$CXX" = "Xg++" && `g++ -v >/dev/null 2>&1` ) || + (test "X$CXX" != "Xg++"))) ; then + AC_PROG_CXXCPP +else + _lt_caught_CXX_error=yes +fi + +AC_LANG_PUSH(C++) +_LT_TAGVAR(archive_cmds_need_lc, $1)=no +_LT_TAGVAR(allow_undefined_flag, $1)= +_LT_TAGVAR(always_export_symbols, $1)=no +_LT_TAGVAR(archive_expsym_cmds, $1)= +_LT_TAGVAR(compiler_needs_object, $1)=no +_LT_TAGVAR(export_dynamic_flag_spec, $1)= +_LT_TAGVAR(hardcode_direct, $1)=no +_LT_TAGVAR(hardcode_direct_absolute, $1)=no +_LT_TAGVAR(hardcode_libdir_flag_spec, $1)= +_LT_TAGVAR(hardcode_libdir_separator, $1)= +_LT_TAGVAR(hardcode_minus_L, $1)=no +_LT_TAGVAR(hardcode_shlibpath_var, $1)=unsupported +_LT_TAGVAR(hardcode_automatic, $1)=no +_LT_TAGVAR(inherit_rpath, $1)=no +_LT_TAGVAR(module_cmds, $1)= +_LT_TAGVAR(module_expsym_cmds, $1)= +_LT_TAGVAR(link_all_deplibs, $1)=unknown +_LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds +_LT_TAGVAR(reload_flag, $1)=$reload_flag +_LT_TAGVAR(reload_cmds, $1)=$reload_cmds +_LT_TAGVAR(no_undefined_flag, $1)= +_LT_TAGVAR(whole_archive_flag_spec, $1)= +_LT_TAGVAR(enable_shared_with_static_runtimes, $1)=no + +# Source file extension for C++ test sources. +ac_ext=cpp + +# Object file extension for compiled C++ test sources. +objext=o +_LT_TAGVAR(objext, $1)=$objext + +# No sense in running all these tests if we already determined that +# the CXX compiler isn't working. Some variables (like enable_shared) +# are currently assumed to apply to all compilers on this platform, +# and will be corrupted by setting them based on a non-working compiler. +if test "$_lt_caught_CXX_error" != yes; then + # Code to be used in simple compile tests + lt_simple_compile_test_code="int some_variable = 0;" + + # Code to be used in simple link tests + lt_simple_link_test_code='int main(int, char *[[]]) { return(0); }' + + # ltmain only uses $CC for tagged configurations so make sure $CC is set. + _LT_TAG_COMPILER + + # save warnings/boilerplate of simple test code + _LT_COMPILER_BOILERPLATE + _LT_LINKER_BOILERPLATE + + # Allow CC to be a program name with arguments. + lt_save_CC=$CC + lt_save_CFLAGS=$CFLAGS + lt_save_LD=$LD + lt_save_GCC=$GCC + GCC=$GXX + lt_save_with_gnu_ld=$with_gnu_ld + lt_save_path_LD=$lt_cv_path_LD + if test -n "${lt_cv_prog_gnu_ldcxx+set}"; then + lt_cv_prog_gnu_ld=$lt_cv_prog_gnu_ldcxx + else + $as_unset lt_cv_prog_gnu_ld + fi + if test -n "${lt_cv_path_LDCXX+set}"; then + lt_cv_path_LD=$lt_cv_path_LDCXX + else + $as_unset lt_cv_path_LD + fi + test -z "${LDCXX+set}" || LD=$LDCXX + CC=${CXX-"c++"} + CFLAGS=$CXXFLAGS + compiler=$CC + _LT_TAGVAR(compiler, $1)=$CC + _LT_CC_BASENAME([$compiler]) + + if test -n "$compiler"; then + # We don't want -fno-exception when compiling C++ code, so set the + # no_builtin_flag separately + if test "$GXX" = yes; then + _LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)=' -fno-builtin' + else + _LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)= + fi + + if test "$GXX" = yes; then + # Set up default GNU C++ configuration + + LT_PATH_LD + + # Check if GNU C++ uses GNU ld as the underlying linker, since the + # archiving commands below assume that GNU ld is being used. + if test "$with_gnu_ld" = yes; then + _LT_TAGVAR(archive_cmds, $1)='$CC $pic_flag -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC $pic_flag -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' + + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' + _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic' + + # If archive_cmds runs LD, not CC, wlarc should be empty + # XXX I think wlarc can be eliminated in ltcf-cxx, but I need to + # investigate it a little bit more. (MM) + wlarc='${wl}' + + # ancient GNU ld didn't support --whole-archive et. al. + if eval "`$CC -print-prog-name=ld` --help 2>&1" | + $GREP 'no-whole-archive' > /dev/null; then + _LT_TAGVAR(whole_archive_flag_spec, $1)="$wlarc"'--whole-archive$convenience '"$wlarc"'--no-whole-archive' + else + _LT_TAGVAR(whole_archive_flag_spec, $1)= + fi + else + with_gnu_ld=no + wlarc= + + # A generic and very simple default shared library creation + # command for GNU C++ for the case where it uses the native + # linker, instead of GNU ld. If possible, this setting should + # overridden to take advantage of the native linker features on + # the platform it is being used on. + _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $lib' + fi + + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"' + + else + GXX=no + with_gnu_ld=no + wlarc= + fi + + # PORTME: fill in a description of your system's C++ link characteristics + AC_MSG_CHECKING([whether the $compiler linker ($LD) supports shared libraries]) + _LT_TAGVAR(ld_shlibs, $1)=yes + case $host_os in + aix3*) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + aix[[4-9]]*) + if test "$host_cpu" = ia64; then + # On IA64, the linker does run time linking by default, so we don't + # have to do anything special. + aix_use_runtimelinking=no + exp_sym_flag='-Bexport' + no_entry_flag="" + else + aix_use_runtimelinking=no + + # Test if we are trying to use run time linking or normal + # AIX style linking. If -brtl is somewhere in LDFLAGS, we + # need to do runtime linking. + case $host_os in aix4.[[23]]|aix4.[[23]].*|aix[[5-9]]*) + for ld_flag in $LDFLAGS; do + case $ld_flag in + *-brtl*) + aix_use_runtimelinking=yes + break + ;; + esac + done + ;; + esac + + exp_sym_flag='-bexport' + no_entry_flag='-bnoentry' + fi + + # When large executables or shared objects are built, AIX ld can + # have problems creating the table of contents. If linking a library + # or program results in "error TOC overflow" add -mminimal-toc to + # CXXFLAGS/CFLAGS for g++/gcc. In the cases where that is not + # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS. + + _LT_TAGVAR(archive_cmds, $1)='' + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_direct_absolute, $1)=yes + _LT_TAGVAR(hardcode_libdir_separator, $1)=':' + _LT_TAGVAR(link_all_deplibs, $1)=yes + _LT_TAGVAR(file_list_spec, $1)='${wl}-f,' + + if test "$GXX" = yes; then + case $host_os in aix4.[[012]]|aix4.[[012]].*) + # We only want to do this on AIX 4.2 and lower, the check + # below for broken collect2 doesn't work under 4.3+ + collect2name=`${CC} -print-prog-name=collect2` + if test -f "$collect2name" && + strings "$collect2name" | $GREP resolve_lib_name >/dev/null + then + # We have reworked collect2 + : + else + # We have old collect2 + _LT_TAGVAR(hardcode_direct, $1)=unsupported + # It fails to find uninstalled libraries when the uninstalled + # path is not listed in the libpath. Setting hardcode_minus_L + # to unsupported forces relinking + _LT_TAGVAR(hardcode_minus_L, $1)=yes + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)= + fi + esac + shared_flag='-shared' + if test "$aix_use_runtimelinking" = yes; then + shared_flag="$shared_flag "'${wl}-G' + fi + else + # not using gcc + if test "$host_cpu" = ia64; then + # VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release + # chokes on -Wl,-G. The following line is correct: + shared_flag='-G' + else + if test "$aix_use_runtimelinking" = yes; then + shared_flag='${wl}-G' + else + shared_flag='${wl}-bM:SRE' + fi + fi + fi + + _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-bexpall' + # It seems that -bexpall does not export symbols beginning with + # underscore (_), so it is better to generate a list of symbols to + # export. + _LT_TAGVAR(always_export_symbols, $1)=yes + if test "$aix_use_runtimelinking" = yes; then + # Warning - without using the other runtime loading flags (-brtl), + # -berok will link without error, but may produce a broken library. + _LT_TAGVAR(allow_undefined_flag, $1)='-berok' + # Determine the default libpath from the value encoded in an empty + # executable. + _LT_SYS_MODULE_PATH_AIX([$1]) + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-blibpath:$libdir:'"$aix_libpath" + + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags `if test "x${allow_undefined_flag}" != "x"; then func_echo_all "${wl}${allow_undefined_flag}"; else :; fi` '"\${wl}$exp_sym_flag:\$export_symbols $shared_flag" + else + if test "$host_cpu" = ia64; then + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-R $libdir:/usr/lib:/lib' + _LT_TAGVAR(allow_undefined_flag, $1)="-z nodefs" + _LT_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags ${wl}${allow_undefined_flag} '"\${wl}$exp_sym_flag:\$export_symbols" + else + # Determine the default libpath from the value encoded in an + # empty executable. + _LT_SYS_MODULE_PATH_AIX([$1]) + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-blibpath:$libdir:'"$aix_libpath" + # Warning - without using the other run time loading flags, + # -berok will link without error, but may produce a broken library. + _LT_TAGVAR(no_undefined_flag, $1)=' ${wl}-bernotok' + _LT_TAGVAR(allow_undefined_flag, $1)=' ${wl}-berok' + if test "$with_gnu_ld" = yes; then + # We only use this code for GNU lds that support --whole-archive. + _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive$convenience ${wl}--no-whole-archive' + else + # Exported symbols can be pulled into shared objects from archives + _LT_TAGVAR(whole_archive_flag_spec, $1)='$convenience' + fi + _LT_TAGVAR(archive_cmds_need_lc, $1)=yes + # This is similar to how AIX traditionally builds its shared + # libraries. + _LT_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs ${wl}-bnoentry $compiler_flags ${wl}-bE:$export_symbols${allow_undefined_flag}~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$soname' + fi + fi + ;; + + beos*) + if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then + _LT_TAGVAR(allow_undefined_flag, $1)=unsupported + # Joseph Beckenbach says some releases of gcc + # support --undefined. This deserves some investigation. FIXME + _LT_TAGVAR(archive_cmds, $1)='$CC -nostart $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + else + _LT_TAGVAR(ld_shlibs, $1)=no + fi + ;; + + chorus*) + case $cc_basename in + *) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + esac + ;; + + cygwin* | mingw* | pw32* | cegcc*) + case $GXX,$cc_basename in + ,cl* | no,cl*) + # Native MSVC + # hardcode_libdir_flag_spec is actually meaningless, as there is + # no search path for DLLs. + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)=' ' + _LT_TAGVAR(allow_undefined_flag, $1)=unsupported + _LT_TAGVAR(always_export_symbols, $1)=yes + _LT_TAGVAR(file_list_spec, $1)='@' + # Tell ltmain to make .lib files, not .a files. + libext=lib + # Tell ltmain to make .dll files, not .so files. + shrext_cmds=".dll" + # FIXME: Setting linknames here is a bad hack. + _LT_TAGVAR(archive_cmds, $1)='$CC -o $output_objdir/$soname $libobjs $compiler_flags $deplibs -Wl,-dll~linknames=' + _LT_TAGVAR(archive_expsym_cmds, $1)='if test "x`$SED 1q $export_symbols`" = xEXPORTS; then + $SED -n -e 's/\\\\\\\(.*\\\\\\\)/-link\\\ -EXPORT:\\\\\\\1/' -e '1\\\!p' < $export_symbols > $output_objdir/$soname.exp; + else + $SED -e 's/\\\\\\\(.*\\\\\\\)/-link\\\ -EXPORT:\\\\\\\1/' < $export_symbols > $output_objdir/$soname.exp; + fi~ + $CC -o $tool_output_objdir$soname $libobjs $compiler_flags $deplibs "@$tool_output_objdir$soname.exp" -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~ + linknames=' + # The linker will not automatically build a static lib if we build a DLL. + # _LT_TAGVAR(old_archive_from_new_cmds, $1)='true' + _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes + # Don't use ranlib + _LT_TAGVAR(old_postinstall_cmds, $1)='chmod 644 $oldlib' + _LT_TAGVAR(postlink_cmds, $1)='lt_outputfile="@OUTPUT@"~ + lt_tool_outputfile="@TOOL_OUTPUT@"~ + case $lt_outputfile in + *.exe|*.EXE) ;; + *) + lt_outputfile="$lt_outputfile.exe" + lt_tool_outputfile="$lt_tool_outputfile.exe" + ;; + esac~ + func_to_tool_file "$lt_outputfile"~ + if test "$MANIFEST_TOOL" != ":" && test -f "$lt_outputfile.manifest"; then + $MANIFEST_TOOL -manifest "$lt_tool_outputfile.manifest" -outputresource:"$lt_tool_outputfile" || exit 1; + $RM "$lt_outputfile.manifest"; + fi' + ;; + *) + # g++ + # _LT_TAGVAR(hardcode_libdir_flag_spec, $1) is actually meaningless, + # as there is no search path for DLLs. + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' + _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-all-symbols' + _LT_TAGVAR(allow_undefined_flag, $1)=unsupported + _LT_TAGVAR(always_export_symbols, $1)=no + _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes + + if $LD --help 2>&1 | $GREP 'auto-import' > /dev/null; then + _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' + # If the export-symbols file already is a .def file (1st line + # is EXPORTS), use it as is; otherwise, prepend... + _LT_TAGVAR(archive_expsym_cmds, $1)='if test "x`$SED 1q $export_symbols`" = xEXPORTS; then + cp $export_symbols $output_objdir/$soname.def; + else + echo EXPORTS > $output_objdir/$soname.def; + cat $export_symbols >> $output_objdir/$soname.def; + fi~ + $CC -shared -nostdlib $output_objdir/$soname.def $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' + else + _LT_TAGVAR(ld_shlibs, $1)=no + fi + ;; + esac + ;; + darwin* | rhapsody*) + _LT_DARWIN_LINKER_FEATURES($1) + ;; + + dgux*) + case $cc_basename in + ec++*) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + ghcx*) + # Green Hills C++ Compiler + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + *) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + esac + ;; + + freebsd2.*) + # C++ shared libraries reported to be fairly broken before + # switch to ELF + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + + freebsd-elf*) + _LT_TAGVAR(archive_cmds_need_lc, $1)=no + ;; + + freebsd* | dragonfly*) + # FreeBSD 3 and later use GNU C++ and GNU ld with standard ELF + # conventions + _LT_TAGVAR(ld_shlibs, $1)=yes + ;; + + haiku*) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + _LT_TAGVAR(link_all_deplibs, $1)=yes + ;; + + hpux9*) + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}+b ${wl}$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=: + _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_minus_L, $1)=yes # Not in the search PATH, + # but as the default + # location of the library. + + case $cc_basename in + CC*) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + aCC*) + _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$CC -b ${wl}+b ${wl}$install_libdir -o $output_objdir/$soname $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib' + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + # + # There doesn't appear to be a way to prevent this compiler from + # explicitly linking system object files so we need to strip them + # from the output so that they don't get included in the library + # dependencies. + output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | $EGREP "\-L"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"' + ;; + *) + if test "$GXX" = yes; then + _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$CC -shared -nostdlib $pic_flag ${wl}+b ${wl}$install_libdir -o $output_objdir/$soname $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib' + else + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + fi + ;; + esac + ;; + + hpux10*|hpux11*) + if test $with_gnu_ld = no; then + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}+b ${wl}$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=: + + case $host_cpu in + hppa*64*|ia64*) + ;; + *) + _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' + ;; + esac + fi + case $host_cpu in + hppa*64*|ia64*) + _LT_TAGVAR(hardcode_direct, $1)=no + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; + *) + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_direct_absolute, $1)=yes + _LT_TAGVAR(hardcode_minus_L, $1)=yes # Not in the search PATH, + # but as the default + # location of the library. + ;; + esac + + case $cc_basename in + CC*) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + aCC*) + case $host_cpu in + hppa*64*) + _LT_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + ;; + ia64*) + _LT_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + ;; + *) + _LT_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + ;; + esac + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + # + # There doesn't appear to be a way to prevent this compiler from + # explicitly linking system object files so we need to strip them + # from the output so that they don't get included in the library + # dependencies. + output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | $GREP "\-L"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"' + ;; + *) + if test "$GXX" = yes; then + if test $with_gnu_ld = no; then + case $host_cpu in + hppa*64*) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib -fPIC ${wl}+h ${wl}$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + ;; + ia64*) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $pic_flag ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + ;; + *) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $pic_flag ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + ;; + esac + fi + else + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + fi + ;; + esac + ;; + + interix[[3-9]]*) + _LT_TAGVAR(hardcode_direct, $1)=no + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir' + _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' + # Hack: On Interix 3.x, we cannot compile PIC because of a broken gcc. + # Instead, shared libraries are loaded at an image base (0x10000000 by + # default) and relocated if they conflict, which is a slow very memory + # consuming and fragmenting process. To avoid this, we pick a random, + # 256 KiB-aligned image base between 0x50000000 and 0x6FFC0000 at link + # time. Moving up from 0x10000000 also allows more sbrk(2) space. + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='sed "s,^,_," $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--retain-symbols-file,$output_objdir/$soname.expsym ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' + ;; + irix5* | irix6*) + case $cc_basename in + CC*) + # SGI C++ + _LT_TAGVAR(archive_cmds, $1)='$CC -shared -all -multigot $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib' + + # Archives containing C++ object files must be created using + # "CC -ar", where "CC" is the IRIX C++ compiler. This is + # necessary to make sure instantiated templates are included + # in the archive. + _LT_TAGVAR(old_archive_cmds, $1)='$CC -ar -WR,-u -o $oldlib $oldobjs' + ;; + *) + if test "$GXX" = yes; then + if test "$with_gnu_ld" = no; then + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' + else + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` -o $lib' + fi + fi + _LT_TAGVAR(link_all_deplibs, $1)=yes + ;; + esac + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=: + _LT_TAGVAR(inherit_rpath, $1)=yes + ;; + + linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) + case $cc_basename in + KCC*) + # Kuck and Associates, Inc. (KAI) C++ Compiler + + # KCC will only create a shared library if the output file + # ends with ".so" (or ".sl" for HP-UX), so rename the library + # to its proper name (with version) after linking. + _LT_TAGVAR(archive_cmds, $1)='tempext=`echo $shared_ext | $SED -e '\''s/\([[^()0-9A-Za-z{}]]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\${tempext}\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib; mv \$templib $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='tempext=`echo $shared_ext | $SED -e '\''s/\([[^()0-9A-Za-z{}]]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\${tempext}\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib ${wl}-retain-symbols-file,$export_symbols; mv \$templib $lib' + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + # + # There doesn't appear to be a way to prevent this compiler from + # explicitly linking system object files so we need to strip them + # from the output so that they don't get included in the library + # dependencies. + output_verbose_link_cmd='templist=`$CC $CFLAGS -v conftest.$objext -o libconftest$shared_ext 2>&1 | $GREP "ld"`; rm -f libconftest$shared_ext; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"' + + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir' + _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic' + + # Archives containing C++ object files must be created using + # "CC -Bstatic", where "CC" is the KAI C++ compiler. + _LT_TAGVAR(old_archive_cmds, $1)='$CC -Bstatic -o $oldlib $oldobjs' + ;; + icpc* | ecpc* ) + # Intel C++ + with_gnu_ld=yes + # version 8.0 and above of icpc choke on multiply defined symbols + # if we add $predep_objects and $postdep_objects, however 7.1 and + # earlier do not add the objects themselves. + case `$CC -V 2>&1` in + *"Version 7."*) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' + ;; + *) # Version 8.0 or newer + tmp_idyn= + case $host_cpu in + ia64*) tmp_idyn=' -i_dynamic';; + esac + _LT_TAGVAR(archive_cmds, $1)='$CC -shared'"$tmp_idyn"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared'"$tmp_idyn"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' + ;; + esac + _LT_TAGVAR(archive_cmds_need_lc, $1)=no + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir' + _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic' + _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive$convenience ${wl}--no-whole-archive' + ;; + pgCC* | pgcpp*) + # Portland Group C++ compiler + case `$CC -V` in + *pgCC\ [[1-5]].* | *pgcpp\ [[1-5]].*) + _LT_TAGVAR(prelink_cmds, $1)='tpldir=Template.dir~ + rm -rf $tpldir~ + $CC --prelink_objects --instantiation_dir $tpldir $objs $libobjs $compile_deplibs~ + compile_command="$compile_command `find $tpldir -name \*.o | sort | $NL2SP`"' + _LT_TAGVAR(old_archive_cmds, $1)='tpldir=Template.dir~ + rm -rf $tpldir~ + $CC --prelink_objects --instantiation_dir $tpldir $oldobjs$old_deplibs~ + $AR $AR_FLAGS $oldlib$oldobjs$old_deplibs `find $tpldir -name \*.o | sort | $NL2SP`~ + $RANLIB $oldlib' + _LT_TAGVAR(archive_cmds, $1)='tpldir=Template.dir~ + rm -rf $tpldir~ + $CC --prelink_objects --instantiation_dir $tpldir $predep_objects $libobjs $deplibs $convenience $postdep_objects~ + $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | sort | $NL2SP` $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='tpldir=Template.dir~ + rm -rf $tpldir~ + $CC --prelink_objects --instantiation_dir $tpldir $predep_objects $libobjs $deplibs $convenience $postdep_objects~ + $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | sort | $NL2SP` $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname ${wl}-retain-symbols-file ${wl}$export_symbols -o $lib' + ;; + *) # Version 6 and above use weak symbols + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname ${wl}-retain-symbols-file ${wl}$export_symbols -o $lib' + ;; + esac + + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}--rpath ${wl}$libdir' + _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic' + _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` ${wl}--no-whole-archive' + ;; + cxx*) + # Compaq C++ + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib ${wl}-retain-symbols-file $wl$export_symbols' + + runpath_var=LD_RUN_PATH + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-rpath $libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=: + + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + # + # There doesn't appear to be a way to prevent this compiler from + # explicitly linking system object files so we need to strip them + # from the output so that they don't get included in the library + # dependencies. + output_verbose_link_cmd='templist=`$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP "ld"`; templist=`func_echo_all "$templist" | $SED "s/\(^.*ld.*\)\( .*ld .*$\)/\1/"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "X$list" | $Xsed' + ;; + xl* | mpixl* | bgxl*) + # IBM XL 8.0 on PPC, with GNU ld + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' + _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic' + _LT_TAGVAR(archive_cmds, $1)='$CC -qmkshrobj $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + if test "x$supports_anon_versioning" = xyes; then + _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $output_objdir/$libname.ver~ + cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ + echo "local: *; };" >> $output_objdir/$libname.ver~ + $CC -qmkshrobj $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-version-script ${wl}$output_objdir/$libname.ver -o $lib' + fi + ;; + *) + case `$CC -V 2>&1 | sed 5q` in + *Sun\ C*) + # Sun C++ 5.9 + _LT_TAGVAR(no_undefined_flag, $1)=' -zdefs' + _LT_TAGVAR(archive_cmds, $1)='$CC -G${allow_undefined_flag} -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G${allow_undefined_flag} -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-retain-symbols-file ${wl}$export_symbols' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' + _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive`new_convenience=; for conv in $convenience\"\"; do test -z \"$conv\" || new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` ${wl}--no-whole-archive' + _LT_TAGVAR(compiler_needs_object, $1)=yes + + # Not sure whether something based on + # $CC $CFLAGS -v conftest.$objext -o libconftest$shared_ext 2>&1 + # would be better. + output_verbose_link_cmd='func_echo_all' + + # Archives containing C++ object files must be created using + # "CC -xar", where "CC" is the Sun C++ compiler. This is + # necessary to make sure instantiated templates are included + # in the archive. + _LT_TAGVAR(old_archive_cmds, $1)='$CC -xar -o $oldlib $oldobjs' + ;; + esac + ;; + esac + ;; + + lynxos*) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + + m88k*) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + + mvs*) + case $cc_basename in + cxx*) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + *) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + esac + ;; + + netbsd*) + if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then + _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $predep_objects $libobjs $deplibs $postdep_objects $linker_flags' + wlarc= + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + fi + # Workaround some broken pre-1.5 toolchains + output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP conftest.$objext | $SED -e "s:-lgcc -lc -lgcc::"' + ;; + + *nto* | *qnx*) + _LT_TAGVAR(ld_shlibs, $1)=yes + ;; + + openbsd2*) + # C++ shared libraries are fairly broken + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + + openbsd*) + if test -f /usr/libexec/ld.so; then + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + _LT_TAGVAR(hardcode_direct_absolute, $1)=yes + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $lib' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir' + if test -z "`echo __ELF__ | $CC -E - | grep __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-retain-symbols-file,$export_symbols -o $lib' + _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' + _LT_TAGVAR(whole_archive_flag_spec, $1)="$wlarc"'--whole-archive$convenience '"$wlarc"'--no-whole-archive' + fi + output_verbose_link_cmd=func_echo_all + else + _LT_TAGVAR(ld_shlibs, $1)=no + fi + ;; + + osf3* | osf4* | osf5*) + case $cc_basename in + KCC*) + # Kuck and Associates, Inc. (KAI) C++ Compiler + + # KCC will only create a shared library if the output file + # ends with ".so" (or ".sl" for HP-UX), so rename the library + # to its proper name (with version) after linking. + _LT_TAGVAR(archive_cmds, $1)='tempext=`echo $shared_ext | $SED -e '\''s/\([[^()0-9A-Za-z{}]]\)/\\\\\1/g'\''`; templib=`echo "$lib" | $SED -e "s/\${tempext}\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib; mv \$templib $lib' + + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=: + + # Archives containing C++ object files must be created using + # the KAI C++ compiler. + case $host in + osf3*) _LT_TAGVAR(old_archive_cmds, $1)='$CC -Bstatic -o $oldlib $oldobjs' ;; + *) _LT_TAGVAR(old_archive_cmds, $1)='$CC -o $oldlib $oldobjs' ;; + esac + ;; + RCC*) + # Rational C++ 2.4.1 + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + cxx*) + case $host in + osf3*) + _LT_TAGVAR(allow_undefined_flag, $1)=' ${wl}-expect_unresolved ${wl}\*' + _LT_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $soname `test -n "$verstring" && func_echo_all "${wl}-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' + ;; + *) + _LT_TAGVAR(allow_undefined_flag, $1)=' -expect_unresolved \*' + _LT_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -msym -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done~ + echo "-hidden">> $lib.exp~ + $CC -shared$allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -msym -soname $soname ${wl}-input ${wl}$lib.exp `test -n "$verstring" && $ECHO "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib~ + $RM $lib.exp' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-rpath $libdir' + ;; + esac + + _LT_TAGVAR(hardcode_libdir_separator, $1)=: + + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + # + # There doesn't appear to be a way to prevent this compiler from + # explicitly linking system object files so we need to strip them + # from the output so that they don't get included in the library + # dependencies. + output_verbose_link_cmd='templist=`$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP "ld" | $GREP -v "ld:"`; templist=`func_echo_all "$templist" | $SED "s/\(^.*ld.*\)\( .*ld.*$\)/\1/"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"' + ;; + *) + if test "$GXX" = yes && test "$with_gnu_ld" = no; then + _LT_TAGVAR(allow_undefined_flag, $1)=' ${wl}-expect_unresolved ${wl}\*' + case $host in + osf3*) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib ${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' + ;; + *) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -nostdlib ${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-msym ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' + ;; + esac + + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=: + + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"' + + else + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + fi + ;; + esac + ;; + + psos*) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + + sunos4*) + case $cc_basename in + CC*) + # Sun C++ 4.x + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + lcc*) + # Lucid + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + *) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + esac + ;; + + solaris*) + case $cc_basename in + CC* | sunCC*) + # Sun C++ 4.2, 5.x and Centerline C++ + _LT_TAGVAR(archive_cmds_need_lc,$1)=yes + _LT_TAGVAR(no_undefined_flag, $1)=' -zdefs' + _LT_TAGVAR(archive_cmds, $1)='$CC -G${allow_undefined_flag} -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ + $CC -G${allow_undefined_flag} ${wl}-M ${wl}$lib.exp -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp' + + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + case $host_os in + solaris2.[[0-5]] | solaris2.[[0-5]].*) ;; + *) + # The compiler driver will combine and reorder linker options, + # but understands `-z linker_flag'. + # Supported since Solaris 2.6 (maybe 2.5.1?) + _LT_TAGVAR(whole_archive_flag_spec, $1)='-z allextract$convenience -z defaultextract' + ;; + esac + _LT_TAGVAR(link_all_deplibs, $1)=yes + + output_verbose_link_cmd='func_echo_all' + + # Archives containing C++ object files must be created using + # "CC -xar", where "CC" is the Sun C++ compiler. This is + # necessary to make sure instantiated templates are included + # in the archive. + _LT_TAGVAR(old_archive_cmds, $1)='$CC -xar -o $oldlib $oldobjs' + ;; + gcx*) + # Green Hills C++ Compiler + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-h $wl$soname -o $lib' + + # The C++ compiler must be used to create the archive. + _LT_TAGVAR(old_archive_cmds, $1)='$CC $LDFLAGS -archive -o $oldlib $oldobjs' + ;; + *) + # GNU C++ compiler with Solaris linker + if test "$GXX" = yes && test "$with_gnu_ld" = no; then + _LT_TAGVAR(no_undefined_flag, $1)=' ${wl}-z ${wl}defs' + if $CC --version | $GREP -v '^2\.7' > /dev/null; then + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -nostdlib $LDFLAGS $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-h $wl$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ + $CC -shared $pic_flag -nostdlib ${wl}-M $wl$lib.exp -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp' + + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"' + else + # g++ 2.7 appears to require `-G' NOT `-shared' on this + # platform. + _LT_TAGVAR(archive_cmds, $1)='$CC -G -nostdlib $LDFLAGS $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-h $wl$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ + $CC -G -nostdlib ${wl}-M $wl$lib.exp -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp' + + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + output_verbose_link_cmd='$CC -G $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"' + fi + + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-R $wl$libdir' + case $host_os in + solaris2.[[0-5]] | solaris2.[[0-5]].*) ;; + *) + _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}-z ${wl}allextract$convenience ${wl}-z ${wl}defaultextract' + ;; + esac + fi + ;; + esac + ;; + + sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[[01]].[[10]]* | unixware7* | sco3.2v5.0.[[024]]*) + _LT_TAGVAR(no_undefined_flag, $1)='${wl}-z,text' + _LT_TAGVAR(archive_cmds_need_lc, $1)=no + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + runpath_var='LD_RUN_PATH' + + case $cc_basename in + CC*) + _LT_TAGVAR(archive_cmds, $1)='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + ;; + *) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + ;; + esac + ;; + + sysv5* | sco3.2v5* | sco5v6*) + # Note: We can NOT use -z defs as we might desire, because we do not + # link with -lc, and that would cause any symbols used from libc to + # always be unresolved, which means just about no library would + # ever link correctly. If we're not using GNU ld we use -z text + # though, which does catch some bad symbols but isn't as heavy-handed + # as -z defs. + _LT_TAGVAR(no_undefined_flag, $1)='${wl}-z,text' + _LT_TAGVAR(allow_undefined_flag, $1)='${wl}-z,nodefs' + _LT_TAGVAR(archive_cmds_need_lc, $1)=no + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-R,$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=':' + _LT_TAGVAR(link_all_deplibs, $1)=yes + _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-Bexport' + runpath_var='LD_RUN_PATH' + + case $cc_basename in + CC*) + _LT_TAGVAR(archive_cmds, $1)='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(old_archive_cmds, $1)='$CC -Tprelink_objects $oldobjs~ + '"$_LT_TAGVAR(old_archive_cmds, $1)" + _LT_TAGVAR(reload_cmds, $1)='$CC -Tprelink_objects $reload_objs~ + '"$_LT_TAGVAR(reload_cmds, $1)" + ;; + *) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + ;; + esac + ;; + + tandem*) + case $cc_basename in + NCC*) + # NonStop-UX NCC 3.20 + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + *) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + esac + ;; + + vxworks*) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + + *) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + esac + + AC_MSG_RESULT([$_LT_TAGVAR(ld_shlibs, $1)]) + test "$_LT_TAGVAR(ld_shlibs, $1)" = no && can_build_shared=no + + _LT_TAGVAR(GCC, $1)="$GXX" + _LT_TAGVAR(LD, $1)="$LD" + + ## CAVEAT EMPTOR: + ## There is no encapsulation within the following macros, do not change + ## the running order or otherwise move them around unless you know exactly + ## what you are doing... + _LT_SYS_HIDDEN_LIBDEPS($1) + _LT_COMPILER_PIC($1) + _LT_COMPILER_C_O($1) + _LT_COMPILER_FILE_LOCKS($1) + _LT_LINKER_SHLIBS($1) + _LT_SYS_DYNAMIC_LINKER($1) + _LT_LINKER_HARDCODE_LIBPATH($1) + + _LT_CONFIG($1) + fi # test -n "$compiler" + + CC=$lt_save_CC + CFLAGS=$lt_save_CFLAGS + LDCXX=$LD + LD=$lt_save_LD + GCC=$lt_save_GCC + with_gnu_ld=$lt_save_with_gnu_ld + lt_cv_path_LDCXX=$lt_cv_path_LD + lt_cv_path_LD=$lt_save_path_LD + lt_cv_prog_gnu_ldcxx=$lt_cv_prog_gnu_ld + lt_cv_prog_gnu_ld=$lt_save_with_gnu_ld +fi # test "$_lt_caught_CXX_error" != yes + +AC_LANG_POP +])# _LT_LANG_CXX_CONFIG + + +# _LT_FUNC_STRIPNAME_CNF +# ---------------------- +# func_stripname_cnf prefix suffix name +# strip PREFIX and SUFFIX off of NAME. +# PREFIX and SUFFIX must not contain globbing or regex special +# characters, hashes, percent signs, but SUFFIX may contain a leading +# dot (in which case that matches only a dot). +# +# This function is identical to the (non-XSI) version of func_stripname, +# except this one can be used by m4 code that may be executed by configure, +# rather than the libtool script. +m4_defun([_LT_FUNC_STRIPNAME_CNF],[dnl +AC_REQUIRE([_LT_DECL_SED]) +AC_REQUIRE([_LT_PROG_ECHO_BACKSLASH]) +func_stripname_cnf () +{ + case ${2} in + .*) func_stripname_result=`$ECHO "${3}" | $SED "s%^${1}%%; s%\\\\${2}\$%%"`;; + *) func_stripname_result=`$ECHO "${3}" | $SED "s%^${1}%%; s%${2}\$%%"`;; + esac +} # func_stripname_cnf +])# _LT_FUNC_STRIPNAME_CNF + +# _LT_SYS_HIDDEN_LIBDEPS([TAGNAME]) +# --------------------------------- +# Figure out "hidden" library dependencies from verbose +# compiler output when linking a shared library. +# Parse the compiler output and extract the necessary +# objects, libraries and library flags. +m4_defun([_LT_SYS_HIDDEN_LIBDEPS], +[m4_require([_LT_FILEUTILS_DEFAULTS])dnl +AC_REQUIRE([_LT_FUNC_STRIPNAME_CNF])dnl +# Dependencies to place before and after the object being linked: +_LT_TAGVAR(predep_objects, $1)= +_LT_TAGVAR(postdep_objects, $1)= +_LT_TAGVAR(predeps, $1)= +_LT_TAGVAR(postdeps, $1)= +_LT_TAGVAR(compiler_lib_search_path, $1)= + +dnl we can't use the lt_simple_compile_test_code here, +dnl because it contains code intended for an executable, +dnl not a library. It's possible we should let each +dnl tag define a new lt_????_link_test_code variable, +dnl but it's only used here... +m4_if([$1], [], [cat > conftest.$ac_ext <<_LT_EOF +int a; +void foo (void) { a = 0; } +_LT_EOF +], [$1], [CXX], [cat > conftest.$ac_ext <<_LT_EOF +class Foo +{ +public: + Foo (void) { a = 0; } +private: + int a; +}; +_LT_EOF +], [$1], [F77], [cat > conftest.$ac_ext <<_LT_EOF + subroutine foo + implicit none + integer*4 a + a=0 + return + end +_LT_EOF +], [$1], [FC], [cat > conftest.$ac_ext <<_LT_EOF + subroutine foo + implicit none + integer a + a=0 + return + end +_LT_EOF +], [$1], [GCJ], [cat > conftest.$ac_ext <<_LT_EOF +public class foo { + private int a; + public void bar (void) { + a = 0; + } +}; +_LT_EOF +], [$1], [GO], [cat > conftest.$ac_ext <<_LT_EOF +package foo +func foo() { +} +_LT_EOF +]) + +_lt_libdeps_save_CFLAGS=$CFLAGS +case "$CC $CFLAGS " in #( +*\ -flto*\ *) CFLAGS="$CFLAGS -fno-lto" ;; +*\ -fwhopr*\ *) CFLAGS="$CFLAGS -fno-whopr" ;; +*\ -fuse-linker-plugin*\ *) CFLAGS="$CFLAGS -fno-use-linker-plugin" ;; +esac + +dnl Parse the compiler output and extract the necessary +dnl objects, libraries and library flags. +if AC_TRY_EVAL(ac_compile); then + # Parse the compiler output and extract the necessary + # objects, libraries and library flags. + + # Sentinel used to keep track of whether or not we are before + # the conftest object file. + pre_test_object_deps_done=no + + for p in `eval "$output_verbose_link_cmd"`; do + case ${prev}${p} in + + -L* | -R* | -l*) + # Some compilers place space between "-{L,R}" and the path. + # Remove the space. + if test $p = "-L" || + test $p = "-R"; then + prev=$p + continue + fi + + # Expand the sysroot to ease extracting the directories later. + if test -z "$prev"; then + case $p in + -L*) func_stripname_cnf '-L' '' "$p"; prev=-L; p=$func_stripname_result ;; + -R*) func_stripname_cnf '-R' '' "$p"; prev=-R; p=$func_stripname_result ;; + -l*) func_stripname_cnf '-l' '' "$p"; prev=-l; p=$func_stripname_result ;; + esac + fi + case $p in + =*) func_stripname_cnf '=' '' "$p"; p=$lt_sysroot$func_stripname_result ;; + esac + if test "$pre_test_object_deps_done" = no; then + case ${prev} in + -L | -R) + # Internal compiler library paths should come after those + # provided the user. The postdeps already come after the + # user supplied libs so there is no need to process them. + if test -z "$_LT_TAGVAR(compiler_lib_search_path, $1)"; then + _LT_TAGVAR(compiler_lib_search_path, $1)="${prev}${p}" + else + _LT_TAGVAR(compiler_lib_search_path, $1)="${_LT_TAGVAR(compiler_lib_search_path, $1)} ${prev}${p}" + fi + ;; + # The "-l" case would never come before the object being + # linked, so don't bother handling this case. + esac + else + if test -z "$_LT_TAGVAR(postdeps, $1)"; then + _LT_TAGVAR(postdeps, $1)="${prev}${p}" + else + _LT_TAGVAR(postdeps, $1)="${_LT_TAGVAR(postdeps, $1)} ${prev}${p}" + fi + fi + prev= + ;; + + *.lto.$objext) ;; # Ignore GCC LTO objects + *.$objext) + # This assumes that the test object file only shows up + # once in the compiler output. + if test "$p" = "conftest.$objext"; then + pre_test_object_deps_done=yes + continue + fi + + if test "$pre_test_object_deps_done" = no; then + if test -z "$_LT_TAGVAR(predep_objects, $1)"; then + _LT_TAGVAR(predep_objects, $1)="$p" + else + _LT_TAGVAR(predep_objects, $1)="$_LT_TAGVAR(predep_objects, $1) $p" + fi + else + if test -z "$_LT_TAGVAR(postdep_objects, $1)"; then + _LT_TAGVAR(postdep_objects, $1)="$p" + else + _LT_TAGVAR(postdep_objects, $1)="$_LT_TAGVAR(postdep_objects, $1) $p" + fi + fi + ;; + + *) ;; # Ignore the rest. + + esac + done + + # Clean up. + rm -f a.out a.exe +else + echo "libtool.m4: error: problem compiling $1 test program" +fi + +$RM -f confest.$objext +CFLAGS=$_lt_libdeps_save_CFLAGS + +# PORTME: override above test on systems where it is broken +m4_if([$1], [CXX], +[case $host_os in +interix[[3-9]]*) + # Interix 3.5 installs completely hosed .la files for C++, so rather than + # hack all around it, let's just trust "g++" to DTRT. + _LT_TAGVAR(predep_objects,$1)= + _LT_TAGVAR(postdep_objects,$1)= + _LT_TAGVAR(postdeps,$1)= + ;; + +linux*) + case `$CC -V 2>&1 | sed 5q` in + *Sun\ C*) + # Sun C++ 5.9 + + # The more standards-conforming stlport4 library is + # incompatible with the Cstd library. Avoid specifying + # it if it's in CXXFLAGS. Ignore libCrun as + # -library=stlport4 depends on it. + case " $CXX $CXXFLAGS " in + *" -library=stlport4 "*) + solaris_use_stlport4=yes + ;; + esac + + if test "$solaris_use_stlport4" != yes; then + _LT_TAGVAR(postdeps,$1)='-library=Cstd -library=Crun' + fi + ;; + esac + ;; + +solaris*) + case $cc_basename in + CC* | sunCC*) + # The more standards-conforming stlport4 library is + # incompatible with the Cstd library. Avoid specifying + # it if it's in CXXFLAGS. Ignore libCrun as + # -library=stlport4 depends on it. + case " $CXX $CXXFLAGS " in + *" -library=stlport4 "*) + solaris_use_stlport4=yes + ;; + esac + + # Adding this requires a known-good setup of shared libraries for + # Sun compiler versions before 5.6, else PIC objects from an old + # archive will be linked into the output, leading to subtle bugs. + if test "$solaris_use_stlport4" != yes; then + _LT_TAGVAR(postdeps,$1)='-library=Cstd -library=Crun' + fi + ;; + esac + ;; +esac +]) + +case " $_LT_TAGVAR(postdeps, $1) " in +*" -lc "*) _LT_TAGVAR(archive_cmds_need_lc, $1)=no ;; +esac + _LT_TAGVAR(compiler_lib_search_dirs, $1)= +if test -n "${_LT_TAGVAR(compiler_lib_search_path, $1)}"; then + _LT_TAGVAR(compiler_lib_search_dirs, $1)=`echo " ${_LT_TAGVAR(compiler_lib_search_path, $1)}" | ${SED} -e 's! -L! !g' -e 's!^ !!'` +fi +_LT_TAGDECL([], [compiler_lib_search_dirs], [1], + [The directories searched by this compiler when creating a shared library]) +_LT_TAGDECL([], [predep_objects], [1], + [Dependencies to place before and after the objects being linked to + create a shared library]) +_LT_TAGDECL([], [postdep_objects], [1]) +_LT_TAGDECL([], [predeps], [1]) +_LT_TAGDECL([], [postdeps], [1]) +_LT_TAGDECL([], [compiler_lib_search_path], [1], + [The library search path used internally by the compiler when linking + a shared library]) +])# _LT_SYS_HIDDEN_LIBDEPS + + +# _LT_LANG_F77_CONFIG([TAG]) +# -------------------------- +# Ensure that the configuration variables for a Fortran 77 compiler are +# suitably defined. These variables are subsequently used by _LT_CONFIG +# to write the compiler configuration to `libtool'. +m4_defun([_LT_LANG_F77_CONFIG], +[AC_LANG_PUSH(Fortran 77) +if test -z "$F77" || test "X$F77" = "Xno"; then + _lt_disable_F77=yes +fi + +_LT_TAGVAR(archive_cmds_need_lc, $1)=no +_LT_TAGVAR(allow_undefined_flag, $1)= +_LT_TAGVAR(always_export_symbols, $1)=no +_LT_TAGVAR(archive_expsym_cmds, $1)= +_LT_TAGVAR(export_dynamic_flag_spec, $1)= +_LT_TAGVAR(hardcode_direct, $1)=no +_LT_TAGVAR(hardcode_direct_absolute, $1)=no +_LT_TAGVAR(hardcode_libdir_flag_spec, $1)= +_LT_TAGVAR(hardcode_libdir_separator, $1)= +_LT_TAGVAR(hardcode_minus_L, $1)=no +_LT_TAGVAR(hardcode_automatic, $1)=no +_LT_TAGVAR(inherit_rpath, $1)=no +_LT_TAGVAR(module_cmds, $1)= +_LT_TAGVAR(module_expsym_cmds, $1)= +_LT_TAGVAR(link_all_deplibs, $1)=unknown +_LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds +_LT_TAGVAR(reload_flag, $1)=$reload_flag +_LT_TAGVAR(reload_cmds, $1)=$reload_cmds +_LT_TAGVAR(no_undefined_flag, $1)= +_LT_TAGVAR(whole_archive_flag_spec, $1)= +_LT_TAGVAR(enable_shared_with_static_runtimes, $1)=no + +# Source file extension for f77 test sources. +ac_ext=f + +# Object file extension for compiled f77 test sources. +objext=o +_LT_TAGVAR(objext, $1)=$objext + +# No sense in running all these tests if we already determined that +# the F77 compiler isn't working. Some variables (like enable_shared) +# are currently assumed to apply to all compilers on this platform, +# and will be corrupted by setting them based on a non-working compiler. +if test "$_lt_disable_F77" != yes; then + # Code to be used in simple compile tests + lt_simple_compile_test_code="\ + subroutine t + return + end +" + + # Code to be used in simple link tests + lt_simple_link_test_code="\ + program t + end +" + + # ltmain only uses $CC for tagged configurations so make sure $CC is set. + _LT_TAG_COMPILER + + # save warnings/boilerplate of simple test code + _LT_COMPILER_BOILERPLATE + _LT_LINKER_BOILERPLATE + + # Allow CC to be a program name with arguments. + lt_save_CC="$CC" + lt_save_GCC=$GCC + lt_save_CFLAGS=$CFLAGS + CC=${F77-"f77"} + CFLAGS=$FFLAGS + compiler=$CC + _LT_TAGVAR(compiler, $1)=$CC + _LT_CC_BASENAME([$compiler]) + GCC=$G77 + if test -n "$compiler"; then + AC_MSG_CHECKING([if libtool supports shared libraries]) + AC_MSG_RESULT([$can_build_shared]) + + AC_MSG_CHECKING([whether to build shared libraries]) + test "$can_build_shared" = "no" && enable_shared=no + + # On AIX, shared libraries and static libraries use the same namespace, and + # are all built from PIC. + case $host_os in + aix3*) + test "$enable_shared" = yes && enable_static=no + if test -n "$RANLIB"; then + archive_cmds="$archive_cmds~\$RANLIB \$lib" + postinstall_cmds='$RANLIB $lib' + fi + ;; + aix[[4-9]]*) + if test "$host_cpu" != ia64 && test "$aix_use_runtimelinking" = no ; then + test "$enable_shared" = yes && enable_static=no + fi + ;; + esac + AC_MSG_RESULT([$enable_shared]) + + AC_MSG_CHECKING([whether to build static libraries]) + # Make sure either enable_shared or enable_static is yes. + test "$enable_shared" = yes || enable_static=yes + AC_MSG_RESULT([$enable_static]) + + _LT_TAGVAR(GCC, $1)="$G77" + _LT_TAGVAR(LD, $1)="$LD" + + ## CAVEAT EMPTOR: + ## There is no encapsulation within the following macros, do not change + ## the running order or otherwise move them around unless you know exactly + ## what you are doing... + _LT_COMPILER_PIC($1) + _LT_COMPILER_C_O($1) + _LT_COMPILER_FILE_LOCKS($1) + _LT_LINKER_SHLIBS($1) + _LT_SYS_DYNAMIC_LINKER($1) + _LT_LINKER_HARDCODE_LIBPATH($1) + + _LT_CONFIG($1) + fi # test -n "$compiler" + + GCC=$lt_save_GCC + CC="$lt_save_CC" + CFLAGS="$lt_save_CFLAGS" +fi # test "$_lt_disable_F77" != yes + +AC_LANG_POP +])# _LT_LANG_F77_CONFIG + + +# _LT_LANG_FC_CONFIG([TAG]) +# ------------------------- +# Ensure that the configuration variables for a Fortran compiler are +# suitably defined. These variables are subsequently used by _LT_CONFIG +# to write the compiler configuration to `libtool'. +m4_defun([_LT_LANG_FC_CONFIG], +[AC_LANG_PUSH(Fortran) + +if test -z "$FC" || test "X$FC" = "Xno"; then + _lt_disable_FC=yes +fi + +_LT_TAGVAR(archive_cmds_need_lc, $1)=no +_LT_TAGVAR(allow_undefined_flag, $1)= +_LT_TAGVAR(always_export_symbols, $1)=no +_LT_TAGVAR(archive_expsym_cmds, $1)= +_LT_TAGVAR(export_dynamic_flag_spec, $1)= +_LT_TAGVAR(hardcode_direct, $1)=no +_LT_TAGVAR(hardcode_direct_absolute, $1)=no +_LT_TAGVAR(hardcode_libdir_flag_spec, $1)= +_LT_TAGVAR(hardcode_libdir_separator, $1)= +_LT_TAGVAR(hardcode_minus_L, $1)=no +_LT_TAGVAR(hardcode_automatic, $1)=no +_LT_TAGVAR(inherit_rpath, $1)=no +_LT_TAGVAR(module_cmds, $1)= +_LT_TAGVAR(module_expsym_cmds, $1)= +_LT_TAGVAR(link_all_deplibs, $1)=unknown +_LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds +_LT_TAGVAR(reload_flag, $1)=$reload_flag +_LT_TAGVAR(reload_cmds, $1)=$reload_cmds +_LT_TAGVAR(no_undefined_flag, $1)= +_LT_TAGVAR(whole_archive_flag_spec, $1)= +_LT_TAGVAR(enable_shared_with_static_runtimes, $1)=no + +# Source file extension for fc test sources. +ac_ext=${ac_fc_srcext-f} + +# Object file extension for compiled fc test sources. +objext=o +_LT_TAGVAR(objext, $1)=$objext + +# No sense in running all these tests if we already determined that +# the FC compiler isn't working. Some variables (like enable_shared) +# are currently assumed to apply to all compilers on this platform, +# and will be corrupted by setting them based on a non-working compiler. +if test "$_lt_disable_FC" != yes; then + # Code to be used in simple compile tests + lt_simple_compile_test_code="\ + subroutine t + return + end +" + + # Code to be used in simple link tests + lt_simple_link_test_code="\ + program t + end +" + + # ltmain only uses $CC for tagged configurations so make sure $CC is set. + _LT_TAG_COMPILER + + # save warnings/boilerplate of simple test code + _LT_COMPILER_BOILERPLATE + _LT_LINKER_BOILERPLATE + + # Allow CC to be a program name with arguments. + lt_save_CC="$CC" + lt_save_GCC=$GCC + lt_save_CFLAGS=$CFLAGS + CC=${FC-"f95"} + CFLAGS=$FCFLAGS + compiler=$CC + GCC=$ac_cv_fc_compiler_gnu + + _LT_TAGVAR(compiler, $1)=$CC + _LT_CC_BASENAME([$compiler]) + + if test -n "$compiler"; then + AC_MSG_CHECKING([if libtool supports shared libraries]) + AC_MSG_RESULT([$can_build_shared]) + + AC_MSG_CHECKING([whether to build shared libraries]) + test "$can_build_shared" = "no" && enable_shared=no + + # On AIX, shared libraries and static libraries use the same namespace, and + # are all built from PIC. + case $host_os in + aix3*) + test "$enable_shared" = yes && enable_static=no + if test -n "$RANLIB"; then + archive_cmds="$archive_cmds~\$RANLIB \$lib" + postinstall_cmds='$RANLIB $lib' + fi + ;; + aix[[4-9]]*) + if test "$host_cpu" != ia64 && test "$aix_use_runtimelinking" = no ; then + test "$enable_shared" = yes && enable_static=no + fi + ;; + esac + AC_MSG_RESULT([$enable_shared]) + + AC_MSG_CHECKING([whether to build static libraries]) + # Make sure either enable_shared or enable_static is yes. + test "$enable_shared" = yes || enable_static=yes + AC_MSG_RESULT([$enable_static]) + + _LT_TAGVAR(GCC, $1)="$ac_cv_fc_compiler_gnu" + _LT_TAGVAR(LD, $1)="$LD" + + ## CAVEAT EMPTOR: + ## There is no encapsulation within the following macros, do not change + ## the running order or otherwise move them around unless you know exactly + ## what you are doing... + _LT_SYS_HIDDEN_LIBDEPS($1) + _LT_COMPILER_PIC($1) + _LT_COMPILER_C_O($1) + _LT_COMPILER_FILE_LOCKS($1) + _LT_LINKER_SHLIBS($1) + _LT_SYS_DYNAMIC_LINKER($1) + _LT_LINKER_HARDCODE_LIBPATH($1) + + _LT_CONFIG($1) + fi # test -n "$compiler" + + GCC=$lt_save_GCC + CC=$lt_save_CC + CFLAGS=$lt_save_CFLAGS +fi # test "$_lt_disable_FC" != yes + +AC_LANG_POP +])# _LT_LANG_FC_CONFIG + + +# _LT_LANG_GCJ_CONFIG([TAG]) +# -------------------------- +# Ensure that the configuration variables for the GNU Java Compiler compiler +# are suitably defined. These variables are subsequently used by _LT_CONFIG +# to write the compiler configuration to `libtool'. +m4_defun([_LT_LANG_GCJ_CONFIG], +[AC_REQUIRE([LT_PROG_GCJ])dnl +AC_LANG_SAVE + +# Source file extension for Java test sources. +ac_ext=java + +# Object file extension for compiled Java test sources. +objext=o +_LT_TAGVAR(objext, $1)=$objext + +# Code to be used in simple compile tests +lt_simple_compile_test_code="class foo {}" + +# Code to be used in simple link tests +lt_simple_link_test_code='public class conftest { public static void main(String[[]] argv) {}; }' + +# ltmain only uses $CC for tagged configurations so make sure $CC is set. +_LT_TAG_COMPILER + +# save warnings/boilerplate of simple test code +_LT_COMPILER_BOILERPLATE +_LT_LINKER_BOILERPLATE + +# Allow CC to be a program name with arguments. +lt_save_CC=$CC +lt_save_CFLAGS=$CFLAGS +lt_save_GCC=$GCC +GCC=yes +CC=${GCJ-"gcj"} +CFLAGS=$GCJFLAGS +compiler=$CC +_LT_TAGVAR(compiler, $1)=$CC +_LT_TAGVAR(LD, $1)="$LD" +_LT_CC_BASENAME([$compiler]) + +# GCJ did not exist at the time GCC didn't implicitly link libc in. +_LT_TAGVAR(archive_cmds_need_lc, $1)=no + +_LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds +_LT_TAGVAR(reload_flag, $1)=$reload_flag +_LT_TAGVAR(reload_cmds, $1)=$reload_cmds + +## CAVEAT EMPTOR: +## There is no encapsulation within the following macros, do not change +## the running order or otherwise move them around unless you know exactly +## what you are doing... +if test -n "$compiler"; then + _LT_COMPILER_NO_RTTI($1) + _LT_COMPILER_PIC($1) + _LT_COMPILER_C_O($1) + _LT_COMPILER_FILE_LOCKS($1) + _LT_LINKER_SHLIBS($1) + _LT_LINKER_HARDCODE_LIBPATH($1) + + _LT_CONFIG($1) +fi + +AC_LANG_RESTORE + +GCC=$lt_save_GCC +CC=$lt_save_CC +CFLAGS=$lt_save_CFLAGS +])# _LT_LANG_GCJ_CONFIG + + +# _LT_LANG_GO_CONFIG([TAG]) +# -------------------------- +# Ensure that the configuration variables for the GNU Go compiler +# are suitably defined. These variables are subsequently used by _LT_CONFIG +# to write the compiler configuration to `libtool'. +m4_defun([_LT_LANG_GO_CONFIG], +[AC_REQUIRE([LT_PROG_GO])dnl +AC_LANG_SAVE + +# Source file extension for Go test sources. +ac_ext=go + +# Object file extension for compiled Go test sources. +objext=o +_LT_TAGVAR(objext, $1)=$objext + +# Code to be used in simple compile tests +lt_simple_compile_test_code="package main; func main() { }" + +# Code to be used in simple link tests +lt_simple_link_test_code='package main; func main() { }' + +# ltmain only uses $CC for tagged configurations so make sure $CC is set. +_LT_TAG_COMPILER + +# save warnings/boilerplate of simple test code +_LT_COMPILER_BOILERPLATE +_LT_LINKER_BOILERPLATE + +# Allow CC to be a program name with arguments. +lt_save_CC=$CC +lt_save_CFLAGS=$CFLAGS +lt_save_GCC=$GCC +GCC=yes +CC=${GOC-"gccgo"} +CFLAGS=$GOFLAGS +compiler=$CC +_LT_TAGVAR(compiler, $1)=$CC +_LT_TAGVAR(LD, $1)="$LD" +_LT_CC_BASENAME([$compiler]) + +# Go did not exist at the time GCC didn't implicitly link libc in. +_LT_TAGVAR(archive_cmds_need_lc, $1)=no + +_LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds +_LT_TAGVAR(reload_flag, $1)=$reload_flag +_LT_TAGVAR(reload_cmds, $1)=$reload_cmds + +## CAVEAT EMPTOR: +## There is no encapsulation within the following macros, do not change +## the running order or otherwise move them around unless you know exactly +## what you are doing... +if test -n "$compiler"; then + _LT_COMPILER_NO_RTTI($1) + _LT_COMPILER_PIC($1) + _LT_COMPILER_C_O($1) + _LT_COMPILER_FILE_LOCKS($1) + _LT_LINKER_SHLIBS($1) + _LT_LINKER_HARDCODE_LIBPATH($1) + + _LT_CONFIG($1) +fi + +AC_LANG_RESTORE + +GCC=$lt_save_GCC +CC=$lt_save_CC +CFLAGS=$lt_save_CFLAGS +])# _LT_LANG_GO_CONFIG + + +# _LT_LANG_RC_CONFIG([TAG]) +# ------------------------- +# Ensure that the configuration variables for the Windows resource compiler +# are suitably defined. These variables are subsequently used by _LT_CONFIG +# to write the compiler configuration to `libtool'. +m4_defun([_LT_LANG_RC_CONFIG], +[AC_REQUIRE([LT_PROG_RC])dnl +AC_LANG_SAVE + +# Source file extension for RC test sources. +ac_ext=rc + +# Object file extension for compiled RC test sources. +objext=o +_LT_TAGVAR(objext, $1)=$objext + +# Code to be used in simple compile tests +lt_simple_compile_test_code='sample MENU { MENUITEM "&Soup", 100, CHECKED }' + +# Code to be used in simple link tests +lt_simple_link_test_code="$lt_simple_compile_test_code" + +# ltmain only uses $CC for tagged configurations so make sure $CC is set. +_LT_TAG_COMPILER + +# save warnings/boilerplate of simple test code +_LT_COMPILER_BOILERPLATE +_LT_LINKER_BOILERPLATE + +# Allow CC to be a program name with arguments. +lt_save_CC="$CC" +lt_save_CFLAGS=$CFLAGS +lt_save_GCC=$GCC +GCC= +CC=${RC-"windres"} +CFLAGS= +compiler=$CC +_LT_TAGVAR(compiler, $1)=$CC +_LT_CC_BASENAME([$compiler]) +_LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)=yes + +if test -n "$compiler"; then + : + _LT_CONFIG($1) +fi + +GCC=$lt_save_GCC +AC_LANG_RESTORE +CC=$lt_save_CC +CFLAGS=$lt_save_CFLAGS +])# _LT_LANG_RC_CONFIG + + +# LT_PROG_GCJ +# ----------- +AC_DEFUN([LT_PROG_GCJ], +[m4_ifdef([AC_PROG_GCJ], [AC_PROG_GCJ], + [m4_ifdef([A][M_PROG_GCJ], [A][M_PROG_GCJ], + [AC_CHECK_TOOL(GCJ, gcj,) + test "x${GCJFLAGS+set}" = xset || GCJFLAGS="-g -O2" + AC_SUBST(GCJFLAGS)])])[]dnl +]) + +# Old name: +AU_ALIAS([LT_AC_PROG_GCJ], [LT_PROG_GCJ]) +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([LT_AC_PROG_GCJ], []) + + +# LT_PROG_GO +# ---------- +AC_DEFUN([LT_PROG_GO], +[AC_CHECK_TOOL(GOC, gccgo,) +]) + + +# LT_PROG_RC +# ---------- +AC_DEFUN([LT_PROG_RC], +[AC_CHECK_TOOL(RC, windres,) +]) + +# Old name: +AU_ALIAS([LT_AC_PROG_RC], [LT_PROG_RC]) +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([LT_AC_PROG_RC], []) + + +# _LT_DECL_EGREP +# -------------- +# If we don't have a new enough Autoconf to choose the best grep +# available, choose the one first in the user's PATH. +m4_defun([_LT_DECL_EGREP], +[AC_REQUIRE([AC_PROG_EGREP])dnl +AC_REQUIRE([AC_PROG_FGREP])dnl +test -z "$GREP" && GREP=grep +_LT_DECL([], [GREP], [1], [A grep program that handles long lines]) +_LT_DECL([], [EGREP], [1], [An ERE matcher]) +_LT_DECL([], [FGREP], [1], [A literal string matcher]) +dnl Non-bleeding-edge autoconf doesn't subst GREP, so do it here too +AC_SUBST([GREP]) +]) + + +# _LT_DECL_OBJDUMP +# -------------- +# If we don't have a new enough Autoconf to choose the best objdump +# available, choose the one first in the user's PATH. +m4_defun([_LT_DECL_OBJDUMP], +[AC_CHECK_TOOL(OBJDUMP, objdump, false) +test -z "$OBJDUMP" && OBJDUMP=objdump +_LT_DECL([], [OBJDUMP], [1], [An object symbol dumper]) +AC_SUBST([OBJDUMP]) +]) + +# _LT_DECL_DLLTOOL +# ---------------- +# Ensure DLLTOOL variable is set. +m4_defun([_LT_DECL_DLLTOOL], +[AC_CHECK_TOOL(DLLTOOL, dlltool, false) +test -z "$DLLTOOL" && DLLTOOL=dlltool +_LT_DECL([], [DLLTOOL], [1], [DLL creation program]) +AC_SUBST([DLLTOOL]) +]) + +# _LT_DECL_SED +# ------------ +# Check for a fully-functional sed program, that truncates +# as few characters as possible. Prefer GNU sed if found. +m4_defun([_LT_DECL_SED], +[AC_PROG_SED +test -z "$SED" && SED=sed +Xsed="$SED -e 1s/^X//" +_LT_DECL([], [SED], [1], [A sed program that does not truncate output]) +_LT_DECL([], [Xsed], ["\$SED -e 1s/^X//"], + [Sed that helps us avoid accidentally triggering echo(1) options like -n]) +])# _LT_DECL_SED + +m4_ifndef([AC_PROG_SED], [ +############################################################ +# NOTE: This macro has been submitted for inclusion into # +# GNU Autoconf as AC_PROG_SED. When it is available in # +# a released version of Autoconf we should remove this # +# macro and use it instead. # +############################################################ + +m4_defun([AC_PROG_SED], +[AC_MSG_CHECKING([for a sed that does not truncate output]) +AC_CACHE_VAL(lt_cv_path_SED, +[# Loop through the user's path and test for sed and gsed. +# Then use that list of sed's as ones to test for truncation. +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for lt_ac_prog in sed gsed; do + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$lt_ac_prog$ac_exec_ext"; then + lt_ac_sed_list="$lt_ac_sed_list $as_dir/$lt_ac_prog$ac_exec_ext" + fi + done + done +done +IFS=$as_save_IFS +lt_ac_max=0 +lt_ac_count=0 +# Add /usr/xpg4/bin/sed as it is typically found on Solaris +# along with /bin/sed that truncates output. +for lt_ac_sed in $lt_ac_sed_list /usr/xpg4/bin/sed; do + test ! -f $lt_ac_sed && continue + cat /dev/null > conftest.in + lt_ac_count=0 + echo $ECHO_N "0123456789$ECHO_C" >conftest.in + # Check for GNU sed and select it if it is found. + if "$lt_ac_sed" --version 2>&1 < /dev/null | grep 'GNU' > /dev/null; then + lt_cv_path_SED=$lt_ac_sed + break + fi + while true; do + cat conftest.in conftest.in >conftest.tmp + mv conftest.tmp conftest.in + cp conftest.in conftest.nl + echo >>conftest.nl + $lt_ac_sed -e 's/a$//' < conftest.nl >conftest.out || break + cmp -s conftest.out conftest.nl || break + # 10000 chars as input seems more than enough + test $lt_ac_count -gt 10 && break + lt_ac_count=`expr $lt_ac_count + 1` + if test $lt_ac_count -gt $lt_ac_max; then + lt_ac_max=$lt_ac_count + lt_cv_path_SED=$lt_ac_sed + fi + done +done +]) +SED=$lt_cv_path_SED +AC_SUBST([SED]) +AC_MSG_RESULT([$SED]) +])#AC_PROG_SED +])#m4_ifndef + +# Old name: +AU_ALIAS([LT_AC_PROG_SED], [AC_PROG_SED]) +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([LT_AC_PROG_SED], []) + + +# _LT_CHECK_SHELL_FEATURES +# ------------------------ +# Find out whether the shell is Bourne or XSI compatible, +# or has some other useful features. +m4_defun([_LT_CHECK_SHELL_FEATURES], +[AC_MSG_CHECKING([whether the shell understands some XSI constructs]) +# Try some XSI features +xsi_shell=no +( _lt_dummy="a/b/c" + test "${_lt_dummy##*/},${_lt_dummy%/*},${_lt_dummy#??}"${_lt_dummy%"$_lt_dummy"}, \ + = c,a/b,b/c, \ + && eval 'test $(( 1 + 1 )) -eq 2 \ + && test "${#_lt_dummy}" -eq 5' ) >/dev/null 2>&1 \ + && xsi_shell=yes +AC_MSG_RESULT([$xsi_shell]) +_LT_CONFIG_LIBTOOL_INIT([xsi_shell='$xsi_shell']) + +AC_MSG_CHECKING([whether the shell understands "+="]) +lt_shell_append=no +( foo=bar; set foo baz; eval "$[1]+=\$[2]" && test "$foo" = barbaz ) \ + >/dev/null 2>&1 \ + && lt_shell_append=yes +AC_MSG_RESULT([$lt_shell_append]) +_LT_CONFIG_LIBTOOL_INIT([lt_shell_append='$lt_shell_append']) + +if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then + lt_unset=unset +else + lt_unset=false +fi +_LT_DECL([], [lt_unset], [0], [whether the shell understands "unset"])dnl + +# test EBCDIC or ASCII +case `echo X|tr X '\101'` in + A) # ASCII based system + # \n is not interpreted correctly by Solaris 8 /usr/ucb/tr + lt_SP2NL='tr \040 \012' + lt_NL2SP='tr \015\012 \040\040' + ;; + *) # EBCDIC based system + lt_SP2NL='tr \100 \n' + lt_NL2SP='tr \r\n \100\100' + ;; +esac +_LT_DECL([SP2NL], [lt_SP2NL], [1], [turn spaces into newlines])dnl +_LT_DECL([NL2SP], [lt_NL2SP], [1], [turn newlines into spaces])dnl +])# _LT_CHECK_SHELL_FEATURES + + +# _LT_PROG_FUNCTION_REPLACE (FUNCNAME, REPLACEMENT-BODY) +# ------------------------------------------------------ +# In `$cfgfile', look for function FUNCNAME delimited by `^FUNCNAME ()$' and +# '^} FUNCNAME ', and replace its body with REPLACEMENT-BODY. +m4_defun([_LT_PROG_FUNCTION_REPLACE], +[dnl { +sed -e '/^$1 ()$/,/^} # $1 /c\ +$1 ()\ +{\ +m4_bpatsubsts([$2], [$], [\\], [^\([ ]\)], [\\\1]) +} # Extended-shell $1 implementation' "$cfgfile" > $cfgfile.tmp \ + && mv -f "$cfgfile.tmp" "$cfgfile" \ + || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") +test 0 -eq $? || _lt_function_replace_fail=: +]) + + +# _LT_PROG_REPLACE_SHELLFNS +# ------------------------- +# Replace existing portable implementations of several shell functions with +# equivalent extended shell implementations where those features are available.. +m4_defun([_LT_PROG_REPLACE_SHELLFNS], +[if test x"$xsi_shell" = xyes; then + _LT_PROG_FUNCTION_REPLACE([func_dirname], [dnl + case ${1} in + */*) func_dirname_result="${1%/*}${2}" ;; + * ) func_dirname_result="${3}" ;; + esac]) + + _LT_PROG_FUNCTION_REPLACE([func_basename], [dnl + func_basename_result="${1##*/}"]) + + _LT_PROG_FUNCTION_REPLACE([func_dirname_and_basename], [dnl + case ${1} in + */*) func_dirname_result="${1%/*}${2}" ;; + * ) func_dirname_result="${3}" ;; + esac + func_basename_result="${1##*/}"]) + + _LT_PROG_FUNCTION_REPLACE([func_stripname], [dnl + # pdksh 5.2.14 does not do ${X%$Y} correctly if both X and Y are + # positional parameters, so assign one to ordinary parameter first. + func_stripname_result=${3} + func_stripname_result=${func_stripname_result#"${1}"} + func_stripname_result=${func_stripname_result%"${2}"}]) + + _LT_PROG_FUNCTION_REPLACE([func_split_long_opt], [dnl + func_split_long_opt_name=${1%%=*} + func_split_long_opt_arg=${1#*=}]) + + _LT_PROG_FUNCTION_REPLACE([func_split_short_opt], [dnl + func_split_short_opt_arg=${1#??} + func_split_short_opt_name=${1%"$func_split_short_opt_arg"}]) + + _LT_PROG_FUNCTION_REPLACE([func_lo2o], [dnl + case ${1} in + *.lo) func_lo2o_result=${1%.lo}.${objext} ;; + *) func_lo2o_result=${1} ;; + esac]) + + _LT_PROG_FUNCTION_REPLACE([func_xform], [ func_xform_result=${1%.*}.lo]) + + _LT_PROG_FUNCTION_REPLACE([func_arith], [ func_arith_result=$(( $[*] ))]) + + _LT_PROG_FUNCTION_REPLACE([func_len], [ func_len_result=${#1}]) +fi + +if test x"$lt_shell_append" = xyes; then + _LT_PROG_FUNCTION_REPLACE([func_append], [ eval "${1}+=\\${2}"]) + + _LT_PROG_FUNCTION_REPLACE([func_append_quoted], [dnl + func_quote_for_eval "${2}" +dnl m4 expansion turns \\\\ into \\, and then the shell eval turns that into \ + eval "${1}+=\\\\ \\$func_quote_for_eval_result"]) + + # Save a `func_append' function call where possible by direct use of '+=' + sed -e 's%func_append \([[a-zA-Z_]]\{1,\}\) "%\1+="%g' $cfgfile > $cfgfile.tmp \ + && mv -f "$cfgfile.tmp" "$cfgfile" \ + || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") + test 0 -eq $? || _lt_function_replace_fail=: +else + # Save a `func_append' function call even when '+=' is not available + sed -e 's%func_append \([[a-zA-Z_]]\{1,\}\) "%\1="$\1%g' $cfgfile > $cfgfile.tmp \ + && mv -f "$cfgfile.tmp" "$cfgfile" \ + || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") + test 0 -eq $? || _lt_function_replace_fail=: +fi + +if test x"$_lt_function_replace_fail" = x":"; then + AC_MSG_WARN([Unable to substitute extended shell functions in $ofile]) +fi +]) + +# _LT_PATH_CONVERSION_FUNCTIONS +# ----------------------------- +# Determine which file name conversion functions should be used by +# func_to_host_file (and, implicitly, by func_to_host_path). These are needed +# for certain cross-compile configurations and native mingw. +m4_defun([_LT_PATH_CONVERSION_FUNCTIONS], +[AC_REQUIRE([AC_CANONICAL_HOST])dnl +AC_REQUIRE([AC_CANONICAL_BUILD])dnl +AC_MSG_CHECKING([how to convert $build file names to $host format]) +AC_CACHE_VAL(lt_cv_to_host_file_cmd, +[case $host in + *-*-mingw* ) + case $build in + *-*-mingw* ) # actually msys + lt_cv_to_host_file_cmd=func_convert_file_msys_to_w32 + ;; + *-*-cygwin* ) + lt_cv_to_host_file_cmd=func_convert_file_cygwin_to_w32 + ;; + * ) # otherwise, assume *nix + lt_cv_to_host_file_cmd=func_convert_file_nix_to_w32 + ;; + esac + ;; + *-*-cygwin* ) + case $build in + *-*-mingw* ) # actually msys + lt_cv_to_host_file_cmd=func_convert_file_msys_to_cygwin + ;; + *-*-cygwin* ) + lt_cv_to_host_file_cmd=func_convert_file_noop + ;; + * ) # otherwise, assume *nix + lt_cv_to_host_file_cmd=func_convert_file_nix_to_cygwin + ;; + esac + ;; + * ) # unhandled hosts (and "normal" native builds) + lt_cv_to_host_file_cmd=func_convert_file_noop + ;; +esac +]) +to_host_file_cmd=$lt_cv_to_host_file_cmd +AC_MSG_RESULT([$lt_cv_to_host_file_cmd]) +_LT_DECL([to_host_file_cmd], [lt_cv_to_host_file_cmd], + [0], [convert $build file names to $host format])dnl + +AC_MSG_CHECKING([how to convert $build file names to toolchain format]) +AC_CACHE_VAL(lt_cv_to_tool_file_cmd, +[#assume ordinary cross tools, or native build. +lt_cv_to_tool_file_cmd=func_convert_file_noop +case $host in + *-*-mingw* ) + case $build in + *-*-mingw* ) # actually msys + lt_cv_to_tool_file_cmd=func_convert_file_msys_to_w32 + ;; + esac + ;; +esac +]) +to_tool_file_cmd=$lt_cv_to_tool_file_cmd +AC_MSG_RESULT([$lt_cv_to_tool_file_cmd]) +_LT_DECL([to_tool_file_cmd], [lt_cv_to_tool_file_cmd], + [0], [convert $build files to toolchain format])dnl +])# _LT_PATH_CONVERSION_FUNCTIONS diff --git a/m4/libxml2.m4 b/m4/libxml2.m4 new file mode 100644 index 0000000..68cd824 --- /dev/null +++ b/m4/libxml2.m4 @@ -0,0 +1,188 @@ +# Configure paths for LIBXML2 +# Mike Hommey 2004-06-19 +# use CPPFLAGS instead of CFLAGS +# Toshio Kuratomi 2001-04-21 +# Adapted from: +# Configure paths for GLIB +# Owen Taylor 97-11-3 + +dnl AM_PATH_XML2([MINIMUM-VERSION, [ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND]]]) +dnl Test for XML, and define XML_CPPFLAGS and XML_LIBS +dnl +AC_DEFUN([AM_PATH_XML2],[ +AC_ARG_WITH(xml-prefix, + [ --with-xml-prefix=PFX Prefix where libxml is installed (optional)], + xml_config_prefix="$withval", xml_config_prefix="") +AC_ARG_WITH(xml-exec-prefix, + [ --with-xml-exec-prefix=PFX Exec prefix where libxml is installed (optional)], + xml_config_exec_prefix="$withval", xml_config_exec_prefix="") +AC_ARG_ENABLE(xmltest, + [ --disable-xmltest Do not try to compile and run a test LIBXML program],, + enable_xmltest=yes) + + if test x$xml_config_exec_prefix != x ; then + xml_config_args="$xml_config_args" + if test x${XML2_CONFIG+set} != xset ; then + XML2_CONFIG=$xml_config_exec_prefix/bin/xml2-config + fi + fi + if test x$xml_config_prefix != x ; then + xml_config_args="$xml_config_args --prefix=$xml_config_prefix" + if test x${XML2_CONFIG+set} != xset ; then + XML2_CONFIG=$xml_config_prefix/bin/xml2-config + fi + fi + + AC_PATH_PROG(XML2_CONFIG, xml2-config, no) + min_xml_version=ifelse([$1], ,2.0.0,[$1]) + AC_MSG_CHECKING(for libxml - version >= $min_xml_version) + no_xml="" + if test "$XML2_CONFIG" = "no" ; then + no_xml=yes + else + XML_CPPFLAGS=`$XML2_CONFIG $xml_config_args --cflags` + XML_LIBS=`$XML2_CONFIG $xml_config_args --libs` + xml_config_major_version=`$XML2_CONFIG $xml_config_args --version | \ + sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\1/'` + xml_config_minor_version=`$XML2_CONFIG $xml_config_args --version | \ + sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\2/'` + xml_config_micro_version=`$XML2_CONFIG $xml_config_args --version | \ + sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\3/'` + if test "x$enable_xmltest" = "xyes" ; then + ac_save_CPPFLAGS="$CPPFLAGS" + ac_save_LIBS="$LIBS" + CPPFLAGS="$CPPFLAGS $XML_CPPFLAGS" + LIBS="$XML_LIBS $LIBS" +dnl +dnl Now check if the installed libxml is sufficiently new. +dnl (Also sanity checks the results of xml2-config to some extent) +dnl + rm -f conf.xmltest + AC_TRY_RUN([ +#include +#include +#include +#include + +int +main() +{ + int xml_major_version, xml_minor_version, xml_micro_version; + int major, minor, micro; + char *tmp_version; + + system("touch conf.xmltest"); + + /* Capture xml2-config output via autoconf/configure variables */ + /* HP/UX 9 (%@#!) writes to sscanf strings */ + tmp_version = (char *)strdup("$min_xml_version"); + if (sscanf(tmp_version, "%d.%d.%d", &major, &minor, µ) != 3) { + printf("%s, bad version string from xml2-config\n", "$min_xml_version"); + exit(1); + } + free(tmp_version); + + /* Capture the version information from the header files */ + tmp_version = (char *)strdup(LIBXML_DOTTED_VERSION); + if (sscanf(tmp_version, "%d.%d.%d", &xml_major_version, &xml_minor_version, &xml_micro_version) != 3) { + printf("%s, bad version string from libxml includes\n", "LIBXML_DOTTED_VERSION"); + exit(1); + } + free(tmp_version); + + /* Compare xml2-config output to the libxml headers */ + if ((xml_major_version != $xml_config_major_version) || + (xml_minor_version != $xml_config_minor_version) || + (xml_micro_version != $xml_config_micro_version)) + { + printf("*** libxml header files (version %d.%d.%d) do not match\n", + xml_major_version, xml_minor_version, xml_micro_version); + printf("*** xml2-config (version %d.%d.%d)\n", + $xml_config_major_version, $xml_config_minor_version, $xml_config_micro_version); + return 1; + } +/* Compare the headers to the library to make sure we match */ + /* Less than ideal -- doesn't provide us with return value feedback, + * only exits if there's a serious mismatch between header and library. + */ + LIBXML_TEST_VERSION; + + /* Test that the library is greater than our minimum version */ + if ((xml_major_version > major) || + ((xml_major_version == major) && (xml_minor_version > minor)) || + ((xml_major_version == major) && (xml_minor_version == minor) && + (xml_micro_version >= micro))) + { + return 0; + } + else + { + printf("\n*** An old version of libxml (%d.%d.%d) was found.\n", + xml_major_version, xml_minor_version, xml_micro_version); + printf("*** You need a version of libxml newer than %d.%d.%d. The latest version of\n", + major, minor, micro); + printf("*** libxml is always available from ftp://ftp.xmlsoft.org.\n"); + printf("***\n"); + printf("*** If you have already installed a sufficiently new version, this error\n"); + printf("*** probably means that the wrong copy of the xml2-config shell script is\n"); + printf("*** being found. The easiest way to fix this is to remove the old version\n"); + printf("*** of LIBXML, but you can also set the XML2_CONFIG environment to point to the\n"); + printf("*** correct copy of xml2-config. (In this case, you will have to\n"); + printf("*** modify your LD_LIBRARY_PATH enviroment variable, or edit /etc/ld.so.conf\n"); + printf("*** so that the correct libraries are found at run-time))\n"); + } + return 1; +} +],, no_xml=yes,[echo $ac_n "cross compiling; assumed OK... $ac_c"]) + CPPFLAGS="$ac_save_CPPFLAGS" + LIBS="$ac_save_LIBS" + fi + fi + + if test "x$no_xml" = x ; then + AC_MSG_RESULT(yes (version $xml_config_major_version.$xml_config_minor_version.$xml_config_micro_version)) + ifelse([$2], , :, [$2]) + else + AC_MSG_RESULT(no) + if test "$XML2_CONFIG" = "no" ; then + echo "*** The xml2-config script installed by LIBXML could not be found" + echo "*** If libxml was installed in PREFIX, make sure PREFIX/bin is in" + echo "*** your path, or set the XML2_CONFIG environment variable to the" + echo "*** full path to xml2-config." + else + if test -f conf.xmltest ; then + : + else + echo "*** Could not run libxml test program, checking why..." + CPPFLAGS="$CPPFLAGS $XML_CPPFLAGS" + LIBS="$LIBS $XML_LIBS" + AC_TRY_LINK([ +#include +#include +], [ LIBXML_TEST_VERSION; return 0;], + [ echo "*** The test program compiled, but did not run. This usually means" + echo "*** that the run-time linker is not finding LIBXML or finding the wrong" + echo "*** version of LIBXML. If it is not finding LIBXML, you'll need to set your" + echo "*** LD_LIBRARY_PATH environment variable, or edit /etc/ld.so.conf to point" + echo "*** to the installed location Also, make sure you have run ldconfig if that" + echo "*** is required on your system" + echo "***" + echo "*** If you have an old version installed, it is best to remove it, although" + echo "*** you may also be able to get things to work by modifying LD_LIBRARY_PATH" ], + [ echo "*** The test program failed to compile or link. See the file config.log for the" + echo "*** exact error that occured. This usually means LIBXML was incorrectly installed" + echo "*** or that you have moved LIBXML since it was installed. In the latter case, you" + echo "*** may want to edit the xml2-config script: $XML2_CONFIG" ]) + CPPFLAGS="$ac_save_CPPFLAGS" + LIBS="$ac_save_LIBS" + fi + fi + + XML_CPPFLAGS="" + XML_LIBS="" + ifelse([$3], , :, [$3]) + fi + AC_SUBST(XML_CPPFLAGS) + AC_SUBST(XML_LIBS) + rm -f conf.xmltest +]) diff --git a/m4/ltoptions.m4 b/m4/ltoptions.m4 new file mode 100644 index 0000000..5d9acd8 --- /dev/null +++ b/m4/ltoptions.m4 @@ -0,0 +1,384 @@ +# Helper functions for option handling. -*- Autoconf -*- +# +# Copyright (C) 2004, 2005, 2007, 2008, 2009 Free Software Foundation, +# Inc. +# Written by Gary V. Vaughan, 2004 +# +# This file is free software; the Free Software Foundation gives +# unlimited permission to copy and/or distribute it, with or without +# modifications, as long as this notice is preserved. + +# serial 7 ltoptions.m4 + +# This is to help aclocal find these macros, as it can't see m4_define. +AC_DEFUN([LTOPTIONS_VERSION], [m4_if([1])]) + + +# _LT_MANGLE_OPTION(MACRO-NAME, OPTION-NAME) +# ------------------------------------------ +m4_define([_LT_MANGLE_OPTION], +[[_LT_OPTION_]m4_bpatsubst($1__$2, [[^a-zA-Z0-9_]], [_])]) + + +# _LT_SET_OPTION(MACRO-NAME, OPTION-NAME) +# --------------------------------------- +# Set option OPTION-NAME for macro MACRO-NAME, and if there is a +# matching handler defined, dispatch to it. Other OPTION-NAMEs are +# saved as a flag. +m4_define([_LT_SET_OPTION], +[m4_define(_LT_MANGLE_OPTION([$1], [$2]))dnl +m4_ifdef(_LT_MANGLE_DEFUN([$1], [$2]), + _LT_MANGLE_DEFUN([$1], [$2]), + [m4_warning([Unknown $1 option `$2'])])[]dnl +]) + + +# _LT_IF_OPTION(MACRO-NAME, OPTION-NAME, IF-SET, [IF-NOT-SET]) +# ------------------------------------------------------------ +# Execute IF-SET if OPTION is set, IF-NOT-SET otherwise. +m4_define([_LT_IF_OPTION], +[m4_ifdef(_LT_MANGLE_OPTION([$1], [$2]), [$3], [$4])]) + + +# _LT_UNLESS_OPTIONS(MACRO-NAME, OPTION-LIST, IF-NOT-SET) +# ------------------------------------------------------- +# Execute IF-NOT-SET unless all options in OPTION-LIST for MACRO-NAME +# are set. +m4_define([_LT_UNLESS_OPTIONS], +[m4_foreach([_LT_Option], m4_split(m4_normalize([$2])), + [m4_ifdef(_LT_MANGLE_OPTION([$1], _LT_Option), + [m4_define([$0_found])])])[]dnl +m4_ifdef([$0_found], [m4_undefine([$0_found])], [$3 +])[]dnl +]) + + +# _LT_SET_OPTIONS(MACRO-NAME, OPTION-LIST) +# ---------------------------------------- +# OPTION-LIST is a space-separated list of Libtool options associated +# with MACRO-NAME. If any OPTION has a matching handler declared with +# LT_OPTION_DEFINE, dispatch to that macro; otherwise complain about +# the unknown option and exit. +m4_defun([_LT_SET_OPTIONS], +[# Set options +m4_foreach([_LT_Option], m4_split(m4_normalize([$2])), + [_LT_SET_OPTION([$1], _LT_Option)]) + +m4_if([$1],[LT_INIT],[ + dnl + dnl Simply set some default values (i.e off) if boolean options were not + dnl specified: + _LT_UNLESS_OPTIONS([LT_INIT], [dlopen], [enable_dlopen=no + ]) + _LT_UNLESS_OPTIONS([LT_INIT], [win32-dll], [enable_win32_dll=no + ]) + dnl + dnl If no reference was made to various pairs of opposing options, then + dnl we run the default mode handler for the pair. For example, if neither + dnl `shared' nor `disable-shared' was passed, we enable building of shared + dnl archives by default: + _LT_UNLESS_OPTIONS([LT_INIT], [shared disable-shared], [_LT_ENABLE_SHARED]) + _LT_UNLESS_OPTIONS([LT_INIT], [static disable-static], [_LT_ENABLE_STATIC]) + _LT_UNLESS_OPTIONS([LT_INIT], [pic-only no-pic], [_LT_WITH_PIC]) + _LT_UNLESS_OPTIONS([LT_INIT], [fast-install disable-fast-install], + [_LT_ENABLE_FAST_INSTALL]) + ]) +])# _LT_SET_OPTIONS + + +## --------------------------------- ## +## Macros to handle LT_INIT options. ## +## --------------------------------- ## + +# _LT_MANGLE_DEFUN(MACRO-NAME, OPTION-NAME) +# ----------------------------------------- +m4_define([_LT_MANGLE_DEFUN], +[[_LT_OPTION_DEFUN_]m4_bpatsubst(m4_toupper([$1__$2]), [[^A-Z0-9_]], [_])]) + + +# LT_OPTION_DEFINE(MACRO-NAME, OPTION-NAME, CODE) +# ----------------------------------------------- +m4_define([LT_OPTION_DEFINE], +[m4_define(_LT_MANGLE_DEFUN([$1], [$2]), [$3])[]dnl +])# LT_OPTION_DEFINE + + +# dlopen +# ------ +LT_OPTION_DEFINE([LT_INIT], [dlopen], [enable_dlopen=yes +]) + +AU_DEFUN([AC_LIBTOOL_DLOPEN], +[_LT_SET_OPTION([LT_INIT], [dlopen]) +AC_DIAGNOSE([obsolete], +[$0: Remove this warning and the call to _LT_SET_OPTION when you +put the `dlopen' option into LT_INIT's first parameter.]) +]) + +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AC_LIBTOOL_DLOPEN], []) + + +# win32-dll +# --------- +# Declare package support for building win32 dll's. +LT_OPTION_DEFINE([LT_INIT], [win32-dll], +[enable_win32_dll=yes + +case $host in +*-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-cegcc*) + AC_CHECK_TOOL(AS, as, false) + AC_CHECK_TOOL(DLLTOOL, dlltool, false) + AC_CHECK_TOOL(OBJDUMP, objdump, false) + ;; +esac + +test -z "$AS" && AS=as +_LT_DECL([], [AS], [1], [Assembler program])dnl + +test -z "$DLLTOOL" && DLLTOOL=dlltool +_LT_DECL([], [DLLTOOL], [1], [DLL creation program])dnl + +test -z "$OBJDUMP" && OBJDUMP=objdump +_LT_DECL([], [OBJDUMP], [1], [Object dumper program])dnl +])# win32-dll + +AU_DEFUN([AC_LIBTOOL_WIN32_DLL], +[AC_REQUIRE([AC_CANONICAL_HOST])dnl +_LT_SET_OPTION([LT_INIT], [win32-dll]) +AC_DIAGNOSE([obsolete], +[$0: Remove this warning and the call to _LT_SET_OPTION when you +put the `win32-dll' option into LT_INIT's first parameter.]) +]) + +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AC_LIBTOOL_WIN32_DLL], []) + + +# _LT_ENABLE_SHARED([DEFAULT]) +# ---------------------------- +# implement the --enable-shared flag, and supports the `shared' and +# `disable-shared' LT_INIT options. +# DEFAULT is either `yes' or `no'. If omitted, it defaults to `yes'. +m4_define([_LT_ENABLE_SHARED], +[m4_define([_LT_ENABLE_SHARED_DEFAULT], [m4_if($1, no, no, yes)])dnl +AC_ARG_ENABLE([shared], + [AS_HELP_STRING([--enable-shared@<:@=PKGS@:>@], + [build shared libraries @<:@default=]_LT_ENABLE_SHARED_DEFAULT[@:>@])], + [p=${PACKAGE-default} + case $enableval in + yes) enable_shared=yes ;; + no) enable_shared=no ;; + *) + enable_shared=no + # Look at the argument we got. We use all the common list separators. + lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR," + for pkg in $enableval; do + IFS="$lt_save_ifs" + if test "X$pkg" = "X$p"; then + enable_shared=yes + fi + done + IFS="$lt_save_ifs" + ;; + esac], + [enable_shared=]_LT_ENABLE_SHARED_DEFAULT) + + _LT_DECL([build_libtool_libs], [enable_shared], [0], + [Whether or not to build shared libraries]) +])# _LT_ENABLE_SHARED + +LT_OPTION_DEFINE([LT_INIT], [shared], [_LT_ENABLE_SHARED([yes])]) +LT_OPTION_DEFINE([LT_INIT], [disable-shared], [_LT_ENABLE_SHARED([no])]) + +# Old names: +AC_DEFUN([AC_ENABLE_SHARED], +[_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[shared]) +]) + +AC_DEFUN([AC_DISABLE_SHARED], +[_LT_SET_OPTION([LT_INIT], [disable-shared]) +]) + +AU_DEFUN([AM_ENABLE_SHARED], [AC_ENABLE_SHARED($@)]) +AU_DEFUN([AM_DISABLE_SHARED], [AC_DISABLE_SHARED($@)]) + +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AM_ENABLE_SHARED], []) +dnl AC_DEFUN([AM_DISABLE_SHARED], []) + + + +# _LT_ENABLE_STATIC([DEFAULT]) +# ---------------------------- +# implement the --enable-static flag, and support the `static' and +# `disable-static' LT_INIT options. +# DEFAULT is either `yes' or `no'. If omitted, it defaults to `yes'. +m4_define([_LT_ENABLE_STATIC], +[m4_define([_LT_ENABLE_STATIC_DEFAULT], [m4_if($1, no, no, yes)])dnl +AC_ARG_ENABLE([static], + [AS_HELP_STRING([--enable-static@<:@=PKGS@:>@], + [build static libraries @<:@default=]_LT_ENABLE_STATIC_DEFAULT[@:>@])], + [p=${PACKAGE-default} + case $enableval in + yes) enable_static=yes ;; + no) enable_static=no ;; + *) + enable_static=no + # Look at the argument we got. We use all the common list separators. + lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR," + for pkg in $enableval; do + IFS="$lt_save_ifs" + if test "X$pkg" = "X$p"; then + enable_static=yes + fi + done + IFS="$lt_save_ifs" + ;; + esac], + [enable_static=]_LT_ENABLE_STATIC_DEFAULT) + + _LT_DECL([build_old_libs], [enable_static], [0], + [Whether or not to build static libraries]) +])# _LT_ENABLE_STATIC + +LT_OPTION_DEFINE([LT_INIT], [static], [_LT_ENABLE_STATIC([yes])]) +LT_OPTION_DEFINE([LT_INIT], [disable-static], [_LT_ENABLE_STATIC([no])]) + +# Old names: +AC_DEFUN([AC_ENABLE_STATIC], +[_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[static]) +]) + +AC_DEFUN([AC_DISABLE_STATIC], +[_LT_SET_OPTION([LT_INIT], [disable-static]) +]) + +AU_DEFUN([AM_ENABLE_STATIC], [AC_ENABLE_STATIC($@)]) +AU_DEFUN([AM_DISABLE_STATIC], [AC_DISABLE_STATIC($@)]) + +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AM_ENABLE_STATIC], []) +dnl AC_DEFUN([AM_DISABLE_STATIC], []) + + + +# _LT_ENABLE_FAST_INSTALL([DEFAULT]) +# ---------------------------------- +# implement the --enable-fast-install flag, and support the `fast-install' +# and `disable-fast-install' LT_INIT options. +# DEFAULT is either `yes' or `no'. If omitted, it defaults to `yes'. +m4_define([_LT_ENABLE_FAST_INSTALL], +[m4_define([_LT_ENABLE_FAST_INSTALL_DEFAULT], [m4_if($1, no, no, yes)])dnl +AC_ARG_ENABLE([fast-install], + [AS_HELP_STRING([--enable-fast-install@<:@=PKGS@:>@], + [optimize for fast installation @<:@default=]_LT_ENABLE_FAST_INSTALL_DEFAULT[@:>@])], + [p=${PACKAGE-default} + case $enableval in + yes) enable_fast_install=yes ;; + no) enable_fast_install=no ;; + *) + enable_fast_install=no + # Look at the argument we got. We use all the common list separators. + lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR," + for pkg in $enableval; do + IFS="$lt_save_ifs" + if test "X$pkg" = "X$p"; then + enable_fast_install=yes + fi + done + IFS="$lt_save_ifs" + ;; + esac], + [enable_fast_install=]_LT_ENABLE_FAST_INSTALL_DEFAULT) + +_LT_DECL([fast_install], [enable_fast_install], [0], + [Whether or not to optimize for fast installation])dnl +])# _LT_ENABLE_FAST_INSTALL + +LT_OPTION_DEFINE([LT_INIT], [fast-install], [_LT_ENABLE_FAST_INSTALL([yes])]) +LT_OPTION_DEFINE([LT_INIT], [disable-fast-install], [_LT_ENABLE_FAST_INSTALL([no])]) + +# Old names: +AU_DEFUN([AC_ENABLE_FAST_INSTALL], +[_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[fast-install]) +AC_DIAGNOSE([obsolete], +[$0: Remove this warning and the call to _LT_SET_OPTION when you put +the `fast-install' option into LT_INIT's first parameter.]) +]) + +AU_DEFUN([AC_DISABLE_FAST_INSTALL], +[_LT_SET_OPTION([LT_INIT], [disable-fast-install]) +AC_DIAGNOSE([obsolete], +[$0: Remove this warning and the call to _LT_SET_OPTION when you put +the `disable-fast-install' option into LT_INIT's first parameter.]) +]) + +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AC_ENABLE_FAST_INSTALL], []) +dnl AC_DEFUN([AM_DISABLE_FAST_INSTALL], []) + + +# _LT_WITH_PIC([MODE]) +# -------------------- +# implement the --with-pic flag, and support the `pic-only' and `no-pic' +# LT_INIT options. +# MODE is either `yes' or `no'. If omitted, it defaults to `both'. +m4_define([_LT_WITH_PIC], +[AC_ARG_WITH([pic], + [AS_HELP_STRING([--with-pic@<:@=PKGS@:>@], + [try to use only PIC/non-PIC objects @<:@default=use both@:>@])], + [lt_p=${PACKAGE-default} + case $withval in + yes|no) pic_mode=$withval ;; + *) + pic_mode=default + # Look at the argument we got. We use all the common list separators. + lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR," + for lt_pkg in $withval; do + IFS="$lt_save_ifs" + if test "X$lt_pkg" = "X$lt_p"; then + pic_mode=yes + fi + done + IFS="$lt_save_ifs" + ;; + esac], + [pic_mode=default]) + +test -z "$pic_mode" && pic_mode=m4_default([$1], [default]) + +_LT_DECL([], [pic_mode], [0], [What type of objects to build])dnl +])# _LT_WITH_PIC + +LT_OPTION_DEFINE([LT_INIT], [pic-only], [_LT_WITH_PIC([yes])]) +LT_OPTION_DEFINE([LT_INIT], [no-pic], [_LT_WITH_PIC([no])]) + +# Old name: +AU_DEFUN([AC_LIBTOOL_PICMODE], +[_LT_SET_OPTION([LT_INIT], [pic-only]) +AC_DIAGNOSE([obsolete], +[$0: Remove this warning and the call to _LT_SET_OPTION when you +put the `pic-only' option into LT_INIT's first parameter.]) +]) + +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AC_LIBTOOL_PICMODE], []) + +## ----------------- ## +## LTDL_INIT Options ## +## ----------------- ## + +m4_define([_LTDL_MODE], []) +LT_OPTION_DEFINE([LTDL_INIT], [nonrecursive], + [m4_define([_LTDL_MODE], [nonrecursive])]) +LT_OPTION_DEFINE([LTDL_INIT], [recursive], + [m4_define([_LTDL_MODE], [recursive])]) +LT_OPTION_DEFINE([LTDL_INIT], [subproject], + [m4_define([_LTDL_MODE], [subproject])]) + +m4_define([_LTDL_TYPE], []) +LT_OPTION_DEFINE([LTDL_INIT], [installable], + [m4_define([_LTDL_TYPE], [installable])]) +LT_OPTION_DEFINE([LTDL_INIT], [convenience], + [m4_define([_LTDL_TYPE], [convenience])]) diff --git a/m4/ltsugar.m4 b/m4/ltsugar.m4 new file mode 100644 index 0000000..9000a05 --- /dev/null +++ b/m4/ltsugar.m4 @@ -0,0 +1,123 @@ +# ltsugar.m4 -- libtool m4 base layer. -*-Autoconf-*- +# +# Copyright (C) 2004, 2005, 2007, 2008 Free Software Foundation, Inc. +# Written by Gary V. Vaughan, 2004 +# +# This file is free software; the Free Software Foundation gives +# unlimited permission to copy and/or distribute it, with or without +# modifications, as long as this notice is preserved. + +# serial 6 ltsugar.m4 + +# This is to help aclocal find these macros, as it can't see m4_define. +AC_DEFUN([LTSUGAR_VERSION], [m4_if([0.1])]) + + +# lt_join(SEP, ARG1, [ARG2...]) +# ----------------------------- +# Produce ARG1SEPARG2...SEPARGn, omitting [] arguments and their +# associated separator. +# Needed until we can rely on m4_join from Autoconf 2.62, since all earlier +# versions in m4sugar had bugs. +m4_define([lt_join], +[m4_if([$#], [1], [], + [$#], [2], [[$2]], + [m4_if([$2], [], [], [[$2]_])$0([$1], m4_shift(m4_shift($@)))])]) +m4_define([_lt_join], +[m4_if([$#$2], [2], [], + [m4_if([$2], [], [], [[$1$2]])$0([$1], m4_shift(m4_shift($@)))])]) + + +# lt_car(LIST) +# lt_cdr(LIST) +# ------------ +# Manipulate m4 lists. +# These macros are necessary as long as will still need to support +# Autoconf-2.59 which quotes differently. +m4_define([lt_car], [[$1]]) +m4_define([lt_cdr], +[m4_if([$#], 0, [m4_fatal([$0: cannot be called without arguments])], + [$#], 1, [], + [m4_dquote(m4_shift($@))])]) +m4_define([lt_unquote], $1) + + +# lt_append(MACRO-NAME, STRING, [SEPARATOR]) +# ------------------------------------------ +# Redefine MACRO-NAME to hold its former content plus `SEPARATOR'`STRING'. +# Note that neither SEPARATOR nor STRING are expanded; they are appended +# to MACRO-NAME as is (leaving the expansion for when MACRO-NAME is invoked). +# No SEPARATOR is output if MACRO-NAME was previously undefined (different +# than defined and empty). +# +# This macro is needed until we can rely on Autoconf 2.62, since earlier +# versions of m4sugar mistakenly expanded SEPARATOR but not STRING. +m4_define([lt_append], +[m4_define([$1], + m4_ifdef([$1], [m4_defn([$1])[$3]])[$2])]) + + + +# lt_combine(SEP, PREFIX-LIST, INFIX, SUFFIX1, [SUFFIX2...]) +# ---------------------------------------------------------- +# Produce a SEP delimited list of all paired combinations of elements of +# PREFIX-LIST with SUFFIX1 through SUFFIXn. Each element of the list +# has the form PREFIXmINFIXSUFFIXn. +# Needed until we can rely on m4_combine added in Autoconf 2.62. +m4_define([lt_combine], +[m4_if(m4_eval([$# > 3]), [1], + [m4_pushdef([_Lt_sep], [m4_define([_Lt_sep], m4_defn([lt_car]))])]]dnl +[[m4_foreach([_Lt_prefix], [$2], + [m4_foreach([_Lt_suffix], + ]m4_dquote(m4_dquote(m4_shift(m4_shift(m4_shift($@)))))[, + [_Lt_sep([$1])[]m4_defn([_Lt_prefix])[$3]m4_defn([_Lt_suffix])])])])]) + + +# lt_if_append_uniq(MACRO-NAME, VARNAME, [SEPARATOR], [UNIQ], [NOT-UNIQ]) +# ----------------------------------------------------------------------- +# Iff MACRO-NAME does not yet contain VARNAME, then append it (delimited +# by SEPARATOR if supplied) and expand UNIQ, else NOT-UNIQ. +m4_define([lt_if_append_uniq], +[m4_ifdef([$1], + [m4_if(m4_index([$3]m4_defn([$1])[$3], [$3$2$3]), [-1], + [lt_append([$1], [$2], [$3])$4], + [$5])], + [lt_append([$1], [$2], [$3])$4])]) + + +# lt_dict_add(DICT, KEY, VALUE) +# ----------------------------- +m4_define([lt_dict_add], +[m4_define([$1($2)], [$3])]) + + +# lt_dict_add_subkey(DICT, KEY, SUBKEY, VALUE) +# -------------------------------------------- +m4_define([lt_dict_add_subkey], +[m4_define([$1($2:$3)], [$4])]) + + +# lt_dict_fetch(DICT, KEY, [SUBKEY]) +# ---------------------------------- +m4_define([lt_dict_fetch], +[m4_ifval([$3], + m4_ifdef([$1($2:$3)], [m4_defn([$1($2:$3)])]), + m4_ifdef([$1($2)], [m4_defn([$1($2)])]))]) + + +# lt_if_dict_fetch(DICT, KEY, [SUBKEY], VALUE, IF-TRUE, [IF-FALSE]) +# ----------------------------------------------------------------- +m4_define([lt_if_dict_fetch], +[m4_if(lt_dict_fetch([$1], [$2], [$3]), [$4], + [$5], + [$6])]) + + +# lt_dict_filter(DICT, [SUBKEY], VALUE, [SEPARATOR], KEY, [...]) +# -------------------------------------------------------------- +m4_define([lt_dict_filter], +[m4_if([$5], [], [], + [lt_join(m4_quote(m4_default([$4], [[, ]])), + lt_unquote(m4_split(m4_normalize(m4_foreach(_Lt_key, lt_car([m4_shiftn(4, $@)]), + [lt_if_dict_fetch([$1], _Lt_key, [$2], [$3], [_Lt_key ])])))))])[]dnl +]) diff --git a/m4/ltversion.m4 b/m4/ltversion.m4 new file mode 100644 index 0000000..07a8602 --- /dev/null +++ b/m4/ltversion.m4 @@ -0,0 +1,23 @@ +# ltversion.m4 -- version numbers -*- Autoconf -*- +# +# Copyright (C) 2004 Free Software Foundation, Inc. +# Written by Scott James Remnant, 2004 +# +# This file is free software; the Free Software Foundation gives +# unlimited permission to copy and/or distribute it, with or without +# modifications, as long as this notice is preserved. + +# @configure_input@ + +# serial 3337 ltversion.m4 +# This file is part of GNU Libtool + +m4_define([LT_PACKAGE_VERSION], [2.4.2]) +m4_define([LT_PACKAGE_REVISION], [1.3337]) + +AC_DEFUN([LTVERSION_VERSION], +[macro_version='2.4.2' +macro_revision='1.3337' +_LT_DECL(, macro_version, 0, [Which release of libtool.m4 was used?]) +_LT_DECL(, macro_revision, 0) +]) diff --git a/m4/lt~obsolete.m4 b/m4/lt~obsolete.m4 new file mode 100644 index 0000000..c573da9 --- /dev/null +++ b/m4/lt~obsolete.m4 @@ -0,0 +1,98 @@ +# lt~obsolete.m4 -- aclocal satisfying obsolete definitions. -*-Autoconf-*- +# +# Copyright (C) 2004, 2005, 2007, 2009 Free Software Foundation, Inc. +# Written by Scott James Remnant, 2004. +# +# This file is free software; the Free Software Foundation gives +# unlimited permission to copy and/or distribute it, with or without +# modifications, as long as this notice is preserved. + +# serial 5 lt~obsolete.m4 + +# These exist entirely to fool aclocal when bootstrapping libtool. +# +# In the past libtool.m4 has provided macros via AC_DEFUN (or AU_DEFUN) +# which have later been changed to m4_define as they aren't part of the +# exported API, or moved to Autoconf or Automake where they belong. +# +# The trouble is, aclocal is a bit thick. It'll see the old AC_DEFUN +# in /usr/share/aclocal/libtool.m4 and remember it, then when it sees us +# using a macro with the same name in our local m4/libtool.m4 it'll +# pull the old libtool.m4 in (it doesn't see our shiny new m4_define +# and doesn't know about Autoconf macros at all.) +# +# So we provide this file, which has a silly filename so it's always +# included after everything else. This provides aclocal with the +# AC_DEFUNs it wants, but when m4 processes it, it doesn't do anything +# because those macros already exist, or will be overwritten later. +# We use AC_DEFUN over AU_DEFUN for compatibility with aclocal-1.6. +# +# Anytime we withdraw an AC_DEFUN or AU_DEFUN, remember to add it here. +# Yes, that means every name once taken will need to remain here until +# we give up compatibility with versions before 1.7, at which point +# we need to keep only those names which we still refer to. + +# This is to help aclocal find these macros, as it can't see m4_define. +AC_DEFUN([LTOBSOLETE_VERSION], [m4_if([1])]) + +m4_ifndef([AC_LIBTOOL_LINKER_OPTION], [AC_DEFUN([AC_LIBTOOL_LINKER_OPTION])]) +m4_ifndef([AC_PROG_EGREP], [AC_DEFUN([AC_PROG_EGREP])]) +m4_ifndef([_LT_AC_PROG_ECHO_BACKSLASH], [AC_DEFUN([_LT_AC_PROG_ECHO_BACKSLASH])]) +m4_ifndef([_LT_AC_SHELL_INIT], [AC_DEFUN([_LT_AC_SHELL_INIT])]) +m4_ifndef([_LT_AC_SYS_LIBPATH_AIX], [AC_DEFUN([_LT_AC_SYS_LIBPATH_AIX])]) +m4_ifndef([_LT_PROG_LTMAIN], [AC_DEFUN([_LT_PROG_LTMAIN])]) +m4_ifndef([_LT_AC_TAGVAR], [AC_DEFUN([_LT_AC_TAGVAR])]) +m4_ifndef([AC_LTDL_ENABLE_INSTALL], [AC_DEFUN([AC_LTDL_ENABLE_INSTALL])]) +m4_ifndef([AC_LTDL_PREOPEN], [AC_DEFUN([AC_LTDL_PREOPEN])]) +m4_ifndef([_LT_AC_SYS_COMPILER], [AC_DEFUN([_LT_AC_SYS_COMPILER])]) +m4_ifndef([_LT_AC_LOCK], [AC_DEFUN([_LT_AC_LOCK])]) +m4_ifndef([AC_LIBTOOL_SYS_OLD_ARCHIVE], [AC_DEFUN([AC_LIBTOOL_SYS_OLD_ARCHIVE])]) +m4_ifndef([_LT_AC_TRY_DLOPEN_SELF], [AC_DEFUN([_LT_AC_TRY_DLOPEN_SELF])]) +m4_ifndef([AC_LIBTOOL_PROG_CC_C_O], [AC_DEFUN([AC_LIBTOOL_PROG_CC_C_O])]) +m4_ifndef([AC_LIBTOOL_SYS_HARD_LINK_LOCKS], [AC_DEFUN([AC_LIBTOOL_SYS_HARD_LINK_LOCKS])]) +m4_ifndef([AC_LIBTOOL_OBJDIR], [AC_DEFUN([AC_LIBTOOL_OBJDIR])]) +m4_ifndef([AC_LTDL_OBJDIR], [AC_DEFUN([AC_LTDL_OBJDIR])]) +m4_ifndef([AC_LIBTOOL_PROG_LD_HARDCODE_LIBPATH], [AC_DEFUN([AC_LIBTOOL_PROG_LD_HARDCODE_LIBPATH])]) +m4_ifndef([AC_LIBTOOL_SYS_LIB_STRIP], [AC_DEFUN([AC_LIBTOOL_SYS_LIB_STRIP])]) +m4_ifndef([AC_PATH_MAGIC], [AC_DEFUN([AC_PATH_MAGIC])]) +m4_ifndef([AC_PROG_LD_GNU], [AC_DEFUN([AC_PROG_LD_GNU])]) +m4_ifndef([AC_PROG_LD_RELOAD_FLAG], [AC_DEFUN([AC_PROG_LD_RELOAD_FLAG])]) +m4_ifndef([AC_DEPLIBS_CHECK_METHOD], [AC_DEFUN([AC_DEPLIBS_CHECK_METHOD])]) +m4_ifndef([AC_LIBTOOL_PROG_COMPILER_NO_RTTI], [AC_DEFUN([AC_LIBTOOL_PROG_COMPILER_NO_RTTI])]) +m4_ifndef([AC_LIBTOOL_SYS_GLOBAL_SYMBOL_PIPE], [AC_DEFUN([AC_LIBTOOL_SYS_GLOBAL_SYMBOL_PIPE])]) +m4_ifndef([AC_LIBTOOL_PROG_COMPILER_PIC], [AC_DEFUN([AC_LIBTOOL_PROG_COMPILER_PIC])]) +m4_ifndef([AC_LIBTOOL_PROG_LD_SHLIBS], [AC_DEFUN([AC_LIBTOOL_PROG_LD_SHLIBS])]) +m4_ifndef([AC_LIBTOOL_POSTDEP_PREDEP], [AC_DEFUN([AC_LIBTOOL_POSTDEP_PREDEP])]) +m4_ifndef([LT_AC_PROG_EGREP], [AC_DEFUN([LT_AC_PROG_EGREP])]) +m4_ifndef([LT_AC_PROG_SED], [AC_DEFUN([LT_AC_PROG_SED])]) +m4_ifndef([_LT_CC_BASENAME], [AC_DEFUN([_LT_CC_BASENAME])]) +m4_ifndef([_LT_COMPILER_BOILERPLATE], [AC_DEFUN([_LT_COMPILER_BOILERPLATE])]) +m4_ifndef([_LT_LINKER_BOILERPLATE], [AC_DEFUN([_LT_LINKER_BOILERPLATE])]) +m4_ifndef([_AC_PROG_LIBTOOL], [AC_DEFUN([_AC_PROG_LIBTOOL])]) +m4_ifndef([AC_LIBTOOL_SETUP], [AC_DEFUN([AC_LIBTOOL_SETUP])]) +m4_ifndef([_LT_AC_CHECK_DLFCN], [AC_DEFUN([_LT_AC_CHECK_DLFCN])]) +m4_ifndef([AC_LIBTOOL_SYS_DYNAMIC_LINKER], [AC_DEFUN([AC_LIBTOOL_SYS_DYNAMIC_LINKER])]) +m4_ifndef([_LT_AC_TAGCONFIG], [AC_DEFUN([_LT_AC_TAGCONFIG])]) +m4_ifndef([AC_DISABLE_FAST_INSTALL], [AC_DEFUN([AC_DISABLE_FAST_INSTALL])]) +m4_ifndef([_LT_AC_LANG_CXX], [AC_DEFUN([_LT_AC_LANG_CXX])]) +m4_ifndef([_LT_AC_LANG_F77], [AC_DEFUN([_LT_AC_LANG_F77])]) +m4_ifndef([_LT_AC_LANG_GCJ], [AC_DEFUN([_LT_AC_LANG_GCJ])]) +m4_ifndef([AC_LIBTOOL_LANG_C_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_C_CONFIG])]) +m4_ifndef([_LT_AC_LANG_C_CONFIG], [AC_DEFUN([_LT_AC_LANG_C_CONFIG])]) +m4_ifndef([AC_LIBTOOL_LANG_CXX_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_CXX_CONFIG])]) +m4_ifndef([_LT_AC_LANG_CXX_CONFIG], [AC_DEFUN([_LT_AC_LANG_CXX_CONFIG])]) +m4_ifndef([AC_LIBTOOL_LANG_F77_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_F77_CONFIG])]) +m4_ifndef([_LT_AC_LANG_F77_CONFIG], [AC_DEFUN([_LT_AC_LANG_F77_CONFIG])]) +m4_ifndef([AC_LIBTOOL_LANG_GCJ_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_GCJ_CONFIG])]) +m4_ifndef([_LT_AC_LANG_GCJ_CONFIG], [AC_DEFUN([_LT_AC_LANG_GCJ_CONFIG])]) +m4_ifndef([AC_LIBTOOL_LANG_RC_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_RC_CONFIG])]) +m4_ifndef([_LT_AC_LANG_RC_CONFIG], [AC_DEFUN([_LT_AC_LANG_RC_CONFIG])]) +m4_ifndef([AC_LIBTOOL_CONFIG], [AC_DEFUN([AC_LIBTOOL_CONFIG])]) +m4_ifndef([_LT_AC_FILE_LTDLL_C], [AC_DEFUN([_LT_AC_FILE_LTDLL_C])]) +m4_ifndef([_LT_REQUIRED_DARWIN_CHECKS], [AC_DEFUN([_LT_REQUIRED_DARWIN_CHECKS])]) +m4_ifndef([_LT_AC_PROG_CXXCPP], [AC_DEFUN([_LT_AC_PROG_CXXCPP])]) +m4_ifndef([_LT_PREPARE_SED_QUOTE_VARS], [AC_DEFUN([_LT_PREPARE_SED_QUOTE_VARS])]) +m4_ifndef([_LT_PROG_ECHO_BACKSLASH], [AC_DEFUN([_LT_PROG_ECHO_BACKSLASH])]) +m4_ifndef([_LT_PROG_F77], [AC_DEFUN([_LT_PROG_F77])]) +m4_ifndef([_LT_PROG_FC], [AC_DEFUN([_LT_PROG_FC])]) +m4_ifndef([_LT_PROG_CXX], [AC_DEFUN([_LT_PROG_CXX])]) diff --git a/missing b/missing new file mode 100755 index 0000000..db98974 --- /dev/null +++ b/missing @@ -0,0 +1,215 @@ +#! /bin/sh +# Common wrapper for a few potentially missing GNU programs. + +scriptversion=2013-10-28.13; # UTC + +# Copyright (C) 1996-2013 Free Software Foundation, Inc. +# Originally written by Fran,cois Pinard , 1996. + +# 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 2, 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 . + +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +if test $# -eq 0; then + echo 1>&2 "Try '$0 --help' for more information" + exit 1 +fi + +case $1 in + + --is-lightweight) + # Used by our autoconf macros to check whether the available missing + # script is modern enough. + exit 0 + ;; + + --run) + # Back-compat with the calling convention used by older automake. + shift + ;; + + -h|--h|--he|--hel|--help) + echo "\ +$0 [OPTION]... PROGRAM [ARGUMENT]... + +Run 'PROGRAM [ARGUMENT]...', returning a proper advice when this fails due +to PROGRAM being missing or too old. + +Options: + -h, --help display this help and exit + -v, --version output version information and exit + +Supported PROGRAM values: + aclocal autoconf autoheader autom4te automake makeinfo + bison yacc flex lex help2man + +Version suffixes to PROGRAM as well as the prefixes 'gnu-', 'gnu', and +'g' are ignored when checking the name. + +Send bug reports to ." + exit $? + ;; + + -v|--v|--ve|--ver|--vers|--versi|--versio|--version) + echo "missing $scriptversion (GNU Automake)" + exit $? + ;; + + -*) + echo 1>&2 "$0: unknown '$1' option" + echo 1>&2 "Try '$0 --help' for more information" + exit 1 + ;; + +esac + +# Run the given program, remember its exit status. +"$@"; st=$? + +# If it succeeded, we are done. +test $st -eq 0 && exit 0 + +# Also exit now if we it failed (or wasn't found), and '--version' was +# passed; such an option is passed most likely to detect whether the +# program is present and works. +case $2 in --version|--help) exit $st;; esac + +# Exit code 63 means version mismatch. This often happens when the user +# tries to use an ancient version of a tool on a file that requires a +# minimum version. +if test $st -eq 63; then + msg="probably too old" +elif test $st -eq 127; then + # Program was missing. + msg="missing on your system" +else + # Program was found and executed, but failed. Give up. + exit $st +fi + +perl_URL=http://www.perl.org/ +flex_URL=http://flex.sourceforge.net/ +gnu_software_URL=http://www.gnu.org/software + +program_details () +{ + case $1 in + aclocal|automake) + echo "The '$1' program is part of the GNU Automake package:" + echo "<$gnu_software_URL/automake>" + echo "It also requires GNU Autoconf, GNU m4 and Perl in order to run:" + echo "<$gnu_software_URL/autoconf>" + echo "<$gnu_software_URL/m4/>" + echo "<$perl_URL>" + ;; + autoconf|autom4te|autoheader) + echo "The '$1' program is part of the GNU Autoconf package:" + echo "<$gnu_software_URL/autoconf/>" + echo "It also requires GNU m4 and Perl in order to run:" + echo "<$gnu_software_URL/m4/>" + echo "<$perl_URL>" + ;; + esac +} + +give_advice () +{ + # Normalize program name to check for. + normalized_program=`echo "$1" | sed ' + s/^gnu-//; t + s/^gnu//; t + s/^g//; t'` + + printf '%s\n' "'$1' is $msg." + + configure_deps="'configure.ac' or m4 files included by 'configure.ac'" + case $normalized_program in + autoconf*) + echo "You should only need it if you modified 'configure.ac'," + echo "or m4 files included by it." + program_details 'autoconf' + ;; + autoheader*) + echo "You should only need it if you modified 'acconfig.h' or" + echo "$configure_deps." + program_details 'autoheader' + ;; + automake*) + echo "You should only need it if you modified 'Makefile.am' or" + echo "$configure_deps." + program_details 'automake' + ;; + aclocal*) + echo "You should only need it if you modified 'acinclude.m4' or" + echo "$configure_deps." + program_details 'aclocal' + ;; + autom4te*) + echo "You might have modified some maintainer files that require" + echo "the 'autom4te' program to be rebuilt." + program_details 'autom4te' + ;; + bison*|yacc*) + echo "You should only need it if you modified a '.y' file." + echo "You may want to install the GNU Bison package:" + echo "<$gnu_software_URL/bison/>" + ;; + lex*|flex*) + echo "You should only need it if you modified a '.l' file." + echo "You may want to install the Fast Lexical Analyzer package:" + echo "<$flex_URL>" + ;; + help2man*) + echo "You should only need it if you modified a dependency" \ + "of a man page." + echo "You may want to install the GNU Help2man package:" + echo "<$gnu_software_URL/help2man/>" + ;; + makeinfo*) + echo "You should only need it if you modified a '.texi' file, or" + echo "any other file indirectly affecting the aspect of the manual." + echo "You might want to install the Texinfo package:" + echo "<$gnu_software_URL/texinfo/>" + echo "The spurious makeinfo call might also be the consequence of" + echo "using a buggy 'make' (AIX, DU, IRIX), in which case you might" + echo "want to install GNU make:" + echo "<$gnu_software_URL/make/>" + ;; + *) + echo "You might have modified some files without having the proper" + echo "tools for further handling them. Check the 'README' file, it" + echo "often tells you about the needed prerequisites for installing" + echo "this package. You may also peek at any GNU archive site, in" + echo "case some other package contains this missing '$1' program." + ;; + esac +} + +give_advice "$1" | sed -e '1s/^/WARNING: /' \ + -e '2,$s/^/ /' >&2 + +# Propagate the correct exit status (expected to be 127 for a program +# not found, 63 for a program that failed due to version mismatch). +exit $st + +# Local variables: +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "scriptversion=" +# time-stamp-format: "%:y-%02m-%02d.%02H" +# time-stamp-time-zone: "UTC" +# time-stamp-end: "; # UTC" +# End: diff --git a/nghttpx.conf.sample b/nghttpx.conf.sample new file mode 100644 index 0000000..97d7954 --- /dev/null +++ b/nghttpx.conf.sample @@ -0,0 +1,29 @@ +# +# Sample configuration file for nghttpx. +# +# * Line staring '#' is treated as comment. +# +# * The option name in the configuration file is the long command-line +# option name with leading '--' stripped (e.g., frontend). Put '=' +# between option name and value. Don't put extra leading or trailing +# spaces. +# +# * The options which do not take argument in the command-line *take* +# argument in the configuration file. Specify 'yes' as argument +# (e.g., http2-proxy=yes). If other string is given, it disables the +# option. +# +# * To specify private key and certificate file, use private-key-file +# and certificate-file. See the examples below. +# +# * conf option cannot be used in the configuration file. It will be +# ignored. +# +# Examples: +# +# frontend=0.0.0.0,3000 +# backend=127.0.0.1,80 +# private-key-file=/path/to/server.key +# certificate-file=/path/to/server.crt +# http2-proxy=no +# workers=1 diff --git a/proxy.pac.sample b/proxy.pac.sample new file mode 100644 index 0000000..9283920 --- /dev/null +++ b/proxy.pac.sample @@ -0,0 +1,6 @@ +function FindProxyForURL(url, host) { + // For SPDY proxy + return "HTTPS localhost:3000"; + // For conventional HTTP proxy + // return "PROXY localhost:3000"; +} diff --git a/python/Makefile.am b/python/Makefile.am new file mode 100644 index 0000000..911888f --- /dev/null +++ b/python/Makefile.am @@ -0,0 +1,48 @@ +# nghttp2 - HTTP/2 C Library + +# Copyright (c) 2013 Tatsuhiro Tsujikawa + +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: + +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. + +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +# This will avoid that setup.py gets deleted before it is executed in +# clean-local in parallel build. +.NOTPARALLEL: + +EXTRA_DIST = cnghttp2.pxd nghttp2.pyx + +if ENABLE_PYTHON_BINDINGS + +all-local: nghttp2.c + $(PYTHON) setup.py build + +install-exec-local: + $(PYTHON) setup.py install --prefix=$(DESTDIR)$(prefix) + +uninstall-local: + rm -rf $(DESTDIR)$(libdir)/python*/site-packages/*nghttp2* + +clean-local: + $(PYTHON) setup.py clean --all + -rm -f $(builddir)/nghttp2.c + +.pyx.c: + $(CYTHON) -o $@ $< + +endif # ENABLE_PYTHON_BINDINGS diff --git a/python/Makefile.in b/python/Makefile.in new file mode 100644 index 0000000..ea9c308 --- /dev/null +++ b/python/Makefile.in @@ -0,0 +1,541 @@ +# Makefile.in generated by automake 1.14.1 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2013 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +# nghttp2 - HTTP/2 C Library + +# Copyright (c) 2013 Tatsuhiro Tsujikawa + +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: + +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. + +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +VPATH = @srcdir@ +am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)' +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +target_triplet = @target@ +subdir = python +DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \ + $(srcdir)/setup.py.in +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/ax_boost_asio.m4 \ + $(top_srcdir)/m4/ax_boost_base.m4 \ + $(top_srcdir)/m4/ax_boost_system.m4 \ + $(top_srcdir)/m4/ax_boost_thread.m4 \ + $(top_srcdir)/m4/ax_check_compile_flag.m4 \ + $(top_srcdir)/m4/ax_cxx_compile_stdcxx_11.m4 \ + $(top_srcdir)/m4/ax_have_epoll.m4 \ + $(top_srcdir)/m4/ax_python_devel.m4 \ + $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/libxml2.m4 \ + $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ + $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = setup.py +CONFIG_CLEAN_VPATH_FILES = +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +SOURCES = +DIST_SOURCES = +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +APPLDFLAGS = @APPLDFLAGS@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +BOOST_ASIO_LIB = @BOOST_ASIO_LIB@ +BOOST_CPPFLAGS = @BOOST_CPPFLAGS@ +BOOST_LDFLAGS = @BOOST_LDFLAGS@ +BOOST_SYSTEM_LIB = @BOOST_SYSTEM_LIB@ +BOOST_THREAD_LIB = @BOOST_THREAD_LIB@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CUNIT_CFLAGS = @CUNIT_CFLAGS@ +CUNIT_LIBS = @CUNIT_LIBS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +CYTHON = @CYTHON@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DLLTOOL = @DLLTOOL@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +GREP = @GREP@ +HAVE_CXX11 = @HAVE_CXX11@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +JANSSON_CFLAGS = @JANSSON_CFLAGS@ +JANSSON_LIBS = @JANSSON_LIBS@ +JEMALLOC_LIBS = @JEMALLOC_LIBS@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBEVENT_OPENSSL_CFLAGS = @LIBEVENT_OPENSSL_CFLAGS@ +LIBEVENT_OPENSSL_LIBS = @LIBEVENT_OPENSSL_LIBS@ +LIBEV_CFLAGS = @LIBEV_CFLAGS@ +LIBEV_LIBS = @LIBEV_LIBS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBSPDYLAY_CFLAGS = @LIBSPDYLAY_CFLAGS@ +LIBSPDYLAY_LIBS = @LIBSPDYLAY_LIBS@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +LT_AGE = @LT_AGE@ +LT_CURRENT = @LT_CURRENT@ +LT_REVISION = @LT_REVISION@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MKDIR_P = @MKDIR_P@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OPENSSL_CFLAGS = @OPENSSL_CFLAGS@ +OPENSSL_LIBS = @OPENSSL_LIBS@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PACKAGE_VERSION_NUM = @PACKAGE_VERSION_NUM@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PKG_CONFIG = @PKG_CONFIG@ +PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ +PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ +PYTHON = @PYTHON@ +PYTHON_CPPFLAGS = @PYTHON_CPPFLAGS@ +PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@ +PYTHON_EXTRA_LDFLAGS = @PYTHON_EXTRA_LDFLAGS@ +PYTHON_EXTRA_LIBS = @PYTHON_EXTRA_LIBS@ +PYTHON_LDFLAGS = @PYTHON_LDFLAGS@ +PYTHON_PLATFORM = @PYTHON_PLATFORM@ +PYTHON_PREFIX = @PYTHON_PREFIX@ +PYTHON_SITE_PKG = @PYTHON_SITE_PKG@ +PYTHON_VERSION = @PYTHON_VERSION@ +RANLIB = @RANLIB@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +TESTLDADD = @TESTLDADD@ +VERSION = @VERSION@ +WARNCFLAGS = @WARNCFLAGS@ +XML2_CONFIG = @XML2_CONFIG@ +XML_CPPFLAGS = @XML_CPPFLAGS@ +XML_LIBS = @XML_LIBS@ +ZLIB_CFLAGS = @ZLIB_CFLAGS@ +ZLIB_LIBS = @ZLIB_LIBS@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +pkgpyexecdir = @pkgpyexecdir@ +pkgpythondir = @pkgpythondir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +pyexecdir = @pyexecdir@ +pythondir = @pythondir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target = @target@ +target_alias = @target_alias@ +target_cpu = @target_cpu@ +target_os = @target_os@ +target_vendor = @target_vendor@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +EXTRA_DIST = cnghttp2.pxd nghttp2.pyx +all: all-am + +.SUFFIXES: +.SUFFIXES: .c .pyx +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu python/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu python/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): +setup.py: $(top_builddir)/config.status $(srcdir)/setup.py.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs +tags TAGS: + +ctags CTAGS: + +cscope cscopelist: + + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +@ENABLE_PYTHON_BINDINGS_FALSE@all-local: +all-am: Makefile all-local +installdirs: +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +@ENABLE_PYTHON_BINDINGS_FALSE@clean-local: +@ENABLE_PYTHON_BINDINGS_FALSE@install-exec-local: +@ENABLE_PYTHON_BINDINGS_FALSE@uninstall-local: +clean: clean-am + +clean-am: clean-generic clean-libtool clean-local mostlyclean-am + +distclean: distclean-am + -rm -f Makefile +distclean-am: clean-am distclean-generic + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: install-exec-local + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-generic mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-local + +.MAKE: install-am install-strip + +.PHONY: all all-am all-local check check-am clean clean-generic \ + clean-libtool clean-local cscopelist-am ctags-am distclean \ + distclean-generic distclean-libtool distdir dvi dvi-am html \ + html-am info info-am install install-am install-data \ + install-data-am install-dvi install-dvi-am install-exec \ + install-exec-am install-exec-local install-html \ + install-html-am install-info install-info-am install-man \ + install-pdf install-pdf-am install-ps install-ps-am \ + install-strip installcheck installcheck-am installdirs \ + maintainer-clean maintainer-clean-generic mostlyclean \ + mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ + tags-am uninstall uninstall-am uninstall-local + + +# This will avoid that setup.py gets deleted before it is executed in +# clean-local in parallel build. +.NOTPARALLEL: + +@ENABLE_PYTHON_BINDINGS_TRUE@all-local: nghttp2.c +@ENABLE_PYTHON_BINDINGS_TRUE@ $(PYTHON) setup.py build + +@ENABLE_PYTHON_BINDINGS_TRUE@install-exec-local: +@ENABLE_PYTHON_BINDINGS_TRUE@ $(PYTHON) setup.py install --prefix=$(DESTDIR)$(prefix) + +@ENABLE_PYTHON_BINDINGS_TRUE@uninstall-local: +@ENABLE_PYTHON_BINDINGS_TRUE@ rm -rf $(DESTDIR)$(libdir)/python*/site-packages/*nghttp2* + +@ENABLE_PYTHON_BINDINGS_TRUE@clean-local: +@ENABLE_PYTHON_BINDINGS_TRUE@ $(PYTHON) setup.py clean --all +@ENABLE_PYTHON_BINDINGS_TRUE@ -rm -f $(builddir)/nghttp2.c + +@ENABLE_PYTHON_BINDINGS_TRUE@.pyx.c: +@ENABLE_PYTHON_BINDINGS_TRUE@ $(CYTHON) -o $@ $< + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/python/cnghttp2.pxd b/python/cnghttp2.pxd new file mode 100644 index 0000000..2718304 --- /dev/null +++ b/python/cnghttp2.pxd @@ -0,0 +1,345 @@ +# nghttp2 - HTTP/2 C Library + +# Copyright (c) 2013 Tatsuhiro Tsujikawa + +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: + +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. + +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +from libc.stdint cimport uint8_t, uint16_t, uint32_t, int32_t + +cdef extern from 'nghttp2/nghttp2.h': + + const char NGHTTP2_PROTO_VERSION_ID[] + const char NGHTTP2_CLIENT_CONNECTION_PREFACE[] + const size_t NGHTTP2_INITIAL_WINDOW_SIZE + const size_t NGHTTP2_DEFAULT_HEADER_TABLE_SIZE + + ctypedef struct nghttp2_session: + pass + + ctypedef enum nghttp2_error: + NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE + NGHTTP2_ERR_DEFERRED + + ctypedef enum nghttp2_flag: + NGHTTP2_FLAG_NONE + NGHTTP2_FLAG_END_STREAM + NGHTTP2_FLAG_ACK + + ctypedef enum nghttp2_error_code: + NGHTTP2_NO_ERROR + NGHTTP2_PROTOCOL_ERROR + NGHTTP2_INTERNAL_ERROR + NGHTTP2_SETTINGS_TIMEOUT + + ctypedef enum nghttp2_frame_type: + NGHTTP2_DATA + NGHTTP2_HEADERS + NGHTTP2_RST_STREAM + NGHTTP2_SETTINGS + NGHTTP2_PUSH_PROMISE + NGHTTP2_GOAWAY + + ctypedef enum nghttp2_nv_flag: + NGHTTP2_NV_FLAG_NONE + NGHTTP2_NV_FLAG_NO_INDEX + + ctypedef struct nghttp2_nv: + uint8_t *name + uint8_t *value + uint16_t namelen + uint16_t valuelen + uint8_t flags + + ctypedef enum nghttp2_settings_id: + SETTINGS_HEADER_TABLE_SIZE + NGHTTP2_SETTINGS_HEADER_TABLE_SIZE + NGHTTP2_SETTINGS_ENABLE_PUSH + NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS + NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE + + ctypedef struct nghttp2_settings_entry: + int32_t settings_id + uint32_t value + + ctypedef struct nghttp2_frame_hd: + size_t length + int32_t stream_id + uint8_t type + uint8_t flags + + ctypedef struct nghttp2_data: + nghttp2_frame_hd hd + size_t padlen + + ctypedef enum nghttp2_headers_category: + NGHTTP2_HCAT_REQUEST + NGHTTP2_HCAT_RESPONSE + NGHTTP2_HCAT_PUSH_RESPONSE + NGHTTP2_HCAT_HEADERS + + ctypedef struct nghttp2_headers: + nghttp2_frame_hd hd + size_t padlen + nghttp2_nv *nva + size_t nvlen + nghttp2_headers_category cat + int32_t pri + + ctypedef struct nghttp2_rst_stream: + nghttp2_frame_hd hd + uint32_t error_code + + + ctypedef struct nghttp2_push_promise: + nghttp2_frame_hd hd + nghttp2_nv *nva + size_t nvlen + int32_t promised_stream_id + + ctypedef struct nghttp2_goaway: + nghttp2_frame_hd hd + int32_t last_stream_id + uint32_t error_code + uint8_t *opaque_data + size_t opaque_data_len + + ctypedef union nghttp2_frame: + nghttp2_frame_hd hd + nghttp2_data data + nghttp2_headers headers + nghttp2_rst_stream rst_stream + nghttp2_push_promise push_promise + nghttp2_goaway goaway + + ctypedef ssize_t (*nghttp2_send_callback)\ + (nghttp2_session *session, const uint8_t *data, size_t length, + int flags, void *user_data) + + ctypedef int (*nghttp2_on_frame_recv_callback)\ + (nghttp2_session *session, const nghttp2_frame *frame, void *user_data) + + ctypedef int (*nghttp2_on_data_chunk_recv_callback)\ + (nghttp2_session *session, uint8_t flags, int32_t stream_id, + const uint8_t *data, size_t length, void *user_data) + + ctypedef int (*nghttp2_before_frame_send_callback)\ + (nghttp2_session *session, const nghttp2_frame *frame, void *user_data) + + ctypedef int (*nghttp2_on_stream_close_callback)\ + (nghttp2_session *session, int32_t stream_id, + uint32_t error_code, void *user_data) + + ctypedef int (*nghttp2_on_begin_headers_callback)\ + (nghttp2_session *session, const nghttp2_frame *frame, void *user_data) + + ctypedef int (*nghttp2_on_header_callback)\ + (nghttp2_session *session, + const nghttp2_frame *frame, + const uint8_t *name, size_t namelen, + const uint8_t *value, size_t valuelen, + uint8_t flags, + void *user_data) + + ctypedef int (*nghttp2_on_frame_send_callback)\ + (nghttp2_session *session, const nghttp2_frame *frame, void *user_data) + + ctypedef int (*nghttp2_on_frame_not_send_callback)\ + (nghttp2_session *session, const nghttp2_frame *frame, + int lib_error_code, void *user_data) + + ctypedef struct nghttp2_session_callbacks: + pass + + int nghttp2_session_callbacks_new( + nghttp2_session_callbacks **callbacks_ptr) + + void nghttp2_session_callbacks_del(nghttp2_session_callbacks *callbacks) + + void nghttp2_session_callbacks_set_send_callback( + nghttp2_session_callbacks *cbs, nghttp2_send_callback send_callback) + + void nghttp2_session_callbacks_set_on_frame_recv_callback( + nghttp2_session_callbacks *cbs, + nghttp2_on_frame_recv_callback on_frame_recv_callback) + + void nghttp2_session_callbacks_set_on_data_chunk_recv_callback( + nghttp2_session_callbacks *cbs, + nghttp2_on_data_chunk_recv_callback on_data_chunk_recv_callback) + + void nghttp2_session_callbacks_set_before_frame_send_callback( + nghttp2_session_callbacks *cbs, + nghttp2_before_frame_send_callback before_frame_send_callback) + + void nghttp2_session_callbacks_set_on_frame_send_callback( + nghttp2_session_callbacks *cbs, + nghttp2_on_frame_send_callback on_frame_send_callback) + + void nghttp2_session_callbacks_set_on_frame_not_send_callback( + nghttp2_session_callbacks *cbs, + nghttp2_on_frame_not_send_callback on_frame_not_send_callback) + + void nghttp2_session_callbacks_set_on_stream_close_callback( + nghttp2_session_callbacks *cbs, + nghttp2_on_stream_close_callback on_stream_close_callback) + + void nghttp2_session_callbacks_set_on_begin_headers_callback( + nghttp2_session_callbacks *cbs, + nghttp2_on_begin_headers_callback on_begin_headers_callback) + + void nghttp2_session_callbacks_set_on_header_callback( + nghttp2_session_callbacks *cbs, + nghttp2_on_header_callback on_header_callback) + + int nghttp2_session_client_new(nghttp2_session **session_ptr, + const nghttp2_session_callbacks *callbacks, + void *user_data) + + int nghttp2_session_server_new(nghttp2_session **session_ptr, + const nghttp2_session_callbacks *callbacks, + void *user_data) + + void nghttp2_session_del(nghttp2_session *session) + + + ssize_t nghttp2_session_mem_recv(nghttp2_session *session, + const uint8_t *data, size_t datalen) + + ssize_t nghttp2_session_mem_send(nghttp2_session *session, + const uint8_t **data_ptr) + + int nghttp2_session_send(nghttp2_session *session) + + int nghttp2_session_want_read(nghttp2_session *session) + + int nghttp2_session_want_write(nghttp2_session *session) + + ctypedef union nghttp2_data_source: + int fd + void *ptr + + ctypedef enum nghttp2_data_flag: + NGHTTP2_DATA_FLAG_NONE + NGHTTP2_DATA_FLAG_EOF + + ctypedef ssize_t (*nghttp2_data_source_read_callback)\ + (nghttp2_session *session, int32_t stream_id, + uint8_t *buf, size_t length, uint32_t *data_flags, + nghttp2_data_source *source, void *user_data) + + ctypedef struct nghttp2_data_provider: + nghttp2_data_source source + nghttp2_data_source_read_callback read_callback + + ctypedef struct nghttp2_priority_spec: + int32_t stream_id + int32_t weight + uint8_t exclusive + + int nghttp2_submit_request(nghttp2_session *session, const nghttp2_priority_spec *pri_spec, + const nghttp2_nv *nva, size_t nvlen, + const nghttp2_data_provider *data_prd, + void *stream_user_data) + + int nghttp2_submit_response(nghttp2_session *session, + int32_t stream_id, + const nghttp2_nv *nva, size_t nvlen, + const nghttp2_data_provider *data_prd) + + int nghttp2_submit_push_promise(nghttp2_session *session, uint8_t flags, + int32_t stream_id, + const nghttp2_nv *nva, size_t nvlen, + void *stream_user_data) + + int nghttp2_submit_settings(nghttp2_session *session, uint8_t flags, + const nghttp2_settings_entry *iv, size_t niv) + + int nghttp2_submit_rst_stream(nghttp2_session *session, uint8_t flags, + int32_t stream_id, + uint32_t error_code) + + void* nghttp2_session_get_stream_user_data(nghttp2_session *session, + uint32_t stream_id) + + int nghttp2_session_set_stream_user_data(nghttp2_session *session, + uint32_t stream_id, + void *stream_user_data) + + int nghttp2_session_terminate_session(nghttp2_session *session, + uint32_t error_code) + + int nghttp2_session_resume_data(nghttp2_session *session, + int32_t stream_id) + + const char* nghttp2_strerror(int lib_error_code) + + int nghttp2_hd_deflate_new(nghttp2_hd_deflater **deflater_ptr, + size_t deflate_hd_table_bufsize_max) + + void nghttp2_hd_deflate_del(nghttp2_hd_deflater *deflater) + + int nghttp2_hd_deflate_change_table_size(nghttp2_hd_deflater *deflater, + size_t hd_table_bufsize_max) + + ssize_t nghttp2_hd_deflate_hd(nghttp2_hd_deflater *deflater, + uint8_t *buf, size_t buflen, + const nghttp2_nv *nva, size_t nvlen) + + size_t nghttp2_hd_deflate_bound(nghttp2_hd_deflater *deflater, + const nghttp2_nv *nva, size_t nvlen) + + int nghttp2_hd_inflate_new(nghttp2_hd_inflater **inflater_ptr) + + void nghttp2_hd_inflate_del(nghttp2_hd_inflater *inflater) + + int nghttp2_hd_inflate_change_table_size(nghttp2_hd_inflater *inflater, + size_t hd_table_bufsize_max) + + ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_inflater *inflater, + nghttp2_nv *nv_out, int *inflate_flags, + uint8_t *input, size_t inlen, int in_final) + + int nghttp2_hd_inflate_end_headers(nghttp2_hd_inflater *inflater) + +cdef extern from 'nghttp2_hd.h': + + # This is macro + int NGHTTP2_HD_ENTRY_OVERHEAD + + ctypedef enum nghttp2_hd_inflate_flag: + NGHTTP2_HD_INFLATE_EMIT + NGHTTP2_HD_INFLATE_FINAL + + ctypedef struct nghttp2_hd_entry: + nghttp2_nv nv + uint8_t flags + + ctypedef struct nghttp2_hd_ringbuf: + size_t len + + ctypedef struct nghttp2_hd_context: + nghttp2_hd_ringbuf hd_table + + ctypedef struct nghttp2_hd_deflater: + nghttp2_hd_context ctx + + ctypedef struct nghttp2_hd_inflater: + nghttp2_hd_context ctx + + nghttp2_hd_entry* nghttp2_hd_table_get(nghttp2_hd_context *context, + size_t index) diff --git a/python/nghttp2.pyx b/python/nghttp2.pyx new file mode 100644 index 0000000..c6b9b66 --- /dev/null +++ b/python/nghttp2.pyx @@ -0,0 +1,1591 @@ +# nghttp2 - HTTP/2 C Library + +# Copyright (c) 2013 Tatsuhiro Tsujikawa + +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: + +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. + +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +cimport cnghttp2 + +from libc.stdlib cimport malloc, free +from libc.string cimport memcpy, memset +from libc.stdint cimport uint8_t, uint16_t, uint32_t, int32_t +import logging + + +DEFAULT_HEADER_TABLE_SIZE = cnghttp2.NGHTTP2_DEFAULT_HEADER_TABLE_SIZE +DEFLATE_MAX_HEADER_TABLE_SIZE = 4096 + +HD_ENTRY_OVERHEAD = cnghttp2.NGHTTP2_HD_ENTRY_OVERHEAD + +class HDTableEntry: + + def __init__(self, name, namelen, value, valuelen): + self.name = name + self.namelen = namelen + self.value = value + self.valuelen = valuelen + + def space(self): + return self.namelen + self.valuelen + HD_ENTRY_OVERHEAD + +cdef _get_hd_table(cnghttp2.nghttp2_hd_context *ctx): + cdef int length = ctx.hd_table.len + cdef cnghttp2.nghttp2_hd_entry *entry + res = [] + for i in range(length): + entry = cnghttp2.nghttp2_hd_table_get(ctx, i) + k = _get_pybytes(entry.nv.name, entry.nv.namelen) + v = _get_pybytes(entry.nv.value, entry.nv.valuelen) + res.append(HDTableEntry(k, entry.nv.namelen, + v, entry.nv.valuelen)) + return res + +cdef _get_pybytes(uint8_t *b, uint16_t blen): + return b[:blen] + +cdef class HDDeflater: + '''Performs header compression. The constructor takes + |hd_table_bufsize_max| parameter, which limits the usage of header + table in the given amount of bytes. This is necessary because the + header compressor and decompressor share the same amount of + header table and the decompressor decides that number. The + compressor may not want to use all header table size because of + limited memory availability. In that case, the + |hd_table_bufsize_max| can be used to cap the upper limit of table + size whatever the header table size is chosen by the decompressor. + The default value of |hd_table_bufsize_max| is 4096 bytes. + + The following example shows how to compress request header sets: + + import binascii, nghttp2 + + deflater = nghttp2.HDDeflater() + res = deflater.deflate([(b'foo', b'bar'), + (b'baz', b'buz')]) + print(binascii.b2a_hex(res)) + + ''' + + cdef cnghttp2.nghttp2_hd_deflater *_deflater + + def __cinit__(self, hd_table_bufsize_max = DEFLATE_MAX_HEADER_TABLE_SIZE): + rv = cnghttp2.nghttp2_hd_deflate_new(&self._deflater, + hd_table_bufsize_max) + if rv != 0: + raise Exception(_strerror(rv)) + + def __dealloc__(self): + cnghttp2.nghttp2_hd_deflate_del(self._deflater) + + def deflate(self, headers): + '''Compresses the |headers|. The |headers| must be sequence of tuple + of name/value pair, which are sequence of bytes (not unicode + string). + + This function returns the encoded header block in byte string. + An exception will be raised on error. + + ''' + cdef cnghttp2.nghttp2_nv *nva = \ + malloc(sizeof(cnghttp2.nghttp2_nv)*\ + len(headers)) + cdef cnghttp2.nghttp2_nv *nvap = nva + + for k, v in headers: + nvap[0].name = k + nvap[0].namelen = len(k) + nvap[0].value = v + nvap[0].valuelen = len(v) + nvap[0].flags = cnghttp2.NGHTTP2_NV_FLAG_NONE + nvap += 1 + + cdef size_t outcap = 0 + cdef ssize_t rv + cdef uint8_t *out + cdef size_t outlen + + outlen = cnghttp2.nghttp2_hd_deflate_bound(self._deflater, + nva, len(headers)) + + out = malloc(outlen) + + rv = cnghttp2.nghttp2_hd_deflate_hd(self._deflater, out, outlen, + nva, len(headers)) + free(nva) + + if rv < 0: + free(out) + + raise Exception(_strerror(rv)) + + cdef bytes res + + try: + res = out[:rv] + finally: + free(out) + + return res + + def change_table_size(self, hd_table_bufsize_max): + '''Changes header table size to |hd_table_bufsize_max| byte. + + An exception will be raised on error. + + ''' + cdef int rv + rv = cnghttp2.nghttp2_hd_deflate_change_table_size(self._deflater, + hd_table_bufsize_max) + if rv != 0: + raise Exception(_strerror(rv)) + + def get_hd_table(self): + '''Returns copy of current dynamic header table.''' + return _get_hd_table(&self._deflater.ctx) + +cdef class HDInflater: + '''Performs header decompression. + + The following example shows how to compress request header sets: + + data = b'0082c5ad82bd0f000362617a0362757a' + inflater = nghttp2.HDInflater() + hdrs = inflater.inflate(data) + print(hdrs) + + ''' + + cdef cnghttp2.nghttp2_hd_inflater *_inflater + + def __cinit__(self): + rv = cnghttp2.nghttp2_hd_inflate_new(&self._inflater) + if rv != 0: + raise Exception(_strerror(rv)) + + def __dealloc__(self): + cnghttp2.nghttp2_hd_inflate_del(self._inflater) + + def inflate(self, data): + '''Decompresses the compressed header block |data|. The |data| must be + byte string (not unicode string). + + ''' + cdef cnghttp2.nghttp2_nv nv + cdef int inflate_flags + cdef ssize_t rv + cdef uint8_t *buf = data + cdef size_t buflen = len(data) + res = [] + while True: + inflate_flags = 0 + rv = cnghttp2.nghttp2_hd_inflate_hd(self._inflater, &nv, + &inflate_flags, + buf, buflen, 1) + if rv < 0: + raise Exception(_strerror(rv)) + buf += rv + buflen -= rv + if inflate_flags & cnghttp2.NGHTTP2_HD_INFLATE_EMIT: + # may throw + res.append((nv.name[:nv.namelen], nv.value[:nv.valuelen])) + if inflate_flags & cnghttp2.NGHTTP2_HD_INFLATE_FINAL: + break + + cnghttp2.nghttp2_hd_inflate_end_headers(self._inflater) + return res + + def change_table_size(self, hd_table_bufsize_max): + '''Changes header table size to |hd_table_bufsize_max| byte. + + An exception will be raised on error. + + ''' + cdef int rv + rv = cnghttp2.nghttp2_hd_inflate_change_table_size(self._inflater, + hd_table_bufsize_max) + if rv != 0: + raise Exception(_strerror(rv)) + + def get_hd_table(self): + '''Returns copy of current dynamic header table.''' + return _get_hd_table(&self._inflater.ctx) + +cdef _strerror(int liberror_code): + return cnghttp2.nghttp2_strerror(liberror_code).decode('utf-8') + +def print_hd_table(hdtable): + '''Convenient function to print |hdtable| to the standard output. This + function does not work if header name/value cannot be decoded using + UTF-8 encoding. + + s=N means the entry occupies N bytes in header table. + + ''' + idx = 0 + for entry in hdtable: + idx += 1 + print('[{}] (s={}) {}: {}'\ + .format(idx, entry.space(), + entry.name.decode('utf-8'), + entry.value.decode('utf-8'))) + +try: + import socket + import io + import asyncio + import traceback + import sys + import email.utils + import datetime + import time + from urllib.parse import urlparse +except ImportError: + asyncio = None + +# body generator flags +DATA_OK = 0 +DATA_EOF = 1 +DATA_DEFERRED = 2 + +class _ByteIOWrapper: + + def __init__(self, b): + self.b = b + + def generate(self, n): + data = self.b.read1(n) + if not data: + return None, DATA_EOF + return data, DATA_OK + +def wrap_body(body): + if body is None: + return body + elif isinstance(body, str): + return _ByteIOWrapper(io.BytesIO(body.encode('utf-8'))).generate + elif isinstance(body, bytes): + return _ByteIOWrapper(io.BytesIO(body)).generate + elif isinstance(body, io.IOBase): + return _ByteIOWrapper(body).generate + else: + # assume that callable in the form f(n) returning tuple byte + # string and flag. + return body + +cdef _get_stream_user_data(cnghttp2.nghttp2_session *session, + int32_t stream_id): + cdef void *stream_user_data + + stream_user_data = cnghttp2.nghttp2_session_get_stream_user_data\ + (session, stream_id) + if stream_user_data == NULL: + return None + + return stream_user_data + +cdef size_t _make_nva(cnghttp2.nghttp2_nv **nva_ptr, headers): + cdef cnghttp2.nghttp2_nv *nva + cdef size_t nvlen + + nvlen = len(headers) + nva = malloc(sizeof(cnghttp2.nghttp2_nv) * nvlen) + for i, (k, v) in enumerate(headers): + nva[i].name = k + nva[i].namelen = len(k) + nva[i].value = v + nva[i].valuelen = len(v) + nva[i].flags = cnghttp2.NGHTTP2_NV_FLAG_NONE + + nva_ptr[0] = nva + + return nvlen + +cdef int server_on_header(cnghttp2.nghttp2_session *session, + const cnghttp2.nghttp2_frame *frame, + const uint8_t *name, size_t namelen, + const uint8_t *value, size_t valuelen, + uint8_t flags, + void *user_data): + cdef http2 = <_HTTP2SessionCoreBase>user_data + logging.debug('server_on_header, type:%s, stream_id:%s', frame.hd.type, frame.hd.stream_id) + + handler = _get_stream_user_data(session, frame.hd.stream_id) + return on_header(name, namelen, value, valuelen, flags, handler) + +cdef int client_on_header(cnghttp2.nghttp2_session *session, + const cnghttp2.nghttp2_frame *frame, + const uint8_t *name, size_t namelen, + const uint8_t *value, size_t valuelen, + uint8_t flags, + void *user_data): + cdef http2 = <_HTTP2SessionCoreBase>user_data + logging.debug('client_on_header, type:%s, stream_id:%s', frame.hd.type, frame.hd.stream_id) + + if frame.hd.type == cnghttp2.NGHTTP2_HEADERS: + handler = _get_stream_user_data(session, frame.hd.stream_id) + elif frame.hd.type == cnghttp2.NGHTTP2_PUSH_PROMISE: + handler = _get_stream_user_data(session, frame.push_promise.promised_stream_id) + + return on_header(name, namelen, value, valuelen, flags, handler) + + +cdef int on_header(const uint8_t *name, size_t namelen, + const uint8_t *value, size_t valuelen, + uint8_t flags, + object handler): + if not handler: + return 0 + + key = name[:namelen] + values = value[:valuelen].split(b'\x00') + if key == b':scheme': + handler.scheme = values[0] + elif key == b':method': + handler.method = values[0] + elif key == b':authority' or key == b'host': + handler.host = values[0] + elif key == b':path': + handler.path = values[0] + elif key == b':status': + handler.status = values[0] + + if key == b'cookie': + handler.cookies.extend(values) + else: + for v in values: + handler.headers.append((key, v)) + + return 0 + +cdef int server_on_begin_request_headers(cnghttp2.nghttp2_session *session, + const cnghttp2.nghttp2_frame *frame, + void *user_data): + cdef http2 = <_HTTP2SessionCore>user_data + + handler = http2._make_handler(frame.hd.stream_id) + cnghttp2.nghttp2_session_set_stream_user_data(session, frame.hd.stream_id, + handler) + + return 0 + +cdef int server_on_begin_headers(cnghttp2.nghttp2_session *session, + const cnghttp2.nghttp2_frame *frame, + void *user_data): + if frame.hd.type == cnghttp2.NGHTTP2_HEADERS: + if frame.headers.cat == cnghttp2.NGHTTP2_HCAT_REQUEST: + return server_on_begin_request_headers(session, frame, user_data) + + return 0 + +cdef int server_on_frame_recv(cnghttp2.nghttp2_session *session, + const cnghttp2.nghttp2_frame *frame, + void *user_data): + cdef http2 = <_HTTP2SessionCore>user_data + logging.debug('server_on_frame_recv, type:%s, stream_id:%s', frame.hd.type, frame.hd.stream_id) + + if frame.hd.type == cnghttp2.NGHTTP2_DATA: + if frame.hd.flags & cnghttp2.NGHTTP2_FLAG_END_STREAM: + handler = _get_stream_user_data(session, frame.hd.stream_id) + if not handler: + return 0 + try: + handler.on_request_done() + except: + sys.stderr.write(traceback.format_exc()) + return http2._rst_stream(frame.hd.stream_id) + elif frame.hd.type == cnghttp2.NGHTTP2_HEADERS: + if frame.headers.cat == cnghttp2.NGHTTP2_HCAT_REQUEST: + handler = _get_stream_user_data(session, frame.hd.stream_id) + if not handler: + return 0 + if handler.cookies: + handler.headers.append((b'cookie', + b'; '.join(handler.cookies))) + handler.cookies = None + try: + handler.on_headers() + if frame.hd.flags & cnghttp2.NGHTTP2_FLAG_END_STREAM: + handler.on_request_done() + except: + sys.stderr.write(traceback.format_exc()) + return http2._rst_stream(frame.hd.stream_id) + elif frame.hd.type == cnghttp2.NGHTTP2_SETTINGS: + if (frame.hd.flags & cnghttp2.NGHTTP2_FLAG_ACK): + http2._stop_settings_timer() + + return 0 + +cdef int on_data_chunk_recv(cnghttp2.nghttp2_session *session, + uint8_t flags, + int32_t stream_id, const uint8_t *data, + size_t length, void *user_data): + cdef http2 = <_HTTP2SessionCoreBase>user_data + + handler = _get_stream_user_data(session, stream_id) + if not handler: + return 0 + + try: + handler.on_data(data[:length]) + except: + sys.stderr.write(traceback.format_exc()) + return http2._rst_stream(stream_id) + + return 0 + +cdef int server_on_frame_send(cnghttp2.nghttp2_session *session, + const cnghttp2.nghttp2_frame *frame, + void *user_data): + cdef http2 = <_HTTP2SessionCore>user_data + logging.debug('server_on_frame_send, type:%s, stream_id:%s', frame.hd.type, frame.hd.stream_id) + + if frame.hd.type == cnghttp2.NGHTTP2_PUSH_PROMISE: + # For PUSH_PROMISE, send push response immediately + handler = _get_stream_user_data\ + (session, frame.push_promise.promised_stream_id) + if not handler: + return 0 + + http2.send_response(handler) + elif frame.hd.type == cnghttp2.NGHTTP2_SETTINGS: + if (frame.hd.flags & cnghttp2.NGHTTP2_FLAG_ACK) != 0: + return 0 + http2._start_settings_timer() + +cdef int server_on_frame_not_send(cnghttp2.nghttp2_session *session, + const cnghttp2.nghttp2_frame *frame, + int lib_error_code, + void *user_data): + cdef http2 = <_HTTP2SessionCore>user_data + logging.debug('server_on_frame_not_send, type:%s, stream_id:%s', frame.hd.type, frame.hd.stream_id) + + if frame.hd.type == cnghttp2.NGHTTP2_PUSH_PROMISE: + # We have to remove handler here. Without this, it is not + # removed until session is terminated. + handler = _get_stream_user_data\ + (session, frame.push_promise.promised_stream_id) + if not handler: + return 0 + http2._remove_handler(handler) + +cdef int on_stream_close(cnghttp2.nghttp2_session *session, + int32_t stream_id, + uint32_t error_code, + void *user_data): + cdef http2 = <_HTTP2SessionCoreBase>user_data + logging.debug('on_stream_close, stream_id:%s', stream_id) + + handler = _get_stream_user_data(session, stream_id) + if not handler: + return 0 + + try: + handler.on_close(error_code) + except: + sys.stderr.write(traceback.format_exc()) + + http2._remove_handler(handler) + + return 0 + +cdef ssize_t data_source_read(cnghttp2.nghttp2_session *session, + int32_t stream_id, + uint8_t *buf, size_t length, + uint32_t *data_flags, + cnghttp2.nghttp2_data_source *source, + void *user_data): + cdef http2 = <_HTTP2SessionCoreBase>user_data + generator = source.ptr + + http2.enter_callback() + try: + data, flag = generator(length) + except: + sys.stderr.write(traceback.format_exc()) + return cnghttp2.NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; + finally: + http2.leave_callback() + + if flag == DATA_DEFERRED: + return cnghttp2.NGHTTP2_ERR_DEFERRED + + if data: + nread = len(data) + memcpy(buf, data, nread) + else: + nread = 0 + + if flag == DATA_EOF: + data_flags[0] = cnghttp2.NGHTTP2_DATA_FLAG_EOF + elif flag != DATA_OK: + return cnghttp2.NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE + + return nread + +cdef int client_on_begin_headers(cnghttp2.nghttp2_session *session, + const cnghttp2.nghttp2_frame *frame, + void *user_data): + cdef http2 = <_HTTP2ClientSessionCore>user_data + + if frame.hd.type == cnghttp2.NGHTTP2_PUSH_PROMISE: + # Generate a temporary handler until the headers are all received + push_handler = BaseResponseHandler() + http2._add_handler(push_handler, frame.push_promise.promised_stream_id) + cnghttp2.nghttp2_session_set_stream_user_data(session, frame.push_promise.promised_stream_id, + push_handler) + + return 0 + +cdef int client_on_frame_recv(cnghttp2.nghttp2_session *session, + const cnghttp2.nghttp2_frame *frame, + void *user_data): + cdef http2 = <_HTTP2ClientSessionCore>user_data + logging.debug('client_on_frame_recv, type:%s, stream_id:%s', frame.hd.type, frame.hd.stream_id) + + if frame.hd.type == cnghttp2.NGHTTP2_DATA: + if frame.hd.flags & cnghttp2.NGHTTP2_FLAG_END_STREAM: + handler = _get_stream_user_data(session, frame.hd.stream_id) + if not handler: + return 0 + try: + handler.on_response_done() + except: + sys.stderr.write(traceback.format_exc()) + return http2._rst_stream(frame.hd.stream_id) + elif frame.hd.type == cnghttp2.NGHTTP2_HEADERS: + if frame.headers.cat == cnghttp2.NGHTTP2_HCAT_RESPONSE or frame.headers.cat == cnghttp2.NGHTTP2_HCAT_PUSH_RESPONSE: + handler = _get_stream_user_data(session, frame.hd.stream_id) + + if not handler: + return 0 + # TODO handle 1xx non-final response + if handler.cookies: + handler.headers.append((b'cookie', + b'; '.join(handler.cookies))) + handler.cookies = None + try: + handler.on_headers() + if frame.hd.flags & cnghttp2.NGHTTP2_FLAG_END_STREAM: + handler.on_response_done() + except: + sys.stderr.write(traceback.format_exc()) + return http2._rst_stream(frame.hd.stream_id) + elif frame.hd.type == cnghttp2.NGHTTP2_SETTINGS: + if (frame.hd.flags & cnghttp2.NGHTTP2_FLAG_ACK): + http2._stop_settings_timer() + elif frame.hd.type == cnghttp2.NGHTTP2_PUSH_PROMISE: + handler = _get_stream_user_data(session, frame.hd.stream_id) + if not handler: + return 0 + # Get the temporary push_handler which now should have all of the header data + push_handler = _get_stream_user_data(session, frame.push_promise.promised_stream_id) + if not push_handler: + return 0 + # Remove the temporary handler + http2._remove_handler(push_handler) + cnghttp2.nghttp2_session_set_stream_user_data(session, frame.push_promise.promised_stream_id, + NULL) + + try: + handler.on_push_promise(push_handler) + except: + sys.stderr.write(traceback.format_exc()) + return http2._rst_stream(frame.hd.stream_id) + + return 0 + +cdef int client_on_frame_send(cnghttp2.nghttp2_session *session, + const cnghttp2.nghttp2_frame *frame, + void *user_data): + cdef http2 = <_HTTP2ClientSessionCore>user_data + logging.debug('client_on_frame_send, type:%s, stream_id:%s', frame.hd.type, frame.hd.stream_id) + + if frame.hd.type == cnghttp2.NGHTTP2_SETTINGS: + if (frame.hd.flags & cnghttp2.NGHTTP2_FLAG_ACK) != 0: + return 0 + http2._start_settings_timer() + +cdef class _HTTP2SessionCoreBase: + cdef cnghttp2.nghttp2_session *session + cdef transport + cdef handler_class + cdef handlers + cdef settings_timer + cdef inside_callback + + def __cinit__(self, transport, handler_class=None): + self.session = NULL + self.transport = transport + self.handler_class = handler_class + self.handlers = set() + self.settings_timer = None + self.inside_callback = False + + def __dealloc__(self): + cnghttp2.nghttp2_session_del(self.session) + + def data_received(self, data): + cdef ssize_t rv + + rv = cnghttp2.nghttp2_session_mem_recv(self.session, data, len(data)) + if rv < 0: + raise Exception('nghttp2_session_mem_recv failed: {}'.format\ + (_strerror(rv))) + self.send_data() + + OUTBUF_MAX = 65535 + SETTINGS_TIMEOUT = 5.0 + + def send_data(self): + cdef ssize_t outbuflen + cdef const uint8_t *outbuf + + while True: + if self.transport.get_write_buffer_size() > self.OUTBUF_MAX: + break + outbuflen = cnghttp2.nghttp2_session_mem_send(self.session, &outbuf) + if outbuflen == 0: + break + if outbuflen < 0: + raise Exception('nghttp2_session_mem_send faild: {}'.format\ + (_strerror(outbuflen))) + self.transport.write(outbuf[:outbuflen]) + + if self.transport.get_write_buffer_size() == 0 and \ + cnghttp2.nghttp2_session_want_read(self.session) == 0 and \ + cnghttp2.nghttp2_session_want_write(self.session) == 0: + self.transport.close() + + def resume(self, stream_id): + cnghttp2.nghttp2_session_resume_data(self.session, stream_id) + if not self.inside_callback: + self.send_data() + + def enter_callback(self): + self.inside_callback = True + + def leave_callback(self): + self.inside_callback = False + + def _make_handler(self, stream_id): + logging.debug('_make_handler, stream_id:%s', stream_id) + handler = self.handler_class(self, stream_id) + self.handlers.add(handler) + return handler + + def _remove_handler(self, handler): + logging.debug('_remove_handler, stream_id:%s', handler.stream_id) + self.handlers.remove(handler) + + def _add_handler(self, handler, stream_id): + logging.debug('_add_handler, stream_id:%s', stream_id) + handler.stream_id = stream_id + handler.http2 = self + handler.remote_address = self._get_remote_address() + self.handlers.add(handler) + + def _rst_stream(self, stream_id, + error_code=cnghttp2.NGHTTP2_INTERNAL_ERROR): + cdef int rv + + rv = cnghttp2.nghttp2_submit_rst_stream\ + (self.session, cnghttp2.NGHTTP2_FLAG_NONE, + stream_id, error_code) + + return rv + + def _get_remote_address(self): + return self.transport.get_extra_info('peername') + + def _start_settings_timer(self): + loop = asyncio.get_event_loop() + self.settings_timer = loop.call_later(self.SETTINGS_TIMEOUT, + self._settings_timeout) + + def _stop_settings_timer(self): + if self.settings_timer: + self.settings_timer.cancel() + self.settings_timer = None + + def _settings_timeout(self): + cdef int rv + + logging.debug('_settings_timeout') + + self.settings_timer = None + + rv = cnghttp2.nghttp2_session_terminate_session\ + (self.session, cnghttp2.NGHTTP2_SETTINGS_TIMEOUT) + try: + self.send_data() + except Exception as err: + sys.stderr.write(traceback.format_exc()) + self.transport.close() + return + + def _log_request(self, handler): + now = datetime.datetime.now() + tv = time.mktime(now.timetuple()) + datestr = email.utils.formatdate(timeval=tv, localtime=False, + usegmt=True) + try: + method = handler.method.decode('utf-8') + except: + method = handler.method + try: + path = handler.path.decode('utf-8') + except: + path = handler.path + logging.info('%s - - [%s] "%s %s HTTP/2" %s - %s', handler.remote_address[0], + datestr, method, path, handler.status, + 'P' if handler.pushed else '-') + + def close(self): + rv = cnghttp2.nghttp2_session_terminate_session\ + (self.session, cnghttp2.NGHTTP2_NO_ERROR) + try: + self.send_data() + except Exception as err: + sys.stderr.write(traceback.format_exc()) + self.transport.close() + return + +cdef class _HTTP2SessionCore(_HTTP2SessionCoreBase): + def __cinit__(self, *args, **kwargs): + cdef cnghttp2.nghttp2_session_callbacks *callbacks + cdef cnghttp2.nghttp2_settings_entry iv[2] + cdef int rv + + super(_HTTP2SessionCore, self).__init__(*args, **kwargs) + + rv = cnghttp2.nghttp2_session_callbacks_new(&callbacks) + + if rv != 0: + raise Exception('nghttp2_session_callbacks_new failed: {}'.format\ + (_strerror(rv))) + + cnghttp2.nghttp2_session_callbacks_set_on_header_callback( + callbacks, server_on_header) + cnghttp2.nghttp2_session_callbacks_set_on_begin_headers_callback( + callbacks, server_on_begin_headers) + cnghttp2.nghttp2_session_callbacks_set_on_frame_recv_callback( + callbacks, server_on_frame_recv) + cnghttp2.nghttp2_session_callbacks_set_on_stream_close_callback( + callbacks, on_stream_close) + cnghttp2.nghttp2_session_callbacks_set_on_frame_send_callback( + callbacks, server_on_frame_send) + cnghttp2.nghttp2_session_callbacks_set_on_frame_not_send_callback( + callbacks, server_on_frame_not_send) + cnghttp2.nghttp2_session_callbacks_set_on_data_chunk_recv_callback( + callbacks, on_data_chunk_recv) + + rv = cnghttp2.nghttp2_session_server_new(&self.session, callbacks, + self) + + cnghttp2.nghttp2_session_callbacks_del(callbacks) + + if rv != 0: + raise Exception('nghttp2_session_server_new failed: {}'.format\ + (_strerror(rv))) + + iv[0].settings_id = cnghttp2.NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS + iv[0].value = 100 + iv[1].settings_id = cnghttp2.NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE + iv[1].value = cnghttp2.NGHTTP2_INITIAL_WINDOW_SIZE + + rv = cnghttp2.nghttp2_submit_settings(self.session, + cnghttp2.NGHTTP2_FLAG_NONE, + iv, sizeof(iv) / sizeof(iv[0])) + + if rv != 0: + raise Exception('nghttp2_submit_settings failed: {}'.format\ + (_strerror(rv))) + + def send_response(self, handler): + cdef cnghttp2.nghttp2_data_provider prd + cdef cnghttp2.nghttp2_data_provider *prd_ptr + cdef cnghttp2.nghttp2_nv *nva + cdef size_t nvlen + cdef int rv + + logging.debug('send_response, stream_id:%s', handler.stream_id) + + nva = NULL + nvlen = _make_nva(&nva, handler.response_headers) + + if handler.response_body: + prd.source.ptr = handler.response_body + prd.read_callback = data_source_read + prd_ptr = &prd + else: + prd_ptr = NULL + + rv = cnghttp2.nghttp2_submit_response(self.session, handler.stream_id, + nva, nvlen, prd_ptr) + + free(nva) + + if rv != 0: + # TODO Ignore return value + self._rst_stream(handler.stream_id) + raise Exception('nghttp2_submit_response failed: {}'.format\ + (_strerror(rv))) + + self._log_request(handler) + + def push(self, handler, promised_handler): + cdef cnghttp2.nghttp2_nv *nva + cdef size_t nvlen + cdef int32_t promised_stream_id + + self.handlers.add(promised_handler) + + nva = NULL + nvlen = _make_nva(&nva, promised_handler.headers) + + promised_stream_id = cnghttp2.nghttp2_submit_push_promise\ + (self.session, + cnghttp2.NGHTTP2_FLAG_NONE, + handler.stream_id, + nva, nvlen, + promised_handler) + if promised_stream_id < 0: + raise Exception('nghttp2_submit_push_promise failed: {}'.format\ + (_strerror(promised_stream_id))) + + promised_handler.stream_id = promised_stream_id + + logging.debug('push, stream_id:%s', promised_stream_id) + + return promised_handler + +cdef class _HTTP2ClientSessionCore(_HTTP2SessionCoreBase): + def __cinit__(self, *args, **kwargs): + cdef cnghttp2.nghttp2_session_callbacks *callbacks + cdef cnghttp2.nghttp2_settings_entry iv[2] + cdef int rv + + super(_HTTP2ClientSessionCore, self).__init__(*args, **kwargs) + + rv = cnghttp2.nghttp2_session_callbacks_new(&callbacks) + + if rv != 0: + raise Exception('nghttp2_session_callbacks_new failed: {}'.format\ + (_strerror(rv))) + + cnghttp2.nghttp2_session_callbacks_set_on_header_callback( + callbacks, client_on_header) + cnghttp2.nghttp2_session_callbacks_set_on_begin_headers_callback( + callbacks, client_on_begin_headers) + cnghttp2.nghttp2_session_callbacks_set_on_frame_recv_callback( + callbacks, client_on_frame_recv) + cnghttp2.nghttp2_session_callbacks_set_on_stream_close_callback( + callbacks, on_stream_close) + cnghttp2.nghttp2_session_callbacks_set_on_frame_send_callback( + callbacks, client_on_frame_send) + cnghttp2.nghttp2_session_callbacks_set_on_data_chunk_recv_callback( + callbacks, on_data_chunk_recv) + + rv = cnghttp2.nghttp2_session_client_new(&self.session, callbacks, + self) + + cnghttp2.nghttp2_session_callbacks_del(callbacks) + + if rv != 0: + raise Exception('nghttp2_session_client_new failed: {}'.format\ + (_strerror(rv))) + + iv[0].settings_id = cnghttp2.NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS + iv[0].value = 100 + iv[1].settings_id = cnghttp2.NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE + iv[1].value = cnghttp2.NGHTTP2_INITIAL_WINDOW_SIZE + + rv = cnghttp2.nghttp2_submit_settings(self.session, + cnghttp2.NGHTTP2_FLAG_NONE, + iv, sizeof(iv) / sizeof(iv[0])) + + if rv != 0: + raise Exception('nghttp2_submit_settings failed: {}'.format\ + (_strerror(rv))) + + def send_request(self, method, scheme, host, path, headers, body, handler): + cdef cnghttp2.nghttp2_data_provider prd + cdef cnghttp2.nghttp2_data_provider *prd_ptr + cdef cnghttp2.nghttp2_priority_spec *pri_ptr + cdef cnghttp2.nghttp2_nv *nva + cdef size_t nvlen + cdef int32_t stream_id + + body = wrap_body(body) + + custom_headers = _encode_headers(headers) + headers = [ + (b':method', method.encode('utf-8')), + (b':scheme', scheme.encode('utf-8')), + (b':authority', host.encode('utf-8')), + (b':path', path.encode('utf-8')) + ] + headers.extend(custom_headers) + + nva = NULL + nvlen = _make_nva(&nva, headers) + + if body: + prd.source.ptr = body + prd.read_callback = data_source_read + prd_ptr = &prd + else: + prd_ptr = NULL + + # TODO: Enable priorities + pri_ptr = NULL + + stream_id = cnghttp2.nghttp2_submit_request\ + (self.session, pri_ptr, + nva, nvlen, prd_ptr, + handler) + free(nva) + + if stream_id < 0: + raise Exception('nghttp2_submit_request failed: {}'.format\ + (_strerror(stream_id))) + + logging.debug('request, stream_id:%s', stream_id) + + self._add_handler(handler, stream_id) + cnghttp2.nghttp2_session_set_stream_user_data(self.session, stream_id, + handler) + + return handler + + def push(self, push_promise, handler): + if handler: + # push_promise accepted, fill in the handler with the stored + # headers from the push_promise + handler.status = push_promise.status + handler.scheme = push_promise.scheme + handler.method = push_promise.method + handler.host = push_promise.host + handler.path = push_promise.path + handler.cookies = push_promise.cookies + handler.stream_id = push_promise.stream_id + handler.http2 = self + handler.pushed = True + + self._add_handler(handler, handler.stream_id) + + cnghttp2.nghttp2_session_set_stream_user_data(self.session, handler.stream_id, + handler) + else: + # push_promise rejected, reset the stream + self._rst_stream(push_promise.stream_id, + error_code=cnghttp2.NGHTTP2_NO_ERROR) + +if asyncio: + + class BaseRequestHandler: + + """HTTP/2 request (stream) handler base class. + + The class is used to handle the HTTP/2 stream. By default, it does + not nothing. It must be subclassed to handle each event callback + method. + + The first callback method invoked is on_headers(). It is called + when HEADERS frame, which includes request header fields, is + arrived. + + If request has request body, on_data(data) is invoked for each + chunk of received data. + + When whole request is received, on_request_done() is invoked. + + When stream is closed, on_close(error_code) is called. + + The application can send response using send_response() method. It + can be used in on_headers(), on_data() or on_request_done(). + + The application can push resource using push() method. It must be + used before send_response() call. + + The following instance variables are available: + + client_address + Contains a tuple of the form (host, port) referring to the client's + address. + + stream_id + Stream ID of this stream + + scheme + Scheme of the request URI. This is a value of :scheme header field. + + method + Method of this stream. This is a value of :method header field. + + host + This is a value of :authority or host header field. + + path + This is a value of :path header field. + + headers + Request header fields + + """ + + def __init__(self, http2, stream_id): + self.headers = [] + self.cookies = [] + # Stream ID. For promised stream, it is initially -1. + self.stream_id = stream_id + self.http2 = http2 + # address of the client + self.remote_address = self.http2._get_remote_address() + # :scheme header field in request + self.scheme = None + # :method header field in request + self.method = None + # :authority or host header field in request + self.host = None + # :path header field in request + self.path = None + # HTTP status + self.status = None + # True if this is a handler for pushed resource + self.pushed = False + + @property + def client_address(self): + return self.remote_address + + def on_headers(self): + + '''Called when request HEADERS is arrived. + + ''' + pass + + def on_data(self, data): + + '''Called when a chunk of request body is arrived. This method + will be called multiple times until all data are received. + + ''' + pass + + def on_request_done(self): + + '''Called when whole request was received + + ''' + pass + + def on_close(self, error_code): + + '''Called when stream is about to close. + + ''' + pass + + def send_response(self, status=200, headers=None, body=None): + + '''Send response. The status is HTTP status code. The headers is + additional response headers. The :status header field is + appended by the library. The body is the response body. It + could be None if response body is empty. Or it must be + instance of either str, bytes, io.IOBase or callable, + called body generator, which takes one parameter, + size. The body generator generates response body. It can + pause generation of response so that it can wait for slow + backend data generation. When invoked, it should return + tuple, byte string and flag. The flag is either DATA_OK, + DATA_EOF and DATA_DEFERRED. For non-empty byte string and + it is not the last chunk of response, DATA_OK is returned + as flag. If this is the last chunk of the response (byte + string is possibly None), DATA_EOF must be returned as + flag. If there is no data available right now, but + additional data are anticipated, return tuple (None, + DATA_DEFERRD). When data arrived, call resume() and + restart response body transmission. + + Only the body generator can pause response body + generation; instance of io.IOBase must not block. + + If instance of str is specified as body, it is encoded + using UTF-8. + + The headers is a list of tuple of the form (name, + value). The name and value can be either unicode string or + byte string. + + On error, exception will be thrown. + + ''' + if self.status is not None: + raise Exception('response has already been sent') + + if not status: + raise Exception('status must not be empty') + + body = wrap_body(body) + + self._set_response_prop(status, headers, body) + self.http2.send_response(self) + + def push(self, path, method='GET', request_headers=None, + status=200, headers=None, body=None): + + '''Push a resource. The path is a path portion of request URI + for this + resource. The method is a method to access this + resource. The request_headers is additional request + headers to access this resource. The :scheme, :method, + :authority and :path are appended by the library. The + :scheme and :authority are inherited from the request (not + request_headers parameter). + + The status is HTTP status code. The headers is additional + response headers. The :status header field is appended by + the library. The body is the response body. It has the + same semantics of body parameter of send_response(). + + The headers and request_headers are a list of tuple of the + form (name, value). The name and value can be either + unicode string or byte string. + + On error, exception will be thrown. + + ''' + if not status: + raise Exception('status must not be empty') + + if not method: + raise Exception('method must not be empty') + + if not path: + raise Exception('path must not be empty') + + body = wrap_body(body) + + promised_handler = self.http2._make_handler(-1) + promised_handler.pushed = True + promised_handler.scheme = self.scheme + promised_handler.method = method.encode('utf-8') + promised_handler.host = self.host + promised_handler.path = path.encode('utf-8') + promised_handler._set_response_prop(status, headers, body) + + headers = [ + (b':method', promised_handler.method), + (b':scheme', promised_handler.scheme), + (b':authority', promised_handler.host), + (b':path', promised_handler.path) + ] + headers.extend(_encode_headers(request_headers)) + + promised_handler.headers = headers + + return self.http2.push(self, promised_handler) + + def _set_response_prop(self, status, headers, body): + self.status = status + + if headers is None: + headers = [] + + self.response_headers = [(b':status', str(status).encode('utf-8'))] + self.response_headers.extend(_encode_headers(headers)) + + self.response_body = body + + def resume(self): + self.http2.resume(self.stream_id) + + def _encode_headers(headers): + if not headers: + return [] + return [(k if isinstance(k, bytes) else k.encode('utf-8'), + v if isinstance(v, bytes) else v.encode('utf-8')) \ + for k, v in headers] + + class _HTTP2Session(asyncio.Protocol): + + def __init__(self, RequestHandlerClass): + asyncio.Protocol.__init__(self) + self.RequestHandlerClass = RequestHandlerClass + self.http2 = None + + def connection_made(self, transport): + address = transport.get_extra_info('peername') + logging.info('connection_made, address:%s, port:%s', address[0], address[1]) + + self.transport = transport + sock = self.transport.get_extra_info('socket') + sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) + ssl_ctx = self.transport.get_extra_info('sslcontext') + if ssl_ctx: + protocol = sock.selected_npn_protocol() + logging.info('npn, protocol:%s', protocol) + if protocol.encode('utf-8') != \ + cnghttp2.NGHTTP2_PROTO_VERSION_ID: + self.transport.abort() + return + try: + self.http2 = _HTTP2SessionCore\ + (self.transport, + self.RequestHandlerClass) + except Exception as err: + sys.stderr.write(traceback.format_exc()) + self.transport.abort() + return + + + def connection_lost(self, exc): + logging.info('connection_lost') + if self.http2: + self.http2 = None + + def data_received(self, data): + try: + self.http2.data_received(data) + except Exception as err: + sys.stderr.write(traceback.format_exc()) + self.transport.close() + return + + def resume_writing(self): + try: + self.http2.send_data() + except Exception as err: + sys.stderr.write(traceback.format_exc()) + self.transport.close() + return + + class HTTP2Server: + + '''HTTP/2 server. + + This class builds on top of the asyncio event loop. On + construction, RequestHandlerClass must be given, which must be a + subclass of BaseRequestHandler class. + + ''' + def __init__(self, address, RequestHandlerClass, ssl=None): + + '''address is a tuple of the listening address and port (e.g., + ('127.0.0.1', 8080)). RequestHandlerClass must be a subclass + of BaseRequestHandler class to handle a HTTP/2 stream. The + ssl can be ssl.SSLContext instance. If it is not None, the + resulting server is SSL/TLS capable. + + ''' + def session_factory(): + return _HTTP2Session(RequestHandlerClass) + + self.loop = asyncio.get_event_loop() + + if ssl: + ssl.set_npn_protocols([cnghttp2.NGHTTP2_PROTO_VERSION_ID\ + .decode('utf-8')]) + + coro = self.loop.create_server(session_factory, + host=address[0], port=address[1], + ssl=ssl) + self.server = self.loop.run_until_complete(coro) + logging.info('listen, address:%s, port:%s', address[0], address[1]) + + def serve_forever(self): + try: + self.loop.run_forever() + finally: + self.server.close() + self.loop.close() + + + + class BaseResponseHandler: + + """HTTP/2 response (stream) handler base class. + + The class is used to handle the HTTP/2 stream. By default, it does + not nothing. It must be subclassed to handle each event callback + method. + + The first callback method invoked is on_headers(). It is called + when HEADERS frame, which includes response header fields, is + arrived. + + If response has a body, on_data(data) is invoked for each + chunk of received data. + + When whole response is received, on_response_done() is invoked. + + When stream is closed, on_close(error_code) is called. + + The application can send follow up requests using HTTP2Client.send_request() method. + + The application can handle push resource using on_push_promise() method. + + The following instance variables are available: + + server_address + Contains a tuple of the form (host, port) referring to the server's + address. + + stream_id + Stream ID of this stream + + scheme + Scheme of the request URI. This is a value of :scheme header field. + + method + Method of this stream. This is a value of :method header field. + + host + This is a value of :authority or host header field. + + path + This is a value of :path header field. + + headers + Response header fields. There is a special exception. If this + object is passed to push_promise(), this instance variable contains + pushed request header fields. + + """ + + def __init__(self, http2=None, stream_id=-1): + self.headers = [] + self.cookies = [] + # Stream ID. For promised stream, it is initially -1. + self.stream_id = stream_id + self.http2 = http2 + # address of the server + self.remote_address = None + # :scheme header field in request + self.scheme = None + # :method header field in request + self.method = None + # :authority or host header field in request + self.host = None + # :path header field in request + self.path = None + # HTTP status + self.status = None + # True if this is a handler for pushed resource + self.pushed = False + + @property + def server_address(self): + return self.remote_address + + def on_headers(self): + + '''Called when response HEADERS is arrived. + + ''' + pass + + def on_data(self, data): + + '''Called when a chunk of response body is arrived. This method + will be called multiple times until all data are received. + + ''' + pass + + def on_response_done(self): + + '''Called when whole response was received + + ''' + pass + + def on_close(self, error_code): + + '''Called when stream is about to close. + + ''' + pass + + def on_push_promise(self, push_promise): + + '''Called when a push is promised. Default behavior is to + cancel the push. If application overrides this method, + it should call either accept_push or reject_push. + + ''' + self.reject_push(push_promise) + + def reject_push(self, push_promise): + + '''Convenience method equivalent to calling accept_push + with a falsy value. + + ''' + self.http2.push(push_promise, None) + + def accept_push(self, push_promise, handler=None): + + '''Accept a push_promise and provider a handler for the + new stream. If a falsy value is supplied for the handler, + the push is rejected. + + ''' + self.http2.push(push_promise, handler) + + def resume(self): + self.http2.resume(self.stream_id) + + class _HTTP2ClientSession(asyncio.Protocol): + + def __init__(self, client): + asyncio.Protocol.__init__(self) + self.http2 = None + self.pending = [] + self.client = client + + def connection_made(self, transport): + address = transport.get_extra_info('peername') + logging.info('connection_made, address:%s, port:%s', address[0], address[1]) + + self.transport = transport + sock = self.transport.get_extra_info('socket') + sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) + ssl_ctx = self.transport.get_extra_info('sslcontext') + if ssl_ctx: + protocol = sock.selected_npn_protocol() + logging.info('npn, protocol:%s', protocol) + if protocol is None or protocol.encode('utf-8') != \ + cnghttp2.NGHTTP2_PROTO_VERSION_ID: + self.transport.abort() + + self.http2 = _HTTP2ClientSessionCore(self.transport) + + # Clear pending requests + send_pending = self.pending + self.pending = [] + for method,scheme,host,path,headers,body,handler in send_pending: + self.send_request(method=method, scheme=scheme, host=host, path=path,\ + headers=headers, body=body, handler=handler) + self.http2.send_data() + + def connection_lost(self, exc): + logging.info('connection_lost') + if self.http2: + self.http2 = None + self.client.close() + + def data_received(self, data): + try: + self.http2.data_received(data) + except Exception as err: + sys.stderr.write(traceback.format_exc()) + self.transport.close() + return + + def resume_writing(self): + try: + self.http2.send_data() + except Exception as err: + sys.stderr.write(traceback.format_exc()) + self.transport.close() + return + + def send_request(self, method, scheme, host, path, headers, body, handler): + try: + # Waiting until connection established + if not self.http2: + self.pending.append([method, scheme, host, path, headers, body, handler]) + return + + self.http2.send_request(method=method, scheme=scheme, host=host, path=path,\ + headers=headers, body=body, handler=handler) + self.http2.send_data() + except Exception as err: + sys.stderr.write(traceback.format_exc()) + self.transport.close() + return + + def close(self): + if self.http2: + self.http2.close() + + + class HTTP2Client: + + '''HTTP/2 client. + + This class builds on top of the asyncio event loop. + + ''' + def __init__(self, address, loop=None, ssl=None): + + '''address is a tuple of the connect address and port (e.g., + ('127.0.0.1', 8080)). The ssl can be ssl.SSLContext instance. + If it is not None, the resulting client is SSL/TLS capable. + ''' + + self.address = address + self.session = _HTTP2ClientSession(self) + def session_factory(): + return self.session + + if ssl: + ssl.set_npn_protocols([cnghttp2.NGHTTP2_PROTO_VERSION_ID\ + .decode('utf-8')]) + + self.loop = loop + if not self.loop: + self.loop = asyncio.get_event_loop() + + coro = self.loop.create_connection(session_factory, + host=address[0], port=address[1], + ssl=ssl) + + if ssl: + self.scheme = 'https' + else: + self.scheme = 'http' + + self.transport,_ = self.loop.run_until_complete(coro) + logging.info('connect, address:%s, port:%s', self.address[0], self.address[1]) + + @property + def io_loop(self): + return self.loop + + def close(self): + self.session.close() + + def send_request(self, method='GET', url='/', headers=None, body=None, handler=None): + url = urlparse(url) + scheme = url.scheme if url.scheme else self.scheme + host = url.netloc if url.netloc else self.address[0]+':'+str(self.address[1]) + path = url.path + if url.params: + path += ';'+url.params + if url.query: + path += '?'+url.query + if url.fragment: + path += '#'+url.fragment + + self.session.send_request(method=method, scheme=scheme, host=host, path=path,\ + headers=headers, body=body, handler=handler) diff --git a/python/setup.py.in b/python/setup.py.in new file mode 100644 index 0000000..7f9de48 --- /dev/null +++ b/python/setup.py.in @@ -0,0 +1,49 @@ +# nghttp2 - HTTP/2 C Library + +# Copyright (c) 2013 Tatsuhiro Tsujikawa + +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: + +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. + +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +import sys +from distutils.core import setup +from distutils.extension import Extension + +if sys.platform == "win32": + LIBS = ['nghttp2_imp', 'ws2_32'] +else: + LIBS = ['nghttp2'] + +setup( + name = 'python-nghttp2', + description = 'Python HTTP/2 library on top of nghttp2', + author = 'Tatsuhiro Tsujikawa', + version = '@PACKAGE_VERSION@', + author_email = 'tatsuhiro.t@gmail.com', + url = 'https://nghttp2.org/', + keywords = [], + ext_modules = [Extension("nghttp2", + ["nghttp2.c"], + include_dirs=['@top_srcdir@/lib', + '@top_srcdir@/lib/includes', + '@top_builddir@/lib/includes'], + library_dirs=['@top_builddir@/lib/.libs'], + libraries=LIBS)], + long_description='TBD' + ) diff --git a/src/HtmlParser.cc b/src/HtmlParser.cc new file mode 100644 index 0000000..bcbfd13 --- /dev/null +++ b/src/HtmlParser.cc @@ -0,0 +1,187 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "HtmlParser.h" + +#include + +#include "util.h" + +namespace nghttp2 { + +ParserData::ParserData(const std::string &base_uri) + : base_uri(base_uri), inside_head(0) {} + +HtmlParser::HtmlParser(const std::string &base_uri) + : base_uri_(base_uri), parser_ctx_(nullptr), parser_data_(base_uri) {} + +HtmlParser::~HtmlParser() { htmlFreeParserCtxt(parser_ctx_); } + +namespace { +const char *get_attr(const xmlChar **attrs, const char *name) { + if (attrs == nullptr) { + return nullptr; + } + for (; *attrs; attrs += 2) { + if (util::strieq(reinterpret_cast(attrs[0]), name)) { + return reinterpret_cast(attrs[1]); + } + } + return nullptr; +} +} // namespace + +namespace { +void add_link(ParserData *parser_data, const char *uri, ResourceType res_type) { + auto u = xmlBuildURI( + reinterpret_cast(uri), + reinterpret_cast(parser_data->base_uri.c_str())); + if (u) { + parser_data->links.push_back( + std::make_pair(reinterpret_cast(u), res_type)); + free(u); + } +} +} // namespace + +namespace { +void start_element_func(void *user_data, const xmlChar *name, + const xmlChar **attrs) { + auto parser_data = static_cast(user_data); + if (util::strieq(reinterpret_cast(name), "head")) { + ++parser_data->inside_head; + } + if (util::strieq(reinterpret_cast(name), "link")) { + auto rel_attr = get_attr(attrs, "rel"); + auto href_attr = get_attr(attrs, "href"); + if (!href_attr) { + return; + } + if (util::strieq(rel_attr, "shortcut icon")) { + add_link(parser_data, href_attr, REQ_OTHERS); + } else if (util::strieq(rel_attr, "stylesheet")) { + add_link(parser_data, href_attr, REQ_CSS); + } + } else if (util::strieq(reinterpret_cast(name), "img")) { + auto src_attr = get_attr(attrs, "src"); + if (!src_attr) { + return; + } + add_link(parser_data, src_attr, REQ_IMG); + } else if (util::strieq(reinterpret_cast(name), "script")) { + auto src_attr = get_attr(attrs, "src"); + if (!src_attr) { + return; + } + if (parser_data->inside_head) { + add_link(parser_data, src_attr, REQ_JS); + } else { + add_link(parser_data, src_attr, REQ_UNBLOCK_JS); + } + } +} +} // namespace + +namespace { +void end_element_func(void *user_data, const xmlChar *name) { + auto parser_data = static_cast(user_data); + if (util::strieq(reinterpret_cast(name), "head")) { + --parser_data->inside_head; + } +} +} // namespace + +namespace { +xmlSAXHandler saxHandler = { + nullptr, // internalSubsetSAXFunc + nullptr, // isStandaloneSAXFunc + nullptr, // hasInternalSubsetSAXFunc + nullptr, // hasExternalSubsetSAXFunc + nullptr, // resolveEntitySAXFunc + nullptr, // getEntitySAXFunc + nullptr, // entityDeclSAXFunc + nullptr, // notationDeclSAXFunc + nullptr, // attributeDeclSAXFunc + nullptr, // elementDeclSAXFunc + nullptr, // unparsedEntityDeclSAXFunc + nullptr, // setDocumentLocatorSAXFunc + nullptr, // startDocumentSAXFunc + nullptr, // endDocumentSAXFunc + &start_element_func, // startElementSAXFunc + &end_element_func, // endElementSAXFunc + nullptr, // referenceSAXFunc + nullptr, // charactersSAXFunc + nullptr, // ignorableWhitespaceSAXFunc + nullptr, // processingInstructionSAXFunc + nullptr, // commentSAXFunc + nullptr, // warningSAXFunc + nullptr, // errorSAXFunc + nullptr, // fatalErrorSAXFunc + nullptr, // getParameterEntitySAXFunc + nullptr, // cdataBlockSAXFunc + nullptr, // externalSubsetSAXFunc + 0, // unsigned int initialized + nullptr, // void * _private + nullptr, // startElementNsSAX2Func + nullptr, // endElementNsSAX2Func + nullptr, // xmlStructuredErrorFunc +}; +} // namespace + +int HtmlParser::parse_chunk(const char *chunk, size_t size, int fin) { + if (!parser_ctx_) { + parser_ctx_ = + htmlCreatePushParserCtxt(&saxHandler, &parser_data_, chunk, size, + base_uri_.c_str(), XML_CHAR_ENCODING_NONE); + if (!parser_ctx_) { + return -1; + } else { + if (fin) { + return parse_chunk_internal(nullptr, 0, fin); + } else { + return 0; + } + } + } else { + return parse_chunk_internal(chunk, size, fin); + } +} + +int HtmlParser::parse_chunk_internal(const char *chunk, size_t size, int fin) { + int rv = htmlParseChunk(parser_ctx_, chunk, size, fin); + if (rv == 0) { + return 0; + } else { + return -1; + } +} + +const std::vector> & +HtmlParser::get_links() const { + return parser_data_.links; +} + +void HtmlParser::clear_links() { parser_data_.links.clear(); } + +} // namespace nghttp2 diff --git a/src/HtmlParser.h b/src/HtmlParser.h new file mode 100644 index 0000000..fc2f2e5 --- /dev/null +++ b/src/HtmlParser.h @@ -0,0 +1,94 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef HTML_PARSER_H +#define HTML_PARSER_H + +#include "nghttp2_config.h" + +#include +#include + +#ifdef HAVE_LIBXML2 + +#include + +#endif // HAVE_LIBXML2 + +namespace nghttp2 { + +enum ResourceType { + REQ_CSS = 1, + REQ_JS, + REQ_UNBLOCK_JS, + REQ_IMG, + REQ_OTHERS, +}; + +struct ParserData { + std::string base_uri; + std::vector> links; + // > 0 if we are inside "head" element. + int inside_head; + ParserData(const std::string &base_uri); +}; + +#ifdef HAVE_LIBXML2 + +class HtmlParser { +public: + HtmlParser(const std::string &base_uri); + ~HtmlParser(); + int parse_chunk(const char *chunk, size_t size, int fin); + const std::vector> &get_links() const; + void clear_links(); + +private: + int parse_chunk_internal(const char *chunk, size_t size, int fin); + + std::string base_uri_; + htmlParserCtxtPtr parser_ctx_; + ParserData parser_data_; +}; + +#else // !HAVE_LIBXML2 + +class HtmlParser { +public: + HtmlParser(const std::string &base_uri) {} + int parse_chunk(const char *chunk, size_t size, int fin) { return 0; } + const std::vector> &get_links() const { + return links_; + } + void clear_links() {} + +private: + std::vector> links_; +}; + +#endif // !HAVE_LIBXML2 + +} // namespace nghttp2 + +#endif // HTML_PARSER_H diff --git a/src/HttpServer.cc b/src/HttpServer.cc new file mode 100644 index 0000000..0465395 --- /dev/null +++ b/src/HttpServer.cc @@ -0,0 +1,1889 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2013 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "HttpServer.h" + +#include +#ifdef HAVE_SYS_SOCKET_H +#include +#endif // HAVE_SYS_SOCKET_H +#ifdef HAVE_NETDB_H +#include +#endif // HAVE_NETDB_H +#ifdef HAVE_UNISTD_H +#include +#endif // HAVE_UNISTD_H +#ifdef HAVE_FCNTL_H +#include +#endif // HAVE_FCNTL_H +#ifdef HAVE_NETINET_IN_H +#include +#endif // HAVE_NETINET_IN_H +#include +#ifdef HAVE_ARPA_INET_H +#include +#endif // HAVE_ARPA_INET_H + +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include "app_helper.h" +#include "http2.h" +#include "util.h" +#include "ssl.h" +#include "template.h" + +#ifndef O_BINARY +#define O_BINARY (0) +#endif // O_BINARY + +namespace nghttp2 { + +namespace { +const std::string DEFAULT_HTML = "index.html"; +const std::string NGHTTPD_SERVER = "nghttpd nghttp2/" NGHTTP2_VERSION; +} // namespace + +namespace { +void delete_handler(Http2Handler *handler) { + handler->remove_self(); + delete handler; +} +} // namespace + +namespace { +void print_session_id(int64_t id) { std::cout << "[id=" << id << "] "; } +} // namespace + +namespace { +template void append_nv(Stream *stream, const Array &nva) { + for (size_t i = 0; i < nva.size(); ++i) { + auto &nv = nva[i]; + auto token = http2::lookup_token(nv.name, nv.namelen); + if (token != -1) { + http2::index_header(stream->hdidx, token, i); + } + http2::add_header(stream->headers, nv.name, nv.namelen, nv.value, + nv.valuelen, nv.flags & NGHTTP2_NV_FLAG_NO_INDEX, token); + } +} +} // namespace + +Config::Config() + : stream_read_timeout(60.), stream_write_timeout(60.), data_ptr(nullptr), + padding(0), num_worker(1), max_concurrent_streams(100), + header_table_size(-1), port(0), verbose(false), daemon(false), + verify_client(false), no_tls(false), error_gzip(false), + early_response(false), hexdump(false), echo_upload(false) {} + +Config::~Config() {} + +namespace { +void stream_timeout_cb(struct ev_loop *loop, ev_timer *w, int revents) { + int rv; + auto stream = static_cast(w->data); + auto hd = stream->handler; + auto config = hd->get_config(); + + ev_timer_stop(hd->get_loop(), &stream->rtimer); + ev_timer_stop(hd->get_loop(), &stream->wtimer); + + if (config->verbose) { + print_session_id(hd->session_id()); + print_timer(); + std::cout << " timeout stream_id=" << stream->stream_id << std::endl; + } + + hd->submit_rst_stream(stream, NGHTTP2_INTERNAL_ERROR); + + rv = hd->on_write(); + if (rv == -1) { + delete_handler(hd); + } +} +} // namespace + +namespace { +void add_stream_read_timeout(Stream *stream) { + auto hd = stream->handler; + ev_timer_again(hd->get_loop(), &stream->rtimer); +} +} // namespace + +namespace { +void add_stream_read_timeout_if_pending(Stream *stream) { + auto hd = stream->handler; + if (ev_is_active(&stream->rtimer)) { + ev_timer_again(hd->get_loop(), &stream->rtimer); + } +} +} // namespace + +namespace { +void add_stream_write_timeout(Stream *stream) { + auto hd = stream->handler; + ev_timer_again(hd->get_loop(), &stream->wtimer); +} +} // namespace + +namespace { +void remove_stream_read_timeout(Stream *stream) { + auto hd = stream->handler; + ev_timer_stop(hd->get_loop(), &stream->rtimer); +} +} // namespace + +namespace { +void remove_stream_write_timeout(Stream *stream) { + auto hd = stream->handler; + ev_timer_stop(hd->get_loop(), &stream->wtimer); +} +} // namespace + +namespace { +void fill_callback(nghttp2_session_callbacks *callbacks, const Config *config); +} // namespace + +class Sessions { +public: + Sessions(HttpServer *sv, struct ev_loop *loop, const Config *config, + SSL_CTX *ssl_ctx) + : sv_(sv), loop_(loop), config_(config), ssl_ctx_(ssl_ctx), + callbacks_(nullptr), next_session_id_(1), tstamp_cached_(ev_now(loop)), + cached_date_(util::http_date(tstamp_cached_)) { + nghttp2_session_callbacks_new(&callbacks_); + + fill_callback(callbacks_, config_); + } + ~Sessions() { + for (auto handler : handlers_) { + delete handler; + } + nghttp2_session_callbacks_del(callbacks_); + } + void add_handler(Http2Handler *handler) { handlers_.insert(handler); } + void remove_handler(Http2Handler *handler) { handlers_.erase(handler); } + SSL_CTX *get_ssl_ctx() const { return ssl_ctx_; } + SSL *ssl_session_new(int fd) { + SSL *ssl = SSL_new(ssl_ctx_); + if (!ssl) { + std::cerr << "SSL_new() failed" << std::endl; + return nullptr; + } + if (SSL_set_fd(ssl, fd) == 0) { + std::cerr << "SSL_set_fd() failed" << std::endl; + SSL_free(ssl); + return nullptr; + } + return ssl; + } + const Config *get_config() const { return config_; } + struct ev_loop *get_loop() const { + return loop_; + } + int64_t get_next_session_id() { + auto session_id = next_session_id_; + if (next_session_id_ == std::numeric_limits::max()) { + next_session_id_ = 1; + } else { + ++next_session_id_; + } + return session_id; + } + const nghttp2_session_callbacks *get_callbacks() const { return callbacks_; } + void accept_connection(int fd) { + util::make_socket_nodelay(fd); + SSL *ssl = nullptr; + if (ssl_ctx_) { + ssl = ssl_session_new(fd); + if (!ssl) { + close(fd); + return; + } + } + auto handler = + make_unique(this, fd, ssl, get_next_session_id()); + handler->setup_bev(); + if (!ssl) { + if (handler->connection_made() != 0) { + return; + } + } + add_handler(handler.release()); + } + void update_cached_date() { cached_date_ = util::http_date(tstamp_cached_); } + const std::string &get_cached_date() { + auto t = ev_now(loop_); + if (t != tstamp_cached_) { + tstamp_cached_ = t; + update_cached_date(); + } + return cached_date_; + } + FileEntry *get_cached_fd(const std::string &path) { + auto i = fd_cache_.find(path); + if (i == std::end(fd_cache_)) { + return nullptr; + } + auto &ent = (*i).second; + ++ent.usecount; + return &ent; + } + FileEntry *cache_fd(const std::string &path, const FileEntry &ent) { + auto rv = fd_cache_.emplace(path, ent); + return &(*rv.first).second; + } + void release_fd(const std::string &path) { + auto i = fd_cache_.find(path); + if (i == std::end(fd_cache_)) { + return; + } + auto &ent = (*i).second; + if (--ent.usecount == 0) { + close(ent.fd); + fd_cache_.erase(i); + } + } + const HttpServer *get_server() const { return sv_; } + +private: + std::set handlers_; + // cache for file descriptors to read file. + std::map fd_cache_; + HttpServer *sv_; + struct ev_loop *loop_; + const Config *config_; + SSL_CTX *ssl_ctx_; + nghttp2_session_callbacks *callbacks_; + int64_t next_session_id_; + ev_tstamp tstamp_cached_; + std::string cached_date_; +}; + +Stream::Stream(Http2Handler *handler, int32_t stream_id) + : handler(handler), file_ent(nullptr), body_length(0), body_offset(0), + stream_id(stream_id), echo_upload(false) { + auto config = handler->get_config(); + ev_timer_init(&rtimer, stream_timeout_cb, 0., config->stream_read_timeout); + ev_timer_init(&wtimer, stream_timeout_cb, 0., config->stream_write_timeout); + rtimer.data = this; + wtimer.data = this; + + headers.reserve(10); + + http2::init_hdidx(hdidx); +} + +Stream::~Stream() { + if (file_ent != nullptr) { + auto sessions = handler->get_sessions(); + sessions->release_fd(file_ent->path); + } + + auto loop = handler->get_loop(); + ev_timer_stop(loop, &rtimer); + ev_timer_stop(loop, &wtimer); +} + +namespace { +void on_session_closed(Http2Handler *hd, int64_t session_id) { + if (hd->get_config()->verbose) { + print_session_id(session_id); + print_timer(); + std::cout << " closed" << std::endl; + } +} +} // namespace + +namespace { +void settings_timeout_cb(struct ev_loop *loop, ev_timer *w, int revents) { + auto hd = static_cast(w->data); + hd->terminate_session(NGHTTP2_SETTINGS_TIMEOUT); + hd->on_write(); +} +} // namespace + +namespace { +void readcb(struct ev_loop *loop, ev_io *w, int revents) { + int rv; + auto handler = static_cast(w->data); + + rv = handler->on_read(); + if (rv == -1) { + delete_handler(handler); + } +} +} // namespace + +namespace { +void writecb(struct ev_loop *loop, ev_io *w, int revents) { + int rv; + auto handler = static_cast(w->data); + + rv = handler->on_write(); + if (rv == -1) { + delete_handler(handler); + } +} +} // namespace + +Http2Handler::Http2Handler(Sessions *sessions, int fd, SSL *ssl, + int64_t session_id) + : session_id_(session_id), session_(nullptr), sessions_(sessions), + ssl_(ssl), data_pending_(nullptr), data_pendinglen_(0), fd_(fd) { + ev_timer_init(&settings_timerev_, settings_timeout_cb, 10., 0.); + ev_io_init(&wev_, writecb, fd, EV_WRITE); + ev_io_init(&rev_, readcb, fd, EV_READ); + + settings_timerev_.data = this; + wev_.data = this; + rev_.data = this; + + auto loop = sessions_->get_loop(); + ev_io_start(loop, &rev_); + + if (ssl) { + SSL_set_accept_state(ssl); + read_ = &Http2Handler::tls_handshake; + write_ = &Http2Handler::tls_handshake; + } else { + read_ = &Http2Handler::read_clear; + write_ = &Http2Handler::write_clear; + } +} + +Http2Handler::~Http2Handler() { + on_session_closed(this, session_id_); + nghttp2_session_del(session_); + if (ssl_) { + SSL_set_shutdown(ssl_, SSL_RECEIVED_SHUTDOWN); + ERR_clear_error(); + SSL_shutdown(ssl_); + } + auto loop = sessions_->get_loop(); + ev_timer_stop(loop, &settings_timerev_); + ev_io_stop(loop, &rev_); + ev_io_stop(loop, &wev_); + if (ssl_) { + SSL_free(ssl_); + } + shutdown(fd_, SHUT_WR); + close(fd_); +} + +void Http2Handler::remove_self() { sessions_->remove_handler(this); } + +struct ev_loop *Http2Handler::get_loop() const { + return sessions_->get_loop(); +} + +Http2Handler::WriteBuf *Http2Handler::get_wb() { return &wb_; } + +int Http2Handler::setup_bev() { return 0; } + +int Http2Handler::fill_wb() { + if (data_pending_) { + auto n = std::min(wb_.wleft(), data_pendinglen_); + wb_.write(data_pending_, n); + if (n < data_pendinglen_) { + data_pending_ += n; + data_pendinglen_ -= n; + return 0; + } + + data_pending_ = nullptr; + data_pendinglen_ = 0; + } + + for (;;) { + const uint8_t *data; + auto datalen = nghttp2_session_mem_send(session_, &data); + + if (datalen < 0) { + std::cerr << "nghttp2_session_mem_send() returned error: " + << nghttp2_strerror(datalen) << std::endl; + return -1; + } + if (datalen == 0) { + break; + } + auto n = wb_.write(data, datalen); + if (n < static_cast(datalen)) { + data_pending_ = data + n; + data_pendinglen_ = datalen - n; + break; + } + } + return 0; +} + +int Http2Handler::read_clear() { + int rv; + std::array buf; + + for (;;) { + ssize_t nread; + while ((nread = read(fd_, buf.data(), buf.size())) == -1 && errno == EINTR) + ; + if (nread == -1) { + if (errno == EAGAIN || errno == EWOULDBLOCK) { + break; + } + return -1; + } + if (nread == 0) { + return -1; + } + + if (get_config()->hexdump) { + util::hexdump(stdout, buf.data(), nread); + } + + rv = nghttp2_session_mem_recv(session_, buf.data(), nread); + if (rv < 0) { + if (rv != NGHTTP2_ERR_BAD_CLIENT_MAGIC) { + std::cerr << "nghttp2_session_mem_recv() returned error: " + << nghttp2_strerror(rv) << std::endl; + } + return -1; + } + } + + return write_(*this); +} + +int Http2Handler::write_clear() { + auto loop = sessions_->get_loop(); + for (;;) { + if (wb_.rleft() > 0) { + ssize_t nwrite; + while ((nwrite = write(fd_, wb_.pos, wb_.rleft())) == -1 && + errno == EINTR) + ; + if (nwrite == -1) { + if (errno == EAGAIN || errno == EWOULDBLOCK) { + ev_io_start(loop, &wev_); + return 0; + } + return -1; + } + wb_.drain(nwrite); + continue; + } + wb_.reset(); + if (fill_wb() != 0) { + return -1; + } + if (wb_.rleft() == 0) { + break; + } + } + + if (wb_.rleft() == 0) { + ev_io_stop(loop, &wev_); + } else { + ev_io_start(loop, &wev_); + } + + if (nghttp2_session_want_read(session_) == 0 && + nghttp2_session_want_write(session_) == 0 && wb_.rleft() == 0) { + return -1; + } + + return 0; +} + +int Http2Handler::tls_handshake() { + ev_io_stop(sessions_->get_loop(), &wev_); + + ERR_clear_error(); + + auto rv = SSL_do_handshake(ssl_); + + if (rv == 0) { + return -1; + } + + if (rv < 0) { + auto err = SSL_get_error(ssl_, rv); + switch (err) { + case SSL_ERROR_WANT_READ: + return 0; + case SSL_ERROR_WANT_WRITE: + ev_io_start(sessions_->get_loop(), &wev_); + return 0; + default: + return -1; + } + } + + if (sessions_->get_config()->verbose) { + std::cerr << "SSL/TLS handshake completed" << std::endl; + } + + if (verify_npn_result() != 0) { + return -1; + } + + read_ = &Http2Handler::read_tls; + write_ = &Http2Handler::write_tls; + + if (connection_made() != 0) { + return -1; + } + + return 0; +} + +int Http2Handler::read_tls() { + std::array buf; + + ERR_clear_error(); + + for (;;) { + auto rv = SSL_read(ssl_, buf.data(), buf.size()); + + if (rv == 0) { + return -1; + } + + if (rv < 0) { + auto err = SSL_get_error(ssl_, rv); + switch (err) { + case SSL_ERROR_WANT_READ: + goto fin; + case SSL_ERROR_WANT_WRITE: + // renegotiation started + return -1; + default: + return -1; + } + } + + auto nread = rv; + + if (get_config()->hexdump) { + util::hexdump(stdout, buf.data(), nread); + } + + rv = nghttp2_session_mem_recv(session_, buf.data(), nread); + if (rv < 0) { + if (rv != NGHTTP2_ERR_BAD_CLIENT_MAGIC) { + std::cerr << "nghttp2_session_mem_recv() returned error: " + << nghttp2_strerror(rv) << std::endl; + } + return -1; + } + } + +fin: + return write_(*this); +} + +int Http2Handler::write_tls() { + auto loop = sessions_->get_loop(); + + ERR_clear_error(); + + for (;;) { + if (wb_.rleft() > 0) { + auto rv = SSL_write(ssl_, wb_.pos, wb_.rleft()); + + if (rv == 0) { + return -1; + } + + if (rv < 0) { + auto err = SSL_get_error(ssl_, rv); + switch (err) { + case SSL_ERROR_WANT_READ: + // renegotiation started + return -1; + case SSL_ERROR_WANT_WRITE: + ev_io_start(sessions_->get_loop(), &wev_); + return 0; + default: + return -1; + } + } + + wb_.drain(rv); + continue; + } + wb_.reset(); + if (fill_wb() != 0) { + return -1; + } + if (wb_.rleft() == 0) { + break; + } + } + + if (wb_.rleft() == 0) { + ev_io_stop(loop, &wev_); + } else { + ev_io_start(loop, &wev_); + } + + if (nghttp2_session_want_read(session_) == 0 && + nghttp2_session_want_write(session_) == 0 && wb_.rleft() == 0) { + return -1; + } + + return 0; +} + +int Http2Handler::on_read() { return read_(*this); } + +int Http2Handler::on_write() { return write_(*this); } + +int Http2Handler::connection_made() { + int r; + + r = nghttp2_session_server_new(&session_, sessions_->get_callbacks(), this); + + if (r != 0) { + return r; + } + + auto config = sessions_->get_config(); + std::array entry; + size_t niv = 1; + + entry[0].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS; + entry[0].value = config->max_concurrent_streams; + + if (config->header_table_size >= 0) { + entry[niv].settings_id = NGHTTP2_SETTINGS_HEADER_TABLE_SIZE; + entry[niv].value = config->header_table_size; + ++niv; + } + r = nghttp2_submit_settings(session_, NGHTTP2_FLAG_NONE, entry.data(), niv); + if (r != 0) { + return r; + } + + ev_timer_start(sessions_->get_loop(), &settings_timerev_); + + return on_write(); +} + +int Http2Handler::verify_npn_result() { + const unsigned char *next_proto = nullptr; + unsigned int next_proto_len; + // Check the negotiated protocol in NPN or ALPN + SSL_get0_next_proto_negotiated(ssl_, &next_proto, &next_proto_len); + for (int i = 0; i < 2; ++i) { + if (next_proto) { + if (sessions_->get_config()->verbose) { + std::string proto(next_proto, next_proto + next_proto_len); + std::cout << "The negotiated protocol: " << proto << std::endl; + } + if (util::check_h2_is_selected(next_proto, next_proto_len)) { + return 0; + } + break; + } else { +#if OPENSSL_VERSION_NUMBER >= 0x10002000L + SSL_get0_alpn_selected(ssl_, &next_proto, &next_proto_len); +#else // OPENSSL_VERSION_NUMBER < 0x10002000L + break; +#endif // OPENSSL_VERSION_NUMBER < 0x10002000L + } + } + if (sessions_->get_config()->verbose) { + std::cerr << "Client did not advertise HTTP/2 protocol." + << " (nghttp2 expects " << NGHTTP2_PROTO_VERSION_ID << ")" + << std::endl; + } + return -1; +} + +int Http2Handler::submit_file_response(const std::string &status, + Stream *stream, time_t last_modified, + off_t file_length, + nghttp2_data_provider *data_prd) { + std::string content_length = util::utos(file_length); + std::string last_modified_str; + auto nva = make_array(http2::make_nv_ls(":status", status), + http2::make_nv_ls("server", NGHTTPD_SERVER), + http2::make_nv_ls("content-length", content_length), + http2::make_nv_ll("cache-control", "max-age=3600"), + http2::make_nv_ls("date", sessions_->get_cached_date()), + http2::make_nv_ll("", ""), http2::make_nv_ll("", "")); + size_t nvlen = 5; + if (last_modified != 0) { + last_modified_str = util::http_date(last_modified); + nva[nvlen++] = http2::make_nv_ls("last-modified", last_modified_str); + } + auto &trailer = get_config()->trailer; + std::string trailer_names; + if (!trailer.empty()) { + trailer_names = trailer[0].name; + for (size_t i = 1; i < trailer.size(); ++i) { + trailer_names += ", "; + trailer_names += trailer[i].name; + } + nva[nvlen++] = http2::make_nv_ls("trailer", trailer_names); + } + return nghttp2_submit_response(session_, stream->stream_id, nva.data(), nvlen, + data_prd); +} + +int Http2Handler::submit_response(const std::string &status, int32_t stream_id, + const Headers &headers, + nghttp2_data_provider *data_prd) { + auto nva = std::vector(); + nva.reserve(3 + headers.size()); + nva.push_back(http2::make_nv_ls(":status", status)); + nva.push_back(http2::make_nv_ls("server", NGHTTPD_SERVER)); + nva.push_back(http2::make_nv_ls("date", sessions_->get_cached_date())); + for (auto &nv : headers) { + nva.push_back(http2::make_nv(nv.name, nv.value, nv.no_index)); + } + int r = nghttp2_submit_response(session_, stream_id, nva.data(), nva.size(), + data_prd); + return r; +} + +int Http2Handler::submit_response(const std::string &status, int32_t stream_id, + nghttp2_data_provider *data_prd) { + auto nva = make_array(http2::make_nv_ls(":status", status), + http2::make_nv_ls("server", NGHTTPD_SERVER)); + return nghttp2_submit_response(session_, stream_id, nva.data(), nva.size(), + data_prd); +} + +int Http2Handler::submit_non_final_response(const std::string &status, + int32_t stream_id) { + auto nva = make_array(http2::make_nv_ls(":status", status)); + return nghttp2_submit_headers(session_, NGHTTP2_FLAG_NONE, stream_id, nullptr, + nva.data(), nva.size(), nullptr); +} + +int Http2Handler::submit_push_promise(Stream *stream, + const std::string &push_path) { + auto authority = + http2::get_header(stream->hdidx, http2::HD__AUTHORITY, stream->headers); + + if (!authority) { + authority = + http2::get_header(stream->hdidx, http2::HD_HOST, stream->headers); + } + + auto nva = + make_array(http2::make_nv_ll(":method", "GET"), + http2::make_nv_ls(":path", push_path), + get_config()->no_tls ? http2::make_nv_ll(":scheme", "http") + : http2::make_nv_ll(":scheme", "https"), + http2::make_nv_ls(":authority", authority->value)); + + auto promised_stream_id = nghttp2_submit_push_promise( + session_, NGHTTP2_FLAG_END_HEADERS, stream->stream_id, nva.data(), + nva.size(), nullptr); + + if (promised_stream_id < 0) { + return promised_stream_id; + } + + auto promised_stream = make_unique(this, promised_stream_id); + + append_nv(promised_stream.get(), nva); + add_stream(promised_stream_id, std::move(promised_stream)); + + return 0; +} + +int Http2Handler::submit_rst_stream(Stream *stream, uint32_t error_code) { + remove_stream_read_timeout(stream); + remove_stream_write_timeout(stream); + + return nghttp2_submit_rst_stream(session_, NGHTTP2_FLAG_NONE, + stream->stream_id, error_code); +} + +void Http2Handler::add_stream(int32_t stream_id, + std::unique_ptr stream) { + id2stream_[stream_id] = std::move(stream); +} + +void Http2Handler::remove_stream(int32_t stream_id) { + id2stream_.erase(stream_id); +} + +Stream *Http2Handler::get_stream(int32_t stream_id) { + auto itr = id2stream_.find(stream_id); + if (itr == std::end(id2stream_)) { + return nullptr; + } else { + return (*itr).second.get(); + } +} + +int64_t Http2Handler::session_id() const { return session_id_; } + +Sessions *Http2Handler::get_sessions() const { return sessions_; } + +const Config *Http2Handler::get_config() const { + return sessions_->get_config(); +} + +void Http2Handler::remove_settings_timer() { + ev_timer_stop(sessions_->get_loop(), &settings_timerev_); +} + +void Http2Handler::terminate_session(uint32_t error_code) { + nghttp2_session_terminate_session(session_, error_code); +} + +ssize_t file_read_callback(nghttp2_session *session, int32_t stream_id, + uint8_t *buf, size_t length, uint32_t *data_flags, + nghttp2_data_source *source, void *user_data) { + int rv; + auto hd = static_cast(user_data); + auto stream = hd->get_stream(stream_id); + + auto nread = std::min(stream->body_length - stream->body_offset, + static_cast(length)); + + *data_flags |= NGHTTP2_DATA_FLAG_NO_COPY; + + if (nread == 0 || stream->body_length == stream->body_offset + nread) { + *data_flags |= NGHTTP2_DATA_FLAG_EOF; + + auto config = hd->get_config(); + if (!config->trailer.empty()) { + std::vector nva; + nva.reserve(config->trailer.size()); + for (auto &kv : config->trailer) { + nva.push_back(http2::make_nv(kv.name, kv.value, kv.no_index)); + } + rv = nghttp2_submit_trailer(session, stream_id, nva.data(), nva.size()); + if (rv != 0) { + if (nghttp2_is_fatal(rv)) { + return NGHTTP2_ERR_CALLBACK_FAILURE; + } + } else { + *data_flags |= NGHTTP2_DATA_FLAG_NO_END_STREAM; + } + } + + if (nghttp2_session_get_stream_remote_close(session, stream_id) == 0) { + remove_stream_read_timeout(stream); + remove_stream_write_timeout(stream); + + hd->submit_rst_stream(stream, NGHTTP2_NO_ERROR); + } + } + + return nread; +} + +namespace { +void prepare_status_response(Stream *stream, Http2Handler *hd, int status) { + auto sessions = hd->get_sessions(); + auto status_page = sessions->get_server()->get_status_page(status); + auto file_ent = &status_page->file_ent; + + // we don't set stream->file_ent since we don't want to expire it. + stream->body_length = file_ent->length; + nghttp2_data_provider data_prd; + data_prd.source.fd = file_ent->fd; + data_prd.read_callback = file_read_callback; + + Headers headers; + headers.emplace_back("content-type", "text/html; charset=UTF-8"); + hd->submit_response(status_page->status, stream->stream_id, headers, + &data_prd); +} +} // namespace + +namespace { +void prepare_echo_response(Stream *stream, Http2Handler *hd) { + auto length = lseek(stream->file_ent->fd, 0, SEEK_END); + if (length == -1) { + hd->submit_rst_stream(stream, NGHTTP2_INTERNAL_ERROR); + return; + } + stream->body_length = length; + if (lseek(stream->file_ent->fd, 0, SEEK_SET) == -1) { + hd->submit_rst_stream(stream, NGHTTP2_INTERNAL_ERROR); + return; + } + nghttp2_data_provider data_prd; + data_prd.source.fd = stream->file_ent->fd; + data_prd.read_callback = file_read_callback; + + Headers headers; + headers.emplace_back("nghttpd-response", "echo"); + headers.emplace_back("content-length", util::utos(length)); + + hd->submit_response("200", stream->stream_id, headers, &data_prd); +} +} // namespace + +namespace { +bool prepare_upload_temp_store(Stream *stream, Http2Handler *hd) { + auto sessions = hd->get_sessions(); + + char tempfn[] = "/tmp/nghttpd.temp.XXXXXX"; + auto fd = mkstemp(tempfn); + if (fd == -1) { + return false; + } + unlink(tempfn); + // Ordinary request never start with "echo:". The length is 0 for + // now. We will update it when we get whole request body. + stream->file_ent = sessions->cache_fd(std::string("echo:") + tempfn, + FileEntry(tempfn, 0, 0, fd)); + stream->echo_upload = true; + return true; +} +} // namespace + +namespace { +void prepare_redirect_response(Stream *stream, Http2Handler *hd, + const std::string &path, int status) { + auto scheme = + http2::get_header(stream->hdidx, http2::HD__SCHEME, stream->headers); + auto authority = + http2::get_header(stream->hdidx, http2::HD__AUTHORITY, stream->headers); + if (!authority) { + authority = + http2::get_header(stream->hdidx, http2::HD_HOST, stream->headers); + } + + auto redirect_url = scheme->value; + redirect_url += "://"; + redirect_url += authority->value; + redirect_url += path; + + auto headers = Headers{{"location", redirect_url}}; + + auto sessions = hd->get_sessions(); + auto status_page = sessions->get_server()->get_status_page(status); + + hd->submit_response(status_page->status, stream->stream_id, headers, nullptr); +} +} // namespace + +namespace { +void prepare_response(Stream *stream, Http2Handler *hd, + bool allow_push = true) { + int rv; + auto reqpath = + http2::get_header(stream->hdidx, http2::HD__PATH, stream->headers)->value; + auto ims = + get_header(stream->hdidx, http2::HD_IF_MODIFIED_SINCE, stream->headers); + + time_t last_mod = 0; + bool last_mod_found = false; + if (ims) { + last_mod_found = true; + last_mod = util::parse_http_date(ims->value); + } + auto query_pos = reqpath.find("?"); + std::string url; + if (query_pos != std::string::npos) { + // Do not response to this request to allow clients to test timeouts. + if (reqpath.find("nghttpd_do_not_respond_to_req=yes", query_pos) != + std::string::npos) { + return; + } + url = reqpath.substr(0, query_pos); + } else { + url = reqpath; + } + + auto sessions = hd->get_sessions(); + + url = util::percentDecode(std::begin(url), std::end(url)); + if (!util::check_path(url)) { + if (stream->file_ent) { + sessions->release_fd(stream->file_ent->path); + stream->file_ent = nullptr; + } + prepare_status_response(stream, hd, 404); + return; + } + auto push_itr = hd->get_config()->push.find(url); + if (allow_push && push_itr != std::end(hd->get_config()->push)) { + for (auto &push_path : (*push_itr).second) { + rv = hd->submit_push_promise(stream, push_path); + if (rv != 0) { + std::cerr << "nghttp2_submit_push_promise() returned error: " + << nghttp2_strerror(rv) << std::endl; + } + } + } + + std::string path = hd->get_config()->htdocs + url; + if (path[path.size() - 1] == '/') { + path += DEFAULT_HTML; + } + + if (stream->echo_upload) { + assert(stream->file_ent); + prepare_echo_response(stream, hd); + return; + } + + auto file_ent = sessions->get_cached_fd(path); + + if (file_ent == nullptr) { + int file = open(path.c_str(), O_RDONLY | O_BINARY); + if (file == -1) { + prepare_status_response(stream, hd, 404); + + return; + } + + struct stat buf; + + if (fstat(file, &buf) == -1) { + close(file); + prepare_status_response(stream, hd, 404); + + return; + } + + if (buf.st_mode & S_IFDIR) { + close(file); + + if (query_pos == std::string::npos) { + reqpath += "/"; + } else { + reqpath.insert(query_pos, "/"); + } + + prepare_redirect_response(stream, hd, reqpath, 301); + + return; + } + + if (last_mod_found && buf.st_mtime <= last_mod) { + close(file); + prepare_status_response(stream, hd, 304); + + return; + } + + file_ent = sessions->cache_fd( + path, FileEntry(path, buf.st_size, buf.st_mtime, file)); + } else if (last_mod_found && file_ent->mtime <= last_mod) { + sessions->release_fd(file_ent->path); + prepare_status_response(stream, hd, 304); + + return; + } + + stream->file_ent = file_ent; + stream->body_length = file_ent->length; + + nghttp2_data_provider data_prd; + + data_prd.source.fd = file_ent->fd; + data_prd.read_callback = file_read_callback; + + hd->submit_file_response("200", stream, file_ent->mtime, file_ent->length, + &data_prd); +} +} // namespace + +namespace { +int on_header_callback(nghttp2_session *session, const nghttp2_frame *frame, + const uint8_t *name, size_t namelen, + const uint8_t *value, size_t valuelen, uint8_t flags, + void *user_data) { + auto hd = static_cast(user_data); + if (hd->get_config()->verbose) { + print_session_id(hd->session_id()); + verbose_on_header_callback(session, frame, name, namelen, value, valuelen, + flags, user_data); + } + if (frame->hd.type != NGHTTP2_HEADERS || + frame->headers.cat != NGHTTP2_HCAT_REQUEST) { + return 0; + } + auto stream = hd->get_stream(frame->hd.stream_id); + if (!stream) { + return 0; + } + + auto token = http2::lookup_token(name, namelen); + + http2::index_header(stream->hdidx, token, stream->headers.size()); + http2::add_header(stream->headers, name, namelen, value, valuelen, + flags & NGHTTP2_NV_FLAG_NO_INDEX, token); + return 0; +} +} // namespace + +namespace { +int on_begin_headers_callback(nghttp2_session *session, + const nghttp2_frame *frame, void *user_data) { + auto hd = static_cast(user_data); + + if (frame->hd.type != NGHTTP2_HEADERS || + frame->headers.cat != NGHTTP2_HCAT_REQUEST) { + return 0; + } + + auto stream = make_unique(hd, frame->hd.stream_id); + + add_stream_read_timeout(stream.get()); + + hd->add_stream(frame->hd.stream_id, std::move(stream)); + + return 0; +} +} // namespace + +namespace { +int hd_on_frame_recv_callback(nghttp2_session *session, + const nghttp2_frame *frame, void *user_data) { + auto hd = static_cast(user_data); + if (hd->get_config()->verbose) { + print_session_id(hd->session_id()); + verbose_on_frame_recv_callback(session, frame, user_data); + } + switch (frame->hd.type) { + case NGHTTP2_DATA: { + // TODO Handle POST + auto stream = hd->get_stream(frame->hd.stream_id); + if (!stream) { + return 0; + } + + if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) { + remove_stream_read_timeout(stream); + if (stream->echo_upload || !hd->get_config()->early_response) { + prepare_response(stream, hd); + } + } else { + add_stream_read_timeout(stream); + } + + break; + } + case NGHTTP2_HEADERS: { + auto stream = hd->get_stream(frame->hd.stream_id); + if (!stream) { + return 0; + } + + if (frame->headers.cat == NGHTTP2_HCAT_REQUEST) { + + auto expect100 = + http2::get_header(stream->hdidx, http2::HD_EXPECT, stream->headers); + + if (expect100 && util::strieq_l("100-continue", expect100->value)) { + hd->submit_non_final_response("100", frame->hd.stream_id); + } + + auto &method = http2::get_header(stream->hdidx, http2::HD__METHOD, + stream->headers)->value; + if (hd->get_config()->echo_upload && + (method == "POST" || method == "PUT")) { + if (!prepare_upload_temp_store(stream, hd)) { + hd->submit_rst_stream(stream, NGHTTP2_INTERNAL_ERROR); + return 0; + } + } else if (hd->get_config()->early_response) { + prepare_response(stream, hd); + } + } + + if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) { + remove_stream_read_timeout(stream); + if (stream->echo_upload || !hd->get_config()->early_response) { + prepare_response(stream, hd); + } + } else { + add_stream_read_timeout(stream); + } + + break; + } + case NGHTTP2_SETTINGS: + if (frame->hd.flags & NGHTTP2_FLAG_ACK) { + hd->remove_settings_timer(); + } + break; + default: + break; + } + return 0; +} +} // namespace + +namespace { +int hd_on_frame_send_callback(nghttp2_session *session, + const nghttp2_frame *frame, void *user_data) { + auto hd = static_cast(user_data); + + if (hd->get_config()->verbose) { + print_session_id(hd->session_id()); + verbose_on_frame_send_callback(session, frame, user_data); + } + + switch (frame->hd.type) { + case NGHTTP2_DATA: + case NGHTTP2_HEADERS: { + auto stream = hd->get_stream(frame->hd.stream_id); + + if (!stream) { + return 0; + } + + if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) { + remove_stream_write_timeout(stream); + } else if (std::min(nghttp2_session_get_stream_remote_window_size( + session, frame->hd.stream_id), + nghttp2_session_get_remote_window_size(session)) <= 0) { + // If stream is blocked by flow control, enable write timeout. + add_stream_read_timeout_if_pending(stream); + add_stream_write_timeout(stream); + } else { + add_stream_read_timeout_if_pending(stream); + remove_stream_write_timeout(stream); + } + + break; + } + case NGHTTP2_PUSH_PROMISE: { + auto promised_stream_id = frame->push_promise.promised_stream_id; + auto promised_stream = hd->get_stream(promised_stream_id); + auto stream = hd->get_stream(frame->hd.stream_id); + + if (!stream || !promised_stream) { + return 0; + } + + add_stream_read_timeout_if_pending(stream); + add_stream_write_timeout(stream); + + prepare_response(promised_stream, hd, /*allow_push */ false); + } + } + return 0; +} +} // namespace + +namespace { +int send_data_callback(nghttp2_session *session, nghttp2_frame *frame, + const uint8_t *framehd, size_t length, + nghttp2_data_source *source, void *user_data) { + auto hd = static_cast(user_data); + auto wb = hd->get_wb(); + auto padlen = frame->data.padlen; + auto stream = hd->get_stream(frame->hd.stream_id); + + if (wb->wleft() < 9 + length + padlen) { + return NGHTTP2_ERR_WOULDBLOCK; + } + + int fd = source->fd; + + auto p = wb->last; + + p = std::copy_n(framehd, 9, p); + + if (padlen) { + *p++ = padlen - 1; + } + + while (length) { + ssize_t nread; + while ((nread = pread(fd, p, length, stream->body_offset)) == -1 && + errno == EINTR) + ; + + if (nread == -1) { + remove_stream_read_timeout(stream); + remove_stream_write_timeout(stream); + + return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; + } + + stream->body_offset += nread; + length -= nread; + p += nread; + } + + if (padlen) { + std::fill(p, p + padlen - 1, 0); + p += padlen - 1; + } + + wb->last = p; + + return 0; +} +} // namespace + +namespace { +ssize_t select_padding_callback(nghttp2_session *session, + const nghttp2_frame *frame, size_t max_payload, + void *user_data) { + auto hd = static_cast(user_data); + return std::min(max_payload, frame->hd.length + hd->get_config()->padding); +} +} // namespace + +namespace { +int on_data_chunk_recv_callback(nghttp2_session *session, uint8_t flags, + int32_t stream_id, const uint8_t *data, + size_t len, void *user_data) { + auto hd = static_cast(user_data); + auto stream = hd->get_stream(stream_id); + + if (!stream) { + return 0; + } + + if (stream->echo_upload) { + assert(stream->file_ent); + while (len) { + ssize_t n; + while ((n = write(stream->file_ent->fd, data, len)) == -1 && + errno == EINTR) + ; + if (n == -1) { + hd->submit_rst_stream(stream, NGHTTP2_INTERNAL_ERROR); + return 0; + } + len -= n; + data += n; + } + } + // TODO Handle POST + + add_stream_read_timeout(stream); + + return 0; +} +} // namespace + +namespace { +int on_stream_close_callback(nghttp2_session *session, int32_t stream_id, + uint32_t error_code, void *user_data) { + auto hd = static_cast(user_data); + hd->remove_stream(stream_id); + if (hd->get_config()->verbose) { + print_session_id(hd->session_id()); + print_timer(); + printf(" stream_id=%d closed\n", stream_id); + fflush(stdout); + } + return 0; +} +} // namespace + +namespace { +void fill_callback(nghttp2_session_callbacks *callbacks, const Config *config) { + nghttp2_session_callbacks_set_on_stream_close_callback( + callbacks, on_stream_close_callback); + + nghttp2_session_callbacks_set_on_frame_recv_callback( + callbacks, hd_on_frame_recv_callback); + + nghttp2_session_callbacks_set_on_frame_send_callback( + callbacks, hd_on_frame_send_callback); + + if (config->verbose) { + nghttp2_session_callbacks_set_on_invalid_frame_recv_callback( + callbacks, verbose_on_invalid_frame_recv_callback); + } + + nghttp2_session_callbacks_set_on_data_chunk_recv_callback( + callbacks, on_data_chunk_recv_callback); + + nghttp2_session_callbacks_set_on_header_callback(callbacks, + on_header_callback); + + nghttp2_session_callbacks_set_on_begin_headers_callback( + callbacks, on_begin_headers_callback); + + nghttp2_session_callbacks_set_send_data_callback(callbacks, + send_data_callback); + + if (config->padding) { + nghttp2_session_callbacks_set_select_padding_callback( + callbacks, select_padding_callback); + } +} +} // namespace + +struct ClientInfo { + int fd; +}; + +struct Worker { + std::unique_ptr sessions; + ev_async w; + // protectes q + std::mutex m; + std::deque q; +}; + +namespace { +void worker_acceptcb(struct ev_loop *loop, ev_async *w, int revents) { + auto worker = static_cast(w->data); + auto &sessions = worker->sessions; + + std::deque q; + { + std::lock_guard lock(worker->m); + q.swap(worker->q); + } + + for (auto c : q) { + sessions->accept_connection(c.fd); + } +} +} // namespace + +namespace { +void run_worker(Worker *worker) { + auto loop = worker->sessions->get_loop(); + + ev_run(loop, 0); +} +} // namespace + +class AcceptHandler { +public: + AcceptHandler(HttpServer *sv, Sessions *sessions, const Config *config) + : sessions_(sessions), config_(config), next_worker_(0) { + if (config_->num_worker == 1) { + return; + } + for (size_t i = 0; i < config_->num_worker; ++i) { + if (config_->verbose) { + std::cerr << "spawning thread #" << i << std::endl; + } + auto worker = make_unique(); + auto loop = ev_loop_new(0); + worker->sessions = + make_unique(sv, loop, config_, sessions_->get_ssl_ctx()); + ev_async_init(&worker->w, worker_acceptcb); + worker->w.data = worker.get(); + ev_async_start(loop, &worker->w); + + auto t = std::thread(run_worker, worker.get()); + t.detach(); + workers_.push_back(std::move(worker)); + } + } + void accept_connection(int fd) { + if (config_->num_worker == 1) { + sessions_->accept_connection(fd); + return; + } + + // Dispatch client to the one of the worker threads, in a round + // robin manner. + auto &worker = workers_[next_worker_]; + if (next_worker_ == config_->num_worker - 1) { + next_worker_ = 0; + } else { + ++next_worker_; + } + { + std::lock_guard lock(worker->m); + worker->q.push_back({fd}); + } + ev_async_send(worker->sessions->get_loop(), &worker->w); + } + +private: + std::vector> workers_; + Sessions *sessions_; + const Config *config_; + // In multi threading mode, this points to the next thread that + // client will be dispatched. + size_t next_worker_; +}; + +namespace { +void acceptcb(struct ev_loop *loop, ev_io *w, int revents); +} // namespace + +class ListenEventHandler { +public: + ListenEventHandler(Sessions *sessions, int fd, + std::shared_ptr acceptor) + : acceptor_(acceptor), sessions_(sessions), fd_(fd) { + ev_io_init(&w_, acceptcb, fd, EV_READ); + w_.data = this; + ev_io_start(sessions_->get_loop(), &w_); + } + void accept_connection() { + for (;;) { +#ifdef HAVE_ACCEPT4 + auto fd = accept4(fd_, nullptr, nullptr, SOCK_NONBLOCK); +#else // !HAVE_ACCEPT4 + auto fd = accept(fd_, nullptr, nullptr); +#endif // !HAVE_ACCEPT4 + if (fd == -1) { + break; + } +#ifndef HAVE_ACCEPT4 + util::make_socket_nonblocking(fd); +#endif // !HAVE_ACCEPT4 + acceptor_->accept_connection(fd); + } + } + +private: + ev_io w_; + std::shared_ptr acceptor_; + Sessions *sessions_; + int fd_; +}; + +namespace { +void acceptcb(struct ev_loop *loop, ev_io *w, int revents) { + auto handler = static_cast(w->data); + handler->accept_connection(); +} +} // namespace + +namespace { +FileEntry make_status_body(int status, uint16_t port) { + std::string body; + body = ""; + body += http2::get_status_string(status); + body += "

    "; + body += http2::get_status_string(status); + body += "


    "; + body += NGHTTPD_SERVER; + body += " at port "; + body += util::utos(port); + body += "
    "; + body += ""; + + char tempfn[] = "/tmp/nghttpd.temp.XXXXXX"; + int fd = mkstemp(tempfn); + if (fd == -1) { + auto error = errno; + std::cerr << "Could not open status response body file: errno=" << error; + assert(0); + } + unlink(tempfn); + ssize_t nwrite; + while ((nwrite = write(fd, body.c_str(), body.size())) == -1 && + errno == EINTR) + ; + if (nwrite == -1) { + auto error = errno; + std::cerr << "Could not write status response body into file: errno=" + << error; + assert(0); + } + + return FileEntry(util::utos(status), nwrite, 0, fd); +} +} // namespace + +// index into HttpServer::status_pages_ +enum { + IDX_200, + IDX_301, + IDX_304, + IDX_400, + IDX_404, +}; + +HttpServer::HttpServer(const Config *config) : config_(config) { + status_pages_ = std::vector{ + {"200", make_status_body(200, config_->port)}, + {"301", make_status_body(301, config_->port)}, + {"304", make_status_body(304, config_->port)}, + {"400", make_status_body(400, config_->port)}, + {"404", make_status_body(404, config_->port)}, + }; +} + +namespace { +int next_proto_cb(SSL *s, const unsigned char **data, unsigned int *len, + void *arg) { + auto next_proto = static_cast *>(arg); + *data = next_proto->data(); + *len = next_proto->size(); + return SSL_TLSEXT_ERR_OK; +} +} // namespace + +namespace { +int verify_callback(int preverify_ok, X509_STORE_CTX *ctx) { + // We don't verify the client certificate. Just request it for the + // testing purpose. + return 1; +} +} // namespace + +namespace { +int start_listen(HttpServer *sv, struct ev_loop *loop, Sessions *sessions, + const Config *config) { + addrinfo hints; + int r; + bool ok = false; + const char *addr = nullptr; + + auto acceptor = std::make_shared(sv, sessions, config); + auto service = util::utos(config->port); + + memset(&hints, 0, sizeof(addrinfo)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_PASSIVE; +#ifdef AI_ADDRCONFIG + hints.ai_flags |= AI_ADDRCONFIG; +#endif // AI_ADDRCONFIG + + if (!config->address.empty()) { + addr = config->address.c_str(); + } + + addrinfo *res, *rp; + r = getaddrinfo(addr, service.c_str(), &hints, &res); + if (r != 0) { + std::cerr << "getaddrinfo() failed: " << gai_strerror(r) << std::endl; + return -1; + } + + for (rp = res; rp; rp = rp->ai_next) { + int fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); + if (fd == -1) { + continue; + } + int val = 1; + if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &val, + static_cast(sizeof(val))) == -1) { + close(fd); + continue; + } + (void)util::make_socket_nonblocking(fd); +#ifdef IPV6_V6ONLY + if (rp->ai_family == AF_INET6) { + if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &val, + static_cast(sizeof(val))) == -1) { + close(fd); + continue; + } + } +#endif // IPV6_V6ONLY + if (bind(fd, rp->ai_addr, rp->ai_addrlen) == 0 && listen(fd, 1000) == 0) { + new ListenEventHandler(sessions, fd, acceptor); + + if (config->verbose) { + std::string s = util::numeric_name(rp->ai_addr, rp->ai_addrlen); + std::cout << (rp->ai_family == AF_INET ? "IPv4" : "IPv6") << ": listen " + << s << ":" << config->port << std::endl; + } + ok = true; + continue; + } else { + std::cerr << strerror(errno) << std::endl; + } + close(fd); + } + freeaddrinfo(res); + + if (!ok) { + return -1; + } + return 0; +} +} // namespace + +#if OPENSSL_VERSION_NUMBER >= 0x10002000L +namespace { +int alpn_select_proto_cb(SSL *ssl, const unsigned char **out, + unsigned char *outlen, const unsigned char *in, + unsigned int inlen, void *arg) { + auto config = static_cast(arg)->get_config(); + if (config->verbose) { + std::cout << "[ALPN] client offers:" << std::endl; + } + if (config->verbose) { + for (unsigned int i = 0; i < inlen; i += in [i] + 1) { + std::cout << " * "; + std::cout.write(reinterpret_cast(&in[i + 1]), in[i]); + std::cout << std::endl; + } + } + if (!util::select_h2(out, outlen, in, inlen)) { + return SSL_TLSEXT_ERR_NOACK; + } + return SSL_TLSEXT_ERR_OK; +} +} // namespace +#endif // OPENSSL_VERSION_NUMBER >= 0x10002000L + +int HttpServer::run() { + SSL_CTX *ssl_ctx = nullptr; + std::vector next_proto; + + if (!config_->no_tls) { + ssl_ctx = SSL_CTX_new(SSLv23_server_method()); + if (!ssl_ctx) { + std::cerr << ERR_error_string(ERR_get_error(), nullptr) << std::endl; + return -1; + } + + SSL_CTX_set_options(ssl_ctx, + SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | + SSL_OP_NO_COMPRESSION | + SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION | + SSL_OP_SINGLE_ECDH_USE | SSL_OP_NO_TICKET | + SSL_OP_CIPHER_SERVER_PREFERENCE); + SSL_CTX_set_mode(ssl_ctx, SSL_MODE_AUTO_RETRY); + SSL_CTX_set_mode(ssl_ctx, SSL_MODE_RELEASE_BUFFERS); + + if (SSL_CTX_set_cipher_list(ssl_ctx, ssl::DEFAULT_CIPHER_LIST) == 0) { + std::cerr << ERR_error_string(ERR_get_error(), nullptr) << std::endl; + return -1; + } + + const unsigned char sid_ctx[] = "nghttpd"; + SSL_CTX_set_session_id_context(ssl_ctx, sid_ctx, sizeof(sid_ctx) - 1); + SSL_CTX_set_session_cache_mode(ssl_ctx, SSL_SESS_CACHE_SERVER); + +#ifndef OPENSSL_NO_EC + + // Disabled SSL_CTX_set_ecdh_auto, because computational cost of + // chosen curve is much higher than P-256. + + // #if OPENSSL_VERSION_NUMBER >= 0x10002000L + // SSL_CTX_set_ecdh_auto(ssl_ctx, 1); + // #else // OPENSSL_VERSION_NUBMER < 0x10002000L + // Use P-256, which is sufficiently secure at the time of this + // writing. + auto ecdh = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1); + if (ecdh == nullptr) { + std::cerr << "EC_KEY_new_by_curv_name failed: " + << ERR_error_string(ERR_get_error(), nullptr); + return -1; + } + SSL_CTX_set_tmp_ecdh(ssl_ctx, ecdh); + EC_KEY_free(ecdh); +// #endif // OPENSSL_VERSION_NUBMER < 0x10002000L + +#endif // OPENSSL_NO_EC + + if (!config_->dh_param_file.empty()) { + // Read DH parameters from file + auto bio = BIO_new_file(config_->dh_param_file.c_str(), "r"); + if (bio == nullptr) { + std::cerr << "BIO_new_file() failed: " + << ERR_error_string(ERR_get_error(), nullptr) << std::endl; + return -1; + } + + auto dh = PEM_read_bio_DHparams(bio, nullptr, nullptr, nullptr); + + if (dh == nullptr) { + std::cerr << "PEM_read_bio_DHparams() failed: " + << ERR_error_string(ERR_get_error(), nullptr) << std::endl; + return -1; + } + + SSL_CTX_set_tmp_dh(ssl_ctx, dh); + DH_free(dh); + BIO_free(bio); + } + + if (SSL_CTX_use_PrivateKey_file(ssl_ctx, config_->private_key_file.c_str(), + SSL_FILETYPE_PEM) != 1) { + std::cerr << "SSL_CTX_use_PrivateKey_file failed." << std::endl; + return -1; + } + if (SSL_CTX_use_certificate_chain_file(ssl_ctx, + config_->cert_file.c_str()) != 1) { + std::cerr << "SSL_CTX_use_certificate_file failed." << std::endl; + return -1; + } + if (SSL_CTX_check_private_key(ssl_ctx) != 1) { + std::cerr << "SSL_CTX_check_private_key failed." << std::endl; + return -1; + } + if (config_->verify_client) { + SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE | + SSL_VERIFY_FAIL_IF_NO_PEER_CERT, + verify_callback); + } + + next_proto = util::get_default_alpn(); + + SSL_CTX_set_next_protos_advertised_cb(ssl_ctx, next_proto_cb, &next_proto); +#if OPENSSL_VERSION_NUMBER >= 0x10002000L + // ALPN selection callback + SSL_CTX_set_alpn_select_cb(ssl_ctx, alpn_select_proto_cb, this); +#endif // OPENSSL_VERSION_NUMBER >= 0x10002000L + } + + auto loop = EV_DEFAULT; + + Sessions sessions(this, loop, config_, ssl_ctx); + if (start_listen(this, loop, &sessions, config_) != 0) { + std::cerr << "Could not listen" << std::endl; + return -1; + } + + ev_run(loop, 0); + return 0; +} + +const Config *HttpServer::get_config() const { return config_; } + +const StatusPage *HttpServer::get_status_page(int status) const { + switch (status) { + case 200: + return &status_pages_[IDX_200]; + case 301: + return &status_pages_[IDX_301]; + case 304: + return &status_pages_[IDX_304]; + case 400: + return &status_pages_[IDX_400]; + case 404: + return &status_pages_[IDX_404]; + default: + assert(0); + } + return nullptr; +} + +} // namespace nghttp2 diff --git a/src/HttpServer.h b/src/HttpServer.h new file mode 100644 index 0000000..212f12a --- /dev/null +++ b/src/HttpServer.h @@ -0,0 +1,202 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2013 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef HTTP_SERVER_H +#define HTTP_SERVER_H + +#include "nghttp2_config.h" + +#include + +#include +#include + +#include +#include +#include +#include + +#include + +#include + +#include + +#include "http2.h" +#include "buffer.h" + +namespace nghttp2 { + +struct Config { + std::map> push; + Headers trailer; + std::string htdocs; + std::string host; + std::string private_key_file; + std::string cert_file; + std::string dh_param_file; + std::string address; + ev_tstamp stream_read_timeout; + ev_tstamp stream_write_timeout; + void *data_ptr; + size_t padding; + size_t num_worker; + size_t max_concurrent_streams; + ssize_t header_table_size; + uint16_t port; + bool verbose; + bool daemon; + bool verify_client; + bool no_tls; + bool error_gzip; + bool early_response; + bool hexdump; + bool echo_upload; + Config(); + ~Config(); +}; + +class Http2Handler; + +struct FileEntry { + FileEntry(std::string path, int64_t length, int64_t mtime, int fd) + : path(std::move(path)), length(length), mtime(mtime), dlprev(nullptr), + dlnext(nullptr), fd(fd), usecount(1) {} + std::string path; + int64_t length; + int64_t mtime; + FileEntry *dlprev, *dlnext; + int fd; + int usecount; +}; + +struct Stream { + Headers headers; + Http2Handler *handler; + FileEntry *file_ent; + ev_timer rtimer; + ev_timer wtimer; + int64_t body_length; + int64_t body_offset; + int32_t stream_id; + http2::HeaderIndex hdidx; + bool echo_upload; + Stream(Http2Handler *handler, int32_t stream_id); + ~Stream(); +}; + +class Sessions; + +class Http2Handler { +public: + Http2Handler(Sessions *sessions, int fd, SSL *ssl, int64_t session_id); + ~Http2Handler(); + + void remove_self(); + int setup_bev(); + int on_read(); + int on_write(); + int connection_made(); + int verify_npn_result(); + + int submit_file_response(const std::string &status, Stream *stream, + time_t last_modified, off_t file_length, + nghttp2_data_provider *data_prd); + + int submit_response(const std::string &status, int32_t stream_id, + nghttp2_data_provider *data_prd); + + int submit_response(const std::string &status, int32_t stream_id, + const Headers &headers, nghttp2_data_provider *data_prd); + + int submit_non_final_response(const std::string &status, int32_t stream_id); + + int submit_push_promise(Stream *stream, const std::string &push_path); + + int submit_rst_stream(Stream *stream, uint32_t error_code); + + void add_stream(int32_t stream_id, std::unique_ptr stream); + void remove_stream(int32_t stream_id); + Stream *get_stream(int32_t stream_id); + int64_t session_id() const; + Sessions *get_sessions() const; + const Config *get_config() const; + void remove_settings_timer(); + void terminate_session(uint32_t error_code); + + int fill_wb(); + + int read_clear(); + int write_clear(); + int tls_handshake(); + int read_tls(); + int write_tls(); + + struct ev_loop *get_loop() const; + + using WriteBuf = Buffer<65536>; + + WriteBuf *get_wb(); + +private: + ev_io wev_; + ev_io rev_; + ev_timer settings_timerev_; + std::map> id2stream_; + WriteBuf wb_; + std::function read_, write_; + int64_t session_id_; + nghttp2_session *session_; + Sessions *sessions_; + SSL *ssl_; + const uint8_t *data_pending_; + size_t data_pendinglen_; + int fd_; +}; + +struct StatusPage { + std::string status; + FileEntry file_ent; +}; + +class HttpServer { +public: + HttpServer(const Config *config); + int listen(); + int run(); + const Config *get_config() const; + const StatusPage *get_status_page(int status) const; + +private: + std::vector status_pages_; + const Config *config_; +}; + +ssize_t file_read_callback(nghttp2_session *session, int32_t stream_id, + uint8_t *buf, size_t length, int *eof, + nghttp2_data_source *source, void *user_data); + +} // namespace nghttp2 + +#endif // HTTP_SERVER_H diff --git a/src/Makefile.am b/src/Makefile.am new file mode 100644 index 0000000..b1336ae --- /dev/null +++ b/src/Makefile.am @@ -0,0 +1,216 @@ +# nghttp2 - HTTP/2 C Library + +# Copyright (c) 2012 Tatsuhiro Tsujikawa + +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: + +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. + +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +SUBDIRS = includes + +bin_PROGRAMS = +check_PROGRAMS = +TESTS = + +AM_CFLAGS = $(WARNCFLAGS) +AM_CPPFLAGS = \ + -DPKGDATADIR='"$(pkgdatadir)"' \ + -Wall \ + -I$(top_srcdir)/lib/includes \ + -I$(top_builddir)/lib/includes \ + -I$(top_srcdir)/lib \ + -I$(top_srcdir)/src/includes \ + -I$(top_srcdir)/third-party \ + @LIBSPDYLAY_CFLAGS@ \ + @XML_CPPFLAGS@ \ + @LIBEV_CFLAGS@ \ + @OPENSSL_CFLAGS@ \ + @JANSSON_CFLAGS@ \ + @ZLIB_CFLAGS@ \ + @DEFS@ + +LDADD = $(top_builddir)/lib/libnghttp2.la \ + $(top_builddir)/third-party/libhttp-parser.la \ + @JEMALLOC_LIBS@ \ + @LIBSPDYLAY_LIBS@ \ + @XML_LIBS@ \ + @LIBEV_LIBS@ \ + @OPENSSL_LIBS@ \ + @JANSSON_LIBS@ \ + @ZLIB_LIBS@ \ + @APPLDFLAGS@ + +if ENABLE_APP + +bin_PROGRAMS += nghttp nghttpd nghttpx + +HELPER_OBJECTS = util.cc \ + http2.cc timegm.c app_helper.cc nghttp2_gzip.c +HELPER_HFILES = util.h \ + http2.h timegm.h app_helper.h nghttp2_config.h \ + nghttp2_gzip.h + +HTML_PARSER_OBJECTS = +HTML_PARSER_HFILES = HtmlParser.h + +if HAVE_LIBXML2 +HTML_PARSER_OBJECTS += HtmlParser.cc +endif # HAVE_LIBXML2 + +nghttp_SOURCES = ${HELPER_OBJECTS} ${HELPER_HFILES} nghttp.cc nghttp.h \ + ${HTML_PARSER_OBJECTS} ${HTML_PARSER_HFILES} \ + ssl.cc ssl.h + +nghttpd_SOURCES = ${HELPER_OBJECTS} ${HELPER_HFILES} nghttpd.cc \ + ssl.cc ssl.h \ + HttpServer.cc HttpServer.h + +bin_PROGRAMS += h2load + +h2load_SOURCES = util.cc util.h \ + http2.cc http2.h h2load.cc h2load.h \ + timegm.c timegm.h \ + ssl.cc ssl.h \ + h2load_session.h \ + h2load_http2_session.cc h2load_http2_session.h + +if HAVE_SPDYLAY +h2load_SOURCES += h2load_spdy_session.cc h2load_spdy_session.h +endif # HAVE_SPDYLAY + +NGHTTPX_SRCS = \ + util.cc util.h http2.cc http2.h timegm.c timegm.h base64.h \ + app_helper.cc app_helper.h \ + ssl.cc ssl.h \ + shrpx_config.cc shrpx_config.h \ + shrpx_error.h \ + shrpx_accept_handler.cc shrpx_accept_handler.h \ + shrpx_connection_handler.cc shrpx_connection_handler.h \ + shrpx_client_handler.cc shrpx_client_handler.h \ + shrpx_upstream.h \ + shrpx_http2_upstream.cc shrpx_http2_upstream.h \ + shrpx_https_upstream.cc shrpx_https_upstream.h \ + shrpx_downstream.cc shrpx_downstream.h \ + shrpx_downstream_connection.cc shrpx_downstream_connection.h \ + shrpx_http_downstream_connection.cc shrpx_http_downstream_connection.h \ + shrpx_http2_downstream_connection.cc shrpx_http2_downstream_connection.h \ + shrpx_http2_session.cc shrpx_http2_session.h \ + shrpx_downstream_queue.cc shrpx_downstream_queue.h \ + shrpx_log.cc shrpx_log.h \ + shrpx_http.cc shrpx_http.h \ + shrpx_io_control.cc shrpx_io_control.h \ + shrpx_ssl.cc shrpx_ssl.h \ + shrpx_worker.cc shrpx_worker.h \ + shrpx_log_config.cc shrpx_log_config.h \ + shrpx_connect_blocker.cc shrpx_connect_blocker.h \ + shrpx_downstream_connection_pool.cc shrpx_downstream_connection_pool.h \ + shrpx_rate_limit.cc shrpx_rate_limit.h \ + shrpx_connection.cc shrpx_connection.h \ + buffer.h memchunk.h template.h + +if HAVE_SPDYLAY +NGHTTPX_SRCS += shrpx_spdy_upstream.cc shrpx_spdy_upstream.h +endif # HAVE_SPDYLAY + +noinst_LIBRARIES = libnghttpx.a +libnghttpx_a_SOURCES = ${NGHTTPX_SRCS} + +nghttpx_SOURCES = shrpx.cc shrpx.h +nghttpx_LDADD = libnghttpx.a ${LDADD} + +if HAVE_CUNIT +check_PROGRAMS += nghttpx-unittest +nghttpx_unittest_SOURCES = shrpx-unittest.cc \ + shrpx_ssl_test.cc shrpx_ssl_test.h \ + shrpx_downstream_test.cc shrpx_downstream_test.h \ + shrpx_config_test.cc shrpx_config_test.h \ + http2_test.cc http2_test.h \ + util_test.cc util_test.h \ + nghttp2_gzip_test.c nghttp2_gzip_test.h \ + nghttp2_gzip.c nghttp2_gzip.h \ + buffer_test.cc buffer_test.h \ + memchunk_test.cc memchunk_test.h +nghttpx_unittest_CPPFLAGS = ${AM_CPPFLAGS}\ + -DNGHTTP2_TESTS_DIR=\"$(top_srcdir)/tests\" +nghttpx_unittest_LDADD = libnghttpx.a ${LDADD} @CUNIT_LIBS@ @TESTLDADD@ + +TESTS += nghttpx-unittest +endif # HAVE_CUNIT + +endif # ENABLE_APP + +if ENABLE_HPACK_TOOLS + +bin_PROGRAMS += inflatehd deflatehd + +HPACK_TOOLS_COMMON_SRCS = comp_helper.c comp_helper.h + +inflatehd_SOURCES = inflatehd.cc $(HPACK_TOOLS_COMMON_SRCS) + +deflatehd_SOURCES = deflatehd.cc $(HPACK_TOOLS_COMMON_SRCS) + +endif # ENABLE_HPACK_TOOLS + +if ENABLE_ASIO_LIB + +pkgconfigdir = $(libdir)/pkgconfig +pkgconfig_DATA = libnghttp2_asio.pc +DISTCLEANFILES = $(pkgconfig_DATA) + +lib_LTLIBRARIES = libnghttp2_asio.la + +libnghttp2_asio_la_SOURCES = \ + util.cc util.h http2.cc http2.h \ + ssl.cc ssl.h \ + asio_common.cc asio_common.h \ + asio_io_service_pool.cc asio_io_service_pool.h \ + asio_server_http2.cc \ + asio_server_http2_impl.cc asio_server_http2_impl.h \ + asio_server.cc asio_server.h \ + asio_server_http2_handler.cc asio_server_http2_handler.h \ + asio_server_connection.h \ + asio_server_request.cc \ + asio_server_request_impl.cc asio_server_request_impl.h \ + asio_server_response.cc \ + asio_server_response_impl.cc asio_server_response_impl.h \ + asio_server_stream.cc asio_server_stream.h \ + asio_server_serve_mux.cc asio_server_serve_mux.h \ + asio_server_request_handler.cc asio_server_request_handler.h \ + asio_server_tls_context.cc asio_server_tls_context.h \ + asio_client_session.cc \ + asio_client_session_impl.cc asio_client_session_impl.h \ + asio_client_session_tcp_impl.cc asio_client_session_tcp_impl.h \ + asio_client_session_tls_impl.cc asio_client_session_tls_impl.h \ + asio_client_response.cc \ + asio_client_response_impl.cc asio_client_response_impl.h \ + asio_client_request.cc \ + asio_client_request_impl.cc asio_client_request_impl.h \ + asio_client_stream.cc asio_client_stream.h \ + asio_client_tls_context.cc asio_client_tls_context.h + +libnghttp2_asio_la_CPPFLAGS = ${AM_CPPFLAGS} ${BOOST_CPPFLAGS} +libnghttp2_asio_la_LDFLAGS = -no-undefined -version-info 1:0:0 +libnghttp2_asio_la_LIBADD = \ + $(top_builddir)/lib/libnghttp2.la \ + $(top_builddir)/third-party/libhttp-parser.la \ + ${BOOST_LDFLAGS} \ + ${BOOST_ASIO_LIB} \ + ${BOOST_THREAD_LIB} \ + ${BOOST_SYSTEM_LIB} \ + @OPENSSL_LIBS@ + +endif # ENABLE_ASIO_LIB diff --git a/src/Makefile.in b/src/Makefile.in new file mode 100644 index 0000000..5702edb --- /dev/null +++ b/src/Makefile.in @@ -0,0 +1,2137 @@ +# Makefile.in generated by automake 1.14.1 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2013 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +# nghttp2 - HTTP/2 C Library + +# Copyright (c) 2012 Tatsuhiro Tsujikawa + +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: + +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. + + + + +VPATH = @srcdir@ +am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)' +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +target_triplet = @target@ +bin_PROGRAMS = $(am__EXEEXT_1) $(am__EXEEXT_2) +check_PROGRAMS = $(am__EXEEXT_3) +TESTS = $(am__EXEEXT_3) +@ENABLE_APP_TRUE@am__append_1 = nghttp nghttpd nghttpx h2load +@ENABLE_APP_TRUE@@HAVE_LIBXML2_TRUE@am__append_2 = HtmlParser.cc +@ENABLE_APP_TRUE@@HAVE_SPDYLAY_TRUE@am__append_3 = h2load_spdy_session.cc h2load_spdy_session.h +@ENABLE_APP_TRUE@@HAVE_SPDYLAY_TRUE@am__append_4 = shrpx_spdy_upstream.cc shrpx_spdy_upstream.h +@ENABLE_APP_TRUE@@HAVE_CUNIT_TRUE@am__append_5 = nghttpx-unittest +@ENABLE_APP_TRUE@@HAVE_CUNIT_TRUE@am__append_6 = nghttpx-unittest +@ENABLE_HPACK_TOOLS_TRUE@am__append_7 = inflatehd deflatehd +subdir = src +DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \ + $(srcdir)/libnghttp2_asio.pc.in $(top_srcdir)/depcomp \ + $(top_srcdir)/test-driver +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/ax_boost_asio.m4 \ + $(top_srcdir)/m4/ax_boost_base.m4 \ + $(top_srcdir)/m4/ax_boost_system.m4 \ + $(top_srcdir)/m4/ax_boost_thread.m4 \ + $(top_srcdir)/m4/ax_check_compile_flag.m4 \ + $(top_srcdir)/m4/ax_cxx_compile_stdcxx_11.m4 \ + $(top_srcdir)/m4/ax_have_epoll.m4 \ + $(top_srcdir)/m4/ax_python_devel.m4 \ + $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/libxml2.m4 \ + $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ + $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = libnghttp2_asio.pc +CONFIG_CLEAN_VPATH_FILES = +LIBRARIES = $(noinst_LIBRARIES) +ARFLAGS = cru +AM_V_AR = $(am__v_AR_@AM_V@) +am__v_AR_ = $(am__v_AR_@AM_DEFAULT_V@) +am__v_AR_0 = @echo " AR " $@; +am__v_AR_1 = +libnghttpx_a_AR = $(AR) $(ARFLAGS) +libnghttpx_a_LIBADD = +am__libnghttpx_a_SOURCES_DIST = util.cc util.h http2.cc http2.h \ + timegm.c timegm.h base64.h app_helper.cc app_helper.h ssl.cc \ + ssl.h shrpx_config.cc shrpx_config.h shrpx_error.h \ + shrpx_accept_handler.cc shrpx_accept_handler.h \ + shrpx_connection_handler.cc shrpx_connection_handler.h \ + shrpx_client_handler.cc shrpx_client_handler.h \ + shrpx_upstream.h shrpx_http2_upstream.cc \ + shrpx_http2_upstream.h shrpx_https_upstream.cc \ + shrpx_https_upstream.h shrpx_downstream.cc shrpx_downstream.h \ + shrpx_downstream_connection.cc shrpx_downstream_connection.h \ + shrpx_http_downstream_connection.cc \ + shrpx_http_downstream_connection.h \ + shrpx_http2_downstream_connection.cc \ + shrpx_http2_downstream_connection.h shrpx_http2_session.cc \ + shrpx_http2_session.h shrpx_downstream_queue.cc \ + shrpx_downstream_queue.h shrpx_log.cc shrpx_log.h \ + shrpx_http.cc shrpx_http.h shrpx_io_control.cc \ + shrpx_io_control.h shrpx_ssl.cc shrpx_ssl.h shrpx_worker.cc \ + shrpx_worker.h shrpx_log_config.cc shrpx_log_config.h \ + shrpx_connect_blocker.cc shrpx_connect_blocker.h \ + shrpx_downstream_connection_pool.cc \ + shrpx_downstream_connection_pool.h shrpx_rate_limit.cc \ + shrpx_rate_limit.h shrpx_connection.cc shrpx_connection.h \ + buffer.h memchunk.h template.h shrpx_spdy_upstream.cc \ + shrpx_spdy_upstream.h +@ENABLE_APP_TRUE@@HAVE_SPDYLAY_TRUE@am__objects_1 = shrpx_spdy_upstream.$(OBJEXT) +@ENABLE_APP_TRUE@am__objects_2 = util.$(OBJEXT) http2.$(OBJEXT) \ +@ENABLE_APP_TRUE@ timegm.$(OBJEXT) app_helper.$(OBJEXT) \ +@ENABLE_APP_TRUE@ ssl.$(OBJEXT) shrpx_config.$(OBJEXT) \ +@ENABLE_APP_TRUE@ shrpx_accept_handler.$(OBJEXT) \ +@ENABLE_APP_TRUE@ shrpx_connection_handler.$(OBJEXT) \ +@ENABLE_APP_TRUE@ shrpx_client_handler.$(OBJEXT) \ +@ENABLE_APP_TRUE@ shrpx_http2_upstream.$(OBJEXT) \ +@ENABLE_APP_TRUE@ shrpx_https_upstream.$(OBJEXT) \ +@ENABLE_APP_TRUE@ shrpx_downstream.$(OBJEXT) \ +@ENABLE_APP_TRUE@ shrpx_downstream_connection.$(OBJEXT) \ +@ENABLE_APP_TRUE@ shrpx_http_downstream_connection.$(OBJEXT) \ +@ENABLE_APP_TRUE@ shrpx_http2_downstream_connection.$(OBJEXT) \ +@ENABLE_APP_TRUE@ shrpx_http2_session.$(OBJEXT) \ +@ENABLE_APP_TRUE@ shrpx_downstream_queue.$(OBJEXT) \ +@ENABLE_APP_TRUE@ shrpx_log.$(OBJEXT) shrpx_http.$(OBJEXT) \ +@ENABLE_APP_TRUE@ shrpx_io_control.$(OBJEXT) \ +@ENABLE_APP_TRUE@ shrpx_ssl.$(OBJEXT) shrpx_worker.$(OBJEXT) \ +@ENABLE_APP_TRUE@ shrpx_log_config.$(OBJEXT) \ +@ENABLE_APP_TRUE@ shrpx_connect_blocker.$(OBJEXT) \ +@ENABLE_APP_TRUE@ shrpx_downstream_connection_pool.$(OBJEXT) \ +@ENABLE_APP_TRUE@ shrpx_rate_limit.$(OBJEXT) \ +@ENABLE_APP_TRUE@ shrpx_connection.$(OBJEXT) $(am__objects_1) +@ENABLE_APP_TRUE@am_libnghttpx_a_OBJECTS = $(am__objects_2) +libnghttpx_a_OBJECTS = $(am_libnghttpx_a_OBJECTS) +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +am__uninstall_files_from_dir = { \ + test -z "$$files" \ + || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ + || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ + $(am__cd) "$$dir" && rm -f $$files; }; \ + } +am__installdirs = "$(DESTDIR)$(libdir)" "$(DESTDIR)$(bindir)" \ + "$(DESTDIR)$(pkgconfigdir)" +LTLIBRARIES = $(lib_LTLIBRARIES) +am__DEPENDENCIES_1 = +@ENABLE_ASIO_LIB_TRUE@libnghttp2_asio_la_DEPENDENCIES = \ +@ENABLE_ASIO_LIB_TRUE@ $(top_builddir)/lib/libnghttp2.la \ +@ENABLE_ASIO_LIB_TRUE@ $(top_builddir)/third-party/libhttp-parser.la \ +@ENABLE_ASIO_LIB_TRUE@ $(am__DEPENDENCIES_1) \ +@ENABLE_ASIO_LIB_TRUE@ $(am__DEPENDENCIES_1) \ +@ENABLE_ASIO_LIB_TRUE@ $(am__DEPENDENCIES_1) \ +@ENABLE_ASIO_LIB_TRUE@ $(am__DEPENDENCIES_1) +am__libnghttp2_asio_la_SOURCES_DIST = util.cc util.h http2.cc http2.h \ + ssl.cc ssl.h asio_common.cc asio_common.h \ + asio_io_service_pool.cc asio_io_service_pool.h \ + asio_server_http2.cc asio_server_http2_impl.cc \ + asio_server_http2_impl.h asio_server.cc asio_server.h \ + asio_server_http2_handler.cc asio_server_http2_handler.h \ + asio_server_connection.h asio_server_request.cc \ + asio_server_request_impl.cc asio_server_request_impl.h \ + asio_server_response.cc asio_server_response_impl.cc \ + asio_server_response_impl.h asio_server_stream.cc \ + asio_server_stream.h asio_server_serve_mux.cc \ + asio_server_serve_mux.h asio_server_request_handler.cc \ + asio_server_request_handler.h asio_server_tls_context.cc \ + asio_server_tls_context.h asio_client_session.cc \ + asio_client_session_impl.cc asio_client_session_impl.h \ + asio_client_session_tcp_impl.cc asio_client_session_tcp_impl.h \ + asio_client_session_tls_impl.cc asio_client_session_tls_impl.h \ + asio_client_response.cc asio_client_response_impl.cc \ + asio_client_response_impl.h asio_client_request.cc \ + asio_client_request_impl.cc asio_client_request_impl.h \ + asio_client_stream.cc asio_client_stream.h \ + asio_client_tls_context.cc asio_client_tls_context.h +@ENABLE_ASIO_LIB_TRUE@am_libnghttp2_asio_la_OBJECTS = \ +@ENABLE_ASIO_LIB_TRUE@ libnghttp2_asio_la-util.lo \ +@ENABLE_ASIO_LIB_TRUE@ libnghttp2_asio_la-http2.lo \ +@ENABLE_ASIO_LIB_TRUE@ libnghttp2_asio_la-ssl.lo \ +@ENABLE_ASIO_LIB_TRUE@ libnghttp2_asio_la-asio_common.lo \ +@ENABLE_ASIO_LIB_TRUE@ libnghttp2_asio_la-asio_io_service_pool.lo \ +@ENABLE_ASIO_LIB_TRUE@ libnghttp2_asio_la-asio_server_http2.lo \ +@ENABLE_ASIO_LIB_TRUE@ libnghttp2_asio_la-asio_server_http2_impl.lo \ +@ENABLE_ASIO_LIB_TRUE@ libnghttp2_asio_la-asio_server.lo \ +@ENABLE_ASIO_LIB_TRUE@ libnghttp2_asio_la-asio_server_http2_handler.lo \ +@ENABLE_ASIO_LIB_TRUE@ libnghttp2_asio_la-asio_server_request.lo \ +@ENABLE_ASIO_LIB_TRUE@ libnghttp2_asio_la-asio_server_request_impl.lo \ +@ENABLE_ASIO_LIB_TRUE@ libnghttp2_asio_la-asio_server_response.lo \ +@ENABLE_ASIO_LIB_TRUE@ libnghttp2_asio_la-asio_server_response_impl.lo \ +@ENABLE_ASIO_LIB_TRUE@ libnghttp2_asio_la-asio_server_stream.lo \ +@ENABLE_ASIO_LIB_TRUE@ libnghttp2_asio_la-asio_server_serve_mux.lo \ +@ENABLE_ASIO_LIB_TRUE@ libnghttp2_asio_la-asio_server_request_handler.lo \ +@ENABLE_ASIO_LIB_TRUE@ libnghttp2_asio_la-asio_server_tls_context.lo \ +@ENABLE_ASIO_LIB_TRUE@ libnghttp2_asio_la-asio_client_session.lo \ +@ENABLE_ASIO_LIB_TRUE@ libnghttp2_asio_la-asio_client_session_impl.lo \ +@ENABLE_ASIO_LIB_TRUE@ libnghttp2_asio_la-asio_client_session_tcp_impl.lo \ +@ENABLE_ASIO_LIB_TRUE@ libnghttp2_asio_la-asio_client_session_tls_impl.lo \ +@ENABLE_ASIO_LIB_TRUE@ libnghttp2_asio_la-asio_client_response.lo \ +@ENABLE_ASIO_LIB_TRUE@ libnghttp2_asio_la-asio_client_response_impl.lo \ +@ENABLE_ASIO_LIB_TRUE@ libnghttp2_asio_la-asio_client_request.lo \ +@ENABLE_ASIO_LIB_TRUE@ libnghttp2_asio_la-asio_client_request_impl.lo \ +@ENABLE_ASIO_LIB_TRUE@ libnghttp2_asio_la-asio_client_stream.lo \ +@ENABLE_ASIO_LIB_TRUE@ libnghttp2_asio_la-asio_client_tls_context.lo +libnghttp2_asio_la_OBJECTS = $(am_libnghttp2_asio_la_OBJECTS) +AM_V_lt = $(am__v_lt_@AM_V@) +am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) +am__v_lt_0 = --silent +am__v_lt_1 = +libnghttp2_asio_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CXXLD) \ + $(AM_CXXFLAGS) $(CXXFLAGS) $(libnghttp2_asio_la_LDFLAGS) \ + $(LDFLAGS) -o $@ +@ENABLE_ASIO_LIB_TRUE@am_libnghttp2_asio_la_rpath = -rpath $(libdir) +@ENABLE_APP_TRUE@am__EXEEXT_1 = nghttp$(EXEEXT) nghttpd$(EXEEXT) \ +@ENABLE_APP_TRUE@ nghttpx$(EXEEXT) h2load$(EXEEXT) +@ENABLE_HPACK_TOOLS_TRUE@am__EXEEXT_2 = inflatehd$(EXEEXT) \ +@ENABLE_HPACK_TOOLS_TRUE@ deflatehd$(EXEEXT) +@ENABLE_APP_TRUE@@HAVE_CUNIT_TRUE@am__EXEEXT_3 = \ +@ENABLE_APP_TRUE@@HAVE_CUNIT_TRUE@ nghttpx-unittest$(EXEEXT) +PROGRAMS = $(bin_PROGRAMS) +am__deflatehd_SOURCES_DIST = deflatehd.cc comp_helper.c comp_helper.h +@ENABLE_HPACK_TOOLS_TRUE@am__objects_3 = comp_helper.$(OBJEXT) +@ENABLE_HPACK_TOOLS_TRUE@am_deflatehd_OBJECTS = deflatehd.$(OBJEXT) \ +@ENABLE_HPACK_TOOLS_TRUE@ $(am__objects_3) +deflatehd_OBJECTS = $(am_deflatehd_OBJECTS) +deflatehd_LDADD = $(LDADD) +deflatehd_DEPENDENCIES = $(top_builddir)/lib/libnghttp2.la \ + $(top_builddir)/third-party/libhttp-parser.la +am__h2load_SOURCES_DIST = util.cc util.h http2.cc http2.h h2load.cc \ + h2load.h timegm.c timegm.h ssl.cc ssl.h h2load_session.h \ + h2load_http2_session.cc h2load_http2_session.h \ + h2load_spdy_session.cc h2load_spdy_session.h +@ENABLE_APP_TRUE@@HAVE_SPDYLAY_TRUE@am__objects_4 = h2load_spdy_session.$(OBJEXT) +@ENABLE_APP_TRUE@am_h2load_OBJECTS = util.$(OBJEXT) http2.$(OBJEXT) \ +@ENABLE_APP_TRUE@ h2load.$(OBJEXT) timegm.$(OBJEXT) \ +@ENABLE_APP_TRUE@ ssl.$(OBJEXT) h2load_http2_session.$(OBJEXT) \ +@ENABLE_APP_TRUE@ $(am__objects_4) +h2load_OBJECTS = $(am_h2load_OBJECTS) +h2load_LDADD = $(LDADD) +h2load_DEPENDENCIES = $(top_builddir)/lib/libnghttp2.la \ + $(top_builddir)/third-party/libhttp-parser.la +am__inflatehd_SOURCES_DIST = inflatehd.cc comp_helper.c comp_helper.h +@ENABLE_HPACK_TOOLS_TRUE@am_inflatehd_OBJECTS = inflatehd.$(OBJEXT) \ +@ENABLE_HPACK_TOOLS_TRUE@ $(am__objects_3) +inflatehd_OBJECTS = $(am_inflatehd_OBJECTS) +inflatehd_LDADD = $(LDADD) +inflatehd_DEPENDENCIES = $(top_builddir)/lib/libnghttp2.la \ + $(top_builddir)/third-party/libhttp-parser.la +am__nghttp_SOURCES_DIST = util.cc http2.cc timegm.c app_helper.cc \ + nghttp2_gzip.c util.h http2.h timegm.h app_helper.h \ + nghttp2_config.h nghttp2_gzip.h nghttp.cc nghttp.h \ + HtmlParser.cc HtmlParser.h ssl.cc ssl.h +@ENABLE_APP_TRUE@am__objects_5 = util.$(OBJEXT) http2.$(OBJEXT) \ +@ENABLE_APP_TRUE@ timegm.$(OBJEXT) app_helper.$(OBJEXT) \ +@ENABLE_APP_TRUE@ nghttp2_gzip.$(OBJEXT) +am__objects_6 = +@ENABLE_APP_TRUE@@HAVE_LIBXML2_TRUE@am__objects_7 = \ +@ENABLE_APP_TRUE@@HAVE_LIBXML2_TRUE@ HtmlParser.$(OBJEXT) +@ENABLE_APP_TRUE@am__objects_8 = $(am__objects_7) +@ENABLE_APP_TRUE@am_nghttp_OBJECTS = $(am__objects_5) $(am__objects_6) \ +@ENABLE_APP_TRUE@ nghttp.$(OBJEXT) $(am__objects_8) \ +@ENABLE_APP_TRUE@ $(am__objects_6) ssl.$(OBJEXT) +nghttp_OBJECTS = $(am_nghttp_OBJECTS) +nghttp_LDADD = $(LDADD) +nghttp_DEPENDENCIES = $(top_builddir)/lib/libnghttp2.la \ + $(top_builddir)/third-party/libhttp-parser.la +am__nghttpd_SOURCES_DIST = util.cc http2.cc timegm.c app_helper.cc \ + nghttp2_gzip.c util.h http2.h timegm.h app_helper.h \ + nghttp2_config.h nghttp2_gzip.h nghttpd.cc ssl.cc ssl.h \ + HttpServer.cc HttpServer.h +@ENABLE_APP_TRUE@am_nghttpd_OBJECTS = $(am__objects_5) \ +@ENABLE_APP_TRUE@ $(am__objects_6) nghttpd.$(OBJEXT) \ +@ENABLE_APP_TRUE@ ssl.$(OBJEXT) HttpServer.$(OBJEXT) +nghttpd_OBJECTS = $(am_nghttpd_OBJECTS) +nghttpd_LDADD = $(LDADD) +nghttpd_DEPENDENCIES = $(top_builddir)/lib/libnghttp2.la \ + $(top_builddir)/third-party/libhttp-parser.la +am__nghttpx_SOURCES_DIST = shrpx.cc shrpx.h +@ENABLE_APP_TRUE@am_nghttpx_OBJECTS = shrpx.$(OBJEXT) +nghttpx_OBJECTS = $(am_nghttpx_OBJECTS) +am__DEPENDENCIES_2 = $(top_builddir)/lib/libnghttp2.la \ + $(top_builddir)/third-party/libhttp-parser.la +@ENABLE_APP_TRUE@nghttpx_DEPENDENCIES = libnghttpx.a \ +@ENABLE_APP_TRUE@ $(am__DEPENDENCIES_2) +am__nghttpx_unittest_SOURCES_DIST = shrpx-unittest.cc \ + shrpx_ssl_test.cc shrpx_ssl_test.h shrpx_downstream_test.cc \ + shrpx_downstream_test.h shrpx_config_test.cc \ + shrpx_config_test.h http2_test.cc http2_test.h util_test.cc \ + util_test.h nghttp2_gzip_test.c nghttp2_gzip_test.h \ + nghttp2_gzip.c nghttp2_gzip.h buffer_test.cc buffer_test.h \ + memchunk_test.cc memchunk_test.h +@ENABLE_APP_TRUE@@HAVE_CUNIT_TRUE@am_nghttpx_unittest_OBJECTS = nghttpx_unittest-shrpx-unittest.$(OBJEXT) \ +@ENABLE_APP_TRUE@@HAVE_CUNIT_TRUE@ nghttpx_unittest-shrpx_ssl_test.$(OBJEXT) \ +@ENABLE_APP_TRUE@@HAVE_CUNIT_TRUE@ nghttpx_unittest-shrpx_downstream_test.$(OBJEXT) \ +@ENABLE_APP_TRUE@@HAVE_CUNIT_TRUE@ nghttpx_unittest-shrpx_config_test.$(OBJEXT) \ +@ENABLE_APP_TRUE@@HAVE_CUNIT_TRUE@ nghttpx_unittest-http2_test.$(OBJEXT) \ +@ENABLE_APP_TRUE@@HAVE_CUNIT_TRUE@ nghttpx_unittest-util_test.$(OBJEXT) \ +@ENABLE_APP_TRUE@@HAVE_CUNIT_TRUE@ nghttpx_unittest-nghttp2_gzip_test.$(OBJEXT) \ +@ENABLE_APP_TRUE@@HAVE_CUNIT_TRUE@ nghttpx_unittest-nghttp2_gzip.$(OBJEXT) \ +@ENABLE_APP_TRUE@@HAVE_CUNIT_TRUE@ nghttpx_unittest-buffer_test.$(OBJEXT) \ +@ENABLE_APP_TRUE@@HAVE_CUNIT_TRUE@ nghttpx_unittest-memchunk_test.$(OBJEXT) +nghttpx_unittest_OBJECTS = $(am_nghttpx_unittest_OBJECTS) +@ENABLE_APP_TRUE@@HAVE_CUNIT_TRUE@nghttpx_unittest_DEPENDENCIES = \ +@ENABLE_APP_TRUE@@HAVE_CUNIT_TRUE@ libnghttpx.a \ +@ENABLE_APP_TRUE@@HAVE_CUNIT_TRUE@ $(am__DEPENDENCIES_2) +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__depfiles_maybe = depfiles +am__mv = mv -f +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CFLAGS) $(CFLAGS) +AM_V_CC = $(am__v_CC_@AM_V@) +am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) +am__v_CC_0 = @echo " CC " $@; +am__v_CC_1 = +CCLD = $(CC) +LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CCLD = $(am__v_CCLD_@AM_V@) +am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) +am__v_CCLD_0 = @echo " CCLD " $@; +am__v_CCLD_1 = +CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) +LTCXXCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CXXFLAGS) $(CXXFLAGS) +AM_V_CXX = $(am__v_CXX_@AM_V@) +am__v_CXX_ = $(am__v_CXX_@AM_DEFAULT_V@) +am__v_CXX_0 = @echo " CXX " $@; +am__v_CXX_1 = +CXXLD = $(CXX) +CXXLINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CXXLD) $(AM_CXXFLAGS) \ + $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CXXLD = $(am__v_CXXLD_@AM_V@) +am__v_CXXLD_ = $(am__v_CXXLD_@AM_DEFAULT_V@) +am__v_CXXLD_0 = @echo " CXXLD " $@; +am__v_CXXLD_1 = +SOURCES = $(libnghttpx_a_SOURCES) $(libnghttp2_asio_la_SOURCES) \ + $(deflatehd_SOURCES) $(h2load_SOURCES) $(inflatehd_SOURCES) \ + $(nghttp_SOURCES) $(nghttpd_SOURCES) $(nghttpx_SOURCES) \ + $(nghttpx_unittest_SOURCES) +DIST_SOURCES = $(am__libnghttpx_a_SOURCES_DIST) \ + $(am__libnghttp2_asio_la_SOURCES_DIST) \ + $(am__deflatehd_SOURCES_DIST) $(am__h2load_SOURCES_DIST) \ + $(am__inflatehd_SOURCES_DIST) $(am__nghttp_SOURCES_DIST) \ + $(am__nghttpd_SOURCES_DIST) $(am__nghttpx_SOURCES_DIST) \ + $(am__nghttpx_unittest_SOURCES_DIST) +RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \ + ctags-recursive dvi-recursive html-recursive info-recursive \ + install-data-recursive install-dvi-recursive \ + install-exec-recursive install-html-recursive \ + install-info-recursive install-pdf-recursive \ + install-ps-recursive install-recursive installcheck-recursive \ + installdirs-recursive pdf-recursive ps-recursive \ + tags-recursive uninstall-recursive +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +DATA = $(pkgconfig_DATA) +RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \ + distclean-recursive maintainer-clean-recursive +am__recursive_targets = \ + $(RECURSIVE_TARGETS) \ + $(RECURSIVE_CLEAN_TARGETS) \ + $(am__extra_recursive_targets) +AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \ + check recheck distdir +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +ETAGS = etags +CTAGS = ctags +am__tty_colors_dummy = \ + mgn= red= grn= lgn= blu= brg= std=; \ + am__color_tests=no +am__tty_colors = { \ + $(am__tty_colors_dummy); \ + if test "X$(AM_COLOR_TESTS)" = Xno; then \ + am__color_tests=no; \ + elif test "X$(AM_COLOR_TESTS)" = Xalways; then \ + am__color_tests=yes; \ + elif test "X$$TERM" != Xdumb && { test -t 1; } 2>/dev/null; then \ + am__color_tests=yes; \ + fi; \ + if test $$am__color_tests = yes; then \ + red=''; \ + grn=''; \ + lgn=''; \ + blu=''; \ + mgn=''; \ + brg=''; \ + std=''; \ + fi; \ +} +am__recheck_rx = ^[ ]*:recheck:[ ]* +am__global_test_result_rx = ^[ ]*:global-test-result:[ ]* +am__copy_in_global_log_rx = ^[ ]*:copy-in-global-log:[ ]* +# A command that, given a newline-separated list of test names on the +# standard input, print the name of the tests that are to be re-run +# upon "make recheck". +am__list_recheck_tests = $(AWK) '{ \ + recheck = 1; \ + while ((rc = (getline line < ($$0 ".trs"))) != 0) \ + { \ + if (rc < 0) \ + { \ + if ((getline line2 < ($$0 ".log")) < 0) \ + recheck = 0; \ + break; \ + } \ + else if (line ~ /$(am__recheck_rx)[nN][Oo]/) \ + { \ + recheck = 0; \ + break; \ + } \ + else if (line ~ /$(am__recheck_rx)[yY][eE][sS]/) \ + { \ + break; \ + } \ + }; \ + if (recheck) \ + print $$0; \ + close ($$0 ".trs"); \ + close ($$0 ".log"); \ +}' +# A command that, given a newline-separated list of test names on the +# standard input, create the global log from their .trs and .log files. +am__create_global_log = $(AWK) ' \ +function fatal(msg) \ +{ \ + print "fatal: making $@: " msg | "cat >&2"; \ + exit 1; \ +} \ +function rst_section(header) \ +{ \ + print header; \ + len = length(header); \ + for (i = 1; i <= len; i = i + 1) \ + printf "="; \ + printf "\n\n"; \ +} \ +{ \ + copy_in_global_log = 1; \ + global_test_result = "RUN"; \ + while ((rc = (getline line < ($$0 ".trs"))) != 0) \ + { \ + if (rc < 0) \ + fatal("failed to read from " $$0 ".trs"); \ + if (line ~ /$(am__global_test_result_rx)/) \ + { \ + sub("$(am__global_test_result_rx)", "", line); \ + sub("[ ]*$$", "", line); \ + global_test_result = line; \ + } \ + else if (line ~ /$(am__copy_in_global_log_rx)[nN][oO]/) \ + copy_in_global_log = 0; \ + }; \ + if (copy_in_global_log) \ + { \ + rst_section(global_test_result ": " $$0); \ + while ((rc = (getline line < ($$0 ".log"))) != 0) \ + { \ + if (rc < 0) \ + fatal("failed to read from " $$0 ".log"); \ + print line; \ + }; \ + printf "\n"; \ + }; \ + close ($$0 ".trs"); \ + close ($$0 ".log"); \ +}' +# Restructured Text title. +am__rst_title = { sed 's/.*/ & /;h;s/./=/g;p;x;s/ *$$//;p;g' && echo; } +# Solaris 10 'make', and several other traditional 'make' implementations, +# pass "-e" to $(SHELL), and POSIX 2008 even requires this. Work around it +# by disabling -e (using the XSI extension "set +e") if it's set. +am__sh_e_setup = case $$- in *e*) set +e;; esac +# Default flags passed to test drivers. +am__common_driver_flags = \ + --color-tests "$$am__color_tests" \ + --enable-hard-errors "$$am__enable_hard_errors" \ + --expect-failure "$$am__expect_failure" +# To be inserted before the command running the test. Creates the +# directory for the log if needed. Stores in $dir the directory +# containing $f, in $tst the test, in $log the log. Executes the +# developer- defined test setup AM_TESTS_ENVIRONMENT (if any), and +# passes TESTS_ENVIRONMENT. Set up options for the wrapper that +# will run the test scripts (or their associated LOG_COMPILER, if +# thy have one). +am__check_pre = \ +$(am__sh_e_setup); \ +$(am__vpath_adj_setup) $(am__vpath_adj) \ +$(am__tty_colors); \ +srcdir=$(srcdir); export srcdir; \ +case "$@" in \ + */*) am__odir=`echo "./$@" | sed 's|/[^/]*$$||'`;; \ + *) am__odir=.;; \ +esac; \ +test "x$$am__odir" = x"." || test -d "$$am__odir" \ + || $(MKDIR_P) "$$am__odir" || exit $$?; \ +if test -f "./$$f"; then dir=./; \ +elif test -f "$$f"; then dir=; \ +else dir="$(srcdir)/"; fi; \ +tst=$$dir$$f; log='$@'; \ +if test -n '$(DISABLE_HARD_ERRORS)'; then \ + am__enable_hard_errors=no; \ +else \ + am__enable_hard_errors=yes; \ +fi; \ +case " $(XFAIL_TESTS) " in \ + *[\ \ ]$$f[\ \ ]* | *[\ \ ]$$dir$$f[\ \ ]*) \ + am__expect_failure=yes;; \ + *) \ + am__expect_failure=no;; \ +esac; \ +$(AM_TESTS_ENVIRONMENT) $(TESTS_ENVIRONMENT) +# A shell command to get the names of the tests scripts with any registered +# extension removed (i.e., equivalently, the names of the test logs, with +# the '.log' extension removed). The result is saved in the shell variable +# '$bases'. This honors runtime overriding of TESTS and TEST_LOGS. Sadly, +# we cannot use something simpler, involving e.g., "$(TEST_LOGS:.log=)", +# since that might cause problem with VPATH rewrites for suffix-less tests. +# See also 'test-harness-vpath-rewrite.sh' and 'test-trs-basic.sh'. +am__set_TESTS_bases = \ + bases='$(TEST_LOGS)'; \ + bases=`for i in $$bases; do echo $$i; done | sed 's/\.log$$//'`; \ + bases=`echo $$bases` +RECHECK_LOGS = $(TEST_LOGS) +TEST_SUITE_LOG = test-suite.log +TEST_EXTENSIONS = @EXEEXT@ .test +LOG_DRIVER = $(SHELL) $(top_srcdir)/test-driver +LOG_COMPILE = $(LOG_COMPILER) $(AM_LOG_FLAGS) $(LOG_FLAGS) +am__set_b = \ + case '$@' in \ + */*) \ + case '$*' in \ + */*) b='$*';; \ + *) b=`echo '$@' | sed 's/\.log$$//'`; \ + esac;; \ + *) \ + b='$*';; \ + esac +am__test_logs1 = $(TESTS:=.log) +am__test_logs2 = $(am__test_logs1:@EXEEXT@.log=.log) +TEST_LOGS = $(am__test_logs2:.test.log=.log) +TEST_LOG_DRIVER = $(SHELL) $(top_srcdir)/test-driver +TEST_LOG_COMPILE = $(TEST_LOG_COMPILER) $(AM_TEST_LOG_FLAGS) \ + $(TEST_LOG_FLAGS) +DIST_SUBDIRS = $(SUBDIRS) +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +am__relativize = \ + dir0=`pwd`; \ + sed_first='s,^\([^/]*\)/.*$$,\1,'; \ + sed_rest='s,^[^/]*/*,,'; \ + sed_last='s,^.*/\([^/]*\)$$,\1,'; \ + sed_butlast='s,/*[^/]*$$,,'; \ + while test -n "$$dir1"; do \ + first=`echo "$$dir1" | sed -e "$$sed_first"`; \ + if test "$$first" != "."; then \ + if test "$$first" = ".."; then \ + dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \ + dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \ + else \ + first2=`echo "$$dir2" | sed -e "$$sed_first"`; \ + if test "$$first2" = "$$first"; then \ + dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \ + else \ + dir2="../$$dir2"; \ + fi; \ + dir0="$$dir0"/"$$first"; \ + fi; \ + fi; \ + dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \ + done; \ + reldir="$$dir2" +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +APPLDFLAGS = @APPLDFLAGS@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +BOOST_ASIO_LIB = @BOOST_ASIO_LIB@ +BOOST_CPPFLAGS = @BOOST_CPPFLAGS@ +BOOST_LDFLAGS = @BOOST_LDFLAGS@ +BOOST_SYSTEM_LIB = @BOOST_SYSTEM_LIB@ +BOOST_THREAD_LIB = @BOOST_THREAD_LIB@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CUNIT_CFLAGS = @CUNIT_CFLAGS@ +CUNIT_LIBS = @CUNIT_LIBS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +CYTHON = @CYTHON@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DLLTOOL = @DLLTOOL@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +GREP = @GREP@ +HAVE_CXX11 = @HAVE_CXX11@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +JANSSON_CFLAGS = @JANSSON_CFLAGS@ +JANSSON_LIBS = @JANSSON_LIBS@ +JEMALLOC_LIBS = @JEMALLOC_LIBS@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBEVENT_OPENSSL_CFLAGS = @LIBEVENT_OPENSSL_CFLAGS@ +LIBEVENT_OPENSSL_LIBS = @LIBEVENT_OPENSSL_LIBS@ +LIBEV_CFLAGS = @LIBEV_CFLAGS@ +LIBEV_LIBS = @LIBEV_LIBS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBSPDYLAY_CFLAGS = @LIBSPDYLAY_CFLAGS@ +LIBSPDYLAY_LIBS = @LIBSPDYLAY_LIBS@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +LT_AGE = @LT_AGE@ +LT_CURRENT = @LT_CURRENT@ +LT_REVISION = @LT_REVISION@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MKDIR_P = @MKDIR_P@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OPENSSL_CFLAGS = @OPENSSL_CFLAGS@ +OPENSSL_LIBS = @OPENSSL_LIBS@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PACKAGE_VERSION_NUM = @PACKAGE_VERSION_NUM@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PKG_CONFIG = @PKG_CONFIG@ +PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ +PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ +PYTHON = @PYTHON@ +PYTHON_CPPFLAGS = @PYTHON_CPPFLAGS@ +PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@ +PYTHON_EXTRA_LDFLAGS = @PYTHON_EXTRA_LDFLAGS@ +PYTHON_EXTRA_LIBS = @PYTHON_EXTRA_LIBS@ +PYTHON_LDFLAGS = @PYTHON_LDFLAGS@ +PYTHON_PLATFORM = @PYTHON_PLATFORM@ +PYTHON_PREFIX = @PYTHON_PREFIX@ +PYTHON_SITE_PKG = @PYTHON_SITE_PKG@ +PYTHON_VERSION = @PYTHON_VERSION@ +RANLIB = @RANLIB@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +TESTLDADD = @TESTLDADD@ +VERSION = @VERSION@ +WARNCFLAGS = @WARNCFLAGS@ +XML2_CONFIG = @XML2_CONFIG@ +XML_CPPFLAGS = @XML_CPPFLAGS@ +XML_LIBS = @XML_LIBS@ +ZLIB_CFLAGS = @ZLIB_CFLAGS@ +ZLIB_LIBS = @ZLIB_LIBS@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +pkgpyexecdir = @pkgpyexecdir@ +pkgpythondir = @pkgpythondir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +pyexecdir = @pyexecdir@ +pythondir = @pythondir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target = @target@ +target_alias = @target_alias@ +target_cpu = @target_cpu@ +target_os = @target_os@ +target_vendor = @target_vendor@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ + +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +SUBDIRS = includes +AM_CFLAGS = $(WARNCFLAGS) +AM_CPPFLAGS = \ + -DPKGDATADIR='"$(pkgdatadir)"' \ + -Wall \ + -I$(top_srcdir)/lib/includes \ + -I$(top_builddir)/lib/includes \ + -I$(top_srcdir)/lib \ + -I$(top_srcdir)/src/includes \ + -I$(top_srcdir)/third-party \ + @LIBSPDYLAY_CFLAGS@ \ + @XML_CPPFLAGS@ \ + @LIBEV_CFLAGS@ \ + @OPENSSL_CFLAGS@ \ + @JANSSON_CFLAGS@ \ + @ZLIB_CFLAGS@ \ + @DEFS@ + +LDADD = $(top_builddir)/lib/libnghttp2.la \ + $(top_builddir)/third-party/libhttp-parser.la \ + @JEMALLOC_LIBS@ \ + @LIBSPDYLAY_LIBS@ \ + @XML_LIBS@ \ + @LIBEV_LIBS@ \ + @OPENSSL_LIBS@ \ + @JANSSON_LIBS@ \ + @ZLIB_LIBS@ \ + @APPLDFLAGS@ + +@ENABLE_APP_TRUE@HELPER_OBJECTS = util.cc \ +@ENABLE_APP_TRUE@ http2.cc timegm.c app_helper.cc nghttp2_gzip.c + +@ENABLE_APP_TRUE@HELPER_HFILES = util.h \ +@ENABLE_APP_TRUE@ http2.h timegm.h app_helper.h nghttp2_config.h \ +@ENABLE_APP_TRUE@ nghttp2_gzip.h + +@ENABLE_APP_TRUE@HTML_PARSER_OBJECTS = $(am__append_2) +@ENABLE_APP_TRUE@HTML_PARSER_HFILES = HtmlParser.h +@ENABLE_APP_TRUE@nghttp_SOURCES = ${HELPER_OBJECTS} ${HELPER_HFILES} nghttp.cc nghttp.h \ +@ENABLE_APP_TRUE@ ${HTML_PARSER_OBJECTS} ${HTML_PARSER_HFILES} \ +@ENABLE_APP_TRUE@ ssl.cc ssl.h + +@ENABLE_APP_TRUE@nghttpd_SOURCES = ${HELPER_OBJECTS} ${HELPER_HFILES} nghttpd.cc \ +@ENABLE_APP_TRUE@ ssl.cc ssl.h \ +@ENABLE_APP_TRUE@ HttpServer.cc HttpServer.h + +@ENABLE_APP_TRUE@h2load_SOURCES = util.cc util.h http2.cc http2.h \ +@ENABLE_APP_TRUE@ h2load.cc h2load.h timegm.c timegm.h ssl.cc \ +@ENABLE_APP_TRUE@ ssl.h h2load_session.h \ +@ENABLE_APP_TRUE@ h2load_http2_session.cc \ +@ENABLE_APP_TRUE@ h2load_http2_session.h $(am__append_3) +@ENABLE_APP_TRUE@NGHTTPX_SRCS = util.cc util.h http2.cc http2.h \ +@ENABLE_APP_TRUE@ timegm.c timegm.h base64.h app_helper.cc \ +@ENABLE_APP_TRUE@ app_helper.h ssl.cc ssl.h shrpx_config.cc \ +@ENABLE_APP_TRUE@ shrpx_config.h shrpx_error.h \ +@ENABLE_APP_TRUE@ shrpx_accept_handler.cc \ +@ENABLE_APP_TRUE@ shrpx_accept_handler.h \ +@ENABLE_APP_TRUE@ shrpx_connection_handler.cc \ +@ENABLE_APP_TRUE@ shrpx_connection_handler.h \ +@ENABLE_APP_TRUE@ shrpx_client_handler.cc \ +@ENABLE_APP_TRUE@ shrpx_client_handler.h shrpx_upstream.h \ +@ENABLE_APP_TRUE@ shrpx_http2_upstream.cc \ +@ENABLE_APP_TRUE@ shrpx_http2_upstream.h \ +@ENABLE_APP_TRUE@ shrpx_https_upstream.cc \ +@ENABLE_APP_TRUE@ shrpx_https_upstream.h shrpx_downstream.cc \ +@ENABLE_APP_TRUE@ shrpx_downstream.h \ +@ENABLE_APP_TRUE@ shrpx_downstream_connection.cc \ +@ENABLE_APP_TRUE@ shrpx_downstream_connection.h \ +@ENABLE_APP_TRUE@ shrpx_http_downstream_connection.cc \ +@ENABLE_APP_TRUE@ shrpx_http_downstream_connection.h \ +@ENABLE_APP_TRUE@ shrpx_http2_downstream_connection.cc \ +@ENABLE_APP_TRUE@ shrpx_http2_downstream_connection.h \ +@ENABLE_APP_TRUE@ shrpx_http2_session.cc shrpx_http2_session.h \ +@ENABLE_APP_TRUE@ shrpx_downstream_queue.cc \ +@ENABLE_APP_TRUE@ shrpx_downstream_queue.h shrpx_log.cc \ +@ENABLE_APP_TRUE@ shrpx_log.h shrpx_http.cc shrpx_http.h \ +@ENABLE_APP_TRUE@ shrpx_io_control.cc shrpx_io_control.h \ +@ENABLE_APP_TRUE@ shrpx_ssl.cc shrpx_ssl.h shrpx_worker.cc \ +@ENABLE_APP_TRUE@ shrpx_worker.h shrpx_log_config.cc \ +@ENABLE_APP_TRUE@ shrpx_log_config.h shrpx_connect_blocker.cc \ +@ENABLE_APP_TRUE@ shrpx_connect_blocker.h \ +@ENABLE_APP_TRUE@ shrpx_downstream_connection_pool.cc \ +@ENABLE_APP_TRUE@ shrpx_downstream_connection_pool.h \ +@ENABLE_APP_TRUE@ shrpx_rate_limit.cc shrpx_rate_limit.h \ +@ENABLE_APP_TRUE@ shrpx_connection.cc shrpx_connection.h \ +@ENABLE_APP_TRUE@ buffer.h memchunk.h template.h \ +@ENABLE_APP_TRUE@ $(am__append_4) +@ENABLE_APP_TRUE@noinst_LIBRARIES = libnghttpx.a +@ENABLE_APP_TRUE@libnghttpx_a_SOURCES = ${NGHTTPX_SRCS} +@ENABLE_APP_TRUE@nghttpx_SOURCES = shrpx.cc shrpx.h +@ENABLE_APP_TRUE@nghttpx_LDADD = libnghttpx.a ${LDADD} +@ENABLE_APP_TRUE@@HAVE_CUNIT_TRUE@nghttpx_unittest_SOURCES = shrpx-unittest.cc \ +@ENABLE_APP_TRUE@@HAVE_CUNIT_TRUE@ shrpx_ssl_test.cc shrpx_ssl_test.h \ +@ENABLE_APP_TRUE@@HAVE_CUNIT_TRUE@ shrpx_downstream_test.cc shrpx_downstream_test.h \ +@ENABLE_APP_TRUE@@HAVE_CUNIT_TRUE@ shrpx_config_test.cc shrpx_config_test.h \ +@ENABLE_APP_TRUE@@HAVE_CUNIT_TRUE@ http2_test.cc http2_test.h \ +@ENABLE_APP_TRUE@@HAVE_CUNIT_TRUE@ util_test.cc util_test.h \ +@ENABLE_APP_TRUE@@HAVE_CUNIT_TRUE@ nghttp2_gzip_test.c nghttp2_gzip_test.h \ +@ENABLE_APP_TRUE@@HAVE_CUNIT_TRUE@ nghttp2_gzip.c nghttp2_gzip.h \ +@ENABLE_APP_TRUE@@HAVE_CUNIT_TRUE@ buffer_test.cc buffer_test.h \ +@ENABLE_APP_TRUE@@HAVE_CUNIT_TRUE@ memchunk_test.cc memchunk_test.h + +@ENABLE_APP_TRUE@@HAVE_CUNIT_TRUE@nghttpx_unittest_CPPFLAGS = ${AM_CPPFLAGS}\ +@ENABLE_APP_TRUE@@HAVE_CUNIT_TRUE@ -DNGHTTP2_TESTS_DIR=\"$(top_srcdir)/tests\" + +@ENABLE_APP_TRUE@@HAVE_CUNIT_TRUE@nghttpx_unittest_LDADD = libnghttpx.a ${LDADD} @CUNIT_LIBS@ @TESTLDADD@ +@ENABLE_HPACK_TOOLS_TRUE@HPACK_TOOLS_COMMON_SRCS = comp_helper.c comp_helper.h +@ENABLE_HPACK_TOOLS_TRUE@inflatehd_SOURCES = inflatehd.cc $(HPACK_TOOLS_COMMON_SRCS) +@ENABLE_HPACK_TOOLS_TRUE@deflatehd_SOURCES = deflatehd.cc $(HPACK_TOOLS_COMMON_SRCS) +@ENABLE_ASIO_LIB_TRUE@pkgconfigdir = $(libdir)/pkgconfig +@ENABLE_ASIO_LIB_TRUE@pkgconfig_DATA = libnghttp2_asio.pc +@ENABLE_ASIO_LIB_TRUE@DISTCLEANFILES = $(pkgconfig_DATA) +@ENABLE_ASIO_LIB_TRUE@lib_LTLIBRARIES = libnghttp2_asio.la +@ENABLE_ASIO_LIB_TRUE@libnghttp2_asio_la_SOURCES = \ +@ENABLE_ASIO_LIB_TRUE@ util.cc util.h http2.cc http2.h \ +@ENABLE_ASIO_LIB_TRUE@ ssl.cc ssl.h \ +@ENABLE_ASIO_LIB_TRUE@ asio_common.cc asio_common.h \ +@ENABLE_ASIO_LIB_TRUE@ asio_io_service_pool.cc asio_io_service_pool.h \ +@ENABLE_ASIO_LIB_TRUE@ asio_server_http2.cc \ +@ENABLE_ASIO_LIB_TRUE@ asio_server_http2_impl.cc asio_server_http2_impl.h \ +@ENABLE_ASIO_LIB_TRUE@ asio_server.cc asio_server.h \ +@ENABLE_ASIO_LIB_TRUE@ asio_server_http2_handler.cc asio_server_http2_handler.h \ +@ENABLE_ASIO_LIB_TRUE@ asio_server_connection.h \ +@ENABLE_ASIO_LIB_TRUE@ asio_server_request.cc \ +@ENABLE_ASIO_LIB_TRUE@ asio_server_request_impl.cc asio_server_request_impl.h \ +@ENABLE_ASIO_LIB_TRUE@ asio_server_response.cc \ +@ENABLE_ASIO_LIB_TRUE@ asio_server_response_impl.cc asio_server_response_impl.h \ +@ENABLE_ASIO_LIB_TRUE@ asio_server_stream.cc asio_server_stream.h \ +@ENABLE_ASIO_LIB_TRUE@ asio_server_serve_mux.cc asio_server_serve_mux.h \ +@ENABLE_ASIO_LIB_TRUE@ asio_server_request_handler.cc asio_server_request_handler.h \ +@ENABLE_ASIO_LIB_TRUE@ asio_server_tls_context.cc asio_server_tls_context.h \ +@ENABLE_ASIO_LIB_TRUE@ asio_client_session.cc \ +@ENABLE_ASIO_LIB_TRUE@ asio_client_session_impl.cc asio_client_session_impl.h \ +@ENABLE_ASIO_LIB_TRUE@ asio_client_session_tcp_impl.cc asio_client_session_tcp_impl.h \ +@ENABLE_ASIO_LIB_TRUE@ asio_client_session_tls_impl.cc asio_client_session_tls_impl.h \ +@ENABLE_ASIO_LIB_TRUE@ asio_client_response.cc \ +@ENABLE_ASIO_LIB_TRUE@ asio_client_response_impl.cc asio_client_response_impl.h \ +@ENABLE_ASIO_LIB_TRUE@ asio_client_request.cc \ +@ENABLE_ASIO_LIB_TRUE@ asio_client_request_impl.cc asio_client_request_impl.h \ +@ENABLE_ASIO_LIB_TRUE@ asio_client_stream.cc asio_client_stream.h \ +@ENABLE_ASIO_LIB_TRUE@ asio_client_tls_context.cc asio_client_tls_context.h + +@ENABLE_ASIO_LIB_TRUE@libnghttp2_asio_la_CPPFLAGS = ${AM_CPPFLAGS} ${BOOST_CPPFLAGS} +@ENABLE_ASIO_LIB_TRUE@libnghttp2_asio_la_LDFLAGS = -no-undefined -version-info 1:0:0 +@ENABLE_ASIO_LIB_TRUE@libnghttp2_asio_la_LIBADD = \ +@ENABLE_ASIO_LIB_TRUE@ $(top_builddir)/lib/libnghttp2.la \ +@ENABLE_ASIO_LIB_TRUE@ $(top_builddir)/third-party/libhttp-parser.la \ +@ENABLE_ASIO_LIB_TRUE@ ${BOOST_LDFLAGS} \ +@ENABLE_ASIO_LIB_TRUE@ ${BOOST_ASIO_LIB} \ +@ENABLE_ASIO_LIB_TRUE@ ${BOOST_THREAD_LIB} \ +@ENABLE_ASIO_LIB_TRUE@ ${BOOST_SYSTEM_LIB} \ +@ENABLE_ASIO_LIB_TRUE@ @OPENSSL_LIBS@ + +all: all-recursive + +.SUFFIXES: +.SUFFIXES: .c .cc .lo .log .o .obj .test .test$(EXEEXT) .trs +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu src/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): +libnghttp2_asio.pc: $(top_builddir)/config.status $(srcdir)/libnghttp2_asio.pc.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ + +clean-noinstLIBRARIES: + -test -z "$(noinst_LIBRARIES)" || rm -f $(noinst_LIBRARIES) + +libnghttpx.a: $(libnghttpx_a_OBJECTS) $(libnghttpx_a_DEPENDENCIES) $(EXTRA_libnghttpx_a_DEPENDENCIES) + $(AM_V_at)-rm -f libnghttpx.a + $(AM_V_AR)$(libnghttpx_a_AR) libnghttpx.a $(libnghttpx_a_OBJECTS) $(libnghttpx_a_LIBADD) + $(AM_V_at)$(RANLIB) libnghttpx.a + +install-libLTLIBRARIES: $(lib_LTLIBRARIES) + @$(NORMAL_INSTALL) + @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \ + list2=; for p in $$list; do \ + if test -f $$p; then \ + list2="$$list2 $$p"; \ + else :; fi; \ + done; \ + test -z "$$list2" || { \ + echo " $(MKDIR_P) '$(DESTDIR)$(libdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(libdir)" || exit 1; \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(libdir)'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(libdir)"; \ + } + +uninstall-libLTLIBRARIES: + @$(NORMAL_UNINSTALL) + @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \ + for p in $$list; do \ + $(am__strip_dir) \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$f'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$f"; \ + done + +clean-libLTLIBRARIES: + -test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES) + @list='$(lib_LTLIBRARIES)'; \ + locs=`for p in $$list; do echo $$p; done | \ + sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ + sort -u`; \ + test -z "$$locs" || { \ + echo rm -f $${locs}; \ + rm -f $${locs}; \ + } + +libnghttp2_asio.la: $(libnghttp2_asio_la_OBJECTS) $(libnghttp2_asio_la_DEPENDENCIES) $(EXTRA_libnghttp2_asio_la_DEPENDENCIES) + $(AM_V_CXXLD)$(libnghttp2_asio_la_LINK) $(am_libnghttp2_asio_la_rpath) $(libnghttp2_asio_la_OBJECTS) $(libnghttp2_asio_la_LIBADD) $(LIBS) +install-binPROGRAMS: $(bin_PROGRAMS) + @$(NORMAL_INSTALL) + @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(bindir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(bindir)" || exit 1; \ + fi; \ + for p in $$list; do echo "$$p $$p"; done | \ + sed 's/$(EXEEXT)$$//' | \ + while read p p1; do if test -f $$p \ + || test -f $$p1 \ + ; then echo "$$p"; echo "$$p"; else :; fi; \ + done | \ + sed -e 'p;s,.*/,,;n;h' \ + -e 's|.*|.|' \ + -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \ + sed 'N;N;N;s,\n, ,g' | \ + $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \ + { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ + if ($$2 == $$4) files[d] = files[d] " " $$1; \ + else { print "f", $$3 "/" $$4, $$1; } } \ + END { for (d in files) print "f", d, files[d] }' | \ + while read type dir files; do \ + if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ + test -z "$$files" || { \ + echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(bindir)$$dir'"; \ + $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(bindir)$$dir" || exit $$?; \ + } \ + ; done + +uninstall-binPROGRAMS: + @$(NORMAL_UNINSTALL) + @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \ + files=`for p in $$list; do echo "$$p"; done | \ + sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \ + -e 's/$$/$(EXEEXT)/' \ + `; \ + test -n "$$list" || exit 0; \ + echo " ( cd '$(DESTDIR)$(bindir)' && rm -f" $$files ")"; \ + cd "$(DESTDIR)$(bindir)" && rm -f $$files + +clean-binPROGRAMS: + @list='$(bin_PROGRAMS)'; test -n "$$list" || exit 0; \ + echo " rm -f" $$list; \ + rm -f $$list || exit $$?; \ + test -n "$(EXEEXT)" || exit 0; \ + list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ + echo " rm -f" $$list; \ + rm -f $$list + +clean-checkPROGRAMS: + @list='$(check_PROGRAMS)'; test -n "$$list" || exit 0; \ + echo " rm -f" $$list; \ + rm -f $$list || exit $$?; \ + test -n "$(EXEEXT)" || exit 0; \ + list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ + echo " rm -f" $$list; \ + rm -f $$list + +deflatehd$(EXEEXT): $(deflatehd_OBJECTS) $(deflatehd_DEPENDENCIES) $(EXTRA_deflatehd_DEPENDENCIES) + @rm -f deflatehd$(EXEEXT) + $(AM_V_CXXLD)$(CXXLINK) $(deflatehd_OBJECTS) $(deflatehd_LDADD) $(LIBS) + +h2load$(EXEEXT): $(h2load_OBJECTS) $(h2load_DEPENDENCIES) $(EXTRA_h2load_DEPENDENCIES) + @rm -f h2load$(EXEEXT) + $(AM_V_CXXLD)$(CXXLINK) $(h2load_OBJECTS) $(h2load_LDADD) $(LIBS) + +inflatehd$(EXEEXT): $(inflatehd_OBJECTS) $(inflatehd_DEPENDENCIES) $(EXTRA_inflatehd_DEPENDENCIES) + @rm -f inflatehd$(EXEEXT) + $(AM_V_CXXLD)$(CXXLINK) $(inflatehd_OBJECTS) $(inflatehd_LDADD) $(LIBS) + +nghttp$(EXEEXT): $(nghttp_OBJECTS) $(nghttp_DEPENDENCIES) $(EXTRA_nghttp_DEPENDENCIES) + @rm -f nghttp$(EXEEXT) + $(AM_V_CXXLD)$(CXXLINK) $(nghttp_OBJECTS) $(nghttp_LDADD) $(LIBS) + +nghttpd$(EXEEXT): $(nghttpd_OBJECTS) $(nghttpd_DEPENDENCIES) $(EXTRA_nghttpd_DEPENDENCIES) + @rm -f nghttpd$(EXEEXT) + $(AM_V_CXXLD)$(CXXLINK) $(nghttpd_OBJECTS) $(nghttpd_LDADD) $(LIBS) + +nghttpx$(EXEEXT): $(nghttpx_OBJECTS) $(nghttpx_DEPENDENCIES) $(EXTRA_nghttpx_DEPENDENCIES) + @rm -f nghttpx$(EXEEXT) + $(AM_V_CXXLD)$(CXXLINK) $(nghttpx_OBJECTS) $(nghttpx_LDADD) $(LIBS) + +nghttpx-unittest$(EXEEXT): $(nghttpx_unittest_OBJECTS) $(nghttpx_unittest_DEPENDENCIES) $(EXTRA_nghttpx_unittest_DEPENDENCIES) + @rm -f nghttpx-unittest$(EXEEXT) + $(AM_V_CXXLD)$(CXXLINK) $(nghttpx_unittest_OBJECTS) $(nghttpx_unittest_LDADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/HtmlParser.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/HttpServer.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/app_helper.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/comp_helper.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/deflatehd.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/h2load.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/h2load_http2_session.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/h2load_spdy_session.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/http2.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/inflatehd.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttp2_asio_la-asio_client_request.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttp2_asio_la-asio_client_request_impl.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttp2_asio_la-asio_client_response.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttp2_asio_la-asio_client_response_impl.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttp2_asio_la-asio_client_session.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttp2_asio_la-asio_client_session_impl.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttp2_asio_la-asio_client_session_tcp_impl.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttp2_asio_la-asio_client_session_tls_impl.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttp2_asio_la-asio_client_stream.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttp2_asio_la-asio_client_tls_context.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttp2_asio_la-asio_common.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttp2_asio_la-asio_io_service_pool.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttp2_asio_la-asio_server.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttp2_asio_la-asio_server_http2.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttp2_asio_la-asio_server_http2_handler.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttp2_asio_la-asio_server_http2_impl.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttp2_asio_la-asio_server_request.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttp2_asio_la-asio_server_request_handler.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttp2_asio_la-asio_server_request_impl.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttp2_asio_la-asio_server_response.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttp2_asio_la-asio_server_response_impl.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttp2_asio_la-asio_server_serve_mux.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttp2_asio_la-asio_server_stream.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttp2_asio_la-asio_server_tls_context.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttp2_asio_la-http2.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttp2_asio_la-ssl.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttp2_asio_la-util.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_gzip.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttpd.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttpx_unittest-buffer_test.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttpx_unittest-http2_test.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttpx_unittest-memchunk_test.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttpx_unittest-nghttp2_gzip.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttpx_unittest-nghttp2_gzip_test.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttpx_unittest-shrpx-unittest.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttpx_unittest-shrpx_config_test.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttpx_unittest-shrpx_downstream_test.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttpx_unittest-shrpx_ssl_test.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttpx_unittest-util_test.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/shrpx.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/shrpx_accept_handler.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/shrpx_client_handler.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/shrpx_config.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/shrpx_connect_blocker.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/shrpx_connection.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/shrpx_connection_handler.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/shrpx_downstream.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/shrpx_downstream_connection.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/shrpx_downstream_connection_pool.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/shrpx_downstream_queue.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/shrpx_http.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/shrpx_http2_downstream_connection.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/shrpx_http2_session.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/shrpx_http2_upstream.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/shrpx_http_downstream_connection.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/shrpx_https_upstream.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/shrpx_io_control.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/shrpx_log.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/shrpx_log_config.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/shrpx_rate_limit.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/shrpx_spdy_upstream.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/shrpx_ssl.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/shrpx_worker.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ssl.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/timegm.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/util.Po@am__quote@ + +.c.o: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\ +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< + +.c.obj: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\ +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.c.lo: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\ +@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< + +nghttpx_unittest-nghttp2_gzip_test.o: nghttp2_gzip_test.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT nghttpx_unittest-nghttp2_gzip_test.o -MD -MP -MF $(DEPDIR)/nghttpx_unittest-nghttp2_gzip_test.Tpo -c -o nghttpx_unittest-nghttp2_gzip_test.o `test -f 'nghttp2_gzip_test.c' || echo '$(srcdir)/'`nghttp2_gzip_test.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nghttpx_unittest-nghttp2_gzip_test.Tpo $(DEPDIR)/nghttpx_unittest-nghttp2_gzip_test.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='nghttp2_gzip_test.c' object='nghttpx_unittest-nghttp2_gzip_test.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o nghttpx_unittest-nghttp2_gzip_test.o `test -f 'nghttp2_gzip_test.c' || echo '$(srcdir)/'`nghttp2_gzip_test.c + +nghttpx_unittest-nghttp2_gzip_test.obj: nghttp2_gzip_test.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT nghttpx_unittest-nghttp2_gzip_test.obj -MD -MP -MF $(DEPDIR)/nghttpx_unittest-nghttp2_gzip_test.Tpo -c -o nghttpx_unittest-nghttp2_gzip_test.obj `if test -f 'nghttp2_gzip_test.c'; then $(CYGPATH_W) 'nghttp2_gzip_test.c'; else $(CYGPATH_W) '$(srcdir)/nghttp2_gzip_test.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nghttpx_unittest-nghttp2_gzip_test.Tpo $(DEPDIR)/nghttpx_unittest-nghttp2_gzip_test.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='nghttp2_gzip_test.c' object='nghttpx_unittest-nghttp2_gzip_test.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o nghttpx_unittest-nghttp2_gzip_test.obj `if test -f 'nghttp2_gzip_test.c'; then $(CYGPATH_W) 'nghttp2_gzip_test.c'; else $(CYGPATH_W) '$(srcdir)/nghttp2_gzip_test.c'; fi` + +nghttpx_unittest-nghttp2_gzip.o: nghttp2_gzip.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT nghttpx_unittest-nghttp2_gzip.o -MD -MP -MF $(DEPDIR)/nghttpx_unittest-nghttp2_gzip.Tpo -c -o nghttpx_unittest-nghttp2_gzip.o `test -f 'nghttp2_gzip.c' || echo '$(srcdir)/'`nghttp2_gzip.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nghttpx_unittest-nghttp2_gzip.Tpo $(DEPDIR)/nghttpx_unittest-nghttp2_gzip.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='nghttp2_gzip.c' object='nghttpx_unittest-nghttp2_gzip.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o nghttpx_unittest-nghttp2_gzip.o `test -f 'nghttp2_gzip.c' || echo '$(srcdir)/'`nghttp2_gzip.c + +nghttpx_unittest-nghttp2_gzip.obj: nghttp2_gzip.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT nghttpx_unittest-nghttp2_gzip.obj -MD -MP -MF $(DEPDIR)/nghttpx_unittest-nghttp2_gzip.Tpo -c -o nghttpx_unittest-nghttp2_gzip.obj `if test -f 'nghttp2_gzip.c'; then $(CYGPATH_W) 'nghttp2_gzip.c'; else $(CYGPATH_W) '$(srcdir)/nghttp2_gzip.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nghttpx_unittest-nghttp2_gzip.Tpo $(DEPDIR)/nghttpx_unittest-nghttp2_gzip.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='nghttp2_gzip.c' object='nghttpx_unittest-nghttp2_gzip.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o nghttpx_unittest-nghttp2_gzip.obj `if test -f 'nghttp2_gzip.c'; then $(CYGPATH_W) 'nghttp2_gzip.c'; else $(CYGPATH_W) '$(srcdir)/nghttp2_gzip.c'; fi` + +.cc.o: +@am__fastdepCXX_TRUE@ $(AM_V_CXX)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\ +@am__fastdepCXX_TRUE@ $(CXXCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCXX_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ $< + +.cc.obj: +@am__fastdepCXX_TRUE@ $(AM_V_CXX)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\ +@am__fastdepCXX_TRUE@ $(CXXCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\ +@am__fastdepCXX_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.cc.lo: +@am__fastdepCXX_TRUE@ $(AM_V_CXX)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\ +@am__fastdepCXX_TRUE@ $(LTCXXCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCXX_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LTCXXCOMPILE) -c -o $@ $< + +libnghttp2_asio_la-util.lo: util.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttp2_asio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttp2_asio_la-util.lo -MD -MP -MF $(DEPDIR)/libnghttp2_asio_la-util.Tpo -c -o libnghttp2_asio_la-util.lo `test -f 'util.cc' || echo '$(srcdir)/'`util.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttp2_asio_la-util.Tpo $(DEPDIR)/libnghttp2_asio_la-util.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='util.cc' object='libnghttp2_asio_la-util.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttp2_asio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttp2_asio_la-util.lo `test -f 'util.cc' || echo '$(srcdir)/'`util.cc + +libnghttp2_asio_la-http2.lo: http2.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttp2_asio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttp2_asio_la-http2.lo -MD -MP -MF $(DEPDIR)/libnghttp2_asio_la-http2.Tpo -c -o libnghttp2_asio_la-http2.lo `test -f 'http2.cc' || echo '$(srcdir)/'`http2.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttp2_asio_la-http2.Tpo $(DEPDIR)/libnghttp2_asio_la-http2.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='http2.cc' object='libnghttp2_asio_la-http2.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttp2_asio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttp2_asio_la-http2.lo `test -f 'http2.cc' || echo '$(srcdir)/'`http2.cc + +libnghttp2_asio_la-ssl.lo: ssl.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttp2_asio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttp2_asio_la-ssl.lo -MD -MP -MF $(DEPDIR)/libnghttp2_asio_la-ssl.Tpo -c -o libnghttp2_asio_la-ssl.lo `test -f 'ssl.cc' || echo '$(srcdir)/'`ssl.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttp2_asio_la-ssl.Tpo $(DEPDIR)/libnghttp2_asio_la-ssl.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='ssl.cc' object='libnghttp2_asio_la-ssl.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttp2_asio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttp2_asio_la-ssl.lo `test -f 'ssl.cc' || echo '$(srcdir)/'`ssl.cc + +libnghttp2_asio_la-asio_common.lo: asio_common.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttp2_asio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttp2_asio_la-asio_common.lo -MD -MP -MF $(DEPDIR)/libnghttp2_asio_la-asio_common.Tpo -c -o libnghttp2_asio_la-asio_common.lo `test -f 'asio_common.cc' || echo '$(srcdir)/'`asio_common.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttp2_asio_la-asio_common.Tpo $(DEPDIR)/libnghttp2_asio_la-asio_common.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='asio_common.cc' object='libnghttp2_asio_la-asio_common.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttp2_asio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttp2_asio_la-asio_common.lo `test -f 'asio_common.cc' || echo '$(srcdir)/'`asio_common.cc + +libnghttp2_asio_la-asio_io_service_pool.lo: asio_io_service_pool.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttp2_asio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttp2_asio_la-asio_io_service_pool.lo -MD -MP -MF $(DEPDIR)/libnghttp2_asio_la-asio_io_service_pool.Tpo -c -o libnghttp2_asio_la-asio_io_service_pool.lo `test -f 'asio_io_service_pool.cc' || echo '$(srcdir)/'`asio_io_service_pool.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttp2_asio_la-asio_io_service_pool.Tpo $(DEPDIR)/libnghttp2_asio_la-asio_io_service_pool.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='asio_io_service_pool.cc' object='libnghttp2_asio_la-asio_io_service_pool.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttp2_asio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttp2_asio_la-asio_io_service_pool.lo `test -f 'asio_io_service_pool.cc' || echo '$(srcdir)/'`asio_io_service_pool.cc + +libnghttp2_asio_la-asio_server_http2.lo: asio_server_http2.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttp2_asio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttp2_asio_la-asio_server_http2.lo -MD -MP -MF $(DEPDIR)/libnghttp2_asio_la-asio_server_http2.Tpo -c -o libnghttp2_asio_la-asio_server_http2.lo `test -f 'asio_server_http2.cc' || echo '$(srcdir)/'`asio_server_http2.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttp2_asio_la-asio_server_http2.Tpo $(DEPDIR)/libnghttp2_asio_la-asio_server_http2.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='asio_server_http2.cc' object='libnghttp2_asio_la-asio_server_http2.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttp2_asio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttp2_asio_la-asio_server_http2.lo `test -f 'asio_server_http2.cc' || echo '$(srcdir)/'`asio_server_http2.cc + +libnghttp2_asio_la-asio_server_http2_impl.lo: asio_server_http2_impl.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttp2_asio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttp2_asio_la-asio_server_http2_impl.lo -MD -MP -MF $(DEPDIR)/libnghttp2_asio_la-asio_server_http2_impl.Tpo -c -o libnghttp2_asio_la-asio_server_http2_impl.lo `test -f 'asio_server_http2_impl.cc' || echo '$(srcdir)/'`asio_server_http2_impl.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttp2_asio_la-asio_server_http2_impl.Tpo $(DEPDIR)/libnghttp2_asio_la-asio_server_http2_impl.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='asio_server_http2_impl.cc' object='libnghttp2_asio_la-asio_server_http2_impl.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttp2_asio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttp2_asio_la-asio_server_http2_impl.lo `test -f 'asio_server_http2_impl.cc' || echo '$(srcdir)/'`asio_server_http2_impl.cc + +libnghttp2_asio_la-asio_server.lo: asio_server.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttp2_asio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttp2_asio_la-asio_server.lo -MD -MP -MF $(DEPDIR)/libnghttp2_asio_la-asio_server.Tpo -c -o libnghttp2_asio_la-asio_server.lo `test -f 'asio_server.cc' || echo '$(srcdir)/'`asio_server.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttp2_asio_la-asio_server.Tpo $(DEPDIR)/libnghttp2_asio_la-asio_server.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='asio_server.cc' object='libnghttp2_asio_la-asio_server.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttp2_asio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttp2_asio_la-asio_server.lo `test -f 'asio_server.cc' || echo '$(srcdir)/'`asio_server.cc + +libnghttp2_asio_la-asio_server_http2_handler.lo: asio_server_http2_handler.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttp2_asio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttp2_asio_la-asio_server_http2_handler.lo -MD -MP -MF $(DEPDIR)/libnghttp2_asio_la-asio_server_http2_handler.Tpo -c -o libnghttp2_asio_la-asio_server_http2_handler.lo `test -f 'asio_server_http2_handler.cc' || echo '$(srcdir)/'`asio_server_http2_handler.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttp2_asio_la-asio_server_http2_handler.Tpo $(DEPDIR)/libnghttp2_asio_la-asio_server_http2_handler.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='asio_server_http2_handler.cc' object='libnghttp2_asio_la-asio_server_http2_handler.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttp2_asio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttp2_asio_la-asio_server_http2_handler.lo `test -f 'asio_server_http2_handler.cc' || echo '$(srcdir)/'`asio_server_http2_handler.cc + +libnghttp2_asio_la-asio_server_request.lo: asio_server_request.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttp2_asio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttp2_asio_la-asio_server_request.lo -MD -MP -MF $(DEPDIR)/libnghttp2_asio_la-asio_server_request.Tpo -c -o libnghttp2_asio_la-asio_server_request.lo `test -f 'asio_server_request.cc' || echo '$(srcdir)/'`asio_server_request.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttp2_asio_la-asio_server_request.Tpo $(DEPDIR)/libnghttp2_asio_la-asio_server_request.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='asio_server_request.cc' object='libnghttp2_asio_la-asio_server_request.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttp2_asio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttp2_asio_la-asio_server_request.lo `test -f 'asio_server_request.cc' || echo '$(srcdir)/'`asio_server_request.cc + +libnghttp2_asio_la-asio_server_request_impl.lo: asio_server_request_impl.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttp2_asio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttp2_asio_la-asio_server_request_impl.lo -MD -MP -MF $(DEPDIR)/libnghttp2_asio_la-asio_server_request_impl.Tpo -c -o libnghttp2_asio_la-asio_server_request_impl.lo `test -f 'asio_server_request_impl.cc' || echo '$(srcdir)/'`asio_server_request_impl.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttp2_asio_la-asio_server_request_impl.Tpo $(DEPDIR)/libnghttp2_asio_la-asio_server_request_impl.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='asio_server_request_impl.cc' object='libnghttp2_asio_la-asio_server_request_impl.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttp2_asio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttp2_asio_la-asio_server_request_impl.lo `test -f 'asio_server_request_impl.cc' || echo '$(srcdir)/'`asio_server_request_impl.cc + +libnghttp2_asio_la-asio_server_response.lo: asio_server_response.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttp2_asio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttp2_asio_la-asio_server_response.lo -MD -MP -MF $(DEPDIR)/libnghttp2_asio_la-asio_server_response.Tpo -c -o libnghttp2_asio_la-asio_server_response.lo `test -f 'asio_server_response.cc' || echo '$(srcdir)/'`asio_server_response.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttp2_asio_la-asio_server_response.Tpo $(DEPDIR)/libnghttp2_asio_la-asio_server_response.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='asio_server_response.cc' object='libnghttp2_asio_la-asio_server_response.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttp2_asio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttp2_asio_la-asio_server_response.lo `test -f 'asio_server_response.cc' || echo '$(srcdir)/'`asio_server_response.cc + +libnghttp2_asio_la-asio_server_response_impl.lo: asio_server_response_impl.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttp2_asio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttp2_asio_la-asio_server_response_impl.lo -MD -MP -MF $(DEPDIR)/libnghttp2_asio_la-asio_server_response_impl.Tpo -c -o libnghttp2_asio_la-asio_server_response_impl.lo `test -f 'asio_server_response_impl.cc' || echo '$(srcdir)/'`asio_server_response_impl.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttp2_asio_la-asio_server_response_impl.Tpo $(DEPDIR)/libnghttp2_asio_la-asio_server_response_impl.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='asio_server_response_impl.cc' object='libnghttp2_asio_la-asio_server_response_impl.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttp2_asio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttp2_asio_la-asio_server_response_impl.lo `test -f 'asio_server_response_impl.cc' || echo '$(srcdir)/'`asio_server_response_impl.cc + +libnghttp2_asio_la-asio_server_stream.lo: asio_server_stream.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttp2_asio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttp2_asio_la-asio_server_stream.lo -MD -MP -MF $(DEPDIR)/libnghttp2_asio_la-asio_server_stream.Tpo -c -o libnghttp2_asio_la-asio_server_stream.lo `test -f 'asio_server_stream.cc' || echo '$(srcdir)/'`asio_server_stream.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttp2_asio_la-asio_server_stream.Tpo $(DEPDIR)/libnghttp2_asio_la-asio_server_stream.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='asio_server_stream.cc' object='libnghttp2_asio_la-asio_server_stream.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttp2_asio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttp2_asio_la-asio_server_stream.lo `test -f 'asio_server_stream.cc' || echo '$(srcdir)/'`asio_server_stream.cc + +libnghttp2_asio_la-asio_server_serve_mux.lo: asio_server_serve_mux.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttp2_asio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttp2_asio_la-asio_server_serve_mux.lo -MD -MP -MF $(DEPDIR)/libnghttp2_asio_la-asio_server_serve_mux.Tpo -c -o libnghttp2_asio_la-asio_server_serve_mux.lo `test -f 'asio_server_serve_mux.cc' || echo '$(srcdir)/'`asio_server_serve_mux.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttp2_asio_la-asio_server_serve_mux.Tpo $(DEPDIR)/libnghttp2_asio_la-asio_server_serve_mux.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='asio_server_serve_mux.cc' object='libnghttp2_asio_la-asio_server_serve_mux.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttp2_asio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttp2_asio_la-asio_server_serve_mux.lo `test -f 'asio_server_serve_mux.cc' || echo '$(srcdir)/'`asio_server_serve_mux.cc + +libnghttp2_asio_la-asio_server_request_handler.lo: asio_server_request_handler.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttp2_asio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttp2_asio_la-asio_server_request_handler.lo -MD -MP -MF $(DEPDIR)/libnghttp2_asio_la-asio_server_request_handler.Tpo -c -o libnghttp2_asio_la-asio_server_request_handler.lo `test -f 'asio_server_request_handler.cc' || echo '$(srcdir)/'`asio_server_request_handler.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttp2_asio_la-asio_server_request_handler.Tpo $(DEPDIR)/libnghttp2_asio_la-asio_server_request_handler.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='asio_server_request_handler.cc' object='libnghttp2_asio_la-asio_server_request_handler.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttp2_asio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttp2_asio_la-asio_server_request_handler.lo `test -f 'asio_server_request_handler.cc' || echo '$(srcdir)/'`asio_server_request_handler.cc + +libnghttp2_asio_la-asio_server_tls_context.lo: asio_server_tls_context.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttp2_asio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttp2_asio_la-asio_server_tls_context.lo -MD -MP -MF $(DEPDIR)/libnghttp2_asio_la-asio_server_tls_context.Tpo -c -o libnghttp2_asio_la-asio_server_tls_context.lo `test -f 'asio_server_tls_context.cc' || echo '$(srcdir)/'`asio_server_tls_context.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttp2_asio_la-asio_server_tls_context.Tpo $(DEPDIR)/libnghttp2_asio_la-asio_server_tls_context.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='asio_server_tls_context.cc' object='libnghttp2_asio_la-asio_server_tls_context.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttp2_asio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttp2_asio_la-asio_server_tls_context.lo `test -f 'asio_server_tls_context.cc' || echo '$(srcdir)/'`asio_server_tls_context.cc + +libnghttp2_asio_la-asio_client_session.lo: asio_client_session.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttp2_asio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttp2_asio_la-asio_client_session.lo -MD -MP -MF $(DEPDIR)/libnghttp2_asio_la-asio_client_session.Tpo -c -o libnghttp2_asio_la-asio_client_session.lo `test -f 'asio_client_session.cc' || echo '$(srcdir)/'`asio_client_session.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttp2_asio_la-asio_client_session.Tpo $(DEPDIR)/libnghttp2_asio_la-asio_client_session.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='asio_client_session.cc' object='libnghttp2_asio_la-asio_client_session.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttp2_asio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttp2_asio_la-asio_client_session.lo `test -f 'asio_client_session.cc' || echo '$(srcdir)/'`asio_client_session.cc + +libnghttp2_asio_la-asio_client_session_impl.lo: asio_client_session_impl.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttp2_asio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttp2_asio_la-asio_client_session_impl.lo -MD -MP -MF $(DEPDIR)/libnghttp2_asio_la-asio_client_session_impl.Tpo -c -o libnghttp2_asio_la-asio_client_session_impl.lo `test -f 'asio_client_session_impl.cc' || echo '$(srcdir)/'`asio_client_session_impl.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttp2_asio_la-asio_client_session_impl.Tpo $(DEPDIR)/libnghttp2_asio_la-asio_client_session_impl.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='asio_client_session_impl.cc' object='libnghttp2_asio_la-asio_client_session_impl.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttp2_asio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttp2_asio_la-asio_client_session_impl.lo `test -f 'asio_client_session_impl.cc' || echo '$(srcdir)/'`asio_client_session_impl.cc + +libnghttp2_asio_la-asio_client_session_tcp_impl.lo: asio_client_session_tcp_impl.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttp2_asio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttp2_asio_la-asio_client_session_tcp_impl.lo -MD -MP -MF $(DEPDIR)/libnghttp2_asio_la-asio_client_session_tcp_impl.Tpo -c -o libnghttp2_asio_la-asio_client_session_tcp_impl.lo `test -f 'asio_client_session_tcp_impl.cc' || echo '$(srcdir)/'`asio_client_session_tcp_impl.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttp2_asio_la-asio_client_session_tcp_impl.Tpo $(DEPDIR)/libnghttp2_asio_la-asio_client_session_tcp_impl.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='asio_client_session_tcp_impl.cc' object='libnghttp2_asio_la-asio_client_session_tcp_impl.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttp2_asio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttp2_asio_la-asio_client_session_tcp_impl.lo `test -f 'asio_client_session_tcp_impl.cc' || echo '$(srcdir)/'`asio_client_session_tcp_impl.cc + +libnghttp2_asio_la-asio_client_session_tls_impl.lo: asio_client_session_tls_impl.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttp2_asio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttp2_asio_la-asio_client_session_tls_impl.lo -MD -MP -MF $(DEPDIR)/libnghttp2_asio_la-asio_client_session_tls_impl.Tpo -c -o libnghttp2_asio_la-asio_client_session_tls_impl.lo `test -f 'asio_client_session_tls_impl.cc' || echo '$(srcdir)/'`asio_client_session_tls_impl.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttp2_asio_la-asio_client_session_tls_impl.Tpo $(DEPDIR)/libnghttp2_asio_la-asio_client_session_tls_impl.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='asio_client_session_tls_impl.cc' object='libnghttp2_asio_la-asio_client_session_tls_impl.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttp2_asio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttp2_asio_la-asio_client_session_tls_impl.lo `test -f 'asio_client_session_tls_impl.cc' || echo '$(srcdir)/'`asio_client_session_tls_impl.cc + +libnghttp2_asio_la-asio_client_response.lo: asio_client_response.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttp2_asio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttp2_asio_la-asio_client_response.lo -MD -MP -MF $(DEPDIR)/libnghttp2_asio_la-asio_client_response.Tpo -c -o libnghttp2_asio_la-asio_client_response.lo `test -f 'asio_client_response.cc' || echo '$(srcdir)/'`asio_client_response.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttp2_asio_la-asio_client_response.Tpo $(DEPDIR)/libnghttp2_asio_la-asio_client_response.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='asio_client_response.cc' object='libnghttp2_asio_la-asio_client_response.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttp2_asio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttp2_asio_la-asio_client_response.lo `test -f 'asio_client_response.cc' || echo '$(srcdir)/'`asio_client_response.cc + +libnghttp2_asio_la-asio_client_response_impl.lo: asio_client_response_impl.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttp2_asio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttp2_asio_la-asio_client_response_impl.lo -MD -MP -MF $(DEPDIR)/libnghttp2_asio_la-asio_client_response_impl.Tpo -c -o libnghttp2_asio_la-asio_client_response_impl.lo `test -f 'asio_client_response_impl.cc' || echo '$(srcdir)/'`asio_client_response_impl.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttp2_asio_la-asio_client_response_impl.Tpo $(DEPDIR)/libnghttp2_asio_la-asio_client_response_impl.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='asio_client_response_impl.cc' object='libnghttp2_asio_la-asio_client_response_impl.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttp2_asio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttp2_asio_la-asio_client_response_impl.lo `test -f 'asio_client_response_impl.cc' || echo '$(srcdir)/'`asio_client_response_impl.cc + +libnghttp2_asio_la-asio_client_request.lo: asio_client_request.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttp2_asio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttp2_asio_la-asio_client_request.lo -MD -MP -MF $(DEPDIR)/libnghttp2_asio_la-asio_client_request.Tpo -c -o libnghttp2_asio_la-asio_client_request.lo `test -f 'asio_client_request.cc' || echo '$(srcdir)/'`asio_client_request.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttp2_asio_la-asio_client_request.Tpo $(DEPDIR)/libnghttp2_asio_la-asio_client_request.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='asio_client_request.cc' object='libnghttp2_asio_la-asio_client_request.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttp2_asio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttp2_asio_la-asio_client_request.lo `test -f 'asio_client_request.cc' || echo '$(srcdir)/'`asio_client_request.cc + +libnghttp2_asio_la-asio_client_request_impl.lo: asio_client_request_impl.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttp2_asio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttp2_asio_la-asio_client_request_impl.lo -MD -MP -MF $(DEPDIR)/libnghttp2_asio_la-asio_client_request_impl.Tpo -c -o libnghttp2_asio_la-asio_client_request_impl.lo `test -f 'asio_client_request_impl.cc' || echo '$(srcdir)/'`asio_client_request_impl.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttp2_asio_la-asio_client_request_impl.Tpo $(DEPDIR)/libnghttp2_asio_la-asio_client_request_impl.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='asio_client_request_impl.cc' object='libnghttp2_asio_la-asio_client_request_impl.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttp2_asio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttp2_asio_la-asio_client_request_impl.lo `test -f 'asio_client_request_impl.cc' || echo '$(srcdir)/'`asio_client_request_impl.cc + +libnghttp2_asio_la-asio_client_stream.lo: asio_client_stream.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttp2_asio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttp2_asio_la-asio_client_stream.lo -MD -MP -MF $(DEPDIR)/libnghttp2_asio_la-asio_client_stream.Tpo -c -o libnghttp2_asio_la-asio_client_stream.lo `test -f 'asio_client_stream.cc' || echo '$(srcdir)/'`asio_client_stream.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttp2_asio_la-asio_client_stream.Tpo $(DEPDIR)/libnghttp2_asio_la-asio_client_stream.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='asio_client_stream.cc' object='libnghttp2_asio_la-asio_client_stream.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttp2_asio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttp2_asio_la-asio_client_stream.lo `test -f 'asio_client_stream.cc' || echo '$(srcdir)/'`asio_client_stream.cc + +libnghttp2_asio_la-asio_client_tls_context.lo: asio_client_tls_context.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttp2_asio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttp2_asio_la-asio_client_tls_context.lo -MD -MP -MF $(DEPDIR)/libnghttp2_asio_la-asio_client_tls_context.Tpo -c -o libnghttp2_asio_la-asio_client_tls_context.lo `test -f 'asio_client_tls_context.cc' || echo '$(srcdir)/'`asio_client_tls_context.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttp2_asio_la-asio_client_tls_context.Tpo $(DEPDIR)/libnghttp2_asio_la-asio_client_tls_context.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='asio_client_tls_context.cc' object='libnghttp2_asio_la-asio_client_tls_context.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttp2_asio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttp2_asio_la-asio_client_tls_context.lo `test -f 'asio_client_tls_context.cc' || echo '$(srcdir)/'`asio_client_tls_context.cc + +nghttpx_unittest-shrpx-unittest.o: shrpx-unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT nghttpx_unittest-shrpx-unittest.o -MD -MP -MF $(DEPDIR)/nghttpx_unittest-shrpx-unittest.Tpo -c -o nghttpx_unittest-shrpx-unittest.o `test -f 'shrpx-unittest.cc' || echo '$(srcdir)/'`shrpx-unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nghttpx_unittest-shrpx-unittest.Tpo $(DEPDIR)/nghttpx_unittest-shrpx-unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='shrpx-unittest.cc' object='nghttpx_unittest-shrpx-unittest.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o nghttpx_unittest-shrpx-unittest.o `test -f 'shrpx-unittest.cc' || echo '$(srcdir)/'`shrpx-unittest.cc + +nghttpx_unittest-shrpx-unittest.obj: shrpx-unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT nghttpx_unittest-shrpx-unittest.obj -MD -MP -MF $(DEPDIR)/nghttpx_unittest-shrpx-unittest.Tpo -c -o nghttpx_unittest-shrpx-unittest.obj `if test -f 'shrpx-unittest.cc'; then $(CYGPATH_W) 'shrpx-unittest.cc'; else $(CYGPATH_W) '$(srcdir)/shrpx-unittest.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nghttpx_unittest-shrpx-unittest.Tpo $(DEPDIR)/nghttpx_unittest-shrpx-unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='shrpx-unittest.cc' object='nghttpx_unittest-shrpx-unittest.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o nghttpx_unittest-shrpx-unittest.obj `if test -f 'shrpx-unittest.cc'; then $(CYGPATH_W) 'shrpx-unittest.cc'; else $(CYGPATH_W) '$(srcdir)/shrpx-unittest.cc'; fi` + +nghttpx_unittest-shrpx_ssl_test.o: shrpx_ssl_test.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT nghttpx_unittest-shrpx_ssl_test.o -MD -MP -MF $(DEPDIR)/nghttpx_unittest-shrpx_ssl_test.Tpo -c -o nghttpx_unittest-shrpx_ssl_test.o `test -f 'shrpx_ssl_test.cc' || echo '$(srcdir)/'`shrpx_ssl_test.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nghttpx_unittest-shrpx_ssl_test.Tpo $(DEPDIR)/nghttpx_unittest-shrpx_ssl_test.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='shrpx_ssl_test.cc' object='nghttpx_unittest-shrpx_ssl_test.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o nghttpx_unittest-shrpx_ssl_test.o `test -f 'shrpx_ssl_test.cc' || echo '$(srcdir)/'`shrpx_ssl_test.cc + +nghttpx_unittest-shrpx_ssl_test.obj: shrpx_ssl_test.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT nghttpx_unittest-shrpx_ssl_test.obj -MD -MP -MF $(DEPDIR)/nghttpx_unittest-shrpx_ssl_test.Tpo -c -o nghttpx_unittest-shrpx_ssl_test.obj `if test -f 'shrpx_ssl_test.cc'; then $(CYGPATH_W) 'shrpx_ssl_test.cc'; else $(CYGPATH_W) '$(srcdir)/shrpx_ssl_test.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nghttpx_unittest-shrpx_ssl_test.Tpo $(DEPDIR)/nghttpx_unittest-shrpx_ssl_test.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='shrpx_ssl_test.cc' object='nghttpx_unittest-shrpx_ssl_test.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o nghttpx_unittest-shrpx_ssl_test.obj `if test -f 'shrpx_ssl_test.cc'; then $(CYGPATH_W) 'shrpx_ssl_test.cc'; else $(CYGPATH_W) '$(srcdir)/shrpx_ssl_test.cc'; fi` + +nghttpx_unittest-shrpx_downstream_test.o: shrpx_downstream_test.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT nghttpx_unittest-shrpx_downstream_test.o -MD -MP -MF $(DEPDIR)/nghttpx_unittest-shrpx_downstream_test.Tpo -c -o nghttpx_unittest-shrpx_downstream_test.o `test -f 'shrpx_downstream_test.cc' || echo '$(srcdir)/'`shrpx_downstream_test.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nghttpx_unittest-shrpx_downstream_test.Tpo $(DEPDIR)/nghttpx_unittest-shrpx_downstream_test.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='shrpx_downstream_test.cc' object='nghttpx_unittest-shrpx_downstream_test.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o nghttpx_unittest-shrpx_downstream_test.o `test -f 'shrpx_downstream_test.cc' || echo '$(srcdir)/'`shrpx_downstream_test.cc + +nghttpx_unittest-shrpx_downstream_test.obj: shrpx_downstream_test.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT nghttpx_unittest-shrpx_downstream_test.obj -MD -MP -MF $(DEPDIR)/nghttpx_unittest-shrpx_downstream_test.Tpo -c -o nghttpx_unittest-shrpx_downstream_test.obj `if test -f 'shrpx_downstream_test.cc'; then $(CYGPATH_W) 'shrpx_downstream_test.cc'; else $(CYGPATH_W) '$(srcdir)/shrpx_downstream_test.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nghttpx_unittest-shrpx_downstream_test.Tpo $(DEPDIR)/nghttpx_unittest-shrpx_downstream_test.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='shrpx_downstream_test.cc' object='nghttpx_unittest-shrpx_downstream_test.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o nghttpx_unittest-shrpx_downstream_test.obj `if test -f 'shrpx_downstream_test.cc'; then $(CYGPATH_W) 'shrpx_downstream_test.cc'; else $(CYGPATH_W) '$(srcdir)/shrpx_downstream_test.cc'; fi` + +nghttpx_unittest-shrpx_config_test.o: shrpx_config_test.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT nghttpx_unittest-shrpx_config_test.o -MD -MP -MF $(DEPDIR)/nghttpx_unittest-shrpx_config_test.Tpo -c -o nghttpx_unittest-shrpx_config_test.o `test -f 'shrpx_config_test.cc' || echo '$(srcdir)/'`shrpx_config_test.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nghttpx_unittest-shrpx_config_test.Tpo $(DEPDIR)/nghttpx_unittest-shrpx_config_test.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='shrpx_config_test.cc' object='nghttpx_unittest-shrpx_config_test.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o nghttpx_unittest-shrpx_config_test.o `test -f 'shrpx_config_test.cc' || echo '$(srcdir)/'`shrpx_config_test.cc + +nghttpx_unittest-shrpx_config_test.obj: shrpx_config_test.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT nghttpx_unittest-shrpx_config_test.obj -MD -MP -MF $(DEPDIR)/nghttpx_unittest-shrpx_config_test.Tpo -c -o nghttpx_unittest-shrpx_config_test.obj `if test -f 'shrpx_config_test.cc'; then $(CYGPATH_W) 'shrpx_config_test.cc'; else $(CYGPATH_W) '$(srcdir)/shrpx_config_test.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nghttpx_unittest-shrpx_config_test.Tpo $(DEPDIR)/nghttpx_unittest-shrpx_config_test.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='shrpx_config_test.cc' object='nghttpx_unittest-shrpx_config_test.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o nghttpx_unittest-shrpx_config_test.obj `if test -f 'shrpx_config_test.cc'; then $(CYGPATH_W) 'shrpx_config_test.cc'; else $(CYGPATH_W) '$(srcdir)/shrpx_config_test.cc'; fi` + +nghttpx_unittest-http2_test.o: http2_test.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT nghttpx_unittest-http2_test.o -MD -MP -MF $(DEPDIR)/nghttpx_unittest-http2_test.Tpo -c -o nghttpx_unittest-http2_test.o `test -f 'http2_test.cc' || echo '$(srcdir)/'`http2_test.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nghttpx_unittest-http2_test.Tpo $(DEPDIR)/nghttpx_unittest-http2_test.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='http2_test.cc' object='nghttpx_unittest-http2_test.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o nghttpx_unittest-http2_test.o `test -f 'http2_test.cc' || echo '$(srcdir)/'`http2_test.cc + +nghttpx_unittest-http2_test.obj: http2_test.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT nghttpx_unittest-http2_test.obj -MD -MP -MF $(DEPDIR)/nghttpx_unittest-http2_test.Tpo -c -o nghttpx_unittest-http2_test.obj `if test -f 'http2_test.cc'; then $(CYGPATH_W) 'http2_test.cc'; else $(CYGPATH_W) '$(srcdir)/http2_test.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nghttpx_unittest-http2_test.Tpo $(DEPDIR)/nghttpx_unittest-http2_test.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='http2_test.cc' object='nghttpx_unittest-http2_test.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o nghttpx_unittest-http2_test.obj `if test -f 'http2_test.cc'; then $(CYGPATH_W) 'http2_test.cc'; else $(CYGPATH_W) '$(srcdir)/http2_test.cc'; fi` + +nghttpx_unittest-util_test.o: util_test.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT nghttpx_unittest-util_test.o -MD -MP -MF $(DEPDIR)/nghttpx_unittest-util_test.Tpo -c -o nghttpx_unittest-util_test.o `test -f 'util_test.cc' || echo '$(srcdir)/'`util_test.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nghttpx_unittest-util_test.Tpo $(DEPDIR)/nghttpx_unittest-util_test.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='util_test.cc' object='nghttpx_unittest-util_test.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o nghttpx_unittest-util_test.o `test -f 'util_test.cc' || echo '$(srcdir)/'`util_test.cc + +nghttpx_unittest-util_test.obj: util_test.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT nghttpx_unittest-util_test.obj -MD -MP -MF $(DEPDIR)/nghttpx_unittest-util_test.Tpo -c -o nghttpx_unittest-util_test.obj `if test -f 'util_test.cc'; then $(CYGPATH_W) 'util_test.cc'; else $(CYGPATH_W) '$(srcdir)/util_test.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nghttpx_unittest-util_test.Tpo $(DEPDIR)/nghttpx_unittest-util_test.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='util_test.cc' object='nghttpx_unittest-util_test.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o nghttpx_unittest-util_test.obj `if test -f 'util_test.cc'; then $(CYGPATH_W) 'util_test.cc'; else $(CYGPATH_W) '$(srcdir)/util_test.cc'; fi` + +nghttpx_unittest-buffer_test.o: buffer_test.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT nghttpx_unittest-buffer_test.o -MD -MP -MF $(DEPDIR)/nghttpx_unittest-buffer_test.Tpo -c -o nghttpx_unittest-buffer_test.o `test -f 'buffer_test.cc' || echo '$(srcdir)/'`buffer_test.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nghttpx_unittest-buffer_test.Tpo $(DEPDIR)/nghttpx_unittest-buffer_test.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='buffer_test.cc' object='nghttpx_unittest-buffer_test.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o nghttpx_unittest-buffer_test.o `test -f 'buffer_test.cc' || echo '$(srcdir)/'`buffer_test.cc + +nghttpx_unittest-buffer_test.obj: buffer_test.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT nghttpx_unittest-buffer_test.obj -MD -MP -MF $(DEPDIR)/nghttpx_unittest-buffer_test.Tpo -c -o nghttpx_unittest-buffer_test.obj `if test -f 'buffer_test.cc'; then $(CYGPATH_W) 'buffer_test.cc'; else $(CYGPATH_W) '$(srcdir)/buffer_test.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nghttpx_unittest-buffer_test.Tpo $(DEPDIR)/nghttpx_unittest-buffer_test.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='buffer_test.cc' object='nghttpx_unittest-buffer_test.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o nghttpx_unittest-buffer_test.obj `if test -f 'buffer_test.cc'; then $(CYGPATH_W) 'buffer_test.cc'; else $(CYGPATH_W) '$(srcdir)/buffer_test.cc'; fi` + +nghttpx_unittest-memchunk_test.o: memchunk_test.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT nghttpx_unittest-memchunk_test.o -MD -MP -MF $(DEPDIR)/nghttpx_unittest-memchunk_test.Tpo -c -o nghttpx_unittest-memchunk_test.o `test -f 'memchunk_test.cc' || echo '$(srcdir)/'`memchunk_test.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nghttpx_unittest-memchunk_test.Tpo $(DEPDIR)/nghttpx_unittest-memchunk_test.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='memchunk_test.cc' object='nghttpx_unittest-memchunk_test.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o nghttpx_unittest-memchunk_test.o `test -f 'memchunk_test.cc' || echo '$(srcdir)/'`memchunk_test.cc + +nghttpx_unittest-memchunk_test.obj: memchunk_test.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT nghttpx_unittest-memchunk_test.obj -MD -MP -MF $(DEPDIR)/nghttpx_unittest-memchunk_test.Tpo -c -o nghttpx_unittest-memchunk_test.obj `if test -f 'memchunk_test.cc'; then $(CYGPATH_W) 'memchunk_test.cc'; else $(CYGPATH_W) '$(srcdir)/memchunk_test.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nghttpx_unittest-memchunk_test.Tpo $(DEPDIR)/nghttpx_unittest-memchunk_test.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='memchunk_test.cc' object='nghttpx_unittest-memchunk_test.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o nghttpx_unittest-memchunk_test.obj `if test -f 'memchunk_test.cc'; then $(CYGPATH_W) 'memchunk_test.cc'; else $(CYGPATH_W) '$(srcdir)/memchunk_test.cc'; fi` + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs +install-pkgconfigDATA: $(pkgconfig_DATA) + @$(NORMAL_INSTALL) + @list='$(pkgconfig_DATA)'; test -n "$(pkgconfigdir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(pkgconfigdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(pkgconfigdir)" || exit 1; \ + fi; \ + for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + echo "$$d$$p"; \ + done | $(am__base_list) | \ + while read files; do \ + echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(pkgconfigdir)'"; \ + $(INSTALL_DATA) $$files "$(DESTDIR)$(pkgconfigdir)" || exit $$?; \ + done + +uninstall-pkgconfigDATA: + @$(NORMAL_UNINSTALL) + @list='$(pkgconfig_DATA)'; test -n "$(pkgconfigdir)" || list=; \ + files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ + dir='$(DESTDIR)$(pkgconfigdir)'; $(am__uninstall_files_from_dir) + +# This directory's subdirectories are mostly independent; you can cd +# into them and run 'make' without going through this Makefile. +# To change the values of 'make' variables: instead of editing Makefiles, +# (1) if the variable is set in 'config.status', edit 'config.status' +# (which will cause the Makefiles to be regenerated when you run 'make'); +# (2) otherwise, pass the desired values on the 'make' command line. +$(am__recursive_targets): + @fail=; \ + if $(am__make_keepgoing); then \ + failcom='fail=yes'; \ + else \ + failcom='exit 1'; \ + fi; \ + dot_seen=no; \ + target=`echo $@ | sed s/-recursive//`; \ + case "$@" in \ + distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ + *) list='$(SUBDIRS)' ;; \ + esac; \ + for subdir in $$list; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + dot_seen=yes; \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || eval $$failcom; \ + done; \ + if test "$$dot_seen" = "no"; then \ + $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ + fi; test -z "$$fail" + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-recursive +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \ + include_option=--etags-include; \ + empty_fix=.; \ + else \ + include_option=--include; \ + empty_fix=; \ + fi; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + test ! -f $$subdir/TAGS || \ + set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \ + fi; \ + done; \ + $(am__define_uniq_tagged_files); \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: ctags-recursive + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" +cscopelist: cscopelist-recursive + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +# Recover from deleted '.trs' file; this should ensure that +# "rm -f foo.log; make foo.trs" re-run 'foo.test', and re-create +# both 'foo.log' and 'foo.trs'. Break the recipe in two subshells +# to avoid problems with "make -n". +.log.trs: + rm -f $< $@ + $(MAKE) $(AM_MAKEFLAGS) $< + +# Leading 'am--fnord' is there to ensure the list of targets does not +# expand to empty, as could happen e.g. with make check TESTS=''. +am--fnord $(TEST_LOGS) $(TEST_LOGS:.log=.trs): $(am__force_recheck) +am--force-recheck: + @: + +$(TEST_SUITE_LOG): $(TEST_LOGS) + @$(am__set_TESTS_bases); \ + am__f_ok () { test -f "$$1" && test -r "$$1"; }; \ + redo_bases=`for i in $$bases; do \ + am__f_ok $$i.trs && am__f_ok $$i.log || echo $$i; \ + done`; \ + if test -n "$$redo_bases"; then \ + redo_logs=`for i in $$redo_bases; do echo $$i.log; done`; \ + redo_results=`for i in $$redo_bases; do echo $$i.trs; done`; \ + if $(am__make_dryrun); then :; else \ + rm -f $$redo_logs && rm -f $$redo_results || exit 1; \ + fi; \ + fi; \ + if test -n "$$am__remaking_logs"; then \ + echo "fatal: making $(TEST_SUITE_LOG): possible infinite" \ + "recursion detected" >&2; \ + else \ + am__remaking_logs=yes $(MAKE) $(AM_MAKEFLAGS) $$redo_logs; \ + fi; \ + if $(am__make_dryrun); then :; else \ + st=0; \ + errmsg="fatal: making $(TEST_SUITE_LOG): failed to create"; \ + for i in $$redo_bases; do \ + test -f $$i.trs && test -r $$i.trs \ + || { echo "$$errmsg $$i.trs" >&2; st=1; }; \ + test -f $$i.log && test -r $$i.log \ + || { echo "$$errmsg $$i.log" >&2; st=1; }; \ + done; \ + test $$st -eq 0 || exit 1; \ + fi + @$(am__sh_e_setup); $(am__tty_colors); $(am__set_TESTS_bases); \ + ws='[ ]'; \ + results=`for b in $$bases; do echo $$b.trs; done`; \ + test -n "$$results" || results=/dev/null; \ + all=` grep "^$$ws*:test-result:" $$results | wc -l`; \ + pass=` grep "^$$ws*:test-result:$$ws*PASS" $$results | wc -l`; \ + fail=` grep "^$$ws*:test-result:$$ws*FAIL" $$results | wc -l`; \ + skip=` grep "^$$ws*:test-result:$$ws*SKIP" $$results | wc -l`; \ + xfail=`grep "^$$ws*:test-result:$$ws*XFAIL" $$results | wc -l`; \ + xpass=`grep "^$$ws*:test-result:$$ws*XPASS" $$results | wc -l`; \ + error=`grep "^$$ws*:test-result:$$ws*ERROR" $$results | wc -l`; \ + if test `expr $$fail + $$xpass + $$error` -eq 0; then \ + success=true; \ + else \ + success=false; \ + fi; \ + br='==================='; br=$$br$$br$$br$$br; \ + result_count () \ + { \ + if test x"$$1" = x"--maybe-color"; then \ + maybe_colorize=yes; \ + elif test x"$$1" = x"--no-color"; then \ + maybe_colorize=no; \ + else \ + echo "$@: invalid 'result_count' usage" >&2; exit 4; \ + fi; \ + shift; \ + desc=$$1 count=$$2; \ + if test $$maybe_colorize = yes && test $$count -gt 0; then \ + color_start=$$3 color_end=$$std; \ + else \ + color_start= color_end=; \ + fi; \ + echo "$${color_start}# $$desc $$count$${color_end}"; \ + }; \ + create_testsuite_report () \ + { \ + result_count $$1 "TOTAL:" $$all "$$brg"; \ + result_count $$1 "PASS: " $$pass "$$grn"; \ + result_count $$1 "SKIP: " $$skip "$$blu"; \ + result_count $$1 "XFAIL:" $$xfail "$$lgn"; \ + result_count $$1 "FAIL: " $$fail "$$red"; \ + result_count $$1 "XPASS:" $$xpass "$$red"; \ + result_count $$1 "ERROR:" $$error "$$mgn"; \ + }; \ + { \ + echo "$(PACKAGE_STRING): $(subdir)/$(TEST_SUITE_LOG)" | \ + $(am__rst_title); \ + create_testsuite_report --no-color; \ + echo; \ + echo ".. contents:: :depth: 2"; \ + echo; \ + for b in $$bases; do echo $$b; done \ + | $(am__create_global_log); \ + } >$(TEST_SUITE_LOG).tmp || exit 1; \ + mv $(TEST_SUITE_LOG).tmp $(TEST_SUITE_LOG); \ + if $$success; then \ + col="$$grn"; \ + else \ + col="$$red"; \ + test x"$$VERBOSE" = x || cat $(TEST_SUITE_LOG); \ + fi; \ + echo "$${col}$$br$${std}"; \ + echo "$${col}Testsuite summary for $(PACKAGE_STRING)$${std}"; \ + echo "$${col}$$br$${std}"; \ + create_testsuite_report --maybe-color; \ + echo "$$col$$br$$std"; \ + if $$success; then :; else \ + echo "$${col}See $(subdir)/$(TEST_SUITE_LOG)$${std}"; \ + if test -n "$(PACKAGE_BUGREPORT)"; then \ + echo "$${col}Please report to $(PACKAGE_BUGREPORT)$${std}"; \ + fi; \ + echo "$$col$$br$$std"; \ + fi; \ + $$success || exit 1 + +check-TESTS: + @list='$(RECHECK_LOGS)'; test -z "$$list" || rm -f $$list + @list='$(RECHECK_LOGS:.log=.trs)'; test -z "$$list" || rm -f $$list + @test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG) + @set +e; $(am__set_TESTS_bases); \ + log_list=`for i in $$bases; do echo $$i.log; done`; \ + trs_list=`for i in $$bases; do echo $$i.trs; done`; \ + log_list=`echo $$log_list`; trs_list=`echo $$trs_list`; \ + $(MAKE) $(AM_MAKEFLAGS) $(TEST_SUITE_LOG) TEST_LOGS="$$log_list"; \ + exit $$?; +recheck: all $(check_PROGRAMS) + @test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG) + @set +e; $(am__set_TESTS_bases); \ + bases=`for i in $$bases; do echo $$i; done \ + | $(am__list_recheck_tests)` || exit 1; \ + log_list=`for i in $$bases; do echo $$i.log; done`; \ + log_list=`echo $$log_list`; \ + $(MAKE) $(AM_MAKEFLAGS) $(TEST_SUITE_LOG) \ + am__force_recheck=am--force-recheck \ + TEST_LOGS="$$log_list"; \ + exit $$? +nghttpx-unittest.log: nghttpx-unittest$(EXEEXT) + @p='nghttpx-unittest$(EXEEXT)'; \ + b='nghttpx-unittest'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +.test.log: + @p='$<'; \ + $(am__set_b); \ + $(am__check_pre) $(TEST_LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_TEST_LOG_DRIVER_FLAGS) $(TEST_LOG_DRIVER_FLAGS) -- $(TEST_LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +@am__EXEEXT_TRUE@.test$(EXEEXT).log: +@am__EXEEXT_TRUE@ @p='$<'; \ +@am__EXEEXT_TRUE@ $(am__set_b); \ +@am__EXEEXT_TRUE@ $(am__check_pre) $(TEST_LOG_DRIVER) --test-name "$$f" \ +@am__EXEEXT_TRUE@ --log-file $$b.log --trs-file $$b.trs \ +@am__EXEEXT_TRUE@ $(am__common_driver_flags) $(AM_TEST_LOG_DRIVER_FLAGS) $(TEST_LOG_DRIVER_FLAGS) -- $(TEST_LOG_COMPILE) \ +@am__EXEEXT_TRUE@ "$$tst" $(AM_TESTS_FD_REDIRECT) + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done + @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + $(am__make_dryrun) \ + || test -d "$(distdir)/$$subdir" \ + || $(MKDIR_P) "$(distdir)/$$subdir" \ + || exit 1; \ + dir1=$$subdir; dir2="$(distdir)/$$subdir"; \ + $(am__relativize); \ + new_distdir=$$reldir; \ + dir1=$$subdir; dir2="$(top_distdir)"; \ + $(am__relativize); \ + new_top_distdir=$$reldir; \ + echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \ + echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \ + ($(am__cd) $$subdir && \ + $(MAKE) $(AM_MAKEFLAGS) \ + top_distdir="$$new_top_distdir" \ + distdir="$$new_distdir" \ + am__remove_distdir=: \ + am__skip_length_check=: \ + am__skip_mode_fix=: \ + distdir) \ + || exit 1; \ + fi; \ + done +check-am: all-am + $(MAKE) $(AM_MAKEFLAGS) $(check_PROGRAMS) + $(MAKE) $(AM_MAKEFLAGS) check-TESTS +check: check-recursive +all-am: Makefile $(LIBRARIES) $(LTLIBRARIES) $(PROGRAMS) $(DATA) +install-binPROGRAMS: install-libLTLIBRARIES + +installdirs: installdirs-recursive +installdirs-am: + for dir in "$(DESTDIR)$(libdir)" "$(DESTDIR)$(bindir)" "$(DESTDIR)$(pkgconfigdir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: install-recursive +install-exec: install-exec-recursive +install-data: install-data-recursive +uninstall: uninstall-recursive + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-recursive +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + -test -z "$(TEST_LOGS)" || rm -f $(TEST_LOGS) + -test -z "$(TEST_LOGS:.log=.trs)" || rm -f $(TEST_LOGS:.log=.trs) + -test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG) + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + -test -z "$(DISTCLEANFILES)" || rm -f $(DISTCLEANFILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-recursive + +clean-am: clean-binPROGRAMS clean-checkPROGRAMS clean-generic \ + clean-libLTLIBRARIES clean-libtool clean-noinstLIBRARIES \ + mostlyclean-am + +distclean: distclean-recursive + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-recursive + +dvi-am: + +html: html-recursive + +html-am: + +info: info-recursive + +info-am: + +install-data-am: install-pkgconfigDATA + +install-dvi: install-dvi-recursive + +install-dvi-am: + +install-exec-am: install-binPROGRAMS install-libLTLIBRARIES + +install-html: install-html-recursive + +install-html-am: + +install-info: install-info-recursive + +install-info-am: + +install-man: + +install-pdf: install-pdf-recursive + +install-pdf-am: + +install-ps: install-ps-recursive + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-recursive + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-recursive + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-recursive + +pdf-am: + +ps: ps-recursive + +ps-am: + +uninstall-am: uninstall-binPROGRAMS uninstall-libLTLIBRARIES \ + uninstall-pkgconfigDATA + +.MAKE: $(am__recursive_targets) check-am install-am install-strip + +.PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am check \ + check-TESTS check-am clean clean-binPROGRAMS \ + clean-checkPROGRAMS clean-generic clean-libLTLIBRARIES \ + clean-libtool clean-noinstLIBRARIES cscopelist-am ctags \ + ctags-am distclean distclean-compile distclean-generic \ + distclean-libtool distclean-tags distdir dvi dvi-am html \ + html-am info info-am install install-am install-binPROGRAMS \ + install-data install-data-am install-dvi install-dvi-am \ + install-exec install-exec-am install-html install-html-am \ + install-info install-info-am install-libLTLIBRARIES \ + install-man install-pdf install-pdf-am install-pkgconfigDATA \ + install-ps install-ps-am install-strip installcheck \ + installcheck-am installdirs installdirs-am maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-compile \ + mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ + recheck tags tags-am uninstall uninstall-am \ + uninstall-binPROGRAMS uninstall-libLTLIBRARIES \ + uninstall-pkgconfigDATA + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/src/app_helper.cc b/src/app_helper.cc new file mode 100644 index 0000000..58f8439 --- /dev/null +++ b/src/app_helper.cc @@ -0,0 +1,505 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include +#ifdef HAVE_SYS_SOCKET_H +#include +#endif // HAVE_SYS_SOCKET_H +#ifdef HAVE_NETDB_H +#include +#endif // HAVE_NETDB_H +#ifdef HAVE_UNISTD_H +#include +#endif // HAVE_UNISTD_H +#ifdef HAVE_FCNTL_H +#include +#endif // HAVE_FCNTL_H +#ifdef HAVE_NETINET_IN_H +#include +#endif // HAVE_NETINET_IN_H +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "app_helper.h" +#include "util.h" +#include "http2.h" + +namespace nghttp2 { + +namespace { +const char *strstatus(uint32_t error_code) { + switch (error_code) { + case NGHTTP2_NO_ERROR: + return "NO_ERROR"; + case NGHTTP2_PROTOCOL_ERROR: + return "PROTOCOL_ERROR"; + case NGHTTP2_INTERNAL_ERROR: + return "INTERNAL_ERROR"; + case NGHTTP2_FLOW_CONTROL_ERROR: + return "FLOW_CONTROL_ERROR"; + case NGHTTP2_SETTINGS_TIMEOUT: + return "SETTINGS_TIMEOUT"; + case NGHTTP2_STREAM_CLOSED: + return "STREAM_CLOSED"; + case NGHTTP2_FRAME_SIZE_ERROR: + return "FRAME_SIZE_ERROR"; + case NGHTTP2_REFUSED_STREAM: + return "REFUSED_STREAM"; + case NGHTTP2_CANCEL: + return "CANCEL"; + case NGHTTP2_COMPRESSION_ERROR: + return "COMPRESSION_ERROR"; + case NGHTTP2_CONNECT_ERROR: + return "CONNECT_ERROR"; + case NGHTTP2_ENHANCE_YOUR_CALM: + return "ENHANCE_YOUR_CALM"; + case NGHTTP2_INADEQUATE_SECURITY: + return "INADEQUATE_SECURITY"; + case NGHTTP2_HTTP_1_1_REQUIRED: + return "HTTP_1_1_REQUIRED"; + default: + return "UNKNOWN"; + } +} +} // namespace + +namespace { +const char *strsettingsid(int32_t id) { + switch (id) { + case NGHTTP2_SETTINGS_HEADER_TABLE_SIZE: + return "SETTINGS_HEADER_TABLE_SIZE"; + case NGHTTP2_SETTINGS_ENABLE_PUSH: + return "SETTINGS_ENABLE_PUSH"; + case NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS: + return "SETTINGS_MAX_CONCURRENT_STREAMS"; + case NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE: + return "SETTINGS_INITIAL_WINDOW_SIZE"; + case NGHTTP2_SETTINGS_MAX_FRAME_SIZE: + return "SETTINGS_MAX_FRAME_SIZE"; + case NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE: + return "SETTINGS_MAX_HEADER_LIST_SIZE"; + default: + return "UNKNOWN"; + } +} +} // namespace + +namespace { +const char *strframetype(uint8_t type) { + switch (type) { + case NGHTTP2_DATA: + return "DATA"; + case NGHTTP2_HEADERS: + return "HEADERS"; + case NGHTTP2_PRIORITY: + return "PRIORITY"; + case NGHTTP2_RST_STREAM: + return "RST_STREAM"; + case NGHTTP2_SETTINGS: + return "SETTINGS"; + case NGHTTP2_PUSH_PROMISE: + return "PUSH_PROMISE"; + case NGHTTP2_PING: + return "PING"; + case NGHTTP2_GOAWAY: + return "GOAWAY"; + case NGHTTP2_WINDOW_UPDATE: + return "WINDOW_UPDATE"; + default: + return "UNKNOWN"; + } +}; +} // namespace + +namespace { +bool color_output = false; +} // namespace + +void set_color_output(bool f) { color_output = f; } + +namespace { +FILE *outfile = stdout; +} // namespace + +void set_output(FILE *file) { outfile = file; } + +namespace { +void print_frame_attr_indent() { fprintf(outfile, " "); } +} // namespace + +namespace { +const char *ansi_esc(const char *code) { return color_output ? code : ""; } +} // namespace + +namespace { +const char *ansi_escend() { return color_output ? "\033[0m" : ""; } +} // namespace + +namespace { +void print_nv(nghttp2_nv *nv) { + fprintf(outfile, "%s%s%s: %s\n", ansi_esc("\033[1;34m"), nv->name, + ansi_escend(), nv->value); +} +} // namespace +namespace { +void print_nv(nghttp2_nv *nva, size_t nvlen) { + auto end = nva + nvlen; + for (; nva != end; ++nva) { + print_frame_attr_indent(); + + print_nv(nva); + } +} +} // namelen + +void print_timer() { + auto millis = get_timer(); + fprintf(outfile, "%s[%3ld.%03ld]%s", ansi_esc("\033[33m"), + (long int)(millis.count() / 1000), (long int)(millis.count() % 1000), + ansi_escend()); +} + +namespace { +void print_frame_hd(const nghttp2_frame_hd &hd) { + fprintf(outfile, "\n", hd.length, + hd.flags, hd.stream_id); +} +} // namespace + +namespace { +void print_flags(const nghttp2_frame_hd &hd) { + std::string s; + switch (hd.type) { + case NGHTTP2_DATA: + if (hd.flags & NGHTTP2_FLAG_END_STREAM) { + s += "END_STREAM"; + } + if (hd.flags & NGHTTP2_FLAG_PADDED) { + if (!s.empty()) { + s += " | "; + } + s += "PADDED"; + } + break; + case NGHTTP2_HEADERS: + if (hd.flags & NGHTTP2_FLAG_END_STREAM) { + s += "END_STREAM"; + } + if (hd.flags & NGHTTP2_FLAG_END_HEADERS) { + if (!s.empty()) { + s += " | "; + } + s += "END_HEADERS"; + } + if (hd.flags & NGHTTP2_FLAG_PADDED) { + if (!s.empty()) { + s += " | "; + } + s += "PADDED"; + } + if (hd.flags & NGHTTP2_FLAG_PRIORITY) { + if (!s.empty()) { + s += " | "; + } + s += "PRIORITY"; + } + + break; + case NGHTTP2_PRIORITY: + break; + case NGHTTP2_SETTINGS: + if (hd.flags & NGHTTP2_FLAG_ACK) { + s += "ACK"; + } + break; + case NGHTTP2_PUSH_PROMISE: + if (hd.flags & NGHTTP2_FLAG_END_HEADERS) { + s += "END_HEADERS"; + } + if (hd.flags & NGHTTP2_FLAG_PADDED) { + if (!s.empty()) { + s += " | "; + } + s += "PADDED"; + } + break; + case NGHTTP2_PING: + if (hd.flags & NGHTTP2_FLAG_ACK) { + s += "ACK"; + } + break; + } + fprintf(outfile, "; %s\n", s.c_str()); +} +} // namespace + +enum print_type { PRINT_SEND, PRINT_RECV }; + +namespace { +const char *frame_name_ansi_esc(print_type ptype) { + return ansi_esc(ptype == PRINT_SEND ? "\033[1;35m" : "\033[1;36m"); +} +} // namespace + +namespace { +void print_frame(print_type ptype, const nghttp2_frame *frame) { + fprintf(outfile, "%s%s%s frame ", frame_name_ansi_esc(ptype), + strframetype(frame->hd.type), ansi_escend()); + print_frame_hd(frame->hd); + if (frame->hd.flags) { + print_frame_attr_indent(); + print_flags(frame->hd); + } + switch (frame->hd.type) { + case NGHTTP2_DATA: + if (frame->data.padlen > 0) { + print_frame_attr_indent(); + fprintf(outfile, "(padlen=%zu)\n", frame->data.padlen); + } + break; + case NGHTTP2_HEADERS: + print_frame_attr_indent(); + fprintf(outfile, "(padlen=%zu", frame->headers.padlen); + if (frame->hd.flags & NGHTTP2_FLAG_PRIORITY) { + fprintf(outfile, ", dep_stream_id=%d, weight=%u, exclusive=%d", + frame->headers.pri_spec.stream_id, frame->headers.pri_spec.weight, + frame->headers.pri_spec.exclusive); + } + fprintf(outfile, ")\n"); + switch (frame->headers.cat) { + case NGHTTP2_HCAT_REQUEST: + print_frame_attr_indent(); + fprintf(outfile, "; Open new stream\n"); + break; + case NGHTTP2_HCAT_RESPONSE: + print_frame_attr_indent(); + fprintf(outfile, "; First response header\n"); + break; + case NGHTTP2_HCAT_PUSH_RESPONSE: + print_frame_attr_indent(); + fprintf(outfile, "; First push response header\n"); + break; + default: + break; + } + print_nv(frame->headers.nva, frame->headers.nvlen); + break; + case NGHTTP2_PRIORITY: + print_frame_attr_indent(); + + fprintf(outfile, "(dep_stream_id=%d, weight=%u, exclusive=%d)\n", + frame->priority.pri_spec.stream_id, frame->priority.pri_spec.weight, + frame->priority.pri_spec.exclusive); + + break; + case NGHTTP2_RST_STREAM: + print_frame_attr_indent(); + fprintf(outfile, "(error_code=%s(0x%02x))\n", + strstatus(frame->rst_stream.error_code), + frame->rst_stream.error_code); + break; + case NGHTTP2_SETTINGS: + print_frame_attr_indent(); + fprintf(outfile, "(niv=%lu)\n", + static_cast(frame->settings.niv)); + for (size_t i = 0; i < frame->settings.niv; ++i) { + print_frame_attr_indent(); + fprintf(outfile, "[%s(0x%02x):%u]\n", + strsettingsid(frame->settings.iv[i].settings_id), + frame->settings.iv[i].settings_id, frame->settings.iv[i].value); + } + break; + case NGHTTP2_PUSH_PROMISE: + print_frame_attr_indent(); + fprintf(outfile, "(padlen=%zu, promised_stream_id=%d)\n", + frame->push_promise.padlen, frame->push_promise.promised_stream_id); + print_nv(frame->push_promise.nva, frame->push_promise.nvlen); + break; + case NGHTTP2_PING: + print_frame_attr_indent(); + fprintf(outfile, "(opaque_data=%s)\n", + util::format_hex(frame->ping.opaque_data, 8).c_str()); + break; + case NGHTTP2_GOAWAY: + print_frame_attr_indent(); + fprintf(outfile, "(last_stream_id=%d, error_code=%s(0x%02x), " + "opaque_data(%u)=[%s])\n", + frame->goaway.last_stream_id, strstatus(frame->goaway.error_code), + frame->goaway.error_code, + static_cast(frame->goaway.opaque_data_len), + util::ascii_dump(frame->goaway.opaque_data, + frame->goaway.opaque_data_len).c_str()); + break; + case NGHTTP2_WINDOW_UPDATE: + print_frame_attr_indent(); + fprintf(outfile, "(window_size_increment=%d)\n", + frame->window_update.window_size_increment); + break; + default: + break; + } +} +} // namespace + +int verbose_on_header_callback(nghttp2_session *session, + const nghttp2_frame *frame, const uint8_t *name, + size_t namelen, const uint8_t *value, + size_t valuelen, uint8_t flags, + void *user_data) { + nghttp2_nv nv = {const_cast(name), const_cast(value), + namelen, valuelen}; + + print_timer(); + fprintf(outfile, " recv (stream_id=%d", frame->hd.stream_id); + if (flags & NGHTTP2_NV_FLAG_NO_INDEX) { + fprintf(outfile, ", sensitive"); + } + fprintf(outfile, ") "); + + print_nv(&nv); + fflush(outfile); + + return 0; +} + +int verbose_on_frame_recv_callback(nghttp2_session *session, + const nghttp2_frame *frame, + void *user_data) { + print_timer(); + fprintf(outfile, " recv "); + print_frame(PRINT_RECV, frame); + fflush(outfile); + return 0; +} + +int verbose_on_invalid_frame_recv_callback(nghttp2_session *session, + const nghttp2_frame *frame, + int lib_error_code, + void *user_data) { + print_timer(); + fprintf(outfile, " [INVALID; error=%s] recv ", + nghttp2_strerror(lib_error_code)); + print_frame(PRINT_RECV, frame); + fflush(outfile); + return 0; +} + +int verbose_on_frame_send_callback(nghttp2_session *session, + const nghttp2_frame *frame, + void *user_data) { + print_timer(); + fprintf(outfile, " send "); + print_frame(PRINT_SEND, frame); + fflush(outfile); + return 0; +} + +int verbose_on_data_chunk_recv_callback(nghttp2_session *session, uint8_t flags, + int32_t stream_id, const uint8_t *data, + size_t len, void *user_data) { + print_timer(); + auto srecv = + nghttp2_session_get_stream_effective_recv_data_length(session, stream_id); + auto crecv = nghttp2_session_get_effective_recv_data_length(session); + + fprintf(outfile, + " recv (stream_id=%d, length=%zu, srecv=%d, crecv=%d) DATA\n", + stream_id, len, srecv, crecv); + fflush(outfile); + + return 0; +} + +namespace { +std::chrono::steady_clock::time_point base_tv; +} // namespace + +void reset_timer() { base_tv = std::chrono::steady_clock::now(); } + +std::chrono::milliseconds get_timer() { + return time_delta(std::chrono::steady_clock::now(), base_tv); +} + +std::chrono::steady_clock::time_point get_time() { + return std::chrono::steady_clock::now(); +} + +ssize_t deflate_data(uint8_t *out, size_t outlen, const uint8_t *in, + size_t inlen) { + int rv; + z_stream zst; + uint8_t temp_out[8192]; + auto temp_outlen = sizeof(temp_out); + + zst.next_in = Z_NULL; + zst.zalloc = Z_NULL; + zst.zfree = Z_NULL; + zst.opaque = Z_NULL; + + rv = deflateInit2(&zst, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 31, 9, + Z_DEFAULT_STRATEGY); + + if (rv != Z_OK) { + return -1; + } + + zst.avail_in = inlen; + zst.next_in = (uint8_t *)in; + zst.avail_out = temp_outlen; + zst.next_out = temp_out; + + rv = deflate(&zst, Z_FINISH); + + deflateEnd(&zst); + + if (rv != Z_STREAM_END) { + return -1; + } + + temp_outlen -= zst.avail_out; + + if (temp_outlen > outlen) { + return -1; + } + + memcpy(out, temp_out, temp_outlen); + + return temp_outlen; +} + +} // namespace nghttp2 diff --git a/src/app_helper.h b/src/app_helper.h new file mode 100644 index 0000000..408adac --- /dev/null +++ b/src/app_helper.h @@ -0,0 +1,95 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef APP_HELPER_H +#define APP_HELPER_H + +#include "nghttp2_config.h" + +#include +#include +#ifdef HAVE_SYS_TIME_H +#include +#endif // HAVE_SYS_TIME_H +#include + +#include +#include + +#include + +namespace nghttp2 { + +int verbose_on_header_callback(nghttp2_session *session, + const nghttp2_frame *frame, const uint8_t *name, + size_t namelen, const uint8_t *value, + size_t valuelen, uint8_t flags, void *user_data); + +int verbose_on_frame_recv_callback(nghttp2_session *session, + const nghttp2_frame *frame, void *user_data); + +int verbose_on_invalid_frame_recv_callback(nghttp2_session *session, + const nghttp2_frame *frame, + int lib_error_code, void *user_data); + +int verbose_on_frame_send_callback(nghttp2_session *session, + const nghttp2_frame *frame, void *user_data); + +int verbose_on_data_chunk_recv_callback(nghttp2_session *session, uint8_t flags, + int32_t stream_id, const uint8_t *data, + size_t len, void *user_data); + +// Returns difference between |a| and |b| in milliseconds, assuming +// |a| is more recent than |b|. +template +std::chrono::milliseconds time_delta(const TimePoint &a, const TimePoint &b) { + return std::chrono::duration_cast(a - b); +} + +// Resets timer +void reset_timer(); + +// Returns the duration since timer reset. +std::chrono::milliseconds get_timer(); + +// Returns current time point. +std::chrono::steady_clock::time_point get_time(); + +void print_timer(); + +// Setting true will print characters with ANSI color escape codes +// when printing HTTP2 frames. This function changes a static +// variable. +void set_color_output(bool f); + +// Set output file when printing HTTP2 frames. By default, stdout is +// used. +void set_output(FILE *file); + +ssize_t deflate_data(uint8_t *out, size_t outlen, const uint8_t *in, + size_t inlen); + +} // namespace nghttp2 + +#endif // APP_HELPER_H diff --git a/src/asio_client_request.cc b/src/asio_client_request.cc new file mode 100644 index 0000000..1fa93ca --- /dev/null +++ b/src/asio_client_request.cc @@ -0,0 +1,67 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2015 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "nghttp2_config.h" + +#include + +#include "asio_client_request_impl.h" + +#include "template.h" + +namespace nghttp2 { +namespace asio_http2 { +namespace client { + +request::request() : impl_(make_unique()) {} + +request::~request() {} + +void request::write_trailer(header_map h) const { + impl_->write_trailer(std::move(h)); +} + +void request::cancel(uint32_t error_code) const { impl_->cancel(error_code); } + +void request::on_response(response_cb cb) const { + impl_->on_response(std::move(cb)); +} + +void request::on_push(request_cb cb) const { impl_->on_push(std::move(cb)); } + +void request::on_close(close_cb cb) const { impl_->on_close(std::move(cb)); } + +const uri_ref &request::uri() const { return impl_->uri(); } + +const std::string &request::method() const { return impl_->method(); } + +const header_map &request::header() const { return impl_->header(); } + +void request::resume() const { impl_->resume(); } + +request_impl &request::impl() const { return *impl_; } + +} // namespace client +} // namespace asio_http2 +} // namespace nghttp2 diff --git a/src/asio_client_request_impl.cc b/src/asio_client_request_impl.cc new file mode 100644 index 0000000..5512c96 --- /dev/null +++ b/src/asio_client_request_impl.cc @@ -0,0 +1,110 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2015 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "asio_client_request_impl.h" + +#include "asio_client_stream.h" +#include "asio_client_session_impl.h" +#include "template.h" + +namespace nghttp2 { +namespace asio_http2 { +namespace client { + +request_impl::request_impl() : strm_(nullptr) {} + +void request_impl::write_trailer(header_map h) { + auto sess = strm_->session(); + sess->write_trailer(*strm_, std::move(h)); +} + +void request_impl::cancel(uint32_t error_code) { + auto sess = strm_->session(); + sess->cancel(*strm_, error_code); +} + +void request_impl::on_response(response_cb cb) { response_cb_ = std::move(cb); } + +void request_impl::call_on_response(response &res) { + if (response_cb_) { + response_cb_(res); + } +} + +void request_impl::on_push(request_cb cb) { push_request_cb_ = std::move(cb); } + +void request_impl::call_on_push(request &push_req) { + if (push_request_cb_) { + push_request_cb_(push_req); + } +}; + +void request_impl::on_close(close_cb cb) { close_cb_ = std::move(cb); } + +void request_impl::call_on_close(uint32_t error_code) { + if (close_cb_) { + close_cb_(error_code); + } +} + +void request_impl::on_read(generator_cb cb) { generator_cb_ = std::move(cb); } + +generator_cb::result_type request_impl::call_on_read(uint8_t *buf, + std::size_t len, + uint32_t *data_flags) { + if (generator_cb_) { + return generator_cb_(buf, len, data_flags); + } + + *data_flags |= NGHTTP2_DATA_FLAG_EOF; + + return 0; +} + +void request_impl::resume() { + auto sess = strm_->session(); + sess->resume(*strm_); +} + +void request_impl::header(header_map h) { header_ = std::move(h); } + +header_map &request_impl::header() { return header_; } + +const header_map &request_impl::header() const { return header_; } + +void request_impl::stream(class stream *strm) { strm_ = strm; } + +void request_impl::uri(uri_ref uri) { uri_ = std::move(uri); } + +const uri_ref &request_impl::uri() const { return uri_; } + +uri_ref &request_impl::uri() { return uri_; } + +void request_impl::method(std::string s) { method_ = std::move(s); } + +const std::string &request_impl::method() const { return method_; } + +} // namespace client +} // namespace asio_http2 +} // namespace nghttp2 diff --git a/src/asio_client_request_impl.h b/src/asio_client_request_impl.h new file mode 100644 index 0000000..802a83d --- /dev/null +++ b/src/asio_client_request_impl.h @@ -0,0 +1,93 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2015 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef ASIO_CLIENT_REQUEST_IMPL_H +#define ASIO_CLIENT_REQUEST_IMPL_H + +#include "nghttp2_config.h" + +#include + +namespace nghttp2 { +namespace asio_http2 { +namespace client { + +class response; +class stream; + +class request_impl { +public: + request_impl(); + + request_impl(const request_impl &) = delete; + request_impl &operator=(const request_impl &) = delete; + + void write_trailer(header_map h); + + void cancel(uint32_t error_code); + + void on_response(response_cb cb); + void call_on_response(response &res); + + void on_push(request_cb cb); + void call_on_push(request &push_req); + + void on_close(close_cb cb); + void call_on_close(uint32_t error_code); + + void on_read(generator_cb cb); + generator_cb::result_type call_on_read(uint8_t *buf, std::size_t len, + uint32_t *data_flags); + + void resume(); + + void header(header_map h); + header_map &header(); + const header_map &header() const; + + void stream(class stream *strm); + + void uri(uri_ref uri); + const uri_ref &uri() const; + uri_ref &uri(); + + void method(std::string s); + const std::string &method() const; + +private: + header_map header_; + response_cb response_cb_; + request_cb push_request_cb_; + close_cb close_cb_; + generator_cb generator_cb_; + class stream *strm_; + uri_ref uri_; + std::string method_; +}; + +} // namespace client +} // namespace asio_http2 +} // namespace nghttp2 + +#endif // ASIO_CLIENT_REQUEST_IMPL_H diff --git a/src/asio_client_response.cc b/src/asio_client_response.cc new file mode 100644 index 0000000..4805422 --- /dev/null +++ b/src/asio_client_response.cc @@ -0,0 +1,53 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2015 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "nghttp2_config.h" + +#include + +#include "asio_client_response_impl.h" + +#include "template.h" + +namespace nghttp2 { +namespace asio_http2 { +namespace client { + +response::response() : impl_(make_unique()) {} + +response::~response() {} + +void response::on_data(data_cb cb) const { impl_->on_data(std::move(cb)); } + +int response::status_code() const { return impl_->status_code(); } + +int64_t response::content_length() const { return impl_->content_length(); } + +const header_map &response::header() const { return impl_->header(); } + +response_impl &response::impl() const { return *impl_; } + +} // namespace client +} // namespace asio_http2 +} // namespace nghttp2 diff --git a/src/asio_client_response_impl.cc b/src/asio_client_response_impl.cc new file mode 100644 index 0000000..fce25a4 --- /dev/null +++ b/src/asio_client_response_impl.cc @@ -0,0 +1,57 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2015 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "asio_client_response_impl.h" + +#include "template.h" + +namespace nghttp2 { +namespace asio_http2 { +namespace client { + +response_impl::response_impl() : content_length_(-1), status_code_(0) {} + +void response_impl::on_data(data_cb cb) { data_cb_ = std::move(cb); } + +void response_impl::call_on_data(const uint8_t *data, std::size_t len) { + if (data_cb_) { + data_cb_(data, len); + } +} + +void response_impl::status_code(int sc) { status_code_ = sc; } + +int response_impl::status_code() const { return status_code_; } + +void response_impl::content_length(int64_t n) { content_length_ = n; } + +int64_t response_impl::content_length() const { return content_length_; } + +header_map &response_impl::header() { return header_; } + +const header_map &response_impl::header() const { return header_; } + +} // namespace client +} // namespace asio_http2 +} // namespace nghttp2 diff --git a/src/asio_client_response_impl.h b/src/asio_client_response_impl.h new file mode 100644 index 0000000..e2c2286 --- /dev/null +++ b/src/asio_client_response_impl.h @@ -0,0 +1,69 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2015 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef ASIO_CLIENT_RESPONSE_IMPL_H +#define ASIO_CLIENT_RESPONSE_IMPL_H + +#include "nghttp2_config.h" + +#include + +namespace nghttp2 { +namespace asio_http2 { +namespace client { + +class response_impl { +public: + response_impl(); + + response_impl(const response_impl &) = delete; + response_impl &operator=(const response_impl &) = delete; + + void on_data(data_cb cb); + + void call_on_data(const uint8_t *data, std::size_t len); + + void status_code(int sc); + int status_code() const; + + void content_length(int64_t n); + int64_t content_length() const; + + header_map &header(); + const header_map &header() const; + +private: + data_cb data_cb_; + + header_map header_; + + int64_t content_length_; + int status_code_; +}; + +} // namespace client +} // namespace asio_http2 +} // namespace nghttp2 + +#endif // ASIO_CLIENT_RESPONSE_IMPL_H diff --git a/src/asio_client_session.cc b/src/asio_client_session.cc new file mode 100644 index 0000000..aaee1e7 --- /dev/null +++ b/src/asio_client_session.cc @@ -0,0 +1,98 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2015 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "nghttp2_config.h" + +#include + +#include "asio_client_session_tcp_impl.h" +#include "asio_client_session_tls_impl.h" +#include "asio_common.h" +#include "template.h" + +namespace nghttp2 { +namespace asio_http2 { +namespace client { + +using boost::asio::ip::tcp; + +session::session(boost::asio::io_service &io_service, const std::string &host, + const std::string &service) + : impl_(make_unique(io_service, host, service)) {} + +session::session(boost::asio::io_service &io_service, + boost::asio::ssl::context &tls_ctx, const std::string &host, + const std::string &service) + : impl_(make_unique(io_service, tls_ctx, host, service)) { +} + +session::~session() {} + +session::session(session &&other) noexcept : impl_(std::move(other.impl_)) {} + +session &session::operator=(session &&other) noexcept { + if (this == &other) { + return *this; + } + + impl_ = std::move(other.impl_); + return *this; +} + +void session::on_connect(connect_cb cb) const { + impl_->on_connect(std::move(cb)); +} + +void session::on_error(error_cb cb) const { impl_->on_error(std::move(cb)); } + +void session::shutdown() const { impl_->shutdown(); } + +boost::asio::io_service &session::io_service() const { + return impl_->io_service(); +} + +const request *session::submit(boost::system::error_code &ec, + const std::string &method, + const std::string &uri, header_map h) const { + return impl_->submit(ec, method, uri, generator_cb(), std::move(h)); +} + +const request *session::submit(boost::system::error_code &ec, + const std::string &method, + const std::string &uri, std::string data, + header_map h) const { + return impl_->submit(ec, method, uri, string_generator(std::move(data)), + std::move(h)); +} + +const request *session::submit(boost::system::error_code &ec, + const std::string &method, + const std::string &uri, generator_cb cb, + header_map h) const { + return impl_->submit(ec, method, uri, std::move(cb), std::move(h)); +} + +} // namespace client +} // namespace asio_http2 +} // nghttp2 diff --git a/src/asio_client_session_impl.cc b/src/asio_client_session_impl.cc new file mode 100644 index 0000000..9cb57c9 --- /dev/null +++ b/src/asio_client_session_impl.cc @@ -0,0 +1,625 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2015 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "asio_client_session_impl.h" + +#include + +#include "asio_client_stream.h" +#include "asio_client_request_impl.h" +#include "asio_client_response_impl.h" +#include "asio_common.h" +#include "template.h" +#include "util.h" +#include "http2.h" + +namespace nghttp2 { +namespace asio_http2 { +namespace client { + +session_impl::session_impl(boost::asio::io_service &io_service) + : wblen_(0), io_service_(io_service), resolver_(io_service), + session_(nullptr), data_pending_(nullptr), data_pendinglen_(0), + writing_(false), inside_callback_(false) {} + +session_impl::~session_impl() { + // finish up all active stream + for (auto &p : streams_) { + auto &strm = p.second; + auto &req = strm->request().impl(); + req.call_on_close(NGHTTP2_INTERNAL_ERROR); + } + + nghttp2_session_del(session_); +} + +void session_impl::start_resolve(const std::string &host, + const std::string &service) { + resolver_.async_resolve({host, service}, + [this](const boost::system::error_code &ec, + tcp::resolver::iterator endpoint_it) { + if (ec) { + not_connected(ec); + return; + } + + start_connect(endpoint_it); + }); +} + +void session_impl::connected(tcp::resolver::iterator endpoint_it) { + if (!setup_session()) { + return; + } + + socket().set_option(boost::asio::ip::tcp::no_delay(true)); + + do_write(); + do_read(); + + auto &connect_cb = on_connect(); + if (connect_cb) { + connect_cb(endpoint_it); + } +} + +void session_impl::not_connected(const boost::system::error_code &ec) { + call_error_cb(ec); +} + +void session_impl::on_connect(connect_cb cb) { connect_cb_ = std::move(cb); } + +void session_impl::on_error(error_cb cb) { error_cb_ = std::move(cb); } + +const connect_cb &session_impl::on_connect() const { return connect_cb_; } + +const error_cb &session_impl::on_error() const { return error_cb_; } + +void session_impl::call_error_cb(const boost::system::error_code &ec) { + auto &error_cb = on_error(); + if (!error_cb) { + return; + } + error_cb(ec); +} + +namespace { +int on_begin_headers_callback(nghttp2_session *session, + const nghttp2_frame *frame, void *user_data) { + if (frame->hd.type != NGHTTP2_PUSH_PROMISE) { + return 0; + } + + auto sess = static_cast(user_data); + sess->create_push_stream(frame->push_promise.promised_stream_id); + + return 0; +} +} // namespace + +namespace { +int on_header_callback(nghttp2_session *session, const nghttp2_frame *frame, + const uint8_t *name, size_t namelen, + const uint8_t *value, size_t valuelen, uint8_t flags, + void *user_data) { + auto sess = static_cast(user_data); + stream *strm; + + switch (frame->hd.type) { + case NGHTTP2_HEADERS: { + strm = sess->find_stream(frame->hd.stream_id); + if (!strm) { + return 0; + } + + // ignore trailers + if (frame->headers.cat == NGHTTP2_HCAT_HEADERS && + !strm->expect_final_response()) { + return 0; + } + + auto token = http2::lookup_token(name, namelen); + + auto &res = strm->response().impl(); + if (token == http2::HD__STATUS) { + res.status_code(util::parse_uint(value, valuelen)); + } else { + + if (token == http2::HD_CONTENT_LENGTH) { + res.content_length(util::parse_uint(value, valuelen)); + } + + res.header().emplace( + std::string(name, name + namelen), + header_value{std::string(value, value + valuelen), + (flags & NGHTTP2_NV_FLAG_NO_INDEX) != 0}); + } + break; + } + case NGHTTP2_PUSH_PROMISE: { + strm = sess->find_stream(frame->push_promise.promised_stream_id); + if (!strm) { + return 0; + } + + auto &req = strm->request().impl(); + auto &uri = req.uri(); + + switch (http2::lookup_token(name, namelen)) { + case http2::HD__METHOD: + req.method(std::string(value, value + valuelen)); + break; + case http2::HD__SCHEME: + uri.scheme.assign(value, value + valuelen); + break; + case http2::HD__PATH: + split_path(uri, value, value + valuelen); + break; + case http2::HD__AUTHORITY: + uri.host.assign(value, value + valuelen); + break; + case http2::HD_HOST: + if (uri.host.empty()) { + uri.host.assign(value, value + valuelen); + } + // fall through + default: + req.header().emplace( + std::string(name, name + namelen), + header_value{std::string(value, value + valuelen), + (flags & NGHTTP2_NV_FLAG_NO_INDEX) != 0}); + } + + break; + } + default: + return 0; + } + + return 0; +} +} // namespace + +namespace { +int on_frame_recv_callback(nghttp2_session *session, const nghttp2_frame *frame, + void *user_data) { + auto sess = static_cast(user_data); + auto strm = sess->find_stream(frame->hd.stream_id); + + switch (frame->hd.type) { + case NGHTTP2_DATA: { + if (!strm) { + return 0; + } + if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) { + strm->response().impl().call_on_data(nullptr, 0); + } + break; + } + case NGHTTP2_HEADERS: { + if (!strm) { + return 0; + } + + // ignore trailers + if (frame->headers.cat == NGHTTP2_HCAT_HEADERS && + !strm->expect_final_response()) { + return 0; + } + + if (strm->expect_final_response()) { + // wait for final response + return 0; + } + + auto &req = strm->request().impl(); + req.call_on_response(strm->response()); + if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) { + strm->response().impl().call_on_data(nullptr, 0); + } + break; + } + case NGHTTP2_PUSH_PROMISE: { + if (!strm) { + return 0; + } + + auto push_strm = sess->find_stream(frame->push_promise.promised_stream_id); + if (!push_strm) { + return 0; + } + + strm->request().impl().call_on_push(push_strm->request()); + + break; + } + } + return 0; +} +} // namespace + +namespace { +int on_data_chunk_recv_callback(nghttp2_session *session, uint8_t flags, + int32_t stream_id, const uint8_t *data, + size_t len, void *user_data) { + auto sess = static_cast(user_data); + auto strm = sess->find_stream(stream_id); + if (!strm) { + return 0; + } + + auto &res = strm->response().impl(); + res.call_on_data(data, len); + + return 0; +} +} // namespace + +namespace { +int on_stream_close_callback(nghttp2_session *session, int32_t stream_id, + uint32_t error_code, void *user_data) { + auto sess = static_cast(user_data); + auto strm = sess->pop_stream(stream_id); + if (!strm) { + return 0; + } + + strm->request().impl().call_on_close(error_code); + + return 0; +} +} // namespace + +bool session_impl::setup_session() { + nghttp2_session_callbacks *callbacks; + nghttp2_session_callbacks_new(&callbacks); + auto cb_del = defer(nghttp2_session_callbacks_del, callbacks); + + nghttp2_session_callbacks_set_on_begin_headers_callback( + callbacks, on_begin_headers_callback); + nghttp2_session_callbacks_set_on_header_callback(callbacks, + on_header_callback); + nghttp2_session_callbacks_set_on_frame_recv_callback(callbacks, + on_frame_recv_callback); + nghttp2_session_callbacks_set_on_data_chunk_recv_callback( + callbacks, on_data_chunk_recv_callback); + nghttp2_session_callbacks_set_on_stream_close_callback( + callbacks, on_stream_close_callback); + + auto rv = nghttp2_session_client_new(&session_, callbacks, this); + if (rv != 0) { + call_error_cb(make_error_code(static_cast(rv))); + return false; + } + + const uint32_t window_size = 256 * 1024 * 1024; + + std::array iv{ + {{NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100}, + // typically client is just a *sink* and just process data as + // much as possible. Use large window size by default. + {NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE, window_size}}}; + nghttp2_submit_settings(session_, NGHTTP2_FLAG_NONE, iv.data(), iv.size()); + // increase connection window size up to window_size + nghttp2_submit_window_update(session_, NGHTTP2_FLAG_NONE, 0, + window_size - + NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE); + return true; +} + +int session_impl::write_trailer(stream &strm, header_map h) { + int rv; + auto nva = std::vector(); + nva.reserve(h.size()); + for (auto &hd : h) { + nva.push_back(nghttp2::http2::make_nv(hd.first, hd.second.value, + hd.second.sensitive)); + } + + rv = nghttp2_submit_trailer(session_, strm.stream_id(), nva.data(), + nva.size()); + + if (rv != 0) { + return -1; + } + + signal_write(); + + return 0; +} + +void session_impl::cancel(stream &strm, uint32_t error_code) { + nghttp2_submit_rst_stream(session_, NGHTTP2_FLAG_NONE, strm.stream_id(), + error_code); + signal_write(); +} + +void session_impl::resume(stream &strm) { + nghttp2_session_resume_data(session_, strm.stream_id()); + signal_write(); +} + +stream *session_impl::find_stream(int32_t stream_id) { + auto it = streams_.find(stream_id); + if (it == std::end(streams_)) { + return nullptr; + } + return (*it).second.get(); +} + +std::unique_ptr session_impl::pop_stream(int32_t stream_id) { + auto it = streams_.find(stream_id); + if (it == std::end(streams_)) { + return nullptr; + } + auto strm = std::move((*it).second); + streams_.erase(it); + return strm; +} + +stream *session_impl::create_push_stream(int32_t stream_id) { + auto strm = create_stream(); + strm->stream_id(stream_id); + auto p = streams_.emplace(stream_id, std::move(strm)); + assert(p.second); + return (*p.first).second.get(); +} + +std::unique_ptr session_impl::create_stream() { + return make_unique(this); +} + +const request *session_impl::submit(boost::system::error_code &ec, + const std::string &method, + const std::string &uri, generator_cb cb, + header_map h) { + ec.clear(); + + http_parser_url u{}; + // TODO Handle CONNECT method + if (http_parser_parse_url(uri.c_str(), uri.size(), 0, &u) != 0) { + ec = make_error_code(boost::system::errc::invalid_argument); + return nullptr; + } + + if ((u.field_set & (1 << UF_SCHEMA)) == 0 || + (u.field_set & (1 << UF_HOST)) == 0) { + ec = make_error_code(boost::system::errc::invalid_argument); + return nullptr; + } + + auto strm = create_stream(); + auto &req = strm->request().impl(); + auto &uref = req.uri(); + + http2::copy_url_component(uref.scheme, &u, UF_SCHEMA, uri.c_str()); + http2::copy_url_component(uref.host, &u, UF_HOST, uri.c_str()); + http2::copy_url_component(uref.raw_path, &u, UF_PATH, uri.c_str()); + http2::copy_url_component(uref.raw_query, &u, UF_QUERY, uri.c_str()); + + if (util::ipv6_numeric_addr(uref.host.c_str())) { + uref.host = "[" + uref.host; + uref.host += "]"; + } + if (u.field_set & (1 << UF_PORT)) { + uref.host += ":"; + uref.host += util::utos(u.port); + } + + if (uref.raw_path.empty()) { + uref.raw_path = "/"; + } + + uref.path = percent_decode(uref.raw_path); + + auto path = uref.raw_path; + if (u.field_set & (1 << UF_QUERY)) { + path += "?"; + path += uref.raw_query; + } + + auto nva = std::vector(); + nva.reserve(3 + h.size()); + nva.push_back(http2::make_nv_ls(":method", method)); + nva.push_back(http2::make_nv_ls(":scheme", uref.scheme)); + nva.push_back(http2::make_nv_ls(":path", path)); + nva.push_back(http2::make_nv_ls(":authority", uref.host)); + for (auto &kv : h) { + nva.push_back( + http2::make_nv(kv.first, kv.second.value, kv.second.sensitive)); + } + + req.header(std::move(h)); + + nghttp2_data_provider *prdptr = nullptr; + nghttp2_data_provider prd; + + if (cb) { + strm->request().impl().on_read(std::move(cb)); + prd.source.ptr = strm.get(); + prd.read_callback = + [](nghttp2_session *session, int32_t stream_id, uint8_t *buf, + size_t length, uint32_t *data_flags, nghttp2_data_source *source, + void *user_data) -> ssize_t { + auto strm = static_cast(source->ptr); + return strm->request().impl().call_on_read(buf, length, data_flags); + }; + prdptr = &prd; + } + + auto stream_id = nghttp2_submit_request(session_, nullptr, nva.data(), + nva.size(), prdptr, strm.get()); + if (stream_id < 0) { + ec = make_error_code(static_cast(stream_id)); + return nullptr; + } + + signal_write(); + + strm->stream_id(stream_id); + + auto p = streams_.emplace(stream_id, std::move(strm)); + assert(p.second); + return &(*p.first).second->request(); +} + +void session_impl::shutdown() { + nghttp2_session_terminate_session(session_, NGHTTP2_NO_ERROR); + signal_write(); +} + +boost::asio::io_service &session_impl::io_service() { return io_service_; } + +void session_impl::signal_write() { + if (!inside_callback_) { + do_write(); + } +} + +bool session_impl::should_stop() const { + return !writing_ && !nghttp2_session_want_read(session_) && + !nghttp2_session_want_write(session_); +} + +namespace { +struct callback_guard { + callback_guard(session_impl &sess) : sess(sess) { sess.enter_callback(); } + ~callback_guard() { sess.leave_callback(); } + + session_impl &sess; +}; +} // namespace + +void session_impl::enter_callback() { + assert(!inside_callback_); + inside_callback_ = true; +} + +void session_impl::leave_callback() { + assert(inside_callback_); + inside_callback_ = false; +} + +void session_impl::do_read() { + read_socket([this](const boost::system::error_code &ec, + std::size_t bytes_transferred) { + if (ec) { + if (ec.value() == boost::asio::error::operation_aborted) { + call_error_cb(ec); + shutdown_socket(); + } + return; + } + + { + callback_guard cg(*this); + + auto rv = + nghttp2_session_mem_recv(session_, rb_.data(), bytes_transferred); + + if (rv != static_cast(bytes_transferred)) { + call_error_cb(make_error_code( + static_cast(rv < 0 ? rv : NGHTTP2_ERR_PROTO))); + shutdown_socket(); + return; + } + } + + do_write(); + + if (should_stop()) { + shutdown_socket(); + return; + } + + do_read(); + }); +} + +void session_impl::do_write() { + if (writing_) { + return; + } + + if (data_pending_) { + std::copy_n(data_pending_, data_pendinglen_, std::begin(wb_) + wblen_); + + wblen_ += data_pendinglen_; + + data_pending_ = nullptr; + data_pendinglen_ = 0; + } + + { + callback_guard cg(*this); + + for (;;) { + const uint8_t *data; + auto n = nghttp2_session_mem_send(session_, &data); + if (n < 0) { + call_error_cb(make_error_code(static_cast(n))); + shutdown_socket(); + return; + } + + if (n == 0) { + break; + } + + if (wblen_ + n > wb_.size()) { + data_pending_ = data; + data_pendinglen_ = n; + + break; + } + + std::copy_n(data, n, std::begin(wb_) + wblen_); + + wblen_ += n; + } + } + + if (wblen_ == 0) { + return; + } + + writing_ = true; + + write_socket([this](const boost::system::error_code &ec, std::size_t n) { + if (ec) { + call_error_cb(ec); + shutdown_socket(); + return; + } + + wblen_ = 0; + writing_ = false; + + do_write(); + }); +} + +} // namespace client +} // namespace asio_http2 +} // nghttp2 diff --git a/src/asio_client_session_impl.h b/src/asio_client_session_impl.h new file mode 100644 index 0000000..88bbad8 --- /dev/null +++ b/src/asio_client_session_impl.h @@ -0,0 +1,123 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2015 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef ASIO_CLIENT_SESSION_IMPL_H +#define ASIO_CLIENT_SESSION_IMPL_H + +#include "nghttp2_config.h" + +#include + +#include + +namespace nghttp2 { +namespace asio_http2 { +namespace client { + +class stream; + +using boost::asio::ip::tcp; + +class session_impl { +public: + session_impl(boost::asio::io_service &io_service); + virtual ~session_impl(); + + void start_resolve(const std::string &host, const std::string &service); + + void connected(tcp::resolver::iterator endpoint_it); + void not_connected(const boost::system::error_code &ec); + + void on_connect(connect_cb cb); + void on_error(error_cb cb); + + const connect_cb &on_connect() const; + const error_cb &on_error() const; + + int write_trailer(stream &strm, header_map h); + + void cancel(stream &strm, uint32_t error_code); + void resume(stream &strm); + + std::unique_ptr create_stream(); + std::unique_ptr pop_stream(int32_t stream_id); + stream *create_push_stream(int32_t stream_id); + stream *find_stream(int32_t stream_id); + + const request *submit(boost::system::error_code &ec, + const std::string &method, const std::string &uri, + generator_cb cb, header_map h); + + virtual void start_connect(tcp::resolver::iterator endpoint_it) = 0; + virtual tcp::socket &socket() = 0; + virtual void read_socket(std::function< + void(const boost::system::error_code &ec, std::size_t n)> h) = 0; + virtual void write_socket(std::function< + void(const boost::system::error_code &ec, std::size_t n)> h) = 0; + virtual void shutdown_socket() = 0; + + void shutdown(); + + boost::asio::io_service &io_service(); + + void signal_write(); + + void enter_callback(); + void leave_callback(); + + void do_read(); + void do_write(); + +protected: + boost::array rb_; + boost::array wb_; + std::size_t wblen_; + +private: + bool should_stop() const; + bool setup_session(); + void call_error_cb(const boost::system::error_code &ec); + + boost::asio::io_service &io_service_; + tcp::resolver resolver_; + + std::map> streams_; + + connect_cb connect_cb_; + error_cb error_cb_; + + nghttp2_session *session_; + + const uint8_t *data_pending_; + std::size_t data_pendinglen_; + + bool writing_; + bool inside_callback_; +}; + +} // namespace client +} // namespace asio_http2 +} // namespace nghttp2 + +#endif // ASIO_CLIENT_SESSION_IMPL_H diff --git a/src/asio_client_session_tcp_impl.cc b/src/asio_client_session_tcp_impl.cc new file mode 100644 index 0000000..41195ba --- /dev/null +++ b/src/asio_client_session_tcp_impl.cc @@ -0,0 +1,69 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2015 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "asio_client_session_tcp_impl.h" + +namespace nghttp2 { +namespace asio_http2 { +namespace client { + +session_tcp_impl::session_tcp_impl(boost::asio::io_service &io_service, + const std::string &host, + const std::string &service) + : session_impl(io_service), socket_(io_service) { + start_resolve(host, service); +} + +session_tcp_impl::~session_tcp_impl() {} + +void session_tcp_impl::start_connect(tcp::resolver::iterator endpoint_it) { + boost::asio::async_connect(socket_, endpoint_it, + [this](const boost::system::error_code &ec, + tcp::resolver::iterator endpoint_it) { + if (ec) { + not_connected(ec); + return; + } + + connected(endpoint_it); + }); +} + +tcp::socket &session_tcp_impl::socket() { return socket_; } + +void session_tcp_impl::read_socket( + std::function h) { + socket_.async_read_some(boost::asio::buffer(rb_), h); +} + +void session_tcp_impl::write_socket( + std::function h) { + boost::asio::async_write(socket_, boost::asio::buffer(wb_, wblen_), h); +} + +void session_tcp_impl::shutdown_socket() { socket_.close(); } + +} // namespace client +} // namespace asio_http2 +} // namespace nghttp2 diff --git a/src/asio_client_session_tcp_impl.h b/src/asio_client_session_tcp_impl.h new file mode 100644 index 0000000..6138828 --- /dev/null +++ b/src/asio_client_session_tcp_impl.h @@ -0,0 +1,60 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2015 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef ASIO_CLIENT_SESSION_TCP_IMPL_H +#define ASIO_CLIENT_SESSION_TCP_IMPL_H + +#include "asio_client_session_impl.h" + +#include + +namespace nghttp2 { +namespace asio_http2 { +namespace client { + +using boost::asio::ip::tcp; + +class session_tcp_impl : public session_impl { +public: + session_tcp_impl(boost::asio::io_service &io_service, const std::string &host, + const std::string &service); + virtual ~session_tcp_impl(); + + virtual void start_connect(tcp::resolver::iterator endpoint_it); + virtual tcp::socket &socket(); + virtual void read_socket(std::function< + void(const boost::system::error_code &ec, std::size_t n)> h); + virtual void write_socket(std::function< + void(const boost::system::error_code &ec, std::size_t n)> h); + virtual void shutdown_socket(); + +private: + tcp::socket socket_; +}; + +} // namespace client +} // namespace asio_http2 +} // namespace nghttp2 + +#endif // ASIO_CLIENT_SESSION_TCP_IMPL_H diff --git a/src/asio_client_session_tls_impl.cc b/src/asio_client_session_tls_impl.cc new file mode 100644 index 0000000..cde2ab3 --- /dev/null +++ b/src/asio_client_session_tls_impl.cc @@ -0,0 +1,85 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2015 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "asio_client_session_tls_impl.h" + +namespace nghttp2 { +namespace asio_http2 { +namespace client { + +session_tls_impl::session_tls_impl(boost::asio::io_service &io_service, + boost::asio::ssl::context &tls_ctx, + const std::string &host, + const std::string &service) + : session_impl(io_service), socket_(io_service, tls_ctx) { + // this callback setting is no effect is + // ssl::context::set_verify_mode(boost::asio::ssl::verify_peer) is + // not used, which is what we want. + socket_.set_verify_callback(boost::asio::ssl::rfc2818_verification(host)); + + start_resolve(host, service); +} + +session_tls_impl::~session_tls_impl() {} + +void session_tls_impl::start_connect(tcp::resolver::iterator endpoint_it) { + boost::asio::async_connect(socket(), endpoint_it, + [this](const boost::system::error_code &ec, + tcp::resolver::iterator endpoint_it) { + if (ec) { + not_connected(ec); + return; + } + + socket_.async_handshake( + boost::asio::ssl::stream_base::client, + [this, endpoint_it](const boost::system::error_code &ec) { + if (ec) { + not_connected(ec); + return; + } + connected(endpoint_it); + }); + }); +} + +tcp::socket &session_tls_impl::socket() { return socket_.next_layer(); } + +void session_tls_impl::read_socket( + std::function h) { + socket_.async_read_some(boost::asio::buffer(rb_), h); +} + +void session_tls_impl::write_socket( + std::function h) { + boost::asio::async_write(socket_, boost::asio::buffer(wb_, wblen_), h); +} + +void session_tls_impl::shutdown_socket() { + socket_.async_shutdown([](const boost::system::error_code &ec) {}); +} + +} // namespace client +} // namespace asio_http2 +} // namespace nghttp2 diff --git a/src/asio_client_session_tls_impl.h b/src/asio_client_session_tls_impl.h new file mode 100644 index 0000000..484e3d4 --- /dev/null +++ b/src/asio_client_session_tls_impl.h @@ -0,0 +1,63 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2015 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef ASIO_CLIENT_SESSION_TLS_IMPL_H +#define ASIO_CLIENT_SESSION_TLS_IMPL_H + +#include "asio_client_session_impl.h" + +#include + +namespace nghttp2 { +namespace asio_http2 { +namespace client { + +using boost::asio::ip::tcp; + +using ssl_socket = boost::asio::ssl::stream; + +class session_tls_impl : public session_impl { +public: + session_tls_impl(boost::asio::io_service &io_service, + boost::asio::ssl::context &tls_ctx, const std::string &host, + const std::string &service); + virtual ~session_tls_impl(); + + virtual void start_connect(tcp::resolver::iterator endpoint_it); + virtual tcp::socket &socket(); + virtual void read_socket(std::function< + void(const boost::system::error_code &ec, std::size_t n)> h); + virtual void write_socket(std::function< + void(const boost::system::error_code &ec, std::size_t n)> h); + virtual void shutdown_socket(); + +private: + ssl_socket socket_; +}; + +} // namespace client +} // namespace asio_http2 +} // namespace nghttp2 + +#endif // ASIO_CLIENT_SESSION_TLS_IMPL_H diff --git a/src/asio_client_stream.cc b/src/asio_client_stream.cc new file mode 100644 index 0000000..2d3aa5b --- /dev/null +++ b/src/asio_client_stream.cc @@ -0,0 +1,59 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2015 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "asio_client_stream.h" + +#include "asio_client_request_impl.h" +#include "asio_client_response_impl.h" +#include "asio_client_session_impl.h" + +namespace nghttp2 { +namespace asio_http2 { +namespace client { + +stream::stream(session_impl *sess) : sess_(sess), stream_id_(0) { + request_.impl().stream(this); +} + +void stream::stream_id(int32_t stream_id) { stream_id_ = stream_id; } + +int32_t stream::stream_id() const { return stream_id_; } + +class request &stream::request() { + return request_; +} + +class response &stream::response() { + return response_; +} + +session_impl *stream::session() const { return sess_; } + +bool stream::expect_final_response() const { + return response_.status_code() / 100 == 1; +} + +} // namespace client +} // namespace asio_http2 +} // namespace nghttp2 diff --git a/src/asio_client_stream.h b/src/asio_client_stream.h new file mode 100644 index 0000000..e3e027a --- /dev/null +++ b/src/asio_client_stream.h @@ -0,0 +1,68 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2015 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef ASIO_CLIENT_STREAM_H +#define ASIO_CLIENT_STREAM_H + +#include "nghttp2_config.h" + +#include + +namespace nghttp2 { +namespace asio_http2 { +namespace client { + +class request; +class response; +class session_impl; + +class stream { +public: + stream(session_impl *sess); + + stream(const stream &) = delete; + stream &operator=(const stream &) = delete; + + void stream_id(int32_t stream_id); + int32_t stream_id() const; + + class request &request(); + class response &response(); + + session_impl *session() const; + + bool expect_final_response() const; + +private: + nghttp2::asio_http2::client::request request_; + nghttp2::asio_http2::client::response response_; + session_impl *sess_; + uint32_t stream_id_; +}; + +} // namespace client +} // namespace asio_http2 +} // namespace nghttp2 + +#endif // ASIO_CLIENT_STREAM_H diff --git a/src/asio_client_tls_context.cc b/src/asio_client_tls_context.cc new file mode 100644 index 0000000..5513834 --- /dev/null +++ b/src/asio_client_tls_context.cc @@ -0,0 +1,64 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2015 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "asio_client_tls_context.h" + +#include + +#include + +#include "ssl.h" +#include "util.h" + +namespace nghttp2 { +namespace asio_http2 { +namespace client { + +namespace { +int client_select_next_proto_cb(SSL *ssl, unsigned char **out, + unsigned char *outlen, const unsigned char *in, + unsigned int inlen, void *arg) { + if (!util::select_h2(const_cast(out), outlen, in, + inlen)) { + return SSL_TLSEXT_ERR_NOACK; + } + return SSL_TLSEXT_ERR_OK; +} +} // namespace + +boost::system::error_code +configure_tls_context(boost::system::error_code &ec, + boost::asio::ssl::context &tls_ctx) { + ec.clear(); + + auto ctx = tls_ctx.native_handle(); + + SSL_CTX_set_next_proto_select_cb(ctx, client_select_next_proto_cb, nullptr); + + return ec; +} + +} // namespace client +} // namespace asio_http2 +} // namespace nghttp2 diff --git a/src/asio_client_tls_context.h b/src/asio_client_tls_context.h new file mode 100644 index 0000000..6287dab --- /dev/null +++ b/src/asio_client_tls_context.h @@ -0,0 +1,32 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2015 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef ASIO_CLIENT_TLS_CONTEXT_H +#define ASIO_CLIENT_TLS_CONTEXT_H + +#include "nghttp2_config.h" + +#include + +#endif // ASIO_CLIENT_TLS_CONTEXT_H diff --git a/src/asio_common.cc b/src/asio_common.cc new file mode 100644 index 0000000..c3f98e7 --- /dev/null +++ b/src/asio_common.cc @@ -0,0 +1,148 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2015 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "asio_common.h" + +#include + +#include "util.h" +#include "template.h" +#include "http2.h" + +namespace nghttp2 { +namespace asio_http2 { + +class nghttp2_category_impl : public boost::system::error_category { +public: + const char *name() const noexcept { return "nghttp2"; } + std::string message(int ev) const { return nghttp2_strerror(ev); } +}; + +const boost::system::error_category &nghttp2_category() noexcept { + static nghttp2_category_impl cat; + return cat; +} + +boost::system::error_code make_error_code(nghttp2_error ev) { + return boost::system::error_code(static_cast(ev), nghttp2_category()); +} + +generator_cb string_generator(std::string data) { + auto strio = std::make_shared>(std::move(data), + data.size()); + return [strio](uint8_t *buf, size_t len, uint32_t *data_flags) { + auto &data = strio->first; + auto &left = strio->second; + auto n = std::min(len, left); + std::copy_n(data.c_str() + data.size() - left, n, buf); + left -= n; + if (left == 0) { + *data_flags |= NGHTTP2_DATA_FLAG_EOF; + } + return n; + }; +} + +generator_cb deferred_generator() { + return [](uint8_t *buf, size_t len, + uint32_t *data_flags) { return NGHTTP2_ERR_DEFERRED; }; +} + +template +std::shared_ptr> defer_shared(F &&f, T &&... t) { + return std::make_shared>(std::forward(f), + std::forward(t)...); +} + +generator_cb file_generator(const std::string &path) { + auto fd = open(path.c_str(), O_RDONLY); + if (fd == -1) { + return generator_cb(); + } + + return file_generator_from_fd(fd); +} + +generator_cb file_generator_from_fd(int fd) { + auto d = defer_shared(close, fd); + + return [fd, d](uint8_t *buf, size_t len, uint32_t *data_flags) + -> generator_cb::result_type { + ssize_t n; + while ((n = read(fd, buf, len)) == -1 && errno == EINTR) + ; + + if (n == -1) { + return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; + } + + if (n == 0) { + *data_flags |= NGHTTP2_DATA_FLAG_EOF; + } + + return n; + }; +} + +bool check_path(const std::string &path) { return util::check_path(path); } + +std::string percent_decode(const std::string &s) { + return util::percentDecode(std::begin(s), std::end(s)); +} + +std::string http_date(int64_t t) { return util::http_date(t); } + +boost::system::error_code host_service_from_uri(boost::system::error_code &ec, + std::string &scheme, + std::string &host, + std::string &service, + const std::string &uri) { + ec.clear(); + + http_parser_url u{}; + if (http_parser_parse_url(uri.c_str(), uri.size(), 0, &u) != 0) { + ec = make_error_code(boost::system::errc::invalid_argument); + return ec; + } + + if ((u.field_set & (1 << UF_SCHEMA)) == 0 || + (u.field_set & (1 << UF_HOST)) == 0) { + ec = make_error_code(boost::system::errc::invalid_argument); + return ec; + } + + http2::copy_url_component(scheme, &u, UF_SCHEMA, uri.c_str()); + http2::copy_url_component(host, &u, UF_HOST, uri.c_str()); + + if (u.field_set & (1 << UF_PORT)) { + http2::copy_url_component(service, &u, UF_PORT, uri.c_str()); + } else { + service = scheme; + } + + return ec; +} + +} // namespace asio_http2 +} // namespace nghttp2 diff --git a/src/asio_common.h b/src/asio_common.h new file mode 100644 index 0000000..8f59a0d --- /dev/null +++ b/src/asio_common.h @@ -0,0 +1,65 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2015 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef ASIO_COMMON_H +#define ASIO_COMMON_H + +#include "nghttp2_config.h" + +#include + +#include + +#include "util.h" + +namespace nghttp2 { + +namespace asio_http2 { + +boost::system::error_code make_error_code(nghttp2_error ev); + +generator_cb string_generator(std::string data); + +// Returns generator_cb, which just returns NGHTTP2_ERR_DEFERRED +generator_cb deferred_generator(); + +template +void split_path(uri_ref &dst, InputIt first, InputIt last) { + auto path_last = std::find(first, last, '?'); + InputIt query_first; + if (path_last == last) { + query_first = path_last = last; + } else { + query_first = path_last + 1; + } + dst.path = util::percentDecode(first, path_last); + dst.raw_path.assign(first, path_last); + dst.raw_query.assign(query_first, last); +} + +} // namespace asio_http2 + +} // namespace nghttp2 + +#endif // ASIO_COMMON_H diff --git a/src/asio_io_service_pool.cc b/src/asio_io_service_pool.cc new file mode 100644 index 0000000..ba40541 --- /dev/null +++ b/src/asio_io_service_pool.cc @@ -0,0 +1,97 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2014 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +// We wrote this code based on the original code which has the +// following license: +// +// io_service_pool.cpp +// ~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2013 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +#include "asio_io_service_pool.h" + +namespace nghttp2 { + +namespace asio_http2 { + +io_service_pool::io_service_pool(std::size_t pool_size) : next_io_service_(0) { + if (pool_size == 0) { + throw std::runtime_error("io_service_pool size is 0"); + } + + // Give all the io_services work to do so that their run() functions will not + // exit until they are explicitly stopped. + for (std::size_t i = 0; i < pool_size; ++i) { + auto io_service = std::make_shared(); + auto work = std::make_shared(*io_service); + io_services_.push_back(io_service); + work_.push_back(work); + } +} + +void io_service_pool::run(bool asynchronous) { + // Create a pool of threads to run all of the io_services. + for (std::size_t i = 0; i < io_services_.size(); ++i) { + futures_.push_back(std::async(std::launch::async, + (size_t (boost::asio::io_service::*)(void)) & + boost::asio::io_service::run, + io_services_[i])); + } + + if (!asynchronous) { + join(); + } +} + +void io_service_pool::join() { + // Wait for all threads in the pool to exit. + for (auto &fut : futures_) { + fut.get(); + } +} + +void io_service_pool::stop() { + // Explicitly stop all io_services. + for (auto &iosv : io_services_) { + iosv->stop(); + } +} + +boost::asio::io_service &io_service_pool::get_io_service() { + // Use a round-robin scheme to choose the next io_service to use. + auto &io_service = *io_services_[next_io_service_]; + ++next_io_service_; + if (next_io_service_ == io_services_.size()) { + next_io_service_ = 0; + } + return io_service; +} + +} // namespace asio_http2 + +} // namespace nghttp2 diff --git a/src/asio_io_service_pool.h b/src/asio_io_service_pool.h new file mode 100644 index 0000000..242cac8 --- /dev/null +++ b/src/asio_io_service_pool.h @@ -0,0 +1,91 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2014 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +// We wrote this code based on the original code which has the +// following license: +// +// io_service_pool.hpp +// ~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2013 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_IO_SERVICE_POOL_H +#define ASIO_IO_SERVICE_POOL_H + +#include "nghttp2_config.h" + +#include +#include +#include + +#include +#include + +#include + +namespace nghttp2 { + +namespace asio_http2 { + +/// A pool of io_service objects. +class io_service_pool : private boost::noncopyable { +public: + /// Construct the io_service pool. + explicit io_service_pool(std::size_t pool_size); + + /// Run all io_service objects in the pool. + void run(bool asynchronous = false); + + /// Stop all io_service objects in the pool. + void stop(); + + /// Join on all io_service objects in the pool. + void join(); + + /// Get an io_service to use. + boost::asio::io_service &get_io_service(); + +private: + /// The pool of io_services. + std::vector> io_services_; + + /// The work that keeps the io_services running. + std::vector> work_; + + /// The next io_service to use for a connection. + std::size_t next_io_service_; + + /// Futures to all the io_service objects + std::vector> futures_; +}; + +} // namespace asio_http2 + +} // namespace nghttp2 + +#endif // ASIO_IO_SERVICE_POOL_H diff --git a/src/asio_server.cc b/src/asio_server.cc new file mode 100644 index 0000000..310e672 --- /dev/null +++ b/src/asio_server.cc @@ -0,0 +1,165 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2014 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +// We wrote this code based on the original code which has the +// following license: +// +// server.cpp +// ~~~~~~~~~~ +// +// Copyright (c) 2003-2013 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#include "asio_server.h" + +#include "asio_server_connection.h" +#include "util.h" + +namespace nghttp2 { +namespace asio_http2 { +namespace server { + +server::server(std::size_t io_service_pool_size) + : io_service_pool_(io_service_pool_size) {} + +boost::system::error_code +server::listen_and_serve(boost::system::error_code &ec, + boost::asio::ssl::context *tls_context, + const std::string &address, const std::string &port, + int backlog, serve_mux &mux, bool asynchronous) { + ec.clear(); + + if (bind_and_listen(ec, address, port, backlog)) { + return ec; + } + + for (auto &acceptor : acceptors_) { + if (tls_context) { + start_accept(*tls_context, acceptor, mux); + } else { + start_accept(acceptor, mux); + } + } + + io_service_pool_.run(asynchronous); + + return ec; +} + +boost::system::error_code server::bind_and_listen(boost::system::error_code &ec, + const std::string &address, + const std::string &port, + int backlog) { + // Open the acceptor with the option to reuse the address (i.e. + // SO_REUSEADDR). + tcp::resolver resolver(io_service_pool_.get_io_service()); + tcp::resolver::query query(address, port); + auto it = resolver.resolve(query, ec); + if (ec) { + return ec; + } + + for (; it != tcp::resolver::iterator(); ++it) { + tcp::endpoint endpoint = *it; + auto acceptor = tcp::acceptor(io_service_pool_.get_io_service()); + + if (acceptor.open(endpoint.protocol(), ec)) { + continue; + } + + acceptor.set_option(tcp::acceptor::reuse_address(true)); + + if (acceptor.bind(endpoint, ec)) { + continue; + } + + if (acceptor.listen( + backlog == -1 ? boost::asio::socket_base::max_connections : backlog, + ec)) { + continue; + } + + acceptors_.push_back(std::move(acceptor)); + } + + if (acceptors_.empty()) { + return ec; + } + + // ec could have some errors since we may have failed to bind some + // interfaces. + ec.clear(); + + return ec; +} + +void server::start_accept(boost::asio::ssl::context &tls_context, + tcp::acceptor &acceptor, serve_mux &mux) { + auto new_connection = std::make_shared>( + mux, io_service_pool_.get_io_service(), tls_context); + + acceptor.async_accept(new_connection->socket().lowest_layer(), + [this, &tls_context, &acceptor, &mux, new_connection]( + const boost::system::error_code &e) { + if (!e) { + new_connection->socket().lowest_layer().set_option(tcp::no_delay(true)); + new_connection->socket().async_handshake( + boost::asio::ssl::stream_base::server, + [new_connection](const boost::system::error_code &e) { + if (!e) { + new_connection->start(); + } + }); + } + + start_accept(tls_context, acceptor, mux); + }); +} + +void server::start_accept(tcp::acceptor &acceptor, serve_mux &mux) { + auto new_connection = std::make_shared>( + mux, io_service_pool_.get_io_service()); + + acceptor.async_accept(new_connection->socket(), + [this, &acceptor, &mux, new_connection]( + const boost::system::error_code &e) { + if (!e) { + new_connection->socket().set_option(tcp::no_delay(true)); + new_connection->start(); + } + + start_accept(acceptor, mux); + }); +} + +void server::stop() { io_service_pool_.stop(); } + +void server::join() { io_service_pool_.join(); } + +} // namespace server +} // namespace asio_http2 +} // namespace nghttp2 diff --git a/src/asio_server.h b/src/asio_server.h new file mode 100644 index 0000000..7f60120 --- /dev/null +++ b/src/asio_server.h @@ -0,0 +1,105 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2014 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +// We wrote this code based on the original code which has the +// following license: +// +// server.hpp +// ~~~~~~~~~~ +// +// Copyright (c) 2003-2013 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_SERVER_H +#define ASIO_SERVER_H + +#include "nghttp2_config.h" + +#include +#include +#include + +#include + +#include + +#include "asio_io_service_pool.h" + +namespace nghttp2 { + +namespace asio_http2 { + +namespace server { + +class serve_mux; + +using boost::asio::ip::tcp; + +using ssl_socket = boost::asio::ssl::stream; + +class server : private boost::noncopyable { +public: + explicit server(std::size_t io_service_pool_size); + + boost::system::error_code + listen_and_serve(boost::system::error_code &ec, + boost::asio::ssl::context *tls_context, + const std::string &address, const std::string &port, + int backlog, serve_mux &mux, bool asynchronous = false); + void join(); + void stop(); + +private: + /// Initiate an asynchronous accept operation. + void start_accept(tcp::acceptor &acceptor, serve_mux &mux); + /// Same as above but with tls_context + void start_accept(boost::asio::ssl::context &tls_context, + tcp::acceptor &acceptor, serve_mux &mux); + + /// Resolves address and bind socket to the resolved addresses. + boost::system::error_code bind_and_listen(boost::system::error_code &ec, + const std::string &address, + const std::string &port, + int backlog); + + /// The pool of io_service objects used to perform asynchronous + /// operations. + io_service_pool io_service_pool_; + + /// Acceptor used to listen for incoming connections. + std::vector acceptors_; + + std::unique_ptr ssl_ctx_; +}; + +} // namespace server + +} // namespace asio_http2 + +} // namespace nghttp2 + +#endif // ASIO_SERVER_H diff --git a/src/asio_server_connection.h b/src/asio_server_connection.h new file mode 100644 index 0000000..5ce48d9 --- /dev/null +++ b/src/asio_server_connection.h @@ -0,0 +1,169 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2014 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +// We wrote this code based on the original code which has the +// following license: +// +// connection.hpp +// ~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2013 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_SERVER_CONNECTION_H +#define ASIO_SERVER_CONNECTION_H + +#include "nghttp2_config.h" + +#include + +#include +#include + +#include + +#include "asio_server_http2_handler.h" +#include "asio_server_serve_mux.h" +#include "util.h" + +namespace nghttp2 { + +namespace asio_http2 { + +namespace server { + +/// Represents a single connection from a client. +template +class connection : public std::enable_shared_from_this>, + private boost::noncopyable { +public: + /// Construct a connection with the given io_service. + template + explicit connection(serve_mux &mux, SocketArgs &&... args) + : socket_(std::forward(args)...), mux_(mux), writing_(false) { + } + + /// Start the first asynchronous operation for the connection. + void start() { + handler_ = std::make_shared(socket_.get_io_service(), + [this]() { do_write(); }, mux_); + if (handler_->start() != 0) { + return; + } + do_read(); + } + + socket_type &socket() { return socket_; } + + void do_read() { + auto self = this->shared_from_this(); + + socket_.async_read_some(boost::asio::buffer(buffer_), + [this, self](const boost::system::error_code &e, + std::size_t bytes_transferred) { + if (!e) { + if (handler_->on_read(buffer_, bytes_transferred) != 0) { + return; + } + + do_write(); + + if (!writing_ && handler_->should_stop()) { + return; + } + + do_read(); + } + + // If an error occurs then no new asynchronous operations are + // started. This means that all shared_ptr references to the + // connection object will disappear and the object will be + // destroyed automatically after this handler returns. The + // connection class's destructor closes the socket. + }); + } + + void do_write() { + auto self = this->shared_from_this(); + + if (writing_) { + return; + } + + int rv; + std::size_t nwrite; + + rv = handler_->on_write(outbuf_, nwrite); + + if (rv != 0) { + return; + } + + if (nwrite == 0) { + return; + } + + writing_ = true; + + boost::asio::async_write( + socket_, boost::asio::buffer(outbuf_, nwrite), + [this, self](const boost::system::error_code &e, std::size_t) { + if (!e) { + writing_ = false; + + do_write(); + } + }); + + // No new asynchronous operations are started. This means that all + // shared_ptr references to the connection object will disappear and + // the object will be destroyed automatically after this handler + // returns. The connection class's destructor closes the socket. + } + +private: + socket_type socket_; + + serve_mux &mux_; + + std::shared_ptr handler_; + + /// Buffer for incoming data. + boost::array buffer_; + + boost::array outbuf_; + + bool writing_; +}; + +} // namespace server + +} // namespace asio_http2 + +} // namespace nghttp2 + +#endif // ASIO_SERVER_CONNECTION_H diff --git a/src/asio_server_http2.cc b/src/asio_server_http2.cc new file mode 100644 index 0000000..7d162e3 --- /dev/null +++ b/src/asio_server_http2.cc @@ -0,0 +1,84 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2014 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "nghttp2_config.h" + +#include + +#include "asio_server_http2_impl.h" +#include "asio_server.h" +#include "template.h" + +namespace nghttp2 { + +namespace asio_http2 { + +namespace server { + +http2::http2() : impl_(make_unique()) {} + +http2::~http2() {} + +http2::http2(http2 &&other) noexcept : impl_(std::move(other.impl_)) {} + +http2 &http2::operator=(http2 &&other) noexcept { + if (this == &other) { + return *this; + } + + impl_ = std::move(other.impl_); + + return *this; +} + +boost::system::error_code http2::listen_and_serve(boost::system::error_code &ec, + const std::string &address, + const std::string &port, + bool asynchronous) { + return impl_->listen_and_serve(ec, nullptr, address, port, asynchronous); +} + +boost::system::error_code http2::listen_and_serve( + boost::system::error_code &ec, boost::asio::ssl::context &tls_context, + const std::string &address, const std::string &port, bool asynchronous) { + return impl_->listen_and_serve(ec, &tls_context, address, port, asynchronous); +} + +void http2::num_threads(size_t num_threads) { impl_->num_threads(num_threads); } + +void http2::backlog(int backlog) { impl_->backlog(backlog); } + +bool http2::handle(std::string pattern, request_cb cb) { + return impl_->handle(std::move(pattern), std::move(cb)); +} + +void http2::stop() { impl_->stop(); } + +void http2::join() { return impl_->join(); } + +} // namespace server + +} // namespace asio_http2 + +} // namespace nghttp2 diff --git a/src/asio_server_http2_handler.cc b/src/asio_server_http2_handler.cc new file mode 100644 index 0000000..c55cb32 --- /dev/null +++ b/src/asio_server_http2_handler.cc @@ -0,0 +1,462 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2014 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "asio_server_http2_handler.h" + +#include + +#include "asio_common.h" +#include "asio_server_serve_mux.h" +#include "asio_server_stream.h" +#include "asio_server_request_impl.h" +#include "asio_server_response_impl.h" +#include "http2.h" +#include "util.h" +#include "template.h" + +namespace nghttp2 { + +namespace asio_http2 { + +namespace server { + +namespace { +int stream_error(nghttp2_session *session, int32_t stream_id, + uint32_t error_code) { + return nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, stream_id, + error_code); +} +} // namespace + +namespace { +int on_begin_headers_callback(nghttp2_session *session, + const nghttp2_frame *frame, void *user_data) { + auto handler = static_cast(user_data); + + if (frame->hd.type != NGHTTP2_HEADERS || + frame->headers.cat != NGHTTP2_HCAT_REQUEST) { + return 0; + } + + handler->create_stream(frame->hd.stream_id); + + return 0; +} +} // namespace + +namespace { +int on_header_callback(nghttp2_session *session, const nghttp2_frame *frame, + const uint8_t *name, size_t namelen, + const uint8_t *value, size_t valuelen, uint8_t flags, + void *user_data) { + auto handler = static_cast(user_data); + auto stream_id = frame->hd.stream_id; + + if (frame->hd.type != NGHTTP2_HEADERS || + frame->headers.cat != NGHTTP2_HCAT_REQUEST) { + return 0; + } + + auto strm = handler->find_stream(stream_id); + if (!strm) { + return 0; + } + + auto &req = strm->request().impl(); + auto &uref = req.uri(); + + switch (nghttp2::http2::lookup_token(name, namelen)) { + case nghttp2::http2::HD__METHOD: + req.method(std::string(value, value + valuelen)); + break; + case nghttp2::http2::HD__SCHEME: + uref.scheme.assign(value, value + valuelen); + break; + case nghttp2::http2::HD__AUTHORITY: + uref.host.assign(value, value + valuelen); + break; + case nghttp2::http2::HD__PATH: + split_path(uref, value, value + valuelen); + break; + case nghttp2::http2::HD_HOST: + if (uref.host.empty()) { + uref.host.assign(value, value + valuelen); + } + // fall through + default: + req.header().emplace(std::string(name, name + namelen), + header_value{std::string(value, value + valuelen), + (flags & NGHTTP2_NV_FLAG_NO_INDEX) != 0}); + } + + return 0; +} +} // namespace + +namespace { +int on_frame_recv_callback(nghttp2_session *session, const nghttp2_frame *frame, + void *user_data) { + auto handler = static_cast(user_data); + auto strm = handler->find_stream(frame->hd.stream_id); + + switch (frame->hd.type) { + case NGHTTP2_DATA: + if (!strm) { + break; + } + + if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) { + strm->request().impl().call_on_data(nullptr, 0); + } + + break; + case NGHTTP2_HEADERS: { + if (!strm || frame->headers.cat != NGHTTP2_HCAT_REQUEST) { + break; + } + + handler->call_on_request(*strm); + + if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) { + strm->request().impl().call_on_data(nullptr, 0); + } + + break; + } + } + + return 0; +} +} // namespace + +namespace { +int on_data_chunk_recv_callback(nghttp2_session *session, uint8_t flags, + int32_t stream_id, const uint8_t *data, + size_t len, void *user_data) { + auto handler = static_cast(user_data); + auto strm = handler->find_stream(stream_id); + + if (!strm) { + return 0; + } + + strm->request().impl().call_on_data(data, len); + + return 0; +} + +} // namespace + +namespace { +int on_stream_close_callback(nghttp2_session *session, int32_t stream_id, + uint32_t error_code, void *user_data) { + auto handler = static_cast(user_data); + + auto strm = handler->find_stream(stream_id); + if (!strm) { + return 0; + } + + strm->response().impl().call_on_close(error_code); + + handler->close_stream(stream_id); + + return 0; +} +} // namespace + +namespace { +int on_frame_send_callback(nghttp2_session *session, const nghttp2_frame *frame, + void *user_data) { + auto handler = static_cast(user_data); + + if (frame->hd.type != NGHTTP2_PUSH_PROMISE) { + return 0; + } + + auto strm = handler->find_stream(frame->push_promise.promised_stream_id); + + if (!strm) { + return 0; + } + + auto &res = strm->response().impl(); + res.push_promise_sent(); + + return 0; +} +} // namespace + +namespace { +int on_frame_not_send_callback(nghttp2_session *session, + const nghttp2_frame *frame, int lib_error_code, + void *user_data) { + if (frame->hd.type != NGHTTP2_HEADERS) { + return 0; + } + + // Issue RST_STREAM so that stream does not hang around. + nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, frame->hd.stream_id, + NGHTTP2_INTERNAL_ERROR); + + return 0; +} +} // namespace + +http2_handler::http2_handler(boost::asio::io_service &io_service, + connection_write writefun, serve_mux &mux) + : writefun_(writefun), mux_(mux), io_service_(io_service), + session_(nullptr), buf_(nullptr), buflen_(0), inside_callback_(false), + tstamp_cached_(time(nullptr)), + formatted_date_(util::http_date(tstamp_cached_)) {} + +http2_handler::~http2_handler() { nghttp2_session_del(session_); } + +const std::string &http2_handler::http_date() { + auto t = time(nullptr); + if (t != tstamp_cached_) { + tstamp_cached_ = t; + formatted_date_ = util::http_date(t); + } + return formatted_date_; +} + +int http2_handler::start() { + int rv; + + nghttp2_session_callbacks *callbacks; + rv = nghttp2_session_callbacks_new(&callbacks); + if (rv != 0) { + return -1; + } + + auto cb_del = defer(nghttp2_session_callbacks_del, callbacks); + + nghttp2_session_callbacks_set_on_begin_headers_callback( + callbacks, on_begin_headers_callback); + nghttp2_session_callbacks_set_on_header_callback(callbacks, + on_header_callback); + nghttp2_session_callbacks_set_on_frame_recv_callback(callbacks, + on_frame_recv_callback); + nghttp2_session_callbacks_set_on_data_chunk_recv_callback( + callbacks, on_data_chunk_recv_callback); + nghttp2_session_callbacks_set_on_stream_close_callback( + callbacks, on_stream_close_callback); + nghttp2_session_callbacks_set_on_frame_send_callback(callbacks, + on_frame_send_callback); + nghttp2_session_callbacks_set_on_frame_not_send_callback( + callbacks, on_frame_not_send_callback); + + rv = nghttp2_session_server_new(&session_, callbacks, this); + if (rv != 0) { + return -1; + } + + nghttp2_settings_entry ent{NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100}; + nghttp2_submit_settings(session_, NGHTTP2_FLAG_NONE, &ent, 1); + + return 0; +} + +stream *http2_handler::create_stream(int32_t stream_id) { + auto p = streams_.emplace(stream_id, make_unique(this, stream_id)); + assert(p.second); + return (*p.first).second.get(); +} + +void http2_handler::close_stream(int32_t stream_id) { + streams_.erase(stream_id); +} + +stream *http2_handler::find_stream(int32_t stream_id) { + auto i = streams_.find(stream_id); + if (i == std::end(streams_)) { + return nullptr; + } + + return (*i).second.get(); +} + +void http2_handler::call_on_request(stream &strm) { + auto cb = mux_.handler(strm.request().impl()); + cb(strm.request(), strm.response()); +} + +bool http2_handler::should_stop() const { + return !nghttp2_session_want_read(session_) && + !nghttp2_session_want_write(session_); +} + +int http2_handler::start_response(stream &strm) { + int rv; + + auto &res = strm.response().impl(); + auto &header = res.header(); + auto nva = std::vector(); + nva.reserve(2 + header.size()); + auto status = util::utos(res.status_code()); + auto date = http_date(); + nva.push_back(nghttp2::http2::make_nv_ls(":status", status)); + nva.push_back(nghttp2::http2::make_nv_ls("date", date)); + for (auto &hd : header) { + nva.push_back(nghttp2::http2::make_nv(hd.first, hd.second.value, + hd.second.sensitive)); + } + + nghttp2_data_provider *prd_ptr = nullptr, prd; + auto &req = strm.request().impl(); + if (::nghttp2::http2::expect_response_body(req.method(), res.status_code())) { + prd.source.ptr = &strm; + prd.read_callback = + [](nghttp2_session *session, int32_t stream_id, uint8_t *buf, + size_t length, uint32_t *data_flags, nghttp2_data_source *source, + void *user_data) -> ssize_t { + auto &strm = *static_cast(source->ptr); + return strm.response().impl().call_read(buf, length, data_flags); + }; + prd_ptr = &prd; + } + rv = nghttp2_submit_response(session_, strm.get_stream_id(), nva.data(), + nva.size(), prd_ptr); + + if (rv != 0) { + return -1; + } + + signal_write(); + + return 0; +} + +int http2_handler::submit_trailer(stream &strm, header_map h) { + int rv; + auto nva = std::vector(); + nva.reserve(h.size()); + for (auto &hd : h) { + nva.push_back(nghttp2::http2::make_nv(hd.first, hd.second.value, + hd.second.sensitive)); + } + + rv = nghttp2_submit_trailer(session_, strm.get_stream_id(), nva.data(), + nva.size()); + + if (rv != 0) { + return -1; + } + + signal_write(); + + return 0; +} + +void http2_handler::enter_callback() { + assert(!inside_callback_); + inside_callback_ = true; +} + +void http2_handler::leave_callback() { + assert(inside_callback_); + inside_callback_ = false; +} + +void http2_handler::stream_error(int32_t stream_id, uint32_t error_code) { + ::nghttp2::asio_http2::server::stream_error(session_, stream_id, error_code); + signal_write(); +} + +void http2_handler::signal_write() { + if (!inside_callback_) { + initiate_write(); + } +} + +void http2_handler::initiate_write() { writefun_(); } + +void http2_handler::resume(stream &strm) { + nghttp2_session_resume_data(session_, strm.get_stream_id()); + signal_write(); +} + +response *http2_handler::push_promise(boost::system::error_code &ec, + stream &strm, std::string method, + std::string raw_path_query, + header_map h) { + int rv; + + ec.clear(); + + auto &req = strm.request().impl(); + + auto nva = std::vector(); + nva.reserve(4 + h.size()); + nva.push_back(nghttp2::http2::make_nv_ls(":method", method)); + nva.push_back(nghttp2::http2::make_nv_ls(":scheme", req.uri().scheme)); + nva.push_back(nghttp2::http2::make_nv_ls(":authority", req.uri().host)); + nva.push_back(nghttp2::http2::make_nv_ls(":path", raw_path_query)); + + for (auto &hd : h) { + nva.push_back(nghttp2::http2::make_nv(hd.first, hd.second.value, + hd.second.sensitive)); + } + + rv = nghttp2_submit_push_promise(session_, NGHTTP2_FLAG_NONE, + strm.get_stream_id(), nva.data(), nva.size(), + nullptr); + + if (rv < 0) { + ec = make_error_code(static_cast(rv)); + return nullptr; + } + + auto promised_strm = create_stream(rv); + auto &promised_req = promised_strm->request().impl(); + promised_req.header(std::move(h)); + promised_req.method(std::move(method)); + + auto &uref = promised_req.uri(); + uref.scheme = req.uri().scheme; + uref.host = req.uri().host; + split_path(uref, std::begin(raw_path_query), std::end(raw_path_query)); + + auto &promised_res = promised_strm->response().impl(); + promised_res.pushed(true); + + signal_write(); + + return &promised_strm->response(); +} + +boost::asio::io_service &http2_handler::io_service() { return io_service_; } + +callback_guard::callback_guard(http2_handler &h) : handler(h) { + handler.enter_callback(); +} + +callback_guard::~callback_guard() { handler.leave_callback(); } + +} // namespace server + +} // namespace asio_http2 + +} // namespace nghttp2 diff --git a/src/asio_server_http2_handler.h b/src/asio_server_http2_handler.h new file mode 100644 index 0000000..068d452 --- /dev/null +++ b/src/asio_server_http2_handler.h @@ -0,0 +1,167 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2014 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef ASIO_SERVER_HTTP2_HANDLER_H +#define ASIO_SERVER_HTTP2_HANDLER_H + +#include "nghttp2_config.h" + +#include +#include +#include + +#include + +#include + +namespace nghttp2 { +namespace asio_http2 { +namespace server { + +class http2_handler; +class stream; +class serve_mux; + +struct callback_guard { + callback_guard(http2_handler &h); + ~callback_guard(); + http2_handler &handler; +}; + +using connection_write = std::function; + +class http2_handler : public std::enable_shared_from_this { +public: + http2_handler(boost::asio::io_service &io_service, connection_write writefun, + serve_mux &mux); + + ~http2_handler(); + + int start(); + + stream *create_stream(int32_t stream_id); + void close_stream(int32_t stream_id); + stream *find_stream(int32_t stream_id); + + void call_on_request(stream &s); + + bool should_stop() const; + + int start_response(stream &s); + + int submit_trailer(stream &s, header_map h); + + void stream_error(int32_t stream_id, uint32_t error_code); + + void initiate_write(); + + void enter_callback(); + void leave_callback(); + + void resume(stream &s); + + response *push_promise(boost::system::error_code &ec, stream &s, + std::string method, std::string raw_path_query, + header_map h); + + void signal_write(); + + boost::asio::io_service &io_service(); + + const std::string &http_date(); + + template + int on_read(const boost::array &buffer, std::size_t len) { + callback_guard cg(*this); + + int rv; + + rv = nghttp2_session_mem_recv(session_, buffer.data(), len); + + if (rv < 0) { + return -1; + } + + return 0; + } + + template + int on_write(boost::array &buffer, std::size_t &len) { + callback_guard cg(*this); + + len = 0; + + if (buf_) { + std::copy_n(buf_, buflen_, std::begin(buffer)); + + len += buflen_; + + buf_ = nullptr; + buflen_ = 0; + } + + for (;;) { + const uint8_t *data; + auto nread = nghttp2_session_mem_send(session_, &data); + if (nread < 0) { + return -1; + } + + if (nread == 0) { + break; + } + + if (len + nread > buffer.size()) { + buf_ = data; + buflen_ = nread; + + break; + } + + std::copy_n(data, nread, std::begin(buffer) + len); + + len += nread; + } + + return 0; + } + +private: + std::map> streams_; + connection_write writefun_; + serve_mux &mux_; + boost::asio::io_service &io_service_; + nghttp2_session *session_; + const uint8_t *buf_; + std::size_t buflen_; + bool inside_callback_; + time_t tstamp_cached_; + std::string formatted_date_; +}; + +} // namespace server +} // namespace asio_http2 +} // namespace nghttp + +#endif // ASIO_SERVER_HTTP2_HANDLER_H diff --git a/src/asio_server_http2_impl.cc b/src/asio_server_http2_impl.cc new file mode 100644 index 0000000..79994a8 --- /dev/null +++ b/src/asio_server_http2_impl.cc @@ -0,0 +1,66 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2014 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "asio_server_http2_impl.h" + +#include + +#include "asio_server.h" +#include "util.h" +#include "ssl.h" +#include "template.h" + +namespace nghttp2 { + +namespace asio_http2 { + +namespace server { + +http2_impl::http2_impl() : num_threads_(1), backlog_(-1) {} + +boost::system::error_code http2_impl::listen_and_serve( + boost::system::error_code &ec, boost::asio::ssl::context *tls_context, + const std::string &address, const std::string &port, bool asynchronous) { + server_.reset(new server(num_threads_)); + return server_->listen_and_serve(ec, tls_context, address, port, backlog_, + mux_, asynchronous); +} + +void http2_impl::num_threads(size_t num_threads) { num_threads_ = num_threads; } + +void http2_impl::backlog(int backlog) { backlog_ = backlog; } + +bool http2_impl::handle(std::string pattern, request_cb cb) { + return mux_.handle(std::move(pattern), std::move(cb)); +} + +void http2_impl::stop() { return server_->stop(); } + +void http2_impl::join() { return server_->join(); } + +} // namespace server + +} // namespace asio_http2 + +} // namespace nghttp2 diff --git a/src/asio_server_http2_impl.h b/src/asio_server_http2_impl.h new file mode 100644 index 0000000..e406869 --- /dev/null +++ b/src/asio_server_http2_impl.h @@ -0,0 +1,67 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2014 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef ASIO_SERVER_HTTP2_IMPL_H +#define ASIO_SERVER_HTTP2_IMPL_H + +#include "nghttp2_config.h" + +#include + +#include "asio_server_serve_mux.h" + +namespace nghttp2 { + +namespace asio_http2 { + +namespace server { + +class server; + +class http2_impl { +public: + http2_impl(); + boost::system::error_code listen_and_serve( + boost::system::error_code &ec, boost::asio::ssl::context *tls_context, + const std::string &address, const std::string &port, bool asynchronous); + void num_threads(size_t num_threads); + void backlog(int backlog); + bool handle(std::string pattern, request_cb cb); + void stop(); + void join(); + +private: + std::unique_ptr server_; + std::size_t num_threads_; + int backlog_; + serve_mux mux_; +}; + +} // namespace server + +} // namespace asio_http2 + +} // namespace nghttp2 + +#endif // ASIO_SERVER_HTTP2_IMPL_H diff --git a/src/asio_server_request.cc b/src/asio_server_request.cc new file mode 100644 index 0000000..0241c48 --- /dev/null +++ b/src/asio_server_request.cc @@ -0,0 +1,55 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2015 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "nghttp2_config.h" + +#include + +#include "asio_server_request_impl.h" + +#include "template.h" + +namespace nghttp2 { +namespace asio_http2 { +namespace server { + +request::request() : impl_(make_unique()) {} + +request::~request() {} + +const header_map &request::header() const { return impl_->header(); } + +const std::string &request::method() const { return impl_->method(); } + +const uri_ref &request::uri() const { return impl_->uri(); } + +void request::on_data(data_cb cb) const { + return impl_->on_data(std::move(cb)); +} + +request_impl &request::impl() const { return *impl_; } + +} // namespace server +} // namespace asio_http2 +} // namespace nghttp2 diff --git a/src/asio_server_request_handler.cc b/src/asio_server_request_handler.cc new file mode 100644 index 0000000..7f113bd --- /dev/null +++ b/src/asio_server_request_handler.cc @@ -0,0 +1,84 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2015 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "asio_server_request_handler.h" + +#include "util.h" +#include "http2.h" + +namespace nghttp2 { +namespace asio_http2 { +namespace server { + +namespace { +std::string create_html(int status_code) { + std::string res; + res.reserve(512); + auto status = ::nghttp2::http2::get_status_string(status_code); + res += R"()"; + res += status; + res += "

    "; + res += status; + res += "

    "; + return res; +} +} // namespace + +request_cb redirect_handler(int status_code, std::string uri) { + return [status_code, uri](const request &req, const response &res) { + header_map h; + h.emplace("location", header_value{std::move(uri)}); + std::string html; + if (req.method() == "GET") { + html = create_html(status_code); + } + h.emplace("content-length", header_value{util::utos(html.size())}); + + res.write_head(status_code, std::move(h)); + res.end(std::move(html)); + }; +} + +request_cb status_handler(int status_code) { + return [status_code](const request &req, const response &res) { + if (!::nghttp2::http2::expect_response_body(status_code)) { + res.write_head(status_code); + res.end(); + return; + } + // we supply content-length for HEAD request, but body will not be + // sent. + auto html = create_html(status_code); + header_map h; + h.emplace("content-length", header_value{util::utos(html.size())}); + h.emplace("content-type", header_value{"text/html; charset=utf-8"}); + + res.write_head(status_code, std::move(h)); + res.end(std::move(html)); + }; +} + +} // namespace server +} // namespace asio_http2 +} // namespace nghttp2 diff --git a/src/asio_server_request_handler.h b/src/asio_server_request_handler.h new file mode 100644 index 0000000..5eefcfd --- /dev/null +++ b/src/asio_server_request_handler.h @@ -0,0 +1,32 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2015 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef ASIO_SERVER_REQUEST_HANDLER_H +#define ASIO_SERVER_REQUEST_HANDLER_H + +#include "nghttp2_config.h" + +#include + +#endif // ASIO_SERVER_REQUEST_HANDLER_H diff --git a/src/asio_server_request_impl.cc b/src/asio_server_request_impl.cc new file mode 100644 index 0000000..3a78208 --- /dev/null +++ b/src/asio_server_request_impl.cc @@ -0,0 +1,59 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2015 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "asio_server_request_impl.h" + +namespace nghttp2 { +namespace asio_http2 { +namespace server { + +request_impl::request_impl() : strm_(nullptr) {} + +const header_map &request_impl::header() const { return header_; } + +const std::string &request_impl::method() const { return method_; } + +const uri_ref &request_impl::uri() const { return uri_; } + +uri_ref &request_impl::uri() { return uri_; } + +void request_impl::header(header_map h) { header_ = std::move(h); } + +header_map &request_impl::header() { return header_; } + +void request_impl::method(std::string arg) { method_ = std::move(arg); } + +void request_impl::on_data(data_cb cb) { on_data_cb_ = std::move(cb); } + +void request_impl::stream(class stream *s) { strm_ = s; } + +void request_impl::call_on_data(const uint8_t *data, std::size_t len) { + if (on_data_cb_) { + on_data_cb_(data, len); + } +} + +} // namespace server +} // namespace asio_http2 +} // namespace nghttp2 diff --git a/src/asio_server_request_impl.h b/src/asio_server_request_impl.h new file mode 100644 index 0000000..8d4b014 --- /dev/null +++ b/src/asio_server_request_impl.h @@ -0,0 +1,69 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2015 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef ASIO_SERVER_REQUEST_IMPL_H +#define ASIO_SERVER_REQUEST_IMPL_H + +#include "nghttp2_config.h" + +#include + +namespace nghttp2 { +namespace asio_http2 { +namespace server { + +class stream; + +class request_impl { +public: + request_impl(); + + void header(header_map h); + const header_map &header() const; + header_map &header(); + + void method(std::string method); + const std::string &method() const; + + const uri_ref &uri() const; + uri_ref &uri(); + + void on_data(data_cb cb); + + void stream(class stream *s); + void call_on_data(const uint8_t *data, std::size_t len); + +private: + class stream *strm_; + header_map header_; + std::string method_; + uri_ref uri_; + data_cb on_data_cb_; +}; + +} // namespace server +} // namespace asio_http2 +} // namespace nghttp2 + +#endif // ASIO_SERVER_REQUEST_IMPL_H diff --git a/src/asio_server_response.cc b/src/asio_server_response.cc new file mode 100644 index 0000000..f420693 --- /dev/null +++ b/src/asio_server_response.cc @@ -0,0 +1,75 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2015 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "nghttp2_config.h" + +#include + +#include "asio_server_response_impl.h" + +#include "template.h" + +namespace nghttp2 { +namespace asio_http2 { +namespace server { + +response::response() : impl_(make_unique()) {} + +response::~response() {} + +void response::write_head(unsigned int status_code, header_map h) const { + impl_->write_head(status_code, std::move(h)); +} + +void response::end(std::string data) const { impl_->end(std::move(data)); } + +void response::end(generator_cb cb) const { impl_->end(std::move(cb)); } + +void response::write_trailer(header_map h) const { + impl_->write_trailer(std::move(h)); +} + +void response::on_close(close_cb cb) const { impl_->on_close(std::move(cb)); } + +void response::cancel(uint32_t error_code) const { impl_->cancel(error_code); } + +const response *response::push(boost::system::error_code &ec, + std::string method, std::string path, + header_map h) const { + return impl_->push(ec, std::move(method), std::move(path), std::move(h)); +} + +void response::resume() const { impl_->resume(); } + +unsigned int response::status_code() const { return impl_->status_code(); } + +boost::asio::io_service &response::io_service() const { + return impl_->io_service(); +} + +response_impl &response::impl() const { return *impl_; } + +} // namespace server +} // namespace asio_http2 +} // namespace nghttp2 diff --git a/src/asio_server_response_impl.cc b/src/asio_server_response_impl.cc new file mode 100644 index 0000000..1cdbc59 --- /dev/null +++ b/src/asio_server_response_impl.cc @@ -0,0 +1,163 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2015 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "asio_server_response_impl.h" + +#include "asio_server_stream.h" +#include "asio_server_request_impl.h" +#include "asio_server_http2_handler.h" +#include "asio_common.h" + +#include "http2.h" + +namespace nghttp2 { +namespace asio_http2 { +namespace server { + +response_impl::response_impl() + : strm_(nullptr), generator_cb_(deferred_generator()), status_code_(200), + state_(response_state::INITIAL), pushed_(false), + push_promise_sent_(false) {} + +unsigned int response_impl::status_code() const { return status_code_; } + +void response_impl::write_head(unsigned int status_code, header_map h) { + if (state_ != response_state::INITIAL) { + return; + } + + status_code_ = status_code; + header_ = std::move(h); + + state_ = response_state::HEADER_DONE; + + if (pushed_ && !push_promise_sent_) { + return; + } + + start_response(); +} + +void response_impl::end(std::string data) { + end(string_generator(std::move(data))); +} + +void response_impl::end(generator_cb cb) { + if (state_ == response_state::BODY_STARTED) { + return; + } + + generator_cb_ = std::move(cb); + + if (state_ == response_state::INITIAL) { + write_head(status_code_); + } else { + // generator_cb is changed, start writing in case it is deferred. + auto handler = strm_->handler(); + handler->resume(*strm_); + } + + state_ = response_state::BODY_STARTED; +} + +void response_impl::write_trailer(header_map h) { + auto handler = strm_->handler(); + handler->submit_trailer(*strm_, std::move(h)); +} + +void response_impl::start_response() { + auto handler = strm_->handler(); + + auto &req = strm_->request().impl(); + + if (!::nghttp2::http2::expect_response_body(req.method(), status_code_)) { + state_ = response_state::BODY_STARTED; + } + + if (handler->start_response(*strm_) != 0) { + handler->stream_error(strm_->get_stream_id(), NGHTTP2_INTERNAL_ERROR); + return; + } +} + +void response_impl::on_close(close_cb cb) { close_cb_ = std::move(cb); } + +void response_impl::call_on_close(uint32_t error_code) { + if (close_cb_) { + close_cb_(error_code); + } +} + +void response_impl::cancel(uint32_t error_code) { + auto handler = strm_->handler(); + handler->stream_error(strm_->get_stream_id(), error_code); +} + +response *response_impl::push(boost::system::error_code &ec, std::string method, + std::string raw_path_query, header_map h) const { + auto handler = strm_->handler(); + return handler->push_promise(ec, *strm_, std::move(method), + std::move(raw_path_query), std::move(h)); +} + +void response_impl::resume() { + auto handler = strm_->handler(); + handler->resume(*strm_); +} + +boost::asio::io_service &response_impl::io_service() { + return strm_->handler()->io_service(); +} + +void response_impl::pushed(bool f) { pushed_ = f; } + +void response_impl::push_promise_sent() { + if (push_promise_sent_) { + return; + } + push_promise_sent_ = true; + if (state_ == response_state::INITIAL) { + return; + } + start_response(); +} + +const header_map &response_impl::header() const { return header_; } + +void response_impl::stream(class stream *s) { strm_ = s; } + +generator_cb::result_type +response_impl::call_read(uint8_t *data, std::size_t len, uint32_t *data_flags) { + if (generator_cb_) { + return generator_cb_(data, len, data_flags); + } + + *data_flags |= NGHTTP2_DATA_FLAG_EOF; + + return 0; +} + +} // namespace server +} // namespace asio_http2 +} // namespace nghttp2 diff --git a/src/asio_server_response_impl.h b/src/asio_server_response_impl.h new file mode 100644 index 0000000..41df7a6 --- /dev/null +++ b/src/asio_server_response_impl.h @@ -0,0 +1,92 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2015 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef ASIO_SERVER_RESPONSE_IMPL_H +#define ASIO_SERVER_RESPONSE_IMPL_H + +#include "nghttp2_config.h" + +#include + +namespace nghttp2 { +namespace asio_http2 { +namespace server { + +class stream; + +enum class response_state { + INITIAL, + // response_impl::write_head() was called + HEADER_DONE, + // response_impl::end() was called + BODY_STARTED, +}; + +class response_impl { +public: + response_impl(); + void write_head(unsigned int status_code, header_map h = header_map{}); + void end(std::string data = ""); + void end(generator_cb cb); + void write_trailer(header_map h); + void on_close(close_cb cb); + void resume(); + + void cancel(uint32_t error_code); + + response *push(boost::system::error_code &ec, std::string method, + std::string raw_path_query, header_map) const; + + boost::asio::io_service &io_service(); + + void start_response(); + + unsigned int status_code() const; + const header_map &header() const; + void pushed(bool f); + void push_promise_sent(); + void stream(class stream *s); + generator_cb::result_type call_read(uint8_t *data, std::size_t len, + uint32_t *data_flags); + void call_on_close(uint32_t error_code); + +private: + class stream *strm_; + header_map header_; + generator_cb generator_cb_; + close_cb close_cb_; + unsigned int status_code_; + response_state state_; + // true if this is pushed stream's response + bool pushed_; + // true if PUSH_PROMISE is sent if this is response of a pushed + // stream + bool push_promise_sent_; +}; + +} // namespace server +} // namespace asio_http2 +} // namespace nghttp2 + +#endif // ASIO_SERVER_RESPONSE_IMPL_H diff --git a/src/asio_server_serve_mux.cc b/src/asio_server_serve_mux.cc new file mode 100644 index 0000000..829568b --- /dev/null +++ b/src/asio_server_serve_mux.cc @@ -0,0 +1,138 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2015 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "asio_server_serve_mux.h" + +#include "asio_server_request_impl.h" +#include "asio_server_request_handler.h" +#include "util.h" +#include "http2.h" + +namespace nghttp2 { + +namespace asio_http2 { + +namespace server { + +bool serve_mux::handle(std::string pattern, request_cb cb) { + if (pattern.empty() || !cb) { + return false; + } + + auto it = mux_.find(pattern); + if (it != std::end(mux_) && (*it).second.user_defined) { + return false; + } + + // if pattern ends with '/' (e.g., /foo/), add implicit permanent + // redirect for '/foo'. + if (pattern.size() >= 2 && pattern.back() == '/') { + auto redirect_pattern = pattern.substr(0, pattern.size() - 1); + auto it = mux_.find(redirect_pattern); + if (it == std::end(mux_) || !(*it).second.user_defined) { + std::string path; + if (pattern[0] == '/') { + path = pattern; + } else { + // skip host part + path = pattern.substr(pattern.find('/')); + } + if (it == std::end(mux_)) { + mux_.emplace(std::move(redirect_pattern), + handler_entry{false, + redirect_handler(301, std::move(path)), + pattern}); + } else { + (*it).second = handler_entry{ + false, redirect_handler(301, std::move(path)), pattern}; + } + } + } + mux_.emplace(pattern, handler_entry{true, std::move(cb), pattern}); + + return true; +} + +request_cb serve_mux::handler(request_impl &req) const { + auto &path = req.uri().path; + if (req.method() != "CONNECT") { + auto clean_path = ::nghttp2::http2::path_join( + nullptr, 0, nullptr, 0, path.c_str(), path.size(), nullptr, 0); + if (clean_path != path) { + auto new_uri = util::percent_encode_path(clean_path); + auto &uref = req.uri(); + if (!uref.raw_query.empty()) { + new_uri += "?"; + new_uri += uref.raw_query; + } + + return redirect_handler(301, std::move(new_uri)); + } + } + auto &host = req.uri().host; + + auto cb = match(host + path); + if (cb) { + return cb; + } + cb = match(path); + if (cb) { + return cb; + } + return status_handler(404); +} + +namespace { +bool path_match(const std::string &pattern, const std::string &path) { + if (pattern.back() != '/') { + return pattern == path; + } + return util::startsWith(path, pattern); +} +} // namespace + +request_cb serve_mux::match(const std::string &path) const { + const handler_entry *ent = nullptr; + size_t best = 0; + for (auto &kv : mux_) { + auto &pattern = kv.first; + if (!path_match(pattern, path)) { + continue; + } + if (!ent || best < pattern.size()) { + best = pattern.size(); + ent = &kv.second; + } + } + if (ent) { + return ent->cb; + } + return request_cb(); +} + +} // namespace server + +} // namespace asio_http2 + +} // namespace nghttp2 diff --git a/src/asio_server_serve_mux.h b/src/asio_server_serve_mux.h new file mode 100644 index 0000000..017a6bc --- /dev/null +++ b/src/asio_server_serve_mux.h @@ -0,0 +1,64 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2015 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef ASIO_SERVER_SERVE_MUX_H +#define ASIO_SERVER_SERVE_MUX_H + +#include "nghttp2_config.h" + +#include + +namespace nghttp2 { + +namespace asio_http2 { + +namespace server { + +class request_impl; + +// port from go's ServeMux + +struct handler_entry { + bool user_defined; + request_cb cb; + std::string pattern; +}; + +class serve_mux { +public: + bool handle(std::string pattern, request_cb cb); + request_cb handler(request_impl &req) const; + request_cb match(const std::string &path) const; + +private: + std::map mux_; +}; + +} // namespace server + +} // namespace asio_http2 + +} // namespace nghttp2 + +#endif // ASIO_SERVER_SERVE_MUX_H diff --git a/src/asio_server_stream.cc b/src/asio_server_stream.cc new file mode 100644 index 0000000..f763c1e --- /dev/null +++ b/src/asio_server_stream.cc @@ -0,0 +1,55 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2015 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "asio_server_stream.h" + +#include "asio_server_http2_handler.h" +#include "asio_server_request_impl.h" +#include "asio_server_response_impl.h" + +namespace nghttp2 { +namespace asio_http2 { +namespace server { + +stream::stream(http2_handler *h, int32_t stream_id) + : handler_(h), stream_id_(stream_id) { + request_.impl().stream(this); + response_.impl().stream(this); +} + +int32_t stream::get_stream_id() const { return stream_id_; } + +class request &stream::request() { + return request_; +} + +class response &stream::response() { + return response_; +} + +http2_handler *stream::handler() const { return handler_; } + +} // namespace server +} // namespace asio_http2 +} // namespace nghttp2 diff --git a/src/asio_server_stream.h b/src/asio_server_stream.h new file mode 100644 index 0000000..cd7e081 --- /dev/null +++ b/src/asio_server_stream.h @@ -0,0 +1,59 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2015 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef ASIO_SERVER_STREAM_H +#define ASIO_SERVER_STREAM_H + +#include "nghttp2_config.h" + +#include + +namespace nghttp2 { +namespace asio_http2 { +namespace server { + +class http2_handler; + +class stream { +public: + stream(http2_handler *h, int32_t stream_id); + + int32_t get_stream_id() const; + class request &request(); + class response &response(); + + http2_handler *handler() const; + +private: + http2_handler *handler_; + class request request_; + class response response_; + int32_t stream_id_; +}; + +} // namespace server +} // namespace asio_http2 +} // namespace nghttp2 + +#endif // ASIO_SERVER_STREAM_H diff --git a/src/asio_server_tls_context.cc b/src/asio_server_tls_context.cc new file mode 100644 index 0000000..11336c9 --- /dev/null +++ b/src/asio_server_tls_context.cc @@ -0,0 +1,85 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2015 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "asio_server_tls_context.h" + +#include + +#include + +#include "ssl.h" +#include "util.h" + +namespace nghttp2 { +namespace asio_http2 { +namespace server { + +namespace { +std::vector &get_alpn_token() { + static auto alpn_token = util::get_default_alpn(); + return alpn_token; +} +} // namespace + +boost::system::error_code +configure_tls_context_easy(boost::system::error_code &ec, + boost::asio::ssl::context &tls_context) { + ec.clear(); + + auto ctx = tls_context.native_handle(); + + SSL_CTX_set_options(ctx, SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | + SSL_OP_NO_COMPRESSION | + SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION | + SSL_OP_SINGLE_ECDH_USE | SSL_OP_NO_TICKET | + SSL_OP_CIPHER_SERVER_PREFERENCE); + SSL_CTX_set_mode(ctx, SSL_MODE_AUTO_RETRY); + SSL_CTX_set_mode(ctx, SSL_MODE_RELEASE_BUFFERS); + + SSL_CTX_set_cipher_list(ctx, ssl::DEFAULT_CIPHER_LIST); + + auto ecdh = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1); + if (ecdh) { + SSL_CTX_set_tmp_ecdh(ctx, ecdh); + EC_KEY_free(ecdh); + } + + SSL_CTX_set_next_protos_advertised_cb( + ctx, + [](SSL *s, const unsigned char **data, unsigned int *len, void *arg) { + auto &token = get_alpn_token(); + + *data = token.data(); + *len = token.size(); + + return SSL_TLSEXT_ERR_OK; + }, + nullptr); + + return ec; +} + +} // namespace server +} // namespace asio_http2 +} // namespace nghttp2 diff --git a/src/asio_server_tls_context.h b/src/asio_server_tls_context.h new file mode 100644 index 0000000..0f9b563 --- /dev/null +++ b/src/asio_server_tls_context.h @@ -0,0 +1,32 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2015 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef ASIO_SERVER_TLS_CONTEXT_H +#define ASIO_SERVER_TLS_CONTEXT_H + +#include "nghttp2_config.h" + +#include + +#endif // ASIO_SERVER_TLS_CONTEXT_H diff --git a/src/base64.h b/src/base64.h new file mode 100644 index 0000000..88e3add --- /dev/null +++ b/src/base64.h @@ -0,0 +1,170 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2013 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef BASE64_H +#define BASE64_H + +#include "nghttp2_config.h" + +#include + +namespace nghttp2 { + +namespace base64 { + +template +std::string encode(InputIterator first, InputIterator last) { + static const char CHAR_TABLE[] = { + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', + 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', + 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', + 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/', + }; + std::string res; + size_t len = last - first; + if (len == 0) { + return res; + } + size_t r = len % 3; + InputIterator j = last - r; + char temp[4]; + while (first != j) { + int n = static_cast(*first++) << 16; + n += static_cast(*first++) << 8; + n += static_cast(*first++); + temp[0] = CHAR_TABLE[n >> 18]; + temp[1] = CHAR_TABLE[(n >> 12) & 0x3fu]; + temp[2] = CHAR_TABLE[(n >> 6) & 0x3fu]; + temp[3] = CHAR_TABLE[n & 0x3fu]; + res.append(temp, sizeof(temp)); + } + if (r == 2) { + int n = static_cast(*first++) << 16; + n += static_cast(*first++) << 8; + temp[0] = CHAR_TABLE[n >> 18]; + temp[1] = CHAR_TABLE[(n >> 12) & 0x3fu]; + temp[2] = CHAR_TABLE[(n >> 6) & 0x3fu]; + temp[3] = '='; + res.append(temp, sizeof(temp)); + } else if (r == 1) { + int n = static_cast(*first++) << 16; + temp[0] = CHAR_TABLE[n >> 18]; + temp[1] = CHAR_TABLE[(n >> 12) & 0x3fu]; + temp[2] = '='; + temp[3] = '='; + res.append(temp, sizeof(temp)); + } + return res; +} + +template +InputIterator getNext(InputIterator first, InputIterator last, const int *tbl) { + for (; first != last; ++first) { + if (tbl[static_cast(*first)] != -1 || *first == '=') { + break; + } + } + return first; +} + +template +std::string decode(InputIterator first, InputIterator last) { + static const int INDEX_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}; + std::string res; + InputIterator k[4]; + int eq = 0; + for (; first != last;) { + for (int i = 1; i <= 4; ++i) { + k[i - 1] = getNext(first, last, INDEX_TABLE); + if (k[i - 1] == last) { + // If i == 1, input may look like this: "TWFu\n" (i.e., + // garbage at the end) + if (i != 1) { + res.clear(); + } + return res; + } else if (*k[i - 1] == '=' && eq == 0) { + eq = i; + } + first = k[i - 1] + 1; + } + if (eq) { + break; + } + int n = (INDEX_TABLE[static_cast(*k[0])] << 18) + + (INDEX_TABLE[static_cast(*k[1])] << 12) + + (INDEX_TABLE[static_cast(*k[2])] << 6) + + INDEX_TABLE[static_cast(*k[3])]; + res += n >> 16; + res += n >> 8 & 0xffu; + res += n & 0xffu; + } + if (eq) { + if (eq <= 2) { + res.clear(); + return res; + } else { + for (int i = eq; i <= 4; ++i) { + if (*k[i - 1] != '=') { + res.clear(); + return res; + } + } + if (eq == 3) { + int n = (INDEX_TABLE[static_cast(*k[0])] << 18) + + (INDEX_TABLE[static_cast(*k[1])] << 12); + res += n >> 16; + } else if (eq == 4) { + int n = (INDEX_TABLE[static_cast(*k[0])] << 18) + + (INDEX_TABLE[static_cast(*k[1])] << 12) + + (INDEX_TABLE[static_cast(*k[2])] << 6); + res += n >> 16; + res += n >> 8 & 0xffu; + } + } + } + return res; +} + +} // namespace base64 + +} // namespace nghttp2 + +#endif // BASE64_H diff --git a/src/buffer.h b/src/buffer.h new file mode 100644 index 0000000..e8c9a5e --- /dev/null +++ b/src/buffer.h @@ -0,0 +1,68 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2014 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef BUFFER_H +#define BUFFER_H + +#include "nghttp2_config.h" + +#include +#include +#include + +namespace nghttp2 { + +template struct Buffer { + Buffer() : pos(std::begin(buf)), last(pos) {} + // Returns the number of bytes to read. + size_t rleft() const { return last - pos; } + // Returns the number of bytes this buffer can store. + size_t wleft() const { return std::end(buf) - last; } + // Writes up to min(wleft(), |count|) bytes from buffer pointed by + // |src|. Returns number of bytes written. + size_t write(const void *src, size_t count) { + count = std::min(count, wleft()); + auto p = static_cast(src); + last = std::copy_n(p, count, last); + return count; + } + size_t write(size_t count) { + count = std::min(count, wleft()); + last += count; + return count; + } + // Drains min(rleft(), |count|) bytes from start of the buffer. + size_t drain(size_t count) { + count = std::min(count, rleft()); + pos += count; + return count; + } + void reset() { pos = last = std::begin(buf); } + std::array buf; + uint8_t *pos, *last; +}; + +} // namespace nghttp2 + +#endif // BUFFER_H diff --git a/src/buffer_test.cc b/src/buffer_test.cc new file mode 100644 index 0000000..38688ed --- /dev/null +++ b/src/buffer_test.cc @@ -0,0 +1,78 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2015 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "buffer_test.h" + +#include +#include +#include + +#include + +#include + +#include "buffer.h" + +namespace nghttp2 { + +void test_buffer_write(void) { + Buffer<16> b; + CU_ASSERT(0 == b.rleft()); + CU_ASSERT(16 == b.wleft()); + + b.write("012", 3); + + CU_ASSERT(3 == b.rleft()); + CU_ASSERT(13 == b.wleft()); + CU_ASSERT(b.pos == std::begin(b.buf)); + + b.drain(3); + + CU_ASSERT(0 == b.rleft()); + CU_ASSERT(13 == b.wleft()); + CU_ASSERT(3 == b.pos - std::begin(b.buf)); + + auto n = b.write("0123456789ABCDEF", 16); + + CU_ASSERT(n == 13); + + CU_ASSERT(13 == b.rleft()); + CU_ASSERT(0 == b.wleft()); + CU_ASSERT(3 == b.pos - std::begin(b.buf)); + CU_ASSERT(0 == memcmp(b.pos, "0123456789ABC", 13)); + + b.reset(); + + CU_ASSERT(0 == b.rleft()); + CU_ASSERT(16 == b.wleft()); + CU_ASSERT(b.pos == std::begin(b.buf)); + + b.write(5); + + CU_ASSERT(5 == b.rleft()); + CU_ASSERT(11 == b.wleft()); + CU_ASSERT(b.pos == std::begin(b.buf)); +} + +} // namespace nghttp2 diff --git a/src/buffer_test.h b/src/buffer_test.h new file mode 100644 index 0000000..abdc987 --- /dev/null +++ b/src/buffer_test.h @@ -0,0 +1,38 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2015 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef BUFFER_TEST_H +#define BUFFER_TEST_H + +#ifdef HAVE_CONFIG_H +#include +#endif // HAVE_CONFIG_H + +namespace nghttp2 { + +void test_buffer_write(void); + +} // namespace nghttp2 + +#endif // BUFFER_TEST_H diff --git a/src/comp_helper.c b/src/comp_helper.c new file mode 100644 index 0000000..8e0c03d --- /dev/null +++ b/src/comp_helper.c @@ -0,0 +1,91 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2013 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "comp_helper.h" +#include + +static void dump_val(json_t *jent, const char *key, uint8_t *val, size_t len) { + json_object_set_new(jent, key, json_pack("s#", val, len)); +} + +json_t *dump_header_table(nghttp2_hd_context *context) { + json_t *obj, *entries; + size_t i; + + obj = json_object(); + entries = json_array(); + for (i = 0; i < context->hd_table.len; ++i) { + nghttp2_hd_entry *ent = nghttp2_hd_table_get(context, i); + json_t *outent = json_object(); + json_object_set_new(outent, "index", json_integer(i + 1)); + dump_val(outent, "name", ent->nv.name, ent->nv.namelen); + dump_val(outent, "value", ent->nv.value, ent->nv.valuelen); + json_object_set_new(outent, "size", + json_integer(ent->nv.namelen + ent->nv.valuelen + + NGHTTP2_HD_ENTRY_OVERHEAD)); + json_array_append_new(entries, outent); + } + json_object_set_new(obj, "entries", entries); + json_object_set_new(obj, "size", json_integer(context->hd_table_bufsize)); + json_object_set_new(obj, "max_size", + json_integer(context->hd_table_bufsize_max)); + return obj; +} + +json_t *dump_header(const uint8_t *name, size_t namelen, const uint8_t *value, + size_t valuelen) { + json_t *nv_pair = json_object(); + char *cname = malloc(namelen + 1); + if (cname == NULL) { + return NULL; + } + memcpy(cname, name, namelen); + cname[namelen] = '\0'; + json_object_set_new(nv_pair, cname, json_pack("s#", value, valuelen)); + free(cname); + return nv_pair; +} + +json_t *dump_headers(const nghttp2_nv *nva, size_t nvlen) { + json_t *headers; + size_t i; + + headers = json_array(); + for (i = 0; i < nvlen; ++i) { + json_array_append_new(headers, dump_header(nva[i].name, nva[i].namelen, + nva[i].value, nva[i].valuelen)); + } + return headers; +} + +void output_json_header(void) { + printf("{\n" + " \"cases\":\n" + " [\n"); +} + +void output_json_footer(void) { + printf(" ]\n" + "}\n"); +} diff --git a/src/comp_helper.h b/src/comp_helper.h new file mode 100644 index 0000000..e86defc --- /dev/null +++ b/src/comp_helper.h @@ -0,0 +1,47 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2013 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGHTTP2_COMP_HELPER_H +#define NGHTTP2_COMP_HELPER_H + +#ifdef HAVE_CONFIG_H +#include +#endif // HAVE_CONFIG_H + +#include + +#include "nghttp2_hd.h" + +json_t *dump_header_table(nghttp2_hd_context *context); + +json_t *dump_header(const uint8_t *name, size_t namelen, const uint8_t *value, + size_t vlauelen); + +json_t *dump_headers(const nghttp2_nv *nva, size_t nvlen); + +void output_json_header(void); + +void output_json_footer(void); + +#endif // NGHTTP2_COMP_HELPER_H diff --git a/src/deflatehd.cc b/src/deflatehd.cc new file mode 100644 index 0000000..cd0c5d5 --- /dev/null +++ b/src/deflatehd.cc @@ -0,0 +1,450 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2013 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifdef HAVE_CONFIG_H +#include +#endif // HAVE_CONFIG_H + +#ifdef HAVE_UNISTD_H +#include +#endif // HAVE_UNISTD_H +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +extern "C" { + +#include "nghttp2_hd.h" +#include "nghttp2_frame.h" + +#include "comp_helper.h" +} + +typedef struct { + size_t table_size; + size_t deflate_table_size; + int http1text; + int dump_header_table; +} deflate_config; + +static deflate_config config; + +static size_t input_sum; +static size_t output_sum; + +static char to_hex_digit(uint8_t n) { + if (n > 9) { + return n - 10 + 'a'; + } + return n + '0'; +} + +static void to_hex(char *dest, const uint8_t *src, size_t len) { + size_t i; + for (i = 0; i < len; ++i) { + *dest++ = to_hex_digit(src[i] >> 4); + *dest++ = to_hex_digit(src[i] & 0xf); + } +} + +static void output_to_json(nghttp2_hd_deflater *deflater, nghttp2_bufs *bufs, + size_t inputlen, const std::vector &nva, + int seq) { + auto len = nghttp2_bufs_len(bufs); + auto hex = std::vector(len * 2); + auto obj = json_object(); + auto comp_ratio = inputlen == 0 ? 0.0 : (double)len / inputlen * 100; + + json_object_set_new(obj, "seq", json_integer(seq)); + json_object_set_new(obj, "input_length", json_integer(inputlen)); + json_object_set_new(obj, "output_length", json_integer(len)); + json_object_set_new(obj, "percentage_of_original_size", + json_real(comp_ratio)); + + auto hexp = hex.data(); + for (auto ci = bufs->head; ci; ci = ci->next) { + auto buf = &ci->buf; + to_hex(hexp, buf->pos, nghttp2_buf_len(buf)); + hexp += nghttp2_buf_len(buf); + } + + if (len == 0) { + json_object_set_new(obj, "wire", json_string("")); + } else { + json_object_set_new(obj, "wire", json_pack("s#", hex.data(), hex.size())); + } + json_object_set_new(obj, "headers", dump_headers(nva.data(), nva.size())); + if (seq == 0) { + // We only change the header table size only once at the beginning + json_object_set_new(obj, "header_table_size", + json_integer(config.table_size)); + } + if (config.dump_header_table) { + json_object_set_new(obj, "header_table", dump_header_table(&deflater->ctx)); + } + json_dumpf(obj, stdout, JSON_PRESERVE_ORDER | JSON_INDENT(2)); + printf("\n"); + json_decref(obj); +} + +static void deflate_hd(nghttp2_hd_deflater *deflater, + const std::vector &nva, size_t inputlen, + int seq) { + ssize_t rv; + nghttp2_bufs bufs; + + nghttp2_bufs_init2(&bufs, 4096, 16, 0, nghttp2_mem_default()); + + rv = nghttp2_hd_deflate_hd_bufs(deflater, &bufs, (nghttp2_nv *)nva.data(), + nva.size()); + if (rv < 0) { + fprintf(stderr, "deflate failed with error code %zd at %d\n", rv, seq); + exit(EXIT_FAILURE); + } + + input_sum += inputlen; + output_sum += nghttp2_bufs_len(&bufs); + + output_to_json(deflater, &bufs, inputlen, nva, seq); + nghttp2_bufs_free(&bufs); +} + +static int deflate_hd_json(json_t *obj, nghttp2_hd_deflater *deflater, + int seq) { + size_t inputlen = 0; + + auto js = json_object_get(obj, "headers"); + if (js == nullptr) { + fprintf(stderr, "'headers' key is missing at %d\n", seq); + return -1; + } + if (!json_is_array(js)) { + fprintf(stderr, "The value of 'headers' key must be an array at %d\n", seq); + return -1; + } + + auto len = json_array_size(js); + auto nva = std::vector(len); + + for (size_t i = 0; i < len; ++i) { + auto nv_pair = json_array_get(js, i); + const char *name; + json_t *value; + + if (!json_is_object(nv_pair) || json_object_size(nv_pair) != 1) { + fprintf(stderr, "bad formatted name/value pair object at %d\n", seq); + return -1; + } + + json_object_foreach(nv_pair, name, value) { + nva[i].name = (uint8_t *)name; + nva[i].namelen = strlen(name); + + if (!json_is_string(value)) { + fprintf(stderr, "value is not string at %d\n", seq); + return -1; + } + + nva[i].value = (uint8_t *)json_string_value(value); + nva[i].valuelen = strlen(json_string_value(value)); + + nva[i].flags = NGHTTP2_NV_FLAG_NONE; + } + + inputlen += nva[i].namelen + nva[i].valuelen; + } + + deflate_hd(deflater, nva, inputlen, seq); + + return 0; +} + +static nghttp2_hd_deflater *init_deflater() { + nghttp2_hd_deflater *deflater; + nghttp2_hd_deflate_new(&deflater, config.deflate_table_size); + nghttp2_hd_deflate_change_table_size(deflater, config.table_size); + return deflater; +} + +static void deinit_deflater(nghttp2_hd_deflater *deflater) { + nghttp2_hd_deflate_del(deflater); +} + +static int perform(void) { + json_error_t error; + + auto json = json_loadf(stdin, 0, &error); + + if (json == nullptr) { + fprintf(stderr, "JSON loading failed\n"); + exit(EXIT_FAILURE); + } + + auto cases = json_object_get(json, "cases"); + + if (cases == nullptr) { + fprintf(stderr, "Missing 'cases' key in root object\n"); + exit(EXIT_FAILURE); + } + + if (!json_is_array(cases)) { + fprintf(stderr, "'cases' must be JSON array\n"); + exit(EXIT_FAILURE); + } + + auto deflater = init_deflater(); + output_json_header(); + auto len = json_array_size(cases); + + for (size_t i = 0; i < len; ++i) { + auto obj = json_array_get(cases, i); + if (!json_is_object(obj)) { + fprintf(stderr, "Unexpected JSON type at %zu. It should be object.\n", i); + continue; + } + if (deflate_hd_json(obj, deflater, i) != 0) { + continue; + } + if (i + 1 < len) { + printf(",\n"); + } + } + output_json_footer(); + deinit_deflater(deflater); + json_decref(json); + return 0; +} + +static int perform_from_http1text(void) { + char line[1 << 14]; + int seq = 0; + + auto deflater = init_deflater(); + output_json_header(); + for (;;) { + std::vector nva; + int end = 0; + size_t inputlen = 0; + + for (;;) { + char *rv = fgets(line, sizeof(line), stdin); + char *val, *val_end; + if (rv == nullptr) { + end = 1; + break; + } else if (line[0] == '\n') { + break; + } + + nva.emplace_back(); + auto &nv = nva.back(); + + val = strchr(line + 1, ':'); + if (val == nullptr) { + fprintf(stderr, "Bad HTTP/1 header field format at %d.\n", seq); + exit(EXIT_FAILURE); + } + *val = '\0'; + ++val; + for (; *val && (*val == ' ' || *val == '\t'); ++val) + ; + for (val_end = val; *val_end && (*val_end != '\r' && *val_end != '\n'); + ++val_end) + ; + *val_end = '\0'; + + nv.namelen = strlen(line); + nv.valuelen = strlen(val); + nv.name = (uint8_t *)strdup(line); + nv.value = (uint8_t *)strdup(val); + nv.flags = NGHTTP2_NV_FLAG_NONE; + + inputlen += nv.namelen + nv.valuelen; + } + + if (!end) { + if (seq > 0) { + printf(",\n"); + } + deflate_hd(deflater, nva, inputlen, seq); + } + + for (auto &nv : nva) { + free(nv.name); + free(nv.value); + } + + if (end) + break; + ++seq; + } + output_json_footer(); + deinit_deflater(deflater); + return 0; +} + +static void print_help(void) { + std::cout << R"(HPACK HTTP/2 header encoder +Usage: deflatehd [OPTIONS] < INPUT + +Reads JSON data or HTTP/1-style header fields from stdin and outputs +deflated header block in JSON array. + +For the JSON input, the root JSON object must contain "context" key, +which indicates which compression context is used. If it is +"request", request compression context is used. Otherwise, response +compression context is used. The value of "cases" key contains the +sequence of input header set. They share the same compression context +and are processed in the order they appear. Each item in the sequence +is a JSON object and it must have at least "headers" key. Its value +is an array of a JSON object containing exactly one name/value pair. + +Example: +{ + "context": "request", + "cases": + [ + { + "headers": [ + { ":method": "GET" }, + { ":path": "/" } + ] + }, + { + "headers": [ + { ":method": "POST" }, + { ":path": "/" } + ] + } + ] +} + +With -t option, the program can accept more familiar HTTP/1 style +header field block. Each header set must be followed by one empty +line: + +Example: + +:method: GET +:scheme: https +:path: / + +:method: POST +user-agent: nghttp2 + +The output of this program can be used as input for inflatehd. + +OPTIONS: + -t, --http1text Use HTTP/1 style header field text as input. + Each header set is delimited by single empty + line. + -s, --table-size= + Set dynamic table size. In the HPACK + specification, this value is denoted by + SETTINGS_HEADER_TABLE_SIZE. + Default: 4096 + -S, --deflate-table-size= + Use first N bytes of dynamic header table + buffer. + Default: 4096 + -d, --dump-header-table + Output dynamic header table.)" << std::endl; +} + +static struct option long_options[] = { + {"http1text", no_argument, nullptr, 't'}, + {"table-size", required_argument, nullptr, 's'}, + {"deflate-table-size", required_argument, nullptr, 'S'}, + {"dump-header-table", no_argument, nullptr, 'd'}, + {nullptr, 0, nullptr, 0}}; + +int main(int argc, char **argv) { + char *end; + + config.table_size = NGHTTP2_HD_DEFAULT_MAX_BUFFER_SIZE; + config.deflate_table_size = NGHTTP2_HD_DEFAULT_MAX_DEFLATE_BUFFER_SIZE; + config.http1text = 0; + config.dump_header_table = 0; + while (1) { + int option_index = 0; + int c = getopt_long(argc, argv, "S:dhs:t", long_options, &option_index); + if (c == -1) { + break; + } + switch (c) { + case 'h': + print_help(); + exit(EXIT_SUCCESS); + case 't': + // --http1text + config.http1text = 1; + break; + case 's': + // --table-size + errno = 0; + config.table_size = strtoul(optarg, &end, 10); + if (errno == ERANGE || *end != '\0') { + fprintf(stderr, "-s: Bad option value\n"); + exit(EXIT_FAILURE); + } + break; + case 'S': + // --deflate-table-size + errno = 0; + config.deflate_table_size = strtoul(optarg, &end, 10); + if (errno == ERANGE || *end != '\0') { + fprintf(stderr, "-S: Bad option value\n"); + exit(EXIT_FAILURE); + } + break; + case 'd': + // --dump-header-table + config.dump_header_table = 1; + break; + case '?': + exit(EXIT_FAILURE); + default: + break; + } + } + if (config.http1text) { + perform_from_http1text(); + } else { + perform(); + } + + auto comp_ratio = input_sum == 0 ? 0.0 : (double)output_sum / input_sum; + + fprintf(stderr, "Overall: input=%zu output=%zu ratio=%.02f\n", input_sum, + output_sum, comp_ratio); + return 0; +} diff --git a/src/h2load.cc b/src/h2load.cc new file mode 100644 index 0000000..5578415 --- /dev/null +++ b/src/h2load.cc @@ -0,0 +1,1528 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2014 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "h2load.h" + +#include +#include +#ifdef HAVE_NETINET_IN_H +#include +#endif // HAVE_NETINET_IN_H +#include +#include +#ifdef HAVE_FCNTL_H +#include +#endif // HAVE_FCNTL_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_SPDYLAY +#include +#endif // HAVE_SPDYLAY + +#include +#include + +#include "http-parser/http_parser.h" + +#include "h2load_http2_session.h" +#ifdef HAVE_SPDYLAY +#include "h2load_spdy_session.h" +#endif // HAVE_SPDYLAY +#include "ssl.h" +#include "http2.h" +#include "util.h" +#include "template.h" + +#ifndef O_BINARY +#define O_BINARY (0) +#endif // O_BINARY + +using namespace nghttp2; + +namespace h2load { + +Config::Config() + : data_length(-1), addrs(nullptr), nreqs(1), nclients(1), nthreads(1), + max_concurrent_streams(-1), window_bits(30), connection_window_bits(30), + no_tls_proto(PROTO_HTTP2), data_fd(-1), port(0), default_port(0), + verbose(false) {} + +Config::~Config() { + freeaddrinfo(addrs); + + if (data_fd != -1) { + close(data_fd); + } +} + +Config config; + +namespace { +void debug(const char *format, ...) { + if (config.verbose) { + fprintf(stderr, "[DEBUG] "); + va_list ap; + va_start(ap, format); + vfprintf(stderr, format, ap); + va_end(ap); + } +} +} // namespace + +namespace { +void debug_nextproto_error() { +#ifdef HAVE_SPDYLAY + debug("no supported protocol was negotiated, expected: %s, " + "spdy/2, spdy/3, spdy/3.1\n", + NGHTTP2_PROTO_VERSION_ID); +#else // !HAVE_SPDYLAY + debug("no supported protocol was negotiated, expected: %s\n", + NGHTTP2_PROTO_VERSION_ID); +#endif // !HAVE_SPDYLAY +} +} // namespace + +RequestStat::RequestStat() : data_offset(0), completed(false) {} + +Stats::Stats(size_t req_todo) + : req_todo(0), req_started(0), req_done(0), req_success(0), + req_status_success(0), req_failed(0), req_error(0), bytes_total(0), + bytes_head(0), bytes_body(0), status(), req_stats(req_todo) {} + +Stream::Stream() : status_success(-1) {} + +namespace { +void writecb(struct ev_loop *loop, ev_io *w, int revents) { + auto client = static_cast(w->data); + auto rv = client->do_write(); + if (rv == Client::ERR_CONNECT_FAIL) { + client->disconnect(); + rv = client->connect(); + if (rv != 0) { + client->fail(); + return; + } + return; + } + if (rv != 0) { + client->fail(); + } +} +} // namespace + +namespace { +void readcb(struct ev_loop *loop, ev_io *w, int revents) { + auto client = static_cast(w->data); + if (client->do_read() != 0) { + client->fail(); + return; + } + writecb(loop, &client->wev, revents); + // client->disconnect() and client->fail() may be called +} +} // namespace + +Client::Client(Worker *worker, size_t req_todo) + : worker(worker), ssl(nullptr), next_addr(config.addrs), reqidx(0), + state(CLIENT_IDLE), first_byte_received(false), req_todo(req_todo), + req_started(0), req_done(0), fd(-1) { + ev_io_init(&wev, writecb, 0, EV_WRITE); + ev_io_init(&rev, readcb, 0, EV_READ); + + wev.data = this; + rev.data = this; +} + +Client::~Client() { disconnect(); } + +int Client::do_read() { return readfn(*this); } +int Client::do_write() { return writefn(*this); } + +int Client::connect() { + record_start_time(&worker->stats); + + while (next_addr) { + auto addr = next_addr; + next_addr = next_addr->ai_next; + fd = util::create_nonblock_socket(addr->ai_family); + if (fd == -1) { + continue; + } + if (config.scheme == "https") { + ssl = SSL_new(worker->ssl_ctx); + + auto config = worker->config; + + if (!util::numeric_host(config->host.c_str())) { + SSL_set_tlsext_host_name(ssl, config->host.c_str()); + } + + SSL_set_fd(ssl, fd); + SSL_set_connect_state(ssl); + } + + auto rv = ::connect(fd, addr->ai_addr, addr->ai_addrlen); + if (rv != 0 && errno != EINPROGRESS) { + if (ssl) { + SSL_free(ssl); + ssl = nullptr; + } + close(fd); + fd = -1; + continue; + } + break; + } + + if (fd == -1) { + return -1; + } + + writefn = &Client::connected; + + ev_io_set(&rev, fd, EV_READ); + ev_io_set(&wev, fd, EV_WRITE); + + ev_io_start(worker->loop, &wev); + + return 0; +} + +void Client::fail() { + process_abandoned_streams(); + + disconnect(); +} + +void Client::disconnect() { + streams.clear(); + session.reset(); + state = CLIENT_IDLE; + ev_io_stop(worker->loop, &wev); + ev_io_stop(worker->loop, &rev); + if (ssl) { + SSL_set_shutdown(ssl, SSL_RECEIVED_SHUTDOWN); + ERR_clear_error(); + SSL_shutdown(ssl); + SSL_free(ssl); + ssl = nullptr; + } + if (fd != -1) { + shutdown(fd, SHUT_WR); + close(fd); + fd = -1; + } +} + +void Client::submit_request() { + auto req_stat = &worker->stats.req_stats[worker->stats.req_started++]; + session->submit_request(req_stat); + ++req_started; +} + +void Client::process_abandoned_streams() { + auto req_abandoned = req_todo - req_done; + + worker->stats.req_failed += req_abandoned; + worker->stats.req_error += req_abandoned; + worker->stats.req_done += req_abandoned; + + req_done = req_todo; +} + +void Client::report_progress() { + if (worker->id == 0 && + worker->stats.req_done % worker->progress_interval == 0) { + std::cout << "progress: " + << worker->stats.req_done * 100 / worker->stats.req_todo + << "% done" << std::endl; + } +} + +namespace { +const char *get_tls_protocol(SSL *ssl) { + auto session = SSL_get_session(ssl); + + switch (session->ssl_version) { + case SSL2_VERSION: + return "SSLv2"; + case SSL3_VERSION: + return "SSLv3"; + case TLS1_2_VERSION: + return "TLSv1.2"; + case TLS1_1_VERSION: + return "TLSv1.1"; + case TLS1_VERSION: + return "TLSv1"; + default: + return "unknown"; + } +} +} // namespace + +namespace { +void print_server_tmp_key(SSL *ssl) { +#if OPENSSL_VERSION_NUMBER >= 0x10002000L + EVP_PKEY *key; + + if (!SSL_get_server_tmp_key(ssl, &key)) { + return; + } + + auto key_del = defer(EVP_PKEY_free, key); + + std::cout << "Server Temp Key: "; + + switch (EVP_PKEY_id(key)) { + case EVP_PKEY_RSA: + std::cout << "RSA " << EVP_PKEY_bits(key) << " bits" << std::endl; + break; + case EVP_PKEY_DH: + std::cout << "DH " << EVP_PKEY_bits(key) << " bits" << std::endl; + break; + case EVP_PKEY_EC: { + auto ec = EVP_PKEY_get1_EC_KEY(key); + auto ec_del = defer(EC_KEY_free, ec); + auto nid = EC_GROUP_get_curve_name(EC_KEY_get0_group(ec)); + auto cname = EC_curve_nid2nist(nid); + if (!cname) { + cname = OBJ_nid2sn(nid); + } + + std::cout << "ECDH " << cname << " " << EVP_PKEY_bits(key) << " bits" + << std::endl; + break; + } + } +#endif // OPENSSL_VERSION_NUMBER >= 0x10002000L +} +} // namespace + +void Client::report_tls_info() { + if (worker->id == 0 && !worker->tls_info_report_done) { + worker->tls_info_report_done = true; + auto cipher = SSL_get_current_cipher(ssl); + std::cout << "Protocol: " << get_tls_protocol(ssl) << "\n" + << "Cipher: " << SSL_CIPHER_get_name(cipher) << std::endl; + print_server_tmp_key(ssl); + } +} + +void Client::terminate_session() { session->terminate(); } + +void Client::on_request(int32_t stream_id) { streams[stream_id] = Stream(); } + +void Client::on_header(int32_t stream_id, const uint8_t *name, size_t namelen, + const uint8_t *value, size_t valuelen) { + auto itr = streams.find(stream_id); + if (itr == std::end(streams)) { + return; + } + auto &stream = (*itr).second; + if (stream.status_success == -1 && namelen == 7 && + util::streq_l(":status", name, namelen)) { + int status = 0; + for (size_t i = 0; i < valuelen; ++i) { + if ('0' <= value[i] && value[i] <= '9') { + status *= 10; + status += value[i] - '0'; + if (status > 999) { + stream.status_success = 0; + return; + } + } else { + break; + } + } + + if (status >= 200 && status < 300) { + ++worker->stats.status[2]; + stream.status_success = 1; + } else if (status < 400) { + ++worker->stats.status[3]; + stream.status_success = 1; + } else if (status < 600) { + ++worker->stats.status[status / 100]; + stream.status_success = 0; + } else { + stream.status_success = 0; + } + } +} + +void Client::on_stream_close(int32_t stream_id, bool success, + RequestStat *req_stat) { + req_stat->stream_close_time = std::chrono::steady_clock::now(); + if (success) { + req_stat->completed = true; + ++worker->stats.req_success; + } + ++worker->stats.req_done; + ++req_done; + if (success && streams[stream_id].status_success == 1) { + ++worker->stats.req_status_success; + } else { + ++worker->stats.req_failed; + } + report_progress(); + streams.erase(stream_id); + if (req_done == req_todo) { + terminate_session(); + return; + } + + if (req_started < req_todo) { + submit_request(); + return; + } +} + +int Client::connection_made() { + if (ssl) { + report_tls_info(); + + const unsigned char *next_proto = nullptr; + unsigned int next_proto_len; + SSL_get0_next_proto_negotiated(ssl, &next_proto, &next_proto_len); + for (int i = 0; i < 2; ++i) { + if (next_proto) { + if (util::check_h2_is_selected(next_proto, next_proto_len)) { + session = make_unique(this); + } else { +#ifdef HAVE_SPDYLAY + auto spdy_version = + spdylay_npn_get_version(next_proto, next_proto_len); + if (spdy_version) { + session = make_unique(this, spdy_version); + } else { + debug_nextproto_error(); + fail(); + return -1; + } +#else // !HAVE_SPDYLAY + debug_nextproto_error(); + fail(); + return -1; +#endif // !HAVE_SPDYLAY + } + } + +#if OPENSSL_VERSION_NUMBER >= 0x10002000L + SSL_get0_alpn_selected(ssl, &next_proto, &next_proto_len); +#else // OPENSSL_VERSION_NUMBER < 0x10002000L + break; +#endif // OPENSSL_VERSION_NUMBER < 0x10002000L + } + + if (!next_proto) { + debug_nextproto_error(); + fail(); + return -1; + } + } else { + switch (config.no_tls_proto) { + case Config::PROTO_HTTP2: + session = make_unique(this); + break; +#ifdef HAVE_SPDYLAY + case Config::PROTO_SPDY2: + session = make_unique(this, SPDYLAY_PROTO_SPDY2); + break; + case Config::PROTO_SPDY3: + session = make_unique(this, SPDYLAY_PROTO_SPDY3); + break; + case Config::PROTO_SPDY3_1: + session = make_unique(this, SPDYLAY_PROTO_SPDY3_1); + break; +#endif // HAVE_SPDYLAY + default: + // unreachable + assert(0); + } + } + + state = CLIENT_CONNECTED; + + session->on_connect(); + + record_connect_time(&worker->stats); + + auto nreq = + std::min(req_todo - req_started, (size_t)config.max_concurrent_streams); + + for (; nreq > 0; --nreq) { + submit_request(); + } + + signal_write(); + + return 0; +} + +int Client::on_read(const uint8_t *data, size_t len) { + auto rv = session->on_read(data, len); + if (rv != 0) { + return -1; + } + worker->stats.bytes_total += len; + signal_write(); + return 0; +} + +int Client::on_write() { + if (session->on_write() != 0) { + return -1; + } + return 0; +} + +int Client::read_clear() { + uint8_t buf[8192]; + + for (;;) { + ssize_t nread; + while ((nread = read(fd, buf, sizeof(buf))) == -1 && errno == EINTR) + ; + if (nread == -1) { + if (errno == EAGAIN || errno == EWOULDBLOCK) { + return 0; + } + return -1; + } + + if (nread == 0) { + return -1; + } + + if (on_read(buf, nread) != 0) { + return -1; + } + + if (!first_byte_received) { + first_byte_received = true; + record_ttfb(&worker->stats); + } + } + + return 0; +} + +int Client::write_clear() { + for (;;) { + if (wb.rleft() > 0) { + ssize_t nwrite; + while ((nwrite = write(fd, wb.pos, wb.rleft())) == -1 && errno == EINTR) + ; + if (nwrite == -1) { + if (errno == EAGAIN || errno == EWOULDBLOCK) { + ev_io_start(worker->loop, &wev); + return 0; + } + return -1; + } + wb.drain(nwrite); + continue; + } + wb.reset(); + if (on_write() != 0) { + return -1; + } + if (wb.rleft() == 0) { + break; + } + } + + ev_io_stop(worker->loop, &wev); + + return 0; +} + +int Client::connected() { + if (!util::check_socket_connected(fd)) { + return ERR_CONNECT_FAIL; + } + ev_io_start(worker->loop, &rev); + ev_io_stop(worker->loop, &wev); + + if (ssl) { + readfn = &Client::tls_handshake; + writefn = &Client::tls_handshake; + + return do_write(); + } + + readfn = &Client::read_clear; + writefn = &Client::write_clear; + + if (connection_made() != 0) { + return -1; + } + + return 0; +} + +int Client::tls_handshake() { + ERR_clear_error(); + + auto rv = SSL_do_handshake(ssl); + + if (rv == 0) { + return -1; + } + + if (rv < 0) { + auto err = SSL_get_error(ssl, rv); + switch (err) { + case SSL_ERROR_WANT_READ: + ev_io_stop(worker->loop, &wev); + return 0; + case SSL_ERROR_WANT_WRITE: + ev_io_start(worker->loop, &wev); + return 0; + default: + return -1; + } + } + + ev_io_stop(worker->loop, &wev); + + readfn = &Client::read_tls; + writefn = &Client::write_tls; + + if (connection_made() != 0) { + return -1; + } + + return 0; +} + +int Client::read_tls() { + uint8_t buf[8192]; + + ERR_clear_error(); + + for (;;) { + auto rv = SSL_read(ssl, buf, sizeof(buf)); + + if (rv == 0) { + return -1; + } + + if (rv < 0) { + auto err = SSL_get_error(ssl, rv); + switch (err) { + case SSL_ERROR_WANT_READ: + return 0; + case SSL_ERROR_WANT_WRITE: + // renegotiation started + return -1; + default: + return -1; + } + } + + if (on_read(buf, rv) != 0) { + return -1; + } + + if (!first_byte_received) { + first_byte_received = true; + record_ttfb(&worker->stats); + } + } +} + +int Client::write_tls() { + ERR_clear_error(); + + for (;;) { + if (wb.rleft() > 0) { + auto rv = SSL_write(ssl, wb.pos, wb.rleft()); + + if (rv == 0) { + return -1; + } + + if (rv < 0) { + auto err = SSL_get_error(ssl, rv); + switch (err) { + case SSL_ERROR_WANT_READ: + // renegotiation started + return -1; + case SSL_ERROR_WANT_WRITE: + ev_io_start(worker->loop, &wev); + return 0; + default: + return -1; + } + } + + wb.drain(rv); + + continue; + } + wb.reset(); + if (on_write() != 0) { + return -1; + } + if (wb.rleft() == 0) { + break; + } + } + + ev_io_stop(worker->loop, &wev); + + return 0; +} + +void Client::record_request_time(RequestStat *req_stat) { + req_stat->request_time = std::chrono::steady_clock::now(); +} + +void Client::record_start_time(Stats *stat) { + stat->start_times.push_back(std::chrono::steady_clock::now()); +} + +void Client::record_connect_time(Stats *stat) { + stat->connect_times.push_back(std::chrono::steady_clock::now()); +} + +void Client::record_ttfb(Stats *stat) { + stat->ttfbs.push_back(std::chrono::steady_clock::now()); +} + +void Client::signal_write() { ev_io_start(worker->loop, &wev); } + +Worker::Worker(uint32_t id, SSL_CTX *ssl_ctx, size_t req_todo, size_t nclients, + Config *config) + : stats(req_todo), loop(ev_loop_new(0)), ssl_ctx(ssl_ctx), config(config), + id(id), tls_info_report_done(false) { + stats.req_todo = req_todo; + progress_interval = std::max((size_t)1, req_todo / 10); + + auto nreqs_per_client = req_todo / nclients; + auto nreqs_rem = req_todo % nclients; + + for (size_t i = 0; i < nclients; ++i) { + auto req_todo = nreqs_per_client; + if (nreqs_rem > 0) { + ++req_todo; + --nreqs_rem; + } + clients.push_back(make_unique(this, req_todo)); + } +} + +Worker::~Worker() { + // first clear clients so that io watchers are stopped before + // destructing ev_loop. + clients.clear(); + ev_loop_destroy(loop); +} + +void Worker::run() { + for (auto &client : clients) { + if (client->connect() != 0) { + std::cerr << "client could not connect to host" << std::endl; + client->fail(); + } + } + ev_run(loop, 0); +} + +namespace { +// Returns percentage of number of samples within mean +/- sd. +template +double within_sd(const std::vector &samples, const Duration &mean, + const Duration &sd) { + if (samples.size() == 0) { + return 0.0; + } + auto lower = mean - sd; + auto upper = mean + sd; + auto m = std::count_if( + std::begin(samples), std::end(samples), + [&lower, &upper](const Duration &t) { return lower <= t && t <= upper; }); + return (m / static_cast(samples.size())) * 100; +} +} // namespace + +namespace { +// Computes statistics using |samples|. The min, max, mean, sd, and +// percentage of number of samples within mean +/- sd are computed. +template +TimeStat compute_time_stat(const std::vector &samples) { + if (samples.size() == 0) { + return {Duration::zero(), Duration::zero(), Duration::zero(), + Duration::zero(), 0.0}; + } + // standard deviation calculated using Rapid calculation method: + // http://en.wikipedia.org/wiki/Standard_deviation#Rapid_calculation_methods + double a = 0, q = 0; + size_t n = 0; + int64_t sum = 0; + auto res = TimeStat{Duration::max(), Duration::min()}; + for (const auto &t : samples) { + ++n; + res.min = std::min(res.min, t); + res.max = std::max(res.max, t); + sum += t.count(); + + auto na = a + (t.count() - a) / n; + q += (t.count() - a) * (t.count() - na); + a = na; + } + + res.mean = Duration(sum / n); + res.sd = Duration(static_cast(sqrt(q / n))); + res.within_sd = within_sd(samples, res.mean, res.sd); + + return res; +} +} // namespace + +namespace { +TimeStats +process_time_stats(const std::vector> &workers) { + size_t nrequest_times = 0, nttfb_times = 0; + for (const auto &w : workers) { + nrequest_times += w->stats.req_stats.size(); + nttfb_times += w->stats.ttfbs.size(); + } + + std::vector request_times; + request_times.reserve(nrequest_times); + std::vector connect_times, ttfb_times; + connect_times.reserve(nttfb_times); + ttfb_times.reserve(nttfb_times); + + for (const auto &w : workers) { + for (const auto &req_stat : w->stats.req_stats) { + if (!req_stat.completed) { + continue; + } + request_times.push_back( + std::chrono::duration_cast( + req_stat.stream_close_time - req_stat.request_time)); + } + + const auto &stat = w->stats; + // rule out cases where we started but didn't connect or get the + // first byte (errors). We will get connect event before FFTB. + assert(stat.start_times.size() >= stat.ttfbs.size()); + assert(stat.connect_times.size() >= stat.ttfbs.size()); + for (size_t i = 0; i < stat.ttfbs.size(); ++i) { + connect_times.push_back( + std::chrono::duration_cast( + stat.connect_times[i] - stat.start_times[i])); + + ttfb_times.push_back( + std::chrono::duration_cast( + stat.ttfbs[i] - stat.start_times[i])); + } + } + + return {compute_time_stat(request_times), compute_time_stat(connect_times), + compute_time_stat(ttfb_times)}; +} +} // namespace + +namespace { +void resolve_host() { + int rv; + addrinfo hints, *res; + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = 0; + hints.ai_flags = AI_ADDRCONFIG; + + rv = getaddrinfo(config.host.c_str(), util::utos(config.port).c_str(), &hints, + &res); + if (rv != 0) { + std::cerr << "getaddrinfo() failed: " << gai_strerror(rv) << std::endl; + exit(EXIT_FAILURE); + } + if (res == nullptr) { + std::cerr << "No address returned" << std::endl; + exit(EXIT_FAILURE); + } + config.addrs = res; +} +} // namespace + +namespace { +std::string get_reqline(const char *uri, const http_parser_url &u) { + std::string reqline; + + if (util::has_uri_field(u, UF_PATH)) { + reqline = util::get_uri_field(uri, u, UF_PATH); + } else { + reqline = "/"; + } + + if (util::has_uri_field(u, UF_QUERY)) { + reqline += "?"; + reqline += util::get_uri_field(uri, u, UF_QUERY); + } + + return reqline; +} +} // namespace + +namespace { +int client_select_next_proto_cb(SSL *ssl, unsigned char **out, + unsigned char *outlen, const unsigned char *in, + unsigned int inlen, void *arg) { + if (util::select_h2(const_cast(out), outlen, in, + inlen)) { + return SSL_TLSEXT_ERR_OK; + } +#ifdef HAVE_SPDYLAY + if (spdylay_select_next_protocol(out, outlen, in, inlen) > 0) { + return SSL_TLSEXT_ERR_OK; + } +#endif + return SSL_TLSEXT_ERR_NOACK; +} +} // namespace + +namespace { +template +std::vector parse_uris(Iterator first, Iterator last) { + std::vector reqlines; + + // First URI is treated specially. We use scheme, host and port of + // this URI and ignore those in the remaining URIs if present. + http_parser_url u; + memset(&u, 0, sizeof(u)); + + if (first == last) { + std::cerr << "no URI available" << std::endl; + exit(EXIT_FAILURE); + } + + auto uri = (*first).c_str(); + ++first; + + if (http_parser_parse_url(uri, strlen(uri), 0, &u) != 0 || + !util::has_uri_field(u, UF_SCHEMA) || !util::has_uri_field(u, UF_HOST)) { + std::cerr << "invalid URI: " << uri << std::endl; + exit(EXIT_FAILURE); + } + + config.scheme = util::get_uri_field(uri, u, UF_SCHEMA); + config.host = util::get_uri_field(uri, u, UF_HOST); + config.default_port = util::get_default_port(uri, u); + if (util::has_uri_field(u, UF_PORT)) { + config.port = u.port; + } else { + config.port = config.default_port; + } + + reqlines.push_back(get_reqline(uri, u)); + + for (; first != last; ++first) { + http_parser_url u; + memset(&u, 0, sizeof(u)); + + auto uri = (*first).c_str(); + + if (http_parser_parse_url(uri, strlen(uri), 0, &u) != 0) { + std::cerr << "invalid URI: " << uri << std::endl; + exit(EXIT_FAILURE); + } + + reqlines.push_back(get_reqline(uri, u)); + } + + return reqlines; +} +} // namespace + +namespace { +std::vector read_uri_from_file(std::istream &infile) { + std::vector uris; + std::string line_uri; + while (std::getline(infile, line_uri)) { + uris.push_back(line_uri); + } + + return uris; +} +} // namespace + +namespace { +void print_version(std::ostream &out) { + out << "h2load nghttp2/" NGHTTP2_VERSION << std::endl; +} +} // namespace + +namespace { +void print_usage(std::ostream &out) { + out << R"(Usage: h2load [OPTIONS]... [URI]... +benchmarking tool for HTTP/2 and SPDY server)" << std::endl; +} +} // namespace + +namespace { +void print_help(std::ostream &out) { + print_usage(out); + + out << R"( + Specify URI to access. Multiple URIs can be specified. + URIs are used in this order for each client. All URIs + are used, then first URI is used and then 2nd URI, and + so on. The scheme, host and port in the subsequent + URIs, if present, are ignored. Those in the first URI + are used solely. +Options: + -n, --requests= + Number of requests. + Default: )" << config.nreqs << R"( + -c, --clients= + Number of concurrent clients. + Default: )" << config.nclients << R"( + -t, --threads= + Number of native threads. + Default: )" << config.nthreads << R"( + -i, --input-file= + Path of a file with multiple URIs are separated by EOLs. + This option will disable URIs getting from command-line. + If '-' is given as , URIs will be read from stdin. + URIs are used in this order for each client. All URIs + are used, then first URI is used and then 2nd URI, and + so on. The scheme, host and port in the subsequent + URIs, if present, are ignored. Those in the first URI + are used solely. + -m, --max-concurrent-streams=(auto|) + Max concurrent streams to issue per session. If "auto" + is given, the number of given URIs is used. + Default: auto + -w, --window-bits= + Sets the stream level initial window size to (2**)-1. + For SPDY, 2** is used instead. + Default: )" << config.window_bits << R"( + -W, --connection-window-bits= + Sets the connection level initial window size to + (2**)-1. For SPDY, if is strictly less than 16, + this option is ignored. Otherwise 2** is used for + SPDY. + Default: )" << config.connection_window_bits << R"( + -H, --header=
    + Add/Override a header to the requests. + -p, --no-tls-proto= + Specify ALPN identifier of the protocol to be used when + accessing http URI without SSL/TLS.)"; +#ifdef HAVE_SPDYLAY + out << R"( + Available protocols: spdy/2, spdy/3, spdy/3.1 and )"; +#else // !HAVE_SPDYLAY + out << R"( + Available protocol: )"; +#endif // !HAVE_SPDYLAY + out << NGHTTP2_CLEARTEXT_PROTO_VERSION_ID << R"( + Default: )" << NGHTTP2_CLEARTEXT_PROTO_VERSION_ID << R"( + -d, --data= + Post FILE to server. The request method is changed to + POST. + -v, --verbose + Output debug information. + --version Display version information and exit. + -h, --help Display this help and exit.)" << std::endl; +} +} // namespace + +int main(int argc, char **argv) { + std::string datafile; + while (1) { + static int flag = 0; + static option long_options[] = { + {"requests", required_argument, nullptr, 'n'}, + {"clients", required_argument, nullptr, 'c'}, + {"data", required_argument, nullptr, 'd'}, + {"threads", required_argument, nullptr, 't'}, + {"max-concurrent-streams", required_argument, nullptr, 'm'}, + {"window-bits", required_argument, nullptr, 'w'}, + {"connection-window-bits", required_argument, nullptr, 'W'}, + {"input-file", required_argument, nullptr, 'i'}, + {"header", required_argument, nullptr, 'H'}, + {"no-tls-proto", required_argument, nullptr, 'p'}, + {"verbose", no_argument, nullptr, 'v'}, + {"help", no_argument, nullptr, 'h'}, + {"version", no_argument, &flag, 1}, + {nullptr, 0, nullptr, 0}}; + int option_index = 0; + auto c = getopt_long(argc, argv, "hvW:c:d:m:n:p:t:w:H:i:", long_options, + &option_index); + if (c == -1) { + break; + } + switch (c) { + case 'n': + config.nreqs = strtoul(optarg, nullptr, 10); + break; + case 'c': + config.nclients = strtoul(optarg, nullptr, 10); + break; + case 'd': + datafile = optarg; + break; + case 't': +#ifdef NOTHREADS + std::cerr << "-t: WARNING: Threading disabled at build time, " + << "no threads created." << std::endl; +#else + config.nthreads = strtoul(optarg, nullptr, 10); +#endif // NOTHREADS + break; + case 'm': + if (util::strieq("auto", optarg)) { + config.max_concurrent_streams = -1; + } else { + config.max_concurrent_streams = strtoul(optarg, nullptr, 10); + } + break; + case 'w': + case 'W': { + errno = 0; + char *endptr = nullptr; + auto n = strtoul(optarg, &endptr, 10); + if (errno == 0 && *endptr == '\0' && n < 31) { + if (c == 'w') { + config.window_bits = n; + } else { + config.connection_window_bits = n; + } + } else { + std::cerr << "-" << static_cast(c) + << ": specify the integer in the range [0, 30], inclusive" + << std::endl; + exit(EXIT_FAILURE); + } + break; + } + case 'H': { + char *header = optarg; + // Skip first possible ':' in the header name + char *value = strchr(optarg + 1, ':'); + if (!value || (header[0] == ':' && header + 1 == value)) { + std::cerr << "-H: invalid header: " << optarg << std::endl; + exit(EXIT_FAILURE); + } + *value = 0; + value++; + while (isspace(*value)) { + value++; + } + if (*value == 0) { + // This could also be a valid case for suppressing a header + // similar to curl + std::cerr << "-H: invalid header - value missing: " << optarg + << std::endl; + exit(EXIT_FAILURE); + } + // Note that there is no processing currently to handle multiple + // message-header fields with the same field name + config.custom_headers.emplace_back(header, value); + util::inp_strlower(config.custom_headers.back().name); + break; + } + case 'i': { + config.ifile = std::string(optarg); + break; + } + case 'p': + if (util::strieq(NGHTTP2_CLEARTEXT_PROTO_VERSION_ID, optarg)) { + config.no_tls_proto = Config::PROTO_HTTP2; +#ifdef HAVE_SPDYLAY + } else if (util::strieq("spdy/2", optarg)) { + config.no_tls_proto = Config::PROTO_SPDY2; + } else if (util::strieq("spdy/3", optarg)) { + config.no_tls_proto = Config::PROTO_SPDY3; + } else if (util::strieq("spdy/3.1", optarg)) { + config.no_tls_proto = Config::PROTO_SPDY3_1; +#endif // HAVE_SPDYLAY + } else { + std::cerr << "-p: unsupported protocol " << optarg << std::endl; + exit(EXIT_FAILURE); + } + break; + case 'v': + config.verbose = true; + break; + case 'h': + print_help(std::cout); + exit(EXIT_SUCCESS); + case '?': + util::show_candidates(argv[optind - 1], long_options); + exit(EXIT_FAILURE); + case 0: + switch (flag) { + case 1: + // version option + print_version(std::cout); + exit(EXIT_SUCCESS); + } + break; + default: + break; + } + } + + if (argc == optind) { + if (config.ifile.empty()) { + std::cerr << "no URI or input file given" << std::endl; + exit(EXIT_FAILURE); + } + } + + if (config.nreqs == 0) { + std::cerr << "-n: the number of requests must be strictly greater than 0." + << std::endl; + exit(EXIT_FAILURE); + } + + if (config.max_concurrent_streams == 0) { + std::cerr << "-m: the max concurrent streams must be strictly greater " + << "than 0." << std::endl; + exit(EXIT_FAILURE); + } + + if (config.nthreads == 0) { + std::cerr << "-t: the number of threads must be strictly greater than 0." + << std::endl; + exit(EXIT_FAILURE); + } + + if (config.nreqs < config.nclients) { + std::cerr << "-n, -c: the number of requests must be greater than or " + << "equal to the concurrent clients." << std::endl; + exit(EXIT_FAILURE); + } + + if (config.nclients < config.nthreads) { + std::cerr << "-c, -t: the number of client must be greater than or equal " + "to the number of threads." << std::endl; + exit(EXIT_FAILURE); + } + + if (config.nthreads > std::thread::hardware_concurrency()) { + std::cerr << "-t: warning: the number of threads is greater than hardware " + << "cores." << std::endl; + } + + if (!datafile.empty()) { + config.data_fd = open(datafile.c_str(), O_RDONLY | O_BINARY); + if (config.data_fd == -1) { + std::cerr << "-d: Could not open file " << datafile << std::endl; + exit(EXIT_FAILURE); + } + struct stat data_stat; + if (fstat(config.data_fd, &data_stat) == -1) { + std::cerr << "-d: Could not stat file " << datafile << std::endl; + exit(EXIT_FAILURE); + } + config.data_length = data_stat.st_size; + } + + struct sigaction act; + memset(&act, 0, sizeof(struct sigaction)); + act.sa_handler = SIG_IGN; + sigaction(SIGPIPE, &act, nullptr); + OPENSSL_config(nullptr); + OpenSSL_add_all_algorithms(); + SSL_load_error_strings(); + SSL_library_init(); + +#ifndef NOTHREADS + ssl::LibsslGlobalLock lock; +#endif // NOTHREADS + + auto ssl_ctx = SSL_CTX_new(SSLv23_client_method()); + if (!ssl_ctx) { + std::cerr << "Failed to create SSL_CTX: " + << ERR_error_string(ERR_get_error(), nullptr) << std::endl; + exit(EXIT_FAILURE); + } + + SSL_CTX_set_options(ssl_ctx, + SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | + SSL_OP_NO_COMPRESSION | + SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION); + SSL_CTX_set_mode(ssl_ctx, SSL_MODE_AUTO_RETRY); + SSL_CTX_set_mode(ssl_ctx, SSL_MODE_RELEASE_BUFFERS); + + if (SSL_CTX_set_cipher_list(ssl_ctx, ssl::DEFAULT_CIPHER_LIST) == 0) { + std::cerr << "SSL_CTX_set_cipher_list failed: " + << ERR_error_string(ERR_get_error(), nullptr) << std::endl; + exit(EXIT_FAILURE); + } + + SSL_CTX_set_next_proto_select_cb(ssl_ctx, client_select_next_proto_cb, + nullptr); + +#if OPENSSL_VERSION_NUMBER >= 0x10002000L + auto proto_list = util::get_default_alpn(); +#ifdef HAVE_SPDYLAY + static const char spdy_proto_list[] = "\x8spdy/3.1\x6spdy/3\x6spdy/2"; + std::copy_n(spdy_proto_list, sizeof(spdy_proto_list) - 1, + std::back_inserter(proto_list)); +#endif // HAVE_SPDYLAY + SSL_CTX_set_alpn_protos(ssl_ctx, proto_list.data(), proto_list.size()); +#endif // OPENSSL_VERSION_NUMBER >= 0x10002000L + + std::vector reqlines; + + if (config.ifile.empty()) { + std::vector uris; + std::copy(&argv[optind], &argv[argc], std::back_inserter(uris)); + reqlines = parse_uris(std::begin(uris), std::end(uris)); + } else { + std::vector uris; + if (config.ifile == "-") { + uris = read_uri_from_file(std::cin); + } else { + std::ifstream infile(config.ifile); + if (!infile) { + std::cerr << "cannot read input file: " << config.ifile << std::endl; + exit(EXIT_FAILURE); + } + + uris = read_uri_from_file(infile); + } + + reqlines = parse_uris(std::begin(uris), std::end(uris)); + } + + if (config.max_concurrent_streams == -1) { + config.max_concurrent_streams = reqlines.size(); + } + + Headers shared_nva; + shared_nva.emplace_back(":scheme", config.scheme); + if (config.port != config.default_port) { + shared_nva.emplace_back(":authority", + config.host + ":" + util::utos(config.port)); + } else { + shared_nva.emplace_back(":authority", config.host); + } + shared_nva.emplace_back(":method", config.data_fd == -1 ? "GET" : "POST"); + + // list overridalbe headers + auto override_hdrs = + make_array(":authority", ":host", ":method", ":scheme"); + + for (auto &kv : config.custom_headers) { + if (std::find(std::begin(override_hdrs), std::end(override_hdrs), + kv.name) != std::end(override_hdrs)) { + // override header + for (auto &nv : shared_nva) { + if ((nv.name == ":authority" && kv.name == ":host") || + (nv.name == kv.name)) { + nv.value = kv.value; + } + } + } else { + // add additional headers + shared_nva.push_back(kv); + } + } + + for (auto &req : reqlines) { + // For nghttp2 + std::vector nva; + + nva.push_back(http2::make_nv_ls(":path", req)); + + for (auto &nv : shared_nva) { + nva.push_back(http2::make_nv(nv.name, nv.value, false)); + } + + config.nva.push_back(std::move(nva)); + + // For spdylay + std::vector cva; + + cva.push_back(":path"); + cva.push_back(req.c_str()); + + for (auto &nv : shared_nva) { + if (nv.name == ":authority") { + cva.push_back(":host"); + } else { + cva.push_back(nv.name.c_str()); + } + cva.push_back(nv.value.c_str()); + } + cva.push_back(":version"); + cva.push_back("HTTP/1.1"); + cva.push_back(nullptr); + + config.nv.push_back(std::move(cva)); + } + + resolve_host(); + + if (config.nclients == 1) { + config.nthreads = 1; + } + + size_t nreqs_per_thread = config.nreqs / config.nthreads; + ssize_t nreqs_rem = config.nreqs % config.nthreads; + + size_t nclients_per_thread = config.nclients / config.nthreads; + ssize_t nclients_rem = config.nclients % config.nthreads; + + std::cout << "starting benchmark..." << std::endl; + + auto start = std::chrono::steady_clock::now(); + + std::vector> workers; + workers.reserve(config.nthreads); + +#ifndef NOTHREADS + std::vector> futures; + for (size_t i = 0; i < config.nthreads - 1; ++i) { + auto nreqs = nreqs_per_thread + (nreqs_rem-- > 0); + auto nclients = nclients_per_thread + (nclients_rem-- > 0); + std::cout << "spawning thread #" << i << ": " << nclients + << " concurrent clients, " << nreqs << " total requests" + << std::endl; + workers.push_back( + make_unique(i, ssl_ctx, nreqs, nclients, &config)); + auto &worker = workers.back(); + futures.push_back( + std::async(std::launch::async, [&worker]() { worker->run(); })); + } +#endif // NOTHREADS + + auto nreqs_last = nreqs_per_thread + (nreqs_rem-- > 0); + auto nclients_last = nclients_per_thread + (nclients_rem-- > 0); + std::cout << "spawning thread #" << (config.nthreads - 1) << ": " + << nclients_last << " concurrent clients, " << nreqs_last + << " total requests" << std::endl; + workers.push_back(make_unique(config.nthreads - 1, ssl_ctx, + nreqs_last, nclients_last, &config)); + workers.back()->run(); + +#ifndef NOTHREADS + for (auto &fut : futures) { + fut.get(); + } +#endif // NOTHREADS + + auto end = std::chrono::steady_clock::now(); + auto duration = + std::chrono::duration_cast(end - start); + + Stats stats(0); + for (const auto &w : workers) { + const auto &s = w->stats; + + stats.req_todo += s.req_todo; + stats.req_started += s.req_started; + stats.req_done += s.req_done; + stats.req_success += s.req_success; + stats.req_status_success += s.req_status_success; + stats.req_failed += s.req_failed; + stats.req_error += s.req_error; + stats.bytes_total += s.bytes_total; + stats.bytes_head += s.bytes_head; + stats.bytes_body += s.bytes_body; + + for (size_t i = 0; i < stats.status.size(); ++i) { + stats.status[i] += s.status[i]; + } + } + + auto ts = process_time_stats(workers); + + // Requests which have not been issued due to connection errors, are + // counted towards req_failed and req_error. + auto req_not_issued = + stats.req_todo - stats.req_status_success - stats.req_failed; + stats.req_failed += req_not_issued; + stats.req_error += req_not_issued; + + // UI is heavily inspired by weighttp[1] and wrk[2] + // + // [1] https://github.com/lighttpd/weighttp + // [2] https://github.com/wg/wrk + size_t rps = 0; + int64_t bps = 0; + if (duration.count() > 0) { + auto secd = static_cast(duration.count()) / (1000 * 1000); + rps = stats.req_success / secd; + bps = stats.bytes_total / secd; + } + + std::cout << R"( +finished in )" << util::format_duration(duration) << ", " << rps << " req/s, " + << util::utos_with_funit(bps) << R"(B/s +requests: )" << stats.req_todo << " total, " << stats.req_started + << " started, " << stats.req_done << " done, " + << stats.req_status_success << " succeeded, " << stats.req_failed + << " failed, " << stats.req_error << R"( errored +status codes: )" << stats.status[2] << " 2xx, " << stats.status[3] << " 3xx, " + << stats.status[4] << " 4xx, " << stats.status[5] << R"( 5xx +traffic: )" << stats.bytes_total << " bytes total, " << stats.bytes_head + << " bytes headers, " << stats.bytes_body << R"( bytes data + min max mean sd +/- sd +time for request: )" << std::setw(10) << util::format_duration(ts.request.min) + << " " << std::setw(10) << util::format_duration(ts.request.max) + << " " << std::setw(10) << util::format_duration(ts.request.mean) + << " " << std::setw(10) << util::format_duration(ts.request.sd) + << std::setw(9) << util::dtos(ts.request.within_sd) << "%" + << "\ntime for connect: " << std::setw(10) + << util::format_duration(ts.connect.min) << " " << std::setw(10) + << util::format_duration(ts.connect.max) << " " << std::setw(10) + << util::format_duration(ts.connect.mean) << " " << std::setw(10) + << util::format_duration(ts.connect.sd) << std::setw(9) + << util::dtos(ts.connect.within_sd) << "%" + << "\ntime to 1st byte: " << std::setw(10) + << util::format_duration(ts.ttfb.min) << " " << std::setw(10) + << util::format_duration(ts.ttfb.max) << " " << std::setw(10) + << util::format_duration(ts.ttfb.mean) << " " << std::setw(10) + << util::format_duration(ts.ttfb.sd) << std::setw(9) + << util::dtos(ts.ttfb.within_sd) << "%" << std::endl; + + SSL_CTX_free(ssl_ctx); + + return 0; +} + +} // namespace h2load + +int main(int argc, char **argv) { return h2load::main(argc, argv); } diff --git a/src/h2load.h b/src/h2load.h new file mode 100644 index 0000000..78a2038 --- /dev/null +++ b/src/h2load.h @@ -0,0 +1,249 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2014 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef H2LOAD_H +#define H2LOAD_H + +#include "nghttp2_config.h" + +#include +#ifdef HAVE_SYS_SOCKET_H +#include +#endif // HAVE_SYS_SOCKET_H +#ifdef HAVE_NETDB_H +#include +#endif // HAVE_NETDB_H + +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include + +#include "http2.h" +#include "buffer.h" + +using namespace nghttp2; + +namespace h2load { + +class Session; + +struct Config { + std::vector> nva; + std::vector> nv; + nghttp2::Headers custom_headers; + std::string scheme; + std::string host; + std::string ifile; + // length of upload data + int64_t data_length; + addrinfo *addrs; + size_t nreqs; + size_t nclients; + size_t nthreads; + // The maximum number of concurrent streams per session. + ssize_t max_concurrent_streams; + size_t window_bits; + size_t connection_window_bits; + enum { PROTO_HTTP2, PROTO_SPDY2, PROTO_SPDY3, PROTO_SPDY3_1 } no_tls_proto; + // file descriptor for upload data + int data_fd; + uint16_t port; + uint16_t default_port; + bool verbose; + + Config(); + ~Config(); +}; + +struct RequestStat { + RequestStat(); + // time point when request was sent + std::chrono::steady_clock::time_point request_time; + // time point when stream was closed + std::chrono::steady_clock::time_point stream_close_time; + // upload data length sent so far + int64_t data_offset; + // true if stream was successfully closed. This means stream was + // not reset, but it does not mean HTTP level error (e.g., 404). + bool completed; +}; + +template struct TimeStat { + // min, max, mean and sd (standard deviation) + Duration min, max, mean, sd; + // percentage of samples inside mean -/+ sd + double within_sd; +}; + +struct TimeStats { + // time for request + TimeStat request; + // time for connect + TimeStat connect; + // time to first byte (TTFB) + TimeStat ttfb; +}; + +enum TimeStatType { STAT_REQUEST, STAT_CONNECT, STAT_FIRST_BYTE }; + +struct Stats { + Stats(size_t req_todo); + // The total number of requests + size_t req_todo; + // The number of requests issued so far + size_t req_started; + // The number of requests finished + size_t req_done; + // The number of requests completed successfull, but not necessarily + // means successful HTTP status code. + size_t req_success; + // The number of requests marked as success. HTTP status code is + // also considered as success. This is subset of req_done. + size_t req_status_success; + // The number of requests failed. This is subset of req_done. + size_t req_failed; + // The number of requests failed due to network errors. This is + // subset of req_failed. + size_t req_error; + // The number of bytes received on the "wire". If SSL/TLS is used, + // this is the number of decrypted bytes the application received. + int64_t bytes_total; + // The number of bytes received in HEADERS frame payload. + int64_t bytes_head; + // The number of bytes received in DATA frame. + int64_t bytes_body; + // The number of each HTTP status category, status[i] is status code + // in the range [i*100, (i+1)*100). + std::array status; + // The statistics per request + std::vector req_stats; + // time connect starts + std::vector start_times; + // time to connect + std::vector connect_times; + // time to first byte (TTFB) + std::vector ttfbs; +}; + +enum ClientState { CLIENT_IDLE, CLIENT_CONNECTED }; + +struct Client; + +struct Worker { + std::vector> clients; + Stats stats; + struct ev_loop *loop; + SSL_CTX *ssl_ctx; + Config *config; + size_t progress_interval; + uint32_t id; + bool tls_info_report_done; + + Worker(uint32_t id, SSL_CTX *ssl_ctx, size_t nreq_todo, size_t nclients, + Config *config); + ~Worker(); + Worker(Worker &&o) = default; + void run(); +}; + +struct Stream { + int status_success; + Stream(); +}; + +struct Client { + std::unordered_map streams; + std::unique_ptr session; + ev_io wev; + ev_io rev; + std::function readfn, writefn; + Worker *worker; + SSL *ssl; + addrinfo *next_addr; + size_t reqidx; + ClientState state; + bool first_byte_received; + // The number of requests this client has to issue. + size_t req_todo; + // The number of requests this client has issued so far. + size_t req_started; + // The number of requests this client has done so far. + size_t req_done; + int fd; + Buffer<65536> wb; + + enum { ERR_CONNECT_FAIL = -100 }; + + Client(Worker *worker, size_t req_todo); + ~Client(); + int connect(); + void disconnect(); + void fail(); + void submit_request(); + void process_abandoned_streams(); + void report_progress(); + void report_tls_info(); + void terminate_session(); + + int do_read(); + int do_write(); + + // low-level I/O callback functions called by do_read/do_write + int connected(); + int read_clear(); + int write_clear(); + int tls_handshake(); + int read_tls(); + int write_tls(); + + int on_read(const uint8_t *data, size_t len); + int on_write(); + + int connection_made(); + + void on_request(int32_t stream_id); + void on_header(int32_t stream_id, const uint8_t *name, size_t namelen, + const uint8_t *value, size_t valuelen); + void on_stream_close(int32_t stream_id, bool success, RequestStat *req_stat); + + void record_request_time(RequestStat *req_stat); + void record_start_time(Stats *stat); + void record_connect_time(Stats *stat); + void record_ttfb(Stats *stat); + + void signal_write(); +}; + +} // namespace h2load + +#endif // H2LOAD_H diff --git a/src/h2load_http2_session.cc b/src/h2load_http2_session.cc new file mode 100644 index 0000000..0cbb702 --- /dev/null +++ b/src/h2load_http2_session.cc @@ -0,0 +1,260 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2014 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "h2load_http2_session.h" + +#include +#include + +#include "h2load.h" +#include "util.h" +#include "template.h" + +using namespace nghttp2; + +namespace h2load { + +Http2Session::Http2Session(Client *client) + : client_(client), session_(nullptr) {} + +Http2Session::~Http2Session() { nghttp2_session_del(session_); } + +namespace { +int on_header_callback(nghttp2_session *session, const nghttp2_frame *frame, + const uint8_t *name, size_t namelen, + const uint8_t *value, size_t valuelen, uint8_t flags, + void *user_data) { + auto client = static_cast(user_data); + if (frame->hd.type != NGHTTP2_HEADERS || + frame->headers.cat != NGHTTP2_HCAT_RESPONSE) { + return 0; + } + client->on_header(frame->hd.stream_id, name, namelen, value, valuelen); + return 0; +} +} // namespace + +namespace { +int on_frame_recv_callback(nghttp2_session *session, const nghttp2_frame *frame, + void *user_data) { + auto client = static_cast(user_data); + if (frame->hd.type != NGHTTP2_HEADERS || + frame->headers.cat != NGHTTP2_HCAT_RESPONSE) { + return 0; + } + client->worker->stats.bytes_head += frame->hd.length; + return 0; +} +} // namespace + +namespace { +int on_data_chunk_recv_callback(nghttp2_session *session, uint8_t flags, + int32_t stream_id, const uint8_t *data, + size_t len, void *user_data) { + auto client = static_cast(user_data); + client->worker->stats.bytes_body += len; + return 0; +} +} // namespace + +namespace { +int on_stream_close_callback(nghttp2_session *session, int32_t stream_id, + uint32_t error_code, void *user_data) { + auto client = static_cast(user_data); + auto req_stat = static_cast( + nghttp2_session_get_stream_user_data(session, stream_id)); + if (!req_stat) { + return 0; + } + client->on_stream_close(stream_id, error_code == NGHTTP2_NO_ERROR, req_stat); + return 0; +} +} // namespace + +namespace { +int before_frame_send_callback(nghttp2_session *session, + const nghttp2_frame *frame, void *user_data) { + if (frame->hd.type != NGHTTP2_HEADERS || + frame->headers.cat != NGHTTP2_HCAT_REQUEST) { + return 0; + } + + auto client = static_cast(user_data); + client->on_request(frame->hd.stream_id); + auto req_stat = static_cast( + nghttp2_session_get_stream_user_data(session, frame->hd.stream_id)); + assert(req_stat); + client->record_request_time(req_stat); + + return 0; +} +} // namespace + +namespace { +ssize_t file_read_callback(nghttp2_session *session, int32_t stream_id, + uint8_t *buf, size_t length, uint32_t *data_flags, + nghttp2_data_source *source, void *user_data) { + auto client = static_cast(user_data); + auto config = client->worker->config; + auto req_stat = static_cast( + nghttp2_session_get_stream_user_data(session, stream_id)); + assert(req_stat); + ssize_t nread; + while ((nread = pread(config->data_fd, buf, length, req_stat->data_offset)) == + -1 && + errno == EINTR) + ; + + if (nread == -1) { + return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; + } + + req_stat->data_offset += nread; + + if (nread == 0 || req_stat->data_offset == config->data_length) { + *data_flags |= NGHTTP2_DATA_FLAG_EOF; + } + + return nread; +} + +} // namespace + +namespace { +ssize_t send_callback(nghttp2_session *session, const uint8_t *data, + size_t length, int flags, void *user_data) { + auto client = static_cast(user_data); + auto &wb = client->wb; + + if (wb.wleft() == 0) { + return NGHTTP2_ERR_WOULDBLOCK; + } + + return wb.write(data, length); +} +} // namespace + +void Http2Session::on_connect() { + int rv; + + nghttp2_session_callbacks *callbacks; + + nghttp2_session_callbacks_new(&callbacks); + + auto callbacks_deleter = defer(nghttp2_session_callbacks_del, callbacks); + + nghttp2_session_callbacks_set_on_frame_recv_callback(callbacks, + on_frame_recv_callback); + + nghttp2_session_callbacks_set_on_data_chunk_recv_callback( + callbacks, on_data_chunk_recv_callback); + + nghttp2_session_callbacks_set_on_stream_close_callback( + callbacks, on_stream_close_callback); + + nghttp2_session_callbacks_set_on_header_callback(callbacks, + on_header_callback); + + nghttp2_session_callbacks_set_before_frame_send_callback( + callbacks, before_frame_send_callback); + + nghttp2_session_callbacks_set_send_callback(callbacks, send_callback); + + nghttp2_session_client_new(&session_, callbacks, client_); + + std::array iv; + iv[0].settings_id = NGHTTP2_SETTINGS_ENABLE_PUSH; + iv[0].value = 0; + iv[1].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE; + iv[1].value = (1 << client_->worker->config->window_bits) - 1; + + rv = nghttp2_submit_settings(session_, NGHTTP2_FLAG_NONE, iv.data(), + iv.size()); + + assert(rv == 0); + + auto extra_connection_window = + (1 << client_->worker->config->connection_window_bits) - 1 - + NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE; + if (extra_connection_window != 0) { + nghttp2_submit_window_update(session_, NGHTTP2_FLAG_NONE, 0, + extra_connection_window); + } + + client_->signal_write(); +} + +void Http2Session::submit_request(RequestStat *req_stat) { + auto config = client_->worker->config; + auto &nva = config->nva[client_->reqidx++]; + + if (client_->reqidx == config->nva.size()) { + client_->reqidx = 0; + } + + nghttp2_data_provider prd{{0}, file_read_callback}; + + auto stream_id = + nghttp2_submit_request(session_, nullptr, nva.data(), nva.size(), + config->data_fd == -1 ? nullptr : &prd, req_stat); + assert(stream_id > 0); +} + +int Http2Session::on_read(const uint8_t *data, size_t len) { + auto rv = nghttp2_session_mem_recv(session_, data, len); + if (rv < 0) { + return -1; + } + + assert(static_cast(rv) == len); + + if (nghttp2_session_want_read(session_) == 0 && + nghttp2_session_want_write(session_) == 0 && client_->wb.rleft() == 0) { + return -1; + } + + client_->signal_write(); + + return 0; +} + +int Http2Session::on_write() { + auto rv = nghttp2_session_send(session_); + if (rv != 0) { + return -1; + } + + if (nghttp2_session_want_read(session_) == 0 && + nghttp2_session_want_write(session_) == 0 && client_->wb.rleft() == 0) { + return -1; + } + + return 0; +} + +void Http2Session::terminate() { + nghttp2_session_terminate_session(session_, NGHTTP2_NO_ERROR); +} + +} // namespace h2load diff --git a/src/h2load_http2_session.h b/src/h2load_http2_session.h new file mode 100644 index 0000000..a06e411 --- /dev/null +++ b/src/h2load_http2_session.h @@ -0,0 +1,53 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2014 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef H2LOAD_HTTP2_SESSION_H +#define H2LOAD_HTTP2_SESSION_H + +#include "h2load_session.h" + +#include + +namespace h2load { + +struct Client; + +class Http2Session : public Session { +public: + Http2Session(Client *client); + virtual ~Http2Session(); + virtual void on_connect(); + virtual void submit_request(RequestStat *req_stat); + virtual int on_read(const uint8_t *data, size_t len); + virtual int on_write(); + virtual void terminate(); + +private: + Client *client_; + nghttp2_session *session_; +}; + +} // namespace h2load + +#endif // H2LOAD_HTTP2_SESSION_H diff --git a/src/h2load_session.h b/src/h2load_session.h new file mode 100644 index 0000000..1817574 --- /dev/null +++ b/src/h2load_session.h @@ -0,0 +1,57 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2014 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef H2LOAD_SESSION_H +#define H2LOAD_SESSION_H + +#include "nghttp2_config.h" + +#include + +#include + +#include "h2load.h" + +namespace h2load { + +class Session { +public: + virtual ~Session() {} + // Called when the connection was made. + virtual void on_connect() = 0; + // Called when one request must be issued. + virtual void submit_request(RequestStat *req_stat) = 0; + // Called when incoming bytes are available. The subclass has to + // return the number of bytes read. + virtual int on_read(const uint8_t *data, size_t len) = 0; + // Called when write is available. Returns 0 on success, otherwise + // return -1. + virtual int on_write() = 0; + // Called when the underlying session must be terminated. + virtual void terminate() = 0; +}; + +} // namespace h2load + +#endif // H2LOAD_SESSION_H diff --git a/src/h2load_spdy_session.cc b/src/h2load_spdy_session.cc new file mode 100644 index 0000000..60258b7 --- /dev/null +++ b/src/h2load_spdy_session.cc @@ -0,0 +1,270 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2014 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "h2load_spdy_session.h" + +#include +#include + +#include "h2load.h" +#include "util.h" + +using namespace nghttp2; + +namespace h2load { + +SpdySession::SpdySession(Client *client, uint16_t spdy_version) + : client_(client), session_(nullptr), spdy_version_(spdy_version) {} + +SpdySession::~SpdySession() { spdylay_session_del(session_); } + +namespace { +void before_ctrl_send_callback(spdylay_session *session, + spdylay_frame_type type, spdylay_frame *frame, + void *user_data) { + auto client = static_cast(user_data); + if (type != SPDYLAY_SYN_STREAM) { + return; + } + client->on_request(frame->syn_stream.stream_id); + auto req_stat = + static_cast(spdylay_session_get_stream_user_data( + session, frame->syn_stream.stream_id)); + client->record_request_time(req_stat); +} +} // namespace + +namespace { +void on_ctrl_recv_callback(spdylay_session *session, spdylay_frame_type type, + spdylay_frame *frame, void *user_data) { + auto client = static_cast(user_data); + if (type != SPDYLAY_SYN_REPLY) { + return; + } + for (auto p = frame->syn_reply.nv; *p; p += 2) { + auto name = *p; + auto value = *(p + 1); + client->on_header(frame->syn_reply.stream_id, + reinterpret_cast(name), strlen(name), + reinterpret_cast(value), strlen(value)); + } + client->worker->stats.bytes_head += frame->syn_reply.hd.length; +} +} // namespace + +namespace { +void on_data_chunk_recv_callback(spdylay_session *session, uint8_t flags, + int32_t stream_id, const uint8_t *data, + size_t len, void *user_data) { + auto client = static_cast(user_data); + client->worker->stats.bytes_body += len; + + auto spdy_session = static_cast(client->session.get()); + + spdy_session->handle_window_update(stream_id, len); +} +} // namespace + +namespace { +void on_stream_close_callback(spdylay_session *session, int32_t stream_id, + spdylay_status_code status_code, + void *user_data) { + auto client = static_cast(user_data); + auto req_stat = static_cast( + spdylay_session_get_stream_user_data(session, stream_id)); + client->on_stream_close(stream_id, status_code == SPDYLAY_OK, req_stat); +} +} // namespace + +namespace { +ssize_t send_callback(spdylay_session *session, const uint8_t *data, + size_t length, int flags, void *user_data) { + auto client = static_cast(user_data); + auto &wb = client->wb; + + if (wb.wleft() == 0) { + return SPDYLAY_ERR_DEFERRED; + } + + return wb.write(data, length); +} +} // namespace + +namespace { +ssize_t file_read_callback(spdylay_session *session, int32_t stream_id, + uint8_t *buf, size_t length, int *eof, + spdylay_data_source *source, void *user_data) { + auto client = static_cast(user_data); + auto config = client->worker->config; + auto req_stat = static_cast( + spdylay_session_get_stream_user_data(session, stream_id)); + + ssize_t nread; + while ((nread = pread(config->data_fd, buf, length, req_stat->data_offset)) == + -1 && + errno == EINTR) + ; + + if (nread == -1) { + return SPDYLAY_ERR_TEMPORAL_CALLBACK_FAILURE; + } + + req_stat->data_offset += nread; + + if (nread == 0 || req_stat->data_offset == config->data_length) { + *eof = 1; + } + + return nread; +} +} // namespace + +void SpdySession::on_connect() { + spdylay_session_callbacks callbacks = {0}; + callbacks.send_callback = send_callback; + callbacks.before_ctrl_send_callback = before_ctrl_send_callback; + callbacks.on_data_chunk_recv_callback = on_data_chunk_recv_callback; + callbacks.on_stream_close_callback = on_stream_close_callback; + callbacks.on_ctrl_recv_callback = on_ctrl_recv_callback; + + spdylay_session_client_new(&session_, spdy_version_, &callbacks, client_); + + int val = 1; + spdylay_session_set_option(session_, SPDYLAY_OPT_NO_AUTO_WINDOW_UPDATE, &val, + sizeof(val)); + + spdylay_settings_entry iv; + iv.settings_id = SPDYLAY_SETTINGS_INITIAL_WINDOW_SIZE; + iv.flags = SPDYLAY_ID_FLAG_SETTINGS_NONE; + iv.value = (1 << client_->worker->config->window_bits); + spdylay_submit_settings(session_, SPDYLAY_FLAG_SETTINGS_NONE, &iv, 1); + + auto config = client_->worker->config; + + if (spdy_version_ >= SPDYLAY_PROTO_SPDY3_1 && + config->connection_window_bits > 16) { + auto delta = + (1 << config->connection_window_bits) - SPDYLAY_INITIAL_WINDOW_SIZE; + spdylay_submit_window_update(session_, 0, delta); + } + + client_->signal_write(); +} + +void SpdySession::submit_request(RequestStat *req_stat) { + auto config = client_->worker->config; + auto &nv = config->nv[client_->reqidx++]; + + if (client_->reqidx == config->nv.size()) { + client_->reqidx = 0; + } + + spdylay_data_provider prd{{0}, file_read_callback}; + + spdylay_submit_request(session_, 0, nv.data(), + config->data_fd == -1 ? nullptr : &prd, req_stat); +} + +int SpdySession::on_read(const uint8_t *data, size_t len) { + auto rv = spdylay_session_mem_recv(session_, data, len); + if (rv < 0) { + return -1; + } + + assert(static_cast(rv) == len); + + if (spdylay_session_want_read(session_) == 0 && + spdylay_session_want_write(session_) == 0 && client_->wb.rleft() == 0) { + return -1; + } + + client_->signal_write(); + + return 0; +} + +int SpdySession::on_write() { + auto rv = spdylay_session_send(session_); + if (rv != 0) { + return -1; + } + + if (spdylay_session_want_read(session_) == 0 && + spdylay_session_want_write(session_) == 0 && client_->wb.rleft() == 0) { + return -1; + } + return 0; +} + +void SpdySession::terminate() { + spdylay_session_fail_session(session_, SPDYLAY_OK); +} + +namespace { +int32_t determine_window_update_transmission(spdylay_session *session, + int32_t stream_id, + size_t window_bits) { + int32_t recv_length; + + if (stream_id == 0) { + recv_length = spdylay_session_get_recv_data_length(session); + } else { + recv_length = + spdylay_session_get_stream_recv_data_length(session, stream_id); + } + + auto window_size = 1 << window_bits; + + if (recv_length != -1 && recv_length >= window_size / 2) { + return recv_length; + } + + return -1; +} +} // namespace + +void SpdySession::handle_window_update(int32_t stream_id, size_t recvlen) { + auto config = client_->worker->config; + size_t connection_window_bits; + + if (config->connection_window_bits > 16) { + connection_window_bits = config->connection_window_bits; + } else { + connection_window_bits = 16; + } + + auto delta = + determine_window_update_transmission(session_, 0, connection_window_bits); + if (delta > 0) { + spdylay_submit_window_update(session_, 0, delta); + } + + delta = determine_window_update_transmission(session_, stream_id, + config->window_bits); + if (delta > 0) { + spdylay_submit_window_update(session_, stream_id, delta); + } +} + +} // namespace h2load diff --git a/src/h2load_spdy_session.h b/src/h2load_spdy_session.h new file mode 100644 index 0000000..f76f25a --- /dev/null +++ b/src/h2load_spdy_session.h @@ -0,0 +1,57 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2014 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef H2LOAD_SPDY_SESSION_H +#define H2LOAD_SPDY_SESSION_H + +#include "h2load_session.h" + +#include + +#include "util.h" + +namespace h2load { + +struct Client; + +class SpdySession : public Session { +public: + SpdySession(Client *client, uint16_t spdy_version); + virtual ~SpdySession(); + virtual void on_connect(); + virtual void submit_request(RequestStat *req_stat); + virtual int on_read(const uint8_t *data, size_t len); + virtual int on_write(); + virtual void terminate(); + void handle_window_update(int32_t stream_id, size_t recvlen); + +private: + Client *client_; + spdylay_session *session_; + uint16_t spdy_version_; +}; + +} // namespace h2load + +#endif // H2LOAD_SPDY_SESSION_H diff --git a/src/http2.cc b/src/http2.cc new file mode 100644 index 0000000..65b2cee --- /dev/null +++ b/src/http2.cc @@ -0,0 +1,1118 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "http2.h" + +#include "util.h" + +namespace nghttp2 { + +namespace http2 { + +std::string get_status_string(unsigned int status_code) { + switch (status_code) { + case 100: + return "100 Continue"; + case 101: + return "101 Switching Protocols"; + case 200: + return "200 OK"; + case 201: + return "201 Created"; + case 202: + return "202 Accepted"; + case 203: + return "203 Non-Authoritative Information"; + case 204: + return "204 No Content"; + case 205: + return "205 Reset Content"; + case 206: + return "206 Partial Content"; + case 300: + return "300 Multiple Choices"; + case 301: + return "301 Moved Permanently"; + case 302: + return "302 Found"; + case 303: + return "303 See Other"; + case 304: + return "304 Not Modified"; + case 305: + return "305 Use Proxy"; + // case 306: return "306 (Unused)"; + case 307: + return "307 Temporary Redirect"; + case 308: + return "308 Permanent Redirect"; + case 400: + return "400 Bad Request"; + case 401: + return "401 Unauthorized"; + case 402: + return "402 Payment Required"; + case 403: + return "403 Forbidden"; + case 404: + return "404 Not Found"; + case 405: + return "405 Method Not Allowed"; + case 406: + return "406 Not Acceptable"; + case 407: + return "407 Proxy Authentication Required"; + case 408: + return "408 Request Timeout"; + case 409: + return "409 Conflict"; + case 410: + return "410 Gone"; + case 411: + return "411 Length Required"; + case 412: + return "412 Precondition Failed"; + case 413: + return "413 Payload Too Large"; + case 414: + return "414 URI Too Long"; + case 415: + return "415 Unsupported Media Type"; + case 416: + return "416 Requested Range Not Satisfiable"; + case 417: + return "417 Expectation Failed"; + case 421: + return "421 Misdirected Request"; + case 426: + return "426 Upgrade Required"; + case 428: + return "428 Precondition Required"; + case 429: + return "429 Too Many Requests"; + case 431: + return "431 Request Header Fields Too Large"; + case 500: + return "500 Internal Server Error"; + case 501: + return "501 Not Implemented"; + case 502: + return "502 Bad Gateway"; + case 503: + return "503 Service Unavailable"; + case 504: + return "504 Gateway Timeout"; + case 505: + return "505 HTTP Version Not Supported"; + case 511: + return "511 Network Authentication Required"; + default: + return util::utos(status_code); + } +} + +void capitalize(std::string &s, size_t offset) { + s[offset] = util::upcase(s[offset]); + for (size_t i = offset + 1, eoi = s.size(); i < eoi; ++i) { + if (s[i - 1] == '-') { + s[i] = util::upcase(s[i]); + } else { + s[i] = util::lowcase(s[i]); + } + } +} + +bool lws(const char *value) { + for (; *value; ++value) { + switch (*value) { + case '\t': + case ' ': + continue; + default: + return false; + } + } + return true; +} + +void copy_url_component(std::string &dest, const http_parser_url *u, int field, + const char *url) { + if (u->field_set & (1 << field)) { + dest.assign(url + u->field_data[field].off, u->field_data[field].len); + } +} + +Headers::value_type to_header(const uint8_t *name, size_t namelen, + const uint8_t *value, size_t valuelen, + bool no_index, int16_t token) { + return Header(std::string(reinterpret_cast(name), namelen), + std::string(reinterpret_cast(value), valuelen), + no_index, token); +} + +void add_header(Headers &nva, const uint8_t *name, size_t namelen, + const uint8_t *value, size_t valuelen, bool no_index, + int16_t token) { + if (valuelen > 0) { + size_t i, j; + for (i = 0; i < valuelen && (value[i] == ' ' || value[i] == '\t'); ++i) + ; + for (j = valuelen - 1; j > i && (value[j] == ' ' || value[j] == '\t'); --j) + ; + value += i; + valuelen -= i + (valuelen - j - 1); + } + nva.push_back(to_header(name, namelen, value, valuelen, no_index, token)); +} + +const Headers::value_type *get_header(const Headers &nva, const char *name) { + const Headers::value_type *res = nullptr; + for (auto &nv : nva) { + if (nv.name == name) { + res = &nv; + } + } + return res; +} + +std::string value_to_str(const Headers::value_type *nv) { + if (nv) { + return nv->value; + } + return ""; +} + +bool non_empty_value(const Headers::value_type *nv) { + return nv && !nv->value.empty(); +} + +nghttp2_nv make_nv(const std::string &name, const std::string &value, + bool no_index) { + uint8_t flags; + + flags = no_index ? NGHTTP2_NV_FLAG_NO_INDEX : NGHTTP2_NV_FLAG_NONE; + + return {(uint8_t *)name.c_str(), (uint8_t *)value.c_str(), name.size(), + value.size(), flags}; +} + +void copy_headers_to_nva(std::vector &nva, const Headers &headers) { + for (auto &kv : headers) { + if (kv.name.empty() || kv.name[0] == ':') { + continue; + } + switch (kv.token) { + case HD_COOKIE: + case HD_CONNECTION: + case HD_HOST: + case HD_HTTP2_SETTINGS: + case HD_KEEP_ALIVE: + case HD_PROXY_CONNECTION: + case HD_SERVER: + case HD_TE: + case HD_TRANSFER_ENCODING: + case HD_UPGRADE: + case HD_VIA: + case HD_X_FORWARDED_FOR: + case HD_X_FORWARDED_PROTO: + continue; + } + nva.push_back(make_nv(kv.name, kv.value, kv.no_index)); + } +} + +void build_http1_headers_from_headers(std::string &hdrs, + const Headers &headers) { + for (auto &kv : headers) { + if (kv.name.empty() || kv.name[0] == ':') { + continue; + } + switch (kv.token) { + case HD_CONNECTION: + case HD_COOKIE: + case HD_HOST: + case HD_HTTP2_SETTINGS: + case HD_KEEP_ALIVE: + case HD_PROXY_CONNECTION: + case HD_SERVER: + case HD_UPGRADE: + case HD_VIA: + case HD_X_FORWARDED_FOR: + case HD_X_FORWARDED_PROTO: + continue; + } + hdrs += kv.name; + capitalize(hdrs, hdrs.size() - kv.name.size()); + hdrs += ": "; + hdrs += kv.value; + hdrs += "\r\n"; + } +} + +int32_t determine_window_update_transmission(nghttp2_session *session, + int32_t stream_id) { + int32_t recv_length, window_size; + if (stream_id == 0) { + recv_length = nghttp2_session_get_effective_recv_data_length(session); + window_size = nghttp2_session_get_effective_local_window_size(session); + } else { + recv_length = nghttp2_session_get_stream_effective_recv_data_length( + session, stream_id); + window_size = nghttp2_session_get_stream_effective_local_window_size( + session, stream_id); + } + if (recv_length != -1 && window_size != -1) { + if (recv_length >= window_size / 2) { + return recv_length; + } + } + return -1; +} + +void dump_nv(FILE *out, const char **nv) { + for (size_t i = 0; nv[i]; i += 2) { + fprintf(out, "%s: %s\n", nv[i], nv[i + 1]); + } + fputc('\n', out); + fflush(out); +} + +void dump_nv(FILE *out, const nghttp2_nv *nva, size_t nvlen) { + auto end = nva + nvlen; + for (; nva != end; ++nva) { + fprintf(out, "%s: %s\n", nva->name, nva->value); + } + fputc('\n', out); + fflush(out); +} + +void dump_nv(FILE *out, const Headers &nva) { + for (auto &nv : nva) { + fprintf(out, "%s: %s\n", nv.name.c_str(), nv.value.c_str()); + } + fputc('\n', out); + fflush(out); +} + +std::string rewrite_location_uri(const std::string &uri, + const http_parser_url &u, + const std::string &match_host, + const std::string &request_authority, + const std::string &upstream_scheme) { + // We just rewrite scheme and authority. + if ((u.field_set & (1 << UF_HOST)) == 0) { + return ""; + } + auto field = &u.field_data[UF_HOST]; + if (!util::startsWith(std::begin(match_host), std::end(match_host), + &uri[field->off], &uri[field->off] + field->len) || + (match_host.size() != field->len && match_host[field->len] != ':')) { + return ""; + } + std::string res; + if (!request_authority.empty()) { + res += upstream_scheme; + res += "://"; + res += request_authority; + } + if (u.field_set & (1 << UF_PATH)) { + field = &u.field_data[UF_PATH]; + res.append(&uri[field->off], field->len); + } + if (u.field_set & (1 << UF_QUERY)) { + field = &u.field_data[UF_QUERY]; + res += "?"; + res.append(&uri[field->off], field->len); + } + if (u.field_set & (1 << UF_FRAGMENT)) { + field = &u.field_data[UF_FRAGMENT]; + res += "#"; + res.append(&uri[field->off], field->len); + } + return res; +} + +int check_nv(const uint8_t *name, size_t namelen, const uint8_t *value, + size_t valuelen) { + if (!nghttp2_check_header_name(name, namelen)) { + return 0; + } + if (!nghttp2_check_header_value(value, valuelen)) { + return 0; + } + return 1; +} + +int parse_http_status_code(const std::string &src) { + if (src.size() != 3) { + return -1; + } + + int status = 0; + for (auto c : src) { + if (!isdigit(c)) { + return -1; + } + status *= 10; + status += c - '0'; + } + + if (status < 100) { + return -1; + } + + return status; +} + +int lookup_token(const std::string &name) { + return lookup_token(reinterpret_cast(name.c_str()), + name.size()); +} + +// This function was generated by genheaderfunc.py. Inspired by h2o +// header lookup. https://github.com/h2o/h2o +int lookup_token(const uint8_t *name, size_t namelen) { + switch (namelen) { + case 2: + switch (name[1]) { + case 'e': + if (util::streq_l("t", name, 1)) { + return HD_TE; + } + break; + } + break; + case 3: + switch (name[2]) { + case 'a': + if (util::streq_l("vi", name, 2)) { + return HD_VIA; + } + break; + } + break; + case 4: + switch (name[3]) { + case 'k': + if (util::streq_l("lin", name, 3)) { + return HD_LINK; + } + break; + case 't': + if (util::streq_l("hos", name, 3)) { + return HD_HOST; + } + break; + } + break; + case 5: + switch (name[4]) { + case 'h': + if (util::streq_l(":pat", name, 4)) { + return HD__PATH; + } + break; + case 't': + if (util::streq_l(":hos", name, 4)) { + return HD__HOST; + } + break; + } + break; + case 6: + switch (name[5]) { + case 'e': + if (util::streq_l("cooki", name, 5)) { + return HD_COOKIE; + } + break; + case 'r': + if (util::streq_l("serve", name, 5)) { + return HD_SERVER; + } + break; + case 't': + if (util::streq_l("expec", name, 5)) { + return HD_EXPECT; + } + break; + } + break; + case 7: + switch (name[6]) { + case 'c': + if (util::streq_l("alt-sv", name, 6)) { + return HD_ALT_SVC; + } + break; + case 'd': + if (util::streq_l(":metho", name, 6)) { + return HD__METHOD; + } + break; + case 'e': + if (util::streq_l(":schem", name, 6)) { + return HD__SCHEME; + } + if (util::streq_l("upgrad", name, 6)) { + return HD_UPGRADE; + } + break; + case 'r': + if (util::streq_l("traile", name, 6)) { + return HD_TRAILER; + } + break; + case 's': + if (util::streq_l(":statu", name, 6)) { + return HD__STATUS; + } + break; + } + break; + case 8: + switch (name[7]) { + case 'n': + if (util::streq_l("locatio", name, 7)) { + return HD_LOCATION; + } + break; + } + break; + case 10: + switch (name[9]) { + case 'e': + if (util::streq_l("keep-aliv", name, 9)) { + return HD_KEEP_ALIVE; + } + break; + case 'n': + if (util::streq_l("connectio", name, 9)) { + return HD_CONNECTION; + } + break; + case 't': + if (util::streq_l("user-agen", name, 9)) { + return HD_USER_AGENT; + } + break; + case 'y': + if (util::streq_l(":authorit", name, 9)) { + return HD__AUTHORITY; + } + break; + } + break; + case 13: + switch (name[12]) { + case 'l': + if (util::streq_l("cache-contro", name, 12)) { + return HD_CACHE_CONTROL; + } + break; + } + break; + case 14: + switch (name[13]) { + case 'h': + if (util::streq_l("content-lengt", name, 13)) { + return HD_CONTENT_LENGTH; + } + break; + case 's': + if (util::streq_l("http2-setting", name, 13)) { + return HD_HTTP2_SETTINGS; + } + break; + } + break; + case 15: + switch (name[14]) { + case 'e': + if (util::streq_l("accept-languag", name, 14)) { + return HD_ACCEPT_LANGUAGE; + } + break; + case 'g': + if (util::streq_l("accept-encodin", name, 14)) { + return HD_ACCEPT_ENCODING; + } + break; + case 'r': + if (util::streq_l("x-forwarded-fo", name, 14)) { + return HD_X_FORWARDED_FOR; + } + break; + } + break; + case 16: + switch (name[15]) { + case 'n': + if (util::streq_l("proxy-connectio", name, 15)) { + return HD_PROXY_CONNECTION; + } + break; + } + break; + case 17: + switch (name[16]) { + case 'e': + if (util::streq_l("if-modified-sinc", name, 16)) { + return HD_IF_MODIFIED_SINCE; + } + break; + case 'g': + if (util::streq_l("transfer-encodin", name, 16)) { + return HD_TRANSFER_ENCODING; + } + break; + case 'o': + if (util::streq_l("x-forwarded-prot", name, 16)) { + return HD_X_FORWARDED_PROTO; + } + break; + } + break; + } + return -1; +} + +void init_hdidx(HeaderIndex &hdidx) { + std::fill(std::begin(hdidx), std::end(hdidx), -1); +} + +void index_header(HeaderIndex &hdidx, int16_t token, size_t idx) { + if (token == -1) { + return; + } + assert(token < HD_MAXIDX); + hdidx[token] = idx; +} + +bool check_http2_request_pseudo_header(const HeaderIndex &hdidx, + int16_t token) { + switch (token) { + case HD__AUTHORITY: + case HD__METHOD: + case HD__PATH: + case HD__SCHEME: + return hdidx[token] == -1; + default: + return false; + } +} + +bool check_http2_response_pseudo_header(const HeaderIndex &hdidx, + int16_t token) { + switch (token) { + case HD__STATUS: + return hdidx[token] == -1; + default: + return false; + } +} + +bool http2_header_allowed(int16_t token) { + switch (token) { + case HD_CONNECTION: + case HD_KEEP_ALIVE: + case HD_PROXY_CONNECTION: + case HD_TRANSFER_ENCODING: + case HD_UPGRADE: + return false; + default: + return true; + } +} + +bool http2_mandatory_request_headers_presence(const HeaderIndex &hdidx) { + if (hdidx[HD__METHOD] == -1 || hdidx[HD__PATH] == -1 || + hdidx[HD__SCHEME] == -1 || + (hdidx[HD__AUTHORITY] == -1 && hdidx[HD_HOST] == -1)) { + return false; + } + return true; +} + +const Headers::value_type *get_header(const HeaderIndex &hdidx, int16_t token, + const Headers &nva) { + auto i = hdidx[token]; + if (i == -1) { + return nullptr; + } + return &nva[i]; +} + +namespace { +template InputIt skip_lws(InputIt first, InputIt last) { + for (; first != last; ++first) { + switch (*first) { + case ' ': + case '\t': + continue; + default: + return first; + } + } + return first; +} +} // namespace + +namespace { +template +InputIt skip_to_next_field(InputIt first, InputIt last) { + for (; first != last; ++first) { + switch (*first) { + case ' ': + case '\t': + case ',': + continue; + default: + return first; + } + } + return first; +} +} // namespace + +namespace { +// Skip to the right dquote ('"'), handling backslash escapes. +// Returns |last| if input is not terminated with '"'. +template +InputIt skip_to_right_dquote(InputIt first, InputIt last) { + for (; first != last;) { + switch (*first) { + case '"': + return first; + case '\\': + ++first; + if (first == last) { + return first; + } + break; + } + ++first; + } + return first; +} +} // namespace + +namespace { +// Returns true if link-param does not match pattern |pat| of length +// |patlen| or it has empty value (""). |pat| should be parmname +// followed by "=". +bool check_link_param_empty(const char *first, const char *last, + const char *pat, size_t patlen) { + if (first + patlen <= last) { + if (std::equal(pat, pat + patlen, first, util::CaseCmp())) { + // we only accept URI if pat is followd by "" (e.g., + // loadpolicy="") here. + if (first + patlen + 2 <= last) { + if (*(first + patlen) != '"' || *(first + patlen + 1) != '"') { + return false; + } + } else { + // here we got invalid production (anchor=") or anchor=? + return false; + } + } + } + return true; +} +} // namespace + +namespace { +std::pair +parse_next_link_header_once(const char *first, const char *last) { + first = skip_to_next_field(first, last); + if (first == last || *first != '<') { + return {{{nullptr, nullptr}}, last}; + } + auto url_first = ++first; + first = std::find(first, last, '>'); + if (first == last) { + return {{{nullptr, nullptr}}, first}; + } + auto url_last = first++; + if (first == last) { + return {{{nullptr, nullptr}}, first}; + } + // we expect ';' or ',' here + switch (*first) { + case ',': + return {{{nullptr, nullptr}}, ++first}; + case ';': + ++first; + break; + default: + return {{{nullptr, nullptr}}, last}; + } + + auto ok = false; + auto ign = false; + for (;;) { + first = skip_lws(first, last); + if (first == last) { + return {{{nullptr, nullptr}}, first}; + } + // we expect link-param + + // rel can take several relations using quoted form. + static constexpr char PLP[] = "rel=\""; + static constexpr size_t PLPLEN = sizeof(PLP) - 1; + + static constexpr char PLT[] = "preload"; + static constexpr size_t PLTLEN = sizeof(PLT) - 1; + if (first + PLPLEN < last && *(first + PLPLEN - 1) == '"' && + std::equal(PLP, PLP + PLPLEN, first, util::CaseCmp())) { + // we have to search preload in whitespace separated list: + // rel="preload something http://example.org/foo" + first += PLPLEN; + auto start = first; + for (; first != last;) { + if (*first != ' ' && *first != '"') { + ++first; + continue; + } + + if (start == first) { + return {{{nullptr, nullptr}}, last}; + } + + if (!ok && start + PLTLEN == first && + std::equal(PLT, PLT + PLTLEN, start, util::CaseCmp())) { + ok = true; + } + + if (*first == '"') { + break; + } + first = skip_lws(first, last); + start = first; + } + if (first == last) { + return {{{nullptr, nullptr}}, first}; + } + assert(*first == '"'); + ++first; + if (first == last || *first == ',') { + goto almost_done; + } + if (*first == ';') { + ++first; + // parse next link-param + continue; + } + return {{{nullptr, nullptr}}, last}; + } + // we are only interested in rel=preload parameter. Others are + // simply skipped. + static constexpr char PL[] = "rel=preload"; + static constexpr size_t PLLEN = sizeof(PL) - 1; + if (first + PLLEN == last) { + if (std::equal(PL, PL + PLLEN, first, util::CaseCmp())) { + ok = true; + // this is the end of sequence + return {{{url_first, url_last}}, last}; + } + } else if (first + PLLEN + 1 <= last) { + switch (*(first + PLLEN)) { + case ',': + if (!std::equal(PL, PL + PLLEN, first, util::CaseCmp())) { + break; + } + ok = true; + // skip including ',' + first += PLLEN + 1; + return {{{url_first, url_last}}, first}; + case ';': + if (!std::equal(PL, PL + PLLEN, first, util::CaseCmp())) { + break; + } + ok = true; + // skip including ';' + first += PLLEN + 1; + // continue parse next link-param + continue; + } + } + // we have to reject URI if we have nonempty anchor parameter. + static constexpr char ANCHOR[] = "anchor="; + static constexpr size_t ANCHORLEN = sizeof(ANCHOR) - 1; + if (!ign && !check_link_param_empty(first, last, ANCHOR, ANCHORLEN)) { + ign = true; + } + + // reject URI if we have non-empty loadpolicy. This could be + // tightened up to just pick up "next" or "insert". + static constexpr char LOADPOLICY[] = "loadpolicy="; + static constexpr size_t LOADPOLICYLEN = sizeof(LOADPOLICY) - 1; + if (!ign && + !check_link_param_empty(first, last, LOADPOLICY, LOADPOLICYLEN)) { + ign = true; + } + + auto param_first = first; + for (; first != last;) { + if (util::in_attr_char(*first)) { + ++first; + continue; + } + // '*' is only allowed at the end of parameter name and must be + // followed by '=' + if (last - first >= 2 && first != param_first) { + if (*first == '*' && *(first + 1) == '=') { + ++first; + break; + } + } + if (*first == '=' || *first == ';' || *first == ',') { + break; + } + return {{{nullptr, nullptr}}, last}; + } + if (param_first == first) { + // empty parmname + return {{{nullptr, nullptr}}, last}; + } + // link-param without value is acceptable (see link-extension) if + // it is not followed by '=' + if (first == last || *first == ',') { + goto almost_done; + } + if (*first == ';') { + ++first; + // parse next link-param + continue; + } + // now parsing link-param value + assert(*first == '='); + ++first; + if (first == last) { + // empty value is not acceptable + return {{{nullptr, nullptr}}, first}; + } + if (*first == '"') { + // quoted-string + first = skip_to_right_dquote(first + 1, last); + if (first == last) { + return {{{nullptr, nullptr}}, first}; + } + ++first; + if (first == last || *first == ',') { + goto almost_done; + } + if (*first == ';') { + ++first; + // parse next link-param + continue; + } + return {{{nullptr, nullptr}}, last}; + } + // not quoted-string, skip to next ',' or ';' + if (*first == ',' || *first == ';') { + // empty value + return {{{nullptr, nullptr}}, last}; + } + for (; first != last; ++first) { + if (*first == ',' || *first == ';') { + break; + } + } + if (first == last || *first == ',') { + goto almost_done; + } + assert(*first == ';'); + ++first; + // parse next link-param + } + +almost_done: + assert(first == last || *first == ','); + + if (first != last) { + ++first; + } + if (ok && !ign) { + return {{{url_first, url_last}}, first}; + } + return {{{nullptr, nullptr}}, first}; +} +} // namespace + +std::vector parse_link_header(const char *src, size_t len) { + auto first = src; + auto last = src + len; + std::vector res; + for (; first != last;) { + auto rv = parse_next_link_header_once(first, last); + first = rv.second; + if (rv.first.uri.first != nullptr && rv.first.uri.second != nullptr) { + res.push_back(rv.first); + } + } + return res; +} + +namespace { +void eat_file(std::string &path) { + if (path.empty()) { + path = "/"; + return; + } + auto p = path.size() - 1; + if (path[p] == '/') { + return; + } + p = path.rfind('/', p); + if (p == std::string::npos) { + // this should not happend in normal case, where we expect path + // starts with '/' + path = "/"; + return; + } + path.erase(std::begin(path) + p + 1, std::end(path)); +} +} // namespace + +namespace { +void eat_dir(std::string &path) { + if (path.empty()) { + path = "/"; + return; + } + auto p = path.size() - 1; + if (path[p] != '/') { + p = path.rfind('/', p); + if (p == std::string::npos) { + // this should not happend in normal case, where we expect path + // starts with '/' + path = "/"; + return; + } + } + if (path[p] == '/') { + if (p == 0) { + return; + } + --p; + } + p = path.rfind('/', p); + if (p == std::string::npos) { + // this should not happend in normal case, where we expect path + // starts with '/' + path = "/"; + return; + } + path.erase(std::begin(path) + p + 1, std::end(path)); +} +} // namespace + +std::string path_join(const char *base_path, size_t base_pathlen, + const char *base_query, size_t base_querylen, + const char *rel_path, size_t rel_pathlen, + const char *rel_query, size_t rel_querylen) { + std::string res; + if (rel_pathlen == 0) { + if (base_pathlen == 0) { + res = "/"; + } else { + res.assign(base_path, base_pathlen); + } + if (rel_querylen == 0) { + if (base_querylen) { + res += "?"; + res.append(base_query, base_querylen); + } + return res; + } + res += "?"; + res.append(rel_query, rel_querylen); + return res; + } + + auto first = rel_path; + auto last = rel_path + rel_pathlen; + + if (rel_path[0] == '/') { + res = "/"; + ++first; + } else if (base_pathlen == 0) { + res = "/"; + } else { + res.assign(base_path, base_pathlen); + } + + for (; first != last;) { + if (*first == '.') { + if (first + 1 == last) { + break; + } + if (*(first + 1) == '/') { + first += 2; + continue; + } + if (*(first + 1) == '.') { + if (first + 2 == last) { + eat_dir(res); + break; + } + if (*(first + 2) == '/') { + eat_dir(res); + first += 3; + continue; + } + } + } + if (res.back() != '/') { + eat_file(res); + } + auto slash = std::find(first, last, '/'); + if (slash == last) { + res.append(first, last); + break; + } + res.append(first, slash + 1); + first = slash + 1; + for (; first != last && *first == '/'; ++first) + ; + } + if (rel_querylen) { + res += "?"; + res.append(rel_query, rel_querylen); + } + return res; +} + +bool expect_response_body(int status_code) { + return status_code / 100 != 1 && status_code != 304 && status_code != 204; +} + +bool expect_response_body(const std::string &method, int status_code) { + return method != "HEAD" && expect_response_body(status_code); +} + +} // namespace http2 + +} // namespace nghttp2 diff --git a/src/http2.h b/src/http2.h new file mode 100644 index 0000000..af8fd10 --- /dev/null +++ b/src/http2.h @@ -0,0 +1,296 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2013 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef HTTP2_H +#define HTTP2_H + +#include "nghttp2_config.h" + +#include +#include +#include +#include +#include + +#include + +#include "http-parser/http_parser.h" + +namespace nghttp2 { + +struct Header { + Header(std::string name, std::string value, bool no_index = false, + int16_t token = -1) + : name(std::move(name)), value(std::move(value)), token(token), + no_index(no_index) {} + + Header() : token(-1), no_index(false) {} + + bool operator==(const Header &other) const { + return name == other.name && value == other.value; + } + + bool operator<(const Header &rhs) const { + return name < rhs.name || (name == rhs.name && value < rhs.value); + } + + std::string name; + std::string value; + int16_t token; + bool no_index; +}; + +typedef std::vector
    Headers; + +namespace http2 { + +std::string get_status_string(unsigned int status_code); + +void capitalize(std::string &s, size_t offset); + +// Returns true if |value| is LWS +bool lws(const char *value); + +// Copies the |field| component value from |u| and |url| to the +// |dest|. If |u| does not have |field|, then this function does +// nothing. +void copy_url_component(std::string &dest, const http_parser_url *u, int field, + const char *url); + +Headers::value_type to_header(const uint8_t *name, size_t namelen, + const uint8_t *value, size_t valuelen, + bool no_index, int16_t token); + +// Add name/value pairs to |nva|. If |no_index| is true, this +// name/value pair won't be indexed when it is forwarded to the next +// hop. This function strips white spaces around |value|. +void add_header(Headers &nva, const uint8_t *name, size_t namelen, + const uint8_t *value, size_t valuelen, bool no_index, + int16_t token); + +// Returns pointer to the entry in |nva| which has name |name|. If +// more than one entries which have the name |name|, last occurrence +// in |nva| is returned. If no such entry exist, returns nullptr. +const Headers::value_type *get_header(const Headers &nva, const char *name); + +// Returns nv->second if nv is not nullptr. Otherwise, returns "". +std::string value_to_str(const Headers::value_type *nv); + +// Returns true if the value of |nv| is not empty. +bool non_empty_value(const Headers::value_type *nv); + +// Creates nghttp2_nv using |name| and |value| and returns it. The +// returned value only references the data pointer to name.c_str() and +// value.c_str(). If |no_index| is true, nghttp2_nv flags member has +// NGHTTP2_NV_FLAG_NO_INDEX flag set. +nghttp2_nv make_nv(const std::string &name, const std::string &value, + bool no_index = false); + +// Create nghttp2_nv from string literal |name| and |value|. +template +nghttp2_nv make_nv_ll(const char (&name)[N], const char (&value)[M]) { + return {(uint8_t *)name, (uint8_t *)value, N - 1, M - 1, + NGHTTP2_NV_FLAG_NONE}; +} + +// Create nghttp2_nv from string literal |name| and c-string |value|. +template +nghttp2_nv make_nv_lc(const char (&name)[N], const char *value) { + return {(uint8_t *)name, (uint8_t *)value, N - 1, strlen(value), + NGHTTP2_NV_FLAG_NONE}; +} + +// Create nghttp2_nv from string literal |name| and std::string +// |value|. +template +nghttp2_nv make_nv_ls(const char (&name)[N], const std::string &value) { + return {(uint8_t *)name, (uint8_t *)value.c_str(), N - 1, value.size(), + NGHTTP2_NV_FLAG_NONE}; +} + +// Appends headers in |headers| to |nv|. |headers| must be indexed +// before this call (its element's token field is assigned). Certain +// headers, including disallowed headers in HTTP/2 spec and headers +// which require special handling (i.e. via), are not copied. +void copy_headers_to_nva(std::vector &nva, const Headers &headers); + +// Appends HTTP/1.1 style header lines to |hdrs| from headers in +// |headers|. |headers| must be indexed before this call (its +// element's token field is assigned). Certain headers, which +// requires special handling (i.e. via and cookie), are not appended. +void build_http1_headers_from_headers(std::string &hdrs, + const Headers &headers); + +// Return positive window_size_increment if WINDOW_UPDATE should be +// sent for the stream |stream_id|. If |stream_id| == 0, this function +// determines the necessity of the WINDOW_UPDATE for a connection. +// +// If the function determines WINDOW_UPDATE is not necessary at the +// moment, it returns -1. +int32_t determine_window_update_transmission(nghttp2_session *session, + int32_t stream_id); + +// Dumps name/value pairs in |nv| to |out|. The |nv| must be +// terminated by nullptr. +void dump_nv(FILE *out, const char **nv); + +// Dumps name/value pairs in |nva| to |out|. +void dump_nv(FILE *out, const nghttp2_nv *nva, size_t nvlen); + +// Dumps name/value pairs in |nva| to |out|. +void dump_nv(FILE *out, const Headers &nva); + +// Rewrites redirection URI which usually appears in location header +// field. The |uri| is the URI in the location header field. The |u| +// stores the result of parsed |uri|. The |request_authority| is the +// host or :authority header field value in the request. The +// |upstream_scheme| is either "https" or "http" in the upstream +// interface. Rewrite is done only if location header field value +// contains |match_host| as host excluding port. The |match_host| and +// |request_authority| could be different. If |request_authority| is +// empty, strip authority. +// +// This function returns the new rewritten URI on success. If the +// location URI is not subject to the rewrite, this function returns +// emtpy string. +std::string rewrite_location_uri(const std::string &uri, + const http_parser_url &u, + const std::string &match_host, + const std::string &request_authority, + const std::string &upstream_scheme); + +// Checks the header name/value pair using nghttp2_check_header_name() +// and nghttp2_check_header_value(). If both function returns nonzero, +// this function returns nonzero. +int check_nv(const uint8_t *name, size_t namelen, const uint8_t *value, + size_t valuelen); + +// Returns parsed HTTP status code. Returns -1 on failure. +int parse_http_status_code(const std::string &src); + +// Header fields to be indexed, except HD_MAXIDX which is convenient +// member to get maximum value. +enum { + HD__AUTHORITY, + HD__HOST, + HD__METHOD, + HD__PATH, + HD__SCHEME, + HD__STATUS, + HD_ACCEPT_ENCODING, + HD_ACCEPT_LANGUAGE, + HD_ALT_SVC, + HD_CACHE_CONTROL, + HD_CONNECTION, + HD_CONTENT_LENGTH, + HD_COOKIE, + HD_EXPECT, + HD_HOST, + HD_HTTP2_SETTINGS, + HD_IF_MODIFIED_SINCE, + HD_KEEP_ALIVE, + HD_LINK, + HD_LOCATION, + HD_PROXY_CONNECTION, + HD_SERVER, + HD_TE, + HD_TRAILER, + HD_TRANSFER_ENCODING, + HD_UPGRADE, + HD_USER_AGENT, + HD_VIA, + HD_X_FORWARDED_FOR, + HD_X_FORWARDED_PROTO, + HD_MAXIDX, +}; + +using HeaderIndex = std::array; + +// Looks up header token for header name |name| of length |namelen|. +// Only headers we are interested in are tokenized. If header name +// cannot be tokenized, returns -1. +int lookup_token(const uint8_t *name, size_t namelen); +int lookup_token(const std::string &name); + +// Initializes |hdidx|, header index. The |hdidx| must point to the +// array containing at least HD_MAXIDX elements. +void init_hdidx(HeaderIndex &hdidx); +// Indexes header |token| using index |idx|. +void index_header(HeaderIndex &hdidx, int16_t token, size_t idx); + +// Returns true if HTTP/2 request pseudo header |token| is not indexed +// yet and not -1. +bool check_http2_request_pseudo_header(const HeaderIndex &hdidx, int16_t token); + +// Returns true if HTTP/2 response pseudo header |token| is not +// indexed yet and not -1. +bool check_http2_response_pseudo_header(const HeaderIndex &hdidx, + int16_t token); + +// Returns true if header field denoted by |token| is allowed for +// HTTP/2. +bool http2_header_allowed(int16_t token); + +// Returns true that |hdidx| contains mandatory HTTP/2 request +// headers. +bool http2_mandatory_request_headers_presence(const HeaderIndex &hdidx); + +// Returns header denoted by |token| using index |hdidx|. +const Headers::value_type *get_header(const HeaderIndex &hdidx, int16_t token, + const Headers &nva); + +struct LinkHeader { + // The region of URI is [uri.first, uri.second). + std::pair uri; +}; + +// Returns next URI-reference in Link header field value |src| of +// length |len|. If no URI-reference found after searching all input, +// returned uri field is empty. This imply that empty URI-reference +// is ignored during parsing. +std::vector parse_link_header(const char *src, size_t len); + +// Constructs path by combining base path |base_path| of length +// |base_pathlen| with another path |rel_path| of length +// |rel_pathlen|. The base path and another path can have optional +// query component. This function assumes |base_path| is normalized. +// In other words, it does not contain ".." or "." path components +// and starts with "/" if it is not empty. +std::string path_join(const char *base_path, size_t base_pathlen, + const char *base_query, size_t base_querylen, + const char *rel_path, size_t rel_pathlen, + const char *rel_query, size_t rel_querylen); + +// true if response has body, taking into account the request method +// and status code. +bool expect_response_body(const std::string &method, int status_code); + +// true if response has body, taking into account status code only. +bool expect_response_body(int status_code); + +} // namespace http2 + +} // namespace nghttp2 + +#endif // HTTP2_H diff --git a/src/http2_test.cc b/src/http2_test.cc new file mode 100644 index 0000000..e2975d1 --- /dev/null +++ b/src/http2_test.cc @@ -0,0 +1,826 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2013 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "http2_test.h" + +#include +#include +#include + +#include + +#include "http-parser/http_parser.h" + +#include "http2.h" +#include "util.h" + +using namespace nghttp2; + +#define MAKE_NV(K, V) \ + { \ + (uint8_t *) K, (uint8_t *)V, sizeof(K) - 1, sizeof(V) - 1, \ + NGHTTP2_NV_FLAG_NONE \ + } + +namespace shrpx { + +namespace { +void check_nv(const Header &a, const nghttp2_nv *b) { + CU_ASSERT(a.name.size() == b->namelen); + CU_ASSERT(a.value.size() == b->valuelen); + CU_ASSERT(memcmp(a.name.c_str(), b->name, b->namelen) == 0); + CU_ASSERT(memcmp(a.value.c_str(), b->value, b->valuelen) == 0); +} +} // namespace + +void test_http2_add_header(void) { + auto nva = Headers(); + + http2::add_header(nva, (const uint8_t *)"alpha", 5, (const uint8_t *)"123", 3, + false, -1); + CU_ASSERT(Headers::value_type("alpha", "123") == nva[0]); + CU_ASSERT(!nva[0].no_index); + + nva.clear(); + + http2::add_header(nva, (const uint8_t *)"alpha", 5, (const uint8_t *)"", 0, + true, -1); + CU_ASSERT(Headers::value_type("alpha", "") == nva[0]); + CU_ASSERT(nva[0].no_index); + + nva.clear(); + + http2::add_header(nva, (const uint8_t *)"a", 1, (const uint8_t *)" b", 2, + false, -1); + CU_ASSERT(Headers::value_type("a", "b") == nva[0]); + + nva.clear(); + + http2::add_header(nva, (const uint8_t *)"a", 1, (const uint8_t *)"b ", 2, + false, -1); + CU_ASSERT(Headers::value_type("a", "b") == nva[0]); + + nva.clear(); + + http2::add_header(nva, (const uint8_t *)"a", 1, (const uint8_t *)" b ", 5, + false, -1); + CU_ASSERT(Headers::value_type("a", "b") == nva[0]); + + nva.clear(); + + http2::add_header(nva, (const uint8_t *)"a", 1, (const uint8_t *)" bravo ", + 9, false, -1); + CU_ASSERT(Headers::value_type("a", "bravo") == nva[0]); + + nva.clear(); + + http2::add_header(nva, (const uint8_t *)"a", 1, (const uint8_t *)" ", 4, + false, -1); + CU_ASSERT(Headers::value_type("a", "") == nva[0]); + + nva.clear(); + + http2::add_header(nva, (const uint8_t *)"te", 2, (const uint8_t *)"trailers", + 8, false, http2::HD_TE); + CU_ASSERT(http2::HD_TE == nva[0].token); +} + +void test_http2_get_header(void) { + auto nva = Headers{{"alpha", "1"}, + {"bravo", "2"}, + {"bravo", "3"}, + {"charlie", "4"}, + {"delta", "5"}, + {"echo", "6"}, + {"content-length", "7"}}; + const Headers::value_type *rv; + rv = http2::get_header(nva, "delta"); + CU_ASSERT(rv != nullptr); + CU_ASSERT("delta" == rv->name); + + rv = http2::get_header(nva, "bravo"); + CU_ASSERT(rv != nullptr); + CU_ASSERT("bravo" == rv->name); + + rv = http2::get_header(nva, "foxtrot"); + CU_ASSERT(rv == nullptr); + + http2::HeaderIndex hdidx; + http2::init_hdidx(hdidx); + hdidx[http2::HD_CONTENT_LENGTH] = 6; + rv = http2::get_header(hdidx, http2::HD_CONTENT_LENGTH, nva); + CU_ASSERT("content-length" == rv->name); +} + +namespace { +auto headers = + Headers{{"alpha", "0", true}, + {"bravo", "1"}, + {"connection", "2", false, http2::HD_CONNECTION}, + {"connection", "3", false, http2::HD_CONNECTION}, + {"delta", "4"}, + {"expect", "5"}, + {"foxtrot", "6"}, + {"tango", "7"}, + {"te", "8", false, http2::HD_TE}, + {"te", "9", false, http2::HD_TE}, + {"x-forwarded-proto", "10", false, http2::HD_X_FORWARDED_FOR}, + {"x-forwarded-proto", "11", false, http2::HD_X_FORWARDED_FOR}, + {"zulu", "12"}}; +} // namespace + +void test_http2_copy_headers_to_nva(void) { + std::vector nva; + http2::copy_headers_to_nva(nva, headers); + CU_ASSERT(7 == nva.size()); + auto ans = std::vector{0, 1, 4, 5, 6, 7, 12}; + for (size_t i = 0; i < ans.size(); ++i) { + check_nv(headers[ans[i]], &nva[i]); + + if (ans[i] == 0) { + CU_ASSERT(nva[i].flags & NGHTTP2_NV_FLAG_NO_INDEX); + } else { + CU_ASSERT(NGHTTP2_NV_FLAG_NONE == nva[i].flags); + } + } +} + +void test_http2_build_http1_headers_from_headers(void) { + std::string hdrs; + http2::build_http1_headers_from_headers(hdrs, headers); + CU_ASSERT(hdrs == "Alpha: 0\r\n" + "Bravo: 1\r\n" + "Delta: 4\r\n" + "Expect: 5\r\n" + "Foxtrot: 6\r\n" + "Tango: 7\r\n" + "Te: 8\r\n" + "Te: 9\r\n" + "Zulu: 12\r\n"); +} + +void test_http2_lws(void) { + CU_ASSERT(!http2::lws("alpha")); + CU_ASSERT(http2::lws(" ")); + CU_ASSERT(http2::lws("")); +} + +namespace { +void check_rewrite_location_uri(const std::string &want, const std::string &uri, + const std::string &match_host, + const std::string &req_authority, + const std::string &upstream_scheme) { + http_parser_url u; + memset(&u, 0, sizeof(u)); + CU_ASSERT(0 == http_parser_parse_url(uri.c_str(), uri.size(), 0, &u)); + auto got = http2::rewrite_location_uri(uri, u, match_host, req_authority, + upstream_scheme); + CU_ASSERT(want == got); +} +} // namespace + +void test_http2_rewrite_location_uri(void) { + check_rewrite_location_uri("https://localhost:3000/alpha?bravo#charlie", + "http://localhost:3001/alpha?bravo#charlie", + "localhost:3001", "localhost:3000", "https"); + check_rewrite_location_uri("https://localhost/", "http://localhost:3001/", + "localhost", "localhost", "https"); + check_rewrite_location_uri("http://localhost/", "http://localhost:3001/", + "localhost", "localhost", "http"); + check_rewrite_location_uri("http://localhost:443/", "http://localhost:3001/", + "localhost", "localhost:443", "http"); + check_rewrite_location_uri("https://localhost:80/", "http://localhost:3001/", + "localhost", "localhost:80", "https"); + check_rewrite_location_uri("", "http://localhost:3001/", "127.0.0.1", + "127.0.0.1", "https"); + check_rewrite_location_uri("https://localhost:3000/", + "http://localhost:3001/", "localhost", + "localhost:3000", "https"); + check_rewrite_location_uri("https://localhost:3000/", "http://localhost/", + "localhost", "localhost:3000", "https"); + + // match_host != req_authority + check_rewrite_location_uri("https://example.org", "http://127.0.0.1:8080", + "127.0.0.1", "example.org", "https"); + check_rewrite_location_uri("", "http://example.org", "127.0.0.1", + "example.org", "https"); +} + +void test_http2_parse_http_status_code(void) { + CU_ASSERT(200 == http2::parse_http_status_code("200")); + CU_ASSERT(102 == http2::parse_http_status_code("102")); + CU_ASSERT(-1 == http2::parse_http_status_code("099")); + CU_ASSERT(-1 == http2::parse_http_status_code("99")); + CU_ASSERT(-1 == http2::parse_http_status_code("-1")); + CU_ASSERT(-1 == http2::parse_http_status_code("20a")); + CU_ASSERT(-1 == http2::parse_http_status_code("")); +} + +void test_http2_index_header(void) { + http2::HeaderIndex hdidx; + http2::init_hdidx(hdidx); + + http2::index_header(hdidx, http2::HD__AUTHORITY, 0); + http2::index_header(hdidx, -1, 1); + + CU_ASSERT(0 == hdidx[http2::HD__AUTHORITY]); +} + +void test_http2_lookup_token(void) { + CU_ASSERT(http2::HD__AUTHORITY == http2::lookup_token(":authority")); + CU_ASSERT(-1 == http2::lookup_token(":authorit")); + CU_ASSERT(-1 == http2::lookup_token(":Authority")); + CU_ASSERT(http2::HD_EXPECT == http2::lookup_token("expect")); +} + +void test_http2_check_http2_pseudo_header(void) { + http2::HeaderIndex hdidx; + http2::init_hdidx(hdidx); + + CU_ASSERT(http2::check_http2_request_pseudo_header(hdidx, http2::HD__METHOD)); + hdidx[http2::HD__PATH] = 0; + CU_ASSERT(http2::check_http2_request_pseudo_header(hdidx, http2::HD__METHOD)); + hdidx[http2::HD__METHOD] = 1; + CU_ASSERT( + !http2::check_http2_request_pseudo_header(hdidx, http2::HD__METHOD)); + CU_ASSERT(!http2::check_http2_request_pseudo_header(hdidx, http2::HD_VIA)); + + http2::init_hdidx(hdidx); + + CU_ASSERT( + http2::check_http2_response_pseudo_header(hdidx, http2::HD__STATUS)); + hdidx[http2::HD__STATUS] = 0; + CU_ASSERT( + !http2::check_http2_response_pseudo_header(hdidx, http2::HD__STATUS)); + CU_ASSERT(!http2::check_http2_response_pseudo_header(hdidx, http2::HD_VIA)); +} + +void test_http2_http2_header_allowed(void) { + CU_ASSERT(http2::http2_header_allowed(http2::HD__PATH)); + CU_ASSERT(http2::http2_header_allowed(http2::HD_CONTENT_LENGTH)); + CU_ASSERT(!http2::http2_header_allowed(http2::HD_CONNECTION)); +} + +void test_http2_mandatory_request_headers_presence(void) { + http2::HeaderIndex hdidx; + http2::init_hdidx(hdidx); + + CU_ASSERT(!http2::http2_mandatory_request_headers_presence(hdidx)); + hdidx[http2::HD__AUTHORITY] = 0; + CU_ASSERT(!http2::http2_mandatory_request_headers_presence(hdidx)); + hdidx[http2::HD__METHOD] = 1; + CU_ASSERT(!http2::http2_mandatory_request_headers_presence(hdidx)); + hdidx[http2::HD__PATH] = 2; + CU_ASSERT(!http2::http2_mandatory_request_headers_presence(hdidx)); + hdidx[http2::HD__SCHEME] = 3; + CU_ASSERT(http2::http2_mandatory_request_headers_presence(hdidx)); + + hdidx[http2::HD__AUTHORITY] = -1; + hdidx[http2::HD_HOST] = 0; + CU_ASSERT(http2::http2_mandatory_request_headers_presence(hdidx)); +} + +void test_http2_parse_link_header(void) { + { + // only URI appears; we don't extract URI unless it bears rel=preload + const char s[] = ""; + auto res = http2::parse_link_header(s, sizeof(s) - 1); + CU_ASSERT(0 == res.size()); + } + { + // URI url should be extracted + const char s[] = "; rel=preload"; + auto res = http2::parse_link_header(s, sizeof(s) - 1); + CU_ASSERT(1 == res.size()); + CU_ASSERT(std::make_pair(&s[1], &s[4]) == res[0].uri); + } + { + // With extra link-param. URI url should be extracted + const char s[] = "; rel=preload; as=file"; + auto res = http2::parse_link_header(s, sizeof(s) - 1); + CU_ASSERT(1 == res.size()); + CU_ASSERT(std::make_pair(&s[1], &s[4]) == res[0].uri); + } + { + // With extra link-param. URI url should be extracted + const char s[] = "; as=file; rel=preload"; + auto res = http2::parse_link_header(s, sizeof(s) - 1); + CU_ASSERT(1 == res.size()); + CU_ASSERT(std::make_pair(&s[1], &s[4]) == res[0].uri); + } + { + // With extra link-param and quote-string. URI url should be + // extracted + const char s[] = R"(; rel=preload; title="foo,bar")"; + auto res = http2::parse_link_header(s, sizeof(s) - 1); + CU_ASSERT(1 == res.size()); + CU_ASSERT(std::make_pair(&s[1], &s[4]) == res[0].uri); + } + { + // With extra link-param and quote-string. URI url should be + // extracted + const char s[] = R"(; title="foo,bar"; rel=preload)"; + auto res = http2::parse_link_header(s, sizeof(s) - 1); + CU_ASSERT(1 == res.size()); + CU_ASSERT(std::make_pair(&s[1], &s[4]) == res[0].uri); + } + { + // ',' after quote-string + const char s[] = R"(; title="foo,bar", ; rel=preload)"; + auto res = http2::parse_link_header(s, sizeof(s) - 1); + CU_ASSERT(1 == res.size()); + CU_ASSERT(std::make_pair(&s[25], &s[28]) == res[0].uri); + } + { + // Only first URI should be extracted. + const char s[] = "; rel=preload, "; + auto res = http2::parse_link_header(s, sizeof(s) - 1); + CU_ASSERT(1 == res.size()); + CU_ASSERT(std::make_pair(&s[1], &s[4]) == res[0].uri); + } + { + // Both have rel=preload, so both urls should be extracted + const char s[] = "; rel=preload, ; rel=preload"; + auto res = http2::parse_link_header(s, sizeof(s) - 1); + CU_ASSERT(2 == res.size()); + CU_ASSERT(std::make_pair(&s[1], &s[4]) == res[0].uri); + CU_ASSERT(std::make_pair(&s[21], &s[24]) == res[1].uri); + } + { + // Second URI uri should be extracted. + const char s[] = ", ;rel=preload"; + auto res = http2::parse_link_header(s, sizeof(s) - 1); + CU_ASSERT(1 == res.size()); + CU_ASSERT(std::make_pair(&s[8], &s[11]) == res[0].uri); + } + { + // Error if input ends with ';' + const char s[] = ";rel=preload;"; + auto res = http2::parse_link_header(s, sizeof(s) - 1); + CU_ASSERT(0 == res.size()); + } + { + // OK if input ends with ',' + const char s[] = ";rel=preload,"; + auto res = http2::parse_link_header(s, sizeof(s) - 1); + CU_ASSERT(1 == res.size()); + CU_ASSERT(std::make_pair(&s[1], &s[4]) == res[0].uri); + } + { + // Multiple repeated ','s between fields is OK + const char s[] = ",,,;rel=preload"; + auto res = http2::parse_link_header(s, sizeof(s) - 1); + CU_ASSERT(1 == res.size()); + CU_ASSERT(std::make_pair(&s[9], &s[12]) == res[0].uri); + } + { + // Error if url is not enclosed by <> + const char s[] = "url>;rel=preload;"; + auto res = http2::parse_link_header(s, sizeof(s) - 1); + CU_ASSERT(0 == res.size()); + } + { + // Error if url is not enclosed by <> + const char s[] = "' is not followed by ';' + const char s[] = " rel=preload"; + auto res = http2::parse_link_header(s, sizeof(s) - 1); + CU_ASSERT(0 == res.size()); + } + { + // Starting with whitespace is no problem. + const char s[] = " ; rel=preload"; + auto res = http2::parse_link_header(s, sizeof(s) - 1); + CU_ASSERT(1 == res.size()); + CU_ASSERT(std::make_pair(&s[3], &s[6]) == res[0].uri); + } + { + // preload is a prefix of bogus rel parameter value + const char s[] = "; rel=preloadx"; + auto res = http2::parse_link_header(s, sizeof(s) - 1); + CU_ASSERT(0 == res.size()); + } + { + // preload in relation-types list + const char s[] = R"(; rel="preload")"; + auto res = http2::parse_link_header(s, sizeof(s) - 1); + CU_ASSERT(1 == res.size()); + CU_ASSERT(std::make_pair(&s[1], &s[4]) == res[0].uri); + } + { + // preload in relation-types list followed by another parameter + const char s[] = R"(; rel="preload foo")"; + auto res = http2::parse_link_header(s, sizeof(s) - 1); + CU_ASSERT(1 == res.size()); + CU_ASSERT(std::make_pair(&s[1], &s[4]) == res[0].uri); + } + { + // preload in relation-types list following another parameter + const char s[] = R"(; rel="foo preload")"; + auto res = http2::parse_link_header(s, sizeof(s) - 1); + CU_ASSERT(1 == res.size()); + CU_ASSERT(std::make_pair(&s[1], &s[4]) == res[0].uri); + } + { + // preload in relation-types list between other parameters + const char s[] = R"(; rel="foo preload bar")"; + auto res = http2::parse_link_header(s, sizeof(s) - 1); + CU_ASSERT(1 == res.size()); + CU_ASSERT(std::make_pair(&s[1], &s[4]) == res[0].uri); + } + { + // preload in relation-types list between other parameters + const char s[] = R"(; rel="foo preload bar")"; + auto res = http2::parse_link_header(s, sizeof(s) - 1); + CU_ASSERT(1 == res.size()); + CU_ASSERT(std::make_pair(&s[1], &s[4]) == res[0].uri); + } + { + // no preload in relation-types list + const char s[] = R"(; rel="foo")"; + auto res = http2::parse_link_header(s, sizeof(s) - 1); + CU_ASSERT(0 == res.size()); + } + { + // no preload in relation-types list, multiple unrelated elements. + const char s[] = R"(; rel="foo bar")"; + auto res = http2::parse_link_header(s, sizeof(s) - 1); + CU_ASSERT(0 == res.size()); + } + { + // preload in relation-types list, followed by another link-value. + const char s[] = R"(; rel="preload", )"; + auto res = http2::parse_link_header(s, sizeof(s) - 1); + CU_ASSERT(1 == res.size()); + CU_ASSERT(std::make_pair(&s[1], &s[4]) == res[0].uri); + } + { + // preload in relation-types list, following another link-value. + const char s[] = R"(, ; rel="preload")"; + auto res = http2::parse_link_header(s, sizeof(s) - 1); + CU_ASSERT(1 == res.size()); + CU_ASSERT(std::make_pair(&s[8], &s[11]) == res[0].uri); + } + { + // preload in relation-types list, followed by another link-param. + const char s[] = R"(; rel="preload"; as="font")"; + auto res = http2::parse_link_header(s, sizeof(s) - 1); + CU_ASSERT(1 == res.size()); + CU_ASSERT(std::make_pair(&s[1], &s[4]) == res[0].uri); + } + { + // preload in relation-types list, followed by character other + // than ';' or ',' + const char s[] = R"(; rel="preload".)"; + auto res = http2::parse_link_header(s, sizeof(s) - 1); + CU_ASSERT(0 == res.size()); + } + { + // preload in relation-types list, followed by ';' but it + // terminates input + const char s[] = R"(; rel="preload";)"; + auto res = http2::parse_link_header(s, sizeof(s) - 1); + CU_ASSERT(0 == res.size()); + } + { + // preload in relation-types list, followed by ',' but it + // terminates input + const char s[] = R"(; rel="preload",)"; + auto res = http2::parse_link_header(s, sizeof(s) - 1); + CU_ASSERT(1 == res.size()); + CU_ASSERT(std::make_pair(&s[1], &s[4]) == res[0].uri); + } + { + // preload in relation-types list but there is preceding white + // space. + const char s[] = R"(; rel=" preload")"; + auto res = http2::parse_link_header(s, sizeof(s) - 1); + CU_ASSERT(0 == res.size()); + } + { + // preload in relation-types list but there is trailing white + // space. + const char s[] = R"(; rel="preload ")"; + auto res = http2::parse_link_header(s, sizeof(s) - 1); + CU_ASSERT(0 == res.size()); + } + { + // backslash escaped characters in quoted-string + const char s[] = R"(; rel=preload; title="foo\"baz\"bar")"; + auto res = http2::parse_link_header(s, sizeof(s) - 1); + CU_ASSERT(1 == res.size()); + CU_ASSERT(std::make_pair(&s[1], &s[4]) == res[0].uri); + } + { + // anchor="" is acceptable + const char s[] = R"(; rel=preload; anchor="")"; + auto res = http2::parse_link_header(s, sizeof(s) - 1); + CU_ASSERT(1 == res.size()); + CU_ASSERT(std::make_pair(&s[1], &s[4]) == res[0].uri); + } + { + // With anchor="#foo", url should be ignored + const char s[] = R"(; rel=preload; anchor="#foo")"; + auto res = http2::parse_link_header(s, sizeof(s) - 1); + CU_ASSERT(0 == res.size()); + } + { + // With anchor=f, url should be ignored + const char s[] = "; rel=preload; anchor=f"; + auto res = http2::parse_link_header(s, sizeof(s) - 1); + CU_ASSERT(0 == res.size()); + } + { + // First url is ignored With anchor="#foo", but url should be + // accepted. + const char s[] = R"(; rel=preload; anchor="#foo", ; rel=preload)"; + auto res = http2::parse_link_header(s, sizeof(s) - 1); + CU_ASSERT(1 == res.size()); + CU_ASSERT(std::make_pair(&s[36], &s[39]) == res[0].uri); + } + { + // With loadpolicy="next", url should be ignored + const char s[] = R"(; rel=preload; loadpolicy="next")"; + auto res = http2::parse_link_header(s, sizeof(s) - 1); + CU_ASSERT(0 == res.size()); + } + { + // url should be picked up if empty loadpolicy is specified + const char s[] = R"(; rel=preload; loadpolicy="")"; + auto res = http2::parse_link_header(s, sizeof(s) - 1); + CU_ASSERT(1 == res.size()); + CU_ASSERT(std::make_pair(&s[1], &s[4]) == res[0].uri); + } + { + // case-insensitive match + const char s[] = R"(; rel=preload; ANCHOR="#foo", ; )" + R"(REL=PRELOAD, ; REL="foo PRELOAD bar")"; + auto res = http2::parse_link_header(s, sizeof(s) - 1); + CU_ASSERT(2 == res.size()); + CU_ASSERT(std::make_pair(&s[36], &s[39]) == res[0].uri); + CU_ASSERT(std::make_pair(&s[42 + 14], &s[42 + 17]) == res[1].uri); + } +} + +void test_http2_path_join(void) { + { + const char base[] = "/"; + const char rel[] = "/"; + CU_ASSERT("/" == http2::path_join(base, sizeof(base) - 1, nullptr, 0, rel, + sizeof(rel) - 1, nullptr, 0)); + } + { + const char base[] = "/"; + const char rel[] = "/alpha"; + CU_ASSERT("/alpha" == http2::path_join(base, sizeof(base) - 1, nullptr, 0, + rel, sizeof(rel) - 1, nullptr, 0)); + } + { + // rel ends with trailing '/' + const char base[] = "/"; + const char rel[] = "/alpha/"; + CU_ASSERT("/alpha/" == http2::path_join(base, sizeof(base) - 1, nullptr, 0, + rel, sizeof(rel) - 1, nullptr, 0)); + } + { + // rel contains multiple components + const char base[] = "/"; + const char rel[] = "/alpha/bravo"; + CU_ASSERT("/alpha/bravo" == http2::path_join(base, sizeof(base) - 1, + nullptr, 0, rel, + sizeof(rel) - 1, nullptr, 0)); + } + { + // rel is relative + const char base[] = "/"; + const char rel[] = "alpha/bravo"; + CU_ASSERT("/alpha/bravo" == http2::path_join(base, sizeof(base) - 1, + nullptr, 0, rel, + sizeof(rel) - 1, nullptr, 0)); + } + { + // rel is relative and base ends without /, which means it refers + // to file. + const char base[] = "/alpha"; + const char rel[] = "bravo/charlie"; + CU_ASSERT("/bravo/charlie" == + http2::path_join(base, sizeof(base) - 1, nullptr, 0, rel, + sizeof(rel) - 1, nullptr, 0)); + } + { + // rel contains repeated '/'s + const char base[] = "/"; + const char rel[] = "/alpha/////bravo/////"; + CU_ASSERT("/alpha/bravo/" == http2::path_join(base, sizeof(base) - 1, + nullptr, 0, rel, + sizeof(rel) - 1, nullptr, 0)); + } + { + // base ends with '/', so '..' eats 'bravo' + const char base[] = "/alpha/bravo/"; + const char rel[] = "../charlie/delta"; + CU_ASSERT("/alpha/charlie/delta" == + http2::path_join(base, sizeof(base) - 1, nullptr, 0, rel, + sizeof(rel) - 1, nullptr, 0)); + } + { + // base does not end with '/', so '..' eats 'alpha/bravo' + const char base[] = "/alpha/bravo"; + const char rel[] = "../charlie"; + CU_ASSERT("/charlie" == http2::path_join(base, sizeof(base) - 1, nullptr, 0, + rel, sizeof(rel) - 1, nullptr, 0)); + } + { + // 'charlie' is eaten by following '..' + const char base[] = "/alpha/bravo/"; + const char rel[] = "../charlie/../delta"; + CU_ASSERT("/alpha/delta" == http2::path_join(base, sizeof(base) - 1, + nullptr, 0, rel, + sizeof(rel) - 1, nullptr, 0)); + } + { + // excessive '..' results in '/' + const char base[] = "/alpha/bravo/"; + const char rel[] = "../../../"; + CU_ASSERT("/" == http2::path_join(base, sizeof(base) - 1, nullptr, 0, rel, + sizeof(rel) - 1, nullptr, 0)); + } + { + // excessive '..' and path component + const char base[] = "/alpha/bravo/"; + const char rel[] = "../../../charlie"; + CU_ASSERT("/charlie" == http2::path_join(base, sizeof(base) - 1, nullptr, 0, + rel, sizeof(rel) - 1, nullptr, 0)); + } + { + // rel ends with '..' + const char base[] = "/alpha/bravo/"; + const char rel[] = "charlie/.."; + CU_ASSERT("/alpha/bravo/" == http2::path_join(base, sizeof(base) - 1, + nullptr, 0, rel, + sizeof(rel) - 1, nullptr, 0)); + } + { + // base empty and rel contains '..' + const char base[] = ""; + const char rel[] = "charlie/.."; + CU_ASSERT("/" == http2::path_join(base, sizeof(base) - 1, nullptr, 0, rel, + sizeof(rel) - 1, nullptr, 0)); + } + { + // '.' is ignored + const char base[] = "/"; + const char rel[] = "charlie/././././delta"; + CU_ASSERT("/charlie/delta" == + http2::path_join(base, sizeof(base) - 1, nullptr, 0, rel, + sizeof(rel) - 1, nullptr, 0)); + } + { + // trailing '.' is ignored + const char base[] = "/"; + const char rel[] = "charlie/."; + CU_ASSERT("/charlie/" == http2::path_join(base, sizeof(base) - 1, nullptr, + 0, rel, sizeof(rel) - 1, nullptr, + 0)); + } + { + // query + const char base[] = "/"; + const char rel[] = "/"; + const char relq[] = "q"; + CU_ASSERT("/?q" == http2::path_join(base, sizeof(base) - 1, nullptr, 0, rel, + sizeof(rel) - 1, relq, + sizeof(relq) - 1)); + } + { + // empty rel and query + const char base[] = "/alpha"; + const char rel[] = ""; + const char relq[] = "q"; + CU_ASSERT("/alpha?q" == http2::path_join(base, sizeof(base) - 1, nullptr, 0, + rel, sizeof(rel) - 1, relq, + sizeof(relq) - 1)); + } + { + // both rel and query are empty + const char base[] = "/alpha"; + const char baseq[] = "r"; + const char rel[] = ""; + const char relq[] = ""; + CU_ASSERT("/alpha?r" == + http2::path_join(base, sizeof(base) - 1, baseq, sizeof(baseq) - 1, + rel, sizeof(rel) - 1, relq, sizeof(relq) - 1)); + } + { + // empty base + const char base[] = ""; + const char rel[] = "/alpha"; + CU_ASSERT("/alpha" == http2::path_join(base, sizeof(base) - 1, nullptr, 0, + rel, sizeof(rel) - 1, nullptr, 0)); + } + { + // everything is empty + CU_ASSERT("/" == + http2::path_join(nullptr, 0, nullptr, 0, nullptr, 0, nullptr, 0)); + } + { + // only baseq is not empty + const char base[] = ""; + const char baseq[] = "r"; + const char rel[] = ""; + CU_ASSERT("/?r" == http2::path_join(base, sizeof(base) - 1, baseq, + sizeof(baseq) - 1, rel, sizeof(rel) - 1, + nullptr, 0)); + } +} + +} // namespace shrpx diff --git a/src/http2_test.h b/src/http2_test.h new file mode 100644 index 0000000..36c5a59 --- /dev/null +++ b/src/http2_test.h @@ -0,0 +1,51 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2013 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef SHRPX_HTTP2_TEST_H +#define SHRPX_HTTP2_TEST_H + +#ifdef HAVE_CONFIG_H +#include +#endif // HAVE_CONFIG_H + +namespace shrpx { + +void test_http2_add_header(void); +void test_http2_get_header(void); +void test_http2_copy_headers_to_nva(void); +void test_http2_build_http1_headers_from_headers(void); +void test_http2_lws(void); +void test_http2_rewrite_location_uri(void); +void test_http2_parse_http_status_code(void); +void test_http2_index_header(void); +void test_http2_lookup_token(void); +void test_http2_check_http2_pseudo_header(void); +void test_http2_http2_header_allowed(void); +void test_http2_mandatory_request_headers_presence(void); +void test_http2_parse_link_header(void); +void test_http2_path_join(void); + +} // namespace shrpx + +#endif // SHRPX_HTTP2_TEST_H diff --git a/src/includes/Makefile.am b/src/includes/Makefile.am new file mode 100644 index 0000000..abc93c1 --- /dev/null +++ b/src/includes/Makefile.am @@ -0,0 +1,27 @@ +# nghttp2 - HTTP/2 C Library + +# Copyright (c) 2014 Tatsuhiro Tsujikawa + +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: + +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. + +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +if ENABLE_ASIO_LIB +nobase_include_HEADERS = nghttp2/asio_http2.h nghttp2/asio_http2_client.h \ + nghttp2/asio_http2_server.h +endif # ENABLE_ASIO_LIB diff --git a/src/includes/Makefile.in b/src/includes/Makefile.in new file mode 100644 index 0000000..9892cef --- /dev/null +++ b/src/includes/Makefile.in @@ -0,0 +1,640 @@ +# Makefile.in generated by automake 1.14.1 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2013 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +# nghttp2 - HTTP/2 C Library + +# Copyright (c) 2014 Tatsuhiro Tsujikawa + +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: + +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. + +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +VPATH = @srcdir@ +am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)' +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +target_triplet = @target@ +subdir = src/includes +DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \ + $(am__nobase_include_HEADERS_DIST) +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/ax_boost_asio.m4 \ + $(top_srcdir)/m4/ax_boost_base.m4 \ + $(top_srcdir)/m4/ax_boost_system.m4 \ + $(top_srcdir)/m4/ax_boost_thread.m4 \ + $(top_srcdir)/m4/ax_check_compile_flag.m4 \ + $(top_srcdir)/m4/ax_cxx_compile_stdcxx_11.m4 \ + $(top_srcdir)/m4/ax_have_epoll.m4 \ + $(top_srcdir)/m4/ax_python_devel.m4 \ + $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/libxml2.m4 \ + $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ + $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +SOURCES = +DIST_SOURCES = +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +am__nobase_include_HEADERS_DIST = nghttp2/asio_http2.h \ + nghttp2/asio_http2_client.h nghttp2/asio_http2_server.h +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +am__uninstall_files_from_dir = { \ + test -z "$$files" \ + || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ + || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ + $(am__cd) "$$dir" && rm -f $$files; }; \ + } +am__installdirs = "$(DESTDIR)$(includedir)" +HEADERS = $(nobase_include_HEADERS) +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +ETAGS = etags +CTAGS = ctags +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +APPLDFLAGS = @APPLDFLAGS@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +BOOST_ASIO_LIB = @BOOST_ASIO_LIB@ +BOOST_CPPFLAGS = @BOOST_CPPFLAGS@ +BOOST_LDFLAGS = @BOOST_LDFLAGS@ +BOOST_SYSTEM_LIB = @BOOST_SYSTEM_LIB@ +BOOST_THREAD_LIB = @BOOST_THREAD_LIB@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CUNIT_CFLAGS = @CUNIT_CFLAGS@ +CUNIT_LIBS = @CUNIT_LIBS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +CYTHON = @CYTHON@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DLLTOOL = @DLLTOOL@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +GREP = @GREP@ +HAVE_CXX11 = @HAVE_CXX11@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +JANSSON_CFLAGS = @JANSSON_CFLAGS@ +JANSSON_LIBS = @JANSSON_LIBS@ +JEMALLOC_LIBS = @JEMALLOC_LIBS@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBEVENT_OPENSSL_CFLAGS = @LIBEVENT_OPENSSL_CFLAGS@ +LIBEVENT_OPENSSL_LIBS = @LIBEVENT_OPENSSL_LIBS@ +LIBEV_CFLAGS = @LIBEV_CFLAGS@ +LIBEV_LIBS = @LIBEV_LIBS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBSPDYLAY_CFLAGS = @LIBSPDYLAY_CFLAGS@ +LIBSPDYLAY_LIBS = @LIBSPDYLAY_LIBS@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +LT_AGE = @LT_AGE@ +LT_CURRENT = @LT_CURRENT@ +LT_REVISION = @LT_REVISION@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MKDIR_P = @MKDIR_P@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OPENSSL_CFLAGS = @OPENSSL_CFLAGS@ +OPENSSL_LIBS = @OPENSSL_LIBS@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PACKAGE_VERSION_NUM = @PACKAGE_VERSION_NUM@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PKG_CONFIG = @PKG_CONFIG@ +PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ +PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ +PYTHON = @PYTHON@ +PYTHON_CPPFLAGS = @PYTHON_CPPFLAGS@ +PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@ +PYTHON_EXTRA_LDFLAGS = @PYTHON_EXTRA_LDFLAGS@ +PYTHON_EXTRA_LIBS = @PYTHON_EXTRA_LIBS@ +PYTHON_LDFLAGS = @PYTHON_LDFLAGS@ +PYTHON_PLATFORM = @PYTHON_PLATFORM@ +PYTHON_PREFIX = @PYTHON_PREFIX@ +PYTHON_SITE_PKG = @PYTHON_SITE_PKG@ +PYTHON_VERSION = @PYTHON_VERSION@ +RANLIB = @RANLIB@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +TESTLDADD = @TESTLDADD@ +VERSION = @VERSION@ +WARNCFLAGS = @WARNCFLAGS@ +XML2_CONFIG = @XML2_CONFIG@ +XML_CPPFLAGS = @XML_CPPFLAGS@ +XML_LIBS = @XML_LIBS@ +ZLIB_CFLAGS = @ZLIB_CFLAGS@ +ZLIB_LIBS = @ZLIB_LIBS@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +pkgpyexecdir = @pkgpyexecdir@ +pkgpythondir = @pkgpythondir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +pyexecdir = @pyexecdir@ +pythondir = @pythondir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target = @target@ +target_alias = @target_alias@ +target_cpu = @target_cpu@ +target_os = @target_os@ +target_vendor = @target_vendor@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +@ENABLE_ASIO_LIB_TRUE@nobase_include_HEADERS = nghttp2/asio_http2.h nghttp2/asio_http2_client.h \ +@ENABLE_ASIO_LIB_TRUE@ nghttp2/asio_http2_server.h + +all: all-am + +.SUFFIXES: +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/includes/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu src/includes/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs +install-nobase_includeHEADERS: $(nobase_include_HEADERS) + @$(NORMAL_INSTALL) + @list='$(nobase_include_HEADERS)'; test -n "$(includedir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(includedir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(includedir)" || exit 1; \ + fi; \ + $(am__nobase_list) | while read dir files; do \ + xfiles=; for file in $$files; do \ + if test -f "$$file"; then xfiles="$$xfiles $$file"; \ + else xfiles="$$xfiles $(srcdir)/$$file"; fi; done; \ + test -z "$$xfiles" || { \ + test "x$$dir" = x. || { \ + echo " $(MKDIR_P) '$(DESTDIR)$(includedir)/$$dir'"; \ + $(MKDIR_P) "$(DESTDIR)$(includedir)/$$dir"; }; \ + echo " $(INSTALL_HEADER) $$xfiles '$(DESTDIR)$(includedir)/$$dir'"; \ + $(INSTALL_HEADER) $$xfiles "$(DESTDIR)$(includedir)/$$dir" || exit $$?; }; \ + done + +uninstall-nobase_includeHEADERS: + @$(NORMAL_UNINSTALL) + @list='$(nobase_include_HEADERS)'; test -n "$(includedir)" || list=; \ + $(am__nobase_strip_setup); files=`$(am__nobase_strip)`; \ + dir='$(DESTDIR)$(includedir)'; $(am__uninstall_files_from_dir) + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-am +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + $(am__define_uniq_tagged_files); \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: ctags-am + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" +cscopelist: cscopelist-am + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(HEADERS) +installdirs: + for dir in "$(DESTDIR)$(includedir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic clean-libtool mostlyclean-am + +distclean: distclean-am + -rm -f Makefile +distclean-am: clean-am distclean-generic distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: install-nobase_includeHEADERS + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-generic mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-nobase_includeHEADERS + +.MAKE: install-am install-strip + +.PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ + clean-libtool cscopelist-am ctags ctags-am distclean \ + distclean-generic distclean-libtool distclean-tags distdir dvi \ + dvi-am html html-am info info-am install install-am \ + install-data install-data-am install-dvi install-dvi-am \ + install-exec install-exec-am install-html install-html-am \ + install-info install-info-am install-man \ + install-nobase_includeHEADERS install-pdf install-pdf-am \ + install-ps install-ps-am install-strip installcheck \ + installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-generic \ + mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \ + uninstall-am uninstall-nobase_includeHEADERS + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/src/includes/nghttp2/asio_http2.h b/src/includes/nghttp2/asio_http2.h new file mode 100644 index 0000000..a54a9e8 --- /dev/null +++ b/src/includes/nghttp2/asio_http2.h @@ -0,0 +1,139 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2014 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef ASIO_HTTP2_H +#define ASIO_HTTP2_H + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +namespace boost { +namespace system { + +template <> struct is_error_code_enum { + BOOST_STATIC_CONSTANT(bool, value = true); +}; + +} // namespace system +} // namespace boost + +namespace nghttp2 { + +namespace asio_http2 { + +struct header_value { + // header field value + std::string value; + // true if the header field value is sensitive information, such as + // authorization information or short length secret cookies. If + // true, those header fields are not indexed by HPACK (but still + // huffman-encoded), which results in lesser compression. + bool sensitive; +}; + +// header fields. The header field name must be lower-cased. +using header_map = std::multimap; + +const boost::system::error_category &nghttp2_category() noexcept; + +struct uri_ref { + std::string scheme; + std::string host; + // form after percent-encoding decoded + std::string path; + // original path, percent-encoded + std::string raw_path; + // original query, percent-encoded + std::string raw_query; + std::string fragment; +}; + +// Callback function when data is arrived. EOF is indicated by +// passing 0 to the second parameter. +typedef std::function data_cb; +typedef std::function void_cb; +typedef std::function error_cb; +// Callback function when request and response are finished. The +// parameter indicates the cause of closure. +typedef std::function close_cb; + +// Callback function to generate response body. This function has the +// same semantics with nghttp2_data_source_read_callback. Just source +// and user_data parameters are removed. +// +// Basically, write at most |len| bytes to |data| and returns the +// number of bytes written. If there is no data left to send, set +// NGHTTP2_DATA_FLAG_EOF to *data_flags (e.g., *data_flags |= +// NGHTTP2_DATA_FLAG_EOF). If there is still data to send but they +// are not available right now, return NGHTTP2_ERR_DEFERRED. In case +// of the error and request/response must be closed, return +// NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE. +typedef std::function< + ssize_t(uint8_t *buf, std::size_t len, uint32_t *data_flags)> generator_cb; + +// Convenient function to create function to read file denoted by +// |path|. This can be passed to response::end(). +generator_cb file_generator(const std::string &path); + +// Like file_generator(const std::string&), but it takes opened file +// descriptor. The passed descriptor will be closed when returned +// function object is destroyed. +generator_cb file_generator_from_fd(int fd); + +// Validates path so that it does not contain directory traversal +// vector. Returns true if path is safe. The |path| must start with +// "/" otherwise returns false. This function should be called after +// percent-decode was performed. +bool check_path(const std::string &path); + +// Performs percent-decode against string |s|. +std::string percent_decode(const std::string &s); + +// Returns HTTP date representation of current posix time |t|. +std::string http_date(int64_t t); + +// Parses |uri| and extract scheme, host and service. The service is +// port component of URI (e.g., "8443") if available, otherwise it is +// scheme (e.g., "https"). +boost::system::error_code host_service_from_uri(boost::system::error_code &ec, + std::string &scheme, + std::string &host, + std::string &service, + const std::string &uri); + +} // namespace asio_http2 + +} // namespace nghttp2 + +#endif // ASIO_HTTP2_H diff --git a/src/includes/nghttp2/asio_http2_client.h b/src/includes/nghttp2/asio_http2_client.h new file mode 100644 index 0000000..a2b51ad --- /dev/null +++ b/src/includes/nghttp2/asio_http2_client.h @@ -0,0 +1,195 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2015 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef ASIO_HTTP2_CLIENT_H +#define ASIO_HTTP2_CLIENT_H + +#include + +namespace nghttp2 { + +namespace asio_http2 { + +namespace client { + +class response_impl; + +class response { +public: + // Application must not call this directly. + response(); + ~response(); + + // Sets callback which is invoked when chunk of response body is + // received. + void on_data(data_cb cb) const; + + // Returns status code. + int status_code() const; + + // Returns content-length. -1 if it is unknown. + int64_t content_length() const; + + // Returns the response header fields. The pusedo header fields, + // which start with colon (:), are exluced from this list. + const header_map &header() const; + + // Application must not call this directly. + response_impl &impl() const; + +private: + std::unique_ptr impl_; +}; + +class request; + +using response_cb = std::function; +using request_cb = std::function; +using connect_cb = + std::function; + +class request_impl; + +class request { +public: + // Application must not call this directly. + request(); + ~request(); + + // Sets callback which is invoked when response header is received. + void on_response(response_cb cb) const; + + // Sets callback which is invoked when push request header is + // received. + void on_push(request_cb cb) const; + + // Sets callback which is invoked when this request and response are + // finished. After the invocation of this callback, the application + // must not access request and response object. + void on_close(close_cb cb) const; + + // Write trailer part. This must be called after setting both + // NGHTTP2_DATA_FLAG_EOF and NGHTTP2_DATA_FLAG_NO_END_STREAM set in + // *data_flag parameter in generator_cb passed to session::submit() + // function. + void write_trailer(header_map h) const; + + // Cancels this request and response with given error code. + void cancel(uint32_t error_code = NGHTTP2_INTERNAL_ERROR) const; + + // Resumes deferred uploading. + void resume() const; + + // Returns method (e.g., GET). + const std::string &method() const; + + // Returns request URI, split into components. + const uri_ref &uri() const; + + // Returns request header fields. The pusedo header fields, which + // start with colon (:), are exluced from this list. + const header_map &header() const; + + // Application must not call this directly. + request_impl &impl() const; + +private: + std::unique_ptr impl_; +}; + +class session_impl; + +class session { +public: + // Starts HTTP/2 session by connecting to |host| and |service| + // (e.g., "80") using clear text TCP connection. + session(boost::asio::io_service &io_service, const std::string &host, + const std::string &service); + + // Starts HTTP/2 session by connecting to |host| and |service| + // (e.g., "443") using encrypted SSL/TLS connection. + session(boost::asio::io_service &io_service, + boost::asio::ssl::context &tls_context, const std::string &host, + const std::string &service); + ~session(); + + session(session &&other) noexcept; + session &operator=(session &&other) noexcept; + + // Sets callback which is invoked after connection is established. + void on_connect(connect_cb cb) const; + + // Sets callback which is invoked there is connection level error + // and session is terminated. + void on_error(error_cb cb) const; + + // Shutdowns connection. + void shutdown() const; + + // Returns underlying io_service object. + boost::asio::io_service &io_service() const; + + // Submits request to server using |method| (e.g., "GET"), |uri| + // (e.g., "http://localhost/") and optionally additional header + // fields. This function returns pointer to request object if it + // succeeds, or nullptr and |ec| contains error message. + const request *submit(boost::system::error_code &ec, + const std::string &method, const std::string &uri, + header_map h = header_map{}) const; + + // Submits request to server using |method| (e.g., "GET"), |uri| + // (e.g., "http://localhost/") and optionally additional header + // fields. The |data| is request body. This function returns + // pointer to request object if it succeeds, or nullptr and |ec| + // contains error message. + const request *submit(boost::system::error_code &ec, + const std::string &method, const std::string &uri, + std::string data, header_map h = header_map{}) const; + + // Submits request to server using |method| (e.g., "GET"), |uri| + // (e.g., "http://localhost/") and optionally additional header + // fields. The |cb| is used to generate request body. This + // function returns pointer to request object if it succeeds, or + // nullptr and |ec| contains error message. + const request *submit(boost::system::error_code &ec, + const std::string &method, const std::string &uri, + generator_cb cb, header_map h = header_map{}) const; + +private: + std::unique_ptr impl_; +}; + +// configure |tls_ctx| for client use. Currently, we just set NPN +// callback for HTTP/2. +boost::system::error_code +configure_tls_context(boost::system::error_code &ec, + boost::asio::ssl::context &tls_ctx); + +} // namespace client + +} // namespace asio_http2 + +} // namespace nghttp2 + +#endif // ASIO_HTTP2_CLIENT_H diff --git a/src/includes/nghttp2/asio_http2_server.h b/src/includes/nghttp2/asio_http2_server.h new file mode 100644 index 0000000..1c5a71f --- /dev/null +++ b/src/includes/nghttp2/asio_http2_server.h @@ -0,0 +1,229 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2015 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef ASIO_HTTP2_SERVER_H +#define ASIO_HTTP2_SERVER_H + +#include + +namespace nghttp2 { + +namespace asio_http2 { + +namespace server { + +class request_impl; +class response_impl; + +class request { +public: + // Application must not call this directly. + request(); + ~request(); + + // Returns request header fields. The pusedo header fields, which + // start with colon (:), are exluced from this list. + const header_map &header() const; + + // Returns method (e.g., GET). + const std::string &method() const; + + // Returns request URI, split into components. + const uri_ref &uri() const; + + // Sets callback which is invoked when chunk of request body is + // received. + void on_data(data_cb cb) const; + + // Application must not call this directly. + request_impl &impl() const; + +private: + std::unique_ptr impl_; +}; + +class response { +public: + // Application must not call this directly. + response(); + ~response(); + + // Write response header using |status_code| (e.g., 200) and + // additional header fields in |h|. + void write_head(unsigned int status_code, header_map h = header_map{}) const; + + // Sends |data| as request body. No further call of end() is + // allowed. + void end(std::string data = "") const; + + // Sets callback as a generator of the response body. No further + // call of end() is allowed. + void end(generator_cb cb) const; + + // Write trailer part. This must be called after setting both + // NGHTTP2_DATA_FLAG_EOF and NGHTTP2_DATA_FLAG_NO_END_STREAM set in + // *data_flag parameter in generator_cb passed to end() function. + void write_trailer(header_map h) const; + + // Sets callback which is invoked when this request and response are + // finished. After the invocation of this callback, the application + // must not access request and response object. + void on_close(close_cb cb) const; + + // Cancels this request and response with given error code. + void cancel(uint32_t error_code = NGHTTP2_INTERNAL_ERROR) const; + + // Resumes deferred response. + void resume() const; + + // Pushes resource denoted by |raw_path_query| using |method|. The + // additional header fields can be given in |h|. This function + // returns pointer to response object for promised stream, otherwise + // nullptr and error code is filled in |ec|. Be aware that the + // header field name given in |h| must be lower-cased. + const response *push(boost::system::error_code &ec, std::string method, + std::string raw_path_query, + header_map h = header_map{}) const; + + // Returns status code. + unsigned int status_code() const; + + // Returns boost::asio::io_service this response is running on. + boost::asio::io_service &io_service() const; + + // Application must not call this directly. + response_impl &impl() const; + +private: + std::unique_ptr impl_; +}; + +// This is so called request callback. Called every time request is +// received. The life time of |request| and |response| objects end +// when callback set by response::on_close() is called. After that, +// the application must not access to those objects. +typedef std::function request_cb; + +class http2_impl; + +class http2 { +public: + http2(); + ~http2(); + + http2(http2 &&other) noexcept; + http2 &operator=(http2 &&other) noexcept; + + // Starts listening connection on given address and port and serves + // incoming requests in cleartext TCP connection. If |asynchronous| + // is false, this function blocks forever unless there is an error. + // If it is true, after server has started, this function returns + // immediately, and the caller should call join() to shutdown server + // gracefully. + boost::system::error_code listen_and_serve(boost::system::error_code &ec, + const std::string &address, + const std::string &port, + bool asynchronous = false); + + // Starts listening connection on given address and port and serves + // incoming requests in SSL/TLS encrypted connection. For + // |asynchronous| parameter, see cleartext version + // |listen_and_serve|. + boost::system::error_code + listen_and_serve(boost::system::error_code &ec, + boost::asio::ssl::context &tls_context, + const std::string &address, const std::string &port, + bool asynchronous = false); + + // Registers request handler |cb| with path pattern |pattern|. This + // function will fail and returns false if same pattern has been + // already registered or |pattern| is empty string. Otherwise + // returns true. The pattern match rule is the same as + // net/http/ServeMux in golang. Quoted from golang manual + // (http://golang.org/pkg/net/http/#ServeMux): + // + // Patterns name fixed, rooted paths, like "/favicon.ico", or + // rooted subtrees, like "/images/" (note the trailing + // slash). Longer patterns take precedence over shorter ones, so + // that if there are handlers registered for both "/images/" and + // "/images/thumbnails/", the latter handler will be called for + // paths beginning "/images/thumbnails/" and the former will + // receive requests for any other paths in the "/images/" subtree. + // + // Note that since a pattern ending in a slash names a rooted + // subtree, the pattern "/" matches all paths not matched by other + // registered patterns, not just the URL with Path == "/". + // + // Patterns may optionally begin with a host name, restricting + // matches to URLs on that host only. Host-specific patterns take + // precedence over general patterns, so that a handler might + // register for the two patterns "/codesearch" and + // "codesearch.google.com/" without also taking over requests for + // "http://www.google.com/". + // + // Just like ServeMux in golang, URL request path is sanitized and + // if they contains . or .. elements, they are redirected to an + // equivalent .- and ..-free URL. + bool handle(std::string pattern, request_cb cb); + + // Sets number of native threads to handle incoming HTTP request. + // It defaults to 1. + void num_threads(size_t num_threads); + + // Sets the maximum length to which the queue of pending + // connections. + void backlog(int backlog); + + // Gracefully stop http2 server + void stop(); + + // Join on http2 server and wait for it to fully stop + void join(); + +private: + std::unique_ptr impl_; +}; + +// Configures |tls_context| for server use. This function sets couple +// of OpenSSL options (disables SSLv2 and SSLv3 and compression) and +// enables ECDHE ciphers. NPN callback is also configured. +boost::system::error_code +configure_tls_context_easy(boost::system::error_code &ec, + boost::asio::ssl::context &tls_context); + +// Returns request handler to do redirect to |uri| using +// |status_code|. The |uri| appears in "location" header field as is. +request_cb redirect_handler(int status_code, std::string uri); + +// Returns request handler to reply with given |status_code| and HTML +// including message about status code. +request_cb status_handler(int status_code); + +} // namespace server + +} // namespace asio_http2 + +} // namespace nghttp2 + +#endif // ASIO_HTTP2_SERVER_H diff --git a/src/inflatehd.cc b/src/inflatehd.cc new file mode 100644 index 0000000..98338be --- /dev/null +++ b/src/inflatehd.cc @@ -0,0 +1,277 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2013 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifdef HAVE_CONFIG_H +#include +#endif // HAVE_CONFIG_H + +#ifdef HAVE_UNISTD_H +#include +#endif // HAVE_UNISTD_H +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +extern "C" { + +#include "nghttp2_hd.h" +#include "nghttp2_frame.h" + +#include "comp_helper.h" +} + +typedef struct { int dump_header_table; } inflate_config; + +static inflate_config config; + +static uint8_t to_ud(char c) { + if (c >= 'A' && c <= 'Z') { + return c - 'A' + 10; + } else if (c >= 'a' && c <= 'z') { + return c - 'a' + 10; + } else { + return c - '0'; + } +} + +static void decode_hex(uint8_t *dest, const char *src, size_t len) { + size_t i; + for (i = 0; i < len; i += 2) { + *dest++ = to_ud(src[i]) << 4 | to_ud(src[i + 1]); + } +} + +static void to_json(nghttp2_hd_inflater *inflater, json_t *headers, + json_t *wire, int seq, size_t old_settings_table_size) { + auto obj = json_object(); + json_object_set_new(obj, "seq", json_integer(seq)); + json_object_set(obj, "wire", wire); + json_object_set(obj, "headers", headers); + if (old_settings_table_size != inflater->settings_hd_table_bufsize_max) { + json_object_set_new(obj, "header_table_size", + json_integer(inflater->settings_hd_table_bufsize_max)); + } + if (config.dump_header_table) { + json_object_set_new(obj, "header_table", dump_header_table(&inflater->ctx)); + } + json_dumpf(obj, stdout, JSON_INDENT(2) | JSON_PRESERVE_ORDER); + json_decref(obj); + printf("\n"); +} + +static int inflate_hd(json_t *obj, nghttp2_hd_inflater *inflater, int seq) { + ssize_t rv; + nghttp2_nv nv; + int inflate_flags; + size_t old_settings_table_size = inflater->settings_hd_table_bufsize_max; + + auto wire = json_object_get(obj, "wire"); + + if (wire == nullptr) { + fprintf(stderr, "'wire' key is missing at %d\n", seq); + return -1; + } + + if (!json_is_string(wire)) { + fprintf(stderr, "'wire' value is not string at %d\n", seq); + return -1; + } + + auto table_size = json_object_get(obj, "header_table_size"); + + if (table_size) { + if (!json_is_integer(table_size)) { + fprintf(stderr, + "The value of 'header_table_size key' is not integer at %d\n", + seq); + return -1; + } + rv = nghttp2_hd_inflate_change_table_size(inflater, + json_integer_value(table_size)); + if (rv != 0) { + fprintf(stderr, + "nghttp2_hd_change_table_size() failed with error %s at %d\n", + nghttp2_strerror(rv), seq); + return -1; + } + } + + auto inputlen = strlen(json_string_value(wire)); + + if (inputlen & 1) { + fprintf(stderr, "Badly formatted output value at %d\n", seq); + exit(EXIT_FAILURE); + } + + auto buflen = inputlen / 2; + auto buf = std::vector(buflen); + + decode_hex(buf.data(), json_string_value(wire), inputlen); + + auto headers = json_array(); + + auto p = buf.data(); + for (;;) { + inflate_flags = 0; + rv = nghttp2_hd_inflate_hd(inflater, &nv, &inflate_flags, p, buflen, 1); + if (rv < 0) { + fprintf(stderr, "inflate failed with error code %zd at %d\n", rv, seq); + exit(EXIT_FAILURE); + } + p += rv; + buflen -= rv; + if (inflate_flags & NGHTTP2_HD_INFLATE_EMIT) { + json_array_append_new( + headers, dump_header(nv.name, nv.namelen, nv.value, nv.valuelen)); + } + if (inflate_flags & NGHTTP2_HD_INFLATE_FINAL) { + break; + } + } + assert(buflen == 0); + nghttp2_hd_inflate_end_headers(inflater); + to_json(inflater, headers, wire, seq, old_settings_table_size); + json_decref(headers); + + return 0; +} + +static int perform(void) { + nghttp2_hd_inflater *inflater = NULL; + json_error_t error; + + auto json = json_loadf(stdin, 0, &error); + + if (json == nullptr) { + fprintf(stderr, "JSON loading failed\n"); + exit(EXIT_FAILURE); + } + + auto cases = json_object_get(json, "cases"); + + if (cases == nullptr) { + fprintf(stderr, "Missing 'cases' key in root object\n"); + exit(EXIT_FAILURE); + } + + if (!json_is_array(cases)) { + fprintf(stderr, "'cases' must be JSON array\n"); + exit(EXIT_FAILURE); + } + + nghttp2_hd_inflate_new(&inflater); + output_json_header(); + auto len = json_array_size(cases); + + for (size_t i = 0; i < len; ++i) { + auto obj = json_array_get(cases, i); + if (!json_is_object(obj)) { + fprintf(stderr, "Unexpected JSON type at %zu. It should be object.\n", i); + continue; + } + if (inflate_hd(obj, inflater, i) != 0) { + continue; + } + if (i + 1 < len) { + printf(",\n"); + } + } + output_json_footer(); + nghttp2_hd_inflate_del(inflater); + json_decref(json); + + return 0; +} + +static void print_help(void) { + std::cout << R"(HPACK HTTP/2 header decoder +Usage: inflatehd [OPTIONS] < INPUT + +Reads JSON data from stdin and outputs inflated name/value pairs in +JSON. + +The root JSON object must contain "context" key, which indicates which +compression context is used. If it is "request", request compression +context is used. Otherwise, response compression context is used. +The value of "cases" key contains the sequence of compressed header +block. They share the same compression context and are processed in +the order they appear. Each item in the sequence is a JSON object and +it must have at least "wire" key. Its value is a string containing +compressed header block in hex string. + +Example: + +{ + "context": "request", + "cases": + [ + { "wire": "0284f77778ff" }, + { "wire": "0185fafd3c3c7f81" } + ] +} + +The output of this program can be used as input for deflatehd. + +OPTIONS: + -d, --dump-header-table + Output dynamic header table.)" << std::endl; + ; +} + +static struct option long_options[] = { + {"dump-header-table", no_argument, nullptr, 'd'}, {nullptr, 0, nullptr, 0}}; + +int main(int argc, char **argv) { + config.dump_header_table = 0; + while (1) { + int option_index = 0; + int c = getopt_long(argc, argv, "dh", long_options, &option_index); + if (c == -1) { + break; + } + switch (c) { + case 'h': + print_help(); + exit(EXIT_SUCCESS); + case 'd': + // --dump-header-table + config.dump_header_table = 1; + break; + case '?': + exit(EXIT_FAILURE); + default: + break; + } + } + perform(); + return 0; +} diff --git a/src/libnghttp2_asio.pc.in b/src/libnghttp2_asio.pc.in new file mode 100644 index 0000000..ec4fbbb --- /dev/null +++ b/src/libnghttp2_asio.pc.in @@ -0,0 +1,33 @@ +# nghttp2 - HTTP/2 C Library + +# Copyright (c) 2014 Tatsuhiro Tsujikawa + +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: + +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. + +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: libnghttp2_asio +Description: HTTP/2 C++ library +URL: https://github.com/tatsuhiro-t/nghttp2 +Version: @VERSION@ +Libs: -L${libdir} -lnghttp2_asio +Cflags: -I${includedir} diff --git a/src/memchunk.h b/src/memchunk.h new file mode 100644 index 0000000..0f12c15 --- /dev/null +++ b/src/memchunk.h @@ -0,0 +1,260 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2014 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef MEMCHUNK_H +#define MEMCHUNK_H + +#include "nghttp2_config.h" + +#include + +#include +#include +#include +#include +#include + +#include "template.h" + +namespace nghttp2 { + +template struct Memchunk { + Memchunk(std::unique_ptr next_chunk) + : pos(std::begin(buf)), last(pos), knext(std::move(next_chunk)), + kprev(nullptr), next(nullptr) { + if (knext) { + knext->kprev = this; + } + } + size_t len() const { return last - pos; } + size_t left() const { return std::end(buf) - last; } + void reset() { pos = last = std::begin(buf); } + std::array buf; + uint8_t *pos, *last; + std::unique_ptr knext; + Memchunk *kprev; + Memchunk *next; + static const size_t size = N; +}; + +template struct Pool { + Pool() : pool(nullptr), freelist(nullptr), poolsize(0) {} + T *get() { + if (freelist) { + auto m = freelist; + freelist = freelist->next; + m->next = nullptr; + m->reset(); + return m; + } + + pool = make_unique(std::move(pool)); + poolsize += T::size; + return pool.get(); + } + void recycle(T *m) { + if (freelist) { + m->next = freelist; + } else { + m->next = nullptr; + } + freelist = m; + } + void shrink(size_t max) { + auto m = freelist; + for (; m && poolsize > max;) { + auto next = m->next; + poolsize -= T::size; + auto p = m->kprev; + if (p) { + p->knext = std::move(m->knext); + if (p->knext) { + p->knext->kprev = p; + } + } else { + pool = std::move(m->knext); + if (pool) { + pool->kprev = nullptr; + } + } + m = next; + } + freelist = m; + } + void clear() { + freelist = nullptr; + pool = nullptr; + poolsize = 0; + } + using value_type = T; + std::unique_ptr pool; + T *freelist; + size_t poolsize; +}; + +template struct Memchunks { + Memchunks(Pool *pool) + : pool(pool), head(nullptr), tail(nullptr), len(0) {} + ~Memchunks() { + if (!pool) { + return; + } + for (auto m = head; m;) { + auto next = m->next; + pool->recycle(m); + m = next; + } + } + size_t append(const void *src, size_t count) { + if (count == 0) { + return 0; + } + + auto first = static_cast(src); + auto last = first + count; + + if (!tail) { + head = tail = pool->get(); + } + + for (;;) { + auto n = std::min(static_cast(last - first), tail->left()); + tail->last = std::copy_n(first, n, tail->last); + first += n; + len += n; + if (first == last) { + break; + } + + tail->next = pool->get(); + tail = tail->next; + } + + return count; + } + template size_t append(const char (&s)[N]) { + return append(s, N - 1); + } + size_t remove(void *dest, size_t count) { + if (!tail || count == 0) { + return 0; + } + + auto first = static_cast(dest); + auto last = first + count; + + auto m = head; + + while (m) { + auto next = m->next; + auto n = std::min(static_cast(last - first), m->len()); + + assert(m->len()); + first = std::copy_n(m->pos, n, first); + m->pos += n; + len -= n; + if (m->len() > 0) { + break; + } + pool->recycle(m); + m = next; + } + head = m; + if (head == nullptr) { + tail = nullptr; + } + + return first - static_cast(dest); + } + size_t drain(size_t count) { + auto ndata = count; + auto m = head; + while (m) { + auto next = m->next; + auto n = std::min(count, m->len()); + m->pos += n; + count -= n; + len -= n; + if (m->len() > 0) { + break; + } + + pool->recycle(m); + m = next; + } + head = m; + if (head == nullptr) { + tail = nullptr; + } + return ndata - count; + } + int riovec(struct iovec *iov, int iovcnt) { + if (!head) { + return 0; + } + auto m = head; + int i; + for (i = 0; i < iovcnt && m; ++i, m = m->next) { + iov[i].iov_base = m->pos; + iov[i].iov_len = m->len(); + } + return i; + } + size_t rleft() const { return len; } + + Pool *pool; + Memchunk *head, *tail; + size_t len; +}; + +using Memchunk16K = Memchunk<16384>; +using MemchunkPool = Pool; +using DefaultMemchunks = Memchunks; + +#define DEFAULT_WR_IOVCNT 16 + +#if defined(IOV_MAX) && IOV_MAX < DEFAULT_WR_IOVCNT +#define MAX_WR_IOVCNT IOV_MAX +#else // !defined(IOV_MAX) || IOV_MAX >= DEFAULT_WR_IOVCNT +#define MAX_WR_IOVCNT DEFAULT_WR_IOVCNT +#endif // !defined(IOV_MAX) || IOV_MAX >= DEFAULT_WR_IOVCNT + +inline int limit_iovec(struct iovec *iov, int iovcnt, size_t max) { + if (max == 0) { + return 0; + } + for (int i = 0; i < iovcnt; ++i) { + auto d = std::min(max, iov[i].iov_len); + iov[i].iov_len = d; + max -= d; + if (max == 0) { + return i + 1; + } + } + return iovcnt; +} + +} // namespace nghttp2 + +#endif // MEMCHUNK_H diff --git a/src/memchunk_test.cc b/src/memchunk_test.cc new file mode 100644 index 0000000..f95f303 --- /dev/null +++ b/src/memchunk_test.cc @@ -0,0 +1,199 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2015 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "memchunk_test.h" + +#include + +#include + +#include "memchunk.h" +#include "util.h" + +namespace nghttp2 { + +void test_pool_recycle(void) { + MemchunkPool pool; + + CU_ASSERT(!pool.pool); + CU_ASSERT(0 == pool.poolsize); + CU_ASSERT(nullptr == pool.freelist); + + auto m1 = pool.get(); + + CU_ASSERT(m1 == pool.pool.get()); + CU_ASSERT(MemchunkPool::value_type::size == pool.poolsize); + CU_ASSERT(nullptr == pool.freelist); + + auto m2 = pool.get(); + + CU_ASSERT(m2 == pool.pool.get()); + CU_ASSERT(2 * MemchunkPool::value_type::size == pool.poolsize); + CU_ASSERT(nullptr == pool.freelist); + CU_ASSERT(m1 == m2->knext.get()); + CU_ASSERT(nullptr == m1->knext.get()); + + auto m3 = pool.get(); + + CU_ASSERT(m3 == pool.pool.get()); + CU_ASSERT(3 * MemchunkPool::value_type::size == pool.poolsize); + CU_ASSERT(nullptr == pool.freelist); + + pool.recycle(m3); + + CU_ASSERT(m3 == pool.pool.get()); + CU_ASSERT(3 * MemchunkPool::value_type::size == pool.poolsize); + CU_ASSERT(m3 == pool.freelist); + + auto m4 = pool.get(); + + CU_ASSERT(m3 == m4); + CU_ASSERT(m4 == pool.pool.get()); + CU_ASSERT(3 * MemchunkPool::value_type::size == pool.poolsize); + CU_ASSERT(nullptr == pool.freelist); + + pool.recycle(m2); + pool.recycle(m1); + + CU_ASSERT(m1 == pool.freelist); + CU_ASSERT(m2 == m1->next); + CU_ASSERT(nullptr == m2->next); +} + +using Memchunk16 = Memchunk<16>; +using MemchunkPool16 = Pool; +using Memchunks16 = Memchunks; + +void test_memchunks_append(void) { + MemchunkPool16 pool; + Memchunks16 chunks(&pool); + + chunks.append("012"); + + auto m = chunks.tail; + + CU_ASSERT(3 == m->len()); + CU_ASSERT(13 == m->left()); + + chunks.append("3456789abcdef@"); + + CU_ASSERT(16 == m->len()); + CU_ASSERT(0 == m->left()); + + m = chunks.tail; + + CU_ASSERT(1 == m->len()); + CU_ASSERT(15 == m->left()); + CU_ASSERT(17 == chunks.rleft()); + + char buf[16]; + size_t nread; + + nread = chunks.remove(buf, 8); + + CU_ASSERT(8 == nread); + CU_ASSERT(0 == memcmp("01234567", buf, nread)); + CU_ASSERT(9 == chunks.rleft()); + + nread = chunks.remove(buf, sizeof(buf)); + + CU_ASSERT(9 == nread); + CU_ASSERT(0 == memcmp("89abcdef@", buf, nread)); + CU_ASSERT(0 == chunks.rleft()); + CU_ASSERT(nullptr == chunks.head); + CU_ASSERT(nullptr == chunks.tail); + CU_ASSERT(32 == pool.poolsize); +} + +void test_memchunks_drain(void) { + MemchunkPool16 pool; + Memchunks16 chunks(&pool); + + chunks.append("0123456789"); + + size_t nread; + + nread = chunks.drain(3); + + CU_ASSERT(3 == nread); + + char buf[16]; + + nread = chunks.remove(buf, sizeof(buf)); + + CU_ASSERT(7 == nread); + CU_ASSERT(0 == memcmp("3456789", buf, nread)); +} + +void test_memchunks_riovec(void) { + MemchunkPool16 pool; + Memchunks16 chunks(&pool); + + char buf[3 * 16]; + + chunks.append(buf, sizeof(buf)); + + std::array iov; + auto iovcnt = chunks.riovec(iov.data(), iov.size()); + + auto m = chunks.head; + + CU_ASSERT(2 == iovcnt); + CU_ASSERT(m->buf.data() == iov[0].iov_base); + CU_ASSERT(m->len() == iov[0].iov_len); + + m = m->next; + + CU_ASSERT(m->buf.data() == iov[1].iov_base); + CU_ASSERT(m->len() == iov[1].iov_len); + + chunks.drain(2 * 16); + + iovcnt = chunks.riovec(iov.data(), iov.size()); + + CU_ASSERT(1 == iovcnt); + + m = chunks.head; + CU_ASSERT(m->buf.data() == iov[0].iov_base); + CU_ASSERT(m->len() == iov[0].iov_len); +} + +void test_memchunks_recycle(void) { + MemchunkPool16 pool; + { + Memchunks16 chunks(&pool); + char buf[32]; + chunks.append(buf, sizeof(buf)); + } + CU_ASSERT(32 == pool.poolsize); + CU_ASSERT(nullptr != pool.freelist); + + auto m = pool.freelist; + m = m->next; + + CU_ASSERT(nullptr != m); + CU_ASSERT(nullptr == m->next); +} + +} // namespace nghttp2 diff --git a/src/memchunk_test.h b/src/memchunk_test.h new file mode 100644 index 0000000..9d170a4 --- /dev/null +++ b/src/memchunk_test.h @@ -0,0 +1,42 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2015 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef MEMCHUNK_TEST_H +#define MEMCHUNK_TEST_H + +#ifdef HAVE_CONFIG_H +#include +#endif // HAVE_CONFIG_H + +namespace nghttp2 { + +void test_pool_recycle(void); +void test_memchunks_append(void); +void test_memchunks_drain(void); +void test_memchunks_riovec(void); +void test_memchunks_recycle(void); + +} // namespace nghttp2 + +#endif // MEMCHUNK_TEST_H diff --git a/src/nghttp.cc b/src/nghttp.cc new file mode 100644 index 0000000..8f6361e --- /dev/null +++ b/src/nghttp.cc @@ -0,0 +1,2652 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2013 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "nghttp.h" + +#include +#ifdef HAVE_UNISTD_H +#include +#endif // HAVE_UNISTD_H +#ifdef HAVE_FCNTL_H +#include +#endif // HAVE_FCNTL_H +#ifdef HAVE_NETINET_IN_H +#include +#endif // HAVE_NETINET_IN_H +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#ifdef HAVE_JANSSON +#include +#endif // HAVE_JANSSON + +#include "app_helper.h" +#include "HtmlParser.h" +#include "util.h" +#include "base64.h" +#include "ssl.h" +#include "template.h" + +#ifndef O_BINARY +#define O_BINARY (0) +#endif // O_BINARY + +namespace nghttp2 { + +// The anchor stream nodes when --no-dep is not used. The stream ID = +// 1 is excluded since it is used as first stream in upgrade case. We +// follows the same dependency anchor nodes as Firefox does. +struct Anchor { + int32_t stream_id; + // stream ID this anchor depends on + int32_t dep_stream_id; + // .. with this weight. + int32_t weight; +}; + +// This is index into anchors. Firefox uses ANCHOR_FOLLOWERS for html +// file. +enum { + ANCHOR_LEADERS, + ANCHOR_UNBLOCKED, + ANCHOR_BACKGROUND, + ANCHOR_SPECULATIVE, + ANCHOR_FOLLOWERS, +}; + +namespace { +auto anchors = std::array{{ + {3, 0, 201}, {5, 0, 101}, {7, 0, 1}, {9, 7, 1}, {11, 3, 1}, +}}; +} // namespace + +Config::Config() + : output_upper_thres(1024 * 1024), padding(0), + peer_max_concurrent_streams(NGHTTP2_INITIAL_MAX_CONCURRENT_STREAMS), + header_table_size(-1), weight(NGHTTP2_DEFAULT_WEIGHT), multiply(1), + timeout(0.), window_bits(-1), connection_window_bits(-1), verbose(0), + null_out(false), remote_name(false), get_assets(false), stat(false), + upgrade(false), continuation(false), no_content_length(false), + no_dep(false), hexdump(false), no_push(false) { + nghttp2_option_new(&http2_option); + nghttp2_option_set_peer_max_concurrent_streams(http2_option, + peer_max_concurrent_streams); +} + +Config::~Config() { nghttp2_option_del(http2_option); } + +namespace { +Config config; +} // namespace + +namespace { +void print_protocol_nego_error() { + std::cerr << "[ERROR] HTTP/2 protocol was not selected." + << " (nghttp2 expects " << NGHTTP2_PROTO_VERSION_ID << ")" + << std::endl; +} +} // namespace + +namespace { +std::string strip_fragment(const char *raw_uri) { + const char *end; + for (end = raw_uri; *end && *end != '#'; ++end) + ; + size_t len = end - raw_uri; + return std::string(raw_uri, len); +} +} // namespace + +Request::Request(const std::string &uri, const http_parser_url &u, + const nghttp2_data_provider *data_prd, int64_t data_length, + const nghttp2_priority_spec &pri_spec, int level) + : uri(uri), u(u), pri_spec(pri_spec), data_length(data_length), + data_offset(0), response_len(0), inflater(nullptr), html_parser(nullptr), + data_prd(data_prd), stream_id(-1), status(0), level(level), + expect_final_response(false) { + http2::init_hdidx(res_hdidx); + http2::init_hdidx(req_hdidx); +} + +Request::~Request() { + nghttp2_gzip_inflate_del(inflater); + delete html_parser; +} + +void Request::init_inflater() { + int rv; + rv = nghttp2_gzip_inflate_new(&inflater); + assert(rv == 0); +} + +void Request::init_html_parser() { html_parser = new HtmlParser(uri); } + +int Request::update_html_parser(const uint8_t *data, size_t len, int fin) { + if (!html_parser) { + return 0; + } + return html_parser->parse_chunk(reinterpret_cast(data), len, + fin); +} + +std::string Request::make_reqpath() const { + std::string path = util::has_uri_field(u, UF_PATH) + ? util::get_uri_field(uri.c_str(), u, UF_PATH) + : "/"; + if (util::has_uri_field(u, UF_QUERY)) { + path += "?"; + path.append(uri.c_str() + u.field_data[UF_QUERY].off, + u.field_data[UF_QUERY].len); + } + return path; +} + +namespace { +nghttp2_priority_spec resolve_dep(int res_type) { + nghttp2_priority_spec pri_spec; + + if (config.no_dep) { + nghttp2_priority_spec_default_init(&pri_spec); + + return pri_spec; + } + + int32_t anchor_id; + int32_t weight; + switch (res_type) { + case REQ_CSS: + case REQ_JS: + anchor_id = anchors[ANCHOR_LEADERS].stream_id; + weight = 2; + break; + case REQ_UNBLOCK_JS: + anchor_id = anchors[ANCHOR_UNBLOCKED].stream_id; + weight = 2; + break; + case REQ_IMG: + anchor_id = anchors[ANCHOR_FOLLOWERS].stream_id; + weight = 12; + break; + default: + anchor_id = anchors[ANCHOR_FOLLOWERS].stream_id; + weight = 2; + } + + nghttp2_priority_spec_init(&pri_spec, anchor_id, weight, 0); + return pri_spec; +} +} // namespace + +bool Request::is_ipv6_literal_addr() const { + if (util::has_uri_field(u, UF_HOST)) { + return memchr(uri.c_str() + u.field_data[UF_HOST].off, ':', + u.field_data[UF_HOST].len); + } else { + return false; + } +} + +bool Request::response_pseudo_header_allowed(int16_t token) const { + if (!res_nva.empty() && res_nva.back().name.c_str()[0] != ':') { + return false; + } + switch (token) { + case http2::HD__STATUS: + return res_hdidx[token] == -1; + default: + return false; + } +} + +bool Request::push_request_pseudo_header_allowed(int16_t token) const { + if (!req_nva.empty() && req_nva.back().name.c_str()[0] != ':') { + return false; + } + switch (token) { + case http2::HD__AUTHORITY: + case http2::HD__METHOD: + case http2::HD__PATH: + case http2::HD__SCHEME: + return req_hdidx[token] == -1; + default: + return false; + } +} + +Headers::value_type *Request::get_res_header(int16_t token) { + auto idx = res_hdidx[token]; + if (idx == -1) { + return nullptr; + } + return &res_nva[idx]; +} + +Headers::value_type *Request::get_req_header(int16_t token) { + auto idx = req_hdidx[token]; + if (idx == -1) { + return nullptr; + } + return &req_nva[idx]; +} + +void Request::record_request_start_time() { + timing.state = RequestState::ON_REQUEST; + timing.request_start_time = get_time(); +} + +void Request::record_response_start_time() { + timing.state = RequestState::ON_RESPONSE; + timing.response_start_time = get_time(); +} + +void Request::record_response_end_time() { + timing.state = RequestState::ON_COMPLETE; + timing.response_end_time = get_time(); +} + +namespace { +int htp_msg_begincb(http_parser *htp) { + if (config.verbose) { + print_timer(); + std::cout << " HTTP Upgrade response" << std::endl; + } + return 0; +} +} // namespace + +namespace { +int htp_statuscb(http_parser *htp, const char *at, size_t length) { + auto client = static_cast(htp->data); + client->upgrade_response_status_code = htp->status_code; + return 0; +} +} // namespace + +namespace { +int htp_msg_completecb(http_parser *htp) { + auto client = static_cast(htp->data); + client->upgrade_response_complete = true; + return 0; +} +} // namespace + +namespace { +http_parser_settings htp_hooks = { + htp_msg_begincb, // http_cb on_message_begin; + nullptr, // http_data_cb on_url; + htp_statuscb, // http_data_cb on_status; + nullptr, // http_data_cb on_header_field; + nullptr, // http_data_cb on_header_value; + nullptr, // http_cb on_headers_complete; + nullptr, // http_data_cb on_body; + htp_msg_completecb // http_cb on_message_complete; +}; +} // namespace + +namespace { +int submit_request(HttpClient *client, const Headers &headers, Request *req) { + auto path = req->make_reqpath(); + auto scheme = util::get_uri_field(req->uri.c_str(), req->u, UF_SCHEMA); + auto build_headers = Headers{{":method", req->data_prd ? "POST" : "GET"}, + {":path", path}, + {":scheme", scheme}, + {":authority", client->hostport}, + {"accept", "*/*"}, + {"accept-encoding", "gzip, deflate"}, + {"user-agent", "nghttp2/" NGHTTP2_VERSION}}; + if (config.continuation) { + for (size_t i = 0; i < 6; ++i) { + build_headers.emplace_back("continuation-test-" + util::utos(i + 1), + std::string(4096, '-')); + } + } + auto num_initial_headers = build_headers.size(); + if (!config.no_content_length && req->data_prd) { + build_headers.emplace_back("content-length", util::utos(req->data_length)); + } + for (auto &kv : headers) { + size_t i; + for (i = 0; i < num_initial_headers; ++i) { + if (kv.name == build_headers[i].name) { + build_headers[i].value = kv.value; + break; + } + } + if (i < num_initial_headers) { + continue; + } + + build_headers.emplace_back(kv.name, kv.value, kv.no_index); + } + + auto nva = std::vector(); + nva.reserve(build_headers.size()); + + for (auto &kv : build_headers) { + nva.push_back(http2::make_nv(kv.name, kv.value, kv.no_index)); + } + + std::string trailer_names; + if (!config.trailer.empty()) { + trailer_names = config.trailer[0].name; + for (size_t i = 1; i < config.trailer.size(); ++i) { + trailer_names += ", "; + trailer_names += config.trailer[i].name; + } + nva.push_back(http2::make_nv_ls("trailer", trailer_names)); + } + + auto stream_id = + nghttp2_submit_request(client->session, &req->pri_spec, nva.data(), + nva.size(), req->data_prd, req); + if (stream_id < 0) { + std::cerr << "[ERROR] nghttp2_submit_request() returned error: " + << nghttp2_strerror(stream_id) << std::endl; + return -1; + } + + req->stream_id = stream_id; + client->request_done(req); + + req->req_nva = std::move(build_headers); + + return 0; +} +} // namespace + +namespace { +void readcb(struct ev_loop *loop, ev_io *w, int revents) { + auto client = static_cast(w->data); + if (client->do_read() != 0) { + client->disconnect(); + } +} +} // namespace + +namespace { +void writecb(struct ev_loop *loop, ev_io *w, int revents) { + auto client = static_cast(w->data); + auto rv = client->do_write(); + if (rv == HttpClient::ERR_CONNECT_FAIL) { + client->connect_fail(); + return; + } + if (rv != 0) { + client->disconnect(); + } +} +} // namespace + +namespace { +void timeoutcb(struct ev_loop *loop, ev_timer *w, int revents) { + auto client = static_cast(w->data); + std::cerr << "[ERROR] Timeout" << std::endl; + client->disconnect(); +} +} // namespace + +namespace { +void settings_timeout_cb(struct ev_loop *loop, ev_timer *w, int revents) { + auto client = static_cast(w->data); + ev_timer_stop(loop, w); + + nghttp2_session_terminate_session(client->session, NGHTTP2_SETTINGS_TIMEOUT); + + client->signal_write(); +} +} // namespace + +HttpClient::HttpClient(const nghttp2_session_callbacks *callbacks, + struct ev_loop *loop, SSL_CTX *ssl_ctx) + : session(nullptr), callbacks(callbacks), loop(loop), ssl_ctx(ssl_ctx), + ssl(nullptr), addrs(nullptr), next_addr(nullptr), cur_addr(nullptr), + complete(0), success(0), settings_payloadlen(0), state(ClientState::IDLE), + upgrade_response_status_code(0), fd(-1), + upgrade_response_complete(false) { + ev_io_init(&wev, writecb, 0, EV_WRITE); + ev_io_init(&rev, readcb, 0, EV_READ); + + wev.data = this; + rev.data = this; + + ev_timer_init(&wt, timeoutcb, 0., config.timeout); + ev_timer_init(&rt, timeoutcb, 0., config.timeout); + + wt.data = this; + rt.data = this; + + ev_timer_init(&settings_timer, settings_timeout_cb, 0., 10.); + + settings_timer.data = this; +} + +HttpClient::~HttpClient() { + disconnect(); + + if (addrs) { + freeaddrinfo(addrs); + addrs = nullptr; + next_addr = nullptr; + } +} + +bool HttpClient::need_upgrade() const { + return config.upgrade && scheme == "http"; +} + +int HttpClient::resolve_host(const std::string &host, uint16_t port) { + int rv; + addrinfo hints; + this->host = host; + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = 0; + hints.ai_flags = AI_ADDRCONFIG; + rv = getaddrinfo(host.c_str(), util::utos(port).c_str(), &hints, &addrs); + if (rv != 0) { + std::cerr << "[ERROR] getaddrinfo() failed: " << gai_strerror(rv) + << std::endl; + return -1; + } + if (addrs == nullptr) { + std::cerr << "[ERROR] No address returned" << std::endl; + return -1; + } + next_addr = addrs; + return 0; +} + +int HttpClient::initiate_connection() { + int rv; + + cur_addr = nullptr; + while (next_addr) { + cur_addr = next_addr; + next_addr = next_addr->ai_next; + fd = util::create_nonblock_socket(cur_addr->ai_family); + if (fd == -1) { + continue; + } + + if (ssl_ctx) { + // We are establishing TLS connection. + ssl = SSL_new(ssl_ctx); + if (!ssl) { + std::cerr << "[ERROR] SSL_new() failed: " + << ERR_error_string(ERR_get_error(), nullptr) << std::endl; + return -1; + } + + SSL_set_fd(ssl, fd); + SSL_set_connect_state(ssl); + + // If the user overrode the :authority or host header, use that + // value for the SNI extension + const char *host_string = nullptr; + auto i = std::find_if(std::begin(config.headers), + std::end(config.headers), [](const Header &nv) { + return ":authority" == nv.name || "host" == nv.name; + }); + if (i != std::end(config.headers)) { + host_string = (*i).value.c_str(); + } else { + host_string = host.c_str(); + } + + if (!util::numeric_host(host_string)) { + SSL_set_tlsext_host_name(ssl, host_string); + } + } + + rv = connect(fd, cur_addr->ai_addr, cur_addr->ai_addrlen); + + if (rv != 0 && errno != EINPROGRESS) { + if (ssl) { + SSL_free(ssl); + ssl = nullptr; + } + close(fd); + fd = -1; + continue; + } + break; + } + + if (fd == -1) { + return -1; + } + + writefn = &HttpClient::connected; + + if (need_upgrade()) { + on_readfn = &HttpClient::on_upgrade_read; + on_writefn = &HttpClient::on_upgrade_connect; + } else { + on_readfn = &HttpClient::on_read; + on_writefn = &HttpClient::on_write; + } + + ev_io_set(&rev, fd, EV_READ); + ev_io_set(&wev, fd, EV_WRITE); + + ev_io_start(loop, &wev); + + ev_timer_again(loop, &wt); + + return 0; +} + +void HttpClient::disconnect() { + state = ClientState::IDLE; + + ev_timer_stop(loop, &settings_timer); + + ev_timer_stop(loop, &rt); + ev_timer_stop(loop, &wt); + + ev_io_stop(loop, &rev); + ev_io_stop(loop, &wev); + + nghttp2_session_del(session); + session = nullptr; + + if (ssl) { + SSL_set_shutdown(ssl, SSL_RECEIVED_SHUTDOWN); + ERR_clear_error(); + SSL_shutdown(ssl); + SSL_free(ssl); + ssl = nullptr; + } + + if (fd != -1) { + shutdown(fd, SHUT_WR); + close(fd); + fd = -1; + } +} + +int HttpClient::read_clear() { + ev_timer_again(loop, &rt); + + std::array buf; + + for (;;) { + ssize_t nread; + while ((nread = read(fd, buf.data(), buf.size())) == -1 && errno == EINTR) + ; + if (nread == -1) { + if (errno == EAGAIN || errno == EWOULDBLOCK) { + return 0; + } + return -1; + } + + if (nread == 0) { + return -1; + } + + if (on_readfn(*this, buf.data(), nread) != 0) { + return -1; + } + } + + return 0; +} + +int HttpClient::write_clear() { + ev_timer_again(loop, &rt); + + for (;;) { + if (wb.rleft() > 0) { + ssize_t nwrite; + while ((nwrite = write(fd, wb.pos, wb.rleft())) == -1 && errno == EINTR) + ; + if (nwrite == -1) { + if (errno == EAGAIN || errno == EWOULDBLOCK) { + ev_io_start(loop, &wev); + ev_timer_again(loop, &wt); + return 0; + } + return -1; + } + wb.drain(nwrite); + continue; + } + wb.reset(); + if (on_writefn(*this) != 0) { + return -1; + } + if (wb.rleft() == 0) { + break; + } + } + + ev_io_stop(loop, &wev); + ev_timer_stop(loop, &wt); + + return 0; +} + +int HttpClient::noop() { return 0; } + +void HttpClient::connect_fail() { + if (state == ClientState::IDLE) { + std::cerr << "[ERROR] Could not connect to the address " + << util::numeric_name(cur_addr->ai_addr, cur_addr->ai_addrlen) + << std::endl; + } + auto cur_state = state; + disconnect(); + if (cur_state == ClientState::IDLE) { + if (initiate_connection() == 0) { + std::cerr << "Trying next address " + << util::numeric_name(cur_addr->ai_addr, cur_addr->ai_addrlen) + << std::endl; + } + } +} + +int HttpClient::connected() { + if (!util::check_socket_connected(fd)) { + return ERR_CONNECT_FAIL; + } + + if (config.verbose) { + print_timer(); + std::cout << " Connected" << std::endl; + } + + state = ClientState::CONNECTED; + + ev_io_start(loop, &rev); + ev_io_stop(loop, &wev); + + ev_timer_again(loop, &rt); + ev_timer_stop(loop, &wt); + + if (ssl) { + readfn = &HttpClient::tls_handshake; + writefn = &HttpClient::tls_handshake; + + return do_write(); + } + + readfn = &HttpClient::read_clear; + writefn = &HttpClient::write_clear; + + if (need_upgrade()) { + htp = make_unique(); + http_parser_init(htp.get(), HTTP_RESPONSE); + htp->data = this; + + return do_write(); + } + + if (connection_made() != 0) { + return -1; + } + + return 0; +} + +namespace { +size_t populate_settings(nghttp2_settings_entry *iv) { + size_t niv = 2; + + iv[0].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS; + iv[0].value = 100; + + iv[1].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE; + if (config.window_bits != -1) { + iv[1].value = (1 << config.window_bits) - 1; + } else { + iv[1].value = NGHTTP2_INITIAL_WINDOW_SIZE; + } + + if (config.header_table_size >= 0) { + iv[niv].settings_id = NGHTTP2_SETTINGS_HEADER_TABLE_SIZE; + iv[niv].value = config.header_table_size; + ++niv; + } + + if (config.no_push) { + iv[niv].settings_id = NGHTTP2_SETTINGS_ENABLE_PUSH; + iv[niv].value = 0; + ++niv; + } + + return niv; +} +} // namespace + +int HttpClient::on_upgrade_connect() { + ssize_t rv; + record_connect_end_time(); + assert(!reqvec.empty()); + std::array iv; + size_t niv = populate_settings(iv.data()); + assert(settings_payload.size() >= 8 * niv); + rv = nghttp2_pack_settings_payload(settings_payload.data(), + settings_payload.size(), iv.data(), niv); + if (rv < 0) { + return -1; + } + settings_payloadlen = rv; + auto token68 = + base64::encode(std::begin(settings_payload), + std::begin(settings_payload) + settings_payloadlen); + util::to_token68(token68); + std::string req; + if (reqvec[0]->data_prd) { + // If the request contains upload data, use OPTIONS * to upgrade + req = "OPTIONS *"; + } else { + req = "GET "; + req += reqvec[0]->make_reqpath(); + } + + auto headers = Headers{{"Host", hostport}, + {"Connection", "Upgrade, HTTP2-Settings"}, + {"Upgrade", NGHTTP2_CLEARTEXT_PROTO_VERSION_ID}, + {"HTTP2-Settings", token68}, + {"Accept", "*/*"}, + {"User-Agent", "nghttp2/" NGHTTP2_VERSION}}; + auto initial_headerslen = headers.size(); + + for (auto &kv : config.headers) { + size_t i; + for (i = 0; i < initial_headerslen; ++i) { + if (util::strieq(kv.name, headers[i].name)) { + headers[i].value = kv.value; + break; + } + } + if (i < initial_headerslen) { + continue; + } + if (kv.name.size() != 0 && kv.name[0] != ':') { + headers.emplace_back(kv.name, kv.value, kv.no_index); + } + } + + req += " HTTP/1.1\r\n"; + + for (auto &kv : headers) { + req += kv.name; + req += ": "; + req += kv.value; + req += "\r\n"; + } + req += "\r\n"; + + wb.write(req.c_str(), req.size()); + + if (config.verbose) { + print_timer(); + std::cout << " HTTP Upgrade request\n" << req << std::endl; + } + + // record request time if this is GET request + if (!reqvec[0]->data_prd) { + reqvec[0]->record_request_start_time(); + } + + on_writefn = &HttpClient::noop; + + signal_write(); + + return 0; +} + +int HttpClient::on_upgrade_read(const uint8_t *data, size_t len) { + int rv; + + auto nread = http_parser_execute(htp.get(), &htp_hooks, + reinterpret_cast(data), len); + + if (config.verbose) { + std::cout.write(reinterpret_cast(data), nread); + } + + auto htperr = HTTP_PARSER_ERRNO(htp.get()); + + if (htperr != HPE_OK) { + std::cerr << "[ERROR] Failed to parse HTTP Upgrade response header: " + << "(" << http_errno_name(htperr) << ") " + << http_errno_description(htperr) << std::endl; + return -1; + } + + if (!upgrade_response_complete) { + return 0; + } + + if (config.verbose) { + std::cout << std::endl; + } + + if (upgrade_response_status_code != 101) { + std::cerr << "[ERROR] HTTP Upgrade failed" << std::endl; + + return -1; + } + + if (config.verbose) { + print_timer(); + std::cout << " HTTP Upgrade success" << std::endl; + } + + on_readfn = &HttpClient::on_read; + on_writefn = &HttpClient::on_write; + + rv = connection_made(); + if (rv != 0) { + return rv; + } + + // Read remaining data in the buffer because it is not notified + // callback anymore. + rv = on_readfn(*this, data + nread, len - nread); + if (rv != 0) { + return rv; + } + + return 0; +} + +int HttpClient::do_read() { return readfn(*this); } +int HttpClient::do_write() { return writefn(*this); } + +int HttpClient::connection_made() { + int rv; + + if (!need_upgrade()) { + record_connect_end_time(); + } + + if (ssl) { + // Check NPN or ALPN result + const unsigned char *next_proto = nullptr; + unsigned int next_proto_len; + SSL_get0_next_proto_negotiated(ssl, &next_proto, &next_proto_len); + for (int i = 0; i < 2; ++i) { + if (next_proto) { + if (config.verbose) { + std::cout << "The negotiated protocol: "; + std::cout.write(reinterpret_cast(next_proto), + next_proto_len); + std::cout << std::endl; + } + if (!util::check_h2_is_selected(next_proto, next_proto_len)) { + next_proto = nullptr; + } + break; + } +#if OPENSSL_VERSION_NUMBER >= 0x10002000L + SSL_get0_alpn_selected(ssl, &next_proto, &next_proto_len); +#else // OPENSSL_VERSION_NUMBER < 0x10002000L + break; +#endif // OPENSSL_VERSION_NUMBER < 0x10002000L + } + if (!next_proto) { + print_protocol_nego_error(); + return -1; + } + } + + rv = nghttp2_session_client_new2(&session, callbacks, this, + config.http2_option); + + if (rv != 0) { + return -1; + } + if (need_upgrade()) { + // Adjust stream user-data depending on the existence of upload + // data + Request *stream_user_data = nullptr; + if (!reqvec[0]->data_prd) { + stream_user_data = reqvec[0].get(); + } + rv = nghttp2_session_upgrade(session, settings_payload.data(), + settings_payloadlen, stream_user_data); + if (rv != 0) { + std::cerr << "[ERROR] nghttp2_session_upgrade() returned error: " + << nghttp2_strerror(rv) << std::endl; + return -1; + } + if (stream_user_data) { + stream_user_data->stream_id = 1; + request_done(stream_user_data); + } + } + // If upgrade succeeds, the SETTINGS value sent with + // HTTP2-Settings header field has already been submitted to + // session object. + if (!need_upgrade()) { + std::array iv; + auto niv = populate_settings(iv.data()); + rv = nghttp2_submit_settings(session, NGHTTP2_FLAG_NONE, iv.data(), niv); + if (rv != 0) { + return -1; + } + } + if (!config.no_dep) { + // Create anchor stream nodes + nghttp2_priority_spec pri_spec; + + for (auto &anchor : anchors) { + nghttp2_priority_spec_init(&pri_spec, anchor.dep_stream_id, anchor.weight, + 0); + rv = nghttp2_submit_priority(session, NGHTTP2_FLAG_NONE, anchor.stream_id, + &pri_spec); + if (rv != 0) { + return -1; + } + } + + rv = nghttp2_session_set_next_stream_id( + session, anchors[ANCHOR_FOLLOWERS].stream_id + 2); + if (rv != 0) { + return -1; + } + + if (need_upgrade()) { + // Amend the priority because we cannot send priority in + // HTTP/1.1 Upgrade. + auto &anchor = anchors[ANCHOR_FOLLOWERS]; + nghttp2_priority_spec_init(&pri_spec, anchor.stream_id, config.weight, 0); + + rv = nghttp2_submit_priority(session, NGHTTP2_FLAG_NONE, 1, &pri_spec); + if (rv != 0) { + return -1; + } + } + } else if (need_upgrade() && config.weight != NGHTTP2_DEFAULT_WEIGHT) { + // Amend the priority because we cannot send priority in + // HTTP/1.1 Upgrade. + nghttp2_priority_spec pri_spec; + + nghttp2_priority_spec_init(&pri_spec, 0, config.weight, 0); + + rv = nghttp2_submit_priority(session, NGHTTP2_FLAG_NONE, 1, &pri_spec); + if (rv != 0) { + return -1; + } + } + + ev_timer_again(loop, &settings_timer); + + if (config.connection_window_bits != -1) { + int32_t wininc = (1 << config.connection_window_bits) - 1 - + NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE; + rv = nghttp2_submit_window_update(session, NGHTTP2_FLAG_NONE, 0, wininc); + if (rv != 0) { + return -1; + } + } + // Adjust first request depending on the existence of the upload + // data + for (auto i = std::begin(reqvec) + (need_upgrade() && !reqvec[0]->data_prd); + i != std::end(reqvec); ++i) { + if (submit_request(this, config.headers, (*i).get()) != 0) { + return -1; + } + } + + signal_write(); + + return 0; +} + +int HttpClient::on_read(const uint8_t *data, size_t len) { + if (config.hexdump) { + util::hexdump(stdout, data, len); + } + + auto rv = nghttp2_session_mem_recv(session, data, len); + if (rv < 0) { + std::cerr << "[ERROR] nghttp2_session_mem_recv() returned error: " + << nghttp2_strerror(rv) << std::endl; + return -1; + } + + assert(static_cast(rv) == len); + + if (nghttp2_session_want_read(session) == 0 && + nghttp2_session_want_write(session) == 0 && wb.rleft() == 0) { + return -1; + } + + signal_write(); + + return 0; +} + +int HttpClient::on_write() { + auto rv = nghttp2_session_send(session); + if (rv != 0) { + std::cerr << "[ERROR] nghttp2_session_send() returned error: " + << nghttp2_strerror(rv) << std::endl; + return -1; + } + + if (nghttp2_session_want_read(session) == 0 && + nghttp2_session_want_write(session) == 0 && wb.rleft() == 0) { + return -1; + } + + return 0; +} + +int HttpClient::tls_handshake() { + ev_timer_again(loop, &rt); + + ERR_clear_error(); + + auto rv = SSL_do_handshake(ssl); + + if (rv == 0) { + return -1; + } + + if (rv < 0) { + auto err = SSL_get_error(ssl, rv); + switch (err) { + case SSL_ERROR_WANT_READ: + ev_io_stop(loop, &wev); + ev_timer_stop(loop, &wt); + return 0; + case SSL_ERROR_WANT_WRITE: + ev_io_start(loop, &wev); + ev_timer_again(loop, &wt); + return 0; + default: + return -1; + } + } + + ev_io_stop(loop, &wev); + ev_timer_stop(loop, &wt); + + readfn = &HttpClient::read_tls; + writefn = &HttpClient::write_tls; + + if (connection_made() != 0) { + return -1; + } + + return 0; +} + +int HttpClient::read_tls() { + ev_timer_again(loop, &rt); + + ERR_clear_error(); + + std::array buf; + for (;;) { + auto rv = SSL_read(ssl, buf.data(), buf.size()); + + if (rv == 0) { + return -1; + } + + if (rv < 0) { + auto err = SSL_get_error(ssl, rv); + switch (err) { + case SSL_ERROR_WANT_READ: + return 0; + case SSL_ERROR_WANT_WRITE: + // renegotiation started + return -1; + default: + return -1; + } + } + + if (on_readfn(*this, buf.data(), rv) != 0) { + return -1; + } + } +} + +int HttpClient::write_tls() { + ev_timer_again(loop, &rt); + + ERR_clear_error(); + + for (;;) { + if (wb.rleft() > 0) { + auto rv = SSL_write(ssl, wb.pos, wb.rleft()); + + if (rv == 0) { + return -1; + } + + if (rv < 0) { + auto err = SSL_get_error(ssl, rv); + switch (err) { + case SSL_ERROR_WANT_READ: + // renegotiation started + return -1; + case SSL_ERROR_WANT_WRITE: + ev_io_start(loop, &wev); + ev_timer_again(loop, &wt); + return 0; + default: + return -1; + } + } + + wb.drain(rv); + + continue; + } + wb.reset(); + if (on_writefn(*this) != 0) { + return -1; + } + if (wb.rleft() == 0) { + break; + } + } + + ev_io_stop(loop, &wev); + ev_timer_stop(loop, &wt); + + return 0; +} + +void HttpClient::signal_write() { ev_io_start(loop, &wev); } + +bool HttpClient::all_requests_processed() const { + return complete == reqvec.size(); +} + +void HttpClient::update_hostport() { + if (reqvec.empty()) { + return; + } + scheme = util::get_uri_field(reqvec[0]->uri.c_str(), reqvec[0]->u, UF_SCHEMA); + std::stringstream ss; + if (reqvec[0]->is_ipv6_literal_addr()) { + ss << "["; + util::write_uri_field(ss, reqvec[0]->uri.c_str(), reqvec[0]->u, UF_HOST); + ss << "]"; + } else { + util::write_uri_field(ss, reqvec[0]->uri.c_str(), reqvec[0]->u, UF_HOST); + } + if (util::has_uri_field(reqvec[0]->u, UF_PORT) && + reqvec[0]->u.port != + util::get_default_port(reqvec[0]->uri.c_str(), reqvec[0]->u)) { + ss << ":" << reqvec[0]->u.port; + } + hostport = ss.str(); +} + +bool HttpClient::add_request(const std::string &uri, + const nghttp2_data_provider *data_prd, + int64_t data_length, + const nghttp2_priority_spec &pri_spec, int level) { + http_parser_url u; + memset(&u, 0, sizeof(u)); + if (http_parser_parse_url(uri.c_str(), uri.size(), 0, &u) != 0) { + return false; + } + if (path_cache.count(uri)) { + return false; + } + + if (config.multiply == 1) { + path_cache.insert(uri); + } + + reqvec.push_back( + make_unique(uri, u, data_prd, data_length, pri_spec, level)); + return true; +} + +void HttpClient::record_start_time() { + timing.system_start_time = std::chrono::system_clock::now(); + timing.start_time = get_time(); +} + +void HttpClient::record_domain_lookup_end_time() { + timing.domain_lookup_end_time = get_time(); +} + +void HttpClient::record_connect_end_time() { + timing.connect_end_time = get_time(); +} + +void HttpClient::request_done(Request *req) { + if (req->stream_id % 2 == 0) { + return; + } +} + +#ifdef HAVE_JANSSON +void HttpClient::output_har(FILE *outfile) { + static auto PAGE_ID = "page_0"; + + auto root = json_object(); + auto log = json_object(); + json_object_set_new(root, "log", log); + json_object_set_new(log, "version", json_string("1.2")); + + auto creator = json_object(); + json_object_set_new(log, "creator", creator); + + json_object_set_new(creator, "name", json_string("nghttp")); + json_object_set_new(creator, "version", json_string(NGHTTP2_VERSION)); + + auto pages = json_array(); + json_object_set_new(log, "pages", pages); + + auto page = json_object(); + json_array_append_new(pages, page); + + json_object_set_new( + page, "startedDateTime", + json_string(util::format_iso8601(timing.system_start_time).c_str())); + json_object_set_new(page, "id", json_string(PAGE_ID)); + json_object_set_new(page, "title", json_string("")); + + json_object_set_new(page, "pageTimings", json_object()); + + auto entries = json_array(); + json_object_set_new(log, "entries", entries); + + auto dns_delta = + std::chrono::duration_cast( + timing.domain_lookup_end_time - timing.start_time).count() / + 1000.0; + auto connect_delta = + std::chrono::duration_cast( + timing.connect_end_time - timing.domain_lookup_end_time).count() / + 1000.0; + + for (size_t i = 0; i < reqvec.size(); ++i) { + auto &req = reqvec[i]; + + if (req->timing.state != RequestState::ON_COMPLETE) { + continue; + } + + auto entry = json_object(); + json_array_append_new(entries, entry); + + auto &req_timing = req->timing; + auto request_time = + (i == 0) ? timing.system_start_time + : timing.system_start_time + + std::chrono::duration_cast< + std::chrono::system_clock::duration>( + req_timing.request_start_time - timing.start_time); + + auto wait_delta = std::chrono::duration_cast( + req_timing.response_start_time - + req_timing.request_start_time).count() / + 1000.0; + auto receive_delta = std::chrono::duration_cast( + req_timing.response_end_time - + req_timing.response_start_time).count() / + 1000.0; + + auto time_sum = + std::chrono::duration_cast( + (i == 0) ? (req_timing.response_end_time - timing.start_time) + : (req_timing.response_end_time - + req_timing.request_start_time)).count() / + 1000.0; + + json_object_set_new( + entry, "startedDateTime", + json_string(util::format_iso8601(request_time).c_str())); + json_object_set_new(entry, "time", json_real(time_sum)); + + auto request = json_object(); + json_object_set_new(entry, "request", request); + + auto method_ptr = http2::get_header(req->req_nva, ":method"); + + const char *method = "GET"; + if (method_ptr) { + method = (*method_ptr).value.c_str(); + } + + auto req_headers = json_array(); + json_object_set_new(request, "headers", req_headers); + + for (auto &nv : req->req_nva) { + auto hd = json_object(); + json_array_append_new(req_headers, hd); + + json_object_set_new(hd, "name", json_string(nv.name.c_str())); + json_object_set_new(hd, "value", json_string(nv.value.c_str())); + } + + json_object_set_new(request, "method", json_string(method)); + json_object_set_new(request, "url", json_string(req->uri.c_str())); + json_object_set_new(request, "httpVersion", json_string("HTTP/2.0")); + json_object_set_new(request, "cookies", json_array()); + json_object_set_new(request, "queryString", json_array()); + json_object_set_new(request, "headersSize", json_integer(-1)); + json_object_set_new(request, "bodySize", json_integer(-1)); + + auto response = json_object(); + json_object_set_new(entry, "response", response); + + auto res_headers = json_array(); + json_object_set_new(response, "headers", res_headers); + + for (auto &nv : req->res_nva) { + auto hd = json_object(); + json_array_append_new(res_headers, hd); + + json_object_set_new(hd, "name", json_string(nv.name.c_str())); + json_object_set_new(hd, "value", json_string(nv.value.c_str())); + } + + json_object_set_new(response, "status", json_integer(req->status)); + json_object_set_new(response, "statusText", json_string("")); + json_object_set_new(response, "httpVersion", json_string("HTTP/2.0")); + json_object_set_new(response, "cookies", json_array()); + + auto content = json_object(); + json_object_set_new(response, "content", content); + + json_object_set_new(content, "size", json_integer(req->response_len)); + + auto content_type_ptr = http2::get_header(req->res_nva, "content-type"); + + const char *content_type = ""; + if (content_type_ptr) { + content_type = content_type_ptr->value.c_str(); + } + + json_object_set_new(content, "mimeType", json_string(content_type)); + + json_object_set_new(response, "redirectURL", json_string("")); + json_object_set_new(response, "headersSize", json_integer(-1)); + json_object_set_new(response, "bodySize", json_integer(-1)); + + json_object_set_new(entry, "cache", json_object()); + + auto timings = json_object(); + json_object_set_new(entry, "timings", timings); + + auto dns_timing = (i == 0) ? dns_delta : 0; + auto connect_timing = (i == 0) ? connect_delta : 0; + + json_object_set_new(timings, "dns", json_real(dns_timing)); + json_object_set_new(timings, "connect", json_real(connect_timing)); + + json_object_set_new(timings, "blocked", json_real(0.0)); + json_object_set_new(timings, "send", json_real(0.0)); + json_object_set_new(timings, "wait", json_real(wait_delta)); + json_object_set_new(timings, "receive", json_real(receive_delta)); + + json_object_set_new(entry, "pageref", json_string(PAGE_ID)); + } + + json_dumpf(root, outfile, JSON_PRESERVE_ORDER | JSON_INDENT(2)); + json_decref(root); +} +#endif // HAVE_JANSSON + +namespace { +void update_html_parser(HttpClient *client, Request *req, const uint8_t *data, + size_t len, int fin) { + if (!req->html_parser) { + return; + } + req->update_html_parser(data, len, fin); + + for (auto &p : req->html_parser->get_links()) { + auto uri = strip_fragment(p.first.c_str()); + auto res_type = p.second; + + http_parser_url u; + memset(&u, 0, sizeof(u)); + if (http_parser_parse_url(uri.c_str(), uri.size(), 0, &u) == 0 && + util::fieldeq(uri.c_str(), u, req->uri.c_str(), req->u, UF_SCHEMA) && + util::fieldeq(uri.c_str(), u, req->uri.c_str(), req->u, UF_HOST) && + util::porteq(uri.c_str(), u, req->uri.c_str(), req->u)) { + // No POST data for assets + auto pri_spec = resolve_dep(res_type); + + if (client->add_request(uri, nullptr, 0, pri_spec, req->level + 1)) { + + submit_request(client, config.headers, client->reqvec.back().get()); + } + } + } + req->html_parser->clear_links(); +} +} // namespace + +namespace { +HttpClient *get_client(void *user_data) { + return static_cast(user_data); +} +} // namespace + +namespace { +int on_data_chunk_recv_callback(nghttp2_session *session, uint8_t flags, + int32_t stream_id, const uint8_t *data, + size_t len, void *user_data) { + auto client = get_client(user_data); + auto req = static_cast( + nghttp2_session_get_stream_user_data(session, stream_id)); + + if (!req) { + return 0; + } + + if (config.verbose >= 2) { + verbose_on_data_chunk_recv_callback(session, flags, stream_id, data, len, + user_data); + } + + req->response_len += len; + + if (req->inflater) { + while (len > 0) { + const size_t MAX_OUTLEN = 4096; + std::array out; + size_t outlen = MAX_OUTLEN; + size_t tlen = len; + int rv = + nghttp2_gzip_inflate(req->inflater, out.data(), &outlen, data, &tlen); + if (rv != 0) { + nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, stream_id, + NGHTTP2_INTERNAL_ERROR); + break; + } + + if (!config.null_out) { + std::cout.write(reinterpret_cast(out.data()), outlen); + } + + update_html_parser(client, req, out.data(), outlen, 0); + data += tlen; + len -= tlen; + } + + return 0; + } + + if (!config.null_out) { + std::cout.write(reinterpret_cast(data), len); + } + + update_html_parser(client, req, data, len, 0); + + return 0; +} +} // namespace + +namespace { +ssize_t select_padding_callback(nghttp2_session *session, + const nghttp2_frame *frame, size_t max_payload, + void *user_data) { + return std::min(max_payload, frame->hd.length + config.padding); +} +} // namespace + +namespace { +void check_response_header(nghttp2_session *session, Request *req) { + bool gzip = false; + + req->expect_final_response = false; + + auto status_hd = req->get_res_header(http2::HD__STATUS); + + if (!status_hd) { + nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, req->stream_id, + NGHTTP2_PROTOCOL_ERROR); + return; + } + + auto status = http2::parse_http_status_code(status_hd->value); + if (status == -1) { + nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, req->stream_id, + NGHTTP2_PROTOCOL_ERROR); + return; + } + + req->status = status; + + for (auto &nv : req->res_nva) { + if ("content-encoding" == nv.name) { + gzip = util::strieq_l("gzip", nv.value) || + util::strieq_l("deflate", nv.value); + continue; + } + } + + if (req->status / 100 == 1) { + req->expect_final_response = true; + req->status = 0; + req->res_nva.clear(); + http2::init_hdidx(req->res_hdidx); + return; + } + + if (gzip) { + if (!req->inflater) { + req->init_inflater(); + } + } + if (config.get_assets && req->level == 0) { + if (!req->html_parser) { + req->init_html_parser(); + } + } +} +} // namespace + +namespace { +int on_begin_headers_callback(nghttp2_session *session, + const nghttp2_frame *frame, void *user_data) { + auto client = get_client(user_data); + switch (frame->hd.type) { + case NGHTTP2_HEADERS: { + auto req = static_cast( + nghttp2_session_get_stream_user_data(session, frame->hd.stream_id)); + if (!req) { + break; + } + + switch (frame->headers.cat) { + case NGHTTP2_HCAT_RESPONSE: + case NGHTTP2_HCAT_PUSH_RESPONSE: + req->record_response_start_time(); + break; + default: + break; + } + + break; + } + case NGHTTP2_PUSH_PROMISE: { + auto stream_id = frame->push_promise.promised_stream_id; + http_parser_url u; + memset(&u, 0, sizeof(u)); + // TODO Set pri and level + nghttp2_priority_spec pri_spec; + + nghttp2_priority_spec_default_init(&pri_spec); + + auto req = make_unique("", u, nullptr, 0, pri_spec); + req->stream_id = stream_id; + + nghttp2_session_set_stream_user_data(session, stream_id, req.get()); + + client->request_done(req.get()); + req->record_request_start_time(); + client->reqvec.push_back(std::move(req)); + + break; + } + } + return 0; +} +} // namespace + +namespace { +int on_header_callback(nghttp2_session *session, const nghttp2_frame *frame, + const uint8_t *name, size_t namelen, + const uint8_t *value, size_t valuelen, uint8_t flags, + void *user_data) { + if (config.verbose) { + verbose_on_header_callback(session, frame, name, namelen, value, valuelen, + flags, user_data); + } + + switch (frame->hd.type) { + case NGHTTP2_HEADERS: { + auto req = static_cast( + nghttp2_session_get_stream_user_data(session, frame->hd.stream_id)); + + if (!req) { + break; + } + + /* ignore trailer header */ + if (frame->headers.cat == NGHTTP2_HCAT_HEADERS && + !req->expect_final_response) { + break; + } + + auto token = http2::lookup_token(name, namelen); + + http2::index_header(req->res_hdidx, token, req->res_nva.size()); + http2::add_header(req->res_nva, name, namelen, value, valuelen, + flags & NGHTTP2_NV_FLAG_NO_INDEX, token); + break; + } + case NGHTTP2_PUSH_PROMISE: { + auto req = static_cast(nghttp2_session_get_stream_user_data( + session, frame->push_promise.promised_stream_id)); + + if (!req) { + break; + } + + auto token = http2::lookup_token(name, namelen); + + http2::index_header(req->req_hdidx, token, req->req_nva.size()); + http2::add_header(req->req_nva, name, namelen, value, valuelen, + flags & NGHTTP2_NV_FLAG_NO_INDEX, token); + break; + } + } + return 0; +} +} // namespace + +namespace { +int on_frame_recv_callback2(nghttp2_session *session, + const nghttp2_frame *frame, void *user_data) { + int rv = 0; + + if (config.verbose) { + verbose_on_frame_recv_callback(session, frame, user_data); + } + + auto client = get_client(user_data); + switch (frame->hd.type) { + case NGHTTP2_DATA: { + auto req = static_cast( + nghttp2_session_get_stream_user_data(session, frame->hd.stream_id)); + if (!req) { + return 0; + ; + } + + if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) { + req->record_response_end_time(); + ++client->success; + } + + break; + } + case NGHTTP2_HEADERS: { + auto req = static_cast( + nghttp2_session_get_stream_user_data(session, frame->hd.stream_id)); + // If this is the HTTP Upgrade with OPTIONS method to avoid POST, + // req is nullptr. + if (!req) { + return 0; + ; + } + + switch (frame->headers.cat) { + case NGHTTP2_HCAT_RESPONSE: + case NGHTTP2_HCAT_PUSH_RESPONSE: + check_response_header(session, req); + break; + case NGHTTP2_HCAT_HEADERS: + if (req->expect_final_response) { + check_response_header(session, req); + break; + } + if ((frame->hd.flags & NGHTTP2_FLAG_END_STREAM) == 0) { + nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, + frame->hd.stream_id, NGHTTP2_PROTOCOL_ERROR); + return 0; + } + break; + default: + assert(0); + } + + if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) { + req->record_response_end_time(); + ++client->success; + } + + break; + } + case NGHTTP2_SETTINGS: + if ((frame->hd.flags & NGHTTP2_FLAG_ACK) == 0) { + break; + } + ev_timer_stop(client->loop, &client->settings_timer); + break; + case NGHTTP2_PUSH_PROMISE: { + auto req = static_cast(nghttp2_session_get_stream_user_data( + session, frame->push_promise.promised_stream_id)); + if (!req) { + break; + } + auto scheme = req->get_req_header(http2::HD__SCHEME); + auto authority = req->get_req_header(http2::HD__AUTHORITY); + auto path = req->get_req_header(http2::HD__PATH); + + if (!authority) { + authority = req->get_req_header(http2::HD_HOST); + } + + // libnghttp2 guarantees :scheme, :method, :path and (:authority | + // host) exist and non-empty. + if (path->value[0] != '/') { + nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, + frame->push_promise.promised_stream_id, + NGHTTP2_PROTOCOL_ERROR); + break; + } + std::string uri = scheme->value; + uri += "://"; + uri += authority->value; + uri += path->value; + http_parser_url u; + memset(&u, 0, sizeof(u)); + if (http_parser_parse_url(uri.c_str(), uri.size(), 0, &u) != 0) { + nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, + frame->push_promise.promised_stream_id, + NGHTTP2_PROTOCOL_ERROR); + break; + } + req->uri = uri; + req->u = u; + + if (client->path_cache.count(uri)) { + nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, + frame->push_promise.promised_stream_id, + NGHTTP2_CANCEL); + break; + } + + if (config.multiply == 1) { + client->path_cache.insert(uri); + } + + break; + } + } + return rv; +} +} // namespace + +namespace { +int before_frame_send_callback(nghttp2_session *session, + const nghttp2_frame *frame, void *user_data) { + if (frame->hd.type != NGHTTP2_HEADERS || + frame->headers.cat != NGHTTP2_HCAT_REQUEST) { + return 0; + } + auto req = static_cast( + nghttp2_session_get_stream_user_data(session, frame->hd.stream_id)); + assert(req); + req->record_request_start_time(); + return 0; +} + +} // namespace + +namespace { +int on_stream_close_callback(nghttp2_session *session, int32_t stream_id, + uint32_t error_code, void *user_data) { + auto client = get_client(user_data); + auto req = static_cast( + nghttp2_session_get_stream_user_data(session, stream_id)); + + if (!req) { + return 0; + } + + update_html_parser(client, req, nullptr, 0, 1); + ++client->complete; + + if (client->all_requests_processed()) { + nghttp2_session_terminate_session(session, NGHTTP2_NO_ERROR); + } + + return 0; +} +} // namespace + +struct RequestResult { + std::chrono::microseconds time; +}; + +namespace { +void print_stats(const HttpClient &client) { + std::cout << "***** Statistics *****" << std::endl; + + std::vector reqs; + reqs.reserve(client.reqvec.size()); + for (const auto &req : client.reqvec) { + if (req->timing.state == RequestState::ON_COMPLETE) { + reqs.push_back(req.get()); + } + } + + std::sort(std::begin(reqs), std::end(reqs), + [](const Request *lhs, const Request *rhs) { + const auto <iming = lhs->timing; + const auto &rtiming = rhs->timing; + return ltiming.response_end_time < rtiming.response_end_time || + (ltiming.response_end_time == rtiming.response_end_time && + ltiming.request_start_time < rtiming.request_start_time); + }); + + std::cout << R"( +Request timing: + responseEnd: the time when last byte of response was received + relative to connectEnd + requestStart: the time just before first byte of request was sent + relative to connectEnd. If '*' is shown, this was + pushed by server. + process: responseEnd - requestStart + code: HTTP status code + size: number of bytes received as response body without + inflation. + URI: request URI + +see http://www.w3.org/TR/resource-timing/#processing-model + +sorted by 'complete' + +id responseEnd requestStart process code size request path)" << std::endl; + + const auto &base = client.timing.connect_end_time; + for (const auto &req : reqs) { + auto response_end = std::chrono::duration_cast( + req->timing.response_end_time - base); + auto request_start = std::chrono::duration_cast( + req->timing.request_start_time - base); + auto total = std::chrono::duration_cast( + req->timing.response_end_time - req->timing.request_start_time); + auto pushed = req->stream_id % 2 == 0; + + std::cout << std::setw(3) << req->stream_id << " " << std::setw(11) + << ("+" + util::format_duration(response_end)) << " " + << (pushed ? "*" : " ") << std::setw(11) + << ("+" + util::format_duration(request_start)) << " " + << std::setw(8) << util::format_duration(total) << " " + << std::setw(4) << req->status << " " << std::setw(4) + << util::utos_with_unit(req->response_len) << " " + << req->make_reqpath() << std::endl; + } +} +} // namespace + +namespace { +int client_select_next_proto_cb(SSL *ssl, unsigned char **out, + unsigned char *outlen, const unsigned char *in, + unsigned int inlen, void *arg) { + if (config.verbose) { + print_timer(); + std::cout << "[NPN] server offers:" << std::endl; + } + for (unsigned int i = 0; i < inlen; i += in [i] + 1) { + if (config.verbose) { + std::cout << " * "; + std::cout.write(reinterpret_cast(&in[i + 1]), in[i]); + std::cout << std::endl; + } + } + if (!util::select_h2(const_cast(out), outlen, in, + inlen)) { + print_protocol_nego_error(); + return SSL_TLSEXT_ERR_NOACK; + } + return SSL_TLSEXT_ERR_OK; +} +} // namespace + +namespace { +// Recommended general purpose "Intermediate compatibility" cipher by +// mozilla. +// +// https://wiki.mozilla.org/Security/Server_Side_TLS +const char *const CIPHER_LIST = + "ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-" + "AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:" + "DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-" + "AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-" + "AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-" + "AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:" + "DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-" + "SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-" + "SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!" + "aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA"; +} // namespace + +namespace { +int communicate( + const std::string &scheme, const std::string &host, uint16_t port, + std::vector> + requests, const nghttp2_session_callbacks *callbacks) { + int result = 0; + auto loop = EV_DEFAULT; + SSL_CTX *ssl_ctx = nullptr; + if (scheme == "https") { + ssl_ctx = SSL_CTX_new(SSLv23_client_method()); + if (!ssl_ctx) { + std::cerr << "[ERROR] Failed to create SSL_CTX: " + << ERR_error_string(ERR_get_error(), nullptr) << std::endl; + result = -1; + goto fin; + } + SSL_CTX_set_options(ssl_ctx, + SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | + SSL_OP_NO_COMPRESSION | + SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION); + SSL_CTX_set_mode(ssl_ctx, SSL_MODE_AUTO_RETRY); + SSL_CTX_set_mode(ssl_ctx, SSL_MODE_RELEASE_BUFFERS); + if (SSL_CTX_set_cipher_list(ssl_ctx, CIPHER_LIST) == 0) { + std::cerr << "[ERROR] " << ERR_error_string(ERR_get_error(), nullptr) + << std::endl; + result = -1; + goto fin; + } + if (!config.keyfile.empty()) { + if (SSL_CTX_use_PrivateKey_file(ssl_ctx, config.keyfile.c_str(), + SSL_FILETYPE_PEM) != 1) { + std::cerr << "[ERROR] " << ERR_error_string(ERR_get_error(), nullptr) + << std::endl; + result = -1; + goto fin; + } + } + if (!config.certfile.empty()) { + if (SSL_CTX_use_certificate_chain_file(ssl_ctx, + config.certfile.c_str()) != 1) { + std::cerr << "[ERROR] " << ERR_error_string(ERR_get_error(), nullptr) + << std::endl; + result = -1; + goto fin; + } + } + SSL_CTX_set_next_proto_select_cb(ssl_ctx, client_select_next_proto_cb, + nullptr); + +#if OPENSSL_VERSION_NUMBER >= 0x10002000L + auto proto_list = util::get_default_alpn(); + + SSL_CTX_set_alpn_protos(ssl_ctx, proto_list.data(), proto_list.size()); +#endif // OPENSSL_VERSION_NUMBER >= 0x10002000L + } + { + HttpClient client{callbacks, loop, ssl_ctx}; + + nghttp2_priority_spec pri_spec; + int32_t dep_stream_id = 0; + + if (!config.no_dep) { + dep_stream_id = anchors[ANCHOR_FOLLOWERS].stream_id; + } + + nghttp2_priority_spec_init(&pri_spec, dep_stream_id, config.weight, 0); + + for (auto req : requests) { + for (int i = 0; i < config.multiply; ++i) { + client.add_request(std::get<0>(req), std::get<1>(req), std::get<2>(req), + pri_spec); + } + } + client.update_hostport(); + + client.record_start_time(); + + if (client.resolve_host(host, port) != 0) { + goto fin; + } + + client.record_domain_lookup_end_time(); + + if (client.initiate_connection() != 0) { + goto fin; + } + ev_run(loop, 0); + +#ifdef HAVE_JANSSON + if (!config.harfile.empty()) { + FILE *outfile; + if (config.harfile == "-") { + outfile = stdout; + } else { + outfile = fopen(config.harfile.c_str(), "wb"); + } + + if (outfile) { + client.output_har(outfile); + + if (outfile != stdout) { + fclose(outfile); + } + } else { + std::cerr << "Cannot open file " << config.harfile << ". " + << "har file could not be created." << std::endl; + } + } +#endif // HAVE_JANSSON + + if (client.success != client.reqvec.size()) { + std::cerr << "Some requests were not processed. total=" + << client.reqvec.size() << ", processed=" << client.success + << std::endl; + } + if (config.stat) { + print_stats(client); + } + } +fin: + if (ssl_ctx) { + SSL_CTX_free(ssl_ctx); + } + return result; +} +} // namespace + +namespace { +ssize_t file_read_callback(nghttp2_session *session, int32_t stream_id, + uint8_t *buf, size_t length, uint32_t *data_flags, + nghttp2_data_source *source, void *user_data) { + int rv; + auto req = static_cast( + nghttp2_session_get_stream_user_data(session, stream_id)); + assert(req); + int fd = source->fd; + ssize_t nread; + + while ((nread = pread(fd, buf, length, req->data_offset)) == -1 && + errno == EINTR) + ; + + if (nread == -1) { + return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; + } + + if (nread == 0) { + *data_flags |= NGHTTP2_DATA_FLAG_EOF; + if (!config.trailer.empty()) { + std::vector nva; + nva.reserve(config.trailer.size()); + for (auto &kv : config.trailer) { + nva.push_back(http2::make_nv(kv.name, kv.value, kv.no_index)); + } + rv = nghttp2_submit_trailer(session, stream_id, nva.data(), nva.size()); + if (rv != 0) { + if (nghttp2_is_fatal(rv)) { + return NGHTTP2_ERR_CALLBACK_FAILURE; + } + } else { + *data_flags |= NGHTTP2_DATA_FLAG_NO_END_STREAM; + } + } + } else { + req->data_offset += nread; + } + + return nread; +} +} // namespace + +namespace { +ssize_t send_callback(nghttp2_session *session, const uint8_t *data, + size_t length, int flags, void *user_data) { + auto client = static_cast(user_data); + auto &wb = client->wb; + + if (wb.wleft() == 0) { + return NGHTTP2_ERR_WOULDBLOCK; + } + + return wb.write(data, length); +} +} // namespace + +namespace { +int run(char **uris, int n) { + nghttp2_session_callbacks *callbacks; + + nghttp2_session_callbacks_new(&callbacks); + auto cbsdel = defer(nghttp2_session_callbacks_del, callbacks); + + nghttp2_session_callbacks_set_on_stream_close_callback( + callbacks, on_stream_close_callback); + + nghttp2_session_callbacks_set_on_frame_recv_callback(callbacks, + on_frame_recv_callback2); + + if (config.verbose) { + nghttp2_session_callbacks_set_on_frame_send_callback( + callbacks, verbose_on_frame_send_callback); + + nghttp2_session_callbacks_set_on_invalid_frame_recv_callback( + callbacks, verbose_on_invalid_frame_recv_callback); + } + + nghttp2_session_callbacks_set_on_data_chunk_recv_callback( + callbacks, on_data_chunk_recv_callback); + + nghttp2_session_callbacks_set_on_begin_headers_callback( + callbacks, on_begin_headers_callback); + + nghttp2_session_callbacks_set_on_header_callback(callbacks, + on_header_callback); + + nghttp2_session_callbacks_set_before_frame_send_callback( + callbacks, before_frame_send_callback); + + nghttp2_session_callbacks_set_send_callback(callbacks, send_callback); + + if (config.padding) { + nghttp2_session_callbacks_set_select_padding_callback( + callbacks, select_padding_callback); + } + + std::string prev_scheme; + std::string prev_host; + uint16_t prev_port = 0; + int failures = 0; + int data_fd = -1; + nghttp2_data_provider data_prd; + struct stat data_stat; + + if (!config.datafile.empty()) { + if (config.datafile == "-") { + if (fstat(0, &data_stat) == 0 && + (data_stat.st_mode & S_IFMT) == S_IFREG) { + // use STDIN if it is a regular file + data_fd = 0; + } else { + // copy the contents of STDIN to a temporary file + char tempfn[] = "/tmp/nghttp.temp.XXXXXX"; + data_fd = mkstemp(tempfn); + if (data_fd == -1) { + std::cerr << "[ERROR] Could not create a temporary file in /tmp" + << std::endl; + return 1; + } + if (unlink(tempfn) != 0) { + std::cerr << "[WARNING] failed to unlink temporary file:" << tempfn + << std::endl; + } + while (1) { + std::array buf; + ssize_t rret, wret; + while ((rret = read(0, buf.data(), buf.size())) == -1 && + errno == EINTR) + ; + if (rret == 0) + break; + if (rret == -1) { + std::cerr << "[ERROR] I/O error while reading from STDIN" + << std::endl; + return 1; + } + while ((wret = write(data_fd, buf.data(), rret)) == -1 && + errno == EINTR) + ; + if (wret != rret) { + std::cerr << "[ERROR] I/O error while writing to temporary file" + << std::endl; + return 1; + } + } + if (fstat(data_fd, &data_stat) == -1) { + close(data_fd); + std::cerr << "[ERROR] Could not stat temporary file" << std::endl; + return 1; + } + } + } else { + data_fd = open(config.datafile.c_str(), O_RDONLY | O_BINARY); + if (data_fd == -1) { + std::cerr << "[ERROR] Could not open file " << config.datafile + << std::endl; + return 1; + } + if (fstat(data_fd, &data_stat) == -1) { + close(data_fd); + std::cerr << "[ERROR] Could not stat file " << config.datafile + << std::endl; + return 1; + } + } + data_prd.source.fd = data_fd; + data_prd.read_callback = file_read_callback; + } + std::vector> + requests; + for (int i = 0; i < n; ++i) { + http_parser_url u; + memset(&u, 0, sizeof(u)); + auto uri = strip_fragment(uris[i]); + if (http_parser_parse_url(uri.c_str(), uri.size(), 0, &u) == 0 && + util::has_uri_field(u, UF_SCHEMA)) { + uint16_t port = util::has_uri_field(u, UF_PORT) + ? u.port + : util::get_default_port(uri.c_str(), u); + if (!util::fieldeq(uri.c_str(), u, UF_SCHEMA, prev_scheme.c_str()) || + !util::fieldeq(uri.c_str(), u, UF_HOST, prev_host.c_str()) || + port != prev_port) { + if (!requests.empty()) { + if (communicate(prev_scheme, prev_host, prev_port, + std::move(requests), callbacks) != 0) { + ++failures; + } + requests.clear(); + } + prev_scheme = util::get_uri_field(uri.c_str(), u, UF_SCHEMA); + prev_host = util::get_uri_field(uri.c_str(), u, UF_HOST); + prev_port = port; + } + requests.emplace_back(uri, data_fd == -1 ? nullptr : &data_prd, + data_stat.st_size); + } + } + if (!requests.empty()) { + if (communicate(prev_scheme, prev_host, prev_port, std::move(requests), + callbacks) != 0) { + ++failures; + } + } + return failures; +} +} // namespace + +namespace { +void print_version(std::ostream &out) { + out << "nghttp nghttp2/" NGHTTP2_VERSION << std::endl; +} +} // namespace + +namespace { +void print_usage(std::ostream &out) { + out << R"(Usage: nghttp [OPTIONS]... ... +HTTP/2 experimental client)" << std::endl; +} +} // namespace + +namespace { +void print_help(std::ostream &out) { + print_usage(out); + out << R"( + Specify URI to access. +Options: + -v, --verbose + Print debug information such as reception and + transmission of frames and name/value pairs. Specifying + this option multiple times increases verbosity. + -n, --null-out + Discard downloaded data. + -O, --remote-name + Save download data in the current directory. The + filename is dereived from URI. If URI ends with '/', + 'index.html' is used as a filename. Not implemented + yet. + -t, --timeout= + Timeout each request after . Set 0 to disable + timeout. + -w, --window-bits= + Sets the stream level initial window size to 2**-1. + -W, --connection-window-bits= + Sets the connection level initial window size to + 2**-1. + -a, --get-assets + Download assets such as stylesheets, images and script + files linked from the downloaded resource. Only links + whose origins are the same with the linking resource + will be downloaded. nghttp prioritizes resources using + HTTP/2 dependency based priority. The priority order, + from highest to lowest, is html itself, css, javascript + and images. + -s, --stat Print statistics. + -H, --header=
    + Add a header to the requests. Example: -H':method: PUT' + --trailer=
    + Add a trailer header to the requests.
    must not + include pseudo header field (header field name starting + with ':'). To send trailer, one must use -d option to + send request body. Example: --trailer 'foo: bar'. + --cert= + Use the specified client certificate file. The file + must be in PEM format. + --key= Use the client private key file. The file must be in + PEM format. + -d, --data= + Post FILE to server. If '-' is given, data will be read + from stdin. + -m, --multiply= + Request each URI times. By default, same URI is not + requested twice. This option disables it too. + -u, --upgrade + Perform HTTP Upgrade for HTTP/2. This option is ignored + if the request URI has https scheme. If -d is used, the + HTTP upgrade request is performed with OPTIONS method. + -p, --weight= + Sets priority group weight. The valid value range is + [)" << NGHTTP2_MIN_WEIGHT << ", " << NGHTTP2_MAX_WEIGHT + << R"(], inclusive. + Default: )" << NGHTTP2_DEFAULT_WEIGHT << R"( + -M, --peer-max-concurrent-streams= + Use as SETTINGS_MAX_CONCURRENT_STREAMS value of + remote endpoint as if it is received in SETTINGS frame. + The default is large enough as it is seen as unlimited. + -c, --header-table-size= + Specify decoder header table size. + -b, --padding= + Add at most bytes to a frame payload as padding. + Specify 0 to disable padding. + -r, --har= + Output HTTP transactions in HAR format. If '-' + is given, data is written to stdout. + --color Force colored log output. + --continuation + Send large header to test CONTINUATION. + --no-content-length + Don't send content-length header field. + --no-dep Don't send dependency based priority hint to server. + --hexdump Display the incoming traffic in hexadecimal (Canonical + hex+ASCII display). If SSL/TLS is used, decrypted data + are used. + --no-push Disable server push. + --version Display version information and exit. + -h, --help Display this help and exit. + +-- + + The argument is an integer and an optional unit (e.g., 10K is + 10 * 1024). Units are K, M and G (powers of 1024). + + The argument is an integer and an optional unit (e.g., 1s + is 1 second and 500ms is 500 milliseconds). Units are h, m, s or ms + (hours, minutes, seconds and milliseconds, respectively). If a unit + is omitted, a second is used as unit.)" << std::endl; +} +} // namespace + +int main(int argc, char **argv) { + bool color = false; + while (1) { + static int flag = 0; + static option long_options[] = { + {"verbose", no_argument, nullptr, 'v'}, + {"null-out", no_argument, nullptr, 'n'}, + {"remote-name", no_argument, nullptr, 'O'}, + {"timeout", required_argument, nullptr, 't'}, + {"window-bits", required_argument, nullptr, 'w'}, + {"connection-window-bits", required_argument, nullptr, 'W'}, + {"get-assets", no_argument, nullptr, 'a'}, + {"stat", no_argument, nullptr, 's'}, + {"help", no_argument, nullptr, 'h'}, + {"header", required_argument, nullptr, 'H'}, + {"data", required_argument, nullptr, 'd'}, + {"multiply", required_argument, nullptr, 'm'}, + {"upgrade", no_argument, nullptr, 'u'}, + {"weight", required_argument, nullptr, 'p'}, + {"peer-max-concurrent-streams", required_argument, nullptr, 'M'}, + {"header-table-size", required_argument, nullptr, 'c'}, + {"padding", required_argument, nullptr, 'b'}, + {"har", required_argument, nullptr, 'r'}, + {"cert", required_argument, &flag, 1}, + {"key", required_argument, &flag, 2}, + {"color", no_argument, &flag, 3}, + {"continuation", no_argument, &flag, 4}, + {"version", no_argument, &flag, 5}, + {"no-content-length", no_argument, &flag, 6}, + {"no-dep", no_argument, &flag, 7}, + {"trailer", required_argument, &flag, 9}, + {"hexdump", no_argument, &flag, 10}, + {"no-push", no_argument, &flag, 11}, + {nullptr, 0, nullptr, 0}}; + int option_index = 0; + int c = getopt_long(argc, argv, "M:Oab:c:d:gm:np:r:hH:vst:uw:W:", + long_options, &option_index); + if (c == -1) { + break; + } + switch (c) { + case 'M': + // peer-max-concurrent-streams option + config.peer_max_concurrent_streams = strtoul(optarg, nullptr, 10); + break; + case 'O': + config.remote_name = true; + break; + case 'h': + print_help(std::cout); + exit(EXIT_SUCCESS); + case 'b': + config.padding = strtol(optarg, nullptr, 10); + break; + case 'n': + config.null_out = true; + break; + case 'p': { + errno = 0; + auto n = strtoul(optarg, nullptr, 10); + if (errno == 0 && NGHTTP2_MIN_WEIGHT <= n && n <= NGHTTP2_MAX_WEIGHT) { + config.weight = n; + } else { + std::cerr << "-p: specify the integer in the range [" + << NGHTTP2_MIN_WEIGHT << ", " << NGHTTP2_MAX_WEIGHT + << "], inclusive" << std::endl; + exit(EXIT_FAILURE); + } + break; + } + case 'r': +#ifdef HAVE_JANSSON + config.harfile = optarg; +#else // !HAVE_JANSSON + std::cerr << "[WARNING]: -r, --har option is ignored because\n" + << "the binary was not compiled with libjansson." << std::endl; +#endif // !HAVE_JANSSON + break; + case 'v': + ++config.verbose; + break; + case 't': + config.timeout = util::parse_duration_with_unit(optarg); + if (config.timeout == std::numeric_limits::infinity()) { + std::cerr << "-t: bad timeout value: " << optarg << std::endl; + exit(EXIT_FAILURE); + } + break; + case 'u': + config.upgrade = true; + break; + case 'w': + case 'W': { + errno = 0; + char *endptr = nullptr; + unsigned long int n = strtoul(optarg, &endptr, 10); + if (errno == 0 && *endptr == '\0' && n < 31) { + if (c == 'w') { + config.window_bits = n; + } else { + config.connection_window_bits = n; + } + } else { + std::cerr << "-" << static_cast(c) + << ": specify the integer in the range [0, 30], inclusive" + << std::endl; + exit(EXIT_FAILURE); + } + break; + } + case 'H': { + char *header = optarg; + // Skip first possible ':' in the header name + char *value = strchr(optarg + 1, ':'); + if (!value || (header[0] == ':' && header + 1 == value)) { + std::cerr << "-H: invalid header: " << optarg << std::endl; + exit(EXIT_FAILURE); + } + *value = 0; + value++; + while (isspace(*value)) { + value++; + } + if (*value == 0) { + // This could also be a valid case for suppressing a header + // similar to curl + std::cerr << "-H: invalid header - value missing: " << optarg + << std::endl; + exit(EXIT_FAILURE); + } + config.headers.emplace_back(header, value, false); + util::inp_strlower(config.headers.back().name); + break; + } + case 'a': +#ifdef HAVE_LIBXML2 + config.get_assets = true; +#else // !HAVE_LIBXML2 + std::cerr << "[WARNING]: -a, --get-assets option is ignored because\n" + << "the binary was not compiled with libxml2." << std::endl; +#endif // !HAVE_LIBXML2 + break; + case 's': + config.stat = true; + break; + case 'd': + config.datafile = optarg; + break; + case 'm': + config.multiply = strtoul(optarg, nullptr, 10); + break; + case 'c': + errno = 0; + config.header_table_size = util::parse_uint_with_unit(optarg); + if (config.header_table_size == -1) { + std::cerr << "-c: Bad option value: " << optarg << std::endl; + exit(EXIT_FAILURE); + } + break; + case '?': + util::show_candidates(argv[optind - 1], long_options); + exit(EXIT_FAILURE); + case 0: + switch (flag) { + case 1: + // cert option + config.certfile = optarg; + break; + case 2: + // key option + config.keyfile = optarg; + break; + case 3: + // color option + color = true; + break; + case 4: + // continuation option + config.continuation = true; + break; + case 5: + // version option + print_version(std::cout); + exit(EXIT_SUCCESS); + case 6: + // no-content-length option + config.no_content_length = true; + break; + case 7: + // no-dep option + config.no_dep = true; + break; + case 9: { + // trailer option + auto header = optarg; + auto value = strchr(optarg, ':'); + if (!value) { + std::cerr << "--trailer: invalid header: " << optarg << std::endl; + exit(EXIT_FAILURE); + } + *value = 0; + value++; + while (isspace(*value)) { + value++; + } + if (*value == 0) { + // This could also be a valid case for suppressing a header + // similar to curl + std::cerr << "--trailer: invalid header - value missing: " << optarg + << std::endl; + exit(EXIT_FAILURE); + } + config.trailer.emplace_back(header, value, false); + util::inp_strlower(config.trailer.back().name); + break; + } + case 10: + // hexdump option + config.hexdump = true; + break; + case 11: + // no-push option + config.no_push = true; + break; + } + break; + default: + break; + } + } + + set_color_output(color || isatty(fileno(stdout))); + + nghttp2_option_set_peer_max_concurrent_streams( + config.http2_option, config.peer_max_concurrent_streams); + + struct sigaction act; + memset(&act, 0, sizeof(struct sigaction)); + act.sa_handler = SIG_IGN; + sigaction(SIGPIPE, &act, nullptr); + OPENSSL_config(nullptr); + OpenSSL_add_all_algorithms(); + SSL_load_error_strings(); + SSL_library_init(); + reset_timer(); + return run(argv + optind, argc - optind); +} + +} // namespace nghttp2 + +int main(int argc, char **argv) { return nghttp2::main(argc, argv); } diff --git a/src/nghttp.h b/src/nghttp.h new file mode 100644 index 0000000..0a7ee35 --- /dev/null +++ b/src/nghttp.h @@ -0,0 +1,272 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2015 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGHTTP_H +#define NGHTTP_H + +#include "nghttp2_config.h" + +#include +#ifdef HAVE_SYS_SOCKET_H +#include +#endif // HAVE_SYS_SOCKET_H +#ifdef HAVE_NETDB_H +#include +#endif // HAVE_NETDB_H + +#include +#include +#include +#include +#include + +#include + +#include + +#include + +#include "http-parser/http_parser.h" + +#include "buffer.h" +#include "http2.h" +#include "nghttp2_gzip.h" + +namespace nghttp2 { + +class HtmlParser; + +struct Config { + Config(); + ~Config(); + + Headers headers; + Headers trailer; + std::string certfile; + std::string keyfile; + std::string datafile; + std::string harfile; + nghttp2_option *http2_option; + size_t output_upper_thres; + size_t padding; + ssize_t peer_max_concurrent_streams; + ssize_t header_table_size; + int32_t weight; + int multiply; + // milliseconds + ev_tstamp timeout; + int window_bits; + int connection_window_bits; + int verbose; + bool null_out; + bool remote_name; + bool get_assets; + bool stat; + bool upgrade; + bool continuation; + bool no_content_length; + bool no_dep; + bool hexdump; + bool no_push; +}; + +enum class RequestState { INITIAL, ON_REQUEST, ON_RESPONSE, ON_COMPLETE }; + +struct RequestTiming { + // The point in time when request is started to be sent. + // Corresponds to requestStart in Resource Timing TR. + std::chrono::steady_clock::time_point request_start_time; + // The point in time when first byte of response is received. + // Corresponds to responseStart in Resource Timing TR. + std::chrono::steady_clock::time_point response_start_time; + // The point in time when last byte of response is received. + // Corresponds to responseEnd in Resource Timing TR. + std::chrono::steady_clock::time_point response_end_time; + RequestState state; + RequestTiming() : state(RequestState::INITIAL) {} +}; + +struct Request { + // For pushed request, |uri| is empty and |u| is zero-cleared. + Request(const std::string &uri, const http_parser_url &u, + const nghttp2_data_provider *data_prd, int64_t data_length, + const nghttp2_priority_spec &pri_spec, int level = 0); + ~Request(); + + void init_inflater(); + + void init_html_parser(); + int update_html_parser(const uint8_t *data, size_t len, int fin); + + std::string make_reqpath() const; + + bool is_ipv6_literal_addr() const; + + bool response_pseudo_header_allowed(int16_t token) const; + bool push_request_pseudo_header_allowed(int16_t token) const; + + Headers::value_type *get_res_header(int16_t token); + Headers::value_type *get_req_header(int16_t token); + + void record_request_start_time(); + void record_response_start_time(); + void record_response_end_time(); + + Headers res_nva; + Headers req_nva; + // URI without fragment + std::string uri; + http_parser_url u; + nghttp2_priority_spec pri_spec; + RequestTiming timing; + int64_t data_length; + int64_t data_offset; + // Number of bytes received from server + int64_t response_len; + nghttp2_gzip *inflater; + HtmlParser *html_parser; + const nghttp2_data_provider *data_prd; + int32_t stream_id; + int status; + // Recursion level: 0: first entity, 1: entity linked from first entity + int level; + http2::HeaderIndex res_hdidx; + // used for incoming PUSH_PROMISE + http2::HeaderIndex req_hdidx; + bool expect_final_response; +}; + +struct SessionTiming { + // The point in time when operation was started. Corresponds to + // startTime in Resource Timing TR, but recorded in system clock time. + std::chrono::system_clock::time_point system_start_time; + // Same as above, but recorded in steady clock time. + std::chrono::steady_clock::time_point start_time; + // The point in time when DNS resolution was completed. Corresponds + // to domainLookupEnd in Resource Timing TR. + std::chrono::steady_clock::time_point domain_lookup_end_time; + // The point in time when connection was established or SSL/TLS + // handshake was completed. Corresponds to connectEnd in Resource + // Timing TR. + std::chrono::steady_clock::time_point connect_end_time; +}; + +enum class ClientState { IDLE, CONNECTED }; + +struct HttpClient { + HttpClient(const nghttp2_session_callbacks *callbacks, struct ev_loop *loop, + SSL_CTX *ssl_ctx); + ~HttpClient(); + + bool need_upgrade() const; + int resolve_host(const std::string &host, uint16_t port); + int initiate_connection(); + void disconnect(); + + int noop(); + int read_clear(); + int write_clear(); + int connected(); + int tls_handshake(); + int read_tls(); + int write_tls(); + + int do_read(); + int do_write(); + + int on_upgrade_connect(); + int on_upgrade_read(const uint8_t *data, size_t len); + int on_read(const uint8_t *data, size_t len); + int on_write(); + + int connection_made(); + void connect_fail(); + void request_done(Request *req); + + void signal_write(); + + bool all_requests_processed() const; + void update_hostport(); + bool add_request(const std::string &uri, + const nghttp2_data_provider *data_prd, int64_t data_length, + const nghttp2_priority_spec &pri_spec, int level = 0); + + void record_start_time(); + void record_domain_lookup_end_time(); + void record_connect_end_time(); + +#ifdef HAVE_JANSSON + void output_har(FILE *outfile); +#endif // HAVE_JANSSON + + std::vector> reqvec; + // Insert path already added in reqvec to prevent multiple request + // for 1 resource. + std::set path_cache; + std::string scheme; + std::string host; + std::string hostport; + // Used for parse the HTTP upgrade response from server + std::unique_ptr htp; + SessionTiming timing; + ev_io wev; + ev_io rev; + ev_timer wt; + ev_timer rt; + ev_timer settings_timer; + std::function readfn, writefn; + std::function on_readfn; + std::function on_writefn; + nghttp2_session *session; + const nghttp2_session_callbacks *callbacks; + struct ev_loop *loop; + SSL_CTX *ssl_ctx; + SSL *ssl; + addrinfo *addrs; + addrinfo *next_addr; + addrinfo *cur_addr; + // The number of completed requests, including failed ones. + size_t complete; + // The number of requests that local endpoint received END_STREAM + // from peer. + size_t success; + // The length of settings_payload + size_t settings_payloadlen; + ClientState state; + // The HTTP status code of the response message of HTTP Upgrade. + unsigned int upgrade_response_status_code; + int fd; + // true if the response message of HTTP Upgrade request is fully + // received. It is not relevant the upgrade succeeds, or not. + bool upgrade_response_complete; + Buffer<65536> wb; + // SETTINGS payload sent as token68 in HTTP Upgrade + std::array settings_payload; + + enum { ERR_CONNECT_FAIL = -100 }; +}; + +} // namespace nghttp2 + +#endif // NGHTTP_H diff --git a/src/nghttp2_config.h b/src/nghttp2_config.h new file mode 100644 index 0000000..9dede35 --- /dev/null +++ b/src/nghttp2_config.h @@ -0,0 +1,38 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGHTTP2_CONFIG_H +#define NGHTTP2_CONFIG_H + +#ifdef HAVE_CONFIG_H +#include +#endif // HAVE_CONFIG_H + +// gcc 4.6 has std::chrono::monotonic_clock, which was renamed as +// std::chrono::steady_clock in C++11 standard. +#ifndef HAVE_STEADY_CLOCK +#define steady_clock monotonic_clock +#endif // !HAVE_STEADY_CLOCK + +#endif // NGHTTP2_CONFIG_H diff --git a/src/nghttp2_gzip.c b/src/nghttp2_gzip.c new file mode 100644 index 0000000..b12f311 --- /dev/null +++ b/src/nghttp2_gzip.c @@ -0,0 +1,92 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "nghttp2_gzip.h" + +#include + +int nghttp2_gzip_inflate_new(nghttp2_gzip **inflater_ptr) { + int rv; + *inflater_ptr = malloc(sizeof(nghttp2_gzip)); + if (*inflater_ptr == NULL) { + return -1; + } + (*inflater_ptr)->finished = 0; + (*inflater_ptr)->zst.next_in = Z_NULL; + (*inflater_ptr)->zst.avail_in = 0; + (*inflater_ptr)->zst.zalloc = Z_NULL; + (*inflater_ptr)->zst.zfree = Z_NULL; + (*inflater_ptr)->zst.opaque = Z_NULL; + rv = inflateInit2(&(*inflater_ptr)->zst, 47); + if (rv != Z_OK) { + free(*inflater_ptr); + return -1; + } + return 0; +} + +void nghttp2_gzip_inflate_del(nghttp2_gzip *inflater) { + if (inflater != NULL) { + inflateEnd(&inflater->zst); + free(inflater); + } +} + +int nghttp2_gzip_inflate(nghttp2_gzip *inflater, uint8_t *out, + size_t *outlen_ptr, const uint8_t *in, + size_t *inlen_ptr) { + int rv; + if (inflater->finished) { + return -1; + } + inflater->zst.avail_in = (unsigned int)*inlen_ptr; + inflater->zst.next_in = (unsigned char *)in; + inflater->zst.avail_out = (unsigned int)*outlen_ptr; + inflater->zst.next_out = out; + + rv = inflate(&inflater->zst, Z_NO_FLUSH); + + *inlen_ptr -= inflater->zst.avail_in; + *outlen_ptr -= inflater->zst.avail_out; + switch (rv) { + case Z_STREAM_END: + inflater->finished = 1; + case Z_OK: + case Z_BUF_ERROR: + return 0; + case Z_DATA_ERROR: + case Z_STREAM_ERROR: + case Z_NEED_DICT: + case Z_MEM_ERROR: + return -1; + default: + assert(0); + /* We need this for some compilers */ + return 0; + } +} + +int nghttp2_gzip_inflate_finished(nghttp2_gzip *inflater) { + return inflater->finished; +} diff --git a/src/nghttp2_gzip.h b/src/nghttp2_gzip.h new file mode 100644 index 0000000..2fa905a --- /dev/null +++ b/src/nghttp2_gzip.h @@ -0,0 +1,122 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGHTTP2_GZIP_H + +#ifdef HAVE_CONFIG_H +#include +#endif /* HAVE_CONFIG_H */ +#include + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @struct + * + * The gzip stream to inflate data. + */ +typedef struct { + z_stream zst; + int8_t finished; +} nghttp2_gzip; + +/** + * @function + * + * A helper function to set up a per request gzip stream to inflate + * data. + * + * This function returns 0 if it succeeds, or -1. + */ +int nghttp2_gzip_inflate_new(nghttp2_gzip **inflater_ptr); + +/** + * @function + * + * Frees the inflate stream. The |inflater| may be ``NULL``. + */ +void nghttp2_gzip_inflate_del(nghttp2_gzip *inflater); + +/** + * @function + * + * Inflates data in |in| with the length |*inlen_ptr| and stores the + * inflated data to |out| which has allocated size at least + * |*outlen_ptr|. On return, |*outlen_ptr| is updated to represent + * the number of data written in |out|. Similarly, |*inlen_ptr| is + * updated to represent the number of input bytes processed. + * + * This function returns 0 if it succeeds, or -1. + * + * The example follows:: + * + * void on_data_chunk_recv_callback(nghttp2_session *session, + * uint8_t flags, + * int32_t stream_id, + * const uint8_t *data, size_t len, + * void *user_data) + * { + * ... + * req = nghttp2_session_get_stream_user_data(session, stream_id); + * nghttp2_gzip *inflater = req->inflater; + * while(len > 0) { + * uint8_t out[MAX_OUTLEN]; + * size_t outlen = MAX_OUTLEN; + * size_t tlen = len; + * int rv; + * rv = nghttp2_gzip_inflate(inflater, out, &outlen, data, &tlen); + * if(rv != 0) { + * nghttp2_submit_rst_stream(session, stream_id, + * NGHTTP2_INTERNAL_ERROR); + * break; + * } + * ... Do stuff ... + * data += tlen; + * len -= tlen; + * } + * .... + * } + */ +int nghttp2_gzip_inflate(nghttp2_gzip *inflater, uint8_t *out, + size_t *outlen_ptr, const uint8_t *in, + size_t *inlen_ptr); + +/** + * @function + * + * Returns nonzero if |inflater| sees the end of deflate stream. + * After this function returns nonzero, `nghttp2_gzip_inflate()` with + * |inflater| gets to return error. + */ +int nghttp2_gzip_inflate_finished(nghttp2_gzip *inflater); + +#ifdef __cplusplus +} +#endif + +#endif /* NGHTTP2_GZIP_H */ diff --git a/src/nghttp2_gzip_test.c b/src/nghttp2_gzip_test.c new file mode 100644 index 0000000..eb43ae6 --- /dev/null +++ b/src/nghttp2_gzip_test.c @@ -0,0 +1,115 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "nghttp2_gzip_test.h" + +#include +#include + +#include + +#include + +#include "nghttp2_gzip.h" + +static ssize_t deflate_data(uint8_t *out, size_t outlen, const uint8_t *in, + size_t inlen) { + int rv; + z_stream zst; + zst.next_in = Z_NULL; + zst.zalloc = Z_NULL; + zst.zfree = Z_NULL; + zst.opaque = Z_NULL; + + rv = deflateInit(&zst, Z_DEFAULT_COMPRESSION); + assert(rv == Z_OK); + + zst.avail_in = (unsigned int)inlen; + zst.next_in = (uint8_t *)in; + zst.avail_out = (unsigned int)outlen; + zst.next_out = out; + rv = deflate(&zst, Z_SYNC_FLUSH); + assert(rv == Z_OK); + + deflateEnd(&zst); + + return outlen - zst.avail_out; +} + +static const char input[] = + "THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND " + "EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF " + "MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND " + "NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE " + "LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION " + "OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION " + "WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE."; + +void test_nghttp2_gzip_inflate(void) { + nghttp2_gzip *inflater; + uint8_t in[4096], out[4096], *inptr; + size_t inlen = sizeof(in); + size_t inproclen, outproclen; + const char *inputptr = input; + + inlen = deflate_data(in, inlen, (const uint8_t *)input, sizeof(input) - 1); + + CU_ASSERT(0 == nghttp2_gzip_inflate_new(&inflater)); + /* First 16 bytes */ + inptr = in; + inproclen = inlen; + outproclen = 16; + CU_ASSERT( + 0 == nghttp2_gzip_inflate(inflater, out, &outproclen, inptr, &inproclen)); + CU_ASSERT(16 == outproclen); + CU_ASSERT(inproclen > 0); + CU_ASSERT(0 == memcmp(inputptr, out, outproclen)); + /* Next 32 bytes */ + inptr += inproclen; + inlen -= inproclen; + inproclen = inlen; + inputptr += outproclen; + outproclen = 32; + CU_ASSERT( + 0 == nghttp2_gzip_inflate(inflater, out, &outproclen, inptr, &inproclen)); + CU_ASSERT(32 == outproclen); + CU_ASSERT(inproclen > 0); + CU_ASSERT(0 == memcmp(inputptr, out, outproclen)); + /* Rest */ + inptr += inproclen; + inlen -= inproclen; + inproclen = inlen; + inputptr += outproclen; + outproclen = sizeof(out); + CU_ASSERT( + 0 == nghttp2_gzip_inflate(inflater, out, &outproclen, inptr, &inproclen)); + CU_ASSERT(sizeof(input) - 49 == outproclen); + CU_ASSERT(inproclen > 0); + CU_ASSERT(0 == memcmp(inputptr, out, outproclen)); + + inlen -= inproclen; + CU_ASSERT(0 == inlen); + + nghttp2_gzip_inflate_del(inflater); +} diff --git a/src/nghttp2_gzip_test.h b/src/nghttp2_gzip_test.h new file mode 100644 index 0000000..2defcdc --- /dev/null +++ b/src/nghttp2_gzip_test.h @@ -0,0 +1,42 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGHTTP2_GZIP_TEST_H +#define NGHTTP2_GZIP_TEST_H + +#ifdef HAVE_CONFIG_H +#include +#endif /* HAVE_CONFIG_H */ + +#ifdef __cplusplus +extern "C" { +#endif + +void test_nghttp2_gzip_inflate(void); + +#ifdef __cplusplus +} +#endif + +#endif /* NGHTTP2_GZIP_TEST_H */ diff --git a/src/nghttpd.cc b/src/nghttpd.cc new file mode 100644 index 0000000..4ffc0bc --- /dev/null +++ b/src/nghttpd.cc @@ -0,0 +1,381 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "nghttp2_config.h" + +#ifdef HAVE_UNISTD_H +#include +#endif // HAVE_UNISTD_H +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "app_helper.h" +#include "HttpServer.h" +#include "util.h" +#include "ssl.h" + +namespace nghttp2 { + +namespace { +int parse_push_config(Config &config, const char *optarg) { + const char *eq = strchr(optarg, '='); + if (eq == NULL) { + return -1; + } + auto &paths = config.push[std::string(optarg, eq)]; + auto optarg_end = optarg + strlen(optarg); + auto i = eq + 1; + for (;;) { + const char *j = strchr(i, ','); + if (j == NULL) { + j = optarg_end; + } + paths.emplace_back(i, j); + if (j == optarg_end) { + break; + } + i = j; + ++i; + } + + return 0; +} +} // namespace + +namespace { +void print_version(std::ostream &out) { + out << "nghttpd nghttp2/" NGHTTP2_VERSION << std::endl; +} +} // namespace + +namespace { +void print_usage(std::ostream &out) { + out << "Usage: nghttpd [OPTION]... [ ]\n" + << "HTTP/2 experimental server" << std::endl; +} +} // namespace + +namespace { +void print_help(std::ostream &out) { + Config config; + print_usage(out); + out << R"( + Specify listening port number. + + Set path to server's private key. Required unless + --no-tls is specified. + Set path to server's certificate. Required unless + --no-tls is specified. +Options: + -a, --address= + The address to bind to. If not specified the default IP + address determined by getaddrinfo is used. + -D, --daemon + Run in a background. If -D is used, the current working + directory is changed to '/'. Therefore if this option + is used, -d option must be specified. + -V, --verify-client + The server sends a client certificate request. If the + client did not return a certificate, the handshake is + terminated. Currently, this option just requests a + client certificate and does not verify it. + -d, --htdocs= + Specify document root. If this option is not specified, + the document root is the current working directory. + -v, --verbose + Print debug information such as reception/ transmission + of frames and name/value pairs. + --no-tls Disable SSL/TLS. + -c, --header-table-size= + Specify decoder header table size. + --color Force colored log output. + -p, --push== + Push resources s when is requested. + This option can be used repeatedly to specify multiple + push configurations. and s are + relative to document root. See --htdocs option. + Example: -p/=/foo.png -p/doc=/bar.css + -b, --padding= + Add at most bytes to a frame payload as padding. + Specify 0 to disable padding. + -m, --max-concurrent-streams= + Set the maximum number of the concurrent streams in one + HTTP/2 session. + Default: )" << config.max_concurrent_streams << R"( + -n, --workers= + Set the number of worker threads. + Default: 1 + -e, --error-gzip + Make error response gzipped. + --dh-param-file= + Path to file that contains DH parameters in PEM format. + Without this option, DHE cipher suites are not + available. + --early-response + Start sending response when request HEADERS is received, + rather than complete request is received. + --trailer=
    + Add a trailer header to a response.
    must not + include pseudo header field (header field name starting + with ':'). The trailer is sent only if a response has + body part. Example: --trailer 'foo: bar'. + --hexdump Display the incoming traffic in hexadecimal (Canonical + hex+ASCII display). If SSL/TLS is used, decrypted data + are used. + --echo-upload + Send back uploaded content if method is POST or PUT. + --version Display version information and exit. + -h, --help Display this help and exit. + +-- + + The argument is an integer and an optional unit (e.g., 10K is + 10 * 1024). Units are K, M and G (powers of 1024).)" << std::endl; +} +} // namespace + +int main(int argc, char **argv) { + Config config; + bool color = false; + while (1) { + static int flag = 0; + static option long_options[] = { + {"address", required_argument, nullptr, 'a'}, + {"daemon", no_argument, nullptr, 'D'}, + {"htdocs", required_argument, nullptr, 'd'}, + {"help", no_argument, nullptr, 'h'}, + {"verbose", no_argument, nullptr, 'v'}, + {"verify-client", no_argument, nullptr, 'V'}, + {"header-table-size", required_argument, nullptr, 'c'}, + {"push", required_argument, nullptr, 'p'}, + {"padding", required_argument, nullptr, 'b'}, + {"max-concurrent-streams", required_argument, nullptr, 'm'}, + {"workers", required_argument, nullptr, 'n'}, + {"error-gzip", no_argument, nullptr, 'e'}, + {"no-tls", no_argument, &flag, 1}, + {"color", no_argument, &flag, 2}, + {"version", no_argument, &flag, 3}, + {"dh-param-file", required_argument, &flag, 4}, + {"early-response", no_argument, &flag, 5}, + {"trailer", required_argument, &flag, 6}, + {"hexdump", no_argument, &flag, 7}, + {"echo-upload", no_argument, &flag, 8}, + {nullptr, 0, nullptr, 0}}; + int option_index = 0; + int c = getopt_long(argc, argv, "DVb:c:d:ehm:n:p:va:", long_options, + &option_index); + char *end; + if (c == -1) { + break; + } + switch (c) { + case 'a': + config.address = optarg; + break; + case 'D': + config.daemon = true; + break; + case 'V': + config.verify_client = true; + break; + case 'b': + config.padding = strtol(optarg, nullptr, 10); + break; + case 'd': + config.htdocs = optarg; + break; + case 'e': + config.error_gzip = true; + break; + case 'm': { + // max-concurrent-streams option + auto n = util::parse_uint(optarg); + if (n == -1) { + std::cerr << "-m: invalid argument: " << optarg << std::endl; + exit(EXIT_FAILURE); + } + config.max_concurrent_streams = n; + break; + } + case 'n': +#ifdef NOTHREADS + std::cerr << "-n: WARNING: Threading disabled at build time, " + << "no threads created." << std::endl; +#else + errno = 0; + config.num_worker = strtoul(optarg, &end, 10); + if (errno == ERANGE || *end != '\0' || config.num_worker == 0) { + std::cerr << "-n: Bad option value: " << optarg << std::endl; + exit(EXIT_FAILURE); + } +#endif // NOTHREADS + break; + case 'h': + print_help(std::cout); + exit(EXIT_SUCCESS); + case 'v': + config.verbose = true; + break; + case 'c': + errno = 0; + config.header_table_size = util::parse_uint_with_unit(optarg); + if (config.header_table_size == -1) { + std::cerr << "-c: Bad option value: " << optarg << std::endl; + exit(EXIT_FAILURE); + } + break; + case 'p': + if (parse_push_config(config, optarg) != 0) { + std::cerr << "-p: Bad option value: " << optarg << std::endl; + } + break; + case '?': + util::show_candidates(argv[optind - 1], long_options); + exit(EXIT_FAILURE); + case 0: + switch (flag) { + case 1: + // no-tls option + config.no_tls = true; + break; + case 2: + // color option + color = true; + break; + case 3: + // version + print_version(std::cout); + exit(EXIT_SUCCESS); + case 4: + // dh-param-file + config.dh_param_file = optarg; + break; + case 5: + // early-response + config.early_response = true; + break; + case 6: { + // trailer option + auto header = optarg; + auto value = strchr(optarg, ':'); + if (!value) { + std::cerr << "--trailer: invalid header: " << optarg << std::endl; + exit(EXIT_FAILURE); + } + *value = 0; + value++; + while (isspace(*value)) { + value++; + } + if (*value == 0) { + // This could also be a valid case for suppressing a header + // similar to curl + std::cerr << "--trailer: invalid header - value missing: " << optarg + << std::endl; + exit(EXIT_FAILURE); + } + config.trailer.emplace_back(header, value, false); + util::inp_strlower(config.trailer.back().name); + break; + } + case 7: + // hexdump option + config.hexdump = true; + break; + case 8: + // echo-upload option + config.echo_upload = true; + break; + } + break; + default: + break; + } + } + if (argc - optind < (config.no_tls ? 1 : 3)) { + print_usage(std::cerr); + std::cerr << "Too few arguments" << std::endl; + exit(EXIT_FAILURE); + } + + config.port = strtol(argv[optind++], nullptr, 10); + + if (!config.no_tls) { + config.private_key_file = argv[optind++]; + config.cert_file = argv[optind++]; + } + + if (config.daemon) { + if (config.htdocs.empty()) { + print_usage(std::cerr); + std::cerr << "-d option must be specified when -D is used." << std::endl; + exit(EXIT_FAILURE); + } + if (daemon(0, 0) == -1) { + perror("daemon"); + exit(EXIT_FAILURE); + } + } + if (config.htdocs.empty()) { + config.htdocs = "./"; + } + + set_color_output(color || isatty(fileno(stdout))); + + struct sigaction act; + memset(&act, 0, sizeof(struct sigaction)); + act.sa_handler = SIG_IGN; + sigaction(SIGPIPE, &act, nullptr); + OPENSSL_config(nullptr); + OpenSSL_add_all_algorithms(); + SSL_load_error_strings(); + SSL_library_init(); +#ifndef NOTHREADS + ssl::LibsslGlobalLock lock; +#endif // NOTHREADS + + reset_timer(); + + HttpServer server(&config); + if (server.run() != 0) { + exit(EXIT_FAILURE); + } + return 0; +} + +} // namespace nghttp2 + +int main(int argc, char **argv) { return nghttp2::main(argc, argv); } diff --git a/src/shrpx-unittest.cc b/src/shrpx-unittest.cc new file mode 100644 index 0000000..86edb1e --- /dev/null +++ b/src/shrpx-unittest.cc @@ -0,0 +1,178 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2013 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifdef HAVE_CONFIG_H +#include +#endif // HAVE_CONFIG_H + +#include +#include +#include +#include +#include +// include test cases' include files here +#include "shrpx_ssl_test.h" +#include "shrpx_downstream_test.h" +#include "shrpx_config_test.h" +#include "http2_test.h" +#include "util_test.h" +#include "nghttp2_gzip_test.h" +#include "buffer_test.h" +#include "memchunk_test.h" +#include "shrpx_config.h" + +static int init_suite1(void) { return 0; } + +static int clean_suite1(void) { return 0; } + +int main(int argc, char *argv[]) { + CU_pSuite pSuite = NULL; + unsigned int num_tests_failed; + + OpenSSL_add_all_algorithms(); + SSL_load_error_strings(); + SSL_library_init(); + + shrpx::create_config(); + + // initialize the CUnit test registry + if (CUE_SUCCESS != CU_initialize_registry()) + return CU_get_error(); + + // add a suite to the registry + pSuite = CU_add_suite("shrpx_TestSuite", init_suite1, clean_suite1); + if (NULL == pSuite) { + CU_cleanup_registry(); + return CU_get_error(); + } + + // add the tests to the suite + if (!CU_add_test(pSuite, "ssl_create_lookup_tree", + shrpx::test_shrpx_ssl_create_lookup_tree) || + !CU_add_test(pSuite, "ssl_cert_lookup_tree_add_cert_from_file", + shrpx::test_shrpx_ssl_cert_lookup_tree_add_cert_from_file) || + !CU_add_test(pSuite, "http2_add_header", shrpx::test_http2_add_header) || + !CU_add_test(pSuite, "http2_get_header", shrpx::test_http2_get_header) || + !CU_add_test(pSuite, "http2_copy_headers_to_nva", + shrpx::test_http2_copy_headers_to_nva) || + !CU_add_test(pSuite, "http2_build_http1_headers_from_headers", + shrpx::test_http2_build_http1_headers_from_headers) || + !CU_add_test(pSuite, "http2_lws", shrpx::test_http2_lws) || + !CU_add_test(pSuite, "http2_rewrite_location_uri", + shrpx::test_http2_rewrite_location_uri) || + !CU_add_test(pSuite, "http2_parse_http_status_code", + shrpx::test_http2_parse_http_status_code) || + !CU_add_test(pSuite, "http2_index_header", + shrpx::test_http2_index_header) || + !CU_add_test(pSuite, "http2_lookup_token", + shrpx::test_http2_lookup_token) || + !CU_add_test(pSuite, "http2_check_http2_pseudo_header", + shrpx::test_http2_check_http2_pseudo_header) || + !CU_add_test(pSuite, "http2_http2_header_allowed", + shrpx::test_http2_http2_header_allowed) || + !CU_add_test(pSuite, "http2_mandatory_request_headers_presence", + shrpx::test_http2_mandatory_request_headers_presence) || + !CU_add_test(pSuite, "http2_parse_link_header", + shrpx::test_http2_parse_link_header) || + !CU_add_test(pSuite, "http2_path_join", shrpx::test_http2_path_join) || + !CU_add_test(pSuite, "downstream_index_request_headers", + shrpx::test_downstream_index_request_headers) || + !CU_add_test(pSuite, "downstream_index_response_headers", + shrpx::test_downstream_index_response_headers) || + !CU_add_test(pSuite, "downstream_get_request_header", + shrpx::test_downstream_get_request_header) || + !CU_add_test(pSuite, "downstream_get_response_header", + shrpx::test_downstream_get_response_header) || + !CU_add_test(pSuite, "downstream_crumble_request_cookie", + shrpx::test_downstream_crumble_request_cookie) || + !CU_add_test(pSuite, "downstream_assemble_request_cookie", + shrpx::test_downstream_assemble_request_cookie) || + !CU_add_test(pSuite, "downstream_rewrite_location_response_header", + shrpx::test_downstream_rewrite_location_response_header) || + !CU_add_test(pSuite, "config_parse_config_str_list", + shrpx::test_shrpx_config_parse_config_str_list) || + !CU_add_test(pSuite, "config_parse_header", + shrpx::test_shrpx_config_parse_header) || + !CU_add_test(pSuite, "config_parse_log_format", + shrpx::test_shrpx_config_parse_log_format) || + !CU_add_test(pSuite, "config_read_tls_ticket_key_file", + shrpx::test_shrpx_config_read_tls_ticket_key_file) || + !CU_add_test(pSuite, "util_streq", shrpx::test_util_streq) || + !CU_add_test(pSuite, "util_strieq", shrpx::test_util_strieq) || + !CU_add_test(pSuite, "util_inp_strlower", + shrpx::test_util_inp_strlower) || + !CU_add_test(pSuite, "util_to_base64", shrpx::test_util_to_base64) || + !CU_add_test(pSuite, "util_to_token68", shrpx::test_util_to_token68) || + !CU_add_test(pSuite, "util_percent_encode_token", + shrpx::test_util_percent_encode_token) || + !CU_add_test(pSuite, "util_percent_encode_path", + shrpx::test_util_percent_encode_path) || + !CU_add_test(pSuite, "util_percent_decode", + shrpx::test_util_percent_decode) || + !CU_add_test(pSuite, "util_quote_string", + shrpx::test_util_quote_string) || + !CU_add_test(pSuite, "util_utox", shrpx::test_util_utox) || + !CU_add_test(pSuite, "util_http_date", shrpx::test_util_http_date) || + !CU_add_test(pSuite, "util_select_h2", shrpx::test_util_select_h2) || + !CU_add_test(pSuite, "util_ipv6_numeric_addr", + shrpx::test_util_ipv6_numeric_addr) || + !CU_add_test(pSuite, "util_utos_with_unit", + shrpx::test_util_utos_with_unit) || + !CU_add_test(pSuite, "util_utos_with_funit", + shrpx::test_util_utos_with_funit) || + !CU_add_test(pSuite, "util_parse_uint_with_unit", + shrpx::test_util_parse_uint_with_unit) || + !CU_add_test(pSuite, "util_parse_uint", shrpx::test_util_parse_uint) || + !CU_add_test(pSuite, "util_parse_duration_with_unit", + shrpx::test_util_parse_duration_with_unit) || + !CU_add_test(pSuite, "util_duration_str", + shrpx::test_util_duration_str) || + !CU_add_test(pSuite, "util_format_duration", + shrpx::test_util_format_duration) || + !CU_add_test(pSuite, "util_starts_with", shrpx::test_util_starts_with) || + !CU_add_test(pSuite, "util_ends_with", shrpx::test_util_ends_with) || + !CU_add_test(pSuite, "gzip_inflate", test_nghttp2_gzip_inflate) || + !CU_add_test(pSuite, "buffer_write", nghttp2::test_buffer_write) || + !CU_add_test(pSuite, "pool_recycle", nghttp2::test_pool_recycle) || + !CU_add_test(pSuite, "memchunk_append", nghttp2::test_memchunks_append) || + !CU_add_test(pSuite, "memchunk_drain", nghttp2::test_memchunks_drain) || + !CU_add_test(pSuite, "memchunk_riovec", nghttp2::test_memchunks_riovec) || + !CU_add_test(pSuite, "memchunk_recycle", + nghttp2::test_memchunks_recycle)) { + CU_cleanup_registry(); + return CU_get_error(); + } + + // Run all tests using the CUnit Basic interface + CU_basic_set_mode(CU_BRM_VERBOSE); + CU_basic_run_tests(); + num_tests_failed = CU_get_number_of_tests_failed(); + CU_cleanup_registry(); + if (CU_get_error() == CUE_SUCCESS) { + return num_tests_failed; + } else { + printf("CUnit Error: %s\n", CU_get_error_msg()); + return CU_get_error(); + } +} diff --git a/src/shrpx.cc b/src/shrpx.cc new file mode 100644 index 0000000..5becb79 --- /dev/null +++ b/src/shrpx.cc @@ -0,0 +1,2165 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "shrpx.h" + +#include +#include +#ifdef HAVE_SYS_SOCKET_H +#include +#endif // HAVE_SYS_SOCKET_H +#include +#ifdef HAVE_NETDB_H +#include +#endif // HAVE_NETDB_H +#include +#ifdef HAVE_NETINET_IN_H +#include +#endif // HAVE_NETINET_IN_H +#include +#ifdef HAVE_ARPA_INET_H +#include +#endif // HAVE_ARPA_INET_H +#ifdef HAVE_UNISTD_H +#include +#endif // HAVE_UNISTD_H +#include +#ifdef HAVE_SYSLOG_H +#include +#endif // HAVE_SYSLOG_H +#include +#ifdef HAVE_LIMITS_H +#include +#endif // HAVE_LIMITS_H +#ifdef HAVE_SYS_TIME_H +#include +#endif // HAVE_SYS_TIME_H +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include + +#include "shrpx_config.h" +#include "shrpx_connection_handler.h" +#include "shrpx_ssl.h" +#include "shrpx_log_config.h" +#include "shrpx_worker.h" +#include "shrpx_accept_handler.h" +#include "shrpx_http2_upstream.h" +#include "shrpx_http2_session.h" +#include "util.h" +#include "app_helper.h" +#include "ssl.h" +#include "template.h" + +extern char **environ; + +using namespace nghttp2; + +namespace shrpx { + +namespace { +const int REOPEN_LOG_SIGNAL = SIGUSR1; +const int EXEC_BINARY_SIGNAL = SIGUSR2; +const int GRACEFUL_SHUTDOWN_SIGNAL = SIGQUIT; +} // namespace + +// Environment variables to tell new binary the listening socket's +// file descriptors. They are not close-on-exec. +#define ENV_LISTENER4_FD "NGHTTPX_LISTENER4_FD" +#define ENV_LISTENER6_FD "NGHTTPX_LISTENER6_FD" + +// Environment variable to tell new binary the port number the current +// binary is listening to. +#define ENV_PORT "NGHTTPX_PORT" + +// Environment variable to tell new binary the listening socket's file +// descriptor if frontend listens UNIX domain socket. +#define ENV_UNIX_FD "NGHTTP2_UNIX_FD" +// Environment variable to tell new binary the UNIX domain socket +// path. +#define ENV_UNIX_PATH "NGHTTP2_UNIX_PATH" + +namespace { +int resolve_hostname(sockaddr_union *addr, size_t *addrlen, + const char *hostname, uint16_t port, int family) { + addrinfo hints; + int rv; + + auto service = util::utos(port); + memset(&hints, 0, sizeof(addrinfo)); + + hints.ai_family = family; + hints.ai_socktype = SOCK_STREAM; +#ifdef AI_ADDRCONFIG + hints.ai_flags |= AI_ADDRCONFIG; +#endif // AI_ADDRCONFIG + addrinfo *res; + + rv = getaddrinfo(hostname, service.c_str(), &hints, &res); + if (rv != 0) { + LOG(FATAL) << "Unable to resolve address for " << hostname << ": " + << gai_strerror(rv); + return -1; + } + + char host[NI_MAXHOST]; + rv = getnameinfo(res->ai_addr, res->ai_addrlen, host, sizeof(host), 0, 0, + NI_NUMERICHOST); + if (rv != 0) { + LOG(FATAL) << "Address resolution for " << hostname + << " failed: " << gai_strerror(rv); + + freeaddrinfo(res); + + return -1; + } + + if (LOG_ENABLED(INFO)) { + LOG(INFO) << "Address resolution for " << hostname + << " succeeded: " << host; + } + + memcpy(addr, res->ai_addr, res->ai_addrlen); + *addrlen = res->ai_addrlen; + freeaddrinfo(res); + return 0; +} +} // namespace + +namespace { +void close_env_fd(std::initializer_list envnames) { + for (auto envname : envnames) { + auto envfd = getenv(envname); + if (!envfd) { + continue; + } + auto fd = strtol(envfd, nullptr, 10); + close(fd); + } +} +} // namespace + +namespace { +std::unique_ptr +create_unix_domain_acceptor(ConnectionHandler *handler) { + auto path = get_config()->host.get(); + auto pathlen = strlen(path); + { + auto envfd = getenv(ENV_UNIX_FD); + auto envpath = getenv(ENV_UNIX_PATH); + if (envfd && envpath) { + auto fd = strtoul(envfd, nullptr, 10); + + if (util::streq(envpath, path)) { + LOG(NOTICE) << "Listening on UNIX domain socket " << path; + + return make_unique(fd, handler); + } + + LOG(WARN) << "UNIX domain socket path was changed between old binary (" + << envpath << ") and new binary (" << path << ")"; + close(fd); + } + } + +#ifdef SOCK_NONBLOCK + auto fd = socket(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK, 0); + if (fd == -1) { + return nullptr; + } +#else // !SOCK_NONBLOCK + auto fd = socket(AF_UNIX, SOCK_STREAM, 0); + if (fd == -1) { + return nullptr; + } + util::make_socket_nonblocking(fd); +#endif // !SOCK_NONBLOCK + int val = 1; + if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &val, + static_cast(sizeof(val))) == -1) { + close(fd); + return nullptr; + } + + sockaddr_union addr; + addr.un.sun_family = AF_UNIX; + if (pathlen + 1 > sizeof(addr.un.sun_path)) { + LOG(FATAL) << "UNIX domain socket path " << path << " is too long > " + << sizeof(addr.un.sun_path); + close(fd); + return nullptr; + } + // copy path including terminal NULL + std::copy_n(path, pathlen + 1, addr.un.sun_path); + + // unlink (remove) already existing UNIX domain socket path + unlink(path); + + if (bind(fd, &addr.sa, sizeof(addr.un)) != 0) { + auto error = errno; + LOG(FATAL) << "Failed to bind UNIX domain socket, error=" << error; + close(fd); + return nullptr; + } + + if (listen(fd, get_config()->backlog) != 0) { + auto error = errno; + LOG(FATAL) << "Failed to listen to UNIX domain socket, error=" << error; + close(fd); + return nullptr; + } + + LOG(NOTICE) << "Listening on UNIX domain socket " << path; + + return make_unique(fd, handler); +} +} // namespace + +namespace { +std::unique_ptr create_acceptor(ConnectionHandler *handler, + int family) { + { + auto envfd = + getenv(family == AF_INET ? ENV_LISTENER4_FD : ENV_LISTENER6_FD); + auto envport = getenv(ENV_PORT); + + if (envfd && envport) { + auto fd = strtoul(envfd, nullptr, 10); + auto port = strtoul(envport, nullptr, 10); + + // Only do this iff NGHTTPX_PORT == get_config()->port. + // Otherwise, close fd, and create server socket as usual. + + if (port == get_config()->port) { + LOG(NOTICE) << "Listening on port " << get_config()->port; + + return make_unique(fd, handler); + } + + LOG(WARN) << "Port was changed between old binary (" << port + << ") and new binary (" << get_config()->port << ")"; + close(fd); + } + } + + addrinfo hints; + int fd = -1; + int rv; + + auto service = util::utos(get_config()->port); + memset(&hints, 0, sizeof(addrinfo)); + hints.ai_family = family; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_PASSIVE; +#ifdef AI_ADDRCONFIG + hints.ai_flags |= AI_ADDRCONFIG; +#endif // AI_ADDRCONFIG + + auto node = strcmp("*", get_config()->host.get()) == 0 + ? nullptr + : get_config()->host.get(); + + addrinfo *res, *rp; + rv = getaddrinfo(node, service.c_str(), &hints, &res); + if (rv != 0) { + if (LOG_ENABLED(INFO)) { + LOG(INFO) << "Unable to get IPv" << (family == AF_INET ? "4" : "6") + << " address for " << get_config()->host.get() << ": " + << gai_strerror(rv); + } + return nullptr; + } + for (rp = res; rp; rp = rp->ai_next) { +#ifdef SOCK_NONBLOCK + fd = + socket(rp->ai_family, rp->ai_socktype | SOCK_NONBLOCK, rp->ai_protocol); + if (fd == -1) { + continue; + } +#else // !SOCK_NONBLOCK + fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); + if (fd == -1) { + continue; + } + util::make_socket_nonblocking(fd); +#endif // !SOCK_NONBLOCK + int val = 1; + if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &val, + static_cast(sizeof(val))) == -1) { + close(fd); + continue; + } + +#ifdef IPV6_V6ONLY + if (family == AF_INET6) { + if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &val, + static_cast(sizeof(val))) == -1) { + close(fd); + continue; + } + } +#endif // IPV6_V6ONLY + +#ifdef TCP_DEFER_ACCEPT + val = 3; + if (setsockopt(fd, IPPROTO_TCP, TCP_DEFER_ACCEPT, &val, + static_cast(sizeof(val))) == -1) { + LOG(WARN) << "Failed to set TCP_DEFER_ACCEPT option to listener socket"; + } +#endif // TCP_DEFER_ACCEPT + + if (bind(fd, rp->ai_addr, rp->ai_addrlen) == 0 && + listen(fd, get_config()->backlog) == 0) { + break; + } + close(fd); + } + + if (!rp) { + LOG(WARN) << "Listening " << (family == AF_INET ? "IPv4" : "IPv6") + << " socket failed"; + + freeaddrinfo(res); + + return nullptr; + } + + char host[NI_MAXHOST]; + rv = getnameinfo(rp->ai_addr, rp->ai_addrlen, host, sizeof(host), nullptr, 0, + NI_NUMERICHOST); + + freeaddrinfo(res); + + if (rv != 0) { + LOG(WARN) << gai_strerror(rv); + + close(fd); + + return nullptr; + } + + LOG(NOTICE) << "Listening on " << host << ", port " << get_config()->port; + + return make_unique(fd, handler); +} +} // namespace + +namespace { +void drop_privileges() { + if (getuid() == 0 && get_config()->uid != 0) { + if (initgroups(get_config()->user.get(), get_config()->gid) != 0) { + auto error = errno; + LOG(FATAL) << "Could not change supplementary groups: " + << strerror(error); + exit(EXIT_FAILURE); + } + if (setgid(get_config()->gid) != 0) { + auto error = errno; + LOG(FATAL) << "Could not change gid: " << strerror(error); + exit(EXIT_FAILURE); + } + if (setuid(get_config()->uid) != 0) { + auto error = errno; + LOG(FATAL) << "Could not change uid: " << strerror(error); + exit(EXIT_FAILURE); + } + if (setuid(0) != -1) { + LOG(FATAL) << "Still have root privileges?"; + exit(EXIT_FAILURE); + } + } +} +} // namespace + +namespace { +void save_pid() { + std::ofstream out(get_config()->pid_file.get(), std::ios::binary); + out << get_config()->pid << "\n"; + out.close(); + if (!out) { + LOG(ERROR) << "Could not save PID to file " << get_config()->pid_file.get(); + exit(EXIT_FAILURE); + } + + if (get_config()->uid != 0) { + if (chown(get_config()->pid_file.get(), get_config()->uid, + get_config()->gid) == -1) { + auto error = errno; + LOG(WARN) << "Changing owner of pid file " << get_config()->pid_file.get() + << " failed: " << strerror(error); + } + } +} +} // namespace + +namespace { +void reopen_log_signal_cb(struct ev_loop *loop, ev_signal *w, int revents) { + auto conn_handler = static_cast(w->data); + + LOG(NOTICE) << "Reopening log files: main"; + + (void)reopen_log_files(); + redirect_stderr_to_errorlog(); + + if (get_config()->num_worker > 1) { + conn_handler->worker_reopen_log_files(); + } +} +} // namespace + +namespace { +void exec_binary_signal_cb(struct ev_loop *loop, ev_signal *w, int revents) { + auto conn_handler = static_cast(w->data); + + LOG(NOTICE) << "Executing new binary"; + + auto pid = fork(); + + if (pid == -1) { + auto error = errno; + LOG(ERROR) << "fork() failed errno=" << error; + return; + } + + if (pid != 0) { + return; + } + + auto exec_path = util::get_exec_path(get_config()->argc, get_config()->argv, + get_config()->cwd); + + if (!exec_path) { + LOG(ERROR) << "Could not resolve the executable path"; + return; + } + + auto argv = make_unique(get_config()->argc + 1); + + argv[0] = exec_path; + for (int i = 1; i < get_config()->argc; ++i) { + argv[i] = strdup(get_config()->argv[i]); + } + argv[get_config()->argc] = nullptr; + + size_t envlen = 0; + for (char **p = environ; *p; ++p, ++envlen) + ; + // 3 for missing (fd4, fd6 and port) or (unix fd and unix path) + auto envp = make_unique(envlen + 3 + 1); + size_t envidx = 0; + + if (get_config()->host_unix) { + auto acceptor = conn_handler->get_acceptor(); + std::string fd = ENV_UNIX_FD "="; + fd += util::utos(acceptor->get_fd()); + envp[envidx++] = strdup(fd.c_str()); + + std::string path = ENV_UNIX_PATH "="; + path += get_config()->host.get(); + envp[envidx++] = strdup(path.c_str()); + } else { + auto acceptor4 = conn_handler->get_acceptor(); + if (acceptor4) { + std::string fd4 = ENV_LISTENER4_FD "="; + fd4 += util::utos(acceptor4->get_fd()); + envp[envidx++] = strdup(fd4.c_str()); + } + + auto acceptor6 = conn_handler->get_acceptor6(); + if (acceptor6) { + std::string fd6 = ENV_LISTENER6_FD "="; + fd6 += util::utos(acceptor6->get_fd()); + envp[envidx++] = strdup(fd6.c_str()); + } + + std::string port = ENV_PORT "="; + port += util::utos(get_config()->port); + envp[envidx++] = strdup(port.c_str()); + } + + for (size_t i = 0; i < envlen; ++i) { + if (util::startsWith(environ[i], ENV_LISTENER4_FD) || + util::startsWith(environ[i], ENV_LISTENER6_FD) || + util::startsWith(environ[i], ENV_PORT) || + util::startsWith(environ[i], ENV_UNIX_FD) || + util::startsWith(environ[i], ENV_UNIX_PATH)) { + continue; + } + + envp[envidx++] = environ[i]; + } + + envp[envidx++] = nullptr; + + if (LOG_ENABLED(INFO)) { + LOG(INFO) << "cmdline"; + for (int i = 0; argv[i]; ++i) { + LOG(INFO) << i << ": " << argv[i]; + } + LOG(INFO) << "environ"; + for (int i = 0; envp[i]; ++i) { + LOG(INFO) << i << ": " << envp[i]; + } + } + + if (execve(argv[0], argv.get(), envp.get()) == -1) { + auto error = errno; + LOG(ERROR) << "execve failed: errno=" << error; + _Exit(EXIT_FAILURE); + } +} +} // namespace + +namespace { +void graceful_shutdown_signal_cb(struct ev_loop *loop, ev_signal *w, + int revents) { + auto conn_handler = static_cast(w->data); + + if (conn_handler->get_graceful_shutdown()) { + return; + } + + LOG(NOTICE) << "Graceful shutdown signal received"; + + conn_handler->set_graceful_shutdown(true); + + conn_handler->disable_acceptor(); + + // After disabling accepting new connection, disptach incoming + // connection in backlog. + + conn_handler->accept_pending_connection(); + + conn_handler->graceful_shutdown_worker(); + + if (get_config()->num_worker == 1 && + conn_handler->get_single_worker()->get_worker_stat()->num_connections > + 0) { + return; + } + + // We have accepted all pending connections. Shutdown main event + // loop. + ev_break(loop); +} +} // namespace + +namespace { +void renew_ticket_key_cb(struct ev_loop *loop, ev_timer *w, int revents) { + auto conn_handler = static_cast(w->data); + const auto &old_ticket_keys = conn_handler->get_ticket_keys(); + + auto ticket_keys = std::make_shared(); + LOG(NOTICE) << "Renew ticket keys: main"; + + // We store at most 2 ticket keys + if (old_ticket_keys) { + auto &old_keys = old_ticket_keys->keys; + auto &new_keys = ticket_keys->keys; + + assert(!old_keys.empty()); + + new_keys.resize(2); + new_keys[1] = old_keys[0]; + } else { + ticket_keys->keys.resize(1); + } + + if (RAND_bytes(reinterpret_cast(&ticket_keys->keys[0]), + sizeof(ticket_keys->keys[0])) == 0) { + if (LOG_ENABLED(INFO)) { + LOG(INFO) << "failed to renew ticket key"; + } + return; + } + + if (LOG_ENABLED(INFO)) { + LOG(INFO) << "ticket keys generation done"; + for (auto &key : ticket_keys->keys) { + LOG(INFO) << "name: " << util::format_hex(key.name, sizeof(key.name)); + } + } + + conn_handler->set_ticket_keys(ticket_keys); + conn_handler->worker_renew_ticket_keys(ticket_keys); +} +} // namespace + +namespace { +int event_loop() { + auto loop = EV_DEFAULT; + + auto conn_handler = make_unique(loop); + if (get_config()->daemon) { + if (daemon(0, 0) == -1) { + auto error = errno; + LOG(FATAL) << "Failed to daemonize: " << strerror(error); + exit(EXIT_FAILURE); + } + + // We get new PID after successful daemon(). + mod_config()->pid = getpid(); + + // daemon redirects stderr file descriptor to /dev/null, so we + // need this. + redirect_stderr_to_errorlog(); + } + + if (get_config()->pid_file) { + save_pid(); + } + + if (get_config()->host_unix) { + close_env_fd({ENV_LISTENER4_FD, ENV_LISTENER6_FD}); + auto acceptor = create_unix_domain_acceptor(conn_handler.get()); + if (!acceptor) { + LOG(FATAL) << "Failed to listen on UNIX domain socket " + << get_config()->host.get(); + exit(EXIT_FAILURE); + } + + conn_handler->set_acceptor(std::move(acceptor)); + } else { + close_env_fd({ENV_UNIX_FD}); + auto acceptor6 = create_acceptor(conn_handler.get(), AF_INET6); + auto acceptor4 = create_acceptor(conn_handler.get(), AF_INET); + if (!acceptor6 && !acceptor4) { + LOG(FATAL) << "Failed to listen on address " << get_config()->host.get() + << ", port " << get_config()->port; + exit(EXIT_FAILURE); + } + + conn_handler->set_acceptor(std::move(acceptor4)); + conn_handler->set_acceptor6(std::move(acceptor6)); + } + + ev_timer renew_ticket_key_timer; + if (!get_config()->upstream_no_tls) { + bool auto_tls_ticket_key = true; + if (!get_config()->tls_ticket_key_files.empty()) { + auto ticket_keys = + read_tls_ticket_key_file(get_config()->tls_ticket_key_files); + if (!ticket_keys) { + LOG(WARN) << "Use internal session ticket key generator"; + } else { + conn_handler->set_ticket_keys(std::move(ticket_keys)); + auto_tls_ticket_key = false; + } + } + if (auto_tls_ticket_key) { + // Renew ticket key every 12hrs + ev_timer_init(&renew_ticket_key_timer, renew_ticket_key_cb, 0., + 12 * 3600.); + renew_ticket_key_timer.data = conn_handler.get(); + ev_timer_again(loop, &renew_ticket_key_timer); + + // Generate first session ticket key before running workers. + renew_ticket_key_cb(loop, &renew_ticket_key_timer, 0); + } + } + + // ListenHandler loads private key, and we listen on a priveleged port. + // After that, we drop the root privileges if needed. + drop_privileges(); + +#ifndef NOTHREADS + int rv; + sigset_t signals; + sigemptyset(&signals); + sigaddset(&signals, REOPEN_LOG_SIGNAL); + sigaddset(&signals, EXEC_BINARY_SIGNAL); + sigaddset(&signals, GRACEFUL_SHUTDOWN_SIGNAL); + rv = pthread_sigmask(SIG_BLOCK, &signals, nullptr); + if (rv != 0) { + LOG(ERROR) << "Blocking signals failed: " << strerror(rv); + } +#endif // !NOTHREADS + + if (get_config()->num_worker == 1) { + conn_handler->create_single_worker(); + } else { + conn_handler->create_worker_thread(get_config()->num_worker); + } + +#ifndef NOTHREADS + rv = pthread_sigmask(SIG_UNBLOCK, &signals, nullptr); + if (rv != 0) { + LOG(ERROR) << "Unblocking signals failed: " << strerror(rv); + } +#endif // !NOTHREADS + + ev_signal reopen_log_sig; + ev_signal_init(&reopen_log_sig, reopen_log_signal_cb, REOPEN_LOG_SIGNAL); + reopen_log_sig.data = conn_handler.get(); + ev_signal_start(loop, &reopen_log_sig); + + ev_signal exec_bin_sig; + ev_signal_init(&exec_bin_sig, exec_binary_signal_cb, EXEC_BINARY_SIGNAL); + exec_bin_sig.data = conn_handler.get(); + ev_signal_start(loop, &exec_bin_sig); + + ev_signal graceful_shutdown_sig; + ev_signal_init(&graceful_shutdown_sig, graceful_shutdown_signal_cb, + GRACEFUL_SHUTDOWN_SIGNAL); + graceful_shutdown_sig.data = conn_handler.get(); + ev_signal_start(loop, &graceful_shutdown_sig); + + if (!get_config()->upstream_no_tls && !get_config()->no_ocsp) { + conn_handler->proceed_next_cert_ocsp(); + } + + if (LOG_ENABLED(INFO)) { + LOG(INFO) << "Entering event loop"; + } + + ev_run(loop, 0); + + conn_handler->join_worker(); + conn_handler->cancel_ocsp_update(); + + return 0; +} +} // namespace + +namespace { +// Returns true if regular file or symbolic link |path| exists. +bool conf_exists(const char *path) { + struct stat buf; + int rv = stat(path, &buf); + return rv == 0 && (buf.st_mode & (S_IFREG | S_IFLNK)); +} +} // namespace + +namespace { +const char *DEFAULT_NPN_LIST = "h2,h2-16,h2-14," +#ifdef HAVE_SPDYLAY + "spdy/3.1," +#endif // HAVE_SPDYLAY + "http/1.1"; +} // namespace + +namespace { +const char *DEFAULT_TLS_PROTO_LIST = "TLSv1.2,TLSv1.1"; +} // namespace + +namespace { +const char *DEFAULT_ACCESSLOG_FORMAT = "$remote_addr - - [$time_local] " + "\"$request\" $status $body_bytes_sent " + "\"$http_referer\" \"$http_user_agent\""; +} // namespace + +namespace { +auto DEFAULT_DOWNSTREAM_HOST = "127.0.0.1"; +int16_t DEFAULT_DOWNSTREAM_PORT = 80; +} // namespace; + +namespace { +void fill_default_config() { + memset(mod_config(), 0, sizeof(*mod_config())); + + mod_config()->verbose = false; + mod_config()->daemon = false; + + mod_config()->server_name = "nghttpx nghttp2/" NGHTTP2_VERSION; + mod_config()->host = strcopy("*"); + mod_config()->port = 3000; + mod_config()->private_key_file = nullptr; + mod_config()->private_key_passwd = nullptr; + mod_config()->cert_file = nullptr; + + // Read timeout for HTTP2 upstream connection + mod_config()->http2_upstream_read_timeout = 180.; + + // Read timeout for non-HTTP2 upstream connection + mod_config()->upstream_read_timeout = 180.; + + // Write timeout for HTTP2/non-HTTP2 upstream connection + mod_config()->upstream_write_timeout = 30.; + + // Read/Write timeouts for downstream connection + mod_config()->downstream_read_timeout = 180.; + mod_config()->downstream_write_timeout = 30.; + + // Read timeout for HTTP/2 stream + mod_config()->stream_read_timeout = 0.; + + // Write timeout for HTTP/2 stream + mod_config()->stream_write_timeout = 0.; + + // Timeout for pooled (idle) connections + mod_config()->downstream_idle_read_timeout = 2.; + + // window bits for HTTP/2 and SPDY upstream/downstream connection + // per stream. 2**16-1 = 64KiB-1, which is HTTP/2 default. Please + // note that SPDY/3 default is 64KiB. + mod_config()->http2_upstream_window_bits = 16; + mod_config()->http2_downstream_window_bits = 16; + + // HTTP/2 SPDY/3.1 has connection-level flow control. The default + // window size for HTTP/2 is 64KiB - 1. SPDY/3's default is 64KiB + mod_config()->http2_upstream_connection_window_bits = 16; + mod_config()->http2_downstream_connection_window_bits = 16; + + mod_config()->upstream_no_tls = false; + mod_config()->downstream_no_tls = false; + + mod_config()->num_worker = 1; + mod_config()->http2_max_concurrent_streams = 100; + mod_config()->add_x_forwarded_for = false; + mod_config()->strip_incoming_x_forwarded_for = false; + mod_config()->no_via = false; + mod_config()->accesslog_file = nullptr; + mod_config()->accesslog_syslog = false; + mod_config()->accesslog_format = parse_log_format(DEFAULT_ACCESSLOG_FORMAT); +#if defined(__ANDROID__) || defined(ANDROID) + // Android does not have /dev/stderr. Use /proc/self/fd/2 instead. + mod_config()->errorlog_file = strcopy("/proc/self/fd/2"); +#else // !__ANDROID__ && ANDROID + mod_config()->errorlog_file = strcopy("/dev/stderr"); +#endif // !__ANDROID__ && ANDROID + mod_config()->errorlog_syslog = false; + mod_config()->conf_path = strcopy("/etc/nghttpx/nghttpx.conf"); + mod_config()->syslog_facility = LOG_DAEMON; + // Default accept() backlog + mod_config()->backlog = 512; + mod_config()->ciphers = nullptr; + mod_config()->http2_proxy = false; + mod_config()->http2_bridge = false; + mod_config()->client_proxy = false; + mod_config()->client = false; + mod_config()->client_mode = false; + mod_config()->insecure = false; + mod_config()->cacert = nullptr; + mod_config()->pid_file = nullptr; + mod_config()->user = nullptr; + mod_config()->uid = 0; + mod_config()->gid = 0; + mod_config()->pid = getpid(); + mod_config()->backend_ipv4 = false; + mod_config()->backend_ipv6 = false; + mod_config()->downstream_http_proxy_userinfo = nullptr; + mod_config()->downstream_http_proxy_host = nullptr; + mod_config()->downstream_http_proxy_port = 0; + mod_config()->downstream_http_proxy_addrlen = 0; + mod_config()->read_rate = 0; + mod_config()->read_burst = 0; + mod_config()->write_rate = 0; + mod_config()->write_burst = 0; + mod_config()->worker_read_rate = 0; + mod_config()->worker_read_burst = 0; + mod_config()->worker_write_rate = 0; + mod_config()->worker_write_burst = 0; + mod_config()->verify_client = false; + mod_config()->verify_client_cacert = nullptr; + mod_config()->client_private_key_file = nullptr; + mod_config()->client_cert_file = nullptr; + mod_config()->http2_upstream_dump_request_header = nullptr; + mod_config()->http2_upstream_dump_response_header = nullptr; + mod_config()->http2_no_cookie_crumbling = false; + mod_config()->upstream_frame_debug = false; + mod_config()->padding = 0; + mod_config()->worker_frontend_connections = 0; + + mod_config()->http2_upstream_callbacks = create_http2_upstream_callbacks(); + mod_config()->http2_downstream_callbacks = + create_http2_downstream_callbacks(); + + nghttp2_option_new(&mod_config()->http2_option); + nghttp2_option_set_no_auto_window_update(get_config()->http2_option, 1); + nghttp2_option_set_no_recv_client_magic(get_config()->http2_option, 1); + + nghttp2_option_new(&mod_config()->http2_client_option); + nghttp2_option_set_no_auto_window_update(get_config()->http2_client_option, + 1); + nghttp2_option_set_peer_max_concurrent_streams( + get_config()->http2_client_option, 100); + + mod_config()->tls_proto_mask = 0; + mod_config()->no_location_rewrite = false; + mod_config()->no_host_rewrite = false; + mod_config()->argc = 0; + mod_config()->argv = nullptr; + mod_config()->downstream_connections_per_host = 8; + mod_config()->downstream_connections_per_frontend = 0; + mod_config()->listener_disable_timeout = 0.; + mod_config()->downstream_request_buffer_size = 16 * 1024; + mod_config()->downstream_response_buffer_size = 16 * 1024; + mod_config()->no_server_push = false; + mod_config()->host_unix = false; + mod_config()->http2_downstream_connections_per_worker = 0; + // ocsp update interval = 14400 secs = 4 hours, borrowed from h2o + mod_config()->ocsp_update_interval = 14400.; + mod_config()->fetch_ocsp_response_file = + strcopy(PKGDATADIR "/fetch-ocsp-response"); + mod_config()->no_ocsp = false; + mod_config()->header_field_buffer = 64 * 1024; + mod_config()->max_header_fields = 100; +} +} // namespace + +namespace { +void print_version(std::ostream &out) { + out << get_config()->server_name << std::endl; +} +} // namespace + +namespace { +void print_usage(std::ostream &out) { + out << R"(Usage: nghttpx [OPTIONS]... [ ] +A reverse proxy for HTTP/2, HTTP/1 and SPDY.)" << std::endl; +} +} // namespace + +namespace { +void print_help(std::ostream &out) { + print_usage(out); + out << R"( + + Set path to server's private key. Required unless -p, + --client or --frontend-no-tls are given. + Set path to server's certificate. Required unless -p, + --client or --frontend-no-tls are given. To make OCSP + stapling work, this must be absolute path. + +Options: + The options are categorized into several groups. + +Connections: + -b, --backend= + Set backend host and port. The multiple backend + addresses are accepted by repeating this option. UNIX + domain socket can be specified by prefixing path name + with "unix:" (e.g., unix:/var/run/backend.sock) + Default: )" << DEFAULT_DOWNSTREAM_HOST << "," + << DEFAULT_DOWNSTREAM_PORT << R"( + -f, --frontend= + Set frontend host and port. If is '*', it + assumes all addresses including both IPv4 and IPv6. + UNIX domain socket can be specified by prefixing path + name with "unix:" (e.g., unix:/var/run/nghttpx.sock) + Default: )" << get_config()->host.get() << "," + << get_config()->port << R"( + --backlog= + Set listen backlog size. + Default: )" << get_config()->backlog << R"( + --backend-ipv4 + Resolve backend hostname to IPv4 address only. + --backend-ipv6 + Resolve backend hostname to IPv6 address only. + --backend-http-proxy-uri= + Specify proxy URI in the form + http://[:@]:. If a proxy + requires authentication, specify and . + Note that they must be properly percent-encoded. This + proxy is used when the backend connection is HTTP/2. + First, make a CONNECT request to the proxy and it + connects to the backend on behalf of nghttpx. This + forms tunnel. After that, nghttpx performs SSL/TLS + handshake with the downstream through the tunnel. The + timeouts when connecting and making CONNECT request can + be specified by --backend-read-timeout and + --backend-write-timeout options. + +Performance: + -n, --workers= + Set the number of worker threads. + Default: )" << get_config()->num_worker << R"( + --read-rate= + Set maximum average read rate on frontend connection. + Setting 0 to this option means read rate is unlimited. + Default: )" << get_config()->read_rate << R"( + --read-burst= + Set maximum read burst size on frontend connection. + Setting 0 to this option means read burst size is + unlimited. + Default: )" << get_config()->read_burst << R"( + --write-rate= + Set maximum average write rate on frontend connection. + Setting 0 to this option means write rate is unlimited. + Default: )" << get_config()->write_rate << R"( + --write-burst= + Set maximum write burst size on frontend connection. + Setting 0 to this option means write burst size is + unlimited. + Default: )" << get_config()->write_burst << R"( + --worker-read-rate= + Set maximum average read rate on frontend connection per + worker. Setting 0 to this option means read rate is + unlimited. Not implemented yet. + Default: )" << get_config()->worker_read_rate << R"( + --worker-read-burst= + Set maximum read burst size on frontend connection per + worker. Setting 0 to this option means read burst size + is unlimited. Not implemented yet. + Default: )" << get_config()->worker_read_burst << R"( + --worker-write-rate= + Set maximum average write rate on frontend connection + per worker. Setting 0 to this option means write rate + is unlimited. Not implemented yet. + Default: )" << get_config()->worker_write_rate << R"( + --worker-write-burst= + Set maximum write burst size on frontend connection per + worker. Setting 0 to this option means write burst size + is unlimited. Not implemented yet. + Default: )" << get_config()->worker_write_burst << R"( + --worker-frontend-connections= + Set maximum number of simultaneous connections frontend + accepts. Setting 0 means unlimited. + Default: )" << get_config()->worker_frontend_connections << R"( + --backend-http2-connections-per-worker= + Set maximum number of HTTP/2 connections per worker. + The default value is 0, which means the number of + backend addresses specified by -b option. + --backend-http1-connections-per-host= + Set maximum number of backend concurrent HTTP/1 + connections per host. This option is meaningful when -s + option is used. To limit the number of connections per + frontend for default mode, use + --backend-http1-connections-per-frontend. + Default: )" << get_config()->downstream_connections_per_host + << R"( + --backend-http1-connections-per-frontend= + Set maximum number of backend concurrent HTTP/1 + connections per frontend. This option is only used for + default mode. 0 means unlimited. To limit the number + of connections per host for HTTP/2 or SPDY proxy mode + (-s option), use --backend-http1-connections-per-host. + Default: )" << get_config()->downstream_connections_per_frontend + << R"( + --rlimit-nofile= + Set maximum number of open files (RLIMIT_NOFILE) to . + If 0 is given, nghttpx does not set the limit. + Default: )" << get_config()->rlimit_nofile << R"( + --backend-request-buffer= + Set buffer size used to store backend request. + Default: )" + << util::utos_with_unit(get_config()->downstream_request_buffer_size) + << R"( + --backend-response-buffer= + Set buffer size used to store backend response. + Default: )" + << util::utos_with_unit(get_config()->downstream_response_buffer_size) + << R"( + +Timeout: + --frontend-http2-read-timeout= + Specify read timeout for HTTP/2 and SPDY frontend + connection. + Default: )" + << util::duration_str(get_config()->http2_upstream_read_timeout) << R"( + --frontend-read-timeout= + Specify read timeout for HTTP/1.1 frontend connection. + Default: )" + << util::duration_str(get_config()->upstream_read_timeout) << R"( + --frontend-write-timeout= + Specify write timeout for all frontend connections. + Default: )" + << util::duration_str(get_config()->upstream_write_timeout) << R"( + --stream-read-timeout= + Specify read timeout for HTTP/2 and SPDY streams. 0 + means no timeout. + Default: )" + << util::duration_str(get_config()->stream_read_timeout) << R"( + --stream-write-timeout= + Specify write timeout for HTTP/2 and SPDY streams. 0 + means no timeout. + Default: )" + << util::duration_str(get_config()->stream_write_timeout) << R"( + --backend-read-timeout= + Specify read timeout for backend connection. + Default: )" + << util::duration_str(get_config()->downstream_read_timeout) << R"( + --backend-write-timeout= + Specify write timeout for backend connection. + Default: )" + << util::duration_str(get_config()->downstream_write_timeout) << R"( + --backend-keep-alive-timeout= + Specify keep-alive timeout for backend connection. + Default: )" + << util::duration_str(get_config()->downstream_idle_read_timeout) << R"( + --listener-disable-timeout= + After accepting connection failed, connection listener + is disabled for a given amount of time. Specifying 0 + disables this feature. + Default: )" + << util::duration_str(get_config()->listener_disable_timeout) << R"( + +SSL/TLS: + --ciphers= + Set allowed cipher list. The format of the string is + described in OpenSSL ciphers(1). + -k, --insecure + Don't verify backend server's certificate if -p, + --client or --http2-bridge are given and + --backend-no-tls is not given. + --cacert= + Set path to trusted CA certificate file if -p, --client + or --http2-bridge are given and --backend-no-tls is not + given. The file must be in PEM format. It can contain + multiple certificates. If the linked OpenSSL is + configured to load system wide certificates, they are + loaded at startup regardless of this option. + --private-key-passwd-file= + Path to file that contains password for the server's + private key. If none is given and the private key is + password protected it'll be requested interactively. + --subcert=: + Specify additional certificate and private key file. + nghttpx will choose certificates based on the hostname + indicated by client using TLS SNI extension. This + option can be used multiple times. To make OCSP + stapling work, must be absolute path. + --backend-tls-sni-field= + Explicitly set the content of the TLS SNI extension. + This will default to the backend HOST name. + --dh-param-file= + Path to file that contains DH parameters in PEM format. + Without this option, DHE cipher suites are not + available. + --npn-list= + Comma delimited list of ALPN protocol identifier sorted + in the order of preference. That means most desirable + protocol comes first. This is used in both ALPN and + NPN. The parameter must be delimited by a single comma + only and any white spaces are treated as a part of + protocol string. + Default: )" << DEFAULT_NPN_LIST << R"( + --verify-client + Require and verify client certificate. + --verify-client-cacert= + Path to file that contains CA certificates to verify + client certificate. The file must be in PEM format. It + can contain multiple certificates. + --client-private-key-file= + Path to file that contains client private key used in + backend client authentication. + --client-cert-file= + Path to file that contains client certificate used in + backend client authentication. + --tls-proto-list= + Comma delimited list of SSL/TLS protocol to be enabled. + The following protocols are available: TLSv1.2, TLSv1.1 + and TLSv1.0. The name matching is done in + case-insensitive manner. The parameter must be + delimited by a single comma only and any white spaces + are treated as a part of protocol string. + Default: )" << DEFAULT_TLS_PROTO_LIST << R"( + --tls-ticket-key-file= + Path to file that contains 48 bytes random data to + construct TLS session ticket parameters. This options + can be used repeatedly to specify multiple ticket + parameters. If several files are given, only the first + key is used to encrypt TLS session tickets. Other keys + are accepted but server will issue new session ticket + with first key. This allows session key rotation. + Please note that key rotation does not occur + automatically. User should rearrange files or change + options values and restart nghttpx gracefully. If + opening or reading given file fails, all loaded keys are + discarded and it is treated as if none of this option is + given. If this option is not given or an error occurred + while opening or reading a file, key is generated + automatically and renewed every 12hrs. At most 2 keys + are stored in memory. + --fetch-ocsp-response-file= + Path to fetch-ocsp-response script file. It should be + absolute path. + Default: )" << get_config()->fetch_ocsp_response_file.get() << R"( + --ocsp-update-interval= + Set interval to update OCSP response cache. + Default: )" + << util::duration_str(get_config()->ocsp_update_interval) << R"( + --no-ocsp Disable OCSP stapling. + +HTTP/2 and SPDY: + -c, --http2-max-concurrent-streams= + Set the maximum number of the concurrent streams in one + HTTP/2 and SPDY session. + Default: )" << get_config()->http2_max_concurrent_streams << R"( + --frontend-http2-window-bits= + Sets the per-stream initial window size of HTTP/2 SPDY + frontend connection. For HTTP/2, the size is 2**-1. + For SPDY, the size is 2**. + Default: )" << get_config()->http2_upstream_window_bits << R"( + --frontend-http2-connection-window-bits= + Sets the per-connection window size of HTTP/2 and SPDY + frontend connection. For HTTP/2, the size is + 2**-1. For SPDY, the size is 2**. + Default: )" << get_config()->http2_upstream_connection_window_bits + << R"( + --frontend-no-tls + Disable SSL/TLS on frontend connections. + --backend-http2-window-bits= + Sets the initial window size of HTTP/2 backend + connection to 2**-1. + Default: )" << get_config()->http2_downstream_window_bits << R"( + --backend-http2-connection-window-bits= + Sets the per-connection window size of HTTP/2 backend + connection to 2**-1. + Default: )" + << get_config()->http2_downstream_connection_window_bits << R"( + --backend-no-tls + Disable SSL/TLS on backend connections. + --http2-no-cookie-crumbling + Don't crumble cookie header field. + --padding= + Add at most bytes to a HTTP/2 frame payload as + padding. Specify 0 to disable padding. This option is + meant for debugging purpose and not intended to enhance + protocol security. + --no-server-push + Disable HTTP/2 server push. Server push is only + supported by default mode and HTTP/2 frontend. SPDY + frontend does not support server push. + +Mode: + (default mode) + Accept HTTP/2, SPDY and HTTP/1.1 over SSL/TLS. If + --frontend-no-tls is used, accept HTTP/2 and HTTP/1.1. + The incoming HTTP/1.1 connection can be upgraded to + HTTP/2 through HTTP Upgrade. The protocol to the + backend is HTTP/1.1. + -s, --http2-proxy + Like default mode, but enable secure proxy mode. + --http2-bridge + Like default mode, but communicate with the backend in + HTTP/2 over SSL/TLS. Thus the incoming all connections + are converted to HTTP/2 connection and relayed to the + backend. See --backend-http-proxy-uri option if you are + behind the proxy and want to connect to the outside + HTTP/2 proxy. + --client Accept HTTP/2 and HTTP/1.1 without SSL/TLS. The + incoming HTTP/1.1 connection can be upgraded to HTTP/2 + connection through HTTP Upgrade. The protocol to the + backend is HTTP/2. To use nghttpx as a forward proxy, + use -p option instead. + -p, --client-proxy + Like --client option, but it also requires the request + path from frontend must be an absolute URI, suitable for + use as a forward proxy. + +Logging: + -L, --log-level= + Set the severity level of log output. must be + one of INFO, NOTICE, WARN, ERROR and FATAL. + Default: NOTICE + --accesslog-file= + Set path to write access log. To reopen file, send USR1 + signal to nghttpx. + --accesslog-syslog + Send access log to syslog. If this option is used, + --accesslog-file option is ignored. + --accesslog-format= + Specify format string for access log. The default + format is combined format. The following variables are + available: + + * $remote_addr: client IP address. + * $time_local: local time in Common Log format. + * $time_iso8601: local time in ISO 8601 format. + * $request: HTTP request line. + * $status: HTTP response status code. + * $body_bytes_sent: the number of bytes sent to client + as response body. + * $http_: value of HTTP request header where + '_' in is replaced with '-'. + * $remote_port: client port. + * $server_port: server port. + * $request_time: request processing time in seconds with + milliseconds resolution. + * $pid: PID of the running process. + * $alpn: ALPN identifier of the protocol which generates + the response. For HTTP/1, ALPN is always http/1.1, + regardless of minor version. + + Default: )" << DEFAULT_ACCESSLOG_FORMAT << R"( + --errorlog-file= + Set path to write error log. To reopen file, send USR1 + signal to nghttpx. stderr will be redirected to the + error log file unless --errorlog-syslog is used. + Default: )" << get_config()->errorlog_file.get() << R"( + --errorlog-syslog + Send error log to syslog. If this option is used, + --errorlog-file option is ignored. + --syslog-facility= + Set syslog facility to . + Default: )" << str_syslog_facility(get_config()->syslog_facility) + << R"( + +HTTP: + --add-x-forwarded-for + Append X-Forwarded-For header field to the downstream + request. + --strip-incoming-x-forwarded-for + Strip X-Forwarded-For header field from inbound client + requests. + --no-via Don't append to Via header field. If Via header field + is received, it is left unaltered. + --no-location-rewrite + Don't rewrite location header field on --http2-bridge, + --client and default mode. For --http2-proxy and + --client-proxy mode, location header field will not be + altered regardless of this option. + --no-host-rewrite + Don't rewrite host and :authority header fields on + --http2-bridge, --client and default mode. For + --http2-proxy and --client-proxy mode, these headers + will not be altered regardless of this option. + --altsvc= + Specify protocol ID, port, host and origin of + alternative service. and are optional. + They are advertised in alt-svc header field only in + HTTP/1.1 frontend. This option can be used multiple + times to specify multiple alternative services. + Example: --altsvc=h2,443 + --add-response-header=
    + Specify additional header field to add to response + header set. This option just appends header field and + won't replace anything already set. This option can be + used several times to specify multiple header fields. + Example: --add-response-header="foo: bar" + --header-field-buffer= + Set maximum buffer size for incoming HTTP header field + list. This is the sum of header name and value in + bytes. + Default: )" + << util::utos_with_unit(get_config()->header_field_buffer) << R"( + --max-header-fields= + Set maximum number of incoming HTTP header fields, which + appear in one request or response header field list. + Default: )" << get_config()->max_header_fields << R"( + +Debug: + --frontend-http2-dump-request-header= + Dumps request headers received by HTTP/2 frontend to the + file denoted in . The output is done in HTTP/1 + header field format and each header block is followed by + an empty line. This option is not thread safe and MUST + NOT be used with option -n, where >= 2. + --frontend-http2-dump-response-header= + Dumps response headers sent from HTTP/2 frontend to the + file denoted in . The output is done in HTTP/1 + header field format and each header block is followed by + an empty line. This option is not thread safe and MUST + NOT be used with option -n, where >= 2. + -o, --frontend-frame-debug + Print HTTP/2 frames in frontend to stderr. This option + is not thread safe and MUST NOT be used with option + -n=N, where N >= 2. + +Process: + -D, --daemon + Run in a background. If -D is used, the current working + directory is changed to '/'. + --pid-file= + Set path to save PID of this program. + --user= + Run this program as . This option is intended to + be used to drop root privileges. + +Misc: + --conf= + Load configuration from . + Default: )" << get_config()->conf_path.get() << R"( + -v, --version + Print version and exit. + -h, --help Print this help and exit. + +-- + + The argument is an integer and an optional unit (e.g., 10K is + 10 * 1024). Units are K, M and G (powers of 1024). + + The argument is an integer and an optional unit (e.g., 1s + is 1 second and 500ms is 500 milliseconds). Units are h, m, s or ms + (hours, minutes, seconds and milliseconds, respectively). If a unit + is omitted, a second is used as unit.)" << std::endl; +} +} // namespace + +int main(int argc, char **argv) { + Log::set_severity_level(NOTICE); + create_config(); + fill_default_config(); + + // We have to copy argv, since getopt_long may change its content. + mod_config()->argc = argc; + mod_config()->argv = new char *[argc]; + + for (int i = 0; i < argc; ++i) { + mod_config()->argv[i] = strdup(argv[i]); + } + + mod_config()->cwd = getcwd(nullptr, 0); + if (mod_config()->cwd == nullptr) { + auto error = errno; + LOG(FATAL) << "failed to get current working directory: errno=" << error; + exit(EXIT_FAILURE); + } + + std::vector> cmdcfgs; + while (1) { + static int flag = 0; + static option long_options[] = { + {"daemon", no_argument, nullptr, 'D'}, + {"log-level", required_argument, nullptr, 'L'}, + {"backend", required_argument, nullptr, 'b'}, + {"http2-max-concurrent-streams", required_argument, nullptr, 'c'}, + {"frontend", required_argument, nullptr, 'f'}, + {"help", no_argument, nullptr, 'h'}, + {"insecure", no_argument, nullptr, 'k'}, + {"workers", required_argument, nullptr, 'n'}, + {"client-proxy", no_argument, nullptr, 'p'}, + {"http2-proxy", no_argument, nullptr, 's'}, + {"version", no_argument, nullptr, 'v'}, + {"frontend-frame-debug", no_argument, nullptr, 'o'}, + {"add-x-forwarded-for", no_argument, &flag, 1}, + {"frontend-http2-read-timeout", required_argument, &flag, 2}, + {"frontend-read-timeout", required_argument, &flag, 3}, + {"frontend-write-timeout", required_argument, &flag, 4}, + {"backend-read-timeout", required_argument, &flag, 5}, + {"backend-write-timeout", required_argument, &flag, 6}, + {"accesslog-file", required_argument, &flag, 7}, + {"backend-keep-alive-timeout", required_argument, &flag, 8}, + {"frontend-http2-window-bits", required_argument, &flag, 9}, + {"pid-file", required_argument, &flag, 10}, + {"user", required_argument, &flag, 11}, + {"conf", required_argument, &flag, 12}, + {"syslog-facility", required_argument, &flag, 14}, + {"backlog", required_argument, &flag, 15}, + {"ciphers", required_argument, &flag, 16}, + {"client", no_argument, &flag, 17}, + {"backend-http2-window-bits", required_argument, &flag, 18}, + {"cacert", required_argument, &flag, 19}, + {"backend-ipv4", no_argument, &flag, 20}, + {"backend-ipv6", no_argument, &flag, 21}, + {"private-key-passwd-file", required_argument, &flag, 22}, + {"no-via", no_argument, &flag, 23}, + {"subcert", required_argument, &flag, 24}, + {"http2-bridge", no_argument, &flag, 25}, + {"backend-http-proxy-uri", required_argument, &flag, 26}, + {"backend-no-tls", no_argument, &flag, 27}, + {"frontend-no-tls", no_argument, &flag, 29}, + {"backend-tls-sni-field", required_argument, &flag, 31}, + {"dh-param-file", required_argument, &flag, 33}, + {"read-rate", required_argument, &flag, 34}, + {"read-burst", required_argument, &flag, 35}, + {"write-rate", required_argument, &flag, 36}, + {"write-burst", required_argument, &flag, 37}, + {"npn-list", required_argument, &flag, 38}, + {"verify-client", no_argument, &flag, 39}, + {"verify-client-cacert", required_argument, &flag, 40}, + {"client-private-key-file", required_argument, &flag, 41}, + {"client-cert-file", required_argument, &flag, 42}, + {"frontend-http2-dump-request-header", required_argument, &flag, 43}, + {"frontend-http2-dump-response-header", required_argument, &flag, 44}, + {"http2-no-cookie-crumbling", no_argument, &flag, 45}, + {"frontend-http2-connection-window-bits", required_argument, &flag, 46}, + {"backend-http2-connection-window-bits", required_argument, &flag, 47}, + {"tls-proto-list", required_argument, &flag, 48}, + {"padding", required_argument, &flag, 49}, + {"worker-read-rate", required_argument, &flag, 50}, + {"worker-read-burst", required_argument, &flag, 51}, + {"worker-write-rate", required_argument, &flag, 52}, + {"worker-write-burst", required_argument, &flag, 53}, + {"altsvc", required_argument, &flag, 54}, + {"add-response-header", required_argument, &flag, 55}, + {"worker-frontend-connections", required_argument, &flag, 56}, + {"accesslog-syslog", no_argument, &flag, 57}, + {"errorlog-file", required_argument, &flag, 58}, + {"errorlog-syslog", no_argument, &flag, 59}, + {"stream-read-timeout", required_argument, &flag, 60}, + {"stream-write-timeout", required_argument, &flag, 61}, + {"no-location-rewrite", no_argument, &flag, 62}, + {"backend-http1-connections-per-host", required_argument, &flag, 63}, + {"listener-disable-timeout", required_argument, &flag, 64}, + {"strip-incoming-x-forwarded-for", no_argument, &flag, 65}, + {"accesslog-format", required_argument, &flag, 66}, + {"backend-http1-connections-per-frontend", required_argument, &flag, + 67}, + {"tls-ticket-key-file", required_argument, &flag, 68}, + {"rlimit-nofile", required_argument, &flag, 69}, + {"tls-ctx-per-worker", no_argument, &flag, 70}, + {"backend-response-buffer", required_argument, &flag, 71}, + {"backend-request-buffer", required_argument, &flag, 72}, + {"no-host-rewrite", no_argument, &flag, 73}, + {"no-server-push", no_argument, &flag, 74}, + {"backend-http2-connections-per-worker", required_argument, &flag, 76}, + {"fetch-ocsp-response-file", required_argument, &flag, 77}, + {"ocsp-update-interval", required_argument, &flag, 78}, + {"no-ocsp", no_argument, &flag, 79}, + {"header-field-buffer", required_argument, &flag, 80}, + {"max-header-fields", required_argument, &flag, 81}, + {nullptr, 0, nullptr, 0}}; + + int option_index = 0; + int c = getopt_long(argc, argv, "DL:b:c:f:hkn:opsv", long_options, + &option_index); + if (c == -1) { + break; + } + switch (c) { + case 'D': + cmdcfgs.emplace_back(SHRPX_OPT_DAEMON, "yes"); + break; + case 'L': + cmdcfgs.emplace_back(SHRPX_OPT_LOG_LEVEL, optarg); + break; + case 'b': + cmdcfgs.emplace_back(SHRPX_OPT_BACKEND, optarg); + break; + case 'c': + cmdcfgs.emplace_back(SHRPX_OPT_HTTP2_MAX_CONCURRENT_STREAMS, optarg); + break; + case 'f': + cmdcfgs.emplace_back(SHRPX_OPT_FRONTEND, optarg); + break; + case 'h': + print_help(std::cout); + exit(EXIT_SUCCESS); + case 'k': + cmdcfgs.emplace_back(SHRPX_OPT_INSECURE, "yes"); + break; + case 'n': +#ifdef NOTHREADS + LOG(WARN) << "Threading disabled at build time, no threads created."; +#else + cmdcfgs.emplace_back(SHRPX_OPT_WORKERS, optarg); +#endif // NOTHREADS + break; + case 'o': + cmdcfgs.emplace_back(SHRPX_OPT_FRONTEND_FRAME_DEBUG, "yes"); + break; + case 'p': + cmdcfgs.emplace_back(SHRPX_OPT_CLIENT_PROXY, "yes"); + break; + case 's': + cmdcfgs.emplace_back(SHRPX_OPT_HTTP2_PROXY, "yes"); + break; + case 'v': + print_version(std::cout); + exit(EXIT_SUCCESS); + case '?': + util::show_candidates(argv[optind - 1], long_options); + exit(EXIT_FAILURE); + case 0: + switch (flag) { + case 1: + // --add-x-forwarded-for + cmdcfgs.emplace_back(SHRPX_OPT_ADD_X_FORWARDED_FOR, "yes"); + break; + case 2: + // --frontend-http2-read-timeout + cmdcfgs.emplace_back(SHRPX_OPT_FRONTEND_HTTP2_READ_TIMEOUT, optarg); + break; + case 3: + // --frontend-read-timeout + cmdcfgs.emplace_back(SHRPX_OPT_FRONTEND_READ_TIMEOUT, optarg); + break; + case 4: + // --frontend-write-timeout + cmdcfgs.emplace_back(SHRPX_OPT_FRONTEND_WRITE_TIMEOUT, optarg); + break; + case 5: + // --backend-read-timeout + cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_READ_TIMEOUT, optarg); + break; + case 6: + // --backend-write-timeout + cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_WRITE_TIMEOUT, optarg); + break; + case 7: + cmdcfgs.emplace_back(SHRPX_OPT_ACCESSLOG_FILE, optarg); + break; + case 8: + // --backend-keep-alive-timeout + cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_KEEP_ALIVE_TIMEOUT, optarg); + break; + case 9: + // --frontend-http2-window-bits + cmdcfgs.emplace_back(SHRPX_OPT_FRONTEND_HTTP2_WINDOW_BITS, optarg); + break; + case 10: + cmdcfgs.emplace_back(SHRPX_OPT_PID_FILE, optarg); + break; + case 11: + cmdcfgs.emplace_back(SHRPX_OPT_USER, optarg); + break; + case 12: + // --conf + mod_config()->conf_path = strcopy(optarg); + break; + case 14: + // --syslog-facility + cmdcfgs.emplace_back(SHRPX_OPT_SYSLOG_FACILITY, optarg); + break; + case 15: + // --backlog + cmdcfgs.emplace_back(SHRPX_OPT_BACKLOG, optarg); + break; + case 16: + // --ciphers + cmdcfgs.emplace_back(SHRPX_OPT_CIPHERS, optarg); + break; + case 17: + // --client + cmdcfgs.emplace_back(SHRPX_OPT_CLIENT, "yes"); + break; + case 18: + // --backend-http2-window-bits + cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_HTTP2_WINDOW_BITS, optarg); + break; + case 19: + // --cacert + cmdcfgs.emplace_back(SHRPX_OPT_CACERT, optarg); + break; + case 20: + // --backend-ipv4 + cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_IPV4, "yes"); + break; + case 21: + // --backend-ipv6 + cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_IPV6, "yes"); + break; + case 22: + // --private-key-passwd-file + cmdcfgs.emplace_back(SHRPX_OPT_PRIVATE_KEY_PASSWD_FILE, optarg); + break; + case 23: + // --no-via + cmdcfgs.emplace_back(SHRPX_OPT_NO_VIA, "yes"); + break; + case 24: + // --subcert + cmdcfgs.emplace_back(SHRPX_OPT_SUBCERT, optarg); + break; + case 25: + // --http2-bridge + cmdcfgs.emplace_back(SHRPX_OPT_HTTP2_BRIDGE, "yes"); + break; + case 26: + // --backend-http-proxy-uri + cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_HTTP_PROXY_URI, optarg); + break; + case 27: + // --backend-no-tls + cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_NO_TLS, "yes"); + break; + case 29: + // --frontend-no-tls + cmdcfgs.emplace_back(SHRPX_OPT_FRONTEND_NO_TLS, "yes"); + break; + case 31: + // --backend-tls-sni-field + cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_TLS_SNI_FIELD, optarg); + break; + case 33: + // --dh-param-file + cmdcfgs.emplace_back(SHRPX_OPT_DH_PARAM_FILE, optarg); + break; + case 34: + // --read-rate + cmdcfgs.emplace_back(SHRPX_OPT_READ_RATE, optarg); + break; + case 35: + // --read-burst + cmdcfgs.emplace_back(SHRPX_OPT_READ_BURST, optarg); + break; + case 36: + // --write-rate + cmdcfgs.emplace_back(SHRPX_OPT_WRITE_RATE, optarg); + break; + case 37: + // --write-burst + cmdcfgs.emplace_back(SHRPX_OPT_WRITE_BURST, optarg); + break; + case 38: + // --npn-list + cmdcfgs.emplace_back(SHRPX_OPT_NPN_LIST, optarg); + break; + case 39: + // --verify-client + cmdcfgs.emplace_back(SHRPX_OPT_VERIFY_CLIENT, "yes"); + break; + case 40: + // --verify-client-cacert + cmdcfgs.emplace_back(SHRPX_OPT_VERIFY_CLIENT_CACERT, optarg); + break; + case 41: + // --client-private-key-file + cmdcfgs.emplace_back(SHRPX_OPT_CLIENT_PRIVATE_KEY_FILE, optarg); + break; + case 42: + // --client-cert-file + cmdcfgs.emplace_back(SHRPX_OPT_CLIENT_CERT_FILE, optarg); + break; + case 43: + // --frontend-http2-dump-request-header + cmdcfgs.emplace_back(SHRPX_OPT_FRONTEND_HTTP2_DUMP_REQUEST_HEADER, + optarg); + break; + case 44: + // --frontend-http2-dump-response-header + cmdcfgs.emplace_back(SHRPX_OPT_FRONTEND_HTTP2_DUMP_RESPONSE_HEADER, + optarg); + break; + case 45: + // --http2-no-cookie-crumbling + cmdcfgs.emplace_back(SHRPX_OPT_HTTP2_NO_COOKIE_CRUMBLING, "yes"); + break; + case 46: + // --frontend-http2-connection-window-bits + cmdcfgs.emplace_back(SHRPX_OPT_FRONTEND_HTTP2_CONNECTION_WINDOW_BITS, + optarg); + break; + case 47: + // --backend-http2-connection-window-bits + cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_HTTP2_CONNECTION_WINDOW_BITS, + optarg); + break; + case 48: + // --tls-proto-list + cmdcfgs.emplace_back(SHRPX_OPT_TLS_PROTO_LIST, optarg); + break; + case 49: + // --padding + cmdcfgs.emplace_back(SHRPX_OPT_PADDING, optarg); + break; + case 50: + // --worker-read-rate + cmdcfgs.emplace_back(SHRPX_OPT_WORKER_READ_RATE, optarg); + break; + case 51: + // --worker-read-burst + cmdcfgs.emplace_back(SHRPX_OPT_WORKER_READ_BURST, optarg); + break; + case 52: + // --worker-write-rate + cmdcfgs.emplace_back(SHRPX_OPT_WORKER_WRITE_RATE, optarg); + break; + case 53: + // --worker-write-burst + cmdcfgs.emplace_back(SHRPX_OPT_WORKER_WRITE_BURST, optarg); + break; + case 54: + // --altsvc + cmdcfgs.emplace_back(SHRPX_OPT_ALTSVC, optarg); + break; + case 55: + // --add-response-header + cmdcfgs.emplace_back(SHRPX_OPT_ADD_RESPONSE_HEADER, optarg); + break; + case 56: + // --worker-frontend-connections + cmdcfgs.emplace_back(SHRPX_OPT_WORKER_FRONTEND_CONNECTIONS, optarg); + break; + case 57: + // --accesslog-syslog + cmdcfgs.emplace_back(SHRPX_OPT_ACCESSLOG_SYSLOG, "yes"); + break; + case 58: + // --errorlog-file + cmdcfgs.emplace_back(SHRPX_OPT_ERRORLOG_FILE, optarg); + break; + case 59: + // --errorlog-syslog + cmdcfgs.emplace_back(SHRPX_OPT_ERRORLOG_SYSLOG, "yes"); + break; + case 60: + // --stream-read-timeout + cmdcfgs.emplace_back(SHRPX_OPT_STREAM_READ_TIMEOUT, optarg); + break; + case 61: + // --stream-write-timeout + cmdcfgs.emplace_back(SHRPX_OPT_STREAM_WRITE_TIMEOUT, optarg); + break; + case 62: + // --no-location-rewrite + cmdcfgs.emplace_back(SHRPX_OPT_NO_LOCATION_REWRITE, "yes"); + break; + case 63: + // --backend-http1-connections-per-host + cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_HTTP1_CONNECTIONS_PER_HOST, + optarg); + break; + case 64: + // --listener-disable-timeout + cmdcfgs.emplace_back(SHRPX_OPT_LISTENER_DISABLE_TIMEOUT, optarg); + break; + case 65: + // --strip-incoming-x-forwarded-for + cmdcfgs.emplace_back(SHRPX_OPT_STRIP_INCOMING_X_FORWARDED_FOR, "yes"); + break; + case 66: + // --accesslog-format + cmdcfgs.emplace_back(SHRPX_OPT_ACCESSLOG_FORMAT, optarg); + break; + case 67: + // --backend-http1-connections-per-frontend + cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_HTTP1_CONNECTIONS_PER_FRONTEND, + optarg); + break; + case 68: + // --tls-ticket-key-file + cmdcfgs.emplace_back(SHRPX_OPT_TLS_TICKET_KEY_FILE, optarg); + break; + case 69: + // --rlimit-nofile + cmdcfgs.emplace_back(SHRPX_OPT_RLIMIT_NOFILE, optarg); + break; + case 71: + // --backend-response-buffer + cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_RESPONSE_BUFFER, optarg); + break; + case 72: + // --backend-request-buffer + cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_REQUEST_BUFFER, optarg); + break; + case 73: + // --no-host-rewrite + cmdcfgs.emplace_back(SHRPX_OPT_NO_HOST_REWRITE, "yes"); + break; + case 74: + // --no-server-push + cmdcfgs.emplace_back(SHRPX_OPT_NO_SERVER_PUSH, "yes"); + break; + case 76: + // --backend-http2-connections-per-worker + cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_HTTP2_CONNECTIONS_PER_WORKER, + optarg); + break; + case 77: + // --fetch-ocsp-response-file + cmdcfgs.emplace_back(SHRPX_OPT_FETCH_OCSP_RESPONSE_FILE, optarg); + break; + case 78: + // --ocsp-update-interval + cmdcfgs.emplace_back(SHRPX_OPT_OCSP_UPDATE_INTERVAL, optarg); + break; + case 79: + // --no-ocsp + cmdcfgs.emplace_back(SHRPX_OPT_NO_OCSP, "yes"); + break; + case 80: + // --header-field-buffer + cmdcfgs.emplace_back(SHRPX_OPT_HEADER_FIELD_BUFFER, optarg); + break; + case 81: + // --max-header-fields + cmdcfgs.emplace_back(SHRPX_OPT_MAX_HEADER_FIELDS, optarg); + break; + default: + break; + } + break; + default: + break; + } + } + + // Initialize OpenSSL before parsing options because we create + // SSL_CTX there. + OPENSSL_config(nullptr); + OpenSSL_add_all_algorithms(); + SSL_load_error_strings(); + SSL_library_init(); + + if (conf_exists(get_config()->conf_path.get())) { + if (load_config(get_config()->conf_path.get()) == -1) { + LOG(FATAL) << "Failed to load configuration from " + << get_config()->conf_path.get(); + exit(EXIT_FAILURE); + } + } + + if (argc - optind >= 2) { + cmdcfgs.emplace_back(SHRPX_OPT_PRIVATE_KEY_FILE, argv[optind++]); + cmdcfgs.emplace_back(SHRPX_OPT_CERTIFICATE_FILE, argv[optind++]); + } + + // First open default log files to deal with errors occurred while + // parsing option values. + reopen_log_files(); + + for (size_t i = 0, len = cmdcfgs.size(); i < len; ++i) { + if (parse_config(cmdcfgs[i].first, cmdcfgs[i].second) == -1) { + LOG(FATAL) << "Failed to parse command-line argument."; + exit(EXIT_FAILURE); + } + } + +#ifndef NOTHREADS + auto lock = make_unique(); +#endif // NOTHREADS + + if (get_config()->accesslog_syslog || get_config()->errorlog_syslog) { + openlog("nghttpx", LOG_NDELAY | LOG_NOWAIT | LOG_PID, + get_config()->syslog_facility); + } + + if (reopen_log_files() != 0) { + LOG(FATAL) << "Failed to open log file"; + exit(EXIT_FAILURE); + } + + redirect_stderr_to_errorlog(); + + if (get_config()->uid != 0) { + if (log_config()->accesslog_fd != -1 && + fchown(log_config()->accesslog_fd, get_config()->uid, + get_config()->gid) == -1) { + auto error = errno; + LOG(WARN) << "Changing owner of access log file failed: " + << strerror(error); + } + if (log_config()->errorlog_fd != -1 && + fchown(log_config()->errorlog_fd, get_config()->uid, + get_config()->gid) == -1) { + auto error = errno; + LOG(WARN) << "Changing owner of error log file failed: " + << strerror(error); + } + } + + if (get_config()->http2_upstream_dump_request_header_file) { + auto path = get_config()->http2_upstream_dump_request_header_file.get(); + auto f = open_file_for_write(path); + + if (f == nullptr) { + LOG(FATAL) << "Failed to open http2 upstream request header file: " + << path; + exit(EXIT_FAILURE); + } + + mod_config()->http2_upstream_dump_request_header = f; + + if (get_config()->uid != 0) { + if (chown(path, get_config()->uid, get_config()->gid) == -1) { + auto error = errno; + LOG(WARN) << "Changing owner of http2 upstream request header file " + << path << " failed: " << strerror(error); + } + } + } + + if (get_config()->http2_upstream_dump_response_header_file) { + auto path = get_config()->http2_upstream_dump_response_header_file.get(); + auto f = open_file_for_write(path); + + if (f == nullptr) { + LOG(FATAL) << "Failed to open http2 upstream response header file: " + << path; + exit(EXIT_FAILURE); + } + + mod_config()->http2_upstream_dump_response_header = f; + + if (get_config()->uid != 0) { + if (chown(path, get_config()->uid, get_config()->gid) == -1) { + auto error = errno; + LOG(WARN) << "Changing owner of http2 upstream response header file" + << " " << path << " failed: " << strerror(error); + } + } + } + + if (get_config()->npn_list.empty()) { + mod_config()->npn_list = parse_config_str_list(DEFAULT_NPN_LIST); + } + if (get_config()->tls_proto_list.empty()) { + mod_config()->tls_proto_list = + parse_config_str_list(DEFAULT_TLS_PROTO_LIST); + } + + mod_config()->tls_proto_mask = + ssl::create_tls_proto_mask(get_config()->tls_proto_list); + + mod_config()->alpn_prefs = ssl::set_alpn_prefs(get_config()->npn_list); + + if (get_config()->backend_ipv4 && get_config()->backend_ipv6) { + LOG(FATAL) << "--backend-ipv4 and --backend-ipv6 cannot be used at the " + << "same time."; + exit(EXIT_FAILURE); + } + + if (get_config()->worker_frontend_connections == 0) { + mod_config()->worker_frontend_connections = + std::numeric_limits::max(); + } + + if (get_config()->http2_proxy + get_config()->http2_bridge + + get_config()->client_proxy + get_config()->client > + 1) { + LOG(FATAL) << "--http2-proxy, --http2-bridge, --client-proxy and --client " + << "cannot be used at the same time."; + exit(EXIT_FAILURE); + } + + if (get_config()->client || get_config()->client_proxy) { + mod_config()->client_mode = true; + mod_config()->upstream_no_tls = true; + } + + if (get_config()->client_mode || get_config()->http2_bridge) { + mod_config()->downstream_proto = PROTO_HTTP2; + } else { + mod_config()->downstream_proto = PROTO_HTTP; + } + + if (!get_config()->upstream_no_tls && + (!get_config()->private_key_file || !get_config()->cert_file)) { + print_usage(std::cerr); + LOG(FATAL) << "Too few arguments"; + exit(EXIT_FAILURE); + } + + if (!get_config()->upstream_no_tls && !get_config()->no_ocsp) { + struct stat buf; + if (stat(get_config()->fetch_ocsp_response_file.get(), &buf) != 0) { + mod_config()->no_ocsp = true; + LOG(WARN) << "--fetch-ocsp-response-file: " + << get_config()->fetch_ocsp_response_file.get() + << " not found. OCSP stapling has been disabled."; + } + } + + if (get_config()->downstream_addrs.empty()) { + DownstreamAddr addr; + addr.host = strcopy(DEFAULT_DOWNSTREAM_HOST); + addr.port = DEFAULT_DOWNSTREAM_PORT; + + mod_config()->downstream_addrs.push_back(std::move(addr)); + } + + if (LOG_ENABLED(INFO)) { + LOG(INFO) << "Resolving backend address"; + } + + for (auto &addr : mod_config()->downstream_addrs) { + + if (addr.host_unix) { + // for AF_UNIX socket, we use "localhost" as host for backend + // hostport. This is used as Host header field to backend and + // not going to be passed to any syscalls. + addr.hostport = + strcopy(util::make_hostport("localhost", get_config()->port)); + + auto path = addr.host.get(); + auto pathlen = strlen(path); + + if (pathlen + 1 > sizeof(addr.addr.un.sun_path)) { + LOG(FATAL) << "UNIX domain socket path " << path << " is too long > " + << sizeof(addr.addr.un.sun_path); + exit(EXIT_FAILURE); + } + + LOG(INFO) << "Use UNIX domain socket path " << path + << " for backend connection"; + + addr.addr.un.sun_family = AF_UNIX; + // copy path including terminal NULL + std::copy_n(path, pathlen + 1, addr.addr.un.sun_path); + addr.addrlen = sizeof(addr.addr.un); + + continue; + } + + addr.hostport = strcopy(util::make_hostport(addr.host.get(), addr.port)); + + if (resolve_hostname( + &addr.addr, &addr.addrlen, addr.host.get(), addr.port, + get_config()->backend_ipv4 + ? AF_INET + : (get_config()->backend_ipv6 ? AF_INET6 : AF_UNSPEC)) == -1) { + exit(EXIT_FAILURE); + } + } + + if (get_config()->downstream_http_proxy_host) { + if (LOG_ENABLED(INFO)) { + LOG(INFO) << "Resolving backend http proxy address"; + } + if (resolve_hostname(&mod_config()->downstream_http_proxy_addr, + &mod_config()->downstream_http_proxy_addrlen, + get_config()->downstream_http_proxy_host.get(), + get_config()->downstream_http_proxy_port, + AF_UNSPEC) == -1) { + exit(EXIT_FAILURE); + } + } + + if (get_config()->http2_downstream_connections_per_worker == 0) { + mod_config()->http2_downstream_connections_per_worker = + get_config()->downstream_addrs.size(); + } + + if (get_config()->rlimit_nofile) { + struct rlimit lim = {static_cast(get_config()->rlimit_nofile), + static_cast(get_config()->rlimit_nofile)}; + if (setrlimit(RLIMIT_NOFILE, &lim) != 0) { + auto error = errno; + LOG(WARN) << "Setting rlimit-nofile failed: " << strerror(error); + } + } + + if (get_config()->upstream_frame_debug) { + // To make it sync to logging + set_output(stderr); + if (isatty(fileno(stdout))) { + set_color_output(true); + } + reset_timer(); + } + + struct sigaction act; + memset(&act, 0, sizeof(struct sigaction)); + act.sa_handler = SIG_IGN; + sigaction(SIGPIPE, &act, nullptr); + + event_loop(); + + LOG(NOTICE) << "Shutdown momentarily"; + + return 0; +} + +} // namespace shrpx + +int main(int argc, char **argv) { return shrpx::main(argc, argv); } diff --git a/src/shrpx.h b/src/shrpx.h new file mode 100644 index 0000000..bd91c76 --- /dev/null +++ b/src/shrpx.h @@ -0,0 +1,49 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef SHRPX_H +#define SHRPX_H + +#ifdef HAVE_CONFIG_H +#include +#endif // HAVE_CONFIG_H + +#include +#ifdef HAVE_SYS_SOCKET_H +#include +#endif // HAVE_SYS_SOCKET_H + +#include + +#include "shrpx_log.h" + +#ifndef HAVE__EXIT +#define _Exit(status) _exit(status) +#endif // !HAVE__EXIT + +#define DIE() exit(EXIT_FAILURE) + +#define SHRPX_READ_WATERMARK (16 * 1024) + +#endif // SHRPX_H diff --git a/src/shrpx_accept_handler.cc b/src/shrpx_accept_handler.cc new file mode 100644 index 0000000..0d44830 --- /dev/null +++ b/src/shrpx_accept_handler.cc @@ -0,0 +1,114 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2014 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "shrpx_accept_handler.h" + +#ifdef HAVE_UNISTD_H +#include +#endif // HAVE_UNISTD_H + +#include + +#include "shrpx_connection_handler.h" +#include "shrpx_config.h" +#include "util.h" + +using namespace nghttp2; + +namespace shrpx { + +namespace { +void acceptcb(struct ev_loop *loop, ev_io *w, int revent) { + auto h = static_cast(w->data); + h->accept_connection(); +} +} // namespace + +AcceptHandler::AcceptHandler(int fd, ConnectionHandler *h) + : conn_hnr_(h), fd_(fd) { + ev_io_init(&wev_, acceptcb, fd_, EV_READ); + wev_.data = this; + ev_io_start(conn_hnr_->get_loop(), &wev_); +} + +AcceptHandler::~AcceptHandler() { + ev_io_stop(conn_hnr_->get_loop(), &wev_); + close(fd_); +} + +void AcceptHandler::accept_connection() { + for (;;) { + sockaddr_union sockaddr; + socklen_t addrlen = sizeof(sockaddr); + +#ifdef HAVE_ACCEPT4 + auto cfd = + accept4(fd_, &sockaddr.sa, &addrlen, SOCK_NONBLOCK | SOCK_CLOEXEC); +#else // !HAVE_ACCEPT4 + auto cfd = accept(fd_, &sockaddr.sa, &addrlen); +#endif // !HAVE_ACCEPT4 + + if (cfd == -1) { + switch (errno) { + case EINTR: + case ENETDOWN: + case EPROTO: + case ENOPROTOOPT: + case EHOSTDOWN: +#ifdef ENONET + case ENONET: +#endif // ENONET + case EHOSTUNREACH: + case EOPNOTSUPP: + case ENETUNREACH: + continue; + case EMFILE: + case ENFILE: + LOG(WARN) << "acceptor: running out file descriptor; disable acceptor " + "temporarily"; + conn_hnr_->disable_acceptor_temporary(30.); + break; + } + + break; + } + +#ifndef HAVE_ACCEPT4 + util::make_socket_nonblocking(cfd); + util::make_socket_closeonexec(cfd); +#endif // !HAVE_ACCEPT4 + + util::make_socket_nodelay(cfd); + + conn_hnr_->handle_connection(cfd, &sockaddr.sa, addrlen); + } +} + +void AcceptHandler::enable() { ev_io_start(conn_hnr_->get_loop(), &wev_); } + +void AcceptHandler::disable() { ev_io_stop(conn_hnr_->get_loop(), &wev_); } + +int AcceptHandler::get_fd() const { return fd_; } + +} // namespace shrpx diff --git a/src/shrpx_accept_handler.h b/src/shrpx_accept_handler.h new file mode 100644 index 0000000..194788b --- /dev/null +++ b/src/shrpx_accept_handler.h @@ -0,0 +1,53 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2014 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef SHRPX_ACCEPT_HANDLER_H +#define SHRPX_ACCEPT_HANDLER_H + +#include "shrpx.h" + +#include + +namespace shrpx { + +class ConnectionHandler; + +class AcceptHandler { +public: + AcceptHandler(int fd, ConnectionHandler *h); + ~AcceptHandler(); + void accept_connection(); + void enable(); + void disable(); + int get_fd() const; + +private: + ev_io wev_; + ConnectionHandler *conn_hnr_; + int fd_; +}; + +} // namespace shrpx + +#endif // SHRPX_ACCEPT_HANDLER_H diff --git a/src/shrpx_client_handler.cc b/src/shrpx_client_handler.cc new file mode 100644 index 0000000..354b95e --- /dev/null +++ b/src/shrpx_client_handler.cc @@ -0,0 +1,793 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "shrpx_client_handler.h" + +#ifdef HAVE_UNISTD_H +#include +#endif // HAVE_UNISTD_H +#include + +#include "shrpx_upstream.h" +#include "shrpx_http2_upstream.h" +#include "shrpx_https_upstream.h" +#include "shrpx_config.h" +#include "shrpx_http_downstream_connection.h" +#include "shrpx_http2_downstream_connection.h" +#include "shrpx_ssl.h" +#include "shrpx_worker.h" +#include "shrpx_downstream_connection_pool.h" +#include "shrpx_downstream.h" +#ifdef HAVE_SPDYLAY +#include "shrpx_spdy_upstream.h" +#endif // HAVE_SPDYLAY +#include "util.h" +#include "template.h" + +using namespace nghttp2; + +namespace shrpx { + +namespace { +void timeoutcb(struct ev_loop *loop, ev_timer *w, int revents) { + auto conn = static_cast(w->data); + auto handler = static_cast(conn->data); + + if (LOG_ENABLED(INFO)) { + CLOG(INFO, handler) << "Time out"; + } + + delete handler; +} +} // namespace + +namespace { +void shutdowncb(struct ev_loop *loop, ev_timer *w, int revents) { + auto handler = static_cast(w->data); + + if (LOG_ENABLED(INFO)) { + CLOG(INFO, handler) << "Close connection due to TLS renegotiation"; + } + + delete handler; +} +} // namespace + +namespace { +void readcb(struct ev_loop *loop, ev_io *w, int revents) { + auto conn = static_cast(w->data); + auto handler = static_cast(conn->data); + + if (handler->do_read() != 0) { + delete handler; + return; + } + if (handler->do_write() != 0) { + delete handler; + return; + } +} +} // namespace + +namespace { +void writecb(struct ev_loop *loop, ev_io *w, int revents) { + auto conn = static_cast(w->data); + auto handler = static_cast(conn->data); + + if (handler->do_write() != 0) { + delete handler; + return; + } +} +} // namespace + +int ClientHandler::read_clear() { + ev_timer_again(conn_.loop, &conn_.rt); + + for (;;) { + if (rb_.rleft() && on_read() != 0) { + return -1; + } + if (rb_.rleft() == 0) { + rb_.reset(); + } else if (rb_.wleft() == 0) { + conn_.rlimit.stopw(); + return 0; + } + + auto nread = conn_.read_clear(rb_.last, rb_.wleft()); + + if (nread == 0) { + return 0; + } + + if (nread < 0) { + return -1; + } + + rb_.write(nread); + } +} + +int ClientHandler::write_clear() { + ev_timer_again(conn_.loop, &conn_.rt); + + for (;;) { + if (wb_.rleft() > 0) { + auto nwrite = conn_.write_clear(wb_.pos, wb_.rleft()); + if (nwrite == 0) { + return 0; + } + if (nwrite < 0) { + return -1; + } + wb_.drain(nwrite); + continue; + } + wb_.reset(); + if (on_write() != 0) { + return -1; + } + if (wb_.rleft() == 0) { + break; + } + } + + conn_.wlimit.stopw(); + ev_timer_stop(conn_.loop, &conn_.wt); + + return 0; +} + +int ClientHandler::tls_handshake() { + ev_timer_again(conn_.loop, &conn_.rt); + + ERR_clear_error(); + + auto rv = conn_.tls_handshake(); + + if (rv == SHRPX_ERR_INPROGRESS) { + return 0; + } + + if (rv < 0) { + return -1; + } + + if (LOG_ENABLED(INFO)) { + CLOG(INFO, this) << "SSL/TLS handshake completed"; + } + + if (validate_next_proto() != 0) { + return -1; + } + + read_ = &ClientHandler::read_tls; + write_ = &ClientHandler::write_tls; + + return 0; +} + +int ClientHandler::read_tls() { + ev_timer_again(conn_.loop, &conn_.rt); + + ERR_clear_error(); + + for (;;) { + // we should process buffered data first before we read EOF. + if (rb_.rleft() && on_read() != 0) { + return -1; + } + if (rb_.rleft() == 0) { + rb_.reset(); + } else if (rb_.wleft() == 0) { + conn_.rlimit.stopw(); + return 0; + } + + auto nread = conn_.read_tls(rb_.last, rb_.wleft()); + + if (nread == 0) { + return 0; + } + + if (nread < 0) { + return -1; + } + + rb_.write(nread); + } +} + +int ClientHandler::write_tls() { + ev_timer_again(conn_.loop, &conn_.rt); + + ERR_clear_error(); + + for (;;) { + if (wb_.rleft() > 0) { + auto nwrite = conn_.write_tls(wb_.pos, wb_.rleft()); + + if (nwrite == 0) { + return 0; + } + + if (nwrite < 0) { + return -1; + } + + wb_.drain(nwrite); + + continue; + } + wb_.reset(); + if (on_write() != 0) { + return -1; + } + if (wb_.rleft() == 0) { + break; + } + } + + conn_.wlimit.stopw(); + ev_timer_stop(conn_.loop, &conn_.wt); + + return 0; +} + +int ClientHandler::upstream_noop() { return 0; } + +int ClientHandler::upstream_read() { + assert(upstream_); + if (upstream_->on_read() != 0) { + return -1; + } + return 0; +} + +int ClientHandler::upstream_write() { + assert(upstream_); + if (upstream_->on_write() != 0) { + return -1; + } + + if (get_should_close_after_write() && wb_.rleft() == 0) { + return -1; + } + + return 0; +} + +int ClientHandler::upstream_http2_connhd_read() { + auto nread = std::min(left_connhd_len_, rb_.rleft()); + if (memcmp(NGHTTP2_CLIENT_MAGIC + NGHTTP2_CLIENT_MAGIC_LEN - left_connhd_len_, + rb_.pos, nread) != 0) { + // There is no downgrade path here. Just drop the connection. + if (LOG_ENABLED(INFO)) { + CLOG(INFO, this) << "invalid client connection header"; + } + + return -1; + } + + left_connhd_len_ -= nread; + rb_.drain(nread); + conn_.rlimit.startw(); + + if (left_connhd_len_ == 0) { + on_read_ = &ClientHandler::upstream_read; + // Run on_read to process data left in buffer since they are not + // notified further + if (on_read() != 0) { + return -1; + } + return 0; + } + + return 0; +} + +int ClientHandler::upstream_http1_connhd_read() { + auto nread = std::min(left_connhd_len_, rb_.rleft()); + if (memcmp(NGHTTP2_CLIENT_MAGIC + NGHTTP2_CLIENT_MAGIC_LEN - left_connhd_len_, + rb_.pos, nread) != 0) { + if (LOG_ENABLED(INFO)) { + CLOG(INFO, this) << "This is HTTP/1.1 connection, " + << "but may be upgraded to HTTP/2 later."; + } + + // Reset header length for later HTTP/2 upgrade + left_connhd_len_ = NGHTTP2_CLIENT_MAGIC_LEN; + on_read_ = &ClientHandler::upstream_read; + on_write_ = &ClientHandler::upstream_write; + + if (on_read() != 0) { + return -1; + } + + return 0; + } + + left_connhd_len_ -= nread; + rb_.drain(nread); + conn_.rlimit.startw(); + + if (left_connhd_len_ == 0) { + if (LOG_ENABLED(INFO)) { + CLOG(INFO, this) << "direct HTTP/2 connection"; + } + + direct_http2_upgrade(); + on_read_ = &ClientHandler::upstream_read; + on_write_ = &ClientHandler::upstream_write; + + // Run on_read to process data left in buffer since they are not + // notified further + if (on_read() != 0) { + return -1; + } + + return 0; + } + + return 0; +} + +ClientHandler::ClientHandler(Worker *worker, int fd, SSL *ssl, + const char *ipaddr, const char *port) + : conn_(worker->get_loop(), fd, ssl, get_config()->upstream_write_timeout, + get_config()->upstream_read_timeout, get_config()->write_rate, + get_config()->write_burst, get_config()->read_rate, + get_config()->read_burst, writecb, readcb, timeoutcb, this), + ipaddr_(ipaddr), port_(port), worker_(worker), + http2session_(worker_->next_http2_session()), + left_connhd_len_(NGHTTP2_CLIENT_MAGIC_LEN), + should_close_after_write_(false) { + + ++worker_->get_worker_stat()->num_connections; + + ev_timer_init(&reneg_shutdown_timer_, shutdowncb, 0., 0.); + + reneg_shutdown_timer_.data = this; + + conn_.rlimit.startw(); + ev_timer_again(conn_.loop, &conn_.rt); + + if (conn_.tls.ssl) { + SSL_set_app_data(conn_.tls.ssl, &conn_); + read_ = write_ = &ClientHandler::tls_handshake; + on_read_ = &ClientHandler::upstream_noop; + on_write_ = &ClientHandler::upstream_write; + } else { + // For non-TLS version, first create HttpsUpstream. It may be + // upgraded to HTTP/2 through HTTP Upgrade or direct HTTP/2 + // connection. + upstream_ = make_unique(this); + alpn_ = "http/1.1"; + read_ = &ClientHandler::read_clear; + write_ = &ClientHandler::write_clear; + on_read_ = &ClientHandler::upstream_http1_connhd_read; + on_write_ = &ClientHandler::upstream_noop; + } +} + +ClientHandler::~ClientHandler() { + if (LOG_ENABLED(INFO)) { + CLOG(INFO, this) << "Deleting"; + } + + if (upstream_) { + upstream_->on_handler_delete(); + } + + auto worker_stat = worker_->get_worker_stat(); + --worker_stat->num_connections; + + if (worker_stat->num_connections == 0) { + worker_->schedule_clear_mcpool(); + } + + ev_timer_stop(conn_.loop, &reneg_shutdown_timer_); + + // TODO If backend is http/2, and it is in CONNECTED state, signal + // it and make it loopbreak when output is zero. + if (worker_->get_graceful_shutdown() && worker_stat->num_connections == 0) { + ev_break(conn_.loop); + } + + if (LOG_ENABLED(INFO)) { + CLOG(INFO, this) << "Deleted"; + } +} + +Upstream *ClientHandler::get_upstream() { return upstream_.get(); } + +struct ev_loop *ClientHandler::get_loop() const { + return conn_.loop; +} + +void ClientHandler::reset_upstream_read_timeout(ev_tstamp t) { + conn_.rt.repeat = t; + if (ev_is_active(&conn_.rt)) { + ev_timer_again(conn_.loop, &conn_.rt); + } +} + +void ClientHandler::reset_upstream_write_timeout(ev_tstamp t) { + conn_.wt.repeat = t; + if (ev_is_active(&conn_.wt)) { + ev_timer_again(conn_.loop, &conn_.wt); + } +} + +int ClientHandler::validate_next_proto() { + const unsigned char *next_proto = nullptr; + unsigned int next_proto_len; + int rv; + + // First set callback for catch all cases + on_read_ = &ClientHandler::upstream_read; + + SSL_get0_next_proto_negotiated(conn_.tls.ssl, &next_proto, &next_proto_len); + for (int i = 0; i < 2; ++i) { + if (next_proto) { + if (LOG_ENABLED(INFO)) { + std::string proto(next_proto, next_proto + next_proto_len); + CLOG(INFO, this) << "The negotiated next protocol: " << proto; + } + if (!ssl::in_proto_list(get_config()->npn_list, next_proto, + next_proto_len)) { + break; + } + if (util::check_h2_is_selected(next_proto, next_proto_len)) { + + on_read_ = &ClientHandler::upstream_http2_connhd_read; + + auto http2_upstream = make_unique(this); + + if (!ssl::check_http2_requirement(conn_.tls.ssl)) { + rv = http2_upstream->terminate_session(NGHTTP2_INADEQUATE_SECURITY); + + if (rv != 0) { + return -1; + } + } + + upstream_ = std::move(http2_upstream); + alpn_.assign(next_proto, next_proto + next_proto_len); + + // At this point, input buffer is already filled with some + // bytes. The read callback is not called until new data + // come. So consume input buffer here. + if (on_read() != 0) { + return -1; + } + + return 0; + } else { +#ifdef HAVE_SPDYLAY + uint16_t version = spdylay_npn_get_version(next_proto, next_proto_len); + if (version) { + upstream_ = make_unique(version, this); + + switch (version) { + case SPDYLAY_PROTO_SPDY2: + alpn_ = "spdy/2"; + break; + case SPDYLAY_PROTO_SPDY3: + alpn_ = "spdy/3"; + break; + case SPDYLAY_PROTO_SPDY3_1: + alpn_ = "spdy/3.1"; + break; + default: + alpn_ = "spdy/unknown"; + } + + // At this point, input buffer is already filled with some + // bytes. The read callback is not called until new data + // come. So consume input buffer here. + if (on_read() != 0) { + return -1; + } + + return 0; + } +#endif // HAVE_SPDYLAY + if (next_proto_len == 8 && memcmp("http/1.1", next_proto, 8) == 0) { + upstream_ = make_unique(this); + alpn_ = "http/1.1"; + + // At this point, input buffer is already filled with some + // bytes. The read callback is not called until new data + // come. So consume input buffer here. + if (on_read() != 0) { + return -1; + } + + return 0; + } + } + break; + } +#if OPENSSL_VERSION_NUMBER >= 0x10002000L + SSL_get0_alpn_selected(conn_.tls.ssl, &next_proto, &next_proto_len); +#else // OPENSSL_VERSION_NUMBER < 0x10002000L + break; +#endif // OPENSSL_VERSION_NUMBER < 0x10002000L + } + if (!next_proto) { + if (LOG_ENABLED(INFO)) { + CLOG(INFO, this) << "No protocol negotiated. Fallback to HTTP/1.1"; + } + upstream_ = make_unique(this); + alpn_ = "http/1.1"; + + // At this point, input buffer is already filled with some bytes. + // The read callback is not called until new data come. So consume + // input buffer here. + if (on_read() != 0) { + return -1; + } + + return 0; + } + if (LOG_ENABLED(INFO)) { + CLOG(INFO, this) << "The negotiated protocol is not supported"; + } + return -1; +} + +int ClientHandler::do_read() { return read_(*this); } +int ClientHandler::do_write() { return write_(*this); } + +int ClientHandler::on_read() { + auto rv = on_read_(*this); + if (rv != 0) { + return rv; + } + conn_.handle_tls_pending_read(); + return 0; +} +int ClientHandler::on_write() { return on_write_(*this); } + +const std::string &ClientHandler::get_ipaddr() const { return ipaddr_; } + +bool ClientHandler::get_should_close_after_write() const { + return should_close_after_write_; +} + +void ClientHandler::set_should_close_after_write(bool f) { + should_close_after_write_ = f; +} + +void ClientHandler::pool_downstream_connection( + std::unique_ptr dconn) { + if (!dconn->poolable()) { + return; + } + if (LOG_ENABLED(INFO)) { + CLOG(INFO, this) << "Pooling downstream connection DCONN:" << dconn.get(); + } + dconn->set_client_handler(nullptr); + auto dconn_pool = worker_->get_dconn_pool(); + dconn_pool->add_downstream_connection(std::move(dconn)); +} + +void ClientHandler::remove_downstream_connection(DownstreamConnection *dconn) { + if (LOG_ENABLED(INFO)) { + CLOG(INFO, this) << "Removing downstream connection DCONN:" << dconn + << " from pool"; + } + auto dconn_pool = worker_->get_dconn_pool(); + dconn_pool->remove_downstream_connection(dconn); +} + +std::unique_ptr +ClientHandler::get_downstream_connection() { + auto dconn_pool = worker_->get_dconn_pool(); + auto dconn = dconn_pool->pop_downstream_connection(); + + if (!dconn) { + if (LOG_ENABLED(INFO)) { + CLOG(INFO, this) << "Downstream connection pool is empty." + << " Create new one"; + } + + auto dconn_pool = worker_->get_dconn_pool(); + + if (http2session_) { + dconn = make_unique(dconn_pool, http2session_); + } else { + dconn = make_unique(dconn_pool, conn_.loop); + } + dconn->set_client_handler(this); + return dconn; + } + + dconn->set_client_handler(this); + + if (LOG_ENABLED(INFO)) { + CLOG(INFO, this) << "Reuse downstream connection DCONN:" << dconn.get() + << " from pool"; + } + + return dconn; +} + +MemchunkPool *ClientHandler::get_mcpool() { return worker_->get_mcpool(); } + +SSL *ClientHandler::get_ssl() const { return conn_.tls.ssl; } + +ConnectBlocker *ClientHandler::get_connect_blocker() const { + return worker_->get_connect_blocker(); +} + +void ClientHandler::direct_http2_upgrade() { + upstream_ = make_unique(this); + alpn_ = NGHTTP2_CLEARTEXT_PROTO_VERSION_ID; + on_read_ = &ClientHandler::upstream_read; +} + +int ClientHandler::perform_http2_upgrade(HttpsUpstream *http) { + auto upstream = make_unique(this); + if (upstream->upgrade_upstream(http) != 0) { + return -1; + } + // http pointer is now owned by upstream. + upstream_.release(); + upstream_ = std::move(upstream); + // TODO We might get other version id in HTTP2-settings, if we + // support aliasing for h2, but we just use library default for now. + alpn_ = NGHTTP2_CLEARTEXT_PROTO_VERSION_ID; + on_read_ = &ClientHandler::upstream_http2_connhd_read; + + static char res[] = "HTTP/1.1 101 Switching Protocols\r\n" + "Connection: Upgrade\r\n" + "Upgrade: " NGHTTP2_CLEARTEXT_PROTO_VERSION_ID "\r\n" + "\r\n"; + wb_.write(res, sizeof(res) - 1); + signal_write(); + return 0; +} + +bool ClientHandler::get_http2_upgrade_allowed() const { return !conn_.tls.ssl; } + +std::string ClientHandler::get_upstream_scheme() const { + if (conn_.tls.ssl) { + return "https"; + } else { + return "http"; + } +} + +void ClientHandler::start_immediate_shutdown() { + ev_timer_start(conn_.loop, &reneg_shutdown_timer_); +} + +namespace { +// Construct absolute request URI from |downstream|, mainly to log +// request URI for proxy request (HTTP/2 proxy or client proxy). This +// is mostly same routine found in +// HttpDownstreamConnection::push_request_headers(), but vastly +// simplified since we only care about absolute URI. +std::string construct_absolute_request_uri(Downstream *downstream) { + const char *authority = nullptr, *host = nullptr; + if (!downstream->get_request_http2_authority().empty()) { + authority = downstream->get_request_http2_authority().c_str(); + } + auto h = downstream->get_request_header(http2::HD_HOST); + if (h) { + host = h->value.c_str(); + } + if (!authority && !host) { + return downstream->get_request_path(); + } + std::string uri; + if (downstream->get_request_http2_scheme().empty()) { + // this comes from HTTP/1 upstream without scheme. Just use http. + uri += "http://"; + } else { + uri += downstream->get_request_http2_scheme(); + uri += "://"; + } + if (authority) { + uri += authority; + } else { + uri += host; + } + + // Server-wide OPTIONS takes following form in proxy request: + // + // OPTIONS http://example.org HTTP/1.1 + // + // Notice that no slash after authority. See + // http://tools.ietf.org/html/rfc7230#section-5.3.4 + if (downstream->get_request_path() != "*") { + uri += downstream->get_request_path(); + } + return uri; +} +} // namespace + +void ClientHandler::write_accesslog(Downstream *downstream) { + upstream_accesslog( + get_config()->accesslog_format, + LogSpec{ + downstream, ipaddr_.c_str(), downstream->get_request_method().c_str(), + + (downstream->get_request_method() != "CONNECT" && + (get_config()->http2_proxy || get_config()->client_proxy)) + ? construct_absolute_request_uri(downstream).c_str() + : downstream->get_request_path().empty() + ? downstream->get_request_http2_authority().c_str() + : downstream->get_request_path().c_str(), + + alpn_.c_str(), + + std::chrono::system_clock::now(), // time_now + downstream->get_request_start_time(), // request_start_time + std::chrono::high_resolution_clock::now(), // request_end_time + + downstream->get_request_major(), downstream->get_request_minor(), + downstream->get_response_http_status(), + downstream->get_response_sent_bodylen(), port_.c_str(), + get_config()->port, get_config()->pid, + }); +} + +void ClientHandler::write_accesslog(int major, int minor, unsigned int status, + int64_t body_bytes_sent) { + auto time_now = std::chrono::system_clock::now(); + auto highres_now = std::chrono::high_resolution_clock::now(); + + upstream_accesslog(get_config()->accesslog_format, + LogSpec{ + nullptr, ipaddr_.c_str(), + "-", // method + "-", // path, + alpn_.c_str(), time_now, + highres_now, // request_start_time TODO is + // there a better value? + highres_now, // request_end_time + major, minor, // major, minor + status, body_bytes_sent, port_.c_str(), + get_config()->port, get_config()->pid, + }); +} + +ClientHandler::WriteBuf *ClientHandler::get_wb() { return &wb_; } + +ClientHandler::ReadBuf *ClientHandler::get_rb() { return &rb_; } + +void ClientHandler::signal_write() { conn_.wlimit.startw(); } + +RateLimit *ClientHandler::get_rlimit() { return &conn_.rlimit; } +RateLimit *ClientHandler::get_wlimit() { return &conn_.wlimit; } + +ev_io *ClientHandler::get_wev() { return &conn_.wev; } + +Worker *ClientHandler::get_worker() const { return worker_; } + +} // namespace shrpx diff --git a/src/shrpx_client_handler.h b/src/shrpx_client_handler.h new file mode 100644 index 0000000..4367c2b --- /dev/null +++ b/src/shrpx_client_handler.h @@ -0,0 +1,154 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef SHRPX_CLIENT_HANDLER_H +#define SHRPX_CLIENT_HANDLER_H + +#include "shrpx.h" + +#include + +#include + +#include + +#include "shrpx_rate_limit.h" +#include "shrpx_connection.h" +#include "buffer.h" +#include "memchunk.h" + +using namespace nghttp2; + +namespace shrpx { + +class Upstream; +class DownstreamConnection; +class Http2Session; +class HttpsUpstream; +class ConnectBlocker; +class DownstreamConnectionPool; +class Worker; +struct WorkerStat; + +class ClientHandler { +public: + ClientHandler(Worker *worker, int fd, SSL *ssl, const char *ipaddr, + const char *port); + ~ClientHandler(); + + // Performs clear text I/O + int read_clear(); + int write_clear(); + // Performs TLS handshake + int tls_handshake(); + // Performs TLS I/O + int read_tls(); + int write_tls(); + + int upstream_noop(); + int upstream_read(); + int upstream_http2_connhd_read(); + int upstream_http1_connhd_read(); + int upstream_write(); + + // Performs I/O operation. Internally calls on_read()/on_write(). + int do_read(); + int do_write(); + + // Processes buffers. No underlying I/O operation will be done. + int on_read(); + int on_write(); + + struct ev_loop *get_loop() const; + void reset_upstream_read_timeout(ev_tstamp t); + void reset_upstream_write_timeout(ev_tstamp t); + int validate_next_proto(); + const std::string &get_ipaddr() const; + const std::string &get_port() const; + bool get_should_close_after_write() const; + void set_should_close_after_write(bool f); + Upstream *get_upstream(); + + void pool_downstream_connection(std::unique_ptr dconn); + void remove_downstream_connection(DownstreamConnection *dconn); + std::unique_ptr get_downstream_connection(); + MemchunkPool *get_mcpool(); + SSL *get_ssl() const; + ConnectBlocker *get_connect_blocker() const; + // Call this function when HTTP/2 connection header is received at + // the start of the connection. + void direct_http2_upgrade(); + // Performs HTTP/2 Upgrade from the connection managed by + // |http|. If this function fails, the connection must be + // terminated. This function returns 0 if it succeeds, or -1. + int perform_http2_upgrade(HttpsUpstream *http); + bool get_http2_upgrade_allowed() const; + // Returns upstream scheme, either "http" or "https" + std::string get_upstream_scheme() const; + void start_immediate_shutdown(); + + // Writes upstream accesslog using |downstream|. The |downstream| + // must not be nullptr. + void write_accesslog(Downstream *downstream); + + // Writes upstream accesslog. This function is used if + // corresponding Downstream object is not available. + void write_accesslog(int major, int minor, unsigned int status, + int64_t body_bytes_sent); + Worker *get_worker() const; + + using WriteBuf = Buffer<32768>; + using ReadBuf = Buffer<8192>; + + WriteBuf *get_wb(); + ReadBuf *get_rb(); + + RateLimit *get_rlimit(); + RateLimit *get_wlimit(); + + void signal_write(); + ev_io *get_wev(); + +private: + Connection conn_; + ev_timer reneg_shutdown_timer_; + std::unique_ptr upstream_; + std::string ipaddr_; + std::string port_; + // The ALPN identifier negotiated for this connection. + std::string alpn_; + std::function read_, write_; + std::function on_read_, on_write_; + Worker *worker_; + Http2Session *http2session_; + // The number of bytes of HTTP/2 client connection header to read + size_t left_connhd_len_; + bool should_close_after_write_; + WriteBuf wb_; + ReadBuf rb_; +}; + +} // namespace shrpx + +#endif // SHRPX_CLIENT_HANDLER_H diff --git a/src/shrpx_config.cc b/src/shrpx_config.cc new file mode 100644 index 0000000..6577343 --- /dev/null +++ b/src/shrpx_config.cc @@ -0,0 +1,1406 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "shrpx_config.h" + +#ifdef HAVE_PWD_H +#include +#endif // HAVE_PWD_H +#ifdef HAVE_NETDB_H +#include +#endif // HAVE_NETDB_H +#ifdef HAVE_SYSLOG_H +#include +#endif // HAVE_SYSLOG_H +#include +#include +#ifdef HAVE_FCNTL_H +#include +#endif // HAVE_FCNTL_H +#ifdef HAVE_UNISTD_H +#include +#endif // HAVE_UNISTD_H + +#include +#include +#include +#include + +#include + +#include "http-parser/http_parser.h" + +#include "shrpx_log.h" +#include "shrpx_ssl.h" +#include "shrpx_http.h" +#include "http2.h" +#include "util.h" +#include "template.h" + +using namespace nghttp2; + +namespace shrpx { + +const char SHRPX_OPT_PRIVATE_KEY_FILE[] = "private-key-file"; +const char SHRPX_OPT_PRIVATE_KEY_PASSWD_FILE[] = "private-key-passwd-file"; +const char SHRPX_OPT_CERTIFICATE_FILE[] = "certificate-file"; +const char SHRPX_OPT_DH_PARAM_FILE[] = "dh-param-file"; +const char SHRPX_OPT_SUBCERT[] = "subcert"; + +const char SHRPX_OPT_BACKEND[] = "backend"; +const char SHRPX_OPT_FRONTEND[] = "frontend"; +const char SHRPX_OPT_WORKERS[] = "workers"; +const char SHRPX_OPT_HTTP2_MAX_CONCURRENT_STREAMS[] = + "http2-max-concurrent-streams"; +const char SHRPX_OPT_LOG_LEVEL[] = "log-level"; +const char SHRPX_OPT_DAEMON[] = "daemon"; +const char SHRPX_OPT_HTTP2_PROXY[] = "http2-proxy"; +const char SHRPX_OPT_HTTP2_BRIDGE[] = "http2-bridge"; +const char SHRPX_OPT_CLIENT_PROXY[] = "client-proxy"; +const char SHRPX_OPT_ADD_X_FORWARDED_FOR[] = "add-x-forwarded-for"; +const char SHRPX_OPT_STRIP_INCOMING_X_FORWARDED_FOR[] = + "strip-incoming-x-forwarded-for"; +const char SHRPX_OPT_NO_VIA[] = "no-via"; +const char SHRPX_OPT_FRONTEND_HTTP2_READ_TIMEOUT[] = + "frontend-http2-read-timeout"; +const char SHRPX_OPT_FRONTEND_READ_TIMEOUT[] = "frontend-read-timeout"; +const char SHRPX_OPT_FRONTEND_WRITE_TIMEOUT[] = "frontend-write-timeout"; +const char SHRPX_OPT_BACKEND_READ_TIMEOUT[] = "backend-read-timeout"; +const char SHRPX_OPT_BACKEND_WRITE_TIMEOUT[] = "backend-write-timeout"; +const char SHRPX_OPT_STREAM_READ_TIMEOUT[] = "stream-read-timeout"; +const char SHRPX_OPT_STREAM_WRITE_TIMEOUT[] = "stream-write-timeout"; +const char SHRPX_OPT_ACCESSLOG_FILE[] = "accesslog-file"; +const char SHRPX_OPT_ACCESSLOG_SYSLOG[] = "accesslog-syslog"; +const char SHRPX_OPT_ACCESSLOG_FORMAT[] = "accesslog-format"; +const char SHRPX_OPT_ERRORLOG_FILE[] = "errorlog-file"; +const char SHRPX_OPT_ERRORLOG_SYSLOG[] = "errorlog-syslog"; +const char SHRPX_OPT_BACKEND_KEEP_ALIVE_TIMEOUT[] = + "backend-keep-alive-timeout"; +const char SHRPX_OPT_FRONTEND_HTTP2_WINDOW_BITS[] = + "frontend-http2-window-bits"; +const char SHRPX_OPT_BACKEND_HTTP2_WINDOW_BITS[] = "backend-http2-window-bits"; +const char SHRPX_OPT_FRONTEND_HTTP2_CONNECTION_WINDOW_BITS[] = + "frontend-http2-connection-window-bits"; +const char SHRPX_OPT_BACKEND_HTTP2_CONNECTION_WINDOW_BITS[] = + "backend-http2-connection-window-bits"; +const char SHRPX_OPT_FRONTEND_NO_TLS[] = "frontend-no-tls"; +const char SHRPX_OPT_BACKEND_NO_TLS[] = "backend-no-tls"; +const char SHRPX_OPT_BACKEND_TLS_SNI_FIELD[] = "backend-tls-sni-field"; +const char SHRPX_OPT_PID_FILE[] = "pid-file"; +const char SHRPX_OPT_USER[] = "user"; +const char SHRPX_OPT_SYSLOG_FACILITY[] = "syslog-facility"; +const char SHRPX_OPT_BACKLOG[] = "backlog"; +const char SHRPX_OPT_CIPHERS[] = "ciphers"; +const char SHRPX_OPT_CLIENT[] = "client"; +const char SHRPX_OPT_INSECURE[] = "insecure"; +const char SHRPX_OPT_CACERT[] = "cacert"; +const char SHRPX_OPT_BACKEND_IPV4[] = "backend-ipv4"; +const char SHRPX_OPT_BACKEND_IPV6[] = "backend-ipv6"; +const char SHRPX_OPT_BACKEND_HTTP_PROXY_URI[] = "backend-http-proxy-uri"; +const char SHRPX_OPT_READ_RATE[] = "read-rate"; +const char SHRPX_OPT_READ_BURST[] = "read-burst"; +const char SHRPX_OPT_WRITE_RATE[] = "write-rate"; +const char SHRPX_OPT_WRITE_BURST[] = "write-burst"; +const char SHRPX_OPT_WORKER_READ_RATE[] = "worker-read-rate"; +const char SHRPX_OPT_WORKER_READ_BURST[] = "worker-read-burst"; +const char SHRPX_OPT_WORKER_WRITE_RATE[] = "worker-write-rate"; +const char SHRPX_OPT_WORKER_WRITE_BURST[] = "worker-write-burst"; +const char SHRPX_OPT_NPN_LIST[] = "npn-list"; +const char SHRPX_OPT_TLS_PROTO_LIST[] = "tls-proto-list"; +const char SHRPX_OPT_VERIFY_CLIENT[] = "verify-client"; +const char SHRPX_OPT_VERIFY_CLIENT_CACERT[] = "verify-client-cacert"; +const char SHRPX_OPT_CLIENT_PRIVATE_KEY_FILE[] = "client-private-key-file"; +const char SHRPX_OPT_CLIENT_CERT_FILE[] = "client-cert-file"; +const char SHRPX_OPT_FRONTEND_HTTP2_DUMP_REQUEST_HEADER[] = + "frontend-http2-dump-request-header"; +const char SHRPX_OPT_FRONTEND_HTTP2_DUMP_RESPONSE_HEADER[] = + "frontend-http2-dump-response-header"; +const char SHRPX_OPT_HTTP2_NO_COOKIE_CRUMBLING[] = "http2-no-cookie-crumbling"; +const char SHRPX_OPT_FRONTEND_FRAME_DEBUG[] = "frontend-frame-debug"; +const char SHRPX_OPT_PADDING[] = "padding"; +const char SHRPX_OPT_ALTSVC[] = "altsvc"; +const char SHRPX_OPT_ADD_RESPONSE_HEADER[] = "add-response-header"; +const char SHRPX_OPT_WORKER_FRONTEND_CONNECTIONS[] = + "worker-frontend-connections"; +const char SHRPX_OPT_NO_LOCATION_REWRITE[] = "no-location-rewrite"; +const char SHRPX_OPT_NO_HOST_REWRITE[] = "no-host-rewrite"; +const char SHRPX_OPT_BACKEND_HTTP1_CONNECTIONS_PER_HOST[] = + "backend-http1-connections-per-host"; +const char SHRPX_OPT_BACKEND_HTTP1_CONNECTIONS_PER_FRONTEND[] = + "backend-http1-connections-per-frontend"; +const char SHRPX_OPT_LISTENER_DISABLE_TIMEOUT[] = "listener-disable-timeout"; +const char SHRPX_OPT_TLS_TICKET_KEY_FILE[] = "tls-ticket-key-file"; +const char SHRPX_OPT_RLIMIT_NOFILE[] = "rlimit-nofile"; +const char SHRPX_OPT_BACKEND_REQUEST_BUFFER[] = "backend-request-buffer"; +const char SHRPX_OPT_BACKEND_RESPONSE_BUFFER[] = "backend-response-buffer"; +const char SHRPX_OPT_NO_SERVER_PUSH[] = "no-server-push"; +const char SHRPX_OPT_BACKEND_HTTP2_CONNECTIONS_PER_WORKER[] = + "backend-http2-connections-per-worker"; +const char SHRPX_OPT_FETCH_OCSP_RESPONSE_FILE[] = "fetch-ocsp-response-file"; +const char SHRPX_OPT_OCSP_UPDATE_INTERVAL[] = "ocsp-update-interval"; +const char SHRPX_OPT_NO_OCSP[] = "no-ocsp"; +const char SHRPX_OPT_HEADER_FIELD_BUFFER[] = "header-field-buffer"; +const char SHRPX_OPT_MAX_HEADER_FIELDS[] = "max-header-fields"; + +namespace { +Config *config = nullptr; +} // namespace + +const Config *get_config() { return config; } + +Config *mod_config() { return config; } + +void create_config() { config = new Config(); } + +TicketKeys::~TicketKeys() { + /* Erase keys from memory */ + for (auto &key : keys) { + memset(&key, 0, sizeof(key)); + } +} + +namespace { +int split_host_port(char *host, size_t hostlen, uint16_t *port_ptr, + const char *hostport) { + // host and port in |hostport| is separated by single ','. + const char *p = strchr(hostport, ','); + if (!p) { + LOG(ERROR) << "Invalid host, port: " << hostport; + return -1; + } + size_t len = p - hostport; + if (hostlen < len + 1) { + LOG(ERROR) << "Hostname too long: " << hostport; + return -1; + } + memcpy(host, hostport, len); + host[len] = '\0'; + + errno = 0; + unsigned long d = strtoul(p + 1, nullptr, 10); + if (errno == 0 && 1 <= d && d <= std::numeric_limits::max()) { + *port_ptr = d; + return 0; + } else { + LOG(ERROR) << "Port is invalid: " << p + 1; + return -1; + } +} +} // namespace + +namespace { +bool is_secure(const char *filename) { + struct stat buf; + int rv = stat(filename, &buf); + if (rv == 0) { + if ((buf.st_mode & S_IRWXU) && !(buf.st_mode & S_IRWXG) && + !(buf.st_mode & S_IRWXO)) { + return true; + } + } + + return false; +} +} // namespace + +std::unique_ptr +read_tls_ticket_key_file(const std::vector &files) { + auto ticket_keys = make_unique(); + auto &keys = ticket_keys->keys; + keys.resize(files.size()); + size_t i = 0; + for (auto &file : files) { + std::ifstream f(file.c_str()); + if (!f) { + LOG(ERROR) << "tls-ticket-key-file: could not open file " << file; + return nullptr; + } + char buf[48]; + f.read(buf, sizeof(buf)); + if (f.gcount() != sizeof(buf)) { + LOG(ERROR) << "tls-ticket-key-file: want to read 48 bytes but read " + << f.gcount() << " bytes from " << file; + return nullptr; + } + + auto &key = keys[i++]; + auto p = buf; + memcpy(key.name, p, sizeof(key.name)); + p += sizeof(key.name); + memcpy(key.aes_key, p, sizeof(key.aes_key)); + p += sizeof(key.aes_key); + memcpy(key.hmac_key, p, sizeof(key.hmac_key)); + + if (LOG_ENABLED(INFO)) { + LOG(INFO) << "session ticket key: " << util::format_hex(key.name, + sizeof(key.name)); + } + } + return ticket_keys; +} + +FILE *open_file_for_write(const char *filename) { +#if defined O_CLOEXEC + auto fd = open(filename, O_WRONLY | O_CLOEXEC | O_CREAT | O_TRUNC, + S_IRUSR | S_IWUSR); +#else + auto fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR); + + // We get race condition if execve is called at the same time. + if (fd != -1) { + util::make_socket_closeonexec(fd); + } +#endif + if (fd == -1) { + LOG(ERROR) << "Failed to open " << filename + << " for writing. Cause: " << strerror(errno); + return nullptr; + } + auto f = fdopen(fd, "wb"); + if (f == nullptr) { + LOG(ERROR) << "Failed to open " << filename + << " for writing. Cause: " << strerror(errno); + return nullptr; + } + + return f; +} + +std::string read_passwd_from_file(const char *filename) { + std::string line; + + if (!is_secure(filename)) { + LOG(ERROR) << "Private key passwd file " << filename + << " has insecure mode."; + return line; + } + + std::ifstream in(filename, std::ios::binary); + if (!in) { + LOG(ERROR) << "Could not open key passwd file " << filename; + return line; + } + + std::getline(in, line); + return line; +} + +std::unique_ptr strcopy(const char *val) { + return strcopy(val, strlen(val)); +} + +std::unique_ptr strcopy(const char *val, size_t len) { + auto res = make_unique(len + 1); + memcpy(res.get(), val, len); + res[len] = '\0'; + return res; +} + +std::unique_ptr strcopy(const std::string &val) { + return strcopy(val.c_str(), val.size()); +} + +std::vector parse_config_str_list(const char *s) { + size_t len = 1; + for (const char *first = s, *p = nullptr; (p = strchr(first, ',')); + ++len, first = p + 1) + ; + auto list = std::vector(len); + auto first = strdup(s); + len = 0; + for (;;) { + auto p = strchr(first, ','); + if (p == nullptr) { + break; + } + list[len++] = first; + *p = '\0'; + first = p + 1; + } + list[len++] = first; + + return list; +} + +void clear_config_str_list(std::vector &list) { + if (list.empty()) { + return; + } + + free(list[0]); + list.clear(); +} + +std::pair parse_header(const char *optarg) { + // We skip possible ":" at the start of optarg. + const auto *colon = strchr(optarg + 1, ':'); + + // name = ":" is not allowed + if (colon == nullptr || (optarg[0] == ':' && colon == optarg + 1)) { + return {"", ""}; + } + + auto value = colon + 1; + for (; *value == '\t' || *value == ' '; ++value) + ; + + return {std::string(optarg, colon), std::string(value, strlen(value))}; +} + +template +int parse_uint(T *dest, const char *opt, const char *optarg) { + char *end = nullptr; + + errno = 0; + + auto val = strtol(optarg, &end, 10); + + if (!optarg[0] || errno != 0 || *end || val < 0) { + LOG(ERROR) << opt << ": bad value. Specify an integer >= 0."; + return -1; + } + + *dest = val; + + return 0; +} + +namespace { +template +int parse_uint_with_unit(T *dest, const char *opt, const char *optarg) { + auto n = util::parse_uint_with_unit(optarg); + if (n == -1) { + LOG(ERROR) << opt << ": bad value: '" << optarg << "'"; + return -1; + } + + *dest = n; + + return 0; +} +} // namespace + +template +int parse_int(T *dest, const char *opt, const char *optarg) { + char *end = nullptr; + + errno = 0; + + auto val = strtol(optarg, &end, 10); + + if (!optarg[0] || errno != 0 || *end) { + LOG(ERROR) << opt << ": bad value. Specify an integer."; + return -1; + } + + *dest = val; + + return 0; +} + +namespace { +LogFragment make_log_fragment(LogFragmentType type, + std::unique_ptr value = nullptr) { + return LogFragment{type, std::move(value)}; +} +} // namespace + +namespace { +bool var_token(char c) { + return util::isAlpha(c) || util::isDigit(c) || c == '_'; +} +} // namespace + +std::vector parse_log_format(const char *optarg) { + auto literal_start = optarg; + auto p = optarg; + auto eop = p + strlen(optarg); + + auto res = std::vector(); + + for (; p != eop;) { + if (*p != '$') { + ++p; + continue; + } + + auto var_start = p; + + ++p; + + for (; p != eop && var_token(*p); ++p) + ; + + auto varlen = p - var_start; + + auto type = SHRPX_LOGF_NONE; + const char *value = nullptr; + size_t valuelen = 0; + + if (util::strieq_l("$remote_addr", var_start, varlen)) { + type = SHRPX_LOGF_REMOTE_ADDR; + } else if (util::strieq_l("$time_local", var_start, varlen)) { + type = SHRPX_LOGF_TIME_LOCAL; + } else if (util::strieq_l("$time_iso8601", var_start, varlen)) { + type = SHRPX_LOGF_TIME_ISO8601; + } else if (util::strieq_l("$request", var_start, varlen)) { + type = SHRPX_LOGF_REQUEST; + } else if (util::strieq_l("$status", var_start, varlen)) { + type = SHRPX_LOGF_STATUS; + } else if (util::strieq_l("$body_bytes_sent", var_start, varlen)) { + type = SHRPX_LOGF_BODY_BYTES_SENT; + } else if (util::istartsWith(var_start, varlen, "$http_")) { + type = SHRPX_LOGF_HTTP; + value = var_start + sizeof("$http_") - 1; + valuelen = varlen - (sizeof("$http_") - 1); + } else if (util::strieq_l("$remote_port", var_start, varlen)) { + type = SHRPX_LOGF_REMOTE_PORT; + } else if (util::strieq_l("$server_port", var_start, varlen)) { + type = SHRPX_LOGF_SERVER_PORT; + } else if (util::strieq_l("$request_time", var_start, varlen)) { + type = SHRPX_LOGF_REQUEST_TIME; + } else if (util::strieq_l("$pid", var_start, varlen)) { + type = SHRPX_LOGF_PID; + } else if (util::strieq_l("$alpn", var_start, varlen)) { + type = SHRPX_LOGF_ALPN; + } else { + LOG(WARN) << "Unrecognized log format variable: " + << std::string(var_start, varlen); + continue; + } + + if (literal_start < var_start) { + res.push_back( + make_log_fragment(SHRPX_LOGF_LITERAL, + strcopy(literal_start, var_start - literal_start))); + } + + if (value == nullptr) { + res.push_back(make_log_fragment(type)); + } else { + res.push_back(make_log_fragment(type, strcopy(value, valuelen))); + auto &v = res.back().value; + for (size_t i = 0; v[i]; ++i) { + if (v[i] == '_') { + v[i] = '-'; + } + } + } + + literal_start = var_start + varlen; + } + + if (literal_start != eop) { + res.push_back(make_log_fragment( + SHRPX_LOGF_LITERAL, strcopy(literal_start, eop - literal_start))); + } + + return res; +} + +namespace { +int parse_duration(ev_tstamp *dest, const char *opt, const char *optarg) { + auto t = util::parse_duration_with_unit(optarg); + if (t == std::numeric_limits::infinity()) { + LOG(ERROR) << opt << ": bad value: '" << optarg << "'"; + return -1; + } + + *dest = t; + + return 0; +} +} // namespace + +int parse_config(const char *opt, const char *optarg) { + char host[NI_MAXHOST]; + uint16_t port; + if (util::strieq(opt, SHRPX_OPT_BACKEND)) { + if (util::istartsWith(optarg, SHRPX_UNIX_PATH_PREFIX)) { + DownstreamAddr addr; + auto path = optarg + str_size(SHRPX_UNIX_PATH_PREFIX); + addr.host = strcopy(path); + addr.host_unix = true; + + mod_config()->downstream_addrs.push_back(std::move(addr)); + + return 0; + } + + if (split_host_port(host, sizeof(host), &port, optarg) == -1) { + return -1; + } + + DownstreamAddr addr; + addr.host = strcopy(host); + addr.port = port; + + mod_config()->downstream_addrs.push_back(std::move(addr)); + + return 0; + } + + if (util::strieq(opt, SHRPX_OPT_FRONTEND)) { + if (util::istartsWith(optarg, SHRPX_UNIX_PATH_PREFIX)) { + auto path = optarg + str_size(SHRPX_UNIX_PATH_PREFIX); + mod_config()->host = strcopy(path); + mod_config()->port = 0; + mod_config()->host_unix = true; + + return 0; + } + + if (split_host_port(host, sizeof(host), &port, optarg) == -1) { + return -1; + } + + mod_config()->host = strcopy(host); + mod_config()->port = port; + mod_config()->host_unix = false; + + return 0; + } + + if (util::strieq(opt, SHRPX_OPT_WORKERS)) { + return parse_uint(&mod_config()->num_worker, opt, optarg); + } + + if (util::strieq(opt, SHRPX_OPT_HTTP2_MAX_CONCURRENT_STREAMS)) { + return parse_uint(&mod_config()->http2_max_concurrent_streams, opt, optarg); + } + + if (util::strieq(opt, SHRPX_OPT_LOG_LEVEL)) { + if (Log::set_severity_level_by_name(optarg) == -1) { + LOG(ERROR) << opt << ": Invalid severity level: " << optarg; + return -1; + } + + return 0; + } + + if (util::strieq(opt, SHRPX_OPT_DAEMON)) { + mod_config()->daemon = util::strieq(optarg, "yes"); + + return 0; + } + + if (util::strieq(opt, SHRPX_OPT_HTTP2_PROXY)) { + mod_config()->http2_proxy = util::strieq(optarg, "yes"); + + return 0; + } + + if (util::strieq(opt, SHRPX_OPT_HTTP2_BRIDGE)) { + mod_config()->http2_bridge = util::strieq(optarg, "yes"); + + return 0; + } + + if (util::strieq(opt, SHRPX_OPT_CLIENT_PROXY)) { + mod_config()->client_proxy = util::strieq(optarg, "yes"); + + return 0; + } + + if (util::strieq(opt, SHRPX_OPT_ADD_X_FORWARDED_FOR)) { + mod_config()->add_x_forwarded_for = util::strieq(optarg, "yes"); + + return 0; + } + + if (util::strieq(opt, SHRPX_OPT_STRIP_INCOMING_X_FORWARDED_FOR)) { + mod_config()->strip_incoming_x_forwarded_for = util::strieq(optarg, "yes"); + + return 0; + } + + if (util::strieq(opt, SHRPX_OPT_NO_VIA)) { + mod_config()->no_via = util::strieq(optarg, "yes"); + + return 0; + } + + if (util::strieq(opt, SHRPX_OPT_FRONTEND_HTTP2_READ_TIMEOUT)) { + return parse_duration(&mod_config()->http2_upstream_read_timeout, opt, + optarg); + } + + if (util::strieq(opt, SHRPX_OPT_FRONTEND_READ_TIMEOUT)) { + return parse_duration(&mod_config()->upstream_read_timeout, opt, optarg); + } + + if (util::strieq(opt, SHRPX_OPT_FRONTEND_WRITE_TIMEOUT)) { + return parse_duration(&mod_config()->upstream_write_timeout, opt, optarg); + } + + if (util::strieq(opt, SHRPX_OPT_BACKEND_READ_TIMEOUT)) { + return parse_duration(&mod_config()->downstream_read_timeout, opt, optarg); + } + + if (util::strieq(opt, SHRPX_OPT_BACKEND_WRITE_TIMEOUT)) { + return parse_duration(&mod_config()->downstream_write_timeout, opt, optarg); + } + + if (util::strieq(opt, SHRPX_OPT_STREAM_READ_TIMEOUT)) { + return parse_duration(&mod_config()->stream_read_timeout, opt, optarg); + } + + if (util::strieq(opt, SHRPX_OPT_STREAM_WRITE_TIMEOUT)) { + return parse_duration(&mod_config()->stream_write_timeout, opt, optarg); + } + + if (util::strieq(opt, SHRPX_OPT_ACCESSLOG_FILE)) { + mod_config()->accesslog_file = strcopy(optarg); + + return 0; + } + + if (util::strieq(opt, SHRPX_OPT_ACCESSLOG_SYSLOG)) { + mod_config()->accesslog_syslog = util::strieq(optarg, "yes"); + + return 0; + } + + if (util::strieq(opt, SHRPX_OPT_ACCESSLOG_FORMAT)) { + mod_config()->accesslog_format = parse_log_format(optarg); + + return 0; + } + + if (util::strieq(opt, SHRPX_OPT_ERRORLOG_FILE)) { + mod_config()->errorlog_file = strcopy(optarg); + + return 0; + } + + if (util::strieq(opt, SHRPX_OPT_ERRORLOG_SYSLOG)) { + mod_config()->errorlog_syslog = util::strieq(optarg, "yes"); + + return 0; + } + + if (util::strieq(opt, SHRPX_OPT_BACKEND_KEEP_ALIVE_TIMEOUT)) { + return parse_duration(&mod_config()->downstream_idle_read_timeout, opt, + optarg); + } + + if (util::strieq(opt, SHRPX_OPT_FRONTEND_HTTP2_WINDOW_BITS) || + util::strieq(opt, SHRPX_OPT_BACKEND_HTTP2_WINDOW_BITS)) { + + size_t *resp; + + if (util::strieq(opt, SHRPX_OPT_FRONTEND_HTTP2_WINDOW_BITS)) { + resp = &mod_config()->http2_upstream_window_bits; + } else { + resp = &mod_config()->http2_downstream_window_bits; + } + + errno = 0; + + int n; + + if (parse_uint(&n, opt, optarg) != 0) { + return -1; + } + + if (n >= 31) { + LOG(ERROR) << opt + << ": specify the integer in the range [0, 30], inclusive"; + return -1; + } + + *resp = n; + + return 0; + } + + if (util::strieq(opt, SHRPX_OPT_FRONTEND_HTTP2_CONNECTION_WINDOW_BITS) || + util::strieq(opt, SHRPX_OPT_BACKEND_HTTP2_CONNECTION_WINDOW_BITS)) { + + size_t *resp; + + if (util::strieq(opt, SHRPX_OPT_FRONTEND_HTTP2_CONNECTION_WINDOW_BITS)) { + resp = &mod_config()->http2_upstream_connection_window_bits; + } else { + resp = &mod_config()->http2_downstream_connection_window_bits; + } + + errno = 0; + + int n; + + if (parse_uint(&n, opt, optarg) != 0) { + return -1; + } + + if (n < 16 || n >= 31) { + LOG(ERROR) << opt + << ": specify the integer in the range [16, 30], inclusive"; + return -1; + } + + *resp = n; + + return 0; + } + + if (util::strieq(opt, SHRPX_OPT_FRONTEND_NO_TLS)) { + mod_config()->upstream_no_tls = util::strieq(optarg, "yes"); + + return 0; + } + + if (util::strieq(opt, SHRPX_OPT_BACKEND_NO_TLS)) { + mod_config()->downstream_no_tls = util::strieq(optarg, "yes"); + + return 0; + } + + if (util::strieq(opt, SHRPX_OPT_BACKEND_TLS_SNI_FIELD)) { + mod_config()->backend_tls_sni_name = strcopy(optarg); + + return 0; + } + + if (util::strieq(opt, SHRPX_OPT_PID_FILE)) { + mod_config()->pid_file = strcopy(optarg); + + return 0; + } + + if (util::strieq(opt, SHRPX_OPT_USER)) { + auto pwd = getpwnam(optarg); + if (!pwd) { + LOG(ERROR) << opt << ": failed to get uid from " << optarg << ": " + << strerror(errno); + return -1; + } + mod_config()->user = strcopy(pwd->pw_name); + mod_config()->uid = pwd->pw_uid; + mod_config()->gid = pwd->pw_gid; + + return 0; + } + + if (util::strieq(opt, SHRPX_OPT_PRIVATE_KEY_FILE)) { + mod_config()->private_key_file = strcopy(optarg); + + return 0; + } + + if (util::strieq(opt, SHRPX_OPT_PRIVATE_KEY_PASSWD_FILE)) { + auto passwd = read_passwd_from_file(optarg); + if (passwd.empty()) { + LOG(ERROR) << opt << ": Couldn't read key file's passwd from " << optarg; + return -1; + } + mod_config()->private_key_passwd = strcopy(passwd); + + return 0; + } + + if (util::strieq(opt, SHRPX_OPT_CERTIFICATE_FILE)) { + mod_config()->cert_file = strcopy(optarg); + + return 0; + } + + if (util::strieq(opt, SHRPX_OPT_DH_PARAM_FILE)) { + mod_config()->dh_param_file = strcopy(optarg); + + return 0; + } + + if (util::strieq(opt, SHRPX_OPT_SUBCERT)) { + // Private Key file and certificate file separated by ':'. + const char *sp = strchr(optarg, ':'); + if (sp) { + std::string keyfile(optarg, sp); + // TODO Do we need private key for subcert? + mod_config()->subcerts.emplace_back(keyfile, sp + 1); + } + + return 0; + } + + if (util::strieq(opt, SHRPX_OPT_SYSLOG_FACILITY)) { + int facility = int_syslog_facility(optarg); + if (facility == -1) { + LOG(ERROR) << opt << ": Unknown syslog facility: " << optarg; + return -1; + } + mod_config()->syslog_facility = facility; + + return 0; + } + + if (util::strieq(opt, SHRPX_OPT_BACKLOG)) { + int n; + if (parse_int(&n, opt, optarg) != 0) { + return -1; + } + + if (n < -1) { + LOG(ERROR) << opt << ": " << optarg << " is not allowed"; + + return -1; + } + + mod_config()->backlog = n; + + return 0; + } + + if (util::strieq(opt, SHRPX_OPT_CIPHERS)) { + mod_config()->ciphers = strcopy(optarg); + + return 0; + } + + if (util::strieq(opt, SHRPX_OPT_CLIENT)) { + mod_config()->client = util::strieq(optarg, "yes"); + + return 0; + } + + if (util::strieq(opt, SHRPX_OPT_INSECURE)) { + mod_config()->insecure = util::strieq(optarg, "yes"); + + return 0; + } + + if (util::strieq(opt, SHRPX_OPT_CACERT)) { + mod_config()->cacert = strcopy(optarg); + + return 0; + } + + if (util::strieq(opt, SHRPX_OPT_BACKEND_IPV4)) { + mod_config()->backend_ipv4 = util::strieq(optarg, "yes"); + + return 0; + } + + if (util::strieq(opt, SHRPX_OPT_BACKEND_IPV6)) { + mod_config()->backend_ipv6 = util::strieq(optarg, "yes"); + + return 0; + } + + if (util::strieq(opt, SHRPX_OPT_BACKEND_HTTP_PROXY_URI)) { + // parse URI and get hostname, port and optionally userinfo. + http_parser_url u; + memset(&u, 0, sizeof(u)); + int rv = http_parser_parse_url(optarg, strlen(optarg), 0, &u); + if (rv == 0) { + std::string val; + if (u.field_set & UF_USERINFO) { + http2::copy_url_component(val, &u, UF_USERINFO, optarg); + // Surprisingly, u.field_set & UF_USERINFO is nonzero even if + // userinfo component is empty string. + if (!val.empty()) { + val = util::percentDecode(val.begin(), val.end()); + mod_config()->downstream_http_proxy_userinfo = strcopy(val); + } + } + if (u.field_set & UF_HOST) { + http2::copy_url_component(val, &u, UF_HOST, optarg); + mod_config()->downstream_http_proxy_host = strcopy(val); + } else { + LOG(ERROR) << opt << ": no hostname specified"; + return -1; + } + if (u.field_set & UF_PORT) { + mod_config()->downstream_http_proxy_port = u.port; + } else { + LOG(ERROR) << opt << ": no port specified"; + return -1; + } + } else { + LOG(ERROR) << opt << ": parse error"; + return -1; + } + + return 0; + } + + if (util::strieq(opt, SHRPX_OPT_READ_RATE)) { + return parse_uint_with_unit(&mod_config()->read_rate, opt, optarg); + } + + if (util::strieq(opt, SHRPX_OPT_READ_BURST)) { + return parse_uint_with_unit(&mod_config()->read_burst, opt, optarg); + } + + if (util::strieq(opt, SHRPX_OPT_WRITE_RATE)) { + return parse_uint_with_unit(&mod_config()->write_rate, opt, optarg); + } + + if (util::strieq(opt, SHRPX_OPT_WRITE_BURST)) { + return parse_uint_with_unit(&mod_config()->write_burst, opt, optarg); + } + + if (util::strieq(opt, SHRPX_OPT_WORKER_READ_RATE)) { + LOG(WARN) << opt << ": not implemented yet"; + return parse_uint_with_unit(&mod_config()->worker_read_rate, opt, optarg); + } + + if (util::strieq(opt, SHRPX_OPT_WORKER_READ_BURST)) { + LOG(WARN) << opt << ": not implemented yet"; + return parse_uint_with_unit(&mod_config()->worker_read_burst, opt, optarg); + } + + if (util::strieq(opt, SHRPX_OPT_WORKER_WRITE_RATE)) { + LOG(WARN) << opt << ": not implemented yet"; + return parse_uint_with_unit(&mod_config()->worker_write_rate, opt, optarg); + } + + if (util::strieq(opt, SHRPX_OPT_WORKER_WRITE_BURST)) { + LOG(WARN) << opt << ": not implemented yet"; + return parse_uint_with_unit(&mod_config()->worker_write_burst, opt, optarg); + } + + if (util::strieq(opt, SHRPX_OPT_NPN_LIST)) { + clear_config_str_list(mod_config()->npn_list); + + mod_config()->npn_list = parse_config_str_list(optarg); + + return 0; + } + + if (util::strieq(opt, SHRPX_OPT_TLS_PROTO_LIST)) { + clear_config_str_list(mod_config()->tls_proto_list); + + mod_config()->tls_proto_list = parse_config_str_list(optarg); + + return 0; + } + + if (util::strieq(opt, SHRPX_OPT_VERIFY_CLIENT)) { + mod_config()->verify_client = util::strieq(optarg, "yes"); + + return 0; + } + + if (util::strieq(opt, SHRPX_OPT_VERIFY_CLIENT_CACERT)) { + mod_config()->verify_client_cacert = strcopy(optarg); + + return 0; + } + + if (util::strieq(opt, SHRPX_OPT_CLIENT_PRIVATE_KEY_FILE)) { + mod_config()->client_private_key_file = strcopy(optarg); + + return 0; + } + + if (util::strieq(opt, SHRPX_OPT_CLIENT_CERT_FILE)) { + mod_config()->client_cert_file = strcopy(optarg); + + return 0; + } + + if (util::strieq(opt, SHRPX_OPT_FRONTEND_HTTP2_DUMP_REQUEST_HEADER)) { + mod_config()->http2_upstream_dump_request_header_file = strcopy(optarg); + + return 0; + } + + if (util::strieq(opt, SHRPX_OPT_FRONTEND_HTTP2_DUMP_RESPONSE_HEADER)) { + mod_config()->http2_upstream_dump_response_header_file = strcopy(optarg); + + return 0; + } + + if (util::strieq(opt, SHRPX_OPT_HTTP2_NO_COOKIE_CRUMBLING)) { + mod_config()->http2_no_cookie_crumbling = util::strieq(optarg, "yes"); + + return 0; + } + + if (util::strieq(opt, SHRPX_OPT_FRONTEND_FRAME_DEBUG)) { + mod_config()->upstream_frame_debug = util::strieq(optarg, "yes"); + + return 0; + } + + if (util::strieq(opt, SHRPX_OPT_PADDING)) { + return parse_uint(&mod_config()->padding, opt, optarg); + } + + if (util::strieq(opt, SHRPX_OPT_ALTSVC)) { + auto tokens = parse_config_str_list(optarg); + + if (tokens.size() < 2) { + // Requires at least protocol_id and port + LOG(ERROR) << opt << ": too few parameters: " << optarg; + return -1; + } + + if (tokens.size() > 4) { + // We only need protocol_id, port, host and origin + LOG(ERROR) << opt << ": too many parameters: " << optarg; + return -1; + } + + int port; + + if (parse_uint(&port, opt, tokens[1]) != 0) { + return -1; + } + + if (port < 1 || + port > static_cast(std::numeric_limits::max())) { + LOG(ERROR) << opt << ": port is invalid: " << tokens[1]; + return -1; + } + + AltSvc altsvc; + + altsvc.port = port; + + altsvc.protocol_id = tokens[0]; + altsvc.protocol_id_len = strlen(altsvc.protocol_id); + + if (tokens.size() > 2) { + altsvc.host = tokens[2]; + altsvc.host_len = strlen(altsvc.host); + + if (tokens.size() > 3) { + altsvc.origin = tokens[3]; + altsvc.origin_len = strlen(altsvc.origin); + } + } + + mod_config()->altsvcs.push_back(std::move(altsvc)); + + return 0; + } + + if (util::strieq(opt, SHRPX_OPT_ADD_RESPONSE_HEADER)) { + auto p = parse_header(optarg); + if (p.first.empty()) { + LOG(ERROR) << opt << ": header field name is empty: " << optarg; + return -1; + } + mod_config()->add_response_headers.push_back(std::move(p)); + + return 0; + } + + if (util::strieq(opt, SHRPX_OPT_WORKER_FRONTEND_CONNECTIONS)) { + return parse_uint(&mod_config()->worker_frontend_connections, opt, optarg); + } + + if (util::strieq(opt, SHRPX_OPT_NO_LOCATION_REWRITE)) { + mod_config()->no_location_rewrite = util::strieq(optarg, "yes"); + + return 0; + } + + if (util::strieq(opt, SHRPX_OPT_NO_HOST_REWRITE)) { + mod_config()->no_host_rewrite = util::strieq(optarg, "yes"); + + return 0; + } + + if (util::strieq(opt, SHRPX_OPT_BACKEND_HTTP1_CONNECTIONS_PER_HOST)) { + int n; + + if (parse_uint(&n, opt, optarg) != 0) { + return -1; + } + + if (n == 0) { + LOG(ERROR) << opt << ": specify an integer strictly more than 0"; + + return -1; + } + + mod_config()->downstream_connections_per_host = n; + + return 0; + } + + if (util::strieq(opt, SHRPX_OPT_BACKEND_HTTP1_CONNECTIONS_PER_FRONTEND)) { + return parse_uint(&mod_config()->downstream_connections_per_frontend, opt, + optarg); + } + + if (util::strieq(opt, SHRPX_OPT_LISTENER_DISABLE_TIMEOUT)) { + return parse_duration(&mod_config()->listener_disable_timeout, opt, optarg); + } + + if (util::strieq(opt, SHRPX_OPT_TLS_TICKET_KEY_FILE)) { + mod_config()->tls_ticket_key_files.push_back(optarg); + return 0; + } + + if (util::strieq(opt, SHRPX_OPT_RLIMIT_NOFILE)) { + int n; + + if (parse_uint(&n, opt, optarg) != 0) { + return -1; + } + + if (n < 0) { + LOG(ERROR) << opt << ": specify the integer more than or equal to 0"; + + return -1; + } + + mod_config()->rlimit_nofile = n; + + return 0; + } + + if (util::strieq(opt, SHRPX_OPT_BACKEND_REQUEST_BUFFER) || + util::strieq(opt, SHRPX_OPT_BACKEND_RESPONSE_BUFFER)) { + size_t n; + if (parse_uint_with_unit(&n, opt, optarg) != 0) { + return -1; + } + + if (n == 0) { + LOG(ERROR) << opt << ": specify an integer strictly more than 0"; + + return -1; + } + + if (util::strieq(opt, SHRPX_OPT_BACKEND_REQUEST_BUFFER)) { + mod_config()->downstream_request_buffer_size = n; + } else { + mod_config()->downstream_response_buffer_size = n; + } + + return 0; + } + + if (util::strieq(opt, SHRPX_OPT_NO_SERVER_PUSH)) { + mod_config()->no_server_push = util::strieq(optarg, "yes"); + + return 0; + } + + if (util::strieq(opt, SHRPX_OPT_BACKEND_HTTP2_CONNECTIONS_PER_WORKER)) { + return parse_uint(&mod_config()->http2_downstream_connections_per_worker, + opt, optarg); + } + + if (util::strieq(opt, SHRPX_OPT_FETCH_OCSP_RESPONSE_FILE)) { + mod_config()->fetch_ocsp_response_file = strcopy(optarg); + + return 0; + } + + if (util::strieq(opt, SHRPX_OPT_OCSP_UPDATE_INTERVAL)) { + return parse_duration(&mod_config()->ocsp_update_interval, opt, optarg); + } + + if (util::strieq(opt, SHRPX_OPT_NO_OCSP)) { + mod_config()->no_ocsp = util::strieq(optarg, "yes"); + + return 0; + } + + if (util::strieq(opt, SHRPX_OPT_HEADER_FIELD_BUFFER)) { + return parse_uint_with_unit(&mod_config()->header_field_buffer, opt, + optarg); + } + + if (util::strieq(opt, SHRPX_OPT_MAX_HEADER_FIELDS)) { + return parse_uint(&mod_config()->max_header_fields, opt, optarg); + } + + if (util::strieq(opt, "conf")) { + LOG(WARN) << "conf: ignored"; + + return 0; + } + + LOG(ERROR) << "Unknown option: " << opt; + + return -1; +} + +int load_config(const char *filename) { + std::ifstream in(filename, std::ios::binary); + if (!in) { + LOG(ERROR) << "Could not open config file " << filename; + return -1; + } + std::string line; + int linenum = 0; + while (std::getline(in, line)) { + ++linenum; + if (line.empty() || line[0] == '#') { + continue; + } + size_t i; + size_t size = line.size(); + for (i = 0; i < size && line[i] != '='; ++i) + ; + if (i == size) { + LOG(ERROR) << "Bad configuration format at line " << linenum; + return -1; + } + line[i] = '\0'; + auto s = line.c_str(); + if (parse_config(s, s + i + 1) == -1) { + return -1; + } + } + return 0; +} + +const char *str_syslog_facility(int facility) { + switch (facility) { + case (LOG_AUTH): + return "auth"; + case (LOG_AUTHPRIV): + return "authpriv"; + case (LOG_CRON): + return "cron"; + case (LOG_DAEMON): + return "daemon"; + case (LOG_FTP): + return "ftp"; + case (LOG_KERN): + return "kern"; + case (LOG_LOCAL0): + return "local0"; + case (LOG_LOCAL1): + return "local1"; + case (LOG_LOCAL2): + return "local2"; + case (LOG_LOCAL3): + return "local3"; + case (LOG_LOCAL4): + return "local4"; + case (LOG_LOCAL5): + return "local5"; + case (LOG_LOCAL6): + return "local6"; + case (LOG_LOCAL7): + return "local7"; + case (LOG_LPR): + return "lpr"; + case (LOG_MAIL): + return "mail"; + case (LOG_SYSLOG): + return "syslog"; + case (LOG_USER): + return "user"; + case (LOG_UUCP): + return "uucp"; + default: + return "(unknown)"; + } +} + +int int_syslog_facility(const char *strfacility) { + if (util::strieq(strfacility, "auth")) { + return LOG_AUTH; + } + + if (util::strieq(strfacility, "authpriv")) { + return LOG_AUTHPRIV; + } + + if (util::strieq(strfacility, "cron")) { + return LOG_CRON; + } + + if (util::strieq(strfacility, "daemon")) { + return LOG_DAEMON; + } + + if (util::strieq(strfacility, "ftp")) { + return LOG_FTP; + } + + if (util::strieq(strfacility, "kern")) { + return LOG_KERN; + } + + if (util::strieq(strfacility, "local0")) { + return LOG_LOCAL0; + } + + if (util::strieq(strfacility, "local1")) { + return LOG_LOCAL1; + } + + if (util::strieq(strfacility, "local2")) { + return LOG_LOCAL2; + } + + if (util::strieq(strfacility, "local3")) { + return LOG_LOCAL3; + } + + if (util::strieq(strfacility, "local4")) { + return LOG_LOCAL4; + } + + if (util::strieq(strfacility, "local5")) { + return LOG_LOCAL5; + } + + if (util::strieq(strfacility, "local6")) { + return LOG_LOCAL6; + } + + if (util::strieq(strfacility, "local7")) { + return LOG_LOCAL7; + } + + if (util::strieq(strfacility, "lpr")) { + return LOG_LPR; + } + + if (util::strieq(strfacility, "mail")) { + return LOG_MAIL; + } + + if (util::strieq(strfacility, "news")) { + return LOG_NEWS; + } + + if (util::strieq(strfacility, "syslog")) { + return LOG_SYSLOG; + } + + if (util::strieq(strfacility, "user")) { + return LOG_USER; + } + + if (util::strieq(strfacility, "uucp")) { + return LOG_UUCP; + } + + return -1; +} + +} // namespace shrpx diff --git a/src/shrpx_config.h b/src/shrpx_config.h new file mode 100644 index 0000000..8afccd5 --- /dev/null +++ b/src/shrpx_config.h @@ -0,0 +1,409 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef SHRPX_CONFIG_H +#define SHRPX_CONFIG_H + +#include "shrpx.h" + +#include +#ifdef HAVE_SYS_SOCKET_H +#include +#endif // HAVE_SYS_SOCKET_H +#include +#ifdef HAVE_NETINET_IN_H +#include +#endif // HAVE_NETINET_IN_H +#ifdef HAVE_ARPA_INET_H +#include +#endif // HAVE_ARPA_INET_H +#include +#include +#include +#include + +#include + +#include + +#include + +namespace shrpx { + +struct LogFragment; + +namespace ssl { + +class CertLookupTree; + +} // namespace ssl + +#define SHRPX_UNIX_PATH_PREFIX "unix:" + +extern const char SHRPX_OPT_PRIVATE_KEY_FILE[]; +extern const char SHRPX_OPT_PRIVATE_KEY_PASSWD_FILE[]; +extern const char SHRPX_OPT_CERTIFICATE_FILE[]; +extern const char SHRPX_OPT_DH_PARAM_FILE[]; +extern const char SHRPX_OPT_SUBCERT[]; +extern const char SHRPX_OPT_BACKEND[]; +extern const char SHRPX_OPT_FRONTEND[]; +extern const char SHRPX_OPT_WORKERS[]; +extern const char SHRPX_OPT_HTTP2_MAX_CONCURRENT_STREAMS[]; +extern const char SHRPX_OPT_LOG_LEVEL[]; +extern const char SHRPX_OPT_DAEMON[]; +extern const char SHRPX_OPT_HTTP2_PROXY[]; +extern const char SHRPX_OPT_HTTP2_BRIDGE[]; +extern const char SHRPX_OPT_CLIENT_PROXY[]; +extern const char SHRPX_OPT_ADD_X_FORWARDED_FOR[]; +extern const char SHRPX_OPT_STRIP_INCOMING_X_FORWARDED_FOR[]; +extern const char SHRPX_OPT_NO_VIA[]; +extern const char SHRPX_OPT_FRONTEND_HTTP2_READ_TIMEOUT[]; +extern const char SHRPX_OPT_FRONTEND_READ_TIMEOUT[]; +extern const char SHRPX_OPT_FRONTEND_WRITE_TIMEOUT[]; +extern const char SHRPX_OPT_BACKEND_READ_TIMEOUT[]; +extern const char SHRPX_OPT_BACKEND_WRITE_TIMEOUT[]; +extern const char SHRPX_OPT_STREAM_READ_TIMEOUT[]; +extern const char SHRPX_OPT_STREAM_WRITE_TIMEOUT[]; +extern const char SHRPX_OPT_ACCESSLOG_FILE[]; +extern const char SHRPX_OPT_ACCESSLOG_SYSLOG[]; +extern const char SHRPX_OPT_ACCESSLOG_FORMAT[]; +extern const char SHRPX_OPT_ERRORLOG_FILE[]; +extern const char SHRPX_OPT_ERRORLOG_SYSLOG[]; +extern const char SHRPX_OPT_BACKEND_KEEP_ALIVE_TIMEOUT[]; +extern const char SHRPX_OPT_FRONTEND_HTTP2_WINDOW_BITS[]; +extern const char SHRPX_OPT_BACKEND_HTTP2_WINDOW_BITS[]; +extern const char SHRPX_OPT_FRONTEND_HTTP2_CONNECTION_WINDOW_BITS[]; +extern const char SHRPX_OPT_BACKEND_HTTP2_CONNECTION_WINDOW_BITS[]; +extern const char SHRPX_OPT_FRONTEND_NO_TLS[]; +extern const char SHRPX_OPT_BACKEND_NO_TLS[]; +extern const char SHRPX_OPT_PID_FILE[]; +extern const char SHRPX_OPT_USER[]; +extern const char SHRPX_OPT_SYSLOG_FACILITY[]; +extern const char SHRPX_OPT_BACKLOG[]; +extern const char SHRPX_OPT_CIPHERS[]; +extern const char SHRPX_OPT_CLIENT[]; +extern const char SHRPX_OPT_INSECURE[]; +extern const char SHRPX_OPT_CACERT[]; +extern const char SHRPX_OPT_BACKEND_IPV4[]; +extern const char SHRPX_OPT_BACKEND_IPV6[]; +extern const char SHRPX_OPT_BACKEND_HTTP_PROXY_URI[]; +extern const char SHRPX_OPT_BACKEND_TLS_SNI_FIELD[]; +extern const char SHRPX_OPT_READ_RATE[]; +extern const char SHRPX_OPT_READ_BURST[]; +extern const char SHRPX_OPT_WRITE_RATE[]; +extern const char SHRPX_OPT_WRITE_BURST[]; +extern const char SHRPX_OPT_WORKER_READ_RATE[]; +extern const char SHRPX_OPT_WORKER_READ_BURST[]; +extern const char SHRPX_OPT_WORKER_WRITE_RATE[]; +extern const char SHRPX_OPT_WORKER_WRITE_BURST[]; +extern const char SHRPX_OPT_NPN_LIST[]; +extern const char SHRPX_OPT_TLS_PROTO_LIST[]; +extern const char SHRPX_OPT_VERIFY_CLIENT[]; +extern const char SHRPX_OPT_VERIFY_CLIENT_CACERT[]; +extern const char SHRPX_OPT_CLIENT_PRIVATE_KEY_FILE[]; +extern const char SHRPX_OPT_CLIENT_CERT_FILE[]; +extern const char SHRPX_OPT_FRONTEND_HTTP2_DUMP_REQUEST_HEADER[]; +extern const char SHRPX_OPT_FRONTEND_HTTP2_DUMP_RESPONSE_HEADER[]; +extern const char SHRPX_OPT_HTTP2_NO_COOKIE_CRUMBLING[]; +extern const char SHRPX_OPT_FRONTEND_FRAME_DEBUG[]; +extern const char SHRPX_OPT_PADDING[]; +extern const char SHRPX_OPT_ALTSVC[]; +extern const char SHRPX_OPT_ADD_RESPONSE_HEADER[]; +extern const char SHRPX_OPT_WORKER_FRONTEND_CONNECTIONS[]; +extern const char SHRPX_OPT_NO_LOCATION_REWRITE[]; +extern const char SHRPX_OPT_NO_HOST_REWRITE[]; +extern const char SHRPX_OPT_BACKEND_HTTP1_CONNECTIONS_PER_HOST[]; +extern const char SHRPX_OPT_BACKEND_HTTP1_CONNECTIONS_PER_FRONTEND[]; +extern const char SHRPX_OPT_LISTENER_DISABLE_TIMEOUT[]; +extern const char SHRPX_OPT_TLS_TICKET_KEY_FILE[]; +extern const char SHRPX_OPT_RLIMIT_NOFILE[]; +extern const char SHRPX_OPT_BACKEND_REQUEST_BUFFER[]; +extern const char SHRPX_OPT_BACKEND_RESPONSE_BUFFER[]; +extern const char SHRPX_OPT_NO_SERVER_PUSH[]; +extern const char SHRPX_OPT_BACKEND_HTTP2_CONNECTIONS_PER_WORKER[]; +extern const char SHRPX_OPT_FETCH_OCSP_RESPONSE_FILE[]; +extern const char SHRPX_OPT_OCSP_UPDATE_INTERVAL[]; +extern const char SHRPX_OPT_NO_OCSP[]; +extern const char SHRPX_OPT_HEADER_FIELD_BUFFER[]; +extern const char SHRPX_OPT_MAX_HEADER_FIELDS[]; + +union sockaddr_union { + sockaddr_storage storage; + sockaddr sa; + sockaddr_in6 in6; + sockaddr_in in; + sockaddr_un un; +}; + +enum shrpx_proto { PROTO_HTTP2, PROTO_HTTP }; + +struct AltSvc { + AltSvc() + : protocol_id(nullptr), host(nullptr), origin(nullptr), + protocol_id_len(0), host_len(0), origin_len(0), port(0) {} + + char *protocol_id; + char *host; + char *origin; + + size_t protocol_id_len; + size_t host_len; + size_t origin_len; + + uint16_t port; +}; + +struct DownstreamAddr { + DownstreamAddr() : addr{{0}}, addrlen(0), port(0), host_unix(false) {} + sockaddr_union addr; + // backend address. If |host_unix| is true, this is UNIX domain + // socket path. + std::unique_ptr host; + std::unique_ptr hostport; + size_t addrlen; + // backend port. 0 if |host_unix| is true. + uint16_t port; + // true if |host| contains UNIX domain socket path. + bool host_unix; +}; + +struct TicketKey { + uint8_t name[16]; + uint8_t aes_key[16]; + uint8_t hmac_key[16]; +}; + +struct TicketKeys { + ~TicketKeys(); + std::vector keys; +}; + +struct Config { + // The list of (private key file, certificate file) pair + std::vector> subcerts; + std::vector altsvcs; + std::vector> add_response_headers; + std::vector alpn_prefs; + std::vector accesslog_format; + std::vector downstream_addrs; + std::vector tls_ticket_key_files; + // binary form of http proxy host and port + sockaddr_union downstream_http_proxy_addr; + ev_tstamp http2_upstream_read_timeout; + ev_tstamp upstream_read_timeout; + ev_tstamp upstream_write_timeout; + ev_tstamp downstream_read_timeout; + ev_tstamp downstream_write_timeout; + ev_tstamp stream_read_timeout; + ev_tstamp stream_write_timeout; + ev_tstamp downstream_idle_read_timeout; + ev_tstamp listener_disable_timeout; + ev_tstamp ocsp_update_interval; + // address of frontend connection. This could be a path to UNIX + // domain socket. In this case, |host_unix| must be true. + std::unique_ptr host; + std::unique_ptr private_key_file; + std::unique_ptr private_key_passwd; + std::unique_ptr cert_file; + std::unique_ptr dh_param_file; + const char *server_name; + std::unique_ptr backend_tls_sni_name; + std::unique_ptr pid_file; + std::unique_ptr conf_path; + std::unique_ptr ciphers; + std::unique_ptr cacert; + // userinfo in http proxy URI, not percent-encoded form + std::unique_ptr downstream_http_proxy_userinfo; + // host in http proxy URI + std::unique_ptr downstream_http_proxy_host; + std::unique_ptr http2_upstream_dump_request_header_file; + std::unique_ptr http2_upstream_dump_response_header_file; + // // Rate limit configuration per connection + // ev_token_bucket_cfg *rate_limit_cfg; + // // Rate limit configuration per worker (thread) + // ev_token_bucket_cfg *worker_rate_limit_cfg; + // list of supported NPN/ALPN protocol strings in the order of + // preference. The each element of this list is a NULL-terminated + // string. + std::vector npn_list; + // list of supported SSL/TLS protocol strings. The each element of + // this list is a NULL-terminated string. + std::vector tls_proto_list; + // Path to file containing CA certificate solely used for client + // certificate validation + std::unique_ptr verify_client_cacert; + std::unique_ptr client_private_key_file; + std::unique_ptr client_cert_file; + std::unique_ptr accesslog_file; + std::unique_ptr errorlog_file; + std::unique_ptr fetch_ocsp_response_file; + FILE *http2_upstream_dump_request_header; + FILE *http2_upstream_dump_response_header; + nghttp2_session_callbacks *http2_upstream_callbacks; + nghttp2_session_callbacks *http2_downstream_callbacks; + nghttp2_option *http2_option; + nghttp2_option *http2_client_option; + char **argv; + char *cwd; + size_t num_worker; + size_t http2_max_concurrent_streams; + size_t http2_upstream_window_bits; + size_t http2_downstream_window_bits; + size_t http2_upstream_connection_window_bits; + size_t http2_downstream_connection_window_bits; + size_t http2_downstream_connections_per_worker; + size_t downstream_connections_per_host; + size_t downstream_connections_per_frontend; + // actual size of downstream_http_proxy_addr + size_t downstream_http_proxy_addrlen; + size_t read_rate; + size_t read_burst; + size_t write_rate; + size_t write_burst; + size_t worker_read_rate; + size_t worker_read_burst; + size_t worker_write_rate; + size_t worker_write_burst; + size_t padding; + size_t worker_frontend_connections; + size_t rlimit_nofile; + size_t downstream_request_buffer_size; + size_t downstream_response_buffer_size; + size_t header_field_buffer; + size_t max_header_fields; + // Bit mask to disable SSL/TLS protocol versions. This will be + // passed to SSL_CTX_set_options(). + long int tls_proto_mask; + // downstream protocol; this will be determined by given options. + shrpx_proto downstream_proto; + int syslog_facility; + int backlog; + int argc; + std::unique_ptr user; + uid_t uid; + gid_t gid; + pid_t pid; + // frontend listening port. 0 if frontend listens on UNIX domain + // socket, in this case |host_unix| must be true. + uint16_t port; + // port in http proxy URI + uint16_t downstream_http_proxy_port; + bool verbose; + bool daemon; + bool verify_client; + bool http2_proxy; + bool http2_bridge; + bool client_proxy; + bool add_x_forwarded_for; + bool strip_incoming_x_forwarded_for; + bool no_via; + bool upstream_no_tls; + bool downstream_no_tls; + // Send accesslog to syslog, ignoring accesslog_file. + bool accesslog_syslog; + // Send errorlog to syslog, ignoring errorlog_file. + bool errorlog_syslog; + bool client; + // true if --client or --client-proxy are enabled. + bool client_mode; + bool insecure; + bool backend_ipv4; + bool backend_ipv6; + bool http2_no_cookie_crumbling; + bool upstream_frame_debug; + bool no_location_rewrite; + bool no_host_rewrite; + bool no_server_push; + // true if host contains UNIX domain socket path + bool host_unix; + bool no_ocsp; +}; + +const Config *get_config(); +Config *mod_config(); +void create_config(); + +// Parses option name |opt| and value |optarg|. The results are +// stored into statically allocated Config object. This function +// returns 0 if it succeeds, or -1. +int parse_config(const char *opt, const char *optarg); + +// Loads configurations from |filename| and stores them in statically +// allocated Config object. This function returns 0 if it succeeds, or +// -1. +int load_config(const char *filename); + +// Read passwd from |filename| +std::string read_passwd_from_file(const char *filename); + +// Parses comma delimited strings in |s| and returns the array of +// pointers, each element points to the each substring in |s|. The +// |s| must be comma delimited list of strings. The strings must be +// delimited by a single comma and any white spaces around it are +// treated as a part of protocol strings. This function may modify +// |s| and the caller must leave it as is after this call. This +// function copies |s| and first element in the return value points to +// it. It is caller's responsibility to deallocate its memory. +std::vector parse_config_str_list(const char *s); + +// Clears all elements of |list|, which is returned by +// parse_config_str_list(). If list is not empty, list[0] is freed by +// free(2). After this call, list.empty() must be true. +void clear_config_str_list(std::vector &list); + +// Parses header field in |optarg|. We expect header field is formed +// like "NAME: VALUE". We require that NAME is non empty string. ":" +// is allowed at the start of the NAME, but NAME == ":" is not +// allowed. This function returns pair of NAME and VALUE. +std::pair parse_header(const char *optarg); + +std::vector parse_log_format(const char *optarg); + +// Returns a copy of NULL-terminated string |val|. +std::unique_ptr strcopy(const char *val); + +// Returns a copy of string |val| of length |n|. The returned string +// will be NULL-terminated. +std::unique_ptr strcopy(const char *val, size_t n); + +// Returns a copy of val.c_str(). +std::unique_ptr strcopy(const std::string &val); + +// Returns string for syslog |facility|. +const char *str_syslog_facility(int facility); + +// Returns integer value of syslog |facility| string. +int int_syslog_facility(const char *strfacility); + +FILE *open_file_for_write(const char *filename); + +// Reads TLS ticket key file in |files| and returns TicketKey which +// stores read key data. This function returns TicketKey if it +// succeeds, or nullptr. +std::unique_ptr +read_tls_ticket_key_file(const std::vector &files); + +} // namespace shrpx + +#endif // SHRPX_CONFIG_H diff --git a/src/shrpx_config_test.cc b/src/shrpx_config_test.cc new file mode 100644 index 0000000..ad570bb --- /dev/null +++ b/src/shrpx_config_test.cc @@ -0,0 +1,175 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2014 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "shrpx_config_test.h" + +#ifdef HAVE_UNISTD_H +#include +#endif // HAVE_UNISTD_H + +#include + +#include + +#include "shrpx_config.h" + +namespace shrpx { + +void test_shrpx_config_parse_config_str_list(void) { + auto res = parse_config_str_list("a"); + CU_ASSERT(1 == res.size()); + CU_ASSERT(0 == strcmp("a", res[0])); + clear_config_str_list(res); + + res = parse_config_str_list("a,"); + CU_ASSERT(2 == res.size()); + CU_ASSERT(0 == strcmp("a", res[0])); + CU_ASSERT(0 == strcmp("", res[1])); + clear_config_str_list(res); + + res = parse_config_str_list(",a,,"); + CU_ASSERT(4 == res.size()); + CU_ASSERT(0 == strcmp("", res[0])); + CU_ASSERT(0 == strcmp("a", res[1])); + CU_ASSERT(0 == strcmp("", res[2])); + CU_ASSERT(0 == strcmp("", res[3])); + clear_config_str_list(res); + + res = parse_config_str_list(""); + CU_ASSERT(1 == res.size()); + CU_ASSERT(0 == strcmp("", res[0])); + clear_config_str_list(res); + + res = parse_config_str_list("alpha,bravo,charlie"); + CU_ASSERT(3 == res.size()); + CU_ASSERT(0 == strcmp("alpha", res[0])); + CU_ASSERT(0 == strcmp("bravo", res[1])); + CU_ASSERT(0 == strcmp("charlie", res[2])); + clear_config_str_list(res); +} + +void test_shrpx_config_parse_header(void) { + auto p = parse_header("a: b"); + CU_ASSERT("a" == p.first); + CU_ASSERT("b" == p.second); + + p = parse_header("a: b"); + CU_ASSERT("a" == p.first); + CU_ASSERT("b" == p.second); + + p = parse_header(":a: b"); + CU_ASSERT(":a" == p.first); + CU_ASSERT("b" == p.second); + + p = parse_header("a: :b"); + CU_ASSERT("a" == p.first); + CU_ASSERT(":b" == p.second); + + p = parse_header(": b"); + CU_ASSERT(p.first.empty()); + + p = parse_header("alpha: bravo charlie"); + CU_ASSERT("alpha" == p.first); + CU_ASSERT("bravo charlie" == p.second); +} + +void test_shrpx_config_parse_log_format(void) { + auto res = parse_log_format("$remote_addr - $remote_user [$time_local] " + "\"$request\" $status $body_bytes_sent " + "\"$http_referer\" \"$http_user_agent\""); + CU_ASSERT(14 == res.size()); + + CU_ASSERT(SHRPX_LOGF_REMOTE_ADDR == res[0].type); + + CU_ASSERT(SHRPX_LOGF_LITERAL == res[1].type); + CU_ASSERT(0 == strcmp(" - $remote_user [", res[1].value.get())); + + CU_ASSERT(SHRPX_LOGF_TIME_LOCAL == res[2].type); + + CU_ASSERT(SHRPX_LOGF_LITERAL == res[3].type); + CU_ASSERT(0 == strcmp("] \"", res[3].value.get())); + + CU_ASSERT(SHRPX_LOGF_REQUEST == res[4].type); + + CU_ASSERT(SHRPX_LOGF_LITERAL == res[5].type); + CU_ASSERT(0 == strcmp("\" ", res[5].value.get())); + + CU_ASSERT(SHRPX_LOGF_STATUS == res[6].type); + + CU_ASSERT(SHRPX_LOGF_LITERAL == res[7].type); + CU_ASSERT(0 == strcmp(" ", res[7].value.get())); + + CU_ASSERT(SHRPX_LOGF_BODY_BYTES_SENT == res[8].type); + + CU_ASSERT(SHRPX_LOGF_LITERAL == res[9].type); + CU_ASSERT(0 == strcmp(" \"", res[9].value.get())); + + CU_ASSERT(SHRPX_LOGF_HTTP == res[10].type); + CU_ASSERT(0 == strcmp("referer", res[10].value.get())); + + CU_ASSERT(SHRPX_LOGF_LITERAL == res[11].type); + CU_ASSERT(0 == strcmp("\" \"", res[11].value.get())); + + CU_ASSERT(SHRPX_LOGF_HTTP == res[12].type); + CU_ASSERT(0 == strcmp("user-agent", res[12].value.get())); + + CU_ASSERT(SHRPX_LOGF_LITERAL == res[13].type); + CU_ASSERT(0 == strcmp("\"", res[13].value.get())); +} + +void test_shrpx_config_read_tls_ticket_key_file(void) { + char file1[] = "/tmp/nghttpx-unittest.XXXXXX"; + auto fd1 = mkstemp(file1); + assert(fd1 != -1); + assert(48 == + write(fd1, "0..............12..............34..............5", 48)); + char file2[] = "/tmp/nghttpx-unittest.XXXXXX"; + auto fd2 = mkstemp(file2); + assert(fd2 != -1); + assert(48 == + write(fd2, "6..............78..............9a..............b", 48)); + + close(fd1); + close(fd2); + auto ticket_keys = read_tls_ticket_key_file({file1, file2}); + unlink(file1); + unlink(file2); + CU_ASSERT(ticket_keys.get() != nullptr); + CU_ASSERT(2 == ticket_keys->keys.size()); + auto key = &ticket_keys->keys[0]; + CU_ASSERT(0 == memcmp("0..............1", key->name, sizeof(key->name))); + CU_ASSERT(0 == + memcmp("2..............3", key->aes_key, sizeof(key->aes_key))); + CU_ASSERT(0 == + memcmp("4..............5", key->hmac_key, sizeof(key->hmac_key))); + + key = &ticket_keys->keys[1]; + CU_ASSERT(0 == memcmp("6..............7", key->name, sizeof(key->name))); + CU_ASSERT(0 == + memcmp("8..............9", key->aes_key, sizeof(key->aes_key))); + CU_ASSERT(0 == + memcmp("a..............b", key->hmac_key, sizeof(key->hmac_key))); +} + +} // namespace shrpx diff --git a/src/shrpx_config_test.h b/src/shrpx_config_test.h new file mode 100644 index 0000000..6d6d035 --- /dev/null +++ b/src/shrpx_config_test.h @@ -0,0 +1,41 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2014 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef SHRPX_CONFIG_TEST_H +#define SHRPX_CONFIG_TEST_H + +#ifdef HAVE_CONFIG_H +#include +#endif // HAVE_CONFIG_H + +namespace shrpx { + +void test_shrpx_config_parse_config_str_list(void); +void test_shrpx_config_parse_header(void); +void test_shrpx_config_parse_log_format(void); +void test_shrpx_config_read_tls_ticket_key_file(void); + +} // namespace shrpx + +#endif // SHRPX_CONFIG_TEST_H diff --git a/src/shrpx_connect_blocker.cc b/src/shrpx_connect_blocker.cc new file mode 100644 index 0000000..3eb6de1 --- /dev/null +++ b/src/shrpx_connect_blocker.cc @@ -0,0 +1,65 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2014 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "shrpx_connect_blocker.h" + +namespace shrpx { + +namespace { +const ev_tstamp INITIAL_SLEEP = 2.; +} // namespace + +namespace { +void connect_blocker_cb(struct ev_loop *loop, ev_timer *w, int revents) { + if (LOG_ENABLED(INFO)) { + LOG(INFO) << "unblock downstream connection"; + } +} +} // namespace + +ConnectBlocker::ConnectBlocker(struct ev_loop *loop) + : loop_(loop), sleep_(INITIAL_SLEEP) { + ev_timer_init(&timer_, connect_blocker_cb, 0., 0.); +} + +ConnectBlocker::~ConnectBlocker() { ev_timer_stop(loop_, &timer_); } + +bool ConnectBlocker::blocked() const { return ev_is_active(&timer_); } + +void ConnectBlocker::on_success() { sleep_ = INITIAL_SLEEP; } + +void ConnectBlocker::on_failure() { + if (ev_is_active(&timer_)) { + return; + } + + sleep_ = std::min(128., sleep_ * 2); + + LOG(WARN) << "connect failure, start sleeping " << sleep_; + + ev_timer_set(&timer_, sleep_, 0.); + ev_timer_start(loop_, &timer_); +} + +} // namespace shrpx diff --git a/src/shrpx_connect_blocker.h b/src/shrpx_connect_blocker.h new file mode 100644 index 0000000..af44564 --- /dev/null +++ b/src/shrpx_connect_blocker.h @@ -0,0 +1,56 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2014 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef SHRPX_CONNECT_BLOCKER_H +#define SHRPX_CONNECT_BLOCKER_H + +#include "shrpx.h" + +#include + +namespace shrpx { + +class ConnectBlocker { +public: + ConnectBlocker(struct ev_loop *loop); + ~ConnectBlocker(); + + // Returns true if making connection is not allowed. + bool blocked() const; + // Call this function if connect operation succeeded. This will + // reset sleep_ to minimum value. + void on_success(); + // Call this function if connect operation failed. This will start + // timer and blocks connection establishment for sleep_ seconds. + void on_failure(); + +private: + ev_timer timer_; + struct ev_loop *loop_; + ev_tstamp sleep_; +}; + +} // namespace + +#endif // SHRPX_CONNECT_BLOCKER_H diff --git a/src/shrpx_connection.cc b/src/shrpx_connection.cc new file mode 100644 index 0000000..30c2ec9 --- /dev/null +++ b/src/shrpx_connection.cc @@ -0,0 +1,333 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2015 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "shrpx_connection.h" + +#ifdef HAVE_UNISTD_H +#include +#endif // HAVE_UNISTD_H + +#include + +#include + +#include "memchunk.h" + +using namespace nghttp2; + +namespace shrpx { +Connection::Connection(struct ev_loop *loop, int fd, SSL *ssl, + ev_tstamp write_timeout, ev_tstamp read_timeout, + size_t write_rate, size_t write_burst, size_t read_rate, + size_t read_burst, IOCb writecb, IOCb readcb, + TimerCb timeoutcb, void *data) + : tls{ssl}, wlimit(loop, &wev, write_rate, write_burst), + rlimit(loop, &rev, read_rate, read_burst, ssl), writecb(writecb), + readcb(readcb), timeoutcb(timeoutcb), loop(loop), data(data), fd(fd) { + + ev_io_init(&wev, writecb, fd, EV_WRITE); + ev_io_init(&rev, readcb, fd, EV_READ); + + wev.data = this; + rev.data = this; + + ev_timer_init(&wt, timeoutcb, 0., write_timeout); + ev_timer_init(&rt, timeoutcb, 0., read_timeout); + + wt.data = this; + rt.data = this; + + // set 0. to double field explicitly just in case + tls.last_write_time = 0.; +} + +Connection::~Connection() { disconnect(); } + +void Connection::disconnect() { + ev_timer_stop(loop, &rt); + ev_timer_stop(loop, &wt); + + rlimit.stopw(); + wlimit.stopw(); + + if (tls.ssl) { + SSL_set_app_data(tls.ssl, nullptr); + SSL_set_shutdown(tls.ssl, SSL_RECEIVED_SHUTDOWN); + ERR_clear_error(); + SSL_shutdown(tls.ssl); + SSL_free(tls.ssl); + tls.ssl = nullptr; + } + + if (fd != -1) { + shutdown(fd, SHUT_WR); + close(fd); + fd = -1; + } +} + +int Connection::tls_handshake() { + auto rv = SSL_do_handshake(tls.ssl); + + if (rv == 0) { + return SHRPX_ERR_NETWORK; + } + + if (rv < 0) { + auto err = SSL_get_error(tls.ssl, rv); + switch (err) { + case SSL_ERROR_WANT_READ: + wlimit.stopw(); + ev_timer_stop(loop, &wt); + return SHRPX_ERR_INPROGRESS; + case SSL_ERROR_WANT_WRITE: + wlimit.startw(); + ev_timer_again(loop, &wt); + return SHRPX_ERR_INPROGRESS; + default: + return SHRPX_ERR_NETWORK; + } + } + + wlimit.stopw(); + ev_timer_stop(loop, &wt); + + tls.initial_handshake_done = true; + + if (LOG_ENABLED(INFO)) { + LOG(INFO) << "SSL/TLS handshake completed"; + if (SSL_session_reused(tls.ssl)) { + LOG(INFO) << "SSL/TLS session reused"; + } + } + + return 0; +} + +namespace { +const size_t SHRPX_SMALL_WRITE_LIMIT = 1300; +const size_t SHRPX_WARMUP_THRESHOLD = 1 << 20; +} // namespace + +size_t Connection::get_tls_write_limit() { + auto t = ev_now(loop); + + if (t - tls.last_write_time > 1.) { + // Time out, use small record size + tls.warmup_writelen = 0; + return SHRPX_SMALL_WRITE_LIMIT; + } + + if (tls.warmup_writelen >= SHRPX_WARMUP_THRESHOLD) { + return std::numeric_limits::max(); + } + + return SHRPX_SMALL_WRITE_LIMIT; +} + +void Connection::update_tls_warmup_writelen(size_t n) { + if (tls.warmup_writelen < SHRPX_WARMUP_THRESHOLD) { + tls.warmup_writelen += n; + } +} + +ssize_t Connection::write_tls(const void *data, size_t len) { + // SSL_write requires the same arguments (buf pointer and its + // length) on SSL_ERROR_WANT_READ or SSL_ERROR_WANT_WRITE. + // get_write_limit() may return smaller length than previously + // passed to SSL_write, which violates OpenSSL assumption. To avoid + // this, we keep last legnth passed to SSL_write to + // tls.last_writelen if SSL_write indicated I/O blocking. + if (tls.last_writelen == 0) { + len = std::min(len, wlimit.avail()); + len = std::min(len, get_tls_write_limit()); + if (len == 0) { + return 0; + } + } else { + len = tls.last_writelen; + tls.last_writelen = 0; + } + + auto rv = SSL_write(tls.ssl, data, len); + + if (rv == 0) { + return SHRPX_ERR_NETWORK; + } + + tls.last_write_time = ev_now(loop); + + if (rv < 0) { + auto err = SSL_get_error(tls.ssl, rv); + switch (err) { + case SSL_ERROR_WANT_READ: + if (LOG_ENABLED(INFO)) { + LOG(INFO) << "Close connection due to TLS renegotiation"; + } + return SHRPX_ERR_NETWORK; + case SSL_ERROR_WANT_WRITE: + tls.last_writelen = len; + wlimit.startw(); + ev_timer_again(loop, &wt); + return 0; + default: + if (LOG_ENABLED(INFO)) { + LOG(INFO) << "SSL_write: SSL_get_error returned " << err; + } + return SHRPX_ERR_NETWORK; + } + } + + wlimit.drain(rv); + + update_tls_warmup_writelen(rv); + + return rv; +} + +ssize_t Connection::read_tls(void *data, size_t len) { + // SSL_read requires the same arguments (buf pointer and its + // length) on SSL_ERROR_WANT_READ or SSL_ERROR_WANT_WRITE. + // rlimit_.avail() or rlimit_.avail() may return different length + // than the length previously passed to SSL_read, which violates + // OpenSSL assumption. To avoid this, we keep last legnth passed + // to SSL_read to tls_last_readlen_ if SSL_read indicated I/O + // blocking. + if (tls.last_readlen == 0) { + len = std::min(len, rlimit.avail()); + if (len == 0) { + return 0; + } + } else { + len = tls.last_readlen; + tls.last_readlen = 0; + } + + auto rv = SSL_read(tls.ssl, data, len); + + if (rv <= 0) { + auto err = SSL_get_error(tls.ssl, rv); + switch (err) { + case SSL_ERROR_WANT_READ: + tls.last_readlen = len; + return 0; + case SSL_ERROR_WANT_WRITE: + if (LOG_ENABLED(INFO)) { + LOG(INFO) << "Close connection due to TLS renegotiation"; + } + return SHRPX_ERR_NETWORK; + case SSL_ERROR_ZERO_RETURN: + return SHRPX_ERR_EOF; + default: + if (LOG_ENABLED(INFO)) { + LOG(INFO) << "SSL_read: SSL_get_error returned " << err; + } + return SHRPX_ERR_NETWORK; + } + } + + rlimit.drain(rv); + + return rv; +} + +ssize_t Connection::write_clear(const void *data, size_t len) { + len = std::min(len, wlimit.avail()); + if (len == 0) { + return 0; + } + + ssize_t nwrite; + while ((nwrite = write(fd, data, len)) == -1 && errno == EINTR) + ; + if (nwrite == -1) { + if (errno == EAGAIN || errno == EWOULDBLOCK) { + wlimit.startw(); + ev_timer_again(loop, &wt); + return 0; + } + return SHRPX_ERR_NETWORK; + } + + wlimit.drain(nwrite); + + return nwrite; +} + +ssize_t Connection::writev_clear(struct iovec *iov, int iovcnt) { + iovcnt = limit_iovec(iov, iovcnt, wlimit.avail()); + if (iovcnt == 0) { + return 0; + } + + ssize_t nwrite; + while ((nwrite = writev(fd, iov, iovcnt)) == -1 && errno == EINTR) + ; + if (nwrite == -1) { + if (errno == EAGAIN || errno == EWOULDBLOCK) { + wlimit.startw(); + ev_timer_again(loop, &wt); + return 0; + } + return SHRPX_ERR_NETWORK; + } + + wlimit.drain(nwrite); + + return nwrite; +} + +ssize_t Connection::read_clear(void *data, size_t len) { + len = std::min(len, rlimit.avail()); + if (len == 0) { + return 0; + } + + ssize_t nread; + while ((nread = read(fd, data, len)) == -1 && errno == EINTR) + ; + if (nread == -1) { + if (errno == EAGAIN || errno == EWOULDBLOCK) { + return 0; + } + return SHRPX_ERR_NETWORK; + } + + if (nread == 0) { + return SHRPX_ERR_EOF; + } + + rlimit.drain(nread); + + return nread; +} + +void Connection::handle_tls_pending_read() { + if (!ev_is_active(&rev)) { + return; + } + rlimit.handle_tls_pending_read(); +} + +} // namespace shrpx diff --git a/src/shrpx_connection.h b/src/shrpx_connection.h new file mode 100644 index 0000000..055d32c --- /dev/null +++ b/src/shrpx_connection.h @@ -0,0 +1,109 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2015 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef SHRPX_CONNECTION_H +#define SHRPX_CONNECTION_H + +#include "shrpx_config.h" + +#include + +#include + +#include + +#include "shrpx_rate_limit.h" +#include "shrpx_error.h" + +namespace shrpx { + +struct TLSConnection { + SSL *ssl; + ev_tstamp last_write_time; + size_t warmup_writelen; + // length passed to SSL_write and SSL_read last time. This is + // required since these functions require the exact same parameters + // on non-blocking I/O. + size_t last_writelen, last_readlen; + bool initial_handshake_done; + bool reneg_started; +}; + +template using EVCb = void (*)(struct ev_loop *, T *, int); + +using IOCb = EVCb; +using TimerCb = EVCb; + +struct Connection { + Connection(struct ev_loop *loop, int fd, SSL *ssl, ev_tstamp write_timeout, + ev_tstamp read_timeout, size_t write_rate, size_t write_burst, + size_t read_rate, size_t read_burst, IOCb writecb, IOCb readcb, + TimerCb timeoutcb, void *data); + ~Connection(); + + void disconnect(); + + int tls_handshake(); + + // All write_* and writev_clear functions return number of bytes + // written. If nothing cannot be written (e.g., there is no + // allowance in RateLimit or underlying connection blocks), return + // 0. SHRPX_ERR_NETWORK is returned in case of error. + // + // All read_* functions return number of bytes read. If nothing + // cannot be read (e.g., there is no allowance in Ratelimit or + // underlying connection blocks), return 0. SHRPX_ERR_EOF is + // returned in case of EOF and no data was read. Otherwise + // SHRPX_ERR_NETWORK is return in case of error. + ssize_t write_tls(const void *data, size_t len); + ssize_t read_tls(void *data, size_t len); + + size_t get_tls_write_limit(); + // Updates the number of bytes written in warm up period. + void update_tls_warmup_writelen(size_t n); + + ssize_t write_clear(const void *data, size_t len); + ssize_t writev_clear(struct iovec *iov, int iovcnt); + ssize_t read_clear(void *data, size_t len); + + void handle_tls_pending_read(); + + TLSConnection tls; + ev_io wev; + ev_io rev; + ev_timer wt; + ev_timer rt; + RateLimit wlimit; + RateLimit rlimit; + IOCb writecb; + IOCb readcb; + TimerCb timeoutcb; + struct ev_loop *loop; + void *data; + int fd; +}; + +} // namespace shrpx + +#endif // SHRPX_CONNECTION_H diff --git a/src/shrpx_connection_handler.cc b/src/shrpx_connection_handler.cc new file mode 100644 index 0000000..ceebf61 --- /dev/null +++ b/src/shrpx_connection_handler.cc @@ -0,0 +1,539 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "shrpx_connection_handler.h" + +#ifdef HAVE_UNISTD_H +#include +#endif // HAVE_UNISTD_H +#include +#include + +#include +#include + +#include "shrpx_client_handler.h" +#include "shrpx_ssl.h" +#include "shrpx_worker.h" +#include "shrpx_config.h" +#include "shrpx_http2_session.h" +#include "shrpx_connect_blocker.h" +#include "shrpx_downstream_connection.h" +#include "shrpx_accept_handler.h" +#include "util.h" +#include "template.h" + +using namespace nghttp2; + +namespace shrpx { + +namespace { +void acceptor_disable_cb(struct ev_loop *loop, ev_timer *w, int revent) { + auto h = static_cast(w->data); + + // If we are in graceful shutdown period, we must not enable + // acceptors again. + if (h->get_graceful_shutdown()) { + return; + } + + h->enable_acceptor(); +} +} // namespace + +namespace { +void ocsp_cb(struct ev_loop *loop, ev_timer *w, int revent) { + auto h = static_cast(w->data); + + // If we are in graceful shutdown period, we won't do ocsp query. + if (h->get_graceful_shutdown()) { + return; + } + + LOG(NOTICE) << "Start ocsp update"; + + h->proceed_next_cert_ocsp(); +} +} // namespace + +namespace { +void ocsp_read_cb(struct ev_loop *loop, ev_io *w, int revent) { + auto h = static_cast(w->data); + + h->read_ocsp_chunk(); +} +} // namespace + +namespace { +void ocsp_chld_cb(struct ev_loop *loop, ev_child *w, int revent) { + auto h = static_cast(w->data); + + h->handle_ocsp_complete(); +} +} // namespace + +ConnectionHandler::ConnectionHandler(struct ev_loop *loop) + : single_worker_(nullptr), loop_(loop), worker_round_robin_cnt_(0), + graceful_shutdown_(false) { + ev_timer_init(&disable_acceptor_timer_, acceptor_disable_cb, 0., 0.); + disable_acceptor_timer_.data = this; + + ev_timer_init(&ocsp_timer_, ocsp_cb, 0., 0.); + ocsp_timer_.data = this; + + ev_io_init(&ocsp_.rev, ocsp_read_cb, -1, EV_READ); + ocsp_.rev.data = this; + + ev_child_init(&ocsp_.chldev, ocsp_chld_cb, 0, 0); + ocsp_.chldev.data = this; + + ocsp_.next = 0; + ocsp_.fd = -1; + + reset_ocsp(); +} + +ConnectionHandler::~ConnectionHandler() { + ev_timer_stop(loop_, &disable_acceptor_timer_); + ev_timer_stop(loop_, &ocsp_timer_); +} + +void ConnectionHandler::worker_reopen_log_files() { + WorkerEvent wev; + + memset(&wev, 0, sizeof(wev)); + wev.type = REOPEN_LOG; + + for (auto &worker : workers_) { + worker->send(wev); + } +} + +void ConnectionHandler::worker_renew_ticket_keys( + const std::shared_ptr &ticket_keys) { + WorkerEvent wev; + + memset(&wev, 0, sizeof(wev)); + wev.type = RENEW_TICKET_KEYS; + wev.ticket_keys = ticket_keys; + + for (auto &worker : workers_) { + worker->send(wev); + } +} + +void ConnectionHandler::create_single_worker() { + auto cert_tree = ssl::create_cert_lookup_tree(); + auto sv_ssl_ctx = ssl::setup_server_ssl_context(all_ssl_ctx_, cert_tree); + auto cl_ssl_ctx = ssl::setup_client_ssl_context(); + + single_worker_ = make_unique(loop_, sv_ssl_ctx, cl_ssl_ctx, cert_tree, + ticket_keys_); +} + +void ConnectionHandler::create_worker_thread(size_t num) { +#ifndef NOTHREADS + assert(workers_.size() == 0); + + auto cert_tree = ssl::create_cert_lookup_tree(); + auto sv_ssl_ctx = ssl::setup_server_ssl_context(all_ssl_ctx_, cert_tree); + auto cl_ssl_ctx = ssl::setup_client_ssl_context(); + + for (size_t i = 0; i < num; ++i) { + auto loop = ev_loop_new(0); + + auto worker = make_unique(loop, sv_ssl_ctx, cl_ssl_ctx, cert_tree, + ticket_keys_); + worker->run_async(); + workers_.push_back(std::move(worker)); + + if (LOG_ENABLED(INFO)) { + LLOG(INFO, this) << "Created thread #" << workers_.size() - 1; + } + } +#endif // NOTHREADS +} + +void ConnectionHandler::join_worker() { +#ifndef NOTHREADS + int n = 0; + + if (LOG_ENABLED(INFO)) { + LLOG(INFO, this) << "Waiting for worker thread to join: n=" + << workers_.size(); + } + + for (auto &worker : workers_) { + worker->wait(); + if (LOG_ENABLED(INFO)) { + LLOG(INFO, this) << "Thread #" << n << " joined"; + } + ++n; + } +#endif // NOTHREADS +} + +void ConnectionHandler::graceful_shutdown_worker() { + if (get_config()->num_worker == 1) { + return; + } + + WorkerEvent wev; + memset(&wev, 0, sizeof(wev)); + wev.type = GRACEFUL_SHUTDOWN; + + if (LOG_ENABLED(INFO)) { + LLOG(INFO, this) << "Sending graceful shutdown signal to worker"; + } + + for (auto &worker : workers_) { + + worker->send(wev); + } +} + +int ConnectionHandler::handle_connection(int fd, sockaddr *addr, int addrlen) { + if (LOG_ENABLED(INFO)) { + LLOG(INFO, this) << "Accepted connection. fd=" << fd; + } + + if (get_config()->num_worker == 1) { + + if (single_worker_->get_worker_stat()->num_connections >= + get_config()->worker_frontend_connections) { + + if (LOG_ENABLED(INFO)) { + LLOG(INFO, this) << "Too many connections >=" + << get_config()->worker_frontend_connections; + } + + close(fd); + return -1; + } + + auto client = + ssl::accept_connection(single_worker_.get(), fd, addr, addrlen); + if (!client) { + LLOG(ERROR, this) << "ClientHandler creation failed"; + + close(fd); + return -1; + } + + return 0; + } + + size_t idx = worker_round_robin_cnt_ % workers_.size(); + if (LOG_ENABLED(INFO)) { + LOG(INFO) << "Dispatch connection to worker #" << idx; + } + ++worker_round_robin_cnt_; + WorkerEvent wev; + memset(&wev, 0, sizeof(wev)); + wev.type = NEW_CONNECTION; + wev.client_fd = fd; + memcpy(&wev.client_addr, addr, addrlen); + wev.client_addrlen = addrlen; + + workers_[idx]->send(wev); + + return 0; +} + +struct ev_loop *ConnectionHandler::get_loop() const { + return loop_; +} + +Worker *ConnectionHandler::get_single_worker() const { + return single_worker_.get(); +} + +void ConnectionHandler::set_acceptor(std::unique_ptr h) { + acceptor_ = std::move(h); +} + +AcceptHandler *ConnectionHandler::get_acceptor() const { + return acceptor_.get(); +} + +void ConnectionHandler::set_acceptor6(std::unique_ptr h) { + acceptor6_ = std::move(h); +} + +AcceptHandler *ConnectionHandler::get_acceptor6() const { + return acceptor6_.get(); +} + +void ConnectionHandler::enable_acceptor() { + if (acceptor_) { + acceptor_->enable(); + } + + if (acceptor6_) { + acceptor6_->enable(); + } +} + +void ConnectionHandler::disable_acceptor() { + if (acceptor_) { + acceptor_->disable(); + } + + if (acceptor6_) { + acceptor6_->disable(); + } +} + +void ConnectionHandler::disable_acceptor_temporary(ev_tstamp t) { + if (t == 0. || ev_is_active(&disable_acceptor_timer_)) { + return; + } + + disable_acceptor(); + + ev_timer_set(&disable_acceptor_timer_, t, 0.); + ev_timer_start(loop_, &disable_acceptor_timer_); +} + +void ConnectionHandler::accept_pending_connection() { + if (acceptor_) { + acceptor_->accept_connection(); + } + if (acceptor6_) { + acceptor6_->accept_connection(); + } +} + +void +ConnectionHandler::set_ticket_keys(std::shared_ptr ticket_keys) { + ticket_keys_ = std::move(ticket_keys); + if (single_worker_) { + single_worker_->set_ticket_keys(ticket_keys_); + } +} + +const std::shared_ptr &ConnectionHandler::get_ticket_keys() const { + return ticket_keys_; +} + +void ConnectionHandler::set_graceful_shutdown(bool f) { + graceful_shutdown_ = f; + if (single_worker_) { + single_worker_->set_graceful_shutdown(f); + } +} + +bool ConnectionHandler::get_graceful_shutdown() const { + return graceful_shutdown_; +} + +void ConnectionHandler::cancel_ocsp_update() { + if (ocsp_.pid == 0) { + return; + } + + kill(ocsp_.pid, SIGTERM); +} + +// inspired by h2o_read_command function from h2o project: +// https://github.com/h2o/h2o +int ConnectionHandler::start_ocsp_update(const char *cert_file) { + int rv; + int pfd[2]; + + if (LOG_ENABLED(INFO)) { + LOG(INFO) << "Start ocsp update for " << cert_file; + } + + assert(!ev_is_active(&ocsp_.rev)); + assert(!ev_is_active(&ocsp_.chldev)); + + char *const argv[] = { + const_cast(get_config()->fetch_ocsp_response_file.get()), + const_cast(cert_file), nullptr}; + char *const envp[] = {nullptr}; + +#ifdef O_CLOEXEC + if (pipe2(pfd, O_CLOEXEC) == -1) { + return -1; + } +#else // !O_CLOEXEC + if (pipe(pfd) == -1) { + return -1; + } + util::make_socket_closeonexec(pfd[0]); + util::make_socket_closeonexec(pfd[1]); +#endif // !O_CLOEXEC + + auto closer = defer([&pfd]() { + if (pfd[0] != -1) { + close(pfd[0]); + } + + if (pfd[1] != -1) { + close(pfd[1]); + } + }); + + auto pid = fork(); + if (pid == -1) { + auto error = errno; + LOG(WARN) << "Could not execute ocsp query command for " << cert_file + << ": " << argv[0] << ", fork() failed, errno=" << error; + return -1; + } + + if (pid == 0) { + // child process + dup2(pfd[1], 1); + close(pfd[0]); + + rv = execve(argv[0], argv, envp); + if (rv == -1) { + auto error = errno; + LOG(WARN) << "Could not execute ocsp query command: " << argv[0] + << ", execve() faild, errno=" << error; + _Exit(EXIT_FAILURE); + } + // unreachable + } + + // parent process + close(pfd[1]); + pfd[1] = -1; + + ocsp_.pid = pid; + ocsp_.fd = pfd[0]; + pfd[0] = -1; + + util::make_socket_nonblocking(ocsp_.fd); + ev_io_set(&ocsp_.rev, ocsp_.fd, EV_READ); + ev_io_start(loop_, &ocsp_.rev); + + ev_child_set(&ocsp_.chldev, ocsp_.pid, 0); + ev_child_start(loop_, &ocsp_.chldev); + + return 0; +} + +void ConnectionHandler::read_ocsp_chunk() { + std::array buf; + for (;;) { + ssize_t n; + while ((n = read(ocsp_.fd, buf.data(), buf.size())) == -1 && errno == EINTR) + ; + + if (n == -1) { + if (errno == EAGAIN || errno == EWOULDBLOCK) { + return; + } + auto error = errno; + LOG(WARN) << "Reading from ocsp query command failed: errno=" << error; + ocsp_.error = error; + + break; + } + + if (n == 0) { + break; + } + + std::copy_n(std::begin(buf), n, std::back_inserter(ocsp_.resp)); + } + + ev_io_stop(loop_, &ocsp_.rev); +} + +void ConnectionHandler::handle_ocsp_complete() { + ev_io_stop(loop_, &ocsp_.rev); + ev_child_stop(loop_, &ocsp_.chldev); + + assert(ocsp_.next < all_ssl_ctx_.size()); + + auto ssl_ctx = all_ssl_ctx_[ocsp_.next]; + auto tls_ctx_data = + static_cast(SSL_CTX_get_app_data(ssl_ctx)); + + auto rstatus = ocsp_.chldev.rstatus; + auto status = WEXITSTATUS(rstatus); + if (ocsp_.error || !WIFEXITED(rstatus) || status != 0) { + LOG(WARN) << "ocsp query command for " << tls_ctx_data->cert_file + << " failed: error=" << ocsp_.error << ", rstatus=" << rstatus + << ", status=" << status; + ++ocsp_.next; + proceed_next_cert_ocsp(); + return; + } + + if (LOG_ENABLED(INFO)) { + LOG(INFO) << "ocsp update for " << tls_ctx_data->cert_file + << " finished successfully"; + } + + { + std::lock_guard g(tls_ctx_data->mu); + tls_ctx_data->ocsp_data = std::move(ocsp_.resp); + } + + ++ocsp_.next; + proceed_next_cert_ocsp(); +} + +void ConnectionHandler::reset_ocsp() { + if (ocsp_.fd != -1) { + close(ocsp_.fd); + } + + ocsp_.fd = -1; + ocsp_.pid = 0; + ocsp_.error = 0; + ocsp_.resp = std::vector(); +} + +void ConnectionHandler::proceed_next_cert_ocsp() { + for (;;) { + reset_ocsp(); + if (ocsp_.next == all_ssl_ctx_.size()) { + ocsp_.next = 0; + // We have updated all ocsp response, and schedule next update. + ev_timer_set(&ocsp_timer_, get_config()->ocsp_update_interval, 0.); + ev_timer_start(loop_, &ocsp_timer_); + return; + } + + auto ssl_ctx = all_ssl_ctx_[ocsp_.next]; + auto tls_ctx_data = + static_cast(SSL_CTX_get_app_data(ssl_ctx)); + auto cert_file = tls_ctx_data->cert_file; + + if (start_ocsp_update(cert_file) != 0) { + ++ocsp_.next; + continue; + } + + break; + } +} + +} // namespace shrpx diff --git a/src/shrpx_connection_handler.h b/src/shrpx_connection_handler.h new file mode 100644 index 0000000..00e4be4 --- /dev/null +++ b/src/shrpx_connection_handler.h @@ -0,0 +1,138 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef SHRPX_CONNECTION_HANDLER_H +#define SHRPX_CONNECTION_HANDLER_H + +#include "shrpx.h" + +#include +#ifdef HAVE_SYS_SOCKET_H +#include +#endif // HAVE_SYS_SOCKET_H + +#include +#include + +#include + +#include + +#include "shrpx_downstream_connection_pool.h" + +namespace shrpx { + +class Http2Session; +class ConnectBlocker; +class AcceptHandler; +class Worker; +struct WorkerStat; +struct TicketKeys; + +struct OCSPUpdateContext { + // ocsp response buffer + std::vector resp; + // index to ConnectionHandler::all_ssl_ctx_, which points to next + // SSL_CTX to update ocsp response cache. + size_t next; + ev_child chldev; + ev_io rev; + // fd to read response from fetch-ocsp-response script + int fd; + // errno encountered while processing response + int error; + // pid of forked fetch-ocsp-response script process + pid_t pid; +}; + +class ConnectionHandler { +public: + ConnectionHandler(struct ev_loop *loop); + ~ConnectionHandler(); + int handle_connection(int fd, sockaddr *addr, int addrlen); + // Creates Worker object for single threaded configuration. + void create_single_worker(); + // Creates |num| Worker objects for multi threaded configuration. + // The |num| must be strictly more than 1. + void create_worker_thread(size_t num); + void worker_reopen_log_files(); + void worker_renew_ticket_keys(const std::shared_ptr &ticket_keys); + void set_ticket_keys(std::shared_ptr ticket_keys); + const std::shared_ptr &get_ticket_keys() const; + struct ev_loop *get_loop() const; + Worker *get_single_worker() const; + void set_acceptor(std::unique_ptr h); + AcceptHandler *get_acceptor() const; + void set_acceptor6(std::unique_ptr h); + AcceptHandler *get_acceptor6() const; + void enable_acceptor(); + void disable_acceptor(); + void disable_acceptor_temporary(ev_tstamp t); + void accept_pending_connection(); + void graceful_shutdown_worker(); + void set_graceful_shutdown(bool f); + bool get_graceful_shutdown() const; + void join_worker(); + + // Cancels ocsp update process + void cancel_ocsp_update(); + // Starts ocsp update for certficate |cert_file|. + int start_ocsp_update(const char *cert_file); + // Reads incoming data from ocsp update process + void read_ocsp_chunk(); + // Handles the completion of one ocsp update + void handle_ocsp_complete(); + // Resets ocsp_; + void reset_ocsp(); + // Proceeds to the next certificate's ocsp update. If all + // certificates' ocsp update has been done, schedule next ocsp + // update. + void proceed_next_cert_ocsp(); + +private: + std::vector all_ssl_ctx_; + OCSPUpdateContext ocsp_; + // Worker instances when multi threaded mode (-nN, N >= 2) is used. + std::vector> workers_; + // Worker instance used when single threaded mode (-n1) is used. + // Otherwise, nullptr and workers_ has instances of Worker instead. + std::unique_ptr single_worker_; + // Current TLS session ticket keys. Note that TLS connection does + // not refer to this field directly. They use TicketKeys object in + // Worker object. + std::shared_ptr ticket_keys_; + struct ev_loop *loop_; + // acceptor for IPv4 address or UNIX domain socket. + std::unique_ptr acceptor_; + // acceptor for IPv6 address + std::unique_ptr acceptor6_; + ev_timer disable_acceptor_timer_; + ev_timer ocsp_timer_; + unsigned int worker_round_robin_cnt_; + bool graceful_shutdown_; +}; + +} // namespace shrpx + +#endif // SHRPX_CONNECTION_HANDLER_H diff --git a/src/shrpx_downstream.cc b/src/shrpx_downstream.cc new file mode 100644 index 0000000..6042878 --- /dev/null +++ b/src/shrpx_downstream.cc @@ -0,0 +1,1218 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "shrpx_downstream.h" + +#include + +#include "http-parser/http_parser.h" + +#include "shrpx_upstream.h" +#include "shrpx_client_handler.h" +#include "shrpx_config.h" +#include "shrpx_error.h" +#include "shrpx_downstream_connection.h" +#include "shrpx_downstream_queue.h" +#include "util.h" +#include "http2.h" + +namespace shrpx { + +namespace { +void upstream_timeoutcb(struct ev_loop *loop, ev_timer *w, int revents) { + auto downstream = static_cast(w->data); + auto upstream = downstream->get_upstream(); + + auto which = revents == EV_READ ? "read" : "write"; + + if (LOG_ENABLED(INFO)) { + DLOG(INFO, downstream) << "upstream timeout stream_id=" + << downstream->get_stream_id() << " event=" << which; + } + + downstream->disable_upstream_rtimer(); + downstream->disable_upstream_wtimer(); + + upstream->on_timeout(downstream); +} +} // namespace + +namespace { +void upstream_rtimeoutcb(struct ev_loop *loop, ev_timer *w, int revents) { + upstream_timeoutcb(loop, w, EV_READ); +} +} // namespace + +namespace { +void upstream_wtimeoutcb(struct ev_loop *loop, ev_timer *w, int revents) { + upstream_timeoutcb(loop, w, EV_WRITE); +} +} // namespace + +namespace { +void downstream_timeoutcb(struct ev_loop *loop, ev_timer *w, int revents) { + auto downstream = static_cast(w->data); + + auto which = revents == EV_READ ? "read" : "write"; + + if (LOG_ENABLED(INFO)) { + DLOG(INFO, downstream) << "downstream timeout stream_id=" + << downstream->get_downstream_stream_id() + << " event=" << which; + } + + downstream->disable_downstream_rtimer(); + downstream->disable_downstream_wtimer(); + + auto dconn = downstream->get_downstream_connection(); + + if (dconn) { + dconn->on_timeout(); + } +} +} // namespace + +namespace { +void downstream_rtimeoutcb(struct ev_loop *loop, ev_timer *w, int revents) { + downstream_timeoutcb(loop, w, EV_READ); +} +} // namespace + +namespace { +void downstream_wtimeoutcb(struct ev_loop *loop, ev_timer *w, int revents) { + downstream_timeoutcb(loop, w, EV_WRITE); +} +} // namespace + +// upstream could be nullptr for unittests +Downstream::Downstream(Upstream *upstream, MemchunkPool *mcpool, + int32_t stream_id, int32_t priority) + : dlnext(nullptr), dlprev(nullptr), + request_start_time_(std::chrono::high_resolution_clock::now()), + request_buf_(mcpool), response_buf_(mcpool), request_bodylen_(0), + response_bodylen_(0), response_sent_bodylen_(0), + request_content_length_(-1), response_content_length_(-1), + upstream_(upstream), blocked_link_(nullptr), request_headers_sum_(0), + response_headers_sum_(0), request_datalen_(0), response_datalen_(0), + num_retry_(0), stream_id_(stream_id), priority_(priority), + downstream_stream_id_(-1), + response_rst_stream_error_code_(NGHTTP2_NO_ERROR), + request_state_(INITIAL), request_major_(1), request_minor_(1), + response_state_(INITIAL), response_http_status_(0), response_major_(1), + response_minor_(1), dispatch_state_(DISPATCH_NONE), + upgrade_request_(false), upgraded_(false), http2_upgrade_seen_(false), + chunked_request_(false), request_connection_close_(false), + request_header_key_prev_(false), request_trailer_key_prev_(false), + request_http2_expect_body_(false), chunked_response_(false), + response_connection_close_(false), response_header_key_prev_(false), + response_trailer_key_prev_(false), expect_final_response_(false), + request_pending_(false) { + + ev_timer_init(&upstream_rtimer_, &upstream_rtimeoutcb, 0., + get_config()->stream_read_timeout); + ev_timer_init(&upstream_wtimer_, &upstream_wtimeoutcb, 0., + get_config()->stream_write_timeout); + ev_timer_init(&downstream_rtimer_, &downstream_rtimeoutcb, 0., + get_config()->stream_read_timeout); + ev_timer_init(&downstream_wtimer_, &downstream_wtimeoutcb, 0., + get_config()->stream_write_timeout); + + upstream_rtimer_.data = this; + upstream_wtimer_.data = this; + downstream_rtimer_.data = this; + downstream_wtimer_.data = this; + + http2::init_hdidx(request_hdidx_); + http2::init_hdidx(response_hdidx_); +} + +Downstream::~Downstream() { + if (LOG_ENABLED(INFO)) { + DLOG(INFO, this) << "Deleting"; + } + + if (blocked_link_) { + detach_blocked_link(blocked_link_); + } + + // check nullptr for unittest + if (upstream_) { + auto loop = upstream_->get_client_handler()->get_loop(); + + ev_timer_stop(loop, &upstream_rtimer_); + ev_timer_stop(loop, &upstream_wtimer_); + ev_timer_stop(loop, &downstream_rtimer_); + ev_timer_stop(loop, &downstream_wtimer_); + } + + if (LOG_ENABLED(INFO)) { + DLOG(INFO, this) << "Deleted"; + } +} + +int Downstream::attach_downstream_connection( + std::unique_ptr dconn) { + if (dconn->attach_downstream(this) != 0) { + return -1; + } + + dconn_ = std::move(dconn); + + return 0; +} + +void Downstream::detach_downstream_connection() { + if (!dconn_) { + return; + } + + dconn_->detach_downstream(this); + + auto handler = dconn_->get_client_handler(); + + handler->pool_downstream_connection( + std::unique_ptr(dconn_.release())); +} + +void Downstream::release_downstream_connection() { dconn_.release(); } + +DownstreamConnection *Downstream::get_downstream_connection() { + return dconn_.get(); +} + +std::unique_ptr Downstream::pop_downstream_connection() { + return std::unique_ptr(dconn_.release()); +} + +void Downstream::pause_read(IOCtrlReason reason) { + if (dconn_) { + dconn_->pause_read(reason); + } +} + +int Downstream::resume_read(IOCtrlReason reason, size_t consumed) { + if (dconn_) { + return dconn_->resume_read(reason, consumed); + } + + return 0; +} + +void Downstream::force_resume_read() { + if (dconn_) { + dconn_->force_resume_read(); + } +} + +namespace { +const Headers::value_type *get_header_linear(const Headers &headers, + const std::string &name) { + const Headers::value_type *res = nullptr; + for (auto &kv : headers) { + if (kv.name == name) { + res = &kv; + } + } + return res; +} +} // namespace + +const Headers &Downstream::get_request_headers() const { + return request_headers_; +} + +void Downstream::assemble_request_cookie() { + std::string &cookie = assembled_request_cookie_; + cookie = ""; + for (auto &kv : request_headers_) { + if (kv.name.size() != 6 || kv.name[5] != 'e' || + !util::streq_l("cooki", kv.name.c_str(), 5)) { + continue; + } + + auto end = kv.value.find_last_not_of(" ;"); + if (end == std::string::npos) { + cookie += kv.value; + } else { + cookie.append(std::begin(kv.value), std::begin(kv.value) + end + 1); + } + cookie += "; "; + } + if (cookie.size() >= 2) { + cookie.erase(cookie.size() - 2); + } +} + +Headers Downstream::crumble_request_cookie() { + Headers cookie_hdrs; + for (auto &kv : request_headers_) { + if (kv.name.size() != 6 || kv.name[5] != 'e' || + !util::streq_l("cooki", kv.name.c_str(), 5)) { + continue; + } + size_t last = kv.value.size(); + + for (size_t j = 0; j < last;) { + j = kv.value.find_first_not_of("\t ;", j); + if (j == std::string::npos) { + break; + } + auto first = j; + + j = kv.value.find(';', j); + if (j == std::string::npos) { + j = last; + } + + cookie_hdrs.push_back( + Header("cookie", kv.value.substr(first, j - first), kv.no_index)); + } + } + return cookie_hdrs; +} + +const std::string &Downstream::get_assembled_request_cookie() const { + return assembled_request_cookie_; +} + +namespace { +void add_header(bool &key_prev, size_t &sum, Headers &headers, std::string name, + std::string value) { + key_prev = true; + sum += name.size() + value.size(); + headers.emplace_back(std::move(name), std::move(value)); +} +} // namespace + +namespace { +void append_last_header_key(bool key_prev, size_t &sum, Headers &headers, + const char *data, size_t len) { + assert(key_prev); + sum += len; + auto &item = headers.back(); + item.name.append(data, len); +} +} // namespace + +namespace { +void append_last_header_value(bool key_prev, size_t &sum, Headers &headers, + const char *data, size_t len) { + assert(!key_prev); + sum += len; + auto &item = headers.back(); + item.value.append(data, len); +} +} // namespace + +namespace { +void set_last_header_value(bool &key_prev, size_t &sum, Headers &headers, + const char *data, size_t len) { + key_prev = false; + sum += len; + auto &item = headers.back(); + item.value.assign(data, len); +} +} // namespace + +namespace { +int index_headers(http2::HeaderIndex &hdidx, Headers &headers, + int64_t &content_length) { + for (size_t i = 0; i < headers.size(); ++i) { + auto &kv = headers[i]; + util::inp_strlower(kv.name); + + auto token = http2::lookup_token( + reinterpret_cast(kv.name.c_str()), kv.name.size()); + if (token < 0) { + continue; + } + + kv.token = token; + http2::index_header(hdidx, token, i); + + if (token == http2::HD_CONTENT_LENGTH) { + auto len = util::parse_uint(kv.value); + if (len == -1) { + return -1; + } + if (content_length != -1) { + return -1; + } + content_length = len; + } + } + return 0; +} +} // namespace + +int Downstream::index_request_headers() { + return index_headers(request_hdidx_, request_headers_, + request_content_length_); +} + +const Headers::value_type *Downstream::get_request_header(int16_t token) const { + return http2::get_header(request_hdidx_, token, request_headers_); +} + +const Headers::value_type * +Downstream::get_request_header(const std::string &name) const { + return get_header_linear(request_headers_, name); +} + +void Downstream::add_request_header(std::string name, std::string value) { + add_header(request_header_key_prev_, request_headers_sum_, request_headers_, + std::move(name), std::move(value)); +} + +void Downstream::set_last_request_header_value(const char *data, size_t len) { + set_last_header_value(request_header_key_prev_, request_headers_sum_, + request_headers_, data, len); +} + +void Downstream::add_request_header(std::string name, std::string value, + int16_t token) { + http2::index_header(request_hdidx_, token, request_headers_.size()); + request_headers_sum_ += name.size() + value.size(); + request_headers_.emplace_back(std::move(name), std::move(value), false, + token); +} + +void Downstream::add_request_header(const uint8_t *name, size_t namelen, + const uint8_t *value, size_t valuelen, + bool no_index, int16_t token) { + http2::index_header(request_hdidx_, token, request_headers_.size()); + request_headers_sum_ += namelen + valuelen; + http2::add_header(request_headers_, name, namelen, value, valuelen, no_index, + token); +} + +bool Downstream::get_request_header_key_prev() const { + return request_header_key_prev_; +} + +void Downstream::append_last_request_header_key(const char *data, size_t len) { + append_last_header_key(request_header_key_prev_, request_headers_sum_, + request_headers_, data, len); +} + +void Downstream::append_last_request_header_value(const char *data, + size_t len) { + append_last_header_value(request_header_key_prev_, request_headers_sum_, + request_headers_, data, len); +} + +void Downstream::clear_request_headers() { + Headers().swap(request_headers_); + http2::init_hdidx(request_hdidx_); +} + +size_t Downstream::get_request_headers_sum() const { + return request_headers_sum_; +} + +void Downstream::add_request_trailer(const uint8_t *name, size_t namelen, + const uint8_t *value, size_t valuelen, + bool no_index, int16_t token) { + // we never index trailer part. Header size limit should be applied + // to all request header fields combined. + request_headers_sum_ += namelen + valuelen; + http2::add_header(request_trailers_, name, namelen, value, valuelen, no_index, + -1); +} + +const Headers &Downstream::get_request_trailers() const { + return request_trailers_; +} + +void Downstream::add_request_trailer(std::string name, std::string value) { + add_header(request_trailer_key_prev_, request_headers_sum_, request_trailers_, + std::move(name), std::move(value)); +} + +void Downstream::set_last_request_trailer_value(const char *data, size_t len) { + set_last_header_value(request_trailer_key_prev_, request_headers_sum_, + request_trailers_, data, len); +} + +bool Downstream::get_request_trailer_key_prev() const { + return request_trailer_key_prev_; +} + +void Downstream::append_last_request_trailer_key(const char *data, size_t len) { + append_last_header_key(request_trailer_key_prev_, request_headers_sum_, + request_trailers_, data, len); +} + +void Downstream::append_last_request_trailer_value(const char *data, + size_t len) { + append_last_header_value(request_trailer_key_prev_, request_headers_sum_, + request_trailers_, data, len); +} + +void Downstream::set_request_method(std::string method) { + request_method_ = std::move(method); +} + +const std::string &Downstream::get_request_method() const { + return request_method_; +} + +void Downstream::set_request_path(std::string path) { + request_path_ = std::move(path); +} + +void Downstream::append_request_path(const char *data, size_t len) { + request_path_.append(data, len); +} + +const std::string &Downstream::get_request_path() const { + return request_path_; +} + +void Downstream::set_request_start_time( + std::chrono::high_resolution_clock::time_point time) { + request_start_time_ = std::move(time); +} + +const std::chrono::high_resolution_clock::time_point & +Downstream::get_request_start_time() const { + return request_start_time_; +} + +const std::string &Downstream::get_request_http2_scheme() const { + return request_http2_scheme_; +} + +void Downstream::set_request_http2_scheme(std::string scheme) { + request_http2_scheme_ = std::move(scheme); +} + +const std::string &Downstream::get_request_http2_authority() const { + return request_http2_authority_; +} + +void Downstream::set_request_http2_authority(std::string authority) { + request_http2_authority_ = std::move(authority); +} + +void Downstream::set_request_major(int major) { request_major_ = major; } + +void Downstream::set_request_minor(int minor) { request_minor_ = minor; } + +int Downstream::get_request_major() const { return request_major_; } + +int Downstream::get_request_minor() const { return request_minor_; } + +void Downstream::reset_upstream(Upstream *upstream) { + upstream_ = upstream; + if (dconn_) { + dconn_->on_upstream_change(upstream); + } +} + +Upstream *Downstream::get_upstream() const { return upstream_; } + +void Downstream::set_stream_id(int32_t stream_id) { stream_id_ = stream_id; } + +int32_t Downstream::get_stream_id() const { return stream_id_; } + +void Downstream::set_request_state(int state) { request_state_ = state; } + +int Downstream::get_request_state() const { return request_state_; } + +bool Downstream::get_chunked_request() const { return chunked_request_; } + +void Downstream::set_chunked_request(bool f) { chunked_request_ = f; } + +bool Downstream::get_request_connection_close() const { + return request_connection_close_; +} + +void Downstream::set_request_connection_close(bool f) { + request_connection_close_ = f; +} + +bool Downstream::get_request_http2_expect_body() const { + return request_http2_expect_body_; +} + +void Downstream::set_request_http2_expect_body(bool f) { + request_http2_expect_body_ = f; +} + +bool Downstream::request_buf_full() { + if (dconn_) { + return request_buf_.rleft() >= get_config()->downstream_request_buffer_size; + } else { + return false; + } +} + +DefaultMemchunks *Downstream::get_request_buf() { return &request_buf_; } + +// Call this function after this object is attached to +// Downstream. Otherwise, the program will crash. +int Downstream::push_request_headers() { + if (!dconn_) { + DLOG(INFO, this) << "dconn_ is NULL"; + return -1; + } + return dconn_->push_request_headers(); +} + +int Downstream::push_upload_data_chunk(const uint8_t *data, size_t datalen) { + // Assumes that request headers have already been pushed to output + // buffer using push_request_headers(). + if (!dconn_) { + DLOG(INFO, this) << "dconn_ is NULL"; + return -1; + } + request_bodylen_ += datalen; + if (dconn_->push_upload_data_chunk(data, datalen) != 0) { + return -1; + } + + request_datalen_ += datalen; + + return 0; +} + +int Downstream::end_upload_data() { + if (!dconn_) { + DLOG(INFO, this) << "dconn_ is NULL"; + return -1; + } + return dconn_->end_upload_data(); +} + +const Headers &Downstream::get_response_headers() const { + return response_headers_; +} + +int Downstream::index_response_headers() { + return index_headers(response_hdidx_, response_headers_, + response_content_length_); +} + +const Headers::value_type * +Downstream::get_response_header(int16_t token) const { + return http2::get_header(response_hdidx_, token, response_headers_); +} + +void Downstream::rewrite_location_response_header( + const std::string &upstream_scheme) { + auto hd = + http2::get_header(response_hdidx_, http2::HD_LOCATION, response_headers_); + if (!hd) { + return; + } + http_parser_url u; + memset(&u, 0, sizeof(u)); + int rv = + http_parser_parse_url((*hd).value.c_str(), (*hd).value.size(), 0, &u); + if (rv != 0) { + return; + } + std::string new_uri; + if (get_config()->no_host_rewrite || request_method_ == "CONNECT") { + if (!request_http2_authority_.empty()) { + new_uri = http2::rewrite_location_uri( + (*hd).value, u, request_http2_authority_, request_http2_authority_, + upstream_scheme); + } + if (new_uri.empty()) { + auto host = get_request_header(http2::HD_HOST); + if (host) { + new_uri = http2::rewrite_location_uri((*hd).value, u, (*host).value, + (*host).value, upstream_scheme); + } else if (!request_downstream_host_.empty()) { + new_uri = http2::rewrite_location_uri( + (*hd).value, u, request_downstream_host_, "", upstream_scheme); + } else { + return; + } + } + } else { + if (request_downstream_host_.empty()) { + return; + } + if (!request_http2_authority_.empty()) { + new_uri = http2::rewrite_location_uri( + (*hd).value, u, request_downstream_host_, request_http2_authority_, + upstream_scheme); + } else { + auto host = get_request_header(http2::HD_HOST); + if (host) { + new_uri = http2::rewrite_location_uri((*hd).value, u, + request_downstream_host_, + (*host).value, upstream_scheme); + } else { + new_uri = http2::rewrite_location_uri( + (*hd).value, u, request_downstream_host_, "", upstream_scheme); + } + } + } + if (!new_uri.empty()) { + auto idx = response_hdidx_[http2::HD_LOCATION]; + response_headers_[idx].value = std::move(new_uri); + } +} + +void Downstream::add_response_header(std::string name, std::string value) { + add_header(response_header_key_prev_, response_headers_sum_, + response_headers_, std::move(name), std::move(value)); +} + +void Downstream::set_last_response_header_value(const char *data, size_t len) { + set_last_header_value(response_header_key_prev_, response_headers_sum_, + response_headers_, data, len); +} + +void Downstream::add_response_header(std::string name, std::string value, + int16_t token) { + http2::index_header(response_hdidx_, token, response_headers_.size()); + response_headers_sum_ += name.size() + value.size(); + response_headers_.emplace_back(std::move(name), std::move(value), false, + token); +} + +void Downstream::add_response_header(const uint8_t *name, size_t namelen, + const uint8_t *value, size_t valuelen, + bool no_index, int16_t token) { + http2::index_header(response_hdidx_, token, response_headers_.size()); + response_headers_sum_ += namelen + valuelen; + http2::add_header(response_headers_, name, namelen, value, valuelen, no_index, + token); +} + +bool Downstream::get_response_header_key_prev() const { + return response_header_key_prev_; +} + +void Downstream::append_last_response_header_key(const char *data, size_t len) { + append_last_header_key(response_header_key_prev_, response_headers_sum_, + response_headers_, data, len); +} + +void Downstream::append_last_response_header_value(const char *data, + size_t len) { + append_last_header_value(response_header_key_prev_, response_headers_sum_, + response_headers_, data, len); +} + +void Downstream::clear_response_headers() { + Headers().swap(response_headers_); + http2::init_hdidx(response_hdidx_); +} + +size_t Downstream::get_response_headers_sum() const { + return response_headers_sum_; +} + +const Headers &Downstream::get_response_trailers() const { + return response_trailers_; +} + +void Downstream::add_response_trailer(const uint8_t *name, size_t namelen, + const uint8_t *value, size_t valuelen, + bool no_index, int16_t token) { + response_headers_sum_ += namelen + valuelen; + http2::add_header(response_trailers_, name, namelen, value, valuelen, + no_index, -1); +} + +unsigned int Downstream::get_response_http_status() const { + return response_http_status_; +} + +void Downstream::add_response_trailer(std::string name, std::string value) { + add_header(response_trailer_key_prev_, response_headers_sum_, + response_trailers_, std::move(name), std::move(value)); +} + +void Downstream::set_last_response_trailer_value(const char *data, size_t len) { + set_last_header_value(response_trailer_key_prev_, response_headers_sum_, + response_trailers_, data, len); +} + +bool Downstream::get_response_trailer_key_prev() const { + return response_trailer_key_prev_; +} + +void Downstream::append_last_response_trailer_key(const char *data, + size_t len) { + append_last_header_key(response_trailer_key_prev_, response_headers_sum_, + response_trailers_, data, len); +} + +void Downstream::append_last_response_trailer_value(const char *data, + size_t len) { + append_last_header_value(response_trailer_key_prev_, response_headers_sum_, + response_trailers_, data, len); +} + +void Downstream::set_response_http_status(unsigned int status) { + response_http_status_ = status; +} + +void Downstream::set_response_major(int major) { response_major_ = major; } + +void Downstream::set_response_minor(int minor) { response_minor_ = minor; } + +int Downstream::get_response_major() const { return response_major_; } + +int Downstream::get_response_minor() const { return response_minor_; } + +int Downstream::get_response_version() const { + return response_major_ * 100 + response_minor_; +} + +bool Downstream::get_chunked_response() const { return chunked_response_; } + +void Downstream::set_chunked_response(bool f) { chunked_response_ = f; } + +bool Downstream::get_response_connection_close() const { + return response_connection_close_; +} + +void Downstream::set_response_connection_close(bool f) { + response_connection_close_ = f; +} + +int Downstream::on_read() { + if (!dconn_) { + DLOG(INFO, this) << "dconn_ is NULL"; + return -1; + } + return dconn_->on_read(); +} + +int Downstream::change_priority(int32_t pri) { + if (!dconn_) { + DLOG(INFO, this) << "dconn_ is NULL"; + return -1; + } + return dconn_->on_priority_change(pri); +} + +void Downstream::set_response_state(int state) { response_state_ = state; } + +int Downstream::get_response_state() const { return response_state_; } + +DefaultMemchunks *Downstream::get_response_buf() { return &response_buf_; } + +bool Downstream::response_buf_full() { + if (dconn_) { + return response_buf_.rleft() >= + get_config()->downstream_response_buffer_size; + } else { + return false; + } +} + +void Downstream::add_response_bodylen(size_t amount) { + response_bodylen_ += amount; +} + +int64_t Downstream::get_response_bodylen() const { return response_bodylen_; } + +void Downstream::add_response_sent_bodylen(size_t amount) { + response_sent_bodylen_ += amount; +} + +int64_t Downstream::get_response_sent_bodylen() const { + return response_sent_bodylen_; +} + +int64_t Downstream::get_response_content_length() const { + return response_content_length_; +} + +void Downstream::set_response_content_length(int64_t len) { + response_content_length_ = len; +} + +int64_t Downstream::get_request_content_length() const { + return request_content_length_; +} + +void Downstream::set_request_content_length(int64_t len) { + request_content_length_ = len; +} + +bool Downstream::validate_request_bodylen() const { + if (request_content_length_ == -1) { + return true; + } + + if (request_content_length_ != request_bodylen_) { + if (LOG_ENABLED(INFO)) { + DLOG(INFO, this) << "request invalid bodylen: content-length=" + << request_content_length_ + << ", received=" << request_bodylen_; + } + return false; + } + + return true; +} + +bool Downstream::validate_response_bodylen() const { + if (!expect_response_body() || response_content_length_ == -1) { + return true; + } + + if (response_content_length_ != response_bodylen_) { + if (LOG_ENABLED(INFO)) { + DLOG(INFO, this) << "response invalid bodylen: content-length=" + << response_content_length_ + << ", received=" << response_bodylen_; + } + return false; + } + + return true; +} + +void Downstream::set_priority(int32_t pri) { priority_ = pri; } + +int32_t Downstream::get_priority() const { return priority_; } + +void Downstream::check_upgrade_fulfilled() { + if (request_method_ == "CONNECT") { + upgraded_ = 200 <= response_http_status_ && response_http_status_ < 300; + + return; + } + + if (response_http_status_ == 101) { + // TODO Do more strict checking for upgrade headers + upgraded_ = upgrade_request_; + + return; + } +} + +void Downstream::inspect_http2_request() { + if (request_method_ == "CONNECT") { + upgrade_request_ = true; + } +} + +void Downstream::inspect_http1_request() { + if (request_method_ == "CONNECT") { + upgrade_request_ = true; + } + + if (!upgrade_request_) { + auto idx = request_hdidx_[http2::HD_UPGRADE]; + if (idx != -1) { + upgrade_request_ = true; + + auto &val = request_headers_[idx].value; + // TODO Perform more strict checking for upgrade headers + if (util::streq_l(NGHTTP2_CLEARTEXT_PROTO_VERSION_ID, val.c_str(), + val.size())) { + http2_upgrade_seen_ = true; + } + } + } + auto idx = request_hdidx_[http2::HD_TRANSFER_ENCODING]; + if (idx != -1) { + request_content_length_ = -1; + if (util::strifind(request_headers_[idx].value.c_str(), "chunked")) { + chunked_request_ = true; + } + } +} + +void Downstream::inspect_http1_response() { + auto idx = response_hdidx_[http2::HD_TRANSFER_ENCODING]; + if (idx != -1) { + response_content_length_ = -1; + if (util::strifind(response_headers_[idx].value.c_str(), "chunked")) { + chunked_response_ = true; + } + } +} + +void Downstream::reset_response() { + response_http_status_ = 0; + response_major_ = 1; + response_minor_ = 1; +} + +bool Downstream::get_non_final_response() const { + return response_http_status_ / 100 == 1; +} + +bool Downstream::get_upgraded() const { return upgraded_; } + +bool Downstream::get_upgrade_request() const { return upgrade_request_; } + +bool Downstream::get_http2_upgrade_request() const { + return request_bodylen_ == 0 && http2_upgrade_seen_ && + request_hdidx_[http2::HD_HTTP2_SETTINGS] != -1; +} + +namespace { +const std::string EMPTY; +} // namespace + +const std::string &Downstream::get_http2_settings() const { + auto idx = request_hdidx_[http2::HD_HTTP2_SETTINGS]; + if (idx == -1) { + return EMPTY; + } + return request_headers_[idx].value; +} + +void Downstream::set_downstream_stream_id(int32_t stream_id) { + downstream_stream_id_ = stream_id; +} + +int32_t Downstream::get_downstream_stream_id() const { + return downstream_stream_id_; +} + +uint32_t Downstream::get_response_rst_stream_error_code() const { + return response_rst_stream_error_code_; +} + +void Downstream::set_response_rst_stream_error_code(uint32_t error_code) { + response_rst_stream_error_code_ = error_code; +} + +void Downstream::set_expect_final_response(bool f) { + expect_final_response_ = f; +} + +bool Downstream::get_expect_final_response() const { + return expect_final_response_; +} + +size_t Downstream::get_request_datalen() const { return request_datalen_; } + +void Downstream::dec_request_datalen(size_t len) { + assert(request_datalen_ >= len); + request_datalen_ -= len; +} + +void Downstream::reset_request_datalen() { request_datalen_ = 0; } + +void Downstream::add_response_datalen(size_t len) { response_datalen_ += len; } + +void Downstream::dec_response_datalen(size_t len) { + assert(response_datalen_ >= len); + response_datalen_ -= len; +} + +size_t Downstream::get_response_datalen() const { return response_datalen_; } + +void Downstream::reset_response_datalen() { response_datalen_ = 0; } + +bool Downstream::expect_response_body() const { + return http2::expect_response_body(request_method_, response_http_status_); +} + +namespace { +bool pseudo_header_allowed(const Headers &headers) { + if (headers.empty()) { + return true; + } + + return headers.back().name.c_str()[0] == ':'; +} +} // namespace + +bool Downstream::request_pseudo_header_allowed(int16_t token) const { + if (!pseudo_header_allowed(request_headers_)) { + return false; + } + return http2::check_http2_request_pseudo_header(request_hdidx_, token); +} + +bool Downstream::response_pseudo_header_allowed(int16_t token) const { + if (!pseudo_header_allowed(response_headers_)) { + return false; + } + return http2::check_http2_response_pseudo_header(response_hdidx_, token); +} + +namespace { +void reset_timer(struct ev_loop *loop, ev_timer *w) { ev_timer_again(loop, w); } +} // namespace + +namespace { +void try_reset_timer(struct ev_loop *loop, ev_timer *w) { + if (!ev_is_active(w)) { + return; + } + ev_timer_again(loop, w); +} +} // namespace + +namespace { +void ensure_timer(struct ev_loop *loop, ev_timer *w) { + if (ev_is_active(w)) { + return; + } + ev_timer_again(loop, w); +} +} // namespace + +namespace { +void disable_timer(struct ev_loop *loop, ev_timer *w) { + ev_timer_stop(loop, w); +} +} // namespace + +void Downstream::reset_upstream_rtimer() { + if (get_config()->stream_read_timeout == 0.) { + return; + } + auto loop = upstream_->get_client_handler()->get_loop(); + reset_timer(loop, &upstream_rtimer_); +} + +void Downstream::reset_upstream_wtimer() { + auto loop = upstream_->get_client_handler()->get_loop(); + if (get_config()->stream_write_timeout != 0.) { + reset_timer(loop, &upstream_wtimer_); + } + if (get_config()->stream_read_timeout != 0.) { + try_reset_timer(loop, &upstream_rtimer_); + } +} + +void Downstream::ensure_upstream_wtimer() { + if (get_config()->stream_write_timeout == 0.) { + return; + } + auto loop = upstream_->get_client_handler()->get_loop(); + ensure_timer(loop, &upstream_wtimer_); +} + +void Downstream::disable_upstream_rtimer() { + if (get_config()->stream_read_timeout == 0.) { + return; + } + auto loop = upstream_->get_client_handler()->get_loop(); + disable_timer(loop, &upstream_rtimer_); +} + +void Downstream::disable_upstream_wtimer() { + if (get_config()->stream_write_timeout == 0.) { + return; + } + auto loop = upstream_->get_client_handler()->get_loop(); + disable_timer(loop, &upstream_wtimer_); +} + +void Downstream::reset_downstream_rtimer() { + if (get_config()->stream_read_timeout == 0.) { + return; + } + auto loop = upstream_->get_client_handler()->get_loop(); + reset_timer(loop, &downstream_rtimer_); +} + +void Downstream::reset_downstream_wtimer() { + auto loop = upstream_->get_client_handler()->get_loop(); + if (get_config()->stream_write_timeout != 0.) { + reset_timer(loop, &downstream_wtimer_); + } + if (get_config()->stream_read_timeout != 0.) { + try_reset_timer(loop, &downstream_rtimer_); + } +} + +void Downstream::ensure_downstream_wtimer() { + if (get_config()->stream_write_timeout == 0.) { + return; + } + auto loop = upstream_->get_client_handler()->get_loop(); + ensure_timer(loop, &downstream_wtimer_); +} + +void Downstream::disable_downstream_rtimer() { + if (get_config()->stream_read_timeout == 0.) { + return; + } + auto loop = upstream_->get_client_handler()->get_loop(); + disable_timer(loop, &downstream_rtimer_); +} + +void Downstream::disable_downstream_wtimer() { + if (get_config()->stream_write_timeout == 0.) { + return; + } + auto loop = upstream_->get_client_handler()->get_loop(); + disable_timer(loop, &downstream_wtimer_); +} + +bool Downstream::accesslog_ready() const { return response_http_status_ > 0; } + +void Downstream::add_retry() { ++num_retry_; } + +bool Downstream::no_more_retry() const { return num_retry_ > 5; } + +void Downstream::set_request_downstream_host(std::string host) { + request_downstream_host_ = std::move(host); +} + +void Downstream::set_request_pending(bool f) { request_pending_ = f; } + +bool Downstream::get_request_pending() const { return request_pending_; } + +bool Downstream::request_submission_ready() const { + return (request_state_ == Downstream::HEADER_COMPLETE || + request_state_ == Downstream::MSG_COMPLETE) && + request_pending_ && response_state_ == Downstream::INITIAL; +} + +int Downstream::get_dispatch_state() const { return dispatch_state_; } + +void Downstream::set_dispatch_state(int s) { dispatch_state_ = s; } + +void Downstream::attach_blocked_link(BlockedLink *l) { + assert(!blocked_link_); + + l->downstream = this; + blocked_link_ = l; +} + +void Downstream::detach_blocked_link(BlockedLink *l) { + assert(blocked_link_); + assert(l->downstream == this); + + l->downstream = nullptr; + blocked_link_ = nullptr; +} + +void Downstream::add_request_headers_sum(size_t amount) { + request_headers_sum_ += amount; +} + +} // namespace shrpx diff --git a/src/shrpx_downstream.h b/src/shrpx_downstream.h new file mode 100644 index 0000000..2bd2931 --- /dev/null +++ b/src/shrpx_downstream.h @@ -0,0 +1,456 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef SHRPX_DOWNSTREAM_H +#define SHRPX_DOWNSTREAM_H + +#include "shrpx.h" + +#include +#include +#include +#include +#include + +#include + +#include + +#include "shrpx_io_control.h" +#include "http2.h" +#include "memchunk.h" + +using namespace nghttp2; + +namespace shrpx { + +class Upstream; +class DownstreamConnection; +struct BlockedLink; + +class Downstream { +public: + Downstream(Upstream *upstream, MemchunkPool *mcpool, int32_t stream_id, + int32_t priority); + ~Downstream(); + void reset_upstream(Upstream *upstream); + Upstream *get_upstream() const; + void set_stream_id(int32_t stream_id); + int32_t get_stream_id() const; + void set_priority(int32_t pri); + int32_t get_priority() const; + void pause_read(IOCtrlReason reason); + int resume_read(IOCtrlReason reason, size_t consumed); + void force_resume_read(); + // Set stream ID for downstream HTTP2 connection. + void set_downstream_stream_id(int32_t stream_id); + int32_t get_downstream_stream_id() const; + + int attach_downstream_connection(std::unique_ptr dconn); + void detach_downstream_connection(); + // Releases dconn_, without freeing it. + void release_downstream_connection(); + DownstreamConnection *get_downstream_connection(); + // Returns dconn_ and nullifies dconn_. + std::unique_ptr pop_downstream_connection(); + + // Returns true if output buffer is full. If underlying dconn_ is + // NULL, this function always returns false. + bool request_buf_full(); + // Returns true if upgrade (HTTP Upgrade or CONNECT) is succeeded. + void check_upgrade_fulfilled(); + // Returns true if the request is upgrade. + bool get_upgrade_request() const; + // Returns true if the upgrade is succeded as a result of the call + // check_upgrade_fulfilled(). + bool get_upgraded() const; + // Inspects HTTP/2 request. + void inspect_http2_request(); + // Inspects HTTP/1 request. This checks whether the request is + // upgrade request and tranfer-encoding etc. + void inspect_http1_request(); + // Returns true if the request is HTTP Upgrade for HTTP/2 + bool get_http2_upgrade_request() const; + // Returns the value of HTTP2-Settings request header field. + const std::string &get_http2_settings() const; + // downstream request API + const Headers &get_request_headers() const; + // Crumbles (split cookie by ";") in request_headers_ and returns + // them. Headers::no_index is inherited. + Headers crumble_request_cookie(); + void assemble_request_cookie(); + const std::string &get_assembled_request_cookie() const; + // Lower the request header field names and indexes request headers. + // If there is any invalid headers (e.g., multiple Content-Length + // having different values), returns -1. + int index_request_headers(); + // Returns pointer to the request header with the name |name|. If + // multiple header have |name| as name, return last occurrence from + // the beginning. If no such header is found, returns nullptr. + // This function must be called after headers are indexed + const Headers::value_type *get_request_header(int16_t token) const; + // Returns pointer to the request header with the name |name|. If + // no such header is found, returns nullptr. + const Headers::value_type *get_request_header(const std::string &name) const; + void add_request_header(std::string name, std::string value); + void set_last_request_header_value(const char *data, size_t len); + + void add_request_header(std::string name, std::string value, int16_t token); + void add_request_header(const uint8_t *name, size_t namelen, + const uint8_t *value, size_t valuelen, bool no_index, + int16_t token); + + bool get_request_header_key_prev() const; + void append_last_request_header_key(const char *data, size_t len); + void append_last_request_header_value(const char *data, size_t len); + // Empties request headers. + void clear_request_headers(); + + size_t get_request_headers_sum() const; + + const Headers &get_request_trailers() const; + void add_request_trailer(const uint8_t *name, size_t namelen, + const uint8_t *value, size_t valuelen, bool no_index, + int16_t token); + void add_request_trailer(std::string name, std::string value); + void set_last_request_trailer_value(const char *data, size_t len); + bool get_request_trailer_key_prev() const; + void append_last_request_trailer_key(const char *data, size_t len); + void append_last_request_trailer_value(const char *data, size_t len); + + void set_request_method(std::string method); + const std::string &get_request_method() const; + void set_request_path(std::string path); + void add_request_headers_sum(size_t amount); + void + set_request_start_time(std::chrono::high_resolution_clock::time_point time); + const std::chrono::high_resolution_clock::time_point & + get_request_start_time() const; + void append_request_path(const char *data, size_t len); + // Returns request path. For HTTP/1.1, this is request-target. For + // HTTP/2, this is :path header field value. + const std::string &get_request_path() const; + // Returns HTTP/2 :scheme header field value. + const std::string &get_request_http2_scheme() const; + void set_request_http2_scheme(std::string scheme); + // Returns HTTP/2 :authority header field value. We also set the + // value retrieved from absolute-form HTTP/1 request. + const std::string &get_request_http2_authority() const; + void set_request_http2_authority(std::string authority); + void set_request_major(int major); + void set_request_minor(int minor); + int get_request_major() const; + int get_request_minor() const; + int push_request_headers(); + bool get_chunked_request() const; + void set_chunked_request(bool f); + bool get_request_connection_close() const; + void set_request_connection_close(bool f); + bool get_request_http2_expect_body() const; + void set_request_http2_expect_body(bool f); + int push_upload_data_chunk(const uint8_t *data, size_t datalen); + int end_upload_data(); + size_t get_request_datalen() const; + void dec_request_datalen(size_t len); + void reset_request_datalen(); + // Validates that received request body length and content-length + // matches. + bool validate_request_bodylen() const; + int64_t get_request_content_length() const; + void set_request_content_length(int64_t len); + bool request_pseudo_header_allowed(int16_t token) const; + void set_request_downstream_host(std::string host); + bool expect_response_body() const; + enum { + INITIAL, + HEADER_COMPLETE, + MSG_COMPLETE, + STREAM_CLOSED, + CONNECT_FAIL, + IDLE, + MSG_RESET, + // header contains invalid header field. We can safely send error + // response (502) to a client. + MSG_BAD_HEADER, + // header fields in HTTP/1 request exceed the configuration limit. + // This state is only transitioned from INITIAL state, and solely + // used to signal 431 status code to the client. + HTTP1_REQUEST_HEADER_TOO_LARGE, + }; + void set_request_state(int state); + int get_request_state() const; + DefaultMemchunks *get_request_buf(); + void set_request_pending(bool f); + bool get_request_pending() const; + // Returns true if request is ready to be submitted to downstream. + bool request_submission_ready() const; + // downstream response API + const Headers &get_response_headers() const; + // Lower the response header field names and indexes response + // headers. If there are invalid headers (e.g., multiple + // Content-Length with different values), returns -1. + int index_response_headers(); + // Returns pointer to the response header with the name |name|. If + // multiple header have |name| as name, return last occurrence from + // the beginning. If no such header is found, returns nullptr. + // This function must be called after response headers are indexed. + const Headers::value_type *get_response_header(int16_t token) const; + // Rewrites the location response header field. + void rewrite_location_response_header(const std::string &upstream_scheme); + void add_response_header(std::string name, std::string value); + void set_last_response_header_value(const char *data, size_t len); + + void add_response_header(std::string name, std::string value, int16_t token); + void add_response_header(const uint8_t *name, size_t namelen, + const uint8_t *value, size_t valuelen, bool no_index, + int16_t token); + + bool get_response_header_key_prev() const; + void append_last_response_header_key(const char *data, size_t len); + void append_last_response_header_value(const char *data, size_t len); + // Empties response headers. + void clear_response_headers(); + + size_t get_response_headers_sum() const; + + const Headers &get_response_trailers() const; + void add_response_trailer(const uint8_t *name, size_t namelen, + const uint8_t *value, size_t valuelen, + bool no_index, int16_t token); + void add_response_trailer(std::string name, std::string value); + void set_last_response_trailer_value(const char *data, size_t len); + bool get_response_trailer_key_prev() const; + void append_last_response_trailer_key(const char *data, size_t len); + void append_last_response_trailer_value(const char *data, size_t len); + + unsigned int get_response_http_status() const; + void set_response_http_status(unsigned int status); + void set_response_major(int major); + void set_response_minor(int minor); + int get_response_major() const; + int get_response_minor() const; + int get_response_version() const; + bool get_chunked_response() const; + void set_chunked_response(bool f); + bool get_response_connection_close() const; + void set_response_connection_close(bool f); + void set_response_state(int state); + int get_response_state() const; + DefaultMemchunks *get_response_buf(); + bool response_buf_full(); + void add_response_bodylen(size_t amount); + int64_t get_response_bodylen() const; + void add_response_sent_bodylen(size_t amount); + int64_t get_response_sent_bodylen() const; + int64_t get_response_content_length() const; + void set_response_content_length(int64_t len); + // Validates that received response body length and content-length + // matches. + bool validate_response_bodylen() const; + uint32_t get_response_rst_stream_error_code() const; + void set_response_rst_stream_error_code(uint32_t error_code); + // Inspects HTTP/1 response. This checks tranfer-encoding etc. + void inspect_http1_response(); + // Clears some of member variables for response. + void reset_response(); + bool get_non_final_response() const; + void set_expect_final_response(bool f); + bool get_expect_final_response() const; + void add_response_datalen(size_t len); + void dec_response_datalen(size_t len); + size_t get_response_datalen() const; + void reset_response_datalen(); + bool response_pseudo_header_allowed(int16_t token) const; + + // Call this method when there is incoming data in downstream + // connection. + int on_read(); + + // Change the priority of downstream + int change_priority(int32_t pri); + + bool get_rst_stream_after_end_stream() const; + void set_rst_stream_after_end_stream(bool f); + + // Resets upstream read timer. If it is active, timeout value is + // reset. If it is not active, timer will be started. + void reset_upstream_rtimer(); + // Resets upstream write timer. If it is active, timeout value is + // reset. If it is not active, timer will be started. This + // function also resets read timer if it has been started. + void reset_upstream_wtimer(); + // Makes sure that upstream write timer is started. If it has been + // started, do nothing. Otherwise, write timer will be started. + void ensure_upstream_wtimer(); + // Disables upstream read timer. + void disable_upstream_rtimer(); + // Disables upstream write timer. + void disable_upstream_wtimer(); + + // Downstream timer functions. They works in a similar way just + // like the upstream timer function. + void reset_downstream_rtimer(); + void reset_downstream_wtimer(); + void ensure_downstream_wtimer(); + void disable_downstream_rtimer(); + void disable_downstream_wtimer(); + + // Returns true if accesslog can be written for this downstream. + bool accesslog_ready() const; + + // Increment retry count + void add_retry(); + // true if retry attempt should not be done. + bool no_more_retry() const; + + int get_dispatch_state() const; + void set_dispatch_state(int s); + + void attach_blocked_link(BlockedLink *l); + void detach_blocked_link(BlockedLink *l); + + enum { + EVENT_ERROR = 0x1, + EVENT_TIMEOUT = 0x2, + }; + + enum { + DISPATCH_NONE, + DISPATCH_PENDING, + DISPATCH_BLOCKED, + DISPATCH_ACTIVE, + DISPATCH_FAILURE, + }; + + Downstream *dlnext, *dlprev; + +private: + Headers request_headers_; + Headers response_headers_; + + // trailer part. For HTTP/1.1, trailer part is only included with + // chunked encoding. For HTTP/2, there is no such limit. + Headers request_trailers_; + Headers response_trailers_; + + std::chrono::high_resolution_clock::time_point request_start_time_; + + std::string request_method_; + std::string request_path_; + std::string request_http2_scheme_; + std::string request_http2_authority_; + // host we requested to downstream. This is used to rewrite + // location header field to decide the location should be rewritten + // or not. + std::string request_downstream_host_; + std::string assembled_request_cookie_; + + DefaultMemchunks request_buf_; + DefaultMemchunks response_buf_; + + ev_timer upstream_rtimer_; + ev_timer upstream_wtimer_; + + ev_timer downstream_rtimer_; + ev_timer downstream_wtimer_; + + // the length of request body received so far + int64_t request_bodylen_; + // the length of response body received so far + int64_t response_bodylen_; + + // the length of response body sent to upstream client + int64_t response_sent_bodylen_; + + // content-length of request body, -1 if it is unknown. + int64_t request_content_length_; + // content-length of response body, -1 if it is unknown. + int64_t response_content_length_; + + Upstream *upstream_; + std::unique_ptr dconn_; + + // only used by HTTP/2 or SPDY upstream + BlockedLink *blocked_link_; + + size_t request_headers_sum_; + size_t response_headers_sum_; + + // The number of bytes not consumed by the application yet. + size_t request_datalen_; + size_t response_datalen_; + + size_t num_retry_; + + int32_t stream_id_; + int32_t priority_; + // stream ID in backend connection + int32_t downstream_stream_id_; + + // RST_STREAM error_code from downstream HTTP2 connection + uint32_t response_rst_stream_error_code_; + + int request_state_; + int request_major_; + int request_minor_; + + int response_state_; + unsigned int response_http_status_; + int response_major_; + int response_minor_; + + // only used by HTTP/2 or SPDY upstream + int dispatch_state_; + + http2::HeaderIndex request_hdidx_; + http2::HeaderIndex response_hdidx_; + + // true if the request contains upgrade token (HTTP Upgrade or + // CONNECT) + bool upgrade_request_; + // true if the connection is upgraded (HTTP Upgrade or CONNECT) + bool upgraded_; + + bool http2_upgrade_seen_; + + bool chunked_request_; + bool request_connection_close_; + bool request_header_key_prev_; + bool request_trailer_key_prev_; + bool request_http2_expect_body_; + + bool chunked_response_; + bool response_connection_close_; + bool response_header_key_prev_; + bool response_trailer_key_prev_; + bool expect_final_response_; + // true if downstream request is pending because backend connection + // has not been established or should be checked before use; + // currently used only with HTTP/2 connection. + bool request_pending_; +}; + +} // namespace shrpx + +#endif // SHRPX_DOWNSTREAM_H diff --git a/src/shrpx_downstream_connection.cc b/src/shrpx_downstream_connection.cc new file mode 100644 index 0000000..77dcd44 --- /dev/null +++ b/src/shrpx_downstream_connection.cc @@ -0,0 +1,52 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "shrpx_downstream_connection.h" + +#include "shrpx_client_handler.h" +#include "shrpx_downstream.h" +#include "shrpx_downstream_connection_pool.h" + +namespace shrpx { + +DownstreamConnection::DownstreamConnection(DownstreamConnectionPool *dconn_pool) + : dconn_pool_(dconn_pool), client_handler_(nullptr), downstream_(nullptr) {} + +DownstreamConnection::~DownstreamConnection() {} + +void DownstreamConnection::set_client_handler(ClientHandler *handler) { + client_handler_ = handler; +} + +ClientHandler *DownstreamConnection::get_client_handler() { + return client_handler_; +} + +Downstream *DownstreamConnection::get_downstream() { return downstream_; } + +DownstreamConnectionPool *DownstreamConnection::get_dconn_pool() const { + return dconn_pool_; +} + +} // namespace shrpx diff --git a/src/shrpx_downstream_connection.h b/src/shrpx_downstream_connection.h new file mode 100644 index 0000000..49ca1be --- /dev/null +++ b/src/shrpx_downstream_connection.h @@ -0,0 +1,77 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef SHRPX_DOWNSTREAM_CONNECTION_H +#define SHRPX_DOWNSTREAM_CONNECTION_H + +#include "shrpx.h" + +#include "shrpx_io_control.h" + +namespace shrpx { + +class ClientHandler; +class Upstream; +class Downstream; +class DownstreamConnectionPool; + +class DownstreamConnection { +public: + DownstreamConnection(DownstreamConnectionPool *dconn_pool); + virtual ~DownstreamConnection(); + virtual int attach_downstream(Downstream *downstream) = 0; + virtual void detach_downstream(Downstream *downstream) = 0; + + virtual int push_request_headers() = 0; + virtual int push_upload_data_chunk(const uint8_t *data, size_t datalen) = 0; + virtual int end_upload_data() = 0; + + virtual void pause_read(IOCtrlReason reason) = 0; + virtual int resume_read(IOCtrlReason reason, size_t consumed) = 0; + virtual void force_resume_read() = 0; + + virtual int on_read() = 0; + virtual int on_write() = 0; + virtual int on_timeout() { return 0; } + + virtual void on_upstream_change(Upstream *uptream) = 0; + virtual int on_priority_change(int32_t pri) = 0; + + // true if this object is poolable. + virtual bool poolable() const = 0; + + void set_client_handler(ClientHandler *client_handler); + ClientHandler *get_client_handler(); + Downstream *get_downstream(); + DownstreamConnectionPool *get_dconn_pool() const; + +protected: + DownstreamConnectionPool *dconn_pool_; + ClientHandler *client_handler_; + Downstream *downstream_; +}; + +} // namespace shrpx + +#endif // SHRPX_DOWNSTREAM_CONNECTION_H diff --git a/src/shrpx_downstream_connection_pool.cc b/src/shrpx_downstream_connection_pool.cc new file mode 100644 index 0000000..d762b77 --- /dev/null +++ b/src/shrpx_downstream_connection_pool.cc @@ -0,0 +1,60 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2014 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "shrpx_downstream_connection_pool.h" +#include "shrpx_downstream_connection.h" + +namespace shrpx { + +DownstreamConnectionPool::DownstreamConnectionPool() {} + +DownstreamConnectionPool::~DownstreamConnectionPool() { + for (auto dconn : pool_) { + delete dconn; + } +} + +void DownstreamConnectionPool::add_downstream_connection( + std::unique_ptr dconn) { + pool_.insert(dconn.release()); +} + +std::unique_ptr +DownstreamConnectionPool::pop_downstream_connection() { + if (pool_.empty()) { + return nullptr; + } + + auto dconn = std::unique_ptr(*std::begin(pool_)); + pool_.erase(std::begin(pool_)); + return dconn; +} + +void DownstreamConnectionPool::remove_downstream_connection( + DownstreamConnection *dconn) { + pool_.erase(dconn); + delete dconn; +} + +} // namespace shrpx diff --git a/src/shrpx_downstream_connection_pool.h b/src/shrpx_downstream_connection_pool.h new file mode 100644 index 0000000..c2edce4 --- /dev/null +++ b/src/shrpx_downstream_connection_pool.h @@ -0,0 +1,52 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2014 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef SHRPX_DOWNSTREAM_CONNECTION_POOL_H +#define SHRPX_DOWNSTREAM_CONNECTION_POOL_H + +#include "shrpx.h" + +#include +#include + +namespace shrpx { + +class DownstreamConnection; + +class DownstreamConnectionPool { +public: + DownstreamConnectionPool(); + ~DownstreamConnectionPool(); + + void add_downstream_connection(std::unique_ptr dconn); + std::unique_ptr pop_downstream_connection(); + void remove_downstream_connection(DownstreamConnection *dconn); + +private: + std::set pool_; +}; + +} // namespace shrpx + +#endif // SHRPX_DOWNSTREAM_CONNECTION_POOL_H diff --git a/src/shrpx_downstream_queue.cc b/src/shrpx_downstream_queue.cc new file mode 100644 index 0000000..32ff4e6 --- /dev/null +++ b/src/shrpx_downstream_queue.cc @@ -0,0 +1,167 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "shrpx_downstream_queue.h" + +#include +#include + +#include "shrpx_downstream.h" + +namespace shrpx { + +DownstreamQueue::HostEntry::HostEntry() : num_active(0) {} + +DownstreamQueue::DownstreamQueue(size_t conn_max_per_host, bool unified_host) + : conn_max_per_host_(conn_max_per_host == 0 + ? std::numeric_limits::max() + : conn_max_per_host), + unified_host_(unified_host) {} + +DownstreamQueue::~DownstreamQueue() { + dlist_delete_all(downstreams_); + for (auto &p : host_entries_) { + auto &ent = p.second; + dlist_delete_all(ent.blocked); + } +} + +void DownstreamQueue::add_pending(std::unique_ptr downstream) { + downstream->set_dispatch_state(Downstream::DISPATCH_PENDING); + downstreams_.append(downstream.release()); +} + +void DownstreamQueue::mark_failure(Downstream *downstream) { + downstream->set_dispatch_state(Downstream::DISPATCH_FAILURE); +} + +DownstreamQueue::HostEntry & +DownstreamQueue::find_host_entry(const std::string &host) { + auto itr = host_entries_.find(host); + if (itr == std::end(host_entries_)) { +#ifdef HAVE_STD_MAP_EMPLACE + std::tie(itr, std::ignore) = host_entries_.emplace(host, HostEntry()); +#else // !HAVE_STD_MAP_EMPLACE + // for g++-4.7 + std::tie(itr, std::ignore) = host_entries_.insert({host, HostEntry()}); +#endif // !HAVE_STD_MAP_EMPLACE + } + return (*itr).second; +} + +const std::string & +DownstreamQueue::make_host_key(const std::string &host) const { + static std::string empty_key; + return unified_host_ ? empty_key : host; +} + +const std::string & +DownstreamQueue::make_host_key(Downstream *downstream) const { + return make_host_key(downstream->get_request_http2_authority()); +} + +void DownstreamQueue::mark_active(Downstream *downstream) { + auto &ent = find_host_entry(make_host_key(downstream)); + ++ent.num_active; + + downstream->set_dispatch_state(Downstream::DISPATCH_ACTIVE); +} + +void DownstreamQueue::mark_blocked(Downstream *downstream) { + auto &ent = find_host_entry(make_host_key(downstream)); + + downstream->set_dispatch_state(Downstream::DISPATCH_BLOCKED); + + auto link = new BlockedLink{}; + downstream->attach_blocked_link(link); + ent.blocked.append(link); +} + +bool DownstreamQueue::can_activate(const std::string &host) const { + auto itr = host_entries_.find(make_host_key(host)); + if (itr == std::end(host_entries_)) { + return true; + } + auto &ent = (*itr).second; + return ent.num_active < conn_max_per_host_; +} + +namespace { +bool remove_host_entry_if_empty(const DownstreamQueue::HostEntry &ent, + DownstreamQueue::HostEntryMap &host_entries, + const std::string &host) { + if (ent.blocked.empty() && ent.num_active == 0) { + host_entries.erase(host); + return true; + } + return false; +} +} // namespace + +Downstream *DownstreamQueue::remove_and_get_blocked(Downstream *downstream) { + // Delete downstream when this function returns. + auto delptr = std::unique_ptr(downstream); + + if (downstream->get_dispatch_state() != Downstream::DISPATCH_ACTIVE) { + assert(downstream->get_dispatch_state() != Downstream::DISPATCH_NONE); + downstreams_.remove(downstream); + return nullptr; + } + + downstreams_.remove(downstream); + + auto &host = make_host_key(downstream); + auto &ent = find_host_entry(host); + --ent.num_active; + + if (remove_host_entry_if_empty(ent, host_entries_, host)) { + return nullptr; + } + + if (ent.num_active >= conn_max_per_host_) { + return nullptr; + } + + for (auto link = ent.blocked.head; link;) { + auto next = link->dlnext; + if (!link->downstream) { + ent.blocked.remove(link); + link = next; + continue; + } + auto next_downstream = link->downstream; + next_downstream->detach_blocked_link(link); + ent.blocked.remove(link); + delete link; + remove_host_entry_if_empty(ent, host_entries_, host); + return next_downstream; + } + return nullptr; +} + +Downstream *DownstreamQueue::get_downstreams() const { + return downstreams_.head; +} + +} // namespace shrpx diff --git a/src/shrpx_downstream_queue.h b/src/shrpx_downstream_queue.h new file mode 100644 index 0000000..d327a05 --- /dev/null +++ b/src/shrpx_downstream_queue.h @@ -0,0 +1,105 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef SHRPX_DOWNSTREAM_QUEUE_H +#define SHRPX_DOWNSTREAM_QUEUE_H + +#include "shrpx.h" + +#include +#include +#include +#include + +#include "template.h" + +using namespace nghttp2; + +namespace shrpx { + +class Downstream; + +// Link entry in HostEntry.blocked and downstream because downstream +// could be deleted in anytime and we'd like to find Downstream in +// O(1). Downstream has field to link back to this object. +struct BlockedLink { + Downstream *downstream; + BlockedLink *dlnext, *dlprev; +}; + +class DownstreamQueue { +public: + struct HostEntry { + // Set of stream ID that blocked by conn_max_per_host_. + DList blocked; + // The number of connections currently made to this host. + size_t num_active; + HostEntry(); + }; + + typedef std::map HostEntryMap; + + // conn_max_per_host == 0 means no limit for downstream connection. + DownstreamQueue(size_t conn_max_per_host = 0, bool unified_host = true); + ~DownstreamQueue(); + // Add |downstream| to this queue. This is entry point for + // Downstream object. + void add_pending(std::unique_ptr downstream); + // Set |downstream| to failure state, which means that downstream + // failed to connect to backend. + void mark_failure(Downstream *downstream); + // Set |downstream| to active state, which means that downstream + // connection has started. + void mark_active(Downstream *downstream); + // Set |downstream| to blocked state, which means that download + // connection was blocked because conn_max_per_host_ limit. + void mark_blocked(Downstream *downstream); + // Returns true if we can make downstream connection to given + // |host|. + bool can_activate(const std::string &host) const; + // Removes and frees |downstream| object. If |downstream| is in + // Downstream::DISPATCH_ACTIVE, this function may return Downstream + // object with the same target host in Downstream::DISPATCH_BLOCKED + // if its connection is now not blocked by conn_max_per_host_ limit. + Downstream *remove_and_get_blocked(Downstream *downstream); + Downstream *get_downstreams() const; + HostEntry &find_host_entry(const std::string &host); + const std::string &make_host_key(const std::string &host) const; + const std::string &make_host_key(Downstream *downstream) const; + +private: + // Per target host structure to keep track of the number of + // connections to the same host. + std::map host_entries_; + DList downstreams_; + // Maximum number of concurrent connections to the same host. + size_t conn_max_per_host_; + // true if downstream host is treated as the same. Used for reverse + // proxying. + bool unified_host_; +}; + +} // namespace shrpx + +#endif // SHRPX_DOWNSTREAM_QUEUE_H diff --git a/src/shrpx_downstream_test.cc b/src/shrpx_downstream_test.cc new file mode 100644 index 0000000..2e7a696 --- /dev/null +++ b/src/shrpx_downstream_test.cc @@ -0,0 +1,160 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2013 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "shrpx_downstream_test.h" + +#include + +#include + +#include "shrpx_downstream.h" + +namespace shrpx { + +void test_downstream_index_request_headers(void) { + Downstream d(nullptr, nullptr, 0, 0); + d.add_request_header("1", "0"); + d.add_request_header("2", "1"); + d.add_request_header("Charlie", "2"); + d.add_request_header("Alpha", "3"); + d.add_request_header("Delta", "4"); + d.add_request_header("BravO", "5"); + d.add_request_header(":method", "6"); + d.add_request_header(":authority", "7"); + d.index_request_headers(); + + auto ans = Headers{{"1", "0"}, + {"2", "1"}, + {"charlie", "2"}, + {"alpha", "3"}, + {"delta", "4"}, + {"bravo", "5"}, + {":method", "6"}, + {":authority", "7"}}; + CU_ASSERT(ans == d.get_request_headers()); +} + +void test_downstream_index_response_headers(void) { + Downstream d(nullptr, nullptr, 0, 0); + d.add_response_header("Charlie", "0"); + d.add_response_header("Alpha", "1"); + d.add_response_header("Delta", "2"); + d.add_response_header("BravO", "3"); + d.index_response_headers(); + + auto ans = + Headers{{"charlie", "0"}, {"alpha", "1"}, {"delta", "2"}, {"bravo", "3"}}; + CU_ASSERT(ans == d.get_response_headers()); +} + +void test_downstream_get_request_header(void) { + Downstream d(nullptr, nullptr, 0, 0); + d.add_request_header("alpha", "0"); + d.add_request_header(":authority", "1"); + d.add_request_header("content-length", "2"); + d.index_request_headers(); + + // By token + CU_ASSERT(Header(":authority", "1") == + *d.get_request_header(http2::HD__AUTHORITY)); + CU_ASSERT(nullptr == d.get_request_header(http2::HD__METHOD)); + + // By name + CU_ASSERT(Header("alpha", "0") == *d.get_request_header("alpha")); + CU_ASSERT(nullptr == d.get_request_header("bravo")); +} + +void test_downstream_get_response_header(void) { + Downstream d(nullptr, nullptr, 0, 0); + d.add_response_header("alpha", "0"); + d.add_response_header(":status", "1"); + d.add_response_header("content-length", "2"); + d.index_response_headers(); + + // By token + CU_ASSERT(Header(":status", "1") == + *d.get_response_header(http2::HD__STATUS)); + CU_ASSERT(nullptr == d.get_response_header(http2::HD__METHOD)); +} + +void test_downstream_crumble_request_cookie(void) { + Downstream d(nullptr, nullptr, 0, 0); + d.add_request_header(":method", "get"); + d.add_request_header(":path", "/"); + auto val = "alpha; bravo; ; ;; charlie;;"; + d.add_request_header( + reinterpret_cast("cookie"), sizeof("cookie") - 1, + reinterpret_cast(val), strlen(val), true, -1); + d.add_request_header("cookie", ";delta"); + d.add_request_header("cookie", "echo"); + auto cookies = d.crumble_request_cookie(); + + Headers ans = {{"cookie", "alpha"}, + {"cookie", "bravo"}, + {"cookie", "charlie"}, + {"cookie", "delta"}, + {"cookie", "echo"}}; + CU_ASSERT(ans == cookies); + CU_ASSERT(cookies[0].no_index); + CU_ASSERT(cookies[1].no_index); + CU_ASSERT(cookies[2].no_index); +} + +void test_downstream_assemble_request_cookie(void) { + Downstream d(nullptr, nullptr, 0, 0); + d.add_request_header(":method", "get"); + d.add_request_header(":path", "/"); + d.add_request_header("cookie", "alpha"); + d.add_request_header("cookie", "bravo;"); + d.add_request_header("cookie", "charlie; "); + d.add_request_header("cookie", "delta;;"); + d.assemble_request_cookie(); + CU_ASSERT("alpha; bravo; charlie; delta" == d.get_assembled_request_cookie()); +} + +void test_downstream_rewrite_location_response_header(void) { + { + Downstream d(nullptr, nullptr, 0, 0); + d.set_request_downstream_host("localhost:3000"); + d.add_request_header("host", "localhost"); + d.add_response_header("location", "http://localhost:3000/"); + d.index_request_headers(); + d.index_response_headers(); + d.rewrite_location_response_header("https"); + auto location = d.get_response_header(http2::HD_LOCATION); + CU_ASSERT("https://localhost/" == (*location).value); + } + { + Downstream d(nullptr, nullptr, 0, 0); + d.set_request_downstream_host("localhost"); + d.set_request_http2_authority("localhost"); + d.add_response_header("location", "http://localhost:3000/"); + d.index_response_headers(); + d.rewrite_location_response_header("https"); + auto location = d.get_response_header(http2::HD_LOCATION); + CU_ASSERT("https://localhost/" == (*location).value); + } +} + +} // namespace shrpx diff --git a/src/shrpx_downstream_test.h b/src/shrpx_downstream_test.h new file mode 100644 index 0000000..67e8f6b --- /dev/null +++ b/src/shrpx_downstream_test.h @@ -0,0 +1,44 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2013 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef SHRPX_DOWNSTREAM_TEST_H +#define SHRPX_DOWNSTREAM_TEST_H + +#ifdef HAVE_CONFIG_H +#include +#endif // HAVE_CONFIG_H + +namespace shrpx { + +void test_downstream_index_request_headers(void); +void test_downstream_index_response_headers(void); +void test_downstream_get_request_header(void); +void test_downstream_get_response_header(void); +void test_downstream_crumble_request_cookie(void); +void test_downstream_assemble_request_cookie(void); +void test_downstream_rewrite_location_response_header(void); + +} // namespace shrpx + +#endif // SHRPX_DOWNSTREAM_TEST_H diff --git a/src/shrpx_error.h b/src/shrpx_error.h new file mode 100644 index 0000000..f1d1bff --- /dev/null +++ b/src/shrpx_error.h @@ -0,0 +1,43 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef SHRPX_ERROR_H +#define SHRPX_ERROR_H + +#include "shrpx.h" + +namespace shrpx { + +// Deprecated, do not use. +enum ErrorCode { + SHRPX_ERR_SUCCESS = 0, + SHRPX_ERR_ERROR = -1, + SHRPX_ERR_NETWORK = -100, + SHRPX_ERR_EOF = -101, + SHRPX_ERR_INPROGRESS = -102, +}; + +} // namespace shrpx + +#endif // SHRPX_ERROR_H diff --git a/src/shrpx_http.cc b/src/shrpx_http.cc new file mode 100644 index 0000000..c3f1aa7 --- /dev/null +++ b/src/shrpx_http.cc @@ -0,0 +1,103 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "shrpx_http.h" + +#include "shrpx_config.h" +#include "shrpx_log.h" +#include "http2.h" +#include "util.h" + +using namespace nghttp2; + +namespace shrpx { + +namespace http { + +std::string create_error_html(unsigned int status_code) { + std::string res; + res.reserve(512); + auto status = http2::get_status_string(status_code); + res += R"()"; + res += status; + res += "

    "; + res += status; + res += "

    "; + res += get_config()->server_name; + res += " at port "; + res += util::utos(get_config()->port); + res += "
    "; + return res; +} + +std::string create_via_header_value(int major, int minor) { + std::string hdrs; + hdrs += static_cast(major + '0'); + if (major < 2) { + hdrs += "."; + hdrs += static_cast(minor + '0'); + } + hdrs += " nghttpx"; + return hdrs; +} + +std::string colorizeHeaders(const char *hdrs) { + std::string nhdrs; + const char *p = strchr(hdrs, '\n'); + if (!p) { + // Not valid HTTP header + return hdrs; + } + nhdrs.append(hdrs, p + 1); + ++p; + while (1) { + const char *np = strchr(p, ':'); + if (!np) { + nhdrs.append(p); + break; + } + nhdrs += TTY_HTTP_HD; + nhdrs.append(p, np); + nhdrs += TTY_RST; + p = np; + np = strchr(p, '\n'); + if (!np) { + nhdrs.append(p); + break; + } + nhdrs.append(p, np + 1); + p = np + 1; + } + return nhdrs; +} + +ssize_t select_padding_callback(nghttp2_session *session, + const nghttp2_frame *frame, size_t max_payload, + void *user_data) { + return std::min(max_payload, frame->hd.length + get_config()->padding); +} + +} // namespace http + +} // namespace shrpx diff --git a/src/shrpx_http.h b/src/shrpx_http.h new file mode 100644 index 0000000..65dbe0e --- /dev/null +++ b/src/shrpx_http.h @@ -0,0 +1,53 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef SHRPX_HTTP_H +#define SHRPX_HTTP_H + +#include "shrpx.h" + +#include + +#include + +namespace shrpx { + +namespace http { + +std::string create_error_html(unsigned int status_code); + +std::string create_via_header_value(int major, int minor); + +// Adds ANSI color codes to HTTP headers |hdrs|. +std::string colorizeHeaders(const char *hdrs); + +ssize_t select_padding_callback(nghttp2_session *session, + const nghttp2_frame *frame, size_t max_payload, + void *user_data); + +} // namespace http + +} // namespace shrpx + +#endif // SHRPX_HTTP_H diff --git a/src/shrpx_http2_downstream_connection.cc b/src/shrpx_http2_downstream_connection.cc new file mode 100644 index 0000000..f5cba83 --- /dev/null +++ b/src/shrpx_http2_downstream_connection.cc @@ -0,0 +1,569 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "shrpx_http2_downstream_connection.h" + +#ifdef HAVE_UNISTD_H +#include +#endif // HAVE_UNISTD_H + +#include "http-parser/http_parser.h" + +#include "shrpx_client_handler.h" +#include "shrpx_upstream.h" +#include "shrpx_downstream.h" +#include "shrpx_config.h" +#include "shrpx_error.h" +#include "shrpx_http.h" +#include "shrpx_http2_session.h" +#include "http2.h" +#include "util.h" + +using namespace nghttp2; + +namespace shrpx { + +Http2DownstreamConnection::Http2DownstreamConnection( + DownstreamConnectionPool *dconn_pool, Http2Session *http2session) + : DownstreamConnection(dconn_pool), dlnext(nullptr), dlprev(nullptr), + http2session_(http2session), sd_(nullptr) {} + +Http2DownstreamConnection::~Http2DownstreamConnection() { + if (LOG_ENABLED(INFO)) { + DCLOG(INFO, this) << "Deleting"; + } + if (downstream_) { + downstream_->disable_downstream_rtimer(); + downstream_->disable_downstream_wtimer(); + + uint32_t error_code; + if (downstream_->get_request_state() == Downstream::STREAM_CLOSED && + downstream_->get_upgraded()) { + // For upgraded connection, send NO_ERROR. Should we consider + // request states other than Downstream::STREAM_CLOSED ? + error_code = NGHTTP2_NO_ERROR; + } else { + error_code = NGHTTP2_INTERNAL_ERROR; + } + + if (downstream_->get_downstream_stream_id() != -1) { + if (LOG_ENABLED(INFO)) { + DCLOG(INFO, this) << "Submit RST_STREAM for DOWNSTREAM:" << downstream_ + << ", stream_id=" + << downstream_->get_downstream_stream_id() + << ", error_code=" << error_code; + } + + submit_rst_stream(downstream_, error_code); + + http2session_->consume(downstream_->get_downstream_stream_id(), + downstream_->get_response_datalen()); + + downstream_->reset_response_datalen(); + + http2session_->signal_write(); + } + } + http2session_->remove_downstream_connection(this); + // Downstream and DownstreamConnection may be deleted + // asynchronously. + if (downstream_) { + downstream_->release_downstream_connection(); + } + if (LOG_ENABLED(INFO)) { + DCLOG(INFO, this) << "Deleted"; + } +} + +int Http2DownstreamConnection::attach_downstream(Downstream *downstream) { + if (LOG_ENABLED(INFO)) { + DCLOG(INFO, this) << "Attaching to DOWNSTREAM:" << downstream; + } + http2session_->add_downstream_connection(this); + if (http2session_->get_state() == Http2Session::DISCONNECTED) { + http2session_->signal_write(); + } + + downstream_ = downstream; + downstream_->reset_downstream_rtimer(); + + return 0; +} + +void Http2DownstreamConnection::detach_downstream(Downstream *downstream) { + if (LOG_ENABLED(INFO)) { + DCLOG(INFO, this) << "Detaching from DOWNSTREAM:" << downstream; + } + if (submit_rst_stream(downstream) == 0) { + http2session_->signal_write(); + } + + if (downstream_->get_downstream_stream_id() != -1) { + http2session_->consume(downstream_->get_downstream_stream_id(), + downstream_->get_response_datalen()); + + downstream_->reset_response_datalen(); + + http2session_->signal_write(); + } + + downstream->disable_downstream_rtimer(); + downstream->disable_downstream_wtimer(); + downstream_ = nullptr; +} + +int Http2DownstreamConnection::submit_rst_stream(Downstream *downstream, + uint32_t error_code) { + int rv = -1; + if (http2session_->get_state() == Http2Session::CONNECTED && + downstream->get_downstream_stream_id() != -1) { + switch (downstream->get_response_state()) { + case Downstream::MSG_RESET: + case Downstream::MSG_BAD_HEADER: + case Downstream::MSG_COMPLETE: + break; + default: + if (LOG_ENABLED(INFO)) { + DCLOG(INFO, this) << "Submit RST_STREAM for DOWNSTREAM:" << downstream + << ", stream_id=" + << downstream->get_downstream_stream_id(); + } + rv = http2session_->submit_rst_stream( + downstream->get_downstream_stream_id(), error_code); + } + } + return rv; +} + +namespace { +ssize_t http2_data_read_callback(nghttp2_session *session, int32_t stream_id, + uint8_t *buf, size_t length, + uint32_t *data_flags, + nghttp2_data_source *source, void *user_data) { + int rv; + auto sd = static_cast( + nghttp2_session_get_stream_user_data(session, stream_id)); + if (!sd || !sd->dconn) { + return NGHTTP2_ERR_DEFERRED; + } + auto dconn = static_cast(source->ptr); + auto downstream = dconn->get_downstream(); + if (!downstream) { + // In this case, RST_STREAM should have been issued. But depending + // on the priority, DATA frame may come first. + return NGHTTP2_ERR_DEFERRED; + } + auto input = downstream->get_request_buf(); + auto nread = input->remove(buf, length); + auto input_empty = input->rleft() == 0; + + if (nread > 0) { + // This is important because it will handle flow control + // stuff. + if (downstream->get_upstream()->resume_read(SHRPX_NO_BUFFER, downstream, + nread) != 0) { + // In this case, downstream may be deleted. + return NGHTTP2_ERR_CALLBACK_FAILURE; + } + + // Check dconn is still alive because Upstream::resume_read() + // may delete downstream which will delete dconn. + if (sd->dconn == nullptr) { + return NGHTTP2_ERR_DEFERRED; + } + } + + if (input_empty && + downstream->get_request_state() == Downstream::MSG_COMPLETE && + // If connection is upgraded, don't set EOF flag, since HTTP/1 + // will set MSG_COMPLETE to request state after upgrade response + // header is seen. + (!downstream->get_upgrade_request() || + (downstream->get_response_state() == Downstream::HEADER_COMPLETE && + !downstream->get_upgraded()))) { + + *data_flags |= NGHTTP2_DATA_FLAG_EOF; + + auto &trailers = downstream->get_request_trailers(); + if (!trailers.empty()) { + std::vector nva; + nva.reserve(trailers.size()); + http2::copy_headers_to_nva(nva, trailers); + if (!nva.empty()) { + rv = nghttp2_submit_trailer(session, stream_id, nva.data(), nva.size()); + if (rv != 0) { + if (nghttp2_is_fatal(rv)) { + return NGHTTP2_ERR_CALLBACK_FAILURE; + } + } else { + *data_flags |= NGHTTP2_DATA_FLAG_NO_END_STREAM; + } + } + } + } + + if (!input_empty) { + downstream->reset_downstream_wtimer(); + } else { + downstream->disable_downstream_wtimer(); + } + + if (nread == 0 && (*data_flags & NGHTTP2_DATA_FLAG_EOF) == 0) { + downstream->disable_downstream_wtimer(); + + return NGHTTP2_ERR_DEFERRED; + } + + return nread; +} +} // namespace + +int Http2DownstreamConnection::push_request_headers() { + int rv; + if (!downstream_) { + return 0; + } + if (!http2session_->can_push_request()) { + // The HTTP2 session to the backend has not been established or + // connection is now being checked. This function will be called + // again just after it is established. + downstream_->set_request_pending(true); + http2session_->start_checking_connection(); + return 0; + } + + downstream_->set_request_pending(false); + + auto no_host_rewrite = get_config()->no_host_rewrite || + get_config()->http2_proxy || + get_config()->client_proxy || + downstream_->get_request_method() == "CONNECT"; + + // http2session_ has already in CONNECTED state, so we can get + // addr_idx here. + auto addr_idx = http2session_->get_addr_idx(); + auto downstream_hostport = + get_config()->downstream_addrs[addr_idx].hostport.get(); + + const char *authority = nullptr, *host = nullptr; + if (!no_host_rewrite) { + if (!downstream_->get_request_http2_authority().empty()) { + authority = downstream_hostport; + } + if (downstream_->get_request_header(http2::HD_HOST)) { + host = downstream_hostport; + } + } else { + if (!downstream_->get_request_http2_authority().empty()) { + authority = downstream_->get_request_http2_authority().c_str(); + } + auto h = downstream_->get_request_header(http2::HD_HOST); + if (h) { + host = h->value.c_str(); + } + } + + if (!authority && !host) { + // upstream is HTTP/1.0. We use backend server's host + // nonetheless. + host = downstream_hostport; + } + + if (authority) { + downstream_->set_request_downstream_host(authority); + } else { + downstream_->set_request_downstream_host(host); + } + + size_t nheader = downstream_->get_request_headers().size(); + + Headers cookies; + if (!get_config()->http2_no_cookie_crumbling) { + cookies = downstream_->crumble_request_cookie(); + } + + // 8 means: + // 1. :method + // 2. :scheme + // 3. :path + // 4. :authority or host (at least either of them exists) + // 5. via (optional) + // 6. x-forwarded-for (optional) + // 7. x-forwarded-proto (optional) + // 8. te (optional) + auto nva = std::vector(); + nva.reserve(nheader + 8 + cookies.size()); + + std::string via_value; + std::string xff_value; + std::string scheme, uri_authority, path, query; + + nva.push_back( + http2::make_nv_ls(":method", downstream_->get_request_method())); + + if (downstream_->get_request_method() == "CONNECT") { + if (authority) { + nva.push_back(http2::make_nv_lc(":authority", authority)); + } else { + nva.push_back( + http2::make_nv_ls(":authority", downstream_->get_request_path())); + } + } else { + if (!downstream_->get_request_http2_scheme().empty()) { + nva.push_back(http2::make_nv_ls(":scheme", + downstream_->get_request_http2_scheme())); + } else if (client_handler_->get_ssl()) { + nva.push_back(http2::make_nv_ll(":scheme", "https")); + } else { + nva.push_back(http2::make_nv_ll(":scheme", "http")); + } + + if (authority) { + nva.push_back(http2::make_nv_lc(":authority", authority)); + } + + nva.push_back(http2::make_nv_ls(":path", downstream_->get_request_path())); + } + + // only emit host header field if :authority is not emitted. They + // both must be the same value. + if (!authority && host) { + nva.push_back(http2::make_nv_lc("host", host)); + } + + http2::copy_headers_to_nva(nva, downstream_->get_request_headers()); + + bool chunked_encoding = false; + auto transfer_encoding = + downstream_->get_request_header(http2::HD_TRANSFER_ENCODING); + if (transfer_encoding && + util::strieq_l("chunked", (*transfer_encoding).value)) { + chunked_encoding = true; + } + + for (auto &nv : cookies) { + nva.push_back(http2::make_nv(nv.name, nv.value, nv.no_index)); + } + + auto xff = downstream_->get_request_header(http2::HD_X_FORWARDED_FOR); + if (get_config()->add_x_forwarded_for) { + if (xff && !get_config()->strip_incoming_x_forwarded_for) { + xff_value = (*xff).value; + xff_value += ", "; + } + xff_value += + downstream_->get_upstream()->get_client_handler()->get_ipaddr(); + nva.push_back(http2::make_nv_ls("x-forwarded-for", xff_value)); + } else if (xff && !get_config()->strip_incoming_x_forwarded_for) { + nva.push_back(http2::make_nv_ls("x-forwarded-for", (*xff).value)); + } + + if (!get_config()->http2_proxy && !get_config()->client_proxy && + downstream_->get_request_method() != "CONNECT") { + // We use same protocol with :scheme header field + if (scheme.empty()) { + if (client_handler_->get_ssl()) { + nva.push_back(http2::make_nv_ll("x-forwarded-proto", "https")); + } else { + nva.push_back(http2::make_nv_ll("x-forwarded-proto", "http")); + } + } else { + nva.push_back(http2::make_nv_ls("x-forwarded-proto", scheme)); + } + } + + auto via = downstream_->get_request_header(http2::HD_VIA); + if (get_config()->no_via) { + if (via) { + nva.push_back(http2::make_nv_ls("via", (*via).value)); + } + } else { + if (via) { + via_value = (*via).value; + via_value += ", "; + } + via_value += http::create_via_header_value( + downstream_->get_request_major(), downstream_->get_request_minor()); + nva.push_back(http2::make_nv_ls("via", via_value)); + } + + auto te = downstream_->get_request_header(http2::HD_TE); + // HTTP/1 upstream request can contain keyword other than + // "trailers". We just forward "trailers". + // TODO more strict handling required here. + if (te && util::strifind(te->value.c_str(), "trailers")) { + nva.push_back(http2::make_nv_ll("te", "trailers")); + } + + if (LOG_ENABLED(INFO)) { + std::stringstream ss; + for (auto &nv : nva) { + ss << TTY_HTTP_HD << nv.name << TTY_RST << ": " << nv.value << "\n"; + } + DCLOG(INFO, this) << "HTTP request headers\n" << ss.str(); + } + + auto content_length = + downstream_->get_request_header(http2::HD_CONTENT_LENGTH); + // TODO check content-length: 0 case + + if (downstream_->get_request_method() == "CONNECT" || chunked_encoding || + content_length || downstream_->get_request_http2_expect_body()) { + // Request-body is expected. + nghttp2_data_provider data_prd; + data_prd.source.ptr = this; + data_prd.read_callback = http2_data_read_callback; + rv = http2session_->submit_request(this, downstream_->get_priority(), + nva.data(), nva.size(), &data_prd); + } else { + rv = http2session_->submit_request(this, downstream_->get_priority(), + nva.data(), nva.size(), nullptr); + } + if (rv != 0) { + DCLOG(FATAL, this) << "nghttp2_submit_request() failed"; + return -1; + } + + downstream_->reset_downstream_wtimer(); + + http2session_->signal_write(); + return 0; +} + +int Http2DownstreamConnection::push_upload_data_chunk(const uint8_t *data, + size_t datalen) { + int rv; + auto output = downstream_->get_request_buf(); + output->append(data, datalen); + if (downstream_->get_downstream_stream_id() != -1) { + rv = http2session_->resume_data(this); + if (rv != 0) { + return -1; + } + + downstream_->ensure_downstream_wtimer(); + + http2session_->signal_write(); + } + return 0; +} + +int Http2DownstreamConnection::end_upload_data() { + int rv; + if (downstream_->get_downstream_stream_id() != -1) { + rv = http2session_->resume_data(this); + if (rv != 0) { + return -1; + } + + downstream_->ensure_downstream_wtimer(); + + http2session_->signal_write(); + } + return 0; +} + +int Http2DownstreamConnection::resume_read(IOCtrlReason reason, + size_t consumed) { + int rv; + + if (http2session_->get_state() != Http2Session::CONNECTED || + !http2session_->get_flow_control()) { + return 0; + } + + if (!downstream_ || downstream_->get_downstream_stream_id() == -1) { + return 0; + } + + if (consumed > 0) { + assert(downstream_->get_response_datalen() >= consumed); + + rv = http2session_->consume(downstream_->get_downstream_stream_id(), + consumed); + + if (rv != 0) { + return -1; + } + + downstream_->dec_response_datalen(consumed); + + http2session_->signal_write(); + } + + return 0; +} + +int Http2DownstreamConnection::on_read() { return 0; } + +int Http2DownstreamConnection::on_write() { return 0; } + +void Http2DownstreamConnection::attach_stream_data(StreamData *sd) { + // It is possible sd->dconn is not NULL. sd is detached when + // on_stream_close_callback. Before that, after MSG_COMPLETE is set + // to Downstream::set_response_state(), upstream's readcb is called + // and execution path eventually could reach here. Since the + // response was already handled, we just detach sd. + detach_stream_data(); + sd_ = sd; + sd_->dconn = this; +} + +StreamData *Http2DownstreamConnection::detach_stream_data() { + if (sd_) { + auto sd = sd_; + sd_ = nullptr; + sd->dconn = nullptr; + return sd; + } + return nullptr; +} + +int Http2DownstreamConnection::on_priority_change(int32_t pri) { + int rv; + if (downstream_->get_priority() == pri) { + return 0; + } + downstream_->set_priority(pri); + if (http2session_->get_state() != Http2Session::CONNECTED) { + return 0; + } + rv = http2session_->submit_priority(this, pri); + if (rv != 0) { + DLOG(FATAL, this) << "nghttp2_submit_priority() failed"; + return -1; + } + http2session_->signal_write(); + return 0; +} + +int Http2DownstreamConnection::on_timeout() { + if (!downstream_) { + return 0; + } + + return submit_rst_stream(downstream_, NGHTTP2_NO_ERROR); +} + +} // namespace shrpx diff --git a/src/shrpx_http2_downstream_connection.h b/src/shrpx_http2_downstream_connection.h new file mode 100644 index 0000000..a2a78a3 --- /dev/null +++ b/src/shrpx_http2_downstream_connection.h @@ -0,0 +1,86 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef SHRPX_HTTP2_DOWNSTREAM_CONNECTION_H +#define SHRPX_HTTP2_DOWNSTREAM_CONNECTION_H + +#include "shrpx.h" + +#include + +#include + +#include "shrpx_downstream_connection.h" + +namespace shrpx { + +struct StreamData; +class Http2Session; +class DownstreamConnectionPool; + +class Http2DownstreamConnection : public DownstreamConnection { +public: + Http2DownstreamConnection(DownstreamConnectionPool *dconn_pool, + Http2Session *http2session); + virtual ~Http2DownstreamConnection(); + virtual int attach_downstream(Downstream *downstream); + virtual void detach_downstream(Downstream *downstream); + + virtual int push_request_headers(); + virtual int push_upload_data_chunk(const uint8_t *data, size_t datalen); + virtual int end_upload_data(); + + virtual void pause_read(IOCtrlReason reason) {} + virtual int resume_read(IOCtrlReason reason, size_t consumed); + virtual void force_resume_read() {} + + virtual int on_read(); + virtual int on_write(); + virtual int on_timeout(); + + virtual void on_upstream_change(Upstream *upstream) {} + virtual int on_priority_change(int32_t pri); + + // This object is not poolable because we dont' have facility to + // migrate to another Http2Session object. + virtual bool poolable() const { return false; } + + int send(); + + void attach_stream_data(StreamData *sd); + StreamData *detach_stream_data(); + + int submit_rst_stream(Downstream *downstream, + uint32_t error_code = NGHTTP2_INTERNAL_ERROR); + + Http2DownstreamConnection *dlnext, *dlprev; + +private: + Http2Session *http2session_; + StreamData *sd_; +}; + +} // namespace shrpx + +#endif // SHRPX_HTTP2_DOWNSTREAM_CONNECTION_H diff --git a/src/shrpx_http2_session.cc b/src/shrpx_http2_session.cc new file mode 100644 index 0000000..c071a72 --- /dev/null +++ b/src/shrpx_http2_session.cc @@ -0,0 +1,1747 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "shrpx_http2_session.h" + +#include +#ifdef HAVE_UNISTD_H +#include +#endif // HAVE_UNISTD_H + +#include + +#include + +#include "shrpx_upstream.h" +#include "shrpx_downstream.h" +#include "shrpx_config.h" +#include "shrpx_error.h" +#include "shrpx_http2_downstream_connection.h" +#include "shrpx_client_handler.h" +#include "shrpx_ssl.h" +#include "shrpx_http.h" +#include "shrpx_worker.h" +#include "shrpx_connect_blocker.h" +#include "http2.h" +#include "util.h" +#include "base64.h" + +using namespace nghttp2; + +namespace shrpx { + +namespace { +const ev_tstamp CONNCHK_TIMEOUT = 5.; +const ev_tstamp CONNCHK_PING_TIMEOUT = 1.; +} // namespace + +namespace { +void connchk_timeout_cb(struct ev_loop *loop, ev_timer *w, int revents) { + auto http2session = static_cast(w->data); + + ev_timer_stop(loop, w); + + switch (http2session->get_connection_check_state()) { + case Http2Session::CONNECTION_CHECK_STARTED: + // ping timeout; disconnect + if (LOG_ENABLED(INFO)) { + SSLOG(INFO, http2session) << "ping timeout"; + } + http2session->disconnect(); + return; + default: + if (LOG_ENABLED(INFO)) { + SSLOG(INFO, http2session) << "connection check required"; + } + http2session->set_connection_check_state( + Http2Session::CONNECTION_CHECK_REQUIRED); + } +} +} // namespace + +namespace { +void settings_timeout_cb(struct ev_loop *loop, ev_timer *w, int revents) { + auto http2session = static_cast(w->data); + http2session->stop_settings_timer(); + SSLOG(INFO, http2session) << "SETTINGS timeout"; + if (http2session->terminate_session(NGHTTP2_SETTINGS_TIMEOUT) != 0) { + http2session->disconnect(); + return; + } + http2session->signal_write(); +} +} // namespace + +namespace { +void timeoutcb(struct ev_loop *loop, ev_timer *w, int revents) { + auto conn = static_cast(w->data); + auto http2session = static_cast(conn->data); + + if (LOG_ENABLED(INFO)) { + SSLOG(INFO, http2session) << "Timeout"; + } + + http2session->disconnect(http2session->get_state() == + Http2Session::CONNECTING); +} +} // namespace + +namespace { +void readcb(struct ev_loop *loop, ev_io *w, int revents) { + int rv; + auto conn = static_cast(w->data); + auto http2session = static_cast(conn->data); + rv = http2session->do_read(); + if (rv != 0) { + http2session->disconnect(http2session->should_hard_fail()); + return; + } + http2session->connection_alive(); + + rv = http2session->do_write(); + if (rv != 0) { + http2session->disconnect(http2session->should_hard_fail()); + return; + } +} +} // namespace + +namespace { +void writecb(struct ev_loop *loop, ev_io *w, int revents) { + int rv; + auto conn = static_cast(w->data); + auto http2session = static_cast(conn->data); + rv = http2session->do_write(); + if (rv != 0) { + http2session->disconnect(http2session->should_hard_fail()); + return; + } + http2session->reset_connection_check_timer_if_not_checking(); +} +} // namespace + +Http2Session::Http2Session(struct ev_loop *loop, SSL_CTX *ssl_ctx, + ConnectBlocker *connect_blocker, Worker *worker) + : conn_(loop, -1, nullptr, get_config()->downstream_write_timeout, + get_config()->downstream_read_timeout, 0, 0, 0, 0, writecb, readcb, + timeoutcb, this), + worker_(worker), connect_blocker_(connect_blocker), ssl_ctx_(ssl_ctx), + session_(nullptr), data_pending_(nullptr), data_pendinglen_(0), + addr_idx_(0), state_(DISCONNECTED), + connection_check_state_(CONNECTION_CHECK_NONE), flow_control_(false) { + + read_ = write_ = &Http2Session::noop; + on_read_ = on_write_ = &Http2Session::noop; + + // We will resuse this many times, so use repeat timeout value. The + // timeout value is set later. + ev_timer_init(&connchk_timer_, connchk_timeout_cb, 0., 0.); + + connchk_timer_.data = this; + + // SETTINGS ACK timeout is 10 seconds for now. We will resuse this + // many times, so use repeat timeout value. + ev_timer_init(&settings_timer_, settings_timeout_cb, 0., 10.); + + settings_timer_.data = this; +} + +Http2Session::~Http2Session() { disconnect(); } + +int Http2Session::disconnect(bool hard) { + if (LOG_ENABLED(INFO)) { + SSLOG(INFO, this) << "Disconnecting"; + } + nghttp2_session_del(session_); + session_ = nullptr; + + rb_.reset(); + wb_.reset(); + + conn_.rlimit.stopw(); + conn_.wlimit.stopw(); + + ev_timer_stop(conn_.loop, &settings_timer_); + ev_timer_stop(conn_.loop, &connchk_timer_); + + read_ = write_ = &Http2Session::noop; + on_read_ = on_write_ = &Http2Session::noop; + + conn_.disconnect(); + + addr_idx_ = 0; + + if (proxy_htp_) { + proxy_htp_.reset(); + } + + connection_check_state_ = CONNECTION_CHECK_NONE; + state_ = DISCONNECTED; + + // Delete all client handler associated to Downstream. When deleting + // Http2DownstreamConnection, it calls this object's + // remove_downstream_connection(). The multiple + // Http2DownstreamConnection objects belong to the same + // ClientHandler object. So first dump ClientHandler objects. We + // want to allow creating new pending Http2DownstreamConnection with + // this object. In order to achieve this, we first swap dconns_ and + // streams_. Upstream::on_downstream_reset() may add + // Http2DownstreamConnection. + auto dconns = std::move(dconns_); + auto streams = std::move(streams_); + + std::set handlers; + for (auto dc = dconns.head; dc; dc = dc->dlnext) { + if (!dc->get_client_handler()) { + continue; + } + handlers.insert(dc->get_client_handler()); + } + for (auto h : handlers) { + if (h->get_upstream()->on_downstream_reset(hard) != 0) { + delete h; + } + } + + for (auto s = streams.head; s;) { + auto next = s->dlnext; + delete s; + s = next; + } + + return 0; +} + +int Http2Session::check_cert() { return ssl::check_cert(conn_.tls.ssl); } + +int Http2Session::initiate_connection() { + int rv = 0; + + if (state_ == DISCONNECTED) { + if (connect_blocker_->blocked()) { + if (LOG_ENABLED(INFO)) { + DCLOG(INFO, this) + << "Downstream connection was blocked by connect_blocker"; + } + return -1; + } + + auto worker_stat = worker_->get_worker_stat(); + addr_idx_ = worker_stat->next_downstream; + ++worker_stat->next_downstream; + worker_stat->next_downstream %= get_config()->downstream_addrs.size(); + + if (LOG_ENABLED(INFO)) { + SSLOG(INFO, this) << "Using downstream address idx=" << addr_idx_ + << " out of " << get_config()->downstream_addrs.size(); + } + } + + auto &downstream_addr = get_config()->downstream_addrs[addr_idx_]; + + if (get_config()->downstream_http_proxy_host && state_ == DISCONNECTED) { + if (LOG_ENABLED(INFO)) { + SSLOG(INFO, this) << "Connecting to the proxy " + << get_config()->downstream_http_proxy_host.get() << ":" + << get_config()->downstream_http_proxy_port; + } + + conn_.fd = util::create_nonblock_socket( + get_config()->downstream_http_proxy_addr.storage.ss_family); + + if (conn_.fd == -1) { + connect_blocker_->on_failure(); + return -1; + } + + rv = connect(conn_.fd, &get_config()->downstream_http_proxy_addr.sa, + get_config()->downstream_http_proxy_addrlen); + if (rv != 0 && errno != EINPROGRESS) { + SSLOG(ERROR, this) << "Failed to connect to the proxy " + << get_config()->downstream_http_proxy_host.get() + << ":" << get_config()->downstream_http_proxy_port; + connect_blocker_->on_failure(); + return -1; + } + + ev_io_set(&conn_.rev, conn_.fd, EV_READ); + ev_io_set(&conn_.wev, conn_.fd, EV_WRITE); + + conn_.wlimit.startw(); + + // TODO we should have timeout for connection establishment + ev_timer_again(conn_.loop, &conn_.wt); + + write_ = &Http2Session::connected; + + on_read_ = &Http2Session::downstream_read_proxy; + on_write_ = &Http2Session::downstream_connect_proxy; + + proxy_htp_ = make_unique(); + http_parser_init(proxy_htp_.get(), HTTP_RESPONSE); + proxy_htp_->data = this; + + state_ = PROXY_CONNECTING; + + return 0; + } + + if (state_ == DISCONNECTED || state_ == PROXY_CONNECTED) { + if (LOG_ENABLED(INFO)) { + SSLOG(INFO, this) << "Connecting to downstream server"; + } + if (ssl_ctx_) { + // We are establishing TLS connection. + conn_.tls.ssl = SSL_new(ssl_ctx_); + if (!conn_.tls.ssl) { + SSLOG(ERROR, this) << "SSL_new() failed: " + << ERR_error_string(ERR_get_error(), NULL); + return -1; + } + + const char *sni_name = nullptr; + if (get_config()->backend_tls_sni_name) { + sni_name = get_config()->backend_tls_sni_name.get(); + } else { + sni_name = downstream_addr.host.get(); + } + + if (sni_name && !util::numeric_host(sni_name)) { + // TLS extensions: SNI. There is no documentation about the return + // code for this function (actually this is macro wrapping SSL_ctrl + // at the time of this writing). + SSL_set_tlsext_host_name(conn_.tls.ssl, sni_name); + } + // If state_ == PROXY_CONNECTED, we has connected to the proxy + // using conn_.fd and tunnel has been established. + if (state_ == DISCONNECTED) { + assert(conn_.fd == -1); + + conn_.fd = util::create_nonblock_socket( + downstream_addr.addr.storage.ss_family); + if (conn_.fd == -1) { + connect_blocker_->on_failure(); + return -1; + } + + rv = connect(conn_.fd, + // TODO maybe not thread-safe? + const_cast(&downstream_addr.addr.sa), + downstream_addr.addrlen); + if (rv != 0 && errno != EINPROGRESS) { + connect_blocker_->on_failure(); + return -1; + } + + ev_io_set(&conn_.rev, conn_.fd, EV_READ); + ev_io_set(&conn_.wev, conn_.fd, EV_WRITE); + } + + if (SSL_set_fd(conn_.tls.ssl, conn_.fd) == 0) { + return -1; + } + + SSL_set_connect_state(conn_.tls.ssl); + } else { + if (state_ == DISCONNECTED) { + // Without TLS and proxy. + assert(conn_.fd == -1); + + conn_.fd = util::create_nonblock_socket( + downstream_addr.addr.storage.ss_family); + + if (conn_.fd == -1) { + connect_blocker_->on_failure(); + return -1; + } + + rv = connect(conn_.fd, const_cast(&downstream_addr.addr.sa), + downstream_addr.addrlen); + if (rv != 0 && errno != EINPROGRESS) { + connect_blocker_->on_failure(); + return -1; + } + + ev_io_set(&conn_.rev, conn_.fd, EV_READ); + ev_io_set(&conn_.wev, conn_.fd, EV_WRITE); + } + } + + write_ = &Http2Session::connected; + + on_write_ = &Http2Session::downstream_write; + on_read_ = &Http2Session::downstream_read; + + // We have been already connected when no TLS and proxy is used. + if (state_ != CONNECTED) { + state_ = CONNECTING; + conn_.wlimit.startw(); + // TODO we should have timeout for connection establishment + ev_timer_again(conn_.loop, &conn_.wt); + } else { + conn_.rlimit.startw(); + ev_timer_again(conn_.loop, &conn_.rt); + } + + return 0; + } + + // Unreachable + DIE(); + return 0; +} + +namespace { +int htp_hdrs_completecb(http_parser *htp) { + auto http2session = static_cast(htp->data); + + // We only read HTTP header part. If tunneling succeeds, response + // body is a different protocol (HTTP/2 in this case), we don't read + // them here. + // + // Here is a caveat: http-parser returns 1 less bytes if we pause + // here. The reason why they do this is probably they want to eat + // last 1 byte in s_headers_done state, on the other hand, this + // callback is called its previous state s_headers_almost_done. We + // will do "+ 1" to the return value to workaround this. + http_parser_pause(htp, 1); + + // We just check status code here + if (htp->status_code == 200) { + if (LOG_ENABLED(INFO)) { + SSLOG(INFO, http2session) << "Tunneling success"; + } + http2session->set_state(Http2Session::PROXY_CONNECTED); + + return 0; + } + + SSLOG(WARN, http2session) << "Tunneling failed: " << htp->status_code; + http2session->set_state(Http2Session::PROXY_FAILED); + + return 0; +} +} // namespace + +namespace { +http_parser_settings htp_hooks = { + nullptr, // http_cb on_message_begin; + nullptr, // http_data_cb on_url; + nullptr, // http_data_cb on_status; + nullptr, // http_data_cb on_header_field; + nullptr, // http_data_cb on_header_value; + htp_hdrs_completecb, // http_cb on_headers_complete; + nullptr, // http_data_cb on_body; + nullptr // http_cb on_message_complete; +}; +} // namespace + +int Http2Session::downstream_read_proxy() { + if (rb_.rleft() == 0) { + return 0; + } + + size_t nread = + http_parser_execute(proxy_htp_.get(), &htp_hooks, + reinterpret_cast(rb_.pos), rb_.rleft()); + + rb_.drain(nread); + + auto htperr = HTTP_PARSER_ERRNO(proxy_htp_.get()); + + if (htperr == HPE_PAUSED) { + switch (state_) { + case Http2Session::PROXY_CONNECTED: + // we need to increment nread by 1 since http_parser_execute() + // returns 1 less value we expect. This means taht + // rb_.pos[nread] points to \x0a (LF), which is last byte of + // empty line to terminate headers. We want to eat that byte + // here. + rb_.drain(1); + + // Initiate SSL/TLS handshake through established tunnel. + if (initiate_connection() != 0) { + return -1; + } + return 0; + case Http2Session::PROXY_FAILED: + return -1; + } + // should not be here + assert(0); + } + + if (htperr != HPE_OK) { + return -1; + } + + return 0; +} + +int Http2Session::downstream_connect_proxy() { + if (LOG_ENABLED(INFO)) { + SSLOG(INFO, this) << "Connected to the proxy"; + } + auto &downstream_addr = get_config()->downstream_addrs[addr_idx_]; + + std::string req = "CONNECT "; + req += downstream_addr.hostport.get(); + if (downstream_addr.port == 80 || downstream_addr.port == 443) { + req += ":"; + req += util::utos(downstream_addr.port); + } + req += " HTTP/1.1\r\nHost: "; + req += downstream_addr.host.get(); + req += "\r\n"; + if (get_config()->downstream_http_proxy_userinfo) { + req += "Proxy-Authorization: Basic "; + size_t len = strlen(get_config()->downstream_http_proxy_userinfo.get()); + req += base64::encode(get_config()->downstream_http_proxy_userinfo.get(), + get_config()->downstream_http_proxy_userinfo.get() + + len); + req += "\r\n"; + } + req += "\r\n"; + if (LOG_ENABLED(INFO)) { + SSLOG(INFO, this) << "HTTP proxy request headers\n" << req; + } + auto nwrite = wb_.write(req.c_str(), req.size()); + if (nwrite != req.size()) { + SSLOG(WARN, this) << "HTTP proxy request is too large"; + return -1; + } + on_write_ = &Http2Session::noop; + + signal_write(); + return 0; +} + +void Http2Session::add_downstream_connection(Http2DownstreamConnection *dconn) { + dconns_.append(dconn); +} + +void +Http2Session::remove_downstream_connection(Http2DownstreamConnection *dconn) { + dconns_.remove(dconn); + dconn->detach_stream_data(); +} + +void Http2Session::remove_stream_data(StreamData *sd) { + streams_.remove(sd); + if (sd->dconn) { + sd->dconn->detach_stream_data(); + } + delete sd; +} + +int Http2Session::submit_request(Http2DownstreamConnection *dconn, int32_t pri, + const nghttp2_nv *nva, size_t nvlen, + const nghttp2_data_provider *data_prd) { + assert(state_ == CONNECTED); + auto sd = make_unique(); + sd->dlnext = sd->dlprev = nullptr; + // TODO Specify nullptr to pri_spec for now + auto stream_id = + nghttp2_submit_request(session_, nullptr, nva, nvlen, data_prd, sd.get()); + if (stream_id < 0) { + SSLOG(FATAL, this) << "nghttp2_submit_request() failed: " + << nghttp2_strerror(stream_id); + return -1; + } + + dconn->attach_stream_data(sd.get()); + dconn->get_downstream()->set_downstream_stream_id(stream_id); + streams_.append(sd.release()); + + return 0; +} + +int Http2Session::submit_rst_stream(int32_t stream_id, uint32_t error_code) { + assert(state_ == CONNECTED); + if (LOG_ENABLED(INFO)) { + SSLOG(INFO, this) << "RST_STREAM stream_id=" << stream_id + << " with error_code=" << error_code; + } + int rv = nghttp2_submit_rst_stream(session_, NGHTTP2_FLAG_NONE, stream_id, + error_code); + if (rv != 0) { + SSLOG(FATAL, this) << "nghttp2_submit_rst_stream() failed: " + << nghttp2_strerror(rv); + return -1; + } + return 0; +} + +int Http2Session::submit_priority(Http2DownstreamConnection *dconn, + int32_t pri) { + assert(state_ == CONNECTED); + if (!dconn) { + return 0; + } + int rv; + + // TODO Disabled temporarily + + // rv = nghttp2_submit_priority(session_, NGHTTP2_FLAG_NONE, + // dconn->get_downstream()-> + // get_downstream_stream_id(), pri); + + rv = 0; + + if (rv < NGHTTP2_ERR_FATAL) { + SSLOG(FATAL, this) << "nghttp2_submit_priority() failed: " + << nghttp2_strerror(rv); + return -1; + } + return 0; +} + +nghttp2_session *Http2Session::get_session() const { return session_; } + +bool Http2Session::get_flow_control() const { return flow_control_; } + +int Http2Session::resume_data(Http2DownstreamConnection *dconn) { + assert(state_ == CONNECTED); + auto downstream = dconn->get_downstream(); + int rv = nghttp2_session_resume_data(session_, + downstream->get_downstream_stream_id()); + switch (rv) { + case 0: + case NGHTTP2_ERR_INVALID_ARGUMENT: + return 0; + default: + SSLOG(FATAL, this) << "nghttp2_resume_session() failed: " + << nghttp2_strerror(rv); + return -1; + } +} + +namespace { +void call_downstream_readcb(Http2Session *http2session, + Downstream *downstream) { + auto upstream = downstream->get_upstream(); + if (!upstream) { + return; + } + if (upstream->downstream_read(downstream->get_downstream_connection()) != 0) { + delete upstream->get_client_handler(); + } +} +} // namespace + +namespace { +int on_stream_close_callback(nghttp2_session *session, int32_t stream_id, + uint32_t error_code, void *user_data) { + auto http2session = static_cast(user_data); + if (LOG_ENABLED(INFO)) { + SSLOG(INFO, http2session) << "Stream stream_id=" << stream_id + << " is being closed with error code " + << error_code; + } + auto sd = static_cast( + nghttp2_session_get_stream_user_data(session, stream_id)); + if (sd == 0) { + // We might get this close callback when pushed streams are + // closed. + return 0; + } + auto dconn = sd->dconn; + if (dconn) { + auto downstream = dconn->get_downstream(); + if (downstream && downstream->get_downstream_stream_id() == stream_id) { + + if (downstream->get_upgraded() && + downstream->get_response_state() == Downstream::HEADER_COMPLETE) { + // For tunneled connection, we have to submit RST_STREAM to + // upstream *after* whole response body is sent. We just set + // MSG_COMPLETE here. Upstream will take care of that. + downstream->get_upstream()->on_downstream_body_complete(downstream); + downstream->set_response_state(Downstream::MSG_COMPLETE); + } else if (error_code == NGHTTP2_NO_ERROR) { + switch (downstream->get_response_state()) { + case Downstream::MSG_COMPLETE: + case Downstream::MSG_BAD_HEADER: + break; + default: + downstream->set_response_state(Downstream::MSG_RESET); + } + } else if (downstream->get_response_state() != + Downstream::MSG_BAD_HEADER) { + downstream->set_response_state(Downstream::MSG_RESET); + } + if (downstream->get_response_state() == Downstream::MSG_RESET && + downstream->get_response_rst_stream_error_code() == + NGHTTP2_NO_ERROR) { + downstream->set_response_rst_stream_error_code(error_code); + } + call_downstream_readcb(http2session, downstream); + // dconn may be deleted + } + } + // The life time of StreamData ends here + http2session->remove_stream_data(sd); + return 0; +} +} // namespace + +void Http2Session::start_settings_timer() { + ev_timer_again(conn_.loop, &settings_timer_); +} + +void Http2Session::stop_settings_timer() { + ev_timer_stop(conn_.loop, &settings_timer_); +} + +namespace { +int on_header_callback(nghttp2_session *session, const nghttp2_frame *frame, + const uint8_t *name, size_t namelen, + const uint8_t *value, size_t valuelen, uint8_t flags, + void *user_data) { + auto sd = static_cast( + nghttp2_session_get_stream_user_data(session, frame->hd.stream_id)); + if (!sd || !sd->dconn) { + return 0; + } + auto downstream = sd->dconn->get_downstream(); + if (!downstream) { + return 0; + } + + if (frame->hd.type != NGHTTP2_HEADERS) { + return 0; + } + + auto trailer = frame->headers.cat != NGHTTP2_HCAT_RESPONSE && + !downstream->get_expect_final_response(); + + if (downstream->get_response_headers_sum() + namelen + valuelen > + get_config()->header_field_buffer || + downstream->get_response_headers().size() >= + get_config()->max_header_fields) { + if (LOG_ENABLED(INFO)) { + DLOG(INFO, downstream) + << "Too large or many header field size=" + << downstream->get_response_headers_sum() + namelen + valuelen + << ", num=" << downstream->get_response_headers().size() + 1; + } + + if (trailer) { + // we don't care trailer part exceeds header size limit; just + // discard it. + return 0; + } + + return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; + } + + if (trailer) { + // just store header fields for trailer part + downstream->add_response_trailer(name, namelen, value, valuelen, + flags & NGHTTP2_NV_FLAG_NO_INDEX, -1); + return 0; + } + + auto token = http2::lookup_token(name, namelen); + + downstream->add_response_header(name, namelen, value, valuelen, + flags & NGHTTP2_NV_FLAG_NO_INDEX, token); + return 0; +} +} // namespace + +namespace { +int on_begin_headers_callback(nghttp2_session *session, + const nghttp2_frame *frame, void *user_data) { + auto http2session = static_cast(user_data); + if (frame->hd.type != NGHTTP2_HEADERS || + frame->headers.cat != NGHTTP2_HCAT_RESPONSE) { + return 0; + } + auto sd = static_cast( + nghttp2_session_get_stream_user_data(session, frame->hd.stream_id)); + if (!sd || !sd->dconn) { + http2session->submit_rst_stream(frame->hd.stream_id, + NGHTTP2_INTERNAL_ERROR); + return 0; + } + auto downstream = sd->dconn->get_downstream(); + if (!downstream || + downstream->get_downstream_stream_id() != frame->hd.stream_id) { + http2session->submit_rst_stream(frame->hd.stream_id, + NGHTTP2_INTERNAL_ERROR); + return 0; + } + return 0; +} +} // namespace + +namespace { +int on_response_headers(Http2Session *http2session, Downstream *downstream, + nghttp2_session *session, const nghttp2_frame *frame) { + int rv; + + auto upstream = downstream->get_upstream(); + + auto &nva = downstream->get_response_headers(); + + downstream->set_expect_final_response(false); + + auto status = downstream->get_response_header(http2::HD__STATUS); + // libnghttp2 guarantees this exists and can be parsed + auto status_code = http2::parse_http_status_code(status->value); + + downstream->set_response_http_status(status_code); + downstream->set_response_major(2); + downstream->set_response_minor(0); + + if (LOG_ENABLED(INFO)) { + std::stringstream ss; + for (auto &nv : nva) { + ss << TTY_HTTP_HD << nv.name << TTY_RST << ": " << nv.value << "\n"; + } + SSLOG(INFO, http2session) + << "HTTP response headers. stream_id=" << frame->hd.stream_id << "\n" + << ss.str(); + } + + if (downstream->get_non_final_response()) { + + if (LOG_ENABLED(INFO)) { + SSLOG(INFO, http2session) << "This is non-final response."; + } + + downstream->set_expect_final_response(true); + rv = upstream->on_downstream_header_complete(downstream); + + // Now Dowstream's response headers are erased. + + if (rv != 0) { + http2session->submit_rst_stream(frame->hd.stream_id, + NGHTTP2_PROTOCOL_ERROR); + downstream->set_response_state(Downstream::MSG_RESET); + } + + return 0; + } + + downstream->set_response_state(Downstream::HEADER_COMPLETE); + downstream->check_upgrade_fulfilled(); + + if (downstream->get_upgraded()) { + downstream->set_response_connection_close(true); + // On upgrade sucess, both ends can send data + if (upstream->resume_read(SHRPX_NO_BUFFER, downstream, 0) != 0) { + // If resume_read fails, just drop connection. Not ideal. + delete upstream->get_client_handler(); + return -1; + } + downstream->set_request_state(Downstream::HEADER_COMPLETE); + if (LOG_ENABLED(INFO)) { + SSLOG(INFO, http2session) + << "HTTP upgrade success. stream_id=" << frame->hd.stream_id; + } + } else { + auto content_length = + downstream->get_response_header(http2::HD_CONTENT_LENGTH); + if (content_length) { + // libnghttp2 guarantees this can be parsed + auto len = util::parse_uint(content_length->value); + downstream->set_response_content_length(len); + } + + if (downstream->get_response_content_length() == -1 && + downstream->expect_response_body()) { + // Here we have response body but Content-Length is not known in + // advance. + if (downstream->get_request_major() <= 0 || + (downstream->get_request_major() == 1 && + downstream->get_request_minor() == 0)) { + // We simply close connection for pre-HTTP/1.1 in this case. + downstream->set_response_connection_close(true); + } else { + // Otherwise, use chunked encoding to keep upstream connection + // open. In HTTP2, we are supporsed not to receive + // transfer-encoding. + downstream->add_response_header("transfer-encoding", "chunked", + http2::HD_TRANSFER_ENCODING); + downstream->set_chunked_response(true); + } + } + } + + rv = upstream->on_downstream_header_complete(downstream); + if (rv != 0) { + http2session->submit_rst_stream(frame->hd.stream_id, + NGHTTP2_PROTOCOL_ERROR); + downstream->set_response_state(Downstream::MSG_RESET); + } + + return 0; +} +} // namespace + +namespace { +int on_frame_recv_callback(nghttp2_session *session, const nghttp2_frame *frame, + void *user_data) { + int rv; + auto http2session = static_cast(user_data); + + switch (frame->hd.type) { + case NGHTTP2_DATA: { + auto sd = static_cast( + nghttp2_session_get_stream_user_data(session, frame->hd.stream_id)); + if (!sd || !sd->dconn) { + return 0; + } + auto downstream = sd->dconn->get_downstream(); + if (!downstream || + downstream->get_downstream_stream_id() != frame->hd.stream_id) { + return 0; + } + + auto upstream = downstream->get_upstream(); + rv = upstream->on_downstream_body(downstream, nullptr, 0, true); + if (rv != 0) { + http2session->submit_rst_stream(frame->hd.stream_id, + NGHTTP2_INTERNAL_ERROR); + downstream->set_response_state(Downstream::MSG_RESET); + + } else if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) { + + downstream->disable_downstream_rtimer(); + + if (downstream->get_response_state() == Downstream::HEADER_COMPLETE) { + + downstream->set_response_state(Downstream::MSG_COMPLETE); + + rv = upstream->on_downstream_body_complete(downstream); + + if (rv != 0) { + downstream->set_response_state(Downstream::MSG_RESET); + } + } + } + + call_downstream_readcb(http2session, downstream); + return 0; + } + case NGHTTP2_HEADERS: { + auto sd = static_cast( + nghttp2_session_get_stream_user_data(session, frame->hd.stream_id)); + if (!sd || !sd->dconn) { + return 0; + } + auto downstream = sd->dconn->get_downstream(); + + if (!downstream) { + return 0; + } + + if (frame->headers.cat == NGHTTP2_HCAT_RESPONSE) { + rv = on_response_headers(http2session, downstream, session, frame); + + if (rv != 0) { + return 0; + } + } else if (frame->headers.cat == NGHTTP2_HCAT_HEADERS) { + if (downstream->get_expect_final_response()) { + rv = on_response_headers(http2session, downstream, session, frame); + + if (rv != 0) { + return 0; + } + } + } + + if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) { + downstream->disable_downstream_rtimer(); + + if (downstream->get_response_state() == Downstream::HEADER_COMPLETE) { + downstream->set_response_state(Downstream::MSG_COMPLETE); + + auto upstream = downstream->get_upstream(); + + rv = upstream->on_downstream_body_complete(downstream); + + if (rv != 0) { + downstream->set_response_state(Downstream::MSG_RESET); + } + } + } else { + downstream->reset_downstream_rtimer(); + } + + // This may delete downstream + call_downstream_readcb(http2session, downstream); + + return 0; + } + case NGHTTP2_RST_STREAM: { + auto sd = static_cast( + nghttp2_session_get_stream_user_data(session, frame->hd.stream_id)); + if (sd && sd->dconn) { + auto downstream = sd->dconn->get_downstream(); + if (downstream && + downstream->get_downstream_stream_id() == frame->hd.stream_id) { + + downstream->set_response_rst_stream_error_code( + frame->rst_stream.error_code); + call_downstream_readcb(http2session, downstream); + } + } + return 0; + } + case NGHTTP2_SETTINGS: + if ((frame->hd.flags & NGHTTP2_FLAG_ACK) == 0) { + return 0; + } + http2session->stop_settings_timer(); + return 0; + case NGHTTP2_PING: + if (frame->hd.flags & NGHTTP2_FLAG_ACK) { + if (LOG_ENABLED(INFO)) { + LOG(INFO) << "PING ACK received"; + } + http2session->connection_alive(); + } + return 0; + case NGHTTP2_PUSH_PROMISE: + if (LOG_ENABLED(INFO)) { + SSLOG(INFO, http2session) + << "Received downstream PUSH_PROMISE stream_id=" + << frame->hd.stream_id + << ", promised_stream_id=" << frame->push_promise.promised_stream_id; + } + // We just respond with RST_STREAM. + http2session->submit_rst_stream(frame->push_promise.promised_stream_id, + NGHTTP2_REFUSED_STREAM); + return 0; + default: + return 0; + } +} +} // namespace + +namespace { +int on_data_chunk_recv_callback(nghttp2_session *session, uint8_t flags, + int32_t stream_id, const uint8_t *data, + size_t len, void *user_data) { + int rv; + auto http2session = static_cast(user_data); + auto sd = static_cast( + nghttp2_session_get_stream_user_data(session, stream_id)); + if (!sd || !sd->dconn) { + http2session->submit_rst_stream(stream_id, NGHTTP2_INTERNAL_ERROR); + + if (http2session->consume(stream_id, len) != 0) { + return NGHTTP2_ERR_CALLBACK_FAILURE; + } + + return 0; + } + auto downstream = sd->dconn->get_downstream(); + if (!downstream || downstream->get_downstream_stream_id() != stream_id || + !downstream->expect_response_body()) { + + http2session->submit_rst_stream(stream_id, NGHTTP2_INTERNAL_ERROR); + + if (http2session->consume(stream_id, len) != 0) { + return NGHTTP2_ERR_CALLBACK_FAILURE; + } + + return 0; + } + + // We don't want DATA after non-final response, which is illegal in + // HTTP. + if (downstream->get_non_final_response()) { + http2session->submit_rst_stream(stream_id, NGHTTP2_PROTOCOL_ERROR); + + if (http2session->consume(stream_id, len) != 0) { + return NGHTTP2_ERR_CALLBACK_FAILURE; + } + + return 0; + } + + downstream->reset_downstream_rtimer(); + + downstream->add_response_bodylen(len); + + auto upstream = downstream->get_upstream(); + rv = upstream->on_downstream_body(downstream, data, len, false); + if (rv != 0) { + http2session->submit_rst_stream(stream_id, NGHTTP2_INTERNAL_ERROR); + + if (http2session->consume(stream_id, len) != 0) { + return NGHTTP2_ERR_CALLBACK_FAILURE; + } + + downstream->set_response_state(Downstream::MSG_RESET); + } + + downstream->add_response_datalen(len); + + call_downstream_readcb(http2session, downstream); + return 0; +} +} // namespace + +namespace { +int on_frame_send_callback(nghttp2_session *session, const nghttp2_frame *frame, + void *user_data) { + auto http2session = static_cast(user_data); + + if (frame->hd.type == NGHTTP2_DATA || frame->hd.type == NGHTTP2_HEADERS) { + if ((frame->hd.flags & NGHTTP2_FLAG_END_STREAM) == 0) { + return 0; + } + + auto sd = static_cast( + nghttp2_session_get_stream_user_data(session, frame->hd.stream_id)); + + if (!sd || !sd->dconn) { + return 0; + } + + auto downstream = sd->dconn->get_downstream(); + + if (!downstream || + downstream->get_downstream_stream_id() != frame->hd.stream_id) { + return 0; + } + + downstream->reset_downstream_rtimer(); + + return 0; + } + + if (frame->hd.type == NGHTTP2_SETTINGS && + (frame->hd.flags & NGHTTP2_FLAG_ACK) == 0) { + http2session->start_settings_timer(); + } + return 0; +} +} // namespace + +namespace { +int on_frame_not_send_callback(nghttp2_session *session, + const nghttp2_frame *frame, int lib_error_code, + void *user_data) { + auto http2session = static_cast(user_data); + if (LOG_ENABLED(INFO)) { + SSLOG(INFO, http2session) << "Failed to send control frame type=" + << static_cast(frame->hd.type) + << "lib_error_code=" << lib_error_code << ":" + << nghttp2_strerror(lib_error_code); + } + if (frame->hd.type == NGHTTP2_HEADERS && + lib_error_code != NGHTTP2_ERR_STREAM_CLOSED && + lib_error_code != NGHTTP2_ERR_STREAM_CLOSING) { + // To avoid stream hanging around, flag Downstream::MSG_RESET. + auto sd = static_cast( + nghttp2_session_get_stream_user_data(session, frame->hd.stream_id)); + if (!sd) { + return 0; + } + if (!sd->dconn) { + return 0; + } + auto downstream = sd->dconn->get_downstream(); + if (!downstream || + downstream->get_downstream_stream_id() != frame->hd.stream_id) { + return 0; + } + downstream->set_response_state(Downstream::MSG_RESET); + call_downstream_readcb(http2session, downstream); + } + return 0; +} +} // namespace + +nghttp2_session_callbacks *create_http2_downstream_callbacks() { + int rv; + nghttp2_session_callbacks *callbacks; + + rv = nghttp2_session_callbacks_new(&callbacks); + + if (rv != 0) { + return nullptr; + } + + nghttp2_session_callbacks_set_on_stream_close_callback( + callbacks, on_stream_close_callback); + + nghttp2_session_callbacks_set_on_frame_recv_callback(callbacks, + on_frame_recv_callback); + + nghttp2_session_callbacks_set_on_data_chunk_recv_callback( + callbacks, on_data_chunk_recv_callback); + + nghttp2_session_callbacks_set_on_frame_send_callback(callbacks, + on_frame_send_callback); + + nghttp2_session_callbacks_set_on_frame_not_send_callback( + callbacks, on_frame_not_send_callback); + + nghttp2_session_callbacks_set_on_header_callback(callbacks, + on_header_callback); + + nghttp2_session_callbacks_set_on_begin_headers_callback( + callbacks, on_begin_headers_callback); + + if (get_config()->padding) { + nghttp2_session_callbacks_set_select_padding_callback( + callbacks, http::select_padding_callback); + } + + return callbacks; +} + +int Http2Session::connection_made() { + int rv; + + state_ = Http2Session::CONNECTED; + + if (ssl_ctx_) { + const unsigned char *next_proto = nullptr; + unsigned int next_proto_len; + SSL_get0_next_proto_negotiated(conn_.tls.ssl, &next_proto, &next_proto_len); + for (int i = 0; i < 2; ++i) { + if (next_proto) { + if (LOG_ENABLED(INFO)) { + std::string proto(next_proto, next_proto + next_proto_len); + SSLOG(INFO, this) << "Negotiated next protocol: " << proto; + } + if (!util::check_h2_is_selected(next_proto, next_proto_len)) { + return -1; + } + break; + } +#if OPENSSL_VERSION_NUMBER >= 0x10002000L + SSL_get0_alpn_selected(conn_.tls.ssl, &next_proto, &next_proto_len); +#else // OPENSSL_VERSION_NUMBER < 0x10002000L + break; +#endif // OPENSSL_VERSION_NUMBER < 0x10002000L + } + if (!next_proto) { + return -1; + } + } + + rv = nghttp2_session_client_new2(&session_, + get_config()->http2_downstream_callbacks, + this, get_config()->http2_client_option); + + if (rv != 0) { + return -1; + } + + flow_control_ = true; + + std::array entry; + entry[0].settings_id = NGHTTP2_SETTINGS_ENABLE_PUSH; + entry[0].value = 0; + entry[1].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS; + entry[1].value = get_config()->http2_max_concurrent_streams; + + entry[2].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE; + entry[2].value = (1 << get_config()->http2_downstream_window_bits) - 1; + + rv = nghttp2_submit_settings(session_, NGHTTP2_FLAG_NONE, entry.data(), + entry.size()); + if (rv != 0) { + return -1; + } + + if (get_config()->http2_downstream_connection_window_bits > 16) { + int32_t delta = + (1 << get_config()->http2_downstream_connection_window_bits) - 1 - + NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE; + rv = nghttp2_submit_window_update(session_, NGHTTP2_FLAG_NONE, 0, delta); + if (rv != 0) { + return -1; + } + } + + auto must_terminate = !get_config()->downstream_no_tls && + !ssl::check_http2_requirement(conn_.tls.ssl); + + if (must_terminate) { + rv = terminate_session(NGHTTP2_INADEQUATE_SECURITY); + + if (rv != 0) { + return -1; + } + } + + if (must_terminate) { + return 0; + } + + reset_connection_check_timer(CONNCHK_TIMEOUT); + + submit_pending_requests(); + + signal_write(); + return 0; +} + +int Http2Session::do_read() { return read_(*this); } +int Http2Session::do_write() { return write_(*this); } + +int Http2Session::on_read() { return on_read_(*this); } +int Http2Session::on_write() { return on_write_(*this); } + +int Http2Session::downstream_read() { + ssize_t rv = 0; + + if (rb_.rleft() > 0) { + rv = nghttp2_session_mem_recv( + session_, reinterpret_cast(rb_.pos), rb_.rleft()); + + if (rv < 0) { + SSLOG(ERROR, this) << "nghttp2_session_recv() returned error: " + << nghttp2_strerror(rv); + return -1; + } + + // nghttp2_session_mem_recv() should consume all input data in + // case of success. + rb_.reset(); + } + + if (nghttp2_session_want_read(session_) == 0 && + nghttp2_session_want_write(session_) == 0 && wb_.rleft() == 0) { + if (LOG_ENABLED(INFO)) { + SSLOG(INFO, this) << "No more read/write for this HTTP2 session"; + } + return -1; + } + + signal_write(); + return 0; +} + +int Http2Session::downstream_write() { + if (data_pending_) { + auto n = std::min(wb_.wleft(), data_pendinglen_); + wb_.write(data_pending_, n); + if (n < data_pendinglen_) { + data_pending_ += n; + data_pendinglen_ -= n; + return 0; + } + + data_pending_ = nullptr; + data_pendinglen_ = 0; + } + + for (;;) { + const uint8_t *data; + auto datalen = nghttp2_session_mem_send(session_, &data); + + if (datalen < 0) { + SSLOG(ERROR, this) << "nghttp2_session_mem_send() returned error: " + << nghttp2_strerror(datalen); + return -1; + } + if (datalen == 0) { + break; + } + auto n = wb_.write(data, datalen); + if (n < static_cast(datalen)) { + data_pending_ = data + n; + data_pendinglen_ = datalen - n; + return 0; + } + } + + if (nghttp2_session_want_read(session_) == 0 && + nghttp2_session_want_write(session_) == 0 && wb_.rleft() == 0) { + if (LOG_ENABLED(INFO)) { + SSLOG(INFO, this) << "No more read/write for this session"; + } + return -1; + } + + return 0; +} + +void Http2Session::signal_write() { + switch (state_) { + case Http2Session::DISCONNECTED: + if (LOG_ENABLED(INFO)) { + LOG(INFO) << "Start connecting to backend server"; + } + if (initiate_connection() != 0) { + if (LOG_ENABLED(INFO)) { + SSLOG(INFO, this) << "Could not initiate backend connection"; + } + disconnect(true); + } + break; + case Http2Session::CONNECTED: + conn_.wlimit.startw(); + break; + } +} + +struct ev_loop *Http2Session::get_loop() const { + return conn_.loop; +} + +ev_io *Http2Session::get_wev() { return &conn_.wev; } + +int Http2Session::get_state() const { return state_; } + +void Http2Session::set_state(int state) { state_ = state; } + +int Http2Session::terminate_session(uint32_t error_code) { + int rv; + rv = nghttp2_session_terminate_session(session_, error_code); + if (rv != 0) { + return -1; + } + return 0; +} + +SSL *Http2Session::get_ssl() const { return conn_.tls.ssl; } + +int Http2Session::consume(int32_t stream_id, size_t len) { + int rv; + + if (!session_) { + return 0; + } + + rv = nghttp2_session_consume(session_, stream_id, len); + + if (rv != 0) { + SSLOG(WARN, this) << "nghttp2_session_consume() returned error: " + << nghttp2_strerror(rv); + + return -1; + } + + return 0; +} + +bool Http2Session::can_push_request() const { + return state_ == CONNECTED && + connection_check_state_ == CONNECTION_CHECK_NONE; +} + +void Http2Session::start_checking_connection() { + if (state_ != CONNECTED || + connection_check_state_ != CONNECTION_CHECK_REQUIRED) { + return; + } + connection_check_state_ = CONNECTION_CHECK_STARTED; + + SSLOG(INFO, this) << "Start checking connection"; + // If connection is down, we may get error when writing data. Issue + // ping frame to see whether connection is alive. + nghttp2_submit_ping(session_, NGHTTP2_FLAG_NONE, NULL); + + // set ping timeout and start timer again + reset_connection_check_timer(CONNCHK_PING_TIMEOUT); + + signal_write(); +} + +void Http2Session::reset_connection_check_timer(ev_tstamp t) { + connchk_timer_.repeat = t; + ev_timer_again(conn_.loop, &connchk_timer_); +} + +void Http2Session::reset_connection_check_timer_if_not_checking() { + if (connection_check_state_ != CONNECTION_CHECK_NONE) { + return; + } + + reset_connection_check_timer(CONNCHK_TIMEOUT); +} + +void Http2Session::connection_alive() { + reset_connection_check_timer(CONNCHK_TIMEOUT); + + if (connection_check_state_ == CONNECTION_CHECK_NONE) { + return; + } + + if (LOG_ENABLED(INFO)) { + SSLOG(INFO, this) << "Connection alive"; + } + + connection_check_state_ = CONNECTION_CHECK_NONE; + + submit_pending_requests(); +} + +void Http2Session::submit_pending_requests() { + for (auto dconn = dconns_.head; dconn; dconn = dconn->dlnext) { + auto downstream = dconn->get_downstream(); + + if (!downstream || !downstream->request_submission_ready()) { + continue; + } + + auto upstream = downstream->get_upstream(); + + if (dconn->push_request_headers() != 0) { + if (LOG_ENABLED(INFO)) { + SSLOG(INFO, this) << "backend request failed"; + } + + upstream->on_downstream_abort_request(downstream, 400); + + continue; + } + + upstream->resume_read(SHRPX_NO_BUFFER, downstream, 0); + } +} + +void Http2Session::set_connection_check_state(int state) { + connection_check_state_ = state; +} + +int Http2Session::get_connection_check_state() const { + return connection_check_state_; +} + +int Http2Session::noop() { return 0; } + +int Http2Session::connected() { + if (!util::check_socket_connected(conn_.fd)) { + return -1; + } + + connect_blocker_->on_success(); + + if (LOG_ENABLED(INFO)) { + SSLOG(INFO, this) << "Connection established"; + } + + conn_.rlimit.startw(); + + if (conn_.tls.ssl) { + read_ = &Http2Session::tls_handshake; + write_ = &Http2Session::tls_handshake; + + return do_write(); + } + + read_ = &Http2Session::read_clear; + write_ = &Http2Session::write_clear; + + if (state_ == PROXY_CONNECTING) { + return do_write(); + } + + if (connection_made() != 0) { + state_ = CONNECT_FAILING; + return -1; + } + + return 0; +} + +int Http2Session::read_clear() { + ev_timer_again(conn_.loop, &conn_.rt); + + for (;;) { + // we should process buffered data first before we read EOF. + if (rb_.rleft() && on_read() != 0) { + return -1; + } + if (rb_.rleft()) { + return 0; + } + rb_.reset(); + + auto nread = conn_.read_clear(rb_.last, rb_.wleft()); + + if (nread == 0) { + return 0; + } + + if (nread < 0) { + return nread; + } + + rb_.write(nread); + } +} + +int Http2Session::write_clear() { + ev_timer_again(conn_.loop, &conn_.rt); + + for (;;) { + if (wb_.rleft() > 0) { + auto nwrite = conn_.write_clear(wb_.pos, wb_.rleft()); + + if (nwrite == 0) { + return 0; + } + + if (nwrite < 0) { + return nwrite; + } + + wb_.drain(nwrite); + continue; + } + + wb_.reset(); + if (on_write() != 0) { + return -1; + } + if (wb_.rleft() == 0) { + break; + } + } + + conn_.wlimit.stopw(); + ev_timer_stop(conn_.loop, &conn_.wt); + + return 0; +} + +int Http2Session::tls_handshake() { + ev_timer_again(conn_.loop, &conn_.rt); + + ERR_clear_error(); + + auto rv = conn_.tls_handshake(); + + if (rv == SHRPX_ERR_INPROGRESS) { + return 0; + } + + if (rv < 0) { + return rv; + } + + if (LOG_ENABLED(INFO)) { + SSLOG(INFO, this) << "SSL/TLS handshake completed"; + } + + if (!get_config()->downstream_no_tls && !get_config()->insecure && + check_cert() != 0) { + return -1; + } + + read_ = &Http2Session::read_tls; + write_ = &Http2Session::write_tls; + + if (connection_made() != 0) { + state_ = CONNECT_FAILING; + return -1; + } + + return 0; +} + +int Http2Session::read_tls() { + ev_timer_again(conn_.loop, &conn_.rt); + + ERR_clear_error(); + + for (;;) { + // we should process buffered data first before we read EOF. + if (rb_.rleft() && on_read() != 0) { + return -1; + } + if (rb_.rleft()) { + return 0; + } + rb_.reset(); + + auto nread = conn_.read_tls(rb_.last, rb_.wleft()); + + if (nread == 0) { + return 0; + } + + if (nread < 0) { + return nread; + } + + rb_.write(nread); + } +} + +int Http2Session::write_tls() { + ev_timer_again(conn_.loop, &conn_.rt); + + ERR_clear_error(); + + for (;;) { + if (wb_.rleft() > 0) { + auto nwrite = conn_.write_tls(wb_.pos, wb_.rleft()); + + if (nwrite == 0) { + return 0; + } + + if (nwrite < 0) { + return nwrite; + } + + wb_.drain(nwrite); + + continue; + } + wb_.reset(); + if (on_write() != 0) { + return -1; + } + if (wb_.rleft() == 0) { + break; + } + } + + conn_.wlimit.stopw(); + ev_timer_stop(conn_.loop, &conn_.wt); + + return 0; +} + +bool Http2Session::should_hard_fail() const { + switch (state_) { + case PROXY_CONNECTING: + case PROXY_FAILED: + case CONNECTING: + case CONNECT_FAILING: + return true; + default: + return false; + } +} + +size_t Http2Session::get_addr_idx() const { return addr_idx_; } + +} // namespace shrpx diff --git a/src/shrpx_http2_session.h b/src/shrpx_http2_session.h new file mode 100644 index 0000000..35d3376 --- /dev/null +++ b/src/shrpx_http2_session.h @@ -0,0 +1,219 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef SHRPX_HTTP2_SESSION_H +#define SHRPX_HTTP2_SESSION_H + +#include "shrpx.h" + +#include +#include + +#include + +#include + +#include + +#include "http-parser/http_parser.h" + +#include "shrpx_connection.h" +#include "buffer.h" +#include "template.h" + +using namespace nghttp2; + +namespace shrpx { + +class Http2DownstreamConnection; +class Worker; +class ConnectBlocker; + +struct StreamData { + StreamData *dlnext, *dlprev; + Http2DownstreamConnection *dconn; +}; + +class Http2Session { +public: + Http2Session(struct ev_loop *loop, SSL_CTX *ssl_ctx, + ConnectBlocker *connect_blocker, Worker *worker); + ~Http2Session(); + + int check_cert(); + + // If hard is true, all pending requests are abandoned and + // associated ClientHandlers will be deleted. + int disconnect(bool hard = false); + int initiate_connection(); + + void add_downstream_connection(Http2DownstreamConnection *dconn); + void remove_downstream_connection(Http2DownstreamConnection *dconn); + + void remove_stream_data(StreamData *sd); + + int submit_request(Http2DownstreamConnection *dconn, int32_t pri, + const nghttp2_nv *nva, size_t nvlen, + const nghttp2_data_provider *data_prd); + + int submit_rst_stream(int32_t stream_id, uint32_t error_code); + + int submit_priority(Http2DownstreamConnection *dconn, int32_t pri); + + int terminate_session(uint32_t error_code); + + nghttp2_session *get_session() const; + + bool get_flow_control() const; + + int resume_data(Http2DownstreamConnection *dconn); + + int connection_made(); + + int do_read(); + int do_write(); + + int on_read(); + int on_write(); + + int connected(); + int read_clear(); + int write_clear(); + int tls_handshake(); + int read_tls(); + int write_tls(); + + int downstream_read_proxy(); + int downstream_connect_proxy(); + + int downstream_read(); + int downstream_write(); + + int noop(); + + void signal_write(); + + struct ev_loop *get_loop() const; + + ev_io *get_wev(); + + int get_state() const; + void set_state(int state); + + void start_settings_timer(); + void stop_settings_timer(); + + SSL *get_ssl() const; + + int consume(int32_t stream_id, size_t len); + + // Returns true if request can be issued on downstream connection. + bool can_push_request() const; + // Initiates the connection checking if downstream connection has + // been established and connection checking is required. + void start_checking_connection(); + // Resets connection check timer to timeout |t|. After timeout, we + // require connection checking. If connection checking is already + // enabled, this timeout is for PING ACK timeout. + void reset_connection_check_timer(ev_tstamp t); + void reset_connection_check_timer_if_not_checking(); + // Signals that connection is alive. Internally + // reset_connection_check_timer() is called. + void connection_alive(); + // Change connection check state. + void set_connection_check_state(int state); + int get_connection_check_state() const; + + bool should_hard_fail() const; + + void submit_pending_requests(); + + size_t get_addr_idx() const; + + enum { + // Disconnected + DISCONNECTED, + // Connecting proxy and making CONNECT request + PROXY_CONNECTING, + // Tunnel is established with proxy + PROXY_CONNECTED, + // Establishing tunnel is failed + PROXY_FAILED, + // Connecting to downstream and/or performing SSL/TLS handshake + CONNECTING, + // Connected to downstream + CONNECTED, + // Connection is started to fail + CONNECT_FAILING, + }; + + static const size_t OUTBUF_MAX_THRES = 64 * 1024; + + enum { + // Connection checking is not required + CONNECTION_CHECK_NONE, + // Connection checking is required + CONNECTION_CHECK_REQUIRED, + // Connection checking has been started + CONNECTION_CHECK_STARTED + }; + + using ReadBuf = Buffer<8192>; + using WriteBuf = Buffer<32768>; + +private: + Connection conn_; + ev_timer settings_timer_; + // This timer has 2 purpose: when it first timeout, set + // connection_check_state_ = CONNECTION_CHECK_REQUIRED. After + // connection check has started, this timer is started again and + // traps PING ACK timeout. + ev_timer connchk_timer_; + DList dconns_; + DList streams_; + std::function read_, write_; + std::function on_read_, on_write_; + // Used to parse the response from HTTP proxy + std::unique_ptr proxy_htp_; + Worker *worker_; + ConnectBlocker *connect_blocker_; + // NULL if no TLS is configured + SSL_CTX *ssl_ctx_; + nghttp2_session *session_; + const uint8_t *data_pending_; + size_t data_pendinglen_; + // index of get_config()->downstream_addrs this object uses + size_t addr_idx_; + int state_; + int connection_check_state_; + bool flow_control_; + WriteBuf wb_; + ReadBuf rb_; +}; + +nghttp2_session_callbacks *create_http2_downstream_callbacks(); + +} // namespace shrpx + +#endif // SHRPX_HTTP2_SESSION_H diff --git a/src/shrpx_http2_upstream.cc b/src/shrpx_http2_upstream.cc new file mode 100644 index 0000000..e3aa67f --- /dev/null +++ b/src/shrpx_http2_upstream.cc @@ -0,0 +1,1661 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "shrpx_http2_upstream.h" + +#include +#include +#include +#include + +#include "shrpx_client_handler.h" +#include "shrpx_https_upstream.h" +#include "shrpx_downstream.h" +#include "shrpx_downstream_connection.h" +#include "shrpx_config.h" +#include "shrpx_http.h" +#include "shrpx_worker.h" +#include "http2.h" +#include "util.h" +#include "base64.h" +#include "app_helper.h" +#include "template.h" + +using namespace nghttp2; + +namespace shrpx { + +namespace { +int on_stream_close_callback(nghttp2_session *session, int32_t stream_id, + uint32_t error_code, void *user_data) { + auto upstream = static_cast(user_data); + if (LOG_ENABLED(INFO)) { + ULOG(INFO, upstream) << "Stream stream_id=" << stream_id + << " is being closed"; + } + + auto downstream = static_cast( + nghttp2_session_get_stream_user_data(session, stream_id)); + + if (!downstream) { + return 0; + } + + upstream->consume(stream_id, downstream->get_request_datalen()); + + downstream->reset_request_datalen(); + + if (downstream->get_request_state() == Downstream::CONNECT_FAIL) { + upstream->remove_downstream(downstream); + // downstream was deleted + + return 0; + } + + downstream->set_request_state(Downstream::STREAM_CLOSED); + + if (downstream->get_response_state() == Downstream::MSG_COMPLETE) { + // At this point, downstream response was read + if (!downstream->get_upgraded() && + !downstream->get_response_connection_close()) { + // Keep-alive + downstream->detach_downstream_connection(); + } + + upstream->remove_downstream(downstream); + // downstream was deleted + + return 0; + } + + // At this point, downstream read may be paused. + + // If shrpx_downstream::push_request_headers() failed, the + // error is handled here. + upstream->remove_downstream(downstream); + // downstream was deleted + + // How to test this case? Request sufficient large download + // and make client send RST_STREAM after it gets first DATA + // frame chunk. + + return 0; +} +} // namespace + +int Http2Upstream::upgrade_upstream(HttpsUpstream *http) { + int rv; + + // to deduce :scheme and :authority, first we have to parse request + // URI. + http_parser_url u = {}; + auto &url = http->get_downstream()->get_request_path(); + + std::string scheme, authority; + rv = http_parser_parse_url(url.c_str(), url.size(), 0, &u); + if (rv == 0) { + http2::copy_url_component(scheme, &u, UF_SCHEMA, url.c_str()); + http2::copy_url_component(authority, &u, UF_HOST, url.c_str()); + } + if (scheme.empty()) { + if (handler_->get_ssl()) { + scheme = "https"; + } else { + scheme = "http"; + } + } + if (!authority.empty()) { + if (authority.find(":") != std::string::npos) { + authority = "[" + authority; + authority += "]"; + } + if (u.field_set & (1 << UF_PORT)) { + authority += ":"; + authority += util::utos(u.port); + } + } + + auto http2_settings = http->get_downstream()->get_http2_settings(); + util::to_base64(http2_settings); + + auto settings_payload = + base64::decode(std::begin(http2_settings), std::end(http2_settings)); + + rv = nghttp2_session_upgrade( + session_, reinterpret_cast(settings_payload.c_str()), + settings_payload.size(), nullptr); + if (rv != 0) { + if (LOG_ENABLED(INFO)) { + ULOG(INFO, this) << "nghttp2_session_upgrade() returned error: " + << nghttp2_strerror(rv); + } + return -1; + } + pre_upstream_.reset(http); + auto downstream = http->pop_downstream(); + downstream->reset_upstream(this); + downstream->set_stream_id(1); + downstream->reset_upstream_rtimer(); + downstream->set_stream_id(1); + downstream->set_priority(0); + downstream->set_request_http2_authority(authority); + downstream->set_request_http2_scheme(scheme); + + auto ptr = downstream.get(); + + nghttp2_session_set_stream_user_data(session_, 1, ptr); + downstream_queue_.add_pending(std::move(downstream)); + downstream_queue_.mark_active(ptr); + + if (LOG_ENABLED(INFO)) { + ULOG(INFO, this) << "Connection upgraded to HTTP/2"; + } + + return 0; +} + +void Http2Upstream::start_settings_timer() { + ev_timer_start(handler_->get_loop(), &settings_timer_); +} + +void Http2Upstream::stop_settings_timer() { + ev_timer_stop(handler_->get_loop(), &settings_timer_); +} + +namespace { +int on_header_callback(nghttp2_session *session, const nghttp2_frame *frame, + const uint8_t *name, size_t namelen, + const uint8_t *value, size_t valuelen, uint8_t flags, + void *user_data) { + if (get_config()->upstream_frame_debug) { + verbose_on_header_callback(session, frame, name, namelen, value, valuelen, + flags, user_data); + } + if (frame->hd.type != NGHTTP2_HEADERS) { + return 0; + } + auto upstream = static_cast(user_data); + auto downstream = static_cast( + nghttp2_session_get_stream_user_data(session, frame->hd.stream_id)); + if (!downstream) { + return 0; + } + + if (downstream->get_request_headers_sum() + namelen + valuelen > + get_config()->header_field_buffer || + downstream->get_request_headers().size() >= + get_config()->max_header_fields) { + if (downstream->get_response_state() == Downstream::MSG_COMPLETE) { + return 0; + } + + if (LOG_ENABLED(INFO)) { + ULOG(INFO, upstream) << "Too large or many header field size=" + << downstream->get_request_headers_sum() + namelen + + valuelen << ", num=" + << downstream->get_request_headers().size() + 1; + } + + // just ignore header fields if this is trailer part. + if (frame->headers.cat == NGHTTP2_HCAT_HEADERS) { + return 0; + } + + if (upstream->error_reply(downstream, 431) != 0) { + return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; + } + + return 0; + } + + if (frame->headers.cat == NGHTTP2_HCAT_HEADERS) { + // just store header fields for trailer part + downstream->add_request_trailer(name, namelen, value, valuelen, + flags & NGHTTP2_NV_FLAG_NO_INDEX, -1); + return 0; + } + + auto token = http2::lookup_token(name, namelen); + + downstream->add_request_header(name, namelen, value, valuelen, + flags & NGHTTP2_NV_FLAG_NO_INDEX, token); + return 0; +} +} // namespace + +namespace { +int on_begin_headers_callback(nghttp2_session *session, + const nghttp2_frame *frame, void *user_data) { + auto upstream = static_cast(user_data); + + if (frame->headers.cat != NGHTTP2_HCAT_REQUEST) { + return 0; + } + if (LOG_ENABLED(INFO)) { + ULOG(INFO, upstream) << "Received upstream request HEADERS stream_id=" + << frame->hd.stream_id; + } + + auto handler = upstream->get_client_handler(); + + // TODO Use priority 0 for now + auto downstream = make_unique(upstream, handler->get_mcpool(), + frame->hd.stream_id, 0); + nghttp2_session_set_stream_user_data(session, frame->hd.stream_id, + downstream.get()); + + downstream->reset_upstream_rtimer(); + + // Although, we deprecated minor version from HTTP/2, we supply + // minor version 0 to use via header field in a conventional way. + downstream->set_request_major(2); + downstream->set_request_minor(0); + + upstream->add_pending_downstream(std::move(downstream)); + + return 0; +} +} // namespace + +int Http2Upstream::on_request_headers(Downstream *downstream, + const nghttp2_frame *frame) { + if (downstream->get_response_state() == Downstream::MSG_COMPLETE) { + return 0; + } + + auto &nva = downstream->get_request_headers(); + + if (LOG_ENABLED(INFO)) { + std::stringstream ss; + for (auto &nv : nva) { + ss << TTY_HTTP_HD << nv.name << TTY_RST << ": " << nv.value << "\n"; + } + ULOG(INFO, this) << "HTTP request headers. stream_id=" + << downstream->get_stream_id() << "\n" << ss.str(); + } + + if (get_config()->http2_upstream_dump_request_header) { + http2::dump_nv(get_config()->http2_upstream_dump_request_header, nva); + } + + auto content_length = + downstream->get_request_header(http2::HD_CONTENT_LENGTH); + if (content_length) { + // libnghttp2 guarantees this can be parsed + auto len = util::parse_uint(content_length->value); + downstream->set_request_content_length(len); + } + + auto authority = downstream->get_request_header(http2::HD__AUTHORITY); + auto path = downstream->get_request_header(http2::HD__PATH); + auto method = downstream->get_request_header(http2::HD__METHOD); + auto scheme = downstream->get_request_header(http2::HD__SCHEME); + + // presence of mandatory header fields are guaranteed by libnghttp2. + + // For HTTP/2 proxy, we request :authority. + if (method->value != "CONNECT" && get_config()->http2_proxy && !authority) { + rst_stream(downstream, NGHTTP2_PROTOCOL_ERROR); + return 0; + } + + downstream->set_request_method(http2::value_to_str(method)); + downstream->set_request_http2_scheme(http2::value_to_str(scheme)); + downstream->set_request_http2_authority(http2::value_to_str(authority)); + downstream->set_request_path(http2::value_to_str(path)); + + if (!(frame->hd.flags & NGHTTP2_FLAG_END_STREAM)) { + downstream->set_request_http2_expect_body(true); + } + + downstream->inspect_http2_request(); + + downstream->set_request_state(Downstream::HEADER_COMPLETE); + if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) { + downstream->disable_upstream_rtimer(); + + downstream->set_request_state(Downstream::MSG_COMPLETE); + } + + start_downstream(downstream); + + return 0; +} + +void Http2Upstream::start_downstream(Downstream *downstream) { + if (downstream_queue_.can_activate( + downstream->get_request_http2_authority())) { + initiate_downstream(downstream); + return; + } + + downstream_queue_.mark_blocked(downstream); +} + +void Http2Upstream::initiate_downstream(Downstream *downstream) { + int rv; + + rv = downstream->attach_downstream_connection( + handler_->get_downstream_connection()); + if (rv != 0) { + // downstream connection fails, send error page + if (error_reply(downstream, 503) != 0) { + rst_stream(downstream, NGHTTP2_INTERNAL_ERROR); + } + + downstream->set_request_state(Downstream::CONNECT_FAIL); + + downstream_queue_.mark_failure(downstream); + + return; + } + rv = downstream->push_request_headers(); + if (rv != 0) { + + if (error_reply(downstream, 503) != 0) { + rst_stream(downstream, NGHTTP2_INTERNAL_ERROR); + } + + downstream_queue_.mark_failure(downstream); + + return; + } + + downstream_queue_.mark_active(downstream); + + return; +} + +namespace { +int on_frame_recv_callback(nghttp2_session *session, const nghttp2_frame *frame, + void *user_data) { + if (get_config()->upstream_frame_debug) { + verbose_on_frame_recv_callback(session, frame, user_data); + } + auto upstream = static_cast(user_data); + + switch (frame->hd.type) { + case NGHTTP2_DATA: { + auto downstream = static_cast( + nghttp2_session_get_stream_user_data(session, frame->hd.stream_id)); + if (!downstream) { + return 0; + } + + if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) { + downstream->disable_upstream_rtimer(); + + downstream->end_upload_data(); + downstream->set_request_state(Downstream::MSG_COMPLETE); + } + + return 0; + } + case NGHTTP2_HEADERS: { + auto downstream = static_cast( + nghttp2_session_get_stream_user_data(session, frame->hd.stream_id)); + if (!downstream) { + return 0; + } + + if (frame->headers.cat == NGHTTP2_HCAT_REQUEST) { + downstream->reset_upstream_rtimer(); + + return upstream->on_request_headers(downstream, frame); + } + + if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) { + downstream->disable_upstream_rtimer(); + + downstream->end_upload_data(); + downstream->set_request_state(Downstream::MSG_COMPLETE); + } + + return 0; + } + case NGHTTP2_SETTINGS: + if ((frame->hd.flags & NGHTTP2_FLAG_ACK) == 0) { + return 0; + } + upstream->stop_settings_timer(); + return 0; + case NGHTTP2_GOAWAY: + if (LOG_ENABLED(INFO)) { + auto debug_data = util::ascii_dump(frame->goaway.opaque_data, + frame->goaway.opaque_data_len); + + ULOG(INFO, upstream) << "GOAWAY received: last-stream-id=" + << frame->goaway.last_stream_id + << ", error_code=" << frame->goaway.error_code + << ", debug_data=" << debug_data; + } + return 0; + default: + return 0; + } +} +} // namespace + +namespace { +int on_data_chunk_recv_callback(nghttp2_session *session, uint8_t flags, + int32_t stream_id, const uint8_t *data, + size_t len, void *user_data) { + auto upstream = static_cast(user_data); + auto downstream = static_cast( + nghttp2_session_get_stream_user_data(session, stream_id)); + + if (!downstream || !downstream->get_downstream_connection()) { + if (upstream->consume(stream_id, len) != 0) { + return NGHTTP2_ERR_CALLBACK_FAILURE; + } + + return 0; + } + + downstream->reset_upstream_rtimer(); + + if (downstream->push_upload_data_chunk(data, len) != 0) { + upstream->rst_stream(downstream, NGHTTP2_INTERNAL_ERROR); + + if (upstream->consume(stream_id, len) != 0) { + return NGHTTP2_ERR_CALLBACK_FAILURE; + } + + return 0; + } + + return 0; +} +} // namespace + +namespace { +int on_frame_send_callback(nghttp2_session *session, const nghttp2_frame *frame, + void *user_data) { + if (get_config()->upstream_frame_debug) { + verbose_on_frame_send_callback(session, frame, user_data); + } + auto upstream = static_cast(user_data); + auto handler = upstream->get_client_handler(); + + switch (frame->hd.type) { + case NGHTTP2_DATA: + case NGHTTP2_HEADERS: { + if ((frame->hd.flags & NGHTTP2_FLAG_END_STREAM) == 0) { + return 0; + } + // RST_STREAM if request is still incomplete. + auto stream_id = frame->hd.stream_id; + auto downstream = static_cast( + nghttp2_session_get_stream_user_data(session, stream_id)); + + if (!downstream) { + return 0; + } + + // For tunneling, issue RST_STREAM to finish the stream. + if (downstream->get_upgraded() || + nghttp2_session_get_stream_remote_close(session, stream_id) == 0) { + if (LOG_ENABLED(INFO)) { + ULOG(INFO, upstream) + << "Send RST_STREAM to " + << (downstream->get_upgraded() ? "tunneled " : "") + << "stream stream_id=" << downstream->get_stream_id() + << " to finish off incomplete request"; + } + + upstream->rst_stream(downstream, NGHTTP2_NO_ERROR); + } + + return 0; + } + case NGHTTP2_SETTINGS: + if ((frame->hd.flags & NGHTTP2_FLAG_ACK) == 0) { + upstream->start_settings_timer(); + } + return 0; + case NGHTTP2_PUSH_PROMISE: { + auto promised_stream_id = frame->push_promise.promised_stream_id; + auto downstream = make_unique(upstream, handler->get_mcpool(), + promised_stream_id, 0); + + nghttp2_session_set_stream_user_data(session, promised_stream_id, + downstream.get()); + + downstream->disable_upstream_rtimer(); + + downstream->set_request_major(2); + downstream->set_request_minor(0); + + for (size_t i = 0; i < frame->push_promise.nvlen; ++i) { + auto &nv = frame->push_promise.nva[i]; + auto token = http2::lookup_token(nv.name, nv.namelen); + switch (token) { + case http2::HD__METHOD: + downstream->set_request_method({nv.value, nv.value + nv.valuelen}); + break; + case http2::HD__SCHEME: + downstream->set_request_http2_scheme( + {nv.value, nv.value + nv.valuelen}); + break; + case http2::HD__AUTHORITY: + downstream->set_request_http2_authority( + {nv.value, nv.value + nv.valuelen}); + break; + case http2::HD__PATH: + downstream->set_request_path({nv.value, nv.value + nv.valuelen}); + break; + } + downstream->add_request_header(nv.name, nv.namelen, nv.value, nv.valuelen, + nv.flags & NGHTTP2_NV_FLAG_NO_INDEX, + token); + } + + downstream->inspect_http2_request(); + + downstream->set_request_state(Downstream::MSG_COMPLETE); + + // a bit weird but start_downstream() expects that given + // downstream is in pending queue. + auto ptr = downstream.get(); + upstream->add_pending_downstream(std::move(downstream)); + upstream->start_downstream(ptr); + + return 0; + } + case NGHTTP2_GOAWAY: + if (LOG_ENABLED(INFO)) { + auto debug_data = util::ascii_dump(frame->goaway.opaque_data, + frame->goaway.opaque_data_len); + + ULOG(INFO, upstream) << "Sending GOAWAY: last-stream-id=" + << frame->goaway.last_stream_id + << ", error_code=" << frame->goaway.error_code + << ", debug_data=" << debug_data; + } + return 0; + default: + return 0; + } +} +} // namespace + +namespace { +int on_frame_not_send_callback(nghttp2_session *session, + const nghttp2_frame *frame, int lib_error_code, + void *user_data) { + auto upstream = static_cast(user_data); + if (LOG_ENABLED(INFO)) { + ULOG(INFO, upstream) << "Failed to send control frame type=" + << static_cast(frame->hd.type) + << ", lib_error_code=" << lib_error_code << ":" + << nghttp2_strerror(lib_error_code); + } + if (frame->hd.type == NGHTTP2_HEADERS && + lib_error_code != NGHTTP2_ERR_STREAM_CLOSED && + lib_error_code != NGHTTP2_ERR_STREAM_CLOSING) { + // To avoid stream hanging around, issue RST_STREAM. + auto downstream = static_cast( + nghttp2_session_get_stream_user_data(session, frame->hd.stream_id)); + if (downstream) { + upstream->rst_stream(downstream, NGHTTP2_INTERNAL_ERROR); + } + } + return 0; +} +} // namespace + +namespace { +uint32_t infer_upstream_rst_stream_error_code(uint32_t downstream_error_code) { + // NGHTTP2_REFUSED_STREAM is important because it tells upstream + // client to retry. + switch (downstream_error_code) { + case NGHTTP2_NO_ERROR: + case NGHTTP2_REFUSED_STREAM: + return downstream_error_code; + default: + return NGHTTP2_INTERNAL_ERROR; + } +} +} // namespace + +namespace { +void settings_timeout_cb(struct ev_loop *loop, ev_timer *w, int revents) { + auto upstream = static_cast(w->data); + auto handler = upstream->get_client_handler(); + ULOG(INFO, upstream) << "SETTINGS timeout"; + if (upstream->terminate_session(NGHTTP2_SETTINGS_TIMEOUT) != 0) { + delete handler; + return; + } + handler->signal_write(); +} +} // namespace + +namespace { +void shutdown_timeout_cb(struct ev_loop *loop, ev_timer *w, int revents) { + auto upstream = static_cast(w->data); + auto handler = upstream->get_client_handler(); + upstream->submit_goaway(); + handler->signal_write(); +} +} // namespace + +namespace { +void prepare_cb(struct ev_loop *loop, ev_prepare *w, int revents) { + auto upstream = static_cast(w->data); + upstream->check_shutdown(); +} +} // namespace + +void Http2Upstream::submit_goaway() { + auto last_stream_id = nghttp2_session_get_last_proc_stream_id(session_); + nghttp2_submit_goaway(session_, NGHTTP2_FLAG_NONE, last_stream_id, + NGHTTP2_NO_ERROR, nullptr, 0); +} + +void Http2Upstream::check_shutdown() { + int rv; + if (shutdown_handled_) { + return; + } + + auto worker = handler_->get_worker(); + + if (worker->get_graceful_shutdown()) { + shutdown_handled_ = true; + rv = nghttp2_submit_shutdown_notice(session_); + if (rv != 0) { + ULOG(FATAL, this) << "nghttp2_submit_shutdown_notice() failed: " + << nghttp2_strerror(rv); + return; + } + handler_->signal_write(); + ev_timer_start(handler_->get_loop(), &shutdown_timer_); + } +} + +nghttp2_session_callbacks *create_http2_upstream_callbacks() { + int rv; + nghttp2_session_callbacks *callbacks; + + rv = nghttp2_session_callbacks_new(&callbacks); + + if (rv != 0) { + return nullptr; + } + + nghttp2_session_callbacks_set_on_stream_close_callback( + callbacks, on_stream_close_callback); + + nghttp2_session_callbacks_set_on_frame_recv_callback(callbacks, + on_frame_recv_callback); + + nghttp2_session_callbacks_set_on_data_chunk_recv_callback( + callbacks, on_data_chunk_recv_callback); + + nghttp2_session_callbacks_set_on_frame_send_callback(callbacks, + on_frame_send_callback); + + nghttp2_session_callbacks_set_on_frame_not_send_callback( + callbacks, on_frame_not_send_callback); + + nghttp2_session_callbacks_set_on_header_callback(callbacks, + on_header_callback); + + nghttp2_session_callbacks_set_on_begin_headers_callback( + callbacks, on_begin_headers_callback); + + if (get_config()->padding) { + nghttp2_session_callbacks_set_select_padding_callback( + callbacks, http::select_padding_callback); + } + + return callbacks; +} + +Http2Upstream::Http2Upstream(ClientHandler *handler) + : downstream_queue_( + get_config()->http2_proxy + ? get_config()->downstream_connections_per_host + : get_config()->downstream_proto == PROTO_HTTP + ? get_config()->downstream_connections_per_frontend + : 0, + !get_config()->http2_proxy), + handler_(handler), session_(nullptr), data_pending_(nullptr), + data_pendinglen_(0), shutdown_handled_(false) { + + int rv; + + rv = nghttp2_session_server_new2(&session_, + get_config()->http2_upstream_callbacks, this, + get_config()->http2_option); + + assert(rv == 0); + + flow_control_ = true; + + // TODO Maybe call from outside? + std::array entry; + entry[0].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS; + entry[0].value = get_config()->http2_max_concurrent_streams; + + entry[1].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE; + entry[1].value = (1 << get_config()->http2_upstream_window_bits) - 1; + + rv = nghttp2_submit_settings(session_, NGHTTP2_FLAG_NONE, entry.data(), + entry.size()); + if (rv != 0) { + ULOG(ERROR, this) << "nghttp2_submit_settings() returned error: " + << nghttp2_strerror(rv); + } + + if (get_config()->http2_upstream_connection_window_bits > 16) { + int32_t delta = (1 << get_config()->http2_upstream_connection_window_bits) - + 1 - NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE; + rv = nghttp2_submit_window_update(session_, NGHTTP2_FLAG_NONE, 0, delta); + + if (rv != 0) { + ULOG(ERROR, this) << "nghttp2_submit_window_update() returned error: " + << nghttp2_strerror(rv); + } + } + + // We wait for SETTINGS ACK at least 10 seconds. + ev_timer_init(&settings_timer_, settings_timeout_cb, 10., 0.); + + settings_timer_.data = this; + + // timer for 2nd GOAWAY. HTTP/2 spec recommend 1 RTT. We wait for + // 2 seconds. + ev_timer_init(&shutdown_timer_, shutdown_timeout_cb, 2., 0); + shutdown_timer_.data = this; + + ev_prepare_init(&prep_, prepare_cb); + prep_.data = this; + ev_prepare_start(handler_->get_loop(), &prep_); + + handler_->reset_upstream_read_timeout( + get_config()->http2_upstream_read_timeout); + + handler_->signal_write(); +} + +Http2Upstream::~Http2Upstream() { + nghttp2_session_del(session_); + ev_prepare_stop(handler_->get_loop(), &prep_); + ev_timer_stop(handler_->get_loop(), &shutdown_timer_); + ev_timer_stop(handler_->get_loop(), &settings_timer_); +} + +int Http2Upstream::on_read() { + ssize_t rv = 0; + auto rb = handler_->get_rb(); + auto rlimit = handler_->get_rlimit(); + + if (rb->rleft()) { + rv = nghttp2_session_mem_recv(session_, rb->pos, rb->rleft()); + if (rv < 0) { + if (rv != NGHTTP2_ERR_BAD_CLIENT_MAGIC) { + ULOG(ERROR, this) << "nghttp2_session_recv() returned error: " + << nghttp2_strerror(rv); + } + return -1; + } + + // nghttp2_session_mem_recv should consume all input bytes on + // success. + assert(static_cast(rv) == rb->rleft()); + rb->reset(); + rlimit->startw(); + } + + auto wb = handler_->get_wb(); + if (nghttp2_session_want_read(session_) == 0 && + nghttp2_session_want_write(session_) == 0 && wb->rleft() == 0) { + if (LOG_ENABLED(INFO)) { + ULOG(INFO, this) << "No more read/write for this HTTP2 session"; + } + return -1; + } + + handler_->signal_write(); + return 0; +} + +// After this function call, downstream may be deleted. +int Http2Upstream::on_write() { + auto wb = handler_->get_wb(); + + if (data_pending_) { + auto n = std::min(wb->wleft(), data_pendinglen_); + wb->write(data_pending_, n); + if (n < data_pendinglen_) { + data_pending_ += n; + data_pendinglen_ -= n; + return 0; + } + + data_pending_ = nullptr; + data_pendinglen_ = 0; + } + + for (;;) { + const uint8_t *data; + auto datalen = nghttp2_session_mem_send(session_, &data); + + if (datalen < 0) { + ULOG(ERROR, this) << "nghttp2_session_mem_send() returned error: " + << nghttp2_strerror(datalen); + return -1; + } + if (datalen == 0) { + break; + } + auto n = wb->write(data, datalen); + if (n < static_cast(datalen)) { + data_pending_ = data + n; + data_pendinglen_ = datalen - n; + return 0; + } + } + + if (nghttp2_session_want_read(session_) == 0 && + nghttp2_session_want_write(session_) == 0 && wb->rleft() == 0) { + if (LOG_ENABLED(INFO)) { + ULOG(INFO, this) << "No more read/write for this HTTP2 session"; + } + return -1; + } + + return 0; +} + +ClientHandler *Http2Upstream::get_client_handler() const { return handler_; } + +int Http2Upstream::downstream_read(DownstreamConnection *dconn) { + auto downstream = dconn->get_downstream(); + + if (downstream->get_request_state() == Downstream::STREAM_CLOSED) { + // If upstream HTTP2 stream was closed, we just close downstream, + // because there is no consumer now. Downstream connection is also + // closed in this case. + remove_downstream(downstream); + // downstream was deleted + + return 0; + } + + if (downstream->get_response_state() == Downstream::MSG_RESET) { + // The downstream stream was reset (canceled). In this case, + // RST_STREAM to the upstream and delete downstream connection + // here. Deleting downstream will be taken place at + // on_stream_close_callback. + rst_stream(downstream, + infer_upstream_rst_stream_error_code( + downstream->get_response_rst_stream_error_code())); + downstream->pop_downstream_connection(); + // dconn was deleted + dconn = nullptr; + } else if (downstream->get_response_state() == Downstream::MSG_BAD_HEADER) { + if (error_reply(downstream, 502) != 0) { + return -1; + } + downstream->pop_downstream_connection(); + // dconn was deleted + dconn = nullptr; + } else { + auto rv = downstream->on_read(); + if (rv == SHRPX_ERR_EOF) { + return downstream_eof(dconn); + } + if (rv != 0) { + if (rv != SHRPX_ERR_NETWORK) { + if (LOG_ENABLED(INFO)) { + DCLOG(INFO, dconn) << "HTTP parser failure"; + } + } + return downstream_error(dconn, Downstream::EVENT_ERROR); + } + // Detach downstream connection early so that it could be reused + // without hitting server's request timeout. + if (downstream->get_response_state() == Downstream::MSG_COMPLETE && + !downstream->get_response_connection_close()) { + // Keep-alive + downstream->detach_downstream_connection(); + } + } + + handler_->signal_write(); + + // At this point, downstream may be deleted. + + return 0; +} + +int Http2Upstream::downstream_write(DownstreamConnection *dconn) { + int rv; + rv = dconn->on_write(); + if (rv == SHRPX_ERR_NETWORK) { + return downstream_error(dconn, Downstream::EVENT_ERROR); + } + if (rv != 0) { + return -1; + } + return 0; +} + +int Http2Upstream::downstream_eof(DownstreamConnection *dconn) { + auto downstream = dconn->get_downstream(); + + if (LOG_ENABLED(INFO)) { + DCLOG(INFO, dconn) << "EOF. stream_id=" << downstream->get_stream_id(); + } + if (downstream->get_request_state() == Downstream::STREAM_CLOSED) { + // If stream was closed already, we don't need to send reply at + // the first place. We can delete downstream. + remove_downstream(downstream); + // downstream was deleted + + return 0; + } + + // Delete downstream connection. If we don't delete it here, it will + // be pooled in on_stream_close_callback. + downstream->pop_downstream_connection(); + // dconn was deleted + dconn = nullptr; + // downstream wil be deleted in on_stream_close_callback. + if (downstream->get_response_state() == Downstream::HEADER_COMPLETE) { + // Server may indicate the end of the request by EOF + if (LOG_ENABLED(INFO)) { + ULOG(INFO, this) << "Downstream body was ended by EOF"; + } + downstream->set_response_state(Downstream::MSG_COMPLETE); + + // For tunneled connection, MSG_COMPLETE signals + // downstream_data_read_callback to send RST_STREAM after pending + // response body is sent. This is needed to ensure that RST_STREAM + // is sent after all pending data are sent. + on_downstream_body_complete(downstream); + } else if (downstream->get_response_state() != Downstream::MSG_COMPLETE) { + // If stream was not closed, then we set MSG_COMPLETE and let + // on_stream_close_callback delete downstream. + if (error_reply(downstream, 502) != 0) { + return -1; + } + } + handler_->signal_write(); + // At this point, downstream may be deleted. + return 0; +} + +int Http2Upstream::downstream_error(DownstreamConnection *dconn, int events) { + auto downstream = dconn->get_downstream(); + + if (LOG_ENABLED(INFO)) { + if (events & Downstream::EVENT_ERROR) { + DCLOG(INFO, dconn) << "Downstream network/general error"; + } else { + DCLOG(INFO, dconn) << "Timeout"; + } + if (downstream->get_upgraded()) { + DCLOG(INFO, dconn) << "Note: this is tunnel connection"; + } + } + + if (downstream->get_request_state() == Downstream::STREAM_CLOSED) { + remove_downstream(downstream); + // downstream was deleted + + return 0; + } + + // Delete downstream connection. If we don't delete it here, it will + // be pooled in on_stream_close_callback. + downstream->pop_downstream_connection(); + // dconn was deleted + dconn = nullptr; + + if (downstream->get_response_state() == Downstream::MSG_COMPLETE) { + // For SSL tunneling, we issue RST_STREAM. For other types of + // stream, we don't have to do anything since response was + // complete. + if (downstream->get_upgraded()) { + rst_stream(downstream, NGHTTP2_NO_ERROR); + } + } else { + if (downstream->get_response_state() == Downstream::HEADER_COMPLETE) { + if (downstream->get_upgraded()) { + on_downstream_body_complete(downstream); + } else { + rst_stream(downstream, NGHTTP2_INTERNAL_ERROR); + } + } else { + unsigned int status; + if (events & Downstream::EVENT_TIMEOUT) { + status = 504; + } else { + status = 502; + } + if (error_reply(downstream, status) != 0) { + return -1; + } + } + downstream->set_response_state(Downstream::MSG_COMPLETE); + } + handler_->signal_write(); + // At this point, downstream may be deleted. + return 0; +} + +int Http2Upstream::rst_stream(Downstream *downstream, uint32_t error_code) { + if (LOG_ENABLED(INFO)) { + ULOG(INFO, this) << "RST_STREAM stream_id=" << downstream->get_stream_id() + << " with error_code=" << error_code; + } + int rv; + rv = nghttp2_submit_rst_stream(session_, NGHTTP2_FLAG_NONE, + downstream->get_stream_id(), error_code); + if (rv < NGHTTP2_ERR_FATAL) { + ULOG(FATAL, this) << "nghttp2_submit_rst_stream() failed: " + << nghttp2_strerror(rv); + DIE(); + } + return 0; +} + +int Http2Upstream::terminate_session(uint32_t error_code) { + int rv; + rv = nghttp2_session_terminate_session(session_, error_code); + if (rv != 0) { + return -1; + } + return 0; +} + +namespace { +ssize_t downstream_data_read_callback(nghttp2_session *session, + int32_t stream_id, uint8_t *buf, + size_t length, uint32_t *data_flags, + nghttp2_data_source *source, + void *user_data) { + int rv; + auto downstream = static_cast(source->ptr); + auto upstream = static_cast(downstream->get_upstream()); + auto body = downstream->get_response_buf(); + assert(body); + + auto dconn = downstream->get_downstream_connection(); + + if (body->rleft() == 0 && dconn && + downstream->get_response_state() != Downstream::MSG_COMPLETE) { + // Try to read more if buffer is empty. This will help small + // buffer and make priority handling a bit better. + if (upstream->downstream_read(dconn) != 0) { + return NGHTTP2_ERR_CALLBACK_FAILURE; + } + } + + auto nread = body->remove(buf, length); + auto body_empty = body->rleft() == 0; + + if (body_empty && + downstream->get_response_state() == Downstream::MSG_COMPLETE) { + + *data_flags |= NGHTTP2_DATA_FLAG_EOF; + + if (!downstream->get_upgraded()) { + auto &trailers = downstream->get_response_trailers(); + if (!trailers.empty()) { + std::vector nva; + nva.reserve(trailers.size()); + http2::copy_headers_to_nva(nva, trailers); + if (!nva.empty()) { + rv = nghttp2_submit_trailer(session, stream_id, nva.data(), + nva.size()); + if (rv != 0) { + if (nghttp2_is_fatal(rv)) { + return NGHTTP2_ERR_CALLBACK_FAILURE; + } + } else { + *data_flags |= NGHTTP2_DATA_FLAG_NO_END_STREAM; + } + } + } + } + } + + if (body_empty) { + downstream->disable_upstream_wtimer(); + } else { + downstream->reset_upstream_wtimer(); + } + + if (nread > 0 && downstream->resume_read(SHRPX_NO_BUFFER, nread) != 0) { + return NGHTTP2_ERR_CALLBACK_FAILURE; + } + + if (nread == 0 && ((*data_flags) & NGHTTP2_DATA_FLAG_EOF) == 0) { + return NGHTTP2_ERR_DEFERRED; + } + + if (nread > 0) { + downstream->add_response_sent_bodylen(nread); + } + + return nread; +} +} // namespace + +int Http2Upstream::error_reply(Downstream *downstream, + unsigned int status_code) { + int rv; + auto html = http::create_error_html(status_code); + downstream->set_response_http_status(status_code); + auto body = downstream->get_response_buf(); + body->append(html.c_str(), html.size()); + downstream->set_response_state(Downstream::MSG_COMPLETE); + + nghttp2_data_provider data_prd; + data_prd.source.ptr = downstream; + data_prd.read_callback = downstream_data_read_callback; + + auto content_length = util::utos(html.size()); + auto status_code_str = util::utos(status_code); + auto nva = + make_array(http2::make_nv_ls(":status", status_code_str), + http2::make_nv_ll("content-type", "text/html; charset=UTF-8"), + http2::make_nv_lc("server", get_config()->server_name), + http2::make_nv_ls("content-length", content_length)); + + rv = nghttp2_submit_response(session_, downstream->get_stream_id(), + nva.data(), nva.size(), &data_prd); + if (rv < NGHTTP2_ERR_FATAL) { + ULOG(FATAL, this) << "nghttp2_submit_response() failed: " + << nghttp2_strerror(rv); + return -1; + } + + return 0; +} + +void +Http2Upstream::add_pending_downstream(std::unique_ptr downstream) { + downstream_queue_.add_pending(std::move(downstream)); +} + +void Http2Upstream::remove_downstream(Downstream *downstream) { + if (downstream->accesslog_ready()) { + handler_->write_accesslog(downstream); + } + + nghttp2_session_set_stream_user_data(session_, downstream->get_stream_id(), + nullptr); + + auto next_downstream = downstream_queue_.remove_and_get_blocked(downstream); + + if (next_downstream) { + initiate_downstream(next_downstream); + } +} + +// WARNING: Never call directly or indirectly nghttp2_session_send or +// nghttp2_session_recv. These calls may delete downstream. +int Http2Upstream::on_downstream_header_complete(Downstream *downstream) { + int rv; + + if (LOG_ENABLED(INFO)) { + if (downstream->get_non_final_response()) { + DLOG(INFO, downstream) << "HTTP non-final response header"; + } else { + DLOG(INFO, downstream) << "HTTP response header completed"; + } + } + + if (!get_config()->http2_proxy && !get_config()->client_proxy && + !get_config()->no_location_rewrite) { + downstream->rewrite_location_response_header( + downstream->get_request_http2_scheme()); + } + + size_t nheader = downstream->get_response_headers().size(); + auto nva = std::vector(); + // 3 means :status and possible server and via header field. + nva.reserve(nheader + 3 + get_config()->add_response_headers.size()); + std::string via_value; + auto response_status = util::utos(downstream->get_response_http_status()); + nva.push_back(http2::make_nv_ls(":status", response_status)); + + http2::copy_headers_to_nva(nva, downstream->get_response_headers()); + + if (downstream->get_non_final_response()) { + if (LOG_ENABLED(INFO)) { + log_response_headers(downstream, nva); + } + + rv = nghttp2_submit_headers(session_, NGHTTP2_FLAG_NONE, + downstream->get_stream_id(), nullptr, + nva.data(), nva.size(), nullptr); + + downstream->clear_response_headers(); + + if (rv != 0) { + ULOG(FATAL, this) << "nghttp2_submit_headers() failed"; + return -1; + } + + return 0; + } + + if (!get_config()->http2_proxy && !get_config()->client_proxy) { + nva.push_back(http2::make_nv_lc("server", get_config()->server_name)); + } else { + auto server = downstream->get_response_header(http2::HD_SERVER); + if (server) { + nva.push_back(http2::make_nv_ls("server", (*server).value)); + } + } + + auto via = downstream->get_response_header(http2::HD_VIA); + if (get_config()->no_via) { + if (via) { + nva.push_back(http2::make_nv_ls("via", (*via).value)); + } + } else { + if (via) { + via_value = (*via).value; + via_value += ", "; + } + via_value += http::create_via_header_value( + downstream->get_response_major(), downstream->get_response_minor()); + nva.push_back(http2::make_nv_ls("via", via_value)); + } + + for (auto &p : get_config()->add_response_headers) { + nva.push_back(http2::make_nv(p.first, p.second)); + } + + if (LOG_ENABLED(INFO)) { + log_response_headers(downstream, nva); + } + + if (get_config()->http2_upstream_dump_response_header) { + http2::dump_nv(get_config()->http2_upstream_dump_response_header, + nva.data(), nva.size()); + } + + nghttp2_data_provider data_prd; + data_prd.source.ptr = downstream; + data_prd.read_callback = downstream_data_read_callback; + + nghttp2_data_provider *data_prdptr; + + if (downstream->expect_response_body()) { + data_prdptr = &data_prd; + } else { + data_prdptr = nullptr; + } + + rv = nghttp2_submit_response(session_, downstream->get_stream_id(), + nva.data(), nva.size(), data_prdptr); + if (rv != 0) { + ULOG(FATAL, this) << "nghttp2_submit_response() failed"; + return -1; + } + + // We need some conditions that must be fulfilled to initiate server + // push. + // + // * Server push is disabled for http2 proxy, since incoming headers + // are mixed origins. We don't know how to reliably determine the + // authority yet. + // + // * If downstream is http/2, it is likely that PUSH_PROMISE is + // coming from there, so we don't initiate PUSH_RPOMISE here. + // + // * We need 200 response code for associated resource. This is too + // restrictive, we will review this later. + // + // * We requires GET or POST for associated resource. Probably we + // don't want to push for HEAD request. Not sure other methods + // are also eligible for push. + if (!get_config()->no_server_push && + get_config()->downstream_proto == PROTO_HTTP && + !get_config()->http2_proxy && (downstream->get_stream_id() % 2) && + downstream->get_response_header(http2::HD_LINK) && + downstream->get_response_http_status() == 200 && + (downstream->get_request_method() == "GET" || + downstream->get_request_method() == "POST")) { + + if (prepare_push_promise(downstream) != 0) { + return -1; + } + } + + return 0; +} + +// WARNING: Never call directly or indirectly nghttp2_session_send or +// nghttp2_session_recv. These calls may delete downstream. +int Http2Upstream::on_downstream_body(Downstream *downstream, + const uint8_t *data, size_t len, + bool flush) { + auto body = downstream->get_response_buf(); + body->append(data, len); + + if (flush) { + nghttp2_session_resume_data(session_, downstream->get_stream_id()); + + downstream->ensure_upstream_wtimer(); + } + + return 0; +} + +// WARNING: Never call directly or indirectly nghttp2_session_send or +// nghttp2_session_recv. These calls may delete downstream. +int Http2Upstream::on_downstream_body_complete(Downstream *downstream) { + if (LOG_ENABLED(INFO)) { + DLOG(INFO, downstream) << "HTTP response completed"; + } + + if (!downstream->validate_response_bodylen()) { + rst_stream(downstream, NGHTTP2_PROTOCOL_ERROR); + downstream->set_response_connection_close(true); + return 0; + } + + nghttp2_session_resume_data(session_, downstream->get_stream_id()); + downstream->ensure_upstream_wtimer(); + + return 0; +} + +bool Http2Upstream::get_flow_control() const { return flow_control_; } + +void Http2Upstream::pause_read(IOCtrlReason reason) {} + +int Http2Upstream::resume_read(IOCtrlReason reason, Downstream *downstream, + size_t consumed) { + if (get_flow_control()) { + assert(downstream->get_request_datalen() >= consumed); + + if (consume(downstream->get_stream_id(), consumed) != 0) { + return -1; + } + + downstream->dec_request_datalen(consumed); + } + + handler_->signal_write(); + return 0; +} + +int Http2Upstream::on_downstream_abort_request(Downstream *downstream, + unsigned int status_code) { + int rv; + + rv = error_reply(downstream, status_code); + + if (rv != 0) { + return -1; + } + + handler_->signal_write(); + return 0; +} + +int Http2Upstream::consume(int32_t stream_id, size_t len) { + int rv; + + rv = nghttp2_session_consume(session_, stream_id, len); + + if (rv != 0) { + ULOG(WARN, this) << "nghttp2_session_consume() returned error: " + << nghttp2_strerror(rv); + return -1; + } + + return 0; +} + +void +Http2Upstream::log_response_headers(Downstream *downstream, + const std::vector &nva) const { + std::stringstream ss; + for (auto &nv : nva) { + ss << TTY_HTTP_HD << nv.name << TTY_RST << ": " << nv.value << "\n"; + } + ULOG(INFO, this) << "HTTP response headers. stream_id=" + << downstream->get_stream_id() << "\n" << ss.str(); +} + +int Http2Upstream::on_timeout(Downstream *downstream) { + if (LOG_ENABLED(INFO)) { + ULOG(INFO, this) << "Stream timeout stream_id=" + << downstream->get_stream_id(); + } + + rst_stream(downstream, NGHTTP2_NO_ERROR); + + return 0; +} + +void Http2Upstream::on_handler_delete() { + for (auto d = downstream_queue_.get_downstreams(); d; d = d->dlnext) { + if (d->get_dispatch_state() == Downstream::DISPATCH_ACTIVE && + d->accesslog_ready()) { + handler_->write_accesslog(d); + } + } +} + +int Http2Upstream::on_downstream_reset(bool no_retry) { + int rv; + + for (auto downstream = downstream_queue_.get_downstreams(); downstream; + downstream = downstream->dlnext) { + if (downstream->get_dispatch_state() != Downstream::DISPATCH_ACTIVE) { + continue; + } + + if (!downstream->request_submission_ready()) { + rst_stream(downstream, NGHTTP2_INTERNAL_ERROR); + downstream->pop_downstream_connection(); + continue; + } + + downstream->pop_downstream_connection(); + + downstream->add_retry(); + + if (no_retry || downstream->no_more_retry()) { + goto fail; + } + + // downstream connection is clean; we can retry with new + // downstream connection. + + rv = downstream->attach_downstream_connection( + handler_->get_downstream_connection()); + if (rv != 0) { + goto fail; + } + + continue; + + fail: + if (on_downstream_abort_request(downstream, 503) != 0) { + return -1; + } + downstream->pop_downstream_connection(); + } + + handler_->signal_write(); + + return 0; +} + +int Http2Upstream::prepare_push_promise(Downstream *downstream) { + int rv; + http_parser_url u; + memset(&u, 0, sizeof(u)); + rv = http_parser_parse_url(downstream->get_request_path().c_str(), + downstream->get_request_path().size(), 0, &u); + if (rv != 0) { + return 0; + } + const char *base; + size_t baselen; + if (u.field_set & (1 << UF_PATH)) { + auto &f = u.field_data[UF_PATH]; + base = downstream->get_request_path().c_str() + f.off; + baselen = f.len; + } else { + base = "/"; + baselen = 1; + } + for (auto &kv : downstream->get_response_headers()) { + if (kv.token != http2::HD_LINK) { + continue; + } + for (auto &link : + http2::parse_link_header(kv.value.c_str(), kv.value.size())) { + auto link_url = link.uri.first; + auto link_urllen = link.uri.second - link.uri.first; + + const char *rel; + size_t rellen; + const char *relq = nullptr; + size_t relqlen = 0; + + http_parser_url v; + memset(&v, 0, sizeof(v)); + rv = http_parser_parse_url(link_url, link_urllen, 0, &v); + if (rv != 0) { + assert(link_urllen); + if (link_url[0] == '/') { + continue; + } + // treat link_url as relative URI. + auto end = std::find(link_url, link_url + link_urllen, '#'); + auto q = std::find(link_url, end, '?'); + rel = link_url; + rellen = q - link_url; + if (q != end) { + relq = q + 1; + relqlen = end - relq; + } + } else { + if (v.field_set & (1 << UF_HOST)) { + continue; + } + if (v.field_set & (1 << UF_PATH)) { + auto &f = v.field_data[UF_PATH]; + rel = link_url + f.off; + rellen = f.len; + } else { + rel = "/"; + rellen = 1; + } + + if (v.field_set & (1 << UF_QUERY)) { + auto &f = v.field_data[UF_QUERY]; + relq = link_url + f.off; + relqlen = f.len; + } + } + auto path = http2::path_join(base, baselen, nullptr, 0, rel, rellen, relq, + relqlen); + rv = submit_push_promise(path, downstream); + if (rv != 0) { + return -1; + } + } + } + return 0; +} + +int Http2Upstream::submit_push_promise(const std::string &path, + Downstream *downstream) { + int rv; + std::vector nva; + nva.reserve(downstream->get_request_headers().size()); + + // juse use "GET" for now + nva.push_back(http2::make_nv_ll(":method", "GET")); + nva.push_back( + http2::make_nv_ls(":scheme", downstream->get_request_http2_scheme())); + nva.push_back(http2::make_nv_ls(":path", path)); + auto &authority = downstream->get_request_http2_authority(); + if (!authority.empty()) { + nva.push_back(http2::make_nv_ls(":authority", authority)); + } + + for (auto &kv : downstream->get_request_headers()) { + switch (kv.token) { + // TODO generate referer + case http2::HD__AUTHORITY: + case http2::HD__SCHEME: + case http2::HD__METHOD: + case http2::HD__PATH: + continue; + case http2::HD_ACCEPT_ENCODING: + case http2::HD_ACCEPT_LANGUAGE: + case http2::HD_CACHE_CONTROL: + case http2::HD_HOST: + case http2::HD_USER_AGENT: + nva.push_back(http2::make_nv(kv.name, kv.value, kv.no_index)); + break; + } + } + + rv = nghttp2_submit_push_promise(session_, NGHTTP2_FLAG_NONE, + downstream->get_stream_id(), nva.data(), + nva.size(), nullptr); + + if (rv < 0) { + if (LOG_ENABLED(INFO)) { + ULOG(INFO, this) << "nghttp2_submit_push_promise() failed: " + << nghttp2_strerror(rv); + } + if (nghttp2_is_fatal(rv)) { + return -1; + } + return 0; + } + + if (LOG_ENABLED(INFO)) { + std::stringstream ss; + for (auto &nv : nva) { + ss << TTY_HTTP_HD << nv.name << TTY_RST << ": " << nv.value << "\n"; + } + ULOG(INFO, this) << "HTTP push request headers. promised_stream_id=" << rv + << "\n" << ss.str(); + } + + return 0; +} + +} // namespace shrpx diff --git a/src/shrpx_http2_upstream.h b/src/shrpx_http2_upstream.h new file mode 100644 index 0000000..ef8c0be --- /dev/null +++ b/src/shrpx_http2_upstream.h @@ -0,0 +1,121 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef SHRPX_HTTP2_UPSTREAM_H +#define SHRPX_HTTP2_UPSTREAM_H + +#include "shrpx.h" + +#include + +#include + +#include + +#include "shrpx_upstream.h" +#include "shrpx_downstream_queue.h" +#include "memchunk.h" + +using namespace nghttp2; + +namespace shrpx { + +class ClientHandler; +class HttpsUpstream; + +class Http2Upstream : public Upstream { +public: + Http2Upstream(ClientHandler *handler); + virtual ~Http2Upstream(); + virtual int on_read(); + virtual int on_write(); + virtual int on_timeout(Downstream *downstream); + virtual int on_downstream_abort_request(Downstream *downstream, + unsigned int status_code); + virtual ClientHandler *get_client_handler() const; + + virtual int downstream_read(DownstreamConnection *dconn); + virtual int downstream_write(DownstreamConnection *dconn); + virtual int downstream_eof(DownstreamConnection *dconn); + virtual int downstream_error(DownstreamConnection *dconn, int events); + + void add_pending_downstream(std::unique_ptr downstream); + void remove_downstream(Downstream *downstream); + + int rst_stream(Downstream *downstream, uint32_t error_code); + int terminate_session(uint32_t error_code); + int error_reply(Downstream *downstream, unsigned int status_code); + + virtual void pause_read(IOCtrlReason reason); + virtual int resume_read(IOCtrlReason reason, Downstream *downstream, + size_t consumed); + + virtual int on_downstream_header_complete(Downstream *downstream); + virtual int on_downstream_body(Downstream *downstream, const uint8_t *data, + size_t len, bool flush); + virtual int on_downstream_body_complete(Downstream *downstream); + + virtual void on_handler_delete(); + virtual int on_downstream_reset(bool no_retry); + + bool get_flow_control() const; + // Perform HTTP/2 upgrade from |upstream|. On success, this object + // takes ownership of the |upstream|. This function returns 0 if it + // succeeds, or -1. + int upgrade_upstream(HttpsUpstream *upstream); + void start_settings_timer(); + void stop_settings_timer(); + int consume(int32_t stream_id, size_t len); + void log_response_headers(Downstream *downstream, + const std::vector &nva) const; + void start_downstream(Downstream *downstream); + void initiate_downstream(Downstream *downstream); + + void submit_goaway(); + void check_shutdown(); + + int prepare_push_promise(Downstream *downstream); + int submit_push_promise(const std::string &path, Downstream *downstream); + + int on_request_headers(Downstream *downstream, const nghttp2_frame *frame); + +private: + std::unique_ptr pre_upstream_; + DownstreamQueue downstream_queue_; + ev_timer settings_timer_; + ev_timer shutdown_timer_; + ev_prepare prep_; + ClientHandler *handler_; + nghttp2_session *session_; + const uint8_t *data_pending_; + size_t data_pendinglen_; + bool flow_control_; + bool shutdown_handled_; +}; + +nghttp2_session_callbacks *create_http2_upstream_callbacks(); + +} // namespace shrpx + +#endif // SHRPX_HTTP2_UPSTREAM_H diff --git a/src/shrpx_http_downstream_connection.cc b/src/shrpx_http_downstream_connection.cc new file mode 100644 index 0000000..1840f75 --- /dev/null +++ b/src/shrpx_http_downstream_connection.cc @@ -0,0 +1,844 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "shrpx_http_downstream_connection.h" + +#include "shrpx_client_handler.h" +#include "shrpx_upstream.h" +#include "shrpx_downstream.h" +#include "shrpx_config.h" +#include "shrpx_error.h" +#include "shrpx_http.h" +#include "shrpx_log_config.h" +#include "shrpx_connect_blocker.h" +#include "shrpx_downstream_connection_pool.h" +#include "shrpx_worker.h" +#include "http2.h" +#include "util.h" + +using namespace nghttp2; + +namespace shrpx { + +namespace { +void timeoutcb(struct ev_loop *loop, ev_timer *w, int revents) { + auto conn = static_cast(w->data); + auto dconn = static_cast(conn->data); + + if (LOG_ENABLED(INFO)) { + DCLOG(INFO, dconn) << "Time out"; + } + + auto downstream = dconn->get_downstream(); + auto upstream = downstream->get_upstream(); + auto handler = upstream->get_client_handler(); + + // Do this so that dconn is not pooled + downstream->set_response_connection_close(true); + + if (upstream->downstream_error(dconn, Downstream::EVENT_TIMEOUT) != 0) { + delete handler; + } +} +} // namespace + +namespace { +void readcb(struct ev_loop *loop, ev_io *w, int revents) { + auto conn = static_cast(w->data); + auto dconn = static_cast(conn->data); + auto downstream = dconn->get_downstream(); + auto upstream = downstream->get_upstream(); + auto handler = upstream->get_client_handler(); + + if (upstream->downstream_read(dconn) != 0) { + delete handler; + } +} +} // namespace + +namespace { +void writecb(struct ev_loop *loop, ev_io *w, int revents) { + auto conn = static_cast(w->data); + auto dconn = static_cast(conn->data); + auto downstream = dconn->get_downstream(); + auto upstream = downstream->get_upstream(); + auto handler = upstream->get_client_handler(); + + if (upstream->downstream_write(dconn) != 0) { + delete handler; + } +} +} // namespace + +namespace { +void connectcb(struct ev_loop *loop, ev_io *w, int revents) { + auto conn = static_cast(w->data); + auto dconn = static_cast(conn->data); + auto downstream = dconn->get_downstream(); + auto upstream = downstream->get_upstream(); + auto handler = upstream->get_client_handler(); + if (dconn->on_connect() != 0) { + if (upstream->on_downstream_abort_request(downstream, 503) != 0) { + delete handler; + } + return; + } + writecb(loop, w, revents); +} +} // namespace + +HttpDownstreamConnection::HttpDownstreamConnection( + DownstreamConnectionPool *dconn_pool, struct ev_loop *loop) + : DownstreamConnection(dconn_pool), + conn_(loop, -1, nullptr, get_config()->downstream_write_timeout, + get_config()->downstream_read_timeout, 0, 0, 0, 0, connectcb, + readcb, timeoutcb, this), + ioctrl_(&conn_.rlimit), response_htp_{0}, addr_idx_(0), + connected_(false) {} + +HttpDownstreamConnection::~HttpDownstreamConnection() { + // Downstream and DownstreamConnection may be deleted + // asynchronously. + if (downstream_) { + downstream_->release_downstream_connection(); + } +} + +int HttpDownstreamConnection::attach_downstream(Downstream *downstream) { + if (LOG_ENABLED(INFO)) { + DCLOG(INFO, this) << "Attaching to DOWNSTREAM:" << downstream; + } + + if (conn_.fd == -1) { + auto connect_blocker = client_handler_->get_connect_blocker(); + + if (connect_blocker->blocked()) { + if (LOG_ENABLED(INFO)) { + DCLOG(INFO, this) + << "Downstream connection was blocked by connect_blocker"; + } + return -1; + } + + auto worker = client_handler_->get_worker(); + auto worker_stat = worker->get_worker_stat(); + auto end = worker_stat->next_downstream; + for (;;) { + auto i = worker_stat->next_downstream; + ++worker_stat->next_downstream; + worker_stat->next_downstream %= get_config()->downstream_addrs.size(); + + conn_.fd = util::create_nonblock_socket( + get_config()->downstream_addrs[i].addr.storage.ss_family); + + if (conn_.fd == -1) { + auto error = errno; + DCLOG(WARN, this) << "socket() failed; errno=" << error; + + connect_blocker->on_failure(); + + return SHRPX_ERR_NETWORK; + } + + int rv; + rv = connect(conn_.fd, &get_config()->downstream_addrs[i].addr.sa, + get_config()->downstream_addrs[i].addrlen); + if (rv != 0 && errno != EINPROGRESS) { + auto error = errno; + DCLOG(WARN, this) << "connect() failed; errno=" << error; + + connect_blocker->on_failure(); + close(conn_.fd); + conn_.fd = -1; + + if (end == worker_stat->next_downstream) { + return SHRPX_ERR_NETWORK; + } + + // Try again with the next downstream server + continue; + } + + if (LOG_ENABLED(INFO)) { + DCLOG(INFO, this) << "Connecting to downstream server"; + } + + addr_idx_ = i; + + ev_io_set(&conn_.wev, conn_.fd, EV_WRITE); + ev_io_set(&conn_.rev, conn_.fd, EV_READ); + + conn_.wlimit.startw(); + + break; + } + } + + downstream_ = downstream; + + http_parser_init(&response_htp_, HTTP_RESPONSE); + response_htp_.data = downstream_; + + ev_set_cb(&conn_.rev, readcb); + + conn_.rt.repeat = get_config()->downstream_read_timeout; + ev_timer_again(conn_.loop, &conn_.rt); + // TODO we should have timeout for connection establishment + ev_timer_again(conn_.loop, &conn_.wt); + + return 0; +} + +int HttpDownstreamConnection::push_request_headers() { + const char *authority = nullptr, *host = nullptr; + auto downstream_hostport = + get_config()->downstream_addrs[addr_idx_].hostport.get(); + + if (!get_config()->no_host_rewrite && !get_config()->http2_proxy && + !get_config()->client_proxy && + downstream_->get_request_method() != "CONNECT") { + if (!downstream_->get_request_http2_authority().empty()) { + authority = downstream_hostport; + } + if (downstream_->get_request_header(http2::HD_HOST)) { + host = downstream_hostport; + } + } else { + if (!downstream_->get_request_http2_authority().empty()) { + authority = downstream_->get_request_http2_authority().c_str(); + } + auto h = downstream_->get_request_header(http2::HD_HOST); + if (h) { + host = h->value.c_str(); + } + } + + if (!authority && !host) { + // upstream is HTTP/1.0. We use backend server's host + // nonetheless. + host = downstream_hostport; + } + + if (authority) { + downstream_->set_request_downstream_host(authority); + } else { + downstream_->set_request_downstream_host(host); + } + + downstream_->assemble_request_cookie(); + + // Assume that method and request path do not contain \r\n. + std::string hdrs = downstream_->get_request_method(); + hdrs += ' '; + if (downstream_->get_request_method() == "CONNECT") { + if (authority) { + hdrs += authority; + } else { + hdrs += downstream_->get_request_path(); + } + } else if (get_config()->http2_proxy || get_config()->client_proxy) { + // Construct absolute-form request target because we are going to + // send a request to a HTTP/1 proxy. + if (downstream_->get_request_http2_scheme().empty()) { + // this comes from HTTP/1 upstream, use http scheme. We don't + // support https forward link yet. + hdrs += "http://"; + } else { + hdrs += downstream_->get_request_http2_scheme(); + hdrs += "://"; + } + if (authority) { + hdrs += authority; + } else { + hdrs += host; + } + + // Server-wide OPTIONS takes following form in proxy request: + // + // OPTIONS http://example.org HTTP/1.1 + // + // Notice that no slash after authority. See + // http://tools.ietf.org/html/rfc7230#section-5.3.4 + if (downstream_->get_request_path() != "*") { + hdrs += downstream_->get_request_path(); + } + } else { + // No proxy case. + hdrs += downstream_->get_request_path(); + } + hdrs += " HTTP/1.1\r\nHost: "; + if (authority) { + hdrs += authority; + } else { + hdrs += host; + } + hdrs += "\r\n"; + + http2::build_http1_headers_from_headers(hdrs, + downstream_->get_request_headers()); + + if (!downstream_->get_assembled_request_cookie().empty()) { + hdrs += "Cookie: "; + hdrs += downstream_->get_assembled_request_cookie(); + hdrs += "\r\n"; + } + + if (downstream_->get_request_method() != "CONNECT" && + downstream_->get_request_http2_expect_body() && + !downstream_->get_request_header(http2::HD_CONTENT_LENGTH)) { + + downstream_->set_chunked_request(true); + hdrs += "Transfer-Encoding: chunked\r\n"; + } + + if (downstream_->get_request_connection_close()) { + hdrs += "Connection: close\r\n"; + } + auto xff = downstream_->get_request_header(http2::HD_X_FORWARDED_FOR); + if (get_config()->add_x_forwarded_for) { + hdrs += "X-Forwarded-For: "; + if (xff && !get_config()->strip_incoming_x_forwarded_for) { + hdrs += (*xff).value; + hdrs += ", "; + } + hdrs += client_handler_->get_ipaddr(); + hdrs += "\r\n"; + } else if (xff && !get_config()->strip_incoming_x_forwarded_for) { + hdrs += "X-Forwarded-For: "; + hdrs += (*xff).value; + hdrs += "\r\n"; + } + if (!get_config()->http2_proxy && !get_config()->client_proxy && + downstream_->get_request_method() != "CONNECT") { + hdrs += "X-Forwarded-Proto: "; + if (!downstream_->get_request_http2_scheme().empty()) { + hdrs += downstream_->get_request_http2_scheme(); + hdrs += "\r\n"; + } else if (client_handler_->get_ssl()) { + hdrs += "https\r\n"; + } else { + hdrs += "http\r\n"; + } + } + auto expect = downstream_->get_request_header(http2::HD_EXPECT); + if (expect && !util::strifind((*expect).value.c_str(), "100-continue")) { + hdrs += "Expect: "; + hdrs += (*expect).value; + hdrs += "\r\n"; + } + auto via = downstream_->get_request_header(http2::HD_VIA); + if (get_config()->no_via) { + if (via) { + hdrs += "Via: "; + hdrs += (*via).value; + hdrs += "\r\n"; + } + } else { + hdrs += "Via: "; + if (via) { + hdrs += (*via).value; + hdrs += ", "; + } + hdrs += http::create_via_header_value(downstream_->get_request_major(), + downstream_->get_request_minor()); + hdrs += "\r\n"; + } + + hdrs += "\r\n"; + if (LOG_ENABLED(INFO)) { + const char *hdrp; + std::string nhdrs; + if (log_config()->errorlog_tty) { + nhdrs = http::colorizeHeaders(hdrs.c_str()); + hdrp = nhdrs.c_str(); + } else { + hdrp = hdrs.c_str(); + } + DCLOG(INFO, this) << "HTTP request headers. stream_id=" + << downstream_->get_stream_id() << "\n" << hdrp; + } + auto output = downstream_->get_request_buf(); + output->append(hdrs.c_str(), hdrs.size()); + + signal_write(); + + return 0; +} + +int HttpDownstreamConnection::push_upload_data_chunk(const uint8_t *data, + size_t datalen) { + auto chunked = downstream_->get_chunked_request(); + auto output = downstream_->get_request_buf(); + + if (chunked) { + auto chunk_size_hex = util::utox(datalen); + output->append(chunk_size_hex.c_str(), chunk_size_hex.size()); + output->append("\r\n"); + } + + output->append(data, datalen); + + if (chunked) { + output->append("\r\n"); + } + + signal_write(); + + return 0; +} + +int HttpDownstreamConnection::end_upload_data() { + if (!downstream_->get_chunked_request()) { + return 0; + } + + auto output = downstream_->get_request_buf(); + auto &trailers = downstream_->get_request_trailers(); + if (trailers.empty()) { + output->append("0\r\n\r\n"); + } else { + output->append("0\r\n"); + std::string trailer_part; + http2::build_http1_headers_from_headers(trailer_part, trailers); + output->append(trailer_part.c_str(), trailer_part.size()); + output->append("\r\n"); + } + + signal_write(); + + return 0; +} + +namespace { +void idle_readcb(struct ev_loop *loop, ev_io *w, int revents) { + auto conn = static_cast(w->data); + auto dconn = static_cast(conn->data); + if (LOG_ENABLED(INFO)) { + DCLOG(INFO, dconn) << "Idle connection EOF"; + } + auto dconn_pool = dconn->get_dconn_pool(); + dconn_pool->remove_downstream_connection(dconn); + // dconn was deleted +} +} // namespace + +namespace { +void idle_timeoutcb(struct ev_loop *loop, ev_timer *w, int revents) { + auto conn = static_cast(w->data); + auto dconn = static_cast(conn->data); + if (LOG_ENABLED(INFO)) { + DCLOG(INFO, dconn) << "Idle connection timeout"; + } + auto dconn_pool = dconn->get_dconn_pool(); + dconn_pool->remove_downstream_connection(dconn); + // dconn was deleted +} +} // namespace + +void HttpDownstreamConnection::detach_downstream(Downstream *downstream) { + if (LOG_ENABLED(INFO)) { + DCLOG(INFO, this) << "Detaching from DOWNSTREAM:" << downstream; + } + downstream_ = nullptr; + ioctrl_.force_resume_read(); + + conn_.rlimit.startw(); + conn_.wlimit.stopw(); + + ev_set_cb(&conn_.rev, idle_readcb); + + ev_timer_stop(conn_.loop, &conn_.wt); + + conn_.rt.repeat = get_config()->downstream_idle_read_timeout; + ev_set_cb(&conn_.rt, idle_timeoutcb); + ev_timer_again(conn_.loop, &conn_.rt); +} + +void HttpDownstreamConnection::pause_read(IOCtrlReason reason) { + ioctrl_.pause_read(reason); +} + +int HttpDownstreamConnection::resume_read(IOCtrlReason reason, + size_t consumed) { + if (!downstream_->response_buf_full()) { + ioctrl_.resume_read(reason); + } + + return 0; +} + +void HttpDownstreamConnection::force_resume_read() { + ioctrl_.force_resume_read(); +} + +namespace { +int htp_msg_begincb(http_parser *htp) { + auto downstream = static_cast(htp->data); + + if (downstream->get_response_state() != Downstream::INITIAL) { + return -1; + } + + return 0; +} +} // namespace + +namespace { +int htp_hdrs_completecb(http_parser *htp) { + auto downstream = static_cast(htp->data); + auto upstream = downstream->get_upstream(); + int rv; + + downstream->set_response_http_status(htp->status_code); + downstream->set_response_major(htp->http_major); + downstream->set_response_minor(htp->http_minor); + + if (downstream->index_response_headers() != 0) { + downstream->set_response_state(Downstream::MSG_BAD_HEADER); + return -1; + } + + if (downstream->get_non_final_response()) { + // Reset content-length because we reuse same Downstream for the + // next response. + downstream->set_response_content_length(-1); + // For non-final response code, we just call + // on_downstream_header_complete() without changing response + // state. + rv = upstream->on_downstream_header_complete(downstream); + + if (rv != 0) { + return -1; + } + + return 0; + } + + downstream->set_response_connection_close(!http_should_keep_alive(htp)); + downstream->set_response_state(Downstream::HEADER_COMPLETE); + downstream->inspect_http1_response(); + downstream->check_upgrade_fulfilled(); + if (downstream->get_upgraded()) { + // content-length must be ignored for upgraded connection. + downstream->set_response_content_length(-1); + downstream->set_response_connection_close(true); + // transfer-encoding not applied to upgraded connection + downstream->set_chunked_response(false); + } + if (upstream->on_downstream_header_complete(downstream) != 0) { + return -1; + } + + if (downstream->get_upgraded()) { + // Upgrade complete, read until EOF in both ends + if (upstream->resume_read(SHRPX_NO_BUFFER, downstream, 0) != 0) { + return -1; + } + downstream->set_request_state(Downstream::HEADER_COMPLETE); + if (LOG_ENABLED(INFO)) { + LOG(INFO) << "HTTP upgrade success. stream_id=" + << downstream->get_stream_id(); + } + } + + unsigned int status = downstream->get_response_http_status(); + // Ignore the response body. HEAD response may contain + // Content-Length or Transfer-Encoding: chunked. Some server send + // 304 status code with nonzero Content-Length, but without response + // body. See + // https://tools.ietf.org/html/rfc7230#section-3.3 + + // TODO It seems that the cases other than HEAD are handled by + // http-parser. Need test. + return downstream->get_request_method() == "HEAD" || + (100 <= status && status <= 199) || status == 204 || + status == 304 + ? 1 + : 0; +} +} // namespace + +namespace { +int htp_hdr_keycb(http_parser *htp, const char *data, size_t len) { + auto downstream = static_cast(htp->data); + + if (downstream->get_response_headers_sum() + len > + get_config()->header_field_buffer) { + if (LOG_ENABLED(INFO)) { + DLOG(INFO, downstream) << "Too large header block size=" + << downstream->get_response_headers_sum() + len; + } + return -1; + } + + if (downstream->get_response_state() == Downstream::INITIAL) { + if (downstream->get_response_header_key_prev()) { + downstream->append_last_response_header_key(data, len); + } else { + if (downstream->get_response_headers().size() >= + get_config()->max_header_fields) { + if (LOG_ENABLED(INFO)) { + DLOG(INFO, downstream) + << "Too many header field num=" + << downstream->get_response_headers().size() + 1; + } + return -1; + } + downstream->add_response_header(std::string(data, len), ""); + } + } else { + // trailer part + if (downstream->get_response_trailer_key_prev()) { + downstream->append_last_response_trailer_key(data, len); + } else { + if (downstream->get_response_headers().size() >= + get_config()->max_header_fields) { + if (LOG_ENABLED(INFO)) { + DLOG(INFO, downstream) + << "Too many header field num=" + << downstream->get_response_headers().size() + 1; + } + return -1; + } + downstream->add_response_trailer(std::string(data, len), ""); + } + } + return 0; +} +} // namespace + +namespace { +int htp_hdr_valcb(http_parser *htp, const char *data, size_t len) { + auto downstream = static_cast(htp->data); + if (downstream->get_response_headers_sum() + len > + get_config()->header_field_buffer) { + if (LOG_ENABLED(INFO)) { + DLOG(INFO, downstream) << "Too large header block size=" + << downstream->get_response_headers_sum() + len; + } + return -1; + } + if (downstream->get_response_state() == Downstream::INITIAL) { + if (downstream->get_response_header_key_prev()) { + downstream->set_last_response_header_value(data, len); + } else { + downstream->append_last_response_header_value(data, len); + } + } else { + if (downstream->get_response_trailer_key_prev()) { + downstream->set_last_response_trailer_value(data, len); + } else { + downstream->append_last_response_trailer_value(data, len); + } + } + return 0; +} +} // namespace + +namespace { +int htp_bodycb(http_parser *htp, const char *data, size_t len) { + auto downstream = static_cast(htp->data); + + downstream->add_response_bodylen(len); + + return downstream->get_upstream()->on_downstream_body( + downstream, reinterpret_cast(data), len, true); +} +} // namespace + +namespace { +int htp_msg_completecb(http_parser *htp) { + auto downstream = static_cast(htp->data); + + if (downstream->get_non_final_response()) { + downstream->reset_response(); + + return 0; + } + + downstream->set_response_state(Downstream::MSG_COMPLETE); + // Block reading another response message from (broken?) + // server. This callback is not called if the connection is + // tunneled. + downstream->pause_read(SHRPX_MSG_BLOCK); + return downstream->get_upstream()->on_downstream_body_complete(downstream); +} +} // namespace + +namespace { +http_parser_settings htp_hooks = { + htp_msg_begincb, // http_cb on_message_begin; + nullptr, // http_data_cb on_url; + nullptr, // http_data_cb on_status; + htp_hdr_keycb, // http_data_cb on_header_field; + htp_hdr_valcb, // http_data_cb on_header_value; + htp_hdrs_completecb, // http_cb on_headers_complete; + htp_bodycb, // http_data_cb on_body; + htp_msg_completecb // http_cb on_message_complete; +}; +} // namespace + +int HttpDownstreamConnection::on_read() { + if (!connected_) { + return 0; + } + + ev_timer_again(conn_.loop, &conn_.rt); + std::array buf; + int rv; + + if (downstream_->get_upgraded()) { + // For upgraded connection, just pass data to the upstream. + for (;;) { + auto nread = conn_.read_clear(buf.data(), buf.size()); + + if (nread == 0) { + return 0; + } + + if (nread < 0) { + return nread; + } + + rv = downstream_->get_upstream()->on_downstream_body( + downstream_, buf.data(), nread, true); + if (rv != 0) { + return rv; + } + + if (downstream_->response_buf_full()) { + downstream_->pause_read(SHRPX_NO_BUFFER); + return 0; + } + } + } + + for (;;) { + auto nread = conn_.read_clear(buf.data(), buf.size()); + + if (nread == 0) { + return 0; + } + + if (nread < 0) { + return nread; + } + + auto nproc = + http_parser_execute(&response_htp_, &htp_hooks, + reinterpret_cast(buf.data()), nread); + + auto htperr = HTTP_PARSER_ERRNO(&response_htp_); + + if (htperr != HPE_OK) { + if (LOG_ENABLED(INFO)) { + DCLOG(INFO, this) << "HTTP parser failure: " + << "(" << http_errno_name(htperr) << ") " + << http_errno_description(htperr); + } + + return -1; + } + + if (nproc != static_cast(nread)) { + if (LOG_ENABLED(INFO)) { + DCLOG(INFO, this) << "nproc != nread"; + } + return -1; + } + + if (downstream_->response_buf_full()) { + downstream_->pause_read(SHRPX_NO_BUFFER); + return 0; + } + } +} + +int HttpDownstreamConnection::on_write() { + if (!connected_) { + return 0; + } + + ev_timer_again(conn_.loop, &conn_.rt); + + auto upstream = downstream_->get_upstream(); + auto input = downstream_->get_request_buf(); + + std::array iov; + + while (input->rleft() > 0) { + auto iovcnt = input->riovec(iov.data(), iov.size()); + + auto nwrite = conn_.writev_clear(iov.data(), iovcnt); + + if (nwrite == 0) { + return 0; + } + + if (nwrite < 0) { + return nwrite; + } + + input->drain(nwrite); + } + + conn_.wlimit.stopw(); + ev_timer_stop(conn_.loop, &conn_.wt); + + if (input->rleft() == 0) { + upstream->resume_read(SHRPX_NO_BUFFER, downstream_, + downstream_->get_request_datalen()); + } + + return 0; +} + +int HttpDownstreamConnection::on_connect() { + auto connect_blocker = client_handler_->get_connect_blocker(); + + if (!util::check_socket_connected(conn_.fd)) { + conn_.wlimit.stopw(); + + if (LOG_ENABLED(INFO)) { + DLOG(INFO, this) << "downstream connect failed"; + } + + return -1; + } + + connected_ = true; + + connect_blocker->on_success(); + + conn_.rlimit.startw(); + ev_set_cb(&conn_.wev, writecb); + + return 0; +} + +void HttpDownstreamConnection::on_upstream_change(Upstream *upstream) {} + +void HttpDownstreamConnection::signal_write() { conn_.wlimit.startw(); } + +} // namespace shrpx diff --git a/src/shrpx_http_downstream_connection.h b/src/shrpx_http_downstream_connection.h new file mode 100644 index 0000000..02480d5 --- /dev/null +++ b/src/shrpx_http_downstream_connection.h @@ -0,0 +1,78 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef SHRPX_HTTP_DOWNSTREAM_CONNECTION_H +#define SHRPX_HTTP_DOWNSTREAM_CONNECTION_H + +#include "shrpx.h" + +#include "http-parser/http_parser.h" + +#include "shrpx_downstream_connection.h" +#include "shrpx_io_control.h" +#include "shrpx_connection.h" + +namespace shrpx { + +class DownstreamConnectionPool; + +class HttpDownstreamConnection : public DownstreamConnection { +public: + HttpDownstreamConnection(DownstreamConnectionPool *dconn_pool, + struct ev_loop *loop); + virtual ~HttpDownstreamConnection(); + virtual int attach_downstream(Downstream *downstream); + virtual void detach_downstream(Downstream *downstream); + + virtual int push_request_headers(); + virtual int push_upload_data_chunk(const uint8_t *data, size_t datalen); + virtual int end_upload_data(); + + virtual void pause_read(IOCtrlReason reason); + virtual int resume_read(IOCtrlReason reason, size_t consumed); + virtual void force_resume_read(); + + virtual int on_read(); + virtual int on_write(); + + virtual void on_upstream_change(Upstream *upstream); + virtual int on_priority_change(int32_t pri) { return 0; } + + virtual bool poolable() const { return true; } + + int on_connect(); + void signal_write(); + +private: + Connection conn_; + IOControl ioctrl_; + http_parser response_htp_; + // index of get_config()->downstream_addrs this object is using + size_t addr_idx_; + bool connected_; +}; + +} // namespace shrpx + +#endif // SHRPX_HTTP_DOWNSTREAM_CONNECTION_H diff --git a/src/shrpx_https_upstream.cc b/src/shrpx_https_upstream.cc new file mode 100644 index 0000000..674f17b --- /dev/null +++ b/src/shrpx_https_upstream.cc @@ -0,0 +1,983 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "shrpx_https_upstream.h" + +#include +#include +#include + +#include "shrpx_client_handler.h" +#include "shrpx_downstream.h" +#include "shrpx_downstream_connection.h" +#include "shrpx_http.h" +#include "shrpx_config.h" +#include "shrpx_error.h" +#include "shrpx_log_config.h" +#include "shrpx_worker.h" +#include "http2.h" +#include "util.h" +#include "template.h" + +using namespace nghttp2; + +namespace shrpx { + +HttpsUpstream::HttpsUpstream(ClientHandler *handler) + : handler_(handler), current_header_length_(0), + ioctrl_(handler->get_rlimit()) { + http_parser_init(&htp_, HTTP_REQUEST); + htp_.data = this; +} + +HttpsUpstream::~HttpsUpstream() {} + +void HttpsUpstream::reset_current_header_length() { + current_header_length_ = 0; +} + +namespace { +int htp_msg_begin(http_parser *htp) { + auto upstream = static_cast(htp->data); + if (LOG_ENABLED(INFO)) { + ULOG(INFO, upstream) << "HTTP request started"; + } + upstream->reset_current_header_length(); + + auto handler = upstream->get_client_handler(); + + // TODO specify 0 as priority for now + upstream->attach_downstream( + make_unique(upstream, handler->get_mcpool(), 0, 0)); + return 0; +} +} // namespace + +namespace { +int htp_uricb(http_parser *htp, const char *data, size_t len) { + auto upstream = static_cast(htp->data); + auto downstream = upstream->get_downstream(); + if (downstream->get_request_headers_sum() + len > + get_config()->header_field_buffer) { + if (LOG_ENABLED(INFO)) { + ULOG(INFO, upstream) << "Too large URI size=" + << downstream->get_request_headers_sum() + len; + } + assert(downstream->get_request_state() == Downstream::INITIAL); + downstream->set_request_state(Downstream::HTTP1_REQUEST_HEADER_TOO_LARGE); + return -1; + } + downstream->add_request_headers_sum(len); + downstream->append_request_path(data, len); + return 0; +} +} // namespace + +namespace { +int htp_hdr_keycb(http_parser *htp, const char *data, size_t len) { + auto upstream = static_cast(htp->data); + auto downstream = upstream->get_downstream(); + if (downstream->get_request_headers_sum() + len > + get_config()->header_field_buffer) { + if (LOG_ENABLED(INFO)) { + ULOG(INFO, upstream) << "Too large header block size=" + << downstream->get_request_headers_sum() + len; + } + if (downstream->get_request_state() == Downstream::INITIAL) { + downstream->set_request_state(Downstream::HTTP1_REQUEST_HEADER_TOO_LARGE); + } + return -1; + } + if (downstream->get_request_state() == Downstream::INITIAL) { + if (downstream->get_request_header_key_prev()) { + downstream->append_last_request_header_key(data, len); + } else { + if (downstream->get_request_headers().size() >= + get_config()->max_header_fields) { + if (LOG_ENABLED(INFO)) { + ULOG(INFO, upstream) << "Too many header field num=" + << downstream->get_request_headers().size() + 1; + } + downstream->set_request_state( + Downstream::HTTP1_REQUEST_HEADER_TOO_LARGE); + return -1; + } + downstream->add_request_header(std::string(data, len), ""); + } + } else { + // trailer part + if (downstream->get_request_trailer_key_prev()) { + downstream->append_last_request_trailer_key(data, len); + } else { + if (downstream->get_request_headers().size() >= + get_config()->max_header_fields) { + if (LOG_ENABLED(INFO)) { + ULOG(INFO, upstream) << "Too many header field num=" + << downstream->get_request_headers().size() + 1; + } + return -1; + } + downstream->add_request_trailer(std::string(data, len), ""); + } + } + return 0; +} +} // namespace + +namespace { +int htp_hdr_valcb(http_parser *htp, const char *data, size_t len) { + auto upstream = static_cast(htp->data); + auto downstream = upstream->get_downstream(); + if (downstream->get_request_headers_sum() + len > + get_config()->header_field_buffer) { + if (LOG_ENABLED(INFO)) { + ULOG(INFO, upstream) << "Too large header block size=" + << downstream->get_request_headers_sum() + len; + } + if (downstream->get_request_state() == Downstream::INITIAL) { + downstream->set_request_state(Downstream::HTTP1_REQUEST_HEADER_TOO_LARGE); + } + return -1; + } + if (downstream->get_request_state() == Downstream::INITIAL) { + if (downstream->get_request_header_key_prev()) { + downstream->set_last_request_header_value(data, len); + } else { + downstream->append_last_request_header_value(data, len); + } + } else { + if (downstream->get_request_trailer_key_prev()) { + downstream->set_last_request_trailer_value(data, len); + } else { + downstream->append_last_request_trailer_value(data, len); + } + } + return 0; +} +} // namespace + +namespace { +void rewrite_request_host_path_from_uri(Downstream *downstream, const char *uri, + http_parser_url &u) { + assert(u.field_set & (1 << UF_HOST)); + + // As per https://tools.ietf.org/html/rfc7230#section-5.4, we + // rewrite host header field with authority component. + std::string authority; + http2::copy_url_component(authority, &u, UF_HOST, uri); + // TODO properly check IPv6 numeric address + if (authority.find(':') != std::string::npos) { + authority = '[' + authority; + authority += ']'; + } + if (u.field_set & (1 << UF_PORT)) { + authority += ':'; + authority += util::utos(u.port); + } + downstream->set_request_http2_authority(authority); + + std::string path; + if (u.field_set & (1 << UF_PATH)) { + http2::copy_url_component(path, &u, UF_PATH, uri); + } else if (downstream->get_request_method() == "OPTIONS") { + // Server-wide OPTIONS takes following form in proxy request: + // + // OPTIONS http://example.org HTTP/1.1 + // + // Notice that no slash after authority. See + // http://tools.ietf.org/html/rfc7230#section-5.3.4 + downstream->set_request_path("*"); + // we ignore query component here + return; + } else { + path = "/"; + } + if (u.field_set & (1 << UF_QUERY)) { + auto &fdata = u.field_data[UF_QUERY]; + path += '?'; + path.append(uri + fdata.off, fdata.len); + } + downstream->set_request_path(std::move(path)); + if (get_config()->http2_proxy || get_config()->client_proxy) { + std::string scheme; + http2::copy_url_component(scheme, &u, UF_SCHEMA, uri); + downstream->set_request_http2_scheme(std::move(scheme)); + } +} +} // namespace + +namespace { +int htp_hdrs_completecb(http_parser *htp) { + int rv; + auto upstream = static_cast(htp->data); + if (LOG_ENABLED(INFO)) { + ULOG(INFO, upstream) << "HTTP request headers completed"; + } + auto downstream = upstream->get_downstream(); + + downstream->set_request_method( + http_method_str((enum http_method)htp->method)); + downstream->set_request_major(htp->http_major); + downstream->set_request_minor(htp->http_minor); + + downstream->set_request_connection_close(!http_should_keep_alive(htp)); + + if (LOG_ENABLED(INFO)) { + std::stringstream ss; + ss << downstream->get_request_method() << " " + << downstream->get_request_path() << " " + << "HTTP/" << downstream->get_request_major() << "." + << downstream->get_request_minor() << "\n"; + const auto &headers = downstream->get_request_headers(); + for (size_t i = 0; i < headers.size(); ++i) { + ss << TTY_HTTP_HD << headers[i].name << TTY_RST << ": " + << headers[i].value << "\n"; + } + ULOG(INFO, upstream) << "HTTP request headers\n" << ss.str(); + } + + if (downstream->index_request_headers() != 0) { + return -1; + } + + if (downstream->get_request_major() == 1 && + downstream->get_request_minor() == 1 && + !downstream->get_request_header(http2::HD_HOST)) { + return -1; + } + + downstream->inspect_http1_request(); + + if (downstream->get_request_method() != "CONNECT") { + http_parser_url u{}; + // make a copy of request path, since we may set request path + // while we are refering to original request path. + auto uri = downstream->get_request_path(); + rv = http_parser_parse_url(uri.c_str(), + downstream->get_request_path().size(), 0, &u); + if (rv != 0) { + // Expect to respond with 400 bad request + return -1; + } + // checking UF_HOST could be redundant, but just in case ... + if (!(u.field_set & (1 << UF_SCHEMA)) || !(u.field_set & (1 << UF_HOST))) { + if (get_config()->http2_proxy || get_config()->client_proxy) { + // Request URI should be absolute-form for client proxy mode + return -1; + } + } else { + rewrite_request_host_path_from_uri(downstream, uri.c_str(), u); + } + } + + rv = downstream->attach_downstream_connection( + upstream->get_client_handler()->get_downstream_connection()); + + if (rv != 0) { + downstream->set_request_state(Downstream::CONNECT_FAIL); + + return -1; + } + + rv = downstream->push_request_headers(); + + if (rv != 0) { + return -1; + } + + downstream->set_request_state(Downstream::HEADER_COMPLETE); + + return 0; +} +} // namespace + +namespace { +int htp_bodycb(http_parser *htp, const char *data, size_t len) { + int rv; + auto upstream = static_cast(htp->data); + auto downstream = upstream->get_downstream(); + rv = downstream->push_upload_data_chunk( + reinterpret_cast(data), len); + if (rv != 0) { + return -1; + } + return 0; +} +} // namespace + +namespace { +int htp_msg_completecb(http_parser *htp) { + int rv; + auto upstream = static_cast(htp->data); + if (LOG_ENABLED(INFO)) { + ULOG(INFO, upstream) << "HTTP request completed"; + } + auto handler = upstream->get_client_handler(); + auto downstream = upstream->get_downstream(); + downstream->set_request_state(Downstream::MSG_COMPLETE); + rv = downstream->end_upload_data(); + if (rv != 0) { + return -1; + } + + if (handler->get_http2_upgrade_allowed() && + downstream->get_http2_upgrade_request() && + handler->perform_http2_upgrade(upstream) != 0) { + if (LOG_ENABLED(INFO)) { + ULOG(INFO, upstream) << "HTTP Upgrade to HTTP/2 failed"; + } + } + + // Stop further processing to complete this request + http_parser_pause(htp, 1); + return 0; +} +} // namespace + +namespace { +http_parser_settings htp_hooks = { + htp_msg_begin, // http_cb on_message_begin; + htp_uricb, // http_data_cb on_url; + nullptr, // http_data_cb on_status; + htp_hdr_keycb, // http_data_cb on_header_field; + htp_hdr_valcb, // http_data_cb on_header_value; + htp_hdrs_completecb, // http_cb on_headers_complete; + htp_bodycb, // http_data_cb on_body; + htp_msg_completecb // http_cb on_message_complete; +}; +} // namespace + +// on_read() does not consume all available data in input buffer if +// one http request is fully received. +int HttpsUpstream::on_read() { + auto rb = handler_->get_rb(); + auto rlimit = handler_->get_rlimit(); + auto downstream = get_downstream(); + + if (rb->rleft() == 0) { + return 0; + } + + // downstream can be nullptr here, because it is initialized in the + // callback chain called by http_parser_execute() + if (downstream && downstream->get_upgraded()) { + + auto rv = downstream->push_upload_data_chunk(rb->pos, rb->rleft()); + + if (rv != 0) { + return -1; + } + + rb->reset(); + rlimit->startw(); + + if (downstream->request_buf_full()) { + if (LOG_ENABLED(INFO)) { + ULOG(INFO, this) << "Downstream request buf is full"; + } + pause_read(SHRPX_NO_BUFFER); + + return 0; + } + + return 0; + } + + if (downstream) { + // To avoid reading next pipelined request + switch (downstream->get_request_state()) { + case Downstream::INITIAL: + case Downstream::HEADER_COMPLETE: + break; + default: + return 0; + } + } + + // http_parser_execute() does nothing once it entered error state. + auto nread = http_parser_execute( + &htp_, &htp_hooks, reinterpret_cast(rb->pos), rb->rleft()); + + rb->drain(nread); + rlimit->startw(); + + // Well, actually header length + some body bytes + current_header_length_ += nread; + + // Get downstream again because it may be initialized in http parser + // execution + downstream = get_downstream(); + + auto htperr = HTTP_PARSER_ERRNO(&htp_); + + if (htperr == HPE_PAUSED) { + return 0; + } + + if (htperr != HPE_OK) { + if (LOG_ENABLED(INFO)) { + ULOG(INFO, this) << "HTTP parse failure: " + << "(" << http_errno_name(htperr) << ") " + << http_errno_description(htperr); + } + + if (downstream && downstream->get_response_state() != Downstream::INITIAL) { + handler_->set_should_close_after_write(true); + handler_->signal_write(); + return 0; + } + + unsigned int status_code; + + if (downstream) { + if (downstream->get_request_state() == Downstream::CONNECT_FAIL) { + status_code = 503; + } else if (downstream->get_request_state() == + Downstream::HTTP1_REQUEST_HEADER_TOO_LARGE) { + status_code = 431; + } else { + status_code = 400; + } + } else { + status_code = 400; + } + + error_reply(status_code); + + handler_->signal_write(); + + return 0; + } + + // downstream can be NULL here. + if (downstream && downstream->request_buf_full()) { + if (LOG_ENABLED(INFO)) { + ULOG(INFO, this) << "Downstream request buffer is full"; + } + + pause_read(SHRPX_NO_BUFFER); + + return 0; + } + + return 0; +} + +int HttpsUpstream::on_write() { + auto downstream = get_downstream(); + if (!downstream) { + return 0; + } + auto wb = handler_->get_wb(); + if (wb->wleft() == 0) { + return 0; + } + + auto dconn = downstream->get_downstream_connection(); + auto output = downstream->get_response_buf(); + + if (output->rleft() == 0 && dconn && + downstream->get_response_state() != Downstream::MSG_COMPLETE) { + if (downstream->resume_read(SHRPX_NO_BUFFER, + downstream->get_response_datalen()) != 0) { + return -1; + } + + if (downstream_read(dconn) != 0) { + return -1; + } + } + + auto n = output->remove(wb->last, wb->wleft()); + wb->write(n); + + if (wb->rleft() > 0) { + return 0; + } + + // We need to postpone detachment until all data are sent so that + // we can notify nghttp2 library all data consumed. + if (downstream->get_response_state() == Downstream::MSG_COMPLETE) { + if (downstream->get_response_connection_close()) { + // Connection close + downstream->pop_downstream_connection(); + // dconn was deleted + } else { + // Keep-alive + downstream->detach_downstream_connection(); + } + // We need this if response ends before request. + if (downstream->get_request_state() == Downstream::MSG_COMPLETE) { + delete_downstream(); + return resume_read(SHRPX_NO_BUFFER, nullptr, 0); + } + } + + return downstream->resume_read(SHRPX_NO_BUFFER, + downstream->get_response_datalen()); +} + +int HttpsUpstream::on_event() { return 0; } + +ClientHandler *HttpsUpstream::get_client_handler() const { return handler_; } + +void HttpsUpstream::pause_read(IOCtrlReason reason) { + ioctrl_.pause_read(reason); +} + +int HttpsUpstream::resume_read(IOCtrlReason reason, Downstream *downstream, + size_t consumed) { + // downstream could be nullptr + if (downstream && downstream->request_buf_full()) { + return 0; + } + if (ioctrl_.resume_read(reason)) { + // Process remaining data in input buffer here because these bytes + // are not notified by readcb until new data arrive. + http_parser_pause(&htp_, 0); + return on_read(); + } + + return 0; +} + +int HttpsUpstream::downstream_read(DownstreamConnection *dconn) { + auto downstream = dconn->get_downstream(); + int rv; + + rv = downstream->on_read(); + + if (rv == SHRPX_ERR_EOF) { + return downstream_eof(dconn); + } + + if (rv < 0) { + return downstream_error(dconn, Downstream::EVENT_ERROR); + } + + if (downstream->get_response_state() == Downstream::MSG_RESET) { + return -1; + } + + if (downstream->get_response_state() == Downstream::MSG_BAD_HEADER) { + error_reply(502); + downstream->pop_downstream_connection(); + goto end; + } + + // Detach downstream connection early so that it could be reused + // without hitting server's request timeout. + if (downstream->get_response_state() == Downstream::MSG_COMPLETE && + !downstream->get_response_connection_close()) { + // Keep-alive + downstream->detach_downstream_connection(); + } + +end: + handler_->signal_write(); + + return 0; +} + +int HttpsUpstream::downstream_write(DownstreamConnection *dconn) { + int rv; + rv = dconn->on_write(); + if (rv == SHRPX_ERR_NETWORK) { + return downstream_error(dconn, Downstream::EVENT_ERROR); + } + + if (rv != 0) { + return -1; + } + + return 0; +} + +int HttpsUpstream::downstream_eof(DownstreamConnection *dconn) { + auto downstream = dconn->get_downstream(); + + if (LOG_ENABLED(INFO)) { + DCLOG(INFO, dconn) << "EOF"; + } + + if (downstream->get_response_state() == Downstream::MSG_COMPLETE) { + goto end; + } + + if (downstream->get_response_state() == Downstream::HEADER_COMPLETE) { + // Server may indicate the end of the request by EOF + if (LOG_ENABLED(INFO)) { + DCLOG(INFO, dconn) << "The end of the response body was indicated by " + << "EOF"; + } + on_downstream_body_complete(downstream); + downstream->set_response_state(Downstream::MSG_COMPLETE); + downstream->pop_downstream_connection(); + goto end; + } + + if (downstream->get_response_state() == Downstream::INITIAL) { + // we did not send any response headers, so we can reply error + // message. + if (LOG_ENABLED(INFO)) { + DCLOG(INFO, dconn) << "Return error reply"; + } + error_reply(502); + downstream->pop_downstream_connection(); + goto end; + } + + // Otherwise, we don't know how to recover from this situation. Just + // drop connection. + return -1; +end: + handler_->signal_write(); + + return 0; +} + +int HttpsUpstream::downstream_error(DownstreamConnection *dconn, int events) { + auto downstream = dconn->get_downstream(); + if (LOG_ENABLED(INFO)) { + if (events & Downstream::EVENT_ERROR) { + DCLOG(INFO, dconn) << "Network error/general error"; + } else { + DCLOG(INFO, dconn) << "Timeout"; + } + } + if (downstream->get_response_state() != Downstream::INITIAL) { + return -1; + } + + unsigned int status; + if (events & Downstream::EVENT_TIMEOUT) { + status = 504; + } else { + status = 502; + } + error_reply(status); + + downstream->pop_downstream_connection(); + + handler_->signal_write(); + return 0; +} + +void HttpsUpstream::error_reply(unsigned int status_code) { + auto html = http::create_error_html(status_code); + auto downstream = get_downstream(); + + if (!downstream) { + attach_downstream( + make_unique(this, handler_->get_mcpool(), 1, 1)); + downstream = get_downstream(); + } + + downstream->set_response_http_status(status_code); + // we are going to close connection for both frontend and backend in + // error condition. This is safest option. + downstream->set_response_connection_close(true); + handler_->set_should_close_after_write(true); + + auto output = downstream->get_response_buf(); + + output->append("HTTP/1.1 "); + auto status_str = http2::get_status_string(status_code); + output->append(status_str.c_str(), status_str.size()); + output->append("\r\nServer: "); + output->append(get_config()->server_name, strlen(get_config()->server_name)); + output->append("\r\nContent-Length: "); + auto cl = util::utos(html.size()); + output->append(cl.c_str(), cl.size()); + output->append("\r\nContent-Type: text/html; " + "charset=UTF-8\r\nConnection: close\r\n\r\n"); + output->append(html.c_str(), html.size()); + + downstream->add_response_sent_bodylen(html.size()); + downstream->set_response_state(Downstream::MSG_COMPLETE); +} + +void HttpsUpstream::attach_downstream(std::unique_ptr downstream) { + assert(!downstream_); + downstream_ = std::move(downstream); +} + +void HttpsUpstream::delete_downstream() { + if (downstream_ && downstream_->accesslog_ready()) { + handler_->write_accesslog(downstream_.get()); + } + + downstream_.reset(); +} + +Downstream *HttpsUpstream::get_downstream() const { return downstream_.get(); } + +std::unique_ptr HttpsUpstream::pop_downstream() { + return std::unique_ptr(downstream_.release()); +} + +int HttpsUpstream::on_downstream_header_complete(Downstream *downstream) { + if (LOG_ENABLED(INFO)) { + if (downstream->get_non_final_response()) { + DLOG(INFO, downstream) << "HTTP non-final response header"; + } else { + DLOG(INFO, downstream) << "HTTP response header completed"; + } + } + + std::string hdrs = "HTTP/"; + hdrs += util::utos(downstream->get_request_major()); + hdrs += "."; + hdrs += util::utos(downstream->get_request_minor()); + hdrs += " "; + hdrs += http2::get_status_string(downstream->get_response_http_status()); + hdrs += "\r\n"; + + if (!get_config()->http2_proxy && !get_config()->client_proxy && + !get_config()->no_location_rewrite) { + downstream->rewrite_location_response_header( + get_client_handler()->get_upstream_scheme()); + } + + http2::build_http1_headers_from_headers(hdrs, + downstream->get_response_headers()); + + auto output = downstream->get_response_buf(); + + if (downstream->get_non_final_response()) { + hdrs += "\r\n"; + + if (LOG_ENABLED(INFO)) { + log_response_headers(hdrs); + } + + output->append(hdrs.c_str(), hdrs.size()); + + downstream->clear_response_headers(); + + return 0; + } + + auto worker = handler_->get_worker(); + + // after graceful shutdown commenced, add connection: close header + // field. + if (worker->get_graceful_shutdown()) { + downstream->set_response_connection_close(true); + } + + // We check downstream->get_response_connection_close() in case when + // the Content-Length is not available. + if (!downstream->get_request_connection_close() && + !downstream->get_response_connection_close()) { + if (downstream->get_request_major() <= 0 || + downstream->get_request_minor() <= 0) { + // We add this header for HTTP/1.0 or HTTP/0.9 clients + hdrs += "Connection: Keep-Alive\r\n"; + } + } else if (!downstream->get_upgraded() || + downstream->get_request_method() != "CONNECT") { + hdrs += "Connection: close\r\n"; + } + + if (!downstream->get_response_header(http2::HD_ALT_SVC)) { + // We won't change or alter alt-svc from backend for now + if (!get_config()->altsvcs.empty()) { + hdrs += "Alt-Svc: "; + + for (auto &altsvc : get_config()->altsvcs) { + hdrs += util::percent_encode_token(altsvc.protocol_id); + hdrs += "=\""; + hdrs += util::quote_string(std::string(altsvc.host, altsvc.host_len)); + hdrs += ":"; + hdrs += util::utos(altsvc.port); + hdrs += "\", "; + } + + hdrs[hdrs.size() - 2] = '\r'; + hdrs[hdrs.size() - 1] = '\n'; + } + } + + if (!get_config()->http2_proxy && !get_config()->client_proxy) { + hdrs += "Server: "; + hdrs += get_config()->server_name; + hdrs += "\r\n"; + } else { + auto server = downstream->get_response_header(http2::HD_SERVER); + if (server) { + hdrs += "Server: "; + hdrs += (*server).value; + hdrs += "\r\n"; + } + } + + auto via = downstream->get_response_header(http2::HD_VIA); + if (get_config()->no_via) { + if (via) { + hdrs += "Via: "; + hdrs += (*via).value; + hdrs += "\r\n"; + } + } else { + hdrs += "Via: "; + if (via) { + hdrs += (*via).value; + hdrs += ", "; + } + hdrs += http::create_via_header_value(downstream->get_response_major(), + downstream->get_response_minor()); + hdrs += "\r\n"; + } + + for (auto &p : get_config()->add_response_headers) { + hdrs += p.first; + hdrs += ": "; + hdrs += p.second; + hdrs += "\r\n"; + } + + hdrs += "\r\n"; + + if (LOG_ENABLED(INFO)) { + log_response_headers(hdrs); + } + + output->append(hdrs.c_str(), hdrs.size()); + + return 0; +} + +int HttpsUpstream::on_downstream_body(Downstream *downstream, + const uint8_t *data, size_t len, + bool flush) { + if (len == 0) { + return 0; + } + auto output = downstream->get_response_buf(); + if (downstream->get_chunked_response()) { + auto chunk_size_hex = util::utox(len); + chunk_size_hex += "\r\n"; + + output->append(chunk_size_hex.c_str(), chunk_size_hex.size()); + } + output->append(data, len); + + downstream->add_response_sent_bodylen(len); + + if (downstream->get_chunked_response()) { + output->append("\r\n"); + } + return 0; +} + +int HttpsUpstream::on_downstream_body_complete(Downstream *downstream) { + if (downstream->get_chunked_response()) { + auto output = downstream->get_response_buf(); + auto &trailers = downstream->get_response_trailers(); + if (trailers.empty()) { + output->append("0\r\n\r\n"); + } else { + output->append("0\r\n"); + std::string trailer_part; + http2::build_http1_headers_from_headers(trailer_part, trailers); + output->append(trailer_part.c_str(), trailer_part.size()); + output->append("\r\n"); + } + } + if (LOG_ENABLED(INFO)) { + DLOG(INFO, downstream) << "HTTP response completed"; + } + + if (!downstream->validate_response_bodylen()) { + downstream->set_response_connection_close(true); + } + + if (downstream->get_request_connection_close() || + downstream->get_response_connection_close()) { + auto handler = get_client_handler(); + handler->set_should_close_after_write(true); + } + return 0; +} + +int HttpsUpstream::on_downstream_abort_request(Downstream *downstream, + unsigned int status_code) { + error_reply(status_code); + handler_->signal_write(); + return 0; +} + +void HttpsUpstream::log_response_headers(const std::string &hdrs) const { + const char *hdrp; + std::string nhdrs; + if (log_config()->errorlog_tty) { + nhdrs = http::colorizeHeaders(hdrs.c_str()); + hdrp = nhdrs.c_str(); + } else { + hdrp = hdrs.c_str(); + } + ULOG(INFO, this) << "HTTP response headers\n" << hdrp; +} + +void HttpsUpstream::on_handler_delete() { + if (downstream_ && downstream_->accesslog_ready()) { + handler_->write_accesslog(downstream_.get()); + } +} + +int HttpsUpstream::on_downstream_reset(bool no_retry) { + int rv; + + if (!downstream_->request_submission_ready()) { + // Return error so that caller can delete handler + return -1; + } + + downstream_->pop_downstream_connection(); + + downstream_->add_retry(); + + if (no_retry || downstream_->no_more_retry()) { + goto fail; + } + + rv = downstream_->attach_downstream_connection( + handler_->get_downstream_connection()); + if (rv != 0) { + goto fail; + } + + return 0; + +fail: + if (on_downstream_abort_request(downstream_.get(), 503) != 0) { + return -1; + } + downstream_->pop_downstream_connection(); + + return 0; +} + +} // namespace shrpx diff --git a/src/shrpx_https_upstream.h b/src/shrpx_https_upstream.h new file mode 100644 index 0000000..3b9d532 --- /dev/null +++ b/src/shrpx_https_upstream.h @@ -0,0 +1,91 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef SHRPX_HTTPS_UPSTREAM_H +#define SHRPX_HTTPS_UPSTREAM_H + +#include "shrpx.h" + +#include +#include + +#include "http-parser/http_parser.h" + +#include "shrpx_upstream.h" +#include "memchunk.h" + +using namespace nghttp2; + +namespace shrpx { + +class ClientHandler; + +class HttpsUpstream : public Upstream { +public: + HttpsUpstream(ClientHandler *handler); + virtual ~HttpsUpstream(); + virtual int on_read(); + virtual int on_write(); + virtual int on_event(); + virtual int on_downstream_abort_request(Downstream *downstream, + unsigned int status_code); + virtual ClientHandler *get_client_handler() const; + + virtual int downstream_read(DownstreamConnection *dconn); + virtual int downstream_write(DownstreamConnection *dconn); + virtual int downstream_eof(DownstreamConnection *dconn); + virtual int downstream_error(DownstreamConnection *dconn, int events); + + void attach_downstream(std::unique_ptr downstream); + void delete_downstream(); + Downstream *get_downstream() const; + std::unique_ptr pop_downstream(); + void error_reply(unsigned int status_code); + + virtual void pause_read(IOCtrlReason reason); + virtual int resume_read(IOCtrlReason reason, Downstream *downstream, + size_t consumed); + + virtual int on_downstream_header_complete(Downstream *downstream); + virtual int on_downstream_body(Downstream *downstream, const uint8_t *data, + size_t len, bool flush); + virtual int on_downstream_body_complete(Downstream *downstream); + + virtual void on_handler_delete(); + virtual int on_downstream_reset(bool no_retry); + + void reset_current_header_length(); + void log_response_headers(const std::string &hdrs) const; + +private: + ClientHandler *handler_; + http_parser htp_; + size_t current_header_length_; + std::unique_ptr downstream_; + IOControl ioctrl_; +}; + +} // namespace shrpx + +#endif // SHRPX_HTTPS_UPSTREAM_H diff --git a/src/shrpx_io_control.cc b/src/shrpx_io_control.cc new file mode 100644 index 0000000..f43a257 --- /dev/null +++ b/src/shrpx_io_control.cc @@ -0,0 +1,66 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "shrpx_io_control.h" + +#include + +#include "shrpx_rate_limit.h" +#include "util.h" + +using namespace nghttp2; + +namespace shrpx { + +IOControl::IOControl(RateLimit *lim) : lim_(lim), rdbits_(0) {} + +IOControl::~IOControl() {} + +void IOControl::pause_read(IOCtrlReason reason) { + rdbits_ |= reason; + if (lim_) { + lim_->stopw(); + } +} + +bool IOControl::resume_read(IOCtrlReason reason) { + rdbits_ &= ~reason; + if (rdbits_ == 0) { + if (lim_) { + lim_->startw(); + } + return true; + } + + return false; +} + +void IOControl::force_resume_read() { + rdbits_ = 0; + if (lim_) { + lim_->startw(); + } +} + +} // namespace shrpx diff --git a/src/shrpx_io_control.h b/src/shrpx_io_control.h new file mode 100644 index 0000000..9baf95a --- /dev/null +++ b/src/shrpx_io_control.h @@ -0,0 +1,57 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef SHRPX_IO_CONTROL_H +#define SHRPX_IO_CONTROL_H + +#include "shrpx.h" + +#include + +#include + +#include "shrpx_rate_limit.h" + +namespace shrpx { + +enum IOCtrlReason { SHRPX_NO_BUFFER = 1 << 0, SHRPX_MSG_BLOCK = 1 << 1 }; + +class IOControl { +public: + IOControl(RateLimit *lim); + ~IOControl(); + void pause_read(IOCtrlReason reason); + // Returns true if read operation is enabled after this call + bool resume_read(IOCtrlReason reason); + // Clear all pause flags and enable read + void force_resume_read(); + +private: + RateLimit *lim_; + uint32_t rdbits_; +}; + +} // namespace shrpx + +#endif // SHRPX_IO_CONTROL_H diff --git a/src/shrpx_log.cc b/src/shrpx_log.cc new file mode 100644 index 0000000..1446df6 --- /dev/null +++ b/src/shrpx_log.cc @@ -0,0 +1,343 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "shrpx_log.h" + +#ifdef HAVE_SYSLOG_H +#include +#endif // HAVE_SYSLOG_H +#ifdef HAVE_UNISTD_H +#include +#endif // HAVE_UNISTD_H +#ifdef HAVE_INTTYPES_H +#include +#endif // HAVE_INTTYPES_H + +#include +#include +#include +#include +#include +#include + +#include "shrpx_config.h" +#include "shrpx_downstream.h" +#include "util.h" +#include "template.h" + +using namespace nghttp2; + +namespace shrpx { + +namespace { +const char *SEVERITY_STR[] = {"INFO", "NOTICE", "WARN", "ERROR", "FATAL"}; +} // namespace + +namespace { +const char *SEVERITY_COLOR[] = { + "\033[1;32m", // INFO + "\033[1;36m", // NOTICE + "\033[1;33m", // WARN + "\033[1;31m", // ERROR + "\033[1;35m", // FATAL +}; +} // namespace + +int Log::severity_thres_ = NOTICE; + +void Log::set_severity_level(int severity) { severity_thres_ = severity; } + +int Log::set_severity_level_by_name(const char *name) { + for (size_t i = 0, max = array_size(SEVERITY_STR); i < max; ++i) { + if (strcmp(SEVERITY_STR[i], name) == 0) { + severity_thres_ = i; + return 0; + } + } + return -1; +} + +int severity_to_syslog_level(int severity) { + switch (severity) { + case (INFO): + return LOG_INFO; + case (NOTICE): + return LOG_NOTICE; + case (WARN): + return LOG_WARNING; + case (ERROR): + return LOG_ERR; + case (FATAL): + return LOG_CRIT; + default: + return -1; + } +} + +Log::Log(int severity, const char *filename, int linenum) + : filename_(filename), severity_(severity), linenum_(linenum) {} + +Log::~Log() { + int rv; + + if (!get_config()) { + return; + } + + auto lgconf = log_config(); + + if (!log_enabled(severity_) || + (lgconf->errorlog_fd == -1 && !get_config()->errorlog_syslog)) { + return; + } + + if (get_config()->errorlog_syslog) { + if (severity_ == NOTICE) { + syslog(severity_to_syslog_level(severity_), "[%s] %s", + SEVERITY_STR[severity_], stream_.str().c_str()); + } else { + syslog(severity_to_syslog_level(severity_), "[%s] %s (%s:%d)", + SEVERITY_STR[severity_], stream_.str().c_str(), filename_, + linenum_); + } + + return; + } + + char buf[4096]; + auto tty = lgconf->errorlog_tty; + + lgconf->update_tstamp(std::chrono::system_clock::now()); + auto &time_local = lgconf->time_local_str; + + if (severity_ == NOTICE) { + rv = snprintf(buf, sizeof(buf), "%s PID%d [%s%s%s] %s\n", + time_local.c_str(), get_config()->pid, + tty ? SEVERITY_COLOR[severity_] : "", SEVERITY_STR[severity_], + tty ? "\033[0m" : "", stream_.str().c_str()); + } else { + rv = snprintf(buf, sizeof(buf), "%s PID%d [%s%s%s] %s%s:%d%s %s\n", + time_local.c_str(), get_config()->pid, + tty ? SEVERITY_COLOR[severity_] : "", SEVERITY_STR[severity_], + tty ? "\033[0m" : "", tty ? "\033[1;30m" : "", filename_, + linenum_, tty ? "\033[0m" : "", stream_.str().c_str()); + } + + if (rv < 0) { + return; + } + + auto nwrite = std::min(static_cast(rv), sizeof(buf) - 1); + + while (write(lgconf->errorlog_fd, buf, nwrite) == -1 && errno == EINTR) + ; +} + +namespace { +template +std::pair copy(const char *src, size_t avail, + OutputIterator oitr) { + auto nwrite = std::min(strlen(src), avail); + auto noitr = std::copy_n(src, nwrite, oitr); + return std::make_pair(noitr, avail - nwrite); +} +} // namespace + +void upstream_accesslog(const std::vector &lfv, + const LogSpec &lgsp) { + auto lgconf = log_config(); + + if (lgconf->accesslog_fd == -1 && !get_config()->accesslog_syslog) { + return; + } + + char buf[4096]; + + auto downstream = lgsp.downstream; + + auto p = buf; + auto avail = sizeof(buf) - 2; + + lgconf->update_tstamp(lgsp.time_now); + auto &time_local = lgconf->time_local_str; + auto &time_iso8601 = lgconf->time_iso8601_str; + + for (auto &lf : lfv) { + switch (lf.type) { + case SHRPX_LOGF_LITERAL: + std::tie(p, avail) = copy(lf.value.get(), avail, p); + break; + case SHRPX_LOGF_REMOTE_ADDR: + std::tie(p, avail) = copy(lgsp.remote_addr, avail, p); + break; + case SHRPX_LOGF_TIME_LOCAL: + std::tie(p, avail) = copy(time_local.c_str(), avail, p); + break; + case SHRPX_LOGF_TIME_ISO8601: + std::tie(p, avail) = copy(time_iso8601.c_str(), avail, p); + break; + case SHRPX_LOGF_REQUEST: + std::tie(p, avail) = copy(lgsp.method, avail, p); + std::tie(p, avail) = copy(" ", avail, p); + std::tie(p, avail) = copy(lgsp.path, avail, p); + std::tie(p, avail) = copy(" HTTP/", avail, p); + std::tie(p, avail) = copy(util::utos(lgsp.major).c_str(), avail, p); + if (lgsp.major < 2) { + std::tie(p, avail) = copy(".", avail, p); + std::tie(p, avail) = copy(util::utos(lgsp.minor).c_str(), avail, p); + } + break; + case SHRPX_LOGF_STATUS: + std::tie(p, avail) = copy(util::utos(lgsp.status).c_str(), avail, p); + break; + case SHRPX_LOGF_BODY_BYTES_SENT: + std::tie(p, avail) = + copy(util::utos(lgsp.body_bytes_sent).c_str(), avail, p); + break; + case SHRPX_LOGF_HTTP: + if (downstream) { + auto hd = downstream->get_request_header(lf.value.get()); + if (hd) { + std::tie(p, avail) = copy((*hd).value.c_str(), avail, p); + break; + } + } + + std::tie(p, avail) = copy("-", avail, p); + + break; + case SHRPX_LOGF_REMOTE_PORT: + std::tie(p, avail) = copy(lgsp.remote_port, avail, p); + break; + case SHRPX_LOGF_SERVER_PORT: + std::tie(p, avail) = copy(util::utos(lgsp.server_port).c_str(), avail, p); + break; + case SHRPX_LOGF_REQUEST_TIME: { + auto t = std::chrono::duration_cast( + lgsp.request_end_time - lgsp.request_start_time).count(); + + auto frac = util::utos(t % 1000); + auto sec = util::utos(t / 1000); + if (frac.size() < 3) { + frac = std::string(3 - frac.size(), '0') + frac; + } + sec += "."; + sec += frac; + + std::tie(p, avail) = copy(sec.c_str(), avail, p); + } break; + case SHRPX_LOGF_PID: + std::tie(p, avail) = copy(util::utos(lgsp.pid).c_str(), avail, p); + break; + case SHRPX_LOGF_ALPN: + std::tie(p, avail) = copy(lgsp.alpn, avail, p); + break; + case SHRPX_LOGF_NONE: + break; + default: + break; + } + } + + *p = '\0'; + + if (get_config()->accesslog_syslog) { + syslog(LOG_INFO, "%s", buf); + + return; + } + + *p++ = '\n'; + + auto nwrite = p - buf; + while (write(lgconf->accesslog_fd, buf, nwrite) == -1 && errno == EINTR) + ; +} + +int reopen_log_files() { + int res = 0; + + auto lgconf = log_config(); + + if (lgconf->accesslog_fd != -1) { + close(lgconf->accesslog_fd); + lgconf->accesslog_fd = -1; + } + + if (!get_config()->accesslog_syslog && get_config()->accesslog_file) { + + lgconf->accesslog_fd = + util::reopen_log_file(get_config()->accesslog_file.get()); + + if (lgconf->accesslog_fd == -1) { + LOG(ERROR) << "Failed to open accesslog file " + << get_config()->accesslog_file.get(); + res = -1; + } + } + + int new_errorlog_fd = -1; + + if (!get_config()->errorlog_syslog && get_config()->errorlog_file) { + + new_errorlog_fd = util::reopen_log_file(get_config()->errorlog_file.get()); + + if (new_errorlog_fd == -1) { + if (lgconf->errorlog_fd != -1) { + LOG(ERROR) << "Failed to open errorlog file " + << get_config()->errorlog_file.get(); + } else { + std::cerr << "Failed to open errorlog file " + << get_config()->errorlog_file.get() << std::endl; + } + + res = -1; + } + } + + if (lgconf->errorlog_fd != -1) { + close(lgconf->errorlog_fd); + lgconf->errorlog_fd = -1; + lgconf->errorlog_tty = false; + } + + if (new_errorlog_fd != -1) { + lgconf->errorlog_fd = new_errorlog_fd; + lgconf->errorlog_tty = isatty(lgconf->errorlog_fd); + } + + return res; +} + +void redirect_stderr_to_errorlog() { + auto lgconf = log_config(); + + if (get_config()->errorlog_syslog || lgconf->errorlog_fd == -1) { + return; + } + + dup2(lgconf->errorlog_fd, STDERR_FILENO); +} + +} // namespace shrpx diff --git a/src/shrpx_log.h b/src/shrpx_log.h new file mode 100644 index 0000000..41645d8 --- /dev/null +++ b/src/shrpx_log.h @@ -0,0 +1,151 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef SHRPX_LOG_H +#define SHRPX_LOG_H + +#include "shrpx.h" + +#include + +#include +#include +#include +#include + +#include "shrpx_log_config.h" + +namespace shrpx { + +class Downstream; + +#define ENABLE_LOG 1 + +#define LOG_ENABLED(SEVERITY) (ENABLE_LOG && Log::log_enabled(SEVERITY)) + +#define LOG(SEVERITY) Log(SEVERITY, __FILE__, __LINE__) + +// Listener log +#define LLOG(SEVERITY, LISTEN) \ + (Log(SEVERITY, __FILE__, __LINE__) << "[LISTEN:" << LISTEN << "] ") + +// Worker log +#define WLOG(SEVERITY, WORKER) \ + (Log(SEVERITY, __FILE__, __LINE__) << "[WORKER:" << WORKER << "] ") + +// ClientHandler log +#define CLOG(SEVERITY, CLIENT_HANDLER) \ + (Log(SEVERITY, __FILE__, __LINE__) << "[CLIENT_HANDLER:" << CLIENT_HANDLER \ + << "] ") + +// Upstream log +#define ULOG(SEVERITY, UPSTREAM) \ + (Log(SEVERITY, __FILE__, __LINE__) << "[UPSTREAM:" << UPSTREAM << "] ") + +// Downstream log +#define DLOG(SEVERITY, DOWNSTREAM) \ + (Log(SEVERITY, __FILE__, __LINE__) << "[DOWNSTREAM:" << DOWNSTREAM << "] ") + +// Downstream connection log +#define DCLOG(SEVERITY, DCONN) \ + (Log(SEVERITY, __FILE__, __LINE__) << "[DCONN:" << DCONN << "] ") + +// Downstream HTTP2 session log +#define SSLOG(SEVERITY, HTTP2) \ + (Log(SEVERITY, __FILE__, __LINE__) << "[DHTTP2:" << HTTP2 << "] ") + +enum SeverityLevel { INFO, NOTICE, WARN, ERROR, FATAL }; + +class Log { +public: + Log(int severity, const char *filename, int linenum); + ~Log(); + template Log &operator<<(Type s) { + stream_ << s; + return *this; + } + static void set_severity_level(int severity); + static int set_severity_level_by_name(const char *name); + static bool log_enabled(int severity) { return severity >= severity_thres_; } + +private: + std::stringstream stream_; + const char *filename_; + int severity_; + int linenum_; + static int severity_thres_; +}; + +#define TTY_HTTP_HD (log_config()->errorlog_tty ? "\033[1;34m" : "") +#define TTY_RST (log_config()->errorlog_tty ? "\033[0m" : "") + +enum LogFragmentType { + SHRPX_LOGF_NONE, + SHRPX_LOGF_LITERAL, + SHRPX_LOGF_REMOTE_ADDR, + SHRPX_LOGF_TIME_LOCAL, + SHRPX_LOGF_TIME_ISO8601, + SHRPX_LOGF_REQUEST, + SHRPX_LOGF_STATUS, + SHRPX_LOGF_BODY_BYTES_SENT, + SHRPX_LOGF_HTTP, + SHRPX_LOGF_REMOTE_PORT, + SHRPX_LOGF_SERVER_PORT, + SHRPX_LOGF_REQUEST_TIME, + SHRPX_LOGF_PID, + SHRPX_LOGF_ALPN, +}; + +struct LogFragment { + LogFragmentType type; + std::unique_ptr value; +}; + +struct LogSpec { + Downstream *downstream; + const char *remote_addr; + const char *method; + const char *path; + const char *alpn; + std::chrono::system_clock::time_point time_now; + std::chrono::high_resolution_clock::time_point request_start_time; + std::chrono::high_resolution_clock::time_point request_end_time; + int major, minor; + unsigned int status; + int64_t body_bytes_sent; + const char *remote_port; + uint16_t server_port; + pid_t pid; +}; + +void upstream_accesslog(const std::vector &lf, + const LogSpec &lgsp); + +int reopen_log_files(); + +void redirect_stderr_to_errorlog(); + +} // namespace shrpx + +#endif // SHRPX_LOG_H diff --git a/src/shrpx_log_config.cc b/src/shrpx_log_config.cc new file mode 100644 index 0000000..6c21bc0 --- /dev/null +++ b/src/shrpx_log_config.cc @@ -0,0 +1,69 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2014 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "shrpx_log_config.h" +#include "util.h" + +using namespace nghttp2; + +namespace shrpx { + +LogConfig::LogConfig() + : accesslog_fd(-1), errorlog_fd(-1), errorlog_tty(false) {} + +#ifndef NOTHREADS +static pthread_key_t lckey; +static pthread_once_t lckey_once = PTHREAD_ONCE_INIT; + +static void make_key(void) { pthread_key_create(&lckey, NULL); } + +LogConfig *log_config(void) { + pthread_once(&lckey_once, make_key); + LogConfig *config = (LogConfig *)pthread_getspecific(lckey); + if (!config) { + config = new LogConfig(); + pthread_setspecific(lckey, config); + } + return config; +} +#else +static LogConfig *config = new LogConfig(); +LogConfig *log_config(void) { return config; } +#endif // NOTHREADS + +void +LogConfig::update_tstamp(const std::chrono::system_clock::time_point &now) { + auto t0 = std::chrono::system_clock::to_time_t(time_str_updated_); + auto t1 = std::chrono::system_clock::to_time_t(now); + if (t0 == t1) { + return; + } + + time_str_updated_ = now; + + time_local_str = util::format_common_log(now); + time_iso8601_str = util::format_iso8601(now); +} + +} // namespace shrpx diff --git a/src/shrpx_log_config.h b/src/shrpx_log_config.h new file mode 100644 index 0000000..46b6357 --- /dev/null +++ b/src/shrpx_log_config.h @@ -0,0 +1,53 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2014 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef SHRPX_LOG_CONFIG_H +#define SHRPX_LOG_CONFIG_H + +#include "shrpx.h" + +#include + +namespace shrpx { + +struct LogConfig { + std::chrono::system_clock::time_point time_str_updated_; + std::string time_local_str; + std::string time_iso8601_str; + int accesslog_fd; + int errorlog_fd; + // true if errorlog_fd is referring to a terminal. + bool errorlog_tty; + + LogConfig(); + void update_tstamp(const std::chrono::system_clock::time_point &now); +}; + +// We need LogConfig per thread to avoid data race around opening file +// descriptor for log files. +extern LogConfig *log_config(void); + +} // namespace shrpx + +#endif // SHRPX_LOG_CONFIG_H diff --git a/src/shrpx_rate_limit.cc b/src/shrpx_rate_limit.cc new file mode 100644 index 0000000..85836c1 --- /dev/null +++ b/src/shrpx_rate_limit.cc @@ -0,0 +1,109 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2015 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "shrpx_rate_limit.h" + +#include + +namespace shrpx { + +namespace { +void regencb(struct ev_loop *loop, ev_timer *w, int revents) { + auto r = static_cast(w->data); + r->regen(); +} +} // namespace + +RateLimit::RateLimit(struct ev_loop *loop, ev_io *w, size_t rate, size_t burst, + SSL *ssl) + : w_(w), loop_(loop), ssl_(ssl), rate_(rate), burst_(burst), avail_(burst), + startw_req_(false) { + ev_timer_init(&t_, regencb, 0., 1.); + t_.data = this; + if (rate_ > 0) { + ev_timer_again(loop_, &t_); + } +} + +RateLimit::~RateLimit() { ev_timer_stop(loop_, &t_); } + +size_t RateLimit::avail() const { + if (rate_ == 0) { + return std::numeric_limits::max(); + } + return avail_; +} + +void RateLimit::drain(size_t n) { + if (rate_ == 0) { + return; + } + n = std::min(avail_, n); + avail_ -= n; + if (avail_ == 0) { + ev_io_stop(loop_, w_); + } +} + +void RateLimit::regen() { + if (rate_ == 0) { + return; + } + if (avail_ + rate_ > burst_) { + avail_ = burst_; + } else { + avail_ += rate_; + } + + if (avail_ > 0 && startw_req_) { + ev_io_start(loop_, w_); + handle_tls_pending_read(); + } +} + +void RateLimit::startw() { + startw_req_ = true; + if (rate_ == 0 || avail_ > 0) { + ev_io_start(loop_, w_); + handle_tls_pending_read(); + return; + } +} + +void RateLimit::stopw() { + startw_req_ = false; + ev_io_stop(loop_, w_); +} + +void RateLimit::handle_tls_pending_read() { + if (!ssl_ || SSL_pending(ssl_) == 0) { + return; + } + + // Note that ev_feed_event works without starting watcher, but we + // only call this function if watcher is active. + ev_feed_event(loop_, w_, EV_READ); +} + +} // namespace shrpx diff --git a/src/shrpx_rate_limit.h b/src/shrpx_rate_limit.h new file mode 100644 index 0000000..4550d01 --- /dev/null +++ b/src/shrpx_rate_limit.h @@ -0,0 +1,65 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2015 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef SHRPX_RATE_LIMIT_H +#define SHRPX_RATE_LIMIT_H + +#include "shrpx.h" + +#include + +#include + +namespace shrpx { + +class RateLimit { +public: + // We need |ssl| object to check that it has unread decrypted bytes. + RateLimit(struct ev_loop *loop, ev_io *w, size_t rate, size_t burst, + SSL *ssl = nullptr); + ~RateLimit(); + size_t avail() const; + void drain(size_t n); + void regen(); + void startw(); + void stopw(); + // Feeds event if ssl_ object has unread decrypted bytes. This is + // required since it is buffered in ssl_ object, io event is not + // generated unless new incoming data is received. + void handle_tls_pending_read(); + +private: + ev_timer t_; + ev_io *w_; + struct ev_loop *loop_; + SSL *ssl_; + size_t rate_; + size_t burst_; + size_t avail_; + bool startw_req_; +}; + +} // namespace shrpx + +#endif // SHRPX_RATE_LIMIT_H diff --git a/src/shrpx_spdy_upstream.cc b/src/shrpx_spdy_upstream.cc new file mode 100644 index 0000000..a14b0ed --- /dev/null +++ b/src/shrpx_spdy_upstream.cc @@ -0,0 +1,1103 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "shrpx_spdy_upstream.h" + +#include +#include +#include +#include + +#include + +#include "shrpx_client_handler.h" +#include "shrpx_downstream.h" +#include "shrpx_downstream_connection.h" +#include "shrpx_config.h" +#include "shrpx_http.h" +#include "http2.h" +#include "util.h" +#include "template.h" + +using namespace nghttp2; + +namespace shrpx { + +namespace { +ssize_t send_callback(spdylay_session *session, const uint8_t *data, size_t len, + int flags, void *user_data) { + auto upstream = static_cast(user_data); + auto handler = upstream->get_client_handler(); + auto wb = handler->get_wb(); + + if (wb->wleft() == 0) { + return SPDYLAY_ERR_WOULDBLOCK; + } + + auto nread = wb->write(data, len); + + return nread; +} +} // namespace + +namespace { +ssize_t recv_callback(spdylay_session *session, uint8_t *buf, size_t len, + int flags, void *user_data) { + auto upstream = static_cast(user_data); + auto handler = upstream->get_client_handler(); + auto rb = handler->get_rb(); + auto rlimit = handler->get_rlimit(); + + if (rb->rleft() == 0) { + return SPDYLAY_ERR_WOULDBLOCK; + } + + auto nread = std::min(rb->rleft(), len); + + memcpy(buf, rb->pos, nread); + rb->drain(nread); + rlimit->startw(); + + return nread; +} +} // namespace + +namespace { +void on_stream_close_callback(spdylay_session *session, int32_t stream_id, + spdylay_status_code status_code, + void *user_data) { + auto upstream = static_cast(user_data); + if (LOG_ENABLED(INFO)) { + ULOG(INFO, upstream) << "Stream stream_id=" << stream_id + << " is being closed"; + } + auto downstream = static_cast( + spdylay_session_get_stream_user_data(session, stream_id)); + if (!downstream) { + return; + } + + upstream->consume(stream_id, downstream->get_request_datalen()); + + downstream->reset_request_datalen(); + + if (downstream->get_request_state() == Downstream::CONNECT_FAIL) { + upstream->remove_downstream(downstream); + // downstrea was deleted + + return; + } + + downstream->set_request_state(Downstream::STREAM_CLOSED); + if (downstream->get_response_state() == Downstream::MSG_COMPLETE) { + // At this point, downstream response was read + if (!downstream->get_upgraded() && + !downstream->get_response_connection_close()) { + // Keep-alive + downstream->detach_downstream_connection(); + } + upstream->remove_downstream(downstream); + // downstrea was deleted + + return; + } + + // At this point, downstream read may be paused. + + // If shrpx_downstream::push_request_headers() failed, the + // error is handled here. + upstream->remove_downstream(downstream); + // downstrea was deleted + + // How to test this case? Request sufficient large download + // and make client send RST_STREAM after it gets first DATA + // frame chunk. +} +} // namespace + +namespace { +void on_ctrl_recv_callback(spdylay_session *session, spdylay_frame_type type, + spdylay_frame *frame, void *user_data) { + auto upstream = static_cast(user_data); + switch (type) { + case SPDYLAY_SYN_STREAM: { + if (LOG_ENABLED(INFO)) { + ULOG(INFO, upstream) << "Received upstream SYN_STREAM stream_id=" + << frame->syn_stream.stream_id; + } + + auto downstream = upstream->add_pending_downstream( + frame->syn_stream.stream_id, frame->syn_stream.pri); + + downstream->reset_upstream_rtimer(); + + auto nv = frame->syn_stream.nv; + + if (LOG_ENABLED(INFO)) { + std::stringstream ss; + for (size_t i = 0; nv[i]; i += 2) { + ss << TTY_HTTP_HD << nv[i] << TTY_RST << ": " << nv[i + 1] << "\n"; + } + ULOG(INFO, upstream) << "HTTP request headers. stream_id=" + << downstream->get_stream_id() << "\n" << ss.str(); + } + + size_t num_headers = 0; + size_t header_buffer = 0; + for (size_t i = 0; nv[i]; i += 2) { + ++num_headers; + header_buffer += strlen(nv[i]) + strlen(nv[i + 1]); + } + + if (header_buffer > get_config()->header_field_buffer || + num_headers > get_config()->max_header_fields) { + upstream->rst_stream(downstream, SPDYLAY_INTERNAL_ERROR); + return; + } + + for (size_t i = 0; nv[i]; i += 2) { + downstream->add_request_header(nv[i], nv[i + 1]); + } + + if (downstream->index_request_headers() != 0) { + if (upstream->error_reply(downstream, 400) != 0) { + ULOG(FATAL, upstream) << "error_reply failed"; + } + return; + } + + auto path = downstream->get_request_header(http2::HD__PATH); + auto scheme = downstream->get_request_header(http2::HD__SCHEME); + auto host = downstream->get_request_header(http2::HD__HOST); + auto method = downstream->get_request_header(http2::HD__METHOD); + + bool is_connect = method && "CONNECT" == method->value; + if (!path || !host || !method || !http2::non_empty_value(host) || + !http2::non_empty_value(path) || !http2::non_empty_value(method) || + (!is_connect && (!scheme || !http2::non_empty_value(scheme)))) { + upstream->rst_stream(downstream, SPDYLAY_INTERNAL_ERROR); + return; + } + + downstream->set_request_method(method->value); + if (is_connect) { + downstream->set_request_http2_authority(path->value); + } else { + downstream->set_request_http2_scheme(scheme->value); + downstream->set_request_http2_authority(host->value); + downstream->set_request_path(path->value); + } + + if (!(frame->syn_stream.hd.flags & SPDYLAY_CTRL_FLAG_FIN)) { + downstream->set_request_http2_expect_body(true); + } + + downstream->inspect_http2_request(); + + downstream->set_request_state(Downstream::HEADER_COMPLETE); + if (frame->syn_stream.hd.flags & SPDYLAY_CTRL_FLAG_FIN) { + if (!downstream->validate_request_bodylen()) { + upstream->rst_stream(downstream, SPDYLAY_PROTOCOL_ERROR); + return; + } + + downstream->disable_upstream_rtimer(); + downstream->set_request_state(Downstream::MSG_COMPLETE); + } + + upstream->start_downstream(downstream); + + break; + } + default: + break; + } +} +} // namespace + +void SpdyUpstream::start_downstream(Downstream *downstream) { + if (downstream_queue_.can_activate( + downstream->get_request_http2_authority())) { + initiate_downstream(downstream); + return; + } + + downstream_queue_.mark_blocked(downstream); +} + +void SpdyUpstream::initiate_downstream(Downstream *downstream) { + int rv = downstream->attach_downstream_connection( + handler_->get_downstream_connection()); + if (rv != 0) { + // If downstream connection fails, issue RST_STREAM. + rst_stream(downstream, SPDYLAY_INTERNAL_ERROR); + downstream->set_request_state(Downstream::CONNECT_FAIL); + + downstream_queue_.mark_failure(downstream); + + return; + } + rv = downstream->push_request_headers(); + if (rv != 0) { + rst_stream(downstream, SPDYLAY_INTERNAL_ERROR); + + downstream_queue_.mark_failure(downstream); + + return; + } + + downstream_queue_.mark_active(downstream); +} + +namespace { +void on_data_chunk_recv_callback(spdylay_session *session, uint8_t flags, + int32_t stream_id, const uint8_t *data, + size_t len, void *user_data) { + auto upstream = static_cast(user_data); + auto downstream = static_cast( + spdylay_session_get_stream_user_data(session, stream_id)); + + if (!downstream) { + upstream->consume(stream_id, len); + + return; + } + + downstream->reset_upstream_rtimer(); + + if (downstream->push_upload_data_chunk(data, len) != 0) { + upstream->rst_stream(downstream, SPDYLAY_INTERNAL_ERROR); + + upstream->consume(stream_id, len); + + return; + } + + if (!upstream->get_flow_control()) { + return; + } + + // If connection-level window control is not enabled (e.g, + // spdy/3), spdylay_session_get_recv_data_length() is always + // returns 0. + if (spdylay_session_get_recv_data_length(session) > + std::max(SPDYLAY_INITIAL_WINDOW_SIZE, + 1 << get_config()->http2_upstream_connection_window_bits)) { + if (LOG_ENABLED(INFO)) { + ULOG(INFO, upstream) + << "Flow control error on connection: " + << "recv_window_size=" + << spdylay_session_get_recv_data_length(session) << ", window_size=" + << (1 << get_config()->http2_upstream_connection_window_bits); + } + spdylay_session_fail_session(session, SPDYLAY_GOAWAY_PROTOCOL_ERROR); + return; + } + if (spdylay_session_get_stream_recv_data_length(session, stream_id) > + std::max(SPDYLAY_INITIAL_WINDOW_SIZE, + 1 << get_config()->http2_upstream_window_bits)) { + if (LOG_ENABLED(INFO)) { + ULOG(INFO, upstream) << "Flow control error: recv_window_size=" + << spdylay_session_get_stream_recv_data_length( + session, stream_id) + << ", initial_window_size=" + << (1 << get_config()->http2_upstream_window_bits); + } + upstream->rst_stream(downstream, SPDYLAY_FLOW_CONTROL_ERROR); + return; + } +} +} // namespace + +namespace { +void on_data_recv_callback(spdylay_session *session, uint8_t flags, + int32_t stream_id, int32_t length, void *user_data) { + auto upstream = static_cast(user_data); + auto downstream = static_cast( + spdylay_session_get_stream_user_data(session, stream_id)); + if (downstream && (flags & SPDYLAY_DATA_FLAG_FIN)) { + if (!downstream->validate_request_bodylen()) { + upstream->rst_stream(downstream, SPDYLAY_PROTOCOL_ERROR); + return; + } + + downstream->disable_upstream_rtimer(); + downstream->end_upload_data(); + downstream->set_request_state(Downstream::MSG_COMPLETE); + } +} +} // namespace + +namespace { +void on_ctrl_not_send_callback(spdylay_session *session, + spdylay_frame_type type, spdylay_frame *frame, + int error_code, void *user_data) { + auto upstream = static_cast(user_data); + if (LOG_ENABLED(INFO)) { + ULOG(INFO, upstream) << "Failed to send control frame type=" << type + << ", error_code=" << error_code << ":" + << spdylay_strerror(error_code); + } + if (type == SPDYLAY_SYN_REPLY && error_code != SPDYLAY_ERR_STREAM_CLOSED && + error_code != SPDYLAY_ERR_STREAM_CLOSING) { + // To avoid stream hanging around, issue RST_STREAM. + auto stream_id = frame->syn_reply.stream_id; + // TODO Could be always nullptr + auto downstream = static_cast( + spdylay_session_get_stream_user_data(session, stream_id)); + if (downstream) { + upstream->rst_stream(downstream, SPDYLAY_INTERNAL_ERROR); + } + } +} +} // namespace + +namespace { +void on_ctrl_recv_parse_error_callback(spdylay_session *session, + spdylay_frame_type type, + const uint8_t *head, size_t headlen, + const uint8_t *payload, + size_t payloadlen, int error_code, + void *user_data) { + auto upstream = static_cast(user_data); + if (LOG_ENABLED(INFO)) { + ULOG(INFO, upstream) << "Failed to parse received control frame. type=" + << type << ", error_code=" << error_code << ":" + << spdylay_strerror(error_code); + } +} +} // namespace + +namespace { +void on_unknown_ctrl_recv_callback(spdylay_session *session, + const uint8_t *head, size_t headlen, + const uint8_t *payload, size_t payloadlen, + void *user_data) { + auto upstream = static_cast(user_data); + if (LOG_ENABLED(INFO)) { + ULOG(INFO, upstream) << "Received unknown control frame."; + } +} +} // namespace + +namespace { +// Infer upstream RST_STREAM status code from downstream HTTP/2 +// error code. +uint32_t infer_upstream_rst_stream_status_code(uint32_t downstream_error_code) { + // Only propagate *_REFUSED_STREAM so that upstream client can + // resend request. + if (downstream_error_code == NGHTTP2_REFUSED_STREAM) { + return SPDYLAY_REFUSED_STREAM; + } else { + return SPDYLAY_INTERNAL_ERROR; + } +} +} // namespace + +SpdyUpstream::SpdyUpstream(uint16_t version, ClientHandler *handler) + : downstream_queue_( + get_config()->http2_proxy + ? get_config()->downstream_connections_per_host + : get_config()->downstream_proto == PROTO_HTTP + ? get_config()->downstream_connections_per_frontend + : 0, + !get_config()->http2_proxy), + handler_(handler), session_(nullptr) { + spdylay_session_callbacks callbacks; + memset(&callbacks, 0, sizeof(callbacks)); + callbacks.send_callback = send_callback; + callbacks.recv_callback = recv_callback; + callbacks.on_stream_close_callback = on_stream_close_callback; + callbacks.on_ctrl_recv_callback = on_ctrl_recv_callback; + callbacks.on_data_chunk_recv_callback = on_data_chunk_recv_callback; + callbacks.on_data_recv_callback = on_data_recv_callback; + callbacks.on_ctrl_not_send_callback = on_ctrl_not_send_callback; + callbacks.on_ctrl_recv_parse_error_callback = + on_ctrl_recv_parse_error_callback; + callbacks.on_unknown_ctrl_recv_callback = on_unknown_ctrl_recv_callback; + + int rv; + rv = spdylay_session_server_new(&session_, version, &callbacks, this); + assert(rv == 0); + + uint32_t max_buffer = 65536; + rv = spdylay_session_set_option(session_, + SPDYLAY_OPT_MAX_RECV_CTRL_FRAME_BUFFER, + &max_buffer, sizeof(max_buffer)); + assert(rv == 0); + + if (version >= SPDYLAY_PROTO_SPDY3) { + int val = 1; + flow_control_ = true; + initial_window_size_ = 1 << get_config()->http2_upstream_window_bits; + rv = spdylay_session_set_option( + session_, SPDYLAY_OPT_NO_AUTO_WINDOW_UPDATE2, &val, sizeof(val)); + assert(rv == 0); + } else { + flow_control_ = false; + initial_window_size_ = 0; + } + // TODO Maybe call from outside? + std::array entry; + entry[0].settings_id = SPDYLAY_SETTINGS_MAX_CONCURRENT_STREAMS; + entry[0].value = get_config()->http2_max_concurrent_streams; + entry[0].flags = SPDYLAY_ID_FLAG_SETTINGS_NONE; + + entry[1].settings_id = SPDYLAY_SETTINGS_INITIAL_WINDOW_SIZE; + entry[1].value = initial_window_size_; + entry[1].flags = SPDYLAY_ID_FLAG_SETTINGS_NONE; + + rv = spdylay_submit_settings(session_, SPDYLAY_FLAG_SETTINGS_NONE, + entry.data(), entry.size()); + assert(rv == 0); + + if (version >= SPDYLAY_PROTO_SPDY3_1 && + get_config()->http2_upstream_connection_window_bits > 16) { + int32_t delta = (1 << get_config()->http2_upstream_connection_window_bits) - + SPDYLAY_INITIAL_WINDOW_SIZE; + rv = spdylay_submit_window_update(session_, 0, delta); + assert(rv == 0); + } + + handler_->reset_upstream_read_timeout( + get_config()->http2_upstream_read_timeout); + + handler_->signal_write(); +} + +SpdyUpstream::~SpdyUpstream() { spdylay_session_del(session_); } + +int SpdyUpstream::on_read() { + int rv = 0; + + rv = spdylay_session_recv(session_); + if (rv < 0) { + if (rv != SPDYLAY_ERR_EOF) { + ULOG(ERROR, this) << "spdylay_session_recv() returned error: " + << spdylay_strerror(rv); + } + return rv; + } + + handler_->signal_write(); + + return 0; +} + +// After this function call, downstream may be deleted. +int SpdyUpstream::on_write() { + int rv = 0; + + rv = spdylay_session_send(session_); + if (rv != 0) { + ULOG(ERROR, this) << "spdylay_session_send() returned error: " + << spdylay_strerror(rv); + return rv; + } + + if (spdylay_session_want_read(session_) == 0 && + spdylay_session_want_write(session_) == 0 && + handler_->get_wb()->rleft() == 0) { + if (LOG_ENABLED(INFO)) { + ULOG(INFO, this) << "No more read/write for this SPDY session"; + } + return -1; + } + return 0; +} + +ClientHandler *SpdyUpstream::get_client_handler() const { return handler_; } + +int SpdyUpstream::downstream_read(DownstreamConnection *dconn) { + auto downstream = dconn->get_downstream(); + + if (downstream->get_request_state() == Downstream::STREAM_CLOSED) { + // If upstream SPDY stream was closed, we just close downstream, + // because there is no consumer now. Downstream connection is also + // closed in this case. + remove_downstream(downstream); + // downstrea was deleted + + return 0; + } + + if (downstream->get_response_state() == Downstream::MSG_RESET) { + // The downstream stream was reset (canceled). In this case, + // RST_STREAM to the upstream and delete downstream connection + // here. Deleting downstream will be taken place at + // on_stream_close_callback. + rst_stream(downstream, + infer_upstream_rst_stream_status_code( + downstream->get_response_rst_stream_error_code())); + downstream->pop_downstream_connection(); + dconn = nullptr; + } else if (downstream->get_response_state() == Downstream::MSG_BAD_HEADER) { + if (error_reply(downstream, 502) != 0) { + return -1; + } + downstream->pop_downstream_connection(); + // dconn was deleted + dconn = nullptr; + } else { + auto rv = downstream->on_read(); + if (rv == SHRPX_ERR_EOF) { + return downstream_eof(dconn); + } + if (rv != 0) { + if (rv != SHRPX_ERR_NETWORK) { + if (LOG_ENABLED(INFO)) { + DCLOG(INFO, dconn) << "HTTP parser failure"; + } + } + return downstream_error(dconn, Downstream::EVENT_ERROR); + } + // Detach downstream connection early so that it could be reused + // without hitting server's request timeout. + if (downstream->get_response_state() == Downstream::MSG_COMPLETE && + !downstream->get_response_connection_close()) { + // Keep-alive + downstream->detach_downstream_connection(); + } + } + + handler_->signal_write(); + // At this point, downstream may be deleted. + + return 0; +} + +int SpdyUpstream::downstream_write(DownstreamConnection *dconn) { + int rv; + rv = dconn->on_write(); + if (rv == SHRPX_ERR_NETWORK) { + return downstream_error(dconn, Downstream::EVENT_ERROR); + } + if (rv != 0) { + return -1; + } + return 0; +} + +int SpdyUpstream::downstream_eof(DownstreamConnection *dconn) { + auto downstream = dconn->get_downstream(); + + if (LOG_ENABLED(INFO)) { + DCLOG(INFO, dconn) << "EOF. stream_id=" << downstream->get_stream_id(); + } + if (downstream->get_request_state() == Downstream::STREAM_CLOSED) { + // If stream was closed already, we don't need to send reply at + // the first place. We can delete downstream. + remove_downstream(downstream); + // downstream was deleted + + return 0; + } + + // Delete downstream connection. If we don't delete it here, it will + // be pooled in on_stream_close_callback. + downstream->pop_downstream_connection(); + // dconn was deleted + dconn = nullptr; + // downstream wil be deleted in on_stream_close_callback. + if (downstream->get_response_state() == Downstream::HEADER_COMPLETE) { + // Server may indicate the end of the request by EOF + if (LOG_ENABLED(INFO)) { + ULOG(INFO, this) << "Downstream body was ended by EOF"; + } + downstream->set_response_state(Downstream::MSG_COMPLETE); + + // For tunneled connection, MSG_COMPLETE signals + // downstream_data_read_callback to send RST_STREAM after pending + // response body is sent. This is needed to ensure that RST_STREAM + // is sent after all pending data are sent. + on_downstream_body_complete(downstream); + } else if (downstream->get_response_state() != Downstream::MSG_COMPLETE) { + // If stream was not closed, then we set MSG_COMPLETE and let + // on_stream_close_callback delete downstream. + if (error_reply(downstream, 502) != 0) { + return -1; + } + } + handler_->signal_write(); + // At this point, downstream may be deleted. + return 0; +} + +int SpdyUpstream::downstream_error(DownstreamConnection *dconn, int events) { + auto downstream = dconn->get_downstream(); + + if (LOG_ENABLED(INFO)) { + if (events & Downstream::EVENT_ERROR) { + DCLOG(INFO, dconn) << "Downstream network/general error"; + } else { + DCLOG(INFO, dconn) << "Timeout"; + } + if (downstream->get_upgraded()) { + DCLOG(INFO, dconn) << "Note: this is tunnel connection"; + } + } + + if (downstream->get_request_state() == Downstream::STREAM_CLOSED) { + remove_downstream(downstream); + // downstream was deleted + + return 0; + } + + // Delete downstream connection. If we don't delete it here, it will + // be pooled in on_stream_close_callback. + downstream->pop_downstream_connection(); + // dconn was deleted + dconn = nullptr; + + if (downstream->get_response_state() == Downstream::MSG_COMPLETE) { + // For SSL tunneling, we issue RST_STREAM. For other types of + // stream, we don't have to do anything since response was + // complete. + if (downstream->get_upgraded()) { + // We want "NO_ERROR" error code but SPDY does not have such + // code for RST_STREAM. + rst_stream(downstream, SPDYLAY_INTERNAL_ERROR); + } + } else { + if (downstream->get_response_state() == Downstream::HEADER_COMPLETE) { + if (downstream->get_upgraded()) { + on_downstream_body_complete(downstream); + } else { + rst_stream(downstream, SPDYLAY_INTERNAL_ERROR); + } + } else { + unsigned int status; + if (events & Downstream::EVENT_TIMEOUT) { + status = 504; + } else { + status = 502; + } + if (error_reply(downstream, status) != 0) { + return -1; + } + } + downstream->set_response_state(Downstream::MSG_COMPLETE); + } + handler_->signal_write(); + // At this point, downstream may be deleted. + return 0; +} + +int SpdyUpstream::rst_stream(Downstream *downstream, int status_code) { + if (LOG_ENABLED(INFO)) { + ULOG(INFO, this) << "RST_STREAM stream_id=" << downstream->get_stream_id(); + } + int rv; + rv = spdylay_submit_rst_stream(session_, downstream->get_stream_id(), + status_code); + if (rv < SPDYLAY_ERR_FATAL) { + ULOG(FATAL, this) << "spdylay_submit_rst_stream() failed: " + << spdylay_strerror(rv); + DIE(); + } + return 0; +} + +namespace { +ssize_t spdy_data_read_callback(spdylay_session *session, int32_t stream_id, + uint8_t *buf, size_t length, int *eof, + spdylay_data_source *source, void *user_data) { + auto downstream = static_cast(source->ptr); + auto upstream = static_cast(downstream->get_upstream()); + auto body = downstream->get_response_buf(); + assert(body); + + auto dconn = downstream->get_downstream_connection(); + + if (body->rleft() == 0 && dconn && + downstream->get_response_state() != Downstream::MSG_COMPLETE) { + // Try to read more if buffer is empty. This will help small + // buffer and make priority handling a bit better. + if (upstream->downstream_read(dconn) != 0) { + return SPDYLAY_ERR_CALLBACK_FAILURE; + } + } + + auto nread = body->remove(buf, length); + auto body_empty = body->rleft() == 0; + + if (nread == 0 && + downstream->get_response_state() == Downstream::MSG_COMPLETE) { + if (!downstream->get_upgraded()) { + *eof = 1; + } else { + // For tunneling, issue RST_STREAM to finish the stream. + if (LOG_ENABLED(INFO)) { + ULOG(INFO, upstream) + << "RST_STREAM to tunneled stream stream_id=" << stream_id; + } + upstream->rst_stream( + downstream, infer_upstream_rst_stream_status_code( + downstream->get_response_rst_stream_error_code())); + } + } + + if (body_empty) { + downstream->disable_upstream_wtimer(); + } else { + downstream->reset_upstream_wtimer(); + } + + if (nread > 0 && downstream->resume_read(SHRPX_NO_BUFFER, nread) != 0) { + return SPDYLAY_ERR_CALLBACK_FAILURE; + } + + if (nread == 0 && *eof != 1) { + return SPDYLAY_ERR_DEFERRED; + } + + if (nread > 0) { + downstream->add_response_sent_bodylen(nread); + } + + return nread; +} +} // namespace + +int SpdyUpstream::error_reply(Downstream *downstream, + unsigned int status_code) { + int rv; + auto html = http::create_error_html(status_code); + downstream->set_response_http_status(status_code); + auto body = downstream->get_response_buf(); + body->append(html.c_str(), html.size()); + downstream->set_response_state(Downstream::MSG_COMPLETE); + + spdylay_data_provider data_prd; + data_prd.source.ptr = downstream; + data_prd.read_callback = spdy_data_read_callback; + + std::string content_length = util::utos(html.size()); + std::string status_string = http2::get_status_string(status_code); + const char *nv[] = {":status", status_string.c_str(), + ":version", "http/1.1", + "content-type", "text/html; charset=UTF-8", + "server", get_config()->server_name, + "content-length", content_length.c_str(), + nullptr}; + + rv = spdylay_submit_response(session_, downstream->get_stream_id(), nv, + &data_prd); + if (rv < SPDYLAY_ERR_FATAL) { + ULOG(FATAL, this) << "spdylay_submit_response() failed: " + << spdylay_strerror(rv); + return -1; + } + + return 0; +} + +Downstream *SpdyUpstream::add_pending_downstream(int32_t stream_id, + int32_t priority) { + auto downstream = make_unique(this, handler_->get_mcpool(), + stream_id, priority); + spdylay_session_set_stream_user_data(session_, stream_id, downstream.get()); + auto res = downstream.get(); + + downstream_queue_.add_pending(std::move(downstream)); + + return res; +} + +void SpdyUpstream::remove_downstream(Downstream *downstream) { + if (downstream->accesslog_ready()) { + handler_->write_accesslog(downstream); + } + + spdylay_session_set_stream_user_data(session_, downstream->get_stream_id(), + nullptr); + + auto next_downstream = downstream_queue_.remove_and_get_blocked(downstream); + + if (next_downstream) { + initiate_downstream(next_downstream); + } +} + +// WARNING: Never call directly or indirectly spdylay_session_send or +// spdylay_session_recv. These calls may delete downstream. +int SpdyUpstream::on_downstream_header_complete(Downstream *downstream) { + if (downstream->get_non_final_response()) { + // SPDY does not support non-final response. We could send it + // with HEADERS and final response in SYN_REPLY, but it is not + // official way. + downstream->clear_response_headers(); + + return 0; + } + + if (LOG_ENABLED(INFO)) { + DLOG(INFO, downstream) << "HTTP response header completed"; + } + + if (!get_config()->http2_proxy && !get_config()->client_proxy && + !get_config()->no_location_rewrite) { + downstream->rewrite_location_response_header( + downstream->get_request_http2_scheme()); + } + size_t nheader = downstream->get_response_headers().size(); + // 8 means server, :status, :version and possible via header field. + auto nv = make_unique( + nheader * 2 + 8 + get_config()->add_response_headers.size() * 2 + 1); + + size_t hdidx = 0; + std::string via_value; + std::string status_string = + http2::get_status_string(downstream->get_response_http_status()); + nv[hdidx++] = ":status"; + nv[hdidx++] = status_string.c_str(); + nv[hdidx++] = ":version"; + nv[hdidx++] = "HTTP/1.1"; + for (auto &hd : downstream->get_response_headers()) { + if (hd.name.empty() || hd.name.c_str()[0] == ':') { + continue; + } + switch (hd.token) { + case http2::HD_CONNECTION: + case http2::HD_KEEP_ALIVE: + case http2::HD_PROXY_CONNECTION: + case http2::HD_TRANSFER_ENCODING: + case http2::HD_VIA: + case http2::HD_SERVER: + continue; + } + + nv[hdidx++] = hd.name.c_str(); + nv[hdidx++] = hd.value.c_str(); + } + + if (!get_config()->http2_proxy && !get_config()->client_proxy) { + nv[hdidx++] = "server"; + nv[hdidx++] = get_config()->server_name; + } else { + auto server = downstream->get_response_header(http2::HD_SERVER); + if (server) { + nv[hdidx++] = "server"; + nv[hdidx++] = server->value.c_str(); + } + } + + auto via = downstream->get_response_header(http2::HD_VIA); + if (get_config()->no_via) { + if (via) { + nv[hdidx++] = "via"; + nv[hdidx++] = via->value.c_str(); + } + } else { + if (via) { + via_value = via->value; + via_value += ", "; + } + via_value += http::create_via_header_value( + downstream->get_response_major(), downstream->get_response_minor()); + nv[hdidx++] = "via"; + nv[hdidx++] = via_value.c_str(); + } + + for (auto &p : get_config()->add_response_headers) { + nv[hdidx++] = p.first.c_str(); + nv[hdidx++] = p.second.c_str(); + } + + nv[hdidx++] = 0; + if (LOG_ENABLED(INFO)) { + std::stringstream ss; + for (size_t i = 0; nv[i]; i += 2) { + ss << TTY_HTTP_HD << nv[i] << TTY_RST << ": " << nv[i + 1] << "\n"; + } + ULOG(INFO, this) << "HTTP response headers. stream_id=" + << downstream->get_stream_id() << "\n" << ss.str(); + } + spdylay_data_provider data_prd; + data_prd.source.ptr = downstream; + data_prd.read_callback = spdy_data_read_callback; + + int rv; + rv = spdylay_submit_response(session_, downstream->get_stream_id(), nv.get(), + &data_prd); + if (rv != 0) { + ULOG(FATAL, this) << "spdylay_submit_response() failed"; + return -1; + } + + return 0; +} + +// WARNING: Never call directly or indirectly spdylay_session_send or +// spdylay_session_recv. These calls may delete downstream. +int SpdyUpstream::on_downstream_body(Downstream *downstream, + const uint8_t *data, size_t len, + bool flush) { + auto body = downstream->get_response_buf(); + body->append(data, len); + + if (flush) { + spdylay_session_resume_data(session_, downstream->get_stream_id()); + + downstream->ensure_upstream_wtimer(); + } + + return 0; +} + +// WARNING: Never call directly or indirectly spdylay_session_send or +// spdylay_session_recv. These calls may delete downstream. +int SpdyUpstream::on_downstream_body_complete(Downstream *downstream) { + if (LOG_ENABLED(INFO)) { + DLOG(INFO, downstream) << "HTTP response completed"; + } + + if (!downstream->validate_response_bodylen()) { + rst_stream(downstream, SPDYLAY_PROTOCOL_ERROR); + downstream->set_response_connection_close(true); + return 0; + } + + spdylay_session_resume_data(session_, downstream->get_stream_id()); + downstream->ensure_upstream_wtimer(); + + return 0; +} + +bool SpdyUpstream::get_flow_control() const { return flow_control_; } + +void SpdyUpstream::pause_read(IOCtrlReason reason) {} + +int SpdyUpstream::resume_read(IOCtrlReason reason, Downstream *downstream, + size_t consumed) { + if (get_flow_control()) { + assert(downstream->get_request_datalen() >= consumed); + + if (consume(downstream->get_stream_id(), consumed) != 0) { + return -1; + } + + downstream->dec_request_datalen(consumed); + } + + handler_->signal_write(); + return 0; +} + +int SpdyUpstream::on_downstream_abort_request(Downstream *downstream, + unsigned int status_code) { + int rv; + + rv = error_reply(downstream, status_code); + + if (rv != 0) { + return -1; + } + + handler_->signal_write(); + return 0; +} + +int SpdyUpstream::consume(int32_t stream_id, size_t len) { + int rv; + + rv = spdylay_session_consume(session_, stream_id, len); + + if (rv != 0) { + ULOG(WARN, this) << "spdylay_session_consume() returned error: " + << spdylay_strerror(rv); + return -1; + } + + return 0; +} + +int SpdyUpstream::on_timeout(Downstream *downstream) { + if (LOG_ENABLED(INFO)) { + ULOG(INFO, this) << "Stream timeout stream_id=" + << downstream->get_stream_id(); + } + + rst_stream(downstream, SPDYLAY_INTERNAL_ERROR); + + return 0; +} + +void SpdyUpstream::on_handler_delete() { + for (auto d = downstream_queue_.get_downstreams(); d; d = d->dlnext) { + if (d->get_dispatch_state() == Downstream::DISPATCH_ACTIVE && + d->accesslog_ready()) { + handler_->write_accesslog(d); + } + } +} + +int SpdyUpstream::on_downstream_reset(bool no_retry) { + int rv; + + for (auto downstream = downstream_queue_.get_downstreams(); downstream; + downstream = downstream->dlnext) { + if (downstream->get_dispatch_state() != Downstream::DISPATCH_ACTIVE) { + continue; + } + + if (!downstream->request_submission_ready()) { + rst_stream(downstream, SPDYLAY_INTERNAL_ERROR); + downstream->pop_downstream_connection(); + continue; + } + + downstream->pop_downstream_connection(); + + downstream->add_retry(); + + if (no_retry || downstream->no_more_retry()) { + goto fail; + } + + // downstream connection is clean; we can retry with new + // downstream connection. + + rv = downstream->attach_downstream_connection( + handler_->get_downstream_connection()); + if (rv != 0) { + goto fail; + } + + continue; + + fail: + if (on_downstream_abort_request(downstream, 503) != 0) { + return -1; + } + downstream->pop_downstream_connection(); + } + + handler_->signal_write(); + + return 0; +} + +} // namespace shrpx diff --git a/src/shrpx_spdy_upstream.h b/src/shrpx_spdy_upstream.h new file mode 100644 index 0000000..2e4aec4 --- /dev/null +++ b/src/shrpx_spdy_upstream.h @@ -0,0 +1,94 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef SHRPX_SPDY_UPSTREAM_H +#define SHRPX_SPDY_UPSTREAM_H + +#include "shrpx.h" + +#include + +#include + +#include + +#include "shrpx_upstream.h" +#include "shrpx_downstream_queue.h" +#include "memchunk.h" +#include "util.h" + +namespace shrpx { + +class ClientHandler; + +class SpdyUpstream : public Upstream { +public: + SpdyUpstream(uint16_t version, ClientHandler *handler); + virtual ~SpdyUpstream(); + virtual int on_read(); + virtual int on_write(); + virtual int on_timeout(Downstream *downstream); + virtual int on_downstream_abort_request(Downstream *downstream, + unsigned int status_code); + virtual ClientHandler *get_client_handler() const; + virtual int downstream_read(DownstreamConnection *dconn); + virtual int downstream_write(DownstreamConnection *dconn); + virtual int downstream_eof(DownstreamConnection *dconn); + virtual int downstream_error(DownstreamConnection *dconn, int events); + Downstream *add_pending_downstream(int32_t stream_id, int32_t priority); + void remove_downstream(Downstream *downstream); + + int rst_stream(Downstream *downstream, int status_code); + int error_reply(Downstream *downstream, unsigned int status_code); + + virtual void pause_read(IOCtrlReason reason); + virtual int resume_read(IOCtrlReason reason, Downstream *downstream, + size_t consumed); + + virtual int on_downstream_header_complete(Downstream *downstream); + virtual int on_downstream_body(Downstream *downstream, const uint8_t *data, + size_t len, bool flush); + virtual int on_downstream_body_complete(Downstream *downstream); + + virtual void on_handler_delete(); + virtual int on_downstream_reset(bool no_retry); + + bool get_flow_control() const; + + int consume(int32_t stream_id, size_t len); + + void start_downstream(Downstream *downstream); + void initiate_downstream(Downstream *downstream); + +private: + DownstreamQueue downstream_queue_; + ClientHandler *handler_; + spdylay_session *session_; + int32_t initial_window_size_; + bool flow_control_; +}; + +} // namespace shrpx + +#endif // SHRPX_SPDY_UPSTREAM_H diff --git a/src/shrpx_ssl.cc b/src/shrpx_ssl.cc new file mode 100644 index 0000000..3d3687e --- /dev/null +++ b/src/shrpx_ssl.cc @@ -0,0 +1,1028 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "shrpx_ssl.h" + +#ifdef HAVE_SYS_SOCKET_H +#include +#endif // HAVE_SYS_SOCKET_H +#ifdef HAVE_NETDB_H +#include +#endif // HAVE_NETDB_H +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include + +#ifdef HAVE_SPDYLAY +#include +#endif // HAVE_SPDYLAY + +#include "shrpx_log.h" +#include "shrpx_client_handler.h" +#include "shrpx_config.h" +#include "shrpx_worker.h" +#include "shrpx_downstream_connection_pool.h" +#include "util.h" +#include "ssl.h" +#include "template.h" + +using namespace nghttp2; + +namespace shrpx { + +namespace ssl { + +namespace { +int next_proto_cb(SSL *s, const unsigned char **data, unsigned int *len, + void *arg) { + auto &prefs = get_config()->alpn_prefs; + *data = prefs.data(); + *len = prefs.size(); + return SSL_TLSEXT_ERR_OK; +} +} // namespace + +namespace { +int verify_callback(int preverify_ok, X509_STORE_CTX *ctx) { + if (!preverify_ok) { + int err = X509_STORE_CTX_get_error(ctx); + int depth = X509_STORE_CTX_get_error_depth(ctx); + LOG(ERROR) << "client certificate verify error:num=" << err << ":" + << X509_verify_cert_error_string(err) << ":depth=" << depth; + } + return preverify_ok; +} +} // namespace + +std::vector set_alpn_prefs(const std::vector &protos) { + size_t len = 0; + + for (auto proto : protos) { + auto n = strlen(proto); + + if (n > 255) { + LOG(FATAL) << "Too long ALPN identifier: " << n; + DIE(); + } + + len += 1 + n; + } + + if (len > (1 << 16) - 1) { + LOG(FATAL) << "Too long ALPN identifier list: " << len; + DIE(); + } + + auto out = std::vector(len); + auto ptr = out.data(); + + for (auto proto : protos) { + auto proto_len = strlen(proto); + + *ptr++ = proto_len; + memcpy(ptr, proto, proto_len); + ptr += proto_len; + } + + return out; +} + +namespace { +int ssl_pem_passwd_cb(char *buf, int size, int rwflag, void *user_data) { + auto config = static_cast(user_data); + int len = (int)strlen(config->private_key_passwd.get()); + if (size < len + 1) { + LOG(ERROR) << "ssl_pem_passwd_cb: buf is too small " << size; + return 0; + } + // Copy string including last '\0'. + memcpy(buf, config->private_key_passwd.get(), len + 1); + return len; +} +} // namespace + +namespace { +int servername_callback(SSL *ssl, int *al, void *arg) { + auto handler = static_cast(SSL_get_app_data(ssl)); + auto worker = handler->get_worker(); + auto cert_tree = worker->get_cert_lookup_tree(); + if (cert_tree) { + const char *hostname = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name); + if (hostname) { + auto ssl_ctx = cert_tree->lookup(hostname, strlen(hostname)); + if (ssl_ctx) { + SSL_set_SSL_CTX(ssl, ssl_ctx); + } + } + } + return SSL_TLSEXT_ERR_OK; +} +} // namespace + +namespace { +int ocsp_resp_cb(SSL *ssl, void *arg) { + auto ssl_ctx = SSL_get_SSL_CTX(ssl); + auto tls_ctx_data = + static_cast(SSL_CTX_get_app_data(ssl_ctx)); + { + std::lock_guard g(tls_ctx_data->mu); + auto &data = tls_ctx_data->ocsp_data; + + if (!data.empty()) { + auto buf = static_cast( + CRYPTO_malloc(data.size(), __FILE__, __LINE__)); + + if (!buf) { + return SSL_TLSEXT_ERR_OK; + } + + std::copy(std::begin(data), std::end(data), buf); + + SSL_set_tlsext_status_ocsp_resp(ssl, buf, data.size()); + } + } + return SSL_TLSEXT_ERR_OK; +} +} // namespace + +namespace { +int ticket_key_cb(SSL *ssl, unsigned char *key_name, unsigned char *iv, + EVP_CIPHER_CTX *ctx, HMAC_CTX *hctx, int enc) { + auto handler = static_cast(SSL_get_app_data(ssl)); + auto worker = handler->get_worker(); + const auto &ticket_keys = worker->get_ticket_keys(); + + if (!ticket_keys) { + // No ticket keys available. + return -1; + } + + auto &keys = ticket_keys->keys; + assert(!keys.empty()); + + if (enc) { + if (RAND_bytes(iv, EVP_MAX_IV_LENGTH) == 0) { + if (LOG_ENABLED(INFO)) { + CLOG(INFO, handler) << "session ticket key: RAND_bytes failed"; + } + return -1; + } + + auto &key = keys[0]; + + if (LOG_ENABLED(INFO)) { + CLOG(INFO, handler) << "encrypt session ticket key: " + << util::format_hex(key.name, 16); + } + + memcpy(key_name, key.name, sizeof(key.name)); + + EVP_EncryptInit_ex(ctx, EVP_aes_128_cbc(), nullptr, key.aes_key, iv); + HMAC_Init_ex(hctx, key.hmac_key, sizeof(key.hmac_key), EVP_sha256(), + nullptr); + return 1; + } + + size_t i; + for (i = 0; i < keys.size(); ++i) { + auto &key = keys[0]; + if (memcmp(key.name, key_name, sizeof(key.name)) == 0) { + break; + } + } + + if (i == keys.size()) { + if (LOG_ENABLED(INFO)) { + CLOG(INFO, handler) << "session ticket key " + << util::format_hex(key_name, 16) << " not found"; + } + return 0; + } + + if (LOG_ENABLED(INFO)) { + CLOG(INFO, handler) << "decrypt session ticket key: " + << util::format_hex(key_name, 16); + } + + auto &key = keys[i]; + HMAC_Init_ex(hctx, key.hmac_key, sizeof(key.hmac_key), EVP_sha256(), nullptr); + EVP_DecryptInit_ex(ctx, EVP_aes_128_cbc(), nullptr, key.aes_key, iv); + + return i == 0 ? 1 : 2; +} +} // namespace + +namespace { +void info_callback(const SSL *ssl, int where, int ret) { + // To mitigate possible DOS attack using lots of renegotiations, we + // disable renegotiation. Since OpenSSL does not provide an easy way + // to disable it, we check that renegotiation is started in this + // callback. + if (where & SSL_CB_HANDSHAKE_START) { + auto conn = static_cast(SSL_get_app_data(ssl)); + if (conn && conn->tls.initial_handshake_done) { + // We only set SSL_get_app_data for ClientHandler for now. + auto handler = static_cast(conn->data); + if (LOG_ENABLED(INFO)) { + CLOG(INFO, handler) << "TLS renegotiation started"; + } + handler->start_immediate_shutdown(); + } + } +} +} // namespace + +#if OPENSSL_VERSION_NUMBER >= 0x10002000L +namespace { +int alpn_select_proto_cb(SSL *ssl, const unsigned char **out, + unsigned char *outlen, const unsigned char *in, + unsigned int inlen, void *arg) { + // We assume that get_config()->npn_list contains ALPN protocol + // identifier sorted by preference order. So we just break when we + // found the first overlap. + for (auto target_proto_id : get_config()->npn_list) { + auto target_proto_len = + strlen(reinterpret_cast(target_proto_id)); + + for (auto p = in, end = in + inlen; p < end;) { + auto proto_id = p + 1; + auto proto_len = *p; + + if (proto_id + proto_len <= end && target_proto_len == proto_len && + memcmp(target_proto_id, proto_id, proto_len) == 0) { + + *out = reinterpret_cast(proto_id); + *outlen = proto_len; + + return SSL_TLSEXT_ERR_OK; + } + + p += 1 + proto_len; + } + } + + return SSL_TLSEXT_ERR_NOACK; +} +} // namespace +#endif // OPENSSL_VERSION_NUMBER >= 0x10002000L + +namespace { +const char *tls_names[] = {"TLSv1.2", "TLSv1.1", "TLSv1.0"}; +const size_t tls_namelen = array_size(tls_names); +const long int tls_masks[] = {SSL_OP_NO_TLSv1_2, SSL_OP_NO_TLSv1_1, + SSL_OP_NO_TLSv1}; +} // namespace + +long int create_tls_proto_mask(const std::vector &tls_proto_list) { + long int res = 0; + + for (size_t i = 0; i < tls_namelen; ++i) { + size_t j; + for (j = 0; j < tls_proto_list.size(); ++j) { + if (util::strieq(tls_names[i], tls_proto_list[j])) { + break; + } + } + if (j == tls_proto_list.size()) { + res |= tls_masks[i]; + } + } + return res; +} + +SSL_CTX *create_ssl_context(const char *private_key_file, + const char *cert_file) { + auto ssl_ctx = SSL_CTX_new(SSLv23_server_method()); + if (!ssl_ctx) { + LOG(FATAL) << ERR_error_string(ERR_get_error(), nullptr); + DIE(); + } + + SSL_CTX_set_options( + ssl_ctx, + SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_COMPRESSION | + SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION | + SSL_OP_SINGLE_ECDH_USE | SSL_OP_SINGLE_DH_USE | + SSL_OP_CIPHER_SERVER_PREFERENCE | get_config()->tls_proto_mask); + + const unsigned char sid_ctx[] = "shrpx"; + SSL_CTX_set_session_id_context(ssl_ctx, sid_ctx, sizeof(sid_ctx) - 1); + SSL_CTX_set_session_cache_mode(ssl_ctx, SSL_SESS_CACHE_SERVER); + + const char *ciphers; + if (get_config()->ciphers) { + ciphers = get_config()->ciphers.get(); + } else { + ciphers = nghttp2::ssl::DEFAULT_CIPHER_LIST; + } + + if (SSL_CTX_set_cipher_list(ssl_ctx, ciphers) == 0) { + LOG(FATAL) << "SSL_CTX_set_cipher_list " << ciphers + << " failed: " << ERR_error_string(ERR_get_error(), nullptr); + DIE(); + } + +#ifndef OPENSSL_NO_EC + + // Disabled SSL_CTX_set_ecdh_auto, because computational cost of + // chosen curve is much higher than P-256. + + // #if OPENSSL_VERSION_NUMBER >= 0x10002000L + // SSL_CTX_set_ecdh_auto(ssl_ctx, 1); + // #else // OPENSSL_VERSION_NUBMER < 0x10002000L + // Use P-256, which is sufficiently secure at the time of this + // writing. + auto ecdh = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1); + if (ecdh == nullptr) { + LOG(FATAL) << "EC_KEY_new_by_curv_name failed: " + << ERR_error_string(ERR_get_error(), nullptr); + DIE(); + } + SSL_CTX_set_tmp_ecdh(ssl_ctx, ecdh); + EC_KEY_free(ecdh); +// #endif // OPENSSL_VERSION_NUBMER < 0x10002000L + +#endif // OPENSSL_NO_EC + + if (get_config()->dh_param_file) { + // Read DH parameters from file + auto bio = BIO_new_file(get_config()->dh_param_file.get(), "r"); + if (bio == nullptr) { + LOG(FATAL) << "BIO_new_file() failed: " + << ERR_error_string(ERR_get_error(), nullptr); + DIE(); + } + auto dh = PEM_read_bio_DHparams(bio, nullptr, nullptr, nullptr); + if (dh == nullptr) { + LOG(FATAL) << "PEM_read_bio_DHparams() failed: " + << ERR_error_string(ERR_get_error(), nullptr); + DIE(); + } + SSL_CTX_set_tmp_dh(ssl_ctx, dh); + DH_free(dh); + BIO_free(bio); + } + + SSL_CTX_set_mode(ssl_ctx, SSL_MODE_AUTO_RETRY); + SSL_CTX_set_mode(ssl_ctx, SSL_MODE_RELEASE_BUFFERS); + if (get_config()->private_key_passwd) { + SSL_CTX_set_default_passwd_cb(ssl_ctx, ssl_pem_passwd_cb); + SSL_CTX_set_default_passwd_cb_userdata(ssl_ctx, (void *)get_config()); + } + if (SSL_CTX_use_PrivateKey_file(ssl_ctx, private_key_file, + SSL_FILETYPE_PEM) != 1) { + LOG(FATAL) << "SSL_CTX_use_PrivateKey_file failed: " + << ERR_error_string(ERR_get_error(), nullptr); + DIE(); + } + if (SSL_CTX_use_certificate_chain_file(ssl_ctx, cert_file) != 1) { + LOG(FATAL) << "SSL_CTX_use_certificate_file failed: " + << ERR_error_string(ERR_get_error(), nullptr); + DIE(); + } + if (SSL_CTX_check_private_key(ssl_ctx) != 1) { + LOG(FATAL) << "SSL_CTX_check_private_key failed: " + << ERR_error_string(ERR_get_error(), nullptr); + DIE(); + } + if (get_config()->verify_client) { + if (get_config()->verify_client_cacert) { + if (SSL_CTX_load_verify_locations( + ssl_ctx, get_config()->verify_client_cacert.get(), nullptr) != + 1) { + + LOG(FATAL) << "Could not load trusted ca certificates from " + << get_config()->verify_client_cacert.get() << ": " + << ERR_error_string(ERR_get_error(), nullptr); + DIE(); + } + // It is heard that SSL_CTX_load_verify_locations() may leave + // error even though it returns success. See + // http://forum.nginx.org/read.php?29,242540 + ERR_clear_error(); + auto list = + SSL_load_client_CA_file(get_config()->verify_client_cacert.get()); + if (!list) { + LOG(FATAL) << "Could not load ca certificates from " + << get_config()->verify_client_cacert.get() << ": " + << ERR_error_string(ERR_get_error(), nullptr); + DIE(); + } + SSL_CTX_set_client_CA_list(ssl_ctx, list); + } + SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE | + SSL_VERIFY_FAIL_IF_NO_PEER_CERT, + verify_callback); + } + SSL_CTX_set_tlsext_servername_callback(ssl_ctx, servername_callback); + SSL_CTX_set_tlsext_ticket_key_cb(ssl_ctx, ticket_key_cb); + SSL_CTX_set_tlsext_status_cb(ssl_ctx, ocsp_resp_cb); + SSL_CTX_set_info_callback(ssl_ctx, info_callback); + + // NPN advertisement + SSL_CTX_set_next_protos_advertised_cb(ssl_ctx, next_proto_cb, nullptr); +#if OPENSSL_VERSION_NUMBER >= 0x10002000L + // ALPN selection callback + SSL_CTX_set_alpn_select_cb(ssl_ctx, alpn_select_proto_cb, nullptr); +#endif // OPENSSL_VERSION_NUMBER >= 0x10002000L + + auto tls_ctx_data = new TLSContextData(); + tls_ctx_data->cert_file = cert_file; + + SSL_CTX_set_app_data(ssl_ctx, tls_ctx_data); + + return ssl_ctx; +} + +namespace { +int select_next_proto_cb(SSL *ssl, unsigned char **out, unsigned char *outlen, + const unsigned char *in, unsigned int inlen, + void *arg) { + if (!util::select_h2(const_cast(out), outlen, in, + inlen)) { + return SSL_TLSEXT_ERR_NOACK; + } + + return SSL_TLSEXT_ERR_OK; +} +} // namespace + +SSL_CTX *create_ssl_client_context() { + auto ssl_ctx = SSL_CTX_new(SSLv23_client_method()); + if (!ssl_ctx) { + LOG(FATAL) << ERR_error_string(ERR_get_error(), nullptr); + DIE(); + } + SSL_CTX_set_options(ssl_ctx, + SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | + SSL_OP_NO_COMPRESSION | + SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION | + get_config()->tls_proto_mask); + + const char *ciphers; + if (get_config()->ciphers) { + ciphers = get_config()->ciphers.get(); + } else { + ciphers = "HIGH:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!3DES:!MD5:!PSK"; + } + if (SSL_CTX_set_cipher_list(ssl_ctx, ciphers) == 0) { + LOG(FATAL) << "SSL_CTX_set_cipher_list " << ciphers + << " failed: " << ERR_error_string(ERR_get_error(), nullptr); + DIE(); + } + + SSL_CTX_set_mode(ssl_ctx, SSL_MODE_AUTO_RETRY); + SSL_CTX_set_mode(ssl_ctx, SSL_MODE_RELEASE_BUFFERS); + + if (SSL_CTX_set_default_verify_paths(ssl_ctx) != 1) { + LOG(WARN) << "Could not load system trusted ca certificates: " + << ERR_error_string(ERR_get_error(), nullptr); + } + + if (get_config()->cacert) { + if (SSL_CTX_load_verify_locations(ssl_ctx, get_config()->cacert.get(), + nullptr) != 1) { + + LOG(FATAL) << "Could not load trusted ca certificates from " + << get_config()->cacert.get() << ": " + << ERR_error_string(ERR_get_error(), nullptr); + DIE(); + } + } + + if (get_config()->client_private_key_file) { + if (SSL_CTX_use_PrivateKey_file(ssl_ctx, + get_config()->client_private_key_file.get(), + SSL_FILETYPE_PEM) != 1) { + LOG(FATAL) << "Could not load client private key from " + << get_config()->client_private_key_file.get() << ": " + << ERR_error_string(ERR_get_error(), nullptr); + DIE(); + } + } + if (get_config()->client_cert_file) { + if (SSL_CTX_use_certificate_chain_file( + ssl_ctx, get_config()->client_cert_file.get()) != 1) { + + LOG(FATAL) << "Could not load client certificate from " + << get_config()->client_cert_file.get() << ": " + << ERR_error_string(ERR_get_error(), nullptr); + DIE(); + } + } + // NPN selection callback + SSL_CTX_set_next_proto_select_cb(ssl_ctx, select_next_proto_cb, nullptr); + +#if OPENSSL_VERSION_NUMBER >= 0x10002000L + // ALPN advertisement; We only advertise HTTP/2 + auto proto_list = util::get_default_alpn(); + + SSL_CTX_set_alpn_protos(ssl_ctx, proto_list.data(), proto_list.size()); +#endif // OPENSSL_VERSION_NUMBER >= 0x10002000L + + return ssl_ctx; +} + +ClientHandler *accept_connection(Worker *worker, int fd, sockaddr *addr, + int addrlen) { + char host[NI_MAXHOST]; + char service[NI_MAXSERV]; + int rv; + rv = getnameinfo(addr, addrlen, host, sizeof(host), service, sizeof(service), + NI_NUMERICHOST | NI_NUMERICSERV); + if (rv != 0) { + LOG(ERROR) << "getnameinfo() failed: " << gai_strerror(rv); + + return nullptr; + } + + int val = 1; + rv = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, reinterpret_cast(&val), + sizeof(val)); + if (rv == -1) { + LOG(WARN) << "Setting option TCP_NODELAY failed: errno=" << errno; + } + SSL *ssl = nullptr; + auto ssl_ctx = worker->get_sv_ssl_ctx(); + if (ssl_ctx) { + ssl = SSL_new(ssl_ctx); + if (!ssl) { + LOG(ERROR) << "SSL_new() failed: " << ERR_error_string(ERR_get_error(), + nullptr); + return nullptr; + } + + if (SSL_set_fd(ssl, fd) == 0) { + LOG(ERROR) << "SSL_set_fd() failed: " << ERR_error_string(ERR_get_error(), + nullptr); + SSL_free(ssl); + return nullptr; + } + + SSL_set_accept_state(ssl); + } + + return new ClientHandler(worker, fd, ssl, host, service); +} + +namespace { +bool tls_hostname_match(const char *pattern, const char *hostname) { + const char *ptWildcard = strchr(pattern, '*'); + if (ptWildcard == nullptr) { + return util::strieq(pattern, hostname); + } + const char *ptLeftLabelEnd = strchr(pattern, '.'); + bool wildcardEnabled = true; + // Do case-insensitive match. At least 2 dots are required to enable + // wildcard match. Also wildcard must be in the left-most label. + // Don't attempt to match a presented identifier where the wildcard + // character is embedded within an A-label. + if (ptLeftLabelEnd == 0 || strchr(ptLeftLabelEnd + 1, '.') == 0 || + ptLeftLabelEnd < ptWildcard || util::istartsWith(pattern, "xn--")) { + wildcardEnabled = false; + } + if (!wildcardEnabled) { + return util::strieq(pattern, hostname); + } + const char *hnLeftLabelEnd = strchr(hostname, '.'); + if (hnLeftLabelEnd == 0 || !util::strieq(ptLeftLabelEnd, hnLeftLabelEnd)) { + return false; + } + // Perform wildcard match. Here '*' must match at least one + // character. + if (hnLeftLabelEnd - hostname < ptLeftLabelEnd - pattern) { + return false; + } + return util::istartsWith(hostname, hnLeftLabelEnd, pattern, ptWildcard) && + util::iendsWith(hostname, hnLeftLabelEnd, ptWildcard + 1, + ptLeftLabelEnd); +} +} // namespace + +namespace { +int verify_hostname(const char *hostname, const sockaddr_union *su, + size_t salen, const std::vector &dns_names, + const std::vector &ip_addrs, + const std::string &common_name) { + if (util::numeric_host(hostname)) { + if (ip_addrs.empty()) { + return util::strieq(common_name.c_str(), hostname) ? 0 : -1; + } + const void *saddr; + switch (su->storage.ss_family) { + case AF_INET: + saddr = &su->in.sin_addr; + break; + case AF_INET6: + saddr = &su->in6.sin6_addr; + break; + default: + return -1; + } + for (size_t i = 0; i < ip_addrs.size(); ++i) { + if (salen == ip_addrs[i].size() && + memcmp(saddr, ip_addrs[i].c_str(), salen) == 0) { + return 0; + } + } + } else { + if (dns_names.empty()) { + return tls_hostname_match(common_name.c_str(), hostname) ? 0 : -1; + } + for (size_t i = 0; i < dns_names.size(); ++i) { + if (tls_hostname_match(dns_names[i].c_str(), hostname)) { + return 0; + } + } + } + return -1; +} +} // namespace + +void get_altnames(X509 *cert, std::vector &dns_names, + std::vector &ip_addrs, + std::string &common_name) { + GENERAL_NAMES *altnames = static_cast( + X509_get_ext_d2i(cert, NID_subject_alt_name, nullptr, nullptr)); + if (altnames) { + auto altnames_deleter = defer(GENERAL_NAMES_free, altnames); + size_t n = sk_GENERAL_NAME_num(altnames); + for (size_t i = 0; i < n; ++i) { + const GENERAL_NAME *altname = sk_GENERAL_NAME_value(altnames, i); + if (altname->type == GEN_DNS) { + const char *name; + name = reinterpret_cast(ASN1_STRING_data(altname->d.ia5)); + if (!name) { + continue; + } + size_t len = ASN1_STRING_length(altname->d.ia5); + if (std::find(name, name + len, '\0') != name + len) { + // Embedded NULL is not permitted. + continue; + } + dns_names.push_back(std::string(name, len)); + } else if (altname->type == GEN_IPADD) { + const unsigned char *ip_addr = altname->d.iPAddress->data; + if (!ip_addr) { + continue; + } + size_t len = altname->d.iPAddress->length; + ip_addrs.push_back( + std::string(reinterpret_cast(ip_addr), len)); + } + } + } + X509_NAME *subjectname = X509_get_subject_name(cert); + if (!subjectname) { + LOG(WARN) << "Could not get X509 name object from the certificate."; + return; + } + int lastpos = -1; + while (1) { + lastpos = X509_NAME_get_index_by_NID(subjectname, NID_commonName, lastpos); + if (lastpos == -1) { + break; + } + X509_NAME_ENTRY *entry = X509_NAME_get_entry(subjectname, lastpos); + unsigned char *out; + int outlen = ASN1_STRING_to_UTF8(&out, X509_NAME_ENTRY_get_data(entry)); + if (outlen < 0) { + continue; + } + if (std::find(out, out + outlen, '\0') != out + outlen) { + // Embedded NULL is not permitted. + continue; + } + common_name.assign(&out[0], &out[outlen]); + OPENSSL_free(out); + break; + } +} + +int check_cert(SSL *ssl) { + auto cert = SSL_get_peer_certificate(ssl); + if (!cert) { + LOG(ERROR) << "No certificate found"; + return -1; + } + auto cert_deleter = defer(X509_free, cert); + long verify_res = SSL_get_verify_result(ssl); + if (verify_res != X509_V_OK) { + LOG(ERROR) << "Certificate verification failed: " + << X509_verify_cert_error_string(verify_res); + return -1; + } + std::string common_name; + std::vector dns_names; + std::vector ip_addrs; + get_altnames(cert, dns_names, ip_addrs, common_name); + if (verify_hostname(get_config()->downstream_addrs[0].host.get(), + &get_config()->downstream_addrs[0].addr, + get_config()->downstream_addrs[0].addrlen, dns_names, + ip_addrs, common_name) != 0) { + LOG(ERROR) << "Certificate verification failed: hostname does not match"; + return -1; + } + return 0; +} + +CertLookupTree::CertLookupTree() { + root_.ssl_ctx = nullptr; + root_.str = nullptr; + root_.first = root_.last = 0; +} + +namespace { +// The |offset| is the index in the hostname we are examining. We are +// going to scan from |offset| in backwards. +void cert_lookup_tree_add_cert(CertNode *node, SSL_CTX *ssl_ctx, char *hostname, + size_t len, int offset) { + int i, next_len = node->next.size(); + char c = hostname[offset]; + CertNode *cn = nullptr; + for (i = 0; i < next_len; ++i) { + cn = node->next[i].get(); + if (cn->str[cn->first] == c) { + break; + } + } + if (i == next_len) { + if (c == '*') { + // We assume hostname as wildcard hostname when first '*' is + // encountered. Note that as per RFC 6125 (6.4.3), there are + // some restrictions for wildcard hostname. We just ignore + // these rules here but do the proper check when we do the + // match. + node->wildcard_certs.emplace_back(hostname, ssl_ctx); + return; + } + + int j; + auto new_node = make_unique(); + new_node->str = hostname; + new_node->first = offset; + // If wildcard is found, set the region before it because we + // don't include it in [first, last). + for (j = offset; j >= 0 && hostname[j] != '*'; --j) + ; + new_node->last = j; + if (j == -1) { + new_node->ssl_ctx = ssl_ctx; + } else { + new_node->ssl_ctx = nullptr; + new_node->wildcard_certs.emplace_back(hostname, ssl_ctx); + } + node->next.push_back(std::move(new_node)); + return; + } + + int j; + for (i = cn->first, j = offset; + i > cn->last && j >= 0 && cn->str[i] == hostname[j]; --i, --j) + ; + if (i == cn->last) { + if (j == -1) { + // If the same hostname already exists, we don't overwrite + // exiting ssl_ctx + if (!cn->ssl_ctx) { + cn->ssl_ctx = ssl_ctx; + } + return; + } + + // The existing hostname is a suffix of this hostname. Continue + // matching at potion j. + cert_lookup_tree_add_cert(cn, ssl_ctx, hostname, len, j); + return; + } + + { + auto new_node = make_unique(); + new_node->ssl_ctx = cn->ssl_ctx; + new_node->str = cn->str; + new_node->first = i; + new_node->last = cn->last; + new_node->wildcard_certs.swap(cn->wildcard_certs); + new_node->next.swap(cn->next); + + cn->next.push_back(std::move(new_node)); + } + + cn->last = i; + if (j == -1) { + // This hostname is a suffix of the existing hostname. + cn->ssl_ctx = ssl_ctx; + return; + } + + // This hostname and existing one share suffix. + cn->ssl_ctx = nullptr; + cert_lookup_tree_add_cert(cn, ssl_ctx, hostname, len, j); +} +} // namespace + +void CertLookupTree::add_cert(SSL_CTX *ssl_ctx, const char *hostname, + size_t len) { + if (len == 0) { + return; + } + // Copy hostname including terminal NULL + hosts_.push_back(make_unique(len + 1)); + const auto &host_copy = hosts_.back(); + for (size_t i = 0; i < len; ++i) { + host_copy[i] = util::lowcase(hostname[i]); + } + host_copy[len] = '\0'; + cert_lookup_tree_add_cert(&root_, ssl_ctx, host_copy.get(), len, len - 1); +} + +namespace { +SSL_CTX *cert_lookup_tree_lookup(CertNode *node, const char *hostname, + size_t len, int offset) { + int i, j; + for (i = node->first, j = offset; + i > node->last && j >= 0 && node->str[i] == util::lowcase(hostname[j]); + --i, --j) + ; + if (i != node->last) { + return nullptr; + } + if (j == -1) { + if (node->ssl_ctx) { + // exact match + return node->ssl_ctx; + } + + // Do not perform wildcard-match because '*' must match at least + // one character. + return nullptr; + } + for (const auto &wildcert : node->wildcard_certs) { + if (tls_hostname_match(wildcert.first, hostname)) { + return wildcert.second; + } + } + auto c = util::lowcase(hostname[j]); + for (const auto &next_node : node->next) { + if (next_node->str[next_node->first] == c) { + return cert_lookup_tree_lookup(next_node.get(), hostname, len, j); + } + } + return nullptr; +} +} // namespace + +SSL_CTX *CertLookupTree::lookup(const char *hostname, size_t len) { + return cert_lookup_tree_lookup(&root_, hostname, len, len - 1); +} + +int cert_lookup_tree_add_cert_from_file(CertLookupTree *lt, SSL_CTX *ssl_ctx, + const char *certfile) { + auto bio = BIO_new(BIO_s_file()); + if (!bio) { + LOG(ERROR) << "BIO_new failed"; + return -1; + } + auto bio_deleter = defer(BIO_vfree, bio); + if (!BIO_read_filename(bio, certfile)) { + LOG(ERROR) << "Could not read certificate file '" << certfile << "'"; + return -1; + } + auto cert = PEM_read_bio_X509(bio, nullptr, nullptr, nullptr); + if (!cert) { + LOG(ERROR) << "Could not read X509 structure from file '" << certfile + << "'"; + return -1; + } + auto cert_deleter = defer(X509_free, cert); + std::string common_name; + std::vector dns_names; + std::vector ip_addrs; + get_altnames(cert, dns_names, ip_addrs, common_name); + for (auto &dns_name : dns_names) { + lt->add_cert(ssl_ctx, dns_name.c_str(), dns_name.size()); + } + lt->add_cert(ssl_ctx, common_name.c_str(), common_name.size()); + return 0; +} + +bool in_proto_list(const std::vector &protos, + const unsigned char *needle, size_t len) { + for (auto proto : protos) { + if (strlen(proto) == len && memcmp(proto, needle, len) == 0) { + return true; + } + } + return false; +} + +bool check_http2_requirement(SSL *ssl) { + auto tls_ver = SSL_version(ssl); + + switch (tls_ver) { + case TLS1_2_VERSION: + break; + default: + if (LOG_ENABLED(INFO)) { + LOG(INFO) << "TLSv1.2 was not negotiated. " + << "HTTP/2 must not be negotiated."; + } + return false; + } + + return true; +} + +SSL_CTX *setup_server_ssl_context(std::vector &all_ssl_ctx, + CertLookupTree *cert_tree) { + if (get_config()->upstream_no_tls) { + return nullptr; + } + + auto ssl_ctx = ssl::create_ssl_context(get_config()->private_key_file.get(), + get_config()->cert_file.get()); + + all_ssl_ctx.push_back(ssl_ctx); + + if (get_config()->subcerts.empty()) { + return ssl_ctx; + } + + if (!cert_tree) { + LOG(WARN) << "We have multiple additional certificates (--subcert), but " + "cert_tree is not given. SNI may not work."; + return ssl_ctx; + } + + for (auto &keycert : get_config()->subcerts) { + auto ssl_ctx = + ssl::create_ssl_context(keycert.first.c_str(), keycert.second.c_str()); + all_ssl_ctx.push_back(ssl_ctx); + if (ssl::cert_lookup_tree_add_cert_from_file( + cert_tree, ssl_ctx, keycert.second.c_str()) == -1) { + LOG(FATAL) << "Failed to add sub certificate."; + DIE(); + } + } + + if (ssl::cert_lookup_tree_add_cert_from_file( + cert_tree, ssl_ctx, get_config()->cert_file.get()) == -1) { + LOG(FATAL) << "Failed to add default certificate."; + DIE(); + } + + return ssl_ctx; +} + +SSL_CTX *setup_client_ssl_context() { + if (get_config()->client_mode) { + return get_config()->downstream_no_tls ? nullptr + : ssl::create_ssl_client_context(); + } + + return get_config()->http2_bridge && !get_config()->downstream_no_tls + ? ssl::create_ssl_client_context() + : nullptr; +} + +CertLookupTree *create_cert_lookup_tree() { + if (get_config()->upstream_no_tls || get_config()->subcerts.empty()) { + return nullptr; + } + return new ssl::CertLookupTree(); +} + +} // namespace ssl + +} // namespace shrpx diff --git a/src/shrpx_ssl.h b/src/shrpx_ssl.h new file mode 100644 index 0000000..5f808cb --- /dev/null +++ b/src/shrpx_ssl.h @@ -0,0 +1,177 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef SHRPX_SSL_H +#define SHRPX_SSL_H + +#include "shrpx.h" + +#include +#include + +#include +#include + +#include + +namespace shrpx { + +class ClientHandler; +class Worker; +class DownstreamConnectionPool; + +namespace ssl { + +// This struct stores the additional information per SSL_CTX. This is +// attached to SSL_CTX using SSL_CTX_set_app_data(). +struct TLSContextData { + // Protects ocsp_data; + std::mutex mu; + // OCSP resonse + std::vector ocsp_data; + + // Path to certificate file + const char *cert_file; +}; + +// Create server side SSL_CTX +SSL_CTX *create_ssl_context(const char *private_key_file, + const char *cert_file); + +// Create client side SSL_CTX +SSL_CTX *create_ssl_client_context(); + +ClientHandler *accept_connection(Worker *worker, int fd, sockaddr *addr, + int addrlen); + +// Check peer's certificate against first downstream address in +// Config::downstream_addrs. We only consider first downstream since +// we use this function for HTTP/2 downstream link only. +int check_cert(SSL *ssl); + +// Retrieves DNS and IP address in subjectAltNames and commonName from +// the |cert|. +void get_altnames(X509 *cert, std::vector &dns_names, + std::vector &ip_addrs, std::string &common_name); + +// CertLookupTree forms lookup tree to get SSL_CTX whose DNS or +// commonName matches hostname in query. The tree is patricia trie +// data structure formed from the tail of the hostname pattern. Each +// CertNode contains part of hostname str member in range [first, +// last) member and the next member contains the following CertNode +// pointers ('following' means character before the current one). The +// CertNode where a hostname pattern ends contains its SSL_CTX pointer +// in the ssl_ctx member. For wildcard hostname pattern, we store the +// its pattern and SSL_CTX in CertNode one before first "*" found from +// the tail. +// +// When querying SSL_CTX with particular hostname, we match from its +// tail in our lookup tree. If the query goes to the first character +// of the hostname and current CertNode has non-NULL ssl_ctx member, +// then it is the exact match. The ssl_ctx member is returned. Along +// the way, if CertNode which contains non-empty wildcard_certs member +// is encountered, wildcard hostname matching is performed against +// them. If there is a match, its SSL_CTX is returned. If none +// matches, query is continued to the next character. + +struct CertNode { + // list of wildcard domain name and its SSL_CTX pair, the wildcard + // '*' appears in this position. + std::vector> wildcard_certs; + // Next CertNode index of CertLookupTree::nodes + std::vector> next; + // SSL_CTX for exact match + SSL_CTX *ssl_ctx; + char *str; + // [first, last) in the reverse direction in str, first >= + // last. This indices only work for str member. + int first, last; +}; + +class CertLookupTree { +public: + CertLookupTree(); + + // Adds |ssl_ctx| with hostname pattern |hostname| with length |len| + // to the lookup tree. The |hostname| must be NULL-terminated. + void add_cert(SSL_CTX *ssl_ctx, const char *hostname, size_t len); + + // Looks up SSL_CTX using the given |hostname| with length |len|. + // If more than one SSL_CTX which matches the query, it is undefined + // which one is returned. The |hostname| must be NULL-terminated. + // If no matching SSL_CTX found, returns NULL. + SSL_CTX *lookup(const char *hostname, size_t len); + +private: + CertNode root_; + // Stores pointers to copied hostname when adding hostname and + // ssl_ctx pair. + std::vector> hosts_; +}; + +// Adds |ssl_ctx| to lookup tree |lt| using hostnames read from +// |certfile|. The subjectAltNames and commonName are considered as +// eligible hostname. This function returns 0 if it succeeds, or -1. +// Even if no ssl_ctx is added to tree, this function returns 0. +int cert_lookup_tree_add_cert_from_file(CertLookupTree *lt, SSL_CTX *ssl_ctx, + const char *certfile); + +// Returns true if |needle| which has |len| bytes is included in the +// protocol list |protos|. +bool in_proto_list(const std::vector &protos, + const unsigned char *needle, size_t len); + +// Returns true if security requirement for HTTP/2 is fulfilled. +bool check_http2_requirement(SSL *ssl); + +// Returns SSL/TLS option mask to disable SSL/TLS protocol version not +// included in |tls_proto_list|. The returned mask can be directly +// passed to SSL_CTX_set_options(). +long int create_tls_proto_mask(const std::vector &tls_proto_list); + +std::vector set_alpn_prefs(const std::vector &protos); + +// Setups server side SSL_CTX. This function inspects get_config() +// and if upstream_no_tls is true, returns nullptr. Otherwise +// construct default SSL_CTX. If subcerts are available +// (get_config()->subcerts), caller should provide CertLookupTree +// object as |cert_tree| parameter, otherwise SNI does not work. All +// the created SSL_CTX is stored into |all_ssl_ctx|. +SSL_CTX *setup_server_ssl_context(std::vector &all_ssl_ctx, + CertLookupTree *cert_tree); + +// Setups client side SSL_CTX. This function inspects get_config() +// and if downstream_no_tls is true, returns nullptr. Otherwise, only +// construct SSL_CTX if either client_mode or http2_bridge is true. +SSL_CTX *setup_client_ssl_context(); + +// Creates CertLookupTree. If frontend is configured not to use TLS, +// this function returns nullptr. +CertLookupTree *create_cert_lookup_tree(); + +} // namespace ssl + +} // namespace shrpx + +#endif // SHRPX_SSL_H diff --git a/src/shrpx_ssl_test.cc b/src/shrpx_ssl_test.cc new file mode 100644 index 0000000..22bac63 --- /dev/null +++ b/src/shrpx_ssl_test.cc @@ -0,0 +1,119 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2013 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "shrpx_ssl_test.h" + +#include + +#include "shrpx_ssl.h" +#include "util.h" +#include "template.h" + +using namespace nghttp2; + +namespace shrpx { + +void test_shrpx_ssl_create_lookup_tree(void) { + auto tree = make_unique(); + SSL_CTX *ctxs[] = { + SSL_CTX_new(SSLv23_method()), SSL_CTX_new(SSLv23_method()), + SSL_CTX_new(SSLv23_method()), SSL_CTX_new(SSLv23_method()), + SSL_CTX_new(SSLv23_method()), SSL_CTX_new(SSLv23_method()), + SSL_CTX_new(SSLv23_method()), SSL_CTX_new(SSLv23_method()), + SSL_CTX_new(SSLv23_method()), SSL_CTX_new(SSLv23_method())}; + + const char *hostnames[] = {"example.com", "www.example.org", + "*www.example.org", "x*.host.domain", + "*yy.host.domain", "nghttp2.sourceforge.net", + "sourceforge.net", + "sourceforge.net", // duplicate + "*.foo.bar", // oo.bar is suffix of *.foo.bar + "oo.bar"}; + int num = array_size(ctxs); + for (int i = 0; i < num; ++i) { + tree->add_cert(ctxs[i], hostnames[i], strlen(hostnames[i])); + } + + CU_ASSERT(ctxs[0] == tree->lookup(hostnames[0], strlen(hostnames[0]))); + CU_ASSERT(ctxs[1] == tree->lookup(hostnames[1], strlen(hostnames[1]))); + const char h1[] = "2www.example.org"; + CU_ASSERT(ctxs[2] == tree->lookup(h1, strlen(h1))); + const char h2[] = "www2.example.org"; + CU_ASSERT(0 == tree->lookup(h2, strlen(h2))); + const char h3[] = "x1.host.domain"; + CU_ASSERT(ctxs[3] == tree->lookup(h3, strlen(h3))); + // Does not match *yy.host.domain, because * must match at least 1 + // character. + const char h4[] = "yy.Host.domain"; + CU_ASSERT(0 == tree->lookup(h4, strlen(h4))); + const char h5[] = "zyy.host.domain"; + CU_ASSERT(ctxs[4] == tree->lookup(h5, strlen(h5))); + CU_ASSERT(0 == tree->lookup("", 0)); + CU_ASSERT(ctxs[5] == tree->lookup(hostnames[5], strlen(hostnames[5]))); + CU_ASSERT(ctxs[6] == tree->lookup(hostnames[6], strlen(hostnames[6]))); + const char h6[] = "pdylay.sourceforge.net"; + for (int i = 0; i < 7; ++i) { + CU_ASSERT(0 == tree->lookup(h6 + i, strlen(h6) - i)); + } + const char h7[] = "x.foo.bar"; + CU_ASSERT(ctxs[8] == tree->lookup(h7, strlen(h7))); + CU_ASSERT(ctxs[9] == tree->lookup(hostnames[9], strlen(hostnames[9]))); + + for (int i = 0; i < num; ++i) { + SSL_CTX_free(ctxs[i]); + } + + SSL_CTX *ctxs2[] = { + SSL_CTX_new(SSLv23_method()), SSL_CTX_new(SSLv23_method()), + SSL_CTX_new(SSLv23_method()), SSL_CTX_new(SSLv23_method())}; + const char *names[] = {"rab", "zab", "zzub", "ab"}; + num = array_size(ctxs2); + + tree = make_unique(); + for (int i = 0; i < num; ++i) { + tree->add_cert(ctxs2[i], names[i], strlen(names[i])); + } + for (int i = 0; i < num; ++i) { + CU_ASSERT(ctxs2[i] == tree->lookup(names[i], strlen(names[i]))); + } + + for (int i = 0; i < num; ++i) { + SSL_CTX_free(ctxs2[i]); + } +} + +void test_shrpx_ssl_cert_lookup_tree_add_cert_from_file(void) { + int rv; + ssl::CertLookupTree tree; + auto ssl_ctx = SSL_CTX_new(SSLv23_method()); + const char certfile[] = NGHTTP2_TESTS_DIR "/testdata/cacert.pem"; + rv = ssl::cert_lookup_tree_add_cert_from_file(&tree, ssl_ctx, certfile); + CU_ASSERT(0 == rv); + const char localhost[] = "localhost"; + CU_ASSERT(ssl_ctx == tree.lookup(localhost, sizeof(localhost) - 1)); + + SSL_CTX_free(ssl_ctx); +} + +} // namespace shrpx diff --git a/src/shrpx_ssl_test.h b/src/shrpx_ssl_test.h new file mode 100644 index 0000000..89b0044 --- /dev/null +++ b/src/shrpx_ssl_test.h @@ -0,0 +1,39 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2013 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef SHRPX_SSL_TEST_H +#define SHRPX_SSL_TEST_H + +#ifdef HAVE_CONFIG_H +#include +#endif // HAVE_CONFIG_H + +namespace shrpx { + +void test_shrpx_ssl_create_lookup_tree(void); +void test_shrpx_ssl_cert_lookup_tree_add_cert_from_file(void); + +} // namespace shrpx + +#endif // SHRPX_SSL_TEST_H diff --git a/src/shrpx_upstream.h b/src/shrpx_upstream.h new file mode 100644 index 0000000..fe9a673 --- /dev/null +++ b/src/shrpx_upstream.h @@ -0,0 +1,69 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef SHRPX_UPSTREAM_H +#define SHRPX_UPSTREAM_H + +#include "shrpx.h" +#include "shrpx_io_control.h" + +namespace shrpx { + +class ClientHandler; +class Downstream; +class DownstreamConnection; + +class Upstream { +public: + virtual ~Upstream() {} + virtual int on_read() = 0; + virtual int on_write() = 0; + virtual int on_timeout(Downstream *downstream) { return 0; }; + virtual int on_downstream_abort_request(Downstream *downstream, + unsigned int status_code) = 0; + virtual int downstream_read(DownstreamConnection *dconn) = 0; + virtual int downstream_write(DownstreamConnection *dconn) = 0; + virtual int downstream_eof(DownstreamConnection *dconn) = 0; + virtual int downstream_error(DownstreamConnection *dconn, int events) = 0; + virtual ClientHandler *get_client_handler() const = 0; + + virtual int on_downstream_header_complete(Downstream *downstream) = 0; + virtual int on_downstream_body(Downstream *downstream, const uint8_t *data, + size_t len, bool flush) = 0; + virtual int on_downstream_body_complete(Downstream *downstream) = 0; + + virtual void on_handler_delete() = 0; + // Called when downstream connection is reset. Currently this is + // only used by Http2Session. If |no_retry| is true, another + // connection attempt using new DownstreamConnection is not allowed. + virtual int on_downstream_reset(bool no_retry) = 0; + + virtual void pause_read(IOCtrlReason reason) = 0; + virtual int resume_read(IOCtrlReason reason, Downstream *downstream, + size_t consumed) = 0; +}; + +} // namespace shrpx + +#endif // SHRPX_UPSTREAM_H diff --git a/src/shrpx_worker.cc b/src/shrpx_worker.cc new file mode 100644 index 0000000..a9bf6e6 --- /dev/null +++ b/src/shrpx_worker.cc @@ -0,0 +1,242 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "shrpx_worker.h" + +#ifdef HAVE_UNISTD_H +#include +#endif // HAVE_UNISTD_H + +#include + +#include "shrpx_ssl.h" +#include "shrpx_log.h" +#include "shrpx_client_handler.h" +#include "shrpx_http2_session.h" +#include "shrpx_log_config.h" +#include "shrpx_connect_blocker.h" +#include "util.h" +#include "template.h" + +namespace shrpx { + +namespace { +void eventcb(struct ev_loop *loop, ev_async *w, int revents) { + auto worker = static_cast(w->data); + worker->process_events(); +} +} // namespace + +namespace { +void mcpool_clear_cb(struct ev_loop *loop, ev_timer *w, int revents) { + auto worker = static_cast(w->data); + if (worker->get_worker_stat()->num_connections != 0) { + return; + } + worker->get_mcpool()->clear(); +} +} // namespace + +Worker::Worker(struct ev_loop *loop, SSL_CTX *sv_ssl_ctx, SSL_CTX *cl_ssl_ctx, + ssl::CertLookupTree *cert_tree, + const std::shared_ptr &ticket_keys) + : next_http2session_(0), loop_(loop), sv_ssl_ctx_(sv_ssl_ctx), + cl_ssl_ctx_(cl_ssl_ctx), cert_tree_(cert_tree), ticket_keys_(ticket_keys), + connect_blocker_(make_unique(loop_)), + graceful_shutdown_(false) { + ev_async_init(&w_, eventcb); + w_.data = this; + ev_async_start(loop_, &w_); + + ev_timer_init(&mcpool_clear_timer_, mcpool_clear_cb, 0., 0.); + mcpool_clear_timer_.data = this; + + if (get_config()->downstream_proto == PROTO_HTTP2) { + auto n = get_config()->http2_downstream_connections_per_worker; + for (; n > 0; --n) { + http2sessions_.push_back(make_unique( + loop_, cl_ssl_ctx, connect_blocker_.get(), this)); + } + } +} + +Worker::~Worker() { + ev_async_stop(loop_, &w_); + ev_timer_stop(loop_, &mcpool_clear_timer_); +} + +void Worker::schedule_clear_mcpool() { + // libev manual says: "If the watcher is already active nothing will + // happen." Since we don't change any timeout here, we don't have + // to worry about querying ev_is_active. + ev_timer_start(loop_, &mcpool_clear_timer_); +} + +void Worker::wait() { +#ifndef NOTHREADS + fut_.get(); +#endif // !NOTHREADS +} + +void Worker::run_async() { +#ifndef NOTHREADS + fut_ = std::async(std::launch::async, [this] { + (void)reopen_log_files(); + ev_run(loop_); + }); +#endif // !NOTHREADS +} + +void Worker::send(const WorkerEvent &event) { + { + std::lock_guard g(m_); + + q_.push_back(event); + } + + ev_async_send(loop_, &w_); +} + +void Worker::process_events() { + std::deque q; + { + std::lock_guard g(m_); + q.swap(q_); + } + for (auto &wev : q) { + switch (wev.type) { + case NEW_CONNECTION: { + if (LOG_ENABLED(INFO)) { + WLOG(INFO, this) << "WorkerEvent: client_fd=" << wev.client_fd + << ", addrlen=" << wev.client_addrlen; + } + + if (worker_stat_.num_connections >= + get_config()->worker_frontend_connections) { + + if (LOG_ENABLED(INFO)) { + WLOG(INFO, this) << "Too many connections >= " + << get_config()->worker_frontend_connections; + } + + close(wev.client_fd); + + break; + } + + auto client_handler = ssl::accept_connection( + this, wev.client_fd, &wev.client_addr.sa, wev.client_addrlen); + if (!client_handler) { + if (LOG_ENABLED(INFO)) { + WLOG(ERROR, this) << "ClientHandler creation failed"; + } + close(wev.client_fd); + break; + } + + if (LOG_ENABLED(INFO)) { + WLOG(INFO, this) << "CLIENT_HANDLER:" << client_handler << " created "; + } + + break; + } + case RENEW_TICKET_KEYS: + WLOG(NOTICE, this) << "Renew ticket keys: worker(" << this << ")"; + + ticket_keys_ = wev.ticket_keys; + + break; + case REOPEN_LOG: + WLOG(NOTICE, this) << "Reopening log files: worker(" << this << ")"; + + reopen_log_files(); + + break; + case GRACEFUL_SHUTDOWN: + WLOG(NOTICE, this) << "Graceful shutdown commencing"; + + graceful_shutdown_ = true; + + if (worker_stat_.num_connections == 0) { + ev_break(loop_); + + return; + } + + break; + default: + if (LOG_ENABLED(INFO)) { + WLOG(INFO, this) << "unknown event type " << wev.type; + } + } + } +} + +ssl::CertLookupTree *Worker::get_cert_lookup_tree() const { return cert_tree_; } + +const std::shared_ptr &Worker::get_ticket_keys() const { + return ticket_keys_; +} + +void Worker::set_ticket_keys(std::shared_ptr ticket_keys) { + ticket_keys_ = std::move(ticket_keys); +} + +WorkerStat *Worker::get_worker_stat() { return &worker_stat_; } + +DownstreamConnectionPool *Worker::get_dconn_pool() { return &dconn_pool_; } + +Http2Session *Worker::next_http2_session() { + if (http2sessions_.empty()) { + return nullptr; + } + + auto res = http2sessions_[next_http2session_].get(); + ++next_http2session_; + if (next_http2session_ >= http2sessions_.size()) { + next_http2session_ = 0; + } + + return res; +} + +ConnectBlocker *Worker::get_connect_blocker() const { + return connect_blocker_.get(); +} + +struct ev_loop *Worker::get_loop() const { + return loop_; +} + +SSL_CTX *Worker::get_sv_ssl_ctx() const { return sv_ssl_ctx_; } + +SSL_CTX *Worker::get_cl_ssl_ctx() const { return cl_ssl_ctx_; } + +void Worker::set_graceful_shutdown(bool f) { graceful_shutdown_ = f; } + +bool Worker::get_graceful_shutdown() const { return graceful_shutdown_; } + +MemchunkPool *Worker::get_mcpool() { return &mcpool_; } + +} // namespace shrpx diff --git a/src/shrpx_worker.h b/src/shrpx_worker.h new file mode 100644 index 0000000..4721371 --- /dev/null +++ b/src/shrpx_worker.h @@ -0,0 +1,142 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef SHRPX_WORKER_H +#define SHRPX_WORKER_H + +#include "shrpx.h" + +#include +#include +#include +#include +#ifndef NOTHREADS +#include +#endif // NOTHREADS + +#include +#include + +#include + +#include "shrpx_config.h" +#include "shrpx_downstream_connection_pool.h" +#include "memchunk.h" + +using namespace nghttp2; + +namespace shrpx { + +class Http2Session; +class ConnectBlocker; + +namespace ssl { +class CertLookupTree; +} // namespace ssl + +struct WorkerStat { + WorkerStat() : num_connections(0), next_downstream(0) {} + + size_t num_connections; + // Next downstream index in Config::downstream_addrs. For HTTP/2 + // downstream connections, this is always 0. For HTTP/1, this is + // used as load balancing. + size_t next_downstream; +}; + +enum WorkerEventType { + NEW_CONNECTION = 0x01, + REOPEN_LOG = 0x02, + GRACEFUL_SHUTDOWN = 0x03, + RENEW_TICKET_KEYS = 0x04, +}; + +struct WorkerEvent { + WorkerEventType type; + struct { + sockaddr_union client_addr; + size_t client_addrlen; + int client_fd; + }; + std::shared_ptr ticket_keys; +}; + +class Worker { +public: + Worker(struct ev_loop *loop, SSL_CTX *sv_ssl_ctx, SSL_CTX *cl_ssl_ctx, + ssl::CertLookupTree *cert_tree, + const std::shared_ptr &ticket_keys); + ~Worker(); + void run_async(); + void wait(); + void process_events(); + void send(const WorkerEvent &event); + + ssl::CertLookupTree *get_cert_lookup_tree() const; + const std::shared_ptr &get_ticket_keys() const; + void set_ticket_keys(std::shared_ptr ticket_keys); + WorkerStat *get_worker_stat(); + DownstreamConnectionPool *get_dconn_pool(); + Http2Session *next_http2_session(); + ConnectBlocker *get_connect_blocker() const; + struct ev_loop *get_loop() const; + SSL_CTX *get_sv_ssl_ctx() const; + SSL_CTX *get_cl_ssl_ctx() const; + + void set_graceful_shutdown(bool f); + bool get_graceful_shutdown() const; + + MemchunkPool *get_mcpool(); + void schedule_clear_mcpool(); + +private: + std::vector> http2sessions_; + size_t next_http2session_; +#ifndef NOTHREADS + std::future fut_; +#endif // NOTHREADS + std::mutex m_; + std::deque q_; + ev_async w_; + ev_timer mcpool_clear_timer_; + MemchunkPool mcpool_; + DownstreamConnectionPool dconn_pool_; + WorkerStat worker_stat_; + struct ev_loop *loop_; + + // Following fields are shared across threads if + // get_config()->tls_ctx_per_worker == true. + SSL_CTX *sv_ssl_ctx_; + SSL_CTX *cl_ssl_ctx_; + ssl::CertLookupTree *cert_tree_; + + std::shared_ptr ticket_keys_; + std::unique_ptr connect_blocker_; + + bool graceful_shutdown_; +}; + +} // namespace shrpx + +#endif // SHRPX_WORKER_H diff --git a/src/ssl.cc b/src/ssl.cc new file mode 100644 index 0000000..8696801 --- /dev/null +++ b/src/ssl.cc @@ -0,0 +1,83 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2014 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "ssl.h" + +#include +#include +#include +#include + +#include + +namespace nghttp2 { + +namespace ssl { + +// Recommended general purpose "Non-Backward Compatible" cipher by +// mozilla. +// +// https://wiki.mozilla.org/Security/Server_Side_TLS +const char *const DEFAULT_CIPHER_LIST = + "ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-" + "AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:" + "DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-" + "AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-" + "AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-" + "AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:" + "DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:!aNULL:!eNULL:" + "!EXPORT:!DES:!RC4:!3DES:!MD5:!PSK"; + +namespace { +std::vector ssl_global_locks; +} // namespace + +namespace { +void ssl_locking_cb(int mode, int type, const char *file, int line) { + if (mode & CRYPTO_LOCK) { + ssl_global_locks[type].lock(); + } else { + ssl_global_locks[type].unlock(); + } +} +} // namespace + +LibsslGlobalLock::LibsslGlobalLock() { + if (!ssl_global_locks.empty()) { + std::cerr << "OpenSSL global lock has been already set" << std::endl; + assert(0); + } + ssl_global_locks = std::vector(CRYPTO_num_locks()); + // CRYPTO_set_id_callback(ssl_thread_id); OpenSSL manual says that + // if threadid_func is not specified using + // CRYPTO_THREADID_set_callback(), then default implementation is + // used. We use this default one. + CRYPTO_set_locking_callback(ssl_locking_cb); +} + +LibsslGlobalLock::~LibsslGlobalLock() { ssl_global_locks.clear(); } + +} // namespace ssl + +} // namespace nghttp2 diff --git a/src/ssl.h b/src/ssl.h new file mode 100644 index 0000000..97f3933 --- /dev/null +++ b/src/ssl.h @@ -0,0 +1,45 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "nghttp2_config.h" + +namespace nghttp2 { + +namespace ssl { + +// Acquire OpenSSL global lock to share SSL_CTX across multiple +// threads. The constructor acquires lock and destructor unlocks. +class LibsslGlobalLock { +public: + LibsslGlobalLock(); + ~LibsslGlobalLock(); + LibsslGlobalLock(const LibsslGlobalLock &) = delete; + LibsslGlobalLock &operator=(const LibsslGlobalLock &) = delete; +}; + +extern const char *const DEFAULT_CIPHER_LIST; + +} // namespace ssl + +} // namespace nghttp2 diff --git a/src/template.h b/src/template.h new file mode 100644 index 0000000..144aed1 --- /dev/null +++ b/src/template.h @@ -0,0 +1,150 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2015 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef TEMPLATE_H +#define TEMPLATE_H + +#include "nghttp2_config.h" + +#include +#include +#include + +namespace nghttp2 { + +template +typename std::enable_if::value, std::unique_ptr>::type +make_unique(U &&... u) { + return std::unique_ptr(new T(std::forward(u)...)); +} + +template +typename std::enable_if::value, std::unique_ptr>::type +make_unique(size_t size) { + return std::unique_ptr(new typename std::remove_extent::type[size]()); +} + +template +std::array make_array(T &&t, Rest &&... rest) { + return std::array{ + {std::forward(t), std::forward(rest)...}}; +} + +template constexpr size_t array_size(T (&)[N]) { + return N; +} + +template constexpr size_t str_size(T (&)[N]) { + return N - 1; +} + +// inspired by , but our +// template can take functions returning other than void. +template struct Defer { + Defer(F &&f, T &&... t) + : f(std::bind(std::forward(f), std::forward(t)...)) {} + Defer(Defer &&o) : f(std::move(o.f)) {} + ~Defer() { f(); } + + using ResultType = typename std::result_of< + typename std::decay::type(typename std::decay::type...)>::type; + std::function f; +}; + +template Defer defer(F &&f, T &&... t) { + return Defer(std::forward(f), std::forward(t)...); +} + +template bool test_flags(T t, F flags) { + return (t & flags) == flags; +} + +// doubly linked list of element T*. T must have field T *dlprev and +// T *dlnext, which point to previous element and next element in the +// list respectively. +template struct DList { + DList() : head(nullptr), tail(nullptr) {} + + DList(const DList &) = delete; + + DList &operator=(const DList &) = delete; + + DList(DList &&other) : head(other.head), tail(other.tail) { + other.head = other.tail = nullptr; + } + + DList &operator=(DList &&other) { + if (this == &other) { + return *this; + } + head = other.head; + tail = other.tail; + other.head = other.tail = nullptr; + return *this; + } + + void append(T *t) { + if (tail) { + tail->dlnext = t; + t->dlprev = tail; + tail = t; + return; + } + head = tail = t; + } + + void remove(T *t) { + auto p = t->dlprev; + auto n = t->dlnext; + if (p) { + p->dlnext = n; + } + if (head == t) { + head = n; + } + if (n) { + n->dlprev = p; + } + if (tail == t) { + tail = p; + } + t->dlprev = t->dlnext = nullptr; + } + + bool empty() const { return head == nullptr; } + + T *head, *tail; +}; + +template void dlist_delete_all(DList &dl) { + for (auto e = dl.head; e;) { + auto next = e->dlnext; + delete e; + e = next; + } +} + +} // namespace nghttp2 + +#endif // TEMPLATE_H diff --git a/src/timegm.c b/src/timegm.c new file mode 100644 index 0000000..90bf53f --- /dev/null +++ b/src/timegm.c @@ -0,0 +1,70 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2013 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "timegm.h" + +#ifndef HAVE_TIMEGM + +#include + +/* Counter the number of leap year in the range [0, y). The |y| is the + year, including century (e.g., 2012) */ +static int count_leap_year(int y) { + y -= 1; + return y / 4 - y / 100 + y / 400; +} + +/* Returns nonzero if the |y| is the leap year. The |y| is the year, + including century (e.g., 2012) */ +static int is_leap_year(int y) { + return y % 4 == 0 && (y % 100 != 0 || y % 400 == 0); +} + +/* The number of days before ith month begins */ +static int daysum[] = {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334}; + +/* Based on the algorithm of Python 2.7 calendar.timegm. */ +time_t timegm(struct tm *tm) { + int days; + int num_leap_year; + int64_t t; + if (tm->tm_mon > 11) { + return -1; + } + num_leap_year = count_leap_year(tm->tm_year + 1900) - count_leap_year(1970); + days = (tm->tm_year - 70) * 365 + num_leap_year + daysum[tm->tm_mon] + + tm->tm_mday - 1; + if (tm->tm_mon >= 2 && is_leap_year(tm->tm_year + 1900)) { + ++days; + } + t = ((int64_t)days * 24 + tm->tm_hour) * 3600 + tm->tm_min * 60 + tm->tm_sec; + if (sizeof(time_t) == 4) { + if (t < INT32_MIN || t > INT32_MAX) { + return -1; + } + } + return (time_t)t; +} + +#endif /* !HAVE_TIMEGM */ diff --git a/src/timegm.h b/src/timegm.h new file mode 100644 index 0000000..32a9526 --- /dev/null +++ b/src/timegm.h @@ -0,0 +1,50 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2013 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef TIMEGM_H +#define TIMEGM_H + +#ifdef HAVE_CONFIG_H +#include +#endif /* HAVE_CONFIG_H */ + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#ifdef HAVE_TIME_H +#include +#endif // HAVE_TIME_H + +#ifndef HAVE_TIMEGM + +time_t timegm(struct tm *tm); + +#endif /* HAVE_TIMEGM */ + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* TIMEGM_H */ diff --git a/src/util.cc b/src/util.cc new file mode 100644 index 0000000..9eb5258 --- /dev/null +++ b/src/util.cc @@ -0,0 +1,1161 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "util.h" + +#ifdef HAVE_TIME_H +#include +#endif // HAVE_TIME_H +#include +#ifdef HAVE_SYS_SOCKET_H +#include +#endif // HAVE_SYS_SOCKET_H +#ifdef HAVE_NETDB_H +#include +#endif // HAVE_NETDB_H +#include +#ifdef HAVE_FCNTL_H +#include +#endif // HAVE_FCNTL_H +#ifdef HAVE_NETINET_IN_H +#include +#endif // HAVE_NETINET_IN_H +#include +#ifdef HAVE_ARPA_INET_H +#include +#endif // HAVE_ARPA_INET_H + +#include +#include +#include +#include +#include +#include + +#include + +#include "timegm.h" +#include "template.h" + +namespace nghttp2 { + +namespace util { + +const char DEFAULT_STRIP_CHARSET[] = "\r\n\t "; + +const char UPPER_XDIGITS[] = "0123456789ABCDEF"; + +bool isAlpha(const char c) { + return ('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z'); +} + +bool isDigit(const char c) { return '0' <= c && c <= '9'; } + +bool isHexDigit(const char c) { + return isDigit(c) || ('A' <= c && c <= 'F') || ('a' <= c && c <= 'f'); +} + +bool inRFC3986UnreservedChars(const char c) { + static const char unreserved[] = {'-', '.', '_', '~'}; + return isAlpha(c) || isDigit(c) || + std::find(&unreserved[0], &unreserved[4], c) != &unreserved[4]; +} + +bool in_rfc3986_sub_delims(const char c) { + static const char sub_delims[] = {'!', '$', '&', '\'', '(', ')', + '*', '+', ',', ';', '='}; + return std::find(std::begin(sub_delims), std::end(sub_delims), c) != + std::end(sub_delims); +} + +std::string percentEncode(const unsigned char *target, size_t len) { + std::string dest; + for (size_t i = 0; i < len; ++i) { + unsigned char c = target[i]; + + if (inRFC3986UnreservedChars(c)) { + dest += c; + } else { + dest += "%"; + dest += UPPER_XDIGITS[c >> 4]; + dest += UPPER_XDIGITS[(c & 0x0f)]; + } + } + return dest; +} + +std::string percentEncode(const std::string &target) { + return percentEncode(reinterpret_cast(target.c_str()), + target.size()); +} + +std::string percent_encode_path(const std::string &s) { + std::string dest; + for (auto c : s) { + if (inRFC3986UnreservedChars(c) || in_rfc3986_sub_delims(c) || c == '/') { + dest += c; + continue; + } + + dest += "%"; + dest += UPPER_XDIGITS[(c >> 4) & 0x0f]; + dest += UPPER_XDIGITS[(c & 0x0f)]; + } + return dest; +} + +bool in_token(char c) { + static const char extra[] = {'!', '#', '$', '%', '&', '\'', '*', '+', + '-', '.', '^', '_', '`', '|', '~'}; + + return isAlpha(c) || isDigit(c) || + std::find(&extra[0], &extra[sizeof(extra)], c) != + &extra[sizeof(extra)]; +} + +bool in_attr_char(char c) { + static const char bad[] = {'*', '\'', '%'}; + return util::in_token(c) && + std::find(std::begin(bad), std::end(bad) - 1, c) == std::end(bad) - 1; +} + +std::string percent_encode_token(const std::string &target) { + auto len = target.size(); + std::string dest; + + for (size_t i = 0; i < len; ++i) { + unsigned char c = target[i]; + + if (c != '%' && in_token(c)) { + dest += c; + } else { + dest += "%"; + dest += UPPER_XDIGITS[c >> 4]; + dest += UPPER_XDIGITS[(c & 0x0f)]; + } + } + return dest; +} + +uint32_t hex_to_uint(char c) { + if (c <= '9') { + return c - '0'; + } + if (c <= 'Z') { + return c - 'A' + 10; + } + if (c <= 'z') { + return c - 'a' + 10; + } + return c; +} + +std::string quote_string(const std::string &target) { + auto cnt = std::count(std::begin(target), std::end(target), '"'); + + if (cnt == 0) { + return target; + } + + std::string res; + res.reserve(target.size() + cnt); + + for (auto c : target) { + if (c == '"') { + res += "\\\""; + } else { + res += c; + } + } + + return res; +} + +namespace { +template +Iterator cpydig(Iterator d, uint32_t n, size_t len) { + auto p = d + len - 1; + + do { + *p-- = (n % 10) + '0'; + n /= 10; + } while (p >= d); + + return d + len; +} +} // namespace + +namespace { +const char *MONTH[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; +const char *DAY_OF_WEEK[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}; +} // namespace + +std::string http_date(time_t t) { + struct tm tms; + std::string res; + + if (gmtime_r(&t, &tms) == nullptr) { + return res; + } + + /* Sat, 27 Sep 2014 06:31:15 GMT */ + res.resize(29); + + auto p = std::begin(res); + + auto s = DAY_OF_WEEK[tms.tm_wday]; + p = std::copy_n(s, 3, p); + *p++ = ','; + *p++ = ' '; + p = cpydig(p, tms.tm_mday, 2); + *p++ = ' '; + s = MONTH[tms.tm_mon]; + p = std::copy_n(s, 3, p); + *p++ = ' '; + p = cpydig(p, tms.tm_year + 1900, 4); + *p++ = ' '; + p = cpydig(p, tms.tm_hour, 2); + *p++ = ':'; + p = cpydig(p, tms.tm_min, 2); + *p++ = ':'; + p = cpydig(p, tms.tm_sec, 2); + s = " GMT"; + p = std::copy_n(s, 4, p); + + return res; +} + +std::string common_log_date(time_t t) { + struct tm tms; + + if (localtime_r(&t, &tms) == nullptr) { + return ""; + } + +#ifdef HAVE_STRUCT_TM_TM_GMTOFF + // Format data like this: + // 03/Jul/2014:00:19:38 +0900 + std::string res; + res.resize(26); + + auto p = std::begin(res); + + p = cpydig(p, tms.tm_mday, 2); + *p++ = '/'; + auto s = MONTH[tms.tm_mon]; + p = std::copy_n(s, 3, p); + *p++ = '/'; + p = cpydig(p, tms.tm_year + 1900, 4); + *p++ = ':'; + p = cpydig(p, tms.tm_hour, 2); + *p++ = ':'; + p = cpydig(p, tms.tm_min, 2); + *p++ = ':'; + p = cpydig(p, tms.tm_sec, 2); + *p++ = ' '; + + auto gmtoff = tms.tm_gmtoff; + if (gmtoff >= 0) { + *p++ = '+'; + } else { + *p++ = '-'; + gmtoff = -gmtoff; + } + + p = cpydig(p, gmtoff / 3600, 2); + p = cpydig(p, (gmtoff % 3600) / 60, 2); + + return res; +#else // !HAVE_STRUCT_TM_TM_GMTOFF + char buf[32]; + + strftime(buf, sizeof(buf), "%d/%b/%Y:%T %z", &tms); + + return buf; +#endif // !HAVE_STRUCT_TM_TM_GMTOFF +} + +std::string iso8601_date(int64_t ms) { + time_t sec = ms / 1000; + + tm tms; + if (localtime_r(&sec, &tms) == nullptr) { + return ""; + } + +#ifdef HAVE_STRUCT_TM_TM_GMTOFF + // Format data like this: + // 2014-11-15T12:58:24.741Z + // 2014-11-15T12:58:24.741+09:00 + std::string res; + res.resize(29); + + auto p = std::begin(res); + + p = cpydig(p, tms.tm_year + 1900, 4); + *p++ = '-'; + p = cpydig(p, tms.tm_mon + 1, 2); + *p++ = '-'; + p = cpydig(p, tms.tm_mday, 2); + *p++ = 'T'; + p = cpydig(p, tms.tm_hour, 2); + *p++ = ':'; + p = cpydig(p, tms.tm_min, 2); + *p++ = ':'; + p = cpydig(p, tms.tm_sec, 2); + *p++ = '.'; + p = cpydig(p, ms % 1000, 3); + + auto gmtoff = tms.tm_gmtoff; + if (gmtoff == 0) { + *p++ = 'Z'; + } else { + if (gmtoff > 0) { + *p++ = '+'; + } else { + *p++ = '-'; + gmtoff = -gmtoff; + } + p = cpydig(p, gmtoff / 3600, 2); + *p++ = ':'; + p = cpydig(p, (gmtoff % 3600) / 60, 2); + } + + res.resize(p - std::begin(res)); + + return res; +#else // !HAVE_STRUCT_TM_TM_GMTOFF + char buf[128]; + + auto nwrite = strftime(buf, sizeof(buf), "%Y-%m-%dT%H:%M:%S", &tms); + nwrite += snprintf(&buf[nwrite], sizeof(buf) - nwrite, ".%03d", + static_cast(ms % 1000)); + auto nzone = strftime(&buf[nwrite], sizeof(buf) - nwrite, "%z", &tms); + + // %z of strftime writes +hhmm or -hhmm not Z, +hh:mm or -hh:mm. Do + // %nothing if nzone is not 5. we don't know how to cope with this. + if (nzone == 5) { + if (memcmp(&buf[nwrite], "+0000", 5) == 0) { + // 0000 should be Z + memcpy(&buf[nwrite], "Z", 2); + } else { + // Move mm part to right by 1 including terminal \0 + memmove(&buf[nwrite + 4], &buf[nwrite + 3], 3); + // Insert ':' between hh and mm + buf[nwrite + 3] = ':'; + } + } + return buf; +#endif // !HAVE_STRUCT_TM_TM_GMTOFF +} + +time_t parse_http_date(const std::string &s) { + tm tm; + memset(&tm, 0, sizeof(tm)); + char *r = strptime(s.c_str(), "%a, %d %b %Y %H:%M:%S GMT", &tm); + if (r == 0) { + return 0; + } + return timegm(&tm); +} + +namespace { +void streq_advance(const char **ap, const char **bp) { + for (; **ap && **bp && lowcase(**ap) == lowcase(**bp); ++*ap, ++*bp) + ; +} +} // namespace + +bool istartsWith(const char *a, const char *b) { + if (!a || !b) { + return false; + } + streq_advance(&a, &b); + return !*b; +} + +bool strieq(const char *a, const char *b) { + if (!a || !b) { + return false; + } + for (; *a && *b && lowcase(*a) == lowcase(*b); ++a, ++b) + ; + return !*a && !*b; +} + +int strcompare(const char *a, const uint8_t *b, size_t bn) { + assert(a && b); + const uint8_t *blast = b + bn; + for (; *a && b != blast; ++a, ++b) { + if (*a < *b) { + return -1; + } else if (*a > *b) { + return 1; + } + } + if (!*a && b == blast) { + return 0; + } else if (b == blast) { + return 1; + } else { + return -1; + } +} + +bool strifind(const char *a, const char *b) { + if (!a || !b) { + return false; + } + for (size_t i = 0; a[i]; ++i) { + const char *ap = &a[i], *bp = b; + for (; *ap && *bp && lowcase(*ap) == lowcase(*bp); ++ap, ++bp) + ; + if (!*bp) { + return true; + } + } + return false; +} + +char upcase(char c) { + if ('a' <= c && c <= 'z') { + return c - 'a' + 'A'; + } else { + return c; + } +} + +namespace { +const char LOWER_XDIGITS[] = "0123456789abcdef"; +} // namespace + +std::string format_hex(const unsigned char *s, size_t len) { + std::string res; + res.resize(len * 2); + + for (size_t i = 0; i < len; ++i) { + unsigned char c = s[i]; + + res[i * 2] = LOWER_XDIGITS[c >> 4]; + res[i * 2 + 1] = LOWER_XDIGITS[c & 0x0f]; + } + return res; +} + +void to_token68(std::string &base64str) { + std::transform(std::begin(base64str), std::end(base64str), + std::begin(base64str), [](char c) { + switch (c) { + case '+': + return '-'; + case '/': + return '_'; + default: + return c; + } + }); + base64str.erase(std::find(std::begin(base64str), std::end(base64str), '='), + std::end(base64str)); +} + +void to_base64(std::string &token68str) { + std::transform(std::begin(token68str), std::end(token68str), + std::begin(token68str), [](char c) { + switch (c) { + case '-': + return '+'; + case '_': + return '/'; + default: + return c; + } + }); + if (token68str.size() & 0x3) { + token68str.append(4 - (token68str.size() & 0x3), '='); + } + return; +} + +namespace { +// Calculates Damerau–Levenshtein distance between c-string a and b +// with given costs. swapcost, subcost, addcost and delcost are cost +// to swap 2 adjacent characters, substitute characters, add character +// and delete character respectively. +int levenshtein(const char *a, int alen, const char *b, int blen, int swapcost, + int subcost, int addcost, int delcost) { + auto dp = std::vector>(3, std::vector(blen + 1)); + for (int i = 0; i <= blen; ++i) { + dp[1][i] = i; + } + for (int i = 1; i <= alen; ++i) { + dp[0][0] = i; + for (int j = 1; j <= blen; ++j) { + dp[0][j] = dp[1][j - 1] + (a[i - 1] == b[j - 1] ? 0 : subcost); + if (i >= 2 && j >= 2 && a[i - 1] != b[j - 1] && a[i - 2] == b[j - 1] && + a[i - 1] == b[j - 2]) { + dp[0][j] = std::min(dp[0][j], dp[2][j - 2] + swapcost); + } + dp[0][j] = std::min(dp[0][j], + std::min(dp[1][j] + delcost, dp[0][j - 1] + addcost)); + } + std::rotate(std::begin(dp), std::begin(dp) + 2, std::end(dp)); + } + return dp[1][blen]; +} +} // namespace + +void show_candidates(const char *unkopt, option *options) { + for (; *unkopt == '-'; ++unkopt) + ; + if (*unkopt == '\0') { + return; + } + auto unkoptend = unkopt; + for (; *unkoptend && *unkoptend != '='; ++unkoptend) + ; + auto unkoptlen = unkoptend - unkopt; + if (unkoptlen == 0) { + return; + } + int prefix_match = 0; + auto cands = std::vector>(); + for (size_t i = 0; options[i].name != nullptr; ++i) { + auto optnamelen = strlen(options[i].name); + // Use cost 0 for prefix match + if (istartsWith(options[i].name, options[i].name + optnamelen, unkopt, + unkopt + unkoptlen)) { + if (optnamelen == static_cast(unkoptlen)) { + // Exact match, then we don't show any condidates. + return; + } + ++prefix_match; + cands.emplace_back(0, options[i].name); + continue; + } + // Use cost 0 for suffix match, but match at least 3 characters + if (unkoptlen >= 3 && + iendsWith(options[i].name, options[i].name + optnamelen, unkopt, + unkopt + unkoptlen)) { + cands.emplace_back(0, options[i].name); + continue; + } + // cost values are borrowed from git, help.c. + int sim = + levenshtein(unkopt, unkoptlen, options[i].name, optnamelen, 0, 2, 1, 3); + cands.emplace_back(sim, options[i].name); + } + if (prefix_match == 1 || cands.empty()) { + return; + } + std::sort(std::begin(cands), std::end(cands)); + int threshold = cands[0].first; + // threshold value is a magic value. + if (threshold > 6) { + return; + } + std::cerr << "\nDid you mean:\n"; + for (auto &item : cands) { + if (item.first > threshold) { + break; + } + std::cerr << "\t--" << item.second << "\n"; + } +} + +bool has_uri_field(const http_parser_url &u, http_parser_url_fields field) { + return u.field_set & (1 << field); +} + +bool fieldeq(const char *uri1, const http_parser_url &u1, const char *uri2, + const http_parser_url &u2, http_parser_url_fields field) { + if (!has_uri_field(u1, field)) { + if (!has_uri_field(u2, field)) { + return true; + } else { + return false; + } + } else if (!has_uri_field(u2, field)) { + return false; + } + if (u1.field_data[field].len != u2.field_data[field].len) { + return false; + } + return memcmp(uri1 + u1.field_data[field].off, + uri2 + u2.field_data[field].off, u1.field_data[field].len) == 0; +} + +bool fieldeq(const char *uri, const http_parser_url &u, + http_parser_url_fields field, const char *t) { + if (!has_uri_field(u, field)) { + if (!t[0]) { + return true; + } else { + return false; + } + } else if (!t[0]) { + return false; + } + int i, len = u.field_data[field].len; + const char *p = uri + u.field_data[field].off; + for (i = 0; i < len && t[i] && p[i] == t[i]; ++i) + ; + return i == len && !t[i]; +} + +std::string get_uri_field(const char *uri, const http_parser_url &u, + http_parser_url_fields field) { + if (util::has_uri_field(u, field)) { + return std::string(uri + u.field_data[field].off, u.field_data[field].len); + } else { + return ""; + } +} + +uint16_t get_default_port(const char *uri, const http_parser_url &u) { + if (util::fieldeq(uri, u, UF_SCHEMA, "https")) { + return 443; + } else if (util::fieldeq(uri, u, UF_SCHEMA, "http")) { + return 80; + } else { + return 443; + } +} + +bool porteq(const char *uri1, const http_parser_url &u1, const char *uri2, + const http_parser_url &u2) { + uint16_t port1, port2; + port1 = + util::has_uri_field(u1, UF_PORT) ? u1.port : get_default_port(uri1, u1); + port2 = + util::has_uri_field(u2, UF_PORT) ? u2.port : get_default_port(uri2, u2); + return port1 == port2; +} + +void write_uri_field(std::ostream &o, const char *uri, const http_parser_url &u, + http_parser_url_fields field) { + if (util::has_uri_field(u, field)) { + o.write(uri + u.field_data[field].off, u.field_data[field].len); + } +} + +bool numeric_host(const char *hostname) { + struct addrinfo hints; + struct addrinfo *res; + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; + hints.ai_flags = AI_NUMERICHOST; + if (getaddrinfo(hostname, nullptr, &hints, &res)) { + return false; + } + freeaddrinfo(res); + return true; +} + +std::string numeric_name(const struct sockaddr *sa, socklen_t salen) { + std::array host; + auto rv = getnameinfo(sa, salen, host.data(), host.size(), nullptr, 0, + NI_NUMERICHOST); + if (rv != 0) { + return "unknown"; + } + return host.data(); +} + +int reopen_log_file(const char *path) { +#if defined(__ANDROID__) || defined(ANDROID) + int fd; + + if (strcmp("/proc/self/fd/1", path) == 0 || + strcmp("/proc/self/fd/2", path) == 0) { + + // We will get permission denied error when O_APPEND is used for + // these paths. + fd = + open(path, O_WRONLY | O_CREAT | O_CLOEXEC, S_IRUSR | S_IWUSR | S_IRGRP); + } else { + fd = open(path, O_WRONLY | O_APPEND | O_CREAT | O_CLOEXEC, + S_IRUSR | S_IWUSR | S_IRGRP); + } +#elif defined O_CLOEXEC + + auto fd = open(path, O_WRONLY | O_APPEND | O_CREAT | O_CLOEXEC, + S_IRUSR | S_IWUSR | S_IRGRP); +#else // !O_CLOEXEC + + auto fd = + open(path, O_WRONLY | O_APPEND | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP); + + // We get race condition if execve is called at the same time. + if (fd != -1) { + make_socket_closeonexec(fd); + } + +#endif // !O_CLOEXEC + + if (fd == -1) { + return -1; + } + + return fd; +} + +std::string ascii_dump(const uint8_t *data, size_t len) { + std::string res; + + for (size_t i = 0; i < len; ++i) { + auto c = data[i]; + + if (c >= 0x20 && c < 0x7f) { + res += c; + } else { + res += "."; + } + } + + return res; +} + +char *get_exec_path(int argc, char **const argv, const char *cwd) { + if (argc == 0 || cwd == nullptr) { + return nullptr; + } + + auto argv0 = argv[0]; + auto len = strlen(argv0); + + char *path; + + if (argv0[0] == '/') { + path = static_cast(malloc(len + 1)); + if (path == nullptr) { + return nullptr; + } + memcpy(path, argv0, len + 1); + } else { + auto cwdlen = strlen(cwd); + path = static_cast(malloc(len + 1 + cwdlen + 1)); + if (path == nullptr) { + return nullptr; + } + memcpy(path, cwd, cwdlen); + path[cwdlen] = '/'; + memcpy(path + cwdlen + 1, argv0, len + 1); + } + + return path; +} + +bool check_path(const std::string &path) { + // We don't like '\' in path. + return !path.empty() && path[0] == '/' && + path.find('\\') == std::string::npos && + path.find("/../") == std::string::npos && + path.find("/./") == std::string::npos && + !util::endsWith(path, "/..") && !util::endsWith(path, "/."); +} + +int64_t to_time64(const timeval &tv) { + return tv.tv_sec * 1000000 + tv.tv_usec; +} + +bool check_h2_is_selected(const unsigned char *proto, size_t len) { + return streq_l(NGHTTP2_PROTO_VERSION_ID, proto, len) || + streq_l(NGHTTP2_H2_16, proto, len) || + streq_l(NGHTTP2_H2_14, proto, len); +} + +namespace { +bool select_h2(const unsigned char **out, unsigned char *outlen, + const unsigned char *in, unsigned int inlen, const char *key, + unsigned int keylen) { + for (auto p = in, end = in + inlen; p + keylen <= end; p += *p + 1) { + if (std::equal(key, key + keylen, p)) { + *out = p + 1; + *outlen = *p; + return true; + } + } + return false; +} +} // namespace + +bool select_h2(const unsigned char **out, unsigned char *outlen, + const unsigned char *in, unsigned int inlen) { + return select_h2(out, outlen, in, inlen, NGHTTP2_PROTO_ALPN, + str_size(NGHTTP2_PROTO_ALPN)) || + select_h2(out, outlen, in, inlen, NGHTTP2_H2_16_ALPN, + str_size(NGHTTP2_H2_16_ALPN)) || + select_h2(out, outlen, in, inlen, NGHTTP2_H2_14_ALPN, + str_size(NGHTTP2_H2_14_ALPN)); +} + +std::vector get_default_alpn() { + auto res = std::vector(str_size(NGHTTP2_PROTO_ALPN) + + str_size(NGHTTP2_H2_16_ALPN) + + str_size(NGHTTP2_H2_14_ALPN)); + auto p = std::begin(res); + + p = std::copy_n(NGHTTP2_PROTO_ALPN, str_size(NGHTTP2_PROTO_ALPN), p); + p = std::copy_n(NGHTTP2_H2_16_ALPN, str_size(NGHTTP2_H2_16_ALPN), p); + p = std::copy_n(NGHTTP2_H2_14_ALPN, str_size(NGHTTP2_H2_14_ALPN), p); + + return res; +} + +int make_socket_closeonexec(int fd) { + int flags; + int rv; + while ((flags = fcntl(fd, F_GETFD)) == -1 && errno == EINTR) + ; + while ((rv = fcntl(fd, F_SETFD, flags | FD_CLOEXEC)) == -1 && errno == EINTR) + ; + return rv; +} + +int make_socket_nonblocking(int fd) { + int flags; + int rv; + while ((flags = fcntl(fd, F_GETFL, 0)) == -1 && errno == EINTR) + ; + while ((rv = fcntl(fd, F_SETFL, flags | O_NONBLOCK)) == -1 && errno == EINTR) + ; + return rv; +} + +int make_socket_nodelay(int fd) { + int val = 1; + if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, reinterpret_cast(&val), + sizeof(val)) == -1) { + return -1; + } + return 0; +} + +int create_nonblock_socket(int family) { +#ifdef SOCK_NONBLOCK + auto fd = socket(family, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0); + + if (fd == -1) { + return -1; + } +#else // !SOCK_NONBLOCK + auto fd = socket(family, SOCK_STREAM, 0); + + if (fd == -1) { + return -1; + } + + make_socket_nonblocking(fd); + make_socket_closeonexec(fd); +#endif // !SOCK_NONBLOCK + + if (family == AF_INET || family == AF_INET6) { + make_socket_nodelay(fd); + } + + return fd; +} + +bool check_socket_connected(int fd) { + int error; + socklen_t len = sizeof(error); + if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &error, &len) == 0) { + if (error != 0) { + return false; + } + } + return true; +} + +bool ipv6_numeric_addr(const char *host) { + uint8_t dst[16]; + return inet_pton(AF_INET6, host, dst) == 1; +} + +namespace { +std::pair parse_uint_digits(const void *ss, size_t len) { + const uint8_t *s = static_cast(ss); + int64_t n = 0; + size_t i; + if (len == 0) { + return {-1, 0}; + } + constexpr int64_t max = std::numeric_limits::max(); + for (i = 0; i < len; ++i) { + if ('0' <= s[i] && s[i] <= '9') { + if (n > max / 10) { + return {-1, 0}; + } + n *= 10; + if (n > max - (s[i] - '0')) { + return {-1, 0}; + } + n += s[i] - '0'; + continue; + } + break; + } + if (i == 0) { + return {-1, 0}; + } + return {n, i}; +} +} // namespace + +int64_t parse_uint_with_unit(const char *s) { + int64_t n; + size_t i; + auto len = strlen(s); + std::tie(n, i) = parse_uint_digits(s, len); + if (n == -1) { + return -1; + } + if (i == len) { + return n; + } + if (i + 1 != len) { + return -1; + } + int mul = 1; + switch (s[i]) { + case 'K': + case 'k': + mul = 1 << 10; + break; + case 'M': + case 'm': + mul = 1 << 20; + break; + case 'G': + case 'g': + mul = 1 << 30; + break; + default: + return -1; + } + constexpr int64_t max = std::numeric_limits::max(); + if (n > max / mul) { + return -1; + } + return n * mul; +} + +int64_t parse_uint(const char *s) { + return parse_uint(reinterpret_cast(s), strlen(s)); +} + +int64_t parse_uint(const std::string &s) { + return parse_uint(reinterpret_cast(s.c_str()), s.size()); +} + +int64_t parse_uint(const uint8_t *s, size_t len) { + int64_t n; + size_t i; + std::tie(n, i) = parse_uint_digits(s, len); + if (n == -1 || i != len) { + return -1; + } + return n; +} + +double parse_duration_with_unit(const char *s) { + constexpr auto max = std::numeric_limits::max(); + int64_t n; + size_t i; + auto len = strlen(s); + std::tie(n, i) = parse_uint_digits(s, len); + if (n == -1) { + goto fail; + } + if (i == len) { + return static_cast(n); + } + switch (s[i]) { + case 'S': + case 's': + // seconds + if (i + 1 != len) { + goto fail; + } + return static_cast(n); + case 'M': + case 'm': + if (i + 1 == len) { + // minutes + if (n > max / 60) { + goto fail; + } + return static_cast(n) * 60; + } + + if (i + 2 != len || (s[i + 1] != 's' && s[i + 1] != 'S')) { + goto fail; + } + // milliseconds + return static_cast(n) / 1000.; + case 'H': + case 'h': + // hours + if (i + 1 != len) { + goto fail; + } + if (n > max / 3600) { + goto fail; + } + return static_cast(n) * 3600; + } +fail: + return std::numeric_limits::infinity(); +} + +std::string duration_str(double t) { + if (t == 0.) { + return "0"; + } + auto frac = static_cast(t * 1000) % 1000; + if (frac > 0) { + return utos(static_cast(t * 1000)) + "ms"; + } + auto v = static_cast(t); + if (v % 60) { + return utos(v) + "s"; + } + v /= 60; + if (v % 60) { + return utos(v) + "m"; + } + v /= 60; + return utos(v) + "h"; +} + +std::string format_duration(const std::chrono::microseconds &u) { + const char *unit = "us"; + int d = 0; + auto t = u.count(); + if (t >= 1000000) { + d = 1000000; + unit = "s"; + } else if (t >= 1000) { + d = 1000; + unit = "ms"; + } else { + return utos(t) + unit; + } + return dtos(static_cast(t) / d) + unit; +} + +std::string dtos(double n) { + auto f = utos(static_cast(round(100. * n)) % 100); + return utos(static_cast(n)) + "." + (f.size() == 1 ? "0" : "") + f; +} + +std::string make_hostport(const char *host, uint16_t port) { + auto ipv6 = ipv6_numeric_addr(host); + std::string hostport; + + if (ipv6) { + hostport += "["; + } + + hostport += host; + + if (ipv6) { + hostport += "]"; + } + + if (port != 80 && port != 443) { + hostport += ":"; + hostport += utos(port); + } + + return hostport; +} + +namespace { +void hexdump8(FILE *out, const uint8_t *first, const uint8_t *last) { + auto stop = std::min(first + 8, last); + for (auto k = first; k != stop; ++k) { + fprintf(out, "%02x ", *k); + } + // each byte needs 3 spaces (2 hex value and space) + for (; stop != first + 8; ++stop) { + fputs(" ", out); + } + // we have extra space after 8 bytes + fputc(' ', out); +} +} // namespace + +void hexdump(FILE *out, const uint8_t *src, size_t len) { + if (len == 0) { + return; + } + size_t buflen = 0; + auto repeated = false; + std::array buf{}; + auto end = src + len; + auto i = src; + for (;;) { + auto nextlen = + std::min(static_cast(16), static_cast(end - i)); + if (nextlen == buflen && + std::equal(std::begin(buf), std::begin(buf) + buflen, i)) { + // as long as adjacent 16 bytes block are the same, we just + // print single '*'. + if (!repeated) { + repeated = true; + fputs("*\n", out); + } + i += nextlen; + continue; + } + repeated = false; + fprintf(out, "%08lx", static_cast(i - src)); + if (i == end) { + fputc('\n', out); + break; + } + fputs(" ", out); + hexdump8(out, i, end); + hexdump8(out, i + 8, std::max(i + 8, end)); + fputc('|', out); + auto stop = std::min(i + 16, end); + buflen = stop - i; + auto p = buf.data(); + for (; i != stop; ++i) { + *p++ = *i; + if (0x20 <= *i && *i <= 0x7e) { + fputc(*i, out); + } else { + fputc('.', out); + } + } + fputs("|\n", out); + } +} + +} // namespace util + +} // namespace nghttp2 diff --git a/src/util.h b/src/util.h new file mode 100644 index 0000000..88e1ff5 --- /dev/null +++ b/src/util.h @@ -0,0 +1,626 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef UTIL_H +#define UTIL_H + +#include "nghttp2_config.h" + +#ifdef HAVE_UNISTD_H +#include +#endif // HAVE_UNISTD_H +#include +#ifdef HAVE_NETDB_H +#include +#endif // HAVE_NETDB_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "http-parser/http_parser.h" + +namespace nghttp2 { + +// The additional HTTP/2 protocol ALPN protocol identifier we also +// supports for our applications to make smooth migration into final +// h2 ALPN ID. +#define NGHTTP2_H2_16_ALPN "\x5h2-16" +#define NGHTTP2_H2_16 "h2-16" + +#define NGHTTP2_H2_14_ALPN "\x5h2-14" +#define NGHTTP2_H2_14 "h2-14" + +namespace util { + +extern const char DEFAULT_STRIP_CHARSET[]; + +template +std::pair +stripIter(InputIterator first, InputIterator last, + const char *chars = DEFAULT_STRIP_CHARSET) { + for (; first != last && strchr(chars, *first) != 0; ++first) + ; + if (first == last) { + return std::make_pair(first, last); + } + InputIterator left = last - 1; + for (; left != first && strchr(chars, *left) != 0; --left) + ; + return std::make_pair(first, left + 1); +} + +template +OutputIterator splitIter(InputIterator first, InputIterator last, + OutputIterator out, char delim, bool doStrip = false, + bool allowEmpty = false) { + for (InputIterator i = first; i != last;) { + InputIterator j = std::find(i, last, delim); + std::pair p(i, j); + if (doStrip) { + p = stripIter(i, j); + } + if (allowEmpty || p.first != p.second) { + *out++ = p; + } + i = j; + if (j != last) { + ++i; + } + } + if (allowEmpty && (first == last || *(last - 1) == delim)) { + *out++ = std::make_pair(last, last); + } + return out; +} + +template +OutputIterator split(InputIterator first, InputIterator last, + OutputIterator out, char delim, bool doStrip = false, + bool allowEmpty = false) { + for (InputIterator i = first; i != last;) { + InputIterator j = std::find(i, last, delim); + std::pair p(i, j); + if (doStrip) { + p = stripIter(i, j); + } + if (allowEmpty || p.first != p.second) { + *out++ = std::string(p.first, p.second); + } + i = j; + if (j != last) { + ++i; + } + } + if (allowEmpty && (first == last || *(last - 1) == delim)) { + *out++ = std::string(last, last); + } + return out; +} + +template +std::string strjoin(InputIterator first, InputIterator last, + const DelimiterType &delim) { + std::string result; + if (first == last) { + return result; + } + InputIterator beforeLast = last - 1; + for (; first != beforeLast; ++first) { + result += *first; + result += delim; + } + result += *beforeLast; + return result; +} + +template +std::string joinPath(InputIterator first, InputIterator last) { + std::vector elements; + for (; first != last; ++first) { + if (*first == "..") { + if (!elements.empty()) { + elements.pop_back(); + } + } else if (*first == ".") { + // do nothing + } else { + elements.push_back(*first); + } + } + return strjoin(elements.begin(), elements.end(), "/"); +} + +bool isAlpha(const char c); + +bool isDigit(const char c); + +bool isHexDigit(const char c); + +bool inRFC3986UnreservedChars(const char c); + +bool in_rfc3986_sub_delims(const char c); + +// Returns true if |c| is in token (HTTP-p1, Section 3.2.6) +bool in_token(char c); + +bool in_attr_char(char c); + +// Returns integer corresponding to hex notation |c|. It is undefined +// if isHexDigit(c) is false. +uint32_t hex_to_uint(char c); + +std::string percentEncode(const unsigned char *target, size_t len); + +std::string percentEncode(const std::string &target); + +// percent-encode path component of URI |s|. +std::string percent_encode_path(const std::string &s); + +template +std::string percentDecode(InputIt first, InputIt last) { + std::string result; + for (; first != last; ++first) { + if (*first == '%') { + if (first + 1 != last && first + 2 != last && isHexDigit(*(first + 1)) && + isHexDigit(*(first + 2))) { + result += (hex_to_uint(*(first + 1)) << 4) + hex_to_uint(*(first + 2)); + first += 2; + continue; + } + result += *first; + continue; + } + result += *first; + } + return result; +} + +// Percent encode |target| if character is not in token or '%'. +std::string percent_encode_token(const std::string &target); + +// Returns quotedString version of |target|. Currently, this function +// just replace '"' with '\"'. +std::string quote_string(const std::string &target); + +std::string format_hex(const unsigned char *s, size_t len); + +std::string http_date(time_t t); + +// Returns given time |t| from epoch in Common Log format (e.g., +// 03/Jul/2014:00:19:38 +0900) +std::string common_log_date(time_t t); + +// Returns given millisecond |ms| from epoch in ISO 8601 format (e.g., +// 2014-11-15T12:58:24.741Z) +std::string iso8601_date(int64_t ms); + +time_t parse_http_date(const std::string &s); + +char upcase(char c); + +inline char lowcase(char c) { + static unsigned char tbl[] = { + 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, 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, 52, 53, 54, 55, 56, 57, 58, 59, + 60, 61, 62, 63, 64, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', + 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', + 'z', 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, + 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, + 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, + 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, + 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, + 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, + 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, + 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, + 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, + 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, + 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, + 255, + }; + return tbl[static_cast(c)]; +} + +template +bool startsWith(InputIterator1 first1, InputIterator1 last1, + InputIterator2 first2, InputIterator2 last2) { + if (last1 - first1 < last2 - first2) { + return false; + } + return std::equal(first2, last2, first1); +} + +inline bool startsWith(const std::string &a, const std::string &b) { + return startsWith(std::begin(a), std::end(a), std::begin(b), std::end(b)); +} + +inline bool startsWith(const char *a, const char *b) { + return startsWith(a, a + strlen(a), b, b + strlen(b)); +} + +struct CaseCmp { + bool operator()(char lhs, char rhs) const { + return lowcase(lhs) == lowcase(rhs); + } +}; + +template +bool istartsWith(InputIterator1 first1, InputIterator1 last1, + InputIterator2 first2, InputIterator2 last2) { + if (last1 - first1 < last2 - first2) { + return false; + } + return std::equal(first2, last2, first1, CaseCmp()); +} + +inline bool istartsWith(const std::string &a, const std::string &b) { + return istartsWith(std::begin(a), std::end(a), std::begin(b), std::end(b)); +} + +template +bool istartsWith(InputIt a, size_t an, const char *b) { + return istartsWith(a, a + an, b, b + strlen(b)); +} + +bool istartsWith(const char *a, const char *b); + +template +bool endsWith(InputIterator1 first1, InputIterator1 last1, + InputIterator2 first2, InputIterator2 last2) { + if (last1 - first1 < last2 - first2) { + return false; + } + return std::equal(first2, last2, last1 - (last2 - first2)); +} + +inline bool endsWith(const std::string &a, const std::string &b) { + return endsWith(std::begin(a), std::end(a), std::begin(b), std::end(b)); +} + +template +bool iendsWith(InputIterator1 first1, InputIterator1 last1, + InputIterator2 first2, InputIterator2 last2) { + if (last1 - first1 < last2 - first2) { + return false; + } + return std::equal(first2, last2, last1 - (last2 - first2), CaseCmp()); +} + +inline bool iendsWith(const std::string &a, const std::string &b) { + return iendsWith(std::begin(a), std::end(a), std::begin(b), std::end(b)); +} + +int strcompare(const char *a, const uint8_t *b, size_t n); + +template bool strieq(const char *a, InputIt b, size_t bn) { + if (!a) { + return false; + } + auto blast = b + bn; + for (; *a && b != blast && lowcase(*a) == lowcase(*b); ++a, ++b) + ; + return !*a && b == blast; +} + +template +bool strieq(InputIt1 a, size_t alen, InputIt2 b, size_t blen) { + if (alen != blen) { + return false; + } + return std::equal(a, a + alen, b, CaseCmp()); +} + +inline bool strieq(const std::string &a, const std::string &b) { + return strieq(std::begin(a), a.size(), std::begin(b), b.size()); +} + +bool strieq(const char *a, const char *b); + +template +bool strieq_l(const char (&a)[N], InputIt b, size_t blen) { + return strieq(a, N - 1, b, blen); +} + +template bool strieq_l(const char (&a)[N], const std::string &b) { + return strieq(a, N - 1, std::begin(b), b.size()); +} + +template bool streq(const char *a, InputIt b, size_t bn) { + if (!a) { + return false; + } + auto blast = b + bn; + for (; *a && b != blast && *a == *b; ++a, ++b) + ; + return !*a && b == blast; +} + +template +bool streq(InputIt1 a, size_t alen, InputIt2 b, size_t blen) { + if (alen != blen) { + return false; + } + return std::equal(a, a + alen, b); +} + +inline bool streq(const char *a, const char *b) { + if (!a || !b) { + return false; + } + return streq(a, strlen(a), b, strlen(b)); +} + +template +bool streq_l(const char (&a)[N], InputIt b, size_t blen) { + return streq(a, N - 1, b, blen); +} + +bool strifind(const char *a, const char *b); + +// Lowercase |s| in place. +inline void inp_strlower(std::string &s) { + std::transform(std::begin(s), std::end(s), std::begin(s), lowcase); +} + +// Returns string representation of |n| with 2 fractional digits. +std::string dtos(double n); + +template std::string utos(T n) { + std::string res; + if (n == 0) { + res = "0"; + return res; + } + int i = 0; + T t = n; + for (; t; t /= 10, ++i) + ; + res.resize(i); + --i; + for (; n; --i, n /= 10) { + res[i] = (n % 10) + '0'; + } + return res; +} + +template std::string utos_with_unit(T n) { + char u = 0; + if (n >= (1 << 30)) { + u = 'G'; + n /= (1 << 30); + } else if (n >= (1 << 20)) { + u = 'M'; + n /= (1 << 20); + } else if (n >= (1 << 10)) { + u = 'K'; + n /= (1 << 10); + } + if (u == 0) { + return utos(n); + } + return utos(n) + u; +} + +// Like utos_with_unit(), but 2 digits fraction part is followed. +template std::string utos_with_funit(T n) { + char u = 0; + int b = 0; + if (n >= (1 << 30)) { + u = 'G'; + b = 30; + } else if (n >= (1 << 20)) { + u = 'M'; + b = 20; + } else if (n >= (1 << 10)) { + u = 'K'; + b = 10; + } + if (b == 0) { + return utos(n); + } + return dtos(static_cast(n) / (1 << b)) + u; +} + +extern const char UPPER_XDIGITS[]; + +template std::string utox(T n) { + std::string res; + if (n == 0) { + res = "0"; + return res; + } + int i = 0; + T t = n; + for (; t; t /= 16, ++i) + ; + res.resize(i); + --i; + for (; n; --i, n /= 16) { + res[i] = UPPER_XDIGITS[(n & 0x0f)]; + } + return res; +} + +void to_token68(std::string &base64str); +void to_base64(std::string &token68str); + +void show_candidates(const char *unkopt, option *options); + +bool has_uri_field(const http_parser_url &u, http_parser_url_fields field); + +bool fieldeq(const char *uri1, const http_parser_url &u1, const char *uri2, + const http_parser_url &u2, http_parser_url_fields field); + +bool fieldeq(const char *uri, const http_parser_url &u, + http_parser_url_fields field, const char *t); + +std::string get_uri_field(const char *uri, const http_parser_url &u, + http_parser_url_fields field); + +uint16_t get_default_port(const char *uri, const http_parser_url &u); + +bool porteq(const char *uri1, const http_parser_url &u1, const char *uri2, + const http_parser_url &u2); + +void write_uri_field(std::ostream &o, const char *uri, const http_parser_url &u, + http_parser_url_fields field); + +bool numeric_host(const char *hostname); + +// Returns numeric address string of |addr|. If getnameinfo() is +// failed, "unknown" is returned. +std::string numeric_name(const struct sockaddr *sa, socklen_t salen); + +// Opens |path| with O_APPEND enabled. If file does not exist, it is +// created first. This function returns file descriptor referring the +// opened file if it succeeds, or -1. +int reopen_log_file(const char *path); + +// Returns ASCII dump of |data| of length |len|. Only ASCII printable +// characters are preserved. Other characters are replaced with ".". +std::string ascii_dump(const uint8_t *data, size_t len); + +// Returns absolute path of executable path. If argc == 0 or |cwd| is +// nullptr, this function returns nullptr. If argv[0] starts with +// '/', this function returns argv[0]. Oterwise return cwd + "/" + +// argv[0]. If non-null is returned, it is NULL-terminated string and +// dynamically allocated by malloc. The caller is responsible to free +// it. +char *get_exec_path(int argc, char **const argv, const char *cwd); + +// Validates path so that it does not contain directory traversal +// vector. Returns true if path is safe. The |path| must start with +// "/" otherwise returns false. This function should be called after +// percent-decode was performed. +bool check_path(const std::string &path); + +// Returns the |tv| value as 64 bit integer using a microsecond as an +// unit. +int64_t to_time64(const timeval &tv); + +// Returns true if ALPN ID |proto| of length |len| is supported HTTP/2 +// protocol identifier. +bool check_h2_is_selected(const unsigned char *alpn, size_t len); + +// Selects h2 protocol ALPN ID if one of supported h2 versions are +// present in |in| of length inlen. Returns true if h2 version is +// selected. +bool select_h2(const unsigned char **out, unsigned char *outlen, + const unsigned char *in, unsigned int inlen); + +// Returns default ALPN protocol list, which only contains supported +// HTTP/2 protocol identifier. +std::vector get_default_alpn(); + +// Returns given time |tp| in Common Log format (e.g., +// 03/Jul/2014:00:19:38 +0900) +// Expected type of |tp| is std::chrono::timepoint +template std::string format_common_log(const T &tp) { + auto t = + std::chrono::duration_cast(tp.time_since_epoch()); + return common_log_date(t.count()); +} + +// Returns given time |tp| in ISO 8601 format (e.g., +// 2014-11-15T12:58:24.741Z) +// Expected type of |tp| is std::chrono::timepoint +template std::string format_iso8601(const T &tp) { + auto t = std::chrono::duration_cast( + tp.time_since_epoch()); + return iso8601_date(t.count()); +} + +// Return the system precision of the template parameter |Clock| as +// a nanosecond value of type |Rep| +template Rep clock_precision() { + std::chrono::duration duration = typename Clock::duration(1); + + return duration.count(); +} + +int make_socket_closeonexec(int fd); +int make_socket_nonblocking(int fd); +int make_socket_nodelay(int fd); + +int create_nonblock_socket(int family); + +bool check_socket_connected(int fd); + +// Returns true if |host| is IPv6 numeric address (e.g., ::1) +bool ipv6_numeric_addr(const char *host); + +// Parses NULL terminated string |s| as unsigned integer and returns +// the parsed integer. Additionally, if |s| ends with 'k', 'm', 'g' +// and its upper case characters, multiply the integer by 1024, 1024 * +// 1024 and 1024 * 1024 respectively. If there is an error, returns +// -1. +int64_t parse_uint_with_unit(const char *s); + +// Parses NULL terminated string |s| as unsigned integer and returns +// the parsed integer. If there is an error, returns -1. +int64_t parse_uint(const char *s); +int64_t parse_uint(const uint8_t *s, size_t len); +int64_t parse_uint(const std::string &s); + +// Parses NULL terminated string |s| as unsigned integer and returns +// the parsed integer casted to double. If |s| ends with "s", the +// parsed value's unit is a second. If |s| ends with "ms", the unit +// is millisecond. If none of them are given, the unit is second. +// This function returns std::numeric_limits::infinity() if +// error occurs. +double parse_duration_with_unit(const char *s); + +// Returns string representation of time duration |t|. If t has +// fractional part (at least more than or equal to 1e-3), |t| is +// multiplied by 1000 and the unit "ms" is appended. Otherwise, |t| +// is left as is and "s" is appended. +std::string duration_str(double t); + +// Returns string representation of time duration |t|. It appends +// unit after the formatting. The available units are s, ms and us. +// The unit which is equal to or less than |t| is used and 2 +// fractional digits follow. +std::string format_duration(const std::chrono::microseconds &u); + +// Creates "host:port" string using given |host| and |port|. If +// |host| is numeric IPv6 address (e.g., ::1), it is enclosed by "[" +// and "]". If |port| is 80 or 443, port part is omitted. +std::string make_hostport(const char *host, uint16_t port); + +// Dumps |src| of length |len| in the format similar to `hexdump -C`. +void hexdump(FILE *out, const uint8_t *src, size_t len); + +} // namespace util + +} // namespace nghttp2 + +#endif // UTIL_H diff --git a/src/util_test.cc b/src/util_test.cc new file mode 100644 index 0000000..c1100b7 --- /dev/null +++ b/src/util_test.cc @@ -0,0 +1,370 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2013 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "util_test.h" + +#include +#include + +#include + +#include + +#include "util.h" +#include "template.h" + +using namespace nghttp2; + +namespace shrpx { + +void test_util_streq(void) { + CU_ASSERT(util::streq("alpha", "alpha", 5)); + CU_ASSERT(util::streq("alpha", "alphabravo", 5)); + CU_ASSERT(!util::streq("alpha", "alphabravo", 6)); + CU_ASSERT(!util::streq("alphabravo", "alpha", 5)); + CU_ASSERT(!util::streq("alpha", "alphA", 5)); + CU_ASSERT(!util::streq("", "a", 1)); + CU_ASSERT(util::streq("", "", 0)); + CU_ASSERT(!util::streq("alpha", "", 0)); + + CU_ASSERT(util::streq("alpha", 5, "alpha", 5)); + CU_ASSERT(!util::streq("alpha", 4, "alpha", 5)); + CU_ASSERT(!util::streq("alpha", 5, "alpha", 4)); + CU_ASSERT(!util::streq("alpha", 5, "alphA", 5)); + char *a = nullptr; + char *b = nullptr; + CU_ASSERT(util::streq(a, 0, b, 0)); + + CU_ASSERT(util::streq_l("alpha", "alpha", 5)); + CU_ASSERT(util::streq_l("alpha", "alphabravo", 5)); + CU_ASSERT(!util::streq_l("alpha", "alphabravo", 6)); + CU_ASSERT(!util::streq_l("alphabravo", "alpha", 5)); + CU_ASSERT(!util::streq_l("alpha", "alphA", 5)); + CU_ASSERT(!util::streq_l("", "a", 1)); + CU_ASSERT(util::streq_l("", "", 0)); + CU_ASSERT(!util::streq_l("alpha", "", 0)); +} + +void test_util_strieq(void) { + CU_ASSERT(util::strieq(std::string("alpha"), std::string("alpha"))); + CU_ASSERT(util::strieq(std::string("alpha"), std::string("AlPhA"))); + CU_ASSERT(util::strieq(std::string(), std::string())); + CU_ASSERT(!util::strieq(std::string("alpha"), std::string("AlPhA "))); + CU_ASSERT(!util::strieq(std::string(), std::string("AlPhA "))); + + CU_ASSERT(util::strieq("alpha", "alpha", 5)); + CU_ASSERT(util::strieq("alpha", "AlPhA", 5)); + CU_ASSERT(util::strieq("", static_cast(nullptr), 0)); + CU_ASSERT(!util::strieq("alpha", "AlPhA ", 6)); + CU_ASSERT(!util::strieq("", "AlPhA ", 6)); + + CU_ASSERT(util::strieq("alpha", "alpha")); + CU_ASSERT(util::strieq("alpha", "AlPhA")); + CU_ASSERT(util::strieq("", "")); + CU_ASSERT(!util::strieq("alpha", "AlPhA ")); + CU_ASSERT(!util::strieq("", "AlPhA ")); + + CU_ASSERT(util::strieq_l("alpha", "alpha", 5)); + CU_ASSERT(util::strieq_l("alpha", "AlPhA", 5)); + CU_ASSERT(util::strieq_l("", static_cast(nullptr), 0)); + CU_ASSERT(!util::strieq_l("alpha", "AlPhA ", 6)); + CU_ASSERT(!util::strieq_l("", "AlPhA ", 6)); + + CU_ASSERT(util::strieq_l("alpha", "alpha")); + CU_ASSERT(util::strieq_l("alpha", "AlPhA")); + CU_ASSERT(util::strieq_l("", "")); + CU_ASSERT(!util::strieq_l("alpha", "AlPhA ")); + CU_ASSERT(!util::strieq_l("", "AlPhA ")); +} + +void test_util_inp_strlower(void) { + std::string a("alPha"); + util::inp_strlower(a); + CU_ASSERT("alpha" == a); + + a = "ALPHA123BRAVO"; + util::inp_strlower(a); + CU_ASSERT("alpha123bravo" == a); + + a = ""; + util::inp_strlower(a); + CU_ASSERT("" == a); +} + +void test_util_to_base64(void) { + std::string x = "AAA--B_"; + util::to_base64(x); + CU_ASSERT("AAA++B/=" == x); + + x = "AAA--B_B"; + util::to_base64(x); + CU_ASSERT("AAA++B/B" == x); +} + +void test_util_to_token68(void) { + std::string x = "AAA++B/="; + util::to_token68(x); + CU_ASSERT("AAA--B_" == x); + + x = "AAA++B/B"; + util::to_token68(x); + CU_ASSERT("AAA--B_B" == x); +} + +void test_util_percent_encode_token(void) { + CU_ASSERT("h2" == util::percent_encode_token("h2")); + CU_ASSERT("h3~" == util::percent_encode_token("h3~")); + CU_ASSERT("100%25" == util::percent_encode_token("100%")); + CU_ASSERT("http%202" == util::percent_encode_token("http 2")); +} + +void test_util_percent_encode_path(void) { + CU_ASSERT("/foo1/bar%3F&/%0A" == util::percent_encode_path("/foo1/bar?&/" + "\x0a")); +} + +void test_util_percent_decode(void) { + { + std::string s = "%66%6F%6f%62%61%72"; + CU_ASSERT("foobar" == util::percentDecode(std::begin(s), std::end(s))); + } + { + std::string s = "%66%6"; + CU_ASSERT("f%6" == util::percentDecode(std::begin(s), std::end(s))); + } + { + std::string s = "%66%"; + CU_ASSERT("f%" == util::percentDecode(std::begin(s), std::end(s))); + } +} + +void test_util_quote_string(void) { + CU_ASSERT("alpha" == util::quote_string("alpha")); + CU_ASSERT("" == util::quote_string("")); + CU_ASSERT("\\\"alpha\\\"" == util::quote_string("\"alpha\"")); +} + +void test_util_utox(void) { + CU_ASSERT("0" == util::utox(0)); + CU_ASSERT("1" == util::utox(1)); + CU_ASSERT("F" == util::utox(15)); + CU_ASSERT("10" == util::utox(16)); + CU_ASSERT("3B9ACA07" == util::utox(1000000007)); + CU_ASSERT("100000000" == util::utox(1LL << 32)); +} + +void test_util_http_date(void) { + CU_ASSERT("Thu, 01 Jan 1970 00:00:00 GMT" == util::http_date(0)); + CU_ASSERT("Wed, 29 Feb 2012 09:15:16 GMT" == util::http_date(1330506916)); +} + +void test_util_select_h2(void) { + const unsigned char *out = NULL; + unsigned char outlen = 0; + + // Check single entry and select it. + const unsigned char t1[] = "\x2h2"; + CU_ASSERT(util::select_h2(&out, &outlen, t1, sizeof(t1) - 1)); + CU_ASSERT( + memcmp(NGHTTP2_PROTO_VERSION_ID, out, NGHTTP2_PROTO_VERSION_ID_LEN) == 0); + CU_ASSERT(NGHTTP2_PROTO_VERSION_ID_LEN == outlen); + + out = NULL; + outlen = 0; + + // Check the case where id is correct but length is invalid and too + // long. + const unsigned char t2[] = "\x6h2-14"; + CU_ASSERT(!util::select_h2(&out, &outlen, t2, sizeof(t2) - 1)); + + // Check the case where h2 is located after bogus ID. + const unsigned char t3[] = "\x2h3\x2h2"; + CU_ASSERT(util::select_h2(&out, &outlen, t3, sizeof(t3) - 1)); + + CU_ASSERT( + memcmp(NGHTTP2_PROTO_VERSION_ID, out, NGHTTP2_PROTO_VERSION_ID_LEN) == 0); + CU_ASSERT(NGHTTP2_PROTO_VERSION_ID_LEN == outlen); + + out = NULL; + outlen = 0; + + // Check the case that last entry's length is invalid and too long. + const unsigned char t4[] = "\x2h3\x6h2-14"; + CU_ASSERT(!util::select_h2(&out, &outlen, t4, sizeof(t4) - 1)); + + // Check the case that all entries are not supported. + const unsigned char t5[] = "\x2h3\x2h4"; + CU_ASSERT(!util::select_h2(&out, &outlen, t5, sizeof(t5) - 1)); + + // Check the case where 2 values are eligible, but last one is + // picked up because it has precedence over the other. + const unsigned char t6[] = "\x5h2-14\x5h2-16"; + CU_ASSERT(util::select_h2(&out, &outlen, t6, sizeof(t6) - 1)); + CU_ASSERT(memcmp(NGHTTP2_H2_16, out, str_size(NGHTTP2_H2_16)) == 0); + CU_ASSERT(str_size(NGHTTP2_H2_16) == outlen); +} + +void test_util_ipv6_numeric_addr(void) { + CU_ASSERT(util::ipv6_numeric_addr("::1")); + CU_ASSERT(util::ipv6_numeric_addr("2001:0db8:85a3:0042:1000:8a2e:0370:7334")); + // IPv4 + CU_ASSERT(!util::ipv6_numeric_addr("127.0.0.1")); + // not numeric address + CU_ASSERT(!util::ipv6_numeric_addr("localhost")); +} + +void test_util_utos_with_unit(void) { + CU_ASSERT("0" == util::utos_with_unit(0)); + CU_ASSERT("1023" == util::utos_with_unit(1023)); + CU_ASSERT("1K" == util::utos_with_unit(1024)); + CU_ASSERT("1K" == util::utos_with_unit(1025)); + CU_ASSERT("1M" == util::utos_with_unit(1 << 20)); + CU_ASSERT("1G" == util::utos_with_unit(1 << 30)); + CU_ASSERT("1024G" == util::utos_with_unit(1LL << 40)); +} + +void test_util_utos_with_funit(void) { + CU_ASSERT("0" == util::utos_with_funit(0)); + CU_ASSERT("1023" == util::utos_with_funit(1023)); + CU_ASSERT("1.00K" == util::utos_with_funit(1024)); + CU_ASSERT("1.00K" == util::utos_with_funit(1025)); + CU_ASSERT("1.09K" == util::utos_with_funit(1119)); + CU_ASSERT("1.27K" == util::utos_with_funit(1300)); + CU_ASSERT("1.00M" == util::utos_with_funit(1 << 20)); + CU_ASSERT("1.18M" == util::utos_with_funit(1234567)); + CU_ASSERT("1.00G" == util::utos_with_funit(1 << 30)); + CU_ASSERT("4492450797.23G" == util::utos_with_funit(4823732313248234343LL)); + CU_ASSERT("1024.00G" == util::utos_with_funit(1LL << 40)); +} + +void test_util_parse_uint_with_unit(void) { + CU_ASSERT(0 == util::parse_uint_with_unit("0")); + CU_ASSERT(1023 == util::parse_uint_with_unit("1023")); + CU_ASSERT(1024 == util::parse_uint_with_unit("1k")); + CU_ASSERT(2048 == util::parse_uint_with_unit("2K")); + CU_ASSERT(1 << 20 == util::parse_uint_with_unit("1m")); + CU_ASSERT(1 << 21 == util::parse_uint_with_unit("2M")); + CU_ASSERT(1 << 30 == util::parse_uint_with_unit("1g")); + CU_ASSERT(1LL << 31 == util::parse_uint_with_unit("2G")); + CU_ASSERT(9223372036854775807LL == + util::parse_uint_with_unit("9223372036854775807")); + // check overflow case + CU_ASSERT(-1 == util::parse_uint_with_unit("9223372036854775808")); + CU_ASSERT(-1 == util::parse_uint_with_unit("10000000000000000000")); + CU_ASSERT(-1 == util::parse_uint_with_unit("9223372036854775807G")); + // bad characters + CU_ASSERT(-1 == util::parse_uint_with_unit("1.1")); + CU_ASSERT(-1 == util::parse_uint_with_unit("1a")); + CU_ASSERT(-1 == util::parse_uint_with_unit("a1")); + CU_ASSERT(-1 == util::parse_uint_with_unit("1T")); + CU_ASSERT(-1 == util::parse_uint_with_unit("")); +} + +void test_util_parse_uint(void) { + CU_ASSERT(0 == util::parse_uint("0")); + CU_ASSERT(1023 == util::parse_uint("1023")); + CU_ASSERT(-1 == util::parse_uint("1k")); + CU_ASSERT(9223372036854775807LL == util::parse_uint("9223372036854775807")); + // check overflow case + CU_ASSERT(-1 == util::parse_uint("9223372036854775808")); + CU_ASSERT(-1 == util::parse_uint("10000000000000000000")); + // bad characters + CU_ASSERT(-1 == util::parse_uint("1.1")); + CU_ASSERT(-1 == util::parse_uint("1a")); + CU_ASSERT(-1 == util::parse_uint("a1")); + CU_ASSERT(-1 == util::parse_uint("1T")); + CU_ASSERT(-1 == util::parse_uint("")); +} + +void test_util_parse_duration_with_unit(void) { + CU_ASSERT(0. == util::parse_duration_with_unit("0")); + CU_ASSERT(123. == util::parse_duration_with_unit("123")); + CU_ASSERT(123. == util::parse_duration_with_unit("123s")); + CU_ASSERT(0.500 == util::parse_duration_with_unit("500ms")); + CU_ASSERT(123. == util::parse_duration_with_unit("123S")); + CU_ASSERT(0.500 == util::parse_duration_with_unit("500MS")); + CU_ASSERT(180 == util::parse_duration_with_unit("3m")); + CU_ASSERT(3600 * 5 == util::parse_duration_with_unit("5h")); + + auto err = std::numeric_limits::infinity(); + // check overflow case + CU_ASSERT(err == util::parse_duration_with_unit("9223372036854775808")); + // bad characters + CU_ASSERT(err == util::parse_duration_with_unit("0u")); + CU_ASSERT(err == util::parse_duration_with_unit("0xs")); + CU_ASSERT(err == util::parse_duration_with_unit("0mt")); + CU_ASSERT(err == util::parse_duration_with_unit("0mss")); + CU_ASSERT(err == util::parse_duration_with_unit("s")); + CU_ASSERT(err == util::parse_duration_with_unit("ms")); +} + +void test_util_duration_str(void) { + CU_ASSERT("0" == util::duration_str(0.)); + CU_ASSERT("1s" == util::duration_str(1.)); + CU_ASSERT("500ms" == util::duration_str(0.5)); + CU_ASSERT("1500ms" == util::duration_str(1.5)); + CU_ASSERT("2m" == util::duration_str(120.)); + CU_ASSERT("121s" == util::duration_str(121.)); + CU_ASSERT("1h" == util::duration_str(3600.)); +} + +void test_util_format_duration(void) { + CU_ASSERT("0us" == util::format_duration(std::chrono::microseconds(0))); + CU_ASSERT("999us" == util::format_duration(std::chrono::microseconds(999))); + CU_ASSERT("1.00ms" == util::format_duration(std::chrono::microseconds(1000))); + CU_ASSERT("1.09ms" == util::format_duration(std::chrono::microseconds(1090))); + CU_ASSERT("1.01ms" == util::format_duration(std::chrono::microseconds(1009))); + CU_ASSERT("999.99ms" == + util::format_duration(std::chrono::microseconds(999990))); + CU_ASSERT("1.00s" == + util::format_duration(std::chrono::microseconds(1000000))); + CU_ASSERT("1.05s" == + util::format_duration(std::chrono::microseconds(1050000))); +} + +void test_util_starts_with(void) { + CU_ASSERT(util::startsWith("foo", "foo")); + CU_ASSERT(util::startsWith("fooo", "foo")); + CU_ASSERT(util::startsWith("ofoo", "")); + CU_ASSERT(!util::startsWith("ofoo", "foo")); + + CU_ASSERT(util::istartsWith("FOO", "fOO")); + CU_ASSERT(util::startsWith("ofoo", "")); + CU_ASSERT(util::istartsWith("fOOo", "Foo")); + CU_ASSERT(!util::istartsWith("ofoo", "foo")); +} + +void test_util_ends_with(void) { + CU_ASSERT(util::endsWith("foo", "foo")); + CU_ASSERT(util::endsWith("foo", "")); + CU_ASSERT(util::endsWith("ofoo", "foo")); + CU_ASSERT(!util::endsWith("ofoo", "fo")); + + CU_ASSERT(util::iendsWith("fOo", "Foo")); + CU_ASSERT(util::iendsWith("foo", "")); + CU_ASSERT(util::iendsWith("oFoo", "fOO")); + CU_ASSERT(!util::iendsWith("ofoo", "fo")); +} + +} // namespace shrpx diff --git a/src/util_test.h b/src/util_test.h new file mode 100644 index 0000000..00290b8 --- /dev/null +++ b/src/util_test.h @@ -0,0 +1,59 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2013 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef UTIL_TEST_H +#define UTIL_TEST_H + +#ifdef HAVE_CONFIG_H +#include +#endif // HAVE_CONFIG_H + +namespace shrpx { + +void test_util_streq(void); +void test_util_strieq(void); +void test_util_inp_strlower(void); +void test_util_to_base64(void); +void test_util_to_token68(void); +void test_util_percent_encode_token(void); +void test_util_percent_encode_path(void); +void test_util_percent_decode(void); +void test_util_quote_string(void); +void test_util_utox(void); +void test_util_http_date(void); +void test_util_select_h2(void); +void test_util_ipv6_numeric_addr(void); +void test_util_utos_with_unit(void); +void test_util_utos_with_funit(void); +void test_util_parse_uint_with_unit(void); +void test_util_parse_uint(void); +void test_util_parse_duration_with_unit(void); +void test_util_duration_str(void); +void test_util_format_duration(void); +void test_util_starts_with(void); +void test_util_ends_with(void); + +} // namespace shrpx + +#endif // UTIL_TEST_H diff --git a/test-driver b/test-driver new file mode 100755 index 0000000..d306056 --- /dev/null +++ b/test-driver @@ -0,0 +1,139 @@ +#! /bin/sh +# test-driver - basic testsuite driver script. + +scriptversion=2013-07-13.22; # UTC + +# Copyright (C) 2011-2013 Free Software Foundation, Inc. +# +# 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 2, 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 . + +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +# This file is maintained in Automake, please report +# bugs to or send patches to +# . + +# Make unconditional expansion of undefined variables an error. This +# helps a lot in preventing typo-related bugs. +set -u + +usage_error () +{ + echo "$0: $*" >&2 + print_usage >&2 + exit 2 +} + +print_usage () +{ + cat <$log_file 2>&1 +estatus=$? +if test $enable_hard_errors = no && test $estatus -eq 99; then + estatus=1 +fi + +case $estatus:$expect_failure in + 0:yes) col=$red res=XPASS recheck=yes gcopy=yes;; + 0:*) col=$grn res=PASS recheck=no gcopy=no;; + 77:*) col=$blu res=SKIP recheck=no gcopy=yes;; + 99:*) col=$mgn res=ERROR recheck=yes gcopy=yes;; + *:yes) col=$lgn res=XFAIL recheck=no gcopy=yes;; + *:*) col=$red res=FAIL recheck=yes gcopy=yes;; +esac + +# Report outcome to console. +echo "${col}${res}${std}: $test_name" + +# Register the test result, and other relevant metadata. +echo ":test-result: $res" > $trs_file +echo ":global-test-result: $res" >> $trs_file +echo ":recheck: $recheck" >> $trs_file +echo ":copy-in-global-log: $gcopy" >> $trs_file + +# Local Variables: +# mode: shell-script +# sh-indentation: 2 +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "scriptversion=" +# time-stamp-format: "%:y-%02m-%02d.%02H" +# time-stamp-time-zone: "UTC" +# time-stamp-end: "; # UTC" +# End: diff --git a/tests/Makefile.am b/tests/Makefile.am new file mode 100644 index 0000000..4ef17bb --- /dev/null +++ b/tests/Makefile.am @@ -0,0 +1,82 @@ +# nghttp2 - HTTP/2 C Library + +# Copyright (c) 2012 Tatsuhiro Tsujikawa + +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: + +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. + +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +SUBDIRS = testdata + +if HAVE_CUNIT + +check_PROGRAMS = main + +if ENABLE_FAILMALLOC +check_PROGRAMS += failmalloc +endif # ENABLE_FAILMALLOC + +OBJECTS = main.c nghttp2_pq_test.c nghttp2_map_test.c nghttp2_queue_test.c \ + nghttp2_test_helper.c \ + nghttp2_frame_test.c \ + nghttp2_stream_test.c \ + nghttp2_session_test.c \ + nghttp2_hd_test.c \ + nghttp2_npn_test.c \ + nghttp2_helper_test.c \ + nghttp2_buf_test.c + +HFILES = nghttp2_pq_test.h nghttp2_map_test.h nghttp2_queue_test.h \ + nghttp2_session_test.h \ + nghttp2_frame_test.h nghttp2_stream_test.h nghttp2_hd_test.h \ + nghttp2_npn_test.h nghttp2_helper_test.h \ + nghttp2_test_helper.h \ + nghttp2_buf_test.h + +main_SOURCES = $(HFILES) $(OBJECTS) + +main_LDADD = ${top_builddir}/lib/libnghttp2.la @CUNIT_LIBS@ @TESTLDADD@ +main_LDFLAGS = -static + +if ENABLE_FAILMALLOC +failmalloc_SOURCES = failmalloc.c failmalloc_test.c failmalloc_test.h \ + malloc_wrapper.c malloc_wrapper.h \ + nghttp2_test_helper.c nghttp2_test_helper.h +failmalloc_LDADD = $(main_LDADD) +failmalloc_LDFLAGS = $(main_LDFLAGS) +endif # ENABLE_FAILMALLOC + +AM_CFLAGS = $(WARNCFLAGS) \ + -I${top_srcdir}/lib \ + -I${top_srcdir}/lib/includes \ + -I${top_builddir}/lib/includes \ + @CUNIT_CFLAGS@ @DEFS@ + +TESTS = main + +if ENABLE_FAILMALLOC +TESTS += failmalloc +endif # ENABLE_FAILMALLOC + +if ENABLE_APP + +# EXTRA_DIST = end_to_end.py +# TESTS += end_to_end.py + +endif # ENABLE_APP + +endif # HAVE_CUNIT diff --git a/tests/Makefile.in b/tests/Makefile.in new file mode 100644 index 0000000..3c9a4c2 --- /dev/null +++ b/tests/Makefile.in @@ -0,0 +1,1253 @@ +# Makefile.in generated by automake 1.14.1 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2013 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +# nghttp2 - HTTP/2 C Library + +# Copyright (c) 2012 Tatsuhiro Tsujikawa + +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: + +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +VPATH = @srcdir@ +am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)' +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +target_triplet = @target@ +@HAVE_CUNIT_TRUE@check_PROGRAMS = main$(EXEEXT) $(am__EXEEXT_1) +@ENABLE_FAILMALLOC_TRUE@@HAVE_CUNIT_TRUE@am__append_1 = failmalloc +@HAVE_CUNIT_TRUE@TESTS = main$(EXEEXT) $(am__EXEEXT_1) +@ENABLE_FAILMALLOC_TRUE@@HAVE_CUNIT_TRUE@am__append_2 = failmalloc +subdir = tests +DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \ + $(top_srcdir)/depcomp $(top_srcdir)/test-driver +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/ax_boost_asio.m4 \ + $(top_srcdir)/m4/ax_boost_base.m4 \ + $(top_srcdir)/m4/ax_boost_system.m4 \ + $(top_srcdir)/m4/ax_boost_thread.m4 \ + $(top_srcdir)/m4/ax_check_compile_flag.m4 \ + $(top_srcdir)/m4/ax_cxx_compile_stdcxx_11.m4 \ + $(top_srcdir)/m4/ax_have_epoll.m4 \ + $(top_srcdir)/m4/ax_python_devel.m4 \ + $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/libxml2.m4 \ + $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ + $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +@ENABLE_FAILMALLOC_TRUE@@HAVE_CUNIT_TRUE@am__EXEEXT_1 = \ +@ENABLE_FAILMALLOC_TRUE@@HAVE_CUNIT_TRUE@ failmalloc$(EXEEXT) +am__failmalloc_SOURCES_DIST = failmalloc.c failmalloc_test.c \ + failmalloc_test.h malloc_wrapper.c malloc_wrapper.h \ + nghttp2_test_helper.c nghttp2_test_helper.h +@ENABLE_FAILMALLOC_TRUE@@HAVE_CUNIT_TRUE@am_failmalloc_OBJECTS = \ +@ENABLE_FAILMALLOC_TRUE@@HAVE_CUNIT_TRUE@ failmalloc.$(OBJEXT) \ +@ENABLE_FAILMALLOC_TRUE@@HAVE_CUNIT_TRUE@ failmalloc_test.$(OBJEXT) \ +@ENABLE_FAILMALLOC_TRUE@@HAVE_CUNIT_TRUE@ malloc_wrapper.$(OBJEXT) \ +@ENABLE_FAILMALLOC_TRUE@@HAVE_CUNIT_TRUE@ nghttp2_test_helper.$(OBJEXT) +failmalloc_OBJECTS = $(am_failmalloc_OBJECTS) +@HAVE_CUNIT_TRUE@am__DEPENDENCIES_1 = \ +@HAVE_CUNIT_TRUE@ ${top_builddir}/lib/libnghttp2.la +@ENABLE_FAILMALLOC_TRUE@@HAVE_CUNIT_TRUE@failmalloc_DEPENDENCIES = $(am__DEPENDENCIES_1) +AM_V_lt = $(am__v_lt_@AM_V@) +am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) +am__v_lt_0 = --silent +am__v_lt_1 = +failmalloc_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(failmalloc_LDFLAGS) $(LDFLAGS) -o $@ +am__main_SOURCES_DIST = nghttp2_pq_test.h nghttp2_map_test.h \ + nghttp2_queue_test.h nghttp2_session_test.h \ + nghttp2_frame_test.h nghttp2_stream_test.h nghttp2_hd_test.h \ + nghttp2_npn_test.h nghttp2_helper_test.h nghttp2_test_helper.h \ + nghttp2_buf_test.h main.c nghttp2_pq_test.c nghttp2_map_test.c \ + nghttp2_queue_test.c nghttp2_test_helper.c \ + nghttp2_frame_test.c nghttp2_stream_test.c \ + nghttp2_session_test.c nghttp2_hd_test.c nghttp2_npn_test.c \ + nghttp2_helper_test.c nghttp2_buf_test.c +am__objects_1 = +@HAVE_CUNIT_TRUE@am__objects_2 = main.$(OBJEXT) \ +@HAVE_CUNIT_TRUE@ nghttp2_pq_test.$(OBJEXT) \ +@HAVE_CUNIT_TRUE@ nghttp2_map_test.$(OBJEXT) \ +@HAVE_CUNIT_TRUE@ nghttp2_queue_test.$(OBJEXT) \ +@HAVE_CUNIT_TRUE@ nghttp2_test_helper.$(OBJEXT) \ +@HAVE_CUNIT_TRUE@ nghttp2_frame_test.$(OBJEXT) \ +@HAVE_CUNIT_TRUE@ nghttp2_stream_test.$(OBJEXT) \ +@HAVE_CUNIT_TRUE@ nghttp2_session_test.$(OBJEXT) \ +@HAVE_CUNIT_TRUE@ nghttp2_hd_test.$(OBJEXT) \ +@HAVE_CUNIT_TRUE@ nghttp2_npn_test.$(OBJEXT) \ +@HAVE_CUNIT_TRUE@ nghttp2_helper_test.$(OBJEXT) \ +@HAVE_CUNIT_TRUE@ nghttp2_buf_test.$(OBJEXT) +@HAVE_CUNIT_TRUE@am_main_OBJECTS = $(am__objects_1) $(am__objects_2) +main_OBJECTS = $(am_main_OBJECTS) +@HAVE_CUNIT_TRUE@main_DEPENDENCIES = \ +@HAVE_CUNIT_TRUE@ ${top_builddir}/lib/libnghttp2.la +main_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(main_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__depfiles_maybe = depfiles +am__mv = mv -f +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CFLAGS) $(CFLAGS) +AM_V_CC = $(am__v_CC_@AM_V@) +am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) +am__v_CC_0 = @echo " CC " $@; +am__v_CC_1 = +CCLD = $(CC) +LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CCLD = $(am__v_CCLD_@AM_V@) +am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) +am__v_CCLD_0 = @echo " CCLD " $@; +am__v_CCLD_1 = +SOURCES = $(failmalloc_SOURCES) $(main_SOURCES) +DIST_SOURCES = $(am__failmalloc_SOURCES_DIST) $(am__main_SOURCES_DIST) +RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \ + ctags-recursive dvi-recursive html-recursive info-recursive \ + install-data-recursive install-dvi-recursive \ + install-exec-recursive install-html-recursive \ + install-info-recursive install-pdf-recursive \ + install-ps-recursive install-recursive installcheck-recursive \ + installdirs-recursive pdf-recursive ps-recursive \ + tags-recursive uninstall-recursive +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \ + distclean-recursive maintainer-clean-recursive +am__recursive_targets = \ + $(RECURSIVE_TARGETS) \ + $(RECURSIVE_CLEAN_TARGETS) \ + $(am__extra_recursive_targets) +AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \ + check recheck distdir +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +ETAGS = etags +CTAGS = ctags +am__tty_colors_dummy = \ + mgn= red= grn= lgn= blu= brg= std=; \ + am__color_tests=no +am__tty_colors = { \ + $(am__tty_colors_dummy); \ + if test "X$(AM_COLOR_TESTS)" = Xno; then \ + am__color_tests=no; \ + elif test "X$(AM_COLOR_TESTS)" = Xalways; then \ + am__color_tests=yes; \ + elif test "X$$TERM" != Xdumb && { test -t 1; } 2>/dev/null; then \ + am__color_tests=yes; \ + fi; \ + if test $$am__color_tests = yes; then \ + red=''; \ + grn=''; \ + lgn=''; \ + blu=''; \ + mgn=''; \ + brg=''; \ + std=''; \ + fi; \ +} +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +am__uninstall_files_from_dir = { \ + test -z "$$files" \ + || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ + || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ + $(am__cd) "$$dir" && rm -f $$files; }; \ + } +am__recheck_rx = ^[ ]*:recheck:[ ]* +am__global_test_result_rx = ^[ ]*:global-test-result:[ ]* +am__copy_in_global_log_rx = ^[ ]*:copy-in-global-log:[ ]* +# A command that, given a newline-separated list of test names on the +# standard input, print the name of the tests that are to be re-run +# upon "make recheck". +am__list_recheck_tests = $(AWK) '{ \ + recheck = 1; \ + while ((rc = (getline line < ($$0 ".trs"))) != 0) \ + { \ + if (rc < 0) \ + { \ + if ((getline line2 < ($$0 ".log")) < 0) \ + recheck = 0; \ + break; \ + } \ + else if (line ~ /$(am__recheck_rx)[nN][Oo]/) \ + { \ + recheck = 0; \ + break; \ + } \ + else if (line ~ /$(am__recheck_rx)[yY][eE][sS]/) \ + { \ + break; \ + } \ + }; \ + if (recheck) \ + print $$0; \ + close ($$0 ".trs"); \ + close ($$0 ".log"); \ +}' +# A command that, given a newline-separated list of test names on the +# standard input, create the global log from their .trs and .log files. +am__create_global_log = $(AWK) ' \ +function fatal(msg) \ +{ \ + print "fatal: making $@: " msg | "cat >&2"; \ + exit 1; \ +} \ +function rst_section(header) \ +{ \ + print header; \ + len = length(header); \ + for (i = 1; i <= len; i = i + 1) \ + printf "="; \ + printf "\n\n"; \ +} \ +{ \ + copy_in_global_log = 1; \ + global_test_result = "RUN"; \ + while ((rc = (getline line < ($$0 ".trs"))) != 0) \ + { \ + if (rc < 0) \ + fatal("failed to read from " $$0 ".trs"); \ + if (line ~ /$(am__global_test_result_rx)/) \ + { \ + sub("$(am__global_test_result_rx)", "", line); \ + sub("[ ]*$$", "", line); \ + global_test_result = line; \ + } \ + else if (line ~ /$(am__copy_in_global_log_rx)[nN][oO]/) \ + copy_in_global_log = 0; \ + }; \ + if (copy_in_global_log) \ + { \ + rst_section(global_test_result ": " $$0); \ + while ((rc = (getline line < ($$0 ".log"))) != 0) \ + { \ + if (rc < 0) \ + fatal("failed to read from " $$0 ".log"); \ + print line; \ + }; \ + printf "\n"; \ + }; \ + close ($$0 ".trs"); \ + close ($$0 ".log"); \ +}' +# Restructured Text title. +am__rst_title = { sed 's/.*/ & /;h;s/./=/g;p;x;s/ *$$//;p;g' && echo; } +# Solaris 10 'make', and several other traditional 'make' implementations, +# pass "-e" to $(SHELL), and POSIX 2008 even requires this. Work around it +# by disabling -e (using the XSI extension "set +e") if it's set. +am__sh_e_setup = case $$- in *e*) set +e;; esac +# Default flags passed to test drivers. +am__common_driver_flags = \ + --color-tests "$$am__color_tests" \ + --enable-hard-errors "$$am__enable_hard_errors" \ + --expect-failure "$$am__expect_failure" +# To be inserted before the command running the test. Creates the +# directory for the log if needed. Stores in $dir the directory +# containing $f, in $tst the test, in $log the log. Executes the +# developer- defined test setup AM_TESTS_ENVIRONMENT (if any), and +# passes TESTS_ENVIRONMENT. Set up options for the wrapper that +# will run the test scripts (or their associated LOG_COMPILER, if +# thy have one). +am__check_pre = \ +$(am__sh_e_setup); \ +$(am__vpath_adj_setup) $(am__vpath_adj) \ +$(am__tty_colors); \ +srcdir=$(srcdir); export srcdir; \ +case "$@" in \ + */*) am__odir=`echo "./$@" | sed 's|/[^/]*$$||'`;; \ + *) am__odir=.;; \ +esac; \ +test "x$$am__odir" = x"." || test -d "$$am__odir" \ + || $(MKDIR_P) "$$am__odir" || exit $$?; \ +if test -f "./$$f"; then dir=./; \ +elif test -f "$$f"; then dir=; \ +else dir="$(srcdir)/"; fi; \ +tst=$$dir$$f; log='$@'; \ +if test -n '$(DISABLE_HARD_ERRORS)'; then \ + am__enable_hard_errors=no; \ +else \ + am__enable_hard_errors=yes; \ +fi; \ +case " $(XFAIL_TESTS) " in \ + *[\ \ ]$$f[\ \ ]* | *[\ \ ]$$dir$$f[\ \ ]*) \ + am__expect_failure=yes;; \ + *) \ + am__expect_failure=no;; \ +esac; \ +$(AM_TESTS_ENVIRONMENT) $(TESTS_ENVIRONMENT) +# A shell command to get the names of the tests scripts with any registered +# extension removed (i.e., equivalently, the names of the test logs, with +# the '.log' extension removed). The result is saved in the shell variable +# '$bases'. This honors runtime overriding of TESTS and TEST_LOGS. Sadly, +# we cannot use something simpler, involving e.g., "$(TEST_LOGS:.log=)", +# since that might cause problem with VPATH rewrites for suffix-less tests. +# See also 'test-harness-vpath-rewrite.sh' and 'test-trs-basic.sh'. +am__set_TESTS_bases = \ + bases='$(TEST_LOGS)'; \ + bases=`for i in $$bases; do echo $$i; done | sed 's/\.log$$//'`; \ + bases=`echo $$bases` +RECHECK_LOGS = $(TEST_LOGS) +TEST_SUITE_LOG = test-suite.log +TEST_EXTENSIONS = @EXEEXT@ .test +LOG_DRIVER = $(SHELL) $(top_srcdir)/test-driver +LOG_COMPILE = $(LOG_COMPILER) $(AM_LOG_FLAGS) $(LOG_FLAGS) +am__set_b = \ + case '$@' in \ + */*) \ + case '$*' in \ + */*) b='$*';; \ + *) b=`echo '$@' | sed 's/\.log$$//'`; \ + esac;; \ + *) \ + b='$*';; \ + esac +am__test_logs1 = $(TESTS:=.log) +am__test_logs2 = $(am__test_logs1:@EXEEXT@.log=.log) +TEST_LOGS = $(am__test_logs2:.test.log=.log) +TEST_LOG_DRIVER = $(SHELL) $(top_srcdir)/test-driver +TEST_LOG_COMPILE = $(TEST_LOG_COMPILER) $(AM_TEST_LOG_FLAGS) \ + $(TEST_LOG_FLAGS) +DIST_SUBDIRS = $(SUBDIRS) +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +am__relativize = \ + dir0=`pwd`; \ + sed_first='s,^\([^/]*\)/.*$$,\1,'; \ + sed_rest='s,^[^/]*/*,,'; \ + sed_last='s,^.*/\([^/]*\)$$,\1,'; \ + sed_butlast='s,/*[^/]*$$,,'; \ + while test -n "$$dir1"; do \ + first=`echo "$$dir1" | sed -e "$$sed_first"`; \ + if test "$$first" != "."; then \ + if test "$$first" = ".."; then \ + dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \ + dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \ + else \ + first2=`echo "$$dir2" | sed -e "$$sed_first"`; \ + if test "$$first2" = "$$first"; then \ + dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \ + else \ + dir2="../$$dir2"; \ + fi; \ + dir0="$$dir0"/"$$first"; \ + fi; \ + fi; \ + dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \ + done; \ + reldir="$$dir2" +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +APPLDFLAGS = @APPLDFLAGS@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +BOOST_ASIO_LIB = @BOOST_ASIO_LIB@ +BOOST_CPPFLAGS = @BOOST_CPPFLAGS@ +BOOST_LDFLAGS = @BOOST_LDFLAGS@ +BOOST_SYSTEM_LIB = @BOOST_SYSTEM_LIB@ +BOOST_THREAD_LIB = @BOOST_THREAD_LIB@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CUNIT_CFLAGS = @CUNIT_CFLAGS@ +CUNIT_LIBS = @CUNIT_LIBS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +CYTHON = @CYTHON@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DLLTOOL = @DLLTOOL@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +GREP = @GREP@ +HAVE_CXX11 = @HAVE_CXX11@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +JANSSON_CFLAGS = @JANSSON_CFLAGS@ +JANSSON_LIBS = @JANSSON_LIBS@ +JEMALLOC_LIBS = @JEMALLOC_LIBS@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBEVENT_OPENSSL_CFLAGS = @LIBEVENT_OPENSSL_CFLAGS@ +LIBEVENT_OPENSSL_LIBS = @LIBEVENT_OPENSSL_LIBS@ +LIBEV_CFLAGS = @LIBEV_CFLAGS@ +LIBEV_LIBS = @LIBEV_LIBS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBSPDYLAY_CFLAGS = @LIBSPDYLAY_CFLAGS@ +LIBSPDYLAY_LIBS = @LIBSPDYLAY_LIBS@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +LT_AGE = @LT_AGE@ +LT_CURRENT = @LT_CURRENT@ +LT_REVISION = @LT_REVISION@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MKDIR_P = @MKDIR_P@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OPENSSL_CFLAGS = @OPENSSL_CFLAGS@ +OPENSSL_LIBS = @OPENSSL_LIBS@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PACKAGE_VERSION_NUM = @PACKAGE_VERSION_NUM@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PKG_CONFIG = @PKG_CONFIG@ +PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ +PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ +PYTHON = @PYTHON@ +PYTHON_CPPFLAGS = @PYTHON_CPPFLAGS@ +PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@ +PYTHON_EXTRA_LDFLAGS = @PYTHON_EXTRA_LDFLAGS@ +PYTHON_EXTRA_LIBS = @PYTHON_EXTRA_LIBS@ +PYTHON_LDFLAGS = @PYTHON_LDFLAGS@ +PYTHON_PLATFORM = @PYTHON_PLATFORM@ +PYTHON_PREFIX = @PYTHON_PREFIX@ +PYTHON_SITE_PKG = @PYTHON_SITE_PKG@ +PYTHON_VERSION = @PYTHON_VERSION@ +RANLIB = @RANLIB@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +TESTLDADD = @TESTLDADD@ +VERSION = @VERSION@ +WARNCFLAGS = @WARNCFLAGS@ +XML2_CONFIG = @XML2_CONFIG@ +XML_CPPFLAGS = @XML_CPPFLAGS@ +XML_LIBS = @XML_LIBS@ +ZLIB_CFLAGS = @ZLIB_CFLAGS@ +ZLIB_LIBS = @ZLIB_LIBS@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +pkgpyexecdir = @pkgpyexecdir@ +pkgpythondir = @pkgpythondir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +pyexecdir = @pyexecdir@ +pythondir = @pythondir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target = @target@ +target_alias = @target_alias@ +target_cpu = @target_cpu@ +target_os = @target_os@ +target_vendor = @target_vendor@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ + +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +SUBDIRS = testdata +@HAVE_CUNIT_TRUE@OBJECTS = main.c nghttp2_pq_test.c nghttp2_map_test.c nghttp2_queue_test.c \ +@HAVE_CUNIT_TRUE@ nghttp2_test_helper.c \ +@HAVE_CUNIT_TRUE@ nghttp2_frame_test.c \ +@HAVE_CUNIT_TRUE@ nghttp2_stream_test.c \ +@HAVE_CUNIT_TRUE@ nghttp2_session_test.c \ +@HAVE_CUNIT_TRUE@ nghttp2_hd_test.c \ +@HAVE_CUNIT_TRUE@ nghttp2_npn_test.c \ +@HAVE_CUNIT_TRUE@ nghttp2_helper_test.c \ +@HAVE_CUNIT_TRUE@ nghttp2_buf_test.c + +@HAVE_CUNIT_TRUE@HFILES = nghttp2_pq_test.h nghttp2_map_test.h nghttp2_queue_test.h \ +@HAVE_CUNIT_TRUE@ nghttp2_session_test.h \ +@HAVE_CUNIT_TRUE@ nghttp2_frame_test.h nghttp2_stream_test.h nghttp2_hd_test.h \ +@HAVE_CUNIT_TRUE@ nghttp2_npn_test.h nghttp2_helper_test.h \ +@HAVE_CUNIT_TRUE@ nghttp2_test_helper.h \ +@HAVE_CUNIT_TRUE@ nghttp2_buf_test.h + +@HAVE_CUNIT_TRUE@main_SOURCES = $(HFILES) $(OBJECTS) +@HAVE_CUNIT_TRUE@main_LDADD = ${top_builddir}/lib/libnghttp2.la @CUNIT_LIBS@ @TESTLDADD@ +@HAVE_CUNIT_TRUE@main_LDFLAGS = -static +@ENABLE_FAILMALLOC_TRUE@@HAVE_CUNIT_TRUE@failmalloc_SOURCES = failmalloc.c failmalloc_test.c failmalloc_test.h \ +@ENABLE_FAILMALLOC_TRUE@@HAVE_CUNIT_TRUE@ malloc_wrapper.c malloc_wrapper.h \ +@ENABLE_FAILMALLOC_TRUE@@HAVE_CUNIT_TRUE@ nghttp2_test_helper.c nghttp2_test_helper.h + +@ENABLE_FAILMALLOC_TRUE@@HAVE_CUNIT_TRUE@failmalloc_LDADD = $(main_LDADD) +@ENABLE_FAILMALLOC_TRUE@@HAVE_CUNIT_TRUE@failmalloc_LDFLAGS = $(main_LDFLAGS) +@HAVE_CUNIT_TRUE@AM_CFLAGS = $(WARNCFLAGS) \ +@HAVE_CUNIT_TRUE@ -I${top_srcdir}/lib \ +@HAVE_CUNIT_TRUE@ -I${top_srcdir}/lib/includes \ +@HAVE_CUNIT_TRUE@ -I${top_builddir}/lib/includes \ +@HAVE_CUNIT_TRUE@ @CUNIT_CFLAGS@ @DEFS@ + +all: all-recursive + +.SUFFIXES: +.SUFFIXES: .c .lo .log .o .obj .test .test$(EXEEXT) .trs +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu tests/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu tests/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): + +clean-checkPROGRAMS: + @list='$(check_PROGRAMS)'; test -n "$$list" || exit 0; \ + echo " rm -f" $$list; \ + rm -f $$list || exit $$?; \ + test -n "$(EXEEXT)" || exit 0; \ + list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ + echo " rm -f" $$list; \ + rm -f $$list + +failmalloc$(EXEEXT): $(failmalloc_OBJECTS) $(failmalloc_DEPENDENCIES) $(EXTRA_failmalloc_DEPENDENCIES) + @rm -f failmalloc$(EXEEXT) + $(AM_V_CCLD)$(failmalloc_LINK) $(failmalloc_OBJECTS) $(failmalloc_LDADD) $(LIBS) + +main$(EXEEXT): $(main_OBJECTS) $(main_DEPENDENCIES) $(EXTRA_main_DEPENDENCIES) + @rm -f main$(EXEEXT) + $(AM_V_CCLD)$(main_LINK) $(main_OBJECTS) $(main_LDADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/failmalloc.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/failmalloc_test.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/main.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/malloc_wrapper.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_buf_test.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_frame_test.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_hd_test.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_helper_test.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_map_test.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_npn_test.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_pq_test.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_queue_test.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_session_test.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_stream_test.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_test_helper.Po@am__quote@ + +.c.o: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\ +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< + +.c.obj: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\ +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.c.lo: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\ +@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +# This directory's subdirectories are mostly independent; you can cd +# into them and run 'make' without going through this Makefile. +# To change the values of 'make' variables: instead of editing Makefiles, +# (1) if the variable is set in 'config.status', edit 'config.status' +# (which will cause the Makefiles to be regenerated when you run 'make'); +# (2) otherwise, pass the desired values on the 'make' command line. +$(am__recursive_targets): + @fail=; \ + if $(am__make_keepgoing); then \ + failcom='fail=yes'; \ + else \ + failcom='exit 1'; \ + fi; \ + dot_seen=no; \ + target=`echo $@ | sed s/-recursive//`; \ + case "$@" in \ + distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ + *) list='$(SUBDIRS)' ;; \ + esac; \ + for subdir in $$list; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + dot_seen=yes; \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || eval $$failcom; \ + done; \ + if test "$$dot_seen" = "no"; then \ + $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ + fi; test -z "$$fail" + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-recursive +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \ + include_option=--etags-include; \ + empty_fix=.; \ + else \ + include_option=--include; \ + empty_fix=; \ + fi; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + test ! -f $$subdir/TAGS || \ + set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \ + fi; \ + done; \ + $(am__define_uniq_tagged_files); \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: ctags-recursive + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" +cscopelist: cscopelist-recursive + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +# Recover from deleted '.trs' file; this should ensure that +# "rm -f foo.log; make foo.trs" re-run 'foo.test', and re-create +# both 'foo.log' and 'foo.trs'. Break the recipe in two subshells +# to avoid problems with "make -n". +.log.trs: + rm -f $< $@ + $(MAKE) $(AM_MAKEFLAGS) $< + +# Leading 'am--fnord' is there to ensure the list of targets does not +# expand to empty, as could happen e.g. with make check TESTS=''. +am--fnord $(TEST_LOGS) $(TEST_LOGS:.log=.trs): $(am__force_recheck) +am--force-recheck: + @: + +$(TEST_SUITE_LOG): $(TEST_LOGS) + @$(am__set_TESTS_bases); \ + am__f_ok () { test -f "$$1" && test -r "$$1"; }; \ + redo_bases=`for i in $$bases; do \ + am__f_ok $$i.trs && am__f_ok $$i.log || echo $$i; \ + done`; \ + if test -n "$$redo_bases"; then \ + redo_logs=`for i in $$redo_bases; do echo $$i.log; done`; \ + redo_results=`for i in $$redo_bases; do echo $$i.trs; done`; \ + if $(am__make_dryrun); then :; else \ + rm -f $$redo_logs && rm -f $$redo_results || exit 1; \ + fi; \ + fi; \ + if test -n "$$am__remaking_logs"; then \ + echo "fatal: making $(TEST_SUITE_LOG): possible infinite" \ + "recursion detected" >&2; \ + else \ + am__remaking_logs=yes $(MAKE) $(AM_MAKEFLAGS) $$redo_logs; \ + fi; \ + if $(am__make_dryrun); then :; else \ + st=0; \ + errmsg="fatal: making $(TEST_SUITE_LOG): failed to create"; \ + for i in $$redo_bases; do \ + test -f $$i.trs && test -r $$i.trs \ + || { echo "$$errmsg $$i.trs" >&2; st=1; }; \ + test -f $$i.log && test -r $$i.log \ + || { echo "$$errmsg $$i.log" >&2; st=1; }; \ + done; \ + test $$st -eq 0 || exit 1; \ + fi + @$(am__sh_e_setup); $(am__tty_colors); $(am__set_TESTS_bases); \ + ws='[ ]'; \ + results=`for b in $$bases; do echo $$b.trs; done`; \ + test -n "$$results" || results=/dev/null; \ + all=` grep "^$$ws*:test-result:" $$results | wc -l`; \ + pass=` grep "^$$ws*:test-result:$$ws*PASS" $$results | wc -l`; \ + fail=` grep "^$$ws*:test-result:$$ws*FAIL" $$results | wc -l`; \ + skip=` grep "^$$ws*:test-result:$$ws*SKIP" $$results | wc -l`; \ + xfail=`grep "^$$ws*:test-result:$$ws*XFAIL" $$results | wc -l`; \ + xpass=`grep "^$$ws*:test-result:$$ws*XPASS" $$results | wc -l`; \ + error=`grep "^$$ws*:test-result:$$ws*ERROR" $$results | wc -l`; \ + if test `expr $$fail + $$xpass + $$error` -eq 0; then \ + success=true; \ + else \ + success=false; \ + fi; \ + br='==================='; br=$$br$$br$$br$$br; \ + result_count () \ + { \ + if test x"$$1" = x"--maybe-color"; then \ + maybe_colorize=yes; \ + elif test x"$$1" = x"--no-color"; then \ + maybe_colorize=no; \ + else \ + echo "$@: invalid 'result_count' usage" >&2; exit 4; \ + fi; \ + shift; \ + desc=$$1 count=$$2; \ + if test $$maybe_colorize = yes && test $$count -gt 0; then \ + color_start=$$3 color_end=$$std; \ + else \ + color_start= color_end=; \ + fi; \ + echo "$${color_start}# $$desc $$count$${color_end}"; \ + }; \ + create_testsuite_report () \ + { \ + result_count $$1 "TOTAL:" $$all "$$brg"; \ + result_count $$1 "PASS: " $$pass "$$grn"; \ + result_count $$1 "SKIP: " $$skip "$$blu"; \ + result_count $$1 "XFAIL:" $$xfail "$$lgn"; \ + result_count $$1 "FAIL: " $$fail "$$red"; \ + result_count $$1 "XPASS:" $$xpass "$$red"; \ + result_count $$1 "ERROR:" $$error "$$mgn"; \ + }; \ + { \ + echo "$(PACKAGE_STRING): $(subdir)/$(TEST_SUITE_LOG)" | \ + $(am__rst_title); \ + create_testsuite_report --no-color; \ + echo; \ + echo ".. contents:: :depth: 2"; \ + echo; \ + for b in $$bases; do echo $$b; done \ + | $(am__create_global_log); \ + } >$(TEST_SUITE_LOG).tmp || exit 1; \ + mv $(TEST_SUITE_LOG).tmp $(TEST_SUITE_LOG); \ + if $$success; then \ + col="$$grn"; \ + else \ + col="$$red"; \ + test x"$$VERBOSE" = x || cat $(TEST_SUITE_LOG); \ + fi; \ + echo "$${col}$$br$${std}"; \ + echo "$${col}Testsuite summary for $(PACKAGE_STRING)$${std}"; \ + echo "$${col}$$br$${std}"; \ + create_testsuite_report --maybe-color; \ + echo "$$col$$br$$std"; \ + if $$success; then :; else \ + echo "$${col}See $(subdir)/$(TEST_SUITE_LOG)$${std}"; \ + if test -n "$(PACKAGE_BUGREPORT)"; then \ + echo "$${col}Please report to $(PACKAGE_BUGREPORT)$${std}"; \ + fi; \ + echo "$$col$$br$$std"; \ + fi; \ + $$success || exit 1 + +check-TESTS: + @list='$(RECHECK_LOGS)'; test -z "$$list" || rm -f $$list + @list='$(RECHECK_LOGS:.log=.trs)'; test -z "$$list" || rm -f $$list + @test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG) + @set +e; $(am__set_TESTS_bases); \ + log_list=`for i in $$bases; do echo $$i.log; done`; \ + trs_list=`for i in $$bases; do echo $$i.trs; done`; \ + log_list=`echo $$log_list`; trs_list=`echo $$trs_list`; \ + $(MAKE) $(AM_MAKEFLAGS) $(TEST_SUITE_LOG) TEST_LOGS="$$log_list"; \ + exit $$?; +recheck: all $(check_PROGRAMS) + @test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG) + @set +e; $(am__set_TESTS_bases); \ + bases=`for i in $$bases; do echo $$i; done \ + | $(am__list_recheck_tests)` || exit 1; \ + log_list=`for i in $$bases; do echo $$i.log; done`; \ + log_list=`echo $$log_list`; \ + $(MAKE) $(AM_MAKEFLAGS) $(TEST_SUITE_LOG) \ + am__force_recheck=am--force-recheck \ + TEST_LOGS="$$log_list"; \ + exit $$? +main.log: main$(EXEEXT) + @p='main$(EXEEXT)'; \ + b='main'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +failmalloc.log: failmalloc$(EXEEXT) + @p='failmalloc$(EXEEXT)'; \ + b='failmalloc'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +.test.log: + @p='$<'; \ + $(am__set_b); \ + $(am__check_pre) $(TEST_LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_TEST_LOG_DRIVER_FLAGS) $(TEST_LOG_DRIVER_FLAGS) -- $(TEST_LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +@am__EXEEXT_TRUE@.test$(EXEEXT).log: +@am__EXEEXT_TRUE@ @p='$<'; \ +@am__EXEEXT_TRUE@ $(am__set_b); \ +@am__EXEEXT_TRUE@ $(am__check_pre) $(TEST_LOG_DRIVER) --test-name "$$f" \ +@am__EXEEXT_TRUE@ --log-file $$b.log --trs-file $$b.trs \ +@am__EXEEXT_TRUE@ $(am__common_driver_flags) $(AM_TEST_LOG_DRIVER_FLAGS) $(TEST_LOG_DRIVER_FLAGS) -- $(TEST_LOG_COMPILE) \ +@am__EXEEXT_TRUE@ "$$tst" $(AM_TESTS_FD_REDIRECT) + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done + @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + $(am__make_dryrun) \ + || test -d "$(distdir)/$$subdir" \ + || $(MKDIR_P) "$(distdir)/$$subdir" \ + || exit 1; \ + dir1=$$subdir; dir2="$(distdir)/$$subdir"; \ + $(am__relativize); \ + new_distdir=$$reldir; \ + dir1=$$subdir; dir2="$(top_distdir)"; \ + $(am__relativize); \ + new_top_distdir=$$reldir; \ + echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \ + echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \ + ($(am__cd) $$subdir && \ + $(MAKE) $(AM_MAKEFLAGS) \ + top_distdir="$$new_top_distdir" \ + distdir="$$new_distdir" \ + am__remove_distdir=: \ + am__skip_length_check=: \ + am__skip_mode_fix=: \ + distdir) \ + || exit 1; \ + fi; \ + done +check-am: all-am + $(MAKE) $(AM_MAKEFLAGS) $(check_PROGRAMS) + $(MAKE) $(AM_MAKEFLAGS) check-TESTS +check: check-recursive +all-am: Makefile +installdirs: installdirs-recursive +installdirs-am: +install: install-recursive +install-exec: install-exec-recursive +install-data: install-data-recursive +uninstall: uninstall-recursive + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-recursive +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + -test -z "$(TEST_LOGS)" || rm -f $(TEST_LOGS) + -test -z "$(TEST_LOGS:.log=.trs)" || rm -f $(TEST_LOGS:.log=.trs) + -test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG) + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-recursive + +clean-am: clean-checkPROGRAMS clean-generic clean-libtool \ + mostlyclean-am + +distclean: distclean-recursive + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-recursive + +dvi-am: + +html: html-recursive + +html-am: + +info: info-recursive + +info-am: + +install-data-am: + +install-dvi: install-dvi-recursive + +install-dvi-am: + +install-exec-am: + +install-html: install-html-recursive + +install-html-am: + +install-info: install-info-recursive + +install-info-am: + +install-man: + +install-pdf: install-pdf-recursive + +install-pdf-am: + +install-ps: install-ps-recursive + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-recursive + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-recursive + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-recursive + +pdf-am: + +ps: ps-recursive + +ps-am: + +uninstall-am: + +.MAKE: $(am__recursive_targets) check-am install-am install-strip + +.PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am check \ + check-TESTS check-am clean clean-checkPROGRAMS clean-generic \ + clean-libtool cscopelist-am ctags ctags-am distclean \ + distclean-compile distclean-generic distclean-libtool \ + distclean-tags distdir dvi dvi-am html html-am info info-am \ + install install-am install-data install-data-am install-dvi \ + install-dvi-am install-exec install-exec-am install-html \ + install-html-am install-info install-info-am install-man \ + install-pdf install-pdf-am install-ps install-ps-am \ + install-strip installcheck installcheck-am installdirs \ + installdirs-am maintainer-clean maintainer-clean-generic \ + mostlyclean mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool pdf pdf-am ps ps-am recheck tags tags-am \ + uninstall uninstall-am + + +# EXTRA_DIST = end_to_end.py +# TESTS += end_to_end.py + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/tests/failmalloc.c b/tests/failmalloc.c new file mode 100644 index 0000000..8c9f16d --- /dev/null +++ b/tests/failmalloc.c @@ -0,0 +1,77 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifdef HAVE_CONFIG_H +#include +#endif /* HAVE_CONFIG_H */ + +#include +#include +#include +/* include test cases' include files here */ + +#include "failmalloc_test.h" + +static int init_suite1(void) { return 0; } + +static int clean_suite1(void) { return 0; } + +int main(int argc _U_, char *argv[] _U_) { + CU_pSuite pSuite = NULL; + unsigned int num_tests_failed; + + /* initialize the CUnit test registry */ + if (CUE_SUCCESS != CU_initialize_registry()) + return CU_get_error(); + + /* add a suite to the registry */ + pSuite = CU_add_suite("libnghttp2_TestSuite", init_suite1, clean_suite1); + if (NULL == pSuite) { + CU_cleanup_registry(); + return CU_get_error(); + } + + /* add the tests to the suite */ + if (!CU_add_test(pSuite, "failmalloc_session_send", + test_nghttp2_session_send) || + !CU_add_test(pSuite, "failmalloc_session_recv", + test_nghttp2_session_recv) || + !CU_add_test(pSuite, "failmalloc_frame", test_nghttp2_frame) || + !CU_add_test(pSuite, "failmalloc_hd", test_nghttp2_hd)) { + CU_cleanup_registry(); + return CU_get_error(); + } + + /* Run all tests using the CUnit Basic interface */ + CU_basic_set_mode(CU_BRM_VERBOSE); + CU_basic_run_tests(); + num_tests_failed = CU_get_number_of_tests_failed(); + CU_cleanup_registry(); + if (CU_get_error() == CUE_SUCCESS) { + return num_tests_failed; + } else { + printf("CUnit Error: %s\n", CU_get_error_msg()); + return CU_get_error(); + } +} diff --git a/tests/failmalloc_test.c b/tests/failmalloc_test.c new file mode 100644 index 0000000..42062d3 --- /dev/null +++ b/tests/failmalloc_test.c @@ -0,0 +1,509 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012, 2014 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "failmalloc_test.h" + +#include + +#include +#include + +#include "nghttp2_session.h" +#include "nghttp2_stream.h" +#include "nghttp2_frame.h" +#include "nghttp2_helper.h" +#include "malloc_wrapper.h" +#include "nghttp2_test_helper.h" + +typedef struct { + uint8_t data[8192]; + uint8_t *datamark, *datalimit; +} data_feed; + +typedef struct { + data_feed *df; + size_t data_source_length; +} my_user_data; + +static void data_feed_init(data_feed *df, nghttp2_bufs *bufs) { + nghttp2_buf *buf; + size_t data_length; + + buf = &bufs->head->buf; + data_length = nghttp2_buf_len(buf); + + assert(data_length <= sizeof(df->data)); + memcpy(df->data, buf->pos, data_length); + df->datamark = df->data; + df->datalimit = df->data + data_length; +} + +static ssize_t null_send_callback(nghttp2_session *session _U_, + const uint8_t *data _U_, size_t len, + int flags _U_, void *user_data _U_) { + return len; +} + +static ssize_t data_feed_recv_callback(nghttp2_session *session _U_, + uint8_t *data, size_t len, int flags _U_, + void *user_data) { + data_feed *df = ((my_user_data *)user_data)->df; + size_t avail = df->datalimit - df->datamark; + size_t wlen = nghttp2_min(avail, len); + memcpy(data, df->datamark, wlen); + df->datamark += wlen; + return wlen; +} + +static ssize_t fixed_length_data_source_read_callback( + nghttp2_session *session _U_, int32_t stream_id _U_, uint8_t *buf _U_, + size_t len, uint32_t *data_flags, nghttp2_data_source *source _U_, + void *user_data) { + my_user_data *ud = (my_user_data *)user_data; + size_t wlen; + if (len < ud->data_source_length) { + wlen = len; + } else { + wlen = ud->data_source_length; + } + ud->data_source_length -= wlen; + if (ud->data_source_length == 0) { + *data_flags = NGHTTP2_DATA_FLAG_EOF; + } + return wlen; +} + +#define TEST_FAILMALLOC_RUN(FUN) \ + do { \ + int nmalloc, i; \ + \ + nghttp2_failmalloc = 0; \ + nghttp2_nmalloc = 0; \ + FUN(); \ + nmalloc = nghttp2_nmalloc; \ + \ + nghttp2_failmalloc = 1; \ + for (i = 0; i < nmalloc; ++i) { \ + nghttp2_nmalloc = 0; \ + nghttp2_failstart = i; \ + /* printf("i=%zu\n", i); */ \ + FUN(); \ + /* printf("nmalloc=%d\n", nghttp2_nmalloc); */ \ + } \ + nghttp2_failmalloc = 0; \ + } while (0) + +static void run_nghttp2_session_send(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + nghttp2_nv nv[] = {MAKE_NV(":host", "example.org"), + MAKE_NV(":scheme", "https")}; + nghttp2_data_provider data_prd; + nghttp2_settings_entry iv[2]; + my_user_data ud; + int rv; + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.send_callback = null_send_callback; + + data_prd.read_callback = fixed_length_data_source_read_callback; + ud.data_source_length = 64 * 1024; + + iv[0].settings_id = NGHTTP2_SETTINGS_HEADER_TABLE_SIZE; + iv[0].value = 4096; + iv[1].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS; + iv[1].value = 100; + + rv = nghttp2_session_client_new3(&session, &callbacks, &ud, NULL, + nghttp2_mem_fm()); + if (rv != 0) { + goto client_new_fail; + } + rv = nghttp2_submit_request(session, NULL, nv, ARRLEN(nv), &data_prd, NULL); + if (rv < 0) { + goto fail; + } + rv = nghttp2_submit_headers(session, NGHTTP2_FLAG_NONE, -1, NULL, nv, + ARRLEN(nv), NULL); + if (rv < 0) { + goto fail; + } + rv = nghttp2_session_send(session); + if (rv != 0) { + goto fail; + } + /* The HEADERS submitted by the previous nghttp2_submit_headers will + have stream ID 3. Send HEADERS to that stream. */ + rv = nghttp2_submit_headers(session, NGHTTP2_FLAG_NONE, 3, NULL, nv, + ARRLEN(nv), NULL); + if (rv != 0) { + goto fail; + } + rv = nghttp2_submit_data(session, NGHTTP2_FLAG_END_STREAM, 3, &data_prd); + if (rv != 0) { + goto fail; + } + rv = nghttp2_session_send(session); + if (rv != 0) { + goto fail; + } + rv = nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, 3, NGHTTP2_CANCEL); + if (rv != 0) { + goto fail; + } + rv = nghttp2_session_send(session); + if (rv != 0) { + goto fail; + } + /* Sending against half-closed stream */ + rv = nghttp2_submit_headers(session, NGHTTP2_FLAG_NONE, 3, NULL, nv, + ARRLEN(nv), NULL); + if (rv != 0) { + goto fail; + } + rv = nghttp2_submit_data(session, NGHTTP2_FLAG_END_STREAM, 3, &data_prd); + if (rv != 0) { + goto fail; + } + rv = nghttp2_submit_ping(session, NGHTTP2_FLAG_NONE, NULL); + if (rv != 0) { + goto fail; + } + rv = nghttp2_submit_settings(session, NGHTTP2_FLAG_NONE, iv, 2); + if (rv != 0) { + goto fail; + } + rv = nghttp2_session_send(session); + if (rv != 0) { + goto fail; + } + rv = nghttp2_submit_goaway(session, NGHTTP2_FLAG_NONE, 100, NGHTTP2_NO_ERROR, + NULL, 0); + if (rv != 0) { + goto fail; + } + rv = nghttp2_session_send(session); + if (rv != 0) { + goto fail; + } + +fail: + nghttp2_session_del(session); +client_new_fail: + ; +} + +void test_nghttp2_session_send(void) { + TEST_FAILMALLOC_RUN(run_nghttp2_session_send); +} + +static void run_nghttp2_session_recv(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + nghttp2_hd_deflater deflater; + nghttp2_frame frame; + nghttp2_bufs bufs; + nghttp2_nv nv[] = {MAKE_NV(":authority", "example.org"), + MAKE_NV(":scheme", "https")}; + nghttp2_settings_entry iv[2]; + my_user_data ud; + data_feed df; + int rv; + nghttp2_nv *nva; + ssize_t nvlen; + + rv = frame_pack_bufs_init(&bufs); + + if (rv != 0) { + return; + } + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.recv_callback = data_feed_recv_callback; + ud.df = &df; + + nghttp2_failmalloc_pause(); + nvlen = ARRLEN(nv); + nghttp2_nv_array_copy(&nva, nv, nvlen, nghttp2_mem_fm()); + nghttp2_hd_deflate_init(&deflater, nghttp2_mem_fm()); + nghttp2_session_server_new3(&session, &callbacks, &ud, NULL, + nghttp2_mem_fm()); + nghttp2_failmalloc_unpause(); + + /* HEADERS */ + nghttp2_failmalloc_pause(); + nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_END_STREAM, 1, + NGHTTP2_HCAT_REQUEST, NULL, nva, nvlen); + nghttp2_frame_pack_headers(&bufs, &frame.headers, &deflater); + nghttp2_frame_headers_free(&frame.headers, nghttp2_mem_fm()); + data_feed_init(&df, &bufs); + nghttp2_bufs_reset(&bufs); + + nghttp2_failmalloc_unpause(); + + rv = nghttp2_session_recv(session); + if (rv != 0) { + goto fail; + } + + /* PING */ + nghttp2_failmalloc_pause(); + nghttp2_frame_ping_init(&frame.ping, NGHTTP2_FLAG_NONE, NULL); + nghttp2_frame_pack_ping(&bufs, &frame.ping); + nghttp2_frame_ping_free(&frame.ping); + data_feed_init(&df, &bufs); + nghttp2_bufs_reset(&bufs); + + nghttp2_failmalloc_unpause(); + + rv = nghttp2_session_recv(session); + if (rv != 0) { + goto fail; + } + + /* RST_STREAM */ + nghttp2_failmalloc_pause(); + nghttp2_frame_rst_stream_init(&frame.rst_stream, 1, NGHTTP2_PROTOCOL_ERROR); + nghttp2_frame_pack_rst_stream(&bufs, &frame.rst_stream); + nghttp2_frame_rst_stream_free(&frame.rst_stream); + nghttp2_bufs_reset(&bufs); + + nghttp2_failmalloc_unpause(); + + rv = nghttp2_session_recv(session); + if (rv != 0) { + goto fail; + } + + /* SETTINGS */ + nghttp2_failmalloc_pause(); + iv[0].settings_id = NGHTTP2_SETTINGS_HEADER_TABLE_SIZE; + iv[0].value = 4096; + iv[1].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS; + iv[1].value = 100; + nghttp2_frame_settings_init(&frame.settings, NGHTTP2_FLAG_NONE, + nghttp2_frame_iv_copy(iv, 2, nghttp2_mem_fm()), + 2); + nghttp2_frame_pack_settings(&bufs, &frame.settings); + nghttp2_frame_settings_free(&frame.settings, nghttp2_mem_fm()); + nghttp2_bufs_reset(&bufs); + + nghttp2_failmalloc_unpause(); + + rv = nghttp2_session_recv(session); + if (rv != 0) { + goto fail; + } + +fail: + nghttp2_bufs_free(&bufs); + nghttp2_session_del(session); + nghttp2_hd_deflate_free(&deflater); +} + +void test_nghttp2_session_recv(void) { + TEST_FAILMALLOC_RUN(run_nghttp2_session_recv); +} + +static void run_nghttp2_frame_pack_headers(void) { + nghttp2_hd_deflater deflater; + nghttp2_hd_inflater inflater; + nghttp2_frame frame, oframe; + nghttp2_bufs bufs; + nghttp2_nv nv[] = {MAKE_NV(":host", "example.org"), + MAKE_NV(":scheme", "https")}; + int rv; + nghttp2_nv *nva; + ssize_t nvlen; + + rv = frame_pack_bufs_init(&bufs); + + if (rv != 0) { + return; + } + + rv = nghttp2_hd_deflate_init(&deflater, nghttp2_mem_fm()); + if (rv != 0) { + goto deflate_init_fail; + } + rv = nghttp2_hd_inflate_init(&inflater, nghttp2_mem_fm()); + if (rv != 0) { + goto inflate_init_fail; + } + nvlen = ARRLEN(nv); + rv = nghttp2_nv_array_copy(&nva, nv, nvlen, nghttp2_mem_fm()); + if (rv < 0) { + goto nv_copy_fail; + } + nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_END_STREAM, 1, + NGHTTP2_HCAT_REQUEST, NULL, nva, nvlen); + rv = nghttp2_frame_pack_headers(&bufs, &frame.headers, &deflater); + if (rv != 0) { + goto fail; + } + rv = unpack_framebuf(&oframe, &bufs); + if (rv != 0) { + goto fail; + } + nghttp2_frame_headers_free(&oframe.headers, nghttp2_mem_fm()); + +fail: + nghttp2_frame_headers_free(&frame.headers, nghttp2_mem_fm()); +nv_copy_fail: + nghttp2_hd_inflate_free(&inflater); +inflate_init_fail: + nghttp2_hd_deflate_free(&deflater); +deflate_init_fail: + nghttp2_bufs_free(&bufs); +} + +static void run_nghttp2_frame_pack_settings(void) { + nghttp2_frame frame, oframe; + nghttp2_bufs bufs; + nghttp2_buf *buf; + nghttp2_settings_entry iv[2], *iv_copy; + int rv; + + rv = frame_pack_bufs_init(&bufs); + + if (rv != 0) { + return; + } + + iv[0].settings_id = NGHTTP2_SETTINGS_HEADER_TABLE_SIZE; + iv[0].value = 4096; + iv[1].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS; + iv[1].value = 100; + + iv_copy = nghttp2_frame_iv_copy(iv, 2, nghttp2_mem_fm()); + + if (iv_copy == NULL) { + goto iv_copy_fail; + } + + nghttp2_frame_settings_init(&frame.settings, NGHTTP2_FLAG_NONE, iv_copy, 2); + + rv = nghttp2_frame_pack_settings(&bufs, &frame.settings); + + if (rv != 0) { + goto fail; + } + + buf = &bufs.head->buf; + + rv = nghttp2_frame_unpack_settings_payload2( + &oframe.settings.iv, &oframe.settings.niv, buf->pos + NGHTTP2_FRAME_HDLEN, + nghttp2_buf_len(buf) - NGHTTP2_FRAME_HDLEN, nghttp2_mem_fm()); + + if (rv != 0) { + goto fail; + } + nghttp2_frame_settings_free(&oframe.settings, nghttp2_mem_fm()); + +fail: + nghttp2_frame_settings_free(&frame.settings, nghttp2_mem_fm()); +iv_copy_fail: + nghttp2_bufs_free(&bufs); +} + +void test_nghttp2_frame(void) { + TEST_FAILMALLOC_RUN(run_nghttp2_frame_pack_headers); + TEST_FAILMALLOC_RUN(run_nghttp2_frame_pack_settings); +} + +static int deflate_inflate(nghttp2_hd_deflater *deflater, + nghttp2_hd_inflater *inflater, nghttp2_bufs *bufs, + nghttp2_nv *nva, size_t nvlen, nghttp2_mem *mem) { + int rv; + + rv = nghttp2_hd_deflate_hd_bufs(deflater, bufs, nva, nvlen); + + if (rv != 0) { + return rv; + } + + rv = (int)inflate_hd(inflater, NULL, bufs, 0, mem); + + if (rv < 0) { + return rv; + } + + nghttp2_bufs_reset(bufs); + + return 0; +} + +static void run_nghttp2_hd(void) { + nghttp2_hd_deflater deflater; + nghttp2_hd_inflater inflater; + nghttp2_bufs bufs; + int rv; + nghttp2_nv nva1[] = {MAKE_NV(":scheme", "https"), + MAKE_NV(":authority", "example.org"), + MAKE_NV(":path", "/slashdot"), + MAKE_NV("accept-encoding", "gzip, deflate")}; + nghttp2_nv nva2[] = { + MAKE_NV(":scheme", "https"), MAKE_NV(":authority", "example.org"), + MAKE_NV(":path", "/style.css"), MAKE_NV("cookie", "nghttp2=FTW")}; + + rv = frame_pack_bufs_init(&bufs); + + if (rv != 0) { + return; + } + + rv = nghttp2_hd_deflate_init(&deflater, nghttp2_mem_fm()); + + if (rv != 0) { + goto deflate_init_fail; + } + + rv = nghttp2_hd_inflate_init(&inflater, nghttp2_mem_fm()); + + if (rv != 0) { + goto inflate_init_fail; + } + + rv = deflate_inflate(&deflater, &inflater, &bufs, nva1, ARRLEN(nva1), + nghttp2_mem_fm()); + + if (rv != 0) { + goto deflate_hd_fail; + } + + rv = deflate_inflate(&deflater, &inflater, &bufs, nva2, ARRLEN(nva2), + nghttp2_mem_fm()); + + if (rv != 0) { + goto deflate_hd_fail; + } + +deflate_hd_fail: + nghttp2_hd_inflate_free(&inflater); +inflate_init_fail: + nghttp2_hd_deflate_free(&deflater); +deflate_init_fail: + nghttp2_bufs_free(&bufs); +} + +void test_nghttp2_hd(void) { TEST_FAILMALLOC_RUN(run_nghttp2_hd); } diff --git a/tests/failmalloc_test.h b/tests/failmalloc_test.h new file mode 100644 index 0000000..1969bc1 --- /dev/null +++ b/tests/failmalloc_test.h @@ -0,0 +1,33 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef FAILMALLOC_TEST_H +#define FAILMALLOC_TEST_H + +void test_nghttp2_session_send(void); +void test_nghttp2_session_recv(void); +void test_nghttp2_frame(void); +void test_nghttp2_hd(void); + +#endif /* FAILMALLOC_TEST_H */ diff --git a/tests/main.c b/tests/main.c new file mode 100644 index 0000000..1d187f7 --- /dev/null +++ b/tests/main.c @@ -0,0 +1,368 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifdef HAVE_CONFIG_H +#include +#endif /* HAVE_CONFIG_H */ + +#include +#include +#include +/* include test cases' include files here */ +#include "nghttp2_pq_test.h" +#include "nghttp2_map_test.h" +#include "nghttp2_queue_test.h" +#include "nghttp2_session_test.h" +#include "nghttp2_frame_test.h" +#include "nghttp2_stream_test.h" +#include "nghttp2_hd_test.h" +#include "nghttp2_npn_test.h" +#include "nghttp2_helper_test.h" +#include "nghttp2_buf_test.h" + +extern int nghttp2_enable_strict_preface; + +static int init_suite1(void) { return 0; } + +static int clean_suite1(void) { return 0; } + +int main(int argc _U_, char *argv[] _U_) { + CU_pSuite pSuite = NULL; + unsigned int num_tests_failed; + + nghttp2_enable_strict_preface = 0; + + /* initialize the CUnit test registry */ + if (CUE_SUCCESS != CU_initialize_registry()) + return CU_get_error(); + + /* add a suite to the registry */ + pSuite = CU_add_suite("libnghttp2_TestSuite", init_suite1, clean_suite1); + if (NULL == pSuite) { + CU_cleanup_registry(); + return CU_get_error(); + } + + /* add the tests to the suite */ + if (!CU_add_test(pSuite, "pq", test_nghttp2_pq) || + !CU_add_test(pSuite, "pq_update", test_nghttp2_pq_update) || + !CU_add_test(pSuite, "map", test_nghttp2_map) || + !CU_add_test(pSuite, "map_functional", test_nghttp2_map_functional) || + !CU_add_test(pSuite, "map_each_free", test_nghttp2_map_each_free) || + !CU_add_test(pSuite, "queue", test_nghttp2_queue) || + !CU_add_test(pSuite, "npn", test_nghttp2_npn) || + !CU_add_test(pSuite, "session_recv", test_nghttp2_session_recv) || + !CU_add_test(pSuite, "session_recv_invalid_stream_id", + test_nghttp2_session_recv_invalid_stream_id) || + !CU_add_test(pSuite, "session_recv_invalid_frame", + test_nghttp2_session_recv_invalid_frame) || + !CU_add_test(pSuite, "session_recv_eof", test_nghttp2_session_recv_eof) || + !CU_add_test(pSuite, "session_recv_data", + test_nghttp2_session_recv_data) || + !CU_add_test(pSuite, "session_recv_continuation", + test_nghttp2_session_recv_continuation) || + !CU_add_test(pSuite, "session_recv_headers_with_priority", + test_nghttp2_session_recv_headers_with_priority) || + !CU_add_test(pSuite, "session_recv_premature_headers", + test_nghttp2_session_recv_premature_headers) || + !CU_add_test(pSuite, "session_recv_unknown_frame", + test_nghttp2_session_recv_unknown_frame) || + !CU_add_test(pSuite, "session_recv_unexpected_continuation", + test_nghttp2_session_recv_unexpected_continuation) || + !CU_add_test(pSuite, "session_recv_settings_header_table_size", + test_nghttp2_session_recv_settings_header_table_size) || + !CU_add_test(pSuite, "session_recv_too_large_frame_length", + test_nghttp2_session_recv_too_large_frame_length) || + !CU_add_test(pSuite, "session_continue", test_nghttp2_session_continue) || + !CU_add_test(pSuite, "session_add_frame", + test_nghttp2_session_add_frame) || + !CU_add_test(pSuite, "session_on_request_headers_received", + test_nghttp2_session_on_request_headers_received) || + !CU_add_test(pSuite, "session_on_response_headers_received", + test_nghttp2_session_on_response_headers_received) || + !CU_add_test(pSuite, "session_on_headers_received", + test_nghttp2_session_on_headers_received) || + !CU_add_test(pSuite, "session_on_push_response_headers_received", + test_nghttp2_session_on_push_response_headers_received) || + !CU_add_test(pSuite, "session_on_priority_received", + test_nghttp2_session_on_priority_received) || + !CU_add_test(pSuite, "session_on_rst_stream_received", + test_nghttp2_session_on_rst_stream_received) || + !CU_add_test(pSuite, "session_on_settings_received", + test_nghttp2_session_on_settings_received) || + !CU_add_test(pSuite, "session_on_push_promise_received", + test_nghttp2_session_on_push_promise_received) || + !CU_add_test(pSuite, "session_on_ping_received", + test_nghttp2_session_on_ping_received) || + !CU_add_test(pSuite, "session_on_goaway_received", + test_nghttp2_session_on_goaway_received) || + !CU_add_test(pSuite, "session_on_window_update_received", + test_nghttp2_session_on_window_update_received) || + !CU_add_test(pSuite, "session_on_data_received", + test_nghttp2_session_on_data_received) || + !CU_add_test(pSuite, "session_send_headers_start_stream", + test_nghttp2_session_send_headers_start_stream) || + !CU_add_test(pSuite, "session_send_headers_reply", + test_nghttp2_session_send_headers_reply) || + !CU_add_test(pSuite, "session_send_headers_frame_size_error", + test_nghttp2_session_send_headers_frame_size_error) || + !CU_add_test(pSuite, "session_send_headers_push_reply", + test_nghttp2_session_send_headers_push_reply) || + !CU_add_test(pSuite, "session_send_rst_stream", + test_nghttp2_session_send_rst_stream) || + !CU_add_test(pSuite, "session_send_push_promise", + test_nghttp2_session_send_push_promise) || + !CU_add_test(pSuite, "session_is_my_stream_id", + test_nghttp2_session_is_my_stream_id) || + !CU_add_test(pSuite, "session_upgrade", test_nghttp2_session_upgrade) || + !CU_add_test(pSuite, "session_reprioritize_stream", + test_nghttp2_session_reprioritize_stream) || + !CU_add_test( + pSuite, "session_reprioritize_stream_with_idle_stream_dep", + test_nghttp2_session_reprioritize_stream_with_idle_stream_dep) || + !CU_add_test(pSuite, "submit_data", test_nghttp2_submit_data) || + !CU_add_test(pSuite, "submit_data_read_length_too_large", + test_nghttp2_submit_data_read_length_too_large) || + !CU_add_test(pSuite, "submit_data_twice", + test_nghttp2_submit_data_twice) || + !CU_add_test(pSuite, "submit_request_with_data", + test_nghttp2_submit_request_with_data) || + !CU_add_test(pSuite, "submit_request_without_data", + test_nghttp2_submit_request_without_data) || + !CU_add_test(pSuite, "submit_response_with_data", + test_nghttp2_submit_response_with_data) || + !CU_add_test(pSuite, "submit_response_without_data", + test_nghttp2_submit_response_without_data) || + !CU_add_test(pSuite, "submit_trailer", test_nghttp2_submit_trailer) || + !CU_add_test(pSuite, "submit_headers_start_stream", + test_nghttp2_submit_headers_start_stream) || + !CU_add_test(pSuite, "submit_headers_reply", + test_nghttp2_submit_headers_reply) || + !CU_add_test(pSuite, "submit_headers_push_reply", + test_nghttp2_submit_headers_push_reply) || + !CU_add_test(pSuite, "submit_headers", test_nghttp2_submit_headers) || + !CU_add_test(pSuite, "submit_headers_continuation", + test_nghttp2_submit_headers_continuation) || + !CU_add_test(pSuite, "submit_priority", test_nghttp2_submit_priority) || + !CU_add_test(pSuite, "session_submit_settings", + test_nghttp2_submit_settings) || + !CU_add_test(pSuite, "session_submit_settings_update_local_window_size", + test_nghttp2_submit_settings_update_local_window_size) || + !CU_add_test(pSuite, "session_submit_push_promise", + test_nghttp2_submit_push_promise) || + !CU_add_test(pSuite, "submit_window_update", + test_nghttp2_submit_window_update) || + !CU_add_test(pSuite, "submit_window_update_local_window_size", + test_nghttp2_submit_window_update_local_window_size) || + !CU_add_test(pSuite, "submit_shutdown_notice", + test_nghttp2_submit_shutdown_notice) || + !CU_add_test(pSuite, "submit_invalid_nv", + test_nghttp2_submit_invalid_nv) || + !CU_add_test(pSuite, "session_open_stream", + test_nghttp2_session_open_stream) || + !CU_add_test(pSuite, "session_open_stream_with_idle_stream_dep", + test_nghttp2_session_open_stream_with_idle_stream_dep) || + !CU_add_test(pSuite, "session_get_next_ob_item", + test_nghttp2_session_get_next_ob_item) || + !CU_add_test(pSuite, "session_pop_next_ob_item", + test_nghttp2_session_pop_next_ob_item) || + !CU_add_test(pSuite, "session_reply_fail", + test_nghttp2_session_reply_fail) || + !CU_add_test(pSuite, "session_max_concurrent_streams", + test_nghttp2_session_max_concurrent_streams) || + !CU_add_test(pSuite, "session_stream_close_on_headers_push", + test_nghttp2_session_stream_close_on_headers_push) || + !CU_add_test(pSuite, "session_stop_data_with_rst_stream", + test_nghttp2_session_stop_data_with_rst_stream) || + !CU_add_test(pSuite, "session_defer_data", + test_nghttp2_session_defer_data) || + !CU_add_test(pSuite, "session_flow_control", + test_nghttp2_session_flow_control) || + !CU_add_test(pSuite, "session_flow_control_data_recv", + test_nghttp2_session_flow_control_data_recv) || + !CU_add_test(pSuite, "session_flow_control_data_with_padding_recv", + test_nghttp2_session_flow_control_data_with_padding_recv) || + !CU_add_test(pSuite, "session_data_read_temporal_failure", + test_nghttp2_session_data_read_temporal_failure) || + !CU_add_test(pSuite, "session_on_stream_close", + test_nghttp2_session_on_stream_close) || + !CU_add_test(pSuite, "session_on_ctrl_not_send", + test_nghttp2_session_on_ctrl_not_send) || + !CU_add_test(pSuite, "session_get_outbound_queue_size", + test_nghttp2_session_get_outbound_queue_size) || + !CU_add_test(pSuite, "session_get_effective_local_window_size", + test_nghttp2_session_get_effective_local_window_size) || + !CU_add_test(pSuite, "session_set_option", + test_nghttp2_session_set_option) || + !CU_add_test(pSuite, "session_data_backoff_by_high_pri_frame", + test_nghttp2_session_data_backoff_by_high_pri_frame) || + !CU_add_test(pSuite, "session_pack_data_with_padding", + test_nghttp2_session_pack_data_with_padding) || + !CU_add_test(pSuite, "session_pack_headers_with_padding", + test_nghttp2_session_pack_headers_with_padding) || + !CU_add_test(pSuite, "pack_settings_payload", + test_nghttp2_pack_settings_payload) || + !CU_add_test(pSuite, "session_stream_dep_add", + test_nghttp2_session_stream_dep_add) || + !CU_add_test(pSuite, "session_stream_dep_remove", + test_nghttp2_session_stream_dep_remove) || + !CU_add_test(pSuite, "session_stream_dep_add_subtree", + test_nghttp2_session_stream_dep_add_subtree) || + !CU_add_test(pSuite, "session_stream_dep_remove_subtree", + test_nghttp2_session_stream_dep_remove_subtree) || + !CU_add_test( + pSuite, "session_stream_dep_all_your_stream_are_belong_to_us", + test_nghttp2_session_stream_dep_all_your_stream_are_belong_to_us) || + !CU_add_test(pSuite, "session_stream_attach_item", + test_nghttp2_session_stream_attach_item) || + !CU_add_test(pSuite, "session_stream_attach_item_subtree", + test_nghttp2_session_stream_attach_item_subtree) || + !CU_add_test(pSuite, "session_stream_keep_closed_stream", + test_nghttp2_session_keep_closed_stream) || + !CU_add_test(pSuite, "session_stream_keep_idle_stream", + test_nghttp2_session_keep_idle_stream) || + !CU_add_test(pSuite, "session_detach_idle_stream", + test_nghttp2_session_detach_idle_stream) || + !CU_add_test(pSuite, "session_large_dep_tree", + test_nghttp2_session_large_dep_tree) || + !CU_add_test(pSuite, "session_graceful_shutdown", + test_nghttp2_session_graceful_shutdown) || + !CU_add_test(pSuite, "session_on_header_temporal_failure", + test_nghttp2_session_on_header_temporal_failure) || + !CU_add_test(pSuite, "session_recv_client_magic", + test_nghttp2_session_recv_client_magic) || + !CU_add_test(pSuite, "session_delete_data_item", + test_nghttp2_session_delete_data_item) || + !CU_add_test(pSuite, "session_open_idle_stream", + test_nghttp2_session_open_idle_stream) || + !CU_add_test(pSuite, "session_cancel_reserved_remote", + test_nghttp2_session_cancel_reserved_remote) || + !CU_add_test(pSuite, "session_reset_pending_headers", + test_nghttp2_session_reset_pending_headers) || + !CU_add_test(pSuite, "session_send_data_callback", + test_nghttp2_session_send_data_callback) || + !CU_add_test(pSuite, "session_on_begin_headers_temporal_failure", + test_nghttp2_session_on_begin_headers_temporal_failure) || + !CU_add_test(pSuite, "http_mandatory_headers", + test_nghttp2_http_mandatory_headers) || + !CU_add_test(pSuite, "http_content_length", + test_nghttp2_http_content_length) || + !CU_add_test(pSuite, "http_content_length_mismatch", + test_nghttp2_http_content_length_mismatch) || + !CU_add_test(pSuite, "http_non_final_response", + test_nghttp2_http_non_final_response) || + !CU_add_test(pSuite, "http_trailer_headers", + test_nghttp2_http_trailer_headers) || + !CU_add_test(pSuite, "http_ignore_regular_header", + test_nghttp2_http_ignore_regular_header) || + !CU_add_test(pSuite, "http_ignore_content_length", + test_nghttp2_http_ignore_content_length) || + !CU_add_test(pSuite, "http_record_request_method", + test_nghttp2_http_record_request_method) || + !CU_add_test(pSuite, "http_push_promise", + test_nghttp2_http_push_promise) || + !CU_add_test(pSuite, "frame_pack_headers", + test_nghttp2_frame_pack_headers) || + !CU_add_test(pSuite, "frame_pack_headers_frame_too_large", + test_nghttp2_frame_pack_headers_frame_too_large) || + !CU_add_test(pSuite, "frame_pack_headers_frame_smallest", + test_nghttp2_submit_data_read_length_smallest) || + !CU_add_test(pSuite, "frame_pack_priority", + test_nghttp2_frame_pack_priority) || + !CU_add_test(pSuite, "frame_pack_rst_stream", + test_nghttp2_frame_pack_rst_stream) || + !CU_add_test(pSuite, "frame_pack_settings", + test_nghttp2_frame_pack_settings) || + !CU_add_test(pSuite, "frame_pack_push_promise", + test_nghttp2_frame_pack_push_promise) || + !CU_add_test(pSuite, "frame_pack_ping", test_nghttp2_frame_pack_ping) || + !CU_add_test(pSuite, "frame_pack_goaway", + test_nghttp2_frame_pack_goaway) || + !CU_add_test(pSuite, "frame_pack_window_update", + test_nghttp2_frame_pack_window_update) || + !CU_add_test(pSuite, "nv_array_copy", test_nghttp2_nv_array_copy) || + !CU_add_test(pSuite, "iv_check", test_nghttp2_iv_check) || + !CU_add_test(pSuite, "hd_deflate", test_nghttp2_hd_deflate) || + !CU_add_test(pSuite, "hd_deflate_same_indexed_repr", + test_nghttp2_hd_deflate_same_indexed_repr) || + !CU_add_test(pSuite, "hd_inflate_indexed", + test_nghttp2_hd_inflate_indexed) || + !CU_add_test(pSuite, "hd_inflate_indname_noinc", + test_nghttp2_hd_inflate_indname_noinc) || + !CU_add_test(pSuite, "hd_inflate_indname_inc", + test_nghttp2_hd_inflate_indname_inc) || + !CU_add_test(pSuite, "hd_inflate_indname_inc_eviction", + test_nghttp2_hd_inflate_indname_inc_eviction) || + !CU_add_test(pSuite, "hd_inflate_newname_noinc", + test_nghttp2_hd_inflate_newname_noinc) || + !CU_add_test(pSuite, "hd_inflate_newname_inc", + test_nghttp2_hd_inflate_newname_inc) || + !CU_add_test(pSuite, "hd_inflate_clearall_inc", + test_nghttp2_hd_inflate_clearall_inc) || + !CU_add_test(pSuite, "hd_inflate_zero_length_huffman", + test_nghttp2_hd_inflate_zero_length_huffman) || + !CU_add_test(pSuite, "hd_ringbuf_reserve", + test_nghttp2_hd_ringbuf_reserve) || + !CU_add_test(pSuite, "hd_change_table_size", + test_nghttp2_hd_change_table_size) || + !CU_add_test(pSuite, "hd_deflate_inflate", + test_nghttp2_hd_deflate_inflate) || + !CU_add_test(pSuite, "hd_no_index", test_nghttp2_hd_no_index) || + !CU_add_test(pSuite, "hd_deflate_bound", test_nghttp2_hd_deflate_bound) || + !CU_add_test(pSuite, "hd_public_api", test_nghttp2_hd_public_api) || + !CU_add_test(pSuite, "hd_decode_length", test_nghttp2_hd_decode_length) || + !CU_add_test(pSuite, "hd_huff_encode", test_nghttp2_hd_huff_encode) || + !CU_add_test(pSuite, "adjust_local_window_size", + test_nghttp2_adjust_local_window_size) || + !CU_add_test(pSuite, "check_header_name", + test_nghttp2_check_header_name) || + !CU_add_test(pSuite, "check_header_value", + test_nghttp2_check_header_value) || + !CU_add_test(pSuite, "bufs_add", test_nghttp2_bufs_add) || + !CU_add_test(pSuite, "bufs_add_stack_buffer_overflow_bug", + test_nghttp2_bufs_add_stack_buffer_overflow_bug) || + !CU_add_test(pSuite, "bufs_addb", test_nghttp2_bufs_addb) || + !CU_add_test(pSuite, "bufs_orb", test_nghttp2_bufs_orb) || + !CU_add_test(pSuite, "bufs_remove", test_nghttp2_bufs_remove) || + !CU_add_test(pSuite, "bufs_reset", test_nghttp2_bufs_reset) || + !CU_add_test(pSuite, "bufs_advance", test_nghttp2_bufs_advance) || + !CU_add_test(pSuite, "bufs_next_present", + test_nghttp2_bufs_next_present) || + !CU_add_test(pSuite, "bufs_realloc", test_nghttp2_bufs_realloc)) { + CU_cleanup_registry(); + return CU_get_error(); + } + + /* Run all tests using the CUnit Basic interface */ + CU_basic_set_mode(CU_BRM_VERBOSE); + CU_basic_run_tests(); + num_tests_failed = CU_get_number_of_tests_failed(); + CU_cleanup_registry(); + if (CU_get_error() == CUE_SUCCESS) { + return num_tests_failed; + } else { + printf("CUnit Error: %s\n", CU_get_error_msg()); + return CU_get_error(); + } +} diff --git a/tests/malloc_wrapper.c b/tests/malloc_wrapper.c new file mode 100644 index 0000000..6873bff --- /dev/null +++ b/tests/malloc_wrapper.c @@ -0,0 +1,75 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "malloc_wrapper.h" + +int nghttp2_failmalloc = 0; +int nghttp2_failstart = 0; +int nghttp2_countmalloc = 1; +int nghttp2_nmalloc = 0; + +#define CHECK_PREREQ \ + do { \ + if (nghttp2_failmalloc && nghttp2_nmalloc >= nghttp2_failstart) { \ + return NULL; \ + } \ + if (nghttp2_countmalloc) { \ + ++nghttp2_nmalloc; \ + } \ + } while (0) + +static void *my_malloc(size_t size, void *mud _U_) { + CHECK_PREREQ; + return malloc(size); +} + +static void my_free(void *ptr, void *mud _U_) { free(ptr); } + +static void *my_calloc(size_t nmemb, size_t size, void *mud _U_) { + CHECK_PREREQ; + return calloc(nmemb, size); +} + +static void *my_realloc(void *ptr, size_t size, void *mud _U_) { + CHECK_PREREQ; + return realloc(ptr, size); +} + +static nghttp2_mem mem = {NULL, my_malloc, my_free, my_calloc, my_realloc}; + +nghttp2_mem *nghttp2_mem_fm(void) { return &mem; } + +static int failmalloc_bk, countmalloc_bk; + +void nghttp2_failmalloc_pause(void) { + failmalloc_bk = nghttp2_failmalloc; + countmalloc_bk = nghttp2_countmalloc; + nghttp2_failmalloc = 0; + nghttp2_countmalloc = 0; +} + +void nghttp2_failmalloc_unpause(void) { + nghttp2_failmalloc = failmalloc_bk; + nghttp2_countmalloc = countmalloc_bk; +} diff --git a/tests/malloc_wrapper.h b/tests/malloc_wrapper.h new file mode 100644 index 0000000..5405d09 --- /dev/null +++ b/tests/malloc_wrapper.h @@ -0,0 +1,61 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef MALLOC_WRAPPER_H +#define MALLOC_WRAPPER_H + +#include + +#include "nghttp2_mem.h" + +/* Global variables to control the behavior of malloc() */ + +/* If nonzero, malloc failure mode is on */ +extern int nghttp2_failmalloc; +/* If nghttp2_failstart <= nghttp2_nmalloc and nghttp2_failmalloc is + nonzero, malloc() fails. */ +extern int nghttp2_failstart; +/* If nonzero, nghttp2_nmalloc is incremented if malloc() succeeds. */ +extern int nghttp2_countmalloc; +/* The number of successful invocation of malloc(). This value is only + incremented if nghttp2_nmalloc is nonzero. */ +extern int nghttp2_nmalloc; + +/* Returns pointer to nghttp2_mem, which, when dereferenced, contains + specifically instrumented memory allocators for failmalloc + tests. */ +nghttp2_mem *nghttp2_mem_fm(void); + +/* Copies nghttp2_failmalloc and nghttp2_countmalloc to statically + allocated space and sets 0 to them. This will effectively make + malloc() work like normal malloc(). This is useful when you want to + disable malloc() failure mode temporarily. */ +void nghttp2_failmalloc_pause(void); + +/* Restores the values of nghttp2_failmalloc and nghttp2_countmalloc + with the values saved by the previous + nghttp2_failmalloc_pause(). */ +void nghttp2_failmalloc_unpause(void); + +#endif /* MALLOC_WRAPPER_H */ diff --git a/tests/nghttp2_buf_test.c b/tests/nghttp2_buf_test.c new file mode 100644 index 0000000..8860554 --- /dev/null +++ b/tests/nghttp2_buf_test.c @@ -0,0 +1,342 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2014 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "nghttp2_buf_test.h" + +#include + +#include "nghttp2_buf.h" +#include "nghttp2_test_helper.h" + +void test_nghttp2_bufs_add(void) { + int rv; + nghttp2_bufs bufs; + uint8_t data[2048]; + nghttp2_mem *mem; + + mem = nghttp2_mem_default(); + + rv = nghttp2_bufs_init(&bufs, 1000, 3, mem); + CU_ASSERT(0 == rv); + + CU_ASSERT(bufs.cur->buf.pos == bufs.cur->buf.last); + + rv = nghttp2_bufs_add(&bufs, data, 493); + CU_ASSERT(0 == rv); + CU_ASSERT(493 == nghttp2_buf_len(&bufs.cur->buf)); + CU_ASSERT(493 == nghttp2_bufs_len(&bufs)); + CU_ASSERT(507 == nghttp2_bufs_cur_avail(&bufs)); + + rv = nghttp2_bufs_add(&bufs, data, 507); + CU_ASSERT(0 == rv); + CU_ASSERT(1000 == nghttp2_buf_len(&bufs.cur->buf)); + CU_ASSERT(1000 == nghttp2_bufs_len(&bufs)); + CU_ASSERT(bufs.cur == bufs.head); + + rv = nghttp2_bufs_add(&bufs, data, 1); + CU_ASSERT(0 == rv); + CU_ASSERT(1 == nghttp2_buf_len(&bufs.cur->buf)); + CU_ASSERT(1001 == nghttp2_bufs_len(&bufs)); + CU_ASSERT(bufs.cur == bufs.head->next); + + nghttp2_bufs_free(&bufs); +} + +/* Test for GH-232, stack-buffer-overflow */ +void test_nghttp2_bufs_add_stack_buffer_overflow_bug(void) { + int rv; + nghttp2_bufs bufs; + uint8_t data[1024]; + nghttp2_mem *mem; + + mem = nghttp2_mem_default(); + + rv = nghttp2_bufs_init(&bufs, 100, 200, mem); + CU_ASSERT(0 == rv); + + rv = nghttp2_bufs_add(&bufs, data, sizeof(data)); + + CU_ASSERT(0 == rv); + CU_ASSERT(sizeof(data) == nghttp2_bufs_len(&bufs)); + + nghttp2_bufs_free(&bufs); +} + +void test_nghttp2_bufs_addb(void) { + int rv; + nghttp2_bufs bufs; + ssize_t i; + nghttp2_mem *mem; + + mem = nghttp2_mem_default(); + + rv = nghttp2_bufs_init(&bufs, 1000, 3, mem); + CU_ASSERT(0 == rv); + + rv = nghttp2_bufs_addb(&bufs, 14); + CU_ASSERT(0 == rv); + CU_ASSERT(1 == nghttp2_buf_len(&bufs.cur->buf)); + CU_ASSERT(1 == nghttp2_bufs_len(&bufs)); + CU_ASSERT(14 == *bufs.cur->buf.pos); + + for (i = 0; i < 999; ++i) { + rv = nghttp2_bufs_addb(&bufs, 254); + + CU_ASSERT(0 == rv); + CU_ASSERT(i + 2 == nghttp2_buf_len(&bufs.cur->buf)); + CU_ASSERT(i + 2 == nghttp2_bufs_len(&bufs)); + CU_ASSERT(254 == *(bufs.cur->buf.last - 1)); + CU_ASSERT(bufs.cur == bufs.head); + } + + rv = nghttp2_bufs_addb(&bufs, 253); + CU_ASSERT(0 == rv); + CU_ASSERT(1 == nghttp2_buf_len(&bufs.cur->buf)); + CU_ASSERT(1001 == nghttp2_bufs_len(&bufs)); + CU_ASSERT(253 == *(bufs.cur->buf.last - 1)); + CU_ASSERT(bufs.cur == bufs.head->next); + + rv = nghttp2_bufs_addb_hold(&bufs, 15); + CU_ASSERT(0 == rv); + CU_ASSERT(1 == nghttp2_buf_len(&bufs.cur->buf)); + CU_ASSERT(1001 == nghttp2_bufs_len(&bufs)); + CU_ASSERT(15 == *(bufs.cur->buf.last)); + + /* test fast version */ + + nghttp2_bufs_fast_addb(&bufs, 240); + + CU_ASSERT(2 == nghttp2_buf_len(&bufs.cur->buf)); + CU_ASSERT(1002 == nghttp2_bufs_len(&bufs)); + CU_ASSERT(240 == *(bufs.cur->buf.last - 1)); + + nghttp2_bufs_fast_addb_hold(&bufs, 113); + + CU_ASSERT(2 == nghttp2_buf_len(&bufs.cur->buf)); + CU_ASSERT(1002 == nghttp2_bufs_len(&bufs)); + CU_ASSERT(113 == *(bufs.cur->buf.last)); + + /* addb_hold when last == end */ + bufs.cur->buf.last = bufs.cur->buf.end; + + rv = nghttp2_bufs_addb_hold(&bufs, 19); + CU_ASSERT(0 == rv); + CU_ASSERT(0 == nghttp2_buf_len(&bufs.cur->buf)); + CU_ASSERT(2000 == nghttp2_bufs_len(&bufs)); + CU_ASSERT(19 == *(bufs.cur->buf.last)); + + nghttp2_bufs_free(&bufs); +} + +void test_nghttp2_bufs_orb(void) { + int rv; + nghttp2_bufs bufs; + nghttp2_mem *mem; + + mem = nghttp2_mem_default(); + + rv = nghttp2_bufs_init(&bufs, 1000, 3, mem); + CU_ASSERT(0 == rv); + + *(bufs.cur->buf.last) = 0; + + rv = nghttp2_bufs_orb_hold(&bufs, 15); + CU_ASSERT(0 == rv); + CU_ASSERT(0 == nghttp2_buf_len(&bufs.cur->buf)); + CU_ASSERT(0 == nghttp2_bufs_len(&bufs)); + CU_ASSERT(15 == *(bufs.cur->buf.last)); + + rv = nghttp2_bufs_orb(&bufs, 240); + CU_ASSERT(0 == rv); + CU_ASSERT(1 == nghttp2_buf_len(&bufs.cur->buf)); + CU_ASSERT(1 == nghttp2_bufs_len(&bufs)); + CU_ASSERT(255 == *(bufs.cur->buf.last - 1)); + + *(bufs.cur->buf.last) = 0; + nghttp2_bufs_fast_orb_hold(&bufs, 240); + CU_ASSERT(240 == *(bufs.cur->buf.last)); + + nghttp2_bufs_fast_orb(&bufs, 15); + CU_ASSERT(255 == *(bufs.cur->buf.last - 1)); + + nghttp2_bufs_free(&bufs); +} + +void test_nghttp2_bufs_remove(void) { + int rv; + nghttp2_bufs bufs; + nghttp2_buf_chain *chain; + int i; + uint8_t *out; + ssize_t outlen; + nghttp2_mem *mem; + + mem = nghttp2_mem_default(); + + rv = nghttp2_bufs_init(&bufs, 1000, 3, mem); + CU_ASSERT(0 == rv); + + nghttp2_buf_shift_right(&bufs.cur->buf, 10); + + rv = nghttp2_bufs_add(&bufs, "hello ", 6); + CU_ASSERT(0 == rv); + + for (i = 0; i < 2; ++i) { + chain = bufs.cur; + + rv = nghttp2_bufs_advance(&bufs); + CU_ASSERT(0 == rv); + + CU_ASSERT(chain->next == bufs.cur); + } + + rv = nghttp2_bufs_add(&bufs, "world", 5); + CU_ASSERT(0 == rv); + + outlen = nghttp2_bufs_remove(&bufs, &out); + CU_ASSERT(11 == outlen); + + CU_ASSERT(0 == memcmp("hello world", out, outlen)); + CU_ASSERT(11 == nghttp2_bufs_len(&bufs)); + + mem->free(out, NULL); + nghttp2_bufs_free(&bufs); +} + +void test_nghttp2_bufs_reset(void) { + int rv; + nghttp2_bufs bufs; + nghttp2_buf_chain *ci; + ssize_t offset = 9; + nghttp2_mem *mem; + + mem = nghttp2_mem_default(); + + rv = nghttp2_bufs_init3(&bufs, 250, 3, 1, offset, mem); + CU_ASSERT(0 == rv); + + rv = nghttp2_bufs_add(&bufs, "foo", 3); + CU_ASSERT(0 == rv); + + rv = nghttp2_bufs_advance(&bufs); + CU_ASSERT(0 == rv); + + rv = nghttp2_bufs_add(&bufs, "bar", 3); + CU_ASSERT(0 == rv); + + CU_ASSERT(6 == nghttp2_bufs_len(&bufs)); + + nghttp2_bufs_reset(&bufs); + + CU_ASSERT(0 == nghttp2_bufs_len(&bufs)); + CU_ASSERT(bufs.cur == bufs.head); + + for (ci = bufs.head; ci; ci = ci->next) { + CU_ASSERT(offset == ci->buf.pos - ci->buf.begin); + CU_ASSERT(ci->buf.pos == ci->buf.last); + } + + CU_ASSERT(bufs.head->next == NULL); + + nghttp2_bufs_free(&bufs); +} + +void test_nghttp2_bufs_advance(void) { + int rv; + nghttp2_bufs bufs; + int i; + nghttp2_mem *mem; + + mem = nghttp2_mem_default(); + + rv = nghttp2_bufs_init(&bufs, 250, 3, mem); + CU_ASSERT(0 == rv); + + for (i = 0; i < 2; ++i) { + rv = nghttp2_bufs_advance(&bufs); + CU_ASSERT(0 == rv); + } + + rv = nghttp2_bufs_advance(&bufs); + CU_ASSERT(NGHTTP2_ERR_BUFFER_ERROR == rv); + + nghttp2_bufs_free(&bufs); +} + +void test_nghttp2_bufs_next_present(void) { + int rv; + nghttp2_bufs bufs; + nghttp2_mem *mem; + + mem = nghttp2_mem_default(); + + rv = nghttp2_bufs_init(&bufs, 250, 3, mem); + CU_ASSERT(0 == rv); + + CU_ASSERT(0 == nghttp2_bufs_next_present(&bufs)); + + rv = nghttp2_bufs_advance(&bufs); + CU_ASSERT(0 == rv); + + nghttp2_bufs_rewind(&bufs); + + CU_ASSERT(0 == nghttp2_bufs_next_present(&bufs)); + + bufs.cur = bufs.head->next; + + rv = nghttp2_bufs_addb(&bufs, 1); + CU_ASSERT(0 == rv); + + nghttp2_bufs_rewind(&bufs); + + CU_ASSERT(0 != nghttp2_bufs_next_present(&bufs)); + + nghttp2_bufs_free(&bufs); +} + +void test_nghttp2_bufs_realloc(void) { + int rv; + nghttp2_bufs bufs; + nghttp2_mem *mem; + + mem = nghttp2_mem_default(); + + rv = nghttp2_bufs_init3(&bufs, 266, 3, 1, 10, mem); + CU_ASSERT(0 == rv); + + /* Create new buffer to see that these buffers are deallocated on + realloc */ + rv = nghttp2_bufs_advance(&bufs); + CU_ASSERT(0 == rv); + + rv = nghttp2_bufs_realloc(&bufs, 522); + CU_ASSERT(0 == rv); + + CU_ASSERT(512 == nghttp2_bufs_cur_avail(&bufs)); + + rv = nghttp2_bufs_realloc(&bufs, 9); + CU_ASSERT(NGHTTP2_ERR_INVALID_ARGUMENT == rv); + + nghttp2_bufs_free(&bufs); +} diff --git a/tests/nghttp2_buf_test.h b/tests/nghttp2_buf_test.h new file mode 100644 index 0000000..0b94a3f --- /dev/null +++ b/tests/nghttp2_buf_test.h @@ -0,0 +1,38 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2014 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGHTTP2_BUF_TEST_H +#define NGHTTP2_BUF_TEST_H + +void test_nghttp2_bufs_add(void); +void test_nghttp2_bufs_add_stack_buffer_overflow_bug(void); +void test_nghttp2_bufs_addb(void); +void test_nghttp2_bufs_orb(void); +void test_nghttp2_bufs_remove(void); +void test_nghttp2_bufs_reset(void); +void test_nghttp2_bufs_advance(void); +void test_nghttp2_bufs_next_present(void); +void test_nghttp2_bufs_realloc(void); + +#endif /* NGHTTP2_BUF_TEST_H */ diff --git a/tests/nghttp2_frame_test.c b/tests/nghttp2_frame_test.c new file mode 100644 index 0000000..0e0d1be --- /dev/null +++ b/tests/nghttp2_frame_test.c @@ -0,0 +1,561 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "nghttp2_frame_test.h" + +#include +#include + +#include + +#include "nghttp2_frame.h" +#include "nghttp2_helper.h" +#include "nghttp2_test_helper.h" +#include "nghttp2_priority_spec.h" + +static nghttp2_nv make_nv(const char *name, const char *value) { + nghttp2_nv nv; + nv.name = (uint8_t *)name; + nv.value = (uint8_t *)value; + nv.namelen = strlen(name); + nv.valuelen = strlen(value); + nv.flags = NGHTTP2_NV_FLAG_NONE; + + return nv; +} + +#define HEADERS_LENGTH 7 + +static nghttp2_nv *headers(nghttp2_mem *mem) { + nghttp2_nv *nva = mem->malloc(sizeof(nghttp2_nv) * HEADERS_LENGTH, NULL); + nva[0] = make_nv("method", "GET"); + nva[1] = make_nv("scheme", "https"); + nva[2] = make_nv("url", "/"); + nva[3] = make_nv("x-head", "foo"); + nva[4] = make_nv("x-head", "bar"); + nva[5] = make_nv("version", "HTTP/1.1"); + nva[6] = make_nv("x-empty", ""); + return nva; +} + +static void check_frame_header(size_t length, uint8_t type, uint8_t flags, + int32_t stream_id, nghttp2_frame_hd *hd) { + CU_ASSERT(length == hd->length); + CU_ASSERT(type == hd->type); + CU_ASSERT(flags == hd->flags); + CU_ASSERT(stream_id == hd->stream_id); + CU_ASSERT(0 == hd->reserved); +} + +void test_nghttp2_frame_pack_headers() { + nghttp2_hd_deflater deflater; + nghttp2_hd_inflater inflater; + nghttp2_headers frame, oframe; + nghttp2_bufs bufs; + nghttp2_nv *nva; + nghttp2_priority_spec pri_spec; + ssize_t nvlen; + nva_out out; + ssize_t hdblocklen; + int rv; + nghttp2_mem *mem; + + mem = nghttp2_mem_default(); + frame_pack_bufs_init(&bufs); + + nva_out_init(&out); + nghttp2_hd_deflate_init(&deflater, mem); + nghttp2_hd_inflate_init(&inflater, mem); + + nva = headers(mem); + nvlen = HEADERS_LENGTH; + + nghttp2_priority_spec_default_init(&pri_spec); + + nghttp2_frame_headers_init( + &frame, NGHTTP2_FLAG_END_STREAM | NGHTTP2_FLAG_END_HEADERS, 1000000007, + NGHTTP2_HCAT_REQUEST, &pri_spec, nva, nvlen); + rv = nghttp2_frame_pack_headers(&bufs, &frame, &deflater); + + nghttp2_bufs_rewind(&bufs); + + CU_ASSERT(0 == rv); + CU_ASSERT(nghttp2_bufs_len(&bufs) > 0); + CU_ASSERT(0 == unpack_framebuf((nghttp2_frame *)&oframe, &bufs)); + + check_frame_header(nghttp2_bufs_len(&bufs) - NGHTTP2_FRAME_HDLEN, + NGHTTP2_HEADERS, + NGHTTP2_FLAG_END_STREAM | NGHTTP2_FLAG_END_HEADERS, + 1000000007, &oframe.hd); + /* We did not include PRIORITY flag */ + CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT == oframe.pri_spec.weight); + + hdblocklen = nghttp2_bufs_len(&bufs) - NGHTTP2_FRAME_HDLEN; + CU_ASSERT(hdblocklen == + inflate_hd(&inflater, &out, &bufs, NGHTTP2_FRAME_HDLEN, mem)); + + CU_ASSERT(7 == out.nvlen); + CU_ASSERT(nvnameeq("method", &out.nva[0])); + CU_ASSERT(nvvalueeq("GET", &out.nva[0])); + + nghttp2_frame_headers_free(&oframe, mem); + nva_out_reset(&out, mem); + nghttp2_bufs_reset(&bufs); + + memset(&oframe, 0, sizeof(oframe)); + /* Next, include NGHTTP2_FLAG_PRIORITY */ + nghttp2_priority_spec_init(&frame.pri_spec, 1000000009, 12, 1); + frame.hd.flags |= NGHTTP2_FLAG_PRIORITY; + + rv = nghttp2_frame_pack_headers(&bufs, &frame, &deflater); + + CU_ASSERT(0 == rv); + CU_ASSERT(nghttp2_bufs_len(&bufs) > 0); + CU_ASSERT(0 == unpack_framebuf((nghttp2_frame *)&oframe, &bufs)); + + check_frame_header(nghttp2_bufs_len(&bufs) - NGHTTP2_FRAME_HDLEN, + NGHTTP2_HEADERS, + NGHTTP2_FLAG_END_STREAM | NGHTTP2_FLAG_END_HEADERS | + NGHTTP2_FLAG_PRIORITY, + 1000000007, &oframe.hd); + + CU_ASSERT(1000000009 == oframe.pri_spec.stream_id); + CU_ASSERT(12 == oframe.pri_spec.weight); + CU_ASSERT(1 == oframe.pri_spec.exclusive); + + hdblocklen = nghttp2_bufs_len(&bufs) - NGHTTP2_FRAME_HDLEN - + nghttp2_frame_priority_len(oframe.hd.flags); + CU_ASSERT(hdblocklen == + inflate_hd(&inflater, &out, &bufs, + NGHTTP2_FRAME_HDLEN + + nghttp2_frame_priority_len(oframe.hd.flags), + mem)); + + nghttp2_nv_array_sort(out.nva, out.nvlen); + CU_ASSERT(nvnameeq("method", &out.nva[0])); + + nghttp2_frame_headers_free(&oframe, mem); + nva_out_reset(&out, mem); + nghttp2_bufs_reset(&bufs); + + nghttp2_bufs_free(&bufs); + nghttp2_frame_headers_free(&frame, mem); + nghttp2_hd_inflate_free(&inflater); + nghttp2_hd_deflate_free(&deflater); +} + +void test_nghttp2_frame_pack_headers_frame_too_large(void) { + nghttp2_hd_deflater deflater; + nghttp2_headers frame; + nghttp2_bufs bufs; + nghttp2_nv *nva; + size_t big_vallen = NGHTTP2_HD_MAX_NV; + nghttp2_nv big_hds[16]; + size_t big_hdslen = ARRLEN(big_hds); + size_t i; + int rv; + nghttp2_mem *mem; + + mem = nghttp2_mem_default(); + frame_pack_bufs_init(&bufs); + + for (i = 0; i < big_hdslen; ++i) { + big_hds[i].name = (uint8_t *)"header"; + big_hds[i].value = mem->malloc(big_vallen + 1, NULL); + memset(big_hds[i].value, '0' + (int)i, big_vallen); + big_hds[i].value[big_vallen] = '\0'; + big_hds[i].namelen = strlen((char *)big_hds[i].name); + big_hds[i].valuelen = big_vallen; + big_hds[i].flags = NGHTTP2_NV_FLAG_NONE; + } + + nghttp2_nv_array_copy(&nva, big_hds, big_hdslen, mem); + nghttp2_hd_deflate_init(&deflater, mem); + nghttp2_frame_headers_init( + &frame, NGHTTP2_FLAG_END_STREAM | NGHTTP2_FLAG_END_HEADERS, 1000000007, + NGHTTP2_HCAT_REQUEST, NULL, nva, big_hdslen); + rv = nghttp2_frame_pack_headers(&bufs, &frame, &deflater); + CU_ASSERT(NGHTTP2_ERR_HEADER_COMP == rv); + + nghttp2_frame_headers_free(&frame, mem); + nghttp2_bufs_free(&bufs); + for (i = 0; i < big_hdslen; ++i) { + mem->free(big_hds[i].value, NULL); + } + nghttp2_hd_deflate_free(&deflater); +} + +void test_nghttp2_frame_pack_priority(void) { + nghttp2_priority frame, oframe; + nghttp2_bufs bufs; + nghttp2_priority_spec pri_spec; + int rv; + + frame_pack_bufs_init(&bufs); + + /* First, pack priority with priority group and weight */ + nghttp2_priority_spec_init(&pri_spec, 1000000009, 12, 1); + + nghttp2_frame_priority_init(&frame, 1000000007, &pri_spec); + rv = nghttp2_frame_pack_priority(&bufs, &frame); + + CU_ASSERT(0 == rv); + CU_ASSERT(NGHTTP2_FRAME_HDLEN + 5 == nghttp2_bufs_len(&bufs)); + CU_ASSERT(0 == unpack_framebuf((nghttp2_frame *)&oframe, &bufs)); + check_frame_header(5, NGHTTP2_PRIORITY, NGHTTP2_FLAG_NONE, 1000000007, + &oframe.hd); + + CU_ASSERT(1000000009 == oframe.pri_spec.stream_id); + CU_ASSERT(12 == oframe.pri_spec.weight); + CU_ASSERT(1 == oframe.pri_spec.exclusive); + + nghttp2_frame_priority_free(&oframe); + nghttp2_bufs_reset(&bufs); + + nghttp2_bufs_free(&bufs); + nghttp2_frame_priority_free(&frame); +} + +void test_nghttp2_frame_pack_rst_stream(void) { + nghttp2_rst_stream frame, oframe; + nghttp2_bufs bufs; + int rv; + + frame_pack_bufs_init(&bufs); + + nghttp2_frame_rst_stream_init(&frame, 1000000007, NGHTTP2_PROTOCOL_ERROR); + rv = nghttp2_frame_pack_rst_stream(&bufs, &frame); + + CU_ASSERT(0 == rv); + CU_ASSERT(NGHTTP2_FRAME_HDLEN + 4 == nghttp2_bufs_len(&bufs)); + CU_ASSERT(0 == unpack_framebuf((nghttp2_frame *)&oframe, &bufs)); + check_frame_header(4, NGHTTP2_RST_STREAM, NGHTTP2_FLAG_NONE, 1000000007, + &oframe.hd); + CU_ASSERT(NGHTTP2_PROTOCOL_ERROR == oframe.error_code); + + nghttp2_frame_rst_stream_free(&oframe); + nghttp2_bufs_reset(&bufs); + + /* Unknown error code is passed to callback as is */ + frame.error_code = 1000000009; + rv = nghttp2_frame_pack_rst_stream(&bufs, &frame); + + CU_ASSERT(0 == rv); + CU_ASSERT(0 == unpack_framebuf((nghttp2_frame *)&oframe, &bufs)); + + check_frame_header(4, NGHTTP2_RST_STREAM, NGHTTP2_FLAG_NONE, 1000000007, + &oframe.hd); + + CU_ASSERT(1000000009 == oframe.error_code); + + nghttp2_frame_rst_stream_free(&oframe); + + nghttp2_frame_rst_stream_free(&frame); + + nghttp2_bufs_free(&bufs); +} + +void test_nghttp2_frame_pack_settings() { + nghttp2_settings frame, oframe; + nghttp2_bufs bufs; + int i; + int rv; + nghttp2_settings_entry iv[] = {{NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 256}, + {NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE, 16384}, + {NGHTTP2_SETTINGS_HEADER_TABLE_SIZE, 4096}}; + nghttp2_mem *mem; + + mem = nghttp2_mem_default(); + frame_pack_bufs_init(&bufs); + + nghttp2_frame_settings_init(&frame, NGHTTP2_FLAG_NONE, + nghttp2_frame_iv_copy(iv, 3, mem), 3); + rv = nghttp2_frame_pack_settings(&bufs, &frame); + + CU_ASSERT(0 == rv); + CU_ASSERT(NGHTTP2_FRAME_HDLEN + 3 * NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH == + nghttp2_bufs_len(&bufs)); + + CU_ASSERT(0 == unpack_framebuf((nghttp2_frame *)&oframe, &bufs)); + check_frame_header(3 * NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH, NGHTTP2_SETTINGS, + NGHTTP2_FLAG_NONE, 0, &oframe.hd); + CU_ASSERT(3 == oframe.niv); + for (i = 0; i < 3; ++i) { + CU_ASSERT(iv[i].settings_id == oframe.iv[i].settings_id); + CU_ASSERT(iv[i].value == oframe.iv[i].value); + } + + nghttp2_bufs_free(&bufs); + nghttp2_frame_settings_free(&frame, mem); + nghttp2_frame_settings_free(&oframe, mem); +} + +void test_nghttp2_frame_pack_push_promise() { + nghttp2_hd_deflater deflater; + nghttp2_hd_inflater inflater; + nghttp2_push_promise frame, oframe; + nghttp2_bufs bufs; + nghttp2_nv *nva; + ssize_t nvlen; + nva_out out; + ssize_t hdblocklen; + int rv; + nghttp2_mem *mem; + + mem = nghttp2_mem_default(); + frame_pack_bufs_init(&bufs); + + nva_out_init(&out); + nghttp2_hd_deflate_init(&deflater, mem); + nghttp2_hd_inflate_init(&inflater, mem); + + nva = headers(mem); + nvlen = HEADERS_LENGTH; + nghttp2_frame_push_promise_init(&frame, NGHTTP2_FLAG_END_HEADERS, 1000000007, + (1U << 31) - 1, nva, nvlen); + rv = nghttp2_frame_pack_push_promise(&bufs, &frame, &deflater); + + CU_ASSERT(0 == rv); + CU_ASSERT(nghttp2_bufs_len(&bufs) > 0); + CU_ASSERT(0 == unpack_framebuf((nghttp2_frame *)&oframe, &bufs)); + + check_frame_header(nghttp2_bufs_len(&bufs) - NGHTTP2_FRAME_HDLEN, + NGHTTP2_PUSH_PROMISE, NGHTTP2_FLAG_END_HEADERS, 1000000007, + &oframe.hd); + CU_ASSERT((1U << 31) - 1 == oframe.promised_stream_id); + + hdblocklen = nghttp2_bufs_len(&bufs) - NGHTTP2_FRAME_HDLEN - 4; + CU_ASSERT(hdblocklen == + inflate_hd(&inflater, &out, &bufs, NGHTTP2_FRAME_HDLEN + 4, mem)); + + CU_ASSERT(7 == out.nvlen); + CU_ASSERT(nvnameeq("method", &out.nva[0])); + CU_ASSERT(nvvalueeq("GET", &out.nva[0])); + + nva_out_reset(&out, mem); + nghttp2_bufs_free(&bufs); + nghttp2_frame_push_promise_free(&oframe, mem); + nghttp2_frame_push_promise_free(&frame, mem); + nghttp2_hd_inflate_free(&inflater); + nghttp2_hd_deflate_free(&deflater); +} + +void test_nghttp2_frame_pack_ping(void) { + nghttp2_ping frame, oframe; + nghttp2_bufs bufs; + const uint8_t opaque_data[] = "01234567"; + int rv; + + frame_pack_bufs_init(&bufs); + + nghttp2_frame_ping_init(&frame, NGHTTP2_FLAG_ACK, opaque_data); + rv = nghttp2_frame_pack_ping(&bufs, &frame); + + CU_ASSERT(0 == rv); + CU_ASSERT(NGHTTP2_FRAME_HDLEN + 8 == nghttp2_bufs_len(&bufs)); + CU_ASSERT(0 == unpack_framebuf((nghttp2_frame *)&oframe, &bufs)); + check_frame_header(8, NGHTTP2_PING, NGHTTP2_FLAG_ACK, 0, &oframe.hd); + CU_ASSERT(memcmp(opaque_data, oframe.opaque_data, sizeof(opaque_data) - 1) == + 0); + + nghttp2_bufs_free(&bufs); + nghttp2_frame_ping_free(&oframe); + nghttp2_frame_ping_free(&frame); +} + +void test_nghttp2_frame_pack_goaway() { + nghttp2_goaway frame, oframe; + nghttp2_bufs bufs; + size_t opaque_data_len = 16; + uint8_t *opaque_data; + int rv; + nghttp2_mem *mem; + + mem = nghttp2_mem_default(); + frame_pack_bufs_init(&bufs); + + opaque_data = mem->malloc(opaque_data_len, NULL); + memcpy(opaque_data, "0123456789abcdef", opaque_data_len); + nghttp2_frame_goaway_init(&frame, 1000000007, NGHTTP2_PROTOCOL_ERROR, + opaque_data, opaque_data_len); + rv = nghttp2_frame_pack_goaway(&bufs, &frame); + + CU_ASSERT(0 == rv); + CU_ASSERT((ssize_t)(NGHTTP2_FRAME_HDLEN + 8 + opaque_data_len) == + nghttp2_bufs_len(&bufs)); + CU_ASSERT(0 == unpack_framebuf((nghttp2_frame *)&oframe, &bufs)); + check_frame_header(24, NGHTTP2_GOAWAY, NGHTTP2_FLAG_NONE, 0, &oframe.hd); + CU_ASSERT(1000000007 == oframe.last_stream_id); + CU_ASSERT(NGHTTP2_PROTOCOL_ERROR == oframe.error_code); + + CU_ASSERT(opaque_data_len == oframe.opaque_data_len); + CU_ASSERT(memcmp(opaque_data, oframe.opaque_data, opaque_data_len) == 0); + + nghttp2_frame_goaway_free(&oframe, mem); + nghttp2_bufs_reset(&bufs); + + /* Unknown error code is passed to callback as is */ + frame.error_code = 1000000009; + + rv = nghttp2_frame_pack_goaway(&bufs, &frame); + + CU_ASSERT(0 == rv); + CU_ASSERT(0 == unpack_framebuf((nghttp2_frame *)&oframe, &bufs)); + check_frame_header(24, NGHTTP2_GOAWAY, NGHTTP2_FLAG_NONE, 0, &oframe.hd); + CU_ASSERT(1000000009 == oframe.error_code); + + nghttp2_frame_goaway_free(&oframe, mem); + + nghttp2_frame_goaway_free(&frame, mem); + + nghttp2_bufs_free(&bufs); +} + +void test_nghttp2_frame_pack_window_update(void) { + nghttp2_window_update frame, oframe; + nghttp2_bufs bufs; + int rv; + + frame_pack_bufs_init(&bufs); + + nghttp2_frame_window_update_init(&frame, NGHTTP2_FLAG_NONE, 1000000007, 4096); + rv = nghttp2_frame_pack_window_update(&bufs, &frame); + + CU_ASSERT(0 == rv); + CU_ASSERT(NGHTTP2_FRAME_HDLEN + 4 == nghttp2_bufs_len(&bufs)); + CU_ASSERT(0 == unpack_framebuf((nghttp2_frame *)&oframe, &bufs)); + check_frame_header(4, NGHTTP2_WINDOW_UPDATE, NGHTTP2_FLAG_NONE, 1000000007, + &oframe.hd); + CU_ASSERT(4096 == oframe.window_size_increment); + + nghttp2_bufs_free(&bufs); + nghttp2_frame_window_update_free(&oframe); + nghttp2_frame_window_update_free(&frame); +} + +void test_nghttp2_nv_array_copy(void) { + nghttp2_nv *nva; + ssize_t rv; + nghttp2_nv emptynv[] = {MAKE_NV("", ""), MAKE_NV("", "")}; + nghttp2_nv nv[] = {MAKE_NV("alpha", "bravo"), MAKE_NV("charlie", "delta")}; + nghttp2_nv bignv; + nghttp2_mem *mem; + + mem = nghttp2_mem_default(); + + bignv.name = (uint8_t *)"echo"; + bignv.namelen = strlen("echo"); + bignv.valuelen = (1 << 14) - 1; + bignv.value = mem->malloc(bignv.valuelen, NULL); + memset(bignv.value, '0', bignv.valuelen); + + rv = nghttp2_nv_array_copy(&nva, NULL, 0, mem); + CU_ASSERT(0 == rv); + CU_ASSERT(NULL == nva); + + rv = nghttp2_nv_array_copy(&nva, emptynv, ARRLEN(emptynv), mem); + CU_ASSERT(0 == rv); + CU_ASSERT(nva[0].namelen == 0); + CU_ASSERT(nva[0].valuelen == 0); + CU_ASSERT(nva[1].namelen == 0); + CU_ASSERT(nva[1].valuelen == 0); + + nghttp2_nv_array_del(nva, mem); + + rv = nghttp2_nv_array_copy(&nva, nv, ARRLEN(nv), mem); + CU_ASSERT(0 == rv); + CU_ASSERT(nva[0].namelen == 5); + CU_ASSERT(0 == memcmp("alpha", nva[0].name, 5)); + CU_ASSERT(nva[0].valuelen = 5); + CU_ASSERT(0 == memcmp("bravo", nva[0].value, 5)); + CU_ASSERT(nva[1].namelen == 7); + CU_ASSERT(0 == memcmp("charlie", nva[1].name, 7)); + CU_ASSERT(nva[1].valuelen == 5); + CU_ASSERT(0 == memcmp("delta", nva[1].value, 5)); + + nghttp2_nv_array_del(nva, mem); + + /* Large header field is acceptable */ + rv = nghttp2_nv_array_copy(&nva, &bignv, 1, mem); + CU_ASSERT(0 == rv); + + nghttp2_nv_array_del(nva, mem); + + mem->free(bignv.value, NULL); +} + +void test_nghttp2_iv_check(void) { + nghttp2_settings_entry iv[5]; + + iv[0].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS; + iv[0].value = 100; + iv[1].settings_id = NGHTTP2_SETTINGS_HEADER_TABLE_SIZE; + iv[1].value = 1024; + + CU_ASSERT(nghttp2_iv_check(iv, 2)); + + iv[1].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE; + iv[1].value = NGHTTP2_MAX_WINDOW_SIZE; + CU_ASSERT(nghttp2_iv_check(iv, 2)); + + /* Too large window size */ + iv[1].value = (uint32_t)NGHTTP2_MAX_WINDOW_SIZE + 1; + CU_ASSERT(0 == nghttp2_iv_check(iv, 2)); + + /* ENABLE_PUSH only allows 0 or 1 */ + iv[1].settings_id = NGHTTP2_SETTINGS_ENABLE_PUSH; + iv[1].value = 0; + CU_ASSERT(nghttp2_iv_check(iv, 2)); + iv[1].value = 1; + CU_ASSERT(nghttp2_iv_check(iv, 2)); + iv[1].value = 3; + CU_ASSERT(!nghttp2_iv_check(iv, 2)); + + /* Undefined SETTINGS ID is allowed */ + iv[1].settings_id = 1000000009; + iv[1].value = 0; + CU_ASSERT(nghttp2_iv_check(iv, 2)); + + /* Too large SETTINGS_HEADER_TABLE_SIZE */ + iv[1].settings_id = NGHTTP2_SETTINGS_HEADER_TABLE_SIZE; + iv[1].value = UINT32_MAX; + CU_ASSERT(!nghttp2_iv_check(iv, 2)); + + /* Too small SETTINGS_MAX_FRAME_SIZE */ + iv[0].settings_id = NGHTTP2_SETTINGS_MAX_FRAME_SIZE; + iv[0].value = NGHTTP2_MAX_FRAME_SIZE_MIN - 1; + CU_ASSERT(!nghttp2_iv_check(iv, 1)); + + /* Too large SETTINGS_MAX_FRAME_SIZE */ + iv[0].settings_id = NGHTTP2_SETTINGS_MAX_FRAME_SIZE; + iv[0].value = NGHTTP2_MAX_FRAME_SIZE_MAX + 1; + CU_ASSERT(!nghttp2_iv_check(iv, 1)); + + /* Max and min SETTINGS_MAX_FRAME_SIZE */ + iv[0].settings_id = NGHTTP2_SETTINGS_MAX_FRAME_SIZE; + iv[0].value = NGHTTP2_MAX_FRAME_SIZE_MIN; + iv[1].settings_id = NGHTTP2_SETTINGS_MAX_FRAME_SIZE; + iv[1].value = NGHTTP2_MAX_FRAME_SIZE_MAX; + CU_ASSERT(nghttp2_iv_check(iv, 2)); +} diff --git a/tests/nghttp2_frame_test.h b/tests/nghttp2_frame_test.h new file mode 100644 index 0000000..a0ce37b --- /dev/null +++ b/tests/nghttp2_frame_test.h @@ -0,0 +1,40 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGHTTP2_FRAME_TEST_H +#define NGHTTP2_FRAME_TEST_H + +void test_nghttp2_frame_pack_headers(void); +void test_nghttp2_frame_pack_headers_frame_too_large(void); +void test_nghttp2_frame_pack_priority(void); +void test_nghttp2_frame_pack_rst_stream(void); +void test_nghttp2_frame_pack_settings(void); +void test_nghttp2_frame_pack_push_promise(void); +void test_nghttp2_frame_pack_ping(void); +void test_nghttp2_frame_pack_goaway(void); +void test_nghttp2_frame_pack_window_update(void); +void test_nghttp2_nv_array_copy(void); +void test_nghttp2_iv_check(void); + +#endif /* NGHTTP2_FRAME_TEST_H */ diff --git a/tests/nghttp2_hd_test.c b/tests/nghttp2_hd_test.c new file mode 100644 index 0000000..ecd7bc1 --- /dev/null +++ b/tests/nghttp2_hd_test.c @@ -0,0 +1,1257 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2013 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "nghttp2_hd_test.h" + +#include +#include + +#include + +#include "nghttp2_hd.h" +#include "nghttp2_frame.h" +#include "nghttp2_test_helper.h" + +#define GET_TABLE_ENT(context, index) nghttp2_hd_table_get(context, index) + +void test_nghttp2_hd_deflate(void) { + nghttp2_hd_deflater deflater; + nghttp2_hd_inflater inflater; + nghttp2_nv nva1[] = {MAKE_NV(":path", "/my-example/index.html"), + MAKE_NV(":scheme", "https"), MAKE_NV("hello", "world")}; + nghttp2_nv nva2[] = {MAKE_NV(":path", "/script.js"), + MAKE_NV(":scheme", "https")}; + nghttp2_nv nva3[] = {MAKE_NV("cookie", "k1=v1"), MAKE_NV("cookie", "k2=v2"), + MAKE_NV("via", "proxy")}; + nghttp2_nv nva4[] = {MAKE_NV(":path", "/style.css"), + MAKE_NV("cookie", "k1=v1"), MAKE_NV("cookie", "k1=v1")}; + nghttp2_nv nva5[] = {MAKE_NV(":path", "/style.css"), + MAKE_NV("x-nghttp2", "")}; + nghttp2_bufs bufs; + ssize_t blocklen; + nva_out out; + int rv; + nghttp2_mem *mem; + + mem = nghttp2_mem_default(); + frame_pack_bufs_init(&bufs); + + nva_out_init(&out); + CU_ASSERT(0 == nghttp2_hd_deflate_init(&deflater, mem)); + CU_ASSERT(0 == nghttp2_hd_inflate_init(&inflater, mem)); + + rv = nghttp2_hd_deflate_hd_bufs(&deflater, &bufs, nva1, ARRLEN(nva1)); + blocklen = nghttp2_bufs_len(&bufs); + + CU_ASSERT(0 == rv); + CU_ASSERT(blocklen > 0); + CU_ASSERT(blocklen == inflate_hd(&inflater, &out, &bufs, 0, mem)); + + CU_ASSERT(3 == out.nvlen); + assert_nv_equal(nva1, out.nva, 3, mem); + + nva_out_reset(&out, mem); + nghttp2_bufs_reset(&bufs); + + /* Second headers */ + rv = nghttp2_hd_deflate_hd_bufs(&deflater, &bufs, nva2, ARRLEN(nva2)); + blocklen = nghttp2_bufs_len(&bufs); + + CU_ASSERT(0 == rv); + CU_ASSERT(blocklen > 0); + CU_ASSERT(blocklen == inflate_hd(&inflater, &out, &bufs, 0, mem)); + + CU_ASSERT(2 == out.nvlen); + assert_nv_equal(nva2, out.nva, 2, mem); + + nva_out_reset(&out, mem); + nghttp2_bufs_reset(&bufs); + + /* Third headers, including same header field name, but value is not + the same. */ + rv = nghttp2_hd_deflate_hd_bufs(&deflater, &bufs, nva3, ARRLEN(nva3)); + blocklen = nghttp2_bufs_len(&bufs); + + CU_ASSERT(0 == rv); + CU_ASSERT(blocklen > 0); + CU_ASSERT(blocklen == inflate_hd(&inflater, &out, &bufs, 0, mem)); + + CU_ASSERT(3 == out.nvlen); + assert_nv_equal(nva3, out.nva, 3, mem); + + nva_out_reset(&out, mem); + nghttp2_bufs_reset(&bufs); + + /* Fourth headers, including duplicate header fields. */ + rv = nghttp2_hd_deflate_hd_bufs(&deflater, &bufs, nva4, ARRLEN(nva4)); + blocklen = nghttp2_bufs_len(&bufs); + + CU_ASSERT(0 == rv); + CU_ASSERT(blocklen > 0); + CU_ASSERT(blocklen == inflate_hd(&inflater, &out, &bufs, 0, mem)); + + CU_ASSERT(3 == out.nvlen); + assert_nv_equal(nva4, out.nva, 3, mem); + + nva_out_reset(&out, mem); + nghttp2_bufs_reset(&bufs); + + /* Fifth headers includes empty value */ + rv = nghttp2_hd_deflate_hd_bufs(&deflater, &bufs, nva5, ARRLEN(nva5)); + blocklen = nghttp2_bufs_len(&bufs); + + CU_ASSERT(0 == rv); + CU_ASSERT(blocklen > 0); + CU_ASSERT(blocklen == inflate_hd(&inflater, &out, &bufs, 0, mem)); + + CU_ASSERT(2 == out.nvlen); + assert_nv_equal(nva5, out.nva, 2, mem); + + nva_out_reset(&out, mem); + nghttp2_bufs_reset(&bufs); + + /* Cleanup */ + nghttp2_bufs_free(&bufs); + nghttp2_hd_inflate_free(&inflater); + nghttp2_hd_deflate_free(&deflater); +} + +void test_nghttp2_hd_deflate_same_indexed_repr(void) { + nghttp2_hd_deflater deflater; + nghttp2_hd_inflater inflater; + nghttp2_nv nva1[] = {MAKE_NV("host", "alpha"), MAKE_NV("host", "alpha")}; + nghttp2_nv nva2[] = {MAKE_NV("host", "alpha"), MAKE_NV("host", "alpha"), + MAKE_NV("host", "alpha")}; + nghttp2_bufs bufs; + ssize_t blocklen; + nva_out out; + int rv; + nghttp2_mem *mem; + + mem = nghttp2_mem_default(); + frame_pack_bufs_init(&bufs); + + nva_out_init(&out); + CU_ASSERT(0 == nghttp2_hd_deflate_init(&deflater, mem)); + CU_ASSERT(0 == nghttp2_hd_inflate_init(&inflater, mem)); + + /* Encode 2 same headers. Emit 1 literal reprs and 1 index repr. */ + rv = nghttp2_hd_deflate_hd_bufs(&deflater, &bufs, nva1, ARRLEN(nva1)); + blocklen = nghttp2_bufs_len(&bufs); + + CU_ASSERT(0 == rv); + CU_ASSERT(blocklen > 0); + CU_ASSERT(blocklen == inflate_hd(&inflater, &out, &bufs, 0, mem)); + + CU_ASSERT(2 == out.nvlen); + assert_nv_equal(nva1, out.nva, 2, mem); + + nva_out_reset(&out, mem); + nghttp2_bufs_reset(&bufs); + + /* Encode 3 same headers. This time, emits 3 index reprs. */ + rv = nghttp2_hd_deflate_hd_bufs(&deflater, &bufs, nva2, ARRLEN(nva2)); + blocklen = nghttp2_bufs_len(&bufs); + + CU_ASSERT(0 == rv); + CU_ASSERT(blocklen == 3); + CU_ASSERT(blocklen == inflate_hd(&inflater, &out, &bufs, 0, mem)); + + CU_ASSERT(3 == out.nvlen); + assert_nv_equal(nva2, out.nva, 3, mem); + + nva_out_reset(&out, mem); + nghttp2_bufs_reset(&bufs); + + /* Cleanup */ + nghttp2_bufs_free(&bufs); + nghttp2_hd_inflate_free(&inflater); + nghttp2_hd_deflate_free(&deflater); +} + +void test_nghttp2_hd_inflate_indexed(void) { + nghttp2_hd_inflater inflater; + nghttp2_bufs bufs; + ssize_t blocklen; + nghttp2_nv nv = MAKE_NV(":path", "/"); + nva_out out; + nghttp2_mem *mem; + + mem = nghttp2_mem_default(); + frame_pack_bufs_init(&bufs); + + nva_out_init(&out); + nghttp2_hd_inflate_init(&inflater, mem); + + nghttp2_bufs_addb(&bufs, (1 << 7) | 4); + + blocklen = nghttp2_bufs_len(&bufs); + + CU_ASSERT(1 == blocklen); + CU_ASSERT(blocklen == inflate_hd(&inflater, &out, &bufs, 0, mem)); + + CU_ASSERT(1 == out.nvlen); + + assert_nv_equal(&nv, out.nva, 1, mem); + + nva_out_reset(&out, mem); + nghttp2_bufs_reset(&bufs); + + /* index = 0 is error */ + nghttp2_bufs_addb(&bufs, 1 << 7); + + blocklen = nghttp2_bufs_len(&bufs); + + CU_ASSERT(1 == blocklen); + CU_ASSERT(NGHTTP2_ERR_HEADER_COMP == + inflate_hd(&inflater, &out, &bufs, 0, mem)); + + nghttp2_bufs_free(&bufs); + nghttp2_hd_inflate_free(&inflater); +} + +void test_nghttp2_hd_inflate_indname_noinc(void) { + nghttp2_hd_inflater inflater; + nghttp2_bufs bufs; + ssize_t blocklen; + nghttp2_nv nv[] = {/* Huffman */ + MAKE_NV("user-agent", "nghttp2"), + /* Expecting no huffman */ + MAKE_NV("user-agent", "x")}; + size_t i; + nva_out out; + nghttp2_mem *mem; + + mem = nghttp2_mem_default(); + frame_pack_bufs_init(&bufs); + + nva_out_init(&out); + nghttp2_hd_inflate_init(&inflater, mem); + + for (i = 0; i < ARRLEN(nv); ++i) { + CU_ASSERT(0 == nghttp2_hd_emit_indname_block(&bufs, 57, &nv[i], + NGHTTP2_HD_WITHOUT_INDEXING)); + + blocklen = nghttp2_bufs_len(&bufs); + + CU_ASSERT(blocklen > 0); + CU_ASSERT(blocklen == inflate_hd(&inflater, &out, &bufs, 0, mem)); + + CU_ASSERT(1 == out.nvlen); + assert_nv_equal(&nv[i], out.nva, 1, mem); + CU_ASSERT(0 == inflater.ctx.hd_table.len); + + nva_out_reset(&out, mem); + nghttp2_bufs_reset(&bufs); + } + + nghttp2_bufs_free(&bufs); + nghttp2_hd_inflate_free(&inflater); +} + +void test_nghttp2_hd_inflate_indname_inc(void) { + nghttp2_hd_inflater inflater; + nghttp2_bufs bufs; + ssize_t blocklen; + nghttp2_nv nv = MAKE_NV("user-agent", "nghttp2"); + nva_out out; + nghttp2_mem *mem; + + mem = nghttp2_mem_default(); + frame_pack_bufs_init(&bufs); + + nva_out_init(&out); + nghttp2_hd_inflate_init(&inflater, mem); + + CU_ASSERT(0 == nghttp2_hd_emit_indname_block(&bufs, 57, &nv, + NGHTTP2_HD_WITH_INDEXING)); + + blocklen = nghttp2_bufs_len(&bufs); + + CU_ASSERT(blocklen > 0); + CU_ASSERT(blocklen == inflate_hd(&inflater, &out, &bufs, 0, mem)); + + CU_ASSERT(1 == out.nvlen); + assert_nv_equal(&nv, out.nva, 1, mem); + CU_ASSERT(1 == inflater.ctx.hd_table.len); + assert_nv_equal( + &nv, &GET_TABLE_ENT(&inflater.ctx, NGHTTP2_STATIC_TABLE_LENGTH + + inflater.ctx.hd_table.len - 1)->nv, + 1, mem); + + nva_out_reset(&out, mem); + nghttp2_bufs_free(&bufs); + nghttp2_hd_inflate_free(&inflater); +} + +void test_nghttp2_hd_inflate_indname_inc_eviction(void) { + nghttp2_hd_inflater inflater; + nghttp2_bufs bufs; + ssize_t blocklen; + uint8_t value[1025]; + nva_out out; + nghttp2_nv nv; + nghttp2_mem *mem; + + mem = nghttp2_mem_default(); + frame_pack_bufs_init(&bufs); + + nva_out_init(&out); + nghttp2_hd_inflate_init(&inflater, mem); + + memset(value, '0', sizeof(value)); + value[sizeof(value) - 1] = '\0'; + nv.value = value; + nv.valuelen = sizeof(value) - 1; + + nv.flags = NGHTTP2_NV_FLAG_NONE; + + CU_ASSERT(0 == nghttp2_hd_emit_indname_block(&bufs, 14, &nv, + NGHTTP2_HD_WITH_INDEXING)); + CU_ASSERT(0 == nghttp2_hd_emit_indname_block(&bufs, 15, &nv, + NGHTTP2_HD_WITH_INDEXING)); + CU_ASSERT(0 == nghttp2_hd_emit_indname_block(&bufs, 16, &nv, + NGHTTP2_HD_WITH_INDEXING)); + CU_ASSERT(0 == nghttp2_hd_emit_indname_block(&bufs, 17, &nv, + NGHTTP2_HD_WITH_INDEXING)); + + blocklen = nghttp2_bufs_len(&bufs); + + CU_ASSERT(blocklen > 0); + + CU_ASSERT(blocklen == inflate_hd(&inflater, &out, &bufs, 0, mem)); + + CU_ASSERT(4 == out.nvlen); + CU_ASSERT(14 == out.nva[0].namelen); + CU_ASSERT(0 == memcmp("accept-charset", out.nva[0].name, out.nva[0].namelen)); + CU_ASSERT(sizeof(value) - 1 == out.nva[0].valuelen); + + nva_out_reset(&out, mem); + nghttp2_bufs_reset(&bufs); + + CU_ASSERT(3 == inflater.ctx.hd_table.len); + + nghttp2_bufs_free(&bufs); + nghttp2_hd_inflate_free(&inflater); +} + +void test_nghttp2_hd_inflate_newname_noinc(void) { + nghttp2_hd_inflater inflater; + nghttp2_bufs bufs; + ssize_t blocklen; + nghttp2_nv nv[] = {/* Expecting huffman for both */ + MAKE_NV("my-long-content-length", "nghttp2"), + /* Expecting no huffman for both */ + MAKE_NV("x", "y"), + /* Huffman for key only */ + MAKE_NV("my-long-content-length", "y"), + /* Huffman for value only */ + MAKE_NV("x", "nghttp2")}; + size_t i; + nva_out out; + nghttp2_mem *mem; + + mem = nghttp2_mem_default(); + frame_pack_bufs_init(&bufs); + + nva_out_init(&out); + nghttp2_hd_inflate_init(&inflater, mem); + for (i = 0; i < ARRLEN(nv); ++i) { + CU_ASSERT(0 == nghttp2_hd_emit_newname_block(&bufs, &nv[i], + NGHTTP2_HD_WITHOUT_INDEXING)); + + blocklen = nghttp2_bufs_len(&bufs); + + CU_ASSERT(blocklen > 0); + CU_ASSERT(blocklen == inflate_hd(&inflater, &out, &bufs, 0, mem)); + + CU_ASSERT(1 == out.nvlen); + assert_nv_equal(&nv[i], out.nva, 1, mem); + CU_ASSERT(0 == inflater.ctx.hd_table.len); + + nva_out_reset(&out, mem); + nghttp2_bufs_reset(&bufs); + } + + nghttp2_bufs_free(&bufs); + nghttp2_hd_inflate_free(&inflater); +} + +void test_nghttp2_hd_inflate_newname_inc(void) { + nghttp2_hd_inflater inflater; + nghttp2_bufs bufs; + ssize_t blocklen; + nghttp2_nv nv = MAKE_NV("x-rel", "nghttp2"); + nva_out out; + nghttp2_mem *mem; + + mem = nghttp2_mem_default(); + frame_pack_bufs_init(&bufs); + + nva_out_init(&out); + nghttp2_hd_inflate_init(&inflater, mem); + + CU_ASSERT( + 0 == nghttp2_hd_emit_newname_block(&bufs, &nv, NGHTTP2_HD_WITH_INDEXING)); + + blocklen = nghttp2_bufs_len(&bufs); + + CU_ASSERT(blocklen > 0); + CU_ASSERT(blocklen == inflate_hd(&inflater, &out, &bufs, 0, mem)); + + CU_ASSERT(1 == out.nvlen); + assert_nv_equal(&nv, out.nva, 1, mem); + CU_ASSERT(1 == inflater.ctx.hd_table.len); + assert_nv_equal( + &nv, &GET_TABLE_ENT(&inflater.ctx, NGHTTP2_STATIC_TABLE_LENGTH + + inflater.ctx.hd_table.len - 1)->nv, + 1, mem); + + nva_out_reset(&out, mem); + nghttp2_bufs_free(&bufs); + nghttp2_hd_inflate_free(&inflater); +} + +void test_nghttp2_hd_inflate_clearall_inc(void) { + nghttp2_hd_inflater inflater; + nghttp2_bufs bufs; + ssize_t blocklen; + nghttp2_nv nv; + uint8_t value[4061]; + nva_out out; + nghttp2_mem *mem; + + mem = nghttp2_mem_default(); + bufs_large_init(&bufs, 8192); + + nva_out_init(&out); + /* Total 4097 bytes space required to hold this entry */ + nv.name = (uint8_t *)"alpha"; + nv.namelen = strlen((char *)nv.name); + memset(value, '0', sizeof(value)); + value[sizeof(value) - 1] = '\0'; + nv.value = value; + nv.valuelen = sizeof(value) - 1; + + nv.flags = NGHTTP2_NV_FLAG_NONE; + + nghttp2_hd_inflate_init(&inflater, mem); + + CU_ASSERT( + 0 == nghttp2_hd_emit_newname_block(&bufs, &nv, NGHTTP2_HD_WITH_INDEXING)); + + blocklen = nghttp2_bufs_len(&bufs); + + CU_ASSERT(blocklen > 0); + CU_ASSERT(blocklen == inflate_hd(&inflater, &out, &bufs, 0, mem)); + + CU_ASSERT(1 == out.nvlen); + assert_nv_equal(&nv, out.nva, 1, mem); + CU_ASSERT(0 == inflater.ctx.hd_table.len); + + nva_out_reset(&out, mem); + + /* Do it again */ + CU_ASSERT(blocklen == inflate_hd(&inflater, &out, &bufs, 0, mem)); + + CU_ASSERT(1 == out.nvlen); + assert_nv_equal(&nv, out.nva, 1, mem); + CU_ASSERT(0 == inflater.ctx.hd_table.len); + + nva_out_reset(&out, mem); + nghttp2_bufs_reset(&bufs); + + /* This time, 4096 bytes space required, which is just fits in the + header table */ + nv.valuelen = sizeof(value) - 2; + + CU_ASSERT( + 0 == nghttp2_hd_emit_newname_block(&bufs, &nv, NGHTTP2_HD_WITH_INDEXING)); + + blocklen = nghttp2_bufs_len(&bufs); + + CU_ASSERT(blocklen > 0); + CU_ASSERT(blocklen == inflate_hd(&inflater, &out, &bufs, 0, mem)); + + CU_ASSERT(1 == out.nvlen); + assert_nv_equal(&nv, out.nva, 1, mem); + CU_ASSERT(1 == inflater.ctx.hd_table.len); + + nva_out_reset(&out, mem); + nghttp2_bufs_reset(&bufs); + + nghttp2_bufs_free(&bufs); + nghttp2_hd_inflate_free(&inflater); +} + +void test_nghttp2_hd_inflate_zero_length_huffman(void) { + nghttp2_hd_inflater inflater; + nghttp2_bufs bufs; + /* Literal header without indexing - new name */ + uint8_t data[] = {0x40, 0x01, 0x78 /* 'x' */, 0x80}; + nva_out out; + nghttp2_mem *mem; + + mem = nghttp2_mem_default(); + frame_pack_bufs_init(&bufs); + + nva_out_init(&out); + + nghttp2_bufs_add(&bufs, data, sizeof(data)); + + /* /\* Literal header without indexing - new name *\/ */ + /* ptr[0] = 0x40; */ + /* ptr[1] = 1; */ + /* ptr[2] = 'x'; */ + /* ptr[3] = 0x80; */ + + nghttp2_hd_inflate_init(&inflater, mem); + CU_ASSERT(4 == inflate_hd(&inflater, &out, &bufs, 0, mem)); + + CU_ASSERT(1 == out.nvlen); + CU_ASSERT(1 == out.nva[0].namelen); + CU_ASSERT('x' == out.nva[0].name[0]); + CU_ASSERT(NULL == out.nva[0].value); + CU_ASSERT(0 == out.nva[0].valuelen); + + nva_out_reset(&out, mem); + nghttp2_bufs_free(&bufs); + nghttp2_hd_inflate_free(&inflater); +} + +void test_nghttp2_hd_ringbuf_reserve(void) { + nghttp2_hd_deflater deflater; + nghttp2_hd_inflater inflater; + nghttp2_nv nv; + nghttp2_bufs bufs; + nva_out out; + int i; + ssize_t rv; + ssize_t blocklen; + nghttp2_mem *mem; + + mem = nghttp2_mem_default(); + frame_pack_bufs_init(&bufs); + nva_out_init(&out); + + nv.flags = NGHTTP2_NV_FLAG_NONE; + nv.name = (uint8_t *)"a"; + nv.namelen = strlen((const char *)nv.name); + nv.valuelen = 4; + nv.value = mem->malloc(nv.valuelen + 1, NULL); + memset(nv.value, 0, nv.valuelen); + + nghttp2_hd_deflate_init2(&deflater, 8000, mem); + nghttp2_hd_inflate_init(&inflater, mem); + + nghttp2_hd_inflate_change_table_size(&inflater, 8000); + nghttp2_hd_deflate_change_table_size(&deflater, 8000); + + for (i = 0; i < 150; ++i) { + memcpy(nv.value, &i, sizeof(i)); + rv = nghttp2_hd_deflate_hd_bufs(&deflater, &bufs, &nv, 1); + blocklen = nghttp2_bufs_len(&bufs); + + CU_ASSERT(0 == rv); + CU_ASSERT(blocklen > 0); + + CU_ASSERT(blocklen == inflate_hd(&inflater, &out, &bufs, 0, mem)); + + CU_ASSERT(1 == out.nvlen); + assert_nv_equal(&nv, out.nva, 1, mem); + + nva_out_reset(&out, mem); + nghttp2_bufs_reset(&bufs); + } + + nghttp2_bufs_free(&bufs); + nghttp2_hd_inflate_free(&inflater); + nghttp2_hd_deflate_free(&deflater); + + mem->free(nv.value, NULL); +} + +void test_nghttp2_hd_change_table_size(void) { + nghttp2_hd_deflater deflater; + nghttp2_hd_inflater inflater; + nghttp2_nv nva[] = {MAKE_NV("alpha", "bravo"), MAKE_NV("charlie", "delta")}; + nghttp2_nv nva2[] = {MAKE_NV(":path", "/")}; + nghttp2_bufs bufs; + ssize_t rv; + nva_out out; + ssize_t blocklen; + nghttp2_mem *mem; + + mem = nghttp2_mem_default(); + frame_pack_bufs_init(&bufs); + + nva_out_init(&out); + + nghttp2_hd_deflate_init(&deflater, mem); + nghttp2_hd_inflate_init(&inflater, mem); + + /* inflater changes notifies 8000 max header table size */ + CU_ASSERT(0 == nghttp2_hd_inflate_change_table_size(&inflater, 8000)); + CU_ASSERT(0 == nghttp2_hd_deflate_change_table_size(&deflater, 8000)); + + CU_ASSERT(4096 == deflater.ctx.hd_table_bufsize_max); + + CU_ASSERT(8000 == inflater.ctx.hd_table_bufsize_max); + CU_ASSERT(8000 == inflater.settings_hd_table_bufsize_max); + + /* This will emit encoding context update with header table size 4096 */ + rv = nghttp2_hd_deflate_hd_bufs(&deflater, &bufs, nva, 2); + blocklen = nghttp2_bufs_len(&bufs); + + CU_ASSERT(0 == rv); + CU_ASSERT(blocklen > 0); + CU_ASSERT(2 == deflater.ctx.hd_table.len); + CU_ASSERT(4096 == deflater.ctx.hd_table_bufsize_max); + + CU_ASSERT(blocklen == inflate_hd(&inflater, &out, &bufs, 0, mem)); + CU_ASSERT(2 == inflater.ctx.hd_table.len); + CU_ASSERT(4096 == inflater.ctx.hd_table_bufsize_max); + CU_ASSERT(8000 == inflater.settings_hd_table_bufsize_max); + + nva_out_reset(&out, mem); + nghttp2_bufs_reset(&bufs); + + /* inflater changes header table size to 1024 */ + CU_ASSERT(0 == nghttp2_hd_inflate_change_table_size(&inflater, 1024)); + CU_ASSERT(0 == nghttp2_hd_deflate_change_table_size(&deflater, 1024)); + + CU_ASSERT(1024 == deflater.ctx.hd_table_bufsize_max); + + CU_ASSERT(1024 == inflater.ctx.hd_table_bufsize_max); + CU_ASSERT(1024 == inflater.settings_hd_table_bufsize_max); + + rv = nghttp2_hd_deflate_hd_bufs(&deflater, &bufs, nva, 2); + blocklen = nghttp2_bufs_len(&bufs); + + CU_ASSERT(0 == rv); + CU_ASSERT(blocklen > 0); + CU_ASSERT(2 == deflater.ctx.hd_table.len); + CU_ASSERT(1024 == deflater.ctx.hd_table_bufsize_max); + + CU_ASSERT(blocklen == inflate_hd(&inflater, &out, &bufs, 0, mem)); + CU_ASSERT(2 == inflater.ctx.hd_table.len); + CU_ASSERT(1024 == inflater.ctx.hd_table_bufsize_max); + CU_ASSERT(1024 == inflater.settings_hd_table_bufsize_max); + + nva_out_reset(&out, mem); + nghttp2_bufs_reset(&bufs); + + /* inflater changes header table size to 0 */ + CU_ASSERT(0 == nghttp2_hd_inflate_change_table_size(&inflater, 0)); + CU_ASSERT(0 == nghttp2_hd_deflate_change_table_size(&deflater, 0)); + + CU_ASSERT(0 == deflater.ctx.hd_table.len); + CU_ASSERT(0 == deflater.ctx.hd_table_bufsize_max); + + CU_ASSERT(0 == inflater.ctx.hd_table.len); + CU_ASSERT(0 == inflater.ctx.hd_table_bufsize_max); + CU_ASSERT(0 == inflater.settings_hd_table_bufsize_max); + + rv = nghttp2_hd_deflate_hd_bufs(&deflater, &bufs, nva, 2); + blocklen = nghttp2_bufs_len(&bufs); + + CU_ASSERT(0 == rv); + CU_ASSERT(blocklen > 0); + CU_ASSERT(0 == deflater.ctx.hd_table.len); + CU_ASSERT(0 == deflater.ctx.hd_table_bufsize_max); + + CU_ASSERT(blocklen == inflate_hd(&inflater, &out, &bufs, 0, mem)); + CU_ASSERT(0 == inflater.ctx.hd_table.len); + CU_ASSERT(0 == inflater.ctx.hd_table_bufsize_max); + CU_ASSERT(0 == inflater.settings_hd_table_bufsize_max); + + nva_out_reset(&out, mem); + nghttp2_bufs_reset(&bufs); + + nghttp2_bufs_free(&bufs); + nghttp2_hd_inflate_free(&inflater); + nghttp2_hd_deflate_free(&deflater); + + /* Check table buffer is expanded */ + frame_pack_bufs_init(&bufs); + + nghttp2_hd_deflate_init2(&deflater, 8192, mem); + nghttp2_hd_inflate_init(&inflater, mem); + + /* First inflater changes header table size to 8000 */ + CU_ASSERT(0 == nghttp2_hd_inflate_change_table_size(&inflater, 8000)); + CU_ASSERT(0 == nghttp2_hd_deflate_change_table_size(&deflater, 8000)); + + CU_ASSERT(8000 == deflater.ctx.hd_table_bufsize_max); + + CU_ASSERT(8000 == inflater.ctx.hd_table_bufsize_max); + CU_ASSERT(8000 == inflater.settings_hd_table_bufsize_max); + + rv = nghttp2_hd_deflate_hd_bufs(&deflater, &bufs, nva, 2); + blocklen = nghttp2_bufs_len(&bufs); + + CU_ASSERT(0 == rv); + CU_ASSERT(blocklen > 0); + CU_ASSERT(2 == deflater.ctx.hd_table.len); + CU_ASSERT(8000 == deflater.ctx.hd_table_bufsize_max); + + CU_ASSERT(blocklen == inflate_hd(&inflater, &out, &bufs, 0, mem)); + CU_ASSERT(2 == inflater.ctx.hd_table.len); + CU_ASSERT(8000 == inflater.ctx.hd_table_bufsize_max); + CU_ASSERT(8000 == inflater.settings_hd_table_bufsize_max); + + nva_out_reset(&out, mem); + nghttp2_bufs_reset(&bufs); + + CU_ASSERT(0 == nghttp2_hd_inflate_change_table_size(&inflater, 16383)); + CU_ASSERT(0 == nghttp2_hd_deflate_change_table_size(&deflater, 16383)); + + CU_ASSERT(8192 == deflater.ctx.hd_table_bufsize_max); + + CU_ASSERT(16383 == inflater.ctx.hd_table_bufsize_max); + CU_ASSERT(16383 == inflater.settings_hd_table_bufsize_max); + + rv = nghttp2_hd_deflate_hd_bufs(&deflater, &bufs, nva, 2); + blocklen = nghttp2_bufs_len(&bufs); + + CU_ASSERT(0 == rv); + CU_ASSERT(blocklen > 0); + CU_ASSERT(2 == deflater.ctx.hd_table.len); + CU_ASSERT(8192 == deflater.ctx.hd_table_bufsize_max); + + CU_ASSERT(blocklen == inflate_hd(&inflater, &out, &bufs, 0, mem)); + CU_ASSERT(2 == inflater.ctx.hd_table.len); + CU_ASSERT(8192 == inflater.ctx.hd_table_bufsize_max); + CU_ASSERT(16383 == inflater.settings_hd_table_bufsize_max); + + nva_out_reset(&out, mem); + nghttp2_bufs_reset(&bufs); + + /* Lastly, check the error condition */ + + rv = nghttp2_hd_emit_table_size(&bufs, 25600); + CU_ASSERT(rv == 0); + CU_ASSERT(NGHTTP2_ERR_HEADER_COMP == + inflate_hd(&inflater, &out, &bufs, 0, mem)); + + nva_out_reset(&out, mem); + nghttp2_bufs_reset(&bufs); + + nghttp2_hd_inflate_free(&inflater); + nghttp2_hd_deflate_free(&deflater); + + /* Check that encoder can handle the case where its allowable buffer + size is less than default size, 4096 */ + nghttp2_hd_deflate_init2(&deflater, 1024, mem); + nghttp2_hd_inflate_init(&inflater, mem); + + CU_ASSERT(1024 == deflater.ctx.hd_table_bufsize_max); + + /* This emits context update with buffer size 1024 */ + rv = nghttp2_hd_deflate_hd_bufs(&deflater, &bufs, nva, 2); + blocklen = nghttp2_bufs_len(&bufs); + + CU_ASSERT(0 == rv); + CU_ASSERT(blocklen > 0); + CU_ASSERT(2 == deflater.ctx.hd_table.len); + CU_ASSERT(1024 == deflater.ctx.hd_table_bufsize_max); + + CU_ASSERT(blocklen == inflate_hd(&inflater, &out, &bufs, 0, mem)); + CU_ASSERT(2 == inflater.ctx.hd_table.len); + CU_ASSERT(1024 == inflater.ctx.hd_table_bufsize_max); + CU_ASSERT(4096 == inflater.settings_hd_table_bufsize_max); + + nva_out_reset(&out, mem); + nghttp2_bufs_reset(&bufs); + + nghttp2_hd_inflate_free(&inflater); + nghttp2_hd_deflate_free(&deflater); + + /* Check that table size UINT32_MAX can be received */ + nghttp2_hd_deflate_init2(&deflater, UINT32_MAX, mem); + nghttp2_hd_inflate_init(&inflater, mem); + + CU_ASSERT(0 == nghttp2_hd_inflate_change_table_size(&inflater, UINT32_MAX)); + CU_ASSERT(0 == nghttp2_hd_deflate_change_table_size(&deflater, UINT32_MAX)); + + rv = nghttp2_hd_deflate_hd_bufs(&deflater, &bufs, nva, 2); + blocklen = nghttp2_bufs_len(&bufs); + + CU_ASSERT(0 == rv); + CU_ASSERT(UINT32_MAX == deflater.ctx.hd_table_bufsize_max); + + CU_ASSERT(blocklen == inflate_hd(&inflater, &out, &bufs, 0, mem)); + CU_ASSERT(UINT32_MAX == inflater.ctx.hd_table_bufsize_max); + CU_ASSERT(UINT32_MAX == inflater.settings_hd_table_bufsize_max); + + nva_out_reset(&out, mem); + nghttp2_bufs_reset(&bufs); + + nghttp2_hd_inflate_free(&inflater); + nghttp2_hd_deflate_free(&deflater); + + /* Check that context update emitted twice */ + nghttp2_hd_deflate_init2(&deflater, 4096, mem); + nghttp2_hd_inflate_init(&inflater, mem); + + CU_ASSERT(0 == nghttp2_hd_inflate_change_table_size(&inflater, 0)); + CU_ASSERT(0 == nghttp2_hd_inflate_change_table_size(&inflater, 3000)); + CU_ASSERT(0 == nghttp2_hd_deflate_change_table_size(&deflater, 0)); + CU_ASSERT(0 == nghttp2_hd_deflate_change_table_size(&deflater, 3000)); + + CU_ASSERT(0 == deflater.min_hd_table_bufsize_max); + CU_ASSERT(3000 == deflater.ctx.hd_table_bufsize_max); + + rv = nghttp2_hd_deflate_hd_bufs(&deflater, &bufs, nva2, 1); + blocklen = nghttp2_bufs_len(&bufs); + + CU_ASSERT(0 == rv); + CU_ASSERT(3 < blocklen); + CU_ASSERT(3000 == deflater.ctx.hd_table_bufsize_max); + CU_ASSERT(UINT32_MAX == deflater.min_hd_table_bufsize_max); + + CU_ASSERT(blocklen == inflate_hd(&inflater, &out, &bufs, 0, mem)); + CU_ASSERT(3000 == inflater.ctx.hd_table_bufsize_max); + CU_ASSERT(3000 == inflater.settings_hd_table_bufsize_max); + + nva_out_reset(&out, mem); + nghttp2_bufs_reset(&bufs); + + nghttp2_hd_inflate_free(&inflater); + nghttp2_hd_deflate_free(&deflater); + + nghttp2_bufs_free(&bufs); +} + +static void check_deflate_inflate(nghttp2_hd_deflater *deflater, + nghttp2_hd_inflater *inflater, + nghttp2_nv *nva, size_t nvlen, + nghttp2_mem *mem) { + nghttp2_bufs bufs; + ssize_t blocklen; + nva_out out; + int rv; + + frame_pack_bufs_init(&bufs); + + nva_out_init(&out); + rv = nghttp2_hd_deflate_hd_bufs(deflater, &bufs, nva, nvlen); + blocklen = nghttp2_bufs_len(&bufs); + + CU_ASSERT(0 == rv); + CU_ASSERT(blocklen >= 0); + + CU_ASSERT(blocklen == inflate_hd(inflater, &out, &bufs, 0, mem)); + + CU_ASSERT(nvlen == out.nvlen); + assert_nv_equal(nva, out.nva, nvlen, mem); + + nva_out_reset(&out, mem); + nghttp2_bufs_free(&bufs); +} + +void test_nghttp2_hd_deflate_inflate(void) { + nghttp2_hd_deflater deflater; + nghttp2_hd_inflater inflater; + nghttp2_nv nv1[] = { + MAKE_NV(":status", "200 OK"), + MAKE_NV("access-control-allow-origin", "*"), + MAKE_NV("cache-control", "private, max-age=0, must-revalidate"), + MAKE_NV("content-length", "76073"), + MAKE_NV("content-type", "text/html"), + MAKE_NV("date", "Sat, 27 Jul 2013 06:22:12 GMT"), + MAKE_NV("expires", "Sat, 27 Jul 2013 06:22:12 GMT"), + MAKE_NV("server", "Apache"), + MAKE_NV("vary", "foobar"), + MAKE_NV("via", "1.1 alphabravo (squid/3.x.x), 1.1 nghttpx"), + MAKE_NV("x-cache", "MISS from alphabravo"), + MAKE_NV("x-cache-action", "MISS"), + MAKE_NV("x-cache-age", "0"), + MAKE_NV("x-cache-lookup", "MISS from alphabravo:3128"), + MAKE_NV("x-lb-nocache", "true"), + }; + nghttp2_nv nv2[] = { + MAKE_NV(":status", "304 Not Modified"), + MAKE_NV("age", "0"), + MAKE_NV("cache-control", "max-age=56682045"), + MAKE_NV("content-type", "text/css"), + MAKE_NV("date", "Sat, 27 Jul 2013 06:22:12 GMT"), + MAKE_NV("expires", "Thu, 14 May 2015 07:22:57 GMT"), + MAKE_NV("last-modified", "Tue, 14 May 2013 07:22:15 GMT"), + MAKE_NV("vary", "Accept-Encoding"), + MAKE_NV("via", "1.1 alphabravo (squid/3.x.x), 1.1 nghttpx"), + MAKE_NV("x-cache", "HIT from alphabravo"), + MAKE_NV("x-cache-lookup", "HIT from alphabravo:3128")}; + nghttp2_nv nv3[] = { + MAKE_NV(":status", "304 Not Modified"), + MAKE_NV("age", "0"), + MAKE_NV("cache-control", "max-age=56682072"), + MAKE_NV("content-type", "text/css"), + MAKE_NV("date", "Sat, 27 Jul 2013 06:22:12 GMT"), + MAKE_NV("expires", "Thu, 14 May 2015 07:23:24 GMT"), + MAKE_NV("last-modified", "Tue, 14 May 2013 07:22:13 GMT"), + MAKE_NV("vary", "Accept-Encoding"), + MAKE_NV("via", "1.1 alphabravo (squid/3.x.x), 1.1 nghttpx"), + MAKE_NV("x-cache", "HIT from alphabravo"), + MAKE_NV("x-cache-lookup", "HIT from alphabravo:3128"), + }; + nghttp2_nv nv4[] = { + MAKE_NV(":status", "304 Not Modified"), + MAKE_NV("age", "0"), + MAKE_NV("cache-control", "max-age=56682022"), + MAKE_NV("content-type", "text/css"), + MAKE_NV("date", "Sat, 27 Jul 2013 06:22:12 GMT"), + MAKE_NV("expires", "Thu, 14 May 2015 07:22:34 GMT"), + MAKE_NV("last-modified", "Tue, 14 May 2013 07:22:14 GMT"), + MAKE_NV("vary", "Accept-Encoding"), + MAKE_NV("via", "1.1 alphabravo (squid/3.x.x), 1.1 nghttpx"), + MAKE_NV("x-cache", "HIT from alphabravo"), + MAKE_NV("x-cache-lookup", "HIT from alphabravo:3128"), + }; + nghttp2_nv nv5[] = { + MAKE_NV(":status", "304 Not Modified"), + MAKE_NV("age", "0"), + MAKE_NV("cache-control", "max-age=4461139"), + MAKE_NV("content-type", "application/x-javascript"), + MAKE_NV("date", "Sat, 27 Jul 2013 06:22:12 GMT"), + MAKE_NV("expires", "Mon, 16 Sep 2013 21:34:31 GMT"), + MAKE_NV("last-modified", "Thu, 05 May 2011 09:15:59 GMT"), + MAKE_NV("vary", "Accept-Encoding"), + MAKE_NV("via", "1.1 alphabravo (squid/3.x.x), 1.1 nghttpx"), + MAKE_NV("x-cache", "HIT from alphabravo"), + MAKE_NV("x-cache-lookup", "HIT from alphabravo:3128"), + }; + nghttp2_nv nv6[] = { + MAKE_NV(":status", "304 Not Modified"), + MAKE_NV("age", "0"), + MAKE_NV("cache-control", "max-age=18645951"), + MAKE_NV("content-type", "application/x-javascript"), + MAKE_NV("date", "Sat, 27 Jul 2013 06:22:12 GMT"), + MAKE_NV("expires", "Fri, 28 Feb 2014 01:48:03 GMT"), + MAKE_NV("last-modified", "Tue, 12 Jul 2011 16:02:59 GMT"), + MAKE_NV("vary", "Accept-Encoding"), + MAKE_NV("via", "1.1 alphabravo (squid/3.x.x), 1.1 nghttpx"), + MAKE_NV("x-cache", "HIT from alphabravo"), + MAKE_NV("x-cache-lookup", "HIT from alphabravo:3128"), + }; + nghttp2_nv nv7[] = { + MAKE_NV(":status", "304 Not Modified"), + MAKE_NV("age", "0"), + MAKE_NV("cache-control", "max-age=31536000"), + MAKE_NV("content-type", "application/javascript"), + MAKE_NV("date", "Sat, 27 Jul 2013 06:22:12 GMT"), + MAKE_NV("etag", "\"6807-4dc5b54e0dcc0\""), + MAKE_NV("expires", "Wed, 21 May 2014 08:32:17 GMT"), + MAKE_NV("last-modified", "Fri, 10 May 2013 11:18:51 GMT"), + MAKE_NV("via", "1.1 alphabravo (squid/3.x.x), 1.1 nghttpx"), + MAKE_NV("x-cache", "HIT from alphabravo"), + MAKE_NV("x-cache-lookup", "HIT from alphabravo:3128"), + }; + nghttp2_nv nv8[] = { + MAKE_NV(":status", "304 Not Modified"), + MAKE_NV("age", "0"), + MAKE_NV("cache-control", "max-age=31536000"), + MAKE_NV("content-type", "application/javascript"), + MAKE_NV("date", "Sat, 27 Jul 2013 06:22:12 GMT"), + MAKE_NV("etag", "\"41c6-4de7d28585b00\""), + MAKE_NV("expires", "Thu, 12 Jun 2014 10:00:58 GMT"), + MAKE_NV("last-modified", "Thu, 06 Jun 2013 14:30:36 GMT"), + MAKE_NV("via", "1.1 alphabravo (squid/3.x.x), 1.1 nghttpx"), + MAKE_NV("x-cache", "HIT from alphabravo"), + MAKE_NV("x-cache-lookup", "HIT from alphabravo:3128"), + }; + nghttp2_nv nv9[] = { + MAKE_NV(":status", "304 Not Modified"), + MAKE_NV("age", "0"), + MAKE_NV("cache-control", "max-age=31536000"), + MAKE_NV("content-type", "application/javascript"), + MAKE_NV("date", "Sat, 27 Jul 2013 06:22:12 GMT"), + MAKE_NV("etag", "\"19d6e-4dc5b35a541c0\""), + MAKE_NV("expires", "Wed, 21 May 2014 08:32:18 GMT"), + MAKE_NV("last-modified", "Fri, 10 May 2013 11:10:07 GMT"), + MAKE_NV("via", "1.1 alphabravo (squid/3.x.x), 1.1 nghttpx"), + MAKE_NV("x-cache", "HIT from alphabravo"), + MAKE_NV("x-cache-lookup", "HIT from alphabravo:3128"), + }; + nghttp2_nv nv10[] = { + MAKE_NV(":status", "304 Not Modified"), + MAKE_NV("age", "0"), + MAKE_NV("cache-control", "max-age=56682045"), + MAKE_NV("content-type", "text/css"), + MAKE_NV("date", "Sat, 27 Jul 2013 06:22:12 GMT"), + MAKE_NV("expires", "Thu, 14 May 2015 07:22:57 GMT"), + MAKE_NV("last-modified", "Tue, 14 May 2013 07:21:53 GMT"), + MAKE_NV("vary", "Accept-Encoding"), + MAKE_NV("via", "1.1 alphabravo (squid/3.x.x), 1.1 nghttpx"), + MAKE_NV("x-cache", "HIT from alphabravo"), + MAKE_NV("x-cache-lookup", "HIT from alphabravo:3128"), + }; + nghttp2_mem *mem; + + mem = nghttp2_mem_default(); + + nghttp2_hd_deflate_init(&deflater, mem); + nghttp2_hd_inflate_init(&inflater, mem); + + check_deflate_inflate(&deflater, &inflater, nv1, ARRLEN(nv1), mem); + check_deflate_inflate(&deflater, &inflater, nv2, ARRLEN(nv2), mem); + check_deflate_inflate(&deflater, &inflater, nv3, ARRLEN(nv3), mem); + check_deflate_inflate(&deflater, &inflater, nv4, ARRLEN(nv4), mem); + check_deflate_inflate(&deflater, &inflater, nv5, ARRLEN(nv5), mem); + check_deflate_inflate(&deflater, &inflater, nv6, ARRLEN(nv6), mem); + check_deflate_inflate(&deflater, &inflater, nv7, ARRLEN(nv7), mem); + check_deflate_inflate(&deflater, &inflater, nv8, ARRLEN(nv8), mem); + check_deflate_inflate(&deflater, &inflater, nv9, ARRLEN(nv9), mem); + check_deflate_inflate(&deflater, &inflater, nv10, ARRLEN(nv10), mem); + + nghttp2_hd_inflate_free(&inflater); + nghttp2_hd_deflate_free(&deflater); +} + +void test_nghttp2_hd_no_index(void) { + nghttp2_hd_deflater deflater; + nghttp2_hd_inflater inflater; + nghttp2_bufs bufs; + ssize_t blocklen; + nghttp2_nv nva[] = { + MAKE_NV(":method", "GET"), MAKE_NV(":method", "POST"), + MAKE_NV(":path", "/foo"), MAKE_NV("version", "HTTP/1.1"), + MAKE_NV(":method", "GET"), + }; + size_t i; + nva_out out; + int rv; + nghttp2_mem *mem; + + mem = nghttp2_mem_default(); + + /* 1st :method: GET can be indexable, last one is not */ + for (i = 1; i < ARRLEN(nva); ++i) { + nva[i].flags = NGHTTP2_NV_FLAG_NO_INDEX; + } + + frame_pack_bufs_init(&bufs); + + nva_out_init(&out); + + nghttp2_hd_deflate_init(&deflater, mem); + nghttp2_hd_inflate_init(&inflater, mem); + + rv = nghttp2_hd_deflate_hd_bufs(&deflater, &bufs, nva, ARRLEN(nva)); + blocklen = nghttp2_bufs_len(&bufs); + + CU_ASSERT(0 == rv); + CU_ASSERT(blocklen > 0); + CU_ASSERT(blocklen == inflate_hd(&inflater, &out, &bufs, 0, mem)); + + CU_ASSERT(ARRLEN(nva) == out.nvlen); + assert_nv_equal(nva, out.nva, ARRLEN(nva), mem); + + CU_ASSERT(out.nva[0].flags == NGHTTP2_NV_FLAG_NONE); + for (i = 1; i < ARRLEN(nva); ++i) { + CU_ASSERT(out.nva[i].flags == NGHTTP2_NV_FLAG_NO_INDEX); + } + + nva_out_reset(&out, mem); + + nghttp2_bufs_free(&bufs); + nghttp2_hd_inflate_free(&inflater); + nghttp2_hd_deflate_free(&deflater); +} + +void test_nghttp2_hd_deflate_bound(void) { + nghttp2_hd_deflater deflater; + nghttp2_nv nva[] = {MAKE_NV(":method", "GET"), MAKE_NV("alpha", "bravo")}; + nghttp2_bufs bufs; + size_t bound, bound2; + nghttp2_mem *mem; + + mem = nghttp2_mem_default(); + frame_pack_bufs_init(&bufs); + + nghttp2_hd_deflate_init(&deflater, mem); + + bound = nghttp2_hd_deflate_bound(&deflater, nva, ARRLEN(nva)); + + CU_ASSERT(12 + 6 * 2 * 2 + nva[0].namelen + nva[0].valuelen + nva[1].namelen + + nva[1].valuelen == + bound); + + nghttp2_hd_deflate_hd_bufs(&deflater, &bufs, nva, ARRLEN(nva)); + + CU_ASSERT(bound > (size_t)nghttp2_bufs_len(&bufs)); + + bound2 = nghttp2_hd_deflate_bound(&deflater, nva, ARRLEN(nva)); + + CU_ASSERT(bound == bound2); + + nghttp2_bufs_free(&bufs); + nghttp2_hd_deflate_free(&deflater); +} + +void test_nghttp2_hd_public_api(void) { + nghttp2_hd_deflater *deflater; + nghttp2_hd_inflater *inflater; + nghttp2_nv nva[] = {MAKE_NV("alpha", "bravo"), MAKE_NV("charlie", "delta")}; + uint8_t buf[4096]; + size_t buflen; + ssize_t blocklen; + nghttp2_bufs bufs; + nghttp2_mem *mem; + + mem = nghttp2_mem_default(); + + CU_ASSERT(0 == nghttp2_hd_deflate_new(&deflater, 4096)); + CU_ASSERT(0 == nghttp2_hd_inflate_new(&inflater)); + + buflen = nghttp2_hd_deflate_bound(deflater, nva, ARRLEN(nva)); + + blocklen = nghttp2_hd_deflate_hd(deflater, buf, buflen, nva, ARRLEN(nva)); + + CU_ASSERT(blocklen > 0); + + nghttp2_bufs_wrap_init(&bufs, buf, blocklen, mem); + bufs.head->buf.last += blocklen; + + CU_ASSERT(blocklen == inflate_hd(inflater, NULL, &bufs, 0, mem)); + + nghttp2_bufs_wrap_free(&bufs); + + nghttp2_hd_inflate_del(inflater); + nghttp2_hd_deflate_del(deflater); + + /* See NGHTTP2_ERR_INSUFF_BUFSIZE */ + CU_ASSERT(0 == nghttp2_hd_deflate_new(&deflater, 4096)); + + blocklen = + nghttp2_hd_deflate_hd(deflater, buf, blocklen - 1, nva, ARRLEN(nva)); + + CU_ASSERT(NGHTTP2_ERR_INSUFF_BUFSIZE == blocklen); + + nghttp2_hd_deflate_del(deflater); +} + +static size_t encode_length(uint8_t *buf, uint64_t n, size_t prefix) { + size_t k = (1 << prefix) - 1; + size_t len = 0; + *buf &= ~k; + if (n >= k) { + *buf++ |= k; + n -= k; + ++len; + } else { + *buf++ |= n; + return 1; + } + do { + ++len; + if (n >= 128) { + *buf++ = (1 << 7) | (n & 0x7f); + n >>= 7; + } else { + *buf++ = (uint8_t)n; + break; + } + } while (n); + return len; +} + +void test_nghttp2_hd_decode_length(void) { + uint32_t out; + size_t shift; + int final; + uint8_t buf[16]; + uint8_t *bufp; + size_t len; + ssize_t rv; + size_t i; + + memset(buf, 0, sizeof(buf)); + len = encode_length(buf, UINT32_MAX, 7); + + rv = nghttp2_hd_decode_length(&out, &shift, &final, 0, 0, buf, buf + len, 7); + + CU_ASSERT((ssize_t)len == rv); + CU_ASSERT(0 != final); + CU_ASSERT(UINT32_MAX == out); + + /* Make sure that we can decode integer if we feed 1 byte at a + time */ + out = 0; + shift = 0; + final = 0; + bufp = buf; + + for (i = 0; i < len; ++i, ++bufp) { + rv = nghttp2_hd_decode_length(&out, &shift, &final, out, shift, bufp, + bufp + 1, 7); + + CU_ASSERT(rv == 1); + + if (final) { + break; + } + } + + CU_ASSERT(i == len - 1); + CU_ASSERT(0 != final); + CU_ASSERT(UINT32_MAX == out); + + /* Check overflow case */ + memset(buf, 0, sizeof(buf)); + len = encode_length(buf, 1ll << 32, 7); + + rv = nghttp2_hd_decode_length(&out, &shift, &final, 0, 0, buf, buf + len, 7); + + CU_ASSERT(-1 == rv); +} + +void test_nghttp2_hd_huff_encode(void) { + int rv; + ssize_t len; + nghttp2_bufs bufs, outbufs; + nghttp2_hd_huff_decode_context ctx; + const uint8_t t1[] = {22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, + 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0}; + + frame_pack_bufs_init(&bufs); + frame_pack_bufs_init(&outbufs); + + rv = nghttp2_hd_huff_encode(&bufs, t1, sizeof(t1)); + + CU_ASSERT(rv == 0); + + nghttp2_hd_huff_decode_context_init(&ctx); + + len = nghttp2_hd_huff_decode(&ctx, &outbufs, bufs.cur->buf.pos, + nghttp2_bufs_len(&bufs), 1); + + CU_ASSERT(nghttp2_bufs_len(&bufs) == len); + CU_ASSERT((ssize_t)sizeof(t1) == nghttp2_bufs_len(&outbufs)); + + CU_ASSERT(0 == memcmp(t1, outbufs.cur->buf.pos, sizeof(t1))); + + nghttp2_bufs_free(&bufs); + nghttp2_bufs_free(&outbufs); +} diff --git a/tests/nghttp2_hd_test.h b/tests/nghttp2_hd_test.h new file mode 100644 index 0000000..e41fad7 --- /dev/null +++ b/tests/nghttp2_hd_test.h @@ -0,0 +1,47 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2013 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGHTTP2_HD_TEST_H +#define NGHTTP2_HD_TEST_H + +void test_nghttp2_hd_deflate(void); +void test_nghttp2_hd_deflate_same_indexed_repr(void); +void test_nghttp2_hd_inflate_indexed(void); +void test_nghttp2_hd_inflate_indname_noinc(void); +void test_nghttp2_hd_inflate_indname_inc(void); +void test_nghttp2_hd_inflate_indname_inc_eviction(void); +void test_nghttp2_hd_inflate_newname_noinc(void); +void test_nghttp2_hd_inflate_newname_inc(void); +void test_nghttp2_hd_inflate_clearall_inc(void); +void test_nghttp2_hd_inflate_zero_length_huffman(void); +void test_nghttp2_hd_ringbuf_reserve(void); +void test_nghttp2_hd_change_table_size(void); +void test_nghttp2_hd_deflate_inflate(void); +void test_nghttp2_hd_no_index(void); +void test_nghttp2_hd_deflate_bound(void); +void test_nghttp2_hd_public_api(void); +void test_nghttp2_hd_decode_length(void); +void test_nghttp2_hd_huff_encode(void); + +#endif /* NGHTTP2_HD_TEST_H */ diff --git a/tests/nghttp2_helper_test.c b/tests/nghttp2_helper_test.c new file mode 100644 index 0000000..b29e67b --- /dev/null +++ b/tests/nghttp2_helper_test.c @@ -0,0 +1,169 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2013 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "nghttp2_helper_test.h" + +#include + +#include "nghttp2_helper.h" + +void test_nghttp2_adjust_local_window_size(void) { + int32_t local_window_size = 100; + int32_t recv_window_size = 50; + int32_t recv_reduction = 0; + int32_t delta; + + delta = 0; + CU_ASSERT(0 == nghttp2_adjust_local_window_size(&local_window_size, + &recv_window_size, + &recv_reduction, &delta)); + CU_ASSERT(100 == local_window_size); + CU_ASSERT(50 == recv_window_size); + CU_ASSERT(0 == recv_reduction); + CU_ASSERT(0 == delta); + + delta = 49; + CU_ASSERT(0 == nghttp2_adjust_local_window_size(&local_window_size, + &recv_window_size, + &recv_reduction, &delta)); + CU_ASSERT(100 == local_window_size); + CU_ASSERT(1 == recv_window_size); + CU_ASSERT(0 == recv_reduction); + CU_ASSERT(49 == delta); + + delta = 1; + CU_ASSERT(0 == nghttp2_adjust_local_window_size(&local_window_size, + &recv_window_size, + &recv_reduction, &delta)); + CU_ASSERT(100 == local_window_size); + CU_ASSERT(0 == recv_window_size); + CU_ASSERT(0 == recv_reduction); + CU_ASSERT(1 == delta); + + delta = 1; + CU_ASSERT(0 == nghttp2_adjust_local_window_size(&local_window_size, + &recv_window_size, + &recv_reduction, &delta)); + CU_ASSERT(101 == local_window_size); + CU_ASSERT(0 == recv_window_size); + CU_ASSERT(0 == recv_reduction); + CU_ASSERT(1 == delta); + + delta = -1; + CU_ASSERT(0 == nghttp2_adjust_local_window_size(&local_window_size, + &recv_window_size, + &recv_reduction, &delta)); + CU_ASSERT(100 == local_window_size); + CU_ASSERT(-1 == recv_window_size); + CU_ASSERT(1 == recv_reduction); + CU_ASSERT(0 == delta); + + delta = 1; + CU_ASSERT(0 == nghttp2_adjust_local_window_size(&local_window_size, + &recv_window_size, + &recv_reduction, &delta)); + CU_ASSERT(101 == local_window_size); + CU_ASSERT(0 == recv_window_size); + CU_ASSERT(0 == recv_reduction); + CU_ASSERT(0 == delta); + + delta = 100; + CU_ASSERT(0 == nghttp2_adjust_local_window_size(&local_window_size, + &recv_window_size, + &recv_reduction, &delta)); + CU_ASSERT(201 == local_window_size); + CU_ASSERT(0 == recv_window_size); + CU_ASSERT(0 == recv_reduction); + CU_ASSERT(100 == delta); + + delta = -3; + CU_ASSERT(0 == nghttp2_adjust_local_window_size(&local_window_size, + &recv_window_size, + &recv_reduction, &delta)); + CU_ASSERT(198 == local_window_size); + CU_ASSERT(-3 == recv_window_size); + CU_ASSERT(3 == recv_reduction); + CU_ASSERT(0 == delta); + + recv_window_size += 3; + + delta = 3; + CU_ASSERT(0 == nghttp2_adjust_local_window_size(&local_window_size, + &recv_window_size, + &recv_reduction, &delta)); + CU_ASSERT(201 == local_window_size); + CU_ASSERT(3 == recv_window_size); + CU_ASSERT(0 == recv_reduction); + CU_ASSERT(0 == delta); + + local_window_size = 100; + recv_window_size = 50; + recv_reduction = 0; + delta = INT32_MAX; + CU_ASSERT(NGHTTP2_ERR_FLOW_CONTROL == + nghttp2_adjust_local_window_size(&local_window_size, + &recv_window_size, &recv_reduction, + &delta)); + CU_ASSERT(100 == local_window_size); + CU_ASSERT(50 == recv_window_size); + CU_ASSERT(0 == recv_reduction); + CU_ASSERT(INT32_MAX == delta); + + delta = INT32_MIN; + CU_ASSERT(NGHTTP2_ERR_FLOW_CONTROL == + nghttp2_adjust_local_window_size(&local_window_size, + &recv_window_size, &recv_reduction, + &delta)); + CU_ASSERT(100 == local_window_size); + CU_ASSERT(50 == recv_window_size); + CU_ASSERT(0 == recv_reduction); + CU_ASSERT(INT32_MIN == delta); +} + +#define check_header_name(S) \ + nghttp2_check_header_name((const uint8_t *)S, sizeof(S) - 1) + +void test_nghttp2_check_header_name(void) { + CU_ASSERT(check_header_name(":path")); + CU_ASSERT(check_header_name("path")); + CU_ASSERT(check_header_name("!#$%&'*+-.^_`|~")); + CU_ASSERT(!check_header_name(":PATH")); + CU_ASSERT(!check_header_name("path:")); + CU_ASSERT(!check_header_name("")); + CU_ASSERT(!check_header_name(":")); +} + +#define check_header_value(S) \ + nghttp2_check_header_value((const uint8_t *)S, sizeof(S) - 1) + +void test_nghttp2_check_header_value(void) { + uint8_t goodval[] = {'a', 'b', 0x80u, 'c', 0xffu, 'd', '\t', ' '}; + uint8_t badval1[] = {'a', 0x1fu, 'b'}; + uint8_t badval2[] = {'a', 0x7fu, 'b'}; + + CU_ASSERT(check_header_value(" !|}~")); + CU_ASSERT(check_header_value(goodval)); + CU_ASSERT(!check_header_value(badval1)); + CU_ASSERT(!check_header_value(badval2)); +} diff --git a/tests/nghttp2_helper_test.h b/tests/nghttp2_helper_test.h new file mode 100644 index 0000000..173fde6 --- /dev/null +++ b/tests/nghttp2_helper_test.h @@ -0,0 +1,32 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2013 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGHTTP2_HELPER_TEST_H +#define NGHTTP2_HELPER_TEST_H + +void test_nghttp2_adjust_local_window_size(void); +void test_nghttp2_check_header_name(void); +void test_nghttp2_check_header_value(void); + +#endif /* NGHTTP2_HELPER_TEST_H */ diff --git a/tests/nghttp2_map_test.c b/tests/nghttp2_map_test.c new file mode 100644 index 0000000..c2ec825 --- /dev/null +++ b/tests/nghttp2_map_test.c @@ -0,0 +1,176 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "nghttp2_map_test.h" + +#include + +#include "nghttp2_map.h" + +typedef struct strentry { + nghttp2_map_entry map_entry; + const char *str; +} strentry; + +static void strentry_init(strentry *entry, key_type key, const char *str) { + nghttp2_map_entry_init(&entry->map_entry, key); + entry->str = str; +} + +void test_nghttp2_map(void) { + strentry foo, FOO, bar, baz, shrubbery; + nghttp2_map map; + nghttp2_map_init(&map, nghttp2_mem_default()); + + strentry_init(&foo, 1, "foo"); + strentry_init(&FOO, 1, "FOO"); + strentry_init(&bar, 2, "bar"); + strentry_init(&baz, 3, "baz"); + strentry_init(&shrubbery, 4, "shrubbery"); + + CU_ASSERT(0 == nghttp2_map_insert(&map, &foo.map_entry)); + CU_ASSERT(strcmp("foo", ((strentry *)nghttp2_map_find(&map, 1))->str) == 0); + CU_ASSERT(1 == nghttp2_map_size(&map)); + + CU_ASSERT(NGHTTP2_ERR_INVALID_ARGUMENT == + nghttp2_map_insert(&map, &FOO.map_entry)); + + CU_ASSERT(1 == nghttp2_map_size(&map)); + CU_ASSERT(strcmp("foo", ((strentry *)nghttp2_map_find(&map, 1))->str) == 0); + + CU_ASSERT(0 == nghttp2_map_insert(&map, &bar.map_entry)); + CU_ASSERT(2 == nghttp2_map_size(&map)); + + CU_ASSERT(0 == nghttp2_map_insert(&map, &baz.map_entry)); + CU_ASSERT(3 == nghttp2_map_size(&map)); + + CU_ASSERT(0 == nghttp2_map_insert(&map, &shrubbery.map_entry)); + CU_ASSERT(4 == nghttp2_map_size(&map)); + + CU_ASSERT(strcmp("baz", ((strentry *)nghttp2_map_find(&map, 3))->str) == 0); + + nghttp2_map_remove(&map, 3); + CU_ASSERT(3 == nghttp2_map_size(&map)); + CU_ASSERT(NULL == nghttp2_map_find(&map, 3)); + + nghttp2_map_remove(&map, 1); + CU_ASSERT(2 == nghttp2_map_size(&map)); + CU_ASSERT(NULL == nghttp2_map_find(&map, 1)); + + /* Erasing non-existent entry */ + nghttp2_map_remove(&map, 1); + CU_ASSERT(2 == nghttp2_map_size(&map)); + CU_ASSERT(NULL == nghttp2_map_find(&map, 1)); + + CU_ASSERT(strcmp("bar", ((strentry *)nghttp2_map_find(&map, 2))->str) == 0); + CU_ASSERT(strcmp("shrubbery", ((strentry *)nghttp2_map_find(&map, 4))->str) == + 0); + + nghttp2_map_free(&map); +} + +static void shuffle(int *a, int n) { + int i; + for (i = n - 1; i >= 1; --i) { + size_t j = (int)((double)(i + 1) * rand() / (RAND_MAX + 1.0)); + int t = a[j]; + a[j] = a[i]; + a[i] = t; + } +} + +static int eachfun(nghttp2_map_entry *entry _U_, void *ptr _U_) { return 0; } + +#define NUM_ENT 6000 +strentry arr[NUM_ENT]; +int order[NUM_ENT]; + +void test_nghttp2_map_functional(void) { + nghttp2_map map; + int i; + + nghttp2_map_init(&map, nghttp2_mem_default()); + for (i = 0; i < NUM_ENT; ++i) { + strentry_init(&arr[i], i + 1, "foo"); + order[i] = i + 1; + } + /* insertion */ + shuffle(order, NUM_ENT); + for (i = 0; i < NUM_ENT; ++i) { + CU_ASSERT(0 == nghttp2_map_insert(&map, &arr[order[i] - 1].map_entry)); + } + /* traverse */ + nghttp2_map_each(&map, eachfun, NULL); + /* find */ + shuffle(order, NUM_ENT); + for (i = 0; i < NUM_ENT; ++i) { + nghttp2_map_find(&map, order[i]); + } + /* remove */ + shuffle(order, NUM_ENT); + for (i = 0; i < NUM_ENT; ++i) { + CU_ASSERT(0 == nghttp2_map_remove(&map, order[i])); + } + + /* each_free (but no op function for testing purpose) */ + for (i = 0; i < NUM_ENT; ++i) { + strentry_init(&arr[i], i + 1, "foo"); + } + /* insert once again */ + for (i = 0; i < NUM_ENT; ++i) { + CU_ASSERT(0 == nghttp2_map_insert(&map, &arr[i].map_entry)); + } + nghttp2_map_each_free(&map, eachfun, NULL); + nghttp2_map_free(&map); +} + +static int entry_free(nghttp2_map_entry *entry, void *ptr) { + nghttp2_mem *mem = ptr; + + mem->free(entry, NULL); + return 0; +} + +void test_nghttp2_map_each_free(void) { + nghttp2_mem *mem = nghttp2_mem_default(); + strentry *foo = mem->malloc(sizeof(strentry), NULL), + *bar = mem->malloc(sizeof(strentry), NULL), + *baz = mem->malloc(sizeof(strentry), NULL), + *shrubbery = mem->malloc(sizeof(strentry), NULL); + nghttp2_map map; + nghttp2_map_init(&map, nghttp2_mem_default()); + + strentry_init(foo, 1, "foo"); + strentry_init(bar, 2, "bar"); + strentry_init(baz, 3, "baz"); + strentry_init(shrubbery, 4, "shrubbery"); + + nghttp2_map_insert(&map, &foo->map_entry); + nghttp2_map_insert(&map, &bar->map_entry); + nghttp2_map_insert(&map, &baz->map_entry); + nghttp2_map_insert(&map, &shrubbery->map_entry); + + nghttp2_map_each_free(&map, entry_free, mem); + nghttp2_map_free(&map); +} diff --git a/tests/nghttp2_map_test.h b/tests/nghttp2_map_test.h new file mode 100644 index 0000000..f0b723b --- /dev/null +++ b/tests/nghttp2_map_test.h @@ -0,0 +1,32 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGHTTP2_MAP_TEST_H +#define NGHTTP2_MAP_TEST_H + +void test_nghttp2_map(void); +void test_nghttp2_map_functional(void); +void test_nghttp2_map_each_free(void); + +#endif /* NGHTTP2_MAP_TEST_H */ diff --git a/tests/nghttp2_npn_test.c b/tests/nghttp2_npn_test.c new file mode 100644 index 0000000..cbd65b7 --- /dev/null +++ b/tests/nghttp2_npn_test.c @@ -0,0 +1,72 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Twist Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "nghttp2_npn_test.h" + +#include + +#include +#include + +static void http2(void) { + const unsigned char p[] = {8, 'h', 't', 't', 'p', '/', '1', '.', '1', 2, + 'h', '2', 6, 's', 'p', 'd', 'y', '/', '3'}; + unsigned char outlen; + unsigned char *out; + CU_ASSERT(1 == nghttp2_select_next_protocol(&out, &outlen, p, sizeof(p))); + CU_ASSERT(NGHTTP2_PROTO_VERSION_ID_LEN == outlen); + CU_ASSERT(memcmp(NGHTTP2_PROTO_VERSION_ID, out, outlen) == 0); +} + +static void http11(void) { + const unsigned char spdy[] = { + 6, 's', 'p', 'd', 'y', '/', '4', 8, 's', 'p', 'd', 'y', '/', + '2', '.', '1', 8, 'h', 't', 't', 'p', '/', '1', '.', '1', + }; + unsigned char outlen; + unsigned char *out; + CU_ASSERT(0 == + nghttp2_select_next_protocol(&out, &outlen, spdy, sizeof(spdy))); + CU_ASSERT(8 == outlen); + CU_ASSERT(memcmp("http/1.1", out, outlen) == 0); +} + +static void no_overlap(void) { + const unsigned char spdy[] = { + 6, 's', 'p', 'd', 'y', '/', '4', 8, 's', 'p', 'd', 'y', '/', + '2', '.', '1', 8, 'h', 't', 't', 'p', '/', '1', '.', '0', + }; + unsigned char outlen = 0; + unsigned char *out = NULL; + CU_ASSERT(-1 == + nghttp2_select_next_protocol(&out, &outlen, spdy, sizeof(spdy))); + CU_ASSERT(0 == outlen); + CU_ASSERT(NULL == out); +} + +void test_nghttp2_npn(void) { + http2(); + http11(); + no_overlap(); +} diff --git a/tests/nghttp2_npn_test.h b/tests/nghttp2_npn_test.h new file mode 100644 index 0000000..e806f3a --- /dev/null +++ b/tests/nghttp2_npn_test.h @@ -0,0 +1,30 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Twist Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGHTTP2_NPN_TEST_H +#define NGHTTP2_NPN_TEST_H + +void test_nghttp2_npn(void); + +#endif /* NGHTTP2_NPN_TEST_H */ diff --git a/tests/nghttp2_pq_test.c b/tests/nghttp2_pq_test.c new file mode 100644 index 0000000..a88f75b --- /dev/null +++ b/tests/nghttp2_pq_test.c @@ -0,0 +1,123 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "nghttp2_pq_test.h" + +#include + +#include "nghttp2_pq.h" + +static int pq_less(const void *lhs, const void *rhs) { + return strcmp(lhs, rhs) < 0; +} + +void test_nghttp2_pq(void) { + int i; + nghttp2_pq pq; + nghttp2_pq_init(&pq, pq_less, nghttp2_mem_default()); + CU_ASSERT(nghttp2_pq_empty(&pq)); + CU_ASSERT(0 == nghttp2_pq_size(&pq)); + CU_ASSERT(0 == nghttp2_pq_push(&pq, (void *)"foo")); + CU_ASSERT(0 == nghttp2_pq_empty(&pq)); + CU_ASSERT(1 == nghttp2_pq_size(&pq)); + CU_ASSERT(strcmp("foo", nghttp2_pq_top(&pq)) == 0); + CU_ASSERT(0 == nghttp2_pq_push(&pq, (void *)"bar")); + CU_ASSERT(strcmp("bar", nghttp2_pq_top(&pq)) == 0); + CU_ASSERT(0 == nghttp2_pq_push(&pq, (void *)"baz")); + CU_ASSERT(strcmp("bar", nghttp2_pq_top(&pq)) == 0); + CU_ASSERT(0 == nghttp2_pq_push(&pq, (void *)"C")); + CU_ASSERT(4 == nghttp2_pq_size(&pq)); + CU_ASSERT(strcmp("C", nghttp2_pq_top(&pq)) == 0); + nghttp2_pq_pop(&pq); + CU_ASSERT(3 == nghttp2_pq_size(&pq)); + CU_ASSERT(strcmp("bar", nghttp2_pq_top(&pq)) == 0); + nghttp2_pq_pop(&pq); + CU_ASSERT(strcmp("baz", nghttp2_pq_top(&pq)) == 0); + nghttp2_pq_pop(&pq); + CU_ASSERT(strcmp("foo", nghttp2_pq_top(&pq)) == 0); + nghttp2_pq_pop(&pq); + CU_ASSERT(nghttp2_pq_empty(&pq)); + CU_ASSERT(0 == nghttp2_pq_size(&pq)); + CU_ASSERT(NULL == nghttp2_pq_top(&pq)); + + /* Add bunch of entry to see realloc works */ + for (i = 0; i < 10000; ++i) { + CU_ASSERT(0 == nghttp2_pq_push(&pq, (void *)"foo")); + CU_ASSERT((size_t)(i + 1) == nghttp2_pq_size(&pq)); + } + for (i = 10000; i > 0; --i) { + CU_ASSERT(NULL != nghttp2_pq_top(&pq)); + nghttp2_pq_pop(&pq); + CU_ASSERT((size_t)(i - 1) == nghttp2_pq_size(&pq)); + } + + nghttp2_pq_free(&pq); +} + +typedef struct { + int key; + int val; +} node; + +static int node_less(const void *lhs, const void *rhs) { + node *ln = (node *)lhs; + node *rn = (node *)rhs; + return ln->key < rn->key; +} + +static int node_update(void *item, void *arg _U_) { + node *nd = (node *)item; + if ((nd->key % 2) == 0) { + nd->key *= -1; + return 1; + } else { + return 0; + } +} + +void test_nghttp2_pq_update(void) { + nghttp2_pq pq; + node nodes[10]; + int i; + node *nd; + int ans[] = {-8, -6, -4, -2, 0, 1, 3, 5, 7, 9}; + + nghttp2_pq_init(&pq, node_less, nghttp2_mem_default()); + + for (i = 0; i < (int)(sizeof(nodes) / sizeof(nodes[0])); ++i) { + nodes[i].key = i; + nodes[i].val = i; + nghttp2_pq_push(&pq, &nodes[i]); + } + + nghttp2_pq_update(&pq, node_update, NULL); + + for (i = 0; i < (int)(sizeof(nodes) / sizeof(nodes[0])); ++i) { + nd = nghttp2_pq_top(&pq); + CU_ASSERT(ans[i] == nd->key); + nghttp2_pq_pop(&pq); + } + + nghttp2_pq_free(&pq); +} diff --git a/tests/nghttp2_pq_test.h b/tests/nghttp2_pq_test.h new file mode 100644 index 0000000..7818194 --- /dev/null +++ b/tests/nghttp2_pq_test.h @@ -0,0 +1,31 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGHTTP2_PQ_TEST_H +#define NGHTTP2_PQ_TEST_H + +void test_nghttp2_pq(void); +void test_nghttp2_pq_update(void); + +#endif /* NGHTTP2_PQ_TEST_H */ diff --git a/tests/nghttp2_queue_test.c b/tests/nghttp2_queue_test.c new file mode 100644 index 0000000..76cc98a --- /dev/null +++ b/tests/nghttp2_queue_test.c @@ -0,0 +1,48 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "nghttp2_queue_test.h" + +#include + +#include "nghttp2_queue.h" + +void test_nghttp2_queue(void) { + int ints[] = {1, 2, 3, 4, 5}; + int i; + nghttp2_queue queue; + nghttp2_queue_init(&queue); + CU_ASSERT(nghttp2_queue_empty(&queue)); + for (i = 0; i < 5; ++i) { + nghttp2_queue_push(&queue, &ints[i]); + CU_ASSERT_EQUAL(ints[0], *(int *)(nghttp2_queue_front(&queue))); + CU_ASSERT(!nghttp2_queue_empty(&queue)); + } + for (i = 0; i < 5; ++i) { + CU_ASSERT_EQUAL(ints[i], *(int *)(nghttp2_queue_front(&queue))); + nghttp2_queue_pop(&queue); + } + CU_ASSERT(nghttp2_queue_empty(&queue)); + nghttp2_queue_free(&queue); +} diff --git a/tests/nghttp2_queue_test.h b/tests/nghttp2_queue_test.h new file mode 100644 index 0000000..944697e --- /dev/null +++ b/tests/nghttp2_queue_test.h @@ -0,0 +1,30 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGHTTP2_QUEUE_TEST_H +#define NGHTTP2_QUEUE_TEST_H + +void test_nghttp2_queue(void); + +#endif /* NGHTTP2_QUEUE_TEST_H */ diff --git a/tests/nghttp2_session_test.c b/tests/nghttp2_session_test.c new file mode 100644 index 0000000..7783e17 --- /dev/null +++ b/tests/nghttp2_session_test.c @@ -0,0 +1,8125 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2013 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "nghttp2_session_test.h" + +#include +#include + +#include + +#include "nghttp2_session.h" +#include "nghttp2_stream.h" +#include "nghttp2_net.h" +#include "nghttp2_helper.h" +#include "nghttp2_test_helper.h" +#include "nghttp2_priority_spec.h" + +extern int nghttp2_enable_strict_preface; + +#define OB_CTRL(ITEM) nghttp2_outbound_item_get_ctrl_frame(ITEM) +#define OB_CTRL_TYPE(ITEM) nghttp2_outbound_item_get_ctrl_frame_type(ITEM) +#define OB_DATA(ITEM) nghttp2_outbound_item_get_data_frame(ITEM) + +typedef struct { + uint8_t buf[65535]; + size_t length; +} accumulator; + +typedef struct { + uint8_t data[8192]; + uint8_t *datamark; + uint8_t *datalimit; + size_t feedseq[8192]; + size_t seqidx; +} scripted_data_feed; + +typedef struct { + accumulator *acc; + scripted_data_feed *df; + int frame_recv_cb_called, invalid_frame_recv_cb_called; + uint8_t recv_frame_type; + int frame_send_cb_called; + uint8_t sent_frame_type; + int frame_not_send_cb_called; + uint8_t not_sent_frame_type; + int not_sent_error; + int stream_close_cb_called; + uint32_t stream_close_error_code; + size_t data_source_length; + int32_t stream_id; + size_t block_count; + int data_chunk_recv_cb_called; + const nghttp2_frame *frame; + size_t fixed_sendlen; + int header_cb_called; + int begin_headers_cb_called; + nghttp2_nv nv; + size_t data_chunk_len; + size_t padlen; + int begin_frame_cb_called; +} my_user_data; + +static const nghttp2_nv reqnv[] = { + MAKE_NV(":method", "GET"), MAKE_NV(":path", "/"), + MAKE_NV(":scheme", "https"), MAKE_NV(":authority", "localhost"), +}; + +static const nghttp2_nv resnv[] = { + MAKE_NV(":status", "200"), +}; + +static const nghttp2_nv trailernv[] = { + // from http://tools.ietf.org/html/rfc6249#section-7 + MAKE_NV("digest", "SHA-256=" + "MWVkMWQxYTRiMzk5MDQ0MzI3NGU5NDEyZTk5OWY1ZGFmNzgyZTJlODYz" + "YjRjYzFhOTlmNTQwYzI2M2QwM2U2MQ=="), +}; + +static void scripted_data_feed_init2(scripted_data_feed *df, + nghttp2_bufs *bufs) { + nghttp2_buf_chain *ci; + nghttp2_buf *buf; + uint8_t *ptr; + size_t len; + + memset(df, 0, sizeof(scripted_data_feed)); + ptr = df->data; + len = 0; + + for (ci = bufs->head; ci; ci = ci->next) { + buf = &ci->buf; + ptr = nghttp2_cpymem(ptr, buf->pos, nghttp2_buf_len(buf)); + len += nghttp2_buf_len(buf); + } + + df->datamark = df->data; + df->datalimit = df->data + len; + df->feedseq[0] = len; +} + +static ssize_t null_send_callback(nghttp2_session *session _U_, + const uint8_t *data _U_, size_t len, + int flags _U_, void *user_data _U_) { + return len; +} + +static ssize_t fail_send_callback(nghttp2_session *session _U_, + const uint8_t *data _U_, size_t len _U_, + int flags _U_, void *user_data _U_) { + return NGHTTP2_ERR_CALLBACK_FAILURE; +} + +static ssize_t fixed_bytes_send_callback(nghttp2_session *session _U_, + const uint8_t *data _U_, size_t len, + int flags _U_, void *user_data) { + size_t fixed_sendlen = ((my_user_data *)user_data)->fixed_sendlen; + return fixed_sendlen < len ? fixed_sendlen : len; +} + +static ssize_t scripted_recv_callback(nghttp2_session *session _U_, + uint8_t *data, size_t len, int flags _U_, + void *user_data) { + scripted_data_feed *df = ((my_user_data *)user_data)->df; + size_t wlen = df->feedseq[df->seqidx] > len ? len : df->feedseq[df->seqidx]; + memcpy(data, df->datamark, wlen); + df->datamark += wlen; + df->feedseq[df->seqidx] -= wlen; + if (df->feedseq[df->seqidx] == 0) { + ++df->seqidx; + } + return wlen; +} + +static ssize_t eof_recv_callback(nghttp2_session *session _U_, + uint8_t *data _U_, size_t len _U_, + int flags _U_, void *user_data _U_) { + return NGHTTP2_ERR_EOF; +} + +static ssize_t accumulator_send_callback(nghttp2_session *session _U_, + const uint8_t *buf, size_t len, + int flags _U_, void *user_data) { + accumulator *acc = ((my_user_data *)user_data)->acc; + assert(acc->length + len < sizeof(acc->buf)); + memcpy(acc->buf + acc->length, buf, len); + acc->length += len; + return len; +} + +static int on_begin_frame_callback(nghttp2_session *session _U_, + const nghttp2_frame_hd *hd _U_, + void *user_data) { + my_user_data *ud = (my_user_data *)user_data; + ++ud->begin_frame_cb_called; + return 0; +} + +static int on_frame_recv_callback(nghttp2_session *session _U_, + const nghttp2_frame *frame, void *user_data) { + my_user_data *ud = (my_user_data *)user_data; + ++ud->frame_recv_cb_called; + ud->recv_frame_type = frame->hd.type; + return 0; +} + +static int on_invalid_frame_recv_callback(nghttp2_session *session _U_, + const nghttp2_frame *frame _U_, + int lib_error_code _U_, + void *user_data) { + my_user_data *ud = (my_user_data *)user_data; + ++ud->invalid_frame_recv_cb_called; + return 0; +} + +static int on_frame_send_callback(nghttp2_session *session _U_, + const nghttp2_frame *frame, void *user_data) { + my_user_data *ud = (my_user_data *)user_data; + ++ud->frame_send_cb_called; + ud->sent_frame_type = frame->hd.type; + return 0; +} + +static int on_frame_not_send_callback(nghttp2_session *session _U_, + const nghttp2_frame *frame, int lib_error, + void *user_data) { + my_user_data *ud = (my_user_data *)user_data; + ++ud->frame_not_send_cb_called; + ud->not_sent_frame_type = frame->hd.type; + ud->not_sent_error = lib_error; + return 0; +} + +static int on_data_chunk_recv_callback(nghttp2_session *session _U_, + uint8_t flags _U_, int32_t stream_id _U_, + const uint8_t *data _U_, size_t len, + void *user_data) { + my_user_data *ud = (my_user_data *)user_data; + ++ud->data_chunk_recv_cb_called; + ud->data_chunk_len = len; + return 0; +} + +static int pause_on_data_chunk_recv_callback(nghttp2_session *session _U_, + uint8_t flags _U_, + int32_t stream_id _U_, + const uint8_t *data _U_, + size_t len _U_, void *user_data) { + my_user_data *ud = (my_user_data *)user_data; + ++ud->data_chunk_recv_cb_called; + return NGHTTP2_ERR_PAUSE; +} + +static ssize_t select_padding_callback(nghttp2_session *session _U_, + const nghttp2_frame *frame, + size_t max_payloadlen, void *user_data) { + my_user_data *ud = (my_user_data *)user_data; + return nghttp2_min(max_payloadlen, frame->hd.length + ud->padlen); +} + +static ssize_t too_large_data_source_length_callback( + nghttp2_session *session _U_, uint8_t frame_type _U_, int32_t stream_id _U_, + int32_t session_remote_window_size _U_, + int32_t stream_remote_window_size _U_, uint32_t remote_max_frame_size _U_, + void *user_data _U_) { + return NGHTTP2_MAX_FRAME_SIZE_MAX + 1; +} + +static ssize_t smallest_length_data_source_length_callback( + nghttp2_session *session _U_, uint8_t frame_type _U_, int32_t stream_id _U_, + int32_t session_remote_window_size _U_, + int32_t stream_remote_window_size _U_, uint32_t remote_max_frame_size _U_, + void *user_data _U_) { + return 1; +} + +static ssize_t fixed_length_data_source_read_callback( + nghttp2_session *session _U_, int32_t stream_id _U_, uint8_t *buf _U_, + size_t len, uint32_t *data_flags, nghttp2_data_source *source _U_, + void *user_data) { + my_user_data *ud = (my_user_data *)user_data; + size_t wlen; + if (len < ud->data_source_length) { + wlen = len; + } else { + wlen = ud->data_source_length; + } + ud->data_source_length -= wlen; + if (ud->data_source_length == 0) { + *data_flags |= NGHTTP2_DATA_FLAG_EOF; + } + return wlen; +} + +static ssize_t temporal_failure_data_source_read_callback( + nghttp2_session *session _U_, int32_t stream_id _U_, uint8_t *buf _U_, + size_t len _U_, uint32_t *data_flags _U_, nghttp2_data_source *source _U_, + void *user_data _U_) { + return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; +} + +static ssize_t fail_data_source_read_callback(nghttp2_session *session _U_, + int32_t stream_id _U_, + uint8_t *buf _U_, size_t len _U_, + uint32_t *data_flags _U_, + nghttp2_data_source *source _U_, + void *user_data _U_) { + return NGHTTP2_ERR_CALLBACK_FAILURE; +} + +static ssize_t no_end_stream_data_source_read_callback( + nghttp2_session *session _U_, int32_t stream_id _U_, uint8_t *buf _U_, + size_t len _U_, uint32_t *data_flags, nghttp2_data_source *source _U_, + void *user_data _U_) { + *data_flags |= NGHTTP2_DATA_FLAG_EOF | NGHTTP2_DATA_FLAG_NO_END_STREAM; + return 0; +} + +static ssize_t no_copy_data_source_read_callback( + nghttp2_session *session _U_, int32_t stream_id _U_, uint8_t *buf _U_, + size_t len, uint32_t *data_flags, nghttp2_data_source *source _U_, + void *user_data) { + my_user_data *ud = (my_user_data *)user_data; + size_t wlen; + if (len < ud->data_source_length) { + wlen = len; + } else { + wlen = ud->data_source_length; + } + + ud->data_source_length -= wlen; + + *data_flags |= NGHTTP2_DATA_FLAG_NO_COPY; + + if (ud->data_source_length == 0) { + *data_flags |= NGHTTP2_DATA_FLAG_EOF; + } + return wlen; +} + +static int send_data_callback(nghttp2_session *session _U_, + nghttp2_frame *frame, const uint8_t *framehd, + size_t length, nghttp2_data_source *source _U_, + void *user_data) { + accumulator *acc = ((my_user_data *)user_data)->acc; + + memcpy(acc->buf + acc->length, framehd, NGHTTP2_FRAME_HDLEN); + acc->length += NGHTTP2_FRAME_HDLEN; + + if (frame->data.padlen) { + *(acc->buf + acc->length++) = frame->data.padlen - 1; + } + + acc->length += length; + + if (frame->data.padlen) { + acc->length += frame->data.padlen - 1; + } + + return 0; +} + +/* static void no_stream_user_data_stream_close_callback */ +/* (nghttp2_session *session, */ +/* int32_t stream_id, */ +/* nghttp2_error_code error_code, */ +/* void *user_data) */ +/* { */ +/* my_user_data* my_data = (my_user_data*)user_data; */ +/* ++my_data->stream_close_cb_called; */ +/* } */ + +static ssize_t block_count_send_callback(nghttp2_session *session _U_, + const uint8_t *data _U_, size_t len, + int flags _U_, void *user_data) { + my_user_data *ud = (my_user_data *)user_data; + ssize_t r; + if (ud->block_count == 0) { + r = NGHTTP2_ERR_WOULDBLOCK; + } else { + --ud->block_count; + r = len; + } + return r; +} + +static int on_header_callback(nghttp2_session *session _U_, + const nghttp2_frame *frame, const uint8_t *name, + size_t namelen, const uint8_t *value, + size_t valuelen, uint8_t flags _U_, + void *user_data) { + my_user_data *ud = (my_user_data *)user_data; + ++ud->header_cb_called; + ud->nv.name = (uint8_t *)name; + ud->nv.namelen = namelen; + ud->nv.value = (uint8_t *)value; + ud->nv.valuelen = valuelen; + + ud->frame = frame; + return 0; +} + +static int pause_on_header_callback(nghttp2_session *session, + const nghttp2_frame *frame, + const uint8_t *name, size_t namelen, + const uint8_t *value, size_t valuelen, + uint8_t flags, void *user_data) { + on_header_callback(session, frame, name, namelen, value, valuelen, flags, + user_data); + return NGHTTP2_ERR_PAUSE; +} + +static int temporal_failure_on_header_callback( + nghttp2_session *session, const nghttp2_frame *frame, const uint8_t *name, + size_t namelen, const uint8_t *value, size_t valuelen, uint8_t flags, + void *user_data) { + on_header_callback(session, frame, name, namelen, value, valuelen, flags, + user_data); + return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; +} + +static int on_begin_headers_callback(nghttp2_session *session _U_, + const nghttp2_frame *frame _U_, + void *user_data) { + my_user_data *ud = (my_user_data *)user_data; + ++ud->begin_headers_cb_called; + return 0; +} + +static int temporal_failure_on_begin_headers_callback( + nghttp2_session *session, const nghttp2_frame *frame, void *user_data) { + on_begin_headers_callback(session, frame, user_data); + return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; +} + +static ssize_t defer_data_source_read_callback(nghttp2_session *session _U_, + int32_t stream_id _U_, + uint8_t *buf _U_, size_t len _U_, + uint32_t *data_flags _U_, + nghttp2_data_source *source _U_, + void *user_data _U_) { + return NGHTTP2_ERR_DEFERRED; +} + +static int on_stream_close_callback(nghttp2_session *session _U_, + int32_t stream_id _U_, + nghttp2_error_code error_code _U_, + void *user_data) { + my_user_data *my_data = (my_user_data *)user_data; + ++my_data->stream_close_cb_called; + my_data->stream_close_error_code = error_code; + + return 0; +} + +static nghttp2_settings_entry *dup_iv(const nghttp2_settings_entry *iv, + size_t niv) { + return nghttp2_frame_iv_copy(iv, niv, nghttp2_mem_default()); +} + +static nghttp2_priority_spec pri_spec_default = {0, NGHTTP2_DEFAULT_WEIGHT, 0}; + +void test_nghttp2_session_recv(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + scripted_data_feed df; + my_user_data user_data; + nghttp2_bufs bufs; + ssize_t framelen; + nghttp2_frame frame; + ssize_t i; + nghttp2_outbound_item *item; + nghttp2_nv *nva; + ssize_t nvlen; + nghttp2_hd_deflater deflater; + int rv; + nghttp2_mem *mem; + + mem = nghttp2_mem_default(); + frame_pack_bufs_init(&bufs); + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.send_callback = null_send_callback; + callbacks.recv_callback = scripted_recv_callback; + callbacks.on_frame_recv_callback = on_frame_recv_callback; + callbacks.on_begin_frame_callback = on_begin_frame_callback; + + user_data.df = &df; + + nghttp2_session_server_new(&session, &callbacks, &user_data); + nghttp2_hd_deflate_init(&deflater, mem); + + nvlen = ARRLEN(reqnv); + nghttp2_nv_array_copy(&nva, reqnv, nvlen, mem); + nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_END_HEADERS, 1, + NGHTTP2_HCAT_HEADERS, NULL, nva, nvlen); + rv = nghttp2_frame_pack_headers(&bufs, &frame.headers, &deflater); + + CU_ASSERT(0 == rv); + + scripted_data_feed_init2(&df, &bufs); + + framelen = nghttp2_bufs_len(&bufs); + + /* Send 1 byte per each read */ + for (i = 0; i < framelen; ++i) { + df.feedseq[i] = 1; + } + + nghttp2_frame_headers_free(&frame.headers, mem); + + user_data.frame_recv_cb_called = 0; + user_data.begin_frame_cb_called = 0; + + while ((ssize_t)df.seqidx < framelen) { + CU_ASSERT(0 == nghttp2_session_recv(session)); + } + CU_ASSERT(1 == user_data.frame_recv_cb_called); + CU_ASSERT(1 == user_data.begin_frame_cb_called); + + nghttp2_bufs_reset(&bufs); + + /* Receive PRIORITY */ + nghttp2_frame_priority_init(&frame.priority, 5, &pri_spec_default); + + rv = nghttp2_frame_pack_priority(&bufs, &frame.priority); + + CU_ASSERT(0 == rv); + + nghttp2_frame_priority_free(&frame.priority); + + scripted_data_feed_init2(&df, &bufs); + + user_data.frame_recv_cb_called = 0; + user_data.begin_frame_cb_called = 0; + + CU_ASSERT(0 == nghttp2_session_recv(session)); + CU_ASSERT(1 == user_data.frame_recv_cb_called); + CU_ASSERT(1 == user_data.begin_frame_cb_called); + + nghttp2_bufs_reset(&bufs); + + nghttp2_hd_deflate_free(&deflater); + nghttp2_session_del(session); + + /* Some tests for frame too large */ + nghttp2_session_server_new(&session, &callbacks, &user_data); + + /* Receive PING with too large payload */ + nghttp2_frame_ping_init(&frame.ping, NGHTTP2_FLAG_NONE, NULL); + + rv = nghttp2_frame_pack_ping(&bufs, &frame.ping); + + CU_ASSERT(0 == rv); + + /* Add extra 16 bytes */ + nghttp2_bufs_seek_last_present(&bufs); + assert(nghttp2_buf_len(&bufs.cur->buf) >= 16); + + bufs.cur->buf.last += 16; + nghttp2_put_uint32be( + bufs.cur->buf.pos, + (uint32_t)(((frame.hd.length + 16) << 8) + bufs.cur->buf.pos[3])); + + nghttp2_frame_ping_free(&frame.ping); + + scripted_data_feed_init2(&df, &bufs); + user_data.frame_recv_cb_called = 0; + user_data.begin_frame_cb_called = 0; + + CU_ASSERT(0 == nghttp2_session_recv(session)); + CU_ASSERT(0 == user_data.frame_recv_cb_called); + CU_ASSERT(0 == user_data.begin_frame_cb_called); + + item = nghttp2_session_get_next_ob_item(session); + CU_ASSERT(NGHTTP2_GOAWAY == item->frame.hd.type); + CU_ASSERT(NGHTTP2_FRAME_SIZE_ERROR == item->frame.goaway.error_code); + CU_ASSERT(0 == nghttp2_session_send(session)); + + nghttp2_bufs_free(&bufs); + nghttp2_session_del(session); +} + +void test_nghttp2_session_recv_invalid_stream_id(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + scripted_data_feed df; + my_user_data user_data; + nghttp2_bufs bufs; + nghttp2_frame frame; + nghttp2_hd_deflater deflater; + int rv; + nghttp2_mem *mem; + nghttp2_nv *nva; + size_t nvlen; + + mem = nghttp2_mem_default(); + frame_pack_bufs_init(&bufs); + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.recv_callback = scripted_recv_callback; + callbacks.on_invalid_frame_recv_callback = on_invalid_frame_recv_callback; + + user_data.df = &df; + user_data.invalid_frame_recv_cb_called = 0; + nghttp2_session_server_new(&session, &callbacks, &user_data); + nghttp2_hd_deflate_init(&deflater, mem); + + nvlen = ARRLEN(reqnv); + nghttp2_nv_array_copy(&nva, reqnv, nvlen, mem); + nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_END_HEADERS, 2, + NGHTTP2_HCAT_HEADERS, NULL, nva, nvlen); + rv = nghttp2_frame_pack_headers(&bufs, &frame.headers, &deflater); + + CU_ASSERT(0 == rv); + CU_ASSERT(nghttp2_bufs_len(&bufs) > 0); + + scripted_data_feed_init2(&df, &bufs); + nghttp2_frame_headers_free(&frame.headers, mem); + + CU_ASSERT(0 == nghttp2_session_recv(session)); + CU_ASSERT(1 == user_data.invalid_frame_recv_cb_called); + + nghttp2_bufs_free(&bufs); + nghttp2_hd_deflate_free(&deflater); + nghttp2_session_del(session); +} + +void test_nghttp2_session_recv_invalid_frame(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + scripted_data_feed df; + my_user_data user_data; + nghttp2_bufs bufs; + nghttp2_frame frame; + nghttp2_nv *nva; + ssize_t nvlen; + nghttp2_hd_deflater deflater; + int rv; + nghttp2_mem *mem; + + mem = nghttp2_mem_default(); + frame_pack_bufs_init(&bufs); + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.recv_callback = scripted_recv_callback; + callbacks.send_callback = null_send_callback; + callbacks.on_frame_send_callback = on_frame_send_callback; + + user_data.df = &df; + user_data.frame_send_cb_called = 0; + nghttp2_session_server_new(&session, &callbacks, &user_data); + nghttp2_hd_deflate_init(&deflater, mem); + nvlen = ARRLEN(reqnv); + nghttp2_nv_array_copy(&nva, reqnv, nvlen, mem); + nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_END_HEADERS, 1, + NGHTTP2_HCAT_HEADERS, NULL, nva, nvlen); + rv = nghttp2_frame_pack_headers(&bufs, &frame.headers, &deflater); + + CU_ASSERT(0 == rv); + CU_ASSERT(nghttp2_bufs_len(&bufs) > 0); + + scripted_data_feed_init2(&df, &bufs); + + CU_ASSERT(0 == nghttp2_session_recv(session)); + CU_ASSERT(0 == nghttp2_session_send(session)); + CU_ASSERT(0 == user_data.frame_send_cb_called); + + /* Receive exactly same bytes of HEADERS is treated as error, because it has + * pseudo headers and without END_STREAM flag set */ + scripted_data_feed_init2(&df, &bufs); + + CU_ASSERT(0 == nghttp2_session_recv(session)); + CU_ASSERT(0 == nghttp2_session_send(session)); + CU_ASSERT(1 == user_data.frame_send_cb_called); + CU_ASSERT(NGHTTP2_RST_STREAM == user_data.sent_frame_type); + + nghttp2_bufs_free(&bufs); + nghttp2_frame_headers_free(&frame.headers, mem); + + nghttp2_hd_deflate_free(&deflater); + nghttp2_session_del(session); +} + +void test_nghttp2_session_recv_eof(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.send_callback = null_send_callback; + callbacks.recv_callback = eof_recv_callback; + + nghttp2_session_client_new(&session, &callbacks, NULL); + CU_ASSERT(NGHTTP2_ERR_EOF == nghttp2_session_recv(session)); + + nghttp2_session_del(session); +} + +void test_nghttp2_session_recv_data(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + my_user_data ud; + uint8_t data[8092]; + ssize_t rv; + nghttp2_outbound_item *item; + nghttp2_stream *stream; + nghttp2_frame_hd hd; + int i; + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.send_callback = null_send_callback; + callbacks.on_data_chunk_recv_callback = on_data_chunk_recv_callback; + callbacks.on_frame_recv_callback = on_frame_recv_callback; + + nghttp2_session_client_new(&session, &callbacks, &ud); + + /* Create DATA frame with length 4KiB */ + memset(data, 0, sizeof(data)); + hd.length = 4096; + hd.type = NGHTTP2_DATA; + hd.flags = NGHTTP2_FLAG_NONE; + hd.stream_id = 1; + nghttp2_frame_pack_frame_hd(data, &hd); + + /* stream 1 is not opened, so it must be responded with connection + error. This is not mandated by the spec */ + ud.data_chunk_recv_cb_called = 0; + ud.frame_recv_cb_called = 0; + rv = nghttp2_session_mem_recv(session, data, NGHTTP2_FRAME_HDLEN + 4096); + CU_ASSERT(NGHTTP2_FRAME_HDLEN + 4096 == rv); + + CU_ASSERT(0 == ud.data_chunk_recv_cb_called); + CU_ASSERT(0 == ud.frame_recv_cb_called); + item = nghttp2_session_get_next_ob_item(session); + CU_ASSERT(NGHTTP2_GOAWAY == item->frame.hd.type); + + nghttp2_session_del(session); + + nghttp2_session_client_new(&session, &callbacks, &ud); + + /* Create stream 1 with CLOSING state. DATA is ignored. */ + stream = nghttp2_session_open_stream(session, 1, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, + NGHTTP2_STREAM_CLOSING, NULL); + /* Set initial window size 16383 to check stream flow control, + isolating it from the conneciton flow control */ + stream->local_window_size = 16383; + + ud.data_chunk_recv_cb_called = 0; + ud.frame_recv_cb_called = 0; + rv = nghttp2_session_mem_recv(session, data, NGHTTP2_FRAME_HDLEN + 4096); + CU_ASSERT(NGHTTP2_FRAME_HDLEN + 4096 == rv); + + CU_ASSERT(0 == ud.data_chunk_recv_cb_called); + CU_ASSERT(0 == ud.frame_recv_cb_called); + item = nghttp2_session_get_next_ob_item(session); + CU_ASSERT(NULL == item); + + /* This is normal case. DATA is acceptable. */ + stream->state = NGHTTP2_STREAM_OPENED; + + ud.data_chunk_recv_cb_called = 0; + ud.frame_recv_cb_called = 0; + rv = nghttp2_session_mem_recv(session, data, NGHTTP2_FRAME_HDLEN + 4096); + CU_ASSERT(NGHTTP2_FRAME_HDLEN + 4096 == rv); + + CU_ASSERT(1 == ud.data_chunk_recv_cb_called); + CU_ASSERT(1 == ud.frame_recv_cb_called); + + CU_ASSERT(NULL == nghttp2_session_get_next_ob_item(session)); + + ud.data_chunk_recv_cb_called = 0; + ud.frame_recv_cb_called = 0; + rv = nghttp2_session_mem_recv(session, data, NGHTTP2_FRAME_HDLEN + 4096); + CU_ASSERT(NGHTTP2_FRAME_HDLEN + 4096 == rv); + + /* Now we got data more than initial-window-size / 2, WINDOW_UPDATE + must be queued */ + CU_ASSERT(1 == ud.data_chunk_recv_cb_called); + CU_ASSERT(1 == ud.frame_recv_cb_called); + item = nghttp2_session_get_next_ob_item(session); + CU_ASSERT(NGHTTP2_WINDOW_UPDATE == item->frame.hd.type); + CU_ASSERT(1 == item->frame.window_update.hd.stream_id); + CU_ASSERT(0 == nghttp2_session_send(session)); + + /* Set initial window size to 1MiB, so that we can check connection + flow control individually */ + stream->local_window_size = 1 << 20; + /* Connection flow control takes into account DATA which is received + in the error condition. We have received 4096 * 4 bytes of + DATA. Additional 4 DATA frames, connection flow control will kick + in. */ + for (i = 0; i < 5; ++i) { + rv = nghttp2_session_mem_recv(session, data, NGHTTP2_FRAME_HDLEN + 4096); + CU_ASSERT(NGHTTP2_FRAME_HDLEN + 4096 == rv); + } + item = nghttp2_session_get_next_ob_item(session); + CU_ASSERT(NGHTTP2_WINDOW_UPDATE == item->frame.hd.type); + CU_ASSERT(0 == item->frame.window_update.hd.stream_id); + CU_ASSERT(0 == nghttp2_session_send(session)); + + /* Reception of DATA with stream ID = 0 causes connection error */ + hd.length = 4096; + hd.type = NGHTTP2_DATA; + hd.flags = NGHTTP2_FLAG_NONE; + hd.stream_id = 0; + nghttp2_frame_pack_frame_hd(data, &hd); + + ud.data_chunk_recv_cb_called = 0; + ud.frame_recv_cb_called = 0; + rv = nghttp2_session_mem_recv(session, data, NGHTTP2_FRAME_HDLEN + 4096); + CU_ASSERT(NGHTTP2_FRAME_HDLEN + 4096 == rv); + + CU_ASSERT(0 == ud.data_chunk_recv_cb_called); + CU_ASSERT(0 == ud.frame_recv_cb_called); + item = nghttp2_session_get_next_ob_item(session); + CU_ASSERT(NGHTTP2_GOAWAY == item->frame.hd.type); + CU_ASSERT(NGHTTP2_PROTOCOL_ERROR == item->frame.goaway.error_code); + + nghttp2_session_del(session); +} + +void test_nghttp2_session_recv_continuation(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + nghttp2_nv *nva; + size_t nvlen; + nghttp2_frame frame; + nghttp2_bufs bufs; + nghttp2_buf *buf; + ssize_t rv; + my_user_data ud; + nghttp2_hd_deflater deflater; + uint8_t data[1024]; + size_t datalen; + nghttp2_frame_hd cont_hd; + nghttp2_priority_spec pri_spec; + nghttp2_mem *mem; + + mem = nghttp2_mem_default(); + frame_pack_bufs_init(&bufs); + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.on_header_callback = on_header_callback; + callbacks.on_begin_headers_callback = on_begin_headers_callback; + callbacks.on_begin_frame_callback = on_begin_frame_callback; + + nghttp2_session_server_new(&session, &callbacks, &ud); + + nghttp2_hd_deflate_init(&deflater, mem); + + /* Make 1 HEADERS and insert CONTINUATION header */ + nvlen = ARRLEN(reqnv); + nghttp2_nv_array_copy(&nva, reqnv, nvlen, mem); + nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_NONE, 1, + NGHTTP2_HCAT_HEADERS, NULL, nva, nvlen); + rv = nghttp2_frame_pack_headers(&bufs, &frame.headers, &deflater); + + CU_ASSERT(0 == rv); + CU_ASSERT(nghttp2_bufs_len(&bufs) > 0); + + /* make sure that all data is in the first buf */ + buf = &bufs.head->buf; + assert(nghttp2_bufs_len(&bufs) == nghttp2_buf_len(buf)); + + nghttp2_frame_headers_free(&frame.headers, mem); + + /* HEADERS's payload is 1 byte */ + memcpy(data, buf->pos, NGHTTP2_FRAME_HDLEN + 1); + datalen = NGHTTP2_FRAME_HDLEN + 1; + buf->pos += NGHTTP2_FRAME_HDLEN + 1; + + nghttp2_put_uint32be(data, (1 << 8) + data[3]); + + /* First CONTINUATION, 2 bytes */ + nghttp2_frame_hd_init(&cont_hd, 2, NGHTTP2_CONTINUATION, NGHTTP2_FLAG_NONE, + 1); + + nghttp2_frame_pack_frame_hd(data + datalen, &cont_hd); + datalen += NGHTTP2_FRAME_HDLEN; + + memcpy(data + datalen, buf->pos, cont_hd.length); + datalen += cont_hd.length; + buf->pos += cont_hd.length; + + /* Second CONTINUATION, rest of the bytes */ + nghttp2_frame_hd_init(&cont_hd, nghttp2_buf_len(buf), NGHTTP2_CONTINUATION, + NGHTTP2_FLAG_END_HEADERS, 1); + + nghttp2_frame_pack_frame_hd(data + datalen, &cont_hd); + datalen += NGHTTP2_FRAME_HDLEN; + + memcpy(data + datalen, buf->pos, cont_hd.length); + datalen += cont_hd.length; + buf->pos += cont_hd.length; + + CU_ASSERT(0 == nghttp2_buf_len(buf)); + + ud.header_cb_called = 0; + ud.begin_frame_cb_called = 0; + + rv = nghttp2_session_mem_recv(session, data, datalen); + CU_ASSERT((ssize_t)datalen == rv); + CU_ASSERT(4 == ud.header_cb_called); + CU_ASSERT(3 == ud.begin_frame_cb_called); + + nghttp2_hd_deflate_free(&deflater); + nghttp2_session_del(session); + + /* Expecting CONTINUATION, but get the other frame */ + nghttp2_session_server_new(&session, &callbacks, &ud); + + nghttp2_hd_deflate_init(&deflater, mem); + + /* HEADERS without END_HEADERS flag */ + nvlen = ARRLEN(reqnv); + nghttp2_nv_array_copy(&nva, reqnv, nvlen, mem); + nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_NONE, 1, + NGHTTP2_HCAT_HEADERS, NULL, nva, nvlen); + nghttp2_bufs_reset(&bufs); + rv = nghttp2_frame_pack_headers(&bufs, &frame.headers, &deflater); + + CU_ASSERT(0 == rv); + CU_ASSERT(nghttp2_bufs_len(&bufs) > 0); + + nghttp2_frame_headers_free(&frame.headers, mem); + + /* make sure that all data is in the first buf */ + buf = &bufs.head->buf; + assert(nghttp2_bufs_len(&bufs) == nghttp2_buf_len(buf)); + + memcpy(data, buf->pos, nghttp2_buf_len(buf)); + datalen = nghttp2_buf_len(buf); + + /* Followed by PRIORITY */ + nghttp2_priority_spec_default_init(&pri_spec); + + nghttp2_frame_priority_init(&frame.priority, 1, &pri_spec); + nghttp2_bufs_reset(&bufs); + + rv = nghttp2_frame_pack_priority(&bufs, &frame.priority); + + CU_ASSERT(0 == rv); + CU_ASSERT(nghttp2_bufs_len(&bufs) > 0); + + memcpy(data + datalen, buf->pos, nghttp2_buf_len(buf)); + datalen += nghttp2_buf_len(buf); + + ud.begin_headers_cb_called = 0; + rv = nghttp2_session_mem_recv(session, data, datalen); + CU_ASSERT((ssize_t)datalen == rv); + + CU_ASSERT(1 == ud.begin_headers_cb_called); + CU_ASSERT(NGHTTP2_GOAWAY == + nghttp2_session_get_next_ob_item(session)->frame.hd.type); + + nghttp2_bufs_free(&bufs); + nghttp2_hd_deflate_free(&deflater); + nghttp2_session_del(session); +} + +void test_nghttp2_session_recv_headers_with_priority(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + nghttp2_nv *nva; + size_t nvlen; + nghttp2_frame frame; + nghttp2_bufs bufs; + nghttp2_buf *buf; + ssize_t rv; + my_user_data ud; + nghttp2_hd_deflater deflater; + nghttp2_outbound_item *item; + nghttp2_priority_spec pri_spec; + nghttp2_stream *stream; + nghttp2_mem *mem; + + mem = nghttp2_mem_default(); + frame_pack_bufs_init(&bufs); + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.on_frame_recv_callback = on_frame_recv_callback; + + nghttp2_session_server_new(&session, &callbacks, &ud); + + nghttp2_hd_deflate_init(&deflater, mem); + + open_stream(session, 1); + + /* With NGHTTP2_FLAG_PRIORITY without exclusive flag set */ + nvlen = ARRLEN(reqnv); + nghttp2_nv_array_copy(&nva, reqnv, nvlen, mem); + + nghttp2_priority_spec_init(&pri_spec, 1, 99, 0); + + nghttp2_frame_headers_init(&frame.headers, + NGHTTP2_FLAG_END_HEADERS | NGHTTP2_FLAG_PRIORITY, + 3, NGHTTP2_HCAT_HEADERS, &pri_spec, nva, nvlen); + + rv = nghttp2_frame_pack_headers(&bufs, &frame.headers, &deflater); + + CU_ASSERT(0 == rv); + CU_ASSERT(nghttp2_bufs_len(&bufs) > 0); + + nghttp2_frame_headers_free(&frame.headers, mem); + + buf = &bufs.head->buf; + assert(nghttp2_bufs_len(&bufs) == nghttp2_buf_len(buf)); + + ud.frame_recv_cb_called = 0; + + rv = nghttp2_session_mem_recv(session, buf->pos, nghttp2_buf_len(buf)); + + CU_ASSERT(nghttp2_buf_len(buf) == rv); + CU_ASSERT(1 == ud.frame_recv_cb_called); + + stream = nghttp2_session_get_stream(session, 3); + + CU_ASSERT(99 == stream->weight); + CU_ASSERT(1 == stream->dep_prev->stream_id); + + nghttp2_bufs_reset(&bufs); + + /* With NGHTTP2_FLAG_PRIORITY, but cut last 1 byte to make it + invalid. */ + nvlen = ARRLEN(reqnv); + nghttp2_nv_array_copy(&nva, reqnv, nvlen, mem); + + nghttp2_priority_spec_init(&pri_spec, 0, 99, 0); + + nghttp2_frame_headers_init(&frame.headers, + NGHTTP2_FLAG_END_HEADERS | NGHTTP2_FLAG_PRIORITY, + 5, NGHTTP2_HCAT_HEADERS, &pri_spec, nva, nvlen); + + rv = nghttp2_frame_pack_headers(&bufs, &frame.headers, &deflater); + + CU_ASSERT(0 == rv); + CU_ASSERT(nghttp2_bufs_len(&bufs) > NGHTTP2_FRAME_HDLEN + 5); + + nghttp2_frame_headers_free(&frame.headers, mem); + + buf = &bufs.head->buf; + /* Make payload shorter than required length to store priority + group */ + nghttp2_put_uint32be(buf->pos, (4 << 8) + buf->pos[3]); + + ud.frame_recv_cb_called = 0; + + rv = nghttp2_session_mem_recv(session, buf->pos, nghttp2_buf_len(buf)); + + CU_ASSERT(nghttp2_buf_len(buf) == rv); + CU_ASSERT(0 == ud.frame_recv_cb_called); + + stream = nghttp2_session_get_stream(session, 5); + + CU_ASSERT(NULL == stream); + + item = nghttp2_session_get_next_ob_item(session); + CU_ASSERT(NULL != item); + CU_ASSERT(NGHTTP2_GOAWAY == item->frame.hd.type); + CU_ASSERT(NGHTTP2_FRAME_SIZE_ERROR == item->frame.goaway.error_code); + + nghttp2_bufs_reset(&bufs); + + nghttp2_hd_deflate_free(&deflater); + nghttp2_session_del(session); + + /* Check dep_stream_id == stream_id */ + nghttp2_session_server_new(&session, &callbacks, &ud); + + nghttp2_hd_deflate_init(&deflater, mem); + + nvlen = ARRLEN(reqnv); + nghttp2_nv_array_copy(&nva, reqnv, nvlen, mem); + + nghttp2_priority_spec_init(&pri_spec, 1, 0, 0); + + nghttp2_frame_headers_init(&frame.headers, + NGHTTP2_FLAG_END_HEADERS | NGHTTP2_FLAG_PRIORITY, + 1, NGHTTP2_HCAT_HEADERS, &pri_spec, nva, nvlen); + + rv = nghttp2_frame_pack_headers(&bufs, &frame.headers, &deflater); + + CU_ASSERT(0 == rv); + CU_ASSERT(nghttp2_bufs_len(&bufs) > 0); + + nghttp2_frame_headers_free(&frame.headers, mem); + + buf = &bufs.head->buf; + assert(nghttp2_bufs_len(&bufs) == nghttp2_buf_len(buf)); + + ud.frame_recv_cb_called = 0; + + rv = nghttp2_session_mem_recv(session, buf->pos, nghttp2_buf_len(buf)); + + CU_ASSERT(nghttp2_buf_len(buf) == rv); + CU_ASSERT(0 == ud.frame_recv_cb_called); + + stream = nghttp2_session_get_stream(session, 1); + + CU_ASSERT(NULL == stream); + + item = nghttp2_session_get_next_ob_item(session); + CU_ASSERT(NULL != item); + CU_ASSERT(NGHTTP2_GOAWAY == item->frame.hd.type); + CU_ASSERT(NGHTTP2_PROTOCOL_ERROR == item->frame.goaway.error_code); + + nghttp2_bufs_reset(&bufs); + + nghttp2_bufs_free(&bufs); + nghttp2_hd_deflate_free(&deflater); + nghttp2_session_del(session); +} + +void test_nghttp2_session_recv_premature_headers(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + nghttp2_bufs bufs; + nghttp2_buf *buf; + ssize_t rv; + my_user_data ud; + nghttp2_hd_deflater deflater; + nghttp2_outbound_item *item; + nghttp2_mem *mem; + uint32_t payloadlen; + + mem = nghttp2_mem_default(); + frame_pack_bufs_init(&bufs); + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.send_callback = null_send_callback; + + nghttp2_session_server_new(&session, &callbacks, &ud); + + nghttp2_hd_deflate_init(&deflater, mem); + + rv = pack_headers(&bufs, &deflater, 1, NGHTTP2_FLAG_END_HEADERS, reqnv, + ARRLEN(reqnv), mem); + + buf = &bufs.head->buf; + /* Intentionally feed payload cutting last 1 byte off */ + payloadlen = nghttp2_get_uint32(buf->pos) >> 8; + nghttp2_put_uint32be(buf->pos, ((payloadlen - 1) << 8) + buf->pos[3]); + rv = nghttp2_session_mem_recv(session, buf->pos, nghttp2_buf_len(buf) - 1); + + CU_ASSERT(rv == nghttp2_buf_len(buf) - 1); + + item = nghttp2_session_get_next_ob_item(session); + + CU_ASSERT(NULL != item); + CU_ASSERT(NGHTTP2_RST_STREAM == item->frame.hd.type); + CU_ASSERT(NGHTTP2_COMPRESSION_ERROR == item->frame.rst_stream.error_code); + CU_ASSERT(1 == item->frame.hd.stream_id); + CU_ASSERT(0 == nghttp2_session_send(session)); + + nghttp2_bufs_reset(&bufs); + nghttp2_hd_deflate_free(&deflater); + nghttp2_session_del(session); + + /* Test for PUSH_PROMISE */ + nghttp2_session_client_new(&session, &callbacks, &ud); + nghttp2_hd_deflate_init(&deflater, mem); + + nghttp2_session_open_stream(session, 1, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, NGHTTP2_STREAM_OPENING, NULL); + + rv = pack_push_promise(&bufs, &deflater, 1, NGHTTP2_FLAG_END_HEADERS, 2, + reqnv, ARRLEN(reqnv), mem); + + CU_ASSERT(0 == rv); + + buf = &bufs.head->buf; + payloadlen = nghttp2_get_uint32(buf->pos) >> 8; + /* Intentionally feed payload cutting last 1 byte off */ + nghttp2_put_uint32be(buf->pos, ((payloadlen - 1) << 8) + buf->pos[3]); + rv = nghttp2_session_mem_recv(session, buf->pos, nghttp2_buf_len(buf) - 1); + + CU_ASSERT(rv == nghttp2_buf_len(buf) - 1); + + item = nghttp2_session_get_next_ob_item(session); + + CU_ASSERT(NULL != item); + CU_ASSERT(NGHTTP2_RST_STREAM == item->frame.hd.type); + CU_ASSERT(NGHTTP2_COMPRESSION_ERROR == item->frame.rst_stream.error_code); + CU_ASSERT(2 == item->frame.hd.stream_id); + CU_ASSERT(0 == nghttp2_session_send(session)); + + nghttp2_hd_deflate_free(&deflater); + nghttp2_session_del(session); + nghttp2_bufs_free(&bufs); +} + +void test_nghttp2_session_recv_unknown_frame(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + my_user_data ud; + uint8_t data[16384]; + size_t datalen; + nghttp2_frame_hd hd; + ssize_t rv; + + nghttp2_frame_hd_init(&hd, 16000, 99, NGHTTP2_FLAG_NONE, 0); + + nghttp2_frame_pack_frame_hd(data, &hd); + datalen = NGHTTP2_FRAME_HDLEN + hd.length; + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.on_frame_recv_callback = on_frame_recv_callback; + + nghttp2_session_server_new(&session, &callbacks, &ud); + + ud.frame_recv_cb_called = 0; + + /* Unknown frame must be ignored */ + rv = nghttp2_session_mem_recv(session, data, datalen); + + CU_ASSERT(rv == (ssize_t)datalen); + CU_ASSERT(0 == ud.frame_recv_cb_called); + CU_ASSERT(NULL == nghttp2_session_get_next_ob_item(session)); + + nghttp2_session_del(session); +} + +void test_nghttp2_session_recv_unexpected_continuation(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + my_user_data ud; + uint8_t data[16384]; + size_t datalen; + nghttp2_frame_hd hd; + ssize_t rv; + nghttp2_outbound_item *item; + + nghttp2_frame_hd_init(&hd, 16000, NGHTTP2_CONTINUATION, + NGHTTP2_FLAG_END_HEADERS, 1); + + nghttp2_frame_pack_frame_hd(data, &hd); + datalen = NGHTTP2_FRAME_HDLEN + hd.length; + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.on_frame_recv_callback = on_frame_recv_callback; + + nghttp2_session_server_new(&session, &callbacks, &ud); + + open_stream(session, 1); + + ud.frame_recv_cb_called = 0; + + /* unexpected CONTINUATION must be treated as connection error */ + rv = nghttp2_session_mem_recv(session, data, datalen); + + CU_ASSERT(rv == (ssize_t)datalen); + CU_ASSERT(0 == ud.frame_recv_cb_called); + + item = nghttp2_session_get_next_ob_item(session); + + CU_ASSERT(NGHTTP2_GOAWAY == item->frame.hd.type); + + nghttp2_session_del(session); +} + +void test_nghttp2_session_recv_settings_header_table_size(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + nghttp2_frame frame; + nghttp2_bufs bufs; + nghttp2_buf *buf; + ssize_t rv; + my_user_data ud; + nghttp2_settings_entry iv[3]; + nghttp2_nv nv = MAKE_NV(":authority", "example.org"); + nghttp2_mem *mem; + + mem = nghttp2_mem_default(); + frame_pack_bufs_init(&bufs); + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.on_frame_recv_callback = on_frame_recv_callback; + callbacks.send_callback = null_send_callback; + + nghttp2_session_client_new(&session, &callbacks, &ud); + + iv[0].settings_id = NGHTTP2_SETTINGS_HEADER_TABLE_SIZE; + iv[0].value = 3000; + + iv[1].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE; + iv[1].value = 16384; + + nghttp2_frame_settings_init(&frame.settings, NGHTTP2_FLAG_NONE, dup_iv(iv, 2), + 2); + + rv = nghttp2_frame_pack_settings(&bufs, &frame.settings); + + CU_ASSERT(0 == rv); + CU_ASSERT(nghttp2_bufs_len(&bufs) > 0); + + nghttp2_frame_settings_free(&frame.settings, mem); + + buf = &bufs.head->buf; + assert(nghttp2_bufs_len(&bufs) == nghttp2_buf_len(buf)); + + ud.frame_recv_cb_called = 0; + + rv = nghttp2_session_mem_recv(session, buf->pos, nghttp2_buf_len(buf)); + + CU_ASSERT(rv == nghttp2_buf_len(buf)); + CU_ASSERT(1 == ud.frame_recv_cb_called); + + CU_ASSERT(3000 == session->remote_settings.header_table_size); + CU_ASSERT(16384 == session->remote_settings.initial_window_size); + + nghttp2_bufs_reset(&bufs); + + /* 2 SETTINGS_HEADER_TABLE_SIZE */ + iv[0].settings_id = NGHTTP2_SETTINGS_HEADER_TABLE_SIZE; + iv[0].value = 3001; + + iv[1].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE; + iv[1].value = 16383; + + iv[2].settings_id = NGHTTP2_SETTINGS_HEADER_TABLE_SIZE; + iv[2].value = 3001; + + nghttp2_frame_settings_init(&frame.settings, NGHTTP2_FLAG_NONE, dup_iv(iv, 3), + 3); + + rv = nghttp2_frame_pack_settings(&bufs, &frame.settings); + + CU_ASSERT(0 == rv); + CU_ASSERT(nghttp2_bufs_len(&bufs) > 0); + + nghttp2_frame_settings_free(&frame.settings, mem); + + buf = &bufs.head->buf; + assert(nghttp2_bufs_len(&bufs) == nghttp2_buf_len(buf)); + + ud.frame_recv_cb_called = 0; + + rv = nghttp2_session_mem_recv(session, buf->pos, nghttp2_buf_len(buf)); + + CU_ASSERT(rv == nghttp2_buf_len(buf)); + CU_ASSERT(1 == ud.frame_recv_cb_called); + + CU_ASSERT(3001 == session->remote_settings.header_table_size); + CU_ASSERT(16383 == session->remote_settings.initial_window_size); + + nghttp2_bufs_reset(&bufs); + + /* 2 SETTINGS_HEADER_TABLE_SIZE; first entry clears dynamic header + table. */ + + nghttp2_submit_request(session, NULL, &nv, 1, NULL, NULL); + nghttp2_session_send(session); + + CU_ASSERT(0 < session->hd_deflater.ctx.hd_table.len); + + iv[0].settings_id = NGHTTP2_SETTINGS_HEADER_TABLE_SIZE; + iv[0].value = 0; + + iv[1].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE; + iv[1].value = 16382; + + iv[2].settings_id = NGHTTP2_SETTINGS_HEADER_TABLE_SIZE; + iv[2].value = 4096; + + nghttp2_frame_settings_init(&frame.settings, NGHTTP2_FLAG_NONE, dup_iv(iv, 3), + 3); + + rv = nghttp2_frame_pack_settings(&bufs, &frame.settings); + + CU_ASSERT(0 == rv); + CU_ASSERT(nghttp2_bufs_len(&bufs) > 0); + + nghttp2_frame_settings_free(&frame.settings, mem); + + buf = &bufs.head->buf; + assert(nghttp2_bufs_len(&bufs) == nghttp2_buf_len(buf)); + + ud.frame_recv_cb_called = 0; + + rv = nghttp2_session_mem_recv(session, buf->pos, nghttp2_buf_len(buf)); + + CU_ASSERT(rv == nghttp2_buf_len(buf)); + CU_ASSERT(1 == ud.frame_recv_cb_called); + + CU_ASSERT(4096 == session->remote_settings.header_table_size); + CU_ASSERT(16382 == session->remote_settings.initial_window_size); + CU_ASSERT(0 == session->hd_deflater.ctx.hd_table.len); + + nghttp2_bufs_reset(&bufs); + + /* 2 SETTINGS_HEADER_TABLE_SIZE; second entry clears dynamic header + table. */ + + nghttp2_submit_request(session, NULL, &nv, 1, NULL, NULL); + nghttp2_session_send(session); + + CU_ASSERT(0 < session->hd_deflater.ctx.hd_table.len); + + iv[0].settings_id = NGHTTP2_SETTINGS_HEADER_TABLE_SIZE; + iv[0].value = 3000; + + iv[1].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE; + iv[1].value = 16381; + + iv[2].settings_id = NGHTTP2_SETTINGS_HEADER_TABLE_SIZE; + iv[2].value = 0; + + nghttp2_frame_settings_init(&frame.settings, NGHTTP2_FLAG_NONE, dup_iv(iv, 3), + 3); + + rv = nghttp2_frame_pack_settings(&bufs, &frame.settings); + + CU_ASSERT(0 == rv); + CU_ASSERT(nghttp2_bufs_len(&bufs) > 0); + + nghttp2_frame_settings_free(&frame.settings, mem); + + buf = &bufs.head->buf; + assert(nghttp2_bufs_len(&bufs) == nghttp2_buf_len(buf)); + + ud.frame_recv_cb_called = 0; + + rv = nghttp2_session_mem_recv(session, buf->pos, nghttp2_buf_len(buf)); + + CU_ASSERT(rv == nghttp2_buf_len(buf)); + CU_ASSERT(1 == ud.frame_recv_cb_called); + + CU_ASSERT(0 == session->remote_settings.header_table_size); + CU_ASSERT(16381 == session->remote_settings.initial_window_size); + CU_ASSERT(0 == session->hd_deflater.ctx.hd_table.len); + + nghttp2_bufs_reset(&bufs); + + nghttp2_bufs_free(&bufs); + nghttp2_session_del(session); +} + +void test_nghttp2_session_recv_too_large_frame_length(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + uint8_t buf[NGHTTP2_FRAME_HDLEN]; + nghttp2_outbound_item *item; + nghttp2_frame_hd hd; + + /* Initial max frame size is NGHTTP2_MAX_FRAME_SIZE_MIN */ + nghttp2_frame_hd_init(&hd, NGHTTP2_MAX_FRAME_SIZE_MIN + 1, NGHTTP2_HEADERS, + NGHTTP2_FLAG_NONE, 1); + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + + nghttp2_session_server_new(&session, &callbacks, NULL); + + nghttp2_frame_pack_frame_hd(buf, &hd); + + CU_ASSERT(sizeof(buf) == nghttp2_session_mem_recv(session, buf, sizeof(buf))); + + item = nghttp2_session_get_next_ob_item(session); + + CU_ASSERT(item != NULL); + CU_ASSERT(NGHTTP2_GOAWAY == item->frame.hd.type); + + nghttp2_session_del(session); +} + +void test_nghttp2_session_continue(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + my_user_data user_data; + const nghttp2_nv nv1[] = {MAKE_NV(":method", "GET"), MAKE_NV(":path", "/")}; + const nghttp2_nv nv2[] = {MAKE_NV("user-agent", "nghttp2/1.0.0"), + MAKE_NV("alpha", "bravo")}; + nghttp2_bufs bufs; + nghttp2_buf *buf; + size_t framelen1, framelen2; + ssize_t rv; + uint8_t buffer[4096]; + nghttp2_buf databuf; + nghttp2_frame frame; + nghttp2_nv *nva; + ssize_t nvlen; + const nghttp2_frame *recv_frame; + nghttp2_frame_hd data_hd; + nghttp2_hd_deflater deflater; + nghttp2_mem *mem; + + mem = nghttp2_mem_default(); + frame_pack_bufs_init(&bufs); + nghttp2_buf_wrap_init(&databuf, buffer, sizeof(buffer)); + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.send_callback = null_send_callback; + callbacks.on_frame_recv_callback = on_frame_recv_callback; + callbacks.on_data_chunk_recv_callback = pause_on_data_chunk_recv_callback; + callbacks.on_header_callback = pause_on_header_callback; + callbacks.on_begin_headers_callback = on_begin_headers_callback; + + nghttp2_session_server_new(&session, &callbacks, &user_data); + /* disable strict HTTP layering checks */ + session->opt_flags |= NGHTTP2_OPTMASK_NO_HTTP_MESSAGING; + + nghttp2_hd_deflate_init(&deflater, mem); + + /* Make 2 HEADERS frames */ + nvlen = ARRLEN(nv1); + nghttp2_nv_array_copy(&nva, nv1, nvlen, mem); + nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_END_HEADERS, 1, + NGHTTP2_HCAT_HEADERS, NULL, nva, nvlen); + rv = nghttp2_frame_pack_headers(&bufs, &frame.headers, &deflater); + + CU_ASSERT(0 == rv); + CU_ASSERT(nghttp2_bufs_len(&bufs) > 0); + + nghttp2_frame_headers_free(&frame.headers, mem); + + buf = &bufs.head->buf; + assert(nghttp2_bufs_len(&bufs) == nghttp2_buf_len(buf)); + + framelen1 = nghttp2_buf_len(buf); + databuf.last = nghttp2_cpymem(databuf.last, buf->pos, nghttp2_buf_len(buf)); + + nvlen = ARRLEN(nv2); + nghttp2_nv_array_copy(&nva, nv2, nvlen, mem); + nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_END_HEADERS, 3, + NGHTTP2_HCAT_HEADERS, NULL, nva, nvlen); + nghttp2_bufs_reset(&bufs); + rv = nghttp2_frame_pack_headers(&bufs, &frame.headers, &deflater); + + CU_ASSERT(0 == rv); + CU_ASSERT(nghttp2_bufs_len(&bufs) > 0); + + nghttp2_frame_headers_free(&frame.headers, mem); + + assert(nghttp2_bufs_len(&bufs) == nghttp2_buf_len(buf)); + + framelen2 = nghttp2_buf_len(buf); + databuf.last = nghttp2_cpymem(databuf.last, buf->pos, nghttp2_buf_len(buf)); + + /* Receive 1st HEADERS and pause */ + user_data.begin_headers_cb_called = 0; + user_data.header_cb_called = 0; + rv = + nghttp2_session_mem_recv(session, databuf.pos, nghttp2_buf_len(&databuf)); + + CU_ASSERT(rv >= 0); + databuf.pos += rv; + + recv_frame = user_data.frame; + CU_ASSERT(NGHTTP2_HEADERS == recv_frame->hd.type); + CU_ASSERT(framelen1 - NGHTTP2_FRAME_HDLEN == recv_frame->hd.length); + + CU_ASSERT(1 == user_data.begin_headers_cb_called); + CU_ASSERT(1 == user_data.header_cb_called); + + CU_ASSERT(nghttp2_nv_equal(&nv1[0], &user_data.nv)); + + /* get 2nd header field */ + user_data.begin_headers_cb_called = 0; + user_data.header_cb_called = 0; + rv = + nghttp2_session_mem_recv(session, databuf.pos, nghttp2_buf_len(&databuf)); + + CU_ASSERT(rv >= 0); + databuf.pos += rv; + + CU_ASSERT(0 == user_data.begin_headers_cb_called); + CU_ASSERT(1 == user_data.header_cb_called); + + CU_ASSERT(nghttp2_nv_equal(&nv1[1], &user_data.nv)); + + /* will call end_headers_callback and receive 2nd HEADERS and pause */ + user_data.begin_headers_cb_called = 0; + user_data.header_cb_called = 0; + rv = + nghttp2_session_mem_recv(session, databuf.pos, nghttp2_buf_len(&databuf)); + + CU_ASSERT(rv >= 0); + databuf.pos += rv; + + recv_frame = user_data.frame; + CU_ASSERT(NGHTTP2_HEADERS == recv_frame->hd.type); + CU_ASSERT(framelen2 - NGHTTP2_FRAME_HDLEN == recv_frame->hd.length); + + CU_ASSERT(1 == user_data.begin_headers_cb_called); + CU_ASSERT(1 == user_data.header_cb_called); + + CU_ASSERT(nghttp2_nv_equal(&nv2[0], &user_data.nv)); + + /* get 2nd header field */ + user_data.begin_headers_cb_called = 0; + user_data.header_cb_called = 0; + rv = + nghttp2_session_mem_recv(session, databuf.pos, nghttp2_buf_len(&databuf)); + + CU_ASSERT(rv >= 0); + databuf.pos += rv; + + CU_ASSERT(0 == user_data.begin_headers_cb_called); + CU_ASSERT(1 == user_data.header_cb_called); + + CU_ASSERT(nghttp2_nv_equal(&nv2[1], &user_data.nv)); + + /* No input data, frame_recv_callback is called */ + user_data.begin_headers_cb_called = 0; + user_data.header_cb_called = 0; + user_data.frame_recv_cb_called = 0; + rv = + nghttp2_session_mem_recv(session, databuf.pos, nghttp2_buf_len(&databuf)); + + CU_ASSERT(rv >= 0); + databuf.pos += rv; + + CU_ASSERT(0 == user_data.begin_headers_cb_called); + CU_ASSERT(0 == user_data.header_cb_called); + CU_ASSERT(1 == user_data.frame_recv_cb_called); + + /* Receive DATA */ + nghttp2_frame_hd_init(&data_hd, 16, NGHTTP2_DATA, NGHTTP2_FLAG_NONE, 1); + + nghttp2_buf_reset(&databuf); + nghttp2_frame_pack_frame_hd(databuf.pos, &data_hd); + + /* Intentionally specify larger buffer size to see pause is kicked + in. */ + databuf.last = databuf.end; + + user_data.frame_recv_cb_called = 0; + rv = + nghttp2_session_mem_recv(session, databuf.pos, nghttp2_buf_len(&databuf)); + + CU_ASSERT(16 + NGHTTP2_FRAME_HDLEN == rv); + CU_ASSERT(0 == user_data.frame_recv_cb_called); + + /* Next nghttp2_session_mem_recv invokes on_frame_recv_callback and + pause again in on_data_chunk_recv_callback since we pass same + DATA frame. */ + user_data.frame_recv_cb_called = 0; + rv = + nghttp2_session_mem_recv(session, databuf.pos, nghttp2_buf_len(&databuf)); + CU_ASSERT(16 + NGHTTP2_FRAME_HDLEN == rv); + CU_ASSERT(1 == user_data.frame_recv_cb_called); + + /* And finally call on_frame_recv_callback with 0 size input */ + user_data.frame_recv_cb_called = 0; + rv = nghttp2_session_mem_recv(session, NULL, 0); + CU_ASSERT(0 == rv); + CU_ASSERT(1 == user_data.frame_recv_cb_called); + + nghttp2_bufs_free(&bufs); + nghttp2_hd_deflate_free(&deflater); + nghttp2_session_del(session); +} + +void test_nghttp2_session_add_frame(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + accumulator acc; + my_user_data user_data; + nghttp2_outbound_item *item; + nghttp2_frame *frame; + nghttp2_nv *nva; + ssize_t nvlen; + nghttp2_mem *mem; + + mem = nghttp2_mem_default(); + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.send_callback = accumulator_send_callback; + + acc.length = 0; + user_data.acc = &acc; + + CU_ASSERT(0 == nghttp2_session_client_new(&session, &callbacks, &user_data)); + + item = mem->malloc(sizeof(nghttp2_outbound_item), NULL); + + nghttp2_outbound_item_init(item); + + frame = &item->frame; + + nvlen = ARRLEN(reqnv); + nghttp2_nv_array_copy(&nva, reqnv, nvlen, mem); + + nghttp2_frame_headers_init( + &frame->headers, NGHTTP2_FLAG_END_HEADERS | NGHTTP2_FLAG_PRIORITY, + session->next_stream_id, NGHTTP2_HCAT_REQUEST, NULL, nva, nvlen); + + session->next_stream_id += 2; + + CU_ASSERT(0 == nghttp2_session_add_item(session, item)); + CU_ASSERT(NULL != nghttp2_outbound_queue_top(&session->ob_syn)); + CU_ASSERT(0 == nghttp2_session_send(session)); + CU_ASSERT(NGHTTP2_HEADERS == acc.buf[3]); + CU_ASSERT((NGHTTP2_FLAG_END_HEADERS | NGHTTP2_FLAG_PRIORITY) == acc.buf[4]); + /* check stream id */ + CU_ASSERT(1 == nghttp2_get_uint32(&acc.buf[5])); + + nghttp2_session_del(session); +} + +void test_nghttp2_session_on_request_headers_received(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + my_user_data user_data; + nghttp2_frame frame; + nghttp2_stream *stream; + int32_t stream_id = 1; + nghttp2_nv malformed_nva[] = {MAKE_NV(":path", "\x01")}; + nghttp2_nv *nva; + size_t nvlen; + nghttp2_priority_spec pri_spec; + nghttp2_mem *mem; + + mem = nghttp2_mem_default(); + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.on_begin_headers_callback = on_begin_headers_callback; + callbacks.on_invalid_frame_recv_callback = on_invalid_frame_recv_callback; + + nghttp2_session_server_new(&session, &callbacks, &user_data); + + nghttp2_priority_spec_init(&pri_spec, 0, 255, 0); + + nghttp2_frame_headers_init( + &frame.headers, NGHTTP2_FLAG_END_HEADERS | NGHTTP2_FLAG_PRIORITY, + stream_id, NGHTTP2_HCAT_REQUEST, &pri_spec, NULL, 0); + + user_data.begin_headers_cb_called = 0; + user_data.invalid_frame_recv_cb_called = 0; + + CU_ASSERT(0 == nghttp2_session_on_request_headers_received(session, &frame)); + CU_ASSERT(1 == user_data.begin_headers_cb_called); + stream = nghttp2_session_get_stream(session, stream_id); + CU_ASSERT(NGHTTP2_STREAM_OPENING == stream->state); + CU_ASSERT(255 == stream->weight); + + nghttp2_frame_headers_free(&frame.headers, mem); + + /* More than un-ACKed max concurrent streams leads REFUSED_STREAM */ + session->pending_local_max_concurrent_stream = 1; + nghttp2_frame_headers_init(&frame.headers, + NGHTTP2_FLAG_END_HEADERS | NGHTTP2_FLAG_PRIORITY, + 3, NGHTTP2_HCAT_HEADERS, NULL, NULL, 0); + user_data.invalid_frame_recv_cb_called = 0; + CU_ASSERT(NGHTTP2_ERR_IGN_HEADER_BLOCK == + nghttp2_session_on_request_headers_received(session, &frame)); + CU_ASSERT(1 == user_data.invalid_frame_recv_cb_called); + CU_ASSERT(0 == (session->goaway_flags & NGHTTP2_GOAWAY_TERM_ON_SEND)); + + nghttp2_frame_headers_free(&frame.headers, mem); + session->local_settings.max_concurrent_streams = + NGHTTP2_INITIAL_MAX_CONCURRENT_STREAMS; + + /* Stream ID less than or equal to the previouly received request + HEADERS is just ignored due to race condition */ + nghttp2_frame_headers_init(&frame.headers, + NGHTTP2_FLAG_END_HEADERS | NGHTTP2_FLAG_PRIORITY, + 3, NGHTTP2_HCAT_HEADERS, NULL, NULL, 0); + user_data.invalid_frame_recv_cb_called = 0; + CU_ASSERT(NGHTTP2_ERR_IGN_HEADER_BLOCK == + nghttp2_session_on_request_headers_received(session, &frame)); + CU_ASSERT(0 == user_data.invalid_frame_recv_cb_called); + CU_ASSERT(0 == (session->goaway_flags & NGHTTP2_GOAWAY_TERM_ON_SEND)); + + nghttp2_frame_headers_free(&frame.headers, mem); + + /* Stream ID is our side and it is idle stream ID, then treat it as + connection error */ + nghttp2_frame_headers_init(&frame.headers, + NGHTTP2_FLAG_END_HEADERS | NGHTTP2_FLAG_PRIORITY, + 2, NGHTTP2_HCAT_HEADERS, NULL, NULL, 0); + user_data.invalid_frame_recv_cb_called = 0; + CU_ASSERT(NGHTTP2_ERR_IGN_HEADER_BLOCK == + nghttp2_session_on_request_headers_received(session, &frame)); + CU_ASSERT(1 == user_data.invalid_frame_recv_cb_called); + CU_ASSERT(session->goaway_flags & NGHTTP2_GOAWAY_TERM_ON_SEND); + + nghttp2_frame_headers_free(&frame.headers, mem); + + nghttp2_session_del(session); + + /* Check malformed headers. The library accept it. */ + nghttp2_session_server_new(&session, &callbacks, &user_data); + + nvlen = ARRLEN(malformed_nva); + nghttp2_nv_array_copy(&nva, malformed_nva, nvlen, mem); + nghttp2_frame_headers_init(&frame.headers, + NGHTTP2_FLAG_END_HEADERS | NGHTTP2_FLAG_PRIORITY, + 1, NGHTTP2_HCAT_HEADERS, NULL, nva, nvlen); + user_data.begin_headers_cb_called = 0; + user_data.invalid_frame_recv_cb_called = 0; + CU_ASSERT(0 == nghttp2_session_on_request_headers_received(session, &frame)); + CU_ASSERT(1 == user_data.begin_headers_cb_called); + CU_ASSERT(0 == user_data.invalid_frame_recv_cb_called); + + nghttp2_frame_headers_free(&frame.headers, mem); + + nghttp2_session_del(session); + + /* Check client side */ + nghttp2_session_client_new(&session, &callbacks, &user_data); + + /* Receiving peer's idle stream ID is subject to connection error */ + nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_END_HEADERS, 2, + NGHTTP2_HCAT_REQUEST, NULL, NULL, 0); + + user_data.invalid_frame_recv_cb_called = 0; + CU_ASSERT(NGHTTP2_ERR_IGN_HEADER_BLOCK == + nghttp2_session_on_request_headers_received(session, &frame)); + CU_ASSERT(1 == user_data.invalid_frame_recv_cb_called); + CU_ASSERT(session->goaway_flags & NGHTTP2_GOAWAY_TERM_ON_SEND); + + nghttp2_frame_headers_free(&frame.headers, mem); + + nghttp2_session_del(session); + + nghttp2_session_client_new(&session, &callbacks, &user_data); + + /* Receiving our's idle stream ID is subject to connection error */ + nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_END_HEADERS, 1, + NGHTTP2_HCAT_REQUEST, NULL, NULL, 0); + + user_data.invalid_frame_recv_cb_called = 0; + CU_ASSERT(NGHTTP2_ERR_IGN_HEADER_BLOCK == + nghttp2_session_on_request_headers_received(session, &frame)); + CU_ASSERT(1 == user_data.invalid_frame_recv_cb_called); + CU_ASSERT(session->goaway_flags & NGHTTP2_GOAWAY_TERM_ON_SEND); + + nghttp2_frame_headers_free(&frame.headers, mem); + + nghttp2_session_del(session); + + nghttp2_session_client_new(&session, &callbacks, &user_data); + + session->next_stream_id = 5; + + /* Stream ID which is not idle and not in stream map is just + ignored */ + nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_END_HEADERS, 3, + NGHTTP2_HCAT_REQUEST, NULL, NULL, 0); + + user_data.invalid_frame_recv_cb_called = 0; + CU_ASSERT(NGHTTP2_ERR_IGN_HEADER_BLOCK == + nghttp2_session_on_request_headers_received(session, &frame)); + CU_ASSERT(0 == user_data.invalid_frame_recv_cb_called); + CU_ASSERT(0 == (session->goaway_flags & NGHTTP2_GOAWAY_TERM_ON_SEND)); + + nghttp2_frame_headers_free(&frame.headers, mem); + + nghttp2_session_del(session); + + nghttp2_session_server_new(&session, &callbacks, &user_data); + + /* Stream ID which is equal to local_last_stream_id is ok. */ + session->local_last_stream_id = 3; + + nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_END_HEADERS, 3, + NGHTTP2_HCAT_REQUEST, NULL, NULL, 0); + + CU_ASSERT(0 == nghttp2_session_on_request_headers_received(session, &frame)); + + nghttp2_frame_headers_free(&frame.headers, mem); + + /* If GOAWAY has been sent, new stream is ignored */ + nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_END_HEADERS, 5, + NGHTTP2_HCAT_REQUEST, NULL, NULL, 0); + + session->goaway_flags |= NGHTTP2_GOAWAY_SENT; + user_data.invalid_frame_recv_cb_called = 0; + CU_ASSERT(NGHTTP2_ERR_IGN_HEADER_BLOCK == + nghttp2_session_on_request_headers_received(session, &frame)); + CU_ASSERT(0 == user_data.invalid_frame_recv_cb_called); + CU_ASSERT(0 == (session->goaway_flags & NGHTTP2_GOAWAY_TERM_ON_SEND)); + + nghttp2_frame_headers_free(&frame.headers, mem); + + nghttp2_session_del(session); +} + +void test_nghttp2_session_on_response_headers_received(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + my_user_data user_data; + nghttp2_frame frame; + nghttp2_stream *stream; + nghttp2_mem *mem; + + mem = nghttp2_mem_default(); + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.on_begin_headers_callback = on_begin_headers_callback; + callbacks.on_invalid_frame_recv_callback = on_invalid_frame_recv_callback; + + nghttp2_session_client_new(&session, &callbacks, &user_data); + stream = nghttp2_session_open_stream(session, 1, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, + NGHTTP2_STREAM_OPENING, NULL); + nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_END_HEADERS, 1, + NGHTTP2_HCAT_HEADERS, NULL, NULL, 0); + + user_data.begin_headers_cb_called = 0; + user_data.invalid_frame_recv_cb_called = 0; + + CU_ASSERT(0 == nghttp2_session_on_response_headers_received(session, &frame, + stream)); + CU_ASSERT(1 == user_data.begin_headers_cb_called); + CU_ASSERT(NGHTTP2_STREAM_OPENED == stream->state); + + nghttp2_frame_headers_free(&frame.headers, mem); + nghttp2_session_del(session); +} + +void test_nghttp2_session_on_headers_received(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + my_user_data user_data; + nghttp2_frame frame; + nghttp2_stream *stream; + nghttp2_mem *mem; + + mem = nghttp2_mem_default(); + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.on_begin_headers_callback = on_begin_headers_callback; + callbacks.on_invalid_frame_recv_callback = on_invalid_frame_recv_callback; + + nghttp2_session_client_new(&session, &callbacks, &user_data); + stream = nghttp2_session_open_stream(session, 1, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, NGHTTP2_STREAM_OPENED, + NULL); + nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_WR); + nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_END_HEADERS, 1, + NGHTTP2_HCAT_HEADERS, NULL, NULL, 0); + + user_data.begin_headers_cb_called = 0; + user_data.invalid_frame_recv_cb_called = 0; + + CU_ASSERT(0 == nghttp2_session_on_headers_received(session, &frame, stream)); + CU_ASSERT(1 == user_data.begin_headers_cb_called); + CU_ASSERT(NGHTTP2_STREAM_OPENED == stream->state); + + /* stream closed */ + frame.hd.flags |= NGHTTP2_FLAG_END_STREAM; + + CU_ASSERT(0 == nghttp2_session_on_headers_received(session, &frame, stream)); + CU_ASSERT(2 == user_data.begin_headers_cb_called); + + /* Check to see when NGHTTP2_STREAM_CLOSING, incoming HEADERS is + discarded. */ + stream = nghttp2_session_open_stream(session, 3, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, + NGHTTP2_STREAM_CLOSING, NULL); + frame.hd.stream_id = 3; + frame.hd.flags = NGHTTP2_FLAG_END_HEADERS; + CU_ASSERT(NGHTTP2_ERR_IGN_HEADER_BLOCK == + nghttp2_session_on_headers_received(session, &frame, stream)); + /* See no counters are updated */ + CU_ASSERT(2 == user_data.begin_headers_cb_called); + CU_ASSERT(0 == user_data.invalid_frame_recv_cb_called); + + /* Server initiated stream */ + stream = nghttp2_session_open_stream(session, 2, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, + NGHTTP2_STREAM_OPENING, NULL); + + /* half closed (remote) */ + frame.hd.flags = NGHTTP2_FLAG_END_HEADERS | NGHTTP2_FLAG_END_STREAM; + frame.hd.stream_id = 2; + + CU_ASSERT(0 == nghttp2_session_on_headers_received(session, &frame, stream)); + CU_ASSERT(3 == user_data.begin_headers_cb_called); + CU_ASSERT(NGHTTP2_STREAM_OPENING == stream->state); + + nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_RD); + + /* Further reception of HEADERS is subject to stream error */ + CU_ASSERT(NGHTTP2_ERR_IGN_HEADER_BLOCK == + nghttp2_session_on_headers_received(session, &frame, stream)); + CU_ASSERT(1 == user_data.invalid_frame_recv_cb_called); + + nghttp2_frame_headers_free(&frame.headers, mem); + + nghttp2_session_del(session); +} + +void test_nghttp2_session_on_push_response_headers_received(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + my_user_data user_data; + nghttp2_frame frame; + nghttp2_stream *stream; + nghttp2_outbound_item *item; + nghttp2_mem *mem; + + mem = nghttp2_mem_default(); + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.send_callback = null_send_callback; + callbacks.on_begin_headers_callback = on_begin_headers_callback; + callbacks.on_invalid_frame_recv_callback = on_invalid_frame_recv_callback; + + nghttp2_session_client_new(&session, &callbacks, &user_data); + stream = nghttp2_session_open_stream(session, 2, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, + NGHTTP2_STREAM_RESERVED, NULL); + nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_END_HEADERS, 2, + NGHTTP2_HCAT_HEADERS, NULL, NULL, 0); + /* nghttp2_session_on_push_response_headers_received assumes + stream's state is NGHTTP2_STREAM_RESERVED and session->server is + 0. */ + + user_data.begin_headers_cb_called = 0; + user_data.invalid_frame_recv_cb_called = 0; + + CU_ASSERT(0 == nghttp2_session_on_push_response_headers_received( + session, &frame, stream)); + CU_ASSERT(1 == user_data.begin_headers_cb_called); + CU_ASSERT(NGHTTP2_STREAM_OPENED == stream->state); + CU_ASSERT(1 == session->num_incoming_streams); + CU_ASSERT(0 == (stream->flags & NGHTTP2_STREAM_FLAG_PUSH)); + + /* If un-ACKed max concurrent streams limit is exceeded, + RST_STREAMed */ + session->pending_local_max_concurrent_stream = 1; + stream = nghttp2_session_open_stream(session, 4, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, + NGHTTP2_STREAM_RESERVED, NULL); + frame.hd.stream_id = 4; + CU_ASSERT(NGHTTP2_ERR_IGN_HEADER_BLOCK == + nghttp2_session_on_push_response_headers_received(session, &frame, + stream)); + item = nghttp2_session_get_next_ob_item(session); + CU_ASSERT(NGHTTP2_RST_STREAM == item->frame.hd.type); + CU_ASSERT(NGHTTP2_REFUSED_STREAM == item->frame.rst_stream.error_code); + CU_ASSERT(1 == session->num_incoming_streams); + + CU_ASSERT(0 == nghttp2_session_send(session)); + CU_ASSERT(1 == session->num_incoming_streams); + + /* If ACKed max concurrent streams limit is exceeded, GOAWAY is + issued */ + session->local_settings.max_concurrent_streams = 1; + + stream = nghttp2_session_open_stream(session, 6, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, + NGHTTP2_STREAM_RESERVED, NULL); + frame.hd.stream_id = 6; + + CU_ASSERT(NGHTTP2_ERR_IGN_HEADER_BLOCK == + nghttp2_session_on_push_response_headers_received(session, &frame, + stream)); + item = nghttp2_session_get_next_ob_item(session); + CU_ASSERT(NGHTTP2_GOAWAY == item->frame.hd.type); + CU_ASSERT(NGHTTP2_PROTOCOL_ERROR == item->frame.goaway.error_code); + CU_ASSERT(1 == session->num_incoming_streams); + + nghttp2_frame_headers_free(&frame.headers, mem); + nghttp2_session_del(session); +} + +void test_nghttp2_session_on_priority_received(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + my_user_data user_data; + nghttp2_frame frame; + nghttp2_stream *stream, *dep_stream; + nghttp2_priority_spec pri_spec; + nghttp2_outbound_item *item; + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.on_frame_recv_callback = on_frame_recv_callback; + callbacks.on_invalid_frame_recv_callback = on_invalid_frame_recv_callback; + + nghttp2_session_server_new(&session, &callbacks, &user_data); + stream = nghttp2_session_open_stream(session, 1, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, + NGHTTP2_STREAM_OPENING, NULL); + + nghttp2_priority_spec_init(&pri_spec, 0, 2, 0); + + nghttp2_frame_priority_init(&frame.priority, 1, &pri_spec); + + /* depend on stream 0 */ + CU_ASSERT(0 == nghttp2_session_on_priority_received(session, &frame)); + + CU_ASSERT(2 == stream->weight); + + stream = nghttp2_session_open_stream(session, 2, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, + NGHTTP2_STREAM_OPENING, NULL); + + dep_stream = nghttp2_session_open_stream(session, 3, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, + NGHTTP2_STREAM_OPENING, NULL); + + frame.hd.stream_id = 2; + + /* using dependency stream */ + nghttp2_priority_spec_init(&frame.priority.pri_spec, 3, 1, 0); + + CU_ASSERT(0 == nghttp2_session_on_priority_received(session, &frame)); + CU_ASSERT(dep_stream == stream->dep_prev); + + /* PRIORITY against idle stream */ + + frame.hd.stream_id = 100; + + CU_ASSERT(0 == nghttp2_session_on_priority_received(session, &frame)); + + stream = nghttp2_session_get_stream_raw(session, frame.hd.stream_id); + + CU_ASSERT(NGHTTP2_STREAM_IDLE == stream->state); + CU_ASSERT(dep_stream == stream->dep_prev); + + nghttp2_frame_priority_free(&frame.priority); + nghttp2_session_del(session); + + /* Check dep_stream_id == stream_id case */ + nghttp2_session_server_new(&session, &callbacks, &user_data); + nghttp2_session_open_stream(session, 1, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, NGHTTP2_STREAM_OPENED, NULL); + + nghttp2_priority_spec_init(&pri_spec, 1, 0, 0); + + nghttp2_frame_priority_init(&frame.priority, 1, &pri_spec); + + CU_ASSERT(0 == nghttp2_session_on_priority_received(session, &frame)); + + item = nghttp2_session_get_next_ob_item(session); + + CU_ASSERT(NGHTTP2_GOAWAY == item->frame.hd.type); + + nghttp2_frame_priority_free(&frame.priority); + nghttp2_session_del(session); +} + +void test_nghttp2_session_on_rst_stream_received(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + my_user_data user_data; + nghttp2_frame frame; + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + nghttp2_session_server_new(&session, &callbacks, &user_data); + nghttp2_session_open_stream(session, 1, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, NGHTTP2_STREAM_OPENING, NULL); + + nghttp2_frame_rst_stream_init(&frame.rst_stream, 1, NGHTTP2_PROTOCOL_ERROR); + + CU_ASSERT(0 == nghttp2_session_on_rst_stream_received(session, &frame)); + CU_ASSERT(NULL == nghttp2_session_get_stream(session, 1)); + + nghttp2_frame_rst_stream_free(&frame.rst_stream); + nghttp2_session_del(session); +} + +void test_nghttp2_session_on_settings_received(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + my_user_data user_data; + nghttp2_stream *stream1, *stream2; + nghttp2_frame frame; + const size_t niv = 5; + nghttp2_settings_entry iv[255]; + nghttp2_outbound_item *item; + nghttp2_nv nv = MAKE_NV(":authority", "example.org"); + nghttp2_mem *mem; + + mem = nghttp2_mem_default(); + + iv[0].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS; + iv[0].value = 50; + + iv[1].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS; + iv[1].value = 1000000009; + + iv[2].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE; + iv[2].value = 64 * 1024; + + iv[3].settings_id = NGHTTP2_SETTINGS_HEADER_TABLE_SIZE; + iv[3].value = 1024; + + iv[4].settings_id = NGHTTP2_SETTINGS_ENABLE_PUSH; + iv[4].value = 0; + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.send_callback = null_send_callback; + + nghttp2_session_client_new(&session, &callbacks, &user_data); + session->remote_settings.initial_window_size = 16 * 1024; + + stream1 = nghttp2_session_open_stream(session, 1, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, + NGHTTP2_STREAM_OPENING, NULL); + stream2 = nghttp2_session_open_stream(session, 2, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, + NGHTTP2_STREAM_OPENING, NULL); + /* Set window size for each streams and will see how settings + updates these values */ + stream1->remote_window_size = 16 * 1024; + stream2->remote_window_size = -48 * 1024; + + nghttp2_frame_settings_init(&frame.settings, NGHTTP2_FLAG_NONE, + dup_iv(iv, niv), niv); + + CU_ASSERT(0 == nghttp2_session_on_settings_received(session, &frame, 0)); + CU_ASSERT(1000000009 == session->remote_settings.max_concurrent_streams); + CU_ASSERT(64 * 1024 == session->remote_settings.initial_window_size); + CU_ASSERT(1024 == session->remote_settings.header_table_size); + CU_ASSERT(0 == session->remote_settings.enable_push); + + CU_ASSERT(64 * 1024 == stream1->remote_window_size); + CU_ASSERT(0 == stream2->remote_window_size); + + frame.settings.iv[2].value = 16 * 1024; + + CU_ASSERT(0 == nghttp2_session_on_settings_received(session, &frame, 0)); + + CU_ASSERT(16 * 1024 == stream1->remote_window_size); + CU_ASSERT(-48 * 1024 == stream2->remote_window_size); + + CU_ASSERT(16 * 1024 == nghttp2_session_get_stream_remote_window_size( + session, stream1->stream_id)); + CU_ASSERT(0 == nghttp2_session_get_stream_remote_window_size( + session, stream2->stream_id)); + + nghttp2_frame_settings_free(&frame.settings, mem); + + nghttp2_session_del(session); + + /* Check ACK with niv > 0 */ + nghttp2_session_server_new(&session, &callbacks, NULL); + nghttp2_frame_settings_init(&frame.settings, NGHTTP2_FLAG_ACK, dup_iv(iv, 1), + 1); + /* Specify inflight_iv deliberately */ + session->inflight_iv = frame.settings.iv; + session->inflight_niv = frame.settings.niv; + + CU_ASSERT(0 == nghttp2_session_on_settings_received(session, &frame, 0)); + item = nghttp2_session_get_next_ob_item(session); + CU_ASSERT(item != NULL); + CU_ASSERT(NGHTTP2_GOAWAY == item->frame.hd.type); + + session->inflight_iv = NULL; + session->inflight_niv = -1; + + nghttp2_frame_settings_free(&frame.settings, mem); + nghttp2_session_del(session); + + /* Check ACK against no inflight SETTINGS */ + nghttp2_session_server_new(&session, &callbacks, NULL); + nghttp2_frame_settings_init(&frame.settings, NGHTTP2_FLAG_ACK, NULL, 0); + + CU_ASSERT(0 == nghttp2_session_on_settings_received(session, &frame, 0)); + item = nghttp2_session_get_next_ob_item(session); + CU_ASSERT(item != NULL); + CU_ASSERT(NGHTTP2_GOAWAY == item->frame.hd.type); + + nghttp2_frame_settings_free(&frame.settings, mem); + nghttp2_session_del(session); + + /* Check that 2 SETTINGS_HEADER_TABLE_SIZE 0 and 4096 are included + and header table size is once cleared to 0. */ + nghttp2_session_client_new(&session, &callbacks, NULL); + + nghttp2_submit_request(session, NULL, &nv, 1, NULL, NULL); + + nghttp2_session_send(session); + + CU_ASSERT(session->hd_deflater.ctx.hd_table.len > 0); + + iv[0].settings_id = NGHTTP2_SETTINGS_HEADER_TABLE_SIZE; + iv[0].value = 0; + + iv[1].settings_id = NGHTTP2_SETTINGS_HEADER_TABLE_SIZE; + iv[1].value = 2048; + + nghttp2_frame_settings_init(&frame.settings, NGHTTP2_FLAG_NONE, dup_iv(iv, 2), + 2); + + CU_ASSERT(0 == nghttp2_session_on_settings_received(session, &frame, 0)); + + CU_ASSERT(0 == session->hd_deflater.ctx.hd_table.len); + CU_ASSERT(2048 == session->hd_deflater.ctx.hd_table_bufsize_max); + CU_ASSERT(2048 == session->remote_settings.header_table_size); + + nghttp2_frame_settings_free(&frame.settings, mem); + nghttp2_session_del(session); + + /* Check too large SETTINGS_MAX_FRAME_SIZE */ + nghttp2_session_server_new(&session, &callbacks, NULL); + + iv[0].settings_id = NGHTTP2_SETTINGS_MAX_FRAME_SIZE; + iv[0].value = NGHTTP2_MAX_FRAME_SIZE_MAX + 1; + + nghttp2_frame_settings_init(&frame.settings, NGHTTP2_FLAG_NONE, dup_iv(iv, 1), + 1); + + CU_ASSERT(0 == nghttp2_session_on_settings_received(session, &frame, 0)); + + item = nghttp2_session_get_next_ob_item(session); + + CU_ASSERT(item != NULL); + CU_ASSERT(NGHTTP2_GOAWAY == item->frame.hd.type); + + nghttp2_frame_settings_free(&frame.settings, mem); + nghttp2_session_del(session); +} + +void test_nghttp2_session_on_push_promise_received(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + my_user_data user_data; + nghttp2_frame frame; + nghttp2_stream *stream, *promised_stream; + nghttp2_outbound_item *item; + nghttp2_nv malformed_nva[] = {MAKE_NV(":path", "\x01")}; + nghttp2_nv *nva; + size_t nvlen; + nghttp2_mem *mem; + nghttp2_settings_entry iv = {NGHTTP2_SETTINGS_ENABLE_PUSH, 0}; + + mem = nghttp2_mem_default(); + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.send_callback = null_send_callback; + callbacks.on_begin_headers_callback = on_begin_headers_callback; + callbacks.on_invalid_frame_recv_callback = on_invalid_frame_recv_callback; + + nghttp2_session_client_new(&session, &callbacks, &user_data); + + stream = nghttp2_session_open_stream(session, 1, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, + NGHTTP2_STREAM_OPENING, NULL); + nghttp2_frame_push_promise_init(&frame.push_promise, NGHTTP2_FLAG_END_HEADERS, + 1, 2, NULL, 0); + + user_data.begin_headers_cb_called = 0; + user_data.invalid_frame_recv_cb_called = 0; + + CU_ASSERT(0 == nghttp2_session_on_push_promise_received(session, &frame)); + + CU_ASSERT(1 == user_data.begin_headers_cb_called); + promised_stream = nghttp2_session_get_stream(session, 2); + CU_ASSERT(NGHTTP2_STREAM_RESERVED == promised_stream->state); + CU_ASSERT(2 == session->last_recv_stream_id); + + /* Attempt to PUSH_PROMISE against half close (remote) */ + nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_RD); + frame.push_promise.promised_stream_id = 4; + + user_data.begin_headers_cb_called = 0; + user_data.invalid_frame_recv_cb_called = 0; + CU_ASSERT(NGHTTP2_ERR_IGN_HEADER_BLOCK == + nghttp2_session_on_push_promise_received(session, &frame)); + + CU_ASSERT(0 == user_data.begin_headers_cb_called); + CU_ASSERT(1 == user_data.invalid_frame_recv_cb_called); + CU_ASSERT(NULL == nghttp2_session_get_stream(session, 4)); + item = nghttp2_session_get_next_ob_item(session); + CU_ASSERT(NGHTTP2_RST_STREAM == item->frame.hd.type); + CU_ASSERT(4 == item->frame.hd.stream_id); + CU_ASSERT(NGHTTP2_PROTOCOL_ERROR == item->frame.rst_stream.error_code); + CU_ASSERT(0 == nghttp2_session_send(session)); + CU_ASSERT(4 == session->last_recv_stream_id); + + /* Attempt to PUSH_PROMISE against stream in closing state */ + stream->shut_flags = NGHTTP2_SHUT_NONE; + stream->state = NGHTTP2_STREAM_CLOSING; + frame.push_promise.promised_stream_id = 6; + + user_data.begin_headers_cb_called = 0; + user_data.invalid_frame_recv_cb_called = 0; + CU_ASSERT(NGHTTP2_ERR_IGN_HEADER_BLOCK == + nghttp2_session_on_push_promise_received(session, &frame)); + + CU_ASSERT(0 == user_data.begin_headers_cb_called); + CU_ASSERT(NULL == nghttp2_session_get_stream(session, 6)); + item = nghttp2_session_get_next_ob_item(session); + CU_ASSERT(NGHTTP2_RST_STREAM == item->frame.hd.type); + CU_ASSERT(6 == item->frame.hd.stream_id); + CU_ASSERT(NGHTTP2_REFUSED_STREAM == item->frame.rst_stream.error_code); + CU_ASSERT(0 == nghttp2_session_send(session)); + + /* Attempt to PUSH_PROMISE against non-existent stream */ + frame.hd.stream_id = 3; + frame.push_promise.promised_stream_id = 8; + + user_data.begin_headers_cb_called = 0; + user_data.invalid_frame_recv_cb_called = 0; + CU_ASSERT(NGHTTP2_ERR_IGN_HEADER_BLOCK == + nghttp2_session_on_push_promise_received(session, &frame)); + + CU_ASSERT(0 == user_data.begin_headers_cb_called); + CU_ASSERT(NULL == nghttp2_session_get_stream(session, 8)); + item = nghttp2_session_get_next_ob_item(session); + CU_ASSERT(NGHTTP2_GOAWAY == item->frame.hd.type); + CU_ASSERT(0 == item->frame.hd.stream_id); + CU_ASSERT(NGHTTP2_PROTOCOL_ERROR == item->frame.goaway.error_code); + CU_ASSERT(0 == nghttp2_session_send(session)); + + nghttp2_session_del(session); + + nghttp2_session_client_new(&session, &callbacks, &user_data); + + stream = nghttp2_session_open_stream(session, 1, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, + NGHTTP2_STREAM_OPENING, NULL); + + /* Same ID twice */ + stream->state = NGHTTP2_STREAM_OPENING; + + user_data.begin_headers_cb_called = 0; + user_data.invalid_frame_recv_cb_called = 0; + CU_ASSERT(NGHTTP2_ERR_IGN_HEADER_BLOCK == + nghttp2_session_on_push_promise_received(session, &frame)); + + CU_ASSERT(0 == user_data.begin_headers_cb_called); + CU_ASSERT(NULL == nghttp2_session_get_stream(session, 8)); + item = nghttp2_session_get_next_ob_item(session); + CU_ASSERT(NGHTTP2_GOAWAY == item->frame.hd.type); + CU_ASSERT(NGHTTP2_PROTOCOL_ERROR == item->frame.goaway.error_code); + CU_ASSERT(0 == nghttp2_session_send(session)); + + /* After GOAWAY, PUSH_PROMISE will be discarded */ + frame.push_promise.promised_stream_id = 10; + + user_data.begin_headers_cb_called = 0; + user_data.invalid_frame_recv_cb_called = 0; + CU_ASSERT(NGHTTP2_ERR_IGN_HEADER_BLOCK == + nghttp2_session_on_push_promise_received(session, &frame)); + + CU_ASSERT(0 == user_data.begin_headers_cb_called); + CU_ASSERT(NULL == nghttp2_session_get_stream(session, 10)); + CU_ASSERT(NULL == nghttp2_session_get_next_ob_item(session)); + + nghttp2_frame_push_promise_free(&frame.push_promise, mem); + nghttp2_session_del(session); + + nghttp2_session_client_new(&session, &callbacks, &user_data); + + nghttp2_session_open_stream(session, 2, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, NGHTTP2_STREAM_RESERVED, NULL); + /* Attempt to PUSH_PROMISE against reserved (remote) stream */ + nghttp2_frame_push_promise_init(&frame.push_promise, NGHTTP2_FLAG_END_HEADERS, + 2, 4, NULL, 0); + + user_data.begin_headers_cb_called = 0; + user_data.invalid_frame_recv_cb_called = 0; + CU_ASSERT(NGHTTP2_ERR_IGN_HEADER_BLOCK == + nghttp2_session_on_push_promise_received(session, &frame)); + + CU_ASSERT(0 == user_data.begin_headers_cb_called); + CU_ASSERT(1 == user_data.invalid_frame_recv_cb_called); + + nghttp2_frame_push_promise_free(&frame.push_promise, mem); + nghttp2_session_del(session); + + /* Disable PUSH */ + nghttp2_session_client_new(&session, &callbacks, &user_data); + + nghttp2_session_open_stream(session, 1, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, NGHTTP2_STREAM_OPENING, NULL); + + session->local_settings.enable_push = 0; + + nghttp2_frame_push_promise_init(&frame.push_promise, NGHTTP2_FLAG_END_HEADERS, + 1, 2, NULL, 0); + + user_data.begin_headers_cb_called = 0; + user_data.invalid_frame_recv_cb_called = 0; + CU_ASSERT(NGHTTP2_ERR_IGN_HEADER_BLOCK == + nghttp2_session_on_push_promise_received(session, &frame)); + + CU_ASSERT(0 == user_data.begin_headers_cb_called); + CU_ASSERT(1 == user_data.invalid_frame_recv_cb_called); + + nghttp2_frame_push_promise_free(&frame.push_promise, mem); + nghttp2_session_del(session); + + /* Check malformed headers. We accept malformed headers */ + nghttp2_session_client_new(&session, &callbacks, &user_data); + + nghttp2_session_open_stream(session, 1, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, NGHTTP2_STREAM_OPENING, NULL); + nvlen = ARRLEN(malformed_nva); + nghttp2_nv_array_copy(&nva, malformed_nva, nvlen, mem); + nghttp2_frame_push_promise_init(&frame.push_promise, NGHTTP2_FLAG_END_HEADERS, + 1, 2, nva, nvlen); + user_data.begin_headers_cb_called = 0; + user_data.invalid_frame_recv_cb_called = 0; + CU_ASSERT(0 == nghttp2_session_on_push_promise_received(session, &frame)); + + CU_ASSERT(1 == user_data.begin_headers_cb_called); + CU_ASSERT(0 == user_data.invalid_frame_recv_cb_called); + + nghttp2_frame_push_promise_free(&frame.push_promise, mem); + nghttp2_session_del(session); + + /* If local_settings.enable_push = 0 is pending, but not acked from + peer, incoming PUSH_PROMISE is rejected */ + nghttp2_session_client_new(&session, &callbacks, &user_data); + + nghttp2_session_open_stream(session, 1, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, NGHTTP2_STREAM_OPENING, NULL); + /* Submit settings with ENABLE_PUSH = 0 (thus disabling push) */ + nghttp2_submit_settings(session, NGHTTP2_FLAG_NONE, &iv, 1); + + nghttp2_frame_push_promise_init(&frame.push_promise, NGHTTP2_FLAG_END_HEADERS, + 1, 2, NULL, 0); + + CU_ASSERT(NGHTTP2_ERR_IGN_HEADER_BLOCK == + nghttp2_session_on_push_promise_received(session, &frame)); + + nghttp2_frame_push_promise_free(&frame.push_promise, mem); + nghttp2_session_del(session); +} + +void test_nghttp2_session_on_ping_received(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + my_user_data user_data; + nghttp2_frame frame; + nghttp2_outbound_item *top; + const uint8_t opaque_data[] = "01234567"; + + user_data.frame_recv_cb_called = 0; + user_data.invalid_frame_recv_cb_called = 0; + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.on_frame_recv_callback = on_frame_recv_callback; + callbacks.on_invalid_frame_recv_callback = on_invalid_frame_recv_callback; + + nghttp2_session_client_new(&session, &callbacks, &user_data); + nghttp2_frame_ping_init(&frame.ping, NGHTTP2_FLAG_ACK, opaque_data); + + CU_ASSERT(0 == nghttp2_session_on_ping_received(session, &frame)); + CU_ASSERT(1 == user_data.frame_recv_cb_called); + + /* Since this ping frame has ACK flag set, no further action is + performed. */ + CU_ASSERT(NULL == nghttp2_outbound_queue_top(&session->ob_urgent)); + + /* Clear the flag, and receive it again */ + frame.hd.flags = NGHTTP2_FLAG_NONE; + + CU_ASSERT(0 == nghttp2_session_on_ping_received(session, &frame)); + CU_ASSERT(2 == user_data.frame_recv_cb_called); + top = nghttp2_outbound_queue_top(&session->ob_urgent); + CU_ASSERT(NGHTTP2_PING == top->frame.hd.type); + CU_ASSERT(NGHTTP2_FLAG_ACK == top->frame.hd.flags); + CU_ASSERT(memcmp(opaque_data, top->frame.ping.opaque_data, 8) == 0); + + nghttp2_frame_ping_free(&frame.ping); + nghttp2_session_del(session); +} + +void test_nghttp2_session_on_goaway_received(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + my_user_data user_data; + nghttp2_frame frame; + int i; + nghttp2_mem *mem; + + mem = nghttp2_mem_default(); + user_data.frame_recv_cb_called = 0; + user_data.invalid_frame_recv_cb_called = 0; + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.on_frame_recv_callback = on_frame_recv_callback; + callbacks.on_invalid_frame_recv_callback = on_invalid_frame_recv_callback; + callbacks.on_stream_close_callback = on_stream_close_callback; + + nghttp2_session_client_new(&session, &callbacks, &user_data); + + for (i = 1; i <= 7; ++i) { + open_stream(session, i); + } + + nghttp2_frame_goaway_init(&frame.goaway, 3, NGHTTP2_PROTOCOL_ERROR, NULL, 0); + + user_data.stream_close_cb_called = 0; + + CU_ASSERT(0 == nghttp2_session_on_goaway_received(session, &frame)); + + CU_ASSERT(1 == user_data.frame_recv_cb_called); + CU_ASSERT(3 == session->remote_last_stream_id); + /* on_stream_close should be callsed for 2 times (stream 5 and 7) */ + CU_ASSERT(2 == user_data.stream_close_cb_called); + + CU_ASSERT(NULL != nghttp2_session_get_stream(session, 1)); + CU_ASSERT(NULL != nghttp2_session_get_stream(session, 2)); + CU_ASSERT(NULL != nghttp2_session_get_stream(session, 3)); + CU_ASSERT(NULL != nghttp2_session_get_stream(session, 4)); + CU_ASSERT(NULL == nghttp2_session_get_stream(session, 5)); + CU_ASSERT(NULL != nghttp2_session_get_stream(session, 6)); + CU_ASSERT(NULL == nghttp2_session_get_stream(session, 7)); + + nghttp2_frame_goaway_free(&frame.goaway, mem); + nghttp2_session_del(session); +} + +void test_nghttp2_session_on_window_update_received(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + my_user_data user_data; + nghttp2_frame frame; + nghttp2_stream *stream; + nghttp2_outbound_item *data_item; + nghttp2_mem *mem; + + mem = nghttp2_mem_default(); + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.on_frame_recv_callback = on_frame_recv_callback; + callbacks.on_invalid_frame_recv_callback = on_invalid_frame_recv_callback; + user_data.frame_recv_cb_called = 0; + user_data.invalid_frame_recv_cb_called = 0; + + nghttp2_session_client_new(&session, &callbacks, &user_data); + + stream = nghttp2_session_open_stream(session, 1, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, NGHTTP2_STREAM_OPENED, + NULL); + + data_item = create_data_ob_item(mem); + + CU_ASSERT(0 == nghttp2_stream_attach_item(stream, data_item, session)); + + nghttp2_frame_window_update_init(&frame.window_update, NGHTTP2_FLAG_NONE, 1, + 16 * 1024); + + CU_ASSERT(0 == nghttp2_session_on_window_update_received(session, &frame)); + CU_ASSERT(1 == user_data.frame_recv_cb_called); + CU_ASSERT(NGHTTP2_INITIAL_WINDOW_SIZE + 16 * 1024 == + stream->remote_window_size); + + CU_ASSERT(0 == + nghttp2_stream_defer_item( + stream, NGHTTP2_STREAM_FLAG_DEFERRED_FLOW_CONTROL, session)); + + CU_ASSERT(0 == nghttp2_session_on_window_update_received(session, &frame)); + CU_ASSERT(2 == user_data.frame_recv_cb_called); + CU_ASSERT(NGHTTP2_INITIAL_WINDOW_SIZE + 16 * 1024 * 2 == + stream->remote_window_size); + CU_ASSERT(0 == (stream->flags & NGHTTP2_STREAM_FLAG_DEFERRED_ALL)); + + nghttp2_frame_window_update_free(&frame.window_update); + + /* Receiving WINDOW_UPDATE on reserved (remote) stream is a + connection error */ + nghttp2_session_open_stream(session, 2, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, NGHTTP2_STREAM_RESERVED, NULL); + + nghttp2_frame_window_update_init(&frame.window_update, NGHTTP2_FLAG_NONE, 2, + 4096); + + CU_ASSERT(!(session->goaway_flags & NGHTTP2_GOAWAY_TERM_ON_SEND)); + CU_ASSERT(0 == nghttp2_session_on_window_update_received(session, &frame)); + CU_ASSERT(session->goaway_flags & NGHTTP2_GOAWAY_TERM_ON_SEND); + + nghttp2_frame_window_update_free(&frame.window_update); + + nghttp2_session_del(session); + + /* Receiving WINDOW_UPDATE on reserved (local) stream is allowed */ + nghttp2_session_server_new(&session, &callbacks, &user_data); + + stream = nghttp2_session_open_stream(session, 2, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, + NGHTTP2_STREAM_RESERVED, NULL); + + nghttp2_frame_window_update_init(&frame.window_update, NGHTTP2_FLAG_NONE, 2, + 4096); + + CU_ASSERT(0 == nghttp2_session_on_window_update_received(session, &frame)); + CU_ASSERT(!(session->goaway_flags & NGHTTP2_GOAWAY_TERM_ON_SEND)); + + CU_ASSERT(NGHTTP2_INITIAL_WINDOW_SIZE + 4096 == stream->remote_window_size); + + nghttp2_frame_window_update_free(&frame.window_update); + + nghttp2_session_del(session); +} + +void test_nghttp2_session_on_data_received(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + my_user_data user_data; + nghttp2_outbound_item *top; + nghttp2_stream *stream; + nghttp2_frame frame; + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + + nghttp2_session_client_new(&session, &callbacks, &user_data); + stream = nghttp2_session_open_stream(session, 2, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, + NGHTTP2_STREAM_OPENING, NULL); + + nghttp2_frame_hd_init(&frame.hd, 4096, NGHTTP2_DATA, NGHTTP2_FLAG_NONE, 2); + + CU_ASSERT(0 == nghttp2_session_on_data_received(session, &frame)); + CU_ASSERT(0 == stream->shut_flags); + + frame.hd.flags = NGHTTP2_FLAG_END_STREAM; + + CU_ASSERT(0 == nghttp2_session_on_data_received(session, &frame)); + CU_ASSERT(NGHTTP2_SHUT_RD == stream->shut_flags); + + /* If NGHTTP2_STREAM_CLOSING state, DATA frame is discarded. */ + nghttp2_session_open_stream(session, 4, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, NGHTTP2_STREAM_CLOSING, NULL); + + frame.hd.flags = NGHTTP2_FLAG_NONE; + frame.hd.stream_id = 4; + + CU_ASSERT(0 == nghttp2_session_on_data_received(session, &frame)); + CU_ASSERT(NULL == nghttp2_outbound_queue_top(&session->ob_reg)); + + /* Check INVALID_STREAM case: DATA frame with stream ID which does + not exist. */ + + frame.hd.stream_id = 6; + + CU_ASSERT(0 == nghttp2_session_on_data_received(session, &frame)); + top = nghttp2_outbound_queue_top(&session->ob_reg); + /* DATA against nonexistent stream is just ignored for now */ + CU_ASSERT(top == NULL); + /* CU_ASSERT(NGHTTP2_RST_STREAM == top->frame.hd.type); */ + /* CU_ASSERT(NGHTTP2_PROTOCOL_ERROR == top->frame.rst_stream.error_code); */ + + nghttp2_session_del(session); +} + +void test_nghttp2_session_send_headers_start_stream(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + nghttp2_outbound_item *item; + nghttp2_frame *frame; + nghttp2_stream *stream; + nghttp2_mem *mem; + + mem = nghttp2_mem_default(); + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.send_callback = null_send_callback; + + nghttp2_session_client_new(&session, &callbacks, NULL); + + item = mem->malloc(sizeof(nghttp2_outbound_item), NULL); + + nghttp2_outbound_item_init(item); + + frame = &item->frame; + + nghttp2_frame_headers_init(&frame->headers, NGHTTP2_FLAG_END_HEADERS, + session->next_stream_id, NGHTTP2_HCAT_REQUEST, + NULL, NULL, 0); + session->next_stream_id += 2; + + nghttp2_session_add_item(session, item); + CU_ASSERT(0 == nghttp2_session_send(session)); + stream = nghttp2_session_get_stream(session, 1); + CU_ASSERT(NGHTTP2_STREAM_OPENING == stream->state); + + nghttp2_session_del(session); +} + +void test_nghttp2_session_send_headers_reply(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + nghttp2_outbound_item *item; + nghttp2_frame *frame; + nghttp2_stream *stream; + nghttp2_mem *mem; + + mem = nghttp2_mem_default(); + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.send_callback = null_send_callback; + + CU_ASSERT(0 == nghttp2_session_client_new(&session, &callbacks, NULL)); + nghttp2_session_open_stream(session, 2, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, NGHTTP2_STREAM_OPENING, NULL); + + item = mem->malloc(sizeof(nghttp2_outbound_item), NULL); + + nghttp2_outbound_item_init(item); + + frame = &item->frame; + + nghttp2_frame_headers_init(&frame->headers, NGHTTP2_FLAG_END_HEADERS, 2, + NGHTTP2_HCAT_HEADERS, NULL, NULL, 0); + nghttp2_session_add_item(session, item); + CU_ASSERT(0 == nghttp2_session_send(session)); + stream = nghttp2_session_get_stream(session, 2); + CU_ASSERT(NGHTTP2_STREAM_OPENED == stream->state); + + nghttp2_session_del(session); +} + +void test_nghttp2_session_send_headers_frame_size_error(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + nghttp2_outbound_item *item; + nghttp2_frame *frame; + nghttp2_nv *nva; + ssize_t nvlen; + size_t vallen = NGHTTP2_HD_MAX_NV; + nghttp2_nv nv[28]; + size_t nnv = ARRLEN(nv); + size_t i; + my_user_data ud; + nghttp2_mem *mem; + + mem = nghttp2_mem_default(); + + for (i = 0; i < nnv; ++i) { + nv[i].name = (uint8_t *)"header"; + nv[i].namelen = strlen((const char *)nv[i].name); + nv[i].value = mem->malloc(vallen + 1, NULL); + memset(nv[i].value, '0' + (int)i, vallen); + nv[i].value[vallen] = '\0'; + nv[i].valuelen = vallen; + nv[i].flags = NGHTTP2_NV_FLAG_NONE; + } + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.send_callback = null_send_callback; + callbacks.on_frame_not_send_callback = on_frame_not_send_callback; + + nghttp2_session_client_new(&session, &callbacks, &ud); + nvlen = nnv; + nghttp2_nv_array_copy(&nva, nv, nvlen, mem); + + item = mem->malloc(sizeof(nghttp2_outbound_item), NULL); + + nghttp2_outbound_item_init(item); + + frame = &item->frame; + + nghttp2_frame_headers_init(&frame->headers, NGHTTP2_FLAG_END_HEADERS, + session->next_stream_id, NGHTTP2_HCAT_REQUEST, + NULL, nva, nvlen); + + session->next_stream_id += 2; + + nghttp2_session_add_item(session, item); + + ud.frame_not_send_cb_called = 0; + + CU_ASSERT(0 == nghttp2_session_send(session)); + + CU_ASSERT(1 == ud.frame_not_send_cb_called); + CU_ASSERT(NGHTTP2_HEADERS == ud.not_sent_frame_type); + CU_ASSERT(NGHTTP2_ERR_FRAME_SIZE_ERROR == ud.not_sent_error); + + for (i = 0; i < nnv; ++i) { + mem->free(nv[i].value, NULL); + } + nghttp2_session_del(session); +} + +void test_nghttp2_session_send_headers_push_reply(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + nghttp2_outbound_item *item; + nghttp2_frame *frame; + nghttp2_stream *stream; + nghttp2_mem *mem; + + mem = nghttp2_mem_default(); + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.send_callback = null_send_callback; + + CU_ASSERT(0 == nghttp2_session_server_new(&session, &callbacks, NULL)); + nghttp2_session_open_stream(session, 2, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, NGHTTP2_STREAM_RESERVED, NULL); + + item = mem->malloc(sizeof(nghttp2_outbound_item), NULL); + + nghttp2_outbound_item_init(item); + + frame = &item->frame; + + nghttp2_frame_headers_init(&frame->headers, NGHTTP2_FLAG_END_HEADERS, 2, + NGHTTP2_HCAT_HEADERS, NULL, NULL, 0); + nghttp2_session_add_item(session, item); + CU_ASSERT(0 == session->num_outgoing_streams); + CU_ASSERT(0 == nghttp2_session_send(session)); + CU_ASSERT(1 == session->num_outgoing_streams); + stream = nghttp2_session_get_stream(session, 2); + CU_ASSERT(NGHTTP2_STREAM_OPENED == stream->state); + CU_ASSERT(0 == (stream->flags & NGHTTP2_STREAM_FLAG_PUSH)); + nghttp2_session_del(session); +} + +void test_nghttp2_session_send_rst_stream(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + my_user_data user_data; + nghttp2_outbound_item *item; + nghttp2_frame *frame; + nghttp2_mem *mem; + + mem = nghttp2_mem_default(); + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.send_callback = null_send_callback; + nghttp2_session_client_new(&session, &callbacks, &user_data); + nghttp2_session_open_stream(session, 1, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, NGHTTP2_STREAM_OPENING, NULL); + + item = mem->malloc(sizeof(nghttp2_outbound_item), NULL); + + nghttp2_outbound_item_init(item); + + frame = &item->frame; + + nghttp2_frame_rst_stream_init(&frame->rst_stream, 1, NGHTTP2_PROTOCOL_ERROR); + nghttp2_session_add_item(session, item); + CU_ASSERT(0 == nghttp2_session_send(session)); + + CU_ASSERT(NULL == nghttp2_session_get_stream(session, 1)); + + nghttp2_session_del(session); +} + +void test_nghttp2_session_send_push_promise(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + nghttp2_outbound_item *item; + nghttp2_frame *frame; + nghttp2_stream *stream; + nghttp2_settings_entry iv; + my_user_data ud; + nghttp2_mem *mem; + + mem = nghttp2_mem_default(); + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.send_callback = null_send_callback; + callbacks.on_frame_not_send_callback = on_frame_not_send_callback; + + nghttp2_session_server_new(&session, &callbacks, &ud); + nghttp2_session_open_stream(session, 1, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, NGHTTP2_STREAM_OPENING, NULL); + + item = mem->malloc(sizeof(nghttp2_outbound_item), NULL); + + nghttp2_outbound_item_init(item); + + frame = &item->frame; + + nghttp2_frame_push_promise_init(&frame->push_promise, + NGHTTP2_FLAG_END_HEADERS, 1, + session->next_stream_id, NULL, 0); + + session->next_stream_id += 2; + + nghttp2_session_add_item(session, item); + + CU_ASSERT(0 == nghttp2_session_send(session)); + stream = nghttp2_session_get_stream(session, 2); + CU_ASSERT(NGHTTP2_STREAM_RESERVED == stream->state); + + /* Received ENABLE_PUSH = 0 */ + iv.settings_id = NGHTTP2_SETTINGS_ENABLE_PUSH; + iv.value = 0; + frame = mem->malloc(sizeof(nghttp2_frame), NULL); + nghttp2_frame_settings_init(&frame->settings, NGHTTP2_FLAG_NONE, + dup_iv(&iv, 1), 1); + nghttp2_session_on_settings_received(session, frame, 1); + nghttp2_frame_settings_free(&frame->settings, mem); + mem->free(frame, NULL); + + item = mem->malloc(sizeof(nghttp2_outbound_item), NULL); + + nghttp2_outbound_item_init(item); + + frame = &item->frame; + + nghttp2_frame_push_promise_init(&frame->push_promise, + NGHTTP2_FLAG_END_HEADERS, 1, -1, NULL, 0); + nghttp2_session_add_item(session, item); + + ud.frame_not_send_cb_called = 0; + CU_ASSERT(0 == nghttp2_session_send(session)); + + CU_ASSERT(1 == ud.frame_not_send_cb_called); + CU_ASSERT(NGHTTP2_PUSH_PROMISE == ud.not_sent_frame_type); + CU_ASSERT(NGHTTP2_ERR_PUSH_DISABLED == ud.not_sent_error); + + nghttp2_session_del(session); + + /* PUSH_PROMISE from client is error */ + nghttp2_session_client_new(&session, &callbacks, &ud); + nghttp2_session_open_stream(session, 1, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, NGHTTP2_STREAM_OPENING, NULL); + item = mem->malloc(sizeof(nghttp2_outbound_item), NULL); + + nghttp2_outbound_item_init(item); + + frame = &item->frame; + + nghttp2_frame_push_promise_init(&frame->push_promise, + NGHTTP2_FLAG_END_HEADERS, 1, -1, NULL, 0); + nghttp2_session_add_item(session, item); + + CU_ASSERT(0 == nghttp2_session_send(session)); + CU_ASSERT(NULL == nghttp2_session_get_stream(session, 3)); + + nghttp2_session_del(session); +} + +void test_nghttp2_session_is_my_stream_id(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + nghttp2_session_server_new(&session, &callbacks, NULL); + + CU_ASSERT(0 == nghttp2_session_is_my_stream_id(session, 0)); + CU_ASSERT(0 == nghttp2_session_is_my_stream_id(session, 1)); + CU_ASSERT(1 == nghttp2_session_is_my_stream_id(session, 2)); + + nghttp2_session_del(session); + + nghttp2_session_client_new(&session, &callbacks, NULL); + + CU_ASSERT(0 == nghttp2_session_is_my_stream_id(session, 0)); + CU_ASSERT(1 == nghttp2_session_is_my_stream_id(session, 1)); + CU_ASSERT(0 == nghttp2_session_is_my_stream_id(session, 2)); + + nghttp2_session_del(session); +} + +void test_nghttp2_session_upgrade(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + uint8_t settings_payload[128]; + size_t settings_payloadlen; + nghttp2_settings_entry iv[16]; + nghttp2_stream *stream; + nghttp2_outbound_item *item; + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.send_callback = null_send_callback; + iv[0].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS; + iv[0].value = 1; + iv[1].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE; + iv[1].value = 4095; + settings_payloadlen = nghttp2_pack_settings_payload( + settings_payload, sizeof(settings_payload), iv, 2); + + /* Check client side */ + nghttp2_session_client_new(&session, &callbacks, NULL); + CU_ASSERT(0 == nghttp2_session_upgrade(session, settings_payload, + settings_payloadlen, &callbacks)); + stream = nghttp2_session_get_stream(session, 1); + CU_ASSERT(stream != NULL); + CU_ASSERT(&callbacks == stream->stream_user_data); + CU_ASSERT(NGHTTP2_SHUT_WR == stream->shut_flags); + item = nghttp2_session_get_next_ob_item(session); + CU_ASSERT(NGHTTP2_SETTINGS == item->frame.hd.type); + CU_ASSERT(2 == item->frame.settings.niv); + CU_ASSERT(NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS == + item->frame.settings.iv[0].settings_id); + CU_ASSERT(1 == item->frame.settings.iv[0].value); + CU_ASSERT(NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE == + item->frame.settings.iv[1].settings_id); + CU_ASSERT(4095 == item->frame.settings.iv[1].value); + + /* Call nghttp2_session_upgrade() again is error */ + CU_ASSERT(NGHTTP2_ERR_PROTO == + nghttp2_session_upgrade(session, settings_payload, + settings_payloadlen, &callbacks)); + nghttp2_session_del(session); + + /* Check server side */ + nghttp2_session_server_new(&session, &callbacks, NULL); + CU_ASSERT(0 == nghttp2_session_upgrade(session, settings_payload, + settings_payloadlen, &callbacks)); + stream = nghttp2_session_get_stream(session, 1); + CU_ASSERT(stream != NULL); + CU_ASSERT(NULL == stream->stream_user_data); + CU_ASSERT(NGHTTP2_SHUT_RD == stream->shut_flags); + CU_ASSERT(NULL == nghttp2_session_get_next_ob_item(session)); + CU_ASSERT(1 == session->remote_settings.max_concurrent_streams); + CU_ASSERT(4095 == session->remote_settings.initial_window_size); + /* Call nghttp2_session_upgrade() again is error */ + CU_ASSERT(NGHTTP2_ERR_PROTO == + nghttp2_session_upgrade(session, settings_payload, + settings_payloadlen, &callbacks)); + nghttp2_session_del(session); + + /* Empty SETTINGS is OK */ + settings_payloadlen = nghttp2_pack_settings_payload( + settings_payload, sizeof(settings_payload), NULL, 0); + + nghttp2_session_client_new(&session, &callbacks, NULL); + CU_ASSERT(0 == nghttp2_session_upgrade(session, settings_payload, + settings_payloadlen, NULL)); + nghttp2_session_del(session); +} + +void test_nghttp2_session_reprioritize_stream(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + my_user_data ud; + nghttp2_stream *stream; + nghttp2_stream *dep_stream; + nghttp2_priority_spec pri_spec; + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.send_callback = block_count_send_callback; + + nghttp2_session_server_new(&session, &callbacks, &ud); + + stream = nghttp2_session_open_stream(session, 1, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, + NGHTTP2_STREAM_OPENING, NULL); + + nghttp2_priority_spec_init(&pri_spec, 0, 10, 0); + + nghttp2_session_reprioritize_stream(session, stream, &pri_spec); + + CU_ASSERT(10 == stream->weight); + CU_ASSERT(NULL == stream->dep_prev); + + /* If depenency to idle stream which is not in depdenency tree yet */ + + nghttp2_priority_spec_init(&pri_spec, 3, 99, 0); + + nghttp2_session_reprioritize_stream(session, stream, &pri_spec); + + CU_ASSERT(99 == stream->weight); + CU_ASSERT(3 == stream->dep_prev->stream_id); + + dep_stream = nghttp2_session_get_stream_raw(session, 3); + + CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT == dep_stream->weight); + + dep_stream = open_stream(session, 3); + + /* Change weight */ + pri_spec.weight = 128; + + nghttp2_session_reprioritize_stream(session, stream, &pri_spec); + + CU_ASSERT(128 == stream->weight); + CU_ASSERT(dep_stream == stream->dep_prev); + + /* Test circular dependency; stream 1 is first removed and becomes + root. Then stream 3 depends on it. */ + nghttp2_priority_spec_init(&pri_spec, 1, 1, 0); + + nghttp2_session_reprioritize_stream(session, dep_stream, &pri_spec); + + CU_ASSERT(1 == dep_stream->weight); + CU_ASSERT(stream == dep_stream->dep_prev); + + /* Making priority to closed stream will result in default + priority */ + session->last_recv_stream_id = 9; + + nghttp2_priority_spec_init(&pri_spec, 5, 5, 0); + + nghttp2_session_reprioritize_stream(session, stream, &pri_spec); + + CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT == stream->weight); + + nghttp2_session_del(session); +} + +void test_nghttp2_session_reprioritize_stream_with_idle_stream_dep(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + nghttp2_stream *stream; + nghttp2_priority_spec pri_spec; + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.send_callback = block_count_send_callback; + + nghttp2_session_server_new(&session, &callbacks, NULL); + + stream = nghttp2_session_open_stream(session, 1, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, + NGHTTP2_STREAM_OPENING, NULL); + + session->pending_local_max_concurrent_stream = 1; + + nghttp2_priority_spec_init(&pri_spec, 101, 10, 0); + + nghttp2_session_reprioritize_stream(session, stream, &pri_spec); + + /* idle stream is not counteed to max concurrent streams */ + + CU_ASSERT(10 == stream->weight); + CU_ASSERT(101 == stream->dep_prev->stream_id); + + stream = nghttp2_session_get_stream_raw(session, 101); + + CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT == stream->weight); + + nghttp2_session_del(session); +} + +void test_nghttp2_submit_data(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + nghttp2_data_provider data_prd; + my_user_data ud; + nghttp2_frame *frame; + nghttp2_frame_hd hd; + nghttp2_active_outbound_item *aob; + nghttp2_bufs *framebufs; + nghttp2_buf *buf; + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.send_callback = block_count_send_callback; + + data_prd.read_callback = fixed_length_data_source_read_callback; + ud.data_source_length = NGHTTP2_DATA_PAYLOADLEN * 2; + CU_ASSERT(0 == nghttp2_session_client_new(&session, &callbacks, &ud)); + aob = &session->aob; + framebufs = &aob->framebufs; + + nghttp2_session_open_stream(session, 1, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, NGHTTP2_STREAM_OPENING, NULL); + CU_ASSERT( + 0 == nghttp2_submit_data(session, NGHTTP2_FLAG_END_STREAM, 1, &data_prd)); + + ud.block_count = 0; + CU_ASSERT(0 == nghttp2_session_send(session)); + frame = &aob->item->frame; + + buf = &framebufs->head->buf; + nghttp2_frame_unpack_frame_hd(&hd, buf->pos); + + CU_ASSERT(NGHTTP2_FLAG_NONE == hd.flags); + CU_ASSERT(NGHTTP2_FLAG_NONE == frame->hd.flags); + /* aux_data.data.flags has these flags */ + CU_ASSERT(NGHTTP2_FLAG_END_STREAM == aob->item->aux_data.data.flags); + + nghttp2_session_del(session); +} + +void test_nghttp2_submit_data_read_length_too_large(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + nghttp2_data_provider data_prd; + my_user_data ud; + nghttp2_frame *frame; + nghttp2_frame_hd hd; + nghttp2_active_outbound_item *aob; + nghttp2_bufs *framebufs; + nghttp2_buf *buf; + size_t payloadlen; + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.send_callback = block_count_send_callback; + callbacks.read_length_callback = too_large_data_source_length_callback; + + data_prd.read_callback = fixed_length_data_source_read_callback; + ud.data_source_length = NGHTTP2_DATA_PAYLOADLEN * 2; + CU_ASSERT(0 == nghttp2_session_client_new(&session, &callbacks, &ud)); + aob = &session->aob; + framebufs = &aob->framebufs; + + nghttp2_session_open_stream(session, 1, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, NGHTTP2_STREAM_OPENING, NULL); + CU_ASSERT( + 0 == nghttp2_submit_data(session, NGHTTP2_FLAG_END_STREAM, 1, &data_prd)); + + ud.block_count = 0; + CU_ASSERT(0 == nghttp2_session_send(session)); + frame = &aob->item->frame; + + buf = &framebufs->head->buf; + nghttp2_frame_unpack_frame_hd(&hd, buf->pos); + + CU_ASSERT(NGHTTP2_FLAG_NONE == hd.flags); + CU_ASSERT(NGHTTP2_FLAG_NONE == frame->hd.flags); + CU_ASSERT(16384 == hd.length) + /* aux_data.data.flags has these flags */ + CU_ASSERT(NGHTTP2_FLAG_END_STREAM == aob->item->aux_data.data.flags); + + nghttp2_session_del(session); + + /* Check that buffers are expanded */ + CU_ASSERT(0 == nghttp2_session_client_new(&session, &callbacks, &ud)); + + ud.data_source_length = NGHTTP2_MAX_FRAME_SIZE_MAX; + + session->remote_settings.max_frame_size = NGHTTP2_MAX_FRAME_SIZE_MAX; + + nghttp2_session_open_stream(session, 1, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, NGHTTP2_STREAM_OPENING, NULL); + CU_ASSERT( + 0 == nghttp2_submit_data(session, NGHTTP2_FLAG_END_STREAM, 1, &data_prd)); + + ud.block_count = 0; + CU_ASSERT(0 == nghttp2_session_send(session)); + + aob = &session->aob; + + frame = &aob->item->frame; + + framebufs = &aob->framebufs; + + buf = &framebufs->head->buf; + nghttp2_frame_unpack_frame_hd(&hd, buf->pos); + + payloadlen = nghttp2_min(NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE, + NGHTTP2_INITIAL_WINDOW_SIZE); + + CU_ASSERT(NGHTTP2_FRAME_HDLEN + 1 + payloadlen == + (size_t)nghttp2_buf_cap(buf)); + CU_ASSERT(NGHTTP2_FLAG_NONE == hd.flags); + CU_ASSERT(NGHTTP2_FLAG_NONE == frame->hd.flags); + CU_ASSERT(payloadlen == hd.length); + /* aux_data.data.flags has these flags */ + CU_ASSERT(NGHTTP2_FLAG_END_STREAM == aob->item->aux_data.data.flags); + + nghttp2_session_del(session); +} + +void test_nghttp2_submit_data_read_length_smallest(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + nghttp2_data_provider data_prd; + my_user_data ud; + nghttp2_frame *frame; + nghttp2_frame_hd hd; + nghttp2_active_outbound_item *aob; + nghttp2_bufs *framebufs; + nghttp2_buf *buf; + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.send_callback = block_count_send_callback; + callbacks.read_length_callback = smallest_length_data_source_length_callback; + + data_prd.read_callback = fixed_length_data_source_read_callback; + ud.data_source_length = NGHTTP2_DATA_PAYLOADLEN * 2; + CU_ASSERT(0 == nghttp2_session_client_new(&session, &callbacks, &ud)); + aob = &session->aob; + framebufs = &aob->framebufs; + + nghttp2_session_open_stream(session, 1, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, NGHTTP2_STREAM_OPENING, NULL); + CU_ASSERT( + 0 == nghttp2_submit_data(session, NGHTTP2_FLAG_END_STREAM, 1, &data_prd)); + + ud.block_count = 0; + CU_ASSERT(0 == nghttp2_session_send(session)); + frame = &aob->item->frame; + + buf = &framebufs->head->buf; + nghttp2_frame_unpack_frame_hd(&hd, buf->pos); + + CU_ASSERT(NGHTTP2_FLAG_NONE == hd.flags); + CU_ASSERT(NGHTTP2_FLAG_NONE == frame->hd.flags); + CU_ASSERT(1 == hd.length) + /* aux_data.data.flags has these flags */ + CU_ASSERT(NGHTTP2_FLAG_END_STREAM == aob->item->aux_data.data.flags); + + nghttp2_session_del(session); +} + +static ssize_t submit_data_twice_data_source_read_callback( + nghttp2_session *session _U_, int32_t stream_id _U_, uint8_t *buf _U_, + size_t len, uint32_t *data_flags, nghttp2_data_source *source _U_, + void *user_data _U_) { + *data_flags |= NGHTTP2_DATA_FLAG_EOF; + return nghttp2_min(len, 16); +} + +static int submit_data_twice_on_frame_send_callback(nghttp2_session *session, + const nghttp2_frame *frame, + void *user_data _U_) { + static int called = 0; + int rv; + nghttp2_data_provider data_prd; + + if (called == 0) { + called = 1; + + data_prd.read_callback = submit_data_twice_data_source_read_callback; + + rv = nghttp2_submit_data(session, NGHTTP2_FLAG_END_STREAM, + frame->hd.stream_id, &data_prd); + CU_ASSERT(0 == rv); + } + + return 0; +} + +void test_nghttp2_submit_data_twice(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + nghttp2_data_provider data_prd; + my_user_data ud; + accumulator acc; + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.send_callback = accumulator_send_callback; + callbacks.on_frame_send_callback = submit_data_twice_on_frame_send_callback; + + data_prd.read_callback = submit_data_twice_data_source_read_callback; + + acc.length = 0; + ud.acc = &acc; + + CU_ASSERT(0 == nghttp2_session_client_new(&session, &callbacks, &ud)); + + nghttp2_session_open_stream(session, 1, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, NGHTTP2_STREAM_OPENING, NULL); + + CU_ASSERT(0 == nghttp2_submit_data(session, NGHTTP2_FLAG_NONE, 1, &data_prd)); + + CU_ASSERT(0 == nghttp2_session_send(session)); + + /* We should have sent 2 DATA frame with 16 bytes payload each */ + CU_ASSERT(NGHTTP2_FRAME_HDLEN * 2 + 16 * 2 == acc.length); + + nghttp2_session_del(session); +} + +void test_nghttp2_submit_request_with_data(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + nghttp2_data_provider data_prd; + my_user_data ud; + nghttp2_outbound_item *item; + nghttp2_mem *mem; + + mem = nghttp2_mem_default(); + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.send_callback = null_send_callback; + + data_prd.read_callback = fixed_length_data_source_read_callback; + ud.data_source_length = 64 * 1024 - 1; + CU_ASSERT(0 == nghttp2_session_client_new(&session, &callbacks, &ud)); + CU_ASSERT(1 == nghttp2_submit_request(session, NULL, reqnv, ARRLEN(reqnv), + &data_prd, NULL)); + item = nghttp2_session_get_next_ob_item(session); + CU_ASSERT(ARRLEN(reqnv) == item->frame.headers.nvlen); + assert_nv_equal(reqnv, item->frame.headers.nva, item->frame.headers.nvlen, + mem); + CU_ASSERT(0 == nghttp2_session_send(session)); + CU_ASSERT(0 == ud.data_source_length); + + nghttp2_session_del(session); +} + +void test_nghttp2_submit_request_without_data(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + accumulator acc; + nghttp2_data_provider data_prd = {{-1}, NULL}; + nghttp2_outbound_item *item; + my_user_data ud; + nghttp2_frame frame; + nghttp2_hd_inflater inflater; + nva_out out; + nghttp2_bufs bufs; + nghttp2_mem *mem; + + mem = nghttp2_mem_default(); + frame_pack_bufs_init(&bufs); + + nva_out_init(&out); + acc.length = 0; + ud.acc = &acc; + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.send_callback = accumulator_send_callback; + CU_ASSERT(0 == nghttp2_session_client_new(&session, &callbacks, &ud)); + + nghttp2_hd_inflate_init(&inflater, mem); + CU_ASSERT(1 == nghttp2_submit_request(session, NULL, reqnv, ARRLEN(reqnv), + &data_prd, NULL)); + item = nghttp2_session_get_next_ob_item(session); + CU_ASSERT(ARRLEN(reqnv) == item->frame.headers.nvlen); + assert_nv_equal(reqnv, item->frame.headers.nva, item->frame.headers.nvlen, + mem); + CU_ASSERT(item->frame.hd.flags & NGHTTP2_FLAG_END_STREAM); + + CU_ASSERT(0 == nghttp2_session_send(session)); + CU_ASSERT(0 == unpack_frame(&frame, acc.buf, acc.length)); + + nghttp2_bufs_add(&bufs, acc.buf, acc.length); + inflate_hd(&inflater, &out, &bufs, NGHTTP2_FRAME_HDLEN, mem); + + CU_ASSERT(ARRLEN(reqnv) == out.nvlen); + assert_nv_equal(reqnv, out.nva, out.nvlen, mem); + nghttp2_frame_headers_free(&frame.headers, mem); + nva_out_reset(&out, mem); + + nghttp2_bufs_free(&bufs); + nghttp2_hd_inflate_free(&inflater); + nghttp2_session_del(session); +} + +void test_nghttp2_submit_response_with_data(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + nghttp2_data_provider data_prd; + my_user_data ud; + nghttp2_outbound_item *item; + nghttp2_mem *mem; + + mem = nghttp2_mem_default(); + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.send_callback = null_send_callback; + + data_prd.read_callback = fixed_length_data_source_read_callback; + ud.data_source_length = 64 * 1024 - 1; + CU_ASSERT(0 == nghttp2_session_server_new(&session, &callbacks, &ud)); + nghttp2_session_open_stream(session, 1, NGHTTP2_FLAG_END_STREAM, + &pri_spec_default, NGHTTP2_STREAM_OPENING, NULL); + CU_ASSERT(0 == nghttp2_submit_response(session, 1, resnv, ARRLEN(resnv), + &data_prd)); + item = nghttp2_session_get_next_ob_item(session); + CU_ASSERT(ARRLEN(resnv) == item->frame.headers.nvlen); + assert_nv_equal(resnv, item->frame.headers.nva, item->frame.headers.nvlen, + mem); + CU_ASSERT(0 == nghttp2_session_send(session)); + CU_ASSERT(0 == ud.data_source_length); + + nghttp2_session_del(session); +} + +void test_nghttp2_submit_response_without_data(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + accumulator acc; + nghttp2_data_provider data_prd = {{-1}, NULL}; + nghttp2_outbound_item *item; + my_user_data ud; + nghttp2_frame frame; + nghttp2_hd_inflater inflater; + nva_out out; + nghttp2_bufs bufs; + nghttp2_mem *mem; + + mem = nghttp2_mem_default(); + frame_pack_bufs_init(&bufs); + + nva_out_init(&out); + acc.length = 0; + ud.acc = &acc; + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.send_callback = accumulator_send_callback; + CU_ASSERT(0 == nghttp2_session_server_new(&session, &callbacks, &ud)); + + nghttp2_hd_inflate_init(&inflater, mem); + nghttp2_session_open_stream(session, 1, NGHTTP2_FLAG_END_STREAM, + &pri_spec_default, NGHTTP2_STREAM_OPENING, NULL); + CU_ASSERT(0 == nghttp2_submit_response(session, 1, resnv, ARRLEN(resnv), + &data_prd)); + item = nghttp2_session_get_next_ob_item(session); + CU_ASSERT(ARRLEN(resnv) == item->frame.headers.nvlen); + assert_nv_equal(resnv, item->frame.headers.nva, item->frame.headers.nvlen, + mem); + CU_ASSERT(item->frame.hd.flags & NGHTTP2_FLAG_END_STREAM); + + CU_ASSERT(0 == nghttp2_session_send(session)); + CU_ASSERT(0 == unpack_frame(&frame, acc.buf, acc.length)); + + nghttp2_bufs_add(&bufs, acc.buf, acc.length); + inflate_hd(&inflater, &out, &bufs, NGHTTP2_FRAME_HDLEN, mem); + + CU_ASSERT(ARRLEN(resnv) == out.nvlen); + assert_nv_equal(resnv, out.nva, out.nvlen, mem); + + nva_out_reset(&out, mem); + nghttp2_bufs_free(&bufs); + nghttp2_frame_headers_free(&frame.headers, mem); + nghttp2_hd_inflate_free(&inflater); + nghttp2_session_del(session); +} + +void test_nghttp2_submit_trailer(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + accumulator acc; + nghttp2_data_provider data_prd; + nghttp2_outbound_item *item; + my_user_data ud; + nghttp2_frame frame; + nghttp2_hd_inflater inflater; + nva_out out; + nghttp2_bufs bufs; + nghttp2_mem *mem; + + mem = nghttp2_mem_default(); + frame_pack_bufs_init(&bufs); + + data_prd.read_callback = no_end_stream_data_source_read_callback; + nva_out_init(&out); + acc.length = 0; + ud.acc = &acc; + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.send_callback = null_send_callback; + CU_ASSERT(0 == nghttp2_session_server_new(&session, &callbacks, &ud)); + + nghttp2_hd_inflate_init(&inflater, mem); + nghttp2_session_open_stream(session, 1, NGHTTP2_FLAG_END_STREAM, + &pri_spec_default, NGHTTP2_STREAM_OPENING, NULL); + CU_ASSERT(0 == nghttp2_submit_response(session, 1, resnv, ARRLEN(resnv), + &data_prd)); + CU_ASSERT(0 == nghttp2_session_send(session)); + + CU_ASSERT(0 == + nghttp2_submit_trailer(session, 1, trailernv, ARRLEN(trailernv))); + + session->callbacks.send_callback = accumulator_send_callback; + + item = nghttp2_session_get_next_ob_item(session); + CU_ASSERT(NGHTTP2_HEADERS == item->frame.hd.type); + CU_ASSERT(NGHTTP2_HCAT_HEADERS == item->frame.headers.cat); + CU_ASSERT(item->frame.hd.flags & NGHTTP2_FLAG_END_STREAM); + + CU_ASSERT(0 == nghttp2_session_send(session)); + CU_ASSERT(0 == unpack_frame(&frame, acc.buf, acc.length)); + + nghttp2_bufs_add(&bufs, acc.buf, acc.length); + inflate_hd(&inflater, &out, &bufs, NGHTTP2_FRAME_HDLEN, mem); + + CU_ASSERT(ARRLEN(trailernv) == out.nvlen); + assert_nv_equal(trailernv, out.nva, out.nvlen, mem); + + nva_out_reset(&out, mem); + nghttp2_bufs_free(&bufs); + nghttp2_frame_headers_free(&frame.headers, mem); + nghttp2_hd_inflate_free(&inflater); + nghttp2_session_del(session); +} + +void test_nghttp2_submit_headers_start_stream(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + nghttp2_outbound_item *item; + nghttp2_mem *mem; + + mem = nghttp2_mem_default(); + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + CU_ASSERT(0 == nghttp2_session_client_new(&session, &callbacks, NULL)); + CU_ASSERT(1 == nghttp2_submit_headers(session, NGHTTP2_FLAG_END_STREAM, -1, + NULL, reqnv, ARRLEN(reqnv), NULL)); + item = nghttp2_session_get_next_ob_item(session); + CU_ASSERT(ARRLEN(reqnv) == item->frame.headers.nvlen); + assert_nv_equal(reqnv, item->frame.headers.nva, item->frame.headers.nvlen, + mem); + CU_ASSERT((NGHTTP2_FLAG_END_HEADERS | NGHTTP2_FLAG_END_STREAM) == + item->frame.hd.flags); + CU_ASSERT(0 == (item->frame.hd.flags & NGHTTP2_FLAG_PRIORITY)); + + nghttp2_session_del(session); +} + +void test_nghttp2_submit_headers_reply(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + my_user_data ud; + nghttp2_outbound_item *item; + nghttp2_stream *stream; + nghttp2_mem *mem; + + mem = nghttp2_mem_default(); + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.send_callback = null_send_callback; + callbacks.on_frame_send_callback = on_frame_send_callback; + + CU_ASSERT(0 == nghttp2_session_server_new(&session, &callbacks, &ud)); + CU_ASSERT(0 == nghttp2_submit_headers(session, NGHTTP2_FLAG_END_STREAM, 1, + NULL, resnv, ARRLEN(resnv), NULL)); + item = nghttp2_session_get_next_ob_item(session); + CU_ASSERT(ARRLEN(resnv) == item->frame.headers.nvlen); + assert_nv_equal(resnv, item->frame.headers.nva, item->frame.headers.nvlen, + mem); + CU_ASSERT((NGHTTP2_FLAG_END_STREAM | NGHTTP2_FLAG_END_HEADERS) == + item->frame.hd.flags); + + ud.frame_send_cb_called = 0; + ud.sent_frame_type = 0; + /* The transimission will be canceled because the stream 1 is not + open. */ + CU_ASSERT(0 == nghttp2_session_send(session)); + CU_ASSERT(0 == ud.frame_send_cb_called); + + stream = nghttp2_session_open_stream(session, 1, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, + NGHTTP2_STREAM_OPENING, NULL); + + CU_ASSERT(0 == nghttp2_submit_headers(session, NGHTTP2_FLAG_END_STREAM, 1, + NULL, resnv, ARRLEN(resnv), NULL)); + CU_ASSERT(0 == nghttp2_session_send(session)); + CU_ASSERT(1 == ud.frame_send_cb_called); + CU_ASSERT(NGHTTP2_HEADERS == ud.sent_frame_type); + CU_ASSERT(stream->shut_flags & NGHTTP2_SHUT_WR); + + nghttp2_session_del(session); +} + +void test_nghttp2_submit_headers_push_reply(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + my_user_data ud; + nghttp2_stream *stream; + int foo; + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.send_callback = null_send_callback; + callbacks.on_frame_send_callback = on_frame_send_callback; + + CU_ASSERT(0 == nghttp2_session_server_new(&session, &callbacks, &ud)); + stream = nghttp2_session_open_stream(session, 2, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, + NGHTTP2_STREAM_RESERVED, NULL); + CU_ASSERT(0 == nghttp2_submit_headers(session, NGHTTP2_FLAG_NONE, 2, NULL, + resnv, ARRLEN(resnv), &foo)); + + ud.frame_send_cb_called = 0; + ud.sent_frame_type = 0; + CU_ASSERT(0 == nghttp2_session_send(session)); + CU_ASSERT(1 == ud.frame_send_cb_called); + CU_ASSERT(NGHTTP2_HEADERS == ud.sent_frame_type); + CU_ASSERT(NGHTTP2_STREAM_OPENED == stream->state); + CU_ASSERT(&foo == stream->stream_user_data); + + nghttp2_session_del(session); + + /* Sending HEADERS from client against stream in reserved state is + error */ + CU_ASSERT(0 == nghttp2_session_client_new(&session, &callbacks, &ud)); + nghttp2_session_open_stream(session, 2, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, NGHTTP2_STREAM_RESERVED, NULL); + CU_ASSERT(0 == nghttp2_submit_headers(session, NGHTTP2_FLAG_NONE, 2, NULL, + reqnv, ARRLEN(reqnv), NULL)); + + ud.frame_send_cb_called = 0; + ud.sent_frame_type = 0; + CU_ASSERT(0 == nghttp2_session_send(session)); + CU_ASSERT(0 == ud.frame_send_cb_called); + + nghttp2_session_del(session); +} + +void test_nghttp2_submit_headers(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + my_user_data ud; + nghttp2_outbound_item *item; + nghttp2_stream *stream; + accumulator acc; + nghttp2_frame frame; + nghttp2_hd_inflater inflater; + nva_out out; + nghttp2_bufs bufs; + nghttp2_mem *mem; + + mem = nghttp2_mem_default(); + frame_pack_bufs_init(&bufs); + + nva_out_init(&out); + acc.length = 0; + ud.acc = &acc; + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.send_callback = accumulator_send_callback; + callbacks.on_frame_send_callback = on_frame_send_callback; + + CU_ASSERT(0 == nghttp2_session_client_new(&session, &callbacks, &ud)); + + nghttp2_hd_inflate_init(&inflater, mem); + CU_ASSERT(0 == nghttp2_submit_headers(session, NGHTTP2_FLAG_END_STREAM, 1, + NULL, reqnv, ARRLEN(reqnv), NULL)); + item = nghttp2_session_get_next_ob_item(session); + CU_ASSERT(ARRLEN(reqnv) == item->frame.headers.nvlen); + assert_nv_equal(reqnv, item->frame.headers.nva, item->frame.headers.nvlen, + mem); + CU_ASSERT((NGHTTP2_FLAG_END_STREAM | NGHTTP2_FLAG_END_HEADERS) == + item->frame.hd.flags); + + ud.frame_send_cb_called = 0; + ud.sent_frame_type = 0; + /* The transimission will be canceled because the stream 1 is not + open. */ + CU_ASSERT(0 == nghttp2_session_send(session)); + CU_ASSERT(0 == ud.frame_send_cb_called); + + stream = nghttp2_session_open_stream(session, 1, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, + NGHTTP2_STREAM_OPENING, NULL); + + CU_ASSERT(0 == nghttp2_submit_headers(session, NGHTTP2_FLAG_END_STREAM, 1, + NULL, reqnv, ARRLEN(reqnv), NULL)); + CU_ASSERT(0 == nghttp2_session_send(session)); + CU_ASSERT(1 == ud.frame_send_cb_called); + CU_ASSERT(NGHTTP2_HEADERS == ud.sent_frame_type); + CU_ASSERT(stream->shut_flags & NGHTTP2_SHUT_WR); + + CU_ASSERT(0 == unpack_frame(&frame, acc.buf, acc.length)); + + nghttp2_bufs_add(&bufs, acc.buf, acc.length); + inflate_hd(&inflater, &out, &bufs, NGHTTP2_FRAME_HDLEN, mem); + + CU_ASSERT(ARRLEN(reqnv) == out.nvlen); + assert_nv_equal(reqnv, out.nva, out.nvlen, mem); + + nva_out_reset(&out, mem); + nghttp2_bufs_free(&bufs); + nghttp2_frame_headers_free(&frame.headers, mem); + + nghttp2_hd_inflate_free(&inflater); + nghttp2_session_del(session); +} + +void test_nghttp2_submit_headers_continuation(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + nghttp2_nv nv[] = { + MAKE_NV("h1", ""), MAKE_NV("h1", ""), MAKE_NV("h1", ""), + MAKE_NV("h1", ""), MAKE_NV("h1", ""), MAKE_NV("h1", ""), + MAKE_NV("h1", ""), + }; + nghttp2_outbound_item *item; + uint8_t data[4096]; + size_t i; + my_user_data ud; + + memset(data, '0', sizeof(data)); + for (i = 0; i < ARRLEN(nv); ++i) { + nv[i].valuelen = sizeof(data); + nv[i].value = data; + } + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.send_callback = null_send_callback; + callbacks.on_frame_send_callback = on_frame_send_callback; + + CU_ASSERT(0 == nghttp2_session_client_new(&session, &callbacks, &ud)); + CU_ASSERT(1 == nghttp2_submit_headers(session, NGHTTP2_FLAG_END_STREAM, -1, + NULL, nv, ARRLEN(nv), NULL)); + item = nghttp2_session_get_next_ob_item(session); + CU_ASSERT(NGHTTP2_HEADERS == item->frame.hd.type); + CU_ASSERT((NGHTTP2_FLAG_END_STREAM | NGHTTP2_FLAG_END_HEADERS) == + item->frame.hd.flags); + CU_ASSERT(0 == (item->frame.hd.flags & NGHTTP2_FLAG_PRIORITY)); + + ud.frame_send_cb_called = 0; + CU_ASSERT(0 == nghttp2_session_send(session)); + CU_ASSERT(1 == ud.frame_send_cb_called); + + nghttp2_session_del(session); +} + +void test_nghttp2_submit_priority(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + nghttp2_stream *stream; + my_user_data ud; + nghttp2_priority_spec pri_spec; + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.send_callback = null_send_callback; + callbacks.on_frame_send_callback = on_frame_send_callback; + + nghttp2_session_client_new(&session, &callbacks, &ud); + stream = nghttp2_session_open_stream(session, 1, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, + NGHTTP2_STREAM_OPENING, NULL); + + nghttp2_priority_spec_init(&pri_spec, 0, 3, 0); + + /* depends on stream 0 */ + CU_ASSERT(0 == + nghttp2_submit_priority(session, NGHTTP2_FLAG_NONE, 1, &pri_spec)); + CU_ASSERT(0 == nghttp2_session_send(session)); + CU_ASSERT(3 == stream->weight); + + /* submit against idle stream */ + CU_ASSERT(0 == + nghttp2_submit_priority(session, NGHTTP2_FLAG_NONE, 3, &pri_spec)); + + ud.frame_send_cb_called = 0; + CU_ASSERT(0 == nghttp2_session_send(session)); + CU_ASSERT(1 == ud.frame_send_cb_called); + + nghttp2_session_del(session); +} + +void test_nghttp2_submit_settings(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + my_user_data ud; + nghttp2_outbound_item *item; + nghttp2_frame *frame; + nghttp2_settings_entry iv[7]; + nghttp2_frame ack_frame; + const int32_t UNKNOWN_ID = 1000000007; + nghttp2_mem *mem; + + mem = nghttp2_mem_default(); + + iv[0].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS; + iv[0].value = 5; + + iv[1].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE; + iv[1].value = 16 * 1024; + + iv[2].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS; + iv[2].value = 50; + + iv[3].settings_id = NGHTTP2_SETTINGS_HEADER_TABLE_SIZE; + iv[3].value = 0; + + iv[4].settings_id = UNKNOWN_ID; + iv[4].value = 999; + + iv[5].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE; + iv[5].value = (uint32_t)NGHTTP2_MAX_WINDOW_SIZE + 1; + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.send_callback = null_send_callback; + callbacks.on_frame_send_callback = on_frame_send_callback; + nghttp2_session_server_new(&session, &callbacks, &ud); + + CU_ASSERT(NGHTTP2_ERR_INVALID_ARGUMENT == + nghttp2_submit_settings(session, NGHTTP2_FLAG_NONE, iv, 6)); + + /* Make sure that local settings are not changed */ + CU_ASSERT(NGHTTP2_INITIAL_MAX_CONCURRENT_STREAMS == + session->local_settings.max_concurrent_streams); + CU_ASSERT(NGHTTP2_INITIAL_WINDOW_SIZE == + session->local_settings.initial_window_size); + + /* Now sends without 6th one */ + CU_ASSERT(0 == nghttp2_submit_settings(session, NGHTTP2_FLAG_NONE, iv, 5)); + + item = nghttp2_session_get_next_ob_item(session); + + CU_ASSERT(NGHTTP2_SETTINGS == item->frame.hd.type); + + frame = &item->frame; + CU_ASSERT(5 == frame->settings.niv); + CU_ASSERT(5 == frame->settings.iv[0].value); + CU_ASSERT(NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS == + frame->settings.iv[0].settings_id); + + CU_ASSERT(16 * 1024 == frame->settings.iv[1].value); + CU_ASSERT(NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE == + frame->settings.iv[1].settings_id); + + CU_ASSERT(UNKNOWN_ID == frame->settings.iv[4].settings_id); + CU_ASSERT(999 == frame->settings.iv[4].value); + + ud.frame_send_cb_called = 0; + CU_ASSERT(0 == nghttp2_session_send(session)); + CU_ASSERT(1 == ud.frame_send_cb_called); + + CU_ASSERT(50 == session->pending_local_max_concurrent_stream); + + nghttp2_frame_settings_init(&ack_frame.settings, NGHTTP2_FLAG_ACK, NULL, 0); + CU_ASSERT(0 == nghttp2_session_on_settings_received(session, &ack_frame, 0)); + nghttp2_frame_settings_free(&ack_frame.settings, mem); + + CU_ASSERT(16 * 1024 == session->local_settings.initial_window_size); + CU_ASSERT(0 == session->hd_inflater.ctx.hd_table_bufsize_max); + CU_ASSERT(50 == session->local_settings.max_concurrent_streams); + CU_ASSERT(NGHTTP2_INITIAL_MAX_CONCURRENT_STREAMS == + session->pending_local_max_concurrent_stream); + + nghttp2_session_del(session); +} + +void test_nghttp2_submit_settings_update_local_window_size(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + nghttp2_outbound_item *item; + nghttp2_settings_entry iv[4]; + nghttp2_stream *stream; + nghttp2_frame ack_frame; + nghttp2_mem *mem; + + mem = nghttp2_mem_default(); + nghttp2_frame_settings_init(&ack_frame.settings, NGHTTP2_FLAG_ACK, NULL, 0); + + iv[0].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE; + iv[0].value = 16 * 1024; + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.send_callback = null_send_callback; + + nghttp2_session_server_new(&session, &callbacks, NULL); + + stream = nghttp2_session_open_stream(session, 1, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, NGHTTP2_STREAM_OPENED, + NULL); + stream->local_window_size = NGHTTP2_INITIAL_WINDOW_SIZE + 100; + stream->recv_window_size = 32768; + + nghttp2_session_open_stream(session, 3, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, NGHTTP2_STREAM_OPENED, NULL); + + CU_ASSERT(0 == nghttp2_submit_settings(session, NGHTTP2_FLAG_NONE, iv, 1)); + CU_ASSERT(0 == nghttp2_session_send(session)); + CU_ASSERT(0 == nghttp2_session_on_settings_received(session, &ack_frame, 0)); + + stream = nghttp2_session_get_stream(session, 1); + CU_ASSERT(0 == stream->recv_window_size); + CU_ASSERT(16 * 1024 + 100 == stream->local_window_size); + + stream = nghttp2_session_get_stream(session, 3); + CU_ASSERT(16 * 1024 == stream->local_window_size); + + item = nghttp2_session_get_next_ob_item(session); + CU_ASSERT(NGHTTP2_WINDOW_UPDATE == item->frame.hd.type); + CU_ASSERT(32768 == item->frame.window_update.window_size_increment); + + nghttp2_session_del(session); + + /* Check overflow case */ + iv[0].value = 128 * 1024; + nghttp2_session_server_new(&session, &callbacks, NULL); + stream = nghttp2_session_open_stream(session, 1, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, NGHTTP2_STREAM_OPENED, + NULL); + stream->local_window_size = NGHTTP2_MAX_WINDOW_SIZE; + + CU_ASSERT(0 == nghttp2_submit_settings(session, NGHTTP2_FLAG_NONE, iv, 1)); + CU_ASSERT(0 == nghttp2_session_send(session)); + CU_ASSERT(0 == nghttp2_session_on_settings_received(session, &ack_frame, 0)); + + item = nghttp2_session_get_next_ob_item(session); + CU_ASSERT(NGHTTP2_GOAWAY == item->frame.hd.type); + CU_ASSERT(NGHTTP2_FLOW_CONTROL_ERROR == item->frame.goaway.error_code); + + nghttp2_session_del(session); + nghttp2_frame_settings_free(&ack_frame.settings, mem); +} + +void test_nghttp2_submit_push_promise(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + my_user_data ud; + nghttp2_stream *stream; + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.send_callback = null_send_callback; + callbacks.on_frame_send_callback = on_frame_send_callback; + callbacks.on_frame_not_send_callback = on_frame_not_send_callback; + + CU_ASSERT(0 == nghttp2_session_server_new(&session, &callbacks, &ud)); + nghttp2_session_open_stream(session, 1, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, NGHTTP2_STREAM_OPENING, NULL); + CU_ASSERT(2 == nghttp2_submit_push_promise(session, NGHTTP2_FLAG_NONE, 1, + reqnv, ARRLEN(reqnv), &ud)); + + ud.frame_send_cb_called = 0; + ud.sent_frame_type = 0; + CU_ASSERT(0 == nghttp2_session_send(session)); + CU_ASSERT(1 == ud.frame_send_cb_called); + CU_ASSERT(NGHTTP2_PUSH_PROMISE == ud.sent_frame_type); + stream = nghttp2_session_get_stream(session, 2); + CU_ASSERT(NGHTTP2_STREAM_RESERVED == stream->state); + CU_ASSERT(&ud == nghttp2_session_get_stream_user_data(session, 2)); + + /* submit PUSH_PROMISE while associated stream is not opened */ + CU_ASSERT(4 == nghttp2_submit_push_promise(session, NGHTTP2_FLAG_NONE, 3, + reqnv, ARRLEN(reqnv), &ud)); + + ud.frame_not_send_cb_called = 0; + + CU_ASSERT(0 == nghttp2_session_send(session)); + CU_ASSERT(1 == ud.frame_not_send_cb_called); + CU_ASSERT(NGHTTP2_PUSH_PROMISE == ud.not_sent_frame_type); + + stream = nghttp2_session_get_stream(session, 4); + + CU_ASSERT(NULL == stream); + + nghttp2_session_del(session); +} + +void test_nghttp2_submit_window_update(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + my_user_data ud; + nghttp2_outbound_item *item; + nghttp2_stream *stream; + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.send_callback = null_send_callback; + + nghttp2_session_client_new(&session, &callbacks, &ud); + stream = nghttp2_session_open_stream(session, 2, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, NGHTTP2_STREAM_OPENED, + NULL); + stream->recv_window_size = 4096; + + CU_ASSERT(0 == + nghttp2_submit_window_update(session, NGHTTP2_FLAG_NONE, 2, 1024)); + item = nghttp2_session_get_next_ob_item(session); + CU_ASSERT(NGHTTP2_WINDOW_UPDATE == item->frame.hd.type); + CU_ASSERT(1024 == item->frame.window_update.window_size_increment); + CU_ASSERT(0 == nghttp2_session_send(session)); + CU_ASSERT(3072 == stream->recv_window_size); + + CU_ASSERT(0 == + nghttp2_submit_window_update(session, NGHTTP2_FLAG_NONE, 2, 4096)); + item = nghttp2_session_get_next_ob_item(session); + CU_ASSERT(NGHTTP2_WINDOW_UPDATE == item->frame.hd.type); + CU_ASSERT(4096 == item->frame.window_update.window_size_increment); + CU_ASSERT(0 == nghttp2_session_send(session)); + CU_ASSERT(0 == stream->recv_window_size); + + CU_ASSERT(0 == + nghttp2_submit_window_update(session, NGHTTP2_FLAG_NONE, 2, 4096)); + item = nghttp2_session_get_next_ob_item(session); + CU_ASSERT(NGHTTP2_WINDOW_UPDATE == item->frame.hd.type); + CU_ASSERT(4096 == item->frame.window_update.window_size_increment); + CU_ASSERT(0 == nghttp2_session_send(session)); + CU_ASSERT(0 == stream->recv_window_size); + + CU_ASSERT(0 == + nghttp2_submit_window_update(session, NGHTTP2_FLAG_NONE, 2, 0)); + /* It is ok if stream is closed or does not exist at the call + time */ + CU_ASSERT(0 == + nghttp2_submit_window_update(session, NGHTTP2_FLAG_NONE, 4, 4096)); + + nghttp2_session_del(session); +} + +void test_nghttp2_submit_window_update_local_window_size(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + nghttp2_outbound_item *item; + nghttp2_stream *stream; + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.send_callback = null_send_callback; + + nghttp2_session_client_new(&session, &callbacks, NULL); + stream = nghttp2_session_open_stream(session, 2, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, NGHTTP2_STREAM_OPENED, + NULL); + stream->recv_window_size = 4096; + + CU_ASSERT(0 == nghttp2_submit_window_update(session, NGHTTP2_FLAG_NONE, 2, + stream->recv_window_size + 1)); + CU_ASSERT(NGHTTP2_INITIAL_WINDOW_SIZE + 1 == stream->local_window_size); + CU_ASSERT(0 == stream->recv_window_size); + item = nghttp2_session_get_next_ob_item(session); + CU_ASSERT(NGHTTP2_WINDOW_UPDATE == item->frame.hd.type); + CU_ASSERT(4097 == item->frame.window_update.window_size_increment); + + CU_ASSERT(0 == nghttp2_session_send(session)); + + /* Let's decrement local window size */ + stream->recv_window_size = 4096; + CU_ASSERT(0 == nghttp2_submit_window_update(session, NGHTTP2_FLAG_NONE, 2, + -stream->local_window_size / 2)); + CU_ASSERT(32768 == stream->local_window_size); + CU_ASSERT(-28672 == stream->recv_window_size); + CU_ASSERT(32768 == stream->recv_reduction); + + item = nghttp2_session_get_next_ob_item(session); + CU_ASSERT(item == NULL); + + /* Increase local window size */ + CU_ASSERT(0 == + nghttp2_submit_window_update(session, NGHTTP2_FLAG_NONE, 2, 16384)); + CU_ASSERT(49152 == stream->local_window_size); + CU_ASSERT(-12288 == stream->recv_window_size); + CU_ASSERT(16384 == stream->recv_reduction); + CU_ASSERT(NULL == nghttp2_session_get_next_ob_item(session)); + + CU_ASSERT(NGHTTP2_ERR_FLOW_CONTROL == + nghttp2_submit_window_update(session, NGHTTP2_FLAG_NONE, 2, + NGHTTP2_MAX_WINDOW_SIZE)); + + CU_ASSERT(0 == nghttp2_session_send(session)); + + /* Check connection-level flow control */ + session->recv_window_size = 4096; + CU_ASSERT(0 == nghttp2_submit_window_update(session, NGHTTP2_FLAG_NONE, 0, + session->recv_window_size + 1)); + CU_ASSERT(NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE + 1 == + session->local_window_size); + CU_ASSERT(0 == session->recv_window_size); + item = nghttp2_session_get_next_ob_item(session); + CU_ASSERT(NGHTTP2_WINDOW_UPDATE == item->frame.hd.type); + CU_ASSERT(4097 == item->frame.window_update.window_size_increment); + + CU_ASSERT(0 == nghttp2_session_send(session)); + + /* Go decrement part */ + session->recv_window_size = 4096; + CU_ASSERT(0 == nghttp2_submit_window_update(session, NGHTTP2_FLAG_NONE, 0, + -session->local_window_size / 2)); + CU_ASSERT(32768 == session->local_window_size); + CU_ASSERT(-28672 == session->recv_window_size); + CU_ASSERT(32768 == session->recv_reduction); + item = nghttp2_session_get_next_ob_item(session); + CU_ASSERT(item == NULL); + + /* Increase local window size */ + CU_ASSERT(0 == + nghttp2_submit_window_update(session, NGHTTP2_FLAG_NONE, 0, 16384)); + CU_ASSERT(49152 == session->local_window_size); + CU_ASSERT(-12288 == session->recv_window_size); + CU_ASSERT(16384 == session->recv_reduction); + CU_ASSERT(NULL == nghttp2_session_get_next_ob_item(session)); + + CU_ASSERT(NGHTTP2_ERR_FLOW_CONTROL == + nghttp2_submit_window_update(session, NGHTTP2_FLAG_NONE, 0, + NGHTTP2_MAX_WINDOW_SIZE)); + + nghttp2_session_del(session); +} + +void test_nghttp2_submit_shutdown_notice(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + my_user_data ud; + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.send_callback = null_send_callback; + callbacks.on_frame_send_callback = on_frame_send_callback; + callbacks.on_frame_not_send_callback = on_frame_not_send_callback; + + nghttp2_session_server_new(&session, &callbacks, &ud); + + CU_ASSERT(0 == nghttp2_submit_shutdown_notice(session)); + + ud.frame_send_cb_called = 0; + + nghttp2_session_send(session); + + CU_ASSERT(1 == ud.frame_send_cb_called); + CU_ASSERT(NGHTTP2_GOAWAY == ud.sent_frame_type); + CU_ASSERT((1u << 31) - 1 == session->local_last_stream_id); + + /* After another GOAWAY, nghttp2_submit_shutdown_notice() is + noop. */ + CU_ASSERT(0 == nghttp2_session_terminate_session(session, NGHTTP2_NO_ERROR)); + + ud.frame_send_cb_called = 0; + + nghttp2_session_send(session); + + CU_ASSERT(1 == ud.frame_send_cb_called); + CU_ASSERT(NGHTTP2_GOAWAY == ud.sent_frame_type); + CU_ASSERT(0 == session->local_last_stream_id); + + CU_ASSERT(0 == nghttp2_submit_shutdown_notice(session)); + + ud.frame_send_cb_called = 0; + ud.frame_not_send_cb_called = 0; + + nghttp2_session_send(session); + + CU_ASSERT(0 == ud.frame_send_cb_called); + CU_ASSERT(0 == ud.frame_not_send_cb_called); + + nghttp2_session_del(session); + + /* Using nghttp2_submit_shutdown_notice() with client side session + is error */ + nghttp2_session_client_new(&session, &callbacks, NULL); + + CU_ASSERT(NGHTTP2_ERR_INVALID_STATE == + nghttp2_submit_shutdown_notice(session)); + + nghttp2_session_del(session); +} + +void test_nghttp2_submit_invalid_nv(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + nghttp2_nv empty_name_nv[] = {MAKE_NV("Version", "HTTP/1.1"), + MAKE_NV("", "empty name")}; + + /* Now invalid header name/value pair in HTTP/1.1 is accepted in + nghttp2 */ + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + + CU_ASSERT(0 == nghttp2_session_server_new(&session, &callbacks, NULL)); + + /* nghttp2_submit_request */ + CU_ASSERT(0 < nghttp2_submit_request(session, NULL, empty_name_nv, + ARRLEN(empty_name_nv), NULL, NULL)); + + /* nghttp2_submit_response */ + CU_ASSERT(0 == nghttp2_submit_response(session, 2, empty_name_nv, + ARRLEN(empty_name_nv), NULL)); + + /* nghttp2_submit_headers */ + CU_ASSERT(0 < nghttp2_submit_headers(session, NGHTTP2_FLAG_NONE, -1, NULL, + empty_name_nv, ARRLEN(empty_name_nv), + NULL)); + + /* nghttp2_submit_push_promise */ + open_stream(session, 1); + + CU_ASSERT(0 < nghttp2_submit_push_promise(session, NGHTTP2_FLAG_NONE, 1, + empty_name_nv, + ARRLEN(empty_name_nv), NULL)); + + nghttp2_session_del(session); +} + +void test_nghttp2_session_open_stream(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + nghttp2_stream *stream; + nghttp2_priority_spec pri_spec; + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + nghttp2_session_server_new(&session, &callbacks, NULL); + + nghttp2_priority_spec_init(&pri_spec, 0, 245, 0); + + stream = nghttp2_session_open_stream(session, 1, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec, NGHTTP2_STREAM_OPENED, NULL); + CU_ASSERT(1 == session->num_incoming_streams); + CU_ASSERT(0 == session->num_outgoing_streams); + CU_ASSERT(NGHTTP2_STREAM_OPENED == stream->state); + CU_ASSERT(245 == stream->weight); + CU_ASSERT(NULL == stream->dep_prev); + CU_ASSERT(NGHTTP2_SHUT_NONE == stream->shut_flags); + + stream = nghttp2_session_open_stream(session, 2, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, + NGHTTP2_STREAM_OPENING, NULL); + CU_ASSERT(1 == session->num_incoming_streams); + CU_ASSERT(1 == session->num_outgoing_streams); + CU_ASSERT(NULL == stream->dep_prev); + CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT == stream->weight); + CU_ASSERT(NGHTTP2_SHUT_NONE == stream->shut_flags); + + stream = nghttp2_session_open_stream(session, 4, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, + NGHTTP2_STREAM_RESERVED, NULL); + CU_ASSERT(1 == session->num_incoming_streams); + CU_ASSERT(1 == session->num_outgoing_streams); + CU_ASSERT(NULL == stream->dep_prev); + CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT == stream->weight); + CU_ASSERT(NGHTTP2_SHUT_RD == stream->shut_flags); + + nghttp2_priority_spec_init(&pri_spec, 1, 17, 1); + + stream = nghttp2_session_open_stream(session, 3, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec, NGHTTP2_STREAM_OPENED, NULL); + CU_ASSERT(17 == stream->weight); + CU_ASSERT(1 == stream->dep_prev->stream_id); + + /* Dependency to idle stream */ + nghttp2_priority_spec_init(&pri_spec, 1000000007, 240, 1); + + stream = nghttp2_session_open_stream(session, 5, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec, NGHTTP2_STREAM_OPENED, NULL); + CU_ASSERT(240 == stream->weight); + CU_ASSERT(1000000007 == stream->dep_prev->stream_id); + + stream = nghttp2_session_get_stream_raw(session, 1000000007); + + CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT == stream->weight); + CU_ASSERT(NULL != stream->root_next); + + /* Dependency to closed stream which is not in dependency tree */ + session->last_recv_stream_id = 7; + + nghttp2_priority_spec_init(&pri_spec, 7, 10, 0); + + stream = nghttp2_session_open_stream(session, 9, NGHTTP2_FLAG_NONE, &pri_spec, + NGHTTP2_STREAM_OPENED, NULL); + + CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT == stream->weight); + + nghttp2_session_del(session); + + nghttp2_session_client_new(&session, &callbacks, NULL); + stream = nghttp2_session_open_stream(session, 4, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, + NGHTTP2_STREAM_RESERVED, NULL); + CU_ASSERT(0 == session->num_incoming_streams); + CU_ASSERT(0 == session->num_outgoing_streams); + CU_ASSERT(NULL == stream->dep_prev); + CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT == stream->weight); + CU_ASSERT(NGHTTP2_SHUT_WR == stream->shut_flags); + + nghttp2_session_del(session); +} + +void test_nghttp2_session_open_stream_with_idle_stream_dep(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + nghttp2_stream *stream; + nghttp2_priority_spec pri_spec; + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + nghttp2_session_server_new(&session, &callbacks, NULL); + + /* Dependency to idle stream */ + nghttp2_priority_spec_init(&pri_spec, 101, 245, 0); + + stream = nghttp2_session_open_stream(session, 1, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec, NGHTTP2_STREAM_OPENED, NULL); + + CU_ASSERT(245 == stream->weight); + CU_ASSERT(101 == stream->dep_prev->stream_id); + + stream = nghttp2_session_get_stream_raw(session, 101); + + CU_ASSERT(NGHTTP2_STREAM_IDLE == stream->state); + CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT == stream->weight); + + nghttp2_priority_spec_init(&pri_spec, 211, 1, 0); + + /* stream 101 was already created as idle. */ + stream = nghttp2_session_open_stream(session, 101, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec, NGHTTP2_STREAM_OPENED, NULL); + + CU_ASSERT(1 == stream->weight); + CU_ASSERT(211 == stream->dep_prev->stream_id); + + stream = nghttp2_session_get_stream_raw(session, 211); + + CU_ASSERT(NGHTTP2_STREAM_IDLE == stream->state); + CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT == stream->weight); + + nghttp2_session_del(session); +} + +void test_nghttp2_session_get_next_ob_item(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + nghttp2_priority_spec pri_spec; + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.send_callback = null_send_callback; + + nghttp2_session_server_new(&session, &callbacks, NULL); + session->remote_settings.max_concurrent_streams = 2; + + CU_ASSERT(NULL == nghttp2_session_get_next_ob_item(session)); + nghttp2_submit_ping(session, NGHTTP2_FLAG_NONE, NULL); + CU_ASSERT(NGHTTP2_PING == + nghttp2_session_get_next_ob_item(session)->frame.hd.type); + + nghttp2_submit_request(session, NULL, NULL, 0, NULL, NULL); + CU_ASSERT(NGHTTP2_PING == + nghttp2_session_get_next_ob_item(session)->frame.hd.type); + + CU_ASSERT(0 == nghttp2_session_send(session)); + CU_ASSERT(NULL == nghttp2_session_get_next_ob_item(session)); + + /* Incoming stream does not affect the number of outgoing max + concurrent streams. */ + nghttp2_session_open_stream(session, 1, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, NGHTTP2_STREAM_OPENING, NULL); + + nghttp2_priority_spec_init(&pri_spec, 0, NGHTTP2_MAX_WEIGHT, 0); + + nghttp2_submit_request(session, &pri_spec, NULL, 0, NULL, NULL); + CU_ASSERT(NGHTTP2_HEADERS == + nghttp2_session_get_next_ob_item(session)->frame.hd.type); + CU_ASSERT(0 == nghttp2_session_send(session)); + + nghttp2_submit_request(session, &pri_spec, NULL, 0, NULL, NULL); + CU_ASSERT(NULL == nghttp2_session_get_next_ob_item(session)); + + session->remote_settings.max_concurrent_streams = 3; + + CU_ASSERT(NGHTTP2_HEADERS == + nghttp2_session_get_next_ob_item(session)->frame.hd.type); + + nghttp2_session_del(session); +} + +void test_nghttp2_session_pop_next_ob_item(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + nghttp2_outbound_item *item; + nghttp2_priority_spec pri_spec; + nghttp2_stream *stream; + nghttp2_mem *mem; + + mem = nghttp2_mem_default(); + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.send_callback = null_send_callback; + + nghttp2_session_server_new(&session, &callbacks, NULL); + session->remote_settings.max_concurrent_streams = 1; + + CU_ASSERT(NULL == nghttp2_session_pop_next_ob_item(session)); + + nghttp2_submit_ping(session, NGHTTP2_FLAG_NONE, NULL); + + nghttp2_priority_spec_init(&pri_spec, 0, 254, 0); + + nghttp2_submit_request(session, &pri_spec, NULL, 0, NULL, NULL); + + item = nghttp2_session_pop_next_ob_item(session); + CU_ASSERT(NGHTTP2_PING == item->frame.hd.type); + nghttp2_outbound_item_free(item, mem); + mem->free(item, NULL); + + item = nghttp2_session_pop_next_ob_item(session); + CU_ASSERT(NGHTTP2_HEADERS == item->frame.hd.type); + nghttp2_outbound_item_free(item, mem); + mem->free(item, NULL); + + CU_ASSERT(NULL == nghttp2_session_pop_next_ob_item(session)); + + /* Incoming stream does not affect the number of outgoing max + concurrent streams. */ + nghttp2_session_open_stream(session, 1, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, NGHTTP2_STREAM_OPENING, NULL); + /* In-flight outgoing stream */ + nghttp2_session_open_stream(session, 4, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, NGHTTP2_STREAM_OPENING, NULL); + + nghttp2_priority_spec_init(&pri_spec, 0, NGHTTP2_MAX_WEIGHT, 0); + + nghttp2_submit_request(session, &pri_spec, NULL, 0, NULL, NULL); + nghttp2_submit_response(session, 1, NULL, 0, NULL); + + item = nghttp2_session_pop_next_ob_item(session); + CU_ASSERT(NGHTTP2_HEADERS == item->frame.hd.type); + CU_ASSERT(1 == item->frame.hd.stream_id); + + stream = nghttp2_session_get_stream(session, 1); + + nghttp2_stream_detach_item(stream, session); + + nghttp2_outbound_item_free(item, mem); + mem->free(item, NULL); + + CU_ASSERT(NULL == nghttp2_session_pop_next_ob_item(session)); + + session->remote_settings.max_concurrent_streams = 2; + + item = nghttp2_session_pop_next_ob_item(session); + CU_ASSERT(NGHTTP2_HEADERS == item->frame.hd.type); + nghttp2_outbound_item_free(item, mem); + mem->free(item, NULL); + + nghttp2_session_del(session); + + /* Check that push reply HEADERS are queued into ob_ss_pq */ + nghttp2_session_server_new(&session, &callbacks, NULL); + session->remote_settings.max_concurrent_streams = 0; + nghttp2_session_open_stream(session, 2, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, NGHTTP2_STREAM_RESERVED, NULL); + CU_ASSERT(0 == nghttp2_submit_headers(session, NGHTTP2_FLAG_END_STREAM, 2, + NULL, NULL, 0, NULL)); + CU_ASSERT(NULL == nghttp2_session_pop_next_ob_item(session)); + CU_ASSERT(1 == nghttp2_outbound_queue_size(&session->ob_syn)); + nghttp2_session_del(session); +} + +void test_nghttp2_session_reply_fail(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + nghttp2_data_provider data_prd; + my_user_data ud; + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.send_callback = fail_send_callback; + + data_prd.read_callback = fixed_length_data_source_read_callback; + ud.data_source_length = 4 * 1024; + CU_ASSERT(0 == nghttp2_session_server_new(&session, &callbacks, &ud)); + nghttp2_session_open_stream(session, 1, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, NGHTTP2_STREAM_OPENING, NULL); + CU_ASSERT(0 == nghttp2_submit_response(session, 1, NULL, 0, &data_prd)); + CU_ASSERT(NGHTTP2_ERR_CALLBACK_FAILURE == nghttp2_session_send(session)); + nghttp2_session_del(session); +} + +void test_nghttp2_session_max_concurrent_streams(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + nghttp2_frame frame; + nghttp2_outbound_item *item; + nghttp2_mem *mem; + + mem = nghttp2_mem_default(); + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.send_callback = null_send_callback; + + nghttp2_session_server_new(&session, &callbacks, NULL); + nghttp2_session_open_stream(session, 1, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, NGHTTP2_STREAM_OPENED, NULL); + + /* Check un-ACKed SETTINGS_MAX_CONCURRENT_STREAMS */ + nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_END_HEADERS, 3, + NGHTTP2_HCAT_HEADERS, NULL, NULL, 0); + session->pending_local_max_concurrent_stream = 1; + + CU_ASSERT(NGHTTP2_ERR_IGN_HEADER_BLOCK == + nghttp2_session_on_request_headers_received(session, &frame)); + + item = nghttp2_outbound_queue_top(&session->ob_reg); + CU_ASSERT(NGHTTP2_RST_STREAM == item->frame.hd.type); + CU_ASSERT(NGHTTP2_REFUSED_STREAM == item->frame.rst_stream.error_code); + + CU_ASSERT(0 == nghttp2_session_send(session)); + + /* Check ACKed SETTINGS_MAX_CONCURRENT_STREAMS */ + session->local_settings.max_concurrent_streams = 1; + frame.hd.stream_id = 5; + + CU_ASSERT(NGHTTP2_ERR_IGN_HEADER_BLOCK == + nghttp2_session_on_request_headers_received(session, &frame)); + + item = nghttp2_outbound_queue_top(&session->ob_reg); + CU_ASSERT(NGHTTP2_GOAWAY == item->frame.hd.type); + CU_ASSERT(NGHTTP2_PROTOCOL_ERROR == item->frame.goaway.error_code); + + nghttp2_frame_headers_free(&frame.headers, mem); + nghttp2_session_del(session); +} + +/* + * Check that on_stream_close_callback is called when server pushed + * HEADERS have NGHTTP2_FLAG_END_STREAM. + */ +void test_nghttp2_session_stream_close_on_headers_push(void) { + /* nghttp2_session *session; */ + /* nghttp2_session_callbacks callbacks; */ + /* const char *nv[] = { NULL }; */ + /* my_user_data ud; */ + /* nghttp2_frame frame; */ + + /* memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); */ + /* callbacks.on_stream_close_callback = */ + /* no_stream_user_data_stream_close_callback; */ + /* ud.stream_close_cb_called = 0; */ + + /* nghttp2_session_client_new(&session, NGHTTP2_PROTO_SPDY2, &callbacks, &ud); + */ + /* nghttp2_session_open_stream(session, 1, NGHTTP2_CTRL_FLAG_NONE, 3, */ + /* NGHTTP2_STREAM_OPENING, NULL); */ + /* nghttp2_frame_syn_stream_init(&frame.syn_stream, NGHTTP2_PROTO_SPDY2, */ + /* NGHTTP2_CTRL_FLAG_FIN | */ + /* NGHTTP2_CTRL_FLAG_UNIDIRECTIONAL, */ + /* 2, 1, 3, dup_nv(nv)); */ + + /* CU_ASSERT(0 == nghttp2_session_on_request_headers_received(session, + * &frame)); */ + + /* nghttp2_frame_syn_stream_free(&frame.syn_stream); */ + /* nghttp2_session_del(session); */ +} + +void test_nghttp2_session_stop_data_with_rst_stream(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + my_user_data ud; + nghttp2_data_provider data_prd; + nghttp2_frame frame; + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.on_frame_send_callback = on_frame_send_callback; + callbacks.send_callback = block_count_send_callback; + data_prd.read_callback = fixed_length_data_source_read_callback; + + ud.frame_send_cb_called = 0; + ud.data_source_length = NGHTTP2_DATA_PAYLOADLEN * 4; + + nghttp2_session_server_new(&session, &callbacks, &ud); + nghttp2_session_open_stream(session, 1, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, NGHTTP2_STREAM_OPENING, NULL); + nghttp2_submit_response(session, 1, NULL, 0, &data_prd); + + ud.block_count = 2; + /* Sends response HEADERS + DATA[0] */ + CU_ASSERT(0 == nghttp2_session_send(session)); + CU_ASSERT(NGHTTP2_DATA == ud.sent_frame_type); + /* data for DATA[1] is read from data_prd but it is not sent */ + CU_ASSERT(ud.data_source_length == NGHTTP2_DATA_PAYLOADLEN * 2); + + nghttp2_frame_rst_stream_init(&frame.rst_stream, 1, NGHTTP2_CANCEL); + CU_ASSERT(0 == nghttp2_session_on_rst_stream_received(session, &frame)); + nghttp2_frame_rst_stream_free(&frame.rst_stream); + + /* Big enough number to send all DATA frames potentially. */ + ud.block_count = 100; + /* Nothing will be sent in the following call. */ + CU_ASSERT(0 == nghttp2_session_send(session)); + /* With RST_STREAM, stream is canceled and further DATA on that + stream are not sent. */ + CU_ASSERT(ud.data_source_length == NGHTTP2_DATA_PAYLOADLEN * 2); + + CU_ASSERT(NULL == nghttp2_session_get_stream(session, 1)); + + nghttp2_session_del(session); +} + +void test_nghttp2_session_defer_data(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + my_user_data ud; + nghttp2_data_provider data_prd; + nghttp2_outbound_item *item; + nghttp2_stream *stream; + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.on_frame_send_callback = on_frame_send_callback; + callbacks.send_callback = block_count_send_callback; + data_prd.read_callback = defer_data_source_read_callback; + + ud.frame_send_cb_called = 0; + ud.data_source_length = NGHTTP2_DATA_PAYLOADLEN * 4; + + nghttp2_session_server_new(&session, &callbacks, &ud); + stream = nghttp2_session_open_stream(session, 1, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, + NGHTTP2_STREAM_OPENING, NULL); + + session->remote_window_size = 1 << 20; + stream->remote_window_size = 1 << 20; + + nghttp2_submit_response(session, 1, NULL, 0, &data_prd); + + ud.block_count = 1; + /* Sends HEADERS reply */ + CU_ASSERT(0 == nghttp2_session_send(session)); + CU_ASSERT(NGHTTP2_HEADERS == ud.sent_frame_type); + /* No data is read */ + CU_ASSERT(ud.data_source_length == NGHTTP2_DATA_PAYLOADLEN * 4); + + ud.block_count = 1; + nghttp2_submit_ping(session, NGHTTP2_FLAG_NONE, NULL); + /* Sends PING */ + CU_ASSERT(0 == nghttp2_session_send(session)); + CU_ASSERT(NGHTTP2_PING == ud.sent_frame_type); + + /* Resume deferred DATA */ + CU_ASSERT(0 == nghttp2_session_resume_data(session, 1)); + item = (nghttp2_outbound_item *)nghttp2_pq_top(&session->ob_da_pq); + item->aux_data.data.data_prd.read_callback = + fixed_length_data_source_read_callback; + ud.block_count = 1; + /* Reads 2 DATA chunks */ + CU_ASSERT(0 == nghttp2_session_send(session)); + CU_ASSERT(ud.data_source_length == NGHTTP2_DATA_PAYLOADLEN * 2); + + /* Deferred again */ + item->aux_data.data.data_prd.read_callback = defer_data_source_read_callback; + /* This is needed since 16KiB block is already read and waiting to be + sent. No read_callback invocation. */ + ud.block_count = 1; + CU_ASSERT(0 == nghttp2_session_send(session)); + CU_ASSERT(ud.data_source_length == NGHTTP2_DATA_PAYLOADLEN * 2); + + /* Resume deferred DATA */ + CU_ASSERT(0 == nghttp2_session_resume_data(session, 1)); + item = (nghttp2_outbound_item *)nghttp2_pq_top(&session->ob_da_pq); + item->aux_data.data.data_prd.read_callback = + fixed_length_data_source_read_callback; + ud.block_count = 1; + /* Reads 2 16KiB blocks */ + CU_ASSERT(0 == nghttp2_session_send(session)); + CU_ASSERT(ud.data_source_length == 0); + + nghttp2_session_del(session); +} + +void test_nghttp2_session_flow_control(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + my_user_data ud; + nghttp2_data_provider data_prd; + nghttp2_frame frame; + nghttp2_stream *stream; + int32_t new_initial_window_size; + nghttp2_settings_entry iv[1]; + nghttp2_frame settings_frame; + nghttp2_mem *mem; + + mem = nghttp2_mem_default(); + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.send_callback = fixed_bytes_send_callback; + callbacks.on_frame_send_callback = on_frame_send_callback; + data_prd.read_callback = fixed_length_data_source_read_callback; + + ud.frame_send_cb_called = 0; + ud.data_source_length = 128 * 1024; + /* Use smaller emission count so that we can check outbound flow + control window calculation is correct. */ + ud.fixed_sendlen = 2 * 1024; + + /* Initial window size to 64KiB - 1*/ + nghttp2_session_client_new(&session, &callbacks, &ud); + /* Change it to 64KiB for easy calculation */ + session->remote_window_size = 64 * 1024; + session->remote_settings.initial_window_size = 64 * 1024; + + nghttp2_submit_request(session, NULL, NULL, 0, &data_prd, NULL); + + /* Sends 64KiB - 1 data */ + CU_ASSERT(0 == nghttp2_session_send(session)); + CU_ASSERT(64 * 1024 == ud.data_source_length); + + /* Back 32KiB in stream window */ + nghttp2_frame_window_update_init(&frame.window_update, NGHTTP2_FLAG_NONE, 1, + 32 * 1024); + nghttp2_session_on_window_update_received(session, &frame); + + /* Send nothing because of connection-level window */ + CU_ASSERT(0 == nghttp2_session_send(session)); + CU_ASSERT(64 * 1024 == ud.data_source_length); + + /* Back 32KiB in connection-level window */ + frame.hd.stream_id = 0; + nghttp2_session_on_window_update_received(session, &frame); + + /* Sends another 32KiB data */ + CU_ASSERT(0 == nghttp2_session_send(session)); + CU_ASSERT(32 * 1024 == ud.data_source_length); + + stream = nghttp2_session_get_stream(session, 1); + /* Change initial window size to 16KiB. The window_size becomes + negative. */ + new_initial_window_size = 16 * 1024; + stream->remote_window_size = + new_initial_window_size - (session->remote_settings.initial_window_size - + stream->remote_window_size); + session->remote_settings.initial_window_size = new_initial_window_size; + CU_ASSERT(-48 * 1024 == stream->remote_window_size); + + /* Back 48KiB to stream window */ + frame.hd.stream_id = 1; + frame.window_update.window_size_increment = 48 * 1024; + nghttp2_session_on_window_update_received(session, &frame); + + /* Nothing is sent because window_size is 0 */ + CU_ASSERT(0 == nghttp2_session_send(session)); + CU_ASSERT(32 * 1024 == ud.data_source_length); + + /* Back 16KiB in stream window */ + frame.hd.stream_id = 1; + frame.window_update.window_size_increment = 16 * 1024; + nghttp2_session_on_window_update_received(session, &frame); + + /* Back 24KiB in connection-level window */ + frame.hd.stream_id = 0; + frame.window_update.window_size_increment = 24 * 1024; + nghttp2_session_on_window_update_received(session, &frame); + + /* Sends another 16KiB data */ + CU_ASSERT(0 == nghttp2_session_send(session)); + CU_ASSERT(16 * 1024 == ud.data_source_length); + + /* Increase initial window size to 32KiB */ + iv[0].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE; + iv[0].value = 32 * 1024; + + nghttp2_frame_settings_init(&settings_frame.settings, NGHTTP2_FLAG_NONE, + dup_iv(iv, 1), 1); + nghttp2_session_on_settings_received(session, &settings_frame, 1); + nghttp2_frame_settings_free(&settings_frame.settings, mem); + + /* Sends another 8KiB data */ + CU_ASSERT(0 == nghttp2_session_send(session)); + CU_ASSERT(8 * 1024 == ud.data_source_length); + + /* Back 8KiB in connection-level window */ + frame.hd.stream_id = 0; + frame.window_update.window_size_increment = 8 * 1024; + nghttp2_session_on_window_update_received(session, &frame); + + /* Sends last 8KiB data */ + CU_ASSERT(0 == nghttp2_session_send(session)); + CU_ASSERT(0 == ud.data_source_length); + CU_ASSERT(nghttp2_session_get_stream(session, 1)->shut_flags & + NGHTTP2_SHUT_WR); + + nghttp2_frame_window_update_free(&frame.window_update); + nghttp2_session_del(session); +} + +void test_nghttp2_session_flow_control_data_recv(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + uint8_t data[64 * 1024 + 16]; + nghttp2_frame_hd hd; + nghttp2_outbound_item *item; + nghttp2_stream *stream; + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.send_callback = null_send_callback; + + /* Initial window size to 64KiB - 1*/ + nghttp2_session_client_new(&session, &callbacks, NULL); + + stream = nghttp2_session_open_stream(session, 1, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, NGHTTP2_STREAM_OPENED, + NULL); + + session->next_stream_id = 3; + + nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_WR); + + session->local_window_size = NGHTTP2_MAX_PAYLOADLEN; + stream->local_window_size = NGHTTP2_MAX_PAYLOADLEN; + + /* Create DATA frame */ + memset(data, 0, sizeof(data)); + nghttp2_frame_hd_init(&hd, NGHTTP2_MAX_PAYLOADLEN, NGHTTP2_DATA, + NGHTTP2_FLAG_END_STREAM, 1); + + nghttp2_frame_pack_frame_hd(data, &hd); + CU_ASSERT(NGHTTP2_MAX_PAYLOADLEN + NGHTTP2_FRAME_HDLEN == + nghttp2_session_mem_recv(session, data, NGHTTP2_MAX_PAYLOADLEN + + NGHTTP2_FRAME_HDLEN)); + + item = nghttp2_session_get_next_ob_item(session); + /* Since this is the last frame, stream-level WINDOW_UPDATE is not + issued, but connection-level is. */ + CU_ASSERT(NGHTTP2_WINDOW_UPDATE == item->frame.hd.type); + CU_ASSERT(0 == item->frame.hd.stream_id); + CU_ASSERT(NGHTTP2_MAX_PAYLOADLEN == + item->frame.window_update.window_size_increment); + + CU_ASSERT(0 == nghttp2_session_send(session)); + + /* Receive DATA for closed stream. They are still subject to under + connection-level flow control, since this situation arises when + RST_STREAM is issued by the remote, but the local side keeps + sending DATA frames. Without calculating connection-level window, + the subsequent flow control gets confused. */ + CU_ASSERT(NGHTTP2_MAX_PAYLOADLEN + NGHTTP2_FRAME_HDLEN == + nghttp2_session_mem_recv(session, data, NGHTTP2_MAX_PAYLOADLEN + + NGHTTP2_FRAME_HDLEN)); + + item = nghttp2_session_get_next_ob_item(session); + CU_ASSERT(NGHTTP2_WINDOW_UPDATE == item->frame.hd.type); + CU_ASSERT(0 == item->frame.hd.stream_id); + CU_ASSERT(NGHTTP2_MAX_PAYLOADLEN == + item->frame.window_update.window_size_increment); + + nghttp2_session_del(session); +} + +void test_nghttp2_session_flow_control_data_with_padding_recv(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + uint8_t data[1024]; + nghttp2_frame_hd hd; + nghttp2_stream *stream; + nghttp2_option *option; + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.send_callback = null_send_callback; + + nghttp2_option_new(&option); + /* Disable auto window update so that we can check padding is + consumed automatically */ + nghttp2_option_set_no_auto_window_update(option, 1); + + /* Initial window size to 64KiB - 1*/ + nghttp2_session_client_new2(&session, &callbacks, NULL, option); + + nghttp2_option_del(option); + + stream = nghttp2_session_open_stream(session, 1, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, NGHTTP2_STREAM_OPENED, + NULL); + + /* Create DATA frame */ + memset(data, 0, sizeof(data)); + nghttp2_frame_hd_init(&hd, 357, NGHTTP2_DATA, + NGHTTP2_FLAG_END_STREAM | NGHTTP2_FLAG_PADDED, 1); + + nghttp2_frame_pack_frame_hd(data, &hd); + /* Set Pad Length field, which itself is padding */ + data[NGHTTP2_FRAME_HDLEN] = 255; + + CU_ASSERT( + (ssize_t)(NGHTTP2_FRAME_HDLEN + hd.length) == + nghttp2_session_mem_recv(session, data, NGHTTP2_FRAME_HDLEN + hd.length)); + + CU_ASSERT((int32_t)hd.length == session->recv_window_size); + CU_ASSERT((int32_t)hd.length == stream->recv_window_size); + CU_ASSERT(256 == session->consumed_size); + CU_ASSERT(256 == stream->consumed_size); + + nghttp2_session_del(session); +} + +void test_nghttp2_session_data_read_temporal_failure(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + my_user_data ud; + nghttp2_data_provider data_prd; + nghttp2_frame frame; + nghttp2_stream *stream; + size_t data_size = 128 * 1024; + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.send_callback = null_send_callback; + callbacks.on_frame_send_callback = on_frame_send_callback; + data_prd.read_callback = fixed_length_data_source_read_callback; + + ud.data_source_length = data_size; + + /* Initial window size is 64KiB - 1 */ + nghttp2_session_client_new(&session, &callbacks, &ud); + nghttp2_submit_request(session, NULL, NULL, 0, &data_prd, NULL); + + /* Sends NGHTTP2_INITIAL_WINDOW_SIZE data, assuming, it is equal to + or smaller than NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE */ + CU_ASSERT(0 == nghttp2_session_send(session)); + CU_ASSERT(data_size - NGHTTP2_INITIAL_WINDOW_SIZE == ud.data_source_length); + + stream = nghttp2_session_get_stream(session, 1); + CU_ASSERT(nghttp2_stream_check_deferred_by_flow_control(stream)); + CU_ASSERT(NGHTTP2_DATA == stream->item->frame.hd.type); + + stream->item->aux_data.data.data_prd.read_callback = + temporal_failure_data_source_read_callback; + + /* Back NGHTTP2_INITIAL_WINDOW_SIZE to both connection-level and + stream-wise window */ + nghttp2_frame_window_update_init(&frame.window_update, NGHTTP2_FLAG_NONE, 1, + NGHTTP2_INITIAL_WINDOW_SIZE); + nghttp2_session_on_window_update_received(session, &frame); + frame.hd.stream_id = 0; + nghttp2_session_on_window_update_received(session, &frame); + nghttp2_frame_window_update_free(&frame.window_update); + + /* Sending data will fail (soft fail) and treated as stream error */ + ud.frame_send_cb_called = 0; + CU_ASSERT(0 == nghttp2_session_send(session)); + CU_ASSERT(data_size - NGHTTP2_INITIAL_WINDOW_SIZE == ud.data_source_length); + + CU_ASSERT(1 == ud.frame_send_cb_called); + CU_ASSERT(NGHTTP2_RST_STREAM == ud.sent_frame_type); + + data_prd.read_callback = fail_data_source_read_callback; + nghttp2_submit_request(session, NULL, NULL, 0, &data_prd, NULL); + /* Sending data will fail (hard fail) and session tear down */ + CU_ASSERT(NGHTTP2_ERR_CALLBACK_FAILURE == nghttp2_session_send(session)); + + nghttp2_session_del(session); +} + +void test_nghttp2_session_on_stream_close(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + my_user_data user_data; + nghttp2_stream *stream; + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.on_stream_close_callback = on_stream_close_callback; + user_data.stream_close_cb_called = 0; + + nghttp2_session_client_new(&session, &callbacks, &user_data); + stream = nghttp2_session_open_stream(session, 1, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, NGHTTP2_STREAM_OPENED, + &user_data); + CU_ASSERT(stream != NULL); + CU_ASSERT(nghttp2_session_close_stream(session, 1, NGHTTP2_NO_ERROR) == 0); + CU_ASSERT(user_data.stream_close_cb_called == 1); + nghttp2_session_del(session); +} + +void test_nghttp2_session_on_ctrl_not_send(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + my_user_data user_data; + nghttp2_stream *stream; + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.on_frame_not_send_callback = on_frame_not_send_callback; + callbacks.send_callback = null_send_callback; + user_data.frame_not_send_cb_called = 0; + user_data.not_sent_frame_type = 0; + user_data.not_sent_error = 0; + + nghttp2_session_server_new(&session, &callbacks, &user_data); + stream = nghttp2_session_open_stream(session, 1, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, + NGHTTP2_STREAM_OPENING, &user_data); + + /* Check response HEADERS */ + /* Send bogus stream ID */ + CU_ASSERT(0 == nghttp2_submit_headers(session, NGHTTP2_FLAG_END_STREAM, 3, + NULL, NULL, 0, NULL)); + CU_ASSERT(0 == nghttp2_session_send(session)); + CU_ASSERT(1 == user_data.frame_not_send_cb_called); + CU_ASSERT(NGHTTP2_HEADERS == user_data.not_sent_frame_type); + CU_ASSERT(NGHTTP2_ERR_STREAM_CLOSED == user_data.not_sent_error); + + user_data.frame_not_send_cb_called = 0; + /* Shutdown transmission */ + stream->shut_flags |= NGHTTP2_SHUT_WR; + CU_ASSERT(0 == nghttp2_submit_headers(session, NGHTTP2_FLAG_END_STREAM, 1, + NULL, NULL, 0, NULL)); + CU_ASSERT(0 == nghttp2_session_send(session)); + CU_ASSERT(1 == user_data.frame_not_send_cb_called); + CU_ASSERT(NGHTTP2_HEADERS == user_data.not_sent_frame_type); + CU_ASSERT(NGHTTP2_ERR_STREAM_SHUT_WR == user_data.not_sent_error); + + stream->shut_flags = NGHTTP2_SHUT_NONE; + user_data.frame_not_send_cb_called = 0; + /* Queue RST_STREAM */ + CU_ASSERT(0 == nghttp2_submit_headers(session, NGHTTP2_FLAG_END_STREAM, 1, + NULL, NULL, 0, NULL)); + CU_ASSERT(0 == nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, 1, + NGHTTP2_INTERNAL_ERROR)); + CU_ASSERT(0 == nghttp2_session_send(session)); + CU_ASSERT(1 == user_data.frame_not_send_cb_called); + CU_ASSERT(NGHTTP2_HEADERS == user_data.not_sent_frame_type); + CU_ASSERT(NGHTTP2_ERR_STREAM_CLOSING == user_data.not_sent_error); + + nghttp2_session_del(session); + + /* Check request HEADERS */ + user_data.frame_not_send_cb_called = 0; + CU_ASSERT(nghttp2_session_client_new(&session, &callbacks, &user_data) == 0); + /* Maximum Stream ID is reached */ + session->next_stream_id = (1u << 31) + 1; + CU_ASSERT(NGHTTP2_ERR_STREAM_ID_NOT_AVAILABLE == + nghttp2_submit_headers(session, NGHTTP2_FLAG_END_STREAM, -1, NULL, + NULL, 0, NULL)); + + user_data.frame_not_send_cb_called = 0; + /* GOAWAY received */ + session->goaway_flags |= NGHTTP2_GOAWAY_RECV; + session->next_stream_id = 9; + + CU_ASSERT(0 < nghttp2_submit_headers(session, NGHTTP2_FLAG_END_STREAM, -1, + NULL, NULL, 0, NULL)); + CU_ASSERT(0 == nghttp2_session_send(session)); + CU_ASSERT(1 == user_data.frame_not_send_cb_called); + CU_ASSERT(NGHTTP2_HEADERS == user_data.not_sent_frame_type); + CU_ASSERT(NGHTTP2_ERR_START_STREAM_NOT_ALLOWED == user_data.not_sent_error); + + nghttp2_session_del(session); +} + +void test_nghttp2_session_get_outbound_queue_size(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + CU_ASSERT(0 == nghttp2_session_client_new(&session, &callbacks, NULL)); + CU_ASSERT(0 == nghttp2_session_get_outbound_queue_size(session)); + + CU_ASSERT(0 == nghttp2_submit_ping(session, NGHTTP2_FLAG_NONE, NULL)); + CU_ASSERT(1 == nghttp2_session_get_outbound_queue_size(session)); + + CU_ASSERT(0 == nghttp2_submit_goaway(session, NGHTTP2_FLAG_NONE, 2, + NGHTTP2_NO_ERROR, NULL, 0)); + CU_ASSERT(2 == nghttp2_session_get_outbound_queue_size(session)); + + nghttp2_session_del(session); +} + +void test_nghttp2_session_get_effective_local_window_size(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + nghttp2_stream *stream; + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + CU_ASSERT(0 == nghttp2_session_client_new(&session, &callbacks, NULL)); + + stream = nghttp2_session_open_stream(session, 1, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, NGHTTP2_STREAM_OPENED, + NULL); + + CU_ASSERT(NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE == + nghttp2_session_get_effective_local_window_size(session)); + CU_ASSERT(0 == nghttp2_session_get_effective_recv_data_length(session)); + + CU_ASSERT(NGHTTP2_INITIAL_WINDOW_SIZE == + nghttp2_session_get_stream_effective_local_window_size(session, 1)); + CU_ASSERT(0 == + nghttp2_session_get_stream_effective_recv_data_length(session, 1)); + + /* Check connection flow control */ + session->recv_window_size = 100; + nghttp2_submit_window_update(session, NGHTTP2_FLAG_NONE, 0, 1100); + + CU_ASSERT(NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE + 1000 == + nghttp2_session_get_effective_local_window_size(session)); + CU_ASSERT(0 == nghttp2_session_get_effective_recv_data_length(session)); + + nghttp2_submit_window_update(session, NGHTTP2_FLAG_NONE, 0, -50); + /* Now session->recv_window_size = -50 */ + CU_ASSERT(NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE + 950 == + nghttp2_session_get_effective_local_window_size(session)); + CU_ASSERT(0 == nghttp2_session_get_effective_recv_data_length(session)); + + session->recv_window_size += 50; + /* Now session->recv_window_size = 0 */ + nghttp2_submit_window_update(session, NGHTTP2_FLAG_NONE, 0, 100); + CU_ASSERT(NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE + 1050 == + nghttp2_session_get_effective_local_window_size(session)); + CU_ASSERT(50 == nghttp2_session_get_effective_recv_data_length(session)); + + /* Check stream flow control */ + stream->recv_window_size = 100; + nghttp2_submit_window_update(session, NGHTTP2_FLAG_NONE, 1, 1100); + + CU_ASSERT(NGHTTP2_INITIAL_WINDOW_SIZE + 1000 == + nghttp2_session_get_stream_effective_local_window_size(session, 1)); + CU_ASSERT(0 == + nghttp2_session_get_stream_effective_recv_data_length(session, 1)); + + nghttp2_submit_window_update(session, NGHTTP2_FLAG_NONE, 1, -50); + /* Now stream->recv_window_size = -50 */ + CU_ASSERT(NGHTTP2_INITIAL_WINDOW_SIZE + 950 == + nghttp2_session_get_stream_effective_local_window_size(session, 1)); + CU_ASSERT(0 == + nghttp2_session_get_stream_effective_recv_data_length(session, 1)); + + stream->recv_window_size += 50; + /* Now stream->recv_window_size = 0 */ + nghttp2_submit_window_update(session, NGHTTP2_FLAG_NONE, 1, 100); + CU_ASSERT(NGHTTP2_INITIAL_WINDOW_SIZE + 1050 == + nghttp2_session_get_stream_effective_local_window_size(session, 1)); + CU_ASSERT(50 == + nghttp2_session_get_stream_effective_recv_data_length(session, 1)); + + nghttp2_session_del(session); +} + +void test_nghttp2_session_set_option(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + nghttp2_option *option; + + nghttp2_option_new(&option); + + nghttp2_option_set_no_auto_window_update(option, 1); + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + nghttp2_session_client_new2(&session, &callbacks, NULL, option); + + CU_ASSERT(session->opt_flags & NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE); + + nghttp2_session_del(session); + + nghttp2_option_set_peer_max_concurrent_streams(option, 100); + + nghttp2_session_client_new2(&session, &callbacks, NULL, option); + + CU_ASSERT(100 == session->remote_settings.max_concurrent_streams); + nghttp2_session_del(session); + + nghttp2_option_del(option); +} + +void test_nghttp2_session_data_backoff_by_high_pri_frame(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + my_user_data ud; + nghttp2_data_provider data_prd; + nghttp2_stream *stream; + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.send_callback = block_count_send_callback; + callbacks.on_frame_send_callback = on_frame_send_callback; + data_prd.read_callback = fixed_length_data_source_read_callback; + + ud.frame_send_cb_called = 0; + ud.data_source_length = NGHTTP2_DATA_PAYLOADLEN * 4; + + nghttp2_session_client_new(&session, &callbacks, &ud); + nghttp2_submit_request(session, NULL, NULL, 0, &data_prd, NULL); + + session->remote_window_size = 1 << 20; + + ud.block_count = 2; + /* Sends request HEADERS + DATA[0] */ + CU_ASSERT(0 == nghttp2_session_send(session)); + + stream = nghttp2_session_get_stream(session, 1); + stream->remote_window_size = 1 << 20; + + CU_ASSERT(NGHTTP2_DATA == ud.sent_frame_type); + /* data for DATA[1] is read from data_prd but it is not sent */ + CU_ASSERT(ud.data_source_length == NGHTTP2_DATA_PAYLOADLEN * 2); + + nghttp2_submit_ping(session, NGHTTP2_FLAG_NONE, NULL); + ud.block_count = 2; + /* Sends DATA[1] + PING, PING is interleaved in DATA sequence */ + CU_ASSERT(0 == nghttp2_session_send(session)); + CU_ASSERT(NGHTTP2_PING == ud.sent_frame_type); + /* data for DATA[2] is read from data_prd but it is not sent */ + CU_ASSERT(ud.data_source_length == NGHTTP2_DATA_PAYLOADLEN); + + ud.block_count = 2; + /* Sends DATA[2..3] */ + CU_ASSERT(0 == nghttp2_session_send(session)); + + CU_ASSERT(stream->shut_flags & NGHTTP2_SHUT_WR); + + nghttp2_session_del(session); +} + +static void check_session_recv_data_with_padding(nghttp2_bufs *bufs, + size_t datalen, + nghttp2_mem *mem) { + nghttp2_session *session; + my_user_data ud; + nghttp2_session_callbacks callbacks; + uint8_t *in; + size_t inlen; + + memset(&callbacks, 0, sizeof(callbacks)); + callbacks.on_frame_recv_callback = on_frame_recv_callback; + callbacks.on_data_chunk_recv_callback = on_data_chunk_recv_callback; + nghttp2_session_server_new(&session, &callbacks, &ud); + + nghttp2_session_open_stream(session, 1, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, NGHTTP2_STREAM_OPENING, NULL); + + inlen = nghttp2_bufs_remove(bufs, &in); + + ud.frame_recv_cb_called = 0; + ud.data_chunk_len = 0; + + CU_ASSERT((ssize_t)inlen == nghttp2_session_mem_recv(session, in, inlen)); + + CU_ASSERT(1 == ud.frame_recv_cb_called); + CU_ASSERT(datalen == ud.data_chunk_len); + + mem->free(in, NULL); + nghttp2_session_del(session); +} + +void test_nghttp2_session_pack_data_with_padding(void) { + nghttp2_session *session; + my_user_data ud; + nghttp2_session_callbacks callbacks; + nghttp2_data_provider data_prd; + nghttp2_frame *frame; + size_t datalen = 55; + nghttp2_mem *mem; + + mem = nghttp2_mem_default(); + + memset(&callbacks, 0, sizeof(callbacks)); + callbacks.send_callback = block_count_send_callback; + callbacks.on_frame_send_callback = on_frame_send_callback; + callbacks.select_padding_callback = select_padding_callback; + + data_prd.read_callback = fixed_length_data_source_read_callback; + + nghttp2_session_client_new(&session, &callbacks, &ud); + + ud.padlen = 63; + + nghttp2_submit_request(session, NULL, NULL, 0, &data_prd, NULL); + ud.block_count = 1; + ud.data_source_length = datalen; + /* Sends HEADERS */ + CU_ASSERT(0 == nghttp2_session_send(session)); + CU_ASSERT(NGHTTP2_HEADERS == ud.sent_frame_type); + + frame = &session->aob.item->frame; + + CU_ASSERT(ud.padlen == frame->data.padlen); + CU_ASSERT(frame->hd.flags & NGHTTP2_FLAG_PADDED); + + /* Check reception of this DATA frame */ + check_session_recv_data_with_padding(&session->aob.framebufs, datalen, mem); + + nghttp2_session_del(session); +} + +void test_nghttp2_session_pack_headers_with_padding(void) { + nghttp2_session *session, *sv_session; + accumulator acc; + my_user_data ud; + nghttp2_session_callbacks callbacks; + + memset(&callbacks, 0, sizeof(callbacks)); + callbacks.send_callback = accumulator_send_callback; + callbacks.on_frame_send_callback = on_frame_send_callback; + callbacks.select_padding_callback = select_padding_callback; + callbacks.on_frame_recv_callback = on_frame_recv_callback; + + acc.length = 0; + ud.acc = &acc; + + nghttp2_session_client_new(&session, &callbacks, &ud); + nghttp2_session_server_new(&sv_session, &callbacks, &ud); + + ud.padlen = 163; + + CU_ASSERT(1 == nghttp2_submit_request(session, NULL, reqnv, ARRLEN(reqnv), + NULL, NULL)); + CU_ASSERT(0 == nghttp2_session_send(session)); + + CU_ASSERT(acc.length < NGHTTP2_MAX_PAYLOADLEN); + ud.frame_recv_cb_called = 0; + CU_ASSERT((ssize_t)acc.length == + nghttp2_session_mem_recv(sv_session, acc.buf, acc.length)); + CU_ASSERT(1 == ud.frame_recv_cb_called); + CU_ASSERT(NULL == nghttp2_session_get_next_ob_item(sv_session)); + + nghttp2_session_del(sv_session); + nghttp2_session_del(session); +} + +void test_nghttp2_pack_settings_payload(void) { + nghttp2_settings_entry iv[2]; + uint8_t buf[64]; + ssize_t len; + nghttp2_settings_entry *resiv; + size_t resniv; + nghttp2_mem *mem; + + mem = nghttp2_mem_default(); + + iv[0].settings_id = NGHTTP2_SETTINGS_HEADER_TABLE_SIZE; + iv[0].value = 1023; + iv[1].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE; + iv[1].value = 4095; + + len = nghttp2_pack_settings_payload(buf, sizeof(buf), iv, 2); + CU_ASSERT(2 * NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH == len); + CU_ASSERT(0 == nghttp2_frame_unpack_settings_payload2(&resiv, &resniv, buf, + len, mem)); + CU_ASSERT(2 == resniv); + CU_ASSERT(NGHTTP2_SETTINGS_HEADER_TABLE_SIZE == resiv[0].settings_id); + CU_ASSERT(1023 == resiv[0].value); + CU_ASSERT(NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE == resiv[1].settings_id); + CU_ASSERT(4095 == resiv[1].value); + + mem->free(resiv, NULL); + + len = nghttp2_pack_settings_payload(buf, 9 /* too small */, iv, 2); + CU_ASSERT(NGHTTP2_ERR_INSUFF_BUFSIZE == len); +} + +#define check_stream_dep_sib(STREAM, DEP_PREV, DEP_NEXT, SIB_PREV, SIB_NEXT) \ + do { \ + CU_ASSERT(DEP_PREV == STREAM->dep_prev); \ + CU_ASSERT(DEP_NEXT == STREAM->dep_next); \ + CU_ASSERT(SIB_PREV == STREAM->sib_prev); \ + CU_ASSERT(SIB_NEXT == STREAM->sib_next); \ + } while (0) + +/* nghttp2_stream_dep_add() and its families functions should be + tested in nghttp2_stream_test.c, but it is easier to use + nghttp2_session_open_stream(). Therefore, we test them here. */ +void test_nghttp2_session_stream_dep_add(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + nghttp2_stream *a, *b, *c, *d, *e; + + memset(&callbacks, 0, sizeof(callbacks)); + + nghttp2_session_server_new(&session, &callbacks, NULL); + + a = open_stream(session, 1); + + c = open_stream_with_dep(session, 5, a); + b = open_stream_with_dep(session, 3, a); + d = open_stream_with_dep(session, 7, c); + + /* a + * | + * b--c + * | + * d + */ + + CU_ASSERT(4 == a->num_substreams); + CU_ASSERT(1 == b->num_substreams); + CU_ASSERT(2 == c->num_substreams); + CU_ASSERT(1 == d->num_substreams); + + CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT * 2 == a->sum_dep_weight); + CU_ASSERT(0 == b->sum_dep_weight); + CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT == c->sum_dep_weight); + CU_ASSERT(0 == d->sum_dep_weight); + + check_stream_dep_sib(a, NULL, b, NULL, NULL); + check_stream_dep_sib(b, a, NULL, NULL, c); + check_stream_dep_sib(c, NULL, d, b, NULL); + check_stream_dep_sib(d, c, NULL, NULL, NULL); + + CU_ASSERT(4 == session->roots.num_streams); + CU_ASSERT(a == session->roots.head); + CU_ASSERT(NULL == a->root_next); + + e = open_stream_with_dep_excl(session, 9, a); + + /* a + * | + * e + * | + * b--c + * | + * d + */ + + CU_ASSERT(5 == a->num_substreams); + CU_ASSERT(4 == e->num_substreams); + CU_ASSERT(1 == b->num_substreams); + CU_ASSERT(2 == c->num_substreams); + CU_ASSERT(1 == d->num_substreams); + + CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT == a->sum_dep_weight); + CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT * 2 == e->sum_dep_weight); + CU_ASSERT(0 == b->sum_dep_weight); + CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT == c->sum_dep_weight); + CU_ASSERT(0 == d->sum_dep_weight); + + check_stream_dep_sib(a, NULL, e, NULL, NULL); + check_stream_dep_sib(e, a, b, NULL, NULL); + check_stream_dep_sib(b, e, NULL, NULL, c); + check_stream_dep_sib(c, NULL, d, b, NULL); + check_stream_dep_sib(d, c, NULL, NULL, NULL); + + CU_ASSERT(5 == session->roots.num_streams); + CU_ASSERT(a == session->roots.head); + CU_ASSERT(NULL == a->root_next); + + nghttp2_session_del(session); +} + +void test_nghttp2_session_stream_dep_remove(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + nghttp2_stream *a, *b, *c, *d, *e, *f; + + memset(&callbacks, 0, sizeof(callbacks)); + + /* Remove root */ + nghttp2_session_server_new(&session, &callbacks, NULL); + + a = open_stream(session, 1); + b = open_stream_with_dep(session, 3, a); + c = open_stream_with_dep(session, 5, a); + d = open_stream_with_dep(session, 7, c); + + /* a + * | + * c--b + * | + * d + */ + + nghttp2_stream_dep_remove(a); + + /* becomes: + * b c + * | + * d + */ + + CU_ASSERT(1 == a->num_substreams); + CU_ASSERT(1 == b->num_substreams); + CU_ASSERT(2 == c->num_substreams); + CU_ASSERT(1 == d->num_substreams); + + CU_ASSERT(0 == a->sum_dep_weight); + CU_ASSERT(0 == b->sum_dep_weight); + CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT == c->sum_dep_weight); + CU_ASSERT(0 == d->sum_dep_weight); + + check_stream_dep_sib(a, NULL, NULL, NULL, NULL); + check_stream_dep_sib(b, NULL, NULL, NULL, NULL); + check_stream_dep_sib(c, NULL, d, NULL, NULL); + check_stream_dep_sib(d, c, NULL, NULL, NULL); + + CU_ASSERT(3 == session->roots.num_streams); + CU_ASSERT(b == session->roots.head); + CU_ASSERT(c == b->root_next); + CU_ASSERT(NULL == c->root_next); + + nghttp2_session_del(session); + + /* Remove left most stream */ + nghttp2_session_server_new(&session, &callbacks, NULL); + + a = open_stream(session, 1); + b = open_stream_with_dep(session, 3, a); + c = open_stream_with_dep(session, 5, a); + d = open_stream_with_dep(session, 7, c); + + /* a + * | + * c--b + * | + * d + */ + + nghttp2_stream_dep_remove(b); + + /* becomes: + * a + * | + * c + * | + * d + */ + + CU_ASSERT(3 == a->num_substreams); + CU_ASSERT(1 == b->num_substreams); + CU_ASSERT(2 == c->num_substreams); + CU_ASSERT(1 == d->num_substreams); + + CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT == a->sum_dep_weight); + CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT == c->sum_dep_weight); + CU_ASSERT(0 == d->sum_dep_weight); + CU_ASSERT(0 == b->sum_dep_weight); + + check_stream_dep_sib(a, NULL, c, NULL, NULL); + check_stream_dep_sib(b, NULL, NULL, NULL, NULL); + check_stream_dep_sib(c, a, d, NULL, NULL); + check_stream_dep_sib(d, c, NULL, NULL, NULL); + + CU_ASSERT(3 == session->roots.num_streams); + CU_ASSERT(a == session->roots.head); + CU_ASSERT(NULL == a->root_next); + + nghttp2_session_del(session); + + /* Remove right most stream */ + nghttp2_session_server_new(&session, &callbacks, NULL); + + a = open_stream(session, 1); + b = open_stream_with_dep(session, 3, a); + c = open_stream_with_dep(session, 5, a); + d = open_stream_with_dep(session, 7, c); + + /* a + * | + * c--b + * | + * d + */ + + nghttp2_stream_dep_remove(c); + + /* becomes: + * a + * | + * d--b + */ + + CU_ASSERT(3 == a->num_substreams); + CU_ASSERT(1 == b->num_substreams); + CU_ASSERT(1 == c->num_substreams); + CU_ASSERT(1 == d->num_substreams); + + CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT * 2 == a->sum_dep_weight); + CU_ASSERT(0 == b->sum_dep_weight); + CU_ASSERT(0 == d->sum_dep_weight); + CU_ASSERT(0 == c->sum_dep_weight); + + check_stream_dep_sib(a, NULL, d, NULL, NULL); + check_stream_dep_sib(b, NULL, NULL, d, NULL); + check_stream_dep_sib(c, NULL, NULL, NULL, NULL); + check_stream_dep_sib(d, a, NULL, NULL, b); + + nghttp2_session_del(session); + + /* Remove middle stream */ + nghttp2_session_server_new(&session, &callbacks, NULL); + + a = open_stream(session, 1); + b = open_stream_with_dep(session, 3, a); + c = open_stream_with_dep(session, 5, a); + d = open_stream_with_dep(session, 7, a); + e = open_stream_with_dep(session, 9, c); + f = open_stream_with_dep(session, 11, c); + + /* a + * | + * d--c--b + * | + * f--e + */ + + CU_ASSERT(6 == a->num_substreams); + CU_ASSERT(1 == b->num_substreams); + CU_ASSERT(3 == c->num_substreams); + CU_ASSERT(1 == d->num_substreams); + CU_ASSERT(1 == e->num_substreams); + CU_ASSERT(1 == f->num_substreams); + + CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT * 3 == a->sum_dep_weight); + CU_ASSERT(0 == b->sum_dep_weight); + CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT * 2 == c->sum_dep_weight); + CU_ASSERT(0 == d->sum_dep_weight); + CU_ASSERT(0 == e->sum_dep_weight); + CU_ASSERT(0 == f->sum_dep_weight); + + nghttp2_stream_dep_remove(c); + + /* becomes: + * a + * | + * d--f--e--b + */ + + CU_ASSERT(5 == a->num_substreams); + CU_ASSERT(1 == b->num_substreams); + CU_ASSERT(1 == c->num_substreams); + CU_ASSERT(1 == d->num_substreams); + CU_ASSERT(1 == e->num_substreams); + CU_ASSERT(1 == f->num_substreams); + + /* c's weight 16 is distributed evenly to e and f. Each weight of e + and f becomes 8. */ + CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT * 2 + 8 * 2 == a->sum_dep_weight); + CU_ASSERT(0 == b->sum_dep_weight); + CU_ASSERT(0 == c->sum_dep_weight); + CU_ASSERT(0 == d->sum_dep_weight); + CU_ASSERT(0 == e->sum_dep_weight); + CU_ASSERT(0 == f->sum_dep_weight); + + check_stream_dep_sib(a, NULL, d, NULL, NULL); + check_stream_dep_sib(b, NULL, NULL, e, NULL); + check_stream_dep_sib(c, NULL, NULL, NULL, NULL); + check_stream_dep_sib(e, NULL, NULL, f, b); + check_stream_dep_sib(f, NULL, NULL, d, e); + check_stream_dep_sib(d, a, NULL, NULL, f); + + nghttp2_session_del(session); +} + +void test_nghttp2_session_stream_dep_add_subtree(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + nghttp2_stream *a, *b, *c, *d, *e, *f; + + memset(&callbacks, 0, sizeof(callbacks)); + + /* dep_stream has dep_next */ + nghttp2_session_server_new(&session, &callbacks, NULL); + + a = open_stream(session, 1); + b = open_stream_with_dep(session, 3, a); + c = open_stream_with_dep(session, 5, a); + d = open_stream_with_dep(session, 7, c); + + e = open_stream(session, 9); + f = open_stream_with_dep(session, 11, e); + + /* a e + * | | + * c--b f + * | + * d + */ + + nghttp2_stream_dep_add_subtree(a, e, session); + + /* becomes + * a + * | + * e--c--b + * | | + * f d + */ + + CU_ASSERT(6 == a->num_substreams); + CU_ASSERT(1 == b->num_substreams); + CU_ASSERT(2 == c->num_substreams); + CU_ASSERT(1 == d->num_substreams); + CU_ASSERT(2 == e->num_substreams); + CU_ASSERT(1 == f->num_substreams); + + CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT * 3 == a->sum_dep_weight); + CU_ASSERT(0 == b->sum_dep_weight); + CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT == c->sum_dep_weight); + CU_ASSERT(0 == d->sum_dep_weight); + CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT == e->sum_dep_weight); + CU_ASSERT(0 == f->sum_dep_weight); + + check_stream_dep_sib(a, NULL, e, NULL, NULL); + check_stream_dep_sib(b, NULL, NULL, c, NULL); + check_stream_dep_sib(c, NULL, d, e, b); + check_stream_dep_sib(d, c, NULL, NULL, NULL); + check_stream_dep_sib(e, a, f, NULL, c); + check_stream_dep_sib(f, e, NULL, NULL, NULL); + + nghttp2_session_del(session); + + /* dep_stream has dep_next and now we insert subtree */ + nghttp2_session_server_new(&session, &callbacks, NULL); + + a = open_stream(session, 1); + b = open_stream_with_dep(session, 3, a); + c = open_stream_with_dep(session, 5, a); + d = open_stream_with_dep(session, 7, c); + + e = open_stream(session, 9); + f = open_stream_with_dep(session, 11, e); + + /* a e + * | | + * c--b f + * | + * d + */ + + nghttp2_stream_dep_insert_subtree(a, e, session); + + /* becomes + * a + * | + * e + * | + * f--c--b + * | + * d + */ + + CU_ASSERT(6 == a->num_substreams); + CU_ASSERT(1 == b->num_substreams); + CU_ASSERT(2 == c->num_substreams); + CU_ASSERT(1 == d->num_substreams); + CU_ASSERT(5 == e->num_substreams); + CU_ASSERT(1 == f->num_substreams); + + CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT == a->sum_dep_weight); + CU_ASSERT(0 == b->sum_dep_weight); + CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT == c->sum_dep_weight); + CU_ASSERT(0 == d->sum_dep_weight); + CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT * 3 == e->sum_dep_weight); + CU_ASSERT(0 == f->sum_dep_weight); + + check_stream_dep_sib(a, NULL, e, NULL, NULL); + check_stream_dep_sib(e, a, f, NULL, NULL); + check_stream_dep_sib(f, e, NULL, NULL, c); + check_stream_dep_sib(b, NULL, NULL, c, NULL); + check_stream_dep_sib(c, NULL, d, f, b); + check_stream_dep_sib(d, c, NULL, NULL, NULL); + + nghttp2_session_del(session); +} + +void test_nghttp2_session_stream_dep_remove_subtree(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + nghttp2_stream *a, *b, *c, *d, *e; + + memset(&callbacks, 0, sizeof(callbacks)); + + /* Remove left most stream */ + nghttp2_session_server_new(&session, &callbacks, NULL); + + a = open_stream(session, 1); + b = open_stream_with_dep(session, 3, a); + c = open_stream_with_dep(session, 5, a); + d = open_stream_with_dep(session, 7, c); + + /* a + * | + * c--b + * | + * d + */ + + nghttp2_stream_dep_remove_subtree(c); + + /* becomes + * a c + * | | + * b d + */ + + CU_ASSERT(2 == a->num_substreams); + CU_ASSERT(1 == b->num_substreams); + CU_ASSERT(2 == c->num_substreams); + CU_ASSERT(1 == d->num_substreams); + + CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT == a->sum_dep_weight); + CU_ASSERT(0 == b->sum_dep_weight); + CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT == c->sum_dep_weight); + CU_ASSERT(0 == d->sum_dep_weight); + + check_stream_dep_sib(a, NULL, b, NULL, NULL); + check_stream_dep_sib(b, a, NULL, NULL, NULL); + check_stream_dep_sib(c, NULL, d, NULL, NULL); + check_stream_dep_sib(d, c, NULL, NULL, NULL); + + nghttp2_session_del(session); + + /* Remove right most stream */ + nghttp2_session_server_new(&session, &callbacks, NULL); + + a = open_stream(session, 1); + b = open_stream_with_dep(session, 3, a); + c = open_stream_with_dep(session, 5, a); + d = open_stream_with_dep(session, 7, c); + + /* a + * | + * c--b + * | + * d + */ + + nghttp2_stream_dep_remove_subtree(b); + + /* becomes + * a b + * | + * c + * | + * d + */ + + CU_ASSERT(3 == a->num_substreams); + CU_ASSERT(1 == b->num_substreams); + CU_ASSERT(2 == c->num_substreams); + CU_ASSERT(1 == d->num_substreams); + + CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT == a->sum_dep_weight); + CU_ASSERT(0 == b->sum_dep_weight); + CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT == c->sum_dep_weight); + CU_ASSERT(0 == d->sum_dep_weight); + + check_stream_dep_sib(a, NULL, c, NULL, NULL); + check_stream_dep_sib(c, a, d, NULL, NULL); + check_stream_dep_sib(d, c, NULL, NULL, NULL); + check_stream_dep_sib(b, NULL, NULL, NULL, NULL); + + nghttp2_session_del(session); + + /* Remove middle stream */ + nghttp2_session_server_new(&session, &callbacks, NULL); + + a = open_stream(session, 1); + e = open_stream_with_dep(session, 9, a); + c = open_stream_with_dep(session, 5, a); + b = open_stream_with_dep(session, 3, a); + d = open_stream_with_dep(session, 7, c); + + /* a + * | + * b--c--e + * | + * d + */ + + nghttp2_stream_dep_remove_subtree(c); + + /* becomes + * a c + * | | + * b--e d + */ + + CU_ASSERT(3 == a->num_substreams); + CU_ASSERT(1 == b->num_substreams); + CU_ASSERT(1 == e->num_substreams); + CU_ASSERT(2 == c->num_substreams); + CU_ASSERT(1 == d->num_substreams); + + CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT * 2 == a->sum_dep_weight); + CU_ASSERT(0 == b->sum_dep_weight); + CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT == c->sum_dep_weight); + CU_ASSERT(0 == d->sum_dep_weight); + CU_ASSERT(0 == e->sum_dep_weight); + + check_stream_dep_sib(a, NULL, b, NULL, NULL); + check_stream_dep_sib(b, a, NULL, NULL, e); + check_stream_dep_sib(e, NULL, NULL, b, NULL); + check_stream_dep_sib(c, NULL, d, NULL, NULL); + check_stream_dep_sib(d, c, NULL, NULL, NULL); + + nghttp2_session_del(session); +} + +void test_nghttp2_session_stream_dep_all_your_stream_are_belong_to_us(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + nghttp2_stream *a, *b, *c, *d; + + memset(&callbacks, 0, sizeof(callbacks)); + + nghttp2_session_server_new(&session, &callbacks, NULL); + + a = open_stream(session, 1); + b = open_stream_with_dep(session, 3, a); + + c = open_stream(session, 5); + + /* a c + * | + * b + */ + + nghttp2_stream_dep_remove_subtree(c); + CU_ASSERT(0 == + nghttp2_stream_dep_all_your_stream_are_belong_to_us(c, session)); + + /* + * c + * | + * a + * | + * b + */ + + CU_ASSERT(3 == c->num_substreams); + CU_ASSERT(2 == a->num_substreams); + CU_ASSERT(1 == b->num_substreams); + + CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT == c->sum_dep_weight); + CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT == a->sum_dep_weight); + CU_ASSERT(0 == b->sum_dep_weight); + + check_stream_dep_sib(c, NULL, a, NULL, NULL); + check_stream_dep_sib(a, c, b, NULL, NULL); + check_stream_dep_sib(b, a, NULL, NULL, NULL); + + nghttp2_session_del(session); + + nghttp2_session_server_new(&session, &callbacks, NULL); + + a = open_stream(session, 1); + + b = open_stream(session, 3); + + c = open_stream(session, 5); + + /* + * a b c + */ + + nghttp2_stream_dep_remove_subtree(c); + CU_ASSERT(0 == + nghttp2_stream_dep_all_your_stream_are_belong_to_us(c, session)); + + /* + * c + * | + * b--a + */ + + CU_ASSERT(3 == c->num_substreams); + CU_ASSERT(1 == a->num_substreams); + CU_ASSERT(1 == b->num_substreams); + + CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT * 2 == c->sum_dep_weight); + CU_ASSERT(0 == b->sum_dep_weight); + CU_ASSERT(0 == a->sum_dep_weight); + + check_stream_dep_sib(c, NULL, b, NULL, NULL); + check_stream_dep_sib(b, c, NULL, NULL, a); + check_stream_dep_sib(a, NULL, NULL, b, NULL); + + nghttp2_session_del(session); + + nghttp2_session_server_new(&session, &callbacks, NULL); + + a = open_stream(session, 1); + b = open_stream_with_dep(session, 3, a); + + c = open_stream(session, 5); + d = open_stream_with_dep(session, 7, c); + + /* a c + * | | + * b d + */ + + nghttp2_stream_dep_remove_subtree(c); + CU_ASSERT(0 == + nghttp2_stream_dep_all_your_stream_are_belong_to_us(c, session)); + + /* + * c + * | + * a--d + * | + * b + */ + + CU_ASSERT(4 == c->num_substreams); + CU_ASSERT(1 == d->num_substreams); + CU_ASSERT(2 == a->num_substreams); + CU_ASSERT(1 == b->num_substreams); + + CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT * 2 == c->sum_dep_weight); + CU_ASSERT(0 == d->sum_dep_weight); + CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT == a->sum_dep_weight); + CU_ASSERT(0 == b->sum_dep_weight); + + check_stream_dep_sib(c, NULL, a, NULL, NULL); + check_stream_dep_sib(d, NULL, NULL, a, NULL); + check_stream_dep_sib(a, c, b, NULL, d); + check_stream_dep_sib(b, a, NULL, NULL, NULL); + + nghttp2_session_del(session); +} + +void test_nghttp2_session_stream_attach_item(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + nghttp2_stream *a, *b, *c, *d; + nghttp2_outbound_item *da, *db, *dc, *dd; + nghttp2_mem *mem; + + mem = nghttp2_mem_default(); + + memset(&callbacks, 0, sizeof(callbacks)); + + nghttp2_session_server_new(&session, &callbacks, NULL); + + a = open_stream(session, 1); + b = open_stream_with_dep(session, 3, a); + c = open_stream_with_dep(session, 5, a); + d = open_stream_with_dep(session, 7, c); + + /* a + * | + * c--b + * | + * d + */ + + db = create_data_ob_item(mem); + + nghttp2_stream_attach_item(b, db, session); + + CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == a->dpri); + CU_ASSERT(NGHTTP2_STREAM_DPRI_TOP == b->dpri); + CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == c->dpri); + CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == d->dpri); + + CU_ASSERT(16 == b->effective_weight); + + CU_ASSERT(16 == a->sum_norest_weight); + + CU_ASSERT(1 == db->queued); + + dc = create_data_ob_item(mem); + + nghttp2_stream_attach_item(c, dc, session); + + CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == a->dpri); + CU_ASSERT(NGHTTP2_STREAM_DPRI_TOP == b->dpri); + CU_ASSERT(NGHTTP2_STREAM_DPRI_TOP == c->dpri); + CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == d->dpri); + + CU_ASSERT(16 * 16 / 32 == b->effective_weight); + CU_ASSERT(16 * 16 / 32 == c->effective_weight); + + CU_ASSERT(32 == a->sum_norest_weight); + + CU_ASSERT(1 == dc->queued); + + da = create_data_ob_item(mem); + + nghttp2_stream_attach_item(a, da, session); + + CU_ASSERT(NGHTTP2_STREAM_DPRI_TOP == a->dpri); + CU_ASSERT(NGHTTP2_STREAM_DPRI_REST == b->dpri); + CU_ASSERT(NGHTTP2_STREAM_DPRI_REST == c->dpri); + CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == d->dpri); + + CU_ASSERT(16 == a->effective_weight); + + CU_ASSERT(1 == da->queued); + + nghttp2_stream_detach_item(a, session); + + CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == a->dpri); + CU_ASSERT(NGHTTP2_STREAM_DPRI_TOP == b->dpri); + CU_ASSERT(NGHTTP2_STREAM_DPRI_TOP == c->dpri); + CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == d->dpri); + + CU_ASSERT(16 * 16 / 32 == b->effective_weight); + CU_ASSERT(16 * 16 / 32 == c->effective_weight); + + dd = create_data_ob_item(mem); + + nghttp2_stream_attach_item(d, dd, session); + + CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == a->dpri); + CU_ASSERT(NGHTTP2_STREAM_DPRI_TOP == b->dpri); + CU_ASSERT(NGHTTP2_STREAM_DPRI_TOP == c->dpri); + CU_ASSERT(NGHTTP2_STREAM_DPRI_REST == d->dpri); + + CU_ASSERT(16 * 16 / 32 == b->effective_weight); + CU_ASSERT(16 * 16 / 32 == c->effective_weight); + + CU_ASSERT(0 == dd->queued); + + nghttp2_stream_detach_item(c, session); + + CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == a->dpri); + CU_ASSERT(NGHTTP2_STREAM_DPRI_TOP == b->dpri); + CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == c->dpri); + CU_ASSERT(NGHTTP2_STREAM_DPRI_TOP == d->dpri); + + CU_ASSERT(16 * 16 / 32 == b->effective_weight); + CU_ASSERT(16 * 16 / 32 == d->effective_weight); + + CU_ASSERT(1 == dd->queued); + + nghttp2_stream_detach_item(b, session); + + CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == a->dpri); + CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == b->dpri); + CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == c->dpri); + CU_ASSERT(NGHTTP2_STREAM_DPRI_TOP == d->dpri); + + CU_ASSERT(16 * 16 / 16 == d->effective_weight); + + CU_ASSERT(1 == dd->queued); + + nghttp2_session_del(session); +} + +void test_nghttp2_session_stream_attach_item_subtree(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + nghttp2_stream *a, *b, *c, *d, *e, *f; + nghttp2_outbound_item *db, *dd, *de; + nghttp2_mem *mem; + + mem = nghttp2_mem_default(); + + memset(&callbacks, 0, sizeof(callbacks)); + + nghttp2_session_server_new(&session, &callbacks, NULL); + + a = open_stream(session, 1); + b = open_stream_with_dep(session, 3, a); + c = open_stream_with_dep(session, 5, a); + d = open_stream_with_dep(session, 7, c); + + e = open_stream(session, 9); + f = open_stream_with_dep(session, 11, e); + /* + * a e + * | | + * c--b f + * | + * d + */ + + de = create_data_ob_item(mem); + + nghttp2_stream_attach_item(e, de, session); + + db = create_data_ob_item(mem); + + nghttp2_stream_attach_item(b, db, session); + + CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == a->dpri); + CU_ASSERT(NGHTTP2_STREAM_DPRI_TOP == b->dpri); + CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == c->dpri); + CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == d->dpri); + CU_ASSERT(NGHTTP2_STREAM_DPRI_TOP == e->dpri); + CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == f->dpri); + + CU_ASSERT(16 == b->effective_weight); + CU_ASSERT(16 == e->effective_weight); + + /* Insert subtree e under a */ + + nghttp2_stream_dep_remove_subtree(e); + nghttp2_stream_dep_insert_subtree(a, e, session); + + /* + * a + * | + * e + * | + * f--c--b + * | + * d + */ + + CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == a->dpri); + CU_ASSERT(NGHTTP2_STREAM_DPRI_REST == b->dpri); + CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == c->dpri); + CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == d->dpri); + CU_ASSERT(NGHTTP2_STREAM_DPRI_TOP == e->dpri); + CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == f->dpri); + + CU_ASSERT(16 == e->effective_weight); + + /* Remove subtree b */ + + nghttp2_stream_dep_remove_subtree(b); + + nghttp2_stream_dep_make_root(b, session); + + /* + * a b + * | + * e + * | + * f--c + * | + * d + */ + + CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == a->dpri); + CU_ASSERT(NGHTTP2_STREAM_DPRI_TOP == b->dpri); + CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == c->dpri); + CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == d->dpri); + CU_ASSERT(NGHTTP2_STREAM_DPRI_TOP == e->dpri); + CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == f->dpri); + + CU_ASSERT(16 == b->effective_weight); + CU_ASSERT(16 == e->effective_weight); + + /* Remove subtree a */ + + nghttp2_stream_dep_remove_subtree(a); + + nghttp2_stream_dep_make_root(a, session); + + CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == a->dpri); + CU_ASSERT(NGHTTP2_STREAM_DPRI_TOP == b->dpri); + CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == c->dpri); + CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == d->dpri); + CU_ASSERT(NGHTTP2_STREAM_DPRI_TOP == e->dpri); + CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == f->dpri); + + /* Remove subtree c */ + + nghttp2_stream_dep_remove_subtree(c); + + nghttp2_stream_dep_make_root(c, session); + + /* + * a b c + * | | + * e d + * | + * f + */ + + CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == a->dpri); + CU_ASSERT(NGHTTP2_STREAM_DPRI_TOP == b->dpri); + CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == c->dpri); + CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == d->dpri); + CU_ASSERT(NGHTTP2_STREAM_DPRI_TOP == e->dpri); + CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == f->dpri); + + dd = create_data_ob_item(mem); + + nghttp2_stream_attach_item(d, dd, session); + + /* Add subtree c to a */ + + nghttp2_stream_dep_remove_subtree(c); + nghttp2_stream_dep_add_subtree(a, c, session); + + /* + * a b + * | + * c--e + * | | + * d f + */ + + CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == a->dpri); + CU_ASSERT(NGHTTP2_STREAM_DPRI_TOP == b->dpri); + CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == c->dpri); + CU_ASSERT(NGHTTP2_STREAM_DPRI_TOP == d->dpri); + CU_ASSERT(NGHTTP2_STREAM_DPRI_TOP == e->dpri); + CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == f->dpri); + + CU_ASSERT(16 == b->effective_weight); + CU_ASSERT(16 * 16 / 32 == e->effective_weight); + CU_ASSERT(16 * 16 / 32 == e->effective_weight); + + CU_ASSERT(32 == a->sum_norest_weight); + CU_ASSERT(16 == c->sum_norest_weight); + + /* Insert b under a */ + + nghttp2_stream_dep_remove_subtree(b); + nghttp2_stream_dep_insert_subtree(a, b, session); + + /* + * a + * | + * b + * | + * e--c + * | | + * f d + */ + + CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == a->dpri); + CU_ASSERT(NGHTTP2_STREAM_DPRI_TOP == b->dpri); + CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == c->dpri); + CU_ASSERT(NGHTTP2_STREAM_DPRI_REST == d->dpri); + CU_ASSERT(NGHTTP2_STREAM_DPRI_REST == e->dpri); + CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == f->dpri); + + CU_ASSERT(16 == b->effective_weight); + + CU_ASSERT(16 == a->sum_norest_weight); + CU_ASSERT(0 == b->sum_norest_weight); + + /* Remove subtree b */ + + nghttp2_stream_dep_remove_subtree(b); + nghttp2_stream_dep_make_root(b, session); + + /* + * b a + * | + * e--c + * | | + * f d + */ + + CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == a->dpri); + CU_ASSERT(NGHTTP2_STREAM_DPRI_TOP == b->dpri); + CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == c->dpri); + CU_ASSERT(NGHTTP2_STREAM_DPRI_REST == d->dpri); + CU_ASSERT(NGHTTP2_STREAM_DPRI_REST == e->dpri); + CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == f->dpri); + + CU_ASSERT(0 == a->sum_norest_weight); + CU_ASSERT(0 == b->sum_norest_weight); + + nghttp2_session_del(session); +} + +void test_nghttp2_session_keep_closed_stream(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + const size_t max_concurrent_streams = 5; + nghttp2_settings_entry iv = {NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, + max_concurrent_streams}; + size_t i; + + memset(&callbacks, 0, sizeof(callbacks)); + callbacks.send_callback = null_send_callback; + + nghttp2_session_server_new(&session, &callbacks, NULL); + + nghttp2_submit_settings(session, NGHTTP2_FLAG_NONE, &iv, 1); + + for (i = 0; i < max_concurrent_streams; ++i) { + open_stream(session, (int)i * 2 + 1); + } + + CU_ASSERT(0 == session->num_closed_streams); + + nghttp2_session_close_stream(session, 1, NGHTTP2_NO_ERROR); + + CU_ASSERT(1 == session->num_closed_streams); + CU_ASSERT(1 == session->closed_stream_tail->stream_id); + CU_ASSERT(session->closed_stream_tail == session->closed_stream_head); + + nghttp2_session_close_stream(session, 5, NGHTTP2_NO_ERROR); + + CU_ASSERT(2 == session->num_closed_streams); + CU_ASSERT(5 == session->closed_stream_tail->stream_id); + CU_ASSERT(1 == session->closed_stream_head->stream_id); + CU_ASSERT(session->closed_stream_head == + session->closed_stream_tail->closed_prev); + CU_ASSERT(NULL == session->closed_stream_tail->closed_next); + CU_ASSERT(session->closed_stream_tail == + session->closed_stream_head->closed_next); + CU_ASSERT(NULL == session->closed_stream_head->closed_prev); + + open_stream(session, 11); + + CU_ASSERT(1 == session->num_closed_streams); + CU_ASSERT(5 == session->closed_stream_tail->stream_id); + CU_ASSERT(session->closed_stream_tail == session->closed_stream_head); + CU_ASSERT(NULL == session->closed_stream_head->closed_prev); + CU_ASSERT(NULL == session->closed_stream_head->closed_next); + + open_stream(session, 13); + + CU_ASSERT(0 == session->num_closed_streams); + CU_ASSERT(NULL == session->closed_stream_tail); + CU_ASSERT(NULL == session->closed_stream_head); + + nghttp2_session_del(session); +} + +void test_nghttp2_session_keep_idle_stream(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + const size_t max_concurrent_streams = 1; + nghttp2_settings_entry iv = {NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, + max_concurrent_streams}; + int i; + + memset(&callbacks, 0, sizeof(callbacks)); + callbacks.send_callback = null_send_callback; + + nghttp2_session_server_new(&session, &callbacks, NULL); + + nghttp2_submit_settings(session, NGHTTP2_FLAG_NONE, &iv, 1); + + /* We at least allow 2 idle streams even if max concurrent streams + is very low. */ + for (i = 0; i < 2; ++i) { + nghttp2_session_open_stream(session, i * 2 + 1, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, NGHTTP2_STREAM_IDLE, NULL); + } + + CU_ASSERT(2 == session->num_idle_streams); + + CU_ASSERT(1 == session->idle_stream_head->stream_id); + CU_ASSERT(3 == session->idle_stream_tail->stream_id); + + nghttp2_session_open_stream(session, 5, NGHTTP2_FLAG_NONE, &pri_spec_default, + NGHTTP2_STREAM_IDLE, NULL); + + CU_ASSERT(2 == session->num_idle_streams); + + CU_ASSERT(3 == session->idle_stream_head->stream_id); + CU_ASSERT(5 == session->idle_stream_tail->stream_id); + + nghttp2_session_del(session); +} + +void test_nghttp2_session_detach_idle_stream(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + int i; + nghttp2_stream *stream; + + memset(&callbacks, 0, sizeof(callbacks)); + callbacks.send_callback = null_send_callback; + + nghttp2_session_server_new(&session, &callbacks, NULL); + + for (i = 1; i <= 3; ++i) { + nghttp2_session_open_stream(session, i, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, NGHTTP2_STREAM_IDLE, NULL); + } + + CU_ASSERT(3 == session->num_idle_streams); + + /* Detach middle stream */ + stream = nghttp2_session_get_stream_raw(session, 2); + + CU_ASSERT(session->idle_stream_head == stream->closed_prev); + CU_ASSERT(session->idle_stream_tail == stream->closed_next); + CU_ASSERT(stream == session->idle_stream_head->closed_next); + CU_ASSERT(stream == session->idle_stream_tail->closed_prev); + + nghttp2_session_detach_idle_stream(session, stream); + + CU_ASSERT(2 == session->num_idle_streams); + + CU_ASSERT(NULL == stream->closed_prev); + CU_ASSERT(NULL == stream->closed_next); + + CU_ASSERT(session->idle_stream_head == + session->idle_stream_tail->closed_prev); + CU_ASSERT(session->idle_stream_tail == + session->idle_stream_head->closed_next); + + /* Detach head stream */ + stream = session->idle_stream_head; + + nghttp2_session_detach_idle_stream(session, stream); + + CU_ASSERT(1 == session->num_idle_streams); + + CU_ASSERT(session->idle_stream_head == session->idle_stream_tail); + CU_ASSERT(NULL == session->idle_stream_head->closed_prev); + CU_ASSERT(NULL == session->idle_stream_head->closed_next); + + /* Detach last stream */ + + stream = session->idle_stream_head; + + nghttp2_session_detach_idle_stream(session, stream); + + CU_ASSERT(0 == session->num_idle_streams); + + CU_ASSERT(NULL == session->idle_stream_head); + CU_ASSERT(NULL == session->idle_stream_tail); + + for (i = 4; i <= 5; ++i) { + nghttp2_session_open_stream(session, i, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, NGHTTP2_STREAM_IDLE, NULL); + } + + CU_ASSERT(2 == session->num_idle_streams); + + /* Detach tail stream */ + + stream = session->idle_stream_tail; + + nghttp2_session_detach_idle_stream(session, stream); + + CU_ASSERT(1 == session->num_idle_streams); + + CU_ASSERT(session->idle_stream_head == session->idle_stream_tail); + CU_ASSERT(NULL == session->idle_stream_head->closed_prev); + CU_ASSERT(NULL == session->idle_stream_head->closed_next); + + nghttp2_session_del(session); +} + +void test_nghttp2_session_large_dep_tree(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + size_t i; + nghttp2_stream *dep_stream = NULL; + nghttp2_stream *root_stream; + int32_t stream_id; + + memset(&callbacks, 0, sizeof(callbacks)); + callbacks.send_callback = null_send_callback; + + nghttp2_session_server_new(&session, &callbacks, NULL); + + stream_id = 1; + for (i = 0; i < NGHTTP2_MAX_DEP_TREE_LENGTH; ++i) { + dep_stream = open_stream_with_dep(session, stream_id, dep_stream); + stream_id += 2; + } + + root_stream = nghttp2_session_get_stream(session, 1); + + /* Check that last dep_stream must be part of tree */ + CU_ASSERT(nghttp2_stream_dep_subtree_find(root_stream, dep_stream)); + + dep_stream = open_stream_with_dep(session, stream_id, dep_stream); + + /* We exceeded NGHTTP2_MAX_DEP_TREE_LENGTH limit. dep_stream is now + root node and has no descendants. */ + CU_ASSERT(!nghttp2_stream_dep_subtree_find(root_stream, dep_stream)); + CU_ASSERT(nghttp2_stream_in_dep_tree(dep_stream)); + + nghttp2_session_del(session); +} + +void test_nghttp2_session_graceful_shutdown(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + my_user_data ud; + + memset(&callbacks, 0, sizeof(callbacks)); + callbacks.send_callback = null_send_callback; + callbacks.on_frame_send_callback = on_frame_send_callback; + callbacks.on_stream_close_callback = on_stream_close_callback; + + nghttp2_session_server_new(&session, &callbacks, &ud); + + open_stream(session, 301); + open_stream(session, 302); + open_stream(session, 309); + open_stream(session, 311); + open_stream(session, 319); + + CU_ASSERT(0 == nghttp2_submit_shutdown_notice(session)); + + ud.frame_send_cb_called = 0; + + CU_ASSERT(0 == nghttp2_session_send(session)); + + CU_ASSERT(1 == ud.frame_send_cb_called); + CU_ASSERT((1u << 31) - 1 == session->local_last_stream_id); + + CU_ASSERT(0 == nghttp2_submit_goaway(session, NGHTTP2_FLAG_NONE, 311, + NGHTTP2_NO_ERROR, NULL, 0)); + + ud.frame_send_cb_called = 0; + ud.stream_close_cb_called = 0; + + CU_ASSERT(0 == nghttp2_session_send(session)); + + CU_ASSERT(1 == ud.frame_send_cb_called); + CU_ASSERT(311 == session->local_last_stream_id); + CU_ASSERT(1 == ud.stream_close_cb_called); + + CU_ASSERT(0 == + nghttp2_session_terminate_session2(session, 301, NGHTTP2_NO_ERROR)); + + ud.frame_send_cb_called = 0; + ud.stream_close_cb_called = 0; + + CU_ASSERT(0 == nghttp2_session_send(session)); + + CU_ASSERT(1 == ud.frame_send_cb_called); + CU_ASSERT(301 == session->local_last_stream_id); + CU_ASSERT(2 == ud.stream_close_cb_called); + + CU_ASSERT(NULL != nghttp2_session_get_stream(session, 301)); + CU_ASSERT(NULL != nghttp2_session_get_stream(session, 302)); + CU_ASSERT(NULL == nghttp2_session_get_stream(session, 309)); + CU_ASSERT(NULL == nghttp2_session_get_stream(session, 311)); + CU_ASSERT(NULL == nghttp2_session_get_stream(session, 319)); + + nghttp2_session_del(session); +} + +void test_nghttp2_session_on_header_temporal_failure(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + my_user_data ud; + nghttp2_bufs bufs; + nghttp2_buf *buf; + nghttp2_hd_deflater deflater; + nghttp2_nv nv[] = {MAKE_NV("alpha", "bravo"), MAKE_NV("charlie", "delta")}; + nghttp2_nv *nva; + size_t hdpos; + ssize_t rv; + nghttp2_frame frame; + nghttp2_frame_hd hd; + nghttp2_outbound_item *item; + nghttp2_mem *mem; + + mem = nghttp2_mem_default(); + memset(&callbacks, 0, sizeof(callbacks)); + callbacks.on_header_callback = temporal_failure_on_header_callback; + + nghttp2_session_server_new(&session, &callbacks, &ud); + + frame_pack_bufs_init(&bufs); + + nghttp2_hd_deflate_init(&deflater, mem); + + nghttp2_nv_array_copy(&nva, reqnv, ARRLEN(reqnv), mem); + + nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_END_STREAM, 1, + NGHTTP2_HCAT_REQUEST, NULL, nva, ARRLEN(reqnv)); + nghttp2_frame_pack_headers(&bufs, &frame.headers, &deflater); + nghttp2_frame_headers_free(&frame.headers, mem); + + /* We are going to create CONTINUATION. First serialize header + block, and then frame header. */ + hdpos = nghttp2_bufs_len(&bufs); + + buf = &bufs.head->buf; + buf->last += NGHTTP2_FRAME_HDLEN; + + nghttp2_hd_deflate_hd_bufs(&deflater, &bufs, &nv[1], 1); + + nghttp2_frame_hd_init(&hd, + nghttp2_bufs_len(&bufs) - hdpos - NGHTTP2_FRAME_HDLEN, + NGHTTP2_CONTINUATION, NGHTTP2_FLAG_END_HEADERS, 1); + + nghttp2_frame_pack_frame_hd(&buf->pos[hdpos], &hd); + + ud.header_cb_called = 0; + rv = nghttp2_session_mem_recv(session, buf->pos, nghttp2_bufs_len(&bufs)); + + CU_ASSERT(rv == nghttp2_bufs_len(&bufs)); + CU_ASSERT(1 == ud.header_cb_called); + + item = nghttp2_session_get_next_ob_item(session); + + CU_ASSERT(NGHTTP2_RST_STREAM == item->frame.hd.type); + CU_ASSERT(1 == item->frame.hd.stream_id); + + /* Make sure no header decompression error occurred */ + CU_ASSERT(NGHTTP2_GOAWAY_NONE == session->goaway_flags); + + nghttp2_hd_deflate_free(&deflater); + nghttp2_session_del(session); + + nghttp2_bufs_reset(&bufs); + + /* Check for PUSH_PROMISE */ + nghttp2_hd_deflate_init(&deflater, mem); + nghttp2_session_client_new(&session, &callbacks, &ud); + + nghttp2_session_open_stream(session, 1, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, NGHTTP2_STREAM_OPENING, NULL); + + rv = pack_push_promise(&bufs, &deflater, 1, NGHTTP2_FLAG_END_HEADERS, 2, + reqnv, ARRLEN(reqnv), mem); + CU_ASSERT(0 == rv); + + ud.header_cb_called = 0; + rv = nghttp2_session_mem_recv(session, bufs.head->buf.pos, + nghttp2_bufs_len(&bufs)); + CU_ASSERT(nghttp2_bufs_len(&bufs) == rv); + CU_ASSERT(1 == ud.header_cb_called); + + item = nghttp2_session_get_next_ob_item(session); + CU_ASSERT(NGHTTP2_RST_STREAM == item->frame.hd.type); + CU_ASSERT(2 == item->frame.hd.stream_id); + CU_ASSERT(NGHTTP2_INTERNAL_ERROR == item->frame.rst_stream.error_code); + + nghttp2_session_del(session); + nghttp2_hd_deflate_free(&deflater); + nghttp2_bufs_free(&bufs); +} + +void test_nghttp2_session_recv_client_magic(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + ssize_t rv; + nghttp2_frame ping_frame; + uint8_t buf[16]; + + /* enable global nghttp2_enable_strict_preface here */ + nghttp2_enable_strict_preface = 1; + + memset(&callbacks, 0, sizeof(callbacks)); + + /* Check success case */ + nghttp2_session_server_new(&session, &callbacks, NULL); + + rv = nghttp2_session_mem_recv(session, (const uint8_t *)NGHTTP2_CLIENT_MAGIC, + NGHTTP2_CLIENT_MAGIC_LEN); + + CU_ASSERT(rv == NGHTTP2_CLIENT_MAGIC_LEN); + CU_ASSERT(NGHTTP2_IB_READ_FIRST_SETTINGS == session->iframe.state); + + /* Receiving PING is error because we want SETTINGS. */ + nghttp2_frame_ping_init(&ping_frame.ping, NGHTTP2_FLAG_NONE, NULL); + + nghttp2_frame_pack_frame_hd(buf, &ping_frame.ping.hd); + + rv = nghttp2_session_mem_recv(session, buf, NGHTTP2_FRAME_HDLEN); + CU_ASSERT(NGHTTP2_FRAME_HDLEN == rv); + CU_ASSERT(NGHTTP2_IB_IGN_ALL == session->iframe.state); + CU_ASSERT(0 == session->iframe.payloadleft); + + nghttp2_frame_ping_free(&ping_frame.ping); + + nghttp2_session_del(session); + + /* Check bad case */ + nghttp2_session_server_new(&session, &callbacks, NULL); + + /* Feed magic with one byte less */ + rv = nghttp2_session_mem_recv(session, (const uint8_t *)NGHTTP2_CLIENT_MAGIC, + NGHTTP2_CLIENT_MAGIC_LEN - 1); + + CU_ASSERT(rv == NGHTTP2_CLIENT_MAGIC_LEN - 1); + CU_ASSERT(NGHTTP2_IB_READ_CLIENT_MAGIC == session->iframe.state); + CU_ASSERT(1 == session->iframe.payloadleft); + + rv = nghttp2_session_mem_recv(session, (const uint8_t *)"\0", 1); + + CU_ASSERT(NGHTTP2_ERR_BAD_CLIENT_MAGIC == rv); + + nghttp2_session_del(session); + + /* disable global nghttp2_enable_strict_preface here */ + nghttp2_enable_strict_preface = 0; +} + +void test_nghttp2_session_delete_data_item(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + nghttp2_stream *a; + nghttp2_data_provider prd; + + memset(&callbacks, 0, sizeof(callbacks)); + + nghttp2_session_server_new(&session, &callbacks, NULL); + + a = open_stream(session, 1); + open_stream_with_dep(session, 3, a); + + /* We don't care about these members, since we won't send data */ + prd.source.ptr = NULL; + prd.read_callback = fail_data_source_read_callback; + + /* This data item will be marked as TOP */ + CU_ASSERT(0 == nghttp2_submit_data(session, NGHTTP2_FLAG_NONE, 1, &prd)); + /* This data item will be marked as REST */ + CU_ASSERT(0 == nghttp2_submit_data(session, NGHTTP2_FLAG_NONE, 3, &prd)); + + nghttp2_session_del(session); +} + +void test_nghttp2_session_open_idle_stream(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + nghttp2_stream *stream; + nghttp2_stream *opened_stream; + nghttp2_priority_spec pri_spec; + nghttp2_frame frame; + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + + nghttp2_session_server_new(&session, &callbacks, NULL); + + nghttp2_priority_spec_init(&pri_spec, 0, 3, 0); + + nghttp2_frame_priority_init(&frame.priority, 1, &pri_spec); + + CU_ASSERT(0 == nghttp2_session_on_priority_received(session, &frame)); + + stream = nghttp2_session_get_stream_raw(session, 1); + + CU_ASSERT(NGHTTP2_STREAM_IDLE == stream->state); + CU_ASSERT(NULL == stream->closed_prev); + CU_ASSERT(NULL == stream->closed_next); + CU_ASSERT(1 == session->num_idle_streams); + CU_ASSERT(session->idle_stream_head == stream); + CU_ASSERT(session->idle_stream_tail == stream); + + opened_stream = nghttp2_session_open_stream( + session, 1, NGHTTP2_STREAM_FLAG_NONE, &pri_spec_default, + NGHTTP2_STREAM_OPENING, NULL); + + CU_ASSERT(stream == opened_stream); + CU_ASSERT(NGHTTP2_STREAM_OPENING == stream->state); + CU_ASSERT(0 == session->num_idle_streams); + CU_ASSERT(NULL == session->idle_stream_head); + CU_ASSERT(NULL == session->idle_stream_tail); + + nghttp2_frame_priority_free(&frame.priority); + + nghttp2_session_del(session); +} + +void test_nghttp2_session_cancel_reserved_remote(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + nghttp2_stream *stream; + nghttp2_frame frame; + nghttp2_nv *nva; + ssize_t nvlen; + nghttp2_hd_deflater deflater; + nghttp2_mem *mem; + nghttp2_bufs bufs; + ssize_t rv; + + mem = nghttp2_mem_default(); + frame_pack_bufs_init(&bufs); + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.send_callback = null_send_callback; + + nghttp2_session_client_new(&session, &callbacks, NULL); + + nghttp2_hd_deflate_init(&deflater, mem); + + stream = nghttp2_session_open_stream(session, 2, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, + NGHTTP2_STREAM_RESERVED, NULL); + + session->last_recv_stream_id = 2; + + nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, 2, NGHTTP2_CANCEL); + + CU_ASSERT(NGHTTP2_STREAM_CLOSING == stream->state); + + CU_ASSERT(0 == nghttp2_session_send(session)); + + nvlen = ARRLEN(resnv); + nghttp2_nv_array_copy(&nva, resnv, nvlen, mem); + + nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_END_HEADERS, 2, + NGHTTP2_HCAT_PUSH_RESPONSE, NULL, nva, nvlen); + rv = nghttp2_frame_pack_headers(&bufs, &frame.headers, &deflater); + + CU_ASSERT(0 == rv); + + rv = nghttp2_session_mem_recv(session, bufs.head->buf.pos, + nghttp2_buf_len(&bufs.head->buf)); + + CU_ASSERT(nghttp2_buf_len(&bufs.head->buf) == rv); + + /* stream is not dangling, so assign NULL */ + stream = NULL; + + /* No RST_STREAM or GOAWAY is generated since stream should be in + NGHTTP2_STREAM_CLOSING and push response should be ignored. */ + CU_ASSERT(0 == nghttp2_outbound_queue_size(&session->ob_reg)); + + /* Check that we can receive push response HEADERS while RST_STREAM + is just queued. */ + nghttp2_session_open_stream(session, 4, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, NGHTTP2_STREAM_RESERVED, NULL); + + session->last_recv_stream_id = 4; + + nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, 2, NGHTTP2_CANCEL); + + nghttp2_bufs_reset(&bufs); + + frame.hd.stream_id = 4; + rv = nghttp2_frame_pack_headers(&bufs, &frame.headers, &deflater); + + CU_ASSERT(0 == rv); + + rv = nghttp2_session_mem_recv(session, bufs.head->buf.pos, + nghttp2_buf_len(&bufs.head->buf)); + + CU_ASSERT(nghttp2_buf_len(&bufs.head->buf) == rv); + + CU_ASSERT(1 == nghttp2_outbound_queue_size(&session->ob_reg)); + + nghttp2_frame_headers_free(&frame.headers, mem); + + nghttp2_hd_deflate_free(&deflater); + + nghttp2_session_del(session); + + nghttp2_bufs_free(&bufs); +} + +void test_nghttp2_session_reset_pending_headers(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + nghttp2_stream *stream; + int32_t stream_id; + my_user_data ud; + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.send_callback = null_send_callback; + callbacks.on_frame_send_callback = on_frame_send_callback; + callbacks.on_frame_not_send_callback = on_frame_not_send_callback; + callbacks.on_stream_close_callback = on_stream_close_callback; + + nghttp2_session_client_new(&session, &callbacks, &ud); + + stream_id = nghttp2_submit_request(session, NULL, NULL, 0, NULL, NULL); + CU_ASSERT(stream_id >= 1); + + nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, stream_id, + NGHTTP2_CANCEL); + + session->remote_settings.max_concurrent_streams = 0; + + /* RST_STREAM cancels pending HEADERS and is not actually sent. */ + ud.frame_send_cb_called = 0; + CU_ASSERT(0 == nghttp2_session_send(session)); + + CU_ASSERT(0 == ud.frame_send_cb_called); + + stream = nghttp2_session_get_stream(session, stream_id); + + CU_ASSERT(NULL == stream); + + /* See HEADERS is not sent. on_stream_close is called just like + transmission failure. */ + session->remote_settings.max_concurrent_streams = 1; + + ud.frame_not_send_cb_called = 0; + ud.stream_close_error_code = 0; + CU_ASSERT(0 == nghttp2_session_send(session)); + + CU_ASSERT(1 == ud.frame_not_send_cb_called); + CU_ASSERT(NGHTTP2_HEADERS == ud.not_sent_frame_type); + CU_ASSERT(NGHTTP2_CANCEL == ud.stream_close_error_code); + + stream = nghttp2_session_get_stream(session, stream_id); + + CU_ASSERT(NULL == stream); + + nghttp2_session_del(session); +} + +void test_nghttp2_session_send_data_callback(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + nghttp2_data_provider data_prd; + my_user_data ud; + accumulator acc; + nghttp2_frame_hd hd; + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.send_callback = accumulator_send_callback; + callbacks.send_data_callback = send_data_callback; + + data_prd.read_callback = no_copy_data_source_read_callback; + + acc.length = 0; + ud.acc = &acc; + + ud.data_source_length = NGHTTP2_DATA_PAYLOADLEN * 2; + + nghttp2_session_client_new(&session, &callbacks, &ud); + + nghttp2_session_open_stream(session, 1, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, NGHTTP2_STREAM_OPENING, NULL); + + nghttp2_submit_data(session, NGHTTP2_FLAG_END_STREAM, 1, &data_prd); + + CU_ASSERT(0 == nghttp2_session_send(session)); + + CU_ASSERT((NGHTTP2_FRAME_HDLEN + NGHTTP2_DATA_PAYLOADLEN) * 2 == acc.length); + + nghttp2_frame_unpack_frame_hd(&hd, acc.buf); + + CU_ASSERT(16384 == hd.length); + CU_ASSERT(NGHTTP2_DATA == hd.type); + CU_ASSERT(NGHTTP2_FLAG_NONE == hd.flags); + + nghttp2_frame_unpack_frame_hd(&hd, acc.buf + NGHTTP2_FRAME_HDLEN + hd.length); + + CU_ASSERT(16384 == hd.length); + CU_ASSERT(NGHTTP2_DATA == hd.type); + CU_ASSERT(NGHTTP2_FLAG_END_STREAM == hd.flags); + + nghttp2_session_del(session); +} + +void test_nghttp2_session_on_begin_headers_temporal_failure(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + my_user_data ud; + nghttp2_bufs bufs; + nghttp2_mem *mem; + ssize_t rv; + nghttp2_hd_deflater deflater; + nghttp2_outbound_item *item; + + mem = nghttp2_mem_default(); + frame_pack_bufs_init(&bufs); + nghttp2_hd_deflate_init(&deflater, mem); + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.on_begin_headers_callback = + temporal_failure_on_begin_headers_callback; + callbacks.on_header_callback = on_header_callback; + callbacks.on_frame_recv_callback = on_frame_recv_callback; + callbacks.send_callback = null_send_callback; + nghttp2_session_server_new(&session, &callbacks, &ud); + + rv = pack_headers(&bufs, &deflater, 1, NGHTTP2_FLAG_END_HEADERS, reqnv, + ARRLEN(reqnv), mem); + CU_ASSERT(0 == rv); + + ud.header_cb_called = 0; + ud.frame_recv_cb_called = 0; + rv = nghttp2_session_mem_recv(session, bufs.head->buf.pos, + nghttp2_bufs_len(&bufs)); + CU_ASSERT(nghttp2_bufs_len(&bufs) == rv); + CU_ASSERT(0 == ud.header_cb_called); + CU_ASSERT(0 == ud.frame_recv_cb_called); + + item = nghttp2_session_get_next_ob_item(session); + CU_ASSERT(NGHTTP2_RST_STREAM == item->frame.hd.type); + CU_ASSERT(1 == item->frame.hd.stream_id); + CU_ASSERT(NGHTTP2_INTERNAL_ERROR == item->frame.rst_stream.error_code); + + nghttp2_session_del(session); + nghttp2_hd_deflate_free(&deflater); + + nghttp2_bufs_reset(&bufs); + /* check for PUSH_PROMISE */ + nghttp2_hd_deflate_init(&deflater, mem); + nghttp2_session_client_new(&session, &callbacks, &ud); + + nghttp2_session_open_stream(session, 1, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, NGHTTP2_STREAM_OPENING, NULL); + + rv = pack_push_promise(&bufs, &deflater, 1, NGHTTP2_FLAG_END_HEADERS, 2, + reqnv, ARRLEN(reqnv), mem); + CU_ASSERT(0 == rv); + + ud.header_cb_called = 0; + ud.frame_recv_cb_called = 0; + rv = nghttp2_session_mem_recv(session, bufs.head->buf.pos, + nghttp2_bufs_len(&bufs)); + CU_ASSERT(nghttp2_bufs_len(&bufs) == rv); + CU_ASSERT(0 == ud.header_cb_called); + CU_ASSERT(0 == ud.frame_recv_cb_called); + + item = nghttp2_session_get_next_ob_item(session); + CU_ASSERT(NGHTTP2_RST_STREAM == item->frame.hd.type); + CU_ASSERT(2 == item->frame.hd.stream_id); + CU_ASSERT(NGHTTP2_INTERNAL_ERROR == item->frame.rst_stream.error_code); + + nghttp2_session_del(session); + nghttp2_hd_deflate_free(&deflater); + nghttp2_bufs_free(&bufs); +} + +static void check_nghttp2_http_recv_headers_fail( + nghttp2_session *session, nghttp2_hd_deflater *deflater, int32_t stream_id, + int stream_state, const nghttp2_nv *nva, size_t nvlen) { + nghttp2_mem *mem; + ssize_t rv; + nghttp2_outbound_item *item; + nghttp2_bufs bufs; + my_user_data *ud; + + mem = nghttp2_mem_default(); + frame_pack_bufs_init(&bufs); + + ud = session->user_data; + + if (stream_state != -1) { + nghttp2_session_open_stream(session, stream_id, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, stream_state, NULL); + } + + rv = pack_headers(&bufs, deflater, stream_id, NGHTTP2_FLAG_END_HEADERS, nva, + nvlen, mem); + CU_ASSERT(0 == rv); + + ud->invalid_frame_recv_cb_called = 0; + + rv = nghttp2_session_mem_recv(session, bufs.head->buf.pos, + nghttp2_buf_len(&bufs.head->buf)); + + CU_ASSERT(nghttp2_buf_len(&bufs.head->buf) == rv); + + item = nghttp2_session_get_next_ob_item(session); + + CU_ASSERT(NGHTTP2_RST_STREAM == item->frame.hd.type); + CU_ASSERT(1 == ud->invalid_frame_recv_cb_called); + + CU_ASSERT(0 == nghttp2_session_send(session)); + + nghttp2_bufs_free(&bufs); +} + +static void check_nghttp2_http_recv_headers_ok( + nghttp2_session *session, nghttp2_hd_deflater *deflater, int32_t stream_id, + int stream_state, const nghttp2_nv *nva, size_t nvlen) { + nghttp2_mem *mem; + ssize_t rv; + nghttp2_bufs bufs; + my_user_data *ud; + + mem = nghttp2_mem_default(); + frame_pack_bufs_init(&bufs); + + ud = session->user_data; + + if (stream_state != -1) { + nghttp2_session_open_stream(session, stream_id, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, stream_state, NULL); + } + + rv = pack_headers(&bufs, deflater, stream_id, NGHTTP2_FLAG_END_HEADERS, nva, + nvlen, mem); + CU_ASSERT(0 == rv); + + ud->frame_recv_cb_called = 0; + + rv = nghttp2_session_mem_recv(session, bufs.head->buf.pos, + nghttp2_buf_len(&bufs.head->buf)); + + CU_ASSERT(nghttp2_buf_len(&bufs.head->buf) == rv); + CU_ASSERT(NULL == nghttp2_session_get_next_ob_item(session)); + CU_ASSERT(1 == ud->frame_recv_cb_called); + + nghttp2_bufs_free(&bufs); +} + +void test_nghttp2_http_mandatory_headers(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + nghttp2_hd_deflater deflater; + nghttp2_mem *mem; + my_user_data ud; + /* test case for response */ + const nghttp2_nv nostatus_resnv[] = {MAKE_NV("server", "foo")}; + const nghttp2_nv dupstatus_resnv[] = {MAKE_NV(":status", "200"), + MAKE_NV(":status", "200")}; + const nghttp2_nv badpseudo_resnv[] = {MAKE_NV(":status", "200"), + MAKE_NV(":scheme", "https")}; + const nghttp2_nv latepseudo_resnv[] = {MAKE_NV("server", "foo"), + MAKE_NV(":status", "200")}; + const nghttp2_nv badstatus_resnv[] = {MAKE_NV(":status", "2000")}; + const nghttp2_nv badcl_resnv[] = {MAKE_NV(":status", "200"), + MAKE_NV("content-length", "-1")}; + const nghttp2_nv dupcl_resnv[] = {MAKE_NV(":status", "200"), + MAKE_NV("content-length", "0"), + MAKE_NV("content-length", "0")}; + const nghttp2_nv badhd_resnv[] = {MAKE_NV(":status", "200"), + MAKE_NV("connection", "close")}; + + /* test case for request */ + const nghttp2_nv nopath_reqnv[] = {MAKE_NV(":scheme", "https"), + MAKE_NV(":method", "GET"), + MAKE_NV(":authority", "localhost")}; + const nghttp2_nv earlyconnect_reqnv[] = { + MAKE_NV(":method", "CONNECT"), MAKE_NV(":scheme", "https"), + MAKE_NV(":path", "/"), MAKE_NV(":authority", "localhost")}; + const nghttp2_nv lateconnect_reqnv[] = { + MAKE_NV(":scheme", "https"), MAKE_NV(":path", "/"), + MAKE_NV(":method", "CONNECT"), MAKE_NV(":authority", "localhost")}; + const nghttp2_nv duppath_reqnv[] = { + MAKE_NV(":scheme", "https"), MAKE_NV(":method", "GET"), + MAKE_NV(":authority", "localhost"), MAKE_NV(":path", "/"), + MAKE_NV(":path", "/")}; + const nghttp2_nv badcl_reqnv[] = { + MAKE_NV(":scheme", "https"), MAKE_NV(":method", "POST"), + MAKE_NV(":authority", "localhost"), MAKE_NV(":path", "/"), + MAKE_NV("content-length", "-1")}; + const nghttp2_nv dupcl_reqnv[] = { + MAKE_NV(":scheme", "https"), MAKE_NV(":method", "POST"), + MAKE_NV(":authority", "localhost"), MAKE_NV(":path", "/"), + MAKE_NV("content-length", "0"), MAKE_NV("content-length", "0")}; + const nghttp2_nv badhd_reqnv[] = { + MAKE_NV(":scheme", "https"), MAKE_NV(":method", "GET"), + MAKE_NV(":authority", "localhost"), MAKE_NV(":path", "/"), + MAKE_NV("connection", "close")}; + const nghttp2_nv badauthority_reqnv[] = { + MAKE_NV(":scheme", "https"), MAKE_NV(":method", "GET"), + MAKE_NV(":authority", "\x0d\x0alocalhost"), MAKE_NV(":path", "/")}; + const nghttp2_nv badhdbtw_reqnv[] = { + MAKE_NV(":scheme", "https"), MAKE_NV(":method", "GET"), + MAKE_NV("foo", "\x0d\x0a"), MAKE_NV(":authority", "localhost"), + MAKE_NV(":path", "/")}; + const nghttp2_nv asteriskget1_reqnv[] = { + MAKE_NV(":path", "*"), MAKE_NV(":scheme", "https"), + MAKE_NV(":authority", "localhost"), MAKE_NV(":method", "GET")}; + const nghttp2_nv asteriskget2_reqnv[] = { + MAKE_NV(":scheme", "https"), MAKE_NV(":authority", "localhost"), + MAKE_NV(":method", "GET"), MAKE_NV(":path", "*")}; + const nghttp2_nv asteriskoptions1_reqnv[] = { + MAKE_NV(":path", "*"), MAKE_NV(":scheme", "https"), + MAKE_NV(":authority", "localhost"), MAKE_NV(":method", "OPTIONS")}; + const nghttp2_nv asteriskoptions2_reqnv[] = { + MAKE_NV(":scheme", "https"), MAKE_NV(":authority", "localhost"), + MAKE_NV(":method", "OPTIONS"), MAKE_NV(":path", "*")}; + + mem = nghttp2_mem_default(); + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.send_callback = null_send_callback; + callbacks.on_frame_recv_callback = on_frame_recv_callback; + callbacks.on_invalid_frame_recv_callback = on_invalid_frame_recv_callback; + + nghttp2_session_client_new(&session, &callbacks, &ud); + + nghttp2_hd_deflate_init(&deflater, mem); + + /* response header lacks :status */ + check_nghttp2_http_recv_headers_fail(session, &deflater, 1, + NGHTTP2_STREAM_OPENING, nostatus_resnv, + ARRLEN(nostatus_resnv)); + + /* response header has 2 :status */ + check_nghttp2_http_recv_headers_fail(session, &deflater, 3, + NGHTTP2_STREAM_OPENING, dupstatus_resnv, + ARRLEN(dupstatus_resnv)); + + /* response header has bad pseudo header :scheme */ + check_nghttp2_http_recv_headers_fail(session, &deflater, 5, + NGHTTP2_STREAM_OPENING, badpseudo_resnv, + ARRLEN(badpseudo_resnv)); + + /* response header has :status after regular header field */ + check_nghttp2_http_recv_headers_fail(session, &deflater, 7, + NGHTTP2_STREAM_OPENING, latepseudo_resnv, + ARRLEN(latepseudo_resnv)); + + /* response header has bad status code */ + check_nghttp2_http_recv_headers_fail(session, &deflater, 9, + NGHTTP2_STREAM_OPENING, badstatus_resnv, + ARRLEN(badstatus_resnv)); + + /* response header has bad content-length */ + check_nghttp2_http_recv_headers_fail(session, &deflater, 11, + NGHTTP2_STREAM_OPENING, badcl_resnv, + ARRLEN(badcl_resnv)); + + /* response header has multiple content-length */ + check_nghttp2_http_recv_headers_fail(session, &deflater, 13, + NGHTTP2_STREAM_OPENING, dupcl_resnv, + ARRLEN(dupcl_resnv)); + + /* response header has disallowed header field */ + check_nghttp2_http_recv_headers_fail(session, &deflater, 15, + NGHTTP2_STREAM_OPENING, badhd_resnv, + ARRLEN(badhd_resnv)); + + nghttp2_hd_deflate_free(&deflater); + + nghttp2_session_del(session); + + /* check server side */ + nghttp2_session_server_new(&session, &callbacks, &ud); + + nghttp2_hd_deflate_init(&deflater, mem); + + /* request header has no :path */ + check_nghttp2_http_recv_headers_fail(session, &deflater, 1, -1, nopath_reqnv, + ARRLEN(nopath_reqnv)); + + /* request header has CONNECT method, but followed by :path */ + check_nghttp2_http_recv_headers_fail(session, &deflater, 3, -1, + earlyconnect_reqnv, + ARRLEN(earlyconnect_reqnv)); + + /* request header has CONNECT method following :path */ + check_nghttp2_http_recv_headers_fail( + session, &deflater, 5, -1, lateconnect_reqnv, ARRLEN(lateconnect_reqnv)); + + /* request header has multiple :path */ + check_nghttp2_http_recv_headers_fail(session, &deflater, 7, -1, duppath_reqnv, + ARRLEN(duppath_reqnv)); + + /* request header has bad content-length */ + check_nghttp2_http_recv_headers_fail(session, &deflater, 9, -1, badcl_reqnv, + ARRLEN(badcl_reqnv)); + + /* request header has multiple content-length */ + check_nghttp2_http_recv_headers_fail(session, &deflater, 11, -1, dupcl_reqnv, + ARRLEN(dupcl_reqnv)); + + /* request header has disallowed header field */ + check_nghttp2_http_recv_headers_fail(session, &deflater, 13, -1, badhd_reqnv, + ARRLEN(badhd_reqnv)); + + /* request header has :authority header field containing illegal + characters */ + check_nghttp2_http_recv_headers_fail(session, &deflater, 15, -1, + badauthority_reqnv, + ARRLEN(badauthority_reqnv)); + + /* request header has regular header field containing illegal + character before all mandatory header fields are seen. */ + check_nghttp2_http_recv_headers_fail(session, &deflater, 17, -1, + badhdbtw_reqnv, ARRLEN(badhdbtw_reqnv)); + + /* request header has "*" in :path header field while method is GET. + :path is received before :method */ + check_nghttp2_http_recv_headers_fail(session, &deflater, 19, -1, + asteriskget1_reqnv, + ARRLEN(asteriskget1_reqnv)); + + /* request header has "*" in :path header field while method is GET. + :method is received before :path */ + check_nghttp2_http_recv_headers_fail(session, &deflater, 21, -1, + asteriskget2_reqnv, + ARRLEN(asteriskget2_reqnv)); + + /* OPTIONS method can include "*" in :path header field. :path is + received before :method. */ + check_nghttp2_http_recv_headers_ok(session, &deflater, 23, -1, + asteriskoptions1_reqnv, + ARRLEN(asteriskoptions1_reqnv)); + + /* OPTIONS method can include "*" in :path header field. :method is + received before :path. */ + check_nghttp2_http_recv_headers_ok(session, &deflater, 25, -1, + asteriskoptions2_reqnv, + ARRLEN(asteriskoptions2_reqnv)); + + nghttp2_hd_deflate_free(&deflater); + + nghttp2_session_del(session); +} + +void test_nghttp2_http_content_length(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + nghttp2_hd_deflater deflater; + nghttp2_mem *mem; + nghttp2_bufs bufs; + ssize_t rv; + nghttp2_stream *stream; + const nghttp2_nv cl_resnv[] = {MAKE_NV(":status", "200"), + MAKE_NV("te", "trailers"), + MAKE_NV("content-length", "9000000000")}; + const nghttp2_nv cl_reqnv[] = { + MAKE_NV(":path", "/"), MAKE_NV(":method", "PUT"), + MAKE_NV(":scheme", "https"), MAKE_NV("te", "trailers"), + MAKE_NV("host", "localhost"), MAKE_NV("content-length", "9000000000")}; + + mem = nghttp2_mem_default(); + frame_pack_bufs_init(&bufs); + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.send_callback = null_send_callback; + + nghttp2_session_client_new(&session, &callbacks, NULL); + + nghttp2_hd_deflate_init(&deflater, mem); + + stream = nghttp2_session_open_stream(session, 1, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, + NGHTTP2_STREAM_OPENING, NULL); + + rv = pack_headers(&bufs, &deflater, 1, NGHTTP2_FLAG_END_HEADERS, cl_resnv, + ARRLEN(cl_resnv), mem); + CU_ASSERT(0 == rv); + + rv = nghttp2_session_mem_recv(session, bufs.head->buf.pos, + nghttp2_buf_len(&bufs.head->buf)); + + CU_ASSERT(nghttp2_buf_len(&bufs.head->buf) == rv); + CU_ASSERT(NULL == nghttp2_session_get_next_ob_item(session)); + CU_ASSERT(9000000000LL == stream->content_length); + CU_ASSERT(200 == stream->status_code); + + nghttp2_hd_deflate_free(&deflater); + + nghttp2_session_del(session); + + nghttp2_bufs_reset(&bufs); + + /* check server side */ + nghttp2_session_server_new(&session, &callbacks, NULL); + + nghttp2_hd_deflate_init(&deflater, mem); + + rv = pack_headers(&bufs, &deflater, 1, NGHTTP2_FLAG_END_HEADERS, cl_reqnv, + ARRLEN(cl_reqnv), mem); + CU_ASSERT(0 == rv); + + rv = nghttp2_session_mem_recv(session, bufs.head->buf.pos, + nghttp2_buf_len(&bufs.head->buf)); + + CU_ASSERT(nghttp2_buf_len(&bufs.head->buf) == rv); + + stream = nghttp2_session_get_stream(session, 1); + + CU_ASSERT(NULL == nghttp2_session_get_next_ob_item(session)); + CU_ASSERT(9000000000LL == stream->content_length); + + nghttp2_hd_deflate_free(&deflater); + + nghttp2_session_del(session); + + nghttp2_bufs_free(&bufs); +} + +void test_nghttp2_http_content_length_mismatch(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + nghttp2_hd_deflater deflater; + nghttp2_mem *mem; + nghttp2_bufs bufs; + ssize_t rv; + const nghttp2_nv cl_reqnv[] = { + MAKE_NV(":path", "/"), MAKE_NV(":method", "PUT"), + MAKE_NV(":authority", "localhost"), MAKE_NV(":scheme", "https"), + MAKE_NV("content-length", "20")}; + nghttp2_outbound_item *item; + nghttp2_frame_hd hd; + + mem = nghttp2_mem_default(); + frame_pack_bufs_init(&bufs); + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.send_callback = null_send_callback; + + nghttp2_session_server_new(&session, &callbacks, NULL); + + nghttp2_hd_deflate_init(&deflater, mem); + + /* header says content-length: 20, but HEADERS has END_STREAM flag set */ + rv = pack_headers(&bufs, &deflater, 1, + NGHTTP2_FLAG_END_HEADERS | NGHTTP2_FLAG_END_STREAM, + cl_reqnv, ARRLEN(cl_reqnv), mem); + CU_ASSERT(0 == rv); + + rv = nghttp2_session_mem_recv(session, bufs.head->buf.pos, + nghttp2_buf_len(&bufs.head->buf)); + + CU_ASSERT(nghttp2_buf_len(&bufs.head->buf) == rv); + + item = nghttp2_session_get_next_ob_item(session); + CU_ASSERT(NGHTTP2_RST_STREAM == item->frame.hd.type); + + CU_ASSERT(0 == nghttp2_session_send(session)); + + nghttp2_bufs_reset(&bufs); + + /* header says content-length: 20, but DATA has 0 byte */ + rv = pack_headers(&bufs, &deflater, 3, NGHTTP2_FLAG_END_HEADERS, cl_reqnv, + ARRLEN(cl_reqnv), mem); + CU_ASSERT(0 == rv); + + nghttp2_frame_hd_init(&hd, 0, NGHTTP2_DATA, NGHTTP2_FLAG_END_STREAM, 3); + nghttp2_frame_pack_frame_hd(bufs.head->buf.last, &hd); + bufs.head->buf.last += NGHTTP2_FRAME_HDLEN; + + rv = nghttp2_session_mem_recv(session, bufs.head->buf.pos, + nghttp2_buf_len(&bufs.head->buf)); + + CU_ASSERT(nghttp2_buf_len(&bufs.head->buf) == rv); + + item = nghttp2_session_get_next_ob_item(session); + CU_ASSERT(NGHTTP2_RST_STREAM == item->frame.hd.type); + + CU_ASSERT(0 == nghttp2_session_send(session)); + + nghttp2_bufs_reset(&bufs); + + /* header says content-length: 20, but DATA has 21 bytes */ + rv = pack_headers(&bufs, &deflater, 5, NGHTTP2_FLAG_END_HEADERS, cl_reqnv, + ARRLEN(cl_reqnv), mem); + CU_ASSERT(0 == rv); + + nghttp2_frame_hd_init(&hd, 21, NGHTTP2_DATA, NGHTTP2_FLAG_END_STREAM, 5); + nghttp2_frame_pack_frame_hd(bufs.head->buf.last, &hd); + bufs.head->buf.last += NGHTTP2_FRAME_HDLEN + 21; + + rv = nghttp2_session_mem_recv(session, bufs.head->buf.pos, + nghttp2_buf_len(&bufs.head->buf)); + + CU_ASSERT(nghttp2_buf_len(&bufs.head->buf) == rv); + + item = nghttp2_session_get_next_ob_item(session); + CU_ASSERT(NGHTTP2_RST_STREAM == item->frame.hd.type); + + CU_ASSERT(0 == nghttp2_session_send(session)); + + nghttp2_bufs_reset(&bufs); + + nghttp2_hd_deflate_free(&deflater); + + nghttp2_session_del(session); + + nghttp2_bufs_free(&bufs); +} + +void test_nghttp2_http_non_final_response(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + nghttp2_hd_deflater deflater; + nghttp2_mem *mem; + nghttp2_bufs bufs; + ssize_t rv; + const nghttp2_nv nonfinal_resnv[] = { + MAKE_NV(":status", "100"), + }; + nghttp2_outbound_item *item; + nghttp2_frame_hd hd; + + mem = nghttp2_mem_default(); + frame_pack_bufs_init(&bufs); + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.send_callback = null_send_callback; + + nghttp2_session_client_new(&session, &callbacks, NULL); + + nghttp2_hd_deflate_init(&deflater, mem); + + /* non-final HEADERS with END_STREAM is illegal */ + nghttp2_session_open_stream(session, 1, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, NGHTTP2_STREAM_OPENING, NULL); + + rv = pack_headers(&bufs, &deflater, 1, + NGHTTP2_FLAG_END_HEADERS | NGHTTP2_FLAG_END_STREAM, + nonfinal_resnv, ARRLEN(nonfinal_resnv), mem); + CU_ASSERT(0 == rv); + + rv = nghttp2_session_mem_recv(session, bufs.head->buf.pos, + nghttp2_buf_len(&bufs.head->buf)); + + CU_ASSERT(nghttp2_buf_len(&bufs.head->buf) == rv); + + item = nghttp2_session_get_next_ob_item(session); + CU_ASSERT(NGHTTP2_RST_STREAM == item->frame.hd.type); + + CU_ASSERT(0 == nghttp2_session_send(session)); + + nghttp2_bufs_reset(&bufs); + + /* non-final HEADERS followed by non-empty DATA is illegal */ + nghttp2_session_open_stream(session, 3, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, NGHTTP2_STREAM_OPENING, NULL); + + rv = pack_headers(&bufs, &deflater, 3, NGHTTP2_FLAG_END_HEADERS, + nonfinal_resnv, ARRLEN(nonfinal_resnv), mem); + CU_ASSERT(0 == rv); + + nghttp2_frame_hd_init(&hd, 10, NGHTTP2_DATA, NGHTTP2_FLAG_END_STREAM, 3); + nghttp2_frame_pack_frame_hd(bufs.head->buf.last, &hd); + bufs.head->buf.last += NGHTTP2_FRAME_HDLEN + 10; + + rv = nghttp2_session_mem_recv(session, bufs.head->buf.pos, + nghttp2_buf_len(&bufs.head->buf)); + + CU_ASSERT(nghttp2_buf_len(&bufs.head->buf) == rv); + + item = nghttp2_session_get_next_ob_item(session); + CU_ASSERT(NGHTTP2_RST_STREAM == item->frame.hd.type); + + CU_ASSERT(0 == nghttp2_session_send(session)); + + nghttp2_bufs_reset(&bufs); + + /* non-final HEADERS followed by empty DATA (without END_STREAM) is + ok */ + nghttp2_session_open_stream(session, 5, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, NGHTTP2_STREAM_OPENING, NULL); + + rv = pack_headers(&bufs, &deflater, 5, NGHTTP2_FLAG_END_HEADERS, + nonfinal_resnv, ARRLEN(nonfinal_resnv), mem); + CU_ASSERT(0 == rv); + + nghttp2_frame_hd_init(&hd, 0, NGHTTP2_DATA, NGHTTP2_FLAG_NONE, 5); + nghttp2_frame_pack_frame_hd(bufs.head->buf.last, &hd); + bufs.head->buf.last += NGHTTP2_FRAME_HDLEN; + + rv = nghttp2_session_mem_recv(session, bufs.head->buf.pos, + nghttp2_buf_len(&bufs.head->buf)); + + CU_ASSERT(nghttp2_buf_len(&bufs.head->buf) == rv); + + CU_ASSERT(NULL == nghttp2_session_get_next_ob_item(session)); + + nghttp2_bufs_reset(&bufs); + + /* non-final HEADERS followed by empty DATA (with END_STREAM) is + illegal */ + nghttp2_session_open_stream(session, 7, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, NGHTTP2_STREAM_OPENING, NULL); + + rv = pack_headers(&bufs, &deflater, 7, NGHTTP2_FLAG_END_HEADERS, + nonfinal_resnv, ARRLEN(nonfinal_resnv), mem); + CU_ASSERT(0 == rv); + + nghttp2_frame_hd_init(&hd, 0, NGHTTP2_DATA, NGHTTP2_FLAG_END_STREAM, 7); + nghttp2_frame_pack_frame_hd(bufs.head->buf.last, &hd); + bufs.head->buf.last += NGHTTP2_FRAME_HDLEN; + + rv = nghttp2_session_mem_recv(session, bufs.head->buf.pos, + nghttp2_buf_len(&bufs.head->buf)); + + CU_ASSERT(nghttp2_buf_len(&bufs.head->buf) == rv); + + item = nghttp2_session_get_next_ob_item(session); + + CU_ASSERT(NGHTTP2_RST_STREAM == item->frame.hd.type); + + CU_ASSERT(0 == nghttp2_session_send(session)); + + nghttp2_bufs_reset(&bufs); + + /* non-final HEADERS followed by final HEADERS is OK */ + nghttp2_session_open_stream(session, 9, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, NGHTTP2_STREAM_OPENING, NULL); + + rv = pack_headers(&bufs, &deflater, 9, NGHTTP2_FLAG_END_HEADERS, + nonfinal_resnv, ARRLEN(nonfinal_resnv), mem); + CU_ASSERT(0 == rv); + + rv = nghttp2_session_mem_recv(session, bufs.head->buf.pos, + nghttp2_buf_len(&bufs.head->buf)); + + CU_ASSERT(nghttp2_buf_len(&bufs.head->buf) == rv); + + nghttp2_bufs_reset(&bufs); + + rv = pack_headers(&bufs, &deflater, 9, NGHTTP2_FLAG_END_HEADERS, resnv, + ARRLEN(resnv), mem); + CU_ASSERT(0 == rv); + + rv = nghttp2_session_mem_recv(session, bufs.head->buf.pos, + nghttp2_buf_len(&bufs.head->buf)); + + CU_ASSERT(nghttp2_buf_len(&bufs.head->buf) == rv); + + CU_ASSERT(NULL == nghttp2_session_get_next_ob_item(session)); + + nghttp2_bufs_reset(&bufs); + + nghttp2_hd_deflate_free(&deflater); + + nghttp2_session_del(session); + + nghttp2_bufs_free(&bufs); +} + +void test_nghttp2_http_trailer_headers(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + nghttp2_hd_deflater deflater; + nghttp2_mem *mem; + nghttp2_bufs bufs; + ssize_t rv; + const nghttp2_nv trailer_reqnv[] = { + MAKE_NV("foo", "bar"), + }; + nghttp2_outbound_item *item; + + mem = nghttp2_mem_default(); + frame_pack_bufs_init(&bufs); + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.send_callback = null_send_callback; + + nghttp2_session_server_new(&session, &callbacks, NULL); + + nghttp2_hd_deflate_init(&deflater, mem); + + /* good trailer header */ + rv = pack_headers(&bufs, &deflater, 1, NGHTTP2_FLAG_END_HEADERS, reqnv, + ARRLEN(reqnv), mem); + CU_ASSERT(0 == rv); + + rv = nghttp2_session_mem_recv(session, bufs.head->buf.pos, + nghttp2_buf_len(&bufs.head->buf)); + + CU_ASSERT(nghttp2_buf_len(&bufs.head->buf) == rv); + + nghttp2_bufs_reset(&bufs); + + rv = pack_headers(&bufs, &deflater, 1, + NGHTTP2_FLAG_END_HEADERS | NGHTTP2_FLAG_END_STREAM, + trailer_reqnv, ARRLEN(trailer_reqnv), mem); + CU_ASSERT(0 == rv); + + rv = nghttp2_session_mem_recv(session, bufs.head->buf.pos, + nghttp2_buf_len(&bufs.head->buf)); + + CU_ASSERT(nghttp2_buf_len(&bufs.head->buf) == rv); + + CU_ASSERT(NULL == nghttp2_session_get_next_ob_item(session)); + + nghttp2_bufs_reset(&bufs); + + /* trailer header without END_STREAM is illegal */ + rv = pack_headers(&bufs, &deflater, 3, NGHTTP2_FLAG_END_HEADERS, reqnv, + ARRLEN(reqnv), mem); + CU_ASSERT(0 == rv); + + rv = nghttp2_session_mem_recv(session, bufs.head->buf.pos, + nghttp2_buf_len(&bufs.head->buf)); + + CU_ASSERT(nghttp2_buf_len(&bufs.head->buf) == rv); + + nghttp2_bufs_reset(&bufs); + + rv = pack_headers(&bufs, &deflater, 3, NGHTTP2_FLAG_END_HEADERS, + trailer_reqnv, ARRLEN(trailer_reqnv), mem); + CU_ASSERT(0 == rv); + + rv = nghttp2_session_mem_recv(session, bufs.head->buf.pos, + nghttp2_buf_len(&bufs.head->buf)); + + CU_ASSERT(nghttp2_buf_len(&bufs.head->buf) == rv); + + item = nghttp2_session_get_next_ob_item(session); + + CU_ASSERT(NGHTTP2_RST_STREAM == item->frame.hd.type); + + CU_ASSERT(0 == nghttp2_session_send(session)); + + nghttp2_bufs_reset(&bufs); + + /* trailer header including pseudo header field is illegal */ + rv = pack_headers(&bufs, &deflater, 5, NGHTTP2_FLAG_END_HEADERS, reqnv, + ARRLEN(reqnv), mem); + CU_ASSERT(0 == rv); + + rv = nghttp2_session_mem_recv(session, bufs.head->buf.pos, + nghttp2_buf_len(&bufs.head->buf)); + + CU_ASSERT(nghttp2_buf_len(&bufs.head->buf) == rv); + + nghttp2_bufs_reset(&bufs); + + rv = pack_headers(&bufs, &deflater, 5, NGHTTP2_FLAG_END_HEADERS, reqnv, + ARRLEN(reqnv), mem); + CU_ASSERT(0 == rv); + + rv = nghttp2_session_mem_recv(session, bufs.head->buf.pos, + nghttp2_buf_len(&bufs.head->buf)); + + CU_ASSERT(nghttp2_buf_len(&bufs.head->buf) == rv); + + item = nghttp2_session_get_next_ob_item(session); + + CU_ASSERT(NGHTTP2_RST_STREAM == item->frame.hd.type); + + CU_ASSERT(0 == nghttp2_session_send(session)); + + nghttp2_bufs_reset(&bufs); + + nghttp2_hd_deflate_free(&deflater); + + nghttp2_session_del(session); + + nghttp2_bufs_free(&bufs); +} + +void test_nghttp2_http_ignore_regular_header(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + nghttp2_hd_deflater deflater; + nghttp2_mem *mem; + nghttp2_bufs bufs; + ssize_t rv; + my_user_data ud; + const nghttp2_nv bad_reqnv[] = { + MAKE_NV(":authority", "localhost"), MAKE_NV(":scheme", "https"), + MAKE_NV(":path", "/"), MAKE_NV(":method", "GET"), + MAKE_NV("foo", "\x0zzz"), MAKE_NV("bar", "buzz"), + }; + const nghttp2_nv bad_ansnv[] = { + MAKE_NV(":authority", "localhost"), MAKE_NV(":scheme", "https"), + MAKE_NV(":path", "/"), MAKE_NV(":method", "GET"), MAKE_NV("bar", "buzz")}; + ssize_t proclen; + size_t i; + + mem = nghttp2_mem_default(); + frame_pack_bufs_init(&bufs); + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.send_callback = null_send_callback; + callbacks.on_header_callback = pause_on_header_callback; + + nghttp2_session_server_new(&session, &callbacks, &ud); + nghttp2_hd_deflate_init(&deflater, mem); + + rv = pack_headers(&bufs, &deflater, 1, + NGHTTP2_FLAG_END_HEADERS | NGHTTP2_FLAG_END_STREAM, + bad_reqnv, ARRLEN(bad_reqnv), mem); + + CU_ASSERT_FATAL(0 == rv); + + proclen = 0; + + for (i = 0; i < 4; ++i) { + rv = nghttp2_session_mem_recv(session, bufs.head->buf.pos + proclen, + nghttp2_buf_len(&bufs.head->buf) - proclen); + CU_ASSERT_FATAL(rv > 0); + proclen += rv; + CU_ASSERT(nghttp2_nv_equal(&bad_ansnv[i], &ud.nv)); + } + + rv = nghttp2_session_mem_recv(session, bufs.head->buf.pos + proclen, + nghttp2_buf_len(&bufs.head->buf) - proclen); + CU_ASSERT_FATAL(rv > 0); + /* header field "foo" must be ignored because it has illegal value. + So we have "bar" header field for 5th header. */ + CU_ASSERT(nghttp2_nv_equal(&bad_ansnv[4], &ud.nv)); + proclen += rv; + + CU_ASSERT(nghttp2_buf_len(&bufs.head->buf) == proclen); + + nghttp2_hd_deflate_free(&deflater); + nghttp2_session_del(session); + nghttp2_bufs_free(&bufs); +} + +void test_nghttp2_http_ignore_content_length(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + nghttp2_hd_deflater deflater; + nghttp2_mem *mem; + nghttp2_bufs bufs; + ssize_t rv; + const nghttp2_nv cl_resnv[] = {MAKE_NV(":status", "304"), + MAKE_NV("content-length", "20")}; + const nghttp2_nv conn_reqnv[] = {MAKE_NV(":authority", "localhost"), + MAKE_NV(":method", "CONNECT"), + MAKE_NV("content-length", "999999")}; + nghttp2_stream *stream; + + mem = nghttp2_mem_default(); + frame_pack_bufs_init(&bufs); + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.send_callback = null_send_callback; + + nghttp2_session_client_new(&session, &callbacks, NULL); + + nghttp2_hd_deflate_init(&deflater, mem); + + /* If status 304, content-length must be ignored */ + nghttp2_session_open_stream(session, 1, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, NGHTTP2_STREAM_OPENING, NULL); + + rv = pack_headers(&bufs, &deflater, 1, + NGHTTP2_FLAG_END_HEADERS | NGHTTP2_FLAG_END_STREAM, + cl_resnv, ARRLEN(cl_resnv), mem); + CU_ASSERT(0 == rv); + + rv = nghttp2_session_mem_recv(session, bufs.head->buf.pos, + nghttp2_buf_len(&bufs.head->buf)); + + CU_ASSERT(nghttp2_buf_len(&bufs.head->buf) == rv); + + CU_ASSERT(NULL == nghttp2_session_get_next_ob_item(session)); + + nghttp2_bufs_reset(&bufs); + + nghttp2_hd_deflate_free(&deflater); + nghttp2_session_del(session); + + /* If request method is CONNECT, content-length must be ignored */ + nghttp2_session_server_new(&session, &callbacks, NULL); + + nghttp2_hd_deflate_init(&deflater, mem); + + rv = pack_headers(&bufs, &deflater, 1, NGHTTP2_FLAG_END_HEADERS, conn_reqnv, + ARRLEN(conn_reqnv), mem); + + CU_ASSERT(0 == rv); + + rv = nghttp2_session_mem_recv(session, bufs.head->buf.pos, + nghttp2_buf_len(&bufs.head->buf)); + + CU_ASSERT(nghttp2_buf_len(&bufs.head->buf) == rv); + + CU_ASSERT(NULL == nghttp2_session_get_next_ob_item(session)); + + stream = nghttp2_session_get_stream(session, 1); + + CU_ASSERT(-1 == stream->content_length); + CU_ASSERT((stream->http_flags & NGHTTP2_HTTP_FLAG_METH_CONNECT) > 0); + + nghttp2_hd_deflate_free(&deflater); + nghttp2_session_del(session); + nghttp2_bufs_free(&bufs); +} + +void test_nghttp2_http_record_request_method(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + const nghttp2_nv conn_reqnv[] = {MAKE_NV(":method", "CONNECT"), + MAKE_NV(":authority", "localhost")}; + const nghttp2_nv conn_resnv[] = {MAKE_NV(":status", "200"), + MAKE_NV("content-length", "9999")}; + nghttp2_stream *stream; + ssize_t rv; + nghttp2_bufs bufs; + nghttp2_hd_deflater deflater; + nghttp2_mem *mem; + + mem = nghttp2_mem_default(); + frame_pack_bufs_init(&bufs); + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.send_callback = null_send_callback; + + nghttp2_session_client_new(&session, &callbacks, NULL); + + nghttp2_hd_deflate_init(&deflater, mem); + + CU_ASSERT(1 == nghttp2_submit_request(session, NULL, conn_reqnv, + ARRLEN(conn_reqnv), NULL, NULL)); + + CU_ASSERT(0 == nghttp2_session_send(session)); + + stream = nghttp2_session_get_stream(session, 1); + + CU_ASSERT(NGHTTP2_HTTP_FLAG_METH_CONNECT == stream->http_flags); + + rv = pack_headers(&bufs, &deflater, 1, NGHTTP2_FLAG_END_HEADERS, conn_resnv, + ARRLEN(conn_resnv), mem); + CU_ASSERT(0 == rv); + + rv = nghttp2_session_mem_recv(session, bufs.head->buf.pos, + nghttp2_buf_len(&bufs.head->buf)); + + CU_ASSERT(nghttp2_buf_len(&bufs.head->buf) == rv); + + CU_ASSERT((NGHTTP2_HTTP_FLAG_METH_CONNECT & stream->http_flags) > 0); + CU_ASSERT(-1 == stream->content_length); + + nghttp2_hd_deflate_free(&deflater); + nghttp2_session_del(session); + nghttp2_bufs_free(&bufs); +} + +void test_nghttp2_http_push_promise(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + nghttp2_hd_deflater deflater; + nghttp2_mem *mem; + nghttp2_bufs bufs; + ssize_t rv; + nghttp2_stream *stream; + const nghttp2_nv bad_reqnv[] = {MAKE_NV(":method", "GET")}; + nghttp2_outbound_item *item; + + mem = nghttp2_mem_default(); + frame_pack_bufs_init(&bufs); + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.send_callback = null_send_callback; + + /* good PUSH_PROMISE case */ + nghttp2_session_client_new(&session, &callbacks, NULL); + + nghttp2_hd_deflate_init(&deflater, mem); + + nghttp2_session_open_stream(session, 1, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, NGHTTP2_STREAM_OPENING, NULL); + + rv = pack_push_promise(&bufs, &deflater, 1, NGHTTP2_FLAG_END_HEADERS, 2, + reqnv, ARRLEN(reqnv), mem); + CU_ASSERT(0 == rv); + + rv = nghttp2_session_mem_recv(session, bufs.head->buf.pos, + nghttp2_buf_len(&bufs.head->buf)); + + CU_ASSERT(nghttp2_buf_len(&bufs.head->buf) == rv); + + CU_ASSERT(NULL == nghttp2_session_get_next_ob_item(session)); + + stream = nghttp2_session_get_stream(session, 2); + CU_ASSERT(NULL != stream); + + nghttp2_bufs_reset(&bufs); + + rv = pack_headers(&bufs, &deflater, 2, NGHTTP2_FLAG_END_HEADERS, resnv, + ARRLEN(resnv), mem); + + CU_ASSERT(0 == rv); + + rv = nghttp2_session_mem_recv(session, bufs.head->buf.pos, + nghttp2_buf_len(&bufs.head->buf)); + + CU_ASSERT(nghttp2_buf_len(&bufs.head->buf) == rv); + + CU_ASSERT(NULL == nghttp2_session_get_next_ob_item(session)); + + CU_ASSERT(200 == stream->status_code); + + nghttp2_bufs_reset(&bufs); + + /* PUSH_PROMISE lacks mandatory header */ + rv = pack_push_promise(&bufs, &deflater, 1, NGHTTP2_FLAG_END_HEADERS, 4, + bad_reqnv, ARRLEN(bad_reqnv), mem); + + CU_ASSERT(0 == rv); + + rv = nghttp2_session_mem_recv(session, bufs.head->buf.pos, + nghttp2_buf_len(&bufs.head->buf)); + + CU_ASSERT(nghttp2_buf_len(&bufs.head->buf) == rv); + + item = nghttp2_session_get_next_ob_item(session); + + CU_ASSERT(NGHTTP2_RST_STREAM == item->frame.hd.type); + CU_ASSERT(4 == item->frame.hd.stream_id); + + nghttp2_bufs_reset(&bufs); + + nghttp2_hd_deflate_free(&deflater); + nghttp2_session_del(session); + nghttp2_bufs_free(&bufs); +} diff --git a/tests/nghttp2_session_test.h b/tests/nghttp2_session_test.h new file mode 100644 index 0000000..e879c78 --- /dev/null +++ b/tests/nghttp2_session_test.h @@ -0,0 +1,138 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGHTTP2_SESSION_TEST_H +#define NGHTTP2_SESSION_TEST_H + +void test_nghttp2_session_recv(void); +void test_nghttp2_session_recv_invalid_stream_id(void); +void test_nghttp2_session_recv_invalid_frame(void); +void test_nghttp2_session_recv_eof(void); +void test_nghttp2_session_recv_data(void); +void test_nghttp2_session_recv_continuation(void); +void test_nghttp2_session_recv_headers_with_priority(void); +void test_nghttp2_session_recv_premature_headers(void); +void test_nghttp2_session_recv_unknown_frame(void); +void test_nghttp2_session_recv_unexpected_continuation(void); +void test_nghttp2_session_recv_settings_header_table_size(void); +void test_nghttp2_session_recv_too_large_frame_length(void); +void test_nghttp2_session_continue(void); +void test_nghttp2_session_add_frame(void); +void test_nghttp2_session_on_request_headers_received(void); +void test_nghttp2_session_on_response_headers_received(void); +void test_nghttp2_session_on_headers_received(void); +void test_nghttp2_session_on_push_response_headers_received(void); +void test_nghttp2_session_on_priority_received(void); +void test_nghttp2_session_on_rst_stream_received(void); +void test_nghttp2_session_on_settings_received(void); +void test_nghttp2_session_on_push_promise_received(void); +void test_nghttp2_session_on_ping_received(void); +void test_nghttp2_session_on_goaway_received(void); +void test_nghttp2_session_on_window_update_received(void); +void test_nghttp2_session_on_data_received(void); +void test_nghttp2_session_send_headers_start_stream(void); +void test_nghttp2_session_send_headers_reply(void); +void test_nghttp2_session_send_headers_frame_size_error(void); +void test_nghttp2_session_send_headers_push_reply(void); +void test_nghttp2_session_send_rst_stream(void); +void test_nghttp2_session_send_push_promise(void); +void test_nghttp2_session_is_my_stream_id(void); +void test_nghttp2_session_upgrade(void); +void test_nghttp2_session_reprioritize_stream(void); +void test_nghttp2_session_reprioritize_stream_with_idle_stream_dep(void); +void test_nghttp2_submit_data(void); +void test_nghttp2_submit_data_read_length_too_large(void); +void test_nghttp2_submit_data_read_length_smallest(void); +void test_nghttp2_submit_data_twice(void); +void test_nghttp2_submit_request_with_data(void); +void test_nghttp2_submit_request_without_data(void); +void test_nghttp2_submit_response_with_data(void); +void test_nghttp2_submit_response_without_data(void); +void test_nghttp2_submit_trailer(void); +void test_nghttp2_submit_headers_start_stream(void); +void test_nghttp2_submit_headers_reply(void); +void test_nghttp2_submit_headers_push_reply(void); +void test_nghttp2_submit_headers(void); +void test_nghttp2_submit_headers_continuation(void); +void test_nghttp2_submit_priority(void); +void test_nghttp2_submit_settings(void); +void test_nghttp2_submit_settings_update_local_window_size(void); +void test_nghttp2_submit_push_promise(void); +void test_nghttp2_submit_window_update(void); +void test_nghttp2_submit_window_update_local_window_size(void); +void test_nghttp2_submit_shutdown_notice(void); +void test_nghttp2_submit_invalid_nv(void); +void test_nghttp2_session_open_stream(void); +void test_nghttp2_session_open_stream_with_idle_stream_dep(void); +void test_nghttp2_session_get_next_ob_item(void); +void test_nghttp2_session_pop_next_ob_item(void); +void test_nghttp2_session_reply_fail(void); +void test_nghttp2_session_max_concurrent_streams(void); +void test_nghttp2_session_stream_close_on_headers_push(void); +void test_nghttp2_session_stop_data_with_rst_stream(void); +void test_nghttp2_session_defer_data(void); +void test_nghttp2_session_flow_control(void); +void test_nghttp2_session_flow_control_data_recv(void); +void test_nghttp2_session_flow_control_data_with_padding_recv(void); +void test_nghttp2_session_data_read_temporal_failure(void); +void test_nghttp2_session_on_stream_close(void); +void test_nghttp2_session_on_ctrl_not_send(void); +void test_nghttp2_session_get_outbound_queue_size(void); +void test_nghttp2_session_get_effective_local_window_size(void); +void test_nghttp2_session_set_option(void); +void test_nghttp2_session_data_backoff_by_high_pri_frame(void); +void test_nghttp2_session_pack_data_with_padding(void); +void test_nghttp2_session_pack_headers_with_padding(void); +void test_nghttp2_pack_settings_payload(void); +void test_nghttp2_session_stream_dep_add(void); +void test_nghttp2_session_stream_dep_remove(void); +void test_nghttp2_session_stream_dep_add_subtree(void); +void test_nghttp2_session_stream_dep_remove_subtree(void); +void test_nghttp2_session_stream_dep_all_your_stream_are_belong_to_us(void); +void test_nghttp2_session_stream_attach_item(void); +void test_nghttp2_session_stream_attach_item_subtree(void); +void test_nghttp2_session_keep_closed_stream(void); +void test_nghttp2_session_keep_idle_stream(void); +void test_nghttp2_session_detach_idle_stream(void); +void test_nghttp2_session_large_dep_tree(void); +void test_nghttp2_session_graceful_shutdown(void); +void test_nghttp2_session_on_header_temporal_failure(void); +void test_nghttp2_session_recv_client_magic(void); +void test_nghttp2_session_delete_data_item(void); +void test_nghttp2_session_open_idle_stream(void); +void test_nghttp2_session_cancel_reserved_remote(void); +void test_nghttp2_session_reset_pending_headers(void); +void test_nghttp2_session_send_data_callback(void); +void test_nghttp2_session_on_begin_headers_temporal_failure(void); +void test_nghttp2_http_mandatory_headers(void); +void test_nghttp2_http_content_length(void); +void test_nghttp2_http_content_length_mismatch(void); +void test_nghttp2_http_non_final_response(void); +void test_nghttp2_http_trailer_headers(void); +void test_nghttp2_http_ignore_regular_header(void); +void test_nghttp2_http_ignore_content_length(void); +void test_nghttp2_http_record_request_method(void); +void test_nghttp2_http_push_promise(void); + +#endif /* NGHTTP2_SESSION_TEST_H */ diff --git a/tests/nghttp2_stream_test.c b/tests/nghttp2_stream_test.c new file mode 100644 index 0000000..9b572d0 --- /dev/null +++ b/tests/nghttp2_stream_test.c @@ -0,0 +1,29 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "nghttp2_stream_test.h" + +#include + +#include "nghttp2_stream.h" diff --git a/tests/nghttp2_stream_test.h b/tests/nghttp2_stream_test.h new file mode 100644 index 0000000..508a8e1 --- /dev/null +++ b/tests/nghttp2_stream_test.h @@ -0,0 +1,28 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGHTTP2_STREAM_TEST_H +#define NGHTTP2_STREAM_TEST_H + +#endif /* NGHTTP2_STREAM_TEST_H */ diff --git a/tests/nghttp2_test_helper.c b/tests/nghttp2_test_helper.c new file mode 100644 index 0000000..a9cb10c --- /dev/null +++ b/tests/nghttp2_test_helper.c @@ -0,0 +1,303 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "nghttp2_test_helper.h" + +#include + +#include + +#include "nghttp2_helper.h" +#include "nghttp2_priority_spec.h" + +int unpack_framebuf(nghttp2_frame *frame, nghttp2_bufs *bufs) { + nghttp2_buf *buf; + + /* Assuming we have required data in first buffer. We don't decode + header block so, we don't mind its space */ + buf = &bufs->head->buf; + return unpack_frame(frame, buf->pos, nghttp2_buf_len(buf)); +} + +int unpack_frame(nghttp2_frame *frame, const uint8_t *in, size_t len) { + int rv = 0; + const uint8_t *payload = in + NGHTTP2_FRAME_HDLEN; + size_t payloadlen = len - NGHTTP2_FRAME_HDLEN; + size_t payloadoff; + nghttp2_mem *mem; + + mem = nghttp2_mem_default(); + + nghttp2_frame_unpack_frame_hd(&frame->hd, in); + switch (frame->hd.type) { + case NGHTTP2_HEADERS: + payloadoff = ((frame->hd.flags & NGHTTP2_FLAG_PADDED) > 0); + rv = nghttp2_frame_unpack_headers_payload( + &frame->headers, payload + payloadoff, payloadlen - payloadoff); + break; + case NGHTTP2_PRIORITY: + nghttp2_frame_unpack_priority_payload(&frame->priority, payload, + payloadlen); + break; + case NGHTTP2_RST_STREAM: + nghttp2_frame_unpack_rst_stream_payload(&frame->rst_stream, payload, + payloadlen); + break; + case NGHTTP2_SETTINGS: + rv = nghttp2_frame_unpack_settings_payload2( + &frame->settings.iv, &frame->settings.niv, payload, payloadlen, mem); + break; + case NGHTTP2_PUSH_PROMISE: + rv = nghttp2_frame_unpack_push_promise_payload(&frame->push_promise, + payload, payloadlen); + break; + case NGHTTP2_PING: + nghttp2_frame_unpack_ping_payload(&frame->ping, payload, payloadlen); + break; + case NGHTTP2_GOAWAY: + nghttp2_frame_unpack_goaway_payload2(&frame->goaway, payload, payloadlen, + mem); + break; + case NGHTTP2_WINDOW_UPDATE: + nghttp2_frame_unpack_window_update_payload(&frame->window_update, payload, + payloadlen); + break; + default: + /* Must not be reachable */ + assert(0); + } + return rv; +} + +int strmemeq(const char *a, const uint8_t *b, size_t bn) { + const uint8_t *c; + if (!a || !b) { + return 0; + } + c = b + bn; + for (; *a && b != c && *a == *b; ++a, ++b) + ; + return !*a && b == c; +} + +int nvnameeq(const char *a, nghttp2_nv *nv) { + return strmemeq(a, nv->name, nv->namelen); +} + +int nvvalueeq(const char *a, nghttp2_nv *nv) { + return strmemeq(a, nv->value, nv->valuelen); +} + +void nva_out_init(nva_out *out) { + memset(out->nva, 0, sizeof(out->nva)); + out->nvlen = 0; +} + +void nva_out_reset(nva_out *out, nghttp2_mem *mem) { + size_t i; + for (i = 0; i < out->nvlen; ++i) { + mem->free(out->nva[i].name, NULL); + mem->free(out->nva[i].value, NULL); + } + memset(out->nva, 0, sizeof(out->nva)); + out->nvlen = 0; +} + +void add_out(nva_out *out, nghttp2_nv *nv, nghttp2_mem *mem) { + nghttp2_nv *onv = &out->nva[out->nvlen]; + if (nv->namelen) { + onv->name = mem->malloc(nv->namelen, NULL); + memcpy(onv->name, nv->name, nv->namelen); + } else { + onv->name = NULL; + } + if (nv->valuelen) { + onv->value = mem->malloc(nv->valuelen, NULL); + memcpy(onv->value, nv->value, nv->valuelen); + } else { + onv->value = NULL; + } + onv->namelen = nv->namelen; + onv->valuelen = nv->valuelen; + + onv->flags = nv->flags; + + ++out->nvlen; +} + +ssize_t inflate_hd(nghttp2_hd_inflater *inflater, nva_out *out, + nghttp2_bufs *bufs, size_t offset, nghttp2_mem *mem) { + ssize_t rv; + nghttp2_nv nv; + int inflate_flags; + nghttp2_buf_chain *ci; + nghttp2_buf *buf; + nghttp2_buf bp; + int final; + size_t processed; + + processed = 0; + + for (ci = bufs->head; ci; ci = ci->next) { + buf = &ci->buf; + final = nghttp2_buf_len(buf) == 0 || ci->next == NULL; + bp = *buf; + + if (offset) { + ssize_t n; + + n = nghttp2_min((ssize_t)offset, nghttp2_buf_len(&bp)); + bp.pos += n; + offset -= n; + } + + for (;;) { + inflate_flags = 0; + rv = nghttp2_hd_inflate_hd(inflater, &nv, &inflate_flags, bp.pos, + nghttp2_buf_len(&bp), final); + + if (rv < 0) { + return rv; + } + + bp.pos += rv; + processed += rv; + + if (inflate_flags & NGHTTP2_HD_INFLATE_EMIT) { + if (out) { + add_out(out, &nv, mem); + } + } + if (inflate_flags & NGHTTP2_HD_INFLATE_FINAL) { + break; + } + } + } + + nghttp2_hd_inflate_end_headers(inflater); + + return processed; +} + +int pack_headers(nghttp2_bufs *bufs, nghttp2_hd_deflater *deflater, + int32_t stream_id, int flags, const nghttp2_nv *nva, + size_t nvlen, nghttp2_mem *mem) { + nghttp2_nv *dnva; + nghttp2_frame frame; + int rv; + + nghttp2_nv_array_copy(&dnva, nva, nvlen, mem); + + nghttp2_frame_headers_init(&frame.headers, flags, stream_id, + NGHTTP2_HCAT_HEADERS, NULL, dnva, nvlen); + rv = nghttp2_frame_pack_headers(bufs, &frame.headers, deflater); + + nghttp2_frame_headers_free(&frame.headers, mem); + + return rv; +} + +int pack_push_promise(nghttp2_bufs *bufs, nghttp2_hd_deflater *deflater, + int32_t stream_id, int flags, int32_t promised_stream_id, + const nghttp2_nv *nva, size_t nvlen, nghttp2_mem *mem) { + nghttp2_nv *dnva; + nghttp2_frame frame; + int rv; + + nghttp2_nv_array_copy(&dnva, nva, nvlen, mem); + + nghttp2_frame_push_promise_init(&frame.push_promise, flags, stream_id, + promised_stream_id, dnva, nvlen); + rv = nghttp2_frame_pack_push_promise(bufs, &frame.push_promise, deflater); + + nghttp2_frame_push_promise_free(&frame.push_promise, mem); + + return rv; +} + +int frame_pack_bufs_init(nghttp2_bufs *bufs) { + /* 1 for Pad Length */ + return nghttp2_bufs_init2(bufs, 4096, 16, NGHTTP2_FRAME_HDLEN + 1, + nghttp2_mem_default()); +} + +void bufs_large_init(nghttp2_bufs *bufs, size_t chunk_size) { + /* 1 for Pad Length */ + nghttp2_bufs_init2(bufs, chunk_size, 16, NGHTTP2_FRAME_HDLEN + 1, + nghttp2_mem_default()); +} + +static nghttp2_stream *open_stream_with_all(nghttp2_session *session, + int32_t stream_id, int32_t weight, + uint8_t exclusive, + nghttp2_stream *dep_stream) { + nghttp2_priority_spec pri_spec; + int32_t dep_stream_id; + + if (dep_stream) { + dep_stream_id = dep_stream->stream_id; + } else { + dep_stream_id = 0; + } + + nghttp2_priority_spec_init(&pri_spec, dep_stream_id, weight, exclusive); + + return nghttp2_session_open_stream(session, stream_id, + NGHTTP2_STREAM_FLAG_NONE, &pri_spec, + NGHTTP2_STREAM_OPENED, NULL); +} + +nghttp2_stream *open_stream(nghttp2_session *session, int32_t stream_id) { + return open_stream_with_all(session, stream_id, NGHTTP2_DEFAULT_WEIGHT, 0, + NULL); +} + +nghttp2_stream *open_stream_with_dep(nghttp2_session *session, + int32_t stream_id, + nghttp2_stream *dep_stream) { + return open_stream_with_all(session, stream_id, NGHTTP2_DEFAULT_WEIGHT, 0, + dep_stream); +} + +nghttp2_stream *open_stream_with_dep_weight(nghttp2_session *session, + int32_t stream_id, int32_t weight, + nghttp2_stream *dep_stream) { + return open_stream_with_all(session, stream_id, weight, 0, dep_stream); +} + +nghttp2_stream *open_stream_with_dep_excl(nghttp2_session *session, + int32_t stream_id, + nghttp2_stream *dep_stream) { + return open_stream_with_all(session, stream_id, NGHTTP2_DEFAULT_WEIGHT, 1, + dep_stream); +} + +nghttp2_outbound_item *create_data_ob_item(nghttp2_mem *mem) { + nghttp2_outbound_item *item; + + item = mem->malloc(sizeof(nghttp2_outbound_item), NULL); + memset(item, 0, sizeof(nghttp2_outbound_item)); + + return item; +} diff --git a/tests/nghttp2_test_helper.h b/tests/nghttp2_test_helper.h new file mode 100644 index 0000000..295c102 --- /dev/null +++ b/tests/nghttp2_test_helper.h @@ -0,0 +1,112 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGHTTP2_TEST_HELPER_H +#define NGHTTP2_TEST_HELPER_H + +#ifdef HAVE_CONFIG_H +#include +#endif /* HAVE_CONFIG_H */ + +#include "nghttp2_frame.h" +#include "nghttp2_hd.h" +#include "nghttp2_session.h" + +#define MAKE_NV(NAME, VALUE) \ + { \ + (uint8_t *)(NAME), (uint8_t *)(VALUE), sizeof((NAME)) - 1, \ + sizeof((VALUE)) - 1, NGHTTP2_NV_FLAG_NONE \ + } +#define ARRLEN(ARR) (sizeof(ARR) / sizeof(ARR[0])) + +#define assert_nv_equal(A, B, len, mem) \ + do { \ + size_t alloclen = sizeof(nghttp2_nv) * len; \ + const nghttp2_nv *sa = A, *sb = B; \ + nghttp2_nv *a = mem->malloc(alloclen, NULL); \ + nghttp2_nv *b = mem->malloc(alloclen, NULL); \ + ssize_t i_; \ + memcpy(a, sa, alloclen); \ + memcpy(b, sb, alloclen); \ + nghttp2_nv_array_sort(a, len); \ + nghttp2_nv_array_sort(b, len); \ + for (i_ = 0; i_ < (ssize_t)len; ++i_) { \ + CU_ASSERT(nghttp2_nv_equal(&a[i_], &b[i_])); \ + } \ + mem->free(b, NULL); \ + mem->free(a, NULL); \ + } while (0); + +int unpack_framebuf(nghttp2_frame *frame, nghttp2_bufs *bufs); + +int unpack_frame(nghttp2_frame *frame, const uint8_t *in, size_t len); + +int strmemeq(const char *a, const uint8_t *b, size_t bn); + +int nvnameeq(const char *a, nghttp2_nv *nv); + +int nvvalueeq(const char *a, nghttp2_nv *nv); + +typedef struct { + nghttp2_nv nva[256]; + size_t nvlen; +} nva_out; + +void nva_out_init(nva_out *out); +void nva_out_reset(nva_out *out, nghttp2_mem *mem); + +void add_out(nva_out *out, nghttp2_nv *nv, nghttp2_mem *mem); + +ssize_t inflate_hd(nghttp2_hd_inflater *inflater, nva_out *out, + nghttp2_bufs *bufs, size_t offset, nghttp2_mem *mem); + +int pack_headers(nghttp2_bufs *bufs, nghttp2_hd_deflater *deflater, + int32_t stream_id, int flags, const nghttp2_nv *nva, + size_t nvlen, nghttp2_mem *mem); + +int pack_push_promise(nghttp2_bufs *bufs, nghttp2_hd_deflater *deflater, + int32_t stream_id, int flags, int32_t promised_stream_id, + const nghttp2_nv *nva, size_t nvlen, nghttp2_mem *mem); + +int frame_pack_bufs_init(nghttp2_bufs *bufs); + +void bufs_large_init(nghttp2_bufs *bufs, size_t chunk_size); + +nghttp2_stream *open_stream(nghttp2_session *session, int32_t stream_id); + +nghttp2_stream *open_stream_with_dep(nghttp2_session *session, + int32_t stream_id, + nghttp2_stream *dep_stream); + +nghttp2_stream *open_stream_with_dep_weight(nghttp2_session *session, + int32_t stream_id, int32_t weight, + nghttp2_stream *dep_stream); + +nghttp2_stream *open_stream_with_dep_excl(nghttp2_session *session, + int32_t stream_id, + nghttp2_stream *dep_stream); + +nghttp2_outbound_item *create_data_ob_item(nghttp2_mem *mem); + +#endif /* NGHTTP2_TEST_HELPER_H */ diff --git a/tests/testdata/Makefile.am b/tests/testdata/Makefile.am new file mode 100644 index 0000000..ee38113 --- /dev/null +++ b/tests/testdata/Makefile.am @@ -0,0 +1,23 @@ +# nghttp2 - HTTP/2 C Library + +# Copyright (c) 2012 Tatsuhiro Tsujikawa + +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: + +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. + +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +EXTRA_DIST = cacert.pem index.html privkey.pem diff --git a/tests/testdata/Makefile.in b/tests/testdata/Makefile.in new file mode 100644 index 0000000..a40481f --- /dev/null +++ b/tests/testdata/Makefile.in @@ -0,0 +1,512 @@ +# Makefile.in generated by automake 1.14.1 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2013 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +# nghttp2 - HTTP/2 C Library + +# Copyright (c) 2012 Tatsuhiro Tsujikawa + +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: + +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +VPATH = @srcdir@ +am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)' +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +target_triplet = @target@ +subdir = tests/testdata +DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/ax_boost_asio.m4 \ + $(top_srcdir)/m4/ax_boost_base.m4 \ + $(top_srcdir)/m4/ax_boost_system.m4 \ + $(top_srcdir)/m4/ax_boost_thread.m4 \ + $(top_srcdir)/m4/ax_check_compile_flag.m4 \ + $(top_srcdir)/m4/ax_cxx_compile_stdcxx_11.m4 \ + $(top_srcdir)/m4/ax_have_epoll.m4 \ + $(top_srcdir)/m4/ax_python_devel.m4 \ + $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/libxml2.m4 \ + $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ + $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +SOURCES = +DIST_SOURCES = +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +APPLDFLAGS = @APPLDFLAGS@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +BOOST_ASIO_LIB = @BOOST_ASIO_LIB@ +BOOST_CPPFLAGS = @BOOST_CPPFLAGS@ +BOOST_LDFLAGS = @BOOST_LDFLAGS@ +BOOST_SYSTEM_LIB = @BOOST_SYSTEM_LIB@ +BOOST_THREAD_LIB = @BOOST_THREAD_LIB@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CUNIT_CFLAGS = @CUNIT_CFLAGS@ +CUNIT_LIBS = @CUNIT_LIBS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +CYTHON = @CYTHON@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DLLTOOL = @DLLTOOL@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +GREP = @GREP@ +HAVE_CXX11 = @HAVE_CXX11@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +JANSSON_CFLAGS = @JANSSON_CFLAGS@ +JANSSON_LIBS = @JANSSON_LIBS@ +JEMALLOC_LIBS = @JEMALLOC_LIBS@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBEVENT_OPENSSL_CFLAGS = @LIBEVENT_OPENSSL_CFLAGS@ +LIBEVENT_OPENSSL_LIBS = @LIBEVENT_OPENSSL_LIBS@ +LIBEV_CFLAGS = @LIBEV_CFLAGS@ +LIBEV_LIBS = @LIBEV_LIBS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBSPDYLAY_CFLAGS = @LIBSPDYLAY_CFLAGS@ +LIBSPDYLAY_LIBS = @LIBSPDYLAY_LIBS@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +LT_AGE = @LT_AGE@ +LT_CURRENT = @LT_CURRENT@ +LT_REVISION = @LT_REVISION@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MKDIR_P = @MKDIR_P@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OPENSSL_CFLAGS = @OPENSSL_CFLAGS@ +OPENSSL_LIBS = @OPENSSL_LIBS@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PACKAGE_VERSION_NUM = @PACKAGE_VERSION_NUM@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PKG_CONFIG = @PKG_CONFIG@ +PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ +PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ +PYTHON = @PYTHON@ +PYTHON_CPPFLAGS = @PYTHON_CPPFLAGS@ +PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@ +PYTHON_EXTRA_LDFLAGS = @PYTHON_EXTRA_LDFLAGS@ +PYTHON_EXTRA_LIBS = @PYTHON_EXTRA_LIBS@ +PYTHON_LDFLAGS = @PYTHON_LDFLAGS@ +PYTHON_PLATFORM = @PYTHON_PLATFORM@ +PYTHON_PREFIX = @PYTHON_PREFIX@ +PYTHON_SITE_PKG = @PYTHON_SITE_PKG@ +PYTHON_VERSION = @PYTHON_VERSION@ +RANLIB = @RANLIB@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +TESTLDADD = @TESTLDADD@ +VERSION = @VERSION@ +WARNCFLAGS = @WARNCFLAGS@ +XML2_CONFIG = @XML2_CONFIG@ +XML_CPPFLAGS = @XML_CPPFLAGS@ +XML_LIBS = @XML_LIBS@ +ZLIB_CFLAGS = @ZLIB_CFLAGS@ +ZLIB_LIBS = @ZLIB_LIBS@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +pkgpyexecdir = @pkgpyexecdir@ +pkgpythondir = @pkgpythondir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +pyexecdir = @pyexecdir@ +pythondir = @pythondir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target = @target@ +target_alias = @target_alias@ +target_cpu = @target_cpu@ +target_os = @target_os@ +target_vendor = @target_vendor@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ + +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +EXTRA_DIST = cacert.pem index.html privkey.pem +all: all-am + +.SUFFIXES: +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu tests/testdata/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu tests/testdata/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs +tags TAGS: + +ctags CTAGS: + +cscope cscopelist: + + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile +installdirs: +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic clean-libtool mostlyclean-am + +distclean: distclean-am + -rm -f Makefile +distclean-am: clean-am distclean-generic + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-generic mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: + +.MAKE: install-am install-strip + +.PHONY: all all-am check check-am clean clean-generic clean-libtool \ + cscopelist-am ctags-am distclean distclean-generic \ + distclean-libtool distdir dvi dvi-am html html-am info info-am \ + install install-am install-data install-data-am install-dvi \ + install-dvi-am install-exec install-exec-am install-html \ + install-html-am install-info install-info-am install-man \ + install-pdf install-pdf-am install-ps install-ps-am \ + install-strip installcheck installcheck-am installdirs \ + maintainer-clean maintainer-clean-generic mostlyclean \ + mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ + tags-am uninstall uninstall-am + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/tests/testdata/cacert.pem b/tests/testdata/cacert.pem new file mode 100644 index 0000000..e462790 --- /dev/null +++ b/tests/testdata/cacert.pem @@ -0,0 +1,14 @@ +-----BEGIN CERTIFICATE----- +MIICKTCCAdOgAwIBAgIJAIsolheWrwMZMA0GCSqGSIb3DQEBBQUAMHAxCzAJBgNV +BAYTAlVTMQswCQYDVQQIDAJDQTENMAsGA1UEBwwEQ2l0eTESMBAGA1UECgwJU3Bk +eSBUZXN0MRIwEAYDVQQDDAlsb2NhbGhvc3QxHTAbBgkqhkiG9w0BCQEWDnNwZHlA +bG9jYWxob3N0MB4XDTEyMDMwMTE5MTI0NVoXDTIzMDUxOTE5MTI0NVowcDELMAkG +A1UEBhMCVVMxCzAJBgNVBAgMAkNBMQ0wCwYDVQQHDARDaXR5MRIwEAYDVQQKDAlT +cGR5IFRlc3QxEjAQBgNVBAMMCWxvY2FsaG9zdDEdMBsGCSqGSIb3DQEJARYOc3Bk +eUBsb2NhbGhvc3QwXDANBgkqhkiG9w0BAQEFAANLADBIAkEAw/2MgzAdlJDm29qH +ZlAibgs9mH+8keOtsRrb4B1PiCcZoHvN9eCVZ4WnzT+0zhHF+nO3YfwVFVC3w7TF +7fLB3QIDAQABo1AwTjAdBgNVHQ4EFgQUVP2Jw9RX6BB76aV5x2qk5qsrAIQwHwYD +VR0jBBgwFoAUVP2Jw9RX6BB76aV5x2qk5qsrAIQwDAYDVR0TBAUwAwEB/zANBgkq +hkiG9w0BAQUFAANBAKd9M5FzQLEZW1KPe9/XNZlgxZ2g3EC5Krxo5I4Ul3MnIYS9 +u4K8t/iprhgOzjFH6+8LVk9v0Za+gU+K43CpUo4= +-----END CERTIFICATE----- diff --git a/tests/testdata/index.html b/tests/testdata/index.html new file mode 100644 index 0000000..cdd75fc --- /dev/null +++ b/tests/testdata/index.html @@ -0,0 +1 @@ +small diff --git a/tests/testdata/privkey.pem b/tests/testdata/privkey.pem new file mode 100644 index 0000000..0ab825b --- /dev/null +++ b/tests/testdata/privkey.pem @@ -0,0 +1,9 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIBOwIBAAJBAMP9jIMwHZSQ5tvah2ZQIm4LPZh/vJHjrbEa2+AdT4gnGaB7zfXg +lWeFp80/tM4Rxfpzt2H8FRVQt8O0xe3ywd0CAwEAAQJBAIQ8PGP/QNYOdlT8OsLj +aneJCgQsm1Rro7ONBbFO1WxslvA6+uJsx4Rs8zLiS8cyqmJ/lmGa7zhwYSOvFQPa +XgECIQDgIcgM/2C67peTm1diKKIoGVVKFCfdRi+Dje6mTl2TQQIhAN/bcFWbG73j +cUVlIsr9Wk1dJzjPPWKeyirF1qd/WbOdAiEApTsCOeLCssxV3jF02B5QfPNAFx6I +zO2C9Z7awque/IECIGCHW3VOoTPMs7dc2Rf3D810cclJdArmtf6juOAZRjDxAiBS +AC+H685IBJ99N5nCbF9NWYIVSkuiKVQ8POYVZX+0Jg== +-----END RSA PRIVATE KEY----- diff --git a/third-party/Makefile.am b/third-party/Makefile.am new file mode 100644 index 0000000..aa8832d --- /dev/null +++ b/third-party/Makefile.am @@ -0,0 +1,32 @@ +# nghttp2 - HTTP/2 C Library + +# Copyright (c) 2014 Tatsuhiro Tsujikawa + +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: + +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. + +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +AM_CPPFLAGS = -Wall @DEFS@ + +noinst_LTLIBRARIES = libhttp-parser.la +libhttp_parser_la_SOURCES = \ + http-parser/http_parser.c \ + http-parser/http_parser.h + + +dist_pkgdata_SCRIPTS = h2o/fetch-ocsp-response diff --git a/third-party/Makefile.in b/third-party/Makefile.in new file mode 100644 index 0000000..352a959 --- /dev/null +++ b/third-party/Makefile.in @@ -0,0 +1,751 @@ +# Makefile.in generated by automake 1.14.1 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2013 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +# nghttp2 - HTTP/2 C Library + +# Copyright (c) 2014 Tatsuhiro Tsujikawa + +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: + +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. + +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +VPATH = @srcdir@ +am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)' +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +target_triplet = @target@ +subdir = third-party +DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \ + $(dist_pkgdata_SCRIPTS) $(top_srcdir)/depcomp +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/ax_boost_asio.m4 \ + $(top_srcdir)/m4/ax_boost_base.m4 \ + $(top_srcdir)/m4/ax_boost_system.m4 \ + $(top_srcdir)/m4/ax_boost_thread.m4 \ + $(top_srcdir)/m4/ax_check_compile_flag.m4 \ + $(top_srcdir)/m4/ax_cxx_compile_stdcxx_11.m4 \ + $(top_srcdir)/m4/ax_have_epoll.m4 \ + $(top_srcdir)/m4/ax_python_devel.m4 \ + $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/libxml2.m4 \ + $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ + $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +LTLIBRARIES = $(noinst_LTLIBRARIES) +libhttp_parser_la_LIBADD = +am__dirstamp = $(am__leading_dot)dirstamp +am_libhttp_parser_la_OBJECTS = http-parser/http_parser.lo +libhttp_parser_la_OBJECTS = $(am_libhttp_parser_la_OBJECTS) +AM_V_lt = $(am__v_lt_@AM_V@) +am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) +am__v_lt_0 = --silent +am__v_lt_1 = +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +am__uninstall_files_from_dir = { \ + test -z "$$files" \ + || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ + || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ + $(am__cd) "$$dir" && rm -f $$files; }; \ + } +am__installdirs = "$(DESTDIR)$(pkgdatadir)" +SCRIPTS = $(dist_pkgdata_SCRIPTS) +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__depfiles_maybe = depfiles +am__mv = mv -f +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CFLAGS) $(CFLAGS) +AM_V_CC = $(am__v_CC_@AM_V@) +am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) +am__v_CC_0 = @echo " CC " $@; +am__v_CC_1 = +CCLD = $(CC) +LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CCLD = $(am__v_CCLD_@AM_V@) +am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) +am__v_CCLD_0 = @echo " CCLD " $@; +am__v_CCLD_1 = +SOURCES = $(libhttp_parser_la_SOURCES) +DIST_SOURCES = $(libhttp_parser_la_SOURCES) +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +ETAGS = etags +CTAGS = ctags +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +APPLDFLAGS = @APPLDFLAGS@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +BOOST_ASIO_LIB = @BOOST_ASIO_LIB@ +BOOST_CPPFLAGS = @BOOST_CPPFLAGS@ +BOOST_LDFLAGS = @BOOST_LDFLAGS@ +BOOST_SYSTEM_LIB = @BOOST_SYSTEM_LIB@ +BOOST_THREAD_LIB = @BOOST_THREAD_LIB@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CUNIT_CFLAGS = @CUNIT_CFLAGS@ +CUNIT_LIBS = @CUNIT_LIBS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +CYTHON = @CYTHON@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DLLTOOL = @DLLTOOL@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +GREP = @GREP@ +HAVE_CXX11 = @HAVE_CXX11@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +JANSSON_CFLAGS = @JANSSON_CFLAGS@ +JANSSON_LIBS = @JANSSON_LIBS@ +JEMALLOC_LIBS = @JEMALLOC_LIBS@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBEVENT_OPENSSL_CFLAGS = @LIBEVENT_OPENSSL_CFLAGS@ +LIBEVENT_OPENSSL_LIBS = @LIBEVENT_OPENSSL_LIBS@ +LIBEV_CFLAGS = @LIBEV_CFLAGS@ +LIBEV_LIBS = @LIBEV_LIBS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBSPDYLAY_CFLAGS = @LIBSPDYLAY_CFLAGS@ +LIBSPDYLAY_LIBS = @LIBSPDYLAY_LIBS@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +LT_AGE = @LT_AGE@ +LT_CURRENT = @LT_CURRENT@ +LT_REVISION = @LT_REVISION@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MKDIR_P = @MKDIR_P@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OPENSSL_CFLAGS = @OPENSSL_CFLAGS@ +OPENSSL_LIBS = @OPENSSL_LIBS@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PACKAGE_VERSION_NUM = @PACKAGE_VERSION_NUM@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PKG_CONFIG = @PKG_CONFIG@ +PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ +PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ +PYTHON = @PYTHON@ +PYTHON_CPPFLAGS = @PYTHON_CPPFLAGS@ +PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@ +PYTHON_EXTRA_LDFLAGS = @PYTHON_EXTRA_LDFLAGS@ +PYTHON_EXTRA_LIBS = @PYTHON_EXTRA_LIBS@ +PYTHON_LDFLAGS = @PYTHON_LDFLAGS@ +PYTHON_PLATFORM = @PYTHON_PLATFORM@ +PYTHON_PREFIX = @PYTHON_PREFIX@ +PYTHON_SITE_PKG = @PYTHON_SITE_PKG@ +PYTHON_VERSION = @PYTHON_VERSION@ +RANLIB = @RANLIB@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +TESTLDADD = @TESTLDADD@ +VERSION = @VERSION@ +WARNCFLAGS = @WARNCFLAGS@ +XML2_CONFIG = @XML2_CONFIG@ +XML_CPPFLAGS = @XML_CPPFLAGS@ +XML_LIBS = @XML_LIBS@ +ZLIB_CFLAGS = @ZLIB_CFLAGS@ +ZLIB_LIBS = @ZLIB_LIBS@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +pkgpyexecdir = @pkgpyexecdir@ +pkgpythondir = @pkgpythondir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +pyexecdir = @pyexecdir@ +pythondir = @pythondir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target = @target@ +target_alias = @target_alias@ +target_cpu = @target_cpu@ +target_os = @target_os@ +target_vendor = @target_vendor@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +AM_CPPFLAGS = -Wall @DEFS@ +noinst_LTLIBRARIES = libhttp-parser.la +libhttp_parser_la_SOURCES = \ + http-parser/http_parser.c \ + http-parser/http_parser.h + +dist_pkgdata_SCRIPTS = h2o/fetch-ocsp-response +all: all-am + +.SUFFIXES: +.SUFFIXES: .c .lo .o .obj +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu third-party/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu third-party/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): + +clean-noinstLTLIBRARIES: + -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES) + @list='$(noinst_LTLIBRARIES)'; \ + locs=`for p in $$list; do echo $$p; done | \ + sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ + sort -u`; \ + test -z "$$locs" || { \ + echo rm -f $${locs}; \ + rm -f $${locs}; \ + } +http-parser/$(am__dirstamp): + @$(MKDIR_P) http-parser + @: > http-parser/$(am__dirstamp) +http-parser/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) http-parser/$(DEPDIR) + @: > http-parser/$(DEPDIR)/$(am__dirstamp) +http-parser/http_parser.lo: http-parser/$(am__dirstamp) \ + http-parser/$(DEPDIR)/$(am__dirstamp) + +libhttp-parser.la: $(libhttp_parser_la_OBJECTS) $(libhttp_parser_la_DEPENDENCIES) $(EXTRA_libhttp_parser_la_DEPENDENCIES) + $(AM_V_CCLD)$(LINK) $(libhttp_parser_la_OBJECTS) $(libhttp_parser_la_LIBADD) $(LIBS) +install-dist_pkgdataSCRIPTS: $(dist_pkgdata_SCRIPTS) + @$(NORMAL_INSTALL) + @list='$(dist_pkgdata_SCRIPTS)'; test -n "$(pkgdatadir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(pkgdatadir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(pkgdatadir)" || exit 1; \ + fi; \ + for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + if test -f "$$d$$p"; then echo "$$d$$p"; echo "$$p"; else :; fi; \ + done | \ + sed -e 'p;s,.*/,,;n' \ + -e 'h;s|.*|.|' \ + -e 'p;x;s,.*/,,;$(transform)' | sed 'N;N;N;s,\n, ,g' | \ + $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1; } \ + { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ + if ($$2 == $$4) { files[d] = files[d] " " $$1; \ + if (++n[d] == $(am__install_max)) { \ + print "f", d, files[d]; n[d] = 0; files[d] = "" } } \ + else { print "f", d "/" $$4, $$1 } } \ + END { for (d in files) print "f", d, files[d] }' | \ + while read type dir files; do \ + if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ + test -z "$$files" || { \ + echo " $(INSTALL_SCRIPT) $$files '$(DESTDIR)$(pkgdatadir)$$dir'"; \ + $(INSTALL_SCRIPT) $$files "$(DESTDIR)$(pkgdatadir)$$dir" || exit $$?; \ + } \ + ; done + +uninstall-dist_pkgdataSCRIPTS: + @$(NORMAL_UNINSTALL) + @list='$(dist_pkgdata_SCRIPTS)'; test -n "$(pkgdatadir)" || exit 0; \ + files=`for p in $$list; do echo "$$p"; done | \ + sed -e 's,.*/,,;$(transform)'`; \ + dir='$(DESTDIR)$(pkgdatadir)'; $(am__uninstall_files_from_dir) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + -rm -f http-parser/*.$(OBJEXT) + -rm -f http-parser/*.lo + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@http-parser/$(DEPDIR)/http_parser.Plo@am__quote@ + +.c.o: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\ +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< + +.c.obj: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\ +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.c.lo: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\ +@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + -rm -rf http-parser/.libs http-parser/_libs + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-am +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + $(am__define_uniq_tagged_files); \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: ctags-am + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" +cscopelist: cscopelist-am + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(LTLIBRARIES) $(SCRIPTS) +installdirs: + for dir in "$(DESTDIR)$(pkgdatadir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + -rm -f http-parser/$(DEPDIR)/$(am__dirstamp) + -rm -f http-parser/$(am__dirstamp) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \ + mostlyclean-am + +distclean: distclean-am + -rm -rf http-parser/$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: install-dist_pkgdataSCRIPTS + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -rf http-parser/$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-dist_pkgdataSCRIPTS + +.MAKE: install-am install-strip + +.PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ + clean-libtool clean-noinstLTLIBRARIES cscopelist-am ctags \ + ctags-am distclean distclean-compile distclean-generic \ + distclean-libtool distclean-tags distdir dvi dvi-am html \ + html-am info info-am install install-am install-data \ + install-data-am install-dist_pkgdataSCRIPTS install-dvi \ + install-dvi-am install-exec install-exec-am install-html \ + install-html-am install-info install-info-am install-man \ + install-pdf install-pdf-am install-ps install-ps-am \ + install-strip installcheck installcheck-am installdirs \ + maintainer-clean maintainer-clean-generic mostlyclean \ + mostlyclean-compile mostlyclean-generic mostlyclean-libtool \ + pdf pdf-am ps ps-am tags tags-am uninstall uninstall-am \ + uninstall-dist_pkgdataSCRIPTS + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/third-party/h2o/fetch-ocsp-response b/third-party/h2o/fetch-ocsp-response new file mode 100755 index 0000000..9272dae --- /dev/null +++ b/third-party/h2o/fetch-ocsp-response @@ -0,0 +1,150 @@ +#! /bin/sh +exec perl -x $0 "$@" +#! perl + +# Copyright (c) 2015 DeNA Co., Ltd. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +# IN THE SOFTWARE. + +use strict; +use warnings; +use File::Temp qw(tempdir); +use Getopt::Long; + +# from sysexits.h +use constant EX_TEMPFAIL => 75; + +my ($issuer_fn, $opt_help); +my $openssl_cmd = 'openssl'; + +GetOptions( + "issuer=s" => \$issuer_fn, + "openssl=s", => \$openssl_cmd, + help => \$opt_help, +) or exit(1); +if ($opt_help) { + print << "EOT"; +Usage: $0 [] + +Options: + --issuer issuer certificate (if omitted, is extracted from the + certificate chain) + --openssl openssl command to use (default: "openssl") + --help prints this help + +The command issues an OCSP request for given server certificate, verifies the +response and prints the resulting DER. + +The command exits 0 if successful, or 75 (EX_TEMPFAIL) on temporary error. +Other exit codes may be returned in case of hard errors. + +EOT + exit(0); +} + +die "no certificate file\n" + if @ARGV == 0; +my $cert_fn = shift @ARGV; + +my $tempdir = tempdir(CLEANUP => 1); + +my $openssl_version = run_openssl("version"); +chomp $openssl_version; +print STDERR "fetch-ocsp-response (using $openssl_version)\n"; + +# obtain ocsp uri +my $ocsp_uri = run_openssl("x509 -in $cert_fn -noout -ocsp_uri"); +chomp $ocsp_uri; +die "failed to extract ocsp URI from $cert_fn\n" + if $ocsp_uri !~ m{^https?://}; +my($ocsp_host) = $ocsp_uri =~ m{^https?://([^/:]+)}; + +# save issuer certificate +if (! defined $issuer_fn) { + my $chain = read_file($cert_fn); + $chain =~ m{-----END CERTIFICATE-----.*?(-----BEGIN CERTIFICATE-----.*?-----END CERTIFICATE-----)}s + or die "--issuer option was not used, and failed to extract issuer certificate from the certificate\n"; + $issuer_fn = "$tempdir/issuer.crt"; + write_file($issuer_fn, "$1\n"); +} + +# obtain response (without verification) +print STDERR "sending OCSP request to $ocsp_uri\n"; +my $resp = run_openssl( + "ocsp -issuer $issuer_fn -cert $cert_fn -url $ocsp_uri" + . ($openssl_version =~ /^OpenSSL 1\./is ? " -header Host $ocsp_host" : "") + . " -noverify -respout $tempdir/resp.der " . join(' ', @ARGV), + 1, +); +print STDERR $resp; + +# verify the response +print STDERR "verifying the response signature\n"; +my $success; +for my $args ( + # try from exotic options + "-VAfile $issuer_fn", # for comodo + "-partial_chain -trusted_first -CAfile $issuer_fn", # these options are only available in OpenSSL >= 1.0.2 + "-CAfile $issuer_fn", # for OpenSSL <= 1.0.1 +) { + if (system("$openssl_cmd ocsp -respin $tempdir/resp.der $args > $tempdir/verify.out 2>&1") == 0) { + print STDERR "verify OK (used: $args)\n"; + $success = 1; + last; + } +} +if (! $success) { + print STDERR read_file("$tempdir/verify.out"); + tempfail("failed to verify the response\n"); +} + +# success +print read_file("$tempdir/resp.der"); +exit 0; + +sub run_openssl { + my ($args, $tempfail) = @_; + open my $fh, "-|", "$openssl_cmd $args" + or die "failed to invoke $openssl_cmd:$!"; + my $resp = do { local $/; <$fh> }; + close $fh + or ($tempfail ? \&tempfail : \&die)->("OpenSSL exitted abnormally: $openssl_cmd $args:$!"); + $resp; +} + +sub read_file { + my $fn = shift; + open my $fh, "<", $fn + or die "failed to open file:$fn:$!"; + local $/; + <$fh>; +} + +sub write_file { + my ($fn, $data) = @_; + open my $fh, ">", $fn + or die "failed to open file:$fn:$!"; + print $fh $data; + close $fh; +} + +sub tempfail { + print STDERR @_; + exit EX_TEMPFAIL; +} diff --git a/third-party/http-parser/http_parser.c b/third-party/http-parser/http_parser.c new file mode 100644 index 0000000..aa6310f --- /dev/null +++ b/third-party/http-parser/http_parser.c @@ -0,0 +1,2418 @@ +/* Based on src/http/ngx_http_parse.c from NGINX copyright Igor Sysoev + * + * Additional changes are licensed under the same terms as NGINX and + * copyright Joyent, Inc. and other Node contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +#include "http_parser.h" +#include +#include +#include +#include +#include +#include + +#ifndef ULLONG_MAX +# define ULLONG_MAX ((uint64_t) -1) /* 2^64-1 */ +#endif + +#ifndef MIN +# define MIN(a,b) ((a) < (b) ? (a) : (b)) +#endif + +#ifndef ARRAY_SIZE +# define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) +#endif + +#ifndef BIT_AT +# define BIT_AT(a, i) \ + (!!((unsigned int) (a)[(unsigned int) (i) >> 3] & \ + (1 << ((unsigned int) (i) & 7)))) +#endif + +#ifndef ELEM_AT +# define ELEM_AT(a, i, v) ((unsigned int) (i) < ARRAY_SIZE(a) ? (a)[(i)] : (v)) +#endif + +#define SET_ERRNO(e) \ +do { \ + parser->http_errno = (e); \ +} while(0) + +#define CURRENT_STATE() p_state +#define UPDATE_STATE(V) p_state = (enum state) (V); +#define RETURN(V) \ +do { \ + parser->state = CURRENT_STATE(); \ + return (V); \ +} while (0); +#define REEXECUTE() \ + goto reexecute; \ + + +#ifdef __GNUC__ +# define LIKELY(X) __builtin_expect(!!(X), 1) +# define UNLIKELY(X) __builtin_expect(!!(X), 0) +#else +# define LIKELY(X) (X) +# define UNLIKELY(X) (X) +#endif + + +/* Run the notify callback FOR, returning ER if it fails */ +#define CALLBACK_NOTIFY_(FOR, ER) \ +do { \ + assert(HTTP_PARSER_ERRNO(parser) == HPE_OK); \ + \ + if (LIKELY(settings->on_##FOR)) { \ + parser->state = CURRENT_STATE(); \ + if (UNLIKELY(0 != settings->on_##FOR(parser))) { \ + SET_ERRNO(HPE_CB_##FOR); \ + } \ + UPDATE_STATE(parser->state); \ + \ + /* We either errored above or got paused; get out */ \ + if (UNLIKELY(HTTP_PARSER_ERRNO(parser) != HPE_OK)) { \ + return (ER); \ + } \ + } \ +} while (0) + +/* Run the notify callback FOR and consume the current byte */ +#define CALLBACK_NOTIFY(FOR) CALLBACK_NOTIFY_(FOR, p - data + 1) + +/* Run the notify callback FOR and don't consume the current byte */ +#define CALLBACK_NOTIFY_NOADVANCE(FOR) CALLBACK_NOTIFY_(FOR, p - data) + +/* Run data callback FOR with LEN bytes, returning ER if it fails */ +#define CALLBACK_DATA_(FOR, LEN, ER) \ +do { \ + assert(HTTP_PARSER_ERRNO(parser) == HPE_OK); \ + \ + if (FOR##_mark) { \ + if (LIKELY(settings->on_##FOR)) { \ + parser->state = CURRENT_STATE(); \ + if (UNLIKELY(0 != \ + settings->on_##FOR(parser, FOR##_mark, (LEN)))) { \ + SET_ERRNO(HPE_CB_##FOR); \ + } \ + UPDATE_STATE(parser->state); \ + \ + /* We either errored above or got paused; get out */ \ + if (UNLIKELY(HTTP_PARSER_ERRNO(parser) != HPE_OK)) { \ + return (ER); \ + } \ + } \ + FOR##_mark = NULL; \ + } \ +} while (0) + +/* Run the data callback FOR and consume the current byte */ +#define CALLBACK_DATA(FOR) \ + CALLBACK_DATA_(FOR, p - FOR##_mark, p - data + 1) + +/* Run the data callback FOR and don't consume the current byte */ +#define CALLBACK_DATA_NOADVANCE(FOR) \ + CALLBACK_DATA_(FOR, p - FOR##_mark, p - data) + +/* Set the mark FOR; non-destructive if mark is already set */ +#define MARK(FOR) \ +do { \ + if (!FOR##_mark) { \ + FOR##_mark = p; \ + } \ +} while (0) + +/* Don't allow the total size of the HTTP headers (including the status + * line) to exceed HTTP_MAX_HEADER_SIZE. This check is here to protect + * embedders against denial-of-service attacks where the attacker feeds + * us a never-ending header that the embedder keeps buffering. + * + * This check is arguably the responsibility of embedders but we're doing + * it on the embedder's behalf because most won't bother and this way we + * make the web a little safer. HTTP_MAX_HEADER_SIZE is still far bigger + * than any reasonable request or response so this should never affect + * day-to-day operation. + */ +#define COUNT_HEADER_SIZE(V) \ +do { \ + parser->nread += (V); \ + if (UNLIKELY(parser->nread > (HTTP_MAX_HEADER_SIZE))) { \ + SET_ERRNO(HPE_HEADER_OVERFLOW); \ + goto error; \ + } \ +} while (0) + + +#define PROXY_CONNECTION "proxy-connection" +#define CONNECTION "connection" +#define CONTENT_LENGTH "content-length" +#define TRANSFER_ENCODING "transfer-encoding" +#define UPGRADE "upgrade" +#define CHUNKED "chunked" +#define KEEP_ALIVE "keep-alive" +#define CLOSE "close" + + +static const char *method_strings[] = + { +#define XX(num, name, string) #string, + HTTP_METHOD_MAP(XX) +#undef XX + }; + + +/* Tokens as defined by rfc 2616. Also lowercases them. + * token = 1* + * separators = "(" | ")" | "<" | ">" | "@" + * | "," | ";" | ":" | "\" | <"> + * | "/" | "[" | "]" | "?" | "=" + * | "{" | "}" | SP | HT + */ +static const char tokens[256] = { +/* 0 nul 1 soh 2 stx 3 etx 4 eot 5 enq 6 ack 7 bel */ + 0, 0, 0, 0, 0, 0, 0, 0, +/* 8 bs 9 ht 10 nl 11 vt 12 np 13 cr 14 so 15 si */ + 0, 0, 0, 0, 0, 0, 0, 0, +/* 16 dle 17 dc1 18 dc2 19 dc3 20 dc4 21 nak 22 syn 23 etb */ + 0, 0, 0, 0, 0, 0, 0, 0, +/* 24 can 25 em 26 sub 27 esc 28 fs 29 gs 30 rs 31 us */ + 0, 0, 0, 0, 0, 0, 0, 0, +/* 32 sp 33 ! 34 " 35 # 36 $ 37 % 38 & 39 ' */ + 0, '!', 0, '#', '$', '%', '&', '\'', +/* 40 ( 41 ) 42 * 43 + 44 , 45 - 46 . 47 / */ + 0, 0, '*', '+', 0, '-', '.', 0, +/* 48 0 49 1 50 2 51 3 52 4 53 5 54 6 55 7 */ + '0', '1', '2', '3', '4', '5', '6', '7', +/* 56 8 57 9 58 : 59 ; 60 < 61 = 62 > 63 ? */ + '8', '9', 0, 0, 0, 0, 0, 0, +/* 64 @ 65 A 66 B 67 C 68 D 69 E 70 F 71 G */ + 0, 'a', 'b', 'c', 'd', 'e', 'f', 'g', +/* 72 H 73 I 74 J 75 K 76 L 77 M 78 N 79 O */ + 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', +/* 80 P 81 Q 82 R 83 S 84 T 85 U 86 V 87 W */ + 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', +/* 88 X 89 Y 90 Z 91 [ 92 \ 93 ] 94 ^ 95 _ */ + 'x', 'y', 'z', 0, 0, 0, '^', '_', +/* 96 ` 97 a 98 b 99 c 100 d 101 e 102 f 103 g */ + '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g', +/* 104 h 105 i 106 j 107 k 108 l 109 m 110 n 111 o */ + 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', +/* 112 p 113 q 114 r 115 s 116 t 117 u 118 v 119 w */ + 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', +/* 120 x 121 y 122 z 123 { 124 | 125 } 126 ~ 127 del */ + 'x', 'y', 'z', 0, '|', 0, '~', 0 }; + + +static const int8_t unhex[256] = + {-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 + , 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,-1,-1,-1,-1,-1,-1 + ,-1,10,11,12,13,14,15,-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,10,11,12,13,14,15,-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 + }; + + +#if HTTP_PARSER_STRICT +# define T(v) 0 +#else +# define T(v) v +#endif + + +static const uint8_t normal_url_char[32] = { +/* 0 nul 1 soh 2 stx 3 etx 4 eot 5 enq 6 ack 7 bel */ + 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0, +/* 8 bs 9 ht 10 nl 11 vt 12 np 13 cr 14 so 15 si */ + 0 | T(2) | 0 | 0 | T(16) | 0 | 0 | 0, +/* 16 dle 17 dc1 18 dc2 19 dc3 20 dc4 21 nak 22 syn 23 etb */ + 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0, +/* 24 can 25 em 26 sub 27 esc 28 fs 29 gs 30 rs 31 us */ + 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0, +/* 32 sp 33 ! 34 " 35 # 36 $ 37 % 38 & 39 ' */ + 0 | 2 | 4 | 0 | 16 | 32 | 64 | 128, +/* 40 ( 41 ) 42 * 43 + 44 , 45 - 46 . 47 / */ + 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, +/* 48 0 49 1 50 2 51 3 52 4 53 5 54 6 55 7 */ + 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, +/* 56 8 57 9 58 : 59 ; 60 < 61 = 62 > 63 ? */ + 1 | 2 | 4 | 8 | 16 | 32 | 64 | 0, +/* 64 @ 65 A 66 B 67 C 68 D 69 E 70 F 71 G */ + 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, +/* 72 H 73 I 74 J 75 K 76 L 77 M 78 N 79 O */ + 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, +/* 80 P 81 Q 82 R 83 S 84 T 85 U 86 V 87 W */ + 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, +/* 88 X 89 Y 90 Z 91 [ 92 \ 93 ] 94 ^ 95 _ */ + 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, +/* 96 ` 97 a 98 b 99 c 100 d 101 e 102 f 103 g */ + 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, +/* 104 h 105 i 106 j 107 k 108 l 109 m 110 n 111 o */ + 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, +/* 112 p 113 q 114 r 115 s 116 t 117 u 118 v 119 w */ + 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, +/* 120 x 121 y 122 z 123 { 124 | 125 } 126 ~ 127 del */ + 1 | 2 | 4 | 8 | 16 | 32 | 64 | 0, }; + +#undef T + +enum state + { s_dead = 1 /* important that this is > 0 */ + + , s_start_req_or_res + , s_res_or_resp_H + , s_start_res + , s_res_H + , s_res_HT + , s_res_HTT + , s_res_HTTP + , s_res_first_http_major + , s_res_http_major + , s_res_first_http_minor + , s_res_http_minor + , s_res_first_status_code + , s_res_status_code + , s_res_status_start + , s_res_status + , s_res_line_almost_done + + , s_start_req + + , s_req_method + , s_req_spaces_before_url + , s_req_schema + , s_req_schema_slash + , s_req_schema_slash_slash + , s_req_server_start + , s_req_server + , s_req_server_with_at + , s_req_path + , s_req_query_string_start + , s_req_query_string + , s_req_fragment_start + , s_req_fragment + , s_req_http_start + , s_req_http_H + , s_req_http_HT + , s_req_http_HTT + , s_req_http_HTTP + , s_req_first_http_major + , s_req_http_major + , s_req_first_http_minor + , s_req_http_minor + , s_req_line_almost_done + + , s_header_field_start + , s_header_field + , s_header_value_discard_ws + , s_header_value_discard_ws_almost_done + , s_header_value_discard_lws + , s_header_value_start + , s_header_value + , s_header_value_lws + + , s_header_almost_done + + , s_chunk_size_start + , s_chunk_size + , s_chunk_parameters + , s_chunk_size_almost_done + + , s_headers_almost_done + , s_headers_done + + /* Important: 's_headers_done' must be the last 'header' state. All + * states beyond this must be 'body' states. It is used for overflow + * checking. See the PARSING_HEADER() macro. + */ + + , s_chunk_data + , s_chunk_data_almost_done + , s_chunk_data_done + + , s_body_identity + , s_body_identity_eof + + , s_message_done + }; + + +#define PARSING_HEADER(state) (state <= s_headers_done) + + +enum header_states + { h_general = 0 + , h_C + , h_CO + , h_CON + + , h_matching_connection + , h_matching_proxy_connection + , h_matching_content_length + , h_matching_transfer_encoding + , h_matching_upgrade + + , h_connection + , h_content_length + , h_transfer_encoding + , h_upgrade + + , h_matching_transfer_encoding_chunked + , h_matching_connection_token_start + , h_matching_connection_keep_alive + , h_matching_connection_close + , h_matching_connection_upgrade + , h_matching_connection_token + + , h_transfer_encoding_chunked + , h_connection_keep_alive + , h_connection_close + , h_connection_upgrade + }; + +enum http_host_state + { + s_http_host_dead = 1 + , s_http_userinfo_start + , s_http_userinfo + , s_http_host_start + , s_http_host_v6_start + , s_http_host + , s_http_host_v6 + , s_http_host_v6_end + , s_http_host_port_start + , s_http_host_port +}; + +/* Macros for character classes; depends on strict-mode */ +#define CR '\r' +#define LF '\n' +#define LOWER(c) (unsigned char)(c | 0x20) +#define IS_ALPHA(c) (LOWER(c) >= 'a' && LOWER(c) <= 'z') +#define IS_NUM(c) ((c) >= '0' && (c) <= '9') +#define IS_ALPHANUM(c) (IS_ALPHA(c) || IS_NUM(c)) +#define IS_HEX(c) (IS_NUM(c) || (LOWER(c) >= 'a' && LOWER(c) <= 'f')) +#define IS_MARK(c) ((c) == '-' || (c) == '_' || (c) == '.' || \ + (c) == '!' || (c) == '~' || (c) == '*' || (c) == '\'' || (c) == '(' || \ + (c) == ')') +#define IS_USERINFO_CHAR(c) (IS_ALPHANUM(c) || IS_MARK(c) || (c) == '%' || \ + (c) == ';' || (c) == ':' || (c) == '&' || (c) == '=' || (c) == '+' || \ + (c) == '$' || (c) == ',') + +#define STRICT_TOKEN(c) (tokens[(unsigned char)c]) + +#if HTTP_PARSER_STRICT +#define TOKEN(c) (tokens[(unsigned char)c]) +#define IS_URL_CHAR(c) (BIT_AT(normal_url_char, (unsigned char)c)) +#define IS_HOST_CHAR(c) (IS_ALPHANUM(c) || (c) == '.' || (c) == '-') +#else +#define TOKEN(c) ((c == ' ') ? ' ' : tokens[(unsigned char)c]) +#define IS_URL_CHAR(c) \ + (BIT_AT(normal_url_char, (unsigned char)c) || ((c) & 0x80)) +#define IS_HOST_CHAR(c) \ + (IS_ALPHANUM(c) || (c) == '.' || (c) == '-' || (c) == '_') +#endif + + +#define start_state (parser->type == HTTP_REQUEST ? s_start_req : s_start_res) + + +#if HTTP_PARSER_STRICT +# define STRICT_CHECK(cond) \ +do { \ + if (cond) { \ + SET_ERRNO(HPE_STRICT); \ + goto error; \ + } \ +} while (0) +# define NEW_MESSAGE() (http_should_keep_alive(parser) ? start_state : s_dead) +#else +# define STRICT_CHECK(cond) +# define NEW_MESSAGE() start_state +#endif + + +/* Map errno values to strings for human-readable output */ +#define HTTP_STRERROR_GEN(n, s) { "HPE_" #n, s }, +static struct { + const char *name; + const char *description; +} http_strerror_tab[] = { + HTTP_ERRNO_MAP(HTTP_STRERROR_GEN) +}; +#undef HTTP_STRERROR_GEN + +int http_message_needs_eof(const http_parser *parser); + +/* Our URL parser. + * + * This is designed to be shared by http_parser_execute() for URL validation, + * hence it has a state transition + byte-for-byte interface. In addition, it + * is meant to be embedded in http_parser_parse_url(), which does the dirty + * work of turning state transitions URL components for its API. + * + * This function should only be invoked with non-space characters. It is + * assumed that the caller cares about (and can detect) the transition between + * URL and non-URL states by looking for these. + */ +static enum state +parse_url_char(enum state s, const char ch) +{ + if (ch == ' ' || ch == '\r' || ch == '\n') { + return s_dead; + } + +#if HTTP_PARSER_STRICT + if (ch == '\t' || ch == '\f') { + return s_dead; + } +#endif + + switch (s) { + case s_req_spaces_before_url: + /* Proxied requests are followed by scheme of an absolute URI (alpha). + * All methods except CONNECT are followed by '/' or '*'. + */ + + if (ch == '/' || ch == '*') { + return s_req_path; + } + + if (IS_ALPHA(ch)) { + return s_req_schema; + } + + break; + + case s_req_schema: + if (IS_ALPHA(ch)) { + return s; + } + + if (ch == ':') { + return s_req_schema_slash; + } + + break; + + case s_req_schema_slash: + if (ch == '/') { + return s_req_schema_slash_slash; + } + + break; + + case s_req_schema_slash_slash: + if (ch == '/') { + return s_req_server_start; + } + + break; + + case s_req_server_with_at: + if (ch == '@') { + return s_dead; + } + + /* FALLTHROUGH */ + case s_req_server_start: + case s_req_server: + if (ch == '/') { + return s_req_path; + } + + if (ch == '?') { + return s_req_query_string_start; + } + + if (ch == '@') { + return s_req_server_with_at; + } + + if (IS_USERINFO_CHAR(ch) || ch == '[' || ch == ']') { + return s_req_server; + } + + break; + + case s_req_path: + if (IS_URL_CHAR(ch)) { + return s; + } + + switch (ch) { + case '?': + return s_req_query_string_start; + + case '#': + return s_req_fragment_start; + } + + break; + + case s_req_query_string_start: + case s_req_query_string: + if (IS_URL_CHAR(ch)) { + return s_req_query_string; + } + + switch (ch) { + case '?': + /* allow extra '?' in query string */ + return s_req_query_string; + + case '#': + return s_req_fragment_start; + } + + break; + + case s_req_fragment_start: + if (IS_URL_CHAR(ch)) { + return s_req_fragment; + } + + switch (ch) { + case '?': + return s_req_fragment; + + case '#': + return s; + } + + break; + + case s_req_fragment: + if (IS_URL_CHAR(ch)) { + return s; + } + + switch (ch) { + case '?': + case '#': + return s; + } + + break; + + default: + break; + } + + /* We should never fall out of the switch above unless there's an error */ + return s_dead; +} + +size_t http_parser_execute (http_parser *parser, + const http_parser_settings *settings, + const char *data, + size_t len) +{ + char c, ch; + int8_t unhex_val; + const char *p = data; + const char *header_field_mark = 0; + const char *header_value_mark = 0; + const char *url_mark = 0; + const char *body_mark = 0; + const char *status_mark = 0; + enum state p_state = (enum state) parser->state; + + /* We're in an error state. Don't bother doing anything. */ + if (HTTP_PARSER_ERRNO(parser) != HPE_OK) { + return 0; + } + + if (len == 0) { + switch (CURRENT_STATE()) { + case s_body_identity_eof: + /* Use of CALLBACK_NOTIFY() here would erroneously return 1 byte read if + * we got paused. + */ + CALLBACK_NOTIFY_NOADVANCE(message_complete); + return 0; + + case s_dead: + case s_start_req_or_res: + case s_start_res: + case s_start_req: + return 0; + + default: + SET_ERRNO(HPE_INVALID_EOF_STATE); + return 1; + } + } + + + if (CURRENT_STATE() == s_header_field) + header_field_mark = data; + if (CURRENT_STATE() == s_header_value) + header_value_mark = data; + switch (CURRENT_STATE()) { + case s_req_path: + case s_req_schema: + case s_req_schema_slash: + case s_req_schema_slash_slash: + case s_req_server_start: + case s_req_server: + case s_req_server_with_at: + case s_req_query_string_start: + case s_req_query_string: + case s_req_fragment_start: + case s_req_fragment: + url_mark = data; + break; + case s_res_status: + status_mark = data; + break; + default: + break; + } + + for (p=data; p != data + len; p++) { + ch = *p; + + if (PARSING_HEADER(CURRENT_STATE())) + COUNT_HEADER_SIZE(1); + +reexecute: + switch (CURRENT_STATE()) { + + case s_dead: + /* this state is used after a 'Connection: close' message + * the parser will error out if it reads another message + */ + if (LIKELY(ch == CR || ch == LF)) + break; + + SET_ERRNO(HPE_CLOSED_CONNECTION); + goto error; + + case s_start_req_or_res: + { + if (ch == CR || ch == LF) + break; + parser->flags = 0; + parser->content_length = ULLONG_MAX; + + if (ch == 'H') { + UPDATE_STATE(s_res_or_resp_H); + + CALLBACK_NOTIFY(message_begin); + } else { + parser->type = HTTP_REQUEST; + UPDATE_STATE(s_start_req); + REEXECUTE(); + } + + break; + } + + case s_res_or_resp_H: + if (ch == 'T') { + parser->type = HTTP_RESPONSE; + UPDATE_STATE(s_res_HT); + } else { + if (UNLIKELY(ch != 'E')) { + SET_ERRNO(HPE_INVALID_CONSTANT); + goto error; + } + + parser->type = HTTP_REQUEST; + parser->method = HTTP_HEAD; + parser->index = 2; + UPDATE_STATE(s_req_method); + } + break; + + case s_start_res: + { + parser->flags = 0; + parser->content_length = ULLONG_MAX; + + switch (ch) { + case 'H': + UPDATE_STATE(s_res_H); + break; + + case CR: + case LF: + break; + + default: + SET_ERRNO(HPE_INVALID_CONSTANT); + goto error; + } + + CALLBACK_NOTIFY(message_begin); + break; + } + + case s_res_H: + STRICT_CHECK(ch != 'T'); + UPDATE_STATE(s_res_HT); + break; + + case s_res_HT: + STRICT_CHECK(ch != 'T'); + UPDATE_STATE(s_res_HTT); + break; + + case s_res_HTT: + STRICT_CHECK(ch != 'P'); + UPDATE_STATE(s_res_HTTP); + break; + + case s_res_HTTP: + STRICT_CHECK(ch != '/'); + UPDATE_STATE(s_res_first_http_major); + break; + + case s_res_first_http_major: + if (UNLIKELY(ch < '0' || ch > '9')) { + SET_ERRNO(HPE_INVALID_VERSION); + goto error; + } + + parser->http_major = ch - '0'; + UPDATE_STATE(s_res_http_major); + break; + + /* major HTTP version or dot */ + case s_res_http_major: + { + if (ch == '.') { + UPDATE_STATE(s_res_first_http_minor); + break; + } + + if (!IS_NUM(ch)) { + SET_ERRNO(HPE_INVALID_VERSION); + goto error; + } + + parser->http_major *= 10; + parser->http_major += ch - '0'; + + if (UNLIKELY(parser->http_major > 999)) { + SET_ERRNO(HPE_INVALID_VERSION); + goto error; + } + + break; + } + + /* first digit of minor HTTP version */ + case s_res_first_http_minor: + if (UNLIKELY(!IS_NUM(ch))) { + SET_ERRNO(HPE_INVALID_VERSION); + goto error; + } + + parser->http_minor = ch - '0'; + UPDATE_STATE(s_res_http_minor); + break; + + /* minor HTTP version or end of request line */ + case s_res_http_minor: + { + if (ch == ' ') { + UPDATE_STATE(s_res_first_status_code); + break; + } + + if (UNLIKELY(!IS_NUM(ch))) { + SET_ERRNO(HPE_INVALID_VERSION); + goto error; + } + + parser->http_minor *= 10; + parser->http_minor += ch - '0'; + + if (UNLIKELY(parser->http_minor > 999)) { + SET_ERRNO(HPE_INVALID_VERSION); + goto error; + } + + break; + } + + case s_res_first_status_code: + { + if (!IS_NUM(ch)) { + if (ch == ' ') { + break; + } + + SET_ERRNO(HPE_INVALID_STATUS); + goto error; + } + parser->status_code = ch - '0'; + UPDATE_STATE(s_res_status_code); + break; + } + + case s_res_status_code: + { + if (!IS_NUM(ch)) { + switch (ch) { + case ' ': + UPDATE_STATE(s_res_status_start); + break; + case CR: + UPDATE_STATE(s_res_line_almost_done); + break; + case LF: + UPDATE_STATE(s_header_field_start); + break; + default: + SET_ERRNO(HPE_INVALID_STATUS); + goto error; + } + break; + } + + parser->status_code *= 10; + parser->status_code += ch - '0'; + + if (UNLIKELY(parser->status_code > 999)) { + SET_ERRNO(HPE_INVALID_STATUS); + goto error; + } + + break; + } + + case s_res_status_start: + { + if (ch == CR) { + UPDATE_STATE(s_res_line_almost_done); + break; + } + + if (ch == LF) { + UPDATE_STATE(s_header_field_start); + break; + } + + MARK(status); + UPDATE_STATE(s_res_status); + parser->index = 0; + break; + } + + case s_res_status: + if (ch == CR) { + UPDATE_STATE(s_res_line_almost_done); + CALLBACK_DATA(status); + break; + } + + if (ch == LF) { + UPDATE_STATE(s_header_field_start); + CALLBACK_DATA(status); + break; + } + + break; + + case s_res_line_almost_done: + STRICT_CHECK(ch != LF); + UPDATE_STATE(s_header_field_start); + break; + + case s_start_req: + { + if (ch == CR || ch == LF) + break; + parser->flags = 0; + parser->content_length = ULLONG_MAX; + + if (UNLIKELY(!IS_ALPHA(ch))) { + SET_ERRNO(HPE_INVALID_METHOD); + goto error; + } + + parser->method = (enum http_method) 0; + parser->index = 1; + switch (ch) { + case 'C': parser->method = HTTP_CONNECT; /* or COPY, CHECKOUT */ break; + case 'D': parser->method = HTTP_DELETE; break; + case 'G': parser->method = HTTP_GET; break; + case 'H': parser->method = HTTP_HEAD; break; + case 'L': parser->method = HTTP_LOCK; break; + case 'M': parser->method = HTTP_MKCOL; /* or MOVE, MKACTIVITY, MERGE, M-SEARCH, MKCALENDAR */ break; + case 'N': parser->method = HTTP_NOTIFY; break; + case 'O': parser->method = HTTP_OPTIONS; break; + case 'P': parser->method = HTTP_POST; + /* or PROPFIND|PROPPATCH|PUT|PATCH|PURGE */ + break; + case 'R': parser->method = HTTP_REPORT; break; + case 'S': parser->method = HTTP_SUBSCRIBE; /* or SEARCH */ break; + case 'T': parser->method = HTTP_TRACE; break; + case 'U': parser->method = HTTP_UNLOCK; /* or UNSUBSCRIBE */ break; + default: + SET_ERRNO(HPE_INVALID_METHOD); + goto error; + } + UPDATE_STATE(s_req_method); + + CALLBACK_NOTIFY(message_begin); + + break; + } + + case s_req_method: + { + const char *matcher; + if (UNLIKELY(ch == '\0')) { + SET_ERRNO(HPE_INVALID_METHOD); + goto error; + } + + matcher = method_strings[parser->method]; + if (ch == ' ' && matcher[parser->index] == '\0') { + UPDATE_STATE(s_req_spaces_before_url); + } else if (ch == matcher[parser->index]) { + ; /* nada */ + } else if (parser->method == HTTP_CONNECT) { + if (parser->index == 1 && ch == 'H') { + parser->method = HTTP_CHECKOUT; + } else if (parser->index == 2 && ch == 'P') { + parser->method = HTTP_COPY; + } else { + SET_ERRNO(HPE_INVALID_METHOD); + goto error; + } + } else if (parser->method == HTTP_MKCOL) { + if (parser->index == 1 && ch == 'O') { + parser->method = HTTP_MOVE; + } else if (parser->index == 1 && ch == 'E') { + parser->method = HTTP_MERGE; + } else if (parser->index == 1 && ch == '-') { + parser->method = HTTP_MSEARCH; + } else if (parser->index == 2 && ch == 'A') { + parser->method = HTTP_MKACTIVITY; + } else if (parser->index == 3 && ch == 'A') { + parser->method = HTTP_MKCALENDAR; + } else { + SET_ERRNO(HPE_INVALID_METHOD); + goto error; + } + } else if (parser->method == HTTP_SUBSCRIBE) { + if (parser->index == 1 && ch == 'E') { + parser->method = HTTP_SEARCH; + } else { + SET_ERRNO(HPE_INVALID_METHOD); + goto error; + } + } else if (parser->index == 1 && parser->method == HTTP_POST) { + if (ch == 'R') { + parser->method = HTTP_PROPFIND; /* or HTTP_PROPPATCH */ + } else if (ch == 'U') { + parser->method = HTTP_PUT; /* or HTTP_PURGE */ + } else if (ch == 'A') { + parser->method = HTTP_PATCH; + } else { + SET_ERRNO(HPE_INVALID_METHOD); + goto error; + } + } else if (parser->index == 2) { + if (parser->method == HTTP_PUT) { + if (ch == 'R') { + parser->method = HTTP_PURGE; + } else { + SET_ERRNO(HPE_INVALID_METHOD); + goto error; + } + } else if (parser->method == HTTP_UNLOCK) { + if (ch == 'S') { + parser->method = HTTP_UNSUBSCRIBE; + } else { + SET_ERRNO(HPE_INVALID_METHOD); + goto error; + } + } else { + SET_ERRNO(HPE_INVALID_METHOD); + goto error; + } + } else if (parser->index == 4 && parser->method == HTTP_PROPFIND && ch == 'P') { + parser->method = HTTP_PROPPATCH; + } else { + SET_ERRNO(HPE_INVALID_METHOD); + goto error; + } + + ++parser->index; + break; + } + + case s_req_spaces_before_url: + { + if (ch == ' ') break; + + MARK(url); + if (parser->method == HTTP_CONNECT) { + UPDATE_STATE(s_req_server_start); + } + + UPDATE_STATE(parse_url_char(CURRENT_STATE(), ch)); + if (UNLIKELY(CURRENT_STATE() == s_dead)) { + SET_ERRNO(HPE_INVALID_URL); + goto error; + } + + break; + } + + case s_req_schema: + case s_req_schema_slash: + case s_req_schema_slash_slash: + case s_req_server_start: + { + switch (ch) { + /* No whitespace allowed here */ + case ' ': + case CR: + case LF: + SET_ERRNO(HPE_INVALID_URL); + goto error; + default: + UPDATE_STATE(parse_url_char(CURRENT_STATE(), ch)); + if (UNLIKELY(CURRENT_STATE() == s_dead)) { + SET_ERRNO(HPE_INVALID_URL); + goto error; + } + } + + break; + } + + case s_req_server: + case s_req_server_with_at: + case s_req_path: + case s_req_query_string_start: + case s_req_query_string: + case s_req_fragment_start: + case s_req_fragment: + { + switch (ch) { + case ' ': + UPDATE_STATE(s_req_http_start); + CALLBACK_DATA(url); + break; + case CR: + case LF: + parser->http_major = 0; + parser->http_minor = 9; + UPDATE_STATE((ch == CR) ? + s_req_line_almost_done : + s_header_field_start); + CALLBACK_DATA(url); + break; + default: + UPDATE_STATE(parse_url_char(CURRENT_STATE(), ch)); + if (UNLIKELY(CURRENT_STATE() == s_dead)) { + SET_ERRNO(HPE_INVALID_URL); + goto error; + } + } + break; + } + + case s_req_http_start: + switch (ch) { + case 'H': + UPDATE_STATE(s_req_http_H); + break; + case ' ': + break; + default: + SET_ERRNO(HPE_INVALID_CONSTANT); + goto error; + } + break; + + case s_req_http_H: + STRICT_CHECK(ch != 'T'); + UPDATE_STATE(s_req_http_HT); + break; + + case s_req_http_HT: + STRICT_CHECK(ch != 'T'); + UPDATE_STATE(s_req_http_HTT); + break; + + case s_req_http_HTT: + STRICT_CHECK(ch != 'P'); + UPDATE_STATE(s_req_http_HTTP); + break; + + case s_req_http_HTTP: + STRICT_CHECK(ch != '/'); + UPDATE_STATE(s_req_first_http_major); + break; + + /* first digit of major HTTP version */ + case s_req_first_http_major: + if (UNLIKELY(ch < '1' || ch > '9')) { + SET_ERRNO(HPE_INVALID_VERSION); + goto error; + } + + parser->http_major = ch - '0'; + UPDATE_STATE(s_req_http_major); + break; + + /* major HTTP version or dot */ + case s_req_http_major: + { + if (ch == '.') { + UPDATE_STATE(s_req_first_http_minor); + break; + } + + if (UNLIKELY(!IS_NUM(ch))) { + SET_ERRNO(HPE_INVALID_VERSION); + goto error; + } + + parser->http_major *= 10; + parser->http_major += ch - '0'; + + if (UNLIKELY(parser->http_major > 999)) { + SET_ERRNO(HPE_INVALID_VERSION); + goto error; + } + + break; + } + + /* first digit of minor HTTP version */ + case s_req_first_http_minor: + if (UNLIKELY(!IS_NUM(ch))) { + SET_ERRNO(HPE_INVALID_VERSION); + goto error; + } + + parser->http_minor = ch - '0'; + UPDATE_STATE(s_req_http_minor); + break; + + /* minor HTTP version or end of request line */ + case s_req_http_minor: + { + if (ch == CR) { + UPDATE_STATE(s_req_line_almost_done); + break; + } + + if (ch == LF) { + UPDATE_STATE(s_header_field_start); + break; + } + + /* XXX allow spaces after digit? */ + + if (UNLIKELY(!IS_NUM(ch))) { + SET_ERRNO(HPE_INVALID_VERSION); + goto error; + } + + parser->http_minor *= 10; + parser->http_minor += ch - '0'; + + if (UNLIKELY(parser->http_minor > 999)) { + SET_ERRNO(HPE_INVALID_VERSION); + goto error; + } + + break; + } + + /* end of request line */ + case s_req_line_almost_done: + { + if (UNLIKELY(ch != LF)) { + SET_ERRNO(HPE_LF_EXPECTED); + goto error; + } + + UPDATE_STATE(s_header_field_start); + break; + } + + case s_header_field_start: + { + if (ch == CR) { + UPDATE_STATE(s_headers_almost_done); + break; + } + + if (ch == LF) { + /* they might be just sending \n instead of \r\n so this would be + * the second \n to denote the end of headers*/ + UPDATE_STATE(s_headers_almost_done); + REEXECUTE(); + } + + c = TOKEN(ch); + + if (UNLIKELY(!c)) { + SET_ERRNO(HPE_INVALID_HEADER_TOKEN); + goto error; + } + + MARK(header_field); + + parser->index = 0; + UPDATE_STATE(s_header_field); + + switch (c) { + case 'c': + parser->header_state = h_C; + break; + + case 'p': + parser->header_state = h_matching_proxy_connection; + break; + + case 't': + parser->header_state = h_matching_transfer_encoding; + break; + + case 'u': + parser->header_state = h_matching_upgrade; + break; + + default: + parser->header_state = h_general; + break; + } + break; + } + + case s_header_field: + { + const char* start = p; + for (; p != data + len; p++) { + ch = *p; + c = TOKEN(ch); + + if (!c) + break; + + switch (parser->header_state) { + case h_general: + break; + + case h_C: + parser->index++; + parser->header_state = (c == 'o' ? h_CO : h_general); + break; + + case h_CO: + parser->index++; + parser->header_state = (c == 'n' ? h_CON : h_general); + break; + + case h_CON: + parser->index++; + switch (c) { + case 'n': + parser->header_state = h_matching_connection; + break; + case 't': + parser->header_state = h_matching_content_length; + break; + default: + parser->header_state = h_general; + break; + } + break; + + /* connection */ + + case h_matching_connection: + parser->index++; + if (parser->index > sizeof(CONNECTION)-1 + || c != CONNECTION[parser->index]) { + parser->header_state = h_general; + } else if (parser->index == sizeof(CONNECTION)-2) { + parser->header_state = h_connection; + } + break; + + /* proxy-connection */ + + case h_matching_proxy_connection: + parser->index++; + if (parser->index > sizeof(PROXY_CONNECTION)-1 + || c != PROXY_CONNECTION[parser->index]) { + parser->header_state = h_general; + } else if (parser->index == sizeof(PROXY_CONNECTION)-2) { + parser->header_state = h_connection; + } + break; + + /* content-length */ + + case h_matching_content_length: + parser->index++; + if (parser->index > sizeof(CONTENT_LENGTH)-1 + || c != CONTENT_LENGTH[parser->index]) { + parser->header_state = h_general; + } else if (parser->index == sizeof(CONTENT_LENGTH)-2) { + parser->header_state = h_content_length; + } + break; + + /* transfer-encoding */ + + case h_matching_transfer_encoding: + parser->index++; + if (parser->index > sizeof(TRANSFER_ENCODING)-1 + || c != TRANSFER_ENCODING[parser->index]) { + parser->header_state = h_general; + } else if (parser->index == sizeof(TRANSFER_ENCODING)-2) { + parser->header_state = h_transfer_encoding; + } + break; + + /* upgrade */ + + case h_matching_upgrade: + parser->index++; + if (parser->index > sizeof(UPGRADE)-1 + || c != UPGRADE[parser->index]) { + parser->header_state = h_general; + } else if (parser->index == sizeof(UPGRADE)-2) { + parser->header_state = h_upgrade; + } + break; + + case h_connection: + case h_content_length: + case h_transfer_encoding: + case h_upgrade: + if (ch != ' ') parser->header_state = h_general; + break; + + default: + assert(0 && "Unknown header_state"); + break; + } + } + + COUNT_HEADER_SIZE(p - start); + + if (p == data + len) { + --p; + break; + } + + if (ch == ':') { + UPDATE_STATE(s_header_value_discard_ws); + CALLBACK_DATA(header_field); + break; + } + + SET_ERRNO(HPE_INVALID_HEADER_TOKEN); + goto error; + } + + case s_header_value_discard_ws: + if (ch == ' ' || ch == '\t') break; + + if (ch == CR) { + UPDATE_STATE(s_header_value_discard_ws_almost_done); + break; + } + + if (ch == LF) { + UPDATE_STATE(s_header_value_discard_lws); + break; + } + + /* FALLTHROUGH */ + + case s_header_value_start: + { + MARK(header_value); + + UPDATE_STATE(s_header_value); + parser->index = 0; + + c = LOWER(ch); + + switch (parser->header_state) { + case h_upgrade: + parser->flags |= F_UPGRADE; + parser->header_state = h_general; + break; + + case h_transfer_encoding: + /* looking for 'Transfer-Encoding: chunked' */ + if ('c' == c) { + parser->header_state = h_matching_transfer_encoding_chunked; + } else { + parser->header_state = h_general; + } + break; + + case h_content_length: + if (UNLIKELY(!IS_NUM(ch))) { + SET_ERRNO(HPE_INVALID_CONTENT_LENGTH); + goto error; + } + + parser->content_length = ch - '0'; + break; + + case h_connection: + /* looking for 'Connection: keep-alive' */ + if (c == 'k') { + parser->header_state = h_matching_connection_keep_alive; + /* looking for 'Connection: close' */ + } else if (c == 'c') { + parser->header_state = h_matching_connection_close; + } else if (c == 'u') { + parser->header_state = h_matching_connection_upgrade; + } else { + parser->header_state = h_matching_connection_token; + } + break; + + /* Multi-value `Connection` header */ + case h_matching_connection_token_start: + break; + + default: + parser->header_state = h_general; + break; + } + break; + } + + case s_header_value: + { + const char* start = p; + enum header_states h_state = (enum header_states) parser->header_state; + for (; p != data + len; p++) { + ch = *p; + if (ch == CR) { + UPDATE_STATE(s_header_almost_done); + parser->header_state = h_state; + CALLBACK_DATA(header_value); + break; + } + + if (ch == LF) { + UPDATE_STATE(s_header_almost_done); + COUNT_HEADER_SIZE(p - start); + parser->header_state = h_state; + CALLBACK_DATA_NOADVANCE(header_value); + REEXECUTE(); + } + + c = LOWER(ch); + + switch (h_state) { + case h_general: + { + const char* p_cr; + const char* p_lf; + size_t limit = data + len - p; + + limit = MIN(limit, HTTP_MAX_HEADER_SIZE); + + p_cr = (const char*) memchr(p, CR, limit); + p_lf = (const char*) memchr(p, LF, limit); + if (p_cr != NULL) { + if (p_lf != NULL && p_cr >= p_lf) + p = p_lf; + else + p = p_cr; + } else if (UNLIKELY(p_lf != NULL)) { + p = p_lf; + } else { + p = data + len; + } + --p; + + break; + } + + case h_connection: + case h_transfer_encoding: + assert(0 && "Shouldn't get here."); + break; + + case h_content_length: + { + uint64_t t; + + if (ch == ' ') break; + + if (UNLIKELY(!IS_NUM(ch))) { + SET_ERRNO(HPE_INVALID_CONTENT_LENGTH); + parser->header_state = h_state; + goto error; + } + + t = parser->content_length; + t *= 10; + t += ch - '0'; + + /* Overflow? Test against a conservative limit for simplicity. */ + if (UNLIKELY((ULLONG_MAX - 10) / 10 < parser->content_length)) { + SET_ERRNO(HPE_INVALID_CONTENT_LENGTH); + parser->header_state = h_state; + goto error; + } + + parser->content_length = t; + break; + } + + /* Transfer-Encoding: chunked */ + case h_matching_transfer_encoding_chunked: + parser->index++; + if (parser->index > sizeof(CHUNKED)-1 + || c != CHUNKED[parser->index]) { + h_state = h_general; + } else if (parser->index == sizeof(CHUNKED)-2) { + h_state = h_transfer_encoding_chunked; + } + break; + + case h_matching_connection_token_start: + /* looking for 'Connection: keep-alive' */ + if (c == 'k') { + h_state = h_matching_connection_keep_alive; + /* looking for 'Connection: close' */ + } else if (c == 'c') { + h_state = h_matching_connection_close; + } else if (c == 'u') { + h_state = h_matching_connection_upgrade; + } else if (STRICT_TOKEN(c)) { + h_state = h_matching_connection_token; + } else if (c == ' ' || c == '\t') { + /* Skip lws */ + } else { + h_state = h_general; + } + break; + + /* looking for 'Connection: keep-alive' */ + case h_matching_connection_keep_alive: + parser->index++; + if (parser->index > sizeof(KEEP_ALIVE)-1 + || c != KEEP_ALIVE[parser->index]) { + h_state = h_matching_connection_token; + } else if (parser->index == sizeof(KEEP_ALIVE)-2) { + h_state = h_connection_keep_alive; + } + break; + + /* looking for 'Connection: close' */ + case h_matching_connection_close: + parser->index++; + if (parser->index > sizeof(CLOSE)-1 || c != CLOSE[parser->index]) { + h_state = h_matching_connection_token; + } else if (parser->index == sizeof(CLOSE)-2) { + h_state = h_connection_close; + } + break; + + /* looking for 'Connection: upgrade' */ + case h_matching_connection_upgrade: + parser->index++; + if (parser->index > sizeof(UPGRADE) - 1 || + c != UPGRADE[parser->index]) { + h_state = h_matching_connection_token; + } else if (parser->index == sizeof(UPGRADE)-2) { + h_state = h_connection_upgrade; + } + break; + + case h_matching_connection_token: + if (ch == ',') { + h_state = h_matching_connection_token_start; + parser->index = 0; + } + break; + + case h_transfer_encoding_chunked: + if (ch != ' ') h_state = h_general; + break; + + case h_connection_keep_alive: + case h_connection_close: + case h_connection_upgrade: + if (ch == ',') { + if (h_state == h_connection_keep_alive) { + parser->flags |= F_CONNECTION_KEEP_ALIVE; + } else if (h_state == h_connection_close) { + parser->flags |= F_CONNECTION_CLOSE; + } else if (h_state == h_connection_upgrade) { + parser->flags |= F_CONNECTION_UPGRADE; + } + h_state = h_matching_connection_token_start; + parser->index = 0; + } else if (ch != ' ') { + h_state = h_matching_connection_token; + } + break; + + default: + UPDATE_STATE(s_header_value); + h_state = h_general; + break; + } + } + parser->header_state = h_state; + + COUNT_HEADER_SIZE(p - start); + + if (p == data + len) + --p; + break; + } + + case s_header_almost_done: + { + STRICT_CHECK(ch != LF); + + UPDATE_STATE(s_header_value_lws); + break; + } + + case s_header_value_lws: + { + if (ch == ' ' || ch == '\t') { + UPDATE_STATE(s_header_value_start); + REEXECUTE(); + } + + /* finished the header */ + switch (parser->header_state) { + case h_connection_keep_alive: + parser->flags |= F_CONNECTION_KEEP_ALIVE; + break; + case h_connection_close: + parser->flags |= F_CONNECTION_CLOSE; + break; + case h_transfer_encoding_chunked: + parser->flags |= F_CHUNKED; + break; + case h_connection_upgrade: + parser->flags |= F_CONNECTION_UPGRADE; + break; + default: + break; + } + + UPDATE_STATE(s_header_field_start); + REEXECUTE(); + } + + case s_header_value_discard_ws_almost_done: + { + STRICT_CHECK(ch != LF); + UPDATE_STATE(s_header_value_discard_lws); + break; + } + + case s_header_value_discard_lws: + { + if (ch == ' ' || ch == '\t') { + UPDATE_STATE(s_header_value_discard_ws); + break; + } else { + switch (parser->header_state) { + case h_connection_keep_alive: + parser->flags |= F_CONNECTION_KEEP_ALIVE; + break; + case h_connection_close: + parser->flags |= F_CONNECTION_CLOSE; + break; + case h_connection_upgrade: + parser->flags |= F_CONNECTION_UPGRADE; + break; + case h_transfer_encoding_chunked: + parser->flags |= F_CHUNKED; + break; + default: + break; + } + + /* header value was empty */ + MARK(header_value); + UPDATE_STATE(s_header_field_start); + CALLBACK_DATA_NOADVANCE(header_value); + REEXECUTE(); + } + } + + case s_headers_almost_done: + { + STRICT_CHECK(ch != LF); + + if (parser->flags & F_TRAILING) { + /* End of a chunked request */ + UPDATE_STATE(NEW_MESSAGE()); + CALLBACK_NOTIFY(message_complete); + break; + } + + UPDATE_STATE(s_headers_done); + + /* Set this here so that on_headers_complete() callbacks can see it */ + parser->upgrade = + ((parser->flags & (F_UPGRADE | F_CONNECTION_UPGRADE)) == + (F_UPGRADE | F_CONNECTION_UPGRADE) || + parser->method == HTTP_CONNECT); + + /* Here we call the headers_complete callback. This is somewhat + * different than other callbacks because if the user returns 1, we + * will interpret that as saying that this message has no body. This + * is needed for the annoying case of recieving a response to a HEAD + * request. + * + * We'd like to use CALLBACK_NOTIFY_NOADVANCE() here but we cannot, so + * we have to simulate it by handling a change in errno below. + */ + if (settings->on_headers_complete) { + switch (settings->on_headers_complete(parser)) { + case 0: + break; + + case 1: + parser->flags |= F_SKIPBODY; + break; + + default: + SET_ERRNO(HPE_CB_headers_complete); + RETURN(p - data); /* Error */ + } + } + + if (HTTP_PARSER_ERRNO(parser) != HPE_OK) { + RETURN(p - data); + } + + REEXECUTE(); + } + + case s_headers_done: + { + STRICT_CHECK(ch != LF); + + parser->nread = 0; + + /* Exit, the rest of the connect is in a different protocol. */ + if (parser->upgrade) { + UPDATE_STATE(NEW_MESSAGE()); + CALLBACK_NOTIFY(message_complete); + RETURN((p - data) + 1); + } + + if (parser->flags & F_SKIPBODY) { + UPDATE_STATE(NEW_MESSAGE()); + CALLBACK_NOTIFY(message_complete); + } else if (parser->flags & F_CHUNKED) { + /* chunked encoding - ignore Content-Length header */ + UPDATE_STATE(s_chunk_size_start); + } else { + if (parser->content_length == 0) { + /* Content-Length header given but zero: Content-Length: 0\r\n */ + UPDATE_STATE(NEW_MESSAGE()); + CALLBACK_NOTIFY(message_complete); + } else if (parser->content_length != ULLONG_MAX) { + /* Content-Length header given and non-zero */ + UPDATE_STATE(s_body_identity); + } else { + if (parser->type == HTTP_REQUEST || + !http_message_needs_eof(parser)) { + /* Assume content-length 0 - read the next */ + UPDATE_STATE(NEW_MESSAGE()); + CALLBACK_NOTIFY(message_complete); + } else { + /* Read body until EOF */ + UPDATE_STATE(s_body_identity_eof); + } + } + } + + break; + } + + case s_body_identity: + { + uint64_t to_read = MIN(parser->content_length, + (uint64_t) ((data + len) - p)); + + assert(parser->content_length != 0 + && parser->content_length != ULLONG_MAX); + + /* The difference between advancing content_length and p is because + * the latter will automaticaly advance on the next loop iteration. + * Further, if content_length ends up at 0, we want to see the last + * byte again for our message complete callback. + */ + MARK(body); + parser->content_length -= to_read; + p += to_read - 1; + + if (parser->content_length == 0) { + UPDATE_STATE(s_message_done); + + /* Mimic CALLBACK_DATA_NOADVANCE() but with one extra byte. + * + * The alternative to doing this is to wait for the next byte to + * trigger the data callback, just as in every other case. The + * problem with this is that this makes it difficult for the test + * harness to distinguish between complete-on-EOF and + * complete-on-length. It's not clear that this distinction is + * important for applications, but let's keep it for now. + */ + CALLBACK_DATA_(body, p - body_mark + 1, p - data); + REEXECUTE(); + } + + break; + } + + /* read until EOF */ + case s_body_identity_eof: + MARK(body); + p = data + len - 1; + + break; + + case s_message_done: + UPDATE_STATE(NEW_MESSAGE()); + CALLBACK_NOTIFY(message_complete); + break; + + case s_chunk_size_start: + { + assert(parser->nread == 1); + assert(parser->flags & F_CHUNKED); + + unhex_val = unhex[(unsigned char)ch]; + if (UNLIKELY(unhex_val == -1)) { + SET_ERRNO(HPE_INVALID_CHUNK_SIZE); + goto error; + } + + parser->content_length = unhex_val; + UPDATE_STATE(s_chunk_size); + break; + } + + case s_chunk_size: + { + uint64_t t; + + assert(parser->flags & F_CHUNKED); + + if (ch == CR) { + UPDATE_STATE(s_chunk_size_almost_done); + break; + } + + unhex_val = unhex[(unsigned char)ch]; + + if (unhex_val == -1) { + if (ch == ';' || ch == ' ') { + UPDATE_STATE(s_chunk_parameters); + break; + } + + SET_ERRNO(HPE_INVALID_CHUNK_SIZE); + goto error; + } + + t = parser->content_length; + t *= 16; + t += unhex_val; + + /* Overflow? Test against a conservative limit for simplicity. */ + if (UNLIKELY((ULLONG_MAX - 16) / 16 < parser->content_length)) { + SET_ERRNO(HPE_INVALID_CONTENT_LENGTH); + goto error; + } + + parser->content_length = t; + break; + } + + case s_chunk_parameters: + { + assert(parser->flags & F_CHUNKED); + /* just ignore this shit. TODO check for overflow */ + if (ch == CR) { + UPDATE_STATE(s_chunk_size_almost_done); + break; + } + break; + } + + case s_chunk_size_almost_done: + { + assert(parser->flags & F_CHUNKED); + STRICT_CHECK(ch != LF); + + parser->nread = 0; + + if (parser->content_length == 0) { + parser->flags |= F_TRAILING; + UPDATE_STATE(s_header_field_start); + } else { + UPDATE_STATE(s_chunk_data); + } + break; + } + + case s_chunk_data: + { + uint64_t to_read = MIN(parser->content_length, + (uint64_t) ((data + len) - p)); + + assert(parser->flags & F_CHUNKED); + assert(parser->content_length != 0 + && parser->content_length != ULLONG_MAX); + + /* See the explanation in s_body_identity for why the content + * length and data pointers are managed this way. + */ + MARK(body); + parser->content_length -= to_read; + p += to_read - 1; + + if (parser->content_length == 0) { + UPDATE_STATE(s_chunk_data_almost_done); + } + + break; + } + + case s_chunk_data_almost_done: + assert(parser->flags & F_CHUNKED); + assert(parser->content_length == 0); + STRICT_CHECK(ch != CR); + UPDATE_STATE(s_chunk_data_done); + CALLBACK_DATA(body); + break; + + case s_chunk_data_done: + assert(parser->flags & F_CHUNKED); + STRICT_CHECK(ch != LF); + parser->nread = 0; + UPDATE_STATE(s_chunk_size_start); + break; + + default: + assert(0 && "unhandled state"); + SET_ERRNO(HPE_INVALID_INTERNAL_STATE); + goto error; + } + } + + /* Run callbacks for any marks that we have leftover after we ran our of + * bytes. There should be at most one of these set, so it's OK to invoke + * them in series (unset marks will not result in callbacks). + * + * We use the NOADVANCE() variety of callbacks here because 'p' has already + * overflowed 'data' and this allows us to correct for the off-by-one that + * we'd otherwise have (since CALLBACK_DATA() is meant to be run with a 'p' + * value that's in-bounds). + */ + + assert(((header_field_mark ? 1 : 0) + + (header_value_mark ? 1 : 0) + + (url_mark ? 1 : 0) + + (body_mark ? 1 : 0) + + (status_mark ? 1 : 0)) <= 1); + + CALLBACK_DATA_NOADVANCE(header_field); + CALLBACK_DATA_NOADVANCE(header_value); + CALLBACK_DATA_NOADVANCE(url); + CALLBACK_DATA_NOADVANCE(body); + CALLBACK_DATA_NOADVANCE(status); + + RETURN(len); + +error: + if (HTTP_PARSER_ERRNO(parser) == HPE_OK) { + SET_ERRNO(HPE_UNKNOWN); + } + + RETURN(p - data); +} + + +/* Does the parser need to see an EOF to find the end of the message? */ +int +http_message_needs_eof (const http_parser *parser) +{ + if (parser->type == HTTP_REQUEST) { + return 0; + } + + /* See RFC 2616 section 4.4 */ + if (parser->status_code / 100 == 1 || /* 1xx e.g. Continue */ + parser->status_code == 204 || /* No Content */ + parser->status_code == 304 || /* Not Modified */ + parser->flags & F_SKIPBODY) { /* response to a HEAD request */ + return 0; + } + + if ((parser->flags & F_CHUNKED) || parser->content_length != ULLONG_MAX) { + return 0; + } + + return 1; +} + + +int +http_should_keep_alive (const http_parser *parser) +{ + if (parser->http_major > 0 && parser->http_minor > 0) { + /* HTTP/1.1 */ + if (parser->flags & F_CONNECTION_CLOSE) { + return 0; + } + } else { + /* HTTP/1.0 or earlier */ + if (!(parser->flags & F_CONNECTION_KEEP_ALIVE)) { + return 0; + } + } + + return !http_message_needs_eof(parser); +} + + +const char * +http_method_str (enum http_method m) +{ + return ELEM_AT(method_strings, m, ""); +} + + +void +http_parser_init (http_parser *parser, enum http_parser_type t) +{ + void *data = parser->data; /* preserve application data */ + memset(parser, 0, sizeof(*parser)); + parser->data = data; + parser->type = t; + parser->state = (t == HTTP_REQUEST ? s_start_req : (t == HTTP_RESPONSE ? s_start_res : s_start_req_or_res)); + parser->http_errno = HPE_OK; +} + +void +http_parser_settings_init(http_parser_settings *settings) +{ + memset(settings, 0, sizeof(*settings)); +} + +const char * +http_errno_name(enum http_errno err) { + assert(err < (sizeof(http_strerror_tab)/sizeof(http_strerror_tab[0]))); + return http_strerror_tab[err].name; +} + +const char * +http_errno_description(enum http_errno err) { + assert(err < (sizeof(http_strerror_tab)/sizeof(http_strerror_tab[0]))); + return http_strerror_tab[err].description; +} + +static enum http_host_state +http_parse_host_char(enum http_host_state s, const char ch) { + switch(s) { + case s_http_userinfo: + case s_http_userinfo_start: + if (ch == '@') { + return s_http_host_start; + } + + if (IS_USERINFO_CHAR(ch)) { + return s_http_userinfo; + } + break; + + case s_http_host_start: + if (ch == '[') { + return s_http_host_v6_start; + } + + if (IS_HOST_CHAR(ch)) { + return s_http_host; + } + + break; + + case s_http_host: + if (IS_HOST_CHAR(ch)) { + return s_http_host; + } + + /* FALLTHROUGH */ + case s_http_host_v6_end: + if (ch == ':') { + return s_http_host_port_start; + } + + break; + + case s_http_host_v6: + if (ch == ']') { + return s_http_host_v6_end; + } + + /* FALLTHROUGH */ + case s_http_host_v6_start: + if (IS_HEX(ch) || ch == ':' || ch == '.') { + return s_http_host_v6; + } + + break; + + case s_http_host_port: + case s_http_host_port_start: + if (IS_NUM(ch)) { + return s_http_host_port; + } + + break; + + default: + break; + } + return s_http_host_dead; +} + +static int +http_parse_host(const char * buf, struct http_parser_url *u, int found_at) { + enum http_host_state s; + + const char *p; + size_t buflen = u->field_data[UF_HOST].off + u->field_data[UF_HOST].len; + + u->field_data[UF_HOST].len = 0; + + s = found_at ? s_http_userinfo_start : s_http_host_start; + + for (p = buf + u->field_data[UF_HOST].off; p < buf + buflen; p++) { + enum http_host_state new_s = http_parse_host_char(s, *p); + + if (new_s == s_http_host_dead) { + return 1; + } + + switch(new_s) { + case s_http_host: + if (s != s_http_host) { + u->field_data[UF_HOST].off = p - buf; + } + u->field_data[UF_HOST].len++; + break; + + case s_http_host_v6: + if (s != s_http_host_v6) { + u->field_data[UF_HOST].off = p - buf; + } + u->field_data[UF_HOST].len++; + break; + + case s_http_host_port: + if (s != s_http_host_port) { + u->field_data[UF_PORT].off = p - buf; + u->field_data[UF_PORT].len = 0; + u->field_set |= (1 << UF_PORT); + } + u->field_data[UF_PORT].len++; + break; + + case s_http_userinfo: + if (s != s_http_userinfo) { + u->field_data[UF_USERINFO].off = p - buf ; + u->field_data[UF_USERINFO].len = 0; + u->field_set |= (1 << UF_USERINFO); + } + u->field_data[UF_USERINFO].len++; + break; + + default: + break; + } + s = new_s; + } + + /* Make sure we don't end somewhere unexpected */ + switch (s) { + case s_http_host_start: + case s_http_host_v6_start: + case s_http_host_v6: + case s_http_host_port_start: + case s_http_userinfo: + case s_http_userinfo_start: + return 1; + default: + break; + } + + return 0; +} + +int +http_parser_parse_url(const char *buf, size_t buflen, int is_connect, + struct http_parser_url *u) +{ + enum state s; + const char *p; + enum http_parser_url_fields uf, old_uf; + int found_at = 0; + + u->port = u->field_set = 0; + s = is_connect ? s_req_server_start : s_req_spaces_before_url; + old_uf = UF_MAX; + + for (p = buf; p < buf + buflen; p++) { + s = parse_url_char(s, *p); + + /* Figure out the next field that we're operating on */ + switch (s) { + case s_dead: + return 1; + + /* Skip delimeters */ + case s_req_schema_slash: + case s_req_schema_slash_slash: + case s_req_server_start: + case s_req_query_string_start: + case s_req_fragment_start: + continue; + + case s_req_schema: + uf = UF_SCHEMA; + break; + + case s_req_server_with_at: + found_at = 1; + + /* FALLTROUGH */ + case s_req_server: + uf = UF_HOST; + break; + + case s_req_path: + uf = UF_PATH; + break; + + case s_req_query_string: + uf = UF_QUERY; + break; + + case s_req_fragment: + uf = UF_FRAGMENT; + break; + + default: + assert(!"Unexpected state"); + return 1; + } + + /* Nothing's changed; soldier on */ + if (uf == old_uf) { + u->field_data[uf].len++; + continue; + } + + u->field_data[uf].off = p - buf; + u->field_data[uf].len = 1; + + u->field_set |= (1 << uf); + old_uf = uf; + } + + /* host must be present if there is a schema */ + /* parsing http:///toto will fail */ + if ((u->field_set & ((1 << UF_SCHEMA) | (1 << UF_HOST))) != 0) { + if (http_parse_host(buf, u, found_at) != 0) { + return 1; + } + } + + /* CONNECT requests can only contain "hostname:port" */ + if (is_connect && u->field_set != ((1 << UF_HOST)|(1 << UF_PORT))) { + return 1; + } + + if (u->field_set & (1 << UF_PORT)) { + /* Don't bother with endp; we've already validated the string */ + unsigned long v = strtoul(buf + u->field_data[UF_PORT].off, NULL, 10); + + /* Ports have a max value of 2^16 */ + if (v > 0xffff) { + return 1; + } + + u->port = (uint16_t) v; + } + + return 0; +} + +void +http_parser_pause(http_parser *parser, int paused) { + /* Users should only be pausing/unpausing a parser that is not in an error + * state. In non-debug builds, there's not much that we can do about this + * other than ignore it. + */ + if (HTTP_PARSER_ERRNO(parser) == HPE_OK || + HTTP_PARSER_ERRNO(parser) == HPE_PAUSED) { + SET_ERRNO((paused) ? HPE_PAUSED : HPE_OK); + } else { + assert(0 && "Attempting to pause parser in error state"); + } +} + +int +http_body_is_final(const struct http_parser *parser) { + return parser->state == s_message_done; +} + +unsigned long +http_parser_version(void) { + return HTTP_PARSER_VERSION_MAJOR * 0x10000 | + HTTP_PARSER_VERSION_MINOR * 0x00100 | + HTTP_PARSER_VERSION_PATCH * 0x00001; +} diff --git a/third-party/http-parser/http_parser.h b/third-party/http-parser/http_parser.h new file mode 100644 index 0000000..99c533a --- /dev/null +++ b/third-party/http-parser/http_parser.h @@ -0,0 +1,335 @@ +/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +#ifndef http_parser_h +#define http_parser_h +#ifdef __cplusplus +extern "C" { +#endif + +/* Also update SONAME in the Makefile whenever you change these. */ +#define HTTP_PARSER_VERSION_MAJOR 2 +#define HTTP_PARSER_VERSION_MINOR 4 +#define HTTP_PARSER_VERSION_PATCH 2 + +#include +#if defined(_WIN32) && !defined(__MINGW32__) && (!defined(_MSC_VER) || _MSC_VER<1600) +#include +#include +typedef __int8 int8_t; +typedef unsigned __int8 uint8_t; +typedef __int16 int16_t; +typedef unsigned __int16 uint16_t; +typedef __int32 int32_t; +typedef unsigned __int32 uint32_t; +typedef __int64 int64_t; +typedef unsigned __int64 uint64_t; +#else +#include +#endif + +/* Compile with -DHTTP_PARSER_STRICT=0 to make less checks, but run + * faster + */ +#ifndef HTTP_PARSER_STRICT +# define HTTP_PARSER_STRICT 1 +#endif + +/* Maximium header size allowed. If the macro is not defined + * before including this header then the default is used. To + * change the maximum header size, define the macro in the build + * environment (e.g. -DHTTP_MAX_HEADER_SIZE=). To remove + * the effective limit on the size of the header, define the macro + * to a very large number (e.g. -DHTTP_MAX_HEADER_SIZE=0x7fffffff) + */ +#ifndef HTTP_MAX_HEADER_SIZE +# define HTTP_MAX_HEADER_SIZE (80*1024) +#endif + +typedef struct http_parser http_parser; +typedef struct http_parser_settings http_parser_settings; + + +/* Callbacks should return non-zero to indicate an error. The parser will + * then halt execution. + * + * The one exception is on_headers_complete. In a HTTP_RESPONSE parser + * returning '1' from on_headers_complete will tell the parser that it + * should not expect a body. This is used when receiving a response to a + * HEAD request which may contain 'Content-Length' or 'Transfer-Encoding: + * chunked' headers that indicate the presence of a body. + * + * http_data_cb does not return data chunks. It will be called arbitrarily + * many times for each string. E.G. you might get 10 callbacks for "on_url" + * each providing just a few characters more data. + */ +typedef int (*http_data_cb) (http_parser*, const char *at, size_t length); +typedef int (*http_cb) (http_parser*); + + +/* Request Methods */ +#define HTTP_METHOD_MAP(XX) \ + XX(0, DELETE, DELETE) \ + XX(1, GET, GET) \ + XX(2, HEAD, HEAD) \ + XX(3, POST, POST) \ + XX(4, PUT, PUT) \ + /* pathological */ \ + XX(5, CONNECT, CONNECT) \ + XX(6, OPTIONS, OPTIONS) \ + XX(7, TRACE, TRACE) \ + /* webdav */ \ + XX(8, COPY, COPY) \ + XX(9, LOCK, LOCK) \ + XX(10, MKCOL, MKCOL) \ + XX(11, MOVE, MOVE) \ + XX(12, PROPFIND, PROPFIND) \ + XX(13, PROPPATCH, PROPPATCH) \ + XX(14, SEARCH, SEARCH) \ + XX(15, UNLOCK, UNLOCK) \ + /* subversion */ \ + XX(16, REPORT, REPORT) \ + XX(17, MKACTIVITY, MKACTIVITY) \ + XX(18, CHECKOUT, CHECKOUT) \ + XX(19, MERGE, MERGE) \ + /* upnp */ \ + XX(20, MSEARCH, M-SEARCH) \ + XX(21, NOTIFY, NOTIFY) \ + XX(22, SUBSCRIBE, SUBSCRIBE) \ + XX(23, UNSUBSCRIBE, UNSUBSCRIBE) \ + /* RFC-5789 */ \ + XX(24, PATCH, PATCH) \ + XX(25, PURGE, PURGE) \ + /* CalDAV */ \ + XX(26, MKCALENDAR, MKCALENDAR) \ + +enum http_method + { +#define XX(num, name, string) HTTP_##name = num, + HTTP_METHOD_MAP(XX) +#undef XX + }; + + +enum http_parser_type { HTTP_REQUEST, HTTP_RESPONSE, HTTP_BOTH }; + + +/* Flag values for http_parser.flags field */ +enum flags + { F_CHUNKED = 1 << 0 + , F_CONNECTION_KEEP_ALIVE = 1 << 1 + , F_CONNECTION_CLOSE = 1 << 2 + , F_CONNECTION_UPGRADE = 1 << 3 + , F_TRAILING = 1 << 4 + , F_UPGRADE = 1 << 5 + , F_SKIPBODY = 1 << 6 + }; + + +/* Map for errno-related constants + * + * The provided argument should be a macro that takes 2 arguments. + */ +#define HTTP_ERRNO_MAP(XX) \ + /* No error */ \ + XX(OK, "success") \ + \ + /* Callback-related errors */ \ + XX(CB_message_begin, "the on_message_begin callback failed") \ + XX(CB_url, "the on_url callback failed") \ + XX(CB_header_field, "the on_header_field callback failed") \ + XX(CB_header_value, "the on_header_value callback failed") \ + XX(CB_headers_complete, "the on_headers_complete callback failed") \ + XX(CB_body, "the on_body callback failed") \ + XX(CB_message_complete, "the on_message_complete callback failed") \ + XX(CB_status, "the on_status callback failed") \ + \ + /* Parsing-related errors */ \ + XX(INVALID_EOF_STATE, "stream ended at an unexpected time") \ + XX(HEADER_OVERFLOW, \ + "too many header bytes seen; overflow detected") \ + XX(CLOSED_CONNECTION, \ + "data received after completed connection: close message") \ + XX(INVALID_VERSION, "invalid HTTP version") \ + XX(INVALID_STATUS, "invalid HTTP status code") \ + XX(INVALID_METHOD, "invalid HTTP method") \ + XX(INVALID_URL, "invalid URL") \ + XX(INVALID_HOST, "invalid host") \ + XX(INVALID_PORT, "invalid port") \ + XX(INVALID_PATH, "invalid path") \ + XX(INVALID_QUERY_STRING, "invalid query string") \ + XX(INVALID_FRAGMENT, "invalid fragment") \ + XX(LF_EXPECTED, "LF character expected") \ + XX(INVALID_HEADER_TOKEN, "invalid character in header") \ + XX(INVALID_CONTENT_LENGTH, \ + "invalid character in content-length header") \ + XX(INVALID_CHUNK_SIZE, \ + "invalid character in chunk size header") \ + XX(INVALID_CONSTANT, "invalid constant string") \ + XX(INVALID_INTERNAL_STATE, "encountered unexpected internal state")\ + XX(STRICT, "strict mode assertion failed") \ + XX(PAUSED, "parser is paused") \ + XX(UNKNOWN, "an unknown error occurred") + + +/* Define HPE_* values for each errno value above */ +#define HTTP_ERRNO_GEN(n, s) HPE_##n, +enum http_errno { + HTTP_ERRNO_MAP(HTTP_ERRNO_GEN) +}; +#undef HTTP_ERRNO_GEN + + +/* Get an http_errno value from an http_parser */ +#define HTTP_PARSER_ERRNO(p) ((enum http_errno) (p)->http_errno) + + +struct http_parser { + /** PRIVATE **/ + unsigned int type : 2; /* enum http_parser_type */ + unsigned int flags : 7; /* F_* values from 'flags' enum; semi-public */ + unsigned int state : 7; /* enum state from http_parser.c */ + unsigned int header_state : 8; /* enum header_state from http_parser.c */ + unsigned int index : 8; /* index into current matcher */ + + uint32_t nread; /* # bytes read in various scenarios */ + uint64_t content_length; /* # bytes in body (0 if no Content-Length header) */ + + /** READ-ONLY **/ + unsigned short http_major; + unsigned short http_minor; + unsigned int status_code : 16; /* responses only */ + unsigned int method : 8; /* requests only */ + unsigned int http_errno : 7; + + /* 1 = Upgrade header was present and the parser has exited because of that. + * 0 = No upgrade header present. + * Should be checked when http_parser_execute() returns in addition to + * error checking. + */ + unsigned int upgrade : 1; + + /** PUBLIC **/ + void *data; /* A pointer to get hook to the "connection" or "socket" object */ +}; + + +struct http_parser_settings { + http_cb on_message_begin; + http_data_cb on_url; + http_data_cb on_status; + http_data_cb on_header_field; + http_data_cb on_header_value; + http_cb on_headers_complete; + http_data_cb on_body; + http_cb on_message_complete; +}; + + +enum http_parser_url_fields + { UF_SCHEMA = 0 + , UF_HOST = 1 + , UF_PORT = 2 + , UF_PATH = 3 + , UF_QUERY = 4 + , UF_FRAGMENT = 5 + , UF_USERINFO = 6 + , UF_MAX = 7 + }; + + +/* Result structure for http_parser_parse_url(). + * + * Callers should index into field_data[] with UF_* values iff field_set + * has the relevant (1 << UF_*) bit set. As a courtesy to clients (and + * because we probably have padding left over), we convert any port to + * a uint16_t. + */ +struct http_parser_url { + uint16_t field_set; /* Bitmask of (1 << UF_*) values */ + uint16_t port; /* Converted UF_PORT string */ + + struct { + uint16_t off; /* Offset into buffer in which field starts */ + uint16_t len; /* Length of run in buffer */ + } field_data[UF_MAX]; +}; + + +/* Returns the library version. Bits 16-23 contain the major version number, + * bits 8-15 the minor version number and bits 0-7 the patch level. + * Usage example: + * + * unsigned long version = http_parser_version(); + * unsigned major = (version >> 16) & 255; + * unsigned minor = (version >> 8) & 255; + * unsigned patch = version & 255; + * printf("http_parser v%u.%u.%u\n", major, minor, patch); + */ +unsigned long http_parser_version(void); + +void http_parser_init(http_parser *parser, enum http_parser_type type); + + +/* Initialize http_parser_settings members to 0 + */ +void http_parser_settings_init(http_parser_settings *settings); + + +/* Executes the parser. Returns number of parsed bytes. Sets + * `parser->http_errno` on error. */ +size_t http_parser_execute(http_parser *parser, + const http_parser_settings *settings, + const char *data, + size_t len); + + +/* If http_should_keep_alive() in the on_headers_complete or + * on_message_complete callback returns 0, then this should be + * the last message on the connection. + * If you are the server, respond with the "Connection: close" header. + * If you are the client, close the connection. + */ +int http_should_keep_alive(const http_parser *parser); + +/* Returns a string version of the HTTP method. */ +const char *http_method_str(enum http_method m); + +/* Return a string name of the given error */ +const char *http_errno_name(enum http_errno err); + +/* Return a string description of the given error */ +const char *http_errno_description(enum http_errno err); + +/* Parse a URL; return nonzero on failure */ +int http_parser_parse_url(const char *buf, size_t buflen, + int is_connect, + struct http_parser_url *u); + +/* Pause or un-pause the parser; a nonzero value pauses */ +void http_parser_pause(http_parser *parser, int paused); + +/* Checks if this is the final chunk of the body. */ +int http_body_is_final(const http_parser *parser); + +#ifdef __cplusplus +} +#endif +#endif -- 2.7.4 From fa350018df1beba69b061141ae48922b60e4ca2f Mon Sep 17 00:00:00 2001 From: cheoleun moon Date: Tue, 23 Aug 2016 01:59:18 -0700 Subject: [PATCH 3/4] Revert "Imported Upstream version 1.0.0" This reverts commit 4f1954eef4f343820dd67933b4f392ebd5cb3b3f. Change-Id: Ia414cda1c72b20fb5cb02c023cce299d788db403 --- AUTHORS | 1 - COPYING | 22 - ChangeLog | 259 - Dockerfile.android | 109 - INSTALL | 370 - Makefile.am | 43 - Makefile.in | 964 - NEWS | 0 README | 1 - README.rst | 1427 -- aclocal.m4 | 1613 -- android-config | 47 - android-make | 33 - compile | 347 - config.guess | 1420 -- config.h.in | 379 - config.sub | 1799 -- configure | 25483 ------------------- configure.ac | 758 - contrib/Makefile.am | 44 - contrib/Makefile.in | 531 - contrib/nghttpx-init.in | 173 - contrib/nghttpx-logrotate | 11 - contrib/nghttpx-upstart.conf.in | 8 - contrib/nghttpx.service.in | 10 - depcomp | 791 - doc/Makefile.am | 320 - doc/Makefile.in | 939 - doc/README.rst | 160 - doc/_themes/sphinx_rtd_theme/__init__.py | 17 - doc/_themes/sphinx_rtd_theme/breadcrumbs.html | 23 - doc/_themes/sphinx_rtd_theme/footer.html | 36 - doc/_themes/sphinx_rtd_theme/layout.html | 181 - doc/_themes/sphinx_rtd_theme/layout_old.html | 205 - doc/_themes/sphinx_rtd_theme/search.html | 50 - doc/_themes/sphinx_rtd_theme/searchbox.html | 9 - .../sphinx_rtd_theme/static/css/badge_only.css | 2 - doc/_themes/sphinx_rtd_theme/static/css/theme.css | 5 - .../sphinx_rtd_theme/static/fonts/FontAwesome.otf | Bin 62856 -> 0 bytes .../static/fonts/fontawesome-webfont.eot | Bin 38205 -> 0 bytes .../static/fonts/fontawesome-webfont.svg | 414 - .../static/fonts/fontawesome-webfont.ttf | Bin 80652 -> 0 bytes .../static/fonts/fontawesome-webfont.woff | Bin 44432 -> 0 bytes doc/_themes/sphinx_rtd_theme/static/js/theme.js | 113 - doc/_themes/sphinx_rtd_theme/theme.conf | 9 - doc/_themes/sphinx_rtd_theme/versions.html | 37 - doc/apiref.rst | 104 - doc/asio_http2.h.rst.in | 5 - doc/asio_http2_client.h.rst.in | 5 - doc/asio_http2_server.h.rst.in | 5 - doc/bash_completion/h2load | 19 - doc/bash_completion/nghttp | 19 - doc/bash_completion/nghttpd | 19 - doc/bash_completion/nghttpx | 19 - doc/building-android-binary.rst.in | 1 - doc/conf.py.in | 253 - doc/contribute.rst.in | 1 - doc/enums.rst | 446 - doc/h2load-howto.rst.in | 1 - doc/h2load.1 | 273 - doc/h2load.1.rst | 204 - doc/index.rst.in | 1 - doc/libnghttp2_asio.rst.in | 1 - doc/macros.rst | 86 - doc/mkapiref.py | 277 - doc/nghttp.1 | 289 - doc/nghttp.1.rst | 233 - doc/nghttp2.h.rst.in | 4 - doc/nghttp2_check_header_name.rst | 17 - doc/nghttp2_check_header_value.rst | 15 - doc/nghttp2_hd_deflate_bound.rst | 14 - doc/nghttp2_hd_deflate_change_table_size.rst | 31 - doc/nghttp2_hd_deflate_del.rst | 13 - doc/nghttp2_hd_deflate_hd.rst | 35 - doc/nghttp2_hd_deflate_new.rst | 24 - doc/nghttp2_hd_deflate_new2.rst | 23 - doc/nghttp2_hd_inflate_change_table_size.rst | 23 - doc/nghttp2_hd_inflate_del.rst | 13 - doc/nghttp2_hd_inflate_end_headers.rst | 16 - doc/nghttp2_hd_inflate_hd.rst | 85 - doc/nghttp2_hd_inflate_new.rst | 21 - doc/nghttp2_hd_inflate_new2.rst | 23 - doc/nghttp2_is_fatal.rst | 14 - doc/nghttp2_nv_compare_name.rst | 17 - doc/nghttp2_option_del.rst | 14 - doc/nghttp2_option_new.rst | 22 - doc/nghttp2_option_set_no_auto_window_update.rst | 18 - doc/nghttp2_option_set_no_http_messaging.rst | 18 - doc/nghttp2_option_set_no_recv_client_magic.rst | 26 - ...ttp2_option_set_peer_max_concurrent_streams.rst | 23 - doc/nghttp2_pack_settings_payload.rst | 29 - doc/nghttp2_priority_spec_check_default.rst | 13 - doc/nghttp2_priority_spec_default_init.rst | 15 - doc/nghttp2_priority_spec_init.rst | 18 - doc/nghttp2_select_next_protocol.rst | 66 - doc/nghttp2_session_callbacks_del.rst | 14 - doc/nghttp2_session_callbacks_new.rst | 25 - ...on_callbacks_set_before_frame_send_callback.rst | 13 - ...lbacks_set_data_source_read_length_callback.rst | 14 - ...ssion_callbacks_set_on_begin_frame_callback.rst | 13 - ...ion_callbacks_set_on_begin_headers_callback.rst | 14 - ...n_callbacks_set_on_data_chunk_recv_callback.rst | 14 - ...on_callbacks_set_on_frame_not_send_callback.rst | 14 - ...ession_callbacks_set_on_frame_recv_callback.rst | 14 - ...ession_callbacks_set_on_frame_send_callback.rst | 13 - ...p2_session_callbacks_set_on_header_callback.rst | 14 - ...allbacks_set_on_invalid_frame_recv_callback.rst | 15 - ...sion_callbacks_set_on_stream_close_callback.rst | 13 - ...nghttp2_session_callbacks_set_recv_callback.rst | 16 - ...ssion_callbacks_set_select_padding_callback.rst | 15 - ...nghttp2_session_callbacks_set_send_callback.rst | 16 - ...p2_session_callbacks_set_send_data_callback.rst | 15 - doc/nghttp2_session_client_new.rst | 29 - doc/nghttp2_session_client_new2.rst | 29 - doc/nghttp2_session_client_new3.rst | 29 - doc/nghttp2_session_consume.rst | 31 - doc/nghttp2_session_consume_connection.rst | 24 - doc/nghttp2_session_consume_stream.rst | 26 - doc/nghttp2_session_del.rst | 14 - ...tp2_session_get_effective_local_window_size.rst | 18 - ...ttp2_session_get_effective_recv_data_length.rst | 22 - doc/nghttp2_session_get_last_proc_stream_id.rst | 19 - doc/nghttp2_session_get_next_stream_id.rst | 15 - doc/nghttp2_session_get_outbound_queue_size.rst | 14 - doc/nghttp2_session_get_remote_settings.rst | 15 - doc/nghttp2_session_get_remote_window_size.rst | 15 - ...sion_get_stream_effective_local_window_size.rst | 18 - ...ssion_get_stream_effective_recv_data_length.rst | 22 - doc/nghttp2_session_get_stream_local_close.rst | 14 - doc/nghttp2_session_get_stream_remote_close.rst | 14 - ...http2_session_get_stream_remote_window_size.rst | 22 - doc/nghttp2_session_get_stream_user_data.rst | 20 - doc/nghttp2_session_mem_recv.rst | 42 - doc/nghttp2_session_mem_send.rst | 38 - doc/nghttp2_session_recv.rst | 72 - doc/nghttp2_session_resume_data.rst | 22 - doc/nghttp2_session_send.rst | 55 - doc/nghttp2_session_server_new.rst | 29 - doc/nghttp2_session_server_new2.rst | 29 - doc/nghttp2_session_server_new3.rst | 29 - doc/nghttp2_session_set_next_stream_id.rst | 24 - doc/nghttp2_session_set_stream_user_data.rst | 26 - doc/nghttp2_session_terminate_session.rst | 34 - doc/nghttp2_session_terminate_session2.rst | 34 - doc/nghttp2_session_upgrade.rst | 44 - doc/nghttp2_session_want_read.rst | 18 - doc/nghttp2_session_want_write.rst | 18 - doc/nghttp2_strerror.rst | 14 - doc/nghttp2_submit_data.rst | 42 - doc/nghttp2_submit_goaway.rst | 53 - doc/nghttp2_submit_headers.rst | 80 - doc/nghttp2_submit_ping.rst | 29 - doc/nghttp2_submit_priority.rst | 37 - doc/nghttp2_submit_push_promise.rst | 65 - doc/nghttp2_submit_request.rst | 68 - doc/nghttp2_submit_response.rst | 57 - doc/nghttp2_submit_rst_stream.rst | 27 - doc/nghttp2_submit_settings.rst | 41 - doc/nghttp2_submit_shutdown_notice.rst | 43 - doc/nghttp2_submit_trailer.rst | 52 - doc/nghttp2_submit_window_update.rst | 43 - doc/nghttp2_version.rst | 17 - doc/nghttp2ver.h.rst.in | 4 - doc/nghttpd.1 | 194 - doc/nghttpd.1.rst | 151 - doc/nghttpx-howto.rst.in | 1 - doc/nghttpx.1 | 899 - doc/nghttpx.1.rst | 811 - doc/package_README.rst.in | 1 - doc/programmers-guide.rst | 105 - doc/python-apiref.rst.in | 1 - doc/sources/building-android-binary.rst | 135 - doc/sources/contribute.rst | 57 - doc/sources/h2load-howto.rst | 91 - doc/sources/index.rst | 53 - doc/sources/libnghttp2_asio.rst | 433 - doc/sources/nghttpx-howto.rst | 303 - doc/sources/python-apiref.rst | 437 - doc/sources/tutorial-client.rst | 439 - doc/sources/tutorial-hpack.rst | 118 - doc/sources/tutorial-server.rst | 550 - doc/tutorial-client.rst.in | 6 - doc/tutorial-hpack.rst.in | 6 - doc/tutorial-server.rst.in | 6 - doc/types.rst | 914 - examples/Makefile.am | 95 - examples/Makefile.in | 926 - examples/asio-cl.cc | 96 - examples/asio-cl2.cc | 134 - examples/asio-sv.cc | 149 - examples/asio-sv2.cc | 125 - examples/client.c | 703 - examples/deflate.c | 206 - examples/libevent-client.c | 561 - examples/libevent-server.c | 734 - examples/tiny-nghttpd.c | 1342 - install-sh | 527 - integration-tests/Makefile.am | 43 - integration-tests/Makefile.in | 537 - integration-tests/alt-server.crt | 21 - integration-tests/alt-server.key | 28 - integration-tests/config.go.in | 5 - integration-tests/nghttpx_http1_test.go | 506 - integration-tests/nghttpx_http2_test.go | 813 - integration-tests/nghttpx_spdy_test.go | 232 - integration-tests/server.crt | 21 - integration-tests/server.key | 28 - integration-tests/server_tester.go | 683 - integration-tests/setenv | 6 - integration-tests/setenv.in | 6 - lib/Makefile.am | 67 - lib/Makefile.in | 930 - lib/Makefile.msvc | 249 - lib/includes/Makefile.am | 23 - lib/includes/Makefile.in | 636 - lib/includes/nghttp2/nghttp2.h | 3860 --- lib/includes/nghttp2/nghttp2ver.h | 42 - lib/includes/nghttp2/nghttp2ver.h.in | 42 - lib/libnghttp2.pc.in | 33 - lib/nghttp2_buf.c | 494 - lib/nghttp2_buf.h | 385 - lib/nghttp2_callbacks.c | 129 - lib/nghttp2_callbacks.h | 112 - lib/nghttp2_frame.c | 888 - lib/nghttp2_frame.h | 527 - lib/nghttp2_hd.c | 2343 -- lib/nghttp2_hd.h | 421 - lib/nghttp2_hd_huffman.c | 227 - lib/nghttp2_hd_huffman.h | 74 - lib/nghttp2_hd_huffman_data.c | 5152 ---- lib/nghttp2_helper.c | 463 - lib/nghttp2_helper.h | 114 - lib/nghttp2_http.c | 459 - lib/nghttp2_http.h | 98 - lib/nghttp2_int.h | 58 - lib/nghttp2_map.c | 190 - lib/nghttp2_map.h | 144 - lib/nghttp2_mem.c | 61 - lib/nghttp2_mem.h | 44 - lib/nghttp2_net.h | 91 - lib/nghttp2_npn.c | 57 - lib/nghttp2_npn.h | 34 - lib/nghttp2_option.c | 58 - lib/nghttp2_option.h | 91 - lib/nghttp2_outbound_item.c | 105 - lib/nghttp2_outbound_item.h | 157 - lib/nghttp2_pq.c | 146 - lib/nghttp2_pq.h | 121 - lib/nghttp2_priority_spec.c | 44 - lib/nghttp2_priority_spec.h | 34 - lib/nghttp2_queue.c | 85 - lib/nghttp2_queue.h | 49 - lib/nghttp2_session.c | 6612 ----- lib/nghttp2_session.h | 762 - lib/nghttp2_stream.c | 1010 - lib/nghttp2_stream.h | 486 - lib/nghttp2_submit.c | 479 - lib/nghttp2_submit.h | 34 - lib/nghttp2_version.c | 38 - ltmain.sh | 9661 ------- m4/ax_boost_asio.m4 | 110 - m4/ax_boost_base.m4 | 275 - m4/ax_boost_system.m4 | 120 - m4/ax_boost_thread.m4 | 149 - m4/ax_check_compile_flag.m4 | 74 - m4/ax_cxx_compile_stdcxx_11.m4 | 133 - m4/ax_have_epoll.m4 | 104 - m4/ax_python_devel.m4 | 344 - m4/libtool.m4 | 7997 ------ m4/libxml2.m4 | 188 - m4/ltoptions.m4 | 384 - m4/ltsugar.m4 | 123 - m4/ltversion.m4 | 23 - m4/lt~obsolete.m4 | 98 - missing | 215 - nghttpx.conf.sample | 29 - proxy.pac.sample | 6 - python/Makefile.am | 48 - python/Makefile.in | 541 - python/cnghttp2.pxd | 345 - python/nghttp2.pyx | 1591 -- python/setup.py.in | 49 - src/HtmlParser.cc | 187 - src/HtmlParser.h | 94 - src/HttpServer.cc | 1889 -- src/HttpServer.h | 202 - src/Makefile.am | 216 - src/Makefile.in | 2137 -- src/app_helper.cc | 505 - src/app_helper.h | 95 - src/asio_client_request.cc | 67 - src/asio_client_request_impl.cc | 110 - src/asio_client_request_impl.h | 93 - src/asio_client_response.cc | 53 - src/asio_client_response_impl.cc | 57 - src/asio_client_response_impl.h | 69 - src/asio_client_session.cc | 98 - src/asio_client_session_impl.cc | 625 - src/asio_client_session_impl.h | 123 - src/asio_client_session_tcp_impl.cc | 69 - src/asio_client_session_tcp_impl.h | 60 - src/asio_client_session_tls_impl.cc | 85 - src/asio_client_session_tls_impl.h | 63 - src/asio_client_stream.cc | 59 - src/asio_client_stream.h | 68 - src/asio_client_tls_context.cc | 64 - src/asio_client_tls_context.h | 32 - src/asio_common.cc | 148 - src/asio_common.h | 65 - src/asio_io_service_pool.cc | 97 - src/asio_io_service_pool.h | 91 - src/asio_server.cc | 165 - src/asio_server.h | 105 - src/asio_server_connection.h | 169 - src/asio_server_http2.cc | 84 - src/asio_server_http2_handler.cc | 462 - src/asio_server_http2_handler.h | 167 - src/asio_server_http2_impl.cc | 66 - src/asio_server_http2_impl.h | 67 - src/asio_server_request.cc | 55 - src/asio_server_request_handler.cc | 84 - src/asio_server_request_handler.h | 32 - src/asio_server_request_impl.cc | 59 - src/asio_server_request_impl.h | 69 - src/asio_server_response.cc | 75 - src/asio_server_response_impl.cc | 163 - src/asio_server_response_impl.h | 92 - src/asio_server_serve_mux.cc | 138 - src/asio_server_serve_mux.h | 64 - src/asio_server_stream.cc | 55 - src/asio_server_stream.h | 59 - src/asio_server_tls_context.cc | 85 - src/asio_server_tls_context.h | 32 - src/base64.h | 170 - src/buffer.h | 68 - src/buffer_test.cc | 78 - src/buffer_test.h | 38 - src/comp_helper.c | 91 - src/comp_helper.h | 47 - src/deflatehd.cc | 450 - src/h2load.cc | 1528 -- src/h2load.h | 249 - src/h2load_http2_session.cc | 260 - src/h2load_http2_session.h | 53 - src/h2load_session.h | 57 - src/h2load_spdy_session.cc | 270 - src/h2load_spdy_session.h | 57 - src/http2.cc | 1118 - src/http2.h | 296 - src/http2_test.cc | 826 - src/http2_test.h | 51 - src/includes/Makefile.am | 27 - src/includes/Makefile.in | 640 - src/includes/nghttp2/asio_http2.h | 139 - src/includes/nghttp2/asio_http2_client.h | 195 - src/includes/nghttp2/asio_http2_server.h | 229 - src/inflatehd.cc | 277 - src/libnghttp2_asio.pc.in | 33 - src/memchunk.h | 260 - src/memchunk_test.cc | 199 - src/memchunk_test.h | 42 - src/nghttp.cc | 2652 -- src/nghttp.h | 272 - src/nghttp2_config.h | 38 - src/nghttp2_gzip.c | 92 - src/nghttp2_gzip.h | 122 - src/nghttp2_gzip_test.c | 115 - src/nghttp2_gzip_test.h | 42 - src/nghttpd.cc | 381 - src/shrpx-unittest.cc | 178 - src/shrpx.cc | 2165 -- src/shrpx.h | 49 - src/shrpx_accept_handler.cc | 114 - src/shrpx_accept_handler.h | 53 - src/shrpx_client_handler.cc | 793 - src/shrpx_client_handler.h | 154 - src/shrpx_config.cc | 1406 - src/shrpx_config.h | 409 - src/shrpx_config_test.cc | 175 - src/shrpx_config_test.h | 41 - src/shrpx_connect_blocker.cc | 65 - src/shrpx_connect_blocker.h | 56 - src/shrpx_connection.cc | 333 - src/shrpx_connection.h | 109 - src/shrpx_connection_handler.cc | 539 - src/shrpx_connection_handler.h | 138 - src/shrpx_downstream.cc | 1218 - src/shrpx_downstream.h | 456 - src/shrpx_downstream_connection.cc | 52 - src/shrpx_downstream_connection.h | 77 - src/shrpx_downstream_connection_pool.cc | 60 - src/shrpx_downstream_connection_pool.h | 52 - src/shrpx_downstream_queue.cc | 167 - src/shrpx_downstream_queue.h | 105 - src/shrpx_downstream_test.cc | 160 - src/shrpx_downstream_test.h | 44 - src/shrpx_error.h | 43 - src/shrpx_http.cc | 103 - src/shrpx_http.h | 53 - src/shrpx_http2_downstream_connection.cc | 569 - src/shrpx_http2_downstream_connection.h | 86 - src/shrpx_http2_session.cc | 1747 -- src/shrpx_http2_session.h | 219 - src/shrpx_http2_upstream.cc | 1661 -- src/shrpx_http2_upstream.h | 121 - src/shrpx_http_downstream_connection.cc | 844 - src/shrpx_http_downstream_connection.h | 78 - src/shrpx_https_upstream.cc | 983 - src/shrpx_https_upstream.h | 91 - src/shrpx_io_control.cc | 66 - src/shrpx_io_control.h | 57 - src/shrpx_log.cc | 343 - src/shrpx_log.h | 151 - src/shrpx_log_config.cc | 69 - src/shrpx_log_config.h | 53 - src/shrpx_rate_limit.cc | 109 - src/shrpx_rate_limit.h | 65 - src/shrpx_spdy_upstream.cc | 1103 - src/shrpx_spdy_upstream.h | 94 - src/shrpx_ssl.cc | 1028 - src/shrpx_ssl.h | 177 - src/shrpx_ssl_test.cc | 119 - src/shrpx_ssl_test.h | 39 - src/shrpx_upstream.h | 69 - src/shrpx_worker.cc | 242 - src/shrpx_worker.h | 142 - src/ssl.cc | 83 - src/ssl.h | 45 - src/template.h | 150 - src/timegm.c | 70 - src/timegm.h | 50 - src/util.cc | 1161 - src/util.h | 626 - src/util_test.cc | 370 - src/util_test.h | 59 - test-driver | 139 - tests/Makefile.am | 82 - tests/Makefile.in | 1253 - tests/failmalloc.c | 77 - tests/failmalloc_test.c | 509 - tests/failmalloc_test.h | 33 - tests/main.c | 368 - tests/malloc_wrapper.c | 75 - tests/malloc_wrapper.h | 61 - tests/nghttp2_buf_test.c | 342 - tests/nghttp2_buf_test.h | 38 - tests/nghttp2_frame_test.c | 561 - tests/nghttp2_frame_test.h | 40 - tests/nghttp2_hd_test.c | 1257 - tests/nghttp2_hd_test.h | 47 - tests/nghttp2_helper_test.c | 169 - tests/nghttp2_helper_test.h | 32 - tests/nghttp2_map_test.c | 176 - tests/nghttp2_map_test.h | 32 - tests/nghttp2_npn_test.c | 72 - tests/nghttp2_npn_test.h | 30 - tests/nghttp2_pq_test.c | 123 - tests/nghttp2_pq_test.h | 31 - tests/nghttp2_queue_test.c | 48 - tests/nghttp2_queue_test.h | 30 - tests/nghttp2_session_test.c | 8125 ------ tests/nghttp2_session_test.h | 138 - tests/nghttp2_stream_test.c | 29 - tests/nghttp2_stream_test.h | 28 - tests/nghttp2_test_helper.c | 303 - tests/nghttp2_test_helper.h | 112 - tests/testdata/Makefile.am | 23 - tests/testdata/Makefile.in | 512 - tests/testdata/cacert.pem | 14 - tests/testdata/index.html | 1 - tests/testdata/privkey.pem | 9 - third-party/Makefile.am | 32 - third-party/Makefile.in | 751 - third-party/h2o/fetch-ocsp-response | 150 - third-party/http-parser/http_parser.c | 2418 -- third-party/http-parser/http_parser.h | 335 - 476 files changed, 171766 deletions(-) delete mode 100644 AUTHORS delete mode 100644 COPYING delete mode 100644 ChangeLog delete mode 100644 Dockerfile.android delete mode 100644 INSTALL delete mode 100644 Makefile.am delete mode 100644 Makefile.in delete mode 100644 NEWS delete mode 100644 README delete mode 100644 README.rst delete mode 100644 aclocal.m4 delete mode 100755 android-config delete mode 100755 android-make delete mode 100755 compile delete mode 100755 config.guess delete mode 100644 config.h.in delete mode 100755 config.sub delete mode 100755 configure delete mode 100644 configure.ac delete mode 100644 contrib/Makefile.am delete mode 100644 contrib/Makefile.in delete mode 100644 contrib/nghttpx-init.in delete mode 100644 contrib/nghttpx-logrotate delete mode 100644 contrib/nghttpx-upstart.conf.in delete mode 100644 contrib/nghttpx.service.in delete mode 100755 depcomp delete mode 100644 doc/Makefile.am delete mode 100644 doc/Makefile.in delete mode 100644 doc/README.rst delete mode 100644 doc/_themes/sphinx_rtd_theme/__init__.py delete mode 100644 doc/_themes/sphinx_rtd_theme/breadcrumbs.html delete mode 100644 doc/_themes/sphinx_rtd_theme/footer.html delete mode 100644 doc/_themes/sphinx_rtd_theme/layout.html delete mode 100644 doc/_themes/sphinx_rtd_theme/layout_old.html delete mode 100644 doc/_themes/sphinx_rtd_theme/search.html delete mode 100644 doc/_themes/sphinx_rtd_theme/searchbox.html delete mode 100644 doc/_themes/sphinx_rtd_theme/static/css/badge_only.css delete mode 100644 doc/_themes/sphinx_rtd_theme/static/css/theme.css delete mode 100644 doc/_themes/sphinx_rtd_theme/static/fonts/FontAwesome.otf delete mode 100644 doc/_themes/sphinx_rtd_theme/static/fonts/fontawesome-webfont.eot delete mode 100644 doc/_themes/sphinx_rtd_theme/static/fonts/fontawesome-webfont.svg delete mode 100644 doc/_themes/sphinx_rtd_theme/static/fonts/fontawesome-webfont.ttf delete mode 100644 doc/_themes/sphinx_rtd_theme/static/fonts/fontawesome-webfont.woff delete mode 100644 doc/_themes/sphinx_rtd_theme/static/js/theme.js delete mode 100644 doc/_themes/sphinx_rtd_theme/theme.conf delete mode 100644 doc/_themes/sphinx_rtd_theme/versions.html delete mode 100644 doc/apiref.rst delete mode 100644 doc/asio_http2.h.rst.in delete mode 100644 doc/asio_http2_client.h.rst.in delete mode 100644 doc/asio_http2_server.h.rst.in delete mode 100644 doc/bash_completion/h2load delete mode 100644 doc/bash_completion/nghttp delete mode 100644 doc/bash_completion/nghttpd delete mode 100644 doc/bash_completion/nghttpx delete mode 100644 doc/building-android-binary.rst.in delete mode 100644 doc/conf.py.in delete mode 100644 doc/contribute.rst.in delete mode 100644 doc/enums.rst delete mode 100644 doc/h2load-howto.rst.in delete mode 100644 doc/h2load.1 delete mode 100644 doc/h2load.1.rst delete mode 100644 doc/index.rst.in delete mode 100644 doc/libnghttp2_asio.rst.in delete mode 100644 doc/macros.rst delete mode 100755 doc/mkapiref.py delete mode 100644 doc/nghttp.1 delete mode 100644 doc/nghttp.1.rst delete mode 100644 doc/nghttp2.h.rst.in delete mode 100644 doc/nghttp2_check_header_name.rst delete mode 100644 doc/nghttp2_check_header_value.rst delete mode 100644 doc/nghttp2_hd_deflate_bound.rst delete mode 100644 doc/nghttp2_hd_deflate_change_table_size.rst delete mode 100644 doc/nghttp2_hd_deflate_del.rst delete mode 100644 doc/nghttp2_hd_deflate_hd.rst delete mode 100644 doc/nghttp2_hd_deflate_new.rst delete mode 100644 doc/nghttp2_hd_deflate_new2.rst delete mode 100644 doc/nghttp2_hd_inflate_change_table_size.rst delete mode 100644 doc/nghttp2_hd_inflate_del.rst delete mode 100644 doc/nghttp2_hd_inflate_end_headers.rst delete mode 100644 doc/nghttp2_hd_inflate_hd.rst delete mode 100644 doc/nghttp2_hd_inflate_new.rst delete mode 100644 doc/nghttp2_hd_inflate_new2.rst delete mode 100644 doc/nghttp2_is_fatal.rst delete mode 100644 doc/nghttp2_nv_compare_name.rst delete mode 100644 doc/nghttp2_option_del.rst delete mode 100644 doc/nghttp2_option_new.rst delete mode 100644 doc/nghttp2_option_set_no_auto_window_update.rst delete mode 100644 doc/nghttp2_option_set_no_http_messaging.rst delete mode 100644 doc/nghttp2_option_set_no_recv_client_magic.rst delete mode 100644 doc/nghttp2_option_set_peer_max_concurrent_streams.rst delete mode 100644 doc/nghttp2_pack_settings_payload.rst delete mode 100644 doc/nghttp2_priority_spec_check_default.rst delete mode 100644 doc/nghttp2_priority_spec_default_init.rst delete mode 100644 doc/nghttp2_priority_spec_init.rst delete mode 100644 doc/nghttp2_select_next_protocol.rst delete mode 100644 doc/nghttp2_session_callbacks_del.rst delete mode 100644 doc/nghttp2_session_callbacks_new.rst delete mode 100644 doc/nghttp2_session_callbacks_set_before_frame_send_callback.rst delete mode 100644 doc/nghttp2_session_callbacks_set_data_source_read_length_callback.rst delete mode 100644 doc/nghttp2_session_callbacks_set_on_begin_frame_callback.rst delete mode 100644 doc/nghttp2_session_callbacks_set_on_begin_headers_callback.rst delete mode 100644 doc/nghttp2_session_callbacks_set_on_data_chunk_recv_callback.rst delete mode 100644 doc/nghttp2_session_callbacks_set_on_frame_not_send_callback.rst delete mode 100644 doc/nghttp2_session_callbacks_set_on_frame_recv_callback.rst delete mode 100644 doc/nghttp2_session_callbacks_set_on_frame_send_callback.rst delete mode 100644 doc/nghttp2_session_callbacks_set_on_header_callback.rst delete mode 100644 doc/nghttp2_session_callbacks_set_on_invalid_frame_recv_callback.rst delete mode 100644 doc/nghttp2_session_callbacks_set_on_stream_close_callback.rst delete mode 100644 doc/nghttp2_session_callbacks_set_recv_callback.rst delete mode 100644 doc/nghttp2_session_callbacks_set_select_padding_callback.rst delete mode 100644 doc/nghttp2_session_callbacks_set_send_callback.rst delete mode 100644 doc/nghttp2_session_callbacks_set_send_data_callback.rst delete mode 100644 doc/nghttp2_session_client_new.rst delete mode 100644 doc/nghttp2_session_client_new2.rst delete mode 100644 doc/nghttp2_session_client_new3.rst delete mode 100644 doc/nghttp2_session_consume.rst delete mode 100644 doc/nghttp2_session_consume_connection.rst delete mode 100644 doc/nghttp2_session_consume_stream.rst delete mode 100644 doc/nghttp2_session_del.rst delete mode 100644 doc/nghttp2_session_get_effective_local_window_size.rst delete mode 100644 doc/nghttp2_session_get_effective_recv_data_length.rst delete mode 100644 doc/nghttp2_session_get_last_proc_stream_id.rst delete mode 100644 doc/nghttp2_session_get_next_stream_id.rst delete mode 100644 doc/nghttp2_session_get_outbound_queue_size.rst delete mode 100644 doc/nghttp2_session_get_remote_settings.rst delete mode 100644 doc/nghttp2_session_get_remote_window_size.rst delete mode 100644 doc/nghttp2_session_get_stream_effective_local_window_size.rst delete mode 100644 doc/nghttp2_session_get_stream_effective_recv_data_length.rst delete mode 100644 doc/nghttp2_session_get_stream_local_close.rst delete mode 100644 doc/nghttp2_session_get_stream_remote_close.rst delete mode 100644 doc/nghttp2_session_get_stream_remote_window_size.rst delete mode 100644 doc/nghttp2_session_get_stream_user_data.rst delete mode 100644 doc/nghttp2_session_mem_recv.rst delete mode 100644 doc/nghttp2_session_mem_send.rst delete mode 100644 doc/nghttp2_session_recv.rst delete mode 100644 doc/nghttp2_session_resume_data.rst delete mode 100644 doc/nghttp2_session_send.rst delete mode 100644 doc/nghttp2_session_server_new.rst delete mode 100644 doc/nghttp2_session_server_new2.rst delete mode 100644 doc/nghttp2_session_server_new3.rst delete mode 100644 doc/nghttp2_session_set_next_stream_id.rst delete mode 100644 doc/nghttp2_session_set_stream_user_data.rst delete mode 100644 doc/nghttp2_session_terminate_session.rst delete mode 100644 doc/nghttp2_session_terminate_session2.rst delete mode 100644 doc/nghttp2_session_upgrade.rst delete mode 100644 doc/nghttp2_session_want_read.rst delete mode 100644 doc/nghttp2_session_want_write.rst delete mode 100644 doc/nghttp2_strerror.rst delete mode 100644 doc/nghttp2_submit_data.rst delete mode 100644 doc/nghttp2_submit_goaway.rst delete mode 100644 doc/nghttp2_submit_headers.rst delete mode 100644 doc/nghttp2_submit_ping.rst delete mode 100644 doc/nghttp2_submit_priority.rst delete mode 100644 doc/nghttp2_submit_push_promise.rst delete mode 100644 doc/nghttp2_submit_request.rst delete mode 100644 doc/nghttp2_submit_response.rst delete mode 100644 doc/nghttp2_submit_rst_stream.rst delete mode 100644 doc/nghttp2_submit_settings.rst delete mode 100644 doc/nghttp2_submit_shutdown_notice.rst delete mode 100644 doc/nghttp2_submit_trailer.rst delete mode 100644 doc/nghttp2_submit_window_update.rst delete mode 100644 doc/nghttp2_version.rst delete mode 100644 doc/nghttp2ver.h.rst.in delete mode 100644 doc/nghttpd.1 delete mode 100644 doc/nghttpd.1.rst delete mode 100644 doc/nghttpx-howto.rst.in delete mode 100644 doc/nghttpx.1 delete mode 100644 doc/nghttpx.1.rst delete mode 100644 doc/package_README.rst.in delete mode 100644 doc/programmers-guide.rst delete mode 100644 doc/python-apiref.rst.in delete mode 100644 doc/sources/building-android-binary.rst delete mode 100644 doc/sources/contribute.rst delete mode 100644 doc/sources/h2load-howto.rst delete mode 100644 doc/sources/index.rst delete mode 100644 doc/sources/libnghttp2_asio.rst delete mode 100644 doc/sources/nghttpx-howto.rst delete mode 100644 doc/sources/python-apiref.rst delete mode 100644 doc/sources/tutorial-client.rst delete mode 100644 doc/sources/tutorial-hpack.rst delete mode 100644 doc/sources/tutorial-server.rst delete mode 100644 doc/tutorial-client.rst.in delete mode 100644 doc/tutorial-hpack.rst.in delete mode 100644 doc/tutorial-server.rst.in delete mode 100644 doc/types.rst delete mode 100644 examples/Makefile.am delete mode 100644 examples/Makefile.in delete mode 100644 examples/asio-cl.cc delete mode 100644 examples/asio-cl2.cc delete mode 100644 examples/asio-sv.cc delete mode 100644 examples/asio-sv2.cc delete mode 100644 examples/client.c delete mode 100644 examples/deflate.c delete mode 100644 examples/libevent-client.c delete mode 100644 examples/libevent-server.c delete mode 100644 examples/tiny-nghttpd.c delete mode 100755 install-sh delete mode 100644 integration-tests/Makefile.am delete mode 100644 integration-tests/Makefile.in delete mode 100644 integration-tests/alt-server.crt delete mode 100644 integration-tests/alt-server.key delete mode 100644 integration-tests/config.go.in delete mode 100644 integration-tests/nghttpx_http1_test.go delete mode 100644 integration-tests/nghttpx_http2_test.go delete mode 100644 integration-tests/nghttpx_spdy_test.go delete mode 100644 integration-tests/server.crt delete mode 100644 integration-tests/server.key delete mode 100644 integration-tests/server_tester.go delete mode 100644 integration-tests/setenv delete mode 100644 integration-tests/setenv.in delete mode 100644 lib/Makefile.am delete mode 100644 lib/Makefile.in delete mode 100644 lib/Makefile.msvc delete mode 100644 lib/includes/Makefile.am delete mode 100644 lib/includes/Makefile.in delete mode 100644 lib/includes/nghttp2/nghttp2.h delete mode 100644 lib/includes/nghttp2/nghttp2ver.h delete mode 100644 lib/includes/nghttp2/nghttp2ver.h.in delete mode 100644 lib/libnghttp2.pc.in delete mode 100644 lib/nghttp2_buf.c delete mode 100644 lib/nghttp2_buf.h delete mode 100644 lib/nghttp2_callbacks.c delete mode 100644 lib/nghttp2_callbacks.h delete mode 100644 lib/nghttp2_frame.c delete mode 100644 lib/nghttp2_frame.h delete mode 100644 lib/nghttp2_hd.c delete mode 100644 lib/nghttp2_hd.h delete mode 100644 lib/nghttp2_hd_huffman.c delete mode 100644 lib/nghttp2_hd_huffman.h delete mode 100644 lib/nghttp2_hd_huffman_data.c delete mode 100644 lib/nghttp2_helper.c delete mode 100644 lib/nghttp2_helper.h delete mode 100644 lib/nghttp2_http.c delete mode 100644 lib/nghttp2_http.h delete mode 100644 lib/nghttp2_int.h delete mode 100644 lib/nghttp2_map.c delete mode 100644 lib/nghttp2_map.h delete mode 100644 lib/nghttp2_mem.c delete mode 100644 lib/nghttp2_mem.h delete mode 100644 lib/nghttp2_net.h delete mode 100644 lib/nghttp2_npn.c delete mode 100644 lib/nghttp2_npn.h delete mode 100644 lib/nghttp2_option.c delete mode 100644 lib/nghttp2_option.h delete mode 100644 lib/nghttp2_outbound_item.c delete mode 100644 lib/nghttp2_outbound_item.h delete mode 100644 lib/nghttp2_pq.c delete mode 100644 lib/nghttp2_pq.h delete mode 100644 lib/nghttp2_priority_spec.c delete mode 100644 lib/nghttp2_priority_spec.h delete mode 100644 lib/nghttp2_queue.c delete mode 100644 lib/nghttp2_queue.h delete mode 100644 lib/nghttp2_session.c delete mode 100644 lib/nghttp2_session.h delete mode 100644 lib/nghttp2_stream.c delete mode 100644 lib/nghttp2_stream.h delete mode 100644 lib/nghttp2_submit.c delete mode 100644 lib/nghttp2_submit.h delete mode 100644 lib/nghttp2_version.c delete mode 100644 ltmain.sh delete mode 100644 m4/ax_boost_asio.m4 delete mode 100644 m4/ax_boost_base.m4 delete mode 100644 m4/ax_boost_system.m4 delete mode 100644 m4/ax_boost_thread.m4 delete mode 100644 m4/ax_check_compile_flag.m4 delete mode 100644 m4/ax_cxx_compile_stdcxx_11.m4 delete mode 100644 m4/ax_have_epoll.m4 delete mode 100644 m4/ax_python_devel.m4 delete mode 100644 m4/libtool.m4 delete mode 100644 m4/libxml2.m4 delete mode 100644 m4/ltoptions.m4 delete mode 100644 m4/ltsugar.m4 delete mode 100644 m4/ltversion.m4 delete mode 100644 m4/lt~obsolete.m4 delete mode 100755 missing delete mode 100644 nghttpx.conf.sample delete mode 100644 proxy.pac.sample delete mode 100644 python/Makefile.am delete mode 100644 python/Makefile.in delete mode 100644 python/cnghttp2.pxd delete mode 100644 python/nghttp2.pyx delete mode 100644 python/setup.py.in delete mode 100644 src/HtmlParser.cc delete mode 100644 src/HtmlParser.h delete mode 100644 src/HttpServer.cc delete mode 100644 src/HttpServer.h delete mode 100644 src/Makefile.am delete mode 100644 src/Makefile.in delete mode 100644 src/app_helper.cc delete mode 100644 src/app_helper.h delete mode 100644 src/asio_client_request.cc delete mode 100644 src/asio_client_request_impl.cc delete mode 100644 src/asio_client_request_impl.h delete mode 100644 src/asio_client_response.cc delete mode 100644 src/asio_client_response_impl.cc delete mode 100644 src/asio_client_response_impl.h delete mode 100644 src/asio_client_session.cc delete mode 100644 src/asio_client_session_impl.cc delete mode 100644 src/asio_client_session_impl.h delete mode 100644 src/asio_client_session_tcp_impl.cc delete mode 100644 src/asio_client_session_tcp_impl.h delete mode 100644 src/asio_client_session_tls_impl.cc delete mode 100644 src/asio_client_session_tls_impl.h delete mode 100644 src/asio_client_stream.cc delete mode 100644 src/asio_client_stream.h delete mode 100644 src/asio_client_tls_context.cc delete mode 100644 src/asio_client_tls_context.h delete mode 100644 src/asio_common.cc delete mode 100644 src/asio_common.h delete mode 100644 src/asio_io_service_pool.cc delete mode 100644 src/asio_io_service_pool.h delete mode 100644 src/asio_server.cc delete mode 100644 src/asio_server.h delete mode 100644 src/asio_server_connection.h delete mode 100644 src/asio_server_http2.cc delete mode 100644 src/asio_server_http2_handler.cc delete mode 100644 src/asio_server_http2_handler.h delete mode 100644 src/asio_server_http2_impl.cc delete mode 100644 src/asio_server_http2_impl.h delete mode 100644 src/asio_server_request.cc delete mode 100644 src/asio_server_request_handler.cc delete mode 100644 src/asio_server_request_handler.h delete mode 100644 src/asio_server_request_impl.cc delete mode 100644 src/asio_server_request_impl.h delete mode 100644 src/asio_server_response.cc delete mode 100644 src/asio_server_response_impl.cc delete mode 100644 src/asio_server_response_impl.h delete mode 100644 src/asio_server_serve_mux.cc delete mode 100644 src/asio_server_serve_mux.h delete mode 100644 src/asio_server_stream.cc delete mode 100644 src/asio_server_stream.h delete mode 100644 src/asio_server_tls_context.cc delete mode 100644 src/asio_server_tls_context.h delete mode 100644 src/base64.h delete mode 100644 src/buffer.h delete mode 100644 src/buffer_test.cc delete mode 100644 src/buffer_test.h delete mode 100644 src/comp_helper.c delete mode 100644 src/comp_helper.h delete mode 100644 src/deflatehd.cc delete mode 100644 src/h2load.cc delete mode 100644 src/h2load.h delete mode 100644 src/h2load_http2_session.cc delete mode 100644 src/h2load_http2_session.h delete mode 100644 src/h2load_session.h delete mode 100644 src/h2load_spdy_session.cc delete mode 100644 src/h2load_spdy_session.h delete mode 100644 src/http2.cc delete mode 100644 src/http2.h delete mode 100644 src/http2_test.cc delete mode 100644 src/http2_test.h delete mode 100644 src/includes/Makefile.am delete mode 100644 src/includes/Makefile.in delete mode 100644 src/includes/nghttp2/asio_http2.h delete mode 100644 src/includes/nghttp2/asio_http2_client.h delete mode 100644 src/includes/nghttp2/asio_http2_server.h delete mode 100644 src/inflatehd.cc delete mode 100644 src/libnghttp2_asio.pc.in delete mode 100644 src/memchunk.h delete mode 100644 src/memchunk_test.cc delete mode 100644 src/memchunk_test.h delete mode 100644 src/nghttp.cc delete mode 100644 src/nghttp.h delete mode 100644 src/nghttp2_config.h delete mode 100644 src/nghttp2_gzip.c delete mode 100644 src/nghttp2_gzip.h delete mode 100644 src/nghttp2_gzip_test.c delete mode 100644 src/nghttp2_gzip_test.h delete mode 100644 src/nghttpd.cc delete mode 100644 src/shrpx-unittest.cc delete mode 100644 src/shrpx.cc delete mode 100644 src/shrpx.h delete mode 100644 src/shrpx_accept_handler.cc delete mode 100644 src/shrpx_accept_handler.h delete mode 100644 src/shrpx_client_handler.cc delete mode 100644 src/shrpx_client_handler.h delete mode 100644 src/shrpx_config.cc delete mode 100644 src/shrpx_config.h delete mode 100644 src/shrpx_config_test.cc delete mode 100644 src/shrpx_config_test.h delete mode 100644 src/shrpx_connect_blocker.cc delete mode 100644 src/shrpx_connect_blocker.h delete mode 100644 src/shrpx_connection.cc delete mode 100644 src/shrpx_connection.h delete mode 100644 src/shrpx_connection_handler.cc delete mode 100644 src/shrpx_connection_handler.h delete mode 100644 src/shrpx_downstream.cc delete mode 100644 src/shrpx_downstream.h delete mode 100644 src/shrpx_downstream_connection.cc delete mode 100644 src/shrpx_downstream_connection.h delete mode 100644 src/shrpx_downstream_connection_pool.cc delete mode 100644 src/shrpx_downstream_connection_pool.h delete mode 100644 src/shrpx_downstream_queue.cc delete mode 100644 src/shrpx_downstream_queue.h delete mode 100644 src/shrpx_downstream_test.cc delete mode 100644 src/shrpx_downstream_test.h delete mode 100644 src/shrpx_error.h delete mode 100644 src/shrpx_http.cc delete mode 100644 src/shrpx_http.h delete mode 100644 src/shrpx_http2_downstream_connection.cc delete mode 100644 src/shrpx_http2_downstream_connection.h delete mode 100644 src/shrpx_http2_session.cc delete mode 100644 src/shrpx_http2_session.h delete mode 100644 src/shrpx_http2_upstream.cc delete mode 100644 src/shrpx_http2_upstream.h delete mode 100644 src/shrpx_http_downstream_connection.cc delete mode 100644 src/shrpx_http_downstream_connection.h delete mode 100644 src/shrpx_https_upstream.cc delete mode 100644 src/shrpx_https_upstream.h delete mode 100644 src/shrpx_io_control.cc delete mode 100644 src/shrpx_io_control.h delete mode 100644 src/shrpx_log.cc delete mode 100644 src/shrpx_log.h delete mode 100644 src/shrpx_log_config.cc delete mode 100644 src/shrpx_log_config.h delete mode 100644 src/shrpx_rate_limit.cc delete mode 100644 src/shrpx_rate_limit.h delete mode 100644 src/shrpx_spdy_upstream.cc delete mode 100644 src/shrpx_spdy_upstream.h delete mode 100644 src/shrpx_ssl.cc delete mode 100644 src/shrpx_ssl.h delete mode 100644 src/shrpx_ssl_test.cc delete mode 100644 src/shrpx_ssl_test.h delete mode 100644 src/shrpx_upstream.h delete mode 100644 src/shrpx_worker.cc delete mode 100644 src/shrpx_worker.h delete mode 100644 src/ssl.cc delete mode 100644 src/ssl.h delete mode 100644 src/template.h delete mode 100644 src/timegm.c delete mode 100644 src/timegm.h delete mode 100644 src/util.cc delete mode 100644 src/util.h delete mode 100644 src/util_test.cc delete mode 100644 src/util_test.h delete mode 100755 test-driver delete mode 100644 tests/Makefile.am delete mode 100644 tests/Makefile.in delete mode 100644 tests/failmalloc.c delete mode 100644 tests/failmalloc_test.c delete mode 100644 tests/failmalloc_test.h delete mode 100644 tests/main.c delete mode 100644 tests/malloc_wrapper.c delete mode 100644 tests/malloc_wrapper.h delete mode 100644 tests/nghttp2_buf_test.c delete mode 100644 tests/nghttp2_buf_test.h delete mode 100644 tests/nghttp2_frame_test.c delete mode 100644 tests/nghttp2_frame_test.h delete mode 100644 tests/nghttp2_hd_test.c delete mode 100644 tests/nghttp2_hd_test.h delete mode 100644 tests/nghttp2_helper_test.c delete mode 100644 tests/nghttp2_helper_test.h delete mode 100644 tests/nghttp2_map_test.c delete mode 100644 tests/nghttp2_map_test.h delete mode 100644 tests/nghttp2_npn_test.c delete mode 100644 tests/nghttp2_npn_test.h delete mode 100644 tests/nghttp2_pq_test.c delete mode 100644 tests/nghttp2_pq_test.h delete mode 100644 tests/nghttp2_queue_test.c delete mode 100644 tests/nghttp2_queue_test.h delete mode 100644 tests/nghttp2_session_test.c delete mode 100644 tests/nghttp2_session_test.h delete mode 100644 tests/nghttp2_stream_test.c delete mode 100644 tests/nghttp2_stream_test.h delete mode 100644 tests/nghttp2_test_helper.c delete mode 100644 tests/nghttp2_test_helper.h delete mode 100644 tests/testdata/Makefile.am delete mode 100644 tests/testdata/Makefile.in delete mode 100644 tests/testdata/cacert.pem delete mode 100644 tests/testdata/index.html delete mode 100644 tests/testdata/privkey.pem delete mode 100644 third-party/Makefile.am delete mode 100644 third-party/Makefile.in delete mode 100755 third-party/h2o/fetch-ocsp-response delete mode 100644 third-party/http-parser/http_parser.c delete mode 100644 third-party/http-parser/http_parser.h diff --git a/AUTHORS b/AUTHORS deleted file mode 100644 index 95bc954..0000000 --- a/AUTHORS +++ /dev/null @@ -1 +0,0 @@ -Tatsuhiro Tsujikawa diff --git a/COPYING b/COPYING deleted file mode 100644 index a43e837..0000000 --- a/COPYING +++ /dev/null @@ -1,22 +0,0 @@ -The MIT License - -Copyright (c) 2012, 2014, 2015 Tatsuhiro Tsujikawa - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/ChangeLog b/ChangeLog deleted file mode 100644 index 9711d5d..0000000 --- a/ChangeLog +++ /dev/null @@ -1,259 +0,0 @@ -commit 553d741f036b3b5f47b0f296e4663fe1de8af6ec -Author: Tatsuhiro Tsujikawa -AuthorDate: 2015-05-16 -Commit: Tatsuhiro Tsujikawa -CommitDate: 2015-05-16 - - Fix migration version number - -commit 6bd728b3c2a6faccad3e4f69c75ca792f3a39e85 -Author: Tatsuhiro Tsujikawa -AuthorDate: 2015-05-16 -Commit: Tatsuhiro Tsujikawa -CommitDate: 2015-05-16 - - Update man pages - -commit a99085891a9378165f68211225d94e3008d742f5 -Author: Tatsuhiro Tsujikawa -AuthorDate: 2015-05-16 -Commit: Tatsuhiro Tsujikawa -CommitDate: 2015-05-16 - - Bump up version number to 1.0.0, LT revision to 14:0:0 - -commit 68d3724fad57db135015de05bdd169706c9e6983 -Author: Tatsuhiro Tsujikawa -AuthorDate: 2015-05-15 -Commit: Tatsuhiro Tsujikawa -CommitDate: 2015-05-15 - - Update README.rst - -commit fe39ec869733920f3f79e9ebdeb7850fbc9ffd2b -Author: Tatsuhiro Tsujikawa -AuthorDate: 2015-05-15 -Commit: Tatsuhiro Tsujikawa -CommitDate: 2015-05-15 - - doc: Update Resources - -commit c896118747e298aaed4ddc86da1169c02bdfc734 -Author: Tatsuhiro Tsujikawa -AuthorDate: 2015-05-15 -Commit: Tatsuhiro Tsujikawa -CommitDate: 2015-05-15 - - Fix required spdylay version - -commit b89140c311e7a3438d29637b44dea09bf20c8e79 -Merge: 92a20c7 a869c39 -Author: Tatsuhiro Tsujikawa -AuthorDate: 2015-05-15 -Commit: Tatsuhiro Tsujikawa -CommitDate: 2015-05-15 - - Merge branch 'v1.0.0' - -commit a869c39a2c2c7ec0db4e8db477b2dfed5f3eaa0f -Author: Tatsuhiro Tsujikawa -AuthorDate: 2015-05-15 -Commit: Tatsuhiro Tsujikawa -CommitDate: 2015-05-15 - - Bump up version number to 1.0.0-DEV - -commit 0b27f005e0c23931a82e612b024f3ace66546924 -Merge: 64b1aae 92a20c7 -Author: Tatsuhiro Tsujikawa -AuthorDate: 2015-05-15 -Commit: Tatsuhiro Tsujikawa -CommitDate: 2015-05-15 - - Merge branch 'master' into v1.0.0 - - Conflicts: - src/HttpServer.cc - -commit 64b1aae56748c7b6816409aee649817ee56aa1e8 -Author: Tatsuhiro Tsujikawa -AuthorDate: 2015-05-08 -Commit: Tatsuhiro Tsujikawa -CommitDate: 2015-05-08 - - integration: Fix TestH2H1Upgrade test failure - -commit e63d6e490aa9fab1cf8240bfd5c3133df241ced0 -Merge: de47350 7f60de0 -Author: Tatsuhiro Tsujikawa -AuthorDate: 2015-05-08 -Commit: Tatsuhiro Tsujikawa -CommitDate: 2015-05-08 - - Merge branch 'master' into v1.0.0 - - Conflicts: - lib/nghttp2_option.h - lib/nghttp2_session.h - src/HttpServer.cc - -commit de4735092a2970f9f5d93de617dc0faf2db9952d -Author: Tatsuhiro Tsujikawa -AuthorDate: 2015-04-28 -Commit: Tatsuhiro Tsujikawa -CommitDate: 2015-04-28 - - Fix doc formatting - -commit 1c4df1832b179802052b9785001381855e03981c -Author: Tatsuhiro Tsujikawa -AuthorDate: 2015-04-28 -Commit: Tatsuhiro Tsujikawa -CommitDate: 2015-04-28 - - Update doc, mainly for RFC numbers - -commit 1ad1fe600574e6fb5760b5ea66ac69472bf5e4d5 -Merge: db4a684 f05a483 -Author: Tatsuhiro Tsujikawa -AuthorDate: 2015-04-28 -Commit: Tatsuhiro Tsujikawa -CommitDate: 2015-04-28 - - Merge branch 'master' into v1.0.0 - -commit db4a68454a07d304ed92cfed9df8401b6b89196f -Merge: 5937b4b 6b0b8ea -Author: Tatsuhiro Tsujikawa -AuthorDate: 2015-04-24 -Commit: Tatsuhiro Tsujikawa -CommitDate: 2015-04-24 - - Merge branch 'master' into v1.0.0 - - Conflicts: - lib/includes/nghttp2/nghttp2.h - -commit 5937b4b6f73380e8a0c22932c118e265de2c30c3 -Merge: 90bfea7 787d401 -Author: Tatsuhiro Tsujikawa -AuthorDate: 2015-04-19 -Commit: Tatsuhiro Tsujikawa -CommitDate: 2015-04-19 - - Merge branch 'master' into v1.0.0 - -commit 90bfea77e098d54429445ee8cf8eee6854ccc767 -Author: Tatsuhiro Tsujikawa -AuthorDate: 2015-04-08 -Commit: Tatsuhiro Tsujikawa -CommitDate: 2015-04-08 - - doc: Remove nghttp2_submit_altsvc.rst - -commit cf0576253f93a096ea79610f5ef6349fa3e6ac9f -Merge: 59e3783 4aca2f0 -Author: Tatsuhiro Tsujikawa -AuthorDate: 2015-04-08 -Commit: Tatsuhiro Tsujikawa -CommitDate: 2015-04-08 - - Merge branch 'master' into v1.0.0 - -commit 59e3783f3f34cf8663b51fd98fdae14993aa1cb8 -Author: Tatsuhiro Tsujikawa -AuthorDate: 2015-04-07 -Commit: Tatsuhiro Tsujikawa -CommitDate: 2015-04-07 - - Update manual entry - -commit 084e4487edee1a0cd9c503a074519888ab347af4 -Author: Tatsuhiro Tsujikawa -AuthorDate: 2015-04-05 -Commit: Tatsuhiro Tsujikawa -CommitDate: 2015-04-05 - - Add migration section - -commit 24897aa50decdde2bf360f5805bc2cfb722f4cc8 -Author: Tatsuhiro Tsujikawa -AuthorDate: 2015-04-05 -Commit: Tatsuhiro Tsujikawa -CommitDate: 2015-04-05 - - Update README.rst - -commit 3e50ef439d914dbf6c611fc4afc8925c9e79d6c7 -Author: Tatsuhiro Tsujikawa -AuthorDate: 2015-04-05 -Commit: Tatsuhiro Tsujikawa -CommitDate: 2015-04-05 - - Announce h2, final HTTP/2 ALPN identifier - -commit 87602e5d7296b37ed3f2864db1965d139657aba6 -Author: Tatsuhiro Tsujikawa -AuthorDate: 2015-04-05 -Commit: Tatsuhiro Tsujikawa -CommitDate: 2015-04-05 - - Use NGHTTP2_PROTOCOL_ERROR for NGHTTP2_ERR_HTTP_{HEADER,MESSAGING} - -commit d0c27d52296a0adb6f5614cb58408c8617f39aab -Author: Tatsuhiro Tsujikawa -AuthorDate: 2015-04-05 -Commit: Tatsuhiro Tsujikawa -CommitDate: 2015-04-05 - - Send 24 bytes client magic byte string by library - - Previously nghttp2_session_send() and nghttp2_session_mem_send() did - not send 24 bytes client magic byte string (MAGIC). We made - nghttp2_session_recv() and nghttp2_session_mem_recv() process MAGIC by - default, so it is natural to make library send MAGIC as well. This - commit makes nghttp2_session_send() and nghttp2_session_mem_send() - send MAGIC. This commit also replace "connection preface" with - "client magic", since we call MAGIC as "connection preface" but it is - just a part of connection preface. NGHTTP2_CLIENT_CONNECTION_PREFACE - macro was replaced with NGHTTP2_CLIENT_MAGIC. The already deprecated - NGHTTP2_CLIENT_CONNECTION_HEADER macro was removed permanently. - nghttp2_option_set_no_recv_client_preface() was renamed as - nghttp2_option_set_no_recv_client_magic(). NGHTTP2_ERR_BAD_PREFACE - was renamed as NGHTTP2_ERR_BAD_CLIENT_MAGIC. - -commit ebf214c8fc45ea96f60a5edb7737b96f1d59e3ee -Author: Tatsuhiro Tsujikawa -AuthorDate: 2015-03-23 -Commit: Tatsuhiro Tsujikawa -CommitDate: 2015-04-05 - - nghttp2_on_invalid_frame_recv_callback should have lib_error_code as param - - nghttp2_error_code is HTTP/2 standard error code and is too coarse to - know what's going on. - -commit 250ea53e4b968bfc2a3dd0e301a1878fdfa41efb -Author: Tatsuhiro Tsujikawa -AuthorDate: 2015-03-23 -Commit: Tatsuhiro Tsujikawa -CommitDate: 2015-04-05 - - Deal with 24 bytes client connection preface by default - - Since HTTP/2 spec requires for client to send connection preface, it - is reasonable to make this option enabled by default. It is still a - use case to disable this, so replace this option with - nghttp2_option_set_no_recv_client_preface(). - -commit 01af6ea70c466b8ace7f22c25737418babddb8b5 -Author: Tatsuhiro Tsujikawa -AuthorDate: 2015-03-22 -Commit: Tatsuhiro Tsujikawa -CommitDate: 2015-04-05 - - Remove ALTSVC related code - - HTTP/2 and HPACK are going to be published as RFC, but ALTSVC is still - in draft state. To make our API stable, it would be better to remove - ALTSVC API for 1.0.0 release. diff --git a/Dockerfile.android b/Dockerfile.android deleted file mode 100644 index 87990f0..0000000 --- a/Dockerfile.android +++ /dev/null @@ -1,109 +0,0 @@ -# vim: ft=dockerfile: -# Dockerfile to build nghttp2 android binary -# -# $ sudo docker build -t nghttp2-android - < Dockerfile.android -# -# After successful build, android binaries are located under -# /root/build/nghttp2. You can copy the binary using docker cp. For -# example, to copy nghttpx binary to host file system location -# /path/to/dest, do this: -# -# $ sudo docker run -v /path/to/dest:/out nghttp2-android cp /root/build/nghttp2/src/nghttpx /out - -FROM ubuntu:trusty - -MAINTAINER Tatsuhiro Tsujikawa - -ENV ANDROID_HOME /root/android -ENV PREFIX $ANDROID_HOME/usr/local -ENV TOOLCHAIN $ANDROID_HOME/toolchain -ENV PATH $TOOLCHAIN/bin:$PATH - -# It would be better to use nearest ubuntu archive mirror for faster -# downloads. -# RUN sed -ie 's/archive\.ubuntu/jp.archive.ubuntu/g' /etc/apt/sources.list - -RUN apt-get update -# genisoimage, libc6-i386 and lib32stdc++6 are required to decompress ndk. -RUN apt-get install -y make binutils autoconf automake autotools-dev libtool \ - pkg-config git curl dpkg-dev libxml2-dev \ - genisoimage libc6-i386 lib32stdc++6 - -WORKDIR /root/build -RUN curl -L -O http://dl.google.com/android/ndk/android-ndk-r10c-linux-x86_64.bin && \ - chmod a+x android-ndk-r10c-linux-x86_64.bin && \ - ./android-ndk-r10c-linux-x86_64.bin && \ - rm android-ndk-r10c-linux-x86_64.bin - -WORKDIR /root/build/android-ndk-r10c -RUN /bin/bash build/tools/make-standalone-toolchain.sh \ - --install-dir=$ANDROID_HOME/toolchain \ - --toolchain=arm-linux-androideabi-4.9 --llvm-version=3.5 \ - --system=linux-x86_64 - -WORKDIR /root/build -RUN git clone https://github.com/tatsuhiro-t/spdylay -WORKDIR /root/build/spdylay -RUN autoreconf -i && \ - ./configure \ - --disable-shared \ - --host=arm-linux-androideabi \ - --build=`dpkg-architecture -qDEB_BUILD_GNU_TYPE` \ - --prefix=$PREFIX \ - --without-libxml2 \ - --disable-src \ - --disable-examples \ - CPPFLAGS="-I$PREFIX/include" \ - PKG_CONFIG_LIBDIR="$PREFIX/lib/pkgconfig" \ - LDFLAGS="-L$PREFIX/lib" && \ - make install - -WORKDIR /root/build -RUN curl -L -O https://www.openssl.org/source/openssl-1.0.2a.tar.gz && \ - tar xf openssl-1.0.2a.tar.gz && \ - rm openssl-1.0.2a.tar.gz - -WORKDIR /root/build/openssl-1.0.2a -RUN export CROSS_COMPILE=$TOOLCHAIN/bin/arm-linux-androideabi- && \ - ./Configure --prefix=$PREFIX android && \ - make && make install_sw - -WORKDIR /root/build -RUN curl -L -O http://dist.schmorp.de/libev/libev-4.19.tar.gz && \ - curl -L -O https://gist.github.com/tatsuhiro-t/48c45f08950f587180ed/raw/80a8f003b5d1091eae497c5995bbaa68096e739b/libev-4.19-android.patch && \ - tar xf libev-4.19.tar.gz && \ - rm libev-4.19.tar.gz - -WORKDIR /root/build/libev-4.19 -RUN patch -p1 < ../libev-4.19-android.patch && \ - ./configure \ - --host=arm-linux-androideabi \ - --build=`dpkg-architecture -qDEB_BUILD_GNU_TYPE` \ - --prefix=$PREFIX \ - --disable-shared \ - --enable-static \ - CPPFLAGS=-I$PREFIX/include \ - LDFLAGS=-L$PREFIX/lib && \ - make install - -WORKDIR /root/build -RUN git clone https://github.com/tatsuhiro-t/nghttp2 -WORKDIR /root/build/nghttp2 -RUN autoreconf -i && \ - ./configure \ - --disable-shared \ - --host=arm-linux-androideabi \ - --build=`dpkg-architecture -qDEB_BUILD_GNU_TYPE` \ - --with-xml-prefix="$PREFIX" \ - --without-libxml2 \ - --disable-python-bindings \ - --disable-examples \ - --disable-threads \ - LIBSPDYLAY_CFLAGS=-I$PREFIX/usr/local/include \ - LIBSPDYLAY_LIBS="-L$PREFIX/usr/local/lib -lspdylay" \ - CPPFLAGS="-fPIE -I$PREFIX/include" \ - CXXFLAGS="-fno-strict-aliasing" \ - PKG_CONFIG_LIBDIR="$PREFIX/lib/pkgconfig" \ - LDFLAGS="-fPIE -pie -L$PREFIX/lib" && \ - make && \ - arm-linux-androideabi-strip src/nghttpx src/nghttpd src/nghttp diff --git a/INSTALL b/INSTALL deleted file mode 100644 index 2099840..0000000 --- a/INSTALL +++ /dev/null @@ -1,370 +0,0 @@ -Installation Instructions -************************* - -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 `' 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/Makefile.am b/Makefile.am deleted file mode 100644 index 1058e02..0000000 --- a/Makefile.am +++ /dev/null @@ -1,43 +0,0 @@ -# nghttp2 - HTTP/2 C Library - -# Copyright (c) 2012 Tatsuhiro Tsujikawa - -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: - -# The above copyright notice and this permission notice shall be -# included in all copies or substantial portions of the Software. - -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -SUBDIRS = lib third-party src examples python tests integration-tests \ - doc contrib - -ACLOCAL_AMFLAGS = -I m4 - -dist_doc_DATA = README.rst - -EXTRA_DIST = nghttpx.conf.sample proxy.pac.sample android-config android-make \ - Dockerfile.android - -.PHONY: clang-format - -# Format source files using clang-format. Don't format source files -# under third-party directory since we are not responsible for thier -# coding style. -clang-format: - CLANGFORMAT=`git config --get clangformat.binary`; \ - test -z $${CLANGFORMAT} && CLANGFORMAT="clang-format"; \ - $${CLANGFORMAT} -i lib/*.{c,h} lib/includes/nghttp2/*.h \ - src/*.{c,cc,h} src/includes/nghttp2/*.h examples/*.{c,cc} \ - tests/*.{c,h} diff --git a/Makefile.in b/Makefile.in deleted file mode 100644 index 0b92de8..0000000 --- a/Makefile.in +++ /dev/null @@ -1,964 +0,0 @@ -# Makefile.in generated by automake 1.14.1 from Makefile.am. -# @configure_input@ - -# Copyright (C) 1994-2013 Free Software Foundation, Inc. - -# This Makefile.in is free software; the Free Software Foundation -# gives unlimited permission to copy and/or distribute it, -# with or without modifications, as long as this notice is preserved. - -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY, to the extent permitted by law; without -# even the implied warranty of MERCHANTABILITY or FITNESS FOR A -# PARTICULAR PURPOSE. - -@SET_MAKE@ - -# nghttp2 - HTTP/2 C Library - -# Copyright (c) 2012 Tatsuhiro Tsujikawa - -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: - -# The above copyright notice and this permission notice shall be -# included in all copies or substantial portions of the Software. - -VPATH = @srcdir@ -am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)' -am__make_running_with_option = \ - case $${target_option-} in \ - ?) ;; \ - *) echo "am__make_running_with_option: internal error: invalid" \ - "target option '$${target_option-}' specified" >&2; \ - exit 1;; \ - esac; \ - has_opt=no; \ - sane_makeflags=$$MAKEFLAGS; \ - if $(am__is_gnu_make); then \ - sane_makeflags=$$MFLAGS; \ - else \ - case $$MAKEFLAGS in \ - *\\[\ \ ]*) \ - bs=\\; \ - sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ - | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ - esac; \ - fi; \ - skip_next=no; \ - strip_trailopt () \ - { \ - flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ - }; \ - for flg in $$sane_makeflags; do \ - test $$skip_next = yes && { skip_next=no; continue; }; \ - case $$flg in \ - *=*|--*) continue;; \ - -*I) strip_trailopt 'I'; skip_next=yes;; \ - -*I?*) strip_trailopt 'I';; \ - -*O) strip_trailopt 'O'; skip_next=yes;; \ - -*O?*) strip_trailopt 'O';; \ - -*l) strip_trailopt 'l'; skip_next=yes;; \ - -*l?*) strip_trailopt 'l';; \ - -[dEDm]) skip_next=yes;; \ - -[JT]) skip_next=yes;; \ - esac; \ - case $$flg in \ - *$$target_option*) has_opt=yes; break;; \ - esac; \ - done; \ - test $$has_opt = yes -am__make_dryrun = (target_option=n; $(am__make_running_with_option)) -am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) -pkgdatadir = $(datadir)/@PACKAGE@ -pkgincludedir = $(includedir)/@PACKAGE@ -pkglibdir = $(libdir)/@PACKAGE@ -pkglibexecdir = $(libexecdir)/@PACKAGE@ -am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd -install_sh_DATA = $(install_sh) -c -m 644 -install_sh_PROGRAM = $(install_sh) -c -install_sh_SCRIPT = $(install_sh) -c -INSTALL_HEADER = $(INSTALL_DATA) -transform = $(program_transform_name) -NORMAL_INSTALL = : -PRE_INSTALL = : -POST_INSTALL = : -NORMAL_UNINSTALL = : -PRE_UNINSTALL = : -POST_UNINSTALL = : -build_triplet = @build@ -host_triplet = @host@ -target_triplet = @target@ -subdir = . -DIST_COMMON = INSTALL NEWS README AUTHORS ChangeLog \ - $(srcdir)/Makefile.in $(srcdir)/Makefile.am \ - $(top_srcdir)/configure $(am__configure_deps) \ - $(srcdir)/config.h.in \ - $(top_srcdir)/lib/includes/nghttp2/nghttp2ver.h.in \ - $(dist_doc_DATA) COPYING compile config.guess config.sub \ - install-sh missing ltmain.sh -ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 -am__aclocal_m4_deps = $(top_srcdir)/m4/ax_boost_asio.m4 \ - $(top_srcdir)/m4/ax_boost_base.m4 \ - $(top_srcdir)/m4/ax_boost_system.m4 \ - $(top_srcdir)/m4/ax_boost_thread.m4 \ - $(top_srcdir)/m4/ax_check_compile_flag.m4 \ - $(top_srcdir)/m4/ax_cxx_compile_stdcxx_11.m4 \ - $(top_srcdir)/m4/ax_have_epoll.m4 \ - $(top_srcdir)/m4/ax_python_devel.m4 \ - $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/libxml2.m4 \ - $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ - $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ - $(top_srcdir)/configure.ac -am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ - $(ACLOCAL_M4) -am__CONFIG_DISTCLEAN_FILES = config.status config.cache config.log \ - configure.lineno config.status.lineno -mkinstalldirs = $(install_sh) -d -CONFIG_HEADER = config.h -CONFIG_CLEAN_FILES = lib/includes/nghttp2/nghttp2ver.h -CONFIG_CLEAN_VPATH_FILES = -AM_V_P = $(am__v_P_@AM_V@) -am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) -am__v_P_0 = false -am__v_P_1 = : -AM_V_GEN = $(am__v_GEN_@AM_V@) -am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) -am__v_GEN_0 = @echo " GEN " $@; -am__v_GEN_1 = -AM_V_at = $(am__v_at_@AM_V@) -am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) -am__v_at_0 = @ -am__v_at_1 = -SOURCES = -DIST_SOURCES = -RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \ - ctags-recursive dvi-recursive html-recursive info-recursive \ - install-data-recursive install-dvi-recursive \ - install-exec-recursive install-html-recursive \ - install-info-recursive install-pdf-recursive \ - install-ps-recursive install-recursive installcheck-recursive \ - installdirs-recursive pdf-recursive ps-recursive \ - tags-recursive uninstall-recursive -am__can_run_installinfo = \ - case $$AM_UPDATE_INFO_DIR in \ - n|no|NO) false;; \ - *) (install-info --version) >/dev/null 2>&1;; \ - esac -am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; -am__vpath_adj = case $$p in \ - $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ - *) f=$$p;; \ - esac; -am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; -am__install_max = 40 -am__nobase_strip_setup = \ - srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` -am__nobase_strip = \ - for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" -am__nobase_list = $(am__nobase_strip_setup); \ - for p in $$list; do echo "$$p $$p"; done | \ - sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ - $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ - if (++n[$$2] == $(am__install_max)) \ - { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ - END { for (dir in files) print dir, files[dir] }' -am__base_list = \ - sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ - sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' -am__uninstall_files_from_dir = { \ - test -z "$$files" \ - || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ - || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ - $(am__cd) "$$dir" && rm -f $$files; }; \ - } -am__installdirs = "$(DESTDIR)$(docdir)" -DATA = $(dist_doc_DATA) -RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \ - distclean-recursive maintainer-clean-recursive -am__recursive_targets = \ - $(RECURSIVE_TARGETS) \ - $(RECURSIVE_CLEAN_TARGETS) \ - $(am__extra_recursive_targets) -AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \ - cscope distdir dist dist-all distcheck -am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) \ - $(LISP)config.h.in -# Read a list of newline-separated strings from the standard input, -# and print each of them once, without duplicates. Input order is -# *not* preserved. -am__uniquify_input = $(AWK) '\ - BEGIN { nonempty = 0; } \ - { items[$$0] = 1; nonempty = 1; } \ - END { if (nonempty) { for (i in items) print i; }; } \ -' -# Make sure the list of sources is unique. This is necessary because, -# e.g., the same source file might be shared among _SOURCES variables -# for different programs/libraries. -am__define_uniq_tagged_files = \ - list='$(am__tagged_files)'; \ - unique=`for i in $$list; do \ - if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ - done | $(am__uniquify_input)` -ETAGS = etags -CTAGS = ctags -CSCOPE = cscope -DIST_SUBDIRS = $(SUBDIRS) -DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) -distdir = $(PACKAGE)-$(VERSION) -top_distdir = $(distdir) -am__remove_distdir = \ - if test -d "$(distdir)"; then \ - find "$(distdir)" -type d ! -perm -200 -exec chmod u+w {} ';' \ - && rm -rf "$(distdir)" \ - || { sleep 5 && rm -rf "$(distdir)"; }; \ - else :; fi -am__post_remove_distdir = $(am__remove_distdir) -am__relativize = \ - dir0=`pwd`; \ - sed_first='s,^\([^/]*\)/.*$$,\1,'; \ - sed_rest='s,^[^/]*/*,,'; \ - sed_last='s,^.*/\([^/]*\)$$,\1,'; \ - sed_butlast='s,/*[^/]*$$,,'; \ - while test -n "$$dir1"; do \ - first=`echo "$$dir1" | sed -e "$$sed_first"`; \ - if test "$$first" != "."; then \ - if test "$$first" = ".."; then \ - dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \ - dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \ - else \ - first2=`echo "$$dir2" | sed -e "$$sed_first"`; \ - if test "$$first2" = "$$first"; then \ - dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \ - else \ - dir2="../$$dir2"; \ - fi; \ - dir0="$$dir0"/"$$first"; \ - fi; \ - fi; \ - dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \ - done; \ - reldir="$$dir2" -DIST_ARCHIVES = $(distdir).tar.gz -GZIP_ENV = --best -DIST_TARGETS = dist-gzip -distuninstallcheck_listfiles = find . -type f -print -am__distuninstallcheck_listfiles = $(distuninstallcheck_listfiles) \ - | sed 's|^\./|$(prefix)/|' | grep -v '$(infodir)/dir$$' -distcleancheck_listfiles = find . -type f -print -ACLOCAL = @ACLOCAL@ -AMTAR = @AMTAR@ -AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ -APPLDFLAGS = @APPLDFLAGS@ -AR = @AR@ -AUTOCONF = @AUTOCONF@ -AUTOHEADER = @AUTOHEADER@ -AUTOMAKE = @AUTOMAKE@ -AWK = @AWK@ -BOOST_ASIO_LIB = @BOOST_ASIO_LIB@ -BOOST_CPPFLAGS = @BOOST_CPPFLAGS@ -BOOST_LDFLAGS = @BOOST_LDFLAGS@ -BOOST_SYSTEM_LIB = @BOOST_SYSTEM_LIB@ -BOOST_THREAD_LIB = @BOOST_THREAD_LIB@ -CC = @CC@ -CCDEPMODE = @CCDEPMODE@ -CFLAGS = @CFLAGS@ -CPP = @CPP@ -CPPFLAGS = @CPPFLAGS@ -CUNIT_CFLAGS = @CUNIT_CFLAGS@ -CUNIT_LIBS = @CUNIT_LIBS@ -CXX = @CXX@ -CXXCPP = @CXXCPP@ -CXXDEPMODE = @CXXDEPMODE@ -CXXFLAGS = @CXXFLAGS@ -CYGPATH_W = @CYGPATH_W@ -CYTHON = @CYTHON@ -DEFS = @DEFS@ -DEPDIR = @DEPDIR@ -DLLTOOL = @DLLTOOL@ -DSYMUTIL = @DSYMUTIL@ -DUMPBIN = @DUMPBIN@ -ECHO_C = @ECHO_C@ -ECHO_N = @ECHO_N@ -ECHO_T = @ECHO_T@ -EGREP = @EGREP@ -EXEEXT = @EXEEXT@ -FGREP = @FGREP@ -GREP = @GREP@ -HAVE_CXX11 = @HAVE_CXX11@ -INSTALL = @INSTALL@ -INSTALL_DATA = @INSTALL_DATA@ -INSTALL_PROGRAM = @INSTALL_PROGRAM@ -INSTALL_SCRIPT = @INSTALL_SCRIPT@ -INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ -JANSSON_CFLAGS = @JANSSON_CFLAGS@ -JANSSON_LIBS = @JANSSON_LIBS@ -JEMALLOC_LIBS = @JEMALLOC_LIBS@ -LD = @LD@ -LDFLAGS = @LDFLAGS@ -LIBEVENT_OPENSSL_CFLAGS = @LIBEVENT_OPENSSL_CFLAGS@ -LIBEVENT_OPENSSL_LIBS = @LIBEVENT_OPENSSL_LIBS@ -LIBEV_CFLAGS = @LIBEV_CFLAGS@ -LIBEV_LIBS = @LIBEV_LIBS@ -LIBOBJS = @LIBOBJS@ -LIBS = @LIBS@ -LIBSPDYLAY_CFLAGS = @LIBSPDYLAY_CFLAGS@ -LIBSPDYLAY_LIBS = @LIBSPDYLAY_LIBS@ -LIBTOOL = @LIBTOOL@ -LIPO = @LIPO@ -LN_S = @LN_S@ -LTLIBOBJS = @LTLIBOBJS@ -LT_AGE = @LT_AGE@ -LT_CURRENT = @LT_CURRENT@ -LT_REVISION = @LT_REVISION@ -MAKEINFO = @MAKEINFO@ -MANIFEST_TOOL = @MANIFEST_TOOL@ -MKDIR_P = @MKDIR_P@ -NM = @NM@ -NMEDIT = @NMEDIT@ -OBJDUMP = @OBJDUMP@ -OBJEXT = @OBJEXT@ -OPENSSL_CFLAGS = @OPENSSL_CFLAGS@ -OPENSSL_LIBS = @OPENSSL_LIBS@ -OTOOL = @OTOOL@ -OTOOL64 = @OTOOL64@ -PACKAGE = @PACKAGE@ -PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ -PACKAGE_NAME = @PACKAGE_NAME@ -PACKAGE_STRING = @PACKAGE_STRING@ -PACKAGE_TARNAME = @PACKAGE_TARNAME@ -PACKAGE_URL = @PACKAGE_URL@ -PACKAGE_VERSION = @PACKAGE_VERSION@ -PACKAGE_VERSION_NUM = @PACKAGE_VERSION_NUM@ -PATH_SEPARATOR = @PATH_SEPARATOR@ -PKG_CONFIG = @PKG_CONFIG@ -PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ -PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ -PYTHON = @PYTHON@ -PYTHON_CPPFLAGS = @PYTHON_CPPFLAGS@ -PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@ -PYTHON_EXTRA_LDFLAGS = @PYTHON_EXTRA_LDFLAGS@ -PYTHON_EXTRA_LIBS = @PYTHON_EXTRA_LIBS@ -PYTHON_LDFLAGS = @PYTHON_LDFLAGS@ -PYTHON_PLATFORM = @PYTHON_PLATFORM@ -PYTHON_PREFIX = @PYTHON_PREFIX@ -PYTHON_SITE_PKG = @PYTHON_SITE_PKG@ -PYTHON_VERSION = @PYTHON_VERSION@ -RANLIB = @RANLIB@ -SED = @SED@ -SET_MAKE = @SET_MAKE@ -SHELL = @SHELL@ -STRIP = @STRIP@ -TESTLDADD = @TESTLDADD@ -VERSION = @VERSION@ -WARNCFLAGS = @WARNCFLAGS@ -XML2_CONFIG = @XML2_CONFIG@ -XML_CPPFLAGS = @XML_CPPFLAGS@ -XML_LIBS = @XML_LIBS@ -ZLIB_CFLAGS = @ZLIB_CFLAGS@ -ZLIB_LIBS = @ZLIB_LIBS@ -abs_builddir = @abs_builddir@ -abs_srcdir = @abs_srcdir@ -abs_top_builddir = @abs_top_builddir@ -abs_top_srcdir = @abs_top_srcdir@ -ac_ct_AR = @ac_ct_AR@ -ac_ct_CC = @ac_ct_CC@ -ac_ct_CXX = @ac_ct_CXX@ -ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ -am__include = @am__include@ -am__leading_dot = @am__leading_dot@ -am__quote = @am__quote@ -am__tar = @am__tar@ -am__untar = @am__untar@ -bindir = @bindir@ -build = @build@ -build_alias = @build_alias@ -build_cpu = @build_cpu@ -build_os = @build_os@ -build_vendor = @build_vendor@ -builddir = @builddir@ -datadir = @datadir@ -datarootdir = @datarootdir@ -docdir = @docdir@ -dvidir = @dvidir@ -exec_prefix = @exec_prefix@ -host = @host@ -host_alias = @host_alias@ -host_cpu = @host_cpu@ -host_os = @host_os@ -host_vendor = @host_vendor@ -htmldir = @htmldir@ -includedir = @includedir@ -infodir = @infodir@ -install_sh = @install_sh@ -libdir = @libdir@ -libexecdir = @libexecdir@ -localedir = @localedir@ -localstatedir = @localstatedir@ -mandir = @mandir@ -mkdir_p = @mkdir_p@ -oldincludedir = @oldincludedir@ -pdfdir = @pdfdir@ -pkgpyexecdir = @pkgpyexecdir@ -pkgpythondir = @pkgpythondir@ -prefix = @prefix@ -program_transform_name = @program_transform_name@ -psdir = @psdir@ -pyexecdir = @pyexecdir@ -pythondir = @pythondir@ -sbindir = @sbindir@ -sharedstatedir = @sharedstatedir@ -srcdir = @srcdir@ -sysconfdir = @sysconfdir@ -target = @target@ -target_alias = @target_alias@ -target_cpu = @target_cpu@ -target_os = @target_os@ -target_vendor = @target_vendor@ -top_build_prefix = @top_build_prefix@ -top_builddir = @top_builddir@ -top_srcdir = @top_srcdir@ - -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -SUBDIRS = lib third-party src examples python tests integration-tests \ - doc contrib - -ACLOCAL_AMFLAGS = -I m4 -dist_doc_DATA = README.rst -EXTRA_DIST = nghttpx.conf.sample proxy.pac.sample android-config android-make \ - Dockerfile.android - -all: config.h - $(MAKE) $(AM_MAKEFLAGS) all-recursive - -.SUFFIXES: -am--refresh: Makefile - @: -$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) - @for dep in $?; do \ - case '$(am__configure_deps)' in \ - *$$dep*) \ - echo ' cd $(srcdir) && $(AUTOMAKE) --gnu'; \ - $(am__cd) $(srcdir) && $(AUTOMAKE) --gnu \ - && exit 0; \ - exit 1;; \ - esac; \ - done; \ - echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu Makefile'; \ - $(am__cd) $(top_srcdir) && \ - $(AUTOMAKE) --gnu Makefile -.PRECIOUS: Makefile -Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status - @case '$?' in \ - *config.status*) \ - echo ' $(SHELL) ./config.status'; \ - $(SHELL) ./config.status;; \ - *) \ - echo ' cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__depfiles_maybe)'; \ - cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__depfiles_maybe);; \ - esac; - -$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) - $(SHELL) ./config.status --recheck - -$(top_srcdir)/configure: $(am__configure_deps) - $(am__cd) $(srcdir) && $(AUTOCONF) -$(ACLOCAL_M4): $(am__aclocal_m4_deps) - $(am__cd) $(srcdir) && $(ACLOCAL) $(ACLOCAL_AMFLAGS) -$(am__aclocal_m4_deps): - -config.h: stamp-h1 - @test -f $@ || rm -f stamp-h1 - @test -f $@ || $(MAKE) $(AM_MAKEFLAGS) stamp-h1 - -stamp-h1: $(srcdir)/config.h.in $(top_builddir)/config.status - @rm -f stamp-h1 - cd $(top_builddir) && $(SHELL) ./config.status config.h -$(srcdir)/config.h.in: $(am__configure_deps) - ($(am__cd) $(top_srcdir) && $(AUTOHEADER)) - rm -f stamp-h1 - touch $@ - -distclean-hdr: - -rm -f config.h stamp-h1 -lib/includes/nghttp2/nghttp2ver.h: $(top_builddir)/config.status $(top_srcdir)/lib/includes/nghttp2/nghttp2ver.h.in - cd $(top_builddir) && $(SHELL) ./config.status $@ - -mostlyclean-libtool: - -rm -f *.lo - -clean-libtool: - -rm -rf .libs _libs - -distclean-libtool: - -rm -f libtool config.lt -install-dist_docDATA: $(dist_doc_DATA) - @$(NORMAL_INSTALL) - @list='$(dist_doc_DATA)'; test -n "$(docdir)" || list=; \ - if test -n "$$list"; then \ - echo " $(MKDIR_P) '$(DESTDIR)$(docdir)'"; \ - $(MKDIR_P) "$(DESTDIR)$(docdir)" || exit 1; \ - fi; \ - for p in $$list; do \ - if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ - echo "$$d$$p"; \ - done | $(am__base_list) | \ - while read files; do \ - echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(docdir)'"; \ - $(INSTALL_DATA) $$files "$(DESTDIR)$(docdir)" || exit $$?; \ - done - -uninstall-dist_docDATA: - @$(NORMAL_UNINSTALL) - @list='$(dist_doc_DATA)'; test -n "$(docdir)" || list=; \ - files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ - dir='$(DESTDIR)$(docdir)'; $(am__uninstall_files_from_dir) - -# This directory's subdirectories are mostly independent; you can cd -# into them and run 'make' without going through this Makefile. -# To change the values of 'make' variables: instead of editing Makefiles, -# (1) if the variable is set in 'config.status', edit 'config.status' -# (which will cause the Makefiles to be regenerated when you run 'make'); -# (2) otherwise, pass the desired values on the 'make' command line. -$(am__recursive_targets): - @fail=; \ - if $(am__make_keepgoing); then \ - failcom='fail=yes'; \ - else \ - failcom='exit 1'; \ - fi; \ - dot_seen=no; \ - target=`echo $@ | sed s/-recursive//`; \ - case "$@" in \ - distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ - *) list='$(SUBDIRS)' ;; \ - esac; \ - for subdir in $$list; do \ - echo "Making $$target in $$subdir"; \ - if test "$$subdir" = "."; then \ - dot_seen=yes; \ - local_target="$$target-am"; \ - else \ - local_target="$$target"; \ - fi; \ - ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ - || eval $$failcom; \ - done; \ - if test "$$dot_seen" = "no"; then \ - $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ - fi; test -z "$$fail" - -ID: $(am__tagged_files) - $(am__define_uniq_tagged_files); mkid -fID $$unique -tags: tags-recursive -TAGS: tags - -tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) - set x; \ - here=`pwd`; \ - if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \ - include_option=--etags-include; \ - empty_fix=.; \ - else \ - include_option=--include; \ - empty_fix=; \ - fi; \ - list='$(SUBDIRS)'; for subdir in $$list; do \ - if test "$$subdir" = .; then :; else \ - test ! -f $$subdir/TAGS || \ - set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \ - fi; \ - done; \ - $(am__define_uniq_tagged_files); \ - shift; \ - if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ - test -n "$$unique" || unique=$$empty_fix; \ - if test $$# -gt 0; then \ - $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ - "$$@" $$unique; \ - else \ - $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ - $$unique; \ - fi; \ - fi -ctags: ctags-recursive - -CTAGS: ctags -ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) - $(am__define_uniq_tagged_files); \ - test -z "$(CTAGS_ARGS)$$unique" \ - || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ - $$unique - -GTAGS: - here=`$(am__cd) $(top_builddir) && pwd` \ - && $(am__cd) $(top_srcdir) \ - && gtags -i $(GTAGS_ARGS) "$$here" -cscope: cscope.files - test ! -s cscope.files \ - || $(CSCOPE) -b -q $(AM_CSCOPEFLAGS) $(CSCOPEFLAGS) -i cscope.files $(CSCOPE_ARGS) -clean-cscope: - -rm -f cscope.files -cscope.files: clean-cscope cscopelist -cscopelist: cscopelist-recursive - -cscopelist-am: $(am__tagged_files) - list='$(am__tagged_files)'; \ - case "$(srcdir)" in \ - [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ - *) sdir=$(subdir)/$(srcdir) ;; \ - esac; \ - for i in $$list; do \ - if test -f "$$i"; then \ - echo "$(subdir)/$$i"; \ - else \ - echo "$$sdir/$$i"; \ - fi; \ - done >> $(top_builddir)/cscope.files - -distclean-tags: - -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags - -rm -f cscope.out cscope.in.out cscope.po.out cscope.files - -distdir: $(DISTFILES) - $(am__remove_distdir) - test -d "$(distdir)" || mkdir "$(distdir)" - @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ - topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ - list='$(DISTFILES)'; \ - dist_files=`for file in $$list; do echo $$file; done | \ - sed -e "s|^$$srcdirstrip/||;t" \ - -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ - case $$dist_files in \ - */*) $(MKDIR_P) `echo "$$dist_files" | \ - sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ - sort -u` ;; \ - esac; \ - for file in $$dist_files; do \ - if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ - if test -d $$d/$$file; then \ - dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ - if test -d "$(distdir)/$$file"; then \ - find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ - fi; \ - if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ - cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ - find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ - fi; \ - cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ - else \ - test -f "$(distdir)/$$file" \ - || cp -p $$d/$$file "$(distdir)/$$file" \ - || exit 1; \ - fi; \ - done - @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ - if test "$$subdir" = .; then :; else \ - $(am__make_dryrun) \ - || test -d "$(distdir)/$$subdir" \ - || $(MKDIR_P) "$(distdir)/$$subdir" \ - || exit 1; \ - dir1=$$subdir; dir2="$(distdir)/$$subdir"; \ - $(am__relativize); \ - new_distdir=$$reldir; \ - dir1=$$subdir; dir2="$(top_distdir)"; \ - $(am__relativize); \ - new_top_distdir=$$reldir; \ - echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \ - echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \ - ($(am__cd) $$subdir && \ - $(MAKE) $(AM_MAKEFLAGS) \ - top_distdir="$$new_top_distdir" \ - distdir="$$new_distdir" \ - am__remove_distdir=: \ - am__skip_length_check=: \ - am__skip_mode_fix=: \ - distdir) \ - || exit 1; \ - fi; \ - done - -test -n "$(am__skip_mode_fix)" \ - || find "$(distdir)" -type d ! -perm -755 \ - -exec chmod u+rwx,go+rx {} \; -o \ - ! -type d ! -perm -444 -links 1 -exec chmod a+r {} \; -o \ - ! -type d ! -perm -400 -exec chmod a+r {} \; -o \ - ! -type d ! -perm -444 -exec $(install_sh) -c -m a+r {} {} \; \ - || chmod -R a+r "$(distdir)" -dist-gzip: distdir - tardir=$(distdir) && $(am__tar) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).tar.gz - $(am__post_remove_distdir) - -dist-bzip2: distdir - tardir=$(distdir) && $(am__tar) | BZIP2=$${BZIP2--9} bzip2 -c >$(distdir).tar.bz2 - $(am__post_remove_distdir) - -dist-lzip: distdir - tardir=$(distdir) && $(am__tar) | lzip -c $${LZIP_OPT--9} >$(distdir).tar.lz - $(am__post_remove_distdir) - -dist-xz: distdir - tardir=$(distdir) && $(am__tar) | XZ_OPT=$${XZ_OPT--e} xz -c >$(distdir).tar.xz - $(am__post_remove_distdir) - -dist-tarZ: distdir - @echo WARNING: "Support for shar distribution archives is" \ - "deprecated." >&2 - @echo WARNING: "It will be removed altogether in Automake 2.0" >&2 - tardir=$(distdir) && $(am__tar) | compress -c >$(distdir).tar.Z - $(am__post_remove_distdir) - -dist-shar: distdir - @echo WARNING: "Support for distribution archives compressed with" \ - "legacy program 'compress' is deprecated." >&2 - @echo WARNING: "It will be removed altogether in Automake 2.0" >&2 - shar $(distdir) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).shar.gz - $(am__post_remove_distdir) - -dist-zip: distdir - -rm -f $(distdir).zip - zip -rq $(distdir).zip $(distdir) - $(am__post_remove_distdir) - -dist dist-all: - $(MAKE) $(AM_MAKEFLAGS) $(DIST_TARGETS) am__post_remove_distdir='@:' - $(am__post_remove_distdir) - -# This target untars the dist file and tries a VPATH configuration. Then -# it guarantees that the distribution is self-contained by making another -# tarfile. -distcheck: dist - case '$(DIST_ARCHIVES)' in \ - *.tar.gz*) \ - GZIP=$(GZIP_ENV) gzip -dc $(distdir).tar.gz | $(am__untar) ;;\ - *.tar.bz2*) \ - bzip2 -dc $(distdir).tar.bz2 | $(am__untar) ;;\ - *.tar.lz*) \ - lzip -dc $(distdir).tar.lz | $(am__untar) ;;\ - *.tar.xz*) \ - xz -dc $(distdir).tar.xz | $(am__untar) ;;\ - *.tar.Z*) \ - uncompress -c $(distdir).tar.Z | $(am__untar) ;;\ - *.shar.gz*) \ - GZIP=$(GZIP_ENV) gzip -dc $(distdir).shar.gz | unshar ;;\ - *.zip*) \ - unzip $(distdir).zip ;;\ - esac - chmod -R a-w $(distdir) - chmod u+w $(distdir) - mkdir $(distdir)/_build $(distdir)/_inst - chmod a-w $(distdir) - test -d $(distdir)/_build || exit 0; \ - dc_install_base=`$(am__cd) $(distdir)/_inst && pwd | sed -e 's,^[^:\\/]:[\\/],/,'` \ - && dc_destdir="$${TMPDIR-/tmp}/am-dc-$$$$/" \ - && am__cwd=`pwd` \ - && $(am__cd) $(distdir)/_build \ - && ../configure \ - $(AM_DISTCHECK_CONFIGURE_FLAGS) \ - $(DISTCHECK_CONFIGURE_FLAGS) \ - --srcdir=.. --prefix="$$dc_install_base" \ - && $(MAKE) $(AM_MAKEFLAGS) \ - && $(MAKE) $(AM_MAKEFLAGS) dvi \ - && $(MAKE) $(AM_MAKEFLAGS) check \ - && $(MAKE) $(AM_MAKEFLAGS) install \ - && $(MAKE) $(AM_MAKEFLAGS) installcheck \ - && $(MAKE) $(AM_MAKEFLAGS) uninstall \ - && $(MAKE) $(AM_MAKEFLAGS) distuninstallcheck_dir="$$dc_install_base" \ - distuninstallcheck \ - && chmod -R a-w "$$dc_install_base" \ - && ({ \ - (cd ../.. && umask 077 && mkdir "$$dc_destdir") \ - && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" install \ - && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" uninstall \ - && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" \ - distuninstallcheck_dir="$$dc_destdir" distuninstallcheck; \ - } || { rm -rf "$$dc_destdir"; exit 1; }) \ - && rm -rf "$$dc_destdir" \ - && $(MAKE) $(AM_MAKEFLAGS) dist \ - && rm -rf $(DIST_ARCHIVES) \ - && $(MAKE) $(AM_MAKEFLAGS) distcleancheck \ - && cd "$$am__cwd" \ - || exit 1 - $(am__post_remove_distdir) - @(echo "$(distdir) archives ready for distribution: "; \ - list='$(DIST_ARCHIVES)'; for i in $$list; do echo $$i; done) | \ - sed -e 1h -e 1s/./=/g -e 1p -e 1x -e '$$p' -e '$$x' -distuninstallcheck: - @test -n '$(distuninstallcheck_dir)' || { \ - echo 'ERROR: trying to run $@ with an empty' \ - '$$(distuninstallcheck_dir)' >&2; \ - exit 1; \ - }; \ - $(am__cd) '$(distuninstallcheck_dir)' || { \ - echo 'ERROR: cannot chdir into $(distuninstallcheck_dir)' >&2; \ - exit 1; \ - }; \ - test `$(am__distuninstallcheck_listfiles) | wc -l` -eq 0 \ - || { echo "ERROR: files left after uninstall:" ; \ - if test -n "$(DESTDIR)"; then \ - echo " (check DESTDIR support)"; \ - fi ; \ - $(distuninstallcheck_listfiles) ; \ - exit 1; } >&2 -distcleancheck: distclean - @if test '$(srcdir)' = . ; then \ - echo "ERROR: distcleancheck can only run from a VPATH build" ; \ - exit 1 ; \ - fi - @test `$(distcleancheck_listfiles) | wc -l` -eq 0 \ - || { echo "ERROR: files left in build directory after distclean:" ; \ - $(distcleancheck_listfiles) ; \ - exit 1; } >&2 -check-am: all-am -check: check-recursive -all-am: Makefile $(DATA) config.h -installdirs: installdirs-recursive -installdirs-am: - for dir in "$(DESTDIR)$(docdir)"; do \ - test -z "$$dir" || $(MKDIR_P) "$$dir"; \ - done -install: install-recursive -install-exec: install-exec-recursive -install-data: install-data-recursive -uninstall: uninstall-recursive - -install-am: all-am - @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am - -installcheck: installcheck-recursive -install-strip: - if test -z '$(STRIP)'; then \ - $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ - install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ - install; \ - else \ - $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ - install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ - "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ - fi -mostlyclean-generic: - -clean-generic: - -distclean-generic: - -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) - -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) - -maintainer-clean-generic: - @echo "This command is intended for maintainers to use" - @echo "it deletes files that may require special tools to rebuild." -clean: clean-recursive - -clean-am: clean-generic clean-libtool mostlyclean-am - -distclean: distclean-recursive - -rm -f $(am__CONFIG_DISTCLEAN_FILES) - -rm -f Makefile -distclean-am: clean-am distclean-generic distclean-hdr \ - distclean-libtool distclean-tags - -dvi: dvi-recursive - -dvi-am: - -html: html-recursive - -html-am: - -info: info-recursive - -info-am: - -install-data-am: install-dist_docDATA - -install-dvi: install-dvi-recursive - -install-dvi-am: - -install-exec-am: - -install-html: install-html-recursive - -install-html-am: - -install-info: install-info-recursive - -install-info-am: - -install-man: - -install-pdf: install-pdf-recursive - -install-pdf-am: - -install-ps: install-ps-recursive - -install-ps-am: - -installcheck-am: - -maintainer-clean: maintainer-clean-recursive - -rm -f $(am__CONFIG_DISTCLEAN_FILES) - -rm -rf $(top_srcdir)/autom4te.cache - -rm -f Makefile -maintainer-clean-am: distclean-am maintainer-clean-generic - -mostlyclean: mostlyclean-recursive - -mostlyclean-am: mostlyclean-generic mostlyclean-libtool - -pdf: pdf-recursive - -pdf-am: - -ps: ps-recursive - -ps-am: - -uninstall-am: uninstall-dist_docDATA - -.MAKE: $(am__recursive_targets) all install-am install-strip - -.PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am \ - am--refresh check check-am clean clean-cscope clean-generic \ - clean-libtool cscope cscopelist-am ctags ctags-am dist \ - dist-all dist-bzip2 dist-gzip dist-lzip dist-shar dist-tarZ \ - dist-xz dist-zip distcheck distclean distclean-generic \ - distclean-hdr distclean-libtool distclean-tags distcleancheck \ - distdir distuninstallcheck dvi dvi-am html html-am info \ - info-am install install-am install-data install-data-am \ - install-dist_docDATA install-dvi install-dvi-am install-exec \ - install-exec-am install-html install-html-am install-info \ - install-info-am install-man install-pdf install-pdf-am \ - install-ps install-ps-am install-strip installcheck \ - installcheck-am installdirs installdirs-am maintainer-clean \ - maintainer-clean-generic mostlyclean mostlyclean-generic \ - mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \ - uninstall-am uninstall-dist_docDATA - - -.PHONY: clang-format - -# Format source files using clang-format. Don't format source files -# under third-party directory since we are not responsible for thier -# coding style. -clang-format: - CLANGFORMAT=`git config --get clangformat.binary`; \ - test -z $${CLANGFORMAT} && CLANGFORMAT="clang-format"; \ - $${CLANGFORMAT} -i lib/*.{c,h} lib/includes/nghttp2/*.h \ - src/*.{c,cc,h} src/includes/nghttp2/*.h examples/*.{c,cc} \ - tests/*.{c,h} - -# Tell versions [3.59,3.63) of GNU make to not export all variables. -# Otherwise a system limit (for SysV at least) may be exceeded. -.NOEXPORT: diff --git a/NEWS b/NEWS deleted file mode 100644 index e69de29..0000000 diff --git a/README b/README deleted file mode 100644 index 5ccc0ea..0000000 --- a/README +++ /dev/null @@ -1 +0,0 @@ -See README.rst diff --git a/README.rst b/README.rst deleted file mode 100644 index 82e8ecc..0000000 --- a/README.rst +++ /dev/null @@ -1,1427 +0,0 @@ -nghttp2 - HTTP/2 C Library -========================== - -This is an implementation of the Hypertext Transfer Protocol version 2 -in C. - -The framing layer of HTTP/2 is implemented as a reusable C -library. On top of that, we have implemented an HTTP/2 client, server -and proxy. We have also developed load test and benchmarking tools for -HTTP/2 and SPDY. - -An HPACK encoder and decoder are available as a public API. - -An experimental high level C++ library is also available. - -We have Python bindings of this library, but we do not have full -code coverage yet. - -Development Status ------------------- - -We have implemented `RFC 7540 `_ -HTTP/2 and `RFC 7541 `_ HPACK - -Header Compression for HTTP/2 - -The nghttp2 code base was forked from the spdylay -(https://github.com/tatsuhiro-t/spdylay) project. - -Public Test Server ------------------- - -The following endpoints are available to try out our nghttp2 -implementation. - -* https://nghttp2.org/ (TLS + ALPN/NPN) - - This endpoint supports ``h2``, ``h2-16``, ``h2-14``, ``spdy/3.1`` - and ``http/1.1`` via ALPN/NPN and requires TLSv1.2 for HTTP/2 - connection. - -* http://nghttp2.org/ (HTTP Upgrade and HTTP/2 Direct) - - ``h2c`` and ``http/1.1``. - -Requirements ------------- - -The following package is required to build the libnghttp2 library: - -* pkg-config >= 0.20 - -To build and run the unit test programs, the following package is -required: - -* cunit >= 2.1 - -To build the documentation, you need to install: - -* sphinx (http://sphinx-doc.org/) - -To build and run the application programs (``nghttp``, ``nghttpd`` and -``nghttpx``) in the ``src`` directory, the following packages are -required: - -* OpenSSL >= 1.0.1 -* libev >= 4.15 -* zlib >= 1.2.3 - -ALPN support requires OpenSSL >= 1.0.2 (released 22 January 2015). - -To enable the SPDY protocol in the application program ``nghttpx`` and -``h2load``, the following package is required: - -* spdylay >= 1.3.2 - -To enable ``-a`` option (getting linked assets from the downloaded -resource) in ``nghttp``, the following package is required: - -* libxml2 >= 2.7.7 - -The HPACK tools require the following package: - -* jansson >= 2.5 - -To build sources under the examples directory, libevent is required: - -* libevent-openssl >= 2.0.8 - -To mitigate heap fragmentation in long running server programs -(``nghttpd`` and ``nghttpx``), jemalloc is recommended: - -* jemalloc - -libnghttp2_asio C++ library requires the following packages: - -* libboost-dev >= 1.54.0 -* libboost-thread-dev >= 1.54.0 - -The Python bindings require the following packages: - -* cython >= 0.19 -* python >= 2.7 - -If you are using Ubuntu 14.04 LTS (trusty), run the following to install the needed packages:: - - sudo apt-get install make binutils autoconf automake autotools-dev libtool pkg-config \ - zlib1g-dev libcunit1-dev libssl-dev libxml2-dev libev-dev libevent-dev libjansson-dev \ - libjemalloc-dev cython python3.4-dev - -spdylay is not packaged in Ubuntu, so you need to build it yourself: -http://tatsuhiro-t.github.io/spdylay/ - -Building 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 - -To compile the source code, gcc >= 4.8.3 or clang >= 3.4 is required. - -.. note:: - - Mac OS X users may need the ``--disable-threads`` configure option to - disable multi-threading in nghttpd, nghttpx and h2load to prevent - them from crashing. A patch is welcome to make multi threading work - on Mac OS X platform. - -Notes for building on Windows (Mingw/Cygwin) --------------------------------------------- - -Under Mingw environment, you can only compile the library, it's -``libnghttp2-X.dll`` and ``libnghttp2.a``. - -If you want to compile the applications(``h2load``, ``nghttp``, -``nghttpx``, ``nghttpd``), you need to use the Cygwin environment. - -Under Cygwin environment, to compile the applications you need to -compile and install the libev first. - -Secondly, you need to undefine the macro ``__STRICT_ANSI__``, if you -not, the functions ``fdopen``, ``fileno`` and ``strptime`` will not -available. - -the sample command like this:: - - $ export CFLAGS="-U__STRICT_ANSI__ -I$libev_PREFIX/include -L$libev_PREFIX/lib" - $ export CXXFLAGS=$CFLAGS - $ ./configure - $ make - -If you want to compile the applications under ``examples/``, you need -to remove or rename the ``event.h`` from libev's installation, because -it conflicts with libevent's installation. - -Building the documentation --------------------------- - -.. note:: - - Documentation is still incomplete. - -To build the documentation, run:: - - $ make html - -The documents will be generated under ``doc/manual/html/``. - -The generated documents will not be installed with ``make install``. - -The online documentation is available at -https://nghttp2.org/documentation/ - -Unit tests ----------- - -Unit tests are done by simply running ``make check``. - -Integration tests ------------------ - -We have the integration tests for the nghttpx proxy server. The tests are -written in the `Go programming language `_ and uses -its testing framework. We depend on the following libraries: - -* https://github.com/bradfitz/http2 -* https://github.com/tatsuhiro-t/go-nghttp2 -* https://golang.org/x/net/spdy - -To download the above packages, after settings ``GOPATH``, run the -following command under ``integration-tests`` directory:: - - $ make itprep - -To run the tests, run the following command under -``integration-tests`` directory:: - - $ make it - -Inside the tests, we use port 3009 to run the test subject server. - -Migration from v0.7.15 or earlier ---------------------------------- - -nghttp2 v1.0.0 introduced several backward incompatible changes. In -this section, we describe these changes and how to migrate to v1.0.0. - -ALPN protocol ID is now ``h2`` and ``h2c`` -++++++++++++++++++++++++++++++++++++++++++ - -Previously we announced ``h2-14`` and ``h2c-14``. v1.0.0 implements -final protocol version, and we changed ALPN ID to ``h2`` and ``h2c``. -The macros ``NGHTTP2_PROTO_VERSION_ID``, -``NGHTTP2_PROTO_VERSION_ID_LEN``, -``NGHTTP2_CLEARTEXT_PROTO_VERSION_ID``, and -``NGHTTP2_CLEARTEXT_PROTO_VERSION_ID_LEN`` have been updated to -reflect this change. - -Basically, existing applications do not have to do anything, just -recompiling is enough for this change. - -Use word "client magic" where we use "client connection preface" -++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ - -We use "client connection preface" to mean first 24 bytes of client -connection preface. This is technically not correct, since client -connection preface is composed of 24 bytes client magic byte string -followed by SETTINGS frame. For clarification, we call "client magic" -for this 24 bytes byte string and updated API. - -* ``NGHTTP2_CLIENT_CONNECTION_PREFACE`` was replaced with - ``NGHTTP2_CLIENT_MAGIC``. -* ``NGHTTP2_CLIENT_CONNECTION_PREFACE_LEN`` was replaced with - ``NGHTTP2_CLIENT_MAGIC_LEN``. -* ``NGHTTP2_BAD_PREFACE`` was renamed as ``NGHTTP2_BAD_CLIENT_MAGIC`` - -The alreay deprecated ``NGHTTP2_CLIENT_CONNECTION_HEADER`` and -``NGHTTP2_CLIENT_CONNECTION_HEADER_LEN`` were removed. - -If application uses these macros, just replace old ones with new ones. -Since v1.0.0, client magic is sent by library (see next subsection), -so client application may just remove these macro use. - -Client magic is sent by library -+++++++++++++++++++++++++++++++ - -Previously nghttp2 library did not send client magic, which is first -24 bytes byte string of client connection preface, and client -applications have to send it by themselves. Since v1.0.0, client -magic is sent by library via first call of ``nghttp2_session_send()`` -or ``nghttp2_session_mem_send()``. - -The client applications which send client magic must remove the -relevant code. - -Remove HTTP Alternative Services (Alt-Svc) related code -+++++++++++++++++++++++++++++++++++++++++++++++++++++++ - -Alt-Svc specification is not finalized yet. To make our API stable, -we have decided to remove all Alt-Svc related API from nghttp2. - -* ``NGHTTP2_EXT_ALTSVC`` was removed. -* ``nghttp2_ext_altsvc`` was removed. - -We have already removed the functionality of Alt-Svc in v0.7 series -and they have been essentially noop. The application using these -macro and struct, remove those lines. - -Use nghttp2_error in nghttp2_on_invalid_frame_recv_callback -+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ - -Previously ``nghttp2_on_invalid_frame_recv_cb_called`` took the -``error_code``, defined in ``nghttp2_error_code``, as parameter. But -they are not detailed enough to debug. Therefore, we decided to use -more detailed ``nghttp2_error`` values instead. - -The application using this callback should update the callback -signature. If it treats ``error_code`` as HTTP/2 error code, update -the code so that it is treated as ``nghttp2_error``. - -Receive client magic by default -+++++++++++++++++++++++++++++++ - -Previously nghttp2 did not process client magic (24 bytes byte -string). To make it deal with it, we had to use -``nghttp2_option_set_recv_client_preface()``. Since v1.0.0, nghttp2 -processes client magic by default and -``nghttp2_option_set_recv_client_preface()`` was removed. - -Some application may want to disable this behaviour, so we added -``nghttp2_option_set_no_recv_client_magic()`` to achieve this. - -The application using ``nghttp2_option_set_recv_client_preface()`` -with nonzero value, just remove it. - -The application using ``nghttp2_option_set_recv_client_preface()`` -with zero value or not using it must use -``nghttp2_option_set_no_recv_client_magic()`` with nonzero value. - -Client, Server and Proxy programs ---------------------------------- - -The ``src`` directory contains the HTTP/2 client, server and proxy programs. - -nghttp - client -+++++++++++++++ - -``nghttp`` is a HTTP/2 client. It can connect to the HTTP/2 server -with prior knowledge, HTTP Upgrade and NPN/ALPN TLS extension. - -It has verbose output mode for framing information. Here is sample -output from ``nghttp`` client:: - - $ nghttp -nv https://nghttp2.org - [ 0.067] Connected - The negotiated protocol: h2 - [ 0.135] send SETTINGS frame - (niv=2) - [SETTINGS_MAX_CONCURRENT_STREAMS(0x03):100] - [SETTINGS_INITIAL_WINDOW_SIZE(0x04):65535] - [ 0.135] send PRIORITY frame - (dep_stream_id=0, weight=201, exclusive=0) - [ 0.135] send PRIORITY frame - (dep_stream_id=0, weight=101, exclusive=0) - [ 0.135] send PRIORITY frame - (dep_stream_id=0, weight=1, exclusive=0) - [ 0.135] send PRIORITY frame - (dep_stream_id=7, weight=1, exclusive=0) - [ 0.135] send PRIORITY frame - (dep_stream_id=3, weight=1, exclusive=0) - [ 0.135] send HEADERS frame - ; END_STREAM | END_HEADERS | PRIORITY - (padlen=0, dep_stream_id=11, weight=16, exclusive=0) - ; Open new stream - :method: GET - :path: / - :scheme: https - :authority: nghttp2.org - accept: */* - accept-encoding: gzip, deflate - user-agent: nghttp2/1.0.0-DEV - [ 0.135] recv SETTINGS frame - (niv=2) - [SETTINGS_MAX_CONCURRENT_STREAMS(0x03):100] - [SETTINGS_INITIAL_WINDOW_SIZE(0x04):65535] - [ 0.135] send SETTINGS frame - ; ACK - (niv=0) - [ 0.165] recv SETTINGS frame - ; ACK - (niv=0) - [ 0.166] recv (stream_id=13) :status: 200 - [ 0.166] recv (stream_id=13) date: Fri, 15 May 2015 14:45:22 GMT - [ 0.166] recv (stream_id=13) content-type: text/html - [ 0.166] recv (stream_id=13) last-modified: Fri, 15 May 2015 14:20:46 GMT - [ 0.166] recv (stream_id=13) etag: W/"555600be-1a7f" - [ 0.166] recv (stream_id=13) link: ; rel=preload; as=stylesheet - [ 0.166] recv (stream_id=13) content-encoding: gzip - [ 0.166] recv (stream_id=13) server: nghttpx nghttp2/1.0.0-DEV - [ 0.166] recv (stream_id=13) via: 1.1 nghttpx - [ 0.166] recv (stream_id=13) strict-transport-security: max-age=31536000 - [ 0.166] recv HEADERS frame - ; END_HEADERS - (padlen=0) - ; First response header - [ 0.166] recv (stream_id=13) :method: GET - [ 0.166] recv (stream_id=13) :scheme: https - [ 0.166] recv (stream_id=13) :path: /stylesheets/screen.css - [ 0.166] recv (stream_id=13) :authority: nghttp2.org - [ 0.166] recv (stream_id=13) accept-encoding: gzip, deflate - [ 0.166] recv (stream_id=13) user-agent: nghttp2/1.0.0-DEV - [ 0.166] recv PUSH_PROMISE frame - ; END_HEADERS - (padlen=0, promised_stream_id=2) - [ 0.166] recv DATA frame - ; END_STREAM - [ 0.167] recv (stream_id=2) :status: 200 - [ 0.167] recv (stream_id=2) date: Fri, 15 May 2015 14:45:22 GMT - [ 0.167] recv (stream_id=2) content-type: text/css - [ 0.167] recv (stream_id=2) last-modified: Fri, 15 May 2015 14:20:46 GMT - [ 0.167] recv (stream_id=2) etag: W/"555600be-9845" - [ 0.167] recv (stream_id=2) content-encoding: gzip - [ 0.167] recv (stream_id=2) server: nghttpx nghttp2/1.0.0-DEV - [ 0.167] recv (stream_id=2) via: 1.1 nghttpx - [ 0.167] recv (stream_id=2) strict-transport-security: max-age=31536000 - [ 0.167] recv HEADERS frame - ; END_HEADERS - (padlen=0) - ; First push response header - [ 0.196] recv DATA frame - ; END_STREAM - [ 0.196] send GOAWAY frame - (last_stream_id=2, error_code=NO_ERROR(0x00), opaque_data(0)=[]) - -The HTTP Upgrade is performed like so:: - - $ nghttp -nvu http://nghttp2.org - [ 0.137] Connected - [ 0.137] HTTP Upgrade request - GET / HTTP/1.1 - Host: nghttp2.org - Connection: Upgrade, HTTP2-Settings - Upgrade: h2c - HTTP2-Settings: AAMAAABkAAQAAP__ - Accept: */* - User-Agent: nghttp2/1.0.0-DEV - - - [ 0.156] HTTP Upgrade response - HTTP/1.1 101 Switching Protocols - Connection: Upgrade - Upgrade: h2c - - - [ 0.156] HTTP Upgrade success - [ 0.157] send SETTINGS frame - (niv=2) - [SETTINGS_MAX_CONCURRENT_STREAMS(0x03):100] - [SETTINGS_INITIAL_WINDOW_SIZE(0x04):65535] - [ 0.157] send PRIORITY frame - (dep_stream_id=0, weight=201, exclusive=0) - [ 0.157] send PRIORITY frame - (dep_stream_id=0, weight=101, exclusive=0) - [ 0.157] send PRIORITY frame - (dep_stream_id=0, weight=1, exclusive=0) - [ 0.157] send PRIORITY frame - (dep_stream_id=7, weight=1, exclusive=0) - [ 0.157] send PRIORITY frame - (dep_stream_id=3, weight=1, exclusive=0) - [ 0.157] send PRIORITY frame - (dep_stream_id=11, weight=16, exclusive=0) - [ 0.157] recv SETTINGS frame - (niv=2) - [SETTINGS_MAX_CONCURRENT_STREAMS(0x03):100] - [SETTINGS_INITIAL_WINDOW_SIZE(0x04):65535] - [ 0.157] recv (stream_id=1) :status: 200 - [ 0.157] recv (stream_id=1) date: Fri, 15 May 2015 14:46:08 GMT - [ 0.157] recv (stream_id=1) content-type: text/html - [ 0.157] recv (stream_id=1) content-length: 6783 - [ 0.157] recv (stream_id=1) last-modified: Fri, 15 May 2015 14:20:46 GMT - [ 0.157] recv (stream_id=1) etag: "555600be-1a7f" - [ 0.157] recv (stream_id=1) link: ; rel=preload; as=stylesheet - [ 0.157] recv (stream_id=1) accept-ranges: bytes - [ 0.157] recv (stream_id=1) server: nghttpx nghttp2/1.0.0-DEV - [ 0.157] recv (stream_id=1) via: 1.1 nghttpx - [ 0.157] recv HEADERS frame - ; END_HEADERS - (padlen=0) - ; First response header - [ 0.157] recv (stream_id=1) :method: GET - [ 0.157] recv (stream_id=1) :scheme: http - [ 0.157] recv (stream_id=1) :path: /stylesheets/screen.css - [ 0.157] recv (stream_id=1) host: nghttp2.org - [ 0.157] recv (stream_id=1) user-agent: nghttp2/1.0.0-DEV - [ 0.157] recv PUSH_PROMISE frame - ; END_HEADERS - (padlen=0, promised_stream_id=2) - [ 0.157] send SETTINGS frame - ; ACK - (niv=0) - [ 0.161] recv DATA frame - ; END_STREAM - [ 0.162] recv (stream_id=2) :status: 200 - [ 0.162] recv (stream_id=2) date: Fri, 15 May 2015 14:46:08 GMT - [ 0.162] recv (stream_id=2) content-type: text/css - [ 0.162] recv (stream_id=2) content-length: 38981 - [ 0.162] recv (stream_id=2) last-modified: Fri, 15 May 2015 14:20:46 GMT - [ 0.162] recv (stream_id=2) etag: "555600be-9845" - [ 0.162] recv (stream_id=2) accept-ranges: bytes - [ 0.162] recv (stream_id=2) server: nghttpx nghttp2/1.0.0-DEV - [ 0.162] recv (stream_id=2) via: 1.1 nghttpx - [ 0.162] recv HEADERS frame - ; END_HEADERS - (padlen=0) - ; First push response header - [ 0.191] recv DATA frame - [ 0.215] recv DATA frame - [ 0.215] send WINDOW_UPDATE frame - (window_size_increment=33322) - [ 0.238] send WINDOW_UPDATE frame - (window_size_increment=33549) - [ 0.238] recv DATA frame - ; END_STREAM - [ 0.238] recv SETTINGS frame - ; ACK - (niv=0) - [ 0.238] send GOAWAY frame - (last_stream_id=2, error_code=NO_ERROR(0x00), opaque_data(0)=[]) - -Using the ``-s`` option, ``nghttp`` prints out some timing information for -requests, sorted by completion time:: - - $ nghttp -nas https://nghttp2.org/ - ***** Statistics ***** - - Request timing: - responseEnd: the time when last byte of response was received - relative to connectEnd - requestStart: the time just before first byte of request was sent - relative to connectEnd. If '*' is shown, this was - pushed by server. - process: responseEnd - requestStart - code: HTTP status code - size: number of bytes received as response body without - inflation. - URI: request URI - - see http://www.w3.org/TR/resource-timing/#processing-model - - sorted by 'complete' - - id responseEnd requestStart process code size request path - 13 +37.19ms +280us 36.91ms 200 2K / - 2 +72.65ms * +36.38ms 36.26ms 200 8K /stylesheets/screen.css - 17 +77.43ms +38.67ms 38.75ms 200 3K /javascripts/octopress.js - 15 +78.12ms +38.66ms 39.46ms 200 3K /javascripts/modernizr-2.0.js - -Using the ``-r`` option, ``nghttp`` writes more detailed timing data to -the given file in HAR format. - -nghttpd - server -++++++++++++++++ - -``nghttpd`` is a multi-threaded static web server. - -By default, it uses SSL/TLS connection. Use ``--no-tls`` option to -disable it. - -``nghttpd`` only accepts HTTP/2 connections via NPN/ALPN or direct -HTTP/2 connections. No HTTP Upgrade is supported. - -The ``-p`` option allows users to configure server push. - -Just like ``nghttp``, it has a verbose output mode for framing -information. Here is sample output from ``nghttpd``:: - - $ nghttpd --no-tls -v 8080 - IPv4: listen 0.0.0.0:8080 - IPv6: listen :::8080 - [id=1] [ 1.521] send SETTINGS frame - (niv=1) - [SETTINGS_MAX_CONCURRENT_STREAMS(0x03):100] - [id=1] [ 1.521] recv SETTINGS frame - (niv=2) - [SETTINGS_MAX_CONCURRENT_STREAMS(0x03):100] - [SETTINGS_INITIAL_WINDOW_SIZE(0x04):65535] - [id=1] [ 1.521] recv SETTINGS frame - ; ACK - (niv=0) - [id=1] [ 1.521] recv PRIORITY frame - (dep_stream_id=0, weight=201, exclusive=0) - [id=1] [ 1.521] recv PRIORITY frame - (dep_stream_id=0, weight=101, exclusive=0) - [id=1] [ 1.521] recv PRIORITY frame - (dep_stream_id=0, weight=1, exclusive=0) - [id=1] [ 1.521] recv PRIORITY frame - (dep_stream_id=7, weight=1, exclusive=0) - [id=1] [ 1.521] recv PRIORITY frame - (dep_stream_id=3, weight=1, exclusive=0) - [id=1] [ 1.521] recv (stream_id=13) :method: GET - [id=1] [ 1.521] recv (stream_id=13) :path: / - [id=1] [ 1.521] recv (stream_id=13) :scheme: http - [id=1] [ 1.521] recv (stream_id=13) :authority: localhost:8080 - [id=1] [ 1.521] recv (stream_id=13) accept: */* - [id=1] [ 1.521] recv (stream_id=13) accept-encoding: gzip, deflate - [id=1] [ 1.521] recv (stream_id=13) user-agent: nghttp2/1.0.0-DEV - [id=1] [ 1.521] recv HEADERS frame - ; END_STREAM | END_HEADERS | PRIORITY - (padlen=0, dep_stream_id=11, weight=16, exclusive=0) - ; Open new stream - [id=1] [ 1.521] send SETTINGS frame - ; ACK - (niv=0) - [id=1] [ 1.521] send HEADERS frame - ; END_HEADERS - (padlen=0) - ; First response header - :status: 200 - server: nghttpd nghttp2/1.0.0-DEV - content-length: 10 - cache-control: max-age=3600 - date: Fri, 15 May 2015 14:49:04 GMT - last-modified: Tue, 30 Sep 2014 12:40:52 GMT - [id=1] [ 1.522] send DATA frame - ; END_STREAM - [id=1] [ 1.522] stream_id=13 closed - [id=1] [ 1.522] recv GOAWAY frame - (last_stream_id=0, error_code=NO_ERROR(0x00), opaque_data(0)=[]) - [id=1] [ 1.522] closed - -nghttpx - proxy -+++++++++++++++ - -``nghttpx`` is a multi-threaded reverse proxy for HTTP/2, SPDY and -HTTP/1.1, and powers http://nghttp2.org and supports HTTP/2 server -push. - -``nghttpx`` implements `important performance-oriented features -`_ in TLS, such as -session IDs, session tickets (with automatic key rotation), OCSP -stapling, dynamic record sizing, ALPN/NPN, forward secrecy and SPDY & -HTTP/2. - -``nghttpx`` has several operational modes: - -================== ============================ ============== ============= -Mode option Frontend Backend Note -================== ============================ ============== ============= -default mode HTTP/2, SPDY, HTTP/1.1 (TLS) HTTP/1.1 Reverse proxy -``--http2-proxy`` HTTP/2, SPDY, HTTP/1.1 (TLS) HTTP/1.1 SPDY proxy -``--http2-bridge`` HTTP/2, SPDY, HTTP/1.1 (TLS) HTTP/2 (TLS) -``--client`` HTTP/2, HTTP/1.1 HTTP/2 (TLS) -``--client-proxy`` HTTP/2, HTTP/1.1 HTTP/2 (TLS) Forward proxy -================== ============================ ============== ============= - -The interesting mode at the moment is the default mode. It works like -a reverse proxy and listens for HTTP/2, SPDY and HTTP/1.1 and can be -deployed as a SSL/TLS terminator for existing web server. - -The default mode, ``--http2-proxy`` and ``--http2-bridge`` modes use -SSL/TLS in the frontend connection by default. To disable SSL/TLS, -use the ``--frontend-no-tls`` option. If that option is used, SPDY is -disabled in the frontend and incoming HTTP/1.1 connections can be -upgraded to HTTP/2 through HTTP Upgrade. - -The ``--http2-bridge``, ``--client`` and ``--client-proxy`` modes use -SSL/TLS in the backend connection by default. To disable SSL/TLS, use -the ``--backend-no-tls`` option. - -``nghttpx`` supports a configuration file. See the ``--conf`` option and -sample configuration file ``nghttpx.conf.sample``. - -In the default mode, (without any of ``--http2-proxy``, -``--http2-bridge``, ``--client-proxy`` and ``--client`` options), -``nghttpx`` works as reverse proxy to the backend server:: - - Client <-- (HTTP/2, SPDY, HTTP/1.1) --> nghttpx <-- (HTTP/1.1) --> Web Server - [reverse proxy] - -With the ``--http2-proxy`` option, it works as a so called secure proxy (aka -SPDY proxy):: - - Client <-- (HTTP/2, SPDY, HTTP/1.1) --> nghttpx <-- (HTTP/1.1) --> Proxy - [secure proxy] (e.g., Squid, ATS) - -The ``Client`` in the above example needs to be configured to use -``nghttpx`` as secure proxy. - -At the time of this writing, Chrome is the only browser which supports -secure proxy. One way to configure Chrome to use a secure proxy is -to create a proxy.pac script like this: - -.. code-block:: javascript - - function FindProxyForURL(url, host) { - return "HTTPS SERVERADDR:PORT"; - } - -``SERVERADDR`` and ``PORT`` is the hostname/address and port of the -machine nghttpx is running on. Please note that Chrome requires a valid -certificate for secure proxy. - -Then run Chrome with the following arguments:: - - $ google-chrome --proxy-pac-url=file:///path/to/proxy.pac --use-npn - -With ``--http2-bridge``, it accepts HTTP/2, SPDY and HTTP/1.1 -connections and communicates with the backend in HTTP/2:: - - Client <-- (HTTP/2, SPDY, HTTP/1.1) --> nghttpx <-- (HTTP/2) --> Web or HTTP/2 Proxy etc - (e.g., nghttpx -s) - -With ``--client-proxy``, it works as a forward proxy and expects -that the backend is an HTTP/2 proxy:: - - Client <-- (HTTP/2, HTTP/1.1) --> nghttpx <-- (HTTP/2) --> HTTP/2 Proxy - [forward proxy] (e.g., nghttpx -s) - -The ``Client`` needs to be configured to use nghttpx as a forward -proxy. The frontend HTTP/1.1 connection can be upgraded to HTTP/2 -through HTTP Upgrade. With the above configuration, one can use -HTTP/1.1 client to access and test their HTTP/2 servers. - -With ``--client``, it works as a reverse proxy and expects that -the backend is an HTTP/2 Web server:: - - Client <-- (HTTP/2, HTTP/1.1) --> nghttpx <-- (HTTP/2) --> Web Server - [reverse proxy] - -The frontend HTTP/1.1 connection can be upgraded to HTTP/2 -through HTTP Upgrade. - -For the operation modes which talk to the backend in HTTP/2 over -SSL/TLS, the backend connections can be tunneled through an HTTP proxy. -The proxy is specified using ``--backend-http-proxy-uri``. The -following figure illustrates the example of the ``--http2-bridge`` and -``--backend-http-proxy-uri`` options to talk to the outside HTTP/2 -proxy through an HTTP proxy:: - - Client <-- (HTTP/2, SPDY, HTTP/1.1) --> nghttpx <-- (HTTP/2) -- - - --===================---> HTTP/2 Proxy - (HTTP proxy tunnel) (e.g., nghttpx -s) - -Benchmarking tool ------------------ - -The ``h2load`` program is a benchmarking tool for HTTP/2 and SPDY. -The SPDY support is enabled if the program was built with the spdylay -library. The UI of ``h2load`` is heavily inspired by ``weighttp`` -(https://github.com/lighttpd/weighttp). The typical usage is as -follows:: - - $ h2load -n100000 -c100 -m100 https://localhost:8443/ - starting benchmark... - spawning thread #0: 100 concurrent clients, 100000 total requests - Protocol: TLSv1.2 - Cipher: ECDHE-RSA-AES128-GCM-SHA256 - Server Temp Key: ECDH P-256 256 bits - progress: 10% done - progress: 20% done - progress: 30% done - progress: 40% done - progress: 50% done - progress: 60% done - progress: 70% done - progress: 80% done - progress: 90% done - progress: 100% done - - finished in 771.26ms, 129658 req/s, 4.71MB/s - requests: 100000 total, 100000 started, 100000 done, 100000 succeeded, 0 failed, 0 errored - status codes: 100000 2xx, 0 3xx, 0 4xx, 0 5xx - traffic: 3812300 bytes total, 1009900 bytes headers, 1000000 bytes data - min max mean sd +/- sd - time for request: 25.12ms 124.55ms 51.07ms 15.36ms 84.87% - time for connect: 208.94ms 254.67ms 241.38ms 7.95ms 63.00% - time to 1st byte: 209.11ms 254.80ms 241.51ms 7.94ms 63.00% - -The above example issued total 100,000 requests, using 100 concurrent -clients (in other words, 100 HTTP/2 sessions), and a maximum of 100 streams -per client. With the ``-t`` option, ``h2load`` will use multiple native -threads to avoid saturating a single core on client side. - -.. warning:: - - **Don't use this tool against publicly available servers.** That is - considered a DOS attack. Please only use it against your private - servers. - -HPACK tools ------------ - -The ``src`` directory contains the HPACK tools. The ``deflatehd`` program is a -command-line header compression tool. The ``inflatehd`` program is a -command-line header decompression tool. Both tools read input from -stdin and write output to stdout. Errors are written to stderr. -They take JSON as input and output. We (mostly) use the same JSON data -format described at https://github.com/http2jp/hpack-test-case. - -deflatehd - header compressor -+++++++++++++++++++++++++++++ - -The ``deflatehd`` program reads JSON data or HTTP/1-style header fields from -stdin and outputs compressed header block in JSON. - -For the JSON input, the root JSON object must include a ``cases`` key. -Its value has to include the sequence of input header set. They share -the same compression context and are processed in the order they -appear. Each item in the sequence is a JSON object and it must -include a ``headers`` key. Its value is an array of JSON objects, -which includes exactly one name/value pair. - -Example: - -.. code-block:: json - - { - "cases": - [ - { - "headers": [ - { ":method": "GET" }, - { ":path": "/" } - ] - }, - { - "headers": [ - { ":method": "POST" }, - { ":path": "/" } - ] - } - ] - } - - -With the ``-t`` option, the program can accept more familiar HTTP/1 style -header field blocks. Each header set is delimited by an empty line: - -Example:: - - :method: GET - :scheme: https - :path: / - - :method: POST - user-agent: nghttp2 - -The output is in JSON object. It should include a ``cases`` key and its -value is an array of JSON objects, which has at least the following keys: - -seq - The index of header set in the input. - -input_length - The sum of the length of the name/value pairs in the input. - -output_length - The length of the compressed header block. - -percentage_of_original_size - ``input_length`` / ``output_length`` * 100 - -wire - The compressed header block as a hex string. - -headers - The input header set. - -header_table_size - The header table size adjusted before deflating the header set. - -Examples: - -.. code-block:: json - - { - "cases": - [ - { - "seq": 0, - "input_length": 66, - "output_length": 20, - "percentage_of_original_size": 30.303030303030305, - "wire": "01881f3468e5891afcbf83868a3d856659c62e3f", - "headers": [ - { - ":authority": "example.org" - }, - { - ":method": "GET" - }, - { - ":path": "/" - }, - { - ":scheme": "https" - }, - { - "user-agent": "nghttp2" - } - ], - "header_table_size": 4096 - } - , - { - "seq": 1, - "input_length": 74, - "output_length": 10, - "percentage_of_original_size": 13.513513513513514, - "wire": "88448504252dd5918485", - "headers": [ - { - ":authority": "example.org" - }, - { - ":method": "POST" - }, - { - ":path": "/account" - }, - { - ":scheme": "https" - }, - { - "user-agent": "nghttp2" - } - ], - "header_table_size": 4096 - } - ] - } - - -The output can be used as the input for ``inflatehd`` and -``deflatehd``. - -With the ``-d`` option, the extra ``header_table`` key is added and its -associated value includes the state of dynamic header table after the -corresponding header set was processed. The value includes at least -the following keys: - -entries - The entry in the header table. If ``referenced`` is ``true``, it - is in the reference set. The ``size`` includes the overhead (32 - bytes). The ``index`` corresponds to the index of header table. - The ``name`` is the header field name and the ``value`` is the - header field value. - -size - The sum of the spaces entries occupied, this includes the - entry overhead. - -max_size - The maximum header table size. - -deflate_size - The sum of the spaces entries occupied within - ``max_deflate_size``. - -max_deflate_size - The maximum header table size the encoder uses. This can be smaller - than ``max_size``. In this case, the encoder only uses up to first - ``max_deflate_size`` buffer. Since the header table size is still - ``max_size``, the encoder has to keep track of entries outside the - ``max_deflate_size`` but inside the ``max_size`` and make sure - that they are no longer referenced. - -Example: - -.. code-block:: json - - { - "cases": - [ - { - "seq": 0, - "input_length": 66, - "output_length": 20, - "percentage_of_original_size": 30.303030303030305, - "wire": "01881f3468e5891afcbf83868a3d856659c62e3f", - "headers": [ - { - ":authority": "example.org" - }, - { - ":method": "GET" - }, - { - ":path": "/" - }, - { - ":scheme": "https" - }, - { - "user-agent": "nghttp2" - } - ], - "header_table_size": 4096, - "header_table": { - "entries": [ - { - "index": 1, - "name": "user-agent", - "value": "nghttp2", - "referenced": true, - "size": 49 - }, - { - "index": 2, - "name": ":scheme", - "value": "https", - "referenced": true, - "size": 44 - }, - { - "index": 3, - "name": ":path", - "value": "/", - "referenced": true, - "size": 38 - }, - { - "index": 4, - "name": ":method", - "value": "GET", - "referenced": true, - "size": 42 - }, - { - "index": 5, - "name": ":authority", - "value": "example.org", - "referenced": true, - "size": 53 - } - ], - "size": 226, - "max_size": 4096, - "deflate_size": 226, - "max_deflate_size": 4096 - } - } - , - { - "seq": 1, - "input_length": 74, - "output_length": 10, - "percentage_of_original_size": 13.513513513513514, - "wire": "88448504252dd5918485", - "headers": [ - { - ":authority": "example.org" - }, - { - ":method": "POST" - }, - { - ":path": "/account" - }, - { - ":scheme": "https" - }, - { - "user-agent": "nghttp2" - } - ], - "header_table_size": 4096, - "header_table": { - "entries": [ - { - "index": 1, - "name": ":method", - "value": "POST", - "referenced": true, - "size": 43 - }, - { - "index": 2, - "name": "user-agent", - "value": "nghttp2", - "referenced": true, - "size": 49 - }, - { - "index": 3, - "name": ":scheme", - "value": "https", - "referenced": true, - "size": 44 - }, - { - "index": 4, - "name": ":path", - "value": "/", - "referenced": false, - "size": 38 - }, - { - "index": 5, - "name": ":method", - "value": "GET", - "referenced": false, - "size": 42 - }, - { - "index": 6, - "name": ":authority", - "value": "example.org", - "referenced": true, - "size": 53 - } - ], - "size": 269, - "max_size": 4096, - "deflate_size": 269, - "max_deflate_size": 4096 - } - } - ] - } - -inflatehd - header decompressor -+++++++++++++++++++++++++++++++ - -The ``inflatehd`` program reads JSON data from stdin and outputs decompressed -name/value pairs in JSON. - -The root JSON object must include the ``cases`` key. Its value has to -include the sequence of compressed header blocks. They share the same -compression context and are processed in the order they appear. Each -item in the sequence is a JSON object and it must have at least a -``wire`` key. Its value is a compressed header block as a hex string. - -Example: - -.. code-block:: json - - { - "cases": - [ - { "wire": "8285" }, - { "wire": "8583" } - ] - } - -The output is a JSON object. It should include a ``cases`` key and its -value is an array of JSON objects, which has at least following keys: - -seq - The index of the header set in the input. - -headers - A JSON array that includes decompressed name/value pairs. - -wire - The compressed header block as a hex string. - -header_table_size - The header table size adjusted before inflating compressed header - block. - -Example: - -.. code-block:: json - - { - "cases": - [ - { - "seq": 0, - "wire": "01881f3468e5891afcbf83868a3d856659c62e3f", - "headers": [ - { - ":authority": "example.org" - }, - { - ":method": "GET" - }, - { - ":path": "/" - }, - { - ":scheme": "https" - }, - { - "user-agent": "nghttp2" - } - ], - "header_table_size": 4096 - } - , - { - "seq": 1, - "wire": "88448504252dd5918485", - "headers": [ - { - ":method": "POST" - }, - { - ":path": "/account" - }, - { - "user-agent": "nghttp2" - }, - { - ":scheme": "https" - }, - { - ":authority": "example.org" - } - ], - "header_table_size": 4096 - } - ] - } - -The output can be used as the input for ``deflatehd`` and -``inflatehd``. - -With the ``-d`` option, the extra ``header_table`` key is added and its -associated value includes the state of the dynamic header table after the -corresponding header set was processed. The format is the same as -``deflatehd``. - -libnghttp2_asio: High level HTTP/2 C++ library ----------------------------------------------- - -libnghttp2_asio is C++ library built on top of libnghttp2 and provides -high level abstraction API to build HTTP/2 applications. It depends -on the Boost::ASIO library and OpenSSL. Currently libnghttp2_asio -provides both client and server APIs. - -libnghttp2_asio is not built by default. Use the ``--enable-asio-lib`` -configure flag to build libnghttp2_asio. The required Boost libraries -are: - -* Boost::Asio -* Boost::System -* Boost::Thread - -The server API is designed to build an HTTP/2 server very easily to utilize -C++11 anonymous functions and closures. The bare minimum example of -an HTTP/2 server looks like this: - -.. code-block:: cpp - - #include - - using namespace nghttp2::asio_http2; - using namespace nghttp2::asio_http2::server; - - int main(int argc, char *argv[]) { - boost::system::error_code ec; - http2 server; - - server.handle("/", [](const request &req, const response &res) { - res.write_head(200); - res.end("hello, world\n"); - }); - - if (server.listen_and_serve(ec, "localhost", "3000")) { - std::cerr << "error: " << ec.message() << std::endl; - } - } - -Here is sample code to use the client API: - -.. code-block:: cpp - - #include - - #include - - using boost::asio::ip::tcp; - - using namespace nghttp2::asio_http2; - using namespace nghttp2::asio_http2::client; - - int main(int argc, char *argv[]) { - boost::system::error_code ec; - boost::asio::io_service io_service; - - // connect to localhost:3000 - session sess(io_service, "localhost", "3000"); - - sess.on_connect([&sess](tcp::resolver::iterator endpoint_it) { - boost::system::error_code ec; - - auto req = sess.submit(ec, "GET", "http://localhost:3000/"); - - req->on_response([](const response &res) { - // print status code and response header fields. - std::cerr << "HTTP/2 " << res.status_code() << std::endl; - for (auto &kv : res.header()) { - std::cerr << kv.first << ": " << kv.second.value << "\n"; - } - std::cerr << std::endl; - - res.on_data([](const uint8_t *data, std::size_t len) { - std::cerr.write(reinterpret_cast(data), len); - std::cerr << std::endl; - }); - }); - - req->on_close([&sess](uint32_t error_code) { - // shutdown session after first request was done. - sess.shutdown(); - }); - }); - - sess.on_error([](const boost::system::error_code &ec) { - std::cerr << "error: " << ec.message() << std::endl; - }); - - io_service.run(); - } - -For more details, see the documentation of libnghttp2_asio. - -Python bindings ---------------- - -The ``python`` directory contains nghttp2 Python bindings. The -bindings currently provide HPACK compressor and decompressor classes -and an HTTP/2 server. - -The extension module is called ``nghttp2``. - -``make`` will build the bindings and target Python version is -determined by the ``configure`` script. If the detected Python version is not -what you expect, specify a path to Python executable in a ``PYTHON`` -variable as an argument to configure script (e.g., ``./configure -PYTHON=/usr/bin/python3.4``). - -The following example code illustrates basic usage of the HPACK compressor -and decompressor in Python: - -.. code-block:: python - - import binascii - import nghttp2 - - deflater = nghttp2.HDDeflater() - inflater = nghttp2.HDInflater() - - data = deflater.deflate([(b'foo', b'bar'), - (b'baz', b'buz')]) - print(binascii.b2a_hex(data)) - - hdrs = inflater.inflate(data) - print(hdrs) - -The ``nghttp2.HTTP2Server`` class builds on top of the asyncio event -loop. On construction, *RequestHandlerClass* must be given, which -must be a subclass of ``nghttp2.BaseRequestHandler`` class. - -The ``BaseRequestHandler`` class is used to handle the HTTP/2 stream. -By default, it does nothing. It must be subclassed to handle each -event callback method. - -The first callback method invoked is ``on_headers()``. It is called -when HEADERS frame, which includes the request header fields, has arrived. - -If the request has a request body, ``on_data(data)`` is invoked for each -chunk of received data. - -Once the entire request is received, ``on_request_done()`` is invoked. - -When the stream is closed, ``on_close(error_code)`` is called. - -The application can send a response using ``send_response()`` method. -It can be used in ``on_headers()``, ``on_data()`` or -``on_request_done()``. - -The application can push resources using the ``push()`` method. It must be -used before the ``send_response()`` call. - -The following instance variables are available: - -client_address - Contains a tuple of the form (host, port) referring to the - client's address. - -stream_id - Stream ID of this stream. - -scheme - Scheme of the request URI. This is a value of :scheme header - field. - -method - Method of this stream. This is a value of :method header field. - -host - This is a value of :authority or host header field. - -path - This is a value of :path header field. - -The following example illustrates the HTTP2Server and -BaseRequestHandler usage: - -.. code-block:: python - - #!/usr/bin/env python - - import io, ssl - import nghttp2 - - class Handler(nghttp2.BaseRequestHandler): - - def on_headers(self): - self.push(path='/css/bootstrap.css', - request_headers = [('content-length', '3')], - status=200, - body='foo') - - self.push(path='/js/bootstrap.js', - method='GET', - request_headers = [('content-length', '10')], - status=200, - body='foobarbuzz') - - self.send_response(status=200, - headers = [('content-type', 'text/plain')], - body=io.BytesIO(b'nghttp2-python FTW')) - - ctx = ssl.SSLContext(ssl.PROTOCOL_SSLv23) - ctx.options = ssl.OP_ALL | ssl.OP_NO_SSLv2 - ctx.load_cert_chain('server.crt', 'server.key') - - # give None to ssl to make the server non-SSL/TLS - server = nghttp2.HTTP2Server(('127.0.0.1', 8443), Handler, ssl=ctx) - server.serve_forever() - -Contribution ------------- - -[This text was composed based on 1.2. License section of curl/libcurl -project.] - -When contributing with code, you agree to put your changes and new -code under the same license nghttp2 is already using unless stated and -agreed otherwise. - -When changing existing source code, do not alter the copyright of -the original file(s). The copyright will still be owned by the -original creator(s) or those who have been assigned copyright by the -original author(s). - -By submitting a patch to the nghttp2 project, you (or your employer, as -the case may be) agree to assign the copyright of your submission to us. -.. the above really needs to be reworded to pass legal muster. -We will credit you for your -changes as far as possible, to give credit but also to keep a trace -back to who made what changes. Please always provide us with your -full real name when contributing! - -See `Contribution Guidelines -`_ for more -details. diff --git a/aclocal.m4 b/aclocal.m4 deleted file mode 100644 index 44bdb74..0000000 --- a/aclocal.m4 +++ /dev/null @@ -1,1613 +0,0 @@ -# generated automatically by aclocal 1.14.1 -*- Autoconf -*- - -# Copyright (C) 1996-2013 Free Software Foundation, Inc. - -# This file is free software; the Free Software Foundation -# gives unlimited permission to copy and/or distribute it, -# with or without modifications, as long as this notice is preserved. - -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY, to the extent permitted by law; without -# even the implied warranty of MERCHANTABILITY or FITNESS FOR A -# PARTICULAR PURPOSE. - -m4_ifndef([AC_CONFIG_MACRO_DIRS], [m4_defun([_AM_CONFIG_MACRO_DIRS], [])m4_defun([AC_CONFIG_MACRO_DIRS], [_AM_CONFIG_MACRO_DIRS($@)])]) -m4_ifndef([AC_AUTOCONF_VERSION], - [m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl -m4_if(m4_defn([AC_AUTOCONF_VERSION]), [2.69],, -[m4_warning([this file was generated for autoconf 2.69. -You have another version of autoconf. It may work, but is not guaranteed to. -If you have problems, you may need to regenerate the build system entirely. -To do so, use the procedure documented by the package, typically 'autoreconf'.])]) - -# pkg.m4 - Macros to locate and utilise pkg-config. -*- Autoconf -*- -# serial 1 (pkg-config-0.24) -# -# Copyright © 2004 Scott James Remnant . -# -# 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 2 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, write to the Free Software -# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -# -# As a special exception to the GNU General Public License, if you -# distribute this file as part of a program that contains a -# configuration script generated by Autoconf, you may include it under -# the same distribution terms that you use for the rest of that program. - -# PKG_PROG_PKG_CONFIG([MIN-VERSION]) -# ---------------------------------- -AC_DEFUN([PKG_PROG_PKG_CONFIG], -[m4_pattern_forbid([^_?PKG_[A-Z_]+$]) -m4_pattern_allow([^PKG_CONFIG(_(PATH|LIBDIR|SYSROOT_DIR|ALLOW_SYSTEM_(CFLAGS|LIBS)))?$]) -m4_pattern_allow([^PKG_CONFIG_(DISABLE_UNINSTALLED|TOP_BUILD_DIR|DEBUG_SPEW)$]) -AC_ARG_VAR([PKG_CONFIG], [path to pkg-config utility]) -AC_ARG_VAR([PKG_CONFIG_PATH], [directories to add to pkg-config's search path]) -AC_ARG_VAR([PKG_CONFIG_LIBDIR], [path overriding pkg-config's built-in search path]) - -if test "x$ac_cv_env_PKG_CONFIG_set" != "xset"; then - AC_PATH_TOOL([PKG_CONFIG], [pkg-config]) -fi -if test -n "$PKG_CONFIG"; then - _pkg_min_version=m4_default([$1], [0.9.0]) - AC_MSG_CHECKING([pkg-config is at least version $_pkg_min_version]) - if $PKG_CONFIG --atleast-pkgconfig-version $_pkg_min_version; then - AC_MSG_RESULT([yes]) - else - AC_MSG_RESULT([no]) - PKG_CONFIG="" - fi -fi[]dnl -])# PKG_PROG_PKG_CONFIG - -# PKG_CHECK_EXISTS(MODULES, [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) -# -# Check to see whether a particular set of modules exists. Similar -# to PKG_CHECK_MODULES(), but does not set variables or print errors. -# -# Please remember that m4 expands AC_REQUIRE([PKG_PROG_PKG_CONFIG]) -# only at the first occurence in configure.ac, so if the first place -# it's called might be skipped (such as if it is within an "if", you -# have to call PKG_CHECK_EXISTS manually -# -------------------------------------------------------------- -AC_DEFUN([PKG_CHECK_EXISTS], -[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl -if test -n "$PKG_CONFIG" && \ - AC_RUN_LOG([$PKG_CONFIG --exists --print-errors "$1"]); then - m4_default([$2], [:]) -m4_ifvaln([$3], [else - $3])dnl -fi]) - -# _PKG_CONFIG([VARIABLE], [COMMAND], [MODULES]) -# --------------------------------------------- -m4_define([_PKG_CONFIG], -[if test -n "$$1"; then - pkg_cv_[]$1="$$1" - elif test -n "$PKG_CONFIG"; then - PKG_CHECK_EXISTS([$3], - [pkg_cv_[]$1=`$PKG_CONFIG --[]$2 "$3" 2>/dev/null` - test "x$?" != "x0" && pkg_failed=yes ], - [pkg_failed=yes]) - else - pkg_failed=untried -fi[]dnl -])# _PKG_CONFIG - -# _PKG_SHORT_ERRORS_SUPPORTED -# ----------------------------- -AC_DEFUN([_PKG_SHORT_ERRORS_SUPPORTED], -[AC_REQUIRE([PKG_PROG_PKG_CONFIG]) -if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then - _pkg_short_errors_supported=yes -else - _pkg_short_errors_supported=no -fi[]dnl -])# _PKG_SHORT_ERRORS_SUPPORTED - - -# PKG_CHECK_MODULES(VARIABLE-PREFIX, MODULES, [ACTION-IF-FOUND], -# [ACTION-IF-NOT-FOUND]) -# -# -# Note that if there is a possibility the first call to -# PKG_CHECK_MODULES might not happen, you should be sure to include an -# explicit call to PKG_PROG_PKG_CONFIG in your configure.ac -# -# -# -------------------------------------------------------------- -AC_DEFUN([PKG_CHECK_MODULES], -[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl -AC_ARG_VAR([$1][_CFLAGS], [C compiler flags for $1, overriding pkg-config])dnl -AC_ARG_VAR([$1][_LIBS], [linker flags for $1, overriding pkg-config])dnl - -pkg_failed=no -AC_MSG_CHECKING([for $1]) - -_PKG_CONFIG([$1][_CFLAGS], [cflags], [$2]) -_PKG_CONFIG([$1][_LIBS], [libs], [$2]) - -m4_define([_PKG_TEXT], [Alternatively, you may set the environment variables $1[]_CFLAGS -and $1[]_LIBS to avoid the need to call pkg-config. -See the pkg-config man page for more details.]) - -if test $pkg_failed = yes; then - AC_MSG_RESULT([no]) - _PKG_SHORT_ERRORS_SUPPORTED - if test $_pkg_short_errors_supported = yes; then - $1[]_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "$2" 2>&1` - else - $1[]_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "$2" 2>&1` - fi - # Put the nasty error message in config.log where it belongs - echo "$$1[]_PKG_ERRORS" >&AS_MESSAGE_LOG_FD - - m4_default([$4], [AC_MSG_ERROR( -[Package requirements ($2) were not met: - -$$1_PKG_ERRORS - -Consider adjusting the PKG_CONFIG_PATH environment variable if you -installed software in a non-standard prefix. - -_PKG_TEXT])[]dnl - ]) -elif test $pkg_failed = untried; then - AC_MSG_RESULT([no]) - m4_default([$4], [AC_MSG_FAILURE( -[The pkg-config script could not be found or is too old. Make sure it -is in your PATH or set the PKG_CONFIG environment variable to the full -path to pkg-config. - -_PKG_TEXT - -To get pkg-config, see .])[]dnl - ]) -else - $1[]_CFLAGS=$pkg_cv_[]$1[]_CFLAGS - $1[]_LIBS=$pkg_cv_[]$1[]_LIBS - AC_MSG_RESULT([yes]) - $3 -fi[]dnl -])# PKG_CHECK_MODULES - - -# PKG_INSTALLDIR(DIRECTORY) -# ------------------------- -# Substitutes the variable pkgconfigdir as the location where a module -# should install pkg-config .pc files. By default the directory is -# $libdir/pkgconfig, but the default can be changed by passing -# DIRECTORY. The user can override through the --with-pkgconfigdir -# parameter. -AC_DEFUN([PKG_INSTALLDIR], -[m4_pushdef([pkg_default], [m4_default([$1], ['${libdir}/pkgconfig'])]) -m4_pushdef([pkg_description], - [pkg-config installation directory @<:@]pkg_default[@:>@]) -AC_ARG_WITH([pkgconfigdir], - [AS_HELP_STRING([--with-pkgconfigdir], pkg_description)],, - [with_pkgconfigdir=]pkg_default) -AC_SUBST([pkgconfigdir], [$with_pkgconfigdir]) -m4_popdef([pkg_default]) -m4_popdef([pkg_description]) -]) dnl PKG_INSTALLDIR - - -# PKG_NOARCH_INSTALLDIR(DIRECTORY) -# ------------------------- -# Substitutes the variable noarch_pkgconfigdir as the location where a -# module should install arch-independent pkg-config .pc files. By -# default the directory is $datadir/pkgconfig, but the default can be -# changed by passing DIRECTORY. The user can override through the -# --with-noarch-pkgconfigdir parameter. -AC_DEFUN([PKG_NOARCH_INSTALLDIR], -[m4_pushdef([pkg_default], [m4_default([$1], ['${datadir}/pkgconfig'])]) -m4_pushdef([pkg_description], - [pkg-config arch-independent installation directory @<:@]pkg_default[@:>@]) -AC_ARG_WITH([noarch-pkgconfigdir], - [AS_HELP_STRING([--with-noarch-pkgconfigdir], pkg_description)],, - [with_noarch_pkgconfigdir=]pkg_default) -AC_SUBST([noarch_pkgconfigdir], [$with_noarch_pkgconfigdir]) -m4_popdef([pkg_default]) -m4_popdef([pkg_description]) -]) dnl PKG_NOARCH_INSTALLDIR - - -# PKG_CHECK_VAR(VARIABLE, MODULE, CONFIG-VARIABLE, -# [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) -# ------------------------------------------- -# Retrieves the value of the pkg-config variable for the given module. -AC_DEFUN([PKG_CHECK_VAR], -[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl -AC_ARG_VAR([$1], [value of $3 for $2, overriding pkg-config])dnl - -_PKG_CONFIG([$1], [variable="][$3]["], [$2]) -AS_VAR_COPY([$1], [pkg_cv_][$1]) - -AS_VAR_IF([$1], [""], [$5], [$4])dnl -])# PKG_CHECK_VAR - -# Copyright (C) 2002-2013 Free Software Foundation, Inc. -# -# This file is free software; the Free Software Foundation -# gives unlimited permission to copy and/or distribute it, -# with or without modifications, as long as this notice is preserved. - -# AM_AUTOMAKE_VERSION(VERSION) -# ---------------------------- -# Automake X.Y traces this macro to ensure aclocal.m4 has been -# generated from the m4 files accompanying Automake X.Y. -# (This private macro should not be called outside this file.) -AC_DEFUN([AM_AUTOMAKE_VERSION], -[am__api_version='1.14' -dnl Some users find AM_AUTOMAKE_VERSION and mistake it for a way to -dnl require some minimum version. Point them to the right macro. -m4_if([$1], [1.14.1], [], - [AC_FATAL([Do not call $0, use AM_INIT_AUTOMAKE([$1]).])])dnl -]) - -# _AM_AUTOCONF_VERSION(VERSION) -# ----------------------------- -# aclocal traces this macro to find the Autoconf version. -# This is a private macro too. Using m4_define simplifies -# the logic in aclocal, which can simply ignore this definition. -m4_define([_AM_AUTOCONF_VERSION], []) - -# AM_SET_CURRENT_AUTOMAKE_VERSION -# ------------------------------- -# Call AM_AUTOMAKE_VERSION and AM_AUTOMAKE_VERSION so they can be traced. -# This function is AC_REQUIREd by AM_INIT_AUTOMAKE. -AC_DEFUN([AM_SET_CURRENT_AUTOMAKE_VERSION], -[AM_AUTOMAKE_VERSION([1.14.1])dnl -m4_ifndef([AC_AUTOCONF_VERSION], - [m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl -_AM_AUTOCONF_VERSION(m4_defn([AC_AUTOCONF_VERSION]))]) - -# AM_AUX_DIR_EXPAND -*- Autoconf -*- - -# Copyright (C) 2001-2013 Free Software Foundation, Inc. -# -# This file is free software; the Free Software Foundation -# gives unlimited permission to copy and/or distribute it, -# with or without modifications, as long as this notice is preserved. - -# For projects using AC_CONFIG_AUX_DIR([foo]), Autoconf sets -# $ac_aux_dir to '$srcdir/foo'. In other projects, it is set to -# '$srcdir', '$srcdir/..', or '$srcdir/../..'. -# -# Of course, Automake must honor this variable whenever it calls a -# tool from the auxiliary directory. The problem is that $srcdir (and -# therefore $ac_aux_dir as well) can be either absolute or relative, -# depending on how configure is run. This is pretty annoying, since -# it makes $ac_aux_dir quite unusable in subdirectories: in the top -# source directory, any form will work fine, but in subdirectories a -# relative path needs to be adjusted first. -# -# $ac_aux_dir/missing -# fails when called from a subdirectory if $ac_aux_dir is relative -# $top_srcdir/$ac_aux_dir/missing -# fails if $ac_aux_dir is absolute, -# fails when called from a subdirectory in a VPATH build with -# a relative $ac_aux_dir -# -# The reason of the latter failure is that $top_srcdir and $ac_aux_dir -# are both prefixed by $srcdir. In an in-source build this is usually -# harmless because $srcdir is '.', but things will broke when you -# start a VPATH build or use an absolute $srcdir. -# -# So we could use something similar to $top_srcdir/$ac_aux_dir/missing, -# iff we strip the leading $srcdir from $ac_aux_dir. That would be: -# am_aux_dir='\$(top_srcdir)/'`expr "$ac_aux_dir" : "$srcdir//*\(.*\)"` -# and then we would define $MISSING as -# MISSING="\${SHELL} $am_aux_dir/missing" -# This will work as long as MISSING is not called from configure, because -# unfortunately $(top_srcdir) has no meaning in configure. -# However there are other variables, like CC, which are often used in -# configure, and could therefore not use this "fixed" $ac_aux_dir. -# -# Another solution, used here, is to always expand $ac_aux_dir to an -# absolute PATH. The drawback is that using absolute paths prevent a -# configured tree to be moved without reconfiguration. - -AC_DEFUN([AM_AUX_DIR_EXPAND], -[AC_REQUIRE([AC_CONFIG_AUX_DIR_DEFAULT])dnl -# Expand $ac_aux_dir to an absolute path. -am_aux_dir=`cd "$ac_aux_dir" && pwd` -]) - -# AM_CONDITIONAL -*- Autoconf -*- - -# Copyright (C) 1997-2013 Free Software Foundation, Inc. -# -# This file is free software; the Free Software Foundation -# gives unlimited permission to copy and/or distribute it, -# with or without modifications, as long as this notice is preserved. - -# AM_CONDITIONAL(NAME, SHELL-CONDITION) -# ------------------------------------- -# Define a conditional. -AC_DEFUN([AM_CONDITIONAL], -[AC_PREREQ([2.52])dnl - m4_if([$1], [TRUE], [AC_FATAL([$0: invalid condition: $1])], - [$1], [FALSE], [AC_FATAL([$0: invalid condition: $1])])dnl -AC_SUBST([$1_TRUE])dnl -AC_SUBST([$1_FALSE])dnl -_AM_SUBST_NOTMAKE([$1_TRUE])dnl -_AM_SUBST_NOTMAKE([$1_FALSE])dnl -m4_define([_AM_COND_VALUE_$1], [$2])dnl -if $2; then - $1_TRUE= - $1_FALSE='#' -else - $1_TRUE='#' - $1_FALSE= -fi -AC_CONFIG_COMMANDS_PRE( -[if test -z "${$1_TRUE}" && test -z "${$1_FALSE}"; then - AC_MSG_ERROR([[conditional "$1" was never defined. -Usually this means the macro was only invoked conditionally.]]) -fi])]) - -# Copyright (C) 1999-2013 Free Software Foundation, Inc. -# -# This file is free software; the Free Software Foundation -# gives unlimited permission to copy and/or distribute it, -# with or without modifications, as long as this notice is preserved. - - -# There are a few dirty hacks below to avoid letting 'AC_PROG_CC' be -# written in clear, in which case automake, when reading aclocal.m4, -# will think it sees a *use*, and therefore will trigger all it's -# C support machinery. Also note that it means that autoscan, seeing -# CC etc. in the Makefile, will ask for an AC_PROG_CC use... - - -# _AM_DEPENDENCIES(NAME) -# ---------------------- -# See how the compiler implements dependency checking. -# NAME is "CC", "CXX", "OBJC", "OBJCXX", "UPC", or "GJC". -# We try a few techniques and use that to set a single cache variable. -# -# We don't AC_REQUIRE the corresponding AC_PROG_CC since the latter was -# modified to invoke _AM_DEPENDENCIES(CC); we would have a circular -# dependency, and given that the user is not expected to run this macro, -# just rely on AC_PROG_CC. -AC_DEFUN([_AM_DEPENDENCIES], -[AC_REQUIRE([AM_SET_DEPDIR])dnl -AC_REQUIRE([AM_OUTPUT_DEPENDENCY_COMMANDS])dnl -AC_REQUIRE([AM_MAKE_INCLUDE])dnl -AC_REQUIRE([AM_DEP_TRACK])dnl - -m4_if([$1], [CC], [depcc="$CC" am_compiler_list=], - [$1], [CXX], [depcc="$CXX" am_compiler_list=], - [$1], [OBJC], [depcc="$OBJC" am_compiler_list='gcc3 gcc'], - [$1], [OBJCXX], [depcc="$OBJCXX" am_compiler_list='gcc3 gcc'], - [$1], [UPC], [depcc="$UPC" am_compiler_list=], - [$1], [GCJ], [depcc="$GCJ" am_compiler_list='gcc3 gcc'], - [depcc="$$1" am_compiler_list=]) - -AC_CACHE_CHECK([dependency style of $depcc], - [am_cv_$1_dependencies_compiler_type], -[if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then - # We make a subdir and do the tests there. Otherwise we can end up - # making bogus files that we don't know about and never remove. For - # instance it was reported that on HP-UX the gcc test will end up - # making a dummy file named 'D' -- because '-MD' means "put the output - # in D". - rm -rf conftest.dir - mkdir conftest.dir - # Copy depcomp to subdir because otherwise we won't find it if we're - # using a relative directory. - cp "$am_depcomp" conftest.dir - cd conftest.dir - # We will build objects and dependencies in a subdirectory because - # it helps to detect inapplicable dependency modes. For instance - # both Tru64's cc and ICC support -MD to output dependencies as a - # side effect of compilation, but ICC will put the dependencies in - # the current directory while Tru64 will put them in the object - # directory. - mkdir sub - - am_cv_$1_dependencies_compiler_type=none - if test "$am_compiler_list" = ""; then - am_compiler_list=`sed -n ['s/^#*\([a-zA-Z0-9]*\))$/\1/p'] < ./depcomp` - fi - am__universal=false - m4_case([$1], [CC], - [case " $depcc " in #( - *\ -arch\ *\ -arch\ *) am__universal=true ;; - esac], - [CXX], - [case " $depcc " in #( - *\ -arch\ *\ -arch\ *) am__universal=true ;; - esac]) - - for depmode in $am_compiler_list; do - # Setup a source with many dependencies, because some compilers - # like to wrap large dependency lists on column 80 (with \), and - # we should not choose a depcomp mode which is confused by this. - # - # We need to recreate these files for each test, as the compiler may - # overwrite some of them when testing with obscure command lines. - # This happens at least with the AIX C compiler. - : > sub/conftest.c - for i in 1 2 3 4 5 6; do - echo '#include "conftst'$i'.h"' >> sub/conftest.c - # Using ": > sub/conftst$i.h" creates only sub/conftst1.h with - # Solaris 10 /bin/sh. - echo '/* dummy */' > sub/conftst$i.h - done - echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf - - # We check with '-c' and '-o' for the sake of the "dashmstdout" - # mode. It turns out that the SunPro C++ compiler does not properly - # handle '-M -o', and we need to detect this. Also, some Intel - # versions had trouble with output in subdirs. - am__obj=sub/conftest.${OBJEXT-o} - am__minus_obj="-o $am__obj" - case $depmode in - gcc) - # This depmode causes a compiler race in universal mode. - test "$am__universal" = false || continue - ;; - nosideeffect) - # After this tag, mechanisms are not by side-effect, so they'll - # only be used when explicitly requested. - if test "x$enable_dependency_tracking" = xyes; then - continue - else - break - fi - ;; - msvc7 | msvc7msys | msvisualcpp | msvcmsys) - # This compiler won't grok '-c -o', but also, the minuso test has - # not run yet. These depmodes are late enough in the game, and - # so weak that their functioning should not be impacted. - am__obj=conftest.${OBJEXT-o} - am__minus_obj= - ;; - none) break ;; - esac - if depmode=$depmode \ - source=sub/conftest.c object=$am__obj \ - depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \ - $SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \ - >/dev/null 2>conftest.err && - grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 && - grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 && - grep $am__obj sub/conftest.Po > /dev/null 2>&1 && - ${MAKE-make} -s -f confmf > /dev/null 2>&1; then - # icc doesn't choke on unknown options, it will just issue warnings - # or remarks (even with -Werror). So we grep stderr for any message - # that says an option was ignored or not supported. - # When given -MP, icc 7.0 and 7.1 complain thusly: - # icc: Command line warning: ignoring option '-M'; no argument required - # The diagnosis changed in icc 8.0: - # icc: Command line remark: option '-MP' not supported - if (grep 'ignoring option' conftest.err || - grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else - am_cv_$1_dependencies_compiler_type=$depmode - break - fi - fi - done - - cd .. - rm -rf conftest.dir -else - am_cv_$1_dependencies_compiler_type=none -fi -]) -AC_SUBST([$1DEPMODE], [depmode=$am_cv_$1_dependencies_compiler_type]) -AM_CONDITIONAL([am__fastdep$1], [ - test "x$enable_dependency_tracking" != xno \ - && test "$am_cv_$1_dependencies_compiler_type" = gcc3]) -]) - - -# AM_SET_DEPDIR -# ------------- -# Choose a directory name for dependency files. -# This macro is AC_REQUIREd in _AM_DEPENDENCIES. -AC_DEFUN([AM_SET_DEPDIR], -[AC_REQUIRE([AM_SET_LEADING_DOT])dnl -AC_SUBST([DEPDIR], ["${am__leading_dot}deps"])dnl -]) - - -# AM_DEP_TRACK -# ------------ -AC_DEFUN([AM_DEP_TRACK], -[AC_ARG_ENABLE([dependency-tracking], [dnl -AS_HELP_STRING( - [--enable-dependency-tracking], - [do not reject slow dependency extractors]) -AS_HELP_STRING( - [--disable-dependency-tracking], - [speeds up one-time build])]) -if test "x$enable_dependency_tracking" != xno; then - am_depcomp="$ac_aux_dir/depcomp" - AMDEPBACKSLASH='\' - am__nodep='_no' -fi -AM_CONDITIONAL([AMDEP], [test "x$enable_dependency_tracking" != xno]) -AC_SUBST([AMDEPBACKSLASH])dnl -_AM_SUBST_NOTMAKE([AMDEPBACKSLASH])dnl -AC_SUBST([am__nodep])dnl -_AM_SUBST_NOTMAKE([am__nodep])dnl -]) - -# Generate code to set up dependency tracking. -*- Autoconf -*- - -# Copyright (C) 1999-2013 Free Software Foundation, Inc. -# -# This file is free software; the Free Software Foundation -# gives unlimited permission to copy and/or distribute it, -# with or without modifications, as long as this notice is preserved. - - -# _AM_OUTPUT_DEPENDENCY_COMMANDS -# ------------------------------ -AC_DEFUN([_AM_OUTPUT_DEPENDENCY_COMMANDS], -[{ - # Older Autoconf quotes --file arguments for eval, but not when files - # are listed without --file. Let's play safe and only enable the eval - # if we detect the quoting. - case $CONFIG_FILES in - *\'*) eval set x "$CONFIG_FILES" ;; - *) set x $CONFIG_FILES ;; - esac - shift - for mf - do - # Strip MF so we end up with the name of the file. - mf=`echo "$mf" | sed -e 's/:.*$//'` - # Check whether this is an Automake generated Makefile or not. - # We used to match only the files named 'Makefile.in', but - # some people rename them; so instead we look at the file content. - # Grep'ing the first line is not enough: some people post-process - # each Makefile.in and add a new line on top of each file to say so. - # Grep'ing the whole file is not good either: AIX grep has a line - # limit of 2048, but all sed's we know have understand at least 4000. - if sed -n 's,^#.*generated by automake.*,X,p' "$mf" | grep X >/dev/null 2>&1; then - dirpart=`AS_DIRNAME("$mf")` - else - continue - fi - # Extract the definition of DEPDIR, am__include, and am__quote - # from the Makefile without running 'make'. - DEPDIR=`sed -n 's/^DEPDIR = //p' < "$mf"` - test -z "$DEPDIR" && continue - am__include=`sed -n 's/^am__include = //p' < "$mf"` - test -z "$am__include" && continue - am__quote=`sed -n 's/^am__quote = //p' < "$mf"` - # Find all dependency output files, they are included files with - # $(DEPDIR) in their names. We invoke sed twice because it is the - # simplest approach to changing $(DEPDIR) to its actual value in the - # expansion. - for file in `sed -n " - s/^$am__include $am__quote\(.*(DEPDIR).*\)$am__quote"'$/\1/p' <"$mf" | \ - sed -e 's/\$(DEPDIR)/'"$DEPDIR"'/g'`; do - # Make sure the directory exists. - test -f "$dirpart/$file" && continue - fdir=`AS_DIRNAME(["$file"])` - AS_MKDIR_P([$dirpart/$fdir]) - # echo "creating $dirpart/$file" - echo '# dummy' > "$dirpart/$file" - done - done -} -])# _AM_OUTPUT_DEPENDENCY_COMMANDS - - -# AM_OUTPUT_DEPENDENCY_COMMANDS -# ----------------------------- -# This macro should only be invoked once -- use via AC_REQUIRE. -# -# This code is only required when automatic dependency tracking -# is enabled. FIXME. This creates each '.P' file that we will -# need in order to bootstrap the dependency handling code. -AC_DEFUN([AM_OUTPUT_DEPENDENCY_COMMANDS], -[AC_CONFIG_COMMANDS([depfiles], - [test x"$AMDEP_TRUE" != x"" || _AM_OUTPUT_DEPENDENCY_COMMANDS], - [AMDEP_TRUE="$AMDEP_TRUE" ac_aux_dir="$ac_aux_dir"]) -]) - -# Do all the work for Automake. -*- Autoconf -*- - -# Copyright (C) 1996-2013 Free Software Foundation, Inc. -# -# This file is free software; the Free Software Foundation -# gives unlimited permission to copy and/or distribute it, -# with or without modifications, as long as this notice is preserved. - -# This macro actually does too much. Some checks are only needed if -# your package does certain things. But this isn't really a big deal. - -dnl Redefine AC_PROG_CC to automatically invoke _AM_PROG_CC_C_O. -m4_define([AC_PROG_CC], -m4_defn([AC_PROG_CC]) -[_AM_PROG_CC_C_O -]) - -# AM_INIT_AUTOMAKE(PACKAGE, VERSION, [NO-DEFINE]) -# AM_INIT_AUTOMAKE([OPTIONS]) -# ----------------------------------------------- -# The call with PACKAGE and VERSION arguments is the old style -# call (pre autoconf-2.50), which is being phased out. PACKAGE -# and VERSION should now be passed to AC_INIT and removed from -# the call to AM_INIT_AUTOMAKE. -# We support both call styles for the transition. After -# the next Automake release, Autoconf can make the AC_INIT -# arguments mandatory, and then we can depend on a new Autoconf -# release and drop the old call support. -AC_DEFUN([AM_INIT_AUTOMAKE], -[AC_PREREQ([2.65])dnl -dnl Autoconf wants to disallow AM_ names. We explicitly allow -dnl the ones we care about. -m4_pattern_allow([^AM_[A-Z]+FLAGS$])dnl -AC_REQUIRE([AM_SET_CURRENT_AUTOMAKE_VERSION])dnl -AC_REQUIRE([AC_PROG_INSTALL])dnl -if test "`cd $srcdir && pwd`" != "`pwd`"; then - # Use -I$(srcdir) only when $(srcdir) != ., so that make's output - # is not polluted with repeated "-I." - AC_SUBST([am__isrc], [' -I$(srcdir)'])_AM_SUBST_NOTMAKE([am__isrc])dnl - # test to see if srcdir already configured - if test -f $srcdir/config.status; then - AC_MSG_ERROR([source directory already configured; run "make distclean" there first]) - fi -fi - -# test whether we have cygpath -if test -z "$CYGPATH_W"; then - if (cygpath --version) >/dev/null 2>/dev/null; then - CYGPATH_W='cygpath -w' - else - CYGPATH_W=echo - fi -fi -AC_SUBST([CYGPATH_W]) - -# Define the identity of the package. -dnl Distinguish between old-style and new-style calls. -m4_ifval([$2], -[AC_DIAGNOSE([obsolete], - [$0: two- and three-arguments forms are deprecated.]) -m4_ifval([$3], [_AM_SET_OPTION([no-define])])dnl - AC_SUBST([PACKAGE], [$1])dnl - AC_SUBST([VERSION], [$2])], -[_AM_SET_OPTIONS([$1])dnl -dnl Diagnose old-style AC_INIT with new-style AM_AUTOMAKE_INIT. -m4_if( - m4_ifdef([AC_PACKAGE_NAME], [ok]):m4_ifdef([AC_PACKAGE_VERSION], [ok]), - [ok:ok],, - [m4_fatal([AC_INIT should be called with package and version arguments])])dnl - AC_SUBST([PACKAGE], ['AC_PACKAGE_TARNAME'])dnl - AC_SUBST([VERSION], ['AC_PACKAGE_VERSION'])])dnl - -_AM_IF_OPTION([no-define],, -[AC_DEFINE_UNQUOTED([PACKAGE], ["$PACKAGE"], [Name of package]) - AC_DEFINE_UNQUOTED([VERSION], ["$VERSION"], [Version number of package])])dnl - -# Some tools Automake needs. -AC_REQUIRE([AM_SANITY_CHECK])dnl -AC_REQUIRE([AC_ARG_PROGRAM])dnl -AM_MISSING_PROG([ACLOCAL], [aclocal-${am__api_version}]) -AM_MISSING_PROG([AUTOCONF], [autoconf]) -AM_MISSING_PROG([AUTOMAKE], [automake-${am__api_version}]) -AM_MISSING_PROG([AUTOHEADER], [autoheader]) -AM_MISSING_PROG([MAKEINFO], [makeinfo]) -AC_REQUIRE([AM_PROG_INSTALL_SH])dnl -AC_REQUIRE([AM_PROG_INSTALL_STRIP])dnl -AC_REQUIRE([AC_PROG_MKDIR_P])dnl -# For better backward compatibility. To be removed once Automake 1.9.x -# dies out for good. For more background, see: -# -# -AC_SUBST([mkdir_p], ['$(MKDIR_P)']) -# We need awk for the "check" target. The system "awk" is bad on -# some platforms. -AC_REQUIRE([AC_PROG_AWK])dnl -AC_REQUIRE([AC_PROG_MAKE_SET])dnl -AC_REQUIRE([AM_SET_LEADING_DOT])dnl -_AM_IF_OPTION([tar-ustar], [_AM_PROG_TAR([ustar])], - [_AM_IF_OPTION([tar-pax], [_AM_PROG_TAR([pax])], - [_AM_PROG_TAR([v7])])]) -_AM_IF_OPTION([no-dependencies],, -[AC_PROVIDE_IFELSE([AC_PROG_CC], - [_AM_DEPENDENCIES([CC])], - [m4_define([AC_PROG_CC], - m4_defn([AC_PROG_CC])[_AM_DEPENDENCIES([CC])])])dnl -AC_PROVIDE_IFELSE([AC_PROG_CXX], - [_AM_DEPENDENCIES([CXX])], - [m4_define([AC_PROG_CXX], - m4_defn([AC_PROG_CXX])[_AM_DEPENDENCIES([CXX])])])dnl -AC_PROVIDE_IFELSE([AC_PROG_OBJC], - [_AM_DEPENDENCIES([OBJC])], - [m4_define([AC_PROG_OBJC], - m4_defn([AC_PROG_OBJC])[_AM_DEPENDENCIES([OBJC])])])dnl -AC_PROVIDE_IFELSE([AC_PROG_OBJCXX], - [_AM_DEPENDENCIES([OBJCXX])], - [m4_define([AC_PROG_OBJCXX], - m4_defn([AC_PROG_OBJCXX])[_AM_DEPENDENCIES([OBJCXX])])])dnl -]) -AC_REQUIRE([AM_SILENT_RULES])dnl -dnl The testsuite driver may need to know about EXEEXT, so add the -dnl 'am__EXEEXT' conditional if _AM_COMPILER_EXEEXT was seen. This -dnl macro is hooked onto _AC_COMPILER_EXEEXT early, see below. -AC_CONFIG_COMMANDS_PRE(dnl -[m4_provide_if([_AM_COMPILER_EXEEXT], - [AM_CONDITIONAL([am__EXEEXT], [test -n "$EXEEXT"])])])dnl - -# POSIX will say in a future version that running "rm -f" with no argument -# is OK; and we want to be able to make that assumption in our Makefile -# recipes. So use an aggressive probe to check that the usage we want is -# actually supported "in the wild" to an acceptable degree. -# See automake bug#10828. -# To make any issue more visible, cause the running configure to be aborted -# by default if the 'rm' program in use doesn't match our expectations; the -# user can still override this though. -if rm -f && rm -fr && rm -rf; then : OK; else - cat >&2 <<'END' -Oops! - -Your 'rm' program seems unable to run without file operands specified -on the command line, even when the '-f' option is present. This is contrary -to the behaviour of most rm programs out there, and not conforming with -the upcoming POSIX standard: - -Please tell bug-automake@gnu.org about your system, including the value -of your $PATH and any error possibly output before this message. This -can help us improve future automake versions. - -END - if test x"$ACCEPT_INFERIOR_RM_PROGRAM" = x"yes"; then - echo 'Configuration will proceed anyway, since you have set the' >&2 - echo 'ACCEPT_INFERIOR_RM_PROGRAM variable to "yes"' >&2 - echo >&2 - else - cat >&2 <<'END' -Aborting the configuration process, to ensure you take notice of the issue. - -You can download and install GNU coreutils to get an 'rm' implementation -that behaves properly: . - -If you want to complete the configuration process using your problematic -'rm' anyway, export the environment variable ACCEPT_INFERIOR_RM_PROGRAM -to "yes", and re-run configure. - -END - AC_MSG_ERROR([Your 'rm' program is bad, sorry.]) - fi -fi -]) - -dnl Hook into '_AC_COMPILER_EXEEXT' early to learn its expansion. Do not -dnl add the conditional right here, as _AC_COMPILER_EXEEXT may be further -dnl mangled by Autoconf and run in a shell conditional statement. -m4_define([_AC_COMPILER_EXEEXT], -m4_defn([_AC_COMPILER_EXEEXT])[m4_provide([_AM_COMPILER_EXEEXT])]) - -# When config.status generates a header, we must update the stamp-h file. -# This file resides in the same directory as the config header -# that is generated. The stamp files are numbered to have different names. - -# Autoconf calls _AC_AM_CONFIG_HEADER_HOOK (when defined) in the -# loop where config.status creates the headers, so we can generate -# our stamp files there. -AC_DEFUN([_AC_AM_CONFIG_HEADER_HOOK], -[# Compute $1's index in $config_headers. -_am_arg=$1 -_am_stamp_count=1 -for _am_header in $config_headers :; do - case $_am_header in - $_am_arg | $_am_arg:* ) - break ;; - * ) - _am_stamp_count=`expr $_am_stamp_count + 1` ;; - esac -done -echo "timestamp for $_am_arg" >`AS_DIRNAME(["$_am_arg"])`/stamp-h[]$_am_stamp_count]) - -# Copyright (C) 2001-2013 Free Software Foundation, Inc. -# -# This file is free software; the Free Software Foundation -# gives unlimited permission to copy and/or distribute it, -# with or without modifications, as long as this notice is preserved. - -# AM_PROG_INSTALL_SH -# ------------------ -# Define $install_sh. -AC_DEFUN([AM_PROG_INSTALL_SH], -[AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl -if test x"${install_sh}" != xset; then - case $am_aux_dir in - *\ * | *\ *) - install_sh="\${SHELL} '$am_aux_dir/install-sh'" ;; - *) - install_sh="\${SHELL} $am_aux_dir/install-sh" - esac -fi -AC_SUBST([install_sh])]) - -# Copyright (C) 2003-2013 Free Software Foundation, Inc. -# -# This file is free software; the Free Software Foundation -# gives unlimited permission to copy and/or distribute it, -# with or without modifications, as long as this notice is preserved. - -# Check whether the underlying file-system supports filenames -# with a leading dot. For instance MS-DOS doesn't. -AC_DEFUN([AM_SET_LEADING_DOT], -[rm -rf .tst 2>/dev/null -mkdir .tst 2>/dev/null -if test -d .tst; then - am__leading_dot=. -else - am__leading_dot=_ -fi -rmdir .tst 2>/dev/null -AC_SUBST([am__leading_dot])]) - -# Check to see how 'make' treats includes. -*- Autoconf -*- - -# Copyright (C) 2001-2013 Free Software Foundation, Inc. -# -# This file is free software; the Free Software Foundation -# gives unlimited permission to copy and/or distribute it, -# with or without modifications, as long as this notice is preserved. - -# AM_MAKE_INCLUDE() -# ----------------- -# Check to see how make treats includes. -AC_DEFUN([AM_MAKE_INCLUDE], -[am_make=${MAKE-make} -cat > confinc << 'END' -am__doit: - @echo this is the am__doit target -.PHONY: am__doit -END -# If we don't find an include directive, just comment out the code. -AC_MSG_CHECKING([for style of include used by $am_make]) -am__include="#" -am__quote= -_am_result=none -# First try GNU make style include. -echo "include confinc" > confmf -# Ignore all kinds of additional output from 'make'. -case `$am_make -s -f confmf 2> /dev/null` in #( -*the\ am__doit\ target*) - am__include=include - am__quote= - _am_result=GNU - ;; -esac -# Now try BSD make style include. -if test "$am__include" = "#"; then - echo '.include "confinc"' > confmf - case `$am_make -s -f confmf 2> /dev/null` in #( - *the\ am__doit\ target*) - am__include=.include - am__quote="\"" - _am_result=BSD - ;; - esac -fi -AC_SUBST([am__include]) -AC_SUBST([am__quote]) -AC_MSG_RESULT([$_am_result]) -rm -f confinc confmf -]) - -# Fake the existence of programs that GNU maintainers use. -*- Autoconf -*- - -# Copyright (C) 1997-2013 Free Software Foundation, Inc. -# -# This file is free software; the Free Software Foundation -# gives unlimited permission to copy and/or distribute it, -# with or without modifications, as long as this notice is preserved. - -# AM_MISSING_PROG(NAME, PROGRAM) -# ------------------------------ -AC_DEFUN([AM_MISSING_PROG], -[AC_REQUIRE([AM_MISSING_HAS_RUN]) -$1=${$1-"${am_missing_run}$2"} -AC_SUBST($1)]) - -# AM_MISSING_HAS_RUN -# ------------------ -# Define MISSING if not defined so far and test if it is modern enough. -# If it is, set am_missing_run to use it, otherwise, to nothing. -AC_DEFUN([AM_MISSING_HAS_RUN], -[AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl -AC_REQUIRE_AUX_FILE([missing])dnl -if test x"${MISSING+set}" != xset; then - case $am_aux_dir in - *\ * | *\ *) - MISSING="\${SHELL} \"$am_aux_dir/missing\"" ;; - *) - MISSING="\${SHELL} $am_aux_dir/missing" ;; - esac -fi -# Use eval to expand $SHELL -if eval "$MISSING --is-lightweight"; then - am_missing_run="$MISSING " -else - am_missing_run= - AC_MSG_WARN(['missing' script is too old or missing]) -fi -]) - -# Helper functions for option handling. -*- Autoconf -*- - -# Copyright (C) 2001-2013 Free Software Foundation, Inc. -# -# This file is free software; the Free Software Foundation -# gives unlimited permission to copy and/or distribute it, -# with or without modifications, as long as this notice is preserved. - -# _AM_MANGLE_OPTION(NAME) -# ----------------------- -AC_DEFUN([_AM_MANGLE_OPTION], -[[_AM_OPTION_]m4_bpatsubst($1, [[^a-zA-Z0-9_]], [_])]) - -# _AM_SET_OPTION(NAME) -# -------------------- -# Set option NAME. Presently that only means defining a flag for this option. -AC_DEFUN([_AM_SET_OPTION], -[m4_define(_AM_MANGLE_OPTION([$1]), [1])]) - -# _AM_SET_OPTIONS(OPTIONS) -# ------------------------ -# OPTIONS is a space-separated list of Automake options. -AC_DEFUN([_AM_SET_OPTIONS], -[m4_foreach_w([_AM_Option], [$1], [_AM_SET_OPTION(_AM_Option)])]) - -# _AM_IF_OPTION(OPTION, IF-SET, [IF-NOT-SET]) -# ------------------------------------------- -# Execute IF-SET if OPTION is set, IF-NOT-SET otherwise. -AC_DEFUN([_AM_IF_OPTION], -[m4_ifset(_AM_MANGLE_OPTION([$1]), [$2], [$3])]) - -# Copyright (C) 1999-2013 Free Software Foundation, Inc. -# -# This file is free software; the Free Software Foundation -# gives unlimited permission to copy and/or distribute it, -# with or without modifications, as long as this notice is preserved. - -# _AM_PROG_CC_C_O -# --------------- -# Like AC_PROG_CC_C_O, but changed for automake. We rewrite AC_PROG_CC -# to automatically call this. -AC_DEFUN([_AM_PROG_CC_C_O], -[AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl -AC_REQUIRE_AUX_FILE([compile])dnl -AC_LANG_PUSH([C])dnl -AC_CACHE_CHECK( - [whether $CC understands -c and -o together], - [am_cv_prog_cc_c_o], - [AC_LANG_CONFTEST([AC_LANG_PROGRAM([])]) - # Make sure it works both with $CC and with simple cc. - # Following AC_PROG_CC_C_O, we do the test twice because some - # compilers refuse to overwrite an existing .o file with -o, - # though they will create one. - am_cv_prog_cc_c_o=yes - for am_i in 1 2; do - if AM_RUN_LOG([$CC -c conftest.$ac_ext -o conftest2.$ac_objext]) \ - && test -f conftest2.$ac_objext; then - : OK - else - am_cv_prog_cc_c_o=no - break - fi - done - rm -f core conftest* - unset am_i]) -if test "$am_cv_prog_cc_c_o" != yes; then - # Losing compiler, so override with the script. - # FIXME: It is wrong to rewrite CC. - # But if we don't then we get into trouble of one sort or another. - # A longer-term fix would be to have automake use am__CC in this case, - # and then we could set am__CC="\$(top_srcdir)/compile \$(CC)" - CC="$am_aux_dir/compile $CC" -fi -AC_LANG_POP([C])]) - -# For backward compatibility. -AC_DEFUN_ONCE([AM_PROG_CC_C_O], [AC_REQUIRE([AC_PROG_CC])]) - -# Copyright (C) 1999-2013 Free Software Foundation, Inc. -# -# This file is free software; the Free Software Foundation -# gives unlimited permission to copy and/or distribute it, -# with or without modifications, as long as this notice is preserved. - - -# AM_PATH_PYTHON([MINIMUM-VERSION], [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) -# --------------------------------------------------------------------------- -# Adds support for distributing Python modules and packages. To -# install modules, copy them to $(pythondir), using the python_PYTHON -# automake variable. To install a package with the same name as the -# automake package, install to $(pkgpythondir), or use the -# pkgpython_PYTHON automake variable. -# -# The variables $(pyexecdir) and $(pkgpyexecdir) are provided as -# locations to install python extension modules (shared libraries). -# Another macro is required to find the appropriate flags to compile -# extension modules. -# -# If your package is configured with a different prefix to python, -# users will have to add the install directory to the PYTHONPATH -# environment variable, or create a .pth file (see the python -# documentation for details). -# -# If the MINIMUM-VERSION argument is passed, AM_PATH_PYTHON will -# cause an error if the version of python installed on the system -# doesn't meet the requirement. MINIMUM-VERSION should consist of -# numbers and dots only. -AC_DEFUN([AM_PATH_PYTHON], - [ - dnl Find a Python interpreter. Python versions prior to 2.0 are not - dnl supported. (2.0 was released on October 16, 2000). - m4_define_default([_AM_PYTHON_INTERPRETER_LIST], -[python python2 python3 python3.3 python3.2 python3.1 python3.0 python2.7 dnl - python2.6 python2.5 python2.4 python2.3 python2.2 python2.1 python2.0]) - - AC_ARG_VAR([PYTHON], [the Python interpreter]) - - m4_if([$1],[],[ - dnl No version check is needed. - # Find any Python interpreter. - if test -z "$PYTHON"; then - AC_PATH_PROGS([PYTHON], _AM_PYTHON_INTERPRETER_LIST, :) - fi - am_display_PYTHON=python - ], [ - dnl A version check is needed. - if test -n "$PYTHON"; then - # If the user set $PYTHON, use it and don't search something else. - AC_MSG_CHECKING([whether $PYTHON version is >= $1]) - AM_PYTHON_CHECK_VERSION([$PYTHON], [$1], - [AC_MSG_RESULT([yes])], - [AC_MSG_RESULT([no]) - AC_MSG_ERROR([Python interpreter is too old])]) - am_display_PYTHON=$PYTHON - else - # Otherwise, try each interpreter until we find one that satisfies - # VERSION. - AC_CACHE_CHECK([for a Python interpreter with version >= $1], - [am_cv_pathless_PYTHON],[ - for am_cv_pathless_PYTHON in _AM_PYTHON_INTERPRETER_LIST none; do - test "$am_cv_pathless_PYTHON" = none && break - AM_PYTHON_CHECK_VERSION([$am_cv_pathless_PYTHON], [$1], [break]) - done]) - # Set $PYTHON to the absolute path of $am_cv_pathless_PYTHON. - if test "$am_cv_pathless_PYTHON" = none; then - PYTHON=: - else - AC_PATH_PROG([PYTHON], [$am_cv_pathless_PYTHON]) - fi - am_display_PYTHON=$am_cv_pathless_PYTHON - fi - ]) - - if test "$PYTHON" = :; then - dnl Run any user-specified action, or abort. - m4_default([$3], [AC_MSG_ERROR([no suitable Python interpreter found])]) - else - - dnl Query Python for its version number. Getting [:3] seems to be - dnl the best way to do this; it's what "site.py" does in the standard - dnl library. - - AC_CACHE_CHECK([for $am_display_PYTHON version], [am_cv_python_version], - [am_cv_python_version=`$PYTHON -c "import sys; sys.stdout.write(sys.version[[:3]])"`]) - AC_SUBST([PYTHON_VERSION], [$am_cv_python_version]) - - dnl Use the values of $prefix and $exec_prefix for the corresponding - dnl values of PYTHON_PREFIX and PYTHON_EXEC_PREFIX. These are made - dnl distinct variables so they can be overridden if need be. However, - dnl general consensus is that you shouldn't need this ability. - - AC_SUBST([PYTHON_PREFIX], ['${prefix}']) - AC_SUBST([PYTHON_EXEC_PREFIX], ['${exec_prefix}']) - - dnl At times (like when building shared libraries) you may want - dnl to know which OS platform Python thinks this is. - - AC_CACHE_CHECK([for $am_display_PYTHON platform], [am_cv_python_platform], - [am_cv_python_platform=`$PYTHON -c "import sys; sys.stdout.write(sys.platform)"`]) - AC_SUBST([PYTHON_PLATFORM], [$am_cv_python_platform]) - - # Just factor out some code duplication. - am_python_setup_sysconfig="\ -import sys -# Prefer sysconfig over distutils.sysconfig, for better compatibility -# with python 3.x. See automake bug#10227. -try: - import sysconfig -except ImportError: - can_use_sysconfig = 0 -else: - can_use_sysconfig = 1 -# Can't use sysconfig in CPython 2.7, since it's broken in virtualenvs: -# -try: - from platform import python_implementation - if python_implementation() == 'CPython' and sys.version[[:3]] == '2.7': - can_use_sysconfig = 0 -except ImportError: - pass" - - dnl Set up 4 directories: - - dnl pythondir -- where to install python scripts. This is the - dnl site-packages directory, not the python standard library - dnl directory like in previous automake betas. This behavior - dnl is more consistent with lispdir.m4 for example. - dnl Query distutils for this directory. - AC_CACHE_CHECK([for $am_display_PYTHON script directory], - [am_cv_python_pythondir], - [if test "x$prefix" = xNONE - then - am_py_prefix=$ac_default_prefix - else - am_py_prefix=$prefix - fi - am_cv_python_pythondir=`$PYTHON -c " -$am_python_setup_sysconfig -if can_use_sysconfig: - sitedir = sysconfig.get_path('purelib', vars={'base':'$am_py_prefix'}) -else: - from distutils import sysconfig - sitedir = sysconfig.get_python_lib(0, 0, prefix='$am_py_prefix') -sys.stdout.write(sitedir)"` - case $am_cv_python_pythondir in - $am_py_prefix*) - am__strip_prefix=`echo "$am_py_prefix" | sed 's|.|.|g'` - am_cv_python_pythondir=`echo "$am_cv_python_pythondir" | sed "s,^$am__strip_prefix,$PYTHON_PREFIX,"` - ;; - *) - case $am_py_prefix in - /usr|/System*) ;; - *) - am_cv_python_pythondir=$PYTHON_PREFIX/lib/python$PYTHON_VERSION/site-packages - ;; - esac - ;; - esac - ]) - AC_SUBST([pythondir], [$am_cv_python_pythondir]) - - dnl pkgpythondir -- $PACKAGE directory under pythondir. Was - dnl PYTHON_SITE_PACKAGE in previous betas, but this naming is - dnl more consistent with the rest of automake. - - AC_SUBST([pkgpythondir], [\${pythondir}/$PACKAGE]) - - dnl pyexecdir -- directory for installing python extension modules - dnl (shared libraries) - dnl Query distutils for this directory. - AC_CACHE_CHECK([for $am_display_PYTHON extension module directory], - [am_cv_python_pyexecdir], - [if test "x$exec_prefix" = xNONE - then - am_py_exec_prefix=$am_py_prefix - else - am_py_exec_prefix=$exec_prefix - fi - am_cv_python_pyexecdir=`$PYTHON -c " -$am_python_setup_sysconfig -if can_use_sysconfig: - sitedir = sysconfig.get_path('platlib', vars={'platbase':'$am_py_prefix'}) -else: - from distutils import sysconfig - sitedir = sysconfig.get_python_lib(1, 0, prefix='$am_py_prefix') -sys.stdout.write(sitedir)"` - case $am_cv_python_pyexecdir in - $am_py_exec_prefix*) - am__strip_prefix=`echo "$am_py_exec_prefix" | sed 's|.|.|g'` - am_cv_python_pyexecdir=`echo "$am_cv_python_pyexecdir" | sed "s,^$am__strip_prefix,$PYTHON_EXEC_PREFIX,"` - ;; - *) - case $am_py_exec_prefix in - /usr|/System*) ;; - *) - am_cv_python_pyexecdir=$PYTHON_EXEC_PREFIX/lib/python$PYTHON_VERSION/site-packages - ;; - esac - ;; - esac - ]) - AC_SUBST([pyexecdir], [$am_cv_python_pyexecdir]) - - dnl pkgpyexecdir -- $(pyexecdir)/$(PACKAGE) - - AC_SUBST([pkgpyexecdir], [\${pyexecdir}/$PACKAGE]) - - dnl Run any user-specified action. - $2 - fi - -]) - - -# AM_PYTHON_CHECK_VERSION(PROG, VERSION, [ACTION-IF-TRUE], [ACTION-IF-FALSE]) -# --------------------------------------------------------------------------- -# Run ACTION-IF-TRUE if the Python interpreter PROG has version >= VERSION. -# Run ACTION-IF-FALSE otherwise. -# This test uses sys.hexversion instead of the string equivalent (first -# word of sys.version), in order to cope with versions such as 2.2c1. -# This supports Python 2.0 or higher. (2.0 was released on October 16, 2000). -AC_DEFUN([AM_PYTHON_CHECK_VERSION], - [prog="import sys -# split strings by '.' and convert to numeric. Append some zeros -# because we need at least 4 digits for the hex conversion. -# map returns an iterator in Python 3.0 and a list in 2.x -minver = list(map(int, '$2'.split('.'))) + [[0, 0, 0]] -minverhex = 0 -# xrange is not present in Python 3.0 and range returns an iterator -for i in list(range(0, 4)): minverhex = (minverhex << 8) + minver[[i]] -sys.exit(sys.hexversion < minverhex)" - AS_IF([AM_RUN_LOG([$1 -c "$prog"])], [$3], [$4])]) - -# Copyright (C) 2001-2013 Free Software Foundation, Inc. -# -# This file is free software; the Free Software Foundation -# gives unlimited permission to copy and/or distribute it, -# with or without modifications, as long as this notice is preserved. - -# AM_RUN_LOG(COMMAND) -# ------------------- -# Run COMMAND, save the exit status in ac_status, and log it. -# (This has been adapted from Autoconf's _AC_RUN_LOG macro.) -AC_DEFUN([AM_RUN_LOG], -[{ echo "$as_me:$LINENO: $1" >&AS_MESSAGE_LOG_FD - ($1) >&AS_MESSAGE_LOG_FD 2>&AS_MESSAGE_LOG_FD - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&AS_MESSAGE_LOG_FD - (exit $ac_status); }]) - -# Check to make sure that the build environment is sane. -*- Autoconf -*- - -# Copyright (C) 1996-2013 Free Software Foundation, Inc. -# -# This file is free software; the Free Software Foundation -# gives unlimited permission to copy and/or distribute it, -# with or without modifications, as long as this notice is preserved. - -# AM_SANITY_CHECK -# --------------- -AC_DEFUN([AM_SANITY_CHECK], -[AC_MSG_CHECKING([whether build environment is sane]) -# Reject unsafe characters in $srcdir or the absolute working directory -# name. Accept space and tab only in the latter. -am_lf=' -' -case `pwd` in - *[[\\\"\#\$\&\'\`$am_lf]]*) - AC_MSG_ERROR([unsafe absolute working directory name]);; -esac -case $srcdir in - *[[\\\"\#\$\&\'\`$am_lf\ \ ]]*) - AC_MSG_ERROR([unsafe srcdir value: '$srcdir']);; -esac - -# Do 'set' in a subshell so we don't clobber the current shell's -# arguments. Must try -L first in case configure is actually a -# symlink; some systems play weird games with the mod time of symlinks -# (eg FreeBSD returns the mod time of the symlink's containing -# directory). -if ( - am_has_slept=no - for am_try in 1 2; do - echo "timestamp, slept: $am_has_slept" > conftest.file - set X `ls -Lt "$srcdir/configure" conftest.file 2> /dev/null` - if test "$[*]" = "X"; then - # -L didn't work. - set X `ls -t "$srcdir/configure" conftest.file` - fi - if test "$[*]" != "X $srcdir/configure conftest.file" \ - && test "$[*]" != "X conftest.file $srcdir/configure"; then - - # If neither matched, then we have a broken ls. This can happen - # if, for instance, CONFIG_SHELL is bash and it inherits a - # broken ls alias from the environment. This has actually - # happened. Such a system could not be considered "sane". - AC_MSG_ERROR([ls -t appears to fail. Make sure there is not a broken - alias in your environment]) - fi - if test "$[2]" = conftest.file || test $am_try -eq 2; then - break - fi - # Just in case. - sleep 1 - am_has_slept=yes - done - test "$[2]" = conftest.file - ) -then - # Ok. - : -else - AC_MSG_ERROR([newly created file is older than distributed files! -Check your system clock]) -fi -AC_MSG_RESULT([yes]) -# If we didn't sleep, we still need to ensure time stamps of config.status and -# generated files are strictly newer. -am_sleep_pid= -if grep 'slept: no' conftest.file >/dev/null 2>&1; then - ( sleep 1 ) & - am_sleep_pid=$! -fi -AC_CONFIG_COMMANDS_PRE( - [AC_MSG_CHECKING([that generated files are newer than configure]) - if test -n "$am_sleep_pid"; then - # Hide warnings about reused PIDs. - wait $am_sleep_pid 2>/dev/null - fi - AC_MSG_RESULT([done])]) -rm -f conftest.file -]) - -# Copyright (C) 2009-2013 Free Software Foundation, Inc. -# -# This file is free software; the Free Software Foundation -# gives unlimited permission to copy and/or distribute it, -# with or without modifications, as long as this notice is preserved. - -# AM_SILENT_RULES([DEFAULT]) -# -------------------------- -# Enable less verbose build rules; with the default set to DEFAULT -# ("yes" being less verbose, "no" or empty being verbose). -AC_DEFUN([AM_SILENT_RULES], -[AC_ARG_ENABLE([silent-rules], [dnl -AS_HELP_STRING( - [--enable-silent-rules], - [less verbose build output (undo: "make V=1")]) -AS_HELP_STRING( - [--disable-silent-rules], - [verbose build output (undo: "make V=0")])dnl -]) -case $enable_silent_rules in @%:@ ((( - yes) AM_DEFAULT_VERBOSITY=0;; - no) AM_DEFAULT_VERBOSITY=1;; - *) AM_DEFAULT_VERBOSITY=m4_if([$1], [yes], [0], [1]);; -esac -dnl -dnl A few 'make' implementations (e.g., NonStop OS and NextStep) -dnl do not support nested variable expansions. -dnl See automake bug#9928 and bug#10237. -am_make=${MAKE-make} -AC_CACHE_CHECK([whether $am_make supports nested variables], - [am_cv_make_support_nested_variables], - [if AS_ECHO([['TRUE=$(BAR$(V)) -BAR0=false -BAR1=true -V=1 -am__doit: - @$(TRUE) -.PHONY: am__doit']]) | $am_make -f - >/dev/null 2>&1; then - am_cv_make_support_nested_variables=yes -else - am_cv_make_support_nested_variables=no -fi]) -if test $am_cv_make_support_nested_variables = yes; then - dnl Using '$V' instead of '$(V)' breaks IRIX make. - AM_V='$(V)' - AM_DEFAULT_V='$(AM_DEFAULT_VERBOSITY)' -else - AM_V=$AM_DEFAULT_VERBOSITY - AM_DEFAULT_V=$AM_DEFAULT_VERBOSITY -fi -AC_SUBST([AM_V])dnl -AM_SUBST_NOTMAKE([AM_V])dnl -AC_SUBST([AM_DEFAULT_V])dnl -AM_SUBST_NOTMAKE([AM_DEFAULT_V])dnl -AC_SUBST([AM_DEFAULT_VERBOSITY])dnl -AM_BACKSLASH='\' -AC_SUBST([AM_BACKSLASH])dnl -_AM_SUBST_NOTMAKE([AM_BACKSLASH])dnl -]) - -# Copyright (C) 2001-2013 Free Software Foundation, Inc. -# -# This file is free software; the Free Software Foundation -# gives unlimited permission to copy and/or distribute it, -# with or without modifications, as long as this notice is preserved. - -# AM_PROG_INSTALL_STRIP -# --------------------- -# One issue with vendor 'install' (even GNU) is that you can't -# specify the program used to strip binaries. This is especially -# annoying in cross-compiling environments, where the build's strip -# is unlikely to handle the host's binaries. -# Fortunately install-sh will honor a STRIPPROG variable, so we -# always use install-sh in "make install-strip", and initialize -# STRIPPROG with the value of the STRIP variable (set by the user). -AC_DEFUN([AM_PROG_INSTALL_STRIP], -[AC_REQUIRE([AM_PROG_INSTALL_SH])dnl -# Installed binaries are usually stripped using 'strip' when the user -# run "make install-strip". However 'strip' might not be the right -# tool to use in cross-compilation environments, therefore Automake -# will honor the 'STRIP' environment variable to overrule this program. -dnl Don't test for $cross_compiling = yes, because it might be 'maybe'. -if test "$cross_compiling" != no; then - AC_CHECK_TOOL([STRIP], [strip], :) -fi -INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s" -AC_SUBST([INSTALL_STRIP_PROGRAM])]) - -# Copyright (C) 2006-2013 Free Software Foundation, Inc. -# -# This file is free software; the Free Software Foundation -# gives unlimited permission to copy and/or distribute it, -# with or without modifications, as long as this notice is preserved. - -# _AM_SUBST_NOTMAKE(VARIABLE) -# --------------------------- -# Prevent Automake from outputting VARIABLE = @VARIABLE@ in Makefile.in. -# This macro is traced by Automake. -AC_DEFUN([_AM_SUBST_NOTMAKE]) - -# AM_SUBST_NOTMAKE(VARIABLE) -# -------------------------- -# Public sister of _AM_SUBST_NOTMAKE. -AC_DEFUN([AM_SUBST_NOTMAKE], [_AM_SUBST_NOTMAKE($@)]) - -# Check how to create a tarball. -*- Autoconf -*- - -# Copyright (C) 2004-2013 Free Software Foundation, Inc. -# -# This file is free software; the Free Software Foundation -# gives unlimited permission to copy and/or distribute it, -# with or without modifications, as long as this notice is preserved. - -# _AM_PROG_TAR(FORMAT) -# -------------------- -# Check how to create a tarball in format FORMAT. -# FORMAT should be one of 'v7', 'ustar', or 'pax'. -# -# Substitute a variable $(am__tar) that is a command -# writing to stdout a FORMAT-tarball containing the directory -# $tardir. -# tardir=directory && $(am__tar) > result.tar -# -# Substitute a variable $(am__untar) that extract such -# a tarball read from stdin. -# $(am__untar) < result.tar -# -AC_DEFUN([_AM_PROG_TAR], -[# Always define AMTAR for backward compatibility. Yes, it's still used -# in the wild :-( We should find a proper way to deprecate it ... -AC_SUBST([AMTAR], ['$${TAR-tar}']) - -# We'll loop over all known methods to create a tar archive until one works. -_am_tools='gnutar m4_if([$1], [ustar], [plaintar]) pax cpio none' - -m4_if([$1], [v7], - [am__tar='$${TAR-tar} chof - "$$tardir"' am__untar='$${TAR-tar} xf -'], - - [m4_case([$1], - [ustar], - [# The POSIX 1988 'ustar' format is defined with fixed-size fields. - # There is notably a 21 bits limit for the UID and the GID. In fact, - # the 'pax' utility can hang on bigger UID/GID (see automake bug#8343 - # and bug#13588). - am_max_uid=2097151 # 2^21 - 1 - am_max_gid=$am_max_uid - # The $UID and $GID variables are not portable, so we need to resort - # to the POSIX-mandated id(1) utility. Errors in the 'id' calls - # below are definitely unexpected, so allow the users to see them - # (that is, avoid stderr redirection). - am_uid=`id -u || echo unknown` - am_gid=`id -g || echo unknown` - AC_MSG_CHECKING([whether UID '$am_uid' is supported by ustar format]) - if test $am_uid -le $am_max_uid; then - AC_MSG_RESULT([yes]) - else - AC_MSG_RESULT([no]) - _am_tools=none - fi - AC_MSG_CHECKING([whether GID '$am_gid' is supported by ustar format]) - if test $am_gid -le $am_max_gid; then - AC_MSG_RESULT([yes]) - else - AC_MSG_RESULT([no]) - _am_tools=none - fi], - - [pax], - [], - - [m4_fatal([Unknown tar format])]) - - AC_MSG_CHECKING([how to create a $1 tar archive]) - - # Go ahead even if we have the value already cached. We do so because we - # need to set the values for the 'am__tar' and 'am__untar' variables. - _am_tools=${am_cv_prog_tar_$1-$_am_tools} - - for _am_tool in $_am_tools; do - case $_am_tool in - gnutar) - for _am_tar in tar gnutar gtar; do - AM_RUN_LOG([$_am_tar --version]) && break - done - am__tar="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$$tardir"' - am__tar_="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$tardir"' - am__untar="$_am_tar -xf -" - ;; - plaintar) - # Must skip GNU tar: if it does not support --format= it doesn't create - # ustar tarball either. - (tar --version) >/dev/null 2>&1 && continue - am__tar='tar chf - "$$tardir"' - am__tar_='tar chf - "$tardir"' - am__untar='tar xf -' - ;; - pax) - am__tar='pax -L -x $1 -w "$$tardir"' - am__tar_='pax -L -x $1 -w "$tardir"' - am__untar='pax -r' - ;; - cpio) - am__tar='find "$$tardir" -print | cpio -o -H $1 -L' - am__tar_='find "$tardir" -print | cpio -o -H $1 -L' - am__untar='cpio -i -H $1 -d' - ;; - none) - am__tar=false - am__tar_=false - am__untar=false - ;; - esac - - # If the value was cached, stop now. We just wanted to have am__tar - # and am__untar set. - test -n "${am_cv_prog_tar_$1}" && break - - # tar/untar a dummy directory, and stop if the command works. - rm -rf conftest.dir - mkdir conftest.dir - echo GrepMe > conftest.dir/file - AM_RUN_LOG([tardir=conftest.dir && eval $am__tar_ >conftest.tar]) - rm -rf conftest.dir - if test -s conftest.tar; then - AM_RUN_LOG([$am__untar /dev/null 2>&1 && break - fi - done - rm -rf conftest.dir - - AC_CACHE_VAL([am_cv_prog_tar_$1], [am_cv_prog_tar_$1=$_am_tool]) - AC_MSG_RESULT([$am_cv_prog_tar_$1])]) - -AC_SUBST([am__tar]) -AC_SUBST([am__untar]) -]) # _AM_PROG_TAR - -m4_include([m4/ax_boost_asio.m4]) -m4_include([m4/ax_boost_base.m4]) -m4_include([m4/ax_boost_system.m4]) -m4_include([m4/ax_boost_thread.m4]) -m4_include([m4/ax_check_compile_flag.m4]) -m4_include([m4/ax_cxx_compile_stdcxx_11.m4]) -m4_include([m4/ax_have_epoll.m4]) -m4_include([m4/ax_python_devel.m4]) -m4_include([m4/libtool.m4]) -m4_include([m4/libxml2.m4]) -m4_include([m4/ltoptions.m4]) -m4_include([m4/ltsugar.m4]) -m4_include([m4/ltversion.m4]) -m4_include([m4/lt~obsolete.m4]) diff --git a/android-config b/android-config deleted file mode 100755 index 9828822..0000000 --- a/android-config +++ /dev/null @@ -1,47 +0,0 @@ -#!/bin/sh -# -# nghttp2 - HTTP/2 C Library -# -# Copyright (c) 2013 Tatsuhiro Tsujikawa -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be -# included in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -if [ -z "$ANDROID_HOME" ]; then - echo 'No $ANDROID_HOME specified.' - exit 1 -fi -PREFIX=$ANDROID_HOME/usr/local -TOOLCHAIN=$ANDROID_HOME/toolchain -PATH=$TOOLCHAIN/bin:$PATH - -./configure \ - --disable-shared \ - --host=arm-linux-androideabi \ - --build=`dpkg-architecture -qDEB_BUILD_GNU_TYPE` \ - --with-xml-prefix="$PREFIX" \ - --without-libxml2 \ - --disable-python-bindings \ - --disable-examples \ - --enable-werror \ - CC=clang \ - CXX=clang++ \ - CPPFLAGS="-fPIE -I$PREFIX/include" \ - PKG_CONFIG_LIBDIR="$PREFIX/lib/pkgconfig" \ - LDFLAGS="-fPIE -pie -L$PREFIX/lib" diff --git a/android-make b/android-make deleted file mode 100755 index 375045a..0000000 --- a/android-make +++ /dev/null @@ -1,33 +0,0 @@ -#!/bin/sh -# -# nghttp2 - HTTP/2 C Library -# -# Copyright (c) 2013 Tatsuhiro Tsujikawa -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be -# included in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -if [ -z "$ANDROID_HOME" ]; then - echo 'No $ANDROID_HOME specified.' - exit 1 -fi -TOOLCHAIN=$ANDROID_HOME/toolchain -PATH=$TOOLCHAIN/bin:$PATH - -make "$@" diff --git a/compile b/compile deleted file mode 100755 index 531136b..0000000 --- a/compile +++ /dev/null @@ -1,347 +0,0 @@ -#! /bin/sh -# Wrapper for compilers which do not understand '-c -o'. - -scriptversion=2012-10-14.11; # UTC - -# Copyright (C) 1999-2013 Free Software Foundation, Inc. -# Written by Tom Tromey . -# -# 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 2, 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 . - -# As a special exception to the GNU General Public License, if you -# distribute this file as part of a program that contains a -# configuration script generated by Autoconf, you may include it under -# the same distribution terms that you use for the rest of that program. - -# This file is maintained in Automake, please report -# bugs to or send patches to -# . - -nl=' -' - -# We need space, tab and new line, in precisely that order. Quoting is -# there to prevent tools from complaining about whitespace usage. -IFS=" "" $nl" - -file_conv= - -# func_file_conv build_file lazy -# Convert a $build file to $host form and store it in $file -# Currently only supports Windows hosts. If the determined conversion -# type is listed in (the comma separated) LAZY, no conversion will -# take place. -func_file_conv () -{ - file=$1 - case $file in - / | /[!/]*) # absolute file, and not a UNC file - if test -z "$file_conv"; then - # lazily determine how to convert abs files - case `uname -s` in - MINGW*) - file_conv=mingw - ;; - CYGWIN*) - file_conv=cygwin - ;; - *) - file_conv=wine - ;; - esac - fi - case $file_conv/,$2, in - *,$file_conv,*) - ;; - mingw/*) - file=`cmd //C echo "$file " | sed -e 's/"\(.*\) " *$/\1/'` - ;; - cygwin/*) - file=`cygpath -m "$file" || echo "$file"` - ;; - wine/*) - file=`winepath -w "$file" || echo "$file"` - ;; - esac - ;; - esac -} - -# func_cl_dashL linkdir -# Make cl look for libraries in LINKDIR -func_cl_dashL () -{ - func_file_conv "$1" - if test -z "$lib_path"; then - lib_path=$file - else - lib_path="$lib_path;$file" - fi - linker_opts="$linker_opts -LIBPATH:$file" -} - -# func_cl_dashl library -# Do a library search-path lookup for cl -func_cl_dashl () -{ - lib=$1 - found=no - save_IFS=$IFS - IFS=';' - for dir in $lib_path $LIB - do - IFS=$save_IFS - if $shared && test -f "$dir/$lib.dll.lib"; then - found=yes - lib=$dir/$lib.dll.lib - break - fi - if test -f "$dir/$lib.lib"; then - found=yes - lib=$dir/$lib.lib - break - fi - if test -f "$dir/lib$lib.a"; then - found=yes - lib=$dir/lib$lib.a - break - fi - done - IFS=$save_IFS - - if test "$found" != yes; then - lib=$lib.lib - fi -} - -# func_cl_wrapper cl arg... -# Adjust compile command to suit cl -func_cl_wrapper () -{ - # Assume a capable shell - lib_path= - shared=: - linker_opts= - for arg - do - if test -n "$eat"; then - eat= - else - case $1 in - -o) - # configure might choose to run compile as 'compile cc -o foo foo.c'. - eat=1 - case $2 in - *.o | *.[oO][bB][jJ]) - func_file_conv "$2" - set x "$@" -Fo"$file" - shift - ;; - *) - func_file_conv "$2" - set x "$@" -Fe"$file" - shift - ;; - esac - ;; - -I) - eat=1 - func_file_conv "$2" mingw - set x "$@" -I"$file" - shift - ;; - -I*) - func_file_conv "${1#-I}" mingw - set x "$@" -I"$file" - shift - ;; - -l) - eat=1 - func_cl_dashl "$2" - set x "$@" "$lib" - shift - ;; - -l*) - func_cl_dashl "${1#-l}" - set x "$@" "$lib" - shift - ;; - -L) - eat=1 - func_cl_dashL "$2" - ;; - -L*) - func_cl_dashL "${1#-L}" - ;; - -static) - shared=false - ;; - -Wl,*) - arg=${1#-Wl,} - save_ifs="$IFS"; IFS=',' - for flag in $arg; do - IFS="$save_ifs" - linker_opts="$linker_opts $flag" - done - IFS="$save_ifs" - ;; - -Xlinker) - eat=1 - linker_opts="$linker_opts $2" - ;; - -*) - set x "$@" "$1" - shift - ;; - *.cc | *.CC | *.cxx | *.CXX | *.[cC]++) - func_file_conv "$1" - set x "$@" -Tp"$file" - shift - ;; - *.c | *.cpp | *.CPP | *.lib | *.LIB | *.Lib | *.OBJ | *.obj | *.[oO]) - func_file_conv "$1" mingw - set x "$@" "$file" - shift - ;; - *) - set x "$@" "$1" - shift - ;; - esac - fi - shift - done - if test -n "$linker_opts"; then - linker_opts="-link$linker_opts" - fi - exec "$@" $linker_opts - exit 1 -} - -eat= - -case $1 in - '') - echo "$0: No command. Try '$0 --help' for more information." 1>&2 - exit 1; - ;; - -h | --h*) - cat <<\EOF -Usage: compile [--help] [--version] PROGRAM [ARGS] - -Wrapper for compilers which do not understand '-c -o'. -Remove '-o dest.o' from ARGS, run PROGRAM with the remaining -arguments, and rename the output as expected. - -If you are trying to build a whole package this is not the -right script to run: please start by reading the file 'INSTALL'. - -Report bugs to . -EOF - exit $? - ;; - -v | --v*) - echo "compile $scriptversion" - exit $? - ;; - cl | *[/\\]cl | cl.exe | *[/\\]cl.exe ) - func_cl_wrapper "$@" # Doesn't return... - ;; -esac - -ofile= -cfile= - -for arg -do - if test -n "$eat"; then - eat= - else - case $1 in - -o) - # configure might choose to run compile as 'compile cc -o foo foo.c'. - # So we strip '-o arg' only if arg is an object. - eat=1 - case $2 in - *.o | *.obj) - ofile=$2 - ;; - *) - set x "$@" -o "$2" - shift - ;; - esac - ;; - *.c) - cfile=$1 - set x "$@" "$1" - shift - ;; - *) - set x "$@" "$1" - shift - ;; - esac - fi - shift -done - -if test -z "$ofile" || test -z "$cfile"; then - # If no '-o' option was seen then we might have been invoked from a - # pattern rule where we don't need one. That is ok -- this is a - # normal compilation that the losing compiler can handle. If no - # '.c' file was seen then we are probably linking. That is also - # ok. - exec "$@" -fi - -# Name of file we expect compiler to create. -cofile=`echo "$cfile" | sed 's|^.*[\\/]||; s|^[a-zA-Z]:||; s/\.c$/.o/'` - -# Create the lock directory. -# Note: use '[/\\:.-]' here to ensure that we don't use the same name -# that we are using for the .o file. Also, base the name on the expected -# object file name, since that is what matters with a parallel build. -lockdir=`echo "$cofile" | sed -e 's|[/\\:.-]|_|g'`.d -while true; do - if mkdir "$lockdir" >/dev/null 2>&1; then - break - fi - sleep 1 -done -# FIXME: race condition here if user kills between mkdir and trap. -trap "rmdir '$lockdir'; exit 1" 1 2 15 - -# Run the compile. -"$@" -ret=$? - -if test -f "$cofile"; then - test "$cofile" = "$ofile" || mv "$cofile" "$ofile" -elif test -f "${cofile}bj"; then - test "${cofile}bj" = "$ofile" || mv "${cofile}bj" "$ofile" -fi - -rmdir "$lockdir" -exit $ret - -# Local Variables: -# mode: shell-script -# sh-indentation: 2 -# eval: (add-hook 'write-file-hooks 'time-stamp) -# time-stamp-start: "scriptversion=" -# time-stamp-format: "%:y-%02m-%02d.%02H" -# time-stamp-time-zone: "UTC" -# time-stamp-end: "; # UTC" -# End: diff --git a/config.guess b/config.guess deleted file mode 100755 index 1f5c50c..0000000 --- a/config.guess +++ /dev/null @@ -1,1420 +0,0 @@ -#! /bin/sh -# Attempt to guess a canonical system name. -# Copyright 1992-2014 Free Software Foundation, Inc. - -timestamp='2014-03-23' - -# This file 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 . -# -# As a special exception to the GNU General Public License, if you -# distribute this file as part of a program that contains a -# configuration script generated by Autoconf, you may include it under -# the same distribution terms that you use for the rest of that -# program. This Exception is an additional permission under section 7 -# of the GNU General Public License, version 3 ("GPLv3"). -# -# Originally written by Per Bothner. -# -# You can get the latest version of this script from: -# http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess;hb=HEAD -# -# Please send patches with a ChangeLog entry to config-patches@gnu.org. - - -me=`echo "$0" | sed -e 's,.*/,,'` - -usage="\ -Usage: $0 [OPTION] - -Output the configuration name of the system \`$me' is run on. - -Operation modes: - -h, --help print this help, then exit - -t, --time-stamp print date of last modification, then exit - -v, --version print version number, then exit - -Report bugs and patches to ." - -version="\ -GNU config.guess ($timestamp) - -Originally written by Per Bothner. -Copyright 1992-2014 Free Software Foundation, Inc. - -This is free software; see the source for copying conditions. There is NO -warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." - -help=" -Try \`$me --help' for more information." - -# Parse command line -while test $# -gt 0 ; do - case $1 in - --time-stamp | --time* | -t ) - echo "$timestamp" ; exit ;; - --version | -v ) - echo "$version" ; exit ;; - --help | --h* | -h ) - echo "$usage"; exit ;; - -- ) # Stop option processing - shift; break ;; - - ) # Use stdin as input. - break ;; - -* ) - echo "$me: invalid option $1$help" >&2 - exit 1 ;; - * ) - break ;; - esac -done - -if test $# != 0; then - echo "$me: too many arguments$help" >&2 - exit 1 -fi - -trap 'exit 1' 1 2 15 - -# CC_FOR_BUILD -- compiler used by this script. Note that the use of a -# compiler to aid in system detection is discouraged as it requires -# temporary files to be created and, as you can see below, it is a -# headache to deal with in a portable fashion. - -# Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still -# use `HOST_CC' if defined, but it is deprecated. - -# Portable tmp directory creation inspired by the Autoconf team. - -set_cc_for_build=' -trap "exitcode=\$?; (rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null) && exit \$exitcode" 0 ; -trap "rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null; exit 1" 1 2 13 15 ; -: ${TMPDIR=/tmp} ; - { tmp=`(umask 077 && mktemp -d "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" ; } || - { test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir $tmp) ; } || - { tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir $tmp) && echo "Warning: creating insecure temp directory" >&2 ; } || - { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; } ; -dummy=$tmp/dummy ; -tmpfiles="$dummy.c $dummy.o $dummy.rel $dummy" ; -case $CC_FOR_BUILD,$HOST_CC,$CC in - ,,) echo "int x;" > $dummy.c ; - for c in cc gcc c89 c99 ; do - if ($c -c -o $dummy.o $dummy.c) >/dev/null 2>&1 ; then - CC_FOR_BUILD="$c"; break ; - fi ; - done ; - if test x"$CC_FOR_BUILD" = x ; then - CC_FOR_BUILD=no_compiler_found ; - fi - ;; - ,,*) CC_FOR_BUILD=$CC ;; - ,*,*) CC_FOR_BUILD=$HOST_CC ;; -esac ; set_cc_for_build= ;' - -# This is needed to find uname on a Pyramid OSx when run in the BSD universe. -# (ghazi@noc.rutgers.edu 1994-08-24) -if (test -f /.attbin/uname) >/dev/null 2>&1 ; then - PATH=$PATH:/.attbin ; export PATH -fi - -UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown -UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown -UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown -UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown - -case "${UNAME_SYSTEM}" in -Linux|GNU|GNU/*) - # If the system lacks a compiler, then just pick glibc. - # We could probably try harder. - LIBC=gnu - - eval $set_cc_for_build - cat <<-EOF > $dummy.c - #include - #if defined(__UCLIBC__) - LIBC=uclibc - #elif defined(__dietlibc__) - LIBC=dietlibc - #else - LIBC=gnu - #endif - EOF - eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^LIBC' | sed 's, ,,g'` - ;; -esac - -# Note: order is significant - the case branches are not exclusive. - -case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in - *:NetBSD:*:*) - # NetBSD (nbsd) targets should (where applicable) match one or - # more of the tuples: *-*-netbsdelf*, *-*-netbsdaout*, - # *-*-netbsdecoff* and *-*-netbsd*. For targets that recently - # switched to ELF, *-*-netbsd* would select the old - # object file format. This provides both forward - # compatibility and a consistent mechanism for selecting the - # object file format. - # - # Note: NetBSD doesn't particularly care about the vendor - # portion of the name. We always set it to "unknown". - sysctl="sysctl -n hw.machine_arch" - UNAME_MACHINE_ARCH=`(/sbin/$sysctl 2>/dev/null || \ - /usr/sbin/$sysctl 2>/dev/null || echo unknown)` - case "${UNAME_MACHINE_ARCH}" in - armeb) machine=armeb-unknown ;; - arm*) machine=arm-unknown ;; - sh3el) machine=shl-unknown ;; - sh3eb) machine=sh-unknown ;; - sh5el) machine=sh5le-unknown ;; - *) machine=${UNAME_MACHINE_ARCH}-unknown ;; - esac - # The Operating System including object format, if it has switched - # to ELF recently, or will in the future. - case "${UNAME_MACHINE_ARCH}" in - arm*|i386|m68k|ns32k|sh3*|sparc|vax) - eval $set_cc_for_build - if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \ - | grep -q __ELF__ - then - # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout). - # Return netbsd for either. FIX? - os=netbsd - else - os=netbsdelf - fi - ;; - *) - os=netbsd - ;; - esac - # The OS release - # Debian GNU/NetBSD machines have a different userland, and - # thus, need a distinct triplet. However, they do not need - # kernel version information, so it can be replaced with a - # suitable tag, in the style of linux-gnu. - case "${UNAME_VERSION}" in - Debian*) - release='-gnu' - ;; - *) - release=`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'` - ;; - esac - # Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM: - # contains redundant information, the shorter form: - # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used. - echo "${machine}-${os}${release}" - exit ;; - *:Bitrig:*:*) - UNAME_MACHINE_ARCH=`arch | sed 's/Bitrig.//'` - echo ${UNAME_MACHINE_ARCH}-unknown-bitrig${UNAME_RELEASE} - exit ;; - *:OpenBSD:*:*) - UNAME_MACHINE_ARCH=`arch | sed 's/OpenBSD.//'` - echo ${UNAME_MACHINE_ARCH}-unknown-openbsd${UNAME_RELEASE} - exit ;; - *:ekkoBSD:*:*) - echo ${UNAME_MACHINE}-unknown-ekkobsd${UNAME_RELEASE} - exit ;; - *:SolidBSD:*:*) - echo ${UNAME_MACHINE}-unknown-solidbsd${UNAME_RELEASE} - exit ;; - macppc:MirBSD:*:*) - echo powerpc-unknown-mirbsd${UNAME_RELEASE} - exit ;; - *:MirBSD:*:*) - echo ${UNAME_MACHINE}-unknown-mirbsd${UNAME_RELEASE} - exit ;; - alpha:OSF1:*:*) - case $UNAME_RELEASE in - *4.0) - UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'` - ;; - *5.*) - UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $4}'` - ;; - esac - # According to Compaq, /usr/sbin/psrinfo has been available on - # OSF/1 and Tru64 systems produced since 1995. I hope that - # covers most systems running today. This code pipes the CPU - # types through head -n 1, so we only detect the type of CPU 0. - ALPHA_CPU_TYPE=`/usr/sbin/psrinfo -v | sed -n -e 's/^ The alpha \(.*\) processor.*$/\1/p' | head -n 1` - case "$ALPHA_CPU_TYPE" in - "EV4 (21064)") - UNAME_MACHINE="alpha" ;; - "EV4.5 (21064)") - UNAME_MACHINE="alpha" ;; - "LCA4 (21066/21068)") - UNAME_MACHINE="alpha" ;; - "EV5 (21164)") - UNAME_MACHINE="alphaev5" ;; - "EV5.6 (21164A)") - UNAME_MACHINE="alphaev56" ;; - "EV5.6 (21164PC)") - UNAME_MACHINE="alphapca56" ;; - "EV5.7 (21164PC)") - UNAME_MACHINE="alphapca57" ;; - "EV6 (21264)") - UNAME_MACHINE="alphaev6" ;; - "EV6.7 (21264A)") - UNAME_MACHINE="alphaev67" ;; - "EV6.8CB (21264C)") - UNAME_MACHINE="alphaev68" ;; - "EV6.8AL (21264B)") - UNAME_MACHINE="alphaev68" ;; - "EV6.8CX (21264D)") - UNAME_MACHINE="alphaev68" ;; - "EV6.9A (21264/EV69A)") - UNAME_MACHINE="alphaev69" ;; - "EV7 (21364)") - UNAME_MACHINE="alphaev7" ;; - "EV7.9 (21364A)") - UNAME_MACHINE="alphaev79" ;; - esac - # A Pn.n version is a patched version. - # A Vn.n version is a released version. - # A Tn.n version is a released field test version. - # A Xn.n version is an unreleased experimental baselevel. - # 1.2 uses "1.2" for uname -r. - echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[PVTX]//' | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` - # Reset EXIT trap before exiting to avoid spurious non-zero exit code. - exitcode=$? - trap '' 0 - exit $exitcode ;; - Alpha\ *:Windows_NT*:*) - # How do we know it's Interix rather than the generic POSIX subsystem? - # Should we change UNAME_MACHINE based on the output of uname instead - # of the specific Alpha model? - echo alpha-pc-interix - exit ;; - 21064:Windows_NT:50:3) - echo alpha-dec-winnt3.5 - exit ;; - Amiga*:UNIX_System_V:4.0:*) - echo m68k-unknown-sysv4 - exit ;; - *:[Aa]miga[Oo][Ss]:*:*) - echo ${UNAME_MACHINE}-unknown-amigaos - exit ;; - *:[Mm]orph[Oo][Ss]:*:*) - echo ${UNAME_MACHINE}-unknown-morphos - exit ;; - *:OS/390:*:*) - echo i370-ibm-openedition - exit ;; - *:z/VM:*:*) - echo s390-ibm-zvmoe - exit ;; - *:OS400:*:*) - echo powerpc-ibm-os400 - exit ;; - arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*) - echo arm-acorn-riscix${UNAME_RELEASE} - exit ;; - arm*:riscos:*:*|arm*:RISCOS:*:*) - echo arm-unknown-riscos - exit ;; - SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*) - echo hppa1.1-hitachi-hiuxmpp - exit ;; - Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*) - # akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE. - if test "`(/bin/universe) 2>/dev/null`" = att ; then - echo pyramid-pyramid-sysv3 - else - echo pyramid-pyramid-bsd - fi - exit ;; - NILE*:*:*:dcosx) - echo pyramid-pyramid-svr4 - exit ;; - DRS?6000:unix:4.0:6*) - echo sparc-icl-nx6 - exit ;; - DRS?6000:UNIX_SV:4.2*:7* | DRS?6000:isis:4.2*:7*) - case `/usr/bin/uname -p` in - sparc) echo sparc-icl-nx7; exit ;; - esac ;; - s390x:SunOS:*:*) - echo ${UNAME_MACHINE}-ibm-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` - exit ;; - sun4H:SunOS:5.*:*) - echo sparc-hal-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` - exit ;; - sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*) - echo sparc-sun-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` - exit ;; - i86pc:AuroraUX:5.*:* | i86xen:AuroraUX:5.*:*) - echo i386-pc-auroraux${UNAME_RELEASE} - exit ;; - i86pc:SunOS:5.*:* | i86xen:SunOS:5.*:*) - eval $set_cc_for_build - SUN_ARCH="i386" - # If there is a compiler, see if it is configured for 64-bit objects. - # Note that the Sun cc does not turn __LP64__ into 1 like gcc does. - # This test works for both compilers. - if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then - if (echo '#ifdef __amd64'; echo IS_64BIT_ARCH; echo '#endif') | \ - (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \ - grep IS_64BIT_ARCH >/dev/null - then - SUN_ARCH="x86_64" - fi - fi - echo ${SUN_ARCH}-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` - exit ;; - sun4*:SunOS:6*:*) - # According to config.sub, this is the proper way to canonicalize - # SunOS6. Hard to guess exactly what SunOS6 will be like, but - # it's likely to be more like Solaris than SunOS4. - echo sparc-sun-solaris3`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` - exit ;; - sun4*:SunOS:*:*) - case "`/usr/bin/arch -k`" in - Series*|S4*) - UNAME_RELEASE=`uname -v` - ;; - esac - # Japanese Language versions have a version number like `4.1.3-JL'. - echo sparc-sun-sunos`echo ${UNAME_RELEASE}|sed -e 's/-/_/'` - exit ;; - sun3*:SunOS:*:*) - echo m68k-sun-sunos${UNAME_RELEASE} - exit ;; - sun*:*:4.2BSD:*) - UNAME_RELEASE=`(sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null` - test "x${UNAME_RELEASE}" = "x" && UNAME_RELEASE=3 - case "`/bin/arch`" in - sun3) - echo m68k-sun-sunos${UNAME_RELEASE} - ;; - sun4) - echo sparc-sun-sunos${UNAME_RELEASE} - ;; - esac - exit ;; - aushp:SunOS:*:*) - echo sparc-auspex-sunos${UNAME_RELEASE} - exit ;; - # The situation for MiNT is a little confusing. The machine name - # can be virtually everything (everything which is not - # "atarist" or "atariste" at least should have a processor - # > m68000). The system name ranges from "MiNT" over "FreeMiNT" - # to the lowercase version "mint" (or "freemint"). Finally - # the system name "TOS" denotes a system which is actually not - # MiNT. But MiNT is downward compatible to TOS, so this should - # be no problem. - atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*) - echo m68k-atari-mint${UNAME_RELEASE} - exit ;; - atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*) - echo m68k-atari-mint${UNAME_RELEASE} - exit ;; - *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*) - echo m68k-atari-mint${UNAME_RELEASE} - exit ;; - milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*) - echo m68k-milan-mint${UNAME_RELEASE} - exit ;; - hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*) - echo m68k-hades-mint${UNAME_RELEASE} - exit ;; - *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*) - echo m68k-unknown-mint${UNAME_RELEASE} - exit ;; - m68k:machten:*:*) - echo m68k-apple-machten${UNAME_RELEASE} - exit ;; - powerpc:machten:*:*) - echo powerpc-apple-machten${UNAME_RELEASE} - exit ;; - RISC*:Mach:*:*) - echo mips-dec-mach_bsd4.3 - exit ;; - RISC*:ULTRIX:*:*) - echo mips-dec-ultrix${UNAME_RELEASE} - exit ;; - VAX*:ULTRIX*:*:*) - echo vax-dec-ultrix${UNAME_RELEASE} - exit ;; - 2020:CLIX:*:* | 2430:CLIX:*:*) - echo clipper-intergraph-clix${UNAME_RELEASE} - exit ;; - mips:*:*:UMIPS | mips:*:*:RISCos) - eval $set_cc_for_build - sed 's/^ //' << EOF >$dummy.c -#ifdef __cplusplus -#include /* for printf() prototype */ - int main (int argc, char *argv[]) { -#else - int main (argc, argv) int argc; char *argv[]; { -#endif - #if defined (host_mips) && defined (MIPSEB) - #if defined (SYSTYPE_SYSV) - printf ("mips-mips-riscos%ssysv\n", argv[1]); exit (0); - #endif - #if defined (SYSTYPE_SVR4) - printf ("mips-mips-riscos%ssvr4\n", argv[1]); exit (0); - #endif - #if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD) - printf ("mips-mips-riscos%sbsd\n", argv[1]); exit (0); - #endif - #endif - exit (-1); - } -EOF - $CC_FOR_BUILD -o $dummy $dummy.c && - dummyarg=`echo "${UNAME_RELEASE}" | sed -n 's/\([0-9]*\).*/\1/p'` && - SYSTEM_NAME=`$dummy $dummyarg` && - { echo "$SYSTEM_NAME"; exit; } - echo mips-mips-riscos${UNAME_RELEASE} - exit ;; - Motorola:PowerMAX_OS:*:*) - echo powerpc-motorola-powermax - exit ;; - Motorola:*:4.3:PL8-*) - echo powerpc-harris-powermax - exit ;; - Night_Hawk:*:*:PowerMAX_OS | Synergy:PowerMAX_OS:*:*) - echo powerpc-harris-powermax - exit ;; - Night_Hawk:Power_UNIX:*:*) - echo powerpc-harris-powerunix - exit ;; - m88k:CX/UX:7*:*) - echo m88k-harris-cxux7 - exit ;; - m88k:*:4*:R4*) - echo m88k-motorola-sysv4 - exit ;; - m88k:*:3*:R3*) - echo m88k-motorola-sysv3 - exit ;; - AViiON:dgux:*:*) - # DG/UX returns AViiON for all architectures - UNAME_PROCESSOR=`/usr/bin/uname -p` - if [ $UNAME_PROCESSOR = mc88100 ] || [ $UNAME_PROCESSOR = mc88110 ] - then - if [ ${TARGET_BINARY_INTERFACE}x = m88kdguxelfx ] || \ - [ ${TARGET_BINARY_INTERFACE}x = x ] - then - echo m88k-dg-dgux${UNAME_RELEASE} - else - echo m88k-dg-dguxbcs${UNAME_RELEASE} - fi - else - echo i586-dg-dgux${UNAME_RELEASE} - fi - exit ;; - M88*:DolphinOS:*:*) # DolphinOS (SVR3) - echo m88k-dolphin-sysv3 - exit ;; - M88*:*:R3*:*) - # Delta 88k system running SVR3 - echo m88k-motorola-sysv3 - exit ;; - XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3) - echo m88k-tektronix-sysv3 - exit ;; - Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD) - echo m68k-tektronix-bsd - exit ;; - *:IRIX*:*:*) - echo mips-sgi-irix`echo ${UNAME_RELEASE}|sed -e 's/-/_/g'` - exit ;; - ????????:AIX?:[12].1:2) # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX. - echo romp-ibm-aix # uname -m gives an 8 hex-code CPU id - exit ;; # Note that: echo "'`uname -s`'" gives 'AIX ' - i*86:AIX:*:*) - echo i386-ibm-aix - exit ;; - ia64:AIX:*:*) - if [ -x /usr/bin/oslevel ] ; then - IBM_REV=`/usr/bin/oslevel` - else - IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE} - fi - echo ${UNAME_MACHINE}-ibm-aix${IBM_REV} - exit ;; - *:AIX:2:3) - if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then - eval $set_cc_for_build - sed 's/^ //' << EOF >$dummy.c - #include - - main() - { - if (!__power_pc()) - exit(1); - puts("powerpc-ibm-aix3.2.5"); - exit(0); - } -EOF - if $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy` - then - echo "$SYSTEM_NAME" - else - echo rs6000-ibm-aix3.2.5 - fi - elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then - echo rs6000-ibm-aix3.2.4 - else - echo rs6000-ibm-aix3.2 - fi - exit ;; - *:AIX:*:[4567]) - IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }'` - if /usr/sbin/lsattr -El ${IBM_CPU_ID} | grep ' POWER' >/dev/null 2>&1; then - IBM_ARCH=rs6000 - else - IBM_ARCH=powerpc - fi - if [ -x /usr/bin/oslevel ] ; then - IBM_REV=`/usr/bin/oslevel` - else - IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE} - fi - echo ${IBM_ARCH}-ibm-aix${IBM_REV} - exit ;; - *:AIX:*:*) - echo rs6000-ibm-aix - exit ;; - ibmrt:4.4BSD:*|romp-ibm:BSD:*) - echo romp-ibm-bsd4.4 - exit ;; - ibmrt:*BSD:*|romp-ibm:BSD:*) # covers RT/PC BSD and - echo romp-ibm-bsd${UNAME_RELEASE} # 4.3 with uname added to - exit ;; # report: romp-ibm BSD 4.3 - *:BOSX:*:*) - echo rs6000-bull-bosx - exit ;; - DPX/2?00:B.O.S.:*:*) - echo m68k-bull-sysv3 - exit ;; - 9000/[34]??:4.3bsd:1.*:*) - echo m68k-hp-bsd - exit ;; - hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*) - echo m68k-hp-bsd4.4 - exit ;; - 9000/[34678]??:HP-UX:*:*) - HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'` - case "${UNAME_MACHINE}" in - 9000/31? ) HP_ARCH=m68000 ;; - 9000/[34]?? ) HP_ARCH=m68k ;; - 9000/[678][0-9][0-9]) - if [ -x /usr/bin/getconf ]; then - sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null` - sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null` - case "${sc_cpu_version}" in - 523) HP_ARCH="hppa1.0" ;; # CPU_PA_RISC1_0 - 528) HP_ARCH="hppa1.1" ;; # CPU_PA_RISC1_1 - 532) # CPU_PA_RISC2_0 - case "${sc_kernel_bits}" in - 32) HP_ARCH="hppa2.0n" ;; - 64) HP_ARCH="hppa2.0w" ;; - '') HP_ARCH="hppa2.0" ;; # HP-UX 10.20 - esac ;; - esac - fi - if [ "${HP_ARCH}" = "" ]; then - eval $set_cc_for_build - sed 's/^ //' << EOF >$dummy.c - - #define _HPUX_SOURCE - #include - #include - - int main () - { - #if defined(_SC_KERNEL_BITS) - long bits = sysconf(_SC_KERNEL_BITS); - #endif - long cpu = sysconf (_SC_CPU_VERSION); - - switch (cpu) - { - case CPU_PA_RISC1_0: puts ("hppa1.0"); break; - case CPU_PA_RISC1_1: puts ("hppa1.1"); break; - case CPU_PA_RISC2_0: - #if defined(_SC_KERNEL_BITS) - switch (bits) - { - case 64: puts ("hppa2.0w"); break; - case 32: puts ("hppa2.0n"); break; - default: puts ("hppa2.0"); break; - } break; - #else /* !defined(_SC_KERNEL_BITS) */ - puts ("hppa2.0"); break; - #endif - default: puts ("hppa1.0"); break; - } - exit (0); - } -EOF - (CCOPTS= $CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null) && HP_ARCH=`$dummy` - test -z "$HP_ARCH" && HP_ARCH=hppa - fi ;; - esac - if [ ${HP_ARCH} = "hppa2.0w" ] - then - eval $set_cc_for_build - - # hppa2.0w-hp-hpux* has a 64-bit kernel and a compiler generating - # 32-bit code. hppa64-hp-hpux* has the same kernel and a compiler - # generating 64-bit code. GNU and HP use different nomenclature: - # - # $ CC_FOR_BUILD=cc ./config.guess - # => hppa2.0w-hp-hpux11.23 - # $ CC_FOR_BUILD="cc +DA2.0w" ./config.guess - # => hppa64-hp-hpux11.23 - - if echo __LP64__ | (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | - grep -q __LP64__ - then - HP_ARCH="hppa2.0w" - else - HP_ARCH="hppa64" - fi - fi - echo ${HP_ARCH}-hp-hpux${HPUX_REV} - exit ;; - ia64:HP-UX:*:*) - HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'` - echo ia64-hp-hpux${HPUX_REV} - exit ;; - 3050*:HI-UX:*:*) - eval $set_cc_for_build - sed 's/^ //' << EOF >$dummy.c - #include - int - main () - { - long cpu = sysconf (_SC_CPU_VERSION); - /* The order matters, because CPU_IS_HP_MC68K erroneously returns - true for CPU_PA_RISC1_0. CPU_IS_PA_RISC returns correct - results, however. */ - if (CPU_IS_PA_RISC (cpu)) - { - switch (cpu) - { - case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break; - case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break; - case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break; - default: puts ("hppa-hitachi-hiuxwe2"); break; - } - } - else if (CPU_IS_HP_MC68K (cpu)) - puts ("m68k-hitachi-hiuxwe2"); - else puts ("unknown-hitachi-hiuxwe2"); - exit (0); - } -EOF - $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy` && - { echo "$SYSTEM_NAME"; exit; } - echo unknown-hitachi-hiuxwe2 - exit ;; - 9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:* ) - echo hppa1.1-hp-bsd - exit ;; - 9000/8??:4.3bsd:*:*) - echo hppa1.0-hp-bsd - exit ;; - *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*) - echo hppa1.0-hp-mpeix - exit ;; - hp7??:OSF1:*:* | hp8?[79]:OSF1:*:* ) - echo hppa1.1-hp-osf - exit ;; - hp8??:OSF1:*:*) - echo hppa1.0-hp-osf - exit ;; - i*86:OSF1:*:*) - if [ -x /usr/sbin/sysversion ] ; then - echo ${UNAME_MACHINE}-unknown-osf1mk - else - echo ${UNAME_MACHINE}-unknown-osf1 - fi - exit ;; - parisc*:Lites*:*:*) - echo hppa1.1-hp-lites - exit ;; - C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*) - echo c1-convex-bsd - exit ;; - C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*) - if getsysinfo -f scalar_acc - then echo c32-convex-bsd - else echo c2-convex-bsd - fi - exit ;; - C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*) - echo c34-convex-bsd - exit ;; - C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*) - echo c38-convex-bsd - exit ;; - C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*) - echo c4-convex-bsd - exit ;; - CRAY*Y-MP:*:*:*) - echo ymp-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' - exit ;; - CRAY*[A-Z]90:*:*:*) - echo ${UNAME_MACHINE}-cray-unicos${UNAME_RELEASE} \ - | sed -e 's/CRAY.*\([A-Z]90\)/\1/' \ - -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \ - -e 's/\.[^.]*$/.X/' - exit ;; - CRAY*TS:*:*:*) - echo t90-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' - exit ;; - CRAY*T3E:*:*:*) - echo alphaev5-cray-unicosmk${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' - exit ;; - CRAY*SV1:*:*:*) - echo sv1-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' - exit ;; - *:UNICOS/mp:*:*) - echo craynv-cray-unicosmp${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' - exit ;; - F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*) - FUJITSU_PROC=`uname -m | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` - FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'` - FUJITSU_REL=`echo ${UNAME_RELEASE} | sed -e 's/ /_/'` - echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" - exit ;; - 5000:UNIX_System_V:4.*:*) - FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'` - FUJITSU_REL=`echo ${UNAME_RELEASE} | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/ /_/'` - echo "sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" - exit ;; - i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*) - echo ${UNAME_MACHINE}-pc-bsdi${UNAME_RELEASE} - exit ;; - sparc*:BSD/OS:*:*) - echo sparc-unknown-bsdi${UNAME_RELEASE} - exit ;; - *:BSD/OS:*:*) - echo ${UNAME_MACHINE}-unknown-bsdi${UNAME_RELEASE} - exit ;; - *:FreeBSD:*:*) - UNAME_PROCESSOR=`/usr/bin/uname -p` - case ${UNAME_PROCESSOR} in - amd64) - echo x86_64-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;; - *) - echo ${UNAME_PROCESSOR}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;; - esac - exit ;; - i*:CYGWIN*:*) - echo ${UNAME_MACHINE}-pc-cygwin - exit ;; - *:MINGW64*:*) - echo ${UNAME_MACHINE}-pc-mingw64 - exit ;; - *:MINGW*:*) - echo ${UNAME_MACHINE}-pc-mingw32 - exit ;; - *:MSYS*:*) - echo ${UNAME_MACHINE}-pc-msys - exit ;; - i*:windows32*:*) - # uname -m includes "-pc" on this system. - echo ${UNAME_MACHINE}-mingw32 - exit ;; - i*:PW*:*) - echo ${UNAME_MACHINE}-pc-pw32 - exit ;; - *:Interix*:*) - case ${UNAME_MACHINE} in - x86) - echo i586-pc-interix${UNAME_RELEASE} - exit ;; - authenticamd | genuineintel | EM64T) - echo x86_64-unknown-interix${UNAME_RELEASE} - exit ;; - IA64) - echo ia64-unknown-interix${UNAME_RELEASE} - exit ;; - esac ;; - [345]86:Windows_95:* | [345]86:Windows_98:* | [345]86:Windows_NT:*) - echo i${UNAME_MACHINE}-pc-mks - exit ;; - 8664:Windows_NT:*) - echo x86_64-pc-mks - exit ;; - i*:Windows_NT*:* | Pentium*:Windows_NT*:*) - # How do we know it's Interix rather than the generic POSIX subsystem? - # It also conflicts with pre-2.0 versions of AT&T UWIN. Should we - # UNAME_MACHINE based on the output of uname instead of i386? - echo i586-pc-interix - exit ;; - i*:UWIN*:*) - echo ${UNAME_MACHINE}-pc-uwin - exit ;; - amd64:CYGWIN*:*:* | x86_64:CYGWIN*:*:*) - echo x86_64-unknown-cygwin - exit ;; - p*:CYGWIN*:*) - echo powerpcle-unknown-cygwin - exit ;; - prep*:SunOS:5.*:*) - echo powerpcle-unknown-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` - exit ;; - *:GNU:*:*) - # the GNU system - echo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-unknown-${LIBC}`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'` - exit ;; - *:GNU/*:*:*) - # other systems with GNU libc and userland - echo ${UNAME_MACHINE}-unknown-`echo ${UNAME_SYSTEM} | sed 's,^[^/]*/,,' | tr '[A-Z]' '[a-z]'``echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`-${LIBC} - exit ;; - i*86:Minix:*:*) - echo ${UNAME_MACHINE}-pc-minix - exit ;; - aarch64:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-${LIBC} - exit ;; - aarch64_be:Linux:*:*) - UNAME_MACHINE=aarch64_be - echo ${UNAME_MACHINE}-unknown-linux-${LIBC} - exit ;; - alpha:Linux:*:*) - case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in - EV5) UNAME_MACHINE=alphaev5 ;; - EV56) UNAME_MACHINE=alphaev56 ;; - PCA56) UNAME_MACHINE=alphapca56 ;; - PCA57) UNAME_MACHINE=alphapca56 ;; - EV6) UNAME_MACHINE=alphaev6 ;; - EV67) UNAME_MACHINE=alphaev67 ;; - EV68*) UNAME_MACHINE=alphaev68 ;; - esac - objdump --private-headers /bin/sh | grep -q ld.so.1 - if test "$?" = 0 ; then LIBC="gnulibc1" ; fi - echo ${UNAME_MACHINE}-unknown-linux-${LIBC} - exit ;; - arc:Linux:*:* | arceb:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-${LIBC} - exit ;; - arm*:Linux:*:*) - eval $set_cc_for_build - if echo __ARM_EABI__ | $CC_FOR_BUILD -E - 2>/dev/null \ - | grep -q __ARM_EABI__ - then - echo ${UNAME_MACHINE}-unknown-linux-${LIBC} - else - if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \ - | grep -q __ARM_PCS_VFP - then - echo ${UNAME_MACHINE}-unknown-linux-${LIBC}eabi - else - echo ${UNAME_MACHINE}-unknown-linux-${LIBC}eabihf - fi - fi - exit ;; - avr32*:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-${LIBC} - exit ;; - cris:Linux:*:*) - echo ${UNAME_MACHINE}-axis-linux-${LIBC} - exit ;; - crisv32:Linux:*:*) - echo ${UNAME_MACHINE}-axis-linux-${LIBC} - exit ;; - frv:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-${LIBC} - exit ;; - hexagon:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-${LIBC} - exit ;; - i*86:Linux:*:*) - echo ${UNAME_MACHINE}-pc-linux-${LIBC} - exit ;; - ia64:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-${LIBC} - exit ;; - m32r*:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-${LIBC} - exit ;; - m68*:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-${LIBC} - exit ;; - mips:Linux:*:* | mips64:Linux:*:*) - eval $set_cc_for_build - sed 's/^ //' << EOF >$dummy.c - #undef CPU - #undef ${UNAME_MACHINE} - #undef ${UNAME_MACHINE}el - #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL) - CPU=${UNAME_MACHINE}el - #else - #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB) - CPU=${UNAME_MACHINE} - #else - CPU= - #endif - #endif -EOF - eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^CPU'` - test x"${CPU}" != x && { echo "${CPU}-unknown-linux-${LIBC}"; exit; } - ;; - openrisc*:Linux:*:*) - echo or1k-unknown-linux-${LIBC} - exit ;; - or32:Linux:*:* | or1k*:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-${LIBC} - exit ;; - padre:Linux:*:*) - echo sparc-unknown-linux-${LIBC} - exit ;; - parisc64:Linux:*:* | hppa64:Linux:*:*) - echo hppa64-unknown-linux-${LIBC} - exit ;; - parisc:Linux:*:* | hppa:Linux:*:*) - # Look for CPU level - case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in - PA7*) echo hppa1.1-unknown-linux-${LIBC} ;; - PA8*) echo hppa2.0-unknown-linux-${LIBC} ;; - *) echo hppa-unknown-linux-${LIBC} ;; - esac - exit ;; - ppc64:Linux:*:*) - echo powerpc64-unknown-linux-${LIBC} - exit ;; - ppc:Linux:*:*) - echo powerpc-unknown-linux-${LIBC} - exit ;; - ppc64le:Linux:*:*) - echo powerpc64le-unknown-linux-${LIBC} - exit ;; - ppcle:Linux:*:*) - echo powerpcle-unknown-linux-${LIBC} - exit ;; - s390:Linux:*:* | s390x:Linux:*:*) - echo ${UNAME_MACHINE}-ibm-linux-${LIBC} - exit ;; - sh64*:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-${LIBC} - exit ;; - sh*:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-${LIBC} - exit ;; - sparc:Linux:*:* | sparc64:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-${LIBC} - exit ;; - tile*:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-${LIBC} - exit ;; - vax:Linux:*:*) - echo ${UNAME_MACHINE}-dec-linux-${LIBC} - exit ;; - x86_64:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-${LIBC} - exit ;; - xtensa*:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-${LIBC} - exit ;; - i*86:DYNIX/ptx:4*:*) - # ptx 4.0 does uname -s correctly, with DYNIX/ptx in there. - # earlier versions are messed up and put the nodename in both - # sysname and nodename. - echo i386-sequent-sysv4 - exit ;; - i*86:UNIX_SV:4.2MP:2.*) - # Unixware is an offshoot of SVR4, but it has its own version - # number series starting with 2... - # I am not positive that other SVR4 systems won't match this, - # I just have to hope. -- rms. - # Use sysv4.2uw... so that sysv4* matches it. - echo ${UNAME_MACHINE}-pc-sysv4.2uw${UNAME_VERSION} - exit ;; - i*86:OS/2:*:*) - # If we were able to find `uname', then EMX Unix compatibility - # is probably installed. - echo ${UNAME_MACHINE}-pc-os2-emx - exit ;; - i*86:XTS-300:*:STOP) - echo ${UNAME_MACHINE}-unknown-stop - exit ;; - i*86:atheos:*:*) - echo ${UNAME_MACHINE}-unknown-atheos - exit ;; - i*86:syllable:*:*) - echo ${UNAME_MACHINE}-pc-syllable - exit ;; - i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.[02]*:*) - echo i386-unknown-lynxos${UNAME_RELEASE} - exit ;; - i*86:*DOS:*:*) - echo ${UNAME_MACHINE}-pc-msdosdjgpp - exit ;; - i*86:*:4.*:* | i*86:SYSTEM_V:4.*:*) - UNAME_REL=`echo ${UNAME_RELEASE} | sed 's/\/MP$//'` - if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then - echo ${UNAME_MACHINE}-univel-sysv${UNAME_REL} - else - echo ${UNAME_MACHINE}-pc-sysv${UNAME_REL} - fi - exit ;; - i*86:*:5:[678]*) - # UnixWare 7.x, OpenUNIX and OpenServer 6. - case `/bin/uname -X | grep "^Machine"` in - *486*) UNAME_MACHINE=i486 ;; - *Pentium) UNAME_MACHINE=i586 ;; - *Pent*|*Celeron) UNAME_MACHINE=i686 ;; - esac - echo ${UNAME_MACHINE}-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION} - exit ;; - i*86:*:3.2:*) - if test -f /usr/options/cb.name; then - UNAME_REL=`sed -n 's/.*Version //p' /dev/null >/dev/null ; then - UNAME_REL=`(/bin/uname -X|grep Release|sed -e 's/.*= //')` - (/bin/uname -X|grep i80486 >/dev/null) && UNAME_MACHINE=i486 - (/bin/uname -X|grep '^Machine.*Pentium' >/dev/null) \ - && UNAME_MACHINE=i586 - (/bin/uname -X|grep '^Machine.*Pent *II' >/dev/null) \ - && UNAME_MACHINE=i686 - (/bin/uname -X|grep '^Machine.*Pentium Pro' >/dev/null) \ - && UNAME_MACHINE=i686 - echo ${UNAME_MACHINE}-pc-sco$UNAME_REL - else - echo ${UNAME_MACHINE}-pc-sysv32 - fi - exit ;; - pc:*:*:*) - # Left here for compatibility: - # uname -m prints for DJGPP always 'pc', but it prints nothing about - # the processor, so we play safe by assuming i586. - # Note: whatever this is, it MUST be the same as what config.sub - # prints for the "djgpp" host, or else GDB configury will decide that - # this is a cross-build. - echo i586-pc-msdosdjgpp - exit ;; - Intel:Mach:3*:*) - echo i386-pc-mach3 - exit ;; - paragon:*:*:*) - echo i860-intel-osf1 - exit ;; - i860:*:4.*:*) # i860-SVR4 - if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then - echo i860-stardent-sysv${UNAME_RELEASE} # Stardent Vistra i860-SVR4 - else # Add other i860-SVR4 vendors below as they are discovered. - echo i860-unknown-sysv${UNAME_RELEASE} # Unknown i860-SVR4 - fi - exit ;; - mini*:CTIX:SYS*5:*) - # "miniframe" - echo m68010-convergent-sysv - exit ;; - mc68k:UNIX:SYSTEM5:3.51m) - echo m68k-convergent-sysv - exit ;; - M680?0:D-NIX:5.3:*) - echo m68k-diab-dnix - exit ;; - M68*:*:R3V[5678]*:*) - test -r /sysV68 && { echo 'm68k-motorola-sysv'; exit; } ;; - 3[345]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 3[34]??/*:*:4.0:3.0 | 4400:*:4.0:3.0 | 4850:*:4.0:3.0 | SKA40:*:4.0:3.0 | SDS2:*:4.0:3.0 | SHG2:*:4.0:3.0 | S7501*:*:4.0:3.0) - OS_REL='' - test -r /etc/.relid \ - && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` - /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ - && { echo i486-ncr-sysv4.3${OS_REL}; exit; } - /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ - && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;; - 3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*) - /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ - && { echo i486-ncr-sysv4; exit; } ;; - NCR*:*:4.2:* | MPRAS*:*:4.2:*) - OS_REL='.3' - test -r /etc/.relid \ - && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` - /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ - && { echo i486-ncr-sysv4.3${OS_REL}; exit; } - /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ - && { echo i586-ncr-sysv4.3${OS_REL}; exit; } - /bin/uname -p 2>/dev/null | /bin/grep pteron >/dev/null \ - && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;; - m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*) - echo m68k-unknown-lynxos${UNAME_RELEASE} - exit ;; - mc68030:UNIX_System_V:4.*:*) - echo m68k-atari-sysv4 - exit ;; - TSUNAMI:LynxOS:2.*:*) - echo sparc-unknown-lynxos${UNAME_RELEASE} - exit ;; - rs6000:LynxOS:2.*:*) - echo rs6000-unknown-lynxos${UNAME_RELEASE} - exit ;; - PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.[02]*:*) - echo powerpc-unknown-lynxos${UNAME_RELEASE} - exit ;; - SM[BE]S:UNIX_SV:*:*) - echo mips-dde-sysv${UNAME_RELEASE} - exit ;; - RM*:ReliantUNIX-*:*:*) - echo mips-sni-sysv4 - exit ;; - RM*:SINIX-*:*:*) - echo mips-sni-sysv4 - exit ;; - *:SINIX-*:*:*) - if uname -p 2>/dev/null >/dev/null ; then - UNAME_MACHINE=`(uname -p) 2>/dev/null` - echo ${UNAME_MACHINE}-sni-sysv4 - else - echo ns32k-sni-sysv - fi - exit ;; - PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort - # says - echo i586-unisys-sysv4 - exit ;; - *:UNIX_System_V:4*:FTX*) - # From Gerald Hewes . - # How about differentiating between stratus architectures? -djm - echo hppa1.1-stratus-sysv4 - exit ;; - *:*:*:FTX*) - # From seanf@swdc.stratus.com. - echo i860-stratus-sysv4 - exit ;; - i*86:VOS:*:*) - # From Paul.Green@stratus.com. - echo ${UNAME_MACHINE}-stratus-vos - exit ;; - *:VOS:*:*) - # From Paul.Green@stratus.com. - echo hppa1.1-stratus-vos - exit ;; - mc68*:A/UX:*:*) - echo m68k-apple-aux${UNAME_RELEASE} - exit ;; - news*:NEWS-OS:6*:*) - echo mips-sony-newsos6 - exit ;; - R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*) - if [ -d /usr/nec ]; then - echo mips-nec-sysv${UNAME_RELEASE} - else - echo mips-unknown-sysv${UNAME_RELEASE} - fi - exit ;; - BeBox:BeOS:*:*) # BeOS running on hardware made by Be, PPC only. - echo powerpc-be-beos - exit ;; - BeMac:BeOS:*:*) # BeOS running on Mac or Mac clone, PPC only. - echo powerpc-apple-beos - exit ;; - BePC:BeOS:*:*) # BeOS running on Intel PC compatible. - echo i586-pc-beos - exit ;; - BePC:Haiku:*:*) # Haiku running on Intel PC compatible. - echo i586-pc-haiku - exit ;; - x86_64:Haiku:*:*) - echo x86_64-unknown-haiku - exit ;; - SX-4:SUPER-UX:*:*) - echo sx4-nec-superux${UNAME_RELEASE} - exit ;; - SX-5:SUPER-UX:*:*) - echo sx5-nec-superux${UNAME_RELEASE} - exit ;; - SX-6:SUPER-UX:*:*) - echo sx6-nec-superux${UNAME_RELEASE} - exit ;; - SX-7:SUPER-UX:*:*) - echo sx7-nec-superux${UNAME_RELEASE} - exit ;; - SX-8:SUPER-UX:*:*) - echo sx8-nec-superux${UNAME_RELEASE} - exit ;; - SX-8R:SUPER-UX:*:*) - echo sx8r-nec-superux${UNAME_RELEASE} - exit ;; - Power*:Rhapsody:*:*) - echo powerpc-apple-rhapsody${UNAME_RELEASE} - exit ;; - *:Rhapsody:*:*) - echo ${UNAME_MACHINE}-apple-rhapsody${UNAME_RELEASE} - exit ;; - *:Darwin:*:*) - UNAME_PROCESSOR=`uname -p` || UNAME_PROCESSOR=unknown - eval $set_cc_for_build - if test "$UNAME_PROCESSOR" = unknown ; then - UNAME_PROCESSOR=powerpc - fi - if test `echo "$UNAME_RELEASE" | sed -e 's/\..*//'` -le 10 ; then - if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then - if (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \ - (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \ - grep IS_64BIT_ARCH >/dev/null - then - case $UNAME_PROCESSOR in - i386) UNAME_PROCESSOR=x86_64 ;; - powerpc) UNAME_PROCESSOR=powerpc64 ;; - esac - fi - fi - elif test "$UNAME_PROCESSOR" = i386 ; then - # Avoid executing cc on OS X 10.9, as it ships with a stub - # that puts up a graphical alert prompting to install - # developer tools. Any system running Mac OS X 10.7 or - # later (Darwin 11 and later) is required to have a 64-bit - # processor. This is not true of the ARM version of Darwin - # that Apple uses in portable devices. - UNAME_PROCESSOR=x86_64 - fi - echo ${UNAME_PROCESSOR}-apple-darwin${UNAME_RELEASE} - exit ;; - *:procnto*:*:* | *:QNX:[0123456789]*:*) - UNAME_PROCESSOR=`uname -p` - if test "$UNAME_PROCESSOR" = "x86"; then - UNAME_PROCESSOR=i386 - UNAME_MACHINE=pc - fi - echo ${UNAME_PROCESSOR}-${UNAME_MACHINE}-nto-qnx${UNAME_RELEASE} - exit ;; - *:QNX:*:4*) - echo i386-pc-qnx - exit ;; - NEO-?:NONSTOP_KERNEL:*:*) - echo neo-tandem-nsk${UNAME_RELEASE} - exit ;; - NSE-*:NONSTOP_KERNEL:*:*) - echo nse-tandem-nsk${UNAME_RELEASE} - exit ;; - NSR-?:NONSTOP_KERNEL:*:*) - echo nsr-tandem-nsk${UNAME_RELEASE} - exit ;; - *:NonStop-UX:*:*) - echo mips-compaq-nonstopux - exit ;; - BS2000:POSIX*:*:*) - echo bs2000-siemens-sysv - exit ;; - DS/*:UNIX_System_V:*:*) - echo ${UNAME_MACHINE}-${UNAME_SYSTEM}-${UNAME_RELEASE} - exit ;; - *:Plan9:*:*) - # "uname -m" is not consistent, so use $cputype instead. 386 - # is converted to i386 for consistency with other x86 - # operating systems. - if test "$cputype" = "386"; then - UNAME_MACHINE=i386 - else - UNAME_MACHINE="$cputype" - fi - echo ${UNAME_MACHINE}-unknown-plan9 - exit ;; - *:TOPS-10:*:*) - echo pdp10-unknown-tops10 - exit ;; - *:TENEX:*:*) - echo pdp10-unknown-tenex - exit ;; - KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*) - echo pdp10-dec-tops20 - exit ;; - XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*) - echo pdp10-xkl-tops20 - exit ;; - *:TOPS-20:*:*) - echo pdp10-unknown-tops20 - exit ;; - *:ITS:*:*) - echo pdp10-unknown-its - exit ;; - SEI:*:*:SEIUX) - echo mips-sei-seiux${UNAME_RELEASE} - exit ;; - *:DragonFly:*:*) - echo ${UNAME_MACHINE}-unknown-dragonfly`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` - exit ;; - *:*VMS:*:*) - UNAME_MACHINE=`(uname -p) 2>/dev/null` - case "${UNAME_MACHINE}" in - A*) echo alpha-dec-vms ; exit ;; - I*) echo ia64-dec-vms ; exit ;; - V*) echo vax-dec-vms ; exit ;; - esac ;; - *:XENIX:*:SysV) - echo i386-pc-xenix - exit ;; - i*86:skyos:*:*) - echo ${UNAME_MACHINE}-pc-skyos`echo ${UNAME_RELEASE}` | sed -e 's/ .*$//' - exit ;; - i*86:rdos:*:*) - echo ${UNAME_MACHINE}-pc-rdos - exit ;; - i*86:AROS:*:*) - echo ${UNAME_MACHINE}-pc-aros - exit ;; - x86_64:VMkernel:*:*) - echo ${UNAME_MACHINE}-unknown-esx - exit ;; -esac - -cat >&2 < in order to provide the needed -information to handle your system. - -config.guess timestamp = $timestamp - -uname -m = `(uname -m) 2>/dev/null || echo unknown` -uname -r = `(uname -r) 2>/dev/null || echo unknown` -uname -s = `(uname -s) 2>/dev/null || echo unknown` -uname -v = `(uname -v) 2>/dev/null || echo unknown` - -/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null` -/bin/uname -X = `(/bin/uname -X) 2>/dev/null` - -hostinfo = `(hostinfo) 2>/dev/null` -/bin/universe = `(/bin/universe) 2>/dev/null` -/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null` -/bin/arch = `(/bin/arch) 2>/dev/null` -/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null` -/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null` - -UNAME_MACHINE = ${UNAME_MACHINE} -UNAME_RELEASE = ${UNAME_RELEASE} -UNAME_SYSTEM = ${UNAME_SYSTEM} -UNAME_VERSION = ${UNAME_VERSION} -EOF - -exit 1 - -# Local variables: -# eval: (add-hook 'write-file-hooks 'time-stamp) -# time-stamp-start: "timestamp='" -# time-stamp-format: "%:y-%02m-%02d" -# time-stamp-end: "'" -# End: diff --git a/config.h.in b/config.h.in deleted file mode 100644 index f88e213..0000000 --- a/config.h.in +++ /dev/null @@ -1,379 +0,0 @@ -/* config.h.in. Generated from configure.ac by autoheader. */ - -/* Define if building universal (internal helper macro) */ -#undef AC_APPLE_UNIVERSAL_BUILD - -/* Define to 1 to enable debug output. */ -#undef DEBUGBUILD - -/* Define to 1 if you have the `accept4' function. */ -#undef HAVE_ACCEPT4 - -/* Define to 1 if you have the header file. */ -#undef HAVE_ARPA_INET_H - -/* define if the Boost library is available */ -#undef HAVE_BOOST - -/* define if the Boost::ASIO library is available */ -#undef HAVE_BOOST_ASIO - -/* define if the Boost::System library is available */ -#undef HAVE_BOOST_SYSTEM - -/* define if the Boost::Thread library is available */ -#undef HAVE_BOOST_THREAD - -/* Define to 1 if your system has a working `chown' function. */ -#undef HAVE_CHOWN - -/* Define to 1 if you have the `clock_gettime`. */ -#undef HAVE_CLOCK_GETTIME - -/* define if the compiler supports basic C++11 syntax */ -#undef HAVE_CXX11 - -/* Define to 1 if you have the declaration of `strerror_r', and to 0 if you - don't. */ -#undef HAVE_DECL_STRERROR_R - -/* Define to 1 if you have the header file. */ -#undef HAVE_DLFCN_H - -/* Define to 1 if you have the `dup2' function. */ -#undef HAVE_DUP2 - -/* Define to 1 if you have the header file. */ -#undef HAVE_FCNTL_H - -/* Define to 1 if you have the `fork' function. */ -#undef HAVE_FORK - -/* Define to 1 if you have the `getcwd' function. */ -#undef HAVE_GETCWD - -/* Define to 1 if you have the `getpwnam' function. */ -#undef HAVE_GETPWNAM - -/* Define to 1 if you have the header file. */ -#undef HAVE_INTTYPES_H - -/* Define to 1 if you have `libjansson` library. */ -#undef HAVE_JANSSON - -/* Define to 1 if you have `libxml2` library. */ -#undef HAVE_LIBXML2 - -/* Define to 1 if you have the header file. */ -#undef HAVE_LIMITS_H - -/* Define to 1 if you have the `localtime_r' function. */ -#undef HAVE_LOCALTIME_R - -/* Define to 1 if your system has a GNU libc compatible `malloc' function, and - to 0 otherwise. */ -#undef HAVE_MALLOC - -/* Define to 1 if you have the `memchr' function. */ -#undef HAVE_MEMCHR - -/* Define to 1 if you have the `memmove' function. */ -#undef HAVE_MEMMOVE - -/* Define to 1 if you have the header file. */ -#undef HAVE_MEMORY_H - -/* Define to 1 if you have the `memset' function. */ -#undef HAVE_MEMSET - -/* Define to 1 if you have the header file. */ -#undef HAVE_NETDB_H - -/* Define to 1 if you have the header file. */ -#undef HAVE_NETINET_IN_H - -/* Define to 1 if the system has the type `ptrdiff_t'. */ -#undef HAVE_PTRDIFF_T - -/* Define to 1 if you have the header file. */ -#undef HAVE_PWD_H - -/* If available, contains the Python version number currently in use. */ -#undef HAVE_PYTHON - -/* Define to 1 if you have the `socket' function. */ -#undef HAVE_SOCKET - -/* Define to 1 if you have `spdylay` library. */ -#undef HAVE_SPDYLAY - -/* Define to 1 if you have the `sqrt' function. */ -#undef HAVE_SQRT - -/* Define to 1 if you have the header file. */ -#undef HAVE_STDDEF_H - -/* Define to 1 if you have the header file. */ -#undef HAVE_STDINT_H - -/* Define to 1 if you have the header file. */ -#undef HAVE_STDLIB_H - -/* Define to 1 if you have the `std::future`. */ -#undef HAVE_STD_FUTURE - -/* Define to 1 if you have the `std::map::emplace`. */ -#undef HAVE_STD_MAP_EMPLACE - -/* Define to 1 if you have the `std::chrono::steady_clock`. */ -#undef HAVE_STEADY_CLOCK - -/* Define to 1 if you have the `strchr' function. */ -#undef HAVE_STRCHR - -/* Define to 1 if you have the `strdup' function. */ -#undef HAVE_STRDUP - -/* Define to 1 if you have the `strerror' function. */ -#undef HAVE_STRERROR - -/* Define to 1 if you have the `strerror_r' function. */ -#undef HAVE_STRERROR_R - -/* Define to 1 if you have the header file. */ -#undef HAVE_STRINGS_H - -/* Define to 1 if you have the header file. */ -#undef HAVE_STRING_H - -/* Define to 1 if you have the `strndup' function. */ -#undef HAVE_STRNDUP - -/* Define to 1 if you have the `strstr' function. */ -#undef HAVE_STRSTR - -/* Define to 1 if you have the `strtol' function. */ -#undef HAVE_STRTOL - -/* Define to 1 if you have the `strtoul' function. */ -#undef HAVE_STRTOUL - -/* Define to 1 if you have `struct tm.tm_gmtoff` member. */ -#undef HAVE_STRUCT_TM_TM_GMTOFF - -/* Define to 1 if you have the header file. */ -#undef HAVE_SYSLOG_H - -/* Define to 1 if you have the header file. */ -#undef HAVE_SYS_SOCKET_H - -/* Define to 1 if you have the header file. */ -#undef HAVE_SYS_STAT_H - -/* Define to 1 if you have the header file. */ -#undef HAVE_SYS_TIME_H - -/* Define to 1 if you have the header file. */ -#undef HAVE_SYS_TYPES_H - -/* Define to 1 if you have the `timegm' function. */ -#undef HAVE_TIMEGM - -/* Define to 1 if you have the header file. */ -#undef HAVE_TIME_H - -/* Define to 1 if you have the header file. */ -#undef HAVE_UNISTD_H - -/* Define to 1 if you have the `vfork' function. */ -#undef HAVE_VFORK - -/* Define to 1 if you have the header file. */ -#undef HAVE_VFORK_H - -/* Define to 1 if `fork' works. */ -#undef HAVE_WORKING_FORK - -/* Define to 1 if `vfork' works. */ -#undef HAVE_WORKING_VFORK - -/* Define to 1 if you have the `_Exit' function. */ -#undef HAVE__EXIT - -/* Define to the sub-directory in which libtool stores uninstalled libraries. - */ -#undef LT_OBJDIR - -/* Define to 1 if assertions should be disabled. */ -#undef NDEBUG - -/* Define to 1 if you want to disable threads. */ -#undef NOTHREADS - -/* Name of package */ -#undef PACKAGE - -/* Define to the address where bug reports for this package should be sent. */ -#undef PACKAGE_BUGREPORT - -/* Define to the full name of this package. */ -#undef PACKAGE_NAME - -/* Define to the full name and version of this package. */ -#undef PACKAGE_STRING - -/* Define to the one symbol short name of this package. */ -#undef PACKAGE_TARNAME - -/* Define to the home page for this package. */ -#undef PACKAGE_URL - -/* Define to the version of this package. */ -#undef PACKAGE_VERSION - -/* The size of `int *', as computed by sizeof. */ -#undef SIZEOF_INT_P - -/* Define to 1 if you have the ANSI C header files. */ -#undef STDC_HEADERS - -/* Define to 1 if strerror_r returns char *. */ -#undef STRERROR_R_CHAR_P - -/* Enable extensions on AIX 3, Interix. */ -#ifndef _ALL_SOURCE -# undef _ALL_SOURCE -#endif -/* Enable GNU extensions on systems that have them. */ -#ifndef _GNU_SOURCE -# undef _GNU_SOURCE -#endif -/* Enable threading extensions on Solaris. */ -#ifndef _POSIX_PTHREAD_SEMANTICS -# undef _POSIX_PTHREAD_SEMANTICS -#endif -/* Enable extensions on HP NonStop. */ -#ifndef _TANDEM_SOURCE -# undef _TANDEM_SOURCE -#endif -/* Enable general extensions on Solaris. */ -#ifndef __EXTENSIONS__ -# undef __EXTENSIONS__ -#endif - - -/* Version number of package */ -#undef VERSION - -/* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most - significant byte first (like Motorola and SPARC, unlike Intel). */ -#if defined AC_APPLE_UNIVERSAL_BUILD -# if defined __BIG_ENDIAN__ -# define WORDS_BIGENDIAN 1 -# endif -#else -# ifndef WORDS_BIGENDIAN -# undef WORDS_BIGENDIAN -# endif -#endif - -/* Enable large inode numbers on Mac OS X 10.5. */ -#ifndef _DARWIN_USE_64_BIT_INODE -# define _DARWIN_USE_64_BIT_INODE 1 -#endif - -/* Number of bits in a file offset, on hosts where this is settable. */ -#undef _FILE_OFFSET_BITS - -/* Define for large files, on AIX-style hosts. */ -#undef _LARGE_FILES - -/* Define to 1 if on MINIX. */ -#undef _MINIX - -/* Define to 2 if the system does not provide POSIX.1 features except with - this defined. */ -#undef _POSIX_1_SOURCE - -/* Define to 1 if you need to in order for `stat' and other things to work. */ -#undef _POSIX_SOURCE - -/* Define for Solaris 2.5.1 so the uint32_t typedef from , - , or is not used. If the typedef were allowed, the - #define below would cause a syntax error. */ -#undef _UINT32_T - -/* Define for Solaris 2.5.1 so the uint64_t typedef from , - , or is not used. If the typedef were allowed, the - #define below would cause a syntax error. */ -#undef _UINT64_T - -/* Define for Solaris 2.5.1 so the uint8_t typedef from , - , or is not used. If the typedef were allowed, the - #define below would cause a syntax error. */ -#undef _UINT8_T - -/* Hint to the compiler that a function parameters is not used */ -#undef _U_ - -/* Define to `int' if doesn't define. */ -#undef gid_t - -/* Define to `__inline__' or `__inline' if that's what the C compiler - calls it, or to nothing if 'inline' is not supported under any name. */ -#ifndef __cplusplus -#undef inline -#endif - -/* Define to the type of a signed integer type of width exactly 16 bits if - such a type exists and the standard includes do not define it. */ -#undef int16_t - -/* Define to the type of a signed integer type of width exactly 32 bits if - such a type exists and the standard includes do not define it. */ -#undef int32_t - -/* Define to the type of a signed integer type of width exactly 64 bits if - such a type exists and the standard includes do not define it. */ -#undef int64_t - -/* Define to the type of a signed integer type of width exactly 8 bits if such - a type exists and the standard includes do not define it. */ -#undef int8_t - -/* Define to rpl_malloc if the replacement function should be used. */ -#undef malloc - -/* Define to `long int' if does not define. */ -#undef off_t - -/* Define to `int' if does not define. */ -#undef pid_t - -/* Define to `unsigned int' if does not define. */ -#undef size_t - -/* Define to `int' if does not define. */ -#undef ssize_t - -/* Define to `int' if doesn't define. */ -#undef uid_t - -/* Define to the type of an unsigned integer type of width exactly 16 bits if - such a type exists and the standard includes do not define it. */ -#undef uint16_t - -/* Define to the type of an unsigned integer type of width exactly 32 bits if - such a type exists and the standard includes do not define it. */ -#undef uint32_t - -/* Define to the type of an unsigned integer type of width exactly 64 bits if - such a type exists and the standard includes do not define it. */ -#undef uint64_t - -/* Define to the type of an unsigned integer type of width exactly 8 bits if - such a type exists and the standard includes do not define it. */ -#undef uint8_t - -/* Define as `fork' if `vfork' does not work. */ -#undef vfork diff --git a/config.sub b/config.sub deleted file mode 100755 index bba4efb..0000000 --- a/config.sub +++ /dev/null @@ -1,1799 +0,0 @@ -#! /bin/sh -# Configuration validation subroutine script. -# Copyright 1992-2014 Free Software Foundation, Inc. - -timestamp='2014-09-11' - -# This file 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 . -# -# As a special exception to the GNU General Public License, if you -# distribute this file as part of a program that contains a -# configuration script generated by Autoconf, you may include it under -# the same distribution terms that you use for the rest of that -# program. This Exception is an additional permission under section 7 -# of the GNU General Public License, version 3 ("GPLv3"). - - -# Please send patches with a ChangeLog entry to config-patches@gnu.org. -# -# Configuration subroutine to validate and canonicalize a configuration type. -# Supply the specified configuration type as an argument. -# If it is invalid, we print an error message on stderr and exit with code 1. -# Otherwise, we print the canonical config type on stdout and succeed. - -# You can get the latest version of this script from: -# http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub;hb=HEAD - -# This file is supposed to be the same for all GNU packages -# and recognize all the CPU types, system types and aliases -# that are meaningful with *any* GNU software. -# Each package is responsible for reporting which valid configurations -# it does not support. The user should be able to distinguish -# a failure to support a valid configuration from a meaningless -# configuration. - -# The goal of this file is to map all the various variations of a given -# machine specification into a single specification in the form: -# CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM -# or in some cases, the newer four-part form: -# CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM -# It is wrong to echo any other type of specification. - -me=`echo "$0" | sed -e 's,.*/,,'` - -usage="\ -Usage: $0 [OPTION] CPU-MFR-OPSYS - $0 [OPTION] ALIAS - -Canonicalize a configuration name. - -Operation modes: - -h, --help print this help, then exit - -t, --time-stamp print date of last modification, then exit - -v, --version print version number, then exit - -Report bugs and patches to ." - -version="\ -GNU config.sub ($timestamp) - -Copyright 1992-2014 Free Software Foundation, Inc. - -This is free software; see the source for copying conditions. There is NO -warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." - -help=" -Try \`$me --help' for more information." - -# Parse command line -while test $# -gt 0 ; do - case $1 in - --time-stamp | --time* | -t ) - echo "$timestamp" ; exit ;; - --version | -v ) - echo "$version" ; exit ;; - --help | --h* | -h ) - echo "$usage"; exit ;; - -- ) # Stop option processing - shift; break ;; - - ) # Use stdin as input. - break ;; - -* ) - echo "$me: invalid option $1$help" - exit 1 ;; - - *local*) - # First pass through any local machine types. - echo $1 - exit ;; - - * ) - break ;; - esac -done - -case $# in - 0) echo "$me: missing argument$help" >&2 - exit 1;; - 1) ;; - *) echo "$me: too many arguments$help" >&2 - exit 1;; -esac - -# Separate what the user gave into CPU-COMPANY and OS or KERNEL-OS (if any). -# Here we must recognize all the valid KERNEL-OS combinations. -maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'` -case $maybe_os in - nto-qnx* | linux-gnu* | linux-android* | linux-dietlibc | linux-newlib* | \ - linux-musl* | linux-uclibc* | uclinux-uclibc* | uclinux-gnu* | kfreebsd*-gnu* | \ - knetbsd*-gnu* | netbsd*-gnu* | \ - kopensolaris*-gnu* | \ - storm-chaos* | os2-emx* | rtmk-nova*) - os=-$maybe_os - basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'` - ;; - android-linux) - os=-linux-android - basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'`-unknown - ;; - *) - basic_machine=`echo $1 | sed 's/-[^-]*$//'` - if [ $basic_machine != $1 ] - then os=`echo $1 | sed 's/.*-/-/'` - else os=; fi - ;; -esac - -### Let's recognize common machines as not being operating systems so -### that things like config.sub decstation-3100 work. We also -### recognize some manufacturers as not being operating systems, so we -### can provide default operating systems below. -case $os in - -sun*os*) - # Prevent following clause from handling this invalid input. - ;; - -dec* | -mips* | -sequent* | -encore* | -pc532* | -sgi* | -sony* | \ - -att* | -7300* | -3300* | -delta* | -motorola* | -sun[234]* | \ - -unicom* | -ibm* | -next | -hp | -isi* | -apollo | -altos* | \ - -convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\ - -c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \ - -harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp | \ - -apple | -axis | -knuth | -cray | -microblaze*) - os= - basic_machine=$1 - ;; - -bluegene*) - os=-cnk - ;; - -sim | -cisco | -oki | -wec | -winbond) - os= - basic_machine=$1 - ;; - -scout) - ;; - -wrs) - os=-vxworks - basic_machine=$1 - ;; - -chorusos*) - os=-chorusos - basic_machine=$1 - ;; - -chorusrdb) - os=-chorusrdb - basic_machine=$1 - ;; - -hiux*) - os=-hiuxwe2 - ;; - -sco6) - os=-sco5v6 - basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` - ;; - -sco5) - os=-sco3.2v5 - basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` - ;; - -sco4) - os=-sco3.2v4 - basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` - ;; - -sco3.2.[4-9]*) - os=`echo $os | sed -e 's/sco3.2./sco3.2v/'` - basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` - ;; - -sco3.2v[4-9]*) - # Don't forget version if it is 3.2v4 or newer. - basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` - ;; - -sco5v6*) - # Don't forget version if it is 3.2v4 or newer. - basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` - ;; - -sco*) - os=-sco3.2v2 - basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` - ;; - -udk*) - basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` - ;; - -isc) - os=-isc2.2 - basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` - ;; - -clix*) - basic_machine=clipper-intergraph - ;; - -isc*) - basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` - ;; - -lynx*178) - os=-lynxos178 - ;; - -lynx*5) - os=-lynxos5 - ;; - -lynx*) - os=-lynxos - ;; - -ptx*) - basic_machine=`echo $1 | sed -e 's/86-.*/86-sequent/'` - ;; - -windowsnt*) - os=`echo $os | sed -e 's/windowsnt/winnt/'` - ;; - -psos*) - os=-psos - ;; - -mint | -mint[0-9]*) - basic_machine=m68k-atari - os=-mint - ;; -esac - -# Decode aliases for certain CPU-COMPANY combinations. -case $basic_machine in - # Recognize the basic CPU types without company name. - # Some are omitted here because they have special meanings below. - 1750a | 580 \ - | a29k \ - | aarch64 | aarch64_be \ - | alpha | alphaev[4-8] | alphaev56 | alphaev6[78] | alphapca5[67] \ - | alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] | alpha64pca5[67] \ - | am33_2.0 \ - | arc | arceb \ - | arm | arm[bl]e | arme[lb] | armv[2-8] | armv[3-8][lb] | armv7[arm] \ - | avr | avr32 \ - | be32 | be64 \ - | bfin \ - | c4x | c8051 | clipper \ - | d10v | d30v | dlx | dsp16xx \ - | epiphany \ - | fido | fr30 | frv \ - | h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \ - | hexagon \ - | i370 | i860 | i960 | ia64 \ - | ip2k | iq2000 \ - | k1om \ - | le32 | le64 \ - | lm32 \ - | m32c | m32r | m32rle | m68000 | m68k | m88k \ - | maxq | mb | microblaze | microblazeel | mcore | mep | metag \ - | mips | mipsbe | mipseb | mipsel | mipsle \ - | mips16 \ - | mips64 | mips64el \ - | mips64octeon | mips64octeonel \ - | mips64orion | mips64orionel \ - | mips64r5900 | mips64r5900el \ - | mips64vr | mips64vrel \ - | mips64vr4100 | mips64vr4100el \ - | mips64vr4300 | mips64vr4300el \ - | mips64vr5000 | mips64vr5000el \ - | mips64vr5900 | mips64vr5900el \ - | mipsisa32 | mipsisa32el \ - | mipsisa32r2 | mipsisa32r2el \ - | mipsisa32r6 | mipsisa32r6el \ - | mipsisa64 | mipsisa64el \ - | mipsisa64r2 | mipsisa64r2el \ - | mipsisa64r6 | mipsisa64r6el \ - | mipsisa64sb1 | mipsisa64sb1el \ - | mipsisa64sr71k | mipsisa64sr71kel \ - | mipsr5900 | mipsr5900el \ - | mipstx39 | mipstx39el \ - | mn10200 | mn10300 \ - | moxie \ - | mt \ - | msp430 \ - | nds32 | nds32le | nds32be \ - | nios | nios2 | nios2eb | nios2el \ - | ns16k | ns32k \ - | open8 | or1k | or1knd | or32 \ - | pdp10 | pdp11 | pj | pjl \ - | powerpc | powerpc64 | powerpc64le | powerpcle \ - | pyramid \ - | riscv32 | riscv64 \ - | rl78 | rx \ - | score \ - | sh | sh[1234] | sh[24]a | sh[24]aeb | sh[23]e | sh[34]eb | sheb | shbe | shle | sh[1234]le | sh3ele \ - | sh64 | sh64le \ - | sparc | sparc64 | sparc64b | sparc64v | sparc86x | sparclet | sparclite \ - | sparcv8 | sparcv9 | sparcv9b | sparcv9v \ - | spu \ - | tahoe | tic4x | tic54x | tic55x | tic6x | tic80 | tron \ - | ubicom32 \ - | v850 | v850e | v850e1 | v850e2 | v850es | v850e2v3 \ - | we32k \ - | x86 | xc16x | xstormy16 | xtensa \ - | z8k | z80) - basic_machine=$basic_machine-unknown - ;; - c54x) - basic_machine=tic54x-unknown - ;; - c55x) - basic_machine=tic55x-unknown - ;; - c6x) - basic_machine=tic6x-unknown - ;; - m6811 | m68hc11 | m6812 | m68hc12 | m68hcs12x | nvptx | picochip) - basic_machine=$basic_machine-unknown - os=-none - ;; - m88110 | m680[12346]0 | m683?2 | m68360 | m5200 | v70 | w65 | z8k) - ;; - ms1) - basic_machine=mt-unknown - ;; - - strongarm | thumb | xscale) - basic_machine=arm-unknown - ;; - xgate) - basic_machine=$basic_machine-unknown - os=-none - ;; - xscaleeb) - basic_machine=armeb-unknown - ;; - - xscaleel) - basic_machine=armel-unknown - ;; - - # We use `pc' rather than `unknown' - # because (1) that's what they normally are, and - # (2) the word "unknown" tends to confuse beginning users. - i*86 | x86_64) - basic_machine=$basic_machine-pc - ;; - # Object if more than one company name word. - *-*-*) - echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2 - exit 1 - ;; - # Recognize the basic CPU types with company name. - 580-* \ - | a29k-* \ - | aarch64-* | aarch64_be-* \ - | alpha-* | alphaev[4-8]-* | alphaev56-* | alphaev6[78]-* \ - | alpha64-* | alpha64ev[4-8]-* | alpha64ev56-* | alpha64ev6[78]-* \ - | alphapca5[67]-* | alpha64pca5[67]-* | arc-* | arceb-* \ - | arm-* | armbe-* | armle-* | armeb-* | armv*-* \ - | avr-* | avr32-* \ - | be32-* | be64-* \ - | bfin-* | bs2000-* \ - | c[123]* | c30-* | [cjt]90-* | c4x-* \ - | c8051-* | clipper-* | craynv-* | cydra-* \ - | d10v-* | d30v-* | dlx-* \ - | elxsi-* \ - | f30[01]-* | f700-* | fido-* | fr30-* | frv-* | fx80-* \ - | h8300-* | h8500-* \ - | hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \ - | hexagon-* \ - | i*86-* | i860-* | i960-* | ia64-* \ - | ip2k-* | iq2000-* \ - | k1om-* \ - | le32-* | le64-* \ - | lm32-* \ - | m32c-* | m32r-* | m32rle-* \ - | m68000-* | m680[012346]0-* | m68360-* | m683?2-* | m68k-* \ - | m88110-* | m88k-* | maxq-* | mcore-* | metag-* \ - | microblaze-* | microblazeel-* \ - | mips-* | mipsbe-* | mipseb-* | mipsel-* | mipsle-* \ - | mips16-* \ - | mips64-* | mips64el-* \ - | mips64octeon-* | mips64octeonel-* \ - | mips64orion-* | mips64orionel-* \ - | mips64r5900-* | mips64r5900el-* \ - | mips64vr-* | mips64vrel-* \ - | mips64vr4100-* | mips64vr4100el-* \ - | mips64vr4300-* | mips64vr4300el-* \ - | mips64vr5000-* | mips64vr5000el-* \ - | mips64vr5900-* | mips64vr5900el-* \ - | mipsisa32-* | mipsisa32el-* \ - | mipsisa32r2-* | mipsisa32r2el-* \ - | mipsisa32r6-* | mipsisa32r6el-* \ - | mipsisa64-* | mipsisa64el-* \ - | mipsisa64r2-* | mipsisa64r2el-* \ - | mipsisa64r6-* | mipsisa64r6el-* \ - | mipsisa64sb1-* | mipsisa64sb1el-* \ - | mipsisa64sr71k-* | mipsisa64sr71kel-* \ - | mipsr5900-* | mipsr5900el-* \ - | mipstx39-* | mipstx39el-* \ - | mmix-* \ - | mt-* \ - | msp430-* \ - | nds32-* | nds32le-* | nds32be-* \ - | nios-* | nios2-* | nios2eb-* | nios2el-* \ - | none-* | np1-* | ns16k-* | ns32k-* \ - | open8-* \ - | or1k*-* \ - | orion-* \ - | pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \ - | powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* \ - | pyramid-* \ - | rl78-* | romp-* | rs6000-* | rx-* \ - | sh-* | sh[1234]-* | sh[24]a-* | sh[24]aeb-* | sh[23]e-* | sh[34]eb-* | sheb-* | shbe-* \ - | shle-* | sh[1234]le-* | sh3ele-* | sh64-* | sh64le-* \ - | sparc-* | sparc64-* | sparc64b-* | sparc64v-* | sparc86x-* | sparclet-* \ - | sparclite-* \ - | sparcv8-* | sparcv9-* | sparcv9b-* | sparcv9v-* | sv1-* | sx?-* \ - | tahoe-* \ - | tic30-* | tic4x-* | tic54x-* | tic55x-* | tic6x-* | tic80-* \ - | tile*-* \ - | tron-* \ - | ubicom32-* \ - | v850-* | v850e-* | v850e1-* | v850es-* | v850e2-* | v850e2v3-* \ - | vax-* \ - | we32k-* \ - | x86-* | x86_64-* | xc16x-* | xps100-* \ - | xstormy16-* | xtensa*-* \ - | ymp-* \ - | z8k-* | z80-*) - ;; - # Recognize the basic CPU types without company name, with glob match. - xtensa*) - basic_machine=$basic_machine-unknown - ;; - # Recognize the various machine names and aliases which stand - # for a CPU type and a company and sometimes even an OS. - 386bsd) - basic_machine=i386-unknown - os=-bsd - ;; - 3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc) - basic_machine=m68000-att - ;; - 3b*) - basic_machine=we32k-att - ;; - a29khif) - basic_machine=a29k-amd - os=-udi - ;; - abacus) - basic_machine=abacus-unknown - ;; - adobe68k) - basic_machine=m68010-adobe - os=-scout - ;; - alliant | fx80) - basic_machine=fx80-alliant - ;; - altos | altos3068) - basic_machine=m68k-altos - ;; - am29k) - basic_machine=a29k-none - os=-bsd - ;; - amd64) - basic_machine=x86_64-pc - ;; - amd64-*) - basic_machine=x86_64-`echo $basic_machine | sed 's/^[^-]*-//'` - ;; - amdahl) - basic_machine=580-amdahl - os=-sysv - ;; - amiga | amiga-*) - basic_machine=m68k-unknown - ;; - amigaos | amigados) - basic_machine=m68k-unknown - os=-amigaos - ;; - amigaunix | amix) - basic_machine=m68k-unknown - os=-sysv4 - ;; - apollo68) - basic_machine=m68k-apollo - os=-sysv - ;; - apollo68bsd) - basic_machine=m68k-apollo - os=-bsd - ;; - aros) - basic_machine=i386-pc - os=-aros - ;; - aux) - basic_machine=m68k-apple - os=-aux - ;; - balance) - basic_machine=ns32k-sequent - os=-dynix - ;; - blackfin) - basic_machine=bfin-unknown - os=-linux - ;; - blackfin-*) - basic_machine=bfin-`echo $basic_machine | sed 's/^[^-]*-//'` - os=-linux - ;; - bluegene*) - basic_machine=powerpc-ibm - os=-cnk - ;; - c54x-*) - basic_machine=tic54x-`echo $basic_machine | sed 's/^[^-]*-//'` - ;; - c55x-*) - basic_machine=tic55x-`echo $basic_machine | sed 's/^[^-]*-//'` - ;; - c6x-*) - basic_machine=tic6x-`echo $basic_machine | sed 's/^[^-]*-//'` - ;; - c90) - basic_machine=c90-cray - os=-unicos - ;; - cegcc) - basic_machine=arm-unknown - os=-cegcc - ;; - convex-c1) - basic_machine=c1-convex - os=-bsd - ;; - convex-c2) - basic_machine=c2-convex - os=-bsd - ;; - convex-c32) - basic_machine=c32-convex - os=-bsd - ;; - convex-c34) - basic_machine=c34-convex - os=-bsd - ;; - convex-c38) - basic_machine=c38-convex - os=-bsd - ;; - cray | j90) - basic_machine=j90-cray - os=-unicos - ;; - craynv) - basic_machine=craynv-cray - os=-unicosmp - ;; - cr16 | cr16-*) - basic_machine=cr16-unknown - os=-elf - ;; - crds | unos) - basic_machine=m68k-crds - ;; - crisv32 | crisv32-* | etraxfs*) - basic_machine=crisv32-axis - ;; - cris | cris-* | etrax*) - basic_machine=cris-axis - ;; - crx) - basic_machine=crx-unknown - os=-elf - ;; - da30 | da30-*) - basic_machine=m68k-da30 - ;; - decstation | decstation-3100 | pmax | pmax-* | pmin | dec3100 | decstatn) - basic_machine=mips-dec - ;; - decsystem10* | dec10*) - basic_machine=pdp10-dec - os=-tops10 - ;; - decsystem20* | dec20*) - basic_machine=pdp10-dec - os=-tops20 - ;; - delta | 3300 | motorola-3300 | motorola-delta \ - | 3300-motorola | delta-motorola) - basic_machine=m68k-motorola - ;; - delta88) - basic_machine=m88k-motorola - os=-sysv3 - ;; - dicos) - basic_machine=i686-pc - os=-dicos - ;; - djgpp) - basic_machine=i586-pc - os=-msdosdjgpp - ;; - dpx20 | dpx20-*) - basic_machine=rs6000-bull - os=-bosx - ;; - dpx2* | dpx2*-bull) - basic_machine=m68k-bull - os=-sysv3 - ;; - ebmon29k) - basic_machine=a29k-amd - os=-ebmon - ;; - elxsi) - basic_machine=elxsi-elxsi - os=-bsd - ;; - encore | umax | mmax) - basic_machine=ns32k-encore - ;; - es1800 | OSE68k | ose68k | ose | OSE) - basic_machine=m68k-ericsson - os=-ose - ;; - fx2800) - basic_machine=i860-alliant - ;; - genix) - basic_machine=ns32k-ns - ;; - gmicro) - basic_machine=tron-gmicro - os=-sysv - ;; - go32) - basic_machine=i386-pc - os=-go32 - ;; - h3050r* | hiux*) - basic_machine=hppa1.1-hitachi - os=-hiuxwe2 - ;; - h8300hms) - basic_machine=h8300-hitachi - os=-hms - ;; - h8300xray) - basic_machine=h8300-hitachi - os=-xray - ;; - h8500hms) - basic_machine=h8500-hitachi - os=-hms - ;; - harris) - basic_machine=m88k-harris - os=-sysv3 - ;; - hp300-*) - basic_machine=m68k-hp - ;; - hp300bsd) - basic_machine=m68k-hp - os=-bsd - ;; - hp300hpux) - basic_machine=m68k-hp - os=-hpux - ;; - hp3k9[0-9][0-9] | hp9[0-9][0-9]) - basic_machine=hppa1.0-hp - ;; - hp9k2[0-9][0-9] | hp9k31[0-9]) - basic_machine=m68000-hp - ;; - hp9k3[2-9][0-9]) - basic_machine=m68k-hp - ;; - hp9k6[0-9][0-9] | hp6[0-9][0-9]) - basic_machine=hppa1.0-hp - ;; - hp9k7[0-79][0-9] | hp7[0-79][0-9]) - basic_machine=hppa1.1-hp - ;; - hp9k78[0-9] | hp78[0-9]) - # FIXME: really hppa2.0-hp - basic_machine=hppa1.1-hp - ;; - hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893) - # FIXME: really hppa2.0-hp - basic_machine=hppa1.1-hp - ;; - hp9k8[0-9][13679] | hp8[0-9][13679]) - basic_machine=hppa1.1-hp - ;; - hp9k8[0-9][0-9] | hp8[0-9][0-9]) - basic_machine=hppa1.0-hp - ;; - hppa-next) - os=-nextstep3 - ;; - hppaosf) - basic_machine=hppa1.1-hp - os=-osf - ;; - hppro) - basic_machine=hppa1.1-hp - os=-proelf - ;; - i370-ibm* | ibm*) - basic_machine=i370-ibm - ;; - i*86v32) - basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` - os=-sysv32 - ;; - i*86v4*) - basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` - os=-sysv4 - ;; - i*86v) - basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` - os=-sysv - ;; - i*86sol2) - basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` - os=-solaris2 - ;; - i386mach) - basic_machine=i386-mach - os=-mach - ;; - i386-vsta | vsta) - basic_machine=i386-unknown - os=-vsta - ;; - iris | iris4d) - basic_machine=mips-sgi - case $os in - -irix*) - ;; - *) - os=-irix4 - ;; - esac - ;; - isi68 | isi) - basic_machine=m68k-isi - os=-sysv - ;; - m68knommu) - basic_machine=m68k-unknown - os=-linux - ;; - m68knommu-*) - basic_machine=m68k-`echo $basic_machine | sed 's/^[^-]*-//'` - os=-linux - ;; - m88k-omron*) - basic_machine=m88k-omron - ;; - magnum | m3230) - basic_machine=mips-mips - os=-sysv - ;; - merlin) - basic_machine=ns32k-utek - os=-sysv - ;; - microblaze*) - basic_machine=microblaze-xilinx - ;; - mingw64) - basic_machine=x86_64-pc - os=-mingw64 - ;; - mingw32) - basic_machine=i686-pc - os=-mingw32 - ;; - mingw32ce) - basic_machine=arm-unknown - os=-mingw32ce - ;; - miniframe) - basic_machine=m68000-convergent - ;; - *mint | -mint[0-9]* | *MiNT | *MiNT[0-9]*) - basic_machine=m68k-atari - os=-mint - ;; - mips3*-*) - basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'` - ;; - mips3*) - basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`-unknown - ;; - monitor) - basic_machine=m68k-rom68k - os=-coff - ;; - morphos) - basic_machine=powerpc-unknown - os=-morphos - ;; - moxiebox) - basic_machine=moxie-unknown - os=-moxiebox - ;; - msdos) - basic_machine=i386-pc - os=-msdos - ;; - ms1-*) - basic_machine=`echo $basic_machine | sed -e 's/ms1-/mt-/'` - ;; - msys) - basic_machine=i686-pc - os=-msys - ;; - mvs) - basic_machine=i370-ibm - os=-mvs - ;; - nacl) - basic_machine=le32-unknown - os=-nacl - ;; - ncr3000) - basic_machine=i486-ncr - os=-sysv4 - ;; - netbsd386) - basic_machine=i386-unknown - os=-netbsd - ;; - netwinder) - basic_machine=armv4l-rebel - os=-linux - ;; - news | news700 | news800 | news900) - basic_machine=m68k-sony - os=-newsos - ;; - news1000) - basic_machine=m68030-sony - os=-newsos - ;; - news-3600 | risc-news) - basic_machine=mips-sony - os=-newsos - ;; - necv70) - basic_machine=v70-nec - os=-sysv - ;; - next | m*-next ) - basic_machine=m68k-next - case $os in - -nextstep* ) - ;; - -ns2*) - os=-nextstep2 - ;; - *) - os=-nextstep3 - ;; - esac - ;; - nh3000) - basic_machine=m68k-harris - os=-cxux - ;; - nh[45]000) - basic_machine=m88k-harris - os=-cxux - ;; - nindy960) - basic_machine=i960-intel - os=-nindy - ;; - mon960) - basic_machine=i960-intel - os=-mon960 - ;; - nonstopux) - basic_machine=mips-compaq - os=-nonstopux - ;; - np1) - basic_machine=np1-gould - ;; - neo-tandem) - basic_machine=neo-tandem - ;; - nse-tandem) - basic_machine=nse-tandem - ;; - nsr-tandem) - basic_machine=nsr-tandem - ;; - op50n-* | op60c-*) - basic_machine=hppa1.1-oki - os=-proelf - ;; - openrisc | openrisc-*) - basic_machine=or32-unknown - ;; - os400) - basic_machine=powerpc-ibm - os=-os400 - ;; - OSE68000 | ose68000) - basic_machine=m68000-ericsson - os=-ose - ;; - os68k) - basic_machine=m68k-none - os=-os68k - ;; - pa-hitachi) - basic_machine=hppa1.1-hitachi - os=-hiuxwe2 - ;; - paragon) - basic_machine=i860-intel - os=-osf - ;; - parisc) - basic_machine=hppa-unknown - os=-linux - ;; - parisc-*) - basic_machine=hppa-`echo $basic_machine | sed 's/^[^-]*-//'` - os=-linux - ;; - pbd) - basic_machine=sparc-tti - ;; - pbb) - basic_machine=m68k-tti - ;; - pc532 | pc532-*) - basic_machine=ns32k-pc532 - ;; - pc98) - basic_machine=i386-pc - ;; - pc98-*) - basic_machine=i386-`echo $basic_machine | sed 's/^[^-]*-//'` - ;; - pentium | p5 | k5 | k6 | nexgen | viac3) - basic_machine=i586-pc - ;; - pentiumpro | p6 | 6x86 | athlon | athlon_*) - basic_machine=i686-pc - ;; - pentiumii | pentium2 | pentiumiii | pentium3) - basic_machine=i686-pc - ;; - pentium4) - basic_machine=i786-pc - ;; - pentium-* | p5-* | k5-* | k6-* | nexgen-* | viac3-*) - basic_machine=i586-`echo $basic_machine | sed 's/^[^-]*-//'` - ;; - pentiumpro-* | p6-* | 6x86-* | athlon-*) - basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'` - ;; - pentiumii-* | pentium2-* | pentiumiii-* | pentium3-*) - basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'` - ;; - pentium4-*) - basic_machine=i786-`echo $basic_machine | sed 's/^[^-]*-//'` - ;; - pn) - basic_machine=pn-gould - ;; - power) basic_machine=power-ibm - ;; - ppc | ppcbe) basic_machine=powerpc-unknown - ;; - ppc-* | ppcbe-*) - basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'` - ;; - ppcle | powerpclittle | ppc-le | powerpc-little) - basic_machine=powerpcle-unknown - ;; - ppcle-* | powerpclittle-*) - basic_machine=powerpcle-`echo $basic_machine | sed 's/^[^-]*-//'` - ;; - ppc64) basic_machine=powerpc64-unknown - ;; - ppc64-*) basic_machine=powerpc64-`echo $basic_machine | sed 's/^[^-]*-//'` - ;; - ppc64le | powerpc64little | ppc64-le | powerpc64-little) - basic_machine=powerpc64le-unknown - ;; - ppc64le-* | powerpc64little-*) - basic_machine=powerpc64le-`echo $basic_machine | sed 's/^[^-]*-//'` - ;; - ps2) - basic_machine=i386-ibm - ;; - pw32) - basic_machine=i586-unknown - os=-pw32 - ;; - rdos | rdos64) - basic_machine=x86_64-pc - os=-rdos - ;; - rdos32) - basic_machine=i386-pc - os=-rdos - ;; - rom68k) - basic_machine=m68k-rom68k - os=-coff - ;; - rm[46]00) - basic_machine=mips-siemens - ;; - rtpc | rtpc-*) - basic_machine=romp-ibm - ;; - s390 | s390-*) - basic_machine=s390-ibm - ;; - s390x | s390x-*) - basic_machine=s390x-ibm - ;; - sa29200) - basic_machine=a29k-amd - os=-udi - ;; - sb1) - basic_machine=mipsisa64sb1-unknown - ;; - sb1el) - basic_machine=mipsisa64sb1el-unknown - ;; - sde) - basic_machine=mipsisa32-sde - os=-elf - ;; - sei) - basic_machine=mips-sei - os=-seiux - ;; - sequent) - basic_machine=i386-sequent - ;; - sh) - basic_machine=sh-hitachi - os=-hms - ;; - sh5el) - basic_machine=sh5le-unknown - ;; - sh64) - basic_machine=sh64-unknown - ;; - sparclite-wrs | simso-wrs) - basic_machine=sparclite-wrs - os=-vxworks - ;; - sps7) - basic_machine=m68k-bull - os=-sysv2 - ;; - spur) - basic_machine=spur-unknown - ;; - st2000) - basic_machine=m68k-tandem - ;; - stratus) - basic_machine=i860-stratus - os=-sysv4 - ;; - strongarm-* | thumb-*) - basic_machine=arm-`echo $basic_machine | sed 's/^[^-]*-//'` - ;; - sun2) - basic_machine=m68000-sun - ;; - sun2os3) - basic_machine=m68000-sun - os=-sunos3 - ;; - sun2os4) - basic_machine=m68000-sun - os=-sunos4 - ;; - sun3os3) - basic_machine=m68k-sun - os=-sunos3 - ;; - sun3os4) - basic_machine=m68k-sun - os=-sunos4 - ;; - sun4os3) - basic_machine=sparc-sun - os=-sunos3 - ;; - sun4os4) - basic_machine=sparc-sun - os=-sunos4 - ;; - sun4sol2) - basic_machine=sparc-sun - os=-solaris2 - ;; - sun3 | sun3-*) - basic_machine=m68k-sun - ;; - sun4) - basic_machine=sparc-sun - ;; - sun386 | sun386i | roadrunner) - basic_machine=i386-sun - ;; - sv1) - basic_machine=sv1-cray - os=-unicos - ;; - symmetry) - basic_machine=i386-sequent - os=-dynix - ;; - t3e) - basic_machine=alphaev5-cray - os=-unicos - ;; - t90) - basic_machine=t90-cray - os=-unicos - ;; - tile*) - basic_machine=$basic_machine-unknown - os=-linux-gnu - ;; - tx39) - basic_machine=mipstx39-unknown - ;; - tx39el) - basic_machine=mipstx39el-unknown - ;; - toad1) - basic_machine=pdp10-xkl - os=-tops20 - ;; - tower | tower-32) - basic_machine=m68k-ncr - ;; - tpf) - basic_machine=s390x-ibm - os=-tpf - ;; - udi29k) - basic_machine=a29k-amd - os=-udi - ;; - ultra3) - basic_machine=a29k-nyu - os=-sym1 - ;; - v810 | necv810) - basic_machine=v810-nec - os=-none - ;; - vaxv) - basic_machine=vax-dec - os=-sysv - ;; - vms) - basic_machine=vax-dec - os=-vms - ;; - vpp*|vx|vx-*) - basic_machine=f301-fujitsu - ;; - vxworks960) - basic_machine=i960-wrs - os=-vxworks - ;; - vxworks68) - basic_machine=m68k-wrs - os=-vxworks - ;; - vxworks29k) - basic_machine=a29k-wrs - os=-vxworks - ;; - w65*) - basic_machine=w65-wdc - os=-none - ;; - w89k-*) - basic_machine=hppa1.1-winbond - os=-proelf - ;; - xbox) - basic_machine=i686-pc - os=-mingw32 - ;; - xps | xps100) - basic_machine=xps100-honeywell - ;; - xscale-* | xscalee[bl]-*) - basic_machine=`echo $basic_machine | sed 's/^xscale/arm/'` - ;; - ymp) - basic_machine=ymp-cray - os=-unicos - ;; - z8k-*-coff) - basic_machine=z8k-unknown - os=-sim - ;; - z80-*-coff) - basic_machine=z80-unknown - os=-sim - ;; - none) - basic_machine=none-none - os=-none - ;; - -# Here we handle the default manufacturer of certain CPU types. It is in -# some cases the only manufacturer, in others, it is the most popular. - w89k) - basic_machine=hppa1.1-winbond - ;; - op50n) - basic_machine=hppa1.1-oki - ;; - op60c) - basic_machine=hppa1.1-oki - ;; - romp) - basic_machine=romp-ibm - ;; - mmix) - basic_machine=mmix-knuth - ;; - rs6000) - basic_machine=rs6000-ibm - ;; - vax) - basic_machine=vax-dec - ;; - pdp10) - # there are many clones, so DEC is not a safe bet - basic_machine=pdp10-unknown - ;; - pdp11) - basic_machine=pdp11-dec - ;; - we32k) - basic_machine=we32k-att - ;; - sh[1234] | sh[24]a | sh[24]aeb | sh[34]eb | sh[1234]le | sh[23]ele) - basic_machine=sh-unknown - ;; - sparc | sparcv8 | sparcv9 | sparcv9b | sparcv9v) - basic_machine=sparc-sun - ;; - cydra) - basic_machine=cydra-cydrome - ;; - orion) - basic_machine=orion-highlevel - ;; - orion105) - basic_machine=clipper-highlevel - ;; - mac | mpw | mac-mpw) - basic_machine=m68k-apple - ;; - pmac | pmac-mpw) - basic_machine=powerpc-apple - ;; - *-unknown) - # Make sure to match an already-canonicalized machine name. - ;; - *) - echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2 - exit 1 - ;; -esac - -# Here we canonicalize certain aliases for manufacturers. -case $basic_machine in - *-digital*) - basic_machine=`echo $basic_machine | sed 's/digital.*/dec/'` - ;; - *-commodore*) - basic_machine=`echo $basic_machine | sed 's/commodore.*/cbm/'` - ;; - *) - ;; -esac - -# Decode manufacturer-specific aliases for certain operating systems. - -if [ x"$os" != x"" ] -then -case $os in - # First match some system type aliases - # that might get confused with valid system types. - # -solaris* is a basic system type, with this one exception. - -auroraux) - os=-auroraux - ;; - -solaris1 | -solaris1.*) - os=`echo $os | sed -e 's|solaris1|sunos4|'` - ;; - -solaris) - os=-solaris2 - ;; - -svr4*) - os=-sysv4 - ;; - -unixware*) - os=-sysv4.2uw - ;; - -gnu/linux*) - os=`echo $os | sed -e 's|gnu/linux|linux-gnu|'` - ;; - # First accept the basic system types. - # The portable systems comes first. - # Each alternative MUST END IN A *, to match a version number. - # -sysv* is not here because it comes later, after sysvr4. - -gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \ - | -*vms* | -sco* | -esix* | -isc* | -aix* | -cnk* | -sunos | -sunos[34]*\ - | -hpux* | -unos* | -osf* | -luna* | -dgux* | -auroraux* | -solaris* \ - | -sym* | -kopensolaris* | -plan9* \ - | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \ - | -aos* | -aros* \ - | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \ - | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \ - | -hiux* | -386bsd* | -knetbsd* | -mirbsd* | -netbsd* \ - | -bitrig* | -openbsd* | -solidbsd* \ - | -ekkobsd* | -kfreebsd* | -freebsd* | -riscix* | -lynxos* \ - | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \ - | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \ - | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \ - | -chorusos* | -chorusrdb* | -cegcc* \ - | -cygwin* | -msys* | -pe* | -psos* | -moss* | -proelf* | -rtems* \ - | -mingw32* | -mingw64* | -linux-gnu* | -linux-android* \ - | -linux-newlib* | -linux-musl* | -linux-uclibc* \ - | -uxpv* | -beos* | -mpeix* | -udk* | -moxiebox* \ - | -interix* | -uwin* | -mks* | -rhapsody* | -darwin* | -opened* \ - | -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \ - | -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* \ - | -os2* | -vos* | -palmos* | -uclinux* | -nucleus* \ - | -morphos* | -superux* | -rtmk* | -rtmk-nova* | -windiss* \ - | -powermax* | -dnix* | -nx6 | -nx7 | -sei* | -dragonfly* \ - | -skyos* | -haiku* | -rdos* | -toppers* | -drops* | -es* | -tirtos*) - # Remember, each alternative MUST END IN *, to match a version number. - ;; - -qnx*) - case $basic_machine in - x86-* | i*86-*) - ;; - *) - os=-nto$os - ;; - esac - ;; - -nto-qnx*) - ;; - -nto*) - os=`echo $os | sed -e 's|nto|nto-qnx|'` - ;; - -sim | -es1800* | -hms* | -xray | -os68k* | -none* | -v88r* \ - | -windows* | -osx | -abug | -netware* | -os9* | -beos* | -haiku* \ - | -macos* | -mpw* | -magic* | -mmixware* | -mon960* | -lnews*) - ;; - -mac*) - os=`echo $os | sed -e 's|mac|macos|'` - ;; - -linux-dietlibc) - os=-linux-dietlibc - ;; - -linux*) - os=`echo $os | sed -e 's|linux|linux-gnu|'` - ;; - -sunos5*) - os=`echo $os | sed -e 's|sunos5|solaris2|'` - ;; - -sunos6*) - os=`echo $os | sed -e 's|sunos6|solaris3|'` - ;; - -opened*) - os=-openedition - ;; - -os400*) - os=-os400 - ;; - -wince*) - os=-wince - ;; - -osfrose*) - os=-osfrose - ;; - -osf*) - os=-osf - ;; - -utek*) - os=-bsd - ;; - -dynix*) - os=-bsd - ;; - -acis*) - os=-aos - ;; - -atheos*) - os=-atheos - ;; - -syllable*) - os=-syllable - ;; - -386bsd) - os=-bsd - ;; - -ctix* | -uts*) - os=-sysv - ;; - -nova*) - os=-rtmk-nova - ;; - -ns2 ) - os=-nextstep2 - ;; - -nsk*) - os=-nsk - ;; - # Preserve the version number of sinix5. - -sinix5.*) - os=`echo $os | sed -e 's|sinix|sysv|'` - ;; - -sinix*) - os=-sysv4 - ;; - -tpf*) - os=-tpf - ;; - -triton*) - os=-sysv3 - ;; - -oss*) - os=-sysv3 - ;; - -svr4) - os=-sysv4 - ;; - -svr3) - os=-sysv3 - ;; - -sysvr4) - os=-sysv4 - ;; - # This must come after -sysvr4. - -sysv*) - ;; - -ose*) - os=-ose - ;; - -es1800*) - os=-ose - ;; - -xenix) - os=-xenix - ;; - -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*) - os=-mint - ;; - -aros*) - os=-aros - ;; - -zvmoe) - os=-zvmoe - ;; - -dicos*) - os=-dicos - ;; - -nacl*) - ;; - -none) - ;; - *) - # Get rid of the `-' at the beginning of $os. - os=`echo $os | sed 's/[^-]*-//'` - echo Invalid configuration \`$1\': system \`$os\' not recognized 1>&2 - exit 1 - ;; -esac -else - -# Here we handle the default operating systems that come with various machines. -# The value should be what the vendor currently ships out the door with their -# machine or put another way, the most popular os provided with the machine. - -# Note that if you're going to try to match "-MANUFACTURER" here (say, -# "-sun"), then you have to tell the case statement up towards the top -# that MANUFACTURER isn't an operating system. Otherwise, code above -# will signal an error saying that MANUFACTURER isn't an operating -# system, and we'll never get to this point. - -case $basic_machine in - score-*) - os=-elf - ;; - spu-*) - os=-elf - ;; - *-acorn) - os=-riscix1.2 - ;; - arm*-rebel) - os=-linux - ;; - arm*-semi) - os=-aout - ;; - c4x-* | tic4x-*) - os=-coff - ;; - c8051-*) - os=-elf - ;; - hexagon-*) - os=-elf - ;; - tic54x-*) - os=-coff - ;; - tic55x-*) - os=-coff - ;; - tic6x-*) - os=-coff - ;; - # This must come before the *-dec entry. - pdp10-*) - os=-tops20 - ;; - pdp11-*) - os=-none - ;; - *-dec | vax-*) - os=-ultrix4.2 - ;; - m68*-apollo) - os=-domain - ;; - i386-sun) - os=-sunos4.0.2 - ;; - m68000-sun) - os=-sunos3 - ;; - m68*-cisco) - os=-aout - ;; - mep-*) - os=-elf - ;; - mips*-cisco) - os=-elf - ;; - mips*-*) - os=-elf - ;; - or32-*) - os=-coff - ;; - *-tti) # must be before sparc entry or we get the wrong os. - os=-sysv3 - ;; - sparc-* | *-sun) - os=-sunos4.1.1 - ;; - *-be) - os=-beos - ;; - *-haiku) - os=-haiku - ;; - *-ibm) - os=-aix - ;; - *-knuth) - os=-mmixware - ;; - *-wec) - os=-proelf - ;; - *-winbond) - os=-proelf - ;; - *-oki) - os=-proelf - ;; - *-hp) - os=-hpux - ;; - *-hitachi) - os=-hiux - ;; - i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent) - os=-sysv - ;; - *-cbm) - os=-amigaos - ;; - *-dg) - os=-dgux - ;; - *-dolphin) - os=-sysv3 - ;; - m68k-ccur) - os=-rtu - ;; - m88k-omron*) - os=-luna - ;; - *-next ) - os=-nextstep - ;; - *-sequent) - os=-ptx - ;; - *-crds) - os=-unos - ;; - *-ns) - os=-genix - ;; - i370-*) - os=-mvs - ;; - *-next) - os=-nextstep3 - ;; - *-gould) - os=-sysv - ;; - *-highlevel) - os=-bsd - ;; - *-encore) - os=-bsd - ;; - *-sgi) - os=-irix - ;; - *-siemens) - os=-sysv4 - ;; - *-masscomp) - os=-rtu - ;; - f30[01]-fujitsu | f700-fujitsu) - os=-uxpv - ;; - *-rom68k) - os=-coff - ;; - *-*bug) - os=-coff - ;; - *-apple) - os=-macos - ;; - *-atari*) - os=-mint - ;; - *) - os=-none - ;; -esac -fi - -# Here we handle the case where we know the os, and the CPU type, but not the -# manufacturer. We pick the logical manufacturer. -vendor=unknown -case $basic_machine in - *-unknown) - case $os in - -riscix*) - vendor=acorn - ;; - -sunos*) - vendor=sun - ;; - -cnk*|-aix*) - vendor=ibm - ;; - -beos*) - vendor=be - ;; - -hpux*) - vendor=hp - ;; - -mpeix*) - vendor=hp - ;; - -hiux*) - vendor=hitachi - ;; - -unos*) - vendor=crds - ;; - -dgux*) - vendor=dg - ;; - -luna*) - vendor=omron - ;; - -genix*) - vendor=ns - ;; - -mvs* | -opened*) - vendor=ibm - ;; - -os400*) - vendor=ibm - ;; - -ptx*) - vendor=sequent - ;; - -tpf*) - vendor=ibm - ;; - -vxsim* | -vxworks* | -windiss*) - vendor=wrs - ;; - -aux*) - vendor=apple - ;; - -hms*) - vendor=hitachi - ;; - -mpw* | -macos*) - vendor=apple - ;; - -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*) - vendor=atari - ;; - -vos*) - vendor=stratus - ;; - esac - basic_machine=`echo $basic_machine | sed "s/unknown/$vendor/"` - ;; -esac - -echo $basic_machine$os -exit - -# Local variables: -# eval: (add-hook 'write-file-hooks 'time-stamp) -# time-stamp-start: "timestamp='" -# time-stamp-format: "%:y-%02m-%02d" -# time-stamp-end: "'" -# End: diff --git a/configure b/configure deleted file mode 100755 index 2d46036..0000000 --- a/configure +++ /dev/null @@ -1,25483 +0,0 @@ -#! /bin/sh -# Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.69 for nghttp2 1.0.0. -# -# Report bugs to . -# -# -# Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc. -# -# -# This configure script is free software; the Free Software Foundation -# gives unlimited permission to copy, distribute and modify it. -## -------------------- ## -## M4sh Initialization. ## -## -------------------- ## - -# Be more Bourne compatible -DUALCASE=1; export DUALCASE # for MKS sh -if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : - emulate sh - NULLCMD=: - # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which - # is contrary to our usage. Disable this feature. - alias -g '${1+"$@"}'='"$@"' - setopt NO_GLOB_SUBST -else - case `(set -o) 2>/dev/null` in #( - *posix*) : - set -o posix ;; #( - *) : - ;; -esac -fi - - -as_nl=' -' -export as_nl -# Printing a long string crashes Solaris 7 /usr/bin/printf. -as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' -as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo -as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo -# Prefer a ksh shell builtin over an external printf program on Solaris, -# but without wasting forks for bash or zsh. -if test -z "$BASH_VERSION$ZSH_VERSION" \ - && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then - as_echo='print -r --' - as_echo_n='print -rn --' -elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then - as_echo='printf %s\n' - as_echo_n='printf %s' -else - if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then - as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' - as_echo_n='/usr/ucb/echo -n' - else - as_echo_body='eval expr "X$1" : "X\\(.*\\)"' - as_echo_n_body='eval - arg=$1; - case $arg in #( - *"$as_nl"*) - expr "X$arg" : "X\\(.*\\)$as_nl"; - arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; - esac; - expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" - ' - export as_echo_n_body - as_echo_n='sh -c $as_echo_n_body as_echo' - fi - export as_echo_body - as_echo='sh -c $as_echo_body as_echo' -fi - -# The user is always right. -if test "${PATH_SEPARATOR+set}" != set; then - PATH_SEPARATOR=: - (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { - (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || - PATH_SEPARATOR=';' - } -fi - - -# IFS -# We need space, tab and new line, in precisely that order. Quoting is -# there to prevent editors from complaining about space-tab. -# (If _AS_PATH_WALK were called with IFS unset, it would disable word -# splitting by setting IFS to empty value.) -IFS=" "" $as_nl" - -# Find who we are. Look in the path if we contain no directory separator. -as_myself= -case $0 in #(( - *[\\/]* ) as_myself=$0 ;; - *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break - done -IFS=$as_save_IFS - - ;; -esac -# We did not find ourselves, most probably we were run as `sh COMMAND' -# in which case we are not to be found in the path. -if test "x$as_myself" = x; then - as_myself=$0 -fi -if test ! -f "$as_myself"; then - $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 - exit 1 -fi - -# Unset variables that we do not need and which cause bugs (e.g. in -# pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1" -# suppresses any "Segmentation fault" message there. '((' could -# trigger a bug in pdksh 5.2.14. -for as_var in BASH_ENV ENV MAIL MAILPATH -do eval test x\${$as_var+set} = xset \ - && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : -done -PS1='$ ' -PS2='> ' -PS4='+ ' - -# NLS nuisances. -LC_ALL=C -export LC_ALL -LANGUAGE=C -export LANGUAGE - -# CDPATH. -(unset CDPATH) >/dev/null 2>&1 && unset CDPATH - -# Use a proper internal environment variable to ensure we don't fall - # into an infinite loop, continuously re-executing ourselves. - if test x"${_as_can_reexec}" != xno && test "x$CONFIG_SHELL" != x; then - _as_can_reexec=no; export _as_can_reexec; - # We cannot yet assume a decent shell, so we have to provide a -# neutralization value for shells without unset; and this also -# works around shells that cannot unset nonexistent variables. -# Preserve -v and -x to the replacement shell. -BASH_ENV=/dev/null -ENV=/dev/null -(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV -case $- in # (((( - *v*x* | *x*v* ) as_opts=-vx ;; - *v* ) as_opts=-v ;; - *x* ) as_opts=-x ;; - * ) as_opts= ;; -esac -exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} -# Admittedly, this is quite paranoid, since all the known shells bail -# out after a failed `exec'. -$as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2 -as_fn_exit 255 - fi - # We don't want this to propagate to other subprocesses. - { _as_can_reexec=; unset _as_can_reexec;} -if test "x$CONFIG_SHELL" = x; then - as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then : - emulate sh - NULLCMD=: - # Pre-4.2 versions of Zsh do word splitting on \${1+\"\$@\"}, which - # is contrary to our usage. Disable this feature. - alias -g '\${1+\"\$@\"}'='\"\$@\"' - setopt NO_GLOB_SUBST -else - case \`(set -o) 2>/dev/null\` in #( - *posix*) : - set -o posix ;; #( - *) : - ;; -esac -fi -" - as_required="as_fn_return () { (exit \$1); } -as_fn_success () { as_fn_return 0; } -as_fn_failure () { as_fn_return 1; } -as_fn_ret_success () { return 0; } -as_fn_ret_failure () { return 1; } - -exitcode=0 -as_fn_success || { exitcode=1; echo as_fn_success failed.; } -as_fn_failure && { exitcode=1; echo as_fn_failure succeeded.; } -as_fn_ret_success || { exitcode=1; echo as_fn_ret_success failed.; } -as_fn_ret_failure && { exitcode=1; echo as_fn_ret_failure succeeded.; } -if ( set x; as_fn_ret_success y && test x = \"\$1\" ); then : - -else - exitcode=1; echo positional parameters were not saved. -fi -test x\$exitcode = x0 || exit 1 -test -x / || exit 1" - as_suggested=" as_lineno_1=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_1a=\$LINENO - as_lineno_2=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_2a=\$LINENO - eval 'test \"x\$as_lineno_1'\$as_run'\" != \"x\$as_lineno_2'\$as_run'\" && - test \"x\`expr \$as_lineno_1'\$as_run' + 1\`\" = \"x\$as_lineno_2'\$as_run'\"' || exit 1 -test \$(( 1 + 1 )) = 2 || exit 1 - - test -n \"\${ZSH_VERSION+set}\${BASH_VERSION+set}\" || ( - ECHO='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' - ECHO=\$ECHO\$ECHO\$ECHO\$ECHO\$ECHO - ECHO=\$ECHO\$ECHO\$ECHO\$ECHO\$ECHO\$ECHO - PATH=/empty FPATH=/empty; export PATH FPATH - test \"X\`printf %s \$ECHO\`\" = \"X\$ECHO\" \\ - || test \"X\`print -r -- \$ECHO\`\" = \"X\$ECHO\" ) || exit 1" - if (eval "$as_required") 2>/dev/null; then : - as_have_required=yes -else - as_have_required=no -fi - if test x$as_have_required = xyes && (eval "$as_suggested") 2>/dev/null; then : - -else - as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -as_found=false -for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - as_found=: - case $as_dir in #( - /*) - for as_base in sh bash ksh sh5; do - # Try only shells that exist, to save several forks. - as_shell=$as_dir/$as_base - if { test -f "$as_shell" || test -f "$as_shell.exe"; } && - { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$as_shell"; } 2>/dev/null; then : - CONFIG_SHELL=$as_shell as_have_required=yes - if { $as_echo "$as_bourne_compatible""$as_suggested" | as_run=a "$as_shell"; } 2>/dev/null; then : - break 2 -fi -fi - done;; - esac - as_found=false -done -$as_found || { if { test -f "$SHELL" || test -f "$SHELL.exe"; } && - { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$SHELL"; } 2>/dev/null; then : - CONFIG_SHELL=$SHELL as_have_required=yes -fi; } -IFS=$as_save_IFS - - - if test "x$CONFIG_SHELL" != x; then : - export CONFIG_SHELL - # We cannot yet assume a decent shell, so we have to provide a -# neutralization value for shells without unset; and this also -# works around shells that cannot unset nonexistent variables. -# Preserve -v and -x to the replacement shell. -BASH_ENV=/dev/null -ENV=/dev/null -(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV -case $- in # (((( - *v*x* | *x*v* ) as_opts=-vx ;; - *v* ) as_opts=-v ;; - *x* ) as_opts=-x ;; - * ) as_opts= ;; -esac -exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} -# Admittedly, this is quite paranoid, since all the known shells bail -# out after a failed `exec'. -$as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2 -exit 255 -fi - - if test x$as_have_required = xno; then : - $as_echo "$0: This script requires a shell more modern than all" - $as_echo "$0: the shells that I found on your system." - if test x${ZSH_VERSION+set} = xset ; then - $as_echo "$0: In particular, zsh $ZSH_VERSION has bugs and should" - $as_echo "$0: be upgraded to zsh 4.3.4 or later." - else - $as_echo "$0: Please tell bug-autoconf@gnu.org and -$0: t-tujikawa@users.sourceforge.net about your system, -$0: including any error possibly output before this -$0: message. Then install a modern shell, or manually run -$0: the script under such a shell if you do have one." - fi - exit 1 -fi -fi -fi -SHELL=${CONFIG_SHELL-/bin/sh} -export SHELL -# Unset more variables known to interfere with behavior of common tools. -CLICOLOR_FORCE= GREP_OPTIONS= -unset CLICOLOR_FORCE GREP_OPTIONS - -## --------------------- ## -## M4sh Shell Functions. ## -## --------------------- ## -# as_fn_unset VAR -# --------------- -# Portably unset VAR. -as_fn_unset () -{ - { eval $1=; unset $1;} -} -as_unset=as_fn_unset - -# as_fn_set_status STATUS -# ----------------------- -# Set $? to STATUS, without forking. -as_fn_set_status () -{ - return $1 -} # as_fn_set_status - -# as_fn_exit STATUS -# ----------------- -# Exit the shell with STATUS, even in a "trap 0" or "set -e" context. -as_fn_exit () -{ - set +e - as_fn_set_status $1 - exit $1 -} # as_fn_exit - -# as_fn_mkdir_p -# ------------- -# Create "$as_dir" as a directory, including parents if necessary. -as_fn_mkdir_p () -{ - - case $as_dir in #( - -*) as_dir=./$as_dir;; - esac - test -d "$as_dir" || eval $as_mkdir_p || { - as_dirs= - while :; do - case $as_dir in #( - *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( - *) as_qdir=$as_dir;; - esac - as_dirs="'$as_qdir' $as_dirs" - as_dir=`$as_dirname -- "$as_dir" || -$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ - X"$as_dir" : 'X\(//\)[^/]' \| \ - X"$as_dir" : 'X\(//\)$' \| \ - X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || -$as_echo X"$as_dir" | - sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ - s//\1/ - q - } - /^X\(\/\/\)[^/].*/{ - s//\1/ - q - } - /^X\(\/\/\)$/{ - s//\1/ - q - } - /^X\(\/\).*/{ - s//\1/ - q - } - s/.*/./; q'` - test -d "$as_dir" && break - done - test -z "$as_dirs" || eval "mkdir $as_dirs" - } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" - - -} # as_fn_mkdir_p - -# as_fn_executable_p FILE -# ----------------------- -# Test if FILE is an executable regular file. -as_fn_executable_p () -{ - test -f "$1" && test -x "$1" -} # as_fn_executable_p -# as_fn_append VAR VALUE -# ---------------------- -# Append the text in VALUE to the end of the definition contained in VAR. Take -# advantage of any shell optimizations that allow amortized linear growth over -# repeated appends, instead of the typical quadratic growth present in naive -# implementations. -if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then : - eval 'as_fn_append () - { - eval $1+=\$2 - }' -else - as_fn_append () - { - eval $1=\$$1\$2 - } -fi # as_fn_append - -# as_fn_arith ARG... -# ------------------ -# Perform arithmetic evaluation on the ARGs, and store the result in the -# global $as_val. Take advantage of shells that can avoid forks. The arguments -# must be portable across $(()) and expr. -if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then : - eval 'as_fn_arith () - { - as_val=$(( $* )) - }' -else - as_fn_arith () - { - as_val=`expr "$@" || test $? -eq 1` - } -fi # as_fn_arith - - -# as_fn_error STATUS ERROR [LINENO LOG_FD] -# ---------------------------------------- -# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are -# provided, also output the error to LOG_FD, referencing LINENO. Then exit the -# script with STATUS, using 1 if that was 0. -as_fn_error () -{ - as_status=$1; test $as_status -eq 0 && as_status=1 - if test "$4"; then - as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack - $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 - fi - $as_echo "$as_me: error: $2" >&2 - as_fn_exit $as_status -} # as_fn_error - -if expr a : '\(a\)' >/dev/null 2>&1 && - test "X`expr 00001 : '.*\(...\)'`" = X001; then - as_expr=expr -else - as_expr=false -fi - -if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then - as_basename=basename -else - as_basename=false -fi - -if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then - as_dirname=dirname -else - as_dirname=false -fi - -as_me=`$as_basename -- "$0" || -$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ - X"$0" : 'X\(//\)$' \| \ - X"$0" : 'X\(/\)' \| . 2>/dev/null || -$as_echo X/"$0" | - sed '/^.*\/\([^/][^/]*\)\/*$/{ - s//\1/ - q - } - /^X\/\(\/\/\)$/{ - s//\1/ - q - } - /^X\/\(\/\).*/{ - s//\1/ - q - } - s/.*/./; q'` - -# Avoid depending upon Character Ranges. -as_cr_letters='abcdefghijklmnopqrstuvwxyz' -as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' -as_cr_Letters=$as_cr_letters$as_cr_LETTERS -as_cr_digits='0123456789' -as_cr_alnum=$as_cr_Letters$as_cr_digits - - - as_lineno_1=$LINENO as_lineno_1a=$LINENO - as_lineno_2=$LINENO as_lineno_2a=$LINENO - eval 'test "x$as_lineno_1'$as_run'" != "x$as_lineno_2'$as_run'" && - test "x`expr $as_lineno_1'$as_run' + 1`" = "x$as_lineno_2'$as_run'"' || { - # Blame Lee E. McMahon (1931-1989) for sed's syntax. :-) - sed -n ' - p - /[$]LINENO/= - ' <$as_myself | - sed ' - s/[$]LINENO.*/&-/ - t lineno - b - :lineno - N - :loop - s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/ - t loop - s/-\n.*// - ' >$as_me.lineno && - chmod +x "$as_me.lineno" || - { $as_echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2; as_fn_exit 1; } - - # If we had to re-execute with $CONFIG_SHELL, we're ensured to have - # already done that, so ensure we don't try to do so again and fall - # in an infinite loop. This has already happened in practice. - _as_can_reexec=no; export _as_can_reexec - # Don't try to exec as it changes $[0], causing all sort of problems - # (the dirname of $[0] is not the place where we might find the - # original and so on. Autoconf is especially sensitive to this). - . "./$as_me.lineno" - # Exit status is that of the last command. - exit -} - -ECHO_C= ECHO_N= ECHO_T= -case `echo -n x` in #((((( --n*) - case `echo 'xy\c'` in - *c*) ECHO_T=' ';; # ECHO_T is single tab character. - xy) ECHO_C='\c';; - *) echo `echo ksh88 bug on AIX 6.1` > /dev/null - ECHO_T=' ';; - esac;; -*) - ECHO_N='-n';; -esac - -rm -f conf$$ conf$$.exe conf$$.file -if test -d conf$$.dir; then - rm -f conf$$.dir/conf$$.file -else - rm -f conf$$.dir - mkdir conf$$.dir 2>/dev/null -fi -if (echo >conf$$.file) 2>/dev/null; then - if ln -s conf$$.file conf$$ 2>/dev/null; then - as_ln_s='ln -s' - # ... but there are two gotchas: - # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. - # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. - # In both cases, we have to default to `cp -pR'. - ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || - as_ln_s='cp -pR' - elif ln conf$$.file conf$$ 2>/dev/null; then - as_ln_s=ln - else - as_ln_s='cp -pR' - fi -else - as_ln_s='cp -pR' -fi -rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file -rmdir conf$$.dir 2>/dev/null - -if mkdir -p . 2>/dev/null; then - as_mkdir_p='mkdir -p "$as_dir"' -else - test -d ./-p && rmdir ./-p - as_mkdir_p=false -fi - -as_test_x='test -x' -as_executable_p=as_fn_executable_p - -# Sed expression to map a string onto a valid CPP name. -as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" - -# Sed expression to map a string onto a valid variable name. -as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" - -SHELL=${CONFIG_SHELL-/bin/sh} - - -test -n "$DJDIR" || exec 7<&0 &1 - -# Name of the host. -# hostname on some systems (SVR3.2, old GNU/Linux) returns a bogus exit status, -# so uname gets run too. -ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q` - -# -# Initializations. -# -ac_default_prefix=/usr/local -ac_clean_files= -ac_config_libobj_dir=. -LIBOBJS= -cross_compiling=no -subdirs= -MFLAGS= -MAKEFLAGS= - -# Identity of this package. -PACKAGE_NAME='nghttp2' -PACKAGE_TARNAME='nghttp2' -PACKAGE_VERSION='1.0.0' -PACKAGE_STRING='nghttp2 1.0.0' -PACKAGE_BUGREPORT='t-tujikawa@users.sourceforge.net' -PACKAGE_URL='' - -# Factoring default headers for most tests. -ac_includes_default="\ -#include -#ifdef HAVE_SYS_TYPES_H -# include -#endif -#ifdef HAVE_SYS_STAT_H -# include -#endif -#ifdef STDC_HEADERS -# include -# include -#else -# ifdef HAVE_STDLIB_H -# include -# endif -#endif -#ifdef HAVE_STRING_H -# if !defined STDC_HEADERS && defined HAVE_MEMORY_H -# include -# endif -# include -#endif -#ifdef HAVE_STRINGS_H -# include -#endif -#ifdef HAVE_INTTYPES_H -# include -#endif -#ifdef HAVE_STDINT_H -# include -#endif -#ifdef HAVE_UNISTD_H -# include -#endif" - -ac_subst_vars='am__EXEEXT_FALSE -am__EXEEXT_TRUE -LTLIBOBJS -APPLDFLAGS -TESTLDADD -WARNCFLAGS -ENABLE_TINY_NGHTTPD_FALSE -ENABLE_TINY_NGHTTPD_TRUE -LIBOBJS -ENABLE_FAILMALLOC_FALSE -ENABLE_FAILMALLOC_TRUE -HAVE_CYTHON_FALSE -HAVE_CYTHON_TRUE -ENABLE_PYTHON_BINDINGS_FALSE -ENABLE_PYTHON_BINDINGS_TRUE -ENABLE_EXAMPLES_FALSE -ENABLE_EXAMPLES_TRUE -ENABLE_ASIO_LIB_FALSE -ENABLE_ASIO_LIB_TRUE -ENABLE_HPACK_TOOLS_FALSE -ENABLE_HPACK_TOOLS_TRUE -ENABLE_APP_FALSE -ENABLE_APP_TRUE -BOOST_THREAD_LIB -BOOST_SYSTEM_LIB -BOOST_ASIO_LIB -BOOST_LDFLAGS -BOOST_CPPFLAGS -HAVE_SPDYLAY_FALSE -HAVE_SPDYLAY_TRUE -LIBSPDYLAY_LIBS -LIBSPDYLAY_CFLAGS -JEMALLOC_LIBS -HAVE_LIBXML2_FALSE -HAVE_LIBXML2_TRUE -XML_LIBS -XML_CPPFLAGS -XML2_CONFIG -JANSSON_LIBS -JANSSON_CFLAGS -LIBEVENT_OPENSSL_LIBS -LIBEVENT_OPENSSL_CFLAGS -OPENSSL_LIBS -OPENSSL_CFLAGS -LIBEV_CFLAGS -LIBEV_LIBS -HAVE_CUNIT_FALSE -HAVE_CUNIT_TRUE -CUNIT_LIBS -CUNIT_CFLAGS -ZLIB_LIBS -ZLIB_CFLAGS -HAVE_CXX11 -PYTHON_EXTRA_LDFLAGS -PYTHON_EXTRA_LIBS -PYTHON_SITE_PKG -PYTHON_LDFLAGS -PYTHON_CPPFLAGS -pkgpyexecdir -pyexecdir -pkgpythondir -pythondir -PYTHON_PLATFORM -PYTHON_EXEC_PREFIX -PYTHON_PREFIX -PYTHON_VERSION -PYTHON -PKG_CONFIG_LIBDIR -PKG_CONFIG_PATH -PKG_CONFIG -am__fastdepCXX_FALSE -am__fastdepCXX_TRUE -CXXDEPMODE -CXXCPP -ac_ct_CXX -CXXFLAGS -CXX -CYTHON -PACKAGE_VERSION_NUM -LT_AGE -LT_REVISION -LT_CURRENT -AM_BACKSLASH -AM_DEFAULT_VERBOSITY -AM_DEFAULT_V -AM_V -am__fastdepCC_FALSE -am__fastdepCC_TRUE -CCDEPMODE -am__nodep -AMDEPBACKSLASH -AMDEP_FALSE -AMDEP_TRUE -am__quote -am__include -DEPDIR -am__untar -am__tar -AMTAR -am__leading_dot -SET_MAKE -mkdir_p -MKDIR_P -INSTALL_STRIP_PROGRAM -install_sh -MAKEINFO -AUTOHEADER -AUTOMAKE -AUTOCONF -ACLOCAL -VERSION -PACKAGE -CYGPATH_W -am__isrc -INSTALL_DATA -INSTALL_SCRIPT -INSTALL_PROGRAM -target_os -target_vendor -target_cpu -target -OTOOL64 -OTOOL -LIPO -NMEDIT -DSYMUTIL -MANIFEST_TOOL -AWK -RANLIB -STRIP -ac_ct_AR -AR -DLLTOOL -OBJDUMP -LN_S -NM -ac_ct_DUMPBIN -DUMPBIN -LD -FGREP -SED -host_os -host_vendor -host_cpu -host -build_os -build_vendor -build_cpu -build -LIBTOOL -EGREP -GREP -CPP -OBJEXT -EXEEXT -ac_ct_CC -CPPFLAGS -LDFLAGS -CFLAGS -CC -target_alias -host_alias -build_alias -LIBS -ECHO_T -ECHO_N -ECHO_C -DEFS -mandir -localedir -libdir -psdir -pdfdir -dvidir -htmldir -infodir -docdir -oldincludedir -includedir -localstatedir -sharedstatedir -sysconfdir -datadir -datarootdir -libexecdir -sbindir -bindir -program_transform_name -prefix -exec_prefix -PACKAGE_URL -PACKAGE_BUGREPORT -PACKAGE_STRING -PACKAGE_VERSION -PACKAGE_TARNAME -PACKAGE_NAME -PATH_SEPARATOR -SHELL' -ac_subst_files='' -ac_user_opts=' -enable_option_checking -enable_shared -enable_static -with_pic -enable_fast_install -with_gnu_ld -with_sysroot -enable_libtool_lock -enable_dependency_tracking -enable_silent_rules -enable_werror -enable_debug -enable_threads -enable_app -enable_hpack_tools -enable_asio_lib -enable_examples -enable_python_bindings -enable_failmalloc -with_libxml2 -with_jemalloc -with_spdylay -with_cython -with_xml_prefix -with_xml_exec_prefix -enable_xmltest -with_boost -with_boost_libdir -with_boost_asio -with_boost_system -with_boost_thread -enable_assert -enable_largefile -' - ac_precious_vars='build_alias -host_alias -target_alias -CC -CFLAGS -LDFLAGS -LIBS -CPPFLAGS -CPP -CYTHON -CXX -CXXFLAGS -CCC -CXXCPP -PKG_CONFIG -PKG_CONFIG_PATH -PKG_CONFIG_LIBDIR -PYTHON -PYTHON_VERSION -ZLIB_CFLAGS -ZLIB_LIBS -CUNIT_CFLAGS -CUNIT_LIBS -OPENSSL_CFLAGS -OPENSSL_LIBS -LIBEVENT_OPENSSL_CFLAGS -LIBEVENT_OPENSSL_LIBS -JANSSON_CFLAGS -JANSSON_LIBS -LIBSPDYLAY_CFLAGS -LIBSPDYLAY_LIBS' - - -# Initialize some variables set by options. -ac_init_help= -ac_init_version=false -ac_unrecognized_opts= -ac_unrecognized_sep= -# The variables have the same names as the options, with -# dashes changed to underlines. -cache_file=/dev/null -exec_prefix=NONE -no_create= -no_recursion= -prefix=NONE -program_prefix=NONE -program_suffix=NONE -program_transform_name=s,x,x, -silent= -site= -srcdir= -verbose= -x_includes=NONE -x_libraries=NONE - -# Installation directory options. -# These are left unexpanded so users can "make install exec_prefix=/foo" -# and all the variables that are supposed to be based on exec_prefix -# by default will actually change. -# Use braces instead of parens because sh, perl, etc. also accept them. -# (The list follows the same order as the GNU Coding Standards.) -bindir='${exec_prefix}/bin' -sbindir='${exec_prefix}/sbin' -libexecdir='${exec_prefix}/libexec' -datarootdir='${prefix}/share' -datadir='${datarootdir}' -sysconfdir='${prefix}/etc' -sharedstatedir='${prefix}/com' -localstatedir='${prefix}/var' -includedir='${prefix}/include' -oldincludedir='/usr/include' -docdir='${datarootdir}/doc/${PACKAGE_TARNAME}' -infodir='${datarootdir}/info' -htmldir='${docdir}' -dvidir='${docdir}' -pdfdir='${docdir}' -psdir='${docdir}' -libdir='${exec_prefix}/lib' -localedir='${datarootdir}/locale' -mandir='${datarootdir}/man' - -ac_prev= -ac_dashdash= -for ac_option -do - # If the previous option needs an argument, assign it. - if test -n "$ac_prev"; then - eval $ac_prev=\$ac_option - ac_prev= - continue - fi - - case $ac_option in - *=?*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;; - *=) ac_optarg= ;; - *) ac_optarg=yes ;; - esac - - # Accept the important Cygnus configure options, so we can diagnose typos. - - case $ac_dashdash$ac_option in - --) - ac_dashdash=yes ;; - - -bindir | --bindir | --bindi | --bind | --bin | --bi) - ac_prev=bindir ;; - -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*) - bindir=$ac_optarg ;; - - -build | --build | --buil | --bui | --bu) - ac_prev=build_alias ;; - -build=* | --build=* | --buil=* | --bui=* | --bu=*) - build_alias=$ac_optarg ;; - - -cache-file | --cache-file | --cache-fil | --cache-fi \ - | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) - ac_prev=cache_file ;; - -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \ - | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*) - cache_file=$ac_optarg ;; - - --config-cache | -C) - cache_file=config.cache ;; - - -datadir | --datadir | --datadi | --datad) - ac_prev=datadir ;; - -datadir=* | --datadir=* | --datadi=* | --datad=*) - datadir=$ac_optarg ;; - - -datarootdir | --datarootdir | --datarootdi | --datarootd | --dataroot \ - | --dataroo | --dataro | --datar) - ac_prev=datarootdir ;; - -datarootdir=* | --datarootdir=* | --datarootdi=* | --datarootd=* \ - | --dataroot=* | --dataroo=* | --dataro=* | --datar=*) - datarootdir=$ac_optarg ;; - - -disable-* | --disable-*) - ac_useropt=`expr "x$ac_option" : 'x-*disable-\(.*\)'` - # Reject names that are not valid shell variable names. - expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && - as_fn_error $? "invalid feature name: $ac_useropt" - ac_useropt_orig=$ac_useropt - ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` - case $ac_user_opts in - *" -"enable_$ac_useropt" -"*) ;; - *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--disable-$ac_useropt_orig" - ac_unrecognized_sep=', ';; - esac - eval enable_$ac_useropt=no ;; - - -docdir | --docdir | --docdi | --doc | --do) - ac_prev=docdir ;; - -docdir=* | --docdir=* | --docdi=* | --doc=* | --do=*) - docdir=$ac_optarg ;; - - -dvidir | --dvidir | --dvidi | --dvid | --dvi | --dv) - ac_prev=dvidir ;; - -dvidir=* | --dvidir=* | --dvidi=* | --dvid=* | --dvi=* | --dv=*) - dvidir=$ac_optarg ;; - - -enable-* | --enable-*) - ac_useropt=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'` - # Reject names that are not valid shell variable names. - expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && - as_fn_error $? "invalid feature name: $ac_useropt" - ac_useropt_orig=$ac_useropt - ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` - case $ac_user_opts in - *" -"enable_$ac_useropt" -"*) ;; - *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--enable-$ac_useropt_orig" - ac_unrecognized_sep=', ';; - esac - eval enable_$ac_useropt=\$ac_optarg ;; - - -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \ - | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \ - | --exec | --exe | --ex) - ac_prev=exec_prefix ;; - -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \ - | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \ - | --exec=* | --exe=* | --ex=*) - exec_prefix=$ac_optarg ;; - - -gas | --gas | --ga | --g) - # Obsolete; use --with-gas. - with_gas=yes ;; - - -help | --help | --hel | --he | -h) - ac_init_help=long ;; - -help=r* | --help=r* | --hel=r* | --he=r* | -hr*) - ac_init_help=recursive ;; - -help=s* | --help=s* | --hel=s* | --he=s* | -hs*) - ac_init_help=short ;; - - -host | --host | --hos | --ho) - ac_prev=host_alias ;; - -host=* | --host=* | --hos=* | --ho=*) - host_alias=$ac_optarg ;; - - -htmldir | --htmldir | --htmldi | --htmld | --html | --htm | --ht) - ac_prev=htmldir ;; - -htmldir=* | --htmldir=* | --htmldi=* | --htmld=* | --html=* | --htm=* \ - | --ht=*) - htmldir=$ac_optarg ;; - - -includedir | --includedir | --includedi | --included | --include \ - | --includ | --inclu | --incl | --inc) - ac_prev=includedir ;; - -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \ - | --includ=* | --inclu=* | --incl=* | --inc=*) - includedir=$ac_optarg ;; - - -infodir | --infodir | --infodi | --infod | --info | --inf) - ac_prev=infodir ;; - -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*) - infodir=$ac_optarg ;; - - -libdir | --libdir | --libdi | --libd) - ac_prev=libdir ;; - -libdir=* | --libdir=* | --libdi=* | --libd=*) - libdir=$ac_optarg ;; - - -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \ - | --libexe | --libex | --libe) - ac_prev=libexecdir ;; - -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \ - | --libexe=* | --libex=* | --libe=*) - libexecdir=$ac_optarg ;; - - -localedir | --localedir | --localedi | --localed | --locale) - ac_prev=localedir ;; - -localedir=* | --localedir=* | --localedi=* | --localed=* | --locale=*) - localedir=$ac_optarg ;; - - -localstatedir | --localstatedir | --localstatedi | --localstated \ - | --localstate | --localstat | --localsta | --localst | --locals) - ac_prev=localstatedir ;; - -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \ - | --localstate=* | --localstat=* | --localsta=* | --localst=* | --locals=*) - localstatedir=$ac_optarg ;; - - -mandir | --mandir | --mandi | --mand | --man | --ma | --m) - ac_prev=mandir ;; - -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*) - mandir=$ac_optarg ;; - - -nfp | --nfp | --nf) - # Obsolete; use --without-fp. - with_fp=no ;; - - -no-create | --no-create | --no-creat | --no-crea | --no-cre \ - | --no-cr | --no-c | -n) - no_create=yes ;; - - -no-recursion | --no-recursion | --no-recursio | --no-recursi \ - | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) - no_recursion=yes ;; - - -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \ - | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \ - | --oldin | --oldi | --old | --ol | --o) - ac_prev=oldincludedir ;; - -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \ - | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \ - | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*) - oldincludedir=$ac_optarg ;; - - -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) - ac_prev=prefix ;; - -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) - prefix=$ac_optarg ;; - - -program-prefix | --program-prefix | --program-prefi | --program-pref \ - | --program-pre | --program-pr | --program-p) - ac_prev=program_prefix ;; - -program-prefix=* | --program-prefix=* | --program-prefi=* \ - | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*) - program_prefix=$ac_optarg ;; - - -program-suffix | --program-suffix | --program-suffi | --program-suff \ - | --program-suf | --program-su | --program-s) - ac_prev=program_suffix ;; - -program-suffix=* | --program-suffix=* | --program-suffi=* \ - | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*) - program_suffix=$ac_optarg ;; - - -program-transform-name | --program-transform-name \ - | --program-transform-nam | --program-transform-na \ - | --program-transform-n | --program-transform- \ - | --program-transform | --program-transfor \ - | --program-transfo | --program-transf \ - | --program-trans | --program-tran \ - | --progr-tra | --program-tr | --program-t) - ac_prev=program_transform_name ;; - -program-transform-name=* | --program-transform-name=* \ - | --program-transform-nam=* | --program-transform-na=* \ - | --program-transform-n=* | --program-transform-=* \ - | --program-transform=* | --program-transfor=* \ - | --program-transfo=* | --program-transf=* \ - | --program-trans=* | --program-tran=* \ - | --progr-tra=* | --program-tr=* | --program-t=*) - program_transform_name=$ac_optarg ;; - - -pdfdir | --pdfdir | --pdfdi | --pdfd | --pdf | --pd) - ac_prev=pdfdir ;; - -pdfdir=* | --pdfdir=* | --pdfdi=* | --pdfd=* | --pdf=* | --pd=*) - pdfdir=$ac_optarg ;; - - -psdir | --psdir | --psdi | --psd | --ps) - ac_prev=psdir ;; - -psdir=* | --psdir=* | --psdi=* | --psd=* | --ps=*) - psdir=$ac_optarg ;; - - -q | -quiet | --quiet | --quie | --qui | --qu | --q \ - | -silent | --silent | --silen | --sile | --sil) - silent=yes ;; - - -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) - ac_prev=sbindir ;; - -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ - | --sbi=* | --sb=*) - sbindir=$ac_optarg ;; - - -sharedstatedir | --sharedstatedir | --sharedstatedi \ - | --sharedstated | --sharedstate | --sharedstat | --sharedsta \ - | --sharedst | --shareds | --shared | --share | --shar \ - | --sha | --sh) - ac_prev=sharedstatedir ;; - -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \ - | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \ - | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \ - | --sha=* | --sh=*) - sharedstatedir=$ac_optarg ;; - - -site | --site | --sit) - ac_prev=site ;; - -site=* | --site=* | --sit=*) - site=$ac_optarg ;; - - -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) - ac_prev=srcdir ;; - -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) - srcdir=$ac_optarg ;; - - -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \ - | --syscon | --sysco | --sysc | --sys | --sy) - ac_prev=sysconfdir ;; - -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \ - | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*) - sysconfdir=$ac_optarg ;; - - -target | --target | --targe | --targ | --tar | --ta | --t) - ac_prev=target_alias ;; - -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*) - target_alias=$ac_optarg ;; - - -v | -verbose | --verbose | --verbos | --verbo | --verb) - verbose=yes ;; - - -version | --version | --versio | --versi | --vers | -V) - ac_init_version=: ;; - - -with-* | --with-*) - ac_useropt=`expr "x$ac_option" : 'x-*with-\([^=]*\)'` - # Reject names that are not valid shell variable names. - expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && - as_fn_error $? "invalid package name: $ac_useropt" - ac_useropt_orig=$ac_useropt - ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` - case $ac_user_opts in - *" -"with_$ac_useropt" -"*) ;; - *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--with-$ac_useropt_orig" - ac_unrecognized_sep=', ';; - esac - eval with_$ac_useropt=\$ac_optarg ;; - - -without-* | --without-*) - ac_useropt=`expr "x$ac_option" : 'x-*without-\(.*\)'` - # Reject names that are not valid shell variable names. - expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && - as_fn_error $? "invalid package name: $ac_useropt" - ac_useropt_orig=$ac_useropt - ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` - case $ac_user_opts in - *" -"with_$ac_useropt" -"*) ;; - *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--without-$ac_useropt_orig" - ac_unrecognized_sep=', ';; - esac - eval with_$ac_useropt=no ;; - - --x) - # Obsolete; use --with-x. - with_x=yes ;; - - -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \ - | --x-incl | --x-inc | --x-in | --x-i) - ac_prev=x_includes ;; - -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \ - | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*) - x_includes=$ac_optarg ;; - - -x-libraries | --x-libraries | --x-librarie | --x-librari \ - | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l) - ac_prev=x_libraries ;; - -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \ - | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) - x_libraries=$ac_optarg ;; - - -*) as_fn_error $? "unrecognized option: \`$ac_option' -Try \`$0 --help' for more information" - ;; - - *=*) - ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='` - # Reject names that are not valid shell variable names. - case $ac_envvar in #( - '' | [0-9]* | *[!_$as_cr_alnum]* ) - as_fn_error $? "invalid variable name: \`$ac_envvar'" ;; - esac - eval $ac_envvar=\$ac_optarg - export $ac_envvar ;; - - *) - # FIXME: should be removed in autoconf 3.0. - $as_echo "$as_me: WARNING: you should use --build, --host, --target" >&2 - expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null && - $as_echo "$as_me: WARNING: invalid host type: $ac_option" >&2 - : "${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}" - ;; - - esac -done - -if test -n "$ac_prev"; then - ac_option=--`echo $ac_prev | sed 's/_/-/g'` - as_fn_error $? "missing argument to $ac_option" -fi - -if test -n "$ac_unrecognized_opts"; then - case $enable_option_checking in - no) ;; - fatal) as_fn_error $? "unrecognized options: $ac_unrecognized_opts" ;; - *) $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2 ;; - esac -fi - -# Check all directory arguments for consistency. -for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \ - datadir sysconfdir sharedstatedir localstatedir includedir \ - oldincludedir docdir infodir htmldir dvidir pdfdir psdir \ - libdir localedir mandir -do - eval ac_val=\$$ac_var - # Remove trailing slashes. - case $ac_val in - */ ) - ac_val=`expr "X$ac_val" : 'X\(.*[^/]\)' \| "X$ac_val" : 'X\(.*\)'` - eval $ac_var=\$ac_val;; - esac - # Be sure to have absolute directory names. - case $ac_val in - [\\/$]* | ?:[\\/]* ) continue;; - NONE | '' ) case $ac_var in *prefix ) continue;; esac;; - esac - as_fn_error $? "expected an absolute directory name for --$ac_var: $ac_val" -done - -# There might be people who depend on the old broken behavior: `$host' -# used to hold the argument of --host etc. -# FIXME: To remove some day. -build=$build_alias -host=$host_alias -target=$target_alias - -# FIXME: To remove some day. -if test "x$host_alias" != x; then - if test "x$build_alias" = x; then - cross_compiling=maybe - elif test "x$build_alias" != "x$host_alias"; then - cross_compiling=yes - fi -fi - -ac_tool_prefix= -test -n "$host_alias" && ac_tool_prefix=$host_alias- - -test "$silent" = yes && exec 6>/dev/null - - -ac_pwd=`pwd` && test -n "$ac_pwd" && -ac_ls_di=`ls -di .` && -ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` || - as_fn_error $? "working directory cannot be determined" -test "X$ac_ls_di" = "X$ac_pwd_ls_di" || - as_fn_error $? "pwd does not report name of working directory" - - -# Find the source files, if location was not specified. -if test -z "$srcdir"; then - ac_srcdir_defaulted=yes - # Try the directory containing this script, then the parent directory. - ac_confdir=`$as_dirname -- "$as_myself" || -$as_expr X"$as_myself" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ - X"$as_myself" : 'X\(//\)[^/]' \| \ - X"$as_myself" : 'X\(//\)$' \| \ - X"$as_myself" : 'X\(/\)' \| . 2>/dev/null || -$as_echo X"$as_myself" | - sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ - s//\1/ - q - } - /^X\(\/\/\)[^/].*/{ - s//\1/ - q - } - /^X\(\/\/\)$/{ - s//\1/ - q - } - /^X\(\/\).*/{ - s//\1/ - q - } - s/.*/./; q'` - srcdir=$ac_confdir - if test ! -r "$srcdir/$ac_unique_file"; then - srcdir=.. - fi -else - ac_srcdir_defaulted=no -fi -if test ! -r "$srcdir/$ac_unique_file"; then - test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .." - as_fn_error $? "cannot find sources ($ac_unique_file) in $srcdir" -fi -ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work" -ac_abs_confdir=`( - cd "$srcdir" && test -r "./$ac_unique_file" || as_fn_error $? "$ac_msg" - pwd)` -# When building in place, set srcdir=. -if test "$ac_abs_confdir" = "$ac_pwd"; then - srcdir=. -fi -# Remove unnecessary trailing slashes from srcdir. -# Double slashes in file names in object file debugging info -# mess up M-x gdb in Emacs. -case $srcdir in -*/) srcdir=`expr "X$srcdir" : 'X\(.*[^/]\)' \| "X$srcdir" : 'X\(.*\)'`;; -esac -for ac_var in $ac_precious_vars; do - eval ac_env_${ac_var}_set=\${${ac_var}+set} - eval ac_env_${ac_var}_value=\$${ac_var} - eval ac_cv_env_${ac_var}_set=\${${ac_var}+set} - eval ac_cv_env_${ac_var}_value=\$${ac_var} -done - -# -# Report the --help message. -# -if test "$ac_init_help" = "long"; then - # Omit some internal or obsolete options to make the list less imposing. - # This message is too long to be a string in the A/UX 3.1 sh. - cat <<_ACEOF -\`configure' configures nghttp2 1.0.0 to adapt to many kinds of systems. - -Usage: $0 [OPTION]... [VAR=VALUE]... - -To assign environment variables (e.g., CC, CFLAGS...), specify them as -VAR=VALUE. See below for descriptions of some of the useful variables. - -Defaults for the options are specified in brackets. - -Configuration: - -h, --help display this help and exit - --help=short display options specific to this package - --help=recursive display the short help of all the included packages - -V, --version display version information and exit - -q, --quiet, --silent do not print \`checking ...' messages - --cache-file=FILE cache test results in FILE [disabled] - -C, --config-cache alias for \`--cache-file=config.cache' - -n, --no-create do not create output files - --srcdir=DIR find the sources in DIR [configure dir or \`..'] - -Installation directories: - --prefix=PREFIX install architecture-independent files in PREFIX - [$ac_default_prefix] - --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX - [PREFIX] - -By default, \`make install' will install all the files in -\`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc. You can specify -an installation prefix other than \`$ac_default_prefix' using \`--prefix', -for instance \`--prefix=\$HOME'. - -For better control, use the options below. - -Fine tuning of the installation directories: - --bindir=DIR user executables [EPREFIX/bin] - --sbindir=DIR system admin executables [EPREFIX/sbin] - --libexecdir=DIR program executables [EPREFIX/libexec] - --sysconfdir=DIR read-only single-machine data [PREFIX/etc] - --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] - --localstatedir=DIR modifiable single-machine data [PREFIX/var] - --libdir=DIR object code libraries [EPREFIX/lib] - --includedir=DIR C header files [PREFIX/include] - --oldincludedir=DIR C header files for non-gcc [/usr/include] - --datarootdir=DIR read-only arch.-independent data root [PREFIX/share] - --datadir=DIR read-only architecture-independent data [DATAROOTDIR] - --infodir=DIR info documentation [DATAROOTDIR/info] - --localedir=DIR locale-dependent data [DATAROOTDIR/locale] - --mandir=DIR man documentation [DATAROOTDIR/man] - --docdir=DIR documentation root [DATAROOTDIR/doc/nghttp2] - --htmldir=DIR html documentation [DOCDIR] - --dvidir=DIR dvi documentation [DOCDIR] - --pdfdir=DIR pdf documentation [DOCDIR] - --psdir=DIR ps documentation [DOCDIR] -_ACEOF - - cat <<\_ACEOF - -Program names: - --program-prefix=PREFIX prepend PREFIX to installed program names - --program-suffix=SUFFIX append SUFFIX to installed program names - --program-transform-name=PROGRAM run sed PROGRAM on installed program names - -System types: - --build=BUILD configure for building on BUILD [guessed] - --host=HOST cross-compile to build programs to run on HOST [BUILD] - --target=TARGET configure for building compilers for TARGET [HOST] -_ACEOF -fi - -if test -n "$ac_init_help"; then - case $ac_init_help in - short | recursive ) echo "Configuration of nghttp2 1.0.0:";; - esac - cat <<\_ACEOF - -Optional Features: - --disable-option-checking ignore unrecognized --enable/--with options - --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no) - --enable-FEATURE[=ARG] include FEATURE [ARG=yes] - --enable-shared[=PKGS] build shared libraries [default=yes] - --enable-static[=PKGS] build static libraries [default=yes] - --enable-fast-install[=PKGS] - optimize for fast installation [default=yes] - --disable-libtool-lock avoid locking (might break parallel builds) - --enable-dependency-tracking - do not reject slow dependency extractors - --disable-dependency-tracking - speeds up one-time build - --enable-silent-rules less verbose build output (undo: "make V=1") - --disable-silent-rules verbose build output (undo: "make V=0") - --enable-werror Turn on compile time warnings - --enable-debug Turn on debug output - --disable-threads Turn off threading in apps - --enable-app Build applications (nghttp, nghttpd and nghttpx) - [default=check] - --enable-hpack-tools Build HPACK tools [default=check] - --enable-asio-lib Build C++ libnghttp2_asio library [default=no] - --enable-examples Build examples [default=check] - --enable-python-bindings - Build Python bindings [default=check] - --disable-failmalloc Do not build failmalloc test program - --disable-xmltest Do not try to compile and run a test LIBXML program - --disable-assert turn off assertions - --disable-largefile omit support for large files - -Optional Packages: - --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] - --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no) - --with-pic[=PKGS] try to use only PIC/non-PIC objects [default=use - both] - --with-gnu-ld assume the C compiler uses GNU ld [default=no] - --with-sysroot=DIR Search for dependent libraries within DIR - (or the compiler's sysroot if not specified). - --with-libxml2 Use libxml2 [default=check] - --with-jemalloc Use jemalloc [default=check] - --with-spdylay Use spdylay [default=check] - --with-cython=PATH Use cython in given PATH - --with-xml-prefix=PFX Prefix where libxml is installed (optional) - --with-xml-exec-prefix=PFX Exec prefix where libxml is installed (optional) - --with-boost[=ARG] use Boost library from a standard location - (ARG=yes), from the specified location (ARG=), - or disable it (ARG=no) [ARG=yes] - --with-boost-libdir=LIB_DIR - Force given directory for boost libraries. Note that - this will override library path detection, so use - this parameter only if default library detection - fails and you know exactly where your boost - libraries are located. - --with-boost-asio[=special-lib] - use the ASIO library from boost - it is possible to - specify a certain library for the linker e.g. - --with-boost-asio=boost_system-gcc41-mt-1_34 - --with-boost-system[=special-lib] - use the System library from boost - it is possible - to specify a certain library for the linker e.g. - --with-boost-system=boost_system-gcc-mt - --with-boost-thread[=special-lib] - use the Thread library from boost - it is possible - to specify a certain library for the linker e.g. - --with-boost-thread=boost_thread-gcc-mt - -Some influential environment variables: - CC C compiler command - CFLAGS C compiler flags - LDFLAGS linker flags, e.g. -L if you have libraries in a - nonstandard directory - LIBS libraries to pass to the linker, e.g. -l - CPPFLAGS (Objective) C/C++ preprocessor flags, e.g. -I if - you have headers in a nonstandard directory - CPP C preprocessor - CYTHON the Cython executable - CXX C++ compiler command - CXXFLAGS C++ compiler flags - CXXCPP C++ preprocessor - PKG_CONFIG path to pkg-config utility - PKG_CONFIG_PATH - directories to add to pkg-config's search path - PKG_CONFIG_LIBDIR - path overriding pkg-config's built-in search path - PYTHON the Python interpreter - PYTHON_VERSION - The installed Python version to use, for example '2.3'. This - string will be appended to the Python interpreter canonical - name. - ZLIB_CFLAGS C compiler flags for ZLIB, overriding pkg-config - ZLIB_LIBS linker flags for ZLIB, overriding pkg-config - CUNIT_CFLAGS - C compiler flags for CUNIT, overriding pkg-config - CUNIT_LIBS linker flags for CUNIT, overriding pkg-config - OPENSSL_CFLAGS - C compiler flags for OPENSSL, overriding pkg-config - OPENSSL_LIBS - linker flags for OPENSSL, overriding pkg-config - LIBEVENT_OPENSSL_CFLAGS - C compiler flags for LIBEVENT_OPENSSL, overriding pkg-config - LIBEVENT_OPENSSL_LIBS - linker flags for LIBEVENT_OPENSSL, overriding pkg-config - JANSSON_CFLAGS - C compiler flags for JANSSON, overriding pkg-config - JANSSON_LIBS - linker flags for JANSSON, overriding pkg-config - LIBSPDYLAY_CFLAGS - C compiler flags for LIBSPDYLAY, overriding pkg-config - LIBSPDYLAY_LIBS - linker flags for LIBSPDYLAY, overriding pkg-config - -Use these variables to override the choices made by `configure' or to help -it to find libraries and programs with nonstandard names/locations. - -Report bugs to . -_ACEOF -ac_status=$? -fi - -if test "$ac_init_help" = "recursive"; then - # If there are subdirs, report their specific --help. - for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue - test -d "$ac_dir" || - { cd "$srcdir" && ac_pwd=`pwd` && srcdir=. && test -d "$ac_dir"; } || - continue - ac_builddir=. - -case "$ac_dir" in -.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; -*) - ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` - # A ".." for each directory in $ac_dir_suffix. - ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` - case $ac_top_builddir_sub in - "") ac_top_builddir_sub=. ac_top_build_prefix= ;; - *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; - esac ;; -esac -ac_abs_top_builddir=$ac_pwd -ac_abs_builddir=$ac_pwd$ac_dir_suffix -# for backward compatibility: -ac_top_builddir=$ac_top_build_prefix - -case $srcdir in - .) # We are building in place. - ac_srcdir=. - ac_top_srcdir=$ac_top_builddir_sub - ac_abs_top_srcdir=$ac_pwd ;; - [\\/]* | ?:[\\/]* ) # Absolute name. - ac_srcdir=$srcdir$ac_dir_suffix; - ac_top_srcdir=$srcdir - ac_abs_top_srcdir=$srcdir ;; - *) # Relative name. - ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix - ac_top_srcdir=$ac_top_build_prefix$srcdir - ac_abs_top_srcdir=$ac_pwd/$srcdir ;; -esac -ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix - - cd "$ac_dir" || { ac_status=$?; continue; } - # Check for guested configure. - if test -f "$ac_srcdir/configure.gnu"; then - echo && - $SHELL "$ac_srcdir/configure.gnu" --help=recursive - elif test -f "$ac_srcdir/configure"; then - echo && - $SHELL "$ac_srcdir/configure" --help=recursive - else - $as_echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2 - fi || ac_status=$? - cd "$ac_pwd" || { ac_status=$?; break; } - done -fi - -test -n "$ac_init_help" && exit $ac_status -if $ac_init_version; then - cat <<\_ACEOF -nghttp2 configure 1.0.0 -generated by GNU Autoconf 2.69 - -Copyright (C) 2012 Free Software Foundation, Inc. -This configure script is free software; the Free Software Foundation -gives unlimited permission to copy, distribute and modify it. -_ACEOF - exit -fi - -## ------------------------ ## -## Autoconf initialization. ## -## ------------------------ ## - -# ac_fn_c_try_compile LINENO -# -------------------------- -# Try to compile conftest.$ac_ext, and return whether this succeeded. -ac_fn_c_try_compile () -{ - as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack - rm -f conftest.$ac_objext - if { { ac_try="$ac_compile" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" -$as_echo "$ac_try_echo"; } >&5 - (eval "$ac_compile") 2>conftest.err - ac_status=$? - if test -s conftest.err; then - grep -v '^ *+' conftest.err >conftest.er1 - cat conftest.er1 >&5 - mv -f conftest.er1 conftest.err - fi - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; } && { - test -z "$ac_c_werror_flag" || - test ! -s conftest.err - } && test -s conftest.$ac_objext; then : - ac_retval=0 -else - $as_echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - - ac_retval=1 -fi - eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno - as_fn_set_status $ac_retval - -} # ac_fn_c_try_compile - -# ac_fn_c_try_cpp LINENO -# ---------------------- -# Try to preprocess conftest.$ac_ext, and return whether this succeeded. -ac_fn_c_try_cpp () -{ - as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack - if { { ac_try="$ac_cpp conftest.$ac_ext" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" -$as_echo "$ac_try_echo"; } >&5 - (eval "$ac_cpp conftest.$ac_ext") 2>conftest.err - ac_status=$? - if test -s conftest.err; then - grep -v '^ *+' conftest.err >conftest.er1 - cat conftest.er1 >&5 - mv -f conftest.er1 conftest.err - fi - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; } > conftest.i && { - test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || - test ! -s conftest.err - }; then : - ac_retval=0 -else - $as_echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - - ac_retval=1 -fi - eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno - as_fn_set_status $ac_retval - -} # ac_fn_c_try_cpp - -# ac_fn_c_check_header_mongrel LINENO HEADER VAR INCLUDES -# ------------------------------------------------------- -# Tests whether HEADER exists, giving a warning if it cannot be compiled using -# the include files in INCLUDES and setting the cache variable VAR -# accordingly. -ac_fn_c_check_header_mongrel () -{ - as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack - if eval \${$3+:} false; then : - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 -$as_echo_n "checking for $2... " >&6; } -if eval \${$3+:} false; then : - $as_echo_n "(cached) " >&6 -fi -eval ac_res=\$$3 - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 -$as_echo "$ac_res" >&6; } -else - # Is the header compilable? -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 usability" >&5 -$as_echo_n "checking $2 usability... " >&6; } -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -$4 -#include <$2> -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - ac_header_compiler=yes -else - ac_header_compiler=no -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_compiler" >&5 -$as_echo "$ac_header_compiler" >&6; } - -# Is the header present? -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 presence" >&5 -$as_echo_n "checking $2 presence... " >&6; } -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#include <$2> -_ACEOF -if ac_fn_c_try_cpp "$LINENO"; then : - ac_header_preproc=yes -else - ac_header_preproc=no -fi -rm -f conftest.err conftest.i conftest.$ac_ext -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_preproc" >&5 -$as_echo "$ac_header_preproc" >&6; } - -# So? What about this header? -case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in #(( - yes:no: ) - { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&5 -$as_echo "$as_me: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&2;} - { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5 -$as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;} - ;; - no:yes:* ) - { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: present but cannot be compiled" >&5 -$as_echo "$as_me: WARNING: $2: present but cannot be compiled" >&2;} - { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: check for missing prerequisite headers?" >&5 -$as_echo "$as_me: WARNING: $2: check for missing prerequisite headers?" >&2;} - { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: see the Autoconf documentation" >&5 -$as_echo "$as_me: WARNING: $2: see the Autoconf documentation" >&2;} - { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&5 -$as_echo "$as_me: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&2;} - { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5 -$as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;} -( $as_echo "## ----------------------------------------------- ## -## Report this to t-tujikawa@users.sourceforge.net ## -## ----------------------------------------------- ##" - ) | sed "s/^/$as_me: WARNING: /" >&2 - ;; -esac - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 -$as_echo_n "checking for $2... " >&6; } -if eval \${$3+:} false; then : - $as_echo_n "(cached) " >&6 -else - eval "$3=\$ac_header_compiler" -fi -eval ac_res=\$$3 - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 -$as_echo "$ac_res" >&6; } -fi - eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno - -} # ac_fn_c_check_header_mongrel - -# ac_fn_c_try_run LINENO -# ---------------------- -# Try to link conftest.$ac_ext, and return whether this succeeded. Assumes -# that executables *can* be run. -ac_fn_c_try_run () -{ - as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack - if { { ac_try="$ac_link" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" -$as_echo "$ac_try_echo"; } >&5 - (eval "$ac_link") 2>&5 - ac_status=$? - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; } && { ac_try='./conftest$ac_exeext' - { { case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" -$as_echo "$ac_try_echo"; } >&5 - (eval "$ac_try") 2>&5 - ac_status=$? - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; }; }; then : - ac_retval=0 -else - $as_echo "$as_me: program exited with status $ac_status" >&5 - $as_echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - - ac_retval=$ac_status -fi - rm -rf conftest.dSYM conftest_ipa8_conftest.oo - eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno - as_fn_set_status $ac_retval - -} # ac_fn_c_try_run - -# ac_fn_c_check_header_compile LINENO HEADER VAR INCLUDES -# ------------------------------------------------------- -# Tests whether HEADER exists and can be compiled using the include files in -# INCLUDES, setting the cache variable VAR accordingly. -ac_fn_c_check_header_compile () -{ - as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 -$as_echo_n "checking for $2... " >&6; } -if eval \${$3+:} false; then : - $as_echo_n "(cached) " >&6 -else - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -$4 -#include <$2> -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - eval "$3=yes" -else - eval "$3=no" -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -fi -eval ac_res=\$$3 - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 -$as_echo "$ac_res" >&6; } - eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno - -} # ac_fn_c_check_header_compile - -# ac_fn_c_try_link LINENO -# ----------------------- -# Try to link conftest.$ac_ext, and return whether this succeeded. -ac_fn_c_try_link () -{ - as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack - rm -f conftest.$ac_objext conftest$ac_exeext - if { { ac_try="$ac_link" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" -$as_echo "$ac_try_echo"; } >&5 - (eval "$ac_link") 2>conftest.err - ac_status=$? - if test -s conftest.err; then - grep -v '^ *+' conftest.err >conftest.er1 - cat conftest.er1 >&5 - mv -f conftest.er1 conftest.err - fi - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; } && { - test -z "$ac_c_werror_flag" || - test ! -s conftest.err - } && test -s conftest$ac_exeext && { - test "$cross_compiling" = yes || - test -x conftest$ac_exeext - }; then : - ac_retval=0 -else - $as_echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - - ac_retval=1 -fi - # Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information - # created by the PGI compiler (conftest_ipa8_conftest.oo), as it would - # interfere with the next link command; also delete a directory that is - # left behind by Apple's compiler. We do this before executing the actions. - rm -rf conftest.dSYM conftest_ipa8_conftest.oo - eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno - as_fn_set_status $ac_retval - -} # ac_fn_c_try_link - -# ac_fn_c_check_func LINENO FUNC VAR -# ---------------------------------- -# Tests whether FUNC exists, setting the cache variable VAR accordingly -ac_fn_c_check_func () -{ - as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 -$as_echo_n "checking for $2... " >&6; } -if eval \${$3+:} false; then : - $as_echo_n "(cached) " >&6 -else - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -/* Define $2 to an innocuous variant, in case declares $2. - For example, HP-UX 11i declares gettimeofday. */ -#define $2 innocuous_$2 - -/* System header to define __stub macros and hopefully few prototypes, - which can conflict with char $2 (); below. - Prefer to if __STDC__ is defined, since - exists even on freestanding compilers. */ - -#ifdef __STDC__ -# include -#else -# include -#endif - -#undef $2 - -/* Override any GCC internal prototype to avoid an error. - Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -#ifdef __cplusplus -extern "C" -#endif -char $2 (); -/* The GNU C library defines this for functions which it implements - to always fail with ENOSYS. Some functions are actually named - something starting with __ and the normal name is an alias. */ -#if defined __stub_$2 || defined __stub___$2 -choke me -#endif - -int -main () -{ -return $2 (); - ; - return 0; -} -_ACEOF -if ac_fn_c_try_link "$LINENO"; then : - eval "$3=yes" -else - eval "$3=no" -fi -rm -f core conftest.err conftest.$ac_objext \ - conftest$ac_exeext conftest.$ac_ext -fi -eval ac_res=\$$3 - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 -$as_echo "$ac_res" >&6; } - eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno - -} # ac_fn_c_check_func - -# ac_fn_cxx_try_compile LINENO -# ---------------------------- -# Try to compile conftest.$ac_ext, and return whether this succeeded. -ac_fn_cxx_try_compile () -{ - as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack - rm -f conftest.$ac_objext - if { { ac_try="$ac_compile" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" -$as_echo "$ac_try_echo"; } >&5 - (eval "$ac_compile") 2>conftest.err - ac_status=$? - if test -s conftest.err; then - grep -v '^ *+' conftest.err >conftest.er1 - cat conftest.er1 >&5 - mv -f conftest.er1 conftest.err - fi - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; } && { - test -z "$ac_cxx_werror_flag" || - test ! -s conftest.err - } && test -s conftest.$ac_objext; then : - ac_retval=0 -else - $as_echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - - ac_retval=1 -fi - eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno - as_fn_set_status $ac_retval - -} # ac_fn_cxx_try_compile - -# ac_fn_cxx_try_cpp LINENO -# ------------------------ -# Try to preprocess conftest.$ac_ext, and return whether this succeeded. -ac_fn_cxx_try_cpp () -{ - as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack - if { { ac_try="$ac_cpp conftest.$ac_ext" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" -$as_echo "$ac_try_echo"; } >&5 - (eval "$ac_cpp conftest.$ac_ext") 2>conftest.err - ac_status=$? - if test -s conftest.err; then - grep -v '^ *+' conftest.err >conftest.er1 - cat conftest.er1 >&5 - mv -f conftest.er1 conftest.err - fi - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; } > conftest.i && { - test -z "$ac_cxx_preproc_warn_flag$ac_cxx_werror_flag" || - test ! -s conftest.err - }; then : - ac_retval=0 -else - $as_echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - - ac_retval=1 -fi - eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno - as_fn_set_status $ac_retval - -} # ac_fn_cxx_try_cpp - -# ac_fn_cxx_try_link LINENO -# ------------------------- -# Try to link conftest.$ac_ext, and return whether this succeeded. -ac_fn_cxx_try_link () -{ - as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack - rm -f conftest.$ac_objext conftest$ac_exeext - if { { ac_try="$ac_link" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" -$as_echo "$ac_try_echo"; } >&5 - (eval "$ac_link") 2>conftest.err - ac_status=$? - if test -s conftest.err; then - grep -v '^ *+' conftest.err >conftest.er1 - cat conftest.er1 >&5 - mv -f conftest.er1 conftest.err - fi - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; } && { - test -z "$ac_cxx_werror_flag" || - test ! -s conftest.err - } && test -s conftest$ac_exeext && { - test "$cross_compiling" = yes || - test -x conftest$ac_exeext - }; then : - ac_retval=0 -else - $as_echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - - ac_retval=1 -fi - # Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information - # created by the PGI compiler (conftest_ipa8_conftest.oo), as it would - # interfere with the next link command; also delete a directory that is - # left behind by Apple's compiler. We do this before executing the actions. - rm -rf conftest.dSYM conftest_ipa8_conftest.oo - eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno - as_fn_set_status $ac_retval - -} # ac_fn_cxx_try_link - -# ac_fn_c_check_type LINENO TYPE VAR INCLUDES -# ------------------------------------------- -# Tests whether TYPE exists after having included INCLUDES, setting cache -# variable VAR accordingly. -ac_fn_c_check_type () -{ - as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 -$as_echo_n "checking for $2... " >&6; } -if eval \${$3+:} false; then : - $as_echo_n "(cached) " >&6 -else - eval "$3=no" - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -$4 -int -main () -{ -if (sizeof ($2)) - return 0; - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -$4 -int -main () -{ -if (sizeof (($2))) - return 0; - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - -else - eval "$3=yes" -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -fi -eval ac_res=\$$3 - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 -$as_echo "$ac_res" >&6; } - eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno - -} # ac_fn_c_check_type - -# ac_fn_c_find_uintX_t LINENO BITS VAR -# ------------------------------------ -# Finds an unsigned integer type with width BITS, setting cache variable VAR -# accordingly. -ac_fn_c_find_uintX_t () -{ - as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for uint$2_t" >&5 -$as_echo_n "checking for uint$2_t... " >&6; } -if eval \${$3+:} false; then : - $as_echo_n "(cached) " >&6 -else - eval "$3=no" - # Order is important - never check a type that is potentially smaller - # than half of the expected target width. - for ac_type in uint$2_t 'unsigned int' 'unsigned long int' \ - 'unsigned long long int' 'unsigned short int' 'unsigned char'; do - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -$ac_includes_default -int -main () -{ -static int test_array [1 - 2 * !((($ac_type) -1 >> ($2 / 2 - 1)) >> ($2 / 2 - 1) == 3)]; -test_array [0] = 0; -return test_array [0]; - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - case $ac_type in #( - uint$2_t) : - eval "$3=yes" ;; #( - *) : - eval "$3=\$ac_type" ;; -esac -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext - if eval test \"x\$"$3"\" = x"no"; then : - -else - break -fi - done -fi -eval ac_res=\$$3 - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 -$as_echo "$ac_res" >&6; } - eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno - -} # ac_fn_c_find_uintX_t - -# ac_fn_c_find_intX_t LINENO BITS VAR -# ----------------------------------- -# Finds a signed integer type with width BITS, setting cache variable VAR -# accordingly. -ac_fn_c_find_intX_t () -{ - as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for int$2_t" >&5 -$as_echo_n "checking for int$2_t... " >&6; } -if eval \${$3+:} false; then : - $as_echo_n "(cached) " >&6 -else - eval "$3=no" - # Order is important - never check a type that is potentially smaller - # than half of the expected target width. - for ac_type in int$2_t 'int' 'long int' \ - 'long long int' 'short int' 'signed char'; do - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -$ac_includes_default - enum { N = $2 / 2 - 1 }; -int -main () -{ -static int test_array [1 - 2 * !(0 < ($ac_type) ((((($ac_type) 1 << N) << N) - 1) * 2 + 1))]; -test_array [0] = 0; -return test_array [0]; - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -$ac_includes_default - enum { N = $2 / 2 - 1 }; -int -main () -{ -static int test_array [1 - 2 * !(($ac_type) ((((($ac_type) 1 << N) << N) - 1) * 2 + 1) - < ($ac_type) ((((($ac_type) 1 << N) << N) - 1) * 2 + 2))]; -test_array [0] = 0; -return test_array [0]; - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - -else - case $ac_type in #( - int$2_t) : - eval "$3=yes" ;; #( - *) : - eval "$3=\$ac_type" ;; -esac -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext - if eval test \"x\$"$3"\" = x"no"; then : - -else - break -fi - done -fi -eval ac_res=\$$3 - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 -$as_echo "$ac_res" >&6; } - eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno - -} # ac_fn_c_find_intX_t - -# ac_fn_c_check_member LINENO AGGR MEMBER VAR INCLUDES -# ---------------------------------------------------- -# Tries to find if the field MEMBER exists in type AGGR, after including -# INCLUDES, setting cache variable VAR accordingly. -ac_fn_c_check_member () -{ - as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2.$3" >&5 -$as_echo_n "checking for $2.$3... " >&6; } -if eval \${$4+:} false; then : - $as_echo_n "(cached) " >&6 -else - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -$5 -int -main () -{ -static $2 ac_aggr; -if (ac_aggr.$3) -return 0; - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - eval "$4=yes" -else - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -$5 -int -main () -{ -static $2 ac_aggr; -if (sizeof ac_aggr.$3) -return 0; - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - eval "$4=yes" -else - eval "$4=no" -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -fi -eval ac_res=\$$4 - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 -$as_echo "$ac_res" >&6; } - eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno - -} # ac_fn_c_check_member - -# ac_fn_c_compute_int LINENO EXPR VAR INCLUDES -# -------------------------------------------- -# Tries to find the compile-time value of EXPR in a program that includes -# INCLUDES, setting VAR accordingly. Returns whether the value could be -# computed -ac_fn_c_compute_int () -{ - as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack - if test "$cross_compiling" = yes; then - # Depending upon the size, compute the lo and hi bounds. -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -$4 -int -main () -{ -static int test_array [1 - 2 * !(($2) >= 0)]; -test_array [0] = 0; -return test_array [0]; - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - ac_lo=0 ac_mid=0 - while :; do - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -$4 -int -main () -{ -static int test_array [1 - 2 * !(($2) <= $ac_mid)]; -test_array [0] = 0; -return test_array [0]; - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - ac_hi=$ac_mid; break -else - as_fn_arith $ac_mid + 1 && ac_lo=$as_val - if test $ac_lo -le $ac_mid; then - ac_lo= ac_hi= - break - fi - as_fn_arith 2 '*' $ac_mid + 1 && ac_mid=$as_val -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext - done -else - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -$4 -int -main () -{ -static int test_array [1 - 2 * !(($2) < 0)]; -test_array [0] = 0; -return test_array [0]; - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - ac_hi=-1 ac_mid=-1 - while :; do - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -$4 -int -main () -{ -static int test_array [1 - 2 * !(($2) >= $ac_mid)]; -test_array [0] = 0; -return test_array [0]; - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - ac_lo=$ac_mid; break -else - as_fn_arith '(' $ac_mid ')' - 1 && ac_hi=$as_val - if test $ac_mid -le $ac_hi; then - ac_lo= ac_hi= - break - fi - as_fn_arith 2 '*' $ac_mid && ac_mid=$as_val -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext - done -else - ac_lo= ac_hi= -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -# Binary search between lo and hi bounds. -while test "x$ac_lo" != "x$ac_hi"; do - as_fn_arith '(' $ac_hi - $ac_lo ')' / 2 + $ac_lo && ac_mid=$as_val - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -$4 -int -main () -{ -static int test_array [1 - 2 * !(($2) <= $ac_mid)]; -test_array [0] = 0; -return test_array [0]; - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - ac_hi=$ac_mid -else - as_fn_arith '(' $ac_mid ')' + 1 && ac_lo=$as_val -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -done -case $ac_lo in #(( -?*) eval "$3=\$ac_lo"; ac_retval=0 ;; -'') ac_retval=1 ;; -esac - else - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -$4 -static long int longval () { return $2; } -static unsigned long int ulongval () { return $2; } -#include -#include -int -main () -{ - - FILE *f = fopen ("conftest.val", "w"); - if (! f) - return 1; - if (($2) < 0) - { - long int i = longval (); - if (i != ($2)) - return 1; - fprintf (f, "%ld", i); - } - else - { - unsigned long int i = ulongval (); - if (i != ($2)) - return 1; - fprintf (f, "%lu", i); - } - /* Do not output a trailing newline, as this causes \r\n confusion - on some platforms. */ - return ferror (f) || fclose (f) != 0; - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_run "$LINENO"; then : - echo >>conftest.val; read $3 &5 -$as_echo_n "checking whether $as_decl_name is declared... " >&6; } -if eval \${$3+:} false; then : - $as_echo_n "(cached) " >&6 -else - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -$4 -int -main () -{ -#ifndef $as_decl_name -#ifdef __cplusplus - (void) $as_decl_use; -#else - (void) $as_decl_name; -#endif -#endif - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - eval "$3=yes" -else - eval "$3=no" -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -fi -eval ac_res=\$$3 - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 -$as_echo "$ac_res" >&6; } - eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno - -} # ac_fn_c_check_decl -cat >config.log <<_ACEOF -This file contains any messages produced by compilers while -running configure, to aid debugging if configure makes a mistake. - -It was created by nghttp2 $as_me 1.0.0, which was -generated by GNU Autoconf 2.69. Invocation command line was - - $ $0 $@ - -_ACEOF -exec 5>>config.log -{ -cat <<_ASUNAME -## --------- ## -## Platform. ## -## --------- ## - -hostname = `(hostname || uname -n) 2>/dev/null | sed 1q` -uname -m = `(uname -m) 2>/dev/null || echo unknown` -uname -r = `(uname -r) 2>/dev/null || echo unknown` -uname -s = `(uname -s) 2>/dev/null || echo unknown` -uname -v = `(uname -v) 2>/dev/null || echo unknown` - -/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown` -/bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown` - -/bin/arch = `(/bin/arch) 2>/dev/null || echo unknown` -/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown` -/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown` -/usr/bin/hostinfo = `(/usr/bin/hostinfo) 2>/dev/null || echo unknown` -/bin/machine = `(/bin/machine) 2>/dev/null || echo unknown` -/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown` -/bin/universe = `(/bin/universe) 2>/dev/null || echo unknown` - -_ASUNAME - -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - $as_echo "PATH: $as_dir" - done -IFS=$as_save_IFS - -} >&5 - -cat >&5 <<_ACEOF - - -## ----------- ## -## Core tests. ## -## ----------- ## - -_ACEOF - - -# Keep a trace of the command line. -# Strip out --no-create and --no-recursion so they do not pile up. -# Strip out --silent because we don't want to record it for future runs. -# Also quote any args containing shell meta-characters. -# Make two passes to allow for proper duplicate-argument suppression. -ac_configure_args= -ac_configure_args0= -ac_configure_args1= -ac_must_keep_next=false -for ac_pass in 1 2 -do - for ac_arg - do - case $ac_arg in - -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;; - -q | -quiet | --quiet | --quie | --qui | --qu | --q \ - | -silent | --silent | --silen | --sile | --sil) - continue ;; - *\'*) - ac_arg=`$as_echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; - esac - case $ac_pass in - 1) as_fn_append ac_configure_args0 " '$ac_arg'" ;; - 2) - as_fn_append ac_configure_args1 " '$ac_arg'" - if test $ac_must_keep_next = true; then - ac_must_keep_next=false # Got value, back to normal. - else - case $ac_arg in - *=* | --config-cache | -C | -disable-* | --disable-* \ - | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \ - | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \ - | -with-* | --with-* | -without-* | --without-* | --x) - case "$ac_configure_args0 " in - "$ac_configure_args1"*" '$ac_arg' "* ) continue ;; - esac - ;; - -* ) ac_must_keep_next=true ;; - esac - fi - as_fn_append ac_configure_args " '$ac_arg'" - ;; - esac - done -done -{ ac_configure_args0=; unset ac_configure_args0;} -{ ac_configure_args1=; unset ac_configure_args1;} - -# When interrupted or exit'd, cleanup temporary files, and complete -# config.log. We remove comments because anyway the quotes in there -# would cause problems or look ugly. -# WARNING: Use '\'' to represent an apostrophe within the trap. -# WARNING: Do not start the trap code with a newline, due to a FreeBSD 4.0 bug. -trap 'exit_status=$? - # Save into config.log some information that might help in debugging. - { - echo - - $as_echo "## ---------------- ## -## Cache variables. ## -## ---------------- ##" - echo - # The following way of writing the cache mishandles newlines in values, -( - for ac_var in `(set) 2>&1 | sed -n '\''s/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'\''`; do - eval ac_val=\$$ac_var - case $ac_val in #( - *${as_nl}*) - case $ac_var in #( - *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 -$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; - esac - case $ac_var in #( - _ | IFS | as_nl) ;; #( - BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( - *) { eval $ac_var=; unset $ac_var;} ;; - esac ;; - esac - done - (set) 2>&1 | - case $as_nl`(ac_space='\'' '\''; set) 2>&1` in #( - *${as_nl}ac_space=\ *) - sed -n \ - "s/'\''/'\''\\\\'\'''\''/g; - s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\''\\2'\''/p" - ;; #( - *) - sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" - ;; - esac | - sort -) - echo - - $as_echo "## ----------------- ## -## Output variables. ## -## ----------------- ##" - echo - for ac_var in $ac_subst_vars - do - eval ac_val=\$$ac_var - case $ac_val in - *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; - esac - $as_echo "$ac_var='\''$ac_val'\''" - done | sort - echo - - if test -n "$ac_subst_files"; then - $as_echo "## ------------------- ## -## File substitutions. ## -## ------------------- ##" - echo - for ac_var in $ac_subst_files - do - eval ac_val=\$$ac_var - case $ac_val in - *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; - esac - $as_echo "$ac_var='\''$ac_val'\''" - done | sort - echo - fi - - if test -s confdefs.h; then - $as_echo "## ----------- ## -## confdefs.h. ## -## ----------- ##" - echo - cat confdefs.h - echo - fi - test "$ac_signal" != 0 && - $as_echo "$as_me: caught signal $ac_signal" - $as_echo "$as_me: exit $exit_status" - } >&5 - rm -f core *.core core.conftest.* && - rm -f -r conftest* confdefs* conf$$* $ac_clean_files && - exit $exit_status -' 0 -for ac_signal in 1 2 13 15; do - trap 'ac_signal='$ac_signal'; as_fn_exit 1' $ac_signal -done -ac_signal=0 - -# confdefs.h avoids OS command line length limits that DEFS can exceed. -rm -f -r conftest* confdefs.h - -$as_echo "/* confdefs.h */" > confdefs.h - -# Predefined preprocessor variables. - -cat >>confdefs.h <<_ACEOF -#define PACKAGE_NAME "$PACKAGE_NAME" -_ACEOF - -cat >>confdefs.h <<_ACEOF -#define PACKAGE_TARNAME "$PACKAGE_TARNAME" -_ACEOF - -cat >>confdefs.h <<_ACEOF -#define PACKAGE_VERSION "$PACKAGE_VERSION" -_ACEOF - -cat >>confdefs.h <<_ACEOF -#define PACKAGE_STRING "$PACKAGE_STRING" -_ACEOF - -cat >>confdefs.h <<_ACEOF -#define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT" -_ACEOF - -cat >>confdefs.h <<_ACEOF -#define PACKAGE_URL "$PACKAGE_URL" -_ACEOF - - -# Let the site file select an alternate cache file if it wants to. -# Prefer an explicitly selected file to automatically selected ones. -ac_site_file1=NONE -ac_site_file2=NONE -if test -n "$CONFIG_SITE"; then - # We do not want a PATH search for config.site. - case $CONFIG_SITE in #(( - -*) ac_site_file1=./$CONFIG_SITE;; - */*) ac_site_file1=$CONFIG_SITE;; - *) ac_site_file1=./$CONFIG_SITE;; - esac -elif test "x$prefix" != xNONE; then - ac_site_file1=$prefix/share/config.site - ac_site_file2=$prefix/etc/config.site -else - ac_site_file1=$ac_default_prefix/share/config.site - ac_site_file2=$ac_default_prefix/etc/config.site -fi -for ac_site_file in "$ac_site_file1" "$ac_site_file2" -do - test "x$ac_site_file" = xNONE && continue - if test /dev/null != "$ac_site_file" && test -r "$ac_site_file"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: loading site script $ac_site_file" >&5 -$as_echo "$as_me: loading site script $ac_site_file" >&6;} - sed 's/^/| /' "$ac_site_file" >&5 - . "$ac_site_file" \ - || { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error $? "failed to load site script $ac_site_file -See \`config.log' for more details" "$LINENO" 5; } - fi -done - -if test -r "$cache_file"; then - # Some versions of bash will fail to source /dev/null (special files - # actually), so we avoid doing that. DJGPP emulates it as a regular file. - if test /dev/null != "$cache_file" && test -f "$cache_file"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: loading cache $cache_file" >&5 -$as_echo "$as_me: loading cache $cache_file" >&6;} - case $cache_file in - [\\/]* | ?:[\\/]* ) . "$cache_file";; - *) . "./$cache_file";; - esac - fi -else - { $as_echo "$as_me:${as_lineno-$LINENO}: creating cache $cache_file" >&5 -$as_echo "$as_me: creating cache $cache_file" >&6;} - >$cache_file -fi - -# Check that the precious variables saved in the cache have kept the same -# value. -ac_cache_corrupted=false -for ac_var in $ac_precious_vars; do - eval ac_old_set=\$ac_cv_env_${ac_var}_set - eval ac_new_set=\$ac_env_${ac_var}_set - eval ac_old_val=\$ac_cv_env_${ac_var}_value - eval ac_new_val=\$ac_env_${ac_var}_value - case $ac_old_set,$ac_new_set in - set,) - { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5 -$as_echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;} - ac_cache_corrupted=: ;; - ,set) - { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was not set in the previous run" >&5 -$as_echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;} - ac_cache_corrupted=: ;; - ,);; - *) - if test "x$ac_old_val" != "x$ac_new_val"; then - # differences in whitespace do not lead to failure. - ac_old_val_w=`echo x $ac_old_val` - ac_new_val_w=`echo x $ac_new_val` - if test "$ac_old_val_w" != "$ac_new_val_w"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' has changed since the previous run:" >&5 -$as_echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;} - ac_cache_corrupted=: - else - { $as_echo "$as_me:${as_lineno-$LINENO}: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&5 -$as_echo "$as_me: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&2;} - eval $ac_var=\$ac_old_val - fi - { $as_echo "$as_me:${as_lineno-$LINENO}: former value: \`$ac_old_val'" >&5 -$as_echo "$as_me: former value: \`$ac_old_val'" >&2;} - { $as_echo "$as_me:${as_lineno-$LINENO}: current value: \`$ac_new_val'" >&5 -$as_echo "$as_me: current value: \`$ac_new_val'" >&2;} - fi;; - esac - # Pass precious variables to config.status. - if test "$ac_new_set" = set; then - case $ac_new_val in - *\'*) ac_arg=$ac_var=`$as_echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;; - *) ac_arg=$ac_var=$ac_new_val ;; - esac - case " $ac_configure_args " in - *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy. - *) as_fn_append ac_configure_args " '$ac_arg'" ;; - esac - fi -done -if $ac_cache_corrupted; then - { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} - { $as_echo "$as_me:${as_lineno-$LINENO}: error: changes in the environment can compromise the build" >&5 -$as_echo "$as_me: error: changes in the environment can compromise the build" >&2;} - as_fn_error $? "run \`make distclean' and/or \`rm $cache_file' and start over" "$LINENO" 5 -fi -## -------------------- ## -## Main body of script. ## -## -------------------- ## - -ac_ext=c -ac_cpp='$CPP $CPPFLAGS' -ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_c_compiler_gnu - - -ac_aux_dir= -for ac_dir in "$srcdir" "$srcdir/.." "$srcdir/../.."; do - if test -f "$ac_dir/install-sh"; then - ac_aux_dir=$ac_dir - ac_install_sh="$ac_aux_dir/install-sh -c" - break - elif test -f "$ac_dir/install.sh"; then - ac_aux_dir=$ac_dir - ac_install_sh="$ac_aux_dir/install.sh -c" - break - elif test -f "$ac_dir/shtool"; then - ac_aux_dir=$ac_dir - ac_install_sh="$ac_aux_dir/shtool install -c" - break - fi -done -if test -z "$ac_aux_dir"; then - as_fn_error $? "cannot find install-sh, install.sh, or shtool in \"$srcdir\" \"$srcdir/..\" \"$srcdir/../..\"" "$LINENO" 5 -fi - -# These three variables are undocumented and unsupported, -# and are intended to be withdrawn in a future Autoconf release. -# They can cause serious problems if a builder's source tree is in a directory -# whose full name contains unusual characters. -ac_config_guess="$SHELL $ac_aux_dir/config.guess" # Please don't use this var. -ac_config_sub="$SHELL $ac_aux_dir/config.sub" # Please don't use this var. -ac_configure="$SHELL $ac_aux_dir/configure" # Please don't use this var. - - -# Expand $ac_aux_dir to an absolute path. -am_aux_dir=`cd "$ac_aux_dir" && pwd` - -ac_ext=c -ac_cpp='$CPP $CPPFLAGS' -ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_c_compiler_gnu -if test -n "$ac_tool_prefix"; then - # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args. -set dummy ${ac_tool_prefix}gcc; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_CC+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -n "$CC"; then - ac_cv_prog_CC="$CC" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_CC="${ac_tool_prefix}gcc" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -CC=$ac_cv_prog_CC -if test -n "$CC"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 -$as_echo "$CC" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - -fi -if test -z "$ac_cv_prog_CC"; then - ac_ct_CC=$CC - # Extract the first word of "gcc", so it can be a program name with args. -set dummy gcc; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_ac_ct_CC+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -n "$ac_ct_CC"; then - ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_ac_ct_CC="gcc" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -ac_ct_CC=$ac_cv_prog_ac_ct_CC -if test -n "$ac_ct_CC"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 -$as_echo "$ac_ct_CC" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - if test "x$ac_ct_CC" = x; then - CC="" - else - case $cross_compiling:$ac_tool_warned in -yes:) -{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 -$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} -ac_tool_warned=yes ;; -esac - CC=$ac_ct_CC - fi -else - CC="$ac_cv_prog_CC" -fi - -if test -z "$CC"; then - if test -n "$ac_tool_prefix"; then - # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args. -set dummy ${ac_tool_prefix}cc; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_CC+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -n "$CC"; then - ac_cv_prog_CC="$CC" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_CC="${ac_tool_prefix}cc" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -CC=$ac_cv_prog_CC -if test -n "$CC"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 -$as_echo "$CC" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - - fi -fi -if test -z "$CC"; then - # Extract the first word of "cc", so it can be a program name with args. -set dummy cc; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_CC+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -n "$CC"; then - ac_cv_prog_CC="$CC" # Let the user override the test. -else - ac_prog_rejected=no -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then - ac_prog_rejected=yes - continue - fi - ac_cv_prog_CC="cc" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -if test $ac_prog_rejected = yes; then - # We found a bogon in the path, so make sure we never use it. - set dummy $ac_cv_prog_CC - shift - if test $# != 0; then - # We chose a different compiler from the bogus one. - # However, it has the same basename, so the bogon will be chosen - # first if we set CC to just the basename; use the full file name. - shift - ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@" - fi -fi -fi -fi -CC=$ac_cv_prog_CC -if test -n "$CC"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 -$as_echo "$CC" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - -fi -if test -z "$CC"; then - if test -n "$ac_tool_prefix"; then - for ac_prog in cl.exe - do - # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. -set dummy $ac_tool_prefix$ac_prog; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_CC+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -n "$CC"; then - ac_cv_prog_CC="$CC" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_CC="$ac_tool_prefix$ac_prog" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -CC=$ac_cv_prog_CC -if test -n "$CC"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 -$as_echo "$CC" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - - test -n "$CC" && break - done -fi -if test -z "$CC"; then - ac_ct_CC=$CC - for ac_prog in cl.exe -do - # Extract the first word of "$ac_prog", so it can be a program name with args. -set dummy $ac_prog; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_ac_ct_CC+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -n "$ac_ct_CC"; then - ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_ac_ct_CC="$ac_prog" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -ac_ct_CC=$ac_cv_prog_ac_ct_CC -if test -n "$ac_ct_CC"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 -$as_echo "$ac_ct_CC" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - - test -n "$ac_ct_CC" && break -done - - if test "x$ac_ct_CC" = x; then - CC="" - else - case $cross_compiling:$ac_tool_warned in -yes:) -{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 -$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} -ac_tool_warned=yes ;; -esac - CC=$ac_ct_CC - fi -fi - -fi - - -test -z "$CC" && { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error $? "no acceptable C compiler found in \$PATH -See \`config.log' for more details" "$LINENO" 5; } - -# Provide some information about the compiler. -$as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5 -set X $ac_compile -ac_compiler=$2 -for ac_option in --version -v -V -qversion; do - { { ac_try="$ac_compiler $ac_option >&5" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" -$as_echo "$ac_try_echo"; } >&5 - (eval "$ac_compiler $ac_option >&5") 2>conftest.err - ac_status=$? - if test -s conftest.err; then - sed '10a\ -... rest of stderr output deleted ... - 10q' conftest.err >conftest.er1 - cat conftest.er1 >&5 - fi - rm -f conftest.er1 conftest.err - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; } -done - -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main () -{ - - ; - return 0; -} -_ACEOF -ac_clean_files_save=$ac_clean_files -ac_clean_files="$ac_clean_files a.out a.out.dSYM a.exe b.out" -# Try to create an executable without -o first, disregard a.out. -# It will help us diagnose broken compilers, and finding out an intuition -# of exeext. -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C compiler works" >&5 -$as_echo_n "checking whether the C compiler works... " >&6; } -ac_link_default=`$as_echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'` - -# The possible output files: -ac_files="a.out conftest.exe conftest a.exe a_out.exe b.out conftest.*" - -ac_rmfiles= -for ac_file in $ac_files -do - case $ac_file in - *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; - * ) ac_rmfiles="$ac_rmfiles $ac_file";; - esac -done -rm -f $ac_rmfiles - -if { { ac_try="$ac_link_default" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" -$as_echo "$ac_try_echo"; } >&5 - (eval "$ac_link_default") 2>&5 - ac_status=$? - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; }; then : - # Autoconf-2.13 could set the ac_cv_exeext variable to `no'. -# So ignore a value of `no', otherwise this would lead to `EXEEXT = no' -# in a Makefile. We should not override ac_cv_exeext if it was cached, -# so that the user can short-circuit this test for compilers unknown to -# Autoconf. -for ac_file in $ac_files '' -do - test -f "$ac_file" || continue - case $ac_file in - *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) - ;; - [ab].out ) - # We found the default executable, but exeext='' is most - # certainly right. - break;; - *.* ) - if test "${ac_cv_exeext+set}" = set && test "$ac_cv_exeext" != no; - then :; else - ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` - fi - # We set ac_cv_exeext here because the later test for it is not - # safe: cross compilers may not add the suffix if given an `-o' - # argument, so we may need to know it at that point already. - # Even if this section looks crufty: it has the advantage of - # actually working. - break;; - * ) - break;; - esac -done -test "$ac_cv_exeext" = no && ac_cv_exeext= - -else - ac_file='' -fi -if test -z "$ac_file"; then : - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -$as_echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - -{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error 77 "C compiler cannot create executables -See \`config.log' for more details" "$LINENO" 5; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 -$as_echo "yes" >&6; } -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler default output file name" >&5 -$as_echo_n "checking for C compiler default output file name... " >&6; } -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_file" >&5 -$as_echo "$ac_file" >&6; } -ac_exeext=$ac_cv_exeext - -rm -f -r a.out a.out.dSYM a.exe conftest$ac_cv_exeext b.out -ac_clean_files=$ac_clean_files_save -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of executables" >&5 -$as_echo_n "checking for suffix of executables... " >&6; } -if { { ac_try="$ac_link" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" -$as_echo "$ac_try_echo"; } >&5 - (eval "$ac_link") 2>&5 - ac_status=$? - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; }; then : - # If both `conftest.exe' and `conftest' are `present' (well, observable) -# catch `conftest.exe'. For instance with Cygwin, `ls conftest' will -# work properly (i.e., refer to `conftest.exe'), while it won't with -# `rm'. -for ac_file in conftest.exe conftest conftest.*; do - test -f "$ac_file" || continue - case $ac_file in - *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; - *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` - break;; - * ) break;; - esac -done -else - { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error $? "cannot compute suffix of executables: cannot compile and link -See \`config.log' for more details" "$LINENO" 5; } -fi -rm -f conftest conftest$ac_cv_exeext -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_exeext" >&5 -$as_echo "$ac_cv_exeext" >&6; } - -rm -f conftest.$ac_ext -EXEEXT=$ac_cv_exeext -ac_exeext=$EXEEXT -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#include -int -main () -{ -FILE *f = fopen ("conftest.out", "w"); - return ferror (f) || fclose (f) != 0; - - ; - return 0; -} -_ACEOF -ac_clean_files="$ac_clean_files conftest.out" -# Check that the compiler produces executables we can run. If not, either -# the compiler is broken, or we cross compile. -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are cross compiling" >&5 -$as_echo_n "checking whether we are cross compiling... " >&6; } -if test "$cross_compiling" != yes; then - { { ac_try="$ac_link" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" -$as_echo "$ac_try_echo"; } >&5 - (eval "$ac_link") 2>&5 - ac_status=$? - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; } - if { ac_try='./conftest$ac_cv_exeext' - { { case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" -$as_echo "$ac_try_echo"; } >&5 - (eval "$ac_try") 2>&5 - ac_status=$? - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; }; }; then - cross_compiling=no - else - if test "$cross_compiling" = maybe; then - cross_compiling=yes - else - { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error $? "cannot run C compiled programs. -If you meant to cross compile, use \`--host'. -See \`config.log' for more details" "$LINENO" 5; } - fi - fi -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $cross_compiling" >&5 -$as_echo "$cross_compiling" >&6; } - -rm -f conftest.$ac_ext conftest$ac_cv_exeext conftest.out -ac_clean_files=$ac_clean_files_save -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of object files" >&5 -$as_echo_n "checking for suffix of object files... " >&6; } -if ${ac_cv_objext+:} false; then : - $as_echo_n "(cached) " >&6 -else - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main () -{ - - ; - return 0; -} -_ACEOF -rm -f conftest.o conftest.obj -if { { ac_try="$ac_compile" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" -$as_echo "$ac_try_echo"; } >&5 - (eval "$ac_compile") 2>&5 - ac_status=$? - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; }; then : - for ac_file in conftest.o conftest.obj conftest.*; do - test -f "$ac_file" || continue; - case $ac_file in - *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM ) ;; - *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'` - break;; - esac -done -else - $as_echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - -{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error $? "cannot compute suffix of object files: cannot compile -See \`config.log' for more details" "$LINENO" 5; } -fi -rm -f conftest.$ac_cv_objext conftest.$ac_ext -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_objext" >&5 -$as_echo "$ac_cv_objext" >&6; } -OBJEXT=$ac_cv_objext -ac_objext=$OBJEXT -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C compiler" >&5 -$as_echo_n "checking whether we are using the GNU C compiler... " >&6; } -if ${ac_cv_c_compiler_gnu+:} false; then : - $as_echo_n "(cached) " >&6 -else - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main () -{ -#ifndef __GNUC__ - choke me -#endif - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - ac_compiler_gnu=yes -else - ac_compiler_gnu=no -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -ac_cv_c_compiler_gnu=$ac_compiler_gnu - -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5 -$as_echo "$ac_cv_c_compiler_gnu" >&6; } -if test $ac_compiler_gnu = yes; then - GCC=yes -else - GCC= -fi -ac_test_CFLAGS=${CFLAGS+set} -ac_save_CFLAGS=$CFLAGS -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5 -$as_echo_n "checking whether $CC accepts -g... " >&6; } -if ${ac_cv_prog_cc_g+:} false; then : - $as_echo_n "(cached) " >&6 -else - ac_save_c_werror_flag=$ac_c_werror_flag - ac_c_werror_flag=yes - ac_cv_prog_cc_g=no - CFLAGS="-g" - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main () -{ - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - ac_cv_prog_cc_g=yes -else - CFLAGS="" - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main () -{ - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - -else - ac_c_werror_flag=$ac_save_c_werror_flag - CFLAGS="-g" - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main () -{ - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - ac_cv_prog_cc_g=yes -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext - ac_c_werror_flag=$ac_save_c_werror_flag -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5 -$as_echo "$ac_cv_prog_cc_g" >&6; } -if test "$ac_test_CFLAGS" = set; then - CFLAGS=$ac_save_CFLAGS -elif test $ac_cv_prog_cc_g = yes; then - if test "$GCC" = yes; then - CFLAGS="-g -O2" - else - CFLAGS="-g" - fi -else - if test "$GCC" = yes; then - CFLAGS="-O2" - else - CFLAGS= - fi -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C89" >&5 -$as_echo_n "checking for $CC option to accept ISO C89... " >&6; } -if ${ac_cv_prog_cc_c89+:} false; then : - $as_echo_n "(cached) " >&6 -else - ac_cv_prog_cc_c89=no -ac_save_CC=$CC -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#include -#include -struct stat; -/* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */ -struct buf { int x; }; -FILE * (*rcsopen) (struct buf *, struct stat *, int); -static char *e (p, i) - char **p; - int i; -{ - return p[i]; -} -static char *f (char * (*g) (char **, int), char **p, ...) -{ - char *s; - va_list v; - va_start (v,p); - s = g (p, va_arg (v,int)); - va_end (v); - return s; -} - -/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has - function prototypes and stuff, but not '\xHH' hex character constants. - These don't provoke an error unfortunately, instead are silently treated - as 'x'. The following induces an error, until -std is added to get - proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an - array size at least. It's necessary to write '\x00'==0 to get something - that's true only with -std. */ -int osf4_cc_array ['\x00' == 0 ? 1 : -1]; - -/* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters - inside strings and character constants. */ -#define FOO(x) 'x' -int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1]; - -int test (int i, double x); -struct s1 {int (*f) (int a);}; -struct s2 {int (*f) (double a);}; -int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int); -int argc; -char **argv; -int -main () -{ -return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]; - ; - return 0; -} -_ACEOF -for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \ - -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" -do - CC="$ac_save_CC $ac_arg" - if ac_fn_c_try_compile "$LINENO"; then : - ac_cv_prog_cc_c89=$ac_arg -fi -rm -f core conftest.err conftest.$ac_objext - test "x$ac_cv_prog_cc_c89" != "xno" && break -done -rm -f conftest.$ac_ext -CC=$ac_save_CC - -fi -# AC_CACHE_VAL -case "x$ac_cv_prog_cc_c89" in - x) - { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 -$as_echo "none needed" >&6; } ;; - xno) - { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 -$as_echo "unsupported" >&6; } ;; - *) - CC="$CC $ac_cv_prog_cc_c89" - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5 -$as_echo "$ac_cv_prog_cc_c89" >&6; } ;; -esac -if test "x$ac_cv_prog_cc_c89" != xno; then : - -fi - -ac_ext=c -ac_cpp='$CPP $CPPFLAGS' -ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_c_compiler_gnu - -ac_ext=c -ac_cpp='$CPP $CPPFLAGS' -ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_c_compiler_gnu -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC understands -c and -o together" >&5 -$as_echo_n "checking whether $CC understands -c and -o together... " >&6; } -if ${am_cv_prog_cc_c_o+:} false; then : - $as_echo_n "(cached) " >&6 -else - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main () -{ - - ; - return 0; -} -_ACEOF - # Make sure it works both with $CC and with simple cc. - # Following AC_PROG_CC_C_O, we do the test twice because some - # compilers refuse to overwrite an existing .o file with -o, - # though they will create one. - am_cv_prog_cc_c_o=yes - for am_i in 1 2; do - if { echo "$as_me:$LINENO: $CC -c conftest.$ac_ext -o conftest2.$ac_objext" >&5 - ($CC -c conftest.$ac_ext -o conftest2.$ac_objext) >&5 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } \ - && test -f conftest2.$ac_objext; then - : OK - else - am_cv_prog_cc_c_o=no - break - fi - done - rm -f core conftest* - unset am_i -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_prog_cc_c_o" >&5 -$as_echo "$am_cv_prog_cc_c_o" >&6; } -if test "$am_cv_prog_cc_c_o" != yes; then - # Losing compiler, so override with the script. - # FIXME: It is wrong to rewrite CC. - # But if we don't then we get into trouble of one sort or another. - # A longer-term fix would be to have automake use am__CC in this case, - # and then we could set am__CC="\$(top_srcdir)/compile \$(CC)" - CC="$am_aux_dir/compile $CC" -fi -ac_ext=c -ac_cpp='$CPP $CPPFLAGS' -ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_c_compiler_gnu - - - -ac_ext=c -ac_cpp='$CPP $CPPFLAGS' -ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_c_compiler_gnu -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to run the C preprocessor" >&5 -$as_echo_n "checking how to run the C preprocessor... " >&6; } -# On Suns, sometimes $CPP names a directory. -if test -n "$CPP" && test -d "$CPP"; then - CPP= -fi -if test -z "$CPP"; then - if ${ac_cv_prog_CPP+:} false; then : - $as_echo_n "(cached) " >&6 -else - # Double quotes because CPP needs to be expanded - for CPP in "$CC -E" "$CC -E -traditional-cpp" "/lib/cpp" - do - ac_preproc_ok=false -for ac_c_preproc_warn_flag in '' yes -do - # Use a header file that comes with gcc, so configuring glibc - # with a fresh cross-compiler works. - # Prefer to if __STDC__ is defined, since - # exists even on freestanding compilers. - # On the NeXT, cc -E runs the code through the compiler's parser, - # not just through cpp. "Syntax error" is here to catch this case. - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#ifdef __STDC__ -# include -#else -# include -#endif - Syntax error -_ACEOF -if ac_fn_c_try_cpp "$LINENO"; then : - -else - # Broken: fails on valid input. -continue -fi -rm -f conftest.err conftest.i conftest.$ac_ext - - # OK, works on sane cases. Now check whether nonexistent headers - # can be detected and how. - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#include -_ACEOF -if ac_fn_c_try_cpp "$LINENO"; then : - # Broken: success on invalid input. -continue -else - # Passes both tests. -ac_preproc_ok=: -break -fi -rm -f conftest.err conftest.i conftest.$ac_ext - -done -# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. -rm -f conftest.i conftest.err conftest.$ac_ext -if $ac_preproc_ok; then : - break -fi - - done - ac_cv_prog_CPP=$CPP - -fi - CPP=$ac_cv_prog_CPP -else - ac_cv_prog_CPP=$CPP -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $CPP" >&5 -$as_echo "$CPP" >&6; } -ac_preproc_ok=false -for ac_c_preproc_warn_flag in '' yes -do - # Use a header file that comes with gcc, so configuring glibc - # with a fresh cross-compiler works. - # Prefer to if __STDC__ is defined, since - # exists even on freestanding compilers. - # On the NeXT, cc -E runs the code through the compiler's parser, - # not just through cpp. "Syntax error" is here to catch this case. - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#ifdef __STDC__ -# include -#else -# include -#endif - Syntax error -_ACEOF -if ac_fn_c_try_cpp "$LINENO"; then : - -else - # Broken: fails on valid input. -continue -fi -rm -f conftest.err conftest.i conftest.$ac_ext - - # OK, works on sane cases. Now check whether nonexistent headers - # can be detected and how. - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#include -_ACEOF -if ac_fn_c_try_cpp "$LINENO"; then : - # Broken: success on invalid input. -continue -else - # Passes both tests. -ac_preproc_ok=: -break -fi -rm -f conftest.err conftest.i conftest.$ac_ext - -done -# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. -rm -f conftest.i conftest.err conftest.$ac_ext -if $ac_preproc_ok; then : - -else - { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error $? "C preprocessor \"$CPP\" fails sanity check -See \`config.log' for more details" "$LINENO" 5; } -fi - -ac_ext=c -ac_cpp='$CPP $CPPFLAGS' -ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_c_compiler_gnu - - -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for grep that handles long lines and -e" >&5 -$as_echo_n "checking for grep that handles long lines and -e... " >&6; } -if ${ac_cv_path_GREP+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -z "$GREP"; then - ac_path_GREP_found=false - # Loop through the user's path and test for each of PROGNAME-LIST - as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_prog in grep ggrep; do - for ac_exec_ext in '' $ac_executable_extensions; do - ac_path_GREP="$as_dir/$ac_prog$ac_exec_ext" - as_fn_executable_p "$ac_path_GREP" || continue -# Check for GNU ac_path_GREP and select it if it is found. - # Check for GNU $ac_path_GREP -case `"$ac_path_GREP" --version 2>&1` in -*GNU*) - ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_found=:;; -*) - ac_count=0 - $as_echo_n 0123456789 >"conftest.in" - while : - do - cat "conftest.in" "conftest.in" >"conftest.tmp" - mv "conftest.tmp" "conftest.in" - cp "conftest.in" "conftest.nl" - $as_echo 'GREP' >> "conftest.nl" - "$ac_path_GREP" -e 'GREP$' -e '-(cannot match)-' < "conftest.nl" >"conftest.out" 2>/dev/null || break - diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break - as_fn_arith $ac_count + 1 && ac_count=$as_val - if test $ac_count -gt ${ac_path_GREP_max-0}; then - # Best one so far, save it but keep looking for a better one - ac_cv_path_GREP="$ac_path_GREP" - ac_path_GREP_max=$ac_count - fi - # 10*(2^10) chars as input seems more than enough - test $ac_count -gt 10 && break - done - rm -f conftest.in conftest.tmp conftest.nl conftest.out;; -esac - - $ac_path_GREP_found && break 3 - done - done - done -IFS=$as_save_IFS - if test -z "$ac_cv_path_GREP"; then - as_fn_error $? "no acceptable grep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 - fi -else - ac_cv_path_GREP=$GREP -fi - -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_GREP" >&5 -$as_echo "$ac_cv_path_GREP" >&6; } - GREP="$ac_cv_path_GREP" - - -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for egrep" >&5 -$as_echo_n "checking for egrep... " >&6; } -if ${ac_cv_path_EGREP+:} false; then : - $as_echo_n "(cached) " >&6 -else - if echo a | $GREP -E '(a|b)' >/dev/null 2>&1 - then ac_cv_path_EGREP="$GREP -E" - else - if test -z "$EGREP"; then - ac_path_EGREP_found=false - # Loop through the user's path and test for each of PROGNAME-LIST - as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_prog in egrep; do - for ac_exec_ext in '' $ac_executable_extensions; do - ac_path_EGREP="$as_dir/$ac_prog$ac_exec_ext" - as_fn_executable_p "$ac_path_EGREP" || continue -# Check for GNU ac_path_EGREP and select it if it is found. - # Check for GNU $ac_path_EGREP -case `"$ac_path_EGREP" --version 2>&1` in -*GNU*) - ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_found=:;; -*) - ac_count=0 - $as_echo_n 0123456789 >"conftest.in" - while : - do - cat "conftest.in" "conftest.in" >"conftest.tmp" - mv "conftest.tmp" "conftest.in" - cp "conftest.in" "conftest.nl" - $as_echo 'EGREP' >> "conftest.nl" - "$ac_path_EGREP" 'EGREP$' < "conftest.nl" >"conftest.out" 2>/dev/null || break - diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break - as_fn_arith $ac_count + 1 && ac_count=$as_val - if test $ac_count -gt ${ac_path_EGREP_max-0}; then - # Best one so far, save it but keep looking for a better one - ac_cv_path_EGREP="$ac_path_EGREP" - ac_path_EGREP_max=$ac_count - fi - # 10*(2^10) chars as input seems more than enough - test $ac_count -gt 10 && break - done - rm -f conftest.in conftest.tmp conftest.nl conftest.out;; -esac - - $ac_path_EGREP_found && break 3 - done - done - done -IFS=$as_save_IFS - if test -z "$ac_cv_path_EGREP"; then - as_fn_error $? "no acceptable egrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 - fi -else - ac_cv_path_EGREP=$EGREP -fi - - fi -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_EGREP" >&5 -$as_echo "$ac_cv_path_EGREP" >&6; } - EGREP="$ac_cv_path_EGREP" - - -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for ANSI C header files" >&5 -$as_echo_n "checking for ANSI C header files... " >&6; } -if ${ac_cv_header_stdc+:} false; then : - $as_echo_n "(cached) " >&6 -else - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#include -#include -#include -#include - -int -main () -{ - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - ac_cv_header_stdc=yes -else - ac_cv_header_stdc=no -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext - -if test $ac_cv_header_stdc = yes; then - # SunOS 4.x string.h does not declare mem*, contrary to ANSI. - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#include - -_ACEOF -if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | - $EGREP "memchr" >/dev/null 2>&1; then : - -else - ac_cv_header_stdc=no -fi -rm -f conftest* - -fi - -if test $ac_cv_header_stdc = yes; then - # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI. - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#include - -_ACEOF -if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | - $EGREP "free" >/dev/null 2>&1; then : - -else - ac_cv_header_stdc=no -fi -rm -f conftest* - -fi - -if test $ac_cv_header_stdc = yes; then - # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi. - if test "$cross_compiling" = yes; then : - : -else - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#include -#include -#if ((' ' & 0x0FF) == 0x020) -# define ISLOWER(c) ('a' <= (c) && (c) <= 'z') -# define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c)) -#else -# define ISLOWER(c) \ - (('a' <= (c) && (c) <= 'i') \ - || ('j' <= (c) && (c) <= 'r') \ - || ('s' <= (c) && (c) <= 'z')) -# define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c)) -#endif - -#define XOR(e, f) (((e) && !(f)) || (!(e) && (f))) -int -main () -{ - int i; - for (i = 0; i < 256; i++) - if (XOR (islower (i), ISLOWER (i)) - || toupper (i) != TOUPPER (i)) - return 2; - return 0; -} -_ACEOF -if ac_fn_c_try_run "$LINENO"; then : - -else - ac_cv_header_stdc=no -fi -rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ - conftest.$ac_objext conftest.beam conftest.$ac_ext -fi - -fi -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_stdc" >&5 -$as_echo "$ac_cv_header_stdc" >&6; } -if test $ac_cv_header_stdc = yes; then - -$as_echo "#define STDC_HEADERS 1" >>confdefs.h - -fi - -# On IRIX 5.3, sys/types and inttypes.h are conflicting. -for ac_header in sys/types.h sys/stat.h stdlib.h string.h memory.h strings.h \ - inttypes.h stdint.h unistd.h -do : - as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` -ac_fn_c_check_header_compile "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default -" -if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : - cat >>confdefs.h <<_ACEOF -#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 -_ACEOF - -fi - -done - - - - ac_fn_c_check_header_mongrel "$LINENO" "minix/config.h" "ac_cv_header_minix_config_h" "$ac_includes_default" -if test "x$ac_cv_header_minix_config_h" = xyes; then : - MINIX=yes -else - MINIX= -fi - - - if test "$MINIX" = yes; then - -$as_echo "#define _POSIX_SOURCE 1" >>confdefs.h - - -$as_echo "#define _POSIX_1_SOURCE 2" >>confdefs.h - - -$as_echo "#define _MINIX 1" >>confdefs.h - - fi - - - { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether it is safe to define __EXTENSIONS__" >&5 -$as_echo_n "checking whether it is safe to define __EXTENSIONS__... " >&6; } -if ${ac_cv_safe_to_define___extensions__+:} false; then : - $as_echo_n "(cached) " >&6 -else - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -# define __EXTENSIONS__ 1 - $ac_includes_default -int -main () -{ - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - ac_cv_safe_to_define___extensions__=yes -else - ac_cv_safe_to_define___extensions__=no -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_safe_to_define___extensions__" >&5 -$as_echo "$ac_cv_safe_to_define___extensions__" >&6; } - test $ac_cv_safe_to_define___extensions__ = yes && - $as_echo "#define __EXTENSIONS__ 1" >>confdefs.h - - $as_echo "#define _ALL_SOURCE 1" >>confdefs.h - - $as_echo "#define _GNU_SOURCE 1" >>confdefs.h - - $as_echo "#define _POSIX_PTHREAD_SEMANTICS 1" >>confdefs.h - - $as_echo "#define _TANDEM_SOURCE 1" >>confdefs.h - - - - -case `pwd` in - *\ * | *\ *) - { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Libtool does not cope well with whitespace in \`pwd\`" >&5 -$as_echo "$as_me: WARNING: Libtool does not cope well with whitespace in \`pwd\`" >&2;} ;; -esac - - - -macro_version='2.4.2' -macro_revision='1.3337' - - - - - - - - - - - - - -ltmain="$ac_aux_dir/ltmain.sh" - -# Make sure we can run config.sub. -$SHELL "$ac_aux_dir/config.sub" sun4 >/dev/null 2>&1 || - as_fn_error $? "cannot run $SHELL $ac_aux_dir/config.sub" "$LINENO" 5 - -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking build system type" >&5 -$as_echo_n "checking build system type... " >&6; } -if ${ac_cv_build+:} false; then : - $as_echo_n "(cached) " >&6 -else - ac_build_alias=$build_alias -test "x$ac_build_alias" = x && - ac_build_alias=`$SHELL "$ac_aux_dir/config.guess"` -test "x$ac_build_alias" = x && - as_fn_error $? "cannot guess build type; you must specify one" "$LINENO" 5 -ac_cv_build=`$SHELL "$ac_aux_dir/config.sub" $ac_build_alias` || - as_fn_error $? "$SHELL $ac_aux_dir/config.sub $ac_build_alias failed" "$LINENO" 5 - -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_build" >&5 -$as_echo "$ac_cv_build" >&6; } -case $ac_cv_build in -*-*-*) ;; -*) as_fn_error $? "invalid value of canonical build" "$LINENO" 5;; -esac -build=$ac_cv_build -ac_save_IFS=$IFS; IFS='-' -set x $ac_cv_build -shift -build_cpu=$1 -build_vendor=$2 -shift; shift -# Remember, the first character of IFS is used to create $*, -# except with old shells: -build_os=$* -IFS=$ac_save_IFS -case $build_os in *\ *) build_os=`echo "$build_os" | sed 's/ /-/g'`;; esac - - -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking host system type" >&5 -$as_echo_n "checking host system type... " >&6; } -if ${ac_cv_host+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test "x$host_alias" = x; then - ac_cv_host=$ac_cv_build -else - ac_cv_host=`$SHELL "$ac_aux_dir/config.sub" $host_alias` || - as_fn_error $? "$SHELL $ac_aux_dir/config.sub $host_alias failed" "$LINENO" 5 -fi - -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_host" >&5 -$as_echo "$ac_cv_host" >&6; } -case $ac_cv_host in -*-*-*) ;; -*) as_fn_error $? "invalid value of canonical host" "$LINENO" 5;; -esac -host=$ac_cv_host -ac_save_IFS=$IFS; IFS='-' -set x $ac_cv_host -shift -host_cpu=$1 -host_vendor=$2 -shift; shift -# Remember, the first character of IFS is used to create $*, -# except with old shells: -host_os=$* -IFS=$ac_save_IFS -case $host_os in *\ *) host_os=`echo "$host_os" | sed 's/ /-/g'`;; esac - - -# Backslashify metacharacters that are still active within -# double-quoted strings. -sed_quote_subst='s/\(["`$\\]\)/\\\1/g' - -# Same as above, but do not quote variable references. -double_quote_subst='s/\(["`\\]\)/\\\1/g' - -# Sed substitution to delay expansion of an escaped shell variable in a -# double_quote_subst'ed string. -delay_variable_subst='s/\\\\\\\\\\\$/\\\\\\$/g' - -# Sed substitution to delay expansion of an escaped single quote. -delay_single_quote_subst='s/'\''/'\'\\\\\\\'\''/g' - -# Sed substitution to avoid accidental globbing in evaled expressions -no_glob_subst='s/\*/\\\*/g' - -ECHO='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' -ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO -ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO$ECHO - -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to print strings" >&5 -$as_echo_n "checking how to print strings... " >&6; } -# Test print first, because it will be a builtin if present. -if test "X`( print -r -- -n ) 2>/dev/null`" = X-n && \ - test "X`print -r -- $ECHO 2>/dev/null`" = "X$ECHO"; then - ECHO='print -r --' -elif test "X`printf %s $ECHO 2>/dev/null`" = "X$ECHO"; then - ECHO='printf %s\n' -else - # Use this function as a fallback that always works. - func_fallback_echo () - { - eval 'cat <<_LTECHO_EOF -$1 -_LTECHO_EOF' - } - ECHO='func_fallback_echo' -fi - -# func_echo_all arg... -# Invoke $ECHO with all args, space-separated. -func_echo_all () -{ - $ECHO "" -} - -case "$ECHO" in - printf*) { $as_echo "$as_me:${as_lineno-$LINENO}: result: printf" >&5 -$as_echo "printf" >&6; } ;; - print*) { $as_echo "$as_me:${as_lineno-$LINENO}: result: print -r" >&5 -$as_echo "print -r" >&6; } ;; - *) { $as_echo "$as_me:${as_lineno-$LINENO}: result: cat" >&5 -$as_echo "cat" >&6; } ;; -esac - - - - - - - - - - - - - - -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for a sed that does not truncate output" >&5 -$as_echo_n "checking for a sed that does not truncate output... " >&6; } -if ${ac_cv_path_SED+:} false; then : - $as_echo_n "(cached) " >&6 -else - ac_script=s/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb/ - for ac_i in 1 2 3 4 5 6 7; do - ac_script="$ac_script$as_nl$ac_script" - done - echo "$ac_script" 2>/dev/null | sed 99q >conftest.sed - { ac_script=; unset ac_script;} - if test -z "$SED"; then - ac_path_SED_found=false - # Loop through the user's path and test for each of PROGNAME-LIST - as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_prog in sed gsed; do - for ac_exec_ext in '' $ac_executable_extensions; do - ac_path_SED="$as_dir/$ac_prog$ac_exec_ext" - as_fn_executable_p "$ac_path_SED" || continue -# Check for GNU ac_path_SED and select it if it is found. - # Check for GNU $ac_path_SED -case `"$ac_path_SED" --version 2>&1` in -*GNU*) - ac_cv_path_SED="$ac_path_SED" ac_path_SED_found=:;; -*) - ac_count=0 - $as_echo_n 0123456789 >"conftest.in" - while : - do - cat "conftest.in" "conftest.in" >"conftest.tmp" - mv "conftest.tmp" "conftest.in" - cp "conftest.in" "conftest.nl" - $as_echo '' >> "conftest.nl" - "$ac_path_SED" -f conftest.sed < "conftest.nl" >"conftest.out" 2>/dev/null || break - diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break - as_fn_arith $ac_count + 1 && ac_count=$as_val - if test $ac_count -gt ${ac_path_SED_max-0}; then - # Best one so far, save it but keep looking for a better one - ac_cv_path_SED="$ac_path_SED" - ac_path_SED_max=$ac_count - fi - # 10*(2^10) chars as input seems more than enough - test $ac_count -gt 10 && break - done - rm -f conftest.in conftest.tmp conftest.nl conftest.out;; -esac - - $ac_path_SED_found && break 3 - done - done - done -IFS=$as_save_IFS - if test -z "$ac_cv_path_SED"; then - as_fn_error $? "no acceptable sed could be found in \$PATH" "$LINENO" 5 - fi -else - ac_cv_path_SED=$SED -fi - -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_SED" >&5 -$as_echo "$ac_cv_path_SED" >&6; } - SED="$ac_cv_path_SED" - rm -f conftest.sed - -test -z "$SED" && SED=sed -Xsed="$SED -e 1s/^X//" - - - - - - - - - - - -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for fgrep" >&5 -$as_echo_n "checking for fgrep... " >&6; } -if ${ac_cv_path_FGREP+:} false; then : - $as_echo_n "(cached) " >&6 -else - if echo 'ab*c' | $GREP -F 'ab*c' >/dev/null 2>&1 - then ac_cv_path_FGREP="$GREP -F" - else - if test -z "$FGREP"; then - ac_path_FGREP_found=false - # Loop through the user's path and test for each of PROGNAME-LIST - as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_prog in fgrep; do - for ac_exec_ext in '' $ac_executable_extensions; do - ac_path_FGREP="$as_dir/$ac_prog$ac_exec_ext" - as_fn_executable_p "$ac_path_FGREP" || continue -# Check for GNU ac_path_FGREP and select it if it is found. - # Check for GNU $ac_path_FGREP -case `"$ac_path_FGREP" --version 2>&1` in -*GNU*) - ac_cv_path_FGREP="$ac_path_FGREP" ac_path_FGREP_found=:;; -*) - ac_count=0 - $as_echo_n 0123456789 >"conftest.in" - while : - do - cat "conftest.in" "conftest.in" >"conftest.tmp" - mv "conftest.tmp" "conftest.in" - cp "conftest.in" "conftest.nl" - $as_echo 'FGREP' >> "conftest.nl" - "$ac_path_FGREP" FGREP < "conftest.nl" >"conftest.out" 2>/dev/null || break - diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break - as_fn_arith $ac_count + 1 && ac_count=$as_val - if test $ac_count -gt ${ac_path_FGREP_max-0}; then - # Best one so far, save it but keep looking for a better one - ac_cv_path_FGREP="$ac_path_FGREP" - ac_path_FGREP_max=$ac_count - fi - # 10*(2^10) chars as input seems more than enough - test $ac_count -gt 10 && break - done - rm -f conftest.in conftest.tmp conftest.nl conftest.out;; -esac - - $ac_path_FGREP_found && break 3 - done - done - done -IFS=$as_save_IFS - if test -z "$ac_cv_path_FGREP"; then - as_fn_error $? "no acceptable fgrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 - fi -else - ac_cv_path_FGREP=$FGREP -fi - - fi -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_FGREP" >&5 -$as_echo "$ac_cv_path_FGREP" >&6; } - FGREP="$ac_cv_path_FGREP" - - -test -z "$GREP" && GREP=grep - - - - - - - - - - - - - - - - - - - -# Check whether --with-gnu-ld was given. -if test "${with_gnu_ld+set}" = set; then : - withval=$with_gnu_ld; test "$withval" = no || with_gnu_ld=yes -else - with_gnu_ld=no -fi - -ac_prog=ld -if test "$GCC" = yes; then - # Check if gcc -print-prog-name=ld gives a path. - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ld used by $CC" >&5 -$as_echo_n "checking for ld used by $CC... " >&6; } - case $host in - *-*-mingw*) - # gcc leaves a trailing carriage return which upsets mingw - ac_prog=`($CC -print-prog-name=ld) 2>&5 | tr -d '\015'` ;; - *) - ac_prog=`($CC -print-prog-name=ld) 2>&5` ;; - esac - case $ac_prog in - # Accept absolute paths. - [\\/]* | ?:[\\/]*) - re_direlt='/[^/][^/]*/\.\./' - # Canonicalize the pathname of ld - ac_prog=`$ECHO "$ac_prog"| $SED 's%\\\\%/%g'` - while $ECHO "$ac_prog" | $GREP "$re_direlt" > /dev/null 2>&1; do - ac_prog=`$ECHO $ac_prog| $SED "s%$re_direlt%/%"` - done - test -z "$LD" && LD="$ac_prog" - ;; - "") - # If it fails, then pretend we aren't using GCC. - ac_prog=ld - ;; - *) - # If it is relative, then search for the first ld in PATH. - with_gnu_ld=unknown - ;; - esac -elif test "$with_gnu_ld" = yes; then - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for GNU ld" >&5 -$as_echo_n "checking for GNU ld... " >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for non-GNU ld" >&5 -$as_echo_n "checking for non-GNU ld... " >&6; } -fi -if ${lt_cv_path_LD+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -z "$LD"; then - lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR - for ac_dir in $PATH; do - IFS="$lt_save_ifs" - test -z "$ac_dir" && ac_dir=. - if test -f "$ac_dir/$ac_prog" || test -f "$ac_dir/$ac_prog$ac_exeext"; then - lt_cv_path_LD="$ac_dir/$ac_prog" - # Check to see if the program is GNU ld. I'd rather use --version, - # but apparently some variants of GNU ld only accept -v. - # Break only if it was the GNU/non-GNU ld that we prefer. - case `"$lt_cv_path_LD" -v 2>&1 &5 -$as_echo "$LD" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi -test -z "$LD" && as_fn_error $? "no acceptable ld found in \$PATH" "$LINENO" 5 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if the linker ($LD) is GNU ld" >&5 -$as_echo_n "checking if the linker ($LD) is GNU ld... " >&6; } -if ${lt_cv_prog_gnu_ld+:} false; then : - $as_echo_n "(cached) " >&6 -else - # I'd rather use --version here, but apparently some GNU lds only accept -v. -case `$LD -v 2>&1 &5 -$as_echo "$lt_cv_prog_gnu_ld" >&6; } -with_gnu_ld=$lt_cv_prog_gnu_ld - - - - - - - - - -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for BSD- or MS-compatible name lister (nm)" >&5 -$as_echo_n "checking for BSD- or MS-compatible name lister (nm)... " >&6; } -if ${lt_cv_path_NM+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -n "$NM"; then - # Let the user override the test. - lt_cv_path_NM="$NM" -else - lt_nm_to_check="${ac_tool_prefix}nm" - if test -n "$ac_tool_prefix" && test "$build" = "$host"; then - lt_nm_to_check="$lt_nm_to_check nm" - fi - for lt_tmp_nm in $lt_nm_to_check; do - lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR - for ac_dir in $PATH /usr/ccs/bin/elf /usr/ccs/bin /usr/ucb /bin; do - IFS="$lt_save_ifs" - test -z "$ac_dir" && ac_dir=. - tmp_nm="$ac_dir/$lt_tmp_nm" - if test -f "$tmp_nm" || test -f "$tmp_nm$ac_exeext" ; then - # Check to see if the nm accepts a BSD-compat flag. - # Adding the `sed 1q' prevents false positives on HP-UX, which says: - # nm: unknown option "B" ignored - # Tru64's nm complains that /dev/null is an invalid object file - case `"$tmp_nm" -B /dev/null 2>&1 | sed '1q'` in - */dev/null* | *'Invalid file or object type'*) - lt_cv_path_NM="$tmp_nm -B" - break - ;; - *) - case `"$tmp_nm" -p /dev/null 2>&1 | sed '1q'` in - */dev/null*) - lt_cv_path_NM="$tmp_nm -p" - break - ;; - *) - lt_cv_path_NM=${lt_cv_path_NM="$tmp_nm"} # keep the first match, but - continue # so that we can try to find one that supports BSD flags - ;; - esac - ;; - esac - fi - done - IFS="$lt_save_ifs" - done - : ${lt_cv_path_NM=no} -fi -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_path_NM" >&5 -$as_echo "$lt_cv_path_NM" >&6; } -if test "$lt_cv_path_NM" != "no"; then - NM="$lt_cv_path_NM" -else - # Didn't find any BSD compatible name lister, look for dumpbin. - if test -n "$DUMPBIN"; then : - # Let the user override the test. - else - if test -n "$ac_tool_prefix"; then - for ac_prog in dumpbin "link -dump" - do - # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. -set dummy $ac_tool_prefix$ac_prog; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_DUMPBIN+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -n "$DUMPBIN"; then - ac_cv_prog_DUMPBIN="$DUMPBIN" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_DUMPBIN="$ac_tool_prefix$ac_prog" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -DUMPBIN=$ac_cv_prog_DUMPBIN -if test -n "$DUMPBIN"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $DUMPBIN" >&5 -$as_echo "$DUMPBIN" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - - test -n "$DUMPBIN" && break - done -fi -if test -z "$DUMPBIN"; then - ac_ct_DUMPBIN=$DUMPBIN - for ac_prog in dumpbin "link -dump" -do - # Extract the first word of "$ac_prog", so it can be a program name with args. -set dummy $ac_prog; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_ac_ct_DUMPBIN+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -n "$ac_ct_DUMPBIN"; then - ac_cv_prog_ac_ct_DUMPBIN="$ac_ct_DUMPBIN" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_ac_ct_DUMPBIN="$ac_prog" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -ac_ct_DUMPBIN=$ac_cv_prog_ac_ct_DUMPBIN -if test -n "$ac_ct_DUMPBIN"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_DUMPBIN" >&5 -$as_echo "$ac_ct_DUMPBIN" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - - test -n "$ac_ct_DUMPBIN" && break -done - - if test "x$ac_ct_DUMPBIN" = x; then - DUMPBIN=":" - else - case $cross_compiling:$ac_tool_warned in -yes:) -{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 -$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} -ac_tool_warned=yes ;; -esac - DUMPBIN=$ac_ct_DUMPBIN - fi -fi - - case `$DUMPBIN -symbols /dev/null 2>&1 | sed '1q'` in - *COFF*) - DUMPBIN="$DUMPBIN -symbols" - ;; - *) - DUMPBIN=: - ;; - esac - fi - - if test "$DUMPBIN" != ":"; then - NM="$DUMPBIN" - fi -fi -test -z "$NM" && NM=nm - - - - - - -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking the name lister ($NM) interface" >&5 -$as_echo_n "checking the name lister ($NM) interface... " >&6; } -if ${lt_cv_nm_interface+:} false; then : - $as_echo_n "(cached) " >&6 -else - lt_cv_nm_interface="BSD nm" - echo "int some_variable = 0;" > conftest.$ac_ext - (eval echo "\"\$as_me:$LINENO: $ac_compile\"" >&5) - (eval "$ac_compile" 2>conftest.err) - cat conftest.err >&5 - (eval echo "\"\$as_me:$LINENO: $NM \\\"conftest.$ac_objext\\\"\"" >&5) - (eval "$NM \"conftest.$ac_objext\"" 2>conftest.err > conftest.out) - cat conftest.err >&5 - (eval echo "\"\$as_me:$LINENO: output\"" >&5) - cat conftest.out >&5 - if $GREP 'External.*some_variable' conftest.out > /dev/null; then - lt_cv_nm_interface="MS dumpbin" - fi - rm -f conftest* -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_nm_interface" >&5 -$as_echo "$lt_cv_nm_interface" >&6; } - -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ln -s works" >&5 -$as_echo_n "checking whether ln -s works... " >&6; } -LN_S=$as_ln_s -if test "$LN_S" = "ln -s"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 -$as_echo "yes" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no, using $LN_S" >&5 -$as_echo "no, using $LN_S" >&6; } -fi - -# find the maximum length of command line arguments -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking the maximum length of command line arguments" >&5 -$as_echo_n "checking the maximum length of command line arguments... " >&6; } -if ${lt_cv_sys_max_cmd_len+:} false; then : - $as_echo_n "(cached) " >&6 -else - i=0 - teststring="ABCD" - - case $build_os in - msdosdjgpp*) - # On DJGPP, this test can blow up pretty badly due to problems in libc - # (any single argument exceeding 2000 bytes causes a buffer overrun - # during glob expansion). Even if it were fixed, the result of this - # check would be larger than it should be. - lt_cv_sys_max_cmd_len=12288; # 12K is about right - ;; - - gnu*) - # Under GNU Hurd, this test is not required because there is - # no limit to the length of command line arguments. - # Libtool will interpret -1 as no limit whatsoever - lt_cv_sys_max_cmd_len=-1; - ;; - - cygwin* | mingw* | cegcc*) - # On Win9x/ME, this test blows up -- it succeeds, but takes - # about 5 minutes as the teststring grows exponentially. - # Worse, since 9x/ME are not pre-emptively multitasking, - # you end up with a "frozen" computer, even though with patience - # the test eventually succeeds (with a max line length of 256k). - # Instead, let's just punt: use the minimum linelength reported by - # all of the supported platforms: 8192 (on NT/2K/XP). - lt_cv_sys_max_cmd_len=8192; - ;; - - mint*) - # On MiNT this can take a long time and run out of memory. - lt_cv_sys_max_cmd_len=8192; - ;; - - amigaos*) - # On AmigaOS with pdksh, this test takes hours, literally. - # So we just punt and use a minimum line length of 8192. - lt_cv_sys_max_cmd_len=8192; - ;; - - netbsd* | freebsd* | openbsd* | darwin* | dragonfly*) - # This has been around since 386BSD, at least. Likely further. - if test -x /sbin/sysctl; then - lt_cv_sys_max_cmd_len=`/sbin/sysctl -n kern.argmax` - elif test -x /usr/sbin/sysctl; then - lt_cv_sys_max_cmd_len=`/usr/sbin/sysctl -n kern.argmax` - else - lt_cv_sys_max_cmd_len=65536 # usable default for all BSDs - fi - # And add a safety zone - lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4` - lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3` - ;; - - interix*) - # We know the value 262144 and hardcode it with a safety zone (like BSD) - lt_cv_sys_max_cmd_len=196608 - ;; - - os2*) - # The test takes a long time on OS/2. - lt_cv_sys_max_cmd_len=8192 - ;; - - osf*) - # Dr. Hans Ekkehard Plesser reports seeing a kernel panic running configure - # due to this test when exec_disable_arg_limit is 1 on Tru64. It is not - # nice to cause kernel panics so lets avoid the loop below. - # First set a reasonable default. - lt_cv_sys_max_cmd_len=16384 - # - if test -x /sbin/sysconfig; then - case `/sbin/sysconfig -q proc exec_disable_arg_limit` in - *1*) lt_cv_sys_max_cmd_len=-1 ;; - esac - fi - ;; - sco3.2v5*) - lt_cv_sys_max_cmd_len=102400 - ;; - sysv5* | sco5v6* | sysv4.2uw2*) - kargmax=`grep ARG_MAX /etc/conf/cf.d/stune 2>/dev/null` - if test -n "$kargmax"; then - lt_cv_sys_max_cmd_len=`echo $kargmax | sed 's/.*[ ]//'` - else - lt_cv_sys_max_cmd_len=32768 - fi - ;; - *) - lt_cv_sys_max_cmd_len=`(getconf ARG_MAX) 2> /dev/null` - if test -n "$lt_cv_sys_max_cmd_len" && \ - test undefined != "$lt_cv_sys_max_cmd_len"; then - lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4` - lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3` - else - # Make teststring a little bigger before we do anything with it. - # a 1K string should be a reasonable start. - for i in 1 2 3 4 5 6 7 8 ; do - teststring=$teststring$teststring - done - SHELL=${SHELL-${CONFIG_SHELL-/bin/sh}} - # If test is not a shell built-in, we'll probably end up computing a - # maximum length that is only half of the actual maximum length, but - # we can't tell. - while { test "X"`env echo "$teststring$teststring" 2>/dev/null` \ - = "X$teststring$teststring"; } >/dev/null 2>&1 && - test $i != 17 # 1/2 MB should be enough - do - i=`expr $i + 1` - teststring=$teststring$teststring - done - # Only check the string length outside the loop. - lt_cv_sys_max_cmd_len=`expr "X$teststring" : ".*" 2>&1` - teststring= - # Add a significant safety factor because C++ compilers can tack on - # massive amounts of additional arguments before passing them to the - # linker. It appears as though 1/2 is a usable value. - lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 2` - fi - ;; - esac - -fi - -if test -n $lt_cv_sys_max_cmd_len ; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_sys_max_cmd_len" >&5 -$as_echo "$lt_cv_sys_max_cmd_len" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: none" >&5 -$as_echo "none" >&6; } -fi -max_cmd_len=$lt_cv_sys_max_cmd_len - - - - - - -: ${CP="cp -f"} -: ${MV="mv -f"} -: ${RM="rm -f"} - -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the shell understands some XSI constructs" >&5 -$as_echo_n "checking whether the shell understands some XSI constructs... " >&6; } -# Try some XSI features -xsi_shell=no -( _lt_dummy="a/b/c" - test "${_lt_dummy##*/},${_lt_dummy%/*},${_lt_dummy#??}"${_lt_dummy%"$_lt_dummy"}, \ - = c,a/b,b/c, \ - && eval 'test $(( 1 + 1 )) -eq 2 \ - && test "${#_lt_dummy}" -eq 5' ) >/dev/null 2>&1 \ - && xsi_shell=yes -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $xsi_shell" >&5 -$as_echo "$xsi_shell" >&6; } - - -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the shell understands \"+=\"" >&5 -$as_echo_n "checking whether the shell understands \"+=\"... " >&6; } -lt_shell_append=no -( foo=bar; set foo baz; eval "$1+=\$2" && test "$foo" = barbaz ) \ - >/dev/null 2>&1 \ - && lt_shell_append=yes -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_shell_append" >&5 -$as_echo "$lt_shell_append" >&6; } - - -if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then - lt_unset=unset -else - lt_unset=false -fi - - - - - -# test EBCDIC or ASCII -case `echo X|tr X '\101'` in - A) # ASCII based system - # \n is not interpreted correctly by Solaris 8 /usr/ucb/tr - lt_SP2NL='tr \040 \012' - lt_NL2SP='tr \015\012 \040\040' - ;; - *) # EBCDIC based system - lt_SP2NL='tr \100 \n' - lt_NL2SP='tr \r\n \100\100' - ;; -esac - - - - - - - - - -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to convert $build file names to $host format" >&5 -$as_echo_n "checking how to convert $build file names to $host format... " >&6; } -if ${lt_cv_to_host_file_cmd+:} false; then : - $as_echo_n "(cached) " >&6 -else - case $host in - *-*-mingw* ) - case $build in - *-*-mingw* ) # actually msys - lt_cv_to_host_file_cmd=func_convert_file_msys_to_w32 - ;; - *-*-cygwin* ) - lt_cv_to_host_file_cmd=func_convert_file_cygwin_to_w32 - ;; - * ) # otherwise, assume *nix - lt_cv_to_host_file_cmd=func_convert_file_nix_to_w32 - ;; - esac - ;; - *-*-cygwin* ) - case $build in - *-*-mingw* ) # actually msys - lt_cv_to_host_file_cmd=func_convert_file_msys_to_cygwin - ;; - *-*-cygwin* ) - lt_cv_to_host_file_cmd=func_convert_file_noop - ;; - * ) # otherwise, assume *nix - lt_cv_to_host_file_cmd=func_convert_file_nix_to_cygwin - ;; - esac - ;; - * ) # unhandled hosts (and "normal" native builds) - lt_cv_to_host_file_cmd=func_convert_file_noop - ;; -esac - -fi - -to_host_file_cmd=$lt_cv_to_host_file_cmd -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_to_host_file_cmd" >&5 -$as_echo "$lt_cv_to_host_file_cmd" >&6; } - - - - - -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to convert $build file names to toolchain format" >&5 -$as_echo_n "checking how to convert $build file names to toolchain format... " >&6; } -if ${lt_cv_to_tool_file_cmd+:} false; then : - $as_echo_n "(cached) " >&6 -else - #assume ordinary cross tools, or native build. -lt_cv_to_tool_file_cmd=func_convert_file_noop -case $host in - *-*-mingw* ) - case $build in - *-*-mingw* ) # actually msys - lt_cv_to_tool_file_cmd=func_convert_file_msys_to_w32 - ;; - esac - ;; -esac - -fi - -to_tool_file_cmd=$lt_cv_to_tool_file_cmd -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_to_tool_file_cmd" >&5 -$as_echo "$lt_cv_to_tool_file_cmd" >&6; } - - - - - -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $LD option to reload object files" >&5 -$as_echo_n "checking for $LD option to reload object files... " >&6; } -if ${lt_cv_ld_reload_flag+:} false; then : - $as_echo_n "(cached) " >&6 -else - lt_cv_ld_reload_flag='-r' -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_ld_reload_flag" >&5 -$as_echo "$lt_cv_ld_reload_flag" >&6; } -reload_flag=$lt_cv_ld_reload_flag -case $reload_flag in -"" | " "*) ;; -*) reload_flag=" $reload_flag" ;; -esac -reload_cmds='$LD$reload_flag -o $output$reload_objs' -case $host_os in - cygwin* | mingw* | pw32* | cegcc*) - if test "$GCC" != yes; then - reload_cmds=false - fi - ;; - darwin*) - if test "$GCC" = yes; then - reload_cmds='$LTCC $LTCFLAGS -nostdlib ${wl}-r -o $output$reload_objs' - else - reload_cmds='$LD$reload_flag -o $output$reload_objs' - fi - ;; -esac - - - - - - - - - -if test -n "$ac_tool_prefix"; then - # Extract the first word of "${ac_tool_prefix}objdump", so it can be a program name with args. -set dummy ${ac_tool_prefix}objdump; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_OBJDUMP+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -n "$OBJDUMP"; then - ac_cv_prog_OBJDUMP="$OBJDUMP" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_OBJDUMP="${ac_tool_prefix}objdump" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -OBJDUMP=$ac_cv_prog_OBJDUMP -if test -n "$OBJDUMP"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $OBJDUMP" >&5 -$as_echo "$OBJDUMP" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - -fi -if test -z "$ac_cv_prog_OBJDUMP"; then - ac_ct_OBJDUMP=$OBJDUMP - # Extract the first word of "objdump", so it can be a program name with args. -set dummy objdump; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_ac_ct_OBJDUMP+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -n "$ac_ct_OBJDUMP"; then - ac_cv_prog_ac_ct_OBJDUMP="$ac_ct_OBJDUMP" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_ac_ct_OBJDUMP="objdump" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -ac_ct_OBJDUMP=$ac_cv_prog_ac_ct_OBJDUMP -if test -n "$ac_ct_OBJDUMP"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_OBJDUMP" >&5 -$as_echo "$ac_ct_OBJDUMP" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - if test "x$ac_ct_OBJDUMP" = x; then - OBJDUMP="false" - else - case $cross_compiling:$ac_tool_warned in -yes:) -{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 -$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} -ac_tool_warned=yes ;; -esac - OBJDUMP=$ac_ct_OBJDUMP - fi -else - OBJDUMP="$ac_cv_prog_OBJDUMP" -fi - -test -z "$OBJDUMP" && OBJDUMP=objdump - - - - - - - - - -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to recognize dependent libraries" >&5 -$as_echo_n "checking how to recognize dependent libraries... " >&6; } -if ${lt_cv_deplibs_check_method+:} false; then : - $as_echo_n "(cached) " >&6 -else - lt_cv_file_magic_cmd='$MAGIC_CMD' -lt_cv_file_magic_test_file= -lt_cv_deplibs_check_method='unknown' -# Need to set the preceding variable on all platforms that support -# interlibrary dependencies. -# 'none' -- dependencies not supported. -# `unknown' -- same as none, but documents that we really don't know. -# 'pass_all' -- all dependencies passed with no checks. -# 'test_compile' -- check by making test program. -# 'file_magic [[regex]]' -- check by looking for files in library path -# which responds to the $file_magic_cmd with a given extended regex. -# If you have `file' or equivalent on your system and you're not sure -# whether `pass_all' will *always* work, you probably want this one. - -case $host_os in -aix[4-9]*) - lt_cv_deplibs_check_method=pass_all - ;; - -beos*) - lt_cv_deplibs_check_method=pass_all - ;; - -bsdi[45]*) - lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [ML]SB (shared object|dynamic lib)' - lt_cv_file_magic_cmd='/usr/bin/file -L' - lt_cv_file_magic_test_file=/shlib/libc.so - ;; - -cygwin*) - # func_win32_libid is a shell function defined in ltmain.sh - lt_cv_deplibs_check_method='file_magic ^x86 archive import|^x86 DLL' - lt_cv_file_magic_cmd='func_win32_libid' - ;; - -mingw* | pw32*) - # Base MSYS/MinGW do not provide the 'file' command needed by - # func_win32_libid shell function, so use a weaker test based on 'objdump', - # unless we find 'file', for example because we are cross-compiling. - # func_win32_libid assumes BSD nm, so disallow it if using MS dumpbin. - if ( test "$lt_cv_nm_interface" = "BSD nm" && file / ) >/dev/null 2>&1; then - lt_cv_deplibs_check_method='file_magic ^x86 archive import|^x86 DLL' - lt_cv_file_magic_cmd='func_win32_libid' - else - # Keep this pattern in sync with the one in func_win32_libid. - lt_cv_deplibs_check_method='file_magic file format (pei*-i386(.*architecture: i386)?|pe-arm-wince|pe-x86-64)' - lt_cv_file_magic_cmd='$OBJDUMP -f' - fi - ;; - -cegcc*) - # use the weaker test based on 'objdump'. See mingw*. - lt_cv_deplibs_check_method='file_magic file format pe-arm-.*little(.*architecture: arm)?' - lt_cv_file_magic_cmd='$OBJDUMP -f' - ;; - -darwin* | rhapsody*) - lt_cv_deplibs_check_method=pass_all - ;; - -freebsd* | dragonfly*) - if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then - case $host_cpu in - i*86 ) - # Not sure whether the presence of OpenBSD here was a mistake. - # Let's accept both of them until this is cleared up. - lt_cv_deplibs_check_method='file_magic (FreeBSD|OpenBSD|DragonFly)/i[3-9]86 (compact )?demand paged shared library' - lt_cv_file_magic_cmd=/usr/bin/file - lt_cv_file_magic_test_file=`echo /usr/lib/libc.so.*` - ;; - esac - else - lt_cv_deplibs_check_method=pass_all - fi - ;; - -haiku*) - lt_cv_deplibs_check_method=pass_all - ;; - -hpux10.20* | hpux11*) - lt_cv_file_magic_cmd=/usr/bin/file - case $host_cpu in - ia64*) - lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|ELF-[0-9][0-9]) shared object file - IA64' - lt_cv_file_magic_test_file=/usr/lib/hpux32/libc.so - ;; - hppa*64*) - lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|ELF[ -][0-9][0-9])(-bit)?( [LM]SB)? shared object( file)?[, -]* PA-RISC [0-9]\.[0-9]' - lt_cv_file_magic_test_file=/usr/lib/pa20_64/libc.sl - ;; - *) - lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|PA-RISC[0-9]\.[0-9]) shared library' - lt_cv_file_magic_test_file=/usr/lib/libc.sl - ;; - esac - ;; - -interix[3-9]*) - # PIC code is broken on Interix 3.x, that's why |\.a not |_pic\.a here - lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so|\.a)$' - ;; - -irix5* | irix6* | nonstopux*) - case $LD in - *-32|*"-32 ") libmagic=32-bit;; - *-n32|*"-n32 ") libmagic=N32;; - *-64|*"-64 ") libmagic=64-bit;; - *) libmagic=never-match;; - esac - lt_cv_deplibs_check_method=pass_all - ;; - -# This must be glibc/ELF. -linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) - lt_cv_deplibs_check_method=pass_all - ;; - -netbsd* | netbsdelf*-gnu) - if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then - lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so\.[0-9]+\.[0-9]+|_pic\.a)$' - else - lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so|_pic\.a)$' - fi - ;; - -newos6*) - lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [ML]SB (executable|dynamic lib)' - lt_cv_file_magic_cmd=/usr/bin/file - lt_cv_file_magic_test_file=/usr/lib/libnls.so - ;; - -*nto* | *qnx*) - lt_cv_deplibs_check_method=pass_all - ;; - -openbsd*) - if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then - lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so\.[0-9]+\.[0-9]+|\.so|_pic\.a)$' - else - lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so\.[0-9]+\.[0-9]+|_pic\.a)$' - fi - ;; - -osf3* | osf4* | osf5*) - lt_cv_deplibs_check_method=pass_all - ;; - -rdos*) - lt_cv_deplibs_check_method=pass_all - ;; - -solaris*) - lt_cv_deplibs_check_method=pass_all - ;; - -sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*) - lt_cv_deplibs_check_method=pass_all - ;; - -sysv4 | sysv4.3*) - case $host_vendor in - motorola) - lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [ML]SB (shared object|dynamic lib) M[0-9][0-9]* Version [0-9]' - lt_cv_file_magic_test_file=`echo /usr/lib/libc.so*` - ;; - ncr) - lt_cv_deplibs_check_method=pass_all - ;; - sequent) - lt_cv_file_magic_cmd='/bin/file' - lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [LM]SB (shared object|dynamic lib )' - ;; - sni) - lt_cv_file_magic_cmd='/bin/file' - lt_cv_deplibs_check_method="file_magic ELF [0-9][0-9]*-bit [LM]SB dynamic lib" - lt_cv_file_magic_test_file=/lib/libc.so - ;; - siemens) - lt_cv_deplibs_check_method=pass_all - ;; - pc) - lt_cv_deplibs_check_method=pass_all - ;; - esac - ;; - -tpf*) - lt_cv_deplibs_check_method=pass_all - ;; -esac - -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_deplibs_check_method" >&5 -$as_echo "$lt_cv_deplibs_check_method" >&6; } - -file_magic_glob= -want_nocaseglob=no -if test "$build" = "$host"; then - case $host_os in - mingw* | pw32*) - if ( shopt | grep nocaseglob ) >/dev/null 2>&1; then - want_nocaseglob=yes - else - file_magic_glob=`echo aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ | $SED -e "s/\(..\)/s\/[\1]\/[\1]\/g;/g"` - fi - ;; - esac -fi - -file_magic_cmd=$lt_cv_file_magic_cmd -deplibs_check_method=$lt_cv_deplibs_check_method -test -z "$deplibs_check_method" && deplibs_check_method=unknown - - - - - - - - - - - - - - - - - - - - - - -if test -n "$ac_tool_prefix"; then - # Extract the first word of "${ac_tool_prefix}dlltool", so it can be a program name with args. -set dummy ${ac_tool_prefix}dlltool; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_DLLTOOL+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -n "$DLLTOOL"; then - ac_cv_prog_DLLTOOL="$DLLTOOL" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_DLLTOOL="${ac_tool_prefix}dlltool" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -DLLTOOL=$ac_cv_prog_DLLTOOL -if test -n "$DLLTOOL"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $DLLTOOL" >&5 -$as_echo "$DLLTOOL" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - -fi -if test -z "$ac_cv_prog_DLLTOOL"; then - ac_ct_DLLTOOL=$DLLTOOL - # Extract the first word of "dlltool", so it can be a program name with args. -set dummy dlltool; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_ac_ct_DLLTOOL+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -n "$ac_ct_DLLTOOL"; then - ac_cv_prog_ac_ct_DLLTOOL="$ac_ct_DLLTOOL" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_ac_ct_DLLTOOL="dlltool" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -ac_ct_DLLTOOL=$ac_cv_prog_ac_ct_DLLTOOL -if test -n "$ac_ct_DLLTOOL"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_DLLTOOL" >&5 -$as_echo "$ac_ct_DLLTOOL" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - if test "x$ac_ct_DLLTOOL" = x; then - DLLTOOL="false" - else - case $cross_compiling:$ac_tool_warned in -yes:) -{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 -$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} -ac_tool_warned=yes ;; -esac - DLLTOOL=$ac_ct_DLLTOOL - fi -else - DLLTOOL="$ac_cv_prog_DLLTOOL" -fi - -test -z "$DLLTOOL" && DLLTOOL=dlltool - - - - - - - - - - -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to associate runtime and link libraries" >&5 -$as_echo_n "checking how to associate runtime and link libraries... " >&6; } -if ${lt_cv_sharedlib_from_linklib_cmd+:} false; then : - $as_echo_n "(cached) " >&6 -else - lt_cv_sharedlib_from_linklib_cmd='unknown' - -case $host_os in -cygwin* | mingw* | pw32* | cegcc*) - # two different shell functions defined in ltmain.sh - # decide which to use based on capabilities of $DLLTOOL - case `$DLLTOOL --help 2>&1` in - *--identify-strict*) - lt_cv_sharedlib_from_linklib_cmd=func_cygming_dll_for_implib - ;; - *) - lt_cv_sharedlib_from_linklib_cmd=func_cygming_dll_for_implib_fallback - ;; - esac - ;; -*) - # fallback: assume linklib IS sharedlib - lt_cv_sharedlib_from_linklib_cmd="$ECHO" - ;; -esac - -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_sharedlib_from_linklib_cmd" >&5 -$as_echo "$lt_cv_sharedlib_from_linklib_cmd" >&6; } -sharedlib_from_linklib_cmd=$lt_cv_sharedlib_from_linklib_cmd -test -z "$sharedlib_from_linklib_cmd" && sharedlib_from_linklib_cmd=$ECHO - - - - - - - -if test -n "$ac_tool_prefix"; then - for ac_prog in ar - do - # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. -set dummy $ac_tool_prefix$ac_prog; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_AR+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -n "$AR"; then - ac_cv_prog_AR="$AR" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_AR="$ac_tool_prefix$ac_prog" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -AR=$ac_cv_prog_AR -if test -n "$AR"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $AR" >&5 -$as_echo "$AR" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - - test -n "$AR" && break - done -fi -if test -z "$AR"; then - ac_ct_AR=$AR - for ac_prog in ar -do - # Extract the first word of "$ac_prog", so it can be a program name with args. -set dummy $ac_prog; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_ac_ct_AR+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -n "$ac_ct_AR"; then - ac_cv_prog_ac_ct_AR="$ac_ct_AR" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_ac_ct_AR="$ac_prog" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -ac_ct_AR=$ac_cv_prog_ac_ct_AR -if test -n "$ac_ct_AR"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_AR" >&5 -$as_echo "$ac_ct_AR" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - - test -n "$ac_ct_AR" && break -done - - if test "x$ac_ct_AR" = x; then - AR="false" - else - case $cross_compiling:$ac_tool_warned in -yes:) -{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 -$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} -ac_tool_warned=yes ;; -esac - AR=$ac_ct_AR - fi -fi - -: ${AR=ar} -: ${AR_FLAGS=cru} - - - - - - - - - - - -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for archiver @FILE support" >&5 -$as_echo_n "checking for archiver @FILE support... " >&6; } -if ${lt_cv_ar_at_file+:} false; then : - $as_echo_n "(cached) " >&6 -else - lt_cv_ar_at_file=no - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main () -{ - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - echo conftest.$ac_objext > conftest.lst - lt_ar_try='$AR $AR_FLAGS libconftest.a @conftest.lst >&5' - { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$lt_ar_try\""; } >&5 - (eval $lt_ar_try) 2>&5 - ac_status=$? - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; } - if test "$ac_status" -eq 0; then - # Ensure the archiver fails upon bogus file names. - rm -f conftest.$ac_objext libconftest.a - { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$lt_ar_try\""; } >&5 - (eval $lt_ar_try) 2>&5 - ac_status=$? - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; } - if test "$ac_status" -ne 0; then - lt_cv_ar_at_file=@ - fi - fi - rm -f conftest.* libconftest.a - -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext - -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_ar_at_file" >&5 -$as_echo "$lt_cv_ar_at_file" >&6; } - -if test "x$lt_cv_ar_at_file" = xno; then - archiver_list_spec= -else - archiver_list_spec=$lt_cv_ar_at_file -fi - - - - - - - -if test -n "$ac_tool_prefix"; then - # Extract the first word of "${ac_tool_prefix}strip", so it can be a program name with args. -set dummy ${ac_tool_prefix}strip; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_STRIP+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -n "$STRIP"; then - ac_cv_prog_STRIP="$STRIP" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_STRIP="${ac_tool_prefix}strip" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -STRIP=$ac_cv_prog_STRIP -if test -n "$STRIP"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $STRIP" >&5 -$as_echo "$STRIP" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - -fi -if test -z "$ac_cv_prog_STRIP"; then - ac_ct_STRIP=$STRIP - # Extract the first word of "strip", so it can be a program name with args. -set dummy strip; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_ac_ct_STRIP+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -n "$ac_ct_STRIP"; then - ac_cv_prog_ac_ct_STRIP="$ac_ct_STRIP" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_ac_ct_STRIP="strip" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -ac_ct_STRIP=$ac_cv_prog_ac_ct_STRIP -if test -n "$ac_ct_STRIP"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_STRIP" >&5 -$as_echo "$ac_ct_STRIP" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - if test "x$ac_ct_STRIP" = x; then - STRIP=":" - else - case $cross_compiling:$ac_tool_warned in -yes:) -{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 -$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} -ac_tool_warned=yes ;; -esac - STRIP=$ac_ct_STRIP - fi -else - STRIP="$ac_cv_prog_STRIP" -fi - -test -z "$STRIP" && STRIP=: - - - - - - -if test -n "$ac_tool_prefix"; then - # Extract the first word of "${ac_tool_prefix}ranlib", so it can be a program name with args. -set dummy ${ac_tool_prefix}ranlib; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_RANLIB+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -n "$RANLIB"; then - ac_cv_prog_RANLIB="$RANLIB" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_RANLIB="${ac_tool_prefix}ranlib" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -RANLIB=$ac_cv_prog_RANLIB -if test -n "$RANLIB"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $RANLIB" >&5 -$as_echo "$RANLIB" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - -fi -if test -z "$ac_cv_prog_RANLIB"; then - ac_ct_RANLIB=$RANLIB - # Extract the first word of "ranlib", so it can be a program name with args. -set dummy ranlib; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_ac_ct_RANLIB+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -n "$ac_ct_RANLIB"; then - ac_cv_prog_ac_ct_RANLIB="$ac_ct_RANLIB" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_ac_ct_RANLIB="ranlib" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -ac_ct_RANLIB=$ac_cv_prog_ac_ct_RANLIB -if test -n "$ac_ct_RANLIB"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_RANLIB" >&5 -$as_echo "$ac_ct_RANLIB" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - if test "x$ac_ct_RANLIB" = x; then - RANLIB=":" - else - case $cross_compiling:$ac_tool_warned in -yes:) -{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 -$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} -ac_tool_warned=yes ;; -esac - RANLIB=$ac_ct_RANLIB - fi -else - RANLIB="$ac_cv_prog_RANLIB" -fi - -test -z "$RANLIB" && RANLIB=: - - - - - - -# Determine commands to create old-style static archives. -old_archive_cmds='$AR $AR_FLAGS $oldlib$oldobjs' -old_postinstall_cmds='chmod 644 $oldlib' -old_postuninstall_cmds= - -if test -n "$RANLIB"; then - case $host_os in - openbsd*) - old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB -t \$tool_oldlib" - ;; - *) - old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB \$tool_oldlib" - ;; - esac - old_archive_cmds="$old_archive_cmds~\$RANLIB \$tool_oldlib" -fi - -case $host_os in - darwin*) - lock_old_archive_extraction=yes ;; - *) - lock_old_archive_extraction=no ;; -esac - - - - - - - - - - - - - - - - - - - - - -for ac_prog in gawk mawk nawk awk -do - # Extract the first word of "$ac_prog", so it can be a program name with args. -set dummy $ac_prog; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_AWK+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -n "$AWK"; then - ac_cv_prog_AWK="$AWK" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_AWK="$ac_prog" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -AWK=$ac_cv_prog_AWK -if test -n "$AWK"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $AWK" >&5 -$as_echo "$AWK" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - - test -n "$AWK" && break -done - - - - - - - - - - - - - - - - - - - -# If no C compiler was specified, use CC. -LTCC=${LTCC-"$CC"} - -# If no C compiler flags were specified, use CFLAGS. -LTCFLAGS=${LTCFLAGS-"$CFLAGS"} - -# Allow CC to be a program name with arguments. -compiler=$CC - - -# Check for command to grab the raw symbol name followed by C symbol from nm. -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking command to parse $NM output from $compiler object" >&5 -$as_echo_n "checking command to parse $NM output from $compiler object... " >&6; } -if ${lt_cv_sys_global_symbol_pipe+:} false; then : - $as_echo_n "(cached) " >&6 -else - -# These are sane defaults that work on at least a few old systems. -# [They come from Ultrix. What could be older than Ultrix?!! ;)] - -# Character class describing NM global symbol codes. -symcode='[BCDEGRST]' - -# Regexp to match symbols that can be accessed directly from C. -sympat='\([_A-Za-z][_A-Za-z0-9]*\)' - -# Define system-specific variables. -case $host_os in -aix*) - symcode='[BCDT]' - ;; -cygwin* | mingw* | pw32* | cegcc*) - symcode='[ABCDGISTW]' - ;; -hpux*) - if test "$host_cpu" = ia64; then - symcode='[ABCDEGRST]' - fi - ;; -irix* | nonstopux*) - symcode='[BCDEGRST]' - ;; -osf*) - symcode='[BCDEGQRST]' - ;; -solaris*) - symcode='[BDRT]' - ;; -sco3.2v5*) - symcode='[DT]' - ;; -sysv4.2uw2*) - symcode='[DT]' - ;; -sysv5* | sco5v6* | unixware* | OpenUNIX*) - symcode='[ABDT]' - ;; -sysv4) - symcode='[DFNSTU]' - ;; -esac - -# If we're using GNU nm, then use its standard symbol codes. -case `$NM -V 2>&1` in -*GNU* | *'with BFD'*) - symcode='[ABCDGIRSTW]' ;; -esac - -# Transform an extracted symbol line into a proper C declaration. -# Some systems (esp. on ia64) link data and code symbols differently, -# so use this general approach. -lt_cv_sys_global_symbol_to_cdecl="sed -n -e 's/^T .* \(.*\)$/extern int \1();/p' -e 's/^$symcode* .* \(.*\)$/extern char \1;/p'" - -# Transform an extracted symbol line into symbol name and symbol address -lt_cv_sys_global_symbol_to_c_name_address="sed -n -e 's/^: \([^ ]*\)[ ]*$/ {\\\"\1\\\", (void *) 0},/p' -e 's/^$symcode* \([^ ]*\) \([^ ]*\)$/ {\"\2\", (void *) \&\2},/p'" -lt_cv_sys_global_symbol_to_c_name_address_lib_prefix="sed -n -e 's/^: \([^ ]*\)[ ]*$/ {\\\"\1\\\", (void *) 0},/p' -e 's/^$symcode* \([^ ]*\) \(lib[^ ]*\)$/ {\"\2\", (void *) \&\2},/p' -e 's/^$symcode* \([^ ]*\) \([^ ]*\)$/ {\"lib\2\", (void *) \&\2},/p'" - -# Handle CRLF in mingw tool chain -opt_cr= -case $build_os in -mingw*) - opt_cr=`$ECHO 'x\{0,1\}' | tr x '\015'` # option cr in regexp - ;; -esac - -# Try without a prefix underscore, then with it. -for ac_symprfx in "" "_"; do - - # Transform symcode, sympat, and symprfx into a raw symbol and a C symbol. - symxfrm="\\1 $ac_symprfx\\2 \\2" - - # Write the raw and C identifiers. - if test "$lt_cv_nm_interface" = "MS dumpbin"; then - # Fake it for dumpbin and say T for any non-static function - # and D for any global variable. - # Also find C++ and __fastcall symbols from MSVC++, - # which start with @ or ?. - lt_cv_sys_global_symbol_pipe="$AWK '"\ -" {last_section=section; section=\$ 3};"\ -" /^COFF SYMBOL TABLE/{for(i in hide) delete hide[i]};"\ -" /Section length .*#relocs.*(pick any)/{hide[last_section]=1};"\ -" \$ 0!~/External *\|/{next};"\ -" / 0+ UNDEF /{next}; / UNDEF \([^|]\)*()/{next};"\ -" {if(hide[section]) next};"\ -" {f=0}; \$ 0~/\(\).*\|/{f=1}; {printf f ? \"T \" : \"D \"};"\ -" {split(\$ 0, a, /\||\r/); split(a[2], s)};"\ -" s[1]~/^[@?]/{print s[1], s[1]; next};"\ -" s[1]~prfx {split(s[1],t,\"@\"); print t[1], substr(t[1],length(prfx))}"\ -" ' prfx=^$ac_symprfx" - else - lt_cv_sys_global_symbol_pipe="sed -n -e 's/^.*[ ]\($symcode$symcode*\)[ ][ ]*$ac_symprfx$sympat$opt_cr$/$symxfrm/p'" - fi - lt_cv_sys_global_symbol_pipe="$lt_cv_sys_global_symbol_pipe | sed '/ __gnu_lto/d'" - - # Check to see that the pipe works correctly. - pipe_works=no - - rm -f conftest* - cat > conftest.$ac_ext <<_LT_EOF -#ifdef __cplusplus -extern "C" { -#endif -char nm_test_var; -void nm_test_func(void); -void nm_test_func(void){} -#ifdef __cplusplus -} -#endif -int main(){nm_test_var='a';nm_test_func();return(0);} -_LT_EOF - - if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 - (eval $ac_compile) 2>&5 - ac_status=$? - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; }; then - # Now try to grab the symbols. - nlist=conftest.nm - if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$NM conftest.$ac_objext \| "$lt_cv_sys_global_symbol_pipe" \> $nlist\""; } >&5 - (eval $NM conftest.$ac_objext \| "$lt_cv_sys_global_symbol_pipe" \> $nlist) 2>&5 - ac_status=$? - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; } && test -s "$nlist"; then - # Try sorting and uniquifying the output. - if sort "$nlist" | uniq > "$nlist"T; then - mv -f "$nlist"T "$nlist" - else - rm -f "$nlist"T - fi - - # Make sure that we snagged all the symbols we need. - if $GREP ' nm_test_var$' "$nlist" >/dev/null; then - if $GREP ' nm_test_func$' "$nlist" >/dev/null; then - cat <<_LT_EOF > conftest.$ac_ext -/* Keep this code in sync between libtool.m4, ltmain, lt_system.h, and tests. */ -#if defined(_WIN32) || defined(__CYGWIN__) || defined(_WIN32_WCE) -/* DATA imports from DLLs on WIN32 con't be const, because runtime - relocations are performed -- see ld's documentation on pseudo-relocs. */ -# define LT_DLSYM_CONST -#elif defined(__osf__) -/* This system does not cope well with relocations in const data. */ -# define LT_DLSYM_CONST -#else -# define LT_DLSYM_CONST const -#endif - -#ifdef __cplusplus -extern "C" { -#endif - -_LT_EOF - # Now generate the symbol file. - eval "$lt_cv_sys_global_symbol_to_cdecl"' < "$nlist" | $GREP -v main >> conftest.$ac_ext' - - cat <<_LT_EOF >> conftest.$ac_ext - -/* The mapping between symbol names and symbols. */ -LT_DLSYM_CONST struct { - const char *name; - void *address; -} -lt__PROGRAM__LTX_preloaded_symbols[] = -{ - { "@PROGRAM@", (void *) 0 }, -_LT_EOF - $SED "s/^$symcode$symcode* \(.*\) \(.*\)$/ {\"\2\", (void *) \&\2},/" < "$nlist" | $GREP -v main >> conftest.$ac_ext - cat <<\_LT_EOF >> conftest.$ac_ext - {0, (void *) 0} -}; - -/* This works around a problem in FreeBSD linker */ -#ifdef FREEBSD_WORKAROUND -static const void *lt_preloaded_setup() { - return lt__PROGRAM__LTX_preloaded_symbols; -} -#endif - -#ifdef __cplusplus -} -#endif -_LT_EOF - # Now try linking the two files. - mv conftest.$ac_objext conftstm.$ac_objext - lt_globsym_save_LIBS=$LIBS - lt_globsym_save_CFLAGS=$CFLAGS - LIBS="conftstm.$ac_objext" - CFLAGS="$CFLAGS$lt_prog_compiler_no_builtin_flag" - if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_link\""; } >&5 - (eval $ac_link) 2>&5 - ac_status=$? - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; } && test -s conftest${ac_exeext}; then - pipe_works=yes - fi - LIBS=$lt_globsym_save_LIBS - CFLAGS=$lt_globsym_save_CFLAGS - else - echo "cannot find nm_test_func in $nlist" >&5 - fi - else - echo "cannot find nm_test_var in $nlist" >&5 - fi - else - echo "cannot run $lt_cv_sys_global_symbol_pipe" >&5 - fi - else - echo "$progname: failed program was:" >&5 - cat conftest.$ac_ext >&5 - fi - rm -rf conftest* conftst* - - # Do not use the global_symbol_pipe unless it works. - if test "$pipe_works" = yes; then - break - else - lt_cv_sys_global_symbol_pipe= - fi -done - -fi - -if test -z "$lt_cv_sys_global_symbol_pipe"; then - lt_cv_sys_global_symbol_to_cdecl= -fi -if test -z "$lt_cv_sys_global_symbol_pipe$lt_cv_sys_global_symbol_to_cdecl"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: failed" >&5 -$as_echo "failed" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: ok" >&5 -$as_echo "ok" >&6; } -fi - -# Response file support. -if test "$lt_cv_nm_interface" = "MS dumpbin"; then - nm_file_list_spec='@' -elif $NM --help 2>/dev/null | grep '[@]FILE' >/dev/null; then - nm_file_list_spec='@' -fi - - - - - - - - - - - - - - - - - - - - - - - - - - - -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for sysroot" >&5 -$as_echo_n "checking for sysroot... " >&6; } - -# Check whether --with-sysroot was given. -if test "${with_sysroot+set}" = set; then : - withval=$with_sysroot; -else - with_sysroot=no -fi - - -lt_sysroot= -case ${with_sysroot} in #( - yes) - if test "$GCC" = yes; then - lt_sysroot=`$CC --print-sysroot 2>/dev/null` - fi - ;; #( - /*) - lt_sysroot=`echo "$with_sysroot" | sed -e "$sed_quote_subst"` - ;; #( - no|'') - ;; #( - *) - { $as_echo "$as_me:${as_lineno-$LINENO}: result: ${with_sysroot}" >&5 -$as_echo "${with_sysroot}" >&6; } - as_fn_error $? "The sysroot must be an absolute path." "$LINENO" 5 - ;; -esac - - { $as_echo "$as_me:${as_lineno-$LINENO}: result: ${lt_sysroot:-no}" >&5 -$as_echo "${lt_sysroot:-no}" >&6; } - - - - - -# Check whether --enable-libtool-lock was given. -if test "${enable_libtool_lock+set}" = set; then : - enableval=$enable_libtool_lock; -fi - -test "x$enable_libtool_lock" != xno && enable_libtool_lock=yes - -# Some flags need to be propagated to the compiler or linker for good -# libtool support. -case $host in -ia64-*-hpux*) - # Find out which ABI we are using. - echo 'int i;' > conftest.$ac_ext - if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 - (eval $ac_compile) 2>&5 - ac_status=$? - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; }; then - case `/usr/bin/file conftest.$ac_objext` in - *ELF-32*) - HPUX_IA64_MODE="32" - ;; - *ELF-64*) - HPUX_IA64_MODE="64" - ;; - esac - fi - rm -rf conftest* - ;; -*-*-irix6*) - # Find out which ABI we are using. - echo '#line '$LINENO' "configure"' > conftest.$ac_ext - if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 - (eval $ac_compile) 2>&5 - ac_status=$? - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; }; then - if test "$lt_cv_prog_gnu_ld" = yes; then - case `/usr/bin/file conftest.$ac_objext` in - *32-bit*) - LD="${LD-ld} -melf32bsmip" - ;; - *N32*) - LD="${LD-ld} -melf32bmipn32" - ;; - *64-bit*) - LD="${LD-ld} -melf64bmip" - ;; - esac - else - case `/usr/bin/file conftest.$ac_objext` in - *32-bit*) - LD="${LD-ld} -32" - ;; - *N32*) - LD="${LD-ld} -n32" - ;; - *64-bit*) - LD="${LD-ld} -64" - ;; - esac - fi - fi - rm -rf conftest* - ;; - -x86_64-*kfreebsd*-gnu|x86_64-*linux*|powerpc*-*linux*| \ -s390*-*linux*|s390*-*tpf*|sparc*-*linux*) - # Find out which ABI we are using. - echo 'int i;' > conftest.$ac_ext - if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 - (eval $ac_compile) 2>&5 - ac_status=$? - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; }; then - case `/usr/bin/file conftest.o` in - *32-bit*) - case $host in - x86_64-*kfreebsd*-gnu) - LD="${LD-ld} -m elf_i386_fbsd" - ;; - x86_64-*linux*) - case `/usr/bin/file conftest.o` in - *x86-64*) - LD="${LD-ld} -m elf32_x86_64" - ;; - *) - LD="${LD-ld} -m elf_i386" - ;; - esac - ;; - powerpc64le-*) - LD="${LD-ld} -m elf32lppclinux" - ;; - powerpc64-*) - LD="${LD-ld} -m elf32ppclinux" - ;; - s390x-*linux*) - LD="${LD-ld} -m elf_s390" - ;; - sparc64-*linux*) - LD="${LD-ld} -m elf32_sparc" - ;; - esac - ;; - *64-bit*) - case $host in - x86_64-*kfreebsd*-gnu) - LD="${LD-ld} -m elf_x86_64_fbsd" - ;; - x86_64-*linux*) - LD="${LD-ld} -m elf_x86_64" - ;; - powerpcle-*) - LD="${LD-ld} -m elf64lppc" - ;; - powerpc-*) - LD="${LD-ld} -m elf64ppc" - ;; - s390*-*linux*|s390*-*tpf*) - LD="${LD-ld} -m elf64_s390" - ;; - sparc*-*linux*) - LD="${LD-ld} -m elf64_sparc" - ;; - esac - ;; - esac - fi - rm -rf conftest* - ;; - -*-*-sco3.2v5*) - # On SCO OpenServer 5, we need -belf to get full-featured binaries. - SAVE_CFLAGS="$CFLAGS" - CFLAGS="$CFLAGS -belf" - { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C compiler needs -belf" >&5 -$as_echo_n "checking whether the C compiler needs -belf... " >&6; } -if ${lt_cv_cc_needs_belf+:} false; then : - $as_echo_n "(cached) " >&6 -else - ac_ext=c -ac_cpp='$CPP $CPPFLAGS' -ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_c_compiler_gnu - - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main () -{ - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_link "$LINENO"; then : - lt_cv_cc_needs_belf=yes -else - lt_cv_cc_needs_belf=no -fi -rm -f core conftest.err conftest.$ac_objext \ - conftest$ac_exeext conftest.$ac_ext - ac_ext=c -ac_cpp='$CPP $CPPFLAGS' -ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_c_compiler_gnu - -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_cc_needs_belf" >&5 -$as_echo "$lt_cv_cc_needs_belf" >&6; } - if test x"$lt_cv_cc_needs_belf" != x"yes"; then - # this is probably gcc 2.8.0, egcs 1.0 or newer; no need for -belf - CFLAGS="$SAVE_CFLAGS" - fi - ;; -*-*solaris*) - # Find out which ABI we are using. - echo 'int i;' > conftest.$ac_ext - if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 - (eval $ac_compile) 2>&5 - ac_status=$? - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; }; then - case `/usr/bin/file conftest.o` in - *64-bit*) - case $lt_cv_prog_gnu_ld in - yes*) - case $host in - i?86-*-solaris*) - LD="${LD-ld} -m elf_x86_64" - ;; - sparc*-*-solaris*) - LD="${LD-ld} -m elf64_sparc" - ;; - esac - # GNU ld 2.21 introduced _sol2 emulations. Use them if available. - if ${LD-ld} -V | grep _sol2 >/dev/null 2>&1; then - LD="${LD-ld}_sol2" - fi - ;; - *) - if ${LD-ld} -64 -r -o conftest2.o conftest.o >/dev/null 2>&1; then - LD="${LD-ld} -64" - fi - ;; - esac - ;; - esac - fi - rm -rf conftest* - ;; -esac - -need_locks="$enable_libtool_lock" - -if test -n "$ac_tool_prefix"; then - # Extract the first word of "${ac_tool_prefix}mt", so it can be a program name with args. -set dummy ${ac_tool_prefix}mt; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_MANIFEST_TOOL+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -n "$MANIFEST_TOOL"; then - ac_cv_prog_MANIFEST_TOOL="$MANIFEST_TOOL" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_MANIFEST_TOOL="${ac_tool_prefix}mt" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -MANIFEST_TOOL=$ac_cv_prog_MANIFEST_TOOL -if test -n "$MANIFEST_TOOL"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $MANIFEST_TOOL" >&5 -$as_echo "$MANIFEST_TOOL" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - -fi -if test -z "$ac_cv_prog_MANIFEST_TOOL"; then - ac_ct_MANIFEST_TOOL=$MANIFEST_TOOL - # Extract the first word of "mt", so it can be a program name with args. -set dummy mt; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_ac_ct_MANIFEST_TOOL+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -n "$ac_ct_MANIFEST_TOOL"; then - ac_cv_prog_ac_ct_MANIFEST_TOOL="$ac_ct_MANIFEST_TOOL" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_ac_ct_MANIFEST_TOOL="mt" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -ac_ct_MANIFEST_TOOL=$ac_cv_prog_ac_ct_MANIFEST_TOOL -if test -n "$ac_ct_MANIFEST_TOOL"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_MANIFEST_TOOL" >&5 -$as_echo "$ac_ct_MANIFEST_TOOL" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - if test "x$ac_ct_MANIFEST_TOOL" = x; then - MANIFEST_TOOL=":" - else - case $cross_compiling:$ac_tool_warned in -yes:) -{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 -$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} -ac_tool_warned=yes ;; -esac - MANIFEST_TOOL=$ac_ct_MANIFEST_TOOL - fi -else - MANIFEST_TOOL="$ac_cv_prog_MANIFEST_TOOL" -fi - -test -z "$MANIFEST_TOOL" && MANIFEST_TOOL=mt -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if $MANIFEST_TOOL is a manifest tool" >&5 -$as_echo_n "checking if $MANIFEST_TOOL is a manifest tool... " >&6; } -if ${lt_cv_path_mainfest_tool+:} false; then : - $as_echo_n "(cached) " >&6 -else - lt_cv_path_mainfest_tool=no - echo "$as_me:$LINENO: $MANIFEST_TOOL '-?'" >&5 - $MANIFEST_TOOL '-?' 2>conftest.err > conftest.out - cat conftest.err >&5 - if $GREP 'Manifest Tool' conftest.out > /dev/null; then - lt_cv_path_mainfest_tool=yes - fi - rm -f conftest* -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_path_mainfest_tool" >&5 -$as_echo "$lt_cv_path_mainfest_tool" >&6; } -if test "x$lt_cv_path_mainfest_tool" != xyes; then - MANIFEST_TOOL=: -fi - - - - - - - case $host_os in - rhapsody* | darwin*) - if test -n "$ac_tool_prefix"; then - # Extract the first word of "${ac_tool_prefix}dsymutil", so it can be a program name with args. -set dummy ${ac_tool_prefix}dsymutil; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_DSYMUTIL+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -n "$DSYMUTIL"; then - ac_cv_prog_DSYMUTIL="$DSYMUTIL" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_DSYMUTIL="${ac_tool_prefix}dsymutil" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -DSYMUTIL=$ac_cv_prog_DSYMUTIL -if test -n "$DSYMUTIL"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $DSYMUTIL" >&5 -$as_echo "$DSYMUTIL" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - -fi -if test -z "$ac_cv_prog_DSYMUTIL"; then - ac_ct_DSYMUTIL=$DSYMUTIL - # Extract the first word of "dsymutil", so it can be a program name with args. -set dummy dsymutil; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_ac_ct_DSYMUTIL+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -n "$ac_ct_DSYMUTIL"; then - ac_cv_prog_ac_ct_DSYMUTIL="$ac_ct_DSYMUTIL" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_ac_ct_DSYMUTIL="dsymutil" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -ac_ct_DSYMUTIL=$ac_cv_prog_ac_ct_DSYMUTIL -if test -n "$ac_ct_DSYMUTIL"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_DSYMUTIL" >&5 -$as_echo "$ac_ct_DSYMUTIL" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - if test "x$ac_ct_DSYMUTIL" = x; then - DSYMUTIL=":" - else - case $cross_compiling:$ac_tool_warned in -yes:) -{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 -$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} -ac_tool_warned=yes ;; -esac - DSYMUTIL=$ac_ct_DSYMUTIL - fi -else - DSYMUTIL="$ac_cv_prog_DSYMUTIL" -fi - - if test -n "$ac_tool_prefix"; then - # Extract the first word of "${ac_tool_prefix}nmedit", so it can be a program name with args. -set dummy ${ac_tool_prefix}nmedit; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_NMEDIT+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -n "$NMEDIT"; then - ac_cv_prog_NMEDIT="$NMEDIT" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_NMEDIT="${ac_tool_prefix}nmedit" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -NMEDIT=$ac_cv_prog_NMEDIT -if test -n "$NMEDIT"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $NMEDIT" >&5 -$as_echo "$NMEDIT" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - -fi -if test -z "$ac_cv_prog_NMEDIT"; then - ac_ct_NMEDIT=$NMEDIT - # Extract the first word of "nmedit", so it can be a program name with args. -set dummy nmedit; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_ac_ct_NMEDIT+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -n "$ac_ct_NMEDIT"; then - ac_cv_prog_ac_ct_NMEDIT="$ac_ct_NMEDIT" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_ac_ct_NMEDIT="nmedit" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -ac_ct_NMEDIT=$ac_cv_prog_ac_ct_NMEDIT -if test -n "$ac_ct_NMEDIT"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_NMEDIT" >&5 -$as_echo "$ac_ct_NMEDIT" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - if test "x$ac_ct_NMEDIT" = x; then - NMEDIT=":" - else - case $cross_compiling:$ac_tool_warned in -yes:) -{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 -$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} -ac_tool_warned=yes ;; -esac - NMEDIT=$ac_ct_NMEDIT - fi -else - NMEDIT="$ac_cv_prog_NMEDIT" -fi - - if test -n "$ac_tool_prefix"; then - # Extract the first word of "${ac_tool_prefix}lipo", so it can be a program name with args. -set dummy ${ac_tool_prefix}lipo; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_LIPO+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -n "$LIPO"; then - ac_cv_prog_LIPO="$LIPO" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_LIPO="${ac_tool_prefix}lipo" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -LIPO=$ac_cv_prog_LIPO -if test -n "$LIPO"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $LIPO" >&5 -$as_echo "$LIPO" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - -fi -if test -z "$ac_cv_prog_LIPO"; then - ac_ct_LIPO=$LIPO - # Extract the first word of "lipo", so it can be a program name with args. -set dummy lipo; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_ac_ct_LIPO+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -n "$ac_ct_LIPO"; then - ac_cv_prog_ac_ct_LIPO="$ac_ct_LIPO" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_ac_ct_LIPO="lipo" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -ac_ct_LIPO=$ac_cv_prog_ac_ct_LIPO -if test -n "$ac_ct_LIPO"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_LIPO" >&5 -$as_echo "$ac_ct_LIPO" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - if test "x$ac_ct_LIPO" = x; then - LIPO=":" - else - case $cross_compiling:$ac_tool_warned in -yes:) -{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 -$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} -ac_tool_warned=yes ;; -esac - LIPO=$ac_ct_LIPO - fi -else - LIPO="$ac_cv_prog_LIPO" -fi - - if test -n "$ac_tool_prefix"; then - # Extract the first word of "${ac_tool_prefix}otool", so it can be a program name with args. -set dummy ${ac_tool_prefix}otool; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_OTOOL+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -n "$OTOOL"; then - ac_cv_prog_OTOOL="$OTOOL" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_OTOOL="${ac_tool_prefix}otool" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -OTOOL=$ac_cv_prog_OTOOL -if test -n "$OTOOL"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $OTOOL" >&5 -$as_echo "$OTOOL" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - -fi -if test -z "$ac_cv_prog_OTOOL"; then - ac_ct_OTOOL=$OTOOL - # Extract the first word of "otool", so it can be a program name with args. -set dummy otool; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_ac_ct_OTOOL+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -n "$ac_ct_OTOOL"; then - ac_cv_prog_ac_ct_OTOOL="$ac_ct_OTOOL" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_ac_ct_OTOOL="otool" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -ac_ct_OTOOL=$ac_cv_prog_ac_ct_OTOOL -if test -n "$ac_ct_OTOOL"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_OTOOL" >&5 -$as_echo "$ac_ct_OTOOL" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - if test "x$ac_ct_OTOOL" = x; then - OTOOL=":" - else - case $cross_compiling:$ac_tool_warned in -yes:) -{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 -$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} -ac_tool_warned=yes ;; -esac - OTOOL=$ac_ct_OTOOL - fi -else - OTOOL="$ac_cv_prog_OTOOL" -fi - - if test -n "$ac_tool_prefix"; then - # Extract the first word of "${ac_tool_prefix}otool64", so it can be a program name with args. -set dummy ${ac_tool_prefix}otool64; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_OTOOL64+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -n "$OTOOL64"; then - ac_cv_prog_OTOOL64="$OTOOL64" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_OTOOL64="${ac_tool_prefix}otool64" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -OTOOL64=$ac_cv_prog_OTOOL64 -if test -n "$OTOOL64"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $OTOOL64" >&5 -$as_echo "$OTOOL64" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - -fi -if test -z "$ac_cv_prog_OTOOL64"; then - ac_ct_OTOOL64=$OTOOL64 - # Extract the first word of "otool64", so it can be a program name with args. -set dummy otool64; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_ac_ct_OTOOL64+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -n "$ac_ct_OTOOL64"; then - ac_cv_prog_ac_ct_OTOOL64="$ac_ct_OTOOL64" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_ac_ct_OTOOL64="otool64" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -ac_ct_OTOOL64=$ac_cv_prog_ac_ct_OTOOL64 -if test -n "$ac_ct_OTOOL64"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_OTOOL64" >&5 -$as_echo "$ac_ct_OTOOL64" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - if test "x$ac_ct_OTOOL64" = x; then - OTOOL64=":" - else - case $cross_compiling:$ac_tool_warned in -yes:) -{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 -$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} -ac_tool_warned=yes ;; -esac - OTOOL64=$ac_ct_OTOOL64 - fi -else - OTOOL64="$ac_cv_prog_OTOOL64" -fi - - - - - - - - - - - - - - - - - - - - - - - - - - - - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for -single_module linker flag" >&5 -$as_echo_n "checking for -single_module linker flag... " >&6; } -if ${lt_cv_apple_cc_single_mod+:} false; then : - $as_echo_n "(cached) " >&6 -else - lt_cv_apple_cc_single_mod=no - if test -z "${LT_MULTI_MODULE}"; then - # By default we will add the -single_module flag. You can override - # by either setting the environment variable LT_MULTI_MODULE - # non-empty at configure time, or by adding -multi_module to the - # link flags. - rm -rf libconftest.dylib* - echo "int foo(void){return 1;}" > conftest.c - echo "$LTCC $LTCFLAGS $LDFLAGS -o libconftest.dylib \ --dynamiclib -Wl,-single_module conftest.c" >&5 - $LTCC $LTCFLAGS $LDFLAGS -o libconftest.dylib \ - -dynamiclib -Wl,-single_module conftest.c 2>conftest.err - _lt_result=$? - # If there is a non-empty error log, and "single_module" - # appears in it, assume the flag caused a linker warning - if test -s conftest.err && $GREP single_module conftest.err; then - cat conftest.err >&5 - # Otherwise, if the output was created with a 0 exit code from - # the compiler, it worked. - elif test -f libconftest.dylib && test $_lt_result -eq 0; then - lt_cv_apple_cc_single_mod=yes - else - cat conftest.err >&5 - fi - rm -rf libconftest.dylib* - rm -f conftest.* - fi -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_apple_cc_single_mod" >&5 -$as_echo "$lt_cv_apple_cc_single_mod" >&6; } - - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for -exported_symbols_list linker flag" >&5 -$as_echo_n "checking for -exported_symbols_list linker flag... " >&6; } -if ${lt_cv_ld_exported_symbols_list+:} false; then : - $as_echo_n "(cached) " >&6 -else - lt_cv_ld_exported_symbols_list=no - save_LDFLAGS=$LDFLAGS - echo "_main" > conftest.sym - LDFLAGS="$LDFLAGS -Wl,-exported_symbols_list,conftest.sym" - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main () -{ - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_link "$LINENO"; then : - lt_cv_ld_exported_symbols_list=yes -else - lt_cv_ld_exported_symbols_list=no -fi -rm -f core conftest.err conftest.$ac_objext \ - conftest$ac_exeext conftest.$ac_ext - LDFLAGS="$save_LDFLAGS" - -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_ld_exported_symbols_list" >&5 -$as_echo "$lt_cv_ld_exported_symbols_list" >&6; } - - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for -force_load linker flag" >&5 -$as_echo_n "checking for -force_load linker flag... " >&6; } -if ${lt_cv_ld_force_load+:} false; then : - $as_echo_n "(cached) " >&6 -else - lt_cv_ld_force_load=no - cat > conftest.c << _LT_EOF -int forced_loaded() { return 2;} -_LT_EOF - echo "$LTCC $LTCFLAGS -c -o conftest.o conftest.c" >&5 - $LTCC $LTCFLAGS -c -o conftest.o conftest.c 2>&5 - echo "$AR cru libconftest.a conftest.o" >&5 - $AR cru libconftest.a conftest.o 2>&5 - echo "$RANLIB libconftest.a" >&5 - $RANLIB libconftest.a 2>&5 - cat > conftest.c << _LT_EOF -int main() { return 0;} -_LT_EOF - echo "$LTCC $LTCFLAGS $LDFLAGS -o conftest conftest.c -Wl,-force_load,./libconftest.a" >&5 - $LTCC $LTCFLAGS $LDFLAGS -o conftest conftest.c -Wl,-force_load,./libconftest.a 2>conftest.err - _lt_result=$? - if test -s conftest.err && $GREP force_load conftest.err; then - cat conftest.err >&5 - elif test -f conftest && test $_lt_result -eq 0 && $GREP forced_load conftest >/dev/null 2>&1 ; then - lt_cv_ld_force_load=yes - else - cat conftest.err >&5 - fi - rm -f conftest.err libconftest.a conftest conftest.c - rm -rf conftest.dSYM - -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_ld_force_load" >&5 -$as_echo "$lt_cv_ld_force_load" >&6; } - case $host_os in - rhapsody* | darwin1.[012]) - _lt_dar_allow_undefined='${wl}-undefined ${wl}suppress' ;; - darwin1.*) - _lt_dar_allow_undefined='${wl}-flat_namespace ${wl}-undefined ${wl}suppress' ;; - darwin*) # darwin 5.x on - # if running on 10.5 or later, the deployment target defaults - # to the OS version, if on x86, and 10.4, the deployment - # target defaults to 10.4. Don't you love it? - case ${MACOSX_DEPLOYMENT_TARGET-10.0},$host in - 10.0,*86*-darwin8*|10.0,*-darwin[91]*) - _lt_dar_allow_undefined='${wl}-undefined ${wl}dynamic_lookup' ;; - 10.[012]*) - _lt_dar_allow_undefined='${wl}-flat_namespace ${wl}-undefined ${wl}suppress' ;; - 10.*) - _lt_dar_allow_undefined='${wl}-undefined ${wl}dynamic_lookup' ;; - esac - ;; - esac - if test "$lt_cv_apple_cc_single_mod" = "yes"; then - _lt_dar_single_mod='$single_module' - fi - if test "$lt_cv_ld_exported_symbols_list" = "yes"; then - _lt_dar_export_syms=' ${wl}-exported_symbols_list,$output_objdir/${libname}-symbols.expsym' - else - _lt_dar_export_syms='~$NMEDIT -s $output_objdir/${libname}-symbols.expsym ${lib}' - fi - if test "$DSYMUTIL" != ":" && test "$lt_cv_ld_force_load" = "no"; then - _lt_dsymutil='~$DSYMUTIL $lib || :' - else - _lt_dsymutil= - fi - ;; - esac - -for ac_header in dlfcn.h -do : - ac_fn_c_check_header_compile "$LINENO" "dlfcn.h" "ac_cv_header_dlfcn_h" "$ac_includes_default -" -if test "x$ac_cv_header_dlfcn_h" = xyes; then : - cat >>confdefs.h <<_ACEOF -#define HAVE_DLFCN_H 1 -_ACEOF - -fi - -done - - - - - -# Set options - - - - enable_dlopen=no - - - enable_win32_dll=no - - - # Check whether --enable-shared was given. -if test "${enable_shared+set}" = set; then : - enableval=$enable_shared; p=${PACKAGE-default} - case $enableval in - yes) enable_shared=yes ;; - no) enable_shared=no ;; - *) - enable_shared=no - # Look at the argument we got. We use all the common list separators. - lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR," - for pkg in $enableval; do - IFS="$lt_save_ifs" - if test "X$pkg" = "X$p"; then - enable_shared=yes - fi - done - IFS="$lt_save_ifs" - ;; - esac -else - enable_shared=yes -fi - - - - - - - - - - # Check whether --enable-static was given. -if test "${enable_static+set}" = set; then : - enableval=$enable_static; p=${PACKAGE-default} - case $enableval in - yes) enable_static=yes ;; - no) enable_static=no ;; - *) - enable_static=no - # Look at the argument we got. We use all the common list separators. - lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR," - for pkg in $enableval; do - IFS="$lt_save_ifs" - if test "X$pkg" = "X$p"; then - enable_static=yes - fi - done - IFS="$lt_save_ifs" - ;; - esac -else - enable_static=yes -fi - - - - - - - - - - -# Check whether --with-pic was given. -if test "${with_pic+set}" = set; then : - withval=$with_pic; lt_p=${PACKAGE-default} - case $withval in - yes|no) pic_mode=$withval ;; - *) - pic_mode=default - # Look at the argument we got. We use all the common list separators. - lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR," - for lt_pkg in $withval; do - IFS="$lt_save_ifs" - if test "X$lt_pkg" = "X$lt_p"; then - pic_mode=yes - fi - done - IFS="$lt_save_ifs" - ;; - esac -else - pic_mode=default -fi - - -test -z "$pic_mode" && pic_mode=default - - - - - - - - # Check whether --enable-fast-install was given. -if test "${enable_fast_install+set}" = set; then : - enableval=$enable_fast_install; p=${PACKAGE-default} - case $enableval in - yes) enable_fast_install=yes ;; - no) enable_fast_install=no ;; - *) - enable_fast_install=no - # Look at the argument we got. We use all the common list separators. - lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR," - for pkg in $enableval; do - IFS="$lt_save_ifs" - if test "X$pkg" = "X$p"; then - enable_fast_install=yes - fi - done - IFS="$lt_save_ifs" - ;; - esac -else - enable_fast_install=yes -fi - - - - - - - - - - - -# This can be used to rebuild libtool when needed -LIBTOOL_DEPS="$ltmain" - -# Always use our own libtool. -LIBTOOL='$(SHELL) $(top_builddir)/libtool' - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -test -z "$LN_S" && LN_S="ln -s" - - - - - - - - - - - - - - -if test -n "${ZSH_VERSION+set}" ; then - setopt NO_GLOB_SUBST -fi - -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for objdir" >&5 -$as_echo_n "checking for objdir... " >&6; } -if ${lt_cv_objdir+:} false; then : - $as_echo_n "(cached) " >&6 -else - rm -f .libs 2>/dev/null -mkdir .libs 2>/dev/null -if test -d .libs; then - lt_cv_objdir=.libs -else - # MS-DOS does not allow filenames that begin with a dot. - lt_cv_objdir=_libs -fi -rmdir .libs 2>/dev/null -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_objdir" >&5 -$as_echo "$lt_cv_objdir" >&6; } -objdir=$lt_cv_objdir - - - - - -cat >>confdefs.h <<_ACEOF -#define LT_OBJDIR "$lt_cv_objdir/" -_ACEOF - - - - -case $host_os in -aix3*) - # AIX sometimes has problems with the GCC collect2 program. For some - # reason, if we set the COLLECT_NAMES environment variable, the problems - # vanish in a puff of smoke. - if test "X${COLLECT_NAMES+set}" != Xset; then - COLLECT_NAMES= - export COLLECT_NAMES - fi - ;; -esac - -# Global variables: -ofile=libtool -can_build_shared=yes - -# All known linkers require a `.a' archive for static linking (except MSVC, -# which needs '.lib'). -libext=a - -with_gnu_ld="$lt_cv_prog_gnu_ld" - -old_CC="$CC" -old_CFLAGS="$CFLAGS" - -# Set sane defaults for various variables -test -z "$CC" && CC=cc -test -z "$LTCC" && LTCC=$CC -test -z "$LTCFLAGS" && LTCFLAGS=$CFLAGS -test -z "$LD" && LD=ld -test -z "$ac_objext" && ac_objext=o - -for cc_temp in $compiler""; do - case $cc_temp in - compile | *[\\/]compile | ccache | *[\\/]ccache ) ;; - distcc | *[\\/]distcc | purify | *[\\/]purify ) ;; - \-*) ;; - *) break;; - esac -done -cc_basename=`$ECHO "$cc_temp" | $SED "s%.*/%%; s%^$host_alias-%%"` - - -# Only perform the check for file, if the check method requires it -test -z "$MAGIC_CMD" && MAGIC_CMD=file -case $deplibs_check_method in -file_magic*) - if test "$file_magic_cmd" = '$MAGIC_CMD'; then - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ${ac_tool_prefix}file" >&5 -$as_echo_n "checking for ${ac_tool_prefix}file... " >&6; } -if ${lt_cv_path_MAGIC_CMD+:} false; then : - $as_echo_n "(cached) " >&6 -else - case $MAGIC_CMD in -[\\/*] | ?:[\\/]*) - lt_cv_path_MAGIC_CMD="$MAGIC_CMD" # Let the user override the test with a path. - ;; -*) - lt_save_MAGIC_CMD="$MAGIC_CMD" - lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR - ac_dummy="/usr/bin$PATH_SEPARATOR$PATH" - for ac_dir in $ac_dummy; do - IFS="$lt_save_ifs" - test -z "$ac_dir" && ac_dir=. - if test -f $ac_dir/${ac_tool_prefix}file; then - lt_cv_path_MAGIC_CMD="$ac_dir/${ac_tool_prefix}file" - if test -n "$file_magic_test_file"; then - case $deplibs_check_method in - "file_magic "*) - file_magic_regex=`expr "$deplibs_check_method" : "file_magic \(.*\)"` - MAGIC_CMD="$lt_cv_path_MAGIC_CMD" - if eval $file_magic_cmd \$file_magic_test_file 2> /dev/null | - $EGREP "$file_magic_regex" > /dev/null; then - : - else - cat <<_LT_EOF 1>&2 - -*** Warning: the command libtool uses to detect shared libraries, -*** $file_magic_cmd, produces output that libtool cannot recognize. -*** The result is that libtool may fail to recognize shared libraries -*** as such. This will affect the creation of libtool libraries that -*** depend on shared libraries, but programs linked with such libtool -*** libraries will work regardless of this problem. Nevertheless, you -*** may want to report the problem to your system manager and/or to -*** bug-libtool@gnu.org - -_LT_EOF - fi ;; - esac - fi - break - fi - done - IFS="$lt_save_ifs" - MAGIC_CMD="$lt_save_MAGIC_CMD" - ;; -esac -fi - -MAGIC_CMD="$lt_cv_path_MAGIC_CMD" -if test -n "$MAGIC_CMD"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $MAGIC_CMD" >&5 -$as_echo "$MAGIC_CMD" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - - - - -if test -z "$lt_cv_path_MAGIC_CMD"; then - if test -n "$ac_tool_prefix"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for file" >&5 -$as_echo_n "checking for file... " >&6; } -if ${lt_cv_path_MAGIC_CMD+:} false; then : - $as_echo_n "(cached) " >&6 -else - case $MAGIC_CMD in -[\\/*] | ?:[\\/]*) - lt_cv_path_MAGIC_CMD="$MAGIC_CMD" # Let the user override the test with a path. - ;; -*) - lt_save_MAGIC_CMD="$MAGIC_CMD" - lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR - ac_dummy="/usr/bin$PATH_SEPARATOR$PATH" - for ac_dir in $ac_dummy; do - IFS="$lt_save_ifs" - test -z "$ac_dir" && ac_dir=. - if test -f $ac_dir/file; then - lt_cv_path_MAGIC_CMD="$ac_dir/file" - if test -n "$file_magic_test_file"; then - case $deplibs_check_method in - "file_magic "*) - file_magic_regex=`expr "$deplibs_check_method" : "file_magic \(.*\)"` - MAGIC_CMD="$lt_cv_path_MAGIC_CMD" - if eval $file_magic_cmd \$file_magic_test_file 2> /dev/null | - $EGREP "$file_magic_regex" > /dev/null; then - : - else - cat <<_LT_EOF 1>&2 - -*** Warning: the command libtool uses to detect shared libraries, -*** $file_magic_cmd, produces output that libtool cannot recognize. -*** The result is that libtool may fail to recognize shared libraries -*** as such. This will affect the creation of libtool libraries that -*** depend on shared libraries, but programs linked with such libtool -*** libraries will work regardless of this problem. Nevertheless, you -*** may want to report the problem to your system manager and/or to -*** bug-libtool@gnu.org - -_LT_EOF - fi ;; - esac - fi - break - fi - done - IFS="$lt_save_ifs" - MAGIC_CMD="$lt_save_MAGIC_CMD" - ;; -esac -fi - -MAGIC_CMD="$lt_cv_path_MAGIC_CMD" -if test -n "$MAGIC_CMD"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $MAGIC_CMD" >&5 -$as_echo "$MAGIC_CMD" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - - else - MAGIC_CMD=: - fi -fi - - fi - ;; -esac - -# Use C for the default configuration in the libtool script - -lt_save_CC="$CC" -ac_ext=c -ac_cpp='$CPP $CPPFLAGS' -ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_c_compiler_gnu - - -# Source file extension for C test sources. -ac_ext=c - -# Object file extension for compiled C test sources. -objext=o -objext=$objext - -# Code to be used in simple compile tests -lt_simple_compile_test_code="int some_variable = 0;" - -# Code to be used in simple link tests -lt_simple_link_test_code='int main(){return(0);}' - - - - - - - -# If no C compiler was specified, use CC. -LTCC=${LTCC-"$CC"} - -# If no C compiler flags were specified, use CFLAGS. -LTCFLAGS=${LTCFLAGS-"$CFLAGS"} - -# Allow CC to be a program name with arguments. -compiler=$CC - -# Save the default compiler, since it gets overwritten when the other -# tags are being tested, and _LT_TAGVAR(compiler, []) is a NOP. -compiler_DEFAULT=$CC - -# save warnings/boilerplate of simple test code -ac_outfile=conftest.$ac_objext -echo "$lt_simple_compile_test_code" >conftest.$ac_ext -eval "$ac_compile" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err -_lt_compiler_boilerplate=`cat conftest.err` -$RM conftest* - -ac_outfile=conftest.$ac_objext -echo "$lt_simple_link_test_code" >conftest.$ac_ext -eval "$ac_link" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err -_lt_linker_boilerplate=`cat conftest.err` -$RM -r conftest* - - -## CAVEAT EMPTOR: -## There is no encapsulation within the following macros, do not change -## the running order or otherwise move them around unless you know exactly -## what you are doing... -if test -n "$compiler"; then - -lt_prog_compiler_no_builtin_flag= - -if test "$GCC" = yes; then - case $cc_basename in - nvcc*) - lt_prog_compiler_no_builtin_flag=' -Xcompiler -fno-builtin' ;; - *) - lt_prog_compiler_no_builtin_flag=' -fno-builtin' ;; - esac - - { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler supports -fno-rtti -fno-exceptions" >&5 -$as_echo_n "checking if $compiler supports -fno-rtti -fno-exceptions... " >&6; } -if ${lt_cv_prog_compiler_rtti_exceptions+:} false; then : - $as_echo_n "(cached) " >&6 -else - lt_cv_prog_compiler_rtti_exceptions=no - ac_outfile=conftest.$ac_objext - echo "$lt_simple_compile_test_code" > conftest.$ac_ext - lt_compiler_flag="-fno-rtti -fno-exceptions" - # Insert the option either (1) after the last *FLAGS variable, or - # (2) before a word containing "conftest.", or (3) at the end. - # Note that $ac_compile itself does not contain backslashes and begins - # with a dollar sign (not a hyphen), so the echo should work correctly. - # The option is referenced via a variable to avoid confusing sed. - lt_compile=`echo "$ac_compile" | $SED \ - -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ - -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ - -e 's:$: $lt_compiler_flag:'` - (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5) - (eval "$lt_compile" 2>conftest.err) - ac_status=$? - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - if (exit $ac_status) && test -s "$ac_outfile"; then - # The compiler can only warn and ignore the option if not recognized - # So say no if there are warnings other than the usual output. - $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' >conftest.exp - $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 - if test ! -s conftest.er2 || diff conftest.exp conftest.er2 >/dev/null; then - lt_cv_prog_compiler_rtti_exceptions=yes - fi - fi - $RM conftest* - -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_rtti_exceptions" >&5 -$as_echo "$lt_cv_prog_compiler_rtti_exceptions" >&6; } - -if test x"$lt_cv_prog_compiler_rtti_exceptions" = xyes; then - lt_prog_compiler_no_builtin_flag="$lt_prog_compiler_no_builtin_flag -fno-rtti -fno-exceptions" -else - : -fi - -fi - - - - - - - lt_prog_compiler_wl= -lt_prog_compiler_pic= -lt_prog_compiler_static= - - - if test "$GCC" = yes; then - lt_prog_compiler_wl='-Wl,' - lt_prog_compiler_static='-static' - - case $host_os in - aix*) - # All AIX code is PIC. - if test "$host_cpu" = ia64; then - # AIX 5 now supports IA64 processor - lt_prog_compiler_static='-Bstatic' - fi - ;; - - amigaos*) - case $host_cpu in - powerpc) - # see comment about AmigaOS4 .so support - lt_prog_compiler_pic='-fPIC' - ;; - m68k) - # FIXME: we need at least 68020 code to build shared libraries, but - # adding the `-m68020' flag to GCC prevents building anything better, - # like `-m68040'. - lt_prog_compiler_pic='-m68020 -resident32 -malways-restore-a4' - ;; - esac - ;; - - beos* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*) - # PIC is the default for these OSes. - ;; - - mingw* | cygwin* | pw32* | os2* | cegcc*) - # This hack is so that the source file can tell whether it is being - # built for inclusion in a dll (and should export symbols for example). - # Although the cygwin gcc ignores -fPIC, still need this for old-style - # (--disable-auto-import) libraries - lt_prog_compiler_pic='-DDLL_EXPORT' - ;; - - darwin* | rhapsody*) - # PIC is the default on this platform - # Common symbols not allowed in MH_DYLIB files - lt_prog_compiler_pic='-fno-common' - ;; - - haiku*) - # PIC is the default for Haiku. - # The "-static" flag exists, but is broken. - lt_prog_compiler_static= - ;; - - hpux*) - # PIC is the default for 64-bit PA HP-UX, but not for 32-bit - # PA HP-UX. On IA64 HP-UX, PIC is the default but the pic flag - # sets the default TLS model and affects inlining. - case $host_cpu in - hppa*64*) - # +Z the default - ;; - *) - lt_prog_compiler_pic='-fPIC' - ;; - esac - ;; - - interix[3-9]*) - # Interix 3.x gcc -fpic/-fPIC options generate broken code. - # Instead, we relocate shared libraries at runtime. - ;; - - msdosdjgpp*) - # Just because we use GCC doesn't mean we suddenly get shared libraries - # on systems that don't support them. - lt_prog_compiler_can_build_shared=no - enable_shared=no - ;; - - *nto* | *qnx*) - # QNX uses GNU C++, but need to define -shared option too, otherwise - # it will coredump. - lt_prog_compiler_pic='-fPIC -shared' - ;; - - sysv4*MP*) - if test -d /usr/nec; then - lt_prog_compiler_pic=-Kconform_pic - fi - ;; - - *) - lt_prog_compiler_pic='-fPIC' - ;; - esac - - case $cc_basename in - nvcc*) # Cuda Compiler Driver 2.2 - lt_prog_compiler_wl='-Xlinker ' - if test -n "$lt_prog_compiler_pic"; then - lt_prog_compiler_pic="-Xcompiler $lt_prog_compiler_pic" - fi - ;; - esac - else - # PORTME Check for flag to pass linker flags through the system compiler. - case $host_os in - aix*) - lt_prog_compiler_wl='-Wl,' - if test "$host_cpu" = ia64; then - # AIX 5 now supports IA64 processor - lt_prog_compiler_static='-Bstatic' - else - lt_prog_compiler_static='-bnso -bI:/lib/syscalls.exp' - fi - ;; - - mingw* | cygwin* | pw32* | os2* | cegcc*) - # This hack is so that the source file can tell whether it is being - # built for inclusion in a dll (and should export symbols for example). - lt_prog_compiler_pic='-DDLL_EXPORT' - ;; - - hpux9* | hpux10* | hpux11*) - lt_prog_compiler_wl='-Wl,' - # PIC is the default for IA64 HP-UX and 64-bit HP-UX, but - # not for PA HP-UX. - case $host_cpu in - hppa*64*|ia64*) - # +Z the default - ;; - *) - lt_prog_compiler_pic='+Z' - ;; - esac - # Is there a better lt_prog_compiler_static that works with the bundled CC? - lt_prog_compiler_static='${wl}-a ${wl}archive' - ;; - - irix5* | irix6* | nonstopux*) - lt_prog_compiler_wl='-Wl,' - # PIC (with -KPIC) is the default. - lt_prog_compiler_static='-non_shared' - ;; - - linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) - case $cc_basename in - # old Intel for x86_64 which still supported -KPIC. - ecc*) - lt_prog_compiler_wl='-Wl,' - lt_prog_compiler_pic='-KPIC' - lt_prog_compiler_static='-static' - ;; - # icc used to be incompatible with GCC. - # ICC 10 doesn't accept -KPIC any more. - icc* | ifort*) - lt_prog_compiler_wl='-Wl,' - lt_prog_compiler_pic='-fPIC' - lt_prog_compiler_static='-static' - ;; - # Lahey Fortran 8.1. - lf95*) - lt_prog_compiler_wl='-Wl,' - lt_prog_compiler_pic='--shared' - lt_prog_compiler_static='--static' - ;; - nagfor*) - # NAG Fortran compiler - lt_prog_compiler_wl='-Wl,-Wl,,' - lt_prog_compiler_pic='-PIC' - lt_prog_compiler_static='-Bstatic' - ;; - pgcc* | pgf77* | pgf90* | pgf95* | pgfortran*) - # Portland Group compilers (*not* the Pentium gcc compiler, - # which looks to be a dead project) - lt_prog_compiler_wl='-Wl,' - lt_prog_compiler_pic='-fpic' - lt_prog_compiler_static='-Bstatic' - ;; - ccc*) - lt_prog_compiler_wl='-Wl,' - # All Alpha code is PIC. - lt_prog_compiler_static='-non_shared' - ;; - xl* | bgxl* | bgf* | mpixl*) - # IBM XL C 8.0/Fortran 10.1, 11.1 on PPC and BlueGene - lt_prog_compiler_wl='-Wl,' - lt_prog_compiler_pic='-qpic' - lt_prog_compiler_static='-qstaticlink' - ;; - *) - case `$CC -V 2>&1 | sed 5q` in - *Sun\ Ceres\ Fortran* | *Sun*Fortran*\ [1-7].* | *Sun*Fortran*\ 8.[0-3]*) - # Sun Fortran 8.3 passes all unrecognized flags to the linker - lt_prog_compiler_pic='-KPIC' - lt_prog_compiler_static='-Bstatic' - lt_prog_compiler_wl='' - ;; - *Sun\ F* | *Sun*Fortran*) - lt_prog_compiler_pic='-KPIC' - lt_prog_compiler_static='-Bstatic' - lt_prog_compiler_wl='-Qoption ld ' - ;; - *Sun\ C*) - # Sun C 5.9 - lt_prog_compiler_pic='-KPIC' - lt_prog_compiler_static='-Bstatic' - lt_prog_compiler_wl='-Wl,' - ;; - *Intel*\ [CF]*Compiler*) - lt_prog_compiler_wl='-Wl,' - lt_prog_compiler_pic='-fPIC' - lt_prog_compiler_static='-static' - ;; - *Portland\ Group*) - lt_prog_compiler_wl='-Wl,' - lt_prog_compiler_pic='-fpic' - lt_prog_compiler_static='-Bstatic' - ;; - esac - ;; - esac - ;; - - newsos6) - lt_prog_compiler_pic='-KPIC' - lt_prog_compiler_static='-Bstatic' - ;; - - *nto* | *qnx*) - # QNX uses GNU C++, but need to define -shared option too, otherwise - # it will coredump. - lt_prog_compiler_pic='-fPIC -shared' - ;; - - osf3* | osf4* | osf5*) - lt_prog_compiler_wl='-Wl,' - # All OSF/1 code is PIC. - lt_prog_compiler_static='-non_shared' - ;; - - rdos*) - lt_prog_compiler_static='-non_shared' - ;; - - solaris*) - lt_prog_compiler_pic='-KPIC' - lt_prog_compiler_static='-Bstatic' - case $cc_basename in - f77* | f90* | f95* | sunf77* | sunf90* | sunf95*) - lt_prog_compiler_wl='-Qoption ld ';; - *) - lt_prog_compiler_wl='-Wl,';; - esac - ;; - - sunos4*) - lt_prog_compiler_wl='-Qoption ld ' - lt_prog_compiler_pic='-PIC' - lt_prog_compiler_static='-Bstatic' - ;; - - sysv4 | sysv4.2uw2* | sysv4.3*) - lt_prog_compiler_wl='-Wl,' - lt_prog_compiler_pic='-KPIC' - lt_prog_compiler_static='-Bstatic' - ;; - - sysv4*MP*) - if test -d /usr/nec ;then - lt_prog_compiler_pic='-Kconform_pic' - lt_prog_compiler_static='-Bstatic' - fi - ;; - - sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*) - lt_prog_compiler_wl='-Wl,' - lt_prog_compiler_pic='-KPIC' - lt_prog_compiler_static='-Bstatic' - ;; - - unicos*) - lt_prog_compiler_wl='-Wl,' - lt_prog_compiler_can_build_shared=no - ;; - - uts4*) - lt_prog_compiler_pic='-pic' - lt_prog_compiler_static='-Bstatic' - ;; - - *) - lt_prog_compiler_can_build_shared=no - ;; - esac - fi - -case $host_os in - # For platforms which do not support PIC, -DPIC is meaningless: - *djgpp*) - lt_prog_compiler_pic= - ;; - *) - lt_prog_compiler_pic="$lt_prog_compiler_pic -DPIC" - ;; -esac - -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $compiler option to produce PIC" >&5 -$as_echo_n "checking for $compiler option to produce PIC... " >&6; } -if ${lt_cv_prog_compiler_pic+:} false; then : - $as_echo_n "(cached) " >&6 -else - lt_cv_prog_compiler_pic=$lt_prog_compiler_pic -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_pic" >&5 -$as_echo "$lt_cv_prog_compiler_pic" >&6; } -lt_prog_compiler_pic=$lt_cv_prog_compiler_pic - -# -# Check to make sure the PIC flag actually works. -# -if test -n "$lt_prog_compiler_pic"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler PIC flag $lt_prog_compiler_pic works" >&5 -$as_echo_n "checking if $compiler PIC flag $lt_prog_compiler_pic works... " >&6; } -if ${lt_cv_prog_compiler_pic_works+:} false; then : - $as_echo_n "(cached) " >&6 -else - lt_cv_prog_compiler_pic_works=no - ac_outfile=conftest.$ac_objext - echo "$lt_simple_compile_test_code" > conftest.$ac_ext - lt_compiler_flag="$lt_prog_compiler_pic -DPIC" - # Insert the option either (1) after the last *FLAGS variable, or - # (2) before a word containing "conftest.", or (3) at the end. - # Note that $ac_compile itself does not contain backslashes and begins - # with a dollar sign (not a hyphen), so the echo should work correctly. - # The option is referenced via a variable to avoid confusing sed. - lt_compile=`echo "$ac_compile" | $SED \ - -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ - -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ - -e 's:$: $lt_compiler_flag:'` - (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5) - (eval "$lt_compile" 2>conftest.err) - ac_status=$? - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - if (exit $ac_status) && test -s "$ac_outfile"; then - # The compiler can only warn and ignore the option if not recognized - # So say no if there are warnings other than the usual output. - $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' >conftest.exp - $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 - if test ! -s conftest.er2 || diff conftest.exp conftest.er2 >/dev/null; then - lt_cv_prog_compiler_pic_works=yes - fi - fi - $RM conftest* - -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_pic_works" >&5 -$as_echo "$lt_cv_prog_compiler_pic_works" >&6; } - -if test x"$lt_cv_prog_compiler_pic_works" = xyes; then - case $lt_prog_compiler_pic in - "" | " "*) ;; - *) lt_prog_compiler_pic=" $lt_prog_compiler_pic" ;; - esac -else - lt_prog_compiler_pic= - lt_prog_compiler_can_build_shared=no -fi - -fi - - - - - - - - - - - -# -# Check to make sure the static flag actually works. -# -wl=$lt_prog_compiler_wl eval lt_tmp_static_flag=\"$lt_prog_compiler_static\" -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler static flag $lt_tmp_static_flag works" >&5 -$as_echo_n "checking if $compiler static flag $lt_tmp_static_flag works... " >&6; } -if ${lt_cv_prog_compiler_static_works+:} false; then : - $as_echo_n "(cached) " >&6 -else - lt_cv_prog_compiler_static_works=no - save_LDFLAGS="$LDFLAGS" - LDFLAGS="$LDFLAGS $lt_tmp_static_flag" - echo "$lt_simple_link_test_code" > conftest.$ac_ext - if (eval $ac_link 2>conftest.err) && test -s conftest$ac_exeext; then - # The linker can only warn and ignore the option if not recognized - # So say no if there are warnings - if test -s conftest.err; then - # Append any errors to the config.log. - cat conftest.err 1>&5 - $ECHO "$_lt_linker_boilerplate" | $SED '/^$/d' > conftest.exp - $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 - if diff conftest.exp conftest.er2 >/dev/null; then - lt_cv_prog_compiler_static_works=yes - fi - else - lt_cv_prog_compiler_static_works=yes - fi - fi - $RM -r conftest* - LDFLAGS="$save_LDFLAGS" - -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_static_works" >&5 -$as_echo "$lt_cv_prog_compiler_static_works" >&6; } - -if test x"$lt_cv_prog_compiler_static_works" = xyes; then - : -else - lt_prog_compiler_static= -fi - - - - - - - - { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler supports -c -o file.$ac_objext" >&5 -$as_echo_n "checking if $compiler supports -c -o file.$ac_objext... " >&6; } -if ${lt_cv_prog_compiler_c_o+:} false; then : - $as_echo_n "(cached) " >&6 -else - lt_cv_prog_compiler_c_o=no - $RM -r conftest 2>/dev/null - mkdir conftest - cd conftest - mkdir out - echo "$lt_simple_compile_test_code" > conftest.$ac_ext - - lt_compiler_flag="-o out/conftest2.$ac_objext" - # Insert the option either (1) after the last *FLAGS variable, or - # (2) before a word containing "conftest.", or (3) at the end. - # Note that $ac_compile itself does not contain backslashes and begins - # with a dollar sign (not a hyphen), so the echo should work correctly. - lt_compile=`echo "$ac_compile" | $SED \ - -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ - -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ - -e 's:$: $lt_compiler_flag:'` - (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5) - (eval "$lt_compile" 2>out/conftest.err) - ac_status=$? - cat out/conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - if (exit $ac_status) && test -s out/conftest2.$ac_objext - then - # The compiler can only warn and ignore the option if not recognized - # So say no if there are warnings - $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' > out/conftest.exp - $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2 - if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then - lt_cv_prog_compiler_c_o=yes - fi - fi - chmod u+w . 2>&5 - $RM conftest* - # SGI C++ compiler will create directory out/ii_files/ for - # template instantiation - test -d out/ii_files && $RM out/ii_files/* && rmdir out/ii_files - $RM out/* && rmdir out - cd .. - $RM -r conftest - $RM conftest* - -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_c_o" >&5 -$as_echo "$lt_cv_prog_compiler_c_o" >&6; } - - - - - - - { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler supports -c -o file.$ac_objext" >&5 -$as_echo_n "checking if $compiler supports -c -o file.$ac_objext... " >&6; } -if ${lt_cv_prog_compiler_c_o+:} false; then : - $as_echo_n "(cached) " >&6 -else - lt_cv_prog_compiler_c_o=no - $RM -r conftest 2>/dev/null - mkdir conftest - cd conftest - mkdir out - echo "$lt_simple_compile_test_code" > conftest.$ac_ext - - lt_compiler_flag="-o out/conftest2.$ac_objext" - # Insert the option either (1) after the last *FLAGS variable, or - # (2) before a word containing "conftest.", or (3) at the end. - # Note that $ac_compile itself does not contain backslashes and begins - # with a dollar sign (not a hyphen), so the echo should work correctly. - lt_compile=`echo "$ac_compile" | $SED \ - -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ - -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ - -e 's:$: $lt_compiler_flag:'` - (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5) - (eval "$lt_compile" 2>out/conftest.err) - ac_status=$? - cat out/conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - if (exit $ac_status) && test -s out/conftest2.$ac_objext - then - # The compiler can only warn and ignore the option if not recognized - # So say no if there are warnings - $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' > out/conftest.exp - $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2 - if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then - lt_cv_prog_compiler_c_o=yes - fi - fi - chmod u+w . 2>&5 - $RM conftest* - # SGI C++ compiler will create directory out/ii_files/ for - # template instantiation - test -d out/ii_files && $RM out/ii_files/* && rmdir out/ii_files - $RM out/* && rmdir out - cd .. - $RM -r conftest - $RM conftest* - -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_c_o" >&5 -$as_echo "$lt_cv_prog_compiler_c_o" >&6; } - - - - -hard_links="nottested" -if test "$lt_cv_prog_compiler_c_o" = no && test "$need_locks" != no; then - # do not overwrite the value of need_locks provided by the user - { $as_echo "$as_me:${as_lineno-$LINENO}: checking if we can lock with hard links" >&5 -$as_echo_n "checking if we can lock with hard links... " >&6; } - hard_links=yes - $RM conftest* - ln conftest.a conftest.b 2>/dev/null && hard_links=no - touch conftest.a - ln conftest.a conftest.b 2>&5 || hard_links=no - ln conftest.a conftest.b 2>/dev/null && hard_links=no - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $hard_links" >&5 -$as_echo "$hard_links" >&6; } - if test "$hard_links" = no; then - { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: \`$CC' does not support \`-c -o', so \`make -j' may be unsafe" >&5 -$as_echo "$as_me: WARNING: \`$CC' does not support \`-c -o', so \`make -j' may be unsafe" >&2;} - need_locks=warn - fi -else - need_locks=no -fi - - - - - - - { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the $compiler linker ($LD) supports shared libraries" >&5 -$as_echo_n "checking whether the $compiler linker ($LD) supports shared libraries... " >&6; } - - runpath_var= - allow_undefined_flag= - always_export_symbols=no - archive_cmds= - archive_expsym_cmds= - compiler_needs_object=no - enable_shared_with_static_runtimes=no - export_dynamic_flag_spec= - export_symbols_cmds='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' - hardcode_automatic=no - hardcode_direct=no - hardcode_direct_absolute=no - hardcode_libdir_flag_spec= - hardcode_libdir_separator= - hardcode_minus_L=no - hardcode_shlibpath_var=unsupported - inherit_rpath=no - link_all_deplibs=unknown - module_cmds= - module_expsym_cmds= - old_archive_from_new_cmds= - old_archive_from_expsyms_cmds= - thread_safe_flag_spec= - whole_archive_flag_spec= - # include_expsyms should be a list of space-separated symbols to be *always* - # included in the symbol list - include_expsyms= - # exclude_expsyms can be an extended regexp of symbols to exclude - # it will be wrapped by ` (' and `)$', so one must not match beginning or - # end of line. Example: `a|bc|.*d.*' will exclude the symbols `a' and `bc', - # as well as any symbol that contains `d'. - exclude_expsyms='_GLOBAL_OFFSET_TABLE_|_GLOBAL__F[ID]_.*' - # Although _GLOBAL_OFFSET_TABLE_ is a valid symbol C name, most a.out - # platforms (ab)use it in PIC code, but their linkers get confused if - # the symbol is explicitly referenced. Since portable code cannot - # rely on this symbol name, it's probably fine to never include it in - # preloaded symbol tables. - # Exclude shared library initialization/finalization symbols. - extract_expsyms_cmds= - - case $host_os in - cygwin* | mingw* | pw32* | cegcc*) - # FIXME: the MSVC++ port hasn't been tested in a loooong time - # When not using gcc, we currently assume that we are using - # Microsoft Visual C++. - if test "$GCC" != yes; then - with_gnu_ld=no - fi - ;; - interix*) - # we just hope/assume this is gcc and not c89 (= MSVC++) - with_gnu_ld=yes - ;; - openbsd*) - with_gnu_ld=no - ;; - linux* | k*bsd*-gnu | gnu*) - link_all_deplibs=no - ;; - esac - - ld_shlibs=yes - - # On some targets, GNU ld is compatible enough with the native linker - # that we're better off using the native interface for both. - lt_use_gnu_ld_interface=no - if test "$with_gnu_ld" = yes; then - case $host_os in - aix*) - # The AIX port of GNU ld has always aspired to compatibility - # with the native linker. However, as the warning in the GNU ld - # block says, versions before 2.19.5* couldn't really create working - # shared libraries, regardless of the interface used. - case `$LD -v 2>&1` in - *\ \(GNU\ Binutils\)\ 2.19.5*) ;; - *\ \(GNU\ Binutils\)\ 2.[2-9]*) ;; - *\ \(GNU\ Binutils\)\ [3-9]*) ;; - *) - lt_use_gnu_ld_interface=yes - ;; - esac - ;; - *) - lt_use_gnu_ld_interface=yes - ;; - esac - fi - - if test "$lt_use_gnu_ld_interface" = yes; then - # If archive_cmds runs LD, not CC, wlarc should be empty - wlarc='${wl}' - - # Set some defaults for GNU ld with shared library support. These - # are reset later if shared libraries are not supported. Putting them - # here allows them to be overridden if necessary. - runpath_var=LD_RUN_PATH - hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' - export_dynamic_flag_spec='${wl}--export-dynamic' - # ancient GNU ld didn't support --whole-archive et. al. - if $LD --help 2>&1 | $GREP 'no-whole-archive' > /dev/null; then - whole_archive_flag_spec="$wlarc"'--whole-archive$convenience '"$wlarc"'--no-whole-archive' - else - whole_archive_flag_spec= - fi - supports_anon_versioning=no - case `$LD -v 2>&1` in - *GNU\ gold*) supports_anon_versioning=yes ;; - *\ [01].* | *\ 2.[0-9].* | *\ 2.10.*) ;; # catch versions < 2.11 - *\ 2.11.93.0.2\ *) supports_anon_versioning=yes ;; # RH7.3 ... - *\ 2.11.92.0.12\ *) supports_anon_versioning=yes ;; # Mandrake 8.2 ... - *\ 2.11.*) ;; # other 2.11 versions - *) supports_anon_versioning=yes ;; - esac - - # See if GNU ld supports shared libraries. - case $host_os in - aix[3-9]*) - # On AIX/PPC, the GNU linker is very broken - if test "$host_cpu" != ia64; then - ld_shlibs=no - cat <<_LT_EOF 1>&2 - -*** Warning: the GNU linker, at least up to release 2.19, is reported -*** to be unable to reliably create shared libraries on AIX. -*** Therefore, libtool is disabling shared libraries support. If you -*** really care for shared libraries, you may want to install binutils -*** 2.20 or above, or modify your PATH so that a non-GNU linker is found. -*** You will then need to restart the configuration process. - -_LT_EOF - fi - ;; - - amigaos*) - case $host_cpu in - powerpc) - # see comment about AmigaOS4 .so support - archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' - archive_expsym_cmds='' - ;; - m68k) - archive_cmds='$RM $output_objdir/a2ixlibrary.data~$ECHO "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$ECHO "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$ECHO "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$ECHO "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)' - hardcode_libdir_flag_spec='-L$libdir' - hardcode_minus_L=yes - ;; - esac - ;; - - beos*) - if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then - allow_undefined_flag=unsupported - # Joseph Beckenbach says some releases of gcc - # support --undefined. This deserves some investigation. FIXME - archive_cmds='$CC -nostart $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' - else - ld_shlibs=no - fi - ;; - - cygwin* | mingw* | pw32* | cegcc*) - # _LT_TAGVAR(hardcode_libdir_flag_spec, ) is actually meaningless, - # as there is no search path for DLLs. - hardcode_libdir_flag_spec='-L$libdir' - export_dynamic_flag_spec='${wl}--export-all-symbols' - allow_undefined_flag=unsupported - always_export_symbols=no - enable_shared_with_static_runtimes=yes - export_symbols_cmds='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[BCDGRS][ ]/s/.*[ ]\([^ ]*\)/\1 DATA/;s/^.*[ ]__nm__\([^ ]*\)[ ][^ ]*/\1 DATA/;/^I[ ]/d;/^[AITW][ ]/s/.* //'\'' | sort | uniq > $export_symbols' - exclude_expsyms='[_]+GLOBAL_OFFSET_TABLE_|[_]+GLOBAL__[FID]_.*|[_]+head_[A-Za-z0-9_]+_dll|[A-Za-z0-9_]+_dll_iname' - - if $LD --help 2>&1 | $GREP 'auto-import' > /dev/null; then - archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' - # If the export-symbols file already is a .def file (1st line - # is EXPORTS), use it as is; otherwise, prepend... - archive_expsym_cmds='if test "x`$SED 1q $export_symbols`" = xEXPORTS; then - cp $export_symbols $output_objdir/$soname.def; - else - echo EXPORTS > $output_objdir/$soname.def; - cat $export_symbols >> $output_objdir/$soname.def; - fi~ - $CC -shared $output_objdir/$soname.def $libobjs $deplibs $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' - else - ld_shlibs=no - fi - ;; - - haiku*) - archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' - link_all_deplibs=yes - ;; - - interix[3-9]*) - hardcode_direct=no - hardcode_shlibpath_var=no - hardcode_libdir_flag_spec='${wl}-rpath,$libdir' - export_dynamic_flag_spec='${wl}-E' - # Hack: On Interix 3.x, we cannot compile PIC because of a broken gcc. - # Instead, shared libraries are loaded at an image base (0x10000000 by - # default) and relocated if they conflict, which is a slow very memory - # consuming and fragmenting process. To avoid this, we pick a random, - # 256 KiB-aligned image base between 0x50000000 and 0x6FFC0000 at link - # time. Moving up from 0x10000000 also allows more sbrk(2) space. - archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' - archive_expsym_cmds='sed "s,^,_," $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--retain-symbols-file,$output_objdir/$soname.expsym ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' - ;; - - gnu* | linux* | tpf* | k*bsd*-gnu | kopensolaris*-gnu) - tmp_diet=no - if test "$host_os" = linux-dietlibc; then - case $cc_basename in - diet\ *) tmp_diet=yes;; # linux-dietlibc with static linking (!diet-dyn) - esac - fi - if $LD --help 2>&1 | $EGREP ': supported targets:.* elf' > /dev/null \ - && test "$tmp_diet" = no - then - tmp_addflag=' $pic_flag' - tmp_sharedflag='-shared' - case $cc_basename,$host_cpu in - pgcc*) # Portland Group C compiler - whole_archive_flag_spec='${wl}--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` ${wl}--no-whole-archive' - tmp_addflag=' $pic_flag' - ;; - pgf77* | pgf90* | pgf95* | pgfortran*) - # Portland Group f77 and f90 compilers - whole_archive_flag_spec='${wl}--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` ${wl}--no-whole-archive' - tmp_addflag=' $pic_flag -Mnomain' ;; - ecc*,ia64* | icc*,ia64*) # Intel C compiler on ia64 - tmp_addflag=' -i_dynamic' ;; - efc*,ia64* | ifort*,ia64*) # Intel Fortran compiler on ia64 - tmp_addflag=' -i_dynamic -nofor_main' ;; - ifc* | ifort*) # Intel Fortran compiler - tmp_addflag=' -nofor_main' ;; - lf95*) # Lahey Fortran 8.1 - whole_archive_flag_spec= - tmp_sharedflag='--shared' ;; - xl[cC]* | bgxl[cC]* | mpixl[cC]*) # IBM XL C 8.0 on PPC (deal with xlf below) - tmp_sharedflag='-qmkshrobj' - tmp_addflag= ;; - nvcc*) # Cuda Compiler Driver 2.2 - whole_archive_flag_spec='${wl}--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` ${wl}--no-whole-archive' - compiler_needs_object=yes - ;; - esac - case `$CC -V 2>&1 | sed 5q` in - *Sun\ C*) # Sun C 5.9 - whole_archive_flag_spec='${wl}--whole-archive`new_convenience=; for conv in $convenience\"\"; do test -z \"$conv\" || new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` ${wl}--no-whole-archive' - compiler_needs_object=yes - tmp_sharedflag='-G' ;; - *Sun\ F*) # Sun Fortran 8.3 - tmp_sharedflag='-G' ;; - esac - archive_cmds='$CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' - - if test "x$supports_anon_versioning" = xyes; then - archive_expsym_cmds='echo "{ global:" > $output_objdir/$libname.ver~ - cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ - echo "local: *; };" >> $output_objdir/$libname.ver~ - $CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-version-script ${wl}$output_objdir/$libname.ver -o $lib' - fi - - case $cc_basename in - xlf* | bgf* | bgxlf* | mpixlf*) - # IBM XL Fortran 10.1 on PPC cannot create shared libs itself - whole_archive_flag_spec='--whole-archive$convenience --no-whole-archive' - hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' - archive_cmds='$LD -shared $libobjs $deplibs $linker_flags -soname $soname -o $lib' - if test "x$supports_anon_versioning" = xyes; then - archive_expsym_cmds='echo "{ global:" > $output_objdir/$libname.ver~ - cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ - echo "local: *; };" >> $output_objdir/$libname.ver~ - $LD -shared $libobjs $deplibs $linker_flags -soname $soname -version-script $output_objdir/$libname.ver -o $lib' - fi - ;; - esac - else - ld_shlibs=no - fi - ;; - - netbsd* | netbsdelf*-gnu) - if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then - archive_cmds='$LD -Bshareable $libobjs $deplibs $linker_flags -o $lib' - wlarc= - else - archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' - archive_expsym_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' - fi - ;; - - solaris*) - if $LD -v 2>&1 | $GREP 'BFD 2\.8' > /dev/null; then - ld_shlibs=no - cat <<_LT_EOF 1>&2 - -*** Warning: The releases 2.8.* of the GNU linker cannot reliably -*** create shared libraries on Solaris systems. Therefore, libtool -*** is disabling shared libraries support. We urge you to upgrade GNU -*** binutils to release 2.9.1 or newer. Another option is to modify -*** your PATH or compiler configuration so that the native linker is -*** used, and then restart. - -_LT_EOF - elif $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then - archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' - archive_expsym_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' - else - ld_shlibs=no - fi - ;; - - sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX*) - case `$LD -v 2>&1` in - *\ [01].* | *\ 2.[0-9].* | *\ 2.1[0-5].*) - ld_shlibs=no - cat <<_LT_EOF 1>&2 - -*** Warning: Releases of the GNU linker prior to 2.16.91.0.3 can not -*** reliably create shared libraries on SCO systems. Therefore, libtool -*** is disabling shared libraries support. We urge you to upgrade GNU -*** binutils to release 2.16.91.0.3 or newer. Another option is to modify -*** your PATH or compiler configuration so that the native linker is -*** used, and then restart. - -_LT_EOF - ;; - *) - # For security reasons, it is highly recommended that you always - # use absolute paths for naming shared libraries, and exclude the - # DT_RUNPATH tag from executables and libraries. But doing so - # requires that you compile everything twice, which is a pain. - if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then - hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' - archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' - archive_expsym_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' - else - ld_shlibs=no - fi - ;; - esac - ;; - - sunos4*) - archive_cmds='$LD -assert pure-text -Bshareable -o $lib $libobjs $deplibs $linker_flags' - wlarc= - hardcode_direct=yes - hardcode_shlibpath_var=no - ;; - - *) - if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then - archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' - archive_expsym_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' - else - ld_shlibs=no - fi - ;; - esac - - if test "$ld_shlibs" = no; then - runpath_var= - hardcode_libdir_flag_spec= - export_dynamic_flag_spec= - whole_archive_flag_spec= - fi - else - # PORTME fill in a description of your system's linker (not GNU ld) - case $host_os in - aix3*) - allow_undefined_flag=unsupported - always_export_symbols=yes - archive_expsym_cmds='$LD -o $output_objdir/$soname $libobjs $deplibs $linker_flags -bE:$export_symbols -T512 -H512 -bM:SRE~$AR $AR_FLAGS $lib $output_objdir/$soname' - # Note: this linker hardcodes the directories in LIBPATH if there - # are no directories specified by -L. - hardcode_minus_L=yes - if test "$GCC" = yes && test -z "$lt_prog_compiler_static"; then - # Neither direct hardcoding nor static linking is supported with a - # broken collect2. - hardcode_direct=unsupported - fi - ;; - - aix[4-9]*) - if test "$host_cpu" = ia64; then - # On IA64, the linker does run time linking by default, so we don't - # have to do anything special. - aix_use_runtimelinking=no - exp_sym_flag='-Bexport' - no_entry_flag="" - else - # If we're using GNU nm, then we don't want the "-C" option. - # -C means demangle to AIX nm, but means don't demangle with GNU nm - # Also, AIX nm treats weak defined symbols like other global - # defined symbols, whereas GNU nm marks them as "W". - if $NM -V 2>&1 | $GREP 'GNU' > /dev/null; then - export_symbols_cmds='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W")) && (substr(\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols' - else - export_symbols_cmds='$NM -BCpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B")) && (substr(\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols' - fi - aix_use_runtimelinking=no - - # Test if we are trying to use run time linking or normal - # AIX style linking. If -brtl is somewhere in LDFLAGS, we - # need to do runtime linking. - case $host_os in aix4.[23]|aix4.[23].*|aix[5-9]*) - for ld_flag in $LDFLAGS; do - if (test $ld_flag = "-brtl" || test $ld_flag = "-Wl,-brtl"); then - aix_use_runtimelinking=yes - break - fi - done - ;; - esac - - exp_sym_flag='-bexport' - no_entry_flag='-bnoentry' - fi - - # When large executables or shared objects are built, AIX ld can - # have problems creating the table of contents. If linking a library - # or program results in "error TOC overflow" add -mminimal-toc to - # CXXFLAGS/CFLAGS for g++/gcc. In the cases where that is not - # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS. - - archive_cmds='' - hardcode_direct=yes - hardcode_direct_absolute=yes - hardcode_libdir_separator=':' - link_all_deplibs=yes - file_list_spec='${wl}-f,' - - if test "$GCC" = yes; then - case $host_os in aix4.[012]|aix4.[012].*) - # We only want to do this on AIX 4.2 and lower, the check - # below for broken collect2 doesn't work under 4.3+ - collect2name=`${CC} -print-prog-name=collect2` - if test -f "$collect2name" && - strings "$collect2name" | $GREP resolve_lib_name >/dev/null - then - # We have reworked collect2 - : - else - # We have old collect2 - hardcode_direct=unsupported - # It fails to find uninstalled libraries when the uninstalled - # path is not listed in the libpath. Setting hardcode_minus_L - # to unsupported forces relinking - hardcode_minus_L=yes - hardcode_libdir_flag_spec='-L$libdir' - hardcode_libdir_separator= - fi - ;; - esac - shared_flag='-shared' - if test "$aix_use_runtimelinking" = yes; then - shared_flag="$shared_flag "'${wl}-G' - fi - link_all_deplibs=no - else - # not using gcc - if test "$host_cpu" = ia64; then - # VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release - # chokes on -Wl,-G. The following line is correct: - shared_flag='-G' - else - if test "$aix_use_runtimelinking" = yes; then - shared_flag='${wl}-G' - else - shared_flag='${wl}-bM:SRE' - fi - fi - fi - - export_dynamic_flag_spec='${wl}-bexpall' - # It seems that -bexpall does not export symbols beginning with - # underscore (_), so it is better to generate a list of symbols to export. - always_export_symbols=yes - if test "$aix_use_runtimelinking" = yes; then - # Warning - without using the other runtime loading flags (-brtl), - # -berok will link without error, but may produce a broken library. - allow_undefined_flag='-berok' - # Determine the default libpath from the value encoded in an - # empty executable. - if test "${lt_cv_aix_libpath+set}" = set; then - aix_libpath=$lt_cv_aix_libpath -else - if ${lt_cv_aix_libpath_+:} false; then : - $as_echo_n "(cached) " >&6 -else - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main () -{ - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_link "$LINENO"; then : - - lt_aix_libpath_sed=' - /Import File Strings/,/^$/ { - /^0/ { - s/^0 *\([^ ]*\) *$/\1/ - p - } - }' - lt_cv_aix_libpath_=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` - # Check for a 64-bit object if we didn't find anything. - if test -z "$lt_cv_aix_libpath_"; then - lt_cv_aix_libpath_=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` - fi -fi -rm -f core conftest.err conftest.$ac_objext \ - conftest$ac_exeext conftest.$ac_ext - if test -z "$lt_cv_aix_libpath_"; then - lt_cv_aix_libpath_="/usr/lib:/lib" - fi - -fi - - aix_libpath=$lt_cv_aix_libpath_ -fi - - hardcode_libdir_flag_spec='${wl}-blibpath:$libdir:'"$aix_libpath" - archive_expsym_cmds='$CC -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags `if test "x${allow_undefined_flag}" != "x"; then func_echo_all "${wl}${allow_undefined_flag}"; else :; fi` '"\${wl}$exp_sym_flag:\$export_symbols $shared_flag" - else - if test "$host_cpu" = ia64; then - hardcode_libdir_flag_spec='${wl}-R $libdir:/usr/lib:/lib' - allow_undefined_flag="-z nodefs" - archive_expsym_cmds="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags ${wl}${allow_undefined_flag} '"\${wl}$exp_sym_flag:\$export_symbols" - else - # Determine the default libpath from the value encoded in an - # empty executable. - if test "${lt_cv_aix_libpath+set}" = set; then - aix_libpath=$lt_cv_aix_libpath -else - if ${lt_cv_aix_libpath_+:} false; then : - $as_echo_n "(cached) " >&6 -else - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main () -{ - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_link "$LINENO"; then : - - lt_aix_libpath_sed=' - /Import File Strings/,/^$/ { - /^0/ { - s/^0 *\([^ ]*\) *$/\1/ - p - } - }' - lt_cv_aix_libpath_=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` - # Check for a 64-bit object if we didn't find anything. - if test -z "$lt_cv_aix_libpath_"; then - lt_cv_aix_libpath_=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` - fi -fi -rm -f core conftest.err conftest.$ac_objext \ - conftest$ac_exeext conftest.$ac_ext - if test -z "$lt_cv_aix_libpath_"; then - lt_cv_aix_libpath_="/usr/lib:/lib" - fi - -fi - - aix_libpath=$lt_cv_aix_libpath_ -fi - - hardcode_libdir_flag_spec='${wl}-blibpath:$libdir:'"$aix_libpath" - # Warning - without using the other run time loading flags, - # -berok will link without error, but may produce a broken library. - no_undefined_flag=' ${wl}-bernotok' - allow_undefined_flag=' ${wl}-berok' - if test "$with_gnu_ld" = yes; then - # We only use this code for GNU lds that support --whole-archive. - whole_archive_flag_spec='${wl}--whole-archive$convenience ${wl}--no-whole-archive' - else - # Exported symbols can be pulled into shared objects from archives - whole_archive_flag_spec='$convenience' - fi - archive_cmds_need_lc=yes - # This is similar to how AIX traditionally builds its shared libraries. - archive_expsym_cmds="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs ${wl}-bnoentry $compiler_flags ${wl}-bE:$export_symbols${allow_undefined_flag}~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$soname' - fi - fi - ;; - - amigaos*) - case $host_cpu in - powerpc) - # see comment about AmigaOS4 .so support - archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' - archive_expsym_cmds='' - ;; - m68k) - archive_cmds='$RM $output_objdir/a2ixlibrary.data~$ECHO "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$ECHO "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$ECHO "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$ECHO "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)' - hardcode_libdir_flag_spec='-L$libdir' - hardcode_minus_L=yes - ;; - esac - ;; - - bsdi[45]*) - export_dynamic_flag_spec=-rdynamic - ;; - - cygwin* | mingw* | pw32* | cegcc*) - # When not using gcc, we currently assume that we are using - # Microsoft Visual C++. - # hardcode_libdir_flag_spec is actually meaningless, as there is - # no search path for DLLs. - case $cc_basename in - cl*) - # Native MSVC - hardcode_libdir_flag_spec=' ' - allow_undefined_flag=unsupported - always_export_symbols=yes - file_list_spec='@' - # Tell ltmain to make .lib files, not .a files. - libext=lib - # Tell ltmain to make .dll files, not .so files. - shrext_cmds=".dll" - # FIXME: Setting linknames here is a bad hack. - archive_cmds='$CC -o $output_objdir/$soname $libobjs $compiler_flags $deplibs -Wl,-dll~linknames=' - archive_expsym_cmds='if test "x`$SED 1q $export_symbols`" = xEXPORTS; then - sed -n -e 's/\\\\\\\(.*\\\\\\\)/-link\\\ -EXPORT:\\\\\\\1/' -e '1\\\!p' < $export_symbols > $output_objdir/$soname.exp; - else - sed -e 's/\\\\\\\(.*\\\\\\\)/-link\\\ -EXPORT:\\\\\\\1/' < $export_symbols > $output_objdir/$soname.exp; - fi~ - $CC -o $tool_output_objdir$soname $libobjs $compiler_flags $deplibs "@$tool_output_objdir$soname.exp" -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~ - linknames=' - # The linker will not automatically build a static lib if we build a DLL. - # _LT_TAGVAR(old_archive_from_new_cmds, )='true' - enable_shared_with_static_runtimes=yes - exclude_expsyms='_NULL_IMPORT_DESCRIPTOR|_IMPORT_DESCRIPTOR_.*' - export_symbols_cmds='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[BCDGRS][ ]/s/.*[ ]\([^ ]*\)/\1,DATA/'\'' | $SED -e '\''/^[AITW][ ]/s/.*[ ]//'\'' | sort | uniq > $export_symbols' - # Don't use ranlib - old_postinstall_cmds='chmod 644 $oldlib' - postlink_cmds='lt_outputfile="@OUTPUT@"~ - lt_tool_outputfile="@TOOL_OUTPUT@"~ - case $lt_outputfile in - *.exe|*.EXE) ;; - *) - lt_outputfile="$lt_outputfile.exe" - lt_tool_outputfile="$lt_tool_outputfile.exe" - ;; - esac~ - if test "$MANIFEST_TOOL" != ":" && test -f "$lt_outputfile.manifest"; then - $MANIFEST_TOOL -manifest "$lt_tool_outputfile.manifest" -outputresource:"$lt_tool_outputfile" || exit 1; - $RM "$lt_outputfile.manifest"; - fi' - ;; - *) - # Assume MSVC wrapper - hardcode_libdir_flag_spec=' ' - allow_undefined_flag=unsupported - # Tell ltmain to make .lib files, not .a files. - libext=lib - # Tell ltmain to make .dll files, not .so files. - shrext_cmds=".dll" - # FIXME: Setting linknames here is a bad hack. - archive_cmds='$CC -o $lib $libobjs $compiler_flags `func_echo_all "$deplibs" | $SED '\''s/ -lc$//'\''` -link -dll~linknames=' - # The linker will automatically build a .lib file if we build a DLL. - old_archive_from_new_cmds='true' - # FIXME: Should let the user specify the lib program. - old_archive_cmds='lib -OUT:$oldlib$oldobjs$old_deplibs' - enable_shared_with_static_runtimes=yes - ;; - esac - ;; - - darwin* | rhapsody*) - - - archive_cmds_need_lc=no - hardcode_direct=no - hardcode_automatic=yes - hardcode_shlibpath_var=unsupported - if test "$lt_cv_ld_force_load" = "yes"; then - whole_archive_flag_spec='`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience ${wl}-force_load,$conv\"; done; func_echo_all \"$new_convenience\"`' - - else - whole_archive_flag_spec='' - fi - link_all_deplibs=yes - allow_undefined_flag="$_lt_dar_allow_undefined" - case $cc_basename in - ifort*) _lt_dar_can_shared=yes ;; - *) _lt_dar_can_shared=$GCC ;; - esac - if test "$_lt_dar_can_shared" = "yes"; then - output_verbose_link_cmd=func_echo_all - archive_cmds="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod${_lt_dsymutil}" - module_cmds="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dsymutil}" - archive_expsym_cmds="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring ${_lt_dar_single_mod}${_lt_dar_export_syms}${_lt_dsymutil}" - module_expsym_cmds="sed -e 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dar_export_syms}${_lt_dsymutil}" - - else - ld_shlibs=no - fi - - ;; - - dgux*) - archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' - hardcode_libdir_flag_spec='-L$libdir' - hardcode_shlibpath_var=no - ;; - - # FreeBSD 2.2.[012] allows us to include c++rt0.o to get C++ constructor - # support. Future versions do this automatically, but an explicit c++rt0.o - # does not break anything, and helps significantly (at the cost of a little - # extra space). - freebsd2.2*) - archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags /usr/lib/c++rt0.o' - hardcode_libdir_flag_spec='-R$libdir' - hardcode_direct=yes - hardcode_shlibpath_var=no - ;; - - # Unfortunately, older versions of FreeBSD 2 do not have this feature. - freebsd2.*) - archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' - hardcode_direct=yes - hardcode_minus_L=yes - hardcode_shlibpath_var=no - ;; - - # FreeBSD 3 and greater uses gcc -shared to do shared libraries. - freebsd* | dragonfly*) - archive_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' - hardcode_libdir_flag_spec='-R$libdir' - hardcode_direct=yes - hardcode_shlibpath_var=no - ;; - - hpux9*) - if test "$GCC" = yes; then - archive_cmds='$RM $output_objdir/$soname~$CC -shared $pic_flag ${wl}+b ${wl}$install_libdir -o $output_objdir/$soname $libobjs $deplibs $compiler_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib' - else - archive_cmds='$RM $output_objdir/$soname~$LD -b +b $install_libdir -o $output_objdir/$soname $libobjs $deplibs $linker_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib' - fi - hardcode_libdir_flag_spec='${wl}+b ${wl}$libdir' - hardcode_libdir_separator=: - hardcode_direct=yes - - # hardcode_minus_L: Not really in the search PATH, - # but as the default location of the library. - hardcode_minus_L=yes - export_dynamic_flag_spec='${wl}-E' - ;; - - hpux10*) - if test "$GCC" = yes && test "$with_gnu_ld" = no; then - archive_cmds='$CC -shared $pic_flag ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags' - else - archive_cmds='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags' - fi - if test "$with_gnu_ld" = no; then - hardcode_libdir_flag_spec='${wl}+b ${wl}$libdir' - hardcode_libdir_separator=: - hardcode_direct=yes - hardcode_direct_absolute=yes - export_dynamic_flag_spec='${wl}-E' - # hardcode_minus_L: Not really in the search PATH, - # but as the default location of the library. - hardcode_minus_L=yes - fi - ;; - - hpux11*) - if test "$GCC" = yes && test "$with_gnu_ld" = no; then - case $host_cpu in - hppa*64*) - archive_cmds='$CC -shared ${wl}+h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags' - ;; - ia64*) - archive_cmds='$CC -shared $pic_flag ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags' - ;; - *) - archive_cmds='$CC -shared $pic_flag ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags' - ;; - esac - else - case $host_cpu in - hppa*64*) - archive_cmds='$CC -b ${wl}+h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags' - ;; - ia64*) - archive_cmds='$CC -b ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags' - ;; - *) - - # Older versions of the 11.00 compiler do not understand -b yet - # (HP92453-01 A.11.01.20 doesn't, HP92453-01 B.11.X.35175-35176.GP does) - { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $CC understands -b" >&5 -$as_echo_n "checking if $CC understands -b... " >&6; } -if ${lt_cv_prog_compiler__b+:} false; then : - $as_echo_n "(cached) " >&6 -else - lt_cv_prog_compiler__b=no - save_LDFLAGS="$LDFLAGS" - LDFLAGS="$LDFLAGS -b" - echo "$lt_simple_link_test_code" > conftest.$ac_ext - if (eval $ac_link 2>conftest.err) && test -s conftest$ac_exeext; then - # The linker can only warn and ignore the option if not recognized - # So say no if there are warnings - if test -s conftest.err; then - # Append any errors to the config.log. - cat conftest.err 1>&5 - $ECHO "$_lt_linker_boilerplate" | $SED '/^$/d' > conftest.exp - $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 - if diff conftest.exp conftest.er2 >/dev/null; then - lt_cv_prog_compiler__b=yes - fi - else - lt_cv_prog_compiler__b=yes - fi - fi - $RM -r conftest* - LDFLAGS="$save_LDFLAGS" - -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler__b" >&5 -$as_echo "$lt_cv_prog_compiler__b" >&6; } - -if test x"$lt_cv_prog_compiler__b" = xyes; then - archive_cmds='$CC -b ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags' -else - archive_cmds='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags' -fi - - ;; - esac - fi - if test "$with_gnu_ld" = no; then - hardcode_libdir_flag_spec='${wl}+b ${wl}$libdir' - hardcode_libdir_separator=: - - case $host_cpu in - hppa*64*|ia64*) - hardcode_direct=no - hardcode_shlibpath_var=no - ;; - *) - hardcode_direct=yes - hardcode_direct_absolute=yes - export_dynamic_flag_spec='${wl}-E' - - # hardcode_minus_L: Not really in the search PATH, - # but as the default location of the library. - hardcode_minus_L=yes - ;; - esac - fi - ;; - - irix5* | irix6* | nonstopux*) - if test "$GCC" = yes; then - archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' - # Try to use the -exported_symbol ld option, if it does not - # work, assume that -exports_file does not work either and - # implicitly export all symbols. - # This should be the same for all languages, so no per-tag cache variable. - { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the $host_os linker accepts -exported_symbol" >&5 -$as_echo_n "checking whether the $host_os linker accepts -exported_symbol... " >&6; } -if ${lt_cv_irix_exported_symbol+:} false; then : - $as_echo_n "(cached) " >&6 -else - save_LDFLAGS="$LDFLAGS" - LDFLAGS="$LDFLAGS -shared ${wl}-exported_symbol ${wl}foo ${wl}-update_registry ${wl}/dev/null" - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -int foo (void) { return 0; } -_ACEOF -if ac_fn_c_try_link "$LINENO"; then : - lt_cv_irix_exported_symbol=yes -else - lt_cv_irix_exported_symbol=no -fi -rm -f core conftest.err conftest.$ac_objext \ - conftest$ac_exeext conftest.$ac_ext - LDFLAGS="$save_LDFLAGS" -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_irix_exported_symbol" >&5 -$as_echo "$lt_cv_irix_exported_symbol" >&6; } - if test "$lt_cv_irix_exported_symbol" = yes; then - archive_expsym_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations ${wl}-exports_file ${wl}$export_symbols -o $lib' - fi - else - archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib' - archive_expsym_cmds='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -exports_file $export_symbols -o $lib' - fi - archive_cmds_need_lc='no' - hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' - hardcode_libdir_separator=: - inherit_rpath=yes - link_all_deplibs=yes - ;; - - netbsd* | netbsdelf*-gnu) - if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then - archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' # a.out - else - archive_cmds='$LD -shared -o $lib $libobjs $deplibs $linker_flags' # ELF - fi - hardcode_libdir_flag_spec='-R$libdir' - hardcode_direct=yes - hardcode_shlibpath_var=no - ;; - - newsos6) - archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' - hardcode_direct=yes - hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' - hardcode_libdir_separator=: - hardcode_shlibpath_var=no - ;; - - *nto* | *qnx*) - ;; - - openbsd*) - if test -f /usr/libexec/ld.so; then - hardcode_direct=yes - hardcode_shlibpath_var=no - hardcode_direct_absolute=yes - if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then - archive_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' - archive_expsym_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags ${wl}-retain-symbols-file,$export_symbols' - hardcode_libdir_flag_spec='${wl}-rpath,$libdir' - export_dynamic_flag_spec='${wl}-E' - else - case $host_os in - openbsd[01].* | openbsd2.[0-7] | openbsd2.[0-7].*) - archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' - hardcode_libdir_flag_spec='-R$libdir' - ;; - *) - archive_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' - hardcode_libdir_flag_spec='${wl}-rpath,$libdir' - ;; - esac - fi - else - ld_shlibs=no - fi - ;; - - os2*) - hardcode_libdir_flag_spec='-L$libdir' - hardcode_minus_L=yes - allow_undefined_flag=unsupported - archive_cmds='$ECHO "LIBRARY $libname INITINSTANCE" > $output_objdir/$libname.def~$ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~echo DATA >> $output_objdir/$libname.def~echo " SINGLE NONSHARED" >> $output_objdir/$libname.def~echo EXPORTS >> $output_objdir/$libname.def~emxexp $libobjs >> $output_objdir/$libname.def~$CC -Zdll -Zcrtdll -o $lib $libobjs $deplibs $compiler_flags $output_objdir/$libname.def' - old_archive_from_new_cmds='emximp -o $output_objdir/$libname.a $output_objdir/$libname.def' - ;; - - osf3*) - if test "$GCC" = yes; then - allow_undefined_flag=' ${wl}-expect_unresolved ${wl}\*' - archive_cmds='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' - else - allow_undefined_flag=' -expect_unresolved \*' - archive_cmds='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib' - fi - archive_cmds_need_lc='no' - hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' - hardcode_libdir_separator=: - ;; - - osf4* | osf5*) # as osf3* with the addition of -msym flag - if test "$GCC" = yes; then - allow_undefined_flag=' ${wl}-expect_unresolved ${wl}\*' - archive_cmds='$CC -shared${allow_undefined_flag} $pic_flag $libobjs $deplibs $compiler_flags ${wl}-msym ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' - hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' - else - allow_undefined_flag=' -expect_unresolved \*' - archive_cmds='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags -msym -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib' - archive_expsym_cmds='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done; printf "%s\\n" "-hidden">> $lib.exp~ - $CC -shared${allow_undefined_flag} ${wl}-input ${wl}$lib.exp $compiler_flags $libobjs $deplibs -soname $soname `test -n "$verstring" && $ECHO "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib~$RM $lib.exp' - - # Both c and cxx compiler support -rpath directly - hardcode_libdir_flag_spec='-rpath $libdir' - fi - archive_cmds_need_lc='no' - hardcode_libdir_separator=: - ;; - - solaris*) - no_undefined_flag=' -z defs' - if test "$GCC" = yes; then - wlarc='${wl}' - archive_cmds='$CC -shared $pic_flag ${wl}-z ${wl}text ${wl}-h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags' - archive_expsym_cmds='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ - $CC -shared $pic_flag ${wl}-z ${wl}text ${wl}-M ${wl}$lib.exp ${wl}-h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp' - else - case `$CC -V 2>&1` in - *"Compilers 5.0"*) - wlarc='' - archive_cmds='$LD -G${allow_undefined_flag} -h $soname -o $lib $libobjs $deplibs $linker_flags' - archive_expsym_cmds='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ - $LD -G${allow_undefined_flag} -M $lib.exp -h $soname -o $lib $libobjs $deplibs $linker_flags~$RM $lib.exp' - ;; - *) - wlarc='${wl}' - archive_cmds='$CC -G${allow_undefined_flag} -h $soname -o $lib $libobjs $deplibs $compiler_flags' - archive_expsym_cmds='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ - $CC -G${allow_undefined_flag} -M $lib.exp -h $soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp' - ;; - esac - fi - hardcode_libdir_flag_spec='-R$libdir' - hardcode_shlibpath_var=no - case $host_os in - solaris2.[0-5] | solaris2.[0-5].*) ;; - *) - # The compiler driver will combine and reorder linker options, - # but understands `-z linker_flag'. GCC discards it without `$wl', - # but is careful enough not to reorder. - # Supported since Solaris 2.6 (maybe 2.5.1?) - if test "$GCC" = yes; then - whole_archive_flag_spec='${wl}-z ${wl}allextract$convenience ${wl}-z ${wl}defaultextract' - else - whole_archive_flag_spec='-z allextract$convenience -z defaultextract' - fi - ;; - esac - link_all_deplibs=yes - ;; - - sunos4*) - if test "x$host_vendor" = xsequent; then - # Use $CC to link under sequent, because it throws in some extra .o - # files that make .init and .fini sections work. - archive_cmds='$CC -G ${wl}-h $soname -o $lib $libobjs $deplibs $compiler_flags' - else - archive_cmds='$LD -assert pure-text -Bstatic -o $lib $libobjs $deplibs $linker_flags' - fi - hardcode_libdir_flag_spec='-L$libdir' - hardcode_direct=yes - hardcode_minus_L=yes - hardcode_shlibpath_var=no - ;; - - sysv4) - case $host_vendor in - sni) - archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' - hardcode_direct=yes # is this really true??? - ;; - siemens) - ## LD is ld it makes a PLAMLIB - ## CC just makes a GrossModule. - archive_cmds='$LD -G -o $lib $libobjs $deplibs $linker_flags' - reload_cmds='$CC -r -o $output$reload_objs' - hardcode_direct=no - ;; - motorola) - archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' - hardcode_direct=no #Motorola manual says yes, but my tests say they lie - ;; - esac - runpath_var='LD_RUN_PATH' - hardcode_shlibpath_var=no - ;; - - sysv4.3*) - archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' - hardcode_shlibpath_var=no - export_dynamic_flag_spec='-Bexport' - ;; - - sysv4*MP*) - if test -d /usr/nec; then - archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' - hardcode_shlibpath_var=no - runpath_var=LD_RUN_PATH - hardcode_runpath_var=yes - ld_shlibs=yes - fi - ;; - - sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[01].[10]* | unixware7* | sco3.2v5.0.[024]*) - no_undefined_flag='${wl}-z,text' - archive_cmds_need_lc=no - hardcode_shlibpath_var=no - runpath_var='LD_RUN_PATH' - - if test "$GCC" = yes; then - archive_cmds='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' - archive_expsym_cmds='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' - else - archive_cmds='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' - archive_expsym_cmds='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' - fi - ;; - - sysv5* | sco3.2v5* | sco5v6*) - # Note: We can NOT use -z defs as we might desire, because we do not - # link with -lc, and that would cause any symbols used from libc to - # always be unresolved, which means just about no library would - # ever link correctly. If we're not using GNU ld we use -z text - # though, which does catch some bad symbols but isn't as heavy-handed - # as -z defs. - no_undefined_flag='${wl}-z,text' - allow_undefined_flag='${wl}-z,nodefs' - archive_cmds_need_lc=no - hardcode_shlibpath_var=no - hardcode_libdir_flag_spec='${wl}-R,$libdir' - hardcode_libdir_separator=':' - link_all_deplibs=yes - export_dynamic_flag_spec='${wl}-Bexport' - runpath_var='LD_RUN_PATH' - - if test "$GCC" = yes; then - archive_cmds='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' - archive_expsym_cmds='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' - else - archive_cmds='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' - archive_expsym_cmds='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' - fi - ;; - - uts4*) - archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' - hardcode_libdir_flag_spec='-L$libdir' - hardcode_shlibpath_var=no - ;; - - *) - ld_shlibs=no - ;; - esac - - if test x$host_vendor = xsni; then - case $host in - sysv4 | sysv4.2uw2* | sysv4.3* | sysv5*) - export_dynamic_flag_spec='${wl}-Blargedynsym' - ;; - esac - fi - fi - -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ld_shlibs" >&5 -$as_echo "$ld_shlibs" >&6; } -test "$ld_shlibs" = no && can_build_shared=no - -with_gnu_ld=$with_gnu_ld - - - - - - - - - - - - - - - -# -# Do we need to explicitly link libc? -# -case "x$archive_cmds_need_lc" in -x|xyes) - # Assume -lc should be added - archive_cmds_need_lc=yes - - if test "$enable_shared" = yes && test "$GCC" = yes; then - case $archive_cmds in - *'~'*) - # FIXME: we may have to deal with multi-command sequences. - ;; - '$CC '*) - # Test whether the compiler implicitly links with -lc since on some - # systems, -lgcc has to come before -lc. If gcc already passes -lc - # to ld, don't add -lc before -lgcc. - { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether -lc should be explicitly linked in" >&5 -$as_echo_n "checking whether -lc should be explicitly linked in... " >&6; } -if ${lt_cv_archive_cmds_need_lc+:} false; then : - $as_echo_n "(cached) " >&6 -else - $RM conftest* - echo "$lt_simple_compile_test_code" > conftest.$ac_ext - - if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 - (eval $ac_compile) 2>&5 - ac_status=$? - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; } 2>conftest.err; then - soname=conftest - lib=conftest - libobjs=conftest.$ac_objext - deplibs= - wl=$lt_prog_compiler_wl - pic_flag=$lt_prog_compiler_pic - compiler_flags=-v - linker_flags=-v - verstring= - output_objdir=. - libname=conftest - lt_save_allow_undefined_flag=$allow_undefined_flag - allow_undefined_flag= - if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$archive_cmds 2\>\&1 \| $GREP \" -lc \" \>/dev/null 2\>\&1\""; } >&5 - (eval $archive_cmds 2\>\&1 \| $GREP \" -lc \" \>/dev/null 2\>\&1) 2>&5 - ac_status=$? - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; } - then - lt_cv_archive_cmds_need_lc=no - else - lt_cv_archive_cmds_need_lc=yes - fi - allow_undefined_flag=$lt_save_allow_undefined_flag - else - cat conftest.err 1>&5 - fi - $RM conftest* - -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_archive_cmds_need_lc" >&5 -$as_echo "$lt_cv_archive_cmds_need_lc" >&6; } - archive_cmds_need_lc=$lt_cv_archive_cmds_need_lc - ;; - esac - fi - ;; -esac - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - { $as_echo "$as_me:${as_lineno-$LINENO}: checking dynamic linker characteristics" >&5 -$as_echo_n "checking dynamic linker characteristics... " >&6; } - -if test "$GCC" = yes; then - case $host_os in - darwin*) lt_awk_arg="/^libraries:/,/LR/" ;; - *) lt_awk_arg="/^libraries:/" ;; - esac - case $host_os in - mingw* | cegcc*) lt_sed_strip_eq="s,=\([A-Za-z]:\),\1,g" ;; - *) lt_sed_strip_eq="s,=/,/,g" ;; - esac - lt_search_path_spec=`$CC -print-search-dirs | awk $lt_awk_arg | $SED -e "s/^libraries://" -e $lt_sed_strip_eq` - case $lt_search_path_spec in - *\;*) - # if the path contains ";" then we assume it to be the separator - # otherwise default to the standard path separator (i.e. ":") - it is - # assumed that no part of a normal pathname contains ";" but that should - # okay in the real world where ";" in dirpaths is itself problematic. - lt_search_path_spec=`$ECHO "$lt_search_path_spec" | $SED 's/;/ /g'` - ;; - *) - lt_search_path_spec=`$ECHO "$lt_search_path_spec" | $SED "s/$PATH_SEPARATOR/ /g"` - ;; - esac - # Ok, now we have the path, separated by spaces, we can step through it - # and add multilib dir if necessary. - lt_tmp_lt_search_path_spec= - lt_multi_os_dir=`$CC $CPPFLAGS $CFLAGS $LDFLAGS -print-multi-os-directory 2>/dev/null` - for lt_sys_path in $lt_search_path_spec; do - if test -d "$lt_sys_path/$lt_multi_os_dir"; then - lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec $lt_sys_path/$lt_multi_os_dir" - else - test -d "$lt_sys_path" && \ - lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec $lt_sys_path" - fi - done - lt_search_path_spec=`$ECHO "$lt_tmp_lt_search_path_spec" | awk ' -BEGIN {RS=" "; FS="/|\n";} { - lt_foo=""; - lt_count=0; - for (lt_i = NF; lt_i > 0; lt_i--) { - if ($lt_i != "" && $lt_i != ".") { - if ($lt_i == "..") { - lt_count++; - } else { - if (lt_count == 0) { - lt_foo="/" $lt_i lt_foo; - } else { - lt_count--; - } - } - } - } - if (lt_foo != "") { lt_freq[lt_foo]++; } - if (lt_freq[lt_foo] == 1) { print lt_foo; } -}'` - # AWK program above erroneously prepends '/' to C:/dos/paths - # for these hosts. - case $host_os in - mingw* | cegcc*) lt_search_path_spec=`$ECHO "$lt_search_path_spec" |\ - $SED 's,/\([A-Za-z]:\),\1,g'` ;; - esac - sys_lib_search_path_spec=`$ECHO "$lt_search_path_spec" | $lt_NL2SP` -else - sys_lib_search_path_spec="/lib /usr/lib /usr/local/lib" -fi -library_names_spec= -libname_spec='lib$name' -soname_spec= -shrext_cmds=".so" -postinstall_cmds= -postuninstall_cmds= -finish_cmds= -finish_eval= -shlibpath_var= -shlibpath_overrides_runpath=unknown -version_type=none -dynamic_linker="$host_os ld.so" -sys_lib_dlsearch_path_spec="/lib /usr/lib" -need_lib_prefix=unknown -hardcode_into_libs=no - -# when you set need_version to no, make sure it does not cause -set_version -# flags to be left without arguments -need_version=unknown - -case $host_os in -aix3*) - version_type=linux # correct to gnu/linux during the next big refactor - library_names_spec='${libname}${release}${shared_ext}$versuffix $libname.a' - shlibpath_var=LIBPATH - - # AIX 3 has no versioning support, so we append a major version to the name. - soname_spec='${libname}${release}${shared_ext}$major' - ;; - -aix[4-9]*) - version_type=linux # correct to gnu/linux during the next big refactor - need_lib_prefix=no - need_version=no - hardcode_into_libs=yes - if test "$host_cpu" = ia64; then - # AIX 5 supports IA64 - library_names_spec='${libname}${release}${shared_ext}$major ${libname}${release}${shared_ext}$versuffix $libname${shared_ext}' - shlibpath_var=LD_LIBRARY_PATH - else - # With GCC up to 2.95.x, collect2 would create an import file - # for dependence libraries. The import file would start with - # the line `#! .'. This would cause the generated library to - # depend on `.', always an invalid library. This was fixed in - # development snapshots of GCC prior to 3.0. - case $host_os in - aix4 | aix4.[01] | aix4.[01].*) - if { echo '#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 97)' - echo ' yes ' - echo '#endif'; } | ${CC} -E - | $GREP yes > /dev/null; then - : - else - can_build_shared=no - fi - ;; - esac - # AIX (on Power*) has no versioning support, so currently we can not hardcode correct - # soname into executable. Probably we can add versioning support to - # collect2, so additional links can be useful in future. - if test "$aix_use_runtimelinking" = yes; then - # If using run time linking (on AIX 4.2 or later) use lib.so - # instead of lib.a to let people know that these are not - # typical AIX shared libraries. - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' - else - # We preserve .a as extension for shared libraries through AIX4.2 - # and later when we are not doing run time linking. - library_names_spec='${libname}${release}.a $libname.a' - soname_spec='${libname}${release}${shared_ext}$major' - fi - shlibpath_var=LIBPATH - fi - ;; - -amigaos*) - case $host_cpu in - powerpc) - # Since July 2007 AmigaOS4 officially supports .so libraries. - # When compiling the executable, add -use-dynld -Lsobjs: to the compileline. - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' - ;; - m68k) - library_names_spec='$libname.ixlibrary $libname.a' - # Create ${libname}_ixlibrary.a entries in /sys/libs. - finish_eval='for lib in `ls $libdir/*.ixlibrary 2>/dev/null`; do libname=`func_echo_all "$lib" | $SED '\''s%^.*/\([^/]*\)\.ixlibrary$%\1%'\''`; test $RM /sys/libs/${libname}_ixlibrary.a; $show "cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a"; cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a || exit 1; done' - ;; - esac - ;; - -beos*) - library_names_spec='${libname}${shared_ext}' - dynamic_linker="$host_os ld.so" - shlibpath_var=LIBRARY_PATH - ;; - -bsdi[45]*) - version_type=linux # correct to gnu/linux during the next big refactor - need_version=no - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - finish_cmds='PATH="\$PATH:/sbin" ldconfig $libdir' - shlibpath_var=LD_LIBRARY_PATH - sys_lib_search_path_spec="/shlib /usr/lib /usr/X11/lib /usr/contrib/lib /lib /usr/local/lib" - sys_lib_dlsearch_path_spec="/shlib /usr/lib /usr/local/lib" - # the default ld.so.conf also contains /usr/contrib/lib and - # /usr/X11R6/lib (/usr/X11 is a link to /usr/X11R6), but let us allow - # libtool to hard-code these into programs - ;; - -cygwin* | mingw* | pw32* | cegcc*) - version_type=windows - shrext_cmds=".dll" - need_version=no - need_lib_prefix=no - - case $GCC,$cc_basename in - yes,*) - # gcc - library_names_spec='$libname.dll.a' - # DLL is installed to $(libdir)/../bin by postinstall_cmds - postinstall_cmds='base_file=`basename \${file}`~ - dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\${base_file}'\''i; echo \$dlname'\''`~ - dldir=$destdir/`dirname \$dlpath`~ - test -d \$dldir || mkdir -p \$dldir~ - $install_prog $dir/$dlname \$dldir/$dlname~ - chmod a+x \$dldir/$dlname~ - if test -n '\''$stripme'\'' && test -n '\''$striplib'\''; then - eval '\''$striplib \$dldir/$dlname'\'' || exit \$?; - fi' - postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~ - dlpath=$dir/\$dldll~ - $RM \$dlpath' - shlibpath_overrides_runpath=yes - - case $host_os in - cygwin*) - # Cygwin DLLs use 'cyg' prefix rather than 'lib' - soname_spec='`echo ${libname} | sed -e 's/^lib/cyg/'``echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext}' - - sys_lib_search_path_spec="$sys_lib_search_path_spec /usr/lib/w32api" - ;; - mingw* | cegcc*) - # MinGW DLLs use traditional 'lib' prefix - soname_spec='${libname}`echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext}' - ;; - pw32*) - # pw32 DLLs use 'pw' prefix rather than 'lib' - library_names_spec='`echo ${libname} | sed -e 's/^lib/pw/'``echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext}' - ;; - esac - dynamic_linker='Win32 ld.exe' - ;; - - *,cl*) - # Native MSVC - libname_spec='$name' - soname_spec='${libname}`echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext}' - library_names_spec='${libname}.dll.lib' - - case $build_os in - mingw*) - sys_lib_search_path_spec= - lt_save_ifs=$IFS - IFS=';' - for lt_path in $LIB - do - IFS=$lt_save_ifs - # Let DOS variable expansion print the short 8.3 style file name. - lt_path=`cd "$lt_path" 2>/dev/null && cmd //C "for %i in (".") do @echo %~si"` - sys_lib_search_path_spec="$sys_lib_search_path_spec $lt_path" - done - IFS=$lt_save_ifs - # Convert to MSYS style. - sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | sed -e 's|\\\\|/|g' -e 's| \\([a-zA-Z]\\):| /\\1|g' -e 's|^ ||'` - ;; - cygwin*) - # Convert to unix form, then to dos form, then back to unix form - # but this time dos style (no spaces!) so that the unix form looks - # like /cygdrive/c/PROGRA~1:/cygdr... - sys_lib_search_path_spec=`cygpath --path --unix "$LIB"` - sys_lib_search_path_spec=`cygpath --path --dos "$sys_lib_search_path_spec" 2>/dev/null` - sys_lib_search_path_spec=`cygpath --path --unix "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"` - ;; - *) - sys_lib_search_path_spec="$LIB" - if $ECHO "$sys_lib_search_path_spec" | $GREP ';[c-zC-Z]:/' >/dev/null; then - # It is most probably a Windows format PATH. - sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e 's/;/ /g'` - else - sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"` - fi - # FIXME: find the short name or the path components, as spaces are - # common. (e.g. "Program Files" -> "PROGRA~1") - ;; - esac - - # DLL is installed to $(libdir)/../bin by postinstall_cmds - postinstall_cmds='base_file=`basename \${file}`~ - dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\${base_file}'\''i; echo \$dlname'\''`~ - dldir=$destdir/`dirname \$dlpath`~ - test -d \$dldir || mkdir -p \$dldir~ - $install_prog $dir/$dlname \$dldir/$dlname' - postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~ - dlpath=$dir/\$dldll~ - $RM \$dlpath' - shlibpath_overrides_runpath=yes - dynamic_linker='Win32 link.exe' - ;; - - *) - # Assume MSVC wrapper - library_names_spec='${libname}`echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext} $libname.lib' - dynamic_linker='Win32 ld.exe' - ;; - esac - # FIXME: first we should search . and the directory the executable is in - shlibpath_var=PATH - ;; - -darwin* | rhapsody*) - dynamic_linker="$host_os dyld" - version_type=darwin - need_lib_prefix=no - need_version=no - library_names_spec='${libname}${release}${major}$shared_ext ${libname}$shared_ext' - soname_spec='${libname}${release}${major}$shared_ext' - shlibpath_overrides_runpath=yes - shlibpath_var=DYLD_LIBRARY_PATH - shrext_cmds='`test .$module = .yes && echo .so || echo .dylib`' - - sys_lib_search_path_spec="$sys_lib_search_path_spec /usr/local/lib" - sys_lib_dlsearch_path_spec='/usr/local/lib /lib /usr/lib' - ;; - -dgux*) - version_type=linux # correct to gnu/linux during the next big refactor - need_lib_prefix=no - need_version=no - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname$shared_ext' - soname_spec='${libname}${release}${shared_ext}$major' - shlibpath_var=LD_LIBRARY_PATH - ;; - -freebsd* | dragonfly*) - # DragonFly does not have aout. When/if they implement a new - # versioning mechanism, adjust this. - if test -x /usr/bin/objformat; then - objformat=`/usr/bin/objformat` - else - case $host_os in - freebsd[23].*) objformat=aout ;; - *) objformat=elf ;; - esac - fi - version_type=freebsd-$objformat - case $version_type in - freebsd-elf*) - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext} $libname${shared_ext}' - need_version=no - need_lib_prefix=no - ;; - freebsd-*) - library_names_spec='${libname}${release}${shared_ext}$versuffix $libname${shared_ext}$versuffix' - need_version=yes - ;; - esac - shlibpath_var=LD_LIBRARY_PATH - case $host_os in - freebsd2.*) - shlibpath_overrides_runpath=yes - ;; - freebsd3.[01]* | freebsdelf3.[01]*) - shlibpath_overrides_runpath=yes - hardcode_into_libs=yes - ;; - freebsd3.[2-9]* | freebsdelf3.[2-9]* | \ - freebsd4.[0-5] | freebsdelf4.[0-5] | freebsd4.1.1 | freebsdelf4.1.1) - shlibpath_overrides_runpath=no - hardcode_into_libs=yes - ;; - *) # from 4.6 on, and DragonFly - shlibpath_overrides_runpath=yes - hardcode_into_libs=yes - ;; - esac - ;; - -haiku*) - version_type=linux # correct to gnu/linux during the next big refactor - need_lib_prefix=no - need_version=no - dynamic_linker="$host_os runtime_loader" - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}${major} ${libname}${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - shlibpath_var=LIBRARY_PATH - shlibpath_overrides_runpath=yes - sys_lib_dlsearch_path_spec='/boot/home/config/lib /boot/common/lib /boot/system/lib' - hardcode_into_libs=yes - ;; - -hpux9* | hpux10* | hpux11*) - # Give a soname corresponding to the major version so that dld.sl refuses to - # link against other versions. - version_type=sunos - need_lib_prefix=no - need_version=no - case $host_cpu in - ia64*) - shrext_cmds='.so' - hardcode_into_libs=yes - dynamic_linker="$host_os dld.so" - shlibpath_var=LD_LIBRARY_PATH - shlibpath_overrides_runpath=yes # Unless +noenvvar is specified. - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - if test "X$HPUX_IA64_MODE" = X32; then - sys_lib_search_path_spec="/usr/lib/hpux32 /usr/local/lib/hpux32 /usr/local/lib" - else - sys_lib_search_path_spec="/usr/lib/hpux64 /usr/local/lib/hpux64" - fi - sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec - ;; - hppa*64*) - shrext_cmds='.sl' - hardcode_into_libs=yes - dynamic_linker="$host_os dld.sl" - shlibpath_var=LD_LIBRARY_PATH # How should we handle SHLIB_PATH - shlibpath_overrides_runpath=yes # Unless +noenvvar is specified. - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - sys_lib_search_path_spec="/usr/lib/pa20_64 /usr/ccs/lib/pa20_64" - sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec - ;; - *) - shrext_cmds='.sl' - dynamic_linker="$host_os dld.sl" - shlibpath_var=SHLIB_PATH - shlibpath_overrides_runpath=no # +s is required to enable SHLIB_PATH - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - ;; - esac - # HP-UX runs *really* slowly unless shared libraries are mode 555, ... - postinstall_cmds='chmod 555 $lib' - # or fails outright, so override atomically: - install_override_mode=555 - ;; - -interix[3-9]*) - version_type=linux # correct to gnu/linux during the next big refactor - need_lib_prefix=no - need_version=no - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - dynamic_linker='Interix 3.x ld.so.1 (PE, like ELF)' - shlibpath_var=LD_LIBRARY_PATH - shlibpath_overrides_runpath=no - hardcode_into_libs=yes - ;; - -irix5* | irix6* | nonstopux*) - case $host_os in - nonstopux*) version_type=nonstopux ;; - *) - if test "$lt_cv_prog_gnu_ld" = yes; then - version_type=linux # correct to gnu/linux during the next big refactor - else - version_type=irix - fi ;; - esac - need_lib_prefix=no - need_version=no - soname_spec='${libname}${release}${shared_ext}$major' - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${release}${shared_ext} $libname${shared_ext}' - case $host_os in - irix5* | nonstopux*) - libsuff= shlibsuff= - ;; - *) - case $LD in # libtool.m4 will add one of these switches to LD - *-32|*"-32 "|*-melf32bsmip|*"-melf32bsmip ") - libsuff= shlibsuff= libmagic=32-bit;; - *-n32|*"-n32 "|*-melf32bmipn32|*"-melf32bmipn32 ") - libsuff=32 shlibsuff=N32 libmagic=N32;; - *-64|*"-64 "|*-melf64bmip|*"-melf64bmip ") - libsuff=64 shlibsuff=64 libmagic=64-bit;; - *) libsuff= shlibsuff= libmagic=never-match;; - esac - ;; - esac - shlibpath_var=LD_LIBRARY${shlibsuff}_PATH - shlibpath_overrides_runpath=no - sys_lib_search_path_spec="/usr/lib${libsuff} /lib${libsuff} /usr/local/lib${libsuff}" - sys_lib_dlsearch_path_spec="/usr/lib${libsuff} /lib${libsuff}" - hardcode_into_libs=yes - ;; - -# No shared lib support for Linux oldld, aout, or coff. -linux*oldld* | linux*aout* | linux*coff*) - dynamic_linker=no - ;; - -# This must be glibc/ELF. -linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) - version_type=linux # correct to gnu/linux during the next big refactor - need_lib_prefix=no - need_version=no - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - finish_cmds='PATH="\$PATH:/sbin" ldconfig -n $libdir' - shlibpath_var=LD_LIBRARY_PATH - shlibpath_overrides_runpath=no - - # Some binutils ld are patched to set DT_RUNPATH - if ${lt_cv_shlibpath_overrides_runpath+:} false; then : - $as_echo_n "(cached) " >&6 -else - lt_cv_shlibpath_overrides_runpath=no - save_LDFLAGS=$LDFLAGS - save_libdir=$libdir - eval "libdir=/foo; wl=\"$lt_prog_compiler_wl\"; \ - LDFLAGS=\"\$LDFLAGS $hardcode_libdir_flag_spec\"" - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main () -{ - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_link "$LINENO"; then : - if ($OBJDUMP -p conftest$ac_exeext) 2>/dev/null | grep "RUNPATH.*$libdir" >/dev/null; then : - lt_cv_shlibpath_overrides_runpath=yes -fi -fi -rm -f core conftest.err conftest.$ac_objext \ - conftest$ac_exeext conftest.$ac_ext - LDFLAGS=$save_LDFLAGS - libdir=$save_libdir - -fi - - shlibpath_overrides_runpath=$lt_cv_shlibpath_overrides_runpath - - # This implies no fast_install, which is unacceptable. - # Some rework will be needed to allow for fast_install - # before this can be enabled. - hardcode_into_libs=yes - - # Append ld.so.conf contents to the search path - if test -f /etc/ld.so.conf; then - lt_ld_extra=`awk '/^include / { system(sprintf("cd /etc; cat %s 2>/dev/null", \$2)); skip = 1; } { if (!skip) print \$0; skip = 0; }' < /etc/ld.so.conf | $SED -e 's/#.*//;/^[ ]*hwcap[ ]/d;s/[:, ]/ /g;s/=[^=]*$//;s/=[^= ]* / /g;s/"//g;/^$/d' | tr '\n' ' '` - sys_lib_dlsearch_path_spec="/lib /usr/lib $lt_ld_extra" - fi - - # We used to test for /lib/ld.so.1 and disable shared libraries on - # powerpc, because MkLinux only supported shared libraries with the - # GNU dynamic linker. Since this was broken with cross compilers, - # most powerpc-linux boxes support dynamic linking these days and - # people can always --disable-shared, the test was removed, and we - # assume the GNU/Linux dynamic linker is in use. - dynamic_linker='GNU/Linux ld.so' - ;; - -netbsdelf*-gnu) - version_type=linux - need_lib_prefix=no - need_version=no - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - shlibpath_var=LD_LIBRARY_PATH - shlibpath_overrides_runpath=no - hardcode_into_libs=yes - dynamic_linker='NetBSD ld.elf_so' - ;; - -netbsd*) - version_type=sunos - need_lib_prefix=no - need_version=no - if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix' - finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' - dynamic_linker='NetBSD (a.out) ld.so' - else - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - dynamic_linker='NetBSD ld.elf_so' - fi - shlibpath_var=LD_LIBRARY_PATH - shlibpath_overrides_runpath=yes - hardcode_into_libs=yes - ;; - -newsos6) - version_type=linux # correct to gnu/linux during the next big refactor - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' - shlibpath_var=LD_LIBRARY_PATH - shlibpath_overrides_runpath=yes - ;; - -*nto* | *qnx*) - version_type=qnx - need_lib_prefix=no - need_version=no - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - shlibpath_var=LD_LIBRARY_PATH - shlibpath_overrides_runpath=no - hardcode_into_libs=yes - dynamic_linker='ldqnx.so' - ;; - -openbsd*) - version_type=sunos - sys_lib_dlsearch_path_spec="/usr/lib" - need_lib_prefix=no - # Some older versions of OpenBSD (3.3 at least) *do* need versioned libs. - case $host_os in - openbsd3.3 | openbsd3.3.*) need_version=yes ;; - *) need_version=no ;; - esac - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix' - finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' - shlibpath_var=LD_LIBRARY_PATH - if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then - case $host_os in - openbsd2.[89] | openbsd2.[89].*) - shlibpath_overrides_runpath=no - ;; - *) - shlibpath_overrides_runpath=yes - ;; - esac - else - shlibpath_overrides_runpath=yes - fi - ;; - -os2*) - libname_spec='$name' - shrext_cmds=".dll" - need_lib_prefix=no - library_names_spec='$libname${shared_ext} $libname.a' - dynamic_linker='OS/2 ld.exe' - shlibpath_var=LIBPATH - ;; - -osf3* | osf4* | osf5*) - version_type=osf - need_lib_prefix=no - need_version=no - soname_spec='${libname}${release}${shared_ext}$major' - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' - shlibpath_var=LD_LIBRARY_PATH - sys_lib_search_path_spec="/usr/shlib /usr/ccs/lib /usr/lib/cmplrs/cc /usr/lib /usr/local/lib /var/shlib" - sys_lib_dlsearch_path_spec="$sys_lib_search_path_spec" - ;; - -rdos*) - dynamic_linker=no - ;; - -solaris*) - version_type=linux # correct to gnu/linux during the next big refactor - need_lib_prefix=no - need_version=no - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - shlibpath_var=LD_LIBRARY_PATH - shlibpath_overrides_runpath=yes - hardcode_into_libs=yes - # ldd complains unless libraries are executable - postinstall_cmds='chmod +x $lib' - ;; - -sunos4*) - version_type=sunos - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix' - finish_cmds='PATH="\$PATH:/usr/etc" ldconfig $libdir' - shlibpath_var=LD_LIBRARY_PATH - shlibpath_overrides_runpath=yes - if test "$with_gnu_ld" = yes; then - need_lib_prefix=no - fi - need_version=yes - ;; - -sysv4 | sysv4.3*) - version_type=linux # correct to gnu/linux during the next big refactor - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - shlibpath_var=LD_LIBRARY_PATH - case $host_vendor in - sni) - shlibpath_overrides_runpath=no - need_lib_prefix=no - runpath_var=LD_RUN_PATH - ;; - siemens) - need_lib_prefix=no - ;; - motorola) - need_lib_prefix=no - need_version=no - shlibpath_overrides_runpath=no - sys_lib_search_path_spec='/lib /usr/lib /usr/ccs/lib' - ;; - esac - ;; - -sysv4*MP*) - if test -d /usr/nec ;then - version_type=linux # correct to gnu/linux during the next big refactor - library_names_spec='$libname${shared_ext}.$versuffix $libname${shared_ext}.$major $libname${shared_ext}' - soname_spec='$libname${shared_ext}.$major' - shlibpath_var=LD_LIBRARY_PATH - fi - ;; - -sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*) - version_type=freebsd-elf - need_lib_prefix=no - need_version=no - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext} $libname${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - shlibpath_var=LD_LIBRARY_PATH - shlibpath_overrides_runpath=yes - hardcode_into_libs=yes - if test "$with_gnu_ld" = yes; then - sys_lib_search_path_spec='/usr/local/lib /usr/gnu/lib /usr/ccs/lib /usr/lib /lib' - else - sys_lib_search_path_spec='/usr/ccs/lib /usr/lib' - case $host_os in - sco3.2v5*) - sys_lib_search_path_spec="$sys_lib_search_path_spec /lib" - ;; - esac - fi - sys_lib_dlsearch_path_spec='/usr/lib' - ;; - -tpf*) - # TPF is a cross-target only. Preferred cross-host = GNU/Linux. - version_type=linux # correct to gnu/linux during the next big refactor - need_lib_prefix=no - need_version=no - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' - shlibpath_var=LD_LIBRARY_PATH - shlibpath_overrides_runpath=no - hardcode_into_libs=yes - ;; - -uts4*) - version_type=linux # correct to gnu/linux during the next big refactor - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - shlibpath_var=LD_LIBRARY_PATH - ;; - -*) - dynamic_linker=no - ;; -esac -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $dynamic_linker" >&5 -$as_echo "$dynamic_linker" >&6; } -test "$dynamic_linker" = no && can_build_shared=no - -variables_saved_for_relink="PATH $shlibpath_var $runpath_var" -if test "$GCC" = yes; then - variables_saved_for_relink="$variables_saved_for_relink GCC_EXEC_PREFIX COMPILER_PATH LIBRARY_PATH" -fi - -if test "${lt_cv_sys_lib_search_path_spec+set}" = set; then - sys_lib_search_path_spec="$lt_cv_sys_lib_search_path_spec" -fi -if test "${lt_cv_sys_lib_dlsearch_path_spec+set}" = set; then - sys_lib_dlsearch_path_spec="$lt_cv_sys_lib_dlsearch_path_spec" -fi - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to hardcode library paths into programs" >&5 -$as_echo_n "checking how to hardcode library paths into programs... " >&6; } -hardcode_action= -if test -n "$hardcode_libdir_flag_spec" || - test -n "$runpath_var" || - test "X$hardcode_automatic" = "Xyes" ; then - - # We can hardcode non-existent directories. - if test "$hardcode_direct" != no && - # If the only mechanism to avoid hardcoding is shlibpath_var, we - # have to relink, otherwise we might link with an installed library - # when we should be linking with a yet-to-be-installed one - ## test "$_LT_TAGVAR(hardcode_shlibpath_var, )" != no && - test "$hardcode_minus_L" != no; then - # Linking always hardcodes the temporary library directory. - hardcode_action=relink - else - # We can link without hardcoding, and we can hardcode nonexisting dirs. - hardcode_action=immediate - fi -else - # We cannot hardcode anything, or else we can only hardcode existing - # directories. - hardcode_action=unsupported -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $hardcode_action" >&5 -$as_echo "$hardcode_action" >&6; } - -if test "$hardcode_action" = relink || - test "$inherit_rpath" = yes; then - # Fast installation is not supported - enable_fast_install=no -elif test "$shlibpath_overrides_runpath" = yes || - test "$enable_shared" = no; then - # Fast installation is not necessary - enable_fast_install=needless -fi - - - - - - - if test "x$enable_dlopen" != xyes; then - enable_dlopen=unknown - enable_dlopen_self=unknown - enable_dlopen_self_static=unknown -else - lt_cv_dlopen=no - lt_cv_dlopen_libs= - - case $host_os in - beos*) - lt_cv_dlopen="load_add_on" - lt_cv_dlopen_libs= - lt_cv_dlopen_self=yes - ;; - - mingw* | pw32* | cegcc*) - lt_cv_dlopen="LoadLibrary" - lt_cv_dlopen_libs= - ;; - - cygwin*) - lt_cv_dlopen="dlopen" - lt_cv_dlopen_libs= - ;; - - darwin*) - # if libdl is installed we need to link against it - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for dlopen in -ldl" >&5 -$as_echo_n "checking for dlopen in -ldl... " >&6; } -if ${ac_cv_lib_dl_dlopen+:} false; then : - $as_echo_n "(cached) " >&6 -else - ac_check_lib_save_LIBS=$LIBS -LIBS="-ldl $LIBS" -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -/* Override any GCC internal prototype to avoid an error. - Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -#ifdef __cplusplus -extern "C" -#endif -char dlopen (); -int -main () -{ -return dlopen (); - ; - return 0; -} -_ACEOF -if ac_fn_c_try_link "$LINENO"; then : - ac_cv_lib_dl_dlopen=yes -else - ac_cv_lib_dl_dlopen=no -fi -rm -f core conftest.err conftest.$ac_objext \ - conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dl_dlopen" >&5 -$as_echo "$ac_cv_lib_dl_dlopen" >&6; } -if test "x$ac_cv_lib_dl_dlopen" = xyes; then : - lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-ldl" -else - - lt_cv_dlopen="dyld" - lt_cv_dlopen_libs= - lt_cv_dlopen_self=yes - -fi - - ;; - - *) - ac_fn_c_check_func "$LINENO" "shl_load" "ac_cv_func_shl_load" -if test "x$ac_cv_func_shl_load" = xyes; then : - lt_cv_dlopen="shl_load" -else - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for shl_load in -ldld" >&5 -$as_echo_n "checking for shl_load in -ldld... " >&6; } -if ${ac_cv_lib_dld_shl_load+:} false; then : - $as_echo_n "(cached) " >&6 -else - ac_check_lib_save_LIBS=$LIBS -LIBS="-ldld $LIBS" -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -/* Override any GCC internal prototype to avoid an error. - Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -#ifdef __cplusplus -extern "C" -#endif -char shl_load (); -int -main () -{ -return shl_load (); - ; - return 0; -} -_ACEOF -if ac_fn_c_try_link "$LINENO"; then : - ac_cv_lib_dld_shl_load=yes -else - ac_cv_lib_dld_shl_load=no -fi -rm -f core conftest.err conftest.$ac_objext \ - conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dld_shl_load" >&5 -$as_echo "$ac_cv_lib_dld_shl_load" >&6; } -if test "x$ac_cv_lib_dld_shl_load" = xyes; then : - lt_cv_dlopen="shl_load" lt_cv_dlopen_libs="-ldld" -else - ac_fn_c_check_func "$LINENO" "dlopen" "ac_cv_func_dlopen" -if test "x$ac_cv_func_dlopen" = xyes; then : - lt_cv_dlopen="dlopen" -else - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for dlopen in -ldl" >&5 -$as_echo_n "checking for dlopen in -ldl... " >&6; } -if ${ac_cv_lib_dl_dlopen+:} false; then : - $as_echo_n "(cached) " >&6 -else - ac_check_lib_save_LIBS=$LIBS -LIBS="-ldl $LIBS" -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -/* Override any GCC internal prototype to avoid an error. - Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -#ifdef __cplusplus -extern "C" -#endif -char dlopen (); -int -main () -{ -return dlopen (); - ; - return 0; -} -_ACEOF -if ac_fn_c_try_link "$LINENO"; then : - ac_cv_lib_dl_dlopen=yes -else - ac_cv_lib_dl_dlopen=no -fi -rm -f core conftest.err conftest.$ac_objext \ - conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dl_dlopen" >&5 -$as_echo "$ac_cv_lib_dl_dlopen" >&6; } -if test "x$ac_cv_lib_dl_dlopen" = xyes; then : - lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-ldl" -else - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for dlopen in -lsvld" >&5 -$as_echo_n "checking for dlopen in -lsvld... " >&6; } -if ${ac_cv_lib_svld_dlopen+:} false; then : - $as_echo_n "(cached) " >&6 -else - ac_check_lib_save_LIBS=$LIBS -LIBS="-lsvld $LIBS" -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -/* Override any GCC internal prototype to avoid an error. - Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -#ifdef __cplusplus -extern "C" -#endif -char dlopen (); -int -main () -{ -return dlopen (); - ; - return 0; -} -_ACEOF -if ac_fn_c_try_link "$LINENO"; then : - ac_cv_lib_svld_dlopen=yes -else - ac_cv_lib_svld_dlopen=no -fi -rm -f core conftest.err conftest.$ac_objext \ - conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_svld_dlopen" >&5 -$as_echo "$ac_cv_lib_svld_dlopen" >&6; } -if test "x$ac_cv_lib_svld_dlopen" = xyes; then : - lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-lsvld" -else - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for dld_link in -ldld" >&5 -$as_echo_n "checking for dld_link in -ldld... " >&6; } -if ${ac_cv_lib_dld_dld_link+:} false; then : - $as_echo_n "(cached) " >&6 -else - ac_check_lib_save_LIBS=$LIBS -LIBS="-ldld $LIBS" -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -/* Override any GCC internal prototype to avoid an error. - Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -#ifdef __cplusplus -extern "C" -#endif -char dld_link (); -int -main () -{ -return dld_link (); - ; - return 0; -} -_ACEOF -if ac_fn_c_try_link "$LINENO"; then : - ac_cv_lib_dld_dld_link=yes -else - ac_cv_lib_dld_dld_link=no -fi -rm -f core conftest.err conftest.$ac_objext \ - conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dld_dld_link" >&5 -$as_echo "$ac_cv_lib_dld_dld_link" >&6; } -if test "x$ac_cv_lib_dld_dld_link" = xyes; then : - lt_cv_dlopen="dld_link" lt_cv_dlopen_libs="-ldld" -fi - - -fi - - -fi - - -fi - - -fi - - -fi - - ;; - esac - - if test "x$lt_cv_dlopen" != xno; then - enable_dlopen=yes - else - enable_dlopen=no - fi - - case $lt_cv_dlopen in - dlopen) - save_CPPFLAGS="$CPPFLAGS" - test "x$ac_cv_header_dlfcn_h" = xyes && CPPFLAGS="$CPPFLAGS -DHAVE_DLFCN_H" - - save_LDFLAGS="$LDFLAGS" - wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $export_dynamic_flag_spec\" - - save_LIBS="$LIBS" - LIBS="$lt_cv_dlopen_libs $LIBS" - - { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether a program can dlopen itself" >&5 -$as_echo_n "checking whether a program can dlopen itself... " >&6; } -if ${lt_cv_dlopen_self+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test "$cross_compiling" = yes; then : - lt_cv_dlopen_self=cross -else - lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 - lt_status=$lt_dlunknown - cat > conftest.$ac_ext <<_LT_EOF -#line $LINENO "configure" -#include "confdefs.h" - -#if HAVE_DLFCN_H -#include -#endif - -#include - -#ifdef RTLD_GLOBAL -# define LT_DLGLOBAL RTLD_GLOBAL -#else -# ifdef DL_GLOBAL -# define LT_DLGLOBAL DL_GLOBAL -# else -# define LT_DLGLOBAL 0 -# endif -#endif - -/* We may have to define LT_DLLAZY_OR_NOW in the command line if we - find out it does not work in some platform. */ -#ifndef LT_DLLAZY_OR_NOW -# ifdef RTLD_LAZY -# define LT_DLLAZY_OR_NOW RTLD_LAZY -# else -# ifdef DL_LAZY -# define LT_DLLAZY_OR_NOW DL_LAZY -# else -# ifdef RTLD_NOW -# define LT_DLLAZY_OR_NOW RTLD_NOW -# else -# ifdef DL_NOW -# define LT_DLLAZY_OR_NOW DL_NOW -# else -# define LT_DLLAZY_OR_NOW 0 -# endif -# endif -# endif -# endif -#endif - -/* When -fvisbility=hidden is used, assume the code has been annotated - correspondingly for the symbols needed. */ -#if defined(__GNUC__) && (((__GNUC__ == 3) && (__GNUC_MINOR__ >= 3)) || (__GNUC__ > 3)) -int fnord () __attribute__((visibility("default"))); -#endif - -int fnord () { return 42; } -int main () -{ - void *self = dlopen (0, LT_DLGLOBAL|LT_DLLAZY_OR_NOW); - int status = $lt_dlunknown; - - if (self) - { - if (dlsym (self,"fnord")) status = $lt_dlno_uscore; - else - { - if (dlsym( self,"_fnord")) status = $lt_dlneed_uscore; - else puts (dlerror ()); - } - /* dlclose (self); */ - } - else - puts (dlerror ()); - - return status; -} -_LT_EOF - if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_link\""; } >&5 - (eval $ac_link) 2>&5 - ac_status=$? - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; } && test -s conftest${ac_exeext} 2>/dev/null; then - (./conftest; exit; ) >&5 2>/dev/null - lt_status=$? - case x$lt_status in - x$lt_dlno_uscore) lt_cv_dlopen_self=yes ;; - x$lt_dlneed_uscore) lt_cv_dlopen_self=yes ;; - x$lt_dlunknown|x*) lt_cv_dlopen_self=no ;; - esac - else : - # compilation failed - lt_cv_dlopen_self=no - fi -fi -rm -fr conftest* - - -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_dlopen_self" >&5 -$as_echo "$lt_cv_dlopen_self" >&6; } - - if test "x$lt_cv_dlopen_self" = xyes; then - wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $lt_prog_compiler_static\" - { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether a statically linked program can dlopen itself" >&5 -$as_echo_n "checking whether a statically linked program can dlopen itself... " >&6; } -if ${lt_cv_dlopen_self_static+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test "$cross_compiling" = yes; then : - lt_cv_dlopen_self_static=cross -else - lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 - lt_status=$lt_dlunknown - cat > conftest.$ac_ext <<_LT_EOF -#line $LINENO "configure" -#include "confdefs.h" - -#if HAVE_DLFCN_H -#include -#endif - -#include - -#ifdef RTLD_GLOBAL -# define LT_DLGLOBAL RTLD_GLOBAL -#else -# ifdef DL_GLOBAL -# define LT_DLGLOBAL DL_GLOBAL -# else -# define LT_DLGLOBAL 0 -# endif -#endif - -/* We may have to define LT_DLLAZY_OR_NOW in the command line if we - find out it does not work in some platform. */ -#ifndef LT_DLLAZY_OR_NOW -# ifdef RTLD_LAZY -# define LT_DLLAZY_OR_NOW RTLD_LAZY -# else -# ifdef DL_LAZY -# define LT_DLLAZY_OR_NOW DL_LAZY -# else -# ifdef RTLD_NOW -# define LT_DLLAZY_OR_NOW RTLD_NOW -# else -# ifdef DL_NOW -# define LT_DLLAZY_OR_NOW DL_NOW -# else -# define LT_DLLAZY_OR_NOW 0 -# endif -# endif -# endif -# endif -#endif - -/* When -fvisbility=hidden is used, assume the code has been annotated - correspondingly for the symbols needed. */ -#if defined(__GNUC__) && (((__GNUC__ == 3) && (__GNUC_MINOR__ >= 3)) || (__GNUC__ > 3)) -int fnord () __attribute__((visibility("default"))); -#endif - -int fnord () { return 42; } -int main () -{ - void *self = dlopen (0, LT_DLGLOBAL|LT_DLLAZY_OR_NOW); - int status = $lt_dlunknown; - - if (self) - { - if (dlsym (self,"fnord")) status = $lt_dlno_uscore; - else - { - if (dlsym( self,"_fnord")) status = $lt_dlneed_uscore; - else puts (dlerror ()); - } - /* dlclose (self); */ - } - else - puts (dlerror ()); - - return status; -} -_LT_EOF - if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_link\""; } >&5 - (eval $ac_link) 2>&5 - ac_status=$? - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; } && test -s conftest${ac_exeext} 2>/dev/null; then - (./conftest; exit; ) >&5 2>/dev/null - lt_status=$? - case x$lt_status in - x$lt_dlno_uscore) lt_cv_dlopen_self_static=yes ;; - x$lt_dlneed_uscore) lt_cv_dlopen_self_static=yes ;; - x$lt_dlunknown|x*) lt_cv_dlopen_self_static=no ;; - esac - else : - # compilation failed - lt_cv_dlopen_self_static=no - fi -fi -rm -fr conftest* - - -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_dlopen_self_static" >&5 -$as_echo "$lt_cv_dlopen_self_static" >&6; } - fi - - CPPFLAGS="$save_CPPFLAGS" - LDFLAGS="$save_LDFLAGS" - LIBS="$save_LIBS" - ;; - esac - - case $lt_cv_dlopen_self in - yes|no) enable_dlopen_self=$lt_cv_dlopen_self ;; - *) enable_dlopen_self=unknown ;; - esac - - case $lt_cv_dlopen_self_static in - yes|no) enable_dlopen_self_static=$lt_cv_dlopen_self_static ;; - *) enable_dlopen_self_static=unknown ;; - esac -fi - - - - - - - - - - - - - - - - - -striplib= -old_striplib= -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether stripping libraries is possible" >&5 -$as_echo_n "checking whether stripping libraries is possible... " >&6; } -if test -n "$STRIP" && $STRIP -V 2>&1 | $GREP "GNU strip" >/dev/null; then - test -z "$old_striplib" && old_striplib="$STRIP --strip-debug" - test -z "$striplib" && striplib="$STRIP --strip-unneeded" - { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 -$as_echo "yes" >&6; } -else -# FIXME - insert some real tests, host_os isn't really good enough - case $host_os in - darwin*) - if test -n "$STRIP" ; then - striplib="$STRIP -x" - old_striplib="$STRIP -S" - { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 -$as_echo "yes" >&6; } - else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } - fi - ;; - *) - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } - ;; - esac -fi - - - - - - - - - - - - - # Report which library types will actually be built - { $as_echo "$as_me:${as_lineno-$LINENO}: checking if libtool supports shared libraries" >&5 -$as_echo_n "checking if libtool supports shared libraries... " >&6; } - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $can_build_shared" >&5 -$as_echo "$can_build_shared" >&6; } - - { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to build shared libraries" >&5 -$as_echo_n "checking whether to build shared libraries... " >&6; } - test "$can_build_shared" = "no" && enable_shared=no - - # On AIX, shared libraries and static libraries use the same namespace, and - # are all built from PIC. - case $host_os in - aix3*) - test "$enable_shared" = yes && enable_static=no - if test -n "$RANLIB"; then - archive_cmds="$archive_cmds~\$RANLIB \$lib" - postinstall_cmds='$RANLIB $lib' - fi - ;; - - aix[4-9]*) - if test "$host_cpu" != ia64 && test "$aix_use_runtimelinking" = no ; then - test "$enable_shared" = yes && enable_static=no - fi - ;; - esac - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $enable_shared" >&5 -$as_echo "$enable_shared" >&6; } - - { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to build static libraries" >&5 -$as_echo_n "checking whether to build static libraries... " >&6; } - # Make sure either enable_shared or enable_static is yes. - test "$enable_shared" = yes || enable_static=yes - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $enable_static" >&5 -$as_echo "$enable_static" >&6; } - - - - -fi -ac_ext=c -ac_cpp='$CPP $CPPFLAGS' -ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_c_compiler_gnu - -CC="$lt_save_CC" - - - - - - - - - - - - - - - - ac_config_commands="$ac_config_commands libtool" - - - - -# Only expand once: - - - - - -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking target system type" >&5 -$as_echo_n "checking target system type... " >&6; } -if ${ac_cv_target+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test "x$target_alias" = x; then - ac_cv_target=$ac_cv_host -else - ac_cv_target=`$SHELL "$ac_aux_dir/config.sub" $target_alias` || - as_fn_error $? "$SHELL $ac_aux_dir/config.sub $target_alias failed" "$LINENO" 5 -fi - -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_target" >&5 -$as_echo "$ac_cv_target" >&6; } -case $ac_cv_target in -*-*-*) ;; -*) as_fn_error $? "invalid value of canonical target" "$LINENO" 5;; -esac -target=$ac_cv_target -ac_save_IFS=$IFS; IFS='-' -set x $ac_cv_target -shift -target_cpu=$1 -target_vendor=$2 -shift; shift -# Remember, the first character of IFS is used to create $*, -# except with old shells: -target_os=$* -IFS=$ac_save_IFS -case $target_os in *\ *) target_os=`echo "$target_os" | sed 's/ /-/g'`;; esac - - -# The aliases save the names the user supplied, while $host etc. -# will get canonicalized. -test -n "$target_alias" && - test "$program_prefix$program_suffix$program_transform_name" = \ - NONENONEs,x,x, && - program_prefix=${target_alias}- - -am__api_version='1.14' - -# Find a good install program. We prefer a C program (faster), -# so one script is as good as another. But avoid the broken or -# incompatible versions: -# SysV /etc/install, /usr/sbin/install -# SunOS /usr/etc/install -# IRIX /sbin/install -# AIX /bin/install -# AmigaOS /C/install, which installs bootblocks on floppy discs -# AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag -# AFS /usr/afsws/bin/install, which mishandles nonexistent args -# SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff" -# OS/2's system install, which has a completely different semantic -# ./install, which can be erroneously created by make from ./install.sh. -# Reject install programs that cannot install multiple files. -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for a BSD-compatible install" >&5 -$as_echo_n "checking for a BSD-compatible install... " >&6; } -if test -z "$INSTALL"; then -if ${ac_cv_path_install+:} false; then : - $as_echo_n "(cached) " >&6 -else - as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - # Account for people who put trailing slashes in PATH elements. -case $as_dir/ in #(( - ./ | .// | /[cC]/* | \ - /etc/* | /usr/sbin/* | /usr/etc/* | /sbin/* | /usr/afsws/bin/* | \ - ?:[\\/]os2[\\/]install[\\/]* | ?:[\\/]OS2[\\/]INSTALL[\\/]* | \ - /usr/ucb/* ) ;; - *) - # OSF1 and SCO ODT 3.0 have their own names for install. - # Don't use installbsd from OSF since it installs stuff as root - # by default. - for ac_prog in ginstall scoinst install; do - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_prog$ac_exec_ext"; then - if test $ac_prog = install && - grep dspmsg "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then - # AIX install. It has an incompatible calling convention. - : - elif test $ac_prog = install && - grep pwplus "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then - # program-specific install script used by HP pwplus--don't use. - : - else - rm -rf conftest.one conftest.two conftest.dir - echo one > conftest.one - echo two > conftest.two - mkdir conftest.dir - if "$as_dir/$ac_prog$ac_exec_ext" -c conftest.one conftest.two "`pwd`/conftest.dir" && - test -s conftest.one && test -s conftest.two && - test -s conftest.dir/conftest.one && - test -s conftest.dir/conftest.two - then - ac_cv_path_install="$as_dir/$ac_prog$ac_exec_ext -c" - break 3 - fi - fi - fi - done - done - ;; -esac - - done -IFS=$as_save_IFS - -rm -rf conftest.one conftest.two conftest.dir - -fi - if test "${ac_cv_path_install+set}" = set; then - INSTALL=$ac_cv_path_install - else - # As a last resort, use the slow shell script. Don't cache a - # value for INSTALL within a source directory, because that will - # break other packages using the cache if that directory is - # removed, or if the value is a relative name. - INSTALL=$ac_install_sh - fi -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $INSTALL" >&5 -$as_echo "$INSTALL" >&6; } - -# Use test -z because SunOS4 sh mishandles braces in ${var-val}. -# It thinks the first close brace ends the variable substitution. -test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}' - -test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL}' - -test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644' - -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether build environment is sane" >&5 -$as_echo_n "checking whether build environment is sane... " >&6; } -# Reject unsafe characters in $srcdir or the absolute working directory -# name. Accept space and tab only in the latter. -am_lf=' -' -case `pwd` in - *[\\\"\#\$\&\'\`$am_lf]*) - as_fn_error $? "unsafe absolute working directory name" "$LINENO" 5;; -esac -case $srcdir in - *[\\\"\#\$\&\'\`$am_lf\ \ ]*) - as_fn_error $? "unsafe srcdir value: '$srcdir'" "$LINENO" 5;; -esac - -# Do 'set' in a subshell so we don't clobber the current shell's -# arguments. Must try -L first in case configure is actually a -# symlink; some systems play weird games with the mod time of symlinks -# (eg FreeBSD returns the mod time of the symlink's containing -# directory). -if ( - am_has_slept=no - for am_try in 1 2; do - echo "timestamp, slept: $am_has_slept" > conftest.file - set X `ls -Lt "$srcdir/configure" conftest.file 2> /dev/null` - if test "$*" = "X"; then - # -L didn't work. - set X `ls -t "$srcdir/configure" conftest.file` - fi - if test "$*" != "X $srcdir/configure conftest.file" \ - && test "$*" != "X conftest.file $srcdir/configure"; then - - # If neither matched, then we have a broken ls. This can happen - # if, for instance, CONFIG_SHELL is bash and it inherits a - # broken ls alias from the environment. This has actually - # happened. Such a system could not be considered "sane". - as_fn_error $? "ls -t appears to fail. Make sure there is not a broken - alias in your environment" "$LINENO" 5 - fi - if test "$2" = conftest.file || test $am_try -eq 2; then - break - fi - # Just in case. - sleep 1 - am_has_slept=yes - done - test "$2" = conftest.file - ) -then - # Ok. - : -else - as_fn_error $? "newly created file is older than distributed files! -Check your system clock" "$LINENO" 5 -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 -$as_echo "yes" >&6; } -# If we didn't sleep, we still need to ensure time stamps of config.status and -# generated files are strictly newer. -am_sleep_pid= -if grep 'slept: no' conftest.file >/dev/null 2>&1; then - ( sleep 1 ) & - am_sleep_pid=$! -fi - -rm -f conftest.file - -test "$program_prefix" != NONE && - program_transform_name="s&^&$program_prefix&;$program_transform_name" -# Use a double $ so make ignores it. -test "$program_suffix" != NONE && - program_transform_name="s&\$&$program_suffix&;$program_transform_name" -# Double any \ or $. -# By default was `s,x,x', remove it if useless. -ac_script='s/[\\$]/&&/g;s/;s,x,x,$//' -program_transform_name=`$as_echo "$program_transform_name" | sed "$ac_script"` - -if test x"${MISSING+set}" != xset; then - case $am_aux_dir in - *\ * | *\ *) - MISSING="\${SHELL} \"$am_aux_dir/missing\"" ;; - *) - MISSING="\${SHELL} $am_aux_dir/missing" ;; - esac -fi -# Use eval to expand $SHELL -if eval "$MISSING --is-lightweight"; then - am_missing_run="$MISSING " -else - am_missing_run= - { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: 'missing' script is too old or missing" >&5 -$as_echo "$as_me: WARNING: 'missing' script is too old or missing" >&2;} -fi - -if test x"${install_sh}" != xset; then - case $am_aux_dir in - *\ * | *\ *) - install_sh="\${SHELL} '$am_aux_dir/install-sh'" ;; - *) - install_sh="\${SHELL} $am_aux_dir/install-sh" - esac -fi - -# Installed binaries are usually stripped using 'strip' when the user -# run "make install-strip". However 'strip' might not be the right -# tool to use in cross-compilation environments, therefore Automake -# will honor the 'STRIP' environment variable to overrule this program. -if test "$cross_compiling" != no; then - if test -n "$ac_tool_prefix"; then - # Extract the first word of "${ac_tool_prefix}strip", so it can be a program name with args. -set dummy ${ac_tool_prefix}strip; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_STRIP+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -n "$STRIP"; then - ac_cv_prog_STRIP="$STRIP" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_STRIP="${ac_tool_prefix}strip" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -STRIP=$ac_cv_prog_STRIP -if test -n "$STRIP"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $STRIP" >&5 -$as_echo "$STRIP" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - -fi -if test -z "$ac_cv_prog_STRIP"; then - ac_ct_STRIP=$STRIP - # Extract the first word of "strip", so it can be a program name with args. -set dummy strip; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_ac_ct_STRIP+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -n "$ac_ct_STRIP"; then - ac_cv_prog_ac_ct_STRIP="$ac_ct_STRIP" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_ac_ct_STRIP="strip" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -ac_ct_STRIP=$ac_cv_prog_ac_ct_STRIP -if test -n "$ac_ct_STRIP"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_STRIP" >&5 -$as_echo "$ac_ct_STRIP" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - if test "x$ac_ct_STRIP" = x; then - STRIP=":" - else - case $cross_compiling:$ac_tool_warned in -yes:) -{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 -$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} -ac_tool_warned=yes ;; -esac - STRIP=$ac_ct_STRIP - fi -else - STRIP="$ac_cv_prog_STRIP" -fi - -fi -INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s" - -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for a thread-safe mkdir -p" >&5 -$as_echo_n "checking for a thread-safe mkdir -p... " >&6; } -if test -z "$MKDIR_P"; then - if ${ac_cv_path_mkdir+:} false; then : - $as_echo_n "(cached) " >&6 -else - as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH$PATH_SEPARATOR/opt/sfw/bin -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_prog in mkdir gmkdir; do - for ac_exec_ext in '' $ac_executable_extensions; do - as_fn_executable_p "$as_dir/$ac_prog$ac_exec_ext" || continue - case `"$as_dir/$ac_prog$ac_exec_ext" --version 2>&1` in #( - 'mkdir (GNU coreutils) '* | \ - 'mkdir (coreutils) '* | \ - 'mkdir (fileutils) '4.1*) - ac_cv_path_mkdir=$as_dir/$ac_prog$ac_exec_ext - break 3;; - esac - done - done - done -IFS=$as_save_IFS - -fi - - test -d ./--version && rmdir ./--version - if test "${ac_cv_path_mkdir+set}" = set; then - MKDIR_P="$ac_cv_path_mkdir -p" - else - # As a last resort, use the slow shell script. Don't cache a - # value for MKDIR_P within a source directory, because that will - # break other packages using the cache if that directory is - # removed, or if the value is a relative name. - MKDIR_P="$ac_install_sh -d" - fi -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $MKDIR_P" >&5 -$as_echo "$MKDIR_P" >&6; } - -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ${MAKE-make} sets \$(MAKE)" >&5 -$as_echo_n "checking whether ${MAKE-make} sets \$(MAKE)... " >&6; } -set x ${MAKE-make} -ac_make=`$as_echo "$2" | sed 's/+/p/g; s/[^a-zA-Z0-9_]/_/g'` -if eval \${ac_cv_prog_make_${ac_make}_set+:} false; then : - $as_echo_n "(cached) " >&6 -else - cat >conftest.make <<\_ACEOF -SHELL = /bin/sh -all: - @echo '@@@%%%=$(MAKE)=@@@%%%' -_ACEOF -# GNU make sometimes prints "make[1]: Entering ...", which would confuse us. -case `${MAKE-make} -f conftest.make 2>/dev/null` in - *@@@%%%=?*=@@@%%%*) - eval ac_cv_prog_make_${ac_make}_set=yes;; - *) - eval ac_cv_prog_make_${ac_make}_set=no;; -esac -rm -f conftest.make -fi -if eval test \$ac_cv_prog_make_${ac_make}_set = yes; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 -$as_echo "yes" >&6; } - SET_MAKE= -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } - SET_MAKE="MAKE=${MAKE-make}" -fi - -rm -rf .tst 2>/dev/null -mkdir .tst 2>/dev/null -if test -d .tst; then - am__leading_dot=. -else - am__leading_dot=_ -fi -rmdir .tst 2>/dev/null - -DEPDIR="${am__leading_dot}deps" - -ac_config_commands="$ac_config_commands depfiles" - - -am_make=${MAKE-make} -cat > confinc << 'END' -am__doit: - @echo this is the am__doit target -.PHONY: am__doit -END -# If we don't find an include directive, just comment out the code. -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for style of include used by $am_make" >&5 -$as_echo_n "checking for style of include used by $am_make... " >&6; } -am__include="#" -am__quote= -_am_result=none -# First try GNU make style include. -echo "include confinc" > confmf -# Ignore all kinds of additional output from 'make'. -case `$am_make -s -f confmf 2> /dev/null` in #( -*the\ am__doit\ target*) - am__include=include - am__quote= - _am_result=GNU - ;; -esac -# Now try BSD make style include. -if test "$am__include" = "#"; then - echo '.include "confinc"' > confmf - case `$am_make -s -f confmf 2> /dev/null` in #( - *the\ am__doit\ target*) - am__include=.include - am__quote="\"" - _am_result=BSD - ;; - esac -fi - - -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $_am_result" >&5 -$as_echo "$_am_result" >&6; } -rm -f confinc confmf - -# Check whether --enable-dependency-tracking was given. -if test "${enable_dependency_tracking+set}" = set; then : - enableval=$enable_dependency_tracking; -fi - -if test "x$enable_dependency_tracking" != xno; then - am_depcomp="$ac_aux_dir/depcomp" - AMDEPBACKSLASH='\' - am__nodep='_no' -fi - if test "x$enable_dependency_tracking" != xno; then - AMDEP_TRUE= - AMDEP_FALSE='#' -else - AMDEP_TRUE='#' - AMDEP_FALSE= -fi - - -# Check whether --enable-silent-rules was given. -if test "${enable_silent_rules+set}" = set; then : - enableval=$enable_silent_rules; -fi - -case $enable_silent_rules in # ((( - yes) AM_DEFAULT_VERBOSITY=0;; - no) AM_DEFAULT_VERBOSITY=1;; - *) AM_DEFAULT_VERBOSITY=1;; -esac -am_make=${MAKE-make} -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $am_make supports nested variables" >&5 -$as_echo_n "checking whether $am_make supports nested variables... " >&6; } -if ${am_cv_make_support_nested_variables+:} false; then : - $as_echo_n "(cached) " >&6 -else - if $as_echo 'TRUE=$(BAR$(V)) -BAR0=false -BAR1=true -V=1 -am__doit: - @$(TRUE) -.PHONY: am__doit' | $am_make -f - >/dev/null 2>&1; then - am_cv_make_support_nested_variables=yes -else - am_cv_make_support_nested_variables=no -fi -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_make_support_nested_variables" >&5 -$as_echo "$am_cv_make_support_nested_variables" >&6; } -if test $am_cv_make_support_nested_variables = yes; then - AM_V='$(V)' - AM_DEFAULT_V='$(AM_DEFAULT_VERBOSITY)' -else - AM_V=$AM_DEFAULT_VERBOSITY - AM_DEFAULT_V=$AM_DEFAULT_VERBOSITY -fi -AM_BACKSLASH='\' - -if test "`cd $srcdir && pwd`" != "`pwd`"; then - # Use -I$(srcdir) only when $(srcdir) != ., so that make's output - # is not polluted with repeated "-I." - am__isrc=' -I$(srcdir)' - # test to see if srcdir already configured - if test -f $srcdir/config.status; then - as_fn_error $? "source directory already configured; run \"make distclean\" there first" "$LINENO" 5 - fi -fi - -# test whether we have cygpath -if test -z "$CYGPATH_W"; then - if (cygpath --version) >/dev/null 2>/dev/null; then - CYGPATH_W='cygpath -w' - else - CYGPATH_W=echo - fi -fi - - -# Define the identity of the package. - PACKAGE='nghttp2' - VERSION='1.0.0' - - -cat >>confdefs.h <<_ACEOF -#define PACKAGE "$PACKAGE" -_ACEOF - - -cat >>confdefs.h <<_ACEOF -#define VERSION "$VERSION" -_ACEOF - -# Some tools Automake needs. - -ACLOCAL=${ACLOCAL-"${am_missing_run}aclocal-${am__api_version}"} - - -AUTOCONF=${AUTOCONF-"${am_missing_run}autoconf"} - - -AUTOMAKE=${AUTOMAKE-"${am_missing_run}automake-${am__api_version}"} - - -AUTOHEADER=${AUTOHEADER-"${am_missing_run}autoheader"} - - -MAKEINFO=${MAKEINFO-"${am_missing_run}makeinfo"} - -# For better backward compatibility. To be removed once Automake 1.9.x -# dies out for good. For more background, see: -# -# -mkdir_p='$(MKDIR_P)' - -# We need awk for the "check" target. The system "awk" is bad on -# some platforms. -# Always define AMTAR for backward compatibility. Yes, it's still used -# in the wild :-( We should find a proper way to deprecate it ... -AMTAR='$${TAR-tar}' - - -# We'll loop over all known methods to create a tar archive until one works. -_am_tools='gnutar pax cpio none' - -am__tar='$${TAR-tar} chof - "$$tardir"' am__untar='$${TAR-tar} xf -' - - - - - -depcc="$CC" am_compiler_list= - -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking dependency style of $depcc" >&5 -$as_echo_n "checking dependency style of $depcc... " >&6; } -if ${am_cv_CC_dependencies_compiler_type+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then - # We make a subdir and do the tests there. Otherwise we can end up - # making bogus files that we don't know about and never remove. For - # instance it was reported that on HP-UX the gcc test will end up - # making a dummy file named 'D' -- because '-MD' means "put the output - # in D". - rm -rf conftest.dir - mkdir conftest.dir - # Copy depcomp to subdir because otherwise we won't find it if we're - # using a relative directory. - cp "$am_depcomp" conftest.dir - cd conftest.dir - # We will build objects and dependencies in a subdirectory because - # it helps to detect inapplicable dependency modes. For instance - # both Tru64's cc and ICC support -MD to output dependencies as a - # side effect of compilation, but ICC will put the dependencies in - # the current directory while Tru64 will put them in the object - # directory. - mkdir sub - - am_cv_CC_dependencies_compiler_type=none - if test "$am_compiler_list" = ""; then - am_compiler_list=`sed -n 's/^#*\([a-zA-Z0-9]*\))$/\1/p' < ./depcomp` - fi - am__universal=false - case " $depcc " in #( - *\ -arch\ *\ -arch\ *) am__universal=true ;; - esac - - for depmode in $am_compiler_list; do - # Setup a source with many dependencies, because some compilers - # like to wrap large dependency lists on column 80 (with \), and - # we should not choose a depcomp mode which is confused by this. - # - # We need to recreate these files for each test, as the compiler may - # overwrite some of them when testing with obscure command lines. - # This happens at least with the AIX C compiler. - : > sub/conftest.c - for i in 1 2 3 4 5 6; do - echo '#include "conftst'$i'.h"' >> sub/conftest.c - # Using ": > sub/conftst$i.h" creates only sub/conftst1.h with - # Solaris 10 /bin/sh. - echo '/* dummy */' > sub/conftst$i.h - done - echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf - - # We check with '-c' and '-o' for the sake of the "dashmstdout" - # mode. It turns out that the SunPro C++ compiler does not properly - # handle '-M -o', and we need to detect this. Also, some Intel - # versions had trouble with output in subdirs. - am__obj=sub/conftest.${OBJEXT-o} - am__minus_obj="-o $am__obj" - case $depmode in - gcc) - # This depmode causes a compiler race in universal mode. - test "$am__universal" = false || continue - ;; - nosideeffect) - # After this tag, mechanisms are not by side-effect, so they'll - # only be used when explicitly requested. - if test "x$enable_dependency_tracking" = xyes; then - continue - else - break - fi - ;; - msvc7 | msvc7msys | msvisualcpp | msvcmsys) - # This compiler won't grok '-c -o', but also, the minuso test has - # not run yet. These depmodes are late enough in the game, and - # so weak that their functioning should not be impacted. - am__obj=conftest.${OBJEXT-o} - am__minus_obj= - ;; - none) break ;; - esac - if depmode=$depmode \ - source=sub/conftest.c object=$am__obj \ - depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \ - $SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \ - >/dev/null 2>conftest.err && - grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 && - grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 && - grep $am__obj sub/conftest.Po > /dev/null 2>&1 && - ${MAKE-make} -s -f confmf > /dev/null 2>&1; then - # icc doesn't choke on unknown options, it will just issue warnings - # or remarks (even with -Werror). So we grep stderr for any message - # that says an option was ignored or not supported. - # When given -MP, icc 7.0 and 7.1 complain thusly: - # icc: Command line warning: ignoring option '-M'; no argument required - # The diagnosis changed in icc 8.0: - # icc: Command line remark: option '-MP' not supported - if (grep 'ignoring option' conftest.err || - grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else - am_cv_CC_dependencies_compiler_type=$depmode - break - fi - fi - done - - cd .. - rm -rf conftest.dir -else - am_cv_CC_dependencies_compiler_type=none -fi - -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_CC_dependencies_compiler_type" >&5 -$as_echo "$am_cv_CC_dependencies_compiler_type" >&6; } -CCDEPMODE=depmode=$am_cv_CC_dependencies_compiler_type - - if - test "x$enable_dependency_tracking" != xno \ - && test "$am_cv_CC_dependencies_compiler_type" = gcc3; then - am__fastdepCC_TRUE= - am__fastdepCC_FALSE='#' -else - am__fastdepCC_TRUE='#' - am__fastdepCC_FALSE= -fi - - - -# POSIX will say in a future version that running "rm -f" with no argument -# is OK; and we want to be able to make that assumption in our Makefile -# recipes. So use an aggressive probe to check that the usage we want is -# actually supported "in the wild" to an acceptable degree. -# See automake bug#10828. -# To make any issue more visible, cause the running configure to be aborted -# by default if the 'rm' program in use doesn't match our expectations; the -# user can still override this though. -if rm -f && rm -fr && rm -rf; then : OK; else - cat >&2 <<'END' -Oops! - -Your 'rm' program seems unable to run without file operands specified -on the command line, even when the '-f' option is present. This is contrary -to the behaviour of most rm programs out there, and not conforming with -the upcoming POSIX standard: - -Please tell bug-automake@gnu.org about your system, including the value -of your $PATH and any error possibly output before this message. This -can help us improve future automake versions. - -END - if test x"$ACCEPT_INFERIOR_RM_PROGRAM" = x"yes"; then - echo 'Configuration will proceed anyway, since you have set the' >&2 - echo 'ACCEPT_INFERIOR_RM_PROGRAM variable to "yes"' >&2 - echo >&2 - else - cat >&2 <<'END' -Aborting the configuration process, to ensure you take notice of the issue. - -You can download and install GNU coreutils to get an 'rm' implementation -that behaves properly: . - -If you want to complete the configuration process using your problematic -'rm' anyway, export the environment variable ACCEPT_INFERIOR_RM_PROGRAM -to "yes", and re-run configure. - -END - as_fn_error $? "Your 'rm' program is bad, sorry." "$LINENO" 5 - fi -fi - -# comment out for now since this requires automake 1.13 or higher and -# travis has older one. -# AM_EXTRA_RECURSIVE_TARGETS([it]) - -# Check whether --enable-silent-rules was given. -if test "${enable_silent_rules+set}" = set; then : - enableval=$enable_silent_rules; -fi - -case $enable_silent_rules in # ((( - yes) AM_DEFAULT_VERBOSITY=0;; - no) AM_DEFAULT_VERBOSITY=1;; - *) AM_DEFAULT_VERBOSITY=0;; -esac -am_make=${MAKE-make} -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $am_make supports nested variables" >&5 -$as_echo_n "checking whether $am_make supports nested variables... " >&6; } -if ${am_cv_make_support_nested_variables+:} false; then : - $as_echo_n "(cached) " >&6 -else - if $as_echo 'TRUE=$(BAR$(V)) -BAR0=false -BAR1=true -V=1 -am__doit: - @$(TRUE) -.PHONY: am__doit' | $am_make -f - >/dev/null 2>&1; then - am_cv_make_support_nested_variables=yes -else - am_cv_make_support_nested_variables=no -fi -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_make_support_nested_variables" >&5 -$as_echo "$am_cv_make_support_nested_variables" >&6; } -if test $am_cv_make_support_nested_variables = yes; then - AM_V='$(V)' - AM_DEFAULT_V='$(AM_DEFAULT_VERBOSITY)' -else - AM_V=$AM_DEFAULT_VERBOSITY - AM_DEFAULT_V=$AM_DEFAULT_VERBOSITY -fi -AM_BACKSLASH='\' - - - -ac_config_headers="$ac_config_headers config.h" - - -LT_CURRENT=14 - -LT_REVISION=0 - -LT_AGE=0 - - -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"` - - - -# Check whether --enable-werror was given. -if test "${enable_werror+set}" = set; then : - enableval=$enable_werror; werror=$enableval -else - werror=no -fi - - -# Check whether --enable-debug was given. -if test "${enable_debug+set}" = set; then : - enableval=$enable_debug; debug=$enableval -else - debug=no -fi - - -# Check whether --enable-threads was given. -if test "${enable_threads+set}" = set; then : - enableval=$enable_threads; threads=$enableval -else - threads=yes -fi - - -# Check whether --enable-app was given. -if test "${enable_app+set}" = set; then : - enableval=$enable_app; request_app=$enableval -else - request_app=check -fi - - -# Check whether --enable-hpack-tools was given. -if test "${enable_hpack_tools+set}" = set; then : - enableval=$enable_hpack_tools; request_hpack_tools=$enableval -else - request_hpack_tools=check -fi - - -# Check whether --enable-asio-lib was given. -if test "${enable_asio_lib+set}" = set; then : - enableval=$enable_asio_lib; request_asio_lib=$enableval -else - request_asio_lib=no -fi - - -# Check whether --enable-examples was given. -if test "${enable_examples+set}" = set; then : - enableval=$enable_examples; request_examples=$enableval -else - request_examples=check -fi - - -# Check whether --enable-python-bindings was given. -if test "${enable_python_bindings+set}" = set; then : - enableval=$enable_python_bindings; request_python_bindings=$enableval -else - request_python_bindings=check -fi - - -# Check whether --enable-failmalloc was given. -if test "${enable_failmalloc+set}" = set; then : - enableval=$enable_failmalloc; request_failmalloc=$enableval -else - request_failmalloc=yes -fi - - - -# Check whether --with-libxml2 was given. -if test "${with_libxml2+set}" = set; then : - withval=$with_libxml2; request_libxml2=$withval -else - request_libxml2=check -fi - - - -# Check whether --with-jemalloc was given. -if test "${with_jemalloc+set}" = set; then : - withval=$with_jemalloc; request_jemalloc=$withval -else - request_jemalloc=check -fi - - - -# Check whether --with-spdylay was given. -if test "${with_spdylay+set}" = set; then : - withval=$with_spdylay; request_spdylay=$withval -else - request_spdylay=check -fi - - - -# Check whether --with-cython was given. -if test "${with_cython+set}" = set; then : - withval=$with_cython; cython_path=$withval -fi - - - - -ac_ext=c -ac_cpp='$CPP $CPPFLAGS' -ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_c_compiler_gnu -if test -n "$ac_tool_prefix"; then - # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args. -set dummy ${ac_tool_prefix}gcc; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_CC+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -n "$CC"; then - ac_cv_prog_CC="$CC" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_CC="${ac_tool_prefix}gcc" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -CC=$ac_cv_prog_CC -if test -n "$CC"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 -$as_echo "$CC" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - -fi -if test -z "$ac_cv_prog_CC"; then - ac_ct_CC=$CC - # Extract the first word of "gcc", so it can be a program name with args. -set dummy gcc; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_ac_ct_CC+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -n "$ac_ct_CC"; then - ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_ac_ct_CC="gcc" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -ac_ct_CC=$ac_cv_prog_ac_ct_CC -if test -n "$ac_ct_CC"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 -$as_echo "$ac_ct_CC" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - if test "x$ac_ct_CC" = x; then - CC="" - else - case $cross_compiling:$ac_tool_warned in -yes:) -{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 -$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} -ac_tool_warned=yes ;; -esac - CC=$ac_ct_CC - fi -else - CC="$ac_cv_prog_CC" -fi - -if test -z "$CC"; then - if test -n "$ac_tool_prefix"; then - # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args. -set dummy ${ac_tool_prefix}cc; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_CC+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -n "$CC"; then - ac_cv_prog_CC="$CC" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_CC="${ac_tool_prefix}cc" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -CC=$ac_cv_prog_CC -if test -n "$CC"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 -$as_echo "$CC" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - - fi -fi -if test -z "$CC"; then - # Extract the first word of "cc", so it can be a program name with args. -set dummy cc; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_CC+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -n "$CC"; then - ac_cv_prog_CC="$CC" # Let the user override the test. -else - ac_prog_rejected=no -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then - ac_prog_rejected=yes - continue - fi - ac_cv_prog_CC="cc" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -if test $ac_prog_rejected = yes; then - # We found a bogon in the path, so make sure we never use it. - set dummy $ac_cv_prog_CC - shift - if test $# != 0; then - # We chose a different compiler from the bogus one. - # However, it has the same basename, so the bogon will be chosen - # first if we set CC to just the basename; use the full file name. - shift - ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@" - fi -fi -fi -fi -CC=$ac_cv_prog_CC -if test -n "$CC"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 -$as_echo "$CC" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - -fi -if test -z "$CC"; then - if test -n "$ac_tool_prefix"; then - for ac_prog in cl.exe - do - # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. -set dummy $ac_tool_prefix$ac_prog; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_CC+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -n "$CC"; then - ac_cv_prog_CC="$CC" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_CC="$ac_tool_prefix$ac_prog" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -CC=$ac_cv_prog_CC -if test -n "$CC"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 -$as_echo "$CC" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - - test -n "$CC" && break - done -fi -if test -z "$CC"; then - ac_ct_CC=$CC - for ac_prog in cl.exe -do - # Extract the first word of "$ac_prog", so it can be a program name with args. -set dummy $ac_prog; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_ac_ct_CC+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -n "$ac_ct_CC"; then - ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_ac_ct_CC="$ac_prog" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -ac_ct_CC=$ac_cv_prog_ac_ct_CC -if test -n "$ac_ct_CC"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 -$as_echo "$ac_ct_CC" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - - test -n "$ac_ct_CC" && break -done - - if test "x$ac_ct_CC" = x; then - CC="" - else - case $cross_compiling:$ac_tool_warned in -yes:) -{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 -$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} -ac_tool_warned=yes ;; -esac - CC=$ac_ct_CC - fi -fi - -fi - - -test -z "$CC" && { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error $? "no acceptable C compiler found in \$PATH -See \`config.log' for more details" "$LINENO" 5; } - -# Provide some information about the compiler. -$as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5 -set X $ac_compile -ac_compiler=$2 -for ac_option in --version -v -V -qversion; do - { { ac_try="$ac_compiler $ac_option >&5" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" -$as_echo "$ac_try_echo"; } >&5 - (eval "$ac_compiler $ac_option >&5") 2>conftest.err - ac_status=$? - if test -s conftest.err; then - sed '10a\ -... rest of stderr output deleted ... - 10q' conftest.err >conftest.er1 - cat conftest.er1 >&5 - fi - rm -f conftest.er1 conftest.err - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; } -done - -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C compiler" >&5 -$as_echo_n "checking whether we are using the GNU C compiler... " >&6; } -if ${ac_cv_c_compiler_gnu+:} false; then : - $as_echo_n "(cached) " >&6 -else - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main () -{ -#ifndef __GNUC__ - choke me -#endif - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - ac_compiler_gnu=yes -else - ac_compiler_gnu=no -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -ac_cv_c_compiler_gnu=$ac_compiler_gnu - -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5 -$as_echo "$ac_cv_c_compiler_gnu" >&6; } -if test $ac_compiler_gnu = yes; then - GCC=yes -else - GCC= -fi -ac_test_CFLAGS=${CFLAGS+set} -ac_save_CFLAGS=$CFLAGS -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5 -$as_echo_n "checking whether $CC accepts -g... " >&6; } -if ${ac_cv_prog_cc_g+:} false; then : - $as_echo_n "(cached) " >&6 -else - ac_save_c_werror_flag=$ac_c_werror_flag - ac_c_werror_flag=yes - ac_cv_prog_cc_g=no - CFLAGS="-g" - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main () -{ - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - ac_cv_prog_cc_g=yes -else - CFLAGS="" - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main () -{ - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - -else - ac_c_werror_flag=$ac_save_c_werror_flag - CFLAGS="-g" - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main () -{ - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - ac_cv_prog_cc_g=yes -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext - ac_c_werror_flag=$ac_save_c_werror_flag -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5 -$as_echo "$ac_cv_prog_cc_g" >&6; } -if test "$ac_test_CFLAGS" = set; then - CFLAGS=$ac_save_CFLAGS -elif test $ac_cv_prog_cc_g = yes; then - if test "$GCC" = yes; then - CFLAGS="-g -O2" - else - CFLAGS="-g" - fi -else - if test "$GCC" = yes; then - CFLAGS="-O2" - else - CFLAGS= - fi -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C89" >&5 -$as_echo_n "checking for $CC option to accept ISO C89... " >&6; } -if ${ac_cv_prog_cc_c89+:} false; then : - $as_echo_n "(cached) " >&6 -else - ac_cv_prog_cc_c89=no -ac_save_CC=$CC -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#include -#include -struct stat; -/* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */ -struct buf { int x; }; -FILE * (*rcsopen) (struct buf *, struct stat *, int); -static char *e (p, i) - char **p; - int i; -{ - return p[i]; -} -static char *f (char * (*g) (char **, int), char **p, ...) -{ - char *s; - va_list v; - va_start (v,p); - s = g (p, va_arg (v,int)); - va_end (v); - return s; -} - -/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has - function prototypes and stuff, but not '\xHH' hex character constants. - These don't provoke an error unfortunately, instead are silently treated - as 'x'. The following induces an error, until -std is added to get - proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an - array size at least. It's necessary to write '\x00'==0 to get something - that's true only with -std. */ -int osf4_cc_array ['\x00' == 0 ? 1 : -1]; - -/* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters - inside strings and character constants. */ -#define FOO(x) 'x' -int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1]; - -int test (int i, double x); -struct s1 {int (*f) (int a);}; -struct s2 {int (*f) (double a);}; -int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int); -int argc; -char **argv; -int -main () -{ -return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]; - ; - return 0; -} -_ACEOF -for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \ - -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" -do - CC="$ac_save_CC $ac_arg" - if ac_fn_c_try_compile "$LINENO"; then : - ac_cv_prog_cc_c89=$ac_arg -fi -rm -f core conftest.err conftest.$ac_objext - test "x$ac_cv_prog_cc_c89" != "xno" && break -done -rm -f conftest.$ac_ext -CC=$ac_save_CC - -fi -# AC_CACHE_VAL -case "x$ac_cv_prog_cc_c89" in - x) - { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 -$as_echo "none needed" >&6; } ;; - xno) - { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 -$as_echo "unsupported" >&6; } ;; - *) - CC="$CC $ac_cv_prog_cc_c89" - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5 -$as_echo "$ac_cv_prog_cc_c89" >&6; } ;; -esac -if test "x$ac_cv_prog_cc_c89" != xno; then : - -fi - -ac_ext=c -ac_cpp='$CPP $CPPFLAGS' -ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_c_compiler_gnu - -ac_ext=c -ac_cpp='$CPP $CPPFLAGS' -ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_c_compiler_gnu -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC understands -c and -o together" >&5 -$as_echo_n "checking whether $CC understands -c and -o together... " >&6; } -if ${am_cv_prog_cc_c_o+:} false; then : - $as_echo_n "(cached) " >&6 -else - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main () -{ - - ; - return 0; -} -_ACEOF - # Make sure it works both with $CC and with simple cc. - # Following AC_PROG_CC_C_O, we do the test twice because some - # compilers refuse to overwrite an existing .o file with -o, - # though they will create one. - am_cv_prog_cc_c_o=yes - for am_i in 1 2; do - if { echo "$as_me:$LINENO: $CC -c conftest.$ac_ext -o conftest2.$ac_objext" >&5 - ($CC -c conftest.$ac_ext -o conftest2.$ac_objext) >&5 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } \ - && test -f conftest2.$ac_objext; then - : OK - else - am_cv_prog_cc_c_o=no - break - fi - done - rm -f core conftest* - unset am_i -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_prog_cc_c_o" >&5 -$as_echo "$am_cv_prog_cc_c_o" >&6; } -if test "$am_cv_prog_cc_c_o" != yes; then - # Losing compiler, so override with the script. - # FIXME: It is wrong to rewrite CC. - # But if we don't then we get into trouble of one sort or another. - # A longer-term fix would be to have automake use am__CC in this case, - # and then we could set am__CC="\$(top_srcdir)/compile \$(CC)" - CC="$am_aux_dir/compile $CC" -fi -ac_ext=c -ac_cpp='$CPP $CPPFLAGS' -ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_c_compiler_gnu - - -ac_ext=cpp -ac_cpp='$CXXCPP $CPPFLAGS' -ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_cxx_compiler_gnu -if test -z "$CXX"; then - if test -n "$CCC"; then - CXX=$CCC - else - if test -n "$ac_tool_prefix"; then - for ac_prog in g++ c++ gpp aCC CC cxx cc++ cl.exe FCC KCC RCC xlC_r xlC - do - # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. -set dummy $ac_tool_prefix$ac_prog; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_CXX+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -n "$CXX"; then - ac_cv_prog_CXX="$CXX" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_CXX="$ac_tool_prefix$ac_prog" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -CXX=$ac_cv_prog_CXX -if test -n "$CXX"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CXX" >&5 -$as_echo "$CXX" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - - test -n "$CXX" && break - done -fi -if test -z "$CXX"; then - ac_ct_CXX=$CXX - for ac_prog in g++ c++ gpp aCC CC cxx cc++ cl.exe FCC KCC RCC xlC_r xlC -do - # Extract the first word of "$ac_prog", so it can be a program name with args. -set dummy $ac_prog; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_ac_ct_CXX+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -n "$ac_ct_CXX"; then - ac_cv_prog_ac_ct_CXX="$ac_ct_CXX" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_ac_ct_CXX="$ac_prog" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -ac_ct_CXX=$ac_cv_prog_ac_ct_CXX -if test -n "$ac_ct_CXX"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CXX" >&5 -$as_echo "$ac_ct_CXX" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - - test -n "$ac_ct_CXX" && break -done - - if test "x$ac_ct_CXX" = x; then - CXX="g++" - else - case $cross_compiling:$ac_tool_warned in -yes:) -{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 -$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} -ac_tool_warned=yes ;; -esac - CXX=$ac_ct_CXX - fi -fi - - fi -fi -# Provide some information about the compiler. -$as_echo "$as_me:${as_lineno-$LINENO}: checking for C++ compiler version" >&5 -set X $ac_compile -ac_compiler=$2 -for ac_option in --version -v -V -qversion; do - { { ac_try="$ac_compiler $ac_option >&5" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" -$as_echo "$ac_try_echo"; } >&5 - (eval "$ac_compiler $ac_option >&5") 2>conftest.err - ac_status=$? - if test -s conftest.err; then - sed '10a\ -... rest of stderr output deleted ... - 10q' conftest.err >conftest.er1 - cat conftest.er1 >&5 - fi - rm -f conftest.er1 conftest.err - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; } -done - -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C++ compiler" >&5 -$as_echo_n "checking whether we are using the GNU C++ compiler... " >&6; } -if ${ac_cv_cxx_compiler_gnu+:} false; then : - $as_echo_n "(cached) " >&6 -else - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main () -{ -#ifndef __GNUC__ - choke me -#endif - - ; - return 0; -} -_ACEOF -if ac_fn_cxx_try_compile "$LINENO"; then : - ac_compiler_gnu=yes -else - ac_compiler_gnu=no -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -ac_cv_cxx_compiler_gnu=$ac_compiler_gnu - -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cxx_compiler_gnu" >&5 -$as_echo "$ac_cv_cxx_compiler_gnu" >&6; } -if test $ac_compiler_gnu = yes; then - GXX=yes -else - GXX= -fi -ac_test_CXXFLAGS=${CXXFLAGS+set} -ac_save_CXXFLAGS=$CXXFLAGS -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CXX accepts -g" >&5 -$as_echo_n "checking whether $CXX accepts -g... " >&6; } -if ${ac_cv_prog_cxx_g+:} false; then : - $as_echo_n "(cached) " >&6 -else - ac_save_cxx_werror_flag=$ac_cxx_werror_flag - ac_cxx_werror_flag=yes - ac_cv_prog_cxx_g=no - CXXFLAGS="-g" - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main () -{ - - ; - return 0; -} -_ACEOF -if ac_fn_cxx_try_compile "$LINENO"; then : - ac_cv_prog_cxx_g=yes -else - CXXFLAGS="" - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main () -{ - - ; - return 0; -} -_ACEOF -if ac_fn_cxx_try_compile "$LINENO"; then : - -else - ac_cxx_werror_flag=$ac_save_cxx_werror_flag - CXXFLAGS="-g" - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main () -{ - - ; - return 0; -} -_ACEOF -if ac_fn_cxx_try_compile "$LINENO"; then : - ac_cv_prog_cxx_g=yes -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext - ac_cxx_werror_flag=$ac_save_cxx_werror_flag -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cxx_g" >&5 -$as_echo "$ac_cv_prog_cxx_g" >&6; } -if test "$ac_test_CXXFLAGS" = set; then - CXXFLAGS=$ac_save_CXXFLAGS -elif test $ac_cv_prog_cxx_g = yes; then - if test "$GXX" = yes; then - CXXFLAGS="-g -O2" - else - CXXFLAGS="-g" - fi -else - if test "$GXX" = yes; then - CXXFLAGS="-O2" - else - CXXFLAGS= - fi -fi -ac_ext=c -ac_cpp='$CPP $CPPFLAGS' -ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_c_compiler_gnu - - - -func_stripname_cnf () -{ - case ${2} in - .*) func_stripname_result=`$ECHO "${3}" | $SED "s%^${1}%%; s%\\\\${2}\$%%"`;; - *) func_stripname_result=`$ECHO "${3}" | $SED "s%^${1}%%; s%${2}\$%%"`;; - esac -} # func_stripname_cnf - - if test -n "$CXX" && ( test "X$CXX" != "Xno" && - ( (test "X$CXX" = "Xg++" && `g++ -v >/dev/null 2>&1` ) || - (test "X$CXX" != "Xg++"))) ; then - ac_ext=cpp -ac_cpp='$CXXCPP $CPPFLAGS' -ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_cxx_compiler_gnu -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to run the C++ preprocessor" >&5 -$as_echo_n "checking how to run the C++ preprocessor... " >&6; } -if test -z "$CXXCPP"; then - if ${ac_cv_prog_CXXCPP+:} false; then : - $as_echo_n "(cached) " >&6 -else - # Double quotes because CXXCPP needs to be expanded - for CXXCPP in "$CXX -E" "/lib/cpp" - do - ac_preproc_ok=false -for ac_cxx_preproc_warn_flag in '' yes -do - # Use a header file that comes with gcc, so configuring glibc - # with a fresh cross-compiler works. - # Prefer to if __STDC__ is defined, since - # exists even on freestanding compilers. - # On the NeXT, cc -E runs the code through the compiler's parser, - # not just through cpp. "Syntax error" is here to catch this case. - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#ifdef __STDC__ -# include -#else -# include -#endif - Syntax error -_ACEOF -if ac_fn_cxx_try_cpp "$LINENO"; then : - -else - # Broken: fails on valid input. -continue -fi -rm -f conftest.err conftest.i conftest.$ac_ext - - # OK, works on sane cases. Now check whether nonexistent headers - # can be detected and how. - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#include -_ACEOF -if ac_fn_cxx_try_cpp "$LINENO"; then : - # Broken: success on invalid input. -continue -else - # Passes both tests. -ac_preproc_ok=: -break -fi -rm -f conftest.err conftest.i conftest.$ac_ext - -done -# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. -rm -f conftest.i conftest.err conftest.$ac_ext -if $ac_preproc_ok; then : - break -fi - - done - ac_cv_prog_CXXCPP=$CXXCPP - -fi - CXXCPP=$ac_cv_prog_CXXCPP -else - ac_cv_prog_CXXCPP=$CXXCPP -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $CXXCPP" >&5 -$as_echo "$CXXCPP" >&6; } -ac_preproc_ok=false -for ac_cxx_preproc_warn_flag in '' yes -do - # Use a header file that comes with gcc, so configuring glibc - # with a fresh cross-compiler works. - # Prefer to if __STDC__ is defined, since - # exists even on freestanding compilers. - # On the NeXT, cc -E runs the code through the compiler's parser, - # not just through cpp. "Syntax error" is here to catch this case. - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#ifdef __STDC__ -# include -#else -# include -#endif - Syntax error -_ACEOF -if ac_fn_cxx_try_cpp "$LINENO"; then : - -else - # Broken: fails on valid input. -continue -fi -rm -f conftest.err conftest.i conftest.$ac_ext - - # OK, works on sane cases. Now check whether nonexistent headers - # can be detected and how. - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#include -_ACEOF -if ac_fn_cxx_try_cpp "$LINENO"; then : - # Broken: success on invalid input. -continue -else - # Passes both tests. -ac_preproc_ok=: -break -fi -rm -f conftest.err conftest.i conftest.$ac_ext - -done -# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. -rm -f conftest.i conftest.err conftest.$ac_ext -if $ac_preproc_ok; then : - -else - { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error $? "C++ preprocessor \"$CXXCPP\" fails sanity check -See \`config.log' for more details" "$LINENO" 5; } -fi - -ac_ext=c -ac_cpp='$CPP $CPPFLAGS' -ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_c_compiler_gnu - -else - _lt_caught_CXX_error=yes -fi - -ac_ext=cpp -ac_cpp='$CXXCPP $CPPFLAGS' -ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_cxx_compiler_gnu - -archive_cmds_need_lc_CXX=no -allow_undefined_flag_CXX= -always_export_symbols_CXX=no -archive_expsym_cmds_CXX= -compiler_needs_object_CXX=no -export_dynamic_flag_spec_CXX= -hardcode_direct_CXX=no -hardcode_direct_absolute_CXX=no -hardcode_libdir_flag_spec_CXX= -hardcode_libdir_separator_CXX= -hardcode_minus_L_CXX=no -hardcode_shlibpath_var_CXX=unsupported -hardcode_automatic_CXX=no -inherit_rpath_CXX=no -module_cmds_CXX= -module_expsym_cmds_CXX= -link_all_deplibs_CXX=unknown -old_archive_cmds_CXX=$old_archive_cmds -reload_flag_CXX=$reload_flag -reload_cmds_CXX=$reload_cmds -no_undefined_flag_CXX= -whole_archive_flag_spec_CXX= -enable_shared_with_static_runtimes_CXX=no - -# Source file extension for C++ test sources. -ac_ext=cpp - -# Object file extension for compiled C++ test sources. -objext=o -objext_CXX=$objext - -# No sense in running all these tests if we already determined that -# the CXX compiler isn't working. Some variables (like enable_shared) -# are currently assumed to apply to all compilers on this platform, -# and will be corrupted by setting them based on a non-working compiler. -if test "$_lt_caught_CXX_error" != yes; then - # Code to be used in simple compile tests - lt_simple_compile_test_code="int some_variable = 0;" - - # Code to be used in simple link tests - lt_simple_link_test_code='int main(int, char *[]) { return(0); }' - - # ltmain only uses $CC for tagged configurations so make sure $CC is set. - - - - - - -# If no C compiler was specified, use CC. -LTCC=${LTCC-"$CC"} - -# If no C compiler flags were specified, use CFLAGS. -LTCFLAGS=${LTCFLAGS-"$CFLAGS"} - -# Allow CC to be a program name with arguments. -compiler=$CC - - - # save warnings/boilerplate of simple test code - ac_outfile=conftest.$ac_objext -echo "$lt_simple_compile_test_code" >conftest.$ac_ext -eval "$ac_compile" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err -_lt_compiler_boilerplate=`cat conftest.err` -$RM conftest* - - ac_outfile=conftest.$ac_objext -echo "$lt_simple_link_test_code" >conftest.$ac_ext -eval "$ac_link" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err -_lt_linker_boilerplate=`cat conftest.err` -$RM -r conftest* - - - # Allow CC to be a program name with arguments. - lt_save_CC=$CC - lt_save_CFLAGS=$CFLAGS - lt_save_LD=$LD - lt_save_GCC=$GCC - GCC=$GXX - lt_save_with_gnu_ld=$with_gnu_ld - lt_save_path_LD=$lt_cv_path_LD - if test -n "${lt_cv_prog_gnu_ldcxx+set}"; then - lt_cv_prog_gnu_ld=$lt_cv_prog_gnu_ldcxx - else - $as_unset lt_cv_prog_gnu_ld - fi - if test -n "${lt_cv_path_LDCXX+set}"; then - lt_cv_path_LD=$lt_cv_path_LDCXX - else - $as_unset lt_cv_path_LD - fi - test -z "${LDCXX+set}" || LD=$LDCXX - CC=${CXX-"c++"} - CFLAGS=$CXXFLAGS - compiler=$CC - compiler_CXX=$CC - for cc_temp in $compiler""; do - case $cc_temp in - compile | *[\\/]compile | ccache | *[\\/]ccache ) ;; - distcc | *[\\/]distcc | purify | *[\\/]purify ) ;; - \-*) ;; - *) break;; - esac -done -cc_basename=`$ECHO "$cc_temp" | $SED "s%.*/%%; s%^$host_alias-%%"` - - - if test -n "$compiler"; then - # We don't want -fno-exception when compiling C++ code, so set the - # no_builtin_flag separately - if test "$GXX" = yes; then - lt_prog_compiler_no_builtin_flag_CXX=' -fno-builtin' - else - lt_prog_compiler_no_builtin_flag_CXX= - fi - - if test "$GXX" = yes; then - # Set up default GNU C++ configuration - - - -# Check whether --with-gnu-ld was given. -if test "${with_gnu_ld+set}" = set; then : - withval=$with_gnu_ld; test "$withval" = no || with_gnu_ld=yes -else - with_gnu_ld=no -fi - -ac_prog=ld -if test "$GCC" = yes; then - # Check if gcc -print-prog-name=ld gives a path. - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ld used by $CC" >&5 -$as_echo_n "checking for ld used by $CC... " >&6; } - case $host in - *-*-mingw*) - # gcc leaves a trailing carriage return which upsets mingw - ac_prog=`($CC -print-prog-name=ld) 2>&5 | tr -d '\015'` ;; - *) - ac_prog=`($CC -print-prog-name=ld) 2>&5` ;; - esac - case $ac_prog in - # Accept absolute paths. - [\\/]* | ?:[\\/]*) - re_direlt='/[^/][^/]*/\.\./' - # Canonicalize the pathname of ld - ac_prog=`$ECHO "$ac_prog"| $SED 's%\\\\%/%g'` - while $ECHO "$ac_prog" | $GREP "$re_direlt" > /dev/null 2>&1; do - ac_prog=`$ECHO $ac_prog| $SED "s%$re_direlt%/%"` - done - test -z "$LD" && LD="$ac_prog" - ;; - "") - # If it fails, then pretend we aren't using GCC. - ac_prog=ld - ;; - *) - # If it is relative, then search for the first ld in PATH. - with_gnu_ld=unknown - ;; - esac -elif test "$with_gnu_ld" = yes; then - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for GNU ld" >&5 -$as_echo_n "checking for GNU ld... " >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for non-GNU ld" >&5 -$as_echo_n "checking for non-GNU ld... " >&6; } -fi -if ${lt_cv_path_LD+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -z "$LD"; then - lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR - for ac_dir in $PATH; do - IFS="$lt_save_ifs" - test -z "$ac_dir" && ac_dir=. - if test -f "$ac_dir/$ac_prog" || test -f "$ac_dir/$ac_prog$ac_exeext"; then - lt_cv_path_LD="$ac_dir/$ac_prog" - # Check to see if the program is GNU ld. I'd rather use --version, - # but apparently some variants of GNU ld only accept -v. - # Break only if it was the GNU/non-GNU ld that we prefer. - case `"$lt_cv_path_LD" -v 2>&1 &5 -$as_echo "$LD" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi -test -z "$LD" && as_fn_error $? "no acceptable ld found in \$PATH" "$LINENO" 5 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if the linker ($LD) is GNU ld" >&5 -$as_echo_n "checking if the linker ($LD) is GNU ld... " >&6; } -if ${lt_cv_prog_gnu_ld+:} false; then : - $as_echo_n "(cached) " >&6 -else - # I'd rather use --version here, but apparently some GNU lds only accept -v. -case `$LD -v 2>&1 &5 -$as_echo "$lt_cv_prog_gnu_ld" >&6; } -with_gnu_ld=$lt_cv_prog_gnu_ld - - - - - - - - # Check if GNU C++ uses GNU ld as the underlying linker, since the - # archiving commands below assume that GNU ld is being used. - if test "$with_gnu_ld" = yes; then - archive_cmds_CXX='$CC $pic_flag -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib' - archive_expsym_cmds_CXX='$CC $pic_flag -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' - - hardcode_libdir_flag_spec_CXX='${wl}-rpath ${wl}$libdir' - export_dynamic_flag_spec_CXX='${wl}--export-dynamic' - - # If archive_cmds runs LD, not CC, wlarc should be empty - # XXX I think wlarc can be eliminated in ltcf-cxx, but I need to - # investigate it a little bit more. (MM) - wlarc='${wl}' - - # ancient GNU ld didn't support --whole-archive et. al. - if eval "`$CC -print-prog-name=ld` --help 2>&1" | - $GREP 'no-whole-archive' > /dev/null; then - whole_archive_flag_spec_CXX="$wlarc"'--whole-archive$convenience '"$wlarc"'--no-whole-archive' - else - whole_archive_flag_spec_CXX= - fi - else - with_gnu_ld=no - wlarc= - - # A generic and very simple default shared library creation - # command for GNU C++ for the case where it uses the native - # linker, instead of GNU ld. If possible, this setting should - # overridden to take advantage of the native linker features on - # the platform it is being used on. - archive_cmds_CXX='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $lib' - fi - - # Commands to make compiler produce verbose output that lists - # what "hidden" libraries, object files and flags are used when - # linking a shared library. - output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"' - - else - GXX=no - with_gnu_ld=no - wlarc= - fi - - # PORTME: fill in a description of your system's C++ link characteristics - { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the $compiler linker ($LD) supports shared libraries" >&5 -$as_echo_n "checking whether the $compiler linker ($LD) supports shared libraries... " >&6; } - ld_shlibs_CXX=yes - case $host_os in - aix3*) - # FIXME: insert proper C++ library support - ld_shlibs_CXX=no - ;; - aix[4-9]*) - if test "$host_cpu" = ia64; then - # On IA64, the linker does run time linking by default, so we don't - # have to do anything special. - aix_use_runtimelinking=no - exp_sym_flag='-Bexport' - no_entry_flag="" - else - aix_use_runtimelinking=no - - # Test if we are trying to use run time linking or normal - # AIX style linking. If -brtl is somewhere in LDFLAGS, we - # need to do runtime linking. - case $host_os in aix4.[23]|aix4.[23].*|aix[5-9]*) - for ld_flag in $LDFLAGS; do - case $ld_flag in - *-brtl*) - aix_use_runtimelinking=yes - break - ;; - esac - done - ;; - esac - - exp_sym_flag='-bexport' - no_entry_flag='-bnoentry' - fi - - # When large executables or shared objects are built, AIX ld can - # have problems creating the table of contents. If linking a library - # or program results in "error TOC overflow" add -mminimal-toc to - # CXXFLAGS/CFLAGS for g++/gcc. In the cases where that is not - # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS. - - archive_cmds_CXX='' - hardcode_direct_CXX=yes - hardcode_direct_absolute_CXX=yes - hardcode_libdir_separator_CXX=':' - link_all_deplibs_CXX=yes - file_list_spec_CXX='${wl}-f,' - - if test "$GXX" = yes; then - case $host_os in aix4.[012]|aix4.[012].*) - # We only want to do this on AIX 4.2 and lower, the check - # below for broken collect2 doesn't work under 4.3+ - collect2name=`${CC} -print-prog-name=collect2` - if test -f "$collect2name" && - strings "$collect2name" | $GREP resolve_lib_name >/dev/null - then - # We have reworked collect2 - : - else - # We have old collect2 - hardcode_direct_CXX=unsupported - # It fails to find uninstalled libraries when the uninstalled - # path is not listed in the libpath. Setting hardcode_minus_L - # to unsupported forces relinking - hardcode_minus_L_CXX=yes - hardcode_libdir_flag_spec_CXX='-L$libdir' - hardcode_libdir_separator_CXX= - fi - esac - shared_flag='-shared' - if test "$aix_use_runtimelinking" = yes; then - shared_flag="$shared_flag "'${wl}-G' - fi - else - # not using gcc - if test "$host_cpu" = ia64; then - # VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release - # chokes on -Wl,-G. The following line is correct: - shared_flag='-G' - else - if test "$aix_use_runtimelinking" = yes; then - shared_flag='${wl}-G' - else - shared_flag='${wl}-bM:SRE' - fi - fi - fi - - export_dynamic_flag_spec_CXX='${wl}-bexpall' - # It seems that -bexpall does not export symbols beginning with - # underscore (_), so it is better to generate a list of symbols to - # export. - always_export_symbols_CXX=yes - if test "$aix_use_runtimelinking" = yes; then - # Warning - without using the other runtime loading flags (-brtl), - # -berok will link without error, but may produce a broken library. - allow_undefined_flag_CXX='-berok' - # Determine the default libpath from the value encoded in an empty - # executable. - if test "${lt_cv_aix_libpath+set}" = set; then - aix_libpath=$lt_cv_aix_libpath -else - if ${lt_cv_aix_libpath__CXX+:} false; then : - $as_echo_n "(cached) " >&6 -else - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main () -{ - - ; - return 0; -} -_ACEOF -if ac_fn_cxx_try_link "$LINENO"; then : - - lt_aix_libpath_sed=' - /Import File Strings/,/^$/ { - /^0/ { - s/^0 *\([^ ]*\) *$/\1/ - p - } - }' - lt_cv_aix_libpath__CXX=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` - # Check for a 64-bit object if we didn't find anything. - if test -z "$lt_cv_aix_libpath__CXX"; then - lt_cv_aix_libpath__CXX=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` - fi -fi -rm -f core conftest.err conftest.$ac_objext \ - conftest$ac_exeext conftest.$ac_ext - if test -z "$lt_cv_aix_libpath__CXX"; then - lt_cv_aix_libpath__CXX="/usr/lib:/lib" - fi - -fi - - aix_libpath=$lt_cv_aix_libpath__CXX -fi - - hardcode_libdir_flag_spec_CXX='${wl}-blibpath:$libdir:'"$aix_libpath" - - archive_expsym_cmds_CXX='$CC -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags `if test "x${allow_undefined_flag}" != "x"; then func_echo_all "${wl}${allow_undefined_flag}"; else :; fi` '"\${wl}$exp_sym_flag:\$export_symbols $shared_flag" - else - if test "$host_cpu" = ia64; then - hardcode_libdir_flag_spec_CXX='${wl}-R $libdir:/usr/lib:/lib' - allow_undefined_flag_CXX="-z nodefs" - archive_expsym_cmds_CXX="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags ${wl}${allow_undefined_flag} '"\${wl}$exp_sym_flag:\$export_symbols" - else - # Determine the default libpath from the value encoded in an - # empty executable. - if test "${lt_cv_aix_libpath+set}" = set; then - aix_libpath=$lt_cv_aix_libpath -else - if ${lt_cv_aix_libpath__CXX+:} false; then : - $as_echo_n "(cached) " >&6 -else - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main () -{ - - ; - return 0; -} -_ACEOF -if ac_fn_cxx_try_link "$LINENO"; then : - - lt_aix_libpath_sed=' - /Import File Strings/,/^$/ { - /^0/ { - s/^0 *\([^ ]*\) *$/\1/ - p - } - }' - lt_cv_aix_libpath__CXX=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` - # Check for a 64-bit object if we didn't find anything. - if test -z "$lt_cv_aix_libpath__CXX"; then - lt_cv_aix_libpath__CXX=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` - fi -fi -rm -f core conftest.err conftest.$ac_objext \ - conftest$ac_exeext conftest.$ac_ext - if test -z "$lt_cv_aix_libpath__CXX"; then - lt_cv_aix_libpath__CXX="/usr/lib:/lib" - fi - -fi - - aix_libpath=$lt_cv_aix_libpath__CXX -fi - - hardcode_libdir_flag_spec_CXX='${wl}-blibpath:$libdir:'"$aix_libpath" - # Warning - without using the other run time loading flags, - # -berok will link without error, but may produce a broken library. - no_undefined_flag_CXX=' ${wl}-bernotok' - allow_undefined_flag_CXX=' ${wl}-berok' - if test "$with_gnu_ld" = yes; then - # We only use this code for GNU lds that support --whole-archive. - whole_archive_flag_spec_CXX='${wl}--whole-archive$convenience ${wl}--no-whole-archive' - else - # Exported symbols can be pulled into shared objects from archives - whole_archive_flag_spec_CXX='$convenience' - fi - archive_cmds_need_lc_CXX=yes - # This is similar to how AIX traditionally builds its shared - # libraries. - archive_expsym_cmds_CXX="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs ${wl}-bnoentry $compiler_flags ${wl}-bE:$export_symbols${allow_undefined_flag}~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$soname' - fi - fi - ;; - - beos*) - if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then - allow_undefined_flag_CXX=unsupported - # Joseph Beckenbach says some releases of gcc - # support --undefined. This deserves some investigation. FIXME - archive_cmds_CXX='$CC -nostart $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' - else - ld_shlibs_CXX=no - fi - ;; - - chorus*) - case $cc_basename in - *) - # FIXME: insert proper C++ library support - ld_shlibs_CXX=no - ;; - esac - ;; - - cygwin* | mingw* | pw32* | cegcc*) - case $GXX,$cc_basename in - ,cl* | no,cl*) - # Native MSVC - # hardcode_libdir_flag_spec is actually meaningless, as there is - # no search path for DLLs. - hardcode_libdir_flag_spec_CXX=' ' - allow_undefined_flag_CXX=unsupported - always_export_symbols_CXX=yes - file_list_spec_CXX='@' - # Tell ltmain to make .lib files, not .a files. - libext=lib - # Tell ltmain to make .dll files, not .so files. - shrext_cmds=".dll" - # FIXME: Setting linknames here is a bad hack. - archive_cmds_CXX='$CC -o $output_objdir/$soname $libobjs $compiler_flags $deplibs -Wl,-dll~linknames=' - archive_expsym_cmds_CXX='if test "x`$SED 1q $export_symbols`" = xEXPORTS; then - $SED -n -e 's/\\\\\\\(.*\\\\\\\)/-link\\\ -EXPORT:\\\\\\\1/' -e '1\\\!p' < $export_symbols > $output_objdir/$soname.exp; - else - $SED -e 's/\\\\\\\(.*\\\\\\\)/-link\\\ -EXPORT:\\\\\\\1/' < $export_symbols > $output_objdir/$soname.exp; - fi~ - $CC -o $tool_output_objdir$soname $libobjs $compiler_flags $deplibs "@$tool_output_objdir$soname.exp" -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~ - linknames=' - # The linker will not automatically build a static lib if we build a DLL. - # _LT_TAGVAR(old_archive_from_new_cmds, CXX)='true' - enable_shared_with_static_runtimes_CXX=yes - # Don't use ranlib - old_postinstall_cmds_CXX='chmod 644 $oldlib' - postlink_cmds_CXX='lt_outputfile="@OUTPUT@"~ - lt_tool_outputfile="@TOOL_OUTPUT@"~ - case $lt_outputfile in - *.exe|*.EXE) ;; - *) - lt_outputfile="$lt_outputfile.exe" - lt_tool_outputfile="$lt_tool_outputfile.exe" - ;; - esac~ - func_to_tool_file "$lt_outputfile"~ - if test "$MANIFEST_TOOL" != ":" && test -f "$lt_outputfile.manifest"; then - $MANIFEST_TOOL -manifest "$lt_tool_outputfile.manifest" -outputresource:"$lt_tool_outputfile" || exit 1; - $RM "$lt_outputfile.manifest"; - fi' - ;; - *) - # g++ - # _LT_TAGVAR(hardcode_libdir_flag_spec, CXX) is actually meaningless, - # as there is no search path for DLLs. - hardcode_libdir_flag_spec_CXX='-L$libdir' - export_dynamic_flag_spec_CXX='${wl}--export-all-symbols' - allow_undefined_flag_CXX=unsupported - always_export_symbols_CXX=no - enable_shared_with_static_runtimes_CXX=yes - - if $LD --help 2>&1 | $GREP 'auto-import' > /dev/null; then - archive_cmds_CXX='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' - # If the export-symbols file already is a .def file (1st line - # is EXPORTS), use it as is; otherwise, prepend... - archive_expsym_cmds_CXX='if test "x`$SED 1q $export_symbols`" = xEXPORTS; then - cp $export_symbols $output_objdir/$soname.def; - else - echo EXPORTS > $output_objdir/$soname.def; - cat $export_symbols >> $output_objdir/$soname.def; - fi~ - $CC -shared -nostdlib $output_objdir/$soname.def $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' - else - ld_shlibs_CXX=no - fi - ;; - esac - ;; - darwin* | rhapsody*) - - - archive_cmds_need_lc_CXX=no - hardcode_direct_CXX=no - hardcode_automatic_CXX=yes - hardcode_shlibpath_var_CXX=unsupported - if test "$lt_cv_ld_force_load" = "yes"; then - whole_archive_flag_spec_CXX='`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience ${wl}-force_load,$conv\"; done; func_echo_all \"$new_convenience\"`' - - else - whole_archive_flag_spec_CXX='' - fi - link_all_deplibs_CXX=yes - allow_undefined_flag_CXX="$_lt_dar_allow_undefined" - case $cc_basename in - ifort*) _lt_dar_can_shared=yes ;; - *) _lt_dar_can_shared=$GCC ;; - esac - if test "$_lt_dar_can_shared" = "yes"; then - output_verbose_link_cmd=func_echo_all - archive_cmds_CXX="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod${_lt_dsymutil}" - module_cmds_CXX="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dsymutil}" - archive_expsym_cmds_CXX="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring ${_lt_dar_single_mod}${_lt_dar_export_syms}${_lt_dsymutil}" - module_expsym_cmds_CXX="sed -e 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dar_export_syms}${_lt_dsymutil}" - if test "$lt_cv_apple_cc_single_mod" != "yes"; then - archive_cmds_CXX="\$CC -r -keep_private_externs -nostdlib -o \${lib}-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \${lib}-master.o \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring${_lt_dsymutil}" - archive_expsym_cmds_CXX="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -r -keep_private_externs -nostdlib -o \${lib}-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \${lib}-master.o \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring${_lt_dar_export_syms}${_lt_dsymutil}" - fi - - else - ld_shlibs_CXX=no - fi - - ;; - - dgux*) - case $cc_basename in - ec++*) - # FIXME: insert proper C++ library support - ld_shlibs_CXX=no - ;; - ghcx*) - # Green Hills C++ Compiler - # FIXME: insert proper C++ library support - ld_shlibs_CXX=no - ;; - *) - # FIXME: insert proper C++ library support - ld_shlibs_CXX=no - ;; - esac - ;; - - freebsd2.*) - # C++ shared libraries reported to be fairly broken before - # switch to ELF - ld_shlibs_CXX=no - ;; - - freebsd-elf*) - archive_cmds_need_lc_CXX=no - ;; - - freebsd* | dragonfly*) - # FreeBSD 3 and later use GNU C++ and GNU ld with standard ELF - # conventions - ld_shlibs_CXX=yes - ;; - - haiku*) - archive_cmds_CXX='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' - link_all_deplibs_CXX=yes - ;; - - hpux9*) - hardcode_libdir_flag_spec_CXX='${wl}+b ${wl}$libdir' - hardcode_libdir_separator_CXX=: - export_dynamic_flag_spec_CXX='${wl}-E' - hardcode_direct_CXX=yes - hardcode_minus_L_CXX=yes # Not in the search PATH, - # but as the default - # location of the library. - - case $cc_basename in - CC*) - # FIXME: insert proper C++ library support - ld_shlibs_CXX=no - ;; - aCC*) - archive_cmds_CXX='$RM $output_objdir/$soname~$CC -b ${wl}+b ${wl}$install_libdir -o $output_objdir/$soname $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib' - # Commands to make compiler produce verbose output that lists - # what "hidden" libraries, object files and flags are used when - # linking a shared library. - # - # There doesn't appear to be a way to prevent this compiler from - # explicitly linking system object files so we need to strip them - # from the output so that they don't get included in the library - # dependencies. - output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | $EGREP "\-L"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"' - ;; - *) - if test "$GXX" = yes; then - archive_cmds_CXX='$RM $output_objdir/$soname~$CC -shared -nostdlib $pic_flag ${wl}+b ${wl}$install_libdir -o $output_objdir/$soname $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib' - else - # FIXME: insert proper C++ library support - ld_shlibs_CXX=no - fi - ;; - esac - ;; - - hpux10*|hpux11*) - if test $with_gnu_ld = no; then - hardcode_libdir_flag_spec_CXX='${wl}+b ${wl}$libdir' - hardcode_libdir_separator_CXX=: - - case $host_cpu in - hppa*64*|ia64*) - ;; - *) - export_dynamic_flag_spec_CXX='${wl}-E' - ;; - esac - fi - case $host_cpu in - hppa*64*|ia64*) - hardcode_direct_CXX=no - hardcode_shlibpath_var_CXX=no - ;; - *) - hardcode_direct_CXX=yes - hardcode_direct_absolute_CXX=yes - hardcode_minus_L_CXX=yes # Not in the search PATH, - # but as the default - # location of the library. - ;; - esac - - case $cc_basename in - CC*) - # FIXME: insert proper C++ library support - ld_shlibs_CXX=no - ;; - aCC*) - case $host_cpu in - hppa*64*) - archive_cmds_CXX='$CC -b ${wl}+h ${wl}$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' - ;; - ia64*) - archive_cmds_CXX='$CC -b ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' - ;; - *) - archive_cmds_CXX='$CC -b ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' - ;; - esac - # Commands to make compiler produce verbose output that lists - # what "hidden" libraries, object files and flags are used when - # linking a shared library. - # - # There doesn't appear to be a way to prevent this compiler from - # explicitly linking system object files so we need to strip them - # from the output so that they don't get included in the library - # dependencies. - output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | $GREP "\-L"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"' - ;; - *) - if test "$GXX" = yes; then - if test $with_gnu_ld = no; then - case $host_cpu in - hppa*64*) - archive_cmds_CXX='$CC -shared -nostdlib -fPIC ${wl}+h ${wl}$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' - ;; - ia64*) - archive_cmds_CXX='$CC -shared -nostdlib $pic_flag ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' - ;; - *) - archive_cmds_CXX='$CC -shared -nostdlib $pic_flag ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' - ;; - esac - fi - else - # FIXME: insert proper C++ library support - ld_shlibs_CXX=no - fi - ;; - esac - ;; - - interix[3-9]*) - hardcode_direct_CXX=no - hardcode_shlibpath_var_CXX=no - hardcode_libdir_flag_spec_CXX='${wl}-rpath,$libdir' - export_dynamic_flag_spec_CXX='${wl}-E' - # Hack: On Interix 3.x, we cannot compile PIC because of a broken gcc. - # Instead, shared libraries are loaded at an image base (0x10000000 by - # default) and relocated if they conflict, which is a slow very memory - # consuming and fragmenting process. To avoid this, we pick a random, - # 256 KiB-aligned image base between 0x50000000 and 0x6FFC0000 at link - # time. Moving up from 0x10000000 also allows more sbrk(2) space. - archive_cmds_CXX='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' - archive_expsym_cmds_CXX='sed "s,^,_," $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--retain-symbols-file,$output_objdir/$soname.expsym ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' - ;; - irix5* | irix6*) - case $cc_basename in - CC*) - # SGI C++ - archive_cmds_CXX='$CC -shared -all -multigot $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib' - - # Archives containing C++ object files must be created using - # "CC -ar", where "CC" is the IRIX C++ compiler. This is - # necessary to make sure instantiated templates are included - # in the archive. - old_archive_cmds_CXX='$CC -ar -WR,-u -o $oldlib $oldobjs' - ;; - *) - if test "$GXX" = yes; then - if test "$with_gnu_ld" = no; then - archive_cmds_CXX='$CC -shared $pic_flag -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' - else - archive_cmds_CXX='$CC -shared $pic_flag -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` -o $lib' - fi - fi - link_all_deplibs_CXX=yes - ;; - esac - hardcode_libdir_flag_spec_CXX='${wl}-rpath ${wl}$libdir' - hardcode_libdir_separator_CXX=: - inherit_rpath_CXX=yes - ;; - - linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) - case $cc_basename in - KCC*) - # Kuck and Associates, Inc. (KAI) C++ Compiler - - # KCC will only create a shared library if the output file - # ends with ".so" (or ".sl" for HP-UX), so rename the library - # to its proper name (with version) after linking. - archive_cmds_CXX='tempext=`echo $shared_ext | $SED -e '\''s/\([^()0-9A-Za-z{}]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\${tempext}\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib; mv \$templib $lib' - archive_expsym_cmds_CXX='tempext=`echo $shared_ext | $SED -e '\''s/\([^()0-9A-Za-z{}]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\${tempext}\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib ${wl}-retain-symbols-file,$export_symbols; mv \$templib $lib' - # Commands to make compiler produce verbose output that lists - # what "hidden" libraries, object files and flags are used when - # linking a shared library. - # - # There doesn't appear to be a way to prevent this compiler from - # explicitly linking system object files so we need to strip them - # from the output so that they don't get included in the library - # dependencies. - output_verbose_link_cmd='templist=`$CC $CFLAGS -v conftest.$objext -o libconftest$shared_ext 2>&1 | $GREP "ld"`; rm -f libconftest$shared_ext; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"' - - hardcode_libdir_flag_spec_CXX='${wl}-rpath,$libdir' - export_dynamic_flag_spec_CXX='${wl}--export-dynamic' - - # Archives containing C++ object files must be created using - # "CC -Bstatic", where "CC" is the KAI C++ compiler. - old_archive_cmds_CXX='$CC -Bstatic -o $oldlib $oldobjs' - ;; - icpc* | ecpc* ) - # Intel C++ - with_gnu_ld=yes - # version 8.0 and above of icpc choke on multiply defined symbols - # if we add $predep_objects and $postdep_objects, however 7.1 and - # earlier do not add the objects themselves. - case `$CC -V 2>&1` in - *"Version 7."*) - archive_cmds_CXX='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib' - archive_expsym_cmds_CXX='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' - ;; - *) # Version 8.0 or newer - tmp_idyn= - case $host_cpu in - ia64*) tmp_idyn=' -i_dynamic';; - esac - archive_cmds_CXX='$CC -shared'"$tmp_idyn"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' - archive_expsym_cmds_CXX='$CC -shared'"$tmp_idyn"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' - ;; - esac - archive_cmds_need_lc_CXX=no - hardcode_libdir_flag_spec_CXX='${wl}-rpath,$libdir' - export_dynamic_flag_spec_CXX='${wl}--export-dynamic' - whole_archive_flag_spec_CXX='${wl}--whole-archive$convenience ${wl}--no-whole-archive' - ;; - pgCC* | pgcpp*) - # Portland Group C++ compiler - case `$CC -V` in - *pgCC\ [1-5].* | *pgcpp\ [1-5].*) - prelink_cmds_CXX='tpldir=Template.dir~ - rm -rf $tpldir~ - $CC --prelink_objects --instantiation_dir $tpldir $objs $libobjs $compile_deplibs~ - compile_command="$compile_command `find $tpldir -name \*.o | sort | $NL2SP`"' - old_archive_cmds_CXX='tpldir=Template.dir~ - rm -rf $tpldir~ - $CC --prelink_objects --instantiation_dir $tpldir $oldobjs$old_deplibs~ - $AR $AR_FLAGS $oldlib$oldobjs$old_deplibs `find $tpldir -name \*.o | sort | $NL2SP`~ - $RANLIB $oldlib' - archive_cmds_CXX='tpldir=Template.dir~ - rm -rf $tpldir~ - $CC --prelink_objects --instantiation_dir $tpldir $predep_objects $libobjs $deplibs $convenience $postdep_objects~ - $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | sort | $NL2SP` $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname -o $lib' - archive_expsym_cmds_CXX='tpldir=Template.dir~ - rm -rf $tpldir~ - $CC --prelink_objects --instantiation_dir $tpldir $predep_objects $libobjs $deplibs $convenience $postdep_objects~ - $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | sort | $NL2SP` $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname ${wl}-retain-symbols-file ${wl}$export_symbols -o $lib' - ;; - *) # Version 6 and above use weak symbols - archive_cmds_CXX='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname -o $lib' - archive_expsym_cmds_CXX='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname ${wl}-retain-symbols-file ${wl}$export_symbols -o $lib' - ;; - esac - - hardcode_libdir_flag_spec_CXX='${wl}--rpath ${wl}$libdir' - export_dynamic_flag_spec_CXX='${wl}--export-dynamic' - whole_archive_flag_spec_CXX='${wl}--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` ${wl}--no-whole-archive' - ;; - cxx*) - # Compaq C++ - archive_cmds_CXX='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib' - archive_expsym_cmds_CXX='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib ${wl}-retain-symbols-file $wl$export_symbols' - - runpath_var=LD_RUN_PATH - hardcode_libdir_flag_spec_CXX='-rpath $libdir' - hardcode_libdir_separator_CXX=: - - # Commands to make compiler produce verbose output that lists - # what "hidden" libraries, object files and flags are used when - # linking a shared library. - # - # There doesn't appear to be a way to prevent this compiler from - # explicitly linking system object files so we need to strip them - # from the output so that they don't get included in the library - # dependencies. - output_verbose_link_cmd='templist=`$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP "ld"`; templist=`func_echo_all "$templist" | $SED "s/\(^.*ld.*\)\( .*ld .*$\)/\1/"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "X$list" | $Xsed' - ;; - xl* | mpixl* | bgxl*) - # IBM XL 8.0 on PPC, with GNU ld - hardcode_libdir_flag_spec_CXX='${wl}-rpath ${wl}$libdir' - export_dynamic_flag_spec_CXX='${wl}--export-dynamic' - archive_cmds_CXX='$CC -qmkshrobj $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' - if test "x$supports_anon_versioning" = xyes; then - archive_expsym_cmds_CXX='echo "{ global:" > $output_objdir/$libname.ver~ - cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ - echo "local: *; };" >> $output_objdir/$libname.ver~ - $CC -qmkshrobj $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-version-script ${wl}$output_objdir/$libname.ver -o $lib' - fi - ;; - *) - case `$CC -V 2>&1 | sed 5q` in - *Sun\ C*) - # Sun C++ 5.9 - no_undefined_flag_CXX=' -zdefs' - archive_cmds_CXX='$CC -G${allow_undefined_flag} -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' - archive_expsym_cmds_CXX='$CC -G${allow_undefined_flag} -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-retain-symbols-file ${wl}$export_symbols' - hardcode_libdir_flag_spec_CXX='-R$libdir' - whole_archive_flag_spec_CXX='${wl}--whole-archive`new_convenience=; for conv in $convenience\"\"; do test -z \"$conv\" || new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` ${wl}--no-whole-archive' - compiler_needs_object_CXX=yes - - # Not sure whether something based on - # $CC $CFLAGS -v conftest.$objext -o libconftest$shared_ext 2>&1 - # would be better. - output_verbose_link_cmd='func_echo_all' - - # Archives containing C++ object files must be created using - # "CC -xar", where "CC" is the Sun C++ compiler. This is - # necessary to make sure instantiated templates are included - # in the archive. - old_archive_cmds_CXX='$CC -xar -o $oldlib $oldobjs' - ;; - esac - ;; - esac - ;; - - lynxos*) - # FIXME: insert proper C++ library support - ld_shlibs_CXX=no - ;; - - m88k*) - # FIXME: insert proper C++ library support - ld_shlibs_CXX=no - ;; - - mvs*) - case $cc_basename in - cxx*) - # FIXME: insert proper C++ library support - ld_shlibs_CXX=no - ;; - *) - # FIXME: insert proper C++ library support - ld_shlibs_CXX=no - ;; - esac - ;; - - netbsd*) - if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then - archive_cmds_CXX='$LD -Bshareable -o $lib $predep_objects $libobjs $deplibs $postdep_objects $linker_flags' - wlarc= - hardcode_libdir_flag_spec_CXX='-R$libdir' - hardcode_direct_CXX=yes - hardcode_shlibpath_var_CXX=no - fi - # Workaround some broken pre-1.5 toolchains - output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP conftest.$objext | $SED -e "s:-lgcc -lc -lgcc::"' - ;; - - *nto* | *qnx*) - ld_shlibs_CXX=yes - ;; - - openbsd2*) - # C++ shared libraries are fairly broken - ld_shlibs_CXX=no - ;; - - openbsd*) - if test -f /usr/libexec/ld.so; then - hardcode_direct_CXX=yes - hardcode_shlibpath_var_CXX=no - hardcode_direct_absolute_CXX=yes - archive_cmds_CXX='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $lib' - hardcode_libdir_flag_spec_CXX='${wl}-rpath,$libdir' - if test -z "`echo __ELF__ | $CC -E - | grep __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then - archive_expsym_cmds_CXX='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-retain-symbols-file,$export_symbols -o $lib' - export_dynamic_flag_spec_CXX='${wl}-E' - whole_archive_flag_spec_CXX="$wlarc"'--whole-archive$convenience '"$wlarc"'--no-whole-archive' - fi - output_verbose_link_cmd=func_echo_all - else - ld_shlibs_CXX=no - fi - ;; - - osf3* | osf4* | osf5*) - case $cc_basename in - KCC*) - # Kuck and Associates, Inc. (KAI) C++ Compiler - - # KCC will only create a shared library if the output file - # ends with ".so" (or ".sl" for HP-UX), so rename the library - # to its proper name (with version) after linking. - archive_cmds_CXX='tempext=`echo $shared_ext | $SED -e '\''s/\([^()0-9A-Za-z{}]\)/\\\\\1/g'\''`; templib=`echo "$lib" | $SED -e "s/\${tempext}\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib; mv \$templib $lib' - - hardcode_libdir_flag_spec_CXX='${wl}-rpath,$libdir' - hardcode_libdir_separator_CXX=: - - # Archives containing C++ object files must be created using - # the KAI C++ compiler. - case $host in - osf3*) old_archive_cmds_CXX='$CC -Bstatic -o $oldlib $oldobjs' ;; - *) old_archive_cmds_CXX='$CC -o $oldlib $oldobjs' ;; - esac - ;; - RCC*) - # Rational C++ 2.4.1 - # FIXME: insert proper C++ library support - ld_shlibs_CXX=no - ;; - cxx*) - case $host in - osf3*) - allow_undefined_flag_CXX=' ${wl}-expect_unresolved ${wl}\*' - archive_cmds_CXX='$CC -shared${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $soname `test -n "$verstring" && func_echo_all "${wl}-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib' - hardcode_libdir_flag_spec_CXX='${wl}-rpath ${wl}$libdir' - ;; - *) - allow_undefined_flag_CXX=' -expect_unresolved \*' - archive_cmds_CXX='$CC -shared${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -msym -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib' - archive_expsym_cmds_CXX='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done~ - echo "-hidden">> $lib.exp~ - $CC -shared$allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -msym -soname $soname ${wl}-input ${wl}$lib.exp `test -n "$verstring" && $ECHO "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib~ - $RM $lib.exp' - hardcode_libdir_flag_spec_CXX='-rpath $libdir' - ;; - esac - - hardcode_libdir_separator_CXX=: - - # Commands to make compiler produce verbose output that lists - # what "hidden" libraries, object files and flags are used when - # linking a shared library. - # - # There doesn't appear to be a way to prevent this compiler from - # explicitly linking system object files so we need to strip them - # from the output so that they don't get included in the library - # dependencies. - output_verbose_link_cmd='templist=`$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP "ld" | $GREP -v "ld:"`; templist=`func_echo_all "$templist" | $SED "s/\(^.*ld.*\)\( .*ld.*$\)/\1/"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"' - ;; - *) - if test "$GXX" = yes && test "$with_gnu_ld" = no; then - allow_undefined_flag_CXX=' ${wl}-expect_unresolved ${wl}\*' - case $host in - osf3*) - archive_cmds_CXX='$CC -shared -nostdlib ${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' - ;; - *) - archive_cmds_CXX='$CC -shared $pic_flag -nostdlib ${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-msym ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' - ;; - esac - - hardcode_libdir_flag_spec_CXX='${wl}-rpath ${wl}$libdir' - hardcode_libdir_separator_CXX=: - - # Commands to make compiler produce verbose output that lists - # what "hidden" libraries, object files and flags are used when - # linking a shared library. - output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"' - - else - # FIXME: insert proper C++ library support - ld_shlibs_CXX=no - fi - ;; - esac - ;; - - psos*) - # FIXME: insert proper C++ library support - ld_shlibs_CXX=no - ;; - - sunos4*) - case $cc_basename in - CC*) - # Sun C++ 4.x - # FIXME: insert proper C++ library support - ld_shlibs_CXX=no - ;; - lcc*) - # Lucid - # FIXME: insert proper C++ library support - ld_shlibs_CXX=no - ;; - *) - # FIXME: insert proper C++ library support - ld_shlibs_CXX=no - ;; - esac - ;; - - solaris*) - case $cc_basename in - CC* | sunCC*) - # Sun C++ 4.2, 5.x and Centerline C++ - archive_cmds_need_lc_CXX=yes - no_undefined_flag_CXX=' -zdefs' - archive_cmds_CXX='$CC -G${allow_undefined_flag} -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' - archive_expsym_cmds_CXX='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ - $CC -G${allow_undefined_flag} ${wl}-M ${wl}$lib.exp -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp' - - hardcode_libdir_flag_spec_CXX='-R$libdir' - hardcode_shlibpath_var_CXX=no - case $host_os in - solaris2.[0-5] | solaris2.[0-5].*) ;; - *) - # The compiler driver will combine and reorder linker options, - # but understands `-z linker_flag'. - # Supported since Solaris 2.6 (maybe 2.5.1?) - whole_archive_flag_spec_CXX='-z allextract$convenience -z defaultextract' - ;; - esac - link_all_deplibs_CXX=yes - - output_verbose_link_cmd='func_echo_all' - - # Archives containing C++ object files must be created using - # "CC -xar", where "CC" is the Sun C++ compiler. This is - # necessary to make sure instantiated templates are included - # in the archive. - old_archive_cmds_CXX='$CC -xar -o $oldlib $oldobjs' - ;; - gcx*) - # Green Hills C++ Compiler - archive_cmds_CXX='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-h $wl$soname -o $lib' - - # The C++ compiler must be used to create the archive. - old_archive_cmds_CXX='$CC $LDFLAGS -archive -o $oldlib $oldobjs' - ;; - *) - # GNU C++ compiler with Solaris linker - if test "$GXX" = yes && test "$with_gnu_ld" = no; then - no_undefined_flag_CXX=' ${wl}-z ${wl}defs' - if $CC --version | $GREP -v '^2\.7' > /dev/null; then - archive_cmds_CXX='$CC -shared $pic_flag -nostdlib $LDFLAGS $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-h $wl$soname -o $lib' - archive_expsym_cmds_CXX='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ - $CC -shared $pic_flag -nostdlib ${wl}-M $wl$lib.exp -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp' - - # Commands to make compiler produce verbose output that lists - # what "hidden" libraries, object files and flags are used when - # linking a shared library. - output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"' - else - # g++ 2.7 appears to require `-G' NOT `-shared' on this - # platform. - archive_cmds_CXX='$CC -G -nostdlib $LDFLAGS $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-h $wl$soname -o $lib' - archive_expsym_cmds_CXX='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ - $CC -G -nostdlib ${wl}-M $wl$lib.exp -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp' - - # Commands to make compiler produce verbose output that lists - # what "hidden" libraries, object files and flags are used when - # linking a shared library. - output_verbose_link_cmd='$CC -G $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"' - fi - - hardcode_libdir_flag_spec_CXX='${wl}-R $wl$libdir' - case $host_os in - solaris2.[0-5] | solaris2.[0-5].*) ;; - *) - whole_archive_flag_spec_CXX='${wl}-z ${wl}allextract$convenience ${wl}-z ${wl}defaultextract' - ;; - esac - fi - ;; - esac - ;; - - sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[01].[10]* | unixware7* | sco3.2v5.0.[024]*) - no_undefined_flag_CXX='${wl}-z,text' - archive_cmds_need_lc_CXX=no - hardcode_shlibpath_var_CXX=no - runpath_var='LD_RUN_PATH' - - case $cc_basename in - CC*) - archive_cmds_CXX='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' - archive_expsym_cmds_CXX='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' - ;; - *) - archive_cmds_CXX='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' - archive_expsym_cmds_CXX='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' - ;; - esac - ;; - - sysv5* | sco3.2v5* | sco5v6*) - # Note: We can NOT use -z defs as we might desire, because we do not - # link with -lc, and that would cause any symbols used from libc to - # always be unresolved, which means just about no library would - # ever link correctly. If we're not using GNU ld we use -z text - # though, which does catch some bad symbols but isn't as heavy-handed - # as -z defs. - no_undefined_flag_CXX='${wl}-z,text' - allow_undefined_flag_CXX='${wl}-z,nodefs' - archive_cmds_need_lc_CXX=no - hardcode_shlibpath_var_CXX=no - hardcode_libdir_flag_spec_CXX='${wl}-R,$libdir' - hardcode_libdir_separator_CXX=':' - link_all_deplibs_CXX=yes - export_dynamic_flag_spec_CXX='${wl}-Bexport' - runpath_var='LD_RUN_PATH' - - case $cc_basename in - CC*) - archive_cmds_CXX='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' - archive_expsym_cmds_CXX='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' - old_archive_cmds_CXX='$CC -Tprelink_objects $oldobjs~ - '"$old_archive_cmds_CXX" - reload_cmds_CXX='$CC -Tprelink_objects $reload_objs~ - '"$reload_cmds_CXX" - ;; - *) - archive_cmds_CXX='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' - archive_expsym_cmds_CXX='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' - ;; - esac - ;; - - tandem*) - case $cc_basename in - NCC*) - # NonStop-UX NCC 3.20 - # FIXME: insert proper C++ library support - ld_shlibs_CXX=no - ;; - *) - # FIXME: insert proper C++ library support - ld_shlibs_CXX=no - ;; - esac - ;; - - vxworks*) - # FIXME: insert proper C++ library support - ld_shlibs_CXX=no - ;; - - *) - # FIXME: insert proper C++ library support - ld_shlibs_CXX=no - ;; - esac - - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ld_shlibs_CXX" >&5 -$as_echo "$ld_shlibs_CXX" >&6; } - test "$ld_shlibs_CXX" = no && can_build_shared=no - - GCC_CXX="$GXX" - LD_CXX="$LD" - - ## CAVEAT EMPTOR: - ## There is no encapsulation within the following macros, do not change - ## the running order or otherwise move them around unless you know exactly - ## what you are doing... - # Dependencies to place before and after the object being linked: -predep_objects_CXX= -postdep_objects_CXX= -predeps_CXX= -postdeps_CXX= -compiler_lib_search_path_CXX= - -cat > conftest.$ac_ext <<_LT_EOF -class Foo -{ -public: - Foo (void) { a = 0; } -private: - int a; -}; -_LT_EOF - - -_lt_libdeps_save_CFLAGS=$CFLAGS -case "$CC $CFLAGS " in #( -*\ -flto*\ *) CFLAGS="$CFLAGS -fno-lto" ;; -*\ -fwhopr*\ *) CFLAGS="$CFLAGS -fno-whopr" ;; -*\ -fuse-linker-plugin*\ *) CFLAGS="$CFLAGS -fno-use-linker-plugin" ;; -esac - -if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 - (eval $ac_compile) 2>&5 - ac_status=$? - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; }; then - # Parse the compiler output and extract the necessary - # objects, libraries and library flags. - - # Sentinel used to keep track of whether or not we are before - # the conftest object file. - pre_test_object_deps_done=no - - for p in `eval "$output_verbose_link_cmd"`; do - case ${prev}${p} in - - -L* | -R* | -l*) - # Some compilers place space between "-{L,R}" and the path. - # Remove the space. - if test $p = "-L" || - test $p = "-R"; then - prev=$p - continue - fi - - # Expand the sysroot to ease extracting the directories later. - if test -z "$prev"; then - case $p in - -L*) func_stripname_cnf '-L' '' "$p"; prev=-L; p=$func_stripname_result ;; - -R*) func_stripname_cnf '-R' '' "$p"; prev=-R; p=$func_stripname_result ;; - -l*) func_stripname_cnf '-l' '' "$p"; prev=-l; p=$func_stripname_result ;; - esac - fi - case $p in - =*) func_stripname_cnf '=' '' "$p"; p=$lt_sysroot$func_stripname_result ;; - esac - if test "$pre_test_object_deps_done" = no; then - case ${prev} in - -L | -R) - # Internal compiler library paths should come after those - # provided the user. The postdeps already come after the - # user supplied libs so there is no need to process them. - if test -z "$compiler_lib_search_path_CXX"; then - compiler_lib_search_path_CXX="${prev}${p}" - else - compiler_lib_search_path_CXX="${compiler_lib_search_path_CXX} ${prev}${p}" - fi - ;; - # The "-l" case would never come before the object being - # linked, so don't bother handling this case. - esac - else - if test -z "$postdeps_CXX"; then - postdeps_CXX="${prev}${p}" - else - postdeps_CXX="${postdeps_CXX} ${prev}${p}" - fi - fi - prev= - ;; - - *.lto.$objext) ;; # Ignore GCC LTO objects - *.$objext) - # This assumes that the test object file only shows up - # once in the compiler output. - if test "$p" = "conftest.$objext"; then - pre_test_object_deps_done=yes - continue - fi - - if test "$pre_test_object_deps_done" = no; then - if test -z "$predep_objects_CXX"; then - predep_objects_CXX="$p" - else - predep_objects_CXX="$predep_objects_CXX $p" - fi - else - if test -z "$postdep_objects_CXX"; then - postdep_objects_CXX="$p" - else - postdep_objects_CXX="$postdep_objects_CXX $p" - fi - fi - ;; - - *) ;; # Ignore the rest. - - esac - done - - # Clean up. - rm -f a.out a.exe -else - echo "libtool.m4: error: problem compiling CXX test program" -fi - -$RM -f confest.$objext -CFLAGS=$_lt_libdeps_save_CFLAGS - -# PORTME: override above test on systems where it is broken -case $host_os in -interix[3-9]*) - # Interix 3.5 installs completely hosed .la files for C++, so rather than - # hack all around it, let's just trust "g++" to DTRT. - predep_objects_CXX= - postdep_objects_CXX= - postdeps_CXX= - ;; - -linux*) - case `$CC -V 2>&1 | sed 5q` in - *Sun\ C*) - # Sun C++ 5.9 - - # The more standards-conforming stlport4 library is - # incompatible with the Cstd library. Avoid specifying - # it if it's in CXXFLAGS. Ignore libCrun as - # -library=stlport4 depends on it. - case " $CXX $CXXFLAGS " in - *" -library=stlport4 "*) - solaris_use_stlport4=yes - ;; - esac - - if test "$solaris_use_stlport4" != yes; then - postdeps_CXX='-library=Cstd -library=Crun' - fi - ;; - esac - ;; - -solaris*) - case $cc_basename in - CC* | sunCC*) - # The more standards-conforming stlport4 library is - # incompatible with the Cstd library. Avoid specifying - # it if it's in CXXFLAGS. Ignore libCrun as - # -library=stlport4 depends on it. - case " $CXX $CXXFLAGS " in - *" -library=stlport4 "*) - solaris_use_stlport4=yes - ;; - esac - - # Adding this requires a known-good setup of shared libraries for - # Sun compiler versions before 5.6, else PIC objects from an old - # archive will be linked into the output, leading to subtle bugs. - if test "$solaris_use_stlport4" != yes; then - postdeps_CXX='-library=Cstd -library=Crun' - fi - ;; - esac - ;; -esac - - -case " $postdeps_CXX " in -*" -lc "*) archive_cmds_need_lc_CXX=no ;; -esac - compiler_lib_search_dirs_CXX= -if test -n "${compiler_lib_search_path_CXX}"; then - compiler_lib_search_dirs_CXX=`echo " ${compiler_lib_search_path_CXX}" | ${SED} -e 's! -L! !g' -e 's!^ !!'` -fi - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - lt_prog_compiler_wl_CXX= -lt_prog_compiler_pic_CXX= -lt_prog_compiler_static_CXX= - - - # C++ specific cases for pic, static, wl, etc. - if test "$GXX" = yes; then - lt_prog_compiler_wl_CXX='-Wl,' - lt_prog_compiler_static_CXX='-static' - - case $host_os in - aix*) - # All AIX code is PIC. - if test "$host_cpu" = ia64; then - # AIX 5 now supports IA64 processor - lt_prog_compiler_static_CXX='-Bstatic' - fi - ;; - - amigaos*) - case $host_cpu in - powerpc) - # see comment about AmigaOS4 .so support - lt_prog_compiler_pic_CXX='-fPIC' - ;; - m68k) - # FIXME: we need at least 68020 code to build shared libraries, but - # adding the `-m68020' flag to GCC prevents building anything better, - # like `-m68040'. - lt_prog_compiler_pic_CXX='-m68020 -resident32 -malways-restore-a4' - ;; - esac - ;; - - beos* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*) - # PIC is the default for these OSes. - ;; - mingw* | cygwin* | os2* | pw32* | cegcc*) - # This hack is so that the source file can tell whether it is being - # built for inclusion in a dll (and should export symbols for example). - # Although the cygwin gcc ignores -fPIC, still need this for old-style - # (--disable-auto-import) libraries - lt_prog_compiler_pic_CXX='-DDLL_EXPORT' - ;; - darwin* | rhapsody*) - # PIC is the default on this platform - # Common symbols not allowed in MH_DYLIB files - lt_prog_compiler_pic_CXX='-fno-common' - ;; - *djgpp*) - # DJGPP does not support shared libraries at all - lt_prog_compiler_pic_CXX= - ;; - haiku*) - # PIC is the default for Haiku. - # The "-static" flag exists, but is broken. - lt_prog_compiler_static_CXX= - ;; - interix[3-9]*) - # Interix 3.x gcc -fpic/-fPIC options generate broken code. - # Instead, we relocate shared libraries at runtime. - ;; - sysv4*MP*) - if test -d /usr/nec; then - lt_prog_compiler_pic_CXX=-Kconform_pic - fi - ;; - hpux*) - # PIC is the default for 64-bit PA HP-UX, but not for 32-bit - # PA HP-UX. On IA64 HP-UX, PIC is the default but the pic flag - # sets the default TLS model and affects inlining. - case $host_cpu in - hppa*64*) - ;; - *) - lt_prog_compiler_pic_CXX='-fPIC' - ;; - esac - ;; - *qnx* | *nto*) - # QNX uses GNU C++, but need to define -shared option too, otherwise - # it will coredump. - lt_prog_compiler_pic_CXX='-fPIC -shared' - ;; - *) - lt_prog_compiler_pic_CXX='-fPIC' - ;; - esac - else - case $host_os in - aix[4-9]*) - # All AIX code is PIC. - if test "$host_cpu" = ia64; then - # AIX 5 now supports IA64 processor - lt_prog_compiler_static_CXX='-Bstatic' - else - lt_prog_compiler_static_CXX='-bnso -bI:/lib/syscalls.exp' - fi - ;; - chorus*) - case $cc_basename in - cxch68*) - # Green Hills C++ Compiler - # _LT_TAGVAR(lt_prog_compiler_static, CXX)="--no_auto_instantiation -u __main -u __premain -u _abort -r $COOL_DIR/lib/libOrb.a $MVME_DIR/lib/CC/libC.a $MVME_DIR/lib/classix/libcx.s.a" - ;; - esac - ;; - mingw* | cygwin* | os2* | pw32* | cegcc*) - # This hack is so that the source file can tell whether it is being - # built for inclusion in a dll (and should export symbols for example). - lt_prog_compiler_pic_CXX='-DDLL_EXPORT' - ;; - dgux*) - case $cc_basename in - ec++*) - lt_prog_compiler_pic_CXX='-KPIC' - ;; - ghcx*) - # Green Hills C++ Compiler - lt_prog_compiler_pic_CXX='-pic' - ;; - *) - ;; - esac - ;; - freebsd* | dragonfly*) - # FreeBSD uses GNU C++ - ;; - hpux9* | hpux10* | hpux11*) - case $cc_basename in - CC*) - lt_prog_compiler_wl_CXX='-Wl,' - lt_prog_compiler_static_CXX='${wl}-a ${wl}archive' - if test "$host_cpu" != ia64; then - lt_prog_compiler_pic_CXX='+Z' - fi - ;; - aCC*) - lt_prog_compiler_wl_CXX='-Wl,' - lt_prog_compiler_static_CXX='${wl}-a ${wl}archive' - case $host_cpu in - hppa*64*|ia64*) - # +Z the default - ;; - *) - lt_prog_compiler_pic_CXX='+Z' - ;; - esac - ;; - *) - ;; - esac - ;; - interix*) - # This is c89, which is MS Visual C++ (no shared libs) - # Anyone wants to do a port? - ;; - irix5* | irix6* | nonstopux*) - case $cc_basename in - CC*) - lt_prog_compiler_wl_CXX='-Wl,' - lt_prog_compiler_static_CXX='-non_shared' - # CC pic flag -KPIC is the default. - ;; - *) - ;; - esac - ;; - linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) - case $cc_basename in - KCC*) - # KAI C++ Compiler - lt_prog_compiler_wl_CXX='--backend -Wl,' - lt_prog_compiler_pic_CXX='-fPIC' - ;; - ecpc* ) - # old Intel C++ for x86_64 which still supported -KPIC. - lt_prog_compiler_wl_CXX='-Wl,' - lt_prog_compiler_pic_CXX='-KPIC' - lt_prog_compiler_static_CXX='-static' - ;; - icpc* ) - # Intel C++, used to be incompatible with GCC. - # ICC 10 doesn't accept -KPIC any more. - lt_prog_compiler_wl_CXX='-Wl,' - lt_prog_compiler_pic_CXX='-fPIC' - lt_prog_compiler_static_CXX='-static' - ;; - pgCC* | pgcpp*) - # Portland Group C++ compiler - lt_prog_compiler_wl_CXX='-Wl,' - lt_prog_compiler_pic_CXX='-fpic' - lt_prog_compiler_static_CXX='-Bstatic' - ;; - cxx*) - # Compaq C++ - # Make sure the PIC flag is empty. It appears that all Alpha - # Linux and Compaq Tru64 Unix objects are PIC. - lt_prog_compiler_pic_CXX= - lt_prog_compiler_static_CXX='-non_shared' - ;; - xlc* | xlC* | bgxl[cC]* | mpixl[cC]*) - # IBM XL 8.0, 9.0 on PPC and BlueGene - lt_prog_compiler_wl_CXX='-Wl,' - lt_prog_compiler_pic_CXX='-qpic' - lt_prog_compiler_static_CXX='-qstaticlink' - ;; - *) - case `$CC -V 2>&1 | sed 5q` in - *Sun\ C*) - # Sun C++ 5.9 - lt_prog_compiler_pic_CXX='-KPIC' - lt_prog_compiler_static_CXX='-Bstatic' - lt_prog_compiler_wl_CXX='-Qoption ld ' - ;; - esac - ;; - esac - ;; - lynxos*) - ;; - m88k*) - ;; - mvs*) - case $cc_basename in - cxx*) - lt_prog_compiler_pic_CXX='-W c,exportall' - ;; - *) - ;; - esac - ;; - netbsd* | netbsdelf*-gnu) - ;; - *qnx* | *nto*) - # QNX uses GNU C++, but need to define -shared option too, otherwise - # it will coredump. - lt_prog_compiler_pic_CXX='-fPIC -shared' - ;; - osf3* | osf4* | osf5*) - case $cc_basename in - KCC*) - lt_prog_compiler_wl_CXX='--backend -Wl,' - ;; - RCC*) - # Rational C++ 2.4.1 - lt_prog_compiler_pic_CXX='-pic' - ;; - cxx*) - # Digital/Compaq C++ - lt_prog_compiler_wl_CXX='-Wl,' - # Make sure the PIC flag is empty. It appears that all Alpha - # Linux and Compaq Tru64 Unix objects are PIC. - lt_prog_compiler_pic_CXX= - lt_prog_compiler_static_CXX='-non_shared' - ;; - *) - ;; - esac - ;; - psos*) - ;; - solaris*) - case $cc_basename in - CC* | sunCC*) - # Sun C++ 4.2, 5.x and Centerline C++ - lt_prog_compiler_pic_CXX='-KPIC' - lt_prog_compiler_static_CXX='-Bstatic' - lt_prog_compiler_wl_CXX='-Qoption ld ' - ;; - gcx*) - # Green Hills C++ Compiler - lt_prog_compiler_pic_CXX='-PIC' - ;; - *) - ;; - esac - ;; - sunos4*) - case $cc_basename in - CC*) - # Sun C++ 4.x - lt_prog_compiler_pic_CXX='-pic' - lt_prog_compiler_static_CXX='-Bstatic' - ;; - lcc*) - # Lucid - lt_prog_compiler_pic_CXX='-pic' - ;; - *) - ;; - esac - ;; - sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*) - case $cc_basename in - CC*) - lt_prog_compiler_wl_CXX='-Wl,' - lt_prog_compiler_pic_CXX='-KPIC' - lt_prog_compiler_static_CXX='-Bstatic' - ;; - esac - ;; - tandem*) - case $cc_basename in - NCC*) - # NonStop-UX NCC 3.20 - lt_prog_compiler_pic_CXX='-KPIC' - ;; - *) - ;; - esac - ;; - vxworks*) - ;; - *) - lt_prog_compiler_can_build_shared_CXX=no - ;; - esac - fi - -case $host_os in - # For platforms which do not support PIC, -DPIC is meaningless: - *djgpp*) - lt_prog_compiler_pic_CXX= - ;; - *) - lt_prog_compiler_pic_CXX="$lt_prog_compiler_pic_CXX -DPIC" - ;; -esac - -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $compiler option to produce PIC" >&5 -$as_echo_n "checking for $compiler option to produce PIC... " >&6; } -if ${lt_cv_prog_compiler_pic_CXX+:} false; then : - $as_echo_n "(cached) " >&6 -else - lt_cv_prog_compiler_pic_CXX=$lt_prog_compiler_pic_CXX -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_pic_CXX" >&5 -$as_echo "$lt_cv_prog_compiler_pic_CXX" >&6; } -lt_prog_compiler_pic_CXX=$lt_cv_prog_compiler_pic_CXX - -# -# Check to make sure the PIC flag actually works. -# -if test -n "$lt_prog_compiler_pic_CXX"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler PIC flag $lt_prog_compiler_pic_CXX works" >&5 -$as_echo_n "checking if $compiler PIC flag $lt_prog_compiler_pic_CXX works... " >&6; } -if ${lt_cv_prog_compiler_pic_works_CXX+:} false; then : - $as_echo_n "(cached) " >&6 -else - lt_cv_prog_compiler_pic_works_CXX=no - ac_outfile=conftest.$ac_objext - echo "$lt_simple_compile_test_code" > conftest.$ac_ext - lt_compiler_flag="$lt_prog_compiler_pic_CXX -DPIC" - # Insert the option either (1) after the last *FLAGS variable, or - # (2) before a word containing "conftest.", or (3) at the end. - # Note that $ac_compile itself does not contain backslashes and begins - # with a dollar sign (not a hyphen), so the echo should work correctly. - # The option is referenced via a variable to avoid confusing sed. - lt_compile=`echo "$ac_compile" | $SED \ - -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ - -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ - -e 's:$: $lt_compiler_flag:'` - (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5) - (eval "$lt_compile" 2>conftest.err) - ac_status=$? - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - if (exit $ac_status) && test -s "$ac_outfile"; then - # The compiler can only warn and ignore the option if not recognized - # So say no if there are warnings other than the usual output. - $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' >conftest.exp - $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 - if test ! -s conftest.er2 || diff conftest.exp conftest.er2 >/dev/null; then - lt_cv_prog_compiler_pic_works_CXX=yes - fi - fi - $RM conftest* - -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_pic_works_CXX" >&5 -$as_echo "$lt_cv_prog_compiler_pic_works_CXX" >&6; } - -if test x"$lt_cv_prog_compiler_pic_works_CXX" = xyes; then - case $lt_prog_compiler_pic_CXX in - "" | " "*) ;; - *) lt_prog_compiler_pic_CXX=" $lt_prog_compiler_pic_CXX" ;; - esac -else - lt_prog_compiler_pic_CXX= - lt_prog_compiler_can_build_shared_CXX=no -fi - -fi - - - - - -# -# Check to make sure the static flag actually works. -# -wl=$lt_prog_compiler_wl_CXX eval lt_tmp_static_flag=\"$lt_prog_compiler_static_CXX\" -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler static flag $lt_tmp_static_flag works" >&5 -$as_echo_n "checking if $compiler static flag $lt_tmp_static_flag works... " >&6; } -if ${lt_cv_prog_compiler_static_works_CXX+:} false; then : - $as_echo_n "(cached) " >&6 -else - lt_cv_prog_compiler_static_works_CXX=no - save_LDFLAGS="$LDFLAGS" - LDFLAGS="$LDFLAGS $lt_tmp_static_flag" - echo "$lt_simple_link_test_code" > conftest.$ac_ext - if (eval $ac_link 2>conftest.err) && test -s conftest$ac_exeext; then - # The linker can only warn and ignore the option if not recognized - # So say no if there are warnings - if test -s conftest.err; then - # Append any errors to the config.log. - cat conftest.err 1>&5 - $ECHO "$_lt_linker_boilerplate" | $SED '/^$/d' > conftest.exp - $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 - if diff conftest.exp conftest.er2 >/dev/null; then - lt_cv_prog_compiler_static_works_CXX=yes - fi - else - lt_cv_prog_compiler_static_works_CXX=yes - fi - fi - $RM -r conftest* - LDFLAGS="$save_LDFLAGS" - -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_static_works_CXX" >&5 -$as_echo "$lt_cv_prog_compiler_static_works_CXX" >&6; } - -if test x"$lt_cv_prog_compiler_static_works_CXX" = xyes; then - : -else - lt_prog_compiler_static_CXX= -fi - - - - - { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler supports -c -o file.$ac_objext" >&5 -$as_echo_n "checking if $compiler supports -c -o file.$ac_objext... " >&6; } -if ${lt_cv_prog_compiler_c_o_CXX+:} false; then : - $as_echo_n "(cached) " >&6 -else - lt_cv_prog_compiler_c_o_CXX=no - $RM -r conftest 2>/dev/null - mkdir conftest - cd conftest - mkdir out - echo "$lt_simple_compile_test_code" > conftest.$ac_ext - - lt_compiler_flag="-o out/conftest2.$ac_objext" - # Insert the option either (1) after the last *FLAGS variable, or - # (2) before a word containing "conftest.", or (3) at the end. - # Note that $ac_compile itself does not contain backslashes and begins - # with a dollar sign (not a hyphen), so the echo should work correctly. - lt_compile=`echo "$ac_compile" | $SED \ - -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ - -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ - -e 's:$: $lt_compiler_flag:'` - (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5) - (eval "$lt_compile" 2>out/conftest.err) - ac_status=$? - cat out/conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - if (exit $ac_status) && test -s out/conftest2.$ac_objext - then - # The compiler can only warn and ignore the option if not recognized - # So say no if there are warnings - $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' > out/conftest.exp - $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2 - if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then - lt_cv_prog_compiler_c_o_CXX=yes - fi - fi - chmod u+w . 2>&5 - $RM conftest* - # SGI C++ compiler will create directory out/ii_files/ for - # template instantiation - test -d out/ii_files && $RM out/ii_files/* && rmdir out/ii_files - $RM out/* && rmdir out - cd .. - $RM -r conftest - $RM conftest* - -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_c_o_CXX" >&5 -$as_echo "$lt_cv_prog_compiler_c_o_CXX" >&6; } - - - - { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler supports -c -o file.$ac_objext" >&5 -$as_echo_n "checking if $compiler supports -c -o file.$ac_objext... " >&6; } -if ${lt_cv_prog_compiler_c_o_CXX+:} false; then : - $as_echo_n "(cached) " >&6 -else - lt_cv_prog_compiler_c_o_CXX=no - $RM -r conftest 2>/dev/null - mkdir conftest - cd conftest - mkdir out - echo "$lt_simple_compile_test_code" > conftest.$ac_ext - - lt_compiler_flag="-o out/conftest2.$ac_objext" - # Insert the option either (1) after the last *FLAGS variable, or - # (2) before a word containing "conftest.", or (3) at the end. - # Note that $ac_compile itself does not contain backslashes and begins - # with a dollar sign (not a hyphen), so the echo should work correctly. - lt_compile=`echo "$ac_compile" | $SED \ - -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ - -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ - -e 's:$: $lt_compiler_flag:'` - (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5) - (eval "$lt_compile" 2>out/conftest.err) - ac_status=$? - cat out/conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - if (exit $ac_status) && test -s out/conftest2.$ac_objext - then - # The compiler can only warn and ignore the option if not recognized - # So say no if there are warnings - $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' > out/conftest.exp - $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2 - if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then - lt_cv_prog_compiler_c_o_CXX=yes - fi - fi - chmod u+w . 2>&5 - $RM conftest* - # SGI C++ compiler will create directory out/ii_files/ for - # template instantiation - test -d out/ii_files && $RM out/ii_files/* && rmdir out/ii_files - $RM out/* && rmdir out - cd .. - $RM -r conftest - $RM conftest* - -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_c_o_CXX" >&5 -$as_echo "$lt_cv_prog_compiler_c_o_CXX" >&6; } - - - - -hard_links="nottested" -if test "$lt_cv_prog_compiler_c_o_CXX" = no && test "$need_locks" != no; then - # do not overwrite the value of need_locks provided by the user - { $as_echo "$as_me:${as_lineno-$LINENO}: checking if we can lock with hard links" >&5 -$as_echo_n "checking if we can lock with hard links... " >&6; } - hard_links=yes - $RM conftest* - ln conftest.a conftest.b 2>/dev/null && hard_links=no - touch conftest.a - ln conftest.a conftest.b 2>&5 || hard_links=no - ln conftest.a conftest.b 2>/dev/null && hard_links=no - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $hard_links" >&5 -$as_echo "$hard_links" >&6; } - if test "$hard_links" = no; then - { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: \`$CC' does not support \`-c -o', so \`make -j' may be unsafe" >&5 -$as_echo "$as_me: WARNING: \`$CC' does not support \`-c -o', so \`make -j' may be unsafe" >&2;} - need_locks=warn - fi -else - need_locks=no -fi - - - - { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the $compiler linker ($LD) supports shared libraries" >&5 -$as_echo_n "checking whether the $compiler linker ($LD) supports shared libraries... " >&6; } - - export_symbols_cmds_CXX='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' - exclude_expsyms_CXX='_GLOBAL_OFFSET_TABLE_|_GLOBAL__F[ID]_.*' - case $host_os in - aix[4-9]*) - # If we're using GNU nm, then we don't want the "-C" option. - # -C means demangle to AIX nm, but means don't demangle with GNU nm - # Also, AIX nm treats weak defined symbols like other global defined - # symbols, whereas GNU nm marks them as "W". - if $NM -V 2>&1 | $GREP 'GNU' > /dev/null; then - export_symbols_cmds_CXX='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W")) && (substr(\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols' - else - export_symbols_cmds_CXX='$NM -BCpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B")) && (substr(\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols' - fi - ;; - pw32*) - export_symbols_cmds_CXX="$ltdll_cmds" - ;; - cygwin* | mingw* | cegcc*) - case $cc_basename in - cl*) - exclude_expsyms_CXX='_NULL_IMPORT_DESCRIPTOR|_IMPORT_DESCRIPTOR_.*' - ;; - *) - export_symbols_cmds_CXX='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[BCDGRS][ ]/s/.*[ ]\([^ ]*\)/\1 DATA/;s/^.*[ ]__nm__\([^ ]*\)[ ][^ ]*/\1 DATA/;/^I[ ]/d;/^[AITW][ ]/s/.* //'\'' | sort | uniq > $export_symbols' - exclude_expsyms_CXX='[_]+GLOBAL_OFFSET_TABLE_|[_]+GLOBAL__[FID]_.*|[_]+head_[A-Za-z0-9_]+_dll|[A-Za-z0-9_]+_dll_iname' - ;; - esac - ;; - linux* | k*bsd*-gnu | gnu*) - link_all_deplibs_CXX=no - ;; - *) - export_symbols_cmds_CXX='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' - ;; - esac - -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ld_shlibs_CXX" >&5 -$as_echo "$ld_shlibs_CXX" >&6; } -test "$ld_shlibs_CXX" = no && can_build_shared=no - -with_gnu_ld_CXX=$with_gnu_ld - - - - - - -# -# Do we need to explicitly link libc? -# -case "x$archive_cmds_need_lc_CXX" in -x|xyes) - # Assume -lc should be added - archive_cmds_need_lc_CXX=yes - - if test "$enable_shared" = yes && test "$GCC" = yes; then - case $archive_cmds_CXX in - *'~'*) - # FIXME: we may have to deal with multi-command sequences. - ;; - '$CC '*) - # Test whether the compiler implicitly links with -lc since on some - # systems, -lgcc has to come before -lc. If gcc already passes -lc - # to ld, don't add -lc before -lgcc. - { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether -lc should be explicitly linked in" >&5 -$as_echo_n "checking whether -lc should be explicitly linked in... " >&6; } -if ${lt_cv_archive_cmds_need_lc_CXX+:} false; then : - $as_echo_n "(cached) " >&6 -else - $RM conftest* - echo "$lt_simple_compile_test_code" > conftest.$ac_ext - - if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 - (eval $ac_compile) 2>&5 - ac_status=$? - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; } 2>conftest.err; then - soname=conftest - lib=conftest - libobjs=conftest.$ac_objext - deplibs= - wl=$lt_prog_compiler_wl_CXX - pic_flag=$lt_prog_compiler_pic_CXX - compiler_flags=-v - linker_flags=-v - verstring= - output_objdir=. - libname=conftest - lt_save_allow_undefined_flag=$allow_undefined_flag_CXX - allow_undefined_flag_CXX= - if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$archive_cmds_CXX 2\>\&1 \| $GREP \" -lc \" \>/dev/null 2\>\&1\""; } >&5 - (eval $archive_cmds_CXX 2\>\&1 \| $GREP \" -lc \" \>/dev/null 2\>\&1) 2>&5 - ac_status=$? - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; } - then - lt_cv_archive_cmds_need_lc_CXX=no - else - lt_cv_archive_cmds_need_lc_CXX=yes - fi - allow_undefined_flag_CXX=$lt_save_allow_undefined_flag - else - cat conftest.err 1>&5 - fi - $RM conftest* - -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_archive_cmds_need_lc_CXX" >&5 -$as_echo "$lt_cv_archive_cmds_need_lc_CXX" >&6; } - archive_cmds_need_lc_CXX=$lt_cv_archive_cmds_need_lc_CXX - ;; - esac - fi - ;; -esac - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - { $as_echo "$as_me:${as_lineno-$LINENO}: checking dynamic linker characteristics" >&5 -$as_echo_n "checking dynamic linker characteristics... " >&6; } - -library_names_spec= -libname_spec='lib$name' -soname_spec= -shrext_cmds=".so" -postinstall_cmds= -postuninstall_cmds= -finish_cmds= -finish_eval= -shlibpath_var= -shlibpath_overrides_runpath=unknown -version_type=none -dynamic_linker="$host_os ld.so" -sys_lib_dlsearch_path_spec="/lib /usr/lib" -need_lib_prefix=unknown -hardcode_into_libs=no - -# when you set need_version to no, make sure it does not cause -set_version -# flags to be left without arguments -need_version=unknown - -case $host_os in -aix3*) - version_type=linux # correct to gnu/linux during the next big refactor - library_names_spec='${libname}${release}${shared_ext}$versuffix $libname.a' - shlibpath_var=LIBPATH - - # AIX 3 has no versioning support, so we append a major version to the name. - soname_spec='${libname}${release}${shared_ext}$major' - ;; - -aix[4-9]*) - version_type=linux # correct to gnu/linux during the next big refactor - need_lib_prefix=no - need_version=no - hardcode_into_libs=yes - if test "$host_cpu" = ia64; then - # AIX 5 supports IA64 - library_names_spec='${libname}${release}${shared_ext}$major ${libname}${release}${shared_ext}$versuffix $libname${shared_ext}' - shlibpath_var=LD_LIBRARY_PATH - else - # With GCC up to 2.95.x, collect2 would create an import file - # for dependence libraries. The import file would start with - # the line `#! .'. This would cause the generated library to - # depend on `.', always an invalid library. This was fixed in - # development snapshots of GCC prior to 3.0. - case $host_os in - aix4 | aix4.[01] | aix4.[01].*) - if { echo '#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 97)' - echo ' yes ' - echo '#endif'; } | ${CC} -E - | $GREP yes > /dev/null; then - : - else - can_build_shared=no - fi - ;; - esac - # AIX (on Power*) has no versioning support, so currently we can not hardcode correct - # soname into executable. Probably we can add versioning support to - # collect2, so additional links can be useful in future. - if test "$aix_use_runtimelinking" = yes; then - # If using run time linking (on AIX 4.2 or later) use lib.so - # instead of lib.a to let people know that these are not - # typical AIX shared libraries. - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' - else - # We preserve .a as extension for shared libraries through AIX4.2 - # and later when we are not doing run time linking. - library_names_spec='${libname}${release}.a $libname.a' - soname_spec='${libname}${release}${shared_ext}$major' - fi - shlibpath_var=LIBPATH - fi - ;; - -amigaos*) - case $host_cpu in - powerpc) - # Since July 2007 AmigaOS4 officially supports .so libraries. - # When compiling the executable, add -use-dynld -Lsobjs: to the compileline. - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' - ;; - m68k) - library_names_spec='$libname.ixlibrary $libname.a' - # Create ${libname}_ixlibrary.a entries in /sys/libs. - finish_eval='for lib in `ls $libdir/*.ixlibrary 2>/dev/null`; do libname=`func_echo_all "$lib" | $SED '\''s%^.*/\([^/]*\)\.ixlibrary$%\1%'\''`; test $RM /sys/libs/${libname}_ixlibrary.a; $show "cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a"; cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a || exit 1; done' - ;; - esac - ;; - -beos*) - library_names_spec='${libname}${shared_ext}' - dynamic_linker="$host_os ld.so" - shlibpath_var=LIBRARY_PATH - ;; - -bsdi[45]*) - version_type=linux # correct to gnu/linux during the next big refactor - need_version=no - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - finish_cmds='PATH="\$PATH:/sbin" ldconfig $libdir' - shlibpath_var=LD_LIBRARY_PATH - sys_lib_search_path_spec="/shlib /usr/lib /usr/X11/lib /usr/contrib/lib /lib /usr/local/lib" - sys_lib_dlsearch_path_spec="/shlib /usr/lib /usr/local/lib" - # the default ld.so.conf also contains /usr/contrib/lib and - # /usr/X11R6/lib (/usr/X11 is a link to /usr/X11R6), but let us allow - # libtool to hard-code these into programs - ;; - -cygwin* | mingw* | pw32* | cegcc*) - version_type=windows - shrext_cmds=".dll" - need_version=no - need_lib_prefix=no - - case $GCC,$cc_basename in - yes,*) - # gcc - library_names_spec='$libname.dll.a' - # DLL is installed to $(libdir)/../bin by postinstall_cmds - postinstall_cmds='base_file=`basename \${file}`~ - dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\${base_file}'\''i; echo \$dlname'\''`~ - dldir=$destdir/`dirname \$dlpath`~ - test -d \$dldir || mkdir -p \$dldir~ - $install_prog $dir/$dlname \$dldir/$dlname~ - chmod a+x \$dldir/$dlname~ - if test -n '\''$stripme'\'' && test -n '\''$striplib'\''; then - eval '\''$striplib \$dldir/$dlname'\'' || exit \$?; - fi' - postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~ - dlpath=$dir/\$dldll~ - $RM \$dlpath' - shlibpath_overrides_runpath=yes - - case $host_os in - cygwin*) - # Cygwin DLLs use 'cyg' prefix rather than 'lib' - soname_spec='`echo ${libname} | sed -e 's/^lib/cyg/'``echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext}' - - ;; - mingw* | cegcc*) - # MinGW DLLs use traditional 'lib' prefix - soname_spec='${libname}`echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext}' - ;; - pw32*) - # pw32 DLLs use 'pw' prefix rather than 'lib' - library_names_spec='`echo ${libname} | sed -e 's/^lib/pw/'``echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext}' - ;; - esac - dynamic_linker='Win32 ld.exe' - ;; - - *,cl*) - # Native MSVC - libname_spec='$name' - soname_spec='${libname}`echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext}' - library_names_spec='${libname}.dll.lib' - - case $build_os in - mingw*) - sys_lib_search_path_spec= - lt_save_ifs=$IFS - IFS=';' - for lt_path in $LIB - do - IFS=$lt_save_ifs - # Let DOS variable expansion print the short 8.3 style file name. - lt_path=`cd "$lt_path" 2>/dev/null && cmd //C "for %i in (".") do @echo %~si"` - sys_lib_search_path_spec="$sys_lib_search_path_spec $lt_path" - done - IFS=$lt_save_ifs - # Convert to MSYS style. - sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | sed -e 's|\\\\|/|g' -e 's| \\([a-zA-Z]\\):| /\\1|g' -e 's|^ ||'` - ;; - cygwin*) - # Convert to unix form, then to dos form, then back to unix form - # but this time dos style (no spaces!) so that the unix form looks - # like /cygdrive/c/PROGRA~1:/cygdr... - sys_lib_search_path_spec=`cygpath --path --unix "$LIB"` - sys_lib_search_path_spec=`cygpath --path --dos "$sys_lib_search_path_spec" 2>/dev/null` - sys_lib_search_path_spec=`cygpath --path --unix "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"` - ;; - *) - sys_lib_search_path_spec="$LIB" - if $ECHO "$sys_lib_search_path_spec" | $GREP ';[c-zC-Z]:/' >/dev/null; then - # It is most probably a Windows format PATH. - sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e 's/;/ /g'` - else - sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"` - fi - # FIXME: find the short name or the path components, as spaces are - # common. (e.g. "Program Files" -> "PROGRA~1") - ;; - esac - - # DLL is installed to $(libdir)/../bin by postinstall_cmds - postinstall_cmds='base_file=`basename \${file}`~ - dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\${base_file}'\''i; echo \$dlname'\''`~ - dldir=$destdir/`dirname \$dlpath`~ - test -d \$dldir || mkdir -p \$dldir~ - $install_prog $dir/$dlname \$dldir/$dlname' - postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~ - dlpath=$dir/\$dldll~ - $RM \$dlpath' - shlibpath_overrides_runpath=yes - dynamic_linker='Win32 link.exe' - ;; - - *) - # Assume MSVC wrapper - library_names_spec='${libname}`echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext} $libname.lib' - dynamic_linker='Win32 ld.exe' - ;; - esac - # FIXME: first we should search . and the directory the executable is in - shlibpath_var=PATH - ;; - -darwin* | rhapsody*) - dynamic_linker="$host_os dyld" - version_type=darwin - need_lib_prefix=no - need_version=no - library_names_spec='${libname}${release}${major}$shared_ext ${libname}$shared_ext' - soname_spec='${libname}${release}${major}$shared_ext' - shlibpath_overrides_runpath=yes - shlibpath_var=DYLD_LIBRARY_PATH - shrext_cmds='`test .$module = .yes && echo .so || echo .dylib`' - - sys_lib_dlsearch_path_spec='/usr/local/lib /lib /usr/lib' - ;; - -dgux*) - version_type=linux # correct to gnu/linux during the next big refactor - need_lib_prefix=no - need_version=no - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname$shared_ext' - soname_spec='${libname}${release}${shared_ext}$major' - shlibpath_var=LD_LIBRARY_PATH - ;; - -freebsd* | dragonfly*) - # DragonFly does not have aout. When/if they implement a new - # versioning mechanism, adjust this. - if test -x /usr/bin/objformat; then - objformat=`/usr/bin/objformat` - else - case $host_os in - freebsd[23].*) objformat=aout ;; - *) objformat=elf ;; - esac - fi - version_type=freebsd-$objformat - case $version_type in - freebsd-elf*) - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext} $libname${shared_ext}' - need_version=no - need_lib_prefix=no - ;; - freebsd-*) - library_names_spec='${libname}${release}${shared_ext}$versuffix $libname${shared_ext}$versuffix' - need_version=yes - ;; - esac - shlibpath_var=LD_LIBRARY_PATH - case $host_os in - freebsd2.*) - shlibpath_overrides_runpath=yes - ;; - freebsd3.[01]* | freebsdelf3.[01]*) - shlibpath_overrides_runpath=yes - hardcode_into_libs=yes - ;; - freebsd3.[2-9]* | freebsdelf3.[2-9]* | \ - freebsd4.[0-5] | freebsdelf4.[0-5] | freebsd4.1.1 | freebsdelf4.1.1) - shlibpath_overrides_runpath=no - hardcode_into_libs=yes - ;; - *) # from 4.6 on, and DragonFly - shlibpath_overrides_runpath=yes - hardcode_into_libs=yes - ;; - esac - ;; - -haiku*) - version_type=linux # correct to gnu/linux during the next big refactor - need_lib_prefix=no - need_version=no - dynamic_linker="$host_os runtime_loader" - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}${major} ${libname}${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - shlibpath_var=LIBRARY_PATH - shlibpath_overrides_runpath=yes - sys_lib_dlsearch_path_spec='/boot/home/config/lib /boot/common/lib /boot/system/lib' - hardcode_into_libs=yes - ;; - -hpux9* | hpux10* | hpux11*) - # Give a soname corresponding to the major version so that dld.sl refuses to - # link against other versions. - version_type=sunos - need_lib_prefix=no - need_version=no - case $host_cpu in - ia64*) - shrext_cmds='.so' - hardcode_into_libs=yes - dynamic_linker="$host_os dld.so" - shlibpath_var=LD_LIBRARY_PATH - shlibpath_overrides_runpath=yes # Unless +noenvvar is specified. - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - if test "X$HPUX_IA64_MODE" = X32; then - sys_lib_search_path_spec="/usr/lib/hpux32 /usr/local/lib/hpux32 /usr/local/lib" - else - sys_lib_search_path_spec="/usr/lib/hpux64 /usr/local/lib/hpux64" - fi - sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec - ;; - hppa*64*) - shrext_cmds='.sl' - hardcode_into_libs=yes - dynamic_linker="$host_os dld.sl" - shlibpath_var=LD_LIBRARY_PATH # How should we handle SHLIB_PATH - shlibpath_overrides_runpath=yes # Unless +noenvvar is specified. - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - sys_lib_search_path_spec="/usr/lib/pa20_64 /usr/ccs/lib/pa20_64" - sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec - ;; - *) - shrext_cmds='.sl' - dynamic_linker="$host_os dld.sl" - shlibpath_var=SHLIB_PATH - shlibpath_overrides_runpath=no # +s is required to enable SHLIB_PATH - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - ;; - esac - # HP-UX runs *really* slowly unless shared libraries are mode 555, ... - postinstall_cmds='chmod 555 $lib' - # or fails outright, so override atomically: - install_override_mode=555 - ;; - -interix[3-9]*) - version_type=linux # correct to gnu/linux during the next big refactor - need_lib_prefix=no - need_version=no - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - dynamic_linker='Interix 3.x ld.so.1 (PE, like ELF)' - shlibpath_var=LD_LIBRARY_PATH - shlibpath_overrides_runpath=no - hardcode_into_libs=yes - ;; - -irix5* | irix6* | nonstopux*) - case $host_os in - nonstopux*) version_type=nonstopux ;; - *) - if test "$lt_cv_prog_gnu_ld" = yes; then - version_type=linux # correct to gnu/linux during the next big refactor - else - version_type=irix - fi ;; - esac - need_lib_prefix=no - need_version=no - soname_spec='${libname}${release}${shared_ext}$major' - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${release}${shared_ext} $libname${shared_ext}' - case $host_os in - irix5* | nonstopux*) - libsuff= shlibsuff= - ;; - *) - case $LD in # libtool.m4 will add one of these switches to LD - *-32|*"-32 "|*-melf32bsmip|*"-melf32bsmip ") - libsuff= shlibsuff= libmagic=32-bit;; - *-n32|*"-n32 "|*-melf32bmipn32|*"-melf32bmipn32 ") - libsuff=32 shlibsuff=N32 libmagic=N32;; - *-64|*"-64 "|*-melf64bmip|*"-melf64bmip ") - libsuff=64 shlibsuff=64 libmagic=64-bit;; - *) libsuff= shlibsuff= libmagic=never-match;; - esac - ;; - esac - shlibpath_var=LD_LIBRARY${shlibsuff}_PATH - shlibpath_overrides_runpath=no - sys_lib_search_path_spec="/usr/lib${libsuff} /lib${libsuff} /usr/local/lib${libsuff}" - sys_lib_dlsearch_path_spec="/usr/lib${libsuff} /lib${libsuff}" - hardcode_into_libs=yes - ;; - -# No shared lib support for Linux oldld, aout, or coff. -linux*oldld* | linux*aout* | linux*coff*) - dynamic_linker=no - ;; - -# This must be glibc/ELF. -linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) - version_type=linux # correct to gnu/linux during the next big refactor - need_lib_prefix=no - need_version=no - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - finish_cmds='PATH="\$PATH:/sbin" ldconfig -n $libdir' - shlibpath_var=LD_LIBRARY_PATH - shlibpath_overrides_runpath=no - - # Some binutils ld are patched to set DT_RUNPATH - if ${lt_cv_shlibpath_overrides_runpath+:} false; then : - $as_echo_n "(cached) " >&6 -else - lt_cv_shlibpath_overrides_runpath=no - save_LDFLAGS=$LDFLAGS - save_libdir=$libdir - eval "libdir=/foo; wl=\"$lt_prog_compiler_wl_CXX\"; \ - LDFLAGS=\"\$LDFLAGS $hardcode_libdir_flag_spec_CXX\"" - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main () -{ - - ; - return 0; -} -_ACEOF -if ac_fn_cxx_try_link "$LINENO"; then : - if ($OBJDUMP -p conftest$ac_exeext) 2>/dev/null | grep "RUNPATH.*$libdir" >/dev/null; then : - lt_cv_shlibpath_overrides_runpath=yes -fi -fi -rm -f core conftest.err conftest.$ac_objext \ - conftest$ac_exeext conftest.$ac_ext - LDFLAGS=$save_LDFLAGS - libdir=$save_libdir - -fi - - shlibpath_overrides_runpath=$lt_cv_shlibpath_overrides_runpath - - # This implies no fast_install, which is unacceptable. - # Some rework will be needed to allow for fast_install - # before this can be enabled. - hardcode_into_libs=yes - - # Append ld.so.conf contents to the search path - if test -f /etc/ld.so.conf; then - lt_ld_extra=`awk '/^include / { system(sprintf("cd /etc; cat %s 2>/dev/null", \$2)); skip = 1; } { if (!skip) print \$0; skip = 0; }' < /etc/ld.so.conf | $SED -e 's/#.*//;/^[ ]*hwcap[ ]/d;s/[:, ]/ /g;s/=[^=]*$//;s/=[^= ]* / /g;s/"//g;/^$/d' | tr '\n' ' '` - sys_lib_dlsearch_path_spec="/lib /usr/lib $lt_ld_extra" - fi - - # We used to test for /lib/ld.so.1 and disable shared libraries on - # powerpc, because MkLinux only supported shared libraries with the - # GNU dynamic linker. Since this was broken with cross compilers, - # most powerpc-linux boxes support dynamic linking these days and - # people can always --disable-shared, the test was removed, and we - # assume the GNU/Linux dynamic linker is in use. - dynamic_linker='GNU/Linux ld.so' - ;; - -netbsdelf*-gnu) - version_type=linux - need_lib_prefix=no - need_version=no - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - shlibpath_var=LD_LIBRARY_PATH - shlibpath_overrides_runpath=no - hardcode_into_libs=yes - dynamic_linker='NetBSD ld.elf_so' - ;; - -netbsd*) - version_type=sunos - need_lib_prefix=no - need_version=no - if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix' - finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' - dynamic_linker='NetBSD (a.out) ld.so' - else - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - dynamic_linker='NetBSD ld.elf_so' - fi - shlibpath_var=LD_LIBRARY_PATH - shlibpath_overrides_runpath=yes - hardcode_into_libs=yes - ;; - -newsos6) - version_type=linux # correct to gnu/linux during the next big refactor - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' - shlibpath_var=LD_LIBRARY_PATH - shlibpath_overrides_runpath=yes - ;; - -*nto* | *qnx*) - version_type=qnx - need_lib_prefix=no - need_version=no - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - shlibpath_var=LD_LIBRARY_PATH - shlibpath_overrides_runpath=no - hardcode_into_libs=yes - dynamic_linker='ldqnx.so' - ;; - -openbsd*) - version_type=sunos - sys_lib_dlsearch_path_spec="/usr/lib" - need_lib_prefix=no - # Some older versions of OpenBSD (3.3 at least) *do* need versioned libs. - case $host_os in - openbsd3.3 | openbsd3.3.*) need_version=yes ;; - *) need_version=no ;; - esac - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix' - finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' - shlibpath_var=LD_LIBRARY_PATH - if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then - case $host_os in - openbsd2.[89] | openbsd2.[89].*) - shlibpath_overrides_runpath=no - ;; - *) - shlibpath_overrides_runpath=yes - ;; - esac - else - shlibpath_overrides_runpath=yes - fi - ;; - -os2*) - libname_spec='$name' - shrext_cmds=".dll" - need_lib_prefix=no - library_names_spec='$libname${shared_ext} $libname.a' - dynamic_linker='OS/2 ld.exe' - shlibpath_var=LIBPATH - ;; - -osf3* | osf4* | osf5*) - version_type=osf - need_lib_prefix=no - need_version=no - soname_spec='${libname}${release}${shared_ext}$major' - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' - shlibpath_var=LD_LIBRARY_PATH - sys_lib_search_path_spec="/usr/shlib /usr/ccs/lib /usr/lib/cmplrs/cc /usr/lib /usr/local/lib /var/shlib" - sys_lib_dlsearch_path_spec="$sys_lib_search_path_spec" - ;; - -rdos*) - dynamic_linker=no - ;; - -solaris*) - version_type=linux # correct to gnu/linux during the next big refactor - need_lib_prefix=no - need_version=no - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - shlibpath_var=LD_LIBRARY_PATH - shlibpath_overrides_runpath=yes - hardcode_into_libs=yes - # ldd complains unless libraries are executable - postinstall_cmds='chmod +x $lib' - ;; - -sunos4*) - version_type=sunos - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix' - finish_cmds='PATH="\$PATH:/usr/etc" ldconfig $libdir' - shlibpath_var=LD_LIBRARY_PATH - shlibpath_overrides_runpath=yes - if test "$with_gnu_ld" = yes; then - need_lib_prefix=no - fi - need_version=yes - ;; - -sysv4 | sysv4.3*) - version_type=linux # correct to gnu/linux during the next big refactor - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - shlibpath_var=LD_LIBRARY_PATH - case $host_vendor in - sni) - shlibpath_overrides_runpath=no - need_lib_prefix=no - runpath_var=LD_RUN_PATH - ;; - siemens) - need_lib_prefix=no - ;; - motorola) - need_lib_prefix=no - need_version=no - shlibpath_overrides_runpath=no - sys_lib_search_path_spec='/lib /usr/lib /usr/ccs/lib' - ;; - esac - ;; - -sysv4*MP*) - if test -d /usr/nec ;then - version_type=linux # correct to gnu/linux during the next big refactor - library_names_spec='$libname${shared_ext}.$versuffix $libname${shared_ext}.$major $libname${shared_ext}' - soname_spec='$libname${shared_ext}.$major' - shlibpath_var=LD_LIBRARY_PATH - fi - ;; - -sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*) - version_type=freebsd-elf - need_lib_prefix=no - need_version=no - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext} $libname${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - shlibpath_var=LD_LIBRARY_PATH - shlibpath_overrides_runpath=yes - hardcode_into_libs=yes - if test "$with_gnu_ld" = yes; then - sys_lib_search_path_spec='/usr/local/lib /usr/gnu/lib /usr/ccs/lib /usr/lib /lib' - else - sys_lib_search_path_spec='/usr/ccs/lib /usr/lib' - case $host_os in - sco3.2v5*) - sys_lib_search_path_spec="$sys_lib_search_path_spec /lib" - ;; - esac - fi - sys_lib_dlsearch_path_spec='/usr/lib' - ;; - -tpf*) - # TPF is a cross-target only. Preferred cross-host = GNU/Linux. - version_type=linux # correct to gnu/linux during the next big refactor - need_lib_prefix=no - need_version=no - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' - shlibpath_var=LD_LIBRARY_PATH - shlibpath_overrides_runpath=no - hardcode_into_libs=yes - ;; - -uts4*) - version_type=linux # correct to gnu/linux during the next big refactor - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - shlibpath_var=LD_LIBRARY_PATH - ;; - -*) - dynamic_linker=no - ;; -esac -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $dynamic_linker" >&5 -$as_echo "$dynamic_linker" >&6; } -test "$dynamic_linker" = no && can_build_shared=no - -variables_saved_for_relink="PATH $shlibpath_var $runpath_var" -if test "$GCC" = yes; then - variables_saved_for_relink="$variables_saved_for_relink GCC_EXEC_PREFIX COMPILER_PATH LIBRARY_PATH" -fi - -if test "${lt_cv_sys_lib_search_path_spec+set}" = set; then - sys_lib_search_path_spec="$lt_cv_sys_lib_search_path_spec" -fi -if test "${lt_cv_sys_lib_dlsearch_path_spec+set}" = set; then - sys_lib_dlsearch_path_spec="$lt_cv_sys_lib_dlsearch_path_spec" -fi - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to hardcode library paths into programs" >&5 -$as_echo_n "checking how to hardcode library paths into programs... " >&6; } -hardcode_action_CXX= -if test -n "$hardcode_libdir_flag_spec_CXX" || - test -n "$runpath_var_CXX" || - test "X$hardcode_automatic_CXX" = "Xyes" ; then - - # We can hardcode non-existent directories. - if test "$hardcode_direct_CXX" != no && - # If the only mechanism to avoid hardcoding is shlibpath_var, we - # have to relink, otherwise we might link with an installed library - # when we should be linking with a yet-to-be-installed one - ## test "$_LT_TAGVAR(hardcode_shlibpath_var, CXX)" != no && - test "$hardcode_minus_L_CXX" != no; then - # Linking always hardcodes the temporary library directory. - hardcode_action_CXX=relink - else - # We can link without hardcoding, and we can hardcode nonexisting dirs. - hardcode_action_CXX=immediate - fi -else - # We cannot hardcode anything, or else we can only hardcode existing - # directories. - hardcode_action_CXX=unsupported -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $hardcode_action_CXX" >&5 -$as_echo "$hardcode_action_CXX" >&6; } - -if test "$hardcode_action_CXX" = relink || - test "$inherit_rpath_CXX" = yes; then - # Fast installation is not supported - enable_fast_install=no -elif test "$shlibpath_overrides_runpath" = yes || - test "$enable_shared" = no; then - # Fast installation is not necessary - enable_fast_install=needless -fi - - - - - - - - fi # test -n "$compiler" - - CC=$lt_save_CC - CFLAGS=$lt_save_CFLAGS - LDCXX=$LD - LD=$lt_save_LD - GCC=$lt_save_GCC - with_gnu_ld=$lt_save_with_gnu_ld - lt_cv_path_LDCXX=$lt_cv_path_LD - lt_cv_path_LD=$lt_save_path_LD - lt_cv_prog_gnu_ldcxx=$lt_cv_prog_gnu_ld - lt_cv_prog_gnu_ld=$lt_save_with_gnu_ld -fi # test "$_lt_caught_CXX_error" != yes - -ac_ext=c -ac_cpp='$CPP $CPPFLAGS' -ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_c_compiler_gnu - - -depcc="$CXX" am_compiler_list= - -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking dependency style of $depcc" >&5 -$as_echo_n "checking dependency style of $depcc... " >&6; } -if ${am_cv_CXX_dependencies_compiler_type+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then - # We make a subdir and do the tests there. Otherwise we can end up - # making bogus files that we don't know about and never remove. For - # instance it was reported that on HP-UX the gcc test will end up - # making a dummy file named 'D' -- because '-MD' means "put the output - # in D". - rm -rf conftest.dir - mkdir conftest.dir - # Copy depcomp to subdir because otherwise we won't find it if we're - # using a relative directory. - cp "$am_depcomp" conftest.dir - cd conftest.dir - # We will build objects and dependencies in a subdirectory because - # it helps to detect inapplicable dependency modes. For instance - # both Tru64's cc and ICC support -MD to output dependencies as a - # side effect of compilation, but ICC will put the dependencies in - # the current directory while Tru64 will put them in the object - # directory. - mkdir sub - - am_cv_CXX_dependencies_compiler_type=none - if test "$am_compiler_list" = ""; then - am_compiler_list=`sed -n 's/^#*\([a-zA-Z0-9]*\))$/\1/p' < ./depcomp` - fi - am__universal=false - case " $depcc " in #( - *\ -arch\ *\ -arch\ *) am__universal=true ;; - esac - - for depmode in $am_compiler_list; do - # Setup a source with many dependencies, because some compilers - # like to wrap large dependency lists on column 80 (with \), and - # we should not choose a depcomp mode which is confused by this. - # - # We need to recreate these files for each test, as the compiler may - # overwrite some of them when testing with obscure command lines. - # This happens at least with the AIX C compiler. - : > sub/conftest.c - for i in 1 2 3 4 5 6; do - echo '#include "conftst'$i'.h"' >> sub/conftest.c - # Using ": > sub/conftst$i.h" creates only sub/conftst1.h with - # Solaris 10 /bin/sh. - echo '/* dummy */' > sub/conftst$i.h - done - echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf - - # We check with '-c' and '-o' for the sake of the "dashmstdout" - # mode. It turns out that the SunPro C++ compiler does not properly - # handle '-M -o', and we need to detect this. Also, some Intel - # versions had trouble with output in subdirs. - am__obj=sub/conftest.${OBJEXT-o} - am__minus_obj="-o $am__obj" - case $depmode in - gcc) - # This depmode causes a compiler race in universal mode. - test "$am__universal" = false || continue - ;; - nosideeffect) - # After this tag, mechanisms are not by side-effect, so they'll - # only be used when explicitly requested. - if test "x$enable_dependency_tracking" = xyes; then - continue - else - break - fi - ;; - msvc7 | msvc7msys | msvisualcpp | msvcmsys) - # This compiler won't grok '-c -o', but also, the minuso test has - # not run yet. These depmodes are late enough in the game, and - # so weak that their functioning should not be impacted. - am__obj=conftest.${OBJEXT-o} - am__minus_obj= - ;; - none) break ;; - esac - if depmode=$depmode \ - source=sub/conftest.c object=$am__obj \ - depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \ - $SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \ - >/dev/null 2>conftest.err && - grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 && - grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 && - grep $am__obj sub/conftest.Po > /dev/null 2>&1 && - ${MAKE-make} -s -f confmf > /dev/null 2>&1; then - # icc doesn't choke on unknown options, it will just issue warnings - # or remarks (even with -Werror). So we grep stderr for any message - # that says an option was ignored or not supported. - # When given -MP, icc 7.0 and 7.1 complain thusly: - # icc: Command line warning: ignoring option '-M'; no argument required - # The diagnosis changed in icc 8.0: - # icc: Command line remark: option '-MP' not supported - if (grep 'ignoring option' conftest.err || - grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else - am_cv_CXX_dependencies_compiler_type=$depmode - break - fi - fi - done - - cd .. - rm -rf conftest.dir -else - am_cv_CXX_dependencies_compiler_type=none -fi - -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_CXX_dependencies_compiler_type" >&5 -$as_echo "$am_cv_CXX_dependencies_compiler_type" >&6; } -CXXDEPMODE=depmode=$am_cv_CXX_dependencies_compiler_type - - if - test "x$enable_dependency_tracking" != xno \ - && test "$am_cv_CXX_dependencies_compiler_type" = gcc3; then - am__fastdepCXX_TRUE= - am__fastdepCXX_FALSE='#' -else - am__fastdepCXX_TRUE='#' - am__fastdepCXX_FALSE= -fi - - -ac_ext=c -ac_cpp='$CPP $CPPFLAGS' -ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_c_compiler_gnu -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to run the C preprocessor" >&5 -$as_echo_n "checking how to run the C preprocessor... " >&6; } -# On Suns, sometimes $CPP names a directory. -if test -n "$CPP" && test -d "$CPP"; then - CPP= -fi -if test -z "$CPP"; then - if ${ac_cv_prog_CPP+:} false; then : - $as_echo_n "(cached) " >&6 -else - # Double quotes because CPP needs to be expanded - for CPP in "$CC -E" "$CC -E -traditional-cpp" "/lib/cpp" - do - ac_preproc_ok=false -for ac_c_preproc_warn_flag in '' yes -do - # Use a header file that comes with gcc, so configuring glibc - # with a fresh cross-compiler works. - # Prefer to if __STDC__ is defined, since - # exists even on freestanding compilers. - # On the NeXT, cc -E runs the code through the compiler's parser, - # not just through cpp. "Syntax error" is here to catch this case. - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#ifdef __STDC__ -# include -#else -# include -#endif - Syntax error -_ACEOF -if ac_fn_c_try_cpp "$LINENO"; then : - -else - # Broken: fails on valid input. -continue -fi -rm -f conftest.err conftest.i conftest.$ac_ext - - # OK, works on sane cases. Now check whether nonexistent headers - # can be detected and how. - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#include -_ACEOF -if ac_fn_c_try_cpp "$LINENO"; then : - # Broken: success on invalid input. -continue -else - # Passes both tests. -ac_preproc_ok=: -break -fi -rm -f conftest.err conftest.i conftest.$ac_ext - -done -# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. -rm -f conftest.i conftest.err conftest.$ac_ext -if $ac_preproc_ok; then : - break -fi - - done - ac_cv_prog_CPP=$CPP - -fi - CPP=$ac_cv_prog_CPP -else - ac_cv_prog_CPP=$CPP -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $CPP" >&5 -$as_echo "$CPP" >&6; } -ac_preproc_ok=false -for ac_c_preproc_warn_flag in '' yes -do - # Use a header file that comes with gcc, so configuring glibc - # with a fresh cross-compiler works. - # Prefer to if __STDC__ is defined, since - # exists even on freestanding compilers. - # On the NeXT, cc -E runs the code through the compiler's parser, - # not just through cpp. "Syntax error" is here to catch this case. - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#ifdef __STDC__ -# include -#else -# include -#endif - Syntax error -_ACEOF -if ac_fn_c_try_cpp "$LINENO"; then : - -else - # Broken: fails on valid input. -continue -fi -rm -f conftest.err conftest.i conftest.$ac_ext - - # OK, works on sane cases. Now check whether nonexistent headers - # can be detected and how. - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#include -_ACEOF -if ac_fn_c_try_cpp "$LINENO"; then : - # Broken: success on invalid input. -continue -else - # Passes both tests. -ac_preproc_ok=: -break -fi -rm -f conftest.err conftest.i conftest.$ac_ext - -done -# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. -rm -f conftest.i conftest.err conftest.$ac_ext -if $ac_preproc_ok; then : - -else - { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error $? "C preprocessor \"$CPP\" fails sanity check -See \`config.log' for more details" "$LINENO" 5; } -fi - -ac_ext=c -ac_cpp='$CPP $CPPFLAGS' -ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_c_compiler_gnu - - -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ln -s works" >&5 -$as_echo_n "checking whether ln -s works... " >&6; } -LN_S=$as_ln_s -if test "$LN_S" = "ln -s"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 -$as_echo "yes" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no, using $LN_S" >&5 -$as_echo "no, using $LN_S" >&6; } -fi - -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ${MAKE-make} sets \$(MAKE)" >&5 -$as_echo_n "checking whether ${MAKE-make} sets \$(MAKE)... " >&6; } -set x ${MAKE-make} -ac_make=`$as_echo "$2" | sed 's/+/p/g; s/[^a-zA-Z0-9_]/_/g'` -if eval \${ac_cv_prog_make_${ac_make}_set+:} false; then : - $as_echo_n "(cached) " >&6 -else - cat >conftest.make <<\_ACEOF -SHELL = /bin/sh -all: - @echo '@@@%%%=$(MAKE)=@@@%%%' -_ACEOF -# GNU make sometimes prints "make[1]: Entering ...", which would confuse us. -case `${MAKE-make} -f conftest.make 2>/dev/null` in - *@@@%%%=?*=@@@%%%*) - eval ac_cv_prog_make_${ac_make}_set=yes;; - *) - eval ac_cv_prog_make_${ac_make}_set=no;; -esac -rm -f conftest.make -fi -if eval test \$ac_cv_prog_make_${ac_make}_set = yes; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 -$as_echo "yes" >&6; } - SET_MAKE= -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } - SET_MAKE="MAKE=${MAKE-make}" -fi - - - - - - - - - - -if test "x$ac_cv_env_PKG_CONFIG_set" != "xset"; then - if test -n "$ac_tool_prefix"; then - # Extract the first word of "${ac_tool_prefix}pkg-config", so it can be a program name with args. -set dummy ${ac_tool_prefix}pkg-config; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_path_PKG_CONFIG+:} false; then : - $as_echo_n "(cached) " >&6 -else - case $PKG_CONFIG in - [\\/]* | ?:[\\/]*) - ac_cv_path_PKG_CONFIG="$PKG_CONFIG" # Let the user override the test with a path. - ;; - *) - as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_path_PKG_CONFIG="$as_dir/$ac_word$ac_exec_ext" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - - ;; -esac -fi -PKG_CONFIG=$ac_cv_path_PKG_CONFIG -if test -n "$PKG_CONFIG"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PKG_CONFIG" >&5 -$as_echo "$PKG_CONFIG" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - -fi -if test -z "$ac_cv_path_PKG_CONFIG"; then - ac_pt_PKG_CONFIG=$PKG_CONFIG - # Extract the first word of "pkg-config", so it can be a program name with args. -set dummy pkg-config; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_path_ac_pt_PKG_CONFIG+:} false; then : - $as_echo_n "(cached) " >&6 -else - case $ac_pt_PKG_CONFIG in - [\\/]* | ?:[\\/]*) - ac_cv_path_ac_pt_PKG_CONFIG="$ac_pt_PKG_CONFIG" # Let the user override the test with a path. - ;; - *) - as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_path_ac_pt_PKG_CONFIG="$as_dir/$ac_word$ac_exec_ext" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - - ;; -esac -fi -ac_pt_PKG_CONFIG=$ac_cv_path_ac_pt_PKG_CONFIG -if test -n "$ac_pt_PKG_CONFIG"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_pt_PKG_CONFIG" >&5 -$as_echo "$ac_pt_PKG_CONFIG" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - if test "x$ac_pt_PKG_CONFIG" = x; then - PKG_CONFIG="" - else - case $cross_compiling:$ac_tool_warned in -yes:) -{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 -$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} -ac_tool_warned=yes ;; -esac - PKG_CONFIG=$ac_pt_PKG_CONFIG - fi -else - PKG_CONFIG="$ac_cv_path_PKG_CONFIG" -fi - -fi -if test -n "$PKG_CONFIG"; then - _pkg_min_version=0.20 - { $as_echo "$as_me:${as_lineno-$LINENO}: checking pkg-config is at least version $_pkg_min_version" >&5 -$as_echo_n "checking pkg-config is at least version $_pkg_min_version... " >&6; } - if $PKG_CONFIG --atleast-pkgconfig-version $_pkg_min_version; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 -$as_echo "yes" >&6; } - else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } - PKG_CONFIG="" - fi -fi - -if test "x$request_python_bindings" != "xno"; then - - - - - - - if test -n "$PYTHON"; then - # If the user set $PYTHON, use it and don't search something else. - { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $PYTHON version is >= 2.7" >&5 -$as_echo_n "checking whether $PYTHON version is >= 2.7... " >&6; } - prog="import sys -# split strings by '.' and convert to numeric. Append some zeros -# because we need at least 4 digits for the hex conversion. -# map returns an iterator in Python 3.0 and a list in 2.x -minver = list(map(int, '2.7'.split('.'))) + [0, 0, 0] -minverhex = 0 -# xrange is not present in Python 3.0 and range returns an iterator -for i in list(range(0, 4)): minverhex = (minverhex << 8) + minver[i] -sys.exit(sys.hexversion < minverhex)" - if { echo "$as_me:$LINENO: $PYTHON -c "$prog"" >&5 - ($PYTHON -c "$prog") >&5 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; then : - { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 -$as_echo "yes" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } - as_fn_error $? "Python interpreter is too old" "$LINENO" 5 -fi - am_display_PYTHON=$PYTHON - else - # Otherwise, try each interpreter until we find one that satisfies - # VERSION. - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for a Python interpreter with version >= 2.7" >&5 -$as_echo_n "checking for a Python interpreter with version >= 2.7... " >&6; } -if ${am_cv_pathless_PYTHON+:} false; then : - $as_echo_n "(cached) " >&6 -else - - for am_cv_pathless_PYTHON in python python2 python3 python3.3 python3.2 python3.1 python3.0 python2.7 python2.6 python2.5 python2.4 python2.3 python2.2 python2.1 python2.0 none; do - test "$am_cv_pathless_PYTHON" = none && break - prog="import sys -# split strings by '.' and convert to numeric. Append some zeros -# because we need at least 4 digits for the hex conversion. -# map returns an iterator in Python 3.0 and a list in 2.x -minver = list(map(int, '2.7'.split('.'))) + [0, 0, 0] -minverhex = 0 -# xrange is not present in Python 3.0 and range returns an iterator -for i in list(range(0, 4)): minverhex = (minverhex << 8) + minver[i] -sys.exit(sys.hexversion < minverhex)" - if { echo "$as_me:$LINENO: $am_cv_pathless_PYTHON -c "$prog"" >&5 - ($am_cv_pathless_PYTHON -c "$prog") >&5 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; then : - break -fi - done -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_pathless_PYTHON" >&5 -$as_echo "$am_cv_pathless_PYTHON" >&6; } - # Set $PYTHON to the absolute path of $am_cv_pathless_PYTHON. - if test "$am_cv_pathless_PYTHON" = none; then - PYTHON=: - else - # Extract the first word of "$am_cv_pathless_PYTHON", so it can be a program name with args. -set dummy $am_cv_pathless_PYTHON; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_path_PYTHON+:} false; then : - $as_echo_n "(cached) " >&6 -else - case $PYTHON in - [\\/]* | ?:[\\/]*) - ac_cv_path_PYTHON="$PYTHON" # Let the user override the test with a path. - ;; - *) - as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_path_PYTHON="$as_dir/$ac_word$ac_exec_ext" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - - ;; -esac -fi -PYTHON=$ac_cv_path_PYTHON -if test -n "$PYTHON"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PYTHON" >&5 -$as_echo "$PYTHON" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - - fi - am_display_PYTHON=$am_cv_pathless_PYTHON - fi - - - if test "$PYTHON" = :; then - : - else - - - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $am_display_PYTHON version" >&5 -$as_echo_n "checking for $am_display_PYTHON version... " >&6; } -if ${am_cv_python_version+:} false; then : - $as_echo_n "(cached) " >&6 -else - am_cv_python_version=`$PYTHON -c "import sys; sys.stdout.write(sys.version[:3])"` -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_python_version" >&5 -$as_echo "$am_cv_python_version" >&6; } - PYTHON_VERSION=$am_cv_python_version - - - - PYTHON_PREFIX='${prefix}' - - PYTHON_EXEC_PREFIX='${exec_prefix}' - - - - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $am_display_PYTHON platform" >&5 -$as_echo_n "checking for $am_display_PYTHON platform... " >&6; } -if ${am_cv_python_platform+:} false; then : - $as_echo_n "(cached) " >&6 -else - am_cv_python_platform=`$PYTHON -c "import sys; sys.stdout.write(sys.platform)"` -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_python_platform" >&5 -$as_echo "$am_cv_python_platform" >&6; } - PYTHON_PLATFORM=$am_cv_python_platform - - - # Just factor out some code duplication. - am_python_setup_sysconfig="\ -import sys -# Prefer sysconfig over distutils.sysconfig, for better compatibility -# with python 3.x. See automake bug#10227. -try: - import sysconfig -except ImportError: - can_use_sysconfig = 0 -else: - can_use_sysconfig = 1 -# Can't use sysconfig in CPython 2.7, since it's broken in virtualenvs: -# -try: - from platform import python_implementation - if python_implementation() == 'CPython' and sys.version[:3] == '2.7': - can_use_sysconfig = 0 -except ImportError: - pass" - - - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $am_display_PYTHON script directory" >&5 -$as_echo_n "checking for $am_display_PYTHON script directory... " >&6; } -if ${am_cv_python_pythondir+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test "x$prefix" = xNONE - then - am_py_prefix=$ac_default_prefix - else - am_py_prefix=$prefix - fi - am_cv_python_pythondir=`$PYTHON -c " -$am_python_setup_sysconfig -if can_use_sysconfig: - sitedir = sysconfig.get_path('purelib', vars={'base':'$am_py_prefix'}) -else: - from distutils import sysconfig - sitedir = sysconfig.get_python_lib(0, 0, prefix='$am_py_prefix') -sys.stdout.write(sitedir)"` - case $am_cv_python_pythondir in - $am_py_prefix*) - am__strip_prefix=`echo "$am_py_prefix" | sed 's|.|.|g'` - am_cv_python_pythondir=`echo "$am_cv_python_pythondir" | sed "s,^$am__strip_prefix,$PYTHON_PREFIX,"` - ;; - *) - case $am_py_prefix in - /usr|/System*) ;; - *) - am_cv_python_pythondir=$PYTHON_PREFIX/lib/python$PYTHON_VERSION/site-packages - ;; - esac - ;; - esac - -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_python_pythondir" >&5 -$as_echo "$am_cv_python_pythondir" >&6; } - pythondir=$am_cv_python_pythondir - - - - pkgpythondir=\${pythondir}/$PACKAGE - - - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $am_display_PYTHON extension module directory" >&5 -$as_echo_n "checking for $am_display_PYTHON extension module directory... " >&6; } -if ${am_cv_python_pyexecdir+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test "x$exec_prefix" = xNONE - then - am_py_exec_prefix=$am_py_prefix - else - am_py_exec_prefix=$exec_prefix - fi - am_cv_python_pyexecdir=`$PYTHON -c " -$am_python_setup_sysconfig -if can_use_sysconfig: - sitedir = sysconfig.get_path('platlib', vars={'platbase':'$am_py_prefix'}) -else: - from distutils import sysconfig - sitedir = sysconfig.get_python_lib(1, 0, prefix='$am_py_prefix') -sys.stdout.write(sitedir)"` - case $am_cv_python_pyexecdir in - $am_py_exec_prefix*) - am__strip_prefix=`echo "$am_py_exec_prefix" | sed 's|.|.|g'` - am_cv_python_pyexecdir=`echo "$am_cv_python_pyexecdir" | sed "s,^$am__strip_prefix,$PYTHON_EXEC_PREFIX,"` - ;; - *) - case $am_py_exec_prefix in - /usr|/System*) ;; - *) - am_cv_python_pyexecdir=$PYTHON_EXEC_PREFIX/lib/python$PYTHON_VERSION/site-packages - ;; - esac - ;; - esac - -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_python_pyexecdir" >&5 -$as_echo "$am_cv_python_pyexecdir" >&6; } - pyexecdir=$am_cv_python_pyexecdir - - - - pkgpyexecdir=\${pyexecdir}/$PACKAGE - - - - fi - - - - # - # Allow the use of a (user set) custom python version - # - - - # Extract the first word of "python[$PYTHON_VERSION]", so it can be a program name with args. -set dummy python$PYTHON_VERSION; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_path_PYTHON+:} false; then : - $as_echo_n "(cached) " >&6 -else - case $PYTHON in - [\\/]* | ?:[\\/]*) - ac_cv_path_PYTHON="$PYTHON" # Let the user override the test with a path. - ;; - *) - as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_path_PYTHON="$as_dir/$ac_word$ac_exec_ext" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - - ;; -esac -fi -PYTHON=$ac_cv_path_PYTHON -if test -n "$PYTHON"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PYTHON" >&5 -$as_echo "$PYTHON" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - - if test -z "$PYTHON"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Cannot find python$PYTHON_VERSION in your system path" >&5 -$as_echo "$as_me: WARNING: Cannot find python$PYTHON_VERSION in your system path" >&2;} - PYTHON_VERSION="" - no_python_devel=yes - fi - -if test -z "$no_python_devel"; then : - - # - # Check for a version of Python >= 2.1.0 - # - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for a version of Python >= '2.1.0'" >&5 -$as_echo_n "checking for a version of Python >= '2.1.0'... " >&6; } - ac_supports_python_ver=`$PYTHON -c "import sys; \ - ver = sys.version.split ()[0]; \ - print (ver >= '2.1.0')"` - if test "$ac_supports_python_ver" != "True"; then - if test -z "$PYTHON_NOVERSIONCHECK"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } - { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: -This version of the AC_PYTHON_DEVEL macro -doesn't work properly with versions of Python before -2.1.0. You may need to re-run configure, setting the -variables PYTHON_CPPFLAGS, PYTHON_LDFLAGS, PYTHON_SITE_PKG, -PYTHON_EXTRA_LIBS and PYTHON_EXTRA_LDFLAGS by hand. -Moreover, to disable this check, set PYTHON_NOVERSIONCHECK -to something else than an empty string. -" >&5 -$as_echo "$as_me: WARNING: -This version of the AC_PYTHON_DEVEL macro -doesn't work properly with versions of Python before -2.1.0. You may need to re-run configure, setting the -variables PYTHON_CPPFLAGS, PYTHON_LDFLAGS, PYTHON_SITE_PKG, -PYTHON_EXTRA_LIBS and PYTHON_EXTRA_LDFLAGS by hand. -Moreover, to disable this check, set PYTHON_NOVERSIONCHECK -to something else than an empty string. -" >&2;} - no_python_devel=yes - else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: skip at user request" >&5 -$as_echo "skip at user request" >&6; } - fi - else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 -$as_echo "yes" >&6; } - fi - -fi # AS_IF - -if test -z "$no_python_devel"; then : - - # - # if the macro parameter ``version'' is set, honour it - # - if test -n ">= '2.7'"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for a version of Python >= '2.7'" >&5 -$as_echo_n "checking for a version of Python >= '2.7'... " >&6; } - ac_supports_python_ver=`$PYTHON -c "import sys; \ - ver = sys.version.split ()[0]; \ - print (ver >= '2.7')"` - if test "$ac_supports_python_ver" = "True"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 -$as_echo "yes" >&6; } - else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } - { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: this package requires Python >= '2.7'. -If you have it installed, but it isn't the default Python -interpreter in your system path, please pass the PYTHON_VERSION -variable to configure. See \`\`configure --help'' for reference. -" >&5 -$as_echo "$as_me: WARNING: this package requires Python >= '2.7'. -If you have it installed, but it isn't the default Python -interpreter in your system path, please pass the PYTHON_VERSION -variable to configure. See \`\`configure --help'' for reference. -" >&2;} - PYTHON_VERSION="" - no_python_devel=yes - fi - fi - -fi # AS_IF - -if test -z "$no_python_devel"; then : - - # - # Check if you have distutils, else fail - # - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for the distutils Python package" >&5 -$as_echo_n "checking for the distutils Python package... " >&6; } - ac_distutils_result=`$PYTHON -c "import distutils" 2>&1` - if test -z "$ac_distutils_result"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 -$as_echo "yes" >&6; } - else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } - { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cannot import Python module \"distutils\". -Please check your Python installation. The error was: -$ac_distutils_result" >&5 -$as_echo "$as_me: WARNING: cannot import Python module \"distutils\". -Please check your Python installation. The error was: -$ac_distutils_result" >&2;} - PYTHON_VERSION="" - no_python_devel=yes - fi - -fi # AS_IF - -if test -z "$no_python_devel"; then : - - # - # Check for Python include path - # - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for Python include path" >&5 -$as_echo_n "checking for Python include path... " >&6; } - if test -z "$PYTHON_CPPFLAGS"; then - python_path=`$PYTHON -c "import distutils.sysconfig; \ - print (distutils.sysconfig.get_python_inc ());"` - plat_python_path=`$PYTHON -c "import distutils.sysconfig; \ - print (distutils.sysconfig.get_python_inc (plat_specific=1));"` - if test -n "${python_path}"; then - if test "${plat_python_path}" != "${python_path}"; then - python_path="-I$python_path -I$plat_python_path" - else - python_path="-I$python_path" - fi - fi - PYTHON_CPPFLAGS=$python_path - fi - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PYTHON_CPPFLAGS" >&5 -$as_echo "$PYTHON_CPPFLAGS" >&6; } - - - # - # Check for Python library path - # - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for Python library path" >&5 -$as_echo_n "checking for Python library path... " >&6; } - if test -z "$PYTHON_LDFLAGS"; then - # (makes two attempts to ensure we've got a version number - # from the interpreter) - ac_python_version=`cat<>confdefs.h <<_ACEOF -#define HAVE_PYTHON "$ac_python_version" -_ACEOF - - - # First, the library directory: - ac_python_libdir=`cat<&5 -$as_echo "$as_me: WARNING: - Cannot determine location of your Python DSO. Please check it was installed with - dynamic libraries enabled, or try setting PYTHON_LDFLAGS by hand. - " >&2;} - no_python_devel=yes - fi - fi - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PYTHON_LDFLAGS" >&5 -$as_echo "$PYTHON_LDFLAGS" >&6; } - - -fi # AS_IF - -if test -z "$no_python_devel"; then : - - # - # Check for site packages - # - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for Python site-packages path" >&5 -$as_echo_n "checking for Python site-packages path... " >&6; } - if test -z "$PYTHON_SITE_PKG"; then - PYTHON_SITE_PKG=`$PYTHON -c "import distutils.sysconfig; \ - print (distutils.sysconfig.get_python_lib(0,0));"` - fi - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PYTHON_SITE_PKG" >&5 -$as_echo "$PYTHON_SITE_PKG" >&6; } - - - # - # libraries which must be linked in when embedding - # - { $as_echo "$as_me:${as_lineno-$LINENO}: checking python extra libraries" >&5 -$as_echo_n "checking python extra libraries... " >&6; } - if test -z "$PYTHON_EXTRA_LIBS"; then - PYTHON_EXTRA_LIBS=`$PYTHON -c "import distutils.sysconfig; \ - conf = distutils.sysconfig.get_config_var; \ - print (conf('LIBS'))"` - fi - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PYTHON_EXTRA_LIBS" >&5 -$as_echo "$PYTHON_EXTRA_LIBS" >&6; } - - - # - # linking flags needed when embedding - # - { $as_echo "$as_me:${as_lineno-$LINENO}: checking python extra linking flags" >&5 -$as_echo_n "checking python extra linking flags... " >&6; } - if test -z "$PYTHON_EXTRA_LDFLAGS"; then - PYTHON_EXTRA_LDFLAGS=`$PYTHON -c "import distutils.sysconfig; \ - conf = distutils.sysconfig.get_config_var; \ - print (conf('LINKFORSHARED'))"` - fi - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PYTHON_EXTRA_LDFLAGS" >&5 -$as_echo "$PYTHON_EXTRA_LDFLAGS" >&6; } - - - # - # final check to see if everything compiles alright - # - { $as_echo "$as_me:${as_lineno-$LINENO}: checking consistency of all components of python development environment" >&5 -$as_echo_n "checking consistency of all components of python development environment... " >&6; } - # save current global flags - ac_save_LIBS="$LIBS" - ac_save_CPPFLAGS="$CPPFLAGS" - LIBS="$ac_save_LIBS $PYTHON_LDFLAGS $PYTHON_EXTRA_LDFLAGS $PYTHON_EXTRA_LIBS" - CPPFLAGS="$ac_save_CPPFLAGS $PYTHON_CPPFLAGS" - ac_ext=c -ac_cpp='$CPP $CPPFLAGS' -ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_c_compiler_gnu - - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - - #include -int -main () -{ -Py_Initialize(); - ; - return 0; -} - -_ACEOF -if ac_fn_c_try_link "$LINENO"; then : - pythonexists=yes -else - pythonexists=no -fi -rm -f core conftest.err conftest.$ac_objext \ - conftest$ac_exeext conftest.$ac_ext - ac_ext=c -ac_cpp='$CPP $CPPFLAGS' -ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_c_compiler_gnu - - # turn back to default flags - CPPFLAGS="$ac_save_CPPFLAGS" - LIBS="$ac_save_LIBS" - - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $pythonexists" >&5 -$as_echo "$pythonexists" >&6; } - - if test ! "x$pythonexists" = "xyes"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: - Could not link test program to Python. Maybe the main Python library has been - installed in some non-standard library path. If so, pass it to configure, - via the LDFLAGS environment variable. - Example: ./configure LDFLAGS=\"-L/usr/non-standard-path/python/lib\" - ============================================================================ - ERROR! - You probably have to install the development version of the Python package - for your distribution. The exact name of this package varies among them. - ============================================================================ - " >&5 -$as_echo "$as_me: WARNING: - Could not link test program to Python. Maybe the main Python library has been - installed in some non-standard library path. If so, pass it to configure, - via the LDFLAGS environment variable. - Example: ./configure LDFLAGS=\"-L/usr/non-standard-path/python/lib\" - ============================================================================ - ERROR! - You probably have to install the development version of the Python package - for your distribution. The exact name of this package varies among them. - ============================================================================ - " >&2;} - PYTHON_VERSION="" - no_python_devel=yes - fi - - # - # all done! - # - -fi # AS_IF - -if test -z "$no_python_devel"; then : - have_python_dev=yes -else - have_python_dev=no -fi - - -fi - -if test "x${cython_path}" = "x"; then - for ac_prog in cython.py cython -do - # Extract the first word of "$ac_prog", so it can be a program name with args. -set dummy $ac_prog; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_CYTHON+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -n "$CYTHON"; then - ac_cv_prog_CYTHON="$CYTHON" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_CYTHON="$ac_prog" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -CYTHON=$ac_cv_prog_CYTHON -if test -n "$CYTHON"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CYTHON" >&5 -$as_echo "$CYTHON" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - - test -n "$CYTHON" && break -done - -else - CYTHON=${cython_path} - -fi - -# -# If we're running GCC or clang define _U_ to be "__attribute__((unused))" -# so we can use _U_ to flag unused function parameters and not get warnings -# about them. Otherwise, define _U_ to be an empty string so that _U_ used -# to flag an unused function parameters will compile with other compilers. -# -# XXX - similar hints for other compilers? -# -if test "x$GCC" = "xyes" -o "x$CC" = "xclang" ; then - -$as_echo "#define _U_ __attribute__((unused))" >>confdefs.h - -else - -$as_echo "#define _U_ /**/" >>confdefs.h - -fi - - ax_cxx_compile_cxx11_required=falsednl - ac_ext=cpp -ac_cpp='$CXXCPP $CPPFLAGS' -ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_cxx_compiler_gnu - ac_success=no - { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CXX supports C++11 features by default" >&5 -$as_echo_n "checking whether $CXX supports C++11 features by default... " >&6; } -if ${ax_cv_cxx_compile_cxx11+:} false; then : - $as_echo_n "(cached) " >&6 -else - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - - template - struct check - { - static_assert(sizeof(int) <= sizeof(T), "not big enough"); - }; - - typedef check> right_angle_brackets; - - int a; - decltype(a) b; - - typedef check check_type; - check_type c; - check_type&& cr = static_cast(c); - - auto d = a; - -_ACEOF -if ac_fn_cxx_try_compile "$LINENO"; then : - ax_cv_cxx_compile_cxx11=yes -else - ax_cv_cxx_compile_cxx11=no -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_cxx_compile_cxx11" >&5 -$as_echo "$ax_cv_cxx_compile_cxx11" >&6; } - if test x$ax_cv_cxx_compile_cxx11 = xyes; then - ac_success=yes - fi - - - - if test x$ac_success = xno; then - for switch in -std=c++11 -std=c++0x; do - cachevar=`$as_echo "ax_cv_cxx_compile_cxx11_$switch" | $as_tr_sh` - { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CXX supports C++11 features with $switch" >&5 -$as_echo_n "checking whether $CXX supports C++11 features with $switch... " >&6; } -if eval \${$cachevar+:} false; then : - $as_echo_n "(cached) " >&6 -else - ac_save_CXXFLAGS="$CXXFLAGS" - CXXFLAGS="$CXXFLAGS $switch" - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - - template - struct check - { - static_assert(sizeof(int) <= sizeof(T), "not big enough"); - }; - - typedef check> right_angle_brackets; - - int a; - decltype(a) b; - - typedef check check_type; - check_type c; - check_type&& cr = static_cast(c); - - auto d = a; - -_ACEOF -if ac_fn_cxx_try_compile "$LINENO"; then : - eval $cachevar=yes -else - eval $cachevar=no -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext - CXXFLAGS="$ac_save_CXXFLAGS" -fi -eval ac_res=\$$cachevar - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 -$as_echo "$ac_res" >&6; } - if eval test x\$$cachevar = xyes; then - CXXFLAGS="$CXXFLAGS $switch" - ac_success=yes - break - fi - done - fi - ac_ext=c -ac_cpp='$CPP $CPPFLAGS' -ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_c_compiler_gnu - - if test x$ax_cxx_compile_cxx11_required = xtrue; then - if test x$ac_success = xno; then - as_fn_error $? "*** A compiler with support for C++11 language features is required." "$LINENO" 5 - fi - else - if test x$ac_success = xno; then - HAVE_CXX11=0 - { $as_echo "$as_me:${as_lineno-$LINENO}: No compiler with C++11 support was found" >&5 -$as_echo "$as_me: No compiler with C++11 support was found" >&6;} - else - HAVE_CXX11=1 - -$as_echo "#define HAVE_CXX11 1" >>confdefs.h - - fi - - - fi - - -ac_ext=cpp -ac_cpp='$CXXCPP $CPPFLAGS' -ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_cxx_compiler_gnu - - -# Check that std::chrono::steady_clock is available. In particular, -# gcc 4.6 does not have one, but has monotonic_clock which is the old -# name existed in the pre-standard draft. If steady_clock is not -# available, don't define HAVE_STEADY_CLOCK and replace steady_clock -# with monotonic_clock. -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether std::chrono::steady_clock is available" >&5 -$as_echo_n "checking whether std::chrono::steady_clock is available... " >&6; } -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -#include - -int -main () -{ - -auto tp = std::chrono::steady_clock::now(); - - ; - return 0; -} -_ACEOF -if ac_fn_cxx_try_compile "$LINENO"; then : - -$as_echo "#define HAVE_STEADY_CLOCK 1" >>confdefs.h - - { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 -$as_echo "yes" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext - -# Check that std::future is available. -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether std::future is available" >&5 -$as_echo_n "checking whether std::future is available... " >&6; } -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -#include -#include - -int -main () -{ - -std::vector> v; - - ; - return 0; -} -_ACEOF -if ac_fn_cxx_try_compile "$LINENO"; then : - -$as_echo "#define HAVE_STD_FUTURE 1" >>confdefs.h - - have_std_future=yes - { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 -$as_echo "yes" >&6; } -else - have_std_future=no - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext - -# Check that std::map::emplace is available for g++-4.7. -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether std::map::emplace is available" >&5 -$as_echo_n "checking whether std::map::emplace is available... " >&6; } -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -#include - -int -main () -{ - -std::map().emplace(1, 2); - - ; - return 0; -} -_ACEOF -if ac_fn_cxx_try_compile "$LINENO"; then : - -$as_echo "#define HAVE_STD_MAP_EMPLACE 1" >>confdefs.h - - have_std_map_emplace=yes - { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 -$as_echo "yes" >&6; } -else - have_std_map_emplace=no - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext - -ac_ext=c -ac_cpp='$CPP $CPPFLAGS' -ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_c_compiler_gnu - - -# Checks for libraries. - -# Additional libraries required for tests. -TESTLDADD= - -# Additional libraries required for programs under src directory. -APPLDFLAGS= - -LIBS_OLD=$LIBS -# Search for dlsym function, which is used in tests. Linux needs -ldl, -# but netbsd does not need it. -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing dlsym" >&5 -$as_echo_n "checking for library containing dlsym... " >&6; } -if ${ac_cv_search_dlsym+:} false; then : - $as_echo_n "(cached) " >&6 -else - ac_func_search_save_LIBS=$LIBS -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -/* Override any GCC internal prototype to avoid an error. - Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -#ifdef __cplusplus -extern "C" -#endif -char dlsym (); -int -main () -{ -return dlsym (); - ; - return 0; -} -_ACEOF -for ac_lib in '' dl; do - if test -z "$ac_lib"; then - ac_res="none required" - else - ac_res=-l$ac_lib - LIBS="-l$ac_lib $ac_func_search_save_LIBS" - fi - if ac_fn_c_try_link "$LINENO"; then : - ac_cv_search_dlsym=$ac_res -fi -rm -f core conftest.err conftest.$ac_objext \ - conftest$ac_exeext - if ${ac_cv_search_dlsym+:} false; then : - break -fi -done -if ${ac_cv_search_dlsym+:} false; then : - -else - ac_cv_search_dlsym=no -fi -rm conftest.$ac_ext -LIBS=$ac_func_search_save_LIBS -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_dlsym" >&5 -$as_echo "$ac_cv_search_dlsym" >&6; } -ac_res=$ac_cv_search_dlsym -if test "$ac_res" != no; then : - test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" - -fi - -TESTLDADD="$LIBS $TESTLDADD" -LIBS=$LIBS_OLD - -LIBS_OLD=$LIBS -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing clock_gettime" >&5 -$as_echo_n "checking for library containing clock_gettime... " >&6; } -if ${ac_cv_search_clock_gettime+:} false; then : - $as_echo_n "(cached) " >&6 -else - ac_func_search_save_LIBS=$LIBS -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -/* Override any GCC internal prototype to avoid an error. - Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -#ifdef __cplusplus -extern "C" -#endif -char clock_gettime (); -int -main () -{ -return clock_gettime (); - ; - return 0; -} -_ACEOF -for ac_lib in '' rt; do - if test -z "$ac_lib"; then - ac_res="none required" - else - ac_res=-l$ac_lib - LIBS="-l$ac_lib $ac_func_search_save_LIBS" - fi - if ac_fn_c_try_link "$LINENO"; then : - ac_cv_search_clock_gettime=$ac_res -fi -rm -f core conftest.err conftest.$ac_objext \ - conftest$ac_exeext - if ${ac_cv_search_clock_gettime+:} false; then : - break -fi -done -if ${ac_cv_search_clock_gettime+:} false; then : - -else - ac_cv_search_clock_gettime=no -fi -rm conftest.$ac_ext -LIBS=$ac_func_search_save_LIBS -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_clock_gettime" >&5 -$as_echo "$ac_cv_search_clock_gettime" >&6; } -ac_res=$ac_cv_search_clock_gettime -if test "$ac_res" != no; then : - test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" - -$as_echo "#define HAVE_CLOCK_GETTIME 1" >>confdefs.h - -fi - -APPLDFLAGS="$LIBS $APPLDFLAGS" -LIBS=$LIBS_OLD - -case "$host" in - *android*) - android_build=yes - # android does not need -pthread, but needs followng 3 libs for C++ - APPLDFLAGS="$APPLDFLAGS -lstdc++ -latomic -lsupc++" - ;; - *) - PTHREAD_LDFLAGS="-pthread" - APPLDFLAGS="$APPLDFLAGS $PTHREAD_LDFLAGS" - ;; -esac - -# zlib -if test "x$android_build" = "xyes"; then - # Use zlib provided by NDK - APPLDFLAGS="-lz $APPLDFLAGS" - have_zlib=yes -else - -pkg_failed=no -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for ZLIB" >&5 -$as_echo_n "checking for ZLIB... " >&6; } - -if test -n "$ZLIB_CFLAGS"; then - pkg_cv_ZLIB_CFLAGS="$ZLIB_CFLAGS" - elif test -n "$PKG_CONFIG"; then - if test -n "$PKG_CONFIG" && \ - { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"zlib >= 1.2.3\""; } >&5 - ($PKG_CONFIG --exists --print-errors "zlib >= 1.2.3") 2>&5 - ac_status=$? - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; }; then - pkg_cv_ZLIB_CFLAGS=`$PKG_CONFIG --cflags "zlib >= 1.2.3" 2>/dev/null` - test "x$?" != "x0" && pkg_failed=yes -else - pkg_failed=yes -fi - else - pkg_failed=untried -fi -if test -n "$ZLIB_LIBS"; then - pkg_cv_ZLIB_LIBS="$ZLIB_LIBS" - elif test -n "$PKG_CONFIG"; then - if test -n "$PKG_CONFIG" && \ - { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"zlib >= 1.2.3\""; } >&5 - ($PKG_CONFIG --exists --print-errors "zlib >= 1.2.3") 2>&5 - ac_status=$? - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; }; then - pkg_cv_ZLIB_LIBS=`$PKG_CONFIG --libs "zlib >= 1.2.3" 2>/dev/null` - test "x$?" != "x0" && pkg_failed=yes -else - pkg_failed=yes -fi - else - pkg_failed=untried -fi - - - -if test $pkg_failed = yes; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } - -if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then - _pkg_short_errors_supported=yes -else - _pkg_short_errors_supported=no -fi - if test $_pkg_short_errors_supported = yes; then - ZLIB_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "zlib >= 1.2.3" 2>&1` - else - ZLIB_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "zlib >= 1.2.3" 2>&1` - fi - # Put the nasty error message in config.log where it belongs - echo "$ZLIB_PKG_ERRORS" >&5 - - have_zlib=no -elif test $pkg_failed = untried; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } - have_zlib=no -else - ZLIB_CFLAGS=$pkg_cv_ZLIB_CFLAGS - ZLIB_LIBS=$pkg_cv_ZLIB_LIBS - { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 -$as_echo "yes" >&6; } - have_zlib=yes -fi - - if test "x${have_zlib}" = "xno"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: $ZLIB_PKG_ERRORS" >&5 -$as_echo "$as_me: $ZLIB_PKG_ERRORS" >&6;} - fi -fi - -# cunit - -pkg_failed=no -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for CUNIT" >&5 -$as_echo_n "checking for CUNIT... " >&6; } - -if test -n "$CUNIT_CFLAGS"; then - pkg_cv_CUNIT_CFLAGS="$CUNIT_CFLAGS" - elif test -n "$PKG_CONFIG"; then - if test -n "$PKG_CONFIG" && \ - { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"cunit >= 2.1\""; } >&5 - ($PKG_CONFIG --exists --print-errors "cunit >= 2.1") 2>&5 - ac_status=$? - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; }; then - pkg_cv_CUNIT_CFLAGS=`$PKG_CONFIG --cflags "cunit >= 2.1" 2>/dev/null` - test "x$?" != "x0" && pkg_failed=yes -else - pkg_failed=yes -fi - else - pkg_failed=untried -fi -if test -n "$CUNIT_LIBS"; then - pkg_cv_CUNIT_LIBS="$CUNIT_LIBS" - elif test -n "$PKG_CONFIG"; then - if test -n "$PKG_CONFIG" && \ - { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"cunit >= 2.1\""; } >&5 - ($PKG_CONFIG --exists --print-errors "cunit >= 2.1") 2>&5 - ac_status=$? - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; }; then - pkg_cv_CUNIT_LIBS=`$PKG_CONFIG --libs "cunit >= 2.1" 2>/dev/null` - test "x$?" != "x0" && pkg_failed=yes -else - pkg_failed=yes -fi - else - pkg_failed=untried -fi - - - -if test $pkg_failed = yes; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } - -if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then - _pkg_short_errors_supported=yes -else - _pkg_short_errors_supported=no -fi - if test $_pkg_short_errors_supported = yes; then - CUNIT_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "cunit >= 2.1" 2>&1` - else - CUNIT_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "cunit >= 2.1" 2>&1` - fi - # Put the nasty error message in config.log where it belongs - echo "$CUNIT_PKG_ERRORS" >&5 - - have_cunit=no -elif test $pkg_failed = untried; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } - have_cunit=no -else - CUNIT_CFLAGS=$pkg_cv_CUNIT_CFLAGS - CUNIT_LIBS=$pkg_cv_CUNIT_LIBS - { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 -$as_echo "yes" >&6; } - have_cunit=yes -fi -# If pkg-config does not find cunit, check it using AC_CHECK_LIB. We -# do this because Debian (Ubuntu) lacks pkg-config file for cunit. -if test "x${have_cunit}" = "xno"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: ${CUNIT_PKG_ERRORS}" >&5 -$as_echo "$as_me: WARNING: ${CUNIT_PKG_ERRORS}" >&2;} - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for CU_initialize_registry in -lcunit" >&5 -$as_echo_n "checking for CU_initialize_registry in -lcunit... " >&6; } -if ${ac_cv_lib_cunit_CU_initialize_registry+:} false; then : - $as_echo_n "(cached) " >&6 -else - ac_check_lib_save_LIBS=$LIBS -LIBS="-lcunit $LIBS" -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -/* Override any GCC internal prototype to avoid an error. - Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -#ifdef __cplusplus -extern "C" -#endif -char CU_initialize_registry (); -int -main () -{ -return CU_initialize_registry (); - ; - return 0; -} -_ACEOF -if ac_fn_c_try_link "$LINENO"; then : - ac_cv_lib_cunit_CU_initialize_registry=yes -else - ac_cv_lib_cunit_CU_initialize_registry=no -fi -rm -f core conftest.err conftest.$ac_objext \ - conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_cunit_CU_initialize_registry" >&5 -$as_echo "$ac_cv_lib_cunit_CU_initialize_registry" >&6; } -if test "x$ac_cv_lib_cunit_CU_initialize_registry" = xyes; then : - have_cunit=yes -else - have_cunit=no -fi - - if test "x${have_cunit}" = "xyes"; then - CUNIT_LIBS="-lcunit" - CUNIT_CFLAGS="" - - - fi -fi -if test "x${have_cunit}" = "xyes"; then - # cunit in Mac OS X requires ncurses. Note that in Mac OS X, test - # program can be built without -lncurses, but it emits runtime - # error. - case "${build}" in - *-apple-darwin*) - CUNIT_LIBS="$CUNIT_LIBS -lncurses" - - ;; - esac -fi - - if test "x${have_cunit}" = "xyes" ; then - HAVE_CUNIT_TRUE= - HAVE_CUNIT_FALSE='#' -else - HAVE_CUNIT_TRUE='#' - HAVE_CUNIT_FALSE= -fi - - -# libev (for src) -# libev does not have pkg-config file. Check it in an old way. -LIBS_OLD=$LIBS -# android requires -lm for floor -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for ev_time in -lev" >&5 -$as_echo_n "checking for ev_time in -lev... " >&6; } -if ${ac_cv_lib_ev_ev_time+:} false; then : - $as_echo_n "(cached) " >&6 -else - ac_check_lib_save_LIBS=$LIBS -LIBS="-lev -lm $LIBS" -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -/* Override any GCC internal prototype to avoid an error. - Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -#ifdef __cplusplus -extern "C" -#endif -char ev_time (); -int -main () -{ -return ev_time (); - ; - return 0; -} -_ACEOF -if ac_fn_c_try_link "$LINENO"; then : - ac_cv_lib_ev_ev_time=yes -else - ac_cv_lib_ev_ev_time=no -fi -rm -f core conftest.err conftest.$ac_objext \ - conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_ev_ev_time" >&5 -$as_echo "$ac_cv_lib_ev_ev_time" >&6; } -if test "x$ac_cv_lib_ev_ev_time" = xyes; then : - have_libev=yes -else - have_libev=no -fi - -if test "x${have_libev}" = "xyes"; then - ac_fn_c_check_header_mongrel "$LINENO" "ev.h" "ac_cv_header_ev_h" "$ac_includes_default" -if test "x$ac_cv_header_ev_h" = xyes; then : - have_libev=yes -else - have_libev=no -fi - - - if test "x${have_libev}" = "xyes"; then - LIBEV_LIBS=-lev - LIBEV_CFLAGS= - - - fi -fi -LIBS=$LIBS_OLD - -# openssl (for src) - -pkg_failed=no -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for OPENSSL" >&5 -$as_echo_n "checking for OPENSSL... " >&6; } - -if test -n "$OPENSSL_CFLAGS"; then - pkg_cv_OPENSSL_CFLAGS="$OPENSSL_CFLAGS" - elif test -n "$PKG_CONFIG"; then - if test -n "$PKG_CONFIG" && \ - { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"openssl >= 1.0.1\""; } >&5 - ($PKG_CONFIG --exists --print-errors "openssl >= 1.0.1") 2>&5 - ac_status=$? - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; }; then - pkg_cv_OPENSSL_CFLAGS=`$PKG_CONFIG --cflags "openssl >= 1.0.1" 2>/dev/null` - test "x$?" != "x0" && pkg_failed=yes -else - pkg_failed=yes -fi - else - pkg_failed=untried -fi -if test -n "$OPENSSL_LIBS"; then - pkg_cv_OPENSSL_LIBS="$OPENSSL_LIBS" - elif test -n "$PKG_CONFIG"; then - if test -n "$PKG_CONFIG" && \ - { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"openssl >= 1.0.1\""; } >&5 - ($PKG_CONFIG --exists --print-errors "openssl >= 1.0.1") 2>&5 - ac_status=$? - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; }; then - pkg_cv_OPENSSL_LIBS=`$PKG_CONFIG --libs "openssl >= 1.0.1" 2>/dev/null` - test "x$?" != "x0" && pkg_failed=yes -else - pkg_failed=yes -fi - else - pkg_failed=untried -fi - - - -if test $pkg_failed = yes; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } - -if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then - _pkg_short_errors_supported=yes -else - _pkg_short_errors_supported=no -fi - if test $_pkg_short_errors_supported = yes; then - OPENSSL_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "openssl >= 1.0.1" 2>&1` - else - OPENSSL_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "openssl >= 1.0.1" 2>&1` - fi - # Put the nasty error message in config.log where it belongs - echo "$OPENSSL_PKG_ERRORS" >&5 - - have_openssl=no -elif test $pkg_failed = untried; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } - have_openssl=no -else - OPENSSL_CFLAGS=$pkg_cv_OPENSSL_CFLAGS - OPENSSL_LIBS=$pkg_cv_OPENSSL_LIBS - { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 -$as_echo "yes" >&6; } - have_openssl=yes -fi -if test "x${have_openssl}" = "xno"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: $OPENSSL_PKG_ERRORS" >&5 -$as_echo "$as_me: $OPENSSL_PKG_ERRORS" >&6;} -fi - -# libevent_openssl (for examples) -# 2.0.8 is required because we use evconnlistener_set_error_cb() - -pkg_failed=no -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for LIBEVENT_OPENSSL" >&5 -$as_echo_n "checking for LIBEVENT_OPENSSL... " >&6; } - -if test -n "$LIBEVENT_OPENSSL_CFLAGS"; then - pkg_cv_LIBEVENT_OPENSSL_CFLAGS="$LIBEVENT_OPENSSL_CFLAGS" - elif test -n "$PKG_CONFIG"; then - if test -n "$PKG_CONFIG" && \ - { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libevent_openssl >= 2.0.8\""; } >&5 - ($PKG_CONFIG --exists --print-errors "libevent_openssl >= 2.0.8") 2>&5 - ac_status=$? - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; }; then - pkg_cv_LIBEVENT_OPENSSL_CFLAGS=`$PKG_CONFIG --cflags "libevent_openssl >= 2.0.8" 2>/dev/null` - test "x$?" != "x0" && pkg_failed=yes -else - pkg_failed=yes -fi - else - pkg_failed=untried -fi -if test -n "$LIBEVENT_OPENSSL_LIBS"; then - pkg_cv_LIBEVENT_OPENSSL_LIBS="$LIBEVENT_OPENSSL_LIBS" - elif test -n "$PKG_CONFIG"; then - if test -n "$PKG_CONFIG" && \ - { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libevent_openssl >= 2.0.8\""; } >&5 - ($PKG_CONFIG --exists --print-errors "libevent_openssl >= 2.0.8") 2>&5 - ac_status=$? - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; }; then - pkg_cv_LIBEVENT_OPENSSL_LIBS=`$PKG_CONFIG --libs "libevent_openssl >= 2.0.8" 2>/dev/null` - test "x$?" != "x0" && pkg_failed=yes -else - pkg_failed=yes -fi - else - pkg_failed=untried -fi - - - -if test $pkg_failed = yes; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } - -if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then - _pkg_short_errors_supported=yes -else - _pkg_short_errors_supported=no -fi - if test $_pkg_short_errors_supported = yes; then - LIBEVENT_OPENSSL_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "libevent_openssl >= 2.0.8" 2>&1` - else - LIBEVENT_OPENSSL_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "libevent_openssl >= 2.0.8" 2>&1` - fi - # Put the nasty error message in config.log where it belongs - echo "$LIBEVENT_OPENSSL_PKG_ERRORS" >&5 - - have_libevent_openssl=no -elif test $pkg_failed = untried; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } - have_libevent_openssl=no -else - LIBEVENT_OPENSSL_CFLAGS=$pkg_cv_LIBEVENT_OPENSSL_CFLAGS - LIBEVENT_OPENSSL_LIBS=$pkg_cv_LIBEVENT_OPENSSL_LIBS - { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 -$as_echo "yes" >&6; } - have_libevent_openssl=yes -fi -if test "x${have_libevent_openssl}" = "xno"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: $LIBEVENT_OPENSSL_PKG_ERRORS" >&5 -$as_echo "$as_me: $LIBEVENT_OPENSSL_PKG_ERRORS" >&6;} -fi - -# jansson (for src/nghttp, src/deflatehd and src/inflatehd) - -pkg_failed=no -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for JANSSON" >&5 -$as_echo_n "checking for JANSSON... " >&6; } - -if test -n "$JANSSON_CFLAGS"; then - pkg_cv_JANSSON_CFLAGS="$JANSSON_CFLAGS" - elif test -n "$PKG_CONFIG"; then - if test -n "$PKG_CONFIG" && \ - { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"jansson >= 2.5\""; } >&5 - ($PKG_CONFIG --exists --print-errors "jansson >= 2.5") 2>&5 - ac_status=$? - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; }; then - pkg_cv_JANSSON_CFLAGS=`$PKG_CONFIG --cflags "jansson >= 2.5" 2>/dev/null` - test "x$?" != "x0" && pkg_failed=yes -else - pkg_failed=yes -fi - else - pkg_failed=untried -fi -if test -n "$JANSSON_LIBS"; then - pkg_cv_JANSSON_LIBS="$JANSSON_LIBS" - elif test -n "$PKG_CONFIG"; then - if test -n "$PKG_CONFIG" && \ - { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"jansson >= 2.5\""; } >&5 - ($PKG_CONFIG --exists --print-errors "jansson >= 2.5") 2>&5 - ac_status=$? - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; }; then - pkg_cv_JANSSON_LIBS=`$PKG_CONFIG --libs "jansson >= 2.5" 2>/dev/null` - test "x$?" != "x0" && pkg_failed=yes -else - pkg_failed=yes -fi - else - pkg_failed=untried -fi - - - -if test $pkg_failed = yes; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } - -if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then - _pkg_short_errors_supported=yes -else - _pkg_short_errors_supported=no -fi - if test $_pkg_short_errors_supported = yes; then - JANSSON_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "jansson >= 2.5" 2>&1` - else - JANSSON_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "jansson >= 2.5" 2>&1` - fi - # Put the nasty error message in config.log where it belongs - echo "$JANSSON_PKG_ERRORS" >&5 - - have_jansson=no -elif test $pkg_failed = untried; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } - have_jansson=no -else - JANSSON_CFLAGS=$pkg_cv_JANSSON_CFLAGS - JANSSON_LIBS=$pkg_cv_JANSSON_LIBS - { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 -$as_echo "yes" >&6; } - have_jansson=yes -fi -if test "x${have_jansson}" == "xyes"; then - -$as_echo "#define HAVE_JANSSON 1" >>confdefs.h - -else - { $as_echo "$as_me:${as_lineno-$LINENO}: $JANSSON_PKG_ERRORS" >&5 -$as_echo "$as_me: $JANSSON_PKG_ERRORS" >&6;} -fi - -# libxml2 (for src/nghttp) -have_libxml2=no -if test "x${request_libxml2}" != "xno"; then - - -# Check whether --with-xml-prefix was given. -if test "${with_xml_prefix+set}" = set; then : - withval=$with_xml_prefix; xml_config_prefix="$withval" -else - xml_config_prefix="" -fi - - -# Check whether --with-xml-exec-prefix was given. -if test "${with_xml_exec_prefix+set}" = set; then : - withval=$with_xml_exec_prefix; xml_config_exec_prefix="$withval" -else - xml_config_exec_prefix="" -fi - -# Check whether --enable-xmltest was given. -if test "${enable_xmltest+set}" = set; then : - enableval=$enable_xmltest; -else - enable_xmltest=yes -fi - - - if test x$xml_config_exec_prefix != x ; then - xml_config_args="$xml_config_args" - if test x${XML2_CONFIG+set} != xset ; then - XML2_CONFIG=$xml_config_exec_prefix/bin/xml2-config - fi - fi - if test x$xml_config_prefix != x ; then - xml_config_args="$xml_config_args --prefix=$xml_config_prefix" - if test x${XML2_CONFIG+set} != xset ; then - XML2_CONFIG=$xml_config_prefix/bin/xml2-config - fi - fi - - # Extract the first word of "xml2-config", so it can be a program name with args. -set dummy xml2-config; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_path_XML2_CONFIG+:} false; then : - $as_echo_n "(cached) " >&6 -else - case $XML2_CONFIG in - [\\/]* | ?:[\\/]*) - ac_cv_path_XML2_CONFIG="$XML2_CONFIG" # Let the user override the test with a path. - ;; - *) - as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_path_XML2_CONFIG="$as_dir/$ac_word$ac_exec_ext" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - - test -z "$ac_cv_path_XML2_CONFIG" && ac_cv_path_XML2_CONFIG="no" - ;; -esac -fi -XML2_CONFIG=$ac_cv_path_XML2_CONFIG -if test -n "$XML2_CONFIG"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $XML2_CONFIG" >&5 -$as_echo "$XML2_CONFIG" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - - min_xml_version=2.7.7 - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for libxml - version >= $min_xml_version" >&5 -$as_echo_n "checking for libxml - version >= $min_xml_version... " >&6; } - no_xml="" - if test "$XML2_CONFIG" = "no" ; then - no_xml=yes - else - XML_CPPFLAGS=`$XML2_CONFIG $xml_config_args --cflags` - XML_LIBS=`$XML2_CONFIG $xml_config_args --libs` - xml_config_major_version=`$XML2_CONFIG $xml_config_args --version | \ - sed 's/\([0-9]*\).\([0-9]*\).\([0-9]*\)/\1/'` - xml_config_minor_version=`$XML2_CONFIG $xml_config_args --version | \ - sed 's/\([0-9]*\).\([0-9]*\).\([0-9]*\)/\2/'` - xml_config_micro_version=`$XML2_CONFIG $xml_config_args --version | \ - sed 's/\([0-9]*\).\([0-9]*\).\([0-9]*\)/\3/'` - if test "x$enable_xmltest" = "xyes" ; then - ac_save_CPPFLAGS="$CPPFLAGS" - ac_save_LIBS="$LIBS" - CPPFLAGS="$CPPFLAGS $XML_CPPFLAGS" - LIBS="$XML_LIBS $LIBS" - rm -f conf.xmltest - if test "$cross_compiling" = yes; then : - echo $ac_n "cross compiling; assumed OK... $ac_c" -else - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -#include -#include -#include -#include - -int -main() -{ - int xml_major_version, xml_minor_version, xml_micro_version; - int major, minor, micro; - char *tmp_version; - - system("touch conf.xmltest"); - - /* Capture xml2-config output via autoconf/configure variables */ - /* HP/UX 9 (%@#!) writes to sscanf strings */ - tmp_version = (char *)strdup("$min_xml_version"); - if (sscanf(tmp_version, "%d.%d.%d", &major, &minor, µ) != 3) { - printf("%s, bad version string from xml2-config\n", "$min_xml_version"); - exit(1); - } - free(tmp_version); - - /* Capture the version information from the header files */ - tmp_version = (char *)strdup(LIBXML_DOTTED_VERSION); - if (sscanf(tmp_version, "%d.%d.%d", &xml_major_version, &xml_minor_version, &xml_micro_version) != 3) { - printf("%s, bad version string from libxml includes\n", "LIBXML_DOTTED_VERSION"); - exit(1); - } - free(tmp_version); - - /* Compare xml2-config output to the libxml headers */ - if ((xml_major_version != $xml_config_major_version) || - (xml_minor_version != $xml_config_minor_version) || - (xml_micro_version != $xml_config_micro_version)) - { - printf("*** libxml header files (version %d.%d.%d) do not match\n", - xml_major_version, xml_minor_version, xml_micro_version); - printf("*** xml2-config (version %d.%d.%d)\n", - $xml_config_major_version, $xml_config_minor_version, $xml_config_micro_version); - return 1; - } -/* Compare the headers to the library to make sure we match */ - /* Less than ideal -- doesn't provide us with return value feedback, - * only exits if there's a serious mismatch between header and library. - */ - LIBXML_TEST_VERSION; - - /* Test that the library is greater than our minimum version */ - if ((xml_major_version > major) || - ((xml_major_version == major) && (xml_minor_version > minor)) || - ((xml_major_version == major) && (xml_minor_version == minor) && - (xml_micro_version >= micro))) - { - return 0; - } - else - { - printf("\n*** An old version of libxml (%d.%d.%d) was found.\n", - xml_major_version, xml_minor_version, xml_micro_version); - printf("*** You need a version of libxml newer than %d.%d.%d. The latest version of\n", - major, minor, micro); - printf("*** libxml is always available from ftp://ftp.xmlsoft.org.\n"); - printf("***\n"); - printf("*** If you have already installed a sufficiently new version, this error\n"); - printf("*** probably means that the wrong copy of the xml2-config shell script is\n"); - printf("*** being found. The easiest way to fix this is to remove the old version\n"); - printf("*** of LIBXML, but you can also set the XML2_CONFIG environment to point to the\n"); - printf("*** correct copy of xml2-config. (In this case, you will have to\n"); - printf("*** modify your LD_LIBRARY_PATH enviroment variable, or edit /etc/ld.so.conf\n"); - printf("*** so that the correct libraries are found at run-time))\n"); - } - return 1; -} - -_ACEOF -if ac_fn_c_try_run "$LINENO"; then : - -else - no_xml=yes -fi -rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ - conftest.$ac_objext conftest.beam conftest.$ac_ext -fi - - CPPFLAGS="$ac_save_CPPFLAGS" - LIBS="$ac_save_LIBS" - fi - fi - - if test "x$no_xml" = x ; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes (version $xml_config_major_version.$xml_config_minor_version.$xml_config_micro_version)" >&5 -$as_echo "yes (version $xml_config_major_version.$xml_config_minor_version.$xml_config_micro_version)" >&6; } - have_libxml2=yes - else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } - if test "$XML2_CONFIG" = "no" ; then - echo "*** The xml2-config script installed by LIBXML could not be found" - echo "*** If libxml was installed in PREFIX, make sure PREFIX/bin is in" - echo "*** your path, or set the XML2_CONFIG environment variable to the" - echo "*** full path to xml2-config." - else - if test -f conf.xmltest ; then - : - else - echo "*** Could not run libxml test program, checking why..." - CPPFLAGS="$CPPFLAGS $XML_CPPFLAGS" - LIBS="$LIBS $XML_LIBS" - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -#include -#include - -int -main () -{ - LIBXML_TEST_VERSION; return 0; - ; - return 0; -} -_ACEOF -if ac_fn_c_try_link "$LINENO"; then : - echo "*** The test program compiled, but did not run. This usually means" - echo "*** that the run-time linker is not finding LIBXML or finding the wrong" - echo "*** version of LIBXML. If it is not finding LIBXML, you'll need to set your" - echo "*** LD_LIBRARY_PATH environment variable, or edit /etc/ld.so.conf to point" - echo "*** to the installed location Also, make sure you have run ldconfig if that" - echo "*** is required on your system" - echo "***" - echo "*** If you have an old version installed, it is best to remove it, although" - echo "*** you may also be able to get things to work by modifying LD_LIBRARY_PATH" -else - echo "*** The test program failed to compile or link. See the file config.log for the" - echo "*** exact error that occured. This usually means LIBXML was incorrectly installed" - echo "*** or that you have moved LIBXML since it was installed. In the latter case, you" - echo "*** may want to edit the xml2-config script: $XML2_CONFIG" -fi -rm -f core conftest.err conftest.$ac_objext \ - conftest$ac_exeext conftest.$ac_ext - CPPFLAGS="$ac_save_CPPFLAGS" - LIBS="$ac_save_LIBS" - fi - fi - - XML_CPPFLAGS="" - XML_LIBS="" - have_libxml2=no - fi - - - rm -f conf.xmltest - - if test "x${have_libxml2}" = "xyes"; then - -$as_echo "#define HAVE_LIBXML2 1" >>confdefs.h - - fi -fi - -if test "x${request_libxml2}" = "xyes" && - test "x${have_libxml2}" != "xyes"; then - as_fn_error $? "libxml2 was requested (--with-libxml2) but not found" "$LINENO" 5 -fi - - if test "x${have_libxml2}" = "xyes" ; then - HAVE_LIBXML2_TRUE= - HAVE_LIBXML2_FALSE='#' -else - HAVE_LIBXML2_TRUE='#' - HAVE_LIBXML2_FALSE= -fi - - -# jemalloc -have_jemalloc=no -if test "x${request_jemalloc}" != "xno"; then - LIBS_OLD=$LIBS - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing malloc_stats_print" >&5 -$as_echo_n "checking for library containing malloc_stats_print... " >&6; } -if ${ac_cv_search_malloc_stats_print+:} false; then : - $as_echo_n "(cached) " >&6 -else - ac_func_search_save_LIBS=$LIBS -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -/* Override any GCC internal prototype to avoid an error. - Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -#ifdef __cplusplus -extern "C" -#endif -char malloc_stats_print (); -int -main () -{ -return malloc_stats_print (); - ; - return 0; -} -_ACEOF -for ac_lib in '' jemalloc; do - if test -z "$ac_lib"; then - ac_res="none required" - else - ac_res=-l$ac_lib - LIBS="-l$ac_lib $PTHREAD_LDFLAGS $ac_func_search_save_LIBS" - fi - if ac_fn_c_try_link "$LINENO"; then : - ac_cv_search_malloc_stats_print=$ac_res -fi -rm -f core conftest.err conftest.$ac_objext \ - conftest$ac_exeext - if ${ac_cv_search_malloc_stats_print+:} false; then : - break -fi -done -if ${ac_cv_search_malloc_stats_print+:} false; then : - -else - ac_cv_search_malloc_stats_print=no -fi -rm conftest.$ac_ext -LIBS=$ac_func_search_save_LIBS -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_malloc_stats_print" >&5 -$as_echo "$ac_cv_search_malloc_stats_print" >&6; } -ac_res=$ac_cv_search_malloc_stats_print -if test "$ac_res" != no; then : - test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" - have_jemalloc=yes -fi - - LIBS=$LIBS_OLD - - if test "x${have_jemalloc}" = "xyes"; then - jemalloc_libs=${ac_cv_search_malloc_stats_print} - else - # On Darwin, malloc_stats_print is je_malloc_stats_print - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing je_malloc_stats_print" >&5 -$as_echo_n "checking for library containing je_malloc_stats_print... " >&6; } -if ${ac_cv_search_je_malloc_stats_print+:} false; then : - $as_echo_n "(cached) " >&6 -else - ac_func_search_save_LIBS=$LIBS -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -/* Override any GCC internal prototype to avoid an error. - Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -#ifdef __cplusplus -extern "C" -#endif -char je_malloc_stats_print (); -int -main () -{ -return je_malloc_stats_print (); - ; - return 0; -} -_ACEOF -for ac_lib in '' jemalloc; do - if test -z "$ac_lib"; then - ac_res="none required" - else - ac_res=-l$ac_lib - LIBS="-l$ac_lib $PTHREAD_LDFLAGS $ac_func_search_save_LIBS" - fi - if ac_fn_c_try_link "$LINENO"; then : - ac_cv_search_je_malloc_stats_print=$ac_res -fi -rm -f core conftest.err conftest.$ac_objext \ - conftest$ac_exeext - if ${ac_cv_search_je_malloc_stats_print+:} false; then : - break -fi -done -if ${ac_cv_search_je_malloc_stats_print+:} false; then : - -else - ac_cv_search_je_malloc_stats_print=no -fi -rm conftest.$ac_ext -LIBS=$ac_func_search_save_LIBS -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_je_malloc_stats_print" >&5 -$as_echo "$ac_cv_search_je_malloc_stats_print" >&6; } -ac_res=$ac_cv_search_je_malloc_stats_print -if test "$ac_res" != no; then : - test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" - have_jemalloc=yes -fi - - LIBS=$LIBS_OLD - - if test "x${have_jemalloc}" = "xyes"; then - jemalloc_libs=${ac_cv_search_je_malloc_stats_print} - fi - fi - - if test "x${have_jemalloc}" = "xyes" && - test "x${jemalloc_libs}" != "xnone required"; then - JEMALLOC_LIBS=${jemalloc_libs} - - fi -fi - -if test "x${request_jemalloc}" = "xyes" && - test "x${have_jemalloc}" != "xyes"; then - as_fn_error $? "jemalloc was requested (--with-jemalloc) but not found" "$LINENO" 5 -fi - -# spdylay (for src/nghttpx and src/h2load) -have_spdylay=no -if test "x${request_spdylay}" != "xno"; then - -pkg_failed=no -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for LIBSPDYLAY" >&5 -$as_echo_n "checking for LIBSPDYLAY... " >&6; } - -if test -n "$LIBSPDYLAY_CFLAGS"; then - pkg_cv_LIBSPDYLAY_CFLAGS="$LIBSPDYLAY_CFLAGS" - elif test -n "$PKG_CONFIG"; then - if test -n "$PKG_CONFIG" && \ - { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libspdylay >= 1.3.2\""; } >&5 - ($PKG_CONFIG --exists --print-errors "libspdylay >= 1.3.2") 2>&5 - ac_status=$? - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; }; then - pkg_cv_LIBSPDYLAY_CFLAGS=`$PKG_CONFIG --cflags "libspdylay >= 1.3.2" 2>/dev/null` - test "x$?" != "x0" && pkg_failed=yes -else - pkg_failed=yes -fi - else - pkg_failed=untried -fi -if test -n "$LIBSPDYLAY_LIBS"; then - pkg_cv_LIBSPDYLAY_LIBS="$LIBSPDYLAY_LIBS" - elif test -n "$PKG_CONFIG"; then - if test -n "$PKG_CONFIG" && \ - { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libspdylay >= 1.3.2\""; } >&5 - ($PKG_CONFIG --exists --print-errors "libspdylay >= 1.3.2") 2>&5 - ac_status=$? - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; }; then - pkg_cv_LIBSPDYLAY_LIBS=`$PKG_CONFIG --libs "libspdylay >= 1.3.2" 2>/dev/null` - test "x$?" != "x0" && pkg_failed=yes -else - pkg_failed=yes -fi - else - pkg_failed=untried -fi - - - -if test $pkg_failed = yes; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } - -if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then - _pkg_short_errors_supported=yes -else - _pkg_short_errors_supported=no -fi - if test $_pkg_short_errors_supported = yes; then - LIBSPDYLAY_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "libspdylay >= 1.3.2" 2>&1` - else - LIBSPDYLAY_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "libspdylay >= 1.3.2" 2>&1` - fi - # Put the nasty error message in config.log where it belongs - echo "$LIBSPDYLAY_PKG_ERRORS" >&5 - - have_spdylay=no -elif test $pkg_failed = untried; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } - have_spdylay=no -else - LIBSPDYLAY_CFLAGS=$pkg_cv_LIBSPDYLAY_CFLAGS - LIBSPDYLAY_LIBS=$pkg_cv_LIBSPDYLAY_LIBS - { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 -$as_echo "yes" >&6; } - have_spdylay=yes -fi - if test "x${have_spdylay}" = "xyes"; then - -$as_echo "#define HAVE_SPDYLAY 1" >>confdefs.h - - else - { $as_echo "$as_me:${as_lineno-$LINENO}: $LIBSPDYLAY_PKG_ERRORS" >&5 -$as_echo "$as_me: $LIBSPDYLAY_PKG_ERRORS" >&6;} - { $as_echo "$as_me:${as_lineno-$LINENO}: The SPDY support in nghttpx and h2load will be disabled." >&5 -$as_echo "$as_me: The SPDY support in nghttpx and h2load will be disabled." >&6;} - fi -fi - -if test "x${request_spdylay}" = "xyes" && - test "x${have_spdylay}" != "xyes"; then - as_fn_error $? "spdylay was requested (--with-spdylay) but not found" "$LINENO" 5 -fi - - if test "x${have_spdylay}" = "xyes" ; then - HAVE_SPDYLAY_TRUE= - HAVE_SPDYLAY_FALSE='#' -else - HAVE_SPDYLAY_TRUE='#' - HAVE_SPDYLAY_FALSE= -fi - - -# Check Boost Asio library -have_asio_lib=no - -if test "x${request_asio_lib}" = "xyes"; then - - -# Check whether --with-boost was given. -if test "${with_boost+set}" = set; then : - withval=$with_boost; - if test "$withval" = "no"; then - want_boost="no" - elif test "$withval" = "yes"; then - want_boost="yes" - ac_boost_path="" - else - want_boost="yes" - ac_boost_path="$withval" - fi - -else - want_boost="yes" -fi - - - - -# Check whether --with-boost-libdir was given. -if test "${with_boost_libdir+set}" = set; then : - withval=$with_boost_libdir; - if test -d "$withval" - then - ac_boost_lib_path="$withval" - else - as_fn_error $? "--with-boost-libdir expected directory name" "$LINENO" 5 - fi - -else - ac_boost_lib_path="" - -fi - - -if test "x$want_boost" = "xyes"; then - boost_lib_version_req=1.54.0 - boost_lib_version_req_shorten=`expr $boost_lib_version_req : '\([0-9]*\.[0-9]*\)'` - boost_lib_version_req_major=`expr $boost_lib_version_req : '\([0-9]*\)'` - boost_lib_version_req_minor=`expr $boost_lib_version_req : '[0-9]*\.\([0-9]*\)'` - boost_lib_version_req_sub_minor=`expr $boost_lib_version_req : '[0-9]*\.[0-9]*\.\([0-9]*\)'` - if test "x$boost_lib_version_req_sub_minor" = "x" ; then - boost_lib_version_req_sub_minor="0" - fi - WANT_BOOST_VERSION=`expr $boost_lib_version_req_major \* 100000 \+ $boost_lib_version_req_minor \* 100 \+ $boost_lib_version_req_sub_minor` - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for boostlib >= $boost_lib_version_req" >&5 -$as_echo_n "checking for boostlib >= $boost_lib_version_req... " >&6; } - succeeded=no - - libsubdirs="lib" - ax_arch=`uname -m` - case $ax_arch in - x86_64) - libsubdirs="lib64 libx32 lib lib64" - ;; - ppc64|s390x|sparc64|aarch64|ppc64le) - libsubdirs="lib64 lib lib64 ppc64le" - ;; - esac - - - libsubdirs="lib/${host_cpu}-${host_os} $libsubdirs" - - case ${host_cpu} in - i?86) - libsubdirs="lib/i386-${host_os} $libsubdirs" - ;; - esac - - if test "$ac_boost_path" != ""; then - BOOST_CPPFLAGS="-I$ac_boost_path/include" - for ac_boost_path_tmp in $libsubdirs; do - if test -d "$ac_boost_path"/"$ac_boost_path_tmp" ; then - BOOST_LDFLAGS="-L$ac_boost_path/$ac_boost_path_tmp" - break - fi - done - elif test "$cross_compiling" != yes; then - for ac_boost_path_tmp in /usr /usr/local /opt /opt/local ; do - if test -d "$ac_boost_path_tmp/include/boost" && test -r "$ac_boost_path_tmp/include/boost"; then - for libsubdir in $libsubdirs ; do - if ls "$ac_boost_path_tmp/$libsubdir/libboost_"* >/dev/null 2>&1 ; then break; fi - done - BOOST_LDFLAGS="-L$ac_boost_path_tmp/$libsubdir" - BOOST_CPPFLAGS="-I$ac_boost_path_tmp/include" - break; - fi - done - fi - - if test "$ac_boost_lib_path" != ""; then - BOOST_LDFLAGS="-L$ac_boost_lib_path" - fi - - CPPFLAGS_SAVED="$CPPFLAGS" - CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS" - export CPPFLAGS - - LDFLAGS_SAVED="$LDFLAGS" - LDFLAGS="$LDFLAGS $BOOST_LDFLAGS" - export LDFLAGS - - - ac_ext=cpp -ac_cpp='$CXXCPP $CPPFLAGS' -ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_cxx_compiler_gnu - - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - - #include - -int -main () -{ - - #if BOOST_VERSION >= $WANT_BOOST_VERSION - // Everything is okay - #else - # error Boost version is too old - #endif - - ; - return 0; -} -_ACEOF -if ac_fn_cxx_try_compile "$LINENO"; then : - - { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 -$as_echo "yes" >&6; } - succeeded=yes - found_system=yes - -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext - ac_ext=c -ac_cpp='$CPP $CPPFLAGS' -ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_c_compiler_gnu - - - - - if test "x$succeeded" != "xyes"; then - _version=0 - if test "$ac_boost_path" != ""; then - if test -d "$ac_boost_path" && test -r "$ac_boost_path"; then - for i in `ls -d $ac_boost_path/include/boost-* 2>/dev/null`; do - _version_tmp=`echo $i | sed "s#$ac_boost_path##" | sed 's/\/include\/boost-//' | sed 's/_/./'` - V_CHECK=`expr $_version_tmp \> $_version` - if test "$V_CHECK" = "1" ; then - _version=$_version_tmp - fi - VERSION_UNDERSCORE=`echo $_version | sed 's/\./_/'` - BOOST_CPPFLAGS="-I$ac_boost_path/include/boost-$VERSION_UNDERSCORE" - done - fi - else - if test "$cross_compiling" != yes; then - for ac_boost_path in /usr /usr/local /opt /opt/local ; do - if test -d "$ac_boost_path" && test -r "$ac_boost_path"; then - for i in `ls -d $ac_boost_path/include/boost-* 2>/dev/null`; do - _version_tmp=`echo $i | sed "s#$ac_boost_path##" | sed 's/\/include\/boost-//' | sed 's/_/./'` - V_CHECK=`expr $_version_tmp \> $_version` - if test "$V_CHECK" = "1" ; then - _version=$_version_tmp - best_path=$ac_boost_path - fi - done - fi - done - - VERSION_UNDERSCORE=`echo $_version | sed 's/\./_/'` - BOOST_CPPFLAGS="-I$best_path/include/boost-$VERSION_UNDERSCORE" - if test "$ac_boost_lib_path" = ""; then - for libsubdir in $libsubdirs ; do - if ls "$best_path/$libsubdir/libboost_"* >/dev/null 2>&1 ; then break; fi - done - BOOST_LDFLAGS="-L$best_path/$libsubdir" - fi - fi - - if test "x$BOOST_ROOT" != "x"; then - for libsubdir in $libsubdirs ; do - if ls "$BOOST_ROOT/stage/$libsubdir/libboost_"* >/dev/null 2>&1 ; then break; fi - done - if test -d "$BOOST_ROOT" && test -r "$BOOST_ROOT" && test -d "$BOOST_ROOT/stage/$libsubdir" && test -r "$BOOST_ROOT/stage/$libsubdir"; then - version_dir=`expr //$BOOST_ROOT : '.*/\(.*\)'` - stage_version=`echo $version_dir | sed 's/boost_//' | sed 's/_/./g'` - stage_version_shorten=`expr $stage_version : '\([0-9]*\.[0-9]*\)'` - V_CHECK=`expr $stage_version_shorten \>\= $_version` - if test "$V_CHECK" = "1" -a "$ac_boost_lib_path" = "" ; then - { $as_echo "$as_me:${as_lineno-$LINENO}: We will use a staged boost library from $BOOST_ROOT" >&5 -$as_echo "$as_me: We will use a staged boost library from $BOOST_ROOT" >&6;} - BOOST_CPPFLAGS="-I$BOOST_ROOT" - BOOST_LDFLAGS="-L$BOOST_ROOT/stage/$libsubdir" - fi - fi - fi - fi - - CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS" - export CPPFLAGS - LDFLAGS="$LDFLAGS $BOOST_LDFLAGS" - export LDFLAGS - - ac_ext=cpp -ac_cpp='$CXXCPP $CPPFLAGS' -ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_cxx_compiler_gnu - - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - - #include - -int -main () -{ - - #if BOOST_VERSION >= $WANT_BOOST_VERSION - // Everything is okay - #else - # error Boost version is too old - #endif - - ; - return 0; -} -_ACEOF -if ac_fn_cxx_try_compile "$LINENO"; then : - - { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 -$as_echo "yes" >&6; } - succeeded=yes - found_system=yes - -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext - ac_ext=c -ac_cpp='$CPP $CPPFLAGS' -ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_c_compiler_gnu - - fi - - if test "$succeeded" != "yes" ; then - if test "$_version" = "0" ; then - { $as_echo "$as_me:${as_lineno-$LINENO}: We could not detect the boost libraries (version $boost_lib_version_req_shorten or higher). If you have a staged boost library (still not installed) please specify \$BOOST_ROOT in your environment and do not give a PATH to --with-boost option. If you are sure you have boost installed, then check your version number looking in . See http://randspringer.de/boost for more documentation." >&5 -$as_echo "$as_me: We could not detect the boost libraries (version $boost_lib_version_req_shorten or higher). If you have a staged boost library (still not installed) please specify \$BOOST_ROOT in your environment and do not give a PATH to --with-boost option. If you are sure you have boost installed, then check your version number looking in . See http://randspringer.de/boost for more documentation." >&6;} - else - { $as_echo "$as_me:${as_lineno-$LINENO}: Your boost libraries seems to old (version $_version)." >&5 -$as_echo "$as_me: Your boost libraries seems to old (version $_version)." >&6;} - fi - # execute ACTION-IF-NOT-FOUND (if present): - have_boost_base=no - else - - - -$as_echo "#define HAVE_BOOST /**/" >>confdefs.h - - # execute ACTION-IF-FOUND (if present): - have_boost_base=yes - fi - - CPPFLAGS="$CPPFLAGS_SAVED" - LDFLAGS="$LDFLAGS_SAVED" -fi - - - - if test "x${have_boost_base}" = "xyes"; then - - -# Check whether --with-boost-asio was given. -if test "${with_boost_asio+set}" = set; then : - withval=$with_boost_asio; - if test "$withval" = "no"; then - want_boost="no" - elif test "$withval" = "yes"; then - want_boost="yes" - ax_boost_user_asio_lib="" - else - want_boost="yes" - ax_boost_user_asio_lib="$withval" - fi - -else - want_boost="yes" - -fi - - - if test "x$want_boost" = "xyes"; then - - CPPFLAGS_SAVED="$CPPFLAGS" - CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS" - export CPPFLAGS - - LDFLAGS_SAVED="$LDFLAGS" - LDFLAGS="$LDFLAGS $BOOST_LDFLAGS" - export LDFLAGS - - { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the Boost::ASIO library is available" >&5 -$as_echo_n "checking whether the Boost::ASIO library is available... " >&6; } -if ${ax_cv_boost_asio+:} false; then : - $as_echo_n "(cached) " >&6 -else - ac_ext=cpp -ac_cpp='$CXXCPP $CPPFLAGS' -ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_cxx_compiler_gnu - - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - #include - -int -main () -{ - - - boost::asio::io_service io; - boost::system::error_code timer_result; - boost::asio::deadline_timer t(io); - t.cancel(); - io.run_one(); - return 0; - - ; - return 0; -} -_ACEOF -if ac_fn_cxx_try_compile "$LINENO"; then : - ax_cv_boost_asio=yes -else - ax_cv_boost_asio=no -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext - ac_ext=c -ac_cpp='$CPP $CPPFLAGS' -ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_c_compiler_gnu - - -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_boost_asio" >&5 -$as_echo "$ax_cv_boost_asio" >&6; } - if test "x$ax_cv_boost_asio" = "xyes"; then - -$as_echo "#define HAVE_BOOST_ASIO /**/" >>confdefs.h - - BN=boost_system - BOOSTLIBDIR=`echo $BOOST_LDFLAGS | sed -e 's/[^\/]*//'` - if test "x$ax_boost_user_asio_lib" = "x"; then - for ax_lib in `ls $BOOSTLIBDIR/libboost_system*.so* $BOOSTLIBDIR/libboost_system*.dylib* $BOOSTLIBDIR/libboost_system*.a* 2>/dev/null | sed 's,.*/,,' | sed -e 's;^lib\(boost_system.*\)\.so.*$;\1;' -e 's;^lib\(boost_system.*\)\.dylib.*$;\1;' -e 's;^lib\(boost_system.*\)\.a.*$;\1;' ` ; do - as_ac_Lib=`$as_echo "ac_cv_lib_$ax_lib''_main" | $as_tr_sh` -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for main in -l$ax_lib" >&5 -$as_echo_n "checking for main in -l$ax_lib... " >&6; } -if eval \${$as_ac_Lib+:} false; then : - $as_echo_n "(cached) " >&6 -else - ac_check_lib_save_LIBS=$LIBS -LIBS="-l$ax_lib $LIBS" -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - - -int -main () -{ -return main (); - ; - return 0; -} -_ACEOF -if ac_fn_c_try_link "$LINENO"; then : - eval "$as_ac_Lib=yes" -else - eval "$as_ac_Lib=no" -fi -rm -f core conftest.err conftest.$ac_objext \ - conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS -fi -eval ac_res=\$$as_ac_Lib - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 -$as_echo "$ac_res" >&6; } -if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : - BOOST_ASIO_LIB="-l$ax_lib" link_thread="yes" break -else - link_thread="no" -fi - - done - else - for ax_lib in $ax_boost_user_asio_lib $BN-$ax_boost_user_asio_lib; do - as_ac_Lib=`$as_echo "ac_cv_lib_$ax_lib''_main" | $as_tr_sh` -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for main in -l$ax_lib" >&5 -$as_echo_n "checking for main in -l$ax_lib... " >&6; } -if eval \${$as_ac_Lib+:} false; then : - $as_echo_n "(cached) " >&6 -else - ac_check_lib_save_LIBS=$LIBS -LIBS="-l$ax_lib $LIBS" -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - - -int -main () -{ -return main (); - ; - return 0; -} -_ACEOF -if ac_fn_c_try_link "$LINENO"; then : - eval "$as_ac_Lib=yes" -else - eval "$as_ac_Lib=no" -fi -rm -f core conftest.err conftest.$ac_objext \ - conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS -fi -eval ac_res=\$$as_ac_Lib - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 -$as_echo "$ac_res" >&6; } -if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : - BOOST_ASIO_LIB="-l$ax_lib" link_asio="yes" break -else - link_asio="no" -fi - - done - - fi - if test "x$ax_lib" = "x"; then - as_fn_error $? "Could not find a version of the library!" "$LINENO" 5 - fi - if test "x$link_asio" = "xno"; then - as_fn_error $? "Could not link against $ax_lib !" "$LINENO" 5 - fi - fi - - CPPFLAGS="$CPPFLAGS_SAVED" - LDFLAGS="$LDFLAGS_SAVED" - fi - - - -# Check whether --with-boost-system was given. -if test "${with_boost_system+set}" = set; then : - withval=$with_boost_system; - if test "$withval" = "no"; then - want_boost="no" - elif test "$withval" = "yes"; then - want_boost="yes" - ax_boost_user_system_lib="" - else - want_boost="yes" - ax_boost_user_system_lib="$withval" - fi - -else - want_boost="yes" - -fi - - - if test "x$want_boost" = "xyes"; then - - - CPPFLAGS_SAVED="$CPPFLAGS" - CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS" - export CPPFLAGS - - LDFLAGS_SAVED="$LDFLAGS" - LDFLAGS="$LDFLAGS $BOOST_LDFLAGS" - export LDFLAGS - - { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the Boost::System library is available" >&5 -$as_echo_n "checking whether the Boost::System library is available... " >&6; } -if ${ax_cv_boost_system+:} false; then : - $as_echo_n "(cached) " >&6 -else - ac_ext=cpp -ac_cpp='$CXXCPP $CPPFLAGS' -ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_cxx_compiler_gnu - - CXXFLAGS_SAVE=$CXXFLAGS - - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#include -int -main () -{ -boost::system::system_category - ; - return 0; -} -_ACEOF -if ac_fn_cxx_try_compile "$LINENO"; then : - ax_cv_boost_system=yes -else - ax_cv_boost_system=no -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext - CXXFLAGS=$CXXFLAGS_SAVE - ac_ext=c -ac_cpp='$CPP $CPPFLAGS' -ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_c_compiler_gnu - - -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_boost_system" >&5 -$as_echo "$ax_cv_boost_system" >&6; } - if test "x$ax_cv_boost_system" = "xyes"; then - - - -$as_echo "#define HAVE_BOOST_SYSTEM /**/" >>confdefs.h - - BOOSTLIBDIR=`echo $BOOST_LDFLAGS | sed -e 's/[^\/]*//'` - - LDFLAGS_SAVE=$LDFLAGS - if test "x$ax_boost_user_system_lib" = "x"; then - for libextension in `ls -r $BOOSTLIBDIR/libboost_system* 2>/dev/null | sed 's,.*/lib,,' | sed 's,\..*,,'` ; do - ax_lib=${libextension} - as_ac_Lib=`$as_echo "ac_cv_lib_$ax_lib''_exit" | $as_tr_sh` -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for exit in -l$ax_lib" >&5 -$as_echo_n "checking for exit in -l$ax_lib... " >&6; } -if eval \${$as_ac_Lib+:} false; then : - $as_echo_n "(cached) " >&6 -else - ac_check_lib_save_LIBS=$LIBS -LIBS="-l$ax_lib $LIBS" -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -/* Override any GCC internal prototype to avoid an error. - Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -#ifdef __cplusplus -extern "C" -#endif -char exit (); -int -main () -{ -return exit (); - ; - return 0; -} -_ACEOF -if ac_fn_c_try_link "$LINENO"; then : - eval "$as_ac_Lib=yes" -else - eval "$as_ac_Lib=no" -fi -rm -f core conftest.err conftest.$ac_objext \ - conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS -fi -eval ac_res=\$$as_ac_Lib - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 -$as_echo "$ac_res" >&6; } -if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : - BOOST_SYSTEM_LIB="-l$ax_lib"; link_system="yes"; break -else - link_system="no" -fi - - done - if test "x$link_system" != "xyes"; then - for libextension in `ls -r $BOOSTLIBDIR/boost_system* 2>/dev/null | sed 's,.*/,,' | sed -e 's,\..*,,'` ; do - ax_lib=${libextension} - as_ac_Lib=`$as_echo "ac_cv_lib_$ax_lib''_exit" | $as_tr_sh` -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for exit in -l$ax_lib" >&5 -$as_echo_n "checking for exit in -l$ax_lib... " >&6; } -if eval \${$as_ac_Lib+:} false; then : - $as_echo_n "(cached) " >&6 -else - ac_check_lib_save_LIBS=$LIBS -LIBS="-l$ax_lib $LIBS" -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -/* Override any GCC internal prototype to avoid an error. - Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -#ifdef __cplusplus -extern "C" -#endif -char exit (); -int -main () -{ -return exit (); - ; - return 0; -} -_ACEOF -if ac_fn_c_try_link "$LINENO"; then : - eval "$as_ac_Lib=yes" -else - eval "$as_ac_Lib=no" -fi -rm -f core conftest.err conftest.$ac_objext \ - conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS -fi -eval ac_res=\$$as_ac_Lib - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 -$as_echo "$ac_res" >&6; } -if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : - BOOST_SYSTEM_LIB="-l$ax_lib"; link_system="yes"; break -else - link_system="no" -fi - - done - fi - - else - for ax_lib in $ax_boost_user_system_lib boost_system-$ax_boost_user_system_lib; do - as_ac_Lib=`$as_echo "ac_cv_lib_$ax_lib''_exit" | $as_tr_sh` -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for exit in -l$ax_lib" >&5 -$as_echo_n "checking for exit in -l$ax_lib... " >&6; } -if eval \${$as_ac_Lib+:} false; then : - $as_echo_n "(cached) " >&6 -else - ac_check_lib_save_LIBS=$LIBS -LIBS="-l$ax_lib $LIBS" -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -/* Override any GCC internal prototype to avoid an error. - Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -#ifdef __cplusplus -extern "C" -#endif -char exit (); -int -main () -{ -return exit (); - ; - return 0; -} -_ACEOF -if ac_fn_c_try_link "$LINENO"; then : - eval "$as_ac_Lib=yes" -else - eval "$as_ac_Lib=no" -fi -rm -f core conftest.err conftest.$ac_objext \ - conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS -fi -eval ac_res=\$$as_ac_Lib - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 -$as_echo "$ac_res" >&6; } -if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : - BOOST_SYSTEM_LIB="-l$ax_lib"; link_system="yes"; break -else - link_system="no" -fi - - done - - fi - if test "x$ax_lib" = "x"; then - as_fn_error $? "Could not find a version of the library!" "$LINENO" 5 - fi - if test "x$link_system" = "xno"; then - as_fn_error $? "Could not link against $ax_lib !" "$LINENO" 5 - fi - fi - - CPPFLAGS="$CPPFLAGS_SAVED" - LDFLAGS="$LDFLAGS_SAVED" - fi - - - -# Check whether --with-boost-thread was given. -if test "${with_boost_thread+set}" = set; then : - withval=$with_boost_thread; - if test "$withval" = "no"; then - want_boost="no" - elif test "$withval" = "yes"; then - want_boost="yes" - ax_boost_user_thread_lib="" - else - want_boost="yes" - ax_boost_user_thread_lib="$withval" - fi - -else - want_boost="yes" - -fi - - - if test "x$want_boost" = "xyes"; then - - - CPPFLAGS_SAVED="$CPPFLAGS" - CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS" - export CPPFLAGS - - LDFLAGS_SAVED="$LDFLAGS" - LDFLAGS="$LDFLAGS $BOOST_LDFLAGS" - export LDFLAGS - - { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the Boost::Thread library is available" >&5 -$as_echo_n "checking whether the Boost::Thread library is available... " >&6; } -if ${ax_cv_boost_thread+:} false; then : - $as_echo_n "(cached) " >&6 -else - ac_ext=cpp -ac_cpp='$CXXCPP $CPPFLAGS' -ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_cxx_compiler_gnu - - CXXFLAGS_SAVE=$CXXFLAGS - - if test "x$host_os" = "xsolaris" ; then - CXXFLAGS="-pthreads $CXXFLAGS" - elif test "x$host_os" = "xmingw32" ; then - CXXFLAGS="-mthreads $CXXFLAGS" - else - CXXFLAGS="-pthread $CXXFLAGS" - fi - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#include -int -main () -{ -boost::thread_group thrds; - return 0; - ; - return 0; -} -_ACEOF -if ac_fn_cxx_try_compile "$LINENO"; then : - ax_cv_boost_thread=yes -else - ax_cv_boost_thread=no -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext - CXXFLAGS=$CXXFLAGS_SAVE - ac_ext=c -ac_cpp='$CPP $CPPFLAGS' -ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_c_compiler_gnu - - -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_boost_thread" >&5 -$as_echo "$ax_cv_boost_thread" >&6; } - if test "x$ax_cv_boost_thread" = "xyes"; then - if test "x$host_os" = "xsolaris" ; then - BOOST_CPPFLAGS="-pthreads $BOOST_CPPFLAGS" - elif test "x$host_os" = "xmingw32" ; then - BOOST_CPPFLAGS="-mthreads $BOOST_CPPFLAGS" - else - BOOST_CPPFLAGS="-pthread $BOOST_CPPFLAGS" - fi - - - - -$as_echo "#define HAVE_BOOST_THREAD /**/" >>confdefs.h - - BOOSTLIBDIR=`echo $BOOST_LDFLAGS | sed -e 's/[^\/]*//'` - - LDFLAGS_SAVE=$LDFLAGS - case "x$host_os" in - *bsd* ) - LDFLAGS="-pthread $LDFLAGS" - break; - ;; - esac - if test "x$ax_boost_user_thread_lib" = "x"; then - for libextension in `ls -r $BOOSTLIBDIR/libboost_thread* 2>/dev/null | sed 's,.*/lib,,' | sed 's,\..*,,'`; do - ax_lib=${libextension} - as_ac_Lib=`$as_echo "ac_cv_lib_$ax_lib''_exit" | $as_tr_sh` -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for exit in -l$ax_lib" >&5 -$as_echo_n "checking for exit in -l$ax_lib... " >&6; } -if eval \${$as_ac_Lib+:} false; then : - $as_echo_n "(cached) " >&6 -else - ac_check_lib_save_LIBS=$LIBS -LIBS="-l$ax_lib $LIBS" -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -/* Override any GCC internal prototype to avoid an error. - Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -#ifdef __cplusplus -extern "C" -#endif -char exit (); -int -main () -{ -return exit (); - ; - return 0; -} -_ACEOF -if ac_fn_c_try_link "$LINENO"; then : - eval "$as_ac_Lib=yes" -else - eval "$as_ac_Lib=no" -fi -rm -f core conftest.err conftest.$ac_objext \ - conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS -fi -eval ac_res=\$$as_ac_Lib - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 -$as_echo "$ac_res" >&6; } -if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : - BOOST_THREAD_LIB="-l$ax_lib"; link_thread="yes"; break -else - link_thread="no" -fi - - done - if test "x$link_thread" != "xyes"; then - for libextension in `ls -r $BOOSTLIBDIR/boost_thread* 2>/dev/null | sed 's,.*/,,' | sed 's,\..*,,'`; do - ax_lib=${libextension} - as_ac_Lib=`$as_echo "ac_cv_lib_$ax_lib''_exit" | $as_tr_sh` -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for exit in -l$ax_lib" >&5 -$as_echo_n "checking for exit in -l$ax_lib... " >&6; } -if eval \${$as_ac_Lib+:} false; then : - $as_echo_n "(cached) " >&6 -else - ac_check_lib_save_LIBS=$LIBS -LIBS="-l$ax_lib $LIBS" -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -/* Override any GCC internal prototype to avoid an error. - Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -#ifdef __cplusplus -extern "C" -#endif -char exit (); -int -main () -{ -return exit (); - ; - return 0; -} -_ACEOF -if ac_fn_c_try_link "$LINENO"; then : - eval "$as_ac_Lib=yes" -else - eval "$as_ac_Lib=no" -fi -rm -f core conftest.err conftest.$ac_objext \ - conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS -fi -eval ac_res=\$$as_ac_Lib - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 -$as_echo "$ac_res" >&6; } -if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : - BOOST_THREAD_LIB="-l$ax_lib"; link_thread="yes"; break -else - link_thread="no" -fi - - done - fi - - else - for ax_lib in $ax_boost_user_thread_lib boost_thread-$ax_boost_user_thread_lib; do - as_ac_Lib=`$as_echo "ac_cv_lib_$ax_lib''_exit" | $as_tr_sh` -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for exit in -l$ax_lib" >&5 -$as_echo_n "checking for exit in -l$ax_lib... " >&6; } -if eval \${$as_ac_Lib+:} false; then : - $as_echo_n "(cached) " >&6 -else - ac_check_lib_save_LIBS=$LIBS -LIBS="-l$ax_lib $LIBS" -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -/* Override any GCC internal prototype to avoid an error. - Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -#ifdef __cplusplus -extern "C" -#endif -char exit (); -int -main () -{ -return exit (); - ; - return 0; -} -_ACEOF -if ac_fn_c_try_link "$LINENO"; then : - eval "$as_ac_Lib=yes" -else - eval "$as_ac_Lib=no" -fi -rm -f core conftest.err conftest.$ac_objext \ - conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS -fi -eval ac_res=\$$as_ac_Lib - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 -$as_echo "$ac_res" >&6; } -if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : - BOOST_THREAD_LIB="-l$ax_lib"; link_thread="yes"; break -else - link_thread="no" -fi - - done - - fi - if test "x$ax_lib" = "x"; then - as_fn_error $? "Could not find a version of the library!" "$LINENO" 5 - fi - if test "x$link_thread" = "xno"; then - as_fn_error $? "Could not link against $ax_lib !" "$LINENO" 5 - else - case "x$host_os" in - *bsd* ) - BOOST_LDFLAGS="-pthread $BOOST_LDFLAGS" - break; - ;; - esac - - fi - fi - - CPPFLAGS="$CPPFLAGS_SAVED" - LDFLAGS="$LDFLAGS_SAVED" - fi - - - if test "x${ax_cv_boost_asio}" = "xyes" && - test "x${ax_cv_boost_system}" = "xyes" && - test "x${ax_cv_boost_thread}" = "xyes"; then - have_asio_lib=yes - fi - fi -fi - -# The nghttp, nghttpd and nghttpx under src depend on zlib, OpenSSL -# and libev -enable_app=no -if test "x${request_app}" != "xno" && - test "x${have_zlib}" = "xyes" && - test "x${have_openssl}" = "xyes" && - test "x${have_libev}" = "xyes"; then - enable_app=yes -fi - -if test "x${request_app}" = "xyes" && - test "x${enable_app}" != "xyes"; then - as_fn_error $? "applications were requested (--enable-app) but dependencies are not met." "$LINENO" 5 -fi - - if test "x${enable_app}" = "xyes" ; then - ENABLE_APP_TRUE= - ENABLE_APP_FALSE='#' -else - ENABLE_APP_TRUE='#' - ENABLE_APP_FALSE= -fi - - -enable_hpack_tools=no -# HPACK tools requires jansson -if test "x${request_hpack_tools}" != "xno" && - test "x${have_jansson}" = "xyes"; then - enable_hpack_tools=yes -fi - -if test "x${request_hpack_tools}" = "xyes" && - test "x${enable_hpack_tools}" != "xyes"; then - as_fn_error $? "HPACK tools were requested (--enable-hpack-tools) but dependencies are not met." "$LINENO" 5 -fi - - if test "x${enable_hpack_tools}" = "xyes" ; then - ENABLE_HPACK_TOOLS_TRUE= - ENABLE_HPACK_TOOLS_FALSE='#' -else - ENABLE_HPACK_TOOLS_TRUE='#' - ENABLE_HPACK_TOOLS_FALSE= -fi - - -# C++ library libnghttp2_asio - -enable_asio_lib=no -if test "x${request_asio_lib}" != "xno" && - test "x${have_asio_lib}" = "xyes"; then - enable_asio_lib=yes -fi - - if test "x${enable_asio_lib}" = "xyes" ; then - ENABLE_ASIO_LIB_TRUE= - ENABLE_ASIO_LIB_FALSE='#' -else - ENABLE_ASIO_LIB_TRUE='#' - ENABLE_ASIO_LIB_FALSE= -fi - - -# The example programs depend on OpenSSL and libevent_openssl -enable_examples=no -if test "x${request_examples}" != "xno" && - test "x${have_openssl}" = "xyes" && - test "x${have_libevent_openssl}" = "xyes"; then - enable_examples=yes -fi - -if test "x${request_examples}" = "xyes" && - test "x${enable_examples}" != "xyes"; then - as_fn_error $? "examples were requested (--enable-examples) but dependencies are not met." "$LINENO" 5 -fi - - if test "x${enable_examples}" = "xyes" ; then - ENABLE_EXAMPLES_TRUE= - ENABLE_EXAMPLES_FALSE='#' -else - ENABLE_EXAMPLES_TRUE='#' - ENABLE_EXAMPLES_FALSE= -fi - - -# Python bindings -enable_python_bindings=no -if test "x${request_python_bindings}" != "xno" && - test "x${CYTHON}" != "x" && - test "x${PYTHON}" != "x:" && - test "x${have_python_dev}" = "xyes"; then - enable_python_bindings=yes -fi - -if test "x${request_python_bindings}" = "xyes" && - test "x${enable_python_bindings}" != "xyes"; then - as_fn_error $? "python bindings were requested (--enable-python-bindings) but dependencies are not met." "$LINENO" 5 -fi - - if test "x${enable_python_bindings}" = "xyes"; then - ENABLE_PYTHON_BINDINGS_TRUE= - ENABLE_PYTHON_BINDINGS_FALSE='#' -else - ENABLE_PYTHON_BINDINGS_TRUE='#' - ENABLE_PYTHON_BINDINGS_FALSE= -fi - - -# Produce cython conditional, so that we can distribute generated C -# source - if test "x${CYTHON}" != "x"; then - HAVE_CYTHON_TRUE= - HAVE_CYTHON_FALSE='#' -else - HAVE_CYTHON_TRUE='#' - HAVE_CYTHON_FALSE= -fi - - -# failmalloc tests -enable_failmalloc=no -if test "x${request_failmalloc}" = "xyes"; then - enable_failmalloc=yes -fi - - if test "x${enable_failmalloc}" = "xyes" ; then - ENABLE_FAILMALLOC_TRUE= - ENABLE_FAILMALLOC_FALSE='#' -else - ENABLE_FAILMALLOC_TRUE='#' - ENABLE_FAILMALLOC_FALSE= -fi - - -# Checks for header files. - - { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to enable assertions" >&5 -$as_echo_n "checking whether to enable assertions... " >&6; } - # Check whether --enable-assert was given. -if test "${enable_assert+set}" = set; then : - enableval=$enable_assert; ac_enable_assert=$enableval - if test "x$enableval" = xno; then : - -$as_echo "#define NDEBUG 1" >>confdefs.h - -elif test "x$enableval" != xyes; then : - { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: invalid argument supplied to --enable-assert" >&5 -$as_echo "$as_me: WARNING: invalid argument supplied to --enable-assert" >&2;} - ac_enable_assert=yes -fi -else - ac_enable_assert=yes -fi - - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_enable_assert" >&5 -$as_echo "$ac_enable_assert" >&6; } - -for ac_header in \ - arpa/inet.h \ - fcntl.h \ - inttypes.h \ - limits.h \ - netdb.h \ - netinet/in.h \ - pwd.h \ - stddef.h \ - stdint.h \ - stdlib.h \ - string.h \ - sys/socket.h \ - sys/time.h \ - syslog.h \ - time.h \ - unistd.h \ - -do : - as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` -ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default" -if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : - cat >>confdefs.h <<_ACEOF -#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 -_ACEOF - -fi - -done - - -# Checks for typedefs, structures, and compiler characteristics. -ac_fn_c_check_type "$LINENO" "size_t" "ac_cv_type_size_t" "$ac_includes_default" -if test "x$ac_cv_type_size_t" = xyes; then : - -else - -cat >>confdefs.h <<_ACEOF -#define size_t unsigned int -_ACEOF - -fi - -ac_fn_c_check_type "$LINENO" "ssize_t" "ac_cv_type_ssize_t" "$ac_includes_default" -if test "x$ac_cv_type_ssize_t" = xyes; then : - -else - -cat >>confdefs.h <<_ACEOF -#define ssize_t int -_ACEOF - -fi - -ac_fn_c_find_uintX_t "$LINENO" "8" "ac_cv_c_uint8_t" -case $ac_cv_c_uint8_t in #( - no|yes) ;; #( - *) - -$as_echo "#define _UINT8_T 1" >>confdefs.h - - -cat >>confdefs.h <<_ACEOF -#define uint8_t $ac_cv_c_uint8_t -_ACEOF -;; - esac - -ac_fn_c_find_uintX_t "$LINENO" "16" "ac_cv_c_uint16_t" -case $ac_cv_c_uint16_t in #( - no|yes) ;; #( - *) - - -cat >>confdefs.h <<_ACEOF -#define uint16_t $ac_cv_c_uint16_t -_ACEOF -;; - esac - -ac_fn_c_find_uintX_t "$LINENO" "32" "ac_cv_c_uint32_t" -case $ac_cv_c_uint32_t in #( - no|yes) ;; #( - *) - -$as_echo "#define _UINT32_T 1" >>confdefs.h - - -cat >>confdefs.h <<_ACEOF -#define uint32_t $ac_cv_c_uint32_t -_ACEOF -;; - esac - -ac_fn_c_find_uintX_t "$LINENO" "64" "ac_cv_c_uint64_t" -case $ac_cv_c_uint64_t in #( - no|yes) ;; #( - *) - -$as_echo "#define _UINT64_T 1" >>confdefs.h - - -cat >>confdefs.h <<_ACEOF -#define uint64_t $ac_cv_c_uint64_t -_ACEOF -;; - esac - -ac_fn_c_find_intX_t "$LINENO" "8" "ac_cv_c_int8_t" -case $ac_cv_c_int8_t in #( - no|yes) ;; #( - *) - -cat >>confdefs.h <<_ACEOF -#define int8_t $ac_cv_c_int8_t -_ACEOF -;; -esac - -ac_fn_c_find_intX_t "$LINENO" "16" "ac_cv_c_int16_t" -case $ac_cv_c_int16_t in #( - no|yes) ;; #( - *) - -cat >>confdefs.h <<_ACEOF -#define int16_t $ac_cv_c_int16_t -_ACEOF -;; -esac - -ac_fn_c_find_intX_t "$LINENO" "32" "ac_cv_c_int32_t" -case $ac_cv_c_int32_t in #( - no|yes) ;; #( - *) - -cat >>confdefs.h <<_ACEOF -#define int32_t $ac_cv_c_int32_t -_ACEOF -;; -esac - -ac_fn_c_find_intX_t "$LINENO" "64" "ac_cv_c_int64_t" -case $ac_cv_c_int64_t in #( - no|yes) ;; #( - *) - -cat >>confdefs.h <<_ACEOF -#define int64_t $ac_cv_c_int64_t -_ACEOF -;; -esac - -ac_fn_c_check_type "$LINENO" "off_t" "ac_cv_type_off_t" "$ac_includes_default" -if test "x$ac_cv_type_off_t" = xyes; then : - -else - -cat >>confdefs.h <<_ACEOF -#define off_t long int -_ACEOF - -fi - -ac_fn_c_check_type "$LINENO" "pid_t" "ac_cv_type_pid_t" "$ac_includes_default" -if test "x$ac_cv_type_pid_t" = xyes; then : - -else - -cat >>confdefs.h <<_ACEOF -#define pid_t int -_ACEOF - -fi - -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for uid_t in sys/types.h" >&5 -$as_echo_n "checking for uid_t in sys/types.h... " >&6; } -if ${ac_cv_type_uid_t+:} false; then : - $as_echo_n "(cached) " >&6 -else - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#include - -_ACEOF -if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | - $EGREP "uid_t" >/dev/null 2>&1; then : - ac_cv_type_uid_t=yes -else - ac_cv_type_uid_t=no -fi -rm -f conftest* - -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_type_uid_t" >&5 -$as_echo "$ac_cv_type_uid_t" >&6; } -if test $ac_cv_type_uid_t = no; then - -$as_echo "#define uid_t int" >>confdefs.h - - -$as_echo "#define gid_t int" >>confdefs.h - -fi - -ac_fn_c_check_type "$LINENO" "ptrdiff_t" "ac_cv_type_ptrdiff_t" "$ac_includes_default" -if test "x$ac_cv_type_ptrdiff_t" = xyes; then : - -cat >>confdefs.h <<_ACEOF -#define HAVE_PTRDIFF_T 1 -_ACEOF - - -fi - - { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether byte ordering is bigendian" >&5 -$as_echo_n "checking whether byte ordering is bigendian... " >&6; } -if ${ac_cv_c_bigendian+:} false; then : - $as_echo_n "(cached) " >&6 -else - ac_cv_c_bigendian=unknown - # See if we're dealing with a universal compiler. - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#ifndef __APPLE_CC__ - not a universal capable compiler - #endif - typedef int dummy; - -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - - # Check for potential -arch flags. It is not universal unless - # there are at least two -arch flags with different values. - ac_arch= - ac_prev= - for ac_word in $CC $CFLAGS $CPPFLAGS $LDFLAGS; do - if test -n "$ac_prev"; then - case $ac_word in - i?86 | x86_64 | ppc | ppc64) - if test -z "$ac_arch" || test "$ac_arch" = "$ac_word"; then - ac_arch=$ac_word - else - ac_cv_c_bigendian=universal - break - fi - ;; - esac - ac_prev= - elif test "x$ac_word" = "x-arch"; then - ac_prev=arch - fi - done -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext - if test $ac_cv_c_bigendian = unknown; then - # See if sys/param.h defines the BYTE_ORDER macro. - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#include - #include - -int -main () -{ -#if ! (defined BYTE_ORDER && defined BIG_ENDIAN \ - && defined LITTLE_ENDIAN && BYTE_ORDER && BIG_ENDIAN \ - && LITTLE_ENDIAN) - bogus endian macros - #endif - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - # It does; now see whether it defined to BIG_ENDIAN or not. - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#include - #include - -int -main () -{ -#if BYTE_ORDER != BIG_ENDIAN - not big endian - #endif - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - ac_cv_c_bigendian=yes -else - ac_cv_c_bigendian=no -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext - fi - if test $ac_cv_c_bigendian = unknown; then - # See if defines _LITTLE_ENDIAN or _BIG_ENDIAN (e.g., Solaris). - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#include - -int -main () -{ -#if ! (defined _LITTLE_ENDIAN || defined _BIG_ENDIAN) - bogus endian macros - #endif - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - # It does; now see whether it defined to _BIG_ENDIAN or not. - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#include - -int -main () -{ -#ifndef _BIG_ENDIAN - not big endian - #endif - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - ac_cv_c_bigendian=yes -else - ac_cv_c_bigendian=no -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext - fi - if test $ac_cv_c_bigendian = unknown; then - # Compile a test program. - if test "$cross_compiling" = yes; then : - # Try to guess by grepping values from an object file. - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -short int ascii_mm[] = - { 0x4249, 0x4765, 0x6E44, 0x6961, 0x6E53, 0x7953, 0 }; - short int ascii_ii[] = - { 0x694C, 0x5454, 0x656C, 0x6E45, 0x6944, 0x6E61, 0 }; - int use_ascii (int i) { - return ascii_mm[i] + ascii_ii[i]; - } - short int ebcdic_ii[] = - { 0x89D3, 0xE3E3, 0x8593, 0x95C5, 0x89C4, 0x9581, 0 }; - short int ebcdic_mm[] = - { 0xC2C9, 0xC785, 0x95C4, 0x8981, 0x95E2, 0xA8E2, 0 }; - int use_ebcdic (int i) { - return ebcdic_mm[i] + ebcdic_ii[i]; - } - extern int foo; - -int -main () -{ -return use_ascii (foo) == use_ebcdic (foo); - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - if grep BIGenDianSyS conftest.$ac_objext >/dev/null; then - ac_cv_c_bigendian=yes - fi - if grep LiTTleEnDian conftest.$ac_objext >/dev/null ; then - if test "$ac_cv_c_bigendian" = unknown; then - ac_cv_c_bigendian=no - else - # finding both strings is unlikely to happen, but who knows? - ac_cv_c_bigendian=unknown - fi - fi -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -else - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -$ac_includes_default -int -main () -{ - - /* Are we little or big endian? From Harbison&Steele. */ - union - { - long int l; - char c[sizeof (long int)]; - } u; - u.l = 1; - return u.c[sizeof (long int) - 1] == 1; - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_run "$LINENO"; then : - ac_cv_c_bigendian=no -else - ac_cv_c_bigendian=yes -fi -rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ - conftest.$ac_objext conftest.beam conftest.$ac_ext -fi - - fi -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_bigendian" >&5 -$as_echo "$ac_cv_c_bigendian" >&6; } - case $ac_cv_c_bigendian in #( - yes) - $as_echo "#define WORDS_BIGENDIAN 1" >>confdefs.h -;; #( - no) - ;; #( - universal) - -$as_echo "#define AC_APPLE_UNIVERSAL_BUILD 1" >>confdefs.h - - ;; #( - *) - as_fn_error $? "unknown endianness - presetting ac_cv_c_bigendian=no (or yes) will help" "$LINENO" 5 ;; - esac - -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for inline" >&5 -$as_echo_n "checking for inline... " >&6; } -if ${ac_cv_c_inline+:} false; then : - $as_echo_n "(cached) " >&6 -else - ac_cv_c_inline=no -for ac_kw in inline __inline__ __inline; do - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#ifndef __cplusplus -typedef int foo_t; -static $ac_kw foo_t static_foo () {return 0; } -$ac_kw foo_t foo () {return 0; } -#endif - -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - ac_cv_c_inline=$ac_kw -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext - test "$ac_cv_c_inline" != no && break -done - -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_inline" >&5 -$as_echo "$ac_cv_c_inline" >&6; } - -case $ac_cv_c_inline in - inline | yes) ;; - *) - case $ac_cv_c_inline in - no) ac_val=;; - *) ac_val=$ac_cv_c_inline;; - esac - cat >>confdefs.h <<_ACEOF -#ifndef __cplusplus -#define inline $ac_val -#endif -_ACEOF - ;; -esac - -# Check whether --enable-largefile was given. -if test "${enable_largefile+set}" = set; then : - enableval=$enable_largefile; -fi - -if test "$enable_largefile" != no; then - - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for special C compiler options needed for large files" >&5 -$as_echo_n "checking for special C compiler options needed for large files... " >&6; } -if ${ac_cv_sys_largefile_CC+:} false; then : - $as_echo_n "(cached) " >&6 -else - ac_cv_sys_largefile_CC=no - if test "$GCC" != yes; then - ac_save_CC=$CC - while :; do - # IRIX 6.2 and later do not support large files by default, - # so use the C compiler's -n32 option if that helps. - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#include - /* Check that off_t can represent 2**63 - 1 correctly. - We can't simply define LARGE_OFF_T to be 9223372036854775807, - since some C++ compilers masquerading as C compilers - incorrectly reject 9223372036854775807. */ -#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31)) - int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 - && LARGE_OFF_T % 2147483647 == 1) - ? 1 : -1]; -int -main () -{ - - ; - return 0; -} -_ACEOF - if ac_fn_c_try_compile "$LINENO"; then : - break -fi -rm -f core conftest.err conftest.$ac_objext - CC="$CC -n32" - if ac_fn_c_try_compile "$LINENO"; then : - ac_cv_sys_largefile_CC=' -n32'; break -fi -rm -f core conftest.err conftest.$ac_objext - break - done - CC=$ac_save_CC - rm -f conftest.$ac_ext - fi -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sys_largefile_CC" >&5 -$as_echo "$ac_cv_sys_largefile_CC" >&6; } - if test "$ac_cv_sys_largefile_CC" != no; then - CC=$CC$ac_cv_sys_largefile_CC - fi - - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for _FILE_OFFSET_BITS value needed for large files" >&5 -$as_echo_n "checking for _FILE_OFFSET_BITS value needed for large files... " >&6; } -if ${ac_cv_sys_file_offset_bits+:} false; then : - $as_echo_n "(cached) " >&6 -else - while :; do - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#include - /* Check that off_t can represent 2**63 - 1 correctly. - We can't simply define LARGE_OFF_T to be 9223372036854775807, - since some C++ compilers masquerading as C compilers - incorrectly reject 9223372036854775807. */ -#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31)) - int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 - && LARGE_OFF_T % 2147483647 == 1) - ? 1 : -1]; -int -main () -{ - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - ac_cv_sys_file_offset_bits=no; break -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#define _FILE_OFFSET_BITS 64 -#include - /* Check that off_t can represent 2**63 - 1 correctly. - We can't simply define LARGE_OFF_T to be 9223372036854775807, - since some C++ compilers masquerading as C compilers - incorrectly reject 9223372036854775807. */ -#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31)) - int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 - && LARGE_OFF_T % 2147483647 == 1) - ? 1 : -1]; -int -main () -{ - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - ac_cv_sys_file_offset_bits=64; break -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext - ac_cv_sys_file_offset_bits=unknown - break -done -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sys_file_offset_bits" >&5 -$as_echo "$ac_cv_sys_file_offset_bits" >&6; } -case $ac_cv_sys_file_offset_bits in #( - no | unknown) ;; - *) -cat >>confdefs.h <<_ACEOF -#define _FILE_OFFSET_BITS $ac_cv_sys_file_offset_bits -_ACEOF -;; -esac -rm -rf conftest* - if test $ac_cv_sys_file_offset_bits = unknown; then - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for _LARGE_FILES value needed for large files" >&5 -$as_echo_n "checking for _LARGE_FILES value needed for large files... " >&6; } -if ${ac_cv_sys_large_files+:} false; then : - $as_echo_n "(cached) " >&6 -else - while :; do - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#include - /* Check that off_t can represent 2**63 - 1 correctly. - We can't simply define LARGE_OFF_T to be 9223372036854775807, - since some C++ compilers masquerading as C compilers - incorrectly reject 9223372036854775807. */ -#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31)) - int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 - && LARGE_OFF_T % 2147483647 == 1) - ? 1 : -1]; -int -main () -{ - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - ac_cv_sys_large_files=no; break -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#define _LARGE_FILES 1 -#include - /* Check that off_t can represent 2**63 - 1 correctly. - We can't simply define LARGE_OFF_T to be 9223372036854775807, - since some C++ compilers masquerading as C compilers - incorrectly reject 9223372036854775807. */ -#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31)) - int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 - && LARGE_OFF_T % 2147483647 == 1) - ? 1 : -1]; -int -main () -{ - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - ac_cv_sys_large_files=1; break -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext - ac_cv_sys_large_files=unknown - break -done -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sys_large_files" >&5 -$as_echo "$ac_cv_sys_large_files" >&6; } -case $ac_cv_sys_large_files in #( - no | unknown) ;; - *) -cat >>confdefs.h <<_ACEOF -#define _LARGE_FILES $ac_cv_sys_large_files -_ACEOF -;; -esac -rm -rf conftest* - fi - - -fi - - -ac_fn_c_check_member "$LINENO" "struct tm" "tm_gmtoff" "ac_cv_member_struct_tm_tm_gmtoff" "#include -" -if test "x$ac_cv_member_struct_tm_tm_gmtoff" = xyes; then : - have_struct_tm_tm_gmtoff=yes -else - have_struct_tm_tm_gmtoff=no -fi - - -if test "x$have_struct_tm_tm_gmtoff" = "xyes"; then - -$as_echo "#define HAVE_STRUCT_TM_TM_GMTOFF 1" >>confdefs.h - -fi - -# Check size of pointer to decide we need 8 bytes alignment -# adjustment. -# The cast to long int works around a bug in the HP C Compiler -# version HP92453-01 B.11.11.23709.GP, which incorrectly rejects -# declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'. -# This bug is HP SR number 8606223364. -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking size of int *" >&5 -$as_echo_n "checking size of int *... " >&6; } -if ${ac_cv_sizeof_int_p+:} false; then : - $as_echo_n "(cached) " >&6 -else - if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (int *))" "ac_cv_sizeof_int_p" "$ac_includes_default"; then : - -else - if test "$ac_cv_type_int_p" = yes; then - { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error 77 "cannot compute sizeof (int *) -See \`config.log' for more details" "$LINENO" 5; } - else - ac_cv_sizeof_int_p=0 - fi -fi - -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_int_p" >&5 -$as_echo "$ac_cv_sizeof_int_p" >&6; } - - - -cat >>confdefs.h <<_ACEOF -#define SIZEOF_INT_P $ac_cv_sizeof_int_p -_ACEOF - - - -# Checks for library functions. -if test "x$cross_compiling" != "xyes"; then - for ac_header in stdlib.h -do : - ac_fn_c_check_header_mongrel "$LINENO" "stdlib.h" "ac_cv_header_stdlib_h" "$ac_includes_default" -if test "x$ac_cv_header_stdlib_h" = xyes; then : - cat >>confdefs.h <<_ACEOF -#define HAVE_STDLIB_H 1 -_ACEOF - -fi - -done - -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for GNU libc compatible malloc" >&5 -$as_echo_n "checking for GNU libc compatible malloc... " >&6; } -if ${ac_cv_func_malloc_0_nonnull+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test "$cross_compiling" = yes; then : - ac_cv_func_malloc_0_nonnull=no -else - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#if defined STDC_HEADERS || defined HAVE_STDLIB_H -# include -#else -char *malloc (); -#endif - -int -main () -{ -return ! malloc (0); - ; - return 0; -} -_ACEOF -if ac_fn_c_try_run "$LINENO"; then : - ac_cv_func_malloc_0_nonnull=yes -else - ac_cv_func_malloc_0_nonnull=no -fi -rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ - conftest.$ac_objext conftest.beam conftest.$ac_ext -fi - -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_malloc_0_nonnull" >&5 -$as_echo "$ac_cv_func_malloc_0_nonnull" >&6; } -if test $ac_cv_func_malloc_0_nonnull = yes; then : - -$as_echo "#define HAVE_MALLOC 1" >>confdefs.h - -else - $as_echo "#define HAVE_MALLOC 0" >>confdefs.h - - case " $LIBOBJS " in - *" malloc.$ac_objext "* ) ;; - *) LIBOBJS="$LIBOBJS malloc.$ac_objext" - ;; -esac - - -$as_echo "#define malloc rpl_malloc" >>confdefs.h - -fi - - -fi - -for ac_header in unistd.h -do : - ac_fn_c_check_header_mongrel "$LINENO" "unistd.h" "ac_cv_header_unistd_h" "$ac_includes_default" -if test "x$ac_cv_header_unistd_h" = xyes; then : - cat >>confdefs.h <<_ACEOF -#define HAVE_UNISTD_H 1 -_ACEOF - -fi - -done - -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for working chown" >&5 -$as_echo_n "checking for working chown... " >&6; } -if ${ac_cv_func_chown_works+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test "$cross_compiling" = yes; then : - ac_cv_func_chown_works=no -else - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -$ac_includes_default -#include - -int -main () -{ - char *f = "conftest.chown"; - struct stat before, after; - - if (creat (f, 0600) < 0) - return 1; - if (stat (f, &before) < 0) - return 1; - if (chown (f, (uid_t) -1, (gid_t) -1) == -1) - return 1; - if (stat (f, &after) < 0) - return 1; - return ! (before.st_uid == after.st_uid && before.st_gid == after.st_gid); - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_run "$LINENO"; then : - ac_cv_func_chown_works=yes -else - ac_cv_func_chown_works=no -fi -rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ - conftest.$ac_objext conftest.beam conftest.$ac_ext -fi - -rm -f conftest.chown - -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_chown_works" >&5 -$as_echo "$ac_cv_func_chown_works" >&6; } -if test $ac_cv_func_chown_works = yes; then - -$as_echo "#define HAVE_CHOWN 1" >>confdefs.h - -fi - -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for error_at_line" >&5 -$as_echo_n "checking for error_at_line... " >&6; } -if ${ac_cv_lib_error_at_line+:} false; then : - $as_echo_n "(cached) " >&6 -else - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#include -int -main () -{ -error_at_line (0, 0, "", 0, "an error occurred"); - ; - return 0; -} -_ACEOF -if ac_fn_c_try_link "$LINENO"; then : - ac_cv_lib_error_at_line=yes -else - ac_cv_lib_error_at_line=no -fi -rm -f core conftest.err conftest.$ac_objext \ - conftest$ac_exeext conftest.$ac_ext -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_error_at_line" >&5 -$as_echo "$ac_cv_lib_error_at_line" >&6; } -if test $ac_cv_lib_error_at_line = no; then - case " $LIBOBJS " in - *" error.$ac_objext "* ) ;; - *) LIBOBJS="$LIBOBJS error.$ac_objext" - ;; -esac - -fi - -for ac_header in vfork.h -do : - ac_fn_c_check_header_mongrel "$LINENO" "vfork.h" "ac_cv_header_vfork_h" "$ac_includes_default" -if test "x$ac_cv_header_vfork_h" = xyes; then : - cat >>confdefs.h <<_ACEOF -#define HAVE_VFORK_H 1 -_ACEOF - -fi - -done - -for ac_func in fork vfork -do : - as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` -ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" -if eval test \"x\$"$as_ac_var"\" = x"yes"; then : - cat >>confdefs.h <<_ACEOF -#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1 -_ACEOF - -fi -done - -if test "x$ac_cv_func_fork" = xyes; then - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for working fork" >&5 -$as_echo_n "checking for working fork... " >&6; } -if ${ac_cv_func_fork_works+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test "$cross_compiling" = yes; then : - ac_cv_func_fork_works=cross -else - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -$ac_includes_default -int -main () -{ - - /* By Ruediger Kuhlmann. */ - return fork () < 0; - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_run "$LINENO"; then : - ac_cv_func_fork_works=yes -else - ac_cv_func_fork_works=no -fi -rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ - conftest.$ac_objext conftest.beam conftest.$ac_ext -fi - -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_fork_works" >&5 -$as_echo "$ac_cv_func_fork_works" >&6; } - -else - ac_cv_func_fork_works=$ac_cv_func_fork -fi -if test "x$ac_cv_func_fork_works" = xcross; then - case $host in - *-*-amigaos* | *-*-msdosdjgpp*) - # Override, as these systems have only a dummy fork() stub - ac_cv_func_fork_works=no - ;; - *) - ac_cv_func_fork_works=yes - ;; - esac - { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: result $ac_cv_func_fork_works guessed because of cross compilation" >&5 -$as_echo "$as_me: WARNING: result $ac_cv_func_fork_works guessed because of cross compilation" >&2;} -fi -ac_cv_func_vfork_works=$ac_cv_func_vfork -if test "x$ac_cv_func_vfork" = xyes; then - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for working vfork" >&5 -$as_echo_n "checking for working vfork... " >&6; } -if ${ac_cv_func_vfork_works+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test "$cross_compiling" = yes; then : - ac_cv_func_vfork_works=cross -else - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -/* Thanks to Paul Eggert for this test. */ -$ac_includes_default -#include -#ifdef HAVE_VFORK_H -# include -#endif -/* On some sparc systems, changes by the child to local and incoming - argument registers are propagated back to the parent. The compiler - is told about this with #include , but some compilers - (e.g. gcc -O) don't grok . Test for this by using a - static variable whose address is put into a register that is - clobbered by the vfork. */ -static void -#ifdef __cplusplus -sparc_address_test (int arg) -# else -sparc_address_test (arg) int arg; -#endif -{ - static pid_t child; - if (!child) { - child = vfork (); - if (child < 0) { - perror ("vfork"); - _exit(2); - } - if (!child) { - arg = getpid(); - write(-1, "", 0); - _exit (arg); - } - } -} - -int -main () -{ - pid_t parent = getpid (); - pid_t child; - - sparc_address_test (0); - - child = vfork (); - - if (child == 0) { - /* Here is another test for sparc vfork register problems. This - test uses lots of local variables, at least as many local - variables as main has allocated so far including compiler - temporaries. 4 locals are enough for gcc 1.40.3 on a Solaris - 4.1.3 sparc, but we use 8 to be safe. A buggy compiler should - reuse the register of parent for one of the local variables, - since it will think that parent can't possibly be used any more - in this routine. Assigning to the local variable will thus - munge parent in the parent process. */ - pid_t - p = getpid(), p1 = getpid(), p2 = getpid(), p3 = getpid(), - p4 = getpid(), p5 = getpid(), p6 = getpid(), p7 = getpid(); - /* Convince the compiler that p..p7 are live; otherwise, it might - use the same hardware register for all 8 local variables. */ - if (p != p1 || p != p2 || p != p3 || p != p4 - || p != p5 || p != p6 || p != p7) - _exit(1); - - /* On some systems (e.g. IRIX 3.3), vfork doesn't separate parent - from child file descriptors. If the child closes a descriptor - before it execs or exits, this munges the parent's descriptor - as well. Test for this by closing stdout in the child. */ - _exit(close(fileno(stdout)) != 0); - } else { - int status; - struct stat st; - - while (wait(&status) != child) - ; - return ( - /* Was there some problem with vforking? */ - child < 0 - - /* Did the child fail? (This shouldn't happen.) */ - || status - - /* Did the vfork/compiler bug occur? */ - || parent != getpid() - - /* Did the file descriptor bug occur? */ - || fstat(fileno(stdout), &st) != 0 - ); - } -} -_ACEOF -if ac_fn_c_try_run "$LINENO"; then : - ac_cv_func_vfork_works=yes -else - ac_cv_func_vfork_works=no -fi -rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ - conftest.$ac_objext conftest.beam conftest.$ac_ext -fi - -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_vfork_works" >&5 -$as_echo "$ac_cv_func_vfork_works" >&6; } - -fi; -if test "x$ac_cv_func_fork_works" = xcross; then - ac_cv_func_vfork_works=$ac_cv_func_vfork - { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: result $ac_cv_func_vfork_works guessed because of cross compilation" >&5 -$as_echo "$as_me: WARNING: result $ac_cv_func_vfork_works guessed because of cross compilation" >&2;} -fi - -if test "x$ac_cv_func_vfork_works" = xyes; then - -$as_echo "#define HAVE_WORKING_VFORK 1" >>confdefs.h - -else - -$as_echo "#define vfork fork" >>confdefs.h - -fi -if test "x$ac_cv_func_fork_works" = xyes; then - -$as_echo "#define HAVE_WORKING_FORK 1" >>confdefs.h - -fi - -# Don't check realloc, since LeakSanitizer detects memory leak during check -# AC_FUNC_REALLOC -ac_fn_c_check_decl "$LINENO" "strerror_r" "ac_cv_have_decl_strerror_r" "$ac_includes_default" -if test "x$ac_cv_have_decl_strerror_r" = xyes; then : - ac_have_decl=1 -else - ac_have_decl=0 -fi - -cat >>confdefs.h <<_ACEOF -#define HAVE_DECL_STRERROR_R $ac_have_decl -_ACEOF - -for ac_func in strerror_r -do : - ac_fn_c_check_func "$LINENO" "strerror_r" "ac_cv_func_strerror_r" -if test "x$ac_cv_func_strerror_r" = xyes; then : - cat >>confdefs.h <<_ACEOF -#define HAVE_STRERROR_R 1 -_ACEOF - -fi -done - -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether strerror_r returns char *" >&5 -$as_echo_n "checking whether strerror_r returns char *... " >&6; } -if ${ac_cv_func_strerror_r_char_p+:} false; then : - $as_echo_n "(cached) " >&6 -else - - ac_cv_func_strerror_r_char_p=no - if test $ac_cv_have_decl_strerror_r = yes; then - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -$ac_includes_default -int -main () -{ - - char buf[100]; - char x = *strerror_r (0, buf, sizeof buf); - char *p = strerror_r (0, buf, sizeof buf); - return !p || x; - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - ac_cv_func_strerror_r_char_p=yes -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext - else - # strerror_r is not declared. Choose between - # systems that have relatively inaccessible declarations for the - # function. BeOS and DEC UNIX 4.0 fall in this category, but the - # former has a strerror_r that returns char*, while the latter - # has a strerror_r that returns `int'. - # This test should segfault on the DEC system. - if test "$cross_compiling" = yes; then : - : -else - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -$ac_includes_default - extern char *strerror_r (); -int -main () -{ -char buf[100]; - char x = *strerror_r (0, buf, sizeof buf); - return ! isalpha (x); - ; - return 0; -} -_ACEOF -if ac_fn_c_try_run "$LINENO"; then : - ac_cv_func_strerror_r_char_p=yes -fi -rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ - conftest.$ac_objext conftest.beam conftest.$ac_ext -fi - - fi - -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_strerror_r_char_p" >&5 -$as_echo "$ac_cv_func_strerror_r_char_p" >&6; } -if test $ac_cv_func_strerror_r_char_p = yes; then - -$as_echo "#define STRERROR_R_CHAR_P 1" >>confdefs.h - -fi - - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for working strnlen" >&5 -$as_echo_n "checking for working strnlen... " >&6; } -if ${ac_cv_func_strnlen_working+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test "$cross_compiling" = yes; then : - # Guess no on AIX systems, yes otherwise. - case "$host_os" in - aix*) ac_cv_func_strnlen_working=no;; - *) ac_cv_func_strnlen_working=yes;; - esac -else - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -$ac_includes_default -int -main () -{ - -#define S "foobar" -#define S_LEN (sizeof S - 1) - - /* At least one implementation is buggy: that of AIX 4.3 would - give strnlen (S, 1) == 3. */ - - int i; - for (i = 0; i < S_LEN + 1; ++i) - { - int expected = i <= S_LEN ? i : S_LEN; - if (strnlen (S, i) != expected) - return 1; - } - return 0; - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_run "$LINENO"; then : - ac_cv_func_strnlen_working=yes -else - ac_cv_func_strnlen_working=no -fi -rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ - conftest.$ac_objext conftest.beam conftest.$ac_ext -fi - -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_strnlen_working" >&5 -$as_echo "$ac_cv_func_strnlen_working" >&6; } -test $ac_cv_func_strnlen_working = no && case " $LIBOBJS " in - *" strnlen.$ac_objext "* ) ;; - *) LIBOBJS="$LIBOBJS strnlen.$ac_objext" - ;; -esac - - - -for ac_func in \ - _Exit \ - accept4 \ - dup2 \ - getcwd \ - getpwnam \ - localtime_r \ - memchr \ - memmove \ - memset \ - socket \ - sqrt \ - strchr \ - strdup \ - strerror \ - strndup \ - strstr \ - strtol \ - strtoul \ - timegm \ - -do : - as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` -ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" -if eval test \"x\$"$as_ac_var"\" = x"yes"; then : - cat >>confdefs.h <<_ACEOF -#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1 -_ACEOF - -fi -done - - -# timerfd_create was added in linux kernel 2.6.25 - -ac_fn_c_check_func "$LINENO" "timerfd_create" "ac_cv_func_timerfd_create" -if test "x$ac_cv_func_timerfd_create" = xyes; then : - have_timerfd_create=yes -else - have_timerfd_create=no -fi - - - -# Checks for epoll availability, primarily for examples/tiny-nghttpd - ax_have_epoll_cppflags="${CPPFLAGS}" - ac_fn_c_check_header_mongrel "$LINENO" "linux/version.h" "ac_cv_header_linux_version_h" "$ac_includes_default" -if test "x$ac_cv_header_linux_version_h" = xyes; then : - CPPFLAGS="${CPPFLAGS} -DHAVE_LINUX_VERSION_H" -fi - - - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for Linux epoll(7) interface" >&5 -$as_echo_n "checking for Linux epoll(7) interface... " >&6; } - if ${ax_cv_have_epoll+:} false; then : - $as_echo_n "(cached) " >&6 -else - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - #include -#ifdef HAVE_LINUX_VERSION_H -# include -# if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,45) -# error linux kernel version is too old to have epoll -# endif -#endif - -int -main () -{ -int fd, rc; -struct epoll_event ev; -fd = epoll_create(128); -rc = epoll_wait(fd, &ev, 1, 0); - ; - return 0; -} -_ACEOF -if ac_fn_c_try_link "$LINENO"; then : - ax_cv_have_epoll=yes -else - ax_cv_have_epoll=no -fi -rm -f core conftest.err conftest.$ac_objext \ - conftest$ac_exeext conftest.$ac_ext -fi - - CPPFLAGS="${ax_have_epoll_cppflags}" - if test "${ax_cv_have_epoll}" = "yes"; then : - { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 -$as_echo "yes" >&6; } -have_epoll=yes -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -have_epoll=no -fi - - - if test "x${have_epoll}" = "xyes" && - test "x${have_timerfd_create}" = "xyes"; then - ENABLE_TINY_NGHTTPD_TRUE= - ENABLE_TINY_NGHTTPD_FALSE='#' -else - ENABLE_TINY_NGHTTPD_TRUE='#' - ENABLE_TINY_NGHTTPD_FALSE= -fi - - -ac_save_CFLAGS=$CFLAGS -CFLAGS= - -if test "x$werror" != "xno"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -Wall" >&5 -$as_echo_n "checking whether C compiler accepts -Wall... " >&6; } -if ${ax_cv_check_cflags___Wall+:} false; then : - $as_echo_n "(cached) " >&6 -else - - ax_check_save_flags=$CFLAGS - CFLAGS="$CFLAGS -Wall" - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main () -{ - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - ax_cv_check_cflags___Wall=yes -else - ax_cv_check_cflags___Wall=no -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext - CFLAGS=$ax_check_save_flags -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags___Wall" >&5 -$as_echo "$ax_cv_check_cflags___Wall" >&6; } -if test x"$ax_cv_check_cflags___Wall" = xyes; then : - CFLAGS="$CFLAGS -Wall" -else - : -fi - - { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -Wextra" >&5 -$as_echo_n "checking whether C compiler accepts -Wextra... " >&6; } -if ${ax_cv_check_cflags___Wextra+:} false; then : - $as_echo_n "(cached) " >&6 -else - - ax_check_save_flags=$CFLAGS - CFLAGS="$CFLAGS -Wextra" - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main () -{ - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - ax_cv_check_cflags___Wextra=yes -else - ax_cv_check_cflags___Wextra=no -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext - CFLAGS=$ax_check_save_flags -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags___Wextra" >&5 -$as_echo "$ax_cv_check_cflags___Wextra" >&6; } -if test x"$ax_cv_check_cflags___Wextra" = xyes; then : - CFLAGS="$CFLAGS -Wextra" -else - : -fi - - { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -Werror" >&5 -$as_echo_n "checking whether C compiler accepts -Werror... " >&6; } -if ${ax_cv_check_cflags___Werror+:} false; then : - $as_echo_n "(cached) " >&6 -else - - ax_check_save_flags=$CFLAGS - CFLAGS="$CFLAGS -Werror" - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main () -{ - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - ax_cv_check_cflags___Werror=yes -else - ax_cv_check_cflags___Werror=no -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext - CFLAGS=$ax_check_save_flags -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags___Werror" >&5 -$as_echo "$ax_cv_check_cflags___Werror" >&6; } -if test x"$ax_cv_check_cflags___Werror" = xyes; then : - CFLAGS="$CFLAGS -Werror" -else - : -fi - - { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -Wmissing-prototypes" >&5 -$as_echo_n "checking whether C compiler accepts -Wmissing-prototypes... " >&6; } -if ${ax_cv_check_cflags___Wmissing_prototypes+:} false; then : - $as_echo_n "(cached) " >&6 -else - - ax_check_save_flags=$CFLAGS - CFLAGS="$CFLAGS -Wmissing-prototypes" - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main () -{ - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - ax_cv_check_cflags___Wmissing_prototypes=yes -else - ax_cv_check_cflags___Wmissing_prototypes=no -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext - CFLAGS=$ax_check_save_flags -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags___Wmissing_prototypes" >&5 -$as_echo "$ax_cv_check_cflags___Wmissing_prototypes" >&6; } -if test x"$ax_cv_check_cflags___Wmissing_prototypes" = xyes; then : - CFLAGS="$CFLAGS -Wmissing-prototypes" -else - : -fi - - { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -Wstrict-prototypes" >&5 -$as_echo_n "checking whether C compiler accepts -Wstrict-prototypes... " >&6; } -if ${ax_cv_check_cflags___Wstrict_prototypes+:} false; then : - $as_echo_n "(cached) " >&6 -else - - ax_check_save_flags=$CFLAGS - CFLAGS="$CFLAGS -Wstrict-prototypes" - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main () -{ - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - ax_cv_check_cflags___Wstrict_prototypes=yes -else - ax_cv_check_cflags___Wstrict_prototypes=no -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext - CFLAGS=$ax_check_save_flags -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags___Wstrict_prototypes" >&5 -$as_echo "$ax_cv_check_cflags___Wstrict_prototypes" >&6; } -if test x"$ax_cv_check_cflags___Wstrict_prototypes" = xyes; then : - CFLAGS="$CFLAGS -Wstrict-prototypes" -else - : -fi - - { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -Wmissing-declarations" >&5 -$as_echo_n "checking whether C compiler accepts -Wmissing-declarations... " >&6; } -if ${ax_cv_check_cflags___Wmissing_declarations+:} false; then : - $as_echo_n "(cached) " >&6 -else - - ax_check_save_flags=$CFLAGS - CFLAGS="$CFLAGS -Wmissing-declarations" - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main () -{ - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - ax_cv_check_cflags___Wmissing_declarations=yes -else - ax_cv_check_cflags___Wmissing_declarations=no -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext - CFLAGS=$ax_check_save_flags -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags___Wmissing_declarations" >&5 -$as_echo "$ax_cv_check_cflags___Wmissing_declarations" >&6; } -if test x"$ax_cv_check_cflags___Wmissing_declarations" = xyes; then : - CFLAGS="$CFLAGS -Wmissing-declarations" -else - : -fi - - { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -Wpointer-arith" >&5 -$as_echo_n "checking whether C compiler accepts -Wpointer-arith... " >&6; } -if ${ax_cv_check_cflags___Wpointer_arith+:} false; then : - $as_echo_n "(cached) " >&6 -else - - ax_check_save_flags=$CFLAGS - CFLAGS="$CFLAGS -Wpointer-arith" - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main () -{ - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - ax_cv_check_cflags___Wpointer_arith=yes -else - ax_cv_check_cflags___Wpointer_arith=no -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext - CFLAGS=$ax_check_save_flags -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags___Wpointer_arith" >&5 -$as_echo "$ax_cv_check_cflags___Wpointer_arith" >&6; } -if test x"$ax_cv_check_cflags___Wpointer_arith" = xyes; then : - CFLAGS="$CFLAGS -Wpointer-arith" -else - : -fi - - { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -Wdeclaration-after-statement" >&5 -$as_echo_n "checking whether C compiler accepts -Wdeclaration-after-statement... " >&6; } -if ${ax_cv_check_cflags___Wdeclaration_after_statement+:} false; then : - $as_echo_n "(cached) " >&6 -else - - ax_check_save_flags=$CFLAGS - CFLAGS="$CFLAGS -Wdeclaration-after-statement" - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main () -{ - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - ax_cv_check_cflags___Wdeclaration_after_statement=yes -else - ax_cv_check_cflags___Wdeclaration_after_statement=no -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext - CFLAGS=$ax_check_save_flags -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags___Wdeclaration_after_statement" >&5 -$as_echo "$ax_cv_check_cflags___Wdeclaration_after_statement" >&6; } -if test x"$ax_cv_check_cflags___Wdeclaration_after_statement" = xyes; then : - CFLAGS="$CFLAGS -Wdeclaration-after-statement" -else - : -fi - - { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -Wformat-security" >&5 -$as_echo_n "checking whether C compiler accepts -Wformat-security... " >&6; } -if ${ax_cv_check_cflags___Wformat_security+:} false; then : - $as_echo_n "(cached) " >&6 -else - - ax_check_save_flags=$CFLAGS - CFLAGS="$CFLAGS -Wformat-security" - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main () -{ - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - ax_cv_check_cflags___Wformat_security=yes -else - ax_cv_check_cflags___Wformat_security=no -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext - CFLAGS=$ax_check_save_flags -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags___Wformat_security" >&5 -$as_echo "$ax_cv_check_cflags___Wformat_security" >&6; } -if test x"$ax_cv_check_cflags___Wformat_security" = xyes; then : - CFLAGS="$CFLAGS -Wformat-security" -else - : -fi - - { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -Wwrite-strings" >&5 -$as_echo_n "checking whether C compiler accepts -Wwrite-strings... " >&6; } -if ${ax_cv_check_cflags___Wwrite_strings+:} false; then : - $as_echo_n "(cached) " >&6 -else - - ax_check_save_flags=$CFLAGS - CFLAGS="$CFLAGS -Wwrite-strings" - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main () -{ - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - ax_cv_check_cflags___Wwrite_strings=yes -else - ax_cv_check_cflags___Wwrite_strings=no -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext - CFLAGS=$ax_check_save_flags -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags___Wwrite_strings" >&5 -$as_echo "$ax_cv_check_cflags___Wwrite_strings" >&6; } -if test x"$ax_cv_check_cflags___Wwrite_strings" = xyes; then : - CFLAGS="$CFLAGS -Wwrite-strings" -else - : -fi - - { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -Wshadow" >&5 -$as_echo_n "checking whether C compiler accepts -Wshadow... " >&6; } -if ${ax_cv_check_cflags___Wshadow+:} false; then : - $as_echo_n "(cached) " >&6 -else - - ax_check_save_flags=$CFLAGS - CFLAGS="$CFLAGS -Wshadow" - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main () -{ - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - ax_cv_check_cflags___Wshadow=yes -else - ax_cv_check_cflags___Wshadow=no -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext - CFLAGS=$ax_check_save_flags -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags___Wshadow" >&5 -$as_echo "$ax_cv_check_cflags___Wshadow" >&6; } -if test x"$ax_cv_check_cflags___Wshadow" = xyes; then : - CFLAGS="$CFLAGS -Wshadow" -else - : -fi - - { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -Winline" >&5 -$as_echo_n "checking whether C compiler accepts -Winline... " >&6; } -if ${ax_cv_check_cflags___Winline+:} false; then : - $as_echo_n "(cached) " >&6 -else - - ax_check_save_flags=$CFLAGS - CFLAGS="$CFLAGS -Winline" - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main () -{ - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - ax_cv_check_cflags___Winline=yes -else - ax_cv_check_cflags___Winline=no -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext - CFLAGS=$ax_check_save_flags -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags___Winline" >&5 -$as_echo "$ax_cv_check_cflags___Winline" >&6; } -if test x"$ax_cv_check_cflags___Winline" = xyes; then : - CFLAGS="$CFLAGS -Winline" -else - : -fi - - { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -Wnested-externs" >&5 -$as_echo_n "checking whether C compiler accepts -Wnested-externs... " >&6; } -if ${ax_cv_check_cflags___Wnested_externs+:} false; then : - $as_echo_n "(cached) " >&6 -else - - ax_check_save_flags=$CFLAGS - CFLAGS="$CFLAGS -Wnested-externs" - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main () -{ - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - ax_cv_check_cflags___Wnested_externs=yes -else - ax_cv_check_cflags___Wnested_externs=no -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext - CFLAGS=$ax_check_save_flags -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags___Wnested_externs" >&5 -$as_echo "$ax_cv_check_cflags___Wnested_externs" >&6; } -if test x"$ax_cv_check_cflags___Wnested_externs" = xyes; then : - CFLAGS="$CFLAGS -Wnested-externs" -else - : -fi - - { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -Wfloat-equal" >&5 -$as_echo_n "checking whether C compiler accepts -Wfloat-equal... " >&6; } -if ${ax_cv_check_cflags___Wfloat_equal+:} false; then : - $as_echo_n "(cached) " >&6 -else - - ax_check_save_flags=$CFLAGS - CFLAGS="$CFLAGS -Wfloat-equal" - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main () -{ - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - ax_cv_check_cflags___Wfloat_equal=yes -else - ax_cv_check_cflags___Wfloat_equal=no -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext - CFLAGS=$ax_check_save_flags -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags___Wfloat_equal" >&5 -$as_echo "$ax_cv_check_cflags___Wfloat_equal" >&6; } -if test x"$ax_cv_check_cflags___Wfloat_equal" = xyes; then : - CFLAGS="$CFLAGS -Wfloat-equal" -else - : -fi - - { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -Wundef" >&5 -$as_echo_n "checking whether C compiler accepts -Wundef... " >&6; } -if ${ax_cv_check_cflags___Wundef+:} false; then : - $as_echo_n "(cached) " >&6 -else - - ax_check_save_flags=$CFLAGS - CFLAGS="$CFLAGS -Wundef" - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main () -{ - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - ax_cv_check_cflags___Wundef=yes -else - ax_cv_check_cflags___Wundef=no -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext - CFLAGS=$ax_check_save_flags -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags___Wundef" >&5 -$as_echo "$ax_cv_check_cflags___Wundef" >&6; } -if test x"$ax_cv_check_cflags___Wundef" = xyes; then : - CFLAGS="$CFLAGS -Wundef" -else - : -fi - - { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -Wendif-labels" >&5 -$as_echo_n "checking whether C compiler accepts -Wendif-labels... " >&6; } -if ${ax_cv_check_cflags___Wendif_labels+:} false; then : - $as_echo_n "(cached) " >&6 -else - - ax_check_save_flags=$CFLAGS - CFLAGS="$CFLAGS -Wendif-labels" - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main () -{ - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - ax_cv_check_cflags___Wendif_labels=yes -else - ax_cv_check_cflags___Wendif_labels=no -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext - CFLAGS=$ax_check_save_flags -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags___Wendif_labels" >&5 -$as_echo "$ax_cv_check_cflags___Wendif_labels" >&6; } -if test x"$ax_cv_check_cflags___Wendif_labels" = xyes; then : - CFLAGS="$CFLAGS -Wendif-labels" -else - : -fi - - { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -Wempty-body" >&5 -$as_echo_n "checking whether C compiler accepts -Wempty-body... " >&6; } -if ${ax_cv_check_cflags___Wempty_body+:} false; then : - $as_echo_n "(cached) " >&6 -else - - ax_check_save_flags=$CFLAGS - CFLAGS="$CFLAGS -Wempty-body" - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main () -{ - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - ax_cv_check_cflags___Wempty_body=yes -else - ax_cv_check_cflags___Wempty_body=no -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext - CFLAGS=$ax_check_save_flags -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags___Wempty_body" >&5 -$as_echo "$ax_cv_check_cflags___Wempty_body" >&6; } -if test x"$ax_cv_check_cflags___Wempty_body" = xyes; then : - CFLAGS="$CFLAGS -Wempty-body" -else - : -fi - - { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -Wcast-align" >&5 -$as_echo_n "checking whether C compiler accepts -Wcast-align... " >&6; } -if ${ax_cv_check_cflags___Wcast_align+:} false; then : - $as_echo_n "(cached) " >&6 -else - - ax_check_save_flags=$CFLAGS - CFLAGS="$CFLAGS -Wcast-align" - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main () -{ - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - ax_cv_check_cflags___Wcast_align=yes -else - ax_cv_check_cflags___Wcast_align=no -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext - CFLAGS=$ax_check_save_flags -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags___Wcast_align" >&5 -$as_echo "$ax_cv_check_cflags___Wcast_align" >&6; } -if test x"$ax_cv_check_cflags___Wcast_align" = xyes; then : - CFLAGS="$CFLAGS -Wcast-align" -else - : -fi - - { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -Wclobbered" >&5 -$as_echo_n "checking whether C compiler accepts -Wclobbered... " >&6; } -if ${ax_cv_check_cflags___Wclobbered+:} false; then : - $as_echo_n "(cached) " >&6 -else - - ax_check_save_flags=$CFLAGS - CFLAGS="$CFLAGS -Wclobbered" - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main () -{ - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - ax_cv_check_cflags___Wclobbered=yes -else - ax_cv_check_cflags___Wclobbered=no -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext - CFLAGS=$ax_check_save_flags -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags___Wclobbered" >&5 -$as_echo "$ax_cv_check_cflags___Wclobbered" >&6; } -if test x"$ax_cv_check_cflags___Wclobbered" = xyes; then : - CFLAGS="$CFLAGS -Wclobbered" -else - : -fi - - { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -Wvla" >&5 -$as_echo_n "checking whether C compiler accepts -Wvla... " >&6; } -if ${ax_cv_check_cflags___Wvla+:} false; then : - $as_echo_n "(cached) " >&6 -else - - ax_check_save_flags=$CFLAGS - CFLAGS="$CFLAGS -Wvla" - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main () -{ - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - ax_cv_check_cflags___Wvla=yes -else - ax_cv_check_cflags___Wvla=no -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext - CFLAGS=$ax_check_save_flags -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags___Wvla" >&5 -$as_echo "$ax_cv_check_cflags___Wvla" >&6; } -if test x"$ax_cv_check_cflags___Wvla" = xyes; then : - CFLAGS="$CFLAGS -Wvla" -else - : -fi - - { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -Wpragmas" >&5 -$as_echo_n "checking whether C compiler accepts -Wpragmas... " >&6; } -if ${ax_cv_check_cflags___Wpragmas+:} false; then : - $as_echo_n "(cached) " >&6 -else - - ax_check_save_flags=$CFLAGS - CFLAGS="$CFLAGS -Wpragmas" - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main () -{ - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - ax_cv_check_cflags___Wpragmas=yes -else - ax_cv_check_cflags___Wpragmas=no -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext - CFLAGS=$ax_check_save_flags -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags___Wpragmas" >&5 -$as_echo "$ax_cv_check_cflags___Wpragmas" >&6; } -if test x"$ax_cv_check_cflags___Wpragmas" = xyes; then : - CFLAGS="$CFLAGS -Wpragmas" -else - : -fi - - { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -Wunreachable-code" >&5 -$as_echo_n "checking whether C compiler accepts -Wunreachable-code... " >&6; } -if ${ax_cv_check_cflags___Wunreachable_code+:} false; then : - $as_echo_n "(cached) " >&6 -else - - ax_check_save_flags=$CFLAGS - CFLAGS="$CFLAGS -Wunreachable-code" - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main () -{ - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - ax_cv_check_cflags___Wunreachable_code=yes -else - ax_cv_check_cflags___Wunreachable_code=no -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext - CFLAGS=$ax_check_save_flags -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags___Wunreachable_code" >&5 -$as_echo "$ax_cv_check_cflags___Wunreachable_code" >&6; } -if test x"$ax_cv_check_cflags___Wunreachable_code" = xyes; then : - CFLAGS="$CFLAGS -Wunreachable-code" -else - : -fi - - { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -Waddress" >&5 -$as_echo_n "checking whether C compiler accepts -Waddress... " >&6; } -if ${ax_cv_check_cflags___Waddress+:} false; then : - $as_echo_n "(cached) " >&6 -else - - ax_check_save_flags=$CFLAGS - CFLAGS="$CFLAGS -Waddress" - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main () -{ - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - ax_cv_check_cflags___Waddress=yes -else - ax_cv_check_cflags___Waddress=no -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext - CFLAGS=$ax_check_save_flags -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags___Waddress" >&5 -$as_echo "$ax_cv_check_cflags___Waddress" >&6; } -if test x"$ax_cv_check_cflags___Waddress" = xyes; then : - CFLAGS="$CFLAGS -Waddress" -else - : -fi - - { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -Wattributes" >&5 -$as_echo_n "checking whether C compiler accepts -Wattributes... " >&6; } -if ${ax_cv_check_cflags___Wattributes+:} false; then : - $as_echo_n "(cached) " >&6 -else - - ax_check_save_flags=$CFLAGS - CFLAGS="$CFLAGS -Wattributes" - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main () -{ - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - ax_cv_check_cflags___Wattributes=yes -else - ax_cv_check_cflags___Wattributes=no -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext - CFLAGS=$ax_check_save_flags -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags___Wattributes" >&5 -$as_echo "$ax_cv_check_cflags___Wattributes" >&6; } -if test x"$ax_cv_check_cflags___Wattributes" = xyes; then : - CFLAGS="$CFLAGS -Wattributes" -else - : -fi - - { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -Wdiv-by-zero" >&5 -$as_echo_n "checking whether C compiler accepts -Wdiv-by-zero... " >&6; } -if ${ax_cv_check_cflags___Wdiv_by_zero+:} false; then : - $as_echo_n "(cached) " >&6 -else - - ax_check_save_flags=$CFLAGS - CFLAGS="$CFLAGS -Wdiv-by-zero" - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main () -{ - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - ax_cv_check_cflags___Wdiv_by_zero=yes -else - ax_cv_check_cflags___Wdiv_by_zero=no -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext - CFLAGS=$ax_check_save_flags -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags___Wdiv_by_zero" >&5 -$as_echo "$ax_cv_check_cflags___Wdiv_by_zero" >&6; } -if test x"$ax_cv_check_cflags___Wdiv_by_zero" = xyes; then : - CFLAGS="$CFLAGS -Wdiv-by-zero" -else - : -fi - - { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -Wshorten-64-to-32" >&5 -$as_echo_n "checking whether C compiler accepts -Wshorten-64-to-32... " >&6; } -if ${ax_cv_check_cflags___Wshorten_64_to_32+:} false; then : - $as_echo_n "(cached) " >&6 -else - - ax_check_save_flags=$CFLAGS - CFLAGS="$CFLAGS -Wshorten-64-to-32" - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main () -{ - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - ax_cv_check_cflags___Wshorten_64_to_32=yes -else - ax_cv_check_cflags___Wshorten_64_to_32=no -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext - CFLAGS=$ax_check_save_flags -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags___Wshorten_64_to_32" >&5 -$as_echo "$ax_cv_check_cflags___Wshorten_64_to_32" >&6; } -if test x"$ax_cv_check_cflags___Wshorten_64_to_32" = xyes; then : - CFLAGS="$CFLAGS -Wshorten-64-to-32" -else - : -fi - - - # Only work with Clang for the moment - { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -Wheader-guard" >&5 -$as_echo_n "checking whether C compiler accepts -Wheader-guard... " >&6; } -if ${ax_cv_check_cflags___Wheader_guard+:} false; then : - $as_echo_n "(cached) " >&6 -else - - ax_check_save_flags=$CFLAGS - CFLAGS="$CFLAGS -Wheader-guard" - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main () -{ - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - ax_cv_check_cflags___Wheader_guard=yes -else - ax_cv_check_cflags___Wheader_guard=no -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext - CFLAGS=$ax_check_save_flags -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags___Wheader_guard" >&5 -$as_echo "$ax_cv_check_cflags___Wheader_guard" >&6; } -if test x"$ax_cv_check_cflags___Wheader_guard" = xyes; then : - CFLAGS="$CFLAGS -Wheader-guard" -else - : -fi - -fi - -WARNCFLAGS=$CFLAGS -CFLAGS=$ac_save_CFLAGS - - - -if test "x$debug" != "xno"; then - -$as_echo "#define DEBUGBUILD 1" >>confdefs.h - -fi - -enable_threads=yes -# Some platform does not have working std::future. We disable -# threading for those platforms. -if test "x$threads" != "xyes" || - test "x$have_std_future" != "xyes"; then - enable_threads=no - -$as_echo "#define NOTHREADS 1" >>confdefs.h - -fi - - - - -ac_config_files="$ac_config_files Makefile lib/Makefile lib/libnghttp2.pc lib/includes/Makefile lib/includes/nghttp2/nghttp2ver.h tests/Makefile tests/testdata/Makefile third-party/Makefile src/Makefile src/includes/Makefile src/libnghttp2_asio.pc examples/Makefile python/Makefile python/setup.py integration-tests/Makefile integration-tests/config.go integration-tests/setenv doc/Makefile doc/conf.py doc/index.rst doc/package_README.rst doc/tutorial-client.rst doc/tutorial-server.rst doc/tutorial-hpack.rst doc/nghttpx-howto.rst doc/h2load-howto.rst doc/libnghttp2_asio.rst doc/python-apiref.rst doc/building-android-binary.rst doc/nghttp2.h.rst doc/nghttp2ver.h.rst doc/asio_http2.h.rst doc/asio_http2_server.h.rst doc/asio_http2_client.h.rst doc/contribute.rst contrib/Makefile" - -cat >confcache <<\_ACEOF -# This file is a shell script that caches the results of configure -# tests run on this system so they can be shared between configure -# scripts and configure runs, see configure's option --config-cache. -# It is not useful on other systems. If it contains results you don't -# want to keep, you may remove or edit it. -# -# config.status only pays attention to the cache file if you give it -# the --recheck option to rerun configure. -# -# `ac_cv_env_foo' variables (set or unset) will be overridden when -# loading this file, other *unset* `ac_cv_foo' will be assigned the -# following values. - -_ACEOF - -# The following way of writing the cache mishandles newlines in values, -# but we know of no workaround that is simple, portable, and efficient. -# So, we kill variables containing newlines. -# Ultrix sh set writes to stderr and can't be redirected directly, -# and sets the high bit in the cache file unless we assign to the vars. -( - for ac_var in `(set) 2>&1 | sed -n 's/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'`; do - eval ac_val=\$$ac_var - case $ac_val in #( - *${as_nl}*) - case $ac_var in #( - *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 -$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; - esac - case $ac_var in #( - _ | IFS | as_nl) ;; #( - BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( - *) { eval $ac_var=; unset $ac_var;} ;; - esac ;; - esac - done - - (set) 2>&1 | - case $as_nl`(ac_space=' '; set) 2>&1` in #( - *${as_nl}ac_space=\ *) - # `set' does not quote correctly, so add quotes: double-quote - # substitution turns \\\\ into \\, and sed turns \\ into \. - sed -n \ - "s/'/'\\\\''/g; - s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p" - ;; #( - *) - # `set' quotes correctly as required by POSIX, so do not add quotes. - sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" - ;; - esac | - sort -) | - sed ' - /^ac_cv_env_/b end - t clear - :clear - s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/ - t end - s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/ - :end' >>confcache -if diff "$cache_file" confcache >/dev/null 2>&1; then :; else - if test -w "$cache_file"; then - if test "x$cache_file" != "x/dev/null"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: updating cache $cache_file" >&5 -$as_echo "$as_me: updating cache $cache_file" >&6;} - if test ! -f "$cache_file" || test -h "$cache_file"; then - cat confcache >"$cache_file" - else - case $cache_file in #( - */* | ?:*) - mv -f confcache "$cache_file"$$ && - mv -f "$cache_file"$$ "$cache_file" ;; #( - *) - mv -f confcache "$cache_file" ;; - esac - fi - fi - else - { $as_echo "$as_me:${as_lineno-$LINENO}: not updating unwritable cache $cache_file" >&5 -$as_echo "$as_me: not updating unwritable cache $cache_file" >&6;} - fi -fi -rm -f confcache - -test "x$prefix" = xNONE && prefix=$ac_default_prefix -# Let make expand exec_prefix. -test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' - -DEFS=-DHAVE_CONFIG_H - -ac_libobjs= -ac_ltlibobjs= -U= -for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue - # 1. Remove the extension, and $U if already installed. - ac_script='s/\$U\././;s/\.o$//;s/\.obj$//' - ac_i=`$as_echo "$ac_i" | sed "$ac_script"` - # 2. Prepend LIBOBJDIR. When used with automake>=1.10 LIBOBJDIR - # will be set to the directory where LIBOBJS objects are built. - as_fn_append ac_libobjs " \${LIBOBJDIR}$ac_i\$U.$ac_objext" - as_fn_append ac_ltlibobjs " \${LIBOBJDIR}$ac_i"'$U.lo' -done -LIBOBJS=$ac_libobjs - -LTLIBOBJS=$ac_ltlibobjs - - -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking that generated files are newer than configure" >&5 -$as_echo_n "checking that generated files are newer than configure... " >&6; } - if test -n "$am_sleep_pid"; then - # Hide warnings about reused PIDs. - wait $am_sleep_pid 2>/dev/null - fi - { $as_echo "$as_me:${as_lineno-$LINENO}: result: done" >&5 -$as_echo "done" >&6; } -if test -z "${AMDEP_TRUE}" && test -z "${AMDEP_FALSE}"; then - as_fn_error $? "conditional \"AMDEP\" was never defined. -Usually this means the macro was only invoked conditionally." "$LINENO" 5 -fi -if test -z "${am__fastdepCC_TRUE}" && test -z "${am__fastdepCC_FALSE}"; then - as_fn_error $? "conditional \"am__fastdepCC\" was never defined. -Usually this means the macro was only invoked conditionally." "$LINENO" 5 -fi - if test -n "$EXEEXT"; then - am__EXEEXT_TRUE= - am__EXEEXT_FALSE='#' -else - am__EXEEXT_TRUE='#' - am__EXEEXT_FALSE= -fi - -if test -z "${am__fastdepCXX_TRUE}" && test -z "${am__fastdepCXX_FALSE}"; then - as_fn_error $? "conditional \"am__fastdepCXX\" was never defined. -Usually this means the macro was only invoked conditionally." "$LINENO" 5 -fi -if test -z "${HAVE_CUNIT_TRUE}" && test -z "${HAVE_CUNIT_FALSE}"; then - as_fn_error $? "conditional \"HAVE_CUNIT\" was never defined. -Usually this means the macro was only invoked conditionally." "$LINENO" 5 -fi -if test -z "${HAVE_LIBXML2_TRUE}" && test -z "${HAVE_LIBXML2_FALSE}"; then - as_fn_error $? "conditional \"HAVE_LIBXML2\" was never defined. -Usually this means the macro was only invoked conditionally." "$LINENO" 5 -fi -if test -z "${HAVE_SPDYLAY_TRUE}" && test -z "${HAVE_SPDYLAY_FALSE}"; then - as_fn_error $? "conditional \"HAVE_SPDYLAY\" was never defined. -Usually this means the macro was only invoked conditionally." "$LINENO" 5 -fi -if test -z "${ENABLE_APP_TRUE}" && test -z "${ENABLE_APP_FALSE}"; then - as_fn_error $? "conditional \"ENABLE_APP\" was never defined. -Usually this means the macro was only invoked conditionally." "$LINENO" 5 -fi -if test -z "${ENABLE_HPACK_TOOLS_TRUE}" && test -z "${ENABLE_HPACK_TOOLS_FALSE}"; then - as_fn_error $? "conditional \"ENABLE_HPACK_TOOLS\" was never defined. -Usually this means the macro was only invoked conditionally." "$LINENO" 5 -fi -if test -z "${ENABLE_ASIO_LIB_TRUE}" && test -z "${ENABLE_ASIO_LIB_FALSE}"; then - as_fn_error $? "conditional \"ENABLE_ASIO_LIB\" was never defined. -Usually this means the macro was only invoked conditionally." "$LINENO" 5 -fi -if test -z "${ENABLE_EXAMPLES_TRUE}" && test -z "${ENABLE_EXAMPLES_FALSE}"; then - as_fn_error $? "conditional \"ENABLE_EXAMPLES\" was never defined. -Usually this means the macro was only invoked conditionally." "$LINENO" 5 -fi -if test -z "${ENABLE_PYTHON_BINDINGS_TRUE}" && test -z "${ENABLE_PYTHON_BINDINGS_FALSE}"; then - as_fn_error $? "conditional \"ENABLE_PYTHON_BINDINGS\" was never defined. -Usually this means the macro was only invoked conditionally." "$LINENO" 5 -fi -if test -z "${HAVE_CYTHON_TRUE}" && test -z "${HAVE_CYTHON_FALSE}"; then - as_fn_error $? "conditional \"HAVE_CYTHON\" was never defined. -Usually this means the macro was only invoked conditionally." "$LINENO" 5 -fi -if test -z "${ENABLE_FAILMALLOC_TRUE}" && test -z "${ENABLE_FAILMALLOC_FALSE}"; then - as_fn_error $? "conditional \"ENABLE_FAILMALLOC\" was never defined. -Usually this means the macro was only invoked conditionally." "$LINENO" 5 -fi - -if test -z "${ENABLE_TINY_NGHTTPD_TRUE}" && test -z "${ENABLE_TINY_NGHTTPD_FALSE}"; then - as_fn_error $? "conditional \"ENABLE_TINY_NGHTTPD\" was never defined. -Usually this means the macro was only invoked conditionally." "$LINENO" 5 -fi - -: "${CONFIG_STATUS=./config.status}" -ac_write_fail=0 -ac_clean_files_save=$ac_clean_files -ac_clean_files="$ac_clean_files $CONFIG_STATUS" -{ $as_echo "$as_me:${as_lineno-$LINENO}: creating $CONFIG_STATUS" >&5 -$as_echo "$as_me: creating $CONFIG_STATUS" >&6;} -as_write_fail=0 -cat >$CONFIG_STATUS <<_ASEOF || as_write_fail=1 -#! $SHELL -# Generated by $as_me. -# Run this file to recreate the current configuration. -# Compiler output produced by configure, useful for debugging -# configure, is in config.log if it exists. - -debug=false -ac_cs_recheck=false -ac_cs_silent=false - -SHELL=\${CONFIG_SHELL-$SHELL} -export SHELL -_ASEOF -cat >>$CONFIG_STATUS <<\_ASEOF || as_write_fail=1 -## -------------------- ## -## M4sh Initialization. ## -## -------------------- ## - -# Be more Bourne compatible -DUALCASE=1; export DUALCASE # for MKS sh -if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : - emulate sh - NULLCMD=: - # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which - # is contrary to our usage. Disable this feature. - alias -g '${1+"$@"}'='"$@"' - setopt NO_GLOB_SUBST -else - case `(set -o) 2>/dev/null` in #( - *posix*) : - set -o posix ;; #( - *) : - ;; -esac -fi - - -as_nl=' -' -export as_nl -# Printing a long string crashes Solaris 7 /usr/bin/printf. -as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' -as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo -as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo -# Prefer a ksh shell builtin over an external printf program on Solaris, -# but without wasting forks for bash or zsh. -if test -z "$BASH_VERSION$ZSH_VERSION" \ - && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then - as_echo='print -r --' - as_echo_n='print -rn --' -elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then - as_echo='printf %s\n' - as_echo_n='printf %s' -else - if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then - as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' - as_echo_n='/usr/ucb/echo -n' - else - as_echo_body='eval expr "X$1" : "X\\(.*\\)"' - as_echo_n_body='eval - arg=$1; - case $arg in #( - *"$as_nl"*) - expr "X$arg" : "X\\(.*\\)$as_nl"; - arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; - esac; - expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" - ' - export as_echo_n_body - as_echo_n='sh -c $as_echo_n_body as_echo' - fi - export as_echo_body - as_echo='sh -c $as_echo_body as_echo' -fi - -# The user is always right. -if test "${PATH_SEPARATOR+set}" != set; then - PATH_SEPARATOR=: - (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { - (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || - PATH_SEPARATOR=';' - } -fi - - -# IFS -# We need space, tab and new line, in precisely that order. Quoting is -# there to prevent editors from complaining about space-tab. -# (If _AS_PATH_WALK were called with IFS unset, it would disable word -# splitting by setting IFS to empty value.) -IFS=" "" $as_nl" - -# Find who we are. Look in the path if we contain no directory separator. -as_myself= -case $0 in #(( - *[\\/]* ) as_myself=$0 ;; - *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break - done -IFS=$as_save_IFS - - ;; -esac -# We did not find ourselves, most probably we were run as `sh COMMAND' -# in which case we are not to be found in the path. -if test "x$as_myself" = x; then - as_myself=$0 -fi -if test ! -f "$as_myself"; then - $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 - exit 1 -fi - -# Unset variables that we do not need and which cause bugs (e.g. in -# pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1" -# suppresses any "Segmentation fault" message there. '((' could -# trigger a bug in pdksh 5.2.14. -for as_var in BASH_ENV ENV MAIL MAILPATH -do eval test x\${$as_var+set} = xset \ - && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : -done -PS1='$ ' -PS2='> ' -PS4='+ ' - -# NLS nuisances. -LC_ALL=C -export LC_ALL -LANGUAGE=C -export LANGUAGE - -# CDPATH. -(unset CDPATH) >/dev/null 2>&1 && unset CDPATH - - -# as_fn_error STATUS ERROR [LINENO LOG_FD] -# ---------------------------------------- -# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are -# provided, also output the error to LOG_FD, referencing LINENO. Then exit the -# script with STATUS, using 1 if that was 0. -as_fn_error () -{ - as_status=$1; test $as_status -eq 0 && as_status=1 - if test "$4"; then - as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack - $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 - fi - $as_echo "$as_me: error: $2" >&2 - as_fn_exit $as_status -} # as_fn_error - - -# as_fn_set_status STATUS -# ----------------------- -# Set $? to STATUS, without forking. -as_fn_set_status () -{ - return $1 -} # as_fn_set_status - -# as_fn_exit STATUS -# ----------------- -# Exit the shell with STATUS, even in a "trap 0" or "set -e" context. -as_fn_exit () -{ - set +e - as_fn_set_status $1 - exit $1 -} # as_fn_exit - -# as_fn_unset VAR -# --------------- -# Portably unset VAR. -as_fn_unset () -{ - { eval $1=; unset $1;} -} -as_unset=as_fn_unset -# as_fn_append VAR VALUE -# ---------------------- -# Append the text in VALUE to the end of the definition contained in VAR. Take -# advantage of any shell optimizations that allow amortized linear growth over -# repeated appends, instead of the typical quadratic growth present in naive -# implementations. -if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then : - eval 'as_fn_append () - { - eval $1+=\$2 - }' -else - as_fn_append () - { - eval $1=\$$1\$2 - } -fi # as_fn_append - -# as_fn_arith ARG... -# ------------------ -# Perform arithmetic evaluation on the ARGs, and store the result in the -# global $as_val. Take advantage of shells that can avoid forks. The arguments -# must be portable across $(()) and expr. -if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then : - eval 'as_fn_arith () - { - as_val=$(( $* )) - }' -else - as_fn_arith () - { - as_val=`expr "$@" || test $? -eq 1` - } -fi # as_fn_arith - - -if expr a : '\(a\)' >/dev/null 2>&1 && - test "X`expr 00001 : '.*\(...\)'`" = X001; then - as_expr=expr -else - as_expr=false -fi - -if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then - as_basename=basename -else - as_basename=false -fi - -if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then - as_dirname=dirname -else - as_dirname=false -fi - -as_me=`$as_basename -- "$0" || -$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ - X"$0" : 'X\(//\)$' \| \ - X"$0" : 'X\(/\)' \| . 2>/dev/null || -$as_echo X/"$0" | - sed '/^.*\/\([^/][^/]*\)\/*$/{ - s//\1/ - q - } - /^X\/\(\/\/\)$/{ - s//\1/ - q - } - /^X\/\(\/\).*/{ - s//\1/ - q - } - s/.*/./; q'` - -# Avoid depending upon Character Ranges. -as_cr_letters='abcdefghijklmnopqrstuvwxyz' -as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' -as_cr_Letters=$as_cr_letters$as_cr_LETTERS -as_cr_digits='0123456789' -as_cr_alnum=$as_cr_Letters$as_cr_digits - -ECHO_C= ECHO_N= ECHO_T= -case `echo -n x` in #((((( --n*) - case `echo 'xy\c'` in - *c*) ECHO_T=' ';; # ECHO_T is single tab character. - xy) ECHO_C='\c';; - *) echo `echo ksh88 bug on AIX 6.1` > /dev/null - ECHO_T=' ';; - esac;; -*) - ECHO_N='-n';; -esac - -rm -f conf$$ conf$$.exe conf$$.file -if test -d conf$$.dir; then - rm -f conf$$.dir/conf$$.file -else - rm -f conf$$.dir - mkdir conf$$.dir 2>/dev/null -fi -if (echo >conf$$.file) 2>/dev/null; then - if ln -s conf$$.file conf$$ 2>/dev/null; then - as_ln_s='ln -s' - # ... but there are two gotchas: - # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. - # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. - # In both cases, we have to default to `cp -pR'. - ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || - as_ln_s='cp -pR' - elif ln conf$$.file conf$$ 2>/dev/null; then - as_ln_s=ln - else - as_ln_s='cp -pR' - fi -else - as_ln_s='cp -pR' -fi -rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file -rmdir conf$$.dir 2>/dev/null - - -# as_fn_mkdir_p -# ------------- -# Create "$as_dir" as a directory, including parents if necessary. -as_fn_mkdir_p () -{ - - case $as_dir in #( - -*) as_dir=./$as_dir;; - esac - test -d "$as_dir" || eval $as_mkdir_p || { - as_dirs= - while :; do - case $as_dir in #( - *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( - *) as_qdir=$as_dir;; - esac - as_dirs="'$as_qdir' $as_dirs" - as_dir=`$as_dirname -- "$as_dir" || -$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ - X"$as_dir" : 'X\(//\)[^/]' \| \ - X"$as_dir" : 'X\(//\)$' \| \ - X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || -$as_echo X"$as_dir" | - sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ - s//\1/ - q - } - /^X\(\/\/\)[^/].*/{ - s//\1/ - q - } - /^X\(\/\/\)$/{ - s//\1/ - q - } - /^X\(\/\).*/{ - s//\1/ - q - } - s/.*/./; q'` - test -d "$as_dir" && break - done - test -z "$as_dirs" || eval "mkdir $as_dirs" - } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" - - -} # as_fn_mkdir_p -if mkdir -p . 2>/dev/null; then - as_mkdir_p='mkdir -p "$as_dir"' -else - test -d ./-p && rmdir ./-p - as_mkdir_p=false -fi - - -# as_fn_executable_p FILE -# ----------------------- -# Test if FILE is an executable regular file. -as_fn_executable_p () -{ - test -f "$1" && test -x "$1" -} # as_fn_executable_p -as_test_x='test -x' -as_executable_p=as_fn_executable_p - -# Sed expression to map a string onto a valid CPP name. -as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" - -# Sed expression to map a string onto a valid variable name. -as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" - - -exec 6>&1 -## ----------------------------------- ## -## Main body of $CONFIG_STATUS script. ## -## ----------------------------------- ## -_ASEOF -test $as_write_fail = 0 && chmod +x $CONFIG_STATUS || ac_write_fail=1 - -cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 -# Save the log message, to keep $0 and so on meaningful, and to -# report actual input values of CONFIG_FILES etc. instead of their -# values after options handling. -ac_log=" -This file was extended by nghttp2 $as_me 1.0.0, which was -generated by GNU Autoconf 2.69. Invocation command line was - - CONFIG_FILES = $CONFIG_FILES - CONFIG_HEADERS = $CONFIG_HEADERS - CONFIG_LINKS = $CONFIG_LINKS - CONFIG_COMMANDS = $CONFIG_COMMANDS - $ $0 $@ - -on `(hostname || uname -n) 2>/dev/null | sed 1q` -" - -_ACEOF - -case $ac_config_files in *" -"*) set x $ac_config_files; shift; ac_config_files=$*;; -esac - -case $ac_config_headers in *" -"*) set x $ac_config_headers; shift; ac_config_headers=$*;; -esac - - -cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 -# Files that config.status was made for. -config_files="$ac_config_files" -config_headers="$ac_config_headers" -config_commands="$ac_config_commands" - -_ACEOF - -cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 -ac_cs_usage="\ -\`$as_me' instantiates files and other configuration actions -from templates according to the current configuration. Unless the files -and actions are specified as TAGs, all are instantiated by default. - -Usage: $0 [OPTION]... [TAG]... - - -h, --help print this help, then exit - -V, --version print version number and configuration settings, then exit - --config print configuration, then exit - -q, --quiet, --silent - do not print progress messages - -d, --debug don't remove temporary files - --recheck update $as_me by reconfiguring in the same conditions - --file=FILE[:TEMPLATE] - instantiate the configuration file FILE - --header=FILE[:TEMPLATE] - instantiate the configuration header FILE - -Configuration files: -$config_files - -Configuration headers: -$config_headers - -Configuration commands: -$config_commands - -Report bugs to ." - -_ACEOF -cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 -ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" -ac_cs_version="\\ -nghttp2 config.status 1.0.0 -configured by $0, generated by GNU Autoconf 2.69, - with options \\"\$ac_cs_config\\" - -Copyright (C) 2012 Free Software Foundation, Inc. -This config.status script is free software; the Free Software Foundation -gives unlimited permission to copy, distribute and modify it." - -ac_pwd='$ac_pwd' -srcdir='$srcdir' -INSTALL='$INSTALL' -MKDIR_P='$MKDIR_P' -AWK='$AWK' -test -n "\$AWK" || AWK=awk -_ACEOF - -cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 -# The default lists apply if the user does not specify any file. -ac_need_defaults=: -while test $# != 0 -do - case $1 in - --*=?*) - ac_option=`expr "X$1" : 'X\([^=]*\)='` - ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'` - ac_shift=: - ;; - --*=) - ac_option=`expr "X$1" : 'X\([^=]*\)='` - ac_optarg= - ac_shift=: - ;; - *) - ac_option=$1 - ac_optarg=$2 - ac_shift=shift - ;; - esac - - case $ac_option in - # Handling of the options. - -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) - ac_cs_recheck=: ;; - --version | --versio | --versi | --vers | --ver | --ve | --v | -V ) - $as_echo "$ac_cs_version"; exit ;; - --config | --confi | --conf | --con | --co | --c ) - $as_echo "$ac_cs_config"; exit ;; - --debug | --debu | --deb | --de | --d | -d ) - debug=: ;; - --file | --fil | --fi | --f ) - $ac_shift - case $ac_optarg in - *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; - '') as_fn_error $? "missing file argument" ;; - esac - as_fn_append CONFIG_FILES " '$ac_optarg'" - ac_need_defaults=false;; - --header | --heade | --head | --hea ) - $ac_shift - case $ac_optarg in - *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; - esac - as_fn_append CONFIG_HEADERS " '$ac_optarg'" - ac_need_defaults=false;; - --he | --h) - # Conflict between --help and --header - as_fn_error $? "ambiguous option: \`$1' -Try \`$0 --help' for more information.";; - --help | --hel | -h ) - $as_echo "$ac_cs_usage"; exit ;; - -q | -quiet | --quiet | --quie | --qui | --qu | --q \ - | -silent | --silent | --silen | --sile | --sil | --si | --s) - ac_cs_silent=: ;; - - # This is an error. - -*) as_fn_error $? "unrecognized option: \`$1' -Try \`$0 --help' for more information." ;; - - *) as_fn_append ac_config_targets " $1" - ac_need_defaults=false ;; - - esac - shift -done - -ac_configure_extra_args= - -if $ac_cs_silent; then - exec 6>/dev/null - ac_configure_extra_args="$ac_configure_extra_args --silent" -fi - -_ACEOF -cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 -if \$ac_cs_recheck; then - set X $SHELL '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion - shift - \$as_echo "running CONFIG_SHELL=$SHELL \$*" >&6 - CONFIG_SHELL='$SHELL' - export CONFIG_SHELL - exec "\$@" -fi - -_ACEOF -cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 -exec 5>>config.log -{ - echo - sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX -## Running $as_me. ## -_ASBOX - $as_echo "$ac_log" -} >&5 - -_ACEOF -cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 -# -# INIT-COMMANDS -# - - -# The HP-UX ksh and POSIX shell print the target directory to stdout -# if CDPATH is set. -(unset CDPATH) >/dev/null 2>&1 && unset CDPATH - -sed_quote_subst='$sed_quote_subst' -double_quote_subst='$double_quote_subst' -delay_variable_subst='$delay_variable_subst' -macro_version='`$ECHO "$macro_version" | $SED "$delay_single_quote_subst"`' -macro_revision='`$ECHO "$macro_revision" | $SED "$delay_single_quote_subst"`' -enable_shared='`$ECHO "$enable_shared" | $SED "$delay_single_quote_subst"`' -enable_static='`$ECHO "$enable_static" | $SED "$delay_single_quote_subst"`' -pic_mode='`$ECHO "$pic_mode" | $SED "$delay_single_quote_subst"`' -enable_fast_install='`$ECHO "$enable_fast_install" | $SED "$delay_single_quote_subst"`' -SHELL='`$ECHO "$SHELL" | $SED "$delay_single_quote_subst"`' -ECHO='`$ECHO "$ECHO" | $SED "$delay_single_quote_subst"`' -PATH_SEPARATOR='`$ECHO "$PATH_SEPARATOR" | $SED "$delay_single_quote_subst"`' -host_alias='`$ECHO "$host_alias" | $SED "$delay_single_quote_subst"`' -host='`$ECHO "$host" | $SED "$delay_single_quote_subst"`' -host_os='`$ECHO "$host_os" | $SED "$delay_single_quote_subst"`' -build_alias='`$ECHO "$build_alias" | $SED "$delay_single_quote_subst"`' -build='`$ECHO "$build" | $SED "$delay_single_quote_subst"`' -build_os='`$ECHO "$build_os" | $SED "$delay_single_quote_subst"`' -SED='`$ECHO "$SED" | $SED "$delay_single_quote_subst"`' -Xsed='`$ECHO "$Xsed" | $SED "$delay_single_quote_subst"`' -GREP='`$ECHO "$GREP" | $SED "$delay_single_quote_subst"`' -EGREP='`$ECHO "$EGREP" | $SED "$delay_single_quote_subst"`' -FGREP='`$ECHO "$FGREP" | $SED "$delay_single_quote_subst"`' -LD='`$ECHO "$LD" | $SED "$delay_single_quote_subst"`' -NM='`$ECHO "$NM" | $SED "$delay_single_quote_subst"`' -LN_S='`$ECHO "$LN_S" | $SED "$delay_single_quote_subst"`' -max_cmd_len='`$ECHO "$max_cmd_len" | $SED "$delay_single_quote_subst"`' -ac_objext='`$ECHO "$ac_objext" | $SED "$delay_single_quote_subst"`' -exeext='`$ECHO "$exeext" | $SED "$delay_single_quote_subst"`' -lt_unset='`$ECHO "$lt_unset" | $SED "$delay_single_quote_subst"`' -lt_SP2NL='`$ECHO "$lt_SP2NL" | $SED "$delay_single_quote_subst"`' -lt_NL2SP='`$ECHO "$lt_NL2SP" | $SED "$delay_single_quote_subst"`' -lt_cv_to_host_file_cmd='`$ECHO "$lt_cv_to_host_file_cmd" | $SED "$delay_single_quote_subst"`' -lt_cv_to_tool_file_cmd='`$ECHO "$lt_cv_to_tool_file_cmd" | $SED "$delay_single_quote_subst"`' -reload_flag='`$ECHO "$reload_flag" | $SED "$delay_single_quote_subst"`' -reload_cmds='`$ECHO "$reload_cmds" | $SED "$delay_single_quote_subst"`' -OBJDUMP='`$ECHO "$OBJDUMP" | $SED "$delay_single_quote_subst"`' -deplibs_check_method='`$ECHO "$deplibs_check_method" | $SED "$delay_single_quote_subst"`' -file_magic_cmd='`$ECHO "$file_magic_cmd" | $SED "$delay_single_quote_subst"`' -file_magic_glob='`$ECHO "$file_magic_glob" | $SED "$delay_single_quote_subst"`' -want_nocaseglob='`$ECHO "$want_nocaseglob" | $SED "$delay_single_quote_subst"`' -DLLTOOL='`$ECHO "$DLLTOOL" | $SED "$delay_single_quote_subst"`' -sharedlib_from_linklib_cmd='`$ECHO "$sharedlib_from_linklib_cmd" | $SED "$delay_single_quote_subst"`' -AR='`$ECHO "$AR" | $SED "$delay_single_quote_subst"`' -AR_FLAGS='`$ECHO "$AR_FLAGS" | $SED "$delay_single_quote_subst"`' -archiver_list_spec='`$ECHO "$archiver_list_spec" | $SED "$delay_single_quote_subst"`' -STRIP='`$ECHO "$STRIP" | $SED "$delay_single_quote_subst"`' -RANLIB='`$ECHO "$RANLIB" | $SED "$delay_single_quote_subst"`' -old_postinstall_cmds='`$ECHO "$old_postinstall_cmds" | $SED "$delay_single_quote_subst"`' -old_postuninstall_cmds='`$ECHO "$old_postuninstall_cmds" | $SED "$delay_single_quote_subst"`' -old_archive_cmds='`$ECHO "$old_archive_cmds" | $SED "$delay_single_quote_subst"`' -lock_old_archive_extraction='`$ECHO "$lock_old_archive_extraction" | $SED "$delay_single_quote_subst"`' -CC='`$ECHO "$CC" | $SED "$delay_single_quote_subst"`' -CFLAGS='`$ECHO "$CFLAGS" | $SED "$delay_single_quote_subst"`' -compiler='`$ECHO "$compiler" | $SED "$delay_single_quote_subst"`' -GCC='`$ECHO "$GCC" | $SED "$delay_single_quote_subst"`' -lt_cv_sys_global_symbol_pipe='`$ECHO "$lt_cv_sys_global_symbol_pipe" | $SED "$delay_single_quote_subst"`' -lt_cv_sys_global_symbol_to_cdecl='`$ECHO "$lt_cv_sys_global_symbol_to_cdecl" | $SED "$delay_single_quote_subst"`' -lt_cv_sys_global_symbol_to_c_name_address='`$ECHO "$lt_cv_sys_global_symbol_to_c_name_address" | $SED "$delay_single_quote_subst"`' -lt_cv_sys_global_symbol_to_c_name_address_lib_prefix='`$ECHO "$lt_cv_sys_global_symbol_to_c_name_address_lib_prefix" | $SED "$delay_single_quote_subst"`' -nm_file_list_spec='`$ECHO "$nm_file_list_spec" | $SED "$delay_single_quote_subst"`' -lt_sysroot='`$ECHO "$lt_sysroot" | $SED "$delay_single_quote_subst"`' -objdir='`$ECHO "$objdir" | $SED "$delay_single_quote_subst"`' -MAGIC_CMD='`$ECHO "$MAGIC_CMD" | $SED "$delay_single_quote_subst"`' -lt_prog_compiler_no_builtin_flag='`$ECHO "$lt_prog_compiler_no_builtin_flag" | $SED "$delay_single_quote_subst"`' -lt_prog_compiler_pic='`$ECHO "$lt_prog_compiler_pic" | $SED "$delay_single_quote_subst"`' -lt_prog_compiler_wl='`$ECHO "$lt_prog_compiler_wl" | $SED "$delay_single_quote_subst"`' -lt_prog_compiler_static='`$ECHO "$lt_prog_compiler_static" | $SED "$delay_single_quote_subst"`' -lt_cv_prog_compiler_c_o='`$ECHO "$lt_cv_prog_compiler_c_o" | $SED "$delay_single_quote_subst"`' -need_locks='`$ECHO "$need_locks" | $SED "$delay_single_quote_subst"`' -MANIFEST_TOOL='`$ECHO "$MANIFEST_TOOL" | $SED "$delay_single_quote_subst"`' -DSYMUTIL='`$ECHO "$DSYMUTIL" | $SED "$delay_single_quote_subst"`' -NMEDIT='`$ECHO "$NMEDIT" | $SED "$delay_single_quote_subst"`' -LIPO='`$ECHO "$LIPO" | $SED "$delay_single_quote_subst"`' -OTOOL='`$ECHO "$OTOOL" | $SED "$delay_single_quote_subst"`' -OTOOL64='`$ECHO "$OTOOL64" | $SED "$delay_single_quote_subst"`' -libext='`$ECHO "$libext" | $SED "$delay_single_quote_subst"`' -shrext_cmds='`$ECHO "$shrext_cmds" | $SED "$delay_single_quote_subst"`' -extract_expsyms_cmds='`$ECHO "$extract_expsyms_cmds" | $SED "$delay_single_quote_subst"`' -archive_cmds_need_lc='`$ECHO "$archive_cmds_need_lc" | $SED "$delay_single_quote_subst"`' -enable_shared_with_static_runtimes='`$ECHO "$enable_shared_with_static_runtimes" | $SED "$delay_single_quote_subst"`' -export_dynamic_flag_spec='`$ECHO "$export_dynamic_flag_spec" | $SED "$delay_single_quote_subst"`' -whole_archive_flag_spec='`$ECHO "$whole_archive_flag_spec" | $SED "$delay_single_quote_subst"`' -compiler_needs_object='`$ECHO "$compiler_needs_object" | $SED "$delay_single_quote_subst"`' -old_archive_from_new_cmds='`$ECHO "$old_archive_from_new_cmds" | $SED "$delay_single_quote_subst"`' -old_archive_from_expsyms_cmds='`$ECHO "$old_archive_from_expsyms_cmds" | $SED "$delay_single_quote_subst"`' -archive_cmds='`$ECHO "$archive_cmds" | $SED "$delay_single_quote_subst"`' -archive_expsym_cmds='`$ECHO "$archive_expsym_cmds" | $SED "$delay_single_quote_subst"`' -module_cmds='`$ECHO "$module_cmds" | $SED "$delay_single_quote_subst"`' -module_expsym_cmds='`$ECHO "$module_expsym_cmds" | $SED "$delay_single_quote_subst"`' -with_gnu_ld='`$ECHO "$with_gnu_ld" | $SED "$delay_single_quote_subst"`' -allow_undefined_flag='`$ECHO "$allow_undefined_flag" | $SED "$delay_single_quote_subst"`' -no_undefined_flag='`$ECHO "$no_undefined_flag" | $SED "$delay_single_quote_subst"`' -hardcode_libdir_flag_spec='`$ECHO "$hardcode_libdir_flag_spec" | $SED "$delay_single_quote_subst"`' -hardcode_libdir_separator='`$ECHO "$hardcode_libdir_separator" | $SED "$delay_single_quote_subst"`' -hardcode_direct='`$ECHO "$hardcode_direct" | $SED "$delay_single_quote_subst"`' -hardcode_direct_absolute='`$ECHO "$hardcode_direct_absolute" | $SED "$delay_single_quote_subst"`' -hardcode_minus_L='`$ECHO "$hardcode_minus_L" | $SED "$delay_single_quote_subst"`' -hardcode_shlibpath_var='`$ECHO "$hardcode_shlibpath_var" | $SED "$delay_single_quote_subst"`' -hardcode_automatic='`$ECHO "$hardcode_automatic" | $SED "$delay_single_quote_subst"`' -inherit_rpath='`$ECHO "$inherit_rpath" | $SED "$delay_single_quote_subst"`' -link_all_deplibs='`$ECHO "$link_all_deplibs" | $SED "$delay_single_quote_subst"`' -always_export_symbols='`$ECHO "$always_export_symbols" | $SED "$delay_single_quote_subst"`' -export_symbols_cmds='`$ECHO "$export_symbols_cmds" | $SED "$delay_single_quote_subst"`' -exclude_expsyms='`$ECHO "$exclude_expsyms" | $SED "$delay_single_quote_subst"`' -include_expsyms='`$ECHO "$include_expsyms" | $SED "$delay_single_quote_subst"`' -prelink_cmds='`$ECHO "$prelink_cmds" | $SED "$delay_single_quote_subst"`' -postlink_cmds='`$ECHO "$postlink_cmds" | $SED "$delay_single_quote_subst"`' -file_list_spec='`$ECHO "$file_list_spec" | $SED "$delay_single_quote_subst"`' -variables_saved_for_relink='`$ECHO "$variables_saved_for_relink" | $SED "$delay_single_quote_subst"`' -need_lib_prefix='`$ECHO "$need_lib_prefix" | $SED "$delay_single_quote_subst"`' -need_version='`$ECHO "$need_version" | $SED "$delay_single_quote_subst"`' -version_type='`$ECHO "$version_type" | $SED "$delay_single_quote_subst"`' -runpath_var='`$ECHO "$runpath_var" | $SED "$delay_single_quote_subst"`' -shlibpath_var='`$ECHO "$shlibpath_var" | $SED "$delay_single_quote_subst"`' -shlibpath_overrides_runpath='`$ECHO "$shlibpath_overrides_runpath" | $SED "$delay_single_quote_subst"`' -libname_spec='`$ECHO "$libname_spec" | $SED "$delay_single_quote_subst"`' -library_names_spec='`$ECHO "$library_names_spec" | $SED "$delay_single_quote_subst"`' -soname_spec='`$ECHO "$soname_spec" | $SED "$delay_single_quote_subst"`' -install_override_mode='`$ECHO "$install_override_mode" | $SED "$delay_single_quote_subst"`' -postinstall_cmds='`$ECHO "$postinstall_cmds" | $SED "$delay_single_quote_subst"`' -postuninstall_cmds='`$ECHO "$postuninstall_cmds" | $SED "$delay_single_quote_subst"`' -finish_cmds='`$ECHO "$finish_cmds" | $SED "$delay_single_quote_subst"`' -finish_eval='`$ECHO "$finish_eval" | $SED "$delay_single_quote_subst"`' -hardcode_into_libs='`$ECHO "$hardcode_into_libs" | $SED "$delay_single_quote_subst"`' -sys_lib_search_path_spec='`$ECHO "$sys_lib_search_path_spec" | $SED "$delay_single_quote_subst"`' -sys_lib_dlsearch_path_spec='`$ECHO "$sys_lib_dlsearch_path_spec" | $SED "$delay_single_quote_subst"`' -hardcode_action='`$ECHO "$hardcode_action" | $SED "$delay_single_quote_subst"`' -enable_dlopen='`$ECHO "$enable_dlopen" | $SED "$delay_single_quote_subst"`' -enable_dlopen_self='`$ECHO "$enable_dlopen_self" | $SED "$delay_single_quote_subst"`' -enable_dlopen_self_static='`$ECHO "$enable_dlopen_self_static" | $SED "$delay_single_quote_subst"`' -old_striplib='`$ECHO "$old_striplib" | $SED "$delay_single_quote_subst"`' -striplib='`$ECHO "$striplib" | $SED "$delay_single_quote_subst"`' -compiler_lib_search_dirs='`$ECHO "$compiler_lib_search_dirs" | $SED "$delay_single_quote_subst"`' -predep_objects='`$ECHO "$predep_objects" | $SED "$delay_single_quote_subst"`' -postdep_objects='`$ECHO "$postdep_objects" | $SED "$delay_single_quote_subst"`' -predeps='`$ECHO "$predeps" | $SED "$delay_single_quote_subst"`' -postdeps='`$ECHO "$postdeps" | $SED "$delay_single_quote_subst"`' -compiler_lib_search_path='`$ECHO "$compiler_lib_search_path" | $SED "$delay_single_quote_subst"`' -LD_CXX='`$ECHO "$LD_CXX" | $SED "$delay_single_quote_subst"`' -reload_flag_CXX='`$ECHO "$reload_flag_CXX" | $SED "$delay_single_quote_subst"`' -reload_cmds_CXX='`$ECHO "$reload_cmds_CXX" | $SED "$delay_single_quote_subst"`' -old_archive_cmds_CXX='`$ECHO "$old_archive_cmds_CXX" | $SED "$delay_single_quote_subst"`' -compiler_CXX='`$ECHO "$compiler_CXX" | $SED "$delay_single_quote_subst"`' -GCC_CXX='`$ECHO "$GCC_CXX" | $SED "$delay_single_quote_subst"`' -lt_prog_compiler_no_builtin_flag_CXX='`$ECHO "$lt_prog_compiler_no_builtin_flag_CXX" | $SED "$delay_single_quote_subst"`' -lt_prog_compiler_pic_CXX='`$ECHO "$lt_prog_compiler_pic_CXX" | $SED "$delay_single_quote_subst"`' -lt_prog_compiler_wl_CXX='`$ECHO "$lt_prog_compiler_wl_CXX" | $SED "$delay_single_quote_subst"`' -lt_prog_compiler_static_CXX='`$ECHO "$lt_prog_compiler_static_CXX" | $SED "$delay_single_quote_subst"`' -lt_cv_prog_compiler_c_o_CXX='`$ECHO "$lt_cv_prog_compiler_c_o_CXX" | $SED "$delay_single_quote_subst"`' -archive_cmds_need_lc_CXX='`$ECHO "$archive_cmds_need_lc_CXX" | $SED "$delay_single_quote_subst"`' -enable_shared_with_static_runtimes_CXX='`$ECHO "$enable_shared_with_static_runtimes_CXX" | $SED "$delay_single_quote_subst"`' -export_dynamic_flag_spec_CXX='`$ECHO "$export_dynamic_flag_spec_CXX" | $SED "$delay_single_quote_subst"`' -whole_archive_flag_spec_CXX='`$ECHO "$whole_archive_flag_spec_CXX" | $SED "$delay_single_quote_subst"`' -compiler_needs_object_CXX='`$ECHO "$compiler_needs_object_CXX" | $SED "$delay_single_quote_subst"`' -old_archive_from_new_cmds_CXX='`$ECHO "$old_archive_from_new_cmds_CXX" | $SED "$delay_single_quote_subst"`' -old_archive_from_expsyms_cmds_CXX='`$ECHO "$old_archive_from_expsyms_cmds_CXX" | $SED "$delay_single_quote_subst"`' -archive_cmds_CXX='`$ECHO "$archive_cmds_CXX" | $SED "$delay_single_quote_subst"`' -archive_expsym_cmds_CXX='`$ECHO "$archive_expsym_cmds_CXX" | $SED "$delay_single_quote_subst"`' -module_cmds_CXX='`$ECHO "$module_cmds_CXX" | $SED "$delay_single_quote_subst"`' -module_expsym_cmds_CXX='`$ECHO "$module_expsym_cmds_CXX" | $SED "$delay_single_quote_subst"`' -with_gnu_ld_CXX='`$ECHO "$with_gnu_ld_CXX" | $SED "$delay_single_quote_subst"`' -allow_undefined_flag_CXX='`$ECHO "$allow_undefined_flag_CXX" | $SED "$delay_single_quote_subst"`' -no_undefined_flag_CXX='`$ECHO "$no_undefined_flag_CXX" | $SED "$delay_single_quote_subst"`' -hardcode_libdir_flag_spec_CXX='`$ECHO "$hardcode_libdir_flag_spec_CXX" | $SED "$delay_single_quote_subst"`' -hardcode_libdir_separator_CXX='`$ECHO "$hardcode_libdir_separator_CXX" | $SED "$delay_single_quote_subst"`' -hardcode_direct_CXX='`$ECHO "$hardcode_direct_CXX" | $SED "$delay_single_quote_subst"`' -hardcode_direct_absolute_CXX='`$ECHO "$hardcode_direct_absolute_CXX" | $SED "$delay_single_quote_subst"`' -hardcode_minus_L_CXX='`$ECHO "$hardcode_minus_L_CXX" | $SED "$delay_single_quote_subst"`' -hardcode_shlibpath_var_CXX='`$ECHO "$hardcode_shlibpath_var_CXX" | $SED "$delay_single_quote_subst"`' -hardcode_automatic_CXX='`$ECHO "$hardcode_automatic_CXX" | $SED "$delay_single_quote_subst"`' -inherit_rpath_CXX='`$ECHO "$inherit_rpath_CXX" | $SED "$delay_single_quote_subst"`' -link_all_deplibs_CXX='`$ECHO "$link_all_deplibs_CXX" | $SED "$delay_single_quote_subst"`' -always_export_symbols_CXX='`$ECHO "$always_export_symbols_CXX" | $SED "$delay_single_quote_subst"`' -export_symbols_cmds_CXX='`$ECHO "$export_symbols_cmds_CXX" | $SED "$delay_single_quote_subst"`' -exclude_expsyms_CXX='`$ECHO "$exclude_expsyms_CXX" | $SED "$delay_single_quote_subst"`' -include_expsyms_CXX='`$ECHO "$include_expsyms_CXX" | $SED "$delay_single_quote_subst"`' -prelink_cmds_CXX='`$ECHO "$prelink_cmds_CXX" | $SED "$delay_single_quote_subst"`' -postlink_cmds_CXX='`$ECHO "$postlink_cmds_CXX" | $SED "$delay_single_quote_subst"`' -file_list_spec_CXX='`$ECHO "$file_list_spec_CXX" | $SED "$delay_single_quote_subst"`' -hardcode_action_CXX='`$ECHO "$hardcode_action_CXX" | $SED "$delay_single_quote_subst"`' -compiler_lib_search_dirs_CXX='`$ECHO "$compiler_lib_search_dirs_CXX" | $SED "$delay_single_quote_subst"`' -predep_objects_CXX='`$ECHO "$predep_objects_CXX" | $SED "$delay_single_quote_subst"`' -postdep_objects_CXX='`$ECHO "$postdep_objects_CXX" | $SED "$delay_single_quote_subst"`' -predeps_CXX='`$ECHO "$predeps_CXX" | $SED "$delay_single_quote_subst"`' -postdeps_CXX='`$ECHO "$postdeps_CXX" | $SED "$delay_single_quote_subst"`' -compiler_lib_search_path_CXX='`$ECHO "$compiler_lib_search_path_CXX" | $SED "$delay_single_quote_subst"`' - -LTCC='$LTCC' -LTCFLAGS='$LTCFLAGS' -compiler='$compiler_DEFAULT' - -# A function that is used when there is no print builtin or printf. -func_fallback_echo () -{ - eval 'cat <<_LTECHO_EOF -\$1 -_LTECHO_EOF' -} - -# Quote evaled strings. -for var in SHELL \ -ECHO \ -PATH_SEPARATOR \ -SED \ -GREP \ -EGREP \ -FGREP \ -LD \ -NM \ -LN_S \ -lt_SP2NL \ -lt_NL2SP \ -reload_flag \ -OBJDUMP \ -deplibs_check_method \ -file_magic_cmd \ -file_magic_glob \ -want_nocaseglob \ -DLLTOOL \ -sharedlib_from_linklib_cmd \ -AR \ -AR_FLAGS \ -archiver_list_spec \ -STRIP \ -RANLIB \ -CC \ -CFLAGS \ -compiler \ -lt_cv_sys_global_symbol_pipe \ -lt_cv_sys_global_symbol_to_cdecl \ -lt_cv_sys_global_symbol_to_c_name_address \ -lt_cv_sys_global_symbol_to_c_name_address_lib_prefix \ -nm_file_list_spec \ -lt_prog_compiler_no_builtin_flag \ -lt_prog_compiler_pic \ -lt_prog_compiler_wl \ -lt_prog_compiler_static \ -lt_cv_prog_compiler_c_o \ -need_locks \ -MANIFEST_TOOL \ -DSYMUTIL \ -NMEDIT \ -LIPO \ -OTOOL \ -OTOOL64 \ -shrext_cmds \ -export_dynamic_flag_spec \ -whole_archive_flag_spec \ -compiler_needs_object \ -with_gnu_ld \ -allow_undefined_flag \ -no_undefined_flag \ -hardcode_libdir_flag_spec \ -hardcode_libdir_separator \ -exclude_expsyms \ -include_expsyms \ -file_list_spec \ -variables_saved_for_relink \ -libname_spec \ -library_names_spec \ -soname_spec \ -install_override_mode \ -finish_eval \ -old_striplib \ -striplib \ -compiler_lib_search_dirs \ -predep_objects \ -postdep_objects \ -predeps \ -postdeps \ -compiler_lib_search_path \ -LD_CXX \ -reload_flag_CXX \ -compiler_CXX \ -lt_prog_compiler_no_builtin_flag_CXX \ -lt_prog_compiler_pic_CXX \ -lt_prog_compiler_wl_CXX \ -lt_prog_compiler_static_CXX \ -lt_cv_prog_compiler_c_o_CXX \ -export_dynamic_flag_spec_CXX \ -whole_archive_flag_spec_CXX \ -compiler_needs_object_CXX \ -with_gnu_ld_CXX \ -allow_undefined_flag_CXX \ -no_undefined_flag_CXX \ -hardcode_libdir_flag_spec_CXX \ -hardcode_libdir_separator_CXX \ -exclude_expsyms_CXX \ -include_expsyms_CXX \ -file_list_spec_CXX \ -compiler_lib_search_dirs_CXX \ -predep_objects_CXX \ -postdep_objects_CXX \ -predeps_CXX \ -postdeps_CXX \ -compiler_lib_search_path_CXX; do - case \`eval \\\\\$ECHO \\\\""\\\\\$\$var"\\\\"\` in - *[\\\\\\\`\\"\\\$]*) - eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"\\\$\$var\\" | \\\$SED \\"\\\$sed_quote_subst\\"\\\`\\\\\\"" - ;; - *) - eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\"" - ;; - esac -done - -# Double-quote double-evaled strings. -for var in reload_cmds \ -old_postinstall_cmds \ -old_postuninstall_cmds \ -old_archive_cmds \ -extract_expsyms_cmds \ -old_archive_from_new_cmds \ -old_archive_from_expsyms_cmds \ -archive_cmds \ -archive_expsym_cmds \ -module_cmds \ -module_expsym_cmds \ -export_symbols_cmds \ -prelink_cmds \ -postlink_cmds \ -postinstall_cmds \ -postuninstall_cmds \ -finish_cmds \ -sys_lib_search_path_spec \ -sys_lib_dlsearch_path_spec \ -reload_cmds_CXX \ -old_archive_cmds_CXX \ -old_archive_from_new_cmds_CXX \ -old_archive_from_expsyms_cmds_CXX \ -archive_cmds_CXX \ -archive_expsym_cmds_CXX \ -module_cmds_CXX \ -module_expsym_cmds_CXX \ -export_symbols_cmds_CXX \ -prelink_cmds_CXX \ -postlink_cmds_CXX; do - case \`eval \\\\\$ECHO \\\\""\\\\\$\$var"\\\\"\` in - *[\\\\\\\`\\"\\\$]*) - eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"\\\$\$var\\" | \\\$SED -e \\"\\\$double_quote_subst\\" -e \\"\\\$sed_quote_subst\\" -e \\"\\\$delay_variable_subst\\"\\\`\\\\\\"" - ;; - *) - eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\"" - ;; - esac -done - -ac_aux_dir='$ac_aux_dir' -xsi_shell='$xsi_shell' -lt_shell_append='$lt_shell_append' - -# See if we are running on zsh, and set the options which allow our -# commands through without removal of \ escapes INIT. -if test -n "\${ZSH_VERSION+set}" ; then - setopt NO_GLOB_SUBST -fi - - - PACKAGE='$PACKAGE' - VERSION='$VERSION' - TIMESTAMP='$TIMESTAMP' - RM='$RM' - ofile='$ofile' - - - - - -AMDEP_TRUE="$AMDEP_TRUE" ac_aux_dir="$ac_aux_dir" - -_ACEOF - -cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 - -# Handling of arguments. -for ac_config_target in $ac_config_targets -do - case $ac_config_target in - "libtool") CONFIG_COMMANDS="$CONFIG_COMMANDS libtool" ;; - "depfiles") CONFIG_COMMANDS="$CONFIG_COMMANDS depfiles" ;; - "config.h") CONFIG_HEADERS="$CONFIG_HEADERS config.h" ;; - "Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;; - "lib/Makefile") CONFIG_FILES="$CONFIG_FILES lib/Makefile" ;; - "lib/libnghttp2.pc") CONFIG_FILES="$CONFIG_FILES lib/libnghttp2.pc" ;; - "lib/includes/Makefile") CONFIG_FILES="$CONFIG_FILES lib/includes/Makefile" ;; - "lib/includes/nghttp2/nghttp2ver.h") CONFIG_FILES="$CONFIG_FILES lib/includes/nghttp2/nghttp2ver.h" ;; - "tests/Makefile") CONFIG_FILES="$CONFIG_FILES tests/Makefile" ;; - "tests/testdata/Makefile") CONFIG_FILES="$CONFIG_FILES tests/testdata/Makefile" ;; - "third-party/Makefile") CONFIG_FILES="$CONFIG_FILES third-party/Makefile" ;; - "src/Makefile") CONFIG_FILES="$CONFIG_FILES src/Makefile" ;; - "src/includes/Makefile") CONFIG_FILES="$CONFIG_FILES src/includes/Makefile" ;; - "src/libnghttp2_asio.pc") CONFIG_FILES="$CONFIG_FILES src/libnghttp2_asio.pc" ;; - "examples/Makefile") CONFIG_FILES="$CONFIG_FILES examples/Makefile" ;; - "python/Makefile") CONFIG_FILES="$CONFIG_FILES python/Makefile" ;; - "python/setup.py") CONFIG_FILES="$CONFIG_FILES python/setup.py" ;; - "integration-tests/Makefile") CONFIG_FILES="$CONFIG_FILES integration-tests/Makefile" ;; - "integration-tests/config.go") CONFIG_FILES="$CONFIG_FILES integration-tests/config.go" ;; - "integration-tests/setenv") CONFIG_FILES="$CONFIG_FILES integration-tests/setenv" ;; - "doc/Makefile") CONFIG_FILES="$CONFIG_FILES doc/Makefile" ;; - "doc/conf.py") CONFIG_FILES="$CONFIG_FILES doc/conf.py" ;; - "doc/index.rst") CONFIG_FILES="$CONFIG_FILES doc/index.rst" ;; - "doc/package_README.rst") CONFIG_FILES="$CONFIG_FILES doc/package_README.rst" ;; - "doc/tutorial-client.rst") CONFIG_FILES="$CONFIG_FILES doc/tutorial-client.rst" ;; - "doc/tutorial-server.rst") CONFIG_FILES="$CONFIG_FILES doc/tutorial-server.rst" ;; - "doc/tutorial-hpack.rst") CONFIG_FILES="$CONFIG_FILES doc/tutorial-hpack.rst" ;; - "doc/nghttpx-howto.rst") CONFIG_FILES="$CONFIG_FILES doc/nghttpx-howto.rst" ;; - "doc/h2load-howto.rst") CONFIG_FILES="$CONFIG_FILES doc/h2load-howto.rst" ;; - "doc/libnghttp2_asio.rst") CONFIG_FILES="$CONFIG_FILES doc/libnghttp2_asio.rst" ;; - "doc/python-apiref.rst") CONFIG_FILES="$CONFIG_FILES doc/python-apiref.rst" ;; - "doc/building-android-binary.rst") CONFIG_FILES="$CONFIG_FILES doc/building-android-binary.rst" ;; - "doc/nghttp2.h.rst") CONFIG_FILES="$CONFIG_FILES doc/nghttp2.h.rst" ;; - "doc/nghttp2ver.h.rst") CONFIG_FILES="$CONFIG_FILES doc/nghttp2ver.h.rst" ;; - "doc/asio_http2.h.rst") CONFIG_FILES="$CONFIG_FILES doc/asio_http2.h.rst" ;; - "doc/asio_http2_server.h.rst") CONFIG_FILES="$CONFIG_FILES doc/asio_http2_server.h.rst" ;; - "doc/asio_http2_client.h.rst") CONFIG_FILES="$CONFIG_FILES doc/asio_http2_client.h.rst" ;; - "doc/contribute.rst") CONFIG_FILES="$CONFIG_FILES doc/contribute.rst" ;; - "contrib/Makefile") CONFIG_FILES="$CONFIG_FILES contrib/Makefile" ;; - - *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;; - esac -done - - -# If the user did not use the arguments to specify the items to instantiate, -# then the envvar interface is used. Set only those that are not. -# We use the long form for the default assignment because of an extremely -# bizarre bug on SunOS 4.1.3. -if $ac_need_defaults; then - test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files - test "${CONFIG_HEADERS+set}" = set || CONFIG_HEADERS=$config_headers - test "${CONFIG_COMMANDS+set}" = set || CONFIG_COMMANDS=$config_commands -fi - -# Have a temporary directory for convenience. Make it in the build tree -# simply because there is no reason against having it here, and in addition, -# creating and moving files from /tmp can sometimes cause problems. -# Hook for its removal unless debugging. -# Note that there is a small window in which the directory will not be cleaned: -# after its creation but before its name has been assigned to `$tmp'. -$debug || -{ - tmp= ac_tmp= - trap 'exit_status=$? - : "${ac_tmp:=$tmp}" - { test ! -d "$ac_tmp" || rm -fr "$ac_tmp"; } && exit $exit_status -' 0 - trap 'as_fn_exit 1' 1 2 13 15 -} -# Create a (secure) tmp directory for tmp files. - -{ - tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` && - test -d "$tmp" -} || -{ - tmp=./conf$$-$RANDOM - (umask 077 && mkdir "$tmp") -} || as_fn_error $? "cannot create a temporary directory in ." "$LINENO" 5 -ac_tmp=$tmp - -# Set up the scripts for CONFIG_FILES section. -# No need to generate them if there are no CONFIG_FILES. -# This happens for instance with `./config.status config.h'. -if test -n "$CONFIG_FILES"; then - - -ac_cr=`echo X | tr X '\015'` -# On cygwin, bash can eat \r inside `` if the user requested igncr. -# But we know of no other shell where ac_cr would be empty at this -# point, so we can use a bashism as a fallback. -if test "x$ac_cr" = x; then - eval ac_cr=\$\'\\r\' -fi -ac_cs_awk_cr=`$AWK 'BEGIN { print "a\rb" }' /dev/null` -if test "$ac_cs_awk_cr" = "a${ac_cr}b"; then - ac_cs_awk_cr='\\r' -else - ac_cs_awk_cr=$ac_cr -fi - -echo 'BEGIN {' >"$ac_tmp/subs1.awk" && -_ACEOF - - -{ - echo "cat >conf$$subs.awk <<_ACEOF" && - echo "$ac_subst_vars" | sed 's/.*/&!$&$ac_delim/' && - echo "_ACEOF" -} >conf$$subs.sh || - as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 -ac_delim_num=`echo "$ac_subst_vars" | grep -c '^'` -ac_delim='%!_!# ' -for ac_last_try in false false false false false :; do - . ./conf$$subs.sh || - as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 - - ac_delim_n=`sed -n "s/.*$ac_delim\$/X/p" conf$$subs.awk | grep -c X` - if test $ac_delim_n = $ac_delim_num; then - break - elif $ac_last_try; then - as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 - else - ac_delim="$ac_delim!$ac_delim _$ac_delim!! " - fi -done -rm -f conf$$subs.sh - -cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 -cat >>"\$ac_tmp/subs1.awk" <<\\_ACAWK && -_ACEOF -sed -n ' -h -s/^/S["/; s/!.*/"]=/ -p -g -s/^[^!]*!// -:repl -t repl -s/'"$ac_delim"'$// -t delim -:nl -h -s/\(.\{148\}\)..*/\1/ -t more1 -s/["\\]/\\&/g; s/^/"/; s/$/\\n"\\/ -p -n -b repl -:more1 -s/["\\]/\\&/g; s/^/"/; s/$/"\\/ -p -g -s/.\{148\}// -t nl -:delim -h -s/\(.\{148\}\)..*/\1/ -t more2 -s/["\\]/\\&/g; s/^/"/; s/$/"/ -p -b -:more2 -s/["\\]/\\&/g; s/^/"/; s/$/"\\/ -p -g -s/.\{148\}// -t delim -' >$CONFIG_STATUS || ac_write_fail=1 -rm -f conf$$subs.awk -cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 -_ACAWK -cat >>"\$ac_tmp/subs1.awk" <<_ACAWK && - for (key in S) S_is_set[key] = 1 - FS = "" - -} -{ - line = $ 0 - nfields = split(line, field, "@") - substed = 0 - len = length(field[1]) - for (i = 2; i < nfields; i++) { - key = field[i] - keylen = length(key) - if (S_is_set[key]) { - value = S[key] - line = substr(line, 1, len) "" value "" substr(line, len + keylen + 3) - len += length(value) + length(field[++i]) - substed = 1 - } else - len += 1 + keylen - } - - print line -} - -_ACAWK -_ACEOF -cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 -if sed "s/$ac_cr//" < /dev/null > /dev/null 2>&1; then - sed "s/$ac_cr\$//; s/$ac_cr/$ac_cs_awk_cr/g" -else - cat -fi < "$ac_tmp/subs1.awk" > "$ac_tmp/subs.awk" \ - || as_fn_error $? "could not setup config files machinery" "$LINENO" 5 -_ACEOF - -# VPATH may cause trouble with some makes, so we remove sole $(srcdir), -# ${srcdir} and @srcdir@ entries from VPATH if srcdir is ".", strip leading and -# trailing colons and then remove the whole line if VPATH becomes empty -# (actually we leave an empty line to preserve line numbers). -if test "x$srcdir" = x.; then - ac_vpsub='/^[ ]*VPATH[ ]*=[ ]*/{ -h -s/// -s/^/:/ -s/[ ]*$/:/ -s/:\$(srcdir):/:/g -s/:\${srcdir}:/:/g -s/:@srcdir@:/:/g -s/^:*// -s/:*$// -x -s/\(=[ ]*\).*/\1/ -G -s/\n// -s/^[^=]*=[ ]*$// -}' -fi - -cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 -fi # test -n "$CONFIG_FILES" - -# Set up the scripts for CONFIG_HEADERS section. -# No need to generate them if there are no CONFIG_HEADERS. -# This happens for instance with `./config.status Makefile'. -if test -n "$CONFIG_HEADERS"; then -cat >"$ac_tmp/defines.awk" <<\_ACAWK || -BEGIN { -_ACEOF - -# Transform confdefs.h into an awk script `defines.awk', embedded as -# here-document in config.status, that substitutes the proper values into -# config.h.in to produce config.h. - -# Create a delimiter string that does not exist in confdefs.h, to ease -# handling of long lines. -ac_delim='%!_!# ' -for ac_last_try in false false :; do - ac_tt=`sed -n "/$ac_delim/p" confdefs.h` - if test -z "$ac_tt"; then - break - elif $ac_last_try; then - as_fn_error $? "could not make $CONFIG_HEADERS" "$LINENO" 5 - else - ac_delim="$ac_delim!$ac_delim _$ac_delim!! " - fi -done - -# For the awk script, D is an array of macro values keyed by name, -# likewise P contains macro parameters if any. Preserve backslash -# newline sequences. - -ac_word_re=[_$as_cr_Letters][_$as_cr_alnum]* -sed -n ' -s/.\{148\}/&'"$ac_delim"'/g -t rset -:rset -s/^[ ]*#[ ]*define[ ][ ]*/ / -t def -d -:def -s/\\$// -t bsnl -s/["\\]/\\&/g -s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\ -D["\1"]=" \3"/p -s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2"/p -d -:bsnl -s/["\\]/\\&/g -s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\ -D["\1"]=" \3\\\\\\n"\\/p -t cont -s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2\\\\\\n"\\/p -t cont -d -:cont -n -s/.\{148\}/&'"$ac_delim"'/g -t clear -:clear -s/\\$// -t bsnlc -s/["\\]/\\&/g; s/^/"/; s/$/"/p -d -:bsnlc -s/["\\]/\\&/g; s/^/"/; s/$/\\\\\\n"\\/p -b cont -' >$CONFIG_STATUS || ac_write_fail=1 - -cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 - for (key in D) D_is_set[key] = 1 - FS = "" -} -/^[\t ]*#[\t ]*(define|undef)[\t ]+$ac_word_re([\t (]|\$)/ { - line = \$ 0 - split(line, arg, " ") - if (arg[1] == "#") { - defundef = arg[2] - mac1 = arg[3] - } else { - defundef = substr(arg[1], 2) - mac1 = arg[2] - } - split(mac1, mac2, "(") #) - macro = mac2[1] - prefix = substr(line, 1, index(line, defundef) - 1) - if (D_is_set[macro]) { - # Preserve the white space surrounding the "#". - print prefix "define", macro P[macro] D[macro] - next - } else { - # Replace #undef with comments. This is necessary, for example, - # in the case of _POSIX_SOURCE, which is predefined and required - # on some systems where configure will not decide to define it. - if (defundef == "undef") { - print "/*", prefix defundef, macro, "*/" - next - } - } -} -{ print } -_ACAWK -_ACEOF -cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 - as_fn_error $? "could not setup config headers machinery" "$LINENO" 5 -fi # test -n "$CONFIG_HEADERS" - - -eval set X " :F $CONFIG_FILES :H $CONFIG_HEADERS :C $CONFIG_COMMANDS" -shift -for ac_tag -do - case $ac_tag in - :[FHLC]) ac_mode=$ac_tag; continue;; - esac - case $ac_mode$ac_tag in - :[FHL]*:*);; - :L* | :C*:*) as_fn_error $? "invalid tag \`$ac_tag'" "$LINENO" 5;; - :[FH]-) ac_tag=-:-;; - :[FH]*) ac_tag=$ac_tag:$ac_tag.in;; - esac - ac_save_IFS=$IFS - IFS=: - set x $ac_tag - IFS=$ac_save_IFS - shift - ac_file=$1 - shift - - case $ac_mode in - :L) ac_source=$1;; - :[FH]) - ac_file_inputs= - for ac_f - do - case $ac_f in - -) ac_f="$ac_tmp/stdin";; - *) # Look for the file first in the build tree, then in the source tree - # (if the path is not absolute). The absolute path cannot be DOS-style, - # because $ac_f cannot contain `:'. - test -f "$ac_f" || - case $ac_f in - [\\/$]*) false;; - *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";; - esac || - as_fn_error 1 "cannot find input file: \`$ac_f'" "$LINENO" 5;; - esac - case $ac_f in *\'*) ac_f=`$as_echo "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac - as_fn_append ac_file_inputs " '$ac_f'" - done - - # Let's still pretend it is `configure' which instantiates (i.e., don't - # use $as_me), people would be surprised to read: - # /* config.h. Generated by config.status. */ - configure_input='Generated from '` - $as_echo "$*" | sed 's|^[^:]*/||;s|:[^:]*/|, |g' - `' by configure.' - if test x"$ac_file" != x-; then - configure_input="$ac_file. $configure_input" - { $as_echo "$as_me:${as_lineno-$LINENO}: creating $ac_file" >&5 -$as_echo "$as_me: creating $ac_file" >&6;} - fi - # Neutralize special characters interpreted by sed in replacement strings. - case $configure_input in #( - *\&* | *\|* | *\\* ) - ac_sed_conf_input=`$as_echo "$configure_input" | - sed 's/[\\\\&|]/\\\\&/g'`;; #( - *) ac_sed_conf_input=$configure_input;; - esac - - case $ac_tag in - *:-:* | *:-) cat >"$ac_tmp/stdin" \ - || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;; - esac - ;; - esac - - ac_dir=`$as_dirname -- "$ac_file" || -$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ - X"$ac_file" : 'X\(//\)[^/]' \| \ - X"$ac_file" : 'X\(//\)$' \| \ - X"$ac_file" : 'X\(/\)' \| . 2>/dev/null || -$as_echo X"$ac_file" | - sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ - s//\1/ - q - } - /^X\(\/\/\)[^/].*/{ - s//\1/ - q - } - /^X\(\/\/\)$/{ - s//\1/ - q - } - /^X\(\/\).*/{ - s//\1/ - q - } - s/.*/./; q'` - as_dir="$ac_dir"; as_fn_mkdir_p - ac_builddir=. - -case "$ac_dir" in -.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; -*) - ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` - # A ".." for each directory in $ac_dir_suffix. - ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` - case $ac_top_builddir_sub in - "") ac_top_builddir_sub=. ac_top_build_prefix= ;; - *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; - esac ;; -esac -ac_abs_top_builddir=$ac_pwd -ac_abs_builddir=$ac_pwd$ac_dir_suffix -# for backward compatibility: -ac_top_builddir=$ac_top_build_prefix - -case $srcdir in - .) # We are building in place. - ac_srcdir=. - ac_top_srcdir=$ac_top_builddir_sub - ac_abs_top_srcdir=$ac_pwd ;; - [\\/]* | ?:[\\/]* ) # Absolute name. - ac_srcdir=$srcdir$ac_dir_suffix; - ac_top_srcdir=$srcdir - ac_abs_top_srcdir=$srcdir ;; - *) # Relative name. - ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix - ac_top_srcdir=$ac_top_build_prefix$srcdir - ac_abs_top_srcdir=$ac_pwd/$srcdir ;; -esac -ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix - - - case $ac_mode in - :F) - # - # CONFIG_FILE - # - - case $INSTALL in - [\\/$]* | ?:[\\/]* ) ac_INSTALL=$INSTALL ;; - *) ac_INSTALL=$ac_top_build_prefix$INSTALL ;; - esac - ac_MKDIR_P=$MKDIR_P - case $MKDIR_P in - [\\/$]* | ?:[\\/]* ) ;; - */*) ac_MKDIR_P=$ac_top_build_prefix$MKDIR_P ;; - esac -_ACEOF - -cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 -# If the template does not know about datarootdir, expand it. -# FIXME: This hack should be removed a few years after 2.60. -ac_datarootdir_hack=; ac_datarootdir_seen= -ac_sed_dataroot=' -/datarootdir/ { - p - q -} -/@datadir@/p -/@docdir@/p -/@infodir@/p -/@localedir@/p -/@mandir@/p' -case `eval "sed -n \"\$ac_sed_dataroot\" $ac_file_inputs"` in -*datarootdir*) ac_datarootdir_seen=yes;; -*@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*) - { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5 -$as_echo "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;} -_ACEOF -cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 - ac_datarootdir_hack=' - s&@datadir@&$datadir&g - s&@docdir@&$docdir&g - s&@infodir@&$infodir&g - s&@localedir@&$localedir&g - s&@mandir@&$mandir&g - s&\\\${datarootdir}&$datarootdir&g' ;; -esac -_ACEOF - -# Neutralize VPATH when `$srcdir' = `.'. -# Shell code in configure.ac might set extrasub. -# FIXME: do we really want to maintain this feature? -cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 -ac_sed_extra="$ac_vpsub -$extrasub -_ACEOF -cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 -:t -/@[a-zA-Z_][a-zA-Z_0-9]*@/!b -s|@configure_input@|$ac_sed_conf_input|;t t -s&@top_builddir@&$ac_top_builddir_sub&;t t -s&@top_build_prefix@&$ac_top_build_prefix&;t t -s&@srcdir@&$ac_srcdir&;t t -s&@abs_srcdir@&$ac_abs_srcdir&;t t -s&@top_srcdir@&$ac_top_srcdir&;t t -s&@abs_top_srcdir@&$ac_abs_top_srcdir&;t t -s&@builddir@&$ac_builddir&;t t -s&@abs_builddir@&$ac_abs_builddir&;t t -s&@abs_top_builddir@&$ac_abs_top_builddir&;t t -s&@INSTALL@&$ac_INSTALL&;t t -s&@MKDIR_P@&$ac_MKDIR_P&;t t -$ac_datarootdir_hack -" -eval sed \"\$ac_sed_extra\" "$ac_file_inputs" | $AWK -f "$ac_tmp/subs.awk" \ - >$ac_tmp/out || as_fn_error $? "could not create $ac_file" "$LINENO" 5 - -test -z "$ac_datarootdir_hack$ac_datarootdir_seen" && - { ac_out=`sed -n '/\${datarootdir}/p' "$ac_tmp/out"`; test -n "$ac_out"; } && - { ac_out=`sed -n '/^[ ]*datarootdir[ ]*:*=/p' \ - "$ac_tmp/out"`; test -z "$ac_out"; } && - { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file contains a reference to the variable \`datarootdir' -which seems to be undefined. Please make sure it is defined" >&5 -$as_echo "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir' -which seems to be undefined. Please make sure it is defined" >&2;} - - rm -f "$ac_tmp/stdin" - case $ac_file in - -) cat "$ac_tmp/out" && rm -f "$ac_tmp/out";; - *) rm -f "$ac_file" && mv "$ac_tmp/out" "$ac_file";; - esac \ - || as_fn_error $? "could not create $ac_file" "$LINENO" 5 - ;; - :H) - # - # CONFIG_HEADER - # - if test x"$ac_file" != x-; then - { - $as_echo "/* $configure_input */" \ - && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" - } >"$ac_tmp/config.h" \ - || as_fn_error $? "could not create $ac_file" "$LINENO" 5 - if diff "$ac_file" "$ac_tmp/config.h" >/dev/null 2>&1; then - { $as_echo "$as_me:${as_lineno-$LINENO}: $ac_file is unchanged" >&5 -$as_echo "$as_me: $ac_file is unchanged" >&6;} - else - rm -f "$ac_file" - mv "$ac_tmp/config.h" "$ac_file" \ - || as_fn_error $? "could not create $ac_file" "$LINENO" 5 - fi - else - $as_echo "/* $configure_input */" \ - && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" \ - || as_fn_error $? "could not create -" "$LINENO" 5 - fi -# Compute "$ac_file"'s index in $config_headers. -_am_arg="$ac_file" -_am_stamp_count=1 -for _am_header in $config_headers :; do - case $_am_header in - $_am_arg | $_am_arg:* ) - break ;; - * ) - _am_stamp_count=`expr $_am_stamp_count + 1` ;; - esac -done -echo "timestamp for $_am_arg" >`$as_dirname -- "$_am_arg" || -$as_expr X"$_am_arg" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ - X"$_am_arg" : 'X\(//\)[^/]' \| \ - X"$_am_arg" : 'X\(//\)$' \| \ - X"$_am_arg" : 'X\(/\)' \| . 2>/dev/null || -$as_echo X"$_am_arg" | - sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ - s//\1/ - q - } - /^X\(\/\/\)[^/].*/{ - s//\1/ - q - } - /^X\(\/\/\)$/{ - s//\1/ - q - } - /^X\(\/\).*/{ - s//\1/ - q - } - s/.*/./; q'`/stamp-h$_am_stamp_count - ;; - - :C) { $as_echo "$as_me:${as_lineno-$LINENO}: executing $ac_file commands" >&5 -$as_echo "$as_me: executing $ac_file commands" >&6;} - ;; - esac - - - case $ac_file$ac_mode in - "libtool":C) - - # See if we are running on zsh, and set the options which allow our - # commands through without removal of \ escapes. - if test -n "${ZSH_VERSION+set}" ; then - setopt NO_GLOB_SUBST - fi - - cfgfile="${ofile}T" - trap "$RM \"$cfgfile\"; exit 1" 1 2 15 - $RM "$cfgfile" - - cat <<_LT_EOF >> "$cfgfile" -#! $SHELL - -# `$ECHO "$ofile" | sed 's%^.*/%%'` - Provide generalized library-building support services. -# Generated automatically by $as_me ($PACKAGE$TIMESTAMP) $VERSION -# Libtool was configured on host `(hostname || uname -n) 2>/dev/null | sed 1q`: -# NOTE: Changes made to this file will be lost: look at ltmain.sh. -# -# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2003, 2004, 2005, -# 2006, 2007, 2008, 2009, 2010, 2011 Free Software -# Foundation, Inc. -# Written by Gordon Matzigkeit, 1996 -# -# This file is part of GNU Libtool. -# -# GNU Libtool 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 2 of -# the License, or (at your option) any later version. -# -# As a special exception to the GNU General Public License, -# if you distribute this file as part of a program or library that -# is built using GNU Libtool, you may include this file under the -# same distribution terms that you use for the rest of that program. -# -# GNU Libtool 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 GNU Libtool; see the file COPYING. If not, a copy -# can be downloaded from http://www.gnu.org/licenses/gpl.html, or -# obtained by writing to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - - -# The names of the tagged configurations supported by this script. -available_tags="CXX " - -# ### BEGIN LIBTOOL CONFIG - -# Which release of libtool.m4 was used? -macro_version=$macro_version -macro_revision=$macro_revision - -# Whether or not to build shared libraries. -build_libtool_libs=$enable_shared - -# Whether or not to build static libraries. -build_old_libs=$enable_static - -# What type of objects to build. -pic_mode=$pic_mode - -# Whether or not to optimize for fast installation. -fast_install=$enable_fast_install - -# Shell to use when invoking shell scripts. -SHELL=$lt_SHELL - -# An echo program that protects backslashes. -ECHO=$lt_ECHO - -# The PATH separator for the build system. -PATH_SEPARATOR=$lt_PATH_SEPARATOR - -# The host system. -host_alias=$host_alias -host=$host -host_os=$host_os - -# The build system. -build_alias=$build_alias -build=$build -build_os=$build_os - -# A sed program that does not truncate output. -SED=$lt_SED - -# Sed that helps us avoid accidentally triggering echo(1) options like -n. -Xsed="\$SED -e 1s/^X//" - -# A grep program that handles long lines. -GREP=$lt_GREP - -# An ERE matcher. -EGREP=$lt_EGREP - -# A literal string matcher. -FGREP=$lt_FGREP - -# A BSD- or MS-compatible name lister. -NM=$lt_NM - -# Whether we need soft or hard links. -LN_S=$lt_LN_S - -# What is the maximum length of a command? -max_cmd_len=$max_cmd_len - -# Object file suffix (normally "o"). -objext=$ac_objext - -# Executable file suffix (normally ""). -exeext=$exeext - -# whether the shell understands "unset". -lt_unset=$lt_unset - -# turn spaces into newlines. -SP2NL=$lt_lt_SP2NL - -# turn newlines into spaces. -NL2SP=$lt_lt_NL2SP - -# convert \$build file names to \$host format. -to_host_file_cmd=$lt_cv_to_host_file_cmd - -# convert \$build files to toolchain format. -to_tool_file_cmd=$lt_cv_to_tool_file_cmd - -# An object symbol dumper. -OBJDUMP=$lt_OBJDUMP - -# Method to check whether dependent libraries are shared objects. -deplibs_check_method=$lt_deplibs_check_method - -# Command to use when deplibs_check_method = "file_magic". -file_magic_cmd=$lt_file_magic_cmd - -# How to find potential files when deplibs_check_method = "file_magic". -file_magic_glob=$lt_file_magic_glob - -# Find potential files using nocaseglob when deplibs_check_method = "file_magic". -want_nocaseglob=$lt_want_nocaseglob - -# DLL creation program. -DLLTOOL=$lt_DLLTOOL - -# Command to associate shared and link libraries. -sharedlib_from_linklib_cmd=$lt_sharedlib_from_linklib_cmd - -# The archiver. -AR=$lt_AR - -# Flags to create an archive. -AR_FLAGS=$lt_AR_FLAGS - -# How to feed a file listing to the archiver. -archiver_list_spec=$lt_archiver_list_spec - -# A symbol stripping program. -STRIP=$lt_STRIP - -# Commands used to install an old-style archive. -RANLIB=$lt_RANLIB -old_postinstall_cmds=$lt_old_postinstall_cmds -old_postuninstall_cmds=$lt_old_postuninstall_cmds - -# Whether to use a lock for old archive extraction. -lock_old_archive_extraction=$lock_old_archive_extraction - -# A C compiler. -LTCC=$lt_CC - -# LTCC compiler flags. -LTCFLAGS=$lt_CFLAGS - -# Take the output of nm and produce a listing of raw symbols and C names. -global_symbol_pipe=$lt_lt_cv_sys_global_symbol_pipe - -# Transform the output of nm in a proper C declaration. -global_symbol_to_cdecl=$lt_lt_cv_sys_global_symbol_to_cdecl - -# Transform the output of nm in a C name address pair. -global_symbol_to_c_name_address=$lt_lt_cv_sys_global_symbol_to_c_name_address - -# Transform the output of nm in a C name address pair when lib prefix is needed. -global_symbol_to_c_name_address_lib_prefix=$lt_lt_cv_sys_global_symbol_to_c_name_address_lib_prefix - -# Specify filename containing input files for \$NM. -nm_file_list_spec=$lt_nm_file_list_spec - -# The root where to search for dependent libraries,and in which our libraries should be installed. -lt_sysroot=$lt_sysroot - -# The name of the directory that contains temporary libtool files. -objdir=$objdir - -# Used to examine libraries when file_magic_cmd begins with "file". -MAGIC_CMD=$MAGIC_CMD - -# Must we lock files when doing compilation? -need_locks=$lt_need_locks - -# Manifest tool. -MANIFEST_TOOL=$lt_MANIFEST_TOOL - -# Tool to manipulate archived DWARF debug symbol files on Mac OS X. -DSYMUTIL=$lt_DSYMUTIL - -# Tool to change global to local symbols on Mac OS X. -NMEDIT=$lt_NMEDIT - -# Tool to manipulate fat objects and archives on Mac OS X. -LIPO=$lt_LIPO - -# ldd/readelf like tool for Mach-O binaries on Mac OS X. -OTOOL=$lt_OTOOL - -# ldd/readelf like tool for 64 bit Mach-O binaries on Mac OS X 10.4. -OTOOL64=$lt_OTOOL64 - -# Old archive suffix (normally "a"). -libext=$libext - -# Shared library suffix (normally ".so"). -shrext_cmds=$lt_shrext_cmds - -# The commands to extract the exported symbol list from a shared archive. -extract_expsyms_cmds=$lt_extract_expsyms_cmds - -# Variables whose values should be saved in libtool wrapper scripts and -# restored at link time. -variables_saved_for_relink=$lt_variables_saved_for_relink - -# Do we need the "lib" prefix for modules? -need_lib_prefix=$need_lib_prefix - -# Do we need a version for libraries? -need_version=$need_version - -# Library versioning type. -version_type=$version_type - -# Shared library runtime path variable. -runpath_var=$runpath_var - -# Shared library path variable. -shlibpath_var=$shlibpath_var - -# Is shlibpath searched before the hard-coded library search path? -shlibpath_overrides_runpath=$shlibpath_overrides_runpath - -# Format of library name prefix. -libname_spec=$lt_libname_spec - -# List of archive names. First name is the real one, the rest are links. -# The last name is the one that the linker finds with -lNAME -library_names_spec=$lt_library_names_spec - -# The coded name of the library, if different from the real name. -soname_spec=$lt_soname_spec - -# Permission mode override for installation of shared libraries. -install_override_mode=$lt_install_override_mode - -# Command to use after installation of a shared archive. -postinstall_cmds=$lt_postinstall_cmds - -# Command to use after uninstallation of a shared archive. -postuninstall_cmds=$lt_postuninstall_cmds - -# Commands used to finish a libtool library installation in a directory. -finish_cmds=$lt_finish_cmds - -# As "finish_cmds", except a single script fragment to be evaled but -# not shown. -finish_eval=$lt_finish_eval - -# Whether we should hardcode library paths into libraries. -hardcode_into_libs=$hardcode_into_libs - -# Compile-time system search path for libraries. -sys_lib_search_path_spec=$lt_sys_lib_search_path_spec - -# Run-time system search path for libraries. -sys_lib_dlsearch_path_spec=$lt_sys_lib_dlsearch_path_spec - -# Whether dlopen is supported. -dlopen_support=$enable_dlopen - -# Whether dlopen of programs is supported. -dlopen_self=$enable_dlopen_self - -# Whether dlopen of statically linked programs is supported. -dlopen_self_static=$enable_dlopen_self_static - -# Commands to strip libraries. -old_striplib=$lt_old_striplib -striplib=$lt_striplib - - -# The linker used to build libraries. -LD=$lt_LD - -# How to create reloadable object files. -reload_flag=$lt_reload_flag -reload_cmds=$lt_reload_cmds - -# Commands used to build an old-style archive. -old_archive_cmds=$lt_old_archive_cmds - -# A language specific compiler. -CC=$lt_compiler - -# Is the compiler the GNU compiler? -with_gcc=$GCC - -# Compiler flag to turn off builtin functions. -no_builtin_flag=$lt_lt_prog_compiler_no_builtin_flag - -# Additional compiler flags for building library objects. -pic_flag=$lt_lt_prog_compiler_pic - -# How to pass a linker flag through the compiler. -wl=$lt_lt_prog_compiler_wl - -# Compiler flag to prevent dynamic linking. -link_static_flag=$lt_lt_prog_compiler_static - -# Does compiler simultaneously support -c and -o options? -compiler_c_o=$lt_lt_cv_prog_compiler_c_o - -# Whether or not to add -lc for building shared libraries. -build_libtool_need_lc=$archive_cmds_need_lc - -# Whether or not to disallow shared libs when runtime libs are static. -allow_libtool_libs_with_static_runtimes=$enable_shared_with_static_runtimes - -# Compiler flag to allow reflexive dlopens. -export_dynamic_flag_spec=$lt_export_dynamic_flag_spec - -# Compiler flag to generate shared objects directly from archives. -whole_archive_flag_spec=$lt_whole_archive_flag_spec - -# Whether the compiler copes with passing no objects directly. -compiler_needs_object=$lt_compiler_needs_object - -# Create an old-style archive from a shared archive. -old_archive_from_new_cmds=$lt_old_archive_from_new_cmds - -# Create a temporary old-style archive to link instead of a shared archive. -old_archive_from_expsyms_cmds=$lt_old_archive_from_expsyms_cmds - -# Commands used to build a shared archive. -archive_cmds=$lt_archive_cmds -archive_expsym_cmds=$lt_archive_expsym_cmds - -# Commands used to build a loadable module if different from building -# a shared archive. -module_cmds=$lt_module_cmds -module_expsym_cmds=$lt_module_expsym_cmds - -# Whether we are building with GNU ld or not. -with_gnu_ld=$lt_with_gnu_ld - -# Flag that allows shared libraries with undefined symbols to be built. -allow_undefined_flag=$lt_allow_undefined_flag - -# Flag that enforces no undefined symbols. -no_undefined_flag=$lt_no_undefined_flag - -# Flag to hardcode \$libdir into a binary during linking. -# This must work even if \$libdir does not exist -hardcode_libdir_flag_spec=$lt_hardcode_libdir_flag_spec - -# Whether we need a single "-rpath" flag with a separated argument. -hardcode_libdir_separator=$lt_hardcode_libdir_separator - -# Set to "yes" if using DIR/libNAME\${shared_ext} during linking hardcodes -# DIR into the resulting binary. -hardcode_direct=$hardcode_direct - -# Set to "yes" if using DIR/libNAME\${shared_ext} during linking hardcodes -# DIR into the resulting binary and the resulting library dependency is -# "absolute",i.e impossible to change by setting \${shlibpath_var} if the -# library is relocated. -hardcode_direct_absolute=$hardcode_direct_absolute - -# Set to "yes" if using the -LDIR flag during linking hardcodes DIR -# into the resulting binary. -hardcode_minus_L=$hardcode_minus_L - -# Set to "yes" if using SHLIBPATH_VAR=DIR during linking hardcodes DIR -# into the resulting binary. -hardcode_shlibpath_var=$hardcode_shlibpath_var - -# Set to "yes" if building a shared library automatically hardcodes DIR -# into the library and all subsequent libraries and executables linked -# against it. -hardcode_automatic=$hardcode_automatic - -# Set to yes if linker adds runtime paths of dependent libraries -# to runtime path list. -inherit_rpath=$inherit_rpath - -# Whether libtool must link a program against all its dependency libraries. -link_all_deplibs=$link_all_deplibs - -# Set to "yes" if exported symbols are required. -always_export_symbols=$always_export_symbols - -# The commands to list exported symbols. -export_symbols_cmds=$lt_export_symbols_cmds - -# Symbols that should not be listed in the preloaded symbols. -exclude_expsyms=$lt_exclude_expsyms - -# Symbols that must always be exported. -include_expsyms=$lt_include_expsyms - -# Commands necessary for linking programs (against libraries) with templates. -prelink_cmds=$lt_prelink_cmds - -# Commands necessary for finishing linking programs. -postlink_cmds=$lt_postlink_cmds - -# Specify filename containing input files. -file_list_spec=$lt_file_list_spec - -# How to hardcode a shared library path into an executable. -hardcode_action=$hardcode_action - -# The directories searched by this compiler when creating a shared library. -compiler_lib_search_dirs=$lt_compiler_lib_search_dirs - -# Dependencies to place before and after the objects being linked to -# create a shared library. -predep_objects=$lt_predep_objects -postdep_objects=$lt_postdep_objects -predeps=$lt_predeps -postdeps=$lt_postdeps - -# The library search path used internally by the compiler when linking -# a shared library. -compiler_lib_search_path=$lt_compiler_lib_search_path - -# ### END LIBTOOL CONFIG - -_LT_EOF - - case $host_os in - aix3*) - cat <<\_LT_EOF >> "$cfgfile" -# AIX sometimes has problems with the GCC collect2 program. For some -# reason, if we set the COLLECT_NAMES environment variable, the problems -# vanish in a puff of smoke. -if test "X${COLLECT_NAMES+set}" != Xset; then - COLLECT_NAMES= - export COLLECT_NAMES -fi -_LT_EOF - ;; - esac - - -ltmain="$ac_aux_dir/ltmain.sh" - - - # We use sed instead of cat because bash on DJGPP gets confused if - # if finds mixed CR/LF and LF-only lines. Since sed operates in - # text mode, it properly converts lines to CR/LF. This bash problem - # is reportedly fixed, but why not run on old versions too? - sed '$q' "$ltmain" >> "$cfgfile" \ - || (rm -f "$cfgfile"; exit 1) - - if test x"$xsi_shell" = xyes; then - sed -e '/^func_dirname ()$/,/^} # func_dirname /c\ -func_dirname ()\ -{\ -\ case ${1} in\ -\ */*) func_dirname_result="${1%/*}${2}" ;;\ -\ * ) func_dirname_result="${3}" ;;\ -\ esac\ -} # Extended-shell func_dirname implementation' "$cfgfile" > $cfgfile.tmp \ - && mv -f "$cfgfile.tmp" "$cfgfile" \ - || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") -test 0 -eq $? || _lt_function_replace_fail=: - - - sed -e '/^func_basename ()$/,/^} # func_basename /c\ -func_basename ()\ -{\ -\ func_basename_result="${1##*/}"\ -} # Extended-shell func_basename implementation' "$cfgfile" > $cfgfile.tmp \ - && mv -f "$cfgfile.tmp" "$cfgfile" \ - || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") -test 0 -eq $? || _lt_function_replace_fail=: - - - sed -e '/^func_dirname_and_basename ()$/,/^} # func_dirname_and_basename /c\ -func_dirname_and_basename ()\ -{\ -\ case ${1} in\ -\ */*) func_dirname_result="${1%/*}${2}" ;;\ -\ * ) func_dirname_result="${3}" ;;\ -\ esac\ -\ func_basename_result="${1##*/}"\ -} # Extended-shell func_dirname_and_basename implementation' "$cfgfile" > $cfgfile.tmp \ - && mv -f "$cfgfile.tmp" "$cfgfile" \ - || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") -test 0 -eq $? || _lt_function_replace_fail=: - - - sed -e '/^func_stripname ()$/,/^} # func_stripname /c\ -func_stripname ()\ -{\ -\ # pdksh 5.2.14 does not do ${X%$Y} correctly if both X and Y are\ -\ # positional parameters, so assign one to ordinary parameter first.\ -\ func_stripname_result=${3}\ -\ func_stripname_result=${func_stripname_result#"${1}"}\ -\ func_stripname_result=${func_stripname_result%"${2}"}\ -} # Extended-shell func_stripname implementation' "$cfgfile" > $cfgfile.tmp \ - && mv -f "$cfgfile.tmp" "$cfgfile" \ - || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") -test 0 -eq $? || _lt_function_replace_fail=: - - - sed -e '/^func_split_long_opt ()$/,/^} # func_split_long_opt /c\ -func_split_long_opt ()\ -{\ -\ func_split_long_opt_name=${1%%=*}\ -\ func_split_long_opt_arg=${1#*=}\ -} # Extended-shell func_split_long_opt implementation' "$cfgfile" > $cfgfile.tmp \ - && mv -f "$cfgfile.tmp" "$cfgfile" \ - || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") -test 0 -eq $? || _lt_function_replace_fail=: - - - sed -e '/^func_split_short_opt ()$/,/^} # func_split_short_opt /c\ -func_split_short_opt ()\ -{\ -\ func_split_short_opt_arg=${1#??}\ -\ func_split_short_opt_name=${1%"$func_split_short_opt_arg"}\ -} # Extended-shell func_split_short_opt implementation' "$cfgfile" > $cfgfile.tmp \ - && mv -f "$cfgfile.tmp" "$cfgfile" \ - || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") -test 0 -eq $? || _lt_function_replace_fail=: - - - sed -e '/^func_lo2o ()$/,/^} # func_lo2o /c\ -func_lo2o ()\ -{\ -\ case ${1} in\ -\ *.lo) func_lo2o_result=${1%.lo}.${objext} ;;\ -\ *) func_lo2o_result=${1} ;;\ -\ esac\ -} # Extended-shell func_lo2o implementation' "$cfgfile" > $cfgfile.tmp \ - && mv -f "$cfgfile.tmp" "$cfgfile" \ - || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") -test 0 -eq $? || _lt_function_replace_fail=: - - - sed -e '/^func_xform ()$/,/^} # func_xform /c\ -func_xform ()\ -{\ - func_xform_result=${1%.*}.lo\ -} # Extended-shell func_xform implementation' "$cfgfile" > $cfgfile.tmp \ - && mv -f "$cfgfile.tmp" "$cfgfile" \ - || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") -test 0 -eq $? || _lt_function_replace_fail=: - - - sed -e '/^func_arith ()$/,/^} # func_arith /c\ -func_arith ()\ -{\ - func_arith_result=$(( $* ))\ -} # Extended-shell func_arith implementation' "$cfgfile" > $cfgfile.tmp \ - && mv -f "$cfgfile.tmp" "$cfgfile" \ - || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") -test 0 -eq $? || _lt_function_replace_fail=: - - - sed -e '/^func_len ()$/,/^} # func_len /c\ -func_len ()\ -{\ - func_len_result=${#1}\ -} # Extended-shell func_len implementation' "$cfgfile" > $cfgfile.tmp \ - && mv -f "$cfgfile.tmp" "$cfgfile" \ - || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") -test 0 -eq $? || _lt_function_replace_fail=: - -fi - -if test x"$lt_shell_append" = xyes; then - sed -e '/^func_append ()$/,/^} # func_append /c\ -func_append ()\ -{\ - eval "${1}+=\\${2}"\ -} # Extended-shell func_append implementation' "$cfgfile" > $cfgfile.tmp \ - && mv -f "$cfgfile.tmp" "$cfgfile" \ - || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") -test 0 -eq $? || _lt_function_replace_fail=: - - - sed -e '/^func_append_quoted ()$/,/^} # func_append_quoted /c\ -func_append_quoted ()\ -{\ -\ func_quote_for_eval "${2}"\ -\ eval "${1}+=\\\\ \\$func_quote_for_eval_result"\ -} # Extended-shell func_append_quoted implementation' "$cfgfile" > $cfgfile.tmp \ - && mv -f "$cfgfile.tmp" "$cfgfile" \ - || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") -test 0 -eq $? || _lt_function_replace_fail=: - - - # Save a `func_append' function call where possible by direct use of '+=' - sed -e 's%func_append \([a-zA-Z_]\{1,\}\) "%\1+="%g' $cfgfile > $cfgfile.tmp \ - && mv -f "$cfgfile.tmp" "$cfgfile" \ - || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") - test 0 -eq $? || _lt_function_replace_fail=: -else - # Save a `func_append' function call even when '+=' is not available - sed -e 's%func_append \([a-zA-Z_]\{1,\}\) "%\1="$\1%g' $cfgfile > $cfgfile.tmp \ - && mv -f "$cfgfile.tmp" "$cfgfile" \ - || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") - test 0 -eq $? || _lt_function_replace_fail=: -fi - -if test x"$_lt_function_replace_fail" = x":"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Unable to substitute extended shell functions in $ofile" >&5 -$as_echo "$as_me: WARNING: Unable to substitute extended shell functions in $ofile" >&2;} -fi - - - mv -f "$cfgfile" "$ofile" || - (rm -f "$ofile" && cp "$cfgfile" "$ofile" && rm -f "$cfgfile") - chmod +x "$ofile" - - - cat <<_LT_EOF >> "$ofile" - -# ### BEGIN LIBTOOL TAG CONFIG: CXX - -# The linker used to build libraries. -LD=$lt_LD_CXX - -# How to create reloadable object files. -reload_flag=$lt_reload_flag_CXX -reload_cmds=$lt_reload_cmds_CXX - -# Commands used to build an old-style archive. -old_archive_cmds=$lt_old_archive_cmds_CXX - -# A language specific compiler. -CC=$lt_compiler_CXX - -# Is the compiler the GNU compiler? -with_gcc=$GCC_CXX - -# Compiler flag to turn off builtin functions. -no_builtin_flag=$lt_lt_prog_compiler_no_builtin_flag_CXX - -# Additional compiler flags for building library objects. -pic_flag=$lt_lt_prog_compiler_pic_CXX - -# How to pass a linker flag through the compiler. -wl=$lt_lt_prog_compiler_wl_CXX - -# Compiler flag to prevent dynamic linking. -link_static_flag=$lt_lt_prog_compiler_static_CXX - -# Does compiler simultaneously support -c and -o options? -compiler_c_o=$lt_lt_cv_prog_compiler_c_o_CXX - -# Whether or not to add -lc for building shared libraries. -build_libtool_need_lc=$archive_cmds_need_lc_CXX - -# Whether or not to disallow shared libs when runtime libs are static. -allow_libtool_libs_with_static_runtimes=$enable_shared_with_static_runtimes_CXX - -# Compiler flag to allow reflexive dlopens. -export_dynamic_flag_spec=$lt_export_dynamic_flag_spec_CXX - -# Compiler flag to generate shared objects directly from archives. -whole_archive_flag_spec=$lt_whole_archive_flag_spec_CXX - -# Whether the compiler copes with passing no objects directly. -compiler_needs_object=$lt_compiler_needs_object_CXX - -# Create an old-style archive from a shared archive. -old_archive_from_new_cmds=$lt_old_archive_from_new_cmds_CXX - -# Create a temporary old-style archive to link instead of a shared archive. -old_archive_from_expsyms_cmds=$lt_old_archive_from_expsyms_cmds_CXX - -# Commands used to build a shared archive. -archive_cmds=$lt_archive_cmds_CXX -archive_expsym_cmds=$lt_archive_expsym_cmds_CXX - -# Commands used to build a loadable module if different from building -# a shared archive. -module_cmds=$lt_module_cmds_CXX -module_expsym_cmds=$lt_module_expsym_cmds_CXX - -# Whether we are building with GNU ld or not. -with_gnu_ld=$lt_with_gnu_ld_CXX - -# Flag that allows shared libraries with undefined symbols to be built. -allow_undefined_flag=$lt_allow_undefined_flag_CXX - -# Flag that enforces no undefined symbols. -no_undefined_flag=$lt_no_undefined_flag_CXX - -# Flag to hardcode \$libdir into a binary during linking. -# This must work even if \$libdir does not exist -hardcode_libdir_flag_spec=$lt_hardcode_libdir_flag_spec_CXX - -# Whether we need a single "-rpath" flag with a separated argument. -hardcode_libdir_separator=$lt_hardcode_libdir_separator_CXX - -# Set to "yes" if using DIR/libNAME\${shared_ext} during linking hardcodes -# DIR into the resulting binary. -hardcode_direct=$hardcode_direct_CXX - -# Set to "yes" if using DIR/libNAME\${shared_ext} during linking hardcodes -# DIR into the resulting binary and the resulting library dependency is -# "absolute",i.e impossible to change by setting \${shlibpath_var} if the -# library is relocated. -hardcode_direct_absolute=$hardcode_direct_absolute_CXX - -# Set to "yes" if using the -LDIR flag during linking hardcodes DIR -# into the resulting binary. -hardcode_minus_L=$hardcode_minus_L_CXX - -# Set to "yes" if using SHLIBPATH_VAR=DIR during linking hardcodes DIR -# into the resulting binary. -hardcode_shlibpath_var=$hardcode_shlibpath_var_CXX - -# Set to "yes" if building a shared library automatically hardcodes DIR -# into the library and all subsequent libraries and executables linked -# against it. -hardcode_automatic=$hardcode_automatic_CXX - -# Set to yes if linker adds runtime paths of dependent libraries -# to runtime path list. -inherit_rpath=$inherit_rpath_CXX - -# Whether libtool must link a program against all its dependency libraries. -link_all_deplibs=$link_all_deplibs_CXX - -# Set to "yes" if exported symbols are required. -always_export_symbols=$always_export_symbols_CXX - -# The commands to list exported symbols. -export_symbols_cmds=$lt_export_symbols_cmds_CXX - -# Symbols that should not be listed in the preloaded symbols. -exclude_expsyms=$lt_exclude_expsyms_CXX - -# Symbols that must always be exported. -include_expsyms=$lt_include_expsyms_CXX - -# Commands necessary for linking programs (against libraries) with templates. -prelink_cmds=$lt_prelink_cmds_CXX - -# Commands necessary for finishing linking programs. -postlink_cmds=$lt_postlink_cmds_CXX - -# Specify filename containing input files. -file_list_spec=$lt_file_list_spec_CXX - -# How to hardcode a shared library path into an executable. -hardcode_action=$hardcode_action_CXX - -# The directories searched by this compiler when creating a shared library. -compiler_lib_search_dirs=$lt_compiler_lib_search_dirs_CXX - -# Dependencies to place before and after the objects being linked to -# create a shared library. -predep_objects=$lt_predep_objects_CXX -postdep_objects=$lt_postdep_objects_CXX -predeps=$lt_predeps_CXX -postdeps=$lt_postdeps_CXX - -# The library search path used internally by the compiler when linking -# a shared library. -compiler_lib_search_path=$lt_compiler_lib_search_path_CXX - -# ### END LIBTOOL TAG CONFIG: CXX -_LT_EOF - - ;; - "depfiles":C) test x"$AMDEP_TRUE" != x"" || { - # Older Autoconf quotes --file arguments for eval, but not when files - # are listed without --file. Let's play safe and only enable the eval - # if we detect the quoting. - case $CONFIG_FILES in - *\'*) eval set x "$CONFIG_FILES" ;; - *) set x $CONFIG_FILES ;; - esac - shift - for mf - do - # Strip MF so we end up with the name of the file. - mf=`echo "$mf" | sed -e 's/:.*$//'` - # Check whether this is an Automake generated Makefile or not. - # We used to match only the files named 'Makefile.in', but - # some people rename them; so instead we look at the file content. - # Grep'ing the first line is not enough: some people post-process - # each Makefile.in and add a new line on top of each file to say so. - # Grep'ing the whole file is not good either: AIX grep has a line - # limit of 2048, but all sed's we know have understand at least 4000. - if sed -n 's,^#.*generated by automake.*,X,p' "$mf" | grep X >/dev/null 2>&1; then - dirpart=`$as_dirname -- "$mf" || -$as_expr X"$mf" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ - X"$mf" : 'X\(//\)[^/]' \| \ - X"$mf" : 'X\(//\)$' \| \ - X"$mf" : 'X\(/\)' \| . 2>/dev/null || -$as_echo X"$mf" | - sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ - s//\1/ - q - } - /^X\(\/\/\)[^/].*/{ - s//\1/ - q - } - /^X\(\/\/\)$/{ - s//\1/ - q - } - /^X\(\/\).*/{ - s//\1/ - q - } - s/.*/./; q'` - else - continue - fi - # Extract the definition of DEPDIR, am__include, and am__quote - # from the Makefile without running 'make'. - DEPDIR=`sed -n 's/^DEPDIR = //p' < "$mf"` - test -z "$DEPDIR" && continue - am__include=`sed -n 's/^am__include = //p' < "$mf"` - test -z "$am__include" && continue - am__quote=`sed -n 's/^am__quote = //p' < "$mf"` - # Find all dependency output files, they are included files with - # $(DEPDIR) in their names. We invoke sed twice because it is the - # simplest approach to changing $(DEPDIR) to its actual value in the - # expansion. - for file in `sed -n " - s/^$am__include $am__quote\(.*(DEPDIR).*\)$am__quote"'$/\1/p' <"$mf" | \ - sed -e 's/\$(DEPDIR)/'"$DEPDIR"'/g'`; do - # Make sure the directory exists. - test -f "$dirpart/$file" && continue - fdir=`$as_dirname -- "$file" || -$as_expr X"$file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ - X"$file" : 'X\(//\)[^/]' \| \ - X"$file" : 'X\(//\)$' \| \ - X"$file" : 'X\(/\)' \| . 2>/dev/null || -$as_echo X"$file" | - sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ - s//\1/ - q - } - /^X\(\/\/\)[^/].*/{ - s//\1/ - q - } - /^X\(\/\/\)$/{ - s//\1/ - q - } - /^X\(\/\).*/{ - s//\1/ - q - } - s/.*/./; q'` - as_dir=$dirpart/$fdir; as_fn_mkdir_p - # echo "creating $dirpart/$file" - echo '# dummy' > "$dirpart/$file" - done - done -} - ;; - - esac -done # for ac_tag - - -as_fn_exit 0 -_ACEOF -ac_clean_files=$ac_clean_files_save - -test $ac_write_fail = 0 || - as_fn_error $? "write failure creating $CONFIG_STATUS" "$LINENO" 5 - - -# configure is writing to config.log, and then calls config.status. -# config.status does its own redirection, appending to config.log. -# Unfortunately, on DOS this fails, as config.log is still kept open -# by configure, so config.status won't be able to write to it; its -# output is simply discarded. So we exec the FD to /dev/null, -# effectively closing config.log, so it can be properly (re)opened and -# appended to by config.status. When coming back to configure, we -# need to make the FD available again. -if test "$no_create" != yes; then - ac_cs_success=: - ac_config_status_args= - test "$silent" = yes && - ac_config_status_args="$ac_config_status_args --quiet" - exec 5>/dev/null - $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false - exec 5>>config.log - # Use ||, not &&, to avoid exiting from the if with $? = 1, which - # would make configure fail if this is the last instruction. - $ac_cs_success || as_fn_exit 1 -fi -if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then - { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: unrecognized options: $ac_unrecognized_opts" >&5 -$as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;} -fi - - -{ $as_echo "$as_me:${as_lineno-$LINENO}: summary of build options: - - Version: ${VERSION} shared $LT_CURRENT:$LT_REVISION:$LT_AGE - Host type: ${host} - Install prefix: ${prefix} - C compiler: ${CC} - CFLAGS: ${CFLAGS} - WARNCFLAGS: ${WARNCFLAGS} - LDFLAGS: ${LDFLAGS} - LIBS: ${LIBS} - CPPFLAGS: ${CPPFLAGS} - C preprocessor: ${CPP} - C++ compiler: ${CXX} - CXXFLAGS: ${CXXFLAGS} - CXXCPP: ${CXXCPP} - Library types: Shared=${enable_shared}, Static=${enable_static} - Python: - Python: ${PYTHON} - PYTHON_VERSION: ${PYTHON_VERSION} - pyexecdir: ${pyexecdir} - Python-dev: ${have_python_dev} - PYTHON_CPPFLAGS:${PYTHON_CPPFLAGS} - PYTHON_LDFLAGS: ${PYTHON_LDFLAGS} - Cython: ${CYTHON} - Test: - CUnit: ${have_cunit} - Failmalloc: ${enable_failmalloc} - Libs: - OpenSSL: ${have_openssl} - Libxml2: ${have_libxml2} - Libev: ${have_libev} - Libevent(SSL): ${have_libevent_openssl} - Spdylay: ${have_spdylay} - Jansson: ${have_jansson} - Jemalloc: ${have_jemalloc} - Boost CPPFLAGS: ${BOOST_CPPFLAGS} - Boost LDFLAGS: ${BOOST_LDFLAGS} - Boost::ASIO: ${BOOST_ASIO_LIB} - Boost::System: ${BOOST_SYSTEM_LIB} - Boost::Thread: ${BOOST_THREAD_LIB} - Features: - Applications: ${enable_app} - HPACK tools: ${enable_hpack_tools} - Libnghttp2_asio:${enable_asio_lib} - Examples: ${enable_examples} - Python bindings:${enable_python_bindings} - Threading: ${enable_threads} -" >&5 -$as_echo "$as_me: summary of build options: - - Version: ${VERSION} shared $LT_CURRENT:$LT_REVISION:$LT_AGE - Host type: ${host} - Install prefix: ${prefix} - C compiler: ${CC} - CFLAGS: ${CFLAGS} - WARNCFLAGS: ${WARNCFLAGS} - LDFLAGS: ${LDFLAGS} - LIBS: ${LIBS} - CPPFLAGS: ${CPPFLAGS} - C preprocessor: ${CPP} - C++ compiler: ${CXX} - CXXFLAGS: ${CXXFLAGS} - CXXCPP: ${CXXCPP} - Library types: Shared=${enable_shared}, Static=${enable_static} - Python: - Python: ${PYTHON} - PYTHON_VERSION: ${PYTHON_VERSION} - pyexecdir: ${pyexecdir} - Python-dev: ${have_python_dev} - PYTHON_CPPFLAGS:${PYTHON_CPPFLAGS} - PYTHON_LDFLAGS: ${PYTHON_LDFLAGS} - Cython: ${CYTHON} - Test: - CUnit: ${have_cunit} - Failmalloc: ${enable_failmalloc} - Libs: - OpenSSL: ${have_openssl} - Libxml2: ${have_libxml2} - Libev: ${have_libev} - Libevent(SSL): ${have_libevent_openssl} - Spdylay: ${have_spdylay} - Jansson: ${have_jansson} - Jemalloc: ${have_jemalloc} - Boost CPPFLAGS: ${BOOST_CPPFLAGS} - Boost LDFLAGS: ${BOOST_LDFLAGS} - Boost::ASIO: ${BOOST_ASIO_LIB} - Boost::System: ${BOOST_SYSTEM_LIB} - Boost::Thread: ${BOOST_THREAD_LIB} - Features: - Applications: ${enable_app} - HPACK tools: ${enable_hpack_tools} - Libnghttp2_asio:${enable_asio_lib} - Examples: ${enable_examples} - Python bindings:${enable_python_bindings} - Threading: ${enable_threads} -" >&6;} diff --git a/configure.ac b/configure.ac deleted file mode 100644 index 94d099d..0000000 --- a/configure.ac +++ /dev/null @@ -1,758 +0,0 @@ -dnl nghttp2 - HTTP/2 C Library - -dnl Copyright (c) 2012, 2013, 2014, 2015 Tatsuhiro Tsujikawa - -dnl Permission is hereby granted, free of charge, to any person obtaining -dnl a copy of this software and associated documentation files (the -dnl "Software"), to deal in the Software without restriction, including -dnl without limitation the rights to use, copy, modify, merge, publish, -dnl distribute, sublicense, and/or sell copies of the Software, and to -dnl permit persons to whom the Software is furnished to do so, subject to -dnl the following conditions: - -dnl The above copyright notice and this permission notice shall be -dnl included in all copies or substantial portions of the Software. - -dnl THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -dnl EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -dnl MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -dnl NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -dnl LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -dnl OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -dnl WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -dnl Do not change user variables! -dnl http://www.gnu.org/software/automake/manual/html_node/Flag-Variables-Ordering.html - -AC_PREREQ(2.61) -AC_INIT([nghttp2], [1.0.0], [t-tujikawa@users.sourceforge.net]) -AC_USE_SYSTEM_EXTENSIONS - -LT_PREREQ([2.2.6]) -LT_INIT() - -AC_CANONICAL_BUILD -AC_CANONICAL_HOST -AC_CANONICAL_TARGET - -AM_INIT_AUTOMAKE([subdir-objects]) -# comment out for now since this requires automake 1.13 or higher and -# travis has older one. -# AM_EXTRA_RECURSIVE_TARGETS([it]) - -m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) - -AC_CONFIG_MACRO_DIR([m4]) -AC_CONFIG_HEADERS([config.h]) - -dnl See versioning rule: -dnl http://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html -AC_SUBST(LT_CURRENT, 14) -AC_SUBST(LT_REVISION, 0) -AC_SUBST(LT_AGE, 0) - -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) - -dnl Checks for command-line options -AC_ARG_ENABLE([werror], - [AS_HELP_STRING([--enable-werror], - [Turn on compile time warnings])], - [werror=$enableval], [werror=no]) - -AC_ARG_ENABLE([debug], - [AS_HELP_STRING([--enable-debug], - [Turn on debug output])], - [debug=$enableval], [debug=no]) - -AC_ARG_ENABLE([threads], - [AS_HELP_STRING([--disable-threads], - [Turn off threading in apps])], - [threads=$enableval], [threads=yes]) - -AC_ARG_ENABLE([app], - [AS_HELP_STRING([--enable-app], - [Build applications (nghttp, nghttpd and nghttpx) [default=check]])], - [request_app=$enableval], [request_app=check]) - -AC_ARG_ENABLE([hpack-tools], - [AS_HELP_STRING([--enable-hpack-tools], - [Build HPACK tools [default=check]])], - [request_hpack_tools=$enableval], [request_hpack_tools=check]) - -AC_ARG_ENABLE([asio-lib], - [AS_HELP_STRING([--enable-asio-lib], - [Build C++ libnghttp2_asio library [default=no]])], - [request_asio_lib=$enableval], [request_asio_lib=no]) - -AC_ARG_ENABLE([examples], - [AS_HELP_STRING([--enable-examples], - [Build examples [default=check]])], - [request_examples=$enableval], [request_examples=check]) - -AC_ARG_ENABLE([python-bindings], - [AS_HELP_STRING([--enable-python-bindings], - [Build Python bindings [default=check]])], - [request_python_bindings=$enableval], [request_python_bindings=check]) - -AC_ARG_ENABLE([failmalloc], - [AS_HELP_STRING([--disable-failmalloc], - [Do not build failmalloc test program])], - [request_failmalloc=$enableval], [request_failmalloc=yes]) - -AC_ARG_WITH([libxml2], - [AS_HELP_STRING([--with-libxml2], - [Use libxml2 [default=check]])], - [request_libxml2=$withval], [request_libxml2=check]) - -AC_ARG_WITH([jemalloc], - [AS_HELP_STRING([--with-jemalloc], - [Use jemalloc [default=check]])], - [request_jemalloc=$withval], [request_jemalloc=check]) - -AC_ARG_WITH([spdylay], - [AS_HELP_STRING([--with-spdylay], - [Use spdylay [default=check]])], - [request_spdylay=$withval], [request_spdylay=check]) - -AC_ARG_WITH([cython], - [AS_HELP_STRING([--with-cython=PATH], - [Use cython in given PATH])], - [cython_path=$withval], []) - -dnl Define variables -AC_ARG_VAR([CYTHON], [the Cython executable]) - -dnl Checks for programs -AC_PROG_CC -AC_PROG_CXX -AC_PROG_CPP -AC_PROG_INSTALL -AC_PROG_LN_S -AC_PROG_MAKE_SET -AC_PROG_MKDIR_P - -PKG_PROG_PKG_CONFIG([0.20]) - -if [test "x$request_python_bindings" != "xno"]; then - AM_PATH_PYTHON([2.7],, [:]) - AX_PYTHON_DEVEL([>= '2.7']) -fi - -if test "x${cython_path}" = "x"; then - AC_CHECK_PROGS([CYTHON], [cython.py cython]) -else - CYTHON=${cython_path} - AC_SUBST([CYTHON]) -fi - -# -# If we're running GCC or clang define _U_ to be "__attribute__((unused))" -# so we can use _U_ to flag unused function parameters and not get warnings -# about them. Otherwise, define _U_ to be an empty string so that _U_ used -# to flag an unused function parameters will compile with other compilers. -# -# XXX - similar hints for other compilers? -# -if test "x$GCC" = "xyes" -o "x$CC" = "xclang" ; then - AC_DEFINE([_U_], [__attribute__((unused))], [Hint to the compiler that a function parameters is not used]) -else - AC_DEFINE([_U_], , [Hint to the compiler that a function parameters is not used]) -fi - -AX_CXX_COMPILE_STDCXX_11([noext], [optional]) - -AC_LANG_PUSH(C++) - -# Check that std::chrono::steady_clock is available. In particular, -# gcc 4.6 does not have one, but has monotonic_clock which is the old -# name existed in the pre-standard draft. If steady_clock is not -# available, don't define HAVE_STEADY_CLOCK and replace steady_clock -# with monotonic_clock. -AC_MSG_CHECKING([whether std::chrono::steady_clock is available]) -AC_COMPILE_IFELSE([AC_LANG_PROGRAM( -[[ -#include -]], -[[ -auto tp = std::chrono::steady_clock::now(); -]])], - [AC_DEFINE([HAVE_STEADY_CLOCK], [1], - [Define to 1 if you have the `std::chrono::steady_clock`.]) - AC_MSG_RESULT([yes])], - [AC_MSG_RESULT([no])]) - -# Check that std::future is available. -AC_MSG_CHECKING([whether std::future is available]) -AC_COMPILE_IFELSE([AC_LANG_PROGRAM( -[[ -#include -#include -]], -[[ -std::vector> v; -]])], - [AC_DEFINE([HAVE_STD_FUTURE], [1], - [Define to 1 if you have the `std::future`.]) - have_std_future=yes - AC_MSG_RESULT([yes])], - [have_std_future=no - AC_MSG_RESULT([no])]) - -# Check that std::map::emplace is available for g++-4.7. -AC_MSG_CHECKING([whether std::map::emplace is available]) -AC_COMPILE_IFELSE([AC_LANG_PROGRAM( -[[ -#include -]], -[[ -std::map().emplace(1, 2); -]])], - [AC_DEFINE([HAVE_STD_MAP_EMPLACE], [1], - [Define to 1 if you have the `std::map::emplace`.]) - have_std_map_emplace=yes - AC_MSG_RESULT([yes])], - [have_std_map_emplace=no - AC_MSG_RESULT([no])]) - -AC_LANG_POP() - -# Checks for libraries. - -# Additional libraries required for tests. -TESTLDADD= - -# Additional libraries required for programs under src directory. -APPLDFLAGS= - -LIBS_OLD=$LIBS -# Search for dlsym function, which is used in tests. Linux needs -ldl, -# but netbsd does not need it. -AC_SEARCH_LIBS([dlsym], [dl]) -TESTLDADD="$LIBS $TESTLDADD" -LIBS=$LIBS_OLD - -LIBS_OLD=$LIBS -AC_SEARCH_LIBS([clock_gettime], [rt], - [AC_DEFINE([HAVE_CLOCK_GETTIME], [1], - [Define to 1 if you have the `clock_gettime`.])]) -APPLDFLAGS="$LIBS $APPLDFLAGS" -LIBS=$LIBS_OLD - -case "$host" in - *android*) - android_build=yes - # android does not need -pthread, but needs followng 3 libs for C++ - APPLDFLAGS="$APPLDFLAGS -lstdc++ -latomic -lsupc++" - ;; - *) - PTHREAD_LDFLAGS="-pthread" - APPLDFLAGS="$APPLDFLAGS $PTHREAD_LDFLAGS" - ;; -esac - -# zlib -if test "x$android_build" = "xyes"; then - # Use zlib provided by NDK - APPLDFLAGS="-lz $APPLDFLAGS" - have_zlib=yes -else - PKG_CHECK_MODULES([ZLIB], [zlib >= 1.2.3], [have_zlib=yes], [have_zlib=no]) - - if test "x${have_zlib}" = "xno"; then - AC_MSG_NOTICE($ZLIB_PKG_ERRORS) - fi -fi - -# cunit -PKG_CHECK_MODULES([CUNIT], [cunit >= 2.1], [have_cunit=yes], [have_cunit=no]) -# If pkg-config does not find cunit, check it using AC_CHECK_LIB. We -# do this because Debian (Ubuntu) lacks pkg-config file for cunit. -if test "x${have_cunit}" = "xno"; then - AC_MSG_WARN([${CUNIT_PKG_ERRORS}]) - AC_CHECK_LIB([cunit], [CU_initialize_registry], - [have_cunit=yes], [have_cunit=no]) - if test "x${have_cunit}" = "xyes"; then - CUNIT_LIBS="-lcunit" - CUNIT_CFLAGS="" - AC_SUBST([CUNIT_LIBS]) - AC_SUBST([CUNIT_CFLAGS]) - fi -fi -if test "x${have_cunit}" = "xyes"; then - # cunit in Mac OS X requires ncurses. Note that in Mac OS X, test - # program can be built without -lncurses, but it emits runtime - # error. - case "${build}" in - *-apple-darwin*) - CUNIT_LIBS="$CUNIT_LIBS -lncurses" - AC_SUBST([CUNIT_LIBS]) - ;; - esac -fi - -AM_CONDITIONAL([HAVE_CUNIT], [ test "x${have_cunit}" = "xyes" ]) - -# libev (for src) -# libev does not have pkg-config file. Check it in an old way. -LIBS_OLD=$LIBS -# android requires -lm for floor -AC_CHECK_LIB([ev], [ev_time], [have_libev=yes], [have_libev=no], [-lm]) -if test "x${have_libev}" = "xyes"; then - AC_CHECK_HEADER([ev.h], [have_libev=yes], [have_libev=no]) - if test "x${have_libev}" = "xyes"; then - LIBEV_LIBS=-lev - LIBEV_CFLAGS= - AC_SUBST([LIBEV_LIBS]) - AC_SUBST([LIBEV_CFLAGS]) - fi -fi -LIBS=$LIBS_OLD - -# openssl (for src) -PKG_CHECK_MODULES([OPENSSL], [openssl >= 1.0.1], - [have_openssl=yes], [have_openssl=no]) -if test "x${have_openssl}" = "xno"; then - AC_MSG_NOTICE($OPENSSL_PKG_ERRORS) -fi - -# libevent_openssl (for examples) -# 2.0.8 is required because we use evconnlistener_set_error_cb() -PKG_CHECK_MODULES([LIBEVENT_OPENSSL], [libevent_openssl >= 2.0.8], - [have_libevent_openssl=yes], [have_libevent_openssl=no]) -if test "x${have_libevent_openssl}" = "xno"; then - AC_MSG_NOTICE($LIBEVENT_OPENSSL_PKG_ERRORS) -fi - -# jansson (for src/nghttp, src/deflatehd and src/inflatehd) -PKG_CHECK_MODULES([JANSSON], [jansson >= 2.5], - [have_jansson=yes], [have_jansson=no]) -if test "x${have_jansson}" == "xyes"; then - AC_DEFINE([HAVE_JANSSON], [1], - [Define to 1 if you have `libjansson` library.]) -else - AC_MSG_NOTICE($JANSSON_PKG_ERRORS) -fi - -# libxml2 (for src/nghttp) -have_libxml2=no -if test "x${request_libxml2}" != "xno"; then - AM_PATH_XML2(2.7.7, [have_libxml2=yes], [have_libxml2=no]) - if test "x${have_libxml2}" = "xyes"; then - AC_DEFINE([HAVE_LIBXML2], [1], [Define to 1 if you have `libxml2` library.]) - fi -fi - -if test "x${request_libxml2}" = "xyes" && - test "x${have_libxml2}" != "xyes"; then - AC_MSG_ERROR([libxml2 was requested (--with-libxml2) but not found]) -fi - -AM_CONDITIONAL([HAVE_LIBXML2], [ test "x${have_libxml2}" = "xyes" ]) - -# jemalloc -have_jemalloc=no -if test "x${request_jemalloc}" != "xno"; then - LIBS_OLD=$LIBS - AC_SEARCH_LIBS([malloc_stats_print], [jemalloc], [have_jemalloc=yes], [], - [$PTHREAD_LDFLAGS]) - LIBS=$LIBS_OLD - - if test "x${have_jemalloc}" = "xyes"; then - jemalloc_libs=${ac_cv_search_malloc_stats_print} - else - # On Darwin, malloc_stats_print is je_malloc_stats_print - AC_SEARCH_LIBS([je_malloc_stats_print], [jemalloc], [have_jemalloc=yes], [], - [$PTHREAD_LDFLAGS]) - LIBS=$LIBS_OLD - - if test "x${have_jemalloc}" = "xyes"; then - jemalloc_libs=${ac_cv_search_je_malloc_stats_print} - fi - fi - - if test "x${have_jemalloc}" = "xyes" && - test "x${jemalloc_libs}" != "xnone required"; then - JEMALLOC_LIBS=${jemalloc_libs} - AC_SUBST([JEMALLOC_LIBS]) - fi -fi - -if test "x${request_jemalloc}" = "xyes" && - test "x${have_jemalloc}" != "xyes"; then - AC_MSG_ERROR([jemalloc was requested (--with-jemalloc) but not found]) -fi - -# spdylay (for src/nghttpx and src/h2load) -have_spdylay=no -if test "x${request_spdylay}" != "xno"; then - PKG_CHECK_MODULES([LIBSPDYLAY], [libspdylay >= 1.3.2], - [have_spdylay=yes], [have_spdylay=no]) - if test "x${have_spdylay}" = "xyes"; then - AC_DEFINE([HAVE_SPDYLAY], [1], [Define to 1 if you have `spdylay` library.]) - else - AC_MSG_NOTICE($LIBSPDYLAY_PKG_ERRORS) - AC_MSG_NOTICE([The SPDY support in nghttpx and h2load will be disabled.]) - fi -fi - -if test "x${request_spdylay}" = "xyes" && - test "x${have_spdylay}" != "xyes"; then - AC_MSG_ERROR([spdylay was requested (--with-spdylay) but not found]) -fi - -AM_CONDITIONAL([HAVE_SPDYLAY], [ test "x${have_spdylay}" = "xyes" ]) - -# Check Boost Asio library -have_asio_lib=no - -if test "x${request_asio_lib}" = "xyes"; then - AX_BOOST_BASE([1.54.0], [have_boost_base=yes], [have_boost_base=no]) - - if test "x${have_boost_base}" = "xyes"; then - AX_BOOST_ASIO() - AX_BOOST_SYSTEM() - AX_BOOST_THREAD() - - if test "x${ax_cv_boost_asio}" = "xyes" && - test "x${ax_cv_boost_system}" = "xyes" && - test "x${ax_cv_boost_thread}" = "xyes"; then - have_asio_lib=yes - fi - fi -fi - -# The nghttp, nghttpd and nghttpx under src depend on zlib, OpenSSL -# and libev -enable_app=no -if test "x${request_app}" != "xno" && - test "x${have_zlib}" = "xyes" && - test "x${have_openssl}" = "xyes" && - test "x${have_libev}" = "xyes"; then - enable_app=yes -fi - -if test "x${request_app}" = "xyes" && - test "x${enable_app}" != "xyes"; then - AC_MSG_ERROR([applications were requested (--enable-app) but dependencies are not met.]) -fi - -AM_CONDITIONAL([ENABLE_APP], [ test "x${enable_app}" = "xyes" ]) - -enable_hpack_tools=no -# HPACK tools requires jansson -if test "x${request_hpack_tools}" != "xno" && - test "x${have_jansson}" = "xyes"; then - enable_hpack_tools=yes -fi - -if test "x${request_hpack_tools}" = "xyes" && - test "x${enable_hpack_tools}" != "xyes"; then - AC_MSG_ERROR([HPACK tools were requested (--enable-hpack-tools) but dependencies are not met.]) -fi - -AM_CONDITIONAL([ENABLE_HPACK_TOOLS], [ test "x${enable_hpack_tools}" = "xyes" ]) - -# C++ library libnghttp2_asio - -enable_asio_lib=no -if test "x${request_asio_lib}" != "xno" && - test "x${have_asio_lib}" = "xyes"; then - enable_asio_lib=yes -fi - -AM_CONDITIONAL([ENABLE_ASIO_LIB], [ test "x${enable_asio_lib}" = "xyes" ]) - -# The example programs depend on OpenSSL and libevent_openssl -enable_examples=no -if test "x${request_examples}" != "xno" && - test "x${have_openssl}" = "xyes" && - test "x${have_libevent_openssl}" = "xyes"; then - enable_examples=yes -fi - -if test "x${request_examples}" = "xyes" && - test "x${enable_examples}" != "xyes"; then - AC_MSG_ERROR([examples were requested (--enable-examples) but dependencies are not met.]) -fi - -AM_CONDITIONAL([ENABLE_EXAMPLES], [ test "x${enable_examples}" = "xyes" ]) - -# Python bindings -enable_python_bindings=no -if test "x${request_python_bindings}" != "xno" && - test "x${CYTHON}" != "x" && - test "x${PYTHON}" != "x:" && - test "x${have_python_dev}" = "xyes"; then - enable_python_bindings=yes -fi - -if test "x${request_python_bindings}" = "xyes" && - test "x${enable_python_bindings}" != "xyes"; then - AC_MSG_ERROR([python bindings were requested (--enable-python-bindings) but dependencies are not met.]) -fi - -AM_CONDITIONAL([ENABLE_PYTHON_BINDINGS], - [test "x${enable_python_bindings}" = "xyes"]) - -# Produce cython conditional, so that we can distribute generated C -# source -AM_CONDITIONAL([HAVE_CYTHON], [test "x${CYTHON}" != "x"]) - -# failmalloc tests -enable_failmalloc=no -if test "x${request_failmalloc}" = "xyes"; then - enable_failmalloc=yes -fi - -AM_CONDITIONAL([ENABLE_FAILMALLOC], [ test "x${enable_failmalloc}" = "xyes" ]) - -# Checks for header files. -AC_HEADER_ASSERT -AC_CHECK_HEADERS([ \ - arpa/inet.h \ - fcntl.h \ - inttypes.h \ - limits.h \ - netdb.h \ - netinet/in.h \ - pwd.h \ - stddef.h \ - stdint.h \ - stdlib.h \ - string.h \ - sys/socket.h \ - sys/time.h \ - syslog.h \ - time.h \ - unistd.h \ -]) - -# Checks for typedefs, structures, and compiler characteristics. -AC_TYPE_SIZE_T -AC_TYPE_SSIZE_T -AC_TYPE_UINT8_T -AC_TYPE_UINT16_T -AC_TYPE_UINT32_T -AC_TYPE_UINT64_T -AC_TYPE_INT8_T -AC_TYPE_INT16_T -AC_TYPE_INT32_T -AC_TYPE_INT64_T -AC_TYPE_OFF_T -AC_TYPE_PID_T -AC_TYPE_UID_T -AC_CHECK_TYPES([ptrdiff_t]) -AC_C_BIGENDIAN -AC_C_INLINE -AC_SYS_LARGEFILE - -AC_CHECK_MEMBER([struct tm.tm_gmtoff], [have_struct_tm_tm_gmtoff=yes], - [have_struct_tm_tm_gmtoff=no], [[#include ]]) - -if test "x$have_struct_tm_tm_gmtoff" = "xyes"; then - AC_DEFINE([HAVE_STRUCT_TM_TM_GMTOFF], [1], - [Define to 1 if you have `struct tm.tm_gmtoff` member.]) -fi - -# Check size of pointer to decide we need 8 bytes alignment -# adjustment. -AC_CHECK_SIZEOF([int *]) - -# Checks for library functions. -if test "x$cross_compiling" != "xyes"; then - AC_FUNC_MALLOC -fi - -AC_FUNC_CHOWN -AC_FUNC_ERROR_AT_LINE -AC_FUNC_FORK -# Don't check realloc, since LeakSanitizer detects memory leak during check -# AC_FUNC_REALLOC -AC_FUNC_STRERROR_R -AC_FUNC_STRNLEN - -AC_CHECK_FUNCS([ \ - _Exit \ - accept4 \ - dup2 \ - getcwd \ - getpwnam \ - localtime_r \ - memchr \ - memmove \ - memset \ - socket \ - sqrt \ - strchr \ - strdup \ - strerror \ - strndup \ - strstr \ - strtol \ - strtoul \ - timegm \ -]) - -# timerfd_create was added in linux kernel 2.6.25 - -AC_CHECK_FUNC([timerfd_create], - [have_timerfd_create=yes], [have_timerfd_create=no]) - - -# Checks for epoll availability, primarily for examples/tiny-nghttpd -AX_HAVE_EPOLL([have_epoll=yes], [have_epoll=no]) - -AM_CONDITIONAL([ENABLE_TINY_NGHTTPD], - [ test "x${have_epoll}" = "xyes" && - test "x${have_timerfd_create}" = "xyes"]) - -ac_save_CFLAGS=$CFLAGS -CFLAGS= - -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"]) - 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"]) - 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"]) - - # Only work with Clang for the moment - AX_CHECK_COMPILE_FLAG([-Wheader-guard], [CFLAGS="$CFLAGS -Wheader-guard"]) -fi - -WARNCFLAGS=$CFLAGS -CFLAGS=$ac_save_CFLAGS - -AC_SUBST([WARNCFLAGS]) - -if test "x$debug" != "xno"; then - AC_DEFINE([DEBUGBUILD], [1], [Define to 1 to enable debug output.]) -fi - -enable_threads=yes -# Some platform does not have working std::future. We disable -# threading for those platforms. -if test "x$threads" != "xyes" || - test "x$have_std_future" != "xyes"; then - enable_threads=no - AC_DEFINE([NOTHREADS], [1], [Define to 1 if you want to disable threads.]) -fi - -AC_SUBST([TESTLDADD]) -AC_SUBST([APPLDFLAGS]) - -AC_CONFIG_FILES([ - Makefile - lib/Makefile - lib/libnghttp2.pc - lib/includes/Makefile - lib/includes/nghttp2/nghttp2ver.h - tests/Makefile - tests/testdata/Makefile - third-party/Makefile - src/Makefile - src/includes/Makefile - src/libnghttp2_asio.pc - examples/Makefile - python/Makefile - python/setup.py - integration-tests/Makefile - integration-tests/config.go - integration-tests/setenv - doc/Makefile - doc/conf.py - doc/index.rst - doc/package_README.rst - doc/tutorial-client.rst - doc/tutorial-server.rst - doc/tutorial-hpack.rst - doc/nghttpx-howto.rst - doc/h2load-howto.rst - doc/libnghttp2_asio.rst - doc/python-apiref.rst - doc/building-android-binary.rst - doc/nghttp2.h.rst - doc/nghttp2ver.h.rst - doc/asio_http2.h.rst - doc/asio_http2_server.h.rst - doc/asio_http2_client.h.rst - doc/contribute.rst - contrib/Makefile -]) -AC_OUTPUT - -AC_MSG_NOTICE([summary of build options: - - Version: ${VERSION} shared $LT_CURRENT:$LT_REVISION:$LT_AGE - Host type: ${host} - Install prefix: ${prefix} - C compiler: ${CC} - CFLAGS: ${CFLAGS} - WARNCFLAGS: ${WARNCFLAGS} - LDFLAGS: ${LDFLAGS} - LIBS: ${LIBS} - CPPFLAGS: ${CPPFLAGS} - C preprocessor: ${CPP} - C++ compiler: ${CXX} - CXXFLAGS: ${CXXFLAGS} - CXXCPP: ${CXXCPP} - Library types: Shared=${enable_shared}, Static=${enable_static} - Python: - Python: ${PYTHON} - PYTHON_VERSION: ${PYTHON_VERSION} - pyexecdir: ${pyexecdir} - Python-dev: ${have_python_dev} - PYTHON_CPPFLAGS:${PYTHON_CPPFLAGS} - PYTHON_LDFLAGS: ${PYTHON_LDFLAGS} - Cython: ${CYTHON} - Test: - CUnit: ${have_cunit} - Failmalloc: ${enable_failmalloc} - Libs: - OpenSSL: ${have_openssl} - Libxml2: ${have_libxml2} - Libev: ${have_libev} - Libevent(SSL): ${have_libevent_openssl} - Spdylay: ${have_spdylay} - Jansson: ${have_jansson} - Jemalloc: ${have_jemalloc} - Boost CPPFLAGS: ${BOOST_CPPFLAGS} - Boost LDFLAGS: ${BOOST_LDFLAGS} - Boost::ASIO: ${BOOST_ASIO_LIB} - Boost::System: ${BOOST_SYSTEM_LIB} - Boost::Thread: ${BOOST_THREAD_LIB} - Features: - Applications: ${enable_app} - HPACK tools: ${enable_hpack_tools} - Libnghttp2_asio:${enable_asio_lib} - Examples: ${enable_examples} - Python bindings:${enable_python_bindings} - Threading: ${enable_threads} -]) diff --git a/contrib/Makefile.am b/contrib/Makefile.am deleted file mode 100644 index 07a3bf8..0000000 --- a/contrib/Makefile.am +++ /dev/null @@ -1,44 +0,0 @@ -# nghttp2 - HTTP/2 C Library - -# Copyright (c) 2014 Tatsuhiro Tsujikawa - -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: - -# The above copyright notice and this permission notice shall be -# included in all copies or substantial portions of the Software. - -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -configfiles = nghttpx-init nghttpx.service nghttpx-upstart.conf - -EXTRA_DIST = $(configfiles:%=%.in) nghttpx-logrotate - -edit = sed -e 's|@bindir[@]|$(bindir)|g' - -nghttpx-init: %: $(srcdir)/%.in - rm -f $@ $@.tmp - $(edit) $< > $@.tmp - chmod +x $@.tmp - mv $@.tmp $@ - -nghttpx.service nghttpx-upstart.conf: %: $(srcdir)/%.in - $(edit) $< > $@ - -$(configfiles): Makefile - -all-local: $(configfiles) - -clean-local: - -rm -f nghttpx-init.tmp $(configfiles) diff --git a/contrib/Makefile.in b/contrib/Makefile.in deleted file mode 100644 index 357b6e3..0000000 --- a/contrib/Makefile.in +++ /dev/null @@ -1,531 +0,0 @@ -# Makefile.in generated by automake 1.14.1 from Makefile.am. -# @configure_input@ - -# Copyright (C) 1994-2013 Free Software Foundation, Inc. - -# This Makefile.in is free software; the Free Software Foundation -# gives unlimited permission to copy and/or distribute it, -# with or without modifications, as long as this notice is preserved. - -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY, to the extent permitted by law; without -# even the implied warranty of MERCHANTABILITY or FITNESS FOR A -# PARTICULAR PURPOSE. - -@SET_MAKE@ - -# nghttp2 - HTTP/2 C Library - -# Copyright (c) 2014 Tatsuhiro Tsujikawa - -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: - -# The above copyright notice and this permission notice shall be -# included in all copies or substantial portions of the Software. - -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -VPATH = @srcdir@ -am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)' -am__make_running_with_option = \ - case $${target_option-} in \ - ?) ;; \ - *) echo "am__make_running_with_option: internal error: invalid" \ - "target option '$${target_option-}' specified" >&2; \ - exit 1;; \ - esac; \ - has_opt=no; \ - sane_makeflags=$$MAKEFLAGS; \ - if $(am__is_gnu_make); then \ - sane_makeflags=$$MFLAGS; \ - else \ - case $$MAKEFLAGS in \ - *\\[\ \ ]*) \ - bs=\\; \ - sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ - | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ - esac; \ - fi; \ - skip_next=no; \ - strip_trailopt () \ - { \ - flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ - }; \ - for flg in $$sane_makeflags; do \ - test $$skip_next = yes && { skip_next=no; continue; }; \ - case $$flg in \ - *=*|--*) continue;; \ - -*I) strip_trailopt 'I'; skip_next=yes;; \ - -*I?*) strip_trailopt 'I';; \ - -*O) strip_trailopt 'O'; skip_next=yes;; \ - -*O?*) strip_trailopt 'O';; \ - -*l) strip_trailopt 'l'; skip_next=yes;; \ - -*l?*) strip_trailopt 'l';; \ - -[dEDm]) skip_next=yes;; \ - -[JT]) skip_next=yes;; \ - esac; \ - case $$flg in \ - *$$target_option*) has_opt=yes; break;; \ - esac; \ - done; \ - test $$has_opt = yes -am__make_dryrun = (target_option=n; $(am__make_running_with_option)) -am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) -pkgdatadir = $(datadir)/@PACKAGE@ -pkgincludedir = $(includedir)/@PACKAGE@ -pkglibdir = $(libdir)/@PACKAGE@ -pkglibexecdir = $(libexecdir)/@PACKAGE@ -am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd -install_sh_DATA = $(install_sh) -c -m 644 -install_sh_PROGRAM = $(install_sh) -c -install_sh_SCRIPT = $(install_sh) -c -INSTALL_HEADER = $(INSTALL_DATA) -transform = $(program_transform_name) -NORMAL_INSTALL = : -PRE_INSTALL = : -POST_INSTALL = : -NORMAL_UNINSTALL = : -PRE_UNINSTALL = : -POST_UNINSTALL = : -build_triplet = @build@ -host_triplet = @host@ -target_triplet = @target@ -subdir = contrib -DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am -ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 -am__aclocal_m4_deps = $(top_srcdir)/m4/ax_boost_asio.m4 \ - $(top_srcdir)/m4/ax_boost_base.m4 \ - $(top_srcdir)/m4/ax_boost_system.m4 \ - $(top_srcdir)/m4/ax_boost_thread.m4 \ - $(top_srcdir)/m4/ax_check_compile_flag.m4 \ - $(top_srcdir)/m4/ax_cxx_compile_stdcxx_11.m4 \ - $(top_srcdir)/m4/ax_have_epoll.m4 \ - $(top_srcdir)/m4/ax_python_devel.m4 \ - $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/libxml2.m4 \ - $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ - $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ - $(top_srcdir)/configure.ac -am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ - $(ACLOCAL_M4) -mkinstalldirs = $(install_sh) -d -CONFIG_HEADER = $(top_builddir)/config.h -CONFIG_CLEAN_FILES = -CONFIG_CLEAN_VPATH_FILES = -AM_V_P = $(am__v_P_@AM_V@) -am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) -am__v_P_0 = false -am__v_P_1 = : -AM_V_GEN = $(am__v_GEN_@AM_V@) -am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) -am__v_GEN_0 = @echo " GEN " $@; -am__v_GEN_1 = -AM_V_at = $(am__v_at_@AM_V@) -am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) -am__v_at_0 = @ -am__v_at_1 = -SOURCES = -DIST_SOURCES = -am__can_run_installinfo = \ - case $$AM_UPDATE_INFO_DIR in \ - n|no|NO) false;; \ - *) (install-info --version) >/dev/null 2>&1;; \ - esac -am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) -DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) -ACLOCAL = @ACLOCAL@ -AMTAR = @AMTAR@ -AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ -APPLDFLAGS = @APPLDFLAGS@ -AR = @AR@ -AUTOCONF = @AUTOCONF@ -AUTOHEADER = @AUTOHEADER@ -AUTOMAKE = @AUTOMAKE@ -AWK = @AWK@ -BOOST_ASIO_LIB = @BOOST_ASIO_LIB@ -BOOST_CPPFLAGS = @BOOST_CPPFLAGS@ -BOOST_LDFLAGS = @BOOST_LDFLAGS@ -BOOST_SYSTEM_LIB = @BOOST_SYSTEM_LIB@ -BOOST_THREAD_LIB = @BOOST_THREAD_LIB@ -CC = @CC@ -CCDEPMODE = @CCDEPMODE@ -CFLAGS = @CFLAGS@ -CPP = @CPP@ -CPPFLAGS = @CPPFLAGS@ -CUNIT_CFLAGS = @CUNIT_CFLAGS@ -CUNIT_LIBS = @CUNIT_LIBS@ -CXX = @CXX@ -CXXCPP = @CXXCPP@ -CXXDEPMODE = @CXXDEPMODE@ -CXXFLAGS = @CXXFLAGS@ -CYGPATH_W = @CYGPATH_W@ -CYTHON = @CYTHON@ -DEFS = @DEFS@ -DEPDIR = @DEPDIR@ -DLLTOOL = @DLLTOOL@ -DSYMUTIL = @DSYMUTIL@ -DUMPBIN = @DUMPBIN@ -ECHO_C = @ECHO_C@ -ECHO_N = @ECHO_N@ -ECHO_T = @ECHO_T@ -EGREP = @EGREP@ -EXEEXT = @EXEEXT@ -FGREP = @FGREP@ -GREP = @GREP@ -HAVE_CXX11 = @HAVE_CXX11@ -INSTALL = @INSTALL@ -INSTALL_DATA = @INSTALL_DATA@ -INSTALL_PROGRAM = @INSTALL_PROGRAM@ -INSTALL_SCRIPT = @INSTALL_SCRIPT@ -INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ -JANSSON_CFLAGS = @JANSSON_CFLAGS@ -JANSSON_LIBS = @JANSSON_LIBS@ -JEMALLOC_LIBS = @JEMALLOC_LIBS@ -LD = @LD@ -LDFLAGS = @LDFLAGS@ -LIBEVENT_OPENSSL_CFLAGS = @LIBEVENT_OPENSSL_CFLAGS@ -LIBEVENT_OPENSSL_LIBS = @LIBEVENT_OPENSSL_LIBS@ -LIBEV_CFLAGS = @LIBEV_CFLAGS@ -LIBEV_LIBS = @LIBEV_LIBS@ -LIBOBJS = @LIBOBJS@ -LIBS = @LIBS@ -LIBSPDYLAY_CFLAGS = @LIBSPDYLAY_CFLAGS@ -LIBSPDYLAY_LIBS = @LIBSPDYLAY_LIBS@ -LIBTOOL = @LIBTOOL@ -LIPO = @LIPO@ -LN_S = @LN_S@ -LTLIBOBJS = @LTLIBOBJS@ -LT_AGE = @LT_AGE@ -LT_CURRENT = @LT_CURRENT@ -LT_REVISION = @LT_REVISION@ -MAKEINFO = @MAKEINFO@ -MANIFEST_TOOL = @MANIFEST_TOOL@ -MKDIR_P = @MKDIR_P@ -NM = @NM@ -NMEDIT = @NMEDIT@ -OBJDUMP = @OBJDUMP@ -OBJEXT = @OBJEXT@ -OPENSSL_CFLAGS = @OPENSSL_CFLAGS@ -OPENSSL_LIBS = @OPENSSL_LIBS@ -OTOOL = @OTOOL@ -OTOOL64 = @OTOOL64@ -PACKAGE = @PACKAGE@ -PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ -PACKAGE_NAME = @PACKAGE_NAME@ -PACKAGE_STRING = @PACKAGE_STRING@ -PACKAGE_TARNAME = @PACKAGE_TARNAME@ -PACKAGE_URL = @PACKAGE_URL@ -PACKAGE_VERSION = @PACKAGE_VERSION@ -PACKAGE_VERSION_NUM = @PACKAGE_VERSION_NUM@ -PATH_SEPARATOR = @PATH_SEPARATOR@ -PKG_CONFIG = @PKG_CONFIG@ -PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ -PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ -PYTHON = @PYTHON@ -PYTHON_CPPFLAGS = @PYTHON_CPPFLAGS@ -PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@ -PYTHON_EXTRA_LDFLAGS = @PYTHON_EXTRA_LDFLAGS@ -PYTHON_EXTRA_LIBS = @PYTHON_EXTRA_LIBS@ -PYTHON_LDFLAGS = @PYTHON_LDFLAGS@ -PYTHON_PLATFORM = @PYTHON_PLATFORM@ -PYTHON_PREFIX = @PYTHON_PREFIX@ -PYTHON_SITE_PKG = @PYTHON_SITE_PKG@ -PYTHON_VERSION = @PYTHON_VERSION@ -RANLIB = @RANLIB@ -SED = @SED@ -SET_MAKE = @SET_MAKE@ -SHELL = @SHELL@ -STRIP = @STRIP@ -TESTLDADD = @TESTLDADD@ -VERSION = @VERSION@ -WARNCFLAGS = @WARNCFLAGS@ -XML2_CONFIG = @XML2_CONFIG@ -XML_CPPFLAGS = @XML_CPPFLAGS@ -XML_LIBS = @XML_LIBS@ -ZLIB_CFLAGS = @ZLIB_CFLAGS@ -ZLIB_LIBS = @ZLIB_LIBS@ -abs_builddir = @abs_builddir@ -abs_srcdir = @abs_srcdir@ -abs_top_builddir = @abs_top_builddir@ -abs_top_srcdir = @abs_top_srcdir@ -ac_ct_AR = @ac_ct_AR@ -ac_ct_CC = @ac_ct_CC@ -ac_ct_CXX = @ac_ct_CXX@ -ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ -am__include = @am__include@ -am__leading_dot = @am__leading_dot@ -am__quote = @am__quote@ -am__tar = @am__tar@ -am__untar = @am__untar@ -bindir = @bindir@ -build = @build@ -build_alias = @build_alias@ -build_cpu = @build_cpu@ -build_os = @build_os@ -build_vendor = @build_vendor@ -builddir = @builddir@ -datadir = @datadir@ -datarootdir = @datarootdir@ -docdir = @docdir@ -dvidir = @dvidir@ -exec_prefix = @exec_prefix@ -host = @host@ -host_alias = @host_alias@ -host_cpu = @host_cpu@ -host_os = @host_os@ -host_vendor = @host_vendor@ -htmldir = @htmldir@ -includedir = @includedir@ -infodir = @infodir@ -install_sh = @install_sh@ -libdir = @libdir@ -libexecdir = @libexecdir@ -localedir = @localedir@ -localstatedir = @localstatedir@ -mandir = @mandir@ -mkdir_p = @mkdir_p@ -oldincludedir = @oldincludedir@ -pdfdir = @pdfdir@ -pkgpyexecdir = @pkgpyexecdir@ -pkgpythondir = @pkgpythondir@ -prefix = @prefix@ -program_transform_name = @program_transform_name@ -psdir = @psdir@ -pyexecdir = @pyexecdir@ -pythondir = @pythondir@ -sbindir = @sbindir@ -sharedstatedir = @sharedstatedir@ -srcdir = @srcdir@ -sysconfdir = @sysconfdir@ -target = @target@ -target_alias = @target_alias@ -target_cpu = @target_cpu@ -target_os = @target_os@ -target_vendor = @target_vendor@ -top_build_prefix = @top_build_prefix@ -top_builddir = @top_builddir@ -top_srcdir = @top_srcdir@ -configfiles = nghttpx-init nghttpx.service nghttpx-upstart.conf -EXTRA_DIST = $(configfiles:%=%.in) nghttpx-logrotate -edit = sed -e 's|@bindir[@]|$(bindir)|g' -all: all-am - -.SUFFIXES: -$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) - @for dep in $?; do \ - case '$(am__configure_deps)' in \ - *$$dep*) \ - ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ - && { if test -f $@; then exit 0; else break; fi; }; \ - exit 1;; \ - esac; \ - done; \ - echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu contrib/Makefile'; \ - $(am__cd) $(top_srcdir) && \ - $(AUTOMAKE) --gnu contrib/Makefile -.PRECIOUS: Makefile -Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status - @case '$?' in \ - *config.status*) \ - cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ - *) \ - echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ - cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ - esac; - -$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) - cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh - -$(top_srcdir)/configure: $(am__configure_deps) - cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh -$(ACLOCAL_M4): $(am__aclocal_m4_deps) - cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh -$(am__aclocal_m4_deps): - -mostlyclean-libtool: - -rm -f *.lo - -clean-libtool: - -rm -rf .libs _libs -tags TAGS: - -ctags CTAGS: - -cscope cscopelist: - - -distdir: $(DISTFILES) - @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ - topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ - list='$(DISTFILES)'; \ - dist_files=`for file in $$list; do echo $$file; done | \ - sed -e "s|^$$srcdirstrip/||;t" \ - -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ - case $$dist_files in \ - */*) $(MKDIR_P) `echo "$$dist_files" | \ - sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ - sort -u` ;; \ - esac; \ - for file in $$dist_files; do \ - if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ - if test -d $$d/$$file; then \ - dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ - if test -d "$(distdir)/$$file"; then \ - find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ - fi; \ - if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ - cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ - find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ - fi; \ - cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ - else \ - test -f "$(distdir)/$$file" \ - || cp -p $$d/$$file "$(distdir)/$$file" \ - || exit 1; \ - fi; \ - done -check-am: all-am -check: check-am -all-am: Makefile all-local -installdirs: -install: install-am -install-exec: install-exec-am -install-data: install-data-am -uninstall: uninstall-am - -install-am: all-am - @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am - -installcheck: installcheck-am -install-strip: - if test -z '$(STRIP)'; then \ - $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ - install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ - install; \ - else \ - $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ - install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ - "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ - fi -mostlyclean-generic: - -clean-generic: - -distclean-generic: - -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) - -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) - -maintainer-clean-generic: - @echo "This command is intended for maintainers to use" - @echo "it deletes files that may require special tools to rebuild." -clean: clean-am - -clean-am: clean-generic clean-libtool clean-local mostlyclean-am - -distclean: distclean-am - -rm -f Makefile -distclean-am: clean-am distclean-generic - -dvi: dvi-am - -dvi-am: - -html: html-am - -html-am: - -info: info-am - -info-am: - -install-data-am: - -install-dvi: install-dvi-am - -install-dvi-am: - -install-exec-am: - -install-html: install-html-am - -install-html-am: - -install-info: install-info-am - -install-info-am: - -install-man: - -install-pdf: install-pdf-am - -install-pdf-am: - -install-ps: install-ps-am - -install-ps-am: - -installcheck-am: - -maintainer-clean: maintainer-clean-am - -rm -f Makefile -maintainer-clean-am: distclean-am maintainer-clean-generic - -mostlyclean: mostlyclean-am - -mostlyclean-am: mostlyclean-generic mostlyclean-libtool - -pdf: pdf-am - -pdf-am: - -ps: ps-am - -ps-am: - -uninstall-am: - -.MAKE: install-am install-strip - -.PHONY: all all-am all-local check check-am clean clean-generic \ - clean-libtool clean-local cscopelist-am ctags-am distclean \ - distclean-generic distclean-libtool distdir dvi dvi-am html \ - html-am info info-am install install-am install-data \ - install-data-am install-dvi install-dvi-am install-exec \ - install-exec-am install-html install-html-am install-info \ - install-info-am install-man install-pdf install-pdf-am \ - install-ps install-ps-am install-strip installcheck \ - installcheck-am installdirs maintainer-clean \ - maintainer-clean-generic mostlyclean mostlyclean-generic \ - mostlyclean-libtool pdf pdf-am ps ps-am tags-am uninstall \ - uninstall-am - - -nghttpx-init: %: $(srcdir)/%.in - rm -f $@ $@.tmp - $(edit) $< > $@.tmp - chmod +x $@.tmp - mv $@.tmp $@ - -nghttpx.service nghttpx-upstart.conf: %: $(srcdir)/%.in - $(edit) $< > $@ - -$(configfiles): Makefile - -all-local: $(configfiles) - -clean-local: - -rm -f nghttpx-init.tmp $(configfiles) - -# Tell versions [3.59,3.63) of GNU make to not export all variables. -# Otherwise a system limit (for SysV at least) may be exceeded. -.NOEXPORT: diff --git a/contrib/nghttpx-init.in b/contrib/nghttpx-init.in deleted file mode 100644 index a1620a9..0000000 --- a/contrib/nghttpx-init.in +++ /dev/null @@ -1,173 +0,0 @@ -#! /bin/sh -### BEGIN INIT INFO -# Provides: nghttpx -# Required-Start: $remote_fs $syslog -# Required-Stop: $remote_fs $syslog -# Default-Start: 2 3 4 5 -# Default-Stop: 0 1 6 -# Short-Description: nghttpx initscript -# Description: nghttpx initscript -### END INIT INFO - -# Author: Tatsuhiro Tsujikawa -# -# Do NOT "set -e" - -# PATH should only include /usr/* if it runs after the mountnfs.sh script -PATH=/sbin:/usr/sbin:/bin:/usr/bin:/usr/local/bin -DESC="HTTP/2 reverse proxy" -NAME=nghttpx -# Depending on the configuration, binary may be located under @sbindir@ -DAEMON=@bindir@/$NAME -PIDFILE=/var/run/$NAME.pid -DAEMON_ARGS="--conf /etc/nghttpx/nghttpx.conf --pid-file=$PIDFILE" -SCRIPTNAME=/etc/init.d/$NAME - -# Exit if the package is not installed -[ -x "$DAEMON" ] || exit 0 - -# Read configuration variable file if it is present -[ -r /etc/default/$NAME ] && . /etc/default/$NAME - -# Load the VERBOSE setting and other rcS variables -. /lib/init/vars.sh - -# Define LSB log_* functions. -# Depend on lsb-base (>= 3.2-14) to ensure that this file is present -# and status_of_proc is working. -. /lib/lsb/init-functions - -# -# Function that starts the daemon/service -# -do_start() -{ - # Return - # 0 if daemon has been started - # 1 if daemon was already running - # 2 if daemon could not be started - start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON --test > /dev/null \ - || return 1 - start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON -- \ - $DAEMON_ARGS \ - || return 2 - # Add code here, if necessary, that waits for the process to be ready - # to handle requests from services started subsequently which depend - # on this one. As a last resort, sleep for some time. -} - -# -# Function that stops the daemon/service -# -do_stop() -{ - # Return - # 0 if daemon has been stopped - # 1 if daemon was already stopped - # 2 if daemon could not be stopped - # other if a failure occurred - start-stop-daemon --stop --quiet --retry=TERM/30/KILL/5 --pidfile $PIDFILE - RETVAL="$?" - [ "$RETVAL" = 2 ] && return 2 - - # Wait for children to finish too if this is a daemon that forks - # and if the daemon is only ever run from this initscript. - # If the above conditions are not satisfied then add some other code - # that waits for the process to drop all resources that could be - # needed by services started subsequently. A last resort is to - # sleep for some time. - #start-stop-daemon --stop --quiet --oknodo --retry=0/30/KILL/5 --exec $DAEMON - #[ "$?" = 2 ] && return 2 - # Many daemons don't delete their pidfiles when they exit. - rm -f $PIDFILE - return "$RETVAL" -} - -# -# Function that sends a SIGHUP to the daemon/service -# -do_reload() { - # - # If the daemon can reload its configuration without - # restarting (for example, when it is sent a SIGHUP), - # then implement that here. - # - start-stop-daemon --stop --signal 1 --quiet --pidfile $PIDFILE --name $NAME - return 0 -} - -case "$1" in - start) - [ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC" "$NAME" - do_start - case "$?" in - 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;; - 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;; - esac - ;; - stop) - [ "$VERBOSE" != no ] && log_daemon_msg "Stopping $DESC" "$NAME" - do_stop - case "$?" in - 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;; - 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;; - esac - ;; - status) - status_of_proc "$DAEMON" "$NAME" && exit 0 || exit $? - ;; - upgrade) - log_daemon_msg "Upgrade $DESC" "$NAME" - pid=`pidofproc -p $PIDFILE $NAME` - case "$?" in - 0) echo "Sending USR2 signal to $pid" - kill -USR2 $pid - echo "Waiting for new binary..." - sleep 5 - echo "Sending QUIT signal to $pid" - kill -QUIT $pid - log_end_msg 0 - ;; - *) echo "pidofproc() failed" - log_end_msg 1 - ;; - esac - ;; - #reload|force-reload) - # - # If do_reload() is not implemented then leave this commented out - # and leave 'force-reload' as an alias for 'restart'. - # - #log_daemon_msg "Reloading $DESC" "$NAME" - #do_reload - #log_end_msg $? - #;; - restart|force-reload) - # - # If the "reload" option is implemented then remove the - # 'force-reload' alias - # - log_daemon_msg "Restarting $DESC" "$NAME" - do_stop - case "$?" in - 0|1) - do_start - case "$?" in - 0) log_end_msg 0 ;; - 1) log_end_msg 1 ;; # Old process is still running - *) log_end_msg 1 ;; # Failed to start - esac - ;; - *) - # Failed to stop - log_end_msg 1 - ;; - esac - ;; - *) - echo "Usage: $SCRIPTNAME {start|stop|status|restart|force-reload|upgrade}" >&2 - exit 3 - ;; -esac - -: diff --git a/contrib/nghttpx-logrotate b/contrib/nghttpx-logrotate deleted file mode 100644 index f1002bd..0000000 --- a/contrib/nghttpx-logrotate +++ /dev/null @@ -1,11 +0,0 @@ -/var/log/nghttpx/*.log { - weekly - rotate 52 - missingok - compress - delaycompress - notifempty - postrotate - killall -USR1 nghttpx 2> /dev/null || true - endscript -} diff --git a/contrib/nghttpx-upstart.conf.in b/contrib/nghttpx-upstart.conf.in deleted file mode 100644 index 0b79916..0000000 --- a/contrib/nghttpx-upstart.conf.in +++ /dev/null @@ -1,8 +0,0 @@ -# vim: ft=upstart: - -description "HTTP/2 reverse proxy" - -start on runlevel [2] -stop on runlevel [016] - -exec @bindir@/nghttpx diff --git a/contrib/nghttpx.service.in b/contrib/nghttpx.service.in deleted file mode 100644 index 9c7f851..0000000 --- a/contrib/nghttpx.service.in +++ /dev/null @@ -1,10 +0,0 @@ -[Unit] -Description=HTTP/2 experimental proxy -After=network.target - -[Service] -Type=simple -ExecStart=@bindir@/nghttpx --errorlog-syslog - -[Install] -WantedBy=multi-user.target diff --git a/depcomp b/depcomp deleted file mode 100755 index 4ebd5b3..0000000 --- a/depcomp +++ /dev/null @@ -1,791 +0,0 @@ -#! /bin/sh -# depcomp - compile a program generating dependencies as side-effects - -scriptversion=2013-05-30.07; # UTC - -# Copyright (C) 1999-2013 Free Software Foundation, Inc. - -# 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 2, 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 . - -# As a special exception to the GNU General Public License, if you -# distribute this file as part of a program that contains a -# configuration script generated by Autoconf, you may include it under -# the same distribution terms that you use for the rest of that program. - -# Originally written by Alexandre Oliva . - -case $1 in - '') - echo "$0: No command. Try '$0 --help' for more information." 1>&2 - exit 1; - ;; - -h | --h*) - cat <<\EOF -Usage: depcomp [--help] [--version] PROGRAM [ARGS] - -Run PROGRAMS ARGS to compile a file, generating dependencies -as side-effects. - -Environment variables: - depmode Dependency tracking mode. - source Source file read by 'PROGRAMS ARGS'. - object Object file output by 'PROGRAMS ARGS'. - DEPDIR directory where to store dependencies. - depfile Dependency file to output. - tmpdepfile Temporary file to use when outputting dependencies. - libtool Whether libtool is used (yes/no). - -Report bugs to . -EOF - exit $? - ;; - -v | --v*) - echo "depcomp $scriptversion" - exit $? - ;; -esac - -# Get the directory component of the given path, and save it in the -# global variables '$dir'. Note that this directory component will -# be either empty or ending with a '/' character. This is deliberate. -set_dir_from () -{ - case $1 in - */*) dir=`echo "$1" | sed -e 's|/[^/]*$|/|'`;; - *) dir=;; - esac -} - -# Get the suffix-stripped basename of the given path, and save it the -# global variable '$base'. -set_base_from () -{ - base=`echo "$1" | sed -e 's|^.*/||' -e 's/\.[^.]*$//'` -} - -# If no dependency file was actually created by the compiler invocation, -# we still have to create a dummy depfile, to avoid errors with the -# Makefile "include basename.Plo" scheme. -make_dummy_depfile () -{ - echo "#dummy" > "$depfile" -} - -# Factor out some common post-processing of the generated depfile. -# Requires the auxiliary global variable '$tmpdepfile' to be set. -aix_post_process_depfile () -{ - # If the compiler actually managed to produce a dependency file, - # post-process it. - if test -f "$tmpdepfile"; then - # Each line is of the form 'foo.o: dependency.h'. - # Do two passes, one to just change these to - # $object: dependency.h - # and one to simply output - # dependency.h: - # which is needed to avoid the deleted-header problem. - { sed -e "s,^.*\.[$lower]*:,$object:," < "$tmpdepfile" - sed -e "s,^.*\.[$lower]*:[$tab ]*,," -e 's,$,:,' < "$tmpdepfile" - } > "$depfile" - rm -f "$tmpdepfile" - else - make_dummy_depfile - fi -} - -# A tabulation character. -tab=' ' -# A newline character. -nl=' -' -# Character ranges might be problematic outside the C locale. -# These definitions help. -upper=ABCDEFGHIJKLMNOPQRSTUVWXYZ -lower=abcdefghijklmnopqrstuvwxyz -digits=0123456789 -alpha=${upper}${lower} - -if test -z "$depmode" || test -z "$source" || test -z "$object"; then - echo "depcomp: Variables source, object and depmode must be set" 1>&2 - exit 1 -fi - -# Dependencies for sub/bar.o or sub/bar.obj go into sub/.deps/bar.Po. -depfile=${depfile-`echo "$object" | - sed 's|[^\\/]*$|'${DEPDIR-.deps}'/&|;s|\.\([^.]*\)$|.P\1|;s|Pobj$|Po|'`} -tmpdepfile=${tmpdepfile-`echo "$depfile" | sed 's/\.\([^.]*\)$/.T\1/'`} - -rm -f "$tmpdepfile" - -# Avoid interferences from the environment. -gccflag= dashmflag= - -# Some modes work just like other modes, but use different flags. We -# parameterize here, but still list the modes in the big case below, -# to make depend.m4 easier to write. Note that we *cannot* use a case -# here, because this file can only contain one case statement. -if test "$depmode" = hp; then - # HP compiler uses -M and no extra arg. - gccflag=-M - depmode=gcc -fi - -if test "$depmode" = dashXmstdout; then - # This is just like dashmstdout with a different argument. - dashmflag=-xM - depmode=dashmstdout -fi - -cygpath_u="cygpath -u -f -" -if test "$depmode" = msvcmsys; then - # This is just like msvisualcpp but w/o cygpath translation. - # Just convert the backslash-escaped backslashes to single forward - # slashes to satisfy depend.m4 - cygpath_u='sed s,\\\\,/,g' - depmode=msvisualcpp -fi - -if test "$depmode" = msvc7msys; then - # This is just like msvc7 but w/o cygpath translation. - # Just convert the backslash-escaped backslashes to single forward - # slashes to satisfy depend.m4 - cygpath_u='sed s,\\\\,/,g' - depmode=msvc7 -fi - -if test "$depmode" = xlc; then - # IBM C/C++ Compilers xlc/xlC can output gcc-like dependency information. - gccflag=-qmakedep=gcc,-MF - depmode=gcc -fi - -case "$depmode" in -gcc3) -## gcc 3 implements dependency tracking that does exactly what -## we want. Yay! Note: for some reason libtool 1.4 doesn't like -## it if -MD -MP comes after the -MF stuff. Hmm. -## Unfortunately, FreeBSD c89 acceptance of flags depends upon -## the command line argument order; so add the flags where they -## appear in depend2.am. Note that the slowdown incurred here -## affects only configure: in makefiles, %FASTDEP% shortcuts this. - for arg - do - case $arg in - -c) set fnord "$@" -MT "$object" -MD -MP -MF "$tmpdepfile" "$arg" ;; - *) set fnord "$@" "$arg" ;; - esac - shift # fnord - shift # $arg - done - "$@" - stat=$? - if test $stat -ne 0; then - rm -f "$tmpdepfile" - exit $stat - fi - mv "$tmpdepfile" "$depfile" - ;; - -gcc) -## Note that this doesn't just cater to obsosete pre-3.x GCC compilers. -## but also to in-use compilers like IMB xlc/xlC and the HP C compiler. -## (see the conditional assignment to $gccflag above). -## There are various ways to get dependency output from gcc. Here's -## why we pick this rather obscure method: -## - Don't want to use -MD because we'd like the dependencies to end -## up in a subdir. Having to rename by hand is ugly. -## (We might end up doing this anyway to support other compilers.) -## - The DEPENDENCIES_OUTPUT environment variable makes gcc act like -## -MM, not -M (despite what the docs say). Also, it might not be -## supported by the other compilers which use the 'gcc' depmode. -## - Using -M directly means running the compiler twice (even worse -## than renaming). - if test -z "$gccflag"; then - gccflag=-MD, - fi - "$@" -Wp,"$gccflag$tmpdepfile" - stat=$? - if test $stat -ne 0; then - rm -f "$tmpdepfile" - exit $stat - fi - rm -f "$depfile" - echo "$object : \\" > "$depfile" - # The second -e expression handles DOS-style file names with drive - # letters. - sed -e 's/^[^:]*: / /' \ - -e 's/^['$alpha']:\/[^:]*: / /' < "$tmpdepfile" >> "$depfile" -## This next piece of magic avoids the "deleted header file" problem. -## The problem is that when a header file which appears in a .P file -## is deleted, the dependency causes make to die (because there is -## typically no way to rebuild the header). We avoid this by adding -## dummy dependencies for each header file. Too bad gcc doesn't do -## this for us directly. -## Some versions of gcc put a space before the ':'. On the theory -## that the space means something, we add a space to the output as -## well. hp depmode also adds that space, but also prefixes the VPATH -## to the object. Take care to not repeat it in the output. -## Some versions of the HPUX 10.20 sed can't process this invocation -## correctly. Breaking it into two sed invocations is a workaround. - tr ' ' "$nl" < "$tmpdepfile" \ - | sed -e 's/^\\$//' -e '/^$/d' -e "s|.*$object$||" -e '/:$/d' \ - | sed -e 's/$/ :/' >> "$depfile" - rm -f "$tmpdepfile" - ;; - -hp) - # This case exists only to let depend.m4 do its work. It works by - # looking at the text of this script. This case will never be run, - # since it is checked for above. - exit 1 - ;; - -sgi) - if test "$libtool" = yes; then - "$@" "-Wp,-MDupdate,$tmpdepfile" - else - "$@" -MDupdate "$tmpdepfile" - fi - stat=$? - if test $stat -ne 0; then - rm -f "$tmpdepfile" - exit $stat - fi - rm -f "$depfile" - - if test -f "$tmpdepfile"; then # yes, the sourcefile depend on other files - echo "$object : \\" > "$depfile" - # Clip off the initial element (the dependent). Don't try to be - # clever and replace this with sed code, as IRIX sed won't handle - # lines with more than a fixed number of characters (4096 in - # IRIX 6.2 sed, 8192 in IRIX 6.5). We also remove comment lines; - # the IRIX cc adds comments like '#:fec' to the end of the - # dependency line. - tr ' ' "$nl" < "$tmpdepfile" \ - | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' \ - | tr "$nl" ' ' >> "$depfile" - echo >> "$depfile" - # The second pass generates a dummy entry for each header file. - tr ' ' "$nl" < "$tmpdepfile" \ - | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' -e 's/$/:/' \ - >> "$depfile" - else - make_dummy_depfile - fi - rm -f "$tmpdepfile" - ;; - -xlc) - # This case exists only to let depend.m4 do its work. It works by - # looking at the text of this script. This case will never be run, - # since it is checked for above. - exit 1 - ;; - -aix) - # The C for AIX Compiler uses -M and outputs the dependencies - # in a .u file. In older versions, this file always lives in the - # current directory. Also, the AIX compiler puts '$object:' at the - # start of each line; $object doesn't have directory information. - # Version 6 uses the directory in both cases. - set_dir_from "$object" - set_base_from "$object" - if test "$libtool" = yes; then - tmpdepfile1=$dir$base.u - tmpdepfile2=$base.u - tmpdepfile3=$dir.libs/$base.u - "$@" -Wc,-M - else - tmpdepfile1=$dir$base.u - tmpdepfile2=$dir$base.u - tmpdepfile3=$dir$base.u - "$@" -M - fi - stat=$? - if test $stat -ne 0; then - rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" - exit $stat - fi - - for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" - do - test -f "$tmpdepfile" && break - done - aix_post_process_depfile - ;; - -tcc) - # tcc (Tiny C Compiler) understand '-MD -MF file' since version 0.9.26 - # FIXME: That version still under development at the moment of writing. - # Make that this statement remains true also for stable, released - # versions. - # It will wrap lines (doesn't matter whether long or short) with a - # trailing '\', as in: - # - # foo.o : \ - # foo.c \ - # foo.h \ - # - # It will put a trailing '\' even on the last line, and will use leading - # spaces rather than leading tabs (at least since its commit 0394caf7 - # "Emit spaces for -MD"). - "$@" -MD -MF "$tmpdepfile" - stat=$? - if test $stat -ne 0; then - rm -f "$tmpdepfile" - exit $stat - fi - rm -f "$depfile" - # Each non-empty line is of the form 'foo.o : \' or ' dep.h \'. - # We have to change lines of the first kind to '$object: \'. - sed -e "s|.*:|$object :|" < "$tmpdepfile" > "$depfile" - # And for each line of the second kind, we have to emit a 'dep.h:' - # dummy dependency, to avoid the deleted-header problem. - sed -n -e 's|^ *\(.*\) *\\$|\1:|p' < "$tmpdepfile" >> "$depfile" - rm -f "$tmpdepfile" - ;; - -## The order of this option in the case statement is important, since the -## shell code in configure will try each of these formats in the order -## listed in this file. A plain '-MD' option would be understood by many -## compilers, so we must ensure this comes after the gcc and icc options. -pgcc) - # Portland's C compiler understands '-MD'. - # Will always output deps to 'file.d' where file is the root name of the - # source file under compilation, even if file resides in a subdirectory. - # The object file name does not affect the name of the '.d' file. - # pgcc 10.2 will output - # foo.o: sub/foo.c sub/foo.h - # and will wrap long lines using '\' : - # foo.o: sub/foo.c ... \ - # sub/foo.h ... \ - # ... - set_dir_from "$object" - # Use the source, not the object, to determine the base name, since - # that's sadly what pgcc will do too. - set_base_from "$source" - tmpdepfile=$base.d - - # For projects that build the same source file twice into different object - # files, the pgcc approach of using the *source* file root name can cause - # problems in parallel builds. Use a locking strategy to avoid stomping on - # the same $tmpdepfile. - lockdir=$base.d-lock - trap " - echo '$0: caught signal, cleaning up...' >&2 - rmdir '$lockdir' - exit 1 - " 1 2 13 15 - numtries=100 - i=$numtries - while test $i -gt 0; do - # mkdir is a portable test-and-set. - if mkdir "$lockdir" 2>/dev/null; then - # This process acquired the lock. - "$@" -MD - stat=$? - # Release the lock. - rmdir "$lockdir" - break - else - # If the lock is being held by a different process, wait - # until the winning process is done or we timeout. - while test -d "$lockdir" && test $i -gt 0; do - sleep 1 - i=`expr $i - 1` - done - fi - i=`expr $i - 1` - done - trap - 1 2 13 15 - if test $i -le 0; then - echo "$0: failed to acquire lock after $numtries attempts" >&2 - echo "$0: check lockdir '$lockdir'" >&2 - exit 1 - fi - - if test $stat -ne 0; then - rm -f "$tmpdepfile" - exit $stat - fi - rm -f "$depfile" - # Each line is of the form `foo.o: dependent.h', - # or `foo.o: dep1.h dep2.h \', or ` dep3.h dep4.h \'. - # Do two passes, one to just change these to - # `$object: dependent.h' and one to simply `dependent.h:'. - sed "s,^[^:]*:,$object :," < "$tmpdepfile" > "$depfile" - # Some versions of the HPUX 10.20 sed can't process this invocation - # correctly. Breaking it into two sed invocations is a workaround. - sed 's,^[^:]*: \(.*\)$,\1,;s/^\\$//;/^$/d;/:$/d' < "$tmpdepfile" \ - | sed -e 's/$/ :/' >> "$depfile" - rm -f "$tmpdepfile" - ;; - -hp2) - # The "hp" stanza above does not work with aCC (C++) and HP's ia64 - # compilers, which have integrated preprocessors. The correct option - # to use with these is +Maked; it writes dependencies to a file named - # 'foo.d', which lands next to the object file, wherever that - # happens to be. - # Much of this is similar to the tru64 case; see comments there. - set_dir_from "$object" - set_base_from "$object" - if test "$libtool" = yes; then - tmpdepfile1=$dir$base.d - tmpdepfile2=$dir.libs/$base.d - "$@" -Wc,+Maked - else - tmpdepfile1=$dir$base.d - tmpdepfile2=$dir$base.d - "$@" +Maked - fi - stat=$? - if test $stat -ne 0; then - rm -f "$tmpdepfile1" "$tmpdepfile2" - exit $stat - fi - - for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" - do - test -f "$tmpdepfile" && break - done - if test -f "$tmpdepfile"; then - sed -e "s,^.*\.[$lower]*:,$object:," "$tmpdepfile" > "$depfile" - # Add 'dependent.h:' lines. - sed -ne '2,${ - s/^ *// - s/ \\*$// - s/$/:/ - p - }' "$tmpdepfile" >> "$depfile" - else - make_dummy_depfile - fi - rm -f "$tmpdepfile" "$tmpdepfile2" - ;; - -tru64) - # The Tru64 compiler uses -MD to generate dependencies as a side - # effect. 'cc -MD -o foo.o ...' puts the dependencies into 'foo.o.d'. - # At least on Alpha/Redhat 6.1, Compaq CCC V6.2-504 seems to put - # dependencies in 'foo.d' instead, so we check for that too. - # Subdirectories are respected. - set_dir_from "$object" - set_base_from "$object" - - if test "$libtool" = yes; then - # Libtool generates 2 separate objects for the 2 libraries. These - # two compilations output dependencies in $dir.libs/$base.o.d and - # in $dir$base.o.d. We have to check for both files, because - # one of the two compilations can be disabled. We should prefer - # $dir$base.o.d over $dir.libs/$base.o.d because the latter is - # automatically cleaned when .libs/ is deleted, while ignoring - # the former would cause a distcleancheck panic. - tmpdepfile1=$dir$base.o.d # libtool 1.5 - tmpdepfile2=$dir.libs/$base.o.d # Likewise. - tmpdepfile3=$dir.libs/$base.d # Compaq CCC V6.2-504 - "$@" -Wc,-MD - else - tmpdepfile1=$dir$base.d - tmpdepfile2=$dir$base.d - tmpdepfile3=$dir$base.d - "$@" -MD - fi - - stat=$? - if test $stat -ne 0; then - rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" - exit $stat - fi - - for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" - do - test -f "$tmpdepfile" && break - done - # Same post-processing that is required for AIX mode. - aix_post_process_depfile - ;; - -msvc7) - if test "$libtool" = yes; then - showIncludes=-Wc,-showIncludes - else - showIncludes=-showIncludes - fi - "$@" $showIncludes > "$tmpdepfile" - stat=$? - grep -v '^Note: including file: ' "$tmpdepfile" - if test $stat -ne 0; then - rm -f "$tmpdepfile" - exit $stat - fi - rm -f "$depfile" - echo "$object : \\" > "$depfile" - # The first sed program below extracts the file names and escapes - # backslashes for cygpath. The second sed program outputs the file - # name when reading, but also accumulates all include files in the - # hold buffer in order to output them again at the end. This only - # works with sed implementations that can handle large buffers. - sed < "$tmpdepfile" -n ' -/^Note: including file: *\(.*\)/ { - s//\1/ - s/\\/\\\\/g - p -}' | $cygpath_u | sort -u | sed -n ' -s/ /\\ /g -s/\(.*\)/'"$tab"'\1 \\/p -s/.\(.*\) \\/\1:/ -H -$ { - s/.*/'"$tab"'/ - G - p -}' >> "$depfile" - echo >> "$depfile" # make sure the fragment doesn't end with a backslash - rm -f "$tmpdepfile" - ;; - -msvc7msys) - # This case exists only to let depend.m4 do its work. It works by - # looking at the text of this script. This case will never be run, - # since it is checked for above. - exit 1 - ;; - -#nosideeffect) - # This comment above is used by automake to tell side-effect - # dependency tracking mechanisms from slower ones. - -dashmstdout) - # Important note: in order to support this mode, a compiler *must* - # always write the preprocessed file to stdout, regardless of -o. - "$@" || exit $? - - # Remove the call to Libtool. - if test "$libtool" = yes; then - while test "X$1" != 'X--mode=compile'; do - shift - done - shift - fi - - # Remove '-o $object'. - IFS=" " - for arg - do - case $arg in - -o) - shift - ;; - $object) - shift - ;; - *) - set fnord "$@" "$arg" - shift # fnord - shift # $arg - ;; - esac - done - - test -z "$dashmflag" && dashmflag=-M - # Require at least two characters before searching for ':' - # in the target name. This is to cope with DOS-style filenames: - # a dependency such as 'c:/foo/bar' could be seen as target 'c' otherwise. - "$@" $dashmflag | - sed "s|^[$tab ]*[^:$tab ][^:][^:]*:[$tab ]*|$object: |" > "$tmpdepfile" - rm -f "$depfile" - cat < "$tmpdepfile" > "$depfile" - # Some versions of the HPUX 10.20 sed can't process this sed invocation - # correctly. Breaking it into two sed invocations is a workaround. - tr ' ' "$nl" < "$tmpdepfile" \ - | sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' \ - | sed -e 's/$/ :/' >> "$depfile" - rm -f "$tmpdepfile" - ;; - -dashXmstdout) - # This case only exists to satisfy depend.m4. It is never actually - # run, as this mode is specially recognized in the preamble. - exit 1 - ;; - -makedepend) - "$@" || exit $? - # Remove any Libtool call - if test "$libtool" = yes; then - while test "X$1" != 'X--mode=compile'; do - shift - done - shift - fi - # X makedepend - shift - cleared=no eat=no - for arg - do - case $cleared in - no) - set ""; shift - cleared=yes ;; - esac - if test $eat = yes; then - eat=no - continue - fi - case "$arg" in - -D*|-I*) - set fnord "$@" "$arg"; shift ;; - # Strip any option that makedepend may not understand. Remove - # the object too, otherwise makedepend will parse it as a source file. - -arch) - eat=yes ;; - -*|$object) - ;; - *) - set fnord "$@" "$arg"; shift ;; - esac - done - obj_suffix=`echo "$object" | sed 's/^.*\././'` - touch "$tmpdepfile" - ${MAKEDEPEND-makedepend} -o"$obj_suffix" -f"$tmpdepfile" "$@" - rm -f "$depfile" - # makedepend may prepend the VPATH from the source file name to the object. - # No need to regex-escape $object, excess matching of '.' is harmless. - sed "s|^.*\($object *:\)|\1|" "$tmpdepfile" > "$depfile" - # Some versions of the HPUX 10.20 sed can't process the last invocation - # correctly. Breaking it into two sed invocations is a workaround. - sed '1,2d' "$tmpdepfile" \ - | tr ' ' "$nl" \ - | sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' \ - | sed -e 's/$/ :/' >> "$depfile" - rm -f "$tmpdepfile" "$tmpdepfile".bak - ;; - -cpp) - # Important note: in order to support this mode, a compiler *must* - # always write the preprocessed file to stdout. - "$@" || exit $? - - # Remove the call to Libtool. - if test "$libtool" = yes; then - while test "X$1" != 'X--mode=compile'; do - shift - done - shift - fi - - # Remove '-o $object'. - IFS=" " - for arg - do - case $arg in - -o) - shift - ;; - $object) - shift - ;; - *) - set fnord "$@" "$arg" - shift # fnord - shift # $arg - ;; - esac - done - - "$@" -E \ - | sed -n -e '/^# [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' \ - -e '/^#line [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' \ - | sed '$ s: \\$::' > "$tmpdepfile" - rm -f "$depfile" - echo "$object : \\" > "$depfile" - cat < "$tmpdepfile" >> "$depfile" - sed < "$tmpdepfile" '/^$/d;s/^ //;s/ \\$//;s/$/ :/' >> "$depfile" - rm -f "$tmpdepfile" - ;; - -msvisualcpp) - # Important note: in order to support this mode, a compiler *must* - # always write the preprocessed file to stdout. - "$@" || exit $? - - # Remove the call to Libtool. - if test "$libtool" = yes; then - while test "X$1" != 'X--mode=compile'; do - shift - done - shift - fi - - IFS=" " - for arg - do - case "$arg" in - -o) - shift - ;; - $object) - shift - ;; - "-Gm"|"/Gm"|"-Gi"|"/Gi"|"-ZI"|"/ZI") - set fnord "$@" - shift - shift - ;; - *) - set fnord "$@" "$arg" - shift - shift - ;; - esac - done - "$@" -E 2>/dev/null | - sed -n '/^#line [0-9][0-9]* "\([^"]*\)"/ s::\1:p' | $cygpath_u | sort -u > "$tmpdepfile" - rm -f "$depfile" - echo "$object : \\" > "$depfile" - sed < "$tmpdepfile" -n -e 's% %\\ %g' -e '/^\(.*\)$/ s::'"$tab"'\1 \\:p' >> "$depfile" - echo "$tab" >> "$depfile" - sed < "$tmpdepfile" -n -e 's% %\\ %g' -e '/^\(.*\)$/ s::\1\::p' >> "$depfile" - rm -f "$tmpdepfile" - ;; - -msvcmsys) - # This case exists only to let depend.m4 do its work. It works by - # looking at the text of this script. This case will never be run, - # since it is checked for above. - exit 1 - ;; - -none) - exec "$@" - ;; - -*) - echo "Unknown depmode $depmode" 1>&2 - exit 1 - ;; -esac - -exit 0 - -# Local Variables: -# mode: shell-script -# sh-indentation: 2 -# eval: (add-hook 'write-file-hooks 'time-stamp) -# time-stamp-start: "scriptversion=" -# time-stamp-format: "%:y-%02m-%02d.%02H" -# time-stamp-time-zone: "UTC" -# time-stamp-end: "; # UTC" -# End: diff --git a/doc/Makefile.am b/doc/Makefile.am deleted file mode 100644 index 0eefa44..0000000 --- a/doc/Makefile.am +++ /dev/null @@ -1,320 +0,0 @@ -# nghttp2 - HTTP/2 C Library - -# Copyright (c) 2012 Tatsuhiro Tsujikawa - -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: - -# The above copyright notice and this permission notice shall be -# included in all copies or substantial portions of the Software. - -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -man_MANS = nghttp.1 nghttpd.1 nghttpx.1 h2load.1 - -APIDOCS= \ - apiref.rst \ - macros.rst \ - enums.rst \ - types.rst \ - nghttp2_check_header_name.rst \ - nghttp2_check_header_value.rst \ - nghttp2_hd_deflate_bound.rst \ - nghttp2_hd_deflate_change_table_size.rst \ - nghttp2_hd_deflate_del.rst \ - nghttp2_hd_deflate_hd.rst \ - nghttp2_hd_deflate_new.rst \ - nghttp2_hd_deflate_new2.rst \ - nghttp2_hd_inflate_change_table_size.rst \ - nghttp2_hd_inflate_del.rst \ - nghttp2_hd_inflate_end_headers.rst \ - nghttp2_hd_inflate_hd.rst \ - nghttp2_hd_inflate_new.rst \ - nghttp2_hd_inflate_new2.rst \ - nghttp2_is_fatal.rst \ - nghttp2_nv_compare_name.rst \ - nghttp2_option_del.rst \ - nghttp2_option_new.rst \ - nghttp2_option_set_no_auto_window_update.rst \ - nghttp2_option_set_no_http_messaging.rst \ - nghttp2_option_set_peer_max_concurrent_streams.rst \ - nghttp2_option_set_no_recv_client_magic.rst \ - nghttp2_pack_settings_payload.rst \ - nghttp2_priority_spec_check_default.rst \ - nghttp2_priority_spec_default_init.rst \ - nghttp2_priority_spec_init.rst \ - nghttp2_select_next_protocol.rst \ - nghttp2_session_callbacks_del.rst \ - nghttp2_session_callbacks_new.rst \ - nghttp2_session_callbacks_set_before_frame_send_callback.rst \ - nghttp2_session_callbacks_set_data_source_read_length_callback.rst \ - nghttp2_session_callbacks_set_on_begin_frame_callback.rst \ - nghttp2_session_callbacks_set_on_begin_headers_callback.rst \ - nghttp2_session_callbacks_set_on_data_chunk_recv_callback.rst \ - nghttp2_session_callbacks_set_on_frame_not_send_callback.rst \ - nghttp2_session_callbacks_set_on_frame_recv_callback.rst \ - nghttp2_session_callbacks_set_on_frame_send_callback.rst \ - nghttp2_session_callbacks_set_on_header_callback.rst \ - nghttp2_session_callbacks_set_on_invalid_frame_recv_callback.rst \ - nghttp2_session_callbacks_set_on_stream_close_callback.rst \ - nghttp2_session_callbacks_set_recv_callback.rst \ - nghttp2_session_callbacks_set_select_padding_callback.rst \ - nghttp2_session_callbacks_set_send_callback.rst \ - nghttp2_session_callbacks_set_send_data_callback.rst \ - nghttp2_session_client_new.rst \ - nghttp2_session_client_new2.rst \ - nghttp2_session_client_new3.rst \ - nghttp2_session_consume.rst \ - nghttp2_session_consume_connection.rst \ - nghttp2_session_consume_stream.rst \ - nghttp2_session_del.rst \ - nghttp2_session_get_effective_local_window_size.rst \ - nghttp2_session_get_effective_recv_data_length.rst \ - nghttp2_session_get_last_proc_stream_id.rst \ - nghttp2_session_get_next_stream_id.rst \ - nghttp2_session_get_outbound_queue_size.rst \ - nghttp2_session_get_remote_settings.rst \ - nghttp2_session_get_remote_window_size.rst \ - nghttp2_session_get_stream_effective_local_window_size.rst \ - nghttp2_session_get_stream_effective_recv_data_length.rst \ - nghttp2_session_get_stream_local_close.rst \ - nghttp2_session_get_stream_remote_close.rst \ - nghttp2_session_get_stream_remote_window_size.rst \ - nghttp2_session_get_stream_user_data.rst \ - nghttp2_session_mem_recv.rst \ - nghttp2_session_mem_send.rst \ - nghttp2_session_recv.rst \ - nghttp2_session_resume_data.rst \ - nghttp2_session_send.rst \ - nghttp2_session_server_new.rst \ - nghttp2_session_server_new2.rst \ - nghttp2_session_server_new3.rst \ - nghttp2_session_set_next_stream_id.rst \ - nghttp2_session_set_stream_user_data.rst \ - nghttp2_session_terminate_session.rst \ - nghttp2_session_terminate_session2.rst \ - nghttp2_session_upgrade.rst \ - nghttp2_session_want_read.rst \ - nghttp2_session_want_write.rst \ - nghttp2_strerror.rst \ - nghttp2_submit_data.rst \ - nghttp2_submit_goaway.rst \ - nghttp2_submit_headers.rst \ - nghttp2_submit_ping.rst \ - nghttp2_submit_priority.rst \ - nghttp2_submit_push_promise.rst \ - nghttp2_submit_request.rst \ - nghttp2_submit_response.rst \ - nghttp2_submit_rst_stream.rst \ - nghttp2_submit_settings.rst \ - nghttp2_submit_shutdown_notice.rst \ - nghttp2_submit_trailer.rst \ - nghttp2_submit_window_update.rst \ - nghttp2_version.rst - -EXTRA_DIST = \ - mkapiref.py \ - README.rst \ - programmers-guide.rst \ - $(APIDOCS) \ - nghttp.1.rst \ - nghttpd.1.rst \ - nghttpx.1.rst \ - h2load.1.rst \ - sources/index.rst \ - sources/tutorial-client.rst \ - sources/tutorial-server.rst \ - sources/tutorial-hpack.rst \ - sources/nghttpx-howto.rst \ - sources/h2load-howto.rst \ - sources/libnghttp2_asio.rst \ - sources/python-apiref.rst \ - sources/building-android-binary.rst \ - sources/contribute.rst \ - _themes/sphinx_rtd_theme/__init__.py \ - _themes/sphinx_rtd_theme/breadcrumbs.html \ - _themes/sphinx_rtd_theme/footer.html \ - _themes/sphinx_rtd_theme/layout.html \ - _themes/sphinx_rtd_theme/layout_old.html \ - _themes/sphinx_rtd_theme/search.html \ - _themes/sphinx_rtd_theme/searchbox.html \ - _themes/sphinx_rtd_theme/static/css/badge_only.css \ - _themes/sphinx_rtd_theme/static/css/theme.css \ - _themes/sphinx_rtd_theme/static/fonts/FontAwesome.otf \ - _themes/sphinx_rtd_theme/static/fonts/fontawesome-webfont.eot \ - _themes/sphinx_rtd_theme/static/fonts/fontawesome-webfont.svg \ - _themes/sphinx_rtd_theme/static/fonts/fontawesome-webfont.ttf \ - _themes/sphinx_rtd_theme/static/fonts/fontawesome-webfont.woff \ - _themes/sphinx_rtd_theme/static/js/theme.js \ - _themes/sphinx_rtd_theme/theme.conf \ - _themes/sphinx_rtd_theme/versions.html \ - $(man_MANS) \ - bash_completion/nghttp \ - bash_completion/nghttpd \ - bash_completion/nghttpx \ - bash_completion/h2load - -# Makefile for Sphinx documentation -# - -# You can set these variables from the command line. -SPHINXOPTS = -SPHINXBUILD = sphinx-build -PAPER = -BUILDDIR = manual - -# Internal variables. -PAPEROPT_a4 = -D latex_paper_size=a4 -PAPEROPT_letter = -D latex_paper_size=letter -ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . - -.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest - -help: - @echo "Please use \`make ' where is one of" - @echo " html to make standalone HTML files" - @echo " dirhtml to make HTML files named index.html in directories" - @echo " singlehtml to make a single large HTML file" - @echo " pickle to make pickle files" - @echo " json to make JSON files" - @echo " htmlhelp to make HTML files and a HTML help project" - @echo " qthelp to make HTML files and a qthelp project" - @echo " devhelp to make HTML files and a Devhelp project" - @echo " epub to make an epub" - @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" - @echo " latexpdf to make LaTeX files and run them through pdflatex" - @echo " text to make text files" - @echo " man to make manual pages" - @echo " changes to make an overview of all changed/added/deprecated items" - @echo " linkcheck to check all external links for integrity" - @echo " doctest to run all doctests embedded in the documentation (if enabled)" - -apiref.rst macros.rst enums.rst types.rst: \ - $(top_builddir)/lib/includes/nghttp2/nghttp2ver.h \ - $(top_builddir)/lib/includes/nghttp2/nghttp2.h - $(PYTHON) $(top_srcdir)/doc/mkapiref.py \ - $@ macros.rst enums.rst types.rst . $^ - -# Inspired by -# http://www.gnu.org/savannah-checkouts/gnu/automake/manual/html_node/Multiple-Outputs.html -apidoc.stamp: $(top_builddir)/lib/includes/nghttp2/nghttp2ver.h \ - $(top_builddir)/lib/includes/nghttp2/nghttp2.h - @rm -f apidoc.tmp - @touch apidoc.tmp - $(PYTHON) $(top_srcdir)/doc/mkapiref.py \ - $@ macros.rst enums.rst types.rst . $^ - @mv -f apidoc.tmp $@ -$(APIDOC): apidoc.stamp -## Recover from the removal of $@ - @if test -f $@; then :; else \ - rm -f apidoc.stamp; \ - $(MAKE) $(AM_MAKEFLAGS) apidoc.stamp; \ - fi - -clean-local: - -rm $(APIDOCS) - -rm -rf $(BUILDDIR)/* - -html-local: apiref.rst - $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html - @echo - @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." - -dirhtml: - $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml - @echo - @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." - -singlehtml: - $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml - @echo - @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." - -pickle: - $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle - @echo - @echo "Build finished; now you can process the pickle files." - -json: - $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json - @echo - @echo "Build finished; now you can process the JSON files." - -htmlhelp: - $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp - @echo - @echo "Build finished; now you can run HTML Help Workshop with the" \ - ".hhp project file in $(BUILDDIR)/htmlhelp." - -qthelp: - $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp - @echo - @echo "Build finished; now you can run "qcollectiongenerator" with the" \ - ".qhcp project file in $(BUILDDIR)/qthelp, like this:" - @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/nghttp2.qhcp" - @echo "To view the help file:" - @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/nghttp2.qhc" - -devhelp: - $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp - @echo - @echo "Build finished." - @echo "To view the help file:" - @echo "# mkdir -p $$HOME/.local/share/devhelp/nghttp2" - @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/nghttp2" - @echo "# devhelp" - -epub: - $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub - @echo - @echo "Build finished. The epub file is in $(BUILDDIR)/epub." - -latex: - $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex - @echo - @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." - @echo "Run \`make' in that directory to run these through (pdf)latex" \ - "(use \`make latexpdf' here to do that automatically)." - -latexpdf: - $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex - @echo "Running LaTeX files through pdflatex..." - $(MAKE) -C $(BUILDDIR)/latex all-pdf - @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." - -text: - $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text - @echo - @echo "Build finished. The text files are in $(BUILDDIR)/text." - -man: apiref.rst - $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man - @echo - @echo "Build finished. The manual pages are in $(BUILDDIR)/man." - -changes: - $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes - @echo - @echo "The overview file is in $(BUILDDIR)/changes." - -linkcheck: - $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck - @echo - @echo "Link check complete; look for any errors in the above output " \ - "or in $(BUILDDIR)/linkcheck/output.txt." - -doctest: - $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest - @echo "Testing of doctests in the sources finished, look at the " \ - "results in $(BUILDDIR)/doctest/output.txt." diff --git a/doc/Makefile.in b/doc/Makefile.in deleted file mode 100644 index 0f53eb8..0000000 --- a/doc/Makefile.in +++ /dev/null @@ -1,939 +0,0 @@ -# Makefile.in generated by automake 1.14.1 from Makefile.am. -# @configure_input@ - -# Copyright (C) 1994-2013 Free Software Foundation, Inc. - -# This Makefile.in is free software; the Free Software Foundation -# gives unlimited permission to copy and/or distribute it, -# with or without modifications, as long as this notice is preserved. - -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY, to the extent permitted by law; without -# even the implied warranty of MERCHANTABILITY or FITNESS FOR A -# PARTICULAR PURPOSE. - -@SET_MAKE@ - -# nghttp2 - HTTP/2 C Library - -# Copyright (c) 2012 Tatsuhiro Tsujikawa - -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: - -# The above copyright notice and this permission notice shall be -# included in all copies or substantial portions of the Software. - -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -VPATH = @srcdir@ -am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)' -am__make_running_with_option = \ - case $${target_option-} in \ - ?) ;; \ - *) echo "am__make_running_with_option: internal error: invalid" \ - "target option '$${target_option-}' specified" >&2; \ - exit 1;; \ - esac; \ - has_opt=no; \ - sane_makeflags=$$MAKEFLAGS; \ - if $(am__is_gnu_make); then \ - sane_makeflags=$$MFLAGS; \ - else \ - case $$MAKEFLAGS in \ - *\\[\ \ ]*) \ - bs=\\; \ - sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ - | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ - esac; \ - fi; \ - skip_next=no; \ - strip_trailopt () \ - { \ - flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ - }; \ - for flg in $$sane_makeflags; do \ - test $$skip_next = yes && { skip_next=no; continue; }; \ - case $$flg in \ - *=*|--*) continue;; \ - -*I) strip_trailopt 'I'; skip_next=yes;; \ - -*I?*) strip_trailopt 'I';; \ - -*O) strip_trailopt 'O'; skip_next=yes;; \ - -*O?*) strip_trailopt 'O';; \ - -*l) strip_trailopt 'l'; skip_next=yes;; \ - -*l?*) strip_trailopt 'l';; \ - -[dEDm]) skip_next=yes;; \ - -[JT]) skip_next=yes;; \ - esac; \ - case $$flg in \ - *$$target_option*) has_opt=yes; break;; \ - esac; \ - done; \ - test $$has_opt = yes -am__make_dryrun = (target_option=n; $(am__make_running_with_option)) -am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) -pkgdatadir = $(datadir)/@PACKAGE@ -pkgincludedir = $(includedir)/@PACKAGE@ -pkglibdir = $(libdir)/@PACKAGE@ -pkglibexecdir = $(libexecdir)/@PACKAGE@ -am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd -install_sh_DATA = $(install_sh) -c -m 644 -install_sh_PROGRAM = $(install_sh) -c -install_sh_SCRIPT = $(install_sh) -c -INSTALL_HEADER = $(INSTALL_DATA) -transform = $(program_transform_name) -NORMAL_INSTALL = : -PRE_INSTALL = : -POST_INSTALL = : -NORMAL_UNINSTALL = : -PRE_UNINSTALL = : -POST_UNINSTALL = : -build_triplet = @build@ -host_triplet = @host@ -target_triplet = @target@ -subdir = doc -DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \ - $(srcdir)/conf.py.in $(srcdir)/index.rst.in \ - $(srcdir)/package_README.rst.in \ - $(srcdir)/tutorial-client.rst.in \ - $(srcdir)/tutorial-server.rst.in \ - $(srcdir)/tutorial-hpack.rst.in $(srcdir)/nghttpx-howto.rst.in \ - $(srcdir)/h2load-howto.rst.in $(srcdir)/libnghttp2_asio.rst.in \ - $(srcdir)/python-apiref.rst.in \ - $(srcdir)/building-android-binary.rst.in \ - $(srcdir)/nghttp2.h.rst.in $(srcdir)/nghttp2ver.h.rst.in \ - $(srcdir)/asio_http2.h.rst.in \ - $(srcdir)/asio_http2_server.h.rst.in \ - $(srcdir)/asio_http2_client.h.rst.in \ - $(srcdir)/contribute.rst.in -ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 -am__aclocal_m4_deps = $(top_srcdir)/m4/ax_boost_asio.m4 \ - $(top_srcdir)/m4/ax_boost_base.m4 \ - $(top_srcdir)/m4/ax_boost_system.m4 \ - $(top_srcdir)/m4/ax_boost_thread.m4 \ - $(top_srcdir)/m4/ax_check_compile_flag.m4 \ - $(top_srcdir)/m4/ax_cxx_compile_stdcxx_11.m4 \ - $(top_srcdir)/m4/ax_have_epoll.m4 \ - $(top_srcdir)/m4/ax_python_devel.m4 \ - $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/libxml2.m4 \ - $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ - $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ - $(top_srcdir)/configure.ac -am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ - $(ACLOCAL_M4) -mkinstalldirs = $(install_sh) -d -CONFIG_HEADER = $(top_builddir)/config.h -CONFIG_CLEAN_FILES = conf.py index.rst package_README.rst \ - tutorial-client.rst tutorial-server.rst tutorial-hpack.rst \ - nghttpx-howto.rst h2load-howto.rst libnghttp2_asio.rst \ - python-apiref.rst building-android-binary.rst nghttp2.h.rst \ - nghttp2ver.h.rst asio_http2.h.rst asio_http2_server.h.rst \ - asio_http2_client.h.rst contribute.rst -CONFIG_CLEAN_VPATH_FILES = -AM_V_P = $(am__v_P_@AM_V@) -am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) -am__v_P_0 = false -am__v_P_1 = : -AM_V_GEN = $(am__v_GEN_@AM_V@) -am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) -am__v_GEN_0 = @echo " GEN " $@; -am__v_GEN_1 = -AM_V_at = $(am__v_at_@AM_V@) -am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) -am__v_at_0 = @ -am__v_at_1 = -SOURCES = -DIST_SOURCES = -am__can_run_installinfo = \ - case $$AM_UPDATE_INFO_DIR in \ - n|no|NO) false;; \ - *) (install-info --version) >/dev/null 2>&1;; \ - esac -am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; -am__vpath_adj = case $$p in \ - $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ - *) f=$$p;; \ - esac; -am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; -am__install_max = 40 -am__nobase_strip_setup = \ - srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` -am__nobase_strip = \ - for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" -am__nobase_list = $(am__nobase_strip_setup); \ - for p in $$list; do echo "$$p $$p"; done | \ - sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ - $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ - if (++n[$$2] == $(am__install_max)) \ - { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ - END { for (dir in files) print dir, files[dir] }' -am__base_list = \ - sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ - sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' -am__uninstall_files_from_dir = { \ - test -z "$$files" \ - || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ - || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ - $(am__cd) "$$dir" && rm -f $$files; }; \ - } -man1dir = $(mandir)/man1 -am__installdirs = "$(DESTDIR)$(man1dir)" -NROFF = nroff -MANS = $(man_MANS) -am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) -DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) -ACLOCAL = @ACLOCAL@ -AMTAR = @AMTAR@ -AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ -APPLDFLAGS = @APPLDFLAGS@ -AR = @AR@ -AUTOCONF = @AUTOCONF@ -AUTOHEADER = @AUTOHEADER@ -AUTOMAKE = @AUTOMAKE@ -AWK = @AWK@ -BOOST_ASIO_LIB = @BOOST_ASIO_LIB@ -BOOST_CPPFLAGS = @BOOST_CPPFLAGS@ -BOOST_LDFLAGS = @BOOST_LDFLAGS@ -BOOST_SYSTEM_LIB = @BOOST_SYSTEM_LIB@ -BOOST_THREAD_LIB = @BOOST_THREAD_LIB@ -CC = @CC@ -CCDEPMODE = @CCDEPMODE@ -CFLAGS = @CFLAGS@ -CPP = @CPP@ -CPPFLAGS = @CPPFLAGS@ -CUNIT_CFLAGS = @CUNIT_CFLAGS@ -CUNIT_LIBS = @CUNIT_LIBS@ -CXX = @CXX@ -CXXCPP = @CXXCPP@ -CXXDEPMODE = @CXXDEPMODE@ -CXXFLAGS = @CXXFLAGS@ -CYGPATH_W = @CYGPATH_W@ -CYTHON = @CYTHON@ -DEFS = @DEFS@ -DEPDIR = @DEPDIR@ -DLLTOOL = @DLLTOOL@ -DSYMUTIL = @DSYMUTIL@ -DUMPBIN = @DUMPBIN@ -ECHO_C = @ECHO_C@ -ECHO_N = @ECHO_N@ -ECHO_T = @ECHO_T@ -EGREP = @EGREP@ -EXEEXT = @EXEEXT@ -FGREP = @FGREP@ -GREP = @GREP@ -HAVE_CXX11 = @HAVE_CXX11@ -INSTALL = @INSTALL@ -INSTALL_DATA = @INSTALL_DATA@ -INSTALL_PROGRAM = @INSTALL_PROGRAM@ -INSTALL_SCRIPT = @INSTALL_SCRIPT@ -INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ -JANSSON_CFLAGS = @JANSSON_CFLAGS@ -JANSSON_LIBS = @JANSSON_LIBS@ -JEMALLOC_LIBS = @JEMALLOC_LIBS@ -LD = @LD@ -LDFLAGS = @LDFLAGS@ -LIBEVENT_OPENSSL_CFLAGS = @LIBEVENT_OPENSSL_CFLAGS@ -LIBEVENT_OPENSSL_LIBS = @LIBEVENT_OPENSSL_LIBS@ -LIBEV_CFLAGS = @LIBEV_CFLAGS@ -LIBEV_LIBS = @LIBEV_LIBS@ -LIBOBJS = @LIBOBJS@ -LIBS = @LIBS@ -LIBSPDYLAY_CFLAGS = @LIBSPDYLAY_CFLAGS@ -LIBSPDYLAY_LIBS = @LIBSPDYLAY_LIBS@ -LIBTOOL = @LIBTOOL@ -LIPO = @LIPO@ -LN_S = @LN_S@ -LTLIBOBJS = @LTLIBOBJS@ -LT_AGE = @LT_AGE@ -LT_CURRENT = @LT_CURRENT@ -LT_REVISION = @LT_REVISION@ -MAKEINFO = @MAKEINFO@ -MANIFEST_TOOL = @MANIFEST_TOOL@ -MKDIR_P = @MKDIR_P@ -NM = @NM@ -NMEDIT = @NMEDIT@ -OBJDUMP = @OBJDUMP@ -OBJEXT = @OBJEXT@ -OPENSSL_CFLAGS = @OPENSSL_CFLAGS@ -OPENSSL_LIBS = @OPENSSL_LIBS@ -OTOOL = @OTOOL@ -OTOOL64 = @OTOOL64@ -PACKAGE = @PACKAGE@ -PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ -PACKAGE_NAME = @PACKAGE_NAME@ -PACKAGE_STRING = @PACKAGE_STRING@ -PACKAGE_TARNAME = @PACKAGE_TARNAME@ -PACKAGE_URL = @PACKAGE_URL@ -PACKAGE_VERSION = @PACKAGE_VERSION@ -PACKAGE_VERSION_NUM = @PACKAGE_VERSION_NUM@ -PATH_SEPARATOR = @PATH_SEPARATOR@ -PKG_CONFIG = @PKG_CONFIG@ -PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ -PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ -PYTHON = @PYTHON@ -PYTHON_CPPFLAGS = @PYTHON_CPPFLAGS@ -PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@ -PYTHON_EXTRA_LDFLAGS = @PYTHON_EXTRA_LDFLAGS@ -PYTHON_EXTRA_LIBS = @PYTHON_EXTRA_LIBS@ -PYTHON_LDFLAGS = @PYTHON_LDFLAGS@ -PYTHON_PLATFORM = @PYTHON_PLATFORM@ -PYTHON_PREFIX = @PYTHON_PREFIX@ -PYTHON_SITE_PKG = @PYTHON_SITE_PKG@ -PYTHON_VERSION = @PYTHON_VERSION@ -RANLIB = @RANLIB@ -SED = @SED@ -SET_MAKE = @SET_MAKE@ -SHELL = @SHELL@ -STRIP = @STRIP@ -TESTLDADD = @TESTLDADD@ -VERSION = @VERSION@ -WARNCFLAGS = @WARNCFLAGS@ -XML2_CONFIG = @XML2_CONFIG@ -XML_CPPFLAGS = @XML_CPPFLAGS@ -XML_LIBS = @XML_LIBS@ -ZLIB_CFLAGS = @ZLIB_CFLAGS@ -ZLIB_LIBS = @ZLIB_LIBS@ -abs_builddir = @abs_builddir@ -abs_srcdir = @abs_srcdir@ -abs_top_builddir = @abs_top_builddir@ -abs_top_srcdir = @abs_top_srcdir@ -ac_ct_AR = @ac_ct_AR@ -ac_ct_CC = @ac_ct_CC@ -ac_ct_CXX = @ac_ct_CXX@ -ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ -am__include = @am__include@ -am__leading_dot = @am__leading_dot@ -am__quote = @am__quote@ -am__tar = @am__tar@ -am__untar = @am__untar@ -bindir = @bindir@ -build = @build@ -build_alias = @build_alias@ -build_cpu = @build_cpu@ -build_os = @build_os@ -build_vendor = @build_vendor@ -builddir = @builddir@ -datadir = @datadir@ -datarootdir = @datarootdir@ -docdir = @docdir@ -dvidir = @dvidir@ -exec_prefix = @exec_prefix@ -host = @host@ -host_alias = @host_alias@ -host_cpu = @host_cpu@ -host_os = @host_os@ -host_vendor = @host_vendor@ -htmldir = @htmldir@ -includedir = @includedir@ -infodir = @infodir@ -install_sh = @install_sh@ -libdir = @libdir@ -libexecdir = @libexecdir@ -localedir = @localedir@ -localstatedir = @localstatedir@ -mandir = @mandir@ -mkdir_p = @mkdir_p@ -oldincludedir = @oldincludedir@ -pdfdir = @pdfdir@ -pkgpyexecdir = @pkgpyexecdir@ -pkgpythondir = @pkgpythondir@ -prefix = @prefix@ -program_transform_name = @program_transform_name@ -psdir = @psdir@ -pyexecdir = @pyexecdir@ -pythondir = @pythondir@ -sbindir = @sbindir@ -sharedstatedir = @sharedstatedir@ -srcdir = @srcdir@ -sysconfdir = @sysconfdir@ -target = @target@ -target_alias = @target_alias@ -target_cpu = @target_cpu@ -target_os = @target_os@ -target_vendor = @target_vendor@ -top_build_prefix = @top_build_prefix@ -top_builddir = @top_builddir@ -top_srcdir = @top_srcdir@ -man_MANS = nghttp.1 nghttpd.1 nghttpx.1 h2load.1 -APIDOCS = \ - apiref.rst \ - macros.rst \ - enums.rst \ - types.rst \ - nghttp2_check_header_name.rst \ - nghttp2_check_header_value.rst \ - nghttp2_hd_deflate_bound.rst \ - nghttp2_hd_deflate_change_table_size.rst \ - nghttp2_hd_deflate_del.rst \ - nghttp2_hd_deflate_hd.rst \ - nghttp2_hd_deflate_new.rst \ - nghttp2_hd_deflate_new2.rst \ - nghttp2_hd_inflate_change_table_size.rst \ - nghttp2_hd_inflate_del.rst \ - nghttp2_hd_inflate_end_headers.rst \ - nghttp2_hd_inflate_hd.rst \ - nghttp2_hd_inflate_new.rst \ - nghttp2_hd_inflate_new2.rst \ - nghttp2_is_fatal.rst \ - nghttp2_nv_compare_name.rst \ - nghttp2_option_del.rst \ - nghttp2_option_new.rst \ - nghttp2_option_set_no_auto_window_update.rst \ - nghttp2_option_set_no_http_messaging.rst \ - nghttp2_option_set_peer_max_concurrent_streams.rst \ - nghttp2_option_set_no_recv_client_magic.rst \ - nghttp2_pack_settings_payload.rst \ - nghttp2_priority_spec_check_default.rst \ - nghttp2_priority_spec_default_init.rst \ - nghttp2_priority_spec_init.rst \ - nghttp2_select_next_protocol.rst \ - nghttp2_session_callbacks_del.rst \ - nghttp2_session_callbacks_new.rst \ - nghttp2_session_callbacks_set_before_frame_send_callback.rst \ - nghttp2_session_callbacks_set_data_source_read_length_callback.rst \ - nghttp2_session_callbacks_set_on_begin_frame_callback.rst \ - nghttp2_session_callbacks_set_on_begin_headers_callback.rst \ - nghttp2_session_callbacks_set_on_data_chunk_recv_callback.rst \ - nghttp2_session_callbacks_set_on_frame_not_send_callback.rst \ - nghttp2_session_callbacks_set_on_frame_recv_callback.rst \ - nghttp2_session_callbacks_set_on_frame_send_callback.rst \ - nghttp2_session_callbacks_set_on_header_callback.rst \ - nghttp2_session_callbacks_set_on_invalid_frame_recv_callback.rst \ - nghttp2_session_callbacks_set_on_stream_close_callback.rst \ - nghttp2_session_callbacks_set_recv_callback.rst \ - nghttp2_session_callbacks_set_select_padding_callback.rst \ - nghttp2_session_callbacks_set_send_callback.rst \ - nghttp2_session_callbacks_set_send_data_callback.rst \ - nghttp2_session_client_new.rst \ - nghttp2_session_client_new2.rst \ - nghttp2_session_client_new3.rst \ - nghttp2_session_consume.rst \ - nghttp2_session_consume_connection.rst \ - nghttp2_session_consume_stream.rst \ - nghttp2_session_del.rst \ - nghttp2_session_get_effective_local_window_size.rst \ - nghttp2_session_get_effective_recv_data_length.rst \ - nghttp2_session_get_last_proc_stream_id.rst \ - nghttp2_session_get_next_stream_id.rst \ - nghttp2_session_get_outbound_queue_size.rst \ - nghttp2_session_get_remote_settings.rst \ - nghttp2_session_get_remote_window_size.rst \ - nghttp2_session_get_stream_effective_local_window_size.rst \ - nghttp2_session_get_stream_effective_recv_data_length.rst \ - nghttp2_session_get_stream_local_close.rst \ - nghttp2_session_get_stream_remote_close.rst \ - nghttp2_session_get_stream_remote_window_size.rst \ - nghttp2_session_get_stream_user_data.rst \ - nghttp2_session_mem_recv.rst \ - nghttp2_session_mem_send.rst \ - nghttp2_session_recv.rst \ - nghttp2_session_resume_data.rst \ - nghttp2_session_send.rst \ - nghttp2_session_server_new.rst \ - nghttp2_session_server_new2.rst \ - nghttp2_session_server_new3.rst \ - nghttp2_session_set_next_stream_id.rst \ - nghttp2_session_set_stream_user_data.rst \ - nghttp2_session_terminate_session.rst \ - nghttp2_session_terminate_session2.rst \ - nghttp2_session_upgrade.rst \ - nghttp2_session_want_read.rst \ - nghttp2_session_want_write.rst \ - nghttp2_strerror.rst \ - nghttp2_submit_data.rst \ - nghttp2_submit_goaway.rst \ - nghttp2_submit_headers.rst \ - nghttp2_submit_ping.rst \ - nghttp2_submit_priority.rst \ - nghttp2_submit_push_promise.rst \ - nghttp2_submit_request.rst \ - nghttp2_submit_response.rst \ - nghttp2_submit_rst_stream.rst \ - nghttp2_submit_settings.rst \ - nghttp2_submit_shutdown_notice.rst \ - nghttp2_submit_trailer.rst \ - nghttp2_submit_window_update.rst \ - nghttp2_version.rst - -EXTRA_DIST = \ - mkapiref.py \ - README.rst \ - programmers-guide.rst \ - $(APIDOCS) \ - nghttp.1.rst \ - nghttpd.1.rst \ - nghttpx.1.rst \ - h2load.1.rst \ - sources/index.rst \ - sources/tutorial-client.rst \ - sources/tutorial-server.rst \ - sources/tutorial-hpack.rst \ - sources/nghttpx-howto.rst \ - sources/h2load-howto.rst \ - sources/libnghttp2_asio.rst \ - sources/python-apiref.rst \ - sources/building-android-binary.rst \ - sources/contribute.rst \ - _themes/sphinx_rtd_theme/__init__.py \ - _themes/sphinx_rtd_theme/breadcrumbs.html \ - _themes/sphinx_rtd_theme/footer.html \ - _themes/sphinx_rtd_theme/layout.html \ - _themes/sphinx_rtd_theme/layout_old.html \ - _themes/sphinx_rtd_theme/search.html \ - _themes/sphinx_rtd_theme/searchbox.html \ - _themes/sphinx_rtd_theme/static/css/badge_only.css \ - _themes/sphinx_rtd_theme/static/css/theme.css \ - _themes/sphinx_rtd_theme/static/fonts/FontAwesome.otf \ - _themes/sphinx_rtd_theme/static/fonts/fontawesome-webfont.eot \ - _themes/sphinx_rtd_theme/static/fonts/fontawesome-webfont.svg \ - _themes/sphinx_rtd_theme/static/fonts/fontawesome-webfont.ttf \ - _themes/sphinx_rtd_theme/static/fonts/fontawesome-webfont.woff \ - _themes/sphinx_rtd_theme/static/js/theme.js \ - _themes/sphinx_rtd_theme/theme.conf \ - _themes/sphinx_rtd_theme/versions.html \ - $(man_MANS) \ - bash_completion/nghttp \ - bash_completion/nghttpd \ - bash_completion/nghttpx \ - bash_completion/h2load - - -# Makefile for Sphinx documentation -# - -# You can set these variables from the command line. -SPHINXOPTS = -SPHINXBUILD = sphinx-build -PAPER = -BUILDDIR = manual - -# Internal variables. -PAPEROPT_a4 = -D latex_paper_size=a4 -PAPEROPT_letter = -D latex_paper_size=letter -ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . -all: all-am - -.SUFFIXES: -$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) - @for dep in $?; do \ - case '$(am__configure_deps)' in \ - *$$dep*) \ - ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ - && { if test -f $@; then exit 0; else break; fi; }; \ - exit 1;; \ - esac; \ - done; \ - echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu doc/Makefile'; \ - $(am__cd) $(top_srcdir) && \ - $(AUTOMAKE) --gnu doc/Makefile -.PRECIOUS: Makefile -Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status - @case '$?' in \ - *config.status*) \ - cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ - *) \ - echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ - cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ - esac; - -$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) - cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh - -$(top_srcdir)/configure: $(am__configure_deps) - cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh -$(ACLOCAL_M4): $(am__aclocal_m4_deps) - cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh -$(am__aclocal_m4_deps): -conf.py: $(top_builddir)/config.status $(srcdir)/conf.py.in - cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ -index.rst: $(top_builddir)/config.status $(srcdir)/index.rst.in - cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ -package_README.rst: $(top_builddir)/config.status $(srcdir)/package_README.rst.in - cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ -tutorial-client.rst: $(top_builddir)/config.status $(srcdir)/tutorial-client.rst.in - cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ -tutorial-server.rst: $(top_builddir)/config.status $(srcdir)/tutorial-server.rst.in - cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ -tutorial-hpack.rst: $(top_builddir)/config.status $(srcdir)/tutorial-hpack.rst.in - cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ -nghttpx-howto.rst: $(top_builddir)/config.status $(srcdir)/nghttpx-howto.rst.in - cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ -h2load-howto.rst: $(top_builddir)/config.status $(srcdir)/h2load-howto.rst.in - cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ -libnghttp2_asio.rst: $(top_builddir)/config.status $(srcdir)/libnghttp2_asio.rst.in - cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ -python-apiref.rst: $(top_builddir)/config.status $(srcdir)/python-apiref.rst.in - cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ -building-android-binary.rst: $(top_builddir)/config.status $(srcdir)/building-android-binary.rst.in - cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ -nghttp2.h.rst: $(top_builddir)/config.status $(srcdir)/nghttp2.h.rst.in - cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ -nghttp2ver.h.rst: $(top_builddir)/config.status $(srcdir)/nghttp2ver.h.rst.in - cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ -asio_http2.h.rst: $(top_builddir)/config.status $(srcdir)/asio_http2.h.rst.in - cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ -asio_http2_server.h.rst: $(top_builddir)/config.status $(srcdir)/asio_http2_server.h.rst.in - cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ -asio_http2_client.h.rst: $(top_builddir)/config.status $(srcdir)/asio_http2_client.h.rst.in - cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ -contribute.rst: $(top_builddir)/config.status $(srcdir)/contribute.rst.in - cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ - -mostlyclean-libtool: - -rm -f *.lo - -clean-libtool: - -rm -rf .libs _libs -install-man1: $(man_MANS) - @$(NORMAL_INSTALL) - @list1=''; \ - list2='$(man_MANS)'; \ - test -n "$(man1dir)" \ - && test -n "`echo $$list1$$list2`" \ - || exit 0; \ - echo " $(MKDIR_P) '$(DESTDIR)$(man1dir)'"; \ - $(MKDIR_P) "$(DESTDIR)$(man1dir)" || exit 1; \ - { for i in $$list1; do echo "$$i"; done; \ - if test -n "$$list2"; then \ - for i in $$list2; do echo "$$i"; done \ - | sed -n '/\.1[a-z]*$$/p'; \ - fi; \ - } | while read p; do \ - if test -f $$p; then d=; else d="$(srcdir)/"; fi; \ - echo "$$d$$p"; echo "$$p"; \ - done | \ - sed -e 'n;s,.*/,,;p;h;s,.*\.,,;s,^[^1][0-9a-z]*$$,1,;x' \ - -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,' | \ - sed 'N;N;s,\n, ,g' | { \ - list=; while read file base inst; do \ - if test "$$base" = "$$inst"; then list="$$list $$file"; else \ - echo " $(INSTALL_DATA) '$$file' '$(DESTDIR)$(man1dir)/$$inst'"; \ - $(INSTALL_DATA) "$$file" "$(DESTDIR)$(man1dir)/$$inst" || exit $$?; \ - fi; \ - done; \ - for i in $$list; do echo "$$i"; done | $(am__base_list) | \ - while read files; do \ - test -z "$$files" || { \ - echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(man1dir)'"; \ - $(INSTALL_DATA) $$files "$(DESTDIR)$(man1dir)" || exit $$?; }; \ - done; } - -uninstall-man1: - @$(NORMAL_UNINSTALL) - @list=''; test -n "$(man1dir)" || exit 0; \ - files=`{ for i in $$list; do echo "$$i"; done; \ - l2='$(man_MANS)'; for i in $$l2; do echo "$$i"; done | \ - sed -n '/\.1[a-z]*$$/p'; \ - } | sed -e 's,.*/,,;h;s,.*\.,,;s,^[^1][0-9a-z]*$$,1,;x' \ - -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,'`; \ - dir='$(DESTDIR)$(man1dir)'; $(am__uninstall_files_from_dir) -tags TAGS: - -ctags CTAGS: - -cscope cscopelist: - - -distdir: $(DISTFILES) - @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ - topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ - list='$(DISTFILES)'; \ - dist_files=`for file in $$list; do echo $$file; done | \ - sed -e "s|^$$srcdirstrip/||;t" \ - -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ - case $$dist_files in \ - */*) $(MKDIR_P) `echo "$$dist_files" | \ - sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ - sort -u` ;; \ - esac; \ - for file in $$dist_files; do \ - if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ - if test -d $$d/$$file; then \ - dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ - if test -d "$(distdir)/$$file"; then \ - find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ - fi; \ - if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ - cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ - find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ - fi; \ - cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ - else \ - test -f "$(distdir)/$$file" \ - || cp -p $$d/$$file "$(distdir)/$$file" \ - || exit 1; \ - fi; \ - done -check-am: all-am -check: check-am -all-am: Makefile $(MANS) -installdirs: - for dir in "$(DESTDIR)$(man1dir)"; do \ - test -z "$$dir" || $(MKDIR_P) "$$dir"; \ - done -install: install-am -install-exec: install-exec-am -install-data: install-data-am -uninstall: uninstall-am - -install-am: all-am - @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am - -installcheck: installcheck-am -install-strip: - if test -z '$(STRIP)'; then \ - $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ - install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ - install; \ - else \ - $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ - install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ - "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ - fi -mostlyclean-generic: - -clean-generic: - -distclean-generic: - -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) - -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) - -maintainer-clean-generic: - @echo "This command is intended for maintainers to use" - @echo "it deletes files that may require special tools to rebuild." -clean: clean-am - -clean-am: clean-generic clean-libtool clean-local mostlyclean-am - -distclean: distclean-am - -rm -f Makefile -distclean-am: clean-am distclean-generic - -dvi: dvi-am - -dvi-am: - -html: html-am - -html-am: html-local - -info: info-am - -info-am: - -install-data-am: install-man - -install-dvi: install-dvi-am - -install-dvi-am: - -install-exec-am: - -install-html: install-html-am - -install-html-am: - -install-info: install-info-am - -install-info-am: - -install-man: install-man1 - -install-pdf: install-pdf-am - -install-pdf-am: - -install-ps: install-ps-am - -install-ps-am: - -installcheck-am: - -maintainer-clean: maintainer-clean-am - -rm -f Makefile -maintainer-clean-am: distclean-am maintainer-clean-generic - -mostlyclean: mostlyclean-am - -mostlyclean-am: mostlyclean-generic mostlyclean-libtool - -pdf: pdf-am - -pdf-am: - -ps: ps-am - -ps-am: - -uninstall-am: uninstall-man - -uninstall-man: uninstall-man1 - -.MAKE: install-am install-strip - -.PHONY: all all-am check check-am clean clean-generic clean-libtool \ - clean-local cscopelist-am ctags-am distclean distclean-generic \ - distclean-libtool distdir dvi dvi-am html html-am html-local \ - info info-am install install-am install-data install-data-am \ - install-dvi install-dvi-am install-exec install-exec-am \ - install-html install-html-am install-info install-info-am \ - install-man install-man1 install-pdf install-pdf-am install-ps \ - install-ps-am install-strip installcheck installcheck-am \ - installdirs maintainer-clean maintainer-clean-generic \ - mostlyclean mostlyclean-generic mostlyclean-libtool pdf pdf-am \ - ps ps-am tags-am uninstall uninstall-am uninstall-man \ - uninstall-man1 - - -.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest - -help: - @echo "Please use \`make ' where is one of" - @echo " html to make standalone HTML files" - @echo " dirhtml to make HTML files named index.html in directories" - @echo " singlehtml to make a single large HTML file" - @echo " pickle to make pickle files" - @echo " json to make JSON files" - @echo " htmlhelp to make HTML files and a HTML help project" - @echo " qthelp to make HTML files and a qthelp project" - @echo " devhelp to make HTML files and a Devhelp project" - @echo " epub to make an epub" - @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" - @echo " latexpdf to make LaTeX files and run them through pdflatex" - @echo " text to make text files" - @echo " man to make manual pages" - @echo " changes to make an overview of all changed/added/deprecated items" - @echo " linkcheck to check all external links for integrity" - @echo " doctest to run all doctests embedded in the documentation (if enabled)" - -apiref.rst macros.rst enums.rst types.rst: \ - $(top_builddir)/lib/includes/nghttp2/nghttp2ver.h \ - $(top_builddir)/lib/includes/nghttp2/nghttp2.h - $(PYTHON) $(top_srcdir)/doc/mkapiref.py \ - $@ macros.rst enums.rst types.rst . $^ - -# Inspired by -# http://www.gnu.org/savannah-checkouts/gnu/automake/manual/html_node/Multiple-Outputs.html -apidoc.stamp: $(top_builddir)/lib/includes/nghttp2/nghttp2ver.h \ - $(top_builddir)/lib/includes/nghttp2/nghttp2.h - @rm -f apidoc.tmp - @touch apidoc.tmp - $(PYTHON) $(top_srcdir)/doc/mkapiref.py \ - $@ macros.rst enums.rst types.rst . $^ - @mv -f apidoc.tmp $@ -$(APIDOC): apidoc.stamp - @if test -f $@; then :; else \ - rm -f apidoc.stamp; \ - $(MAKE) $(AM_MAKEFLAGS) apidoc.stamp; \ - fi - -clean-local: - -rm $(APIDOCS) - -rm -rf $(BUILDDIR)/* - -html-local: apiref.rst - $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html - @echo - @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." - -dirhtml: - $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml - @echo - @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." - -singlehtml: - $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml - @echo - @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." - -pickle: - $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle - @echo - @echo "Build finished; now you can process the pickle files." - -json: - $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json - @echo - @echo "Build finished; now you can process the JSON files." - -htmlhelp: - $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp - @echo - @echo "Build finished; now you can run HTML Help Workshop with the" \ - ".hhp project file in $(BUILDDIR)/htmlhelp." - -qthelp: - $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp - @echo - @echo "Build finished; now you can run "qcollectiongenerator" with the" \ - ".qhcp project file in $(BUILDDIR)/qthelp, like this:" - @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/nghttp2.qhcp" - @echo "To view the help file:" - @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/nghttp2.qhc" - -devhelp: - $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp - @echo - @echo "Build finished." - @echo "To view the help file:" - @echo "# mkdir -p $$HOME/.local/share/devhelp/nghttp2" - @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/nghttp2" - @echo "# devhelp" - -epub: - $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub - @echo - @echo "Build finished. The epub file is in $(BUILDDIR)/epub." - -latex: - $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex - @echo - @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." - @echo "Run \`make' in that directory to run these through (pdf)latex" \ - "(use \`make latexpdf' here to do that automatically)." - -latexpdf: - $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex - @echo "Running LaTeX files through pdflatex..." - $(MAKE) -C $(BUILDDIR)/latex all-pdf - @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." - -text: - $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text - @echo - @echo "Build finished. The text files are in $(BUILDDIR)/text." - -man: apiref.rst - $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man - @echo - @echo "Build finished. The manual pages are in $(BUILDDIR)/man." - -changes: - $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes - @echo - @echo "The overview file is in $(BUILDDIR)/changes." - -linkcheck: - $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck - @echo - @echo "Link check complete; look for any errors in the above output " \ - "or in $(BUILDDIR)/linkcheck/output.txt." - -doctest: - $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest - @echo "Testing of doctests in the sources finished, look at the " \ - "results in $(BUILDDIR)/doctest/output.txt." - -# Tell versions [3.59,3.63) of GNU make to not export all variables. -# Otherwise a system limit (for SysV at least) may be exceeded. -.NOEXPORT: diff --git a/doc/README.rst b/doc/README.rst deleted file mode 100644 index 35bfe7e..0000000 --- a/doc/README.rst +++ /dev/null @@ -1,160 +0,0 @@ -nghttp2 Documentation -===================== - -The documentation of nghttp2 is generated using Sphinx. This -directory contains the source files to be processed by Sphinx. The -source file for API reference is generated using a script called -``mkapiref.py`` from the nghttp2 C source code. - -Generating API reference ------------------------- - -As described earlier, we use ``mkapiref.py`` to generate rst formatted -text of API reference from C source code. The ``mkapiref.py`` is not -so flexible and it requires that C source code is formatted in rather -strict rules. - -To generate API reference, just run ``make html``. It runs -``mkapiref.py`` and then run Sphinx to build the entire document. - -The ``mkapiref.py`` reads C source code and searches the comment block -starts with ``/**``. In other words, it only processes the comment -block starting ``/**``. The comment block must end with ``*/``. The -``mkapiref.py`` requires that which type of the object this comment -block refers to. To specify the type of the object, the next line -must contain the so-caled action keyword. Currently, the following -action keywords are supported: ``@function``, ``@functypedef``, -``@enum``, ``@struct`` and ``@union``. The following sections -describes each action keyword. - -@function -######### - -``@function`` is used to refer to the function. The comment block is -used for the document for the function. After the script sees the end -of the comment block, it consumes the lines as the function -declaration until the line which ends with ``;`` is encountered. - -In Sphinx doc, usually the function argument is formatted like -``*this*``. But in C, ``*`` is used for dereferencing a pointer and -we must escape ``*`` with a back slash. To avoid this, we format the -argument like ``|this|``. The ``mkapiref.py`` translates it with -``*this*``, as escaping ``*`` inside ``|`` and ``|`` as necessary. -Note that this shadows the substitution feature of Sphinx. - -The example follows:: - - /** - * @function - * - * Submits PING frame to the |session|. - */ - int nghttp2_submit_ping(nghttp2_session *session); - - -@functypedef -############ - -``@functypedef`` is used to refer to the typedef of the function -pointer. The formatting rule is pretty much the same with -``@function``, but this outputs ``type`` domain, rather than -``function`` domain. - -The example follows:: - - /** - * @functypedef - * - * Callback function invoked when |session| wants to send data to - * remote peer. - */ - typedef ssize_t (*nghttp2_send_callback) - (nghttp2_session *session, - const uint8_t *data, size_t length, int flags, void *user_data); - -@enum -##### - -``@enum`` is used to refer to the enum. Currently, only enum typedefs -are supported. The comment block is used for the document for the -enum type itself. To document each values, put comment block starting -with the line ``/**`` and ending with the ``*/`` just before the enum -value. When the line starts with ``}`` is encountered, the -``mkapiref.py`` extracts strings next to ``}`` as the name of enum. - -At the time of this writing, Sphinx does not support enum type. So we -use ``type`` domain for enum it self and ``macro`` domain for each -value. To refer to the enum value, use ``:enum:`` pseudo role. The -``mkapiref.py`` replaces it with ``:macro:``. By doing this, when -Sphinx will support enum officially, we can replace ``:enum:`` with -the official role easily. - -The example follows:: - - /** - * @enum - * Error codes used in the nghttp2 library. - */ - typedef enum { - /** - * Invalid argument passed. - */ - NGHTTP2_ERR_INVALID_ARGUMENT = -501, - /** - * Zlib error. - */ - NGHTTP2_ERR_ZLIB = -502, - } nghttp2_error; - -@struct -####### - -``@struct`` is used to refer to the struct. Currently, only struct -typedefs are supported. The comment block is used for the document for -the struct type itself.To document each member, put comment block -starting with the line ``/**`` and ending with the ``*/`` just before -the member. When the line starts with ``}`` is encountered, the -``mkapiref.py`` extracts strings next to ``}`` as the name of struct. -The block-less typedef is also supported. In this case, typedef -declaration must be all in one line and the ``mkapiref.py`` uses last -word as the name of struct. - -Some examples follow:: - - /** - * @struct - * The control frame header. - */ - typedef struct { - /** - * SPDY protocol version. - */ - uint16_t version; - /** - * The type of this control frame. - */ - uint16_t type; - /** - * The control frame flags. - */ - uint8_t flags; - /** - * The length field of this control frame. - */ - int32_t length; - } nghttp2_ctrl_hd; - - /** - * @struct - * - * The primary structure to hold the resources needed for a SPDY - * session. The details of this structure is hidden from the public - * API. - */ - typedef struct nghttp2_session nghttp2_session; - -@union -###### - -``@union`` is used to refer to the union. Currently, ``@union`` is an -alias of ``@struct``. diff --git a/doc/_themes/sphinx_rtd_theme/__init__.py b/doc/_themes/sphinx_rtd_theme/__init__.py deleted file mode 100644 index 95ddc52..0000000 --- a/doc/_themes/sphinx_rtd_theme/__init__.py +++ /dev/null @@ -1,17 +0,0 @@ -"""Sphinx ReadTheDocs theme. - -From https://github.com/ryan-roemer/sphinx-bootstrap-theme. - -""" -import os - -VERSION = (0, 1, 8) - -__version__ = ".".join(str(v) for v in VERSION) -__version_full__ = __version__ - - -def get_html_theme_path(): - """Return list of HTML theme paths.""" - cur_dir = os.path.abspath(os.path.dirname(os.path.dirname(__file__))) - return cur_dir diff --git a/doc/_themes/sphinx_rtd_theme/breadcrumbs.html b/doc/_themes/sphinx_rtd_theme/breadcrumbs.html deleted file mode 100644 index 0028421..0000000 --- a/doc/_themes/sphinx_rtd_theme/breadcrumbs.html +++ /dev/null @@ -1,23 +0,0 @@ -
    - -
    -
    diff --git a/doc/_themes/sphinx_rtd_theme/footer.html b/doc/_themes/sphinx_rtd_theme/footer.html deleted file mode 100644 index 6347a44..0000000 --- a/doc/_themes/sphinx_rtd_theme/footer.html +++ /dev/null @@ -1,36 +0,0 @@ -
    - {% if next or prev %} - - {% endif %} - -
    - -
    -

    - {%- if show_copyright %} - {%- if hasdoc('copyright') %} - {% trans path=pathto('copyright'), copyright=copyright|e %}© Copyright {{ copyright }}.{% endtrans %} - {%- else %} - {% trans copyright=copyright|e %}© Copyright {{ copyright }}.{% endtrans %} - {%- endif %} - {%- endif %} - - {%- if last_updated %} - {% trans last_updated=last_updated|e %}Last updated on {{ last_updated }}.{% endtrans %} - {%- endif %} -

    -
    - - {%- if show_sphinx %} - {% trans %}Built with Sphinx using a theme provided by Read the Docs{% endtrans %}. - {%- endif %} - -
    - diff --git a/doc/_themes/sphinx_rtd_theme/layout.html b/doc/_themes/sphinx_rtd_theme/layout.html deleted file mode 100644 index 9481d8b..0000000 --- a/doc/_themes/sphinx_rtd_theme/layout.html +++ /dev/null @@ -1,181 +0,0 @@ -{# TEMPLATE VAR SETTINGS #} -{%- set url_root = pathto('', 1) %} -{%- if url_root == '#' %}{% set url_root = '' %}{% endif %} -{%- if not embedded and docstitle %} - {%- set titlesuffix = " — "|safe + docstitle|e %} -{%- else %} - {%- set titlesuffix = "" %} -{%- endif %} - - - - - - - {{ metatags }} - - {% block htmltitle %} - {{ title|striptags|e }}{{ titlesuffix }} - {% endblock %} - - {# FAVICON #} - {% if favicon %} - - {% endif %} - - {# CSS #} - - {# OPENSEARCH #} - {% if not embedded %} - {% if use_opensearch %} - - {% endif %} - - {% endif %} - - {# RTD hosts this file, so just load on non RTD builds #} - {% if not READTHEDOCS %} - - {% endif %} - - {% for cssfile in css_files %} - - {% endfor %} - - {% for cssfile in extra_css_files %} - - {% endfor %} - - {%- block linktags %} - {%- if hasdoc('about') %} - - {%- endif %} - {%- if hasdoc('genindex') %} - - {%- endif %} - {%- if hasdoc('search') %} - - {%- endif %} - {%- if hasdoc('copyright') %} - - {%- endif %} - - {%- if parents %} - - {%- endif %} - {%- if next %} - - {%- endif %} - {%- if prev %} - - {%- endif %} - {%- endblock %} - {%- block extrahead %} {% endblock %} - - {# Keep modernizr in head - http://modernizr.com/docs/#installing #} - - - - - - -
    - - {# SIDE NAV, TOGGLES ON MOBILE #} - - -
    - - {# MOBILE NAV, TRIGGLES SIDE NAV ON TOGGLE #} - - - - {# PAGE CONTENT #} -
    -
    - {% include "breadcrumbs.html" %} -
    - {% block body %}{% endblock %} -
    - {% include "footer.html" %} -
    -
    - -
    - -
    - {% include "versions.html" %} - - {% if not embedded %} - - - {%- for scriptfile in script_files %} - - {%- endfor %} - - {% endif %} - - {# RTD hosts this file, so just load on non RTD builds #} - {% if not READTHEDOCS %} - - {% endif %} - - {# STICKY NAVIGATION #} - {% if theme_sticky_navigation %} - - {% endif %} - - {%- block footer %} {% endblock %} - - - diff --git a/doc/_themes/sphinx_rtd_theme/layout_old.html b/doc/_themes/sphinx_rtd_theme/layout_old.html deleted file mode 100644 index deb8df2..0000000 --- a/doc/_themes/sphinx_rtd_theme/layout_old.html +++ /dev/null @@ -1,205 +0,0 @@ -{# - basic/layout.html - ~~~~~~~~~~~~~~~~~ - - Master layout template for Sphinx themes. - - :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS. - :license: BSD, see LICENSE for details. -#} -{%- block doctype -%} - -{%- endblock %} -{%- set reldelim1 = reldelim1 is not defined and ' »' or reldelim1 %} -{%- set reldelim2 = reldelim2 is not defined and ' |' or reldelim2 %} -{%- set render_sidebar = (not embedded) and (not theme_nosidebar|tobool) and - (sidebars != []) %} -{%- set url_root = pathto('', 1) %} -{# XXX necessary? #} -{%- if url_root == '#' %}{% set url_root = '' %}{% endif %} -{%- if not embedded and docstitle %} - {%- set titlesuffix = " — "|safe + docstitle|e %} -{%- else %} - {%- set titlesuffix = "" %} -{%- endif %} - -{%- macro relbar() %} - -{%- endmacro %} - -{%- macro sidebar() %} - {%- if render_sidebar %} -
    -
    - {%- block sidebarlogo %} - {%- if logo %} - - {%- endif %} - {%- endblock %} - {%- if sidebars != None %} - {#- new style sidebar: explicitly include/exclude templates #} - {%- for sidebartemplate in sidebars %} - {%- include sidebartemplate %} - {%- endfor %} - {%- else %} - {#- old style sidebars: using blocks -- should be deprecated #} - {%- block sidebartoc %} - {%- include "localtoc.html" %} - {%- endblock %} - {%- block sidebarrel %} - {%- include "relations.html" %} - {%- endblock %} - {%- block sidebarsourcelink %} - {%- include "sourcelink.html" %} - {%- endblock %} - {%- if customsidebar %} - {%- include customsidebar %} - {%- endif %} - {%- block sidebarsearch %} - {%- include "searchbox.html" %} - {%- endblock %} - {%- endif %} -
    -
    - {%- endif %} -{%- endmacro %} - -{%- macro script() %} - - {%- for scriptfile in script_files %} - - {%- endfor %} -{%- endmacro %} - -{%- macro css() %} - - - {%- for cssfile in css_files %} - - {%- endfor %} -{%- endmacro %} - - - - - {{ metatags }} - {%- block htmltitle %} - {{ title|striptags|e }}{{ titlesuffix }} - {%- endblock %} - {{ css() }} - {%- if not embedded %} - {{ script() }} - {%- if use_opensearch %} - - {%- endif %} - {%- if favicon %} - - {%- endif %} - {%- endif %} -{%- block linktags %} - {%- if hasdoc('about') %} - - {%- endif %} - {%- if hasdoc('genindex') %} - - {%- endif %} - {%- if hasdoc('search') %} - - {%- endif %} - {%- if hasdoc('copyright') %} - - {%- endif %} - - {%- if parents %} - - {%- endif %} - {%- if next %} - - {%- endif %} - {%- if prev %} - - {%- endif %} -{%- endblock %} -{%- block extrahead %} {% endblock %} - - -{%- block header %}{% endblock %} - -{%- block relbar1 %}{{ relbar() }}{% endblock %} - -{%- block content %} - {%- block sidebar1 %} {# possible location for sidebar #} {% endblock %} - -
    - {%- block document %} -
    - {%- if render_sidebar %} -
    - {%- endif %} -
    - {% block body %} {% endblock %} -
    - {%- if render_sidebar %} -
    - {%- endif %} -
    - {%- endblock %} - - {%- block sidebar2 %}{{ sidebar() }}{% endblock %} -
    -
    -{%- endblock %} - -{%- block relbar2 %}{{ relbar() }}{% endblock %} - -{%- block footer %} - -

    asdf asdf asdf asdf 22

    -{%- endblock %} - - - diff --git a/doc/_themes/sphinx_rtd_theme/search.html b/doc/_themes/sphinx_rtd_theme/search.html deleted file mode 100644 index e3aa9b5..0000000 --- a/doc/_themes/sphinx_rtd_theme/search.html +++ /dev/null @@ -1,50 +0,0 @@ -{# - basic/search.html - ~~~~~~~~~~~~~~~~~ - - Template for the search page. - - :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS. - :license: BSD, see LICENSE for details. -#} -{%- extends "layout.html" %} -{% set title = _('Search') %} -{% set script_files = script_files + ['_static/searchtools.js'] %} -{% block footer %} - - {# this is used when loading the search index using $.ajax fails, - such as on Chrome for documents on localhost #} - - {{ super() }} -{% endblock %} -{% block body %} - - - {% if search_performed %} -

    {{ _('Search Results') }}

    - {% if not search_results %} -

    {{ _('Your search did not match any documents. Please make sure that all words are spelled correctly and that you\'ve selected enough categories.') }}

    - {% endif %} - {% endif %} -
    - {% if search_results %} -
      - {% for href, caption, context in search_results %} -
    • - {{ caption }} -

      {{ context|e }}

      -
    • - {% endfor %} -
    - {% endif %} -
    -{% endblock %} diff --git a/doc/_themes/sphinx_rtd_theme/searchbox.html b/doc/_themes/sphinx_rtd_theme/searchbox.html deleted file mode 100644 index 35ad52c..0000000 --- a/doc/_themes/sphinx_rtd_theme/searchbox.html +++ /dev/null @@ -1,9 +0,0 @@ -{%- if builder != 'singlehtml' %} -
    -
    - - - -
    -
    -{%- endif %} diff --git a/doc/_themes/sphinx_rtd_theme/static/css/badge_only.css b/doc/_themes/sphinx_rtd_theme/static/css/badge_only.css deleted file mode 100644 index 7e17fb1..0000000 --- a/doc/_themes/sphinx_rtd_theme/static/css/badge_only.css +++ /dev/null @@ -1,2 +0,0 @@ -.fa:before{-webkit-font-smoothing:antialiased}.clearfix{*zoom:1}.clearfix:before,.clearfix:after{display:table;content:""}.clearfix:after{clear:both}@font-face{font-family:FontAwesome;font-weight:normal;font-style:normal;src:url("../font/fontawesome_webfont.eot");src:url("../font/fontawesome_webfont.eot?#iefix") format("embedded-opentype"),url("../font/fontawesome_webfont.woff") format("woff"),url("../font/fontawesome_webfont.ttf") format("truetype"),url("../font/fontawesome_webfont.svg#FontAwesome") format("svg")}.fa:before{display:inline-block;font-family:FontAwesome;font-style:normal;font-weight:normal;line-height:1;text-decoration:inherit}a .fa{display:inline-block;text-decoration:inherit}li .fa{display:inline-block}li .fa-large:before,li .fa-large:before{width:1.875em}ul.fas{list-style-type:none;margin-left:2em;text-indent:-0.8em}ul.fas li .fa{width:0.8em}ul.fas li .fa-large:before,ul.fas li .fa-large:before{vertical-align:baseline}.fa-book:before{content:""}.icon-book:before{content:""}.fa-caret-down:before{content:""}.icon-caret-down:before{content:""}.fa-caret-up:before{content:""}.icon-caret-up:before{content:""}.fa-caret-left:before{content:""}.icon-caret-left:before{content:""}.fa-caret-right:before{content:""}.icon-caret-right:before{content:""}.rst-versions{position:fixed;bottom:0;left:0;width:300px;color:#fcfcfc;background:#1f1d1d;border-top:solid 10px #343131;font-family:"Lato","proxima-nova","Helvetica Neue",Arial,sans-serif;z-index:400}.rst-versions a{color:#2980B9;text-decoration:none}.rst-versions .rst-badge-small{display:none}.rst-versions .rst-current-version{padding:12px;background-color:#272525;display:block;text-align:right;font-size:90%;cursor:pointer;color:#27AE60;*zoom:1}.rst-versions .rst-current-version:before,.rst-versions .rst-current-version:after{display:table;content:""}.rst-versions .rst-current-version:after{clear:both}.rst-versions .rst-current-version .fa{color:#fcfcfc}.rst-versions .rst-current-version .fa-book{float:left}.rst-versions .rst-current-version .icon-book{float:left}.rst-versions .rst-current-version.rst-out-of-date{background-color:#E74C3C;color:#fff}.rst-versions .rst-current-version.rst-active-old-version{background-color:#F1C40F;color:#000}.rst-versions.shift-up .rst-other-versions{display:block}.rst-versions .rst-other-versions{font-size:90%;padding:12px;color:gray;display:none}.rst-versions .rst-other-versions hr{display:block;height:1px;border:0;margin:20px 0;padding:0;border-top:solid 1px #413d3d}.rst-versions .rst-other-versions dd{display:inline-block;margin:0}.rst-versions .rst-other-versions dd a{display:inline-block;padding:6px;color:#fcfcfc}.rst-versions.rst-badge{width:auto;bottom:20px;right:20px;left:auto;border:none;max-width:300px}.rst-versions.rst-badge .icon-book{float:none}.rst-versions.rst-badge .fa-book{float:none}.rst-versions.rst-badge.shift-up .rst-current-version{text-align:right}.rst-versions.rst-badge.shift-up .rst-current-version .fa-book{float:left}.rst-versions.rst-badge.shift-up .rst-current-version .icon-book{float:left}.rst-versions.rst-badge .rst-current-version{width:auto;height:30px;line-height:30px;padding:0 6px;display:block;text-align:center}@media screen and (max-width: 768px){.rst-versions{width:85%;display:none}.rst-versions.shift{display:block}img{width:100%;height:auto}} -/*# sourceMappingURL=badge_only.css.map */ diff --git a/doc/_themes/sphinx_rtd_theme/static/css/theme.css b/doc/_themes/sphinx_rtd_theme/static/css/theme.css deleted file mode 100644 index 57b98fe..0000000 --- a/doc/_themes/sphinx_rtd_theme/static/css/theme.css +++ /dev/null @@ -1,5 +0,0 @@ -*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}article,aside,details,figcaption,figure,footer,header,hgroup,nav,section{display:block}audio,canvas,video{display:inline-block;*display:inline;*zoom:1}audio:not([controls]){display:none}[hidden]{display:none}*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}html{font-size:100%;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%}body{margin:0}a:hover,a:active{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:bold}blockquote{margin:0}dfn{font-style:italic}ins{background:#ff9;color:#000;text-decoration:none}mark{background:#ff0;color:#000;font-style:italic;font-weight:bold}pre,code,.rst-content tt,.rst-content code,kbd,samp{font-family:monospace,serif;_font-family:"courier new",monospace;font-size:1em}pre{white-space:pre}q{quotes:none}q:before,q:after{content:"";content:none}small{font-size:85%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-0.5em}sub{bottom:-0.25em}ul,ol,dl{margin:0;padding:0;list-style:none;list-style-image:none}li{list-style:none}dd{margin:0}img{border:0;-ms-interpolation-mode:bicubic;vertical-align:middle;max-width:100%}svg:not(:root){overflow:hidden}figure{margin:0}form{margin:0}fieldset{border:0;margin:0;padding:0}label{cursor:pointer}legend{border:0;*margin-left:-7px;padding:0;white-space:normal}button,input,select,textarea{font-size:100%;margin:0;vertical-align:baseline;*vertical-align:middle}button,input{line-height:normal}button,input[type="button"],input[type="reset"],input[type="submit"]{cursor:pointer;-webkit-appearance:button;*overflow:visible}button[disabled],input[disabled]{cursor:default}input[type="checkbox"],input[type="radio"]{box-sizing:border-box;padding:0;*width:13px;*height:13px}input[type="search"]{-webkit-appearance:textfield;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box}input[type="search"]::-webkit-search-decoration,input[type="search"]::-webkit-search-cancel-button{-webkit-appearance:none}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}textarea{overflow:auto;vertical-align:top;resize:vertical}table{border-collapse:collapse;border-spacing:0}td{vertical-align:top}.chromeframe{margin:0.2em 0;background:#ccc;color:#000;padding:0.2em 0}.ir{display:block;border:0;text-indent:-999em;overflow:hidden;background-color:transparent;background-repeat:no-repeat;text-align:left;direction:ltr;*line-height:0}.ir br{display:none}.hidden{display:none !important;visibility:hidden}.visuallyhidden{border:0;clip:rect(0 0 0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.visuallyhidden.focusable:active,.visuallyhidden.focusable:focus{clip:auto;height:auto;margin:0;overflow:visible;position:static;width:auto}.invisible{visibility:hidden}.relative{position:relative}big,small{font-size:100%}@media print{html,body,section{background:none !important}*{box-shadow:none !important;text-shadow:none !important;filter:none !important;-ms-filter:none !important}a,a:visited{text-decoration:underline}.ir a:after,a[href^="javascript:"]:after,a[href^="#"]:after{content:""}pre,blockquote{page-break-inside:avoid}thead{display:table-header-group}tr,img{page-break-inside:avoid}img{max-width:100% !important}@page{margin:0.5cm}p,h2,.rst-content p.caption,h3{orphans:3;widows:3}h2,.rst-content p.caption,h3{page-break-after:avoid}}.fa:before,.wy-menu-vertical li span.toctree-expand:before,.wy-menu-vertical li.on a span.toctree-expand:before,.wy-menu-vertical li.current>a span.toctree-expand:before,.rst-content .admonition-title:before,.rst-content h1 .headerlink:before,.rst-content h2 .headerlink:before,.rst-content p.caption .headerlink:before,.rst-content h3 .headerlink:before,.rst-content h4 .headerlink:before,.rst-content h5 .headerlink:before,.rst-content h6 .headerlink:before,.rst-content dl dt .headerlink:before,.rst-content tt.download span:first-child:before,.rst-content code.download span:first-child:before,.icon:before,.wy-dropdown .caret:before,.wy-inline-validate.wy-inline-validate-success .wy-input-context:before,.wy-inline-validate.wy-inline-validate-danger .wy-input-context:before,.wy-inline-validate.wy-inline-validate-warning .wy-input-context:before,.wy-inline-validate.wy-inline-validate-info .wy-input-context:before,.wy-alert,.rst-content .note,.rst-content .attention,.rst-content .caution,.rst-content .danger,.rst-content .error,.rst-content .hint,.rst-content .important,.rst-content .tip,.rst-content .warning,.rst-content .seealso,.rst-content .admonition-todo,.btn,input[type="text"],input[type="password"],input[type="email"],input[type="url"],input[type="date"],input[type="month"],input[type="time"],input[type="datetime"],input[type="datetime-local"],input[type="week"],input[type="number"],input[type="search"],input[type="tel"],input[type="color"],select,textarea,.wy-menu-vertical li.on a,.wy-menu-vertical li.current>a,.wy-side-nav-search>a,.wy-side-nav-search .wy-dropdown>a,.wy-nav-top a{-webkit-font-smoothing:antialiased}.clearfix{*zoom:1}.clearfix:before,.clearfix:after{display:table;content:""}.clearfix:after{clear:both}/*! - * Font Awesome 4.2.0 by @davegandy - http://fontawesome.io - @fontawesome - * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) - */@font-face{font-family:'FontAwesome';src:url("../fonts/fontawesome-webfont.eot?v=4.2.0");src:url("../fonts/fontawesome-webfont.eot?#iefix&v=4.2.0") format("embedded-opentype"),url("../fonts/fontawesome-webfont.woff?v=4.2.0") format("woff"),url("../fonts/fontawesome-webfont.ttf?v=4.2.0") format("truetype"),url("../fonts/fontawesome-webfont.svg?v=4.2.0#fontawesomeregular") format("svg");font-weight:normal;font-style:normal}.fa,.wy-menu-vertical li span.toctree-expand,.wy-menu-vertical li.on a span.toctree-expand,.wy-menu-vertical li.current>a span.toctree-expand,.rst-content .admonition-title,.rst-content h1 .headerlink,.rst-content h2 .headerlink,.rst-content p.caption .headerlink,.rst-content h3 .headerlink,.rst-content h4 .headerlink,.rst-content h5 .headerlink,.rst-content h6 .headerlink,.rst-content dl dt .headerlink,.rst-content tt.download span:first-child,.rst-content code.download span:first-child,.icon{display:inline-block;font:normal normal normal 14px/1 FontAwesome;font-size:inherit;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.fa-lg{font-size:1.33333em;line-height:0.75em;vertical-align:-15%}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-fw{width:1.28571em;text-align:center}.fa-ul{padding-left:0;margin-left:2.14286em;list-style-type:none}.fa-ul>li{position:relative}.fa-li{position:absolute;left:-2.14286em;width:2.14286em;top:0.14286em;text-align:center}.fa-li.fa-lg{left:-1.85714em}.fa-border{padding:.2em .25em .15em;border:solid 0.08em #eee;border-radius:.1em}.pull-right{float:right}.pull-left{float:left}.fa.pull-left,.wy-menu-vertical li span.pull-left.toctree-expand,.wy-menu-vertical li.on a span.pull-left.toctree-expand,.wy-menu-vertical li.current>a span.pull-left.toctree-expand,.rst-content .pull-left.admonition-title,.rst-content h1 .pull-left.headerlink,.rst-content h2 .pull-left.headerlink,.rst-content p.caption .pull-left.headerlink,.rst-content h3 .pull-left.headerlink,.rst-content h4 .pull-left.headerlink,.rst-content h5 .pull-left.headerlink,.rst-content h6 .pull-left.headerlink,.rst-content dl dt .pull-left.headerlink,.rst-content tt.download span.pull-left:first-child,.rst-content code.download span.pull-left:first-child,.pull-left.icon{margin-right:.3em}.fa.pull-right,.wy-menu-vertical li span.pull-right.toctree-expand,.wy-menu-vertical li.on a span.pull-right.toctree-expand,.wy-menu-vertical li.current>a span.pull-right.toctree-expand,.rst-content .pull-right.admonition-title,.rst-content h1 .pull-right.headerlink,.rst-content h2 .pull-right.headerlink,.rst-content p.caption .pull-right.headerlink,.rst-content h3 .pull-right.headerlink,.rst-content h4 .pull-right.headerlink,.rst-content h5 .pull-right.headerlink,.rst-content h6 .pull-right.headerlink,.rst-content dl dt .pull-right.headerlink,.rst-content tt.download span.pull-right:first-child,.rst-content code.download span.pull-right:first-child,.pull-right.icon{margin-left:.3em}.fa-spin{-webkit-animation:fa-spin 2s infinite linear;animation:fa-spin 2s infinite linear}@-webkit-keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.fa-rotate-90{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=1);-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=2);-webkit-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=3);-webkit-transform:rotate(270deg);-ms-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=0);-webkit-transform:scale(-1, 1);-ms-transform:scale(-1, 1);transform:scale(-1, 1)}.fa-flip-vertical{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=2);-webkit-transform:scale(1, -1);-ms-transform:scale(1, -1);transform:scale(1, -1)}:root .fa-rotate-90,:root .fa-rotate-180,:root .fa-rotate-270,:root .fa-flip-horizontal,:root .fa-flip-vertical{filter:none}.fa-stack{position:relative;display:inline-block;width:2em;height:2em;line-height:2em;vertical-align:middle}.fa-stack-1x,.fa-stack-2x{position:absolute;left:0;width:100%;text-align:center}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-glass:before{content:""}.fa-music:before{content:""}.fa-search:before,.icon-search:before{content:""}.fa-envelope-o:before{content:""}.fa-heart:before{content:""}.fa-star:before{content:""}.fa-star-o:before{content:""}.fa-user:before{content:""}.fa-film:before{content:""}.fa-th-large:before{content:""}.fa-th:before{content:""}.fa-th-list:before{content:""}.fa-check:before{content:""}.fa-remove:before,.fa-close:before,.fa-times:before{content:""}.fa-search-plus:before{content:""}.fa-search-minus:before{content:""}.fa-power-off:before{content:""}.fa-signal:before{content:""}.fa-gear:before,.fa-cog:before{content:""}.fa-trash-o:before{content:""}.fa-home:before,.icon-home:before{content:""}.fa-file-o:before{content:""}.fa-clock-o:before{content:""}.fa-road:before{content:""}.fa-download:before,.rst-content tt.download span:first-child:before,.rst-content code.download span:first-child:before{content:""}.fa-arrow-circle-o-down:before{content:""}.fa-arrow-circle-o-up:before{content:""}.fa-inbox:before{content:""}.fa-play-circle-o:before{content:""}.fa-rotate-right:before,.fa-repeat:before{content:""}.fa-refresh:before{content:""}.fa-list-alt:before{content:""}.fa-lock:before{content:""}.fa-flag:before{content:""}.fa-headphones:before{content:""}.fa-volume-off:before{content:""}.fa-volume-down:before{content:""}.fa-volume-up:before{content:""}.fa-qrcode:before{content:""}.fa-barcode:before{content:""}.fa-tag:before{content:""}.fa-tags:before{content:""}.fa-book:before,.icon-book:before{content:""}.fa-bookmark:before{content:""}.fa-print:before{content:""}.fa-camera:before{content:""}.fa-font:before{content:""}.fa-bold:before{content:""}.fa-italic:before{content:""}.fa-text-height:before{content:""}.fa-text-width:before{content:""}.fa-align-left:before{content:""}.fa-align-center:before{content:""}.fa-align-right:before{content:""}.fa-align-justify:before{content:""}.fa-list:before{content:""}.fa-dedent:before,.fa-outdent:before{content:""}.fa-indent:before{content:""}.fa-video-camera:before{content:""}.fa-photo:before,.fa-image:before,.fa-picture-o:before{content:""}.fa-pencil:before{content:""}.fa-map-marker:before{content:""}.fa-adjust:before{content:""}.fa-tint:before{content:""}.fa-edit:before,.fa-pencil-square-o:before{content:""}.fa-share-square-o:before{content:""}.fa-check-square-o:before{content:""}.fa-arrows:before{content:""}.fa-step-backward:before{content:""}.fa-fast-backward:before{content:""}.fa-backward:before{content:""}.fa-play:before{content:""}.fa-pause:before{content:""}.fa-stop:before{content:""}.fa-forward:before{content:""}.fa-fast-forward:before{content:""}.fa-step-forward:before{content:""}.fa-eject:before{content:""}.fa-chevron-left:before{content:""}.fa-chevron-right:before{content:""}.fa-plus-circle:before{content:""}.fa-minus-circle:before{content:""}.fa-times-circle:before,.wy-inline-validate.wy-inline-validate-danger .wy-input-context:before{content:""}.fa-check-circle:before,.wy-inline-validate.wy-inline-validate-success .wy-input-context:before{content:""}.fa-question-circle:before{content:""}.fa-info-circle:before{content:""}.fa-crosshairs:before{content:""}.fa-times-circle-o:before{content:""}.fa-check-circle-o:before{content:""}.fa-ban:before{content:""}.fa-arrow-left:before{content:""}.fa-arrow-right:before{content:""}.fa-arrow-up:before{content:""}.fa-arrow-down:before{content:""}.fa-mail-forward:before,.fa-share:before{content:""}.fa-expand:before{content:""}.fa-compress:before{content:""}.fa-plus:before{content:""}.fa-minus:before{content:""}.fa-asterisk:before{content:""}.fa-exclamation-circle:before,.wy-inline-validate.wy-inline-validate-warning .wy-input-context:before,.wy-inline-validate.wy-inline-validate-info .wy-input-context:before,.rst-content .admonition-title:before{content:""}.fa-gift:before{content:""}.fa-leaf:before{content:""}.fa-fire:before,.icon-fire:before{content:""}.fa-eye:before{content:""}.fa-eye-slash:before{content:""}.fa-warning:before,.fa-exclamation-triangle:before{content:""}.fa-plane:before{content:""}.fa-calendar:before{content:""}.fa-random:before{content:""}.fa-comment:before{content:""}.fa-magnet:before{content:""}.fa-chevron-up:before{content:""}.fa-chevron-down:before{content:""}.fa-retweet:before{content:""}.fa-shopping-cart:before{content:""}.fa-folder:before{content:""}.fa-folder-open:before{content:""}.fa-arrows-v:before{content:""}.fa-arrows-h:before{content:""}.fa-bar-chart-o:before,.fa-bar-chart:before{content:""}.fa-twitter-square:before{content:""}.fa-facebook-square:before{content:""}.fa-camera-retro:before{content:""}.fa-key:before{content:""}.fa-gears:before,.fa-cogs:before{content:""}.fa-comments:before{content:""}.fa-thumbs-o-up:before{content:""}.fa-thumbs-o-down:before{content:""}.fa-star-half:before{content:""}.fa-heart-o:before{content:""}.fa-sign-out:before{content:""}.fa-linkedin-square:before{content:""}.fa-thumb-tack:before{content:""}.fa-external-link:before{content:""}.fa-sign-in:before{content:""}.fa-trophy:before{content:""}.fa-github-square:before{content:""}.fa-upload:before{content:""}.fa-lemon-o:before{content:""}.fa-phone:before{content:""}.fa-square-o:before{content:""}.fa-bookmark-o:before{content:""}.fa-phone-square:before{content:""}.fa-twitter:before{content:""}.fa-facebook:before{content:""}.fa-github:before,.icon-github:before{content:""}.fa-unlock:before{content:""}.fa-credit-card:before{content:""}.fa-rss:before{content:""}.fa-hdd-o:before{content:""}.fa-bullhorn:before{content:""}.fa-bell:before{content:""}.fa-certificate:before{content:""}.fa-hand-o-right:before{content:""}.fa-hand-o-left:before{content:""}.fa-hand-o-up:before{content:""}.fa-hand-o-down:before{content:""}.fa-arrow-circle-left:before,.icon-circle-arrow-left:before{content:""}.fa-arrow-circle-right:before,.icon-circle-arrow-right:before{content:""}.fa-arrow-circle-up:before{content:""}.fa-arrow-circle-down:before{content:""}.fa-globe:before{content:""}.fa-wrench:before{content:""}.fa-tasks:before{content:""}.fa-filter:before{content:""}.fa-briefcase:before{content:""}.fa-arrows-alt:before{content:""}.fa-group:before,.fa-users:before{content:""}.fa-chain:before,.fa-link:before,.icon-link:before{content:""}.fa-cloud:before{content:""}.fa-flask:before{content:""}.fa-cut:before,.fa-scissors:before{content:""}.fa-copy:before,.fa-files-o:before{content:""}.fa-paperclip:before{content:""}.fa-save:before,.fa-floppy-o:before{content:""}.fa-square:before{content:""}.fa-navicon:before,.fa-reorder:before,.fa-bars:before{content:""}.fa-list-ul:before{content:""}.fa-list-ol:before{content:""}.fa-strikethrough:before{content:""}.fa-underline:before{content:""}.fa-table:before{content:""}.fa-magic:before{content:""}.fa-truck:before{content:""}.fa-pinterest:before{content:""}.fa-pinterest-square:before{content:""}.fa-google-plus-square:before{content:""}.fa-google-plus:before{content:""}.fa-money:before{content:""}.fa-caret-down:before,.wy-dropdown .caret:before,.icon-caret-down:before{content:""}.fa-caret-up:before{content:""}.fa-caret-left:before{content:""}.fa-caret-right:before{content:""}.fa-columns:before{content:""}.fa-unsorted:before,.fa-sort:before{content:""}.fa-sort-down:before,.fa-sort-desc:before{content:""}.fa-sort-up:before,.fa-sort-asc:before{content:""}.fa-envelope:before{content:""}.fa-linkedin:before{content:""}.fa-rotate-left:before,.fa-undo:before{content:""}.fa-legal:before,.fa-gavel:before{content:""}.fa-dashboard:before,.fa-tachometer:before{content:""}.fa-comment-o:before{content:""}.fa-comments-o:before{content:""}.fa-flash:before,.fa-bolt:before{content:""}.fa-sitemap:before{content:""}.fa-umbrella:before{content:""}.fa-paste:before,.fa-clipboard:before{content:""}.fa-lightbulb-o:before{content:""}.fa-exchange:before{content:""}.fa-cloud-download:before{content:""}.fa-cloud-upload:before{content:""}.fa-user-md:before{content:""}.fa-stethoscope:before{content:""}.fa-suitcase:before{content:""}.fa-bell-o:before{content:""}.fa-coffee:before{content:""}.fa-cutlery:before{content:""}.fa-file-text-o:before{content:""}.fa-building-o:before{content:""}.fa-hospital-o:before{content:""}.fa-ambulance:before{content:""}.fa-medkit:before{content:""}.fa-fighter-jet:before{content:""}.fa-beer:before{content:""}.fa-h-square:before{content:""}.fa-plus-square:before{content:""}.fa-angle-double-left:before{content:""}.fa-angle-double-right:before{content:""}.fa-angle-double-up:before{content:""}.fa-angle-double-down:before{content:""}.fa-angle-left:before{content:""}.fa-angle-right:before{content:""}.fa-angle-up:before{content:""}.fa-angle-down:before{content:""}.fa-desktop:before{content:""}.fa-laptop:before{content:""}.fa-tablet:before{content:""}.fa-mobile-phone:before,.fa-mobile:before{content:""}.fa-circle-o:before{content:""}.fa-quote-left:before{content:""}.fa-quote-right:before{content:""}.fa-spinner:before{content:""}.fa-circle:before{content:""}.fa-mail-reply:before,.fa-reply:before{content:""}.fa-github-alt:before{content:""}.fa-folder-o:before{content:""}.fa-folder-open-o:before{content:""}.fa-smile-o:before{content:""}.fa-frown-o:before{content:""}.fa-meh-o:before{content:""}.fa-gamepad:before{content:""}.fa-keyboard-o:before{content:""}.fa-flag-o:before{content:""}.fa-flag-checkered:before{content:""}.fa-terminal:before{content:""}.fa-code:before{content:""}.fa-mail-reply-all:before,.fa-reply-all:before{content:""}.fa-star-half-empty:before,.fa-star-half-full:before,.fa-star-half-o:before{content:""}.fa-location-arrow:before{content:""}.fa-crop:before{content:""}.fa-code-fork:before{content:""}.fa-unlink:before,.fa-chain-broken:before{content:""}.fa-question:before{content:""}.fa-info:before{content:""}.fa-exclamation:before{content:""}.fa-superscript:before{content:""}.fa-subscript:before{content:""}.fa-eraser:before{content:""}.fa-puzzle-piece:before{content:""}.fa-microphone:before{content:""}.fa-microphone-slash:before{content:""}.fa-shield:before{content:""}.fa-calendar-o:before{content:""}.fa-fire-extinguisher:before{content:""}.fa-rocket:before{content:""}.fa-maxcdn:before{content:""}.fa-chevron-circle-left:before{content:""}.fa-chevron-circle-right:before{content:""}.fa-chevron-circle-up:before{content:""}.fa-chevron-circle-down:before{content:""}.fa-html5:before{content:""}.fa-css3:before{content:""}.fa-anchor:before{content:""}.fa-unlock-alt:before{content:""}.fa-bullseye:before{content:""}.fa-ellipsis-h:before{content:""}.fa-ellipsis-v:before{content:""}.fa-rss-square:before{content:""}.fa-play-circle:before{content:""}.fa-ticket:before{content:""}.fa-minus-square:before{content:""}.fa-minus-square-o:before,.wy-menu-vertical li.on a span.toctree-expand:before,.wy-menu-vertical li.current>a span.toctree-expand:before{content:""}.fa-level-up:before{content:""}.fa-level-down:before{content:""}.fa-check-square:before{content:""}.fa-pencil-square:before{content:""}.fa-external-link-square:before{content:""}.fa-share-square:before{content:""}.fa-compass:before{content:""}.fa-toggle-down:before,.fa-caret-square-o-down:before{content:""}.fa-toggle-up:before,.fa-caret-square-o-up:before{content:""}.fa-toggle-right:before,.fa-caret-square-o-right:before{content:""}.fa-euro:before,.fa-eur:before{content:""}.fa-gbp:before{content:""}.fa-dollar:before,.fa-usd:before{content:""}.fa-rupee:before,.fa-inr:before{content:""}.fa-cny:before,.fa-rmb:before,.fa-yen:before,.fa-jpy:before{content:""}.fa-ruble:before,.fa-rouble:before,.fa-rub:before{content:""}.fa-won:before,.fa-krw:before{content:""}.fa-bitcoin:before,.fa-btc:before{content:""}.fa-file:before{content:""}.fa-file-text:before{content:""}.fa-sort-alpha-asc:before{content:""}.fa-sort-alpha-desc:before{content:""}.fa-sort-amount-asc:before{content:""}.fa-sort-amount-desc:before{content:""}.fa-sort-numeric-asc:before{content:""}.fa-sort-numeric-desc:before{content:""}.fa-thumbs-up:before{content:""}.fa-thumbs-down:before{content:""}.fa-youtube-square:before{content:""}.fa-youtube:before{content:""}.fa-xing:before{content:""}.fa-xing-square:before{content:""}.fa-youtube-play:before{content:""}.fa-dropbox:before{content:""}.fa-stack-overflow:before{content:""}.fa-instagram:before{content:""}.fa-flickr:before{content:""}.fa-adn:before{content:""}.fa-bitbucket:before,.icon-bitbucket:before{content:""}.fa-bitbucket-square:before{content:""}.fa-tumblr:before{content:""}.fa-tumblr-square:before{content:""}.fa-long-arrow-down:before{content:""}.fa-long-arrow-up:before{content:""}.fa-long-arrow-left:before{content:""}.fa-long-arrow-right:before{content:""}.fa-apple:before{content:""}.fa-windows:before{content:""}.fa-android:before{content:""}.fa-linux:before{content:""}.fa-dribbble:before{content:""}.fa-skype:before{content:""}.fa-foursquare:before{content:""}.fa-trello:before{content:""}.fa-female:before{content:""}.fa-male:before{content:""}.fa-gittip:before{content:""}.fa-sun-o:before{content:""}.fa-moon-o:before{content:""}.fa-archive:before{content:""}.fa-bug:before{content:""}.fa-vk:before{content:""}.fa-weibo:before{content:""}.fa-renren:before{content:""}.fa-pagelines:before{content:""}.fa-stack-exchange:before{content:""}.fa-arrow-circle-o-right:before{content:""}.fa-arrow-circle-o-left:before{content:""}.fa-toggle-left:before,.fa-caret-square-o-left:before{content:""}.fa-dot-circle-o:before{content:""}.fa-wheelchair:before{content:""}.fa-vimeo-square:before{content:""}.fa-turkish-lira:before,.fa-try:before{content:""}.fa-plus-square-o:before,.wy-menu-vertical li span.toctree-expand:before{content:""}.fa-space-shuttle:before{content:""}.fa-slack:before{content:""}.fa-envelope-square:before{content:""}.fa-wordpress:before{content:""}.fa-openid:before{content:""}.fa-institution:before,.fa-bank:before,.fa-university:before{content:""}.fa-mortar-board:before,.fa-graduation-cap:before{content:""}.fa-yahoo:before{content:""}.fa-google:before{content:""}.fa-reddit:before{content:""}.fa-reddit-square:before{content:""}.fa-stumbleupon-circle:before{content:""}.fa-stumbleupon:before{content:""}.fa-delicious:before{content:""}.fa-digg:before{content:""}.fa-pied-piper:before{content:""}.fa-pied-piper-alt:before{content:""}.fa-drupal:before{content:""}.fa-joomla:before{content:""}.fa-language:before{content:""}.fa-fax:before{content:""}.fa-building:before{content:""}.fa-child:before{content:""}.fa-paw:before{content:""}.fa-spoon:before{content:""}.fa-cube:before{content:""}.fa-cubes:before{content:""}.fa-behance:before{content:""}.fa-behance-square:before{content:""}.fa-steam:before{content:""}.fa-steam-square:before{content:""}.fa-recycle:before{content:""}.fa-automobile:before,.fa-car:before{content:""}.fa-cab:before,.fa-taxi:before{content:""}.fa-tree:before{content:""}.fa-spotify:before{content:""}.fa-deviantart:before{content:""}.fa-soundcloud:before{content:""}.fa-database:before{content:""}.fa-file-pdf-o:before{content:""}.fa-file-word-o:before{content:""}.fa-file-excel-o:before{content:""}.fa-file-powerpoint-o:before{content:""}.fa-file-photo-o:before,.fa-file-picture-o:before,.fa-file-image-o:before{content:""}.fa-file-zip-o:before,.fa-file-archive-o:before{content:""}.fa-file-sound-o:before,.fa-file-audio-o:before{content:""}.fa-file-movie-o:before,.fa-file-video-o:before{content:""}.fa-file-code-o:before{content:""}.fa-vine:before{content:""}.fa-codepen:before{content:""}.fa-jsfiddle:before{content:""}.fa-life-bouy:before,.fa-life-buoy:before,.fa-life-saver:before,.fa-support:before,.fa-life-ring:before{content:""}.fa-circle-o-notch:before{content:""}.fa-ra:before,.fa-rebel:before{content:""}.fa-ge:before,.fa-empire:before{content:""}.fa-git-square:before{content:""}.fa-git:before{content:""}.fa-hacker-news:before{content:""}.fa-tencent-weibo:before{content:""}.fa-qq:before{content:""}.fa-wechat:before,.fa-weixin:before{content:""}.fa-send:before,.fa-paper-plane:before{content:""}.fa-send-o:before,.fa-paper-plane-o:before{content:""}.fa-history:before{content:""}.fa-circle-thin:before{content:""}.fa-header:before{content:""}.fa-paragraph:before{content:""}.fa-sliders:before{content:""}.fa-share-alt:before{content:""}.fa-share-alt-square:before{content:""}.fa-bomb:before{content:""}.fa-soccer-ball-o:before,.fa-futbol-o:before{content:""}.fa-tty:before{content:""}.fa-binoculars:before{content:""}.fa-plug:before{content:""}.fa-slideshare:before{content:""}.fa-twitch:before{content:""}.fa-yelp:before{content:""}.fa-newspaper-o:before{content:""}.fa-wifi:before{content:""}.fa-calculator:before{content:""}.fa-paypal:before{content:""}.fa-google-wallet:before{content:""}.fa-cc-visa:before{content:""}.fa-cc-mastercard:before{content:""}.fa-cc-discover:before{content:""}.fa-cc-amex:before{content:""}.fa-cc-paypal:before{content:""}.fa-cc-stripe:before{content:""}.fa-bell-slash:before{content:""}.fa-bell-slash-o:before{content:""}.fa-trash:before{content:""}.fa-copyright:before{content:""}.fa-at:before{content:""}.fa-eyedropper:before{content:""}.fa-paint-brush:before{content:""}.fa-birthday-cake:before{content:""}.fa-area-chart:before{content:""}.fa-pie-chart:before{content:""}.fa-line-chart:before{content:""}.fa-lastfm:before{content:""}.fa-lastfm-square:before{content:""}.fa-toggle-off:before{content:""}.fa-toggle-on:before{content:""}.fa-bicycle:before{content:""}.fa-bus:before{content:""}.fa-ioxhost:before{content:""}.fa-angellist:before{content:""}.fa-cc:before{content:""}.fa-shekel:before,.fa-sheqel:before,.fa-ils:before{content:""}.fa-meanpath:before{content:""}.fa,.wy-menu-vertical li span.toctree-expand,.wy-menu-vertical li.on a span.toctree-expand,.wy-menu-vertical li.current>a span.toctree-expand,.rst-content .admonition-title,.rst-content h1 .headerlink,.rst-content h2 .headerlink,.rst-content p.caption .headerlink,.rst-content h3 .headerlink,.rst-content h4 .headerlink,.rst-content h5 .headerlink,.rst-content h6 .headerlink,.rst-content dl dt .headerlink,.rst-content tt.download span:first-child,.rst-content code.download span:first-child,.icon,.wy-dropdown .caret,.wy-inline-validate.wy-inline-validate-success .wy-input-context,.wy-inline-validate.wy-inline-validate-danger .wy-input-context,.wy-inline-validate.wy-inline-validate-warning .wy-input-context,.wy-inline-validate.wy-inline-validate-info .wy-input-context{font-family:inherit}.fa:before,.wy-menu-vertical li span.toctree-expand:before,.wy-menu-vertical li.on a span.toctree-expand:before,.wy-menu-vertical li.current>a span.toctree-expand:before,.rst-content .admonition-title:before,.rst-content h1 .headerlink:before,.rst-content h2 .headerlink:before,.rst-content p.caption .headerlink:before,.rst-content h3 .headerlink:before,.rst-content h4 .headerlink:before,.rst-content h5 .headerlink:before,.rst-content h6 .headerlink:before,.rst-content dl dt .headerlink:before,.rst-content tt.download span:first-child:before,.rst-content code.download span:first-child:before,.icon:before,.wy-dropdown .caret:before,.wy-inline-validate.wy-inline-validate-success .wy-input-context:before,.wy-inline-validate.wy-inline-validate-danger .wy-input-context:before,.wy-inline-validate.wy-inline-validate-warning .wy-input-context:before,.wy-inline-validate.wy-inline-validate-info .wy-input-context:before{font-family:"FontAwesome";display:inline-block;font-style:normal;font-weight:normal;line-height:1;text-decoration:inherit}a .fa,a .wy-menu-vertical li span.toctree-expand,.wy-menu-vertical li a span.toctree-expand,.wy-menu-vertical li.on a span.toctree-expand,.wy-menu-vertical li.current>a span.toctree-expand,a .rst-content .admonition-title,.rst-content a .admonition-title,a .rst-content h1 .headerlink,.rst-content h1 a .headerlink,a .rst-content h2 .headerlink,.rst-content h2 a .headerlink,a .rst-content p.caption .headerlink,.rst-content p.caption a .headerlink,a .rst-content h3 .headerlink,.rst-content h3 a .headerlink,a .rst-content h4 .headerlink,.rst-content h4 a .headerlink,a .rst-content h5 .headerlink,.rst-content h5 a .headerlink,a .rst-content h6 .headerlink,.rst-content h6 a .headerlink,a .rst-content dl dt .headerlink,.rst-content dl dt a .headerlink,a .rst-content tt.download span:first-child,.rst-content tt.download a span:first-child,a .rst-content code.download span:first-child,.rst-content code.download a span:first-child,a .icon{display:inline-block;text-decoration:inherit}.btn .fa,.btn .wy-menu-vertical li span.toctree-expand,.wy-menu-vertical li .btn span.toctree-expand,.btn .wy-menu-vertical li.on a span.toctree-expand,.wy-menu-vertical li.on a .btn span.toctree-expand,.btn .wy-menu-vertical li.current>a span.toctree-expand,.wy-menu-vertical li.current>a .btn span.toctree-expand,.btn .rst-content .admonition-title,.rst-content .btn .admonition-title,.btn .rst-content h1 .headerlink,.rst-content h1 .btn .headerlink,.btn .rst-content h2 .headerlink,.rst-content h2 .btn .headerlink,.btn .rst-content p.caption .headerlink,.rst-content p.caption .btn .headerlink,.btn .rst-content h3 .headerlink,.rst-content h3 .btn .headerlink,.btn .rst-content h4 .headerlink,.rst-content h4 .btn .headerlink,.btn .rst-content h5 .headerlink,.rst-content h5 .btn .headerlink,.btn .rst-content h6 .headerlink,.rst-content h6 .btn .headerlink,.btn .rst-content dl dt .headerlink,.rst-content dl dt .btn .headerlink,.btn .rst-content tt.download span:first-child,.rst-content tt.download .btn span:first-child,.btn .rst-content code.download span:first-child,.rst-content code.download .btn span:first-child,.btn .icon,.nav .fa,.nav .wy-menu-vertical li span.toctree-expand,.wy-menu-vertical li .nav span.toctree-expand,.nav .wy-menu-vertical li.on a span.toctree-expand,.wy-menu-vertical li.on a .nav span.toctree-expand,.nav .wy-menu-vertical li.current>a span.toctree-expand,.wy-menu-vertical li.current>a .nav span.toctree-expand,.nav .rst-content .admonition-title,.rst-content .nav .admonition-title,.nav .rst-content h1 .headerlink,.rst-content h1 .nav .headerlink,.nav .rst-content h2 .headerlink,.rst-content h2 .nav .headerlink,.nav .rst-content p.caption .headerlink,.rst-content p.caption .nav .headerlink,.nav .rst-content h3 .headerlink,.rst-content h3 .nav .headerlink,.nav .rst-content h4 .headerlink,.rst-content h4 .nav .headerlink,.nav .rst-content h5 .headerlink,.rst-content h5 .nav .headerlink,.nav .rst-content h6 .headerlink,.rst-content h6 .nav .headerlink,.nav .rst-content dl dt .headerlink,.rst-content dl dt .nav .headerlink,.nav .rst-content tt.download span:first-child,.rst-content tt.download .nav span:first-child,.nav .rst-content code.download span:first-child,.rst-content code.download .nav span:first-child,.nav .icon{display:inline}.btn .fa.fa-large,.btn .wy-menu-vertical li span.fa-large.toctree-expand,.wy-menu-vertical li .btn span.fa-large.toctree-expand,.btn .rst-content .fa-large.admonition-title,.rst-content .btn .fa-large.admonition-title,.btn .rst-content h1 .fa-large.headerlink,.rst-content h1 .btn .fa-large.headerlink,.btn .rst-content h2 .fa-large.headerlink,.rst-content h2 .btn .fa-large.headerlink,.btn .rst-content p.caption .fa-large.headerlink,.rst-content p.caption .btn .fa-large.headerlink,.btn .rst-content h3 .fa-large.headerlink,.rst-content h3 .btn .fa-large.headerlink,.btn .rst-content h4 .fa-large.headerlink,.rst-content h4 .btn .fa-large.headerlink,.btn .rst-content h5 .fa-large.headerlink,.rst-content h5 .btn .fa-large.headerlink,.btn .rst-content h6 .fa-large.headerlink,.rst-content h6 .btn .fa-large.headerlink,.btn .rst-content dl dt .fa-large.headerlink,.rst-content dl dt .btn .fa-large.headerlink,.btn .rst-content tt.download span.fa-large:first-child,.rst-content tt.download .btn span.fa-large:first-child,.btn .rst-content code.download span.fa-large:first-child,.rst-content code.download .btn span.fa-large:first-child,.btn .fa-large.icon,.nav .fa.fa-large,.nav .wy-menu-vertical li span.fa-large.toctree-expand,.wy-menu-vertical li .nav span.fa-large.toctree-expand,.nav .rst-content .fa-large.admonition-title,.rst-content .nav .fa-large.admonition-title,.nav .rst-content h1 .fa-large.headerlink,.rst-content h1 .nav .fa-large.headerlink,.nav .rst-content h2 .fa-large.headerlink,.rst-content h2 .nav .fa-large.headerlink,.nav .rst-content p.caption .fa-large.headerlink,.rst-content p.caption .nav .fa-large.headerlink,.nav .rst-content h3 .fa-large.headerlink,.rst-content h3 .nav .fa-large.headerlink,.nav .rst-content h4 .fa-large.headerlink,.rst-content h4 .nav .fa-large.headerlink,.nav .rst-content h5 .fa-large.headerlink,.rst-content h5 .nav .fa-large.headerlink,.nav .rst-content h6 .fa-large.headerlink,.rst-content h6 .nav .fa-large.headerlink,.nav .rst-content dl dt .fa-large.headerlink,.rst-content dl dt .nav .fa-large.headerlink,.nav .rst-content tt.download span.fa-large:first-child,.rst-content tt.download .nav span.fa-large:first-child,.nav .rst-content code.download span.fa-large:first-child,.rst-content code.download .nav span.fa-large:first-child,.nav .fa-large.icon{line-height:0.9em}.btn .fa.fa-spin,.btn .wy-menu-vertical li span.fa-spin.toctree-expand,.wy-menu-vertical li .btn span.fa-spin.toctree-expand,.btn .rst-content .fa-spin.admonition-title,.rst-content .btn .fa-spin.admonition-title,.btn .rst-content h1 .fa-spin.headerlink,.rst-content h1 .btn .fa-spin.headerlink,.btn .rst-content h2 .fa-spin.headerlink,.rst-content h2 .btn .fa-spin.headerlink,.btn .rst-content p.caption .fa-spin.headerlink,.rst-content p.caption .btn .fa-spin.headerlink,.btn .rst-content h3 .fa-spin.headerlink,.rst-content h3 .btn .fa-spin.headerlink,.btn .rst-content h4 .fa-spin.headerlink,.rst-content h4 .btn .fa-spin.headerlink,.btn .rst-content h5 .fa-spin.headerlink,.rst-content h5 .btn .fa-spin.headerlink,.btn .rst-content h6 .fa-spin.headerlink,.rst-content h6 .btn .fa-spin.headerlink,.btn .rst-content dl dt .fa-spin.headerlink,.rst-content dl dt .btn .fa-spin.headerlink,.btn .rst-content tt.download span.fa-spin:first-child,.rst-content tt.download .btn span.fa-spin:first-child,.btn .rst-content code.download span.fa-spin:first-child,.rst-content code.download .btn span.fa-spin:first-child,.btn .fa-spin.icon,.nav .fa.fa-spin,.nav .wy-menu-vertical li span.fa-spin.toctree-expand,.wy-menu-vertical li .nav span.fa-spin.toctree-expand,.nav .rst-content .fa-spin.admonition-title,.rst-content .nav .fa-spin.admonition-title,.nav .rst-content h1 .fa-spin.headerlink,.rst-content h1 .nav .fa-spin.headerlink,.nav .rst-content h2 .fa-spin.headerlink,.rst-content h2 .nav .fa-spin.headerlink,.nav .rst-content p.caption .fa-spin.headerlink,.rst-content p.caption .nav .fa-spin.headerlink,.nav .rst-content h3 .fa-spin.headerlink,.rst-content h3 .nav .fa-spin.headerlink,.nav .rst-content h4 .fa-spin.headerlink,.rst-content h4 .nav .fa-spin.headerlink,.nav .rst-content h5 .fa-spin.headerlink,.rst-content h5 .nav .fa-spin.headerlink,.nav .rst-content h6 .fa-spin.headerlink,.rst-content h6 .nav .fa-spin.headerlink,.nav .rst-content dl dt .fa-spin.headerlink,.rst-content dl dt .nav .fa-spin.headerlink,.nav .rst-content tt.download span.fa-spin:first-child,.rst-content tt.download .nav span.fa-spin:first-child,.nav .rst-content code.download span.fa-spin:first-child,.rst-content code.download .nav span.fa-spin:first-child,.nav .fa-spin.icon{display:inline-block}.btn.fa:before,.wy-menu-vertical li span.btn.toctree-expand:before,.rst-content .btn.admonition-title:before,.rst-content h1 .btn.headerlink:before,.rst-content h2 .btn.headerlink:before,.rst-content p.caption .btn.headerlink:before,.rst-content h3 .btn.headerlink:before,.rst-content h4 .btn.headerlink:before,.rst-content h5 .btn.headerlink:before,.rst-content h6 .btn.headerlink:before,.rst-content dl dt .btn.headerlink:before,.rst-content tt.download span.btn:first-child:before,.rst-content code.download span.btn:first-child:before,.btn.icon:before{opacity:0.5;-webkit-transition:opacity 0.05s ease-in;-moz-transition:opacity 0.05s ease-in;transition:opacity 0.05s ease-in}.btn.fa:hover:before,.wy-menu-vertical li span.btn.toctree-expand:hover:before,.rst-content .btn.admonition-title:hover:before,.rst-content h1 .btn.headerlink:hover:before,.rst-content h2 .btn.headerlink:hover:before,.rst-content p.caption .btn.headerlink:hover:before,.rst-content h3 .btn.headerlink:hover:before,.rst-content h4 .btn.headerlink:hover:before,.rst-content h5 .btn.headerlink:hover:before,.rst-content h6 .btn.headerlink:hover:before,.rst-content dl dt .btn.headerlink:hover:before,.rst-content tt.download span.btn:first-child:hover:before,.rst-content code.download span.btn:first-child:hover:before,.btn.icon:hover:before{opacity:1}.btn-mini .fa:before,.btn-mini .wy-menu-vertical li span.toctree-expand:before,.wy-menu-vertical li .btn-mini span.toctree-expand:before,.btn-mini .rst-content .admonition-title:before,.rst-content .btn-mini .admonition-title:before,.btn-mini .rst-content h1 .headerlink:before,.rst-content h1 .btn-mini .headerlink:before,.btn-mini .rst-content h2 .headerlink:before,.rst-content h2 .btn-mini .headerlink:before,.btn-mini .rst-content p.caption .headerlink:before,.rst-content p.caption .btn-mini .headerlink:before,.btn-mini .rst-content h3 .headerlink:before,.rst-content h3 .btn-mini .headerlink:before,.btn-mini .rst-content h4 .headerlink:before,.rst-content h4 .btn-mini .headerlink:before,.btn-mini .rst-content h5 .headerlink:before,.rst-content h5 .btn-mini .headerlink:before,.btn-mini .rst-content h6 .headerlink:before,.rst-content h6 .btn-mini .headerlink:before,.btn-mini .rst-content dl dt .headerlink:before,.rst-content dl dt .btn-mini .headerlink:before,.btn-mini .rst-content tt.download span:first-child:before,.rst-content tt.download .btn-mini span:first-child:before,.btn-mini .rst-content code.download span:first-child:before,.rst-content code.download .btn-mini span:first-child:before,.btn-mini .icon:before{font-size:14px;vertical-align:-15%}.wy-alert,.rst-content .note,.rst-content .attention,.rst-content .caution,.rst-content .danger,.rst-content .error,.rst-content .hint,.rst-content .important,.rst-content .tip,.rst-content .warning,.rst-content .seealso,.rst-content .admonition-todo{padding:12px;line-height:24px;margin-bottom:24px;background:#e7f2fa}.wy-alert-title,.rst-content .admonition-title{color:#fff;font-weight:bold;display:block;color:#fff;background:#6ab0de;margin:-12px;padding:6px 12px;margin-bottom:12px}.wy-alert.wy-alert-danger,.rst-content .wy-alert-danger.note,.rst-content .wy-alert-danger.attention,.rst-content .wy-alert-danger.caution,.rst-content .danger,.rst-content .error,.rst-content .wy-alert-danger.hint,.rst-content .wy-alert-danger.important,.rst-content .wy-alert-danger.tip,.rst-content .wy-alert-danger.warning,.rst-content .wy-alert-danger.seealso,.rst-content .wy-alert-danger.admonition-todo{background:#fdf3f2}.wy-alert.wy-alert-danger .wy-alert-title,.rst-content .wy-alert-danger.note .wy-alert-title,.rst-content .wy-alert-danger.attention .wy-alert-title,.rst-content .wy-alert-danger.caution .wy-alert-title,.rst-content .danger .wy-alert-title,.rst-content .error .wy-alert-title,.rst-content .wy-alert-danger.hint .wy-alert-title,.rst-content .wy-alert-danger.important .wy-alert-title,.rst-content .wy-alert-danger.tip .wy-alert-title,.rst-content .wy-alert-danger.warning .wy-alert-title,.rst-content .wy-alert-danger.seealso .wy-alert-title,.rst-content .wy-alert-danger.admonition-todo .wy-alert-title,.wy-alert.wy-alert-danger .rst-content .admonition-title,.rst-content .wy-alert.wy-alert-danger .admonition-title,.rst-content .wy-alert-danger.note .admonition-title,.rst-content .wy-alert-danger.attention .admonition-title,.rst-content .wy-alert-danger.caution .admonition-title,.rst-content .danger .admonition-title,.rst-content .error .admonition-title,.rst-content .wy-alert-danger.hint .admonition-title,.rst-content .wy-alert-danger.important .admonition-title,.rst-content .wy-alert-danger.tip .admonition-title,.rst-content .wy-alert-danger.warning .admonition-title,.rst-content .wy-alert-danger.seealso .admonition-title,.rst-content .wy-alert-danger.admonition-todo .admonition-title{background:#f29f97}.wy-alert.wy-alert-warning,.rst-content .wy-alert-warning.note,.rst-content .attention,.rst-content .caution,.rst-content .wy-alert-warning.danger,.rst-content .wy-alert-warning.error,.rst-content .wy-alert-warning.hint,.rst-content .wy-alert-warning.important,.rst-content .wy-alert-warning.tip,.rst-content .warning,.rst-content .wy-alert-warning.seealso,.rst-content .admonition-todo{background:#ffedcc}.wy-alert.wy-alert-warning .wy-alert-title,.rst-content .wy-alert-warning.note .wy-alert-title,.rst-content .attention .wy-alert-title,.rst-content .caution .wy-alert-title,.rst-content .wy-alert-warning.danger .wy-alert-title,.rst-content .wy-alert-warning.error .wy-alert-title,.rst-content .wy-alert-warning.hint .wy-alert-title,.rst-content .wy-alert-warning.important .wy-alert-title,.rst-content .wy-alert-warning.tip .wy-alert-title,.rst-content .warning .wy-alert-title,.rst-content .wy-alert-warning.seealso .wy-alert-title,.rst-content .admonition-todo .wy-alert-title,.wy-alert.wy-alert-warning .rst-content .admonition-title,.rst-content .wy-alert.wy-alert-warning .admonition-title,.rst-content .wy-alert-warning.note .admonition-title,.rst-content .attention .admonition-title,.rst-content .caution .admonition-title,.rst-content .wy-alert-warning.danger .admonition-title,.rst-content .wy-alert-warning.error .admonition-title,.rst-content .wy-alert-warning.hint .admonition-title,.rst-content .wy-alert-warning.important .admonition-title,.rst-content .wy-alert-warning.tip .admonition-title,.rst-content .warning .admonition-title,.rst-content .wy-alert-warning.seealso .admonition-title,.rst-content .admonition-todo .admonition-title{background:#f0b37e}.wy-alert.wy-alert-info,.rst-content .note,.rst-content .wy-alert-info.attention,.rst-content .wy-alert-info.caution,.rst-content .wy-alert-info.danger,.rst-content .wy-alert-info.error,.rst-content .wy-alert-info.hint,.rst-content .wy-alert-info.important,.rst-content .wy-alert-info.tip,.rst-content .wy-alert-info.warning,.rst-content .seealso,.rst-content .wy-alert-info.admonition-todo{background:#e7f2fa}.wy-alert.wy-alert-info .wy-alert-title,.rst-content .note .wy-alert-title,.rst-content .wy-alert-info.attention .wy-alert-title,.rst-content .wy-alert-info.caution .wy-alert-title,.rst-content .wy-alert-info.danger .wy-alert-title,.rst-content .wy-alert-info.error .wy-alert-title,.rst-content .wy-alert-info.hint .wy-alert-title,.rst-content .wy-alert-info.important .wy-alert-title,.rst-content .wy-alert-info.tip .wy-alert-title,.rst-content .wy-alert-info.warning .wy-alert-title,.rst-content .seealso .wy-alert-title,.rst-content .wy-alert-info.admonition-todo .wy-alert-title,.wy-alert.wy-alert-info .rst-content .admonition-title,.rst-content .wy-alert.wy-alert-info .admonition-title,.rst-content .note .admonition-title,.rst-content .wy-alert-info.attention .admonition-title,.rst-content .wy-alert-info.caution .admonition-title,.rst-content .wy-alert-info.danger .admonition-title,.rst-content .wy-alert-info.error .admonition-title,.rst-content .wy-alert-info.hint .admonition-title,.rst-content .wy-alert-info.important .admonition-title,.rst-content .wy-alert-info.tip .admonition-title,.rst-content .wy-alert-info.warning .admonition-title,.rst-content .seealso .admonition-title,.rst-content .wy-alert-info.admonition-todo .admonition-title{background:#6ab0de}.wy-alert.wy-alert-success,.rst-content .wy-alert-success.note,.rst-content .wy-alert-success.attention,.rst-content .wy-alert-success.caution,.rst-content .wy-alert-success.danger,.rst-content .wy-alert-success.error,.rst-content .hint,.rst-content .important,.rst-content .tip,.rst-content .wy-alert-success.warning,.rst-content .wy-alert-success.seealso,.rst-content .wy-alert-success.admonition-todo{background:#dbfaf4}.wy-alert.wy-alert-success .wy-alert-title,.rst-content .wy-alert-success.note .wy-alert-title,.rst-content .wy-alert-success.attention .wy-alert-title,.rst-content .wy-alert-success.caution .wy-alert-title,.rst-content .wy-alert-success.danger .wy-alert-title,.rst-content .wy-alert-success.error .wy-alert-title,.rst-content .hint .wy-alert-title,.rst-content .important .wy-alert-title,.rst-content .tip .wy-alert-title,.rst-content .wy-alert-success.warning .wy-alert-title,.rst-content .wy-alert-success.seealso .wy-alert-title,.rst-content .wy-alert-success.admonition-todo .wy-alert-title,.wy-alert.wy-alert-success .rst-content .admonition-title,.rst-content .wy-alert.wy-alert-success .admonition-title,.rst-content .wy-alert-success.note .admonition-title,.rst-content .wy-alert-success.attention .admonition-title,.rst-content .wy-alert-success.caution .admonition-title,.rst-content .wy-alert-success.danger .admonition-title,.rst-content .wy-alert-success.error .admonition-title,.rst-content .hint .admonition-title,.rst-content .important .admonition-title,.rst-content .tip .admonition-title,.rst-content .wy-alert-success.warning .admonition-title,.rst-content .wy-alert-success.seealso .admonition-title,.rst-content .wy-alert-success.admonition-todo .admonition-title{background:#1abc9c}.wy-alert.wy-alert-neutral,.rst-content .wy-alert-neutral.note,.rst-content .wy-alert-neutral.attention,.rst-content .wy-alert-neutral.caution,.rst-content .wy-alert-neutral.danger,.rst-content .wy-alert-neutral.error,.rst-content .wy-alert-neutral.hint,.rst-content .wy-alert-neutral.important,.rst-content .wy-alert-neutral.tip,.rst-content .wy-alert-neutral.warning,.rst-content .wy-alert-neutral.seealso,.rst-content .wy-alert-neutral.admonition-todo{background:#f3f6f6}.wy-alert.wy-alert-neutral .wy-alert-title,.rst-content .wy-alert-neutral.note .wy-alert-title,.rst-content .wy-alert-neutral.attention .wy-alert-title,.rst-content .wy-alert-neutral.caution .wy-alert-title,.rst-content .wy-alert-neutral.danger .wy-alert-title,.rst-content .wy-alert-neutral.error .wy-alert-title,.rst-content .wy-alert-neutral.hint .wy-alert-title,.rst-content .wy-alert-neutral.important .wy-alert-title,.rst-content .wy-alert-neutral.tip .wy-alert-title,.rst-content .wy-alert-neutral.warning .wy-alert-title,.rst-content .wy-alert-neutral.seealso .wy-alert-title,.rst-content .wy-alert-neutral.admonition-todo .wy-alert-title,.wy-alert.wy-alert-neutral .rst-content .admonition-title,.rst-content .wy-alert.wy-alert-neutral .admonition-title,.rst-content .wy-alert-neutral.note .admonition-title,.rst-content .wy-alert-neutral.attention .admonition-title,.rst-content .wy-alert-neutral.caution .admonition-title,.rst-content .wy-alert-neutral.danger .admonition-title,.rst-content .wy-alert-neutral.error .admonition-title,.rst-content .wy-alert-neutral.hint .admonition-title,.rst-content .wy-alert-neutral.important .admonition-title,.rst-content .wy-alert-neutral.tip .admonition-title,.rst-content .wy-alert-neutral.warning .admonition-title,.rst-content .wy-alert-neutral.seealso .admonition-title,.rst-content .wy-alert-neutral.admonition-todo .admonition-title{color:#404040;background:#e1e4e5}.wy-alert.wy-alert-neutral a,.rst-content .wy-alert-neutral.note a,.rst-content .wy-alert-neutral.attention a,.rst-content .wy-alert-neutral.caution a,.rst-content .wy-alert-neutral.danger a,.rst-content .wy-alert-neutral.error a,.rst-content .wy-alert-neutral.hint a,.rst-content .wy-alert-neutral.important a,.rst-content .wy-alert-neutral.tip a,.rst-content .wy-alert-neutral.warning a,.rst-content .wy-alert-neutral.seealso a,.rst-content .wy-alert-neutral.admonition-todo a{color:#2980B9}.wy-alert p:last-child,.rst-content .note p:last-child,.rst-content .attention p:last-child,.rst-content .caution p:last-child,.rst-content .danger p:last-child,.rst-content .error p:last-child,.rst-content .hint p:last-child,.rst-content .important p:last-child,.rst-content .tip p:last-child,.rst-content .warning p:last-child,.rst-content .seealso p:last-child,.rst-content .admonition-todo p:last-child{margin-bottom:0}.wy-tray-container{position:fixed;bottom:0px;left:0;z-index:600}.wy-tray-container li{display:block;width:300px;background:transparent;color:#fff;text-align:center;box-shadow:0 5px 5px 0 rgba(0,0,0,0.1);padding:0 24px;min-width:20%;opacity:0;height:0;line-height:56px;overflow:hidden;-webkit-transition:all 0.3s ease-in;-moz-transition:all 0.3s ease-in;transition:all 0.3s ease-in}.wy-tray-container li.wy-tray-item-success{background:#27AE60}.wy-tray-container li.wy-tray-item-info{background:#2980B9}.wy-tray-container li.wy-tray-item-warning{background:#E67E22}.wy-tray-container li.wy-tray-item-danger{background:#E74C3C}.wy-tray-container li.on{opacity:1;height:56px}@media screen and (max-width: 768px){.wy-tray-container{bottom:auto;top:0;width:100%}.wy-tray-container li{width:100%}}button{font-size:100%;margin:0;vertical-align:baseline;*vertical-align:middle;cursor:pointer;line-height:normal;-webkit-appearance:button;*overflow:visible}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}button[disabled]{cursor:default}.btn{display:inline-block;border-radius:2px;line-height:normal;white-space:nowrap;text-align:center;cursor:pointer;font-size:100%;padding:6px 12px 8px 12px;color:#fff;border:1px solid rgba(0,0,0,0.1);background-color:#27AE60;text-decoration:none;font-weight:normal;font-family:"Lato","proxima-nova","Helvetica Neue",Arial,sans-serif;box-shadow:0px 1px 2px -1px rgba(255,255,255,0.5) inset,0px -2px 0px 0px rgba(0,0,0,0.1) inset;outline-none:false;vertical-align:middle;*display:inline;zoom:1;-webkit-user-drag:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-webkit-transition:all 0.1s linear;-moz-transition:all 0.1s linear;transition:all 0.1s linear}.btn-hover{background:#2e8ece;color:#fff}.btn:hover{background:#2cc36b;color:#fff}.btn:focus{background:#2cc36b;outline:0}.btn:active{box-shadow:0px -1px 0px 0px rgba(0,0,0,0.05) inset,0px 2px 0px 0px rgba(0,0,0,0.1) inset;padding:8px 12px 6px 12px}.btn:visited{color:#fff}.btn:disabled{background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);filter:alpha(opacity=40);opacity:0.4;cursor:not-allowed;box-shadow:none}.btn-disabled{background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);filter:alpha(opacity=40);opacity:0.4;cursor:not-allowed;box-shadow:none}.btn-disabled:hover,.btn-disabled:focus,.btn-disabled:active{background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);filter:alpha(opacity=40);opacity:0.4;cursor:not-allowed;box-shadow:none}.btn::-moz-focus-inner{padding:0;border:0}.btn-small{font-size:80%}.btn-info{background-color:#2980B9 !important}.btn-info:hover{background-color:#2e8ece !important}.btn-neutral{background-color:#f3f6f6 !important;color:#404040 !important}.btn-neutral:hover{background-color:#e5ebeb !important;color:#404040}.btn-neutral:visited{color:#404040 !important}.btn-success{background-color:#27AE60 !important}.btn-success:hover{background-color:#295 !important}.btn-danger{background-color:#E74C3C !important}.btn-danger:hover{background-color:#ea6153 !important}.btn-warning{background-color:#E67E22 !important}.btn-warning:hover{background-color:#e98b39 !important}.btn-invert{background-color:#222}.btn-invert:hover{background-color:#2f2f2f !important}.btn-link{background-color:transparent !important;color:#2980B9;box-shadow:none;border-color:transparent !important}.btn-link:hover{background-color:transparent !important;color:#409ad5 !important;box-shadow:none}.btn-link:active{background-color:transparent !important;color:#409ad5 !important;box-shadow:none}.btn-link:visited{color:#9B59B6}.wy-btn-group .btn,.wy-control .btn{vertical-align:middle}.wy-btn-group{margin-bottom:24px;*zoom:1}.wy-btn-group:before,.wy-btn-group:after{display:table;content:""}.wy-btn-group:after{clear:both}.wy-dropdown{position:relative;display:inline-block}.wy-dropdown-active .wy-dropdown-menu{display:block}.wy-dropdown-menu{position:absolute;left:0;display:none;float:left;top:100%;min-width:100%;background:#fcfcfc;z-index:100;border:solid 1px #cfd7dd;box-shadow:0 2px 2px 0 rgba(0,0,0,0.1);padding:12px}.wy-dropdown-menu>dd>a{display:block;clear:both;color:#404040;white-space:nowrap;font-size:90%;padding:0 12px;cursor:pointer}.wy-dropdown-menu>dd>a:hover{background:#2980B9;color:#fff}.wy-dropdown-menu>dd.divider{border-top:solid 1px #cfd7dd;margin:6px 0}.wy-dropdown-menu>dd.search{padding-bottom:12px}.wy-dropdown-menu>dd.search input[type="search"]{width:100%}.wy-dropdown-menu>dd.call-to-action{background:#e3e3e3;text-transform:uppercase;font-weight:500;font-size:80%}.wy-dropdown-menu>dd.call-to-action:hover{background:#e3e3e3}.wy-dropdown-menu>dd.call-to-action .btn{color:#fff}.wy-dropdown.wy-dropdown-up .wy-dropdown-menu{bottom:100%;top:auto;left:auto;right:0}.wy-dropdown.wy-dropdown-bubble .wy-dropdown-menu{background:#fcfcfc;margin-top:2px}.wy-dropdown.wy-dropdown-bubble .wy-dropdown-menu a{padding:6px 12px}.wy-dropdown.wy-dropdown-bubble .wy-dropdown-menu a:hover{background:#2980B9;color:#fff}.wy-dropdown.wy-dropdown-left .wy-dropdown-menu{right:0;left:auto;text-align:right}.wy-dropdown-arrow:before{content:" ";border-bottom:5px solid #f5f5f5;border-left:5px solid transparent;border-right:5px solid transparent;position:absolute;display:block;top:-4px;left:50%;margin-left:-3px}.wy-dropdown-arrow.wy-dropdown-arrow-left:before{left:11px}.wy-form-stacked select{display:block}.wy-form-aligned input,.wy-form-aligned textarea,.wy-form-aligned select,.wy-form-aligned .wy-help-inline,.wy-form-aligned label{display:inline-block;*display:inline;*zoom:1;vertical-align:middle}.wy-form-aligned .wy-control-group>label{display:inline-block;vertical-align:middle;width:10em;margin:6px 12px 0 0;float:left}.wy-form-aligned .wy-control{float:left}.wy-form-aligned .wy-control label{display:block}.wy-form-aligned .wy-control select{margin-top:6px}fieldset{border:0;margin:0;padding:0}legend{display:block;width:100%;border:0;padding:0;white-space:normal;margin-bottom:24px;font-size:150%;*margin-left:-7px}label{display:block;margin:0 0 0.3125em 0;color:#333;font-size:90%}input,select,textarea{font-size:100%;margin:0;vertical-align:baseline;*vertical-align:middle}.wy-control-group{margin-bottom:24px;*zoom:1;max-width:68em;margin-left:auto;margin-right:auto;*zoom:1}.wy-control-group:before,.wy-control-group:after{display:table;content:""}.wy-control-group:after{clear:both}.wy-control-group:before,.wy-control-group:after{display:table;content:""}.wy-control-group:after{clear:both}.wy-control-group.wy-control-group-required>label:after{content:" *";color:#E74C3C}.wy-control-group .wy-form-full,.wy-control-group .wy-form-halves,.wy-control-group .wy-form-thirds{padding-bottom:12px}.wy-control-group .wy-form-full select,.wy-control-group .wy-form-halves select,.wy-control-group .wy-form-thirds select{width:100%}.wy-control-group .wy-form-full input[type="text"],.wy-control-group .wy-form-full input[type="password"],.wy-control-group .wy-form-full input[type="email"],.wy-control-group .wy-form-full input[type="url"],.wy-control-group .wy-form-full input[type="date"],.wy-control-group .wy-form-full input[type="month"],.wy-control-group .wy-form-full input[type="time"],.wy-control-group .wy-form-full input[type="datetime"],.wy-control-group .wy-form-full input[type="datetime-local"],.wy-control-group .wy-form-full input[type="week"],.wy-control-group .wy-form-full input[type="number"],.wy-control-group .wy-form-full input[type="search"],.wy-control-group .wy-form-full input[type="tel"],.wy-control-group .wy-form-full input[type="color"],.wy-control-group .wy-form-halves input[type="text"],.wy-control-group .wy-form-halves input[type="password"],.wy-control-group .wy-form-halves input[type="email"],.wy-control-group .wy-form-halves input[type="url"],.wy-control-group .wy-form-halves input[type="date"],.wy-control-group .wy-form-halves input[type="month"],.wy-control-group .wy-form-halves input[type="time"],.wy-control-group .wy-form-halves input[type="datetime"],.wy-control-group .wy-form-halves input[type="datetime-local"],.wy-control-group .wy-form-halves input[type="week"],.wy-control-group .wy-form-halves input[type="number"],.wy-control-group .wy-form-halves input[type="search"],.wy-control-group .wy-form-halves input[type="tel"],.wy-control-group .wy-form-halves input[type="color"],.wy-control-group .wy-form-thirds input[type="text"],.wy-control-group .wy-form-thirds input[type="password"],.wy-control-group .wy-form-thirds input[type="email"],.wy-control-group .wy-form-thirds input[type="url"],.wy-control-group .wy-form-thirds input[type="date"],.wy-control-group .wy-form-thirds input[type="month"],.wy-control-group .wy-form-thirds input[type="time"],.wy-control-group .wy-form-thirds input[type="datetime"],.wy-control-group .wy-form-thirds input[type="datetime-local"],.wy-control-group .wy-form-thirds input[type="week"],.wy-control-group .wy-form-thirds input[type="number"],.wy-control-group .wy-form-thirds input[type="search"],.wy-control-group .wy-form-thirds input[type="tel"],.wy-control-group .wy-form-thirds input[type="color"]{width:100%}.wy-control-group .wy-form-full{float:left;display:block;margin-right:2.35765%;width:100%;margin-right:0}.wy-control-group .wy-form-full:last-child{margin-right:0}.wy-control-group .wy-form-halves{float:left;display:block;margin-right:2.35765%;width:48.82117%}.wy-control-group .wy-form-halves:last-child{margin-right:0}.wy-control-group .wy-form-halves:nth-of-type(2n){margin-right:0}.wy-control-group .wy-form-halves:nth-of-type(2n+1){clear:left}.wy-control-group .wy-form-thirds{float:left;display:block;margin-right:2.35765%;width:31.76157%}.wy-control-group .wy-form-thirds:last-child{margin-right:0}.wy-control-group .wy-form-thirds:nth-of-type(3n){margin-right:0}.wy-control-group .wy-form-thirds:nth-of-type(3n+1){clear:left}.wy-control-group.wy-control-group-no-input .wy-control{margin:6px 0 0 0;font-size:90%}.wy-control-no-input{display:inline-block;margin:6px 0 0 0;font-size:90%}.wy-control-group.fluid-input input[type="text"],.wy-control-group.fluid-input input[type="password"],.wy-control-group.fluid-input input[type="email"],.wy-control-group.fluid-input input[type="url"],.wy-control-group.fluid-input input[type="date"],.wy-control-group.fluid-input input[type="month"],.wy-control-group.fluid-input input[type="time"],.wy-control-group.fluid-input input[type="datetime"],.wy-control-group.fluid-input input[type="datetime-local"],.wy-control-group.fluid-input input[type="week"],.wy-control-group.fluid-input input[type="number"],.wy-control-group.fluid-input input[type="search"],.wy-control-group.fluid-input input[type="tel"],.wy-control-group.fluid-input input[type="color"]{width:100%}.wy-form-message-inline{display:inline-block;padding-left:0.3em;color:#666;vertical-align:middle;font-size:90%}.wy-form-message{display:block;color:#999;font-size:70%;margin-top:0.3125em;font-style:italic}.wy-form-message p{font-size:inherit;font-style:italic;margin-bottom:6px}.wy-form-message p:last-child{margin-bottom:0}input{line-height:normal}input[type="button"],input[type="reset"],input[type="submit"]{-webkit-appearance:button;cursor:pointer;font-family:"Lato","proxima-nova","Helvetica Neue",Arial,sans-serif;*overflow:visible}input[type="text"],input[type="password"],input[type="email"],input[type="url"],input[type="date"],input[type="month"],input[type="time"],input[type="datetime"],input[type="datetime-local"],input[type="week"],input[type="number"],input[type="search"],input[type="tel"],input[type="color"]{-webkit-appearance:none;padding:6px;display:inline-block;border:1px solid #ccc;font-size:80%;font-family:"Lato","proxima-nova","Helvetica Neue",Arial,sans-serif;box-shadow:inset 0 1px 3px #ddd;border-radius:0;-webkit-transition:border 0.3s linear;-moz-transition:border 0.3s linear;transition:border 0.3s linear}input[type="datetime-local"]{padding:0.34375em 0.625em}input[disabled]{cursor:default}input[type="checkbox"],input[type="radio"]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;padding:0;margin-right:0.3125em;*height:13px;*width:13px}input[type="search"]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}input[type="search"]::-webkit-search-cancel-button,input[type="search"]::-webkit-search-decoration{-webkit-appearance:none}input[type="text"]:focus,input[type="password"]:focus,input[type="email"]:focus,input[type="url"]:focus,input[type="date"]:focus,input[type="month"]:focus,input[type="time"]:focus,input[type="datetime"]:focus,input[type="datetime-local"]:focus,input[type="week"]:focus,input[type="number"]:focus,input[type="search"]:focus,input[type="tel"]:focus,input[type="color"]:focus{outline:0;outline:thin dotted \9;border-color:#333}input.no-focus:focus{border-color:#ccc !important}input[type="file"]:focus,input[type="radio"]:focus,input[type="checkbox"]:focus{outline:thin dotted #333;outline:1px auto #129FEA}input[type="text"][disabled],input[type="password"][disabled],input[type="email"][disabled],input[type="url"][disabled],input[type="date"][disabled],input[type="month"][disabled],input[type="time"][disabled],input[type="datetime"][disabled],input[type="datetime-local"][disabled],input[type="week"][disabled],input[type="number"][disabled],input[type="search"][disabled],input[type="tel"][disabled],input[type="color"][disabled]{cursor:not-allowed;background-color:#fafafa}input:focus:invalid,textarea:focus:invalid,select:focus:invalid{color:#E74C3C;border:1px solid #E74C3C}input:focus:invalid:focus,textarea:focus:invalid:focus,select:focus:invalid:focus{border-color:#E74C3C}input[type="file"]:focus:invalid:focus,input[type="radio"]:focus:invalid:focus,input[type="checkbox"]:focus:invalid:focus{outline-color:#E74C3C}input.wy-input-large{padding:12px;font-size:100%}textarea{overflow:auto;vertical-align:top;width:100%;font-family:"Lato","proxima-nova","Helvetica Neue",Arial,sans-serif}select,textarea{padding:0.5em 0.625em;display:inline-block;border:1px solid #ccc;font-size:80%;box-shadow:inset 0 1px 3px #ddd;-webkit-transition:border 0.3s linear;-moz-transition:border 0.3s linear;transition:border 0.3s linear}select{border:1px solid #ccc;background-color:#fff}select[multiple]{height:auto}select:focus,textarea:focus{outline:0}select[disabled],textarea[disabled],input[readonly],select[readonly],textarea[readonly]{cursor:not-allowed;background-color:#fafafa}input[type="radio"][disabled],input[type="checkbox"][disabled]{cursor:not-allowed}.wy-checkbox,.wy-radio{margin:6px 0;color:#404040;display:block}.wy-checkbox input,.wy-radio input{vertical-align:baseline}.wy-form-message-inline{display:inline-block;*display:inline;*zoom:1;vertical-align:middle}.wy-input-prefix,.wy-input-suffix{white-space:nowrap;padding:6px}.wy-input-prefix .wy-input-context,.wy-input-suffix .wy-input-context{line-height:27px;padding:0 8px;display:inline-block;font-size:80%;background-color:#f3f6f6;border:solid 1px #ccc;color:#999}.wy-input-suffix .wy-input-context{border-left:0}.wy-input-prefix .wy-input-context{border-right:0}.wy-switch{width:36px;height:12px;margin:12px 0;position:relative;border-radius:4px;background:#ccc;cursor:pointer;-webkit-transition:all 0.2s ease-in-out;-moz-transition:all 0.2s ease-in-out;transition:all 0.2s ease-in-out}.wy-switch:before{position:absolute;content:"";display:block;width:18px;height:18px;border-radius:4px;background:#999;left:-3px;top:-3px;-webkit-transition:all 0.2s ease-in-out;-moz-transition:all 0.2s ease-in-out;transition:all 0.2s ease-in-out}.wy-switch:after{content:"false";position:absolute;left:48px;display:block;font-size:12px;color:#ccc}.wy-switch.active{background:#1e8449}.wy-switch.active:before{left:24px;background:#27AE60}.wy-switch.active:after{content:"true"}.wy-switch.disabled,.wy-switch.active.disabled{cursor:not-allowed}.wy-control-group.wy-control-group-error .wy-form-message,.wy-control-group.wy-control-group-error>label{color:#E74C3C}.wy-control-group.wy-control-group-error input[type="text"],.wy-control-group.wy-control-group-error input[type="password"],.wy-control-group.wy-control-group-error input[type="email"],.wy-control-group.wy-control-group-error input[type="url"],.wy-control-group.wy-control-group-error input[type="date"],.wy-control-group.wy-control-group-error input[type="month"],.wy-control-group.wy-control-group-error input[type="time"],.wy-control-group.wy-control-group-error input[type="datetime"],.wy-control-group.wy-control-group-error input[type="datetime-local"],.wy-control-group.wy-control-group-error input[type="week"],.wy-control-group.wy-control-group-error input[type="number"],.wy-control-group.wy-control-group-error input[type="search"],.wy-control-group.wy-control-group-error input[type="tel"],.wy-control-group.wy-control-group-error input[type="color"]{border:solid 1px #E74C3C}.wy-control-group.wy-control-group-error textarea{border:solid 1px #E74C3C}.wy-inline-validate{white-space:nowrap}.wy-inline-validate .wy-input-context{padding:0.5em 0.625em;display:inline-block;font-size:80%}.wy-inline-validate.wy-inline-validate-success .wy-input-context{color:#27AE60}.wy-inline-validate.wy-inline-validate-danger .wy-input-context{color:#E74C3C}.wy-inline-validate.wy-inline-validate-warning .wy-input-context{color:#E67E22}.wy-inline-validate.wy-inline-validate-info .wy-input-context{color:#2980B9}.rotate-90{-webkit-transform:rotate(90deg);-moz-transform:rotate(90deg);-ms-transform:rotate(90deg);-o-transform:rotate(90deg);transform:rotate(90deg)}.rotate-180{-webkit-transform:rotate(180deg);-moz-transform:rotate(180deg);-ms-transform:rotate(180deg);-o-transform:rotate(180deg);transform:rotate(180deg)}.rotate-270{-webkit-transform:rotate(270deg);-moz-transform:rotate(270deg);-ms-transform:rotate(270deg);-o-transform:rotate(270deg);transform:rotate(270deg)}.mirror{-webkit-transform:scaleX(-1);-moz-transform:scaleX(-1);-ms-transform:scaleX(-1);-o-transform:scaleX(-1);transform:scaleX(-1)}.mirror.rotate-90{-webkit-transform:scaleX(-1) rotate(90deg);-moz-transform:scaleX(-1) rotate(90deg);-ms-transform:scaleX(-1) rotate(90deg);-o-transform:scaleX(-1) rotate(90deg);transform:scaleX(-1) rotate(90deg)}.mirror.rotate-180{-webkit-transform:scaleX(-1) rotate(180deg);-moz-transform:scaleX(-1) rotate(180deg);-ms-transform:scaleX(-1) rotate(180deg);-o-transform:scaleX(-1) rotate(180deg);transform:scaleX(-1) rotate(180deg)}.mirror.rotate-270{-webkit-transform:scaleX(-1) rotate(270deg);-moz-transform:scaleX(-1) rotate(270deg);-ms-transform:scaleX(-1) rotate(270deg);-o-transform:scaleX(-1) rotate(270deg);transform:scaleX(-1) rotate(270deg)}@media only screen and (max-width: 480px){.wy-form button[type="submit"]{margin:0.7em 0 0}.wy-form input[type="text"],.wy-form input[type="password"],.wy-form input[type="email"],.wy-form input[type="url"],.wy-form input[type="date"],.wy-form input[type="month"],.wy-form input[type="time"],.wy-form input[type="datetime"],.wy-form input[type="datetime-local"],.wy-form input[type="week"],.wy-form input[type="number"],.wy-form input[type="search"],.wy-form input[type="tel"],.wy-form input[type="color"]{margin-bottom:0.3em;display:block}.wy-form label{margin-bottom:0.3em;display:block}.wy-form input[type="password"],.wy-form input[type="email"],.wy-form input[type="url"],.wy-form input[type="date"],.wy-form input[type="month"],.wy-form input[type="time"],.wy-form input[type="datetime"],.wy-form input[type="datetime-local"],.wy-form input[type="week"],.wy-form input[type="number"],.wy-form input[type="search"],.wy-form input[type="tel"],.wy-form input[type="color"]{margin-bottom:0}.wy-form-aligned .wy-control-group label{margin-bottom:0.3em;text-align:left;display:block;width:100%}.wy-form-aligned .wy-control{margin:1.5em 0 0 0}.wy-form .wy-help-inline,.wy-form-message-inline,.wy-form-message{display:block;font-size:80%;padding:6px 0}}@media screen and (max-width: 768px){.tablet-hide{display:none}}@media screen and (max-width: 480px){.mobile-hide{display:none}}.float-left{float:left}.float-right{float:right}.full-width{width:100%}.wy-table,.rst-content table.docutils,.rst-content table.field-list{border-collapse:collapse;border-spacing:0;empty-cells:show;margin-bottom:24px}.wy-table caption,.rst-content table.docutils caption,.rst-content table.field-list caption{color:#000;font:italic 85%/1 arial,sans-serif;padding:1em 0;text-align:center}.wy-table td,.rst-content table.docutils td,.rst-content table.field-list td,.wy-table th,.rst-content table.docutils th,.rst-content table.field-list th{font-size:90%;margin:0;overflow:visible;padding:8px 16px}.wy-table td:first-child,.rst-content table.docutils td:first-child,.rst-content table.field-list td:first-child,.wy-table th:first-child,.rst-content table.docutils th:first-child,.rst-content table.field-list th:first-child{border-left-width:0}.wy-table thead,.rst-content table.docutils thead,.rst-content table.field-list thead{color:#000;text-align:left;vertical-align:bottom;white-space:nowrap}.wy-table thead th,.rst-content table.docutils thead th,.rst-content table.field-list thead th{font-weight:bold;border-bottom:solid 2px #e1e4e5}.wy-table td,.rst-content table.docutils td,.rst-content table.field-list td{background-color:transparent;vertical-align:middle}.wy-table td p,.rst-content table.docutils td p,.rst-content table.field-list td p{line-height:18px}.wy-table td p:last-child,.rst-content table.docutils td p:last-child,.rst-content table.field-list td p:last-child{margin-bottom:0}.wy-table .wy-table-cell-min,.rst-content table.docutils .wy-table-cell-min,.rst-content table.field-list .wy-table-cell-min{width:1%;padding-right:0}.wy-table .wy-table-cell-min input[type=checkbox],.rst-content table.docutils .wy-table-cell-min input[type=checkbox],.rst-content table.field-list .wy-table-cell-min input[type=checkbox],.wy-table .wy-table-cell-min input[type=checkbox],.rst-content table.docutils .wy-table-cell-min input[type=checkbox],.rst-content table.field-list .wy-table-cell-min input[type=checkbox]{margin:0}.wy-table-secondary{color:gray;font-size:90%}.wy-table-tertiary{color:gray;font-size:80%}.wy-table-odd td,.wy-table-striped tr:nth-child(2n-1) td,.rst-content table.docutils:not(.field-list) tr:nth-child(2n-1) td{background-color:#f3f6f6}.wy-table-backed{background-color:#f3f6f6}.wy-table-bordered-all,.rst-content table.docutils{border:1px solid #e1e4e5}.wy-table-bordered-all td,.rst-content table.docutils td{border-bottom:1px solid #e1e4e5;border-left:1px solid #e1e4e5}.wy-table-bordered-all tbody>tr:last-child td,.rst-content table.docutils tbody>tr:last-child td{border-bottom-width:0}.wy-table-bordered{border:1px solid #e1e4e5}.wy-table-bordered-rows td{border-bottom:1px solid #e1e4e5}.wy-table-bordered-rows tbody>tr:last-child td{border-bottom-width:0}.wy-table-horizontal tbody>tr:last-child td{border-bottom-width:0}.wy-table-horizontal td,.wy-table-horizontal th{border-width:0 0 1px 0;border-bottom:1px solid #e1e4e5}.wy-table-horizontal tbody>tr:last-child td{border-bottom-width:0}.wy-table-responsive{margin-bottom:24px;max-width:100%;overflow:auto}.wy-table-responsive table{margin-bottom:0 !important}.wy-table-responsive table td,.wy-table-responsive table th{white-space:nowrap}a{color:#2980B9;text-decoration:none;cursor:pointer}a:hover{color:#3091d1}a:visited{color:#9B59B6}html{height:100%;overflow-x:hidden}body{font-family:"Lato","proxima-nova","Helvetica Neue",Arial,sans-serif;font-weight:normal;color:#404040;min-height:100%;overflow-x:hidden;background:#edf0f2}.wy-text-left{text-align:left}.wy-text-center{text-align:center}.wy-text-right{text-align:right}.wy-text-large{font-size:120%}.wy-text-normal{font-size:100%}.wy-text-small,small{font-size:80%}.wy-text-strike{text-decoration:line-through}.wy-text-warning{color:#E67E22 !important}a.wy-text-warning:hover{color:#eb9950 !important}.wy-text-info{color:#2980B9 !important}a.wy-text-info:hover{color:#409ad5 !important}.wy-text-success{color:#27AE60 !important}a.wy-text-success:hover{color:#36d278 !important}.wy-text-danger{color:#E74C3C !important}a.wy-text-danger:hover{color:#ed7669 !important}.wy-text-neutral{color:#404040 !important}a.wy-text-neutral:hover{color:#595959 !important}h1,h2,.rst-content p.caption,h3,h4,h5,h6,legend{margin-top:0;font-weight:700;font-family:"Roboto Slab","ff-tisa-web-pro","Georgia",Arial,sans-serif}p{line-height:24px;margin:0;font-size:16px;margin-bottom:24px}h1{font-size:175%}h2,.rst-content p.caption{font-size:150%}h3{font-size:125%}h4{font-size:115%}h5{font-size:110%}h6{font-size:100%}hr{display:block;height:1px;border:0;border-top:1px solid #e1e4e5;margin:24px 0;padding:0}code,.rst-content tt,.rst-content code{white-space:nowrap;max-width:100%;background:#fff;border:solid 1px #e1e4e5;font-size:75%;padding:0 5px;font-family:Consolas,"Andale Mono WT","Andale Mono","Lucida Console","Lucida Sans Typewriter","DejaVu Sans Mono","Bitstream Vera Sans Mono","Liberation Mono","Nimbus Mono L",Monaco,"Courier New",Courier,monospace;color:#E74C3C;overflow-x:auto}code.code-large,.rst-content tt.code-large{font-size:90%}.wy-plain-list-disc,.rst-content .section ul,.rst-content .toctree-wrapper ul,article ul{list-style:disc;line-height:24px;margin-bottom:24px}.wy-plain-list-disc li,.rst-content .section ul li,.rst-content .toctree-wrapper ul li,article ul li{list-style:disc;margin-left:24px}.wy-plain-list-disc li p:last-child,.rst-content .section ul li p:last-child,.rst-content .toctree-wrapper ul li p:last-child,article ul li p:last-child{margin-bottom:0}.wy-plain-list-disc li ul,.rst-content .section ul li ul,.rst-content .toctree-wrapper ul li ul,article ul li ul{margin-bottom:0}.wy-plain-list-disc li li,.rst-content .section ul li li,.rst-content .toctree-wrapper ul li li,article ul li li{list-style:circle}.wy-plain-list-disc li li li,.rst-content .section ul li li li,.rst-content .toctree-wrapper ul li li li,article ul li li li{list-style:square}.wy-plain-list-disc li ol li,.rst-content .section ul li ol li,.rst-content .toctree-wrapper ul li ol li,article ul li ol li{list-style:decimal}.wy-plain-list-decimal,.rst-content .section ol,.rst-content ol.arabic,article ol{list-style:decimal;line-height:24px;margin-bottom:24px}.wy-plain-list-decimal li,.rst-content .section ol li,.rst-content ol.arabic li,article ol li{list-style:decimal;margin-left:24px}.wy-plain-list-decimal li p:last-child,.rst-content .section ol li p:last-child,.rst-content ol.arabic li p:last-child,article ol li p:last-child{margin-bottom:0}.wy-plain-list-decimal li ul,.rst-content .section ol li ul,.rst-content ol.arabic li ul,article ol li ul{margin-bottom:0}.wy-plain-list-decimal li ul li,.rst-content .section ol li ul li,.rst-content ol.arabic li ul li,article ol li ul li{list-style:disc}.codeblock-example{border:1px solid #e1e4e5;border-bottom:none;padding:24px;padding-top:48px;font-weight:500;background:#fff;position:relative}.codeblock-example:after{content:"Example";position:absolute;top:0px;left:0px;background:#9B59B6;color:#fff;padding:6px 12px}.codeblock-example.prettyprint-example-only{border:1px solid #e1e4e5;margin-bottom:24px}.codeblock,pre.literal-block,.rst-content .literal-block,.rst-content pre.literal-block,div[class^='highlight']{border:1px solid #e1e4e5;padding:0px;overflow-x:auto;background:#fff;margin:1px 0 24px 0}.codeblock div[class^='highlight'],pre.literal-block div[class^='highlight'],.rst-content .literal-block div[class^='highlight'],div[class^='highlight'] div[class^='highlight']{border:none;background:none;margin:0}div[class^='highlight'] td.code{width:100%}.linenodiv pre{border-right:solid 1px #e6e9ea;margin:0;padding:12px 12px;font-family:Consolas,"Andale Mono WT","Andale Mono","Lucida Console","Lucida Sans Typewriter","DejaVu Sans Mono","Bitstream Vera Sans Mono","Liberation Mono","Nimbus Mono L",Monaco,"Courier New",Courier,monospace;font-size:12px;line-height:1.5;color:#d9d9d9}div[class^='highlight'] pre{white-space:pre;margin:0;padding:12px 12px;font-family:Consolas,"Andale Mono WT","Andale Mono","Lucida Console","Lucida Sans Typewriter","DejaVu Sans Mono","Bitstream Vera Sans Mono","Liberation Mono","Nimbus Mono L",Monaco,"Courier New",Courier,monospace;font-size:12px;line-height:1.5;display:block;overflow:auto;color:#404040}@media print{.codeblock,pre.literal-block,.rst-content .literal-block,.rst-content pre.literal-block,div[class^='highlight'],div[class^='highlight'] pre{white-space:pre-wrap}}.hll{background-color:#ffc;margin:0 -12px;padding:0 12px;display:block}.c{color:#998;font-style:italic}.err{color:#a61717;background-color:#e3d2d2}.k{font-weight:bold}.o{font-weight:bold}.cm{color:#998;font-style:italic}.cp{color:#999;font-weight:bold}.c1{color:#998;font-style:italic}.cs{color:#999;font-weight:bold;font-style:italic}.gd{color:#000;background-color:#fdd}.gd .x{color:#000;background-color:#faa}.ge{font-style:italic}.gr{color:#a00}.gh{color:#999}.gi{color:#000;background-color:#dfd}.gi .x{color:#000;background-color:#afa}.go{color:#888}.gp{color:#555}.gs{font-weight:bold}.gu{color:purple;font-weight:bold}.gt{color:#a00}.kc{font-weight:bold}.kd{font-weight:bold}.kn{font-weight:bold}.kp{font-weight:bold}.kr{font-weight:bold}.kt{color:#458;font-weight:bold}.m{color:#099}.s{color:#d14}.n{color:#333}.na{color:teal}.nb{color:#0086b3}.nc{color:#458;font-weight:bold}.no{color:teal}.ni{color:purple}.ne{color:#900;font-weight:bold}.nf{color:#900;font-weight:bold}.nn{color:#555}.nt{color:navy}.nv{color:teal}.ow{font-weight:bold}.w{color:#bbb}.mf{color:#099}.mh{color:#099}.mi{color:#099}.mo{color:#099}.sb{color:#d14}.sc{color:#d14}.sd{color:#d14}.s2{color:#d14}.se{color:#d14}.sh{color:#d14}.si{color:#d14}.sx{color:#d14}.sr{color:#009926}.s1{color:#d14}.ss{color:#990073}.bp{color:#999}.vc{color:teal}.vg{color:teal}.vi{color:teal}.il{color:#099}.gc{color:#999;background-color:#EAF2F5}.wy-breadcrumbs li{display:inline-block}.wy-breadcrumbs li.wy-breadcrumbs-aside{float:right}.wy-breadcrumbs li a{display:inline-block;padding:5px}.wy-breadcrumbs li a:first-child{padding-left:0}.wy-breadcrumbs-extra{margin-bottom:0;color:#b3b3b3;font-size:80%;display:inline-block}@media screen and (max-width: 480px){.wy-breadcrumbs-extra{display:none}.wy-breadcrumbs li.wy-breadcrumbs-aside{display:none}}@media print{.wy-breadcrumbs li.wy-breadcrumbs-aside{display:none}}.wy-affix{position:fixed;top:1.618em}.wy-menu a:hover{text-decoration:none}.wy-menu-horiz{*zoom:1}.wy-menu-horiz:before,.wy-menu-horiz:after{display:table;content:""}.wy-menu-horiz:after{clear:both}.wy-menu-horiz ul,.wy-menu-horiz li{display:inline-block}.wy-menu-horiz li:hover{background:rgba(255,255,255,0.1)}.wy-menu-horiz li.divide-left{border-left:solid 1px #404040}.wy-menu-horiz li.divide-right{border-right:solid 1px #404040}.wy-menu-horiz a{height:32px;display:inline-block;line-height:32px;padding:0 16px}.wy-menu-vertical header,.wy-menu-vertical p.caption{height:32px;display:inline-block;line-height:32px;padding:0 1.618em;margin-bottom:0;display:block;font-weight:bold;text-transform:uppercase;font-size:80%;color:#555;white-space:nowrap}.wy-menu-vertical ul{margin-bottom:0}.wy-menu-vertical li.divide-top{border-top:solid 1px #404040}.wy-menu-vertical li.divide-bottom{border-bottom:solid 1px #404040}.wy-menu-vertical li.current{background:#e3e3e3}.wy-menu-vertical li.current a{color:gray;border-right:solid 1px #c9c9c9;padding:0.4045em 2.427em}.wy-menu-vertical li.current a:hover{background:#d6d6d6}.wy-menu-vertical li code,.wy-menu-vertical li .rst-content tt,.rst-content .wy-menu-vertical li tt{border:none;background:inherit;color:inherit;padding-left:0;padding-right:0}.wy-menu-vertical li span.toctree-expand{display:block;float:left;margin-left:-1.2em;font-size:0.8em;line-height:1.6em;color:#4d4d4d}.wy-menu-vertical li.on a,.wy-menu-vertical li.current>a{color:#404040;padding:0.4045em 1.618em;font-weight:bold;position:relative;background:#fcfcfc;border:none;border-bottom:solid 1px #c9c9c9;border-top:solid 1px #c9c9c9;padding-left:1.618em -4px}.wy-menu-vertical li.on a:hover,.wy-menu-vertical li.current>a:hover{background:#fcfcfc}.wy-menu-vertical li.on a:hover span.toctree-expand,.wy-menu-vertical li.current>a:hover span.toctree-expand{color:gray}.wy-menu-vertical li.on a span.toctree-expand,.wy-menu-vertical li.current>a span.toctree-expand{display:block;font-size:0.8em;line-height:1.6em;color:#333}.wy-menu-vertical li.toctree-l1.current li.toctree-l2>ul,.wy-menu-vertical li.toctree-l2.current li.toctree-l3>ul{display:none}.wy-menu-vertical li.toctree-l1.current li.toctree-l2.current>ul,.wy-menu-vertical li.toctree-l2.current li.toctree-l3.current>ul{display:block}.wy-menu-vertical li.toctree-l2.current>a{background:#c9c9c9;padding:0.4045em 2.427em}.wy-menu-vertical li.toctree-l2.current li.toctree-l3>a{display:block;background:#c9c9c9;padding:0.4045em 4.045em}.wy-menu-vertical li.toctree-l2 a:hover span.toctree-expand{color:gray}.wy-menu-vertical li.toctree-l2 span.toctree-expand{color:#a3a3a3}.wy-menu-vertical li.toctree-l3{font-size:0.9em}.wy-menu-vertical li.toctree-l3.current>a{background:#bdbdbd;padding:0.4045em 4.045em}.wy-menu-vertical li.toctree-l3.current li.toctree-l4>a{display:block;background:#bdbdbd;padding:0.4045em 5.663em;border-top:none;border-bottom:none}.wy-menu-vertical li.toctree-l3 a:hover span.toctree-expand{color:gray}.wy-menu-vertical li.toctree-l3 span.toctree-expand{color:#969696}.wy-menu-vertical li.toctree-l4{font-size:0.9em}.wy-menu-vertical li.current ul{display:block}.wy-menu-vertical li ul{margin-bottom:0;display:none}.wy-menu-vertical .local-toc li ul{display:block}.wy-menu-vertical li ul li a{margin-bottom:0;color:#b3b3b3;font-weight:normal}.wy-menu-vertical a{display:inline-block;line-height:18px;padding:0.4045em 1.618em;display:block;position:relative;font-size:90%;color:#b3b3b3}.wy-menu-vertical a:hover{background-color:#4e4a4a;cursor:pointer}.wy-menu-vertical a:hover span.toctree-expand{color:#b3b3b3}.wy-menu-vertical a:active{background-color:#2980B9;cursor:pointer;color:#fff}.wy-menu-vertical a:active span.toctree-expand{color:#fff}.wy-side-nav-search{z-index:200;background-color:#2980B9;text-align:center;padding:0.809em;display:block;color:#fcfcfc;margin-bottom:0.809em}.wy-side-nav-search input[type=text]{width:100%;border-radius:50px;padding:6px 12px;border-color:#2472a4}.wy-side-nav-search img{display:block;margin:auto auto 0.809em auto;height:45px;width:45px;background-color:#2980B9;padding:5px;border-radius:100%}.wy-side-nav-search>a,.wy-side-nav-search .wy-dropdown>a{color:#fcfcfc;font-size:100%;font-weight:bold;display:inline-block;padding:4px 6px;margin-bottom:0.809em}.wy-side-nav-search>a:hover,.wy-side-nav-search .wy-dropdown>a:hover{background:rgba(255,255,255,0.1)}.wy-side-nav-search>a img.logo,.wy-side-nav-search .wy-dropdown>a img.logo{display:block;margin:0 auto;height:auto;width:auto;border-radius:0;max-width:100%;background:transparent}.wy-side-nav-search>a.icon img.logo,.wy-side-nav-search .wy-dropdown>a.icon img.logo{margin-top:0.85em}.wy-nav .wy-menu-vertical header{color:#2980B9}.wy-nav .wy-menu-vertical a{color:#b3b3b3}.wy-nav .wy-menu-vertical a:hover{background-color:#2980B9;color:#fff}[data-menu-wrap]{-webkit-transition:all 0.2s ease-in;-moz-transition:all 0.2s ease-in;transition:all 0.2s ease-in;position:absolute;opacity:1;width:100%;opacity:0}[data-menu-wrap].move-center{left:0;right:auto;opacity:1}[data-menu-wrap].move-left{right:auto;left:-100%;opacity:0}[data-menu-wrap].move-right{right:-100%;left:auto;opacity:0}.wy-body-for-nav{background:left repeat-y #fcfcfc;background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyRpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoTWFjaW50b3NoKSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDoxOERBMTRGRDBFMUUxMUUzODUwMkJCOThDMEVFNURFMCIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDoxOERBMTRGRTBFMUUxMUUzODUwMkJCOThDMEVFNURFMCI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOjE4REExNEZCMEUxRTExRTM4NTAyQkI5OEMwRUU1REUwIiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOjE4REExNEZDMEUxRTExRTM4NTAyQkI5OEMwRUU1REUwIi8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+EwrlwAAAAA5JREFUeNpiMDU0BAgwAAE2AJgB9BnaAAAAAElFTkSuQmCC);background-size:300px 1px}.wy-grid-for-nav{position:absolute;width:100%;height:100%}.wy-nav-side{position:fixed;top:0;bottom:0;left:0;padding-bottom:2em;width:300px;overflow-x:hidden;overflow-y:scroll;min-height:100%;background:#343131;z-index:200}.wy-nav-top{display:none;background:#2980B9;color:#fff;padding:0.4045em 0.809em;position:relative;line-height:50px;text-align:center;font-size:100%;*zoom:1}.wy-nav-top:before,.wy-nav-top:after{display:table;content:""}.wy-nav-top:after{clear:both}.wy-nav-top a{color:#fff;font-weight:bold}.wy-nav-top img{margin-right:12px;height:45px;width:45px;background-color:#2980B9;padding:5px;border-radius:100%}.wy-nav-top i{font-size:30px;float:left;cursor:pointer}.wy-nav-content-wrap{margin-left:300px;background:#fcfcfc;min-height:100%}.wy-nav-content{padding:1.618em 3.236em;height:100%;max-width:800px;margin:auto}.wy-body-mask{position:fixed;width:100%;height:100%;background:rgba(0,0,0,0.2);display:none;z-index:499}.wy-body-mask.on{display:block}footer{color:#999}footer p{margin-bottom:12px}.rst-footer-buttons{*zoom:1}.rst-footer-buttons:before,.rst-footer-buttons:after{display:table;content:""}.rst-footer-buttons:after{clear:both}#search-results .search li{margin-bottom:24px;border-bottom:solid 1px #e1e4e5;padding-bottom:24px}#search-results .search li:first-child{border-top:solid 1px #e1e4e5;padding-top:24px}#search-results .search li a{font-size:120%;margin-bottom:12px;display:inline-block}#search-results .context{color:gray;font-size:90%}@media screen and (max-width: 768px){.wy-body-for-nav{background:#fcfcfc}.wy-nav-top{display:block}.wy-nav-side{left:-300px}.wy-nav-side.shift{width:85%;left:0}.wy-nav-content-wrap{margin-left:0}.wy-nav-content-wrap .wy-nav-content{padding:1.618em}.wy-nav-content-wrap.shift{position:fixed;min-width:100%;left:85%;top:0;height:100%;overflow:hidden}}@media screen and (min-width: 1400px){.wy-nav-content-wrap{background:rgba(0,0,0,0.05)}.wy-nav-content{margin:0;background:#fcfcfc}}@media print{.rst-versions,footer,.wy-nav-side{display:none}.wy-nav-content-wrap{margin-left:0}}.rst-versions{position:fixed;bottom:0;left:0;width:300px;color:#fcfcfc;background:#1f1d1d;border-top:solid 10px #343131;font-family:"Lato","proxima-nova","Helvetica Neue",Arial,sans-serif;z-index:400}.rst-versions a{color:#2980B9;text-decoration:none}.rst-versions .rst-badge-small{display:none}.rst-versions .rst-current-version{padding:12px;background-color:#272525;display:block;text-align:right;font-size:90%;cursor:pointer;color:#27AE60;*zoom:1}.rst-versions .rst-current-version:before,.rst-versions .rst-current-version:after{display:table;content:""}.rst-versions .rst-current-version:after{clear:both}.rst-versions .rst-current-version .fa,.rst-versions .rst-current-version .wy-menu-vertical li span.toctree-expand,.wy-menu-vertical li .rst-versions .rst-current-version span.toctree-expand,.rst-versions .rst-current-version .rst-content .admonition-title,.rst-content .rst-versions .rst-current-version .admonition-title,.rst-versions .rst-current-version .rst-content h1 .headerlink,.rst-content h1 .rst-versions .rst-current-version .headerlink,.rst-versions .rst-current-version .rst-content h2 .headerlink,.rst-content h2 .rst-versions .rst-current-version .headerlink,.rst-versions .rst-current-version .rst-content p.caption .headerlink,.rst-content p.caption .rst-versions .rst-current-version .headerlink,.rst-versions .rst-current-version .rst-content h3 .headerlink,.rst-content h3 .rst-versions .rst-current-version .headerlink,.rst-versions .rst-current-version .rst-content h4 .headerlink,.rst-content h4 .rst-versions .rst-current-version .headerlink,.rst-versions .rst-current-version .rst-content h5 .headerlink,.rst-content h5 .rst-versions .rst-current-version .headerlink,.rst-versions .rst-current-version .rst-content h6 .headerlink,.rst-content h6 .rst-versions .rst-current-version .headerlink,.rst-versions .rst-current-version .rst-content dl dt .headerlink,.rst-content dl dt .rst-versions .rst-current-version .headerlink,.rst-versions .rst-current-version .rst-content tt.download span:first-child,.rst-content tt.download .rst-versions .rst-current-version span:first-child,.rst-versions .rst-current-version .rst-content code.download span:first-child,.rst-content code.download .rst-versions .rst-current-version span:first-child,.rst-versions .rst-current-version .icon{color:#fcfcfc}.rst-versions .rst-current-version .fa-book,.rst-versions .rst-current-version .icon-book{float:left}.rst-versions .rst-current-version .icon-book{float:left}.rst-versions .rst-current-version.rst-out-of-date{background-color:#E74C3C;color:#fff}.rst-versions .rst-current-version.rst-active-old-version{background-color:#F1C40F;color:#000}.rst-versions.shift-up .rst-other-versions{display:block}.rst-versions .rst-other-versions{font-size:90%;padding:12px;color:gray;display:none}.rst-versions .rst-other-versions hr{display:block;height:1px;border:0;margin:20px 0;padding:0;border-top:solid 1px #413d3d}.rst-versions .rst-other-versions dd{display:inline-block;margin:0}.rst-versions .rst-other-versions dd a{display:inline-block;padding:6px;color:#fcfcfc}.rst-versions.rst-badge{width:auto;bottom:20px;right:20px;left:auto;border:none;max-width:300px}.rst-versions.rst-badge .icon-book{float:none}.rst-versions.rst-badge .fa-book,.rst-versions.rst-badge .icon-book{float:none}.rst-versions.rst-badge.shift-up .rst-current-version{text-align:right}.rst-versions.rst-badge.shift-up .rst-current-version .fa-book,.rst-versions.rst-badge.shift-up .rst-current-version .icon-book{float:left}.rst-versions.rst-badge.shift-up .rst-current-version .icon-book{float:left}.rst-versions.rst-badge .rst-current-version{width:auto;height:30px;line-height:30px;padding:0 6px;display:block;text-align:center}@media screen and (max-width: 768px){.rst-versions{width:85%;display:none}.rst-versions.shift{display:block}img{width:100%;height:auto}}.rst-content img{max-width:100%;height:auto !important}.rst-content div.figure{margin-bottom:24px}.rst-content div.figure.align-center{text-align:center}.rst-content .section>img,.rst-content .section>a>img{margin-bottom:24px}.rst-content blockquote{margin-left:24px;line-height:24px;margin-bottom:24px}.rst-content .note .last,.rst-content .attention .last,.rst-content .caution .last,.rst-content .danger .last,.rst-content .error .last,.rst-content .hint .last,.rst-content .important .last,.rst-content .tip .last,.rst-content .warning .last,.rst-content .seealso .last,.rst-content .admonition-todo .last{margin-bottom:0}.rst-content .admonition-title:before{margin-right:4px}.rst-content .admonition table{border-color:rgba(0,0,0,0.1)}.rst-content .admonition table td,.rst-content .admonition table th{background:transparent !important;border-color:rgba(0,0,0,0.1) !important}.rst-content .section ol.loweralpha,.rst-content .section ol.loweralpha li{list-style:lower-alpha}.rst-content .section ol.upperalpha,.rst-content .section ol.upperalpha li{list-style:upper-alpha}.rst-content .section ol p,.rst-content .section ul p{margin-bottom:12px}.rst-content .line-block{margin-left:24px}.rst-content .topic-title{font-weight:bold;margin-bottom:12px}.rst-content .toc-backref{color:#404040}.rst-content .align-right{float:right;margin:0px 0px 24px 24px}.rst-content .align-left{float:left;margin:0px 24px 24px 0px}.rst-content .align-center{margin:auto;display:block}.rst-content h1 .headerlink,.rst-content h2 .headerlink,.rst-content p.caption .headerlink,.rst-content h3 .headerlink,.rst-content h4 .headerlink,.rst-content h5 .headerlink,.rst-content h6 .headerlink,.rst-content dl dt .headerlink,.rst-content p.caption .headerlink{display:none;visibility:hidden;font-size:14px}.rst-content h1 .headerlink:after,.rst-content h2 .headerlink:after,.rst-content p.caption .headerlink:after,.rst-content h3 .headerlink:after,.rst-content h4 .headerlink:after,.rst-content h5 .headerlink:after,.rst-content h6 .headerlink:after,.rst-content dl dt .headerlink:after,.rst-content p.caption .headerlink:after{visibility:visible;content:"";font-family:FontAwesome;display:inline-block}.rst-content h1:hover .headerlink,.rst-content h2:hover .headerlink,.rst-content p.caption:hover .headerlink,.rst-content h3:hover .headerlink,.rst-content h4:hover .headerlink,.rst-content h5:hover .headerlink,.rst-content h6:hover .headerlink,.rst-content dl dt:hover .headerlink,.rst-content p.caption:hover .headerlink{display:inline-block}.rst-content .sidebar{float:right;width:40%;display:block;margin:0 0 24px 24px;padding:24px;background:#f3f6f6;border:solid 1px #e1e4e5}.rst-content .sidebar p,.rst-content .sidebar ul,.rst-content .sidebar dl{font-size:90%}.rst-content .sidebar .last{margin-bottom:0}.rst-content .sidebar .sidebar-title{display:block;font-family:"Roboto Slab","ff-tisa-web-pro","Georgia",Arial,sans-serif;font-weight:bold;background:#e1e4e5;padding:6px 12px;margin:-24px;margin-bottom:24px;font-size:100%}.rst-content .highlighted{background:#F1C40F;display:inline-block;font-weight:bold;padding:0 6px}.rst-content .footnote-reference,.rst-content .citation-reference{vertical-align:super;font-size:90%}.rst-content table.docutils.citation,.rst-content table.docutils.footnote{background:none;border:none;color:#999}.rst-content table.docutils.citation td,.rst-content table.docutils.citation tr,.rst-content table.docutils.footnote td,.rst-content table.docutils.footnote tr{border:none;background-color:transparent !important;white-space:normal}.rst-content table.docutils.citation td.label,.rst-content table.docutils.footnote td.label{padding-left:0;padding-right:0;vertical-align:top}.rst-content table.field-list{border:none}.rst-content table.field-list td{border:none;padding-top:5px}.rst-content table.field-list td>strong{display:inline-block;margin-top:3px}.rst-content table.field-list .field-name{padding-right:10px;text-align:left;white-space:nowrap}.rst-content table.field-list .field-body{text-align:left;padding-left:0}.rst-content tt,.rst-content tt,.rst-content code{color:#000}.rst-content tt big,.rst-content tt em,.rst-content tt big,.rst-content code big,.rst-content tt em,.rst-content code em{font-size:100% !important;line-height:normal}.rst-content tt .xref,a .rst-content tt,.rst-content tt .xref,.rst-content code .xref,a .rst-content tt,a .rst-content code{font-weight:bold}.rst-content a tt,.rst-content a tt,.rst-content a code{color:#2980B9}.rst-content dl{margin-bottom:24px}.rst-content dl dt{font-weight:bold}.rst-content dl p,.rst-content dl table,.rst-content dl ul,.rst-content dl ol{margin-bottom:12px !important}.rst-content dl dd{margin:0 0 12px 24px}.rst-content dl:not(.docutils){margin-bottom:24px}.rst-content dl:not(.docutils) dt{display:inline-block;margin:6px 0;font-size:90%;line-height:normal;background:#e7f2fa;color:#2980B9;border-top:solid 3px #6ab0de;padding:6px;position:relative}.rst-content dl:not(.docutils) dt:before{color:#6ab0de}.rst-content dl:not(.docutils) dt .headerlink{color:#404040;font-size:100% !important}.rst-content dl:not(.docutils) dl dt{margin-bottom:6px;border:none;border-left:solid 3px #ccc;background:#f0f0f0;color:gray}.rst-content dl:not(.docutils) dl dt .headerlink{color:#404040;font-size:100% !important}.rst-content dl:not(.docutils) dt:first-child{margin-top:0}.rst-content dl:not(.docutils) tt,.rst-content dl:not(.docutils) tt,.rst-content dl:not(.docutils) code{font-weight:bold}.rst-content dl:not(.docutils) tt.descname,.rst-content dl:not(.docutils) tt.descclassname,.rst-content dl:not(.docutils) tt.descname,.rst-content dl:not(.docutils) code.descname,.rst-content dl:not(.docutils) tt.descclassname,.rst-content dl:not(.docutils) code.descclassname{background-color:transparent;border:none;padding:0;font-size:100% !important}.rst-content dl:not(.docutils) tt.descname,.rst-content dl:not(.docutils) tt.descname,.rst-content dl:not(.docutils) code.descname{font-weight:bold}.rst-content dl:not(.docutils) .optional{display:inline-block;padding:0 4px;color:#000;font-weight:bold}.rst-content dl:not(.docutils) .property{display:inline-block;padding-right:8px}.rst-content .viewcode-link,.rst-content .viewcode-back{display:inline-block;color:#27AE60;font-size:80%;padding-left:24px}.rst-content .viewcode-back{display:block;float:right}.rst-content p.rubric{margin-bottom:12px;font-weight:bold}.rst-content tt.download,.rst-content code.download{background:inherit;padding:inherit;font-family:inherit;font-size:inherit;color:inherit;border:inherit;white-space:inherit}.rst-content tt.download span:first-child:before,.rst-content code.download span:first-child:before{margin-right:4px}@media screen and (max-width: 480px){.rst-content .sidebar{width:100%}}span[id*='MathJax-Span']{color:#404040}.math{text-align:center}@font-face{font-family:"Inconsolata";font-style:normal;font-weight:400;src:local("Inconsolata"),url(../fonts/Inconsolata.ttf) format("truetype")}@font-face{font-family:"Inconsolata";font-style:normal;font-weight:700;src:local("Inconsolata Bold"),local("Inconsolata-Bold"),url(../fonts/Inconsolata-Bold.ttf) format("truetype")}@font-face{font-family:"Lato";font-style:normal;font-weight:400;src:local("Lato Regular"),local("Lato-Regular"),url(../fonts/Lato-Regular.ttf) format("truetype")}@font-face{font-family:"Lato";font-style:normal;font-weight:700;src:local("Lato Bold"),local("Lato-Bold"),url(../fonts/Lato-Bold.ttf) format("truetype")}@font-face{font-family:"Roboto Slab";font-style:normal;font-weight:400;src:local("Roboto Slab Regular"),local("RobotoSlab-Regular"),url(../fonts/RobotoSlab-Regular.ttf) format("truetype")}@font-face{font-family:"Roboto Slab";font-style:normal;font-weight:700;src:local("Roboto Slab Bold"),local("RobotoSlab-Bold"),url(../fonts/RobotoSlab-Bold.ttf) format("truetype")} -/*# sourceMappingURL=theme.css.map */ diff --git a/doc/_themes/sphinx_rtd_theme/static/fonts/FontAwesome.otf b/doc/_themes/sphinx_rtd_theme/static/fonts/FontAwesome.otf deleted file mode 100644 index 8b0f54e47e1d356dcf1496942a50e228e0f1ee14..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 62856 zcmcfp2Y3_5)&LBzEbU6(wGF`%u_do$I-wUs=poc3^xzP>t859|l91%ydy%{4ZewH9 zLNU#OK%5)jlp7M#adH#VlN(Y~MSVYG)7F`Dsts8mQIv>+ztD)dFw+9OVG%`1 zdML`ns?&x=Qnp|IfM+dm&(}ePcdqmf37+Ghm#p%f+FVKQ2*chjkzF#ZB~9w-bef!xGBr6D7h{6UGOP@t%*!8rhr zqTX&D_txFJckW8F88SgJDOYWQiq1}9HpST zU`<34PZ)C!_3}_&M2)6kC53tq%16Wv<;B!kk^fL$a$g&o8ZTNrRL|U3FQqy}Aw%^t z%FjbIl=r0M9>Z`rYKq77t>{++@-k0@oM~*1+}p2(7`Q4V*n=HYq=vsI?g5v}-nP z3|{}}ibb1(*R0;YdDD}@+q7nj-e?F6nlWp}oWMD=X3yOms||yGW^I(#9B4HL0`>*2 zG{Pq6qjlCmi#Eba+D94TAv}p9V_D5%k=nR0b4*~E)oRv<#|upiMk~z0GGmR=Yz-V5 ze^pq5HgIj2Au?HKwVD>qoJsnJx#u=RZ=|+Tk5lVmJ2z1#N=q3aw}vu8YK7c-N>4=y zwHEjdq-Iky;2wVdD3u7c7HAy@>636rQ}I+R6-Jq%%_eFi6$}s_rB+ajpcD*stEugP zo136*FtrWZo1wQ}7%h+r0@$R$MYWppE&yKBVk^ODoieQIXI-PMCWPv3^jr9p7*cDDu9q6%xx{?3;;b@n3omixrmwx*YNmZf9p3xm@i;8 zp?TpJjUB@J0D^@;Vq@WEgcj}}s2gf=U*-SLs=qz||El20$!O-RlsfnS_J9)6lK^rf z@F|+|fem;DctSVzuQ6lCs>g=*`}C{(m-TP#-`gM6ukSbXXY`l%AL#GuKiB_u|L6U` z^xwJVb4z_|(yht2X53nKYvZlGw+y#3Zk69U@CS95u-8E9*x%q${UiIw^e^w<+#lK> z-M_Ej)SuN~+27uOroXrU-Tp88`)^UVM&1epcn{s0b!+*p&9_2tnQmp>swD94ennAt zcir7`_tDR9d~W}I%Sf-0+(^%nvXRn}u#+RjBRxinMp7g0j<_@8_K4p{{5Im&i2f13 zj`+pr(-A+9_-Vw=5kHRjVZ`?%z8i6aJ1^|@`u}w?=l`!y{JYkcahKF7zYy(4XAHaLAh7>kswf;WDJ8 zodnW*&mk}LA4ATyzs;HS z&jMIk)X1SUY8WQ8mk8qz!5gX{ac?|#KNXah-`{R{t;jx;+arrw4mTM?C=b`)g9B|K zKbe$=Z!xqbc>xxr!#G3cIJ_43-sk>0XiMsaXE3e+56S@N-W&nebhy1GS=0t{!`!CB zeXl$`20SDCO)=z#yl@A)%foXM<_FJ&aY(!S?qN9ajLc&>wDpF%>BD`=97%ujZX|^{ zkUJb;(Bvllh3Ak$Tkm1o9O@S+z@h#=rtsbrEayd0}DguL&kx00m+ja=Bpt$)C)Jj(+GE#@N5{qN_YooPx`~Xe7HP3 z{%{$_+eqqQIN>I3Ngv^P)=&zdhx-v8M)G7X!|w&{r;s|*7v>g7Gy(!cXqP3lRov@8 zR1fWh=MwT9Zqok0{>Y@@?`{gwSN{7?L`gvE7m2*?lX6LUm1893w2Pdz9?n{^!(W2e zdWpaFl9b@u0BLprBcj#q)KgjW@7iqlGG5Yvz*k2E1b+8G7f(?i1&vA9XxDLyUk5nmBs6~80?xA;He-^DJ8RN^C1NybWMO6ExxOV&s>OP-SKlxQUu zNxCEtRJdwMgQQb(MDmQ}tmIiqujCEMHOY0!HkBMipnS7>{u``WKCv$?i#JtM9$^4u7g87d5nYqQ>kup*r>4Q>U zI$1hRI!8KRx>mYFs*@&5bEW0dI%&J~sPvTdy!1usRp|%PFQwl}f0q6xb;-PBD%k|t zY}tI-V%aj;YS{+aQ?dwIjLaxYk`>BoWsR~9*)iEk*+tn)va7OpWS_{smHjSrdP+V0 zJk_4#J?D9@_1xwe?HTK7@=Wl|@+|Uf_B`o%#`BWri=J_T=4`v|*&UBhl-L)Zv5p0%+J>@(~s_AL7X`wDx7eUJT&{SSMK z9pETV%t<)~r{X4Z^SBk<7A}m7;^H_fm&|2x`CJ88%QbUt++pq*cal5LUErSMUf^El zUgJLCKIVSme)FQdBwi!E`Us0Q z%p9T98WOazMw1pS4`!>y8fGSUh&Ik-O^&x{%~AT;IIAusHq0EYwdzPtZ?PI<%-T3( zf;Poyj0@2lgv1zcHAY2Q^wEZ}*a%}ZXpR=04ir-WpbZI&wOaLYTC*`MGSZl6h=r8Y z4d>%cq(*NDHzt{4!;(WH^yY|Ityyc*hFL*fHES(8GA!v5YmA7AiVce8e_;!6kC&7Z?Hyy8O0n%G}drq zY^2^A7ORi2YLl!XIxW$Sg>0fe(yD_8(T0#%Z4_w&Inczd&{N0@YP37MFWzF+MkX06M(8q>71~9GMQF*2ge2%AwMG*R7f)W-5CO{_W(pxQ1Gtd{5P-01VNw=dm{|+^ z6%j+0-eT37Lc+r$ViLp5kx^l=IKzeEl&qvF4E7NA%LH2ey@o@10m4vTyAQN~fSq7A zx?gWNFHF`H8*d3AI~%7r4CUPWFH{<1gk*m_30u(tfF`iWB#nqQTC}hv2E8F#m?SuDFTQn3UEkkc8@TWC!-F{GC^ww z>q*$~q;*EKK82V{VgW}(B4CfL)4q56 z4)D)xH0hF~^)O1fFcUYy3iJruY7hufKutIFVd8R^gr`Ecp*I_TDL24)U$r5ORbRg-pCjNXR?8@hRjlg!)^B z(D!dOu%iM74)q`)qGOHW+C($Zqs|&;iLn3^gGC89>$Oo4U_&EF=f-R>g=zQ41JxU% z^ai~(IaX`22o=$0BPn|0z*CK8 zK%DqkW2^;?Z85-a0Z6ni9$1JOKmq#-j|FR7G;j-Zd_)ZF6-)}K?p{V%Lg*B4TBUeba0p4h(`{lkhnUa;!S@mlEwb3uRAAna%X|R34lqnNUbFX_%$pF{0bXxjWdRmGt^CFZcG*MWq&*% zpD-JDPJjsSWiSA$4WFQ~!(L z(g@%$q;&`!M=`(;0H;FcJiPEeUTy)bGXu%#O;$^MxH}UvXTe-kd`b#g8@(3xP*30x znc%M+5eqCjy*4&-n6xnX2oC%!5s^Uj?t@SuO@S=#uW(bx z{WX6b2|^FDjXG;w?7RqzWiB8Wa4|QJBTGftngtFZz*C@qy(Q$Y1K?iO@DUL*ch+1% z9wK1j&>$1McLEb&Zk8+5#cF{jf&aTxfx3yPAYib-S%s<1oju2WfRYkWB~Tuak9)I+ z(-1(skh!xT*2bHo!{JN-dNJ<8yjM5m zG60rH7zk-~uZGNixK`kLe=CruA#>*j!96b-j;Z)?t?(j4`6Spia^GJE{4Ojx680Zt zNWe8%t069;H$XAk92OS^LR}2VREDV856=$Q!%mO|6<}C_6UCa{zd}W<5upDiblg`Y z4Cvl7f*bc0-6U;-JxByu&zNWdaxxqBk$}(fNs-__0UlzBNj3priZ@%}*dQl4?7A@u zxFO-}z(C>X2fTOs4u7+;J0*%HiJsMQxqoBiu59bC{I)* zIwpEv)GK;ZbY1kl=qJ%1q5%)ugY$R_l;6D`VIDej?~k_t(Uq#ab(*CcOB-jjSFxlRYtLG(g8nl{qO zbOHT5{ZCLqIVOM^&rD@zGV_^TOav3dn3%)Nr_5K(_smbsZ;XR+Nxh{3(y`L%(je&q z=^E)esaBdKO_%0LE2WLn1JX|EJJNqkKa+kfy&=6R{Z;m$EI>A1Hd!`RHd8iFwn+Af zOe@pN;$&u7o$Qe8lVqKiD_fkJ-=Jui1W386V`Pb1S)E zZZ{Xs={O@7&!utMTpf3Udy%`wead~q-Q@bYKfGjKDz6z{L0&7o9`}0EYlm03m(I)J zmEe`?mG4#O)#laVb=0fN>w?#dUN3vS=Jl4>2VS3feeLyw*Uw(Rc{#l9deh#V_egJz z_ayH*-iy4Kd2jIE?ESR2*4ylzxhxHlZ~0u+4bSNe2Avwqk&^$DHRv=KS#CD3;S~8SQm|;x zN%uXOg<%H!6sOWpT07MECb~&~iaal%Kr~kA@W=0ly z{t+$Uxdi~XHN7!e%}J9R(_7UXGlAu{@LgPTdU`T9mC4D=%h61g=2Yj|)i)V?b+ui? zE#uW(1@DS-MfI`{o?I@T&abi;)~M_?7x@=n*uipt?Z;r>c-GlBp66Pcnp(J_b~W~k zJU4;W8IE;z9Xr-_5FpZ3`8gH2s@$By{Co|!66RIRN3*C1^>ST?V>+@U!LTF2up`?- zL$|?lw4^nqr~{nKnUu7&6b%lRrZlCsr~{Z@h76@~^htykcl!R`V4$yrCB3Hbq$wn746_@NOa-3Klzp2l^gn2VQjbAuo0?#JQLL z$Mz}bSE*b<%<3&$R%={A(pBfD{9}jO88R43TRRf@j!umu(~;H5a&uR%M853YmDj$} zIQyjET)Xy-no~>!4446Ue9XYDW$(ym^9NXsBiI!j&bBmH*VjYd5uCtsQXS7>`8HO> zDbN}`0?ouLy46Rz8=vn%p8Uqm@ezB}D0m6pght^=)w6thX?kgz2G3qG5zoOZl-P#$ z;62Eu9_V9|U>i5{jy^LBsJUYYou6NrldH_F$f?R#6Z}L^@PMpQjwrgSs={8Q zoOChE&E(fDVqJZ+_^S(9K%?|z4Qv@&$Gd6owP0l%>_y%&IxVx)7#jOLcGPC4#d!g42=Yrv!#JYwQRKph}ax;`_tIz`20);H(1 zsJH++i<8d1wvyoE7px2R-tQK>V~5{WU|KHT4=~~?>;J-zTfD!37u?D8Q>s%Z8#$yy z%h5wD_x>xdywB+ughWP$WMyPzRwT*3=TpiXGn-0FZKbMbDvnhisqR1g!-dcPCCh&K zU-?&5z+T@$$>=nPF5$IkC4LdF#0#)`=@RwFOYj1u#w%4&w-#zI;XGu*dusADPKoOm z8YZ0Itm0}4+W;2`1!=edNfwuq23(9Y^AiBwidZ$*g5O$1LZ$6+E(!Uc|#A>nDKry|{>zcC#+K%kF13+aeB` z9VD9p6UpVd$^V7B9CH{zE9`mIIchS3J(9JvNG|5m;2dy7E#^4~49g)Y8pA2@Lg!dK zg2BOf!)Nnef3=~Zrna)izq+0-OJ%Z4GBT8|Rd_LG9C|4SxZ~=3jfW$p9$pYw$y_dg z$>JhlV>uJMiW^X%#R@E9a470Q>roqx9zaWQErSDbk~yp(uQ0DT&%cNvuP5iE^LQ+u z26PNWna=x2;dpDwYtF2PX<;eXb5R_ zZZpZ*jjdH0&h{xRQ82^3_v)+fai0dznTkb#fpNA>TZj!$wMBp(y(a5G+OcF=O-IX7 zI1yn7^P5|gEmh6+^=fi-zRxzcYPfTi=c-TFqDL>HS)ZW?kxW)_xu>W{<;ZnRKUuRK|0& z{yIfL1XJ`OLv>qeQ+d6Ac^h59pu}O!d{)1 zv*gVuu9H;FWrMuddxQ0v#UA3Pz#$I+SM%g3Mhc$GgAw6?7&+-zJQ9zbG>QEFIth(L zBY*uBja2)zlewX3ESktVZS|5(mkM&oHz$Xv$b>E&ZkH^c3ZkKeyP{@`J>81Zl|K725KKL~og7cTUw&+r2C zUk9>oB)d(Z#5JNP*mUmDq4TywX6_8%+DKj@yYsN}P;F;x zs~Sy06X}*#uDQ7i4t1y4@e^&gBNN(#@|4_eym;lN^{dj7Q_?EUGMmj-qU3N8NR(vr zL5@U0AW!DyaDfW~n7L>qoU7ycb%~=uC}_($bO;~RAg|+gl_}Tm%SPM9pFM`C+p(U`f$Ogj39`p#D49F9Oe2B)Y(1=eW zw)bneg>cL|gV(T-@p*5{tE=Jcu_#{Qxp*GXIvt3kkYHpQ3rMZzl>31_u>s6-4t1k$ z+%4rq9}T342VUdi$!t^dQ!_JRmu7%?geCz#$k7y78#|!3og3_v;<;Rny}YW5!%{qk zYr=}g#4>emYj$g9vy8LVs?h8`L_|TiBLNz~6T}mIn`7Q#x%%eXmYM^ywlbt>Y*KQW ztPgGNM5|#@Lho##(bo(L9oRr~qe#cANDc%f=kjIw`MHHTDlBJG(mA{ekB4g&=UR+@ z#y>k2b08anAWukZCeRZa(ch0ofCOX(Es0wN+K`%qt+#QuZ7_-y0m}#2?n`dsD*wD% zU9TxGD=jNm!ZzETgs?z(%&2dH6S29assTs?*$2o*DW}7G$(=zkCn=n0K=g91j%PTP zO^O&KdH%vD8V)3XPz7L>;2B8w07~qv;%G|;IoyGV`0yOvTG|Z!pBsQ#a448*<@V{7 zdf2gEhBIedl9SbV5}wF0Z(rH8R)gfF3J%|GPxzE<#INuQA;=Fuj>54gr^1)E;a_nA zo)4mW8(@oc8NVA2@UCNk;D%})%w{#z2H@ok=K_g?v+@cKVge`%egi3pAfR$7s)V8% zDeAC@I!=iS?|Kv_iSmi9WFEB;;){P5Rf%dKM4(>OC~6j+5}g+P=`qz~g~xw9Zi~l? z6U67mcO<+dT5?YEC%uhsrC(z|gAE zO*vJ0Soy8esY(oZgqQLER6n4etX{4*s1K;GsNYi~jhAMuW{;*_b1QI4;QGKH$2>CT zA7i<(=f?Sr+dQskyn1}e_?r{PPpF*GHsRt#zlr~zR50n=$@LGNnX+igA5%|F+cqs@ z+S}6~n7(}aZ!^p@%4hsObLz||W*(ijYF6oN$QX$5KDr7zAHmywn^DlpJ_O|_m=Lh-A{Et-MyoGSNERokiok) zBnhB3NFqWKByj{Ii5OXtL=iv-I)VcRzH|jku>?yL&Y*4VU{JsS#rOmaeBcup%p(vg z?BW3W4M&OsA3!q@+*i8Vuj{V(uR|WXD@)op>iqEmJe@|bq0uaUO$x21Z|quaWJ_xUXAmZ_~hhx4bGFsw0wse^@d)0B zL-DjAP%gua%Yc&7*ptG~HMb>n%yYV^Ir+quNu8Y~X zOsAO}fxX6IZ{=QTe4}1~-O+ORpvERWcIMrGol^hUixhq6Nu^Kwy$j!Uz@hXT4-9Ss z-^eat$rCh}7lHN*%g%HL&}$Su8|+c)fPpL~YD3OWLx-U)QRDO)^r8pth-2Z11unc6 zgng%-ae6tu=(e_wW5-~S1W_f(E39}MY+<0HH}t}`?3|LK9Q9xyw$l+A#;7pmon0@m z&K*)1ESq+ndV%!`g!5xSUcduLyEub)22bZfY4K@?Qx%R1r~Nu#$Db%*0|u7If<;f- zZs~|Wl!(S*4>TT2kOs?S>p%Q{+3%`Sh&B5C`;XrEP=ho`23o%ajYA%X+By!lcghCs z(t*>G`3tf5iS25v9E+7>u>TlY=(eddSF1{x5@z+(?=Ec9VE;d`68_zm&3^yMUl5~Q z0Git}{%n4T8P1e5L>?Gep2ptkLk#cJzMcm|(|{by6<_nIywA5V(E)G8Gcom+3bm`G z563%p(Fbx;4q8>~c*j#Xi_WWWENE06tM5GgA^R;KAldIYrnu%>=<-IpTt0YLpJO5Z z7ka_5=ykNkF$!&QjdCo4<9+{Y{}-4YM?Pfn-Sr?2iLE?(P=OM*pd0w2DX66fl@N?-1iD^%I(}!F>Y{#DE3uA#DGd2hEe5<#MzbG*8eJ9rAVS*a7>X z{S`8p!61R*K0CV=3?EN|rl+Y>-AblM$u#nWsCFL|0B zfQG|)pZ4~I6JVA_-Cz?4mQ3W`hJitlTLhF*gLObK6@qDS+lA0x(4E2J0agpr&cu^; zCO{MD_+OBcSu~yntMX9y*I=$xBgAa|S3PuJ@wbLP?TrDFLn7oI!1w?W6b|fFfXJWR zs>T5*;3zvdesBW5jGjNr;s6}*4v+5OI|y>`@(7+gbxs`u84}+uPY@vw00iu76xufo z;xcky3)%Z&;>+Yhm+!$8%J?!scS9CB;mhtZ2z){+m9XdqJo!a-xeFw$i9EJ~O~`HB z##U^V3ifpbIY!5;!OjkR*D9R>68VYgd@_*MUtkE$$-fkUxcc07c}E{~7;XvDpX)Cb|1|XFuvZq>JsB#)PveQe{;jxBiN^8{5K0jUrRqVzDg~18#Ciz@>FQUv zymy! z&*Od810Fl&u{>a&NYRqnoKmjF>yBohOh1`&!vECeGZ#-?l2ulhSKE~}#We+0>ac&U zetlbytST=DEOI$HMPT2?V*?FMarLpa{zkN(ZYfS}NLFDp%px@Hdbg?*+HWKXULd8 zkEK16c|6zUdZ=x9l%!V#N--vs)1Y?7`7@ zUn0ko6}wEv0^s#bf$8Y;nt{g#G6c;O9Rxkp~37xp$cQT7Cj!TNVhT`^& zI&4Hw_&KKS_Q{rzgsVT3nbUxjS!=s=ByFFeTQM)>Kqhz5aopk1G=ntHm(bZMG8dQ$BhNn1}_Fh1}7Nti)0c zsT@ogRyZ#PtP12$h;{@IwrJG15JZTZim@zu2-s#H3a(^DF9b*f!~-`SXB4TWX_;v% zT*RcM)i;-FDx{sz1Pp>3(E_#;_tAw?r_B|uIG=Ss?X=o8Z{QexDBE<7`o%{7?Ua9oUL)qyK{_Ai_VIOP#S7N&Z?ckpe>SiZNU9u zm_q=i4bJZ5(sVGj!PB!f7mo=XL{82L5inMgk&7V{T*SK~8Nwgw=%`(Z+g00lwVjUA zU=<3WUD{k?Dq6tekKu^y$hJ1`S7AGt=)v}92iHh2woB0rmiQX{&w_)RM|6e?WpRxG1qwgX1Z!msyPF7Ub7d7P6Vlc}3fyKQX z{8za}`FR?A4PT@4^9plwl!99goGkcu9*=ILU}-~rO?{;X|K@0ah;2_8fQ@>SAE*Hu zm0Ehb1*Q3A1^#G9oZ@s=Z~7@U&T;h6C(|Pi z>r_B2x`_Sz(lt28)kCN2v$jPmT?xPQJ9rqtDh3Y{nDII?+Y{^5u5Q$qRByH=X89*( zW+qsbz#re{>&mNY!JH4q<+i%|_71QcjvmY20Be`s_Y9ba=Ca)^9*q@#$RFGQTd(6C zD%WBR767mVjOD@V9ovsqp^2K>2HSzmI?N+AtVd2c@Vk*_I(IXT8ZbX?y>VB zUjx`hNA3vvLF4-_R%7+suyd>U8$5c5_dOFpf9J3&TGE@)C^juSC%r(E5|OF3M9T2A z8F=ALyha5M-v?g!X1a!$w-VTSu>AxDq`vRwfu|HHXh4~0-SQeQgF!}1ZYz~VPn9c zflBaRv=`n3Qn*Usc#Ek45eF0^LSR7lb6Mh?HnDpSg`cyk1F(JR%Ob?7Vgyf{qpy_(zgvuS>Vj=cLo{pa z>7>`QufDBBFQFGv3;F@B7jX-I>9Oo}NgLE_GwF{*7W7V4osfp`C!~n`D{ zw)N2Ge`)&ziIhHfGEX#uH_&MpKf(LB?vesIuAl_mzgzL^#-FF3QCH;Vl;)~*24l45 z5hQEJ5XpdL?T;vL1Qt`RP}9%>a6BA^|X!|NjdB_-jxI_CZ_l=Idxa zYiv&H$kZH3Ka|;-Ec<2Ut6=@}QDUDhSUP#7+LCO}G^NX|nW;%eh5%56KxP0ZU4iv*KA7w1xTwa7;q_g#*D8$PI$hF$~8E;@fbZi2er?M%mste&UVe zXw>l^U;pv=3AlcEd7Zho235`~JX|gRb zKMD8VG5SSkg(gI)?#yI@*VMn7sL4H8YOkr6)!UoP8&pmwgM1I4LNhLF(2)Uk4S`SY@Fxs`Oc(;0h69>rvKnWwBS-<;xgEr(x6DibxmxA2GpmIW%yoQloTB&TirQB-&)3iy;JKCM^{C2fZQ!-8vmGcos@_>` zs?06jUahZ9ZjxoybQv>rMOIl>wlW*yIdawc z1=gI%9Q>fsugF}o-=uuC4DGI?OOHNR`nu}nH;VJ$(-gdSwdhq6NdZ#d`u?6~~Z{9B`t z1-wD7iVv{1TrJ$)^S%f-D(W5jPFReasvb;xyJU+{ge@XLF!sW1Y>t#pxHf&n1 zT#>nH|1Pz8XL!_BlgzYrRr(xN=QBka^;w~<(os*A)DqVV3{f`x~wu*<2rlCTY(;`{I>jL zIg(cYQuReK+EM8DP0?Fb7i+$1ey6Rcv#0a&>5I>wJl%P&@mbk{muvs|59Qaf*EhbW z_U+#I{v1%Pj(mLjABWnTWxgjboH*Xqepc3gw(i1Z<%PWN^t0;pv+-Sq_cH?QCUG% zdPQ{U<|=F`!^+a9%Ut<>^NXIy4^bDT=A~pM$7FvlUt%w-s(;S!0?Is#=3GHno8CWo>lpI)FKe$jT79zST+OkX zwj*_?YR}i6x1XsyQCHPo(E_mQ%IeFS(o1y3!G*H?$*YP&RM{3=S)>NP*O)ZkUffX9 zT;l&u;qy61(`3n|nI*aE+#T^)mAc-5XO|S1md4@P{+a8x;&v0(YMUovWmkUrJ&Pu zXoQi+mlzyVO8Y8*2502splvA@57<9pE;b(RGHHC@z@yN7Q&))11UB+fcs{K&H5xCf zKDlFG%!H&Hbw@N1lr{f|?xO7oSi+$#0O~rDel$eo146*S?V*`hq6(0H%NP%`pACJIXr6*_&%wUIKAOx$>g;p&(WnhH6fYKMq71sza*elGHFyzT zNPIVF5n6Pb9n8$&3wSgMoXv3B$C6Mh1fewGk~#e>zp;A#;b65xG}uIkv|TbiuX_H{ zk&Epb2jy&{55H9X#uX)4CZOX@#Zq2#rw<$&plbvIOi;aXCP=0bJUn3c-RxUQ+%1X* z{>fL~SNpafs_Cq6Q#Z8rzSI7;tgaj)tW-6%1zF{q_Q!hHHYCdG6KgDHrSE2tnfv2@ z*#3!n`zLrG>Rg06WEV2S+hbHQ5ecCgnnkz+d`6wy7t4G@cPx&bJ`uY72A&*2kiR() z6bXoV6U+i~@qib)t=M{V>dOo`ML-S4(`fXOqhDdqDM`!8!N1|({Bm;AN^(==Jist4j@u&|VHkfH@Du$@Qy2AQ$ zyS=B!4Apu-Qm z??=AR!Q1>cw5nx=g{6hW@|2gSS+|amKUv#qsXH{+_oKfB=iXcIlJfGBa)=elxEVFOi~iUHd&I=pcASXucdT%& zI1%%L?ZgRx=S$9)Xz&P5Vg--jbHH8UD3D7bnD#I%oeT0z8Q3~q@{90U0|W>Iq7TOh z1NXBNgAP&M96-(t7<7ax5CV`lsF`;0Kr{)mF%V-31dg>2)dn!v5Y0Px-e3)^bLR_u zAk-tD0EPi=Wb4oq5)tMOdh~ZfmOf-|vv(;;YY^!I0+^8?SJRo`dC@ukP#kZu9gS@X z7R zCS-&8Ac`H_`5nyExf3wSe-KjId?+zTryShb!;;qltDAkOl@Z$Z084;cCoF^bIV@Ee zi3{;N-Umb2864mq;zq|m6=t(Nu}cM>#x8r?A+v@+MLw**Gn*WdKniw(tq8euTdsi8Zq0W~rrMOat z%m0Qa9T0xxB&|C-8&94BV}cy@fj6lSv`8TpH^P5~fbH1MJPwr1O5YI>fq5L>0N%zO zpw)L380LDgt&xsGhe10dgc}3xt5^u(a<_ofE8Q_ik&>4J5mvKj)0vr&g(IvQf*&EM z=Wz@dRD$rSN=YG=v%iJN&b$_g?5u8v$WA1*LC~f?kA!H=1=V$Z2@4m*i z!)jf11|vI|n8CTKI0gr=6lqxSh(fRxsD;zUZFwYAz1w8iX;p%+pFb`A>8H=%KcT*I z^vK~Cl@~X6uZ!LX%cM?9PfXsuNtT-rdYCFNudJd#gZ+NZs4Z-@H~OP-Um>6O(8DSS zoDRl3UI$DI2g5tT@K!iGt*{MN6a;gygZes?bp@Y!A_yRcap%RV1Aj6_&7Kx;2d?wJhEtaB~olpbt#z|334}xAjCm}zo^*y)xKLutVI8W?{JDyFB1Q@ zZ_8I|ht9Q2;aCbEKK)ESZ-CDnes(Q&ErZV-ejfVF;b+G(wNC)OE>Uz9__G-Nz3=RO zZ6z2L7<36;qB{jz2UcO}R4@MkgsPa&d5c9es2Nn#RuU84VO2XdgMo>XE1Z^x!2y&xJLkH-3zbN3m%kH8KljihAJNb-ug>0nsnuBd*6X?d6;)zd+r*T zW2CS(mmnq)+H`6@{E%?I6J&tp0rb`DATh%L%b^w|O)E&6u#ND-5T68qh?oB|I~X|p z2@cFJ@H7ifZHSfthPe--wSjaqP6Yd#K)hyrfmUFjYbnTCJU^_5+x3N53hR# z%hh$(x|pT}S$1`GUZbk5zWG3NVQWdVrl`BPyIbklk4}H?SP7qr0PoF%gUtaaGMsqM zLWgx1?>y+dy%z!%qyh8|Q3L#d1ncPA3r`1b?*eB7@SU5^Ai{UTK*kTiV-(5hX({SM zd~#Y-s|GzOZEb1-=Sncs(wLU4DMm9C=_P4d;9uOpB&F3gYEqmc8a&F?73#_=d%0bO zOpM)LR8XaQxY8$jL6_Ykc&_$lHY{ri9Qr?lgOz-=rM)PkfMXZbcU8L&C61U zPD*?Y2U(X+x>f4h?fglZc;v8 z4XQz@C<#qQf2!cj1MkmH#g|cl&Gf^j-P?oJ;GFSuJ$4<3t(D<3({U9}#P2J0<+>`p zx+3xLwwx_^=b~}Sgz9{Iih9qH1F>&>{Td2=L3RG-`qbw&u{VB6y{SUe(A4wqAe9D; z`f9Wr?Y)Yw${Ma#zj>8d_#v(fJp@s(pg{&fWG{s1xT8FPC^iG04cu0s8#oI-dO3!C z)ukmxrS$QQT{BkW8dtF1<*URuP!?W^j$vPQNohq19dkwZ{d=g!5q!$w3*la{n*$Ow zUgQWyI(rdKs&+03P}IdMxon^wJ+EegJG^7B0Xxyc%CLKZ^bQ;6Uhr6Dl5U z*PMIqT+i`;$Qlk-w;v`8L*z602~b(lJVNvDvqSXW2=x9Z55$h2lomT!MMg4@`|!bbNtJ)t8(lGj!JyO57)!Bt(Pt>F0vKDH>o6MXX+Gi=;uJYQV7SX zDF7jBiywIBDywp93TsRJOKtE~7}!oUH*Z3GK79S*zYT3e^>CeVRgw<&V*iqIh%Zr9 zSC>^(g0^$Bwx+V7sNNq3IoG3kXx`16S5eTqtNx(10=0Et1*sM6Fn;`rt0#cl1;ImD zSRpS5K1Zw^3dHeOM zu@muwpA$d5brnd044QhC_)A~aod2Qw`&c>N|F)9h5%!0F8W~ zOX7qE><;<;HLE}y1wH9Hs3Sy80@-H}q@3Y{UXUS<^Hw5*49O3md?gc|=`UFU{A{4D zfsjB9Qhx~vM5zLGEd^u)kVD*p1(97&Lo5)Q4r>Qeb258EQC(D1Sf$265MffCpAA7} zu0Bx7gPCP)Q$bU99Yk<~t)Ve9xh6@Kl$@ImT2Y@%PG@Hoq@^K<+=iYnHXFSjIS=0spgd563i}N>f zk6XpVsBFQsxjg;O?JtUpi3k7a-Q)VbjFxT zvu)6pLrfF{lxH+gg0LQH5P-V>h`o9|_GVmVuA$1Ut2S;}6C%w{$x2C4(R#2LTireA zGXTz?AH*3;N=>Ee2jA~L^BMn|dECX&Z;-VqG#0AMi!9bMen9!STMt!W*k*AJ@r}uQ zOwxJ#0$W;D`|_L0>bXB)X}$J3c{4?dR8nb)ib(I>Bhm|}!`AHMjyMjLHP^%~-Mo6` zw)brZ^7oZWu@o)zM-Yj0asEV>kgepk&VHgHWG&VNHI`!fX8XTrvGZR*G;ak; z_W2{SfrA;dl|CgNoxWurPdk&P60(Nu^~V4|r@17&e~&0W^3bDNU~(%E9)-op%uY-c z!!*o*9Hxl@^o{X&85^7#&^;#N47#r>34Hv6m?MO%%Dp&A&K~$gK==z0Z!KOreIzYJ zA#wr=C8jcPn25upDggj}Cvm6@vF=Xfc`&lY418P3?p#c^TJ*y6+{M}Iawy-Ig>1DK zY~u>H*|&zM-k0?pe*4j*+qWO>+>w@4$0gOJ?bxYe?;qVB-jj3QZPzMy(gsqpp^5YA zFX&!-O}Fjd=*mbQYb6XH(N}FJ(GedN384c>e;Q10bUcFbZU6}(KwzBws*Q6FYaiCZ zZ#>h|a>fHt=4mJiy?OObZ6j8`8bz?L28{2 zw?jE)-rUJk=AOM;r}^|8;JYqI*Z+LN$?fbzkl5X$ltsyf3BcYCtWMdHv^{aV?~eVu z_U_y-&9MQ@s@g$iq|>$<&YF(d2q6oj0kB)y(C~t={B60uI#4%?j0yP(YC21tkd&N| z!6z;?Xbnq3Q^JzN5~<{SpB&GQAwU;D7aGMQZ2-R`&61Xr&NZyxwPDBF#4vqW>NfgX zxDR65@rf!rQ<9LESY+hLz;MUbg3zK+-;i~|8$#AgK|X~5LkN-i*M)PyeIgfQ&ov|Y zKxE(5B-QHcQhlqzLP;5J54mbj=OuLx1%qt?^bw&`B{My_)@>-2gp*gR(Pz9{PZ%WcbGeJfMYUJa}R{xq( z!4Wm+0@+>hv3$}5nLGtwdB2d)!dJ|$Z2BieX4oF0#rORpS2BDwoUT1t*y&<5l|L z6PbO#Ve63PCayBPXnBxIzSa7(#u8(Wjs~D}bToL~v?1%ZN$GZW z!(kqL9+nsmT)E>$aPm%m1+I3V)#N2Ly7HrVueeoKd$91>F;#VDO?nmAaHRC?IaN1U zZ&vTC^W|P??H8 zt(!nK+>8$!$*cVzZrvGPA673t_b$aqj8zAT<+D#>a3p8$?kzvX?;}qU@g5?BC5kU9 zNte%;U|{64t-UaPaW-@T5p?cToA-<*J~B<&ohWw)w!cW5@;|KTS&P zdM@^C&=Jm7WvQuF;Sk3XkA)rN%thJ7MXHv_mUYKCt3-bAB$=I!*|QU!uBKhZbP#=E z{Sx{zpByqec&nOX;AWqEGK|~B`?q~EWY@agEBCD0xAy$>Ep+Iw{iNP-%OAfs{d|!=I z%ex;^FJ#^vx*H}$k2uZ0HJ)?}>4_CsabMZA&Jc#Ys@R)F(Rw9Lnly(JKiTo73>MNq zq;8P#^nSs+0)*yGh>sxm?VNs(q>+3~)5-AR<@jg7zvM1>+fC`5PU709ONw3o%D0y+ z7|mswByTJ^_0cCMPF%l!bkVeIUby+#Unxi=_cmXCea8A#Yhts;gSNn2s#9Pz3USvXoF>* z1qz5+X8?tr|2n`1gQ*WEI3#r%uqSZ+d-PuzdxCevO7{WvelUFa4`d{OX2>D4?1)DchD@fD zkx%dkAp|kmQ5vKI{Ml#3kIgO2u;~m?lEMpM-UP%pX}gRT#qSnQ+qz-D6$q_np!we% z#v?kG2bBWvH=AG#w*FfNQ__W`u+YjV21KEFU3k~oQ%RRJQ(xlui|RfS2y{pT?e^Yl zoa-{#q3lO}fkjxdhI{XB1CWzLfSViu(}yU&meJ<>;tZL)HC{G=GR2dFGCGgM(hcOp zc<#XBrr@#!>B(h9OJ=BM1i{H1Fk=7*NWK%0{1(am0WAXt1hurZ6dgNxgexm*+I8T# zlzdnWQp*O$sKYg~>3mgubySt5{$3Fhd@G5fmb|miIhNGRb505zc}JO(V|1k3puUlv zVK8KvQ|##wWHRMgrSb{-)fbf+_Ed`@!;qN;Vuv*?H#5f~&5~GivT_Y}>8uM%b55o; z-2&{m$(U)(uo!Ha)=Zn(Y?0OnDswC*yTN9#rXh)#k(r%lO}85C#+)1}!T?>BW?Q-) z$N&gO7?C!&r8$gJd2c<)gch?+dfA|~r&?1?TuPcDJ&%jV_J>m7EhjX#&CG}$0P zV@ffmr)Q^Sg970&18-w9*`%(;t~pG_3l3q!?yMtxnd!T?G&{m;R=oLg7VQ$ITGp7= z0HX<~kKqLViyF`ZX25vy#L&qLUWauretq((&qI0l`2SD>mMinB4LhRCn7V~eVN$Fu zP8}EPK`3b5+K*vxxV7R}@zhr)XmR%Is!M9}cy4h%WV1ykvRAQnh@pe{fv& z4*p=(dxuqWYvqlw>o-&+{ZrCN-X*Vc=MP?M_+-0u_wDcZ{HT^2{IRNumXT-n?|1B1 z=UB5$IlSCH!4a1o75#4VyDL-+@C;qngg&E|n?r_%!H$Fxa>!;Y#Q zJ9
      g6hQci^?554dATb{-)j(lvyL)qjwGIrcmNyA&2j9QlLX#>zGk0YGw8Y0t7} z+PSpKrBzXR^BU&X&u^5LYzx}8W!6yo_5yY2rrM%#o=*P_5TfpV$aHB!P1v68r^wsi zT~yTvH^kL(o6l@H7j!ncBI0PIU5a>aR+@U_l(_iK{L;vv`C;!$gXTofeoHlI-^ltA zT-B`Yb9QUn=r{!HR+Diroen%7dND$}<<__Be^h^bp}gTdf2j6ML*-FvabwA+ds(pZ zfy~tgkh^zYV6#uF7?F{H%UG1<8ZSdFz){i9u6Ud{1>I7Ua+C0nKW(N#L#O8VmTb*iYcu)G-VbL#WM zVB#}Tnp{>JQ?dU;^5Q{tb#;WkoZk^g`b@ONNX>?@cw$|lV z&JBAfW_sGk2aaE^xi)jdl+Z~D(#vy3?jNKE2l!>$n@$b0gjsPmDvM|;F6?1sv2^RQ zIPGi|?RvKFzvprb%}a_`)ksZQMw5yTAzf$>(l?k(3k}H#QAb9ZEm3?k?uKUuk(V;1 z0kjJRW^{l$G%VY)jeiZi*l`QV47KnB`AX0W7+4Y>~o`MOdo|%T7~g ztikuX2)V9J2nk6(w;zD`)Jvp^Mu}>^E~ZbSS; z*Zo|tkcpTS>s^~L9X82BTR}R4cv3St*PGj)R#a0_X1e$m*diS>$m?OMsKW65c8;8T z2qltca@XV1dl(1Eoof*~XJi8x{H;z{FSP9exv)nilVk%B2LX|SCB|DoZk;N_`j5Ha zfm4p+ZCKVh;WeoWp z!RedSOtNVSZX+jr6)3EAuWfXHB@Hz1 z*tT1Z%x77N9dMLF)@rHLlYr?8v#Bd{f!E2LX(Zsj_iYzfEdpHoG0XPApRP0j%oYmH zH372)r{QV58!G6OWQY(cDz%mumZ_c9;s(E!38L{r&g!da&(FCyXaHh zTSq6V+pEPB-a39%*a-$kimsk%@VZH>T5DAQEB)a1F&9uXUySp`T0k{@LV^lE`2 z)43IDw=N!0st66~CZ0kgZqupf=+wI-NWS?J>DKd`AvZoHk~h9?2HX3Y1LW5basVP9 zQ)yo**yCs^M#IQ5Nb|UVQ_>=`oZ5(p+IL7vwS?Gr5E~-s_*B}>pE|w<1xf*0YgcA) zb+^h|zWy3{CmmLekB({(b8c4RO;#JZO1@Pg9MStcc@vM`bLbNKZ5zFcKtUEbn>}!p zZGeE@CEuw?1bqojhSYJ^d`n@WYLZO8n}rw>Es0jd(eU;o`W^ijy-SPeHf|?YHBcUY z)exx$>suGuI|zWULPQ5 zbC$6U(!zYx@m+ZgR#f1G@P}<;3-h&yRYcXMlR3+L7SdU1o=tqqqPM5j+R3bwK1b*r zTUdEiU7Bxg`gVI+Ir1)?57IN7D50=CwOnnpXJ^~^T6;x>t@a3+<3naGME9|wFZ*d} zwF}8CA2R1it*xTMUh8Y~{4{B|)9fZ5g4hilQ#msrtNTrC5pzoQab;fOx*LftZPakKsXgDT($l>er~IP`$3R?+c;=JLVI z1J`U^Bi$S_ZTK?gH^FH_7yfoXFF)82agksD$D=KztGZQI*;IJI@}88uA%@nc6z-8f z&wl1HB8TrijVRaR_cE(h9`ZU)Kc*b{p2ZNI8;4W}8t*dcC_(EXhsv|dEoI#5YTenx zsv28OK_w^O`g&kP^nnjl4MiVR*0AxII_LbAPcB~g7-E`YdF1Pt2Yg5rs{7X(Zf!qC zMY;m6Kv$qEifCN8Z$7x-8rmP{Gw&kZa0ST8=C{0gFle| zICm8pPgQEhS_q(TthBExUc+O2aIMH-yl~)+Nh$kX_>Gp;g=;G}NYP;~* zEaC8zOa>91Zz8H*jAQmxTSL=B{HoWhEVq`3j^3St>Nh80zDn|K)IayU%^FdLA`hx?}fepwKVnEe6z~QsH)z!SEtlSJ~ z$L9`@rw}qxSe0ZZ?E;f?u94fn1iwd}5N|Rj@NzO|L*?4S)fSvu3Gv4ONTGAbVL)UE zVz_0J;x()6E7kOk0N60YsEUkV_2XRrgJ6v5MkzYe7;<~sG8Ju>u%5nx=sX((KqW6X zJ*c|K?fawt5$WoQPW;bH1;di#y$@)YrIV1;kJTEJ}_u) z^m6s)mBkg?JU@AF6T54s&A#|ChY@*a`T(j>4+y$;YdaAgt1jTH3#tpMicU7-E@_sw zwtRo}k*Yx=|D?&OK*%B|6xm<}E=lxPfoPLg3Koi|I5P6v=niqTW1OA}YTNLTi@3Pq z!DSVGiT8Rc*ojLFcL;vzvf1T9JAemRW@W%KrRN}jqujjEH*af_w`GD! zLeWhkmhC`eN@d85;c?QJO>>Spt9L=(xV;sbuabP_HIL-T` zC2wooCJCsBb3KFN>7F(FNn0GrJWYBNxzRy1Ao~`Vm6sMD#;yUR^Pr-vx<5;^t9Fw< zI15L}l*a2fQ>s4LQRg^Pk$WPtf=C_mo3HHFuhz)F#S_`?E>q^)kyOga&vaxYrby+# z;A4ov=A;=x&dA6}sf!Pci8V`eO=0obsuV*~R$5A`K0i7>Cp}STPfo~Biip)0Cudmo z$>}+e)=SGUXBQ+}Oj3g}Bg3G!Ch8MXQj=44shP%@*rc$AG--C$W>YqAPO@%_EKIhh z@5s#0EHGuI79_?S^YwPAr+a!^9Ng!4z21^pnvt5DWXd!o13qs{%-b3pZT6xJ;U2$c+|=1hQhFf@a#}&RNS@GeU3Vl8w=o zIr*lH%*;$6$AWqWc~JfQB5#5|kBoKt4C zLEIt9o(T-WI!k%AJ-0R^*MN2g9M|Wk7wF@Y?WV>QL!#7Xu{v_q4wE@D$50ejb1cUg zW8V#AlRYy(JdqtZV~;*RIXfZ>Qpa)SiShVk+HQSHat1K=2?^2Jv1Yp|LTAii+5*N@ zW3pLqNG`QHwxpRVEu~o%Y2Fr!43)Ura%|<9He*40cA`a}6JHosnrksvK?)Sxytqf7 zYELQ4&CAU%w^)myV;YoMs>&<0m_~T{??CX!>wb7{u-r6zd;(%Q zb;&X5_$@|Tjy)&G?l725`BgR(epg~ndQM7yW=@LK4so*Tbi1)U-xM#+$uV29RoMx) zxKcB;Aft_$TzX2pImM7^3Xim8CKg9##o}rMjWaDZBNaa{Gs6&LFy)!8`MIpaxQXe= z$DNfXt0^yAWhyDnHx=V%Vq~n+;(~(wf_zJLW|5&Lt2U!1JH6D51T;>z)sAG49XyXb zTV-`YLS9l>Vxc}KH=`gox1=mTs>D!gu%#F3Gjb~I=4@$sPOiQ%xhT0R%@~zuv}Hmi zJ|iCyu-E$2ZqukHoZ0wEe&V3cm44zt&~92LX`DX7>q`3KiI>_Ikr&(FXn(_pW$+&% zPp8p1$2rG|oZW2*U~mEk`G&}0v*+il3ep|PcCLBWz^X~= zbeR{?1gV0#WITwLQ!n%R4F%1OK-O4fojrUR7aT~IEJWV$u>)yb7AEy171>LcO(cr; zR%N)%>FC<=2O$xv&}nW!#3s(K>sKAJ8E{a=Oe!PUo$TX|m6S8NaajjR#~CXTl7-~I zr8AHgvNAm`rpg7Em>HJ}Kde{7a4Z1_cPiRJs1AU-Cp4{F8vxyH4{+Hu*oC<7W#?0xT2I0<9ZouT}fIhTo|C$-CFTB zU0irFpRBWPg-e02eSp})1OGvj+tbBr-x`k+NQeFdNE9_7QP{mC3Ol4p*_On!7xu*K ziyHE(jJ@z-&3L{+!%TgGMFyda%v3IM9OOSc^v;;7m92wuD|`>1YSFcj?|)ELnX4>S zT>Pq)sVk_u*R4o3m0M`-Xxio8vR`?k5`X;ly+eOkq^>jVFFaAw3Pcp0r_1qpp74QC z()zPM3GfJM1^mf$v>rq7y?r8L=59q0g4Z-cdBZ|#0iBENHG-VwcZcs z)1hR(d{QTQN+&;26TEgZUL%T)2}=o6gGo>ZtkxQ`mMOm0)~a?DR99ATn;UnmJFb31 zCV!#R@pU^kH*%E~)%iQ2Xqy~U#*=k)ov17(FMOM-eZF&nGB`;W8O1ej-nxIWnt82@ z_it_7%tuD)l0!P$$Fb=;vhKD9NzT6;Swq*dMxdJOlD98Vei`za_B6+~5}jHwao2eD z*oi^&wfwLNH=?g>*KQ_%`$LuPx>02)`435k8r&|i!pVE%qzRGfK4EGlRqgevv-)QHB|hY+pxxPGe?c%I{Mj z(5J3QPmSoe>s9rT@u7?6^Ya#kjJLnx=zXOx={!Zc;MRlSd+IaC^D7SWHdaw0ophVz zBTwx_yG=?-PfJTr@vT_7IDfwS)xNy3IsRFGx zr7EUS>PMG5`zXV=tw~y;me+KeHKk(zES`4yWc_a!&q!UM=*KW(r&8@5RxxPFhRTPz!2)P|SfE{$Sk_HUeR+pNao|~HMn`t&? z8!aihJ_w?Th=_3j;U3Ls*ST9oLYo`J$m`^5D-?k&Ilg2H;e=B6Kuk>3u?F)oPAi*| zVID(ErQ?m~wfsSopSUtn16rkc-I7?{I-cBsr#c7IZ-98=#4Q^(@a}TX#EKZz2_XS^t=*Mfh+Lt0|b$SfxsYJDFlGY6(B(i zPQ~LkCDS_qEKE)Yd%u#fHRyRFclCf&h=n}gIS0KqVHGPNa$NE8WPtL{hFkAk;*huf zN_1e|g6jEd`qc2@^eJt%_P{z`7~~!V8Y`5v)Rkw?R^mC`#=8dzgGBKq$(2>A{X2K; ztEx(gFG1+i{S_n>Y8Po$Bi?yu#Dayj`_^;qrOq%y?$5UhrJ|XaZmqwg2KDe6 zJO=YXLO{X>CqO`|kw5{0-Nfv{)E@*mw~#YIS{Z{hN!E^K&mBM&?0$D+yaf*+TvD+= zE}@7gyXkIGVPff;Xw_qd#O-h)a7wk_xGBPjPh*u0Qg+BhG?K;+nFvhnBE~_3{3hd= zx!U|SSq|Af$eSY`s#R*SSJ#d|z*#$FEl~~VFN-yIMFk=B254^bHbmEpWULknV70Ec zUH{7$PHosfw__I{>5OU7(eD?cc(9W=%JEk5pnJoka`Mb3K(L=C@|WA>)Ahm&Bb8TH zo_MQ-`-wbSIyvo0!(cGXmNmi}fym;e^y7@lMmX^%$HFRytD^W5I(XkHvnXWE#+fK)l}dg;M^M9u|=N`R9ecJtfHd z%CC+uFRduf$5fFd9&H*uTIDa6D<BsB~lLv|aP6mKD*Lng_kV z@{n}pp@_prRp+XX9@@|CKXkF;3-#AmgJ+%RcW>M?ZFip{qtCbL1s0K|#0>Do`-Y1t z*SWM4X$R8kCf3X;S(z&>n5ea{SJR2~#nmH*@{Fl69;N5<3YZ$7pc zo#amz9;-eE!QZ{xYpNR?t9KVSNq1Z+y!x4{(O3`UIWh;C6bxe5v3o;)9Db)eN*f$< zMv|_h{*;^L3y%1SdMa-kk0zApr1^2S$+WwQ-j=*<9h| z{ik^Hl=|me`BklaYt@BaN1Kl9+t*xouyj{ZbKY@09va91soatvbW1JEQkiOv6@{vD zTcN|jS*_cxAJ}(h??43)DLjZghst3r&8X#K%`m%~#4J-HZ^6B>pdhn2tIQs#UZW_8VjT<+r(+%4s}GyoysBgnvww{23nm_@wD$26ukXAae*n|i z?wYOi|C6!2{`41-K|P@3o>aimrDQ3BNO3ksw`BPyKbH&tBMg;}P!-bj1xXxPN|!Rr zKOIy`8*Fwz5$;zph?F*PE&W`F$-Lt-fbM;iv&rJwOo)~}U!aRGki}&21(7q%J>s~m zJ<>V!xQ7m`0X(hy_Z@SyoWQ!eF9Y(@q1+|Ou@ze^99cvbi7b|4TaKCx70Z7G3?1sS zj{BI*8IJfdD7_vg_r_&WVPOc)BH6!Gq}Aq)ovea(@x-t4j`1yGZ>~k*eLnV8^5-5j zL5p(;83RNq1O1p`FZLr=#9ZePYZqiMKS5-xn$*x|IOD184~x!8vx+Z$O9U?LXjUtr zJmQaT-TZX-!gr>;`;x9dH!AwV+h40mpI^vqvJHs?F{nywXaW+uljy>?Dwfx8;EQ6- z>4vC`gw(){L_-wFt9GgX!6m>=G0Y}7EX6`65YZOUK#+n?)3G#yX1)H#q2t@Qcj=Ur zz${hVoXvAWR!Ad1{Y?Lb+7sLR(%FxUB0V5!&=-$v>^;jvyJR^~;5KH6(@&@TS#_6n z{2S87g&)oO3?1+K;kP%gG%lJsb!9Kz0B$roeqBvo{ux02tz-;bk>?>z9Sgr|Jk`Ec zv0@iG9%oL2v8=)@7u%~X44i$K{Gr_Ze(D!^kV3b{%$a5Pj}W>TLSREi+|z+V9Zm`XGsJRsdT*M=Y9`QpK> zGvpy0%tpYX>9{W*C<9C$!EYJTYomDNxjK=7O=OH(cw0=>GoV^1E(|Wrsf?ChnbAl) z4+a-1JOaH|k`s$*qe`2&aNAOFFaeOEj=Mtj1rmFKATL9vT!#%fb36t-f-K!nW=@Bx zQv&>z6dH;^;I3tzR*ez9o%Z9k*h+ipG=bF}Rldk|7Nbh=fDuZhe0GM;K&{ z^yG2ahCW1BLCSD7Eg{eKy@c;8kmuO+mM}JcOz5qBRmaeR5iX}l?y=!TCcPi# zIi#V5W<0gYuAXIISed#89JTv+(`=N)g~jW`BgcL1gFa|PMC{fA+|E#52%k)c$U!2m zw+&D;x?U z3M~MeY_bNN{Z^s%E+8oLG)%j|!QNmFoh5tx7Yp2UZV>=zRJdB9M(NhNwU`mpFe4%u z!z4_Bg6r5U3!4e8uqh6(a!{}j!N>&035-k#uX*r&_~nSmyr2O}DWFG^#?|Ho?NSd{ z0-ERUHt3-%9=G9Vf>FT4$1#7yj_H`d+mkSlN8Lq>^Vl>$3rYhsSU=f&blUr+lXV(a zj!x5nU*`N+8N3-KSHoZ)i!iB(L0*(eXO8SOo_6-=pwrI1zPL1!rz6QTbSyIFqlsuk zZQ#z}Mrr#V1cqF#UGGf#EC9&%31a_+Bl`{hjf$==<52;w6B&YkkbacD`yqMiwHqEi z_8a7>yN5o+*Dx}N;C2~II!W(b{N^{7&~lC-g>(#gxqCVJ#`%EUl!uasu3k#|&Es(L zjkwZJ^ny~}^s{No=Tw9{dE&(W1Fw!pki?uNCX&y-_{qfkb+xnyE6G_%2)#suIe93Z z`bOVrt9W^n8R4dz;;fuO8IOB#S>&d0OtQ571FM0^$+x-cD{xy8WPm zRS&UL`4zC81!$v!96bh^{rO{oD(uMtSEIZLm_fKnAu;N|6|cbuV6n+Foe$s- z;41f_<_8AcUtkw89`yPxaiO6+yL-T%?2aNm)`CJ+p`jqf!3FQC+Im=BSDjZ@&hOoQ zWbY}JS6kdYP#B0f3@R6?7i?U%F_4dmPDW9r6+0q!1#^xRD7mN;lME>+J@^~_O_YL6 zN}?*!n&e2~b_GZ5SfSpggYX`|F>u+&1s&y&1m9u`p9CDp`meG)~ldk&6wMNxjX$$d;XJj0_!;fat`|IxL^gvNVqzJ zcBD+0;Eqs!`0nmek)uOdn{Y^;zv(cewU+ z`PJ?BeFBb&=)_-M0UWBIiqs=YlPCmm%nVWf%}nF6Bp!0we)=cKY5W~cgtaWL0(?%h zdKXh=V#^BbGub^%b6Ol5OF=2B^dJ<6bz?I9aM5C`V+p@7Z{?P#gvi9mB;P&X_CF({ ziq9uLB2THX4wM45@*!fsT>N#R|9R(SKe|=<1o1x`l_~zBj(jNlyX0M5Pea%q zSAi{2osnTOW$;e zA38W$(7_S<|3;UzA2mc4MpmWynygk+j=HQQuQ-<%n*6$^+lw*4y!Mmodsj~Z2%hU~7(MqZv0H7{yh2A3EY|j?h2UECq zK)~g+9M-#BGeI)8EKKc`%B4Nvu3^Z)~t&kkHb_ySnqx|fM@3xdHpDF=o83~iTjuUeH@myN#+!^;#!S^Fjl+(_1b6D(seRw5 zf4WH|vO;wcQORzc|4IGR4ZJN<7vk+ry#40X`UU6sbh{lix%n6KIbiTRv05rYxKMba4FSlTw?mw!(f}m(7FkOITv{(| zZ3g5(+5=!W9*Bq+ z04Z+6qX5@=?aRA|UK!8HU025c;GgR+4T+5j+N=t9=t^R_xY!h3xN380@QxTRHNg-Y zr;`6L{rHx1+}yfz>o2P>pWAn?jz4$2{zD{$Qj7QXh0NOs(lKyVf8K8_! zh=4S+w$AE+ z*!Xa;>f|WN;lWs7X4BY;R z)!Ub;Jw=|YtL*vZyt~g&GNF$|UtX0~t@a`Xm#q$67r~?XYyTEJEHKdNz_1?2GmfhJ^ib)KLJIiLyuCzkL( zNJ1tz%g!(R$I_4<46OoeLv98Vp<>1+C<7d33X+eB}u=hC$Vq&FDtl4!uQ5EAy})F6=!V^wt0GqI6g8gRupETL01|9su9kc>Vt>5EXVy`rPy zlCwhc#r6}eH&jf|89ZbMQX=52G-E#<7J;4Y672$jH&vWR-#sN2Tn++KO1pN2hA~ng z!2X)%?>CPX?q((GEuc^A($1B2wlHl)qWfF9-O=K$1n#XnJ;Pg6dIn>smvW3TkGmVY zwhqIj3lqXqdiwvm(f`lauV9u$W2kQR6=J%Hm?%2Iy8y_T(VLlj;e>k;1NVaU_Pp$S zhET$!PZU3Sfq!Jde|H=NY3bxaAlkP#f93HOf)IPwzAlrei5iH5xe0E@%JC5T?*qFC zuriYZ0ARO63Sa>IsRWr^2KV}DnLJ~P;Ap^rLvKJV53NV009CDMGom8!j5>LH1^_kO z5zicfD2!JXf-Oy$jO5NrL}Nz&9gWGh0o!V2(HI~3pC_$3`8l?1DH)2>$?PClWC~}1 zQT7ocuJE3kmDn2^X6$;RtstXsTIz|;{CUz7o(T(!TDnPv%VuZD9xM`K+7q-Q1pDz2 z+fbI>6R7dNCMYxjwF;-hyI^7j9q=4$Fg*m^XMM!nAmF(2KlLBU@UDuzf}yDExE=A) zV?~dk2bu;kMh=;9+}{7VB?H(k*(xDz?3N6|n+6YkJgWhdr6b7mKhZXHX9CXhM*IO- zGApZrHn(uJt%2%VL^B{tgjxOynWh;4(!F>_Pz$m)@*8+bwL~WxAPx$GJZ3`>QKU+! zHe7TNHgLEol`4XQs$>m8B6;I|F%G5^L2Wt!dt+V{-$!dxnFLdt2=8?*q^&^&p^2=9 zEDuN?7fp8!D=&bsi2}Z6{Kl+t>dDZXLO3Ic zDnxD_dul-hqm@l^s8~xjaruv+h7On|idw)tm2~rvD6~qbxwX0-*zj$cO96ZsZAEYr z?=3B-APkOqRl4mh}C`aJ4t|L63P4s+* zm2)^+>pEQ4?eSlpV+z-COqWiHy7yCL|2#;?28Gzb)BgXhAUW1_R-~Mj@=528E!n^X z`AC&;o%Ns%Jz#H7dEPpkad21%I!%XWs!b*|16I%I1v6ml{rAX@UvBS*x^CMLvgM968Z7RT?Z(? z)39>CJbpwLj@8206k{}9aN|$H&=Taf+R>0p3meqiIx2W0Afi>?dGoVjsQu%OFFRYy zG>?a5>+stE`N)wIf1@FWfstEn5Zk}Fx(6dp*0Yfsh|k- z*3LrWi_LEAn<7~td_Jc(5K4?ID`m^DY^UM2t3{ICi7`c&bhuvw0J@OJ3iw9(_4Jmp zV`j`4Gp1$6*PJ}_`iCuF^TK4R^?;@Sma~`)eUbP6ZiKhhzalmy6TB!HCQ^34Ra4XM{ht}1@Se6s2py`KSES^ zm&9_PItlXCdtY~NTVq_4xrR5zWyHj(q6^|GitP40J6Bu@`Rr;bqH&+1W`sZH8mjmS zc8(7ARd;}eP@o2**{b{!gWBUu$m92*=V{||n#s|zVhGeVegGQvt3M)8I`X5Iq?8Z& z)DtH%PpVIzu;iZL9UomT_z2(ph+rxz!RW|jCF!%4@B@g5D?8;ldscNV_FCX4939-} ztwHn|zH0EmyjRt|dg;Ua@b~DmeXh`<>cDBS6DFwUIp&sWxdF86T7a(msA!jb`poe@ z9D?;4L8&99YEnr4s)HJ^4}a`oK9NBf&r1}Bc?t6Zw-f3WV(wrj6|^Fu1%cbarTq%` z6za~cTFB%6!D6QU-*iPVzv3dqCB^31Ht*7D^bn682@jR=DTyh14pMM`iB<x=hnsaCE0*CbGEzC%fAM6_0vSa8o>|uwn#20$?zrMD|Mo80PKz^b0<1{ z39k<<-?UrbsNY+jzgzleu4u!Z3>9yOpzY`Jh_o|Evk*YESoYzOoy3BF$k~ccye6aCT8%s!73dX^rqou+ zbTauNqF9RG{60J^#ZnE1N(=AmAhP!}V4XNHamu4Tvdl3WPJZa>*?E(B7Ny3gf2%;_ z>!GOYtUh9s1 zC4bxi?2*vbtO;NiUz=G&b*QY3`F4PWA#30gqPRASY-63qmjN0q+5u*byl1CQ?QQ?H zp|j1qVSC4h-W?8Wcb27p`Zfe@iI|@v_zzf7yijdyni(L zBmt7pEkWGdxl1X3*IWLGlP4~(TeB~MRY3C86q0|#Y9Jkf`zMpX`?E~`O*HCbMX=gN z^2Cod1*}3A>5Sf7#8;L1MO8H{3gGGN3#SW(!9-z40t4OMi%Y3dNuN)qFR!4|1yV8- zg|E+&SB{cy`O+$xFrq7c-aubkL}jz2WUhofb&>QvPrBQr6!lD7-D{ux(!gL_ekf1o zND^}rt%)}2SqQN`e~J!BPX}X`gh|Y$CD|ovGT`2VxkSPjrWYCtGo*0miE0fQ_VEvg zr1Tw$Fuv>H#dO#>s@f+dizVr`b;j)&4S9DumyHK`>{)n1W&b@CY#`**kI3Z77>u7~ zPX?l6806F0K)iQR)-eoBo*FWc;_xm4g5;4JSBrbaRM}(rSuXIg6!$BV>>x9x;np_rZomuJ=XN^fV z#JZpMb3O7wEti;5!=+fC5<^*@wN!Z8PxOqBvv)fm=>cNE7GbN4pJ+N3G~keyD&0MW zp7m(Er|^>KiV3qq1AwM6WCJLcuW_I$LlmHu?kty*Vv~mCK+-jqaEosZ{Ec?qP2UQk zb*6YnLa{*#$?PnPx**?{Z{_WU$V8kc>r|-M>esbe_(HjKdBNKkfG@pD#?Gl1xfV$v z{e5lM?2nR(ut-D}6(|qBpYYyn2P(SycuKl%PlzpwQD;eFViH0Vc^ctf<~B{5oszKn z{Z+m~C;I1bccy4%TFJJ0b$(G!ZZR(`AbNq7e@!h0y+K`HQg<+oA1-8)zsR4We_(uL z{JPdC3u_I#qROR(o}7DfvJt2~cp>eIZHWoN_7L9?du`M%Cd<_-4z38>nZ~i`t5sc7 zRalkJI{{E)+Uc))%^%?urZ`x#cSY{Il6J)*&ufWrsyzTj7j@3NVvC}9;O1>!H*>P8=k4Jhd8DiBF3oG? z>Lfp(s3F6Sp;j+`^Vb&AF7@v3!P08yL<#{d0({`_uyDYlBj5e~P9CQhW{@(wjJ&bt zbIip;Glr&B45f{t1RyJ*10mPz{kr~!{(l+#*#h8Mza!tpmPQvw75K)0n7y6u=m5?F zfxB_zjO>kjeQ6y&PK_yuDvU0T^~Dj$zv-P0VCt8jJwc_OKDFz!FIDb#=O(56*-l9n ziRH1S^xx!;j~5C%?#(ASSnYz~H^-^Q?RxVRaIoLe?@D9K6DyKf%Vi{uZYSGsYijc9 z)O9r;EN>k?Ni7pOpBwo$)#iQ$JBB7NcRH3IJUllabj3ll>QA4#dbvbH`UY_ElfmF8I@XvbXNs#Oio% z+8VMco8Qsy5N*od6#{j0hj`DfoqO<+(;)(yXp9g{x^IM#%YAT!{6zC{*8wFVKP#^- z(#X%=0YK|ZWFR$?M49si=f9P-`xqK8E&_M`Rs~5@5#K(yXzvlTf;Qil?JnD=KKa3> zMZEkhc~cf`PT(w|A|YSg4RM|BShL3_mxhJCzLq)PQvMv&s z_Zi)V2r@$+iZyh)vTg3qRKiiYw*OT1rY%)9IzFU6{os45oB1~jZ*b;3`*}-_)GU!V zr6Z*)-bN+r$rE?n1l*Q%fh3BGbRK@bchCN)I)^rX)=pJzir5ma<3hHqOkb@YH7dVw zG@opq1C3s(JQSXli6ug~LStEGIsW-3-ngm1sebREZD&1SQ(aZR=Su(6M6M!|pU<`Z zetQn>%+YSNOAviZHR|)NSO55}!rZ)d2crH#O;e z{`T+8!DN*`tavCwk>+ki6mhLal8y?H9$8q}Y=|U6ujME_u}sn&#O32M1P%zv0}ud^ zO6}>%-s1%@|Hy^m8IQ>vW>i?ZKESH}%G!RN)ChN!DSOlR?S}-1r^)ffZ*G5^`|UT8 z>w)k9OWLTLJ`WL~8-)LTT4Xmz`8?DRJF)wGy6WqYTPf0f7La6JNtaEWQr<9&gECsu z?xwVT>c5YPkd*|Wmv)i+dE%oa-QK0L?)ot+_yjN)TOutht&S`mYFwIX~0 zERce}=s%Jh^UkQ{i$kTX9Jm(IQmDc?SiF!$UL6wmDB(6Ouhnx1ix?dMDCa)=a&5kF zo0JQq;Km?-gxIK$CwwUU!}{z3%!)$ka_BTTosZ$|!a|+_!?<}VAZ8lc417V4wNF0r z0LNA%hI$VT-S1AC?<1s!DPGTv`EK?@$)(#LQWa<;+ zRrIvjQDKELqu1{Z$_ptD>ho-q#+8EmaGXG7e5E7_#R zH6f-w*1n2MsF$j}*;|SM5h_3lp2GUxXBYPniZAi`iA9;fRtyk5(PD*Mjl3z>mgC4{ zj;RjJh|Uf815|P)U>O}t4;HLuWm#NN46@zx$51o1aP#KQd3*L`_rIcil1<4-&oHS0 zpR^=%T%NvVhL5-84(x?&3r}|5V&L8pbZ4gCl9Zd`ix3%dLXd&80n&{cGzy|~*lc;( zdA=3Gzph^R==`~}zL1AXxeLtKEf|?l8=gtNMzm1;HN8%*%WwIKKXv9PcMzWt;ydOS z=`UmHzs`Uf;s+5f@+$qBa2m2-%>KS1-n%O)vXn22v<9VaqEp*jeaOGXz$m=#%z@1S zc`78WEKug}Nr1c5xR(k`ed=Wbd-_)Mu(wZ(hF+i-d{8~|LW{;%s1ka5sH=bP=3MRB z4LbDoOa$(N55*rCS`Qz7i>;Tsm$IEYAHqKGXuSIXB4|b2L4OA`_1n-^_~3@d_1HCD z**-#CjDibJAMp}*Go^h+rVI&v{A&cM7m+u`h2WbnUPzXltRm4Ow;*0Fzn_-k4_WM z?RY);qK97_)hYQh#nJ9rh;=8t#BSfD52a>G@P{u&mZ0=b4U9Mdc@~Y9T3SD zJ?SgI=+a{81l6qdF|)VY#ED6%Ne14KWJz=+|N4s05J>7y97dOhN}XyrrUN{6542>Y z_=|%lZvF&1N|bEiiBVsyVka&*Y7N{80pk@DQ?xK1VL8$t3_-o&#BJ2>&Ah z`kss0TjWOmQ-L)XC=<-jm65pl|5>=!)r{m&yRJ!dLh~w84CA2Ghcc5rlj4)XmS82TfOjq4jZxk4LPgYsVjm*t^2Xd+3IPJ$FIO5AOaSuPU=s zGE&lszoxL%#K%LGXcQSmR~JiTvlEHG%;v~(n8@W=RN*z1(#ui-YI@m7-KJrOBDRAt z3}Wa%xQDSF60n2aZpkwVrLn>&_oz}gG)v!e&G(1$@M?6py+w)36$#{IeWo7V8;doW zk19yQ{OD9jstYPB3b=~=T2x#{LcZ0fLSF!Si7qKJO3y0Yuk;h=(f7!E-A}Puamh7f=X>x0-E*QbBg;7l=8i{cg* zbsds+tw`FzkVY6mp`3-62sbm`w^k4C?lQg~$q)%RTP!-;#bt4gQs!4>Y>z8PYC+)> zzH>=dcnE}O6+Us%nW1?R&~~UwsKqVQu7HsVhHV-W>j6}onrs4$$yaYJNGm|0@=#Lyn%RprcsWuT0BL zFrre|L3$9Cx{L{+@}?G<9S(Ak97Lrqb5W`tvX|{sm9!aoJ)v2^6Kcn`w0J(ad$+0S zQdZLjUsn06X+ze`4S0Eo9P-HP?s3I>Fy@|ToJ~L%w#Dgm;9#OI7Aq2GD}ePa6y~eFW21sytS`L845#YH6+aO=)N(P(OTc8Kk z=PYS_cwQV3WDuXGvwH?loyAWY6;1o^qUq*@)PzKX)Rbc(G2H+L;({!^HyqpS2~Q(v4)cM<^+X6w ztyLm-WK|;e=@8w){xni2SO=8nsg)_PX)V&MEkRHS20c_`fo_Jhp&y!+(n| z+GdW_`$p&!Bf?d%AHxeHs`Ol?zRp};gte*Fr?eoiyix@fa2<@m$Ee}s(k_+ZpXRZa zrR>mEcKb!c9H$n~2Sh%)E5FZ*F=@4mQ~& zCjCApJ%1o$uYMAntu8f`=H-;WPloxJb4`v6y8%)Gsb*<*#_+0MYOvQFbQWzK%J+jR zrFgLBW3h2l*81!q>DwUmP?5yL==n)ZKlm1??m6T`HF@^O2H@0+t&Wn65~*i)*-ST+ z5ENBdBq&K70!OHCIg~`o<6Tyv7nbJ{V);=ln{T^^O62j_?A$jp@?x2co+ClxhhKa` zM8DmhX3FMl1{7q>c4RXY*zZK{lUHaePs*2C(*g1ZzDZ5(C{HnpM)Nd$Ao-VuzBpL( zlUv@Ob+bQ2%;zAchS&)MPkch`56H4MV(a4C0Ps3Vr|WLecdl~urPH+A2ai-g+_?-~ zR)6xGKMtFlj=?kMW#`(gjvJ)U|LN;Hpqse1u4Qb^3>uphdx$MrBUB-BLeP!Oi$MD|wul29* zUjj>-raLot&OP^>v-kEaD#-!udsYF0^8M)MI*!aoQ&p&JNCNbC5leS&N4@@7`i7Dg z5bZ>=Xg+wP-Xe;PW0X`rc+DutK@1{FV~!}1M1t!vH#I9WeHb{OQd5lamXyK_OdbZ2 z?2KJo7b$pf4osB-R zx054D(-nV!IrJuOnb(s$L|z2((f2!jIy8=nGZZf(!}%&hokD28<#aw057I?)XP=f| ztw449NVC zmpBpSm5<5HyJVIVu(dj8`)>m)$|R`F*W~Eeia&9&j@~6lrz`$qD{%JZ-0d2(7#6E=vv?r zw7AM1eV_fLUz&;AFNhd`s4yq*#}I^IG2IQ>TVMJLOXPW&Ju5$~-nG}Hp+^8}GUS>-Q*OvqIfk<_*(pI= zREE49D$f&x=u)}+QnHab)Sla}qQ$Jc0Szc*a^LPW99Gc+`~togGsId-7JXDlvMR}% zm%gLJ+c@{P?{&TZMKbZ?=w8R$0$oKvuN^9q2kc+ubFiOk=G(&r;0_zAr-XK{oo}!jAQr;d4`CK>{uiu3 zKhi;-Iiu)toKQcm7^+5b+*gY3JK(yWrpQUvB<0BSSgZB6f+VtCiu*l}AE^Nb@wpA0 z8~vZ%agFz2Z!H$DOcG~P0f%rLD_)%EReH%(L?*bPgh`Y zyeS=^dx{+gc(S?l6m|RIaD7Ml@3)(M2Y1Gy2xdT1n*(F+D@f#B*ss1rq<*qR5!}7C z2&DyB+cN~4-G?*q&0R!w^nF|Gps7XbectlMEmC2Egg=ItghTlWyFx;D?+R^hZ)^LVy_WM|DeoA_LaHrMh+DR% z`0AFYtk5mnu_GubaLX?L%`3)GJ|LUhlN}nmN7*Z|yZ412%oW>mFGhbD#RVXxtJ+A0 zsw$YVV~t^@!n!4h+a;@8q21O0)LqTE&BhYtEgP zLQpgNYLB3717AXD4{1jGLwD_N4rxaNbC(I1LE5K(Ws6@O`G*OpU@8z&pNtRzF6>QyG5p+l)^V*r(D-iTTj zy*rl+%nc5O>ZZW%X$}RU=ArCIls~qj-T&a0{XvI!SeKQour4q0J-U^PgpI_tx${-< z`SABNx>~&@t(7DDn7_We_m@#~I{JKI2ZDyEIV6KF5$^2Wi>Iy;kB{vcKVeoMLZ*EB z{gq7*NLQ3Prh^nUKHr2sqTT`W`7%WzK zWt_3dSX!%etm*z#IH;?Pj?%{kqE>?qw8YoeSSt>S_I-{sNTq+eT!m}z42iVa&< zrgMoB9>ze`FyeSGqiW5{q76rr&vP-~7#`e(l;yX^2UTB-whJeYo;Pu2kcR_)M-4_v zyeATG&AE&dTS}L6Rj(K(OvTo{S=}0e`oBi}+4T0r_ad()9*;ksc%1u;IZfA`0#5W6 zLpC_vgdOR@K+HzOh9~0$!)*<5nxv}q76gO`vWJUWN^$O$jkbfT1C7ZMRhrV+q7a<> zKo(-3uEG&EI4mMDLKU58u1wctmE=@l;&S|B+Q7Q^<75ejH26_EBOF7Ot<+LerXlSg zI~dl!h@8Vj$PA3@s~2t&=GLu;hOszRbm8qzeGW!ZIYO1tX5 zL&ioMbjEBkDX$2V<;tqk=4y?7zCxgYT}13|)!v}WL&2I2le)*; zXWg06G8)Xbx9qPxplWM~4X|p8V)FL*E0O;u4=h56AtonP%!x^h(UVr$slDx*AHg{AthzA?nDvqnV+TsHnHI)(OovW3@KyJ4unx?Z;m#&DN#YIq;T*R0;^cu<<=rfI=2d$j-(TY21Tr?ihHvz#^ z0fPCap$2kscZx5culk&8ATCCbIkC#e@!l>DVIeJ_Ps-(knHt~PH)?%b$5$^fLr%2* zH&V|MH~UaIsiEHrr&ABd;v6G(SNN+o?T!zO(8NZh?pUpaGriipqbghsY-o$`QXOxr zIM|@6YA_$cmAOa07bZBKV?ttLlb|M-UR;_ZS%8unrQLagLu7a5M;0cE5$2kd7S(}+ z)o-_J{8)FntmXl7Tu7sMGm!YRKkV)n47o-?_d3Lyl(_m`Dw+n3luY=i>3U;QQ8K*g zR?l3J{^zQw$>EotY)m%kz4Rt4WF$!%(^i4`CtMf%QcHzF+5HY=ZY&wP!Xy>VV0I-& zX_GY$>*HbZ!3HIcKz`_T5~HnEk?qp1rPe}Ak;Y^(l&0J0eLMBcH5iR5dqdBRA{&-j zyij};hfxj@fyka)Boc9w?h?U}o=pAd4`O_3Qf!zcA*o9%EJj?WIM-sb;K}*b6Kyq! zh*Je+T5_$0m|zx~3rbYv4W_v?E&){?&(m;2F52p1&kzdJ4EjvHV_fepPqYt=yf#Oe zNsnb|UTK-BS#as!U_z3r%7J__fU&iRFR(p9J-60G9Oy^{SHrRl4a}rL&?0 z#cm!*h8oD&ARvsQewlq^oRw>!5j4s`flk)qJ%UDP#_8tFiyFo4r5Xb!Z9~E4jQ9Oi zBi4@kY~Dj17eOLO6zU>Wm^nll8c2lZq4l#HHNSAJM1y0Kp~y5yeL&%K*{XK75AVJv z&uxZG?z6Rjk$6o zYfqNcPj7j<+!q|uAs)~=dn!36x2Mu`0x)&w$s^ifPa-$uj-+mID@)(73TCOUubRP3 zc))(f;8wf!Od+mNSRyK+cTKLGj$ymk8091bH;cMD9zUL9e@xwawMGW_t4;KF3Bo6% zp-qVu-9i!_-Tl@Q8yPL{eb)Y*u!9coew8jg3_d4Eg}p_XLkHUbMICp@Ksn9pUI^{O zsrI3cFUhlaQz-ZoR%_RAXPZWC4K6i!kAz4>8DB(Xv+&`<{)0mf2W77a60K zq@NHN78WQzKEnitH67G+dy~Oz^0xF%o0Kr(d+2r`vMb0QvYnW_(z}v7F(o!Iz1}Q6 zWZx%X#xGJO0P=G{S*ipCe>%o1CCJlX1&OedP8UI^?htkc1??2+TxMs`{tgY9&UWnI z-+{qxE$hx>x&y0lfQRSl=#(13@MF#BoE0(O=O@ggt;je$4OCX-j zzi?!6&s#!aTk+w@{i{Eo);hb6hF+!##WXri?kTud?_5atUq?F$0L{+DDi z`jw6R_63>x1^J!WoV)LLj~9xU&E2?W|B8CU59gY=6D`+vtWKdRV@{bR28`?eO+4U_TyVVO23dsWXZ%S z_n*=WMIW1vb#ZU^CJWK?OUC+arNVqVF^vvs^s!B@-*!Fj6W#TcYlS7AB_774EhwFwb)au}T$ikzo_llP!W|Gk`>93ir=I_Vs|ykaIz~& zs5Aa7RqJQPEeT%}zBX|4mVhn0)`TvL;b<_K<7j6W6ungzAeII+?e5sqvG;iR8PM6B z`5^V0>Vxwp8`x+{F4SJx&yh@a?VLFgvsIgSSZV?_5oK}JsSTXIG3(rYrCkI=MutOX z_XJCo2LVcf_#q=oh`X>}yD5HqDwn!_OQyeS^~NIGcFlH>v4%8+*2gsInmAo^28Lbx zNKn8{W4p=@*R(brXl^`E)lq%e_HNMy4iCsNRPijPP4on_s9;M`tXLFlORUmy35_l3 z2UO?JR~mkvJEMD$;Em? zkWfI5S;{tyRGW(nOeT^1Y4<3$3g(W$*Gz%rjI!Fp{snYhTVA#wM z>7NddG<}Yg?MNxKrrR(s;D=D1CD{NiYqJ(3N`?x@5f~7_Vgzw%DGwuUqGfDpR$ZY8 z5O|J0)!{+^@szL(smdSKPtXi@5BjGi&6ZPA=v7i!WVI=AXqUT^@Ue6>?UpYx<{!D#D z>htTbQ~p#PIA*OotEoM6!g@s2c}gF3K@)xPxbC3p?za%__*QfNyCdH;e9k#sy#0)q? zQl9LdV{Z}+y>lFA*zP&wqKBo!Fz1 z_|dCU&nkUPm zHNB_l8^TI||5X~tTz2Jg|8wWMj-M0lbJ_R(kFOGYx?+XLqkG3QZ@#K;RoFi?ct6@;hcZh z%2ocGR*Fwr`J@2|ki5IO^PQTQN95ZI`^k@wRTH*4uR5tLecy?i#LDN3Pzwp{)v$*@ z-#4GwyWi3o*zwV~P468nZ#&;!3ky6gwTwJh<6gDogP*&{^mGe*^K!HnBWF#o%&XQI z*zb}AOM$*RBpJ*Bm4(JwOFl>ca=a=OgA6eYmvZg{WtU`Gs}lUuRs|dLYs~vO_kOZxW#%T^ z0b{FiUv_0$L3*JsH6c9E@3qL+(-x*KEeh<=*<#{zva>TwQ>`(ayKDj@D-SK(yfeo5 z`(D$Y56}en{@jpHE*F`v2DL;sQ1Or5N8&5B=G2;~6N#TRy$i25D=UucYe&?Ot5eI4 zS@-GBn2zC4K67Q3+nuIDYO*sx3!kERkdN8Y|iOGgDIyKm#(wE$+_e zOV^6ajrE0=_QoH!6X)%>w8x@aQY^>AE=(z1%2mExvMX#NSDtE-QkwPowLE{G`-`l)RXNjVEgAICsuTCc|yw z`pINaw~whxDc6@46~uD%brL9K>$CEdIb~_3$XVe~d08eKrm!_Bxslu%1c)+q+WgF% z*z}CJ)FORxnYqAiVd}oDBc>+nnU?aFwUv8JJ1=K*?#o?d zvfr|*e{U_U$*;YX@Jrm^zGV8WZ#Z|IOy;uq+O|vK-$i|za=qXa)4lcNnc3&px6i45 zJ(t@NkdYXwq1+n@6Z3}Ujmf9|tV5GGES#`q)ryrN)OqLVn6-N%vlr*a8aGswUVZN_ z^+bi%CY((Dj_*JuGd6l{`t?Jn`mKWyyC>o9Uhj~a51Y3^kQ`=1MWH{v@>O?7kA?aSv{(C2kBpPPrs z><{TPxBL7x7yG?G5)iDdBrXW-xp;#v!o~f|9&@{}XV%o%36iMAi|2l%jK%=TwoDO~ zqfK_`%^8$N5TC1lpy?fSqh$q0eeHhkKbC%LP9bje6~J9Laos-j zh7e4b4yBXmh>_`scayiKqMU5^0kU*OX%^ReygN?7?9HG789PMF?cdQCg`Dj1bO<%P zg#6hy5Oq$|+qjaG?-iX^xg#@2#`?YpfB}hg#0hCe8u>1b4&mI_W?HjKGObCiiLHtI zNy)$dCS&vRexNRA>Cim-5=UIpF#%Xg(tBo0nbJ`}G5e5@x;w~ws9$rj*n!$>AmXQ*yee|_igU@g<1~Lo%E^$uWcD&TS4sX&gN1v+U#|N|w45-VI;FIG zfqw0(!)xu@4E+Z2wvD2G@7Z@yxOBpr65BeIhsxTU8bwTO-Q4$tk zNwzh^qM-)+OLF4b#Uk|bP##vfFQFA&)s89MooA#eMPF+qia2fGKh)2fyKj;i3K6v$ zN5RuDh4odOK6>=DNdCV3co++OrG3X`#}4U3&#=p=g?qZ1c6R@L1|?eEr6gIPf7pY= z4(%oU?;m@8_x@K~j;`b4%A2CzQ@z(*TUo9-dh)BI->&*&(O$}j1#tF>i||a;0NT&| z8zGS!&y(06lGQ)BAM%!;Mm~mKhp@dBfAJ0l`|Ei9_gz{pk`}s8K)o0epL7v1dLj{P zG?|T-Y>QX61&sdrwCj;4xxiX7!SgRdf+0_zMZ3m%N*kw?hZ<56yyzloq+Jj71^S%S z46n2dbR8wVz|yWUQk7b^-YZ*ggn9###768!jTvdVx_rG?zP>o!oK3pMcw%E@T#GYA za|X(A3rN>PG=mx?rT0t=XqO9%K^lVJBVFSxS(ZGr$qVHM7K;+iB3+NBOktqLuS&~brtUyYxo%28vd(`5XI67K&m4fLT}bPf7?ZFy)e=a`g8 zKcn9}CMyZJ3{R0Jl!?}p=TI?+{^8db`a)rhBwGvy0!g-b403ZjQJ4r|1BheCqS|FN z_;E)nE_=&$sITq;AA=+sw;FT|01X#POn*|k^QKfq?1O2}7W+-08?@kFGyHZ1!E3yp zQFxI73M5Wn^X$FLP-)Qsg;zv`VS7Uak(MqtjG!Kv1O1JZ6GOSWNo|m)+C*ctVbHyZ z^wQbNGRstw%p2mYOF_|YAf6aQ7mLWDN%;9WpzL!sXuzns4ji0n{2utzcX}SV-t>?> zhq)M#kXlrZlLg@I8;U9pyyLY102_%zuQs~J(2`d4yf+(K=KhH{o77_z3`s|(0D;<> zBag~YNJYpqJ~b^$+(_M)4K+Z*hlZ?4i7w^V@3;K~hUML@_r}(VK}Fia8OCA8DY@&x ziW;%2ET<~_Xlh*$XK2_~Fj2J9ytP3F<&NluZ6nAw&amQ-O^Cjy)g)MP^tjUS0uelC zO*!(diLnMlVnXR24XBhP?$|=CCy_LXn933MV%avxD`8Q2W$pnnhm5~bXHZ_N{hq%1 zXfbEFx$dl0B<2D+Q5lyK7lSg>y!R7~Fhe;oszDC8CX%eiBc>n|-+7eS$qlHP`Uldz zC;{6JJsOFJ?lsav)X&=o{Y=(cKP4;e0YvdHBD#~i-P1^+5aRC}<6pC>Ch2rbSM#xp z)m^dg#FVWL_2(WEH$fk#O^YjvE6%L4R9Y%(iz_oK(@in@IF8I6BwQrQ*D$FW(Lo2d z)5!~b>9fAn{UYcphWf)tMBVQOGWXMq#2Z4fNS0U8HHh*qrYD0r_d*|fG6bg2^B8{feKug`Fv+3+na6?{F(v$XQ=^{I4Pat=IOpOv>>vj=VUVgcL zc08Krvo}2^sA#WgZ);2|1a8P(1KUyDnbI8898|nDWheYNeNe@o{rdg-0~MD24Yw1% zTlpbEXh0-GhUo?R+PidWAT|m}i`-QlP#kb5@=!5h>d7;^zZw6{OSRN}7j?#J-LC4L z?J4ak9n~f6+>h#$WI_;R`4nWJFWkQPYE;5p%sLqyABEwddY~=7?66J%}M`j&OOj zr7o!+!)Tv+0fv&kyhC!&Hu5E6J03m%Ci`%|9`w8*B)|SLu+|f4z@mvWro6Z;KE`k^W?%EV(n(oV`O$)#v(FOQzOxM{>fS9l&RK|TP1&flv#^A(+&EEu(fn;r z=bDxP|)vi%~c?1(jy`9cr@oihO(rpybjAhveZ+VeFm+#p!lWi6Ba<0{>fK$93>1hPBJ&ybFv|_7iAMo7Vu9gpxkCu;@zbaoKUm{;AUoYP*-!9)RkC7+Jb@HR~ z6Y}%&JMu^JKjoC^$1sd5^DHxlna0d!<}(|aEzC}44->+aGF41H)52V2t}r*5JIpW4 z-^{;RXSP2(fOThIVAbr4?09w#yM$fKZeq8wfov!n&1SMKY%6=3z0Tfe|6m_;E?j@k zjT^_!_1ppO2zQnHn!CsS*1v!MXZtJrPwxLl|K9$igk*OiZDg0B41%uR4M8eEsEoc z4;5c2epe91W4<5XpI7li_>uf%{uSPbU%{{D*YSaTI3L4j@g}~Uujlvi2l-C^1b>#l z#9!ra@}KkH@b~%Ok!56DJzR&nj&+^n`m*aB*VkSBT-Uj7a^3E_+cn%Z#x>DZ@0#gq za5cM@x;D8UaXsdG%JrP<2d-CKue;uH{m%7Q*MD3eyScdacN^g5?k2hobsOn6-fgCcW? z4*U|uFI`ly;}rZHg@%7gA@V@v@{`pX9?(?*KQo!1hlNjBh#=MuYs`aMt6Mh$)G)e{ z_!Zddc=3NFF?(6TV@4$MHemV55+M0O6NvCXND3Pi5{f@S!M}=@55|F>(ql;67$VQi z#!1Xl^UQb!Uzt>TU4d$2UKp##Dt&#%d<%t1>tQZx{BLkVj9+r!N$ftC#*&Md1z0@SVTqPo zBWx;O2v@`?`l#@DiAzW1rOU!IixfN(7wb;WR7=bS;QYnk>FSdw*Wr9$QuD%8HIGVg zzX@u7ySi1)6Z1AKl$w<4iKpeJb=x<6lFTWd3hn4 z>BzdySVtDCE?qEL-q(D+{Na}0!<*7uGWTVb=aktBD&bTWR3(?_Y&(u^J{;1(fQ=oL>;jKrJbIpPgPlAuom4vF+5@{#;d zz-#+g1NZ=j&>m2(4j{vjnvw85e2rlV(mqn}>Ot?4W>35j^f&|+t%-@*8A+PW@uAx` zf#jdCo0_*Bix)FW%d*Q<`&&wC>NIv=`z*V!W0pL+u0^+BRaTZ~D%BjTXzuJ%ddvH> zU7?+uD@0RLVt%4(04^K}+%L345^1Zcy+GQLi~sU8R6r80y~d(z_ECh^H~r)#NT-SeByTpYRC$hc-aOlNgM6$v_1C zMhpfMHh7+Yq70;+FVyU77W0^)vA!dyt12YiR1~ApL5DB3U%}-a`1MC6lh<(#wSQuWY2FSV7-l;U-+ucq*b%7`6Ib9V%j16n zY5$>qi$%+r6gAJzQ}gH4-80IiyI-#8o}pLp=M=p&%5f6q>fRZt3SQ#9;z3tGT1^M@ zE)x__?9h{m^%GmvCsD!UQhV@_geZa}1WyNp+E2ifH3j$2TQ1|*t7{ef1jS=NW}|xP z>wu>$hmnE89A70}BVXD8I__hjJ_cL3l>wJ{zNOq;ZYuH0FM?QL5tq;8rRS%c((AXP zbe$H;iIL&ADPV1%FkZnq;o1E3f(%ne_xj%To(&Pz<~AkIW*23dGEBYeyVrY0G{mH$ zKYg-}2xM#R?ugS*Ljf<2-n8R>liY z_&A=@abPFUlw?CpNiz2YqFG#=FLAvhASTCMEW;0&e@ByGk9Y_Tn9)BbsX+rmBw7aI z$&k)gv@DyvtK{Po(o<6SM73o1cyE`}*y;a$@e(WmXN+?8!qo~XD9N)hiSb3y`QQ;1 z7o{6%=AkgczpgM)KVDu}S9;(eu3ScjX5G4pI2$yLc;nZpStp&L6lg*01%LihKV>D4 zh|tGj;8<{e#Wg&Jn-zRSgW*U+1Mg%oDV%w5k{F0Tk1$TJxfM<%Q7HhS=X11Ku^CAb zi3{Tw$L-LCCnQC2sjM+vo?%Qi>O^auk*`+_cuMC>eP%qB`5~_<`0a{bK%Sth(hbCu z#Fxw;+tm*KIdIUm@9N{A6M5;;kp?k|W{Yo3sMA`+NK0sDTx{Q?qUHxrojMq&&hjM| zV_vbw1mQAOHmgg`%!H(DiWiUzjq-{1FIjYAQ1hS*z&k}Bme*--sI`%r*L>uXykxhA z@2G9;dymaO?^9D1S};U;^KbX8`sUQm9@$?LT%b1vU0qK#)oQpPu~1 z5|RI~k7R)Vk29nh(3~XK9zUIOL4~x(n6D0A)&LciUy#S~li2FGlJGF4gu$0CZBt2O zb)|Tp<%O{dJ{kSlmdqbaI-sq`Bc{RA)W>?v$HrxbTC`q~mb!#S6(6GB1M2{jQCzr4UfpzM)b|+6>&*Cd1m?Nq*mP8KhpWgynDUB>cpQJ1IYRZU^PR|GtCpH>Dv$ z%T7=LWV%r;aRl&Ar@fhh2n;Ueg}G>lN1>WG6%`blVN2i@*+8U|Y~zJhemj>7wHweybP}6B8X5@%KxfR$ED_&C1m@@Wa#tpo&|RQIu{=FH0?@8WQVC zTX=%e;B}(;gs9JwK`4%x+xMj6p5dl?@N8R*IU+*D!_s5MyqE_eGrk~6!?Wlp!@x5& zAFpBHOyvb>)LF3N{Iy2)weu?$i2VCl>wSsNR6!>rnm)I4sfMR)Zfv%E=N7|y)ol7) zeqI^|hcOZ*ecz83!uH+39kYQOD4G8+3EES}GhBSFlffum38#9=2qCl5XH$5xSkb~e zcTrY2r{En(-OxDNPGA0uzDQmkhYgn~c%ynid>SMYP|b2v0!n9O!}{Xcf%%0X_k;)h za-TDrl6Z5RHJ;NI6=(3iC)a%-B@Qq&RrGOX-(-2^frK3@0;=11^m`Z>X( zY&S$i@C`655t3m+StJj!Bv5Sgabg3;UJz9>u$}N#se$_QtTb>x;gy6moZ={ko&aAu z(1$8idxA~2aB0Zwd(DJR!(AvgS&KB*3Ug&i1$XNVFX0-x5o_inB%hC$cdB`b)}#>T zY#tySuseKd1;|HOiK^Qqn)n_dq0iDd}CdCJ>1^9%;wli9$KqfXst#{TC2RU9ge!hU{l5u$GDu^pCUj~|^P&2$(s({I(A>l|zdeA5g^DojrwX{*0oX5K$ oMn#JeYGU{pbzL1U=O}QIp<2eKYy*8l(j diff --git a/doc/_themes/sphinx_rtd_theme/static/fonts/fontawesome-webfont.eot b/doc/_themes/sphinx_rtd_theme/static/fonts/fontawesome-webfont.eot deleted file mode 100644 index 7c79c6a6bc9a128a2a8eaffbe49a4338625fdbc2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 38205 zcmZ^IWlSYp%;vqo1upLH?(XjH?(XhB4DRmk?(Q(SyX)W#I)m#B?7N%&@gNzPg3A9y|F{1i{C~vS%_!vmy8pvq0i*!V z04IP4KosB&umrgOcXRyD0su$=wg0R&z!TsAFa@~%hfn~t{zKgUi?RJbIV1oM026@a zKV<`u{HH7cRsj2daa8}Gnk4^EMF2odUHbodF(eRY6Og71NK*#{I$+FQ#4RkN>Xu5t zDV|CZ0erHH%7mJ7f9C(hMgfc`(&`gnuuiqhEZtN@Gm6qm9jtBTu`bUstuVt`VE1U^ zQeRP-GNx@G1O+8HnNjpn78T|1$sHu=pO{n+?Hbd%?rXh*b{x)ZZ9Ey*heliTM$ph9 zeSOvxJI7sn2z_VOStQwpj}H7Y+@M&VY|#ngtbu=`HY)^$pT2Bh?F%Qz)A!hd^bxco z(ph?3k$*g}cpvrc9fcXhjj;5WPot~Co6>e-hv7*v=?ht4ZzfafOKSl*nvanjGNp%5 zqVHEAb0A25 ztDEMbuMI$uR5*rQ;Ex2f;9~>x3rZo2m^kwR6UQRPZz@Czx8NQJM6qF(2xu!inpqCE zp&p-KF}@yM;D2@511uFKw|p7`rR5E%Q=P-zPeXA1Ktriy6is`S1oMudP6;lGGo*>+ z8#MeQ*S6fE;37Z&V&V2oyeT_l1gp@&a)ah*E|M@ELRv^E70jhArQEOCVR(XrnfK5q zp=6hd;d{^XAPeI<#-L-CBvNu5_(Jtd*&!2*tS%|-yzds5)A{0f(w};Y^KBe@AdynU zQL37Co!%Eq%0_)~bcR`#k94J}qgc4SSR@Ul!8_*tW{Z3Z>U6}ivNUHWn8P$)EbfkT z@k>R%?c7o_o;AP3>Pi=p)K`@mYLKBdm&H(%0ai{ls$|XAptE5F3tx6U{?(i@T>GA3 z^_!F+A*NF}bxUB`5ssZLyE(_w@^Dbsgs-6_CGq92Gx|oi!cA-HhDACy{4K)xs|&hF z>LTWj1(w}4LTGz@)0q87y$|wm>pEPvgpR{F10WY$v~2DYt@t>2Z4;zPN_He3aPb@z ziE0^tt>sf2&yu8qR?@PaDB@HEgBHaU>ZnpXEB^D(;d~K@`H3P(?)J@Vn z@CfT^4qS#V(v@+Tim_UUz_Xd-$p=1fq8#h)@{UE|bVYBR`b>ehNCJ;D5bU7L26}ay zF9bjM0OWm1Ao>6*BK&HtwoOBWueI2fo{G7Y(GD|!_MzfV9ur=<&-+oRNRfybM70FE ziI3L556BV<%TDstB!_UPon6HAw*b{&kueNsC+=#&J+)243^;t8PopRU4eb)@)UjTC z%|J@gDtLqz=z5jdArpDBF8$;L=m(uEBXxr?n&v3{9kTU@&#yiW%YPB)RIU}%aSn`6 z$@EM;F;6}0Oe=&L&gfL&?rfC)Kx@IRPdd3jy;|W(cPJI&mJ)b22%#Jh)6+MBXi}{R zv^IAae*Q9Ff|}Y>L3KPUWC=0h^@i;U8!M>_cS{w^1mL3n#)V zzLDJBVg}IArNIql9*}a_j5k%x5~ySF{kx7~rG&ilzkAtDE&P%=41?qbzUVW>mJ;wI zG5?8dPhnkm~3cU8v`qiyh&L1E1^VPh=!%X+Uo>1c96Q;$2#!T1Ajyyr?xG>dq*93%MpnA#<7B$B#7=HPXzf=n$eqoJt`+9|FBhvLb+Wa z4m8GHx>=pcMvH?ROyEX%6zNvTMAD1qZ;AsG_0HNgMRs*xMPr|7Ah1x>6n>WIU!Rbx zAYDQVirff^+o%FmVd0B_;=cS=Pb5fBM{XhmuA5{$CX^gd>K>tNd;Lue-*M39)i8u$ zvloM|Alu~~`DW*t3*x9MP(pP*a$yx_Za4IsuM$&kOP znIjBTyD&_q?33=(F8vwuz4}#@VC5b=BR^1qta#WB)w-2XWN|LD`9AlpS}&US6%rj_ zR)6|i3w@-sbdLY*wIZzMyd+h(eZ#``O&@Bi9YU38yi!ozx7p}(2j2!@LD^z z=Hq^=#||B`(#WvR3+)d*sr80BN|Ky6Jt`#Qjwg11 zG(HT7qi~b5*RMzyF*&HHxNqS2WkJBe>I_J0^)kQLmlNmelxf#>?%GJIl_lQcfQhMcCHR zpjs9>tRLYo;~E98pm1*t7SyL+0x}cVhI- z>CT#lG-N@6SO=jawi;8;(_?PT(9ie_1fvY;Jk2=I_w!E z!Y^R`3t#8*m?I|Ud>4es$FXWl2HUO$%~7*kxDsbkG4Q&Gd8^ez857WVF=K{GnKur# zV9TxY3P)fpjfiFra;dkVwPR>95jhb+kD|;*iA+l2Oqxik?B99KpfozgmzxwxSylWb zg)%DWt{5oQP7NgLljJDmH3}IPvoJ+PtxxycCnYT&69cDw>&}In&F09a^uTC0WeDa( zEL8Nxmcz5q4LfwxV%sU0hvQRh+z2C;vEp+E2B3SEF-f|#6-mSx*mK)c0$fDM7kPz8 z?`_-7=l0}C#Zht53SIt`Y4vfg!7WuL-bBA!&v`K(@{u2PXiuNAgvs0jjDCI?mYq<; z@mZQ{ZtFKytujvz#Oopf6!|7kA*r+I0ob}^W8~7^gRdfY+9S_F(zSHB!HwR(Y{(zI z-ibb7)VpopINsALOXkwt^<)cm?aV--LZ?;j*$ezC^n=3iBOB=!JGQ8>rYy~O6p6Wf zY~=*?XKaLp<&Qo6W*RX!e1xBb&9_ct3YV5z_iE#2JViml)_rvMZsp2wS_7iXxJvew%gf;mkQY%&1+`Gi*e*2*B>O@GO()_#LH6z(C{)jcjQ~2H z)FMk)q>Sp8;Wk^A>(}J1pqse|RN~jF+6{lt1bbson9)wiI+YmW7Np-sVNxH|T&AA! zBI7Xjs!)N);7)_r(h`BeuV_SgPbsHm*uRBUVktIpforWVBjVz-avd%1F&mvltBvF? zfNt|pMlEQ@*r7Zr@j1anSI{yWHPQ$!*)ikAEYb7Vw$0#qFN1VR2OI)KFA*m1z+qk`Qy*pW{`d{N@Nn-0){$edMYF#Lln)aUBU%x zpbeNn0tProp-?4C-fLh&EA7jUs3uXR>mE(WMi;sRvb?M`LI&#S!`abZ>*?LAUzBEv z;)Sf?7eJk&T&RX^Zw74e7XPe{@Ple&hu)^v@rLAWVA)heayJ-&0YhI9ste5a#M@pF z()}*Gekga)6xf{ah%_;p~T z+j{vjFu{}Ns1UWUeQeT)f!3d>d;a(X|5DX!wu&XZ9eRYc!uzZQ6r{8oI2ArhVA%G? zHyb=YT19dD63$YpPa%n8ND7_Z+Jr5NQ>dEfM3VIVW%dBxo*UEF9g+=Z` z3D|>we0$`qMMT%+#&?bKsMuGo8^3qSNM2?u$wL0_nc8UkL68&{gP*hNYcXSBRb%cB?pVTSk*kfIOciI=QQrZ1JZwiYyN9#?{qgO7Q!32 zgX+p(BAS0u%GTgED?@bG%^)gzHm;AuU5;tPf-`#gsCDOP-I(3&c+iFWwqT)~_?WRs z0IY9YJeXjU!Nm%OqKuR|k8Mk;_D%MBlM=Kp?lshdEZwvMKMFR{C5D4la_j_TyeaQ~ zdSvtTk@H$=sJHwFks8_|tO%{fojwPmtKj`Q1zQ>HauCfT53_ze)l zTG-M87<=xxy| zDdO)&IMC;(lZM18FVB?v=R|Rw@)!k9^%zF2N_oFCDrd~Y_ws}mz~dKX%-kV41cU}} zQ~qUWCv|=_P_%uplL?G&6J|d>Wk_c3gKFN@F)jA%#ii3cI4UcpfE7lu4V5L?>N`$! zk)h#WZ(15(Finwk1ceGKs3lJx3!EAjUatNdO{TJTR0f@n1S1an1=2=8TU1Ml9{F^EsNZr(g5=z%U97>sgM zril2uR`W@#-Wt5t4Bn5Yz{|T;kcFdy!DE^@u598ty3OaS54s~Hb)tkY7zz6}Z_G@k z&5BO9g?I?$$5+Ud9=`SC0y?M!A2=yUZ(a`GKLJ%Ec-W*#J(z zal~$;zmv0W6y8{yxu3p}rN~roYmS7RdYm}J=#D391J6{cb%T#4)$PQp>Q8-uV-c7&nmY~uoMX$~7PY5dy=uY?@pM1GFC@wI|v|Qrw-=$Sf4{wk5&4_=sF>gnp z*P({nvArrS(l#^E8wXB^60 zjj8eIprA~2PY#gR{Q)B%m?ITG#X@32;je#;)B6g}9@Lo{@=*J&tl^#@&d70hV zqvdqNZSrNvD`pj@qo;n?u+SB3dYiht9J6DcMtae}KQt|F%fb$wYUmT-k7u?}UG8yl z)Fn}2q?zp*uBGX@u7bNWI76Nt7RMm)!sbX2Hz;8bW%E3gv$UWV_F%`6i4Cp7qpcfJ zDggycgt){-@q3Xf(|fbVc=5I>92_~)!?urM`!cFbfKnO~Et7=kL&!+Ci3&hjX#21i zKFjJr(e$x^2(e2@eFplc?uR%6Bo=N#WU7i-P3r}$20vvC5=maef9!lE`8^MhF~c2C zpe=9m1d%QT;koR$`WI=uIaOv;*&wjp4F`WIs*eFc#p^<+tI9=knDS`Y5Hk`w5F|r_ z4?}k75;f>g@CXGS58Xp^u#Y!M9~*|c8HAWY>=({SS*)Ox9&@4z<~uD-@;AQcA~6`) znp0N7D_`!W=)@bxJMyWUz#U*pQ{cN0!i%$t+J2M;9RU6#E3;dfkcw9t9*NT*lcI1S zbVTz`ZG|Ev(sHZt5`F5KoNfAh|<`q^eO8loN$OjJIl2#PXtQA)~wGv&f^-Al_TjJ58Pa+M5kmz-NhD0 z>XD-aM~}AOprfr!hqfUw;f(eLw$1NUyo!L*Yc&h>8ZR3PcRsr zpYsNmhGRf-y508v%`$L8SaCUt#Le-|`Pk(FB`->6b$q*QiU>;5;ZO^-`(W`&3^SQ( zkqH=nN4>YBjf+!y{$c`$oM{CvIf05nmqxq36o*w@|2|2@sQgRAPEnrIYoiG6NcTuA zi20@ezU2fusTA{G1B8BuLkp+2=rSrPB@K@xP~VI_i<*3sk11&W&=Hk2t3r5-zDpV6 z#dQ?z6_e_cU_h5fCw*a;JR+eAljWPV_Vci#Oh=B8idNeaXLW~$1j{iF5rJu`*b1F% zh*c0OefvNb3TPm=QtqJnS&kg0IhUac=EH`4_JOdO2>dyQq`rdoW9z5}NrSU|aEVe@ z!0U9?EzH~X@v58!f-M3vXUndSwO;G6qI#e7_sY;FZ`~pD{4qHs6Dq@w0jvTvuB-~N z8+2+lf)Uo1oXzp{W-SR*n2#9tSW9am$`FVl_l@Qnkpcu$B>@qN%5&yQ1Sw+BnKemL zRfpwW%f=D?SAe7)%1{97X=s}IQA|YiL6S9K$N>{4hvtXo3ypJsGLwUJwmpXvvPb`i zPkFFE0I#G&1qC%RlILTgZcE(q9+YC<%6We|>5Vf%t>CBZCH(2j~p;r3-+a*1_ko zbDXT3(;;8uXXy6+1Dk)LQsHjW_wQy>RZ=1Ndb*^$3dPZD;?iXgYVT4mXTRmuV@H@d z+u^8>gmn-Ztx&?PG9OW)by86jFo4ZHASsxOGZ=Hk?0FLtV$3cds2baN$3E4A#Cl31p{Ux18pUuLY!{ z4`cJ3-aWj(HRT`W2eeMg9XCNOM0LZ3*_F@?(ptb*MXl6wMq(2O8`(E*p^_64!N@mh zN}T6Iy|eL?DEPiQ3hfe{h(y80^dA*EwBR9&WeP}~^-1)Q!~NsxR;~NduFokawu-+X zBk?;o@e$fU1Ti{AzikyOdXzd22eX9kBS`pQkdEjn{K^EqmgG`{$d@+XqZ9O6SY_gu zVF`tjkVmDrsCq}^dc~hYd`tGM!y0j&M8QMw%5XSu{5J^=s>#z|3VD@{Gx!}uptysk zT-+YXFP4p2TEnMWl(`?Zi-2;tKPjKmJ|@->q=`h8(^8lcI;rt9Vh4rL1X0bU&<>to zQ6;sD%}9Rgx_URn9|V~;>{Y$#W1I~`l^ZP`I}3}K2ERDD$UwHe2|PEk(Z?gSX5)<+ zdUVERMQ8fU8wU?*Omoc^6-f@ZzMlOCCI4JZ6pFU7w%(&U3w2ffD{wNRM)kBsFp1D~ z$hptcdV!tgO9it8id@_=mRh|S1`n@*{P87e8yPYawPY3Ej4zfgPmjpJt2xkQ)}yWE z8!BwmbeSH$?$nPCXocC}BuHU>8G_#JzpON-o8dHDrRT}GC=zG4n-7RYj5gxvKZ=Te zSOn$?;)Y`Oh+*oP4+?!cN|V?jhT*7k+1UwXf3vmw_`8RK38Xw0v`a;iv1{x~`@aLM%hM*qtStGVzXCYf`q* z_(Exk=MfFjEUpAv%V>G@&>gR|FJndsyiouJU(}m+h$7w~k3( zW%y9pi}!Z98ob(Mvpx~OfountwA-jxjjOYhbyE7{fri?p4n@6qdH^jr7&38fVczz`O5|rS zdy!`@=)KgM`o`*xTGX6Xu3ZvA3j2C&@tIF-vj3*NrQ~{bnX;X!<-Ae3z#`X$V(A?- zR>Eba34!GF`jUademjbn#TO6DETFmI1 zzS4Ag!l8Mt{T_^WuF)6(;xNHm4}e?OJGCJrNUFcL`Kh&jmc&pBdHbLT;X{(%Yck+$ z9rjdgp4HO5J=y1e6o0fXPkuh0x`e&vK^jbN zLp|T>34R?^3!C<1=U?}@-t=y2v*M`L27Wk8BFOxfx|1;Xni@||$FAh)b)?sBW> zzw>aD<;V80(-5HXqbXyvg-F(qA6|AbNFJ@SK>r2 z1KK76v~3*m5M?RO@~rZr4@<>T$Pxjuw=^e(_#E?V8&W8b5hz8G9Og?S%wxe24~VR& z0*ZpRTVmJdRbj=qb<5uLm(abvLXYTU9@-jw)?ms&mfc8AE!QY0D)J>g-lmy@O#5rY z6WLsH{weaGczE8jONV{}7m$23_L)sEBHTLA?Zbb6s1(3*q~4x|K72BGM_9-U=s9sU39y!~V5p@k##Z1v$ zRm8R`n7%GrkuQ9-DMesZFZqp1B@nB$^Rq%jm}XzRNYPx9EK!;LbE>VkX}0H7VYmtx zJjuxDl_{Gm<0co4N93{5g1C}PR|$ebo?XxyrGGPoPNS1T35K!QkOYXJjNv~{hQ<}) zj=PwUzrPmNOe$M3S>%bIQ{zQ?gB@@uBh3V44xG940Al0GE|aM6Jr(w5h1=03lZIFbBq;fVp3GD+(ARJ!+=|3t4d~)LXIZ2?0`BfXcHj8 zbFHKWn9noh6O;9%f2%6a{o=6@ySg)Fj7Dl80r{ry(Q=;~OrOv@ysCr@xCg4Q?h) z0>WslwOatjzulyT&7q=aiqW`VEU)869Tu$`L`7jXD3k3&LeBAPXqa?S`Pd|7 z2qFA79}#)cd|QZvZPO?h+Y&M#*`{8bO5oYngy#14(vLt|k0Chlj3L@1ZEP_ANPmHY|$QXQ!wD`4GueT7t zb9DaP`^6}`7+hfI+Lt3byh=*|2RmW|5RYL%|k;X#f~6nsc z*CEiAl#o!);6?bZ&&7Cuw=)?`YsI9rCORFy;ceZau=(}DK+fzi?8WFD6_MBMG$ml= zMsh-4ss&nJ$hgT~NSX41@Jwctel6t^3f!aS7D~w?`X92Uy{}4vADR1Y?ObuRR)4U} z2pv1}O4qjvl5YamQNHtoGN&HSZttO^zz9Oa6hS-=n2);DK{SzE6Q+vde1;^FCjSC9$*dy_*- zJ%hTbBmFU~CdErX%Nyeb$#OsI&ESCeA;@k@I4(q&7^1U1`s(G-VP}*LfJS{r7`{#t z3XBp#j3T)A zE{aoA15z}9lo-8(YRQ(SblP(l(>v_To=WdGwoOA(@uxpNPV2il0IpNJ2f3e-`Bpo!hL?RGM5E3eh8=8p>5^l_lXR9EPYY1}o z(k*0k1kU9Jyl--}Xw&XwA1P8^Q?cdv!cZY&l&Kq>B9GCGmdj4wHT^9dwMXYPap)$` zHcW`T%JL;fA%H>*c_mB?l#JLN?qHDW%PHjlUn{q>GpoUxp}-?hslNMUVKQVajYo`7 z>$&QaAbR9@gn)v*X_q1S^FTc3n^;^>(C45_gJ;x8ksNA!J8?Eww{X(y5t1#x)f`Qv z$afQ#`DUDiAP+HE#XzFQfSdoe-ssF`yXbms&A6+g4ZQu2BGnb5t5;(%?va?q$&kRJ6O8P9QtkTz$f0HLozGu3sL1T)XQ$jv*TKZZcy0*t| zK_TQs!%2>%4P>HGk!Wh`(xKdSBv*e;=wIYw7-Vd3f_575 z(1=MApsGiLJ4hjLR@)szko>7!=Mo)iqa96vMJ&dRf?a3#D;$evQ z{_YY+Q+@rn5PCc^9*jnFAMTfUSH-g22#!1STP2Pao1A(Ln%MXc8bY?jv~j`xipY2wT{IOb13X&AJk-5nTR+wl5td2i1=+j94+tN z#ltppQ4jMkmI!9MfaNY_6h(w`qsE!^;@090RmQ!EZH8N8Qs0vKiosb!dcr~y0z;3Y zc?m2$yi;?v#SgG}?w`?N$lDPxJUGnrqzyF6ECSA6iHE zMmXjfI#M|SwM2gyozz_z3C})%JT?s!dVF)l`84z(f|d!j{UQ}Ap@rBDEw3W{Itg{I zNJZsRdQPFi!zloCuI^&>(+Blj{~CtNs_W>xFkZX125*_wJ98t$i=ehjc`5@(yd(2u zT?>W>QqvI(U(%#Yz#1J9RBWcyAngI(;j%jXs@elcsgk zjas-ld1lL{O~fH~9q|_tC9}!DV`;gM=*! z8ip;mpc5sz9uI7RwZ8;>dJ+ele$aWeoXuWdAdG)CWRFuFEcP@LxmdwxSkc?z&}UJ_ z08WXvLj!wjn}~#TCX9NPIc`2z*W@bg%&xvOIewG`y0STb1mq~gp%uS^6(Q2#as80L z|18VSW315517}JcsqYkA`{6di;aW;2wkA=R*}KLiI|h=(ZGMB;EvE)S-hI2->&k0% z9XqG;&yK?V5qPfiI~0EURzMh8%w+%yGtpQbwTJUzWxcJ04&k#-5q-L>x4-B58gbL6 z2xm7dvGamFUVE4Zr@ae^f-=YsOjlm-GtAO}f{z+x7G{VW%aDvWBS9C{t6kOzj6H0^ z8YEmZmqmb$bHtEg+s8(GP#b=%AwIf3^lBpJg*Iv)ludv@gk@!u2{OHFA6|f=Fq7aj zD+OB~lm_FIcUcWY;}m@2*m(lKDEH|8!o1JKb|~q19`#wLQ_GD~ON#)q2!G}Hvt*)$ zd9t^xsn0=5lknsVSWEoU0229mEB7LcH>W7Vgsl%_@8?~uWwUD} z`XxhMRw~@(gYFi7+syt*GUAJxp0gKYG=_J&X?gwDFQyc*lF^iqR$g!<7wKhv-j6q& zzvr-n4l-w3hE0T=>}pxf__W3O`L&E&t$3^wrU9$^^ zTq~O8NYqYbldSWw*?>enK`TBbRn4&WcxtJ4QS?lHx}AtuYG_I?@`rj4X*rCV_~hukuD?XojV7i&{J2ZIr-*=BAMJ&k0JU9NIq# zkz0mMp78F9fe^?!Lg>!&0Zv9yf1mgsQlc6Q2-;;B1cw%=UqR+R=4DvR@&Cl2mBVKp z^$`k`%+4)*RPDpZ+$`m!LPH4&7pOZJ^plAKLhYLIT;iCK$q`45h2sKPP+o4cvJ{4+ zpZ%hK0QCWZEa(A+(-JPhPI>g+A@NBZ4C1@Z-ovz)*y?$kP0pSY@G|23zIIL@AFT2F zs-71oJ&Y}5MHOWGq@sArAoRIn$v&m}RBSsfUX8-fT)OITeMh~nx83g&vx-Oqcgs|* z0bOZp(4vsA!q{KcO(H5w3TQmzrO>)0VYDJ+$~Uf)iS6H$2*$^fsf}xz&Yd&Y5X0HZ zjHgQtaD};It7$bx3Z?b+Fq}>o!)(VO$Jw!?$W@^;heX|Rh=zOW3}!StFr>yb+lI=g zJcd3Yp$`6a*px@(a0;3x=(&u1`w?jX71o9Wt9FhHFEp(_D{=3x62uA}6M*ayf6r`9 z{auu7q^{SrEDhaj2Rnth^rvap#Bh}zQhGPu7Cg6vIMx20KW7#nSo9ih-fDL||8rD| z?F30se51-f=q|`|T*15_ITLh-woarjY*hr4YRGl)Q{BK8@AEZqf4Nti}!Cu+IxrT8t+nm2+GO*-^Y=+7-}W$WHpXp&=F_>|8~SXJ;k>(5GYwS}>~9;4YWl$R5|{36(|VO1 zwA-mm_p+urSKUi)o32KYVnVxTZ^R6m7W2CBzih2-%sCYD18CZgOx?(EU;#>TVzC z00(zo?At;%HQ60Bfd^w)H!PbA>p26=*O9x30bYiwULWM8Z1)w>k0~~hV*-x2hl`^5 zwvGQLmgWW69OCf}RVH|!GS^Kqj3uFc*8R z>e>_(uv`W0+l#JF-(pIhARC;Vf_Ng2GxaJ;u7u6$exj3mrNpQ&j8R5-_%w#@_dyFn zvfSFh;%61eB05sSi z`Yhwg!&_DQtF z@0MJfCj_nYMS;n0llhGVkt;VYD^)vdca2fi&Jxmb>Q(!TcrtN+d|{4d!pqNB58zvq zN6-gHE(cK#CVr}E+uMbADdD5Fx1CzLaF1G$h-i^8M~qM+U23HtrBU;fPGThCE3r#% zopji+n%!Bnw33WI6yuFBU6F8W<0iVBzZHiZWi_U8T>yt@>h4K-BC1D$QCEsYhW~%%K(pj127tbyQhk7Ay!gYzjdO6Jt%k64wTo!kNfR0(2(dmneO zNT(;B$nIq^p)NRYG&JB=)I$JLR%< zzmjY5$0?7q491IWEL@6lbW(tFH3cm-iZR96WL+7riuoI&%Wvc%f~Rk&UVc2OqyLh0 zt)zq%Ry*TI#p1L$g8ypa{k};(6X(P$bCI95$H>}a^Py)5qYzY!9`U4vuN1P2rcC?$ zlVNL5_VeCzjsC-y)gptp;v=bE95bAGZY=oqD|OdI`#wjEs&x1K_?Vh-aSb&0BW~pF zs_jI6Q42NGbW9u1-kcK!^Cb(GHYHzs2!5ZWm;*f(d>Rf96ldZ=5^gw|n50nHT?n#+ zm;B|@@%4;pV=36ej{7<&-t{k{6hYExI-_M{D1Igphg@gvS5->f7_GdMA|ZD`{{(7& znEZjFK$xuM77w{$+D~*8T*P3WT1s#b5Q4u3&1k}6%e}2$Kk#&_wV}x|e-b-#^-6Fz zYTo-I_g zT!2Be5zcJp=#oOI`tRcwDTDphmGbYOy+Sz4xg5n@({V^nWI{v3uHv~MNTwqAD3yoo zXuN)7AcX>t?kRET5$a=B0h5q9xBQG;s!LDHZ2bYy^Icm_ej+o+SP5`$Jv1f%z~3yf zP$(J&Gv_JQaf`vy|1lauI~cJY`u7{0h;ONdWBoh;0Zu|S9*(5HDdOq;z-DAQ83$ua z$3$3P{qZ%b;Tr8TR6eMpX;~)9WQyE7>E&uHhlxf)j?>=2#ILCvT8Y37Yr(th(MYRWZ!h1J(B(s@fbpan5 zN!;*SXL=%wfQf*u8edjrRe}VIxd)(`@`S8pv<^cB3GPr~O5j%vV+_XR*J?o$HB+kn z4Y9}N78Xe-Kgh_5F}hK3)kB?}_`hl5D_2M)#Dg!nVO|fcgZS;a%r)26Q2> z5s+VrrE-t79bfCeEzP8gG@&>rv>9OLf`*wCd+8eHPnwf^d1b6*BBP#@uy{NcJURbR zn?^PGElmeWUbqANIGDFOsRx{weXt5hSaGCZ5!UuYo_#03-SBZvVyOHi@C7fKc={u! zy4obhWSV$($=o?lSk|VBEosrdiomxzXx0$?t32;oPxD`smBja5{XM|GkytzG7HB+i zI+_xONpRW*Wd-t^I!(3t7vo7RQW9G!Ly6#|(XcAj8qJ;fwg=fURXgNm3T~Jf)b?{AxFghlwu)YxhxEJiZS)NI7FL&!Il2W z_|u~DS1!2t%?WR4WaN05$M-KE7P>R_b}bE5?Q~_J7SKG$*`2s}@rt`P6VF%tDnv(# zFb5Oy28(nbPf?AV@MPu!z;Cr6lx{K#EY5&jGQ`6&(#r#JWGyDOXM1CKL7XH!)0WSWHc&>o0D5 zS0bJEzjr@awn>pb_vpmH0}$;w3^y;zi#CF!#oTN1wYo5-P zBKPi8elw+db`nlW#MhUR`Gybz1|~kx)*uH6Wzad z+4w^?sTHI3FOWV(vrBcNKzGJ*RG`C3rwb)b3H zG2>8)%R{9^uPtgBJe49tAcmer5+`{{ckMtKLJJ}L`+>$>9w!FziW(a1tEOp!jk`8- ziUe|c5+g``wWAGqkR+FCJMleG!nIX)1Exf!WgJwMv=+^n(5_Xq)Sv@`bj(;%W)Gzc z@2ZB@YYM(l#Z<}C#p@me^!LN74(|KfT%uUcU|}+(B_v$!tp1Ij*ivQ!BtjAZ7^_ZW zOr<@(=633BJO%nWl+>z3PW^{!OSd>f(E@ozDI;uR>SxQS=K;IGAvIp9NAeyXR&TQA zszK87!&H|)M~H~41*VL%r0>+ZHg4H8u5s|WOK6Tf0x0}ee<|?ixzaq?qNg0;gBD_S zA(=kCH%5uabf_=}GKd!2$Hm|v=pM*BBGu$WN8UeUKFk(Gu)XRKFBbyA5bdb9su7m6 z&HoE9K+nHtmRW0-n>^F2HS2=1!7d-&=XPeK!D&joa2^FQ1^fOmsnrrI8pg#BK6(W`PW8j-?^%>Y%1# zJ?EQ-4xVGt)JO^*IJ8ZpC%76145J*l%rM_c)PW==CPc^UnFSlp1Zig~W&`_FpnF1Xi-ZmVYk(M)eBG z?*xE7f!3hW&5p7p?Q*68}WEeih55*V?c8|1V$59nxh+M6$Er*@mi zJXApP#GbfKPF`P$tQWePqVvkuTI#?in8t{3n!IC%v?}j4r2w!9kASC#R=ij+*9OHG z#-mmxq*0CxB=RJDD0w~`DJD0d)6Y1526{m8RLF~s$q&f?Eg3~%@3_}Mp{;>m*~d5x zoZNOGoqVK!^*FDEN9}TgK*FJ@=_DSdb4rO|99j7}i zg2nv#36Zvh+*I&0=IS9z8w?l?ItCn>+5A{|YTrTa@BDjBwGKeFmbB{yd@O+>t25QCl;N0D7+GD{+rcr@YAL>3O#8Ao8#IgKqSs++?_8G5&SD8{oeu=_d^ zPQH8nD;}21YI&})RXV>w;%I=wYD<|FyXHY^?LKFo-x=#7y?7wKIv3- z^qm1Qe@X)2nhgT%=@9hxADhYWm^{Tc@-FZ!qeoY1fk_A4>jqT()5WL8QpDkH*#t3V z^q6CIQ=9(-bT*R}(w0_YQ)=so&l84Kl+Z5n_IM4D?fNXDU3A8N-eIYMzQd4^ov#`b z=OMNrM+ovoct55A6Xn^vCn>bwjWsr@k4zjGJVJ*ReuHoK9v2Q2k`mb`A}H-Rl?HqUD-6VE}d{ zKiY)If#boCCP?xG(~-F)BEZ^#M6w8VRAdwTF}}APoU|_`X>tS2)FX#}h+&5MjMjD_ zNb#H_>vxTmnK@S6zz3gUX{Kpb!u(?ki2ZQLB(z3*C~FZY%k+?>R6`9}a17CzKq3IY z6og`t1{o-1@G2?dYR}K$O(bYXbAjQ}KI5~Pqd(1cX102Xv!a@YQ0^N~#8EJ8PR60Z&V|tu8sG~O zUg01sgSE;DQ>mer!Ua2@c@G^BO&6vD@JGmi z&U46(LZ0n^Cm*K{l&cM()za{B2i_ zza!H;u&@;2AN1^9oaU4d1gFo9wWGCeFu5eYJeffpbny^_WC#XJ0Az(?c(*5u!ww*2 z>4*TRoV`h4lCeIr_;@H>rQhFv7}IeGP#9+H$ufm90V#rx)8afQ7Sk}Jj=ZAuQdNny zrWg}qxG6*Hz%)puO@?vnTI;SMggHx7pQ*lXs2EJt0_EYo7q10Uj)2(Y7Mn$zM0 z2;K!2GTt_#I{tVG*R7UlY{@JXLCXhHjyR5jquHnq%~}aRseT#fK(n8n7gEsrC|t9Y zeQwgw{od@g)ecMG4f=c`u!$W98mz;RR17*_1`sMe6pt1vuof<`Rq6V{GN8pd>>HUc#MOtPD5%F% zRl!K!W7Fk2A||J}`DHS*>7KUI?Vov+c2P`yJ4_5MQ4$6eKwPqOdmn zV5adY8IlxSSb6$&EFypH8%8qJNf`X8ODmSwVUgNf07D@1u`==`G1{lR)nCn*?Uaze z8ERJpU?O{DDgeEP3u+nP(dnk&8#Nh(@(X06EOCgvgMvge;pb%p$82x+-$;n}lc5hp zpG$z+hc#3mp?-|6fOKsTDN`FHP^?NB*PUqO*%1{BycWECs%9*x09AB^as8SPBrK=W2-Zg zeLhUvw{SegHUv^P*pRj|RI9YJEHbq?Ik3&E3*mcMp;4|kJ_Bkh?XXo*kz9jEw%|O> zAdP*cBGgJ0uz2SQmQ0E}jenNSVxtW1dv@lN9q4kNGh`W~&}NT9s@F#3veFQcWS1y` zA_lDmAZ+3-4aow?Kq??1S3;p;E5vHNBm@9?+>D8%mIOHPL?$WL5dLlAqP=Q83Q;yu zS{b-J7yI6|9OiA4X@erlLErB|?E4i*3?#}l>`N$&p8gV=Pvqr?ED=fjrWz>1E z6FUJJmx8-a{V8)|W_~tK!M1E{FWA%5M5f8uw@Dd8EY07aYO(d)}rCQOWY65heABPXqQErYW-2fDnrkO ztE2rPTq!g!0x0Atth5e&kuT<(yv#_BF(!)`^SNmJ#{k`<*_prG*ZZNUVx-d-uMkDp zqEKQI!9SFjt0+Qtg)D(CiD&TKLOfrp4g}VXzzU~20OcdVBM3yKcE_5dW@g&?l+>7{ zIv^^qF0z7I(G0j-EA8yVXg&h}`xcAvUJz~!1AmeAS2x5(3a!zyC&<5RnWQK-hqOd_ zc&(bTi8g`G!B9S3vE>@j!HHKS)Cp5?@`OBIP{t;Eh`m;7d7&DDdR06-zI@Q&Zv-Q6 z{oV+P!PH+yFCt{2@6g%lc(b9)+5om{bif=Jxh)rOjZS!2`BEG>Gcw_ZNM5K%vaD(tF!1aj%Rtq_uY^j?pqW2L}L|!!!mNkhB4gzT$Kjv@yA= zJwzG=JTL{22aiBJS5s73{;d*vfJdsGM)K*(8akWp3Y}5?>v&b&zt{&0_g|ruU3^hPfd@fw*3_UfnMaL&{H+@!#6amQ70ET-< zu|Ypz1`Fs?6q8c@vmF*bieE)i2%3jEB6eIxnYLdXs1Ypzl<5;IWn&Y#J>jBb*0aw# zs58CR#-X+&j1K(EE-YHLf{8VZe`mqWH?1F!a9p_HrTLM<2Dz}*rq39~1`Q$QRL-C%0vP5VD zRJBqG!^prX8%vOQ8Rl>)Y*PKEMEU0X1_6a1L<0{AEQ-YAIDy89oQcuUb}=VR@rBu8 zxS^a4jNSU>db0Cx46A4zlb0|pv~5w4(c?Y5GGSaDXCX!{au9dzE*%e(k-{o;TUrAT z?EJxOx1|o@G_ipNNf%>syK^T4yFdxqVnuN^N4mazcURzTMGoA%!Qlgre8$qF+&32E zmkbg_VtL~+4@!v(%fsYHoQpl|MfFJc(u-m!lnD4mQvMeM{-EE5VUY#LUo|A1)_fqy z4e46XLQ%odYP%q#{E9P%MIfveEH?7bM{63%dxtUDP6Pti6c6&Ic?%n#Vdik-WhiVY zI1v_rMF!~t6aU1NDHo8)**-``MT3o*Cj=*f;-8UE;caqdzezL2pO{6hFHn3kOji;( z4EIkc;b@F){zhYjuyu&-O=+d7{`fV5Vs^gS}r zSlnz8Ufy^}Z1`vtnigWm!4?Xime#mJM~<5aKp>h-1zL~HA9X?et-KMkR!ZBBSEup} z<0}P0xUD5UK^yKajIh)6%pnU3$6^cnUjs^(WJkRmGGqQn|94Rz9JC3vPHbpaH}2+m z;UNGc>@|wGTc zn*CC)q?r!38f)2vsgP0}p({#+tte3(dAODUxSkY_Xp6WM(ycQlk>? zi90?Q2y`8f__Bj69I2m_C6sx+$`Ci73zahi4QQ#f7PvCCC--9`@nmIR8rm3^al&0+?ciPZVSfYtY_kBWwX) zp6!T*Elqhf2}~d$8UgO(P0b9H5-m$5i?4DAMEqWaKU51A8=pheK>-U2!brk25D-jZ zlt!DGCN4@pZHe4wRFY$vCjp@%m`2U*lR~5YgMq$kDT+Gx%+D)Pl*Kww`z8%2&`4$& z;gM`8E+{mJ79N7i?emDeL75VTddW}~l79wxVj=@)O1g*oiONH*B7l$$y;QYF{U(f> zbN(Gh22oA$&m}bHx+8Rjz-V4F>1U-sch#wX4$9!Kzf5y?qR6C`%nZ>}i}kNDb=8MW z&@a*la2TgL*_*dnu}`!`tjs3A4frq7=1b0>#>CJTQ;TuLj;|$=Zs#f^#Eso-jzS$n z_#5!N4U<;jYQLfw*}|AGJSzorKs?F-nS@Mo2Cgtjfd;|)WyyXl#t9AVro(Ji)cy#C zI*Tm3cyJh71DShm3fl-!FhCYgK3#Ij0GMny<3MrthIShbB%$A#=jA#HrY>sg)ScIG z>%2(!sh#7(gR&Kv>OZ1q8Sy~2k{-pOw?&-2w*&!cc>&HmLJI@LA&hvKQ3rw;t$`5v zDM*QOIQTChL~kTeu@e*oe=}fE4M$fJA?WR$j+b2PnAyXL(~Vfi`fRoplMeQJ8|Z48UpB~H_8y!d!9pe^6HHD1aUz1_pVYE?jJ+3wcV#7-iw5}o<8 z&AS4Hqy}IF1q{@n(RIvtR6r~&ga8N*@PIlq++i^l|0TDP=;Hq{UyzJ1OVA?6n0 z4QlwkniuXNq0ABZ=3(Ppe^{zWhR61~>Ga27j`Gh254B8-5?STtj!x0X&@q<+fDe)I zaFC3whx5$L`U8{1!ImV2V7Ukv0HLU&fWmrCtO=I2{4MEXZUW% z>9&DLp7LW-HLm7|q{-=nhk~AF6Uzu9Nc$}fQ7bZ)bmUmWU$Hcst&8(uYZeln08gBQ zNRYG0F+E}(L%f@lr$~e7laWe?ngZ6Ds&l|Oe4)ol>_v$V8oJi=6}sJ`EHD946S7pG zs{9ZZr*dt~6UahCj`Op3_JBwW-Q3Bx z|2mRHEuG2CBLVydoBRbJs&_OEv%Wc{5qVaKF18Lc)8n72VHMq4pd}P_Ao+qtQk-mH7em4XOK1+uveEcxLlJ9YyE+iI{!6(Zpc#W~ z%a(LBj{H92-)(`>k@G)^M(jDoLS`@#rbmtnbE)AMo)UTE9rs6T`Fo>R8Tt4bvx`{1(3U}|7q1)xk?AJ;`EsNSj zoot2O!X5_KVP^7>_5!!0H|+N7rH!CY!%5`+ELrOV^?*o~@zJcQuwG06Z&tI-HhTsc z{HWxvNl%VcCoL?if#}y70(3J$`vO8uHU5v75-j7>4w`m>&<7C{nO$X@v(ftV+O*RF)vL#5k^C_^Q%7jjvhR_`)>;Vm+FN|}p z)gymTb9zD5+%icdKC_YHs{l#h9$}Xif)Na9*4p^K@+qRX%9X%h#k+0}fpO6S!m_)2 zx#?$Kec=qO+g5YPdDNb+U4OQ6C0grZf2?JpM}Vk?5ugl9v4p9TqU(R zwehj_SZigl-5|e(BU4I7ot2wHR*M82NJvq#Hemw_Xa!TNSl3#@p-SQx!!Bh?;U2=7 z@7dSC57Ir9kjC3}RhAS{@d#5;1lAS-%N7?X#!ObJ0Q*{#tTKA}X@K(n=oZ40Z8w8j z-H`WFqR5_0%?P&?uV7fD7Ec!bHO2o|x_Vq&66q%du~yNeGg0!a>Cm6Um`808R+Vy0 zFcc69fue?5SA_LF0IxD)W+9-i;G^-Xx(;_@LU#@?kqaCzaFYoyp+cfr&4F^A(ku%? z6b?(lBjCjpw!f^kq;XMRRB{s&WiuQZ@C8d=aq;rB*j0$LOJL}5oV3T`iqZx-PFA*P zxGk`xy)Z(el4?S)0Ki~l*Ubb&k>#cW)6$Ia&5IF?khaEE(;Y?*!LU^}UtLKUw4t{* zc+q~-)bHIzLx@az>jYuL!j~kJaFKFvUR#Ptw#H8#MwEttL32Z4mJ-=K$}Y6L{*L7k zErl;};dP94!}>%8k|o{K%71cf!xyuL{1}bwW}&^qar3-BZKY%;;+f`ci;jQ$4CR^l z)Ya4}O@PFoWsHJW0C{#(t!RP_t`>p?-61{8QJO*~IGFe&CZ%I2zxRnz7+UWuaody- ze6`-on7{<}gW(jCawHQDlYK0-p<`#B58DL+Yl5)ZFcFHK=g5%Ihx58Q$b(o&9%6mCUc^N6v-aAsc ze7TH23DIau58oINcMYJz$zY9a#lDJxq(}hYYA@{%ZE*XTH3u+jmi# z*(?MSVWH2l(OGhB7(Znaj)rjuOi=dh)PIZ^c9TOu0Qv^LFaWl;!T@^PSg={7;ipP- zuK66IeGU`|=NLR{fJD)xb|)=a$8Q!APZ)r&Pl{eK&4c3FoiAJ}IC^goa(@a&XJ$y* zBU3yIMiVK^+^WzU*d{~CS!Q>^d|;i%U>&AFX#fjR(mdSox5_4DWD2m!X!?IkdWbo5U6=| zVPgD^i0w!^S(2L$NHLC>Y%%^q&e@Fk)Muh17!6Urj6@{4C=bT4U_BON11L58s4?PX zF>gdjJ+lvaLS<2FIbxZE+8HVvQCQu*xjBXz&tUJk*c!DIxB28dyFa)SVJTL3D*E5qWqDE7Z`i`Zd*P#PzBqVkyZ z5q%lpV%R|9YCX->J21*3l(8x(<>|n|+n(5AL8=bd1Ry}5wzdQOPW?S;wSfddz=AO+ z!7U^Bjn3$aR_-W+pLpTYsJ*&TzW2{|A>&*in$F9@WI@OArgp_)KHSg33^s( z5~`f2W7b3(+uN`9F+<@5e(Z;3i8qzYNWT|_tjG`ta71e>%F+7AVNV<6Y1}AA&v=Qvs%_gNXx=;*d6MyF0m?T?Un#o31OYwfPZID zZzNh_l4ob41SEtA6oCx7@U6ZIRZ^n0mlJ+8srg`Hxk>aaN5?3Sa|R2;Fj)4moM}UZ zEINtcya{S%&jwoJHO-jj#smn)wjD|WBYNOQlC58nohb2jW;kgbrh(W-)7%G?UyuRK zq#$@)8N|iVL4v!PW4=H@SyOn2@C5{mEGbK_y07%OMkOEMw_}S1z9K~+0eY|#i8L&r z`O$RIAgy_)#!?I{oEbyMwk#>y%Ly`D_c7-lEIxv6s@cGjum~#fakjfVOI#U6$FnS# z9LblHni{IC@p|&viO{*&-8yhv3?c^*I5y;d!(m?ftBs~fM6gn*^zmpW!m?BIcZ98y zTqmBGxINDRj1|tUYb{rhbEx^-$3jOeD1p&73z1b@8nXhKR@@6Nk?lHQ;uBp!ZM%lR zX)|>lLL}?SKA$WH=y@juIcC&!NIHkhOSXnQF*6fAANb7#OM0K-N#muPPZKP~#BHNVp!*5$Nou5LQxB$Zth)w9_gP8MVrYqkOc0 zkHJ$*X%k9xA2m3onQgoigKInz1YaP>Q0Z%VmU+=VfXd_X^0KA0ut4QcWJ^5hJ`6ua zuCpX!n_L+Hpv)nsrl<;kD+}s7la&>tnX#9|>Eg-?JD66St-s=I(J>+j%4L(%SpzF; zS>fk{L`;%*6VFrQ3Ob9LtAU*f7iP)Dxg*8$LpW0nngO&4DGN6Ga zz4D*cG5Y9&*aaW$)`_wl00W@7hzU=vjJ^jKrN|OdB_=|R$)IErcOzU3PXGzP91Hvi z1Hl^^bMsoP8b8*4*}h*`t?5K5o9(L2m_g(;hR6-;>4-nw1Y$essv5)r@mv=#!+mVN zy369O0e5E`5Do^y)Vq4weGDxy==KBE3$&*InScmzgD^d?bg~3>CN7J|hGT#TVq6_H>LXckc$bjRTuVCLUusB6cyzAmf)Ai!_ z#NL7-QejN*Es8S0`o8uSvn&U&yki0>-hGK8%rLOTKyd0wIP}F1=VeljySB4p zAC4tj&8X^{G3FU9TSGOf;e}0Tv1%pb3~bca5GaMH!j^hyKwv2Kkoa#D z;0KmE9^Cr~I>STVp^-DAxC0TX-;T}}5|Tj*&`S6NN=L#tauE?ESk}Y5B?#=6kBD_1 z?hI+lp^#}^Q@oV0SQ}71VqQ0ZWKiZx2cPjU$b?FL&64ep_D%dLZb(=#sQzpHc3_4q zOhFO*A~K*YaSpn7Q^k2$pduQ{R0s?AbcoR~WCYX27hsSq3kKuCmN9KIkwi;E^UrCo z6naP;$%&f&33H(+k6xX;W_o;%+j1sjpg`HqnUg@1&UA@RUDky%TBv-aSXR#SThC9Z zqE0FlL_fE&{ra&uWBs~jX6h&ozJOS-)u3kQ#;1c@bDs8CKdCQ!N)GOMNgPylAM5tB^Tg+x(7axuJy z94GC-zN&g^t1IzBVrkMB9GRjbPOmR0msE+i@AmGVDVox*h+UJysK8Q6=M6dl39=$S zs98&3*h(IP@Y3j|uAJ-d52&RW5E-^N#YWVn{i{27&cWY1_5isF1~i1p&!Ps62gUYd zyxX*Z73$wL|Fz8)_&gFPC#22_m*i9$rLK1YI6@mD*C{G-FlpZYw;i0twe}~AGSfQw z!C0U7L)gp|46XKQ2ep-=RAnwz&dX%Kk=HGRLSn&OW)TMJsy_rj{=1K*&{WXgo*Gc2 zn_nd;t5X*425l}ot30tixWqiA1b!O>c$yy8v)-dFG&L_|65kx4v;YrKVbDI5MHG^R z3el>MOrP7Pj_VrxAhHnyw9!6MCYp9Y1WKWQNh1Zq!Na3sjangyjt@GKro}*W!(I9< zGoj<@=PAKtkg`gB0Ul92Sa+2KJcXg)VL`sCP+QUac}1(GXjdOh0|Rh6EcQPvaEBBi z96an|jEZcYCz24@lz{N2E9Mw#5P;LjI&F=`q~&C7<<)zftjMP@-ieh?ELQcxyhY}# znQ;OSr;t7=q*m{7x~Y88brlsasSa|N%ZuqZnvZIfWvI|-gru{fY0`zn1&Uy9_%Flv zaahF3-!VeC_alhq|Hd7K$NqU#`$(ja5uK6goYrYc9T*cpY^LA_d#(g-s}_hO33!{W zu<;{BC^|VSP^6c|Mx%YvyHsRkzATp8cR(dvA_PUU;>Z~!pgDpzIf!)KvnNFQg2ht9 zM5x*Ffz4G3I?7qoSRr`TivVfRJHd zoJFkEZXfR_Xa$IP;eqzNtvG}ta$SJG&5q4E9gjFE`b*4zE`c%F9HiNZg=JB9(&1{0 zWyr5e$4?g5fi3p+E_BhcYfTh#xGL@-T5T6GH2&F@G&x9)s}12;tzbIaBnvJ$ICaP& ze^nu_1xDfs08>W02FLy635_!IVp;=mhx=QG(k_I zyz44f$^wBYtxB;?Q+L5tvdZh$lFC%@zB?seOIsPAd)7I%!%cw$0D5N!$csEp_%82T z7%1q7K9@w$*S3fTfD8*O_c9H!4uLR$?~8yH_N?EHi{OZ9Y6u7tNkB8xFye@Hy(f;E zy1z0c!an5ClOL9O*+xdH(g?FVCq4%2v4P>XWh({1DkWn~aTXvyP$$oZ`H1u^3@5_j z^`+Zb)|k^Jk!jyz6cunPNEhJ+e^=0dy~U?z$w;8q^|o69JE4ZgJ?kzX4v3@%!{UG6 zu8jx)Li+`<$4Jr70=lW!pVL;v42Vv@+hYx8p4PZTGK!^yK|7RV37)0~2@DJZdm(_Y zWJlV3VBKqk^aw#!Y6ZVl`Rw8zfFUKIMW*0MAmsXzCsH;$_L7IkIfemz5C8}r{r$5D zd{=>IW55BM`8323BGh@z_Wg;tF$51pm=?>I1e?->(hQ|5Q~@HSp6wiM@!z_77*y4n>&`>+j z06xsW@8mRfTozfzz zZ2VlioyxFOLUDBtNoW9stu=ZI4!wsq5=5lHqz<%jQa%WSQ`Dh2B7$2V*<%y{Bqxpr zSK58v zG`SZEQ=|FhA?yJWAsF#gP|xxo3%&nV;a#u9ktlmGOm__!Pz{@VFc|zlsp0ySPu9M? zeaA(C1_wjnsTOhtF-JbpXI+W;8kXGymUz#ppCbUharZ^hLiJ|XU6AwdX=E@`DCkYi z3=}IaC6LkaY~Mqf;N}WLQnyNY<~v!EXk*v|JTf7ph3gU?8Z$A`?Ib|sGDwT&^;jYf z@DX@RLt?)HeKs6-^j?MdWop25`Z*SF_ySTGf+sOT6k#+1Cdoz0C2SltLr1lF;7$^= z?_{OrkFfcWGFgmd(*g@hxl6Gk{Q-XpIj0_6N=__4;69cAsXC+(FRCEY!m+F99IQ-h z1HkwQFlgL2WujwMNFk-Q3r2G;=5^fQHnrRd1G`-$qwpTjGsy}kBbxZ1Dr*#^Ql3RQ ztw$2#r?j~|sOZDDgb;a??gQuu9g9|#=*5hMt?@;l<|9ZCj1 zEcQqS#+J4WAnm_GsU-apwifKKT0X_oO;%S{=_oixDKMnfR#Oy=sa^o1lAjj6pe#zD z(w>71(70IF1Ps95E?yfF;RSSxE~(cug}_ChZD73;>RsK;YhLDP99uish%65nL|wUk z?wifwh;p@{U>OP2NYG0V_h`krC&UzFK53YewW4tCLz~K}yAe7vj9t&o30)KecRGszp2)O(re$IL+ zTFc*{gB=R3l0c!5`xArP0!JG*7)Xp)xg(CFiId6ztZ9+lf*m;#X?Sd+9!5^XepPlm z*BBRwM;+;Lnu&1cW$STl2=-bVP+bvO?VH`;75SKt@9gK zP=cW+lc`mCkoPcV_vszRmD@ex;T!wypI}$sw zSGkxS?#QQ--pnkXWY5NRFV5JZXxqG^`-*(f^#8A^j*cg=Q%EwvQ`n(iguOCU;vEN- zU@zIu0Stu`e?$pkytDqWx9in z*8g$Cq2g$-73Ta+OPoY!HRt5%7`zn?w&ua|(q`eHe*@sk&k`J?f3S72vLk}OA5cI5 zg*}x#yD71X0Gc@0j*;{@`>Ay{JS;HKi`ejso$^(&<{_@iN#8Q2QNO{J1{d~yo_1Pt>@V3Of?LefzId^#%f zyI?dh=n-Xd$mZBb8^9jWI4Ic0Yprv6TnmL0!a^CP#1Dv;TJIV0?1yu8+3rAtP#o?tr>?)Kz|DPY8472R0<|)qKOh0N-uY? zS&<-XyFRE!FFIs42kXNOVLG+K5iKBhV;cT%dqH%71kDgp)& zsgH%$$>utLqrN0_%%VK`;T9?hB)#ddsz`*2dmc9sm|w;-jCV@k;dgQ5m`sG9am$^N zZD7LSP||v>+9wG9AU6Z}%(dV<5jE4cLHkZ%)wx3X&AUmByS}`;)eFW@-42@?xiAs$ zUD#%yNQ&~RHEfPg1B)$?mBQw74TAIh`(0_S0jCS01)VNl+_IwgHLH@%qQh~!1 z0m1J#M%#181prie;{Iw`tcURn`FnB)u=|+MfosUgz+FYVBR`nS(3$e`9#cn0$fCW-{J- zKV70+l`gtvv@?pyCR?*Lt6sBYMFG-59y7P=SB=e znfRUiJj{hf^3dX+Nh}7xaD@Sn6Ca&T(u;o*fYu$urJ>lL!}}XwE0sQaf0?B>Lyt2} zVy#S4W}<1IVC(V+brX(#pBBmxQVOkZ=N~UORTS^?L5OVy4q>5yH34u8o5L4QqBNrX z!^UL!N5JFLNH!*Ei|~J=ECL)M_I!Sm2%9@WW|fvo&?u1v;jBW>IiM{R?6#etr_OVI zIQU&g6E1zW?kwuekEum?T%FjO7V1Q*h_LxLugHDNzqf$Q$Ae5xLa)JzWGHe{CZCQR zy1M;5&tk?0$|yGqfA>VKQl`K!O_QSX`$k4-0vCsQb9_!QwD9RjUu6!ie^~`!zxDX+ zf`K`#*U1MwJ(tgaiC~Ts6ug;b&hl+0412lNDn~fqdp!GdQ=2xB48v0l#V=e z-Zzy}H!z6qYkF0QIkQl*QW0Hwl;>%)y%oUdn#@N04uw9;0I2{h>Kksto%Gz=xnhgB z(YeZSjkYBO3BdYSv<0h};;DWjja)bq&Nr`_1N|zs3hw- zBNC#^WvvX>*R>2&{Jngq>f=lOCRO2GkFp!K7B#3-DVb;Dqk;iwzE<{dn~!|EcjC445>}()P{b< zz^8$<1M&7iz-aM5WDn6INCyA~X0J`n1P*oSK4CzvaFP42tD@&CoV$h|wupoLVU1mn zM$rgRiW7j@v+q{ib}?Hy6%sR)N!DCD2d>M=Vw8qZwpj7u_l8XhK(`7YN%?hUOcx5z3~@%eZ%$4vBxE_@q%u#}-1&pb$uV$*w=4)7;V|ZE5$An? z{9I;)2{=%L3P7i6YKN9$XLEdik#MMHU1S`PDU>vzxV1ANl`#~+Z7z948>~;zO@QH~ zQz`Ok=3%}-%mDYofnd6^5xE}vgClw1%oVuSe(y4S6ro{UJSJtz&cq9*;l328SEN0J ziREB3u>~nC3&n$^XmHnHao*#Xk3C>C6drl7{t7X8TVMt$0>gh7W2y;UfzHci5^E{A zAjoDwhU<$3Nf$+sDx)#@<{^$4RrO=IWjOsz6tKiD`|7ptclbNuMTurBxGQk;8EI=7 zP{QGVgCKjDSi>VyS%65N60zB!ZF-~Khd}XW<;qT)1{FR!9p&*4P%4py_sRs4A)>S^ zE@m-VKUc z!OHht{0<^eb_VU1#JXr9c77(D7hEdo+{6e*O$7S@*M{{GUMNIvWD$AqQ z&=#rOB=m@f09RTZ$vHXq+2f3{Tg&lO6GQca64!0=Aw5UE$l1pJSEU4%g$TpG9kKHIqV!5 zgeI`@2h{R>Z3Njj-G~4Lv*!?(VmAOFbH2j73`2+{U>f<1lxjT|;a-gfDPi=*#Pf9ldF&jevss!IsT^wf9EB1|385PE*HNG`qdf@G z1_m(bjwjzQW&azHfE|co3j-|^%=7{`4EHyFl}=C>HYA&4^3g?+i*I=b%s}}^8mB;l zh_!__{Zdy3=!|9@UW4(FrDYKrMZC?tZl~{q+CodO8-*y(hRh4hOK$GguBQ!f+tM?Z z`M3v{_ok4+;-Zr=Dzi1bPOQ39yGDpO^@@jVf$N6EX1)nkqCTNH#!vSt^@eyqAre-M z#C&S)u>XXeEKi}tDL~`T#6OgH#$g>>YhBZsNLr<9Zb0yh+-2C&Ar_5e3SJ_h#+$_= zmV4BVq4~PWPuncYsg;H|!n}|+cpyoIM774v zO^--5^f&-+{-;gsBT{H`)h7P&H7s@2!yT4Rk%lk|bb(1`V2F2t#L9DrR)aF&m)D{6 z*h~Y;W8X>Q8#;~v^rqD_q#p-Jx8Jb1!bs+VfewgnX`Rp0clH>+LJJEFLX&Z(9s?%% zQRO$<@Xc-+H6Ui1JKUym+-IFW&|OG!B#+gRl#z+)cx(k3OdM@aCyS$}OF$98TO?6_ z#;Mk^JQGrumPEUJ6Voflg1Q%H&UF7YFA3A78q?qTf2xXD*gn#OI_j0tEiU?!{O$}O zWj`g-VXyO9eZ8}k^C`V$c2(JQ={2~wt0nNC44eFvtO}(PCTm!q6}7$mWRE} zw!{JyaK*sQQc$>zr+Mk(A*dC%a}1f|g@+12-H$_gG3_80Sk-6uWY=;5|z`tFl0=f;#mvlGQ?zli^lD$F? z4C6mPY;}ZO!ghjx((8e3Wq!ob4Yvh2R}FF`%K4=VT-FoBtPwG{hl2|uJp#RTG!5kW z+dn9haS~>!qX0{xE@(jLur?H9`H5?dL0zIZT95I@J1-Z}>(q$Z-$R zgTrU<6Z)YW0)Efkr~;NL?7bK7rD#f~3iaa2oGV2|W;?|ByTi?Q;H6Cd((zGs?*{Q$ zqusfyzr098LnDxsBq(-oE~!X4oI|J+S_lteX$SyxV)05`L(MJShk!f)Sei_c$fz4y z{0hOQ7YeMa{Jn~oa2_EA+plYBfq@8;)`abAB-7HW7eP?IAoLL(fuVIJCMeTG?!4r$ zget<&RS@b5FuU`@EB3j}r(n-kLq%22p>bUgVaz?qKk9fOVu{EP-u}7yzJftMZiGg= zPDo7C9UVkE+XcDe_-clr*6u6RVmP3E0t<~wRJf#q-DHzwFhIG)Wx8ni@k30GP*DM|iyK_C#|&%$4$fe|X^3MP=RDL7}@U9SPeHP^N^^sb+1 zp9V2PcFt(@!BR_4!3Eksgk+W$yxv`LRVFeUHfV$v|Gz$m8G+0Y;KMtL7$C8sD&6A^ z8tt3^oyl$j9a`u{^a%e3wlpLpx}o~xJo6k3IAsLJ;0rFHy+=p7$G=cTy<>2ZLJ%Vw zh&s^MSO%6!AovQlBxTyI1!)bagEXAh#COP3Ga5GgI0E|EQKd9qYk8pG@EJMB5F#Ii z(?Zz7?-n5H1*R4AMOltZkSDu<`T+(YBfTzV(scN>_RL@AQ2z|k%$yh<9O^O%+V8H$p^x5B!&fqwM6W5HnQtZ%KgZtYJ;%-J0K`*@RNKb6 za)5XeBeyWXQX7bMpeB$(j!NVcJUvC$v^lklNjy;sn*rn15LkysA=j$g(w$pEBSLVkBB%Y88T_Bl_`FrHJ77>&`7rX90BsbvmY4IU3Ik@&d# z%V0^5Ss$(ec@&20WsU~UsdY+9r8`n&L4}b7D_!|ZNIF?#uzG?vZ&9QH2taFUa;U!) zpOopLPK<+Q2gz_+$(3+r(Is<7@|e>CBxI;{!w8eo0cxTh{@wKG1UN$!2ns5)0UiL` zS^ZJ)5peyp?GBBBF*FkE7F|35xS~-n6BFO}dnnw4UWgx2sQ|l$#kyW0O)N#s;Uh*| zBq}TXPIUZqvNQ-;&gm}{CS;h{G9Rz~#K^@VmI~y?PW@S+Bsvi^Q1QsarV|4NkOenG z+EwQX+zdIWNy2FjLjxNE0_x~>##mpRZP38KfcC8+Dk+IlBLT!>3HlPDT^PRuv#vR5 z;W~d@MG}Ja(g*~_Y`}dqie{ADK#J>}C)kdxy%WoW_3lEWpJ9`UK1P&|j*Pj2GCp zWO8?>j97(h8LiI1Fdak=rg+nF*6O7Q*-Lrtn}jy=mm??!+jXvgS}lbgqg!qHo(L5q zGnw$|r3yz`YrF|Ad6pj8!nvd{nc@)iIy2xJ3fg)d z;X;~y_gH9gr0i!OO-bO5xJUadI~D@^(*)GM85dI6=x`j^3T)idi0ST+0ZHy8e!Uew zAAn&6zXu95(GS12jO_}Eh>tLc_}5U3-GD4k6Y``J#UQCk{HX;)60)9Z53kunrzrXk z#FWflWssd;p@KC%(t9ig7xte~4F-jBIEQ>Q%xYxLyW(aav*v!r)YQuY6DY8U#_N@j z!q^OtWE{nwF}tm>Bko_+iRyxQ#u>ftBx#bmPU@1G*XHG4((<1qwqs3)v|2=Z93W^B>lK@N%1DWH4 zh-s>K6QbdX`{5=`X|U0dH8iO2L!8lTwZ5@G8LRCq07R^VY0X_96LH$gDf*#fC7 z*>*NZ#d$6hNI@Vnr~2GoDt(H}Td9 z#W+(W!}0*A3t{vR__%C4|h><<(a9k0mV89;2~y0GLbaWqfqb&Wdz+2 z3KG|Q9N3(hLI)18PI36QP$0m+oB}7zoK=gipwZ35Mh;wUPl5W9?igb(VyT3ff#^g0x^$1zxXFf!HQkK zS{puhkV&Ig{Nc*%cR(7`rnp9-8`s!kd}3fgASbXLHq zzATe?n}agP1VU6Md0b$;cBXcE9cL zVR4aVL`QsTXbZup5SGk+Wr>#~gv45ic1M~gy+@flV56X0T5vuO>3d#i*x44r;fBGWnXCgZ3w))l+TvRFz}E-@;kRK zoigNz#0I2Hp_bTx1F_l5jZz64O~lS1P(WMWYSqKy^>86z9$jj&NP;0v^krWlV2lDa zP)$LNhM)yw-Z@FZ&jhPn_K}kk7NtaQTMLI*fkKFk*aH0la&yH3TI*q9T~3T_;;Z1Y z+t*=2kKrg5fZVHPu=(nkezaBSUU)z>3|Fc`_?=El@VefO=oo!#-O*%@N=lG=0J@+x zqR5msA@8Z}2t#rRsTFu+X>W@II`HJr3KsRvHSa8Cte4vW%zrVOWb$(gIya=L&F$o8 zC!W)pomoa``&sOPNNy)jWAuZ?Rn%oh!j=Lkb>4hg*+KkM6IiJPh%is>)uF2#S2@}I zC)f9Fwm<%b41e=g!jkwC>*Hj*LPdKyL|oQ*K~DOA6erODf?pG%!i`9Ev{G_4KG-z55hx3fZ+5}ux zFll&T+^*}r;D#@5E_TJGY{}FywEI5_<gk-VGiT)19+e5*NrCbeBIB}VH$^_t0a~>~ zjTLN?6QB}6UB2u@JG%2%H!9(dsA_mf^+gn0)Jdgh;*=@P?aGNXsLTneKH&8AIwx8} zPiEIK;(Xd9%UyTw%bNqwQp9dR@lAY=E=_w>b_JZYYy?BicG)gTXLb^MH(wyr(xVwiY5GrR^@E#4%k`@6b9;KCHZZ z%L?u_GUh+{HCeE#LOvoSNMb+~aAnpUfvf!mZfG}eWeau!ARQ1TjWEb8dkAp39Vj~U zv@iG5SJew&N^U1T(A+vFra=^5vu2PrEM!F6TUH}CoL6JJZcM2#mC?`?XOy`@g)wL5 zKteUGP|MIw*v4}(AQ()W033j#<$fR)qHJ+JC5vlZwg>X zD_$6PGfZir)_HHmiaBCg4}{=Z6jOaWzLqhEi4eguCgSCnrqG0wgwkGg8&Y13uzZDN z#*>x?-GL|;`zd%;0YvDoArwX`WKaa#Rx8dVrbIP~RV6UPt-Cnt>|lp53j8Tr@fshj z@l7;VkOrIjJ`Gw^xsa&sS_)x;0c)Qi5k%+ds3yD$Bf#3c>MM?6fiA+19}qV*hiFgG zt0D4Fz=E)~Kg6+=(-{WUX(TkALind7oaCB#Yea=&TcAKDj@j5}@WE42@&fFrUg&=Y zymO9hZh!_3`Jm&_bFz{+Ym%+~jJE}KoP&fWh9{OYUVA&h0L%n|X^!?3kRZeNcv|ZN z?lr6BvY@e{w^7Zst)uFD>Kop?J#{8%t0xUE8)5DgL{V`|a-epGv(n-Pq*F|(>>0NK z>f%sQQiXmM7F7W&B(Rd8P8lYmaS23{uO+NYkda|K6kBPt}dP~TV`5-bc z2sk3(hh$&~q!HdAbcAFdkXRhNJgjhlc~JNf)FY_IE*O|*V9OD?15Jj2400KoH0WjV zp9Z28gk1q~1j!ICB)~&(kO2Y$H3-uWTpXk`NMvC7Ln4MJ40Ippe!-$cfQ2v#LKDm= z&`_YDK@);zg4PDO3WOC1Ens|rssL&N><9P?;5C3LK(zsD0=@?T2pj$Xj{m!S>;D7& z|L{IieNpqEupdodiF~W@|1tRQ@muAWsJ?#vX!z*%yTG4P{5E=f;iJZ7(0Ajn@T#4z4zC7QD2%3Ff)Ocg-i0?QXz&0ASR~&F~(D z4+FO)zwl+Ru{)gF&e(R9ye*gahqMOOdS_{`p&TZbN3} zO4>MqZ5rdExMe&rj;N5jxiq|QdR&K4@n$r5YVhF7^ggha6Y%&gcSaJzeSVDx4g+gLDYO6l@O(c_MRFWi2fFL0*d2lr) z8n#&-XQxbsNQp1-1>ZE|25lV(ItxN336wT|AOUA~<$G#-Lm;EUflWQ2PaKt!V0)2@ zjJ^F|+4&{1156y1XVhq>2He_=DqEeIy1hpzgCD+R&0^9)0J$9*>C2In3%|&ElmRjaUw6#F0}I9dQeSkV z^RzLX`Af@FJ2@Woj(}VlLHkjbhA`x+CcA>^#@fP__w;dyboTg56DwFGCb^;j5X8cR zLI{`Gb#h_5wKMp3fnJO4ppzx@>y2a(Io#{*0K_;QW;p`_@ys!fAt{OENE;VuFUsbC z40h0pe4(G)dKLkoLJvYaa^3p$CM(sf4-6kw&$s8>k>#d3MdQwty-GY+EW*B82yv!H z8Fn=-o&)#nl90Ts0VOSU&X&>=kMHhvbI0fY{(po}wG&vZJ1Jm_MJ znZg=Dkqpd@MdosKGVTZb?tb%;6?47t(q~qaF@Efi<-zN6t1FL;l|p`+*eXW$PP8xU zwWe{O_Xtuc+^SR3q|qm4G$l~R@qD`i7bMI(4}Xz8p=K+^y_=BS%Lg9Q6@x9R42G{_ z3ujo$F#cfmIf!D-V!92kt)M)q0D%-tAve2&X~N~C(5xJOS!o9sX5A#7=E-d828}6u zEb|K&T5zgCoJb4p$9EH%f$C+G{LUH~tv){r`^C=p-iX<)ZyiuM4Ejlj;Qv_AJ(c<1^(u_O? z!9h&{iHbJXecG1W(?@=BXRrQfFq_r>Ns)O5dSc{+eKeE=LOWeoQOS>{1I3Ae^qV~& zMVyz(&kg>Lss1J>_F3JQ!_(JMF8oZMFC>f!8((o%fP?>WM~N{K#TOxx2Vhi)P6SnG z)VYfB8mattOu)u&z%DmUTfB(}1hry-W*%Yg>w+FF)KGK#rMv?{gx4!L8ZvRY&?8aA z;?n6XbgqHq_MOB=vo=uJ@dBJizk1;t-NhFZbHOU^dIl=QTGU~9L~Nxz!`v4c?YE}^ z4+HBd(|2gGF>P2X@V2WdAP`hl5OzNW-tpn--;vOvJ>heyF11A#Oo;gW?0Uow;-T@b z87P-Fkc% z~9spB&5E0V2-wEC_4B>(&?nod9X8@&nMmf`& zo$*$@gQu^K+>qXKi|&%C5CBQn7X`%)XlLO0#_N}~Ut#AR2aZTmd*lP))3~cX>ZY-5 z)zaJ>3=Mgmg{PR(r*IL{;-cKyzQcsI%^R(R*z=GO28L`>2+IhR4ekE+4 zM+Gjxzqe4kWU~R-5>VMZT-3ZM(po&(PI(v(&1dv(86XaN;BvHm}^fU38+P=hf%-Z4PrXG}u{ z^{g=)0^+lVS>{0*NjXNV8&_q+Y)FC5rw3J)qxWAWsHWI1Q7czoL5fLjuNaLok>pJ0 zQivnSZfgD;R3V$T#E<_`Og=^fL87?6@mL~$cPHC8+zk`RkkHzqC2ee!6OOT25}?Au z8lo5|NxX-eBv?+_Jl(h9D~;e6g@3JwzU4b}rUS0FtbaUHZZ$m{NtvL!ESZJHISL z#$q3276qW>>e0K9BC6Lm!PDcC*mJ>96;}jV-`)zxB`?jOs*Xw=t0)s{mG?QRw~8qt zfu=rKWTTDPq=!y;1b*tE3H@nBXu_aSH~}ouMp}xlRsiQy|?8 z+=eFuOFpAznJa$ z9HP}Oq&hZZjUr$CB~(eAM!iJ*;=b?Yrx6h>^|H)MP==A9VPv1#j0hS{CaVQ1a0U*_ zOPt|Q3|tBH4>cTq2$K@~xI!3~L_nbiL8%UpJy?`vZOB>f8|q^o(U}ch?lcb}gFn9* z1|~O!l8`0`5O(Y2Oh~*GnI51ZmY26LDazLJ5qc&Ez{Mb8VGH2izKeuw*Z=?k00000 E0QL`y%>V!Z diff --git a/doc/_themes/sphinx_rtd_theme/static/fonts/fontawesome-webfont.svg b/doc/_themes/sphinx_rtd_theme/static/fonts/fontawesome-webfont.svg deleted file mode 100644 index 45fdf33..0000000 --- a/doc/_themes/sphinx_rtd_theme/static/fonts/fontawesome-webfont.svg +++ /dev/null @@ -1,414 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/doc/_themes/sphinx_rtd_theme/static/fonts/fontawesome-webfont.ttf b/doc/_themes/sphinx_rtd_theme/static/fonts/fontawesome-webfont.ttf deleted file mode 100644 index e89738de5eaf8fca33a2f2cdc5cb4929caa62b71..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 80652 zcmd4434B!5y$62Jx!dgfl1wJaOp=*N2qchXlCUL1*hxS(1pzUj2!bdoh~hR1qKGRh zwYF;1y3o}w_SLrdruJ!H7kRd|tG>S2R@?Wq7TP{rA#?eEf9K95lK|TG|33fEKg+%6 z+hTSaAdmL)uWh^R%I%Bq{=#vIHGE2vyyxxQ zu>PXwf4+35#HOMTl7@fkt@MNGkN*dqzrXxudarck;ms?=9TzfXbVcIGGxh+E^d!f> ztp1kWBdO@h9ZDcN>E)O$)*L%OUQ<(5(?2L3bseob+I4i% z(X~e}J$l2@yN*6`^z%o*bo9v4Umbn#sBz47tm;_Pv94o_j;%d*>9HG*-F57d|CLTs zlc>gL3N=cjYLt$8j>eB>jxIjhe{|c??9qFU4jg^^^s&K$J;*W3T~FTeWV|2+Pm&&ML33QxpS<_UX3 zo}ee-@q2t8ugBw&J>0`QlKZ6FaOd4a?i23g?ho95bN|)-zJuoA|NMsm7K+s}nqB%Y z{lQI|ivK_S=vvsKmRk#edAb%6i2hSQfN{*f8@=C#{(3MdvZPB=N8B5iy>ag#%Ndz% zd|;azJHAbmj*E8`hfQQA(J-EOQqrDKvr;880iAi{Eunx`8?Q;WwYSE-ESYZWVy*F( zDyBWrn7@r>BFSWAC`(6{$=}vkS07fh;rcptPAzWdrDR(Yf3n1{ZmbPgSS%G{s_+g8 z?`TBE8*uTOCf?S?TU)|jb#%6^y@R#4wuCfk)~1cCHg1}Q(}asx@ZVV6;lsib{$)h;3&X! zv#^nE>r1k8t{W+F*LfUs0DkxY35 zA&hmqcN%Y!F$Y>O5DtZ_l&QR>OYUgz=wcmSb8^yNnjQ>PHkL5{@qN#TZq2kl zV*Di$^E=g?)6Z1RVL6_0`tSSJtJ;*Bj-~)(fu@d{DcY;wYCkW#w&!@JXYJY^HP^E? zCQEfyNA@&MoHS`-XZ2cas^9s{_6MI-Cq)uIUm`L|ee%J^d;3q| zxwSnC)nU#t^(_m0Cn*@xCMAs)wp8(Omy8LeF_j-`^X2cc)%HzmHU_(Hx@>V>-Qvq` z>KZiO%HNyy@l}?(^Dn$><{N)&oS&(y%gk^5+Z+G+R{j~Y?$2TF2BjKgP>~{l@+5#xb#STNuZ8r?=WCN#*;G43z#WbeP}pXPs)z27Nc6N(s* z7!KVTtaQBluA?%jx!7OW`ifw}I-h-~p~09u-%4wQ;KqEnm7v$k5_U|!oKTDHICC?U z%UO%D>hNJ>6>FK#cCl;NcSO4y&fF{>U=3aD2IJ-~<7dX|?|etL6`R@eA+4k~0kR8WvKfSYMJobh>0d z!tvr{#Gs=xQsl%)QZ6lGj9fo`gtklOnC+PFB5q~+|H?r@3FXkQznBmY53W~ekX>W(B9tH3|SwvWJ~1XLheJ)N0I z(>o?V_Wu8Me(d|W)LC!j>N`8@S%!`yX`U_3UsHzz6Au-Z2`g~&4=#RcvTJE15t5HKCG3gq~ zrQNE0NeW>%!QQ27HO-7A+qxMxD=QAwOuIFjAAehPar8FhU^GezmgM(PUjEZ!aVvTo z+f4ar)c6Iz7iCcIr6=E0eaZm|+(=!(&9s`76^CY2-C-SFe<+|^nd%cY8^1JuY1YJ& zNEP13l7-rTiL2s0XS!=XLA99lj7d|~VsD&Yr5kF;8J`tNS3NtP z3km=mX{w2Vehi0vgtJWyPIUIJBgSuye>Z-6WY=Q{8ZWMnxyP;FvgG!|uO7aA$(Hrw z+_CD-;|@HQ&-QKV!ynInl1lD6!lIx2D(l%Ab2W~;IJV%Y*K9&@JhkbXpDu`9Jg(6d z+iJYP7vu#V=X4}m3WTqqe@p2FDIs8{2q`V01X>50LF_ODG-LDB`qKNS2O{^EnaD-4lj8PxQryhw9Ovnz(^f)Ef8uU z2*Uc*F(U!YNG;Z=rsJ1-f#sUgX(1$2M8Sf-$E7Al%LWLdqj6bc7WX_~h3j9O9*_O&uJZbsHf!YGkkdK3@Lg87({WRsC>(L4Fb~li4zjJka)fxa zJ<+n#5wRuivR)E)-_{cKI=|)#Zn4_0Xty~X_TcLBmPr*n=oDp}nkFxCIBd?kyKP%a z3)^)xWl9 z2=r7xK?qCFaWA6%eUW<(OS^n>tOSf)XGrI(tU^jX@g7V5_k36_LmfzD;9cZ2Bt60U(mW+|v56fMdYE1^I$# zYn;WCDXavVH)nd^#bB7oM%}kFw5ay^Kq2z{plQ z*kp&z*ff+Sx=PK|ch*OZe~qcIBxv>_<;k*S^aT##S!CCW3BP%kt1v!dz`J42aRDEB3Q^9 zD21}(34VTQ(IZF1Jhn)Zz6j{i3uu>ET5e**HtBLu3lZPM0<{ndq;MH6#$^pcf*PO; zMvz-W$VC(*%z=WTFr*hN%2>epb!UK;F`wfv4j+HNDW7rrSOAxeqqrVmK4(7D6k(59 z>H=&TuDEgKDHL&|2wN7Yv#`e^JgPA4Vt%KQQyd--xMIJPNp#^Pj`Q2Qlz>0#cjjo8 zb50~ryxS#YuAmFBly%H=0lx0*)XAQmQFc zVkB8gwmsEZe;gBw3IE}(Q$9K6HufsO;~U;;BjaoL8JTLYcN~)dnc$I_H0~)Ok20lF zEH*-E-`3fATPOE6R2mt-pXDkWQY&S}~TyokXyw@6buLX;*ub6eMzw9v-7(QKA+|L8-TdVjzepa!yjpUdH3-BzoS z^RN#-q^Xcm5ON2MJ89*!I0RmDT*l@V565YbFRc3xzln{*{*Zi$V6!2au+0Bx*H7*XCt+j>rd*JFSa16?@c(S!c!QKzj4ghXs#(BNfx8MKW zBJs8JwfVZoW#4CImaWG3K089H-N*b}ZU%&_l97od>r+*??<+P0u+n#%g zsAHWhdSusS8*aiP8m2FSuj{0_Xk|d>QoN=P1j~p30GtQ5SzQ}+72XTOe%Vit(OY{CQQmf*S4a-!rCL=&B z(CJbN?hlE3G6w2QX%r&SuPF&0CF^DV!xjJeG^zaQE{7S&Sbe7~`Fyx7${c(L58e zQHg&n=5!keg~5Y?YTC|+Ni!3LPbVIMqgMshgqEEacs{gm38lO<&kG^fB@*scroW@{W9O-ROG z?Ki$`92a<4V+*lVm4Oqq!r4Ns(=2x7h2|P0c!?=lQP+gi*9Iv8O(X`OOKxkDF*?Ne zobDYgd-fcgJCZD`sVSrXWW;TobD9?$z6W_|Am$cJq`G6!Mus~mfQn}2SD_BIBt{9=O676JNwgjI2{$qRA*qp zvSkYbovCER>AZt|+W4^(V4Bja^`^ROZ@>N8x+WyW%^&~$qtIa-G4fN@WF!@+bhkh8 zwI|x$m4OtXf9h9_Hsi+CxKkHaoJx6QHS@3*=2;ynM>brCBC90_4WiIPkRH+w+RqOe zN(FF1EwlrzVyy;i(|-KN@y|g0(=VMF60C3?yj!}~TkDMnThnx%epwbjau%!?u^sde zS&;zAY~an5J+Sao@ENtSReJH*(HOgzJIJ)h-SLtH00GoIooB1?3c{;3Nd zItcmYsr^Vn(q;B#D)b#vYpu7{|Nr8@8$Yqw+Un|u@z>RLLv?kx_zn@U-bhFpUq!UIUk>Ec_WYcV*tuLL-w-b>i$yiSh=vxZ!f`sbB z-=>;v02>IL2n8amC4Bu+tzcQvxVok)_R|ElFqg}#JPB|&a9k?c0rhlyvZITWpoS78Q5&7WEiJ5reQ7B^2Lk}GYoL%= zdn%+7>()ZDog}I(uyQ4NZDW1N_=Eq-8ABTu-W@FqX$*TJcLcTYc#EuZIVuOoDNI+C zI>q0tFbn6dkY@2Z{egH2Qe!9oV8P;$@m}5B^M*cAVYl1Lu9iPh*=}Lub)G!&2gTvy z{mybFh(vw>iA|?mQEDd78@ej9V#}hL)08Hcr9!g@Ds0IuNn5?eUZd4*tFbnz&RR9H zBWbC%S^^P^BN0!PhnOZ?w=EdDYUgaXr(#ZZM1DO~>#m~xQcw#9Q43}gLkhU~n2-ZN zSIk-+8nHbWxKEwL8t%nvp~o20mvgBjMit)x|{(&v217kK;Gm%Ge*DDkEd}3 zEcC!xm-842CmxLU*PoOw7i%S}X9dq3hdfu3$P5EU7$6d8bf|e|%Z9~Ok|{^`$n)Pj zbm+Z9@*t5+$Fp=CZ1rzQb1A*S-a;nkyjT2|&-h^`Q0)lX6-|y- zd2IoUi~3Kv3m6l4zz+$=258kmIHE^D78r%v8a=4{12SEsE6Br81A-H=yVLljW!mAz zZ!?>~I$A&okdQ`<6<~_!8j=WO#3+Sdi03dcjeVKjpH3tjrYu|h^nwZ|^TwVpeCh1v zpJ`hJI}?`wEuRox*yL5LTveEj*?p~5%N0oAuA89xRMrq!uySK#dh&$v<1*cm>%O>Z zO=Ym9XTkiNmu`P)`A_5S*wT4(F1w;K@(28nZKh;Nq5U>8jB7UBSrvR=yRd(vYP`*;+HPhnDTHj9A0I9 zUwx&cqSImVx$JtSCuC{Z7`6G?^i)mH{qZ@BE4tRvo=G?yR%Lu>da}{Mn7+e%c4ZViB0LPC|dWSDQ?y(zK%Ro0605Cgn)Hvx}3u07gM+AOX_w zkpve4C?F}UF31K#B34<&_qDw-vEY2y_hr!QjHD)jLV?bWz1 za6@1U{(bSqi%T==jTI_t<;-KTFcx_@ec_at-z_(uUAC~DyA{sWb*Tr9uNWV{uPIfo z+dPWJHbKSg*(@$4q(rQ7Ptp;r%^hQ(?YewTNKu(qVYg1aDDIC`cv-_aCwLp zzmL_AXI7`3hCXU58T#XYKJA3l> zv2a47oQfj}bB~LhhNHNbrF#mFIgz3RyXYg5{~xv6G>w$e7}0LgC>2Lx6(n*T$N%eg zkF|yPsQl>hE*4my+5|EWAjXcl7&dJ%nBi$iu?x{ z2ftGj%|0QHinvmm9w{RalF0@=9;Ji-BYRfTUkOT$Q~OxZF_@NeWa$HlDaDXu`|weD z)=wQ25=a-Cs2=)9yU343sRq+51u4TSMuiR~ojH9{&~~Dal923rLE_K^7Wz~a8B{Ww z&TvSVQjk&kjID=u<}*7F9oorrI}fq@d=(C7iiA<)ysDqw_f+xDp`A~%1AY}62U7+I zJ_z)c4!@QvsR`EvAJpCg_ASjYkl>ra5eYsTFHVL_xFce_d3M{twrvB-w&Pir8Q|b# zJ`f$%GU(}jrPh{;hYD`X!%RLWin5sBd4h^L6+99f}e!kWQ(MMn=A)U zAjLaUdayOf+CarI@Hn7s!Q!KRUdVeHI03TS2(c}z-&vjISA}eP{?|H=yh?9p14B8Z zUwtR>l+piGU3)tDP6DO2WaWVnm9mAX)c1`3p&T3FgXzRmY~aac@_!&z5qz1Tv31DS zMoCm$z(-h9LclJY#vtrq+_>M>s!2{I zYjl@PtYN67JwZBoGJlc58$jk$C5K^&5nz>}sIJr~dK83K0HP*H>|Qfg8m}$UE|H?nvgB=pa{W}siM-Fvh3iT%GguL@o^=lx>; z6V@Be^{V|1{nP+slcg?c9$ID2rj*27hB}ykG-wld0`d&8Fzg@i{<-` zL1oPvV{i>@@g9t_epJ)h&vV1|NQK~+4u zhQ-!IQ42X9(Y%r_0IOI3=q_E|S>6$+z zRy|qvcj=_bArOavE}&+MU6f8b{gH*8Hf>w6cfM%E;}8D9$coiJU>v@3=L9)yQ9L$V zX!5vPJy<(+(Pg(kw|M|4BjRUSKd&|N#eVvo6>6kLDfaTGew(w*W3jR~j4bfQxZLi2 z#5K?ckHqy#+;;WeUAdxtjswo~89U-m~%dGnMrGy#Pjk^B_V zmR$w8Wcg{@LX#uvigl>K^jWfHYOmA7YJe zI{s=n9uKP%!+c%7${C2Lxk$i?R2{*T*jEHkO?G!Cg*J>MOpPj0FU6f+*dItV&g76V z1b)pJ&Z!wP(E#rzjwNY&55X=l5!R#o)VENrBjrccGxDs4XEAo+;jV=ttEC~7{vmN(Hc`<9+{#fpHLj)Nd9eTcO~l4NgU1bOrQL!VpqQp zib+yUYF})TFh>{Clp6kaemgWrcOVVJ5D~Q z^rB8sKjecYq+-~LVDp})?U-e;_|57^a!dOlcUVjWQBca@2J(2{ZyU8X`l3 z!ZKqBCZ5TXguooG(a*5PF(lMTyU2d2(5_-@PHjVp@6l=BYJ$lrZz=76qtMm1H8T=; zL)Zn0K6KS|1i=Ogr#OaMVYNs06d3hV8d164|J-wa|0;h)gc6YoBu~A$=ZzS1s)}zl0NU8}YaCa@jC(V+kyrbM#+k?(iPn;jyOUHEk1n>nCMH%%UO0z z>j#QY`}pTq9$fm9GT()oV^&#NTRhnmitd5??kC*r}T6#G;# zT{4>ua-y&#TH0ZnA=XK;L!+!AC74DR4QTuOh2bC?SJFX#O5+DyJ}yy7B#fLm`Q*Eh zF_YgK+uo5i(hMI&X~g#gMiv-qQ}zODLySC{h&;4W71rlt+aHv#vZ#wET>Bzi;ca&u1rSmPQ3G&xc}HYiM#26F&DUrAx`u3aCK}v z5XBiDFVsi4Yh=C%cTL3z2uCAvAX#O!28fAe3N0efEC^aMGBB5Io|*; znm#!N-*Pp!BJbKaaM^bcoHJC;|9tC{V5ij>OsjqaADrKikrhxvC#!sg?|y7=-hJ+h z1KA#I_y(psW-K8JT^i~i=~ohErf-5MqY3uB9yQZHd2 zvjZa~Xp3ZD8@!%alE$wWbO-JULWg8MMCtqzV+|Kq%teyO5p!I#pgnWsn^55C(m=2- zc&&s31%G#_6ye;};fuGT2`1lW5MwsD{u3X+e0^7~s(RfXhwgC8H>Mxw-yH;Z#wB>& z`%#L>5l40V**gX{bj;Fft?q!=8o^Fk`P6szvipbKFk7%?rwBtNM2*2;N z&8GHYeSp@@0(J;^#d;j(7lv2JFaTl1RM?0Z{hjqWI5G4KuZ97UVXzgE$y@i7tD=12 zT^#R{O_6XaY>I zy0Q0#)#3Ig+TkVzzd}|0UQ?E8H^PXK&+) zOL6<-#w)_ZyY=IEnDis^28kc{4fX92q8$_?LW8qXYst__)tzbG_lR*${^0d6!=uONX5J;|nf-!1;nR z;Aa={tq#p%(H!~vY;JI`5@f>Qp(NlYC%k*B$?74I_QJLiviuMzi+0vZL^FH<;r2qr zb8Cy~r-q?6ndySL5uA8v{a|qk(va@Lkaobx)kSmBI-~R3H$)mSllep!x+h^|kYM?>=wK^lWze7D}H+0pF!brYsPI zmJ3$apq9uww+rYAb{>=fIg39EKmqTa$Y+f=ezOaUzARX=Hn5NBUybl&pvidW^`8#j zf4loY*wftDRarGI;N=!s?pn|l<<=D+dtqzGSHAqE2U50Fpe9w8>W+D2*iv0^=+?;y6u&ad)|$TZN008T^SNbfDq%}` z!`3x>whKNF>jv^OH>^@6@(ZNtFn2F#qXGiyrouwdsRDzCQ&kG-ltwgcC#6Ye_4l7O zX{N$f-LY>~hnee<&D?;{A<#kbFWPh7vU&4XxAtclYgoShrq8Y~URir{;R+2o=rOw`ynAzQsbu|GY)=^OFN;>mcZ!a(H*m zl+Fg^cfe||twYm&W80aacA6VEAOpqB7ROtJ7c0s7{osYbwWA#Qx&XvrY1RQkn>Q|6 zu^xSSn(rIw1-q49Y^>Ql$>wwH@{GUx*vdfQzRXUduRN7Uv*#g zJIv!<=W)Q7hue&a``>C|?@!n>rzW%HvoGxNz4y&8U%4&wC9oPacOKx=qXM4d1X0-a zKLRJoFe@FlDg}-OMVWU@qh6w3BEioP=-Z6|I)(Xwx=JWE z8X376kOPuHLlCBjbXbK#M(rP;>3eKI^=5U4BD*!?zm0rab@p3b+-*HPWarF=w8md# zvZ1(OFP3$A_{RtOa%z8DuJ5t@Jin`7W3rPC8Tl8zu6`@G4;|J$PRBYcOT#KDY=IYY z)~P-^(3c^pAjN6ISe|NoO%~*2b$ym}CFFl`({em9<_syfuqYSThlMu3e8!`ERRiZnEi zMP$Jc5#>1f%D2H?2YMl9o^VB!WU&lY2fq~-8LZDFXYwY7KrAnja($5jo!gQVAv zZSGvv*4NV0Hl<=}p$K_k7u^e~$VqA9qG{vGVoj9|GpDaO@9J4*9b+yQpHiyVJU5|Z zUPGl2lMK0_{?0-DonuVaUE!Lh>8bO+BJN{DguAA^vsj>NT6a^|)}B>YFFvO=E*>6r z#Vn3-!@43p4A3EwrXWbbnrJF;STdDPwkK&1R68gfLl?uQsp!&C3!KaK52%x zLXlNwgU_NqG1yR6Wqc3<> zX3R4ldkN$@#175VmNt!RS~{)S%u>K3auYXm6bxx3$8*{58ZSKe9P9b6C;_NVh7=`4 zj1ZpS7mXAxeT)VU;<$pz<`P{_!7K{Odzd(O@dmU)eAILyQ)mUZN;_K`=7elaJYN3f@5 z0o&xm4S7;s!3skuoXKlZSF7N+rh`~5z!4z5Lq^vHGgzgBaffH2xbNL8e_x!wA1goc zF4NUA`9XrCAt{m!CHNPAAb?8pl)LSU&Xg}kl4;>vBA)4$bB0uwkay{oWj4=5GN+HY zT4yP82a---bts`HX)S^l&tfe=*Dw~&q57mqd3)BJ$gJ73XAQ%V53JcE59CE&&e7Ev zOi7D#x&rn1rEw!o^AX@&xu@3x|%IUO3Bou zjYC7ZwMV8KUr<@$#WB2mUUjXpy>)J+s=Ailfis&jaQ-}FyQX-RlE#p1N8&l`h0w^s z3I;#~@E~+6q+!6!1ZE`S0hI9^1dUi~rRrPC7Sy%MFWV?!S&23m>sRP;@c@1>ek`L) za?X4gy@N11KzEb|8DMM59fZF4v=xqMgG*iy(!bC+ybB$I|0c~HOntCJ_XS1*?35_xct%NR#)2>jcL0W$O{82u=(lp6e? zog*^kiBbmb({!kWb>iqClK~k^rzE7yuv-UW0liA65afU0gi`Hefe?YFX3Q#|F?;%& z71yda{rarR)y?S(=U0ZDk>HkD+wYB(-T(P*|8~cQN#ME1!JIDRZfYw5gVIxFYBJ6sl}dnsEbubsQ|6Ni@jtP>a?dFs%p_WOl2qN7$|owN|! z*9Kd~SdZQT)Qa%S)t#4q;lVw-cQcLMU)m79`Sq=nQm@~0=kC|@xA1G(`=xKw#hgl* zQ;M5Zf%m1LH|Rnuh=VNQTG|Wv1D4Zq$&-v}o=}X^avb2Mmxclm0wsCC=jvJOi~2h2 zU4MeN@WI!H4pJ;rC0mG7IP@m@0cJI6=-)E=>$Gfd`nUw+AIL=0z5Gj2-`XCcGwM4n zB6Q8ri&H}FSVPY}CB5Ejv zaXMM@)1;GB5-8n=Z5~%(3RHAety1I+Ow9ZZ;}(;t8J*>CulHJ0HH~ur8_`AM>ZAE} z&mMl_l^0mcz!R_RW*79!O*OIgUZ+i4y!_nB^0P2eTRg78kB7zCki6?-HBIzz{kTO@ z{^;&ko)};)FTC=^;b)D9`{hOid-1NfX$zOG>Ou3xT61Hq9R(iuVqR{P4ofEr{i4`J zX8+JLki&&(BB>SFgMxPoupc%l5H({176Bmw+e1|JcZVy&$P|MW;T@=v#)?KR1tdf7 z5iyX!d4OI4)kqsC#jXs6fpg$82Xh>hhanckEC2k%a#lc*d=TNRu)UZ^BkQt$!XB*Y z)b;RAzuk6aqTcS%!(X@iSh%L)D&1+f-J{#OJYmO!HrH^`(A8A5rm?iB#X&_K)7)V@ zit_9O4qvOXi(C3!fk433XW_e)R-fa62b|tkMd|7++-Pmkl&h6iuk(R_w0t2X(@8Z|;YOPb5vwvXF_=jxVQDy%lwqR{wc8S~nQ zi`uOYOVw5SDxd3;rcp&beW8gpVeZWj-r;dqlwV%1$aB{QIS;O#D=WxWxIMU08KxWX zXFm_O<~Hy-bT3@#mXH23PZ9hI94u(;gpfyhC>TbHz>(l4i5RCOXd=-A#qPzz)IoMs zX#{D)i$kl8(Tc4DtYYm_xT9|x-}u*aR$cc{U5jk@b1(y3m0<``=cx?ZuDk1-Y&N@r z&F0hYy3Q7?^whyIg8VK~EZ}IVd+54V=NQMnJEiI|R=@rFz2Tb<%KMG~d3T>@WxW*~ zE$kUJMVGO8CWDFkvUxw+x&PgL`||s){^7i``b03PG2B!%O_yCBrd#V*diE%*majRw zcVX|`pAOUW*dBHGD{dW$nuAqZ8*c;hN!AW?SRe(^QxY?xUtO@Nq}xbzV2RK&p??j5 zg)vAYBtAJAfh_^uOD<@n426vX=&3g4sYNZuK!2t`QkG~4btuX5@pTO;#658)Dx1R- z)gSM^CZ|@_`qBY+tT8*ungo^m**ojb>;J~J+e5}6AzbFG+c0HPSvc94YF)l}&ctUo zJ@^z=o#ffpg;Tyib^Y4NRkt*TXQ?f*bZwn4pVf4?#mnbE9jWrnUl41VT|V8**3_N5 zAYQj{W-zp2;r_=aG}iZ~c{bf!w!1f7e$Ae7i5a)=IPZc70T)D{0=WTC>ySVp{=h!qkX`Q5q$w(Sf?HcBtUOu}ewqU-eDsuMH z`P^%9>smhRtE)}NTGUzL##^q6tX)6#`%@OSY<%#7^RAjTdqyI@e%U#}mW8|FM@ger zKYsip`_zRSLcy5}>*5QD#yj~rIinJv4{Ga_;K_1kY_Mc?@c2uo21hPkmlW@LGHOF` z2EqNqc^3&8lo8k~z@ng4Nsvk~SBM3zWgBPqui13h z!x;FPdMQJ^S_oq6k(tH>n->Zuuv2)IETkU9EDskmwQfAind(MFEHdGw=vaj;NmW=3 zD9EeX6nVg(A0(5?j9_hYq>796E3sh2X_~{s#+)*1d-4$Vz>U$)TVRehNQ$wT$zZb> z$oKqU!6sh7x(w$GARxE3WmM!9;#~glyWhRf z=4_uocQTtgkI(+IP>PqVuodSu6j zp8OqbPtsRA>0y3lDeXr%T2hFfx0Ag-^rJ*dz)XrFmqEaQC{I{~DVfF*aNsTQhr~2` zfq@1=-QkaeS2dQka<79`sC~vIk>tY{&|W6ON48z?Fdtx$yugekgQM|zFte2oZv}fR z8M*c)E}8Ku4e2FJHrhid6nHd6F&f4a;$;7UsUJ3WF4~t;IgmQ0+@VCLIbz++MFVKU zOv`OE7F-r{`)q!@soUgtJc}tLqe$LwLWm4XUKA`^F_X&0CoeTnMm#4}ob(*2I7Qnr z*AQ?@8FWLepi^MbI^3r=h?y|8?dSyX{5XV-2Wk_SLdxktkX?CbCpqH_m}R0TkQACQ zTe!CK5V3Hl14Y(K?i|CA%X22=T1>DOI5{hLa19!<`51X1SuCtXIv&umGX)X(9~(E> zMPN%7b~v;Ig>*`wWFX(Bg0PAJ1rRGZYxcbbC#A#6w@*q7?mV1bcIPXXk4q;jr_b!& z;d2dPN_OYwze-=J)5S%m6^SIL3``Mnud1utnK&A&DMAJ3+X7-q!c3xG7xi*aY4gZg|#;U zlD0d6KQu&xfPH)lCh# zMKzmM$Nw(Hja|bt4Ik<7PT?^HU+Q@I(9S`RH)Ly@yn5Y?hO-hAqMK96^IksBlfI&I zeB!Kz%(~T+>#f0wJu|}osewSyqd9av)M&FgyXMWLU>u>)ps-vA^81?AVYlEv?a;M| zsy9O`tgEuxpxf*a>e_cWG&uRH9+>CbxooqP$z1*-p$%>cdjGg?f>zdk*6y>fIeYcx z*7~xtNW>nSV7+`bF5JAhy-ceE)!Nt)t5;;J%cZKe&Tu%{?1X!A@@6>{mf=i+7J$hW zemQ`-92UIWT<^sggT?b`xj_}laN0Xajsq+(EC7vz`6yV%LtjaB3nSX4G}_>2f)`9@ z()0_0>@yt+tR8S^w1lvy;s{*t>p<*Z z!AhBB#e+b$MC%EavRM|72^a$ze51?muvu(2#p+)anD+arjT>in?wiqnTowzoCL#VuNe)gP2552f++V7_L`vOZA*tmjV1RfuM zdHnv0s_2ABcy%b@W7dh`vQYb^`TzaLo9YJ|!YjsChN|l({EP+mKWTj9M928b%FE`L ztqj*c)^OQRj(l~-)ai>R+BPf?uL|3|URy}3f0)Ju^h&{&0-9*xDD)l!VNz*Od!~r2 zAc7WKok`b`G?K;#ga)KBRru}%@sE_`lbE?Kb|$QR<5%9 z^w!Rn@)Z>>-B)W*#@uqHYx2y=Ha*Dt{%s$xaaCA-oh{P>uF7#r`Q$nNIhxGsD^`@Z zbhhd~dzD-}@hs-eE?jS2T%BpHShIFR&>nzSm4D9Ua%EhlD=@94(`T)4)$o1)*2jXn z4RyOJWp^xTuk}H0V&Z&ZGh*7_kKUV3ad1=mNBm6I{;KGCL)(lh755nOD;g+z9nnG| z_%dUzXhIeQQCmlt`9C!H3Pfb=>2uFzPdm;Sg+)4%WCzba+t{qG`tW!x0=@+RG)q;Tx{ps|lRu?R^fi>%c_!Z%1ou-)@~{~s`kaj@M*sd*~ zc|Pm=#7~VMebzYkW^Ln}&tCjgbv)WQZrgpc7WFI|e+^sxvgPpJJNmcwCoVou*|dJP zD|)k$fA3$m-mBcsuV1Iy!(ZH?B<1mUEnC_9z?W^wy1j=l3QoSV+h(qdpO0e5|xWW4_Sit>MUpNdrc-gvzbj`s-9o-i(3 zh-e@`{^xg{i)3G!x{%#_;)kXw5uql5p9H;=K*rqNX>$hkD*_yn^TY^`A^bA6Y!YTt zNr<3?1&;Yq0#LRh_Kut@`VCMFpIm2sN%X_#DKrn>31BM7&fU;zk(9L&?>4`XqHj#mxYMseX72QVfMY+CvMj4YY(63d$K}C6r~iZm zr{R7CjPhschv>WlUZ!s;A-eCdhc2igB2X}mSkFR=Hx+grh&itg-{Df-$UO(F4}8pY z*yY=}-&c8Sc^wZK-*~GWR#XvnfYn`o#jV`Q1HS0pkpy#m35K%Q|E#<=;ETwRPyg4~ zzwuM%5njB;OVL0uUj7!F9pZK6w^sVR&Regz+<4>hia?;Y{AX-8tNfCaCCcvxv*G;d zH@+-1e=*DZ{cgxJw56C<1GTW?}m&l3+@XpkAMc^tne=-T)-_ZhV9Pd^bBb)df zd&OYjRSl!{xwbx9WPNRqv0pIl$rl4YKM`tvU*N?jjpK&U@4~YYG?}4ZFL)WawS!ov zV>8iVphW0QVb$qK7WU?`1EOkT4#=3#JceO3Nz4L0jpx<=+pBDj`fsKk)s+ojpJ;1v z=+%K+Z;g&?uuc4WLuIui{mpuZt?KqMr5Y-4y|uDobQzu<^B51&WA=uT%Ev`VSKVN9 zRPWzkWw(tgBjzP5U`U62VbfUIqcH3v7Z&r^l%|31DwRDJG^e6Fgl>fE_-b#>Oyn_D$|ZY(zMg_o8bE=U|%FQD#Y7avmMLh5+S z;ZIF1h#X_KFf0mPWqd}hv%aReJ9+&RA$C=%;4v^cy{vKO^!?+5nI%igC+D-7OsT-J zFMaWYU6V~|%WGV}4&KXqkI1Ml7FeS%h$my{05mS+`>O%P+7^CfCxNHU_7D z>V+HcdX};2a$Grd@y8zA#I6cGaecD8xu)J(JA;?GDuQKU8;hlTvpieYGA=I58eftL zfx?a_!_#LrE=x}iEQCGouqd)DcJ|Ut#^h}%US_&?>g-S4q4r%A3Qq2N@ZyaRPMfuB zZ*8V)X|Q8~j6wAJtuTxz$ZCaLTfml590>}Y04bIZ=0?*A(Gs4;sEVNs{lz}7)I zUKmgCNKn-Y{fN*@f*3&#Fx4f~+S7`5KNv>hhBBGFn0Bjrx=C-EY>J<0&LQFw9C2Z; z+h@>Rw=cNn)-iJ}#LiP^^9&$yUIB0|${E16mgMKkI(fPn+WagNRIBt42h{>#W7x#L zXUb=)1rF(eH4fq_Bn~G()R$7UO+pjUDyUV_C}0S(R&R}qCWhdj z*iq{Fr>dfEvoVHE$dBJIG?i^$&75PKwgE-a`a)wOBMn7qV~nHR2p?8xR|=aI+9euB zgEj2kDn80Es$I&dJs*Amb+9Bwc25bkTT6!G6 zI{i~=sIyQluMMH@j&=yJLWm?QN@(Gv3(PW0)lik~NTC`Mc2MjgRUPKNFc{hpe2KMGTN4M0Mq{Zl7$q%OlR~e$WNHmHn(mOr zq`1mLAp1Z?gwU>zwq!@BL%bYVkJ{Mzrw-0@KS02|i9RWBIV8)@#wQkj^SZ#jQC0iX7Hsm&?_{R*=3X9F*Rozj&&d*i5&ee#Df(Wo$?NepMIka+wHwLXAQe{NflsU6% z+zxRIBNcg#jyPUWzB?3zI>jf3WSQxWnp;;nj0ekA89h^N+-}hkc@jTv9e!mluM)%; zbs2`+3Td=zg=AW-mUV>h3~{e4`e~y7{DULJWhZV z$Ix5LWYw+$yj2?_apDWI9Lg3Aky~NUU`60ftD;%`vgT5CuhW7!nL&*!G)8L3U9MWJ zPN!96_~?`tripbs6t`N2v9ytsgAXsTVuZqgyK?5XxR?W>H&xw=DACNOFwCnGP}Fk8 zDl>)a77Qqc+Z{m@tjwjW9;+g2nnROa7|F$VAi$DUmD3=fPeSJa>)<86A-6XIG$z-Fn_bf<X~j}>pSeswiai#x7;04^a=|o zHdzXu3~D!k_twGB!iup-<%>wx!n(HuDjeATlAIHvY9Un}`;FJJc|{`9 z-^eP`5K?4)M{evN9gQ)Ivh+8UDT=wU1GBf!lmQtmso=k_g?xr&l!&KZ3_Az9*8E0P zi+U}-`{WnV=3tR(`03+Msx(gd1-|R#&qqX{Imr*3ZT1Iz{{}+=eG!d^m^rdjB)d}@ zhv6|Gg(Yc-5b`RBcykb*k*rxTX9aa6^#76}DUg)W_p?cD%^=e2hYDQ!00MXh&pi5I z3G44!t4i6tWW-GI$p8@?0~mrqGDd}bo&*j9YpI__JtHg*t=Pz5=w`NuBnsrA174Bj zAoLZJYFr@J5w>!s6rAJ=Rv~d9ei09fyQ*wF%r3YGod%I3J`{A1@v!mmJv2b1fr9qw z9(DmP_#+NSJ-UFHS>9?~!b9Q7|;*yG03lx9S&g z2w#aT#@!2P_+)8@v`ku!t_wS^w1>1bU}!)Hfrk-&9rN|-g4Jm8E7m9lmnE|A5eBz- zmKRF!C6901yL8)iTJP0UXZEPd=+9l-dKT}!ZSUe9Tj6upLuQ;j`J93^sT|+7bnnK; zm#956r(WHwU1u5#azNpdMQq);#&Du?f8KS5Ph+bs!p797E_@+7|LCG6*Qz`AS0=)Z zCdBjmI$D>Co8tS9>Me{SF zN22wq%KM_xS1TIEmXdEg`@UsYU$gAUvXv{(*>&~uSC@~;;}eIdJtkK>BIWM-PTg-u z8g{M!Q4u*1<-bQFT5%wnLZOQ4(S`DF9$j`|+1dZG?CNXJS-BE5kIvG%z*@}$cU54F z1YAHpAOwLxqYCxS6bI_rHy=Hb1G>CxJ4eL7M;Mzrr+@RohMS&Y*+<`mW8IA#nxI7`cA~EsZ zB0@lmq&3oJ>1t`ObO&yc#1>XDDv%tR-ePrQje|G`4N4jDr3v(wtYAU4(j_8a+ex)6 zsBQWJXkpTUEL70BNfOp!r)h1GK}%E41v~=NWkfweB~&y1@Dzf0!i*WUAl*T4m7fy) zIJ<bgFWYnPZRf1A>+6^9Ik0S&)wyez(>iO}fjvvt>uN*e z+57I@vuwSNl9o&Pmt0jd^0O{|Znre2adYkAvU3nxxuN)Ov@(KDXfy1?z@_Owo|qeFgb>z;9S;=l){ z*y{q8=7{V8S;YQ3#xogX$>sePsI@&x#K>jXgSX4rG_VN)f6=~Cji?X_Sb^Y+5+p(& z**FA(#%DgDj~0lyy%jMx5F64@n+QR#*h_{pn!x|00m={3mmnB@3WB`;XHCl*KVgm7 zVsZR8HqFSA$3K_q<)52L1s6=$eikcya{>>e4&!U}KQVs7KV$sF_!PdKH$ZOQ_!5p( z-#_#>C2QsYZA?;5?oqE(uOod2c`X6lOu?h+tR(WL2##0X*y-ktwOq^2@i&K`mRHNMSxQTG)~ zS5D`%FZ|e!M=q2tSAO!*UtOMm+~)91xAF5A9^8C!-_T#XmuHrC^Vwy|%2C;m4gEiK{lgY8LcUti zW04jM6b(hIrcKn;^qA49KP*2w?p`q@oth;ycU&APof9cKu(wZ_q{VSE2U;^DnfkO8 z^gEzvik@S>!VV3&_^8$uHEv_CkBx|2&=Zm$#kK+UXsKrHxT!)MeX+E_t3pS}?h&W_ z01V*Fxs-o1_6i$`bd702pWL+W)xW~}Yns#ttbK`e9ngVTHA48BZqrkcKBOTT5g)LE zddeS+3!y6sBx`UNLVvzaYCzjYcn4rdyRuUK-&WPDEpeB(v#Dz{oYp|NY~{7mn{3C&AtI6|43)`Tu!rgp-*)z4*b^gHU3 zi?5yLs{l{=KY(m8KR9{7|DU06X@Cnq#sM0b@sRo831Zd6+f((G}2m25mpZIv36j}4j( z;C=Nq(4g@E8s1cNzlZRAGc8BzL@rXqqENp@K`qic>gu|&5uIobG}rDcTrg*AenUPJ zniI{)VZ~5_UGPkp^bfra@_w(r&L)I^kP0?6IokinDX1=M@ z)?IMu{%zZvTRb*fKcvzFhupsB+hh9Y2r0a}cxS?e<~qsHpj78{-N{vTg3y<&XhxL~NFa@zFmU3ak= z$8(BK?8)>E+}_FeMa6wK6k17W0?SmC_w#zy5m3%ib+?Z?AKfvaV(w zp81BXm$8}InMH{X2Tt9Q#)WV~9tcB^Q9}r~F;>KVq)G502hIW(@e-wgk>D(Q>Dw%_ z4rpg3juR(fH+a$EP-|#^;^pPb^Yih?c0T`nb2I+L->0vnzL`D{zssL}tB#(g=riiT;) zg!eRU!GI}(9~hZd_ybdHN?I);B)R*${0d8c)2#ooUah#pv*|jgC1i?;C2XscFoAw0Y5=wuX+8! zTOPc6UCUI9E`nIW)&)5$?9!`pCL8-~ZqW&zJE`zHv2j;_dU*3oyBm9UUD?t5&7di$ z9SgmF%Q?6F=H9&zeY~(Gylrtob^GS|Q>x_diR+fIoqyr}UfFd6V#W~PpQ)V#l_OV1 zrE+u?HiR#!92sSaF_i|0kxP}%_v*{sYnqS!dE%u{ukAgy>zvYAGt6$upw`%{e{uiK z_wQfZOqKJ*t6Jv!miz3_&|^F<0i56^iwYl$HL%zp=iRkq%DA3OuV`O&XHadhl-a$` z)w|VpmA%|qWY00^<==gH%j$=MQTN{#o>#LpG1j~K-1fDtLGcZQDU`*^I%af~ zRkV+F*a2@ zlYQqRbxTeMJGyd5?cCnp%ANyrc3+vF3T}UJ%DnbXQzle5cvfJL|~-hkLbp`M02S`iMdZr((3Y9evH-jHK2a+cexH1<$k@5Xs`leX+m zG_C8dzc|#guKnCq-m!_LHRmnd%Z}~eKWSz~dwWGFo=C()*WN1sSJRG5yPG4y{zv;s7K452_o-6#ymjR42ds~zQd zO>VwvMv0kpt|c>eAKpEqMA-=?YY(4H5>1klhd+e+88j^F*J8_(J*@xgu82z>c>mgi zJ7><^c~IHOCCE382V}k#6DO1O2<0{c@dE8)2}va;5xD{%KqYQX!La}`lbnF%ADgHj ziJioA_^}h-`?W;&__G)&BH_T{SuWh9Q5gs%We{KBH)F%N9|@h|b;`2|RZ>Vw{JSLg zku1(1266@hi||q9LsBC9Jv@Oj%8X|d%Ckd}LL8w%NboYlX#-DFI8UbVKzU54@E_;D zhhlYryANDzXem4qY@z)g-4lKA|3u1#3jm$a12@oYUO-Bo>;rm_)N?ZF90{R7ylX!& z%&A?V!5i7CkOoO49cm|D-r-`7YPR2IwZs|PkbeiC`^vs!*)O7YKpTqaJ6^`G=sWbg z(w>>Vf;Usag$L2NAdyk>e?;``4su8rH1jPEdaM?-ny33@rEVxLxrsu&Yhv|AHPg& z9DJYHG0|TY{nv_;%Brf$l1qOdV+&>-tdUP9w3T^94o6X5r8e=AujIzInZ4b-&mV`s z>v|kn!9StI2m_!bf}9+|C66>zplpx|-1d;e2Dce^nAQOgJ6C?1En}3b&Xm=6RnxwxbjUsJ z2bM)xiPIW1M52SAL6mWNSXXFpUn^o4xZVuCizi=&29j$k6^K|rDwVoTENq9-OW^`q`_Mk ziAUB05TC4ur3~M)z+{5=*$h#<+vw5jNd;MK##fC2d>^)0$t~bB_}1ySqEu(Nb@wS% zDe4j<4i|g{pBtnLqKvj=^?@^BhQZD3nX|3}JO*M!$rlD|Vl-nx&D@dk7GyR)24Ycr zt%HL7$#a|o1Tmws`}}-Opt?ePesj0Y)ph#;m#s`#&VNZM;6pz7adJ}>Vb zrg@rPa^0u$Q#7uLE}#KG7d*87!CQ#rbArv+Vr-M_UQ}m`5<)u04FQIM9T`wLpyHiR6ePH9uQ>%NH z%x+sB)#$GI8*}{aC&S=kZu=Rq#U5p`haXO_54;X8(6*J?wHT^HZIpW9OAr~@mt!%2 z?-v&%aq-5_CtLEI=&@j*C zEHGGlpLpeo53c^(SHL!${Nk$-8!o;0b@SXo)qOB5y&dB4_GD;iiR`>|T3&1A5NQAqrVQ@)sSb{in6v}%w; z7jq-#7E3Tdc9XZhb}Q_4Ggr>c1@9?d204?MTNm>RtwKC`&C^x{^@`qys=ymmJ?G-b`H=HsMU4Q76d3-LJjVW zIxTdX;t7_f^hki`aCW~UYB!&WDv{fN;CX;xo>YSL-vV^A7`~;j7@@Z_hA7}gqo3SX zS_{CKqI>#Skl#<6)CIVIehPgI*9FCdL1rhj73)C{h=jsd^1L-RAT2CK-*M#yaTOfm z7|o9*o#M+}+;Zuyf$tu9PhuGrhLKB1CBWmLsoP0v;(zeg!y$zlA)|AGA*CUhFc7?S4q%t`D!ldH>{nx)E|oN{wpg{!N(%T>{4F3-uSl$x8$S1-Qd zneRVy!(tJQ;51iM<88s|wUc+wDleb4bMpDKjAh2#Zn)t#>}H*R$EK?3TdH&GB7s1p zHqYy;s4lCmEvv5ZdGl)NT3v4Smg!ZS?pX2grt#x9JH+b;BuyGJuxc)&V^oP%f#DKti~TMtPKgC4pFD#B*e+D0d zmYLq<_W3<;*XNsIpMUfq?DNxG3&=h{s*GqlCCwrrZ-#u7A#G!PfiXN=8R;`8C;4U+A(-|$01{+vA5IHI1%=+ zN#k<%v5EU~)*cQb=qU)*9p6uAf}YQy>x3=CDEFsbTmS?JGPP^Rfde}_cOTxe#9G_= zvTJ1v@X5MbR=QqpE$HnnXiXemyEw0eW_d~8VnX2ZR{Y|=k^ z_gx^Wp)H8-Nv7KZy3Gv#29O=C-30*a7T9LF+N;{jO=9S|LL_qSR6kl;(qkM235Qb{pzL8ZmeAT*`^r`AXlt}529YAF z+Ld9%`5ev-@VGz>B;pL{SZRIgn4#VwAks^a!|@{42vGxvcA#B|L*5FHCR~1;J)KgV*D`=XsnQpsTdad4%C3J0>d`> z_^5LzOVcZRh_bly94Bdsmyao0#U;?(RDw(|86=v_@nBL?kAO70kMp8vgmqkN&rAl+W~;;gX%WkpM{t z6oxFz4Vtu(UovN&QTz^AeF@tnnmanF#=BSQkLTEFh-I|W)NgR;SNlpclrJ6YvX4#}ro z8JjEt>IgbYUf%ypWArOV)ZmR$GDsvicrwYymDsPikM;C$2D+cN{J4C0`Vig~sy0CD zPa=&Gq1c(5VYeEJOF$on$;VWiVb7er`_g@g-c%evnlMf>y$L3pFTDz{!M6&xhQ(H~ zL#LhW(pcZ}%dkURbU#MKj|wc+w6!mT`{wQf1GHWZ9U=nU-=DEfCy5OBoi92Q{yxPj z!ylbSCTT(YW0N6ulHJS5ogqcwV z&qu;1`#M$sT3jBNhR#q$*h`4}OLERe>Oa}vH_ZJ7agmWH#Tjbz@s~1%;Jz6CRNADJ zP4aed&_&*k}kB9L;+<$O24wD4k!dQ)04Ok9slF9GNeFF*k zcN3`jd-@WIzW$zIFxlUq3AZ)2nZP260oKFR2pdWS@jv7$i$2Ku27>)ToiFLr zVL!n7g18D^H`s_QCE(!_XQmYc+LH;6!ad}E?8W~W<%dZ;YgV}w z70pnQU>H}Te$!+Ug;OTh=yJ*ZO4;Ze_?A*Ce12rfgapc>lxp+?LgUDS3E-h;i2syo zfQ>(fBvefQAu}V-4X9_*nJx-j4Ap=&lq(Qh_XZBC4F-8TyP6$1VgutLrd|1(oA#XiXWc#waFCwugwTx5zJby1j0Wl}zOHNL>V#oj=<&U9Ir zp;UpYg2Gc)OR5OHfND1SGL>tF>KjsxGlizwGwt9yo45YUs5uCq*sF1eJyU4{vp=pSg<}f+wRamPUl?Nd;5Db!1!ygR>Qv+l)*1+a01Vzq) z4H7pY&LDTY$m|v~5gki&SF{`HD{w0+rGg%s>kBDg8leV&=0dE?2r4`R0t|wO%7%-) zti%HH!hso7SJ#3lyJ}b;eVV_u{bV0dMEU1W;`8dBJ_VAhPuys;^&!3%c5wj(QqXb5 zo?(Txb8v1C@i{$MrKng~W>CN+)&eaed0=?VSPyAcIK9<|i=B=sVc$lw6>0%9wFVp; zhOzZlajnsSq9Gon!iqm1;grbR1sH0i6Y(mZ_hZrx7FAIx zKogz))C7HOER;5|r;v@McKR|73-u}K?9=*taYis09OO4hv?aQgS$~Wuk4hD^Fk3zg zBKb8pHU^7;(+G>5c$55V%4^HB+n$!aSL(}3l>5EYz!30_^qNkwYgp5V*40*lgnaVh zrX`q`Iyxs+OnQMk^9`bEW0#!l+DImQEOLmbT6?&mc%W;e2<_1se-ILMd1IH*Po{pp zJRV*P=2yA>4A-g1r5tX5LKs@cw-ks!NlZQevtZ8iP0sd z2R3${aX4Vy1VyD7q%~LZ(o`cRv%iu`jAi$73#)5;ULc-c`F~UgBQ=6ckw*=&zvI{ z+UcS0)T{JRySSJhTHV9rDh5B`Str@$eDqR%Sk@TjKBAdX$^AUDhnuMQZDv6HUQIs> z9-imOWiAm0BT^ef=^7_DM8bGSLu6JRm^5pGaB){%CR&jb*Jib=)#29Vn{K;f`2aaq zsgTQEMagr8pWYK^eczVS11fQ40 zyr+3q1-(BgKde<143rp|{IZU{WcVUS5$vGq&lfQ#T16*}U9kOENMz39mMul^O=@w9 zXMnCUr)6GC4sC?nh7O-QaM76CCp|Lh*3yd(B$gk#a?S&Dt~|6nG0+m-f8!4iFP)jZ z|G-siL#NwdyluQbeTz}m;9;v_a zP4NleYHgHnj!%HLpFbPix3sUSB1rAZcvf<6z56qP^efdl)#xu zoB=3Q*(!vfMX==yp!7p&amjz=!pP6$pG9;&e@>+?Xa58Hb97^?eX@a1bpc{I{;_GR z9{xxk{OI9T*fZ&)huwU5K9H@_2e-@Q|G@?H=VC~Y`RvJIewpx>MGa&_v%)YQ)$aoOQ);M zK~)9)|FmvKcqxN=E%D$aIJ-PWt8Of3GHrQI8$_Zxuex*I}nb zQ_y<;H8dg_f2@oGsmP{+9WM-0Oz;+=YB2#th{KY!IH23eIusJ=A(!6CZ@$@o=|9SX3zi2DzN8bFE_?N%l>~g9b%+<~ce_6Q9z zLB2-vnp(|fiEUF3gm0X&0#{Rw6ctli@bZ+6Z}R!by{X$BH;XYP?Q0 z%9mVyV^igp&4zbTtS5!2uPW{QN^f3fAkdhHbUlQCoDaZ|L!At>0wBtv-kXyx<{ zDq#o_#J^JL6;tm>CGEv(gC~&c_k;}&ms(}E1sqnb^sSSsu%HfmghZgM7*1DOrv-{# z@Wqrn8+@?EO@np+h9kbjmR*lnZlV zx|o|fDkU=po58*jmI`t1zc5Pm`p*a8*QLU(zr|lq|L{Fx4;Jst>F0Vq?*7-{QJO4V ze&RlYd_JJ){$I}-8h`}XJ zz7?KTMAq6eVW4w=a&B2IB-z@s^sa7Y{rKr6F*`r?@u#F``ED}b_S7!Uk>9;6T3XyX z!Jo6ZmIQTN5^IN#Wvd@pV3CsMS?P-zc^y^&l?72DQQ#b%3xuC-;6#Wf(Ns|s$R3xM zgjKF@sP+JIdx&9FlVXxjwHP6XL6b<{`}LH31qfeJB}^1^PfKnh1m;461t{xTui$cU z`qgUENDh6JJ#$KBFq@3BR}DGf5Pm6IRO9z$saqyZq_v~ zb;~F6Cuy)C=D;=i@iZO~o9Py=%X&@fAIhuQEvHmQ-_Qq{{*;Q31q7O6NYrEnGY{}I zP<wD4m;$J15AMqV$M(8_|yWS+rb=ZI3fAtPu(cef{XYA@^{>8lr&PRtXJMQ z;$sR;=)pu8#Jsce*fc&jGLr%NIHG9et4B&KK1CpxkSGZuo@g5<-VS7I7KDBuI2s?{ zu;zl;q_WtUdYoC^duBFOpW8CNG(6etFq!W)t98)jb=|XP4)bLm@ClRax|^B<9`C#y zdqKomKKI6Ops}(fk(YChO}ERCZ)S$p-dj*$E^iAor}HVd7Wuf)NKqzlW*UQCC2a@X znX`VTi%@cMy)U$CT(?F^y>Wo6!>DWhT;{-r;W9r?^+%;u{UnLdhRU!Un|zdk^uMQh zGC2{uL1l`GQDs?GWxqZ@m&NF7F_z0BWQ~om-~hdwHj*Z#qGOS^oNB3nx4uqQNVp*p zcbL!%!UTx~kPN37j)yp)Lrq2u1*^(nB$b%4i0}UP{2)5HJ7Yhz~e| zdV}>2Sx&z2+||fGBe-!z)a6{u*sf<^5k5@GqEtKcoSC&vV`?fao;Ci++%*?oRW)tV z^m_4w`|lqt(VN^Z---KKnAsk9Pl^J2(^T@_1M+9`uZ8XQXy|TgENu>TDdSB|c?!insMEx+Qz!M=>m+{7I{hsrOXA2nb*;bfstGGrPL;l* zO22tEP|i-TQTv*X#?Ba32tYQFw=To{5ka|C5kfffkm`kx04$>*M;Lfwl63+3?s3g$ zR%6a!GTN9@McZsR7I7@%I7x6hQoL|l?x3n{Od<9X_OvdlPQA_j9eZ(t!OqdZ;ftVk z1HuX{K6%s*1&Z_ZgG!eh>l%1!R*qCLauNHpj)fdN*kd2|I)$%kYyX zxp>x?DdnA!3xmvKEWE6@qGeuqOnCk5c^BnJ@+%@;%MR-!dNYtRg@TB9cv)AZ0@p8^ z-?bih&1*?~P{{!P>I;{Zd&X6DmCjkho}NuV?Tpy86sa*x@#9eyQ3S4jR|V6@ zvYP~j)AFuBmainBzWc#9Gp@em%lhpKC@yX`HuXYZyzq=-##Ck z^iGl>)~i=^C{8Ux0@-M; zZ=3q8_;^aS;K98+=S=Zy0e9=4GH2)B2Nx)W5Z@ynNi~Fb5hi-*h4eFc<)tvcr|6r0Qou5{qQ8d=5+2 z@ywIl45h}lhm3YT$`&Rm&-_J zT2LYdxsv!JgqV4XqJmVRc!P`IHUZC8loLkFDbl*Mk>ieS^mNi8nPUTiaa?IyLe zVf>ng9GEC9tiobs{UU&jO=@L$_sIP=y_WR|4&y5C<68y?Xrzn5wGZZRsBD@V(uK9A zYM&uEZTtjBNg35GRA6)nJpc`+x)q%Ya(-J23;0mo0BHz48-Jm~#US556Kl@rwLM+TJD&p8uVu<`Us#N-ZWDf}z1l;&b%JCe5BQ zYaTHHwY@tcKTjZ!L){yshpc9JyyjL^_O`4)3xF6Rw~IxHvm&wV02;G=mt1L zA7q*z-ZM%=j4FdzepWH+~Hh68Nu+sCw^XA7qY^}srSEqJb|56j*sRE-RI73=B-s^mpI1f&srlt6cX;4&{f_^EL{KTQGabEI<2!#br0& z{{N{}bDL1%2W+yLx$vNa8Q;F$ zYce2TDR=_#yd$PR<2u#_Hl2-gp8jo_iajks@JL_83|Lpa$LS%-EQ zURM=apCoJ8))mjyGyAJ5PO;=Ddj=0xMWry(BbASBzHTV7M5k*MzQT8ll#-PA85(+U zKO>yBk{Bhxh6277kgFX-VN5+7Ha)NTh%z zJsvoJ(^Mut7~fFQXmf)1;`$n}3#3!8CvqI(ykcFDT)g^=ivn^#UJ6HJJ3a}Oma)&Q z2e6ydGI;mYpp5sjWI;3{B#r$R7nr@_ek1z>#~A#&dS8{69IH z<77A!S7pz%k8qE|is2sR=G&d(mD#gtnC@#p-Q9{O9P?_)@ti{<@b*L64dRl(5Q90% zmQzSyz;3#=wxNf;VX@2a*v%F@Fnr~cLQoz^4T#C5xw*IIcI7S=`mzhg9=Wx)r-A*4 znI5s2>5)`I2r|q~c|hn{iYIQ(&0X4)UDE7!${}B9ihD*^Yc)W>PIGP?pyPC!MIPgF zkb~r>K2#b)@EmjmOy=0AVc)|BfSo@k?;!5uEryNHUOp3{E;jFSTzNV1_Yn5p4& z0`ZS~7mi4)MZp>rSR<>%V3r%|3tGc9MB zRe2<3@d2ew8VnrgC`vK9m82aGuiWo!cgp=v!4q&yh_e+?~~wsDa#{`WsnE(@%)6X15aq-BXGG z1P{{#iUb?H75Qf1B@!F5K1DP6NSjz4ApJ?Zi+jjKs)oOumau=x7!uNWl|xcA=MyfJ z1k&vFh_8i3lTj_1oxT7%!1VyWmcOOn-<6DY9k zeyN(hY111-pE@A>knZJWD>wunbO7?Mu`gfdC@RQxBVCNyZ2I#Nlbh1cAe9pG=rHv= zPV*+SbKF>mWwXWc22*+Qee)4A$s)ZHGRY)20y$u_KhkM3SvMN3+pb2+7&Tsifmf5E=#u-pSB!S(VDbmw6V`^%i>y%xtG9{&90 zBNO!M+@kL3zj9dinw|0$$M7JE%2c($ws`|G({h}^)HcL&lIJ3N0GUe0QlD{*ctD#~ z=uo=)Azc&Df2jMY8t`@`_ea2@X~Z{va>QZTZ+5m{+SQq(wp&+gZC1UoX-_0F`_lYK zS8ZLad}d|)n2H?x^LIJT`z?-f>pGep8oOz>&T27>-ul*sCCe_hmqeyjRK^>6>L99Pm zDGZg^G!EAxEAm%~j&PoLL8reg76>B^thX}SI(|{Q&-S3tTG0l)0f08+p+pVfzGL8m zl@5exCSZHWvQ=~+X7XqWW$6M?)J#@ zsc+a_POCG_X7@)xfU?0B!rThb(&fxfw)9@>2#4twt1D*Q^c7t9g|KwME%>AAfDtlCg zO?6mSo1OC=mR_?{Xt&vH4tZg8p>L6$-Rrbj?5XcL&Ak@Ke5ZLeFgKnyJBgPeVG?x! z3=s}#iAJy#5C+1b;gSsv#vy7#ct+{z#2q{&=N?F=FlVq0sh8wO*uSZrWUbSDf5t35 zKvxD3P9JzlT>a8cIl=ChcmLN#qn+1q;bxS5o5ev21X3ZOY&sxZ+Tf9$r@9a$!x?tM zqzed3M6`u!Vqv-fpj+jFA|r}?#E4Dc0sQe>_iBAdeA;inen0j`yU_O<)%CH^ zb+o%+G4hbvuJ)_XVXM#6`gZ%Y%h?6zs{L2n3`hn+()V%^pE? zUJ9Z#vQnsFzhFm`$sk5)>Q@`SZj^ntux;|dxuB*W&Uj*c; z1jKy+hgP?0=mbjxPFgk6^^TjjZ8d9aW^TP~&h1?#w>u^~Un*#N^Y{a}QrL zY5l}Xk96uJ8wA3^Gd1iGV+Eb}GB)_R@Y$fYpy|BST}2H=IVO!DKgvY4$>xV6#}}cR zkQZ418PsSDDCpjT3WZPSW81F8L=LNDAZox&6$#nN)DQoS40uBjA)|S+IH#I5REw&? z0a7jyHUp&%NwSo+T7Ico;nnziNv5izdGnQ6=2_~X5#K&L%mh1gsropzq756u!FR9= z&r(#BwGg(AU6@J+$SUosIha2+kPG5rEfyK1N=y4caIr`+TySX#rqMV<#4)8>z+A#W z3Aq`V3OC&tN798jCZ4v2_RboobpLlIn9FN96S&_mhSV0$e}$O%*#+&$3O( z^@rqcCdUUC3-$8#8mrNwcYpDQJTR^DpOw?(cPGAo&-+sEZ!2w*ixrwq=4SwzpkY(@ z&_p@W=eXi8=LmL(9yrrZ!AqwXtkWGDMmso+J{Jbg+|^PrTVsF`kV;bD3E1L9PS6SK z=O?FB`~=&cGu3(+j6Ro8o8bz` z!85mp&^M~iBU)ovvl1Mt;N~+m1=~FI`&k=+k9qa0>ABuP-n|iW)_{5oT;titd<2d- zq12QRqv-h8?Aeum_jj@CK-m;Rw`?bOZF>lU1;&h@R^FPKwh z(`h$pCG)n0-rVcYUvubtLgnVo>~XD6Z8Mo2jSHSjZ62EMLv^p`p3TE`|8hDvs(Q{Z zYmTo`_t&!P_v0^V2q|6plMkJ#_JgCVsjfL=d(iq$a(e>nJLy+}1E}=6;)pRCT^hpx z=}3_8jB=i7w1ksPdCp*OK_^260(ihys6vn#keR(_b;AGGv7} zsMCQ|rV?|{+}uwu!8?V(P%s8AENCkWPH$;w85h|&VY*Nd@B>33;ukK@i3q~x#KMrH zIZ_fUYj!!^1=YpP`M&7%vOp<oB$@JDx<&+A))0Jz~>h*p{ zsI#iqms1q=hcBJ6@XmJo^r9;gjry3?Zm$rDVPj+*8g6=!5aBbr96hWnUc}0@ zU}UUB?v-m*-&8%J`VmG+8~|rpH)ec2z|;!e@Bu>(fp8o+Yw@&kt|qOPw__l1gB@-m zwve<3bVV`ZK@Q*!tpGGZP*`<+ZCx$pUZUWRYF10m%F$4eBZWe}1``Gl`DmPhZP&&q z!!_PjgTheU9=B&G3ONGN;IRo1tB_@kU(5*d83z#YmOMKQ19{K3x2Im{nu;_89kEDA zuW3iZ9G8c+X-#9op^lDV(HN8Vq#&9C@!CAMD{oc6eMO;9!{o~o3Bm0&w3l9m)Pf&f zRW{z>asdYXY9V?xAi!NI^EuOM;xlzYZP+-Kh1_{nH37FfP*auXKGxB}p`|-CM!cPU zo~{1-%U#uo_IS9krsji*@?v)X#NF}@#pSuSC@Ylz;S;O{%(vlCt-EAQ5&P)w;u81M z`aFxrQ5+34UEUOkMspjdkFW7FliMgZ+*wm|XKhOS&fKylwbiO_DqDE;@p+}qblhAz z4-t;VKmM_Isdsh#PcPonm=}%aHS%4cnQfN;TwoJ?4C!nm4mg_Wvb9Bgb^tHw&sZyl z$Hx+2*X&YVt-3??7?;1XCQwL-8q8m9b)<%{ZS6IoGjvO)^WqpCaT-r`k$9L77=)ys z*0Jb$3^xc^)jU(LRukky1ksr^DuR53uo@AaPI;1QoSCslj0#aDFM#t;AEDyQF|Wtt zjj=iBoHN+CPJU_4N)}waI3LN2*EgxZW9#6nJ!c8XTE&xrSVw0p zH!n6}G6WDI)wf`Q@C(0XQRA~I|FeyY&3+s=JtMr&j|cs$cC55iMsn9qVo&ErCUit| zbE6#-BDrkVl6ZB6S+|6VjzB&u`p*szEBAC(RCFHh?oR!LeJo#D;ueE!y}YB!7isB! zVT!+@?l-A5W9#b!bImn|q6rIE&x+L4L}neuE*=Qz#UH&fVZs{|Qwu-b+SH|SyER=+ z8$YIFt;?mwv1Eb4`|r#;^}ykVr-bJ2e(wx*gtKmvYJUy9Qw9K7Rwy-)z7lrwT&jZm<+%7|kvAf~R?ER$J zFaFGEOnu6_j0S_}lM-F&BfKE!BO@L2~kRm+3yHr?;CCn&h(cM6Rr`>&b&ZHvWR zB+fR4Q!zmfg&{bzx0&#twyQ=?7e!A3T?F|u!>XuKEC?C1CGsNCItkQqK9(ux1_fEB zM>C=eRQa;1pfD7&SrO_EMZ93O+SX3`{owB3Pg-ZQScUYtxF>zSWU8GdTncvfBk*qr>xZF1t-VNG9xeqd> z31h`^tC8gy?uao;78$YwNh#t~;}0%gNDLlvA}f4fszrQ?oxCZ`c8Gn0zlMb_)iy_X zIF_3KGvT}$sUz$dyKbkvNoe13^N#(uuv^%YR7V))8Au%#)-D=r@(a&FCd{mfiroyFVNeqCU>qrZxaLwe8j*-c2 zvKWvIYsh&NJw|=*kwufdU4*PdBuG5=+@aM56s@W zb+&ZT?5!6HSG9HSerqSQ_II|WF7}7R?8z@4d+dwHgd6Y69Wy5PK0Nf%@aUNR zBPar~gR&sOs~JlGRNP<&Drg>I4Z!qqf)guJgZm^$V{l}@TqfZ zI5q)N7(!7Fy*TBCs4qec5rDWWb=%^xyxeHfl==;p7niq96QvuMF1h4A*W|J)`5pPA z(u#y5e`$U5dvCYJmoCs*&1FRke(}QUib-=4uAHF8@du%Pz^$ z>vfe?T0@~fH>}s@nzSUUah%Bs_?rJ3=KW(eiaVpvfS$_>tQrI=Yr`FZ;kZ&H& z?nDcseFe&#SqDznS&N*-AXHX{8Tm)o@C-NUqOL1mKA4@P2u*^3Xf}z1KC*GFElOfs9NMI zn8O;~evR4%%~g)e>C?h+rPk)8L~SfbTDw+by1ij`pkjq{{955BaZi1yEnq6Ny2j>r zUi-5mb*-z=*yYMyVs=H{@K>uIo(1qqK*OnK!ta~bB+w~jw}tYXcuvlBy3>3vH4=Ey zI0h-RHYmWQ#`sqq!o)6)I{>& zvV#bodyRQ{Rbx9ZgVDLPrFCXU>p1pdc9ULqtifx~&0oP{$5{BBapOvgz2B18&nzt| zinv@Bv!p()O~g|PA%&ra=mS+c-@<5>neds-EZ<`=TMY7DW}V(OphTiUNV3UE#6~7< zPNy_L%A1oxyoG!-R614X(fEZd8m0(n%gaK$(28O?}+`?G7v zra%2o(xH*{X-GQ+-3a(4O+OW3RH=l$XbM0wW>*0Xgm?1(R&PRkMtQ_wdRURv6D|}H zLZNWC#6NQh3%^5#2a~Lf1R8cAkS>pUQ*7Sl$*Ls_#<$F#U32TrH*VVa$mBJ>h2_gv zP1@dFTRST}{($^$UVd9$U8F;tHuZ6aq=Ibxu3gUugP}s4sQ>Zap@aGPg@xmb5*;<& zn|8h^UD7gbT3emNsJVIlx-p^+ZrekC@t6}L)^sD*a#&I$a7m!(d1Ws=lv+T4n&jX% za*+}oscqeeX#78^3xs%T`{2jBgqy_+2j3U&Lj8$mVTP%9<84;>|I`EfZ3(VdlQ)*e zC8hUjWpz{7JcRCpQAKx>o)Y3ES}GbRBTn2-L5k$14rhS60`eIGb;BT~6 z(CZC)*zusp6Z8(AENO09(A+G|N|aA)UeJ7?xwNF2O|3`>kFHA&u1Kz*q&1nflb5}@ zY_isD(z3(!dvi%?vy|th_bC5<(Oe?WDQ#{pWsjCLJ5#GF5`UtzKPlTpg>XB&x&DQ1 z+g_;OYu0K^`$|gonKW8+>gLQ-rAbur|yq$=ZoR~y3#^aB=%C-|g?SZg@QjkuR%X<@ z9cDAL6y|s&$z_aLn>0F&Cnu6?Fgn0%*mFF#bq=N+v z8wwe`O_{;6z@G1O$AdM6db2|?!RwblTkl7!l>*!cL`qHz;|PgS_0ez6rSh|v%T)D=1c4!uS2L>)Gl)6j5EaZ}5b_*i2s z7z&9NX0iHh0qK0^WExb3Sw*8+BhO(vz+CAJ0<#&A!3*6j$hSLu)|`MX&rql>Rgb;U zzw=|k9&NfPDDn=>RKkY=Qt5#o>1o(yY-@Ow^c7n+Hp`{ zjVrL06$qkH&+?p}d{$Br71LGX4bUt@MTW&65WyYUx3QFGndTT|oXl<&h z@OA2JIzg@1*4nI-qdHARPKP&-IkyJgYZm(*k)Tm5vHJzMurRCZM>?dC77ef>3buNQ zIR=b&9X$JBuMUXnzX=+hU}a{rMl!3RY%qyTI`NVz$LsOHbJ!s{rv_|Vhd$4PVT?}7 z4dyV`Y{sxQ*^S3#%p-3qoN8jjnT=^3)N_ zy!wf|#!pg*s=_&_R*um)b&{!|CO=@rBA3B|OCqj32n|IAkV0BvQCJRnF)D`1a2|t} zON_>(5UtQ&B}FhO3CKiH9fhK}l|h|Rrv^!)6UiBk(Nmo60DB3(Id#ZLmVslFR3*y= z!B%(E?yJJqXFuH6;tt9`l@GH;UDY=pxHKA(9IG$hd7wYYD#W+n_{qXC8*Uo>I~H_d z)^lG>pS5?(gi9thTi+88F}ekhSkfwhUH8PiovV7G5{Q zcv!fxs`Xs0W#_w#7vIs{X)!bPFW5ig#LlYM~ue%Ondf@LQPFGVK5yDu$0Q2 zb7znQxJ7j64927rNwNc}vF(>s#NQ9nmR%<#>4e)$Ma%F_Q8X{-rJ?jv55WHd2r%5r z12-SHlLiy_Dj$+6Fo2wKcmi>grV=xaX3xaRkn=}P-k-`p*CR@(y`rz89kv+#=jDIO zt0`^(IO>$uEV+6LaGd0xz5lUy?|(3Of|RoP`{eVj4uD#JN~wVX`ssIA*&X}jhf5oZ z^L#A1Zk?R;i9PhdUZt#%EeDXvhP-OQp;FsG+jPb~%&us&O!*`gViywtd*pvO2IwY$ zEad@S8ZkkcNPwB&Gq{nLAy?!>u?K z0@x^zw^GjNJq3PnD88}C>V!dgSW-4>K^%3cxh?6zc8D>=+?lEi&gii zt#;EFUzlz9l~pUhnoP>C@~imOX8z&}6Yuk+`um7;aA1V0B1FrGlxaBCLsrTN&%nwv zuh$iE)|j9$$l(?zz{UBvuHk9ZjUS+v=-p0JI?9vEh#uUu_#g>~+ z9I9~?Sc);H6@9T{GcKjxfaf1qdWNb;YZ*q{kflTx>V&W=dj{i|6Dpd{8f=Ac^VmA3 z8cfh7Zsla(9)`ofOcqqZQ+=8q=mXl}o2J63FNMHMl#qr2kUKF=083Dr9;AS1f$I{% z{UM42@jEmeLKqZjFdYVYFzC_r0P&*ZH5i)f951R}iT34VlQrj0X|hQ;ul4_`q6(R&HjxqyI1yQva2L&u&tVUoq#0+?C@u`5(4><-(Yfw69 zM)MgY7ZOL19zyU&Ah&3Dd5`+W%rw~x>1rsWDOzjI#D7EHj)J{%2hL6 zQDg6v;&!vCP%n6#M!&#JYI{Mbv37CP*jiXwpcf>6>5|so9R@4RJNPH4t$K1FRh@cB z^SOE&^vy)|DiM*o23BxYWJnH%w1eu-W1?9RFJA=tjV2?)$l)YI92>=@ zI&extAX4bUF`K-3Efl>9FbVRiuWbGgJjqzpE~ph`F9q5A7h99z#=R<_23WXl>EN@ zUvKTXCix&+Jav4zq_J2vnrnVpQC=>nEe6xLrJY;nB_F(UYT^cq3By2WYH8bIwg6<#(YQuf)_rLM zzK$}q^_cN>-x#%dR!?e6!0)II%z3JFLfoM#XsFcq0bns~ci0TAh!Z}(DhlC`L2#$6 z^$75%B*aC?NDN|WN2H^4!NV^+|L}ny7lwZ<-;sLd7+k!i__0?~PqL!>3%k1)esS>N z7wQ%{Fesn5;#bV~T{hvDsS^2vU#(zA2HBtUe<@>%LT5<2s7s)KK_nith{U35R8WUt z^#wh)2v8^h0aozV(XpD2)lf3UE7XwoB@09wkf>IyK^B_I8ah;85?s{XyP|tmv(3Iq zKJuCqDOQfM(p5#1yB95AFgLXMrTv@Ra^iliXHw^~ISUfynu(V!U(iw$@~8ol5SY|Z zYl+rOxuCg7t#QGo3AxBpS+{7}<()#TW#;^O)0^yeZ?(oZt!w+%>)3a?wzdRCOMZ^Q z@Sgl{=8xvEw~kvJI&<07-E%8l;hEFR_VzJR5bb#lQ@2dawL8Z&wY61QZI?{ZxF$^9 zxak|6Ia9jMSu}TI9efFv__f})cw>R!oq5@umV5{1k9gx%T5nTDRH%a8%nkqHzryxO zUf3=ko5Z;+3Z#Qt4r(|%{YBs^rZ6wkU$@L2Cl97RnY~5&<;jxF-RMMf>bHYgs8rClzow^(gBx zJF|h|PmAb+)*4}pNHNOVC=;lXfmA;ArKJ^z>_wS4P_8E(F6L++el!mtsiJotLDZL&koA%;!_`kmrnBt0xYObF z6~0_^F8Fe{st#1Z%ULpTX^wiV13>-COsED**bl=NE-u?zfMH z#mLsxp;cFw=9ZOu^Ylg$+P=!bxQTW572BL9cSn`o2x?(3Dsq>!l+G*MyS?}7kybl# z@BGT~F40+1Kfg*_F}-%lOn0!tH+%eQ=;k8-x3a5&v!lA|bME`x_p!T4^PK=oNJ9uA zY<82)hZHtp2}wvoNMlGs!ppq(?t5?Y=FLpzW50l~4IiaIDMri>u|-5gtcW!#(we3b z5h)_piY?-=h_PaeNU^rH@{7U$xihob1*|{c?wxz?x#ymH?z!ilduQg(On(+DsR!m| zvI_(*9-cGxqLsy^pFPrBnNyfPeaj>F;3XXkPmkZ5#$7r1XxxMtOO0s*NK6yS@RUxS zuD~B)p|oNm9PZ*i2d4-8^hPE%JqD)q@h59>`+i1p?5k&vf9;X>sozedb8W?$-;d*| z?Lg8{$DEn?c1jo>r=-G)lV3Y?{Hxf%TvU>w@P&;TzoVqy6Tx>raPIfPeTpAie~;mO8eXHHKb*@F z(Eji_kp2JX6WSl5SDb#<6Wd`wVDH4?8{K-TQQ@m+ zLS?IRY3i}F;_uj2pl75 zClU7|W+4OzMtv1JxRn2tGcyuK8(vLzQ~JZVj6V8c>NRG_K`5?Sq3f>$4Yj_BPe;0 z7vV-#dm`G2`Dwg^E;**HKnOnArk|1SS9vH0UMo}`A@3sBqv{&dc`Lmiz_>;X>^O){3BW5ywLa2(5ma&wXHpGX($ zhi!m^7}NR@xDJ($@#B0z19%aqP&F}J*hn4L0^o=C*TC|3luLdKOu1YfiG}g5-{g6jv|=T$m@&o zs6WABB9D)PS28mWAbI81ze`xF2P@cxGT8if&BNPG@*h z0G`uH#9Rl{f5dMF_LKd8|IXF6X-BkIXdOB96!v9amROKDoZOInIr(1dvee_L)9D@Q z=Q6d->Fkc|k?b378`_>|JA=0s-k*Cdza;-qVW2Qvc(K@5+*^FCeW3k`ju{=BJ09=c z)p>X4sVR%6d~xc))Tci-JZ;sq2d2F{ebe;EW^A2ta%RuW+RS4!e==*qtZlO%oZUJ5 zzS%#WvwzP0bG|hf`u16c)=+=7{@ty;pq$a zUwH3@#}_SLba>I@i{8Fy{zbbkdUA1L@w&y2U);XLTJl}omYlY9&C(-F-@UZ|(z`Bw zvwNWX$z_L@o$4`r-sqj$yS?|N<#U!_zWn&|pR8E5;`4o4-_E`#SI%E~3|FDwSbg*A z7uU>KQ(p6>Pn@{C{c`j2qnE#N#r7*+?Kk@$>VIYJv30Z74X-xZv@ zZdd27y}O>+^`qVWyASMsVE2jL-`mr@=g^+xHzaT9yWz+U@9f>V*WdfhzP^3K`%dxS zjoWTKQJPmew15Bp*Y(5tv*pF*d&{p?u$ijzeD!Gc9oa3b^5t4ztyX)t-d{gff2*;z zaoi{vYm8CjE5_*qmmM$<9BCGs1I@>qZ<$NXhs~%;)OyWcVq5kz zj&L?RuN+)*@F_R#Hr%JZJ>Iu`;qUTa3AP3=4{jZNX=u~XH->kNR7dxYK012(rp-4U zx#{(r*W7H~{Kzc>x4eC5;i17pj~sgO(2s6C_twE%A0At9_=mS0xqaI0qqjeI$DBKE zyyM|Jr`=h-^NCMS{q(DMeetgEerEJDU%ESe_ujjoxckj}`tN!A-dXpKe)tcghwy(? z%*NR~|AfK-r}ZO*zoPaihB_s25e@f0dDt^d7-KyVEO38xLj)(Z`M5(G(%@848;;-< zo;rOvg3~DbYy@Y({nZH0YO`oGg4?udbR>fDjRtx=f?v?^{k91Hy4Fo^;=3ao@s`Uj z?OLoLC7uiK($;G>Vjs|ET;r=KtcPP4t|Kf(i1XLtYb8?iK;1&T9ifi5hMSs>uR*K_ zzpdI1a9E2g(rb{~0o+yi?$kEG+f^#8Wipqp5AfLut}f~@luTXt#?Vr&Tir?Sg8sT8 zP4E9A&o)RRAxkK^3%I6ub)jW8+Tv>sq`Pn~VWZ_EsKtQ%4b^TgQvnp$S_6$cp$w-( z4f(+9cpgYX2i)!^sC1NMyn#F2!2~WAN-yyeYRq|eslI3xVu+O@&LySvwp-*h^?!q6xN^co7xCY1NIQAkw zt5ddQ{N5kc_Jq*nBOOH=uh7?UeOS9syGOfQ`>e({SCV+pK8;;iS>B$5{h{yyfvuHNWp}Ba?Hoq$WJnEwJX+GXsy@0RL(uK5$E~3SB zG2VrD2`>F!O5NDm)r0ff<@^)_zDTi(R?`~1$n7%v1a87zLH)EAbI_GEKv&Uv>;cJLv$;R(WmGz-A1?59dsvs zn(iWeewOZ`d+D=uAAOGQr(eMH1HVWQ&@a(Z?7V-FewiMkU!l*_7wBR7ReFSejUJ_6 zr^o0w@RG>i#8-oUi@r#|O;6JA&{Oog^d7VIM`WN~heV^W9s0liEAPCumoz$YSp zOh2Ljq@U7%(R+mV4A6hm8G0Y{KXz*2T6R*TL|SA7UI!_1c(F-A6a}vMicaiznkqgf zritldhM1|%7qi4{F-Oc5^TauLrsF)(CC(S~#RX!4__$aoE)d1fAg&VY#nobi*eEuMYs6-; zMQjz<~XMc8cr8F0ote5jTjvVxPECl*E3ai?a4jQ4v)kMNQO2L*T7+ z*c@Prmav2^9C1*%!V|s-#Gn`w!(v2?ikrmE;udj8+$zSzr^I1#o48%vp*@fZETg-7 zZ8yg~-Q97#EK2u8ac>kakKz?k+!w_wqj*&mua4riVcfGmj8~}mD%6vzo4V(vT7hR& z(w@}aN+T<+L225KOf``9lb)};IX;wR%kf8&fhXN$%`jV8zfm%Ew=RX>$S`bpzOb8V zSGMdynHjb1R>`okDz*bZVb^MD&!}6vnW)(Hl<(?ZBiXQ9G7E09q?>-yH(E03+IqE6 zwTCPd0Hd>UA{{u4OBq(#9?mVuWpr0S@R1aSdo@5-F%pE znYrwJJPBcX0D|>C6-mX zX}!t}p<&1=tA?NQ8oDb}m4<|dxWkH`FP&0ZuQZ2rw_2>}P+^?P#z2ylo^o^;0Sv=- zGBw*}@`56d6N*!mNXY}T;ulcQplgRMFUASggf_Emu4Pyem=BFep)+<<#l?ex zgi64KiQ5dTW{1VRiYuk%HEh2a6$`DR4Fy9eSJtf<)LqveQku+%ppqgR!hw?u0c8)H_@==0C=!gU#l&)`}#wk&{VY|jC%vU$tVDY62?7}bjLxvB#3>D8t z#%8Zlh0x+lsNA&^O*xXpX!f#^$X?NJ1g)}H3LI8kN0ef5Io+llNkcbldF5R~pOWDY zg^MVfhSh{|hCQ5d0e3%3CeV>OivF|0HycN!!4x`7(Xp&f+YfvZWG@Ih8e zjrY7V@vx%yc<_eFoFY(#Gf{)Haa+?N=X3x!RB7g6Vi+{6;A+D4yhNi~&6Z&eP@a`6 zOVi9(SgkcE)|a^ky0H{mw*q;*XA~4TZ7ODkObLy%bk-uLPQoY#9g|RjGr176fe*LK zGCkyC%r{cL?lrwMJSue7R(1_ptLUE0vE_#2Bvp6qz=2z_nkg7$P)(Pm4iAy21U|ab z8Ob@iqwL3UlAb;&bKEsCdk zTe8|T{Ctf?LM;a*M3< zf~sIPgxRAi{!E&wO0S7&BW>yqN6JwALd!05yVPhbME0)iEq5@m{ZO=g2!{QP)>;-C z6Vj$I`#$>j8{~9O4m&(V0it)&fsUsZAStf}K~go$5LTik8<{$0 zcSo;g;pUWGWO*&Y#o861Tnp^FnuU%rd+8=dP*t`mfk0+&}oBi3yY$@+znO zEXWI;wAV1CS#6Ienoyc4JVlk@USUIl;WeO97tT)d#4}u}!a+r|w(gT%B;25!Xu3m*vR~n4vTPe4vz^Khl}8|= z)6mNpk)__A)l4}z6F?W*k<4x#5}-16yR1L8T@442@X)z@CNu^v#TACdA`t||;-DUMaCk_l9+ qx{Kk=rVu5YQ9XR<GPS>b$X_& zr@E%wRZdI{1Qg`ERKc?6xc~A0WB<2^i7Cl^2Z(%A-2Y_45ThzCA}aRH^uB$9 zZxMnHfc%hCWMKYgf4_bHZ|OyVd7v9w>)U;^-fxkDfPgv7S$2Y(>N|cju!HXysQ(p` zsg=9QH@g46Jsf$-2G#R*$WrR zL!siQ#}&N%w0_klvWRwyOkEG73-*c8@-muo+C7K=Bo3EnwJa2(a7H43$lf1EY>~q! z3mwbDz*EeaKAD%~!kO0Da<=BcLYl9Y|AkDJC@+d9(`X+~b8i5nitUFHth3Kob^|K4b^+um zCzkfUZBhJvn6ir5@{`bg_*ZV3kqLJlv+x=L&aJNfHpm5oTk-ekfPQ^}Ai4oNyP&<4 z4wo2xW*l46c-}VDn{&eVe+u%qqksC#~wFzVQ80u_cqNWek zbBc>7*?S&wJP1z?ZJE|9HFP$>!(E>9#}Ap1>aQYQ5{}2y3E|wz7&jtHxVVwn=%hQY z;qjf|^^)n)ldPiv0xXz?KE!&$l;lHOUw3+jrV$bPMc!^m7S$1Rb@bVn8fpmcJZb(dkg+ z@wt!x9qkVViWH;cz*ZTCEDchhtu|2t*sFa#t3yk{U5eg*0j@NXFmdy2gmq4a;U4d| zw+Ti^aFMFVRuw{sgP`21@$TBW+f}ke)6b9Z<4V}1tn9->HAsph=1duR5}waeP+aCN z1b`;+bQy!4; zWAS1tVL8em;&*91yvo~$NY~6YK5>+OOFn+brPzsWhB3F&7ys+#>6ZD2yZHTs%Ji0= zjCppcIO<-@cdXvbX^m{?~DK#d`OOh>+l3d&lcz&JI$C>^4TZZGWx^seZ;RM^z0S&l$GBd=)kwB*_S zSXrWfaCYlS=$YSNz+arKAJVqi*_9oqUFIN|rWr%9cE`qOEaNL{q%rE%+s zn2dxp#y2Aq;f!?q{U%gOA|zcRnZLcxrJ*5oaG}C#G4(h2+({}3sph5Z2uOp-=!o*B zvEA_9ALloGI)X^c)m(a2E5LtrP?2Evl#}0E5>wYM+8hc2bEEL!HNWYx0kza0h|D9(I|EO;H%cx zz&r5VY7r(XD=R9tV1|ifO!Y1NrEH(yW88w{M_K~^&I-Dz{p6S&w#WDnvMCUSFP)>nOjbYLi|+d@eZ-Z0-%(Fmv3*onRo_phiTs z*<<^mNoMQ!%PQ@?Uhq?_e$0(YE&Eh_s4zh9olq|UZWT^@hGr3?9#o~~Zhw0Bgzl_y z%H`~0d!wFfltQ z$ewvMz({&pSbm{NXgKFsWu{mPKwAiCyhT80(2RL^sx&hTQo!9G_w7YIwv87L z&EL*@oRfq;GY+a+UUK-Waj8`cl^LSY%|AanbldO`&1_#UL?&Gbxjnim(w8aUAjIVq zu|-rOsAxqMq2V8p-K$xe5QHuvgte({1?@P|@VYDdm^F`yM)nTT>aVON_|Km*Ei~*E zr@%m~S~`bi^{S;B==r(ZDUmxOG?I6IGIODeHC|I zJ&$?qS=jo=;M8<93Vp@EsFe-9Yj<>r(oDS@Oi%cI4b899W&FS2lSCq36kv`XNT#5( zpf0w(hgHuqXm0Enj+ok?MKGml&6~4ty}XBn1~e9Zt0uln;j9wIc@smE2+wNneD<2`b!F@FG2KIL~R0*pnjCX3Y1jQ$Li(HUa|jkS+am1C+1#x zVak2~*An~Ocr8A&@`1ozi)qJ~=ZadctMC>cv$s5bg<#t0V8Hnxwhu4orpP2nrw00Uc zlYMcu%$^icmD1$$?a0GpmcTTGc8mkzC2wJS)DQ{I^2LK?l9dLSJjWY_aZ77^Zz*tt zc4P(+XwBGLj^^Qs$q4Kwi9Fe1^twrXJU4_y z#19xYv^)I`6b6c2=B4QPH|!#FW)RF#+X?IEmFkxV6yY9Jo)t254Ib5j-xd|M@^K>p zxg_qYevP4}x&G$P+7BmmPUzK>x*Y8cT$IJ)0OZEv6lcKx7ITe;!eNi8Ee2>Mm(bCd zf|k4xm{7R)G^I9h_679;JFu?6N{Uh~ANmG@OJP+ELg9t+M@ZSF!DzJQ!Fex8d_Y&n z3ekTwY)0P~TY!#Z*Jkz}?@7n(D14NQZgbF`@P4|;rA5b5qL}R)XmJ=&7IoFWtBg!F zt}M*`RwZyV3Lp8!`&(U(8?F^E4?+HzS}?N<|JsUoIF|MKRHlKS@7%=gXW#x$@qlDU zlT3~3zFji_>C|5oU9G!)Dn87QfE}zYS4WCZWO2o=WJP7lMGmsu-jiZ2^vXp$`C#x? z>dW%K;p=gOm-#PUPkl-6N+NdDF?csf5y-%Tda7O1YRB@LcON{EcN#?Tz}) zWAI#6CM@^ZQ5t;+1YQz~&;iilU}`7hA%AE{pOIohR7Y{bqXdOjmRt>M&UWQ~Vcy(G z)t#ez39hKek_g*xGi{VwY|GE{^B@1Fxn7LNt+~0WHlZ+4a1()LoIberY?m~&=G4-B zcXnOET5IJVC(3i<*C3XWkJ}7sC|D>MR4Rd1{B+;i4%%ocroOwg=sGW%aBgmY92bTR23baR4$iRyZ*1Y=A z|M>#^7&ln6VZ&qe-zB~j*ToWEx&n1xhlkoFE;;nN9TwS11}8(aolu8i+A=6re%zE% z6ry<61v-u$o!cWT@3Y9;5NSdL!Uh$D)<#;-Nx1JYt;-9_j>GZ{wJY>Fw)c$%sjc5u zexe>U(gArOn|f?IbY$jE`;$uW)t(<3p1$1u%6|6EQlPZpgns>a6?`}J`lDx zZ~k4=6Cni(G}dT)Z9SChi0~HSpJ+M_6h%9BQP<30U^z^H^7Rr2`~=ilT4eg?>r457 zLZULx-&4J#p8j_|`%#_bfr2ST@uS!S3QJ&|mzRWv+|@AOa8j77Z{MwpQHkp6I-xb( z_v_|_bY`QVkzciuol;93a`vQ zs^MiHr->$DQ-p`P6~Q3&^mI)f-sHTTwV<$ofW6QE&t%rJs>fj2s)=g}mtnhsk-I*p zc~%VR)-`5C{`@usmN<*JbqT4Z!Vmu#eX$bGP=W;MLOHBA@t=0Jtvf;`-hddU4t}=k zSK%YgWd*P%yD|r}+iO>C0|=gN+t&UV^9u$*$X1`T@$b2dMTn*aVkCBEr=R{#J>v@E zbRlOsdb8t{)^VkO2TK8aqnVj?e``bll#StP?Job(v`beo8&wSH*ys%dKLUMqC}4PC zU%kpgcOkmYTg_iktGxflzP(=`NtiO7tF%TChCz^MW;~tW-8_>&E-`JYM8n;sXeX-? zVKk@vSKZ4V+pZn_$B;L>aUUtV<@A8(he74E_I0&&)`~{Nb$hDX$S=&N4%^*KI-^VV zN$WRG>wc0ZwDBwR*e#R6^+C?U8ziJGm-yTt?qoyaSIC*4ZR@m0?QZ!CO-6^~WYyCm z8>V#|fSd&%8$m{yQFsT-`*Ka2HfmtFEXK=S3_pzeC0P}xX5<@6wTI@>oGpKP-BJe% z)JH>4UQy%uvZ3@Mjas0_wnwcn&k<%9tcihE2Pp7k|Ne&!TjFH`M@mZsUn~&437G!W%z(AAI(q~1`EakbK07<{iGOlA)ML4}J-oG5fWt9w)YWD1x%#l@ z{Iwi29pO{FP0>B{c=Ae(FA7Z}1Y;2S{O=bi$H-?@{~^;PiK-l2|VRp-*vxy!A<(dM`QNPyViJ12&Wy%n%&V|>03~VFw9YCiaPALOch&Q z_Sf+HlkGG4DYzM>{*71uF7m2BFdpH}--V8$WO8LN+A}QFO48--nJf4Z?XsFaIqKv2 zV8e&LktQ{1Imj~E5$%6-cWnTvClrBbk^uoHQi(CLQ&Uo<+zn|B@~SmT6ZfQOznPqq zTS}9bnnHgsIb#8&k|#Xh_CT4?{H$Muv2j8RnX5Z2L?YsKoI5#eV_Q$2zC_We3g#X= zC|BHD-;*lnLrczI9~f4dLqYcL*b5Gw+xho%vhGj*GB}FuMz_)Zzs)=A$94#K{!eAO zL5$K|I*q)&#cM|aqU5Xaya5~#*VEqONEoj(J-_27yNne)DN-Q|Yfll)Qo6|IQ=b;q zNgTSYUBfRpR}DD9=gMYwk&k@jkKunh*(vv3qmit>m?Lbb8PNN0f#bQU&WUQv+`$-B z1T$o{h0h!X_aLr0^6&5q9T-G4sQKl_A|u*jv}e%^NHIhMQNo`CpTisGJbw#3Wli_( zx4we*8a7aDxTEM|-irl=W4U zo@ZTrZh6F`I~@ZF@+cSTc)g=Zm!{17i#RIA_FfF%jeJg^WTY?%fZXHrx6hsK!~H=l zHvHKk;kW}>wrSBhahlN$gCvqdYjH?p%vu5!{Z_w-r+BV<*2zfFQK8qNx_n1X6s$>u zQ6~zqxWRHMLdQ^EhK?}=c+IL1U5X-_Z1&QegVztgU>EO8WEirqWhd{+EYf)~a@=TeOSqCgDZeKe;1KeHv;S1$F3%t3$6ssViVjB>yc&f9=GcMRY z!>x#FTAOw}*Y0dGo1Cx0e*%I9n4oo&IBSXBA<9$=avYwP3#!EvBjM)A@7y0m7f3UNp(@Q9L-?jk@MC*ca za)TGEoDh_~W0540;KZk2>x9wZ3(T?WZ*6Lw=F8*8a4U{H1sPIFX336^8PJI#5P5;@E1hu7-Q@pkx!tLSdB2wSzf zyBFmixHW$o47%2X`R=H`T!$6RrYEZd(U;(m=BFpk;-E*~+A?FOJ24Vlm2->Ne>WUE zSK9l?a3p=Rf20haZOOpi%OhCL6rf~@bY-0{ zxcKfP9A-1jZo4ZF;@1!LaT5oohBZp*JEsxN$-o)o0?=5aJv7TqG3Bnupkka9El=*! za+>50^vO2!iG?T|x7?@V=vHy!123AsIi)3!7>nk0Y!lfCU*C+!0m$ui`VOmj%H~d`w$yZxFsI;3Z8v9|2&wx3J1jhEa$ts1jZdApJKqFL^;fH4 z*M%w)tma4khE+iV8R?njIXpXfo!Vg#M@yhEOdc=VU8ESwMI(e3v8}TFL?Eb&|m{K!{Ucg{@(mQf;V3>w2T4#* zAEt+k)eRJ}gfqF}n>*2x>ha&=r4h-=r%=Q%129#WsN~1uk4T2Ppmo(W@Y_Vk*iQ+^ z9f?)c1Q}3cXNmih-lp|p-CAPk5LTOE&2%s~43FZ}fV-Z>M*DIuwcD`MrbDh+5usH$ zr}rU^G|<}zg_VkseUd0|i}<{jP(xu~5bP4aIfH!RYt{1L&(&>;EW5K^r_U?SE$EJ+ zx9g3=39XGM&;+SCDHPU`G_;7()Yk81^HD;p0`70Bod!noMTae_%&!<=RfO2T7ln>A zIojV4Oaw0kW-a@MuOlrT9*q?vuiN;iUli8-O>c(HFT!sAsJ3NzB{y;a4gw6{@^0`F z4J;VGA>saK!$}h2c<;yzY7^=wi6YikE9T>qZ5mnq`Ps3CI-akDVWnf&g}1~+`b*d^ znbBNa#R_>GCTt?JMhzw84}w~JsY3+vn13 zj^9Tp7>-$r9Veq#1~yM|Bps6aPspt!>ZZ-4lq}_IMCEof`-iC{9RvXZP5g57Pm~U~Pt5$1zovU{%mi^zw!`_V;rZ~V3ioY? z7?+xP1upW+&=6%FNUY5oK?aOS@jP*Z2_iI}uMYh!A)95{Uh$NAI%8*xE#0GT48P0`L;pO2L*9U*c z*=IzuX@##EkH^~8Y3B;zD*6yh0~c`zNkfW`!-S${i2cM(S!+TDjs zIi|HnX6Bv3up*wc^6j^nlw#a-8)GqaSca$^#UWzJYJsTF%HkR^O?gE}rfxxUj@|P; z?0R`mn|CGZLgplF*`j`&9rQ^}a9x9+7LACEG<1c91CC%Rl+(u>^IQXJ8i_K>7)pAy zv{Ge>a_a3|EL*DTxPQllq`|3X`~$cUFUbL>0@v_L}9+ z^~Svk=y*7LSu1;imj@*3ztdAAunHDWT#g#OLuUvzQEI)GSmRhVihHUlGPe+zF=(|k;PwrEOd zBvUSPFVblcER<6&Y6=UMv>cejqse}Fu(;*6Cs>+hB<_>y7+O9_He~P=CaPJzA~VGV z$4HT*eb&No5^b}uk7%BU7P$I@PEn3$PX-TOY|WTn^BC5~R9=z}7M`NtqBSGgB(YCf zY=0Pem~>xvr_z2z_wdK0E9v0W>0}hv>BLU&O5&bEvw}e0Y6m=U( zdM^gqaBpy)UkOFrbR&_`y`hx_gQR7sdFa)UX$sPIc(#sC%w~yTvf!n${aMB7%=n7? zHgPt_*ki&$-CFv5Tq38-gCp=0E4hP>9VwzOBb@;QCsYS(NJD}siSnvn;q(Eq6WVsx z)t5I~e}4s}tLC7TU7qw{RylYhI<}f45su60Fs~6@F5G@z2mfZc zPpC~{a?CyV&}glU`lU#rW4wy14PLojJYiWQ-&>PBPMCIOq5sN4(fZfVEo-It5kO>( z-0cP+c5NZy;sk=hGun25?MzXw?2Nl7RTBt5yf?w6X(yOadjZaX;{9 z&eGWy=Dx4J5J{naM2Z=u+ZCTy&ik=?;4n39C#Y1&XrfTYliB&nzt5`j?2v2EUqi?4 zXW5A8Tkl*)@)mmw#GaOhN?fO-Z6VB1Me6m92vF z!H!j>Qb&j6K2qbyI7;y6T&?&-93O)4q?XwY(%nACKdVU3*6fp+*ZnD%JGN)aVkx~T zzYjA=%u@?RcO_F8`;m-TXF$(pDjSa0s9N{wMvXUunti~`5a=1=5N>GPo;@huZ7Blw-Kq0(b4S{JP+f3PgUE{qHl{~6mn+njuxTv9vj zrM}(Cn_6U}Y*#zKYEaaeV(zsk!L&ilA3I(GAe0@cA-Iipk`{NOtO+sT?is4X$I5j? zE;$*+x>C=*(aAq8eQ#DC6rNO`ceN#h_V;!Uj*n*EES8tDFj^?#Z!=Vs6G6jc?@(u7 ze?Fg&i6w|8Y!cQiVJ^AG-pb6P5RGI{88{h8sQh5OCGAV7|}0x%8|ZtpsoZ0Vr^u3RfP?`l_m(qr|C`chpN*<7A4R#7tAsY)7P ze(o8b(g^jk@{#LK8u^+7q^}KsD%{3T<{l1S?rjfE+&{`JMVA4m4lc;eN6{|H+az&> zuF@LU(BH80t5MZ8V$k)fDq~?lCXc8v09z02tRoo~76 z*!*;*C-|lZErNu~3hNchWdjtr!!6(;dV?W#4Wwse6P=XvPTc^Hduzw&G?!7vrH^T( z5qmKj=U!afFIB)dxcR0h%^7iDZ5qmx#e!dRn0^Z3^IIVtOwR_9pM{Uaikq@NC<6?` z&u`ZZBfsL!1A5fL%J>l}tC+JSqqrw{K1H&8b!5oQK=w+@@r8i*bRC_C2{qhw5D^nW zh!pnJ;SX#T`J7tIw(83E#P|;HH8UE@DTnG2zk}{ZMNP)^Vkd_@(K4#MMuINK?J=eU zlhBOH+>fVSq zO<(JrTlS@q^juk4-D=-yk?@AOC02tM87gk`I$m$Fv^XE%ZLXKXcAGor#SEF4h#&S!P5*RR`0exopuGp@Ue$7luUpBn5xa#G?)#Bl@1h7*%(#8 z`>}yaCVLD4wxk;R=Z;JXMMaghD8BB;ocenKfKo)np*y$hF@&$R(_+IJM;r3jXK>7* zb`?;w=F{O|OVbLn>#;dG`}J4DgdiO6c0=KaT%;xc?S<%Cjqhc}6Io&)O=hX&J>b%d z7hT|ZROSj>%aILdsiNht({eHLWm^Qj6>7=>zyV*kOD~Dm!HALNH~JCP*uAlUrPbYP_9W6wc%2qIF+rB7sE#5OZ%Z0|Rs22~}tK1kE1ui5v{9OA)(+fv0bZ)7tE$ z@uwq%n(Mlsv-;-B$a(i}cw=WS{if^DxM;*OMaVx8nF<%3uOOMj*eH%fA*t3Mc&>iq zjUlP}*=}I2-dPOvWB5N@*fF^WG9}?1oiO}yZQR%3y1NuUZ*Vr-b5);kLTm#&cF|iq zo)fp7r&ivhKKUxN--D{x8%1vU=zWeJ`<7wy!n1#NXCBM>Bw$JMJXR4F3Rbjb9!Cr?&_bN`Q^gC5O!ott+R%cPpCO zVs46N7O{2py?O%}>IZ2}+%r9m%EXl#V!A*j9z$VRHwE#ATM-Oo>-l=8De{X6)Pr6% zh8^(2N@_6gtl1dFemr>#EDWl3>d#7O&#YMNJv8NWxcHz>xs!0`$sHUN7ItYhD*L*2Pt zWDaQST>!q7(`_rr+42rMbLH55cUhy|%=fg^aNpLj|9MXzP=XXxx=Qs#iqGpHT8?&7 z6!OQ}G@>JZ=stZ+0hmO~iy6jc5)xy-yB4h$c#NwJ+m1gRCD}9&c@aR6VVoe@Y@t46 zu$#l1e0^Dk7;;|LYA4L9!JR;l#!%=H-0Hpli_WnNRZI`}1|!!3padFbEi5*>se_!- z$;nE`adT69GCE=6*CGl0nhQ6dV>W6;$+$f!4g2eF6UGbKNv`H@Fs^xdkT3uaVNa=y z<<{CN(S#t`tEs0%!+%_h@H5Q(zSOEEb%tFC+wBJX!bNe5n4gt5wt!*{`lEW!Xzjdy z@xgq<826Y?GJ1r(GY_b%zm@p7U+%O9ZC?kiK~3hspk&<9n-G%A4kjGC00X=c;rOY4 z#q0eK7k+LNc$0dDP+S%WPD96u0sZ2)$W+Xfv%Q*fz7F*YD}3(}z?Dpw60k#=j0o`& zl}8FCNN)T)3NO+pjx6sdjB;PVNSYrya*ptQy1s-jLgERQ*32H10+YH8GRaxf>;CS9;>dp6+duUCX~A^mJqr&MvJ39p$&%X_BjC zgVm1gi9G(*d17rKP+5dSL03~s4)W1vON_ACdjP`KEu!-vOZT!TyDGBYVjw;k%tlNm z?H8dtp{pThq&; zQKo;LPJ(;9^zV*G7TzU`xh`CoDoefMcRx{gcs!oR$6TbUKktA8K;p~YV`rJT=4$k+ zsVbUwpc4a|Tj6Q)w$yO!uvcO1SKi}=qMYD1qBDk}1>qI)4@9y+%ADuUy27QkaW4a# zltqU72AoTjDAUYeKxImvoFf`kXKrVhj%EdN`pB06y@+N@;5!{RzE)DBCouxJ*Q z1lz_Frhk_*Zi*!v&zZ7Iahel}8Pf%_N>|E#GG4-ej$AzK>s{Wq z2x3@14@^cA#%E|&chd@$?Gb)r zu!%HgjRkf868>Q`z%hx6tK3pwJ6?|6_x9JKUo>%4d3$0GEp$)B>$2|NZB1;_2Y+Q55ay(j^PTTI%pHkj? z=n<&$@z#9Z7<#~unCY_Kn(pvsd-5@Vd$L*Q1vkGsBIyuM+d$J@^$zr{U0&tHYPr{L zD%MGI&EA}IH|JQ4|I}6qnC$>tzQw`3`do}tmfd$EG;E8GwCovgMP7qicb<>5Ca|Yi z!;&*I%6bY4o{s48a@*eOBJAs0f+y0{?J^VFTk5dcezUk0b3pIZ)y~i|UJu!`R8p)? zI;WD4RbKp6Ogn`x6~gJsOS#4;cy=TVW#iC91+w`UcfM39bZ~9W%sXa`H3~n!SvtsT zOm_F=T&V%EgX^_R>(+v5JBNR`=-$kP2B8)m9eg5?)cv<2w%;@B-of` z(1h*SaZCdov3EU_Ch6wD$#xLg3pMvtWTfdhKEBi!^Wk3L1s&6olVndKi$=Xu8eK&Y z;0J$;w_68rvD3=)bjsH?VIUQ%i5S%UKayDHyqwf_w&gdMH6K3GX^gg zUIv=E-B5e?zwZN{8lIS@qkeY|c&>>&I%FKhPl%pJrLE-`=xqXndUGQjs!GO{P^pvh zk^q71UYX$Kf%=iMR%CPm17mq*YlbT>wQe1-=JDI@vB~3~XtyDNX1JZTe1WFUrDv)H zo(-yrt<7@DHriz~=83Hm8QGiQ4Ehv0@l+o5OhnjvSXNZ)(wTMMZIFlDQ)%| z=!E!pZxd66Rbe=Am6Qo%JjPf)p?UM}YyJolDk#3JqEMp*QY|7e_QQnmH@G!B!z}qa`UmNVmA?Z@k`~PA z@O~4A&a&r0Rr~QkNZw0*275Gdn}+o>3)e-M_x>mwp$#0&e_$TxRxXjHPxDYH@Y!MV zuo?$y1ZqyGA8Q16Rmc=YCr?JN=2smrxRD^Qjmi zXwdWMIHIM4O~0q`yfrS{xqmwu4{n=q4$&UA3xO z&oAYXNy}Zs#_}2RFGSEEp zE`VO_(PKBHgWnTM8=rLf2K5Umfp|(us$Qrf?)V9-+qM#GTN&5pEDD_vMqQRT$t#3M z0(S>~DBWvtRFUv@Hwxq6kHf!M7|3K-BGqJJSWB%22>!0@o?55>^tw)hU_!Dl)^67O z?Gwxtt#*ZJ6O+w#KdH>a2ZY)b==-_JYbh4Ru@x^-4eZJN7^4euUgsgr!OeWwU&~;B zrSGX5;*q<6DkhOPWnvg(4+x<3>Bp>P&_TIK)m^{*3qQw_9GD;AxS2f_(8AB#Ra7S+ z^Y8RCz3bx?Nb|%ta z9y79_M3F+Qe5f5QS)`z-pR@q!7ks5x-@%-pv}*wk)G{|ECA85<*nV@Y+gw*6X!sHE zD5B`3VXZalk#4}ok1L0Drj{A2SK5SRq^5&62d`*K`;ASdfR)bmwJ`>l{zETY_%RE%KV!$b;9cUhOO$ zUfZu!Z+r=-!wEiW<`q6laNnNpk?&mR3d%D3gq^6-*|3m9n11l&{cH=6^gQ3INb!A4 z+nXr7T+b;Q&d*9ni^EUwgWuzym#}Y3oiHR@atrQ2`_s>E8V91=7F0pHV7n=i{nxC) zOd2dvV}#nB>I!Nxzg1Y_hmRUv^dBN|69zn(dun=4(jS}r5%l-f8mXp+x^a6Y{#L|z zROt|?kiT89{X-cs#mCzx+xfsO}H^+UK`i=@#P!c|kTtFDOfRT2Uy{wvGV9PaN`{`EqZ~eI=^PA6nF7A|(5?HQ zkgnEOG+ThTz3I_N$Wh~^R)YN!mJSAT>Ka6D>Rr9oAJ!nYMMsk;yaoBplHy_fg(3yu zuDQsAS2r<)RpnLEC?P-320<@{bl?3PsgFn$k9mIu`-Md?u3G?8VpFR)c+PgBTCdBG zp-a|F7F&;LSaCPSQ4`h}t5>YiRB4cvXeDJ`QaH)4eyf3pw}o4=u-u9TY2?seE!Loo zS<98TW0C%xhcPD7O|GTgnTVA7M^oBMIx%8{Vb1R{#AQM;@q5<^28&hYH8GqdS#drv zG%y`nl=p!!hVds`G)lHVcHnYaf>}FJ_>cGGiQejWF}u9fWVsW%F}#3=gFg?o*VB)d zgU5oGq?Vr60xrCo>+JQO33I$5sMHinfoq90ar8qKk^9v?|^E-ahz(2~neOa1OT#p4KDp|p?ZTL$#XuHFw(=Bw6 ze94Q3l@ng|gxJD18tHFR@AQ1%;m#MXp-WSDUR=-q?Eb{H+3TFMA3Vbn5HO`=mmp=G zy;DlWPRYq4OUXJ|!pOPWW+rb+@za8qVMJ_D47R-d5G?6ViPx`|J%A@AyF|&ID~nnk zGnax5oie{7q&1BbN?Yi@K6P`PyMaC*hirbKKJt~VlHR(sWXK9`7zw_6+Jcz|Ac`D$ zrl7i#W7?7_&~n$CnRjlo=wZRjX1X%%<$a`htos$Q`LZr1;QSC{^4X0#fMNT%D292g z%Fy-I#;5I@UWCw^%pf01h!wUesgvqrsog8Ed8~aM#?`laRds7*Li;J;+tqE~I@V#L z(N#jk{h_+k{=jsZw!dcn@Q^}Vt$uFp)p{DQ+j$?w)zFdBOp~GNzT%D^B77?mg&3Jq zl*=73X#iH#@iTdNu1kpWr=~%(9dbwRh6FeNBJ>tWO~z}!tPmUDVCTfaR;RtNHuFmD zWUD!2&BsIIBNPE6*P)TA_+>hG#YJT5o*<5{Z5EenF>#0fjwhtVs)nhPi;GiR<-?TF z zk;~TA673(NkVaj(KBc!w@05^onf3r){p@)dSXW+z5Lp53b?WLjJ5O4}&eE6r=G3#l zy9na&jq-~fNu=eZP^F3@M#1VeV%Q;f01*?feWPUTUCiQz{OtlxQ)i&@(#7sf8_RFn z_zl(qN&8!`sG8}DRNz9@oyZ(9k0j>gd*tGkRe2Q9bZcMCsT=#ykBxk8cCY4Gdpwh0 zy*~CL>-Yx0fm$;?pN@TKAG7GRipAf5#Ct~Cv$1(>jow@A%?Hzd978^HCH=@W`nU%) z=`da;>@~y%Ys6noaF$BJ1F^cNy>H*x^%%cTvmR3HCGw~F(nf>cj$+TE&m+X8ZH>5w zj_*JJ5geh<&LG^&-3>MYy%*rG^(k7ws@ z*_b@N#vePW%*V5wbBnJ{$8pss)61p$TJkZ175bmw=WhhQp5(Ib+)Sf5pivxQ6zlO6_a z7r&o1Wltfm8fboXwM*@ zalz;j)vkuSndmtIF_CJE`<2E-gZiOYt@q>xMD!(Jvbu1Sx=WwA z+IJPe(23K1LI1ChdzPLb+7YUrTh|UD7TbSc@KLI|%C=5xH=IrpE}O*9w5la8YxEcv zeV4%MfIM-lweSDZN}B#iA|}#o+Oyfopn2|)Z#cSB_!yEau@Ar{XjGwJSbJMrd(RH* zAS%aCl37VG!#y5G2!6MZW&nf_F#W~qK{Oc_V4Mvrb7rR zaD`}!x$m4bqEVR%Kr?fL zq~QKRCFhO|PIXCZy;8|fbQPb;0^ECu@y=7uu3o+kH$<#({Lu|yC37Xi_2_&M#UP_vB*vzllRG-w1(FRoe6UqPn$t=7S42cMJGFvl+IRP=vyce0b_H5T?##eWt=$YhyyWe?nneKNYaUvqieyUY8aa+3$I)Ln>|D*~Jl z<4Ewq^?;t%9c#%ZRkJOfdR#GGrmDn)lZPgl@3BQD-x5QuuO@^qO-Ns^AG7mEQ3$gEkR)fL~Y3alDY;Pl&n}w-3HeGCb3d2QZUKx?qr>rf; z#Mg1qkMigkZBD4a+RR%=l<)8--dW2Ay=cvslI70vs?8_vtv%oGOZ za4iqRHSUYxDXJ{^+AIq+nny0%+*4Va-JLEbOgR(EEVz*Kn7CJIWsW$3PvO~GMqkz{ZqoU~wYPiMoO9t$Le-2q60_uwD`;<&V<9s)7P^2IFSOJ!r$Yj5Ci>kRS? zPk+I@I?EQ?J*F!&@WN_3l@|$AMNNKAHmq#klK$c#K#A762^-MdahNGs8T4H5k4hfJ zRWPh_TyaB(Dt@~o)m@mw-E$A4opDDRKp5)UbktNSHf;wal=;EX)RVithHKI5U~dv5 zEML6jw9DXf&g^HeIX?T}A-YbjHweU^tM5+J@7g2bmDlz3R~UO)12l!)NlQ-yRiGMp zl-KgM(YRCBbT&Tc8~|79hF07`a5K_oQXg^~Jc#OAq%MpdrgVS?BsR+;jG5TP5jf3Ffl+ zOXvV|59xBeeytPE*WLESN^7lfpZl;gQiB5O_KeD~>}Xn}3brqixTGo$F-0t~XP>gN zT4z2ra&~LS;HK_HtZg-6rY82HZlf}7Xl+%L`{MrxHbBY0^g>0um3@>UI$m$`q@GtQ z1M9?AoyS`1oT4wqQ?;v&4Oc}-Q&;G8d4V-+oJ|s{&pAoYoorN2Zr8bEvpfk5a3?-Y zAI${6CN&fE53C?}^pxyAdgGKG(F;;M;gVBvDN!bDDU};%#^hwAisVc@kz`Ra(m-wx zJt1h6gu9)UP&0G%Op)o2rtX0>y|#;ZnEX8+yPizK!%|4zxD{v(VOnH{7RazY4>epT zd1OjsQbH@v*pgIaMb-=PWg=C<7$xkuwZKq3!ZyaZ8cC_?Ak{6+n+1 zmLiOwlFjG_tUCf&5sQsb!!4BSLZ5VJqMxA3>T#5y^<*ZZxi;_VGUc$qbH}N*RA{lvE1e=RDr0^|+ z#V_zaUX*15k|^*dRgjHdNsQKpBuO^&gg1g&<|8)IA{Z4_wDLx?QRK}wg8~k_0gR%- z!21=oPOg(gFew&dm54>b8b#5-%Rxn`afpHdykO;9+a*b~ldwUwN-}mxCW6gsuuBKe zkVS#;icx|VmGBm@124I|FmJqhwX%+;tfp`IU;A?pxf<$~aij@!p=HeBri%52Z z(IbfxAr`ZX7wZg)*&*8ea#SUvNhYFC#Dp$`wZSR!ga}3=0U)mL5qS%a69J<{OlDOE zdPN?VEh@cyHw%O|9)}U+7Re@yM6BU!MIL)5D#T=v4M6|dWJLk1LvTy7065%6SrkR1 zS(d~GUM9TYAr78*S`<5PHu4T)^Ei&abT_Z^P6=eAohOQ5l4Lqn1l%^!Y&1zC!Nnx< zHltOr5S%-r5`mZ1IwIKZaFU{s_B=R1F@tQ7B!fykfMDSPy9Ggt;Lsauc+n&xc#Dcc z0B~Fhh>`$;T@s82A{qtBsPd9klpPj>T`;&MBG54sJ+@lWV6<3_B3Ny_{0WR%2+B>9cFnbADN)m$rx zZh^K{V75zTOrBBf^dB6bv=IksuT! z1R$;iU*co2wurxSoZ5~0cGcYX$_X)RjEu)*_yl>)+xFJ&x>C-p>!#W5+N<9Y z@4d=sbCm8C{)owA7cyDrBbz<}wg#xCq>Bz`7e*HohSN$zcUDmP=PuJN< zy@b*sDF06J4cCc&fupFumKV5D`cW=wLjNOKW@P61@ozL&W^++96mL%Dq4c+i^!HUF z$9R+;xng#XD*m!>M0JQ)IT|#TS(`h-shUbZ{v>kE!f%@DHMQtthUPfc2XDe(>YEZ{ zb}8A+Q8~pn_MMWdF$lTKHlQNz5c~eX#Op{xzZ}2`rEjXxYis&Z^q~`2_6OX?J{Zzj zb}-bpQRMPPP7CVnlVRGmVH^Ug0Fv+9s2c;{SZxz$A;%dBWfi!`z6fMwCs3Kul%dKw za{1#$x(zEE1|{_Ipcz@L$ZHS4Id@^F%O485OM5_j;4V5qrH=sJ1?OOZ>NA@g>3tMS z1Lt5S_64niFU~A-@qd^+Um!6d7d6O5bI}y6ZkB@9EvmX4BFF5TJGdF#Ol}Uhl3UNX z;*>zK>)eDaB0@0v*Q-n1xbj!5nF$9b-@^oMF)t~lAj=;)fB%Z@S4;g@%%0mP3gbU_ zt@JJ1fAjujeM;$b*Q2_fJbraanv@T1U$OuEN0y6yb7x=CFI}w*3lfCFN|;-$6h5Gdlcr2mJ|5RM#**QStS6R~}q>`hTvx z;;Pka*J8=zy(OEIl+Rqp?*9-jxU|j)Pylo zE%X=&K_cylINahtJLhjbp5HpZ6aJYio4Shoa@yP4yW|JjyRQ7&Gp@Vt489ibED3S# zn5V6TFE+&BPHjg_-*%uR%P4b8xeeS_?h0-{ciWh)e-Rjuk?nB|Ik%RUI>XtMOpuky zG=|x?W7yR$!?vkVZE4aegE6CH`|iGZ^*WQhX~n*SE9V(4d-hn2^Hv_*w_=kl zHnp67;O>1ZH_4dNa54F+)nT{f10wG~zM-{a`G#|sB=lG7@{ZQTl5;ocFR%`Utf%>S ztB82guZGA7?wG^WyuDTM@k9CIzrI3DL_Z{b+NG{&#GXTxZ*QLfGuj7lPp?|K>Z*Y| z(yJOQ#>I<`mWEa7I|gQ7m^f`!>W;zo86fn*UW1&oN20D=hWRfz3j1W@kAyWD@XDU?i4Dj{SYjDa{@DC8QM1+f1&+?d|vy7_8I7+x;*r26~HwPjs8o>>psTU7EbIF zuNJRnR+(L8ttj1sMoFN(q~!pmFC2{d-4oJ_S3kJxrgKOCx#P8m9=wd4sdU>dO7W4? z&f9u$fH(B6$gS!vKI045$7|t!rN?eowDWo|U9q;C%s=-NyB<83H(d7Vhkm!C_=sY* zcPr$q!9!aw7#RI$@2cF2UNXNXULUN}&cnDK1@7-&yW&zTY|}V-II1f>U;nlTlYwL3 zjTzIgcO=U!uZg;#;w0Z11^OW%j?d>^iuNa^-KO8b<#D)q9BwUNrJ;*q$Jp&0&xXIo z-^e~nl()`MpjL5}73`05y2S>VM+9 z)i-O$@{JBlctA1ya=wX+^l$o1MpKKUBluo87wkgSpY|?ScLAd6k za)Hk-`!)q@yFCn>yqR!;1RLeAP zZQZQd$(bt`cC2j8)^=&%(Z|f{RQb!#Ij8B7MzbR}aGiFcc1!npEP`a)^?eHEA> z5E#>yNiw>TR;s;W1FC$&4z|kW03WLQf(pZam;wmJo6}ic>c?BMxke?aB&IO@0h9cL z@A|#%`)>rHV^`lLipeUPS6MsKYxi6_Z*E`TFXnHV6?+>#B{zB7V~dt8UUt=`%Ws=$ zGf=wmJX^pfMy9v)%wC-9ADrH{JWTRq-`vYZrk}n3sr+@SIT~MfRhP34Y0CRL*Uz4{ zcJbV~J+4-N%?U1%zGQQDMx?df>Gn3-%?7LG!uCKsHjRXr#0@iJQMaeg*VR35)#Cap zzUVph)=7=G>4s@ppE|O#*DdJ-;&GS0#-sOE?{TX>WHvz1@_MpkpPQlSJ*sDHcLaLYENxz%vX zxmL33#epl3)}NkOEZKO2RdU;W@g@D+E;{(cuH9YT9=oGfTjOz^}1 zuzzBGC+j?x?dUNn;wty}7>%1c?xUxyc2jbf$sUMQw5(!V5bmfrwJ|4eoh(PQ3u7U^g09FvhQlnW z*h8Qj5hd-ZN)9s?#8Z7){Su<|^-CS4q~FdC00Yso9XCTU3-p0cu6Z;@m$XM zw81kMhQE@SdEnhcm;T_|Swq+CpS$J3pgAbFOI}y^x=;M(GkZVx&YJGXt}`0`Z*%Vf zA4hTbjql91>t*+v?xfT8Q$1Na-JQBl#g^qNcN-g7*v6I%xMPFcVH=E1GX{)lu^Bd2)ZIb^@v#%vMgOaynb(GPq9+38qe!&#@{i%qyEt z{B6RvCs*~K*l}L@^r>1iqhdK@&8zp_eBZuRO}KKFNOkiZ+Y+1cDSR2pOF)v~W%E6c z1nWTXzh>WgX?K0!wkz6~-{E3ax(cIJY?*)ft-CM3|C4!5p3U=$tJ~JknpiC@S$3N& zJyQ9(C03-@gsBx+w&5`@4NlduI+cLqiLV)zT$GIy>0BN;Qx{J%3}HgWvHQVr3`a&~ zjb((z(~X31_#>6Hck!(b+j$rF$6Q9P+E^+2j0GyC^rw$+S@EDNVE$y@1>r^Uan=>* zx36k((QiDkMXCr^bWH822(`C`BGsHhsb=@>lO`W{Ys%d_ap_M}IO&^8)Cb(_7gn}; zbdd3AJVsA}&m9Dl_-WwBm$1zR9pLz~OKWHK_gD2Dn7Q*xXUetZf$rJu>$}I-G&+6p z#tEAa-4NnbtWFi5x_IZq4{Yhf5kln789oYmz9^(B(Hy)M%@MUB1r|f_+r~uQEs(BF zhb-Wb<0$Rsy*Ry&9B1*2>n5#+=?&zV>~x5BEQ+K*+(Z%FMD!Y^s=(+ID~;8h(H-qy zH#^$3ac8`7b#H8|yLol{`OB^2;)}u;%-aJ_?AzBhE!5r~a!2Cvi2Ir&(tkHzx~;d# z?@HW#)08;FsbGoo=C^)&buY6f(@I_Dpxak~nn&Ydpw3s<+tj(b*;x?jrSELow{zx! zzN-HIS+$qK*6EdZ&!4n$LSw7XUK6Tm?pj(uaM>PH)%c4#nkU82ueQQj?Ha4Wp6&+oO_}@SR?FH~F>ZtgwO9qwk_nwFZ;j%lB_9%lJt2r%p$6$&MtO9@X+UOo?Woxf zbG#-t+%&aJi*2rDQ+FQTIkik)z_L|`PbKh}#3T-X9I$^&tT8+WJx=t20|x1Sls1!fLogOlF&Ije;uujhE)rrV`aH5O zf}~iR!6ip3HATneYi0g(Ihg>1qzn-pge1m6NCFZ^BFcgP^0jd)0WpS%Hp@1ghFic^ zkKBWpc>aCF499c=#+ke_%V39A0OO?0^0RO{Pp0sJ^mB*j>J(8_*iGU@{g@+jwA?WO z`%(#!y(pD{eKMVRRu*6qrv|j5i|IR+7y+SxW!EGl5Wb|V{y{LYzI;iybk!nNTX}QTibR)ab9tL;q4c1q z<>FaW*<{;dx?$)866tTR4*Y9rSygp)RoS*b2f^Iw2gA~-IA2xd69ivT6(9f9R(50S zwEkZ5&L2f%{Th--Se{1Qu*hM{IJS~_J4h@R#yb}bRlsfbl9WwwzVswm3|7pBGncLS z(K68TlWTj!Y7(o;w!0^QJ5*0rMb*lYClLvH#npr(7tlI}?tTrl)*>IEpQ+%i7w z45!`(*Ml#{jXUTXS6BSk;amWTm%Spr zf5$`8Z!hA3V!ujn;Je@4(*Nv%88Z$%+rQ+A3H$TB7Q0si@y0tq;VX2Z^n&#ME0^7{ zS5=@mpoFT${pj@9&{bXS2lBicmtVN{vR6s4{XUsMCQ(W1R|)jB)BtK$T+)-fDluzsBze*lSo0(6e;V z#G#W6ssOq`ZBZ(T6;X?BrFNj3D$vc%5IqJxYxJq8RAZdF^E6eC>Jp@~cp!3YHDAXT+0O7|gHi8*xS^S`Zj`*(YYKmBEw+AY%&wwY>QHLe5bW;xBCK zHJEyCJ76+Yz$N5JN(LW->GQ6>R`h;%rB}QbBW{5;V9FQQ0U2osrYWP3f}QqCox?8e zW~VkyJy6m!wP}M+KI28Q*esuylurG*sOVk5J&A8}-51gmnQ=kJ1+(D!k3vE$k_$0x zJ|C44^L&G|01eU)3I+&4%BgX1& zqkzP|0C#{7!5vKE>QDBsdvQ`t-@+NKYXY3&>Q8|1$**(ZVrJtQ*kTWZ;IU&l`wSWr z(b%>uzZTg#)CTZdI13^JI6D>t5{>Bv(ks%x?p)P(f!9-55t%mmR-n4`&eRVu2E)m7 zAT_WJ-wUDPIwsNo*z%c2>gr~j#A21M|FM@I`*8m!=YVZE_072v8@6qI9gPp*G(~Sm zW0+g^QOnMmn8?bGn{;9T8YO5y`sC@&f;#oSwun&~jm-1XDn=n_1@X8fcJ>&! zM!|^mZ%wvS+X^6CXrN0j1ZusFuGa|#MukeMUIO!ZO6Cl=6(fbvZ4Qqlj2?3zacX;q z6Md8;aWsu|$WwJCa_VBAL=kKCm|Ih7p}b8J983BjMi(rp%TIeuCNpP`u~j=InYkA4 zO-`vz*5zcAB+~S!Qw!2^Q6~H!qwpA`HL?X3tCU>EO@<@wz=%yUnaMZ@Q3}r**j)z9 z0S`}ZM<A*)YFa zqt=R`k~$6M{PY^29lX~KQdC(*84innE_Jg1$dP_5!qiNgRs%cL0j;PCg(fwre4Nq9 z`BY7l^4CKlm8fOmQ^0st&y9aQ0O1=;AY6ilQYPzjQcyM|LB)`6=9c|T?ooy$cQz-y zc{qU!@odmYvc*0LDS??JQ^e8>lc)|9D3{)XRL&7qSHhq*vmVa{3GC(o1HhHVvrS!u z&YzPa?|eXZVPLnDR*&X`zN}nHcxwz)3AKp$ZAqHC>{rFfm}pAJ`DG^JxwM9(#1;@U z;po3C&IZ<+Nun5ebD2LJYab!11B8R3U0hR(%T=><^1%4D`wr||JHAs@s!C|z*Cx=i zGqIwwv5BcFD5%u7hD<%ZJ*H5rwz8n0ifL-BT(RJWr+)g>4GU;ul@8UQySb*+PTW4d zvU2+Ni5E^+SEz5j;f7n$V)})*udkl6v8FKUcR2jDMOIs=rlPjCq9$as7S-Z?(ZZUI zQ>xeBzVz7owzl=h$oMbg{if`s|q06`+|laVe#AF2iVuR`ZxcE~tJu@s>@187Oi?pfH%3~nLeQHqdU zTv1q`(U3= z0DZ&ux?;oSAD@= zFkx@Os>80jo;uf*{wZWRz7YUMrReN$@T;X{I>hCV#J#`c(gO!B?c8~I<3fFH=ZmIg z%{}YZ^)xRtz1ULR-(TDkKfG!|Q5pWY%Ze6Y{EggJ=N6But+=*K)Gyq4cqje)bg)Y{ zhh1)qsX0k6hSVRUiE;TbsY;p-mAJ&n7lGcTD=OzH5PO;Y_HatFSw2D}iJELmM_0WJ zaedD_0XwHMHhFPMfV=o4P@F7w<8^P7QN`H<@7#lT)pw!Rq2+*#c*_#AwE5_J?;YK1 z`u#xy(c$zVDNc|sCYH@Z0^0C7A?7kW_c}IM~;r4Gd1p9>2R_<7*EUd9`bfc1%X@c=%|yHkKlvl66<>6@t$wL z;Hkr_PEo54^YQnN#`iA5sGHdEa+Dr7uue*(lIYQl67?e&ZX-B|*~4-e?Uhu!ECKM@ z3|qMyk#1s<@mq$kv)MDf`Mj`Q^@Nb1zAGQ10cZ74WIq}jPVU8_hio#HK%c_USGeQT zYV>hH8Md~M1SbxRT>qAEc|bH`)2_WI19FZoo8i(cp{ml@yu%#1k&%ww?9A@QEUrN? zMtlM$Qc4lOOa_T2vp$68Tr$7oh|H}jjr40x5uVjg$r;269HUTISOWU8uCOn&YpFvt zg{OHbQKSL&8kN*Pl*o%uc!5mpraa92(SEZ>sGm`PGtG)!IgD^Bw|+Wroj$|<)BhLGhiBM7 zyv!hRDuL@pfU~H4=J~;FP5(K%;(7a0{~TlIKmQM&DE;%SCHwA13`jaC3uJkr&)A}P zmT%@M>QB^H|M$O=|4A>+4pn*mwE$!|4!n`!kyXtgY#xoNA9iOolK&&U`}_93(^#`b zBb$sD3^IrE%9BXnFVi}+5KnYe z_Csf2 zV}<-LHLBEc84TPt>OOcChOj#)~X?ZxcahJn+Xc+XZU}Fz!PCkY1%zy1>AoE9p|$5;g@|4uS!f5^HvGSA&U0700
      V$fDV|Iw z-#ZH8@kAo&8X6qN(~8+vauls2VmxK&6M~O83OR_xEJ{?4GZ$vqTJvKqld>-g({5yZ zQg}d+aKr=sA0y&0N0jUP@W+l-E-5LOEh#@sE>(PF$z%fAxLms77r=&*IN+7kRQjJx z7)f!ZSVPr=oSQMt$IFbh6K+)1sO%~!q*8%5&`OO;C2axw!GSS%A17;M5BiZ$*&=OG zjlEmuazo|%&rG?fTpW)wL%EL1HO5Xj3qM@G?|$?Ia#QdID%V)M;Z(V-WNSazpDuAo zHTG^?uBp_uOqiK9ti6udyQbH z7slF&%5}!-jR)gpd5^eM8FuGfZ$cd@efF?^Lw`DUW0CO< z^$j>Hd(ZFP3C{Gk$vvk6Efc0^$@ly>ULd&WOz#BWvl88NW3HUvv+?Q5Gc;$~uPn=r zRWhFHXdVQUGplXawtz_97=lfQ!*~!=X3>XZ6lF>zFbX>YGXRsEBW)b6aADX4IvG0s5>sZmuo|SX_=VFgY zV_N(u-2z%#Zmb-B-g06b7?drNJw-C{joCo5W2p0LD$Jl_=S=P&;L@j0r`WK(^o0Q(Z3C5IKRtzxnfznlS04*>PKd z>}{z%K={em^tQxucw7^D?Ay>{)pXE~wjeP=5t?Q8z zJ?pT`p3G+PRfp?J27A`gi8CC4alCt74@_cLKbiUtuR_AFeEJyssWHo~gL!HWlJ&?u zollK)_7iAoRKeEufCMi084fVXRD5KK0V(kr_EUKnv`I=y8L5J-C%uhWn$t$pYh7_C+bU;?Rl}hhR*GXFEt3B#)5( zI<$56?5(qlZAhas}%!{evS#;{97qv0-Eui-TYy^&?TElbwldixSgj4M$h z))~UC;YHID_Z_%umAmCCM|jOW zt8cvfroAigSsiv<1^RntcXrMm{<-ADmk&V zWm(&{*FHTubN;5~(`S2KGp8-zG;hYh@bAcq-$Htv!(Yi+M_ZYJ38~(xc+P!{iD^fX zG7Um4Gl;XlK&=eOhgz6``+}(79T{0Lq^PnvHmCe@5s$ak z!hIDvl`L6km;NY3n0U#e0uT^RU5#y{G7cjyG@vRDvh^Y959NnCP9?MDMw(nQdY(lO z&-a!WOE=pL-il(d+VaFet}4esV`TgfTN;+Ydf_?YzD^QH9u}La9 z7DndQ0+W{?`&1hG^w@H=1k9($J{U>n{_>?a-E=9s0lH1k(xp9io1qH4nn%u+lJI5A zbGJdm^N8{8(0tBLH?11J8i!l&grw2-qYI=-Jp zgc%W^kp~N ziT?%F2@MCR93o!O(W+_qW?c5UGb{)RpTQsdsj(kgSKrtF9SVzwIBJVf# z#i(7<7#ryYkQeFy(f~QnfOBgx1=|pL5RHFj5jvi>%~_~2YA%+}GO<0pk>nZ>+ygMe z1(^2qWitP8peU0?#)y%y)l4=V8r%~P?4Q}X?Ec>4AAEH(cEQqEtgxbf>#2*pMZ^hK z-GKuht5K;_cj<$>2QZ-zBD#qr}X9&8x&Y(lUL_<7S3-_Dnvj0z-uy>HwRi` z;yMj$5KK6)DN}bA_24q9hMGWaz~3Rqo1-H6MeD%`8Y-2jIn1O|Rx_#>I*96Ow*3EU z7CL_7#g`v{=*_q3kN$qMNo4D^HDbtK;jOS(?c(wit3^{;_15DL?5}j+bn2o1QCmS< z(s1E3ec;jO6_-4_R;qh?Q{^D1qzgG4FLG*zq5s?vQF14Zkbice;<+;L+5fB|u`LP7 zCB$Cf!+Bw&>;)FnNEa;Z9?O8BVk!mQ5b=)Ec+@H#+iD_J=4BP)K3sYFMt&CaDS3W9 zl8pFK<}`~*iDq<6n1(?DF!c49#e^%zvaYG%c&Oq)?3(P@AR0f*a-ILVBjfJ9k> z&LfN4MWsP$qbPD(PkE$}Q zgaZjPAVo0&5|Y40)(M!q0g&!!cOGp7ElnEmm2~r5)?zhUrB z#C+q}A(=C#2oQspoH&&k=gfHQLt-%-N$&tIqNU3J;nT9pT3Z1JJNG4KRn#Jtw6-F> zh%Sq@O(_c+$)=55!aPkD6UlF1?Sca7ypWzI=0>EC_5EEdiwd)N@_EbMAC0LZECcbta4B*30Mi_35;wu$smZ4!_cUJqxWN& zdGJRPn1N=yj zna!UAqhqGy#==7BGr?;HJ+o7{d@g;S1`7fL+9y4l#sdP=%<#Ir+oZmfZw+oaO{s0! z2Lk13iu46Q7U8^P<3V!%z*Y}PcMt(q3aj>f*SQtx0QP*Y6Xq<9xbaF0ONY@-aQl8G8fq3#At70 zlfz=2U0^Ksi*yHgGSUuv9X@EGNz+Ik6W~OVE!q%TF@mAtEj7 z)ImCs&QZ_5y|WMm@n#Sd0zdY~`hjZ@AH+Wlmm(+91n>=yS`;g>t0@o04e^`37`?!Y zA(7mXut<9&ZUX2Kj?Q%hOy&&*WwslVYZH#pmw$8Arl4u1N`Jc~C7yp~ zKQLVl&1es;D7XfI9Z$amKTb(BQ#EZ#XL>iP(}eF+C-%&BqQ7UIK1oRoJ-kjmYc9TO{L*EUm~&L=53e{X!RQ*b zuk2{(4EB)v0Hkm2VrBe1%8%pDE!gxzdO(28UD!IB06i&6dX)Q0uPzu$1R7FQpw)oZ zX|ztGb%GnnL_CuVhp38D4_Y#4DcktoA>(JijQK^-z%f3q*~9CgjAot9r6%;_^4wVk zJV8&yh%rB~aElYNGYQy)G6@sNn6bqWV~5DZKu9TAFuk<9veSRD3s}^iUHzfv+1^s` zni;b%ar&Jhf6wB>O21MIAcVz!`taf&e+ccrWKPc-bk^+V_=i=1Wr59GQE92K?kS(S z5Ii{pAKD%~5@eC6p^DV|J1e_Or!QDIv%IIe-cniNwLu0#02pe-rRkE?N1P*`mX^hs z1mUv_lkbn>%~{fQ5;Pv5@YhJJ>y#_Kj%NWEnFU-HCL#Ud4+K^*ZDRn`AEZBElK}yZ zL@TGMlhQXQam*|oPrNHVW7{hSNA9(Ou6N}jLdK&cs6WdkYVXODdm;YC5wS>?*+^nk zJMe6dZkR2O63CJ7JZkj3LXN6Hkk7|(u$cTn26YGe3vpTnvr@X{s_m3i=t?`j z1zw^%;2K_%jcu0slRR=P1NtsSqe;gS(#tHiIun=TTYCSV>{z;g)6R%NQ>ZaSc5d3g zv_lSRfpM5Pb$#okr|Cyi)Z7R5Y@gX}=Q)nIchB6u=YhHMK$y!rPvc#9@px!;8{Pg9 z5e}obM`Zb=g}dw;YEd+qe1|^29Aphm<<>D_$9IHrG11$OS@h%u+JhvvBybT>5F*p% ztxr2e+)yme{vqsn^6wPVZZwf|2a&8dB^ML!Ps3FDLpVK2=Ag=yI~KvY_36(V=aOZE zn%(H2pTOThIU1b)kw&3mXeqANou<~_AWwEXmbx0(bv2t9V~Ig)HELL~u5D#qLGRvP z9SG^vAW1XmDpr2yeNxh(MkGS&MRpCBKNj_22h#u%PJ!)~$7XCW zL7kM~l^S(i%g&Mhm-GqE>6CG!W>94S+xmJ=g4ux8nHX701&ME^n;-A#lddqR1{o!O zX(muG2PosB2_$sTv|+|it`oETM6b&_2B6(yG>AG2TDs96?Iw8L-0Sy9k3FU>bksfY zlJwY1(tqLKTbZE?f85wq22Z6}I$q~;4|UPc;6Kncqr3ZO!((0WfJ6CX(ORTcWw7@- zl0lO1-l4BuE{f92AS{Z@u@=`Lir`mbExdAsCG%Q*6ok=vwIaTvK|UG2eMY=^`T6M4 z!8E|WRhb5}&woCA89h$E9l9+DOD~gx&=W>JAD0RjO)lok=sbMIxtO z8^lSzhmrKK80uLVV#h18;fP;!2Z5Vr{md%E&^1+XndSNCw2xT8Dh8~mNp06lb!;M$ z`f2JH^sz@$AHN@oTqAwF3@nAN6X31ymfU?e>A#xOaqhpfe$)QO>AJE37ndUhPM}`uYejXyYa5Oz${SuvvgY-c$tG_PTsdF zk3&^}L#-4Xg{$iX);v`?Pw6y=GoEZ?3y5XFcj=@&DlIoD7_I93Ez)|aR$9O1e5H<2 zn9zvXXHh8h%R0WgSr)DvCLDhA@Pr0=^PJOM{MPT1`EA=#0-)U;#aGJ|Lmk1&Qnl zI)e{3N<(DN6)&BrD69u#`x036I!_L$)Sx&&`cclp_k0K@YJmwI7l8Vm+q6cL z_BK%b(T|t2K&2vk`PZd;UeXFGCH?Zqn8=*p&M|_~gAC<_Y>4O*qgWpv!(mj#ZkNko zFzQD!0i%VyvxYFj>-k${Qy z%W5$pMWHG6ob()630I*38FQ(m4x@2nDj|CO!)o9AYrjc2^X2mkQ|JjLE+veX6!ZTa6wFkXmk?^G3vr0Uda-lLrS8X zN=dsBJyJ^Q)B{?jlBGo5&|Q;U61p!)6bJk;p-$>d;&55OmnRE=U``eo^%)+A%hR)a z<$tEd0W1?O&wq=b!sTgM0G%VBe49vLng2d><35K*c60ijT6r9JP9PCT`zdK7NRu<^ zN5{e4bfmVf54@o>O79xAIwSBJrBl!)4W|2DcI8s=+sP9bQeF2W4O~+R9Tycg0DF$Q%!kCfSE&_L-`dDrV zXgMf2G}_>ZZr=xx5)mvd!sn5eL+6RC5tikbBv%eU&Tm#`2Av|{(Xq0LA{GroOl~Z1 zjVurSDdzmM5D38z_8|e9G#Cwfk(gXTzmi`jB7f5VL}ltjBa+p^>4A>-dZ=Jlqz=Tgt5J%u zcq5^kxJX$H+#w6$sGyuxUd4uHf(ym8Vh1DrnwQq7Sw<_`9OwmzA4_+)F2)Vi4(SeD zs3jfXg2CmB)Jl#nr!88B(VGe!#k!p@)POe)N)>Hm9g>Zv!Haq%A=sdxmUfJLahKpL zE;Jh$R;$(g?Wo3#X=gZ=Wf=(AcSY@btyn)!&~4BOZve`Qp07QMU9x~?Xc{KgX*9YG zc7LZvqhF`iZ{ANc=t2Nlo=@xJ^bl%~)?DQ5a7(_7%z~YNI7JKdhmjB*cLp5Un6c#0 zL#W9+b%Ln9U@@-g;;(=9%weP=tWavTDz>bza!x;}Cdp#2f*%OFyU~lhUb+FFc^GxE zU7~i6PWa2QKkrZ!sCKCVRI-J>-YIVjx;9x-RPaQWMpt1;4NvU;~*8x z1_;Np0!$zyhlkx6Ezx4d-kIHk?tbf=58elSI+eowOM_B+1>*s z4Y+7D`TjntG9E+PVA*n=aPSG!W72H~LC}D;FDbRVwBp>Ef({*6FKVyA=c3i-Spoqf zM4|@aS*P6IG%-OMS|r=uWRar=BSs_jRV3?ZTn%TsnK{?tOdMSJ5b6{p4-vTJH`rMy^M_!_;fJuUGg;ty+==!xHY&RGTf;2BM z&o;!d`k?Lyr{h|ehz z_>>fs21z>wXtcc;^$gJ~T1?j3s2Fow-Ql1Y??6hByhGLzY0_h8FD)}+)7jGI#zQ*u zUfklarG=-n1_vJd=i!W_lK}vmywW=^aM#t|3E=3oyJw(1Yu(b@1dsf!dwAPX8~>x% z??X$q5e~eD>+^{FI=r}O0jp9O_S@O>z={ia+fEz51YC4JYu|5Bsn~^U@hLZW9!F!w z98iwbX9hEtJ(Nf!Qb?7S-a;E_*YQNcg?ee~h|LE3(XUPg`-!YATb99my;ftBj(~of z{HxLGrTfz-VEwl4G{t;~+A&N`Bsf79Oyr_tc(XU+37Wk|5BiK^ND4BB170HzO0?F* zB4KkhjDDOnT^nLN1UR&&g~J&>l-(vw6kjM_Tca>= zD(#fDZ^qrX%`CZX`epsiuRANcn&#I`S11|+oz-ojYNyy$;A^VsE^p)6Mo)W1W56fS zi6^HN9=^J3&4elobNUn*qE3US!r%}9#hv#6F!VM2YKSjxydZU_ug+JX;h^*|pjnN< z?g@c!++nv>#Q`9_jHU;L&RQJG^CKALoXBAr(r9w_yD?%D5;wEp4VdGjNTO%ffVvu* z8XC-CGhno)1W4&?q!(&rSuKk>QH{Twb7GmF>Dgz7nE+##Y9Om-0bOqO;xiN#mDO{a z;&yNtjonAJQ!`OJgfWGYmq(KfkTH=mYLPsd5N(OYgj~^9fTN@x`7mCJVUfA-#}hS}vX4o9p^|=%qaLIrwy-5hTnY|h=}bKh)@ziQ+)X2VxE02v z>p8tzr!;@_hBP?2>Yr7UrS~R$aQ6pH{~xOij0t!&r<@r;CWB~V`*2;q8xXGe=sai? zlu8=V8~?T-^_fCYLkPFfm#i7e|-~(vx$AJ`>H-&AV-&oty-B~js^@B51`ZIf7&*t$h zA)64?8~lOU7aE{>M#ZWt4_>tG9;Z}(AAr0RSd4?PR3Hf#Wo@;26>(FzT7pGj??M%6t=BAat{Kl?a0qI%-ln&W%a z{k8o1{qigg!K5pH>cO#UKQywMYZJ) z{myNza7}5hYp(aN8$SgWJM85E`0eoW0zZTs;`7`>lfNuj(PR?M#Wf{OPFr9~g@?15 zbQ`EFzk8hIi#gJmh}oAnQZx5k%tXtDRvg?ypoK9>F_h_+(@lcgqmjm3Z{&|Rov9&K z#=!b%(%%_{jur$HQ0m=P-66YZDpd1IrCo4$R`=Tqd;z<6+thh?v>T`Ru821%gLsJ`V zocWO;i2g-b^p|$dh0|tvBb$!>L8oA`5L*w-rVN`68W2f9YZ368P3Y{}Xf5Vm!U-2O zpq9|*xm^S)Gz~=QBK-`B?R?NnfGN#kOvp-Nu#m(g8{{yEhA~|ZZ@L_#40E>>84U(w z(bMhispoqpO#?sf2>RVht{niK$pTt=O{v%2(c$uyYWP!-);J=yMP^gca)mhWtE5k)Pp_(IQ<+Svw(|Wju)iFwr?lry4o9XbT)bC33AoKg)nSL(>V|1KZj| zwdS%?ANcgHk}~s?$|9XbC@s|Y=AakkpAQs9F;&Z z+%}884m4i=4ULz%{;`l+O6{QbQ@2x(5d9k?2BLS(BB7_Y#vjJmw#Kk~jMtKRc@fk* zBIM=yBVN*Bnn8Hfi;ZC>9uL~AAxynI=OSGM!*`=z;UYZ*glTkl3}hS@Gks6)XSnbA z$LOK-i$SZ!Vhw_s=bbmyuv&UyO<31zI~=Z+r@VK-P!s%P(D~tMV7F z>H<#|`p0(!3JU`rR}`@R@XFnVEKh zHPWTkHh**P^WFBk=pRxm$HiifS=zA5H-6rV>HcuoKm9mbL>vw!{fjrokAGuAYTn12 z8hbdind@m>_ZeR2O(q_#GdgL#^beq)bYR77>Dvj9%s^KMdLHS)H<>AEV=aDL7#xsp za6?Nu*dfP8Vt(I$Q6kRV2b`=K$HbaoMiIu=UUSCS0-^x#gmYA1I|84ZO{x?CcWKm0 z>*pnQ`nPIz>I=}LR;etXm)WG_0t5xYe^}@X1!+>qgE<7yE7a>N!7_t+=sb|R)nwFH z!i!z>b(J|j1Uxp0gtrbOj$%6w_6(S5&WfX}Vu0)c7C^S5L4d??>nNwnPIK|of`V7< zcuuKQ7@jE>=@@VPiBps=L~69j^|Zh%l+qBmRq>}`#%CJ5>rrcrzX#HfbULk%o}uxk zf>3gMk>U*A0q{Q!SB=J-p=6wKf)havcUuCVNhbM}`!eR-0J+|b!BL$ORqS!Q4SJIf zQqT$Ydc&%&KM(EvbJuEvP7l-D^zQWb!bwIDHwi)@l?Vt56^I{BuDQ3Zdzqr3K(Va5 z?cO!RHz^s1ic7Kwh~E>lEf=Ftn=u1(kdGjJ9{rD*l^Uc>e^8LdRP+ZX6aSwub@?We~t7f!u{@F(+3JMGn@22^Ly#9 z(rZ8`eJTAz`Z*|~cS=8(z69e49zDhGB=L0mY-zkWBA1N-BX4#GFL1k*Dc_R5SeqICYa3TuKiN{T?Q@sn(hBSTHr`xA20gsiWWoxNf_&9=2b4^QHT4 z0k?pKsSYnH&tU2>Ts6P#a2t5zsY6eJ&!r=~K|gpo_0$|V@uO6i9X^xiV=<>O;wUtd z;Gk7Z7mmgsZ(1&(vXWyiJyVYPi;a|~X6`d3-r4=U^r7imubrtZ@Ja8VNbEXsVpjsZ zUQ+aMQ3?5Zc+-qi2WD*AG=sTh#-@wmRjr*n-`WoJ$<E!4^`mQNHl>%(kp}T@zm4-P(4-- zZx4Gp`$HtB;|#4h_`zR1> z1xSo=0#4)zHh~}QX7CZr3la0NI97tLQf!U{iwXn2?$}!0ua>k0Rm5@=#oGE{Zk1|4wUU(OiXITj87g>hmi?T{GjR0v9Lz1;z%=oZ*Ch4qH*~9+GbR z=8)d3WqGLdn(a!u$W!NY?l=jyfzsQX3;^ESI>lw2InyX;8jY(rR1{u1eqlnPI07$o zc$JE(YF_2B7kZU^QK3TN9TMypc66J@RnbO;$rJJRJ!eqfbQ9;Pqo2M{vN>xDjXML5 zb(*45N3F8vg>4T_v{yQvdUZ(f&kId4wGjSK`CTcFgqI zA1u{kp&m)PVr?`KL<5x`5Dr7!uu;qzz;e9Y)=nDjXRr<+j1stdX8OuOd2se5#r(ai zXc()UaQ%~}j$p;@4^#v?%-WF0`KveFzM48UtG`R?zgxrF^;LI%`?$xc-={Q|ulv39 zkG;Kt@-U;Y_&A{81ntVl0e!+&T+ECECBwX5x0Q!1rj>#<+T4DzW>H7=d{gmE&|tQ6 ztjWaj1t!tPBY~ae3sN*6EMQix;xxC_&2WU4ifyaluOpV2yVarb=uP9Co!9)<$JUxW z>K;?!Laixa25L|nj^7FsDlJo*;?X>ewb2_PoMYh1KcVUTCY?4|)3JHu z@+njMR?e8#)L^zexG)|M2HAwP{U6dLSNZ(b;wfK_Gm4Ians79_8an>qjK-!;8w114 zA4xwYLRhN2GGC-QY&7MlHAndpm(HIX_7|ztK#)GWM_p7@J+5uP-aH{!m&ot-Q?VH<@%=h8@)=^yxTEp{|AzZY*P~(C{mR zR=QiI)v2UAwF;#vjje~2B!iStsX)RYiVU&+pUT8$P%yMo-yJN~GNO2j1VS@|0RuocmlB3FuM?noicXPxW)R>r`0rL3c!H;J2}TqO4i10D z5*?{QnrDjUlIeTO{@vlo@t9F2iHk6zRB#V!iXZ3{`Bgv-l#Od&kJ>XpG6vJ#3Jb?x z4-F$}=@!3dqG8G0p&-M#Dih#YO%`^2aQ5Yi>VE5;j(tAbD)@anKF>GXKoeDRKO@A~b( zVlHc*Jh?S0sJWZhtS+SuG^5GqW24cWu9n%7{YJuMlwQIIQ*-ejml)cNL!_XP+T05( z;r~iq1S6>}L!a${H`5mneE{zyypjZ?mEB2V77LN&Hx=m|6jc)?^A?j{vhwUEcXAo_ zkt8EFWA&0K^FiWk!%2!bN*zap7UOULoMg?DFC_he)L6i~F00jL0ViD+i_1E6s;sGT zZc`I8JzhDvX>QYjrt-2TFewy=53f!PElsTH;x$@+;^H?KPvo^49vsHUo65?Ym?A5_ zkNp4DrZQ<}c~et4c(|-dOf3(^|BAQ%D*whq@HTLB?D@@`pO5X)@|`8nwl@gl|Gmc>oVgzz3>97x5A!kUEZbb5@f#gt{>%tmiQQ4<5yMl1OB& zv2Y~ulT5udo)c(1RREda1I-=*d8Re zka~h1X~8$Bi2^6Yg#iTAgeI^*yp9ga4T0~En}7)75mG>OHz&=T@I7$>v6YM1z5@6l zv3j9e$K+WvOkiO6^tl%N5SrW;wGeL9^o`T)>}26BY9+&p>>@_5vMFfkc7|bTn&&yj z$N&fdr02vKB;F!1R|!;;yf*hdw>ns?2Wq8R&}xCsQ($2jlRBtx)8$^!yC(Q&3Bg-mO5ExXn0>5r3 z-6q)d1r9@z%EOnl<1RLtTJPRe0-4IoLcykDK?7Q5I(-&%n@2%A0jQ}3bbEoQ=b1R` zEHNu-#ZJAFX88Jc0P2hN6~&NND?yQHae^`*qt|JyKxbzaR=pZPBhV;~N*#wvLUYB8 z$RMedVf0o2GzL+xWR#F)8IIP{i^XWt3XC|(Vc-R2 zkp*>Q^pXl)1pqW@QMc9@)z*1x!#KZBsbN%t$J6aLv9wlS#@RF$wZ2nlRB{Ch&ZVQd zirTiI@u#(uJW89vQiK`4mq$BI*VnH5)p^^>&7jCpcC>Txmh~$eUz=CmRRW>Mj~ZPe zYKmCDZgyo@bFO<&+TY~5d%Sd6&XufK#h~JMu$b=mo0(N z5WQ*VRbKtmAMb58yQJSphr#@wni~&n3-}pf#n$Zyk}eRU-+ANL^Ges=H1rQNp~LCV zd^2VGo{i%#>uS=!PagtGQ^({T;|oNnqcq-nzH#%UeEgD*pU~$$z6S0^o*w#0THBkB>H)CC`VC0Zl=? zzPm6|##vGKqLIeH!WYKEEljsx3)PEtk`P@5Fmr9VhLE}DJ=$sZ=R6dW_%Vc zP$ry0e?Cmm7L(2Q7`2VD2pF@CxjEP{e`eoHg*O^$`5tuZ$ z>Ckx=S5I4bMs-7}h=u*z3Ee z_V1QAq*Hh!+Xf7g?VDtblng?NRf(sv477ly7=%e6tO?D##7$L=m4GxxNije_?2D-r zwYNl4Cn6CzIdV7xl+uQiW%Z4vTg%G8VW*!fYzo5FFtU5APL~Q8O$-z?(n_7~Qf-B9 z2)5|UAeFrq{Y0d%rS&JvN-r&GY$(HwhfFD4O-ByH=B@fNeJY>_Py>$W%XC}y`XSh= zA7+0b@y7m95sv4;|HOV@A|r#rv_~|%H4w0WM_e8(`b{##pE^Vlf^tYarNm!K>vAUr zvb=vR#SRjLM%l{~q`hX*LgIghk&@KL#E6$pGn0{=Y1HhQTp1kv5ia^`<=4u9J=q=_ z2(>5e0p-_~e=Q1^)ENNPy#gdwbOXvD_3inOJ$wEG43^ZDgE@Pp3-y9MAbo+Ufq@}l z7xduvz0$Grx{@LrNUUBhC2VvbzF?1BRtA^VPa;^;!malVOS#RmSY}jRPhGryQ9JoV z>+5=8qGz2nNJ>M;C7BbhZ)hDU$!pR$yrd6G1P>1k^sHM4Ue1*xWB+pFxb+rnBFHef zK_o_5tiF6h4-0w?#-gf{xy?3TQ=`w;JhwDdWHd1IM+_<-gFjd%^%dKZgi=yc=mGZP zzDbtr#uyhWkUsGydm8nlZfrv(;077MG2^fQhq#^;h~I!GLf~ScJP>ZJFbeLu3lDvF()I- zf_LFMJ;3#`NvfTiNHW;Uk;02dLfj2>40cI+La-`BGuR5!gb0nm7{uR4F+tNwgXsV_ zPQd5-0`|d<*F;f>3cq4a@%AO-65$KG8+H1pOocX4q>aCAkYO>7i-B74I6dXKSQ`+J z589;(sl-o!>L>8L+Q6|buZy*!C_c{`N?mpgq~-_)wYpc$1|eel>xKbbv4DJ`d>iSH zkhC+V8cQ9Sll_b`VlXW+1xELY{03zj%)TuH4%acFNf!fR9Eet_jASxE_D@czq5#$tXtpnJuhjbAngFvev=`H*Y>v3D@G>x&? z7{_wLwKYf)QIrKvQ?|Its0Td52;Pldhu5EPD^PjY^k3V=(Tu(f2pS8^ z8Wg5ly`d;tUQ(!qoS;;(P{(rxOAnO4~YYHdV=W z1Ax2MU|~5C$(RhSHrK2!ENYrxUC083uc5!Yq+P4=D4|7E+ab`f#$tCv?Sg>1#Zy(R zgp9p>VN3s|Dm_gD^dGW%rOb`{Aon#pnNpEauZo&Ot)zCLFEXnKV;)?xij+=k1|JhO zt3L#MNPoj0V=U_PBV8Abj5seS3<6Qlt)qe!Qe6-htYM|K6V zLMyA~@Q2vFI?ZemI%jNBD7CsG-ssdhPgMTb+SN0vs$O5Ub}`Zn2c*-7{v!QJryKy_ z&|iQb1STE)xs;MVkpBCv-B%|b01GCyRWh7T&v94(E>u|wS)EE#zo>K5>;h3yZbbz% z&2P1pF|6Iz1m?^O2bDEZyQ0w7((=%}!f~47!fjs;c_!#}cDHA|%W=Eb!Ln*?v5r;u zF7NYso>_eUB1h4QroNjd=&YX}k{8!?UcaZmrDMxeYc>KV@xYan;y36ts2jk>=GKi` zof`G1hLvz}@3uPhbX11cJ}r8>t(4VH?@MiT*o7L$%qKd>M+C08u8Oly&i4mypp=w| z`OyiVE7GqqYrP5bn1t8|3_KbvjTS~=E;{!7bH@(+(&PQ5bbIQh6ZZih6FKox>T%$^ z&(qsG@0)`MzhRpt$B=Zv(zk)_Ct&>VQf1PIZ!ZN$hrr*QzmtBF#zv;t%Q%W!jqNQo z7Ew8hCkPp6Jk~+%N&x8disE$^ud~G<8VRvT+h=r0wLwD^wuk8Or_AA1_A=M}-u|V% z)0+&&_0rMTM7v!)4$7DNCic!>GIy4H!wdU1v=&6{yrrvi@yxmLN^ZigC3Bm@ZVSt3 z6ppUCT3sOAeNmH-wT81z?%A^GI`HG3P0cP^ z=PXdE-j}`w_CNu6>!eOlXe%b|oKk&{Z=6vt4W&Mxv61=Rsj|%9#u@aq85@D4ea;r? zpFq21PCJ-znmP?8qMvIzI%aR#k|%2xAZe*Oom(>|ZKvf7iBU`{?21(OO_hu$4-}ZIQwWm`KWNlvSN--T)-UlC}!>)IBQ`C(?tZWmW%rI&hs8UO&zEcs`QL%~TX;Q4*01OJp%Co?WRh7EG;VG@@nDtr#KG z#NGwbZFb{KDUm+Cyg_>HCwE9+-~Rf8#>)-?{+XR`ZHA79)0EawV*FexvH9sfsL;)g zw)ggT`oVqDN(1;j z+C$-`c8%FQb>M0c27zH7D3Ilw=)@WxWMq{t8w}J6BKhl?R460@6(JdtHD^|gQ7V0q zNjxi^{Mmp`c$?-_O0D&y%u>*yonVXJZk4vA7bgKj_QK@Pq?6AII=HkQa4JK>s^~gD zyY?N{P)}@PO?d0l^D`?_ffks4ilcIK`Pbew>a#hW>LXVsJE&znYTq*_8;=@sOq@#; z={`9Rr0<*=+M~`VcRE|fHue7jDoYD$004N}V_;-pU|?ZjXo@RJkLS1f%D~Oe00QUc zW`)D(|Ns9pus5)QxEu^jAPN9Cg$rB&004N}V_;-pU}N}qmw|!3;Xe?tH!uK2kO5;K z0I6LEeE@jcg;cRl12GKsT`m_1IMIcLE)`;6XcwS}@qPfdj!1|PKuCyzP7zn5ugFYzITwTLGqsUul~03g?(GI z$Nvn^x|r_)-_XCSO{+dM*h6>eWewk3wb=*uYlgFXwsW!`?@s5i?!;@H#-=g%hhvaf z8cNdU8*<&++t|&1TT_KNm%!Jd-1eZCbC!&d^qr3*cWcXy&v~Etq88bC(d033+1s4k zf(LUyxoCJuH5v1^Qe*XLf9@+Jl5a~kl_C@U{B0r(8#HJ~G2{_N;1iZoDGhkn}5)14*olpEb$m@Oe z7GBPD_ElHqefpq!-0K*}=F8OX-u*y2YP`-7(W58n*+^Fm=(lJU<~;+Z+=HgCdLMW5 zkb9ry4R#FSQ|DRjPTOLhym^OUKNrb$n1#66*f$ln7kg%9oK@|$^7{vZ16004N} zV_;wqBLm7Y1TaiuxWeefSircBiGj(6S%tZY#e?M>%P&?N)@7`J*h1Kju&1&A;RxZF z#PNXBgL4JvKdvCI30$|hb+~8oxbRf)oZ>a(jp1Fw=fbywUyR>}f0;mpK$pNHK`p^m zLM}qvgeycWM5c&*5cLvWBIYM{K-@??O?;F1HwhJq0Eror0+M}_Kco_*CP-bAW|LNu z4wEjCULyTUMoPv@_Xd}DVQnbDXdUeY%)rH9jbWYPBcmLn2gX9iLB?lHq)hBg_LzJ# zwJ@Dy#$Xm^w#Hn^e3M0h#RJP4%TrcjR!LSHZ1>sm+2z6FPkDM8tU7XjsM7g|ko#s~LcE#PreUpcr$2w0p&qbaGJnwn_@sjfL@oMmz=e5UM z#5=}&osXB#312PWeZD{ZGW_27yZN68kO;^M*ca#$xGC^mkWo-p(1~E9kTYQ%VUxms zh5Lk8gdd3zh=_?;5%DF`Au=m+O60!C7f}XLby0hwS)$FNCq=)D35zL-*%50NTM_#R z1mgnY_QlJ@*Ciw*+)HdqJd~uB)RS~8nI$tRB z7FGSJ_Nks!eXqum8x&?Ko>b}&=)tA-JYfx$W)I6z0q@}9mNUKz9 zTshx$_qHC1o+?ZT0KC^I-vD^pV_;-p zV4TJz$soc20!%>62!sp_4q!e502Y`53;=lAb&$_a!axwlzZLvLjGhef*cju%1Gd!@ zH$+hr1cC&;7NpWBf6`VIAHxUm;K2v+q&JT~fzRRB=~lpKHoNnincZ(@2fzxRk%CHR z0NC6yD`e@#Jcm^rYffPUP0eX+;a>ARHu0o+fp1?mFH-$e^Agt8gXRp@)T8EQY^xW| zZ^)_-&F?VP7tU~kG7MBPL57)Yn*%w!k}1*~V$6)kx?TBq^rlTps=BoP)EoC_LLuW0E*b4fzt@a8jE17u;y)%T zecDh@G~gdfq8h2pc78yGk<>XN^{GCVzC!ky#|~Fg-MaGnVFenLC;7x zl3FKNGE=}D$8ngMnVFd!W@d1h6Q{bRS$N65-R`PVLv{79U%e$N>7U1!OIMZt&kr6^ zO^HfnQ0e~CJ*B%#_mv(*85LAfLmdq?(Lx&?bTNX_(!HgJN)KQRa)K7RTXuoPZOt1t;NToPtwv8cxRDFxN~h83bOxPCXVKYo4xLNq(fM=%T}T(v z#dHZ>N|({)bOl{WSJBmU4P8sukwMp!Nml7mvdJMqJ?fK79&M!o`4mt{k|NqhF(s5z zM)R~li?l?`bOYT;H_^>>3*Ab$(d~2x-AQ+q9pDX&!MZYEQCr``!Y2Ba7`&9eBnIzR9OFX-l2s5_bh6v|{FC$TPSx+lT zYQ`IwO9mlUeuSR3=A)9=w4=NS@wFh z#OsHqU$$kxn#N}0R$Li~2CpUz(@!g@7l=wMO{e3?h0td~nHxi;mPM+odZ8s3+mUZB z8MYVOzTiD0VW#z1^kR{?4dsen(3ke0((}!Jix1;Ot_(%enwNeS2!s7;7oysrS;$#b z+ZNl>5p~PdeK|Gz75+;qmXw2rY63GJRHN7n)0%AtA~q{M8K(T*cWPd0`kviR#bRo> z!t1+fOUnzMle#Vb)(;I|^wLf)+9FIv+|HF)4e#di)+|ZA-cm)KrR{|dkIUy3vK~9q zGi{-wX3TqzkoCy3(<~OXNQAcMw*oUVl&>PLnT}eJBg}pZ$4je;YsR8#yMiO6F07lR zA~Gz~9xRx#)9slY!lBj}3KbRfYGg797#K3D_hhW>9X))g=#>hkDz*wc?eISHvCL22 z9V+?=&B)IZLjj`|cwr&7a}a5{E(f~rZp#FRgy$)(>4iO+PfP4rh%j+w+AXH#sA%%U zTxwZnI26q|mJ8aCb}ni!8o8WB#dnPe9U_Gzb|>+ch0)7=zf;IbVEX=;ShRgJFjw5F z^t~R#PMAH;kytdu5(ABIqp1Yjmx<_bR6;N8>)}<7XDAxB>5I@Y<63NnjtuIy34FexmyaGrYDt?Dw$o!2ia6h_T`0yuq8tvOEw=70%|QQMjCRQ#T8&gnd8A`jYfvao2xB7Am6MwaASDZTE22E3l)d78Dg9? zD!@)TPLi_ga8fWDICx>j629NIRako**i^J!zQzLGT2yGOYblFziwekij!0t_ksH=o z^a7*nOj)#kl3Ip2Tw0>G5OdDE)znM|NsSqm57V?_PxNdv5iNz>JWs0qSY}a0#j?s6 z$())cOlF9(ouz!05l6+0G=99Ol9=_`BR2jUU%`~6cgC<`i`@`uwvLflQkM*VO^J!K%puNUW?E=nf zWM>F%T~V0hQ^sp5m|Gi+?U?W0WJYApYx&9vgJEGcm>2k-`(i|g*ceu@POj!it*cUM z1Wudhrmjpl_@a?yUaD@ap+Kc}tl3rWx?= zW@w9AAe@1hwtLDY-es#`*9F%BH>auIL{E%6GP4wvLKSh1zjc-zf9p()zjeAgS8H{C zd(Fhga7Jr&Xx$OXfXhbBHzU<)proBZTIyUn8#@KQHQrj=GMN@j=VE@(eA+PN!{lSD zT>br}RzU?En6b4KsA*^o4Jy4Q79*8~`R(!rM)|mE60jrH9;a4V4uo6pGuK6?(_os@ zxM--igc>=b1x+oCW~ae1=IUko74>3hYKM53Kf1zq1pzUchg>qS_?GN6UtFmV%(xniN5;)ipu6Y2Z&+ z>?E10F*cbpTRE#1AZBLb>bM=_-HQ@0SyPb4S8T(gRWYU}rkeWcr`E5rk^LQ6eL3iI zom0LxHhjTJuV9!98nO9z{fyAGu2aI8+Bn(DOTMlMoc5g7s ul li.current').removeClass('current'); - parent_li.toggleClass('current'); -} - -$(document).ready(function() { - // Shift nav in mobile when clicking the menu. - $(document).on('click', "[data-toggle='wy-nav-top']", function() { - $("[data-toggle='wy-nav-shift']").toggleClass("shift"); - $("[data-toggle='rst-versions']").toggleClass("shift"); - }); - // Nav menu link click operations - $(document).on('click', ".wy-menu-vertical .current ul li a", function() { - var target = $(this); - // Close menu when you click a link. - $("[data-toggle='wy-nav-shift']").removeClass("shift"); - $("[data-toggle='rst-versions']").toggleClass("shift"); - // Handle dynamic display of l3 and l4 nav lists - toggleCurrent(target); - if (typeof(window.SphinxRtdTheme) != 'undefined') { - window.SphinxRtdTheme.StickyNav.hashChange(); - } - }); - $(document).on('click', "[data-toggle='rst-current-version']", function() { - $("[data-toggle='rst-versions']").toggleClass("shift-up"); - }); - // Make tables responsive - $("table.docutils:not(.field-list)").wrap("
      "); - - // Add expand links to all parents of nested ul - $('.wy-menu-vertical ul').siblings('a').each(function () { - var link = $(this); - expand = $(''); - expand.on('click', function (ev) { - toggleCurrent(link); - ev.stopPropagation(); - return false; - }); - link.prepend(expand); - }); -}); - -// Sphinx theme state -window.SphinxRtdTheme = (function (jquery) { - var stickyNav = (function () { - var navBar, - win, - winScroll = false, - linkScroll = false, - winPosition = 0, - enable = function () { - init(); - reset(); - win.on('hashchange', reset); - - // Set scrolling - win.on('scroll', function () { - if (!linkScroll) { - winScroll = true; - } - }); - setInterval(function () { - if (winScroll) { - winScroll = false; - var newWinPosition = win.scrollTop(), - navPosition = navBar.scrollTop(), - newNavPosition = navPosition + (newWinPosition - winPosition); - navBar.scrollTop(newNavPosition); - winPosition = newWinPosition; - } - }, 25); - }, - init = function () { - navBar = jquery('nav.wy-nav-side:first'); - win = jquery(window); - }, - reset = function () { - // Get anchor from URL and open up nested nav - var anchor = encodeURI(window.location.hash); - if (anchor) { - try { - var link = $('.wy-menu-vertical') - .find('[href="' + anchor + '"]'); - $('.wy-menu-vertical li.toctree-l1 li.current') - .removeClass('current'); - link.closest('li.toctree-l2').addClass('current'); - link.closest('li.toctree-l3').addClass('current'); - link.closest('li.toctree-l4').addClass('current'); - } - catch (err) { - console.log("Error expanding nav for anchor", err); - } - } - }, - hashChange = function () { - linkScroll = true; - win.one('hashchange', function () { - linkScroll = false; - }); - }; - jquery(init); - return { - enable: enable, - hashChange: hashChange - }; - }()); - return { - StickyNav: stickyNav - }; -}($)); diff --git a/doc/_themes/sphinx_rtd_theme/theme.conf b/doc/_themes/sphinx_rtd_theme/theme.conf deleted file mode 100644 index b71548b..0000000 --- a/doc/_themes/sphinx_rtd_theme/theme.conf +++ /dev/null @@ -1,9 +0,0 @@ -[theme] -inherit = basic -stylesheet = css/theme.css - -[options] -typekit_id = hiw1hhg -analytics_id = -sticky_navigation = False -logo_only = diff --git a/doc/_themes/sphinx_rtd_theme/versions.html b/doc/_themes/sphinx_rtd_theme/versions.html deleted file mode 100644 index 8b3eb79..0000000 --- a/doc/_themes/sphinx_rtd_theme/versions.html +++ /dev/null @@ -1,37 +0,0 @@ -{% if READTHEDOCS %} -{# Add rst-badge after rst-versions for small badge style. #} -
      - - Read the Docs - v: {{ current_version }} - - -
      -
      -
      Versions
      - {% for slug, url in versions %} -
      {{ slug }}
      - {% endfor %} -
      -
      -
      Downloads
      - {% for type, url in downloads %} -
      {{ type }}
      - {% endfor %} -
      -
      -
      On Read the Docs
      -
      - Project Home -
      -
      - Builds -
      -
      -
      - Free document hosting provided by Read the Docs. - -
      -
      -{% endif %} - diff --git a/doc/apiref.rst b/doc/apiref.rst deleted file mode 100644 index ab66564..0000000 --- a/doc/apiref.rst +++ /dev/null @@ -1,104 +0,0 @@ - -API Reference -============= - -.. toctree:: - :maxdepth: 1 - - macros - enums - types - nghttp2_check_header_name - nghttp2_check_header_value - nghttp2_hd_deflate_bound - nghttp2_hd_deflate_change_table_size - nghttp2_hd_deflate_del - nghttp2_hd_deflate_hd - nghttp2_hd_deflate_new - nghttp2_hd_deflate_new2 - nghttp2_hd_inflate_change_table_size - nghttp2_hd_inflate_del - nghttp2_hd_inflate_end_headers - nghttp2_hd_inflate_hd - nghttp2_hd_inflate_new - nghttp2_hd_inflate_new2 - nghttp2_is_fatal - nghttp2_nv_compare_name - nghttp2_option_del - nghttp2_option_new - nghttp2_option_set_no_auto_window_update - nghttp2_option_set_no_http_messaging - nghttp2_option_set_no_recv_client_magic - nghttp2_option_set_peer_max_concurrent_streams - nghttp2_pack_settings_payload - nghttp2_priority_spec_check_default - nghttp2_priority_spec_default_init - nghttp2_priority_spec_init - nghttp2_select_next_protocol - nghttp2_session_callbacks_del - nghttp2_session_callbacks_new - nghttp2_session_callbacks_set_before_frame_send_callback - nghttp2_session_callbacks_set_data_source_read_length_callback - nghttp2_session_callbacks_set_on_begin_frame_callback - nghttp2_session_callbacks_set_on_begin_headers_callback - nghttp2_session_callbacks_set_on_data_chunk_recv_callback - nghttp2_session_callbacks_set_on_frame_not_send_callback - nghttp2_session_callbacks_set_on_frame_recv_callback - nghttp2_session_callbacks_set_on_frame_send_callback - nghttp2_session_callbacks_set_on_header_callback - nghttp2_session_callbacks_set_on_invalid_frame_recv_callback - nghttp2_session_callbacks_set_on_stream_close_callback - nghttp2_session_callbacks_set_recv_callback - nghttp2_session_callbacks_set_select_padding_callback - nghttp2_session_callbacks_set_send_callback - nghttp2_session_callbacks_set_send_data_callback - nghttp2_session_client_new - nghttp2_session_client_new2 - nghttp2_session_client_new3 - nghttp2_session_consume - nghttp2_session_consume_connection - nghttp2_session_consume_stream - nghttp2_session_del - nghttp2_session_get_effective_local_window_size - nghttp2_session_get_effective_recv_data_length - nghttp2_session_get_last_proc_stream_id - nghttp2_session_get_next_stream_id - nghttp2_session_get_outbound_queue_size - nghttp2_session_get_remote_settings - nghttp2_session_get_remote_window_size - nghttp2_session_get_stream_effective_local_window_size - nghttp2_session_get_stream_effective_recv_data_length - nghttp2_session_get_stream_local_close - nghttp2_session_get_stream_remote_close - nghttp2_session_get_stream_remote_window_size - nghttp2_session_get_stream_user_data - nghttp2_session_mem_recv - nghttp2_session_mem_send - nghttp2_session_recv - nghttp2_session_resume_data - nghttp2_session_send - nghttp2_session_server_new - nghttp2_session_server_new2 - nghttp2_session_server_new3 - nghttp2_session_set_next_stream_id - nghttp2_session_set_stream_user_data - nghttp2_session_terminate_session - nghttp2_session_terminate_session2 - nghttp2_session_upgrade - nghttp2_session_want_read - nghttp2_session_want_write - nghttp2_strerror - nghttp2_submit_data - nghttp2_submit_goaway - nghttp2_submit_headers - nghttp2_submit_ping - nghttp2_submit_priority - nghttp2_submit_push_promise - nghttp2_submit_request - nghttp2_submit_response - nghttp2_submit_rst_stream - nghttp2_submit_settings - nghttp2_submit_shutdown_notice - nghttp2_submit_trailer - nghttp2_submit_window_update - nghttp2_version diff --git a/doc/asio_http2.h.rst.in b/doc/asio_http2.h.rst.in deleted file mode 100644 index 645ed48..0000000 --- a/doc/asio_http2.h.rst.in +++ /dev/null @@ -1,5 +0,0 @@ -asio_http2.h -============ - -.. literalinclude:: @top_srcdir@/src/includes/nghttp2/asio_http2.h - :language: cpp diff --git a/doc/asio_http2_client.h.rst.in b/doc/asio_http2_client.h.rst.in deleted file mode 100644 index 756f00f..0000000 --- a/doc/asio_http2_client.h.rst.in +++ /dev/null @@ -1,5 +0,0 @@ -asio_http2_client.h -=================== - -.. literalinclude:: @top_srcdir@/src/includes/nghttp2/asio_http2_client.h - :language: cpp diff --git a/doc/asio_http2_server.h.rst.in b/doc/asio_http2_server.h.rst.in deleted file mode 100644 index e7c1434..0000000 --- a/doc/asio_http2_server.h.rst.in +++ /dev/null @@ -1,5 +0,0 @@ -asio_http2_server.h -=================== - -.. literalinclude:: @top_srcdir@/src/includes/nghttp2/asio_http2_server.h - :language: cpp diff --git a/doc/bash_completion/h2load b/doc/bash_completion/h2load deleted file mode 100644 index 2a4bf98..0000000 --- a/doc/bash_completion/h2load +++ /dev/null @@ -1,19 +0,0 @@ -_h2load() -{ - local cur prev split=false - COMPREPLY=() - COMP_WORDBREAKS=${COMP_WORDBREAKS//=} - - cmd=${COMP_WORDS[0]} - _get_comp_words_by_ref cur prev - case $cur in - -*) - COMPREPLY=( $( compgen -W '--threads --connection-window-bits --input-file --help --requests --data --verbose --version --window-bits --clients --no-tls-proto --header --max-concurrent-streams ' -- "$cur" ) ) - ;; - *) - _filedir - return 0 - esac - return 0 -} -complete -F _h2load h2load diff --git a/doc/bash_completion/nghttp b/doc/bash_completion/nghttp deleted file mode 100644 index 6bb7a3e..0000000 --- a/doc/bash_completion/nghttp +++ /dev/null @@ -1,19 +0,0 @@ -_nghttp() -{ - local cur prev split=false - COMPREPLY=() - COMP_WORDBREAKS=${COMP_WORDBREAKS//=} - - cmd=${COMP_WORDS[0]} - _get_comp_words_by_ref cur prev - case $cur in - -*) - COMPREPLY=( $( compgen -W '--no-push --verbose --no-dep --get-assets --har --header-table-size --multiply --padding --hexdump --continuation --connection-window-bits --peer-max-concurrent-streams --timeout --data --no-content-length --version --color --cert --upgrade --remote-name --trailer --weight --help --key --null-out --window-bits --stat --header ' -- "$cur" ) ) - ;; - *) - _filedir - return 0 - esac - return 0 -} -complete -F _nghttp nghttp diff --git a/doc/bash_completion/nghttpd b/doc/bash_completion/nghttpd deleted file mode 100644 index 6de3d26..0000000 --- a/doc/bash_completion/nghttpd +++ /dev/null @@ -1,19 +0,0 @@ -_nghttpd() -{ - local cur prev split=false - COMPREPLY=() - COMP_WORDBREAKS=${COMP_WORDBREAKS//=} - - cmd=${COMP_WORDS[0]} - _get_comp_words_by_ref cur prev - case $cur in - -*) - COMPREPLY=( $( compgen -W '--error-gzip --push --header-table-size --trailer --htdocs --address --padding --verbose --version --help --hexdump --dh-param-file --daemon --verify-client --echo-upload --workers --no-tls --color --early-response --max-concurrent-streams ' -- "$cur" ) ) - ;; - *) - _filedir - return 0 - esac - return 0 -} -complete -F _nghttpd nghttpd diff --git a/doc/bash_completion/nghttpx b/doc/bash_completion/nghttpx deleted file mode 100644 index 7bbb77c..0000000 --- a/doc/bash_completion/nghttpx +++ /dev/null @@ -1,19 +0,0 @@ -_nghttpx() -{ - local cur prev split=false - COMPREPLY=() - COMP_WORDBREAKS=${COMP_WORDBREAKS//=} - - cmd=${COMP_WORDS[0]} - _get_comp_words_by_ref cur prev - case $cur in - -*) - COMPREPLY=( $( compgen -W '--worker-read-rate --frontend-no-tls --frontend-http2-dump-response-header --backend-http1-connections-per-frontend --tls-ticket-key-file --verify-client-cacert --backend-request-buffer --backend-http2-connection-window-bits --conf --worker-write-burst --npn-list --fetch-ocsp-response-file --stream-read-timeout --accesslog-syslog --frontend-http2-read-timeout --listener-disable-timeout --frontend-http2-connection-window-bits --ciphers --strip-incoming-x-forwarded-for --daemon --backend-keep-alive-timeout --backend-http-proxy-uri --backend-http1-connections-per-host --rlimit-nofile --no-via --ocsp-update-interval --backend-write-timeout --client --http2-no-cookie-crumbling --worker-read-burst --client-proxy --http2-bridge --accesslog-format --errorlog-syslog --errorlog-file --http2-max-concurrent-streams --frontend-write-timeout --read-burst --backend-ipv4 --backend-ipv6 --backend --insecure --log-level --tls-proto-list --backend-http2-connections-per-worker --dh-param-file --worker-frontend-connections --header-field-buffer --no-server-push --no-location-rewrite --no-ocsp --backend-response-buffer --workers --frontend-http2-window-bits --no-host-rewrite --worker-write-rate --backend-tls-sni-field --subcert --help --frontend-frame-debug --pid-file --frontend-http2-dump-request-header --private-key-passwd-file --write-rate --altsvc --user --add-x-forwarded-for --syslog-facility --frontend-read-timeout --backlog --write-burst --backend-http2-window-bits --padding --stream-write-timeout --cacert --version --verify-client --backend-read-timeout --frontend --accesslog-file --http2-proxy --max-header-fields --backend-no-tls --client-private-key-file --client-cert-file --add-response-header --read-rate ' -- "$cur" ) ) - ;; - *) - _filedir - return 0 - esac - return 0 -} -complete -F _nghttpx nghttpx diff --git a/doc/building-android-binary.rst.in b/doc/building-android-binary.rst.in deleted file mode 100644 index 2c77c98..0000000 --- a/doc/building-android-binary.rst.in +++ /dev/null @@ -1 +0,0 @@ -.. include:: @top_srcdir@/doc/sources/building-android-binary.rst diff --git a/doc/conf.py.in b/doc/conf.py.in deleted file mode 100644 index 0e572cf..0000000 --- a/doc/conf.py.in +++ /dev/null @@ -1,253 +0,0 @@ -# -*- coding: utf-8 -*- -# nghttp2 - HTTP/2 C Library - -# Copyright (c) 2012 Tatsuhiro Tsujikawa - -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: - -# The above copyright notice and this permission notice shall be -# included in all copies or substantial portions of the Software. - -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -# -# nghttp2 documentation build configuration file, created by -# sphinx-quickstart on Sun Mar 11 22:57:49 2012. -# -# This file is execfile()d with the current directory set to its containing dir. -# -# Note that not all possible configuration values are present in this -# autogenerated file. -# -# All configuration values have a default; values that are commented out -# serve to show the default. - -import sys, os - -# If extensions (or modules to document with autodoc) are in another directory, -# add these directories to sys.path here. If the directory is relative to the -# documentation root, use os.path.abspath to make it absolute, like shown here. -#sys.path.insert(0, os.path.abspath('.')) - -# -- General configuration ----------------------------------------------------- - -# If your documentation needs a minimal Sphinx version, state it here. -#needs_sphinx = '1.0' - -# Add any Sphinx extension module names here, as strings. They can be extensions -# coming with Sphinx (named 'sphinx.ext.*') or your custom ones. -extensions = [] - -# Add any paths that contain templates here, relative to this directory. -templates_path = ['@top_srcdir@/_templates'] - -# The suffix of source filenames. -source_suffix = '.rst' - -# The encoding of source files. -#source_encoding = 'utf-8-sig' - -# The master toctree document. -master_doc = 'index' - -# General information about the project. -project = u'nghttp2' -copyright = u'2012, 2015, Tatsuhiro Tsujikawa' - -# The version info for the project you're documenting, acts as replacement for -# |version| and |release|, also used in various other places throughout the -# built documents. -# -# The short X.Y version. -version = '@PACKAGE_VERSION@' -# The full version, including alpha/beta/rc tags. -release = '@PACKAGE_VERSION@' - -# The language for content autogenerated by Sphinx. Refer to documentation -# for a list of supported languages. -#language = None - -# There are two options for replacing |today|: either, you set today to some -# non-false value, then it is used: -#today = '' -# Else, today_fmt is used as the format for a strftime call. -#today_fmt = '%B %d, %Y' - -# List of patterns, relative to source directory, that match files and -# directories to ignore when looking for source files. -exclude_patterns = ['manual', 'README.rst', '*-header.rst', 'sources'] - -# The reST default role (used for this markup: `text`) to use for all documents. -default_role = 'c:func' -primary_domain = 'c' - -# If true, '()' will be appended to :func: etc. cross-reference text. -#add_function_parentheses = True - -# If true, the current module name will be prepended to all description -# unit titles (such as .. function::). -#add_module_names = True - -# If true, sectionauthor and moduleauthor directives will be shown in the -# output. They are ignored by default. -#show_authors = False - -# The default language to highlight source code in. The default is 'python'. -highlight_language = 'c' - -# The name of the Pygments (syntax highlighting) style to use. -pygments_style = 'sphinx' - -# A list of ignored prefixes for module index sorting. -#modindex_common_prefix = [] - - -# -- Options for HTML output --------------------------------------------------- - -# The theme to use for HTML and HTML Help pages. See the documentation for -# a list of builtin themes. -html_theme = 'sphinx_rtd_theme' - -# Theme options are theme-specific and customize the look and feel of a theme -# further. For a list of options available for each theme, see the -# documentation. -#html_theme_options = {} - -# Add any paths that contain custom themes here, relative to this directory. -html_theme_path = ['@top_srcdir@/doc/_themes'] - -# The name for this set of Sphinx documents. If None, it defaults to -# " v documentation". -#html_title = None - -# A shorter title for the navigation bar. Default is the same as html_title. -#html_short_title = None - -# The name of an image file (relative to this directory) to place at the top -# of the sidebar. -#html_logo = None - -# The name of an image file (within the static path) to use as favicon of the -# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 -# pixels large. -#html_favicon = None - -# Add any paths that contain custom static files (such as style sheets) here, -# relative to this directory. They are copied after the builtin static files, -# so a file named "default.css" will overwrite the builtin "default.css". -#html_static_path = [] - -# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, -# using the given strftime format. -#html_last_updated_fmt = '%b %d, %Y' - -# If true, SmartyPants will be used to convert quotes and dashes to -# typographically correct entities. -html_use_smartypants = False - -# Custom sidebar templates, maps document names to template names. -html_sidebars = { - '**': ['menu.html', 'localtoc.html', 'relations.html', 'sourcelink.html', - 'searchbox.html'] - } - -# Additional templates that should be rendered to pages, maps page names to -# template names. -#html_additional_pages = {} - -# If false, no module index is generated. -#html_domain_indices = True - -# If false, no index is generated. -#html_use_index = True - -# If true, the index is split into individual pages for each letter. -#html_split_index = False - -# If true, links to the reST sources are added to the pages. -html_show_sourcelink = False -html_copy_source = False - -# If true, "Created using Sphinx" is shown in the HTML footer. Default is True. -#html_show_sphinx = True - -# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. -#html_show_copyright = True - -# If true, an OpenSearch description file will be output, and all pages will -# contain a tag referring to it. The value of this option must be the -# base URL from which the finished HTML is served. -#html_use_opensearch = '' - -# This is the file name suffix for HTML files (e.g. ".xhtml"). -#html_file_suffix = None - -# Output file base name for HTML help builder. -htmlhelp_basename = 'nghttp2doc' - - -# -- Options for LaTeX output -------------------------------------------------- - -# The paper size ('letter' or 'a4'). -#latex_paper_size = 'letter' - -# The font size ('10pt', '11pt' or '12pt'). -#latex_font_size = '10pt' - -# Grouping the document tree into LaTeX files. List of tuples -# (source start file, target name, title, author, documentclass [howto/manual]). -latex_documents = [ - ('index', 'nghttp2.tex', u'nghttp2 Documentation', - u'Tatsuhiro Tsujikawa', 'manual'), -] - -# The name of an image file (relative to this directory) to place at the top of -# the title page. -#latex_logo = None - -# For "manual" documents, if this is true, then toplevel headings are parts, -# not chapters. -#latex_use_parts = False - -# If true, show page references after internal links. -#latex_show_pagerefs = False - -# If true, show URL addresses after external links. -#latex_show_urls = False - -# Additional stuff for the LaTeX preamble. -#latex_preamble = '' - -# Documents to append as an appendix to all manuals. -#latex_appendices = [] - -# If false, no module index is generated. -#latex_domain_indices = True - - -# -- Options for manual page output -------------------------------------------- - -# One entry per manual page. List of tuples -# (source start file, name, description, authors, manual section). -man_pages = [ - ('nghttp.1', 'nghttp', u'HTTP/2 experimental client', - [u'Tatsuhiro Tsujikawa'], 1), - ('nghttpd.1', 'nghttpd', u'HTTP/2 experimental server', - [u'Tatsuhiro Tsujikawa'], 1), - ('nghttpx.1', 'nghttpx', u'HTTP/2 experimental proxy', - [u'Tatsuhiro Tsujikawa'], 1), - ('h2load.1', 'h2load', u'HTTP/2 benchmarking tool', - [u'Tatsuhiro Tsujikawa'], 1) -] diff --git a/doc/contribute.rst.in b/doc/contribute.rst.in deleted file mode 100644 index 6a229d4..0000000 --- a/doc/contribute.rst.in +++ /dev/null @@ -1 +0,0 @@ -.. include:: @top_srcdir@/doc/sources/contribute.rst diff --git a/doc/enums.rst b/doc/enums.rst deleted file mode 100644 index 27041c9..0000000 --- a/doc/enums.rst +++ /dev/null @@ -1,446 +0,0 @@ - -Enums -===== -.. type:: nghttp2_error - - - Error codes used in this library. The code range is [-999, -500], - inclusive. The following values are defined: - - .. macro:: NGHTTP2_ERR_INVALID_ARGUMENT - - (``-501``) - Invalid argument passed. - .. macro:: NGHTTP2_ERR_BUFFER_ERROR - - (``-502``) - Out of buffer space. - .. macro:: NGHTTP2_ERR_UNSUPPORTED_VERSION - - (``-503``) - The specified protocol version is not supported. - .. macro:: NGHTTP2_ERR_WOULDBLOCK - - (``-504``) - Used as a return value from :type:`nghttp2_send_callback`, - :type:`nghttp2_recv_callback` and - :type:`nghttp2_send_data_callback` to indicate that the operation - would block. - .. macro:: NGHTTP2_ERR_PROTO - - (``-505``) - General protocol error - .. macro:: NGHTTP2_ERR_INVALID_FRAME - - (``-506``) - The frame is invalid. - .. macro:: NGHTTP2_ERR_EOF - - (``-507``) - The peer performed a shutdown on the connection. - .. macro:: NGHTTP2_ERR_DEFERRED - - (``-508``) - Used as a return value from - :func:`nghttp2_data_source_read_callback` to indicate that data - transfer is postponed. See - :func:`nghttp2_data_source_read_callback` for details. - .. macro:: NGHTTP2_ERR_STREAM_ID_NOT_AVAILABLE - - (``-509``) - Stream ID has reached the maximum value. Therefore no stream ID - is available. - .. macro:: NGHTTP2_ERR_STREAM_CLOSED - - (``-510``) - The stream is already closed; or the stream ID is invalid. - .. macro:: NGHTTP2_ERR_STREAM_CLOSING - - (``-511``) - RST_STREAM has been added to the outbound queue. The stream is - in closing state. - .. macro:: NGHTTP2_ERR_STREAM_SHUT_WR - - (``-512``) - The transmission is not allowed for this stream (e.g., a frame - with END_STREAM flag set has already sent). - .. macro:: NGHTTP2_ERR_INVALID_STREAM_ID - - (``-513``) - The stream ID is invalid. - .. macro:: NGHTTP2_ERR_INVALID_STREAM_STATE - - (``-514``) - The state of the stream is not valid (e.g., DATA cannot be sent - to the stream if response HEADERS has not been sent). - .. macro:: NGHTTP2_ERR_DEFERRED_DATA_EXIST - - (``-515``) - Another DATA frame has already been deferred. - .. macro:: NGHTTP2_ERR_START_STREAM_NOT_ALLOWED - - (``-516``) - Starting new stream is not allowed (e.g., GOAWAY has been sent - and/or received). - .. macro:: NGHTTP2_ERR_GOAWAY_ALREADY_SENT - - (``-517``) - GOAWAY has already been sent. - .. macro:: NGHTTP2_ERR_INVALID_HEADER_BLOCK - - (``-518``) - The received frame contains the invalid header block (e.g., There - are duplicate header names; or the header names are not encoded - in US-ASCII character set and not lower cased; or the header name - is zero-length string; or the header value contains multiple - in-sequence NUL bytes). - .. macro:: NGHTTP2_ERR_INVALID_STATE - - (``-519``) - Indicates that the context is not suitable to perform the - requested operation. - .. macro:: NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE - - (``-521``) - The user callback function failed due to the temporal error. - .. macro:: NGHTTP2_ERR_FRAME_SIZE_ERROR - - (``-522``) - The length of the frame is invalid, either too large or too small. - .. macro:: NGHTTP2_ERR_HEADER_COMP - - (``-523``) - Header block inflate/deflate error. - .. macro:: NGHTTP2_ERR_FLOW_CONTROL - - (``-524``) - Flow control error - .. macro:: NGHTTP2_ERR_INSUFF_BUFSIZE - - (``-525``) - Insufficient buffer size given to function. - .. macro:: NGHTTP2_ERR_PAUSE - - (``-526``) - Callback was paused by the application - .. macro:: NGHTTP2_ERR_TOO_MANY_INFLIGHT_SETTINGS - - (``-527``) - There are too many in-flight SETTING frame and no more - transmission of SETTINGS is allowed. - .. macro:: NGHTTP2_ERR_PUSH_DISABLED - - (``-528``) - The server push is disabled. - .. macro:: NGHTTP2_ERR_DATA_EXIST - - (``-529``) - DATA frame for a given stream has been already submitted and has - not been fully processed yet. - .. macro:: NGHTTP2_ERR_SESSION_CLOSING - - (``-530``) - The current session is closing due to a connection error or - `nghttp2_session_terminate_session()` is called. - .. macro:: NGHTTP2_ERR_HTTP_HEADER - - (``-531``) - Invalid HTTP header field was received and stream is going to be - closed. - .. macro:: NGHTTP2_ERR_HTTP_MESSAGING - - (``-532``) - Violation in HTTP messaging rule. - .. macro:: NGHTTP2_ERR_REFUSED_STREAM - - (``-533``) - Stream was refused. - .. macro:: NGHTTP2_ERR_INTERNAL - - (``-534``) - Unexpected internal error, but recovered. - .. macro:: NGHTTP2_ERR_FATAL - - (``-900``) - The errors < :macro:`NGHTTP2_ERR_FATAL` mean that the library is - under unexpected condition and processing was terminated (e.g., - out of memory). If application receives this error code, it must - stop using that :type:`nghttp2_session` object and only allowed - operation for that object is deallocate it using - `nghttp2_session_del()`. - .. macro:: NGHTTP2_ERR_NOMEM - - (``-901``) - Out of memory. This is a fatal error. - .. macro:: NGHTTP2_ERR_CALLBACK_FAILURE - - (``-902``) - The user callback function failed. This is a fatal error. - .. macro:: NGHTTP2_ERR_BAD_CLIENT_MAGIC - - (``-903``) - Invalid client magic (see :macro:`NGHTTP2_CLIENT_MAGIC`) was - received and further processing is not possible. - -.. type:: nghttp2_nv_flag - - - The flags for header field name/value pair. - - .. macro:: NGHTTP2_NV_FLAG_NONE - - (``0``) - No flag set. - .. macro:: NGHTTP2_NV_FLAG_NO_INDEX - - (``0x01``) - Indicates that this name/value pair must not be indexed ("Literal - Header Field never Indexed" representation must be used in HPACK - encoding). Other implementation calls this bit as "sensitive". - -.. type:: nghttp2_frame_type - - - The frame types in HTTP/2 specification. - - .. macro:: NGHTTP2_DATA - - (``0``) - The DATA frame. - .. macro:: NGHTTP2_HEADERS - - (``0x01``) - The HEADERS frame. - .. macro:: NGHTTP2_PRIORITY - - (``0x02``) - The PRIORITY frame. - .. macro:: NGHTTP2_RST_STREAM - - (``0x03``) - The RST_STREAM frame. - .. macro:: NGHTTP2_SETTINGS - - (``0x04``) - The SETTINGS frame. - .. macro:: NGHTTP2_PUSH_PROMISE - - (``0x05``) - The PUSH_PROMISE frame. - .. macro:: NGHTTP2_PING - - (``0x06``) - The PING frame. - .. macro:: NGHTTP2_GOAWAY - - (``0x07``) - The GOAWAY frame. - .. macro:: NGHTTP2_WINDOW_UPDATE - - (``0x08``) - The WINDOW_UPDATE frame. - .. macro:: NGHTTP2_CONTINUATION - - (``0x09``) - The CONTINUATION frame. This frame type won't be passed to any - callbacks because the library processes this frame type and its - preceding HEADERS/PUSH_PROMISE as a single frame. - -.. type:: nghttp2_flag - - - The flags for HTTP/2 frames. This enum defines all flags for all - frames. - - .. macro:: NGHTTP2_FLAG_NONE - - (``0``) - No flag set. - .. macro:: NGHTTP2_FLAG_END_STREAM - - (``0x01``) - The END_STREAM flag. - .. macro:: NGHTTP2_FLAG_END_HEADERS - - (``0x04``) - The END_HEADERS flag. - .. macro:: NGHTTP2_FLAG_ACK - - (``0x01``) - The ACK flag. - .. macro:: NGHTTP2_FLAG_PADDED - - (``0x08``) - The PADDED flag. - .. macro:: NGHTTP2_FLAG_PRIORITY - - (``0x20``) - The PRIORITY flag. - -.. type:: nghttp2_settings_id - - The SETTINGS ID. - - .. macro:: NGHTTP2_SETTINGS_HEADER_TABLE_SIZE - - (``0x01``) - SETTINGS_HEADER_TABLE_SIZE - .. macro:: NGHTTP2_SETTINGS_ENABLE_PUSH - - (``0x02``) - SETTINGS_ENABLE_PUSH - .. macro:: NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS - - (``0x03``) - SETTINGS_MAX_CONCURRENT_STREAMS - .. macro:: NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE - - (``0x04``) - SETTINGS_INITIAL_WINDOW_SIZE - .. macro:: NGHTTP2_SETTINGS_MAX_FRAME_SIZE - - (``0x05``) - SETTINGS_MAX_FRAME_SIZE - .. macro:: NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE - - (``0x06``) - SETTINGS_MAX_HEADER_LIST_SIZE - -.. type:: nghttp2_error_code - - The status codes for the RST_STREAM and GOAWAY frames. - - .. macro:: NGHTTP2_NO_ERROR - - (``0x00``) - No errors. - .. macro:: NGHTTP2_PROTOCOL_ERROR - - (``0x01``) - PROTOCOL_ERROR - .. macro:: NGHTTP2_INTERNAL_ERROR - - (``0x02``) - INTERNAL_ERROR - .. macro:: NGHTTP2_FLOW_CONTROL_ERROR - - (``0x03``) - FLOW_CONTROL_ERROR - .. macro:: NGHTTP2_SETTINGS_TIMEOUT - - (``0x04``) - SETTINGS_TIMEOUT - .. macro:: NGHTTP2_STREAM_CLOSED - - (``0x05``) - STREAM_CLOSED - .. macro:: NGHTTP2_FRAME_SIZE_ERROR - - (``0x06``) - FRAME_SIZE_ERROR - .. macro:: NGHTTP2_REFUSED_STREAM - - (``0x07``) - REFUSED_STREAM - .. macro:: NGHTTP2_CANCEL - - (``0x08``) - CANCEL - .. macro:: NGHTTP2_COMPRESSION_ERROR - - (``0x09``) - COMPRESSION_ERROR - .. macro:: NGHTTP2_CONNECT_ERROR - - (``0x0a``) - CONNECT_ERROR - .. macro:: NGHTTP2_ENHANCE_YOUR_CALM - - (``0x0b``) - ENHANCE_YOUR_CALM - .. macro:: NGHTTP2_INADEQUATE_SECURITY - - (``0x0c``) - INADEQUATE_SECURITY - .. macro:: NGHTTP2_HTTP_1_1_REQUIRED - - (``0x0d``) - HTTP_1_1_REQUIRED - -.. type:: nghttp2_data_flag - - - The flags used to set in *data_flags* output parameter in - :type:`nghttp2_data_source_read_callback`. - - .. macro:: NGHTTP2_DATA_FLAG_NONE - - (``0``) - No flag set. - .. macro:: NGHTTP2_DATA_FLAG_EOF - - (``0x01``) - Indicates EOF was sensed. - .. macro:: NGHTTP2_DATA_FLAG_NO_END_STREAM - - (``0x02``) - Indicates that END_STREAM flag must not be set even if - NGHTTP2_DATA_FLAG_EOF is set. Usually this flag is used to send - trailer header fields with `nghttp2_submit_request()` or - `nghttp2_submit_response()`. - .. macro:: NGHTTP2_DATA_FLAG_NO_COPY - - (``0x04``) - Indicates that application will send complete DATA frame in - :type:`nghttp2_send_data_callback`. - -.. type:: nghttp2_headers_category - - - The category of HEADERS, which indicates the role of the frame. In - HTTP/2 spec, request, response, push response and other arbitrary - headers (e.g., trailers) are all called just HEADERS. To give the - application the role of incoming HEADERS frame, we define several - categories. - - .. macro:: NGHTTP2_HCAT_REQUEST - - (``0``) - The HEADERS frame is opening new stream, which is analogous to - SYN_STREAM in SPDY. - .. macro:: NGHTTP2_HCAT_RESPONSE - - (``1``) - The HEADERS frame is the first response headers, which is - analogous to SYN_REPLY in SPDY. - .. macro:: NGHTTP2_HCAT_PUSH_RESPONSE - - (``2``) - The HEADERS frame is the first headers sent against reserved - stream. - .. macro:: NGHTTP2_HCAT_HEADERS - - (``3``) - The HEADERS frame which does not apply for the above categories, - which is analogous to HEADERS in SPDY. If non-final response - (e.g., status 1xx) is used, final response HEADERS frame will be - categorized here. - -.. type:: nghttp2_hd_inflate_flag - - - The flags for header inflation. - - .. macro:: NGHTTP2_HD_INFLATE_NONE - - (``0``) - No flag set. - .. macro:: NGHTTP2_HD_INFLATE_FINAL - - (``0x01``) - Indicates all headers were inflated. - .. macro:: NGHTTP2_HD_INFLATE_EMIT - - (``0x02``) - Indicates a header was emitted. - diff --git a/doc/h2load-howto.rst.in b/doc/h2load-howto.rst.in deleted file mode 100644 index 252069d..0000000 --- a/doc/h2load-howto.rst.in +++ /dev/null @@ -1 +0,0 @@ -.. include:: @top_srcdir@/doc/sources/h2load-howto.rst diff --git a/doc/h2load.1 b/doc/h2load.1 deleted file mode 100644 index beeb863..0000000 --- a/doc/h2load.1 +++ /dev/null @@ -1,273 +0,0 @@ -.\" Man page generated from reStructuredText. -. -.TH "H2LOAD" "1" "May 16, 2015" "1.0.0" "nghttp2" -.SH NAME -h2load \- HTTP/2 benchmarking tool -. -.nr rst2man-indent-level 0 -. -.de1 rstReportMargin -\\$1 \\n[an-margin] -level \\n[rst2man-indent-level] -level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] -- -\\n[rst2man-indent0] -\\n[rst2man-indent1] -\\n[rst2man-indent2] -.. -.de1 INDENT -.\" .rstReportMargin pre: -. RS \\$1 -. nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] -. nr rst2man-indent-level +1 -.\" .rstReportMargin post: -.. -.de UNINDENT -. RE -.\" indent \\n[an-margin] -.\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] -.nr rst2man-indent-level -1 -.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] -.in \\n[rst2man-indent\\n[rst2man-indent-level]]u -.. -.SH SYNOPSIS -.sp -\fBh2load\fP [OPTIONS]... [URI]... -.SH DESCRIPTION -.sp -benchmarking tool for HTTP/2 and SPDY server -.INDENT 0.0 -.TP -.B -Specify URI to access. Multiple URIs can be specified. -URIs are used in this order for each client. All URIs -are used, then first URI is used and then 2nd URI, and -so on. The scheme, host and port in the subsequent -URIs, if present, are ignored. Those in the first URI -are used solely. -.UNINDENT -.SH OPTIONS -.INDENT 0.0 -.TP -.B \-n, \-\-requests= -Number of requests. -.sp -Default: \fB1\fP -.UNINDENT -.INDENT 0.0 -.TP -.B \-c, \-\-clients= -Number of concurrent clients. -.sp -Default: \fB1\fP -.UNINDENT -.INDENT 0.0 -.TP -.B \-t, \-\-threads= -Number of native threads. -.sp -Default: \fB1\fP -.UNINDENT -.INDENT 0.0 -.TP -.B \-i, \-\-input\-file= -Path of a file with multiple URIs are separated by EOLs. -This option will disable URIs getting from command\-line. -If \(aq\-\(aq is given as , URIs will be read from stdin. -URIs are used in this order for each client. All URIs -are used, then first URI is used and then 2nd URI, and -so on. The scheme, host and port in the subsequent -URIs, if present, are ignored. Those in the first URI -are used solely. -.UNINDENT -.INDENT 0.0 -.TP -.B \-m, \-\-max\-concurrent\-streams=(auto|) -Max concurrent streams to issue per session. If "auto" -is given, the number of given URIs is used. -.sp -Default: \fBauto\fP -.UNINDENT -.INDENT 0.0 -.TP -.B \-w, \-\-window\-bits= -Sets the stream level initial window size to (2**)\-1. -For SPDY, 2** is used instead. -.sp -Default: \fB30\fP -.UNINDENT -.INDENT 0.0 -.TP -.B \-W, \-\-connection\-window\-bits= -Sets the connection level initial window size to -(2**)\-1. For SPDY, if is strictly less than 16, -this option is ignored. Otherwise 2** is used for -SPDY. -.sp -Default: \fB30\fP -.UNINDENT -.INDENT 0.0 -.TP -.B \-H, \-\-header=
      -Add/Override a header to the requests. -.UNINDENT -.INDENT 0.0 -.TP -.B \-p, \-\-no\-tls\-proto= -Specify ALPN identifier of the protocol to be used when -accessing http URI without SSL/TLS. -Available protocols: spdy/2, spdy/3, spdy/3.1 and h2c -.sp -Default: \fBh2c\fP -.UNINDENT -.INDENT 0.0 -.TP -.B \-d, \-\-data= -Post FILE to server. The request method is changed to -POST. -.UNINDENT -.INDENT 0.0 -.TP -.B \-v, \-\-verbose -Output debug information. -.UNINDENT -.INDENT 0.0 -.TP -.B \-\-version -Display version information and exit. -.UNINDENT -.INDENT 0.0 -.TP -.B \-h, \-\-help -Display this help and exit. -.UNINDENT -.SH OUTPUT -.INDENT 0.0 -.TP -.B requests -.INDENT 7.0 -.TP -.B total -The number of requests h2load was instructed to make. -.TP -.B started -The number of requests h2load has started. -.TP -.B done -The number of requests completed. -.TP -.B succeeded -The number of requests completed successfully. Only HTTP status -code 2xx or3xx are considered as success. -.TP -.B failed -The number of requests failed, including HTTP level failures -(non\-successful HTTP status code). -.TP -.B errored -The number of requests failed, except for HTTP level failures. -status code. This is the subset of the number reported in -\fBfailed\fP and most likely the network level failures or stream -was reset by RST_STREAM. -.UNINDENT -.TP -.B status codes -The number of status code h2load received. -.TP -.B traffic -.INDENT 7.0 -.TP -.B total -The number of bytes received from the server "on the wire". If -requests were made via TLS, this value is the number of decrpyted -bytes. -.TP -.B headers -The number of response header bytes from the server without -decompression. For HTTP/2, this is the sum of the payload of -HEADERS frame. For SPDY, this is the sum of the payload of -SYN_REPLY frame. -.TP -.B data -The number of response body bytes received from the server. -.UNINDENT -.TP -.B time for request -.INDENT 7.0 -.TP -.B min -The minimum time taken for request and response. -.TP -.B max -The maximum time taken for request and response. -.TP -.B mean -The mean time taken for request and response. -.TP -.B sd -The standard deviation of the time taken for request and response. -.TP -.B +/\- sd -The fraction of the number of requests within standard deviation -range (mean +/\- sd) against total number of successful requests. -.UNINDENT -.TP -.B time for connect -.INDENT 7.0 -.TP -.B min -The minimum time taken to connect to a server. -.TP -.B max -The maximum time taken to connect to a server. -.TP -.B mean -The mean time taken to connect to a server. -.TP -.B sd -The standard deviation of the time taken to connect to a server. -.TP -.B +/\- sd -The fraction of the number of connections within standard -deviation range (mean +/\- sd) against total number of successful -connections. -.UNINDENT -.TP -.B time for 1st byte (of (decrypted in case of TLS) application data) -.INDENT 7.0 -.TP -.B min -The minimum time taken to get 1st byte from a server. -.TP -.B max -The maximum time taken to get 1st byte from a server. -.TP -.B mean -The mean time taken to get 1st byte from a server. -.TP -.B sd -The standard deviation of the time taken to get 1st byte from a -server. -.TP -.B +/\- sd -The fraction of the number of connections within standard -deviation range (mean +/\- sd) against total number of successful -connections. -.UNINDENT -.UNINDENT -.SH FLOW CONTROL -.sp -h2load sets large flow control window by default, and effectively -disables flow control to avoid under utilization of server -performance. To set smaller flow control window, use \fI\%\-w\fP and -\fI\%\-W\fP options. For example, use \fB\-w16 \-W16\fP to set default -window size described in HTTP/2 and SPDY protocol specification. -.SH SEE ALSO -.sp -\fInghttp(1)\fP, \fInghttpd(1)\fP, \fInghttpx(1)\fP -.SH AUTHOR -Tatsuhiro Tsujikawa -.SH COPYRIGHT -2012, 2015, Tatsuhiro Tsujikawa -.\" Generated by docutils manpage writer. -. diff --git a/doc/h2load.1.rst b/doc/h2load.1.rst deleted file mode 100644 index 21e5a42..0000000 --- a/doc/h2load.1.rst +++ /dev/null @@ -1,204 +0,0 @@ - -.. GENERATED by help2rst.py. DO NOT EDIT DIRECTLY. - -.. program:: h2load - -h2load(1) -========= - -SYNOPSIS --------- - -**h2load** [OPTIONS]... [URI]... - -DESCRIPTION ------------ - -benchmarking tool for HTTP/2 and SPDY server - -.. describe:: - - Specify URI to access. Multiple URIs can be specified. - URIs are used in this order for each client. All URIs - are used, then first URI is used and then 2nd URI, and - so on. The scheme, host and port in the subsequent - URIs, if present, are ignored. Those in the first URI - are used solely. - -OPTIONS -------- - -.. option:: -n, --requests= - - Number of requests. - - Default: ``1`` - -.. option:: -c, --clients= - - Number of concurrent clients. - - Default: ``1`` - -.. option:: -t, --threads= - - Number of native threads. - - Default: ``1`` - -.. option:: -i, --input-file= - - Path of a file with multiple URIs are separated by EOLs. - This option will disable URIs getting from command-line. - If '-' is given as , URIs will be read from stdin. - URIs are used in this order for each client. All URIs - are used, then first URI is used and then 2nd URI, and - so on. The scheme, host and port in the subsequent - URIs, if present, are ignored. Those in the first URI - are used solely. - -.. option:: -m, --max-concurrent-streams=(auto|) - - Max concurrent streams to issue per session. If "auto" - is given, the number of given URIs is used. - - Default: ``auto`` - -.. option:: -w, --window-bits= - - Sets the stream level initial window size to (2\*\*)-1. - For SPDY, 2** is used instead. - - Default: ``30`` - -.. option:: -W, --connection-window-bits= - - Sets the connection level initial window size to - (2**)-1. For SPDY, if is strictly less than 16, - this option is ignored. Otherwise 2\*\* is used for - SPDY. - - Default: ``30`` - -.. option:: -H, --header=
      - - Add/Override a header to the requests. - -.. option:: -p, --no-tls-proto= - - Specify ALPN identifier of the protocol to be used when - accessing http URI without SSL/TLS. - Available protocols: spdy/2, spdy/3, spdy/3.1 and h2c - - Default: ``h2c`` - -.. option:: -d, --data= - - Post FILE to server. The request method is changed to - POST. - -.. option:: -v, --verbose - - Output debug information. - -.. option:: --version - - Display version information and exit. - -.. option:: -h, --help - - Display this help and exit. - -OUTPUT ------- - -requests - total - The number of requests h2load was instructed to make. - started - The number of requests h2load has started. - done - The number of requests completed. - succeeded - The number of requests completed successfully. Only HTTP status - code 2xx or3xx are considered as success. - failed - The number of requests failed, including HTTP level failures - (non-successful HTTP status code). - errored - The number of requests failed, except for HTTP level failures. - status code. This is the subset of the number reported in - ``failed`` and most likely the network level failures or stream - was reset by RST_STREAM. - -status codes - The number of status code h2load received. - -traffic - total - The number of bytes received from the server "on the wire". If - requests were made via TLS, this value is the number of decrpyted - bytes. - headers - The number of response header bytes from the server without - decompression. For HTTP/2, this is the sum of the payload of - HEADERS frame. For SPDY, this is the sum of the payload of - SYN_REPLY frame. - data - The number of response body bytes received from the server. - -time for request - min - The minimum time taken for request and response. - max - The maximum time taken for request and response. - mean - The mean time taken for request and response. - sd - The standard deviation of the time taken for request and response. - +/- sd - The fraction of the number of requests within standard deviation - range (mean +/- sd) against total number of successful requests. - -time for connect - min - The minimum time taken to connect to a server. - max - The maximum time taken to connect to a server. - mean - The mean time taken to connect to a server. - sd - The standard deviation of the time taken to connect to a server. - +/- sd - The fraction of the number of connections within standard - deviation range (mean +/- sd) against total number of successful - connections. - -time for 1st byte (of (decrypted in case of TLS) application data) - min - The minimum time taken to get 1st byte from a server. - max - The maximum time taken to get 1st byte from a server. - mean - The mean time taken to get 1st byte from a server. - sd - The standard deviation of the time taken to get 1st byte from a - server. - +/- sd - The fraction of the number of connections within standard - deviation range (mean +/- sd) against total number of successful - connections. - -FLOW CONTROL ------------- - -h2load sets large flow control window by default, and effectively -disables flow control to avoid under utilization of server -performance. To set smaller flow control window, use :option:`-w` and -:option:`-W` options. For example, use ``-w16 -W16`` to set default -window size described in HTTP/2 and SPDY protocol specification. - -SEE ALSO --------- - -:manpage:`nghttp(1)`, :manpage:`nghttpd(1)`, :manpage:`nghttpx(1)` diff --git a/doc/index.rst.in b/doc/index.rst.in deleted file mode 100644 index 2a49369..0000000 --- a/doc/index.rst.in +++ /dev/null @@ -1 +0,0 @@ -.. include:: @top_srcdir@/doc/sources/index.rst diff --git a/doc/libnghttp2_asio.rst.in b/doc/libnghttp2_asio.rst.in deleted file mode 100644 index 38254e1..0000000 --- a/doc/libnghttp2_asio.rst.in +++ /dev/null @@ -1 +0,0 @@ -.. include:: @top_srcdir@/doc/sources/libnghttp2_asio.rst diff --git a/doc/macros.rst b/doc/macros.rst deleted file mode 100644 index ee9ec33..0000000 --- a/doc/macros.rst +++ /dev/null @@ -1,86 +0,0 @@ - -Macros -====== -.. macro:: NGHTTP2_VERSION - - Version number of the nghttp2 library release -.. macro:: NGHTTP2_VERSION_NUM - - Numerical representation of the version number of the nghttp2 library - 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. -.. macro:: NGHTTP2_PROTO_VERSION_ID - - - The protocol version identification string of this library - supports. This identifier is used if HTTP/2 is used over TLS. -.. macro:: NGHTTP2_PROTO_VERSION_ID_LEN - - - The length of :macro:`NGHTTP2_PROTO_VERSION_ID`. -.. macro:: NGHTTP2_PROTO_ALPN - - - The seriazlied form of ALPN protocol identifier this library - supports. Notice that first byte is the length of following - protocol identifier. This is the same wire format of `TLS ALPN - extension `_. This is useful - to process incoming ALPN tokens in wire format. -.. macro:: NGHTTP2_PROTO_ALPN_LEN - - - The length of :macro:`NGHTTP2_PROTO_ALPN`. -.. macro:: NGHTTP2_CLEARTEXT_PROTO_VERSION_ID - - - The protocol version identification string of this library - supports. This identifier is used if HTTP/2 is used over cleartext - TCP. -.. macro:: NGHTTP2_CLEARTEXT_PROTO_VERSION_ID_LEN - - - The length of :macro:`NGHTTP2_CLEARTEXT_PROTO_VERSION_ID`. -.. macro:: NGHTTP2_VERSION_AGE - - - The age of :type:`nghttp2_info` -.. macro:: NGHTTP2_DEFAULT_WEIGHT - - - The default weight of stream dependency. -.. macro:: NGHTTP2_MAX_WEIGHT - - - The maximum weight of stream dependency. -.. macro:: NGHTTP2_MIN_WEIGHT - - - The minimum weight of stream dependency. -.. macro:: NGHTTP2_MAX_WINDOW_SIZE - - - The maximum window size -.. macro:: NGHTTP2_INITIAL_WINDOW_SIZE - - - The initial window size for stream level flow control. -.. macro:: NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE - - - The initial window size for connection level flow control. -.. macro:: NGHTTP2_DEFAULT_HEADER_TABLE_SIZE - - - The default header table size. -.. macro:: NGHTTP2_CLIENT_MAGIC - - - The client magic string, which is the first 24 bytes byte string of - client connection preface. -.. macro:: NGHTTP2_CLIENT_MAGIC_LEN - - - The length of :macro:`NGHTTP2_CLIENT_MAGIC`. -.. macro:: NGHTTP2_INITIAL_MAX_CONCURRENT_STREAMS - - Default maximum concurrent streams. diff --git a/doc/mkapiref.py b/doc/mkapiref.py deleted file mode 100755 index 23ab9cc..0000000 --- a/doc/mkapiref.py +++ /dev/null @@ -1,277 +0,0 @@ -#!/usr/bin/env python -# nghttp2 - HTTP/2 C Library - -# Copyright (c) 2012 Tatsuhiro Tsujikawa - -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: - -# The above copyright notice and this permission notice shall be -# included in all copies or substantial portions of the Software. - -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -# Generates API reference from C source code. -from __future__ import print_function # At least python 2.6 is required -import re, sys, argparse, os.path - -class FunctionDoc: - def __init__(self, name, content, domain): - self.name = name - self.content = content - self.domain = domain - if self.domain == 'function': - self.funcname = re.search(r'(nghttp2_[^ )]+)\(', self.name).group(1) - - def write(self, out): - out.write('.. {}:: {}\n'.format(self.domain, self.name)) - out.write('\n') - for line in self.content: - out.write(' {}\n'.format(line)) - -class StructDoc: - def __init__(self, name, content, members, member_domain): - self.name = name - self.content = content - self.members = members - self.member_domain = member_domain - - def write(self, out): - if self.name: - out.write('.. type:: {}\n'.format(self.name)) - out.write('\n') - for line in self.content: - out.write(' {}\n'.format(line)) - out.write('\n') - for name, content in self.members: - out.write(' .. {}:: {}\n'.format(self.member_domain, name)) - out.write('\n') - for line in content: - out.write(' {}\n'.format(line)) - out.write('\n') - -class MacroDoc: - def __init__(self, name, content): - self.name = name - self.content = content - - def write(self, out): - out.write('''.. macro:: {}\n'''.format(self.name)) - out.write('\n') - for line in self.content: - out.write(' {}\n'.format(line)) - -def make_api_ref(infiles): - macros = [] - enums = [] - types = [] - functions = [] - for infile in infiles: - while True: - line = infile.readline() - if not line: - break - elif line == '/**\n': - line = infile.readline() - doctype = line.split()[1] - if doctype == '@function': - functions.append(process_function('function', infile)) - elif doctype == '@functypedef': - types.append(process_function('type', infile)) - elif doctype == '@struct' or doctype == '@union': - types.append(process_struct(infile)) - elif doctype == '@enum': - enums.append(process_enum(infile)) - elif doctype == '@macro': - macros.append(process_macro(infile)) - return macros, enums, types, functions - - alldocs = [('Macros', macros), - ('Enums', enums), - ('Types (structs, unions and typedefs)', types), - ('Functions', functions)] - -def output( - indexfile, macrosfile, enumsfile, typesfile, funcsdir, - macros, enums, types, functions): - indexfile.write(''' -API Reference -============= - -.. toctree:: - :maxdepth: 1 - - macros - enums - types -''') - - for doc in functions: - indexfile.write(' {}\n'.format(doc.funcname)) - - macrosfile.write(''' -Macros -====== -''') - for doc in macros: - doc.write(macrosfile) - - enumsfile.write(''' -Enums -===== -''') - for doc in enums: - doc.write(enumsfile) - - typesfile.write(''' -Types (structs, unions and typedefs) -==================================== -''') - for doc in types: - doc.write(typesfile) - - for doc in functions: - with open(os.path.join(funcsdir, doc.funcname + '.rst'), 'w') as f: - f.write(''' -{funcname} -{secul} - -Synopsis --------- - -*#include * - -'''.format(funcname=doc.funcname, secul='='*len(doc.funcname))) - doc.write(f) - -def process_macro(infile): - content = read_content(infile) - line = infile.readline() - macro_name = line.split()[1] - return MacroDoc(macro_name, content) - -def process_enum(infile): - members = [] - enum_name = None - content = read_content(infile) - while True: - line = infile.readline() - if not line: - break - elif re.match(r'\s*/\*\*\n', line): - member_content = read_content(infile) - line = infile.readline() - items = line.split() - member_name = items[0] - if len(items) >= 3: - member_content.insert(0, '(``{}``) '\ - .format(' '.join(items[2:]).rstrip(','))) - members.append((member_name, member_content)) - elif line.startswith('}'): - enum_name = line.rstrip().split()[1] - enum_name = re.sub(r';$', '', enum_name) - break - return StructDoc(enum_name, content, members, 'macro') - -def process_struct(infile): - members = [] - struct_name = None - content = read_content(infile) - while True: - line = infile.readline() - if not line: - break - elif re.match(r'\s*/\*\*\n', line): - member_content = read_content(infile) - line = infile.readline() - member_name = line.rstrip().rstrip(';') - members.append((member_name, member_content)) - elif line.startswith('}') or\ - (line.startswith('typedef ') and line.endswith(';\n')): - if line.startswith('}'): - index = 1 - else: - index = 3 - struct_name = line.rstrip().split()[index] - struct_name = re.sub(r';$', '', struct_name) - break - return StructDoc(struct_name, content, members, 'member') - -def process_function(domain, infile): - content = read_content(infile) - func_proto = [] - while True: - line = infile.readline() - if not line: - break - elif line == '\n': - break - else: - func_proto.append(line) - func_proto = ''.join(func_proto) - func_proto = re.sub(r';\n$', '', func_proto) - func_proto = re.sub(r'\s+', ' ', func_proto) - func_proto = re.sub(r'NGHTTP2_EXTERN ', '', func_proto) - return FunctionDoc(func_proto, content, domain) - -def read_content(infile): - content = [] - while True: - line = infile.readline() - if not line: - break - if re.match(r'\s*\*/\n', line): - break - else: - content.append(transform_content(line.rstrip())) - return content - -def arg_repl(matchobj): - return '*{}*'.format(matchobj.group(1).replace('*', '\\*')) - -def transform_content(content): - content = re.sub(r'^\s+\* ?', '', content) - content = re.sub(r'\|([^\s|]+)\|', arg_repl, content) - content = re.sub(r':enum:', ':macro:', content) - return content - -if __name__ == '__main__': - parser = argparse.ArgumentParser(description="Generate API reference") - parser.add_argument('index', type=argparse.FileType('w'), - help='index output file') - parser.add_argument('macros', type=argparse.FileType('w'), - help='macros section output file. The filename should be macros.rst') - parser.add_argument('enums', type=argparse.FileType('w'), - help='enums section output file. The filename should be enums.rst') - parser.add_argument('types', type=argparse.FileType('w'), - help='types section output file. The filename should be types.rst') - parser.add_argument('funcsdir', - help='functions doc output dir') - parser.add_argument('files', nargs='+', type=argparse.FileType('r'), - help='source file') - args = parser.parse_args() - macros = [] - enums = [] - types = [] - funcs = [] - for infile in args.files: - m, e, t, f = make_api_ref(args.files) - macros.extend(m) - enums.extend(e) - types.extend(t) - funcs.extend(f) - funcs.sort(key=lambda x: x.funcname) - output( - args.index, args.macros, args.enums, args.types, args.funcsdir, - macros, enums, types, funcs) diff --git a/doc/nghttp.1 b/doc/nghttp.1 deleted file mode 100644 index c29360f..0000000 --- a/doc/nghttp.1 +++ /dev/null @@ -1,289 +0,0 @@ -.\" Man page generated from reStructuredText. -. -.TH "NGHTTP" "1" "May 16, 2015" "1.0.0" "nghttp2" -.SH NAME -nghttp \- HTTP/2 experimental client -. -.nr rst2man-indent-level 0 -. -.de1 rstReportMargin -\\$1 \\n[an-margin] -level \\n[rst2man-indent-level] -level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] -- -\\n[rst2man-indent0] -\\n[rst2man-indent1] -\\n[rst2man-indent2] -.. -.de1 INDENT -.\" .rstReportMargin pre: -. RS \\$1 -. nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] -. nr rst2man-indent-level +1 -.\" .rstReportMargin post: -.. -.de UNINDENT -. RE -.\" indent \\n[an-margin] -.\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] -.nr rst2man-indent-level -1 -.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] -.in \\n[rst2man-indent\\n[rst2man-indent-level]]u -.. -.SH SYNOPSIS -.sp -\fBnghttp\fP [OPTIONS]... ... -.SH DESCRIPTION -.sp -HTTP/2 experimental client -.INDENT 0.0 -.TP -.B -Specify URI to access. -.UNINDENT -.SH OPTIONS -.INDENT 0.0 -.TP -.B \-v, \-\-verbose -Print debug information such as reception and -transmission of frames and name/value pairs. Specifying -this option multiple times increases verbosity. -.UNINDENT -.INDENT 0.0 -.TP -.B \-n, \-\-null\-out -Discard downloaded data. -.UNINDENT -.INDENT 0.0 -.TP -.B \-O, \-\-remote\-name -Save download data in the current directory. The -filename is dereived from URI. If URI ends with \(aq\fI/\fP\(aq, -\(aqindex.html\(aq is used as a filename. Not implemented -yet. -.UNINDENT -.INDENT 0.0 -.TP -.B \-t, \-\-timeout= -Timeout each request after . Set 0 to disable -timeout. -.UNINDENT -.INDENT 0.0 -.TP -.B \-w, \-\-window\-bits= -Sets the stream level initial window size to 2**\-1. -.UNINDENT -.INDENT 0.0 -.TP -.B \-W, \-\-connection\-window\-bits= -Sets the connection level initial window size to -2**\-1. -.UNINDENT -.INDENT 0.0 -.TP -.B \-a, \-\-get\-assets -Download assets such as stylesheets, images and script -files linked from the downloaded resource. Only links -whose origins are the same with the linking resource -will be downloaded. nghttp prioritizes resources using -HTTP/2 dependency based priority. The priority order, -from highest to lowest, is html itself, css, javascript -and images. -.UNINDENT -.INDENT 0.0 -.TP -.B \-s, \-\-stat -Print statistics. -.UNINDENT -.INDENT 0.0 -.TP -.B \-H, \-\-header=
      -Add a header to the requests. Example: \fI\%\-H\fP\(aq:method: PUT\(aq -.UNINDENT -.INDENT 0.0 -.TP -.B \-\-trailer=
      -Add a trailer header to the requests.
      must not -include pseudo header field (header field name starting -with \(aq:\(aq). To send trailer, one must use \fI\%\-d\fP option to -send request body. Example: \fI\%\-\-trailer\fP \(aqfoo: bar\(aq. -.UNINDENT -.INDENT 0.0 -.TP -.B \-\-cert= -Use the specified client certificate file. The file -must be in PEM format. -.UNINDENT -.INDENT 0.0 -.TP -.B \-\-key= -Use the client private key file. The file must be in -PEM format. -.UNINDENT -.INDENT 0.0 -.TP -.B \-d, \-\-data= -Post FILE to server. If \(aq\-\(aq is given, data will be read -from stdin. -.UNINDENT -.INDENT 0.0 -.TP -.B \-m, \-\-multiply= -Request each URI times. By default, same URI is not -requested twice. This option disables it too. -.UNINDENT -.INDENT 0.0 -.TP -.B \-u, \-\-upgrade -Perform HTTP Upgrade for HTTP/2. This option is ignored -if the request URI has https scheme. If \fI\%\-d\fP is used, the -HTTP upgrade request is performed with OPTIONS method. -.UNINDENT -.INDENT 0.0 -.TP -.B \-p, \-\-weight= -Sets priority group weight. The valid value range is -[1, 256], inclusive. -.sp -Default: \fB16\fP -.UNINDENT -.INDENT 0.0 -.TP -.B \-M, \-\-peer\-max\-concurrent\-streams= -Use as SETTINGS_MAX_CONCURRENT_STREAMS value of -remote endpoint as if it is received in SETTINGS frame. -The default is large enough as it is seen as unlimited. -.UNINDENT -.INDENT 0.0 -.TP -.B \-c, \-\-header\-table\-size= -Specify decoder header table size. -.UNINDENT -.INDENT 0.0 -.TP -.B \-b, \-\-padding= -Add at most bytes to a frame payload as padding. -Specify 0 to disable padding. -.UNINDENT -.INDENT 0.0 -.TP -.B \-r, \-\-har= -Output HTTP transactions in HAR format. If \(aq\-\(aq -is given, data is written to stdout. -.UNINDENT -.INDENT 0.0 -.TP -.B \-\-color -Force colored log output. -.UNINDENT -.INDENT 0.0 -.TP -.B \-\-continuation -Send large header to test CONTINUATION. -.UNINDENT -.INDENT 0.0 -.TP -.B \-\-no\-content\-length -Don\(aqt send content\-length header field. -.UNINDENT -.INDENT 0.0 -.TP -.B \-\-no\-dep -Don\(aqt send dependency based priority hint to server. -.UNINDENT -.INDENT 0.0 -.TP -.B \-\-hexdump -Display the incoming traffic in hexadecimal (Canonical -hex+ASCII display). If SSL/TLS is used, decrypted data -are used. -.UNINDENT -.INDENT 0.0 -.TP -.B \-\-no\-push -Disable server push. -.UNINDENT -.INDENT 0.0 -.TP -.B \-\-version -Display version information and exit. -.UNINDENT -.INDENT 0.0 -.TP -.B \-h, \-\-help -Display this help and exit. -.UNINDENT -.sp -The argument is an integer and an optional unit (e.g., 10K is -10 * 1024). Units are K, M and G (powers of 1024). -.sp -The argument is an integer and an optional unit (e.g., 1s -is 1 second and 500ms is 500 milliseconds). Units are h, m, s or ms -(hours, minutes, seconds and milliseconds, respectively). If a unit -is omitted, a second is used as unit. -.SH DEPENDENCY BASED PRIORITY -.sp -nghttp sends priority hints to server by default unless -\fI\%\-\-no\-dep\fP is used. nghttp mimics the way Firefox employs to -manages dependency using idle streams. We follows the behaviour of -Firefox Nightly as of April, 2015, and nghttp\(aqs behaviour is very -static and could be different from Firefox in detail. But reproducing -the same behaviour of Firefox is not our goal. The goal is provide -the easy way to test out the dependency priority in server -implementation. -.sp -When connection is established, nghttp sends 5 PRIORITY frames to idle -streams 3, 5, 7, 9 and 11 to create "anchor" nodes in dependency -tree: -.INDENT 0.0 -.INDENT 3.5 -.sp -.nf -.ft C - +\-\-\-\-\-+ - |id=0 | - +\-\-\-\-\-+ - ^ ^ ^ - w=201 / | \e w=1 - / | \e - / w=101| \e - +\-\-\-\-\-+ +\-\-\-\-\-+ +\-\-\-\-\-+ - |id=3 | |id=5 | |id=7 | - +\-\-\-\-\-+ +\-\-\-\-\-+ +\-\-\-\-\-+ - ^ ^ -w=1 | w=1 | - | | - +\-\-\-\-\-+ +\-\-\-\-\-+ - |id=11| |id=9 | - +\-\-\-\-\-+ +\-\-\-\-\-+ -.ft P -.fi -.UNINDENT -.UNINDENT -.sp -In the above figure, \fBid\fP means stream ID, and \fBw\fP means weight. -The stream 0 is non\-existence stream, and forms the root of the tree. -The stream 7 and 9 are not used for now. -.sp -The URIs given in the command\-line depend on stream 11 with the weight -given in \fI\%\-p\fP option, which defaults to 16. -.sp -If \fI\%\-a\fP option is used, nghttp parses the resource pointed by -URI given in command\-line as html, and extracts resource links from -it. When requesting those resources, nghttp uses dependency according -to its resource type. -.sp -For CSS, and Javascript files inside "head" element, they depend on -stream 3 with the weight 2. The Javascript files outside "head" -element depend on stream 5 with the weight 2. The mages depend on -stream 11 with the weight 12. The other resources (e.g., icon) depend -on stream 11 with the weight 2. -.SH SEE ALSO -.sp -\fInghttpd(1)\fP, \fInghttpx(1)\fP, \fIh2load(1)\fP -.SH AUTHOR -Tatsuhiro Tsujikawa -.SH COPYRIGHT -2012, 2015, Tatsuhiro Tsujikawa -.\" Generated by docutils manpage writer. -. diff --git a/doc/nghttp.1.rst b/doc/nghttp.1.rst deleted file mode 100644 index 7f2449b..0000000 --- a/doc/nghttp.1.rst +++ /dev/null @@ -1,233 +0,0 @@ - -.. GENERATED by help2rst.py. DO NOT EDIT DIRECTLY. - -.. program:: nghttp - -nghttp(1) -========= - -SYNOPSIS --------- - -**nghttp** [OPTIONS]... ... - -DESCRIPTION ------------ - -HTTP/2 experimental client - -.. describe:: - - Specify URI to access. - -OPTIONS -------- - -.. option:: -v, --verbose - - Print debug information such as reception and - transmission of frames and name/value pairs. Specifying - this option multiple times increases verbosity. - -.. option:: -n, --null-out - - Discard downloaded data. - -.. option:: -O, --remote-name - - Save download data in the current directory. The - filename is dereived from URI. If URI ends with '*/*', - 'index.html' is used as a filename. Not implemented - yet. - -.. option:: -t, --timeout= - - Timeout each request after . Set 0 to disable - timeout. - -.. option:: -w, --window-bits= - - Sets the stream level initial window size to 2\*\*-1. - -.. option:: -W, --connection-window-bits= - - Sets the connection level initial window size to - 2\*\*-1. - -.. option:: -a, --get-assets - - Download assets such as stylesheets, images and script - files linked from the downloaded resource. Only links - whose origins are the same with the linking resource - will be downloaded. nghttp prioritizes resources using - HTTP/2 dependency based priority. The priority order, - from highest to lowest, is html itself, css, javascript - and images. - -.. option:: -s, --stat - - Print statistics. - -.. option:: -H, --header=
      - - Add a header to the requests. Example: :option:`-H`\':method: PUT' - -.. option:: --trailer=
      - - Add a trailer header to the requests.
      must not - include pseudo header field (header field name starting - with ':'). To send trailer, one must use :option:`-d` option to - send request body. Example: :option:`--trailer` 'foo: bar'. - -.. option:: --cert= - - Use the specified client certificate file. The file - must be in PEM format. - -.. option:: --key= - - Use the client private key file. The file must be in - PEM format. - -.. option:: -d, --data= - - Post FILE to server. If '-' is given, data will be read - from stdin. - -.. option:: -m, --multiply= - - Request each URI times. By default, same URI is not - requested twice. This option disables it too. - -.. option:: -u, --upgrade - - Perform HTTP Upgrade for HTTP/2. This option is ignored - if the request URI has https scheme. If :option:`-d` is used, the - HTTP upgrade request is performed with OPTIONS method. - -.. option:: -p, --weight= - - Sets priority group weight. The valid value range is - [1, 256], inclusive. - - Default: ``16`` - -.. option:: -M, --peer-max-concurrent-streams= - - Use as SETTINGS_MAX_CONCURRENT_STREAMS value of - remote endpoint as if it is received in SETTINGS frame. - The default is large enough as it is seen as unlimited. - -.. option:: -c, --header-table-size= - - Specify decoder header table size. - -.. option:: -b, --padding= - - Add at most bytes to a frame payload as padding. - Specify 0 to disable padding. - -.. option:: -r, --har= - - Output HTTP transactions in HAR format. If '-' - is given, data is written to stdout. - -.. option:: --color - - Force colored log output. - -.. option:: --continuation - - Send large header to test CONTINUATION. - -.. option:: --no-content-length - - Don't send content-length header field. - -.. option:: --no-dep - - Don't send dependency based priority hint to server. - -.. option:: --hexdump - - Display the incoming traffic in hexadecimal (Canonical - hex+ASCII display). If SSL/TLS is used, decrypted data - are used. - -.. option:: --no-push - - Disable server push. - -.. option:: --version - - Display version information and exit. - -.. option:: -h, --help - - Display this help and exit. - - - -The argument is an integer and an optional unit (e.g., 10K is -10 * 1024). Units are K, M and G (powers of 1024). - -The argument is an integer and an optional unit (e.g., 1s -is 1 second and 500ms is 500 milliseconds). Units are h, m, s or ms -(hours, minutes, seconds and milliseconds, respectively). If a unit -is omitted, a second is used as unit. - -DEPENDENCY BASED PRIORITY -------------------------- - -nghttp sends priority hints to server by default unless -:option:`--no-dep` is used. nghttp mimics the way Firefox employs to -manages dependency using idle streams. We follows the behaviour of -Firefox Nightly as of April, 2015, and nghttp's behaviour is very -static and could be different from Firefox in detail. But reproducing -the same behaviour of Firefox is not our goal. The goal is provide -the easy way to test out the dependency priority in server -implementation. - -When connection is established, nghttp sends 5 PRIORITY frames to idle -streams 3, 5, 7, 9 and 11 to create "anchor" nodes in dependency -tree:: - - +-----+ - |id=0 | - +-----+ - ^ ^ ^ - w=201 / | \ w=1 - / | \ - / w=101| \ - +-----+ +-----+ +-----+ - |id=3 | |id=5 | |id=7 | - +-----+ +-----+ +-----+ - ^ ^ - w=1 | w=1 | - | | - +-----+ +-----+ - |id=11| |id=9 | - +-----+ +-----+ - -In the above figure, ``id`` means stream ID, and ``w`` means weight. -The stream 0 is non-existence stream, and forms the root of the tree. -The stream 7 and 9 are not used for now. - -The URIs given in the command-line depend on stream 11 with the weight -given in :option:`-p` option, which defaults to 16. - -If :option:`-a` option is used, nghttp parses the resource pointed by -URI given in command-line as html, and extracts resource links from -it. When requesting those resources, nghttp uses dependency according -to its resource type. - -For CSS, and Javascript files inside "head" element, they depend on -stream 3 with the weight 2. The Javascript files outside "head" -element depend on stream 5 with the weight 2. The mages depend on -stream 11 with the weight 12. The other resources (e.g., icon) depend -on stream 11 with the weight 2. - -SEE ALSO --------- - -:manpage:`nghttpd(1)`, :manpage:`nghttpx(1)`, :manpage:`h2load(1)` diff --git a/doc/nghttp2.h.rst.in b/doc/nghttp2.h.rst.in deleted file mode 100644 index 29e641d..0000000 --- a/doc/nghttp2.h.rst.in +++ /dev/null @@ -1,4 +0,0 @@ -nghttp2.h -========= - -.. literalinclude:: @top_srcdir@/lib/includes/nghttp2/nghttp2.h diff --git a/doc/nghttp2_check_header_name.rst b/doc/nghttp2_check_header_name.rst deleted file mode 100644 index 2fba2c5..0000000 --- a/doc/nghttp2_check_header_name.rst +++ /dev/null @@ -1,17 +0,0 @@ - -nghttp2_check_header_name -========================= - -Synopsis --------- - -*#include * - -.. function:: int nghttp2_check_header_name(const uint8_t *name, size_t len) - - - Returns nonzero if HTTP header field name *name* of length *len* is - valid according to http://tools.ietf.org/html/rfc7230#section-3.2 - - Because this is a header field name in HTTP2, the upper cased alphabet - is treated as error. diff --git a/doc/nghttp2_check_header_value.rst b/doc/nghttp2_check_header_value.rst deleted file mode 100644 index e6f792e..0000000 --- a/doc/nghttp2_check_header_value.rst +++ /dev/null @@ -1,15 +0,0 @@ - -nghttp2_check_header_value -========================== - -Synopsis --------- - -*#include * - -.. function:: int nghttp2_check_header_value(const uint8_t *value, size_t len) - - - Returns nonzero if HTTP header field value *value* of length *len* - is valid according to - http://tools.ietf.org/html/rfc7230#section-3.2 diff --git a/doc/nghttp2_hd_deflate_bound.rst b/doc/nghttp2_hd_deflate_bound.rst deleted file mode 100644 index 27eaed3..0000000 --- a/doc/nghttp2_hd_deflate_bound.rst +++ /dev/null @@ -1,14 +0,0 @@ - -nghttp2_hd_deflate_bound -======================== - -Synopsis --------- - -*#include * - -.. function:: size_t nghttp2_hd_deflate_bound(nghttp2_hd_deflater *deflater, const nghttp2_nv *nva, size_t nvlen) - - - Returns an upper bound on the compressed size after deflation of - *nva* of length *nvlen*. diff --git a/doc/nghttp2_hd_deflate_change_table_size.rst b/doc/nghttp2_hd_deflate_change_table_size.rst deleted file mode 100644 index d0bec79..0000000 --- a/doc/nghttp2_hd_deflate_change_table_size.rst +++ /dev/null @@ -1,31 +0,0 @@ - -nghttp2_hd_deflate_change_table_size -==================================== - -Synopsis --------- - -*#include * - -.. function:: int nghttp2_hd_deflate_change_table_size(nghttp2_hd_deflater *deflater, size_t settings_hd_table_bufsize_max) - - - Changes header table size of the *deflater* to - *settings_hd_table_bufsize_max* bytes. This may trigger eviction - in the dynamic table. - - The *settings_hd_table_bufsize_max* should be the value received in - SETTINGS_HEADER_TABLE_SIZE. - - The deflater never uses more memory than - ``deflate_hd_table_bufsize_max`` bytes specified in - `nghttp2_hd_deflate_new()`. Therefore, if - *settings_hd_table_bufsize_max* > ``deflate_hd_table_bufsize_max``, - resulting maximum table size becomes - ``deflate_hd_table_bufsize_max``. - - This function returns 0 if it succeeds, or one of the following - negative error codes: - - :macro:`NGHTTP2_ERR_NOMEM` - Out of memory. diff --git a/doc/nghttp2_hd_deflate_del.rst b/doc/nghttp2_hd_deflate_del.rst deleted file mode 100644 index ea81a35..0000000 --- a/doc/nghttp2_hd_deflate_del.rst +++ /dev/null @@ -1,13 +0,0 @@ - -nghttp2_hd_deflate_del -====================== - -Synopsis --------- - -*#include * - -.. function:: void nghttp2_hd_deflate_del(nghttp2_hd_deflater *deflater) - - - Deallocates any resources allocated for *deflater*. diff --git a/doc/nghttp2_hd_deflate_hd.rst b/doc/nghttp2_hd_deflate_hd.rst deleted file mode 100644 index 4c80bfc..0000000 --- a/doc/nghttp2_hd_deflate_hd.rst +++ /dev/null @@ -1,35 +0,0 @@ - -nghttp2_hd_deflate_hd -===================== - -Synopsis --------- - -*#include * - -.. function:: ssize_t nghttp2_hd_deflate_hd(nghttp2_hd_deflater *deflater, uint8_t *buf, size_t buflen, const nghttp2_nv *nva, size_t nvlen) - - - Deflates the *nva*, which has the *nvlen* name/value pairs, into - the *buf* of length *buflen*. - - If *buf* is not large enough to store the deflated header block, - this function fails with :macro:`NGHTTP2_ERR_INSUFF_BUFSIZE`. The - caller should use `nghttp2_hd_deflate_bound()` to know the upper - bound of buffer size required to deflate given header name/value - pairs. - - Once this function fails, subsequent call of this function always - returns :macro:`NGHTTP2_ERR_HEADER_COMP`. - - After this function returns, it is safe to delete the *nva*. - - This function returns 0 if it succeeds, or one of the following - negative error codes: - - :macro:`NGHTTP2_ERR_NOMEM` - Out of memory. - :macro:`NGHTTP2_ERR_HEADER_COMP` - Deflation process has failed. - :macro:`NGHTTP2_ERR_INSUFF_BUFSIZE` - The provided *buflen* size is too small to hold the output. diff --git a/doc/nghttp2_hd_deflate_new.rst b/doc/nghttp2_hd_deflate_new.rst deleted file mode 100644 index 146113f..0000000 --- a/doc/nghttp2_hd_deflate_new.rst +++ /dev/null @@ -1,24 +0,0 @@ - -nghttp2_hd_deflate_new -====================== - -Synopsis --------- - -*#include * - -.. function:: int nghttp2_hd_deflate_new(nghttp2_hd_deflater **deflater_ptr, size_t deflate_hd_table_bufsize_max) - - - Initializes *\*deflater_ptr* for deflating name/values pairs. - - The *deflate_hd_table_bufsize_max* is the upper bound of header - table size the deflater will use. - - If this function fails, *\*deflater_ptr* is left untouched. - - This function returns 0 if it succeeds, or one of the following - negative error codes: - - :macro:`NGHTTP2_ERR_NOMEM` - Out of memory. diff --git a/doc/nghttp2_hd_deflate_new2.rst b/doc/nghttp2_hd_deflate_new2.rst deleted file mode 100644 index f128f3e..0000000 --- a/doc/nghttp2_hd_deflate_new2.rst +++ /dev/null @@ -1,23 +0,0 @@ - -nghttp2_hd_deflate_new2 -======================= - -Synopsis --------- - -*#include * - -.. function:: int nghttp2_hd_deflate_new2(nghttp2_hd_deflater **deflater_ptr, size_t deflate_hd_table_bufsize_max, nghttp2_mem *mem) - - - Like `nghttp2_hd_deflate_new()`, but with additional custom memory - allocator specified in the *mem*. - - The *mem* can be ``NULL`` and the call is equivalent to - `nghttp2_hd_deflate_new()`. - - This function does not take ownership *mem*. The application is - responsible for freeing *mem*. - - The library code does not refer to *mem* pointer after this - function returns, so the application can safely free it. diff --git a/doc/nghttp2_hd_inflate_change_table_size.rst b/doc/nghttp2_hd_inflate_change_table_size.rst deleted file mode 100644 index e859207..0000000 --- a/doc/nghttp2_hd_inflate_change_table_size.rst +++ /dev/null @@ -1,23 +0,0 @@ - -nghttp2_hd_inflate_change_table_size -==================================== - -Synopsis --------- - -*#include * - -.. function:: int nghttp2_hd_inflate_change_table_size(nghttp2_hd_inflater *inflater, size_t settings_hd_table_bufsize_max) - - - Changes header table size in the *inflater*. This may trigger - eviction in the dynamic table. - - The *settings_hd_table_bufsize_max* should be the value transmitted - in SETTINGS_HEADER_TABLE_SIZE. - - This function returns 0 if it succeeds, or one of the following - negative error codes: - - :macro:`NGHTTP2_ERR_NOMEM` - Out of memory. diff --git a/doc/nghttp2_hd_inflate_del.rst b/doc/nghttp2_hd_inflate_del.rst deleted file mode 100644 index bdf51bb..0000000 --- a/doc/nghttp2_hd_inflate_del.rst +++ /dev/null @@ -1,13 +0,0 @@ - -nghttp2_hd_inflate_del -====================== - -Synopsis --------- - -*#include * - -.. function:: void nghttp2_hd_inflate_del(nghttp2_hd_inflater *inflater) - - - Deallocates any resources allocated for *inflater*. diff --git a/doc/nghttp2_hd_inflate_end_headers.rst b/doc/nghttp2_hd_inflate_end_headers.rst deleted file mode 100644 index 63ea728..0000000 --- a/doc/nghttp2_hd_inflate_end_headers.rst +++ /dev/null @@ -1,16 +0,0 @@ - -nghttp2_hd_inflate_end_headers -============================== - -Synopsis --------- - -*#include * - -.. function:: int nghttp2_hd_inflate_end_headers(nghttp2_hd_inflater *inflater) - - - Signals the end of decompression for one header block. - - This function returns 0 if it succeeds. Currently this function - always succeeds. diff --git a/doc/nghttp2_hd_inflate_hd.rst b/doc/nghttp2_hd_inflate_hd.rst deleted file mode 100644 index 216cdfe..0000000 --- a/doc/nghttp2_hd_inflate_hd.rst +++ /dev/null @@ -1,85 +0,0 @@ - -nghttp2_hd_inflate_hd -===================== - -Synopsis --------- - -*#include * - -.. function:: ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_inflater *inflater, nghttp2_nv *nv_out, int *inflate_flags, uint8_t *in, size_t inlen, int in_final) - - - Inflates name/value block stored in *in* with length *inlen*. This - function performs decompression. For each successful emission of - header name/value pair, :macro:`NGHTTP2_HD_INFLATE_EMIT` is set in - *\*inflate_flags* and name/value pair is assigned to the *nv_out* - and the function returns. The caller must not free the members of - *nv_out*. - - The *nv_out* may include pointers to the memory region in the *in*. - The caller must retain the *in* while the *nv_out* is used. - - The application should call this function repeatedly until the - ``(*inflate_flags) & NGHTTP2_HD_INFLATE_FINAL`` is nonzero and - return value is non-negative. This means the all input values are - processed successfully. Then the application must call - `nghttp2_hd_inflate_end_headers()` to prepare for the next header - block input. - - The caller can feed complete compressed header block. It also can - feed it in several chunks. The caller must set *in_final* to - nonzero if the given input is the last block of the compressed - header. - - This function returns the number of bytes processed if it succeeds, - or one of the following negative error codes: - - :macro:`NGHTTP2_ERR_NOMEM` - Out of memory. - :macro:`NGHTTP2_ERR_HEADER_COMP` - Inflation process has failed. - :macro:`NGHTTP2_ERR_BUFFER_ERROR` - The heder field name or value is too large. - - Example follows:: - - int inflate_header_block(nghttp2_hd_inflater *hd_inflater, - uint8_t *in, size_t inlen, int final) - { - ssize_t rv; - - for(;;) { - nghttp2_nv nv; - int inflate_flags = 0; - - rv = nghttp2_hd_inflate_hd(hd_inflater, &nv, &inflate_flags, - in, inlen, final); - - if(rv < 0) { - fprintf(stderr, "inflate failed with error code %zd", rv); - return -1; - } - - in += rv; - inlen -= rv; - - if(inflate_flags & NGHTTP2_HD_INFLATE_EMIT) { - fwrite(nv.name, nv.namelen, 1, stderr); - fprintf(stderr, ": "); - fwrite(nv.value, nv.valuelen, 1, stderr); - fprintf(stderr, "\n"); - } - if(inflate_flags & NGHTTP2_HD_INFLATE_FINAL) { - nghttp2_hd_inflate_end_headers(hd_inflater); - break; - } - if((inflate_flags & NGHTTP2_HD_INFLATE_EMIT) == 0 && - inlen == 0) { - break; - } - } - - return 0; - } - diff --git a/doc/nghttp2_hd_inflate_new.rst b/doc/nghttp2_hd_inflate_new.rst deleted file mode 100644 index cfd2212..0000000 --- a/doc/nghttp2_hd_inflate_new.rst +++ /dev/null @@ -1,21 +0,0 @@ - -nghttp2_hd_inflate_new -====================== - -Synopsis --------- - -*#include * - -.. function:: int nghttp2_hd_inflate_new(nghttp2_hd_inflater **inflater_ptr) - - - Initializes *\*inflater_ptr* for inflating name/values pairs. - - If this function fails, *\*inflater_ptr* is left untouched. - - This function returns 0 if it succeeds, or one of the following - negative error codes: - - :macro:`NGHTTP2_ERR_NOMEM` - Out of memory. diff --git a/doc/nghttp2_hd_inflate_new2.rst b/doc/nghttp2_hd_inflate_new2.rst deleted file mode 100644 index f052019..0000000 --- a/doc/nghttp2_hd_inflate_new2.rst +++ /dev/null @@ -1,23 +0,0 @@ - -nghttp2_hd_inflate_new2 -======================= - -Synopsis --------- - -*#include * - -.. function:: int nghttp2_hd_inflate_new2(nghttp2_hd_inflater **inflater_ptr, nghttp2_mem *mem) - - - Like `nghttp2_hd_inflate_new()`, but with additional custom memory - allocator specified in the *mem*. - - The *mem* can be ``NULL`` and the call is equivalent to - `nghttp2_hd_inflate_new()`. - - This function does not take ownership *mem*. The application is - responsible for freeing *mem*. - - The library code does not refer to *mem* pointer after this - function returns, so the application can safely free it. diff --git a/doc/nghttp2_is_fatal.rst b/doc/nghttp2_is_fatal.rst deleted file mode 100644 index b548219..0000000 --- a/doc/nghttp2_is_fatal.rst +++ /dev/null @@ -1,14 +0,0 @@ - -nghttp2_is_fatal -================ - -Synopsis --------- - -*#include * - -.. function:: int nghttp2_is_fatal(int lib_error_code) - - - Returns nonzero if the :type:`nghttp2_error` library error code - *lib_error* is fatal. diff --git a/doc/nghttp2_nv_compare_name.rst b/doc/nghttp2_nv_compare_name.rst deleted file mode 100644 index 9fa4c1b..0000000 --- a/doc/nghttp2_nv_compare_name.rst +++ /dev/null @@ -1,17 +0,0 @@ - -nghttp2_nv_compare_name -======================= - -Synopsis --------- - -*#include * - -.. function:: int nghttp2_nv_compare_name(const nghttp2_nv *lhs, const nghttp2_nv *rhs) - - - Compares ``lhs->name`` of length ``lhs->namelen`` bytes and - ``rhs->name`` of length ``rhs->namelen`` bytes. Returns negative - integer if ``lhs->name`` is found to be less than ``rhs->name``; or - returns positive integer if ``lhs->name`` is found to be greater - than ``rhs->name``; or returns 0 otherwise. diff --git a/doc/nghttp2_option_del.rst b/doc/nghttp2_option_del.rst deleted file mode 100644 index b816b71..0000000 --- a/doc/nghttp2_option_del.rst +++ /dev/null @@ -1,14 +0,0 @@ - -nghttp2_option_del -================== - -Synopsis --------- - -*#include * - -.. function:: void nghttp2_option_del(nghttp2_option *option) - - - Frees any resources allocated for *option*. If *option* is - ``NULL``, this function does nothing. diff --git a/doc/nghttp2_option_new.rst b/doc/nghttp2_option_new.rst deleted file mode 100644 index 4dcfb64..0000000 --- a/doc/nghttp2_option_new.rst +++ /dev/null @@ -1,22 +0,0 @@ - -nghttp2_option_new -================== - -Synopsis --------- - -*#include * - -.. function:: int nghttp2_option_new(nghttp2_option **option_ptr) - - - Initializes *\*option_ptr* with default values. - - When the application finished using this object, it can use - `nghttp2_option_del()` to free its memory. - - This function returns 0 if it succeeds, or one of the following - negative error codes: - - :macro:`NGHTTP2_ERR_NOMEM` - Out of memory. diff --git a/doc/nghttp2_option_set_no_auto_window_update.rst b/doc/nghttp2_option_set_no_auto_window_update.rst deleted file mode 100644 index 737b6ce..0000000 --- a/doc/nghttp2_option_set_no_auto_window_update.rst +++ /dev/null @@ -1,18 +0,0 @@ - -nghttp2_option_set_no_auto_window_update -======================================== - -Synopsis --------- - -*#include * - -.. function:: void nghttp2_option_set_no_auto_window_update(nghttp2_option *option, int val) - - - This option prevents the library from sending WINDOW_UPDATE for a - connection automatically. If this option is set to nonzero, the - library won't send WINDOW_UPDATE for DATA until application calls - `nghttp2_session_consume()` to indicate the consumed amount of - data. Don't use `nghttp2_submit_window_update()` for this purpose. - By default, this option is set to zero. diff --git a/doc/nghttp2_option_set_no_http_messaging.rst b/doc/nghttp2_option_set_no_http_messaging.rst deleted file mode 100644 index 602fe3b..0000000 --- a/doc/nghttp2_option_set_no_http_messaging.rst +++ /dev/null @@ -1,18 +0,0 @@ - -nghttp2_option_set_no_http_messaging -==================================== - -Synopsis --------- - -*#include * - -.. function:: void nghttp2_option_set_no_http_messaging(nghttp2_option *option, int val) - - - By default, nghttp2 library enforces subset of HTTP Messaging rules - described in `HTTP/2 specification, section 8 - `_. See - :ref:`http-messaging` section for details. For those applications - who use nghttp2 library as non-HTTP use, give nonzero to *val* to - disable this enforcement. diff --git a/doc/nghttp2_option_set_no_recv_client_magic.rst b/doc/nghttp2_option_set_no_recv_client_magic.rst deleted file mode 100644 index e45fe54..0000000 --- a/doc/nghttp2_option_set_no_recv_client_magic.rst +++ /dev/null @@ -1,26 +0,0 @@ - -nghttp2_option_set_no_recv_client_magic -======================================= - -Synopsis --------- - -*#include * - -.. function:: void nghttp2_option_set_no_recv_client_magic(nghttp2_option *option, int val) - - - By default, nghttp2 library, if configured as server, requires - first 24 bytes of client magic byte string (MAGIC). In most cases, - this will simplify the implementation of server. But sometimes - server may want to detect the application protocol based on first - few bytes on clear text communication. - - If this option is used with nonzero *val*, nghttp2 library does not - handle MAGIC. It still checks following SETTINGS frame. This - means that applications should deal with MAGIC by themselves. - - If this option is not used or used with zero value, if MAGIC does - not match :macro:`NGHTTP2_CLIENT_MAGIC`, `nghttp2_session_recv()` - and `nghttp2_session_mem_recv()` will return error - :macro:`NGHTTP2_ERR_BAD_CLIENT_MAGIC`, which is fatal error. diff --git a/doc/nghttp2_option_set_peer_max_concurrent_streams.rst b/doc/nghttp2_option_set_peer_max_concurrent_streams.rst deleted file mode 100644 index b15fae3..0000000 --- a/doc/nghttp2_option_set_peer_max_concurrent_streams.rst +++ /dev/null @@ -1,23 +0,0 @@ - -nghttp2_option_set_peer_max_concurrent_streams -============================================== - -Synopsis --------- - -*#include * - -.. function:: void nghttp2_option_set_peer_max_concurrent_streams(nghttp2_option *option, uint32_t val) - - - This option sets the SETTINGS_MAX_CONCURRENT_STREAMS value of - remote endpoint as if it is received in SETTINGS frame. Without - specifying this option, before the local endpoint receives - SETTINGS_MAX_CONCURRENT_STREAMS in SETTINGS frame from remote - endpoint, SETTINGS_MAX_CONCURRENT_STREAMS is unlimited. This may - cause problem if local endpoint submits lots of requests initially - and sending them at once to the remote peer may lead to the - rejection of some requests. Specifying this option to the sensible - value, say 100, may avoid this kind of issue. This value will be - overwritten if the local endpoint receives - SETTINGS_MAX_CONCURRENT_STREAMS from the remote endpoint. diff --git a/doc/nghttp2_pack_settings_payload.rst b/doc/nghttp2_pack_settings_payload.rst deleted file mode 100644 index a4ce9fd..0000000 --- a/doc/nghttp2_pack_settings_payload.rst +++ /dev/null @@ -1,29 +0,0 @@ - -nghttp2_pack_settings_payload -============================= - -Synopsis --------- - -*#include * - -.. function:: ssize_t nghttp2_pack_settings_payload(uint8_t *buf, size_t buflen, const nghttp2_settings_entry *iv, size_t niv) - - - Serializes the SETTINGS values *iv* in the *buf*. The size of the - *buf* is specified by *buflen*. The number of entries in the *iv* - array is given by *niv*. The required space in *buf* for the *niv* - entries is ``8*niv`` bytes and if the given buffer is too small, an - error is returned. This function is used mainly for creating a - SETTINGS payload to be sent with the ``HTTP2-Settings`` header - field in an HTTP Upgrade request. The data written in *buf* is NOT - base64url encoded and the application is responsible for encoding. - - This function returns the number of bytes written in *buf*, or one - of the following negative error codes: - - :macro:`NGHTTP2_ERR_INVALID_ARGUMENT` - The *iv* contains duplicate settings ID or invalid value. - - :macro:`NGHTTP2_ERR_INSUFF_BUFSIZE` - The provided *buflen* size is too small to hold the output. diff --git a/doc/nghttp2_priority_spec_check_default.rst b/doc/nghttp2_priority_spec_check_default.rst deleted file mode 100644 index 12a2765..0000000 --- a/doc/nghttp2_priority_spec_check_default.rst +++ /dev/null @@ -1,13 +0,0 @@ - -nghttp2_priority_spec_check_default -=================================== - -Synopsis --------- - -*#include * - -.. function:: int nghttp2_priority_spec_check_default(const nghttp2_priority_spec *pri_spec) - - - Returns nonzero if the *pri_spec* is filled with default values. diff --git a/doc/nghttp2_priority_spec_default_init.rst b/doc/nghttp2_priority_spec_default_init.rst deleted file mode 100644 index 66f03a3..0000000 --- a/doc/nghttp2_priority_spec_default_init.rst +++ /dev/null @@ -1,15 +0,0 @@ - -nghttp2_priority_spec_default_init -================================== - -Synopsis --------- - -*#include * - -.. function:: void nghttp2_priority_spec_default_init(nghttp2_priority_spec *pri_spec) - - - Initializes *pri_spec* with the default values. The default values - are: stream_id = 0, weight = :macro:`NGHTTP2_DEFAULT_WEIGHT` and - exclusive = 0. diff --git a/doc/nghttp2_priority_spec_init.rst b/doc/nghttp2_priority_spec_init.rst deleted file mode 100644 index e6d3757..0000000 --- a/doc/nghttp2_priority_spec_init.rst +++ /dev/null @@ -1,18 +0,0 @@ - -nghttp2_priority_spec_init -========================== - -Synopsis --------- - -*#include * - -.. function:: void nghttp2_priority_spec_init(nghttp2_priority_spec *pri_spec, int32_t stream_id, int32_t weight, int exclusive) - - - Initializes *pri_spec* with the *stream_id* of the stream to depend - on with *weight* and its exclusive flag. If *exclusive* is - nonzero, exclusive flag is set. - - The *weight* must be in [:macro:`NGHTTP2_MIN_WEIGHT`, - :macro:`NGHTTP2_MAX_WEIGHT`], inclusive. diff --git a/doc/nghttp2_select_next_protocol.rst b/doc/nghttp2_select_next_protocol.rst deleted file mode 100644 index 5ace810..0000000 --- a/doc/nghttp2_select_next_protocol.rst +++ /dev/null @@ -1,66 +0,0 @@ - -nghttp2_select_next_protocol -============================ - -Synopsis --------- - -*#include * - -.. function:: int nghttp2_select_next_protocol(unsigned char **out, unsigned char *outlen, const unsigned char *in, unsigned int inlen) - - - A helper function for dealing with NPN in client side or ALPN in - server side. The *in* contains peer's protocol list in preferable - order. The format of *in* is length-prefixed and not - null-terminated. For example, ``h2`` and - ``http/1.1`` stored in *in* like this:: - - in[0] = 2 - in[1..2] = "h2" - in[3] = 8 - in[4..11] = "http/1.1" - inlen = 12 - - The selection algorithm is as follows: - - 1. If peer's list contains HTTP/2 protocol the library supports, - it is selected and returns 1. The following step is not taken. - - 2. If peer's list contains ``http/1.1``, this function selects - ``http/1.1`` and returns 0. The following step is not taken. - - 3. This function selects nothing and returns -1 (So called - non-overlap case). In this case, *out* and *outlen* are left - untouched. - - Selecting ``h2`` means that ``h2`` is written into *\*out* and its - length (which is 2) is assigned to *\*outlen*. - - For ALPN, refer to https://tools.ietf.org/html/rfc7301 - - See http://technotes.googlecode.com/git/nextprotoneg.html for more - details about NPN. - - For NPN, to use this method you should do something like:: - - static int select_next_proto_cb(SSL* ssl, - unsigned char **out, - unsigned char *outlen, - const unsigned char *in, - unsigned int inlen, - void *arg) - { - int rv; - rv = nghttp2_select_next_protocol(out, outlen, in, inlen); - if (rv == -1) { - return SSL_TLSEXT_ERR_NOACK; - } - if (rv == 1) { - ((MyType*)arg)->http2_selected = 1; - } - return SSL_TLSEXT_ERR_OK; - } - ... - SSL_CTX_set_next_proto_select_cb(ssl_ctx, select_next_proto_cb, my_obj); - diff --git a/doc/nghttp2_session_callbacks_del.rst b/doc/nghttp2_session_callbacks_del.rst deleted file mode 100644 index 2372355..0000000 --- a/doc/nghttp2_session_callbacks_del.rst +++ /dev/null @@ -1,14 +0,0 @@ - -nghttp2_session_callbacks_del -============================= - -Synopsis --------- - -*#include * - -.. function:: void nghttp2_session_callbacks_del(nghttp2_session_callbacks *callbacks) - - - Frees any resources allocated for *callbacks*. If *callbacks* is - ``NULL``, this function does nothing. diff --git a/doc/nghttp2_session_callbacks_new.rst b/doc/nghttp2_session_callbacks_new.rst deleted file mode 100644 index 809db59..0000000 --- a/doc/nghttp2_session_callbacks_new.rst +++ /dev/null @@ -1,25 +0,0 @@ - -nghttp2_session_callbacks_new -============================= - -Synopsis --------- - -*#include * - -.. function:: int nghttp2_session_callbacks_new(nghttp2_session_callbacks **callbacks_ptr) - - - Initializes *\*callbacks_ptr* with NULL values. - - The initialized object can be used when initializing multiple - :type:`nghttp2_session` objects. - - When the application finished using this object, it can use - `nghttp2_session_callbacks_del()` to free its memory. - - This function returns 0 if it succeeds, or one of the following - negative error codes: - - :macro:`NGHTTP2_ERR_NOMEM` - Out of memory. diff --git a/doc/nghttp2_session_callbacks_set_before_frame_send_callback.rst b/doc/nghttp2_session_callbacks_set_before_frame_send_callback.rst deleted file mode 100644 index 0f52c10..0000000 --- a/doc/nghttp2_session_callbacks_set_before_frame_send_callback.rst +++ /dev/null @@ -1,13 +0,0 @@ - -nghttp2_session_callbacks_set_before_frame_send_callback -======================================================== - -Synopsis --------- - -*#include * - -.. function:: void nghttp2_session_callbacks_set_before_frame_send_callback( nghttp2_session_callbacks *cbs, nghttp2_before_frame_send_callback before_frame_send_callback) - - - Sets callback function invoked before a non-DATA frame is sent. diff --git a/doc/nghttp2_session_callbacks_set_data_source_read_length_callback.rst b/doc/nghttp2_session_callbacks_set_data_source_read_length_callback.rst deleted file mode 100644 index e2819fa..0000000 --- a/doc/nghttp2_session_callbacks_set_data_source_read_length_callback.rst +++ /dev/null @@ -1,14 +0,0 @@ - -nghttp2_session_callbacks_set_data_source_read_length_callback -============================================================== - -Synopsis --------- - -*#include * - -.. function:: void nghttp2_session_callbacks_set_data_source_read_length_callback( nghttp2_session_callbacks *cbs, nghttp2_data_source_read_length_callback data_source_read_length_callback) - - - Sets callback function determine the length allowed in - :type:`nghttp2_data_source_read_callback`. diff --git a/doc/nghttp2_session_callbacks_set_on_begin_frame_callback.rst b/doc/nghttp2_session_callbacks_set_on_begin_frame_callback.rst deleted file mode 100644 index 95f0b72..0000000 --- a/doc/nghttp2_session_callbacks_set_on_begin_frame_callback.rst +++ /dev/null @@ -1,13 +0,0 @@ - -nghttp2_session_callbacks_set_on_begin_frame_callback -===================================================== - -Synopsis --------- - -*#include * - -.. function:: void nghttp2_session_callbacks_set_on_begin_frame_callback( nghttp2_session_callbacks *cbs, nghttp2_on_begin_frame_callback on_begin_frame_callback) - - - Sets callback function invoked when a frame header is received. diff --git a/doc/nghttp2_session_callbacks_set_on_begin_headers_callback.rst b/doc/nghttp2_session_callbacks_set_on_begin_headers_callback.rst deleted file mode 100644 index 970b1fd..0000000 --- a/doc/nghttp2_session_callbacks_set_on_begin_headers_callback.rst +++ /dev/null @@ -1,14 +0,0 @@ - -nghttp2_session_callbacks_set_on_begin_headers_callback -======================================================= - -Synopsis --------- - -*#include * - -.. function:: void nghttp2_session_callbacks_set_on_begin_headers_callback( nghttp2_session_callbacks *cbs, nghttp2_on_begin_headers_callback on_begin_headers_callback) - - - Sets callback function invoked when the reception of header block - in HEADERS or PUSH_PROMISE is started. diff --git a/doc/nghttp2_session_callbacks_set_on_data_chunk_recv_callback.rst b/doc/nghttp2_session_callbacks_set_on_data_chunk_recv_callback.rst deleted file mode 100644 index 04fa3f6..0000000 --- a/doc/nghttp2_session_callbacks_set_on_data_chunk_recv_callback.rst +++ /dev/null @@ -1,14 +0,0 @@ - -nghttp2_session_callbacks_set_on_data_chunk_recv_callback -========================================================= - -Synopsis --------- - -*#include * - -.. function:: void nghttp2_session_callbacks_set_on_data_chunk_recv_callback( nghttp2_session_callbacks *cbs, nghttp2_on_data_chunk_recv_callback on_data_chunk_recv_callback) - - - Sets callback function invoked when a chunk of data in DATA frame - is received. diff --git a/doc/nghttp2_session_callbacks_set_on_frame_not_send_callback.rst b/doc/nghttp2_session_callbacks_set_on_frame_not_send_callback.rst deleted file mode 100644 index 73e9fb1..0000000 --- a/doc/nghttp2_session_callbacks_set_on_frame_not_send_callback.rst +++ /dev/null @@ -1,14 +0,0 @@ - -nghttp2_session_callbacks_set_on_frame_not_send_callback -======================================================== - -Synopsis --------- - -*#include * - -.. function:: void nghttp2_session_callbacks_set_on_frame_not_send_callback( nghttp2_session_callbacks *cbs, nghttp2_on_frame_not_send_callback on_frame_not_send_callback) - - - Sets callback function invoked when a non-DATA frame is not sent - because of an error. diff --git a/doc/nghttp2_session_callbacks_set_on_frame_recv_callback.rst b/doc/nghttp2_session_callbacks_set_on_frame_recv_callback.rst deleted file mode 100644 index 736ae53..0000000 --- a/doc/nghttp2_session_callbacks_set_on_frame_recv_callback.rst +++ /dev/null @@ -1,14 +0,0 @@ - -nghttp2_session_callbacks_set_on_frame_recv_callback -==================================================== - -Synopsis --------- - -*#include * - -.. function:: void nghttp2_session_callbacks_set_on_frame_recv_callback( nghttp2_session_callbacks *cbs, nghttp2_on_frame_recv_callback on_frame_recv_callback) - - - Sets callback function invoked by `nghttp2_session_recv()` and - `nghttp2_session_mem_recv()` when a frame is received. diff --git a/doc/nghttp2_session_callbacks_set_on_frame_send_callback.rst b/doc/nghttp2_session_callbacks_set_on_frame_send_callback.rst deleted file mode 100644 index 7a02ee5..0000000 --- a/doc/nghttp2_session_callbacks_set_on_frame_send_callback.rst +++ /dev/null @@ -1,13 +0,0 @@ - -nghttp2_session_callbacks_set_on_frame_send_callback -==================================================== - -Synopsis --------- - -*#include * - -.. function:: void nghttp2_session_callbacks_set_on_frame_send_callback( nghttp2_session_callbacks *cbs, nghttp2_on_frame_send_callback on_frame_send_callback) - - - Sets callback function invoked after a frame is sent. diff --git a/doc/nghttp2_session_callbacks_set_on_header_callback.rst b/doc/nghttp2_session_callbacks_set_on_header_callback.rst deleted file mode 100644 index c843aeb..0000000 --- a/doc/nghttp2_session_callbacks_set_on_header_callback.rst +++ /dev/null @@ -1,14 +0,0 @@ - -nghttp2_session_callbacks_set_on_header_callback -================================================ - -Synopsis --------- - -*#include * - -.. function:: void nghttp2_session_callbacks_set_on_header_callback( nghttp2_session_callbacks *cbs, nghttp2_on_header_callback on_header_callback) - - - Sets callback function invoked when a header name/value pair is - received. diff --git a/doc/nghttp2_session_callbacks_set_on_invalid_frame_recv_callback.rst b/doc/nghttp2_session_callbacks_set_on_invalid_frame_recv_callback.rst deleted file mode 100644 index 97e4851..0000000 --- a/doc/nghttp2_session_callbacks_set_on_invalid_frame_recv_callback.rst +++ /dev/null @@ -1,15 +0,0 @@ - -nghttp2_session_callbacks_set_on_invalid_frame_recv_callback -============================================================ - -Synopsis --------- - -*#include * - -.. function:: void nghttp2_session_callbacks_set_on_invalid_frame_recv_callback( nghttp2_session_callbacks *cbs, nghttp2_on_invalid_frame_recv_callback on_invalid_frame_recv_callback) - - - Sets callback function invoked by `nghttp2_session_recv()` and - `nghttp2_session_mem_recv()` when an invalid non-DATA frame is - received. diff --git a/doc/nghttp2_session_callbacks_set_on_stream_close_callback.rst b/doc/nghttp2_session_callbacks_set_on_stream_close_callback.rst deleted file mode 100644 index 9349a1c..0000000 --- a/doc/nghttp2_session_callbacks_set_on_stream_close_callback.rst +++ /dev/null @@ -1,13 +0,0 @@ - -nghttp2_session_callbacks_set_on_stream_close_callback -====================================================== - -Synopsis --------- - -*#include * - -.. function:: void nghttp2_session_callbacks_set_on_stream_close_callback( nghttp2_session_callbacks *cbs, nghttp2_on_stream_close_callback on_stream_close_callback) - - - Sets callback function invoked when the stream is closed. diff --git a/doc/nghttp2_session_callbacks_set_recv_callback.rst b/doc/nghttp2_session_callbacks_set_recv_callback.rst deleted file mode 100644 index 89b6788..0000000 --- a/doc/nghttp2_session_callbacks_set_recv_callback.rst +++ /dev/null @@ -1,16 +0,0 @@ - -nghttp2_session_callbacks_set_recv_callback -=========================================== - -Synopsis --------- - -*#include * - -.. function:: void nghttp2_session_callbacks_set_recv_callback( nghttp2_session_callbacks *cbs, nghttp2_recv_callback recv_callback) - - - Sets callback function invoked when the a session wants to receive - data from the remote peer. This callback is not necessary if the - application uses solely `nghttp2_session_mem_recv()` to process - received data. diff --git a/doc/nghttp2_session_callbacks_set_select_padding_callback.rst b/doc/nghttp2_session_callbacks_set_select_padding_callback.rst deleted file mode 100644 index 17837c2..0000000 --- a/doc/nghttp2_session_callbacks_set_select_padding_callback.rst +++ /dev/null @@ -1,15 +0,0 @@ - -nghttp2_session_callbacks_set_select_padding_callback -===================================================== - -Synopsis --------- - -*#include * - -.. function:: void nghttp2_session_callbacks_set_select_padding_callback( nghttp2_session_callbacks *cbs, nghttp2_select_padding_callback select_padding_callback) - - - Sets callback function invoked when the library asks application - how many padding bytes are required for the transmission of the - given frame. diff --git a/doc/nghttp2_session_callbacks_set_send_callback.rst b/doc/nghttp2_session_callbacks_set_send_callback.rst deleted file mode 100644 index bd7d2b9..0000000 --- a/doc/nghttp2_session_callbacks_set_send_callback.rst +++ /dev/null @@ -1,16 +0,0 @@ - -nghttp2_session_callbacks_set_send_callback -=========================================== - -Synopsis --------- - -*#include * - -.. function:: void nghttp2_session_callbacks_set_send_callback( nghttp2_session_callbacks *cbs, nghttp2_send_callback send_callback) - - - Sets callback function invoked when a session wants to send data to - the remote peer. This callback is not necessary if the application - uses solely `nghttp2_session_mem_send()` to serialize data to - transmit. diff --git a/doc/nghttp2_session_callbacks_set_send_data_callback.rst b/doc/nghttp2_session_callbacks_set_send_data_callback.rst deleted file mode 100644 index 5678cfc..0000000 --- a/doc/nghttp2_session_callbacks_set_send_data_callback.rst +++ /dev/null @@ -1,15 +0,0 @@ - -nghttp2_session_callbacks_set_send_data_callback -================================================ - -Synopsis --------- - -*#include * - -.. function:: void nghttp2_session_callbacks_set_send_data_callback( nghttp2_session_callbacks *cbs, nghttp2_send_data_callback send_data_callback) - - - Sets callback function invoked when - :macro:`NGHTTP2_DATA_FLAG_NO_COPY` is used in - :type:`nghttp2_data_source_read_callback` to avoid data copy. diff --git a/doc/nghttp2_session_client_new.rst b/doc/nghttp2_session_client_new.rst deleted file mode 100644 index 6f0c261..0000000 --- a/doc/nghttp2_session_client_new.rst +++ /dev/null @@ -1,29 +0,0 @@ - -nghttp2_session_client_new -========================== - -Synopsis --------- - -*#include * - -.. function:: int nghttp2_session_client_new(nghttp2_session **session_ptr, const nghttp2_session_callbacks *callbacks, void *user_data) - - - Initializes *\*session_ptr* for client use. The all members of - *callbacks* are copied to *\*session_ptr*. Therefore *\*session_ptr* - does not store *callbacks*. The *user_data* is an arbitrary user - supplied data, which will be passed to the callback functions. - - The :type:`nghttp2_send_callback` must be specified. If the - application code uses `nghttp2_session_recv()`, the - :type:`nghttp2_recv_callback` must be specified. The other members - of *callbacks* can be ``NULL``. - - If this function fails, *\*session_ptr* is left untouched. - - This function returns 0 if it succeeds, or one of the following - negative error codes: - - :macro:`NGHTTP2_ERR_NOMEM` - Out of memory. diff --git a/doc/nghttp2_session_client_new2.rst b/doc/nghttp2_session_client_new2.rst deleted file mode 100644 index 863d429..0000000 --- a/doc/nghttp2_session_client_new2.rst +++ /dev/null @@ -1,29 +0,0 @@ - -nghttp2_session_client_new2 -=========================== - -Synopsis --------- - -*#include * - -.. function:: int nghttp2_session_client_new2(nghttp2_session **session_ptr, const nghttp2_session_callbacks *callbacks, void *user_data, const nghttp2_option *option) - - - Like `nghttp2_session_client_new()`, but with additional options - specified in the *option*. - - The *option* can be ``NULL`` and the call is equivalent to - `nghttp2_session_client_new()`. - - This function does not take ownership *option*. The application is - responsible for freeing *option* if it finishes using the object. - - The library code does not refer to *option* after this function - returns. - - This function returns 0 if it succeeds, or one of the following - negative error codes: - - :macro:`NGHTTP2_ERR_NOMEM` - Out of memory. diff --git a/doc/nghttp2_session_client_new3.rst b/doc/nghttp2_session_client_new3.rst deleted file mode 100644 index 22bef5a..0000000 --- a/doc/nghttp2_session_client_new3.rst +++ /dev/null @@ -1,29 +0,0 @@ - -nghttp2_session_client_new3 -=========================== - -Synopsis --------- - -*#include * - -.. function:: int nghttp2_session_client_new3( nghttp2_session **session_ptr, const nghttp2_session_callbacks *callbacks, void *user_data, const nghttp2_option *option, nghttp2_mem *mem) - - - Like `nghttp2_session_client_new2()`, but with additional custom - memory allocator specified in the *mem*. - - The *mem* can be ``NULL`` and the call is equivalent to - `nghttp2_session_client_new2()`. - - This function does not take ownership *mem*. The application is - responsible for freeing *mem*. - - The library code does not refer to *mem* pointer after this - function returns, so the application can safely free it. - - This function returns 0 if it succeeds, or one of the following - negative error codes: - - :macro:`NGHTTP2_ERR_NOMEM` - Out of memory. diff --git a/doc/nghttp2_session_consume.rst b/doc/nghttp2_session_consume.rst deleted file mode 100644 index c4d08f2..0000000 --- a/doc/nghttp2_session_consume.rst +++ /dev/null @@ -1,31 +0,0 @@ - -nghttp2_session_consume -======================= - -Synopsis --------- - -*#include * - -.. function:: int nghttp2_session_consume(nghttp2_session *session, int32_t stream_id, size_t size) - - - Tells the *session* that *size* bytes for a stream denoted by - *stream_id* were consumed by application and are ready to - WINDOW_UPDATE. The consumed bytes are counted towards both - connection and stream level WINDOW_UPDATE (see - `nghttp2_session_consume_connection()` and - `nghttp2_session_consume_stream()` to update consumption - independently). This function is intended to be used without - automatic window update (see - `nghttp2_option_set_no_auto_window_update()`). - - This function returns 0 if it succeeds, or one of the following - negative error codes: - - :macro:`NGHTTP2_ERR_NOMEM` - Out of memory. - :macro:`NGHTTP2_ERR_INVALID_ARGUMENT` - The *stream_id* is 0. - :macro:`NGHTTP2_ERR_INVALID_STATE` - Automatic WINDOW_UPDATE is not disabled. diff --git a/doc/nghttp2_session_consume_connection.rst b/doc/nghttp2_session_consume_connection.rst deleted file mode 100644 index c263cd6..0000000 --- a/doc/nghttp2_session_consume_connection.rst +++ /dev/null @@ -1,24 +0,0 @@ - -nghttp2_session_consume_connection -================================== - -Synopsis --------- - -*#include * - -.. function:: int nghttp2_session_consume_connection(nghttp2_session *session, size_t size) - - - Like `nghttp2_session_consume()`, but this only tells library that - *size* bytes were consumed only for connection level. Note that - HTTP/2 maintains connection and stream level flow control windows - independently. - - This function returns 0 if it succeeds, or one of the following - negative error codes: - - :macro:`NGHTTP2_ERR_NOMEM` - Out of memory. - :macro:`NGHTTP2_ERR_INVALID_STATE` - Automatic WINDOW_UPDATE is not disabled. diff --git a/doc/nghttp2_session_consume_stream.rst b/doc/nghttp2_session_consume_stream.rst deleted file mode 100644 index 694ee50..0000000 --- a/doc/nghttp2_session_consume_stream.rst +++ /dev/null @@ -1,26 +0,0 @@ - -nghttp2_session_consume_stream -============================== - -Synopsis --------- - -*#include * - -.. function:: int nghttp2_session_consume_stream(nghttp2_session *session, int32_t stream_id, size_t size) - - - Like `nghttp2_session_consume()`, but this only tells library that - *size* bytes were consumed only for stream denoted by *stream_id*. - Note that HTTP/2 maintains connection and stream level flow control - windows independently. - - This function returns 0 if it succeeds, or one of the following - negative error codes: - - :macro:`NGHTTP2_ERR_NOMEM` - Out of memory. - :macro:`NGHTTP2_ERR_INVALID_ARGUMENT` - The *stream_id* is 0. - :macro:`NGHTTP2_ERR_INVALID_STATE` - Automatic WINDOW_UPDATE is not disabled. diff --git a/doc/nghttp2_session_del.rst b/doc/nghttp2_session_del.rst deleted file mode 100644 index 1c00d7c..0000000 --- a/doc/nghttp2_session_del.rst +++ /dev/null @@ -1,14 +0,0 @@ - -nghttp2_session_del -=================== - -Synopsis --------- - -*#include * - -.. function:: void nghttp2_session_del(nghttp2_session *session) - - - Frees any resources allocated for *session*. If *session* is - ``NULL``, this function does nothing. diff --git a/doc/nghttp2_session_get_effective_local_window_size.rst b/doc/nghttp2_session_get_effective_local_window_size.rst deleted file mode 100644 index 60efcba..0000000 --- a/doc/nghttp2_session_get_effective_local_window_size.rst +++ /dev/null @@ -1,18 +0,0 @@ - -nghttp2_session_get_effective_local_window_size -=============================================== - -Synopsis --------- - -*#include * - -.. function:: int32_t nghttp2_session_get_effective_local_window_size(nghttp2_session *session) - - - Returns the local (receive) window size for a connection. The - local window size can be adjusted by - `nghttp2_submit_window_update()`. This function takes into account - that and returns effective window size. - - This function returns -1 if it fails. diff --git a/doc/nghttp2_session_get_effective_recv_data_length.rst b/doc/nghttp2_session_get_effective_recv_data_length.rst deleted file mode 100644 index be4e97a..0000000 --- a/doc/nghttp2_session_get_effective_recv_data_length.rst +++ /dev/null @@ -1,22 +0,0 @@ - -nghttp2_session_get_effective_recv_data_length -============================================== - -Synopsis --------- - -*#include * - -.. function:: int32_t nghttp2_session_get_effective_recv_data_length(nghttp2_session *session) - - - Returns the number of DATA payload in bytes received without - WINDOW_UPDATE transmission for a connection. The local (receive) - window size can be adjusted by `nghttp2_submit_window_update()`. - This function takes into account that and returns effective data - length. In particular, if the local window size is reduced by - submitting negative window_size_increment with - `nghttp2_submit_window_update()`, this function returns the number - of bytes less than actually received. - - This function returns -1 if it fails. diff --git a/doc/nghttp2_session_get_last_proc_stream_id.rst b/doc/nghttp2_session_get_last_proc_stream_id.rst deleted file mode 100644 index 726fffd..0000000 --- a/doc/nghttp2_session_get_last_proc_stream_id.rst +++ /dev/null @@ -1,19 +0,0 @@ - -nghttp2_session_get_last_proc_stream_id -======================================= - -Synopsis --------- - -*#include * - -.. function:: int32_t nghttp2_session_get_last_proc_stream_id(nghttp2_session *session) - - - Returns the last stream ID of a stream for which - :type:`nghttp2_on_frame_recv_callback` was invoked most recently. - The returned value can be used as last_stream_id parameter for - `nghttp2_submit_goaway()` and - `nghttp2_session_terminate_session2()`. - - This function always succeeds. diff --git a/doc/nghttp2_session_get_next_stream_id.rst b/doc/nghttp2_session_get_next_stream_id.rst deleted file mode 100644 index a68401c..0000000 --- a/doc/nghttp2_session_get_next_stream_id.rst +++ /dev/null @@ -1,15 +0,0 @@ - -nghttp2_session_get_next_stream_id -================================== - -Synopsis --------- - -*#include * - -.. function:: uint32_t nghttp2_session_get_next_stream_id(nghttp2_session *session) - - - Returns the next outgoing stream ID. Notice that return type is - uint32_t. If we run out of stream ID for this session, this - function returns 1 << 31. diff --git a/doc/nghttp2_session_get_outbound_queue_size.rst b/doc/nghttp2_session_get_outbound_queue_size.rst deleted file mode 100644 index 8816ee7..0000000 --- a/doc/nghttp2_session_get_outbound_queue_size.rst +++ /dev/null @@ -1,14 +0,0 @@ - -nghttp2_session_get_outbound_queue_size -======================================= - -Synopsis --------- - -*#include * - -.. function:: size_t nghttp2_session_get_outbound_queue_size(nghttp2_session *session) - - - Returns the number of frames in the outbound queue. This does not - include the deferred DATA frames. diff --git a/doc/nghttp2_session_get_remote_settings.rst b/doc/nghttp2_session_get_remote_settings.rst deleted file mode 100644 index 6e9ad91..0000000 --- a/doc/nghttp2_session_get_remote_settings.rst +++ /dev/null @@ -1,15 +0,0 @@ - -nghttp2_session_get_remote_settings -=================================== - -Synopsis --------- - -*#include * - -.. function:: uint32_t nghttp2_session_get_remote_settings(nghttp2_session *session, nghttp2_settings_id id) - - - Returns the value of SETTINGS *id* notified by a remote endpoint. - The *id* must be one of values defined in - :macro:`nghttp2_settings_id`. diff --git a/doc/nghttp2_session_get_remote_window_size.rst b/doc/nghttp2_session_get_remote_window_size.rst deleted file mode 100644 index cd810c2..0000000 --- a/doc/nghttp2_session_get_remote_window_size.rst +++ /dev/null @@ -1,15 +0,0 @@ - -nghttp2_session_get_remote_window_size -====================================== - -Synopsis --------- - -*#include * - -.. function:: int32_t nghttp2_session_get_remote_window_size(nghttp2_session *session) - - - Returns the remote window size for a connection. - - This function always succeeds. diff --git a/doc/nghttp2_session_get_stream_effective_local_window_size.rst b/doc/nghttp2_session_get_stream_effective_local_window_size.rst deleted file mode 100644 index 387f7a6..0000000 --- a/doc/nghttp2_session_get_stream_effective_local_window_size.rst +++ /dev/null @@ -1,18 +0,0 @@ - -nghttp2_session_get_stream_effective_local_window_size -====================================================== - -Synopsis --------- - -*#include * - -.. function:: int32_t nghttp2_session_get_stream_effective_local_window_size( nghttp2_session *session, int32_t stream_id) - - - Returns the local (receive) window size for the stream *stream_id*. - The local window size can be adjusted by - `nghttp2_submit_window_update()`. This function takes into account - that and returns effective window size. - - This function returns -1 if it fails. diff --git a/doc/nghttp2_session_get_stream_effective_recv_data_length.rst b/doc/nghttp2_session_get_stream_effective_recv_data_length.rst deleted file mode 100644 index 8e01b2f..0000000 --- a/doc/nghttp2_session_get_stream_effective_recv_data_length.rst +++ /dev/null @@ -1,22 +0,0 @@ - -nghttp2_session_get_stream_effective_recv_data_length -===================================================== - -Synopsis --------- - -*#include * - -.. function:: int32_t nghttp2_session_get_stream_effective_recv_data_length( nghttp2_session *session, int32_t stream_id) - - - Returns the number of DATA payload in bytes received without - WINDOW_UPDATE transmission for the stream *stream_id*. The local - (receive) window size can be adjusted by - `nghttp2_submit_window_update()`. This function takes into account - that and returns effective data length. In particular, if the - local window size is reduced by submitting negative - window_size_increment with `nghttp2_submit_window_update()`, this - function returns the number of bytes less than actually received. - - This function returns -1 if it fails. diff --git a/doc/nghttp2_session_get_stream_local_close.rst b/doc/nghttp2_session_get_stream_local_close.rst deleted file mode 100644 index cc01d8d..0000000 --- a/doc/nghttp2_session_get_stream_local_close.rst +++ /dev/null @@ -1,14 +0,0 @@ - -nghttp2_session_get_stream_local_close -====================================== - -Synopsis --------- - -*#include * - -.. function:: int nghttp2_session_get_stream_local_close(nghttp2_session *session, int32_t stream_id) - - - Returns 1 if local peer half closed the given stream *stream_id*. - Returns 0 if it did not. Returns -1 if no such stream exists. diff --git a/doc/nghttp2_session_get_stream_remote_close.rst b/doc/nghttp2_session_get_stream_remote_close.rst deleted file mode 100644 index a0a540c..0000000 --- a/doc/nghttp2_session_get_stream_remote_close.rst +++ /dev/null @@ -1,14 +0,0 @@ - -nghttp2_session_get_stream_remote_close -======================================= - -Synopsis --------- - -*#include * - -.. function:: int nghttp2_session_get_stream_remote_close(nghttp2_session *session, int32_t stream_id) - - - Returns 1 if remote peer half closed the given stream *stream_id*. - Returns 0 if it did not. Returns -1 if no such stream exists. diff --git a/doc/nghttp2_session_get_stream_remote_window_size.rst b/doc/nghttp2_session_get_stream_remote_window_size.rst deleted file mode 100644 index 5404162..0000000 --- a/doc/nghttp2_session_get_stream_remote_window_size.rst +++ /dev/null @@ -1,22 +0,0 @@ - -nghttp2_session_get_stream_remote_window_size -============================================= - -Synopsis --------- - -*#include * - -.. function:: int32_t nghttp2_session_get_stream_remote_window_size(nghttp2_session *session, int32_t stream_id) - - - Returns the remote window size for a given stream *stream_id*. - - This is the amount of flow-controlled payload (e.g., DATA) that the - local endpoint can send without stream level WINDOW_UPDATE. There - is also connection level flow control, so the effective size of - payload that the local endpoint can actually send is - min(`nghttp2_session_get_stream_remote_window_size()`, - `nghttp2_session_get_remote_window_size()`). - - This function returns -1 if it fails. diff --git a/doc/nghttp2_session_get_stream_user_data.rst b/doc/nghttp2_session_get_stream_user_data.rst deleted file mode 100644 index 94f18c1..0000000 --- a/doc/nghttp2_session_get_stream_user_data.rst +++ /dev/null @@ -1,20 +0,0 @@ - -nghttp2_session_get_stream_user_data -==================================== - -Synopsis --------- - -*#include * - -.. function:: void * nghttp2_session_get_stream_user_data(nghttp2_session *session, int32_t stream_id) - - - Returns stream_user_data for the stream *stream_id*. The - stream_user_data is provided by `nghttp2_submit_request()`, - `nghttp2_submit_headers()` or - `nghttp2_session_set_stream_user_data()`. Unless it is set using - `nghttp2_session_set_stream_user_data()`, if the stream is - initiated by the remote endpoint, stream_user_data is always - ``NULL``. If the stream does not exist, this function returns - ``NULL``. diff --git a/doc/nghttp2_session_mem_recv.rst b/doc/nghttp2_session_mem_recv.rst deleted file mode 100644 index 994db42..0000000 --- a/doc/nghttp2_session_mem_recv.rst +++ /dev/null @@ -1,42 +0,0 @@ - -nghttp2_session_mem_recv -======================== - -Synopsis --------- - -*#include * - -.. function:: ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in, size_t inlen) - - - Processes data *in* as an input from the remote endpoint. The - *inlen* indicates the number of bytes in the *in*. - - This function behaves like `nghttp2_session_recv()` except that it - does not use :type:`nghttp2_recv_callback` to receive data; the - *in* is the only data for the invocation of this function. If all - bytes are processed, this function returns. The other callbacks - are called in the same way as they are in `nghttp2_session_recv()`. - - In the current implementation, this function always tries to - processes all input data unless either an error occurs or - :macro:`NGHTTP2_ERR_PAUSE` is returned from - :type:`nghttp2_on_header_callback` or - :type:`nghttp2_on_data_chunk_recv_callback`. If - :macro:`NGHTTP2_ERR_PAUSE` is used, the return value includes the - number of bytes which was used to produce the data or frame for the - callback. - - This function returns the number of processed bytes, or one of the - following negative error codes: - - :macro:`NGHTTP2_ERR_NOMEM` - Out of memory. - :macro:`NGHTTP2_ERR_CALLBACK_FAILURE` - The callback function failed. - :macro:`NGHTTP2_ERR_BAD_CLIENT_MAGIC` - Invalid client magic was detected. This error only returns - when *session* was configured as server and - `nghttp2_option_set_no_recv_client_magic()` is not used with - nonzero value. diff --git a/doc/nghttp2_session_mem_send.rst b/doc/nghttp2_session_mem_send.rst deleted file mode 100644 index 979b637..0000000 --- a/doc/nghttp2_session_mem_send.rst +++ /dev/null @@ -1,38 +0,0 @@ - -nghttp2_session_mem_send -======================== - -Synopsis --------- - -*#include * - -.. function:: ssize_t nghttp2_session_mem_send(nghttp2_session *session, const uint8_t **data_ptr) - - - Returns the serialized data to send. - - This function behaves like `nghttp2_session_send()` except that it - does not use :type:`nghttp2_send_callback` to transmit data. - Instead, it assigns the pointer to the serialized data to the - *\*data_ptr* and returns its length. The other callbacks are called - in the same way as they are in `nghttp2_session_send()`. - - If no data is available to send, this function returns 0. - - This function may not return all serialized data in one invocation. - To get all data, call this function repeatedly until it returns 0 - or one of negative error codes. - - The assigned *\*data_ptr* is valid until the next call of - `nghttp2_session_mem_send()` or `nghttp2_session_send()`. - - The caller must send all data before sending the next chunk of - data. - - This function returns the length of the data pointed by the - *\*data_ptr* if it succeeds, or one of the following negative error - codes: - - :macro:`NGHTTP2_ERR_NOMEM` - Out of memory. diff --git a/doc/nghttp2_session_recv.rst b/doc/nghttp2_session_recv.rst deleted file mode 100644 index 61da799..0000000 --- a/doc/nghttp2_session_recv.rst +++ /dev/null @@ -1,72 +0,0 @@ - -nghttp2_session_recv -==================== - -Synopsis --------- - -*#include * - -.. function:: int nghttp2_session_recv(nghttp2_session *session) - - - Receives frames from the remote peer. - - This function receives as many frames as possible until the user - callback :type:`nghttp2_recv_callback` returns - :macro:`NGHTTP2_ERR_WOULDBLOCK`. This function calls several - callback functions which are passed when initializing the - *session*. Here is the simple time chart which tells when each - callback is invoked: - - 1. :type:`nghttp2_recv_callback` is invoked one or more times to - receive frame header. - - 2. When frame header is received, - :type:`nghttp2_on_begin_frame_callback` is invoked. - - 3. If the frame is DATA frame: - - 1. :type:`nghttp2_recv_callback` is invoked to receive DATA - payload. For each chunk of data, - :type:`nghttp2_on_data_chunk_recv_callback` is invoked. - - 2. If one DATA frame is completely received, - :type:`nghttp2_on_frame_recv_callback` is invoked. If the - reception of the frame triggers the closure of the stream, - :type:`nghttp2_on_stream_close_callback` is invoked. - - 4. If the frame is the control frame: - - 1. :type:`nghttp2_recv_callback` is invoked one or more times to - receive whole frame. - - 2. If the received frame is valid, then following actions are - taken. If the frame is either HEADERS or PUSH_PROMISE, - :type:`nghttp2_on_begin_headers_callback` is invoked. Then - :type:`nghttp2_on_header_callback` is invoked for each header - name/value pair. After all name/value pairs are emitted - successfully, :type:`nghttp2_on_frame_recv_callback` is - invoked. For other frames, - :type:`nghttp2_on_frame_recv_callback` is invoked. If the - reception of the frame triggers the closure of the stream, - :type:`nghttp2_on_stream_close_callback` is invoked. - - 3. If the received frame is unpacked but is interpreted as - invalid, :type:`nghttp2_on_invalid_frame_recv_callback` is - invoked. - - This function returns 0 if it succeeds, or one of the following - negative error codes: - - :macro:`NGHTTP2_ERR_EOF` - The remote peer did shutdown on the connection. - :macro:`NGHTTP2_ERR_NOMEM` - Out of memory. - :macro:`NGHTTP2_ERR_CALLBACK_FAILURE` - The callback function failed. - :macro:`NGHTTP2_ERR_BAD_CLIENT_MAGIC` - Invalid client magic was detected. This error only returns - when *session* was configured as server and - `nghttp2_option_set_no_recv_client_magic()` is not used with - nonzero value. diff --git a/doc/nghttp2_session_resume_data.rst b/doc/nghttp2_session_resume_data.rst deleted file mode 100644 index daa9b8f..0000000 --- a/doc/nghttp2_session_resume_data.rst +++ /dev/null @@ -1,22 +0,0 @@ - -nghttp2_session_resume_data -=========================== - -Synopsis --------- - -*#include * - -.. function:: int nghttp2_session_resume_data(nghttp2_session *session, int32_t stream_id) - - - Puts back previously deferred DATA frame in the stream *stream_id* - to the outbound queue. - - This function returns 0 if it succeeds, or one of the following - negative error codes: - - :macro:`NGHTTP2_ERR_INVALID_ARGUMENT` - The stream does not exist; or no deferred data exist. - :macro:`NGHTTP2_ERR_NOMEM` - Out of memory. diff --git a/doc/nghttp2_session_send.rst b/doc/nghttp2_session_send.rst deleted file mode 100644 index 1f40bab..0000000 --- a/doc/nghttp2_session_send.rst +++ /dev/null @@ -1,55 +0,0 @@ - -nghttp2_session_send -==================== - -Synopsis --------- - -*#include * - -.. function:: int nghttp2_session_send(nghttp2_session *session) - - - Sends pending frames to the remote peer. - - This function retrieves the highest prioritized frame from the - outbound queue and sends it to the remote peer. It does this as - many as possible until the user callback - :type:`nghttp2_send_callback` returns - :macro:`NGHTTP2_ERR_WOULDBLOCK` or the outbound queue becomes empty. - This function calls several callback functions which are passed - when initializing the *session*. Here is the simple time chart - which tells when each callback is invoked: - - 1. Get the next frame to send from outbound queue. - - 2. Prepare transmission of the frame. - - 3. If the control frame cannot be sent because some preconditions - are not met (e.g., request HEADERS cannot be sent after GOAWAY), - :type:`nghttp2_on_frame_not_send_callback` is invoked. Abort - the following steps. - - 4. If the frame is HEADERS, PUSH_PROMISE or DATA, - :type:`nghttp2_select_padding_callback` is invoked. - - 5. If the frame is request HEADERS, the stream is opened here. - - 6. :type:`nghttp2_before_frame_send_callback` is invoked. - - 7. :type:`nghttp2_send_callback` is invoked one or more times to - send the frame. - - 8. :type:`nghttp2_on_frame_send_callback` is invoked. - - 9. If the transmission of the frame triggers closure of the stream, - the stream is closed and - :type:`nghttp2_on_stream_close_callback` is invoked. - - This function returns 0 if it succeeds, or one of the following - negative error codes: - - :macro:`NGHTTP2_ERR_NOMEM` - Out of memory. - :macro:`NGHTTP2_ERR_CALLBACK_FAILURE` - The callback function failed. diff --git a/doc/nghttp2_session_server_new.rst b/doc/nghttp2_session_server_new.rst deleted file mode 100644 index 7350180..0000000 --- a/doc/nghttp2_session_server_new.rst +++ /dev/null @@ -1,29 +0,0 @@ - -nghttp2_session_server_new -========================== - -Synopsis --------- - -*#include * - -.. function:: int nghttp2_session_server_new(nghttp2_session **session_ptr, const nghttp2_session_callbacks *callbacks, void *user_data) - - - Initializes *\*session_ptr* for server use. The all members of - *callbacks* are copied to *\*session_ptr*. Therefore *\*session_ptr* - does not store *callbacks*. The *user_data* is an arbitrary user - supplied data, which will be passed to the callback functions. - - The :type:`nghttp2_send_callback` must be specified. If the - application code uses `nghttp2_session_recv()`, the - :type:`nghttp2_recv_callback` must be specified. The other members - of *callbacks* can be ``NULL``. - - If this function fails, *\*session_ptr* is left untouched. - - This function returns 0 if it succeeds, or one of the following - negative error codes: - - :macro:`NGHTTP2_ERR_NOMEM` - Out of memory. diff --git a/doc/nghttp2_session_server_new2.rst b/doc/nghttp2_session_server_new2.rst deleted file mode 100644 index 482e651..0000000 --- a/doc/nghttp2_session_server_new2.rst +++ /dev/null @@ -1,29 +0,0 @@ - -nghttp2_session_server_new2 -=========================== - -Synopsis --------- - -*#include * - -.. function:: int nghttp2_session_server_new2(nghttp2_session **session_ptr, const nghttp2_session_callbacks *callbacks, void *user_data, const nghttp2_option *option) - - - Like `nghttp2_session_server_new()`, but with additional options - specified in the *option*. - - The *option* can be ``NULL`` and the call is equivalent to - `nghttp2_session_server_new()`. - - This function does not take ownership *option*. The application is - responsible for freeing *option* if it finishes using the object. - - The library code does not refer to *option* after this function - returns. - - This function returns 0 if it succeeds, or one of the following - negative error codes: - - :macro:`NGHTTP2_ERR_NOMEM` - Out of memory. diff --git a/doc/nghttp2_session_server_new3.rst b/doc/nghttp2_session_server_new3.rst deleted file mode 100644 index 35d1e39..0000000 --- a/doc/nghttp2_session_server_new3.rst +++ /dev/null @@ -1,29 +0,0 @@ - -nghttp2_session_server_new3 -=========================== - -Synopsis --------- - -*#include * - -.. function:: int nghttp2_session_server_new3( nghttp2_session **session_ptr, const nghttp2_session_callbacks *callbacks, void *user_data, const nghttp2_option *option, nghttp2_mem *mem) - - - Like `nghttp2_session_server_new2()`, but with additional custom - memory allocator specified in the *mem*. - - The *mem* can be ``NULL`` and the call is equivalent to - `nghttp2_session_server_new2()`. - - This function does not take ownership *mem*. The application is - responsible for freeing *mem*. - - The library code does not refer to *mem* pointer after this - function returns, so the application can safely free it. - - This function returns 0 if it succeeds, or one of the following - negative error codes: - - :macro:`NGHTTP2_ERR_NOMEM` - Out of memory. diff --git a/doc/nghttp2_session_set_next_stream_id.rst b/doc/nghttp2_session_set_next_stream_id.rst deleted file mode 100644 index 5f2efa9..0000000 --- a/doc/nghttp2_session_set_next_stream_id.rst +++ /dev/null @@ -1,24 +0,0 @@ - -nghttp2_session_set_next_stream_id -================================== - -Synopsis --------- - -*#include * - -.. function:: int nghttp2_session_set_next_stream_id(nghttp2_session *session, int32_t next_stream_id) - - - Tells the *session* that next stream ID is *next_stream_id*. The - *next_stream_id* must be equal or greater than the value returned - by `nghttp2_session_get_next_stream_id()`. - - This function returns 0 if it succeeds, or one of the following - negative error codes: - - :macro:`NGHTTP2_ERR_INVALID_ARGUMENT` - The *next_stream_id* is strictly less than the value - `nghttp2_session_get_next_stream_id()` returns; or - *next_stream_id* is invalid (e.g., even integer for client, or - odd integer for server). diff --git a/doc/nghttp2_session_set_stream_user_data.rst b/doc/nghttp2_session_set_stream_user_data.rst deleted file mode 100644 index 6700650..0000000 --- a/doc/nghttp2_session_set_stream_user_data.rst +++ /dev/null @@ -1,26 +0,0 @@ - -nghttp2_session_set_stream_user_data -==================================== - -Synopsis --------- - -*#include * - -.. function:: int nghttp2_session_set_stream_user_data(nghttp2_session *session, int32_t stream_id, void *stream_user_data) - - - Sets the *stream_user_data* to the stream denoted by the - *stream_id*. If a stream user data is already set to the stream, - it is replaced with the *stream_user_data*. It is valid to specify - ``NULL`` in the *stream_user_data*, which nullifies the associated - data pointer. - - It is valid to set the *stream_user_data* to the stream reserved by - PUSH_PROMISE frame. - - This function returns 0 if it succeeds, or one of following - negative error codes: - - :macro:`NGHTTP2_ERR_INVALID_ARGUMENT` - The stream does not exist diff --git a/doc/nghttp2_session_terminate_session.rst b/doc/nghttp2_session_terminate_session.rst deleted file mode 100644 index 2b85319..0000000 --- a/doc/nghttp2_session_terminate_session.rst +++ /dev/null @@ -1,34 +0,0 @@ - -nghttp2_session_terminate_session -================================= - -Synopsis --------- - -*#include * - -.. function:: int nghttp2_session_terminate_session(nghttp2_session *session, uint32_t error_code) - - - Signals the session so that the connection should be terminated. - - The last stream ID is the minimum value between the stream ID of a - stream for which :type:`nghttp2_on_frame_recv_callback` was called - most recently and the last stream ID we have sent to the peer - previously. - - The *error_code* is the error code of this GOAWAY frame. The - pre-defined error code is one of :macro:`nghttp2_error_code`. - - After the transmission, both `nghttp2_session_want_read()` and - `nghttp2_session_want_write()` return 0. - - This function should be called when the connection should be - terminated after sending GOAWAY. If the remaining streams should - be processed after GOAWAY, use `nghttp2_submit_goaway()` instead. - - This function returns 0 if it succeeds, or one of the following - negative error codes: - - :macro:`NGHTTP2_ERR_NOMEM` - Out of memory. diff --git a/doc/nghttp2_session_terminate_session2.rst b/doc/nghttp2_session_terminate_session2.rst deleted file mode 100644 index fd31508..0000000 --- a/doc/nghttp2_session_terminate_session2.rst +++ /dev/null @@ -1,34 +0,0 @@ - -nghttp2_session_terminate_session2 -================================== - -Synopsis --------- - -*#include * - -.. function:: int nghttp2_session_terminate_session2(nghttp2_session *session, int32_t last_stream_id, uint32_t error_code) - - - Signals the session so that the connection should be terminated. - - This function behaves like `nghttp2_session_terminate_session()`, - but the last stream ID can be specified by the application for fine - grained control of stream. The HTTP/2 specification does not allow - last_stream_id to be increased. So the actual value sent as - last_stream_id is the minimum value between the given - *last_stream_id* and the last_stream_id we have previously sent to - the peer. - - The *last_stream_id* is peer's stream ID or 0. So if *session* is - initialized as client, *last_stream_id* must be even or 0. If - *session* is initialized as server, *last_stream_id* must be odd or - 0. - - This function returns 0 if it succeeds, or one of the following - negative error codes: - - :macro:`NGHTTP2_ERR_NOMEM` - Out of memory. - :macro:`NGHTTP2_ERR_INVALID_ARGUMENT` - The *last_stream_id* is invalid. diff --git a/doc/nghttp2_session_upgrade.rst b/doc/nghttp2_session_upgrade.rst deleted file mode 100644 index f750e7c..0000000 --- a/doc/nghttp2_session_upgrade.rst +++ /dev/null @@ -1,44 +0,0 @@ - -nghttp2_session_upgrade -======================= - -Synopsis --------- - -*#include * - -.. function:: int nghttp2_session_upgrade(nghttp2_session *session, const uint8_t *settings_payload, size_t settings_payloadlen, void *stream_user_data) - - - Performs post-process of HTTP Upgrade request. This function can - be called from both client and server, but the behavior is very - different in each other. - - If called from client side, the *settings_payload* must be the - value sent in ``HTTP2-Settings`` header field and must be decoded - by base64url decoder. The *settings_payloadlen* is the length of - *settings_payload*. The *settings_payload* is unpacked and its - setting values will be submitted using `nghttp2_submit_settings()`. - This means that the client application code does not need to submit - SETTINGS by itself. The stream with stream ID=1 is opened and the - *stream_user_data* is used for its stream_user_data. The opened - stream becomes half-closed (local) state. - - If called from server side, the *settings_payload* must be the - value received in ``HTTP2-Settings`` header field and must be - decoded by base64url decoder. The *settings_payloadlen* is the - length of *settings_payload*. It is treated as if the SETTINGS - frame with that payload is received. Thus, callback functions for - the reception of SETTINGS frame will be invoked. The stream with - stream ID=1 is opened. The *stream_user_data* is ignored. The - opened stream becomes half-closed (remote). - - This function returns 0 if it succeeds, or one of the following - negative error codes: - - :macro:`NGHTTP2_ERR_NOMEM` - Out of memory. - :macro:`NGHTTP2_ERR_INVALID_ARGUMENT` - The *settings_payload* is badly formed. - :macro:`NGHTTP2_ERR_PROTO` - The stream ID 1 is already used or closed; or is not available. diff --git a/doc/nghttp2_session_want_read.rst b/doc/nghttp2_session_want_read.rst deleted file mode 100644 index 4a2af49..0000000 --- a/doc/nghttp2_session_want_read.rst +++ /dev/null @@ -1,18 +0,0 @@ - -nghttp2_session_want_read -========================= - -Synopsis --------- - -*#include * - -.. function:: int nghttp2_session_want_read(nghttp2_session *session) - - - Returns nonzero value if *session* wants to receive data from the - remote peer. - - If both `nghttp2_session_want_read()` and - `nghttp2_session_want_write()` return 0, the application should - drop the connection. diff --git a/doc/nghttp2_session_want_write.rst b/doc/nghttp2_session_want_write.rst deleted file mode 100644 index 96f27fc..0000000 --- a/doc/nghttp2_session_want_write.rst +++ /dev/null @@ -1,18 +0,0 @@ - -nghttp2_session_want_write -========================== - -Synopsis --------- - -*#include * - -.. function:: int nghttp2_session_want_write(nghttp2_session *session) - - - Returns nonzero value if *session* wants to send data to the remote - peer. - - If both `nghttp2_session_want_read()` and - `nghttp2_session_want_write()` return 0, the application should - drop the connection. diff --git a/doc/nghttp2_strerror.rst b/doc/nghttp2_strerror.rst deleted file mode 100644 index 479593e..0000000 --- a/doc/nghttp2_strerror.rst +++ /dev/null @@ -1,14 +0,0 @@ - -nghttp2_strerror -================ - -Synopsis --------- - -*#include * - -.. function:: const char *nghttp2_strerror(int lib_error_code) - - - Returns string describing the *lib_error_code*. The - *lib_error_code* must be one of the :macro:`nghttp2_error`. diff --git a/doc/nghttp2_submit_data.rst b/doc/nghttp2_submit_data.rst deleted file mode 100644 index d0c5579..0000000 --- a/doc/nghttp2_submit_data.rst +++ /dev/null @@ -1,42 +0,0 @@ - -nghttp2_submit_data -=================== - -Synopsis --------- - -*#include * - -.. function:: int nghttp2_submit_data(nghttp2_session *session, uint8_t flags, int32_t stream_id, const nghttp2_data_provider *data_prd) - - - Submits one or more DATA frames to the stream *stream_id*. The - data to be sent are provided by *data_prd*. If *flags* contains - :macro:`NGHTTP2_FLAG_END_STREAM`, the last DATA frame has END_STREAM - flag set. - - This function does not take ownership of the *data_prd*. The - function copies the members of the *data_prd*. - - This function returns 0 if it succeeds, or one of the following - negative error codes: - - :macro:`NGHTTP2_ERR_NOMEM` - Out of memory. - :macro:`NGHTTP2_ERR_DATA_EXIST` - DATA has been already submitted and not fully processed yet. - :macro:`NGHTTP2_ERR_INVALID_ARGUMENT` - The *stream_id* is 0. - :macro:`NGHTTP2_ERR_STREAM_CLOSED` - The stream was alreay closed; or the *stream_id* is invalid. - - .. note:: - - Currently, only one data is allowed for a stream at a time. - Submitting data more than once before first data is finished - results in :macro:`NGHTTP2_ERR_DATA_EXIST` error code. The - earliest callback which tells that previous data is done is - :type:`nghttp2_on_frame_send_callback`. In side that callback, - new data can be submitted using `nghttp2_submit_data()`. Of - course, all data except for last one must not have - :macro:`NGHTTP2_FLAG_END_STREAM` flag set in *flags*. diff --git a/doc/nghttp2_submit_goaway.rst b/doc/nghttp2_submit_goaway.rst deleted file mode 100644 index 93a99f1..0000000 --- a/doc/nghttp2_submit_goaway.rst +++ /dev/null @@ -1,53 +0,0 @@ - -nghttp2_submit_goaway -===================== - -Synopsis --------- - -*#include * - -.. function:: int nghttp2_submit_goaway(nghttp2_session *session, uint8_t flags, int32_t last_stream_id, uint32_t error_code, const uint8_t *opaque_data, size_t opaque_data_len) - - - Submits GOAWAY frame with the last stream ID *last_stream_id* and - the error code *error_code*. - - The pre-defined error code is one of :macro:`nghttp2_error_code`. - - The *flags* is currently ignored and should be - :macro:`NGHTTP2_FLAG_NONE`. - - The *last_stream_id* is peer's stream ID or 0. So if *session* is - initialized as client, *last_stream_id* must be even or 0. If - *session* is initialized as server, *last_stream_id* must be odd or - 0. - - The HTTP/2 specification says last_stream_id must not be increased - from the value previously sent. So the actual value sent as - last_stream_id is the minimum value between the given - *last_stream_id* and the last_stream_id previously sent to the - peer. - - If the *opaque_data* is not ``NULL`` and *opaque_data_len* is not - zero, those data will be sent as additional debug data. The - library makes a copy of the memory region pointed by *opaque_data* - with the length *opaque_data_len*, so the caller does not need to - keep this memory after the return of this function. If the - *opaque_data_len* is 0, the *opaque_data* could be ``NULL``. - - After successful transmission of GOAWAY, following things happen. - All incoming streams having strictly more than *last_stream_id* are - closed. All incoming HEADERS which starts new stream are simply - ignored. After all active streams are handled, both - `nghttp2_session_want_read()` and `nghttp2_session_want_write()` - return 0 and the application can close session. - - This function returns 0 if it succeeds, or one of the following - negative error codes: - - :macro:`NGHTTP2_ERR_NOMEM` - Out of memory. - :macro:`NGHTTP2_ERR_INVALID_ARGUMENT` - The *opaque_data_len* is too large; the *last_stream_id* is - invalid. diff --git a/doc/nghttp2_submit_headers.rst b/doc/nghttp2_submit_headers.rst deleted file mode 100644 index f328bad..0000000 --- a/doc/nghttp2_submit_headers.rst +++ /dev/null @@ -1,80 +0,0 @@ - -nghttp2_submit_headers -====================== - -Synopsis --------- - -*#include * - -.. function:: int32_t nghttp2_submit_headers(nghttp2_session *session, uint8_t flags, int32_t stream_id, const nghttp2_priority_spec *pri_spec, const nghttp2_nv *nva, size_t nvlen, void *stream_user_data) - - - Submits HEADERS frame. The *flags* is bitwise OR of the - following values: - - * :macro:`NGHTTP2_FLAG_END_STREAM` - - If *flags* includes :macro:`NGHTTP2_FLAG_END_STREAM`, this frame has - END_STREAM flag set. - - The library handles the CONTINUATION frame internally and it - correctly sets END_HEADERS to the last sequence of the PUSH_PROMISE - or CONTINUATION frame. - - If the *stream_id* is -1, this frame is assumed as request (i.e., - request HEADERS frame which opens new stream). In this case, the - assigned stream ID will be returned. Otherwise, specify stream ID - in *stream_id*. - - The *pri_spec* is priority specification of this request. ``NULL`` - means the default priority (see - `nghttp2_priority_spec_default_init()`). To specify the priority, - use `nghttp2_priority_spec_init()`. If *pri_spec* is not ``NULL``, - this function will copy its data members. - - The ``pri_spec->weight`` must be in [:macro:`NGHTTP2_MIN_WEIGHT`, - :macro:`NGHTTP2_MAX_WEIGHT`], inclusive. If ``pri_spec->weight`` is - strictly less than :macro:`NGHTTP2_MIN_WEIGHT`, it becomes - :macro:`NGHTTP2_MIN_WEIGHT`. If it is strictly greater than - :macro:`NGHTTP2_MAX_WEIGHT`, it becomes :macro:`NGHTTP2_MAX_WEIGHT`. - - The *nva* is an array of name/value pair :type:`nghttp2_nv` with - *nvlen* elements. The application is responsible to include - required pseudo-header fields (header field whose name starts with - ":") in *nva* and must place pseudo-headers before regular header - fields. - - This function creates copies of all name/value pairs in *nva*. It - also lower-cases all names in *nva*. The order of elements in - *nva* is preserved. - - The *stream_user_data* is a pointer to an arbitrary data which is - associated to the stream this frame will open. Therefore it is - only used if this frame opens streams, in other words, it changes - stream state from idle or reserved to open. - - This function is low-level in a sense that the application code can - specify flags directly. For usual HTTP request, - `nghttp2_submit_request()` is useful. - - This function returns newly assigned stream ID if it succeeds and - *stream_id* is -1. Otherwise, this function returns 0 if it - succeeds, or one of the following negative error codes: - - :macro:`NGHTTP2_ERR_NOMEM` - Out of memory. - :macro:`NGHTTP2_ERR_STREAM_ID_NOT_AVAILABLE` - No stream ID is available because maximum stream ID was - reached. - :macro:`NGHTTP2_ERR_INVALID_ARGUMENT` - The *stream_id* is 0. - - .. warning:: - - This function returns assigned stream ID if it succeeds and - *stream_id* is -1. But that stream is not opened yet. The - application must not submit frame to that stream ID before - :type:`nghttp2_before_frame_send_callback` is called for this - frame. - diff --git a/doc/nghttp2_submit_ping.rst b/doc/nghttp2_submit_ping.rst deleted file mode 100644 index 7c6ec8f..0000000 --- a/doc/nghttp2_submit_ping.rst +++ /dev/null @@ -1,29 +0,0 @@ - -nghttp2_submit_ping -=================== - -Synopsis --------- - -*#include * - -.. function:: int nghttp2_submit_ping(nghttp2_session *session, uint8_t flags, const uint8_t *opaque_data) - - - Submits PING frame. You don't have to send PING back when you - received PING frame. The library automatically submits PING frame - in this case. - - The *flags* is currently ignored and should be - :macro:`NGHTTP2_FLAG_NONE`. - - If the *opaque_data* is non ``NULL``, then it should point to the 8 - bytes array of memory to specify opaque data to send with PING - frame. If the *opaque_data* is ``NULL``, zero-cleared 8 bytes will - be sent as opaque data. - - This function returns 0 if it succeeds, or one of the following - negative error codes: - - :macro:`NGHTTP2_ERR_NOMEM` - Out of memory. diff --git a/doc/nghttp2_submit_priority.rst b/doc/nghttp2_submit_priority.rst deleted file mode 100644 index 2fda48a..0000000 --- a/doc/nghttp2_submit_priority.rst +++ /dev/null @@ -1,37 +0,0 @@ - -nghttp2_submit_priority -======================= - -Synopsis --------- - -*#include * - -.. function:: int nghttp2_submit_priority(nghttp2_session *session, uint8_t flags, int32_t stream_id, const nghttp2_priority_spec *pri_spec) - - - Submits PRIORITY frame to change the priority of stream *stream_id* - to the priority specification *pri_spec*. - - The *flags* is currently ignored and should be - :macro:`NGHTTP2_FLAG_NONE`. - - The *pri_spec* is priority specification of this request. ``NULL`` - is not allowed for this function. To specify the priority, use - `nghttp2_priority_spec_init()`. This function will copy its data - members. - - The ``pri_spec->weight`` must be in [:macro:`NGHTTP2_MIN_WEIGHT`, - :macro:`NGHTTP2_MAX_WEIGHT`], inclusive. If ``pri_spec->weight`` is - strictly less than :macro:`NGHTTP2_MIN_WEIGHT`, it becomes - :macro:`NGHTTP2_MIN_WEIGHT`. If it is strictly greater than - :macro:`NGHTTP2_MAX_WEIGHT`, it becomes :macro:`NGHTTP2_MAX_WEIGHT`. - - This function returns 0 if it succeeds, or one of the following - negative error codes: - - :macro:`NGHTTP2_ERR_NOMEM` - Out of memory. - :macro:`NGHTTP2_ERR_INVALID_ARGUMENT` - The *stream_id* is 0; or the *pri_spec* is NULL; or trying to - depend on itself. diff --git a/doc/nghttp2_submit_push_promise.rst b/doc/nghttp2_submit_push_promise.rst deleted file mode 100644 index 8d66933..0000000 --- a/doc/nghttp2_submit_push_promise.rst +++ /dev/null @@ -1,65 +0,0 @@ - -nghttp2_submit_push_promise -=========================== - -Synopsis --------- - -*#include * - -.. function:: int32_t nghttp2_submit_push_promise(nghttp2_session *session, uint8_t flags, int32_t stream_id, const nghttp2_nv *nva, size_t nvlen, void *promised_stream_user_data) - - - Submits PUSH_PROMISE frame. - - The *flags* is currently ignored. The library handles the - CONTINUATION frame internally and it correctly sets END_HEADERS to - the last sequence of the PUSH_PROMISE or CONTINUATION frame. - - The *stream_id* must be client initiated stream ID. - - The *nva* is an array of name/value pair :type:`nghttp2_nv` with - *nvlen* elements. The application is responsible to include - required pseudo-header fields (header field whose name starts with - ":") in *nva* and must place pseudo-headers before regular header - fields. - - This function creates copies of all name/value pairs in *nva*. It - also lower-cases all names in *nva*. The order of elements in - *nva* is preserved. - - The *promised_stream_user_data* is a pointer to an arbitrary data - which is associated to the promised stream this frame will open and - make it in reserved state. It is available using - `nghttp2_session_get_stream_user_data()`. The application can - access it in :type:`nghttp2_before_frame_send_callback` and - :type:`nghttp2_on_frame_send_callback` of this frame. - - The client side is not allowed to use this function. - - To submit response headers and data, use - `nghttp2_submit_response()`. - - This function returns assigned promised stream ID if it succeeds, - or one of the following negative error codes: - - :macro:`NGHTTP2_ERR_NOMEM` - Out of memory. - :macro:`NGHTTP2_ERR_PROTO` - This function was invoked when *session* is initialized as - client. - :macro:`NGHTTP2_ERR_STREAM_ID_NOT_AVAILABLE` - No stream ID is available because maximum stream ID was - reached. - :macro:`NGHTTP2_ERR_INVALID_ARGUMENT` - The *stream_id* is 0; The *stream_id* does not designate stream - that peer initiated. - - .. warning:: - - This function returns assigned promised stream ID if it succeeds. - But that stream is not opened yet. The application must not - submit frame to that stream ID before - :type:`nghttp2_before_frame_send_callback` is called for this - frame. - diff --git a/doc/nghttp2_submit_request.rst b/doc/nghttp2_submit_request.rst deleted file mode 100644 index a4b36dd..0000000 --- a/doc/nghttp2_submit_request.rst +++ /dev/null @@ -1,68 +0,0 @@ - -nghttp2_submit_request -====================== - -Synopsis --------- - -*#include * - -.. function:: int32_t nghttp2_submit_request(nghttp2_session *session, const nghttp2_priority_spec *pri_spec, const nghttp2_nv *nva, size_t nvlen, const nghttp2_data_provider *data_prd, void *stream_user_data) - - - Submits HEADERS frame and optionally one or more DATA frames. - - The *pri_spec* is priority specification of this request. ``NULL`` - means the default priority (see - `nghttp2_priority_spec_default_init()`). To specify the priority, - use `nghttp2_priority_spec_init()`. If *pri_spec* is not ``NULL``, - this function will copy its data members. - - The ``pri_spec->weight`` must be in [:macro:`NGHTTP2_MIN_WEIGHT`, - :macro:`NGHTTP2_MAX_WEIGHT`], inclusive. If ``pri_spec->weight`` is - strictly less than :macro:`NGHTTP2_MIN_WEIGHT`, it becomes - :macro:`NGHTTP2_MIN_WEIGHT`. If it is strictly greater than - :macro:`NGHTTP2_MAX_WEIGHT`, it becomes :macro:`NGHTTP2_MAX_WEIGHT`. - - The *nva* is an array of name/value pair :type:`nghttp2_nv` with - *nvlen* elements. The application is responsible to include - required pseudo-header fields (header field whose name starts with - ":") in *nva* and must place pseudo-headers before regular header - fields. - - This function creates copies of all name/value pairs in *nva*. It - also lower-cases all names in *nva*. The order of elements in - *nva* is preserved. - - HTTP/2 specification has requirement about header fields in the - request HEADERS. See the specification for more details. - - If *data_prd* is not ``NULL``, it provides data which will be sent - in subsequent DATA frames. In this case, a method that allows - request message bodies - (https://tools.ietf.org/html/rfc7231#section-4) must be specified - with ``:method`` key in *nva* (e.g. ``POST``). This function does - not take ownership of the *data_prd*. The function copies the - members of the *data_prd*. If *data_prd* is ``NULL``, HEADERS have - END_STREAM set. The *stream_user_data* is data associated to the - stream opened by this request and can be an arbitrary pointer, - which can be retrieved later by - `nghttp2_session_get_stream_user_data()`. - - This function returns assigned stream ID if it succeeds, or one of - the following negative error codes: - - :macro:`NGHTTP2_ERR_NOMEM` - Out of memory. - :macro:`NGHTTP2_ERR_STREAM_ID_NOT_AVAILABLE` - No stream ID is available because maximum stream ID was - reached. - - .. warning:: - - This function returns assigned stream ID if it succeeds. But - that stream is not opened yet. The application must not submit - frame to that stream ID before - :type:`nghttp2_before_frame_send_callback` is called for this - frame. - diff --git a/doc/nghttp2_submit_response.rst b/doc/nghttp2_submit_response.rst deleted file mode 100644 index f01d892..0000000 --- a/doc/nghttp2_submit_response.rst +++ /dev/null @@ -1,57 +0,0 @@ - -nghttp2_submit_response -======================= - -Synopsis --------- - -*#include * - -.. function:: int nghttp2_submit_response(nghttp2_session *session, int32_t stream_id, const nghttp2_nv *nva, size_t nvlen, const nghttp2_data_provider *data_prd) - - - Submits response HEADERS frame and optionally one or more DATA - frames against the stream *stream_id*. - - The *nva* is an array of name/value pair :type:`nghttp2_nv` with - *nvlen* elements. The application is responsible to include - required pseudo-header fields (header field whose name starts with - ":") in *nva* and must place pseudo-headers before regular header - fields. - - This function creates copies of all name/value pairs in *nva*. It - also lower-cases all names in *nva*. The order of elements in - *nva* is preserved. - - HTTP/2 specification has requirement about header fields in the - response HEADERS. See the specification for more details. - - If *data_prd* is not ``NULL``, it provides data which will be sent - in subsequent DATA frames. This function does not take ownership - of the *data_prd*. The function copies the members of the - *data_prd*. If *data_prd* is ``NULL``, HEADERS will have - END_STREAM flag set. - - This method can be used as normal HTTP response and push response. - When pushing a resource using this function, the *session* must be - configured using `nghttp2_session_server_new()` or its variants and - the target stream denoted by the *stream_id* must be reserved using - `nghttp2_submit_push_promise()`. - - To send non-final response headers (e.g., HTTP status 101), don't - use this function because this function half-closes the outbound - stream. Instead, use `nghttp2_submit_headers()` for this purpose. - - This function returns 0 if it succeeds, or one of the following - negative error codes: - - :macro:`NGHTTP2_ERR_NOMEM` - Out of memory. - :macro:`NGHTTP2_ERR_INVALID_ARGUMENT` - The *stream_id* is 0. - - .. warning:: - - Calling this function twice for the same stream ID may lead to - program crash. It is generally considered to a programming error - to commit response twice. diff --git a/doc/nghttp2_submit_rst_stream.rst b/doc/nghttp2_submit_rst_stream.rst deleted file mode 100644 index a179424..0000000 --- a/doc/nghttp2_submit_rst_stream.rst +++ /dev/null @@ -1,27 +0,0 @@ - -nghttp2_submit_rst_stream -========================= - -Synopsis --------- - -*#include * - -.. function:: int nghttp2_submit_rst_stream(nghttp2_session *session, uint8_t flags, int32_t stream_id, uint32_t error_code) - - - Submits RST_STREAM frame to cancel/reject the stream *stream_id* - with the error code *error_code*. - - The pre-defined error code is one of :macro:`nghttp2_error_code`. - - The *flags* is currently ignored and should be - :macro:`NGHTTP2_FLAG_NONE`. - - This function returns 0 if it succeeds, or one of the following - negative error codes: - - :macro:`NGHTTP2_ERR_NOMEM` - Out of memory. - :macro:`NGHTTP2_ERR_INVALID_ARGUMENT` - The *stream_id* is 0. diff --git a/doc/nghttp2_submit_settings.rst b/doc/nghttp2_submit_settings.rst deleted file mode 100644 index dc4df55..0000000 --- a/doc/nghttp2_submit_settings.rst +++ /dev/null @@ -1,41 +0,0 @@ - -nghttp2_submit_settings -======================= - -Synopsis --------- - -*#include * - -.. function:: int nghttp2_submit_settings(nghttp2_session *session, uint8_t flags, const nghttp2_settings_entry *iv, size_t niv) - - - Stores local settings and submits SETTINGS frame. The *iv* is the - pointer to the array of :type:`nghttp2_settings_entry`. The *niv* - indicates the number of :type:`nghttp2_settings_entry`. - - The *flags* is currently ignored and should be - :macro:`NGHTTP2_FLAG_NONE`. - - This function does not take ownership of the *iv*. This function - copies all the elements in the *iv*. - - While updating individual stream's local window size, if the window - size becomes strictly larger than NGHTTP2_MAX_WINDOW_SIZE, - RST_STREAM is issued against such a stream. - - SETTINGS with :macro:`NGHTTP2_FLAG_ACK` is automatically submitted - by the library and application could not send it at its will. - - This function returns 0 if it succeeds, or one of the following - negative error codes: - - :macro:`NGHTTP2_ERR_INVALID_ARGUMENT` - The *iv* contains invalid value (e.g., initial window size - strictly greater than (1 << 31) - 1. - :macro:`NGHTTP2_ERR_TOO_MANY_INFLIGHT_SETTINGS` - There is already another in-flight SETTINGS. Note that the - current implementation only allows 1 in-flight SETTINGS frame - without ACK flag set. - :macro:`NGHTTP2_ERR_NOMEM` - Out of memory. diff --git a/doc/nghttp2_submit_shutdown_notice.rst b/doc/nghttp2_submit_shutdown_notice.rst deleted file mode 100644 index d6c28b8..0000000 --- a/doc/nghttp2_submit_shutdown_notice.rst +++ /dev/null @@ -1,43 +0,0 @@ - -nghttp2_submit_shutdown_notice -============================== - -Synopsis --------- - -*#include * - -.. function:: int nghttp2_submit_shutdown_notice(nghttp2_session *session) - - - Signals to the client that the server started graceful shutdown - procedure. - - This function is only usable for server. If this function is - called with client side session, this function returns - :macro:`NGHTTP2_ERR_INVALID_STATE`. - - To gracefully shutdown HTTP/2 session, server should call this - function to send GOAWAY with last_stream_id (1u << 31) - 1. And - after some delay (e.g., 1 RTT), send another GOAWAY with the stream - ID that the server has some processing using - `nghttp2_submit_goaway()`. See also - `nghttp2_session_get_last_proc_stream_id()`. - - Unlike `nghttp2_submit_goaway()`, this function just sends GOAWAY - and does nothing more. This is a mere indication to the client - that session shutdown is imminent. The application should call - `nghttp2_submit_goaway()` with appropriate last_stream_id after - this call. - - If one or more GOAWAY frame have been already sent by either - `nghttp2_submit_goaway()` or `nghttp2_session_terminate_session()`, - this function has no effect. - - This function returns 0 if it succeeds, or one of the following - negative error codes: - - :macro:`NGHTTP2_ERR_NOMEM` - Out of memory. - :macro:`NGHTTP2_ERR_INVALID_STATE` - The *session* is initialized as client. diff --git a/doc/nghttp2_submit_trailer.rst b/doc/nghttp2_submit_trailer.rst deleted file mode 100644 index 420c6b9..0000000 --- a/doc/nghttp2_submit_trailer.rst +++ /dev/null @@ -1,52 +0,0 @@ - -nghttp2_submit_trailer -====================== - -Synopsis --------- - -*#include * - -.. function:: int nghttp2_submit_trailer(nghttp2_session *session, int32_t stream_id, const nghttp2_nv *nva, size_t nvlen) - - - Submits trailer HEADERS against the stream *stream_id*. - - The *nva* is an array of name/value pair :type:`nghttp2_nv` with - *nvlen* elements. The application is responsible not to include - required pseudo-header fields (header field whose name starts with - ":") in *nva*. - - This function creates copies of all name/value pairs in *nva*. It - also lower-cases all names in *nva*. The order of elements in - *nva* is preserved. - - For server, trailer must be followed by response HEADERS or - response DATA. The library does not check that response HEADERS - has already sent and if `nghttp2_submit_trailer()` is called before - any response HEADERS submission (usually by - `nghttp2_submit_response()`), the content of *nva* will be sent as - reponse headers, which will result in error. - - This function has the same effect with `nghttp2_submit_headers()`, - with flags = :macro:`NGHTTP2_FLAG_END_HEADERS` and both pri_spec and - stream_user_data to NULL. - - To submit trailer after `nghttp2_submit_response()` is called, the - application has to specify :type:`nghttp2_data_provider` to - `nghttp2_submit_response()`. In side - :type:`nghttp2_data_source_read_callback`, when setting - :macro:`NGHTTP2_DATA_FLAG_EOF`, also set - :macro:`NGHTTP2_DATA_FLAG_NO_END_STREAM`. After that, the - application can send trailer using `nghttp2_submit_trailer()`. - `nghttp2_submit_trailer()` can be used inside - :type:`nghttp2_data_source_read_callback`. - - This function returns 0 if it succeeds and *stream_id* is -1. - Otherwise, this function returns 0 if it succeeds, or one of the - following negative error codes: - - :macro:`NGHTTP2_ERR_NOMEM` - Out of memory. - :macro:`NGHTTP2_ERR_INVALID_ARGUMENT` - The *stream_id* is 0. diff --git a/doc/nghttp2_submit_window_update.rst b/doc/nghttp2_submit_window_update.rst deleted file mode 100644 index 4410444..0000000 --- a/doc/nghttp2_submit_window_update.rst +++ /dev/null @@ -1,43 +0,0 @@ - -nghttp2_submit_window_update -============================ - -Synopsis --------- - -*#include * - -.. function:: int nghttp2_submit_window_update(nghttp2_session *session, uint8_t flags, int32_t stream_id, int32_t window_size_increment) - - - Submits WINDOW_UPDATE frame. - - The *flags* is currently ignored and should be - :macro:`NGHTTP2_FLAG_NONE`. - - The *stream_id* is the stream ID to send this WINDOW_UPDATE. To - send connection level WINDOW_UPDATE, specify 0 to *stream_id*. - - If the *window_size_increment* is positive, the WINDOW_UPDATE with - that value as window_size_increment is queued. If the - *window_size_increment* is larger than the received bytes from the - remote endpoint, the local window size is increased by that - difference. - - If the *window_size_increment* is negative, the local window size - is decreased by -*window_size_increment*. If automatic - WINDOW_UPDATE is enabled - (`nghttp2_option_set_no_auto_window_update()`), and the library - decided that the WINDOW_UPDATE should be submitted, then - WINDOW_UPDATE is queued with the current received bytes count. - - If the *window_size_increment* is 0, the function does nothing and - returns 0. - - This function returns 0 if it succeeds, or one of the following - negative error codes: - - :macro:`NGHTTP2_ERR_FLOW_CONTROL` - The local window size overflow or gets negative. - :macro:`NGHTTP2_ERR_NOMEM` - Out of memory. diff --git a/doc/nghttp2_version.rst b/doc/nghttp2_version.rst deleted file mode 100644 index 3f16c54..0000000 --- a/doc/nghttp2_version.rst +++ /dev/null @@ -1,17 +0,0 @@ - -nghttp2_version -=============== - -Synopsis --------- - -*#include * - -.. function:: nghttp2_info *nghttp2_version(int least_version) - - - Returns a pointer to a nghttp2_info struct with version information - about the run-time library in use. The *least_version* argument - can be set to a 24 bit numerical value for the least accepted - version number and if the condition is not met, this function will - return a ``NULL``. Pass in 0 to skip the version checking. diff --git a/doc/nghttp2ver.h.rst.in b/doc/nghttp2ver.h.rst.in deleted file mode 100644 index c6aa779..0000000 --- a/doc/nghttp2ver.h.rst.in +++ /dev/null @@ -1,4 +0,0 @@ -nghttp2ver.h -============ - -.. literalinclude:: @top_builddir@/lib/includes/nghttp2/nghttp2ver.h diff --git a/doc/nghttpd.1 b/doc/nghttpd.1 deleted file mode 100644 index 24e7bb9..0000000 --- a/doc/nghttpd.1 +++ /dev/null @@ -1,194 +0,0 @@ -.\" Man page generated from reStructuredText. -. -.TH "NGHTTPD" "1" "May 16, 2015" "1.0.0" "nghttp2" -.SH NAME -nghttpd \- HTTP/2 experimental server -. -.nr rst2man-indent-level 0 -. -.de1 rstReportMargin -\\$1 \\n[an-margin] -level \\n[rst2man-indent-level] -level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] -- -\\n[rst2man-indent0] -\\n[rst2man-indent1] -\\n[rst2man-indent2] -.. -.de1 INDENT -.\" .rstReportMargin pre: -. RS \\$1 -. nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] -. nr rst2man-indent-level +1 -.\" .rstReportMargin post: -.. -.de UNINDENT -. RE -.\" indent \\n[an-margin] -.\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] -.nr rst2man-indent-level -1 -.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] -.in \\n[rst2man-indent\\n[rst2man-indent-level]]u -.. -.SH SYNOPSIS -.sp -\fBnghttpd\fP [OPTION]... [ ] -.SH DESCRIPTION -.sp -HTTP/2 experimental server -.INDENT 0.0 -.TP -.B -Specify listening port number. -.UNINDENT -.INDENT 0.0 -.TP -.B -Set path to server\(aqs private key. Required unless -\fI\%\-\-no\-tls\fP is specified. -.UNINDENT -.INDENT 0.0 -.TP -.B -Set path to server\(aqs certificate. Required unless -\fI\%\-\-no\-tls\fP is specified. -.UNINDENT -.SH OPTIONS -.INDENT 0.0 -.TP -.B \-a, \-\-address= -The address to bind to. If not specified the default IP -address determined by getaddrinfo is used. -.UNINDENT -.INDENT 0.0 -.TP -.B \-D, \-\-daemon -Run in a background. If \fI\%\-D\fP is used, the current working -directory is changed to \(aq\fI/\fP\(aq. Therefore if this option -is used, \fI\%\-d\fP option must be specified. -.UNINDENT -.INDENT 0.0 -.TP -.B \-V, \-\-verify\-client -The server sends a client certificate request. If the -client did not return a certificate, the handshake is -terminated. Currently, this option just requests a -client certificate and does not verify it. -.UNINDENT -.INDENT 0.0 -.TP -.B \-d, \-\-htdocs= -Specify document root. If this option is not specified, -the document root is the current working directory. -.UNINDENT -.INDENT 0.0 -.TP -.B \-v, \-\-verbose -Print debug information such as reception/ transmission -of frames and name/value pairs. -.UNINDENT -.INDENT 0.0 -.TP -.B \-\-no\-tls -Disable SSL/TLS. -.UNINDENT -.INDENT 0.0 -.TP -.B \-c, \-\-header\-table\-size= -Specify decoder header table size. -.UNINDENT -.INDENT 0.0 -.TP -.B \-\-color -Force colored log output. -.UNINDENT -.INDENT 0.0 -.TP -.B \-p, \-\-push== -Push resources s when is requested. -This option can be used repeatedly to specify multiple -push configurations. and s are -relative to document root. See \fI\%\-\-htdocs\fP option. -Example: \fI\%\-p\fP/=/foo.png \fI\%\-p\fP/doc=/bar.css -.UNINDENT -.INDENT 0.0 -.TP -.B \-b, \-\-padding= -Add at most bytes to a frame payload as padding. -Specify 0 to disable padding. -.UNINDENT -.INDENT 0.0 -.TP -.B \-m, \-\-max\-concurrent\-streams= -Set the maximum number of the concurrent streams in one -HTTP/2 session. -.sp -Default: \fB100\fP -.UNINDENT -.INDENT 0.0 -.TP -.B \-n, \-\-workers= -Set the number of worker threads. -.sp -Default: \fB1\fP -.UNINDENT -.INDENT 0.0 -.TP -.B \-e, \-\-error\-gzip -Make error response gzipped. -.UNINDENT -.INDENT 0.0 -.TP -.B \-\-dh\-param\-file= -Path to file that contains DH parameters in PEM format. -Without this option, DHE cipher suites are not -available. -.UNINDENT -.INDENT 0.0 -.TP -.B \-\-early\-response -Start sending response when request HEADERS is received, -rather than complete request is received. -.UNINDENT -.INDENT 0.0 -.TP -.B \-\-trailer=
      -Add a trailer header to a response.
      must not -include pseudo header field (header field name starting -with \(aq:\(aq). The trailer is sent only if a response has -body part. Example: \fI\%\-\-trailer\fP \(aqfoo: bar\(aq. -.UNINDENT -.INDENT 0.0 -.TP -.B \-\-hexdump -Display the incoming traffic in hexadecimal (Canonical -hex+ASCII display). If SSL/TLS is used, decrypted data -are used. -.UNINDENT -.INDENT 0.0 -.TP -.B \-\-echo\-upload -Send back uploaded content if method is POST or PUT. -.UNINDENT -.INDENT 0.0 -.TP -.B \-\-version -Display version information and exit. -.UNINDENT -.INDENT 0.0 -.TP -.B \-h, \-\-help -Display this help and exit. -.UNINDENT -.sp -The argument is an integer and an optional unit (e.g., 10K is -10 * 1024). Units are K, M and G (powers of 1024). -.SH SEE ALSO -.sp -\fInghttp(1)\fP, \fInghttpx(1)\fP, \fIh2load(1)\fP -.SH AUTHOR -Tatsuhiro Tsujikawa -.SH COPYRIGHT -2012, 2015, Tatsuhiro Tsujikawa -.\" Generated by docutils manpage writer. -. diff --git a/doc/nghttpd.1.rst b/doc/nghttpd.1.rst deleted file mode 100644 index e9ac30d..0000000 --- a/doc/nghttpd.1.rst +++ /dev/null @@ -1,151 +0,0 @@ - -.. GENERATED by help2rst.py. DO NOT EDIT DIRECTLY. - -.. program:: nghttpd - -nghttpd(1) -========== - -SYNOPSIS --------- - -**nghttpd** [OPTION]... [ ] - -DESCRIPTION ------------ - -HTTP/2 experimental server - -.. describe:: - - Specify listening port number. - -.. describe:: - - - Set path to server's private key. Required unless - :option:`--no-tls` is specified. - -.. describe:: - - Set path to server's certificate. Required unless - :option:`--no-tls` is specified. - -OPTIONS -------- - -.. option:: -a, --address= - - The address to bind to. If not specified the default IP - address determined by getaddrinfo is used. - -.. option:: -D, --daemon - - Run in a background. If :option:`-D` is used, the current working - directory is changed to '*/*'. Therefore if this option - is used, :option:`-d` option must be specified. - -.. option:: -V, --verify-client - - The server sends a client certificate request. If the - client did not return a certificate, the handshake is - terminated. Currently, this option just requests a - client certificate and does not verify it. - -.. option:: -d, --htdocs= - - Specify document root. If this option is not specified, - the document root is the current working directory. - -.. option:: -v, --verbose - - Print debug information such as reception/ transmission - of frames and name/value pairs. - -.. option:: --no-tls - - Disable SSL/TLS. - -.. option:: -c, --header-table-size= - - Specify decoder header table size. - -.. option:: --color - - Force colored log output. - -.. option:: -p, --push== - - Push resources s when is requested. - This option can be used repeatedly to specify multiple - push configurations. and s are - relative to document root. See :option:`--htdocs` option. - Example: :option:`-p`\/=/foo.png :option:`-p`\/doc=/bar.css - -.. option:: -b, --padding= - - Add at most bytes to a frame payload as padding. - Specify 0 to disable padding. - -.. option:: -m, --max-concurrent-streams= - - Set the maximum number of the concurrent streams in one - HTTP/2 session. - - Default: ``100`` - -.. option:: -n, --workers= - - Set the number of worker threads. - - Default: ``1`` - -.. option:: -e, --error-gzip - - Make error response gzipped. - -.. option:: --dh-param-file= - - Path to file that contains DH parameters in PEM format. - Without this option, DHE cipher suites are not - available. - -.. option:: --early-response - - Start sending response when request HEADERS is received, - rather than complete request is received. - -.. option:: --trailer=
      - - Add a trailer header to a response.
      must not - include pseudo header field (header field name starting - with ':'). The trailer is sent only if a response has - body part. Example: :option:`--trailer` 'foo: bar'. - -.. option:: --hexdump - - Display the incoming traffic in hexadecimal (Canonical - hex+ASCII display). If SSL/TLS is used, decrypted data - are used. - -.. option:: --echo-upload - - Send back uploaded content if method is POST or PUT. - -.. option:: --version - - Display version information and exit. - -.. option:: -h, --help - - Display this help and exit. - - - -The argument is an integer and an optional unit (e.g., 10K is -10 * 1024). Units are K, M and G (powers of 1024). - -SEE ALSO --------- - -:manpage:`nghttp(1)`, :manpage:`nghttpx(1)`, :manpage:`h2load(1)` diff --git a/doc/nghttpx-howto.rst.in b/doc/nghttpx-howto.rst.in deleted file mode 100644 index 082ce51..0000000 --- a/doc/nghttpx-howto.rst.in +++ /dev/null @@ -1 +0,0 @@ -.. include:: @top_srcdir@/doc/sources/nghttpx-howto.rst diff --git a/doc/nghttpx.1 b/doc/nghttpx.1 deleted file mode 100644 index e42ec76..0000000 --- a/doc/nghttpx.1 +++ /dev/null @@ -1,899 +0,0 @@ -.\" Man page generated from reStructuredText. -. -.TH "NGHTTPX" "1" "May 16, 2015" "1.0.0" "nghttp2" -.SH NAME -nghttpx \- HTTP/2 experimental proxy -. -.nr rst2man-indent-level 0 -. -.de1 rstReportMargin -\\$1 \\n[an-margin] -level \\n[rst2man-indent-level] -level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] -- -\\n[rst2man-indent0] -\\n[rst2man-indent1] -\\n[rst2man-indent2] -.. -.de1 INDENT -.\" .rstReportMargin pre: -. RS \\$1 -. nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] -. nr rst2man-indent-level +1 -.\" .rstReportMargin post: -.. -.de UNINDENT -. RE -.\" indent \\n[an-margin] -.\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] -.nr rst2man-indent-level -1 -.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] -.in \\n[rst2man-indent\\n[rst2man-indent-level]]u -.. -.SH SYNOPSIS -.sp -\fBnghttpx\fP [OPTIONS]... [ ] -.SH DESCRIPTION -.sp -A reverse proxy for HTTP/2, HTTP/1 and SPDY. -.INDENT 0.0 -.TP -.B -Set path to server\(aqs private key. Required unless \fI\%\-p\fP, -\fI\%\-\-client\fP or \fI\%\-\-frontend\-no\-tls\fP are given. -.UNINDENT -.INDENT 0.0 -.TP -.B -Set path to server\(aqs certificate. Required unless \fI\%\-p\fP, -\fI\%\-\-client\fP or \fI\%\-\-frontend\-no\-tls\fP are given. To make OCSP -stapling work, this must be absolute path. -.UNINDENT -.SH OPTIONS -.sp -The options are categorized into several groups. -.SS Connections -.INDENT 0.0 -.TP -.B \-b, \-\-backend= -Set backend host and port. The multiple backend -addresses are accepted by repeating this option. UNIX -domain socket can be specified by prefixing path name -with "unix:" (e.g., unix:/var/run/backend.sock) -.sp -Default: \fB127.0.0.1,80\fP -.UNINDENT -.INDENT 0.0 -.TP -.B \-f, \-\-frontend= -Set frontend host and port. If is \(aq*\(aq, it -assumes all addresses including both IPv4 and IPv6. -UNIX domain socket can be specified by prefixing path -name with "unix:" (e.g., unix:/var/run/nghttpx.sock) -.sp -Default: \fB*,3000\fP -.UNINDENT -.INDENT 0.0 -.TP -.B \-\-backlog= -Set listen backlog size. -.sp -Default: \fB512\fP -.UNINDENT -.INDENT 0.0 -.TP -.B \-\-backend\-ipv4 -Resolve backend hostname to IPv4 address only. -.UNINDENT -.INDENT 0.0 -.TP -.B \-\-backend\-ipv6 -Resolve backend hostname to IPv6 address only. -.UNINDENT -.INDENT 0.0 -.TP -.B \-\-backend\-http\-proxy\-uri= -Specify proxy URI in the form -\fI\%http:/\fP/[:@]:. If a proxy -requires authentication, specify and . -Note that they must be properly percent\-encoded. This -proxy is used when the backend connection is HTTP/2. -First, make a CONNECT request to the proxy and it -connects to the backend on behalf of nghttpx. This -forms tunnel. After that, nghttpx performs SSL/TLS -handshake with the downstream through the tunnel. The -timeouts when connecting and making CONNECT request can -be specified by \fI\%\-\-backend\-read\-timeout\fP and -\fI\%\-\-backend\-write\-timeout\fP options. -.UNINDENT -.SS Performance -.INDENT 0.0 -.TP -.B \-n, \-\-workers= -Set the number of worker threads. -.sp -Default: \fB1\fP -.UNINDENT -.INDENT 0.0 -.TP -.B \-\-read\-rate= -Set maximum average read rate on frontend connection. -Setting 0 to this option means read rate is unlimited. -.sp -Default: \fB0\fP -.UNINDENT -.INDENT 0.0 -.TP -.B \-\-read\-burst= -Set maximum read burst size on frontend connection. -Setting 0 to this option means read burst size is -unlimited. -.sp -Default: \fB0\fP -.UNINDENT -.INDENT 0.0 -.TP -.B \-\-write\-rate= -Set maximum average write rate on frontend connection. -Setting 0 to this option means write rate is unlimited. -.sp -Default: \fB0\fP -.UNINDENT -.INDENT 0.0 -.TP -.B \-\-write\-burst= -Set maximum write burst size on frontend connection. -Setting 0 to this option means write burst size is -unlimited. -.sp -Default: \fB0\fP -.UNINDENT -.INDENT 0.0 -.TP -.B \-\-worker\-read\-rate= -Set maximum average read rate on frontend connection per -worker. Setting 0 to this option means read rate is -unlimited. Not implemented yet. -.sp -Default: \fB0\fP -.UNINDENT -.INDENT 0.0 -.TP -.B \-\-worker\-read\-burst= -Set maximum read burst size on frontend connection per -worker. Setting 0 to this option means read burst size -is unlimited. Not implemented yet. -.sp -Default: \fB0\fP -.UNINDENT -.INDENT 0.0 -.TP -.B \-\-worker\-write\-rate= -Set maximum average write rate on frontend connection -per worker. Setting 0 to this option means write rate -is unlimited. Not implemented yet. -.sp -Default: \fB0\fP -.UNINDENT -.INDENT 0.0 -.TP -.B \-\-worker\-write\-burst= -Set maximum write burst size on frontend connection per -worker. Setting 0 to this option means write burst size -is unlimited. Not implemented yet. -.sp -Default: \fB0\fP -.UNINDENT -.INDENT 0.0 -.TP -.B \-\-worker\-frontend\-connections= -Set maximum number of simultaneous connections frontend -accepts. Setting 0 means unlimited. -.sp -Default: \fB0\fP -.UNINDENT -.INDENT 0.0 -.TP -.B \-\-backend\-http2\-connections\-per\-worker= -Set maximum number of HTTP/2 connections per worker. -The default value is 0, which means the number of -backend addresses specified by \fI\%\-b\fP option. -.UNINDENT -.INDENT 0.0 -.TP -.B \-\-backend\-http1\-connections\-per\-host= -Set maximum number of backend concurrent HTTP/1 -connections per host. This option is meaningful when \fI\%\-s\fP -option is used. To limit the number of connections per -frontend for default mode, use -\fI\%\-\-backend\-http1\-connections\-per\-frontend\fP\&. -.sp -Default: \fB8\fP -.UNINDENT -.INDENT 0.0 -.TP -.B \-\-backend\-http1\-connections\-per\-frontend= -Set maximum number of backend concurrent HTTP/1 -connections per frontend. This option is only used for -default mode. 0 means unlimited. To limit the number -of connections per host for HTTP/2 or SPDY proxy mode -(\-s option), use \fI\%\-\-backend\-http1\-connections\-per\-host\fP\&. -.sp -Default: \fB0\fP -.UNINDENT -.INDENT 0.0 -.TP -.B \-\-rlimit\-nofile= -Set maximum number of open files (RLIMIT_NOFILE) to . -If 0 is given, nghttpx does not set the limit. -.sp -Default: \fB0\fP -.UNINDENT -.INDENT 0.0 -.TP -.B \-\-backend\-request\-buffer= -Set buffer size used to store backend request. -.sp -Default: \fB16K\fP -.UNINDENT -.INDENT 0.0 -.TP -.B \-\-backend\-response\-buffer= -Set buffer size used to store backend response. -.sp -Default: \fB16K\fP -.UNINDENT -.SS Timeout -.INDENT 0.0 -.TP -.B \-\-frontend\-http2\-read\-timeout= -Specify read timeout for HTTP/2 and SPDY frontend -connection. -.sp -Default: \fB3m\fP -.UNINDENT -.INDENT 0.0 -.TP -.B \-\-frontend\-read\-timeout= -Specify read timeout for HTTP/1.1 frontend connection. -.sp -Default: \fB3m\fP -.UNINDENT -.INDENT 0.0 -.TP -.B \-\-frontend\-write\-timeout= -Specify write timeout for all frontend connections. -.sp -Default: \fB30s\fP -.UNINDENT -.INDENT 0.0 -.TP -.B \-\-stream\-read\-timeout= -Specify read timeout for HTTP/2 and SPDY streams. 0 -means no timeout. -.sp -Default: \fB0\fP -.UNINDENT -.INDENT 0.0 -.TP -.B \-\-stream\-write\-timeout= -Specify write timeout for HTTP/2 and SPDY streams. 0 -means no timeout. -.sp -Default: \fB0\fP -.UNINDENT -.INDENT 0.0 -.TP -.B \-\-backend\-read\-timeout= -Specify read timeout for backend connection. -.sp -Default: \fB3m\fP -.UNINDENT -.INDENT 0.0 -.TP -.B \-\-backend\-write\-timeout= -Specify write timeout for backend connection. -.sp -Default: \fB30s\fP -.UNINDENT -.INDENT 0.0 -.TP -.B \-\-backend\-keep\-alive\-timeout= -Specify keep\-alive timeout for backend connection. -.sp -Default: \fB2s\fP -.UNINDENT -.INDENT 0.0 -.TP -.B \-\-listener\-disable\-timeout= -After accepting connection failed, connection listener -is disabled for a given amount of time. Specifying 0 -disables this feature. -.sp -Default: \fB0\fP -.UNINDENT -.SS SSL/TLS -.INDENT 0.0 -.TP -.B \-\-ciphers= -Set allowed cipher list. The format of the string is -described in OpenSSL ciphers(1). -.UNINDENT -.INDENT 0.0 -.TP -.B \-k, \-\-insecure -Don\(aqt verify backend server\(aqs certificate if \fI\%\-p\fP, -\fI\%\-\-client\fP or \fI\%\-\-http2\-bridge\fP are given and -\fI\%\-\-backend\-no\-tls\fP is not given. -.UNINDENT -.INDENT 0.0 -.TP -.B \-\-cacert= -Set path to trusted CA certificate file if \fI\%\-p\fP, \fI\%\-\-client\fP -or \fI\%\-\-http2\-bridge\fP are given and \fI\%\-\-backend\-no\-tls\fP is not -given. The file must be in PEM format. It can contain -multiple certificates. If the linked OpenSSL is -configured to load system wide certificates, they are -loaded at startup regardless of this option. -.UNINDENT -.INDENT 0.0 -.TP -.B \-\-private\-key\-passwd\-file= -Path to file that contains password for the server\(aqs -private key. If none is given and the private key is -password protected it\(aqll be requested interactively. -.UNINDENT -.INDENT 0.0 -.TP -.B \-\-subcert=: -Specify additional certificate and private key file. -nghttpx will choose certificates based on the hostname -indicated by client using TLS SNI extension. This -option can be used multiple times. To make OCSP -stapling work, must be absolute path. -.UNINDENT -.INDENT 0.0 -.TP -.B \-\-backend\-tls\-sni\-field= -Explicitly set the content of the TLS SNI extension. -This will default to the backend HOST name. -.UNINDENT -.INDENT 0.0 -.TP -.B \-\-dh\-param\-file= -Path to file that contains DH parameters in PEM format. -Without this option, DHE cipher suites are not -available. -.UNINDENT -.INDENT 0.0 -.TP -.B \-\-npn\-list= -Comma delimited list of ALPN protocol identifier sorted -in the order of preference. That means most desirable -protocol comes first. This is used in both ALPN and -NPN. The parameter must be delimited by a single comma -only and any white spaces are treated as a part of -protocol string. -.sp -Default: \fBh2,h2\-16,h2\-14,spdy/3.1,http/1.1\fP -.UNINDENT -.INDENT 0.0 -.TP -.B \-\-verify\-client -Require and verify client certificate. -.UNINDENT -.INDENT 0.0 -.TP -.B \-\-verify\-client\-cacert= -Path to file that contains CA certificates to verify -client certificate. The file must be in PEM format. It -can contain multiple certificates. -.UNINDENT -.INDENT 0.0 -.TP -.B \-\-client\-private\-key\-file= -Path to file that contains client private key used in -backend client authentication. -.UNINDENT -.INDENT 0.0 -.TP -.B \-\-client\-cert\-file= -Path to file that contains client certificate used in -backend client authentication. -.UNINDENT -.INDENT 0.0 -.TP -.B \-\-tls\-proto\-list= -Comma delimited list of SSL/TLS protocol to be enabled. -The following protocols are available: TLSv1.2, TLSv1.1 -and TLSv1.0. The name matching is done in -case\-insensitive manner. The parameter must be -delimited by a single comma only and any white spaces -are treated as a part of protocol string. -.sp -Default: \fBTLSv1.2,TLSv1.1\fP -.UNINDENT -.INDENT 0.0 -.TP -.B \-\-tls\-ticket\-key\-file= -Path to file that contains 48 bytes random data to -construct TLS session ticket parameters. This options -can be used repeatedly to specify multiple ticket -parameters. If several files are given, only the first -key is used to encrypt TLS session tickets. Other keys -are accepted but server will issue new session ticket -with first key. This allows session key rotation. -Please note that key rotation does not occur -automatically. User should rearrange files or change -options values and restart nghttpx gracefully. If -opening or reading given file fails, all loaded keys are -discarded and it is treated as if none of this option is -given. If this option is not given or an error occurred -while opening or reading a file, key is generated -automatically and renewed every 12hrs. At most 2 keys -are stored in memory. -.UNINDENT -.INDENT 0.0 -.TP -.B \-\-fetch\-ocsp\-response\-file= -Path to fetch\-ocsp\-response script file. It should be -absolute path. -.sp -Default: \fB/usr/local/share/nghttp2/fetch\-ocsp\-response\fP -.UNINDENT -.INDENT 0.0 -.TP -.B \-\-ocsp\-update\-interval= -Set interval to update OCSP response cache. -.sp -Default: \fB4h\fP -.UNINDENT -.INDENT 0.0 -.TP -.B \-\-no\-ocsp -Disable OCSP stapling. -.UNINDENT -.SS HTTP/2 and SPDY -.INDENT 0.0 -.TP -.B \-c, \-\-http2\-max\-concurrent\-streams= -Set the maximum number of the concurrent streams in one -HTTP/2 and SPDY session. -.sp -Default: \fB100\fP -.UNINDENT -.INDENT 0.0 -.TP -.B \-\-frontend\-http2\-window\-bits= -Sets the per\-stream initial window size of HTTP/2 SPDY -frontend connection. For HTTP/2, the size is 2**\-1. -For SPDY, the size is 2**. -.sp -Default: \fB16\fP -.UNINDENT -.INDENT 0.0 -.TP -.B \-\-frontend\-http2\-connection\-window\-bits= -Sets the per\-connection window size of HTTP/2 and SPDY -frontend connection. For HTTP/2, the size is -2**\-1. For SPDY, the size is 2**. -.sp -Default: \fB16\fP -.UNINDENT -.INDENT 0.0 -.TP -.B \-\-frontend\-no\-tls -Disable SSL/TLS on frontend connections. -.UNINDENT -.INDENT 0.0 -.TP -.B \-\-backend\-http2\-window\-bits= -Sets the initial window size of HTTP/2 backend -connection to 2**\-1. -.sp -Default: \fB16\fP -.UNINDENT -.INDENT 0.0 -.TP -.B \-\-backend\-http2\-connection\-window\-bits= -Sets the per\-connection window size of HTTP/2 backend -connection to 2**\-1. -.sp -Default: \fB16\fP -.UNINDENT -.INDENT 0.0 -.TP -.B \-\-backend\-no\-tls -Disable SSL/TLS on backend connections. -.UNINDENT -.INDENT 0.0 -.TP -.B \-\-http2\-no\-cookie\-crumbling -Don\(aqt crumble cookie header field. -.UNINDENT -.INDENT 0.0 -.TP -.B \-\-padding= -Add at most bytes to a HTTP/2 frame payload as -padding. Specify 0 to disable padding. This option is -meant for debugging purpose and not intended to enhance -protocol security. -.UNINDENT -.INDENT 0.0 -.TP -.B \-\-no\-server\-push -Disable HTTP/2 server push. Server push is only -supported by default mode and HTTP/2 frontend. SPDY -frontend does not support server push. -.UNINDENT -.SS Mode -.INDENT 0.0 -.TP -.B (default mode) -Accept HTTP/2, SPDY and HTTP/1.1 over SSL/TLS. If -\fI\%\-\-frontend\-no\-tls\fP is used, accept HTTP/2 and HTTP/1.1. -The incoming HTTP/1.1 connection can be upgraded to -HTTP/2 through HTTP Upgrade. The protocol to the -backend is HTTP/1.1. -.UNINDENT -.INDENT 0.0 -.TP -.B \-s, \-\-http2\-proxy -Like default mode, but enable secure proxy mode. -.UNINDENT -.INDENT 0.0 -.TP -.B \-\-http2\-bridge -Like default mode, but communicate with the backend in -HTTP/2 over SSL/TLS. Thus the incoming all connections -are converted to HTTP/2 connection and relayed to the -backend. See \fI\%\-\-backend\-http\-proxy\-uri\fP option if you are -behind the proxy and want to connect to the outside -HTTP/2 proxy. -.UNINDENT -.INDENT 0.0 -.TP -.B \-\-client -Accept HTTP/2 and HTTP/1.1 without SSL/TLS. The -incoming HTTP/1.1 connection can be upgraded to HTTP/2 -connection through HTTP Upgrade. The protocol to the -backend is HTTP/2. To use nghttpx as a forward proxy, -use \fI\%\-p\fP option instead. -.UNINDENT -.INDENT 0.0 -.TP -.B \-p, \-\-client\-proxy -Like \fI\%\-\-client\fP option, but it also requires the request -path from frontend must be an absolute URI, suitable for -use as a forward proxy. -.UNINDENT -.SS Logging -.INDENT 0.0 -.TP -.B \-L, \-\-log\-level= -Set the severity level of log output. must be -one of INFO, NOTICE, WARN, ERROR and FATAL. -.sp -Default: \fBNOTICE\fP -.UNINDENT -.INDENT 0.0 -.TP -.B \-\-accesslog\-file= -Set path to write access log. To reopen file, send USR1 -signal to nghttpx. -.UNINDENT -.INDENT 0.0 -.TP -.B \-\-accesslog\-syslog -Send access log to syslog. If this option is used, -\fI\%\-\-accesslog\-file\fP option is ignored. -.UNINDENT -.INDENT 0.0 -.TP -.B \-\-accesslog\-format= -Specify format string for access log. The default -format is combined format. The following variables are -available: -.INDENT 7.0 -.IP \(bu 2 -$remote_addr: client IP address. -.IP \(bu 2 -$time_local: local time in Common Log format. -.IP \(bu 2 -$time_iso8601: local time in ISO 8601 format. -.IP \(bu 2 -$request: HTTP request line. -.IP \(bu 2 -$status: HTTP response status code. -.IP \(bu 2 -$body_bytes_sent: the number of bytes sent to client -as response body. -.IP \(bu 2 -$http_: value of HTTP request header where -\(aq_\(aq in is replaced with \(aq\-\(aq. -.IP \(bu 2 -$remote_port: client port. -.IP \(bu 2 -$server_port: server port. -.IP \(bu 2 -$request_time: request processing time in seconds with -milliseconds resolution. -.IP \(bu 2 -$pid: PID of the running process. -.IP \(bu 2 -$alpn: ALPN identifier of the protocol which generates -the response. For HTTP/1, ALPN is always http/1.1, -regardless of minor version. -.UNINDENT -.sp -Default: \fB$remote_addr \- \- [$time_local] "$request" $status $body_bytes_sent "$http_referer" "$http_user_agent"\fP -.UNINDENT -.INDENT 0.0 -.TP -.B \-\-errorlog\-file= -Set path to write error log. To reopen file, send USR1 -signal to nghttpx. stderr will be redirected to the -error log file unless \fI\%\-\-errorlog\-syslog\fP is used. -.sp -Default: \fB/dev/stderr\fP -.UNINDENT -.INDENT 0.0 -.TP -.B \-\-errorlog\-syslog -Send error log to syslog. If this option is used, -\fI\%\-\-errorlog\-file\fP option is ignored. -.UNINDENT -.INDENT 0.0 -.TP -.B \-\-syslog\-facility= -Set syslog facility to . -.sp -Default: \fBdaemon\fP -.UNINDENT -.SS HTTP -.INDENT 0.0 -.TP -.B \-\-add\-x\-forwarded\-for -Append X\-Forwarded\-For header field to the downstream -request. -.UNINDENT -.INDENT 0.0 -.TP -.B \-\-strip\-incoming\-x\-forwarded\-for -Strip X\-Forwarded\-For header field from inbound client -requests. -.UNINDENT -.INDENT 0.0 -.TP -.B \-\-no\-via -Don\(aqt append to Via header field. If Via header field -is received, it is left unaltered. -.UNINDENT -.INDENT 0.0 -.TP -.B \-\-no\-location\-rewrite -Don\(aqt rewrite location header field on \fI\%\-\-http2\-bridge\fP, -\fI\%\-\-client\fP and default mode. For \fI\%\-\-http2\-proxy\fP and -\fI\%\-\-client\-proxy\fP mode, location header field will not be -altered regardless of this option. -.UNINDENT -.INDENT 0.0 -.TP -.B \-\-no\-host\-rewrite -Don\(aqt rewrite host and :authority header fields on -\fI\%\-\-http2\-bridge\fP, \fI\%\-\-client\fP and default mode. For -\fI\%\-\-http2\-proxy\fP and \fI\%\-\-client\-proxy\fP mode, these headers -will not be altered regardless of this option. -.UNINDENT -.INDENT 0.0 -.TP -.B \-\-altsvc= -Specify protocol ID, port, host and origin of -alternative service. and are optional. -They are advertised in alt\-svc header field only in -HTTP/1.1 frontend. This option can be used multiple -times to specify multiple alternative services. -Example: \fI\%\-\-altsvc\fP=h2,443 -.UNINDENT -.INDENT 0.0 -.TP -.B \-\-add\-response\-header=
      -Specify additional header field to add to response -header set. This option just appends header field and -won\(aqt replace anything already set. This option can be -used several times to specify multiple header fields. -Example: \fI\%\-\-add\-response\-header\fP="foo: bar" -.UNINDENT -.INDENT 0.0 -.TP -.B \-\-header\-field\-buffer= -Set maximum buffer size for incoming HTTP header field -list. This is the sum of header name and value in -bytes. -.sp -Default: \fB64K\fP -.UNINDENT -.INDENT 0.0 -.TP -.B \-\-max\-header\-fields= -Set maximum number of incoming HTTP header fields, which -appear in one request or response header field list. -.sp -Default: \fB100\fP -.UNINDENT -.SS Debug -.INDENT 0.0 -.TP -.B \-\-frontend\-http2\-dump\-request\-header= -Dumps request headers received by HTTP/2 frontend to the -file denoted in . The output is done in HTTP/1 -header field format and each header block is followed by -an empty line. This option is not thread safe and MUST -NOT be used with option \fI\%\-n\fP, where >= 2. -.UNINDENT -.INDENT 0.0 -.TP -.B \-\-frontend\-http2\-dump\-response\-header= -Dumps response headers sent from HTTP/2 frontend to the -file denoted in . The output is done in HTTP/1 -header field format and each header block is followed by -an empty line. This option is not thread safe and MUST -NOT be used with option \fI\%\-n\fP, where >= 2. -.UNINDENT -.INDENT 0.0 -.TP -.B \-o, \-\-frontend\-frame\-debug -Print HTTP/2 frames in frontend to stderr. This option -is not thread safe and MUST NOT be used with option -\fI\%\-n\fP=N, where N >= 2. -.UNINDENT -.SS Process -.INDENT 0.0 -.TP -.B \-D, \-\-daemon -Run in a background. If \fI\%\-D\fP is used, the current working -directory is changed to \(aq\fI/\fP\(aq. -.UNINDENT -.INDENT 0.0 -.TP -.B \-\-pid\-file= -Set path to save PID of this program. -.UNINDENT -.INDENT 0.0 -.TP -.B \-\-user= -Run this program as . This option is intended to -be used to drop root privileges. -.UNINDENT -.SS Misc -.INDENT 0.0 -.TP -.B \-\-conf= -Load configuration from . -.sp -Default: \fB/etc/nghttpx/nghttpx.conf\fP -.UNINDENT -.INDENT 0.0 -.TP -.B \-v, \-\-version -Print version and exit. -.UNINDENT -.INDENT 0.0 -.TP -.B \-h, \-\-help -Print this help and exit. -.UNINDENT -.sp -The argument is an integer and an optional unit (e.g., 10K is -10 * 1024). Units are K, M and G (powers of 1024). -.sp -The argument is an integer and an optional unit (e.g., 1s -is 1 second and 500ms is 500 milliseconds). Units are h, m, s or ms -(hours, minutes, seconds and milliseconds, respectively). If a unit -is omitted, a second is used as unit. -.SH FILES -.INDENT 0.0 -.TP -.B \fI/etc/nghttpx/nghttpx.conf\fP -The default configuration file path nghttpx searches at startup. -The configuration file path can be changed using \fI\%\-\-conf\fP -option. -.sp -Those lines which are staring \fB#\fP are treated as comment. -.sp -The option name in the configuration file is the long command\-line -option name with leading \fB\-\-\fP stripped (e.g., \fBfrontend\fP). Put -\fB=\fP between option name and value. Don\(aqt put extra leading or -trailing spaces. -.sp -The options which do not take argument in the command\-line \fItake\fP -argument in the configuration file. Specify \fByes\fP as an argument -(e.g., \fBhttp2\-proxy=yes\fP). If other string is given, it is -ignored. -.sp -To specify private key and certificate file which are given as -positional arguments in command\-line, use \fBprivate\-key\-file\fP and -\fBcertificate\-file\fP\&. -.sp -\fI\%\-\-conf\fP option cannot be used in the configuration file and -will be ignored if specified. -.UNINDENT -.SH SIGNALS -.INDENT 0.0 -.TP -.B SIGQUIT -Shutdown gracefully. First accept pending connections and stop -accepting connection. After all connections are handled, nghttpx -exits. -.TP -.B SIGUSR1 -Reopen log files. -.TP -.B SIGUSR2 -Fork and execute nghttpx. It will execute the binary in the same -path with same command\-line arguments and environment variables. -After new process comes up, sending SIGQUIT to the original process -to perform hot swapping. -.UNINDENT -.SH SERVER PUSH -.sp -nghttpx supports HTTP/2 server push in default mode. nghttpx looks -for Link header field (\fI\%RFC 5988\fP) in response headers from -backend server and extracts URI\-reference with parameter -\fBrel=preload\fP (see \fI\%preload\fP) -and pushes those URIs to the frontend client. Here is a sample Link -header field to initiate server push: -.INDENT 0.0 -.INDENT 3.5 -.sp -.nf -.ft C -Link: ; rel=preload -Link: ; rel=preload -.ft P -.fi -.UNINDENT -.UNINDENT -.sp -Currently, the following restrictions are applied for server push: -.INDENT 0.0 -.IP 1. 3 -URI\-reference must not contain authority. If it exists, it is not -pushed. \fB/fonts/font.woff\fP and \fBcss/theme.css\fP are eligible to -be pushed. \fBhttps://example.org/fonts/font.woff\fP and -\fB//example.org/css/theme.css\fP are not. -.IP 2. 3 -The associated stream must have method "GET" or "POST". The -associated stream\(aqs status code must be 200. -.UNINDENT -.sp -These limitations may be loosened in the future release. -.SH UNIX DOMAIN SOCKET -.sp -nghttpx supports UNIX domain socket with a filename for both frontend -and backend connections. -.sp -Please note that current nghttpx implementation does not delete a -socket with a filename. And on start up, if nghttpx detects that the -specified socket already exists in the file system, nghttpx first -deletes it. However, if SIGUSR2 is used to execute new binary and -both old and new configurations use same filename, new binary does not -delete the socket and continues to use it. -.SH OCSP STAPLING -.sp -OCSP query is done using external perl script \fBfetch\-ocsp\-response\fP, -which has been developed as part of h2o project -(\fI\%https://github.com/h2o/h2o\fP). -.sp -The script file is usually installed under -\fB$(prefix)/share/nghttp2/\fP directory. The actual path to script can -be customized using \fI\%\-\-fetch\-ocsp\-response\-file\fP option. -.SH SEE ALSO -.sp -\fInghttp(1)\fP, \fInghttpd(1)\fP, \fIh2load(1)\fP -.SH AUTHOR -Tatsuhiro Tsujikawa -.SH COPYRIGHT -2012, 2015, Tatsuhiro Tsujikawa -.\" Generated by docutils manpage writer. -. diff --git a/doc/nghttpx.1.rst b/doc/nghttpx.1.rst deleted file mode 100644 index ad3f915..0000000 --- a/doc/nghttpx.1.rst +++ /dev/null @@ -1,811 +0,0 @@ - -.. GENERATED by help2rst.py. DO NOT EDIT DIRECTLY. - -.. program:: nghttpx - -nghttpx(1) -========== - -SYNOPSIS --------- - -**nghttpx** [OPTIONS]... [ ] - -DESCRIPTION ------------ - -A reverse proxy for HTTP/2, HTTP/1 and SPDY. - -.. describe:: - - - Set path to server's private key. Required unless :option:`-p`\, - :option:`--client` or :option:`\--frontend-no-tls` are given. - -.. describe:: - - Set path to server's certificate. Required unless :option:`-p`\, - :option:`--client` or :option:`\--frontend-no-tls` are given. To make OCSP - stapling work, this must be absolute path. - - -OPTIONS -------- - -The options are categorized into several groups. - -Connections -~~~~~~~~~~~ - -.. option:: -b, --backend= - - Set backend host and port. The multiple backend - addresses are accepted by repeating this option. UNIX - domain socket can be specified by prefixing path name - with "unix:" (e.g., unix:/var/run/backend.sock) - - Default: ``127.0.0.1,80`` - -.. option:: -f, --frontend= - - Set frontend host and port. If is '\*', it - assumes all addresses including both IPv4 and IPv6. - UNIX domain socket can be specified by prefixing path - name with "unix:" (e.g., unix:/var/run/nghttpx.sock) - - Default: ``*,3000`` - -.. option:: --backlog= - - Set listen backlog size. - - Default: ``512`` - -.. option:: --backend-ipv4 - - Resolve backend hostname to IPv4 address only. - -.. option:: --backend-ipv6 - - Resolve backend hostname to IPv6 address only. - -.. option:: --backend-http-proxy-uri= - - Specify proxy URI in the form - http://[:@]:. If a proxy - requires authentication, specify and . - Note that they must be properly percent-encoded. This - proxy is used when the backend connection is HTTP/2. - First, make a CONNECT request to the proxy and it - connects to the backend on behalf of nghttpx. This - forms tunnel. After that, nghttpx performs SSL/TLS - handshake with the downstream through the tunnel. The - timeouts when connecting and making CONNECT request can - be specified by :option:`--backend-read-timeout` and - :option:`--backend-write-timeout` options. - - -Performance -~~~~~~~~~~~ - -.. option:: -n, --workers= - - Set the number of worker threads. - - Default: ``1`` - -.. option:: --read-rate= - - Set maximum average read rate on frontend connection. - Setting 0 to this option means read rate is unlimited. - - Default: ``0`` - -.. option:: --read-burst= - - Set maximum read burst size on frontend connection. - Setting 0 to this option means read burst size is - unlimited. - - Default: ``0`` - -.. option:: --write-rate= - - Set maximum average write rate on frontend connection. - Setting 0 to this option means write rate is unlimited. - - Default: ``0`` - -.. option:: --write-burst= - - Set maximum write burst size on frontend connection. - Setting 0 to this option means write burst size is - unlimited. - - Default: ``0`` - -.. option:: --worker-read-rate= - - Set maximum average read rate on frontend connection per - worker. Setting 0 to this option means read rate is - unlimited. Not implemented yet. - - Default: ``0`` - -.. option:: --worker-read-burst= - - Set maximum read burst size on frontend connection per - worker. Setting 0 to this option means read burst size - is unlimited. Not implemented yet. - - Default: ``0`` - -.. option:: --worker-write-rate= - - Set maximum average write rate on frontend connection - per worker. Setting 0 to this option means write rate - is unlimited. Not implemented yet. - - Default: ``0`` - -.. option:: --worker-write-burst= - - Set maximum write burst size on frontend connection per - worker. Setting 0 to this option means write burst size - is unlimited. Not implemented yet. - - Default: ``0`` - -.. option:: --worker-frontend-connections= - - Set maximum number of simultaneous connections frontend - accepts. Setting 0 means unlimited. - - Default: ``0`` - -.. option:: --backend-http2-connections-per-worker= - - Set maximum number of HTTP/2 connections per worker. - The default value is 0, which means the number of - backend addresses specified by :option:`-b` option. - -.. option:: --backend-http1-connections-per-host= - - Set maximum number of backend concurrent HTTP/1 - connections per host. This option is meaningful when :option:`-s` - option is used. To limit the number of connections per - frontend for default mode, use - :option:`--backend-http1-connections-per-frontend`\. - - Default: ``8`` - -.. option:: --backend-http1-connections-per-frontend= - - Set maximum number of backend concurrent HTTP/1 - connections per frontend. This option is only used for - default mode. 0 means unlimited. To limit the number - of connections per host for HTTP/2 or SPDY proxy mode - (-s option), use :option:`--backend-http1-connections-per-host`\. - - Default: ``0`` - -.. option:: --rlimit-nofile= - - Set maximum number of open files (RLIMIT_NOFILE) to . - If 0 is given, nghttpx does not set the limit. - - Default: ``0`` - -.. option:: --backend-request-buffer= - - Set buffer size used to store backend request. - - Default: ``16K`` - -.. option:: --backend-response-buffer= - - Set buffer size used to store backend response. - - Default: ``16K`` - - -Timeout -~~~~~~~ - -.. option:: --frontend-http2-read-timeout= - - Specify read timeout for HTTP/2 and SPDY frontend - connection. - - Default: ``3m`` - -.. option:: --frontend-read-timeout= - - Specify read timeout for HTTP/1.1 frontend connection. - - Default: ``3m`` - -.. option:: --frontend-write-timeout= - - Specify write timeout for all frontend connections. - - Default: ``30s`` - -.. option:: --stream-read-timeout= - - Specify read timeout for HTTP/2 and SPDY streams. 0 - means no timeout. - - Default: ``0`` - -.. option:: --stream-write-timeout= - - Specify write timeout for HTTP/2 and SPDY streams. 0 - means no timeout. - - Default: ``0`` - -.. option:: --backend-read-timeout= - - Specify read timeout for backend connection. - - Default: ``3m`` - -.. option:: --backend-write-timeout= - - Specify write timeout for backend connection. - - Default: ``30s`` - -.. option:: --backend-keep-alive-timeout= - - Specify keep-alive timeout for backend connection. - - Default: ``2s`` - -.. option:: --listener-disable-timeout= - - After accepting connection failed, connection listener - is disabled for a given amount of time. Specifying 0 - disables this feature. - - Default: ``0`` - - -SSL/TLS -~~~~~~~ - -.. option:: --ciphers= - - Set allowed cipher list. The format of the string is - described in OpenSSL ciphers(1). - -.. option:: -k, --insecure - - Don't verify backend server's certificate if :option:`-p`\, - :option:`--client` or :option:`\--http2-bridge` are given and - :option:`--backend-no-tls` is not given. - -.. option:: --cacert= - - Set path to trusted CA certificate file if :option:`-p`\, :option:`--client` - or :option:`--http2-bridge` are given and :option:`\--backend-no-tls` is not - given. The file must be in PEM format. It can contain - multiple certificates. If the linked OpenSSL is - configured to load system wide certificates, they are - loaded at startup regardless of this option. - -.. option:: --private-key-passwd-file= - - Path to file that contains password for the server's - private key. If none is given and the private key is - password protected it'll be requested interactively. - -.. option:: --subcert=: - - Specify additional certificate and private key file. - nghttpx will choose certificates based on the hostname - indicated by client using TLS SNI extension. This - option can be used multiple times. To make OCSP - stapling work, must be absolute path. - -.. option:: --backend-tls-sni-field= - - Explicitly set the content of the TLS SNI extension. - This will default to the backend HOST name. - -.. option:: --dh-param-file= - - Path to file that contains DH parameters in PEM format. - Without this option, DHE cipher suites are not - available. - -.. option:: --npn-list= - - Comma delimited list of ALPN protocol identifier sorted - in the order of preference. That means most desirable - protocol comes first. This is used in both ALPN and - NPN. The parameter must be delimited by a single comma - only and any white spaces are treated as a part of - protocol string. - - Default: ``h2,h2-16,h2-14,spdy/3.1,http/1.1`` - -.. option:: --verify-client - - Require and verify client certificate. - -.. option:: --verify-client-cacert= - - Path to file that contains CA certificates to verify - client certificate. The file must be in PEM format. It - can contain multiple certificates. - -.. option:: --client-private-key-file= - - Path to file that contains client private key used in - backend client authentication. - -.. option:: --client-cert-file= - - Path to file that contains client certificate used in - backend client authentication. - -.. option:: --tls-proto-list= - - Comma delimited list of SSL/TLS protocol to be enabled. - The following protocols are available: TLSv1.2, TLSv1.1 - and TLSv1.0. The name matching is done in - case-insensitive manner. The parameter must be - delimited by a single comma only and any white spaces - are treated as a part of protocol string. - - Default: ``TLSv1.2,TLSv1.1`` - -.. option:: --tls-ticket-key-file= - - Path to file that contains 48 bytes random data to - construct TLS session ticket parameters. This options - can be used repeatedly to specify multiple ticket - parameters. If several files are given, only the first - key is used to encrypt TLS session tickets. Other keys - are accepted but server will issue new session ticket - with first key. This allows session key rotation. - Please note that key rotation does not occur - automatically. User should rearrange files or change - options values and restart nghttpx gracefully. If - opening or reading given file fails, all loaded keys are - discarded and it is treated as if none of this option is - given. If this option is not given or an error occurred - while opening or reading a file, key is generated - automatically and renewed every 12hrs. At most 2 keys - are stored in memory. - -.. option:: --fetch-ocsp-response-file= - - Path to fetch-ocsp-response script file. It should be - absolute path. - - Default: ``/usr/local/share/nghttp2/fetch-ocsp-response`` - -.. option:: --ocsp-update-interval= - - Set interval to update OCSP response cache. - - Default: ``4h`` - -.. option:: --no-ocsp - - Disable OCSP stapling. - - -HTTP/2 and SPDY -~~~~~~~~~~~~~~~ - -.. option:: -c, --http2-max-concurrent-streams= - - Set the maximum number of the concurrent streams in one - HTTP/2 and SPDY session. - - Default: ``100`` - -.. option:: --frontend-http2-window-bits= - - Sets the per-stream initial window size of HTTP/2 SPDY - frontend connection. For HTTP/2, the size is 2\*\*-1. - For SPDY, the size is 2\*\*. - - Default: ``16`` - -.. option:: --frontend-http2-connection-window-bits= - - Sets the per-connection window size of HTTP/2 and SPDY - frontend connection. For HTTP/2, the size is - 2**-1. For SPDY, the size is 2\*\*. - - Default: ``16`` - -.. option:: --frontend-no-tls - - Disable SSL/TLS on frontend connections. - -.. option:: --backend-http2-window-bits= - - Sets the initial window size of HTTP/2 backend - connection to 2\*\*-1. - - Default: ``16`` - -.. option:: --backend-http2-connection-window-bits= - - Sets the per-connection window size of HTTP/2 backend - connection to 2\*\*-1. - - Default: ``16`` - -.. option:: --backend-no-tls - - Disable SSL/TLS on backend connections. - -.. option:: --http2-no-cookie-crumbling - - Don't crumble cookie header field. - -.. option:: --padding= - - Add at most bytes to a HTTP/2 frame payload as - padding. Specify 0 to disable padding. This option is - meant for debugging purpose and not intended to enhance - protocol security. - -.. option:: --no-server-push - - Disable HTTP/2 server push. Server push is only - supported by default mode and HTTP/2 frontend. SPDY - frontend does not support server push. - - -Mode -~~~~ - -.. describe:: (default mode) - - - Accept HTTP/2, SPDY and HTTP/1.1 over SSL/TLS. If - :option:`--frontend-no-tls` is used, accept HTTP/2 and HTTP/1.1. - The incoming HTTP/1.1 connection can be upgraded to - HTTP/2 through HTTP Upgrade. The protocol to the - backend is HTTP/1.1. - -.. option:: -s, --http2-proxy - - Like default mode, but enable secure proxy mode. - -.. option:: --http2-bridge - - Like default mode, but communicate with the backend in - HTTP/2 over SSL/TLS. Thus the incoming all connections - are converted to HTTP/2 connection and relayed to the - backend. See :option:`--backend-http-proxy-uri` option if you are - behind the proxy and want to connect to the outside - HTTP/2 proxy. - -.. option:: --client - - Accept HTTP/2 and HTTP/1.1 without SSL/TLS. The - incoming HTTP/1.1 connection can be upgraded to HTTP/2 - connection through HTTP Upgrade. The protocol to the - backend is HTTP/2. To use nghttpx as a forward proxy, - use :option:`-p` option instead. - -.. option:: -p, --client-proxy - - Like :option:`--client` option, but it also requires the request - path from frontend must be an absolute URI, suitable for - use as a forward proxy. - - -Logging -~~~~~~~ - -.. option:: -L, --log-level= - - Set the severity level of log output. must be - one of INFO, NOTICE, WARN, ERROR and FATAL. - - Default: ``NOTICE`` - -.. option:: --accesslog-file= - - Set path to write access log. To reopen file, send USR1 - signal to nghttpx. - -.. option:: --accesslog-syslog - - Send access log to syslog. If this option is used, - :option:`--accesslog-file` option is ignored. - -.. option:: --accesslog-format= - - Specify format string for access log. The default - format is combined format. The following variables are - available: - - * $remote_addr: client IP address. - * $time_local: local time in Common Log format. - * $time_iso8601: local time in ISO 8601 format. - * $request: HTTP request line. - * $status: HTTP response status code. - * $body_bytes_sent: the number of bytes sent to client - as response body. - * $http_: value of HTTP request header where - '_' in is replaced with '-'. - * $remote_port: client port. - * $server_port: server port. - * $request_time: request processing time in seconds with - milliseconds resolution. - * $pid: PID of the running process. - * $alpn: ALPN identifier of the protocol which generates - the response. For HTTP/1, ALPN is always http/1.1, - regardless of minor version. - - - Default: ``$remote_addr - - [$time_local] "$request" $status $body_bytes_sent "$http_referer" "$http_user_agent"`` - -.. option:: --errorlog-file= - - Set path to write error log. To reopen file, send USR1 - signal to nghttpx. stderr will be redirected to the - error log file unless :option:`--errorlog-syslog` is used. - - Default: ``/dev/stderr`` - -.. option:: --errorlog-syslog - - Send error log to syslog. If this option is used, - :option:`--errorlog-file` option is ignored. - -.. option:: --syslog-facility= - - Set syslog facility to . - - Default: ``daemon`` - - -HTTP -~~~~ - -.. option:: --add-x-forwarded-for - - Append X-Forwarded-For header field to the downstream - request. - -.. option:: --strip-incoming-x-forwarded-for - - Strip X-Forwarded-For header field from inbound client - requests. - -.. option:: --no-via - - Don't append to Via header field. If Via header field - is received, it is left unaltered. - -.. option:: --no-location-rewrite - - Don't rewrite location header field on :option:`--http2-bridge`\, - :option:`--client` and default mode. For :option:`\--http2-proxy` and - :option:`--client-proxy` mode, location header field will not be - altered regardless of this option. - -.. option:: --no-host-rewrite - - Don't rewrite host and :authority header fields on - :option:`--http2-bridge`\, :option:`--client` and default mode. For - :option:`--http2-proxy` and :option:`\--client-proxy` mode, these headers - will not be altered regardless of this option. - -.. option:: --altsvc= - - Specify protocol ID, port, host and origin of - alternative service. and are optional. - They are advertised in alt-svc header field only in - HTTP/1.1 frontend. This option can be used multiple - times to specify multiple alternative services. - Example: :option:`--altsvc`\=h2,443 - -.. option:: --add-response-header=
      - - Specify additional header field to add to response - header set. This option just appends header field and - won't replace anything already set. This option can be - used several times to specify multiple header fields. - Example: :option:`--add-response-header`\="foo: bar" - -.. option:: --header-field-buffer= - - Set maximum buffer size for incoming HTTP header field - list. This is the sum of header name and value in - bytes. - - Default: ``64K`` - -.. option:: --max-header-fields= - - Set maximum number of incoming HTTP header fields, which - appear in one request or response header field list. - - Default: ``100`` - - -Debug -~~~~~ - -.. option:: --frontend-http2-dump-request-header= - - Dumps request headers received by HTTP/2 frontend to the - file denoted in . The output is done in HTTP/1 - header field format and each header block is followed by - an empty line. This option is not thread safe and MUST - NOT be used with option :option:`-n`\, where >= 2. - -.. option:: --frontend-http2-dump-response-header= - - Dumps response headers sent from HTTP/2 frontend to the - file denoted in . The output is done in HTTP/1 - header field format and each header block is followed by - an empty line. This option is not thread safe and MUST - NOT be used with option :option:`-n`\, where >= 2. - -.. option:: -o, --frontend-frame-debug - - Print HTTP/2 frames in frontend to stderr. This option - is not thread safe and MUST NOT be used with option - :option:`-n`\=N, where N >= 2. - - -Process -~~~~~~~ - -.. option:: -D, --daemon - - Run in a background. If :option:`-D` is used, the current working - directory is changed to '*/*'. - -.. option:: --pid-file= - - Set path to save PID of this program. - -.. option:: --user= - - Run this program as . This option is intended to - be used to drop root privileges. - - -Misc -~~~~ - -.. option:: --conf= - - Load configuration from . - - Default: ``/etc/nghttpx/nghttpx.conf`` - -.. option:: -v, --version - - Print version and exit. - -.. option:: -h, --help - - Print this help and exit. - - - -The argument is an integer and an optional unit (e.g., 10K is -10 * 1024). Units are K, M and G (powers of 1024). - -The argument is an integer and an optional unit (e.g., 1s -is 1 second and 500ms is 500 milliseconds). Units are h, m, s or ms -(hours, minutes, seconds and milliseconds, respectively). If a unit -is omitted, a second is used as unit. - -FILES ------ - -*/etc/nghttpx/nghttpx.conf* - The default configuration file path nghttpx searches at startup. - The configuration file path can be changed using :option:`--conf` - option. - - Those lines which are staring ``#`` are treated as comment. - - The option name in the configuration file is the long command-line - option name with leading ``--`` stripped (e.g., ``frontend``). Put - ``=`` between option name and value. Don't put extra leading or - trailing spaces. - - The options which do not take argument in the command-line *take* - argument in the configuration file. Specify ``yes`` as an argument - (e.g., ``http2-proxy=yes``). If other string is given, it is - ignored. - - To specify private key and certificate file which are given as - positional arguments in command-line, use ``private-key-file`` and - ``certificate-file``. - - :option:`--conf` option cannot be used in the configuration file and - will be ignored if specified. - -SIGNALS -------- - -SIGQUIT - Shutdown gracefully. First accept pending connections and stop - accepting connection. After all connections are handled, nghttpx - exits. - -SIGUSR1 - Reopen log files. - -SIGUSR2 - Fork and execute nghttpx. It will execute the binary in the same - path with same command-line arguments and environment variables. - After new process comes up, sending SIGQUIT to the original process - to perform hot swapping. - -SERVER PUSH ------------ - -nghttpx supports HTTP/2 server push in default mode. nghttpx looks -for Link header field (`RFC 5988 -`_) in response headers from -backend server and extracts URI-reference with parameter -``rel=preload`` (see `preload -`_) -and pushes those URIs to the frontend client. Here is a sample Link -header field to initiate server push: - -.. code-block:: http - - Link: ; rel=preload - Link: ; rel=preload - -Currently, the following restrictions are applied for server push: - -1. URI-reference must not contain authority. If it exists, it is not - pushed. ``/fonts/font.woff`` and ``css/theme.css`` are eligible to - be pushed. ``https://example.org/fonts/font.woff`` and - ``//example.org/css/theme.css`` are not. - -2. The associated stream must have method "GET" or "POST". The - associated stream's status code must be 200. - -These limitations may be loosened in the future release. - -UNIX DOMAIN SOCKET ------------------- - -nghttpx supports UNIX domain socket with a filename for both frontend -and backend connections. - -Please note that current nghttpx implementation does not delete a -socket with a filename. And on start up, if nghttpx detects that the -specified socket already exists in the file system, nghttpx first -deletes it. However, if SIGUSR2 is used to execute new binary and -both old and new configurations use same filename, new binary does not -delete the socket and continues to use it. - -OCSP STAPLING -------------- - -OCSP query is done using external perl script ``fetch-ocsp-response``, -which has been developed as part of h2o project -(https://github.com/h2o/h2o). - -The script file is usually installed under -``$(prefix)/share/nghttp2/`` directory. The actual path to script can -be customized using :option:`--fetch-ocsp-response-file` option. - -SEE ALSO --------- - -:manpage:`nghttp(1)`, :manpage:`nghttpd(1)`, :manpage:`h2load(1)` diff --git a/doc/package_README.rst.in b/doc/package_README.rst.in deleted file mode 100644 index dfa6b2d..0000000 --- a/doc/package_README.rst.in +++ /dev/null @@ -1 +0,0 @@ -.. include:: @top_srcdir@/README.rst diff --git a/doc/programmers-guide.rst b/doc/programmers-guide.rst deleted file mode 100644 index 94c72e9..0000000 --- a/doc/programmers-guide.rst +++ /dev/null @@ -1,105 +0,0 @@ -Programmers' Guide -================== - -Includes --------- - -To use the public APIs, include ``nghttp2/nghttp2.h``:: - - #include - -The header files are also available online: :doc:`nghttp2.h` and -:doc:`nghttp2ver.h`. - -Remarks -------- - -Do not call `nghttp2_session_send()`, `nghttp2_session_mem_send()`, -`nghttp2_session_recv()` or `nghttp2_session_mem_recv()` from the -nghttp2 callback functions directly or indirectly. It will lead to the -crash. You can submit requests or frames in the callbacks then call -these functions outside the callbacks. - -`nghttp2_session_send()` and `nghttp2_session_mem_send()` send first -24 bytes of client magic string (MAGIC) -(:macro:`NGHTTP2_CLIENT_MAGIC`) on client configuration. The -applications are responsible to send SETTINGS frame as part of -connection preface using `nghttp2_submit_settings()`. Similarly, -`nghttp2_session_recv()` and `nghttp2_session_mem_recv()` consume -MAGIC on server configuration unless -`nghttp2_option_set_no_recv_client_magic()` is used with nonzero -option value. - -.. _http-messaging: - -HTTP Messaging --------------- - -By default, nghttp2 library checks HTTP messaging rules described in -`HTTP/2 specification, section 8 -`_. -Everything described in that section is not validated however. We -briefly describe what the library does in this area. In the following -description, without loss of generality we omit CONTINUATION frame -since they must follow HEADERS frame and are processed atomically. In -other words, they are just one big HEADERS frame. To disable these -validations, use `nghttp2_option_set_no_http_messaging()`. - -For HTTP request, including those carried by PUSH_PROMISE, HTTP -message starts with one HEADERS frame containing request headers. It -is followed by zero or more DATA frames containing request body, which -is followed by zero or one HEADERS containing trailer headers. The -request headers must include ":scheme", ":method" and ":path" pseudo -header fields unless ":method" is not "CONNECT". ":authority" is -optional, but nghttp2 requires either ":authority" or "Host" header -field must be present. If ":method" is "CONNECT", the request headers -must include ":method" and ":authority" and must omit ":scheme" and -":path". - -For HTTP response, HTTP message starts with zero or more HEADERS -frames containing non-final response (status code 1xx). They are -followed by one HEADERS frame containing final response headers -(non-1xx). It is followed by zero or more DATA frames containing -response body, which is followed by zero or one HEADERS containing -trailer headers. The non-final and final response headers must -contain ":status" pseudo header field containing 3 digits only. - -All request and response headers must include exactly one valid value -for each pseudo header field. Additionally nghttp2 requires all -request headers must not include more than one "Host" header field. - -HTTP/2 prohibits connection-specific header fields. The following -header fields must not appear: "Connection", "Keep-Alive", -"Proxy-Connection", "Transfer-Encoding" and "Upgrade". Additionally, -"TE" header field must not include any value other than "trailers". - -Each header field name and value must obey the field-name and -field-value production rules described in `RFC 7230, section -3.2. `_. -Additionally, all field name must be lower cased. While the pseudo -header fields must satisfy these rules, we just ignore illegal regular -headers (this means that these header fields are not passed to -application callback). This is because these illegal header fields -are floating around in existing internet and resetting stream just -because of this may break many web sites. This is especially true if -we forward to or translate from HTTP/1 traffic. - -For "http" or "https" URIs, ":path" pseudo header fields must start -with "/". The only exception is OPTIONS request, in that case, "*" is -allowed in ":path" pseudo header field to represent system-wide -OPTIONS request. - -With the above validations, nghttp2 library guarantees that header -field name passed to `nghttp2_on_header_callback()` is not empty. -Also required pseudo headers are all present and not empty. - -nghttp2 enforces "Content-Length" validation as well. All request or -response headers must not contain more than one "Content-Length" -header field. If "Content-Length" header field is present, it must be -parsed as 64 bit signed integer. The sum of data length in the -following DATA frames must match with the number in "Content-Length" -header field if it is present (this does not include padding bytes). - -Any deviation results in stream error of type PROTOCOL_ERROR. If -error is found in PUSH_PROMISE frame, stream error is raised against -promised stream. diff --git a/doc/python-apiref.rst.in b/doc/python-apiref.rst.in deleted file mode 100644 index 5fd40de..0000000 --- a/doc/python-apiref.rst.in +++ /dev/null @@ -1 +0,0 @@ -.. include:: @top_srcdir@/doc/sources/python-apiref.rst diff --git a/doc/sources/building-android-binary.rst b/doc/sources/building-android-binary.rst deleted file mode 100644 index 99aec39..0000000 --- a/doc/sources/building-android-binary.rst +++ /dev/null @@ -1,135 +0,0 @@ -Building Android binary -======================= - -In this article, we briefly describe how to build Android binary using -`Android NDK `_ -cross-compiler on Debian Linux. - -The easiest way to build android binary is use Dockerfile.android. -See Dockerfile.android for more details. If you cannot use -Dockerfile.android for whatever reason, continue to read the rest of -this article. - -We offer ``android-config`` and ``android-make`` scripts to make the -build easier. To make these script work, NDK toolchain must be -installed in the following way. First, let us introduce -``ANDROID_HOME`` environment variable. We need to install toolchain -under ``$ANDROID_HOME/toolchain``. An user can freely choose the path -for ``ANDROID_HOME``. For example, to install toolchain under -``$ANDROID_HOME/toolchain``, do this in the the directory where NDK is -unpacked:: - - $ build/tools/make-standalone-toolchain.sh \ - --install-dir=$ANDROID_HOME/toolchain \ - --toolchain=arm-linux-androideabi-4.9 \ - --llvm-version=3.5 \ - --platform=android-16 - -The additional flag ``--system=linux-x86_64`` may be required if you -are using x86_64 system. - -The platform level is not important here because we don't use Android -specific C/C++ API. - -The dependent libraries, such as OpenSSL and libev should be built -with the toolchain and installed under ``$ANDROID_HOME/usr/local``. -We recommend to build these libraries as static library to make the -deployment easier. libxml2 support is currently disabled. - -We use zlib which comes with Android NDK, so we don't have to build it -by ourselves. - -If SPDY support is required for nghttpx and h2load, build and install -spdylay as well. - -Before running ``android-config`` and ``android-make``, -``ANDROID_HOME`` environment variable must be set to point to the -correct path. Also add ``$ANDROID_HOME/toolchain/bin`` to ``PATH``:: - - $ export PATH=$PATH:$ANDROID_HOME/toolchain/bin - -To configure OpenSSL, use the following script: - -.. code-block:: sh - - #!/bin/sh - - if [ -z "$ANDROID_HOME" ]; then - echo 'No $ANDROID_HOME specified.' - exit 1 - fi - PREFIX=$ANDROID_HOME/usr/local - TOOLCHAIN=$ANDROID_HOME/toolchain - PATH=$TOOLCHAIN/bin:$PATH - - export CROSS_COMPILE=$TOOLCHAIN/bin/arm-linux-androideabi- - ./Configure --prefix=$PREFIX android - -And run ``make install`` to build and install. - -We cannot compile libev without modification. Apply `this patch -`_ before -configuring libev. This patch is for libev-4.19. After applying the -patch, to configure libev, use the following script: - -.. code-block:: sh - - #!/bin/sh - - if [ -z "$ANDROID_HOME" ]; then - echo 'No $ANDROID_HOME specified.' - exit 1 - fi - PREFIX=$ANDROID_HOME/usr/local - TOOLCHAIN=$ANDROID_HOME/toolchain - PATH=$TOOLCHAIN/bin:$PATH - - ./configure \ - --host=arm-linux-androideabi \ - --build=`dpkg-architecture -qDEB_BUILD_GNU_TYPE` \ - --prefix=$PREFIX \ - --disable-shared \ - --enable-static \ - CPPFLAGS=-I$PREFIX/include \ - LDFLAGS=-L$PREFIX/lib - -And run ``make install`` to build and install. - -To configure spdylay, use the following script: - -.. code-block:: sh - - if [ -z "$ANDROID_HOME" ]; then - echo 'No $ANDROID_HOME specified.' - exit 1 - fi - PREFIX=$ANDROID_HOME/usr/local - TOOLCHAIN=$ANDROID_HOME/toolchain - PATH=$TOOLCHAIN/bin:$PATH - - ./configure \ - --disable-shared \ - --host=arm-linux-androideabi \ - --build=`dpkg-architecture -qDEB_BUILD_GNU_TYPE` \ - --prefix=$PREFIX \ - --without-libxml2 \ - --disable-src \ - --disable-examples \ - CPPFLAGS="-I$PREFIX/include" \ - PKG_CONFIG_LIBDIR="$PREFIX/lib/pkgconfig" \ - LDFLAGS="-L$PREFIX/lib" - -And run ``make install`` to build and install. After spdylay -installation, edit $ANDROID_HOME/usr/local/lib/pkgconfig/libspdylay.pc -and remove the following line:: - - Requires.private: zlib - -After prerequisite libraries are prepared, run ``android-config`` and -then ``android-make`` to compile nghttp2 source files. - -If all went well, application binaries, such as nghttpx, are created -under src directory. Strip debugging information from the binary -using the following command:: - - $ arm-linux-androideabi-strip src/nghttpx diff --git a/doc/sources/contribute.rst b/doc/sources/contribute.rst deleted file mode 100644 index 14a11e2..0000000 --- a/doc/sources/contribute.rst +++ /dev/null @@ -1,57 +0,0 @@ -Contribution Guidelines -======================= - -[This text was composed based on 1.2. License section of curl/libcurl -project.] - -When contributing with code, you agree to put your changes and new -code under the same license nghttp2 is already using unless stated and -agreed otherwise. - -When changing existing source code, you do not alter the copyright of -the original file(s). The copyright will still be owned by the -original creator(s) or those who have been assigned copyright by the -original author(s). - -By submitting a patch to the nghttp2 project, you are assumed to have -the right to the code and to be allowed by your employer or whatever -to hand over that patch/code to us. We will credit you for your -changes as far as possible, to give credit but also to keep a trace -back to who made what changes. Please always provide us with your -full real name when contributing! - -Coding style ------------- - -We use clang-format to format source code consistently. The -clang-format configuration file .clang-format is located at the root -directory. Since clang-format produces slightly different results -between versions, we currently use clang-format which comes with -clang-3.5. - -To detect any violation to the coding style, we recommend to setup git -pre-commit hook to check coding style of the changes you introduced. -The pre-commit file is located at the root directory. Copy it under -.git/hooks and make sure that it is executable. The pre-commit script -uses clang-format-diff.py to detect any style errors. If it is not in -your PATH or it exists under different name (e.g., -clang-format-diff-3.5 in debian), either add it to PATH variable or -add git option ``clangformatdiff.binary`` to point to the script. - -For emacs users, integrating clang-format to emacs is very easy. -clang-format.el should come with clang distribution. If it is not -found, download it from `here -`_. -And add these lines to your .emacs file: - -.. code-block:: lisp - - ;; From - ;; https://code.google.com/p/chromium/wiki/Emacs#Use_Google's_C++_style! - (load "//clang-format.el") - (add-hook 'c-mode-common-hook - (function (lambda () (local-set-key (kbd "TAB") - 'clang-format-region)))) - -You can find other editor integration in -http://clang.llvm.org/docs/ClangFormat.html. diff --git a/doc/sources/h2load-howto.rst b/doc/sources/h2load-howto.rst deleted file mode 100644 index 7c07194..0000000 --- a/doc/sources/h2load-howto.rst +++ /dev/null @@ -1,91 +0,0 @@ -h2load - HTTP/2 benchmarking tool - HOW-TO -========================================== - -h2load is benchmarking tool for HTTP/2. If built with -spdylay (http://tatsuhiro-t.github.io/spdylay/) library, it also -supports SPDY protocol. It supports SSL/TLS and clear text for both -HTTP/2 and SPDY. - -Basic Usage ------------ - -In order to set benchmark settings, specify following 3 options. - -``-n`` - The number of total requests. Default: 1 - -``-c`` - The number of concurrent clients. Default: 1 - -``-m`` - The max concurrent streams to issue per client. - If ``auto`` is given, the number of given URIs is used. - Default: ``auto`` - -Here is a command-line to perform benchmark to URI \https://localhost -using total 100000 requests, 100 concurrent clients and 10 max -concurrent streams:: - - $ h2load -n100000 -c100 -m10 https://localhost - -The benchmarking result looks like this:: - - finished in 0 sec, 385 millisec and 851 microsec, 2591 req/s, 1689 kbytes/s - requests: 1000 total, 1000 started, 1000 done, 1000 succeeded, 0 failed, 0 errored - status codes: 1000 2xx, 0 3xx, 0 4xx, 0 5xx - traffic: 667500 bytes total, 28700 bytes headers, 612000 bytes data - -The number of ``failed`` is the number of requests returned with non -2xx status. The number of ``error`` is the number of ``failed`` plus -the number of requests which failed with connection error. - -The number of ``total`` in ``traffic`` is the received application -data. If SSL/TLS is used, this number is calculated after decryption. -The number of ``headers`` is the sum of payload size of response -HEADERS (or SYN_REPLY for SPDY). This number comes before -decompressing header block. The number of ``data`` is the sum of -response body. - -Flow Control ------------- - -HTTP/2 and SPDY/3 or later employ flow control and it may affect -benchmarking results. By default, h2load uses large enough flow -control window, which effectively disables flow control. To adjust -receiver flow control window size, there are following options: - -``-w`` - Sets the stream level initial window size to - (2**)-1. For SPDY, 2** is used instead. - -``-W`` - Sets the connection level initial window size to - (2**)-1. For SPDY, if is strictly less - than 16, this option is ignored. Otherwise - 2** is used for SPDY. - -Multi-Threading ---------------- - -Sometimes benchmarking client itself becomes a bottleneck. To remedy -this situation, use ``-t`` option to specify the number of native -thread to use. - -``-t`` - The number of native threads. Default: 1 - -Selecting protocol for clear text ---------------------------------- - -By default, if \http:// URI is given, HTTP/2 protocol is used. To -change the protocol to use for clear text, use ``-p`` option. - -Multiple URIs -------------- - -If multiple URIs are specified, they are used in round robin manner. - -.. note:: - - Please note that h2load uses scheme, host and port in the first URI - and ignores those parts in the rest of the URIs. diff --git a/doc/sources/index.rst b/doc/sources/index.rst deleted file mode 100644 index 14a469f..0000000 --- a/doc/sources/index.rst +++ /dev/null @@ -1,53 +0,0 @@ -.. nghttp2 documentation master file, created by - sphinx-quickstart on Sun Mar 11 22:57:49 2012. - You can adapt this file completely to your liking, but it should at least - contain the root `toctree` directive. - -nghttp2 - HTTP/2 C Library -============================ - -This is an experimental implementation of Hypertext Transfer Protocol -version 2. - -The project is hosted at `github.com/tatsuhiro-t/nghttp2 `_. - -Contents: - -.. toctree:: - :maxdepth: 2 - - package_README - contribute - building-android-binary - tutorial-client - tutorial-server - tutorial-hpack - nghttp.1 - nghttpd.1 - nghttpx.1 - h2load.1 - nghttpx-howto - h2load-howto - programmers-guide - apiref - libnghttp2_asio - python-apiref - nghttp2.h - nghttp2ver.h - asio_http2_server.h - asio_http2_client.h - asio_http2.h - Source - Issues - nghttp2.org - -Released Versions -================= - -https://github.com/tatsuhiro-t/nghttp2/releases - -Resources ---------- - -* HTTP/2 https://tools.ietf.org/html/rfc7540 -* HPACK https://tools.ietf.org/html/rfc7541 diff --git a/doc/sources/libnghttp2_asio.rst b/doc/sources/libnghttp2_asio.rst deleted file mode 100644 index 76999f9..0000000 --- a/doc/sources/libnghttp2_asio.rst +++ /dev/null @@ -1,433 +0,0 @@ -libnghttp2_asio: High level HTTP/2 C++ library -============================================== - -libnghttp2_asio is C++ library built on top of libnghttp2 and provides -high level abstraction API to build HTTP/2 applications. It depends -on Boost::ASIO library and OpenSSL. Currently libnghttp2_asio -provides server and client side API. - -libnghttp2_asio is not built by default. Use ``--enable-asio-lib`` -configure flag to build libnghttp2_asio. The required Boost libraries -are: - -* Boost::Asio -* Boost::System -* Boost::Thread - -We have 3 header files for this library: - -* :doc:`asio_http2_server.h` -* :doc:`asio_http2_client.h` -* :doc:`asio_http2.h` - -asio_http2.h is included from the other two files. - -To build a program with libnghttp2_asio, link to the following -libraries:: - - -lnghttp2_asio -lboost_system - -If ``boost::asio::ssl`` is used in application code, OpenSSL is also -required in link line:: - - -lnghttp2_asio -lboost_system -lssl -lcrypto - -Server API ----------- - -To use server API, first include following header file: - -.. code-block:: cpp - - #include - -Also take a look at that header file :doc:`asio_http2_server.h`. - -Server API is designed to build HTTP/2 server very easily to utilize -C++11 anonymous function and closure. The bare minimum example of -HTTP/2 server looks like this: - -.. code-block:: cpp - - using namespace nghttp2::asio_http2; - using namespace nghttp2::asio_http2::server; - - int main(int argc, char *argv[]) { - boost::system::error_code ec; - http2 server; - - server.handle("/", [](const request &req, const response &res) { - res.write_head(200); - res.end("hello, world\n"); - }); - - if (server.listen_and_serve(ec, "localhost", "3000")) { - std::cerr << "error: " << ec.message() << std::endl; - } - } - -First we instantiate ``nghttp2::asio_http2::server::http2`` object. -``nghttp2::asio_http2::server::http2::handle`` function registers -pattern and its handler function. In this example, we register "/" as -pattern, which matches all requests. Then call -``nghttp2::asio_http2::server::http2::listen_and_serve`` function with -address and port to listen to. - -The ``req`` and ``res`` represent HTTP request and response -respectively. ``nghttp2::asio_http2_::server::response::write_head`` -constructs HTTP response header fields. The first argument is HTTP -status code, in the above example, which is 200. The second argument, -which is omitted in the above example, is additional header fields to -send. - -``nghttp2::asio_http2::server::response::end`` sends response body. -In the above example, we send string "hello, world". - -The life time of req and res object ends after the callback set by -``nghttp2::asio_http2::server::response::on_close`` function. -Application must not use those objects after this call. - -Serving static files and enabling SSL/TLS -+++++++++++++++++++++++++++++++++++++++++ - -In this example, we serve a couple of static files and also enable -SSL/TLS. - -.. code-block:: cpp - - #include - - using namespace nghttp2::asio_http2; - using namespace nghttp2::asio_http2::server; - - int main(int argc, char *argv[]) { - boost::system::error_code ec; - boost::asio::ssl::context tls(boost::asio::ssl::context::sslv23); - - tls.use_private_key_file("server.key", boost::asio::ssl::context::pem); - tls.use_certificate_chain_file("server.crt"); - - configure_tls_context_easy(ec, tls); - - http2 server; - - server.handle("/index.html", [](const request &req, const response &res) { - res.write_head(200); - res.end(file_generator("index.html")); - }); - - if (server.listen_and_serve(ec, tls, "localhost", "3000")) { - std::cerr << "error: " << ec.message() << std::endl; - } - } - -We first create ``boost::asio::ssl::context`` object and set path to -private key file and certificate file. -``nghttp2::asio_http2::server::configure_tls_context_easy`` function -configures SSL/TLS context object for HTTP/2 server use, including NPN -callbacks. - -In the above example, if request path is "/index.html", we serve -index.html file in the current working directory. -``nghttp2::asio_http2::server::response::end`` has overload to take -function of type ``nghttp2::asio_http2::generator_cb`` and application -pass its implementation to generate response body. For the -convenience, libnghttp2_asio library provides -``nghttp2::asio_http2::file_generator`` function to generate function -to server static file. If other resource is requested, server -automatically responds with 404 status code. - -Server push -+++++++++++ - -Server push is also supported. - -.. code-block:: cpp - - #include - - using namespace nghttp2::asio_http2; - using namespace nghttp2::asio_http2::server; - - int main(int argc, char *argv[]) { - boost::system::error_code ec; - boost::asio::ssl::context tls(boost::asio::ssl::context::sslv23); - - tls.use_private_key_file("server.key", boost::asio::ssl::context::pem); - tls.use_certificate_chain_file("server.crt"); - - configure_tls_context_easy(ec, tls); - - http2 server; - - std::string style_css = "h1 { color: green; }"; - - server.handle("/", [&style_css](const request &req, const response &res) { - boost::system::error_code ec; - auto push = res.push(ec, "GET", "/style.css"); - push->write_head(200); - push->end(style_css); - - res.write_head(200); - res.end(R"( - - HTTP/2 FTW - -

      This should be green

      - - )"); - }); - - server.handle("/style.css", - [&style_css](const request &req, const response &res) { - res.write_head(200); - res.end(style_css); - }); - - if (server.listen_and_serve(ec, tls, "localhost", "3000")) { - std::cerr << "error: " << ec.message() << std::endl; - } - } - -When client requested any resource other than "/style.css", we push -"/style.css". To push resource, call -``nghttp2::asio_http2::server::response::push`` function with desired -method and path. It returns another response object and use its -functions to send push response. - -Enable multi-threading -++++++++++++++++++++++ - -Enabling multi-threading is very easy. Just call -``nghttp2::asio_http2::server::http2::num_threads`` function with the -desired number of threads: - -.. code-block:: cpp - - http2 server; - - // Use 4 native threads - server.num_threads(4); - -Client API ----------- - -To use client API, first include following header file: - -.. code-block:: cpp - - #include - -Also take a look at that header file :doc:`asio_http2_client.h`. - -Here is the sample client code to access HTTP/2 server and print out -response header fields and response body to the console screen: - -.. code-block:: cpp - - #include - - #include - - using boost::asio::ip::tcp; - - using namespace nghttp2::asio_http2; - using namespace nghttp2::asio_http2::client; - - int main(int argc, char *argv[]) { - boost::system::error_code ec; - boost::asio::io_service io_service; - - // connect to localhost:3000 - session sess(io_service, "localhost", "3000"); - - sess.on_connect([&sess](tcp::resolver::iterator endpoint_it) { - boost::system::error_code ec; - - auto req = sess.submit(ec, "GET", "http://localhost:3000/"); - - req->on_response([](const response &res) { - // print status code and response header fields. - std::cerr << "HTTP/2 " << res.status_code() << std::endl; - for (auto &kv : res.header()) { - std::cerr << kv.first << ": " << kv.second.value << "\n"; - } - std::cerr << std::endl; - - res.on_data([](const uint8_t *data, std::size_t len) { - std::cerr.write(reinterpret_cast(data), len); - std::cerr << std::endl; - }); - }); - - req->on_close([&sess](uint32_t error_code) { - // shutdown session after first request was done. - sess.shutdown(); - }); - }); - - sess.on_error([](const boost::system::error_code &ec) { - std::cerr << "error: " << ec.message() << std::endl; - }); - - io_service.run(); - } - -``nghttp2::asio_http2::client::session`` object takes -``boost::asio::io_service`` object and remote server address. When -connection is made, the callback function passed to -``nghttp2::asio_http2::client::on_connect`` is invoked with connected -address as its parameter. After this callback call, use -``nghttp2::asio_http2::session::submit`` to send request to the -server. You can submit multiple requests at once without waiting for -the completion of previous request. - -The life time of req and res object ends after the callback set by -``nghttp2::asio_http2::server::request::on_close`` function. -Application must not use those objects after this call. - -Normally, client does not stop even after all requests are done unless -connection is lost. To stop client, call -``nghttp2::asio_http2::server::session::shutdown()``. - -Recieve server push and enable SSL/TLS -++++++++++++++++++++++++++++++++++++++ - -.. code-block:: cpp - - #include - - #include - - using boost::asio::ip::tcp; - - using namespace nghttp2::asio_http2; - using namespace nghttp2::asio_http2::client; - - int main(int argc, char *argv[]) { - boost::system::error_code ec; - boost::asio::io_service io_service; - - boost::asio::ssl::context tls(boost::asio::ssl::context::sslv23); - tls.set_default_verify_paths(); - // disabled to make development easier... - // tls_ctx.set_verify_mode(boost::asio::ssl::verify_peer); - configure_tls_context(ec, tls); - - // connect to localhost:3000 - session sess(io_service, tls, "localhost", "3000"); - - sess.on_connect([&sess](tcp::resolver::iterator endpoint_it) { - boost::system::error_code ec; - - auto req = sess.submit(ec, "GET", "http://localhost:3000/"); - - req->on_response([&sess](const response &res) { - std::cerr << "response received!" << std::endl; - res.on_data([&sess](const uint8_t *data, std::size_t len) { - std::cerr.write(reinterpret_cast(data), len); - std::cerr << std::endl; - }); - }); - - req->on_push([](const request &push) { - std::cerr << "push request received!" << std::endl; - push.on_response([](const response &res) { - std::cerr << "push response received!" << std::endl; - res.on_data([](const uint8_t *data, std::size_t len) { - std::cerr.write(reinterpret_cast(data), len); - std::cerr << std::endl; - }); - }); - }); - }); - - sess.on_error([](const boost::system::error_code &ec) { - std::cerr << "error: " << ec.message() << std::endl; - }); - - io_service.run(); - } - -The above sample code demonstrates how to enable SSL/TLS and receive -server push. Currently, -``nghttp2::asio_http2::client::configure_tls_context`` function setups -NPN callbacks for SSL/TLS context for HTTP/2 use. - -To receive server push, use -``nghttp2::asio_http2::client::request::on_push`` function to set -callback function which is invoked when server push request is -arrived. The callback function takes -``nghttp2::asio_http2::client::request`` object, which contains the -pushed request. To get server push response, set callback using -``nghttp2::asio_http2::client::request::on_response``. - -As stated in the previous section, client does not stop automatically -as long as HTTP/2 session is fine and connection is alive. We don't -call ``nghttp2::asio_http2::client::session::shutdown`` in this -example, so the program does not terminate after all responses are -received. Hit Ctrl-C to terminate the program. - -Multiple concurrent requests -++++++++++++++++++++++++++++ - -.. code-block:: cpp - - #include - - #include - - using boost::asio::ip::tcp; - - using namespace nghttp2::asio_http2; - using namespace nghttp2::asio_http2::client; - - int main(int argc, char *argv[]) { - boost::system::error_code ec; - boost::asio::io_service io_service; - - // connect to localhost:3000 - session sess(io_service, "localhost", "3000"); - - sess.on_connect([&sess](tcp::resolver::iterator endpoint_it) { - boost::system::error_code ec; - - auto printer = [](const response &res) { - res.on_data([](const uint8_t *data, std::size_t len) { - std::cerr.write(reinterpret_cast(data), len); - std::cerr << std::endl; - }); - }; - - std::size_t num = 3; - auto count = std::make_shared(num); - - for (std::size_t i = 0; i < num; ++i) { - auto req = sess.submit(ec, "GET", - "http://localhost:3000/" + std::to_string(i + 1)); - - req->on_response(printer); - req->on_close([&sess, count](uint32_t error_code) { - if (--*count == 0) { - // shutdown session after |num| requests were done. - sess.shutdown(); - } - }); - } - }); - - sess.on_error([](const boost::system::error_code &ec) { - std::cerr << "error: " << ec.message() << std::endl; - }); - - io_service.run(); - } - -Here is the sample to send 3 requests at once. Depending on the -server settings, these requests are processed out-of-order. In this -example, we have a trick to shutdown session after all requests were -done. We made ``count`` object which is shared pointer to int and is -initialized to 3. On each request closure (the invocation of the -callback set by ``nghttp2::asio_http2::client::request::on_close``), -we decrement the count. If count becomes 0, we are sure that all -requests have been done and initiate shutdown. diff --git a/doc/sources/nghttpx-howto.rst b/doc/sources/nghttpx-howto.rst deleted file mode 100644 index 1f7d87e..0000000 --- a/doc/sources/nghttpx-howto.rst +++ /dev/null @@ -1,303 +0,0 @@ -nghttpx - HTTP/2 proxy - HOW-TO -=============================== - -nghttpx is a proxy translating protocols between HTTP/2 and other -protocols (e.g., HTTP/1, SPDY). It operates in several modes and each -mode may require additional programs to work with. This article -describes each operation mode and explains the intended use-cases. It -also covers some useful options later. - -Default mode ------------- - -If nghttpx is invoked without any ``-s``, ``-p`` and ``--client``, it -operates in default mode. In this mode, nghttpx frontend listens for -HTTP/2 requests and translates them to HTTP/1 requests. Thus it works -as reverse proxy (gateway) for HTTP/2 clients to HTTP/1 web server. -HTTP/1 requests are also supported in frontend as a fallback. If -nghttpx is linked with spdylay library and frontend connection is -SSL/TLS, the frontend also supports SPDY protocol. - -By default, this mode's frontend connection is encrypted using -SSL/TLS. So server's private key and certificate must be supplied to -the command line (or through configuration file). In this case, the -frontend protocol selection will is done via ALPN or NPN. - -With ``--frontend-no-tls`` option, user can turn off SSL/TLS in -frontend connection. In this case, SPDY protocol is not available -even if spdylay library is liked to nghttpx. HTTP/2 and HTTP/1 are -available on the frontend and a HTTP/1 connection can be upgraded to -HTTP/2 using HTTP Upgrade. Starting HTTP/2 connection by sending -HTTP/2 connection preface is also supported. - -The backend is supposed to be HTTP/1 Web server. For example, to make -nghttpx listen to encrypted HTTP/2 requests at port 8443, and a -backend HTTP/1 web server is configured to listen to HTTP/1 request at -port 8080 in the same host, run nghttpx command-line like this:: - - $ nghttpx -f0.0.0.0,8443 -b127.0.0.1,8080 /path/to/server.key /path/to/server.crt - -Then HTTP/2 enabled client can access to the nghttpx in HTTP/2. For -example, you can send GET request to the server using nghttp:: - - $ nghttp -nv https://localhost:8443/ - -HTTP/2 proxy mode ------------------ - -If nghttpx is invoked with ``-s`` option, it operates in HTTP/2 proxy -mode. The supported protocols in frontend and backend connections are -the same in `default mode`_. The difference is that this mode acts -like forward proxy and assumes the backend is HTTP/1 proxy server -(e.g., squid, traffic server). So HTTP/1 request must include -absolute URI in request line. - -By default, frontend connection is encrypted. So this mode is also -called secure proxy. If nghttpx is linked with spdylay, it supports -SPDY protocols and it works as so called SPDY proxy. - -With ``--frontend-no-tls`` option, SSL/TLS is turned off in frontend -connection, so the connection gets insecure. - -The backend must be HTTP/1 proxy server. nghttpx supports multiple -backend server addresses. It translates incoming requests to HTTP/1 -request to backend server. The backend server performs real proxy -work for each request, for example, dispatching requests to the origin -server and caching contents. - -For example, to make nghttpx listen to encrypted HTTP/2 requests at -port 8443, and a backend HTTP/1 proxy server is configured to listen -to HTTP/1 request at port 8080 in the same host, run nghttpx -command-line like this:: - - $ nghttpx -s -f'*,8443' -b127.0.0.1,8080 /path/to/server.key /path/to/server.crt - -At the time of this writing, Firefox nightly supports HTTP/2 proxy. -Chromium can use nghttpx as secure (SPDY) proxy and will support -HTTP/2 proxy in the near future. - -To make Firefox nightly or Chromium use nghttpx as HTTP/2 or SPDY -proxy, user has to create proxy.pac script file like this: - -.. code-block:: javascript - - function FindProxyForURL(url, host) { - return "HTTPS SERVERADDR:PORT"; - } - -``SERVERADDR`` and ``PORT`` is the hostname/address and port of the -machine nghttpx is running. Please note that both Firefox nightly and -Chromium require valid certificate for secure proxy. - -For Firefox nightly, open Preference window and select Advanced then -click Network tab. Clicking Connection Settings button will show the -dialog. Select "Automatic proxy configuration URL" and enter the path -to proxy.pac file, something like this:: - - file:///path/to/proxy.pac - -For Chromium, use following command-line:: - - $ google-chrome --proxy-pac-url=file:///path/to/proxy.pac --use-npn - -As HTTP/1 proxy server, Squid may work as out-of-box. Traffic server -requires to be configured as forward proxy. Here is the minimum -configuration items to edit:: - - CONFIG proxy.config.reverse_proxy.enabled INT 0 - CONFIG proxy.config.url_remap.remap_required INT 0 - -Consult Traffic server `documentation -`_ -to know how to configure traffic server as forward proxy and its -security implications. - -Client mode ------------ - -If nghttpx is invoked with ``--client`` option, it operates in client -mode. In this mode, nghttpx listens for plain, unencrypted HTTP/2 and -HTTP/1 requests and translates them to encrypted HTTP/2 requests to -the backend. User cannot enable SSL/TLS in frontend connection. - -HTTP/1 frontend connection can be upgraded to HTTP/2 using HTTP -Upgrade. To disable SSL/TLS in backend connection, use -``--backend-no-tls`` option. - -By default, the number of backend HTTP/2 connections per worker -(thread) is determined by number of ``-b`` option. To adjust this -value, use ``--backend-http2-connections-per-worker`` option. - -The backend server is supporsed to be a HTTP/2 web server (e.g., -nghttpd). The one use-case of this mode is utilize existing HTTP/1 -clients to test HTTP/2 deployment. Suppose that HTTP/2 web server -listens to port 80 without encryption. Then run nghttpx as client -mode to access to that web server:: - - $ nghttpx --client -f127.0.0.1,8080 -b127.0.0.1,80 --backend-no-tls - -.. note:: - - You may need ``-k`` option if HTTP/2 server enables SSL/TLS and - its certificate is self-signed. But please note that it is - insecure. - -Then you can use curl to access HTTP/2 server via nghttpx:: - - $ curl http://localhost:8080/ - -Client proxy mode ------------------ - -If nghttpx is invoked with ``-p`` option, it operates in client proxy -mode. This mode behaves like `client mode`_, but it works like -forward proxy. So HTTP/1 request must include absolute URI in request -line. - -HTTP/1 frontend connection can be upgraded to HTTP/2 using HTTP -Upgrade. To disable SSL/TLS in backend connection, use -``--backend-no-tls`` option. - -By default, the number of backend HTTP/2 connections per worker -(thread) is determined by number of ``-b`` option. To adjust this -value, use ``--backend-http2-connections-per-worker`` option. - -The backend server must be a HTTP/2 proxy. You can use nghttpx in -`HTTP/2 proxy mode`_ as backend server. The one use-case of this mode -is utilize existing HTTP/1 clients to test HTTP/2 connections between -2 proxies. The another use-case is use this mode to aggregate local -HTTP/1 connections to one HTTP/2 backend encrypted connection. This -makes HTTP/1 clients which does not support secure proxy can use -secure HTTP/2 proxy via nghttpx client mode. - -Suppose that HTTP/2 proxy listens to port 8443, just like we saw in -`HTTP/2 proxy mode`_. To run nghttpx in client proxy mode to access -that server, invoke nghttpx like this:: - - $ nghttpx -p -f127.0.0.1,8080 -b127.0.0.1,8443 - -.. note:: - - You may need ``-k`` option if HTTP/2 server's certificate is - self-signed. But please note that it is insecure. - -Then you can use curl to issue HTTP request via HTTP/2 proxy:: - - $ curl --http-proxy=http://localhost:8080 http://www.google.com/ - -You can configure web browser to use localhost:8080 as forward -proxy. - -HTTP/2 bridge mode ------------------- - -If nghttpx is invoked with ``--http2-bridge`` option, it operates in -HTTP/2 bridge mode. The supported protocols in frontend connections -are the same in `default mode`_. The protocol in backend is HTTP/2 -only. - -With ``--frontend-no-tls`` option, SSL/TLS is turned off in frontend -connection, so the connection gets insecure. To disable SSL/TLS in -backend connection, use ``--backend-no-tls`` option. - -By default, the number of backend HTTP/2 connections per worker -(thread) is determined by number of ``-b`` option. To adjust this -value, use ``--backend-http2-connections-per-worker`` option. - -The backend server is supporsed to be a HTTP/2 web server or HTTP/2 -proxy. If backend server is HTTP/2 proxy, use -``--no-location-rewrite`` and ``--no-host-rewrite`` options to disable -rewriting location, host and :authority header field. - -The use-case of this mode is aggregate the incoming connections to one -HTTP/2 connection. One backend HTTP/2 connection is created per -worker (thread). - -Disable SSL/TLS ---------------- - -In `default mode`_, `HTTP/2 proxy mode`_ and `HTTP/2 bridge mode`_, -frontend connections are encrypted with SSL/TLS by default. To turn -off SSL/TLS, use ``--frontend-no-tls`` option. If this option is -used, the private key and certificate are not required to run nghttpx. - -In `client mode`_, `client proxy mode`_ and `HTTP/2 bridge mode`_, -backend connections are encrypted with SSL/TLS by default. To turn -off SSL/TLS, use ``--backend-no-tls`` option. - -Specifying additional CA certificate ------------------------------------- - -By default, nghttpx tries to read CA certificate from system. But -depending on the system you use, this may fail or is not supported. -To specify CA certificate manually, use ``--cacert`` option. The -specified file must be PEM format and can contain multiple -certificates. - -By default, nghttpx validates server's certificate. If you want to -turn off this validation, knowing this is really insecure and what you -are doing, you can use ``-k`` option to disable certificate -validation. - -Read/write rate limit ---------------------- - -nghttpx supports transfer rate limiting on frontend connections. You -can do rate limit per frontend connection for reading and writing -individually. - -To perform rate limit for reading, use ``--read-rate`` and -``--read-burst`` options. For writing, use ``--write-rate`` and -``--write-burst``. - -Please note that rate limit is performed on top of TCP and nothing to -do with HTTP/2 flow control. - -Rewriting location header field -------------------------------- - -nghttpx automatically rewrites location response header field if the -following all conditions satisfy: - -* URI in location header field is not absolute URI or is not https URI. -* URI in location header field includes non empty host component. -* host (without port) in URI in location header field must match the - host appearing in :authority or host header field. - -When rewrite happens, URI scheme and port are replaced with the ones -used in frontend, and host is replaced with which appears in -:authority or host request header field. :authority header field has -precedence. If the above conditions are not met with the host value -in :authority header field, rewrite is retried with the value in host -header field. - -Hot swapping ------------- - -nghttpx supports hot swapping using signals. The hot swapping in -nghttpx is multi step process. First send USR2 signal to nghttpx -process. It will do fork and execute new executable, using same -command-line arguments and environment variables. At this point, both -current and new processes can accept requests. To gracefully shutdown -current process, send QUIT signal to current nghttpx process. When -all existing frontend connections are done, the current process will -exit. At this point, only new nghttpx process exists and serves -incoming requests. - -Re-opening log files --------------------- - -When rotating log files, it is desirable to re-open log files after -log rotation daemon renamed existing log files. To tell nghttpx to -re-open log files, send USR1 signal to nghttpx process. It will -re-open files specified by ``--accesslog-file`` and -``--errorlog-file`` options. - -Multiple backend addresses --------------------------- - -nghttpx supports multiple backend addresses. To specify them, just -use ``-b`` option repeatedly. For example, to use backend1:8080 and -backend2:8080, use command-line like this: ``-bbackend1,8080 --bbackend2,8080``. For HTTP/2 backend, see also -``--backend-http2-connections-per-worker`` option. diff --git a/doc/sources/python-apiref.rst b/doc/sources/python-apiref.rst deleted file mode 100644 index d9d8494..0000000 --- a/doc/sources/python-apiref.rst +++ /dev/null @@ -1,437 +0,0 @@ -Python API Reference -==================== - -.. py:module:: nghttp2 - -nghttp2 offers some high level Python API to C library. The bindings -currently provide HPACK compressor and decompressor classes and HTTP/2 -server class. - -The extension module is called ``nghttp2``. - -``make`` will build the bindings. The target Python version is -determined by configure script. If the detected Python version is not -what you expect, specify a path to Python executable in ``PYTHON`` -variable as an argument to configure script (e.g., ``./configure -PYTHON=/usr/bin/python3.4``). - -HPACK API ---------- - -.. py:class:: HDDeflater(hd_table_bufsize_max=DEFLATE_MAX_HEADER_TABLE_SIZE) - - This class is used to perform header compression. The - *hd_table_bufsize_max* limits the usage of header table in the - given amount of bytes. The default value is - :py:data:`DEFLATE_MAX_HEADER_TABLE_SIZE`. This is necessary - because the deflater and inflater share the same amount of header - table and the inflater decides that number. The deflater may not - want to use all header table size because of limited memory - availability. In that case, *hd_table_bufsize_max* can be used to - cap the upper limit of table size whatever the header table size is - chosen by the inflater. - - .. py:method:: deflate(headers) - - Deflates the *headers*. The *headers* must be sequence of tuple - of name/value pair, which are byte strings (not unicode string). - - This method returns the deflated header block in byte string. - Raises the exception if any error occurs. - - .. py:method:: set_no_refset(no_refset) - - Tells the deflater not to use reference set if *no_refset* is - evaluated to ``True``. If that happens, on each subsequent - invocation of :py:meth:`deflate()`, deflater will clear up - refersent set. - - .. py:method:: change_table_size(hd_table_bufsize_max) - - Changes header table size to *hd_table_bufsize_max* byte. if - *hd_table_bufsize_max* is strictly larger than - ``hd_table_bufsize_max`` given in constructor, - ``hd_table_bufsize_max`` is used as header table size instead. - - Raises the exception if any error occurs. - - .. py:method:: get_hd_table() - - Returns copy of current dynamic header table. - -The following example shows how to deflate header name/value pairs: - -.. code-block:: python - - import binascii, nghttp2 - - deflater = nghttp2.HDDeflater() - - res = deflater.deflate([(b'foo', b'bar'), - (b'baz', b'buz')]) - - print(binascii.b2a_hex(res)) - - -.. py:class:: HDInflater() - - This class is used to perform header decompression. - - .. py:method:: inflate(data) - - Inflates the deflated header block *data*. The *data* must be - byte string. - - Raises the exception if any error occurs. - - .. py:method:: change_table_size(hd_table_bufsize_max) - - Changes header table size to *hd_table_bufsize_max* byte. - - Raises the exception if any error occurs. - - .. py:method:: get_hd_table() - - Returns copy of current dynamic header table. - -The following example shows how to inflate deflated header block: - -.. code-block:: python - - deflater = nghttp2.HDDeflater() - - data = deflater.deflate([(b'foo', b'bar'), - (b'baz', b'buz')]) - - inflater = nghttp2.HDInflater() - - hdrs = inflater.inflate(data) - - print(hdrs) - - -.. py:function:: print_hd_table(hdtable) - - Convenient function to print *hdtable* to the standard output. The - *hdtable* is the one retrieved by - :py:meth:`HDDeflater.get_hd_table()` or - :py:meth:`HDInflater.get_hd_table()`. This function does not work - if header name/value cannot be decoded using UTF-8 encoding. - - In output, ``s=N`` means the entry occupies ``N`` bytes in header - table. If ``r=y``, then the entry is in the reference set. - -.. py:data:: DEFAULT_HEADER_TABLE_SIZE - - The default header table size, which is 4096 as per HTTP/2 - specification. - -.. py:data:: DEFLATE_MAX_HEADER_TABLE_SIZE - - The default header table size for deflater. The initial value - is 4096. - -HTTP/2 servers --------------- - -.. note:: - - We use :py:mod:`asyncio` for HTTP/2 server classes. Therefore, - Python 3.4 or later is required to use these objects. To - explicitly configure nghttp2 build to use Python 3.4, specify the - ``PYTHON`` variable to the path to Python 3.4 executable when - invoking configure script like this:: - - $ ./configure PYTHON=/usr/bin/python3.4 - -.. py:class:: HTTP2Server(address, RequestHandlerClass, ssl=None) - - This class builds on top of the :py:mod:`asyncio` event loop. On - construction, *RequestHandlerClass* must be given, which must be a - subclass of :py:class:`BaseRequestHandler` class. - - The *address* must be a tuple of hostname/IP address and port to - bind. If hostname/IP address is ``None``, all interfaces are - assumed. - - To enable SSL/TLS, specify instance of :py:class:`ssl.SSLContext` - in *ssl*. Before passing *ssl* to - :py:func:`BaseEventLoop.create_server`, ALPN protocol identifiers - are set using :py:meth:`ssl.SSLContext.set_npn_protocols`. - - To disable SSL/TLS, omit *ssl* or specify ``None``. - - .. py:method:: serve_forever() - - Runs server and processes incoming requests forever. - -.. py:class:: BaseRequestHandler(http2, stream_id) - - The class is used to handle the single HTTP/2 stream. By default, - it does not nothing. It must be subclassed to handle each event - callback method. - - The first callback method invoked is :py:meth:`on_headers()`. It is - called when HEADERS frame, which includes request header fields, is - arrived. - - If request has request body, :py:meth:`on_data()` is invoked for - each chunk of received data chunk. - - When whole request is received, :py:meth:`on_request_done()` is - invoked. - - When stream is closed, :py:meth:`on_close()` is called. - - The application can send response using :py:meth:`send_response()` - method. It can be used in :py:meth:`on_headers()`, - :py:meth:`on_data()` or :py:meth:`on_request_done()`. - - The application can push resource using :py:meth:`push()` method. - It must be used before :py:meth:`send_response()` call. - - A :py:class:`BaseRequestHandler` has the following instance - variables: - - .. py:attribute:: client_address - - Contains a tuple of the form ``(host, port)`` referring to the - client's address. - - .. py:attribute:: stream_id - - Stream ID of this stream - - .. py:attribute:: scheme - - Scheme of the request URI. This is a value of ``:scheme`` - header field. - - .. py:attribute:: method - - Method of this stream. This is a value of ``:method`` header - field. - - .. py:attribute:: host - - This is a value of ``:authority`` or ``host`` header field. - - .. py:attribute:: path - - This is a value of ``:path`` header field. - - .. py:attribute:: headers - - Request header fields. - - A :py:class:`BaseRequestHandler` has the following methods: - - .. py:method:: on_headers() - - Called when request HEADERS is arrived. By default, this method - does nothing. - - .. py:method:: on_data(data) - - Called when a chunk of request body *data* is arrived. This - method will be called multiple times until all data are - received. By default, this method does nothing. - - .. py:method:: on_request_done() - - Called when whole request was received. By default, this method - does nothing. - - .. py:method:: on_close(error_code) - - Called when stream is about to close. The *error_code* - indicates the reason of closure. If it is ``0``, the stream is - going to close without error. - - .. py:method:: send_response(status=200, headers=None, body=None) - - Send response. The *status* is HTTP status code. The *headers* - is additional response headers. The *:status* header field will - be appended by the library. The *body* is the response body. - It could be ``None`` if response body is empty. Or it must be - instance of either ``str``, ``bytes``, :py:class:`io.IOBase` or - callable, called body generator, which takes one parameter, - size. The body generator generates response body. It can pause - generation of response so that it can wait for slow backend data - generation. When invoked, it should return tuple, byte string - at most size length and flag. The flag is either - :py:data:`DATA_OK`, :py:data:`DATA_EOF` or - :py:data:`DATA_DEFERRED`. For non-empty byte string and it is - not the last chunk of response, :py:data:`DATA_OK` must be - returned as flag. If this is the last chunk of the response - (byte string could be ``None``), :py:data:`DATA_EOF` must be - returned as flag. If there is no data available right now, but - additional data are anticipated, return tuple (``None``, - :py:data:`DATA_DEFERRED`). When data arrived, call - :py:meth:`resume()` and restart response body transmission. - - Only the body generator can pause response body generation; - instance of :py:class:`io.IOBase` must not block. - - If instance of ``str`` is specified as *body*, it will be - encoded using UTF-8. - - The *headers* is a list of tuple of the form ``(name, - value)``. The ``name`` and ``value`` can be either byte string - or unicode string. In the latter case, they will be encoded - using UTF-8. - - Raises the exception if any error occurs. - - .. py:method:: push(path, method='GET', request_headers=None, status=200, headers=None, body=None) - - Push a specified resource. The *path* is a path portion of - request URI for this resource. The *method* is a method to - access this resource. The *request_headers* is additional - request headers to access this resource. The ``:scheme``, - ``:method``, ``:authority`` and ``:path`` are appended by the - library. The ``:scheme`` and ``:authority`` are inherited from - request header fields of the associated stream. - - The *status* is HTTP status code. The *headers* is additional - response headers. The ``:status`` header field is appended by - the library. The *body* is the response body. It has the same - semantics of *body* parameter of :py:meth:`send_response()`. - - The headers and request_headers are a list of tuple of the form - ``(name, value)``. The ``name`` and ``value`` can be either byte - string or unicode string. In the latter case, they will be - encoded using UTF-8. - - Returns an instance of ``RequestHandlerClass`` specified in - :py:class:`HTTP2Server` constructor for the pushed resource. - - Raises the exception if any error occurs. - - .. py:method:: resume() - - Signals the restarting of response body transmission paused by - ``DATA_DEFERRED`` from the body generator (see - :py:meth:`send_response()` about the body generator). It is not - an error calling this method while response body transmission is - not paused. - -.. py:data:: DATA_OK - - ``DATA_OK`` indicates non empty data is generated from body generator. - -.. py:data:: DATA_EOF - - ``DATA_EOF`` indicates the end of response body. - -.. py:data:: DATA_DEFERRED - - ``DATA_DEFERRED`` indicates that data are not available right now - and response should be paused. - -The following example illustrates :py:class:`HTTP2Server` and -:py:class:`BaseRequestHandler` usage: - -.. code-block:: python - - #!/usr/bin/env python - - import io, ssl - - import nghttp2 - - class Handler(nghttp2.BaseRequestHandler): - - def on_headers(self): - self.push(path='/css/style.css', - request_headers = [('content-type', 'text/css')], - status=200, - body='body{margin:0;}') - - self.send_response(status=200, - headers = [('content-type', 'text/plain')], - body=io.BytesIO(b'nghttp2-python FTW')) - - ctx = ssl.SSLContext(ssl.PROTOCOL_SSLv23) - ctx.options = ssl.OP_ALL | ssl.OP_NO_SSLv2 | ssl.OP_NO_SSLv3 - ctx.load_cert_chain('server.crt', 'server.key') - - # give None to ssl to make the server non-SSL/TLS - server = nghttp2.HTTP2Server(('127.0.0.1', 8443), Handler, ssl=ctx) - server.serve_forever() - -The following example illustrates HTTP/2 server using asynchronous -response body generation. This is simplified reverse proxy: - -.. code-block:: python - - #!/usr/bin/env python - - import ssl - import os - import urllib - import asyncio - import io - - import nghttp2 - - @asyncio.coroutine - def get_http_header(handler, url): - url = urllib.parse.urlsplit(url) - ssl = url.scheme == 'https' - if url.port == None: - if url.scheme == 'https': - port = 443 - else: - port = 80 - else: - port = url.port - - connect = asyncio.open_connection(url.hostname, port, ssl=ssl) - reader, writer = yield from connect - req = 'GET {path} HTTP/1.0\r\n\r\n'.format(path=url.path or '/') - writer.write(req.encode('utf-8')) - # skip response header fields - while True: - line = yield from reader.readline() - line = line.rstrip() - if not line: - break - # read body - while True: - b = yield from reader.read(4096) - if not b: - break - handler.buf.write(b) - writer.close() - handler.buf.seek(0) - handler.eof = True - handler.resume() - - class Body: - def __init__(self, handler): - self.handler = handler - self.handler.eof = False - self.handler.buf = io.BytesIO() - - def generate(self, n): - buf = self.handler.buf - data = buf.read1(n) - if not data and not self.handler.eof: - return None, nghttp2.DATA_DEFERRED - return data, nghttp2.DATA_EOF if self.handler.eof else nghttp2.DATA_OK - - class Handler(nghttp2.BaseRequestHandler): - - def on_headers(self): - body = Body(self) - asyncio.async(get_http_header( - self, 'http://localhost' + self.path.decode('utf-8'))) - self.send_response(status=200, body=body.generate) - - ctx = ssl.SSLContext(ssl.PROTOCOL_SSLv23) - ctx.options = ssl.OP_ALL | ssl.OP_NO_SSLv2 | ssl.OP_NO_SSLv3 - ctx.load_cert_chain('server.crt', 'server.key') - - server = nghttp2.HTTP2Server(('127.0.0.1', 8443), Handler, ssl=ctx) - server.serve_forever() diff --git a/doc/sources/tutorial-client.rst b/doc/sources/tutorial-client.rst deleted file mode 100644 index 6ce1238..0000000 --- a/doc/sources/tutorial-client.rst +++ /dev/null @@ -1,439 +0,0 @@ -Tutorial: HTTP/2 client -========================= - -In this tutorial, we are going to write very primitive HTTP/2 -client. The complete source code, `libevent-client.c`_, is attached at -the end of this page. It also resides in examples directory in the -archive or repository. - -This simple client takes 1 argument, HTTPS URI, and retrieves the -resource denoted by the URI. Its synopsis is like this:: - - $ libevent-client HTTPS_URI - -We use libevent in this tutorial to handle networking I/O. Please -note that nghttp2 itself does not depend on libevent. - -First we do some setup routine for libevent and OpenSSL library in -function ``main()`` and ``run()``, which is not so relevant to nghttp2 -library use. The one thing you should look at is setup NPN callback. -The NPN callback is used for the client to select the next application -protocol over the SSL/TLS transport. In this tutorial, we use -`nghttp2_select_next_protocol()` function to select the HTTP/2 -protocol the library supports:: - - static int select_next_proto_cb(SSL *ssl _U_, unsigned char **out, - unsigned char *outlen, const unsigned char *in, - unsigned int inlen, void *arg _U_) { - if (nghttp2_select_next_protocol(out, outlen, in, inlen) <= 0) { - errx(1, "Server did not advertise " NGHTTP2_PROTO_VERSION_ID); - } - return SSL_TLSEXT_ERR_OK; - } - -The callback is set to the SSL_CTX object using -``SSL_CTX_set_next_proto_select_cb()`` function:: - - static SSL_CTX *create_ssl_ctx(void) { - SSL_CTX *ssl_ctx; - ssl_ctx = SSL_CTX_new(SSLv23_client_method()); - if (!ssl_ctx) { - errx(1, "Could not create SSL/TLS context: %s", - ERR_error_string(ERR_get_error(), NULL)); - } - SSL_CTX_set_options(ssl_ctx, - SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | - SSL_OP_NO_COMPRESSION | - SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION); - SSL_CTX_set_next_proto_select_cb(ssl_ctx, select_next_proto_cb, NULL); - return ssl_ctx; - } - -We use ``http2_session_data`` structure to store the data related to -the HTTP/2 session:: - - typedef struct { - nghttp2_session *session; - struct evdns_base *dnsbase; - struct bufferevent *bev; - http2_stream_data *stream_data; - } http2_session_data; - -Since this program only handles 1 URI, it uses only 1 stream. We store -its stream specific data in ``http2_stream_data`` structure and the -``stream_data`` points to it. The ``struct http2_stream_data`` is -defined as follows:: - - typedef struct { - /* The NULL-terminated URI string to retrieve. */ - const char *uri; - /* Parsed result of the |uri| */ - struct http_parser_url *u; - /* The authority portion of the |uri|, not NULL-terminated */ - char *authority; - /* The path portion of the |uri|, including query, not - NULL-terminated */ - char *path; - /* The length of the |authority| */ - size_t authoritylen; - /* The length of the |path| */ - size_t pathlen; - /* The stream ID of this stream */ - int32_t stream_id; - } http2_stream_data; - -We creates and initializes these structures in -``create_http2_session_data()`` and ``create_http2_stream_data()`` -respectively. - -Then we call function ``initiate_connection()`` to start connecting to -the remote server:: - - static void initiate_connection(struct event_base *evbase, SSL_CTX *ssl_ctx, - const char *host, uint16_t port, - http2_session_data *session_data) { - int rv; - struct bufferevent *bev; - SSL *ssl; - - ssl = create_ssl(ssl_ctx); - bev = bufferevent_openssl_socket_new( - evbase, -1, ssl, BUFFEREVENT_SSL_CONNECTING, - BEV_OPT_DEFER_CALLBACKS | BEV_OPT_CLOSE_ON_FREE); - bufferevent_setcb(bev, readcb, writecb, eventcb, session_data); - rv = bufferevent_socket_connect_hostname(bev, session_data->dnsbase, - AF_UNSPEC, host, port); - - if (rv != 0) { - errx(1, "Could not connect to the remote host %s", host); - } - session_data->bev = bev; - } - -We set 3 callbacks for the bufferevent: ``reacb``, ``writecb`` and -``eventcb``. - -The ``eventcb()`` is invoked by libevent event loop when an event -(e.g., connection has been established, timeout, etc) happens on the -underlying network socket:: - - static void eventcb(struct bufferevent *bev, short events, void *ptr) { - http2_session_data *session_data = (http2_session_data *)ptr; - if (events & BEV_EVENT_CONNECTED) { - int fd = bufferevent_getfd(bev); - int val = 1; - fprintf(stderr, "Connected\n"); - setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char *)&val, sizeof(val)); - initialize_nghttp2_session(session_data); - send_client_connection_header(session_data); - submit_request(session_data); - if (session_send(session_data) != 0) { - delete_http2_session_data(session_data); - } - return; - } - if (events & BEV_EVENT_EOF) { - warnx("Disconnected from the remote host"); - } else if (events & BEV_EVENT_ERROR) { - warnx("Network error"); - } else if (events & BEV_EVENT_TIMEOUT) { - warnx("Timeout"); - } - delete_http2_session_data(session_data); - } - -For ``BEV_EVENT_EOF``, ``BEV_EVENT_ERROR`` and ``BEV_EVENT_TIMEOUT`` -event, we just simply tear down the connection. The -``BEV_EVENT_CONNECTED`` event is invoked when SSL/TLS handshake is -finished successfully. We first initialize nghttp2 session object in -``initialize_nghttp2_session()`` function:: - - static void initialize_nghttp2_session(http2_session_data *session_data) { - nghttp2_session_callbacks *callbacks; - - nghttp2_session_callbacks_new(&callbacks); - - nghttp2_session_callbacks_set_send_callback(callbacks, send_callback); - - nghttp2_session_callbacks_set_on_frame_recv_callback(callbacks, - on_frame_recv_callback); - - nghttp2_session_callbacks_set_on_data_chunk_recv_callback( - callbacks, on_data_chunk_recv_callback); - - nghttp2_session_callbacks_set_on_stream_close_callback( - callbacks, on_stream_close_callback); - - nghttp2_session_callbacks_set_on_header_callback(callbacks, - on_header_callback); - - nghttp2_session_callbacks_set_on_begin_headers_callback( - callbacks, on_begin_headers_callback); - - nghttp2_session_client_new(&session_data->session, callbacks, session_data); - - nghttp2_session_callbacks_del(callbacks); - } - -Since we are creating client, we use `nghttp2_session_client_new()` to -initialize nghttp2 session object. We setup 7 callbacks for the -nghttp2 session. We'll explain these callbacks later. - -The `delete_http2_session_data()` destroys ``session_data`` and frees -its bufferevent, so it closes underlying connection as well. It also -calls `nghttp2_session_del()` to delete nghttp2 session object. - -We begin HTTP/2 communication by sending client connection preface, -which is 24 bytes magic byte string (:macro:`NGHTTP2_CLIENT_MAGIC`) -followed by SETTINGS frame. First 24 bytes magic string is -automatically sent by nghttp2 library. We send SETTINGS frame in -``send_client_connection_header()``:: - - static void send_client_connection_header(http2_session_data *session_data) { - nghttp2_settings_entry iv[1] = { - {NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100}}; - int rv; - - /* client 24 bytes magic string will be sent by nghttp2 library */ - rv = nghttp2_submit_settings(session_data->session, NGHTTP2_FLAG_NONE, iv, - ARRLEN(iv)); - if (rv != 0) { - errx(1, "Could not submit SETTINGS: %s", nghttp2_strerror(rv)); - } - } - -Here we specify SETTINGS_MAX_CONCURRENT_STREAMS to 100, which is -really not needed for this tiny example program, but we are -demonstrating the use of SETTINGS frame. To queue the SETTINGS frame -for the transmission, we use `nghttp2_submit_settings()`. Note that -`nghttp2_submit_settings()` function only queues the frame and not -actually send it. All ``nghttp2_submit_*()`` family functions have -this property. To actually send the frame, `nghttp2_session_send()` is -used, which is described about later. - -After the transmission of client connection header, we enqueue HTTP -request in ``submit_request()`` function:: - - static void submit_request(http2_session_data *session_data) { - int32_t stream_id; - http2_stream_data *stream_data = session_data->stream_data; - const char *uri = stream_data->uri; - const struct http_parser_url *u = stream_data->u; - nghttp2_nv hdrs[] = { - MAKE_NV2(":method", "GET"), - MAKE_NV(":scheme", &uri[u->field_data[UF_SCHEMA].off], - u->field_data[UF_SCHEMA].len), - MAKE_NV(":authority", stream_data->authority, stream_data->authoritylen), - MAKE_NV(":path", stream_data->path, stream_data->pathlen)}; - fprintf(stderr, "Request headers:\n"); - print_headers(stderr, hdrs, ARRLEN(hdrs)); - stream_id = nghttp2_submit_request(session_data->session, NULL, hdrs, - ARRLEN(hdrs), NULL, stream_data); - if (stream_id < 0) { - errx(1, "Could not submit HTTP request: %s", nghttp2_strerror(stream_id)); - } - - stream_data->stream_id = stream_id; - } - -We build HTTP request header fields in ``hdrs`` which is an array of -:type:`nghttp2_nv`. There are 4 header fields to be sent: ``:method``, -``:scheme``, ``:authority`` and ``:path``. To queue this HTTP request, -we use `nghttp2_submit_request()` function. The `stream_data` is -passed in *stream_user_data* parameter. It is used in nghttp2 -callbacks which we'll describe about later. -`nghttp2_submit_request()` returns the newly assigned stream ID for -this request. - -The next bufferevent callback is ``readcb()``, which is invoked when -data is available to read in the bufferevent input buffer:: - - static void readcb(struct bufferevent *bev, void *ptr) { - http2_session_data *session_data = (http2_session_data *)ptr; - ssize_t readlen; - struct evbuffer *input = bufferevent_get_input(bev); - size_t datalen = evbuffer_get_length(input); - unsigned char *data = evbuffer_pullup(input, -1); - - readlen = nghttp2_session_mem_recv(session_data->session, data, datalen); - if (readlen < 0) { - warnx("Fatal error: %s", nghttp2_strerror((int)readlen)); - delete_http2_session_data(session_data); - return; - } - if (evbuffer_drain(input, readlen) != 0) { - warnx("Fatal error: evbuffer_drain failed"); - delete_http2_session_data(session_data); - return; - } - if (session_send(session_data) != 0) { - delete_http2_session_data(session_data); - return; - } - } - -In this function, we feed all unprocessed, received data to nghttp2 -session object using `nghttp2_session_mem_recv()` function. The -`nghttp2_session_mem_recv()` processes the received data and may -invoke nghttp2 callbacks and also queue frames. Since there may be -pending frames, we call ``session_send()`` function to send those -frames. The ``session_send()`` function is defined as follows:: - - static int session_send(http2_session_data *session_data) { - int rv; - - rv = nghttp2_session_send(session_data->session); - if (rv != 0) { - warnx("Fatal error: %s", nghttp2_strerror(rv)); - return -1; - } - return 0; - } - -The `nghttp2_session_send()` function serializes the frame into wire -format and call ``send_callback()`` function of type -:type:`nghttp2_send_callback`. The ``send_callback()`` is defined as -follows:: - - static ssize_t send_callback(nghttp2_session *session _U_, const uint8_t *data, - size_t length, int flags _U_, void *user_data) { - http2_session_data *session_data = (http2_session_data *)user_data; - struct bufferevent *bev = session_data->bev; - bufferevent_write(bev, data, length); - return length; - } - -Since we use bufferevent to abstract network I/O, we just write the -data to the bufferevent object. Note that `nghttp2_session_send()` -continues to write all frames queued so far. If we were writing the -data to the non-blocking socket directly using ``write()`` system call -in the ``send_callback()``, we will surely get ``EAGAIN`` or -``EWOULDBLOCK`` since the socket has limited send buffer. If that -happens, we can return :macro:`NGHTTP2_ERR_WOULDBLOCK` to signal the -nghttp2 library to stop sending further data. But writing to the -bufferevent, we have to regulate the amount data to be buffered by -ourselves to avoid possible huge memory consumption. In this example -client, we do not limit anything. To see how to regulate the amount of -buffered data, see the ``send_callback()`` in the server tutorial. - -The third bufferevent callback is ``writecb()``, which is invoked when -all data written in the bufferevent output buffer have been sent:: - - static void writecb(struct bufferevent *bev _U_, void *ptr) { - http2_session_data *session_data = (http2_session_data *)ptr; - if (nghttp2_session_want_read(session_data->session) == 0 && - nghttp2_session_want_write(session_data->session) == 0 && - evbuffer_get_length(bufferevent_get_output(session_data->bev)) == 0) { - delete_http2_session_data(session_data); - } - } - -As described earlier, we just write off all data in `send_callback()`, -we have no data to write in this function. All we have to do is check -we have to drop connection or not. The nghttp2 session object keeps -track of reception and transmission of GOAWAY frame and other error -conditions as well. Using these information, nghttp2 session object -will tell whether the connection should be dropped or not. More -specifically, both `nghttp2_session_want_read()` and -`nghttp2_session_want_write()` return 0, we have no business in the -connection. But since we are using bufferevent and its deferred -callback option, the bufferevent output buffer may contain the pending -data when the ``writecb()`` is called. To handle this situation, we -also check whether the output buffer is empty or not. If these -conditions are met, we drop connection. - -We have already described about nghttp2 callback ``send_callback()``. -Let's describe remaining nghttp2 callbacks we setup in -``initialize_nghttp2_setup()`` function. - -Each request header name/value pair is emitted via -``on_header_callback`` function:: - - static int on_header_callback(nghttp2_session *session _U_, - const nghttp2_frame *frame, const uint8_t *name, - size_t namelen, const uint8_t *value, - size_t valuelen, uint8_t flags _U_, - void *user_data) { - http2_session_data *session_data = (http2_session_data *)user_data; - switch (frame->hd.type) { - case NGHTTP2_HEADERS: - if (frame->headers.cat == NGHTTP2_HCAT_RESPONSE && - session_data->stream_data->stream_id == frame->hd.stream_id) { - /* Print response headers for the initiated request. */ - print_header(stderr, name, namelen, value, valuelen); - break; - } - } - return 0; - } - -In this tutorial, we just print the name/value pair. - -After all name/value pairs are emitted for a frame, -``on_frame_recv_callback`` function is called:: - - static int on_frame_recv_callback(nghttp2_session *session _U_, - const nghttp2_frame *frame, void *user_data) { - http2_session_data *session_data = (http2_session_data *)user_data; - switch (frame->hd.type) { - case NGHTTP2_HEADERS: - if (frame->headers.cat == NGHTTP2_HCAT_RESPONSE && - session_data->stream_data->stream_id == frame->hd.stream_id) { - fprintf(stderr, "All headers received\n"); - } - break; - } - return 0; - } - -In this tutorial, we are just interested in the HTTP response -HEADERS. We check the frame type and its category (it should be -:macro:`NGHTTP2_HCAT_RESPONSE` for HTTP response HEADERS). Also check -its stream ID. - -The ``on_data_chunk_recv_callback()`` function is invoked when a chunk -of data is received from the remote peer:: - - static int on_data_chunk_recv_callback(nghttp2_session *session _U_, - uint8_t flags _U_, int32_t stream_id, - const uint8_t *data, size_t len, - void *user_data) { - http2_session_data *session_data = (http2_session_data *)user_data; - if (session_data->stream_data->stream_id == stream_id) { - fwrite(data, len, 1, stdout); - } - return 0; - } - -In our case, a chunk of data is response body. After checking stream -ID, we just write the received data to the stdout. Note that the -output in the terminal may be corrupted if the response body contains -some binary data. - -The ``on_stream_close_callback()`` function is invoked when the stream -is about to close:: - - static int on_stream_close_callback(nghttp2_session *session, int32_t stream_id, - nghttp2_error_code error_code, - void *user_data) { - http2_session_data *session_data = (http2_session_data *)user_data; - int rv; - - if (session_data->stream_data->stream_id == stream_id) { - fprintf(stderr, "Stream %d closed with error_code=%d\n", stream_id, - error_code); - rv = nghttp2_session_terminate_session(session, NGHTTP2_NO_ERROR); - if (rv != 0) { - return NGHTTP2_ERR_CALLBACK_FAILURE; - } - } - return 0; - } - -If the stream ID matches the one we initiated, it means that its -stream is going to be closed. Since we have finished to get the -resource we want (or the stream was reset by RST_STREAM from the -remote peer), we call `nghttp2_session_terminate_session()` to -commencing the closure of the HTTP/2 session gracefully. If you have -some data associated for the stream to be closed, you may delete it -here. diff --git a/doc/sources/tutorial-hpack.rst b/doc/sources/tutorial-hpack.rst deleted file mode 100644 index 0b4d59f..0000000 --- a/doc/sources/tutorial-hpack.rst +++ /dev/null @@ -1,118 +0,0 @@ -Tutorial: HPACK API -=================== - -In this tutorial, we describe basic use of HPACK API in nghttp2 -library. We briefly describe APIs for deflating and inflating header -fields. The example of using these APIs are presented as complete -source code `deflate.c`_. - -Deflating (encoding) headers ----------------------------- - -First we need to initialize :type:`nghttp2_hd_deflater` object using -`nghttp2_hd_deflate_new()` function:: - - int nghttp2_hd_deflate_new(nghttp2_hd_deflater **deflater_ptr, - size_t deflate_hd_table_bufsize_max); - -This function allocates :type:`nghttp2_hd_deflater` object and -initializes it and assigns its pointer to ``*deflater_ptr`` passed by -parameter. The *deflate_hd_table_bufsize_max* is the upper bound of -header table size the deflater will use. This will limit the memory -usage in deflater object for dynamic header table. If you doubt, just -specify 4096 here, which is the default upper bound of dynamic header -table buffer size. - -To encode header fields, `nghttp2_hd_deflate_hd()` function:: - - ssize_t nghttp2_hd_deflate_hd(nghttp2_hd_deflater *deflater, - uint8_t *buf, size_t buflen, - const nghttp2_nv *nva, size_t nvlen); - -The *deflater* is the deflater object initialized by -`nghttp2_hd_deflate_new()` function described above. The *buf* is a -pointer to buffer to store encoded byte string. The *buflen* is -capacity of *buf*. The *nva* is a pointer to :type:`nghttp2_nv`, -which is an array of header fields to deflate. The *nvlen* is the -number of header fields which *nva* contains. - -It is important to initialize and assign all members of -:type:`nghttp2_nv`. If a header field should not be inserted in -dynamic header table for a security reason, set -:macro:`NGHTTP2_NV_FLAG_NO_INDEX` flag in :member:`nghttp2_nv.flags`. - -`nghttp2_hd_deflate_hd()` processes all headers given in *nva*. The -*nva* must include all request or response header fields to be sent in -one HEADERS (or optionally following (multiple) CONTINUATION -frame(s)). The *buf* must have enough space to store the encoded -result. Otherwise, the function will fail. To estimate the upper -bound of encoded result, use `nghttp2_hd_deflate_bound()` function:: - - size_t nghttp2_hd_deflate_bound(nghttp2_hd_deflater *deflater, - const nghttp2_nv *nva, size_t nvlen); - -Pass this function with the same parameters *deflater*, *nva* and -*nvlen* which will be passed to `nghttp2_hd_deflate_hd()`. - -The subsequent call of `nghttp2_hd_deflate_hd()` will use current -encoder state and perform differential encoding which is the -fundamental compression gain for HPACK. - -Once `nghttp2_hd_deflate_hd()` fails, it cannot be undone and its -further call with the same deflater object shall fail. So it is very -important to use `nghttp2_hd_deflate_bound()` to know the required -size of buffer. - -To delete :type:`nghttp2_hd_deflater` object, use `nghttp2_hd_deflate_del()` -function. - -Inflating (decoding) headers ----------------------------- - -We use :type:`nghttp2_hd_inflater` object to inflate compressed header -data. To initialize the object, use `nghttp2_hd_inflate_new()`:: - - int nghttp2_hd_inflate_new(nghttp2_hd_inflater **inflater_ptr); - -To inflate header data, use `nghttp2_hd_inflate_hd()` function:: - - ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_inflater *inflater, - nghttp2_nv *nv_out, int *inflate_flags, - uint8_t *in, size_t inlen, int in_final); - -The *inflater* is the inflater object initialized above. The *nv_out* -is a pointer to :type:`nghttp2_nv` to store the result. The *in* is a -pointer to input data and *inlen* is its length. The caller is not -required to specify whole deflated header data to *in* at once. It -can call this function multiple times for portion of the data in -streaming way. If *in_final* is nonzero, it tells the function that -the passed data is the final sequence of deflated header data. The -*inflate_flags* is output parameter and successful call of this -function stores a set of flags in it. It will be described later. - -This function returns when each header field is inflated. When this -happens, the function sets :macro:`NGHTTP2_HD_INFLATE_EMIT` flag to -*inflate_flag* parameter and header field is stored in *nv_out*. The -return value indicates the number of data read from *in* to processed -so far. It may be less than *inlen*. The caller should call the -function repeatedly until all data are processed by adjusting *in* and -*inlen* with the processed bytes. - -If *in_final* is nonzero and all given data was processed, the -function sets :macro:`NGHTTP2_HD_INFLATE_FINAL` flag to -*inflate_flag*. If the caller sees this flag set, call -`nghttp2_hd_inflate_end_headers()` function. - -If *in_final* is zero and :macro:`NGHTTP2_HD_INFLATE_EMIT` flag is not -set, it indicates that all given data was processed. The caller is -required to pass subsequent data. - -It is important to note that the function may produce one or more -header fields even if *inlen* is 0 when *in_final* is nonzero, due to -differential encoding. - -The example use of `nghttp2_hd_inflate_hd()` is shown in -`inflate_header_block()` function in `deflate.c`_. - -To delete :type:`nghttp2_hd_inflater` object, use `nghttp2_hd_inflate_del()` -function. diff --git a/doc/sources/tutorial-server.rst b/doc/sources/tutorial-server.rst deleted file mode 100644 index 8f5b583..0000000 --- a/doc/sources/tutorial-server.rst +++ /dev/null @@ -1,550 +0,0 @@ -Tutorial: HTTP/2 server -========================= - -In this tutorial, we are going to write single-threaded, event-based -HTTP/2 web server, which supports HTTPS only. It can handle -concurrent multiple requests, but only the GET method is supported. The -complete source code, `libevent-server.c`_, is attached at the end of -this page. It also resides in examples directory in the archive or -repository. - -This simple server takes 3 arguments, a port number to listen to, a path to -your SSL/TLS private key file and a path to your certificate file. Its -synopsis is like this:: - - $ libevent-server PORT /path/to/server.key /path/to/server.crt - -We use libevent in this tutorial to handle networking I/O. Please -note that nghttp2 itself does not depend on libevent. - -First we create a setup routine for libevent and OpenSSL in the functions -``main()`` and ``run()``. One thing in there you should look at, is the setup -of the NPN callback. The NPN callback is used for the server to advertise -which application protocols the server supports to a client. In this example -program, when creating ``SSL_CTX`` object, we store the application protocol -name in the wire format of NPN in a statically allocated buffer. This is safe -because we only create one ``SSL_CTX`` object in the program's entire life -time:: - - static unsigned char next_proto_list[256]; - static size_t next_proto_list_len; - - static int next_proto_cb(SSL *s _U_, const unsigned char **data, - unsigned int *len, void *arg _U_) { - *data = next_proto_list; - *len = (unsigned int)next_proto_list_len; - return SSL_TLSEXT_ERR_OK; - } - - static SSL_CTX *create_ssl_ctx(const char *key_file, const char *cert_file) { - SSL_CTX *ssl_ctx; - EC_KEY *ecdh; - - ssl_ctx = SSL_CTX_new(SSLv23_server_method()); - - ... - - next_proto_list[0] = NGHTTP2_PROTO_VERSION_ID_LEN; - memcpy(&next_proto_list[1], NGHTTP2_PROTO_VERSION_ID, - NGHTTP2_PROTO_VERSION_ID_LEN); - next_proto_list_len = 1 + NGHTTP2_PROTO_VERSION_ID_LEN; - - SSL_CTX_set_next_protos_advertised_cb(ssl_ctx, next_proto_cb, NULL); - return ssl_ctx; - } - -The wire format of NPN is a sequence of length prefixed string. Exactly one -byte is used to specify the length of each protocol identifier. In this -tutorial, we advertise the specific HTTP/2 protocol version the current -nghttp2 library supports. The nghttp2 library exports its identifier in -:macro:`NGHTTP2_PROTO_VERSION_ID`. The ``next_proto_cb()`` function is the -server-side NPN callback. In the OpenSSL implementation, we just assign the -pointer to the NPN buffers we filled in earlier. The NPN callback function is -set to the ``SSL_CTX`` object using -``SSL_CTX_set_next_protos_advertised_cb()``. - -We use the ``app_content`` structure to store application-wide data:: - - struct app_context { - SSL_CTX *ssl_ctx; - struct event_base *evbase; - }; - -We use the ``http2_session_data`` structure to store session-level -(which corresponds to one HTTP/2 connection) data:: - - typedef struct http2_session_data { - struct http2_stream_data root; - struct bufferevent *bev; - app_context *app_ctx; - nghttp2_session *session; - char *client_addr; - } http2_session_data; - -We use the ``http2_stream_data`` structure to store stream-level data:: - - typedef struct http2_stream_data { - struct http2_stream_data *prev, *next; - char *request_path; - int32_t stream_id; - int fd; - } http2_stream_data; - -A single HTTP/2 session can have multiple streams. We manage these -multiple streams with a doubly linked list. The first element of this -list is pointed to by the ``root->next`` in ``http2_session_data``. -Initially, ``root->next`` is ``NULL``. We use libevent's bufferevent -structure to perform network I/O. Note that the bufferevent object is -kept in ``http2_session_data`` and not in ``http2_stream_data``. This -is because ``http2_stream_data`` is just a logical stream multiplexed -over the single connection managed by bufferevent in -``http2_session_data``. - -We first create a listener object to accept incoming connections. We use -libevent's ``struct evconnlistener`` for this purpose:: - - static void start_listen(struct event_base *evbase, const char *service, - app_context *app_ctx) { - int rv; - struct addrinfo hints; - struct addrinfo *res, *rp; - - memset(&hints, 0, sizeof(hints)); - hints.ai_family = AF_UNSPEC; - hints.ai_socktype = SOCK_STREAM; - hints.ai_flags = AI_PASSIVE; - #ifdef AI_ADDRCONFIG - hints.ai_flags |= AI_ADDRCONFIG; - #endif /* AI_ADDRCONFIG */ - - rv = getaddrinfo(NULL, service, &hints, &res); - if (rv != 0) { - errx(1, NULL); - } - for (rp = res; rp; rp = rp->ai_next) { - struct evconnlistener *listener; - listener = evconnlistener_new_bind( - evbase, acceptcb, app_ctx, LEV_OPT_CLOSE_ON_FREE | LEV_OPT_REUSEABLE, - 16, rp->ai_addr, rp->ai_addrlen); - if (listener) { - freeaddrinfo(res); - - return; - } - } - errx(1, "Could not start listener"); - } - -We specify the ``acceptcb`` callback which is called when a new connection is -accepted:: - - static void acceptcb(struct evconnlistener *listener _U_, int fd, - struct sockaddr *addr, int addrlen, void *arg) { - app_context *app_ctx = (app_context *)arg; - http2_session_data *session_data; - - session_data = create_http2_session_data(app_ctx, fd, addr, addrlen); - - bufferevent_setcb(session_data->bev, readcb, writecb, eventcb, session_data); - } - -Here we create the ``http2_session_data`` object. The bufferevent for -this connection is also initialized at this time. We specify three -callbacks for the bufferevent: ``readcb``, ``writecb`` and -``eventcb``. - -The ``eventcb()`` callback is invoked by the libevent event loop when an event -(e.g., connection has been established, timeout, etc) happens on the -underlying network socket:: - - static void eventcb(struct bufferevent *bev _U_, short events, void *ptr) { - http2_session_data *session_data = (http2_session_data *)ptr; - if (events & BEV_EVENT_CONNECTED) { - fprintf(stderr, "%s connected\n", session_data->client_addr); - - initialize_nghttp2_session(session_data); - - if (send_server_connection_header(session_data) != 0) { - delete_http2_session_data(session_data); - return; - } - - return; - } - if (events & BEV_EVENT_EOF) { - fprintf(stderr, "%s EOF\n", session_data->client_addr); - } else if (events & BEV_EVENT_ERROR) { - fprintf(stderr, "%s network error\n", session_data->client_addr); - } else if (events & BEV_EVENT_TIMEOUT) { - fprintf(stderr, "%s timeout\n", session_data->client_addr); - } - delete_http2_session_data(session_data); - } - -For the ``BEV_EVENT_EOF``, ``BEV_EVENT_ERROR`` and -``BEV_EVENT_TIMEOUT`` events, we just simply tear down the connection. -The ``delete_http2_session_data()`` function destroys the -``http2_session_data`` object and thus also its bufferevent member. -As a result, the underlying connection is closed. The -``BEV_EVENT_CONNECTED`` event is invoked when SSL/TLS handshake is -finished successfully. Now we are ready to start the HTTP/2 -communication. - -We initialize a nghttp2 session object which is done in -``initialize_nghttp2_session()``:: - - static void initialize_nghttp2_session(http2_session_data *session_data) { - nghttp2_session_callbacks *callbacks; - - nghttp2_session_callbacks_new(&callbacks); - - nghttp2_session_callbacks_set_send_callback(callbacks, send_callback); - - nghttp2_session_callbacks_set_on_frame_recv_callback(callbacks, - on_frame_recv_callback); - - nghttp2_session_callbacks_set_on_stream_close_callback( - callbacks, on_stream_close_callback); - - nghttp2_session_callbacks_set_on_header_callback(callbacks, - on_header_callback); - - nghttp2_session_callbacks_set_on_begin_headers_callback( - callbacks, on_begin_headers_callback); - - nghttp2_session_server_new(&session_data->session, callbacks, session_data); - - nghttp2_session_callbacks_del(callbacks); - } - -Since we are creating a server and uses options, the nghttp2 session -object is created using `nghttp2_session_server_new2()` function. We -registers five callbacks for nghttp2 session object. We'll talk about -these callbacks later. - -After initialization of the nghttp2 session object, we are going to send -a server connection header in ``send_server_connection_header()``:: - - static int send_server_connection_header(http2_session_data *session_data) { - nghttp2_settings_entry iv[1] = { - {NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100}}; - int rv; - - rv = nghttp2_submit_settings(session_data->session, NGHTTP2_FLAG_NONE, iv, - ARRLEN(iv)); - if (rv != 0) { - warnx("Fatal error: %s", nghttp2_strerror(rv)); - return -1; - } - return 0; - } - -The server connection header is a SETTINGS frame. We specify -SETTINGS_MAX_CONCURRENT_STREAMS to 100 in the SETTINGS frame. To queue -the SETTINGS frame for the transmission, we use -`nghttp2_submit_settings()`. Note that `nghttp2_submit_settings()` -function only queues the frame and it does not actually send it. All -functions in the ``nghttp2_submit_*()`` family have this property. To -actually send the frame, `nghttp2_session_send()` should be used, as -described later. - -Since bufferevent may buffer more than the first 24 bytes from the client, we -have to process them here since libevent won't invoke callback functions for -this pending data. To process the received data, we call the -``session_recv()`` function:: - - static int session_recv(http2_session_data *session_data) { - ssize_t readlen; - struct evbuffer *input = bufferevent_get_input(session_data->bev); - size_t datalen = evbuffer_get_length(input); - unsigned char *data = evbuffer_pullup(input, -1); - - readlen = nghttp2_session_mem_recv(session_data->session, data, datalen); - if (readlen < 0) { - warnx("Fatal error: %s", nghttp2_strerror((int)readlen)); - return -1; - } - if (evbuffer_drain(input, readlen) != 0) { - warnx("Fatal error: evbuffer_drain failed"); - return -1; - } - if (session_send(session_data) != 0) { - return -1; - } - return 0; - } - -In this function, we feed all unprocessed but already received data to the -nghttp2 session object using the `nghttp2_session_mem_recv()` function. The -`nghttp2_session_mem_recv()` function processes the data and may invoke the -nghttp2 callbacks and also queue outgoing frames. Since there may be pending -outgoing frames, we call ``session_send()`` function to send off those -frames. The ``session_send()`` function is defined as follows:: - - static int session_send(http2_session_data *session_data) { - int rv; - rv = nghttp2_session_send(session_data->session); - if (rv != 0) { - warnx("Fatal error: %s", nghttp2_strerror(rv)); - return -1; - } - return 0; - } - -The `nghttp2_session_send()` function serializes the frame into wire -format and calls ``send_callback()`` of type -:type:`nghttp2_send_callback`. The ``send_callback()`` is defined as -follows:: - - static ssize_t send_callback(nghttp2_session *session _U_, const uint8_t *data, - size_t length, int flags _U_, void *user_data) { - http2_session_data *session_data = (http2_session_data *)user_data; - struct bufferevent *bev = session_data->bev; - /* Avoid excessive buffering in server side. */ - if (evbuffer_get_length(bufferevent_get_output(session_data->bev)) >= - OUTPUT_WOULDBLOCK_THRESHOLD) { - return NGHTTP2_ERR_WOULDBLOCK; - } - bufferevent_write(bev, data, length); - return length; - } - -Since we use bufferevent to abstract network I/O, we just write the -data to the bufferevent object. Note that `nghttp2_session_send()` -continues to write all frames queued so far. If we were writing the -data to a non-blocking socket directly using ``write()`` system call -in the ``send_callback()``, we would surely get ``EAGAIN`` or -``EWOULDBLOCK`` back since the socket has limited send buffer. If that -happens, we can return :macro:`NGHTTP2_ERR_WOULDBLOCK` to signal the -nghttp2 library to stop sending further data. But when writing to the -bufferevent, we have to regulate the amount data to get buffered -ourselves to avoid using huge amounts of memory. To achieve this, we -check the size of the output buffer and if it reaches more than or -equal to ``OUTPUT_WOULDBLOCK_THRESHOLD`` bytes, we stop writing data -and return :macro:`NGHTTP2_ERR_WOULDBLOCK` to tell the library to stop -calling send_callback. - -The next bufferevent callback is ``readcb()``, which is invoked when -data is available to read in the bufferevent input buffer:: - - static void readcb(struct bufferevent *bev _U_, void *ptr) { - http2_session_data *session_data = (http2_session_data *)ptr; - if (session_recv(session_data) != 0) { - delete_http2_session_data(session_data); - return; - } - } - -In this function, we just call ``session_recv()`` to process incoming -data. - -The third bufferevent callback is ``writecb()``, which is invoked when all -data in the bufferevent output buffer has been sent:: - - static void writecb(struct bufferevent *bev, void *ptr) { - http2_session_data *session_data = (http2_session_data *)ptr; - if (evbuffer_get_length(bufferevent_get_output(bev)) > 0) { - return; - } - if (nghttp2_session_want_read(session_data->session) == 0 && - nghttp2_session_want_write(session_data->session) == 0) { - delete_http2_session_data(session_data); - return; - } - if (session_send(session_data) != 0) { - delete_http2_session_data(session_data); - return; - } - } - -First we check whether we should drop the connection or not. The nghttp2 -session object keeps track of reception and transmission of GOAWAY frames and -other error conditions as well. Using this information, the nghttp2 session -object will tell whether the connection should be dropped or not. More -specifically, if both `nghttp2_session_want_read()` and -`nghttp2_session_want_write()` return 0, we have no business left in the -connection. But since we are using bufferevent and its deferred callback -option, the bufferevent output buffer may contain pending data when the -``writecb()`` is called. To handle this, we check whether the output buffer is -empty or not. If all these conditions are met, we drop connection. - -Otherwise, we call ``session_send()`` to process the pending output -data. Remember that in ``send_callback()``, we must not write all data to -bufferevent to avoid excessive buffering. We continue processing pending data -when the output buffer becomes empty. - -We have already described the nghttp2 callback ``send_callback()``. Let's -learn about the remaining nghttp2 callbacks we setup in -``initialize_nghttp2_setup()`` function. - -The ``on_begin_headers_callback()`` function is invoked when the reception of -a header block in HEADERS or PUSH_PROMISE frame is started:: - - static int on_begin_headers_callback(nghttp2_session *session, - const nghttp2_frame *frame, - void *user_data) { - http2_session_data *session_data = (http2_session_data *)user_data; - http2_stream_data *stream_data; - - if (frame->hd.type != NGHTTP2_HEADERS || - frame->headers.cat != NGHTTP2_HCAT_REQUEST) { - return 0; - } - stream_data = create_http2_stream_data(session_data, frame->hd.stream_id); - nghttp2_session_set_stream_user_data(session, frame->hd.stream_id, - stream_data); - return 0; - } - -We are only interested in the HEADERS frame in this function. Since the -HEADERS frame has several roles in the HTTP/2 protocol, we check that it is a -request HEADERS, which opens new stream. If the frame is a request HEADERS, we -create a ``http2_stream_data`` object to store the stream related data. We -associate the created ``http2_stream_data`` object with the stream in the -nghttp2 session object using `nghttp2_set_stream_user_data()` to get the -object without searching through the doubly linked list. - -In this example server, we want to serve files relative to the current working -directory in which the program was invoked. Each header name/value pair is -emitted via ``on_header_callback`` function, which is called after -``on_begin_headers_callback()``:: - - static int on_header_callback(nghttp2_session *session, - const nghttp2_frame *frame, const uint8_t *name, - size_t namelen, const uint8_t *value, - size_t valuelen, uint8_t flags _U_, - void *user_data _U_) { - http2_stream_data *stream_data; - const char PATH[] = ":path"; - switch (frame->hd.type) { - case NGHTTP2_HEADERS: - if (frame->headers.cat != NGHTTP2_HCAT_REQUEST) { - break; - } - stream_data = - nghttp2_session_get_stream_user_data(session, frame->hd.stream_id); - if (!stream_data || stream_data->request_path) { - break; - } - if (namelen == sizeof(PATH) - 1 && memcmp(PATH, name, namelen) == 0) { - size_t j; - for (j = 0; j < valuelen && value[j] != '?'; ++j) - ; - stream_data->request_path = percent_decode(value, j); - } - break; - } - return 0; - } - -We search for the ``:path`` header field among the request headers and store -the requested path in the ``http2_stream_data`` object. In this example -program, we ignore ``:method`` header field and always treat the request as a -GET request. - -The ``on_frame_recv_callback()`` function is invoked when a frame is -fully received:: - - static int on_frame_recv_callback(nghttp2_session *session, - const nghttp2_frame *frame, void *user_data) { - http2_session_data *session_data = (http2_session_data *)user_data; - http2_stream_data *stream_data; - switch (frame->hd.type) { - case NGHTTP2_DATA: - case NGHTTP2_HEADERS: - /* Check that the client request has finished */ - if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) { - stream_data = - nghttp2_session_get_stream_user_data(session, frame->hd.stream_id); - /* For DATA and HEADERS frame, this callback may be called after - on_stream_close_callback. Check that stream still alive. */ - if (!stream_data) { - return 0; - } - return on_request_recv(session, session_data, stream_data); - } - break; - default: - break; - } - return 0; - } - -First we retrieve the ``http2_stream_data`` object associated with the stream -in ``on_begin_headers_callback()``. It is done using -`nghttp2_session_get_stream_user_data()`. If the requested path cannot be -served for some reason (e.g., file is not found), we send a 404 response, -which is done in ``error_reply()``. Otherwise, we open the requested file and -send its content. We send the header field ``:status`` as a single response -header. - -Sending the content of the file is done in ``send_response()`` function:: - - static int send_response(nghttp2_session *session, int32_t stream_id, - nghttp2_nv *nva, size_t nvlen, int fd) { - int rv; - nghttp2_data_provider data_prd; - data_prd.source.fd = fd; - data_prd.read_callback = file_read_callback; - - rv = nghttp2_submit_response(session, stream_id, nva, nvlen, &data_prd); - if (rv != 0) { - warnx("Fatal error: %s", nghttp2_strerror(rv)); - return -1; - } - return 0; - } - -The nghttp2 library uses the :type:`nghttp2_data_provider` structure to -send entity body to the remote peer. The ``source`` member of this -structure is a union and it can be either void pointer or int which is -intended to be used as file descriptor. In this example server, we use -the file descriptor. We also set the ``file_read_callback()`` callback -function to read the contents of the file:: - - static ssize_t file_read_callback(nghttp2_session *session _U_, - int32_t stream_id _U_, uint8_t *buf, - size_t length, uint32_t *data_flags, - nghttp2_data_source *source, - void *user_data _U_) { - int fd = source->fd; - ssize_t r; - while ((r = read(fd, buf, length)) == -1 && errno == EINTR) - ; - if (r == -1) { - return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; - } - if (r == 0) { - *data_flags |= NGHTTP2_DATA_FLAG_EOF; - } - return r; - } - -If an error happens while reading the file, we return -:macro:`NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE`. This tells the -library to send RST_STREAM to the stream. When all data has been read, set -the :macro:`NGHTTP2_DATA_FLAG_EOF` flag to ``*data_flags`` to tell the -nghttp2 library that we have finished reading the file. - -The `nghttp2_submit_response()` function is used to send the response to the -remote peer. - -The ``on_stream_close_callback()`` function is invoked when the stream -is about to close:: - - static int on_stream_close_callback(nghttp2_session *session, int32_t stream_id, - uint32_t error_code _U_, void *user_data) { - http2_session_data *session_data = (http2_session_data *)user_data; - http2_stream_data *stream_data; - - stream_data = nghttp2_session_get_stream_user_data(session, stream_id); - if (!stream_data) { - return 0; - } - remove_stream(session_data, stream_data); - delete_http2_stream_data(stream_data); - return 0; - } - -We destroy the ``http2_stream_data`` object in this function since the stream -is about to close and we no longer use that object. diff --git a/doc/tutorial-client.rst.in b/doc/tutorial-client.rst.in deleted file mode 100644 index 4f7fcfc..0000000 --- a/doc/tutorial-client.rst.in +++ /dev/null @@ -1,6 +0,0 @@ -.. include:: @top_srcdir@/doc/sources/tutorial-client.rst - -libevent-client.c ------------------ - -.. literalinclude:: @top_srcdir@/examples/libevent-client.c diff --git a/doc/tutorial-hpack.rst.in b/doc/tutorial-hpack.rst.in deleted file mode 100644 index 832dedf..0000000 --- a/doc/tutorial-hpack.rst.in +++ /dev/null @@ -1,6 +0,0 @@ -.. include:: @top_srcdir@/doc/sources/tutorial-hpack.rst - -deflate.c ---------- - -.. literalinclude:: @top_srcdir@/examples/deflate.c diff --git a/doc/tutorial-server.rst.in b/doc/tutorial-server.rst.in deleted file mode 100644 index 1972266..0000000 --- a/doc/tutorial-server.rst.in +++ /dev/null @@ -1,6 +0,0 @@ -.. include:: @top_srcdir@/doc/sources/tutorial-server.rst - -libevent-server.c ------------------ - -.. literalinclude:: @top_srcdir@/examples/libevent-server.c diff --git a/doc/types.rst b/doc/types.rst deleted file mode 100644 index acb2ef1..0000000 --- a/doc/types.rst +++ /dev/null @@ -1,914 +0,0 @@ - -Types (structs, unions and typedefs) -==================================== -.. type:: nghttp2_session - - - The primary structure to hold the resources needed for a HTTP/2 - session. The details of this structure are intentionally hidden - from the public API. - - -.. type:: nghttp2_info - - - This struct is what `nghttp2_version()` returns. It holds - information about the particular nghttp2 version. - - .. member:: int age - - Age of this struct. This instance of nghttp2 sets it to - :macro:`NGHTTP2_VERSION_AGE` but a future version may bump it and - add more struct fields at the bottom - .. member:: int version_num - - the :macro:`NGHTTP2_VERSION_NUM` number (since age ==1) - .. member:: const char *version_str - - points to the :macro:`NGHTTP2_VERSION` string (since age ==1) - .. member:: const char *proto_str - - points to the :macro:`NGHTTP2_PROTO_VERSION_ID` string this - instance implements (since age ==1) - -.. type:: nghttp2_nv - - - The name/value pair, which mainly used to represent header fields. - - .. member:: uint8_t *name - - The *name* byte string. If this struct is presented from library - (e.g., :type:`nghttp2_on_frame_recv_callback`), *name* is - guaranteed to be NULL-terminated. When application is - constructing this struct, *name* is not required to be - NULL-terminated. - .. member:: uint8_t *value - - The *value* byte string. If this struct is presented from - library (e.g., :type:`nghttp2_on_frame_recv_callback`), *value* - is guaranteed to be NULL-terminated. When application is - constructing this struct, *value* is not required to be - NULL-terminated. - .. member:: size_t namelen - - The length of the *name*, excluding terminating NULL. - .. member:: size_t valuelen - - The length of the *value*, excluding terminating NULL. - .. member:: uint8_t flags - - Bitwise OR of one or more of :type:`nghttp2_nv_flag`. - -.. type:: nghttp2_frame_hd - - The frame header. - - .. member:: size_t length - - The length field of this frame, excluding frame header. - .. member:: int32_t stream_id - - The stream identifier (aka, stream ID) - .. member:: uint8_t type - - The type of this frame. See `nghttp2_frame_type`. - .. member:: uint8_t flags - - The flags. - .. member:: uint8_t reserved - - Reserved bit in frame header. Currently, this is always set to 0 - and application should not expect something useful in here. - -.. type:: nghttp2_data_source - - - This union represents the some kind of data source passed to - :type:`nghttp2_data_source_read_callback`. - - .. member:: int fd - - The integer field, suitable for a file descriptor. - .. member:: void *ptr - - The pointer to an arbitrary object. - -.. type:: typedef ssize_t (*nghttp2_data_source_read_callback)( nghttp2_session *session, int32_t stream_id, uint8_t *buf, size_t length, uint32_t *data_flags, nghttp2_data_source *source, void *user_data) - - - Callback function invoked when the library wants to read data from - the *source*. The read data is sent in the stream *stream_id*. - The implementation of this function must read at most *length* - bytes of data from *source* (or possibly other places) and store - them in *buf* and return number of data stored in *buf*. If EOF is - reached, set :macro:`NGHTTP2_DATA_FLAG_EOF` flag in *\*data_flags*. - - Sometime it is desirable to avoid copying data into *buf* and let - application to send data directly. To achieve this, set - :macro:`NGHTTP2_DATA_FLAG_NO_COPY` to *\*data_flags* (and possibly - other flags, just like when we do copy), and return the number of - bytes to send without copying data into *buf*. The library, seeing - :macro:`NGHTTP2_DATA_FLAG_NO_COPY`, will invoke - :type:`nghttp2_send_data_callback`. The application must send - complete DATA frame in that callback. - - If this callback is set by `nghttp2_submit_request()`, - `nghttp2_submit_response()` or `nghttp2_submit_headers()` and - `nghttp2_submit_data()` with flag parameter - :macro:`NGHTTP2_FLAG_END_STREAM` set, and - :macro:`NGHTTP2_DATA_FLAG_EOF` flag is set to *\*data_flags*, DATA - frame will have END_STREAM flag set. Usually, this is expected - behaviour and all are fine. One exception is send trailer header - fields. You cannot send trailers after sending frame with - END_STREAM set. To avoid this problem, one can set - :macro:`NGHTTP2_DATA_FLAG_NO_END_STREAM` along with - :macro:`NGHTTP2_DATA_FLAG_EOF` to signal the library not to set - END_STREAM in DATA frame. Then application can use - `nghttp2_submit_trailer()` to send trailers. - `nghttp2_submit_trailer()` can be called inside this callback. - - If the application wants to postpone DATA frames (e.g., - asynchronous I/O, or reading data blocks for long time), it is - achieved by returning :macro:`NGHTTP2_ERR_DEFERRED` without reading - any data in this invocation. The library removes DATA frame from - the outgoing queue temporarily. To move back deferred DATA frame - to outgoing queue, call `nghttp2_session_resume_data()`. In case - of error, there are 2 choices. Returning - :macro:`NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE` will close the stream - by issuing RST_STREAM with :macro:`NGHTTP2_INTERNAL_ERROR`. If a - different error code is desirable, use - `nghttp2_submit_rst_stream()` with a desired error code and then - return :macro:`NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE`. Returning - :macro:`NGHTTP2_ERR_CALLBACK_FAILURE` will signal the entire session - failure. -.. type:: nghttp2_data_provider - - - This struct represents the data source and the way to read a chunk - of data from it. - - .. member:: nghttp2_data_source source - - The data source. - .. member:: nghttp2_data_source_read_callback read_callback - - The callback function to read a chunk of data from the *source*. - -.. type:: nghttp2_data - - - The DATA frame. The received data is delivered via - :type:`nghttp2_on_data_chunk_recv_callback`. - - .. member:: size_t padlen - - The length of the padding in this frame. This includes PAD_HIGH - and PAD_LOW. - -.. type:: nghttp2_priority_spec - - - The structure to specify stream dependency. - - .. member:: int32_t stream_id - - The stream ID of the stream to depend on. Specifying 0 makes - stream not depend any other stream. - .. member:: int32_t weight - - The weight of this dependency. - .. member:: uint8_t exclusive - - nonzero means exclusive dependency - -.. type:: nghttp2_headers - - - The HEADERS frame. It has the following members: - - .. member:: nghttp2_frame_hd hd - - The frame header. - .. member:: size_t padlen - - The length of the padding in this frame. This includes PAD_HIGH - and PAD_LOW. - .. member:: nghttp2_priority_spec pri_spec - - The priority specification - .. member:: nghttp2_nv *nva - - The name/value pairs. - .. member:: size_t nvlen - - The number of name/value pairs in *nva*. - .. member:: nghttp2_headers_category cat - - The category of this HEADERS frame. - -.. type:: nghttp2_priority - - - The PRIORITY frame. It has the following members: - - .. member:: nghttp2_frame_hd hd - - The frame header. - .. member:: nghttp2_priority_spec pri_spec - - The priority specification. - -.. type:: nghttp2_rst_stream - - - The RST_STREAM frame. It has the following members: - - .. member:: nghttp2_frame_hd hd - - The frame header. - .. member:: uint32_t error_code - - The error code. See :type:`nghttp2_error_code`. - -.. type:: nghttp2_settings_entry - - - The SETTINGS ID/Value pair. It has the following members: - - .. member:: int32_t settings_id - - The SETTINGS ID. See :type:`nghttp2_settings_id`. - .. member:: uint32_t value - - The value of this entry. - -.. type:: nghttp2_settings - - - The SETTINGS frame. It has the following members: - - .. member:: nghttp2_frame_hd hd - - The frame header. - .. member:: size_t niv - - The number of SETTINGS ID/Value pairs in *iv*. - .. member:: nghttp2_settings_entry *iv - - The pointer to the array of SETTINGS ID/Value pair. - -.. type:: nghttp2_push_promise - - - The PUSH_PROMISE frame. It has the following members: - - .. member:: nghttp2_frame_hd hd - - The frame header. - .. member:: size_t padlen - - The length of the padding in this frame. This includes PAD_HIGH - and PAD_LOW. - .. member:: nghttp2_nv *nva - - The name/value pairs. - .. member:: size_t nvlen - - The number of name/value pairs in *nva*. - .. member:: int32_t promised_stream_id - - The promised stream ID - .. member:: uint8_t reserved - - Reserved bit. Currently this is always set to 0 and application - should not expect something useful in here. - -.. type:: nghttp2_ping - - - The PING frame. It has the following members: - - .. member:: nghttp2_frame_hd hd - - The frame header. - .. member:: uint8_t opaque_data[8] - - The opaque data - -.. type:: nghttp2_goaway - - - The GOAWAY frame. It has the following members: - - .. member:: nghttp2_frame_hd hd - - The frame header. - .. member:: int32_t last_stream_id - - The last stream stream ID. - .. member:: uint32_t error_code - - The error code. See :type:`nghttp2_error_code`. - .. member:: uint8_t *opaque_data - - The additional debug data - .. member:: size_t opaque_data_len - - The length of *opaque_data* member. - .. member:: uint8_t reserved - - Reserved bit. Currently this is always set to 0 and application - should not expect something useful in here. - -.. type:: nghttp2_window_update - - - The WINDOW_UPDATE frame. It has the following members: - - .. member:: nghttp2_frame_hd hd - - The frame header. - .. member:: int32_t window_size_increment - - The window size increment. - .. member:: uint8_t reserved - - Reserved bit. Currently this is always set to 0 and application - should not expect something useful in here. - -.. type:: nghttp2_extension - - - The extension frame. It has following members: - - .. member:: nghttp2_frame_hd hd - - The frame header. - .. member:: void *payload - - The pointer to extension payload. The exact pointer type is - determined by hd.type. - - Currently, no extension is supported. This is a place holder for - the future extensions. - -.. type:: nghttp2_frame - - - This union includes all frames to pass them to various function - calls as nghttp2_frame type. The CONTINUATION frame is omitted - from here because the library deals with it internally. - - .. member:: nghttp2_frame_hd hd - - The frame header, which is convenient to inspect frame header. - .. member:: nghttp2_data data - - The DATA frame. - .. member:: nghttp2_headers headers - - The HEADERS frame. - .. member:: nghttp2_priority priority - - The PRIORITY frame. - .. member:: nghttp2_rst_stream rst_stream - - The RST_STREAM frame. - .. member:: nghttp2_settings settings - - The SETTINGS frame. - .. member:: nghttp2_push_promise push_promise - - The PUSH_PROMISE frame. - .. member:: nghttp2_ping ping - - The PING frame. - .. member:: nghttp2_goaway goaway - - The GOAWAY frame. - .. member:: nghttp2_window_update window_update - - The WINDOW_UPDATE frame. - .. member:: nghttp2_extension ext - - The extension frame. - -.. type:: typedef ssize_t (*nghttp2_send_callback)(nghttp2_session *session, const uint8_t *data, size_t length, int flags, void *user_data) - - - Callback function invoked when *session* wants to send data to the - remote peer. The implementation of this function must send at most - *length* bytes of data stored in *data*. The *flags* is currently - not used and always 0. It must return the number of bytes sent if - it succeeds. If it cannot send any single byte without blocking, - it must return :macro:`NGHTTP2_ERR_WOULDBLOCK`. For other errors, - it must return :macro:`NGHTTP2_ERR_CALLBACK_FAILURE`. The - *user_data* pointer is the third argument passed in to the call to - `nghttp2_session_client_new()` or `nghttp2_session_server_new()`. - - This callback is required if the application uses - `nghttp2_session_send()` to send data to the remote endpoint. If - the application uses solely `nghttp2_session_mem_send()` instead, - this callback function is unnecessary. - - To set this callback to :type:`nghttp2_session_callbacks`, use - `nghttp2_session_callbacks_set_send_callback()`. -.. type:: typedef int (*nghttp2_send_data_callback)(nghttp2_session *session, nghttp2_frame *frame, const uint8_t *framehd, size_t length, nghttp2_data_source *source, void *user_data) - - - Callback function invoked when :macro:`NGHTTP2_DATA_FLAG_NO_COPY` is - used in :type:`nghttp2_data_source_read_callback` to send complete - DATA frame. - - The *frame* is a DATA frame to send. The *framehd* is the - serialized frame header (9 bytes). The *length* is the length of - application data to send (this does not include padding). The - *source* is the same pointer passed to - :type:`nghttp2_data_source_read_callback`. - - The application first must send frame header *framehd* of length 9 - bytes. If ``frame->padlen > 0``, send 1 byte of value - ``frame->padlen - 1``. Then send exactly *length* bytes of - application data. Finally, if ``frame->padlen > 0``, send - ``frame->padlen - 1`` bytes of zero (they are padding). - - The application has to send complete DATA frame in this callback. - If all data were written successfully, return 0. - - If it cannot send it all, just return - :macro:`NGHTTP2_ERR_WOULDBLOCK`; the library will call this callback - with the same parameters later (It is recommended to send complete - DATA frame at once in this function to deal with error; if partial - frame data has already sent, it is impossible to send another data - in that state, and all we can do is tear down connection). If - application decided to reset this stream, return - :macro:`NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE`, then the library - will send RST_STREAM with INTERNAL_ERROR as error code. The - application can also return :macro:`NGHTTP2_ERR_CALLBACK_FAILURE`, - which will result in connection closure. Returning any other value - is treated as :macro:`NGHTTP2_ERR_CALLBACK_FAILURE` is returned. -.. type:: typedef ssize_t (*nghttp2_recv_callback)(nghttp2_session *session, uint8_t *buf, size_t length, int flags, void *user_data) - - - Callback function invoked when *session* wants to receive data from - the remote peer. The implementation of this function must read at - most *length* bytes of data and store it in *buf*. The *flags* is - currently not used and always 0. It must return the number of - bytes written in *buf* if it succeeds. If it cannot read any - single byte without blocking, it must return - :macro:`NGHTTP2_ERR_WOULDBLOCK`. If it gets EOF before it reads any - single byte, it must return :macro:`NGHTTP2_ERR_EOF`. For other - errors, it must return :macro:`NGHTTP2_ERR_CALLBACK_FAILURE`. - Returning 0 is treated as :macro:`NGHTTP2_ERR_WOULDBLOCK`. The - *user_data* pointer is the third argument passed in to the call to - `nghttp2_session_client_new()` or `nghttp2_session_server_new()`. - - This callback is required if the application uses - `nghttp2_session_recv()` to receive data from the remote endpoint. - If the application uses solely `nghttp2_session_mem_recv()` - instead, this callback function is unnecessary. - - To set this callback to :type:`nghttp2_session_callbacks`, use - `nghttp2_session_callbacks_set_recv_callback()`. -.. type:: typedef int (*nghttp2_on_frame_recv_callback)(nghttp2_session *session, const nghttp2_frame *frame, void *user_data) - - - Callback function invoked by `nghttp2_session_recv()` and - `nghttp2_session_mem_recv()` when a frame is received. The - *user_data* pointer is the third argument passed in to the call to - `nghttp2_session_client_new()` or `nghttp2_session_server_new()`. - - If frame is HEADERS or PUSH_PROMISE, the ``nva`` and ``nvlen`` - member of their data structure are always ``NULL`` and 0 - respectively. The header name/value pairs are emitted via - :type:`nghttp2_on_header_callback`. - - For HEADERS, PUSH_PROMISE and DATA frames, this callback may be - called after stream is closed (see - :type:`nghttp2_on_stream_close_callback`). The application should - check that stream is still alive using its own stream management or - :func:`nghttp2_session_get_stream_user_data()`. - - Only HEADERS and DATA frame can signal the end of incoming data. - If ``frame->hd.flags & NGHTTP2_FLAG_END_STREAM`` is nonzero, the - *frame* is the last frame from the remote peer in this stream. - - This callback won't be called for CONTINUATION frames. - HEADERS/PUSH_PROMISE + CONTINUATIONs are treated as single frame. - - The implementation of this function must return 0 if it succeeds. - If nonzero value is returned, it is treated as fatal error and - `nghttp2_session_recv()` and `nghttp2_session_mem_recv()` functions - immediately return :macro:`NGHTTP2_ERR_CALLBACK_FAILURE`. - - To set this callback to :type:`nghttp2_session_callbacks`, use - `nghttp2_session_callbacks_set_on_frame_recv_callback()`. -.. type:: typedef int (*nghttp2_on_invalid_frame_recv_callback)( nghttp2_session *session, const nghttp2_frame *frame, int lib_error_code, void *user_data) - - - Callback function invoked by `nghttp2_session_recv()` and - `nghttp2_session_mem_recv()` when an invalid non-DATA frame is - received. The error is indicated by the *lib_error_code*, which is - one of the values defined in :type:`nghttp2_error`. When this - callback function is invoked, the library automatically submits - either RST_STREAM or GOAWAY frame. The *user_data* pointer is the - third argument passed in to the call to - `nghttp2_session_client_new()` or `nghttp2_session_server_new()`. - - If frame is HEADERS or PUSH_PROMISE, the ``nva`` and ``nvlen`` - member of their data structure are always ``NULL`` and 0 - respectively. - - The implementation of this function must return 0 if it succeeds. - If nonzero is returned, it is treated as fatal error and - `nghttp2_session_recv()` and `nghttp2_session_mem_recv()` functions - immediately return :macro:`NGHTTP2_ERR_CALLBACK_FAILURE`. - - To set this callback to :type:`nghttp2_session_callbacks`, use - `nghttp2_session_callbacks_set_on_invalid_frame_recv_callback()`. -.. type:: typedef int (*nghttp2_on_data_chunk_recv_callback)(nghttp2_session *session, uint8_t flags, int32_t stream_id, const uint8_t *data, size_t len, void *user_data) - - - Callback function invoked when a chunk of data in DATA frame is - received. The *stream_id* is the stream ID this DATA frame belongs - to. The *flags* is the flags of DATA frame which this data chunk - is contained. ``(flags & NGHTTP2_FLAG_END_STREAM) != 0`` does not - necessarily mean this chunk of data is the last one in the stream. - You should use :type:`nghttp2_on_frame_recv_callback` to know all - data frames are received. The *user_data* pointer is the third - argument passed in to the call to `nghttp2_session_client_new()` or - `nghttp2_session_server_new()`. - - If the application uses `nghttp2_session_mem_recv()`, it can return - :macro:`NGHTTP2_ERR_PAUSE` to make `nghttp2_session_mem_recv()` - return without processing further input bytes. The memory by - pointed by the *data* is retained until - `nghttp2_session_mem_recv()` or `nghttp2_session_recv()` is called. - The application must retain the input bytes which was used to - produce the *data* parameter, because it may refer to the memory - region included in the input bytes. - - The implementation of this function must return 0 if it succeeds. - If nonzero is returned, it is treated as fatal error, and - `nghttp2_session_recv()` and `nghttp2_session_mem_recv()` functions - immediately return :macro:`NGHTTP2_ERR_CALLBACK_FAILURE`. - - To set this callback to :type:`nghttp2_session_callbacks`, use - `nghttp2_session_callbacks_set_on_data_chunk_recv_callback()`. -.. type:: typedef int (*nghttp2_before_frame_send_callback)(nghttp2_session *session, const nghttp2_frame *frame, void *user_data) - - - Callback function invoked just before the non-DATA frame *frame* is - sent. The *user_data* pointer is the third argument passed in to - the call to `nghttp2_session_client_new()` or - `nghttp2_session_server_new()`. - - The implementation of this function must return 0 if it succeeds. - If nonzero is returned, it is treated as fatal error and - `nghttp2_session_send()` and `nghttp2_session_mem_send()` functions - immediately return :macro:`NGHTTP2_ERR_CALLBACK_FAILURE`. - - To set this callback to :type:`nghttp2_session_callbacks`, use - `nghttp2_session_callbacks_set_before_frame_send_callback()`. -.. type:: typedef int (*nghttp2_on_frame_send_callback)(nghttp2_session *session, const nghttp2_frame *frame, void *user_data) - - - Callback function invoked after the frame *frame* is sent. The - *user_data* pointer is the third argument passed in to the call to - `nghttp2_session_client_new()` or `nghttp2_session_server_new()`. - - The implementation of this function must return 0 if it succeeds. - If nonzero is returned, it is treated as fatal error and - `nghttp2_session_send()` and `nghttp2_session_mem_send()` functions - immediately return :macro:`NGHTTP2_ERR_CALLBACK_FAILURE`. - - To set this callback to :type:`nghttp2_session_callbacks`, use - `nghttp2_session_callbacks_set_on_frame_send_callback()`. -.. type:: typedef int (*nghttp2_on_frame_not_send_callback)(nghttp2_session *session, const nghttp2_frame *frame, int lib_error_code, void *user_data) - - - Callback function invoked after the non-DATA frame *frame* is not - sent because of the error. The error is indicated by the - *lib_error_code*, which is one of the values defined in - :type:`nghttp2_error`. The *user_data* pointer is the third - argument passed in to the call to `nghttp2_session_client_new()` or - `nghttp2_session_server_new()`. - - The implementation of this function must return 0 if it succeeds. - If nonzero is returned, it is treated as fatal error and - `nghttp2_session_send()` and `nghttp2_session_mem_send()` functions - immediately return :macro:`NGHTTP2_ERR_CALLBACK_FAILURE`. - - `nghttp2_session_get_stream_user_data()` can be used to get - associated data. - - To set this callback to :type:`nghttp2_session_callbacks`, use - `nghttp2_session_callbacks_set_on_frame_not_send_callback()`. -.. type:: typedef int (*nghttp2_on_stream_close_callback)(nghttp2_session *session, int32_t stream_id, uint32_t error_code, void *user_data) - - - Callback function invoked when the stream *stream_id* is closed. - The reason of closure is indicated by the *error_code*. The - *error_code* is usually one of :macro:`nghttp2_error_code`, but that - is not guaranteed. The stream_user_data, which was specified in - `nghttp2_submit_request()` or `nghttp2_submit_headers()`, is still - available in this function. The *user_data* pointer is the third - argument passed in to the call to `nghttp2_session_client_new()` or - `nghttp2_session_server_new()`. - - This function is also called for a stream in reserved state. - - The implementation of this function must return 0 if it succeeds. - If nonzero is returned, it is treated as fatal error and - `nghttp2_session_recv()`, `nghttp2_session_mem_recv()`, - `nghttp2_session_send()`, and `nghttp2_session_mem_send()` - functions immediately return :macro:`NGHTTP2_ERR_CALLBACK_FAILURE`. - - To set this callback to :type:`nghttp2_session_callbacks`, use - `nghttp2_session_callbacks_set_on_stream_close_callback()`. -.. type:: typedef int (*nghttp2_on_begin_headers_callback)(nghttp2_session *session, const nghttp2_frame *frame, void *user_data) - - - Callback function invoked when the reception of header block in - HEADERS or PUSH_PROMISE is started. Each header name/value pair - will be emitted by :type:`nghttp2_on_header_callback`. - - The ``frame->hd.flags`` may not have - :macro:`NGHTTP2_FLAG_END_HEADERS` flag set, which indicates that one - or more CONTINUATION frames are involved. But the application does - not need to care about that because the header name/value pairs are - emitted transparently regardless of CONTINUATION frames. - - The server applications probably create an object to store - information about new stream if ``frame->hd.type == - NGHTTP2_HEADERS`` and ``frame->headers.cat == - NGHTTP2_HCAT_REQUEST``. If *session* is configured as server side, - ``frame->headers.cat`` is either ``NGHTTP2_HCAT_REQUEST`` - containing request headers or ``NGHTTP2_HCAT_HEADERS`` containing - trailer headers and never get PUSH_PROMISE in this callback. - - For the client applications, ``frame->hd.type`` is either - ``NGHTTP2_HEADERS`` or ``NGHTTP2_PUSH_PROMISE``. In case of - ``NGHTTP2_HEADERS``, ``frame->headers.cat == - NGHTTP2_HCAT_RESPONSE`` means that it is the first response - headers, but it may be non-final response which is indicated by 1xx - status code. In this case, there may be zero or more HEADERS frame - with ``frame->headers.cat == NGHTTP2_HCAT_HEADERS`` which has - non-final response code and finally client gets exactly one HEADERS - frame with ``frame->headers.cat == NGHTTP2_HCAT_HEADERS`` - containing final response headers (non-1xx status code). The - trailer headers also has ``frame->headers.cat == - NGHTTP2_HCAT_HEADERS`` which does not contain any status code. - - Returning :macro:`NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE` will close - the stream (promised stream if frame is PUSH_PROMISE) by issuing - RST_STREAM with :macro:`NGHTTP2_INTERNAL_ERROR`. In this case, - :type:`nghttp2_on_header_callback` and - :type:`nghttp2_on_frame_recv_callback` will not be invoked. If a - different error code is desirable, use - `nghttp2_submit_rst_stream()` with a desired error code and then - return :macro:`NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE`. Again, use - ``frame->push_promise.promised_stream_id`` as stream_id parameter - in `nghttp2_submit_rst_stream()` if frame is PUSH_PROMISE. - - The implementation of this function must return 0 if it succeeds. - It can return :macro:`NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE` to - reset the stream (promised stream if frame is PUSH_PROMISE). For - critical errors, it must return - :macro:`NGHTTP2_ERR_CALLBACK_FAILURE`. If the other value is - returned, it is treated as if :macro:`NGHTTP2_ERR_CALLBACK_FAILURE` - is returned. If :macro:`NGHTTP2_ERR_CALLBACK_FAILURE` is returned, - `nghttp2_session_mem_recv()` function will immediately return - :macro:`NGHTTP2_ERR_CALLBACK_FAILURE`. - - To set this callback to :type:`nghttp2_session_callbacks`, use - `nghttp2_session_callbacks_set_on_begin_headers_callback()`. -.. type:: typedef int (*nghttp2_on_header_callback)(nghttp2_session *session, const nghttp2_frame *frame, const uint8_t *name, size_t namelen, const uint8_t *value, size_t valuelen, uint8_t flags, void *user_data) - - - Callback function invoked when a header name/value pair is received - for the *frame*. The *name* of length *namelen* is header name. - The *value* of length *valuelen* is header value. The *flags* is - bitwise OR of one or more of :type:`nghttp2_nv_flag`. - - If :macro:`NGHTTP2_NV_FLAG_NO_INDEX` is set in *flags*, the receiver - must not index this name/value pair when forwarding it to the next - hop. More specifically, "Literal Header Field never Indexed" - representation must be used in HPACK encoding. - - When this callback is invoked, ``frame->hd.type`` is either - :macro:`NGHTTP2_HEADERS` or :macro:`NGHTTP2_PUSH_PROMISE`. After all - header name/value pairs are processed with this callback, and no - error has been detected, :type:`nghttp2_on_frame_recv_callback` - will be invoked. If there is an error in decompression, - :type:`nghttp2_on_frame_recv_callback` for the *frame* will not be - invoked. - - Both *name* and *value* are guaranteed to be NULL-terminated. The - *namelen* and *valuelen* do not include terminal NULL. If - `nghttp2_option_set_no_http_messaging()` is used with nonzero - value, NULL character may be included in *name* or *value* before - terminating NULL. - - Please note that unless `nghttp2_option_set_no_http_messaging()` is - used, nghttp2 library does perform validation against the *name* - and the *value* using `nghttp2_check_header_name()` and - `nghttp2_check_header_value()`. In addition to this, nghttp2 - performs vaidation based on HTTP Messaging rule, which is briefly - explained in :ref:`http-messaging` section. - - If the application uses `nghttp2_session_mem_recv()`, it can return - :macro:`NGHTTP2_ERR_PAUSE` to make `nghttp2_session_mem_recv()` - return without processing further input bytes. The memory pointed - by *frame*, *name* and *value* parameters are retained until - `nghttp2_session_mem_recv()` or `nghttp2_session_recv()` is called. - The application must retain the input bytes which was used to - produce these parameters, because it may refer to the memory region - included in the input bytes. - - Returning :macro:`NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE` will close - the stream (promised stream if frame is PUSH_PROMISE) by issuing - RST_STREAM with :macro:`NGHTTP2_INTERNAL_ERROR`. In this case, - :type:`nghttp2_on_header_callback` and - :type:`nghttp2_on_frame_recv_callback` will not be invoked. If a - different error code is desirable, use - `nghttp2_submit_rst_stream()` with a desired error code and then - return :macro:`NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE`. Again, use - ``frame->push_promise.promised_stream_id`` as stream_id parameter - in `nghttp2_submit_rst_stream()` if frame is PUSH_PROMISE. - - The implementation of this function must return 0 if it succeeds. - It may return :macro:`NGHTTP2_ERR_PAUSE` or - :macro:`NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE`. For other critical - failures, it must return :macro:`NGHTTP2_ERR_CALLBACK_FAILURE`. If - the other nonzero value is returned, it is treated as - :macro:`NGHTTP2_ERR_CALLBACK_FAILURE`. If - :macro:`NGHTTP2_ERR_CALLBACK_FAILURE` is returned, - `nghttp2_session_recv()` and `nghttp2_session_mem_recv()` functions - immediately return :macro:`NGHTTP2_ERR_CALLBACK_FAILURE`. - - To set this callback to :type:`nghttp2_session_callbacks`, use - `nghttp2_session_callbacks_set_on_header_callback()`. -.. type:: typedef ssize_t (*nghttp2_select_padding_callback)(nghttp2_session *session, const nghttp2_frame *frame, size_t max_payloadlen, void *user_data) - - - Callback function invoked when the library asks application how - many padding bytes are required for the transmission of the - *frame*. The application must choose the total length of payload - including padded bytes in range [frame->hd.length, max_payloadlen], - inclusive. Choosing number not in this range will be treated as - :macro:`NGHTTP2_ERR_CALLBACK_FAILURE`. Returning - ``frame->hd.length`` means no padding is added. Returning - :macro:`NGHTTP2_ERR_CALLBACK_FAILURE` will make - `nghttp2_session_send()` and `nghttp2_session_mem_send()` functions - immediately return :macro:`NGHTTP2_ERR_CALLBACK_FAILURE`. - - To set this callback to :type:`nghttp2_session_callbacks`, use - `nghttp2_session_callbacks_set_select_padding_callback()`. -.. type:: typedef ssize_t (*nghttp2_data_source_read_length_callback)( nghttp2_session *session, uint8_t frame_type, int32_t stream_id, int32_t session_remote_window_size, int32_t stream_remote_window_size, uint32_t remote_max_frame_size, void *user_data) - - - Callback function invoked when library wants to get max length of - data to send data to the remote peer. The implementation of this - function should return a value in the following range. [1, - min(*session_remote_window_size*, *stream_remote_window_size*, - *remote_max_frame_size*)]. If a value greater than this range is - returned than the max allow value will be used. Returning a value - smaller than this range is treated as - :macro:`NGHTTP2_ERR_CALLBACK_FAILURE`. The *frame_type* is provided - for future extensibility and identifies the type of frame (see - :type:`nghttp2_frame_type`) for which to get the length for. - Currently supported frame types are: :macro:`NGHTTP2_DATA`. - - This callback can be used to control the length in bytes for which - :type:`nghttp2_data_source_read_callback` is allowed to send to the - remote endpoint. This callback is optional. Returning - :macro:`NGHTTP2_ERR_CALLBACK_FAILURE` will signal the entire session - failure. - - To set this callback to :type:`nghttp2_session_callbacks`, use - `nghttp2_session_callbacks_set_data_source_read_length_callback()`. -.. type:: typedef int (*nghttp2_on_begin_frame_callback)(nghttp2_session *session, const nghttp2_frame_hd *hd, void *user_data) - - - Callback function invoked when a frame header is received. The - *hd* points to received frame header. - - Unlike :type:`nghttp2_on_frame_recv_callback`, this callback will - also be called when frame header of CONTINUATION frame is received. - - If both :type:`nghttp2_on_begin_frame_callback` and - :type:`nghttp2_on_begin_headers_callback` are set and HEADERS or - PUSH_PROMISE is received, :type:`nghttp2_on_begin_frame_callback` - will be called first. - - The implementation of this function must return 0 if it succeeds. - If nonzero value is returned, it is treated as fatal error and - `nghttp2_session_recv()` and `nghttp2_session_mem_recv()` functions - immediately return :macro:`NGHTTP2_ERR_CALLBACK_FAILURE`. - - To set this callback to :type:`nghttp2_session_callbacks`, use - `nghttp2_session_callbacks_set_on_begin_frame_callback()`. -.. type:: nghttp2_session_callbacks - - - Callback functions for :type:`nghttp2_session`. The details of - this structure are intentionally hidden from the public API. - - -.. type:: typedef void *(*nghttp2_malloc)(size_t size, void *mem_user_data) - - - Custom memory allocator to replace malloc(). The *mem_user_data* - is the mem_user_data member of :type:`nghttp2_mem` structure. -.. type:: typedef void (*nghttp2_free)(void *ptr, void *mem_user_data) - - - Custom memory allocator to replace free(). The *mem_user_data* is - the mem_user_data member of :type:`nghttp2_mem` structure. -.. type:: typedef void *(*nghttp2_calloc)(size_t nmemb, size_t size, void *mem_user_data) - - - Custom memory allocator to replace calloc(). The *mem_user_data* - is the mem_user_data member of :type:`nghttp2_mem` structure. -.. type:: typedef void *(*nghttp2_realloc)(void *ptr, size_t size, void *mem_user_data) - - - Custom memory allocator to replace realloc(). The *mem_user_data* - is the mem_user_data member of :type:`nghttp2_mem` structure. -.. type:: nghttp2_mem - - - Custom memory allocator functions and user defined pointer. The - *mem_user_data* member is passed to each allocator function. This - can be used, for example, to achieve per-session memory pool. - - In the following example code, ``my_malloc``, ``my_free``, - ``my_calloc`` and ``my_realloc`` are the replacement of the - standard allocators ``malloc``, ``free``, ``calloc`` and - ``realloc`` respectively:: - - void *my_malloc_cb(size_t size, void *mem_user_data) { - return my_malloc(size); - } - - void my_free_cb(void *ptr, void *mem_user_data) { my_free(ptr); } - - void *my_calloc_cb(size_t nmemb, size_t size, void *mem_user_data) { - return my_calloc(nmemb, size); - } - - void *my_realloc_cb(void *ptr, size_t size, void *mem_user_data) { - return my_realloc(ptr, size); - } - - void session_new() { - nghttp2_session *session; - nghttp2_session_callbacks *callbacks; - nghttp2_mem mem = {NULL, my_malloc_cb, my_free_cb, my_calloc_cb, - my_realloc_cb}; - - ... - - nghttp2_session_client_new3(&session, callbacks, NULL, NULL, &mem); - - ... - } - - .. member:: void *mem_user_data - - An arbitrary user supplied data. This is passed to each - allocator function. - .. member:: nghttp2_malloc malloc - - Custom allocator function to replace malloc(). - .. member:: nghttp2_free free - - Custom allocator function to replace free(). - .. member:: nghttp2_calloc calloc - - Custom allocator function to replace calloc(). - .. member:: nghttp2_realloc realloc - - Custom allocator function to replace realloc(). - -.. type:: nghttp2_option - - - Configuration options for :type:`nghttp2_session`. The details of - this structure are intentionally hidden from the public API. - - -.. type:: nghttp2_hd_deflater - - - HPACK deflater object. - - -.. type:: nghttp2_hd_inflater - - - HPACK inflater object. - - diff --git a/examples/Makefile.am b/examples/Makefile.am deleted file mode 100644 index 5bcf2d1..0000000 --- a/examples/Makefile.am +++ /dev/null @@ -1,95 +0,0 @@ -# nghttp2 - HTTP/2 C Library - -# Copyright (c) 2012 Tatsuhiro Tsujikawa - -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: - -# The above copyright notice and this permission notice shall be -# included in all copies or substantial portions of the Software. - -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -if ENABLE_EXAMPLES - -AM_CFLAGS = $(WARNCFLAGS) -AM_CPPFLAGS = \ - -Wall \ - -I$(top_srcdir)/lib/includes \ - -I$(top_builddir)/lib/includes \ - -I$(top_srcdir)/src/includes \ - -I$(top_srcdir)/third-party \ - @LIBEVENT_OPENSSL_CFLAGS@ \ - @OPENSSL_CFLAGS@ \ - @DEFS@ -LDADD = $(top_builddir)/lib/libnghttp2.la \ - $(top_builddir)/third-party/libhttp-parser.la \ - @LIBEVENT_OPENSSL_LIBS@ \ - @OPENSSL_LIBS@ - -noinst_PROGRAMS = client libevent-client libevent-server deflate - -client_SOURCES = client.c - -libevent_client_SOURCES = libevent-client.c - -libevent_server_SOURCES = libevent-server.c - -deflate_SOURCES = deflate.c - -if ENABLE_TINY_NGHTTPD - -noinst_PROGRAMS += tiny-nghttpd - -tiny_nghttpd_SOURCES = tiny-nghttpd.c - -endif # ENABLE_TINY_NGHTTPD - -if ENABLE_ASIO_LIB - -noinst_PROGRAMS += asio-sv asio-sv2 asio-cl asio-cl2 - -# AM_CPPFLAGS must be placed first, so that header file (e.g., -# nghttp2/nghttp2.h) in this package is used rather than installed -# one. -ASIOCPPFLAGS = ${AM_CPPFLAGS} ${BOOST_CPPFLAGS} -ASIOLDADD = $(top_builddir)/lib/libnghttp2.la \ - $(top_builddir)/src/libnghttp2_asio.la @JEMALLOC_LIBS@ \ - $(top_builddir)/third-party/libhttp-parser.la \ - ${BOOST_LDFLAGS} \ - ${BOOST_ASIO_LIB} \ - ${BOOST_THREAD_LIB} \ - ${BOOST_SYSTEM_LIB} \ - @OPENSSL_LIBS@ \ - @APPLDFLAGS@ - -asio_sv_SOURCES = asio-sv.cc -asio_sv_CPPFLAGS = ${ASIOCPPFLAGS} -asio_sv_LDADD = ${ASIOLDADD} - -asio_sv2_SOURCES = asio-sv2.cc -asio_sv2_CPPFLAGS = ${ASIOCPPFLAGS} -asio_sv2_LDADD = ${ASIOLDADD} - -asio_cl_SOURCES = asio-cl.cc -asio_cl_CPPFLAGS = ${ASIOCPPFLAGS} -asio_cl_LDADD = ${ASIOLDADD} - -asio_cl2_SOURCES = asio-cl2.cc -asio_cl2_CPPFLAGS = ${ASIOCPPFLAGS} -asio_cl2_LDADD = ${ASIOLDADD} - -endif # ENABLE_ASIO_LIB - -endif # ENABLE_EXAMPLES diff --git a/examples/Makefile.in b/examples/Makefile.in deleted file mode 100644 index 9b55d0a..0000000 --- a/examples/Makefile.in +++ /dev/null @@ -1,926 +0,0 @@ -# Makefile.in generated by automake 1.14.1 from Makefile.am. -# @configure_input@ - -# Copyright (C) 1994-2013 Free Software Foundation, Inc. - -# This Makefile.in is free software; the Free Software Foundation -# gives unlimited permission to copy and/or distribute it, -# with or without modifications, as long as this notice is preserved. - -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY, to the extent permitted by law; without -# even the implied warranty of MERCHANTABILITY or FITNESS FOR A -# PARTICULAR PURPOSE. - -@SET_MAKE@ - -# nghttp2 - HTTP/2 C Library - -# Copyright (c) 2012 Tatsuhiro Tsujikawa - -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: - -# The above copyright notice and this permission notice shall be -# included in all copies or substantial portions of the Software. - -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -VPATH = @srcdir@ -am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)' -am__make_running_with_option = \ - case $${target_option-} in \ - ?) ;; \ - *) echo "am__make_running_with_option: internal error: invalid" \ - "target option '$${target_option-}' specified" >&2; \ - exit 1;; \ - esac; \ - has_opt=no; \ - sane_makeflags=$$MAKEFLAGS; \ - if $(am__is_gnu_make); then \ - sane_makeflags=$$MFLAGS; \ - else \ - case $$MAKEFLAGS in \ - *\\[\ \ ]*) \ - bs=\\; \ - sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ - | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ - esac; \ - fi; \ - skip_next=no; \ - strip_trailopt () \ - { \ - flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ - }; \ - for flg in $$sane_makeflags; do \ - test $$skip_next = yes && { skip_next=no; continue; }; \ - case $$flg in \ - *=*|--*) continue;; \ - -*I) strip_trailopt 'I'; skip_next=yes;; \ - -*I?*) strip_trailopt 'I';; \ - -*O) strip_trailopt 'O'; skip_next=yes;; \ - -*O?*) strip_trailopt 'O';; \ - -*l) strip_trailopt 'l'; skip_next=yes;; \ - -*l?*) strip_trailopt 'l';; \ - -[dEDm]) skip_next=yes;; \ - -[JT]) skip_next=yes;; \ - esac; \ - case $$flg in \ - *$$target_option*) has_opt=yes; break;; \ - esac; \ - done; \ - test $$has_opt = yes -am__make_dryrun = (target_option=n; $(am__make_running_with_option)) -am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) -pkgdatadir = $(datadir)/@PACKAGE@ -pkgincludedir = $(includedir)/@PACKAGE@ -pkglibdir = $(libdir)/@PACKAGE@ -pkglibexecdir = $(libexecdir)/@PACKAGE@ -am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd -install_sh_DATA = $(install_sh) -c -m 644 -install_sh_PROGRAM = $(install_sh) -c -install_sh_SCRIPT = $(install_sh) -c -INSTALL_HEADER = $(INSTALL_DATA) -transform = $(program_transform_name) -NORMAL_INSTALL = : -PRE_INSTALL = : -POST_INSTALL = : -NORMAL_UNINSTALL = : -PRE_UNINSTALL = : -POST_UNINSTALL = : -build_triplet = @build@ -host_triplet = @host@ -target_triplet = @target@ -@ENABLE_EXAMPLES_TRUE@noinst_PROGRAMS = client$(EXEEXT) \ -@ENABLE_EXAMPLES_TRUE@ libevent-client$(EXEEXT) \ -@ENABLE_EXAMPLES_TRUE@ libevent-server$(EXEEXT) \ -@ENABLE_EXAMPLES_TRUE@ deflate$(EXEEXT) $(am__EXEEXT_1) \ -@ENABLE_EXAMPLES_TRUE@ $(am__EXEEXT_2) -@ENABLE_EXAMPLES_TRUE@@ENABLE_TINY_NGHTTPD_TRUE@am__append_1 = tiny-nghttpd -@ENABLE_ASIO_LIB_TRUE@@ENABLE_EXAMPLES_TRUE@am__append_2 = asio-sv asio-sv2 asio-cl asio-cl2 -subdir = examples -DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \ - $(top_srcdir)/depcomp -ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 -am__aclocal_m4_deps = $(top_srcdir)/m4/ax_boost_asio.m4 \ - $(top_srcdir)/m4/ax_boost_base.m4 \ - $(top_srcdir)/m4/ax_boost_system.m4 \ - $(top_srcdir)/m4/ax_boost_thread.m4 \ - $(top_srcdir)/m4/ax_check_compile_flag.m4 \ - $(top_srcdir)/m4/ax_cxx_compile_stdcxx_11.m4 \ - $(top_srcdir)/m4/ax_have_epoll.m4 \ - $(top_srcdir)/m4/ax_python_devel.m4 \ - $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/libxml2.m4 \ - $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ - $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ - $(top_srcdir)/configure.ac -am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ - $(ACLOCAL_M4) -mkinstalldirs = $(install_sh) -d -CONFIG_HEADER = $(top_builddir)/config.h -CONFIG_CLEAN_FILES = -CONFIG_CLEAN_VPATH_FILES = -@ENABLE_EXAMPLES_TRUE@@ENABLE_TINY_NGHTTPD_TRUE@am__EXEEXT_1 = tiny-nghttpd$(EXEEXT) -@ENABLE_ASIO_LIB_TRUE@@ENABLE_EXAMPLES_TRUE@am__EXEEXT_2 = \ -@ENABLE_ASIO_LIB_TRUE@@ENABLE_EXAMPLES_TRUE@ asio-sv$(EXEEXT) \ -@ENABLE_ASIO_LIB_TRUE@@ENABLE_EXAMPLES_TRUE@ asio-sv2$(EXEEXT) \ -@ENABLE_ASIO_LIB_TRUE@@ENABLE_EXAMPLES_TRUE@ asio-cl$(EXEEXT) \ -@ENABLE_ASIO_LIB_TRUE@@ENABLE_EXAMPLES_TRUE@ asio-cl2$(EXEEXT) -PROGRAMS = $(noinst_PROGRAMS) -am__asio_cl_SOURCES_DIST = asio-cl.cc -@ENABLE_ASIO_LIB_TRUE@@ENABLE_EXAMPLES_TRUE@am_asio_cl_OBJECTS = asio_cl-asio-cl.$(OBJEXT) -asio_cl_OBJECTS = $(am_asio_cl_OBJECTS) -am__DEPENDENCIES_1 = -@ENABLE_ASIO_LIB_TRUE@@ENABLE_EXAMPLES_TRUE@am__DEPENDENCIES_2 = $(top_builddir)/lib/libnghttp2.la \ -@ENABLE_ASIO_LIB_TRUE@@ENABLE_EXAMPLES_TRUE@ $(top_builddir)/src/libnghttp2_asio.la \ -@ENABLE_ASIO_LIB_TRUE@@ENABLE_EXAMPLES_TRUE@ $(top_builddir)/third-party/libhttp-parser.la \ -@ENABLE_ASIO_LIB_TRUE@@ENABLE_EXAMPLES_TRUE@ $(am__DEPENDENCIES_1) \ -@ENABLE_ASIO_LIB_TRUE@@ENABLE_EXAMPLES_TRUE@ $(am__DEPENDENCIES_1) \ -@ENABLE_ASIO_LIB_TRUE@@ENABLE_EXAMPLES_TRUE@ $(am__DEPENDENCIES_1) \ -@ENABLE_ASIO_LIB_TRUE@@ENABLE_EXAMPLES_TRUE@ $(am__DEPENDENCIES_1) -@ENABLE_ASIO_LIB_TRUE@@ENABLE_EXAMPLES_TRUE@asio_cl_DEPENDENCIES = $(am__DEPENDENCIES_2) -AM_V_lt = $(am__v_lt_@AM_V@) -am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) -am__v_lt_0 = --silent -am__v_lt_1 = -am__asio_cl2_SOURCES_DIST = asio-cl2.cc -@ENABLE_ASIO_LIB_TRUE@@ENABLE_EXAMPLES_TRUE@am_asio_cl2_OBJECTS = asio_cl2-asio-cl2.$(OBJEXT) -asio_cl2_OBJECTS = $(am_asio_cl2_OBJECTS) -@ENABLE_ASIO_LIB_TRUE@@ENABLE_EXAMPLES_TRUE@asio_cl2_DEPENDENCIES = $(am__DEPENDENCIES_2) -am__asio_sv_SOURCES_DIST = asio-sv.cc -@ENABLE_ASIO_LIB_TRUE@@ENABLE_EXAMPLES_TRUE@am_asio_sv_OBJECTS = asio_sv-asio-sv.$(OBJEXT) -asio_sv_OBJECTS = $(am_asio_sv_OBJECTS) -@ENABLE_ASIO_LIB_TRUE@@ENABLE_EXAMPLES_TRUE@asio_sv_DEPENDENCIES = $(am__DEPENDENCIES_2) -am__asio_sv2_SOURCES_DIST = asio-sv2.cc -@ENABLE_ASIO_LIB_TRUE@@ENABLE_EXAMPLES_TRUE@am_asio_sv2_OBJECTS = asio_sv2-asio-sv2.$(OBJEXT) -asio_sv2_OBJECTS = $(am_asio_sv2_OBJECTS) -@ENABLE_ASIO_LIB_TRUE@@ENABLE_EXAMPLES_TRUE@asio_sv2_DEPENDENCIES = $(am__DEPENDENCIES_2) -am__client_SOURCES_DIST = client.c -@ENABLE_EXAMPLES_TRUE@am_client_OBJECTS = client.$(OBJEXT) -client_OBJECTS = $(am_client_OBJECTS) -client_LDADD = $(LDADD) -@ENABLE_EXAMPLES_TRUE@client_DEPENDENCIES = \ -@ENABLE_EXAMPLES_TRUE@ $(top_builddir)/lib/libnghttp2.la \ -@ENABLE_EXAMPLES_TRUE@ $(top_builddir)/third-party/libhttp-parser.la -am__deflate_SOURCES_DIST = deflate.c -@ENABLE_EXAMPLES_TRUE@am_deflate_OBJECTS = deflate.$(OBJEXT) -deflate_OBJECTS = $(am_deflate_OBJECTS) -deflate_LDADD = $(LDADD) -@ENABLE_EXAMPLES_TRUE@deflate_DEPENDENCIES = \ -@ENABLE_EXAMPLES_TRUE@ $(top_builddir)/lib/libnghttp2.la \ -@ENABLE_EXAMPLES_TRUE@ $(top_builddir)/third-party/libhttp-parser.la -am__libevent_client_SOURCES_DIST = libevent-client.c -@ENABLE_EXAMPLES_TRUE@am_libevent_client_OBJECTS = \ -@ENABLE_EXAMPLES_TRUE@ libevent-client.$(OBJEXT) -libevent_client_OBJECTS = $(am_libevent_client_OBJECTS) -libevent_client_LDADD = $(LDADD) -@ENABLE_EXAMPLES_TRUE@libevent_client_DEPENDENCIES = \ -@ENABLE_EXAMPLES_TRUE@ $(top_builddir)/lib/libnghttp2.la \ -@ENABLE_EXAMPLES_TRUE@ $(top_builddir)/third-party/libhttp-parser.la -am__libevent_server_SOURCES_DIST = libevent-server.c -@ENABLE_EXAMPLES_TRUE@am_libevent_server_OBJECTS = \ -@ENABLE_EXAMPLES_TRUE@ libevent-server.$(OBJEXT) -libevent_server_OBJECTS = $(am_libevent_server_OBJECTS) -libevent_server_LDADD = $(LDADD) -@ENABLE_EXAMPLES_TRUE@libevent_server_DEPENDENCIES = \ -@ENABLE_EXAMPLES_TRUE@ $(top_builddir)/lib/libnghttp2.la \ -@ENABLE_EXAMPLES_TRUE@ $(top_builddir)/third-party/libhttp-parser.la -am__tiny_nghttpd_SOURCES_DIST = tiny-nghttpd.c -@ENABLE_EXAMPLES_TRUE@@ENABLE_TINY_NGHTTPD_TRUE@am_tiny_nghttpd_OBJECTS = tiny-nghttpd.$(OBJEXT) -tiny_nghttpd_OBJECTS = $(am_tiny_nghttpd_OBJECTS) -tiny_nghttpd_LDADD = $(LDADD) -@ENABLE_EXAMPLES_TRUE@tiny_nghttpd_DEPENDENCIES = \ -@ENABLE_EXAMPLES_TRUE@ $(top_builddir)/lib/libnghttp2.la \ -@ENABLE_EXAMPLES_TRUE@ $(top_builddir)/third-party/libhttp-parser.la -AM_V_P = $(am__v_P_@AM_V@) -am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) -am__v_P_0 = false -am__v_P_1 = : -AM_V_GEN = $(am__v_GEN_@AM_V@) -am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) -am__v_GEN_0 = @echo " GEN " $@; -am__v_GEN_1 = -AM_V_at = $(am__v_at_@AM_V@) -am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) -am__v_at_0 = @ -am__v_at_1 = -DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) -depcomp = $(SHELL) $(top_srcdir)/depcomp -am__depfiles_maybe = depfiles -am__mv = mv -f -COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ - $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ - $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ - $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ - $(AM_CFLAGS) $(CFLAGS) -AM_V_CC = $(am__v_CC_@AM_V@) -am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) -am__v_CC_0 = @echo " CC " $@; -am__v_CC_1 = -CCLD = $(CC) -LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ - $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ - $(AM_LDFLAGS) $(LDFLAGS) -o $@ -AM_V_CCLD = $(am__v_CCLD_@AM_V@) -am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) -am__v_CCLD_0 = @echo " CCLD " $@; -am__v_CCLD_1 = -CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ - $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -LTCXXCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \ - $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) \ - $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ - $(AM_CXXFLAGS) $(CXXFLAGS) -AM_V_CXX = $(am__v_CXX_@AM_V@) -am__v_CXX_ = $(am__v_CXX_@AM_DEFAULT_V@) -am__v_CXX_0 = @echo " CXX " $@; -am__v_CXX_1 = -CXXLD = $(CXX) -CXXLINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \ - $(LIBTOOLFLAGS) --mode=link $(CXXLD) $(AM_CXXFLAGS) \ - $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ -AM_V_CXXLD = $(am__v_CXXLD_@AM_V@) -am__v_CXXLD_ = $(am__v_CXXLD_@AM_DEFAULT_V@) -am__v_CXXLD_0 = @echo " CXXLD " $@; -am__v_CXXLD_1 = -SOURCES = $(asio_cl_SOURCES) $(asio_cl2_SOURCES) $(asio_sv_SOURCES) \ - $(asio_sv2_SOURCES) $(client_SOURCES) $(deflate_SOURCES) \ - $(libevent_client_SOURCES) $(libevent_server_SOURCES) \ - $(tiny_nghttpd_SOURCES) -DIST_SOURCES = $(am__asio_cl_SOURCES_DIST) \ - $(am__asio_cl2_SOURCES_DIST) $(am__asio_sv_SOURCES_DIST) \ - $(am__asio_sv2_SOURCES_DIST) $(am__client_SOURCES_DIST) \ - $(am__deflate_SOURCES_DIST) \ - $(am__libevent_client_SOURCES_DIST) \ - $(am__libevent_server_SOURCES_DIST) \ - $(am__tiny_nghttpd_SOURCES_DIST) -am__can_run_installinfo = \ - case $$AM_UPDATE_INFO_DIR in \ - n|no|NO) false;; \ - *) (install-info --version) >/dev/null 2>&1;; \ - esac -am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) -# Read a list of newline-separated strings from the standard input, -# and print each of them once, without duplicates. Input order is -# *not* preserved. -am__uniquify_input = $(AWK) '\ - BEGIN { nonempty = 0; } \ - { items[$$0] = 1; nonempty = 1; } \ - END { if (nonempty) { for (i in items) print i; }; } \ -' -# Make sure the list of sources is unique. This is necessary because, -# e.g., the same source file might be shared among _SOURCES variables -# for different programs/libraries. -am__define_uniq_tagged_files = \ - list='$(am__tagged_files)'; \ - unique=`for i in $$list; do \ - if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ - done | $(am__uniquify_input)` -ETAGS = etags -CTAGS = ctags -DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) -ACLOCAL = @ACLOCAL@ -AMTAR = @AMTAR@ -AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ -APPLDFLAGS = @APPLDFLAGS@ -AR = @AR@ -AUTOCONF = @AUTOCONF@ -AUTOHEADER = @AUTOHEADER@ -AUTOMAKE = @AUTOMAKE@ -AWK = @AWK@ -BOOST_ASIO_LIB = @BOOST_ASIO_LIB@ -BOOST_CPPFLAGS = @BOOST_CPPFLAGS@ -BOOST_LDFLAGS = @BOOST_LDFLAGS@ -BOOST_SYSTEM_LIB = @BOOST_SYSTEM_LIB@ -BOOST_THREAD_LIB = @BOOST_THREAD_LIB@ -CC = @CC@ -CCDEPMODE = @CCDEPMODE@ -CFLAGS = @CFLAGS@ -CPP = @CPP@ -CPPFLAGS = @CPPFLAGS@ -CUNIT_CFLAGS = @CUNIT_CFLAGS@ -CUNIT_LIBS = @CUNIT_LIBS@ -CXX = @CXX@ -CXXCPP = @CXXCPP@ -CXXDEPMODE = @CXXDEPMODE@ -CXXFLAGS = @CXXFLAGS@ -CYGPATH_W = @CYGPATH_W@ -CYTHON = @CYTHON@ -DEFS = @DEFS@ -DEPDIR = @DEPDIR@ -DLLTOOL = @DLLTOOL@ -DSYMUTIL = @DSYMUTIL@ -DUMPBIN = @DUMPBIN@ -ECHO_C = @ECHO_C@ -ECHO_N = @ECHO_N@ -ECHO_T = @ECHO_T@ -EGREP = @EGREP@ -EXEEXT = @EXEEXT@ -FGREP = @FGREP@ -GREP = @GREP@ -HAVE_CXX11 = @HAVE_CXX11@ -INSTALL = @INSTALL@ -INSTALL_DATA = @INSTALL_DATA@ -INSTALL_PROGRAM = @INSTALL_PROGRAM@ -INSTALL_SCRIPT = @INSTALL_SCRIPT@ -INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ -JANSSON_CFLAGS = @JANSSON_CFLAGS@ -JANSSON_LIBS = @JANSSON_LIBS@ -JEMALLOC_LIBS = @JEMALLOC_LIBS@ -LD = @LD@ -LDFLAGS = @LDFLAGS@ -LIBEVENT_OPENSSL_CFLAGS = @LIBEVENT_OPENSSL_CFLAGS@ -LIBEVENT_OPENSSL_LIBS = @LIBEVENT_OPENSSL_LIBS@ -LIBEV_CFLAGS = @LIBEV_CFLAGS@ -LIBEV_LIBS = @LIBEV_LIBS@ -LIBOBJS = @LIBOBJS@ -LIBS = @LIBS@ -LIBSPDYLAY_CFLAGS = @LIBSPDYLAY_CFLAGS@ -LIBSPDYLAY_LIBS = @LIBSPDYLAY_LIBS@ -LIBTOOL = @LIBTOOL@ -LIPO = @LIPO@ -LN_S = @LN_S@ -LTLIBOBJS = @LTLIBOBJS@ -LT_AGE = @LT_AGE@ -LT_CURRENT = @LT_CURRENT@ -LT_REVISION = @LT_REVISION@ -MAKEINFO = @MAKEINFO@ -MANIFEST_TOOL = @MANIFEST_TOOL@ -MKDIR_P = @MKDIR_P@ -NM = @NM@ -NMEDIT = @NMEDIT@ -OBJDUMP = @OBJDUMP@ -OBJEXT = @OBJEXT@ -OPENSSL_CFLAGS = @OPENSSL_CFLAGS@ -OPENSSL_LIBS = @OPENSSL_LIBS@ -OTOOL = @OTOOL@ -OTOOL64 = @OTOOL64@ -PACKAGE = @PACKAGE@ -PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ -PACKAGE_NAME = @PACKAGE_NAME@ -PACKAGE_STRING = @PACKAGE_STRING@ -PACKAGE_TARNAME = @PACKAGE_TARNAME@ -PACKAGE_URL = @PACKAGE_URL@ -PACKAGE_VERSION = @PACKAGE_VERSION@ -PACKAGE_VERSION_NUM = @PACKAGE_VERSION_NUM@ -PATH_SEPARATOR = @PATH_SEPARATOR@ -PKG_CONFIG = @PKG_CONFIG@ -PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ -PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ -PYTHON = @PYTHON@ -PYTHON_CPPFLAGS = @PYTHON_CPPFLAGS@ -PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@ -PYTHON_EXTRA_LDFLAGS = @PYTHON_EXTRA_LDFLAGS@ -PYTHON_EXTRA_LIBS = @PYTHON_EXTRA_LIBS@ -PYTHON_LDFLAGS = @PYTHON_LDFLAGS@ -PYTHON_PLATFORM = @PYTHON_PLATFORM@ -PYTHON_PREFIX = @PYTHON_PREFIX@ -PYTHON_SITE_PKG = @PYTHON_SITE_PKG@ -PYTHON_VERSION = @PYTHON_VERSION@ -RANLIB = @RANLIB@ -SED = @SED@ -SET_MAKE = @SET_MAKE@ -SHELL = @SHELL@ -STRIP = @STRIP@ -TESTLDADD = @TESTLDADD@ -VERSION = @VERSION@ -WARNCFLAGS = @WARNCFLAGS@ -XML2_CONFIG = @XML2_CONFIG@ -XML_CPPFLAGS = @XML_CPPFLAGS@ -XML_LIBS = @XML_LIBS@ -ZLIB_CFLAGS = @ZLIB_CFLAGS@ -ZLIB_LIBS = @ZLIB_LIBS@ -abs_builddir = @abs_builddir@ -abs_srcdir = @abs_srcdir@ -abs_top_builddir = @abs_top_builddir@ -abs_top_srcdir = @abs_top_srcdir@ -ac_ct_AR = @ac_ct_AR@ -ac_ct_CC = @ac_ct_CC@ -ac_ct_CXX = @ac_ct_CXX@ -ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ -am__include = @am__include@ -am__leading_dot = @am__leading_dot@ -am__quote = @am__quote@ -am__tar = @am__tar@ -am__untar = @am__untar@ -bindir = @bindir@ -build = @build@ -build_alias = @build_alias@ -build_cpu = @build_cpu@ -build_os = @build_os@ -build_vendor = @build_vendor@ -builddir = @builddir@ -datadir = @datadir@ -datarootdir = @datarootdir@ -docdir = @docdir@ -dvidir = @dvidir@ -exec_prefix = @exec_prefix@ -host = @host@ -host_alias = @host_alias@ -host_cpu = @host_cpu@ -host_os = @host_os@ -host_vendor = @host_vendor@ -htmldir = @htmldir@ -includedir = @includedir@ -infodir = @infodir@ -install_sh = @install_sh@ -libdir = @libdir@ -libexecdir = @libexecdir@ -localedir = @localedir@ -localstatedir = @localstatedir@ -mandir = @mandir@ -mkdir_p = @mkdir_p@ -oldincludedir = @oldincludedir@ -pdfdir = @pdfdir@ -pkgpyexecdir = @pkgpyexecdir@ -pkgpythondir = @pkgpythondir@ -prefix = @prefix@ -program_transform_name = @program_transform_name@ -psdir = @psdir@ -pyexecdir = @pyexecdir@ -pythondir = @pythondir@ -sbindir = @sbindir@ -sharedstatedir = @sharedstatedir@ -srcdir = @srcdir@ -sysconfdir = @sysconfdir@ -target = @target@ -target_alias = @target_alias@ -target_cpu = @target_cpu@ -target_os = @target_os@ -target_vendor = @target_vendor@ -top_build_prefix = @top_build_prefix@ -top_builddir = @top_builddir@ -top_srcdir = @top_srcdir@ -@ENABLE_EXAMPLES_TRUE@AM_CFLAGS = $(WARNCFLAGS) -@ENABLE_EXAMPLES_TRUE@AM_CPPFLAGS = \ -@ENABLE_EXAMPLES_TRUE@ -Wall \ -@ENABLE_EXAMPLES_TRUE@ -I$(top_srcdir)/lib/includes \ -@ENABLE_EXAMPLES_TRUE@ -I$(top_builddir)/lib/includes \ -@ENABLE_EXAMPLES_TRUE@ -I$(top_srcdir)/src/includes \ -@ENABLE_EXAMPLES_TRUE@ -I$(top_srcdir)/third-party \ -@ENABLE_EXAMPLES_TRUE@ @LIBEVENT_OPENSSL_CFLAGS@ \ -@ENABLE_EXAMPLES_TRUE@ @OPENSSL_CFLAGS@ \ -@ENABLE_EXAMPLES_TRUE@ @DEFS@ - -@ENABLE_EXAMPLES_TRUE@LDADD = $(top_builddir)/lib/libnghttp2.la \ -@ENABLE_EXAMPLES_TRUE@ $(top_builddir)/third-party/libhttp-parser.la \ -@ENABLE_EXAMPLES_TRUE@ @LIBEVENT_OPENSSL_LIBS@ \ -@ENABLE_EXAMPLES_TRUE@ @OPENSSL_LIBS@ - -@ENABLE_EXAMPLES_TRUE@client_SOURCES = client.c -@ENABLE_EXAMPLES_TRUE@libevent_client_SOURCES = libevent-client.c -@ENABLE_EXAMPLES_TRUE@libevent_server_SOURCES = libevent-server.c -@ENABLE_EXAMPLES_TRUE@deflate_SOURCES = deflate.c -@ENABLE_EXAMPLES_TRUE@@ENABLE_TINY_NGHTTPD_TRUE@tiny_nghttpd_SOURCES = tiny-nghttpd.c - -# AM_CPPFLAGS must be placed first, so that header file (e.g., -# nghttp2/nghttp2.h) in this package is used rather than installed -# one. -@ENABLE_ASIO_LIB_TRUE@@ENABLE_EXAMPLES_TRUE@ASIOCPPFLAGS = ${AM_CPPFLAGS} ${BOOST_CPPFLAGS} -@ENABLE_ASIO_LIB_TRUE@@ENABLE_EXAMPLES_TRUE@ASIOLDADD = $(top_builddir)/lib/libnghttp2.la \ -@ENABLE_ASIO_LIB_TRUE@@ENABLE_EXAMPLES_TRUE@ $(top_builddir)/src/libnghttp2_asio.la @JEMALLOC_LIBS@ \ -@ENABLE_ASIO_LIB_TRUE@@ENABLE_EXAMPLES_TRUE@ $(top_builddir)/third-party/libhttp-parser.la \ -@ENABLE_ASIO_LIB_TRUE@@ENABLE_EXAMPLES_TRUE@ ${BOOST_LDFLAGS} \ -@ENABLE_ASIO_LIB_TRUE@@ENABLE_EXAMPLES_TRUE@ ${BOOST_ASIO_LIB} \ -@ENABLE_ASIO_LIB_TRUE@@ENABLE_EXAMPLES_TRUE@ ${BOOST_THREAD_LIB} \ -@ENABLE_ASIO_LIB_TRUE@@ENABLE_EXAMPLES_TRUE@ ${BOOST_SYSTEM_LIB} \ -@ENABLE_ASIO_LIB_TRUE@@ENABLE_EXAMPLES_TRUE@ @OPENSSL_LIBS@ \ -@ENABLE_ASIO_LIB_TRUE@@ENABLE_EXAMPLES_TRUE@ @APPLDFLAGS@ - -@ENABLE_ASIO_LIB_TRUE@@ENABLE_EXAMPLES_TRUE@asio_sv_SOURCES = asio-sv.cc -@ENABLE_ASIO_LIB_TRUE@@ENABLE_EXAMPLES_TRUE@asio_sv_CPPFLAGS = ${ASIOCPPFLAGS} -@ENABLE_ASIO_LIB_TRUE@@ENABLE_EXAMPLES_TRUE@asio_sv_LDADD = ${ASIOLDADD} -@ENABLE_ASIO_LIB_TRUE@@ENABLE_EXAMPLES_TRUE@asio_sv2_SOURCES = asio-sv2.cc -@ENABLE_ASIO_LIB_TRUE@@ENABLE_EXAMPLES_TRUE@asio_sv2_CPPFLAGS = ${ASIOCPPFLAGS} -@ENABLE_ASIO_LIB_TRUE@@ENABLE_EXAMPLES_TRUE@asio_sv2_LDADD = ${ASIOLDADD} -@ENABLE_ASIO_LIB_TRUE@@ENABLE_EXAMPLES_TRUE@asio_cl_SOURCES = asio-cl.cc -@ENABLE_ASIO_LIB_TRUE@@ENABLE_EXAMPLES_TRUE@asio_cl_CPPFLAGS = ${ASIOCPPFLAGS} -@ENABLE_ASIO_LIB_TRUE@@ENABLE_EXAMPLES_TRUE@asio_cl_LDADD = ${ASIOLDADD} -@ENABLE_ASIO_LIB_TRUE@@ENABLE_EXAMPLES_TRUE@asio_cl2_SOURCES = asio-cl2.cc -@ENABLE_ASIO_LIB_TRUE@@ENABLE_EXAMPLES_TRUE@asio_cl2_CPPFLAGS = ${ASIOCPPFLAGS} -@ENABLE_ASIO_LIB_TRUE@@ENABLE_EXAMPLES_TRUE@asio_cl2_LDADD = ${ASIOLDADD} -all: all-am - -.SUFFIXES: -.SUFFIXES: .c .cc .lo .o .obj -$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) - @for dep in $?; do \ - case '$(am__configure_deps)' in \ - *$$dep*) \ - ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ - && { if test -f $@; then exit 0; else break; fi; }; \ - exit 1;; \ - esac; \ - done; \ - echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu examples/Makefile'; \ - $(am__cd) $(top_srcdir) && \ - $(AUTOMAKE) --gnu examples/Makefile -.PRECIOUS: Makefile -Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status - @case '$?' in \ - *config.status*) \ - cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ - *) \ - echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ - cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ - esac; - -$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) - cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh - -$(top_srcdir)/configure: $(am__configure_deps) - cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh -$(ACLOCAL_M4): $(am__aclocal_m4_deps) - cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh -$(am__aclocal_m4_deps): - -clean-noinstPROGRAMS: - @list='$(noinst_PROGRAMS)'; test -n "$$list" || exit 0; \ - echo " rm -f" $$list; \ - rm -f $$list || exit $$?; \ - test -n "$(EXEEXT)" || exit 0; \ - list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ - echo " rm -f" $$list; \ - rm -f $$list - -asio-cl$(EXEEXT): $(asio_cl_OBJECTS) $(asio_cl_DEPENDENCIES) $(EXTRA_asio_cl_DEPENDENCIES) - @rm -f asio-cl$(EXEEXT) - $(AM_V_CXXLD)$(CXXLINK) $(asio_cl_OBJECTS) $(asio_cl_LDADD) $(LIBS) - -asio-cl2$(EXEEXT): $(asio_cl2_OBJECTS) $(asio_cl2_DEPENDENCIES) $(EXTRA_asio_cl2_DEPENDENCIES) - @rm -f asio-cl2$(EXEEXT) - $(AM_V_CXXLD)$(CXXLINK) $(asio_cl2_OBJECTS) $(asio_cl2_LDADD) $(LIBS) - -asio-sv$(EXEEXT): $(asio_sv_OBJECTS) $(asio_sv_DEPENDENCIES) $(EXTRA_asio_sv_DEPENDENCIES) - @rm -f asio-sv$(EXEEXT) - $(AM_V_CXXLD)$(CXXLINK) $(asio_sv_OBJECTS) $(asio_sv_LDADD) $(LIBS) - -asio-sv2$(EXEEXT): $(asio_sv2_OBJECTS) $(asio_sv2_DEPENDENCIES) $(EXTRA_asio_sv2_DEPENDENCIES) - @rm -f asio-sv2$(EXEEXT) - $(AM_V_CXXLD)$(CXXLINK) $(asio_sv2_OBJECTS) $(asio_sv2_LDADD) $(LIBS) - -client$(EXEEXT): $(client_OBJECTS) $(client_DEPENDENCIES) $(EXTRA_client_DEPENDENCIES) - @rm -f client$(EXEEXT) - $(AM_V_CCLD)$(LINK) $(client_OBJECTS) $(client_LDADD) $(LIBS) - -deflate$(EXEEXT): $(deflate_OBJECTS) $(deflate_DEPENDENCIES) $(EXTRA_deflate_DEPENDENCIES) - @rm -f deflate$(EXEEXT) - $(AM_V_CCLD)$(LINK) $(deflate_OBJECTS) $(deflate_LDADD) $(LIBS) - -libevent-client$(EXEEXT): $(libevent_client_OBJECTS) $(libevent_client_DEPENDENCIES) $(EXTRA_libevent_client_DEPENDENCIES) - @rm -f libevent-client$(EXEEXT) - $(AM_V_CCLD)$(LINK) $(libevent_client_OBJECTS) $(libevent_client_LDADD) $(LIBS) - -libevent-server$(EXEEXT): $(libevent_server_OBJECTS) $(libevent_server_DEPENDENCIES) $(EXTRA_libevent_server_DEPENDENCIES) - @rm -f libevent-server$(EXEEXT) - $(AM_V_CCLD)$(LINK) $(libevent_server_OBJECTS) $(libevent_server_LDADD) $(LIBS) - -tiny-nghttpd$(EXEEXT): $(tiny_nghttpd_OBJECTS) $(tiny_nghttpd_DEPENDENCIES) $(EXTRA_tiny_nghttpd_DEPENDENCIES) - @rm -f tiny-nghttpd$(EXEEXT) - $(AM_V_CCLD)$(LINK) $(tiny_nghttpd_OBJECTS) $(tiny_nghttpd_LDADD) $(LIBS) - -mostlyclean-compile: - -rm -f *.$(OBJEXT) - -distclean-compile: - -rm -f *.tab.c - -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asio_cl-asio-cl.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asio_cl2-asio-cl2.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asio_sv-asio-sv.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asio_sv2-asio-sv2.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/client.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/deflate.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libevent-client.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libevent-server.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tiny-nghttpd.Po@am__quote@ - -.c.o: -@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\ -@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ -@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< - -.c.obj: -@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\ -@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\ -@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` - -.c.lo: -@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\ -@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ -@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< - -.cc.o: -@am__fastdepCXX_TRUE@ $(AM_V_CXX)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\ -@am__fastdepCXX_TRUE@ $(CXXCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ -@am__fastdepCXX_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po -@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ $< - -.cc.obj: -@am__fastdepCXX_TRUE@ $(AM_V_CXX)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\ -@am__fastdepCXX_TRUE@ $(CXXCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\ -@am__fastdepCXX_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po -@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'` - -.cc.lo: -@am__fastdepCXX_TRUE@ $(AM_V_CXX)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\ -@am__fastdepCXX_TRUE@ $(LTCXXCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ -@am__fastdepCXX_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Plo -@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LTCXXCOMPILE) -c -o $@ $< - -asio_cl-asio-cl.o: asio-cl.cc -@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(asio_cl_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT asio_cl-asio-cl.o -MD -MP -MF $(DEPDIR)/asio_cl-asio-cl.Tpo -c -o asio_cl-asio-cl.o `test -f 'asio-cl.cc' || echo '$(srcdir)/'`asio-cl.cc -@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/asio_cl-asio-cl.Tpo $(DEPDIR)/asio_cl-asio-cl.Po -@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='asio-cl.cc' object='asio_cl-asio-cl.o' libtool=no @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(asio_cl_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o asio_cl-asio-cl.o `test -f 'asio-cl.cc' || echo '$(srcdir)/'`asio-cl.cc - -asio_cl-asio-cl.obj: asio-cl.cc -@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(asio_cl_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT asio_cl-asio-cl.obj -MD -MP -MF $(DEPDIR)/asio_cl-asio-cl.Tpo -c -o asio_cl-asio-cl.obj `if test -f 'asio-cl.cc'; then $(CYGPATH_W) 'asio-cl.cc'; else $(CYGPATH_W) '$(srcdir)/asio-cl.cc'; fi` -@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/asio_cl-asio-cl.Tpo $(DEPDIR)/asio_cl-asio-cl.Po -@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='asio-cl.cc' object='asio_cl-asio-cl.obj' libtool=no @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(asio_cl_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o asio_cl-asio-cl.obj `if test -f 'asio-cl.cc'; then $(CYGPATH_W) 'asio-cl.cc'; else $(CYGPATH_W) '$(srcdir)/asio-cl.cc'; fi` - -asio_cl2-asio-cl2.o: asio-cl2.cc -@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(asio_cl2_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT asio_cl2-asio-cl2.o -MD -MP -MF $(DEPDIR)/asio_cl2-asio-cl2.Tpo -c -o asio_cl2-asio-cl2.o `test -f 'asio-cl2.cc' || echo '$(srcdir)/'`asio-cl2.cc -@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/asio_cl2-asio-cl2.Tpo $(DEPDIR)/asio_cl2-asio-cl2.Po -@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='asio-cl2.cc' object='asio_cl2-asio-cl2.o' libtool=no @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(asio_cl2_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o asio_cl2-asio-cl2.o `test -f 'asio-cl2.cc' || echo '$(srcdir)/'`asio-cl2.cc - -asio_cl2-asio-cl2.obj: asio-cl2.cc -@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(asio_cl2_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT asio_cl2-asio-cl2.obj -MD -MP -MF $(DEPDIR)/asio_cl2-asio-cl2.Tpo -c -o asio_cl2-asio-cl2.obj `if test -f 'asio-cl2.cc'; then $(CYGPATH_W) 'asio-cl2.cc'; else $(CYGPATH_W) '$(srcdir)/asio-cl2.cc'; fi` -@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/asio_cl2-asio-cl2.Tpo $(DEPDIR)/asio_cl2-asio-cl2.Po -@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='asio-cl2.cc' object='asio_cl2-asio-cl2.obj' libtool=no @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(asio_cl2_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o asio_cl2-asio-cl2.obj `if test -f 'asio-cl2.cc'; then $(CYGPATH_W) 'asio-cl2.cc'; else $(CYGPATH_W) '$(srcdir)/asio-cl2.cc'; fi` - -asio_sv-asio-sv.o: asio-sv.cc -@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(asio_sv_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT asio_sv-asio-sv.o -MD -MP -MF $(DEPDIR)/asio_sv-asio-sv.Tpo -c -o asio_sv-asio-sv.o `test -f 'asio-sv.cc' || echo '$(srcdir)/'`asio-sv.cc -@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/asio_sv-asio-sv.Tpo $(DEPDIR)/asio_sv-asio-sv.Po -@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='asio-sv.cc' object='asio_sv-asio-sv.o' libtool=no @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(asio_sv_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o asio_sv-asio-sv.o `test -f 'asio-sv.cc' || echo '$(srcdir)/'`asio-sv.cc - -asio_sv-asio-sv.obj: asio-sv.cc -@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(asio_sv_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT asio_sv-asio-sv.obj -MD -MP -MF $(DEPDIR)/asio_sv-asio-sv.Tpo -c -o asio_sv-asio-sv.obj `if test -f 'asio-sv.cc'; then $(CYGPATH_W) 'asio-sv.cc'; else $(CYGPATH_W) '$(srcdir)/asio-sv.cc'; fi` -@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/asio_sv-asio-sv.Tpo $(DEPDIR)/asio_sv-asio-sv.Po -@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='asio-sv.cc' object='asio_sv-asio-sv.obj' libtool=no @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(asio_sv_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o asio_sv-asio-sv.obj `if test -f 'asio-sv.cc'; then $(CYGPATH_W) 'asio-sv.cc'; else $(CYGPATH_W) '$(srcdir)/asio-sv.cc'; fi` - -asio_sv2-asio-sv2.o: asio-sv2.cc -@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(asio_sv2_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT asio_sv2-asio-sv2.o -MD -MP -MF $(DEPDIR)/asio_sv2-asio-sv2.Tpo -c -o asio_sv2-asio-sv2.o `test -f 'asio-sv2.cc' || echo '$(srcdir)/'`asio-sv2.cc -@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/asio_sv2-asio-sv2.Tpo $(DEPDIR)/asio_sv2-asio-sv2.Po -@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='asio-sv2.cc' object='asio_sv2-asio-sv2.o' libtool=no @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(asio_sv2_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o asio_sv2-asio-sv2.o `test -f 'asio-sv2.cc' || echo '$(srcdir)/'`asio-sv2.cc - -asio_sv2-asio-sv2.obj: asio-sv2.cc -@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(asio_sv2_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT asio_sv2-asio-sv2.obj -MD -MP -MF $(DEPDIR)/asio_sv2-asio-sv2.Tpo -c -o asio_sv2-asio-sv2.obj `if test -f 'asio-sv2.cc'; then $(CYGPATH_W) 'asio-sv2.cc'; else $(CYGPATH_W) '$(srcdir)/asio-sv2.cc'; fi` -@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/asio_sv2-asio-sv2.Tpo $(DEPDIR)/asio_sv2-asio-sv2.Po -@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='asio-sv2.cc' object='asio_sv2-asio-sv2.obj' libtool=no @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(asio_sv2_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o asio_sv2-asio-sv2.obj `if test -f 'asio-sv2.cc'; then $(CYGPATH_W) 'asio-sv2.cc'; else $(CYGPATH_W) '$(srcdir)/asio-sv2.cc'; fi` - -mostlyclean-libtool: - -rm -f *.lo - -clean-libtool: - -rm -rf .libs _libs - -ID: $(am__tagged_files) - $(am__define_uniq_tagged_files); mkid -fID $$unique -tags: tags-am -TAGS: tags - -tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) - set x; \ - here=`pwd`; \ - $(am__define_uniq_tagged_files); \ - shift; \ - if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ - test -n "$$unique" || unique=$$empty_fix; \ - if test $$# -gt 0; then \ - $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ - "$$@" $$unique; \ - else \ - $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ - $$unique; \ - fi; \ - fi -ctags: ctags-am - -CTAGS: ctags -ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) - $(am__define_uniq_tagged_files); \ - test -z "$(CTAGS_ARGS)$$unique" \ - || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ - $$unique - -GTAGS: - here=`$(am__cd) $(top_builddir) && pwd` \ - && $(am__cd) $(top_srcdir) \ - && gtags -i $(GTAGS_ARGS) "$$here" -cscopelist: cscopelist-am - -cscopelist-am: $(am__tagged_files) - list='$(am__tagged_files)'; \ - case "$(srcdir)" in \ - [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ - *) sdir=$(subdir)/$(srcdir) ;; \ - esac; \ - for i in $$list; do \ - if test -f "$$i"; then \ - echo "$(subdir)/$$i"; \ - else \ - echo "$$sdir/$$i"; \ - fi; \ - done >> $(top_builddir)/cscope.files - -distclean-tags: - -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags - -distdir: $(DISTFILES) - @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ - topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ - list='$(DISTFILES)'; \ - dist_files=`for file in $$list; do echo $$file; done | \ - sed -e "s|^$$srcdirstrip/||;t" \ - -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ - case $$dist_files in \ - */*) $(MKDIR_P) `echo "$$dist_files" | \ - sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ - sort -u` ;; \ - esac; \ - for file in $$dist_files; do \ - if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ - if test -d $$d/$$file; then \ - dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ - if test -d "$(distdir)/$$file"; then \ - find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ - fi; \ - if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ - cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ - find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ - fi; \ - cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ - else \ - test -f "$(distdir)/$$file" \ - || cp -p $$d/$$file "$(distdir)/$$file" \ - || exit 1; \ - fi; \ - done -check-am: all-am -check: check-am -all-am: Makefile $(PROGRAMS) -installdirs: -install: install-am -install-exec: install-exec-am -install-data: install-data-am -uninstall: uninstall-am - -install-am: all-am - @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am - -installcheck: installcheck-am -install-strip: - if test -z '$(STRIP)'; then \ - $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ - install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ - install; \ - else \ - $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ - install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ - "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ - fi -mostlyclean-generic: - -clean-generic: - -distclean-generic: - -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) - -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) - -maintainer-clean-generic: - @echo "This command is intended for maintainers to use" - @echo "it deletes files that may require special tools to rebuild." -clean: clean-am - -clean-am: clean-generic clean-libtool clean-noinstPROGRAMS \ - mostlyclean-am - -distclean: distclean-am - -rm -rf ./$(DEPDIR) - -rm -f Makefile -distclean-am: clean-am distclean-compile distclean-generic \ - distclean-tags - -dvi: dvi-am - -dvi-am: - -html: html-am - -html-am: - -info: info-am - -info-am: - -install-data-am: - -install-dvi: install-dvi-am - -install-dvi-am: - -install-exec-am: - -install-html: install-html-am - -install-html-am: - -install-info: install-info-am - -install-info-am: - -install-man: - -install-pdf: install-pdf-am - -install-pdf-am: - -install-ps: install-ps-am - -install-ps-am: - -installcheck-am: - -maintainer-clean: maintainer-clean-am - -rm -rf ./$(DEPDIR) - -rm -f Makefile -maintainer-clean-am: distclean-am maintainer-clean-generic - -mostlyclean: mostlyclean-am - -mostlyclean-am: mostlyclean-compile mostlyclean-generic \ - mostlyclean-libtool - -pdf: pdf-am - -pdf-am: - -ps: ps-am - -ps-am: - -uninstall-am: - -.MAKE: install-am install-strip - -.PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ - clean-libtool clean-noinstPROGRAMS cscopelist-am ctags \ - ctags-am distclean distclean-compile distclean-generic \ - distclean-libtool distclean-tags distdir dvi dvi-am html \ - html-am info info-am install install-am install-data \ - install-data-am install-dvi install-dvi-am install-exec \ - install-exec-am install-html install-html-am install-info \ - install-info-am install-man install-pdf install-pdf-am \ - install-ps install-ps-am install-strip installcheck \ - installcheck-am installdirs maintainer-clean \ - maintainer-clean-generic mostlyclean mostlyclean-compile \ - mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ - tags tags-am uninstall uninstall-am - - -# Tell versions [3.59,3.63) of GNU make to not export all variables. -# Otherwise a system limit (for SysV at least) may be exceeded. -.NOEXPORT: diff --git a/examples/asio-cl.cc b/examples/asio-cl.cc deleted file mode 100644 index c7d7bb1..0000000 --- a/examples/asio-cl.cc +++ /dev/null @@ -1,96 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2015 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -#include - -#include - -using boost::asio::ip::tcp; - -using namespace nghttp2::asio_http2; -using namespace nghttp2::asio_http2::client; - -int main(int argc, char *argv[]) { - try { - if (argc < 2) { - std::cerr << "Usage: asio-cl URI" << std::endl; - return 1; - } - boost::system::error_code ec; - boost::asio::io_service io_service; - - std::string uri = argv[1]; - std::string scheme, host, service; - - if (host_service_from_uri(ec, scheme, host, service, uri)) { - std::cerr << "error: bad URI: " << ec.message() << std::endl; - return 1; - } - - boost::asio::ssl::context tls_ctx(boost::asio::ssl::context::sslv23); - tls_ctx.set_default_verify_paths(); - // disabled to make development easier... - // tls_ctx.set_verify_mode(boost::asio::ssl::verify_peer); - configure_tls_context(ec, tls_ctx); - - auto sess = scheme == "https" ? session(io_service, tls_ctx, host, service) - : session(io_service, host, service); - - sess.on_connect([&sess, &uri](tcp::resolver::iterator endpoint_it) { - boost::system::error_code ec; - auto req = sess.submit(ec, "GET", uri); - - if (ec) { - std::cerr << "error: " << ec.message() << std::endl; - return; - } - - req->on_response([&sess](const response &res) { - std::cerr << "HTTP/2 " << res.status_code() << std::endl; - for (auto &kv : res.header()) { - std::cerr << kv.first << ": " << kv.second.value << "\n"; - } - std::cerr << std::endl; - - res.on_data([&sess](const uint8_t *data, std::size_t len) { - std::cerr.write(reinterpret_cast(data), len); - std::cerr << std::endl; - }); - }); - - req->on_close([&sess](uint32_t error_code) { sess.shutdown(); }); - }); - - sess.on_error([](const boost::system::error_code &ec) { - std::cerr << "error: " << ec.message() << std::endl; - }); - - io_service.run(); - } catch (std::exception &e) { - std::cerr << "exception: " << e.what() << "\n"; - } - - return 0; -} diff --git a/examples/asio-cl2.cc b/examples/asio-cl2.cc deleted file mode 100644 index 2a8ed71..0000000 --- a/examples/asio-cl2.cc +++ /dev/null @@ -1,134 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2015 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -#include -#include - -#include - -using boost::asio::ip::tcp; - -using namespace nghttp2::asio_http2; -using namespace nghttp2::asio_http2::client; - -void print_header(const header_map &h) { - for (auto &kv : h) { - std::cerr << kv.first << ": " << kv.second.value << "\n"; - } - std::cerr << std::endl; -} - -void print_header(const response &res) { - std::cerr << "HTTP/2 " << res.status_code() << "\n"; - print_header(res.header()); -} - -void print_header(const request &req) { - auto &uri = req.uri(); - std::cerr << req.method() << " " << uri.scheme << "://" << uri.host - << uri.path; - if (!uri.raw_query.empty()) { - std::cerr << "?" << uri.raw_query; - } - std::cerr << " HTTP/2\n"; - print_header(req.header()); -} - -int main(int argc, char *argv[]) { - try { - if (argc < 2) { - std::cerr << "Usage: asio-cl URI" << std::endl; - return 1; - } - boost::system::error_code ec; - boost::asio::io_service io_service; - - std::string uri = argv[1]; - std::string scheme, host, service; - - if (host_service_from_uri(ec, scheme, host, service, uri)) { - std::cerr << "error: bad URI: " << ec.message() << std::endl; - return 1; - } - - boost::asio::ssl::context tls_ctx(boost::asio::ssl::context::sslv23); - tls_ctx.set_default_verify_paths(); - // disabled to make development easier... - // tls_ctx.set_verify_mode(boost::asio::ssl::verify_peer); - configure_tls_context(ec, tls_ctx); - - auto sess = scheme == "https" ? session(io_service, tls_ctx, host, service) - : session(io_service, host, service); - - sess.on_connect([&sess, &uri](tcp::resolver::iterator endpoint_it) { - std::cerr << "connected to " << (*endpoint_it).endpoint() << std::endl; - boost::system::error_code ec; - auto req = sess.submit(ec, "GET", uri, {{"cookie", {"foo=bar", true}}}); - if (ec) { - std::cerr << "error: " << ec.message() << std::endl; - return; - } - - req->on_response([&sess, req](const response &res) { - std::cerr << "response header was received" << std::endl; - print_header(res); - - res.on_data([&sess](const uint8_t *data, std::size_t len) { - std::cerr.write(reinterpret_cast(data), len); - std::cerr << std::endl; - }); - }); - - req->on_close([&sess](uint32_t error_code) { - std::cerr << "request done with error_code=" << error_code << std::endl; - }); - - req->on_push([](const request &push_req) { - std::cerr << "push request was received" << std::endl; - - print_header(push_req); - - push_req.on_response([](const response &res) { - std::cerr << "push response header was received" << std::endl; - - res.on_data([](const uint8_t *data, std::size_t len) { - std::cerr.write(reinterpret_cast(data), len); - std::cerr << std::endl; - }); - }); - }); - }); - - sess.on_error([](const boost::system::error_code &ec) { - std::cerr << "error: " << ec.message() << std::endl; - }); - - io_service.run(); - } catch (std::exception &e) { - std::cerr << "exception: " << e.what() << "\n"; - } - - return 0; -} diff --git a/examples/asio-sv.cc b/examples/asio-sv.cc deleted file mode 100644 index 47a1d84..0000000 --- a/examples/asio-sv.cc +++ /dev/null @@ -1,149 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2014 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -// We wrote this code based on the original code which has the -// following license: -// -// main.cpp -// ~~~~~~~~ -// -// Copyright (c) 2003-2013 Christopher M. Kohlhoff (chris at kohlhoff dot com) -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// - -#include -#include - -#include - -using namespace nghttp2::asio_http2; -using namespace nghttp2::asio_http2::server; - -int main(int argc, char *argv[]) { - try { - // Check command line arguments. - if (argc < 4) { - std::cerr - << "Usage: asio-sv
      [ " - << "]\n"; - return 1; - } - - boost::system::error_code ec; - - std::string addr = argv[1]; - std::string port = argv[2]; - std::size_t num_threads = std::stoi(argv[3]); - - http2 server; - - server.num_threads(num_threads); - - server.handle("/", [](const request &req, const response &res) { - res.write_head(200, {{"foo", {"bar"}}}); - res.end("hello, world\n"); - }); - server.handle("/secret/", [](const request &req, const response &res) { - res.write_head(200); - res.end("under construction!\n"); - }); - server.handle("/push", [](const request &req, const response &res) { - boost::system::error_code ec; - auto push = res.push(ec, "GET", "/push/1"); - if (!ec) { - push->write_head(200); - push->end("server push FTW!\n"); - } - - res.write_head(200); - res.end("you'll receive server push!\n"); - }); - server.handle("/delay", [](const request &req, const response &res) { - res.write_head(200); - - auto timer = std::make_shared( - res.io_service(), boost::posix_time::seconds(3)); - auto closed = std::make_shared(); - - res.on_close([timer, closed](uint32_t error_code) { - timer->cancel(); - *closed = true; - }); - - timer->async_wait([&res, closed](const boost::system::error_code &ec) { - if (ec || *closed) { - return; - } - - res.end("finally!\n"); - }); - }); - server.handle("/trailer", [](const request &req, const response &res) { - // send trailer part. - res.write_head(200, {{"trailers", {"digest"}}}); - - std::string body = "nghttp2 FTW!\n"; - auto left = std::make_shared(body.size()); - - res.end([&res, body, left](uint8_t *dst, std::size_t len, - uint32_t *data_flags) { - auto n = std::min(len, *left); - std::copy_n(body.c_str() + (body.size() - *left), n, dst); - *left -= n; - if (*left == 0) { - *data_flags |= - NGHTTP2_DATA_FLAG_EOF | NGHTTP2_DATA_FLAG_NO_END_STREAM; - // RFC 3230 Instance Digests in HTTP. The digest value is - // SHA-256 message digest of body. - res.write_trailer( - {{"digest", - {"SHA-256=qqXqskW7F3ueBSvmZRCiSwl2ym4HRO0M/pvQCBlSDis="}}}); - } - return n; - }); - }); - - if (argc >= 6) { - boost::asio::ssl::context tls(boost::asio::ssl::context::sslv23); - tls.use_private_key_file(argv[4], boost::asio::ssl::context::pem); - tls.use_certificate_chain_file(argv[5]); - - configure_tls_context_easy(ec, tls); - - if (server.listen_and_serve(ec, tls, addr, port)) { - std::cerr << "error: " << ec.message() << std::endl; - } - } else { - if (server.listen_and_serve(ec, addr, port)) { - std::cerr << "error: " << ec.message() << std::endl; - } - } - } catch (std::exception &e) { - std::cerr << "exception: " << e.what() << "\n"; - } - - return 0; -} diff --git a/examples/asio-sv2.cc b/examples/asio-sv2.cc deleted file mode 100644 index 8d2580e..0000000 --- a/examples/asio-sv2.cc +++ /dev/null @@ -1,125 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2014 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -// We wrote this code based on the original code which has the -// following license: -// -// main.cpp -// ~~~~~~~~ -// -// Copyright (c) 2003-2013 Christopher M. Kohlhoff (chris at kohlhoff dot com) -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// -#include -#include -#ifdef HAVE_UNISTD_H -#include -#endif // HAVE_UNISTD_H -#ifdef HAVE_FCNTL_H -#include -#endif // HAVE_FCNTL_H -#include -#include - -#include - -using namespace nghttp2::asio_http2; -using namespace nghttp2::asio_http2::server; - -int main(int argc, char *argv[]) { - try { - // Check command line arguments. - if (argc < 5) { - std::cerr << "Usage: asio-sv2
      " - << "[ ]\n"; - return 1; - } - - boost::system::error_code ec; - - std::string addr = argv[1]; - std::string port = argv[2]; - std::size_t num_threads = std::stoi(argv[3]); - std::string docroot = argv[4]; - - http2 server; - - server.num_threads(num_threads); - - server.handle("/", [&docroot](const request &req, const response &res) { - auto path = percent_decode(req.uri().path); - if (!check_path(path)) { - res.write_head(404); - res.end(); - return; - } - - if (path == "/") { - path = "/index.html"; - } - - path = docroot + path; - auto fd = open(path.c_str(), O_RDONLY); - if (fd == -1) { - res.write_head(404); - res.end(); - return; - } - - auto header = header_map(); - - struct stat stbuf; - if (stat(path.c_str(), &stbuf) == 0) { - header.emplace("content-length", - header_value{std::to_string(stbuf.st_size)}); - header.emplace("last-modified", - header_value{http_date(stbuf.st_mtime)}); - } - res.write_head(200, std::move(header)); - res.end(file_generator_from_fd(fd)); - }); - - if (argc >= 7) { - boost::asio::ssl::context tls(boost::asio::ssl::context::sslv23); - tls.use_private_key_file(argv[5], boost::asio::ssl::context::pem); - tls.use_certificate_chain_file(argv[6]); - - configure_tls_context_easy(ec, tls); - - if (server.listen_and_serve(ec, tls, addr, port)) { - std::cerr << "error: " << ec.message() << std::endl; - } - } else { - if (server.listen_and_serve(ec, addr, port)) { - std::cerr << "error: " << ec.message() << std::endl; - } - } - } catch (std::exception &e) { - std::cerr << "exception: " << e.what() << "\n"; - } - - return 0; -} diff --git a/examples/client.c b/examples/client.c deleted file mode 100644 index 4b4deee..0000000 --- a/examples/client.c +++ /dev/null @@ -1,703 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2013 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -/* - * This program is written to show how to use nghttp2 API in C and - * intentionally made simple. - */ -#ifdef HAVE_CONFIG_H -#include -#endif /* HAVE_CONFIG_H */ - -#include -#include -#ifdef HAVE_UNISTD_H -#include -#endif /* HAVE_UNISTD_H */ -#ifdef HAVE_FCNTL_H -#include -#endif /* HAVE_FCNTL_H */ -#include -#ifdef HAVE_SYS_SOCKET_H -#include -#endif /* HAVE_SYS_SOCKET_H */ -#ifdef HAVE_NETDB_H -#include -#endif /* HAVE_NETDB_H */ -#ifdef HAVE_NETINET_IN_H -#include -#endif /* HAVE_NETINET_IN_H */ -#include -#include -#include -#include -#include - -#include - -#include -#include -#include - -enum { IO_NONE, WANT_READ, WANT_WRITE }; - -#define MAKE_NV(NAME, VALUE) \ - { \ - (uint8_t *) NAME, (uint8_t *)VALUE, sizeof(NAME) - 1, sizeof(VALUE) - 1, \ - NGHTTP2_NV_FLAG_NONE \ - } - -#define MAKE_NV_CS(NAME, VALUE) \ - { \ - (uint8_t *) NAME, (uint8_t *)VALUE, sizeof(NAME) - 1, strlen(VALUE), \ - NGHTTP2_NV_FLAG_NONE \ - } - -struct Connection { - SSL *ssl; - nghttp2_session *session; - /* WANT_READ if SSL/TLS connection needs more input; or WANT_WRITE - if it needs more output; or IO_NONE. This is necessary because - SSL/TLS re-negotiation is possible at any time. nghttp2 API - offers similar functions like nghttp2_session_want_read() and - nghttp2_session_want_write() but they do not take into account - SSL/TSL connection. */ - int want_io; -}; - -struct Request { - char *host; - /* In this program, path contains query component as well. */ - char *path; - /* This is the concatenation of host and port with ":" in - between. */ - char *hostport; - /* Stream ID for this request. */ - int32_t stream_id; - uint16_t port; -}; - -struct URI { - const char *host; - /* In this program, path contains query component as well. */ - const char *path; - size_t pathlen; - const char *hostport; - size_t hostlen; - size_t hostportlen; - uint16_t port; -}; - -/* - * Returns copy of string |s| with the length |len|. The returned - * string is NULL-terminated. - */ -static char *strcopy(const char *s, size_t len) { - char *dst; - dst = malloc(len + 1); - memcpy(dst, s, len); - dst[len] = '\0'; - return dst; -} - -/* - * Prints error message |msg| and exit. - */ -static void die(const char *msg) { - fprintf(stderr, "FATAL: %s\n", msg); - exit(EXIT_FAILURE); -} - -/* - * Prints error containing the function name |func| and message |msg| - * and exit. - */ -static void dief(const char *func, const char *msg) { - fprintf(stderr, "FATAL: %s: %s\n", func, msg); - exit(EXIT_FAILURE); -} - -/* - * Prints error containing the function name |func| and error code - * |error_code| and exit. - */ -static void diec(const char *func, int error_code) { - fprintf(stderr, "FATAL: %s: error_code=%d, msg=%s\n", func, error_code, - nghttp2_strerror(error_code)); - exit(EXIT_FAILURE); -} - -/* - * The implementation of nghttp2_send_callback type. Here we write - * |data| with size |length| to the network and return the number of - * bytes actually written. See the documentation of - * nghttp2_send_callback for the details. - */ -static ssize_t send_callback(nghttp2_session *session _U_, const uint8_t *data, - size_t length, int flags _U_, void *user_data) { - struct Connection *connection; - int rv; - connection = (struct Connection *)user_data; - connection->want_io = IO_NONE; - ERR_clear_error(); - rv = SSL_write(connection->ssl, data, (int)length); - if (rv <= 0) { - int err = SSL_get_error(connection->ssl, rv); - if (err == SSL_ERROR_WANT_WRITE || err == SSL_ERROR_WANT_READ) { - connection->want_io = - (err == SSL_ERROR_WANT_READ ? WANT_READ : WANT_WRITE); - rv = NGHTTP2_ERR_WOULDBLOCK; - } else { - rv = NGHTTP2_ERR_CALLBACK_FAILURE; - } - } - return rv; -} - -/* - * The implementation of nghttp2_recv_callback type. Here we read data - * from the network and write them in |buf|. The capacity of |buf| is - * |length| bytes. Returns the number of bytes stored in |buf|. See - * the documentation of nghttp2_recv_callback for the details. - */ -static ssize_t recv_callback(nghttp2_session *session _U_, uint8_t *buf, - size_t length, int flags _U_, void *user_data) { - struct Connection *connection; - int rv; - connection = (struct Connection *)user_data; - connection->want_io = IO_NONE; - ERR_clear_error(); - rv = SSL_read(connection->ssl, buf, (int)length); - if (rv < 0) { - int err = SSL_get_error(connection->ssl, rv); - if (err == SSL_ERROR_WANT_WRITE || err == SSL_ERROR_WANT_READ) { - connection->want_io = - (err == SSL_ERROR_WANT_READ ? WANT_READ : WANT_WRITE); - rv = NGHTTP2_ERR_WOULDBLOCK; - } else { - rv = NGHTTP2_ERR_CALLBACK_FAILURE; - } - } else if (rv == 0) { - rv = NGHTTP2_ERR_EOF; - } - return rv; -} - -static int on_frame_send_callback(nghttp2_session *session, - const nghttp2_frame *frame, - void *user_data _U_) { - size_t i; - switch (frame->hd.type) { - case NGHTTP2_HEADERS: - if (nghttp2_session_get_stream_user_data(session, frame->hd.stream_id)) { - const nghttp2_nv *nva = frame->headers.nva; - printf("[INFO] C ----------------------------> S (HEADERS)\n"); - for (i = 0; i < frame->headers.nvlen; ++i) { - fwrite(nva[i].name, nva[i].namelen, 1, stdout); - printf(": "); - fwrite(nva[i].value, nva[i].valuelen, 1, stdout); - printf("\n"); - } - } - break; - case NGHTTP2_RST_STREAM: - printf("[INFO] C ----------------------------> S (RST_STREAM)\n"); - break; - case NGHTTP2_GOAWAY: - printf("[INFO] C ----------------------------> S (GOAWAY)\n"); - break; - } - return 0; -} - -static int on_frame_recv_callback(nghttp2_session *session, - const nghttp2_frame *frame, - void *user_data _U_) { - size_t i; - switch (frame->hd.type) { - case NGHTTP2_HEADERS: - if (frame->headers.cat == NGHTTP2_HCAT_RESPONSE) { - const nghttp2_nv *nva = frame->headers.nva; - struct Request *req; - req = nghttp2_session_get_stream_user_data(session, frame->hd.stream_id); - if (req) { - printf("[INFO] C <---------------------------- S (HEADERS)\n"); - for (i = 0; i < frame->headers.nvlen; ++i) { - fwrite(nva[i].name, nva[i].namelen, 1, stdout); - printf(": "); - fwrite(nva[i].value, nva[i].valuelen, 1, stdout); - printf("\n"); - } - } - } - break; - case NGHTTP2_RST_STREAM: - printf("[INFO] C <---------------------------- S (RST_STREAM)\n"); - break; - case NGHTTP2_GOAWAY: - printf("[INFO] C <---------------------------- S (GOAWAY)\n"); - break; - } - return 0; -} - -/* - * The implementation of nghttp2_on_stream_close_callback type. We use - * this function to know the response is fully received. Since we just - * fetch 1 resource in this program, after reception of the response, - * we submit GOAWAY and close the session. - */ -static int on_stream_close_callback(nghttp2_session *session, int32_t stream_id, - uint32_t error_code _U_, - void *user_data _U_) { - struct Request *req; - req = nghttp2_session_get_stream_user_data(session, stream_id); - if (req) { - int rv; - rv = nghttp2_session_terminate_session(session, NGHTTP2_NO_ERROR); - - if (rv != 0) { - diec("nghttp2_session_terminate_session", rv); - } - } - return 0; -} - -#define MAX_OUTLEN 4096 - -/* - * The implementation of nghttp2_on_data_chunk_recv_callback type. We - * use this function to print the received response body. - */ -static int on_data_chunk_recv_callback(nghttp2_session *session, - uint8_t flags _U_, int32_t stream_id, - const uint8_t *data, size_t len, - void *user_data _U_) { - struct Request *req; - req = nghttp2_session_get_stream_user_data(session, stream_id); - if (req) { - printf("[INFO] C <---------------------------- S (DATA chunk)\n" - "%lu bytes\n", - (unsigned long int)len); - fwrite(data, 1, len, stdout); - printf("\n"); - } - return 0; -} - -/* - * Setup callback functions. nghttp2 API offers many callback - * functions, but most of them are optional. The send_callback is - * always required. Since we use nghttp2_session_recv(), the - * recv_callback is also required. - */ -static void setup_nghttp2_callbacks(nghttp2_session_callbacks *callbacks) { - nghttp2_session_callbacks_set_send_callback(callbacks, send_callback); - - nghttp2_session_callbacks_set_recv_callback(callbacks, recv_callback); - - nghttp2_session_callbacks_set_on_frame_send_callback(callbacks, - on_frame_send_callback); - - nghttp2_session_callbacks_set_on_frame_recv_callback(callbacks, - on_frame_recv_callback); - - nghttp2_session_callbacks_set_on_stream_close_callback( - callbacks, on_stream_close_callback); - - nghttp2_session_callbacks_set_on_data_chunk_recv_callback( - callbacks, on_data_chunk_recv_callback); -} - -/* - * Callback function for TLS NPN. Since this program only supports - * HTTP/2 protocol, if server does not offer HTTP/2 the nghttp2 - * library supports, we terminate program. - */ -static int select_next_proto_cb(SSL *ssl _U_, unsigned char **out, - unsigned char *outlen, const unsigned char *in, - unsigned int inlen, void *arg _U_) { - int rv; - /* nghttp2_select_next_protocol() selects HTTP/2 protocol the - nghttp2 library supports. */ - rv = nghttp2_select_next_protocol(out, outlen, in, inlen); - if (rv <= 0) { - die("Server did not advertise HTTP/2 protocol"); - } - return SSL_TLSEXT_ERR_OK; -} - -/* - * Setup SSL/TLS context. - */ -static void init_ssl_ctx(SSL_CTX *ssl_ctx) { - /* Disable SSLv2 and enable all workarounds for buggy servers */ - SSL_CTX_set_options(ssl_ctx, SSL_OP_ALL | SSL_OP_NO_SSLv2); - SSL_CTX_set_mode(ssl_ctx, SSL_MODE_AUTO_RETRY); - SSL_CTX_set_mode(ssl_ctx, SSL_MODE_RELEASE_BUFFERS); - /* Set NPN callback */ - SSL_CTX_set_next_proto_select_cb(ssl_ctx, select_next_proto_cb, NULL); -} - -static void ssl_handshake(SSL *ssl, int fd) { - int rv; - if (SSL_set_fd(ssl, fd) == 0) { - dief("SSL_set_fd", ERR_error_string(ERR_get_error(), NULL)); - } - ERR_clear_error(); - rv = SSL_connect(ssl); - if (rv <= 0) { - dief("SSL_connect", ERR_error_string(ERR_get_error(), NULL)); - } -} - -/* - * Connects to the host |host| and port |port|. This function returns - * the file descriptor of the client socket. - */ -static int connect_to(const char *host, uint16_t port) { - struct addrinfo hints; - int fd = -1; - int rv; - char service[NI_MAXSERV]; - struct addrinfo *res, *rp; - snprintf(service, sizeof(service), "%u", port); - memset(&hints, 0, sizeof(struct addrinfo)); - hints.ai_family = AF_UNSPEC; - hints.ai_socktype = SOCK_STREAM; - rv = getaddrinfo(host, service, &hints, &res); - if (rv != 0) { - dief("getaddrinfo", gai_strerror(rv)); - } - for (rp = res; rp; rp = rp->ai_next) { - fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); - if (fd == -1) { - continue; - } - while ((rv = connect(fd, rp->ai_addr, rp->ai_addrlen)) == -1 && - errno == EINTR) - ; - if (rv == 0) { - break; - } - close(fd); - fd = -1; - } - freeaddrinfo(res); - return fd; -} - -static void make_non_block(int fd) { - int flags, rv; - while ((flags = fcntl(fd, F_GETFL, 0)) == -1 && errno == EINTR) - ; - if (flags == -1) { - dief("fcntl", strerror(errno)); - } - while ((rv = fcntl(fd, F_SETFL, flags | O_NONBLOCK)) == -1 && errno == EINTR) - ; - if (rv == -1) { - dief("fcntl", strerror(errno)); - } -} - -static void set_tcp_nodelay(int fd) { - int val = 1; - int rv; - rv = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &val, (socklen_t)sizeof(val)); - if (rv == -1) { - dief("setsockopt", strerror(errno)); - } -} - -/* - * Update |pollfd| based on the state of |connection|. - */ -static void ctl_poll(struct pollfd *pollfd, struct Connection *connection) { - pollfd->events = 0; - if (nghttp2_session_want_read(connection->session) || - connection->want_io == WANT_READ) { - pollfd->events |= POLLIN; - } - if (nghttp2_session_want_write(connection->session) || - connection->want_io == WANT_WRITE) { - pollfd->events |= POLLOUT; - } -} - -/* - * Submits the request |req| to the connection |connection|. This - * function does not send packets; just append the request to the - * internal queue in |connection->session|. - */ -static void submit_request(struct Connection *connection, struct Request *req) { - int32_t stream_id; - /* Make sure that the last item is NULL */ - const nghttp2_nv nva[] = {MAKE_NV(":method", "GET"), - MAKE_NV_CS(":path", req->path), - MAKE_NV(":scheme", "https"), - MAKE_NV_CS(":authority", req->hostport), - MAKE_NV("accept", "*/*"), - MAKE_NV("user-agent", "nghttp2/" NGHTTP2_VERSION)}; - - stream_id = nghttp2_submit_request(connection->session, NULL, nva, - sizeof(nva) / sizeof(nva[0]), NULL, req); - - if (stream_id < 0) { - diec("nghttp2_submit_request", stream_id); - } - - req->stream_id = stream_id; - printf("[INFO] Stream ID = %d\n", stream_id); -} - -/* - * Performs the network I/O. - */ -static void exec_io(struct Connection *connection) { - int rv; - rv = nghttp2_session_recv(connection->session); - if (rv != 0) { - diec("nghttp2_session_recv", rv); - } - rv = nghttp2_session_send(connection->session); - if (rv != 0) { - diec("nghttp2_session_send", rv); - } -} - -static void request_init(struct Request *req, const struct URI *uri) { - req->host = strcopy(uri->host, uri->hostlen); - req->port = uri->port; - req->path = strcopy(uri->path, uri->pathlen); - req->hostport = strcopy(uri->hostport, uri->hostportlen); - req->stream_id = -1; -} - -static void request_free(struct Request *req) { - free(req->host); - free(req->path); - free(req->hostport); -} - -/* - * Fetches the resource denoted by |uri|. - */ -static void fetch_uri(const struct URI *uri) { - nghttp2_session_callbacks *callbacks; - int fd; - SSL_CTX *ssl_ctx; - SSL *ssl; - struct Request req; - struct Connection connection; - int rv; - nfds_t npollfds = 1; - struct pollfd pollfds[1]; - - request_init(&req, uri); - - /* Establish connection and setup SSL */ - fd = connect_to(req.host, req.port); - if (fd == -1) { - die("Could not open file descriptor"); - } - ssl_ctx = SSL_CTX_new(SSLv23_client_method()); - if (ssl_ctx == NULL) { - dief("SSL_CTX_new", ERR_error_string(ERR_get_error(), NULL)); - } - init_ssl_ctx(ssl_ctx); - ssl = SSL_new(ssl_ctx); - if (ssl == NULL) { - dief("SSL_new", ERR_error_string(ERR_get_error(), NULL)); - } - /* To simplify the program, we perform SSL/TLS handshake in blocking - I/O. */ - ssl_handshake(ssl, fd); - - connection.ssl = ssl; - connection.want_io = IO_NONE; - - /* Here make file descriptor non-block */ - make_non_block(fd); - set_tcp_nodelay(fd); - - printf("[INFO] SSL/TLS handshake completed\n"); - - rv = nghttp2_session_callbacks_new(&callbacks); - - if (rv != 0) { - diec("nghttp2_session_callbacks_new", rv); - } - - setup_nghttp2_callbacks(callbacks); - - rv = nghttp2_session_client_new(&connection.session, callbacks, &connection); - - nghttp2_session_callbacks_del(callbacks); - - if (rv != 0) { - diec("nghttp2_session_client_new", rv); - } - - nghttp2_submit_settings(connection.session, NGHTTP2_FLAG_NONE, NULL, 0); - - /* Submit the HTTP request to the outbound queue. */ - submit_request(&connection, &req); - - pollfds[0].fd = fd; - ctl_poll(pollfds, &connection); - - /* Event loop */ - while (nghttp2_session_want_read(connection.session) || - nghttp2_session_want_write(connection.session)) { - int nfds = poll(pollfds, npollfds, -1); - if (nfds == -1) { - dief("poll", strerror(errno)); - } - if (pollfds[0].revents & (POLLIN | POLLOUT)) { - exec_io(&connection); - } - if ((pollfds[0].revents & POLLHUP) || (pollfds[0].revents & POLLERR)) { - die("Connection error"); - } - ctl_poll(pollfds, &connection); - } - - /* Resource cleanup */ - nghttp2_session_del(connection.session); - SSL_shutdown(ssl); - SSL_free(ssl); - SSL_CTX_free(ssl_ctx); - shutdown(fd, SHUT_WR); - close(fd); - request_free(&req); -} - -static int parse_uri(struct URI *res, const char *uri) { - /* We only interested in https */ - size_t len, i, offset; - int ipv6addr = 0; - memset(res, 0, sizeof(struct URI)); - len = strlen(uri); - if (len < 9 || memcmp("https://", uri, 8) != 0) { - return -1; - } - offset = 8; - res->host = res->hostport = &uri[offset]; - res->hostlen = 0; - if (uri[offset] == '[') { - /* IPv6 literal address */ - ++offset; - ++res->host; - ipv6addr = 1; - for (i = offset; i < len; ++i) { - if (uri[i] == ']') { - res->hostlen = i - offset; - offset = i + 1; - break; - } - } - } else { - const char delims[] = ":/?#"; - for (i = offset; i < len; ++i) { - if (strchr(delims, uri[i]) != NULL) { - break; - } - } - res->hostlen = i - offset; - offset = i; - } - if (res->hostlen == 0) { - return -1; - } - /* Assuming https */ - res->port = 443; - if (offset < len) { - if (uri[offset] == ':') { - /* port */ - const char delims[] = "/?#"; - int port = 0; - ++offset; - for (i = offset; i < len; ++i) { - if (strchr(delims, uri[i]) != NULL) { - break; - } - if ('0' <= uri[i] && uri[i] <= '9') { - port *= 10; - port += uri[i] - '0'; - if (port > 65535) { - return -1; - } - } else { - return -1; - } - } - if (port == 0) { - return -1; - } - offset = i; - res->port = port; - } - } - res->hostportlen = uri + offset + ipv6addr - res->host; - for (i = offset; i < len; ++i) { - if (uri[i] == '#') { - break; - } - } - if (i - offset == 0) { - res->path = "/"; - res->pathlen = 1; - } else { - res->path = &uri[offset]; - res->pathlen = i - offset; - } - return 0; -} - -int main(int argc, char **argv) { - struct URI uri; - struct sigaction act; - int rv; - - if (argc < 2) { - die("Specify a https URI"); - } - - memset(&act, 0, sizeof(struct sigaction)); - act.sa_handler = SIG_IGN; - sigaction(SIGPIPE, &act, 0); - - OPENSSL_config(NULL); - OpenSSL_add_all_algorithms(); - SSL_load_error_strings(); - SSL_library_init(); - - rv = parse_uri(&uri, argv[1]); - if (rv != 0) { - die("parse_uri failed"); - } - fetch_uri(&uri); - return EXIT_SUCCESS; -} diff --git a/examples/deflate.c b/examples/deflate.c deleted file mode 100644 index d0e7d3f..0000000 --- a/examples/deflate.c +++ /dev/null @@ -1,206 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2014 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#ifdef HAVE_CONFIG_H -#include -#endif /* !HAVE_CONFIG_H */ - -#include -#include - -#include - -#define MAKE_NV(K, V) \ - { \ - (uint8_t *) K, (uint8_t *)V, sizeof(K) - 1, sizeof(V) - 1, \ - NGHTTP2_NV_FLAG_NONE \ - } - -static void deflate(nghttp2_hd_deflater *deflater, - nghttp2_hd_inflater *inflater, const nghttp2_nv *const nva, - size_t nvlen); - -static int inflate_header_block(nghttp2_hd_inflater *inflater, uint8_t *in, - size_t inlen, int final); - -int main(int argc _U_, char **argv _U_) { - int rv; - nghttp2_hd_deflater *deflater; - nghttp2_hd_inflater *inflater; - /* Define 1st header set. This is looks like a HTTP request. */ - nghttp2_nv nva1[] = { - MAKE_NV(":scheme", "https"), MAKE_NV(":authority", "example.org"), - MAKE_NV(":path", "/"), MAKE_NV("user-agent", "libnghttp2"), - MAKE_NV("accept-encoding", "gzip, deflate")}; - /* Define 2nd header set */ - nghttp2_nv nva2[] = {MAKE_NV(":scheme", "https"), - MAKE_NV(":authority", "example.org"), - MAKE_NV(":path", "/stylesheet/style.css"), - MAKE_NV("user-agent", "libnghttp2"), - MAKE_NV("accept-encoding", "gzip, deflate"), - MAKE_NV("referer", "https://example.org")}; - - rv = nghttp2_hd_deflate_new(&deflater, 4096); - - if (rv != 0) { - fprintf(stderr, "nghttp2_hd_deflate_init failed with error: %s\n", - nghttp2_strerror(rv)); - exit(EXIT_FAILURE); - } - - rv = nghttp2_hd_inflate_new(&inflater); - - if (rv != 0) { - fprintf(stderr, "nghttp2_hd_inflate_init failed with error: %s\n", - nghttp2_strerror(rv)); - exit(EXIT_FAILURE); - } - - /* Encode and decode 1st header set */ - deflate(deflater, inflater, nva1, sizeof(nva1) / sizeof(nva1[0])); - - /* Encode and decode 2nd header set, using differential encoding - using state after encoding 1st header set. */ - deflate(deflater, inflater, nva2, sizeof(nva2) / sizeof(nva2[0])); - - nghttp2_hd_inflate_del(inflater); - nghttp2_hd_deflate_del(deflater); - - return 0; -} - -static void deflate(nghttp2_hd_deflater *deflater, - nghttp2_hd_inflater *inflater, const nghttp2_nv *const nva, - size_t nvlen) { - ssize_t rv; - uint8_t *buf; - size_t buflen; - size_t outlen; - size_t i; - size_t sum; - - sum = 0; - - for (i = 0; i < nvlen; ++i) { - sum += nva[i].namelen + nva[i].valuelen; - } - - printf("Input (%zu byte(s)):\n\n", sum); - - for (i = 0; i < nvlen; ++i) { - fwrite(nva[i].name, nva[i].namelen, 1, stdout); - printf(": "); - fwrite(nva[i].value, nva[i].valuelen, 1, stdout); - printf("\n"); - } - - buflen = nghttp2_hd_deflate_bound(deflater, nva, nvlen); - buf = malloc(buflen); - - rv = nghttp2_hd_deflate_hd(deflater, buf, buflen, nva, nvlen); - - if (rv < 0) { - fprintf(stderr, "nghttp2_hd_deflate_hd() failed with error: %s\n", - nghttp2_strerror((int)rv)); - - free(buf); - - exit(EXIT_FAILURE); - } - - outlen = rv; - - printf("\nDeflate (%zu byte(s), ratio %.02f):\n\n", outlen, - sum == 0 ? 0 : (double)outlen / sum); - - for (i = 0; i < outlen; ++i) { - if ((i & 0x0fu) == 0) { - printf("%08zX: ", i); - } - - printf("%02X ", buf[i]); - - if (((i + 1) & 0x0fu) == 0) { - printf("\n"); - } - } - - printf("\n\nInflate:\n\n"); - - /* We pass 1 to final parameter, because buf contains whole deflated - header data. */ - rv = inflate_header_block(inflater, buf, outlen, 1); - - if (rv != 0) { - free(buf); - - exit(EXIT_FAILURE); - } - - printf("\n-----------------------------------------------------------" - "--------------------\n"); - - free(buf); -} - -int inflate_header_block(nghttp2_hd_inflater *inflater, uint8_t *in, - size_t inlen, int final) { - ssize_t rv; - - for (;;) { - nghttp2_nv nv; - int inflate_flags = 0; - size_t proclen; - - rv = nghttp2_hd_inflate_hd(inflater, &nv, &inflate_flags, in, inlen, final); - - if (rv < 0) { - fprintf(stderr, "inflate failed with error code %zd", rv); - return -1; - } - - proclen = rv; - - in += proclen; - inlen -= proclen; - - if (inflate_flags & NGHTTP2_HD_INFLATE_EMIT) { - fwrite(nv.name, nv.namelen, 1, stderr); - fprintf(stderr, ": "); - fwrite(nv.value, nv.valuelen, 1, stderr); - fprintf(stderr, "\n"); - } - - if (inflate_flags & NGHTTP2_HD_INFLATE_FINAL) { - nghttp2_hd_inflate_end_headers(inflater); - break; - } - - if ((inflate_flags & NGHTTP2_HD_INFLATE_EMIT) == 0 && inlen == 0) { - break; - } - } - - return 0; -} diff --git a/examples/libevent-client.c b/examples/libevent-client.c deleted file mode 100644 index 17ed94b..0000000 --- a/examples/libevent-client.c +++ /dev/null @@ -1,561 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2013 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#ifdef HAVE_CONFIG_H -#include -#endif /* HAVE_CONFIG_H */ - -#include -#ifdef HAVE_UNISTD_H -#include -#endif /* HAVE_UNISTD_H */ -#ifdef HAVE_SYS_SOCKET_H -#include -#endif /* HAVE_SYS_SOCKET_H */ -#ifdef HAVE_NETINET_IN_H -#include -#endif /* HAVE_NETINET_IN_H */ -#include -#include -#include - -#include -#include -#include - -#include -#include -#include -#include - -#include - -#include "http-parser/http_parser.h" - -#define ARRLEN(x) (sizeof(x) / sizeof(x[0])) - -typedef struct { - /* The NULL-terminated URI string to retrieve. */ - const char *uri; - /* Parsed result of the |uri| */ - struct http_parser_url *u; - /* The authority portion of the |uri|, not NULL-terminated */ - char *authority; - /* The path portion of the |uri|, including query, not - NULL-terminated */ - char *path; - /* The length of the |authority| */ - size_t authoritylen; - /* The length of the |path| */ - size_t pathlen; - /* The stream ID of this stream */ - int32_t stream_id; -} http2_stream_data; - -typedef struct { - nghttp2_session *session; - struct evdns_base *dnsbase; - struct bufferevent *bev; - http2_stream_data *stream_data; -} http2_session_data; - -static http2_stream_data *create_http2_stream_data(const char *uri, - struct http_parser_url *u) { - /* MAX 5 digits (max 65535) + 1 ':' + 1 NULL (because of snprintf) */ - size_t extra = 7; - http2_stream_data *stream_data = malloc(sizeof(http2_stream_data)); - - stream_data->uri = uri; - stream_data->u = u; - stream_data->stream_id = -1; - - stream_data->authoritylen = u->field_data[UF_HOST].len; - stream_data->authority = malloc(stream_data->authoritylen + extra); - memcpy(stream_data->authority, &uri[u->field_data[UF_HOST].off], - u->field_data[UF_HOST].len); - if (u->field_set & (1 << UF_PORT)) { - stream_data->authoritylen += - snprintf(stream_data->authority + u->field_data[UF_HOST].len, extra, - ":%u", u->port); - } - - stream_data->pathlen = 0; - if (u->field_set & (1 << UF_PATH)) { - stream_data->pathlen = u->field_data[UF_PATH].len; - } - if (u->field_set & (1 << UF_QUERY)) { - /* +1 for '?' character */ - stream_data->pathlen += u->field_data[UF_QUERY].len + 1; - } - if (stream_data->pathlen > 0) { - stream_data->path = malloc(stream_data->pathlen); - if (u->field_set & (1 << UF_PATH)) { - memcpy(stream_data->path, &uri[u->field_data[UF_PATH].off], - u->field_data[UF_PATH].len); - } - if (u->field_set & (1 << UF_QUERY)) { - memcpy(stream_data->path + u->field_data[UF_PATH].len + 1, - &uri[u->field_data[UF_QUERY].off], u->field_data[UF_QUERY].len); - } - } else { - stream_data->path = NULL; - } - return stream_data; -} - -static void delete_http2_stream_data(http2_stream_data *stream_data) { - free(stream_data->path); - free(stream_data->authority); - free(stream_data); -} - -/* Initializes |session_data| */ -static http2_session_data * -create_http2_session_data(struct event_base *evbase) { - http2_session_data *session_data = malloc(sizeof(http2_session_data)); - - memset(session_data, 0, sizeof(http2_session_data)); - session_data->dnsbase = evdns_base_new(evbase, 1); - return session_data; -} - -static void delete_http2_session_data(http2_session_data *session_data) { - SSL *ssl = bufferevent_openssl_get_ssl(session_data->bev); - - if (ssl) { - SSL_shutdown(ssl); - } - bufferevent_free(session_data->bev); - session_data->bev = NULL; - evdns_base_free(session_data->dnsbase, 1); - session_data->dnsbase = NULL; - nghttp2_session_del(session_data->session); - session_data->session = NULL; - if (session_data->stream_data) { - delete_http2_stream_data(session_data->stream_data); - session_data->stream_data = NULL; - } - free(session_data); -} - -static void print_header(FILE *f, const uint8_t *name, size_t namelen, - const uint8_t *value, size_t valuelen) { - fwrite(name, namelen, 1, f); - fprintf(f, ": "); - fwrite(value, valuelen, 1, f); - fprintf(f, "\n"); -} - -/* Print HTTP headers to |f|. Please note that this function does not - take into account that header name and value are sequence of - octets, therefore they may contain non-printable characters. */ -static void print_headers(FILE *f, nghttp2_nv *nva, size_t nvlen) { - size_t i; - for (i = 0; i < nvlen; ++i) { - print_header(f, nva[i].name, nva[i].namelen, nva[i].value, nva[i].valuelen); - } - fprintf(f, "\n"); -} - -/* nghttp2_send_callback. Here we transmit the |data|, |length| bytes, - to the network. Because we are using libevent bufferevent, we just - write those bytes into bufferevent buffer. */ -static ssize_t send_callback(nghttp2_session *session _U_, const uint8_t *data, - size_t length, int flags _U_, void *user_data) { - http2_session_data *session_data = (http2_session_data *)user_data; - struct bufferevent *bev = session_data->bev; - bufferevent_write(bev, data, length); - return length; -} - -/* nghttp2_on_header_callback: Called when nghttp2 library emits - single header name/value pair. */ -static int on_header_callback(nghttp2_session *session _U_, - const nghttp2_frame *frame, const uint8_t *name, - size_t namelen, const uint8_t *value, - size_t valuelen, uint8_t flags _U_, - void *user_data) { - http2_session_data *session_data = (http2_session_data *)user_data; - switch (frame->hd.type) { - case NGHTTP2_HEADERS: - if (frame->headers.cat == NGHTTP2_HCAT_RESPONSE && - session_data->stream_data->stream_id == frame->hd.stream_id) { - /* Print response headers for the initiated request. */ - print_header(stderr, name, namelen, value, valuelen); - break; - } - } - return 0; -} - -/* nghttp2_on_begin_headers_callback: Called when nghttp2 library gets - started to receive header block. */ -static int on_begin_headers_callback(nghttp2_session *session _U_, - const nghttp2_frame *frame, - void *user_data) { - http2_session_data *session_data = (http2_session_data *)user_data; - switch (frame->hd.type) { - case NGHTTP2_HEADERS: - if (frame->headers.cat == NGHTTP2_HCAT_RESPONSE && - session_data->stream_data->stream_id == frame->hd.stream_id) { - fprintf(stderr, "Response headers for stream ID=%d:\n", - frame->hd.stream_id); - } - break; - } - return 0; -} - -/* nghttp2_on_frame_recv_callback: Called when nghttp2 library - received a complete frame from the remote peer. */ -static int on_frame_recv_callback(nghttp2_session *session _U_, - const nghttp2_frame *frame, void *user_data) { - http2_session_data *session_data = (http2_session_data *)user_data; - switch (frame->hd.type) { - case NGHTTP2_HEADERS: - if (frame->headers.cat == NGHTTP2_HCAT_RESPONSE && - session_data->stream_data->stream_id == frame->hd.stream_id) { - fprintf(stderr, "All headers received\n"); - } - break; - } - return 0; -} - -/* nghttp2_on_data_chunk_recv_callback: Called when DATA frame is - received from the remote peer. In this implementation, if the frame - is meant to the stream we initiated, print the received data in - stdout, so that the user can redirect its output to the file - easily. */ -static int on_data_chunk_recv_callback(nghttp2_session *session _U_, - uint8_t flags _U_, int32_t stream_id, - const uint8_t *data, size_t len, - void *user_data) { - http2_session_data *session_data = (http2_session_data *)user_data; - if (session_data->stream_data->stream_id == stream_id) { - fwrite(data, len, 1, stdout); - } - return 0; -} - -/* nghttp2_on_stream_close_callback: Called when a stream is about to - closed. This example program only deals with 1 HTTP request (1 - stream), if it is closed, we send GOAWAY and tear down the - session */ -static int on_stream_close_callback(nghttp2_session *session, int32_t stream_id, - uint32_t error_code, void *user_data) { - http2_session_data *session_data = (http2_session_data *)user_data; - int rv; - - if (session_data->stream_data->stream_id == stream_id) { - fprintf(stderr, "Stream %d closed with error_code=%d\n", stream_id, - error_code); - rv = nghttp2_session_terminate_session(session, NGHTTP2_NO_ERROR); - if (rv != 0) { - return NGHTTP2_ERR_CALLBACK_FAILURE; - } - } - return 0; -} - -/* NPN TLS extension client callback. We check that server advertised - the HTTP/2 protocol the nghttp2 library supports. If not, exit - the program. */ -static int select_next_proto_cb(SSL *ssl _U_, unsigned char **out, - unsigned char *outlen, const unsigned char *in, - unsigned int inlen, void *arg _U_) { - if (nghttp2_select_next_protocol(out, outlen, in, inlen) <= 0) { - errx(1, "Server did not advertise " NGHTTP2_PROTO_VERSION_ID); - } - return SSL_TLSEXT_ERR_OK; -} - -/* Create SSL_CTX. */ -static SSL_CTX *create_ssl_ctx(void) { - SSL_CTX *ssl_ctx; - ssl_ctx = SSL_CTX_new(SSLv23_client_method()); - if (!ssl_ctx) { - errx(1, "Could not create SSL/TLS context: %s", - ERR_error_string(ERR_get_error(), NULL)); - } - SSL_CTX_set_options(ssl_ctx, - SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | - SSL_OP_NO_COMPRESSION | - SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION); - SSL_CTX_set_next_proto_select_cb(ssl_ctx, select_next_proto_cb, NULL); - return ssl_ctx; -} - -/* Create SSL object */ -static SSL *create_ssl(SSL_CTX *ssl_ctx) { - SSL *ssl; - ssl = SSL_new(ssl_ctx); - if (!ssl) { - errx(1, "Could not create SSL/TLS session object: %s", - ERR_error_string(ERR_get_error(), NULL)); - } - return ssl; -} - -static void initialize_nghttp2_session(http2_session_data *session_data) { - nghttp2_session_callbacks *callbacks; - - nghttp2_session_callbacks_new(&callbacks); - - nghttp2_session_callbacks_set_send_callback(callbacks, send_callback); - - nghttp2_session_callbacks_set_on_frame_recv_callback(callbacks, - on_frame_recv_callback); - - nghttp2_session_callbacks_set_on_data_chunk_recv_callback( - callbacks, on_data_chunk_recv_callback); - - nghttp2_session_callbacks_set_on_stream_close_callback( - callbacks, on_stream_close_callback); - - nghttp2_session_callbacks_set_on_header_callback(callbacks, - on_header_callback); - - nghttp2_session_callbacks_set_on_begin_headers_callback( - callbacks, on_begin_headers_callback); - - nghttp2_session_client_new(&session_data->session, callbacks, session_data); - - nghttp2_session_callbacks_del(callbacks); -} - -static void send_client_connection_header(http2_session_data *session_data) { - nghttp2_settings_entry iv[1] = { - {NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100}}; - int rv; - - /* client 24 bytes magic string will be sent by nghttp2 library */ - rv = nghttp2_submit_settings(session_data->session, NGHTTP2_FLAG_NONE, iv, - ARRLEN(iv)); - if (rv != 0) { - errx(1, "Could not submit SETTINGS: %s", nghttp2_strerror(rv)); - } -} - -#define MAKE_NV(NAME, VALUE, VALUELEN) \ - { \ - (uint8_t *) NAME, (uint8_t *)VALUE, sizeof(NAME) - 1, VALUELEN, \ - NGHTTP2_NV_FLAG_NONE \ - } - -#define MAKE_NV2(NAME, VALUE) \ - { \ - (uint8_t *) NAME, (uint8_t *)VALUE, sizeof(NAME) - 1, sizeof(VALUE) - 1, \ - NGHTTP2_NV_FLAG_NONE \ - } - -/* Send HTTP request to the remote peer */ -static void submit_request(http2_session_data *session_data) { - int32_t stream_id; - http2_stream_data *stream_data = session_data->stream_data; - const char *uri = stream_data->uri; - const struct http_parser_url *u = stream_data->u; - nghttp2_nv hdrs[] = { - MAKE_NV2(":method", "GET"), - MAKE_NV(":scheme", &uri[u->field_data[UF_SCHEMA].off], - u->field_data[UF_SCHEMA].len), - MAKE_NV(":authority", stream_data->authority, stream_data->authoritylen), - MAKE_NV(":path", stream_data->path, stream_data->pathlen)}; - fprintf(stderr, "Request headers:\n"); - print_headers(stderr, hdrs, ARRLEN(hdrs)); - stream_id = nghttp2_submit_request(session_data->session, NULL, hdrs, - ARRLEN(hdrs), NULL, stream_data); - if (stream_id < 0) { - errx(1, "Could not submit HTTP request: %s", nghttp2_strerror(stream_id)); - } - - stream_data->stream_id = stream_id; -} - -/* Serialize the frame and send (or buffer) the data to - bufferevent. */ -static int session_send(http2_session_data *session_data) { - int rv; - - rv = nghttp2_session_send(session_data->session); - if (rv != 0) { - warnx("Fatal error: %s", nghttp2_strerror(rv)); - return -1; - } - return 0; -} - -/* readcb for bufferevent. Here we get the data from the input buffer - of bufferevent and feed them to nghttp2 library. This may invoke - nghttp2 callbacks. It may also queues the frame in nghttp2 session - context. To send them, we call session_send() in the end. */ -static void readcb(struct bufferevent *bev, void *ptr) { - http2_session_data *session_data = (http2_session_data *)ptr; - ssize_t readlen; - struct evbuffer *input = bufferevent_get_input(bev); - size_t datalen = evbuffer_get_length(input); - unsigned char *data = evbuffer_pullup(input, -1); - - readlen = nghttp2_session_mem_recv(session_data->session, data, datalen); - if (readlen < 0) { - warnx("Fatal error: %s", nghttp2_strerror((int)readlen)); - delete_http2_session_data(session_data); - return; - } - if (evbuffer_drain(input, readlen) != 0) { - warnx("Fatal error: evbuffer_drain failed"); - delete_http2_session_data(session_data); - return; - } - if (session_send(session_data) != 0) { - delete_http2_session_data(session_data); - return; - } -} - -/* writecb for bufferevent. To greaceful shutdown after sending or - receiving GOAWAY, we check the some conditions on the nghttp2 - library and output buffer of bufferevent. If it indicates we have - no business to this session, tear down the connection. */ -static void writecb(struct bufferevent *bev _U_, void *ptr) { - http2_session_data *session_data = (http2_session_data *)ptr; - if (nghttp2_session_want_read(session_data->session) == 0 && - nghttp2_session_want_write(session_data->session) == 0 && - evbuffer_get_length(bufferevent_get_output(session_data->bev)) == 0) { - delete_http2_session_data(session_data); - } -} - -/* eventcb for bufferevent. For the purpose of simplicity and - readability of the example program, we omitted the certificate and - peer verification. After SSL/TLS handshake is over, initialize - nghttp2 library session, and send client connection header. Then - send HTTP request. */ -static void eventcb(struct bufferevent *bev, short events, void *ptr) { - http2_session_data *session_data = (http2_session_data *)ptr; - if (events & BEV_EVENT_CONNECTED) { - int fd = bufferevent_getfd(bev); - int val = 1; - fprintf(stderr, "Connected\n"); - setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char *)&val, sizeof(val)); - initialize_nghttp2_session(session_data); - send_client_connection_header(session_data); - submit_request(session_data); - if (session_send(session_data) != 0) { - delete_http2_session_data(session_data); - } - return; - } - if (events & BEV_EVENT_EOF) { - warnx("Disconnected from the remote host"); - } else if (events & BEV_EVENT_ERROR) { - warnx("Network error"); - } else if (events & BEV_EVENT_TIMEOUT) { - warnx("Timeout"); - } - delete_http2_session_data(session_data); -} - -/* Start connecting to the remote peer |host:port| */ -static void initiate_connection(struct event_base *evbase, SSL_CTX *ssl_ctx, - const char *host, uint16_t port, - http2_session_data *session_data) { - int rv; - struct bufferevent *bev; - SSL *ssl; - - ssl = create_ssl(ssl_ctx); - bev = bufferevent_openssl_socket_new( - evbase, -1, ssl, BUFFEREVENT_SSL_CONNECTING, - BEV_OPT_DEFER_CALLBACKS | BEV_OPT_CLOSE_ON_FREE); - bufferevent_setcb(bev, readcb, writecb, eventcb, session_data); - rv = bufferevent_socket_connect_hostname(bev, session_data->dnsbase, - AF_UNSPEC, host, port); - - if (rv != 0) { - errx(1, "Could not connect to the remote host %s", host); - } - session_data->bev = bev; -} - -/* Get resource denoted by the |uri|. The debug and error messages are - printed in stderr, while the response body is printed in stdout. */ -static void run(const char *uri) { - struct http_parser_url u; - char *host; - uint16_t port; - int rv; - SSL_CTX *ssl_ctx; - struct event_base *evbase; - http2_session_data *session_data; - - /* Parse the |uri| and stores its components in |u| */ - rv = http_parser_parse_url(uri, strlen(uri), 0, &u); - if (rv != 0) { - errx(1, "Could not parse URI %s", uri); - } - host = strndup(&uri[u.field_data[UF_HOST].off], u.field_data[UF_HOST].len); - if (!(u.field_set & (1 << UF_PORT))) { - port = 443; - } else { - port = u.port; - } - - ssl_ctx = create_ssl_ctx(); - - evbase = event_base_new(); - - session_data = create_http2_session_data(evbase); - session_data->stream_data = create_http2_stream_data(uri, &u); - - initiate_connection(evbase, ssl_ctx, host, port, session_data); - free(host); - host = NULL; - - event_base_loop(evbase, 0); - - event_base_free(evbase); - SSL_CTX_free(ssl_ctx); -} - -int main(int argc, char **argv) { - struct sigaction act; - - if (argc < 2) { - fprintf(stderr, "Usage: libevent-client HTTPS_URI\n"); - exit(EXIT_FAILURE); - } - - memset(&act, 0, sizeof(struct sigaction)); - act.sa_handler = SIG_IGN; - sigaction(SIGPIPE, &act, NULL); - - OPENSSL_config(NULL); - OpenSSL_add_all_algorithms(); - SSL_load_error_strings(); - SSL_library_init(); - - run(argv[1]); - return 0; -} diff --git a/examples/libevent-server.c b/examples/libevent-server.c deleted file mode 100644 index 412c6d0..0000000 --- a/examples/libevent-server.c +++ /dev/null @@ -1,734 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2013 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#ifdef HAVE_CONFIG_H -#include -#endif /* HAVE_CONFIG_H */ - -#include -#ifdef HAVE_SYS_SOCKET_H -#include -#endif /* HAVE_SYS_SOCKET_H */ -#ifdef HAVE_NETDB_H -#include -#endif /* HAVE_NETDB_H */ -#include -#ifdef HAVE_UNISTD_H -#include -#endif /* HAVE_UNISTD_H */ -#include -#ifdef HAVE_FCNTL_H -#include -#endif /* HAVE_FCNTL_H */ -#include -#ifdef HAVE_NETINET_IN_H -#include -#endif /* HAVE_NETINET_IN_H */ -#include -#include - -#include -#include -#include - -#include -#include -#include -#include - -#include - -#define OUTPUT_WOULDBLOCK_THRESHOLD (1 << 16) - -#define ARRLEN(x) (sizeof(x) / sizeof(x[0])) - -#define MAKE_NV(NAME, VALUE) \ - { \ - (uint8_t *) NAME, (uint8_t *)VALUE, sizeof(NAME) - 1, sizeof(VALUE) - 1, \ - NGHTTP2_NV_FLAG_NONE \ - } - -struct app_context; -typedef struct app_context app_context; - -typedef struct http2_stream_data { - struct http2_stream_data *prev, *next; - char *request_path; - int32_t stream_id; - int fd; -} http2_stream_data; - -typedef struct http2_session_data { - struct http2_stream_data root; - struct bufferevent *bev; - app_context *app_ctx; - nghttp2_session *session; - char *client_addr; -} http2_session_data; - -struct app_context { - SSL_CTX *ssl_ctx; - struct event_base *evbase; -}; - -static unsigned char next_proto_list[256]; -static size_t next_proto_list_len; - -static int next_proto_cb(SSL *s _U_, const unsigned char **data, - unsigned int *len, void *arg _U_) { - *data = next_proto_list; - *len = (unsigned int)next_proto_list_len; - return SSL_TLSEXT_ERR_OK; -} - -/* Create SSL_CTX. */ -static SSL_CTX *create_ssl_ctx(const char *key_file, const char *cert_file) { - SSL_CTX *ssl_ctx; - EC_KEY *ecdh; - - ssl_ctx = SSL_CTX_new(SSLv23_server_method()); - if (!ssl_ctx) { - errx(1, "Could not create SSL/TLS context: %s", - ERR_error_string(ERR_get_error(), NULL)); - } - SSL_CTX_set_options(ssl_ctx, - SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | - SSL_OP_NO_COMPRESSION | - SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION); - - ecdh = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1); - if (!ecdh) { - errx(1, "EC_KEY_new_by_curv_name failed: %s", - ERR_error_string(ERR_get_error(), NULL)); - } - SSL_CTX_set_tmp_ecdh(ssl_ctx, ecdh); - EC_KEY_free(ecdh); - - if (SSL_CTX_use_PrivateKey_file(ssl_ctx, key_file, SSL_FILETYPE_PEM) != 1) { - errx(1, "Could not read private key file %s", key_file); - } - if (SSL_CTX_use_certificate_chain_file(ssl_ctx, cert_file) != 1) { - errx(1, "Could not read certificate file %s", cert_file); - } - - next_proto_list[0] = NGHTTP2_PROTO_VERSION_ID_LEN; - memcpy(&next_proto_list[1], NGHTTP2_PROTO_VERSION_ID, - NGHTTP2_PROTO_VERSION_ID_LEN); - next_proto_list_len = 1 + NGHTTP2_PROTO_VERSION_ID_LEN; - - SSL_CTX_set_next_protos_advertised_cb(ssl_ctx, next_proto_cb, NULL); - return ssl_ctx; -} - -/* Create SSL object */ -static SSL *create_ssl(SSL_CTX *ssl_ctx) { - SSL *ssl; - ssl = SSL_new(ssl_ctx); - if (!ssl) { - errx(1, "Could not create SSL/TLS session object: %s", - ERR_error_string(ERR_get_error(), NULL)); - } - return ssl; -} - -static void add_stream(http2_session_data *session_data, - http2_stream_data *stream_data) { - stream_data->next = session_data->root.next; - session_data->root.next = stream_data; - stream_data->prev = &session_data->root; - if (stream_data->next) { - stream_data->next->prev = stream_data; - } -} - -static void remove_stream(http2_session_data *session_data _U_, - http2_stream_data *stream_data) { - stream_data->prev->next = stream_data->next; - if (stream_data->next) { - stream_data->next->prev = stream_data->prev; - } -} - -static http2_stream_data * -create_http2_stream_data(http2_session_data *session_data, int32_t stream_id) { - http2_stream_data *stream_data; - stream_data = malloc(sizeof(http2_stream_data)); - memset(stream_data, 0, sizeof(http2_stream_data)); - stream_data->stream_id = stream_id; - stream_data->fd = -1; - - add_stream(session_data, stream_data); - return stream_data; -} - -static void delete_http2_stream_data(http2_stream_data *stream_data) { - if (stream_data->fd != -1) { - close(stream_data->fd); - } - free(stream_data->request_path); - free(stream_data); -} - -static http2_session_data *create_http2_session_data(app_context *app_ctx, - int fd, - struct sockaddr *addr, - int addrlen) { - int rv; - http2_session_data *session_data; - SSL *ssl; - char host[NI_MAXHOST]; - int val = 1; - - ssl = create_ssl(app_ctx->ssl_ctx); - session_data = malloc(sizeof(http2_session_data)); - memset(session_data, 0, sizeof(http2_session_data)); - session_data->app_ctx = app_ctx; - setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char *)&val, sizeof(val)); - session_data->bev = bufferevent_openssl_socket_new( - app_ctx->evbase, fd, ssl, BUFFEREVENT_SSL_ACCEPTING, - BEV_OPT_CLOSE_ON_FREE | BEV_OPT_DEFER_CALLBACKS); - rv = getnameinfo(addr, addrlen, host, sizeof(host), NULL, 0, NI_NUMERICHOST); - if (rv != 0) { - session_data->client_addr = strdup("(unknown)"); - } else { - session_data->client_addr = strdup(host); - } - - return session_data; -} - -static void delete_http2_session_data(http2_session_data *session_data) { - http2_stream_data *stream_data; - SSL *ssl = bufferevent_openssl_get_ssl(session_data->bev); - fprintf(stderr, "%s disconnected\n", session_data->client_addr); - if (ssl) { - SSL_shutdown(ssl); - } - bufferevent_free(session_data->bev); - nghttp2_session_del(session_data->session); - for (stream_data = session_data->root.next; stream_data;) { - http2_stream_data *next = stream_data->next; - delete_http2_stream_data(stream_data); - stream_data = next; - } - free(session_data->client_addr); - free(session_data); -} - -/* Serialize the frame and send (or buffer) the data to - bufferevent. */ -static int session_send(http2_session_data *session_data) { - int rv; - rv = nghttp2_session_send(session_data->session); - if (rv != 0) { - warnx("Fatal error: %s", nghttp2_strerror(rv)); - return -1; - } - return 0; -} - -/* Read the data in the bufferevent and feed them into nghttp2 library - function. Invocation of nghttp2_session_mem_recv() may make - additional pending frames, so call session_send() at the end of the - function. */ -static int session_recv(http2_session_data *session_data) { - ssize_t readlen; - struct evbuffer *input = bufferevent_get_input(session_data->bev); - size_t datalen = evbuffer_get_length(input); - unsigned char *data = evbuffer_pullup(input, -1); - - readlen = nghttp2_session_mem_recv(session_data->session, data, datalen); - if (readlen < 0) { - warnx("Fatal error: %s", nghttp2_strerror((int)readlen)); - return -1; - } - if (evbuffer_drain(input, readlen) != 0) { - warnx("Fatal error: evbuffer_drain failed"); - return -1; - } - if (session_send(session_data) != 0) { - return -1; - } - return 0; -} - -static ssize_t send_callback(nghttp2_session *session _U_, const uint8_t *data, - size_t length, int flags _U_, void *user_data) { - http2_session_data *session_data = (http2_session_data *)user_data; - struct bufferevent *bev = session_data->bev; - /* Avoid excessive buffering in server side. */ - if (evbuffer_get_length(bufferevent_get_output(session_data->bev)) >= - OUTPUT_WOULDBLOCK_THRESHOLD) { - return NGHTTP2_ERR_WOULDBLOCK; - } - bufferevent_write(bev, data, length); - return length; -} - -/* Returns nonzero if the string |s| ends with the substring |sub| */ -static int ends_with(const char *s, const char *sub) { - size_t slen = strlen(s); - size_t sublen = strlen(sub); - if (slen < sublen) { - return 0; - } - return memcmp(s + slen - sublen, sub, sublen) == 0; -} - -/* Returns int value of hex string character |c| */ -static uint8_t hex_to_uint(uint8_t c) { - if ('0' <= c && c <= '9') { - return c - '0'; - } - if ('A' <= c && c <= 'F') { - return c - 'A' + 10; - } - if ('a' <= c && c <= 'f') { - return c - 'a' + 10; - } - return 0; -} - -/* Decodes percent-encoded byte string |value| with length |valuelen| - and returns the decoded byte string in allocated buffer. The return - value is NULL terminated. The caller must free the returned - string. */ -static char *percent_decode(const uint8_t *value, size_t valuelen) { - char *res; - - res = malloc(valuelen + 1); - if (valuelen > 3) { - size_t i, j; - for (i = 0, j = 0; i < valuelen - 2;) { - if (value[i] != '%' || !isxdigit(value[i + 1]) || - !isxdigit(value[i + 2])) { - res[j++] = value[i++]; - continue; - } - res[j++] = (hex_to_uint(value[i + 1]) << 4) + hex_to_uint(value[i + 2]); - i += 3; - } - memcpy(&res[j], &value[i], 2); - res[j + 2] = '\0'; - } else { - memcpy(res, value, valuelen); - res[valuelen] = '\0'; - } - return res; -} - -static ssize_t file_read_callback(nghttp2_session *session _U_, - int32_t stream_id _U_, uint8_t *buf, - size_t length, uint32_t *data_flags, - nghttp2_data_source *source, - void *user_data _U_) { - int fd = source->fd; - ssize_t r; - while ((r = read(fd, buf, length)) == -1 && errno == EINTR) - ; - if (r == -1) { - return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; - } - if (r == 0) { - *data_flags |= NGHTTP2_DATA_FLAG_EOF; - } - return r; -} - -static int send_response(nghttp2_session *session, int32_t stream_id, - nghttp2_nv *nva, size_t nvlen, int fd) { - int rv; - nghttp2_data_provider data_prd; - data_prd.source.fd = fd; - data_prd.read_callback = file_read_callback; - - rv = nghttp2_submit_response(session, stream_id, nva, nvlen, &data_prd); - if (rv != 0) { - warnx("Fatal error: %s", nghttp2_strerror(rv)); - return -1; - } - return 0; -} - -const char ERROR_HTML[] = "404" - "

      404 Not Found

      "; - -static int error_reply(nghttp2_session *session, - http2_stream_data *stream_data) { - int rv; - ssize_t writelen; - int pipefd[2]; - nghttp2_nv hdrs[] = {MAKE_NV(":status", "404")}; - - rv = pipe(pipefd); - if (rv != 0) { - warn("Could not create pipe"); - rv = nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, - stream_data->stream_id, - NGHTTP2_INTERNAL_ERROR); - if (rv != 0) { - warnx("Fatal error: %s", nghttp2_strerror(rv)); - return -1; - } - return 0; - } - - writelen = write(pipefd[1], ERROR_HTML, sizeof(ERROR_HTML) - 1); - close(pipefd[1]); - - if (writelen != sizeof(ERROR_HTML) - 1) { - close(pipefd[0]); - return -1; - } - - stream_data->fd = pipefd[0]; - - if (send_response(session, stream_data->stream_id, hdrs, ARRLEN(hdrs), - pipefd[0]) != 0) { - close(pipefd[0]); - return -1; - } - return 0; -} - -/* nghttp2_on_header_callback: Called when nghttp2 library emits - single header name/value pair. */ -static int on_header_callback(nghttp2_session *session, - const nghttp2_frame *frame, const uint8_t *name, - size_t namelen, const uint8_t *value, - size_t valuelen, uint8_t flags _U_, - void *user_data _U_) { - http2_stream_data *stream_data; - const char PATH[] = ":path"; - switch (frame->hd.type) { - case NGHTTP2_HEADERS: - if (frame->headers.cat != NGHTTP2_HCAT_REQUEST) { - break; - } - stream_data = - nghttp2_session_get_stream_user_data(session, frame->hd.stream_id); - if (!stream_data || stream_data->request_path) { - break; - } - if (namelen == sizeof(PATH) - 1 && memcmp(PATH, name, namelen) == 0) { - size_t j; - for (j = 0; j < valuelen && value[j] != '?'; ++j) - ; - stream_data->request_path = percent_decode(value, j); - } - break; - } - return 0; -} - -static int on_begin_headers_callback(nghttp2_session *session, - const nghttp2_frame *frame, - void *user_data) { - http2_session_data *session_data = (http2_session_data *)user_data; - http2_stream_data *stream_data; - - if (frame->hd.type != NGHTTP2_HEADERS || - frame->headers.cat != NGHTTP2_HCAT_REQUEST) { - return 0; - } - stream_data = create_http2_stream_data(session_data, frame->hd.stream_id); - nghttp2_session_set_stream_user_data(session, frame->hd.stream_id, - stream_data); - return 0; -} - -/* Minimum check for directory traversal. Returns nonzero if it is - safe. */ -static int check_path(const char *path) { - /* We don't like '\' in url. */ - return path[0] && path[0] == '/' && strchr(path, '\\') == NULL && - strstr(path, "/../") == NULL && strstr(path, "/./") == NULL && - !ends_with(path, "/..") && !ends_with(path, "/."); -} - -static int on_request_recv(nghttp2_session *session, - http2_session_data *session_data, - http2_stream_data *stream_data) { - int fd; - nghttp2_nv hdrs[] = {MAKE_NV(":status", "200")}; - char *rel_path; - - if (!stream_data->request_path) { - if (error_reply(session, stream_data) != 0) { - return NGHTTP2_ERR_CALLBACK_FAILURE; - } - return 0; - } - fprintf(stderr, "%s GET %s\n", session_data->client_addr, - stream_data->request_path); - if (!check_path(stream_data->request_path)) { - if (error_reply(session, stream_data) != 0) { - return NGHTTP2_ERR_CALLBACK_FAILURE; - } - return 0; - } - for (rel_path = stream_data->request_path; *rel_path == '/'; ++rel_path) - ; - fd = open(rel_path, O_RDONLY); - if (fd == -1) { - if (error_reply(session, stream_data) != 0) { - return NGHTTP2_ERR_CALLBACK_FAILURE; - } - return 0; - } - stream_data->fd = fd; - - if (send_response(session, stream_data->stream_id, hdrs, ARRLEN(hdrs), fd) != - 0) { - close(fd); - return NGHTTP2_ERR_CALLBACK_FAILURE; - } - return 0; -} - -static int on_frame_recv_callback(nghttp2_session *session, - const nghttp2_frame *frame, void *user_data) { - http2_session_data *session_data = (http2_session_data *)user_data; - http2_stream_data *stream_data; - switch (frame->hd.type) { - case NGHTTP2_DATA: - case NGHTTP2_HEADERS: - /* Check that the client request has finished */ - if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) { - stream_data = - nghttp2_session_get_stream_user_data(session, frame->hd.stream_id); - /* For DATA and HEADERS frame, this callback may be called after - on_stream_close_callback. Check that stream still alive. */ - if (!stream_data) { - return 0; - } - return on_request_recv(session, session_data, stream_data); - } - break; - default: - break; - } - return 0; -} - -static int on_stream_close_callback(nghttp2_session *session, int32_t stream_id, - uint32_t error_code _U_, void *user_data) { - http2_session_data *session_data = (http2_session_data *)user_data; - http2_stream_data *stream_data; - - stream_data = nghttp2_session_get_stream_user_data(session, stream_id); - if (!stream_data) { - return 0; - } - remove_stream(session_data, stream_data); - delete_http2_stream_data(stream_data); - return 0; -} - -static void initialize_nghttp2_session(http2_session_data *session_data) { - nghttp2_session_callbacks *callbacks; - - nghttp2_session_callbacks_new(&callbacks); - - nghttp2_session_callbacks_set_send_callback(callbacks, send_callback); - - nghttp2_session_callbacks_set_on_frame_recv_callback(callbacks, - on_frame_recv_callback); - - nghttp2_session_callbacks_set_on_stream_close_callback( - callbacks, on_stream_close_callback); - - nghttp2_session_callbacks_set_on_header_callback(callbacks, - on_header_callback); - - nghttp2_session_callbacks_set_on_begin_headers_callback( - callbacks, on_begin_headers_callback); - - nghttp2_session_server_new(&session_data->session, callbacks, session_data); - - nghttp2_session_callbacks_del(callbacks); -} - -/* Send HTTP/2 client connection header, which includes 24 bytes - magic octets and SETTINGS frame */ -static int send_server_connection_header(http2_session_data *session_data) { - nghttp2_settings_entry iv[1] = { - {NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100}}; - int rv; - - rv = nghttp2_submit_settings(session_data->session, NGHTTP2_FLAG_NONE, iv, - ARRLEN(iv)); - if (rv != 0) { - warnx("Fatal error: %s", nghttp2_strerror(rv)); - return -1; - } - return 0; -} - -/* readcb for bufferevent after client connection header was - checked. */ -static void readcb(struct bufferevent *bev _U_, void *ptr) { - http2_session_data *session_data = (http2_session_data *)ptr; - if (session_recv(session_data) != 0) { - delete_http2_session_data(session_data); - return; - } -} - -/* writecb for bufferevent. To greaceful shutdown after sending or - receiving GOAWAY, we check the some conditions on the nghttp2 - library and output buffer of bufferevent. If it indicates we have - no business to this session, tear down the connection. If the - connection is not going to shutdown, we call session_send() to - process pending data in the output buffer. This is necessary - because we have a threshold on the buffer size to avoid too much - buffering. See send_callback(). */ -static void writecb(struct bufferevent *bev, void *ptr) { - http2_session_data *session_data = (http2_session_data *)ptr; - if (evbuffer_get_length(bufferevent_get_output(bev)) > 0) { - return; - } - if (nghttp2_session_want_read(session_data->session) == 0 && - nghttp2_session_want_write(session_data->session) == 0) { - delete_http2_session_data(session_data); - return; - } - if (session_send(session_data) != 0) { - delete_http2_session_data(session_data); - return; - } -} - -/* eventcb for bufferevent */ -static void eventcb(struct bufferevent *bev _U_, short events, void *ptr) { - http2_session_data *session_data = (http2_session_data *)ptr; - if (events & BEV_EVENT_CONNECTED) { - fprintf(stderr, "%s connected\n", session_data->client_addr); - - initialize_nghttp2_session(session_data); - - if (send_server_connection_header(session_data) != 0) { - delete_http2_session_data(session_data); - return; - } - - return; - } - if (events & BEV_EVENT_EOF) { - fprintf(stderr, "%s EOF\n", session_data->client_addr); - } else if (events & BEV_EVENT_ERROR) { - fprintf(stderr, "%s network error\n", session_data->client_addr); - } else if (events & BEV_EVENT_TIMEOUT) { - fprintf(stderr, "%s timeout\n", session_data->client_addr); - } - delete_http2_session_data(session_data); -} - -/* callback for evconnlistener */ -static void acceptcb(struct evconnlistener *listener _U_, int fd, - struct sockaddr *addr, int addrlen, void *arg) { - app_context *app_ctx = (app_context *)arg; - http2_session_data *session_data; - - session_data = create_http2_session_data(app_ctx, fd, addr, addrlen); - - bufferevent_setcb(session_data->bev, readcb, writecb, eventcb, session_data); -} - -static void start_listen(struct event_base *evbase, const char *service, - app_context *app_ctx) { - int rv; - struct addrinfo hints; - struct addrinfo *res, *rp; - - memset(&hints, 0, sizeof(hints)); - hints.ai_family = AF_UNSPEC; - hints.ai_socktype = SOCK_STREAM; - hints.ai_flags = AI_PASSIVE; -#ifdef AI_ADDRCONFIG - hints.ai_flags |= AI_ADDRCONFIG; -#endif /* AI_ADDRCONFIG */ - - rv = getaddrinfo(NULL, service, &hints, &res); - if (rv != 0) { - errx(1, NULL); - } - for (rp = res; rp; rp = rp->ai_next) { - struct evconnlistener *listener; - listener = evconnlistener_new_bind( - evbase, acceptcb, app_ctx, LEV_OPT_CLOSE_ON_FREE | LEV_OPT_REUSEABLE, - 16, rp->ai_addr, rp->ai_addrlen); - if (listener) { - freeaddrinfo(res); - - return; - } - } - errx(1, "Could not start listener"); -} - -static void initialize_app_context(app_context *app_ctx, SSL_CTX *ssl_ctx, - struct event_base *evbase) { - memset(app_ctx, 0, sizeof(app_context)); - app_ctx->ssl_ctx = ssl_ctx; - app_ctx->evbase = evbase; -} - -static void run(const char *service, const char *key_file, - const char *cert_file) { - SSL_CTX *ssl_ctx; - app_context app_ctx; - struct event_base *evbase; - - ssl_ctx = create_ssl_ctx(key_file, cert_file); - evbase = event_base_new(); - initialize_app_context(&app_ctx, ssl_ctx, evbase); - start_listen(evbase, service, &app_ctx); - - event_base_loop(evbase, 0); - - event_base_free(evbase); - SSL_CTX_free(ssl_ctx); -} - -int main(int argc, char **argv) { - struct sigaction act; - - if (argc < 4) { - fprintf(stderr, "Usage: libevent-server PORT KEY_FILE CERT_FILE\n"); - exit(EXIT_FAILURE); - } - - memset(&act, 0, sizeof(struct sigaction)); - act.sa_handler = SIG_IGN; - sigaction(SIGPIPE, &act, NULL); - - OPENSSL_config(NULL); - OpenSSL_add_all_algorithms(); - SSL_load_error_strings(); - SSL_library_init(); - - run(argv[1], argv[2], argv[3]); - return 0; -} diff --git a/examples/tiny-nghttpd.c b/examples/tiny-nghttpd.c deleted file mode 100644 index 3410e9d..0000000 --- a/examples/tiny-nghttpd.c +++ /dev/null @@ -1,1342 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2014 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -/* - * This program is intended to measure library performance, avoiding - * overhead of underlying I/O library (e.g., libevent, Boost ASIO). - */ -#define _GNU_SOURCE - -#ifdef HAVE_CONFIG_H -#include -#endif /* HAVE_CONFIG_H */ - -#include -#ifdef HAVE_SYS_SOCKET_H -#include -#endif /* HAVE_SYS_SOCKET_H */ -#include -#ifdef HAVE_FCNTL_H -#include -#endif /* HAVE_FCNTL_H */ -#ifdef HAVE_NETDB_H -#include -#endif /* HAVE_NETDB_H */ -#ifdef HAVE_NETINET_IN_H -#include -#endif /* HAVE_NETINET_IN_H */ -#include -#ifdef HAVE_UNISTD_H -#include -#endif /* HAVE_UNISTD_H */ -#include -#ifdef HAVE_TIME_H -#include -#endif /* HAVE_TIME_H */ -#include -#include -#include -#include -#include -#include -#include - -#include - -#define SERVER_NAME "tiny-nghttpd nghttp2/" NGHTTP2_VERSION - -#define MAKE_NV(name, value) \ - { \ - (uint8_t *)(name), (uint8_t *)(value), sizeof((name)) - 1, \ - sizeof((value)) - 1, NGHTTP2_NV_FLAG_NONE \ - } - -#define MAKE_NV2(name, value, valuelen) \ - { \ - (uint8_t *)(name), (uint8_t *)(value), sizeof((name)) - 1, (valuelen), \ - NGHTTP2_NV_FLAG_NONE \ - } - -#define array_size(a) (sizeof((a)) / sizeof((a)[0])) - -/* Returns the length of remaning data in buffer */ -#define io_buf_len(iobuf) ((iobuf)->last - (iobuf)->pos) -/* Returns the space buffer can still accept */ -#define io_buf_left(iobuf) ((iobuf)->end - (iobuf)->last) - -typedef struct { - /* beginning of buffer */ - uint8_t *begin; - /* one byte beyond the end of buffer */ - uint8_t *end; - /* next read/write position of buffer */ - uint8_t *pos; - /* one byte beyond last data of buffer */ - uint8_t *last; -} io_buf; - -typedef struct { - /* epoll fd */ - int epfd; -} io_loop; - -typedef struct stream { - struct stream *prev, *next; - /* mandatory header fields */ - char *method; - char *scheme; - char *authority; - char *path; - char *host; - /* region of response body in rawscrbuf */ - uint8_t *res_begin, *res_end; - /* io_buf wrapping rawscrbuf */ - io_buf scrbuf; - int64_t fileleft; - /* length of mandatory header fields */ - size_t methodlen; - size_t schemelen; - size_t authoritylen; - size_t pathlen; - size_t hostlen; - /* stream ID of this stream */ - int32_t stream_id; - /* fd for reading file */ - int filefd; - /* scratch buffer for this stream */ - uint8_t rawscrbuf[4096]; -} stream; - -typedef struct { int (*handler)(io_loop *, uint32_t, void *); } evhandle; - -typedef struct { - evhandle evhn; - nghttp2_session *session; - /* list of stream */ - stream strm_head; - /* pending library output */ - const uint8_t *cache; - /* io_buf wrapping rawoutbuf */ - io_buf buf; - /* length of cache */ - size_t cachelen; - /* client fd */ - int fd; - /* output buffer */ - uint8_t rawoutbuf[65536]; -} connection; - -typedef struct { - evhandle evhn; - /* listening fd */ - int fd; -} server; - -typedef struct { - evhandle evhn; - /* timerfd */ - int fd; -} timer; - -/* document root */ -const char *docroot; -/* length of docroot */ -size_t docrootlen; - -nghttp2_session_callbacks *shared_callbacks; - -static int handle_accept(io_loop *loop, uint32_t events, void *ptr); -static int handle_connection(io_loop *loop, uint32_t events, void *ptr); -static int handle_timer(io_loop *loop, uint32_t events, void *ptr); - -static void io_buf_init(io_buf *buf, uint8_t *underlying, size_t len) { - buf->begin = buf->pos = buf->last = underlying; - buf->end = underlying + len; -} - -static void io_buf_add(io_buf *buf, const void *src, size_t len) { - memcpy(buf->last, src, len); - buf->last += len; -} - -static char *io_buf_add_str(io_buf *buf, const void *src, size_t len) { - uint8_t *start = buf->last; - - memcpy(buf->last, src, len); - buf->last += len; - *buf->last++ = '\0'; - - return (char *)start; -} - -static int memeq(const void *a, const void *b, size_t n) { - return memcmp(a, b, n) == 0; -} - -#define streq(A, B, N) ((sizeof((A)) - 1) == (N) && memeq((A), (B), (N))) - -typedef enum { - NGHTTP2_TOKEN__AUTHORITY, - NGHTTP2_TOKEN__METHOD, - NGHTTP2_TOKEN__PATH, - NGHTTP2_TOKEN__SCHEME, - NGHTTP2_TOKEN_HOST -} nghttp2_token; - -/* Inspired by h2o header lookup. https://github.com/h2o/h2o */ -static int lookup_token(const uint8_t *name, size_t namelen) { - switch (namelen) { - case 5: - switch (name[namelen - 1]) { - case 'h': - if (streq(":pat", name, 4)) { - return NGHTTP2_TOKEN__PATH; - } - break; - } - break; - case 7: - switch (name[namelen - 1]) { - case 'd': - if (streq(":metho", name, 6)) { - return NGHTTP2_TOKEN__METHOD; - } - break; - case 'e': - if (streq(":schem", name, 6)) { - return NGHTTP2_TOKEN__SCHEME; - } - break; - } - break; - case 10: - switch (name[namelen - 1]) { - case 'y': - if (streq(":authorit", name, 9)) { - return NGHTTP2_TOKEN__AUTHORITY; - } - break; - } - break; - } - return -1; -} - -static char *cpydig(char *buf, int n, size_t len) { - char *p; - - p = buf + len - 1; - do { - *p-- = (n % 10) + '0'; - n /= 10; - } while (p >= buf); - - return buf + len; -} - -static const char *MONTH[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", - "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; -static const char *DAY_OF_WEEK[] = {"Sun", "Mon", "Tue", "Wed", - "Thu", "Fri", "Sat"}; - -static size_t http_date(char *buf, time_t t) { - struct tm tms; - char *p = buf; - - if (gmtime_r(&t, &tms) == NULL) { - return 0; - } - - /* Sat, 27 Sep 2014 06:31:15 GMT */ - - memcpy(p, DAY_OF_WEEK[tms.tm_wday], 3); - p += 3; - *p++ = ','; - *p++ = ' '; - p = cpydig(p, tms.tm_mday, 2); - *p++ = ' '; - memcpy(p, MONTH[tms.tm_mon], 3); - p += 3; - *p++ = ' '; - p = cpydig(p, tms.tm_year + 1900, 4); - *p++ = ' '; - p = cpydig(p, tms.tm_hour, 2); - *p++ = ':'; - p = cpydig(p, tms.tm_min, 2); - *p++ = ':'; - p = cpydig(p, tms.tm_sec, 2); - memcpy(p, " GMT", 4); - p += 4; - - return p - buf; -} - -static char date[29]; -static char datelen; - -static void update_date() { datelen = http_date(date, time(NULL)); } - -static size_t utos(char *buf, size_t len, uint64_t n) { - size_t nwrite = 0; - uint64_t t = n; - - if (len == 0) { - return 0; - } - - if (n == 0) { - buf[0] = '0'; - return 1; - } - - for (; t; t /= 10, ++nwrite) - ; - - if (nwrite > len) { - return 0; - } - - buf += nwrite - 1; - do { - *buf-- = (n % 10) + '0'; - n /= 10; - } while (n); - - return nwrite; -} - -static void print_errno(const char *prefix, int errnum) { - char buf[1024]; - char *errmsg; - - errmsg = strerror_r(errnum, buf, sizeof(buf)); - - fprintf(stderr, "%s: %s\n", prefix, errmsg); -} - -#define list_insert(head, elem) \ - do { \ - (elem)->prev = (head); \ - (elem)->next = (head)->next; \ - \ - if ((head)->next) { \ - (head)->next->prev = (elem); \ - } \ - (head)->next = (elem); \ - } while (0) - -#define list_remove(elem) \ - do { \ - (elem)->prev->next = (elem)->next; \ - if ((elem)->next) { \ - (elem)->next->prev = (elem)->prev; \ - } \ - } while (0) - -static stream *stream_new(int32_t stream_id, connection *conn) { - stream *strm; - - strm = malloc(sizeof(stream)); - - strm->prev = strm->next = NULL; - strm->method = NULL; - strm->scheme = NULL; - strm->authority = NULL; - strm->path = NULL; - strm->host = NULL; - strm->res_begin = NULL; - strm->res_end = NULL; - strm->methodlen = 0; - strm->schemelen = 0; - strm->authoritylen = 0; - strm->pathlen = 0; - strm->hostlen = 0; - strm->stream_id = stream_id; - strm->filefd = -1; - strm->fileleft = 0; - - list_insert(&conn->strm_head, strm); - - io_buf_init(&strm->scrbuf, strm->rawscrbuf, sizeof(strm->rawscrbuf)); - - return strm; -} - -static void stream_del(stream *strm) { - list_remove(strm); - - if (strm->filefd != -1) { - close(strm->filefd); - } - - free(strm); -} - -static connection *connection_new(int fd) { - connection *conn; - int rv; - - conn = malloc(sizeof(connection)); - - rv = nghttp2_session_server_new(&conn->session, shared_callbacks, conn); - - if (rv != 0) { - goto cleanup; - } - - conn->fd = fd; - conn->cache = NULL; - conn->cachelen = 0; - io_buf_init(&conn->buf, conn->rawoutbuf, sizeof(conn->rawoutbuf)); - conn->evhn.handler = handle_connection; - conn->strm_head.next = NULL; - - return conn; - -cleanup: - free(conn); - return NULL; -} - -static void connection_del(connection *conn) { - stream *strm; - - nghttp2_session_del(conn->session); - shutdown(conn->fd, SHUT_WR); - close(conn->fd); - - strm = conn->strm_head.next; - while (strm) { - stream *next_strm = strm->next; - - stream_del(strm); - strm = next_strm; - } - - free(conn); -} - -static int connection_start(connection *conn) { - nghttp2_settings_entry iv = {NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100}; - int rv; - - rv = nghttp2_submit_settings(conn->session, NGHTTP2_FLAG_NONE, &iv, 1); - - if (rv != 0) { - return -1; - } - - return 0; -} - -static int server_init(server *serv, const char *node, const char *service) { - int rv; - struct addrinfo hints; - struct addrinfo *res, *rp; - int fd; - int on = 1; - socklen_t optlen = sizeof(on); - - memset(&hints, 0, sizeof(hints)); - hints.ai_family = AF_UNSPEC; - hints.ai_socktype = SOCK_STREAM; - hints.ai_protocol = 0; - hints.ai_flags = AI_PASSIVE | AI_ADDRCONFIG; - - rv = getaddrinfo(node, service, &hints, &res); - - if (rv != 0) { - fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv)); - return -1; - } - - for (rp = res; rp; rp = rp->ai_next) { - fd = - socket(rp->ai_family, rp->ai_socktype | SOCK_NONBLOCK, rp->ai_protocol); - - if (fd == -1) { - continue; - } - - rv = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, optlen); - - if (rv == -1) { - print_errno("setsockopt", errno); - } - - if (bind(fd, rp->ai_addr, rp->ai_addrlen) != 0) { - close(fd); - continue; - } - - if (listen(fd, 65536) != 0) { - close(fd); - continue; - } - - break; - } - - freeaddrinfo(res); - - if (!rp) { - fprintf(stderr, "No address to bind\n"); - return -1; - } - - serv->fd = fd; - serv->evhn.handler = handle_accept; - - return 0; -} - -static int timer_init(timer *tmr) { - int fd; - struct itimerspec timerval = {{1, 0}, {1, 0}}; - - fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK); - if (fd == -1) { - print_errno("timerfd_create", errno); - return -1; - } - - if (timerfd_settime(fd, 0, &timerval, NULL) != 0) { - print_errno("timerfd_settime", errno); - return -1; - } - - tmr->fd = fd; - tmr->evhn.handler = handle_timer; - - return 0; -} - -static int io_loop_init(io_loop *loop) { - int epfd; - - epfd = epoll_create1(0); - - if (epfd == -1) { - print_errno("epoll_create", errno); - return -1; - } - - loop->epfd = epfd; - - return 0; -} - -static int io_loop_ctl(io_loop *loop, int op, int fd, uint32_t events, - void *ptr) { - int rv; - struct epoll_event ev; - - ev.events = events; - ev.data.ptr = ptr; - - rv = epoll_ctl(loop->epfd, op, fd, &ev); - - if (rv != 0) { - print_errno("epoll_ctl", errno); - return -1; - } - - return 0; -} - -static int io_loop_add(io_loop *loop, int fd, uint32_t events, void *ptr) { - return io_loop_ctl(loop, EPOLL_CTL_ADD, fd, events, ptr); -} - -static int io_loop_mod(io_loop *loop, int fd, uint32_t events, void *ptr) { - return io_loop_ctl(loop, EPOLL_CTL_MOD, fd, events, ptr); -} - -static int io_loop_run(io_loop *loop, server *serv _U_) { -#define NUM_EVENTS 1024 - struct epoll_event events[NUM_EVENTS]; - - for (;;) { - int nev; - evhandle *evhn; - struct epoll_event *ev, *end; - - while ((nev = epoll_wait(loop->epfd, events, NUM_EVENTS, -1)) == -1 && - errno == EINTR) - ; - - if (nev == -1) { - print_errno("epoll_wait", errno); - return -1; - } - - for (ev = events, end = events + nev; ev != end; ++ev) { - evhn = ev->data.ptr; - evhn->handler(loop, ev->events, ev->data.ptr); - } - } -} - -static int handle_timer(io_loop *loop _U_, uint32_t events _U_, void *ptr) { - timer *tmr = ptr; - int64_t buf; - ssize_t nread; - - while ((nread = read(tmr->fd, &buf, sizeof(buf))) == -1 && errno == EINTR) - ; - - assert(nread == sizeof(buf)); - - update_date(); - - return 0; -} - -static int handle_accept(io_loop *loop, uint32_t events _U_, void *ptr) { - int acfd; - server *serv = ptr; - int on = 1; - socklen_t optlen = sizeof(on); - int rv; - - for (;;) { - connection *conn; - - while ((acfd = accept4(serv->fd, NULL, NULL, SOCK_NONBLOCK)) == -1 && - errno == EINTR) - ; - - if (acfd == -1) { - switch (errno) { - case ENETDOWN: - case EPROTO: - case ENOPROTOOPT: - case EHOSTDOWN: - case ENONET: - case EHOSTUNREACH: - case EOPNOTSUPP: - case ENETUNREACH: - continue; - } - return 0; - } - - rv = setsockopt(acfd, IPPROTO_TCP, TCP_NODELAY, &on, optlen); - - if (rv == -1) { - print_errno("setsockopt", errno); - } - - conn = connection_new(acfd); - - if (conn == NULL) { - close(acfd); - continue; - } - - if (connection_start(conn) != 0 || - io_loop_add(loop, acfd, EPOLLIN | EPOLLOUT, conn) != 0) { - connection_del(conn); - } - } -} - -static void stream_error(connection *conn, int32_t stream_id, - uint32_t error_code) { - nghttp2_submit_rst_stream(conn->session, NGHTTP2_FLAG_NONE, stream_id, - error_code); -} - -static int send_data_callback(nghttp2_session *session _U_, - nghttp2_frame *frame, const uint8_t *framehd, - size_t length, nghttp2_data_source *source, - void *user_data) { - connection *conn = user_data; - uint8_t *p = conn->buf.last; - stream *strm = source->ptr; - - /* We never use padding in this program */ - assert(frame->data.padlen == 0); - - if ((size_t)io_buf_left(&conn->buf) < 9 + frame->hd.length) { - return NGHTTP2_ERR_WOULDBLOCK; - } - - memcpy(p, framehd, 9); - p += 9; - - while (length) { - ssize_t nread; - while ((nread = read(strm->filefd, p, length)) == -1 && errno == EINTR) - ; - if (nread == -1) { - return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; - } - - length -= nread; - p += nread; - } - - conn->buf.last = p; - - return 0; -} - -static ssize_t fd_read_callback(nghttp2_session *session _U_, - int32_t stream_id _U_, uint8_t *buf _U_, - size_t length, uint32_t *data_flags, - nghttp2_data_source *source, - void *user_data _U_) { - stream *strm = source->ptr; - ssize_t nread = - (int64_t)length < strm->fileleft ? (int64_t)length : strm->fileleft; - - *data_flags |= NGHTTP2_DATA_FLAG_NO_COPY; - - strm->fileleft -= nread; - if (nread == 0 || strm->fileleft == 0) { - if (strm->fileleft != 0) { - return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; - } - *data_flags |= NGHTTP2_DATA_FLAG_EOF; - } - return nread; -} - -static ssize_t resbuf_read_callback(nghttp2_session *session _U_, - int32_t stream_id _U_, uint8_t *buf, - size_t length, uint32_t *data_flags, - nghttp2_data_source *source, - void *user_data _U_) { - stream *strm = source->ptr; - size_t left = strm->res_end - strm->res_begin; - size_t nwrite = length < left ? length : left; - - memcpy(buf, strm->res_begin, nwrite); - strm->res_begin += nwrite; - - if (strm->res_begin == strm->res_end) { - *data_flags |= NGHTTP2_DATA_FLAG_EOF; - } - - return nwrite; -} - -static int hex_digit(char c) { - return ('0' <= c && c <= '9') || ('A' <= c && c <= 'F') || - ('a' <= c && c <= 'f'); -} - -static unsigned int hex_to_uint(char c) { - if (c <= '9') { - return c - '0'; - } - - if (c <= 'F') { - return c - 'A' + 10; - } - - return c - 'a' + 10; -} - -static void percent_decode(io_buf *buf, const char *s) { - for (; *s; ++s) { - if (*s == '?' || *s == '#') { - break; - } - - if (*s == '%' && hex_digit(*(s + 1)) && hex_digit(*(s + 2))) { - *buf->last++ = (hex_to_uint(*(s + 1)) << 4) + hex_to_uint(*(s + 2)); - s += 2; - continue; - } - - *buf->last++ = *s; - } -} - -static int check_path(const char *path, size_t len) { - return path[0] == '/' && strchr(path, '\\') == NULL && - strstr(path, "/../") == NULL && strstr(path, "/./") == NULL && - (len < 3 || memcmp(path + len - 3, "/..", 3) != 0) && - (len < 2 || memcmp(path + len - 2, "/.", 2) != 0); -} - -static int make_path(io_buf *pathbuf, const char *req, size_t reqlen _U_) { - uint8_t *p; - - if (req[0] != '/') { - return -1; - } - - if (docrootlen + strlen(req) + sizeof("index.html") > - (size_t)io_buf_left(pathbuf)) { - return -1; - } - - io_buf_add(pathbuf, docroot, docrootlen); - - p = pathbuf->last; - - percent_decode(pathbuf, req); - - if (*(pathbuf->last - 1) == '/') { - io_buf_add(pathbuf, "index.html", sizeof("index.html") - 1); - } - - *pathbuf->last++ = '\0'; - - if (!check_path((const char *)p, pathbuf->last - 1 - p)) { - - return -1; - } - - return 0; -} - -static int status_response(stream *strm, connection *conn, - const char *status_code) { - int rv; - size_t status_codelen = strlen(status_code); - char contentlength[19]; - size_t contentlengthlen; - size_t reslen; - nghttp2_data_provider prd, *prdptr; - nghttp2_nv nva[5] = { - MAKE_NV(":status", ""), MAKE_NV("server", SERVER_NAME), - MAKE_NV2("date", date, datelen), MAKE_NV("content-length", ""), - }; - size_t nvlen = 3; - - nva[0].value = (uint8_t *)status_code; - nva[0].valuelen = strlen(status_code); - -#define BODY1 "" -#define BODY2 "

      " -#define BODY3 "

      " - - reslen = sizeof(BODY1) - 1 + sizeof(BODY2) - 1 + sizeof(BODY3) - 1 + - status_codelen * 2; - - if ((size_t)io_buf_left(&strm->scrbuf) < reslen) { - contentlength[0] = '0'; - contentlengthlen = 1; - prdptr = NULL; - } else { - contentlengthlen = utos(contentlength, sizeof(contentlength), reslen); - - strm->res_begin = strm->scrbuf.last; - - io_buf_add(&strm->scrbuf, BODY1, sizeof(BODY1) - 1); - io_buf_add(&strm->scrbuf, status_code, strlen(status_code)); - io_buf_add(&strm->scrbuf, BODY2, sizeof(BODY2) - 1); - io_buf_add(&strm->scrbuf, status_code, strlen(status_code)); - io_buf_add(&strm->scrbuf, BODY3, sizeof(BODY3) - 1); - - strm->res_end = strm->scrbuf.last; - prdptr = &prd; - } - - nva[nvlen].value = (uint8_t *)contentlength; - nva[nvlen].valuelen = contentlengthlen; - - ++nvlen; - - prd.source.ptr = strm; - prd.read_callback = resbuf_read_callback; - - rv = nghttp2_submit_response(conn->session, strm->stream_id, nva, nvlen, - prdptr); - if (rv != 0) { - return -1; - } - - return 0; -} - -static int redirect_response(stream *strm, connection *conn) { - int rv; - size_t locationlen; - nghttp2_nv nva[5] = { - MAKE_NV(":status", "301"), MAKE_NV("server", SERVER_NAME), - MAKE_NV2("date", date, datelen), MAKE_NV("content-length", "0"), - MAKE_NV("location", ""), - }; - - /* + 1 for trailing '/' */ - locationlen = strm->schemelen + 3 + strm->hostlen + strm->pathlen + 1; - if ((size_t)io_buf_left(&strm->scrbuf) < locationlen) { - return -1; - } - - nva[4].value = strm->scrbuf.last; - nva[4].valuelen = locationlen; - - io_buf_add(&strm->scrbuf, strm->scheme, strm->schemelen); - io_buf_add(&strm->scrbuf, "://", 3); - io_buf_add(&strm->scrbuf, strm->host, strm->hostlen); - io_buf_add(&strm->scrbuf, strm->path, strm->pathlen); - *strm->scrbuf.last++ = '/'; - - rv = nghttp2_submit_response(conn->session, strm->stream_id, nva, - array_size(nva), NULL); - - if (rv != 0) { - return -1; - } - - return 0; -} - -static int process_request(stream *strm, connection *conn) { - int fd; - struct stat stbuf; - int rv; - nghttp2_data_provider prd; - char lastmod[32]; - size_t lastmodlen; - char contentlength[19]; - size_t contentlengthlen; - char path[1024]; - io_buf pathbuf; - nghttp2_nv nva[5] = { - MAKE_NV(":status", "200"), MAKE_NV("server", SERVER_NAME), - MAKE_NV2("date", date, datelen), MAKE_NV("content-length", ""), - }; - size_t nvlen = 3; - - io_buf_init(&pathbuf, (uint8_t *)path, sizeof(path)); - - rv = make_path(&pathbuf, strm->path, strm->pathlen); - - if (rv != 0) { - return status_response(strm, conn, "400"); - } - - fd = open(path, O_RDONLY); - - if (fd == -1) { - return status_response(strm, conn, "404"); - } - - strm->filefd = fd; - - rv = fstat(fd, &stbuf); - - if (rv == -1) { - return status_response(strm, conn, "404"); - } - - if (stbuf.st_mode & S_IFDIR) { - return redirect_response(strm, conn); - } - - prd.source.ptr = strm; - prd.read_callback = fd_read_callback; - - strm->fileleft = stbuf.st_size; - - lastmodlen = http_date(lastmod, stbuf.st_mtim.tv_sec); - contentlengthlen = utos(contentlength, sizeof(contentlength), stbuf.st_size); - - nva[nvlen].value = (uint8_t *)contentlength; - nva[nvlen].valuelen = contentlengthlen; - - ++nvlen; - - if (lastmodlen) { - nva[nvlen].name = (uint8_t *)"last-modified"; - nva[nvlen].namelen = sizeof("last-modified") - 1; - nva[nvlen].value = (uint8_t *)lastmod; - nva[nvlen].valuelen = lastmodlen; - nva[nvlen].flags = NGHTTP2_NV_FLAG_NONE; - - ++nvlen; - } - - rv = - nghttp2_submit_response(conn->session, strm->stream_id, nva, nvlen, &prd); - if (rv != 0) { - return -1; - } - - return 0; -} - -static int on_begin_headers_callback(nghttp2_session *session, - const nghttp2_frame *frame, - void *user_data) { - connection *conn = user_data; - stream *strm; - - if (frame->hd.type != NGHTTP2_HEADERS || - frame->headers.cat != NGHTTP2_HCAT_REQUEST) { - return 0; - } - - strm = stream_new(frame->hd.stream_id, conn); - - if (!strm) { - nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, frame->hd.stream_id, - NGHTTP2_INTERNAL_ERROR); - return 0; - } - - nghttp2_session_set_stream_user_data(session, frame->hd.stream_id, strm); - - return 0; -} - -static int on_header_callback(nghttp2_session *session, - const nghttp2_frame *frame, const uint8_t *name, - size_t namelen, const uint8_t *value, - size_t valuelen, uint8_t flags _U_, - void *user_data _U_) { - stream *strm; - - if (frame->hd.type != NGHTTP2_HEADERS || - frame->headers.cat != NGHTTP2_HCAT_REQUEST) { - return 0; - } - - strm = nghttp2_session_get_stream_user_data(session, frame->hd.stream_id); - - if (!strm) { - return 0; - } - - switch (lookup_token(name, namelen)) { - case NGHTTP2_TOKEN__METHOD: - strm->method = io_buf_add_str(&strm->scrbuf, value, valuelen); - if (!strm->method) { - return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; - } - strm->methodlen = valuelen; - break; - case NGHTTP2_TOKEN__SCHEME: - strm->scheme = io_buf_add_str(&strm->scrbuf, value, valuelen); - if (!strm->scheme) { - return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; - } - strm->schemelen = valuelen; - break; - case NGHTTP2_TOKEN__AUTHORITY: - strm->authority = io_buf_add_str(&strm->scrbuf, value, valuelen); - if (!strm->authority) { - return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; - } - strm->authoritylen = valuelen; - break; - case NGHTTP2_TOKEN__PATH: - strm->path = io_buf_add_str(&strm->scrbuf, value, valuelen); - if (!strm->path) { - return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; - } - strm->pathlen = valuelen; - break; - case NGHTTP2_TOKEN_HOST: - strm->host = io_buf_add_str(&strm->scrbuf, value, valuelen); - if (!strm->host) { - return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; - } - strm->hostlen = valuelen; - break; - } - - return 0; -} - -static int on_frame_recv_callback(nghttp2_session *session, - const nghttp2_frame *frame, void *user_data) { - connection *conn = user_data; - stream *strm; - - if (frame->hd.type != NGHTTP2_HEADERS || - frame->headers.cat != NGHTTP2_HCAT_REQUEST) { - return 0; - } - - strm = nghttp2_session_get_stream_user_data(session, frame->hd.stream_id); - - if (!strm) { - return 0; - } - - if (!strm->host) { - strm->host = strm->authority; - strm->hostlen = strm->authoritylen; - } - - if (process_request(strm, conn) != 0) { - stream_error(conn, strm->stream_id, NGHTTP2_INTERNAL_ERROR); - return 0; - } - - return 0; -} - -static int on_stream_close_callback(nghttp2_session *session, int32_t stream_id, - uint32_t error_code _U_, - void *user_data _U_) { - stream *strm; - - strm = nghttp2_session_get_stream_user_data(session, stream_id); - - if (!strm) { - return 0; - } - - stream_del(strm); - - return 0; -} - -static int on_frame_not_send_callback(nghttp2_session *session _U_, - const nghttp2_frame *frame, - int lib_error_code _U_, void *user_data) { - connection *conn = user_data; - - if (frame->hd.type != NGHTTP2_HEADERS) { - return 0; - } - - /* Issue RST_STREAM so that stream does not hang around. */ - nghttp2_submit_rst_stream(conn->session, NGHTTP2_FLAG_NONE, - frame->hd.stream_id, NGHTTP2_INTERNAL_ERROR); - - return 0; -} - -static int do_read(connection *conn) { - uint8_t buf[32768]; - - for (;;) { - ssize_t nread; - ssize_t nproc; - - while ((nread = read(conn->fd, buf, sizeof(buf))) == -1 && errno == EINTR) - ; - if (nread == -1) { - if (errno == EAGAIN || errno == EWOULDBLOCK) { - return 0; - } - - return -1; - } - - if (nread == 0) { - return -1; - } - - nproc = nghttp2_session_mem_recv(conn->session, buf, nread); - - if (nproc < 0) { - return -1; - } - } -} - -static int do_write(connection *conn) { - for (;;) { - if (io_buf_len(&conn->buf)) { - ssize_t nwrite; - while ((nwrite = write(conn->fd, conn->buf.pos, - io_buf_len(&conn->buf))) == -1 && - errno == EINTR) - ; - if (nwrite == -1) { - if (errno == EAGAIN || errno == EWOULDBLOCK) { - return 0; - } - return -1; - } - - conn->buf.pos += nwrite; - - if (io_buf_len(&conn->buf)) { - return 0; - } - - io_buf_init(&conn->buf, conn->rawoutbuf, sizeof(conn->rawoutbuf)); - } - - if (conn->cache) { - io_buf_add(&conn->buf, conn->cache, conn->cachelen); - conn->cache = NULL; - conn->cachelen = 0; - } - - for (;;) { - ssize_t n; - const uint8_t *b; - - n = nghttp2_session_mem_send(conn->session, &b); - - if (n < 0) { - return -1; - } - - if (n == 0) { - if (io_buf_len(&conn->buf) == 0) { - return 0; - } - break; - } - - if (io_buf_left(&conn->buf) < n) { - conn->cache = b; - conn->cachelen = n; - break; - } - - io_buf_add(&conn->buf, b, n); - } - } -} - -static int handle_connection(io_loop *loop, uint32_t events, void *ptr) { - connection *conn = ptr; - int rv; - uint32_t nextev = 0; - - if (events & (EPOLLHUP | EPOLLERR)) { - goto cleanup; - } - - if (events & EPOLLIN) { - rv = do_read(conn); - - if (rv != 0) { - goto cleanup; - } - } - - rv = do_write(conn); - - if (rv != 0) { - goto cleanup; - } - - if (nghttp2_session_want_read(conn->session)) { - nextev |= EPOLLIN; - } - - if (io_buf_len(&conn->buf) || nghttp2_session_want_write(conn->session)) { - nextev |= EPOLLOUT; - } - - if (!nextev) { - goto cleanup; - } - - io_loop_mod(loop, conn->fd, nextev, conn); - - return 0; - -cleanup: - connection_del(conn); - - return 0; -} - -int main(int argc, char **argv) { - int rv; - server serv; - timer tmr; - io_loop loop; - struct sigaction act; - const char *address; - const char *service; - - if (argc < 4) { - fprintf(stderr, "Usage: tiny-nghttpd
      \n"); - exit(EXIT_FAILURE); - } - - address = argv[1]; - service = argv[2]; - docroot = argv[3]; - docrootlen = strlen(docroot); - - memset(&act, 0, sizeof(act)); - act.sa_handler = SIG_IGN; - sigaction(SIGPIPE, &act, NULL); - - rv = server_init(&serv, address, service); - - if (rv != 0) { - exit(EXIT_FAILURE); - } - - rv = timer_init(&tmr); - - if (rv != 0) { - exit(EXIT_FAILURE); - } - - rv = io_loop_init(&loop); - - if (rv != 0) { - exit(EXIT_FAILURE); - } - - rv = nghttp2_session_callbacks_new(&shared_callbacks); - if (rv != 0) { - fprintf(stderr, "nghttp2_session_callbacks_new: %s", nghttp2_strerror(rv)); - exit(EXIT_FAILURE); - } - - nghttp2_session_callbacks_set_on_begin_headers_callback( - shared_callbacks, on_begin_headers_callback); - nghttp2_session_callbacks_set_on_header_callback(shared_callbacks, - on_header_callback); - nghttp2_session_callbacks_set_on_frame_recv_callback(shared_callbacks, - on_frame_recv_callback); - nghttp2_session_callbacks_set_on_stream_close_callback( - shared_callbacks, on_stream_close_callback); - nghttp2_session_callbacks_set_on_frame_not_send_callback( - shared_callbacks, on_frame_not_send_callback); - nghttp2_session_callbacks_set_send_data_callback(shared_callbacks, - send_data_callback); - - rv = io_loop_add(&loop, serv.fd, EPOLLIN, &serv); - - if (rv != 0) { - exit(EXIT_FAILURE); - } - - rv = io_loop_add(&loop, tmr.fd, EPOLLIN, &tmr); - - if (rv != 0) { - exit(EXIT_FAILURE); - } - - update_date(); - - io_loop_run(&loop, &serv); - - nghttp2_session_callbacks_del(shared_callbacks); - - return 0; -} diff --git a/install-sh b/install-sh deleted file mode 100755 index 377bb86..0000000 --- a/install-sh +++ /dev/null @@ -1,527 +0,0 @@ -#!/bin/sh -# install - install a program, script, or datafile - -scriptversion=2011-11-20.07; # UTC - -# This originates from X11R5 (mit/util/scripts/install.sh), which was -# later released in X11R6 (xc/config/util/install.sh) with the -# following copyright and license. -# -# Copyright (C) 1994 X Consortium -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to -# deal in the Software without restriction, including without limitation the -# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -# sell copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN -# AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC- -# TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# -# Except as contained in this notice, the name of the X Consortium shall not -# be used in advertising or otherwise to promote the sale, use or other deal- -# ings in this Software without prior written authorization from the X Consor- -# tium. -# -# -# FSF changes to this file are in the public domain. -# -# Calling this script install-sh is preferred over install.sh, to prevent -# 'make' implicit rules from creating a file called install from it -# when there is no Makefile. -# -# This script is compatible with the BSD install script, but was written -# from scratch. - -nl=' -' -IFS=" "" $nl" - -# set DOITPROG to echo to test this script - -# Don't use :- since 4.3BSD and earlier shells don't like it. -doit=${DOITPROG-} -if test -z "$doit"; then - doit_exec=exec -else - doit_exec=$doit -fi - -# Put in absolute file names if you don't have them in your path; -# or use environment vars. - -chgrpprog=${CHGRPPROG-chgrp} -chmodprog=${CHMODPROG-chmod} -chownprog=${CHOWNPROG-chown} -cmpprog=${CMPPROG-cmp} -cpprog=${CPPROG-cp} -mkdirprog=${MKDIRPROG-mkdir} -mvprog=${MVPROG-mv} -rmprog=${RMPROG-rm} -stripprog=${STRIPPROG-strip} - -posix_glob='?' -initialize_posix_glob=' - test "$posix_glob" != "?" || { - if (set -f) 2>/dev/null; then - posix_glob= - else - posix_glob=: - fi - } -' - -posix_mkdir= - -# Desired mode of installed file. -mode=0755 - -chgrpcmd= -chmodcmd=$chmodprog -chowncmd= -mvcmd=$mvprog -rmcmd="$rmprog -f" -stripcmd= - -src= -dst= -dir_arg= -dst_arg= - -copy_on_change=false -no_target_directory= - -usage="\ -Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE - or: $0 [OPTION]... SRCFILES... DIRECTORY - or: $0 [OPTION]... -t DIRECTORY SRCFILES... - or: $0 [OPTION]... -d DIRECTORIES... - -In the 1st form, copy SRCFILE to DSTFILE. -In the 2nd and 3rd, copy all SRCFILES to DIRECTORY. -In the 4th, create DIRECTORIES. - -Options: - --help display this help and exit. - --version display version info and exit. - - -c (ignored) - -C install only if different (preserve the last data modification time) - -d create directories instead of installing files. - -g GROUP $chgrpprog installed files to GROUP. - -m MODE $chmodprog installed files to MODE. - -o USER $chownprog installed files to USER. - -s $stripprog installed files. - -t DIRECTORY install into DIRECTORY. - -T report an error if DSTFILE is a directory. - -Environment variables override the default commands: - CHGRPPROG CHMODPROG CHOWNPROG CMPPROG CPPROG MKDIRPROG MVPROG - RMPROG STRIPPROG -" - -while test $# -ne 0; do - case $1 in - -c) ;; - - -C) copy_on_change=true;; - - -d) dir_arg=true;; - - -g) chgrpcmd="$chgrpprog $2" - shift;; - - --help) echo "$usage"; exit $?;; - - -m) mode=$2 - case $mode in - *' '* | *' '* | *' -'* | *'*'* | *'?'* | *'['*) - echo "$0: invalid mode: $mode" >&2 - exit 1;; - esac - shift;; - - -o) chowncmd="$chownprog $2" - shift;; - - -s) stripcmd=$stripprog;; - - -t) dst_arg=$2 - # Protect names problematic for 'test' and other utilities. - case $dst_arg in - -* | [=\(\)!]) dst_arg=./$dst_arg;; - esac - shift;; - - -T) no_target_directory=true;; - - --version) echo "$0 $scriptversion"; exit $?;; - - --) shift - break;; - - -*) echo "$0: invalid option: $1" >&2 - exit 1;; - - *) break;; - esac - shift -done - -if test $# -ne 0 && test -z "$dir_arg$dst_arg"; then - # When -d is used, all remaining arguments are directories to create. - # When -t is used, the destination is already specified. - # Otherwise, the last argument is the destination. Remove it from $@. - for arg - do - if test -n "$dst_arg"; then - # $@ is not empty: it contains at least $arg. - set fnord "$@" "$dst_arg" - shift # fnord - fi - shift # arg - dst_arg=$arg - # Protect names problematic for 'test' and other utilities. - case $dst_arg in - -* | [=\(\)!]) dst_arg=./$dst_arg;; - esac - done -fi - -if test $# -eq 0; then - if test -z "$dir_arg"; then - echo "$0: no input file specified." >&2 - exit 1 - fi - # It's OK to call 'install-sh -d' without argument. - # This can happen when creating conditional directories. - exit 0 -fi - -if test -z "$dir_arg"; then - do_exit='(exit $ret); exit $ret' - trap "ret=129; $do_exit" 1 - trap "ret=130; $do_exit" 2 - trap "ret=141; $do_exit" 13 - trap "ret=143; $do_exit" 15 - - # Set umask so as not to create temps with too-generous modes. - # However, 'strip' requires both read and write access to temps. - case $mode in - # Optimize common cases. - *644) cp_umask=133;; - *755) cp_umask=22;; - - *[0-7]) - if test -z "$stripcmd"; then - u_plus_rw= - else - u_plus_rw='% 200' - fi - cp_umask=`expr '(' 777 - $mode % 1000 ')' $u_plus_rw`;; - *) - if test -z "$stripcmd"; then - u_plus_rw= - else - u_plus_rw=,u+rw - fi - cp_umask=$mode$u_plus_rw;; - esac -fi - -for src -do - # Protect names problematic for 'test' and other utilities. - case $src in - -* | [=\(\)!]) src=./$src;; - esac - - if test -n "$dir_arg"; then - dst=$src - dstdir=$dst - test -d "$dstdir" - dstdir_status=$? - else - - # Waiting for this to be detected by the "$cpprog $src $dsttmp" command - # might cause directories to be created, which would be especially bad - # if $src (and thus $dsttmp) contains '*'. - if test ! -f "$src" && test ! -d "$src"; then - echo "$0: $src does not exist." >&2 - exit 1 - fi - - if test -z "$dst_arg"; then - echo "$0: no destination specified." >&2 - exit 1 - fi - dst=$dst_arg - - # If destination is a directory, append the input filename; won't work - # if double slashes aren't ignored. - if test -d "$dst"; then - if test -n "$no_target_directory"; then - echo "$0: $dst_arg: Is a directory" >&2 - exit 1 - fi - dstdir=$dst - dst=$dstdir/`basename "$src"` - dstdir_status=0 - else - # Prefer dirname, but fall back on a substitute if dirname fails. - dstdir=` - (dirname "$dst") 2>/dev/null || - expr X"$dst" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ - X"$dst" : 'X\(//\)[^/]' \| \ - X"$dst" : 'X\(//\)$' \| \ - X"$dst" : 'X\(/\)' \| . 2>/dev/null || - echo X"$dst" | - sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ - s//\1/ - q - } - /^X\(\/\/\)[^/].*/{ - s//\1/ - q - } - /^X\(\/\/\)$/{ - s//\1/ - q - } - /^X\(\/\).*/{ - s//\1/ - q - } - s/.*/./; q' - ` - - test -d "$dstdir" - dstdir_status=$? - fi - fi - - obsolete_mkdir_used=false - - if test $dstdir_status != 0; then - case $posix_mkdir in - '') - # Create intermediate dirs using mode 755 as modified by the umask. - # This is like FreeBSD 'install' as of 1997-10-28. - umask=`umask` - case $stripcmd.$umask in - # Optimize common cases. - *[2367][2367]) mkdir_umask=$umask;; - .*0[02][02] | .[02][02] | .[02]) mkdir_umask=22;; - - *[0-7]) - mkdir_umask=`expr $umask + 22 \ - - $umask % 100 % 40 + $umask % 20 \ - - $umask % 10 % 4 + $umask % 2 - `;; - *) mkdir_umask=$umask,go-w;; - esac - - # With -d, create the new directory with the user-specified mode. - # Otherwise, rely on $mkdir_umask. - if test -n "$dir_arg"; then - mkdir_mode=-m$mode - else - mkdir_mode= - fi - - posix_mkdir=false - case $umask in - *[123567][0-7][0-7]) - # POSIX mkdir -p sets u+wx bits regardless of umask, which - # is incompatible with FreeBSD 'install' when (umask & 300) != 0. - ;; - *) - tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$ - trap 'ret=$?; rmdir "$tmpdir/d" "$tmpdir" 2>/dev/null; exit $ret' 0 - - if (umask $mkdir_umask && - exec $mkdirprog $mkdir_mode -p -- "$tmpdir/d") >/dev/null 2>&1 - then - if test -z "$dir_arg" || { - # Check for POSIX incompatibilities with -m. - # HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or - # other-writable bit of parent directory when it shouldn't. - # FreeBSD 6.1 mkdir -m -p sets mode of existing directory. - ls_ld_tmpdir=`ls -ld "$tmpdir"` - case $ls_ld_tmpdir in - d????-?r-*) different_mode=700;; - d????-?--*) different_mode=755;; - *) false;; - esac && - $mkdirprog -m$different_mode -p -- "$tmpdir" && { - ls_ld_tmpdir_1=`ls -ld "$tmpdir"` - test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1" - } - } - then posix_mkdir=: - fi - rmdir "$tmpdir/d" "$tmpdir" - else - # Remove any dirs left behind by ancient mkdir implementations. - rmdir ./$mkdir_mode ./-p ./-- 2>/dev/null - fi - trap '' 0;; - esac;; - esac - - if - $posix_mkdir && ( - umask $mkdir_umask && - $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir" - ) - then : - else - - # The umask is ridiculous, or mkdir does not conform to POSIX, - # or it failed possibly due to a race condition. Create the - # directory the slow way, step by step, checking for races as we go. - - case $dstdir in - /*) prefix='/';; - [-=\(\)!]*) prefix='./';; - *) prefix='';; - esac - - eval "$initialize_posix_glob" - - oIFS=$IFS - IFS=/ - $posix_glob set -f - set fnord $dstdir - shift - $posix_glob set +f - IFS=$oIFS - - prefixes= - - for d - do - test X"$d" = X && continue - - prefix=$prefix$d - if test -d "$prefix"; then - prefixes= - else - if $posix_mkdir; then - (umask=$mkdir_umask && - $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir") && break - # Don't fail if two instances are running concurrently. - test -d "$prefix" || exit 1 - else - case $prefix in - *\'*) qprefix=`echo "$prefix" | sed "s/'/'\\\\\\\\''/g"`;; - *) qprefix=$prefix;; - esac - prefixes="$prefixes '$qprefix'" - fi - fi - prefix=$prefix/ - done - - if test -n "$prefixes"; then - # Don't fail if two instances are running concurrently. - (umask $mkdir_umask && - eval "\$doit_exec \$mkdirprog $prefixes") || - test -d "$dstdir" || exit 1 - obsolete_mkdir_used=true - fi - fi - fi - - if test -n "$dir_arg"; then - { test -z "$chowncmd" || $doit $chowncmd "$dst"; } && - { test -z "$chgrpcmd" || $doit $chgrpcmd "$dst"; } && - { test "$obsolete_mkdir_used$chowncmd$chgrpcmd" = false || - test -z "$chmodcmd" || $doit $chmodcmd $mode "$dst"; } || exit 1 - else - - # Make a couple of temp file names in the proper directory. - dsttmp=$dstdir/_inst.$$_ - rmtmp=$dstdir/_rm.$$_ - - # Trap to clean up those temp files at exit. - trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0 - - # Copy the file name to the temp name. - (umask $cp_umask && $doit_exec $cpprog "$src" "$dsttmp") && - - # and set any options; do chmod last to preserve setuid bits. - # - # If any of these fail, we abort the whole thing. If we want to - # ignore errors from any of these, just make sure not to ignore - # errors from the above "$doit $cpprog $src $dsttmp" command. - # - { test -z "$chowncmd" || $doit $chowncmd "$dsttmp"; } && - { test -z "$chgrpcmd" || $doit $chgrpcmd "$dsttmp"; } && - { test -z "$stripcmd" || $doit $stripcmd "$dsttmp"; } && - { test -z "$chmodcmd" || $doit $chmodcmd $mode "$dsttmp"; } && - - # If -C, don't bother to copy if it wouldn't change the file. - if $copy_on_change && - old=`LC_ALL=C ls -dlL "$dst" 2>/dev/null` && - new=`LC_ALL=C ls -dlL "$dsttmp" 2>/dev/null` && - - eval "$initialize_posix_glob" && - $posix_glob set -f && - set X $old && old=:$2:$4:$5:$6 && - set X $new && new=:$2:$4:$5:$6 && - $posix_glob set +f && - - test "$old" = "$new" && - $cmpprog "$dst" "$dsttmp" >/dev/null 2>&1 - then - rm -f "$dsttmp" - else - # Rename the file to the real destination. - $doit $mvcmd -f "$dsttmp" "$dst" 2>/dev/null || - - # The rename failed, perhaps because mv can't rename something else - # to itself, or perhaps because mv is so ancient that it does not - # support -f. - { - # Now remove or move aside any old file at destination location. - # We try this two ways since rm can't unlink itself on some - # systems and the destination file might be busy for other - # reasons. In this case, the final cleanup might fail but the new - # file should still install successfully. - { - test ! -f "$dst" || - $doit $rmcmd -f "$dst" 2>/dev/null || - { $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null && - { $doit $rmcmd -f "$rmtmp" 2>/dev/null; :; } - } || - { echo "$0: cannot unlink or rename $dst" >&2 - (exit 1); exit 1 - } - } && - - # Now rename the file to the real destination. - $doit $mvcmd "$dsttmp" "$dst" - } - fi || exit 1 - - trap '' 0 - fi -done - -# Local variables: -# eval: (add-hook 'write-file-hooks 'time-stamp) -# time-stamp-start: "scriptversion=" -# time-stamp-format: "%:y-%02m-%02d.%02H" -# time-stamp-time-zone: "UTC" -# time-stamp-end: "; # UTC" -# End: diff --git a/integration-tests/Makefile.am b/integration-tests/Makefile.am deleted file mode 100644 index f03cd78..0000000 --- a/integration-tests/Makefile.am +++ /dev/null @@ -1,43 +0,0 @@ -# nghttp2 - HTTP/2 C Library - -# Copyright (c) 2015 Tatsuhiro Tsujikawa - -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: - -# The above copyright notice and this permission notice shall be -# included in all copies or substantial portions of the Software. - -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -EXTRA_DIST = \ - nghttpx_http1_test.go \ - nghttpx_http2_test.go \ - nghttpx_spdy_test.go \ - server_tester.go \ - server.key \ - server.crt \ - alt-server.key \ - alt-server.crt \ - setenv - -.PHONY: itprep it - -itprep: - go get -d -v github.com/bradfitz/http2 - go get -d -v github.com/tatsuhiro-t/go-nghttp2 - go get -d -v golang.org/x/net/spdy - -it: - sh setenv go test -v diff --git a/integration-tests/Makefile.in b/integration-tests/Makefile.in deleted file mode 100644 index 1110f9a..0000000 --- a/integration-tests/Makefile.in +++ /dev/null @@ -1,537 +0,0 @@ -# Makefile.in generated by automake 1.14.1 from Makefile.am. -# @configure_input@ - -# Copyright (C) 1994-2013 Free Software Foundation, Inc. - -# This Makefile.in is free software; the Free Software Foundation -# gives unlimited permission to copy and/or distribute it, -# with or without modifications, as long as this notice is preserved. - -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY, to the extent permitted by law; without -# even the implied warranty of MERCHANTABILITY or FITNESS FOR A -# PARTICULAR PURPOSE. - -@SET_MAKE@ - -# nghttp2 - HTTP/2 C Library - -# Copyright (c) 2015 Tatsuhiro Tsujikawa - -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: - -# The above copyright notice and this permission notice shall be -# included in all copies or substantial portions of the Software. - -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -VPATH = @srcdir@ -am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)' -am__make_running_with_option = \ - case $${target_option-} in \ - ?) ;; \ - *) echo "am__make_running_with_option: internal error: invalid" \ - "target option '$${target_option-}' specified" >&2; \ - exit 1;; \ - esac; \ - has_opt=no; \ - sane_makeflags=$$MAKEFLAGS; \ - if $(am__is_gnu_make); then \ - sane_makeflags=$$MFLAGS; \ - else \ - case $$MAKEFLAGS in \ - *\\[\ \ ]*) \ - bs=\\; \ - sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ - | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ - esac; \ - fi; \ - skip_next=no; \ - strip_trailopt () \ - { \ - flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ - }; \ - for flg in $$sane_makeflags; do \ - test $$skip_next = yes && { skip_next=no; continue; }; \ - case $$flg in \ - *=*|--*) continue;; \ - -*I) strip_trailopt 'I'; skip_next=yes;; \ - -*I?*) strip_trailopt 'I';; \ - -*O) strip_trailopt 'O'; skip_next=yes;; \ - -*O?*) strip_trailopt 'O';; \ - -*l) strip_trailopt 'l'; skip_next=yes;; \ - -*l?*) strip_trailopt 'l';; \ - -[dEDm]) skip_next=yes;; \ - -[JT]) skip_next=yes;; \ - esac; \ - case $$flg in \ - *$$target_option*) has_opt=yes; break;; \ - esac; \ - done; \ - test $$has_opt = yes -am__make_dryrun = (target_option=n; $(am__make_running_with_option)) -am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) -pkgdatadir = $(datadir)/@PACKAGE@ -pkgincludedir = $(includedir)/@PACKAGE@ -pkglibdir = $(libdir)/@PACKAGE@ -pkglibexecdir = $(libexecdir)/@PACKAGE@ -am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd -install_sh_DATA = $(install_sh) -c -m 644 -install_sh_PROGRAM = $(install_sh) -c -install_sh_SCRIPT = $(install_sh) -c -INSTALL_HEADER = $(INSTALL_DATA) -transform = $(program_transform_name) -NORMAL_INSTALL = : -PRE_INSTALL = : -POST_INSTALL = : -NORMAL_UNINSTALL = : -PRE_UNINSTALL = : -POST_UNINSTALL = : -build_triplet = @build@ -host_triplet = @host@ -target_triplet = @target@ -subdir = integration-tests -DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \ - $(srcdir)/config.go.in $(srcdir)/setenv.in -ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 -am__aclocal_m4_deps = $(top_srcdir)/m4/ax_boost_asio.m4 \ - $(top_srcdir)/m4/ax_boost_base.m4 \ - $(top_srcdir)/m4/ax_boost_system.m4 \ - $(top_srcdir)/m4/ax_boost_thread.m4 \ - $(top_srcdir)/m4/ax_check_compile_flag.m4 \ - $(top_srcdir)/m4/ax_cxx_compile_stdcxx_11.m4 \ - $(top_srcdir)/m4/ax_have_epoll.m4 \ - $(top_srcdir)/m4/ax_python_devel.m4 \ - $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/libxml2.m4 \ - $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ - $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ - $(top_srcdir)/configure.ac -am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ - $(ACLOCAL_M4) -mkinstalldirs = $(install_sh) -d -CONFIG_HEADER = $(top_builddir)/config.h -CONFIG_CLEAN_FILES = config.go setenv -CONFIG_CLEAN_VPATH_FILES = -AM_V_P = $(am__v_P_@AM_V@) -am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) -am__v_P_0 = false -am__v_P_1 = : -AM_V_GEN = $(am__v_GEN_@AM_V@) -am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) -am__v_GEN_0 = @echo " GEN " $@; -am__v_GEN_1 = -AM_V_at = $(am__v_at_@AM_V@) -am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) -am__v_at_0 = @ -am__v_at_1 = -SOURCES = -DIST_SOURCES = -am__can_run_installinfo = \ - case $$AM_UPDATE_INFO_DIR in \ - n|no|NO) false;; \ - *) (install-info --version) >/dev/null 2>&1;; \ - esac -am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) -DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) -ACLOCAL = @ACLOCAL@ -AMTAR = @AMTAR@ -AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ -APPLDFLAGS = @APPLDFLAGS@ -AR = @AR@ -AUTOCONF = @AUTOCONF@ -AUTOHEADER = @AUTOHEADER@ -AUTOMAKE = @AUTOMAKE@ -AWK = @AWK@ -BOOST_ASIO_LIB = @BOOST_ASIO_LIB@ -BOOST_CPPFLAGS = @BOOST_CPPFLAGS@ -BOOST_LDFLAGS = @BOOST_LDFLAGS@ -BOOST_SYSTEM_LIB = @BOOST_SYSTEM_LIB@ -BOOST_THREAD_LIB = @BOOST_THREAD_LIB@ -CC = @CC@ -CCDEPMODE = @CCDEPMODE@ -CFLAGS = @CFLAGS@ -CPP = @CPP@ -CPPFLAGS = @CPPFLAGS@ -CUNIT_CFLAGS = @CUNIT_CFLAGS@ -CUNIT_LIBS = @CUNIT_LIBS@ -CXX = @CXX@ -CXXCPP = @CXXCPP@ -CXXDEPMODE = @CXXDEPMODE@ -CXXFLAGS = @CXXFLAGS@ -CYGPATH_W = @CYGPATH_W@ -CYTHON = @CYTHON@ -DEFS = @DEFS@ -DEPDIR = @DEPDIR@ -DLLTOOL = @DLLTOOL@ -DSYMUTIL = @DSYMUTIL@ -DUMPBIN = @DUMPBIN@ -ECHO_C = @ECHO_C@ -ECHO_N = @ECHO_N@ -ECHO_T = @ECHO_T@ -EGREP = @EGREP@ -EXEEXT = @EXEEXT@ -FGREP = @FGREP@ -GREP = @GREP@ -HAVE_CXX11 = @HAVE_CXX11@ -INSTALL = @INSTALL@ -INSTALL_DATA = @INSTALL_DATA@ -INSTALL_PROGRAM = @INSTALL_PROGRAM@ -INSTALL_SCRIPT = @INSTALL_SCRIPT@ -INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ -JANSSON_CFLAGS = @JANSSON_CFLAGS@ -JANSSON_LIBS = @JANSSON_LIBS@ -JEMALLOC_LIBS = @JEMALLOC_LIBS@ -LD = @LD@ -LDFLAGS = @LDFLAGS@ -LIBEVENT_OPENSSL_CFLAGS = @LIBEVENT_OPENSSL_CFLAGS@ -LIBEVENT_OPENSSL_LIBS = @LIBEVENT_OPENSSL_LIBS@ -LIBEV_CFLAGS = @LIBEV_CFLAGS@ -LIBEV_LIBS = @LIBEV_LIBS@ -LIBOBJS = @LIBOBJS@ -LIBS = @LIBS@ -LIBSPDYLAY_CFLAGS = @LIBSPDYLAY_CFLAGS@ -LIBSPDYLAY_LIBS = @LIBSPDYLAY_LIBS@ -LIBTOOL = @LIBTOOL@ -LIPO = @LIPO@ -LN_S = @LN_S@ -LTLIBOBJS = @LTLIBOBJS@ -LT_AGE = @LT_AGE@ -LT_CURRENT = @LT_CURRENT@ -LT_REVISION = @LT_REVISION@ -MAKEINFO = @MAKEINFO@ -MANIFEST_TOOL = @MANIFEST_TOOL@ -MKDIR_P = @MKDIR_P@ -NM = @NM@ -NMEDIT = @NMEDIT@ -OBJDUMP = @OBJDUMP@ -OBJEXT = @OBJEXT@ -OPENSSL_CFLAGS = @OPENSSL_CFLAGS@ -OPENSSL_LIBS = @OPENSSL_LIBS@ -OTOOL = @OTOOL@ -OTOOL64 = @OTOOL64@ -PACKAGE = @PACKAGE@ -PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ -PACKAGE_NAME = @PACKAGE_NAME@ -PACKAGE_STRING = @PACKAGE_STRING@ -PACKAGE_TARNAME = @PACKAGE_TARNAME@ -PACKAGE_URL = @PACKAGE_URL@ -PACKAGE_VERSION = @PACKAGE_VERSION@ -PACKAGE_VERSION_NUM = @PACKAGE_VERSION_NUM@ -PATH_SEPARATOR = @PATH_SEPARATOR@ -PKG_CONFIG = @PKG_CONFIG@ -PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ -PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ -PYTHON = @PYTHON@ -PYTHON_CPPFLAGS = @PYTHON_CPPFLAGS@ -PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@ -PYTHON_EXTRA_LDFLAGS = @PYTHON_EXTRA_LDFLAGS@ -PYTHON_EXTRA_LIBS = @PYTHON_EXTRA_LIBS@ -PYTHON_LDFLAGS = @PYTHON_LDFLAGS@ -PYTHON_PLATFORM = @PYTHON_PLATFORM@ -PYTHON_PREFIX = @PYTHON_PREFIX@ -PYTHON_SITE_PKG = @PYTHON_SITE_PKG@ -PYTHON_VERSION = @PYTHON_VERSION@ -RANLIB = @RANLIB@ -SED = @SED@ -SET_MAKE = @SET_MAKE@ -SHELL = @SHELL@ -STRIP = @STRIP@ -TESTLDADD = @TESTLDADD@ -VERSION = @VERSION@ -WARNCFLAGS = @WARNCFLAGS@ -XML2_CONFIG = @XML2_CONFIG@ -XML_CPPFLAGS = @XML_CPPFLAGS@ -XML_LIBS = @XML_LIBS@ -ZLIB_CFLAGS = @ZLIB_CFLAGS@ -ZLIB_LIBS = @ZLIB_LIBS@ -abs_builddir = @abs_builddir@ -abs_srcdir = @abs_srcdir@ -abs_top_builddir = @abs_top_builddir@ -abs_top_srcdir = @abs_top_srcdir@ -ac_ct_AR = @ac_ct_AR@ -ac_ct_CC = @ac_ct_CC@ -ac_ct_CXX = @ac_ct_CXX@ -ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ -am__include = @am__include@ -am__leading_dot = @am__leading_dot@ -am__quote = @am__quote@ -am__tar = @am__tar@ -am__untar = @am__untar@ -bindir = @bindir@ -build = @build@ -build_alias = @build_alias@ -build_cpu = @build_cpu@ -build_os = @build_os@ -build_vendor = @build_vendor@ -builddir = @builddir@ -datadir = @datadir@ -datarootdir = @datarootdir@ -docdir = @docdir@ -dvidir = @dvidir@ -exec_prefix = @exec_prefix@ -host = @host@ -host_alias = @host_alias@ -host_cpu = @host_cpu@ -host_os = @host_os@ -host_vendor = @host_vendor@ -htmldir = @htmldir@ -includedir = @includedir@ -infodir = @infodir@ -install_sh = @install_sh@ -libdir = @libdir@ -libexecdir = @libexecdir@ -localedir = @localedir@ -localstatedir = @localstatedir@ -mandir = @mandir@ -mkdir_p = @mkdir_p@ -oldincludedir = @oldincludedir@ -pdfdir = @pdfdir@ -pkgpyexecdir = @pkgpyexecdir@ -pkgpythondir = @pkgpythondir@ -prefix = @prefix@ -program_transform_name = @program_transform_name@ -psdir = @psdir@ -pyexecdir = @pyexecdir@ -pythondir = @pythondir@ -sbindir = @sbindir@ -sharedstatedir = @sharedstatedir@ -srcdir = @srcdir@ -sysconfdir = @sysconfdir@ -target = @target@ -target_alias = @target_alias@ -target_cpu = @target_cpu@ -target_os = @target_os@ -target_vendor = @target_vendor@ -top_build_prefix = @top_build_prefix@ -top_builddir = @top_builddir@ -top_srcdir = @top_srcdir@ -EXTRA_DIST = \ - nghttpx_http1_test.go \ - nghttpx_http2_test.go \ - nghttpx_spdy_test.go \ - server_tester.go \ - server.key \ - server.crt \ - alt-server.key \ - alt-server.crt \ - setenv - -all: all-am - -.SUFFIXES: -$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) - @for dep in $?; do \ - case '$(am__configure_deps)' in \ - *$$dep*) \ - ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ - && { if test -f $@; then exit 0; else break; fi; }; \ - exit 1;; \ - esac; \ - done; \ - echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu integration-tests/Makefile'; \ - $(am__cd) $(top_srcdir) && \ - $(AUTOMAKE) --gnu integration-tests/Makefile -.PRECIOUS: Makefile -Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status - @case '$?' in \ - *config.status*) \ - cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ - *) \ - echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ - cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ - esac; - -$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) - cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh - -$(top_srcdir)/configure: $(am__configure_deps) - cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh -$(ACLOCAL_M4): $(am__aclocal_m4_deps) - cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh -$(am__aclocal_m4_deps): -config.go: $(top_builddir)/config.status $(srcdir)/config.go.in - cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ -setenv: $(top_builddir)/config.status $(srcdir)/setenv.in - cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ - -mostlyclean-libtool: - -rm -f *.lo - -clean-libtool: - -rm -rf .libs _libs -tags TAGS: - -ctags CTAGS: - -cscope cscopelist: - - -distdir: $(DISTFILES) - @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ - topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ - list='$(DISTFILES)'; \ - dist_files=`for file in $$list; do echo $$file; done | \ - sed -e "s|^$$srcdirstrip/||;t" \ - -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ - case $$dist_files in \ - */*) $(MKDIR_P) `echo "$$dist_files" | \ - sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ - sort -u` ;; \ - esac; \ - for file in $$dist_files; do \ - if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ - if test -d $$d/$$file; then \ - dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ - if test -d "$(distdir)/$$file"; then \ - find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ - fi; \ - if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ - cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ - find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ - fi; \ - cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ - else \ - test -f "$(distdir)/$$file" \ - || cp -p $$d/$$file "$(distdir)/$$file" \ - || exit 1; \ - fi; \ - done -check-am: all-am -check: check-am -all-am: Makefile -installdirs: -install: install-am -install-exec: install-exec-am -install-data: install-data-am -uninstall: uninstall-am - -install-am: all-am - @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am - -installcheck: installcheck-am -install-strip: - if test -z '$(STRIP)'; then \ - $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ - install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ - install; \ - else \ - $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ - install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ - "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ - fi -mostlyclean-generic: - -clean-generic: - -distclean-generic: - -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) - -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) - -maintainer-clean-generic: - @echo "This command is intended for maintainers to use" - @echo "it deletes files that may require special tools to rebuild." -clean: clean-am - -clean-am: clean-generic clean-libtool mostlyclean-am - -distclean: distclean-am - -rm -f Makefile -distclean-am: clean-am distclean-generic - -dvi: dvi-am - -dvi-am: - -html: html-am - -html-am: - -info: info-am - -info-am: - -install-data-am: - -install-dvi: install-dvi-am - -install-dvi-am: - -install-exec-am: - -install-html: install-html-am - -install-html-am: - -install-info: install-info-am - -install-info-am: - -install-man: - -install-pdf: install-pdf-am - -install-pdf-am: - -install-ps: install-ps-am - -install-ps-am: - -installcheck-am: - -maintainer-clean: maintainer-clean-am - -rm -f Makefile -maintainer-clean-am: distclean-am maintainer-clean-generic - -mostlyclean: mostlyclean-am - -mostlyclean-am: mostlyclean-generic mostlyclean-libtool - -pdf: pdf-am - -pdf-am: - -ps: ps-am - -ps-am: - -uninstall-am: - -.MAKE: install-am install-strip - -.PHONY: all all-am check check-am clean clean-generic clean-libtool \ - cscopelist-am ctags-am distclean distclean-generic \ - distclean-libtool distdir dvi dvi-am html html-am info info-am \ - install install-am install-data install-data-am install-dvi \ - install-dvi-am install-exec install-exec-am install-html \ - install-html-am install-info install-info-am install-man \ - install-pdf install-pdf-am install-ps install-ps-am \ - install-strip installcheck installcheck-am installdirs \ - maintainer-clean maintainer-clean-generic mostlyclean \ - mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ - tags-am uninstall uninstall-am - - -.PHONY: itprep it - -itprep: - go get -d -v github.com/bradfitz/http2 - go get -d -v github.com/tatsuhiro-t/go-nghttp2 - go get -d -v golang.org/x/net/spdy - -it: - sh setenv go test -v - -# Tell versions [3.59,3.63) of GNU make to not export all variables. -# Otherwise a system limit (for SysV at least) may be exceeded. -.NOEXPORT: diff --git a/integration-tests/alt-server.crt b/integration-tests/alt-server.crt deleted file mode 100644 index f003eb1..0000000 --- a/integration-tests/alt-server.crt +++ /dev/null @@ -1,21 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDhzCCAm+gAwIBAgIJANfuEldiquMNMA0GCSqGSIb3DQEBCwUAMFoxCzAJBgNV -BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX -aWRnaXRzIFB0eSBMdGQxEzARBgNVBAMMCmFsdC1kb21haW4wHhcNMTUwMTI1MDYy -NTQxWhcNMjUwMTIyMDYyNTQxWjBaMQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29t -ZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMRMwEQYD -VQQDDAphbHQtZG9tYWluMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA -0IwhDOGDipGrJQ9IoRSzPdkU/Ii4aJgGKHlXminym42X0VI3IW61RLvOHRlHVmVH -JQjFuDo2x+y81t9NlDg3HGUbSpzOzpm6StiutB7c4hreT5G4r0YKya1ugiemN0+p -qjIPJWm2jVnf448eZvUKRKEQ9W0MLZjiNjVGKrKlwo7fIlXg4N3+YixLYffAT1NV -d1T6V5jzlbruj15gK2nGjMQ9D1h1t9vTbTxY+mtk72aX0Y64IE6pPBWLFSSH8ozU -idDoL3AZwz2Jker+ALKK8CM4uho/RPpyW1C06HH+HLdH2MqEjDOROde/Nzxm668O -gK/JWGIEyUqYiUXx0yhFxwIDAQABo1AwTjAdBgNVHQ4EFgQU/Y0GDN2uPjbyePcu -95ZvYEK/gHIwHwYDVR0jBBgwFoAU/Y0GDN2uPjbyePcu95ZvYEK/gHIwDAYDVR0T -BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAodD6LVCzL3wfsZ6TxTzf9TfgIdbj -ilL3SEMT/xnfTXT3SLYScTRqQIAI29Y7dOLMq89p4hY2wmeUEhBUAz+y9G2JVr8o -6EbxXrQpWgNJogELqoNnMdrDxB5RsmDDKEJ/rLjDfSkjWbK7B2PZsqVTDgjekCFw -u6FqTIjn/O1O/L5tjwxwxjHmQod/maFCvXoDOVBuwdHnkp298tqlvsHfHO8m++Wj -+XYB8plMIjpeTh9v4w9Jc4QZ59lK/3Tt4qaENeQrMEubKSY/Zen7L2bzhk+cChWT -GSGz9uNXieoZaH79D0wnyZaSZ5Ds4ActMevnGg3iYXuzuFqx8Pungn74Vg== ------END CERTIFICATE----- diff --git a/integration-tests/alt-server.key b/integration-tests/alt-server.key deleted file mode 100644 index a977663..0000000 --- a/integration-tests/alt-server.key +++ /dev/null @@ -1,28 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIIEwAIBADANBgkqhkiG9w0BAQEFAASCBKowggSmAgEAAoIBAQDQjCEM4YOKkasl -D0ihFLM92RT8iLhomAYoeVeaKfKbjZfRUjchbrVEu84dGUdWZUclCMW4OjbH7LzW -302UODccZRtKnM7OmbpK2K60HtziGt5PkbivRgrJrW6CJ6Y3T6mqMg8labaNWd/j -jx5m9QpEoRD1bQwtmOI2NUYqsqXCjt8iVeDg3f5iLEth98BPU1V3VPpXmPOVuu6P -XmAracaMxD0PWHW329NtPFj6a2TvZpfRjrggTqk8FYsVJIfyjNSJ0OgvcBnDPYmR -6v4AsorwIzi6Gj9E+nJbULTocf4ct0fYyoSMM5E51783PGbrrw6Ar8lYYgTJSpiJ -RfHTKEXHAgMBAAECggEBALTrjFSXY72YB+h7rN+JjMIwDIPUvF6I3HbKZhQpJf6K -xNVkRM2tNHavku0tm/S4ohLf3F+pqRKiL2Udjjjy1+S7VgTRqpwTQ0lhV5aNW8SP -2KMg4R61XfB+k+s4KHu9kYxEJ12mqydPe+r3o0FgfYryTDsOYk1AX6b1aqzqFOGF -7GaqLALSbKU59tcJJ1SZNBbpIKFUrAT9nZt9dW02/foqP5bzUk43Yjw48xmLwegc -bMXXcpZhNZSktltvwRw7Q4Foc9kuRlMdTAnAD9PnMCcZwicS/YeVVF6Rz4fGviKv -7/kPHQ7g4YpFktVDzuZ5xw6GDVFeJ6uGMVUX8+EePvkCgYEA+/nrcn82nFHCxm8Q -0iiUhi/AoXjZg+O5Ytaje9O/YNoX+c4ywe13h0+TXKH79O0KfTwXeJyDgPZbAIFV -9oURellRYUzKDafnBHis2f+Ywn6GqHL5e2X30ZxIp1GK46pcvne1YuvJhgGmiVay -vd7sRx09OKU124dG22rIFCis6asCgYEA0+CsA6LrEwQ/aPJYASY3VHNO/WoAOnPg -Cwsg+02XWsPEwP//lNmpanz8TUm2URS063ZK8bx7t3ejvDgBdsRwwjiMlDp7XTUU -3Zk+mhCV2qkMi02aKemvz29bDhmh5JoH7W3IwsXtJYO0yZDYrDR3ioiKRccioPoE -b/Nq781sEFUCgYEA4xqx9xRpaCLY5nicNI6WrwrDF8YQZisNn+PMnYKP7v8itOgA -H4GkRbSXINpueKZc2dsbXH3UmJtyEdaAYBw3UIrIKmZHhl9afFE3mZQhXssjGxfl -fC6/WZD+eq+n+uJFjPXf6jSSAdHjA828dB1D4CSeVTuyexZF6uUnR+QRVNkCgYEA -i+pb7XLSpZYygY03zFp+Q0h6KyKqz+7hTqmkuA8/GfMZpRHop1UtaWLsAeXhfZ2c -87kEOKptUHSzLYIWhWWnyLorK1+LQ7vf8Y5XJso5C1KDNCKk4XSuYt94U9FddWa6 -QXI0F1s5BYL6Cfma++0R2+va08Vy+rbf40XtojoXWJkCgYEA0hMQSCvok7is27nQ -G80KXfmghU2eEB7zif3T00/fwJycxEbmnNeof+SKmhdY4ZgqTscfOxlQPflV/eqB -xs4GnFDDeM0F8KH0BimOXxr7sJPFCg22PCCQQcRtM/KoU+ip/kNmTfwrsC0xMFPU -HD8M1JCZF2eLMekXXP3cB0U4sUs= ------END PRIVATE KEY----- diff --git a/integration-tests/config.go.in b/integration-tests/config.go.in deleted file mode 100644 index 0a6fd6b..0000000 --- a/integration-tests/config.go.in +++ /dev/null @@ -1,5 +0,0 @@ -package nghttp2 - -const ( - buildDir = "@top_builddir@" -) diff --git a/integration-tests/nghttpx_http1_test.go b/integration-tests/nghttpx_http1_test.go deleted file mode 100644 index c8d5870..0000000 --- a/integration-tests/nghttpx_http1_test.go +++ /dev/null @@ -1,506 +0,0 @@ -package nghttp2 - -import ( - "bufio" - "fmt" - "github.com/bradfitz/http2/hpack" - "io" - "net/http" - "syscall" - "testing" -) - -// TestH1H1PlainGET tests whether simple HTTP/1 GET request works. -func TestH1H1PlainGET(t *testing.T) { - st := newServerTester(nil, t, noopHandler) - defer st.Close() - - res, err := st.http1(requestParam{ - name: "TestH1H1PlainGET", - }) - if err != nil { - t.Fatalf("Error st.http1() = %v", err) - } - - want := 200 - if got := res.status; got != want { - t.Errorf("status = %v; want %v", got, want) - } -} - -// TestH1H1PlainGETClose tests whether simple HTTP/1 GET request with -// Connetion: close request header field works. -func TestH1H1PlainGETClose(t *testing.T) { - st := newServerTester(nil, t, noopHandler) - defer st.Close() - - res, err := st.http1(requestParam{ - name: "TestH1H1PlainGETClose", - header: []hpack.HeaderField{ - pair("Connection", "close"), - }, - }) - if err != nil { - t.Fatalf("Error st.http1() = %v", err) - } - - want := 200 - if got := res.status; got != want { - t.Errorf("status = %v; want %v", got, want) - } -} - -// TestH1H1MultipleRequestCL tests that server rejects request which -// contains multiple Content-Length header fields. -func TestH1H1MultipleRequestCL(t *testing.T) { - st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) { - t.Errorf("server should not forward bad request") - }) - defer st.Close() - - if _, err := io.WriteString(st.conn, fmt.Sprintf(`GET / HTTP/1.1 -Host: %v -Test-Case: TestH1H1MultipleRequestCL -Content-Length: 0 -Content-Length: 0 - -`, st.authority)); err != nil { - t.Fatalf("Error io.WriteString() = %v", err) - } - - resp, err := http.ReadResponse(bufio.NewReader(st.conn), nil) - if err != nil { - t.Fatalf("Error http.ReadResponse() = %v", err) - } - - want := 400 - if got := resp.StatusCode; got != want { - t.Errorf("status: %v; want %v", got, want) - } -} - -// TestH1H1ConnectFailure tests that server handles the situation that -// connection attempt to HTTP/1 backend failed. -func TestH1H1ConnectFailure(t *testing.T) { - st := newServerTester(nil, t, noopHandler) - defer st.Close() - - // shutdown backend server to simulate backend connect failure - st.ts.Close() - - res, err := st.http1(requestParam{ - name: "TestH1H1ConnectFailure", - }) - if err != nil { - t.Fatalf("Error st.http1() = %v", err) - } - want := 503 - if got := res.status; got != want { - t.Errorf("status: %v; want %v", got, want) - } -} - -// TestH1H1GracefulShutdown tests graceful shutdown. -func TestH1H1GracefulShutdown(t *testing.T) { - st := newServerTester(nil, t, noopHandler) - defer st.Close() - - res, err := st.http1(requestParam{ - name: "TestH1H1GracefulShutdown-1", - }) - if err != nil { - t.Fatalf("Error st.http1() = %v", err) - } - - if got, want := res.status, 200; got != want { - t.Errorf("status: %v; want %v", got, want) - } - - st.cmd.Process.Signal(syscall.SIGQUIT) - - res, err = st.http1(requestParam{ - name: "TestH1H1GracefulShutdown-2", - }) - if err != nil { - t.Fatalf("Error st.http1() = %v", err) - } - - if got, want := res.status, 200; got != want { - t.Errorf("status: %v; want %v", got, want) - } - - if got, want := res.connClose, true; got != want { - t.Errorf("res.connClose: %v; want %v", got, want) - } - - want := io.EOF - if _, err := st.conn.Read(nil); err == nil || err != want { - t.Errorf("st.conn.Read(): %v; want %v", err, want) - } -} - -// TestH1H1HostRewrite tests that server rewrites Host header field -func TestH1H1HostRewrite(t *testing.T) { - st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) { - w.Header().Add("request-host", r.Host) - }) - defer st.Close() - - res, err := st.http1(requestParam{ - name: "TestH1H1HostRewrite", - }) - if err != nil { - t.Fatalf("Error st.http1() = %v", err) - } - if got, want := res.status, 200; got != want { - t.Errorf("status: %v; want %v", got, want) - } - if got, want := res.header.Get("request-host"), st.backendHost; got != want { - t.Errorf("request-host: %v; want %v", got, want) - } -} - -// TestH1H1HTTP10 tests that server can accept HTTP/1.0 request -// without Host header field -func TestH1H1HTTP10(t *testing.T) { - st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) { - w.Header().Add("request-host", r.Host) - }) - defer st.Close() - - if _, err := io.WriteString(st.conn, "GET / HTTP/1.0\r\nTest-Case: TestH1H1HTTP10\r\n\r\n"); err != nil { - t.Fatalf("Error io.WriteString() = %v", err) - } - - resp, err := http.ReadResponse(bufio.NewReader(st.conn), nil) - if err != nil { - t.Fatalf("Error http.ReadResponse() = %v", err) - } - - if got, want := resp.StatusCode, 200; got != want { - t.Errorf("status: %v; want %v", got, want) - } - if got, want := resp.Header.Get("request-host"), st.backendHost; got != want { - t.Errorf("request-host: %v; want %v", got, want) - } -} - -// TestH1H1HTTP10NoHostRewrite tests that server generates host header -// field using actual backend server even if --no-http-rewrite is -// used. -func TestH1H1HTTP10NoHostRewrite(t *testing.T) { - st := newServerTester([]string{"--no-host-rewrite"}, t, func(w http.ResponseWriter, r *http.Request) { - w.Header().Add("request-host", r.Host) - }) - defer st.Close() - - if _, err := io.WriteString(st.conn, "GET / HTTP/1.0\r\nTest-Case: TestH1H1HTTP10NoHostRewrite\r\n\r\n"); err != nil { - t.Fatalf("Error io.WriteString() = %v", err) - } - - resp, err := http.ReadResponse(bufio.NewReader(st.conn), nil) - if err != nil { - t.Fatalf("Error http.ReadResponse() = %v", err) - } - - if got, want := resp.StatusCode, 200; got != want { - t.Errorf("status: %v; want %v", got, want) - } - if got, want := resp.Header.Get("request-host"), st.backendHost; got != want { - t.Errorf("request-host: %v; want %v", got, want) - } -} - -// TestH1H1RequestTrailer tests request trailer part is forwarded to -// backend. -func TestH1H1RequestTrailer(t *testing.T) { - st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) { - buf := make([]byte, 4096) - for { - _, err := r.Body.Read(buf) - if err == io.EOF { - break - } - if err != nil { - t.Fatalf("r.Body.Read() = %v", err) - } - } - if got, want := r.Trailer.Get("foo"), "bar"; got != want { - t.Errorf("r.Trailer.Get(foo): %v; want %v", got, want) - } - }) - defer st.Close() - - res, err := st.http1(requestParam{ - name: "TestH1H1RequestTrailer", - body: []byte("1"), - trailer: []hpack.HeaderField{ - pair("foo", "bar"), - }, - }) - if err != nil { - t.Fatalf("Error st.http1() = %v", err) - } - if got, want := res.status, 200; got != want { - t.Errorf("res.status: %v; want %v", got, want) - } -} - -// TestH1H1HeaderFieldBufferPath tests that request with request path -// larger than configured buffer size is rejected. -func TestH1H1HeaderFieldBufferPath(t *testing.T) { - // The value 100 is chosen so that sum of header fields bytes - // does not exceed it. We use > 100 bytes URI to exceed this - // limit. - st := newServerTester([]string{"--header-field-buffer=100"}, t, func(w http.ResponseWriter, r *http.Request) { - t.Fatal("execution path should not be here") - }) - defer st.Close() - - res, err := st.http1(requestParam{ - name: "TestH1H1HeaderFieldBufferPath", - path: "/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", - }) - if err != nil { - t.Fatalf("Error st.http1() = %v", err) - } - if got, want := res.status, 431; got != want { - t.Errorf("status: %v; want %v", got, want) - } -} - -// TestH1H1HeaderFieldBuffer tests that request with header fields -// larger than configured buffer size is rejected. -func TestH1H1HeaderFieldBuffer(t *testing.T) { - st := newServerTester([]string{"--header-field-buffer=10"}, t, func(w http.ResponseWriter, r *http.Request) { - t.Fatal("execution path should not be here") - }) - defer st.Close() - - res, err := st.http1(requestParam{ - name: "TestH1H1HeaderFieldBuffer", - }) - if err != nil { - t.Fatalf("Error st.http1() = %v", err) - } - if got, want := res.status, 431; got != want { - t.Errorf("status: %v; want %v", got, want) - } -} - -// TestH1H1HeaderFields tests that request with header fields more -// than configured number is rejected. -func TestH1H1HeaderFields(t *testing.T) { - st := newServerTester([]string{"--max-header-fields=1"}, t, func(w http.ResponseWriter, r *http.Request) { - t.Fatal("execution path should not be here") - }) - defer st.Close() - - res, err := st.http1(requestParam{ - name: "TestH1H1HeaderFields", - header: []hpack.HeaderField{ - // Add extra header field to ensure that - // header field limit exceeds - pair("Connection", "close"), - }, - }) - if err != nil { - t.Fatalf("Error st.http1() = %v", err) - } - if got, want := res.status, 431; got != want { - t.Errorf("status: %v; want %v", got, want) - } -} - -// TestH1H2ConnectFailure tests that server handles the situation that -// connection attempt to HTTP/2 backend failed. -func TestH1H2ConnectFailure(t *testing.T) { - st := newServerTester([]string{"--http2-bridge"}, t, noopHandler) - defer st.Close() - - // simulate backend connect attempt failure - st.ts.Close() - - res, err := st.http1(requestParam{ - name: "TestH1H2ConnectFailure", - }) - if err != nil { - t.Fatalf("Error st.http1() = %v", err) - } - want := 503 - if got := res.status; got != want { - t.Errorf("status: %v; want %v", got, want) - } -} - -// TestH1H2NoHost tests that server rejects request without Host -// header field for HTTP/2 backend. -func TestH1H2NoHost(t *testing.T) { - st := newServerTester([]string{"--http2-bridge"}, t, func(w http.ResponseWriter, r *http.Request) { - t.Errorf("server should not forward bad request") - }) - defer st.Close() - - // without Host header field, we expect 400 response - if _, err := io.WriteString(st.conn, "GET / HTTP/1.1\r\nTest-Case: TestH1H2NoHost\r\n\r\n"); err != nil { - t.Fatalf("Error io.WriteString() = %v", err) - } - - resp, err := http.ReadResponse(bufio.NewReader(st.conn), nil) - if err != nil { - t.Fatalf("Error http.ReadResponse() = %v", err) - } - - want := 400 - if got := resp.StatusCode; got != want { - t.Errorf("status: %v; want %v", got, want) - } -} - -// TestH1H2HTTP10 tests that server can accept HTTP/1.0 request -// without Host header field -func TestH1H2HTTP10(t *testing.T) { - st := newServerTester([]string{"--http2-bridge"}, t, func(w http.ResponseWriter, r *http.Request) { - w.Header().Add("request-host", r.Host) - }) - defer st.Close() - - if _, err := io.WriteString(st.conn, "GET / HTTP/1.0\r\nTest-Case: TestH1H2HTTP10\r\n\r\n"); err != nil { - t.Fatalf("Error io.WriteString() = %v", err) - } - - resp, err := http.ReadResponse(bufio.NewReader(st.conn), nil) - if err != nil { - t.Fatalf("Error http.ReadResponse() = %v", err) - } - - if got, want := resp.StatusCode, 200; got != want { - t.Errorf("status: %v; want %v", got, want) - } - if got, want := resp.Header.Get("request-host"), st.backendHost; got != want { - t.Errorf("request-host: %v; want %v", got, want) - } -} - -// TestH1H2HTTP10NoHostRewrite tests that server generates host header -// field using actual backend server even if --no-http-rewrite is -// used. -func TestH1H2HTTP10NoHostRewrite(t *testing.T) { - st := newServerTester([]string{"--http2-bridge", "--no-host-rewrite"}, t, func(w http.ResponseWriter, r *http.Request) { - w.Header().Add("request-host", r.Host) - }) - defer st.Close() - - if _, err := io.WriteString(st.conn, "GET / HTTP/1.0\r\nTest-Case: TestH1H2HTTP10NoHostRewrite\r\n\r\n"); err != nil { - t.Fatalf("Error io.WriteString() = %v", err) - } - - resp, err := http.ReadResponse(bufio.NewReader(st.conn), nil) - if err != nil { - t.Fatalf("Error http.ReadResponse() = %v", err) - } - - if got, want := resp.StatusCode, 200; got != want { - t.Errorf("status: %v; want %v", got, want) - } - if got, want := resp.Header.Get("request-host"), st.backendHost; got != want { - t.Errorf("request-host: %v; want %v", got, want) - } -} - -// TestH1H2CrumbleCookie tests that Cookies are crumbled and assembled -// when forwarding to HTTP/2 backend link. go-nghttp2 server -// concatenates crumbled Cookies automatically, so this test is not -// much effective now. -func TestH1H2CrumbleCookie(t *testing.T) { - st := newServerTester([]string{"--http2-bridge"}, t, func(w http.ResponseWriter, r *http.Request) { - if got, want := r.Header.Get("Cookie"), "alpha; bravo; charlie"; got != want { - t.Errorf("Cookie: %v; want %v", got, want) - } - }) - defer st.Close() - - res, err := st.http1(requestParam{ - name: "TestH1H2CrumbleCookie", - header: []hpack.HeaderField{ - pair("Cookie", "alpha; bravo; charlie"), - }, - }) - if err != nil { - t.Fatalf("Error st.http1() = %v", err) - } - if got, want := res.status, 200; got != want { - t.Errorf("status: %v; want %v", got, want) - } -} - -// TestH1H2GenerateVia tests that server generates Via header field to and -// from backend server. -func TestH1H2GenerateVia(t *testing.T) { - st := newServerTester([]string{"--http2-bridge"}, t, func(w http.ResponseWriter, r *http.Request) { - if got, want := r.Header.Get("Via"), "1.1 nghttpx"; got != want { - t.Errorf("Via: %v; want %v", got, want) - } - }) - defer st.Close() - - res, err := st.http1(requestParam{ - name: "TestH1H2GenerateVia", - }) - if err != nil { - t.Fatalf("Error st.http1() = %v", err) - } - if got, want := res.header.Get("Via"), "2 nghttpx"; got != want { - t.Errorf("Via: %v; want %v", got, want) - } -} - -// TestH1H2AppendVia tests that server adds value to existing Via -// header field to and from backend server. -func TestH1H2AppendVia(t *testing.T) { - st := newServerTester([]string{"--http2-bridge"}, t, func(w http.ResponseWriter, r *http.Request) { - if got, want := r.Header.Get("Via"), "foo, 1.1 nghttpx"; got != want { - t.Errorf("Via: %v; want %v", got, want) - } - w.Header().Add("Via", "bar") - }) - defer st.Close() - - res, err := st.http1(requestParam{ - name: "TestH1H2AppendVia", - header: []hpack.HeaderField{ - pair("via", "foo"), - }, - }) - if err != nil { - t.Fatalf("Error st.http1() = %v", err) - } - if got, want := res.header.Get("Via"), "bar, 2 nghttpx"; got != want { - t.Errorf("Via: %v; want %v", got, want) - } -} - -// TestH1H2NoVia tests that server does not add value to existing Via -// header field to and from backend server. -func TestH1H2NoVia(t *testing.T) { - st := newServerTester([]string{"--http2-bridge", "--no-via"}, t, func(w http.ResponseWriter, r *http.Request) { - if got, want := r.Header.Get("Via"), "foo"; got != want { - t.Errorf("Via: %v; want %v", got, want) - } - w.Header().Add("Via", "bar") - }) - defer st.Close() - - res, err := st.http1(requestParam{ - name: "TestH1H2NoVia", - header: []hpack.HeaderField{ - pair("via", "foo"), - }, - }) - if err != nil { - t.Fatalf("Error st.http1() = %v", err) - } - if got, want := res.header.Get("Via"), "bar"; got != want { - t.Errorf("Via: %v; want %v", got, want) - } -} diff --git a/integration-tests/nghttpx_http2_test.go b/integration-tests/nghttpx_http2_test.go deleted file mode 100644 index 953e9d4..0000000 --- a/integration-tests/nghttpx_http2_test.go +++ /dev/null @@ -1,813 +0,0 @@ -package nghttp2 - -import ( - "crypto/tls" - "fmt" - "github.com/bradfitz/http2" - "github.com/bradfitz/http2/hpack" - "io" - "io/ioutil" - "net/http" - "strings" - "syscall" - "testing" -) - -// TestH2H1PlainGET tests whether simple HTTP/2 GET request works. -func TestH2H1PlainGET(t *testing.T) { - st := newServerTester(nil, t, noopHandler) - defer st.Close() - - res, err := st.http2(requestParam{ - name: "TestH2H1PlainGET", - }) - if err != nil { - t.Fatalf("Error st.http2() = %v", err) - } - - want := 200 - if res.status != want { - t.Errorf("status = %v; want %v", res.status, want) - } -} - -// TestH2H1AddXff tests that server generates X-Forwarded-For header -// field when forwarding request to backend. -func TestH2H1AddXff(t *testing.T) { - st := newServerTester([]string{"--add-x-forwarded-for"}, t, func(w http.ResponseWriter, r *http.Request) { - xff := r.Header.Get("X-Forwarded-For") - want := "127.0.0.1" - if xff != want { - t.Errorf("X-Forwarded-For = %v; want %v", xff, want) - } - }) - defer st.Close() - - _, err := st.http2(requestParam{ - name: "TestH2H1AddXff", - }) - if err != nil { - t.Fatalf("Error st.http2() = %v", err) - } -} - -// TestH2H1AddXff2 tests that server appends X-Forwarded-For header -// field to existing one when forwarding request to backend. -func TestH2H1AddXff2(t *testing.T) { - st := newServerTester([]string{"--add-x-forwarded-for"}, t, func(w http.ResponseWriter, r *http.Request) { - xff := r.Header.Get("X-Forwarded-For") - want := "host, 127.0.0.1" - if xff != want { - t.Errorf("X-Forwarded-For = %v; want %v", xff, want) - } - }) - defer st.Close() - - _, err := st.http2(requestParam{ - name: "TestH2H1AddXff2", - header: []hpack.HeaderField{ - pair("x-forwarded-for", "host"), - }, - }) - if err != nil { - t.Fatalf("Error st.http2() = %v", err) - } -} - -// TestH2H1StripXff tests that --strip-incoming-x-forwarded-for -// option. -func TestH2H1StripXff(t *testing.T) { - st := newServerTester([]string{"--strip-incoming-x-forwarded-for"}, t, func(w http.ResponseWriter, r *http.Request) { - if xff, found := r.Header["X-Forwarded-For"]; found { - t.Errorf("X-Forwarded-For = %v; want nothing", xff) - } - }) - defer st.Close() - - _, err := st.http2(requestParam{ - name: "TestH2H1StripXff1", - header: []hpack.HeaderField{ - pair("x-forwarded-for", "host"), - }, - }) - if err != nil { - t.Fatalf("Error st.http2() = %v", err) - } -} - -// TestH2H1StripAddXff tests that --strip-incoming-x-forwarded-for and -// --add-x-forwarded-for options. -func TestH2H1StripAddXff(t *testing.T) { - args := []string{ - "--strip-incoming-x-forwarded-for", - "--add-x-forwarded-for", - } - st := newServerTester(args, t, func(w http.ResponseWriter, r *http.Request) { - xff := r.Header.Get("X-Forwarded-For") - want := "127.0.0.1" - if xff != want { - t.Errorf("X-Forwarded-For = %v; want %v", xff, want) - } - }) - defer st.Close() - - _, err := st.http2(requestParam{ - name: "TestH2H1StripAddXff", - header: []hpack.HeaderField{ - pair("x-forwarded-for", "host"), - }, - }) - if err != nil { - t.Fatalf("Error st.http2() = %v", err) - } -} - -// TestH2H1GenerateVia tests that server generates Via header field to and -// from backend server. -func TestH2H1GenerateVia(t *testing.T) { - st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) { - if got, want := r.Header.Get("Via"), "2 nghttpx"; got != want { - t.Errorf("Via: %v; want %v", got, want) - } - }) - defer st.Close() - - res, err := st.http2(requestParam{ - name: "TestH2H1GenerateVia", - }) - if err != nil { - t.Fatalf("Error st.http2() = %v", err) - } - if got, want := res.header.Get("Via"), "1.1 nghttpx"; got != want { - t.Errorf("Via: %v; want %v", got, want) - } -} - -// TestH2H1AppendVia tests that server adds value to existing Via -// header field to and from backend server. -func TestH2H1AppendVia(t *testing.T) { - st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) { - if got, want := r.Header.Get("Via"), "foo, 2 nghttpx"; got != want { - t.Errorf("Via: %v; want %v", got, want) - } - w.Header().Add("Via", "bar") - }) - defer st.Close() - - res, err := st.http2(requestParam{ - name: "TestH2H1AppendVia", - header: []hpack.HeaderField{ - pair("via", "foo"), - }, - }) - if err != nil { - t.Fatalf("Error st.http2() = %v", err) - } - if got, want := res.header.Get("Via"), "bar, 1.1 nghttpx"; got != want { - t.Errorf("Via: %v; want %v", got, want) - } -} - -// TestH2H1NoVia tests that server does not add value to existing Via -// header field to and from backend server. -func TestH2H1NoVia(t *testing.T) { - st := newServerTester([]string{"--no-via"}, t, func(w http.ResponseWriter, r *http.Request) { - if got, want := r.Header.Get("Via"), "foo"; got != want { - t.Errorf("Via: %v; want %v", got, want) - } - w.Header().Add("Via", "bar") - }) - defer st.Close() - - res, err := st.http2(requestParam{ - name: "TestH2H1NoVia", - header: []hpack.HeaderField{ - pair("via", "foo"), - }, - }) - if err != nil { - t.Fatalf("Error st.http2() = %v", err) - } - if got, want := res.header.Get("Via"), "bar"; got != want { - t.Errorf("Via: %v; want %v", got, want) - } -} - -// TestH2H1HostRewrite tests that server rewrites host header field -func TestH2H1HostRewrite(t *testing.T) { - st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) { - w.Header().Add("request-host", r.Host) - }) - defer st.Close() - - res, err := st.http2(requestParam{ - name: "TestH2H1HostRewrite", - }) - if err != nil { - t.Fatalf("Error st.http2() = %v", err) - } - if got, want := res.status, 200; got != want { - t.Errorf("status: %v; want %v", got, want) - } - if got, want := res.header.Get("request-host"), st.backendHost; got != want { - t.Errorf("request-host: %v; want %v", got, want) - } -} - -// TestH2H1NoHostRewrite tests that server does not rewrite host -// header field -func TestH2H1NoHostRewrite(t *testing.T) { - st := newServerTester([]string{"--no-host-rewrite"}, t, func(w http.ResponseWriter, r *http.Request) { - w.Header().Add("request-host", r.Host) - }) - defer st.Close() - - res, err := st.http2(requestParam{ - name: "TestH2H1NoHostRewrite", - }) - if err != nil { - t.Fatalf("Error st.http2() = %v", err) - } - if got, want := res.status, 200; got != want { - t.Errorf("status: %v; want %v", got, want) - } - if got, want := res.header.Get("request-host"), st.frontendHost; got != want { - t.Errorf("request-host: %v; want %v", got, want) - } -} - -// TestH2H1BadRequestCL tests that server rejects request whose -// content-length header field value does not match its request body -// size. -func TestH2H1BadRequestCL(t *testing.T) { - st := newServerTester(nil, t, noopHandler) - defer st.Close() - - // we set content-length: 1024, but the actual request body is - // 3 bytes. - res, err := st.http2(requestParam{ - name: "TestH2H1BadRequestCL", - method: "POST", - header: []hpack.HeaderField{ - pair("content-length", "1024"), - }, - body: []byte("foo"), - }) - if err != nil { - t.Fatalf("Error st.http2() = %v", err) - } - - want := http2.ErrCodeProtocol - if res.errCode != want { - t.Errorf("res.errCode = %v; want %v", res.errCode, want) - } -} - -// TestH2H1BadResponseCL tests that server returns error when -// content-length response header field value does not match its -// response body size. -func TestH2H1BadResponseCL(t *testing.T) { - st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) { - // we set content-length: 1024, but only send 3 bytes. - w.Header().Add("Content-Length", "1024") - w.Write([]byte("foo")) - }) - defer st.Close() - - res, err := st.http2(requestParam{ - name: "TestH2H1BadResponseCL", - }) - if err != nil { - t.Fatalf("Error st.http2() = %v", err) - } - - want := http2.ErrCodeProtocol - if res.errCode != want { - t.Errorf("res.errCode = %v; want %v", res.errCode, want) - } -} - -// TestH2H1LocationRewrite tests location header field rewriting -// works. -func TestH2H1LocationRewrite(t *testing.T) { - st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) { - // TODO we cannot get st.ts's port number here.. 8443 - // is just a place holder. We ignore it on rewrite. - w.Header().Add("Location", "http://127.0.0.1:8443/p/q?a=b#fragment") - }) - defer st.Close() - - res, err := st.http2(requestParam{ - name: "TestH2H1LocationRewrite", - }) - if err != nil { - t.Fatalf("Error st.http2() = %v", err) - } - - want := fmt.Sprintf("http://127.0.0.1:%v/p/q?a=b#fragment", serverPort) - if got := res.header.Get("Location"); got != want { - t.Errorf("Location: %v; want %v", got, want) - } -} - -// TestH2H1ChunkedRequestBody tests that chunked request body works. -func TestH2H1ChunkedRequestBody(t *testing.T) { - st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) { - want := "[chunked]" - if got := fmt.Sprint(r.TransferEncoding); got != want { - t.Errorf("Transfer-Encoding: %v; want %v", got, want) - } - body, err := ioutil.ReadAll(r.Body) - if err != nil { - t.Fatalf("Error reading r.body: %v", err) - } - want = "foo" - if got := string(body); got != want { - t.Errorf("body: %v; want %v", got, want) - } - }) - defer st.Close() - - _, err := st.http2(requestParam{ - name: "TestH2H1ChunkedRequestBody", - method: "POST", - body: []byte("foo"), - }) - if err != nil { - t.Fatalf("Error st.http2() = %v", err) - } -} - -// TestH2H1MultipleRequestCL tests that server rejects request with -// multiple Content-Length request header fields. -func TestH2H1MultipleRequestCL(t *testing.T) { - st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) { - t.Errorf("server should not forward bad request") - }) - defer st.Close() - - res, err := st.http2(requestParam{ - name: "TestH2H1MultipleRequestCL", - header: []hpack.HeaderField{ - pair("content-length", "1"), - pair("content-length", "1"), - }, - }) - if err != nil { - t.Fatalf("Error st.http2() = %v", err) - } - if got, want := res.errCode, http2.ErrCodeProtocol; got != want { - t.Errorf("res.errCode: %v; want %v", got, want) - } -} - -// TestH2H1InvalidRequestCL tests that server rejects request with -// Content-Length which cannot be parsed as a number. -func TestH2H1InvalidRequestCL(t *testing.T) { - st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) { - t.Errorf("server should not forward bad request") - }) - defer st.Close() - - res, err := st.http2(requestParam{ - name: "TestH2H1InvalidRequestCL", - header: []hpack.HeaderField{ - pair("content-length", ""), - }, - }) - if err != nil { - t.Fatalf("Error st.http2() = %v", err) - } - if got, want := res.errCode, http2.ErrCodeProtocol; got != want { - t.Errorf("res.errCode: %v; want %v", got, want) - } -} - -// TestH2H1ConnectFailure tests that server handles the situation that -// connection attempt to HTTP/1 backend failed. -func TestH2H1ConnectFailure(t *testing.T) { - st := newServerTester(nil, t, noopHandler) - defer st.Close() - - // shutdown backend server to simulate backend connect failure - st.ts.Close() - - res, err := st.http2(requestParam{ - name: "TestH2H1ConnectFailure", - }) - if err != nil { - t.Fatalf("Error st.http2() = %v", err) - } - want := 503 - if got := res.status; got != want { - t.Errorf("status: %v; want %v", got, want) - } -} - -// TestH2H1AssembleCookies tests that crumbled cookies in HTTP/2 -// request is assembled into 1 when forwarding to HTTP/1 backend link. -func TestH2H1AssembleCookies(t *testing.T) { - st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) { - if got, want := r.Header.Get("Cookie"), "alpha; bravo; charlie"; got != want { - t.Errorf("Cookie: %v; want %v", got, want) - } - }) - defer st.Close() - - res, err := st.http2(requestParam{ - name: "TestH2H1AssembleCookies", - header: []hpack.HeaderField{ - pair("cookie", "alpha"), - pair("cookie", "bravo"), - pair("cookie", "charlie"), - }, - }) - if err != nil { - t.Fatalf("Error st.http2() = %v", err) - } - if got, want := res.status, 200; got != want { - t.Errorf("status: %v; want %v", got, want) - } -} - -// TestH2H1TETrailers tests that server accepts TE request header -// field if it has trailers only. -func TestH2H1TETrailers(t *testing.T) { - st := newServerTester(nil, t, noopHandler) - defer st.Close() - - res, err := st.http2(requestParam{ - name: "TestH2H1TETrailers", - header: []hpack.HeaderField{ - pair("te", "trailers"), - }, - }) - if err != nil { - t.Fatalf("Error st.http2() = %v", err) - } - if got, want := res.status, 200; got != want { - t.Errorf("status: %v; want %v", got, want) - } -} - -// TestH2H1TEGzip tests that server resets stream if TE request header -// field contains gzip. -func TestH2H1TEGzip(t *testing.T) { - st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) { - t.Error("server should not forward bad request") - }) - defer st.Close() - - res, err := st.http2(requestParam{ - name: "TestH2H1TEGzip", - header: []hpack.HeaderField{ - pair("te", "gzip"), - }, - }) - if err != nil { - t.Fatalf("Error st.http2() = %v", err) - } - if got, want := res.errCode, http2.ErrCodeProtocol; got != want { - t.Errorf("res.errCode = %v; want %v", res.errCode, want) - } -} - -// TestH2H1SNI tests server's TLS SNI extension feature. It must -// choose appropriate certificate depending on the indicated -// server_name from client. -func TestH2H1SNI(t *testing.T) { - st := newServerTesterTLSConfig([]string{"--subcert=" + testDir + "/alt-server.key:" + testDir + "/alt-server.crt"}, t, noopHandler, &tls.Config{ - ServerName: "alt-domain", - }) - defer st.Close() - - tlsConn := st.conn.(*tls.Conn) - connState := tlsConn.ConnectionState() - cert := connState.PeerCertificates[0] - - if got, want := cert.Subject.CommonName, "alt-domain"; got != want { - t.Errorf("CommonName: %v; want %v", got, want) - } -} - -// TestH2H1ServerPush tests server push using Link header field from -// backend server. -func TestH2H1ServerPush(t *testing.T) { - st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) { - // only resources marked as rel=preload are pushed - if !strings.HasPrefix(r.URL.Path, "/css/") { - w.Header().Add("Link", "; rel=preload, , ; rel=preload") - } - }) - defer st.Close() - - res, err := st.http2(requestParam{ - name: "TestH2H1ServerPush", - }) - if err != nil { - t.Fatalf("Error st.http2() = %v", err) - } - if got, want := res.status, 200; got != want { - t.Errorf("res.status: %v; want %v", got, want) - } - if got, want := len(res.pushResponse), 2; got != want { - t.Fatalf("len(res.pushResponse): %v; want %v", got, want) - } - mainCSS := res.pushResponse[0] - if got, want := mainCSS.status, 200; got != want { - t.Errorf("mainCSS.status: %v; want %v", got, want) - } - themeCSS := res.pushResponse[1] - if got, want := themeCSS.status, 200; got != want { - t.Errorf("themeCSS.status: %v; want %v", got, want) - } -} - -// TestH2H1RequestTrailer tests request trailer part is forwarded to -// backend. -func TestH2H1RequestTrailer(t *testing.T) { - st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) { - buf := make([]byte, 4096) - for { - _, err := r.Body.Read(buf) - if err == io.EOF { - break - } - if err != nil { - t.Fatalf("r.Body.Read() = %v", err) - } - } - if got, want := r.Trailer.Get("foo"), "bar"; got != want { - t.Errorf("r.Trailer.Get(foo): %v; want %v", got, want) - } - }) - defer st.Close() - - res, err := st.http2(requestParam{ - name: "TestH2H1RequestTrailer", - body: []byte("1"), - trailer: []hpack.HeaderField{ - pair("foo", "bar"), - }, - }) - if err != nil { - t.Fatalf("Error st.http2() = %v", err) - } - if got, want := res.status, 200; got != want { - t.Errorf("res.status: %v; want %v", got, want) - } -} - -// TestH2H1HeaderFieldBuffer tests that request with header fields -// larger than configured buffer size is rejected. -func TestH2H1HeaderFieldBuffer(t *testing.T) { - st := newServerTester([]string{"--header-field-buffer=10"}, t, func(w http.ResponseWriter, r *http.Request) { - t.Fatal("execution path should not be here") - }) - defer st.Close() - - res, err := st.http2(requestParam{ - name: "TestH2H1HeaderFieldBuffer", - }) - if err != nil { - t.Fatalf("Error st.http2() = %v", err) - } - if got, want := res.status, 431; got != want { - t.Errorf("status: %v; want %v", got, want) - } -} - -// TestH2H1HeaderFields tests that request with header fields more -// than configured number is rejected. -func TestH2H1HeaderFields(t *testing.T) { - st := newServerTester([]string{"--max-header-fields=1"}, t, func(w http.ResponseWriter, r *http.Request) { - t.Fatal("execution path should not be here") - }) - defer st.Close() - - res, err := st.http2(requestParam{ - name: "TestH2H1HeaderFields", - // we have at least 4 pseudo-header fields sent, and - // that ensures that buffer limit exceeds. - }) - if err != nil { - t.Fatalf("Error st.http2() = %v", err) - } - if got, want := res.status, 431; got != want { - t.Errorf("status: %v; want %v", got, want) - } -} - -// TestH2H1Upgrade tests HTTP Upgrade to HTTP/2 -func TestH2H1Upgrade(t *testing.T) { - st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) {}) - defer st.Close() - - res, err := st.http1(requestParam{ - name: "TestH2H1Upgrade", - header: []hpack.HeaderField{ - pair("Connection", "Upgrade, HTTP2-Settings"), - pair("Upgrade", "h2c"), - pair("HTTP2-Settings", "AAMAAABkAAQAAP__"), - }, - }) - - if err != nil { - t.Fatalf("Error st.http1() = %v", err) - } - - if got, want := res.status, 101; got != want { - t.Errorf("res.status: %v; want %v", got, want) - } - - res, err = st.http2(requestParam{ - httpUpgrade: true, - }) - if err != nil { - t.Fatalf("Error st.http2() = %v", err) - } - if got, want := res.status, 200; got != want { - t.Errorf("res.status: %v; want %v", got, want) - } -} - -// TestH2H1GracefulShutdown tests graceful shutdown. -func TestH2H1GracefulShutdown(t *testing.T) { - st := newServerTester(nil, t, noopHandler) - defer st.Close() - - fmt.Fprint(st.conn, "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n") - if err := st.fr.WriteSettings(); err != nil { - t.Fatalf("st.fr.WriteSettings(): %v", err) - } - - header := []hpack.HeaderField{ - pair(":method", "GET"), - pair(":scheme", "http"), - pair(":authority", st.authority), - pair(":path", "/"), - } - - for _, h := range header { - _ = st.enc.WriteField(h) - } - - if err := st.fr.WriteHeaders(http2.HeadersFrameParam{ - StreamID: 1, - EndStream: false, - EndHeaders: true, - BlockFragment: st.headerBlkBuf.Bytes(), - }); err != nil { - t.Fatalf("st.fr.WriteHeaders(): %v", err) - } - - // send SIGQUIT signal to nghttpx to perform graceful shutdown - st.cmd.Process.Signal(syscall.SIGQUIT) - - // after signal, finish request body - if err := st.fr.WriteData(1, true, nil); err != nil { - t.Fatalf("st.fr.WriteData(): %v", err) - } - - numGoAway := 0 - - for { - fr, err := st.readFrame() - if err != nil { - if err == io.EOF { - want := 2 - if got := numGoAway; got != want { - t.Fatalf("numGoAway: %v; want %v", got, want) - } - return - } - t.Fatalf("st.readFrame(): %v", err) - } - switch f := fr.(type) { - case *http2.GoAwayFrame: - numGoAway += 1 - want := http2.ErrCodeNo - if got := f.ErrCode; got != want { - t.Fatalf("f.ErrCode(%v): %v; want %v", numGoAway, got, want) - } - switch numGoAway { - case 1: - want := (uint32(1) << 31) - 1 - if got := f.LastStreamID; got != want { - t.Fatalf("f.LastStreamID(%v): %v; want %v", numGoAway, got, want) - } - case 2: - want := uint32(1) - if got := f.LastStreamID; got != want { - t.Fatalf("f.LastStreamID(%v): %v; want %v", numGoAway, got, want) - } - case 3: - t.Fatalf("too many GOAWAYs received") - } - } - } -} - -// TestH2H2MultipleResponseCL tests that server returns error if -// multiple Content-Length response header fields are received. -func TestH2H2MultipleResponseCL(t *testing.T) { - st := newServerTester([]string{"--http2-bridge"}, t, func(w http.ResponseWriter, r *http.Request) { - w.Header().Add("content-length", "1") - w.Header().Add("content-length", "1") - }) - defer st.Close() - - res, err := st.http2(requestParam{ - name: "TestH2H2MultipleResponseCL", - }) - if err != nil { - t.Fatalf("Error st.http2() = %v", err) - } - if got, want := res.errCode, http2.ErrCodeInternal; got != want { - t.Errorf("res.errCode: %v; want %v", got, want) - } -} - -// TestH2H2InvalidResponseCL tests that server returns error if -// Content-Length response header field value cannot be parsed as a -// number. -func TestH2H2InvalidResponseCL(t *testing.T) { - st := newServerTester([]string{"--http2-bridge"}, t, func(w http.ResponseWriter, r *http.Request) { - w.Header().Add("content-length", "") - }) - defer st.Close() - - res, err := st.http2(requestParam{ - name: "TestH2H2InvalidResponseCL", - }) - if err != nil { - t.Fatalf("Error st.http2() = %v", err) - } - if got, want := res.errCode, http2.ErrCodeInternal; got != want { - t.Errorf("res.errCode: %v; want %v", got, want) - } -} - -// TestH2H2ConnectFailure tests that server handles the situation that -// connection attempt to HTTP/2 backend failed. -func TestH2H2ConnectFailure(t *testing.T) { - st := newServerTester([]string{"--http2-bridge"}, t, noopHandler) - defer st.Close() - - // simulate backend connect attempt failure - st.ts.Close() - - res, err := st.http2(requestParam{ - name: "TestH2H2ConnectFailure", - }) - if err != nil { - t.Fatalf("Error st.http2() = %v", err) - } - want := 503 - if got := res.status; got != want { - t.Errorf("status: %v; want %v", got, want) - } -} - -// TestH2H2HostRewrite tests that server rewrites host header field -func TestH2H2HostRewrite(t *testing.T) { - st := newServerTester([]string{"--http2-bridge"}, t, func(w http.ResponseWriter, r *http.Request) { - w.Header().Add("request-host", r.Host) - }) - defer st.Close() - - res, err := st.http2(requestParam{ - name: "TestH2H2HostRewrite", - }) - if err != nil { - t.Fatalf("Error st.http2() = %v", err) - } - if got, want := res.status, 200; got != want { - t.Errorf("status: %v; want %v", got, want) - } - if got, want := res.header.Get("request-host"), st.backendHost; got != want { - t.Errorf("request-host: %v; want %v", got, want) - } -} - -// TestH2H2NoHostRewrite tests that server does not rewrite host -// header field -func TestH2H2NoHostRewrite(t *testing.T) { - st := newServerTester([]string{"--http2-bridge", "--no-host-rewrite"}, t, func(w http.ResponseWriter, r *http.Request) { - w.Header().Add("request-host", r.Host) - }) - defer st.Close() - - res, err := st.http2(requestParam{ - name: "TestH2H2NoHostRewrite", - }) - if err != nil { - t.Fatalf("Error st.http2() = %v", err) - } - if got, want := res.status, 200; got != want { - t.Errorf("status: %v; want %v", got, want) - } - if got, want := res.header.Get("request-host"), st.frontendHost; got != want { - t.Errorf("request-host: %v; want %v", got, want) - } -} diff --git a/integration-tests/nghttpx_spdy_test.go b/integration-tests/nghttpx_spdy_test.go deleted file mode 100644 index 232e1da..0000000 --- a/integration-tests/nghttpx_spdy_test.go +++ /dev/null @@ -1,232 +0,0 @@ -package nghttp2 - -import ( - "github.com/bradfitz/http2/hpack" - "golang.org/x/net/spdy" - "net/http" - "testing" -) - -// TestS3H1PlainGET tests whether simple SPDY GET request works. -func TestS3H1PlainGET(t *testing.T) { - st := newServerTesterTLS([]string{"--npn-list=spdy/3.1"}, t, noopHandler) - defer st.Close() - - res, err := st.spdy(requestParam{ - name: "TestS3H1PlainGET", - }) - if err != nil { - t.Fatalf("Error st.spdy() = %v", err) - } - - want := 200 - if got := res.status; got != want { - t.Errorf("status = %v; want %v", got, want) - } -} - -// TestS3H1BadRequestCL tests that server rejects request whose -// content-length header field value does not match its request body -// size. -func TestS3H1BadRequestCL(t *testing.T) { - st := newServerTesterTLS([]string{"--npn-list=spdy/3.1"}, t, noopHandler) - defer st.Close() - - // we set content-length: 1024, but the actual request body is - // 3 bytes. - res, err := st.spdy(requestParam{ - name: "TestS3H1BadRequestCL", - method: "POST", - header: []hpack.HeaderField{ - pair("content-length", "1024"), - }, - body: []byte("foo"), - }) - if err != nil { - t.Fatalf("Error st.spdy() = %v", err) - } - - want := spdy.ProtocolError - if got := res.spdyRstErrCode; got != want { - t.Errorf("res.spdyRstErrCode = %v; want %v", got, want) - } -} - -// TestS3H1MultipleRequestCL tests that server rejects request with -// multiple Content-Length request header fields. -func TestS3H1MultipleRequestCL(t *testing.T) { - st := newServerTesterTLS([]string{"--npn-list=spdy/3.1"}, t, func(w http.ResponseWriter, r *http.Request) { - t.Errorf("server should not forward bad request") - }) - defer st.Close() - - res, err := st.spdy(requestParam{ - name: "TestS3H1MultipleRequestCL", - header: []hpack.HeaderField{ - pair("content-length", "1"), - pair("content-length", "1"), - }, - }) - if err != nil { - t.Fatalf("Error st.spdy() = %v", err) - } - want := 400 - if got := res.status; got != want { - t.Errorf("status: %v; want %v", got, want) - } -} - -// TestS3H1InvalidRequestCL tests that server rejects request with -// Content-Length which cannot be parsed as a number. -func TestS3H1InvalidRequestCL(t *testing.T) { - st := newServerTesterTLS([]string{"--npn-list=spdy/3.1"}, t, func(w http.ResponseWriter, r *http.Request) { - t.Errorf("server should not forward bad request") - }) - defer st.Close() - - res, err := st.spdy(requestParam{ - name: "TestS3H1InvalidRequestCL", - header: []hpack.HeaderField{ - pair("content-length", ""), - }, - }) - if err != nil { - t.Fatalf("Error st.spdy() = %v", err) - } - want := 400 - if got := res.status; got != want { - t.Errorf("status: %v; want %v", got, want) - } -} - -// TestS3H1GenerateVia tests that server generates Via header field to and -// from backend server. -func TestS3H1GenerateVia(t *testing.T) { - st := newServerTesterTLS([]string{"--npn-list=spdy/3.1"}, t, func(w http.ResponseWriter, r *http.Request) { - if got, want := r.Header.Get("Via"), "1.1 nghttpx"; got != want { - t.Errorf("Via: %v; want %v", got, want) - } - }) - defer st.Close() - - res, err := st.spdy(requestParam{ - name: "TestS3H1GenerateVia", - }) - if err != nil { - t.Fatalf("Error st.spdy() = %v", err) - } - if got, want := res.header.Get("Via"), "1.1 nghttpx"; got != want { - t.Errorf("Via: %v; want %v", got, want) - } -} - -// TestS3H1AppendVia tests that server adds value to existing Via -// header field to and from backend server. -func TestS3H1AppendVia(t *testing.T) { - st := newServerTesterTLS([]string{"--npn-list=spdy/3.1"}, t, func(w http.ResponseWriter, r *http.Request) { - if got, want := r.Header.Get("Via"), "foo, 1.1 nghttpx"; got != want { - t.Errorf("Via: %v; want %v", got, want) - } - w.Header().Add("Via", "bar") - }) - defer st.Close() - - res, err := st.spdy(requestParam{ - name: "TestS3H1AppendVia", - header: []hpack.HeaderField{ - pair("via", "foo"), - }, - }) - if err != nil { - t.Fatalf("Error st.spdy() = %v", err) - } - if got, want := res.header.Get("Via"), "bar, 1.1 nghttpx"; got != want { - t.Errorf("Via: %v; want %v", got, want) - } -} - -// TestS3H1NoVia tests that server does not add value to existing Via -// header field to and from backend server. -func TestS3H1NoVia(t *testing.T) { - st := newServerTesterTLS([]string{"--npn-list=spdy/3.1", "--no-via"}, t, func(w http.ResponseWriter, r *http.Request) { - if got, want := r.Header.Get("Via"), "foo"; got != want { - t.Errorf("Via: %v; want %v", got, want) - } - w.Header().Add("Via", "bar") - }) - defer st.Close() - - res, err := st.spdy(requestParam{ - name: "TestS3H1NoVia", - header: []hpack.HeaderField{ - pair("via", "foo"), - }, - }) - if err != nil { - t.Fatalf("Error st.spdy() = %v", err) - } - if got, want := res.header.Get("Via"), "bar"; got != want { - t.Errorf("Via: %v; want %v", got, want) - } -} - -// TestS3H1HeaderFieldBuffer tests that request with header fields -// larger than configured buffer size is rejected. -func TestS3H1HeaderFieldBuffer(t *testing.T) { - st := newServerTesterTLS([]string{"--npn-list=spdy/3.1", "--header-field-buffer=10"}, t, func(w http.ResponseWriter, r *http.Request) { - t.Fatal("execution path should not be here") - }) - defer st.Close() - - res, err := st.spdy(requestParam{ - name: "TestS3H1HeaderFieldBuffer", - }) - if err != nil { - t.Fatalf("Error st.spdy() = %v", err) - } - if got, want := res.spdyRstErrCode, spdy.InternalError; got != want { - t.Errorf("res.spdyRstErrCode: %v; want %v", got, want) - } -} - -// TestS3H1HeaderFields tests that request with header fields more -// than configured number is rejected. -func TestS3H1HeaderFields(t *testing.T) { - st := newServerTesterTLS([]string{"--npn-list=spdy/3.1", "--max-header-fields=1"}, t, func(w http.ResponseWriter, r *http.Request) { - t.Fatal("execution path should not be here") - }) - defer st.Close() - - res, err := st.spdy(requestParam{ - name: "TestS3H1HeaderFields", - // we have at least 5 pseudo-header fields sent, and - // that ensures that buffer limit exceeds. - }) - if err != nil { - t.Fatalf("Error st.spdy() = %v", err) - } - if got, want := res.spdyRstErrCode, spdy.InternalError; got != want { - t.Errorf("res.spdyRstErrCode: %v; want %v", got, want) - } -} - -// TestS3H2ConnectFailure tests that server handles the situation that -// connection attempt to HTTP/2 backend failed. -func TestS3H2ConnectFailure(t *testing.T) { - st := newServerTesterTLS([]string{"--npn-list=spdy/3.1", "--http2-bridge"}, t, noopHandler) - defer st.Close() - - // simulate backend connect attempt failure - st.ts.Close() - - res, err := st.spdy(requestParam{ - name: "TestS3H2ConnectFailure", - }) - if err != nil { - t.Fatalf("Error st.spdy() = %v", err) - } - want := 503 - if got := res.status; got != want { - t.Errorf("status: %v; want %v", got, want) - } -} diff --git a/integration-tests/server.crt b/integration-tests/server.crt deleted file mode 100644 index c50fdaa..0000000 --- a/integration-tests/server.crt +++ /dev/null @@ -1,21 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDhTCCAm2gAwIBAgIJAOvIx8xIxgyOMA0GCSqGSIb3DQEBCwUAMFkxCzAJBgNV -BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX -aWRnaXRzIFB0eSBMdGQxEjAQBgNVBAMMCTEyNy4wLjAuMTAeFw0xNTAxMjMxMjI0 -MjdaFw0yNTAxMjAxMjI0MjdaMFkxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21l -LVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQxEjAQBgNV -BAMMCTEyNy4wLjAuMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMuI -QZRI/iBaxPTjTWGemt8tCEfzZWxuIW3hY/gIhwJDfH2SbourBh1s9vqcqhBq5vmo -kdfVQXAnNLjIG1uhWmcHuNnKrE5hU82N6i9RsmuM5TQRvhsamHri4G+EXJMu9GqF -Mso8g7MWpRSGKf+8gfjAVNwfCHFiu8oBcMmy3l54MFHgRLSveAMhiPB0e3Xlnpr5 -2bS/oGTx5ynwPgBpEn2FrpT4Z/aLCLzJ/ysgNH8BXEh7n/v7xM3vd5grqB039rd5 -JoxlWvp+4XpzKp5upaqmOcVUq4pDSFUQ3w6C+v33Z3OK6Qaon7GMxLv3Us3b7PZ3 -1CLoWJR2o3OSnUfO/gUCAwEAAaNQME4wHQYDVR0OBBYEFLc5JWPUUVx4GJesogMV -w2Rz0L3yMB8GA1UdIwQYMBaAFLc5JWPUUVx4GJesogMVw2Rz0L3yMAwGA1UdEwQF -MAMBAf8wDQYJKoZIhvcNAQELBQADggEBAAP/cJWpM+GEjmVYHFacKTdbXBMox2Xn -QY2NLm00WPOGvKnO7czMFfX/pEmiq71kD45rLLfbaJP205QpxqiAIvhFhuq50Co7 -sTDtwcDTPLX9H7Ugjt4sTMPiwC14uVXFfoT/J46zMjXwP00qKyfszc2tkIgHfrTl -h4M1hkdfmMximir/Ii7TdYYJ3oGS8tdcYb6D4DZwAljKmxF6iUOwFCUgpTmqDBT5 -irXY8D27DzuNN5Pg07rwAlwXLCzrJE10UtO4MmRVXwpzmoaRQD4/tna6bZzdetvs -gPdGP6W1o0q85gullieMJWeKyQA/wasoE7fypn4pHAdTZm/vH+v7GHg= ------END CERTIFICATE----- diff --git a/integration-tests/server.key b/integration-tests/server.key deleted file mode 100644 index 0fc02bb..0000000 --- a/integration-tests/server.key +++ /dev/null @@ -1,28 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDLiEGUSP4gWsT0 -401hnprfLQhH82VsbiFt4WP4CIcCQ3x9km6LqwYdbPb6nKoQaub5qJHX1UFwJzS4 -yBtboVpnB7jZyqxOYVPNjeovUbJrjOU0Eb4bGph64uBvhFyTLvRqhTLKPIOzFqUU -hin/vIH4wFTcHwhxYrvKAXDJst5eeDBR4ES0r3gDIYjwdHt15Z6a+dm0v6Bk8ecp -8D4AaRJ9ha6U+Gf2iwi8yf8rIDR/AVxIe5/7+8TN73eYK6gdN/a3eSaMZVr6fuF6 -cyqebqWqpjnFVKuKQ0hVEN8Ogvr992dziukGqJ+xjMS791LN2+z2d9Qi6FiUdqNz -kp1Hzv4FAgMBAAECggEACG26GYP0Ui6wHVwUZkiFLVzWDPS9bIIbDEvbMfhYbvWQ -gDrCLTKF7E4I5FP8jvV+XzRl5cRFE3nsKwLObzr9XWrqcsp73DsXl1mbKx58/ws0 -qrVZZBHz4pLmrHeUxduZ75dYhRuAcLgtWe48awTJdR2x5fO7C8cE89afbxrjLpJE -tVyiw6vVB0GfWTZodxtAFMTX1KVm4bTngXfg0NF1FBNHAX3Cm6t4YCE41hKSc0IQ -Jr3C4e9uj8poze1B17k79bGB8HNMbbc8Ws0sdbxi5xnY+HUA/mYQrmGXo8sdqiYC -EYCMqPm3iJrCmmpHukGf2Vt9k1aLlJ+lxOclSwFO+QKBgQDoRmoprfdmU20LyxYH -eVeVqggqmhNohwnuhIvOAyrWGUkbDsssqx2Vv82z0WHAAkwEvQ984UzaYWCCL3m3 -+JzpF2dz6aKhXIaYnXBlk3STMGUCDT5ysPvsin9z/unzkffh3vrbDBARGFYWG18x -eUyTDOVVeTZNHUJXGjRyiftCkwKBgQDgUkR6dHU4ciSt7Y0UkyAgtZ7POR41T05L -bcxbjJeqm6qlj+oP9WUk7JxeSEFUbrMiROABLPPqTwmGo4xrDRx/e7WrqN6QBKC+ -Y8CfalrKRb0np60x7Mxx0kbmHp5cwv9QDKznKViOYSgKxFrOFZyMAEXQdZ3FvjXF -OQWrw86kBwKBgQDXuxa9MWO3uUJtkqkaNfw/+FVvY/0kt09lJdxHci+l/IQmyl2w -Vhm7TRK7sXvtfvSl7gblgMgFiC2/nGKbmR/7ag5e3R98aVhlhMywuvyp/GfEORLI -KVNChfwMezVFUUx+j8BEFHcTuZuzGqcWZ0fUyER0V4k0pDlKdv9BZqBkWwKBgCdP -o3qGQCilMDJex/OMGPxCd9M+4kFbZZAobMC6cbXPU+dxwgYL7i67XGfVZ8WBJNlj -kpICK7irIzM6JBh6krzwlBTCIkbA2N6kopQNUl3SPOTfKKXwJp/nxs77HKuK7K09 -m2tjPoatFhRU9sjY1rdeMN3oTr7hp5CpfonsZaEvAoGAEPsZcDd4N9ap5bgaeDy9 -NOfLsIyaxT5k6moRIiy83QPihvCuECP16+r6M5tiSfgt/PtCimdjhRiqXzIHNRhh -Nfsv13vUtZgt8cYXuTdI4a8feKI7Q4876ME8Qp3WM5/UNZWq6/sWCuZFqbXUhqM0 -mwNEi5Zddzf8VsSL2gCraQg= ------END PRIVATE KEY----- diff --git a/integration-tests/server_tester.go b/integration-tests/server_tester.go deleted file mode 100644 index 87e593b..0000000 --- a/integration-tests/server_tester.go +++ /dev/null @@ -1,683 +0,0 @@ -package nghttp2 - -import ( - "bufio" - "bytes" - "crypto/tls" - "errors" - "fmt" - "github.com/bradfitz/http2" - "github.com/bradfitz/http2/hpack" - "github.com/tatsuhiro-t/go-nghttp2" - "golang.org/x/net/spdy" - "io" - "io/ioutil" - "net" - "net/http" - "net/http/httptest" - "net/url" - "os/exec" - "sort" - "strconv" - "strings" - "testing" - "time" -) - -const ( - serverBin = buildDir + "/src/nghttpx" - serverPort = 3009 - testDir = buildDir + "/integration-tests" -) - -func pair(name, value string) hpack.HeaderField { - return hpack.HeaderField{ - Name: name, - Value: value, - } -} - -type serverTester struct { - args []string // command-line arguments - cmd *exec.Cmd // test frontend server process, which is test subject - url string // test frontend server URL - t *testing.T - ts *httptest.Server // backend server - frontendHost string // frontend server host - backendHost string // backend server host - conn net.Conn // connection to frontend server - h2PrefaceSent bool // HTTP/2 preface was sent in conn - nextStreamID uint32 // next stream ID - fr *http2.Framer // HTTP/2 framer - spdyFr *spdy.Framer // SPDY/3.1 framer - headerBlkBuf bytes.Buffer // buffer to store encoded header block - enc *hpack.Encoder // HTTP/2 HPACK encoder - header http.Header // received header fields - dec *hpack.Decoder // HTTP/2 HPACK decoder - authority string // server's host:port - frCh chan http2.Frame // used for incoming HTTP/2 frame - spdyFrCh chan spdy.Frame // used for incoming SPDY frame - errCh chan error -} - -// newServerTester creates test context for plain TCP frontend -// connection. -func newServerTester(args []string, t *testing.T, handler http.HandlerFunc) *serverTester { - return newServerTesterInternal(args, t, handler, false, nil) -} - -// newServerTester creates test context for TLS frontend connection. -func newServerTesterTLS(args []string, t *testing.T, handler http.HandlerFunc) *serverTester { - return newServerTesterInternal(args, t, handler, true, nil) -} - -// newServerTester creates test context for TLS frontend connection -// with given clientConfig -func newServerTesterTLSConfig(args []string, t *testing.T, handler http.HandlerFunc, clientConfig *tls.Config) *serverTester { - return newServerTesterInternal(args, t, handler, true, clientConfig) -} - -// newServerTesterInternal creates test context. If frontendTLS is -// true, set up TLS frontend connection. -func newServerTesterInternal(args []string, t *testing.T, handler http.HandlerFunc, frontendTLS bool, clientConfig *tls.Config) *serverTester { - ts := httptest.NewUnstartedServer(handler) - - backendTLS := false - for _, k := range args { - switch k { - case "--http2-bridge": - backendTLS = true - } - } - if backendTLS { - nghttp2.ConfigureServer(ts.Config, &nghttp2.Server{}) - // According to httptest/server.go, we have to set - // NextProtos separately for ts.TLS. NextProtos set - // in nghttp2.ConfigureServer is effectively ignored. - ts.TLS = new(tls.Config) - ts.TLS.NextProtos = append(ts.TLS.NextProtos, "h2-14") - ts.StartTLS() - args = append(args, "-k") - } else { - ts.Start() - } - scheme := "http" - if frontendTLS { - scheme = "https" - args = append(args, testDir+"/server.key", testDir+"/server.crt") - } else { - args = append(args, "--frontend-no-tls") - } - - backendURL, err := url.Parse(ts.URL) - if err != nil { - t.Fatalf("Error parsing URL from httptest.Server: %v", err) - } - - // URL.Host looks like "127.0.0.1:8080", but we want - // "127.0.0.1,8080" - b := "-b" + strings.Replace(backendURL.Host, ":", ",", -1) - args = append(args, fmt.Sprintf("-f127.0.0.1,%v", serverPort), b, - "--errorlog-file="+testDir+"/log.txt", "-LINFO") - - authority := fmt.Sprintf("127.0.0.1:%v", serverPort) - - st := &serverTester{ - cmd: exec.Command(serverBin, args...), - t: t, - ts: ts, - url: fmt.Sprintf("%v://%v", scheme, authority), - frontendHost: fmt.Sprintf("127.0.0.1:%v", serverPort), - backendHost: backendURL.Host, - nextStreamID: 1, - authority: authority, - frCh: make(chan http2.Frame), - spdyFrCh: make(chan spdy.Frame), - errCh: make(chan error), - } - - if err := st.cmd.Start(); err != nil { - st.t.Fatalf("Error starting %v: %v", serverBin, err) - } - - retry := 0 - for { - var conn net.Conn - var err error - if frontendTLS { - var tlsConfig *tls.Config - if clientConfig == nil { - tlsConfig = new(tls.Config) - } else { - tlsConfig = clientConfig - } - tlsConfig.InsecureSkipVerify = true - tlsConfig.NextProtos = []string{"h2-14", "spdy/3.1"} - conn, err = tls.Dial("tcp", authority, tlsConfig) - } else { - conn, err = net.Dial("tcp", authority) - } - if err != nil { - retry += 1 - if retry >= 100 { - st.Close() - st.t.Fatalf("Error server is not responding too long; server command-line arguments may be invalid") - } - time.Sleep(150 * time.Millisecond) - continue - } - if frontendTLS { - tlsConn := conn.(*tls.Conn) - cs := tlsConn.ConnectionState() - if !cs.NegotiatedProtocolIsMutual { - st.Close() - st.t.Fatalf("Error negotiated next protocol is not mutual") - } - } - st.conn = conn - break - } - - st.fr = http2.NewFramer(st.conn, st.conn) - spdyFr, err := spdy.NewFramer(st.conn, st.conn) - if err != nil { - st.Close() - st.t.Fatalf("Error spdy.NewFramer: %v", err) - } - st.spdyFr = spdyFr - st.enc = hpack.NewEncoder(&st.headerBlkBuf) - st.dec = hpack.NewDecoder(4096, func(f hpack.HeaderField) { - st.header.Add(f.Name, f.Value) - }) - - return st -} - -func (st *serverTester) Close() { - if st.conn != nil { - st.conn.Close() - } - if st.cmd != nil { - st.cmd.Process.Kill() - st.cmd.Wait() - } - if st.ts != nil { - st.ts.Close() - } -} - -func (st *serverTester) readFrame() (http2.Frame, error) { - go func() { - f, err := st.fr.ReadFrame() - if err != nil { - st.errCh <- err - return - } - st.frCh <- f - }() - - select { - case f := <-st.frCh: - return f, nil - case err := <-st.errCh: - return nil, err - case <-time.After(5 * time.Second): - return nil, errors.New("timeout waiting for frame") - } -} - -func (st *serverTester) readSpdyFrame() (spdy.Frame, error) { - go func() { - f, err := st.spdyFr.ReadFrame() - if err != nil { - st.errCh <- err - return - } - st.spdyFrCh <- f - }() - - select { - case f := <-st.spdyFrCh: - return f, nil - case err := <-st.errCh: - return nil, err - case <-time.After(2 * time.Second): - return nil, errors.New("timeout waiting for frame") - } -} - -type requestParam struct { - name string // name for this request to identify the request in log easily - streamID uint32 // stream ID, automatically assigned if 0 - method string // method, defaults to GET - scheme string // scheme, defaults to http - authority string // authority, defaults to backend server address - path string // path, defaults to / - header []hpack.HeaderField // additional request header fields - body []byte // request body - trailer []hpack.HeaderField // trailer part - httpUpgrade bool // true if upgraded to HTTP/2 through HTTP Upgrade -} - -// wrapper for request body to set trailer part -type chunkedBodyReader struct { - trailer []hpack.HeaderField - trailerWritten bool - body io.Reader - req *http.Request -} - -func (cbr *chunkedBodyReader) Read(p []byte) (n int, err error) { - // document says that we have to set http.Request.Trailer - // after request was sent and before body returns EOF. - if !cbr.trailerWritten { - cbr.trailerWritten = true - for _, h := range cbr.trailer { - cbr.req.Trailer.Set(h.Name, h.Value) - } - } - return cbr.body.Read(p) -} - -func (st *serverTester) http1(rp requestParam) (*serverResponse, error) { - method := "GET" - if rp.method != "" { - method = rp.method - } - - var body io.Reader - var cbr *chunkedBodyReader - if rp.body != nil { - body = bytes.NewBuffer(rp.body) - if len(rp.trailer) != 0 { - cbr = &chunkedBodyReader{ - trailer: rp.trailer, - body: body, - } - body = cbr - } - } - - reqURL := st.url - - if rp.path != "" { - u, err := url.Parse(st.url) - if err != nil { - st.t.Fatalf("Error parsing URL from st.url %v: %v", st.url, err) - } - u.Path = rp.path - reqURL = u.String() - } - - req, err := http.NewRequest(method, reqURL, body) - if err != nil { - return nil, err - } - for _, h := range rp.header { - req.Header.Add(h.Name, h.Value) - } - req.Header.Add("Test-Case", rp.name) - if cbr != nil { - cbr.req = req - // this makes request use chunked encoding - req.ContentLength = -1 - req.Trailer = make(http.Header) - for _, h := range cbr.trailer { - req.Trailer.Set(h.Name, "") - } - } - if err := req.Write(st.conn); err != nil { - return nil, err - } - resp, err := http.ReadResponse(bufio.NewReader(st.conn), req) - if err != nil { - return nil, err - } - respBody, err := ioutil.ReadAll(resp.Body) - if err != nil { - return nil, err - } - resp.Body.Close() - - res := &serverResponse{ - status: resp.StatusCode, - header: resp.Header, - body: respBody, - connClose: resp.Close, - } - - return res, nil -} - -func (st *serverTester) spdy(rp requestParam) (*serverResponse, error) { - res := &serverResponse{} - - var id spdy.StreamId - if rp.streamID != 0 { - id = spdy.StreamId(rp.streamID) - if id >= spdy.StreamId(st.nextStreamID) && id%2 == 1 { - st.nextStreamID = uint32(id) + 2 - } - } else { - id = spdy.StreamId(st.nextStreamID) - st.nextStreamID += 2 - } - - method := "GET" - if rp.method != "" { - method = rp.method - } - - scheme := "http" - if rp.scheme != "" { - scheme = rp.scheme - } - - host := st.authority - if rp.authority != "" { - host = rp.authority - } - - path := "/" - if rp.path != "" { - path = rp.path - } - - header := make(http.Header) - header.Add(":method", method) - header.Add(":scheme", scheme) - header.Add(":host", host) - header.Add(":path", path) - header.Add(":version", "HTTP/1.1") - header.Add("test-case", rp.name) - for _, h := range rp.header { - header.Add(h.Name, h.Value) - } - - var synStreamFlags spdy.ControlFlags - if len(rp.body) == 0 { - synStreamFlags = spdy.ControlFlagFin - } - if err := st.spdyFr.WriteFrame(&spdy.SynStreamFrame{ - CFHeader: spdy.ControlFrameHeader{ - Flags: synStreamFlags, - }, - StreamId: id, - Headers: header, - }); err != nil { - return nil, err - } - - if len(rp.body) != 0 { - if err := st.spdyFr.WriteFrame(&spdy.DataFrame{ - StreamId: id, - Flags: spdy.DataFlagFin, - Data: rp.body, - }); err != nil { - return nil, err - } - } - -loop: - for { - fr, err := st.readSpdyFrame() - if err != nil { - return res, err - } - switch f := fr.(type) { - case *spdy.SynReplyFrame: - if f.StreamId != id { - break - } - res.header = cloneHeader(f.Headers) - if _, err := fmt.Sscan(res.header.Get(":status"), &res.status); err != nil { - return res, fmt.Errorf("Error parsing status code: %v", err) - } - if f.CFHeader.Flags&spdy.ControlFlagFin != 0 { - break loop - } - case *spdy.DataFrame: - if f.StreamId != id { - break - } - res.body = append(res.body, f.Data...) - if f.Flags&spdy.DataFlagFin != 0 { - break loop - } - case *spdy.RstStreamFrame: - if f.StreamId != id { - break - } - res.spdyRstErrCode = f.Status - break loop - case *spdy.GoAwayFrame: - if f.Status == spdy.GoAwayOK { - break - } - res.spdyGoAwayErrCode = f.Status - break loop - } - } - return res, nil -} - -func (st *serverTester) http2(rp requestParam) (*serverResponse, error) { - st.headerBlkBuf.Reset() - st.header = make(http.Header) - - var id uint32 - if rp.streamID != 0 { - id = rp.streamID - if id >= st.nextStreamID && id%2 == 1 { - st.nextStreamID = id + 2 - } - } else { - id = st.nextStreamID - st.nextStreamID += 2 - } - - if !st.h2PrefaceSent { - st.h2PrefaceSent = true - fmt.Fprint(st.conn, "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n") - if err := st.fr.WriteSettings(); err != nil { - return nil, err - } - } - - res := &serverResponse{ - streamID: id, - } - - streams := make(map[uint32]*serverResponse) - streams[id] = res - - if !rp.httpUpgrade { - method := "GET" - if rp.method != "" { - method = rp.method - } - _ = st.enc.WriteField(pair(":method", method)) - - scheme := "http" - if rp.scheme != "" { - scheme = rp.scheme - } - _ = st.enc.WriteField(pair(":scheme", scheme)) - - authority := st.authority - if rp.authority != "" { - authority = rp.authority - } - _ = st.enc.WriteField(pair(":authority", authority)) - - path := "/" - if rp.path != "" { - path = rp.path - } - _ = st.enc.WriteField(pair(":path", path)) - - _ = st.enc.WriteField(pair("test-case", rp.name)) - - for _, h := range rp.header { - _ = st.enc.WriteField(h) - } - - err := st.fr.WriteHeaders(http2.HeadersFrameParam{ - StreamID: id, - EndStream: len(rp.body) == 0 && len(rp.trailer) == 0, - EndHeaders: true, - BlockFragment: st.headerBlkBuf.Bytes(), - }) - if err != nil { - return nil, err - } - - if len(rp.body) != 0 { - // TODO we assume rp.body fits in 1 frame - if err := st.fr.WriteData(id, len(rp.trailer) == 0, rp.body); err != nil { - return nil, err - } - } - - if len(rp.trailer) != 0 { - st.headerBlkBuf.Reset() - for _, h := range rp.trailer { - _ = st.enc.WriteField(h) - } - err := st.fr.WriteHeaders(http2.HeadersFrameParam{ - StreamID: id, - EndStream: true, - EndHeaders: true, - BlockFragment: st.headerBlkBuf.Bytes(), - }) - if err != nil { - return nil, err - } - } - } -loop: - for { - fr, err := st.readFrame() - if err != nil { - return res, err - } - switch f := fr.(type) { - case *http2.HeadersFrame: - _, err := st.dec.Write(f.HeaderBlockFragment()) - if err != nil { - return res, err - } - sr, ok := streams[f.FrameHeader.StreamID] - if !ok { - st.header = make(http.Header) - break - } - sr.header = cloneHeader(st.header) - var status int - status, err = strconv.Atoi(sr.header.Get(":status")) - if err != nil { - return res, fmt.Errorf("Error parsing status code: %v", err) - } - sr.status = status - if f.StreamEnded() { - if streamEnded(res, streams, sr) { - break loop - } - } - case *http2.PushPromiseFrame: - _, err := st.dec.Write(f.HeaderBlockFragment()) - if err != nil { - return res, err - } - sr := &serverResponse{ - streamID: f.PromiseID, - reqHeader: cloneHeader(st.header), - } - streams[sr.streamID] = sr - case *http2.DataFrame: - sr, ok := streams[f.FrameHeader.StreamID] - if !ok { - break - } - sr.body = append(sr.body, f.Data()...) - if f.StreamEnded() { - if streamEnded(res, streams, sr) { - break loop - } - } - case *http2.RSTStreamFrame: - sr, ok := streams[f.FrameHeader.StreamID] - if !ok { - break - } - sr.errCode = f.ErrCode - if streamEnded(res, streams, sr) { - break loop - } - case *http2.GoAwayFrame: - if f.ErrCode == http2.ErrCodeNo { - break - } - res.errCode = f.ErrCode - res.connErr = true - break loop - case *http2.SettingsFrame: - if f.IsAck() { - break - } - if err := st.fr.WriteSettingsAck(); err != nil { - return res, err - } - } - } - sort.Sort(ByStreamID(res.pushResponse)) - return res, nil -} - -func streamEnded(mainSr *serverResponse, streams map[uint32]*serverResponse, sr *serverResponse) bool { - delete(streams, sr.streamID) - if mainSr.streamID != sr.streamID { - mainSr.pushResponse = append(mainSr.pushResponse, sr) - } - return len(streams) == 0 -} - -type serverResponse struct { - status int // HTTP status code - header http.Header // response header fields - body []byte // response body - streamID uint32 // stream ID in HTTP/2 - errCode http2.ErrCode // error code received in HTTP/2 RST_STREAM or GOAWAY - connErr bool // true if HTTP/2 connection error - spdyGoAwayErrCode spdy.GoAwayStatus // status code received in SPDY RST_STREAM - spdyRstErrCode spdy.RstStreamStatus // status code received in SPDY GOAWAY - connClose bool // Conection: close is included in response header in HTTP/1 test - reqHeader http.Header // http request header, currently only sotres pushed request header - pushResponse []*serverResponse // pushed response -} - -type ByStreamID []*serverResponse - -func (b ByStreamID) Len() int { - return len(b) -} - -func (b ByStreamID) Swap(i, j int) { - b[i], b[j] = b[j], b[i] -} - -func (b ByStreamID) Less(i, j int) bool { - return b[i].streamID < b[j].streamID -} - -func cloneHeader(h http.Header) http.Header { - h2 := make(http.Header, len(h)) - for k, vv := range h { - vv2 := make([]string, len(vv)) - copy(vv2, vv) - h2[k] = vv2 - } - return h2 -} - -func noopHandler(w http.ResponseWriter, r *http.Request) {} diff --git a/integration-tests/setenv b/integration-tests/setenv deleted file mode 100644 index 56879b9..0000000 --- a/integration-tests/setenv +++ /dev/null @@ -1,6 +0,0 @@ -#!/bin/sh -e - -export CGO_CFLAGS="-I/mnt/nghttp2/lib/includes -I/mnt/nghttp2/lib/includes" -export CGO_LDFLAGS="-L/mnt/nghttp2/lib/.libs" -export LD_LIBRARY_PATH="/mnt/nghttp2/lib/.libs" -"$@" diff --git a/integration-tests/setenv.in b/integration-tests/setenv.in deleted file mode 100644 index 2c2d3c9..0000000 --- a/integration-tests/setenv.in +++ /dev/null @@ -1,6 +0,0 @@ -#!/bin/sh -e - -export CGO_CFLAGS="-I@abs_top_srcdir@/lib/includes -I@abs_top_builddir@/lib/includes" -export CGO_LDFLAGS="-L@abs_top_builddir@/lib/.libs" -export LD_LIBRARY_PATH="@abs_top_builddir@/lib/.libs" -"$@" diff --git a/lib/Makefile.am b/lib/Makefile.am deleted file mode 100644 index 6d2393e..0000000 --- a/lib/Makefile.am +++ /dev/null @@ -1,67 +0,0 @@ -# nghttp2 - HTTP/2 C Library - -# Copyright (c) 2012, 2013 Tatsuhiro Tsujikawa - -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: - -# The above copyright notice and this permission notice shall be -# included in all copies or substantial portions of the Software. - -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -SUBDIRS = includes - -EXTRA_DIST = Makefile.msvc - -AM_CFLAGS = $(WARNCFLAGS) -AM_CPPFLAGS = -I$(srcdir)/includes -I$(builddir)/includes @DEFS@ - -pkgconfigdir = $(libdir)/pkgconfig -pkgconfig_DATA = libnghttp2.pc -DISTCLEANFILES = $(pkgconfig_DATA) - -lib_LTLIBRARIES = libnghttp2.la - -OBJECTS = nghttp2_pq.c nghttp2_map.c nghttp2_queue.c \ - nghttp2_frame.c \ - nghttp2_buf.c \ - nghttp2_stream.c nghttp2_outbound_item.c \ - nghttp2_session.c nghttp2_submit.c \ - nghttp2_helper.c \ - nghttp2_npn.c \ - nghttp2_hd.c nghttp2_hd_huffman.c nghttp2_hd_huffman_data.c \ - nghttp2_version.c \ - nghttp2_priority_spec.c \ - nghttp2_option.c \ - nghttp2_callbacks.c \ - nghttp2_mem.c \ - nghttp2_http.c - -HFILES = nghttp2_pq.h nghttp2_int.h nghttp2_map.h nghttp2_queue.h \ - nghttp2_frame.h \ - nghttp2_buf.h \ - nghttp2_session.h nghttp2_helper.h nghttp2_stream.h nghttp2_int.h \ - nghttp2_npn.h \ - nghttp2_submit.h nghttp2_outbound_item.h \ - nghttp2_net.h \ - nghttp2_hd.h nghttp2_hd_huffman.h \ - nghttp2_priority_spec.h \ - nghttp2_option.h \ - nghttp2_callbacks.h \ - nghttp2_mem.h \ - nghttp2_http.h - -libnghttp2_la_SOURCES = $(HFILES) $(OBJECTS) -libnghttp2_la_LDFLAGS = -no-undefined \ - -version-info $(LT_CURRENT):$(LT_REVISION):$(LT_AGE) diff --git a/lib/Makefile.in b/lib/Makefile.in deleted file mode 100644 index dd1cc37..0000000 --- a/lib/Makefile.in +++ /dev/null @@ -1,930 +0,0 @@ -# Makefile.in generated by automake 1.14.1 from Makefile.am. -# @configure_input@ - -# Copyright (C) 1994-2013 Free Software Foundation, Inc. - -# This Makefile.in is free software; the Free Software Foundation -# gives unlimited permission to copy and/or distribute it, -# with or without modifications, as long as this notice is preserved. - -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY, to the extent permitted by law; without -# even the implied warranty of MERCHANTABILITY or FITNESS FOR A -# PARTICULAR PURPOSE. - -@SET_MAKE@ - -# nghttp2 - HTTP/2 C Library - -# Copyright (c) 2012, 2013 Tatsuhiro Tsujikawa - -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: - -# The above copyright notice and this permission notice shall be -# included in all copies or substantial portions of the Software. - - -VPATH = @srcdir@ -am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)' -am__make_running_with_option = \ - case $${target_option-} in \ - ?) ;; \ - *) echo "am__make_running_with_option: internal error: invalid" \ - "target option '$${target_option-}' specified" >&2; \ - exit 1;; \ - esac; \ - has_opt=no; \ - sane_makeflags=$$MAKEFLAGS; \ - if $(am__is_gnu_make); then \ - sane_makeflags=$$MFLAGS; \ - else \ - case $$MAKEFLAGS in \ - *\\[\ \ ]*) \ - bs=\\; \ - sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ - | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ - esac; \ - fi; \ - skip_next=no; \ - strip_trailopt () \ - { \ - flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ - }; \ - for flg in $$sane_makeflags; do \ - test $$skip_next = yes && { skip_next=no; continue; }; \ - case $$flg in \ - *=*|--*) continue;; \ - -*I) strip_trailopt 'I'; skip_next=yes;; \ - -*I?*) strip_trailopt 'I';; \ - -*O) strip_trailopt 'O'; skip_next=yes;; \ - -*O?*) strip_trailopt 'O';; \ - -*l) strip_trailopt 'l'; skip_next=yes;; \ - -*l?*) strip_trailopt 'l';; \ - -[dEDm]) skip_next=yes;; \ - -[JT]) skip_next=yes;; \ - esac; \ - case $$flg in \ - *$$target_option*) has_opt=yes; break;; \ - esac; \ - done; \ - test $$has_opt = yes -am__make_dryrun = (target_option=n; $(am__make_running_with_option)) -am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) -pkgdatadir = $(datadir)/@PACKAGE@ -pkgincludedir = $(includedir)/@PACKAGE@ -pkglibdir = $(libdir)/@PACKAGE@ -pkglibexecdir = $(libexecdir)/@PACKAGE@ -am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd -install_sh_DATA = $(install_sh) -c -m 644 -install_sh_PROGRAM = $(install_sh) -c -install_sh_SCRIPT = $(install_sh) -c -INSTALL_HEADER = $(INSTALL_DATA) -transform = $(program_transform_name) -NORMAL_INSTALL = : -PRE_INSTALL = : -POST_INSTALL = : -NORMAL_UNINSTALL = : -PRE_UNINSTALL = : -POST_UNINSTALL = : -build_triplet = @build@ -host_triplet = @host@ -target_triplet = @target@ -subdir = lib -DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \ - $(srcdir)/libnghttp2.pc.in $(top_srcdir)/depcomp -ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 -am__aclocal_m4_deps = $(top_srcdir)/m4/ax_boost_asio.m4 \ - $(top_srcdir)/m4/ax_boost_base.m4 \ - $(top_srcdir)/m4/ax_boost_system.m4 \ - $(top_srcdir)/m4/ax_boost_thread.m4 \ - $(top_srcdir)/m4/ax_check_compile_flag.m4 \ - $(top_srcdir)/m4/ax_cxx_compile_stdcxx_11.m4 \ - $(top_srcdir)/m4/ax_have_epoll.m4 \ - $(top_srcdir)/m4/ax_python_devel.m4 \ - $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/libxml2.m4 \ - $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ - $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ - $(top_srcdir)/configure.ac -am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ - $(ACLOCAL_M4) -mkinstalldirs = $(install_sh) -d -CONFIG_HEADER = $(top_builddir)/config.h -CONFIG_CLEAN_FILES = libnghttp2.pc -CONFIG_CLEAN_VPATH_FILES = -am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; -am__vpath_adj = case $$p in \ - $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ - *) f=$$p;; \ - esac; -am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; -am__install_max = 40 -am__nobase_strip_setup = \ - srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` -am__nobase_strip = \ - for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" -am__nobase_list = $(am__nobase_strip_setup); \ - for p in $$list; do echo "$$p $$p"; done | \ - sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ - $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ - if (++n[$$2] == $(am__install_max)) \ - { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ - END { for (dir in files) print dir, files[dir] }' -am__base_list = \ - sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ - sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' -am__uninstall_files_from_dir = { \ - test -z "$$files" \ - || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ - || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ - $(am__cd) "$$dir" && rm -f $$files; }; \ - } -am__installdirs = "$(DESTDIR)$(libdir)" "$(DESTDIR)$(pkgconfigdir)" -LTLIBRARIES = $(lib_LTLIBRARIES) -libnghttp2_la_LIBADD = -am__objects_1 = -am__objects_2 = nghttp2_pq.lo nghttp2_map.lo nghttp2_queue.lo \ - nghttp2_frame.lo nghttp2_buf.lo nghttp2_stream.lo \ - nghttp2_outbound_item.lo nghttp2_session.lo nghttp2_submit.lo \ - nghttp2_helper.lo nghttp2_npn.lo nghttp2_hd.lo \ - nghttp2_hd_huffman.lo nghttp2_hd_huffman_data.lo \ - nghttp2_version.lo nghttp2_priority_spec.lo nghttp2_option.lo \ - nghttp2_callbacks.lo nghttp2_mem.lo nghttp2_http.lo -am_libnghttp2_la_OBJECTS = $(am__objects_1) $(am__objects_2) -libnghttp2_la_OBJECTS = $(am_libnghttp2_la_OBJECTS) -AM_V_lt = $(am__v_lt_@AM_V@) -am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) -am__v_lt_0 = --silent -am__v_lt_1 = -libnghttp2_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ - $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ - $(libnghttp2_la_LDFLAGS) $(LDFLAGS) -o $@ -AM_V_P = $(am__v_P_@AM_V@) -am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) -am__v_P_0 = false -am__v_P_1 = : -AM_V_GEN = $(am__v_GEN_@AM_V@) -am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) -am__v_GEN_0 = @echo " GEN " $@; -am__v_GEN_1 = -AM_V_at = $(am__v_at_@AM_V@) -am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) -am__v_at_0 = @ -am__v_at_1 = -DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) -depcomp = $(SHELL) $(top_srcdir)/depcomp -am__depfiles_maybe = depfiles -am__mv = mv -f -COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ - $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ - $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ - $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ - $(AM_CFLAGS) $(CFLAGS) -AM_V_CC = $(am__v_CC_@AM_V@) -am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) -am__v_CC_0 = @echo " CC " $@; -am__v_CC_1 = -CCLD = $(CC) -LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ - $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ - $(AM_LDFLAGS) $(LDFLAGS) -o $@ -AM_V_CCLD = $(am__v_CCLD_@AM_V@) -am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) -am__v_CCLD_0 = @echo " CCLD " $@; -am__v_CCLD_1 = -SOURCES = $(libnghttp2_la_SOURCES) -DIST_SOURCES = $(libnghttp2_la_SOURCES) -RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \ - ctags-recursive dvi-recursive html-recursive info-recursive \ - install-data-recursive install-dvi-recursive \ - install-exec-recursive install-html-recursive \ - install-info-recursive install-pdf-recursive \ - install-ps-recursive install-recursive installcheck-recursive \ - installdirs-recursive pdf-recursive ps-recursive \ - tags-recursive uninstall-recursive -am__can_run_installinfo = \ - case $$AM_UPDATE_INFO_DIR in \ - n|no|NO) false;; \ - *) (install-info --version) >/dev/null 2>&1;; \ - esac -DATA = $(pkgconfig_DATA) -RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \ - distclean-recursive maintainer-clean-recursive -am__recursive_targets = \ - $(RECURSIVE_TARGETS) \ - $(RECURSIVE_CLEAN_TARGETS) \ - $(am__extra_recursive_targets) -AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \ - distdir -am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) -# Read a list of newline-separated strings from the standard input, -# and print each of them once, without duplicates. Input order is -# *not* preserved. -am__uniquify_input = $(AWK) '\ - BEGIN { nonempty = 0; } \ - { items[$$0] = 1; nonempty = 1; } \ - END { if (nonempty) { for (i in items) print i; }; } \ -' -# Make sure the list of sources is unique. This is necessary because, -# e.g., the same source file might be shared among _SOURCES variables -# for different programs/libraries. -am__define_uniq_tagged_files = \ - list='$(am__tagged_files)'; \ - unique=`for i in $$list; do \ - if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ - done | $(am__uniquify_input)` -ETAGS = etags -CTAGS = ctags -DIST_SUBDIRS = $(SUBDIRS) -DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) -am__relativize = \ - dir0=`pwd`; \ - sed_first='s,^\([^/]*\)/.*$$,\1,'; \ - sed_rest='s,^[^/]*/*,,'; \ - sed_last='s,^.*/\([^/]*\)$$,\1,'; \ - sed_butlast='s,/*[^/]*$$,,'; \ - while test -n "$$dir1"; do \ - first=`echo "$$dir1" | sed -e "$$sed_first"`; \ - if test "$$first" != "."; then \ - if test "$$first" = ".."; then \ - dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \ - dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \ - else \ - first2=`echo "$$dir2" | sed -e "$$sed_first"`; \ - if test "$$first2" = "$$first"; then \ - dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \ - else \ - dir2="../$$dir2"; \ - fi; \ - dir0="$$dir0"/"$$first"; \ - fi; \ - fi; \ - dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \ - done; \ - reldir="$$dir2" -ACLOCAL = @ACLOCAL@ -AMTAR = @AMTAR@ -AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ -APPLDFLAGS = @APPLDFLAGS@ -AR = @AR@ -AUTOCONF = @AUTOCONF@ -AUTOHEADER = @AUTOHEADER@ -AUTOMAKE = @AUTOMAKE@ -AWK = @AWK@ -BOOST_ASIO_LIB = @BOOST_ASIO_LIB@ -BOOST_CPPFLAGS = @BOOST_CPPFLAGS@ -BOOST_LDFLAGS = @BOOST_LDFLAGS@ -BOOST_SYSTEM_LIB = @BOOST_SYSTEM_LIB@ -BOOST_THREAD_LIB = @BOOST_THREAD_LIB@ -CC = @CC@ -CCDEPMODE = @CCDEPMODE@ -CFLAGS = @CFLAGS@ -CPP = @CPP@ -CPPFLAGS = @CPPFLAGS@ -CUNIT_CFLAGS = @CUNIT_CFLAGS@ -CUNIT_LIBS = @CUNIT_LIBS@ -CXX = @CXX@ -CXXCPP = @CXXCPP@ -CXXDEPMODE = @CXXDEPMODE@ -CXXFLAGS = @CXXFLAGS@ -CYGPATH_W = @CYGPATH_W@ -CYTHON = @CYTHON@ -DEFS = @DEFS@ -DEPDIR = @DEPDIR@ -DLLTOOL = @DLLTOOL@ -DSYMUTIL = @DSYMUTIL@ -DUMPBIN = @DUMPBIN@ -ECHO_C = @ECHO_C@ -ECHO_N = @ECHO_N@ -ECHO_T = @ECHO_T@ -EGREP = @EGREP@ -EXEEXT = @EXEEXT@ -FGREP = @FGREP@ -GREP = @GREP@ -HAVE_CXX11 = @HAVE_CXX11@ -INSTALL = @INSTALL@ -INSTALL_DATA = @INSTALL_DATA@ -INSTALL_PROGRAM = @INSTALL_PROGRAM@ -INSTALL_SCRIPT = @INSTALL_SCRIPT@ -INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ -JANSSON_CFLAGS = @JANSSON_CFLAGS@ -JANSSON_LIBS = @JANSSON_LIBS@ -JEMALLOC_LIBS = @JEMALLOC_LIBS@ -LD = @LD@ -LDFLAGS = @LDFLAGS@ -LIBEVENT_OPENSSL_CFLAGS = @LIBEVENT_OPENSSL_CFLAGS@ -LIBEVENT_OPENSSL_LIBS = @LIBEVENT_OPENSSL_LIBS@ -LIBEV_CFLAGS = @LIBEV_CFLAGS@ -LIBEV_LIBS = @LIBEV_LIBS@ -LIBOBJS = @LIBOBJS@ -LIBS = @LIBS@ -LIBSPDYLAY_CFLAGS = @LIBSPDYLAY_CFLAGS@ -LIBSPDYLAY_LIBS = @LIBSPDYLAY_LIBS@ -LIBTOOL = @LIBTOOL@ -LIPO = @LIPO@ -LN_S = @LN_S@ -LTLIBOBJS = @LTLIBOBJS@ -LT_AGE = @LT_AGE@ -LT_CURRENT = @LT_CURRENT@ -LT_REVISION = @LT_REVISION@ -MAKEINFO = @MAKEINFO@ -MANIFEST_TOOL = @MANIFEST_TOOL@ -MKDIR_P = @MKDIR_P@ -NM = @NM@ -NMEDIT = @NMEDIT@ -OBJDUMP = @OBJDUMP@ -OBJEXT = @OBJEXT@ -OPENSSL_CFLAGS = @OPENSSL_CFLAGS@ -OPENSSL_LIBS = @OPENSSL_LIBS@ -OTOOL = @OTOOL@ -OTOOL64 = @OTOOL64@ -PACKAGE = @PACKAGE@ -PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ -PACKAGE_NAME = @PACKAGE_NAME@ -PACKAGE_STRING = @PACKAGE_STRING@ -PACKAGE_TARNAME = @PACKAGE_TARNAME@ -PACKAGE_URL = @PACKAGE_URL@ -PACKAGE_VERSION = @PACKAGE_VERSION@ -PACKAGE_VERSION_NUM = @PACKAGE_VERSION_NUM@ -PATH_SEPARATOR = @PATH_SEPARATOR@ -PKG_CONFIG = @PKG_CONFIG@ -PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ -PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ -PYTHON = @PYTHON@ -PYTHON_CPPFLAGS = @PYTHON_CPPFLAGS@ -PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@ -PYTHON_EXTRA_LDFLAGS = @PYTHON_EXTRA_LDFLAGS@ -PYTHON_EXTRA_LIBS = @PYTHON_EXTRA_LIBS@ -PYTHON_LDFLAGS = @PYTHON_LDFLAGS@ -PYTHON_PLATFORM = @PYTHON_PLATFORM@ -PYTHON_PREFIX = @PYTHON_PREFIX@ -PYTHON_SITE_PKG = @PYTHON_SITE_PKG@ -PYTHON_VERSION = @PYTHON_VERSION@ -RANLIB = @RANLIB@ -SED = @SED@ -SET_MAKE = @SET_MAKE@ -SHELL = @SHELL@ -STRIP = @STRIP@ -TESTLDADD = @TESTLDADD@ -VERSION = @VERSION@ -WARNCFLAGS = @WARNCFLAGS@ -XML2_CONFIG = @XML2_CONFIG@ -XML_CPPFLAGS = @XML_CPPFLAGS@ -XML_LIBS = @XML_LIBS@ -ZLIB_CFLAGS = @ZLIB_CFLAGS@ -ZLIB_LIBS = @ZLIB_LIBS@ -abs_builddir = @abs_builddir@ -abs_srcdir = @abs_srcdir@ -abs_top_builddir = @abs_top_builddir@ -abs_top_srcdir = @abs_top_srcdir@ -ac_ct_AR = @ac_ct_AR@ -ac_ct_CC = @ac_ct_CC@ -ac_ct_CXX = @ac_ct_CXX@ -ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ -am__include = @am__include@ -am__leading_dot = @am__leading_dot@ -am__quote = @am__quote@ -am__tar = @am__tar@ -am__untar = @am__untar@ -bindir = @bindir@ -build = @build@ -build_alias = @build_alias@ -build_cpu = @build_cpu@ -build_os = @build_os@ -build_vendor = @build_vendor@ -builddir = @builddir@ -datadir = @datadir@ -datarootdir = @datarootdir@ -docdir = @docdir@ -dvidir = @dvidir@ -exec_prefix = @exec_prefix@ -host = @host@ -host_alias = @host_alias@ -host_cpu = @host_cpu@ -host_os = @host_os@ -host_vendor = @host_vendor@ -htmldir = @htmldir@ -includedir = @includedir@ -infodir = @infodir@ -install_sh = @install_sh@ -libdir = @libdir@ -libexecdir = @libexecdir@ -localedir = @localedir@ -localstatedir = @localstatedir@ -mandir = @mandir@ -mkdir_p = @mkdir_p@ -oldincludedir = @oldincludedir@ -pdfdir = @pdfdir@ -pkgpyexecdir = @pkgpyexecdir@ -pkgpythondir = @pkgpythondir@ -prefix = @prefix@ -program_transform_name = @program_transform_name@ -psdir = @psdir@ -pyexecdir = @pyexecdir@ -pythondir = @pythondir@ -sbindir = @sbindir@ -sharedstatedir = @sharedstatedir@ -srcdir = @srcdir@ -sysconfdir = @sysconfdir@ -target = @target@ -target_alias = @target_alias@ -target_cpu = @target_cpu@ -target_os = @target_os@ -target_vendor = @target_vendor@ -top_build_prefix = @top_build_prefix@ -top_builddir = @top_builddir@ -top_srcdir = @top_srcdir@ - -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -SUBDIRS = includes -EXTRA_DIST = Makefile.msvc -AM_CFLAGS = $(WARNCFLAGS) -AM_CPPFLAGS = -I$(srcdir)/includes -I$(builddir)/includes @DEFS@ -pkgconfigdir = $(libdir)/pkgconfig -pkgconfig_DATA = libnghttp2.pc -DISTCLEANFILES = $(pkgconfig_DATA) -lib_LTLIBRARIES = libnghttp2.la -OBJECTS = nghttp2_pq.c nghttp2_map.c nghttp2_queue.c \ - nghttp2_frame.c \ - nghttp2_buf.c \ - nghttp2_stream.c nghttp2_outbound_item.c \ - nghttp2_session.c nghttp2_submit.c \ - nghttp2_helper.c \ - nghttp2_npn.c \ - nghttp2_hd.c nghttp2_hd_huffman.c nghttp2_hd_huffman_data.c \ - nghttp2_version.c \ - nghttp2_priority_spec.c \ - nghttp2_option.c \ - nghttp2_callbacks.c \ - nghttp2_mem.c \ - nghttp2_http.c - -HFILES = nghttp2_pq.h nghttp2_int.h nghttp2_map.h nghttp2_queue.h \ - nghttp2_frame.h \ - nghttp2_buf.h \ - nghttp2_session.h nghttp2_helper.h nghttp2_stream.h nghttp2_int.h \ - nghttp2_npn.h \ - nghttp2_submit.h nghttp2_outbound_item.h \ - nghttp2_net.h \ - nghttp2_hd.h nghttp2_hd_huffman.h \ - nghttp2_priority_spec.h \ - nghttp2_option.h \ - nghttp2_callbacks.h \ - nghttp2_mem.h \ - nghttp2_http.h - -libnghttp2_la_SOURCES = $(HFILES) $(OBJECTS) -libnghttp2_la_LDFLAGS = -no-undefined \ - -version-info $(LT_CURRENT):$(LT_REVISION):$(LT_AGE) - -all: all-recursive - -.SUFFIXES: -.SUFFIXES: .c .lo .o .obj -$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) - @for dep in $?; do \ - case '$(am__configure_deps)' in \ - *$$dep*) \ - ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ - && { if test -f $@; then exit 0; else break; fi; }; \ - exit 1;; \ - esac; \ - done; \ - echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu lib/Makefile'; \ - $(am__cd) $(top_srcdir) && \ - $(AUTOMAKE) --gnu lib/Makefile -.PRECIOUS: Makefile -Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status - @case '$?' in \ - *config.status*) \ - cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ - *) \ - echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ - cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ - esac; - -$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) - cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh - -$(top_srcdir)/configure: $(am__configure_deps) - cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh -$(ACLOCAL_M4): $(am__aclocal_m4_deps) - cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh -$(am__aclocal_m4_deps): -libnghttp2.pc: $(top_builddir)/config.status $(srcdir)/libnghttp2.pc.in - cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ - -install-libLTLIBRARIES: $(lib_LTLIBRARIES) - @$(NORMAL_INSTALL) - @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \ - list2=; for p in $$list; do \ - if test -f $$p; then \ - list2="$$list2 $$p"; \ - else :; fi; \ - done; \ - test -z "$$list2" || { \ - echo " $(MKDIR_P) '$(DESTDIR)$(libdir)'"; \ - $(MKDIR_P) "$(DESTDIR)$(libdir)" || exit 1; \ - echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(libdir)'"; \ - $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(libdir)"; \ - } - -uninstall-libLTLIBRARIES: - @$(NORMAL_UNINSTALL) - @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \ - for p in $$list; do \ - $(am__strip_dir) \ - echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$f'"; \ - $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$f"; \ - done - -clean-libLTLIBRARIES: - -test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES) - @list='$(lib_LTLIBRARIES)'; \ - locs=`for p in $$list; do echo $$p; done | \ - sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ - sort -u`; \ - test -z "$$locs" || { \ - echo rm -f $${locs}; \ - rm -f $${locs}; \ - } - -libnghttp2.la: $(libnghttp2_la_OBJECTS) $(libnghttp2_la_DEPENDENCIES) $(EXTRA_libnghttp2_la_DEPENDENCIES) - $(AM_V_CCLD)$(libnghttp2_la_LINK) -rpath $(libdir) $(libnghttp2_la_OBJECTS) $(libnghttp2_la_LIBADD) $(LIBS) - -mostlyclean-compile: - -rm -f *.$(OBJEXT) - -distclean-compile: - -rm -f *.tab.c - -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_buf.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_callbacks.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_frame.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_hd.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_hd_huffman.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_hd_huffman_data.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_helper.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_http.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_map.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_mem.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_npn.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_option.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_outbound_item.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_pq.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_priority_spec.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_queue.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_session.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_stream.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_submit.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_version.Plo@am__quote@ - -.c.o: -@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\ -@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ -@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< - -.c.obj: -@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\ -@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\ -@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` - -.c.lo: -@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\ -@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ -@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< - -mostlyclean-libtool: - -rm -f *.lo - -clean-libtool: - -rm -rf .libs _libs -install-pkgconfigDATA: $(pkgconfig_DATA) - @$(NORMAL_INSTALL) - @list='$(pkgconfig_DATA)'; test -n "$(pkgconfigdir)" || list=; \ - if test -n "$$list"; then \ - echo " $(MKDIR_P) '$(DESTDIR)$(pkgconfigdir)'"; \ - $(MKDIR_P) "$(DESTDIR)$(pkgconfigdir)" || exit 1; \ - fi; \ - for p in $$list; do \ - if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ - echo "$$d$$p"; \ - done | $(am__base_list) | \ - while read files; do \ - echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(pkgconfigdir)'"; \ - $(INSTALL_DATA) $$files "$(DESTDIR)$(pkgconfigdir)" || exit $$?; \ - done - -uninstall-pkgconfigDATA: - @$(NORMAL_UNINSTALL) - @list='$(pkgconfig_DATA)'; test -n "$(pkgconfigdir)" || list=; \ - files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ - dir='$(DESTDIR)$(pkgconfigdir)'; $(am__uninstall_files_from_dir) - -# This directory's subdirectories are mostly independent; you can cd -# into them and run 'make' without going through this Makefile. -# To change the values of 'make' variables: instead of editing Makefiles, -# (1) if the variable is set in 'config.status', edit 'config.status' -# (which will cause the Makefiles to be regenerated when you run 'make'); -# (2) otherwise, pass the desired values on the 'make' command line. -$(am__recursive_targets): - @fail=; \ - if $(am__make_keepgoing); then \ - failcom='fail=yes'; \ - else \ - failcom='exit 1'; \ - fi; \ - dot_seen=no; \ - target=`echo $@ | sed s/-recursive//`; \ - case "$@" in \ - distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ - *) list='$(SUBDIRS)' ;; \ - esac; \ - for subdir in $$list; do \ - echo "Making $$target in $$subdir"; \ - if test "$$subdir" = "."; then \ - dot_seen=yes; \ - local_target="$$target-am"; \ - else \ - local_target="$$target"; \ - fi; \ - ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ - || eval $$failcom; \ - done; \ - if test "$$dot_seen" = "no"; then \ - $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ - fi; test -z "$$fail" - -ID: $(am__tagged_files) - $(am__define_uniq_tagged_files); mkid -fID $$unique -tags: tags-recursive -TAGS: tags - -tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) - set x; \ - here=`pwd`; \ - if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \ - include_option=--etags-include; \ - empty_fix=.; \ - else \ - include_option=--include; \ - empty_fix=; \ - fi; \ - list='$(SUBDIRS)'; for subdir in $$list; do \ - if test "$$subdir" = .; then :; else \ - test ! -f $$subdir/TAGS || \ - set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \ - fi; \ - done; \ - $(am__define_uniq_tagged_files); \ - shift; \ - if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ - test -n "$$unique" || unique=$$empty_fix; \ - if test $$# -gt 0; then \ - $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ - "$$@" $$unique; \ - else \ - $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ - $$unique; \ - fi; \ - fi -ctags: ctags-recursive - -CTAGS: ctags -ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) - $(am__define_uniq_tagged_files); \ - test -z "$(CTAGS_ARGS)$$unique" \ - || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ - $$unique - -GTAGS: - here=`$(am__cd) $(top_builddir) && pwd` \ - && $(am__cd) $(top_srcdir) \ - && gtags -i $(GTAGS_ARGS) "$$here" -cscopelist: cscopelist-recursive - -cscopelist-am: $(am__tagged_files) - list='$(am__tagged_files)'; \ - case "$(srcdir)" in \ - [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ - *) sdir=$(subdir)/$(srcdir) ;; \ - esac; \ - for i in $$list; do \ - if test -f "$$i"; then \ - echo "$(subdir)/$$i"; \ - else \ - echo "$$sdir/$$i"; \ - fi; \ - done >> $(top_builddir)/cscope.files - -distclean-tags: - -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags - -distdir: $(DISTFILES) - @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ - topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ - list='$(DISTFILES)'; \ - dist_files=`for file in $$list; do echo $$file; done | \ - sed -e "s|^$$srcdirstrip/||;t" \ - -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ - case $$dist_files in \ - */*) $(MKDIR_P) `echo "$$dist_files" | \ - sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ - sort -u` ;; \ - esac; \ - for file in $$dist_files; do \ - if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ - if test -d $$d/$$file; then \ - dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ - if test -d "$(distdir)/$$file"; then \ - find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ - fi; \ - if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ - cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ - find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ - fi; \ - cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ - else \ - test -f "$(distdir)/$$file" \ - || cp -p $$d/$$file "$(distdir)/$$file" \ - || exit 1; \ - fi; \ - done - @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ - if test "$$subdir" = .; then :; else \ - $(am__make_dryrun) \ - || test -d "$(distdir)/$$subdir" \ - || $(MKDIR_P) "$(distdir)/$$subdir" \ - || exit 1; \ - dir1=$$subdir; dir2="$(distdir)/$$subdir"; \ - $(am__relativize); \ - new_distdir=$$reldir; \ - dir1=$$subdir; dir2="$(top_distdir)"; \ - $(am__relativize); \ - new_top_distdir=$$reldir; \ - echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \ - echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \ - ($(am__cd) $$subdir && \ - $(MAKE) $(AM_MAKEFLAGS) \ - top_distdir="$$new_top_distdir" \ - distdir="$$new_distdir" \ - am__remove_distdir=: \ - am__skip_length_check=: \ - am__skip_mode_fix=: \ - distdir) \ - || exit 1; \ - fi; \ - done -check-am: all-am -check: check-recursive -all-am: Makefile $(LTLIBRARIES) $(DATA) -installdirs: installdirs-recursive -installdirs-am: - for dir in "$(DESTDIR)$(libdir)" "$(DESTDIR)$(pkgconfigdir)"; do \ - test -z "$$dir" || $(MKDIR_P) "$$dir"; \ - done -install: install-recursive -install-exec: install-exec-recursive -install-data: install-data-recursive -uninstall: uninstall-recursive - -install-am: all-am - @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am - -installcheck: installcheck-recursive -install-strip: - if test -z '$(STRIP)'; then \ - $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ - install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ - install; \ - else \ - $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ - install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ - "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ - fi -mostlyclean-generic: - -clean-generic: - -distclean-generic: - -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) - -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) - -test -z "$(DISTCLEANFILES)" || rm -f $(DISTCLEANFILES) - -maintainer-clean-generic: - @echo "This command is intended for maintainers to use" - @echo "it deletes files that may require special tools to rebuild." -clean: clean-recursive - -clean-am: clean-generic clean-libLTLIBRARIES clean-libtool \ - mostlyclean-am - -distclean: distclean-recursive - -rm -rf ./$(DEPDIR) - -rm -f Makefile -distclean-am: clean-am distclean-compile distclean-generic \ - distclean-tags - -dvi: dvi-recursive - -dvi-am: - -html: html-recursive - -html-am: - -info: info-recursive - -info-am: - -install-data-am: install-pkgconfigDATA - -install-dvi: install-dvi-recursive - -install-dvi-am: - -install-exec-am: install-libLTLIBRARIES - -install-html: install-html-recursive - -install-html-am: - -install-info: install-info-recursive - -install-info-am: - -install-man: - -install-pdf: install-pdf-recursive - -install-pdf-am: - -install-ps: install-ps-recursive - -install-ps-am: - -installcheck-am: - -maintainer-clean: maintainer-clean-recursive - -rm -rf ./$(DEPDIR) - -rm -f Makefile -maintainer-clean-am: distclean-am maintainer-clean-generic - -mostlyclean: mostlyclean-recursive - -mostlyclean-am: mostlyclean-compile mostlyclean-generic \ - mostlyclean-libtool - -pdf: pdf-recursive - -pdf-am: - -ps: ps-recursive - -ps-am: - -uninstall-am: uninstall-libLTLIBRARIES uninstall-pkgconfigDATA - -.MAKE: $(am__recursive_targets) install-am install-strip - -.PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am check \ - check-am clean clean-generic clean-libLTLIBRARIES \ - clean-libtool cscopelist-am ctags ctags-am distclean \ - distclean-compile distclean-generic distclean-libtool \ - distclean-tags distdir dvi dvi-am html html-am info info-am \ - install install-am install-data install-data-am install-dvi \ - install-dvi-am install-exec install-exec-am install-html \ - install-html-am install-info install-info-am \ - install-libLTLIBRARIES install-man install-pdf install-pdf-am \ - install-pkgconfigDATA install-ps install-ps-am install-strip \ - installcheck installcheck-am installdirs installdirs-am \ - maintainer-clean maintainer-clean-generic mostlyclean \ - mostlyclean-compile mostlyclean-generic mostlyclean-libtool \ - pdf pdf-am ps ps-am tags tags-am uninstall uninstall-am \ - uninstall-libLTLIBRARIES uninstall-pkgconfigDATA - - -# Tell versions [3.59,3.63) of GNU make to not export all variables. -# Otherwise a system limit (for SysV at least) may be exceeded. -.NOEXPORT: diff --git a/lib/Makefile.msvc b/lib/Makefile.msvc deleted file mode 100644 index edc6d74..0000000 --- a/lib/Makefile.msvc +++ /dev/null @@ -1,249 +0,0 @@ -# -# GNU Makefile for nghttp2 / MSVC. -# -# By G. Vanem 2013 -# Updated 3/2015 by Remo Eichenberger @remoe -# The MIT License apply. -# - -# -# Choose your weapons: -# Set 'USE_CYTHON=1' to build and install the 'nghttp2.pyd' Python extension. -# -USE_CYTHON = 1 - -_VERSION := $(shell grep AC_INIT ../configure.ac | cut -d'[' -f3 | sed -e 's/-DEV], //g') -_VERSION := $(subst ., ,$(_VERSION)) -VER_MAJOR = $(word 1,$(_VERSION)) -VER_MINOR = $(word 2,$(_VERSION)) -VER_MICRO = $(word 3,$(_VERSION)) -VERSION = $(VER_MAJOR).$(VER_MINOR).$(VER_MICRO) -VERSION_NUM = ($(VER_MAJOR) << 16) + ($(VER_MINOR) << 8) + $(VER_MICRO) - -GENERATED = 'Generated by $(realpath Makefile.MSVC)' - -# -# Where to copy nghttp2.dll + lib + headers to. -# Note: 'make install' is not in default targets. Do it explicitly. -# -VC_ROOT = $(realpath $(VCINSTALLDIR)) -INSTALL_BIN = $(VC_ROOT)/bin -INSTALL_LIB = $(VC_ROOT)/lib -INSTALL_HDR = $(VC_ROOT)/include - -# -# Build for DEBUG-model and RELEASE at the same time. -# -TARGETS = nghttp2.lib nghttp2.dll nghttp2_imp.lib \ - nghttp2d.lib nghttp2d.dll nghttp2d_imp.lib - -EXT_LIBS = - -OBJ_DIR = MSVC_obj - -NGHTTP2_PDB_R = $(OBJ_DIR)/nghttp2.pdb -NGHTTP2_PDB_D = $(OBJ_DIR)/nghttp2d.pdb - -CC = cl -CFLAGS = -I./includes -Dssize_t=long -D_U_="" - -CFLAGS_R = -nologo -MD -W3 -Zi -Fd./$(NGHTTP2_PDB_R) -CFLAGS_D = -nologo -MDd -W3 -Zi -Fd./$(NGHTTP2_PDB_D) \ - -Ot -D_DEBUG -GF -RTCs -RTCu # -RTCc -GS - -LDFLAGS = -nologo -machine:x64 -map -debug -incremental:no # -verbose - -NGHTTP2_SRC = nghttp2_pq.c \ - nghttp2_map.c \ - nghttp2_queue.c \ - nghttp2_frame.c \ - nghttp2_buf.c \ - nghttp2_stream.c \ - nghttp2_outbound_item.c \ - nghttp2_session.c \ - nghttp2_submit.c \ - nghttp2_helper.c \ - nghttp2_npn.c \ - nghttp2_hd.c \ - nghttp2_hd_huffman.c \ - nghttp2_hd_huffman_data.c \ - nghttp2_version.c \ - nghttp2_priority_spec.c \ - nghttp2_option.c \ - nghttp2_callbacks.c \ - nghttp2_mem.c \ - nghttp2_http.c - -NGHTTP2_OBJ_R = $(addprefix $(OBJ_DIR)/r_, $(notdir $(NGHTTP2_SRC:.c=.obj))) -NGHTTP2_OBJ_D = $(addprefix $(OBJ_DIR)/d_, $(notdir $(NGHTTP2_SRC:.c=.obj))) - -all: intro $(OBJ_DIR) $(TARGETS) - @echo 'Welcome to NgHTTP2 (release + debug).' - @echo 'Do a "make -f Makefile.MSVC install" at own risk!' - -intro: - @echo 'Building NgHTTP (MSVC) ver. "$(VERSION)".' - -test_ver: - @echo '$$(VERSION): "$(VERSION)".' - @echo '$$(_VERSION): "$(_VERSION)".' - @echo '$$(VER_MAJOR): "$(VER_MAJOR)".' - @echo '$$(VER_MINOR): "$(VER_MINOR)".' - @echo '$$(VER_MICRO): "$(VER_MICRO)".' - -$(OBJ_DIR): - - mkdir $(OBJ_DIR) - -install: includes/nghttp2/nghttp2.h includes/nghttp2/nghttp2ver.h \ - nghttp2.dll nghttp2.lib nghttp2_imp.lib \ - nghttp2d.dll nghttp2d.lib nghttp2d_imp.lib \ - copy_headers_and_libs install_nghttp2_pyd_$(USE_CYTHON) - -# -# This MUST be done before using the 'install_nghttp2_pyd_1' rule. -# -copy_headers_and_libs: - - mkdir $(INSTALL_HDR)/nghttp2 - cp --update $(addprefix includes/nghttp2/, nghttp2.h nghttp2ver.h) $(INSTALL_HDR)/nghttp2 - cp --update nghttp2.dll nghttp2d.dll $(NGHTTP2_PDB_R) $(NGHTTP2_PDB_D) $(INSTALL_BIN) - cp --update nghttp2.lib nghttp2d.lib nghttp2_imp.lib nghttp2d_imp.lib $(INSTALL_LIB) - @echo - -nghttp2.lib: $(NGHTTP2_OBJ_R) - lib -nologo -out:$@ $^ - @echo - -nghttp2d.lib: $(NGHTTP2_OBJ_D) - lib -nologo -out:$@ $^ - @echo - -nghttp2.dll nghttp2_imp.lib: $(NGHTTP2_OBJ_R) $(OBJ_DIR)/r_nghttp2.res - link $(LDFLAGS) -dll -out:nghttp2.dll -implib:nghttp2_imp.lib \ - $(NGHTTP2_OBJ_R) $(OBJ_DIR)/r_nghttp2.res $(EXT_LIBS) - @echo - -nghttp2d.dll nghttp2d_imp.lib: $(NGHTTP2_OBJ_D) $(OBJ_DIR)/d_nghttp2.res - link $(LDFLAGS) -dll -out:nghttp2d.dll -implib:nghttp2d_imp.lib \ - $(NGHTTP2_OBJ_D) $(OBJ_DIR)/d_nghttp2.res $(EXT_LIBS) - @echo - -install_nghttp2_pyd_0: ; - -install_nghttp2_pyd_1: $(addprefix ../python/, setup.py.in nghttp2.pyx) - cd ../python ; \ - echo '# $(GENERATED). DO NOT EDIT.' > setup.py ; \ - sed -e 's/@top_srcdir@/../' \ - -e 's/@top_builddir@/../' \ - -e 's/@PACKAGE_VERSION@/$(VERSION)/' setup.py.in >> setup.py ; \ - cython -v nghttp2.pyx ; \ - python setup.py install - -clean_nghttp2_pyd_0: ; - -clean_nghttp2_pyd_1: - cd ../python ; \ - rm -f setup.py nghttp2.c ; \ - rm -fR build/* - -$(OBJ_DIR)/r_%.obj: %.c - $(CC) $(CFLAGS_R) $(CFLAGS) -Fo$@ -c $< - @echo - -$(OBJ_DIR)/d_%.obj: %.c - $(CC) $(CFLAGS_D) $(CFLAGS) -Fo$@ -c $< - @echo - -$(OBJ_DIR)/r_nghttp2.res: nghttp2.rc - rc -nologo -D_RELEASE -Fo $@ $< - @echo - -$(OBJ_DIR)/d_nghttp2.res: nghttp2.rc - rc -nologo -D_DEBUG -Fo $@ $< - @echo - -includes/nghttp2/nghttp2ver.h: includes/nghttp2/nghttp2ver.h.in - sed < includes/nghttp2/nghttp2ver.h.in \ - -e 's/@PACKAGE_VERSION@/$(VERSION)/g' \ - -e 's/@PACKAGE_VERSION_NUM@/($(VERSION_NUM))/g' > $@ - touch --reference=includes/nghttp2/nghttp2ver.h.in $@ - - -define RES_FILE - #include - - VS_VERSION_INFO VERSIONINFO - FILEVERSION $(VER_MAJOR), $(VER_MINOR), $(VER_MICRO), 0 - PRODUCTVERSION $(VER_MAJOR), $(VER_MINOR), $(VER_MICRO), 0 - FILEFLAGSMASK 0x3fL - FILEOS 0x40004L - FILETYPE 0x2L - FILESUBTYPE 0x0L - #ifdef _DEBUG - #define VER_STR "$(VERSION).0 (MSVC debug)" - #define DBG "d" - FILEFLAGS 0x1L - #else - #define VER_STR "$(VERSION).0 (MSVC release)" - #define DBG "" - FILEFLAGS 0x0L - #endif - BEGIN - BLOCK "StringFileInfo" - BEGIN - BLOCK "040904b0" - BEGIN - VALUE "CompanyName", "http://tatsuhiro-t.github.io/nghttp2/" - VALUE "FileDescription", "nghttp2; HTTP/2 C library" - VALUE "FileVersion", VER_STR - VALUE "InternalName", "nghttp2" DBG - VALUE "LegalCopyright", "The MIT License" - VALUE "LegalTrademarks", "" - VALUE "OriginalFilename", "nghttp2" DBG ".dll" - VALUE "ProductName", "NGHTTP2." - VALUE "ProductVersion", VER_STR - VALUE "PrivateBuild", "The privat build of ." - VALUE "SpecialBuild", "" - END - END - BLOCK "VarFileInfo" - BEGIN - VALUE "Translation", 0x409, 1200 - END - END -endef - -export RES_FILE - -nghttp2.rc: Makefile.MSVC - @echo 'Generating $@...' - @echo ' /* $(GENERATED). DO NOT EDIT.' > $@ - @echo ' */' >> $@ - @echo "$$RES_FILE" >> $@ - -clean: - rm -f $(OBJ_DIR)/* nghttp2_imp.exp nghttp2_imp.exp \ - nghttp2.map nghttp2d.map nghttp2.rc includes/nghttp2/nghttp2ver.h - @echo - -vclean realclean: clean clean_nghttp2_pyd_$(USE_CYTHON) - rm -f $(TARGETS) nghttp2.pdb nghttp2d.pdb nghttp2_imp.exp nghttp2d_imp.exp .depend.MSVC - - rmdir $(OBJ_DIR) - -# -# Use gcc to generated the dependencies. No MSVC specific args please! -# -REPLACE_R = 's/\(.*\)\.o: /\n$$(OBJ_DIR)\/r_\1.obj: /' -REPLACE_D = 's/\(.*\)\.o: /\n$$(OBJ_DIR)\/d_\1.obj: /' - -depend: includes/nghttp2/nghttp2ver.h - @echo '# $(GENERATED). DO NOT EDIT.' > .depend.MSVC - gcc -MM $(CFLAGS) $(NGHTTP2_SRC) >> .depend.tmp - @echo '#' >> .depend.MSVC - @echo '# Release lib objects:' >> .depend.MSVC - sed -e $(REPLACE_R) .depend.tmp >> .depend.MSVC - @echo '#' >> .depend.MSVC - @echo '# Debug lib objects:' >> .depend.MSVC - sed -e $(REPLACE_D) .depend.tmp >> .depend.MSVC - rm -f .depend.tmp - --include .depend.MSVC diff --git a/lib/includes/Makefile.am b/lib/includes/Makefile.am deleted file mode 100644 index 80af63c..0000000 --- a/lib/includes/Makefile.am +++ /dev/null @@ -1,23 +0,0 @@ -# nghttp2 - HTTP/2 C Library - -# Copyright (c) 2012 Tatsuhiro Tsujikawa - -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: - -# The above copyright notice and this permission notice shall be -# included in all copies or substantial portions of the Software. - -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -nobase_include_HEADERS = nghttp2/nghttp2.h nghttp2/nghttp2ver.h diff --git a/lib/includes/Makefile.in b/lib/includes/Makefile.in deleted file mode 100644 index f2d93d8..0000000 --- a/lib/includes/Makefile.in +++ /dev/null @@ -1,636 +0,0 @@ -# Makefile.in generated by automake 1.14.1 from Makefile.am. -# @configure_input@ - -# Copyright (C) 1994-2013 Free Software Foundation, Inc. - -# This Makefile.in is free software; the Free Software Foundation -# gives unlimited permission to copy and/or distribute it, -# with or without modifications, as long as this notice is preserved. - -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY, to the extent permitted by law; without -# even the implied warranty of MERCHANTABILITY or FITNESS FOR A -# PARTICULAR PURPOSE. - -@SET_MAKE@ - -# nghttp2 - HTTP/2 C Library - -# Copyright (c) 2012 Tatsuhiro Tsujikawa - -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: - -# The above copyright notice and this permission notice shall be -# included in all copies or substantial portions of the Software. - -VPATH = @srcdir@ -am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)' -am__make_running_with_option = \ - case $${target_option-} in \ - ?) ;; \ - *) echo "am__make_running_with_option: internal error: invalid" \ - "target option '$${target_option-}' specified" >&2; \ - exit 1;; \ - esac; \ - has_opt=no; \ - sane_makeflags=$$MAKEFLAGS; \ - if $(am__is_gnu_make); then \ - sane_makeflags=$$MFLAGS; \ - else \ - case $$MAKEFLAGS in \ - *\\[\ \ ]*) \ - bs=\\; \ - sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ - | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ - esac; \ - fi; \ - skip_next=no; \ - strip_trailopt () \ - { \ - flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ - }; \ - for flg in $$sane_makeflags; do \ - test $$skip_next = yes && { skip_next=no; continue; }; \ - case $$flg in \ - *=*|--*) continue;; \ - -*I) strip_trailopt 'I'; skip_next=yes;; \ - -*I?*) strip_trailopt 'I';; \ - -*O) strip_trailopt 'O'; skip_next=yes;; \ - -*O?*) strip_trailopt 'O';; \ - -*l) strip_trailopt 'l'; skip_next=yes;; \ - -*l?*) strip_trailopt 'l';; \ - -[dEDm]) skip_next=yes;; \ - -[JT]) skip_next=yes;; \ - esac; \ - case $$flg in \ - *$$target_option*) has_opt=yes; break;; \ - esac; \ - done; \ - test $$has_opt = yes -am__make_dryrun = (target_option=n; $(am__make_running_with_option)) -am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) -pkgdatadir = $(datadir)/@PACKAGE@ -pkgincludedir = $(includedir)/@PACKAGE@ -pkglibdir = $(libdir)/@PACKAGE@ -pkglibexecdir = $(libexecdir)/@PACKAGE@ -am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd -install_sh_DATA = $(install_sh) -c -m 644 -install_sh_PROGRAM = $(install_sh) -c -install_sh_SCRIPT = $(install_sh) -c -INSTALL_HEADER = $(INSTALL_DATA) -transform = $(program_transform_name) -NORMAL_INSTALL = : -PRE_INSTALL = : -POST_INSTALL = : -NORMAL_UNINSTALL = : -PRE_UNINSTALL = : -POST_UNINSTALL = : -build_triplet = @build@ -host_triplet = @host@ -target_triplet = @target@ -subdir = lib/includes -DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \ - $(nobase_include_HEADERS) -ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 -am__aclocal_m4_deps = $(top_srcdir)/m4/ax_boost_asio.m4 \ - $(top_srcdir)/m4/ax_boost_base.m4 \ - $(top_srcdir)/m4/ax_boost_system.m4 \ - $(top_srcdir)/m4/ax_boost_thread.m4 \ - $(top_srcdir)/m4/ax_check_compile_flag.m4 \ - $(top_srcdir)/m4/ax_cxx_compile_stdcxx_11.m4 \ - $(top_srcdir)/m4/ax_have_epoll.m4 \ - $(top_srcdir)/m4/ax_python_devel.m4 \ - $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/libxml2.m4 \ - $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ - $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ - $(top_srcdir)/configure.ac -am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ - $(ACLOCAL_M4) -mkinstalldirs = $(install_sh) -d -CONFIG_HEADER = $(top_builddir)/config.h -CONFIG_CLEAN_FILES = -CONFIG_CLEAN_VPATH_FILES = -AM_V_P = $(am__v_P_@AM_V@) -am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) -am__v_P_0 = false -am__v_P_1 = : -AM_V_GEN = $(am__v_GEN_@AM_V@) -am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) -am__v_GEN_0 = @echo " GEN " $@; -am__v_GEN_1 = -AM_V_at = $(am__v_at_@AM_V@) -am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) -am__v_at_0 = @ -am__v_at_1 = -SOURCES = -DIST_SOURCES = -am__can_run_installinfo = \ - case $$AM_UPDATE_INFO_DIR in \ - n|no|NO) false;; \ - *) (install-info --version) >/dev/null 2>&1;; \ - esac -am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; -am__vpath_adj = case $$p in \ - $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ - *) f=$$p;; \ - esac; -am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; -am__install_max = 40 -am__nobase_strip_setup = \ - srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` -am__nobase_strip = \ - for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" -am__nobase_list = $(am__nobase_strip_setup); \ - for p in $$list; do echo "$$p $$p"; done | \ - sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ - $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ - if (++n[$$2] == $(am__install_max)) \ - { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ - END { for (dir in files) print dir, files[dir] }' -am__base_list = \ - sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ - sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' -am__uninstall_files_from_dir = { \ - test -z "$$files" \ - || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ - || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ - $(am__cd) "$$dir" && rm -f $$files; }; \ - } -am__installdirs = "$(DESTDIR)$(includedir)" -HEADERS = $(nobase_include_HEADERS) -am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) -# Read a list of newline-separated strings from the standard input, -# and print each of them once, without duplicates. Input order is -# *not* preserved. -am__uniquify_input = $(AWK) '\ - BEGIN { nonempty = 0; } \ - { items[$$0] = 1; nonempty = 1; } \ - END { if (nonempty) { for (i in items) print i; }; } \ -' -# Make sure the list of sources is unique. This is necessary because, -# e.g., the same source file might be shared among _SOURCES variables -# for different programs/libraries. -am__define_uniq_tagged_files = \ - list='$(am__tagged_files)'; \ - unique=`for i in $$list; do \ - if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ - done | $(am__uniquify_input)` -ETAGS = etags -CTAGS = ctags -DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) -ACLOCAL = @ACLOCAL@ -AMTAR = @AMTAR@ -AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ -APPLDFLAGS = @APPLDFLAGS@ -AR = @AR@ -AUTOCONF = @AUTOCONF@ -AUTOHEADER = @AUTOHEADER@ -AUTOMAKE = @AUTOMAKE@ -AWK = @AWK@ -BOOST_ASIO_LIB = @BOOST_ASIO_LIB@ -BOOST_CPPFLAGS = @BOOST_CPPFLAGS@ -BOOST_LDFLAGS = @BOOST_LDFLAGS@ -BOOST_SYSTEM_LIB = @BOOST_SYSTEM_LIB@ -BOOST_THREAD_LIB = @BOOST_THREAD_LIB@ -CC = @CC@ -CCDEPMODE = @CCDEPMODE@ -CFLAGS = @CFLAGS@ -CPP = @CPP@ -CPPFLAGS = @CPPFLAGS@ -CUNIT_CFLAGS = @CUNIT_CFLAGS@ -CUNIT_LIBS = @CUNIT_LIBS@ -CXX = @CXX@ -CXXCPP = @CXXCPP@ -CXXDEPMODE = @CXXDEPMODE@ -CXXFLAGS = @CXXFLAGS@ -CYGPATH_W = @CYGPATH_W@ -CYTHON = @CYTHON@ -DEFS = @DEFS@ -DEPDIR = @DEPDIR@ -DLLTOOL = @DLLTOOL@ -DSYMUTIL = @DSYMUTIL@ -DUMPBIN = @DUMPBIN@ -ECHO_C = @ECHO_C@ -ECHO_N = @ECHO_N@ -ECHO_T = @ECHO_T@ -EGREP = @EGREP@ -EXEEXT = @EXEEXT@ -FGREP = @FGREP@ -GREP = @GREP@ -HAVE_CXX11 = @HAVE_CXX11@ -INSTALL = @INSTALL@ -INSTALL_DATA = @INSTALL_DATA@ -INSTALL_PROGRAM = @INSTALL_PROGRAM@ -INSTALL_SCRIPT = @INSTALL_SCRIPT@ -INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ -JANSSON_CFLAGS = @JANSSON_CFLAGS@ -JANSSON_LIBS = @JANSSON_LIBS@ -JEMALLOC_LIBS = @JEMALLOC_LIBS@ -LD = @LD@ -LDFLAGS = @LDFLAGS@ -LIBEVENT_OPENSSL_CFLAGS = @LIBEVENT_OPENSSL_CFLAGS@ -LIBEVENT_OPENSSL_LIBS = @LIBEVENT_OPENSSL_LIBS@ -LIBEV_CFLAGS = @LIBEV_CFLAGS@ -LIBEV_LIBS = @LIBEV_LIBS@ -LIBOBJS = @LIBOBJS@ -LIBS = @LIBS@ -LIBSPDYLAY_CFLAGS = @LIBSPDYLAY_CFLAGS@ -LIBSPDYLAY_LIBS = @LIBSPDYLAY_LIBS@ -LIBTOOL = @LIBTOOL@ -LIPO = @LIPO@ -LN_S = @LN_S@ -LTLIBOBJS = @LTLIBOBJS@ -LT_AGE = @LT_AGE@ -LT_CURRENT = @LT_CURRENT@ -LT_REVISION = @LT_REVISION@ -MAKEINFO = @MAKEINFO@ -MANIFEST_TOOL = @MANIFEST_TOOL@ -MKDIR_P = @MKDIR_P@ -NM = @NM@ -NMEDIT = @NMEDIT@ -OBJDUMP = @OBJDUMP@ -OBJEXT = @OBJEXT@ -OPENSSL_CFLAGS = @OPENSSL_CFLAGS@ -OPENSSL_LIBS = @OPENSSL_LIBS@ -OTOOL = @OTOOL@ -OTOOL64 = @OTOOL64@ -PACKAGE = @PACKAGE@ -PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ -PACKAGE_NAME = @PACKAGE_NAME@ -PACKAGE_STRING = @PACKAGE_STRING@ -PACKAGE_TARNAME = @PACKAGE_TARNAME@ -PACKAGE_URL = @PACKAGE_URL@ -PACKAGE_VERSION = @PACKAGE_VERSION@ -PACKAGE_VERSION_NUM = @PACKAGE_VERSION_NUM@ -PATH_SEPARATOR = @PATH_SEPARATOR@ -PKG_CONFIG = @PKG_CONFIG@ -PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ -PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ -PYTHON = @PYTHON@ -PYTHON_CPPFLAGS = @PYTHON_CPPFLAGS@ -PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@ -PYTHON_EXTRA_LDFLAGS = @PYTHON_EXTRA_LDFLAGS@ -PYTHON_EXTRA_LIBS = @PYTHON_EXTRA_LIBS@ -PYTHON_LDFLAGS = @PYTHON_LDFLAGS@ -PYTHON_PLATFORM = @PYTHON_PLATFORM@ -PYTHON_PREFIX = @PYTHON_PREFIX@ -PYTHON_SITE_PKG = @PYTHON_SITE_PKG@ -PYTHON_VERSION = @PYTHON_VERSION@ -RANLIB = @RANLIB@ -SED = @SED@ -SET_MAKE = @SET_MAKE@ -SHELL = @SHELL@ -STRIP = @STRIP@ -TESTLDADD = @TESTLDADD@ -VERSION = @VERSION@ -WARNCFLAGS = @WARNCFLAGS@ -XML2_CONFIG = @XML2_CONFIG@ -XML_CPPFLAGS = @XML_CPPFLAGS@ -XML_LIBS = @XML_LIBS@ -ZLIB_CFLAGS = @ZLIB_CFLAGS@ -ZLIB_LIBS = @ZLIB_LIBS@ -abs_builddir = @abs_builddir@ -abs_srcdir = @abs_srcdir@ -abs_top_builddir = @abs_top_builddir@ -abs_top_srcdir = @abs_top_srcdir@ -ac_ct_AR = @ac_ct_AR@ -ac_ct_CC = @ac_ct_CC@ -ac_ct_CXX = @ac_ct_CXX@ -ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ -am__include = @am__include@ -am__leading_dot = @am__leading_dot@ -am__quote = @am__quote@ -am__tar = @am__tar@ -am__untar = @am__untar@ -bindir = @bindir@ -build = @build@ -build_alias = @build_alias@ -build_cpu = @build_cpu@ -build_os = @build_os@ -build_vendor = @build_vendor@ -builddir = @builddir@ -datadir = @datadir@ -datarootdir = @datarootdir@ -docdir = @docdir@ -dvidir = @dvidir@ -exec_prefix = @exec_prefix@ -host = @host@ -host_alias = @host_alias@ -host_cpu = @host_cpu@ -host_os = @host_os@ -host_vendor = @host_vendor@ -htmldir = @htmldir@ -includedir = @includedir@ -infodir = @infodir@ -install_sh = @install_sh@ -libdir = @libdir@ -libexecdir = @libexecdir@ -localedir = @localedir@ -localstatedir = @localstatedir@ -mandir = @mandir@ -mkdir_p = @mkdir_p@ -oldincludedir = @oldincludedir@ -pdfdir = @pdfdir@ -pkgpyexecdir = @pkgpyexecdir@ -pkgpythondir = @pkgpythondir@ -prefix = @prefix@ -program_transform_name = @program_transform_name@ -psdir = @psdir@ -pyexecdir = @pyexecdir@ -pythondir = @pythondir@ -sbindir = @sbindir@ -sharedstatedir = @sharedstatedir@ -srcdir = @srcdir@ -sysconfdir = @sysconfdir@ -target = @target@ -target_alias = @target_alias@ -target_cpu = @target_cpu@ -target_os = @target_os@ -target_vendor = @target_vendor@ -top_build_prefix = @top_build_prefix@ -top_builddir = @top_builddir@ -top_srcdir = @top_srcdir@ - -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -nobase_include_HEADERS = nghttp2/nghttp2.h nghttp2/nghttp2ver.h -all: all-am - -.SUFFIXES: -$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) - @for dep in $?; do \ - case '$(am__configure_deps)' in \ - *$$dep*) \ - ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ - && { if test -f $@; then exit 0; else break; fi; }; \ - exit 1;; \ - esac; \ - done; \ - echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu lib/includes/Makefile'; \ - $(am__cd) $(top_srcdir) && \ - $(AUTOMAKE) --gnu lib/includes/Makefile -.PRECIOUS: Makefile -Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status - @case '$?' in \ - *config.status*) \ - cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ - *) \ - echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ - cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ - esac; - -$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) - cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh - -$(top_srcdir)/configure: $(am__configure_deps) - cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh -$(ACLOCAL_M4): $(am__aclocal_m4_deps) - cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh -$(am__aclocal_m4_deps): - -mostlyclean-libtool: - -rm -f *.lo - -clean-libtool: - -rm -rf .libs _libs -install-nobase_includeHEADERS: $(nobase_include_HEADERS) - @$(NORMAL_INSTALL) - @list='$(nobase_include_HEADERS)'; test -n "$(includedir)" || list=; \ - if test -n "$$list"; then \ - echo " $(MKDIR_P) '$(DESTDIR)$(includedir)'"; \ - $(MKDIR_P) "$(DESTDIR)$(includedir)" || exit 1; \ - fi; \ - $(am__nobase_list) | while read dir files; do \ - xfiles=; for file in $$files; do \ - if test -f "$$file"; then xfiles="$$xfiles $$file"; \ - else xfiles="$$xfiles $(srcdir)/$$file"; fi; done; \ - test -z "$$xfiles" || { \ - test "x$$dir" = x. || { \ - echo " $(MKDIR_P) '$(DESTDIR)$(includedir)/$$dir'"; \ - $(MKDIR_P) "$(DESTDIR)$(includedir)/$$dir"; }; \ - echo " $(INSTALL_HEADER) $$xfiles '$(DESTDIR)$(includedir)/$$dir'"; \ - $(INSTALL_HEADER) $$xfiles "$(DESTDIR)$(includedir)/$$dir" || exit $$?; }; \ - done - -uninstall-nobase_includeHEADERS: - @$(NORMAL_UNINSTALL) - @list='$(nobase_include_HEADERS)'; test -n "$(includedir)" || list=; \ - $(am__nobase_strip_setup); files=`$(am__nobase_strip)`; \ - dir='$(DESTDIR)$(includedir)'; $(am__uninstall_files_from_dir) - -ID: $(am__tagged_files) - $(am__define_uniq_tagged_files); mkid -fID $$unique -tags: tags-am -TAGS: tags - -tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) - set x; \ - here=`pwd`; \ - $(am__define_uniq_tagged_files); \ - shift; \ - if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ - test -n "$$unique" || unique=$$empty_fix; \ - if test $$# -gt 0; then \ - $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ - "$$@" $$unique; \ - else \ - $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ - $$unique; \ - fi; \ - fi -ctags: ctags-am - -CTAGS: ctags -ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) - $(am__define_uniq_tagged_files); \ - test -z "$(CTAGS_ARGS)$$unique" \ - || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ - $$unique - -GTAGS: - here=`$(am__cd) $(top_builddir) && pwd` \ - && $(am__cd) $(top_srcdir) \ - && gtags -i $(GTAGS_ARGS) "$$here" -cscopelist: cscopelist-am - -cscopelist-am: $(am__tagged_files) - list='$(am__tagged_files)'; \ - case "$(srcdir)" in \ - [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ - *) sdir=$(subdir)/$(srcdir) ;; \ - esac; \ - for i in $$list; do \ - if test -f "$$i"; then \ - echo "$(subdir)/$$i"; \ - else \ - echo "$$sdir/$$i"; \ - fi; \ - done >> $(top_builddir)/cscope.files - -distclean-tags: - -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags - -distdir: $(DISTFILES) - @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ - topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ - list='$(DISTFILES)'; \ - dist_files=`for file in $$list; do echo $$file; done | \ - sed -e "s|^$$srcdirstrip/||;t" \ - -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ - case $$dist_files in \ - */*) $(MKDIR_P) `echo "$$dist_files" | \ - sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ - sort -u` ;; \ - esac; \ - for file in $$dist_files; do \ - if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ - if test -d $$d/$$file; then \ - dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ - if test -d "$(distdir)/$$file"; then \ - find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ - fi; \ - if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ - cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ - find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ - fi; \ - cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ - else \ - test -f "$(distdir)/$$file" \ - || cp -p $$d/$$file "$(distdir)/$$file" \ - || exit 1; \ - fi; \ - done -check-am: all-am -check: check-am -all-am: Makefile $(HEADERS) -installdirs: - for dir in "$(DESTDIR)$(includedir)"; do \ - test -z "$$dir" || $(MKDIR_P) "$$dir"; \ - done -install: install-am -install-exec: install-exec-am -install-data: install-data-am -uninstall: uninstall-am - -install-am: all-am - @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am - -installcheck: installcheck-am -install-strip: - if test -z '$(STRIP)'; then \ - $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ - install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ - install; \ - else \ - $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ - install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ - "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ - fi -mostlyclean-generic: - -clean-generic: - -distclean-generic: - -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) - -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) - -maintainer-clean-generic: - @echo "This command is intended for maintainers to use" - @echo "it deletes files that may require special tools to rebuild." -clean: clean-am - -clean-am: clean-generic clean-libtool mostlyclean-am - -distclean: distclean-am - -rm -f Makefile -distclean-am: clean-am distclean-generic distclean-tags - -dvi: dvi-am - -dvi-am: - -html: html-am - -html-am: - -info: info-am - -info-am: - -install-data-am: install-nobase_includeHEADERS - -install-dvi: install-dvi-am - -install-dvi-am: - -install-exec-am: - -install-html: install-html-am - -install-html-am: - -install-info: install-info-am - -install-info-am: - -install-man: - -install-pdf: install-pdf-am - -install-pdf-am: - -install-ps: install-ps-am - -install-ps-am: - -installcheck-am: - -maintainer-clean: maintainer-clean-am - -rm -f Makefile -maintainer-clean-am: distclean-am maintainer-clean-generic - -mostlyclean: mostlyclean-am - -mostlyclean-am: mostlyclean-generic mostlyclean-libtool - -pdf: pdf-am - -pdf-am: - -ps: ps-am - -ps-am: - -uninstall-am: uninstall-nobase_includeHEADERS - -.MAKE: install-am install-strip - -.PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ - clean-libtool cscopelist-am ctags ctags-am distclean \ - distclean-generic distclean-libtool distclean-tags distdir dvi \ - dvi-am html html-am info info-am install install-am \ - install-data install-data-am install-dvi install-dvi-am \ - install-exec install-exec-am install-html install-html-am \ - install-info install-info-am install-man \ - install-nobase_includeHEADERS install-pdf install-pdf-am \ - install-ps install-ps-am install-strip installcheck \ - installcheck-am installdirs maintainer-clean \ - maintainer-clean-generic mostlyclean mostlyclean-generic \ - mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \ - uninstall-am uninstall-nobase_includeHEADERS - - -# Tell versions [3.59,3.63) of GNU make to not export all variables. -# Otherwise a system limit (for SysV at least) may be exceeded. -.NOEXPORT: diff --git a/lib/includes/nghttp2/nghttp2.h b/lib/includes/nghttp2/nghttp2.h deleted file mode 100644 index 5f5fdab..0000000 --- a/lib/includes/nghttp2/nghttp2.h +++ /dev/null @@ -1,3860 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2013, 2014 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#ifndef NGHTTP2_H -#define NGHTTP2_H - -/* Define WIN32 when build target is Win32 API (borrowed from - libcurl) */ -#if (defined(_WIN32) || defined(__WIN32__)) && !defined(WIN32) -#define WIN32 -#endif - -#ifdef __cplusplus -extern "C" { -#endif - -#include -#include -#include - -#include - -#ifdef NGHTTP2_STATICLIB -#define NGHTTP2_EXTERN -#elif defined(WIN32) -#define NGHTTP2_EXTERN __declspec(dllexport) -#else /* !defined(WIN32) */ -#define NGHTTP2_EXTERN -#endif /* !defined(WIN32) */ - -/** - * @macro - * - * The protocol version identification string of this library - * supports. This identifier is used if HTTP/2 is used over TLS. - */ -#define NGHTTP2_PROTO_VERSION_ID "h2" -/** - * @macro - * - * The length of :macro:`NGHTTP2_PROTO_VERSION_ID`. - */ -#define NGHTTP2_PROTO_VERSION_ID_LEN 2 - -/** - * @macro - * - * The seriazlied form of ALPN protocol identifier this library - * supports. Notice that first byte is the length of following - * protocol identifier. This is the same wire format of `TLS ALPN - * extension `_. This is useful - * to process incoming ALPN tokens in wire format. - */ -#define NGHTTP2_PROTO_ALPN "\x2h2" - -/** - * @macro - * - * The length of :macro:`NGHTTP2_PROTO_ALPN`. - */ -#define NGHTTP2_PROTO_ALPN_LEN (sizeof(NGHTTP2_PROTO_ALPN) - 1) - -/** - * @macro - * - * The protocol version identification string of this library - * supports. This identifier is used if HTTP/2 is used over cleartext - * TCP. - */ -#define NGHTTP2_CLEARTEXT_PROTO_VERSION_ID "h2c" - -/** - * @macro - * - * The length of :macro:`NGHTTP2_CLEARTEXT_PROTO_VERSION_ID`. - */ -#define NGHTTP2_CLEARTEXT_PROTO_VERSION_ID_LEN 3 - -struct nghttp2_session; -/** - * @struct - * - * The primary structure to hold the resources needed for a HTTP/2 - * session. The details of this structure are intentionally hidden - * from the public API. - */ -typedef struct nghttp2_session nghttp2_session; - -/** - * @macro - * - * The age of :type:`nghttp2_info` - */ -#define NGHTTP2_VERSION_AGE 1 - -/** - * @struct - * - * This struct is what `nghttp2_version()` returns. It holds - * information about the particular nghttp2 version. - */ -typedef struct { - /** - * Age of this struct. This instance of nghttp2 sets it to - * :macro:`NGHTTP2_VERSION_AGE` but a future version may bump it and - * add more struct fields at the bottom - */ - int age; - /** - * the :macro:`NGHTTP2_VERSION_NUM` number (since age ==1) - */ - int version_num; - /** - * points to the :macro:`NGHTTP2_VERSION` string (since age ==1) - */ - const char *version_str; - /** - * points to the :macro:`NGHTTP2_PROTO_VERSION_ID` string this - * instance implements (since age ==1) - */ - const char *proto_str; - /* -------- the above fields all exist when age == 1 */ -} nghttp2_info; - -/** - * @macro - * - * The default weight of stream dependency. - */ -#define NGHTTP2_DEFAULT_WEIGHT 16 - -/** - * @macro - * - * The maximum weight of stream dependency. - */ -#define NGHTTP2_MAX_WEIGHT 256 - -/** - * @macro - * - * The minimum weight of stream dependency. - */ -#define NGHTTP2_MIN_WEIGHT 1 - -/** - * @macro - * - * The maximum window size - */ -#define NGHTTP2_MAX_WINDOW_SIZE ((int32_t)((1U << 31) - 1)) - -/** - * @macro - * - * The initial window size for stream level flow control. - */ -#define NGHTTP2_INITIAL_WINDOW_SIZE ((1 << 16) - 1) -/** - * @macro - * - * The initial window size for connection level flow control. - */ -#define NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE ((1 << 16) - 1) - -/** - * @macro - * - * The default header table size. - */ -#define NGHTTP2_DEFAULT_HEADER_TABLE_SIZE (1 << 12) - -/** - * @macro - * - * The client magic string, which is the first 24 bytes byte string of - * client connection preface. - */ -#define NGHTTP2_CLIENT_MAGIC "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n" - -/** - * @macro - * - * The length of :macro:`NGHTTP2_CLIENT_MAGIC`. - */ -#define NGHTTP2_CLIENT_MAGIC_LEN 24 - -/** - * @enum - * - * Error codes used in this library. The code range is [-999, -500], - * inclusive. The following values are defined: - */ -typedef enum { - /** - * Invalid argument passed. - */ - NGHTTP2_ERR_INVALID_ARGUMENT = -501, - /** - * Out of buffer space. - */ - NGHTTP2_ERR_BUFFER_ERROR = -502, - /** - * The specified protocol version is not supported. - */ - NGHTTP2_ERR_UNSUPPORTED_VERSION = -503, - /** - * Used as a return value from :type:`nghttp2_send_callback`, - * :type:`nghttp2_recv_callback` and - * :type:`nghttp2_send_data_callback` to indicate that the operation - * would block. - */ - NGHTTP2_ERR_WOULDBLOCK = -504, - /** - * General protocol error - */ - NGHTTP2_ERR_PROTO = -505, - /** - * The frame is invalid. - */ - NGHTTP2_ERR_INVALID_FRAME = -506, - /** - * The peer performed a shutdown on the connection. - */ - NGHTTP2_ERR_EOF = -507, - /** - * Used as a return value from - * :func:`nghttp2_data_source_read_callback` to indicate that data - * transfer is postponed. See - * :func:`nghttp2_data_source_read_callback` for details. - */ - NGHTTP2_ERR_DEFERRED = -508, - /** - * Stream ID has reached the maximum value. Therefore no stream ID - * is available. - */ - NGHTTP2_ERR_STREAM_ID_NOT_AVAILABLE = -509, - /** - * The stream is already closed; or the stream ID is invalid. - */ - NGHTTP2_ERR_STREAM_CLOSED = -510, - /** - * RST_STREAM has been added to the outbound queue. The stream is - * in closing state. - */ - NGHTTP2_ERR_STREAM_CLOSING = -511, - /** - * The transmission is not allowed for this stream (e.g., a frame - * with END_STREAM flag set has already sent). - */ - NGHTTP2_ERR_STREAM_SHUT_WR = -512, - /** - * The stream ID is invalid. - */ - NGHTTP2_ERR_INVALID_STREAM_ID = -513, - /** - * The state of the stream is not valid (e.g., DATA cannot be sent - * to the stream if response HEADERS has not been sent). - */ - NGHTTP2_ERR_INVALID_STREAM_STATE = -514, - /** - * Another DATA frame has already been deferred. - */ - NGHTTP2_ERR_DEFERRED_DATA_EXIST = -515, - /** - * Starting new stream is not allowed (e.g., GOAWAY has been sent - * and/or received). - */ - NGHTTP2_ERR_START_STREAM_NOT_ALLOWED = -516, - /** - * GOAWAY has already been sent. - */ - NGHTTP2_ERR_GOAWAY_ALREADY_SENT = -517, - /** - * The received frame contains the invalid header block (e.g., There - * are duplicate header names; or the header names are not encoded - * in US-ASCII character set and not lower cased; or the header name - * is zero-length string; or the header value contains multiple - * in-sequence NUL bytes). - */ - NGHTTP2_ERR_INVALID_HEADER_BLOCK = -518, - /** - * Indicates that the context is not suitable to perform the - * requested operation. - */ - NGHTTP2_ERR_INVALID_STATE = -519, - /** - * The user callback function failed due to the temporal error. - */ - NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE = -521, - /** - * The length of the frame is invalid, either too large or too small. - */ - NGHTTP2_ERR_FRAME_SIZE_ERROR = -522, - /** - * Header block inflate/deflate error. - */ - NGHTTP2_ERR_HEADER_COMP = -523, - /** - * Flow control error - */ - NGHTTP2_ERR_FLOW_CONTROL = -524, - /** - * Insufficient buffer size given to function. - */ - NGHTTP2_ERR_INSUFF_BUFSIZE = -525, - /** - * Callback was paused by the application - */ - NGHTTP2_ERR_PAUSE = -526, - /** - * There are too many in-flight SETTING frame and no more - * transmission of SETTINGS is allowed. - */ - NGHTTP2_ERR_TOO_MANY_INFLIGHT_SETTINGS = -527, - /** - * The server push is disabled. - */ - NGHTTP2_ERR_PUSH_DISABLED = -528, - /** - * DATA frame for a given stream has been already submitted and has - * not been fully processed yet. - */ - NGHTTP2_ERR_DATA_EXIST = -529, - /** - * The current session is closing due to a connection error or - * `nghttp2_session_terminate_session()` is called. - */ - NGHTTP2_ERR_SESSION_CLOSING = -530, - /** - * Invalid HTTP header field was received and stream is going to be - * closed. - */ - NGHTTP2_ERR_HTTP_HEADER = -531, - /** - * Violation in HTTP messaging rule. - */ - NGHTTP2_ERR_HTTP_MESSAGING = -532, - /** - * Stream was refused. - */ - NGHTTP2_ERR_REFUSED_STREAM = -533, - /** - * Unexpected internal error, but recovered. - */ - NGHTTP2_ERR_INTERNAL = -534, - /** - * The errors < :enum:`NGHTTP2_ERR_FATAL` mean that the library is - * under unexpected condition and processing was terminated (e.g., - * out of memory). If application receives this error code, it must - * stop using that :type:`nghttp2_session` object and only allowed - * operation for that object is deallocate it using - * `nghttp2_session_del()`. - */ - NGHTTP2_ERR_FATAL = -900, - /** - * Out of memory. This is a fatal error. - */ - NGHTTP2_ERR_NOMEM = -901, - /** - * The user callback function failed. This is a fatal error. - */ - NGHTTP2_ERR_CALLBACK_FAILURE = -902, - /** - * Invalid client magic (see :macro:`NGHTTP2_CLIENT_MAGIC`) was - * received and further processing is not possible. - */ - NGHTTP2_ERR_BAD_CLIENT_MAGIC = -903 -} nghttp2_error; - -/** - * @enum - * - * The flags for header field name/value pair. - */ -typedef enum { - /** - * No flag set. - */ - NGHTTP2_NV_FLAG_NONE = 0, - /** - * Indicates that this name/value pair must not be indexed ("Literal - * Header Field never Indexed" representation must be used in HPACK - * encoding). Other implementation calls this bit as "sensitive". - */ - NGHTTP2_NV_FLAG_NO_INDEX = 0x01 -} nghttp2_nv_flag; - -/** - * @struct - * - * The name/value pair, which mainly used to represent header fields. - */ -typedef struct { - /** - * The |name| byte string. If this struct is presented from library - * (e.g., :type:`nghttp2_on_frame_recv_callback`), |name| is - * guaranteed to be NULL-terminated. When application is - * constructing this struct, |name| is not required to be - * NULL-terminated. - */ - uint8_t *name; - /** - * The |value| byte string. If this struct is presented from - * library (e.g., :type:`nghttp2_on_frame_recv_callback`), |value| - * is guaranteed to be NULL-terminated. When application is - * constructing this struct, |value| is not required to be - * NULL-terminated. - */ - uint8_t *value; - /** - * The length of the |name|, excluding terminating NULL. - */ - size_t namelen; - /** - * The length of the |value|, excluding terminating NULL. - */ - size_t valuelen; - /** - * Bitwise OR of one or more of :type:`nghttp2_nv_flag`. - */ - uint8_t flags; -} nghttp2_nv; - -/** - * @enum - * - * The frame types in HTTP/2 specification. - */ -typedef enum { - /** - * The DATA frame. - */ - NGHTTP2_DATA = 0, - /** - * The HEADERS frame. - */ - NGHTTP2_HEADERS = 0x01, - /** - * The PRIORITY frame. - */ - NGHTTP2_PRIORITY = 0x02, - /** - * The RST_STREAM frame. - */ - NGHTTP2_RST_STREAM = 0x03, - /** - * The SETTINGS frame. - */ - NGHTTP2_SETTINGS = 0x04, - /** - * The PUSH_PROMISE frame. - */ - NGHTTP2_PUSH_PROMISE = 0x05, - /** - * The PING frame. - */ - NGHTTP2_PING = 0x06, - /** - * The GOAWAY frame. - */ - NGHTTP2_GOAWAY = 0x07, - /** - * The WINDOW_UPDATE frame. - */ - NGHTTP2_WINDOW_UPDATE = 0x08, - /** - * The CONTINUATION frame. This frame type won't be passed to any - * callbacks because the library processes this frame type and its - * preceding HEADERS/PUSH_PROMISE as a single frame. - */ - NGHTTP2_CONTINUATION = 0x09 -} nghttp2_frame_type; - -/** - * @enum - * - * The flags for HTTP/2 frames. This enum defines all flags for all - * frames. - */ -typedef enum { - /** - * No flag set. - */ - NGHTTP2_FLAG_NONE = 0, - /** - * The END_STREAM flag. - */ - NGHTTP2_FLAG_END_STREAM = 0x01, - /** - * The END_HEADERS flag. - */ - NGHTTP2_FLAG_END_HEADERS = 0x04, - /** - * The ACK flag. - */ - NGHTTP2_FLAG_ACK = 0x01, - /** - * The PADDED flag. - */ - NGHTTP2_FLAG_PADDED = 0x08, - /** - * The PRIORITY flag. - */ - NGHTTP2_FLAG_PRIORITY = 0x20 -} nghttp2_flag; - -/** - * @enum - * The SETTINGS ID. - */ -typedef enum { - /** - * SETTINGS_HEADER_TABLE_SIZE - */ - NGHTTP2_SETTINGS_HEADER_TABLE_SIZE = 0x01, - /** - * SETTINGS_ENABLE_PUSH - */ - NGHTTP2_SETTINGS_ENABLE_PUSH = 0x02, - /** - * SETTINGS_MAX_CONCURRENT_STREAMS - */ - NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS = 0x03, - /** - * SETTINGS_INITIAL_WINDOW_SIZE - */ - NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE = 0x04, - /** - * SETTINGS_MAX_FRAME_SIZE - */ - NGHTTP2_SETTINGS_MAX_FRAME_SIZE = 0x05, - /** - * SETTINGS_MAX_HEADER_LIST_SIZE - */ - NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE = 0x06 -} nghttp2_settings_id; -/* Note: If we add SETTINGS, update the capacity of - NGHTTP2_INBOUND_NUM_IV as well */ - -/** - * @macro - * Default maximum concurrent streams. - */ -#define NGHTTP2_INITIAL_MAX_CONCURRENT_STREAMS ((1U << 31) - 1) - -/** - * @enum - * The status codes for the RST_STREAM and GOAWAY frames. - */ -typedef enum { - /** - * No errors. - */ - NGHTTP2_NO_ERROR = 0x00, - /** - * PROTOCOL_ERROR - */ - NGHTTP2_PROTOCOL_ERROR = 0x01, - /** - * INTERNAL_ERROR - */ - NGHTTP2_INTERNAL_ERROR = 0x02, - /** - * FLOW_CONTROL_ERROR - */ - NGHTTP2_FLOW_CONTROL_ERROR = 0x03, - /** - * SETTINGS_TIMEOUT - */ - NGHTTP2_SETTINGS_TIMEOUT = 0x04, - /** - * STREAM_CLOSED - */ - NGHTTP2_STREAM_CLOSED = 0x05, - /** - * FRAME_SIZE_ERROR - */ - NGHTTP2_FRAME_SIZE_ERROR = 0x06, - /** - * REFUSED_STREAM - */ - NGHTTP2_REFUSED_STREAM = 0x07, - /** - * CANCEL - */ - NGHTTP2_CANCEL = 0x08, - /** - * COMPRESSION_ERROR - */ - NGHTTP2_COMPRESSION_ERROR = 0x09, - /** - * CONNECT_ERROR - */ - NGHTTP2_CONNECT_ERROR = 0x0a, - /** - * ENHANCE_YOUR_CALM - */ - NGHTTP2_ENHANCE_YOUR_CALM = 0x0b, - /** - * INADEQUATE_SECURITY - */ - NGHTTP2_INADEQUATE_SECURITY = 0x0c, - /** - * HTTP_1_1_REQUIRED - */ - NGHTTP2_HTTP_1_1_REQUIRED = 0x0d -} nghttp2_error_code; - -/** - * @struct - * The frame header. - */ -typedef struct { - /** - * The length field of this frame, excluding frame header. - */ - size_t length; - /** - * The stream identifier (aka, stream ID) - */ - int32_t stream_id; - /** - * The type of this frame. See `nghttp2_frame_type`. - */ - uint8_t type; - /** - * The flags. - */ - uint8_t flags; - /** - * Reserved bit in frame header. Currently, this is always set to 0 - * and application should not expect something useful in here. - */ - uint8_t reserved; -} nghttp2_frame_hd; - -/** - * @union - * - * This union represents the some kind of data source passed to - * :type:`nghttp2_data_source_read_callback`. - */ -typedef union { - /** - * The integer field, suitable for a file descriptor. - */ - int fd; - /** - * The pointer to an arbitrary object. - */ - void *ptr; -} nghttp2_data_source; - -/** - * @enum - * - * The flags used to set in |data_flags| output parameter in - * :type:`nghttp2_data_source_read_callback`. - */ -typedef enum { - /** - * No flag set. - */ - NGHTTP2_DATA_FLAG_NONE = 0, - /** - * Indicates EOF was sensed. - */ - NGHTTP2_DATA_FLAG_EOF = 0x01, - /** - * Indicates that END_STREAM flag must not be set even if - * NGHTTP2_DATA_FLAG_EOF is set. Usually this flag is used to send - * trailer header fields with `nghttp2_submit_request()` or - * `nghttp2_submit_response()`. - */ - NGHTTP2_DATA_FLAG_NO_END_STREAM = 0x02, - /** - * Indicates that application will send complete DATA frame in - * :type:`nghttp2_send_data_callback`. - */ - NGHTTP2_DATA_FLAG_NO_COPY = 0x04 -} nghttp2_data_flag; - -/** - * @functypedef - * - * Callback function invoked when the library wants to read data from - * the |source|. The read data is sent in the stream |stream_id|. - * The implementation of this function must read at most |length| - * bytes of data from |source| (or possibly other places) and store - * them in |buf| and return number of data stored in |buf|. If EOF is - * reached, set :enum:`NGHTTP2_DATA_FLAG_EOF` flag in |*data_flags|. - * - * Sometime it is desirable to avoid copying data into |buf| and let - * application to send data directly. To achieve this, set - * :enum:`NGHTTP2_DATA_FLAG_NO_COPY` to |*data_flags| (and possibly - * other flags, just like when we do copy), and return the number of - * bytes to send without copying data into |buf|. The library, seeing - * :enum:`NGHTTP2_DATA_FLAG_NO_COPY`, will invoke - * :type:`nghttp2_send_data_callback`. The application must send - * complete DATA frame in that callback. - * - * If this callback is set by `nghttp2_submit_request()`, - * `nghttp2_submit_response()` or `nghttp2_submit_headers()` and - * `nghttp2_submit_data()` with flag parameter - * :enum:`NGHTTP2_FLAG_END_STREAM` set, and - * :enum:`NGHTTP2_DATA_FLAG_EOF` flag is set to |*data_flags|, DATA - * frame will have END_STREAM flag set. Usually, this is expected - * behaviour and all are fine. One exception is send trailer header - * fields. You cannot send trailers after sending frame with - * END_STREAM set. To avoid this problem, one can set - * :enum:`NGHTTP2_DATA_FLAG_NO_END_STREAM` along with - * :enum:`NGHTTP2_DATA_FLAG_EOF` to signal the library not to set - * END_STREAM in DATA frame. Then application can use - * `nghttp2_submit_trailer()` to send trailers. - * `nghttp2_submit_trailer()` can be called inside this callback. - * - * If the application wants to postpone DATA frames (e.g., - * asynchronous I/O, or reading data blocks for long time), it is - * achieved by returning :enum:`NGHTTP2_ERR_DEFERRED` without reading - * any data in this invocation. The library removes DATA frame from - * the outgoing queue temporarily. To move back deferred DATA frame - * to outgoing queue, call `nghttp2_session_resume_data()`. In case - * of error, there are 2 choices. Returning - * :enum:`NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE` will close the stream - * by issuing RST_STREAM with :enum:`NGHTTP2_INTERNAL_ERROR`. If a - * different error code is desirable, use - * `nghttp2_submit_rst_stream()` with a desired error code and then - * return :enum:`NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE`. Returning - * :enum:`NGHTTP2_ERR_CALLBACK_FAILURE` will signal the entire session - * failure. - */ -typedef ssize_t (*nghttp2_data_source_read_callback)( - nghttp2_session *session, int32_t stream_id, uint8_t *buf, size_t length, - uint32_t *data_flags, nghttp2_data_source *source, void *user_data); - -/** - * @struct - * - * This struct represents the data source and the way to read a chunk - * of data from it. - */ -typedef struct { - /** - * The data source. - */ - nghttp2_data_source source; - /** - * The callback function to read a chunk of data from the |source|. - */ - nghttp2_data_source_read_callback read_callback; -} nghttp2_data_provider; - -/** - * @struct - * - * The DATA frame. The received data is delivered via - * :type:`nghttp2_on_data_chunk_recv_callback`. - */ -typedef struct { - nghttp2_frame_hd hd; - /** - * The length of the padding in this frame. This includes PAD_HIGH - * and PAD_LOW. - */ - size_t padlen; -} nghttp2_data; - -/** - * @enum - * - * The category of HEADERS, which indicates the role of the frame. In - * HTTP/2 spec, request, response, push response and other arbitrary - * headers (e.g., trailers) are all called just HEADERS. To give the - * application the role of incoming HEADERS frame, we define several - * categories. - */ -typedef enum { - /** - * The HEADERS frame is opening new stream, which is analogous to - * SYN_STREAM in SPDY. - */ - NGHTTP2_HCAT_REQUEST = 0, - /** - * The HEADERS frame is the first response headers, which is - * analogous to SYN_REPLY in SPDY. - */ - NGHTTP2_HCAT_RESPONSE = 1, - /** - * The HEADERS frame is the first headers sent against reserved - * stream. - */ - NGHTTP2_HCAT_PUSH_RESPONSE = 2, - /** - * The HEADERS frame which does not apply for the above categories, - * which is analogous to HEADERS in SPDY. If non-final response - * (e.g., status 1xx) is used, final response HEADERS frame will be - * categorized here. - */ - NGHTTP2_HCAT_HEADERS = 3 -} nghttp2_headers_category; - -/** - * @struct - * - * The structure to specify stream dependency. - */ -typedef struct { - /** - * The stream ID of the stream to depend on. Specifying 0 makes - * stream not depend any other stream. - */ - int32_t stream_id; - /** - * The weight of this dependency. - */ - int32_t weight; - /** - * nonzero means exclusive dependency - */ - uint8_t exclusive; -} nghttp2_priority_spec; - -/** - * @struct - * - * The HEADERS frame. It has the following members: - */ -typedef struct { - /** - * The frame header. - */ - nghttp2_frame_hd hd; - /** - * The length of the padding in this frame. This includes PAD_HIGH - * and PAD_LOW. - */ - size_t padlen; - /** - * The priority specification - */ - nghttp2_priority_spec pri_spec; - /** - * The name/value pairs. - */ - nghttp2_nv *nva; - /** - * The number of name/value pairs in |nva|. - */ - size_t nvlen; - /** - * The category of this HEADERS frame. - */ - nghttp2_headers_category cat; -} nghttp2_headers; - -/** - * @struct - * - * The PRIORITY frame. It has the following members: - */ -typedef struct { - /** - * The frame header. - */ - nghttp2_frame_hd hd; - /** - * The priority specification. - */ - nghttp2_priority_spec pri_spec; -} nghttp2_priority; - -/** - * @struct - * - * The RST_STREAM frame. It has the following members: - */ -typedef struct { - /** - * The frame header. - */ - nghttp2_frame_hd hd; - /** - * The error code. See :type:`nghttp2_error_code`. - */ - uint32_t error_code; -} nghttp2_rst_stream; - -/** - * @struct - * - * The SETTINGS ID/Value pair. It has the following members: - */ -typedef struct { - /** - * The SETTINGS ID. See :type:`nghttp2_settings_id`. - */ - int32_t settings_id; - /** - * The value of this entry. - */ - uint32_t value; -} nghttp2_settings_entry; - -/** - * @struct - * - * The SETTINGS frame. It has the following members: - */ -typedef struct { - /** - * The frame header. - */ - nghttp2_frame_hd hd; - /** - * The number of SETTINGS ID/Value pairs in |iv|. - */ - size_t niv; - /** - * The pointer to the array of SETTINGS ID/Value pair. - */ - nghttp2_settings_entry *iv; -} nghttp2_settings; - -/** - * @struct - * - * The PUSH_PROMISE frame. It has the following members: - */ -typedef struct { - /** - * The frame header. - */ - nghttp2_frame_hd hd; - /** - * The length of the padding in this frame. This includes PAD_HIGH - * and PAD_LOW. - */ - size_t padlen; - /** - * The name/value pairs. - */ - nghttp2_nv *nva; - /** - * The number of name/value pairs in |nva|. - */ - size_t nvlen; - /** - * The promised stream ID - */ - int32_t promised_stream_id; - /** - * Reserved bit. Currently this is always set to 0 and application - * should not expect something useful in here. - */ - uint8_t reserved; -} nghttp2_push_promise; - -/** - * @struct - * - * The PING frame. It has the following members: - */ -typedef struct { - /** - * The frame header. - */ - nghttp2_frame_hd hd; - /** - * The opaque data - */ - uint8_t opaque_data[8]; -} nghttp2_ping; - -/** - * @struct - * - * The GOAWAY frame. It has the following members: - */ -typedef struct { - /** - * The frame header. - */ - nghttp2_frame_hd hd; - /** - * The last stream stream ID. - */ - int32_t last_stream_id; - /** - * The error code. See :type:`nghttp2_error_code`. - */ - uint32_t error_code; - /** - * The additional debug data - */ - uint8_t *opaque_data; - /** - * The length of |opaque_data| member. - */ - size_t opaque_data_len; - /** - * Reserved bit. Currently this is always set to 0 and application - * should not expect something useful in here. - */ - uint8_t reserved; -} nghttp2_goaway; - -/** - * @struct - * - * The WINDOW_UPDATE frame. It has the following members: - */ -typedef struct { - /** - * The frame header. - */ - nghttp2_frame_hd hd; - /** - * The window size increment. - */ - int32_t window_size_increment; - /** - * Reserved bit. Currently this is always set to 0 and application - * should not expect something useful in here. - */ - uint8_t reserved; -} nghttp2_window_update; - -/** - * @struct - * - * The extension frame. It has following members: - */ -typedef struct { - /** - * The frame header. - */ - nghttp2_frame_hd hd; - /** - * The pointer to extension payload. The exact pointer type is - * determined by hd.type. - * - * Currently, no extension is supported. This is a place holder for - * the future extensions. - */ - void *payload; -} nghttp2_extension; - -/** - * @union - * - * This union includes all frames to pass them to various function - * calls as nghttp2_frame type. The CONTINUATION frame is omitted - * from here because the library deals with it internally. - */ -typedef union { - /** - * The frame header, which is convenient to inspect frame header. - */ - nghttp2_frame_hd hd; - /** - * The DATA frame. - */ - nghttp2_data data; - /** - * The HEADERS frame. - */ - nghttp2_headers headers; - /** - * The PRIORITY frame. - */ - nghttp2_priority priority; - /** - * The RST_STREAM frame. - */ - nghttp2_rst_stream rst_stream; - /** - * The SETTINGS frame. - */ - nghttp2_settings settings; - /** - * The PUSH_PROMISE frame. - */ - nghttp2_push_promise push_promise; - /** - * The PING frame. - */ - nghttp2_ping ping; - /** - * The GOAWAY frame. - */ - nghttp2_goaway goaway; - /** - * The WINDOW_UPDATE frame. - */ - nghttp2_window_update window_update; - /** - * The extension frame. - */ - nghttp2_extension ext; -} nghttp2_frame; - -/** - * @functypedef - * - * Callback function invoked when |session| wants to send data to the - * remote peer. The implementation of this function must send at most - * |length| bytes of data stored in |data|. The |flags| is currently - * not used and always 0. It must return the number of bytes sent if - * it succeeds. If it cannot send any single byte without blocking, - * it must return :enum:`NGHTTP2_ERR_WOULDBLOCK`. For other errors, - * it must return :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`. The - * |user_data| pointer is the third argument passed in to the call to - * `nghttp2_session_client_new()` or `nghttp2_session_server_new()`. - * - * This callback is required if the application uses - * `nghttp2_session_send()` to send data to the remote endpoint. If - * the application uses solely `nghttp2_session_mem_send()` instead, - * this callback function is unnecessary. - * - * To set this callback to :type:`nghttp2_session_callbacks`, use - * `nghttp2_session_callbacks_set_send_callback()`. - */ -typedef ssize_t (*nghttp2_send_callback)(nghttp2_session *session, - const uint8_t *data, size_t length, - int flags, void *user_data); - -/** - * @functypedef - * - * Callback function invoked when :enum:`NGHTTP2_DATA_FLAG_NO_COPY` is - * used in :type:`nghttp2_data_source_read_callback` to send complete - * DATA frame. - * - * The |frame| is a DATA frame to send. The |framehd| is the - * serialized frame header (9 bytes). The |length| is the length of - * application data to send (this does not include padding). The - * |source| is the same pointer passed to - * :type:`nghttp2_data_source_read_callback`. - * - * The application first must send frame header |framehd| of length 9 - * bytes. If ``frame->padlen > 0``, send 1 byte of value - * ``frame->padlen - 1``. Then send exactly |length| bytes of - * application data. Finally, if ``frame->padlen > 0``, send - * ``frame->padlen - 1`` bytes of zero (they are padding). - * - * The application has to send complete DATA frame in this callback. - * If all data were written successfully, return 0. - * - * If it cannot send it all, just return - * :enum:`NGHTTP2_ERR_WOULDBLOCK`; the library will call this callback - * with the same parameters later (It is recommended to send complete - * DATA frame at once in this function to deal with error; if partial - * frame data has already sent, it is impossible to send another data - * in that state, and all we can do is tear down connection). If - * application decided to reset this stream, return - * :enum:`NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE`, then the library - * will send RST_STREAM with INTERNAL_ERROR as error code. The - * application can also return :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`, - * which will result in connection closure. Returning any other value - * is treated as :enum:`NGHTTP2_ERR_CALLBACK_FAILURE` is returned. - */ -typedef int (*nghttp2_send_data_callback)(nghttp2_session *session, - nghttp2_frame *frame, - const uint8_t *framehd, size_t length, - nghttp2_data_source *source, - void *user_data); - -/** - * @functypedef - * - * Callback function invoked when |session| wants to receive data from - * the remote peer. The implementation of this function must read at - * most |length| bytes of data and store it in |buf|. The |flags| is - * currently not used and always 0. It must return the number of - * bytes written in |buf| if it succeeds. If it cannot read any - * single byte without blocking, it must return - * :enum:`NGHTTP2_ERR_WOULDBLOCK`. If it gets EOF before it reads any - * single byte, it must return :enum:`NGHTTP2_ERR_EOF`. For other - * errors, it must return :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`. - * Returning 0 is treated as :enum:`NGHTTP2_ERR_WOULDBLOCK`. The - * |user_data| pointer is the third argument passed in to the call to - * `nghttp2_session_client_new()` or `nghttp2_session_server_new()`. - * - * This callback is required if the application uses - * `nghttp2_session_recv()` to receive data from the remote endpoint. - * If the application uses solely `nghttp2_session_mem_recv()` - * instead, this callback function is unnecessary. - * - * To set this callback to :type:`nghttp2_session_callbacks`, use - * `nghttp2_session_callbacks_set_recv_callback()`. - */ -typedef ssize_t (*nghttp2_recv_callback)(nghttp2_session *session, uint8_t *buf, - size_t length, int flags, - void *user_data); - -/** - * @functypedef - * - * Callback function invoked by `nghttp2_session_recv()` and - * `nghttp2_session_mem_recv()` when a frame is received. The - * |user_data| pointer is the third argument passed in to the call to - * `nghttp2_session_client_new()` or `nghttp2_session_server_new()`. - * - * If frame is HEADERS or PUSH_PROMISE, the ``nva`` and ``nvlen`` - * member of their data structure are always ``NULL`` and 0 - * respectively. The header name/value pairs are emitted via - * :type:`nghttp2_on_header_callback`. - * - * For HEADERS, PUSH_PROMISE and DATA frames, this callback may be - * called after stream is closed (see - * :type:`nghttp2_on_stream_close_callback`). The application should - * check that stream is still alive using its own stream management or - * :func:`nghttp2_session_get_stream_user_data()`. - * - * Only HEADERS and DATA frame can signal the end of incoming data. - * If ``frame->hd.flags & NGHTTP2_FLAG_END_STREAM`` is nonzero, the - * |frame| is the last frame from the remote peer in this stream. - * - * This callback won't be called for CONTINUATION frames. - * HEADERS/PUSH_PROMISE + CONTINUATIONs are treated as single frame. - * - * The implementation of this function must return 0 if it succeeds. - * If nonzero value is returned, it is treated as fatal error and - * `nghttp2_session_recv()` and `nghttp2_session_mem_recv()` functions - * immediately return :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`. - * - * To set this callback to :type:`nghttp2_session_callbacks`, use - * `nghttp2_session_callbacks_set_on_frame_recv_callback()`. - */ -typedef int (*nghttp2_on_frame_recv_callback)(nghttp2_session *session, - const nghttp2_frame *frame, - void *user_data); - -/** - * @functypedef - * - * Callback function invoked by `nghttp2_session_recv()` and - * `nghttp2_session_mem_recv()` when an invalid non-DATA frame is - * received. The error is indicated by the |lib_error_code|, which is - * one of the values defined in :type:`nghttp2_error`. When this - * callback function is invoked, the library automatically submits - * either RST_STREAM or GOAWAY frame. The |user_data| pointer is the - * third argument passed in to the call to - * `nghttp2_session_client_new()` or `nghttp2_session_server_new()`. - * - * If frame is HEADERS or PUSH_PROMISE, the ``nva`` and ``nvlen`` - * member of their data structure are always ``NULL`` and 0 - * respectively. - * - * The implementation of this function must return 0 if it succeeds. - * If nonzero is returned, it is treated as fatal error and - * `nghttp2_session_recv()` and `nghttp2_session_mem_recv()` functions - * immediately return :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`. - * - * To set this callback to :type:`nghttp2_session_callbacks`, use - * `nghttp2_session_callbacks_set_on_invalid_frame_recv_callback()`. - */ -typedef int (*nghttp2_on_invalid_frame_recv_callback)( - nghttp2_session *session, const nghttp2_frame *frame, int lib_error_code, - void *user_data); - -/** - * @functypedef - * - * Callback function invoked when a chunk of data in DATA frame is - * received. The |stream_id| is the stream ID this DATA frame belongs - * to. The |flags| is the flags of DATA frame which this data chunk - * is contained. ``(flags & NGHTTP2_FLAG_END_STREAM) != 0`` does not - * necessarily mean this chunk of data is the last one in the stream. - * You should use :type:`nghttp2_on_frame_recv_callback` to know all - * data frames are received. The |user_data| pointer is the third - * argument passed in to the call to `nghttp2_session_client_new()` or - * `nghttp2_session_server_new()`. - * - * If the application uses `nghttp2_session_mem_recv()`, it can return - * :enum:`NGHTTP2_ERR_PAUSE` to make `nghttp2_session_mem_recv()` - * return without processing further input bytes. The memory by - * pointed by the |data| is retained until - * `nghttp2_session_mem_recv()` or `nghttp2_session_recv()` is called. - * The application must retain the input bytes which was used to - * produce the |data| parameter, because it may refer to the memory - * region included in the input bytes. - * - * The implementation of this function must return 0 if it succeeds. - * If nonzero is returned, it is treated as fatal error, and - * `nghttp2_session_recv()` and `nghttp2_session_mem_recv()` functions - * immediately return :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`. - * - * To set this callback to :type:`nghttp2_session_callbacks`, use - * `nghttp2_session_callbacks_set_on_data_chunk_recv_callback()`. - */ -typedef int (*nghttp2_on_data_chunk_recv_callback)(nghttp2_session *session, - uint8_t flags, - int32_t stream_id, - const uint8_t *data, - size_t len, void *user_data); - -/** - * @functypedef - * - * Callback function invoked just before the non-DATA frame |frame| is - * sent. The |user_data| pointer is the third argument passed in to - * the call to `nghttp2_session_client_new()` or - * `nghttp2_session_server_new()`. - * - * The implementation of this function must return 0 if it succeeds. - * If nonzero is returned, it is treated as fatal error and - * `nghttp2_session_send()` and `nghttp2_session_mem_send()` functions - * immediately return :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`. - * - * To set this callback to :type:`nghttp2_session_callbacks`, use - * `nghttp2_session_callbacks_set_before_frame_send_callback()`. - */ -typedef int (*nghttp2_before_frame_send_callback)(nghttp2_session *session, - const nghttp2_frame *frame, - void *user_data); - -/** - * @functypedef - * - * Callback function invoked after the frame |frame| is sent. The - * |user_data| pointer is the third argument passed in to the call to - * `nghttp2_session_client_new()` or `nghttp2_session_server_new()`. - * - * The implementation of this function must return 0 if it succeeds. - * If nonzero is returned, it is treated as fatal error and - * `nghttp2_session_send()` and `nghttp2_session_mem_send()` functions - * immediately return :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`. - * - * To set this callback to :type:`nghttp2_session_callbacks`, use - * `nghttp2_session_callbacks_set_on_frame_send_callback()`. - */ -typedef int (*nghttp2_on_frame_send_callback)(nghttp2_session *session, - const nghttp2_frame *frame, - void *user_data); - -/** - * @functypedef - * - * Callback function invoked after the non-DATA frame |frame| is not - * sent because of the error. The error is indicated by the - * |lib_error_code|, which is one of the values defined in - * :type:`nghttp2_error`. The |user_data| pointer is the third - * argument passed in to the call to `nghttp2_session_client_new()` or - * `nghttp2_session_server_new()`. - * - * The implementation of this function must return 0 if it succeeds. - * If nonzero is returned, it is treated as fatal error and - * `nghttp2_session_send()` and `nghttp2_session_mem_send()` functions - * immediately return :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`. - * - * `nghttp2_session_get_stream_user_data()` can be used to get - * associated data. - * - * To set this callback to :type:`nghttp2_session_callbacks`, use - * `nghttp2_session_callbacks_set_on_frame_not_send_callback()`. - */ -typedef int (*nghttp2_on_frame_not_send_callback)(nghttp2_session *session, - const nghttp2_frame *frame, - int lib_error_code, - void *user_data); - -/** - * @functypedef - * - * Callback function invoked when the stream |stream_id| is closed. - * The reason of closure is indicated by the |error_code|. The - * |error_code| is usually one of :enum:`nghttp2_error_code`, but that - * is not guaranteed. The stream_user_data, which was specified in - * `nghttp2_submit_request()` or `nghttp2_submit_headers()`, is still - * available in this function. The |user_data| pointer is the third - * argument passed in to the call to `nghttp2_session_client_new()` or - * `nghttp2_session_server_new()`. - * - * This function is also called for a stream in reserved state. - * - * The implementation of this function must return 0 if it succeeds. - * If nonzero is returned, it is treated as fatal error and - * `nghttp2_session_recv()`, `nghttp2_session_mem_recv()`, - * `nghttp2_session_send()`, and `nghttp2_session_mem_send()` - * functions immediately return :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`. - * - * To set this callback to :type:`nghttp2_session_callbacks`, use - * `nghttp2_session_callbacks_set_on_stream_close_callback()`. - */ -typedef int (*nghttp2_on_stream_close_callback)(nghttp2_session *session, - int32_t stream_id, - uint32_t error_code, - void *user_data); - -/** - * @functypedef - * - * Callback function invoked when the reception of header block in - * HEADERS or PUSH_PROMISE is started. Each header name/value pair - * will be emitted by :type:`nghttp2_on_header_callback`. - * - * The ``frame->hd.flags`` may not have - * :enum:`NGHTTP2_FLAG_END_HEADERS` flag set, which indicates that one - * or more CONTINUATION frames are involved. But the application does - * not need to care about that because the header name/value pairs are - * emitted transparently regardless of CONTINUATION frames. - * - * The server applications probably create an object to store - * information about new stream if ``frame->hd.type == - * NGHTTP2_HEADERS`` and ``frame->headers.cat == - * NGHTTP2_HCAT_REQUEST``. If |session| is configured as server side, - * ``frame->headers.cat`` is either ``NGHTTP2_HCAT_REQUEST`` - * containing request headers or ``NGHTTP2_HCAT_HEADERS`` containing - * trailer headers and never get PUSH_PROMISE in this callback. - * - * For the client applications, ``frame->hd.type`` is either - * ``NGHTTP2_HEADERS`` or ``NGHTTP2_PUSH_PROMISE``. In case of - * ``NGHTTP2_HEADERS``, ``frame->headers.cat == - * NGHTTP2_HCAT_RESPONSE`` means that it is the first response - * headers, but it may be non-final response which is indicated by 1xx - * status code. In this case, there may be zero or more HEADERS frame - * with ``frame->headers.cat == NGHTTP2_HCAT_HEADERS`` which has - * non-final response code and finally client gets exactly one HEADERS - * frame with ``frame->headers.cat == NGHTTP2_HCAT_HEADERS`` - * containing final response headers (non-1xx status code). The - * trailer headers also has ``frame->headers.cat == - * NGHTTP2_HCAT_HEADERS`` which does not contain any status code. - * - * Returning :enum:`NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE` will close - * the stream (promised stream if frame is PUSH_PROMISE) by issuing - * RST_STREAM with :enum:`NGHTTP2_INTERNAL_ERROR`. In this case, - * :type:`nghttp2_on_header_callback` and - * :type:`nghttp2_on_frame_recv_callback` will not be invoked. If a - * different error code is desirable, use - * `nghttp2_submit_rst_stream()` with a desired error code and then - * return :enum:`NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE`. Again, use - * ``frame->push_promise.promised_stream_id`` as stream_id parameter - * in `nghttp2_submit_rst_stream()` if frame is PUSH_PROMISE. - * - * The implementation of this function must return 0 if it succeeds. - * It can return :enum:`NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE` to - * reset the stream (promised stream if frame is PUSH_PROMISE). For - * critical errors, it must return - * :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`. If the other value is - * returned, it is treated as if :enum:`NGHTTP2_ERR_CALLBACK_FAILURE` - * is returned. If :enum:`NGHTTP2_ERR_CALLBACK_FAILURE` is returned, - * `nghttp2_session_mem_recv()` function will immediately return - * :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`. - * - * To set this callback to :type:`nghttp2_session_callbacks`, use - * `nghttp2_session_callbacks_set_on_begin_headers_callback()`. - */ -typedef int (*nghttp2_on_begin_headers_callback)(nghttp2_session *session, - const nghttp2_frame *frame, - void *user_data); - -/** - * @functypedef - * - * Callback function invoked when a header name/value pair is received - * for the |frame|. The |name| of length |namelen| is header name. - * The |value| of length |valuelen| is header value. The |flags| is - * bitwise OR of one or more of :type:`nghttp2_nv_flag`. - * - * If :enum:`NGHTTP2_NV_FLAG_NO_INDEX` is set in |flags|, the receiver - * must not index this name/value pair when forwarding it to the next - * hop. More specifically, "Literal Header Field never Indexed" - * representation must be used in HPACK encoding. - * - * When this callback is invoked, ``frame->hd.type`` is either - * :enum:`NGHTTP2_HEADERS` or :enum:`NGHTTP2_PUSH_PROMISE`. After all - * header name/value pairs are processed with this callback, and no - * error has been detected, :type:`nghttp2_on_frame_recv_callback` - * will be invoked. If there is an error in decompression, - * :type:`nghttp2_on_frame_recv_callback` for the |frame| will not be - * invoked. - * - * Both |name| and |value| are guaranteed to be NULL-terminated. The - * |namelen| and |valuelen| do not include terminal NULL. If - * `nghttp2_option_set_no_http_messaging()` is used with nonzero - * value, NULL character may be included in |name| or |value| before - * terminating NULL. - * - * Please note that unless `nghttp2_option_set_no_http_messaging()` is - * used, nghttp2 library does perform validation against the |name| - * and the |value| using `nghttp2_check_header_name()` and - * `nghttp2_check_header_value()`. In addition to this, nghttp2 - * performs vaidation based on HTTP Messaging rule, which is briefly - * explained in :ref:`http-messaging` section. - * - * If the application uses `nghttp2_session_mem_recv()`, it can return - * :enum:`NGHTTP2_ERR_PAUSE` to make `nghttp2_session_mem_recv()` - * return without processing further input bytes. The memory pointed - * by |frame|, |name| and |value| parameters are retained until - * `nghttp2_session_mem_recv()` or `nghttp2_session_recv()` is called. - * The application must retain the input bytes which was used to - * produce these parameters, because it may refer to the memory region - * included in the input bytes. - * - * Returning :enum:`NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE` will close - * the stream (promised stream if frame is PUSH_PROMISE) by issuing - * RST_STREAM with :enum:`NGHTTP2_INTERNAL_ERROR`. In this case, - * :type:`nghttp2_on_header_callback` and - * :type:`nghttp2_on_frame_recv_callback` will not be invoked. If a - * different error code is desirable, use - * `nghttp2_submit_rst_stream()` with a desired error code and then - * return :enum:`NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE`. Again, use - * ``frame->push_promise.promised_stream_id`` as stream_id parameter - * in `nghttp2_submit_rst_stream()` if frame is PUSH_PROMISE. - * - * The implementation of this function must return 0 if it succeeds. - * It may return :enum:`NGHTTP2_ERR_PAUSE` or - * :enum:`NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE`. For other critical - * failures, it must return :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`. If - * the other nonzero value is returned, it is treated as - * :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`. If - * :enum:`NGHTTP2_ERR_CALLBACK_FAILURE` is returned, - * `nghttp2_session_recv()` and `nghttp2_session_mem_recv()` functions - * immediately return :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`. - * - * To set this callback to :type:`nghttp2_session_callbacks`, use - * `nghttp2_session_callbacks_set_on_header_callback()`. - */ -typedef int (*nghttp2_on_header_callback)(nghttp2_session *session, - const nghttp2_frame *frame, - const uint8_t *name, size_t namelen, - const uint8_t *value, size_t valuelen, - uint8_t flags, void *user_data); - -/** - * @functypedef - * - * Callback function invoked when the library asks application how - * many padding bytes are required for the transmission of the - * |frame|. The application must choose the total length of payload - * including padded bytes in range [frame->hd.length, max_payloadlen], - * inclusive. Choosing number not in this range will be treated as - * :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`. Returning - * ``frame->hd.length`` means no padding is added. Returning - * :enum:`NGHTTP2_ERR_CALLBACK_FAILURE` will make - * `nghttp2_session_send()` and `nghttp2_session_mem_send()` functions - * immediately return :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`. - * - * To set this callback to :type:`nghttp2_session_callbacks`, use - * `nghttp2_session_callbacks_set_select_padding_callback()`. - */ -typedef ssize_t (*nghttp2_select_padding_callback)(nghttp2_session *session, - const nghttp2_frame *frame, - size_t max_payloadlen, - void *user_data); - -/** - * @functypedef - * - * Callback function invoked when library wants to get max length of - * data to send data to the remote peer. The implementation of this - * function should return a value in the following range. [1, - * min(|session_remote_window_size|, |stream_remote_window_size|, - * |remote_max_frame_size|)]. If a value greater than this range is - * returned than the max allow value will be used. Returning a value - * smaller than this range is treated as - * :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`. The |frame_type| is provided - * for future extensibility and identifies the type of frame (see - * :type:`nghttp2_frame_type`) for which to get the length for. - * Currently supported frame types are: :enum:`NGHTTP2_DATA`. - * - * This callback can be used to control the length in bytes for which - * :type:`nghttp2_data_source_read_callback` is allowed to send to the - * remote endpoint. This callback is optional. Returning - * :enum:`NGHTTP2_ERR_CALLBACK_FAILURE` will signal the entire session - * failure. - * - * To set this callback to :type:`nghttp2_session_callbacks`, use - * `nghttp2_session_callbacks_set_data_source_read_length_callback()`. - */ -typedef ssize_t (*nghttp2_data_source_read_length_callback)( - nghttp2_session *session, uint8_t frame_type, int32_t stream_id, - int32_t session_remote_window_size, int32_t stream_remote_window_size, - uint32_t remote_max_frame_size, void *user_data); - -/** - * @functypedef - * - * Callback function invoked when a frame header is received. The - * |hd| points to received frame header. - * - * Unlike :type:`nghttp2_on_frame_recv_callback`, this callback will - * also be called when frame header of CONTINUATION frame is received. - * - * If both :type:`nghttp2_on_begin_frame_callback` and - * :type:`nghttp2_on_begin_headers_callback` are set and HEADERS or - * PUSH_PROMISE is received, :type:`nghttp2_on_begin_frame_callback` - * will be called first. - * - * The implementation of this function must return 0 if it succeeds. - * If nonzero value is returned, it is treated as fatal error and - * `nghttp2_session_recv()` and `nghttp2_session_mem_recv()` functions - * immediately return :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`. - * - * To set this callback to :type:`nghttp2_session_callbacks`, use - * `nghttp2_session_callbacks_set_on_begin_frame_callback()`. - */ -typedef int (*nghttp2_on_begin_frame_callback)(nghttp2_session *session, - const nghttp2_frame_hd *hd, - void *user_data); - -struct nghttp2_session_callbacks; - -/** - * @struct - * - * Callback functions for :type:`nghttp2_session`. The details of - * this structure are intentionally hidden from the public API. - */ -typedef struct nghttp2_session_callbacks nghttp2_session_callbacks; - -/** - * @function - * - * Initializes |*callbacks_ptr| with NULL values. - * - * The initialized object can be used when initializing multiple - * :type:`nghttp2_session` objects. - * - * When the application finished using this object, it can use - * `nghttp2_session_callbacks_del()` to free its memory. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * :enum:`NGHTTP2_ERR_NOMEM` - * Out of memory. - */ -NGHTTP2_EXTERN int -nghttp2_session_callbacks_new(nghttp2_session_callbacks **callbacks_ptr); - -/** - * @function - * - * Frees any resources allocated for |callbacks|. If |callbacks| is - * ``NULL``, this function does nothing. - */ -NGHTTP2_EXTERN void -nghttp2_session_callbacks_del(nghttp2_session_callbacks *callbacks); - -/** - * @function - * - * Sets callback function invoked when a session wants to send data to - * the remote peer. This callback is not necessary if the application - * uses solely `nghttp2_session_mem_send()` to serialize data to - * transmit. - */ -NGHTTP2_EXTERN void nghttp2_session_callbacks_set_send_callback( - nghttp2_session_callbacks *cbs, nghttp2_send_callback send_callback); - -/** - * @function - * - * Sets callback function invoked when the a session wants to receive - * data from the remote peer. This callback is not necessary if the - * application uses solely `nghttp2_session_mem_recv()` to process - * received data. - */ -NGHTTP2_EXTERN void nghttp2_session_callbacks_set_recv_callback( - nghttp2_session_callbacks *cbs, nghttp2_recv_callback recv_callback); - -/** - * @function - * - * Sets callback function invoked by `nghttp2_session_recv()` and - * `nghttp2_session_mem_recv()` when a frame is received. - */ -NGHTTP2_EXTERN void nghttp2_session_callbacks_set_on_frame_recv_callback( - nghttp2_session_callbacks *cbs, - nghttp2_on_frame_recv_callback on_frame_recv_callback); - -/** - * @function - * - * Sets callback function invoked by `nghttp2_session_recv()` and - * `nghttp2_session_mem_recv()` when an invalid non-DATA frame is - * received. - */ -NGHTTP2_EXTERN void -nghttp2_session_callbacks_set_on_invalid_frame_recv_callback( - nghttp2_session_callbacks *cbs, - nghttp2_on_invalid_frame_recv_callback on_invalid_frame_recv_callback); - -/** - * @function - * - * Sets callback function invoked when a chunk of data in DATA frame - * is received. - */ -NGHTTP2_EXTERN void nghttp2_session_callbacks_set_on_data_chunk_recv_callback( - nghttp2_session_callbacks *cbs, - nghttp2_on_data_chunk_recv_callback on_data_chunk_recv_callback); - -/** - * @function - * - * Sets callback function invoked before a non-DATA frame is sent. - */ -NGHTTP2_EXTERN void nghttp2_session_callbacks_set_before_frame_send_callback( - nghttp2_session_callbacks *cbs, - nghttp2_before_frame_send_callback before_frame_send_callback); - -/** - * @function - * - * Sets callback function invoked after a frame is sent. - */ -NGHTTP2_EXTERN void nghttp2_session_callbacks_set_on_frame_send_callback( - nghttp2_session_callbacks *cbs, - nghttp2_on_frame_send_callback on_frame_send_callback); - -/** - * @function - * - * Sets callback function invoked when a non-DATA frame is not sent - * because of an error. - */ -NGHTTP2_EXTERN void nghttp2_session_callbacks_set_on_frame_not_send_callback( - nghttp2_session_callbacks *cbs, - nghttp2_on_frame_not_send_callback on_frame_not_send_callback); - -/** - * @function - * - * Sets callback function invoked when the stream is closed. - */ -NGHTTP2_EXTERN void nghttp2_session_callbacks_set_on_stream_close_callback( - nghttp2_session_callbacks *cbs, - nghttp2_on_stream_close_callback on_stream_close_callback); - -/** - * @function - * - * Sets callback function invoked when the reception of header block - * in HEADERS or PUSH_PROMISE is started. - */ -NGHTTP2_EXTERN void nghttp2_session_callbacks_set_on_begin_headers_callback( - nghttp2_session_callbacks *cbs, - nghttp2_on_begin_headers_callback on_begin_headers_callback); - -/** - * @function - * - * Sets callback function invoked when a header name/value pair is - * received. - */ -NGHTTP2_EXTERN void nghttp2_session_callbacks_set_on_header_callback( - nghttp2_session_callbacks *cbs, - nghttp2_on_header_callback on_header_callback); - -/** - * @function - * - * Sets callback function invoked when the library asks application - * how many padding bytes are required for the transmission of the - * given frame. - */ -NGHTTP2_EXTERN void nghttp2_session_callbacks_set_select_padding_callback( - nghttp2_session_callbacks *cbs, - nghttp2_select_padding_callback select_padding_callback); - -/** - * @function - * - * Sets callback function determine the length allowed in - * :type:`nghttp2_data_source_read_callback`. - */ -NGHTTP2_EXTERN void -nghttp2_session_callbacks_set_data_source_read_length_callback( - nghttp2_session_callbacks *cbs, - nghttp2_data_source_read_length_callback data_source_read_length_callback); - -/** - * @function - * - * Sets callback function invoked when a frame header is received. - */ -NGHTTP2_EXTERN void nghttp2_session_callbacks_set_on_begin_frame_callback( - nghttp2_session_callbacks *cbs, - nghttp2_on_begin_frame_callback on_begin_frame_callback); - -/** - * @function - * - * Sets callback function invoked when - * :enum:`NGHTTP2_DATA_FLAG_NO_COPY` is used in - * :type:`nghttp2_data_source_read_callback` to avoid data copy. - */ -NGHTTP2_EXTERN void nghttp2_session_callbacks_set_send_data_callback( - nghttp2_session_callbacks *cbs, - nghttp2_send_data_callback send_data_callback); - -/** - * @functypedef - * - * Custom memory allocator to replace malloc(). The |mem_user_data| - * is the mem_user_data member of :type:`nghttp2_mem` structure. - */ -typedef void *(*nghttp2_malloc)(size_t size, void *mem_user_data); - -/** - * @functypedef - * - * Custom memory allocator to replace free(). The |mem_user_data| is - * the mem_user_data member of :type:`nghttp2_mem` structure. - */ -typedef void (*nghttp2_free)(void *ptr, void *mem_user_data); - -/** - * @functypedef - * - * Custom memory allocator to replace calloc(). The |mem_user_data| - * is the mem_user_data member of :type:`nghttp2_mem` structure. - */ -typedef void *(*nghttp2_calloc)(size_t nmemb, size_t size, void *mem_user_data); - -/** - * @functypedef - * - * Custom memory allocator to replace realloc(). The |mem_user_data| - * is the mem_user_data member of :type:`nghttp2_mem` structure. - */ -typedef void *(*nghttp2_realloc)(void *ptr, size_t size, void *mem_user_data); - -/** - * @struct - * - * Custom memory allocator functions and user defined pointer. The - * |mem_user_data| member is passed to each allocator function. This - * can be used, for example, to achieve per-session memory pool. - * - * In the following example code, ``my_malloc``, ``my_free``, - * ``my_calloc`` and ``my_realloc`` are the replacement of the - * standard allocators ``malloc``, ``free``, ``calloc`` and - * ``realloc`` respectively:: - * - * void *my_malloc_cb(size_t size, void *mem_user_data) { - * return my_malloc(size); - * } - * - * void my_free_cb(void *ptr, void *mem_user_data) { my_free(ptr); } - * - * void *my_calloc_cb(size_t nmemb, size_t size, void *mem_user_data) { - * return my_calloc(nmemb, size); - * } - * - * void *my_realloc_cb(void *ptr, size_t size, void *mem_user_data) { - * return my_realloc(ptr, size); - * } - * - * void session_new() { - * nghttp2_session *session; - * nghttp2_session_callbacks *callbacks; - * nghttp2_mem mem = {NULL, my_malloc_cb, my_free_cb, my_calloc_cb, - * my_realloc_cb}; - * - * ... - * - * nghttp2_session_client_new3(&session, callbacks, NULL, NULL, &mem); - * - * ... - * } - */ -typedef struct { - /** - * An arbitrary user supplied data. This is passed to each - * allocator function. - */ - void *mem_user_data; - /** - * Custom allocator function to replace malloc(). - */ - nghttp2_malloc malloc; - /** - * Custom allocator function to replace free(). - */ - nghttp2_free free; - /** - * Custom allocator function to replace calloc(). - */ - nghttp2_calloc calloc; - /** - * Custom allocator function to replace realloc(). - */ - nghttp2_realloc realloc; -} nghttp2_mem; - -struct nghttp2_option; - -/** - * @struct - * - * Configuration options for :type:`nghttp2_session`. The details of - * this structure are intentionally hidden from the public API. - */ -typedef struct nghttp2_option nghttp2_option; - -/** - * @function - * - * Initializes |*option_ptr| with default values. - * - * When the application finished using this object, it can use - * `nghttp2_option_del()` to free its memory. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * :enum:`NGHTTP2_ERR_NOMEM` - * Out of memory. - */ -NGHTTP2_EXTERN int nghttp2_option_new(nghttp2_option **option_ptr); - -/** - * @function - * - * Frees any resources allocated for |option|. If |option| is - * ``NULL``, this function does nothing. - */ -NGHTTP2_EXTERN void nghttp2_option_del(nghttp2_option *option); - -/** - * @function - * - * This option prevents the library from sending WINDOW_UPDATE for a - * connection automatically. If this option is set to nonzero, the - * library won't send WINDOW_UPDATE for DATA until application calls - * `nghttp2_session_consume()` to indicate the consumed amount of - * data. Don't use `nghttp2_submit_window_update()` for this purpose. - * By default, this option is set to zero. - */ -NGHTTP2_EXTERN void -nghttp2_option_set_no_auto_window_update(nghttp2_option *option, int val); - -/** - * @function - * - * This option sets the SETTINGS_MAX_CONCURRENT_STREAMS value of - * remote endpoint as if it is received in SETTINGS frame. Without - * specifying this option, before the local endpoint receives - * SETTINGS_MAX_CONCURRENT_STREAMS in SETTINGS frame from remote - * endpoint, SETTINGS_MAX_CONCURRENT_STREAMS is unlimited. This may - * cause problem if local endpoint submits lots of requests initially - * and sending them at once to the remote peer may lead to the - * rejection of some requests. Specifying this option to the sensible - * value, say 100, may avoid this kind of issue. This value will be - * overwritten if the local endpoint receives - * SETTINGS_MAX_CONCURRENT_STREAMS from the remote endpoint. - */ -NGHTTP2_EXTERN void -nghttp2_option_set_peer_max_concurrent_streams(nghttp2_option *option, - uint32_t val); - -/** - * @function - * - * By default, nghttp2 library, if configured as server, requires - * first 24 bytes of client magic byte string (MAGIC). In most cases, - * this will simplify the implementation of server. But sometimes - * server may want to detect the application protocol based on first - * few bytes on clear text communication. - * - * If this option is used with nonzero |val|, nghttp2 library does not - * handle MAGIC. It still checks following SETTINGS frame. This - * means that applications should deal with MAGIC by themselves. - * - * If this option is not used or used with zero value, if MAGIC does - * not match :macro:`NGHTTP2_CLIENT_MAGIC`, `nghttp2_session_recv()` - * and `nghttp2_session_mem_recv()` will return error - * :enum:`NGHTTP2_ERR_BAD_CLIENT_MAGIC`, which is fatal error. - */ -NGHTTP2_EXTERN void -nghttp2_option_set_no_recv_client_magic(nghttp2_option *option, int val); - -/** - * @function - * - * By default, nghttp2 library enforces subset of HTTP Messaging rules - * described in `HTTP/2 specification, section 8 - * `_. See - * :ref:`http-messaging` section for details. For those applications - * who use nghttp2 library as non-HTTP use, give nonzero to |val| to - * disable this enforcement. - */ -NGHTTP2_EXTERN void nghttp2_option_set_no_http_messaging(nghttp2_option *option, - int val); - -/** - * @function - * - * Initializes |*session_ptr| for client use. The all members of - * |callbacks| are copied to |*session_ptr|. Therefore |*session_ptr| - * does not store |callbacks|. The |user_data| is an arbitrary user - * supplied data, which will be passed to the callback functions. - * - * The :type:`nghttp2_send_callback` must be specified. If the - * application code uses `nghttp2_session_recv()`, the - * :type:`nghttp2_recv_callback` must be specified. The other members - * of |callbacks| can be ``NULL``. - * - * If this function fails, |*session_ptr| is left untouched. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * :enum:`NGHTTP2_ERR_NOMEM` - * Out of memory. - */ -NGHTTP2_EXTERN int -nghttp2_session_client_new(nghttp2_session **session_ptr, - const nghttp2_session_callbacks *callbacks, - void *user_data); - -/** - * @function - * - * Initializes |*session_ptr| for server use. The all members of - * |callbacks| are copied to |*session_ptr|. Therefore |*session_ptr| - * does not store |callbacks|. The |user_data| is an arbitrary user - * supplied data, which will be passed to the callback functions. - * - * The :type:`nghttp2_send_callback` must be specified. If the - * application code uses `nghttp2_session_recv()`, the - * :type:`nghttp2_recv_callback` must be specified. The other members - * of |callbacks| can be ``NULL``. - * - * If this function fails, |*session_ptr| is left untouched. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * :enum:`NGHTTP2_ERR_NOMEM` - * Out of memory. - */ -NGHTTP2_EXTERN int -nghttp2_session_server_new(nghttp2_session **session_ptr, - const nghttp2_session_callbacks *callbacks, - void *user_data); - -/** - * @function - * - * Like `nghttp2_session_client_new()`, but with additional options - * specified in the |option|. - * - * The |option| can be ``NULL`` and the call is equivalent to - * `nghttp2_session_client_new()`. - * - * This function does not take ownership |option|. The application is - * responsible for freeing |option| if it finishes using the object. - * - * The library code does not refer to |option| after this function - * returns. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * :enum:`NGHTTP2_ERR_NOMEM` - * Out of memory. - */ -NGHTTP2_EXTERN int -nghttp2_session_client_new2(nghttp2_session **session_ptr, - const nghttp2_session_callbacks *callbacks, - void *user_data, const nghttp2_option *option); - -/** - * @function - * - * Like `nghttp2_session_server_new()`, but with additional options - * specified in the |option|. - * - * The |option| can be ``NULL`` and the call is equivalent to - * `nghttp2_session_server_new()`. - * - * This function does not take ownership |option|. The application is - * responsible for freeing |option| if it finishes using the object. - * - * The library code does not refer to |option| after this function - * returns. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * :enum:`NGHTTP2_ERR_NOMEM` - * Out of memory. - */ -NGHTTP2_EXTERN int -nghttp2_session_server_new2(nghttp2_session **session_ptr, - const nghttp2_session_callbacks *callbacks, - void *user_data, const nghttp2_option *option); - -/** - * @function - * - * Like `nghttp2_session_client_new2()`, but with additional custom - * memory allocator specified in the |mem|. - * - * The |mem| can be ``NULL`` and the call is equivalent to - * `nghttp2_session_client_new2()`. - * - * This function does not take ownership |mem|. The application is - * responsible for freeing |mem|. - * - * The library code does not refer to |mem| pointer after this - * function returns, so the application can safely free it. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * :enum:`NGHTTP2_ERR_NOMEM` - * Out of memory. - */ -NGHTTP2_EXTERN int nghttp2_session_client_new3( - nghttp2_session **session_ptr, const nghttp2_session_callbacks *callbacks, - void *user_data, const nghttp2_option *option, nghttp2_mem *mem); - -/** - * @function - * - * Like `nghttp2_session_server_new2()`, but with additional custom - * memory allocator specified in the |mem|. - * - * The |mem| can be ``NULL`` and the call is equivalent to - * `nghttp2_session_server_new2()`. - * - * This function does not take ownership |mem|. The application is - * responsible for freeing |mem|. - * - * The library code does not refer to |mem| pointer after this - * function returns, so the application can safely free it. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * :enum:`NGHTTP2_ERR_NOMEM` - * Out of memory. - */ -NGHTTP2_EXTERN int nghttp2_session_server_new3( - nghttp2_session **session_ptr, const nghttp2_session_callbacks *callbacks, - void *user_data, const nghttp2_option *option, nghttp2_mem *mem); - -/** - * @function - * - * Frees any resources allocated for |session|. If |session| is - * ``NULL``, this function does nothing. - */ -NGHTTP2_EXTERN void nghttp2_session_del(nghttp2_session *session); - -/** - * @function - * - * Sends pending frames to the remote peer. - * - * This function retrieves the highest prioritized frame from the - * outbound queue and sends it to the remote peer. It does this as - * many as possible until the user callback - * :type:`nghttp2_send_callback` returns - * :enum:`NGHTTP2_ERR_WOULDBLOCK` or the outbound queue becomes empty. - * This function calls several callback functions which are passed - * when initializing the |session|. Here is the simple time chart - * which tells when each callback is invoked: - * - * 1. Get the next frame to send from outbound queue. - * - * 2. Prepare transmission of the frame. - * - * 3. If the control frame cannot be sent because some preconditions - * are not met (e.g., request HEADERS cannot be sent after GOAWAY), - * :type:`nghttp2_on_frame_not_send_callback` is invoked. Abort - * the following steps. - * - * 4. If the frame is HEADERS, PUSH_PROMISE or DATA, - * :type:`nghttp2_select_padding_callback` is invoked. - * - * 5. If the frame is request HEADERS, the stream is opened here. - * - * 6. :type:`nghttp2_before_frame_send_callback` is invoked. - * - * 7. :type:`nghttp2_send_callback` is invoked one or more times to - * send the frame. - * - * 8. :type:`nghttp2_on_frame_send_callback` is invoked. - * - * 9. If the transmission of the frame triggers closure of the stream, - * the stream is closed and - * :type:`nghttp2_on_stream_close_callback` is invoked. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * :enum:`NGHTTP2_ERR_NOMEM` - * Out of memory. - * :enum:`NGHTTP2_ERR_CALLBACK_FAILURE` - * The callback function failed. - */ -NGHTTP2_EXTERN int nghttp2_session_send(nghttp2_session *session); - -/** - * @function - * - * Returns the serialized data to send. - * - * This function behaves like `nghttp2_session_send()` except that it - * does not use :type:`nghttp2_send_callback` to transmit data. - * Instead, it assigns the pointer to the serialized data to the - * |*data_ptr| and returns its length. The other callbacks are called - * in the same way as they are in `nghttp2_session_send()`. - * - * If no data is available to send, this function returns 0. - * - * This function may not return all serialized data in one invocation. - * To get all data, call this function repeatedly until it returns 0 - * or one of negative error codes. - * - * The assigned |*data_ptr| is valid until the next call of - * `nghttp2_session_mem_send()` or `nghttp2_session_send()`. - * - * The caller must send all data before sending the next chunk of - * data. - * - * This function returns the length of the data pointed by the - * |*data_ptr| if it succeeds, or one of the following negative error - * codes: - * - * :enum:`NGHTTP2_ERR_NOMEM` - * Out of memory. - */ -NGHTTP2_EXTERN ssize_t nghttp2_session_mem_send(nghttp2_session *session, - const uint8_t **data_ptr); - -/** - * @function - * - * Receives frames from the remote peer. - * - * This function receives as many frames as possible until the user - * callback :type:`nghttp2_recv_callback` returns - * :enum:`NGHTTP2_ERR_WOULDBLOCK`. This function calls several - * callback functions which are passed when initializing the - * |session|. Here is the simple time chart which tells when each - * callback is invoked: - * - * 1. :type:`nghttp2_recv_callback` is invoked one or more times to - * receive frame header. - * - * 2. When frame header is received, - * :type:`nghttp2_on_begin_frame_callback` is invoked. - * - * 3. If the frame is DATA frame: - * - * 1. :type:`nghttp2_recv_callback` is invoked to receive DATA - * payload. For each chunk of data, - * :type:`nghttp2_on_data_chunk_recv_callback` is invoked. - * - * 2. If one DATA frame is completely received, - * :type:`nghttp2_on_frame_recv_callback` is invoked. If the - * reception of the frame triggers the closure of the stream, - * :type:`nghttp2_on_stream_close_callback` is invoked. - * - * 4. If the frame is the control frame: - * - * 1. :type:`nghttp2_recv_callback` is invoked one or more times to - * receive whole frame. - * - * 2. If the received frame is valid, then following actions are - * taken. If the frame is either HEADERS or PUSH_PROMISE, - * :type:`nghttp2_on_begin_headers_callback` is invoked. Then - * :type:`nghttp2_on_header_callback` is invoked for each header - * name/value pair. After all name/value pairs are emitted - * successfully, :type:`nghttp2_on_frame_recv_callback` is - * invoked. For other frames, - * :type:`nghttp2_on_frame_recv_callback` is invoked. If the - * reception of the frame triggers the closure of the stream, - * :type:`nghttp2_on_stream_close_callback` is invoked. - * - * 3. If the received frame is unpacked but is interpreted as - * invalid, :type:`nghttp2_on_invalid_frame_recv_callback` is - * invoked. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * :enum:`NGHTTP2_ERR_EOF` - * The remote peer did shutdown on the connection. - * :enum:`NGHTTP2_ERR_NOMEM` - * Out of memory. - * :enum:`NGHTTP2_ERR_CALLBACK_FAILURE` - * The callback function failed. - * :enum:`NGHTTP2_ERR_BAD_CLIENT_MAGIC` - * Invalid client magic was detected. This error only returns - * when |session| was configured as server and - * `nghttp2_option_set_no_recv_client_magic()` is not used with - * nonzero value. - */ -NGHTTP2_EXTERN int nghttp2_session_recv(nghttp2_session *session); - -/** - * @function - * - * Processes data |in| as an input from the remote endpoint. The - * |inlen| indicates the number of bytes in the |in|. - * - * This function behaves like `nghttp2_session_recv()` except that it - * does not use :type:`nghttp2_recv_callback` to receive data; the - * |in| is the only data for the invocation of this function. If all - * bytes are processed, this function returns. The other callbacks - * are called in the same way as they are in `nghttp2_session_recv()`. - * - * In the current implementation, this function always tries to - * processes all input data unless either an error occurs or - * :enum:`NGHTTP2_ERR_PAUSE` is returned from - * :type:`nghttp2_on_header_callback` or - * :type:`nghttp2_on_data_chunk_recv_callback`. If - * :enum:`NGHTTP2_ERR_PAUSE` is used, the return value includes the - * number of bytes which was used to produce the data or frame for the - * callback. - * - * This function returns the number of processed bytes, or one of the - * following negative error codes: - * - * :enum:`NGHTTP2_ERR_NOMEM` - * Out of memory. - * :enum:`NGHTTP2_ERR_CALLBACK_FAILURE` - * The callback function failed. - * :enum:`NGHTTP2_ERR_BAD_CLIENT_MAGIC` - * Invalid client magic was detected. This error only returns - * when |session| was configured as server and - * `nghttp2_option_set_no_recv_client_magic()` is not used with - * nonzero value. - */ -NGHTTP2_EXTERN ssize_t nghttp2_session_mem_recv(nghttp2_session *session, - const uint8_t *in, - size_t inlen); - -/** - * @function - * - * Puts back previously deferred DATA frame in the stream |stream_id| - * to the outbound queue. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * :enum:`NGHTTP2_ERR_INVALID_ARGUMENT` - * The stream does not exist; or no deferred data exist. - * :enum:`NGHTTP2_ERR_NOMEM` - * Out of memory. - */ -NGHTTP2_EXTERN int nghttp2_session_resume_data(nghttp2_session *session, - int32_t stream_id); - -/** - * @function - * - * Returns nonzero value if |session| wants to receive data from the - * remote peer. - * - * If both `nghttp2_session_want_read()` and - * `nghttp2_session_want_write()` return 0, the application should - * drop the connection. - */ -NGHTTP2_EXTERN int nghttp2_session_want_read(nghttp2_session *session); - -/** - * @function - * - * Returns nonzero value if |session| wants to send data to the remote - * peer. - * - * If both `nghttp2_session_want_read()` and - * `nghttp2_session_want_write()` return 0, the application should - * drop the connection. - */ -NGHTTP2_EXTERN int nghttp2_session_want_write(nghttp2_session *session); - -/** - * @function - * - * Returns stream_user_data for the stream |stream_id|. The - * stream_user_data is provided by `nghttp2_submit_request()`, - * `nghttp2_submit_headers()` or - * `nghttp2_session_set_stream_user_data()`. Unless it is set using - * `nghttp2_session_set_stream_user_data()`, if the stream is - * initiated by the remote endpoint, stream_user_data is always - * ``NULL``. If the stream does not exist, this function returns - * ``NULL``. - */ -NGHTTP2_EXTERN void * -nghttp2_session_get_stream_user_data(nghttp2_session *session, - int32_t stream_id); - -/** - * @function - * - * Sets the |stream_user_data| to the stream denoted by the - * |stream_id|. If a stream user data is already set to the stream, - * it is replaced with the |stream_user_data|. It is valid to specify - * ``NULL`` in the |stream_user_data|, which nullifies the associated - * data pointer. - * - * It is valid to set the |stream_user_data| to the stream reserved by - * PUSH_PROMISE frame. - * - * This function returns 0 if it succeeds, or one of following - * negative error codes: - * - * :enum:`NGHTTP2_ERR_INVALID_ARGUMENT` - * The stream does not exist - */ -NGHTTP2_EXTERN int -nghttp2_session_set_stream_user_data(nghttp2_session *session, - int32_t stream_id, void *stream_user_data); - -/** - * @function - * - * Returns the number of frames in the outbound queue. This does not - * include the deferred DATA frames. - */ -NGHTTP2_EXTERN size_t - nghttp2_session_get_outbound_queue_size(nghttp2_session *session); - -/** - * @function - * - * Returns the number of DATA payload in bytes received without - * WINDOW_UPDATE transmission for the stream |stream_id|. The local - * (receive) window size can be adjusted by - * `nghttp2_submit_window_update()`. This function takes into account - * that and returns effective data length. In particular, if the - * local window size is reduced by submitting negative - * window_size_increment with `nghttp2_submit_window_update()`, this - * function returns the number of bytes less than actually received. - * - * This function returns -1 if it fails. - */ -NGHTTP2_EXTERN int32_t nghttp2_session_get_stream_effective_recv_data_length( - nghttp2_session *session, int32_t stream_id); - -/** - * @function - * - * Returns the local (receive) window size for the stream |stream_id|. - * The local window size can be adjusted by - * `nghttp2_submit_window_update()`. This function takes into account - * that and returns effective window size. - * - * This function returns -1 if it fails. - */ -NGHTTP2_EXTERN int32_t nghttp2_session_get_stream_effective_local_window_size( - nghttp2_session *session, int32_t stream_id); - -/** - * @function - * - * Returns the number of DATA payload in bytes received without - * WINDOW_UPDATE transmission for a connection. The local (receive) - * window size can be adjusted by `nghttp2_submit_window_update()`. - * This function takes into account that and returns effective data - * length. In particular, if the local window size is reduced by - * submitting negative window_size_increment with - * `nghttp2_submit_window_update()`, this function returns the number - * of bytes less than actually received. - * - * This function returns -1 if it fails. - */ -NGHTTP2_EXTERN int32_t - nghttp2_session_get_effective_recv_data_length(nghttp2_session *session); - -/** - * @function - * - * Returns the local (receive) window size for a connection. The - * local window size can be adjusted by - * `nghttp2_submit_window_update()`. This function takes into account - * that and returns effective window size. - * - * This function returns -1 if it fails. - */ -NGHTTP2_EXTERN int32_t - nghttp2_session_get_effective_local_window_size(nghttp2_session *session); - -/** - * @function - * - * Returns the remote window size for a given stream |stream_id|. - * - * This is the amount of flow-controlled payload (e.g., DATA) that the - * local endpoint can send without stream level WINDOW_UPDATE. There - * is also connection level flow control, so the effective size of - * payload that the local endpoint can actually send is - * min(`nghttp2_session_get_stream_remote_window_size()`, - * `nghttp2_session_get_remote_window_size()`). - * - * This function returns -1 if it fails. - */ -NGHTTP2_EXTERN int32_t - nghttp2_session_get_stream_remote_window_size(nghttp2_session *session, - int32_t stream_id); - -/** - * @function - * - * Returns the remote window size for a connection. - * - * This function always succeeds. - */ -NGHTTP2_EXTERN int32_t - nghttp2_session_get_remote_window_size(nghttp2_session *session); - -/** - * @function - * - * Returns 1 if local peer half closed the given stream |stream_id|. - * Returns 0 if it did not. Returns -1 if no such stream exists. - */ -NGHTTP2_EXTERN int -nghttp2_session_get_stream_local_close(nghttp2_session *session, - int32_t stream_id); - -/** - * @function - * - * Returns 1 if remote peer half closed the given stream |stream_id|. - * Returns 0 if it did not. Returns -1 if no such stream exists. - */ -NGHTTP2_EXTERN int -nghttp2_session_get_stream_remote_close(nghttp2_session *session, - int32_t stream_id); - -/** - * @function - * - * Signals the session so that the connection should be terminated. - * - * The last stream ID is the minimum value between the stream ID of a - * stream for which :type:`nghttp2_on_frame_recv_callback` was called - * most recently and the last stream ID we have sent to the peer - * previously. - * - * The |error_code| is the error code of this GOAWAY frame. The - * pre-defined error code is one of :enum:`nghttp2_error_code`. - * - * After the transmission, both `nghttp2_session_want_read()` and - * `nghttp2_session_want_write()` return 0. - * - * This function should be called when the connection should be - * terminated after sending GOAWAY. If the remaining streams should - * be processed after GOAWAY, use `nghttp2_submit_goaway()` instead. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * :enum:`NGHTTP2_ERR_NOMEM` - * Out of memory. - */ -NGHTTP2_EXTERN int nghttp2_session_terminate_session(nghttp2_session *session, - uint32_t error_code); - -/** - * @function - * - * Signals the session so that the connection should be terminated. - * - * This function behaves like `nghttp2_session_terminate_session()`, - * but the last stream ID can be specified by the application for fine - * grained control of stream. The HTTP/2 specification does not allow - * last_stream_id to be increased. So the actual value sent as - * last_stream_id is the minimum value between the given - * |last_stream_id| and the last_stream_id we have previously sent to - * the peer. - * - * The |last_stream_id| is peer's stream ID or 0. So if |session| is - * initialized as client, |last_stream_id| must be even or 0. If - * |session| is initialized as server, |last_stream_id| must be odd or - * 0. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * :enum:`NGHTTP2_ERR_NOMEM` - * Out of memory. - * :enum:`NGHTTP2_ERR_INVALID_ARGUMENT` - * The |last_stream_id| is invalid. - */ -NGHTTP2_EXTERN int nghttp2_session_terminate_session2(nghttp2_session *session, - int32_t last_stream_id, - uint32_t error_code); - -/** - * @function - * - * Signals to the client that the server started graceful shutdown - * procedure. - * - * This function is only usable for server. If this function is - * called with client side session, this function returns - * :enum:`NGHTTP2_ERR_INVALID_STATE`. - * - * To gracefully shutdown HTTP/2 session, server should call this - * function to send GOAWAY with last_stream_id (1u << 31) - 1. And - * after some delay (e.g., 1 RTT), send another GOAWAY with the stream - * ID that the server has some processing using - * `nghttp2_submit_goaway()`. See also - * `nghttp2_session_get_last_proc_stream_id()`. - * - * Unlike `nghttp2_submit_goaway()`, this function just sends GOAWAY - * and does nothing more. This is a mere indication to the client - * that session shutdown is imminent. The application should call - * `nghttp2_submit_goaway()` with appropriate last_stream_id after - * this call. - * - * If one or more GOAWAY frame have been already sent by either - * `nghttp2_submit_goaway()` or `nghttp2_session_terminate_session()`, - * this function has no effect. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * :enum:`NGHTTP2_ERR_NOMEM` - * Out of memory. - * :enum:`NGHTTP2_ERR_INVALID_STATE` - * The |session| is initialized as client. - */ -NGHTTP2_EXTERN int nghttp2_submit_shutdown_notice(nghttp2_session *session); - -/** - * @function - * - * Returns the value of SETTINGS |id| notified by a remote endpoint. - * The |id| must be one of values defined in - * :enum:`nghttp2_settings_id`. - */ -NGHTTP2_EXTERN uint32_t - nghttp2_session_get_remote_settings(nghttp2_session *session, - nghttp2_settings_id id); - -/** - * @function - * - * Tells the |session| that next stream ID is |next_stream_id|. The - * |next_stream_id| must be equal or greater than the value returned - * by `nghttp2_session_get_next_stream_id()`. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * :enum:`NGHTTP2_ERR_INVALID_ARGUMENT` - * The |next_stream_id| is strictly less than the value - * `nghttp2_session_get_next_stream_id()` returns; or - * |next_stream_id| is invalid (e.g., even integer for client, or - * odd integer for server). - */ -NGHTTP2_EXTERN int nghttp2_session_set_next_stream_id(nghttp2_session *session, - int32_t next_stream_id); - -/** - * @function - * - * Returns the next outgoing stream ID. Notice that return type is - * uint32_t. If we run out of stream ID for this session, this - * function returns 1 << 31. - */ -NGHTTP2_EXTERN uint32_t - nghttp2_session_get_next_stream_id(nghttp2_session *session); - -/** - * @function - * - * Tells the |session| that |size| bytes for a stream denoted by - * |stream_id| were consumed by application and are ready to - * WINDOW_UPDATE. The consumed bytes are counted towards both - * connection and stream level WINDOW_UPDATE (see - * `nghttp2_session_consume_connection()` and - * `nghttp2_session_consume_stream()` to update consumption - * independently). This function is intended to be used without - * automatic window update (see - * `nghttp2_option_set_no_auto_window_update()`). - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * :enum:`NGHTTP2_ERR_NOMEM` - * Out of memory. - * :enum:`NGHTTP2_ERR_INVALID_ARGUMENT` - * The |stream_id| is 0. - * :enum:`NGHTTP2_ERR_INVALID_STATE` - * Automatic WINDOW_UPDATE is not disabled. - */ -NGHTTP2_EXTERN int nghttp2_session_consume(nghttp2_session *session, - int32_t stream_id, size_t size); - -/** - * @function - * - * Like `nghttp2_session_consume()`, but this only tells library that - * |size| bytes were consumed only for connection level. Note that - * HTTP/2 maintains connection and stream level flow control windows - * independently. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * :enum:`NGHTTP2_ERR_NOMEM` - * Out of memory. - * :enum:`NGHTTP2_ERR_INVALID_STATE` - * Automatic WINDOW_UPDATE is not disabled. - */ -NGHTTP2_EXTERN int nghttp2_session_consume_connection(nghttp2_session *session, - size_t size); - -/** - * @function - * - * Like `nghttp2_session_consume()`, but this only tells library that - * |size| bytes were consumed only for stream denoted by |stream_id|. - * Note that HTTP/2 maintains connection and stream level flow control - * windows independently. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * :enum:`NGHTTP2_ERR_NOMEM` - * Out of memory. - * :enum:`NGHTTP2_ERR_INVALID_ARGUMENT` - * The |stream_id| is 0. - * :enum:`NGHTTP2_ERR_INVALID_STATE` - * Automatic WINDOW_UPDATE is not disabled. - */ -NGHTTP2_EXTERN int nghttp2_session_consume_stream(nghttp2_session *session, - int32_t stream_id, - size_t size); - -/** - * @function - * - * Performs post-process of HTTP Upgrade request. This function can - * be called from both client and server, but the behavior is very - * different in each other. - * - * If called from client side, the |settings_payload| must be the - * value sent in ``HTTP2-Settings`` header field and must be decoded - * by base64url decoder. The |settings_payloadlen| is the length of - * |settings_payload|. The |settings_payload| is unpacked and its - * setting values will be submitted using `nghttp2_submit_settings()`. - * This means that the client application code does not need to submit - * SETTINGS by itself. The stream with stream ID=1 is opened and the - * |stream_user_data| is used for its stream_user_data. The opened - * stream becomes half-closed (local) state. - * - * If called from server side, the |settings_payload| must be the - * value received in ``HTTP2-Settings`` header field and must be - * decoded by base64url decoder. The |settings_payloadlen| is the - * length of |settings_payload|. It is treated as if the SETTINGS - * frame with that payload is received. Thus, callback functions for - * the reception of SETTINGS frame will be invoked. The stream with - * stream ID=1 is opened. The |stream_user_data| is ignored. The - * opened stream becomes half-closed (remote). - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * :enum:`NGHTTP2_ERR_NOMEM` - * Out of memory. - * :enum:`NGHTTP2_ERR_INVALID_ARGUMENT` - * The |settings_payload| is badly formed. - * :enum:`NGHTTP2_ERR_PROTO` - * The stream ID 1 is already used or closed; or is not available. - */ -NGHTTP2_EXTERN int nghttp2_session_upgrade(nghttp2_session *session, - const uint8_t *settings_payload, - size_t settings_payloadlen, - void *stream_user_data); - -/** - * @function - * - * Serializes the SETTINGS values |iv| in the |buf|. The size of the - * |buf| is specified by |buflen|. The number of entries in the |iv| - * array is given by |niv|. The required space in |buf| for the |niv| - * entries is ``8*niv`` bytes and if the given buffer is too small, an - * error is returned. This function is used mainly for creating a - * SETTINGS payload to be sent with the ``HTTP2-Settings`` header - * field in an HTTP Upgrade request. The data written in |buf| is NOT - * base64url encoded and the application is responsible for encoding. - * - * This function returns the number of bytes written in |buf|, or one - * of the following negative error codes: - * - * :enum:`NGHTTP2_ERR_INVALID_ARGUMENT` - * The |iv| contains duplicate settings ID or invalid value. - * - * :enum:`NGHTTP2_ERR_INSUFF_BUFSIZE` - * The provided |buflen| size is too small to hold the output. - */ -NGHTTP2_EXTERN ssize_t - nghttp2_pack_settings_payload(uint8_t *buf, size_t buflen, - const nghttp2_settings_entry *iv, size_t niv); - -/** - * @function - * - * Returns string describing the |lib_error_code|. The - * |lib_error_code| must be one of the :enum:`nghttp2_error`. - */ -NGHTTP2_EXTERN const char *nghttp2_strerror(int lib_error_code); - -/** - * @function - * - * Initializes |pri_spec| with the |stream_id| of the stream to depend - * on with |weight| and its exclusive flag. If |exclusive| is - * nonzero, exclusive flag is set. - * - * The |weight| must be in [:enum:`NGHTTP2_MIN_WEIGHT`, - * :enum:`NGHTTP2_MAX_WEIGHT`], inclusive. - */ -NGHTTP2_EXTERN void nghttp2_priority_spec_init(nghttp2_priority_spec *pri_spec, - int32_t stream_id, - int32_t weight, int exclusive); - -/** - * @function - * - * Initializes |pri_spec| with the default values. The default values - * are: stream_id = 0, weight = :macro:`NGHTTP2_DEFAULT_WEIGHT` and - * exclusive = 0. - */ -NGHTTP2_EXTERN void -nghttp2_priority_spec_default_init(nghttp2_priority_spec *pri_spec); - -/** - * @function - * - * Returns nonzero if the |pri_spec| is filled with default values. - */ -NGHTTP2_EXTERN int -nghttp2_priority_spec_check_default(const nghttp2_priority_spec *pri_spec); - -/** - * @function - * - * Submits HEADERS frame and optionally one or more DATA frames. - * - * The |pri_spec| is priority specification of this request. ``NULL`` - * means the default priority (see - * `nghttp2_priority_spec_default_init()`). To specify the priority, - * use `nghttp2_priority_spec_init()`. If |pri_spec| is not ``NULL``, - * this function will copy its data members. - * - * The ``pri_spec->weight`` must be in [:enum:`NGHTTP2_MIN_WEIGHT`, - * :enum:`NGHTTP2_MAX_WEIGHT`], inclusive. If ``pri_spec->weight`` is - * strictly less than :enum:`NGHTTP2_MIN_WEIGHT`, it becomes - * :enum:`NGHTTP2_MIN_WEIGHT`. If it is strictly greater than - * :enum:`NGHTTP2_MAX_WEIGHT`, it becomes :enum:`NGHTTP2_MAX_WEIGHT`. - * - * The |nva| is an array of name/value pair :type:`nghttp2_nv` with - * |nvlen| elements. The application is responsible to include - * required pseudo-header fields (header field whose name starts with - * ":") in |nva| and must place pseudo-headers before regular header - * fields. - * - * This function creates copies of all name/value pairs in |nva|. It - * also lower-cases all names in |nva|. The order of elements in - * |nva| is preserved. - * - * HTTP/2 specification has requirement about header fields in the - * request HEADERS. See the specification for more details. - * - * If |data_prd| is not ``NULL``, it provides data which will be sent - * in subsequent DATA frames. In this case, a method that allows - * request message bodies - * (https://tools.ietf.org/html/rfc7231#section-4) must be specified - * with ``:method`` key in |nva| (e.g. ``POST``). This function does - * not take ownership of the |data_prd|. The function copies the - * members of the |data_prd|. If |data_prd| is ``NULL``, HEADERS have - * END_STREAM set. The |stream_user_data| is data associated to the - * stream opened by this request and can be an arbitrary pointer, - * which can be retrieved later by - * `nghttp2_session_get_stream_user_data()`. - * - * This function returns assigned stream ID if it succeeds, or one of - * the following negative error codes: - * - * :enum:`NGHTTP2_ERR_NOMEM` - * Out of memory. - * :enum:`NGHTTP2_ERR_STREAM_ID_NOT_AVAILABLE` - * No stream ID is available because maximum stream ID was - * reached. - * - * .. warning:: - * - * This function returns assigned stream ID if it succeeds. But - * that stream is not opened yet. The application must not submit - * frame to that stream ID before - * :type:`nghttp2_before_frame_send_callback` is called for this - * frame. - * - */ -NGHTTP2_EXTERN int32_t - nghttp2_submit_request(nghttp2_session *session, - const nghttp2_priority_spec *pri_spec, - const nghttp2_nv *nva, size_t nvlen, - const nghttp2_data_provider *data_prd, - void *stream_user_data); - -/** - * @function - * - * Submits response HEADERS frame and optionally one or more DATA - * frames against the stream |stream_id|. - * - * The |nva| is an array of name/value pair :type:`nghttp2_nv` with - * |nvlen| elements. The application is responsible to include - * required pseudo-header fields (header field whose name starts with - * ":") in |nva| and must place pseudo-headers before regular header - * fields. - * - * This function creates copies of all name/value pairs in |nva|. It - * also lower-cases all names in |nva|. The order of elements in - * |nva| is preserved. - * - * HTTP/2 specification has requirement about header fields in the - * response HEADERS. See the specification for more details. - * - * If |data_prd| is not ``NULL``, it provides data which will be sent - * in subsequent DATA frames. This function does not take ownership - * of the |data_prd|. The function copies the members of the - * |data_prd|. If |data_prd| is ``NULL``, HEADERS will have - * END_STREAM flag set. - * - * This method can be used as normal HTTP response and push response. - * When pushing a resource using this function, the |session| must be - * configured using `nghttp2_session_server_new()` or its variants and - * the target stream denoted by the |stream_id| must be reserved using - * `nghttp2_submit_push_promise()`. - * - * To send non-final response headers (e.g., HTTP status 101), don't - * use this function because this function half-closes the outbound - * stream. Instead, use `nghttp2_submit_headers()` for this purpose. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * :enum:`NGHTTP2_ERR_NOMEM` - * Out of memory. - * :enum:`NGHTTP2_ERR_INVALID_ARGUMENT` - * The |stream_id| is 0. - * - * .. warning:: - * - * Calling this function twice for the same stream ID may lead to - * program crash. It is generally considered to a programming error - * to commit response twice. - */ -NGHTTP2_EXTERN int -nghttp2_submit_response(nghttp2_session *session, int32_t stream_id, - const nghttp2_nv *nva, size_t nvlen, - const nghttp2_data_provider *data_prd); - -/** - * @function - * - * Submits trailer HEADERS against the stream |stream_id|. - * - * The |nva| is an array of name/value pair :type:`nghttp2_nv` with - * |nvlen| elements. The application is responsible not to include - * required pseudo-header fields (header field whose name starts with - * ":") in |nva|. - * - * This function creates copies of all name/value pairs in |nva|. It - * also lower-cases all names in |nva|. The order of elements in - * |nva| is preserved. - * - * For server, trailer must be followed by response HEADERS or - * response DATA. The library does not check that response HEADERS - * has already sent and if `nghttp2_submit_trailer()` is called before - * any response HEADERS submission (usually by - * `nghttp2_submit_response()`), the content of |nva| will be sent as - * reponse headers, which will result in error. - * - * This function has the same effect with `nghttp2_submit_headers()`, - * with flags = :enum:`NGHTTP2_FLAG_END_HEADERS` and both pri_spec and - * stream_user_data to NULL. - * - * To submit trailer after `nghttp2_submit_response()` is called, the - * application has to specify :type:`nghttp2_data_provider` to - * `nghttp2_submit_response()`. In side - * :type:`nghttp2_data_source_read_callback`, when setting - * :enum:`NGHTTP2_DATA_FLAG_EOF`, also set - * :enum:`NGHTTP2_DATA_FLAG_NO_END_STREAM`. After that, the - * application can send trailer using `nghttp2_submit_trailer()`. - * `nghttp2_submit_trailer()` can be used inside - * :type:`nghttp2_data_source_read_callback`. - * - * This function returns 0 if it succeeds and |stream_id| is -1. - * Otherwise, this function returns 0 if it succeeds, or one of the - * following negative error codes: - * - * :enum:`NGHTTP2_ERR_NOMEM` - * Out of memory. - * :enum:`NGHTTP2_ERR_INVALID_ARGUMENT` - * The |stream_id| is 0. - */ -NGHTTP2_EXTERN int nghttp2_submit_trailer(nghttp2_session *session, - int32_t stream_id, - const nghttp2_nv *nva, size_t nvlen); - -/** - * @function - * - * Submits HEADERS frame. The |flags| is bitwise OR of the - * following values: - * - * * :enum:`NGHTTP2_FLAG_END_STREAM` - * - * If |flags| includes :enum:`NGHTTP2_FLAG_END_STREAM`, this frame has - * END_STREAM flag set. - * - * The library handles the CONTINUATION frame internally and it - * correctly sets END_HEADERS to the last sequence of the PUSH_PROMISE - * or CONTINUATION frame. - * - * If the |stream_id| is -1, this frame is assumed as request (i.e., - * request HEADERS frame which opens new stream). In this case, the - * assigned stream ID will be returned. Otherwise, specify stream ID - * in |stream_id|. - * - * The |pri_spec| is priority specification of this request. ``NULL`` - * means the default priority (see - * `nghttp2_priority_spec_default_init()`). To specify the priority, - * use `nghttp2_priority_spec_init()`. If |pri_spec| is not ``NULL``, - * this function will copy its data members. - * - * The ``pri_spec->weight`` must be in [:enum:`NGHTTP2_MIN_WEIGHT`, - * :enum:`NGHTTP2_MAX_WEIGHT`], inclusive. If ``pri_spec->weight`` is - * strictly less than :enum:`NGHTTP2_MIN_WEIGHT`, it becomes - * :enum:`NGHTTP2_MIN_WEIGHT`. If it is strictly greater than - * :enum:`NGHTTP2_MAX_WEIGHT`, it becomes :enum:`NGHTTP2_MAX_WEIGHT`. - * - * The |nva| is an array of name/value pair :type:`nghttp2_nv` with - * |nvlen| elements. The application is responsible to include - * required pseudo-header fields (header field whose name starts with - * ":") in |nva| and must place pseudo-headers before regular header - * fields. - * - * This function creates copies of all name/value pairs in |nva|. It - * also lower-cases all names in |nva|. The order of elements in - * |nva| is preserved. - * - * The |stream_user_data| is a pointer to an arbitrary data which is - * associated to the stream this frame will open. Therefore it is - * only used if this frame opens streams, in other words, it changes - * stream state from idle or reserved to open. - * - * This function is low-level in a sense that the application code can - * specify flags directly. For usual HTTP request, - * `nghttp2_submit_request()` is useful. - * - * This function returns newly assigned stream ID if it succeeds and - * |stream_id| is -1. Otherwise, this function returns 0 if it - * succeeds, or one of the following negative error codes: - * - * :enum:`NGHTTP2_ERR_NOMEM` - * Out of memory. - * :enum:`NGHTTP2_ERR_STREAM_ID_NOT_AVAILABLE` - * No stream ID is available because maximum stream ID was - * reached. - * :enum:`NGHTTP2_ERR_INVALID_ARGUMENT` - * The |stream_id| is 0. - * - * .. warning:: - * - * This function returns assigned stream ID if it succeeds and - * |stream_id| is -1. But that stream is not opened yet. The - * application must not submit frame to that stream ID before - * :type:`nghttp2_before_frame_send_callback` is called for this - * frame. - * - */ -NGHTTP2_EXTERN int32_t - nghttp2_submit_headers(nghttp2_session *session, uint8_t flags, - int32_t stream_id, - const nghttp2_priority_spec *pri_spec, - const nghttp2_nv *nva, size_t nvlen, - void *stream_user_data); - -/** - * @function - * - * Submits one or more DATA frames to the stream |stream_id|. The - * data to be sent are provided by |data_prd|. If |flags| contains - * :enum:`NGHTTP2_FLAG_END_STREAM`, the last DATA frame has END_STREAM - * flag set. - * - * This function does not take ownership of the |data_prd|. The - * function copies the members of the |data_prd|. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * :enum:`NGHTTP2_ERR_NOMEM` - * Out of memory. - * :enum:`NGHTTP2_ERR_DATA_EXIST` - * DATA has been already submitted and not fully processed yet. - * :enum:`NGHTTP2_ERR_INVALID_ARGUMENT` - * The |stream_id| is 0. - * :enum:`NGHTTP2_ERR_STREAM_CLOSED` - * The stream was alreay closed; or the |stream_id| is invalid. - * - * .. note:: - * - * Currently, only one data is allowed for a stream at a time. - * Submitting data more than once before first data is finished - * results in :enum:`NGHTTP2_ERR_DATA_EXIST` error code. The - * earliest callback which tells that previous data is done is - * :type:`nghttp2_on_frame_send_callback`. In side that callback, - * new data can be submitted using `nghttp2_submit_data()`. Of - * course, all data except for last one must not have - * :enum:`NGHTTP2_FLAG_END_STREAM` flag set in |flags|. - */ -NGHTTP2_EXTERN int nghttp2_submit_data(nghttp2_session *session, uint8_t flags, - int32_t stream_id, - const nghttp2_data_provider *data_prd); - -/** - * @function - * - * Submits PRIORITY frame to change the priority of stream |stream_id| - * to the priority specification |pri_spec|. - * - * The |flags| is currently ignored and should be - * :enum:`NGHTTP2_FLAG_NONE`. - * - * The |pri_spec| is priority specification of this request. ``NULL`` - * is not allowed for this function. To specify the priority, use - * `nghttp2_priority_spec_init()`. This function will copy its data - * members. - * - * The ``pri_spec->weight`` must be in [:enum:`NGHTTP2_MIN_WEIGHT`, - * :enum:`NGHTTP2_MAX_WEIGHT`], inclusive. If ``pri_spec->weight`` is - * strictly less than :enum:`NGHTTP2_MIN_WEIGHT`, it becomes - * :enum:`NGHTTP2_MIN_WEIGHT`. If it is strictly greater than - * :enum:`NGHTTP2_MAX_WEIGHT`, it becomes :enum:`NGHTTP2_MAX_WEIGHT`. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * :enum:`NGHTTP2_ERR_NOMEM` - * Out of memory. - * :enum:`NGHTTP2_ERR_INVALID_ARGUMENT` - * The |stream_id| is 0; or the |pri_spec| is NULL; or trying to - * depend on itself. - */ -NGHTTP2_EXTERN int -nghttp2_submit_priority(nghttp2_session *session, uint8_t flags, - int32_t stream_id, - const nghttp2_priority_spec *pri_spec); - -/** - * @function - * - * Submits RST_STREAM frame to cancel/reject the stream |stream_id| - * with the error code |error_code|. - * - * The pre-defined error code is one of :enum:`nghttp2_error_code`. - * - * The |flags| is currently ignored and should be - * :enum:`NGHTTP2_FLAG_NONE`. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * :enum:`NGHTTP2_ERR_NOMEM` - * Out of memory. - * :enum:`NGHTTP2_ERR_INVALID_ARGUMENT` - * The |stream_id| is 0. - */ -NGHTTP2_EXTERN int nghttp2_submit_rst_stream(nghttp2_session *session, - uint8_t flags, int32_t stream_id, - uint32_t error_code); - -/** - * @function - * - * Stores local settings and submits SETTINGS frame. The |iv| is the - * pointer to the array of :type:`nghttp2_settings_entry`. The |niv| - * indicates the number of :type:`nghttp2_settings_entry`. - * - * The |flags| is currently ignored and should be - * :enum:`NGHTTP2_FLAG_NONE`. - * - * This function does not take ownership of the |iv|. This function - * copies all the elements in the |iv|. - * - * While updating individual stream's local window size, if the window - * size becomes strictly larger than NGHTTP2_MAX_WINDOW_SIZE, - * RST_STREAM is issued against such a stream. - * - * SETTINGS with :enum:`NGHTTP2_FLAG_ACK` is automatically submitted - * by the library and application could not send it at its will. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * :enum:`NGHTTP2_ERR_INVALID_ARGUMENT` - * The |iv| contains invalid value (e.g., initial window size - * strictly greater than (1 << 31) - 1. - * :enum:`NGHTTP2_ERR_TOO_MANY_INFLIGHT_SETTINGS` - * There is already another in-flight SETTINGS. Note that the - * current implementation only allows 1 in-flight SETTINGS frame - * without ACK flag set. - * :enum:`NGHTTP2_ERR_NOMEM` - * Out of memory. - */ -NGHTTP2_EXTERN int nghttp2_submit_settings(nghttp2_session *session, - uint8_t flags, - const nghttp2_settings_entry *iv, - size_t niv); - -/** - * @function - * - * Submits PUSH_PROMISE frame. - * - * The |flags| is currently ignored. The library handles the - * CONTINUATION frame internally and it correctly sets END_HEADERS to - * the last sequence of the PUSH_PROMISE or CONTINUATION frame. - * - * The |stream_id| must be client initiated stream ID. - * - * The |nva| is an array of name/value pair :type:`nghttp2_nv` with - * |nvlen| elements. The application is responsible to include - * required pseudo-header fields (header field whose name starts with - * ":") in |nva| and must place pseudo-headers before regular header - * fields. - * - * This function creates copies of all name/value pairs in |nva|. It - * also lower-cases all names in |nva|. The order of elements in - * |nva| is preserved. - * - * The |promised_stream_user_data| is a pointer to an arbitrary data - * which is associated to the promised stream this frame will open and - * make it in reserved state. It is available using - * `nghttp2_session_get_stream_user_data()`. The application can - * access it in :type:`nghttp2_before_frame_send_callback` and - * :type:`nghttp2_on_frame_send_callback` of this frame. - * - * The client side is not allowed to use this function. - * - * To submit response headers and data, use - * `nghttp2_submit_response()`. - * - * This function returns assigned promised stream ID if it succeeds, - * or one of the following negative error codes: - * - * :enum:`NGHTTP2_ERR_NOMEM` - * Out of memory. - * :enum:`NGHTTP2_ERR_PROTO` - * This function was invoked when |session| is initialized as - * client. - * :enum:`NGHTTP2_ERR_STREAM_ID_NOT_AVAILABLE` - * No stream ID is available because maximum stream ID was - * reached. - * :enum:`NGHTTP2_ERR_INVALID_ARGUMENT` - * The |stream_id| is 0; The |stream_id| does not designate stream - * that peer initiated. - * - * .. warning:: - * - * This function returns assigned promised stream ID if it succeeds. - * But that stream is not opened yet. The application must not - * submit frame to that stream ID before - * :type:`nghttp2_before_frame_send_callback` is called for this - * frame. - * - */ -NGHTTP2_EXTERN int32_t - nghttp2_submit_push_promise(nghttp2_session *session, uint8_t flags, - int32_t stream_id, const nghttp2_nv *nva, - size_t nvlen, void *promised_stream_user_data); - -/** - * @function - * - * Submits PING frame. You don't have to send PING back when you - * received PING frame. The library automatically submits PING frame - * in this case. - * - * The |flags| is currently ignored and should be - * :enum:`NGHTTP2_FLAG_NONE`. - * - * If the |opaque_data| is non ``NULL``, then it should point to the 8 - * bytes array of memory to specify opaque data to send with PING - * frame. If the |opaque_data| is ``NULL``, zero-cleared 8 bytes will - * be sent as opaque data. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * :enum:`NGHTTP2_ERR_NOMEM` - * Out of memory. - */ -NGHTTP2_EXTERN int nghttp2_submit_ping(nghttp2_session *session, uint8_t flags, - const uint8_t *opaque_data); - -/** - * @function - * - * Submits GOAWAY frame with the last stream ID |last_stream_id| and - * the error code |error_code|. - * - * The pre-defined error code is one of :enum:`nghttp2_error_code`. - * - * The |flags| is currently ignored and should be - * :enum:`NGHTTP2_FLAG_NONE`. - * - * The |last_stream_id| is peer's stream ID or 0. So if |session| is - * initialized as client, |last_stream_id| must be even or 0. If - * |session| is initialized as server, |last_stream_id| must be odd or - * 0. - * - * The HTTP/2 specification says last_stream_id must not be increased - * from the value previously sent. So the actual value sent as - * last_stream_id is the minimum value between the given - * |last_stream_id| and the last_stream_id previously sent to the - * peer. - * - * If the |opaque_data| is not ``NULL`` and |opaque_data_len| is not - * zero, those data will be sent as additional debug data. The - * library makes a copy of the memory region pointed by |opaque_data| - * with the length |opaque_data_len|, so the caller does not need to - * keep this memory after the return of this function. If the - * |opaque_data_len| is 0, the |opaque_data| could be ``NULL``. - * - * After successful transmission of GOAWAY, following things happen. - * All incoming streams having strictly more than |last_stream_id| are - * closed. All incoming HEADERS which starts new stream are simply - * ignored. After all active streams are handled, both - * `nghttp2_session_want_read()` and `nghttp2_session_want_write()` - * return 0 and the application can close session. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * :enum:`NGHTTP2_ERR_NOMEM` - * Out of memory. - * :enum:`NGHTTP2_ERR_INVALID_ARGUMENT` - * The |opaque_data_len| is too large; the |last_stream_id| is - * invalid. - */ -NGHTTP2_EXTERN int nghttp2_submit_goaway(nghttp2_session *session, - uint8_t flags, int32_t last_stream_id, - uint32_t error_code, - const uint8_t *opaque_data, - size_t opaque_data_len); - -/** - * @function - * - * Returns the last stream ID of a stream for which - * :type:`nghttp2_on_frame_recv_callback` was invoked most recently. - * The returned value can be used as last_stream_id parameter for - * `nghttp2_submit_goaway()` and - * `nghttp2_session_terminate_session2()`. - * - * This function always succeeds. - */ -NGHTTP2_EXTERN int32_t - nghttp2_session_get_last_proc_stream_id(nghttp2_session *session); - -/** - * @function - * - * Submits WINDOW_UPDATE frame. - * - * The |flags| is currently ignored and should be - * :enum:`NGHTTP2_FLAG_NONE`. - * - * The |stream_id| is the stream ID to send this WINDOW_UPDATE. To - * send connection level WINDOW_UPDATE, specify 0 to |stream_id|. - * - * If the |window_size_increment| is positive, the WINDOW_UPDATE with - * that value as window_size_increment is queued. If the - * |window_size_increment| is larger than the received bytes from the - * remote endpoint, the local window size is increased by that - * difference. - * - * If the |window_size_increment| is negative, the local window size - * is decreased by -|window_size_increment|. If automatic - * WINDOW_UPDATE is enabled - * (`nghttp2_option_set_no_auto_window_update()`), and the library - * decided that the WINDOW_UPDATE should be submitted, then - * WINDOW_UPDATE is queued with the current received bytes count. - * - * If the |window_size_increment| is 0, the function does nothing and - * returns 0. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * :enum:`NGHTTP2_ERR_FLOW_CONTROL` - * The local window size overflow or gets negative. - * :enum:`NGHTTP2_ERR_NOMEM` - * Out of memory. - */ -NGHTTP2_EXTERN int nghttp2_submit_window_update(nghttp2_session *session, - uint8_t flags, - int32_t stream_id, - int32_t window_size_increment); - -/** - * @function - * - * Compares ``lhs->name`` of length ``lhs->namelen`` bytes and - * ``rhs->name`` of length ``rhs->namelen`` bytes. Returns negative - * integer if ``lhs->name`` is found to be less than ``rhs->name``; or - * returns positive integer if ``lhs->name`` is found to be greater - * than ``rhs->name``; or returns 0 otherwise. - */ -NGHTTP2_EXTERN int nghttp2_nv_compare_name(const nghttp2_nv *lhs, - const nghttp2_nv *rhs); - -/** - * @function - * - * A helper function for dealing with NPN in client side or ALPN in - * server side. The |in| contains peer's protocol list in preferable - * order. The format of |in| is length-prefixed and not - * null-terminated. For example, ``h2`` and - * ``http/1.1`` stored in |in| like this:: - * - * in[0] = 2 - * in[1..2] = "h2" - * in[3] = 8 - * in[4..11] = "http/1.1" - * inlen = 12 - * - * The selection algorithm is as follows: - * - * 1. If peer's list contains HTTP/2 protocol the library supports, - * it is selected and returns 1. The following step is not taken. - * - * 2. If peer's list contains ``http/1.1``, this function selects - * ``http/1.1`` and returns 0. The following step is not taken. - * - * 3. This function selects nothing and returns -1 (So called - * non-overlap case). In this case, |out| and |outlen| are left - * untouched. - * - * Selecting ``h2`` means that ``h2`` is written into |*out| and its - * length (which is 2) is assigned to |*outlen|. - * - * For ALPN, refer to https://tools.ietf.org/html/rfc7301 - * - * See http://technotes.googlecode.com/git/nextprotoneg.html for more - * details about NPN. - * - * For NPN, to use this method you should do something like:: - * - * static int select_next_proto_cb(SSL* ssl, - * unsigned char **out, - * unsigned char *outlen, - * const unsigned char *in, - * unsigned int inlen, - * void *arg) - * { - * int rv; - * rv = nghttp2_select_next_protocol(out, outlen, in, inlen); - * if (rv == -1) { - * return SSL_TLSEXT_ERR_NOACK; - * } - * if (rv == 1) { - * ((MyType*)arg)->http2_selected = 1; - * } - * return SSL_TLSEXT_ERR_OK; - * } - * ... - * SSL_CTX_set_next_proto_select_cb(ssl_ctx, select_next_proto_cb, my_obj); - * - */ -NGHTTP2_EXTERN int nghttp2_select_next_protocol(unsigned char **out, - unsigned char *outlen, - const unsigned char *in, - unsigned int inlen); - -/** - * @function - * - * Returns a pointer to a nghttp2_info struct with version information - * about the run-time library in use. The |least_version| argument - * can be set to a 24 bit numerical value for the least accepted - * version number and if the condition is not met, this function will - * return a ``NULL``. Pass in 0 to skip the version checking. - */ -NGHTTP2_EXTERN nghttp2_info *nghttp2_version(int least_version); - -/** - * @function - * - * Returns nonzero if the :type:`nghttp2_error` library error code - * |lib_error| is fatal. - */ -NGHTTP2_EXTERN int nghttp2_is_fatal(int lib_error_code); - -/** - * @function - * - * Returns nonzero if HTTP header field name |name| of length |len| is - * valid according to http://tools.ietf.org/html/rfc7230#section-3.2 - * - * Because this is a header field name in HTTP2, the upper cased alphabet - * is treated as error. - */ -NGHTTP2_EXTERN int nghttp2_check_header_name(const uint8_t *name, size_t len); - -/** - * @function - * - * Returns nonzero if HTTP header field value |value| of length |len| - * is valid according to - * http://tools.ietf.org/html/rfc7230#section-3.2 - */ -NGHTTP2_EXTERN int nghttp2_check_header_value(const uint8_t *value, size_t len); - -/* HPACK API */ - -struct nghttp2_hd_deflater; - -/** - * @struct - * - * HPACK deflater object. - */ -typedef struct nghttp2_hd_deflater nghttp2_hd_deflater; - -/** - * @function - * - * Initializes |*deflater_ptr| for deflating name/values pairs. - * - * The |deflate_hd_table_bufsize_max| is the upper bound of header - * table size the deflater will use. - * - * If this function fails, |*deflater_ptr| is left untouched. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * :enum:`NGHTTP2_ERR_NOMEM` - * Out of memory. - */ -NGHTTP2_EXTERN int nghttp2_hd_deflate_new(nghttp2_hd_deflater **deflater_ptr, - size_t deflate_hd_table_bufsize_max); - -/** - * @function - * - * Like `nghttp2_hd_deflate_new()`, but with additional custom memory - * allocator specified in the |mem|. - * - * The |mem| can be ``NULL`` and the call is equivalent to - * `nghttp2_hd_deflate_new()`. - * - * This function does not take ownership |mem|. The application is - * responsible for freeing |mem|. - * - * The library code does not refer to |mem| pointer after this - * function returns, so the application can safely free it. - */ -NGHTTP2_EXTERN int nghttp2_hd_deflate_new2(nghttp2_hd_deflater **deflater_ptr, - size_t deflate_hd_table_bufsize_max, - nghttp2_mem *mem); - -/** - * @function - * - * Deallocates any resources allocated for |deflater|. - */ -NGHTTP2_EXTERN void nghttp2_hd_deflate_del(nghttp2_hd_deflater *deflater); - -/** - * @function - * - * Changes header table size of the |deflater| to - * |settings_hd_table_bufsize_max| bytes. This may trigger eviction - * in the dynamic table. - * - * The |settings_hd_table_bufsize_max| should be the value received in - * SETTINGS_HEADER_TABLE_SIZE. - * - * The deflater never uses more memory than - * ``deflate_hd_table_bufsize_max`` bytes specified in - * `nghttp2_hd_deflate_new()`. Therefore, if - * |settings_hd_table_bufsize_max| > ``deflate_hd_table_bufsize_max``, - * resulting maximum table size becomes - * ``deflate_hd_table_bufsize_max``. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * :enum:`NGHTTP2_ERR_NOMEM` - * Out of memory. - */ -NGHTTP2_EXTERN int -nghttp2_hd_deflate_change_table_size(nghttp2_hd_deflater *deflater, - size_t settings_hd_table_bufsize_max); - -/** - * @function - * - * Deflates the |nva|, which has the |nvlen| name/value pairs, into - * the |buf| of length |buflen|. - * - * If |buf| is not large enough to store the deflated header block, - * this function fails with :enum:`NGHTTP2_ERR_INSUFF_BUFSIZE`. The - * caller should use `nghttp2_hd_deflate_bound()` to know the upper - * bound of buffer size required to deflate given header name/value - * pairs. - * - * Once this function fails, subsequent call of this function always - * returns :enum:`NGHTTP2_ERR_HEADER_COMP`. - * - * After this function returns, it is safe to delete the |nva|. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * :enum:`NGHTTP2_ERR_NOMEM` - * Out of memory. - * :enum:`NGHTTP2_ERR_HEADER_COMP` - * Deflation process has failed. - * :enum:`NGHTTP2_ERR_INSUFF_BUFSIZE` - * The provided |buflen| size is too small to hold the output. - */ -NGHTTP2_EXTERN ssize_t - nghttp2_hd_deflate_hd(nghttp2_hd_deflater *deflater, uint8_t *buf, - size_t buflen, const nghttp2_nv *nva, size_t nvlen); - -/** - * @function - * - * Returns an upper bound on the compressed size after deflation of - * |nva| of length |nvlen|. - */ -NGHTTP2_EXTERN size_t nghttp2_hd_deflate_bound(nghttp2_hd_deflater *deflater, - const nghttp2_nv *nva, - size_t nvlen); - -struct nghttp2_hd_inflater; - -/** - * @struct - * - * HPACK inflater object. - */ -typedef struct nghttp2_hd_inflater nghttp2_hd_inflater; - -/** - * @function - * - * Initializes |*inflater_ptr| for inflating name/values pairs. - * - * If this function fails, |*inflater_ptr| is left untouched. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * :enum:`NGHTTP2_ERR_NOMEM` - * Out of memory. - */ -NGHTTP2_EXTERN int nghttp2_hd_inflate_new(nghttp2_hd_inflater **inflater_ptr); - -/** - * @function - * - * Like `nghttp2_hd_inflate_new()`, but with additional custom memory - * allocator specified in the |mem|. - * - * The |mem| can be ``NULL`` and the call is equivalent to - * `nghttp2_hd_inflate_new()`. - * - * This function does not take ownership |mem|. The application is - * responsible for freeing |mem|. - * - * The library code does not refer to |mem| pointer after this - * function returns, so the application can safely free it. - */ -NGHTTP2_EXTERN int nghttp2_hd_inflate_new2(nghttp2_hd_inflater **inflater_ptr, - nghttp2_mem *mem); - -/** - * @function - * - * Deallocates any resources allocated for |inflater|. - */ -NGHTTP2_EXTERN void nghttp2_hd_inflate_del(nghttp2_hd_inflater *inflater); - -/** - * @function - * - * Changes header table size in the |inflater|. This may trigger - * eviction in the dynamic table. - * - * The |settings_hd_table_bufsize_max| should be the value transmitted - * in SETTINGS_HEADER_TABLE_SIZE. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * :enum:`NGHTTP2_ERR_NOMEM` - * Out of memory. - */ -NGHTTP2_EXTERN int -nghttp2_hd_inflate_change_table_size(nghttp2_hd_inflater *inflater, - size_t settings_hd_table_bufsize_max); - -/** - * @enum - * - * The flags for header inflation. - */ -typedef enum { - /** - * No flag set. - */ - NGHTTP2_HD_INFLATE_NONE = 0, - /** - * Indicates all headers were inflated. - */ - NGHTTP2_HD_INFLATE_FINAL = 0x01, - /** - * Indicates a header was emitted. - */ - NGHTTP2_HD_INFLATE_EMIT = 0x02 -} nghttp2_hd_inflate_flag; - -/** - * @function - * - * Inflates name/value block stored in |in| with length |inlen|. This - * function performs decompression. For each successful emission of - * header name/value pair, :enum:`NGHTTP2_HD_INFLATE_EMIT` is set in - * |*inflate_flags| and name/value pair is assigned to the |nv_out| - * and the function returns. The caller must not free the members of - * |nv_out|. - * - * The |nv_out| may include pointers to the memory region in the |in|. - * The caller must retain the |in| while the |nv_out| is used. - * - * The application should call this function repeatedly until the - * ``(*inflate_flags) & NGHTTP2_HD_INFLATE_FINAL`` is nonzero and - * return value is non-negative. This means the all input values are - * processed successfully. Then the application must call - * `nghttp2_hd_inflate_end_headers()` to prepare for the next header - * block input. - * - * The caller can feed complete compressed header block. It also can - * feed it in several chunks. The caller must set |in_final| to - * nonzero if the given input is the last block of the compressed - * header. - * - * This function returns the number of bytes processed if it succeeds, - * or one of the following negative error codes: - * - * :enum:`NGHTTP2_ERR_NOMEM` - * Out of memory. - * :enum:`NGHTTP2_ERR_HEADER_COMP` - * Inflation process has failed. - * :enum:`NGHTTP2_ERR_BUFFER_ERROR` - * The heder field name or value is too large. - * - * Example follows:: - * - * int inflate_header_block(nghttp2_hd_inflater *hd_inflater, - * uint8_t *in, size_t inlen, int final) - * { - * ssize_t rv; - * - * for(;;) { - * nghttp2_nv nv; - * int inflate_flags = 0; - * - * rv = nghttp2_hd_inflate_hd(hd_inflater, &nv, &inflate_flags, - * in, inlen, final); - * - * if(rv < 0) { - * fprintf(stderr, "inflate failed with error code %zd", rv); - * return -1; - * } - * - * in += rv; - * inlen -= rv; - * - * if(inflate_flags & NGHTTP2_HD_INFLATE_EMIT) { - * fwrite(nv.name, nv.namelen, 1, stderr); - * fprintf(stderr, ": "); - * fwrite(nv.value, nv.valuelen, 1, stderr); - * fprintf(stderr, "\n"); - * } - * if(inflate_flags & NGHTTP2_HD_INFLATE_FINAL) { - * nghttp2_hd_inflate_end_headers(hd_inflater); - * break; - * } - * if((inflate_flags & NGHTTP2_HD_INFLATE_EMIT) == 0 && - * inlen == 0) { - * break; - * } - * } - * - * return 0; - * } - * - */ -NGHTTP2_EXTERN ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_inflater *inflater, - nghttp2_nv *nv_out, - int *inflate_flags, uint8_t *in, - size_t inlen, int in_final); - -/** - * @function - * - * Signals the end of decompression for one header block. - * - * This function returns 0 if it succeeds. Currently this function - * always succeeds. - */ -NGHTTP2_EXTERN int -nghttp2_hd_inflate_end_headers(nghttp2_hd_inflater *inflater); - -#ifdef __cplusplus -} -#endif - -#endif /* NGHTTP2_H */ diff --git a/lib/includes/nghttp2/nghttp2ver.h b/lib/includes/nghttp2/nghttp2ver.h deleted file mode 100644 index 09be018..0000000 --- a/lib/includes/nghttp2/nghttp2ver.h +++ /dev/null @@ -1,42 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2012, 2013 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#ifndef NGHTTP2VER_H -#define NGHTTP2VER_H - -/** - * @macro - * Version number of the nghttp2 library release - */ -#define NGHTTP2_VERSION "1.0.0" - -/** - * @macro - * Numerical representation of the version number of the nghttp2 library - * 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 NGHTTP2_VERSION_NUM 0x010000 - -#endif /* NGHTTP2VER_H */ diff --git a/lib/includes/nghttp2/nghttp2ver.h.in b/lib/includes/nghttp2/nghttp2ver.h.in deleted file mode 100644 index 7717a64..0000000 --- a/lib/includes/nghttp2/nghttp2ver.h.in +++ /dev/null @@ -1,42 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2012, 2013 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#ifndef NGHTTP2VER_H -#define NGHTTP2VER_H - -/** - * @macro - * Version number of the nghttp2 library release - */ -#define NGHTTP2_VERSION "@PACKAGE_VERSION@" - -/** - * @macro - * Numerical representation of the version number of the nghttp2 library - * 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 NGHTTP2_VERSION_NUM @PACKAGE_VERSION_NUM@ - -#endif /* NGHTTP2VER_H */ diff --git a/lib/libnghttp2.pc.in b/lib/libnghttp2.pc.in deleted file mode 100644 index da6938f..0000000 --- a/lib/libnghttp2.pc.in +++ /dev/null @@ -1,33 +0,0 @@ -# nghttp2 - HTTP/2 C Library - -# Copyright (c) 2012, 2013 Tatsuhiro Tsujikawa - -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: - -# The above copyright notice and this permission notice shall be -# included in all copies or substantial portions of the Software. - -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -prefix=@prefix@ -exec_prefix=@exec_prefix@ -libdir=@libdir@ -includedir=@includedir@ - -Name: libnghttp2 -Description: HTTP/2 C library -URL: https://github.com/tatsuhiro-t/nghttp2 -Version: @VERSION@ -Libs: -L${libdir} -lnghttp2 -Cflags: -I${includedir} diff --git a/lib/nghttp2_buf.c b/lib/nghttp2_buf.c deleted file mode 100644 index 96454a8..0000000 --- a/lib/nghttp2_buf.c +++ /dev/null @@ -1,494 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2014 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#include "nghttp2_buf.h" - -#include - -#include "nghttp2_helper.h" - -void nghttp2_buf_init(nghttp2_buf *buf) { - buf->begin = NULL; - buf->end = NULL; - buf->pos = NULL; - buf->last = NULL; - buf->mark = NULL; -} - -int nghttp2_buf_init2(nghttp2_buf *buf, size_t initial, nghttp2_mem *mem) { - nghttp2_buf_init(buf); - return nghttp2_buf_reserve(buf, initial, mem); -} - -void nghttp2_buf_free(nghttp2_buf *buf, nghttp2_mem *mem) { - if (buf == NULL) { - return; - } - - nghttp2_mem_free(mem, buf->begin); - buf->begin = NULL; -} - -int nghttp2_buf_reserve(nghttp2_buf *buf, size_t new_cap, nghttp2_mem *mem) { - uint8_t *ptr; - size_t cap; - - cap = nghttp2_buf_cap(buf); - - if (cap >= new_cap) { - return 0; - } - - new_cap = nghttp2_max(new_cap, cap * 2); - - ptr = nghttp2_mem_realloc(mem, buf->begin, new_cap); - if (ptr == NULL) { - return NGHTTP2_ERR_NOMEM; - } - - buf->pos = ptr + (buf->pos - buf->begin); - buf->last = ptr + (buf->last - buf->begin); - buf->mark = ptr + (buf->mark - buf->begin); - buf->begin = ptr; - buf->end = ptr + new_cap; - - return 0; -} - -void nghttp2_buf_reset(nghttp2_buf *buf) { - buf->pos = buf->last = buf->mark = buf->begin; -} - -void nghttp2_buf_wrap_init(nghttp2_buf *buf, uint8_t *begin, size_t len) { - buf->begin = buf->pos = buf->last = buf->mark = begin; - buf->end = begin + len; -} - -static int buf_chain_new(nghttp2_buf_chain **chain, size_t chunk_length, - nghttp2_mem *mem) { - int rv; - - *chain = nghttp2_mem_malloc(mem, sizeof(nghttp2_buf_chain)); - if (*chain == NULL) { - return NGHTTP2_ERR_NOMEM; - } - - (*chain)->next = NULL; - - rv = nghttp2_buf_init2(&(*chain)->buf, chunk_length, mem); - if (rv != 0) { - nghttp2_mem_free(mem, *chain); - return NGHTTP2_ERR_NOMEM; - } - - return 0; -} - -static void buf_chain_del(nghttp2_buf_chain *chain, nghttp2_mem *mem) { - nghttp2_buf_free(&chain->buf, mem); - nghttp2_mem_free(mem, chain); -} - -int nghttp2_bufs_init(nghttp2_bufs *bufs, size_t chunk_length, size_t max_chunk, - nghttp2_mem *mem) { - return nghttp2_bufs_init2(bufs, chunk_length, max_chunk, 0, mem); -} - -int nghttp2_bufs_init2(nghttp2_bufs *bufs, size_t chunk_length, - size_t max_chunk, size_t offset, nghttp2_mem *mem) { - return nghttp2_bufs_init3(bufs, chunk_length, max_chunk, max_chunk, offset, - mem); -} - -int nghttp2_bufs_init3(nghttp2_bufs *bufs, size_t chunk_length, - size_t max_chunk, size_t chunk_keep, size_t offset, - nghttp2_mem *mem) { - int rv; - nghttp2_buf_chain *chain; - - if (chunk_keep == 0 || max_chunk < chunk_keep || chunk_length < offset) { - return NGHTTP2_ERR_INVALID_ARGUMENT; - } - - rv = buf_chain_new(&chain, chunk_length, mem); - if (rv != 0) { - return rv; - } - - bufs->mem = mem; - bufs->offset = offset; - - bufs->head = chain; - bufs->cur = bufs->head; - - nghttp2_buf_shift_right(&bufs->cur->buf, offset); - - bufs->chunk_length = chunk_length; - bufs->chunk_used = 1; - bufs->max_chunk = max_chunk; - bufs->chunk_keep = chunk_keep; - - return 0; -} - -int nghttp2_bufs_realloc(nghttp2_bufs *bufs, size_t chunk_length) { - int rv; - nghttp2_buf_chain *chain; - - if (chunk_length < bufs->offset) { - return NGHTTP2_ERR_INVALID_ARGUMENT; - } - - rv = buf_chain_new(&chain, chunk_length, bufs->mem); - if (rv != 0) { - return rv; - } - - nghttp2_bufs_free(bufs); - - bufs->head = chain; - bufs->cur = bufs->head; - - nghttp2_buf_shift_right(&bufs->cur->buf, bufs->offset); - - bufs->chunk_length = chunk_length; - bufs->chunk_used = 1; - - return 0; -} - -void nghttp2_bufs_free(nghttp2_bufs *bufs) { - nghttp2_buf_chain *chain, *next_chain; - - if (bufs == NULL) { - return; - } - - for (chain = bufs->head; chain;) { - next_chain = chain->next; - - buf_chain_del(chain, bufs->mem); - - chain = next_chain; - } - - bufs->head = NULL; -} - -int nghttp2_bufs_wrap_init(nghttp2_bufs *bufs, uint8_t *begin, size_t len, - nghttp2_mem *mem) { - nghttp2_buf_chain *chain; - - chain = nghttp2_mem_malloc(mem, sizeof(nghttp2_buf_chain)); - if (chain == NULL) { - return NGHTTP2_ERR_NOMEM; - } - - chain->next = NULL; - - nghttp2_buf_wrap_init(&chain->buf, begin, len); - - bufs->mem = mem; - bufs->offset = 0; - - bufs->head = chain; - bufs->cur = bufs->head; - - bufs->chunk_length = len; - bufs->chunk_used = 1; - bufs->max_chunk = 1; - bufs->chunk_keep = 1; - - return 0; -} - -void nghttp2_bufs_wrap_free(nghttp2_bufs *bufs) { - if (bufs == NULL) { - return; - } - - nghttp2_mem_free(bufs->mem, bufs->head); - bufs->head = NULL; -} - -void nghttp2_bufs_seek_last_present(nghttp2_bufs *bufs) { - nghttp2_buf_chain *ci; - - for (ci = bufs->cur; ci; ci = ci->next) { - if (nghttp2_buf_len(&ci->buf) == 0) { - return; - } else { - bufs->cur = ci; - } - } -} - -ssize_t nghttp2_bufs_len(nghttp2_bufs *bufs) { - nghttp2_buf_chain *ci; - ssize_t len; - - len = 0; - for (ci = bufs->head; ci; ci = ci->next) { - len += nghttp2_buf_len(&ci->buf); - } - - return len; -} - -static ssize_t bufs_avail(nghttp2_bufs *bufs) { - return (ssize_t)(nghttp2_buf_avail(&bufs->cur->buf) + - (bufs->chunk_length - bufs->offset) * - (bufs->max_chunk - bufs->chunk_used)); -} - -static int bufs_alloc_chain(nghttp2_bufs *bufs) { - int rv; - nghttp2_buf_chain *chain; - - if (bufs->cur->next) { - bufs->cur = bufs->cur->next; - - return 0; - } - - if (bufs->max_chunk == bufs->chunk_used) { - return NGHTTP2_ERR_BUFFER_ERROR; - } - - rv = buf_chain_new(&chain, bufs->chunk_length, bufs->mem); - if (rv != 0) { - return rv; - } - - DEBUGF(fprintf(stderr, - "new buffer %zu bytes allocated for bufs %p, used %zu\n", - bufs->chunk_length, bufs, bufs->chunk_used)); - - ++bufs->chunk_used; - - bufs->cur->next = chain; - bufs->cur = chain; - - nghttp2_buf_shift_right(&bufs->cur->buf, bufs->offset); - - return 0; -} - -int nghttp2_bufs_add(nghttp2_bufs *bufs, const void *data, size_t len) { - int rv; - size_t nwrite; - nghttp2_buf *buf; - const uint8_t *p; - - if (bufs_avail(bufs) < (ssize_t)len) { - return NGHTTP2_ERR_BUFFER_ERROR; - } - - p = data; - - while (len) { - buf = &bufs->cur->buf; - - nwrite = nghttp2_min((size_t)nghttp2_buf_avail(buf), len); - if (nwrite == 0) { - rv = bufs_alloc_chain(bufs); - if (rv != 0) { - return rv; - } - continue; - } - - buf->last = nghttp2_cpymem(buf->last, p, nwrite); - p += nwrite; - len -= nwrite; - } - - return 0; -} - -static int bufs_ensure_addb(nghttp2_bufs *bufs) { - int rv; - nghttp2_buf *buf; - - buf = &bufs->cur->buf; - - if (nghttp2_buf_avail(buf) > 0) { - return 0; - } - - rv = bufs_alloc_chain(bufs); - if (rv != 0) { - return rv; - } - - return 0; -} - -int nghttp2_bufs_addb(nghttp2_bufs *bufs, uint8_t b) { - int rv; - - rv = bufs_ensure_addb(bufs); - if (rv != 0) { - return rv; - } - - *bufs->cur->buf.last++ = b; - - return 0; -} - -int nghttp2_bufs_addb_hold(nghttp2_bufs *bufs, uint8_t b) { - int rv; - - rv = bufs_ensure_addb(bufs); - if (rv != 0) { - return rv; - } - - *bufs->cur->buf.last = b; - - return 0; -} - -int nghttp2_bufs_orb(nghttp2_bufs *bufs, uint8_t b) { - int rv; - - rv = bufs_ensure_addb(bufs); - if (rv != 0) { - return rv; - } - - *bufs->cur->buf.last++ |= b; - - return 0; -} - -int nghttp2_bufs_orb_hold(nghttp2_bufs *bufs, uint8_t b) { - int rv; - - rv = bufs_ensure_addb(bufs); - if (rv != 0) { - return rv; - } - - *bufs->cur->buf.last |= b; - - return 0; -} - -ssize_t nghttp2_bufs_remove(nghttp2_bufs *bufs, uint8_t **out) { - size_t len; - nghttp2_buf_chain *chain; - nghttp2_buf *buf; - uint8_t *res; - nghttp2_buf resbuf; - - len = 0; - - for (chain = bufs->head; chain; chain = chain->next) { - len += nghttp2_buf_len(&chain->buf); - } - - if (len == 0) { - res = NULL; - return 0; - } - - res = nghttp2_mem_malloc(bufs->mem, len); - if (res == NULL) { - return NGHTTP2_ERR_NOMEM; - } - - nghttp2_buf_wrap_init(&resbuf, res, len); - - for (chain = bufs->head; chain; chain = chain->next) { - buf = &chain->buf; - resbuf.last = nghttp2_cpymem(resbuf.last, buf->pos, nghttp2_buf_len(buf)); - } - - *out = res; - - return (ssize_t)len; -} - -size_t nghttp2_bufs_remove_copy(nghttp2_bufs *bufs, uint8_t *out) { - size_t len; - nghttp2_buf_chain *chain; - nghttp2_buf *buf; - nghttp2_buf resbuf; - - len = nghttp2_bufs_len(bufs); - - nghttp2_buf_wrap_init(&resbuf, out, len); - - for (chain = bufs->head; chain; chain = chain->next) { - buf = &chain->buf; - resbuf.last = nghttp2_cpymem(resbuf.last, buf->pos, nghttp2_buf_len(buf)); - } - - return len; -} - -void nghttp2_bufs_reset(nghttp2_bufs *bufs) { - nghttp2_buf_chain *chain, *ci; - size_t k; - - k = bufs->chunk_keep; - - for (ci = bufs->head; ci; ci = ci->next) { - nghttp2_buf_reset(&ci->buf); - nghttp2_buf_shift_right(&ci->buf, bufs->offset); - - if (--k == 0) { - break; - } - } - - if (ci) { - chain = ci->next; - ci->next = NULL; - - for (ci = chain; ci;) { - chain = ci->next; - - buf_chain_del(ci, bufs->mem); - - ci = chain; - } - - bufs->chunk_used = bufs->chunk_keep; - } - - bufs->cur = bufs->head; -} - -int nghttp2_bufs_advance(nghttp2_bufs *bufs) { return bufs_alloc_chain(bufs); } - -int nghttp2_bufs_next_present(nghttp2_bufs *bufs) { - nghttp2_buf_chain *chain; - - chain = bufs->cur->next; - - return chain && nghttp2_buf_len(&chain->buf); -} diff --git a/lib/nghttp2_buf.h b/lib/nghttp2_buf.h deleted file mode 100644 index 9e986c6..0000000 --- a/lib/nghttp2_buf.h +++ /dev/null @@ -1,385 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2014 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#ifndef NGHTTP2_BUF_H -#define NGHTTP2_BUF_H - -#ifdef HAVE_CONFIG_H -#include -#endif /* HAVE_CONFIG_H */ - -#include - -#include "nghttp2_int.h" -#include "nghttp2_mem.h" - -typedef struct { - /* This points to the beginning of the buffer. The effective range - of buffer is [begin, end). */ - uint8_t *begin; - /* This points to the memory one byte beyond the end of the - buffer. */ - uint8_t *end; - /* The position indicator for effective start of the buffer. pos <= - last must be hold. */ - uint8_t *pos; - /* The position indicator for effective one beyond of the end of the - buffer. last <= end must be hold. */ - uint8_t *last; - /* Mark arbitrary position in buffer [begin, end) */ - uint8_t *mark; -} nghttp2_buf; - -#define nghttp2_buf_len(BUF) ((ssize_t)((BUF)->last - (BUF)->pos)) -#define nghttp2_buf_avail(BUF) ((ssize_t)((BUF)->end - (BUF)->last)) -#define nghttp2_buf_mark_avail(BUF) ((ssize_t)((BUF)->mark - (BUF)->last)) -#define nghttp2_buf_cap(BUF) ((ssize_t)((BUF)->end - (BUF)->begin)) - -#define nghttp2_buf_pos_offset(BUF) ((ssize_t)((BUF)->pos - (BUF)->begin)) -#define nghttp2_buf_last_offset(BUF) ((ssize_t)((BUF)->last - (BUF)->begin)) - -#define nghttp2_buf_shift_right(BUF, AMT) \ - do { \ - (BUF)->pos += AMT; \ - (BUF)->last += AMT; \ - } while (0) - -#define nghttp2_buf_shift_left(BUF, AMT) \ - do { \ - (BUF)->pos -= AMT; \ - (BUF)->last -= AMT; \ - } while (0) - -/* - * Initializes the |buf|. No memory is allocated in this function. Use - * nghttp2_buf_reserve() or nghttp2_buf_reserve2() to allocate memory. - */ -void nghttp2_buf_init(nghttp2_buf *buf); - -/* - * Initializes the |buf| and allocates at least |initial| bytes of - * memory. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP2_ERR_NOMEM - * Out of memory - */ -int nghttp2_buf_init2(nghttp2_buf *buf, size_t initial, nghttp2_mem *mem); - -/* - * Frees buffer in |buf|. - */ -void nghttp2_buf_free(nghttp2_buf *buf, nghttp2_mem *mem); - -/* - * Extends buffer so that nghttp2_buf_cap() returns at least - * |new_cap|. If extensions took place, buffer pointers in |buf| will - * change. - * - * This function returns 0 if it succeeds, or one of the followings - * negative error codes: - * - * NGHTTP2_ERR_NOMEM - * Out of memory - */ -int nghttp2_buf_reserve(nghttp2_buf *buf, size_t new_cap, nghttp2_mem *mem); - -/* - * Resets pos, last, mark member of |buf| to buf->begin. - */ -void nghttp2_buf_reset(nghttp2_buf *buf); - -/* - * Initializes |buf| using supplied buffer |begin| of length - * |len|. Semantically, the application should not call *_reserve() or - * nghttp2_free() functions for |buf|. - */ -void nghttp2_buf_wrap_init(nghttp2_buf *buf, uint8_t *begin, size_t len); - -struct nghttp2_buf_chain; - -typedef struct nghttp2_buf_chain nghttp2_buf_chain; - -/* Chains 2 buffers */ -struct nghttp2_buf_chain { - /* Points to the subsequent buffer. NULL if there is no such - buffer. */ - nghttp2_buf_chain *next; - nghttp2_buf buf; -}; - -typedef struct { - /* Points to the first buffer */ - nghttp2_buf_chain *head; - /* Buffer pointer where write occurs. */ - nghttp2_buf_chain *cur; - /* Memory allocator */ - nghttp2_mem *mem; - /* The buffer capacity of each buf */ - size_t chunk_length; - /* The maximum number of nghttp2_buf_chain */ - size_t max_chunk; - /* The number of nghttp2_buf_chain allocated */ - size_t chunk_used; - /* The number of nghttp2_buf_chain to keep on reset */ - size_t chunk_keep; - /* pos offset from begin in each buffers. On initialization and - reset, buf->pos and buf->last are positioned at buf->begin + - offset. */ - size_t offset; -} nghttp2_bufs; - -/* - * This is the same as calling nghttp2_bufs_init2 with the given - * arguments and offset = 0. - */ -int nghttp2_bufs_init(nghttp2_bufs *bufs, size_t chunk_length, size_t max_chunk, - nghttp2_mem *mem); - -/* - * This is the same as calling nghttp2_bufs_init3 with the given - * arguments and chunk_keep = max_chunk. - */ -int nghttp2_bufs_init2(nghttp2_bufs *bufs, size_t chunk_length, - size_t max_chunk, size_t offset, nghttp2_mem *mem); - -/* - * Initializes |bufs|. Each buffer size is given in the - * |chunk_length|. The maximum number of buffers is given in the - * |max_chunk|. On reset, first |chunk_keep| buffers are kept and - * remaining buffers are deleted. Each buffer will have bufs->pos and - * bufs->last shifted to left by |offset| bytes on creation and reset. - * - * This function allocates first buffer. bufs->head and bufs->cur - * will point to the first buffer after this call. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP2_ERR_NOMEM - * Out of memory. - * NGHTTP2_ERR_INVALID_ARGUMENT - * chunk_keep is 0; or max_chunk < chunk_keep; or offset is too - * long. - */ -int nghttp2_bufs_init3(nghttp2_bufs *bufs, size_t chunk_length, - size_t max_chunk, size_t chunk_keep, size_t offset, - nghttp2_mem *mem); - -/* - * Frees any related resources to the |bufs|. - */ -void nghttp2_bufs_free(nghttp2_bufs *bufs); - -/* - * Initializes |bufs| using supplied buffer |begin| of length |len|. - * The first buffer bufs->head uses buffer |begin|. The buffer size - * is fixed and no allocate extra chunk buffer is allocated. In other - * words, max_chunk = chunk_keep = 1. To free the resource allocated - * for |bufs|, use nghttp2_bufs_wrap_free(). - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP2_ERR_NOMEM - * Out of memory. - */ -int nghttp2_bufs_wrap_init(nghttp2_bufs *bufs, uint8_t *begin, size_t len, - nghttp2_mem *mem); - -/* - * Frees any related resource to the |bufs|. This function does not - * free supplied buffer provided in nghttp2_bufs_wrap_init(). - */ -void nghttp2_bufs_wrap_free(nghttp2_bufs *bufs); - -/* - * Reallocates internal buffer using |chunk_length|. The max_chunk, - * chunk_keep and offset do not change. After successful allocation - * of new buffer, previous buffers are deallocated without copying - * anything into new buffers. chunk_used is reset to 1. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP2_ERR_NOMEM - * Out of memory. - * NGHTTP2_ERR_INVALID_ARGUMENT - * chunk_length < offset - */ -int nghttp2_bufs_realloc(nghttp2_bufs *bufs, size_t chunk_length); - -/* - * Appends the |data| of length |len| to the |bufs|. The write starts - * at bufs->cur->buf.last. A new buffers will be allocated to store - * all data. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP2_ERR_NOMEM - * Out of memory. - * NGHTTP2_ERR_BUFFER_ERROR - * Out of buffer space. - */ -int nghttp2_bufs_add(nghttp2_bufs *bufs, const void *data, size_t len); - -/* - * Appends a single byte |b| to the |bufs|. The write starts at - * bufs->cur->buf.last. A new buffers will be allocated to store all - * data. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP2_ERR_NOMEM - * Out of memory. - * NGHTTP2_ERR_BUFFER_ERROR - * Out of buffer space. - */ -int nghttp2_bufs_addb(nghttp2_bufs *bufs, uint8_t b); - -/* - * Behaves like nghttp2_bufs_addb(), but this does not update - * buf->last pointer. - */ -int nghttp2_bufs_addb_hold(nghttp2_bufs *bufs, uint8_t b); - -#define nghttp2_bufs_fast_addb(BUFS, B) \ - do { \ - *(BUFS)->cur->buf.last++ = B; \ - } while (0) - -#define nghttp2_bufs_fast_addb_hold(BUFS, B) \ - do { \ - *(BUFS)->cur->buf.last = B; \ - } while (0) - -/* - * Performs bitwise-OR of |b| at bufs->cur->buf.last. A new buffers - * will be allocated if necessary. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP2_ERR_NOMEM - * Out of memory. - * NGHTTP2_ERR_BUFFER_ERROR - * Out of buffer space. - */ -int nghttp2_bufs_orb(nghttp2_bufs *bufs, uint8_t b); - -/* - * Behaves like nghttp2_bufs_orb(), but does not update buf->last - * pointer. - */ -int nghttp2_bufs_orb_hold(nghttp2_bufs *bufs, uint8_t b); - -#define nghttp2_bufs_fast_orb(BUFS, B) \ - do { \ - *(BUFS)->cur->buf.last++ |= B; \ - } while (0) - -#define nghttp2_bufs_fast_orb_hold(BUFS, B) \ - do { \ - *(BUFS)->cur->buf.last |= B; \ - } while (0) - -/* - * Copies all data stored in |bufs| to the contagious buffer. This - * function allocates the contagious memory to store all data in - * |bufs| and assigns it to |*out|. - * - * The contents of |bufs| is left unchanged. - * - * This function returns the length of copied data and assigns the - * pointer to copied data to |*out| if it succeeds, or one of the - * following negative error codes: - * - * NGHTTP2_ERR_NOMEM - * Out of memory - */ -ssize_t nghttp2_bufs_remove(nghttp2_bufs *bufs, uint8_t **out); - -/* - * Copies all data stored in |bufs| to |out|. This function assumes - * that the buffer space pointed by |out| has at least - * nghttp2_bufs(bufs) bytes. - * - * The contents of |bufs| is left unchanged. - * - * This function returns the length of copied data. - */ -size_t nghttp2_bufs_remove_copy(nghttp2_bufs *bufs, uint8_t *out); - -/* - * Resets |bufs| and makes the buffers empty. - */ -void nghttp2_bufs_reset(nghttp2_bufs *bufs); - -/* - * Moves bufs->cur to bufs->cur->next. If resulting bufs->cur is - * NULL, this function allocates new buffers and bufs->cur points to - * it. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP2_ERR_NOMEM - * Out of memory - * NGHTTP2_ERR_BUFFER_ERROR - * Out of buffer space. - */ -int nghttp2_bufs_advance(nghttp2_bufs *bufs); - -/* Sets bufs->cur to bufs->head */ -#define nghttp2_bufs_rewind(BUFS) \ - do { \ - (BUFS)->cur = (BUFS)->head; \ - } while (0) - -/* - * Move bufs->cur, from the current position, using next member, to - * the last buf which has nghttp2_buf_len(buf) > 0 without seeing buf - * which satisfies nghttp2_buf_len(buf) == 0. If - * nghttp2_buf_len(&bufs->cur->buf) == 0 or bufs->cur->next is NULL, - * bufs->cur is unchanged. - */ -void nghttp2_bufs_seek_last_present(nghttp2_bufs *bufs); - -/* - * Returns nonzero if bufs->cur->next is not emtpy. - */ -int nghttp2_bufs_next_present(nghttp2_bufs *bufs); - -#define nghttp2_bufs_cur_avail(BUFS) nghttp2_buf_avail(&(BUFS)->cur->buf) - -/* - * Returns the buffer length of |bufs|. - */ -ssize_t nghttp2_bufs_len(nghttp2_bufs *bufs); - -#endif /* NGHTTP2_BUF_H */ diff --git a/lib/nghttp2_callbacks.c b/lib/nghttp2_callbacks.c deleted file mode 100644 index 3c4be9a..0000000 --- a/lib/nghttp2_callbacks.c +++ /dev/null @@ -1,129 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2014 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#include "nghttp2_callbacks.h" - -#include - -int nghttp2_session_callbacks_new(nghttp2_session_callbacks **callbacks_ptr) { - *callbacks_ptr = calloc(1, sizeof(nghttp2_session_callbacks)); - - if (*callbacks_ptr == NULL) { - return NGHTTP2_ERR_NOMEM; - } - - return 0; -} - -void nghttp2_session_callbacks_del(nghttp2_session_callbacks *callbacks) { - free(callbacks); -} - -void nghttp2_session_callbacks_set_send_callback( - nghttp2_session_callbacks *cbs, nghttp2_send_callback send_callback) { - cbs->send_callback = send_callback; -} - -void nghttp2_session_callbacks_set_recv_callback( - nghttp2_session_callbacks *cbs, nghttp2_recv_callback recv_callback) { - cbs->recv_callback = recv_callback; -} - -void nghttp2_session_callbacks_set_on_frame_recv_callback( - nghttp2_session_callbacks *cbs, - nghttp2_on_frame_recv_callback on_frame_recv_callback) { - cbs->on_frame_recv_callback = on_frame_recv_callback; -} - -void nghttp2_session_callbacks_set_on_invalid_frame_recv_callback( - nghttp2_session_callbacks *cbs, - nghttp2_on_invalid_frame_recv_callback on_invalid_frame_recv_callback) { - cbs->on_invalid_frame_recv_callback = on_invalid_frame_recv_callback; -} - -void nghttp2_session_callbacks_set_on_data_chunk_recv_callback( - nghttp2_session_callbacks *cbs, - nghttp2_on_data_chunk_recv_callback on_data_chunk_recv_callback) { - cbs->on_data_chunk_recv_callback = on_data_chunk_recv_callback; -} - -void nghttp2_session_callbacks_set_before_frame_send_callback( - nghttp2_session_callbacks *cbs, - nghttp2_before_frame_send_callback before_frame_send_callback) { - cbs->before_frame_send_callback = before_frame_send_callback; -} - -void nghttp2_session_callbacks_set_on_frame_send_callback( - nghttp2_session_callbacks *cbs, - nghttp2_on_frame_send_callback on_frame_send_callback) { - cbs->on_frame_send_callback = on_frame_send_callback; -} - -void nghttp2_session_callbacks_set_on_frame_not_send_callback( - nghttp2_session_callbacks *cbs, - nghttp2_on_frame_not_send_callback on_frame_not_send_callback) { - cbs->on_frame_not_send_callback = on_frame_not_send_callback; -} - -void nghttp2_session_callbacks_set_on_stream_close_callback( - nghttp2_session_callbacks *cbs, - nghttp2_on_stream_close_callback on_stream_close_callback) { - cbs->on_stream_close_callback = on_stream_close_callback; -} - -void nghttp2_session_callbacks_set_on_begin_headers_callback( - nghttp2_session_callbacks *cbs, - nghttp2_on_begin_headers_callback on_begin_headers_callback) { - cbs->on_begin_headers_callback = on_begin_headers_callback; -} - -void nghttp2_session_callbacks_set_on_header_callback( - nghttp2_session_callbacks *cbs, - nghttp2_on_header_callback on_header_callback) { - cbs->on_header_callback = on_header_callback; -} - -void nghttp2_session_callbacks_set_select_padding_callback( - nghttp2_session_callbacks *cbs, - nghttp2_select_padding_callback select_padding_callback) { - cbs->select_padding_callback = select_padding_callback; -} - -void nghttp2_session_callbacks_set_data_source_read_length_callback( - nghttp2_session_callbacks *cbs, - nghttp2_data_source_read_length_callback data_source_read_length_callback) { - cbs->read_length_callback = data_source_read_length_callback; -} - -void nghttp2_session_callbacks_set_on_begin_frame_callback( - nghttp2_session_callbacks *cbs, - nghttp2_on_begin_frame_callback on_begin_frame_callback) { - cbs->on_begin_frame_callback = on_begin_frame_callback; -} - -void nghttp2_session_callbacks_set_send_data_callback( - nghttp2_session_callbacks *cbs, - nghttp2_send_data_callback send_data_callback) { - cbs->send_data_callback = send_data_callback; -} diff --git a/lib/nghttp2_callbacks.h b/lib/nghttp2_callbacks.h deleted file mode 100644 index 37958ea..0000000 --- a/lib/nghttp2_callbacks.h +++ /dev/null @@ -1,112 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2014 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#ifndef NGHTTP2_CALLBACKS_H -#define NGHTTP2_CALLBACKS_H - -#ifdef HAVE_CONFIG_H -#include -#endif /* HAVE_CONFIG_H */ - -#include - -/* - * Callback functions. - */ -struct nghttp2_session_callbacks { - /** - * Callback function invoked when the session wants to send data to - * the remote peer. This callback is not necessary if the - * application uses solely `nghttp2_session_mem_send()` to serialize - * data to transmit. - */ - nghttp2_send_callback send_callback; - /** - * Callback function invoked when the session wants to receive data - * from the remote peer. This callback is not necessary if the - * application uses solely `nghttp2_session_mem_recv()` to process - * received data. - */ - nghttp2_recv_callback recv_callback; - /** - * Callback function invoked by `nghttp2_session_recv()` when a - * frame is received. - */ - nghttp2_on_frame_recv_callback on_frame_recv_callback; - /** - * Callback function invoked by `nghttp2_session_recv()` when an - * invalid non-DATA frame is received. - */ - nghttp2_on_invalid_frame_recv_callback on_invalid_frame_recv_callback; - /** - * Callback function invoked when a chunk of data in DATA frame is - * received. - */ - nghttp2_on_data_chunk_recv_callback on_data_chunk_recv_callback; - /** - * Callback function invoked before a non-DATA frame is sent. - */ - nghttp2_before_frame_send_callback before_frame_send_callback; - /** - * Callback function invoked after a frame is sent. - */ - nghttp2_on_frame_send_callback on_frame_send_callback; - /** - * The callback function invoked when a non-DATA frame is not sent - * because of an error. - */ - nghttp2_on_frame_not_send_callback on_frame_not_send_callback; - /** - * Callback function invoked when the stream is closed. - */ - nghttp2_on_stream_close_callback on_stream_close_callback; - /** - * Callback function invoked when the reception of header block in - * HEADERS or PUSH_PROMISE is started. - */ - nghttp2_on_begin_headers_callback on_begin_headers_callback; - /** - * Callback function invoked when a header name/value pair is - * received. - */ - nghttp2_on_header_callback on_header_callback; - /** - * Callback function invoked when the library asks application how - * many padding bytes are required for the transmission of the given - * frame. - */ - nghttp2_select_padding_callback select_padding_callback; - /** - * The callback function used to determine the length allowed in - * `nghttp2_data_source_read_callback()` - */ - nghttp2_data_source_read_length_callback read_length_callback; - /** - * Sets callback function invoked when a frame header is received. - */ - nghttp2_on_begin_frame_callback on_begin_frame_callback; - nghttp2_send_data_callback send_data_callback; -}; - -#endif /* NGHTTP2_CALLBACKS_H */ diff --git a/lib/nghttp2_frame.c b/lib/nghttp2_frame.c deleted file mode 100644 index 07f93a1..0000000 --- a/lib/nghttp2_frame.c +++ /dev/null @@ -1,888 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2013 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#include "nghttp2_frame.h" - -#include -#include -#include -#include - -#include "nghttp2_helper.h" -#include "nghttp2_net.h" -#include "nghttp2_priority_spec.h" - -void nghttp2_frame_pack_frame_hd(uint8_t *buf, const nghttp2_frame_hd *hd) { - nghttp2_put_uint32be(&buf[0], (uint32_t)(hd->length << 8)); - buf[3] = hd->type; - buf[4] = hd->flags; - nghttp2_put_uint32be(&buf[5], hd->stream_id); - /* ignore hd->reserved for now */ -} - -void nghttp2_frame_unpack_frame_hd(nghttp2_frame_hd *hd, const uint8_t *buf) { - hd->length = nghttp2_get_uint32(&buf[0]) >> 8; - hd->type = buf[3]; - hd->flags = buf[4]; - hd->stream_id = nghttp2_get_uint32(&buf[5]) & NGHTTP2_STREAM_ID_MASK; - hd->reserved = 0; -} - -void nghttp2_frame_hd_init(nghttp2_frame_hd *hd, size_t length, uint8_t type, - uint8_t flags, int32_t stream_id) { - hd->length = length; - hd->type = type; - hd->flags = flags; - hd->stream_id = stream_id; - hd->reserved = 0; -} - -void nghttp2_frame_headers_init(nghttp2_headers *frame, uint8_t flags, - int32_t stream_id, nghttp2_headers_category cat, - const nghttp2_priority_spec *pri_spec, - nghttp2_nv *nva, size_t nvlen) { - nghttp2_frame_hd_init(&frame->hd, 0, NGHTTP2_HEADERS, flags, stream_id); - frame->padlen = 0; - frame->nva = nva; - frame->nvlen = nvlen; - frame->cat = cat; - - if (pri_spec) { - frame->pri_spec = *pri_spec; - } else { - nghttp2_priority_spec_default_init(&frame->pri_spec); - } -} - -void nghttp2_frame_headers_free(nghttp2_headers *frame, nghttp2_mem *mem) { - nghttp2_nv_array_del(frame->nva, mem); -} - -void nghttp2_frame_priority_init(nghttp2_priority *frame, int32_t stream_id, - const nghttp2_priority_spec *pri_spec) { - nghttp2_frame_hd_init(&frame->hd, NGHTTP2_PRIORITY_SPECLEN, NGHTTP2_PRIORITY, - NGHTTP2_FLAG_NONE, stream_id); - frame->pri_spec = *pri_spec; -} - -void nghttp2_frame_priority_free(nghttp2_priority *frame _U_) {} - -void nghttp2_frame_rst_stream_init(nghttp2_rst_stream *frame, int32_t stream_id, - uint32_t error_code) { - nghttp2_frame_hd_init(&frame->hd, 4, NGHTTP2_RST_STREAM, NGHTTP2_FLAG_NONE, - stream_id); - frame->error_code = error_code; -} - -void nghttp2_frame_rst_stream_free(nghttp2_rst_stream *frame _U_) {} - -void nghttp2_frame_settings_init(nghttp2_settings *frame, uint8_t flags, - nghttp2_settings_entry *iv, size_t niv) { - nghttp2_frame_hd_init(&frame->hd, niv * NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH, - NGHTTP2_SETTINGS, flags, 0); - frame->niv = niv; - frame->iv = iv; -} - -void nghttp2_frame_settings_free(nghttp2_settings *frame, nghttp2_mem *mem) { - nghttp2_mem_free(mem, frame->iv); -} - -void nghttp2_frame_push_promise_init(nghttp2_push_promise *frame, uint8_t flags, - int32_t stream_id, - int32_t promised_stream_id, - nghttp2_nv *nva, size_t nvlen) { - nghttp2_frame_hd_init(&frame->hd, 0, NGHTTP2_PUSH_PROMISE, flags, stream_id); - frame->padlen = 0; - frame->nva = nva; - frame->nvlen = nvlen; - frame->promised_stream_id = promised_stream_id; - frame->reserved = 0; -} - -void nghttp2_frame_push_promise_free(nghttp2_push_promise *frame, - nghttp2_mem *mem) { - nghttp2_nv_array_del(frame->nva, mem); -} - -void nghttp2_frame_ping_init(nghttp2_ping *frame, uint8_t flags, - const uint8_t *opaque_data) { - nghttp2_frame_hd_init(&frame->hd, 8, NGHTTP2_PING, flags, 0); - if (opaque_data) { - memcpy(frame->opaque_data, opaque_data, sizeof(frame->opaque_data)); - } else { - memset(frame->opaque_data, 0, sizeof(frame->opaque_data)); - } -} - -void nghttp2_frame_ping_free(nghttp2_ping *frame _U_) {} - -void nghttp2_frame_goaway_init(nghttp2_goaway *frame, int32_t last_stream_id, - uint32_t error_code, uint8_t *opaque_data, - size_t opaque_data_len) { - nghttp2_frame_hd_init(&frame->hd, 8 + opaque_data_len, NGHTTP2_GOAWAY, - NGHTTP2_FLAG_NONE, 0); - frame->last_stream_id = last_stream_id; - frame->error_code = error_code; - frame->opaque_data = opaque_data; - frame->opaque_data_len = opaque_data_len; - frame->reserved = 0; -} - -void nghttp2_frame_goaway_free(nghttp2_goaway *frame, nghttp2_mem *mem) { - nghttp2_mem_free(mem, frame->opaque_data); -} - -void nghttp2_frame_window_update_init(nghttp2_window_update *frame, - uint8_t flags, int32_t stream_id, - int32_t window_size_increment) { - nghttp2_frame_hd_init(&frame->hd, 4, NGHTTP2_WINDOW_UPDATE, flags, stream_id); - frame->window_size_increment = window_size_increment; - frame->reserved = 0; -} - -void nghttp2_frame_window_update_free(nghttp2_window_update *frame _U_) {} - -size_t nghttp2_frame_trail_padlen(nghttp2_frame *frame, size_t padlen) { - return padlen - ((frame->hd.flags & NGHTTP2_FLAG_PADDED) > 0); -} - -void nghttp2_frame_data_init(nghttp2_data *frame, uint8_t flags, - int32_t stream_id) { - /* At this moment, the length of DATA frame is unknown */ - nghttp2_frame_hd_init(&frame->hd, 0, NGHTTP2_DATA, flags, stream_id); - frame->padlen = 0; -} - -void nghttp2_frame_data_free(nghttp2_data *frame _U_) {} - -size_t nghttp2_frame_priority_len(uint8_t flags) { - if (flags & NGHTTP2_FLAG_PRIORITY) { - return NGHTTP2_PRIORITY_SPECLEN; - } - - return 0; -} - -size_t nghttp2_frame_headers_payload_nv_offset(nghttp2_headers *frame) { - return nghttp2_frame_priority_len(frame->hd.flags); -} - -/* - * Call this function after payload was serialized, but not before - * changing buf->pos and serializing frame header. - * - * This function assumes bufs->cur points to the last buf chain of the - * frame(s). - * - * This function serializes frame header for HEADERS/PUSH_PROMISE and - * handles their successive CONTINUATION frames. - * - * We don't process any padding here. - */ -static int frame_pack_headers_shared(nghttp2_bufs *bufs, - nghttp2_frame_hd *frame_hd) { - nghttp2_buf *buf; - nghttp2_buf_chain *ci, *ce; - nghttp2_frame_hd hd; - - buf = &bufs->head->buf; - - hd = *frame_hd; - hd.length = nghttp2_buf_len(buf); - - DEBUGF(fprintf(stderr, "send: HEADERS/PUSH_PROMISE, payloadlen=%zu\n", - hd.length)); - - /* We have multiple frame buffers, which means one or more - CONTINUATION frame is involved. Remove END_HEADERS flag from the - first frame. */ - if (bufs->head != bufs->cur) { - hd.flags &= ~NGHTTP2_FLAG_END_HEADERS; - } - - buf->pos -= NGHTTP2_FRAME_HDLEN; - nghttp2_frame_pack_frame_hd(buf->pos, &hd); - - if (bufs->head != bufs->cur) { - /* 2nd and later frames are CONTINUATION frames. */ - hd.type = NGHTTP2_CONTINUATION; - /* We don't have no flags except for last CONTINUATION */ - hd.flags = NGHTTP2_FLAG_NONE; - - ce = bufs->cur; - - for (ci = bufs->head->next; ci != ce; ci = ci->next) { - buf = &ci->buf; - - hd.length = nghttp2_buf_len(buf); - - DEBUGF(fprintf(stderr, "send: int CONTINUATION, payloadlen=%zu\n", - hd.length)); - - buf->pos -= NGHTTP2_FRAME_HDLEN; - nghttp2_frame_pack_frame_hd(buf->pos, &hd); - } - - buf = &ci->buf; - hd.length = nghttp2_buf_len(buf); - /* Set END_HEADERS flag for last CONTINUATION */ - hd.flags = NGHTTP2_FLAG_END_HEADERS; - - DEBUGF(fprintf(stderr, "send: last CONTINUATION, payloadlen=%zu\n", - hd.length)); - - buf->pos -= NGHTTP2_FRAME_HDLEN; - nghttp2_frame_pack_frame_hd(buf->pos, &hd); - } - - return 0; -} - -int nghttp2_frame_pack_headers(nghttp2_bufs *bufs, nghttp2_headers *frame, - nghttp2_hd_deflater *deflater) { - size_t nv_offset; - int rv; - nghttp2_buf *buf; - - assert(bufs->head == bufs->cur); - - nv_offset = nghttp2_frame_headers_payload_nv_offset(frame); - - buf = &bufs->cur->buf; - - buf->pos += nv_offset; - buf->last = buf->pos; - - /* This call will adjust buf->last to the correct position */ - rv = nghttp2_hd_deflate_hd_bufs(deflater, bufs, frame->nva, frame->nvlen); - - if (rv == NGHTTP2_ERR_BUFFER_ERROR) { - rv = NGHTTP2_ERR_HEADER_COMP; - } - - buf->pos -= nv_offset; - - if (rv != 0) { - return rv; - } - - if (frame->hd.flags & NGHTTP2_FLAG_PRIORITY) { - nghttp2_frame_pack_priority_spec(buf->pos, &frame->pri_spec); - } - - frame->padlen = 0; - frame->hd.length = nghttp2_bufs_len(bufs); - - return frame_pack_headers_shared(bufs, &frame->hd); -} - -void nghttp2_frame_pack_priority_spec(uint8_t *buf, - const nghttp2_priority_spec *pri_spec) { - nghttp2_put_uint32be(buf, pri_spec->stream_id); - if (pri_spec->exclusive) { - buf[0] |= 0x80; - } - buf[4] = pri_spec->weight - 1; -} - -void nghttp2_frame_unpack_priority_spec(nghttp2_priority_spec *pri_spec, - uint8_t flags _U_, - const uint8_t *payload, - size_t payloadlen _U_) { - int32_t dep_stream_id; - uint8_t exclusive; - int32_t weight; - - dep_stream_id = nghttp2_get_uint32(payload) & NGHTTP2_STREAM_ID_MASK; - exclusive = (payload[0] & 0x80) > 0; - weight = payload[4] + 1; - - nghttp2_priority_spec_init(pri_spec, dep_stream_id, weight, exclusive); -} - -int nghttp2_frame_unpack_headers_payload(nghttp2_headers *frame, - const uint8_t *payload, - size_t payloadlen) { - if (frame->hd.flags & NGHTTP2_FLAG_PRIORITY) { - nghttp2_frame_unpack_priority_spec(&frame->pri_spec, frame->hd.flags, - payload, payloadlen); - } else { - nghttp2_priority_spec_default_init(&frame->pri_spec); - } - - frame->nva = NULL; - frame->nvlen = 0; - - return 0; -} - -int nghttp2_frame_pack_priority(nghttp2_bufs *bufs, nghttp2_priority *frame) { - nghttp2_buf *buf; - - assert(bufs->head == bufs->cur); - - buf = &bufs->head->buf; - - assert(nghttp2_buf_avail(buf) >= NGHTTP2_PRIORITY_SPECLEN); - - buf->pos -= NGHTTP2_FRAME_HDLEN; - - nghttp2_frame_pack_frame_hd(buf->pos, &frame->hd); - - nghttp2_frame_pack_priority_spec(buf->last, &frame->pri_spec); - - buf->last += NGHTTP2_PRIORITY_SPECLEN; - - return 0; -} - -void nghttp2_frame_unpack_priority_payload(nghttp2_priority *frame, - const uint8_t *payload, - size_t payloadlen) { - nghttp2_frame_unpack_priority_spec(&frame->pri_spec, frame->hd.flags, payload, - payloadlen); -} - -int nghttp2_frame_pack_rst_stream(nghttp2_bufs *bufs, - nghttp2_rst_stream *frame) { - nghttp2_buf *buf; - - assert(bufs->head == bufs->cur); - - buf = &bufs->head->buf; - - assert(nghttp2_buf_avail(buf) >= 4); - - buf->pos -= NGHTTP2_FRAME_HDLEN; - - nghttp2_frame_pack_frame_hd(buf->pos, &frame->hd); - - nghttp2_put_uint32be(buf->last, frame->error_code); - buf->last += 4; - - return 0; -} - -void nghttp2_frame_unpack_rst_stream_payload(nghttp2_rst_stream *frame, - const uint8_t *payload, - size_t payloadlen _U_) { - frame->error_code = nghttp2_get_uint32(payload); -} - -int nghttp2_frame_pack_settings(nghttp2_bufs *bufs, nghttp2_settings *frame) { - nghttp2_buf *buf; - - assert(bufs->head == bufs->cur); - - buf = &bufs->head->buf; - - if (nghttp2_buf_avail(buf) < (ssize_t)frame->hd.length) { - return NGHTTP2_ERR_FRAME_SIZE_ERROR; - } - - buf->pos -= NGHTTP2_FRAME_HDLEN; - - nghttp2_frame_pack_frame_hd(buf->pos, &frame->hd); - - buf->last += - nghttp2_frame_pack_settings_payload(buf->last, frame->iv, frame->niv); - - return 0; -} - -size_t nghttp2_frame_pack_settings_payload(uint8_t *buf, - const nghttp2_settings_entry *iv, - size_t niv) { - size_t i; - for (i = 0; i < niv; ++i, buf += NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH) { - nghttp2_put_uint16be(buf, iv[i].settings_id); - nghttp2_put_uint32be(buf + 2, iv[i].value); - } - return NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH * niv; -} - -int nghttp2_frame_unpack_settings_payload(nghttp2_settings *frame, - nghttp2_settings_entry *iv, - size_t niv, nghttp2_mem *mem) { - size_t payloadlen = niv * sizeof(nghttp2_settings_entry); - - if (niv == 0) { - frame->iv = NULL; - } else { - frame->iv = nghttp2_mem_malloc(mem, payloadlen); - - if (frame->iv == NULL) { - return NGHTTP2_ERR_NOMEM; - } - - memcpy(frame->iv, iv, payloadlen); - } - - frame->niv = niv; - return 0; -} - -void nghttp2_frame_unpack_settings_entry(nghttp2_settings_entry *iv, - const uint8_t *payload) { - iv->settings_id = nghttp2_get_uint16(&payload[0]); - iv->value = nghttp2_get_uint32(&payload[2]); -} - -int nghttp2_frame_unpack_settings_payload2(nghttp2_settings_entry **iv_ptr, - size_t *niv_ptr, - const uint8_t *payload, - size_t payloadlen, - nghttp2_mem *mem) { - size_t i; - - *niv_ptr = payloadlen / NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH; - - if (*niv_ptr == 0) { - *iv_ptr = NULL; - - return 0; - } - - *iv_ptr = - nghttp2_mem_malloc(mem, (*niv_ptr) * sizeof(nghttp2_settings_entry)); - - if (*iv_ptr == NULL) { - return NGHTTP2_ERR_NOMEM; - } - - for (i = 0; i < *niv_ptr; ++i) { - size_t off = i * NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH; - nghttp2_frame_unpack_settings_entry(&(*iv_ptr)[i], &payload[off]); - } - - return 0; -} - -int nghttp2_frame_pack_push_promise(nghttp2_bufs *bufs, - nghttp2_push_promise *frame, - nghttp2_hd_deflater *deflater) { - size_t nv_offset = 4; - int rv; - nghttp2_buf *buf; - - assert(bufs->head == bufs->cur); - - buf = &bufs->cur->buf; - - buf->pos += nv_offset; - buf->last = buf->pos; - - /* This call will adjust buf->last to the correct position */ - rv = nghttp2_hd_deflate_hd_bufs(deflater, bufs, frame->nva, frame->nvlen); - - if (rv == NGHTTP2_ERR_BUFFER_ERROR) { - rv = NGHTTP2_ERR_HEADER_COMP; - } - - buf->pos -= nv_offset; - - if (rv != 0) { - return rv; - } - - nghttp2_put_uint32be(buf->pos, frame->promised_stream_id); - - frame->padlen = 0; - frame->hd.length = nghttp2_bufs_len(bufs); - - return frame_pack_headers_shared(bufs, &frame->hd); -} - -int nghttp2_frame_unpack_push_promise_payload(nghttp2_push_promise *frame, - const uint8_t *payload, - size_t payloadlen _U_) { - frame->promised_stream_id = - nghttp2_get_uint32(payload) & NGHTTP2_STREAM_ID_MASK; - frame->nva = NULL; - frame->nvlen = 0; - return 0; -} - -int nghttp2_frame_pack_ping(nghttp2_bufs *bufs, nghttp2_ping *frame) { - nghttp2_buf *buf; - - assert(bufs->head == bufs->cur); - - buf = &bufs->head->buf; - - assert(nghttp2_buf_avail(buf) >= 8); - - buf->pos -= NGHTTP2_FRAME_HDLEN; - - nghttp2_frame_pack_frame_hd(buf->pos, &frame->hd); - - buf->last = - nghttp2_cpymem(buf->last, frame->opaque_data, sizeof(frame->opaque_data)); - - return 0; -} - -void nghttp2_frame_unpack_ping_payload(nghttp2_ping *frame, - const uint8_t *payload, - size_t payloadlen _U_) { - memcpy(frame->opaque_data, payload, sizeof(frame->opaque_data)); -} - -int nghttp2_frame_pack_goaway(nghttp2_bufs *bufs, nghttp2_goaway *frame) { - int rv; - nghttp2_buf *buf; - - assert(bufs->head == bufs->cur); - - buf = &bufs->head->buf; - - buf->pos -= NGHTTP2_FRAME_HDLEN; - - nghttp2_frame_pack_frame_hd(buf->pos, &frame->hd); - - nghttp2_put_uint32be(buf->last, frame->last_stream_id); - buf->last += 4; - - nghttp2_put_uint32be(buf->last, frame->error_code); - buf->last += 4; - - rv = nghttp2_bufs_add(bufs, frame->opaque_data, frame->opaque_data_len); - - if (rv == NGHTTP2_ERR_BUFFER_ERROR) { - return NGHTTP2_ERR_FRAME_SIZE_ERROR; - } - - if (rv != 0) { - return rv; - } - - return 0; -} - -void nghttp2_frame_unpack_goaway_payload(nghttp2_goaway *frame, - const uint8_t *payload, - size_t payloadlen _U_, - uint8_t *var_gift_payload, - size_t var_gift_payloadlen) { - frame->last_stream_id = nghttp2_get_uint32(payload) & NGHTTP2_STREAM_ID_MASK; - frame->error_code = nghttp2_get_uint32(payload + 4); - - frame->opaque_data = var_gift_payload; - frame->opaque_data_len = var_gift_payloadlen; -} - -int nghttp2_frame_unpack_goaway_payload2(nghttp2_goaway *frame, - const uint8_t *payload, - size_t payloadlen, nghttp2_mem *mem) { - uint8_t *var_gift_payload; - size_t var_gift_payloadlen; - - if (payloadlen > 8) { - var_gift_payloadlen = payloadlen - 8; - } else { - var_gift_payloadlen = 0; - } - - payloadlen -= var_gift_payloadlen; - - if (!var_gift_payloadlen) { - var_gift_payload = NULL; - } else { - var_gift_payload = nghttp2_mem_malloc(mem, var_gift_payloadlen); - - if (var_gift_payload == NULL) { - return NGHTTP2_ERR_NOMEM; - } - - memcpy(var_gift_payload, payload + 8, var_gift_payloadlen); - } - - nghttp2_frame_unpack_goaway_payload(frame, payload, payloadlen, - var_gift_payload, var_gift_payloadlen); - - return 0; -} - -int nghttp2_frame_pack_window_update(nghttp2_bufs *bufs, - nghttp2_window_update *frame) { - nghttp2_buf *buf; - - assert(bufs->head == bufs->cur); - - buf = &bufs->head->buf; - - assert(nghttp2_buf_avail(buf) >= 4); - - buf->pos -= NGHTTP2_FRAME_HDLEN; - - nghttp2_frame_pack_frame_hd(buf->pos, &frame->hd); - - nghttp2_put_uint32be(buf->last, frame->window_size_increment); - buf->last += 4; - - return 0; -} - -void nghttp2_frame_unpack_window_update_payload(nghttp2_window_update *frame, - const uint8_t *payload, - size_t payloadlen _U_) { - frame->window_size_increment = - nghttp2_get_uint32(payload) & NGHTTP2_WINDOW_SIZE_INCREMENT_MASK; -} - -nghttp2_settings_entry *nghttp2_frame_iv_copy(const nghttp2_settings_entry *iv, - size_t niv, nghttp2_mem *mem) { - nghttp2_settings_entry *iv_copy; - size_t len = niv * sizeof(nghttp2_settings_entry); - - if (len == 0) { - return NULL; - } - - iv_copy = nghttp2_mem_malloc(mem, len); - - if (iv_copy == NULL) { - return NULL; - } - - memcpy(iv_copy, iv, len); - - return iv_copy; -} - -int nghttp2_nv_equal(const nghttp2_nv *a, const nghttp2_nv *b) { - return a->namelen == b->namelen && a->valuelen == b->valuelen && - memcmp(a->name, b->name, a->namelen) == 0 && - memcmp(a->value, b->value, a->valuelen) == 0; -} - -void nghttp2_nv_array_del(nghttp2_nv *nva, nghttp2_mem *mem) { - nghttp2_mem_free(mem, nva); -} - -static int bytes_compar(const uint8_t *a, size_t alen, const uint8_t *b, - size_t blen) { - int rv; - - if (alen == blen) { - return memcmp(a, b, alen); - } - - if (alen < blen) { - rv = memcmp(a, b, alen); - - if (rv == 0) { - return -1; - } - - return rv; - } - - rv = memcmp(a, b, blen); - - if (rv == 0) { - return 1; - } - - return rv; -} - -int nghttp2_nv_compare_name(const nghttp2_nv *lhs, const nghttp2_nv *rhs) { - return bytes_compar(lhs->name, lhs->namelen, rhs->name, rhs->namelen); -} - -static int nv_compar(const void *lhs, const void *rhs) { - const nghttp2_nv *a = (const nghttp2_nv *)lhs; - const nghttp2_nv *b = (const nghttp2_nv *)rhs; - int rv; - - rv = bytes_compar(a->name, a->namelen, b->name, b->namelen); - - if (rv == 0) { - return bytes_compar(a->value, a->valuelen, b->value, b->valuelen); - } - - return rv; -} - -void nghttp2_nv_array_sort(nghttp2_nv *nva, size_t nvlen) { - qsort(nva, nvlen, sizeof(nghttp2_nv), nv_compar); -} - -int nghttp2_nv_array_copy(nghttp2_nv **nva_ptr, const nghttp2_nv *nva, - size_t nvlen, nghttp2_mem *mem) { - size_t i; - uint8_t *data; - size_t buflen = 0; - nghttp2_nv *p; - - for (i = 0; i < nvlen; ++i) { - /* + 2 for null-termination */ - buflen += nva[i].namelen + nva[i].valuelen + 2; - } - - if (nvlen == 0) { - *nva_ptr = NULL; - - return 0; - } - - buflen += sizeof(nghttp2_nv) * nvlen; - - *nva_ptr = nghttp2_mem_malloc(mem, buflen); - - if (*nva_ptr == NULL) { - return NGHTTP2_ERR_NOMEM; - } - - p = *nva_ptr; - data = (uint8_t *)(*nva_ptr) + sizeof(nghttp2_nv) * nvlen; - - for (i = 0; i < nvlen; ++i) { - p->flags = nva[i].flags; - - memcpy(data, nva[i].name, nva[i].namelen); - p->name = data; - p->namelen = nva[i].namelen; - data[p->namelen] = '\0'; - nghttp2_downcase(p->name, p->namelen); - data += nva[i].namelen + 1; - memcpy(data, nva[i].value, nva[i].valuelen); - p->value = data; - p->valuelen = nva[i].valuelen; - data[p->valuelen] = '\0'; - data += nva[i].valuelen + 1; - ++p; - } - return 0; -} - -int nghttp2_iv_check(const nghttp2_settings_entry *iv, size_t niv) { - size_t i; - for (i = 0; i < niv; ++i) { - switch (iv[i].settings_id) { - case NGHTTP2_SETTINGS_HEADER_TABLE_SIZE: - if (iv[i].value > NGHTTP2_MAX_HEADER_TABLE_SIZE) { - return 0; - } - break; - case NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS: - break; - case NGHTTP2_SETTINGS_ENABLE_PUSH: - if (iv[i].value != 0 && iv[i].value != 1) { - return 0; - } - break; - case NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE: - if (iv[i].value > (uint32_t)NGHTTP2_MAX_WINDOW_SIZE) { - return 0; - } - break; - case NGHTTP2_SETTINGS_MAX_FRAME_SIZE: - if (iv[i].value < NGHTTP2_MAX_FRAME_SIZE_MIN || - iv[i].value > NGHTTP2_MAX_FRAME_SIZE_MAX) { - return 0; - } - break; - case NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE: - break; - } - } - return 1; -} - -static void frame_set_pad(nghttp2_buf *buf, size_t padlen, int framehd_only) { - size_t trail_padlen; - size_t newlen; - - DEBUGF(fprintf(stderr, "send: padlen=%zu, shift left 1 bytes\n", padlen)); - - memmove(buf->pos - 1, buf->pos, NGHTTP2_FRAME_HDLEN); - - --buf->pos; - - buf->pos[4] |= NGHTTP2_FLAG_PADDED; - - newlen = (nghttp2_get_uint32(buf->pos) >> 8) + padlen; - nghttp2_put_uint32be(buf->pos, (uint32_t)((newlen << 8) + buf->pos[3])); - - if (framehd_only) { - return; - } - - trail_padlen = padlen - 1; - buf->pos[NGHTTP2_FRAME_HDLEN] = trail_padlen; - - /* zero out padding */ - memset(buf->last, 0, trail_padlen); - /* extend buffers trail_padlen bytes, since we ate previous padlen - - trail_padlen byte(s) */ - buf->last += trail_padlen; -} - -int nghttp2_frame_add_pad(nghttp2_bufs *bufs, nghttp2_frame_hd *hd, - size_t padlen, int framehd_only) { - nghttp2_buf *buf; - - if (padlen == 0) { - DEBUGF(fprintf(stderr, "send: padlen = 0, nothing to do\n")); - - return 0; - } - - /* - * We have arranged bufs like this: - * - * 0 1 2 3 - * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | |Frame header | Frame payload... : - * +-+-----------------+-------------------------------------------+ - * | |Frame header | Frame payload... : - * +-+-----------------+-------------------------------------------+ - * | |Frame header | Frame payload... : - * +-+-----------------+-------------------------------------------+ - * - * We arranged padding so that it is included in the first frame - * completely. For padded frame, we are going to adjust buf->pos of - * frame which includes padding and serialize (memmove) frame header - * in the correct position. Also extends buf->last to include - * padding. - */ - - buf = &bufs->head->buf; - - assert(nghttp2_buf_avail(buf) >= (ssize_t)padlen - 1); - - frame_set_pad(buf, padlen, framehd_only); - - hd->length += padlen; - hd->flags |= NGHTTP2_FLAG_PADDED; - - DEBUGF(fprintf(stderr, "send: final payloadlen=%zu, padlen=%zu\n", hd->length, - padlen)); - - return 0; -} diff --git a/lib/nghttp2_frame.h b/lib/nghttp2_frame.h deleted file mode 100644 index 7fc336c..0000000 --- a/lib/nghttp2_frame.h +++ /dev/null @@ -1,527 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2012 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#ifndef NGHTTP2_FRAME_H -#define NGHTTP2_FRAME_H - -#ifdef HAVE_CONFIG_H -#include -#endif /* HAVE_CONFIG_H */ - -#include -#include "nghttp2_hd.h" -#include "nghttp2_buf.h" - -#define NGHTTP2_STREAM_ID_MASK ((1u << 31) - 1) -#define NGHTTP2_PRI_GROUP_ID_MASK ((1u << 31) - 1) -#define NGHTTP2_PRIORITY_MASK ((1u << 31) - 1) -#define NGHTTP2_WINDOW_SIZE_INCREMENT_MASK ((1u << 31) - 1) -#define NGHTTP2_SETTINGS_ID_MASK ((1 << 24) - 1) - -/* The number of bytes of frame header. */ -#define NGHTTP2_FRAME_HDLEN 9 - -#define NGHTTP2_MAX_FRAME_SIZE_MAX ((1 << 24) - 1) -#define NGHTTP2_MAX_FRAME_SIZE_MIN (1 << 14) - -#define NGHTTP2_MAX_PAYLOADLEN 16384 -/* The one frame buffer length for tranmission. We may use several of - them to support CONTINUATION. To account for Pad Length field, we - allocate extra 1 byte, which saves extra large memcopying. */ -#define NGHTTP2_FRAMEBUF_CHUNKLEN \ - (NGHTTP2_FRAME_HDLEN + 1 + NGHTTP2_MAX_PAYLOADLEN) - -/* Number of inbound buffer */ -#define NGHTTP2_FRAMEBUF_MAX_NUM 5 - -/* The default length of DATA frame payload. */ -#define NGHTTP2_DATA_PAYLOADLEN NGHTTP2_MAX_FRAME_SIZE_MIN - -/* Maximum headers payload length, calculated in compressed form. - This applies to transmission only. */ -#define NGHTTP2_MAX_HEADERSLEN 65536 - -/* The number of bytes for each SETTINGS entry */ -#define NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH 6 - -/* The maximum header table size in SETTINGS_HEADER_TABLE_SIZE */ -#define NGHTTP2_MAX_HEADER_TABLE_SIZE ((1u << 31) - 1) - -/* Length of priority related fields in HEADERS/PRIORITY frames */ -#define NGHTTP2_PRIORITY_SPECLEN 5 - -/* Maximum length of padding in bytes. */ -#define NGHTTP2_MAX_PADLEN 256 - -/* Union of extension frame payload */ -typedef union { int dummy; } nghttp2_ext_frame_payload; - -void nghttp2_frame_pack_frame_hd(uint8_t *buf, const nghttp2_frame_hd *hd); - -void nghttp2_frame_unpack_frame_hd(nghttp2_frame_hd *hd, const uint8_t *buf); - -/** - * Initializes frame header |hd| with given parameters. Reserved bit - * is set to 0. - */ -void nghttp2_frame_hd_init(nghttp2_frame_hd *hd, size_t length, uint8_t type, - uint8_t flags, int32_t stream_id); - -/** - * Returns the number of priority field depending on the |flags|. If - * |flags| has neither NGHTTP2_FLAG_PRIORITY_GROUP nor - * NGHTTP2_FLAG_PRIORITY_DEPENDENCY set, return 0. - */ -size_t nghttp2_frame_priority_len(uint8_t flags); - -/** - * Packs the |pri_spec| in |buf|. This function assumes |buf| has - * enough space for serialization. - */ -void nghttp2_frame_pack_priority_spec(uint8_t *buf, - const nghttp2_priority_spec *pri_spec); - -/** - * Unpacks the priority specification from payload |payload| of length - * |payloadlen| to |pri_spec|. The |flags| is used to determine what - * kind of priority specification is in |payload|. This function - * assumes the |payload| contains whole priority specification. - */ -void nghttp2_frame_unpack_priority_spec(nghttp2_priority_spec *pri_spec, - uint8_t flags, const uint8_t *payload, - size_t payloadlen); - -/* - * Returns the offset from the HEADERS frame payload where the - * compressed header block starts. The frame payload does not include - * frame header. - */ -size_t nghttp2_frame_headers_payload_nv_offset(nghttp2_headers *frame); - -/* - * Packs HEADERS frame |frame| in wire format and store it in |bufs|. - * This function expands |bufs| as necessary to store frame. - * - * The caller must make sure that nghttp2_bufs_reset(bufs) is called - * before calling this function. - * - * frame->hd.length is assigned after length is determined during - * packing process. CONTINUATION frames are also serialized in this - * function. This function does not handle padding. - * - * This function returns 0 if it succeeds, or returns one of the - * following negative error codes: - * - * NGHTTP2_ERR_HEADER_COMP - * The deflate operation failed. - * NGHTTP2_ERR_NOMEM - * Out of memory. - */ -int nghttp2_frame_pack_headers(nghttp2_bufs *bufs, nghttp2_headers *frame, - nghttp2_hd_deflater *deflater); - -/* - * Unpacks HEADERS frame byte sequence into |frame|. This function - * only unapcks bytes that come before name/value header block and - * after possible Pad Length field. - * - * This function always succeeds and returns 0. - */ -int nghttp2_frame_unpack_headers_payload(nghttp2_headers *frame, - const uint8_t *payload, - size_t payloadlen); - -/* - * Packs PRIORITY frame |frame| in wire format and store it in - * |bufs|. - * - * The caller must make sure that nghttp2_bufs_reset(bufs) is called - * before calling this function. - * - * This function always succeeds and returns 0. - */ -int nghttp2_frame_pack_priority(nghttp2_bufs *bufs, nghttp2_priority *frame); - -/* - * Unpacks PRIORITY wire format into |frame|. - */ -void nghttp2_frame_unpack_priority_payload(nghttp2_priority *frame, - const uint8_t *payload, - size_t payloadlen); - -/* - * Packs RST_STREAM frame |frame| in wire frame format and store it in - * |bufs|. - * - * The caller must make sure that nghttp2_bufs_reset(bufs) is called - * before calling this function. - * - * This function always succeeds and returns 0. - */ -int nghttp2_frame_pack_rst_stream(nghttp2_bufs *bufs, - nghttp2_rst_stream *frame); - -/* - * Unpacks RST_STREAM frame byte sequence into |frame|. - */ -void nghttp2_frame_unpack_rst_stream_payload(nghttp2_rst_stream *frame, - const uint8_t *payload, - size_t payloadlen); - -/* - * Packs SETTINGS frame |frame| in wire format and store it in - * |bufs|. - * - * The caller must make sure that nghttp2_bufs_reset(bufs) is called - * before calling this function. - * - * This function returns 0 if it succeeds, or returns one of the - * following negative error codes: - * - * NGHTTP2_ERR_FRAME_SIZE_ERROR - * The length of the frame is too large. - */ -int nghttp2_frame_pack_settings(nghttp2_bufs *bufs, nghttp2_settings *frame); - -/* - * Packs the |iv|, which includes |niv| entries, in the |buf|, - * assuming the |buf| has at least 8 * |niv| bytes. - * - * Returns the number of bytes written into the |buf|. - */ -size_t nghttp2_frame_pack_settings_payload(uint8_t *buf, - const nghttp2_settings_entry *iv, - size_t niv); - -void nghttp2_frame_unpack_settings_entry(nghttp2_settings_entry *iv, - const uint8_t *payload); - -/* - * Makes a copy of |iv| in frame->settings.iv. The |niv| is assigned - * to frame->settings.niv. - * - * This function returns 0 if it succeeds or one of the following - * negative error codes: - * - * NGHTTP2_ERR_NOMEM - * Out of memory. - */ -int nghttp2_frame_unpack_settings_payload(nghttp2_settings *frame, - nghttp2_settings_entry *iv, - size_t niv, nghttp2_mem *mem); - -/* - * Unpacks SETTINGS payload into |*iv_ptr|. The number of entries are - * assigned to the |*niv_ptr|. This function allocates enough memory - * to store the result in |*iv_ptr|. The caller is responsible to free - * |*iv_ptr| after its use. - * - * This function returns 0 if it succeeds or one of the following - * negative error codes: - * - * NGHTTP2_ERR_NOMEM - * Out of memory. - */ -int nghttp2_frame_unpack_settings_payload2(nghttp2_settings_entry **iv_ptr, - size_t *niv_ptr, - const uint8_t *payload, - size_t payloadlen, nghttp2_mem *mem); - -/* - * Packs PUSH_PROMISE frame |frame| in wire format and store it in - * |bufs|. This function expands |bufs| as necessary to store - * frame. - * - * The caller must make sure that nghttp2_bufs_reset(bufs) is called - * before calling this function. - * - * frame->hd.length is assigned after length is determined during - * packing process. CONTINUATION frames are also serialized in this - * function. This function does not handle padding. - * - * This function returns 0 if it succeeds, or returns one of the - * following negative error codes: - * - * NGHTTP2_ERR_HEADER_COMP - * The deflate operation failed. - * NGHTTP2_ERR_NOMEM - * Out of memory. - */ -int nghttp2_frame_pack_push_promise(nghttp2_bufs *bufs, - nghttp2_push_promise *frame, - nghttp2_hd_deflater *deflater); - -/* - * Unpacks PUSH_PROMISE frame byte sequence into |frame|. This - * function only unapcks bytes that come before name/value header - * block and after possible Pad Length field. - * - * This function returns 0 if it succeeds or one of the following - * negative error codes: - * - * NGHTTP2_ERR_PROTO - * TODO END_HEADERS flag is not set - */ -int nghttp2_frame_unpack_push_promise_payload(nghttp2_push_promise *frame, - const uint8_t *payload, - size_t payloadlen); - -/* - * Packs PING frame |frame| in wire format and store it in - * |bufs|. - * - * The caller must make sure that nghttp2_bufs_reset(bufs) is called - * before calling this function. - * - * This function always succeeds and returns 0. - */ -int nghttp2_frame_pack_ping(nghttp2_bufs *bufs, nghttp2_ping *frame); - -/* - * Unpacks PING wire format into |frame|. - */ -void nghttp2_frame_unpack_ping_payload(nghttp2_ping *frame, - const uint8_t *payload, - size_t payloadlen); - -/* - * Packs GOAWAY frame |frame| in wire format and store it in |bufs|. - * This function expands |bufs| as necessary to store frame. - * - * The caller must make sure that nghttp2_bufs_reset(bufs) is called - * before calling this function. - * - * This function returns 0 if it succeeds or one of the following - * negative error codes: - * - * NGHTTP2_ERR_NOMEM - * Out of memory. - * NGHTTP2_ERR_FRAME_SIZE_ERROR - * The length of the frame is too large. - */ -int nghttp2_frame_pack_goaway(nghttp2_bufs *bufs, nghttp2_goaway *frame); - -/* - * Unpacks GOAWAY wire format into |frame|. The |payload| of length - * |payloadlen| contains first 8 bytes of payload. The - * |var_gift_payload| of length |var_gift_payloadlen| contains - * remaining payload and its buffer is gifted to the function and then - * |frame|. The |var_gift_payloadlen| must be freed by - * nghttp2_frame_goaway_free(). - */ -void nghttp2_frame_unpack_goaway_payload(nghttp2_goaway *frame, - const uint8_t *payload, - size_t payloadlen, - uint8_t *var_gift_payload, - size_t var_gift_payloadlen); - -/* - * Unpacks GOAWAY wire format into |frame|. This function only exists - * for unit test. After allocating buffer for debug data, this - * function internally calls nghttp2_frame_unpack_goaway_payload(). - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP2_ERR_NOMEM - * Out of memory. - */ -int nghttp2_frame_unpack_goaway_payload2(nghttp2_goaway *frame, - const uint8_t *payload, - size_t payloadlen, nghttp2_mem *mem); - -/* - * Packs WINDOW_UPDATE frame |frame| in wire frame format and store it - * in |bufs|. - * - * The caller must make sure that nghttp2_bufs_reset(bufs) is called - * before calling this function. - * - * This function always succeeds and returns 0. - */ -int nghttp2_frame_pack_window_update(nghttp2_bufs *bufs, - nghttp2_window_update *frame); - -/* - * Unpacks WINDOW_UPDATE frame byte sequence into |frame|. - */ -void nghttp2_frame_unpack_window_update_payload(nghttp2_window_update *frame, - const uint8_t *payload, - size_t payloadlen); - -/* - * Initializes HEADERS frame |frame| with given values. |frame| takes - * ownership of |nva|, so caller must not free it. If |stream_id| is - * not assigned yet, it must be -1. - */ -void nghttp2_frame_headers_init(nghttp2_headers *frame, uint8_t flags, - int32_t stream_id, nghttp2_headers_category cat, - const nghttp2_priority_spec *pri_spec, - nghttp2_nv *nva, size_t nvlen); - -void nghttp2_frame_headers_free(nghttp2_headers *frame, nghttp2_mem *mem); - -void nghttp2_frame_priority_init(nghttp2_priority *frame, int32_t stream_id, - const nghttp2_priority_spec *pri_spec); - -void nghttp2_frame_priority_free(nghttp2_priority *frame); - -void nghttp2_frame_rst_stream_init(nghttp2_rst_stream *frame, int32_t stream_id, - uint32_t error_code); - -void nghttp2_frame_rst_stream_free(nghttp2_rst_stream *frame); - -/* - * Initializes PUSH_PROMISE frame |frame| with given values. |frame| - * takes ownership of |nva|, so caller must not free it. - */ -void nghttp2_frame_push_promise_init(nghttp2_push_promise *frame, uint8_t flags, - int32_t stream_id, - int32_t promised_stream_id, - nghttp2_nv *nva, size_t nvlen); - -void nghttp2_frame_push_promise_free(nghttp2_push_promise *frame, - nghttp2_mem *mem); - -/* - * Initializes SETTINGS frame |frame| with given values. |frame| takes - * ownership of |iv|, so caller must not free it. The |flags| are - * bitwise-OR of one or more of nghttp2_settings_flag. - */ -void nghttp2_frame_settings_init(nghttp2_settings *frame, uint8_t flags, - nghttp2_settings_entry *iv, size_t niv); - -void nghttp2_frame_settings_free(nghttp2_settings *frame, nghttp2_mem *mem); - -/* - * Initializes PING frame |frame| with given values. If the - * |opqeue_data| is not NULL, it must point to 8 bytes memory region - * of data. The data pointed by |opaque_data| is copied. It can be - * NULL. In this case, 8 bytes NULL is used. - */ -void nghttp2_frame_ping_init(nghttp2_ping *frame, uint8_t flags, - const uint8_t *opque_data); - -void nghttp2_frame_ping_free(nghttp2_ping *frame); - -/* - * Initializes GOAWAY frame |frame| with given values. On success, - * this function takes ownership of |opaque_data|, so caller must not - * free it. If the |opaque_data_len| is 0, opaque_data could be NULL. - */ -void nghttp2_frame_goaway_init(nghttp2_goaway *frame, int32_t last_stream_id, - uint32_t error_code, uint8_t *opaque_data, - size_t opaque_data_len); - -void nghttp2_frame_goaway_free(nghttp2_goaway *frame, nghttp2_mem *mem); - -void nghttp2_frame_window_update_init(nghttp2_window_update *frame, - uint8_t flags, int32_t stream_id, - int32_t window_size_increment); - -void nghttp2_frame_window_update_free(nghttp2_window_update *frame); - -/* - * Returns the number of padding bytes after payload. The total - * padding length is given in the |padlen|. The returned value does - * not include the Pad Length field. - */ -size_t nghttp2_frame_trail_padlen(nghttp2_frame *frame, size_t padlen); - -void nghttp2_frame_data_init(nghttp2_data *frame, uint8_t flags, - int32_t stream_id); - -void nghttp2_frame_data_free(nghttp2_data *frame); - -/* - * Makes copy of |iv| and return the copy. The |niv| is the number of - * entries in |iv|. This function returns the pointer to the copy if - * it succeeds, or NULL. - */ -nghttp2_settings_entry *nghttp2_frame_iv_copy(const nghttp2_settings_entry *iv, - size_t niv, nghttp2_mem *mem); - -/* - * Sorts the |nva| in ascending order of name and value. If names are - * equivalent, sort them by value. - */ -void nghttp2_nv_array_sort(nghttp2_nv *nva, size_t nvlen); - -/* - * Copies name/value pairs from |nva|, which contains |nvlen| pairs, - * to |*nva_ptr|, which is dynamically allocated so that all items can - * be stored. The resultant name and value in nghttp2_nv are - * guaranteed to be NULL-terminated even if the input is not - * null-terminated. - * - * The |*nva_ptr| must be freed using nghttp2_nv_array_del(). - * - * This function returns 0 if it succeeds or one of the following - * negative error codes: - * - * NGHTTP2_ERR_NOMEM - * Out of memory. - */ -int nghttp2_nv_array_copy(nghttp2_nv **nva_ptr, const nghttp2_nv *nva, - size_t nvlen, nghttp2_mem *mem); - -/* - * Returns nonzero if the name/value pair |a| equals to |b|. The name - * is compared in case-sensitive, because we ensure that this function - * is called after the name is lower-cased. - */ -int nghttp2_nv_equal(const nghttp2_nv *a, const nghttp2_nv *b); - -/* - * Frees |nva|. - */ -void nghttp2_nv_array_del(nghttp2_nv *nva, nghttp2_mem *mem); - -/* - * Checks that the |iv|, which includes |niv| entries, does not have - * invalid values. - * - * This function returns nonzero if it succeeds, or 0. - */ -int nghttp2_iv_check(const nghttp2_settings_entry *iv, size_t niv); - -/* - * Sets Pad Length field and flags and adjusts frame header position - * of each buffers in |bufs|. The number of padding is given in the - * |padlen| including Pad Length field. The |hd| is the frame header - * for the serialized data. This function fills zeros padding region - * unless framehd_only is nonzero. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP2_ERR_NOMEM - * Out of memory. - * NGHTTP2_ERR_FRAME_SIZE_ERROR - * The length of the resulting frame is too large. - */ -int nghttp2_frame_add_pad(nghttp2_bufs *bufs, nghttp2_frame_hd *hd, - size_t padlen, int framehd_only); - -#endif /* NGHTTP2_FRAME_H */ diff --git a/lib/nghttp2_hd.c b/lib/nghttp2_hd.c deleted file mode 100644 index f9444e0..0000000 --- a/lib/nghttp2_hd.c +++ /dev/null @@ -1,2343 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2013 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#include "nghttp2_hd.h" - -#include -#include -#include - -#include "nghttp2_helper.h" -#include "nghttp2_int.h" - -/* Make scalar initialization form of nghttp2_hd_entry */ -#define MAKE_STATIC_ENT(N, V, T) \ - { \ - { (uint8_t *)(N), (uint8_t *)(V), sizeof((N)) - 1, sizeof((V)) - 1, 0 } \ - , (T), 1, NGHTTP2_HD_FLAG_NONE \ - } - -/* Generated by mkstatictbl.py */ -/* 3rd parameter is nghttp2_token value for header field name. We use - first enum value if same header names are repeated (e.g., - :status). */ -static nghttp2_hd_entry static_table[] = { - MAKE_STATIC_ENT(":authority", "", 0), - MAKE_STATIC_ENT(":method", "GET", 1), - MAKE_STATIC_ENT(":method", "POST", 1), - MAKE_STATIC_ENT(":path", "/", 3), - MAKE_STATIC_ENT(":path", "/index.html", 3), - MAKE_STATIC_ENT(":scheme", "http", 5), - MAKE_STATIC_ENT(":scheme", "https", 5), - MAKE_STATIC_ENT(":status", "200", 7), - MAKE_STATIC_ENT(":status", "204", 7), - MAKE_STATIC_ENT(":status", "206", 7), - MAKE_STATIC_ENT(":status", "304", 7), - MAKE_STATIC_ENT(":status", "400", 7), - MAKE_STATIC_ENT(":status", "404", 7), - MAKE_STATIC_ENT(":status", "500", 7), - MAKE_STATIC_ENT("accept-charset", "", 14), - MAKE_STATIC_ENT("accept-encoding", "gzip, deflate", 15), - MAKE_STATIC_ENT("accept-language", "", 16), - MAKE_STATIC_ENT("accept-ranges", "", 17), - MAKE_STATIC_ENT("accept", "", 18), - MAKE_STATIC_ENT("access-control-allow-origin", "", 19), - MAKE_STATIC_ENT("age", "", 20), - MAKE_STATIC_ENT("allow", "", 21), - MAKE_STATIC_ENT("authorization", "", 22), - MAKE_STATIC_ENT("cache-control", "", 23), - MAKE_STATIC_ENT("content-disposition", "", 24), - MAKE_STATIC_ENT("content-encoding", "", 25), - MAKE_STATIC_ENT("content-language", "", 26), - MAKE_STATIC_ENT("content-length", "", 27), - MAKE_STATIC_ENT("content-location", "", 28), - MAKE_STATIC_ENT("content-range", "", 29), - MAKE_STATIC_ENT("content-type", "", 30), - MAKE_STATIC_ENT("cookie", "", 31), - MAKE_STATIC_ENT("date", "", 32), - MAKE_STATIC_ENT("etag", "", 33), - MAKE_STATIC_ENT("expect", "", 34), - MAKE_STATIC_ENT("expires", "", 35), - MAKE_STATIC_ENT("from", "", 36), - MAKE_STATIC_ENT("host", "", 37), - MAKE_STATIC_ENT("if-match", "", 38), - MAKE_STATIC_ENT("if-modified-since", "", 39), - MAKE_STATIC_ENT("if-none-match", "", 40), - MAKE_STATIC_ENT("if-range", "", 41), - MAKE_STATIC_ENT("if-unmodified-since", "", 42), - MAKE_STATIC_ENT("last-modified", "", 43), - MAKE_STATIC_ENT("link", "", 44), - MAKE_STATIC_ENT("location", "", 45), - MAKE_STATIC_ENT("max-forwards", "", 46), - MAKE_STATIC_ENT("proxy-authenticate", "", 47), - MAKE_STATIC_ENT("proxy-authorization", "", 48), - MAKE_STATIC_ENT("range", "", 49), - MAKE_STATIC_ENT("referer", "", 50), - MAKE_STATIC_ENT("refresh", "", 51), - MAKE_STATIC_ENT("retry-after", "", 52), - MAKE_STATIC_ENT("server", "", 53), - MAKE_STATIC_ENT("set-cookie", "", 54), - MAKE_STATIC_ENT("strict-transport-security", "", 55), - MAKE_STATIC_ENT("transfer-encoding", "", 56), - MAKE_STATIC_ENT("user-agent", "", 57), - MAKE_STATIC_ENT("vary", "", 58), - MAKE_STATIC_ENT("via", "", 59), - MAKE_STATIC_ENT("www-authenticate", "", 60), -}; - -static int memeq(const void *s1, const void *s2, size_t n) { - return memcmp(s1, s2, n) == 0; -} - -/* - * This function was generated by genlibtokenlookup.py. Inspired by - * h2o header lookup. https://github.com/h2o/h2o - */ -static int lookup_token(const uint8_t *name, size_t namelen) { - switch (namelen) { - case 2: - switch (name[1]) { - case 'e': - if (lstreq("t", name, 1)) { - return NGHTTP2_TOKEN_TE; - } - break; - } - break; - case 3: - switch (name[2]) { - case 'a': - if (lstreq("vi", name, 2)) { - return NGHTTP2_TOKEN_VIA; - } - break; - case 'e': - if (lstreq("ag", name, 2)) { - return NGHTTP2_TOKEN_AGE; - } - break; - } - break; - case 4: - switch (name[3]) { - case 'e': - if (lstreq("dat", name, 3)) { - return NGHTTP2_TOKEN_DATE; - } - break; - case 'g': - if (lstreq("eta", name, 3)) { - return NGHTTP2_TOKEN_ETAG; - } - break; - case 'k': - if (lstreq("lin", name, 3)) { - return NGHTTP2_TOKEN_LINK; - } - break; - case 'm': - if (lstreq("fro", name, 3)) { - return NGHTTP2_TOKEN_FROM; - } - break; - case 't': - if (lstreq("hos", name, 3)) { - return NGHTTP2_TOKEN_HOST; - } - break; - case 'y': - if (lstreq("var", name, 3)) { - return NGHTTP2_TOKEN_VARY; - } - break; - } - break; - case 5: - switch (name[4]) { - case 'e': - if (lstreq("rang", name, 4)) { - return NGHTTP2_TOKEN_RANGE; - } - break; - case 'h': - if (lstreq(":pat", name, 4)) { - return NGHTTP2_TOKEN__PATH; - } - if (lstreq(":pat", name, 4)) { - return NGHTTP2_TOKEN__PATH; - } - break; - case 'w': - if (lstreq("allo", name, 4)) { - return NGHTTP2_TOKEN_ALLOW; - } - break; - } - break; - case 6: - switch (name[5]) { - case 'e': - if (lstreq("cooki", name, 5)) { - return NGHTTP2_TOKEN_COOKIE; - } - break; - case 'r': - if (lstreq("serve", name, 5)) { - return NGHTTP2_TOKEN_SERVER; - } - break; - case 't': - if (lstreq("accep", name, 5)) { - return NGHTTP2_TOKEN_ACCEPT; - } - if (lstreq("expec", name, 5)) { - return NGHTTP2_TOKEN_EXPECT; - } - break; - } - break; - case 7: - switch (name[6]) { - case 'd': - if (lstreq(":metho", name, 6)) { - return NGHTTP2_TOKEN__METHOD; - } - if (lstreq(":metho", name, 6)) { - return NGHTTP2_TOKEN__METHOD; - } - break; - case 'e': - if (lstreq(":schem", name, 6)) { - return NGHTTP2_TOKEN__SCHEME; - } - if (lstreq(":schem", name, 6)) { - return NGHTTP2_TOKEN__SCHEME; - } - if (lstreq("upgrad", name, 6)) { - return NGHTTP2_TOKEN_UPGRADE; - } - break; - case 'h': - if (lstreq("refres", name, 6)) { - return NGHTTP2_TOKEN_REFRESH; - } - break; - case 'r': - if (lstreq("refere", name, 6)) { - return NGHTTP2_TOKEN_REFERER; - } - break; - case 's': - if (lstreq(":statu", name, 6)) { - return NGHTTP2_TOKEN__STATUS; - } - if (lstreq(":statu", name, 6)) { - return NGHTTP2_TOKEN__STATUS; - } - if (lstreq(":statu", name, 6)) { - return NGHTTP2_TOKEN__STATUS; - } - if (lstreq(":statu", name, 6)) { - return NGHTTP2_TOKEN__STATUS; - } - if (lstreq(":statu", name, 6)) { - return NGHTTP2_TOKEN__STATUS; - } - if (lstreq(":statu", name, 6)) { - return NGHTTP2_TOKEN__STATUS; - } - if (lstreq(":statu", name, 6)) { - return NGHTTP2_TOKEN__STATUS; - } - if (lstreq("expire", name, 6)) { - return NGHTTP2_TOKEN_EXPIRES; - } - break; - } - break; - case 8: - switch (name[7]) { - case 'e': - if (lstreq("if-rang", name, 7)) { - return NGHTTP2_TOKEN_IF_RANGE; - } - break; - case 'h': - if (lstreq("if-matc", name, 7)) { - return NGHTTP2_TOKEN_IF_MATCH; - } - break; - case 'n': - if (lstreq("locatio", name, 7)) { - return NGHTTP2_TOKEN_LOCATION; - } - break; - } - break; - case 10: - switch (name[9]) { - case 'e': - if (lstreq("keep-aliv", name, 9)) { - return NGHTTP2_TOKEN_KEEP_ALIVE; - } - if (lstreq("set-cooki", name, 9)) { - return NGHTTP2_TOKEN_SET_COOKIE; - } - break; - case 'n': - if (lstreq("connectio", name, 9)) { - return NGHTTP2_TOKEN_CONNECTION; - } - break; - case 't': - if (lstreq("user-agen", name, 9)) { - return NGHTTP2_TOKEN_USER_AGENT; - } - break; - case 'y': - if (lstreq(":authorit", name, 9)) { - return NGHTTP2_TOKEN__AUTHORITY; - } - break; - } - break; - case 11: - switch (name[10]) { - case 'r': - if (lstreq("retry-afte", name, 10)) { - return NGHTTP2_TOKEN_RETRY_AFTER; - } - break; - } - break; - case 12: - switch (name[11]) { - case 'e': - if (lstreq("content-typ", name, 11)) { - return NGHTTP2_TOKEN_CONTENT_TYPE; - } - break; - case 's': - if (lstreq("max-forward", name, 11)) { - return NGHTTP2_TOKEN_MAX_FORWARDS; - } - break; - } - break; - case 13: - switch (name[12]) { - case 'd': - if (lstreq("last-modifie", name, 12)) { - return NGHTTP2_TOKEN_LAST_MODIFIED; - } - break; - case 'e': - if (lstreq("content-rang", name, 12)) { - return NGHTTP2_TOKEN_CONTENT_RANGE; - } - break; - case 'h': - if (lstreq("if-none-matc", name, 12)) { - return NGHTTP2_TOKEN_IF_NONE_MATCH; - } - break; - case 'l': - if (lstreq("cache-contro", name, 12)) { - return NGHTTP2_TOKEN_CACHE_CONTROL; - } - break; - case 'n': - if (lstreq("authorizatio", name, 12)) { - return NGHTTP2_TOKEN_AUTHORIZATION; - } - break; - case 's': - if (lstreq("accept-range", name, 12)) { - return NGHTTP2_TOKEN_ACCEPT_RANGES; - } - break; - } - break; - case 14: - switch (name[13]) { - case 'h': - if (lstreq("content-lengt", name, 13)) { - return NGHTTP2_TOKEN_CONTENT_LENGTH; - } - break; - case 't': - if (lstreq("accept-charse", name, 13)) { - return NGHTTP2_TOKEN_ACCEPT_CHARSET; - } - break; - } - break; - case 15: - switch (name[14]) { - case 'e': - if (lstreq("accept-languag", name, 14)) { - return NGHTTP2_TOKEN_ACCEPT_LANGUAGE; - } - break; - case 'g': - if (lstreq("accept-encodin", name, 14)) { - return NGHTTP2_TOKEN_ACCEPT_ENCODING; - } - break; - } - break; - case 16: - switch (name[15]) { - case 'e': - if (lstreq("content-languag", name, 15)) { - return NGHTTP2_TOKEN_CONTENT_LANGUAGE; - } - if (lstreq("www-authenticat", name, 15)) { - return NGHTTP2_TOKEN_WWW_AUTHENTICATE; - } - break; - case 'g': - if (lstreq("content-encodin", name, 15)) { - return NGHTTP2_TOKEN_CONTENT_ENCODING; - } - break; - case 'n': - if (lstreq("content-locatio", name, 15)) { - return NGHTTP2_TOKEN_CONTENT_LOCATION; - } - if (lstreq("proxy-connectio", name, 15)) { - return NGHTTP2_TOKEN_PROXY_CONNECTION; - } - break; - } - break; - case 17: - switch (name[16]) { - case 'e': - if (lstreq("if-modified-sinc", name, 16)) { - return NGHTTP2_TOKEN_IF_MODIFIED_SINCE; - } - break; - case 'g': - if (lstreq("transfer-encodin", name, 16)) { - return NGHTTP2_TOKEN_TRANSFER_ENCODING; - } - break; - } - break; - case 18: - switch (name[17]) { - case 'e': - if (lstreq("proxy-authenticat", name, 17)) { - return NGHTTP2_TOKEN_PROXY_AUTHENTICATE; - } - break; - } - break; - case 19: - switch (name[18]) { - case 'e': - if (lstreq("if-unmodified-sinc", name, 18)) { - return NGHTTP2_TOKEN_IF_UNMODIFIED_SINCE; - } - break; - case 'n': - if (lstreq("content-dispositio", name, 18)) { - return NGHTTP2_TOKEN_CONTENT_DISPOSITION; - } - if (lstreq("proxy-authorizatio", name, 18)) { - return NGHTTP2_TOKEN_PROXY_AUTHORIZATION; - } - break; - } - break; - case 25: - switch (name[24]) { - case 'y': - if (lstreq("strict-transport-securit", name, 24)) { - return NGHTTP2_TOKEN_STRICT_TRANSPORT_SECURITY; - } - break; - } - break; - case 27: - switch (name[26]) { - case 'n': - if (lstreq("access-control-allow-origi", name, 26)) { - return NGHTTP2_TOKEN_ACCESS_CONTROL_ALLOW_ORIGIN; - } - break; - } - break; - } - return -1; -} - -int nghttp2_hd_entry_init(nghttp2_hd_entry *ent, uint8_t flags, uint8_t *name, - size_t namelen, uint8_t *value, size_t valuelen, - int token, nghttp2_mem *mem) { - int rv = 0; - - /* Since nghttp2_hd_entry is used for indexing, ent->nv.flags always - NGHTTP2_NV_FLAG_NONE */ - ent->nv.flags = NGHTTP2_NV_FLAG_NONE; - - if ((flags & NGHTTP2_HD_FLAG_NAME_ALLOC) && - (flags & NGHTTP2_HD_FLAG_NAME_GIFT) == 0) { - if (namelen == 0) { - flags &= ~NGHTTP2_HD_FLAG_NAME_ALLOC; - ent->nv.name = (uint8_t *)""; - } else { - /* copy including terminating NULL byte */ - ent->nv.name = nghttp2_memdup(name, namelen + 1, mem); - if (ent->nv.name == NULL) { - rv = NGHTTP2_ERR_NOMEM; - goto fail; - } - } - } else { - ent->nv.name = name; - } - if ((flags & NGHTTP2_HD_FLAG_VALUE_ALLOC) && - (flags & NGHTTP2_HD_FLAG_VALUE_GIFT) == 0) { - if (valuelen == 0) { - flags &= ~NGHTTP2_HD_FLAG_VALUE_ALLOC; - ent->nv.value = (uint8_t *)""; - } else { - /* copy including terminating NULL byte */ - ent->nv.value = nghttp2_memdup(value, valuelen + 1, mem); - if (ent->nv.value == NULL) { - rv = NGHTTP2_ERR_NOMEM; - goto fail2; - } - } - } else { - ent->nv.value = value; - } - ent->nv.namelen = namelen; - ent->nv.valuelen = valuelen; - ent->token = token; - ent->ref = 1; - ent->flags = flags; - - return 0; - -fail2: - if (flags & NGHTTP2_HD_FLAG_NAME_ALLOC) { - nghttp2_mem_free(mem, ent->nv.name); - } -fail: - return rv; -} - -void nghttp2_hd_entry_free(nghttp2_hd_entry *ent, nghttp2_mem *mem) { - assert(ent->ref == 0); - if (ent->flags & NGHTTP2_HD_FLAG_NAME_ALLOC) { - nghttp2_mem_free(mem, ent->nv.name); - } - if (ent->flags & NGHTTP2_HD_FLAG_VALUE_ALLOC) { - nghttp2_mem_free(mem, ent->nv.value); - } -} - -static int hd_ringbuf_init(nghttp2_hd_ringbuf *ringbuf, size_t bufsize, - nghttp2_mem *mem) { - size_t size; - for (size = 1; size < bufsize; size <<= 1) - ; - ringbuf->buffer = nghttp2_mem_malloc(mem, sizeof(nghttp2_hd_entry *) * size); - if (ringbuf->buffer == NULL) { - return NGHTTP2_ERR_NOMEM; - } - ringbuf->mask = size - 1; - ringbuf->first = 0; - ringbuf->len = 0; - return 0; -} - -static nghttp2_hd_entry *hd_ringbuf_get(nghttp2_hd_ringbuf *ringbuf, - size_t idx) { - assert(idx < ringbuf->len); - return ringbuf->buffer[(ringbuf->first + idx) & ringbuf->mask]; -} - -static int hd_ringbuf_reserve(nghttp2_hd_ringbuf *ringbuf, size_t bufsize, - nghttp2_mem *mem) { - size_t i; - size_t size; - nghttp2_hd_entry **buffer; - - if (ringbuf->mask + 1 >= bufsize) { - return 0; - } - for (size = 1; size < bufsize; size <<= 1) - ; - buffer = nghttp2_mem_malloc(mem, sizeof(nghttp2_hd_entry *) * size); - if (buffer == NULL) { - return NGHTTP2_ERR_NOMEM; - } - for (i = 0; i < ringbuf->len; ++i) { - buffer[i] = hd_ringbuf_get(ringbuf, i); - } - nghttp2_mem_free(mem, ringbuf->buffer); - ringbuf->buffer = buffer; - ringbuf->mask = size - 1; - ringbuf->first = 0; - return 0; -} - -static void hd_ringbuf_free(nghttp2_hd_ringbuf *ringbuf, nghttp2_mem *mem) { - size_t i; - if (ringbuf == NULL) { - return; - } - for (i = 0; i < ringbuf->len; ++i) { - nghttp2_hd_entry *ent = hd_ringbuf_get(ringbuf, i); - --ent->ref; - nghttp2_hd_entry_free(ent, mem); - nghttp2_mem_free(mem, ent); - } - nghttp2_mem_free(mem, ringbuf->buffer); -} - -static int hd_ringbuf_push_front(nghttp2_hd_ringbuf *ringbuf, - nghttp2_hd_entry *ent, nghttp2_mem *mem) { - int rv; - - rv = hd_ringbuf_reserve(ringbuf, ringbuf->len + 1, mem); - - if (rv != 0) { - return rv; - } - - ringbuf->buffer[--ringbuf->first & ringbuf->mask] = ent; - ++ringbuf->len; - - return 0; -} - -static void hd_ringbuf_pop_back(nghttp2_hd_ringbuf *ringbuf) { - assert(ringbuf->len > 0); - --ringbuf->len; -} - -static int hd_context_init(nghttp2_hd_context *context, nghttp2_mem *mem) { - int rv; - context->mem = mem; - context->bad = 0; - context->hd_table_bufsize_max = NGHTTP2_HD_DEFAULT_MAX_BUFFER_SIZE; - rv = hd_ringbuf_init(&context->hd_table, context->hd_table_bufsize_max / - NGHTTP2_HD_ENTRY_OVERHEAD, - mem); - if (rv != 0) { - return rv; - } - - context->hd_table_bufsize = 0; - return 0; -} - -static void hd_context_free(nghttp2_hd_context *context) { - hd_ringbuf_free(&context->hd_table, context->mem); -} - -int nghttp2_hd_deflate_init(nghttp2_hd_deflater *deflater, nghttp2_mem *mem) { - return nghttp2_hd_deflate_init2( - deflater, NGHTTP2_HD_DEFAULT_MAX_DEFLATE_BUFFER_SIZE, mem); -} - -int nghttp2_hd_deflate_init2(nghttp2_hd_deflater *deflater, - size_t deflate_hd_table_bufsize_max, - nghttp2_mem *mem) { - int rv; - rv = hd_context_init(&deflater->ctx, mem); - if (rv != 0) { - return rv; - } - - if (deflate_hd_table_bufsize_max < NGHTTP2_HD_DEFAULT_MAX_BUFFER_SIZE) { - deflater->notify_table_size_change = 1; - deflater->ctx.hd_table_bufsize_max = deflate_hd_table_bufsize_max; - } else { - deflater->notify_table_size_change = 0; - } - - deflater->deflate_hd_table_bufsize_max = deflate_hd_table_bufsize_max; - deflater->min_hd_table_bufsize_max = UINT32_MAX; - - return 0; -} - -int nghttp2_hd_inflate_init(nghttp2_hd_inflater *inflater, nghttp2_mem *mem) { - int rv; - - rv = hd_context_init(&inflater->ctx, mem); - if (rv != 0) { - goto fail; - } - - inflater->settings_hd_table_bufsize_max = NGHTTP2_HD_DEFAULT_MAX_BUFFER_SIZE; - - inflater->ent_keep = NULL; - inflater->nv_keep = NULL; - - inflater->opcode = NGHTTP2_HD_OPCODE_NONE; - inflater->state = NGHTTP2_HD_STATE_OPCODE; - - rv = nghttp2_bufs_init3(&inflater->nvbufs, NGHTTP2_HD_MAX_NV / 8, 8, 1, 0, - mem); - - if (rv != 0) { - goto nvbufs_fail; - } - - inflater->huffman_encoded = 0; - inflater->index = 0; - inflater->left = 0; - inflater->shift = 0; - inflater->newnamelen = 0; - inflater->index_required = 0; - inflater->no_index = 0; - - return 0; - -nvbufs_fail: - hd_context_free(&inflater->ctx); -fail: - return rv; -} - -static void hd_inflate_keep_free(nghttp2_hd_inflater *inflater) { - nghttp2_mem *mem; - - mem = inflater->ctx.mem; - if (inflater->ent_keep) { - if (inflater->ent_keep->ref == 0) { - nghttp2_hd_entry_free(inflater->ent_keep, mem); - nghttp2_mem_free(mem, inflater->ent_keep); - } - inflater->ent_keep = NULL; - } - - nghttp2_mem_free(mem, inflater->nv_keep); - inflater->nv_keep = NULL; -} - -void nghttp2_hd_deflate_free(nghttp2_hd_deflater *deflater) { - hd_context_free(&deflater->ctx); -} - -void nghttp2_hd_inflate_free(nghttp2_hd_inflater *inflater) { - hd_inflate_keep_free(inflater); - nghttp2_bufs_free(&inflater->nvbufs); - hd_context_free(&inflater->ctx); -} - -static size_t entry_room(size_t namelen, size_t valuelen) { - return NGHTTP2_HD_ENTRY_OVERHEAD + namelen + valuelen; -} - -static int emit_indexed_header(nghttp2_nv *nv_out, int *token_out, - nghttp2_hd_entry *ent) { - DEBUGF(fprintf(stderr, "inflatehd: header emission: %s: %s\n", ent->nv.name, - ent->nv.value)); - /* ent->ref may be 0. This happens if the encoder emits literal - block larger than header table capacity with indexing. */ - *nv_out = ent->nv; - *token_out = ent->token; - return 0; -} - -static int emit_literal_header(nghttp2_nv *nv_out, int *token_out, - nghttp2_nv *nv) { - DEBUGF(fprintf(stderr, "inflatehd: header emission: %s: %s\n", nv->name, - nv->value)); - *nv_out = *nv; - *token_out = lookup_token(nv->name, nv->namelen); - return 0; -} - -static size_t count_encoded_length(size_t n, size_t prefix) { - size_t k = (1 << prefix) - 1; - size_t len = 0; - - if (n < k) { - return 1; - } - - n -= k; - ++len; - - for (; n >= 128; n >>= 7, ++len) - ; - - return len + 1; -} - -static size_t encode_length(uint8_t *buf, size_t n, size_t prefix) { - size_t k = (1 << prefix) - 1; - uint8_t *begin = buf; - - *buf &= ~k; - - if (n < k) { - *buf |= n; - return 1; - } - - *buf++ |= k; - n -= k; - - for (; n >= 128; n >>= 7) { - *buf++ = (1 << 7) | (n & 0x7f); - } - - *buf++ = (uint8_t)n; - - return (size_t)(buf - begin); -} - -/* - * Decodes |prefix| prefixed integer stored from |in|. The |last| - * represents the 1 beyond the last of the valid contiguous memory - * region from |in|. The decoded integer must be less than or equal - * to UINT32_MAX. - * - * If the |initial| is nonzero, it is used as a initial value, this - * function assumes the |in| starts with intermediate data. - * - * An entire integer is decoded successfully, decoded, the |*final| is - * set to nonzero. - * - * This function stores the decoded integer in |*res| if it succeed, - * including partial decoding (in this case, number of shift to make - * in the next call will be stored in |*shift_ptr|) and returns number - * of bytes processed, or returns -1, indicating decoding error. - */ -static ssize_t decode_length(uint32_t *res, size_t *shift_ptr, int *final, - uint32_t initial, size_t shift, uint8_t *in, - uint8_t *last, size_t prefix) { - uint32_t k = (1 << prefix) - 1; - uint32_t n = initial; - uint8_t *start = in; - - *shift_ptr = 0; - *final = 0; - - if (n == 0) { - if ((*in & k) != k) { - *res = (*in) & k; - *final = 1; - return 1; - } - - n = k; - - if (++in == last) { - *res = n; - return (ssize_t)(in - start); - } - } - - for (; in != last; ++in, shift += 7) { - uint32_t add = *in & 0x7f; - - if ((UINT32_MAX >> shift) < add) { - DEBUGF(fprintf(stderr, "inflate: integer overflow on shift\n")); - return -1; - } - - add <<= shift; - - if (UINT32_MAX - add < n) { - DEBUGF(fprintf(stderr, "inflate: integer overflow on addition\n")); - return -1; - } - - n += add; - - if ((*in & (1 << 7)) == 0) { - break; - } - } - - *shift_ptr = shift; - - if (in == last) { - *res = n; - return (ssize_t)(in - start); - } - - *res = n; - *final = 1; - return (ssize_t)(in + 1 - start); -} - -static int emit_table_size(nghttp2_bufs *bufs, size_t table_size) { - int rv; - uint8_t *bufp; - size_t blocklen; - uint8_t sb[16]; - - DEBUGF(fprintf(stderr, "deflatehd: emit table_size=%zu\n", table_size)); - - blocklen = count_encoded_length(table_size, 5); - - if (sizeof(sb) < blocklen) { - return NGHTTP2_ERR_HEADER_COMP; - } - - bufp = sb; - - *bufp = 0x20u; - - encode_length(bufp, table_size, 5); - - rv = nghttp2_bufs_add(bufs, sb, blocklen); - if (rv != 0) { - return rv; - } - - return 0; -} - -static int emit_indexed_block(nghttp2_bufs *bufs, size_t idx) { - int rv; - size_t blocklen; - uint8_t sb[16]; - uint8_t *bufp; - - blocklen = count_encoded_length(idx + 1, 7); - - DEBUGF(fprintf(stderr, "deflatehd: emit indexed index=%zu, %zu bytes\n", idx, - blocklen)); - - if (sizeof(sb) < blocklen) { - return NGHTTP2_ERR_HEADER_COMP; - } - - bufp = sb; - *bufp = 0x80u; - encode_length(bufp, idx + 1, 7); - - rv = nghttp2_bufs_add(bufs, sb, blocklen); - if (rv != 0) { - return rv; - } - - return 0; -} - -static int emit_string(nghttp2_bufs *bufs, const uint8_t *str, size_t len) { - int rv; - uint8_t sb[16]; - uint8_t *bufp; - size_t blocklen; - size_t enclen; - int huffman = 0; - - enclen = nghttp2_hd_huff_encode_count(str, len); - - if (enclen < len) { - huffman = 1; - } else { - enclen = len; - } - - blocklen = count_encoded_length(enclen, 7); - - DEBUGF(fprintf(stderr, "deflatehd: emit string str=")); - DEBUGF(fwrite(str, len, 1, stderr)); - DEBUGF(fprintf(stderr, ", length=%zu, huffman=%d, encoded_length=%zu\n", len, - huffman, enclen)); - - if (sizeof(sb) < blocklen) { - return NGHTTP2_ERR_HEADER_COMP; - } - - bufp = sb; - *bufp = huffman ? 1 << 7 : 0; - encode_length(bufp, enclen, 7); - - rv = nghttp2_bufs_add(bufs, sb, blocklen); - if (rv != 0) { - return rv; - } - - if (huffman) { - rv = nghttp2_hd_huff_encode(bufs, str, len); - } else { - assert(enclen == len); - rv = nghttp2_bufs_add(bufs, str, len); - } - - return rv; -} - -static uint8_t pack_first_byte(int indexing_mode) { - switch (indexing_mode) { - case NGHTTP2_HD_WITH_INDEXING: - return 0x40u; - case NGHTTP2_HD_WITHOUT_INDEXING: - return 0; - case NGHTTP2_HD_NEVER_INDEXING: - return 0x10u; - default: - assert(0); - } - /* This is required to compile with android NDK r10d + - --enable-werror */ - return 0; -} - -static int emit_indname_block(nghttp2_bufs *bufs, size_t idx, - const nghttp2_nv *nv, int indexing_mode) { - int rv; - uint8_t *bufp; - size_t blocklen; - uint8_t sb[16]; - size_t prefixlen; - - if (indexing_mode == NGHTTP2_HD_WITH_INDEXING) { - prefixlen = 6; - } else { - prefixlen = 4; - } - - DEBUGF(fprintf(stderr, "deflatehd: emit indname index=%zu, valuelen=%zu, " - "indexing_mode=%d\n", - idx, nv->valuelen, indexing_mode)); - - blocklen = count_encoded_length(idx + 1, prefixlen); - - if (sizeof(sb) < blocklen) { - return NGHTTP2_ERR_HEADER_COMP; - } - - bufp = sb; - - *bufp = pack_first_byte(indexing_mode); - - encode_length(bufp, idx + 1, prefixlen); - - rv = nghttp2_bufs_add(bufs, sb, blocklen); - if (rv != 0) { - return rv; - } - - rv = emit_string(bufs, nv->value, nv->valuelen); - if (rv != 0) { - return rv; - } - - return 0; -} - -static int emit_newname_block(nghttp2_bufs *bufs, const nghttp2_nv *nv, - int indexing_mode) { - int rv; - - DEBUGF(fprintf(stderr, "deflatehd: emit newname namelen=%zu, valuelen=%zu, " - "indexing_mode=%d\n", - nv->namelen, nv->valuelen, indexing_mode)); - - rv = nghttp2_bufs_addb(bufs, pack_first_byte(indexing_mode)); - if (rv != 0) { - return rv; - } - - rv = emit_string(bufs, nv->name, nv->namelen); - if (rv != 0) { - return rv; - } - - rv = emit_string(bufs, nv->value, nv->valuelen); - if (rv != 0) { - return rv; - } - - return 0; -} - -static nghttp2_hd_entry *add_hd_table_incremental(nghttp2_hd_context *context, - const nghttp2_nv *nv, - int token, - uint8_t entry_flags) { - int rv; - nghttp2_hd_entry *new_ent; - size_t room; - nghttp2_mem *mem; - - mem = context->mem; - room = entry_room(nv->namelen, nv->valuelen); - - while (context->hd_table_bufsize + room > context->hd_table_bufsize_max && - context->hd_table.len > 0) { - - size_t idx = context->hd_table.len - 1; - nghttp2_hd_entry *ent = hd_ringbuf_get(&context->hd_table, idx); - - context->hd_table_bufsize -= entry_room(ent->nv.namelen, ent->nv.valuelen); - - DEBUGF(fprintf(stderr, "hpack: remove item from header table: %s: %s\n", - ent->nv.name, ent->nv.value)); - - hd_ringbuf_pop_back(&context->hd_table); - if (--ent->ref == 0) { - nghttp2_hd_entry_free(ent, mem); - nghttp2_mem_free(mem, ent); - } - } - - new_ent = nghttp2_mem_malloc(mem, sizeof(nghttp2_hd_entry)); - if (new_ent == NULL) { - return NULL; - } - - rv = nghttp2_hd_entry_init(new_ent, entry_flags, nv->name, nv->namelen, - nv->value, nv->valuelen, token, mem); - if (rv != 0) { - nghttp2_mem_free(mem, new_ent); - return NULL; - } - - if (room > context->hd_table_bufsize_max) { - /* The entry taking more than NGHTTP2_HD_MAX_BUFFER_SIZE is - immediately evicted. */ - --new_ent->ref; - } else { - rv = hd_ringbuf_push_front(&context->hd_table, new_ent, mem); - - if (rv != 0) { - --new_ent->ref; - - if ((entry_flags & NGHTTP2_HD_FLAG_NAME_ALLOC) && - (entry_flags & NGHTTP2_HD_FLAG_NAME_GIFT)) { - /* nv->name are managed by caller. */ - new_ent->nv.name = NULL; - new_ent->nv.namelen = 0; - } - if ((entry_flags & NGHTTP2_HD_FLAG_VALUE_ALLOC) && - (entry_flags & NGHTTP2_HD_FLAG_VALUE_GIFT)) { - /* nv->value are managed by caller. */ - new_ent->nv.value = NULL; - new_ent->nv.valuelen = 0; - } - - nghttp2_hd_entry_free(new_ent, mem); - nghttp2_mem_free(mem, new_ent); - - return NULL; - } - - context->hd_table_bufsize += room; - } - return new_ent; -} - -static int name_eq(const nghttp2_nv *a, const nghttp2_nv *b) { - return a->namelen == b->namelen && memeq(a->name, b->name, a->namelen); -} - -static int value_eq(const nghttp2_nv *a, const nghttp2_nv *b) { - return a->valuelen == b->valuelen && memeq(a->value, b->value, a->valuelen); -} - -typedef struct { - ssize_t index; - /* Nonzero if both name and value are matched. */ - uint8_t name_value_match; -} search_result; - -static search_result search_static_table(const nghttp2_nv *nv, int token, - int indexing_mode) { - search_result res = {token, 0}; - int i; - - if (indexing_mode == NGHTTP2_HD_NEVER_INDEXING) { - return res; - } - - for (i = token; - i <= NGHTTP2_TOKEN_WWW_AUTHENTICATE && static_table[i].token == token; - ++i) { - if (value_eq(&static_table[i].nv, nv)) { - res.index = i; - res.name_value_match = 1; - return res; - } - } - return res; -} - -static search_result search_hd_table(nghttp2_hd_context *context, - const nghttp2_nv *nv, int token, - int indexing_mode) { - search_result res = {-1, 0}; - size_t i; - - if (token >= 0 && token <= NGHTTP2_TOKEN_WWW_AUTHENTICATE) { - res = search_static_table(nv, token, indexing_mode); - if (res.name_value_match) { - return res; - } - } - - for (i = 0; i < context->hd_table.len; ++i) { - nghttp2_hd_entry *ent = hd_ringbuf_get(&context->hd_table, i); - if (ent->token != token || (token == -1 && !name_eq(&ent->nv, nv))) { - continue; - } - - if (res.index == -1) { - res.index = (ssize_t)(i + NGHTTP2_STATIC_TABLE_LENGTH); - } - - if (indexing_mode != NGHTTP2_HD_NEVER_INDEXING && value_eq(&ent->nv, nv)) { - res.index = (ssize_t)(i + NGHTTP2_STATIC_TABLE_LENGTH); - res.name_value_match = 1; - return res; - } - } - - return res; -} - -static void hd_context_shrink_table_size(nghttp2_hd_context *context) { - nghttp2_mem *mem; - - mem = context->mem; - - while (context->hd_table_bufsize > context->hd_table_bufsize_max && - context->hd_table.len > 0) { - size_t idx = context->hd_table.len - 1; - nghttp2_hd_entry *ent = hd_ringbuf_get(&context->hd_table, idx); - context->hd_table_bufsize -= entry_room(ent->nv.namelen, ent->nv.valuelen); - hd_ringbuf_pop_back(&context->hd_table); - if (--ent->ref == 0) { - nghttp2_hd_entry_free(ent, mem); - nghttp2_mem_free(mem, ent); - } - } -} - -int nghttp2_hd_deflate_change_table_size(nghttp2_hd_deflater *deflater, - size_t settings_hd_table_bufsize_max) { - size_t next_bufsize = nghttp2_min(settings_hd_table_bufsize_max, - deflater->deflate_hd_table_bufsize_max); - - deflater->ctx.hd_table_bufsize_max = next_bufsize; - - deflater->min_hd_table_bufsize_max = - nghttp2_min(deflater->min_hd_table_bufsize_max, next_bufsize); - - deflater->notify_table_size_change = 1; - - hd_context_shrink_table_size(&deflater->ctx); - return 0; -} - -int nghttp2_hd_inflate_change_table_size(nghttp2_hd_inflater *inflater, - size_t settings_hd_table_bufsize_max) { - inflater->settings_hd_table_bufsize_max = settings_hd_table_bufsize_max; - inflater->ctx.hd_table_bufsize_max = settings_hd_table_bufsize_max; - hd_context_shrink_table_size(&inflater->ctx); - return 0; -} - -#define INDEX_RANGE_VALID(context, idx) \ - ((idx) < (context)->hd_table.len + NGHTTP2_STATIC_TABLE_LENGTH) - -static size_t get_max_index(nghttp2_hd_context *context) { - return context->hd_table.len + NGHTTP2_STATIC_TABLE_LENGTH - 1; -} - -nghttp2_hd_entry *nghttp2_hd_table_get(nghttp2_hd_context *context, - size_t idx) { - assert(INDEX_RANGE_VALID(context, idx)); - if (idx >= NGHTTP2_STATIC_TABLE_LENGTH) { - return hd_ringbuf_get(&context->hd_table, - idx - NGHTTP2_STATIC_TABLE_LENGTH); - } else { - return &static_table[idx]; - } -} - -static int hd_deflate_decide_indexing(nghttp2_hd_deflater *deflater, - const nghttp2_nv *nv, int token) { - if (token == NGHTTP2_TOKEN__PATH || token == NGHTTP2_TOKEN_AGE || - token == NGHTTP2_TOKEN_CONTENT_LENGTH || token == NGHTTP2_TOKEN_ETAG || - token == NGHTTP2_TOKEN_IF_MODIFIED_SINCE || - token == NGHTTP2_TOKEN_IF_NONE_MATCH || token == NGHTTP2_TOKEN_LOCATION || - token == NGHTTP2_TOKEN_SET_COOKIE || - entry_room(nv->namelen, nv->valuelen) > - deflater->ctx.hd_table_bufsize_max * 3 / 4) { - return NGHTTP2_HD_WITHOUT_INDEXING; - } - - return NGHTTP2_HD_WITH_INDEXING; -} - -static int deflate_nv(nghttp2_hd_deflater *deflater, nghttp2_bufs *bufs, - const nghttp2_nv *nv) { - int rv; - search_result res; - ssize_t idx; - int indexing_mode; - int token; - nghttp2_mem *mem; - - DEBUGF(fprintf(stderr, "deflatehd: deflating %s: %s\n", nv->name, nv->value)); - - mem = deflater->ctx.mem; - - token = lookup_token(nv->name, nv->namelen); - - /* Don't index authorization header field since it may contain low - entropy secret data (e.g., id/password). Also cookie header - field with less than 20 bytes value is also never indexed. This - is the same criteria used in Firefox codebase. */ - indexing_mode = - token == NGHTTP2_TOKEN_AUTHORIZATION || - (token == NGHTTP2_TOKEN_COOKIE && nv->valuelen < 20) || - (nv->flags & NGHTTP2_NV_FLAG_NO_INDEX) - ? NGHTTP2_HD_NEVER_INDEXING - : hd_deflate_decide_indexing(deflater, nv, token); - - res = search_hd_table(&deflater->ctx, nv, token, indexing_mode); - - idx = res.index; - - if (res.name_value_match) { - - DEBUGF(fprintf(stderr, "deflatehd: name/value match index=%zd\n", idx)); - - rv = emit_indexed_block(bufs, idx); - if (rv != 0) { - return rv; - } - - return 0; - } - - if (res.index != -1) { - DEBUGF(fprintf(stderr, "deflatehd: name match index=%zd\n", res.index)); - } - - if (indexing_mode == NGHTTP2_HD_WITH_INDEXING) { - nghttp2_hd_entry *new_ent; - if (idx != -1 && idx < (ssize_t)NGHTTP2_STATIC_TABLE_LENGTH) { - nghttp2_nv nv_indname; - nv_indname = *nv; - nv_indname.name = nghttp2_hd_table_get(&deflater->ctx, idx)->nv.name; - new_ent = add_hd_table_incremental(&deflater->ctx, &nv_indname, token, - NGHTTP2_HD_FLAG_VALUE_ALLOC); - } else { - new_ent = add_hd_table_incremental(&deflater->ctx, nv, token, - NGHTTP2_HD_FLAG_NAME_ALLOC | - NGHTTP2_HD_FLAG_VALUE_ALLOC); - } - if (!new_ent) { - return NGHTTP2_ERR_HEADER_COMP; - } - if (new_ent->ref == 0) { - nghttp2_hd_entry_free(new_ent, mem); - nghttp2_mem_free(mem, new_ent); - } - } - if (idx == -1) { - rv = emit_newname_block(bufs, nv, indexing_mode); - } else { - rv = emit_indname_block(bufs, idx, nv, indexing_mode); - } - if (rv != 0) { - return rv; - } - - return 0; -} - -int nghttp2_hd_deflate_hd_bufs(nghttp2_hd_deflater *deflater, - nghttp2_bufs *bufs, const nghttp2_nv *nv, - size_t nvlen) { - size_t i; - int rv = 0; - - if (deflater->ctx.bad) { - return NGHTTP2_ERR_HEADER_COMP; - } - - if (deflater->notify_table_size_change) { - size_t min_hd_table_bufsize_max; - - min_hd_table_bufsize_max = deflater->min_hd_table_bufsize_max; - - deflater->notify_table_size_change = 0; - deflater->min_hd_table_bufsize_max = UINT32_MAX; - - if (deflater->ctx.hd_table_bufsize_max > min_hd_table_bufsize_max) { - - rv = emit_table_size(bufs, min_hd_table_bufsize_max); - - if (rv != 0) { - goto fail; - } - } - - rv = emit_table_size(bufs, deflater->ctx.hd_table_bufsize_max); - - if (rv != 0) { - goto fail; - } - } - - for (i = 0; i < nvlen; ++i) { - rv = deflate_nv(deflater, bufs, &nv[i]); - if (rv != 0) { - goto fail; - } - } - - DEBUGF( - fprintf(stderr, "deflatehd: all input name/value pairs were deflated\n")); - - return 0; -fail: - DEBUGF(fprintf(stderr, "deflatehd: error return %d\n", rv)); - - deflater->ctx.bad = 1; - return rv; -} - -ssize_t nghttp2_hd_deflate_hd(nghttp2_hd_deflater *deflater, uint8_t *buf, - size_t buflen, const nghttp2_nv *nv, - size_t nvlen) { - nghttp2_bufs bufs; - int rv; - nghttp2_mem *mem; - - mem = deflater->ctx.mem; - - rv = nghttp2_bufs_wrap_init(&bufs, buf, buflen, mem); - - if (rv != 0) { - return rv; - } - - rv = nghttp2_hd_deflate_hd_bufs(deflater, &bufs, nv, nvlen); - - buflen = nghttp2_bufs_len(&bufs); - - nghttp2_bufs_wrap_free(&bufs); - - if (rv == NGHTTP2_ERR_BUFFER_ERROR) { - return NGHTTP2_ERR_INSUFF_BUFSIZE; - } - - if (rv != 0) { - return rv; - } - - return (ssize_t)buflen; -} - -size_t nghttp2_hd_deflate_bound(nghttp2_hd_deflater *deflater _U_, - const nghttp2_nv *nva, size_t nvlen) { - size_t n = 0; - size_t i; - - /* Possible Maximum Header Table Size Change. Encoding (1u << 31) - - 1 using 4 bit prefix requires 6 bytes. We may emit this at most - twice. */ - n += 12; - - /* Use Literal Header Field without indexing - New Name, since it is - most space consuming format. Also we choose the less one between - non-huffman and huffman, so using literal byte count is - sufficient for upper bound. - - Encoding (1u << 31) - 1 using 7 bit prefix requires 6 bytes. We - need 2 of this for |nvlen| header fields. */ - n += 6 * 2 * nvlen; - - for (i = 0; i < nvlen; ++i) { - n += nva[i].namelen + nva[i].valuelen; - } - - return n; -} - -int nghttp2_hd_deflate_new(nghttp2_hd_deflater **deflater_ptr, - size_t deflate_hd_table_bufsize_max) { - return nghttp2_hd_deflate_new2(deflater_ptr, deflate_hd_table_bufsize_max, - NULL); -} - -int nghttp2_hd_deflate_new2(nghttp2_hd_deflater **deflater_ptr, - size_t deflate_hd_table_bufsize_max, - nghttp2_mem *mem) { - int rv; - nghttp2_hd_deflater *deflater; - - if (mem == NULL) { - mem = nghttp2_mem_default(); - } - - deflater = nghttp2_mem_malloc(mem, sizeof(nghttp2_hd_deflater)); - - if (deflater == NULL) { - return NGHTTP2_ERR_NOMEM; - } - - rv = nghttp2_hd_deflate_init2(deflater, deflate_hd_table_bufsize_max, mem); - - if (rv != 0) { - nghttp2_mem_free(mem, deflater); - - return rv; - } - - *deflater_ptr = deflater; - - return 0; -} - -void nghttp2_hd_deflate_del(nghttp2_hd_deflater *deflater) { - nghttp2_mem *mem; - - mem = deflater->ctx.mem; - - nghttp2_hd_deflate_free(deflater); - - nghttp2_mem_free(mem, deflater); -} - -static void hd_inflate_set_huffman_encoded(nghttp2_hd_inflater *inflater, - const uint8_t *in) { - inflater->huffman_encoded = (*in & (1 << 7)) != 0; -} - -/* - * Decodes the integer from the range [in, last). The result is - * assigned to |inflater->left|. If the |inflater->left| is 0, then - * it performs variable integer decoding from scratch. Otherwise, it - * uses the |inflater->left| as the initial value and continues to - * decode assuming that [in, last) begins with intermediary sequence. - * - * This function returns the number of bytes read if it succeeds, or - * one of the following negative error codes: - * - * NGHTTP2_ERR_HEADER_COMP - * Integer decoding failed - */ -static ssize_t hd_inflate_read_len(nghttp2_hd_inflater *inflater, int *rfin, - uint8_t *in, uint8_t *last, size_t prefix, - size_t maxlen) { - ssize_t rv; - uint32_t out; - - *rfin = 0; - - rv = decode_length(&out, &inflater->shift, rfin, (uint32_t)inflater->left, - inflater->shift, in, last, prefix); - - if (rv == -1) { - DEBUGF(fprintf(stderr, "inflatehd: integer decoding failed\n")); - return NGHTTP2_ERR_HEADER_COMP; - } - - if (out > maxlen) { - DEBUGF(fprintf( - stderr, "inflatehd: integer exceeded the maximum value %zu\n", maxlen)); - return NGHTTP2_ERR_HEADER_COMP; - } - - inflater->left = out; - - DEBUGF(fprintf(stderr, "inflatehd: decoded integer is %u\n", out)); - - return rv; -} - -/* - * Reads |inflater->left| bytes from the range [in, last) and performs - * huffman decoding against them and pushes the result into the - * |buffer|. - * - * This function returns the number of bytes read if it succeeds, or - * one of the following negative error codes: - * - * NGHTTP2_ERR_NOMEM - * Out of memory - * NGHTTP2_ERR_HEADER_COMP - * Huffman decoding failed - * NGHTTP2_ERR_BUFFER_ERROR - * Out of buffer space. - */ -static ssize_t hd_inflate_read_huff(nghttp2_hd_inflater *inflater, - nghttp2_bufs *bufs, uint8_t *in, - uint8_t *last) { - ssize_t readlen; - int final = 0; - if ((size_t)(last - in) >= inflater->left) { - last = in + inflater->left; - final = 1; - } - readlen = nghttp2_hd_huff_decode(&inflater->huff_decode_ctx, bufs, in, - last - in, final); - - if (readlen < 0) { - DEBUGF(fprintf(stderr, "inflatehd: huffman decoding failed\n")); - return readlen; - } - inflater->left -= (size_t)readlen; - return readlen; -} - -/* - * Reads |inflater->left| bytes from the range [in, last) and copies - * them into the |buffer|. - * - * This function returns the number of bytes read if it succeeds, or - * one of the following negative error codes: - * - * NGHTTP2_ERR_NOMEM - * Out of memory - * NGHTTP2_ERR_HEADER_COMP - * Header decompression failed - * NGHTTP2_ERR_BUFFER_ERROR - * Out of buffer space. - */ -static ssize_t hd_inflate_read(nghttp2_hd_inflater *inflater, - nghttp2_bufs *bufs, uint8_t *in, uint8_t *last) { - int rv; - size_t len = nghttp2_min((size_t)(last - in), inflater->left); - rv = nghttp2_bufs_add(bufs, in, len); - if (rv != 0) { - return rv; - } - inflater->left -= len; - return (ssize_t)len; -} - -/* - * Finalize indexed header representation reception. If header is - * emitted, |*nv_out| is filled with that value and 0 is returned. If - * no header is emitted, 1 is returned. - * - * This function returns either 0 or 1 if it succeeds, or one of the - * following negative error codes: - * - * NGHTTP2_ERR_NOMEM - * Out of memory - */ -static int hd_inflate_commit_indexed(nghttp2_hd_inflater *inflater, - nghttp2_nv *nv_out, int *token_out) { - nghttp2_hd_entry *ent = nghttp2_hd_table_get(&inflater->ctx, inflater->index); - - emit_indexed_header(nv_out, token_out, ent); - - return 0; -} - -static int hd_inflate_remove_bufs(nghttp2_hd_inflater *inflater, nghttp2_nv *nv, - int value_only) { - ssize_t rv; - size_t buflen; - uint8_t *buf; - nghttp2_buf *pbuf; - - if (inflater->index_required || - inflater->nvbufs.head != inflater->nvbufs.cur) { - - rv = nghttp2_bufs_remove(&inflater->nvbufs, &buf); - - if (rv < 0) { - return NGHTTP2_ERR_NOMEM; - } - - nghttp2_bufs_reset(&inflater->nvbufs); - - buflen = rv; - - if (value_only) { - /* we don't use this value, so no need to NULL-terminate */ - nv->name = NULL; - nv->namelen = 0; - - nv->value = buf; - nv->valuelen = buflen - 1; - } else { - nv->name = buf; - nv->namelen = inflater->newnamelen; - - nv->value = buf + nv->namelen + 1; - nv->valuelen = buflen - nv->namelen - 2; - } - - return 0; - } - - /* If we are not going to store header in header table and - name/value are in first chunk, we just refer them from nv, - instead of mallocing another memory. */ - - pbuf = &inflater->nvbufs.head->buf; - - if (value_only) { - /* we don't use this value, so no need to NULL-terminate */ - nv->name = NULL; - nv->namelen = 0; - - nv->value = pbuf->pos; - nv->valuelen = nghttp2_buf_len(pbuf) - 1; - } else { - nv->name = pbuf->pos; - nv->namelen = inflater->newnamelen; - - nv->value = pbuf->pos + nv->namelen + 1; - nv->valuelen = nghttp2_buf_len(pbuf) - nv->namelen - 2; - } - - /* Resetting does not change the content of first buffer */ - nghttp2_bufs_reset(&inflater->nvbufs); - - return 0; -} - -static int hd_inflate_remove_bufs_with_name(nghttp2_hd_inflater *inflater, - nghttp2_nv *nv, - nghttp2_hd_entry *ent_name) { -#ifndef NDEBUG - size_t rv; -#endif - size_t buflen; - uint8_t *buf; - nghttp2_mem *mem; - - mem = inflater->ctx.mem; - - /* Allocate buffer including name in ent_name, plus terminating - NULL. */ - buflen = ent_name->nv.namelen + 1 + nghttp2_bufs_len(&inflater->nvbufs); - - buf = nghttp2_mem_malloc(mem, buflen); - if (buf == NULL) { - return NGHTTP2_ERR_NOMEM; - } - - /* Copy including terminal NULL */ - memcpy(buf, ent_name->nv.name, ent_name->nv.namelen + 1); -#ifndef NDEBUG - rv = -#endif - nghttp2_bufs_remove_copy(&inflater->nvbufs, - buf + ent_name->nv.namelen + 1); - assert(ent_name->nv.namelen + 1 + rv == buflen); - - nghttp2_bufs_reset(&inflater->nvbufs); - - nv->name = buf; - nv->namelen = ent_name->nv.namelen; - - nv->value = buf + nv->namelen + 1; - nv->valuelen = buflen - nv->namelen - 2; - - return 0; -} - -/* - * Finalize literal header representation - new name- reception. If - * header is emitted, |*nv_out| is filled with that value and 0 is - * returned. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP2_ERR_NOMEM - * Out of memory - */ -static int hd_inflate_commit_newname(nghttp2_hd_inflater *inflater, - nghttp2_nv *nv_out, int *token_out) { - int rv; - nghttp2_nv nv; - nghttp2_mem *mem; - - mem = inflater->ctx.mem; - - rv = hd_inflate_remove_bufs(inflater, &nv, 0 /* name and value */); - if (rv != 0) { - return NGHTTP2_ERR_NOMEM; - } - - if (inflater->no_index) { - nv.flags = NGHTTP2_NV_FLAG_NO_INDEX; - } else { - nv.flags = NGHTTP2_NV_FLAG_NONE; - } - - if (inflater->index_required) { - nghttp2_hd_entry *new_ent; - uint8_t ent_flags; - - /* nv->value points to the middle of the buffer pointed by - nv->name. So we just need to keep track of nv->name for memory - management. */ - ent_flags = NGHTTP2_HD_FLAG_NAME_ALLOC | NGHTTP2_HD_FLAG_NAME_GIFT; - - new_ent = add_hd_table_incremental( - &inflater->ctx, &nv, lookup_token(nv.name, nv.namelen), ent_flags); - - if (new_ent) { - emit_indexed_header(nv_out, token_out, new_ent); - inflater->ent_keep = new_ent; - - return 0; - } - - nghttp2_mem_free(mem, nv.name); - - return NGHTTP2_ERR_NOMEM; - } - - emit_literal_header(nv_out, token_out, &nv); - - if (nv.name != inflater->nvbufs.head->buf.pos) { - inflater->nv_keep = nv.name; - } - - return 0; -} - -/* - * Finalize literal header representation - indexed name- - * reception. If header is emitted, |*nv_out| is filled with that - * value and 0 is returned. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP2_ERR_NOMEM - * Out of memory - */ -static int hd_inflate_commit_indname(nghttp2_hd_inflater *inflater, - nghttp2_nv *nv_out, int *token_out) { - int rv; - nghttp2_nv nv; - nghttp2_hd_entry *ent_name; - nghttp2_mem *mem; - - mem = inflater->ctx.mem; - - if (inflater->no_index) { - nv.flags = NGHTTP2_NV_FLAG_NO_INDEX; - } else { - nv.flags = NGHTTP2_NV_FLAG_NONE; - } - - ent_name = nghttp2_hd_table_get(&inflater->ctx, inflater->index); - - if (inflater->index_required) { - nghttp2_hd_entry *new_ent; - uint8_t ent_flags; - - if (inflater->index < NGHTTP2_STATIC_TABLE_LENGTH) { - /* We don't copy name in static table */ - rv = hd_inflate_remove_bufs(inflater, &nv, 1 /* value only */); - if (rv != 0) { - return NGHTTP2_ERR_NOMEM; - } - nv.name = ent_name->nv.name; - nv.namelen = ent_name->nv.namelen; - - ent_flags = NGHTTP2_HD_FLAG_VALUE_ALLOC | NGHTTP2_HD_FLAG_VALUE_GIFT; - } else { - rv = hd_inflate_remove_bufs_with_name(inflater, &nv, ent_name); - if (rv != 0) { - return NGHTTP2_ERR_NOMEM; - } - /* nv->name and nv->value are in the same buffer. */ - ent_flags = NGHTTP2_HD_FLAG_NAME_ALLOC | NGHTTP2_HD_FLAG_NAME_GIFT; - } - - new_ent = add_hd_table_incremental(&inflater->ctx, &nv, ent_name->token, - ent_flags); - - /* At this point, ent_name might be deleted. */ - - if (new_ent) { - emit_indexed_header(nv_out, token_out, new_ent); - - inflater->ent_keep = new_ent; - - return 0; - } - - nghttp2_mem_free(mem, nv.value); - - return NGHTTP2_ERR_NOMEM; - } - - rv = hd_inflate_remove_bufs(inflater, &nv, 1 /* value only */); - if (rv != 0) { - return NGHTTP2_ERR_NOMEM; - } - - nv.name = ent_name->nv.name; - nv.namelen = ent_name->nv.namelen; - - emit_literal_header(nv_out, token_out, &nv); - - if (nv.value != inflater->nvbufs.head->buf.pos) { - inflater->nv_keep = nv.value; - } - - return 0; -} - -ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_inflater *inflater, nghttp2_nv *nv_out, - int *inflate_flags, uint8_t *in, size_t inlen, - int in_final) { - int token; - - return nghttp2_hd_inflate_hd2(inflater, nv_out, inflate_flags, &token, in, - inlen, in_final); -} - -ssize_t nghttp2_hd_inflate_hd2(nghttp2_hd_inflater *inflater, - nghttp2_nv *nv_out, int *inflate_flags, - int *token_out, uint8_t *in, size_t inlen, - int in_final) { - ssize_t rv = 0; - uint8_t *first = in; - uint8_t *last = in + inlen; - int rfin = 0; - int busy = 0; - - if (inflater->ctx.bad) { - return NGHTTP2_ERR_HEADER_COMP; - } - - DEBUGF(fprintf(stderr, "inflatehd: start state=%d\n", inflater->state)); - hd_inflate_keep_free(inflater); - *token_out = -1; - *inflate_flags = NGHTTP2_HD_INFLATE_NONE; - for (; in != last || busy;) { - busy = 0; - switch (inflater->state) { - case NGHTTP2_HD_STATE_OPCODE: - if ((*in & 0xe0u) == 0x20u) { - DEBUGF(fprintf(stderr, "inflatehd: header table size change\n")); - inflater->opcode = NGHTTP2_HD_OPCODE_INDEXED; - inflater->state = NGHTTP2_HD_STATE_READ_TABLE_SIZE; - } else if (*in & 0x80u) { - DEBUGF(fprintf(stderr, "inflatehd: indexed repr\n")); - inflater->opcode = NGHTTP2_HD_OPCODE_INDEXED; - inflater->state = NGHTTP2_HD_STATE_READ_INDEX; - } else { - if (*in == 0x40u || *in == 0 || *in == 0x10u) { - DEBUGF( - fprintf(stderr, "inflatehd: literal header repr - new name\n")); - inflater->opcode = NGHTTP2_HD_OPCODE_NEWNAME; - inflater->state = NGHTTP2_HD_STATE_NEWNAME_CHECK_NAMELEN; - } else { - DEBUGF(fprintf(stderr, - "inflatehd: literal header repr - indexed name\n")); - inflater->opcode = NGHTTP2_HD_OPCODE_INDNAME; - inflater->state = NGHTTP2_HD_STATE_READ_INDEX; - } - inflater->index_required = (*in & 0x40) != 0; - inflater->no_index = (*in & 0xf0u) == 0x10u; - DEBUGF(fprintf(stderr, "inflatehd: indexing required=%d, no_index=%d\n", - inflater->index_required, inflater->no_index)); - if (inflater->opcode == NGHTTP2_HD_OPCODE_NEWNAME) { - ++in; - } - } - inflater->left = 0; - inflater->shift = 0; - break; - case NGHTTP2_HD_STATE_READ_TABLE_SIZE: - rfin = 0; - rv = hd_inflate_read_len(inflater, &rfin, in, last, 5, - inflater->settings_hd_table_bufsize_max); - if (rv < 0) { - goto fail; - } - in += rv; - if (!rfin) { - goto almost_ok; - } - DEBUGF(fprintf(stderr, "inflatehd: table_size=%zu\n", inflater->left)); - inflater->ctx.hd_table_bufsize_max = inflater->left; - hd_context_shrink_table_size(&inflater->ctx); - inflater->state = NGHTTP2_HD_STATE_OPCODE; - break; - case NGHTTP2_HD_STATE_READ_INDEX: { - size_t prefixlen; - - if (inflater->opcode == NGHTTP2_HD_OPCODE_INDEXED) { - prefixlen = 7; - } else if (inflater->index_required) { - prefixlen = 6; - } else { - prefixlen = 4; - } - - rfin = 0; - rv = hd_inflate_read_len(inflater, &rfin, in, last, prefixlen, - get_max_index(&inflater->ctx) + 1); - if (rv < 0) { - goto fail; - } - - in += rv; - - if (!rfin) { - goto almost_ok; - } - - if (inflater->left == 0) { - rv = NGHTTP2_ERR_HEADER_COMP; - goto fail; - } - - DEBUGF(fprintf(stderr, "inflatehd: index=%zu\n", inflater->left)); - if (inflater->opcode == NGHTTP2_HD_OPCODE_INDEXED) { - inflater->index = inflater->left; - --inflater->index; - - rv = hd_inflate_commit_indexed(inflater, nv_out, token_out); - if (rv < 0) { - goto fail; - } - inflater->state = NGHTTP2_HD_STATE_OPCODE; - /* If rv == 1, no header was emitted */ - if (rv == 0) { - *inflate_flags |= NGHTTP2_HD_INFLATE_EMIT; - return (ssize_t)(in - first); - } - } else { - inflater->index = inflater->left; - --inflater->index; - - inflater->state = NGHTTP2_HD_STATE_CHECK_VALUELEN; - } - break; - } - case NGHTTP2_HD_STATE_NEWNAME_CHECK_NAMELEN: - hd_inflate_set_huffman_encoded(inflater, in); - inflater->state = NGHTTP2_HD_STATE_NEWNAME_READ_NAMELEN; - inflater->left = 0; - inflater->shift = 0; - DEBUGF(fprintf(stderr, "inflatehd: huffman encoded=%d\n", - inflater->huffman_encoded != 0)); - /* Fall through */ - case NGHTTP2_HD_STATE_NEWNAME_READ_NAMELEN: - rfin = 0; - rv = hd_inflate_read_len(inflater, &rfin, in, last, 7, NGHTTP2_HD_MAX_NV); - if (rv < 0) { - goto fail; - } - in += rv; - if (!rfin) { - DEBUGF(fprintf(stderr, - "inflatehd: integer not fully decoded. current=%zu\n", - inflater->left)); - - goto almost_ok; - } - - if (inflater->huffman_encoded) { - nghttp2_hd_huff_decode_context_init(&inflater->huff_decode_ctx); - - inflater->state = NGHTTP2_HD_STATE_NEWNAME_READ_NAMEHUFF; - } else { - inflater->state = NGHTTP2_HD_STATE_NEWNAME_READ_NAME; - } - break; - case NGHTTP2_HD_STATE_NEWNAME_READ_NAMEHUFF: - rv = hd_inflate_read_huff(inflater, &inflater->nvbufs, in, last); - if (rv < 0) { - goto fail; - } - - in += rv; - - DEBUGF(fprintf(stderr, "inflatehd: %zd bytes read\n", rv)); - - if (inflater->left) { - DEBUGF(fprintf(stderr, "inflatehd: still %zu bytes to go\n", - inflater->left)); - - goto almost_ok; - } - - inflater->newnamelen = nghttp2_bufs_len(&inflater->nvbufs); - - rv = nghttp2_bufs_addb(&inflater->nvbufs, '\0'); - if (rv != 0) { - goto fail; - } - - inflater->state = NGHTTP2_HD_STATE_CHECK_VALUELEN; - - break; - case NGHTTP2_HD_STATE_NEWNAME_READ_NAME: - rv = hd_inflate_read(inflater, &inflater->nvbufs, in, last); - if (rv < 0) { - goto fail; - } - - in += rv; - - DEBUGF(fprintf(stderr, "inflatehd: %zd bytes read\n", rv)); - if (inflater->left) { - DEBUGF(fprintf(stderr, "inflatehd: still %zu bytes to go\n", - inflater->left)); - - goto almost_ok; - } - - inflater->newnamelen = nghttp2_bufs_len(&inflater->nvbufs); - - rv = nghttp2_bufs_addb(&inflater->nvbufs, '\0'); - if (rv != 0) { - goto fail; - } - - inflater->state = NGHTTP2_HD_STATE_CHECK_VALUELEN; - - break; - case NGHTTP2_HD_STATE_CHECK_VALUELEN: - hd_inflate_set_huffman_encoded(inflater, in); - inflater->state = NGHTTP2_HD_STATE_READ_VALUELEN; - inflater->left = 0; - inflater->shift = 0; - DEBUGF(fprintf(stderr, "inflatehd: huffman encoded=%d\n", - inflater->huffman_encoded != 0)); - /* Fall through */ - case NGHTTP2_HD_STATE_READ_VALUELEN: - rfin = 0; - rv = hd_inflate_read_len(inflater, &rfin, in, last, 7, NGHTTP2_HD_MAX_NV); - if (rv < 0) { - goto fail; - } - - in += rv; - - if (!rfin) { - goto almost_ok; - } - - DEBUGF(fprintf(stderr, "inflatehd: valuelen=%zu\n", inflater->left)); - - if (inflater->huffman_encoded) { - nghttp2_hd_huff_decode_context_init(&inflater->huff_decode_ctx); - - inflater->state = NGHTTP2_HD_STATE_READ_VALUEHUFF; - } else { - inflater->state = NGHTTP2_HD_STATE_READ_VALUE; - } - - busy = 1; - - break; - case NGHTTP2_HD_STATE_READ_VALUEHUFF: - rv = hd_inflate_read_huff(inflater, &inflater->nvbufs, in, last); - if (rv < 0) { - goto fail; - } - - in += rv; - - DEBUGF(fprintf(stderr, "inflatehd: %zd bytes read\n", rv)); - - if (inflater->left) { - DEBUGF(fprintf(stderr, "inflatehd: still %zu bytes to go\n", - inflater->left)); - - goto almost_ok; - } - - rv = nghttp2_bufs_addb(&inflater->nvbufs, '\0'); - if (rv != 0) { - goto fail; - } - - if (inflater->opcode == NGHTTP2_HD_OPCODE_NEWNAME) { - rv = hd_inflate_commit_newname(inflater, nv_out, token_out); - } else { - rv = hd_inflate_commit_indname(inflater, nv_out, token_out); - } - - if (rv != 0) { - goto fail; - } - - inflater->state = NGHTTP2_HD_STATE_OPCODE; - *inflate_flags |= NGHTTP2_HD_INFLATE_EMIT; - - return (ssize_t)(in - first); - case NGHTTP2_HD_STATE_READ_VALUE: - rv = hd_inflate_read(inflater, &inflater->nvbufs, in, last); - if (rv < 0) { - DEBUGF(fprintf(stderr, "inflatehd: value read failure %zd: %s\n", rv, - nghttp2_strerror((int)rv))); - goto fail; - } - - in += rv; - - DEBUGF(fprintf(stderr, "inflatehd: %zd bytes read\n", rv)); - - if (inflater->left) { - DEBUGF(fprintf(stderr, "inflatehd: still %zu bytes to go\n", - inflater->left)); - goto almost_ok; - } - - rv = nghttp2_bufs_addb(&inflater->nvbufs, '\0'); - if (rv != 0) { - goto fail; - } - - if (inflater->opcode == NGHTTP2_HD_OPCODE_NEWNAME) { - rv = hd_inflate_commit_newname(inflater, nv_out, token_out); - } else { - rv = hd_inflate_commit_indname(inflater, nv_out, token_out); - } - - if (rv != 0) { - goto fail; - } - - inflater->state = NGHTTP2_HD_STATE_OPCODE; - *inflate_flags |= NGHTTP2_HD_INFLATE_EMIT; - - return (ssize_t)(in - first); - } - } - - assert(in == last); - - DEBUGF(fprintf(stderr, "inflatehd: all input bytes were processed\n")); - - if (in_final) { - DEBUGF(fprintf(stderr, "inflatehd: in_final set\n")); - - if (inflater->state != NGHTTP2_HD_STATE_OPCODE) { - DEBUGF(fprintf(stderr, "inflatehd: unacceptable state=%d\n", - inflater->state)); - rv = NGHTTP2_ERR_HEADER_COMP; - - goto fail; - } - *inflate_flags |= NGHTTP2_HD_INFLATE_FINAL; - } - return (ssize_t)(in - first); - -almost_ok: - if (in_final && inflater->state != NGHTTP2_HD_STATE_OPCODE) { - DEBUGF(fprintf(stderr, "inflatehd: input ended prematurely\n")); - - rv = NGHTTP2_ERR_HEADER_COMP; - - goto fail; - } - return (ssize_t)(in - first); - -fail: - DEBUGF(fprintf(stderr, "inflatehd: error return %zd\n", rv)); - - inflater->ctx.bad = 1; - return rv; -} - -int nghttp2_hd_inflate_end_headers(nghttp2_hd_inflater *inflater) { - hd_inflate_keep_free(inflater); - return 0; -} - -int nghttp2_hd_inflate_new(nghttp2_hd_inflater **inflater_ptr) { - return nghttp2_hd_inflate_new2(inflater_ptr, NULL); -} - -int nghttp2_hd_inflate_new2(nghttp2_hd_inflater **inflater_ptr, - nghttp2_mem *mem) { - int rv; - nghttp2_hd_inflater *inflater; - - if (mem == NULL) { - mem = nghttp2_mem_default(); - } - - inflater = nghttp2_mem_malloc(mem, sizeof(nghttp2_hd_inflater)); - - if (inflater == NULL) { - return NGHTTP2_ERR_NOMEM; - } - - rv = nghttp2_hd_inflate_init(inflater, mem); - - if (rv != 0) { - nghttp2_mem_free(mem, inflater); - - return rv; - } - - *inflater_ptr = inflater; - - return 0; -} - -void nghttp2_hd_inflate_del(nghttp2_hd_inflater *inflater) { - nghttp2_mem *mem; - - mem = inflater->ctx.mem; - nghttp2_hd_inflate_free(inflater); - - nghttp2_mem_free(mem, inflater); -} - -int nghttp2_hd_emit_indname_block(nghttp2_bufs *bufs, size_t idx, - nghttp2_nv *nv, int indexing_mode) { - - return emit_indname_block(bufs, idx, nv, indexing_mode); -} - -int nghttp2_hd_emit_newname_block(nghttp2_bufs *bufs, nghttp2_nv *nv, - int indexing_mode) { - return emit_newname_block(bufs, nv, indexing_mode); -} - -int nghttp2_hd_emit_table_size(nghttp2_bufs *bufs, size_t table_size) { - return emit_table_size(bufs, table_size); -} - -ssize_t nghttp2_hd_decode_length(uint32_t *res, size_t *shift_ptr, int *final, - uint32_t initial, size_t shift, uint8_t *in, - uint8_t *last, size_t prefix) { - return decode_length(res, shift_ptr, final, initial, shift, in, last, prefix); -} diff --git a/lib/nghttp2_hd.h b/lib/nghttp2_hd.h deleted file mode 100644 index 1b9ca7e..0000000 --- a/lib/nghttp2_hd.h +++ /dev/null @@ -1,421 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2013 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#ifndef NGHTTP2_HD_H -#define NGHTTP2_HD_H - -#ifdef HAVE_CONFIG_H -#include -#endif /* HAVE_CONFIG_H */ - -#include - -#include "nghttp2_hd_huffman.h" -#include "nghttp2_buf.h" -#include "nghttp2_mem.h" - -#define NGHTTP2_HD_DEFAULT_MAX_BUFFER_SIZE NGHTTP2_DEFAULT_HEADER_TABLE_SIZE -#define NGHTTP2_HD_ENTRY_OVERHEAD 32 - -/* The maximum length of one name/value pair. This is the sum of the - length of name and value. This is not specified by the spec. We - just chose the arbitrary size */ -#define NGHTTP2_HD_MAX_NV 65536 - -/* Default size of maximum table buffer size for encoder. Even if - remote decoder notifies larger buffer size for its decoding, - encoder only uses the memory up to this value. */ -#define NGHTTP2_HD_DEFAULT_MAX_DEFLATE_BUFFER_SIZE (1 << 12) - -/* Exported for unit test */ -#define NGHTTP2_STATIC_TABLE_LENGTH 61 - -/* Generated by genlibtokenlookup.py */ -typedef enum { - NGHTTP2_TOKEN__AUTHORITY = 0, - NGHTTP2_TOKEN__METHOD = 1, - NGHTTP2_TOKEN__PATH = 3, - NGHTTP2_TOKEN__SCHEME = 5, - NGHTTP2_TOKEN__STATUS = 7, - NGHTTP2_TOKEN_ACCEPT_CHARSET = 14, - NGHTTP2_TOKEN_ACCEPT_ENCODING = 15, - NGHTTP2_TOKEN_ACCEPT_LANGUAGE = 16, - NGHTTP2_TOKEN_ACCEPT_RANGES = 17, - NGHTTP2_TOKEN_ACCEPT = 18, - NGHTTP2_TOKEN_ACCESS_CONTROL_ALLOW_ORIGIN = 19, - NGHTTP2_TOKEN_AGE = 20, - NGHTTP2_TOKEN_ALLOW = 21, - NGHTTP2_TOKEN_AUTHORIZATION = 22, - NGHTTP2_TOKEN_CACHE_CONTROL = 23, - NGHTTP2_TOKEN_CONTENT_DISPOSITION = 24, - NGHTTP2_TOKEN_CONTENT_ENCODING = 25, - NGHTTP2_TOKEN_CONTENT_LANGUAGE = 26, - NGHTTP2_TOKEN_CONTENT_LENGTH = 27, - NGHTTP2_TOKEN_CONTENT_LOCATION = 28, - NGHTTP2_TOKEN_CONTENT_RANGE = 29, - NGHTTP2_TOKEN_CONTENT_TYPE = 30, - NGHTTP2_TOKEN_COOKIE = 31, - NGHTTP2_TOKEN_DATE = 32, - NGHTTP2_TOKEN_ETAG = 33, - NGHTTP2_TOKEN_EXPECT = 34, - NGHTTP2_TOKEN_EXPIRES = 35, - NGHTTP2_TOKEN_FROM = 36, - NGHTTP2_TOKEN_HOST = 37, - NGHTTP2_TOKEN_IF_MATCH = 38, - NGHTTP2_TOKEN_IF_MODIFIED_SINCE = 39, - NGHTTP2_TOKEN_IF_NONE_MATCH = 40, - NGHTTP2_TOKEN_IF_RANGE = 41, - NGHTTP2_TOKEN_IF_UNMODIFIED_SINCE = 42, - NGHTTP2_TOKEN_LAST_MODIFIED = 43, - NGHTTP2_TOKEN_LINK = 44, - NGHTTP2_TOKEN_LOCATION = 45, - NGHTTP2_TOKEN_MAX_FORWARDS = 46, - NGHTTP2_TOKEN_PROXY_AUTHENTICATE = 47, - NGHTTP2_TOKEN_PROXY_AUTHORIZATION = 48, - NGHTTP2_TOKEN_RANGE = 49, - NGHTTP2_TOKEN_REFERER = 50, - NGHTTP2_TOKEN_REFRESH = 51, - NGHTTP2_TOKEN_RETRY_AFTER = 52, - NGHTTP2_TOKEN_SERVER = 53, - NGHTTP2_TOKEN_SET_COOKIE = 54, - NGHTTP2_TOKEN_STRICT_TRANSPORT_SECURITY = 55, - NGHTTP2_TOKEN_TRANSFER_ENCODING = 56, - NGHTTP2_TOKEN_USER_AGENT = 57, - NGHTTP2_TOKEN_VARY = 58, - NGHTTP2_TOKEN_VIA = 59, - NGHTTP2_TOKEN_WWW_AUTHENTICATE = 60, - NGHTTP2_TOKEN_TE, - NGHTTP2_TOKEN_CONNECTION, - NGHTTP2_TOKEN_KEEP_ALIVE, - NGHTTP2_TOKEN_PROXY_CONNECTION, - NGHTTP2_TOKEN_UPGRADE -} nghttp2_token; - -typedef enum { - NGHTTP2_HD_FLAG_NONE = 0, - /* Indicates name was dynamically allocated and must be freed */ - NGHTTP2_HD_FLAG_NAME_ALLOC = 1, - /* Indicates value was dynamically allocated and must be freed */ - NGHTTP2_HD_FLAG_VALUE_ALLOC = 1 << 1, - /* Indicates that the name was gifted to the entry and no copying - necessary. */ - NGHTTP2_HD_FLAG_NAME_GIFT = 1 << 2, - /* Indicates that the value was gifted to the entry and no copying - necessary. */ - NGHTTP2_HD_FLAG_VALUE_GIFT = 1 << 3 -} nghttp2_hd_flags; - -typedef struct { - nghttp2_nv nv; - /* nghttp2_token value for nv.name. It could be -1 if we have no - token for that header field name. */ - int token; - /* Reference count */ - uint8_t ref; - uint8_t flags; -} nghttp2_hd_entry; - -typedef struct { - nghttp2_hd_entry **buffer; - size_t mask; - size_t first; - size_t len; -} nghttp2_hd_ringbuf; - -typedef enum { - NGHTTP2_HD_OPCODE_NONE, - NGHTTP2_HD_OPCODE_INDEXED, - NGHTTP2_HD_OPCODE_NEWNAME, - NGHTTP2_HD_OPCODE_INDNAME -} nghttp2_hd_opcode; - -typedef enum { - NGHTTP2_HD_STATE_OPCODE, - NGHTTP2_HD_STATE_READ_TABLE_SIZE, - NGHTTP2_HD_STATE_READ_INDEX, - NGHTTP2_HD_STATE_NEWNAME_CHECK_NAMELEN, - NGHTTP2_HD_STATE_NEWNAME_READ_NAMELEN, - NGHTTP2_HD_STATE_NEWNAME_READ_NAMEHUFF, - NGHTTP2_HD_STATE_NEWNAME_READ_NAME, - NGHTTP2_HD_STATE_CHECK_VALUELEN, - NGHTTP2_HD_STATE_READ_VALUELEN, - NGHTTP2_HD_STATE_READ_VALUEHUFF, - NGHTTP2_HD_STATE_READ_VALUE -} nghttp2_hd_inflate_state; - -typedef enum { - NGHTTP2_HD_WITH_INDEXING, - NGHTTP2_HD_WITHOUT_INDEXING, - NGHTTP2_HD_NEVER_INDEXING -} nghttp2_hd_indexing_mode; - -typedef struct { - /* dynamic header table */ - nghttp2_hd_ringbuf hd_table; - /* Memory allocator */ - nghttp2_mem *mem; - /* Abstract buffer size of hd_table as described in the spec. This - is the sum of length of name/value in hd_table + - NGHTTP2_HD_ENTRY_OVERHEAD bytes overhead per each entry. */ - size_t hd_table_bufsize; - /* The effective header table size. */ - size_t hd_table_bufsize_max; - /* If inflate/deflate error occurred, this value is set to 1 and - further invocation of inflate/deflate will fail with - NGHTTP2_ERR_HEADER_COMP. */ - uint8_t bad; -} nghttp2_hd_context; - -struct nghttp2_hd_deflater { - nghttp2_hd_context ctx; - /* The upper limit of the header table size the deflater accepts. */ - size_t deflate_hd_table_bufsize_max; - /* Minimum header table size notified in the next context update */ - size_t min_hd_table_bufsize_max; - /* If nonzero, send header table size using encoding context update - in the next deflate process */ - uint8_t notify_table_size_change; -}; - -struct nghttp2_hd_inflater { - nghttp2_hd_context ctx; - /* header buffer */ - nghttp2_bufs nvbufs; - /* Stores current state of huffman decoding */ - nghttp2_hd_huff_decode_context huff_decode_ctx; - /* Pointer to the nghttp2_hd_entry which is used current header - emission. This is required because in some cases the - ent_keep->ref == 0 and we have to keep track of it. */ - nghttp2_hd_entry *ent_keep; - /* Pointer to the name/value pair buffer which is used in the - current header emission. */ - uint8_t *nv_keep; - /* The number of bytes to read */ - size_t left; - /* The index in indexed repr or indexed name */ - size_t index; - /* The length of new name encoded in literal. For huffman encoded - string, this is the length after it is decoded. */ - size_t newnamelen; - /* The maximum header table size the inflater supports. This is the - same value transmitted in SETTINGS_HEADER_TABLE_SIZE */ - size_t settings_hd_table_bufsize_max; - /* The number of next shift to decode integer */ - size_t shift; - nghttp2_hd_opcode opcode; - nghttp2_hd_inflate_state state; - /* nonzero if string is huffman encoded */ - uint8_t huffman_encoded; - /* nonzero if deflater requires that current entry is indexed */ - uint8_t index_required; - /* nonzero if deflater requires that current entry must not be - indexed */ - uint8_t no_index; -}; - -/* - * Initializes the |ent| members. If NGHTTP2_HD_FLAG_NAME_ALLOC bit - * set in the |flags|, the content pointed by the |name| with length - * |namelen| is copied. Likewise, if NGHTTP2_HD_FLAG_VALUE_ALLOC bit - * set in the |flags|, the content pointed by the |value| with length - * |valuelen| is copied. The |token| is enum number looked up by - * |name|. It could be -1 if we don't have that enum value. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP2_ERR_NOMEM - * Out of memory. - */ -int nghttp2_hd_entry_init(nghttp2_hd_entry *ent, uint8_t flags, uint8_t *name, - size_t namelen, uint8_t *value, size_t valuelen, - int token, nghttp2_mem *mem); - -void nghttp2_hd_entry_free(nghttp2_hd_entry *ent, nghttp2_mem *mem); - -/* - * Initializes |deflater| for deflating name/values pairs. - * - * The encoder only uses up to - * NGHTTP2_HD_DEFAULT_MAX_DEFLATE_BUFFER_SIZE bytes for header table - * even if the larger value is specified later in - * nghttp2_hd_change_table_size(). - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP2_ERR_NOMEM - * Out of memory. - */ -int nghttp2_hd_deflate_init(nghttp2_hd_deflater *deflater, nghttp2_mem *mem); - -/* - * Initializes |deflater| for deflating name/values pairs. - * - * The encoder only uses up to |deflate_hd_table_bufsize_max| bytes - * for header table even if the larger value is specified later in - * nghttp2_hd_change_table_size(). - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP2_ERR_NOMEM - * Out of memory. - */ -int nghttp2_hd_deflate_init2(nghttp2_hd_deflater *deflater, - size_t deflate_hd_table_bufsize_max, - nghttp2_mem *mem); - -/* - * Deallocates any resources allocated for |deflater|. - */ -void nghttp2_hd_deflate_free(nghttp2_hd_deflater *deflater); - -/* - * Deflates the |nva|, which has the |nvlen| name/value pairs, into - * the |bufs|. - * - * This function expands |bufs| as necessary to store the result. If - * buffers is full and the process still requires more space, this - * funtion fails and returns NGHTTP2_ERR_HEADER_COMP. - * - * After this function returns, it is safe to delete the |nva|. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP2_ERR_NOMEM - * Out of memory. - * NGHTTP2_ERR_HEADER_COMP - * Deflation process has failed. - * NGHTTP2_ERR_BUFFER_ERROR - * Out of buffer space. - */ -int nghttp2_hd_deflate_hd_bufs(nghttp2_hd_deflater *deflater, - nghttp2_bufs *bufs, const nghttp2_nv *nva, - size_t nvlen); - -/* - * Initializes |inflater| for inflating name/values pairs. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * :enum:`NGHTTP2_ERR_NOMEM` - * Out of memory. - */ -int nghttp2_hd_inflate_init(nghttp2_hd_inflater *inflater, nghttp2_mem *mem); - -/* - * Deallocates any resources allocated for |inflater|. - */ -void nghttp2_hd_inflate_free(nghttp2_hd_inflater *inflater); - -/* - * Similar to nghttp2_hd_inflate_hd(), but this takes additional - * output parameter |token|. On successful header emission, it - * contains nghttp2_token value for nv_out->name. It could be -1 if - * we don't have enum value for the name. Other than that return - * values and semantics are the same as nghttp2_hd_inflate_hd(). - */ -ssize_t nghttp2_hd_inflate_hd2(nghttp2_hd_inflater *inflater, - nghttp2_nv *nv_out, int *inflate_flags, - int *token, uint8_t *in, size_t inlen, - int in_final); - -/* For unittesting purpose */ -int nghttp2_hd_emit_indname_block(nghttp2_bufs *bufs, size_t index, - nghttp2_nv *nv, int indexing_mode); - -/* For unittesting purpose */ -int nghttp2_hd_emit_newname_block(nghttp2_bufs *bufs, nghttp2_nv *nv, - int indexing_mode); - -/* For unittesting purpose */ -int nghttp2_hd_emit_table_size(nghttp2_bufs *bufs, size_t table_size); - -/* For unittesting purpose */ -nghttp2_hd_entry *nghttp2_hd_table_get(nghttp2_hd_context *context, - size_t index); - -/* For unittesting purpose */ -ssize_t nghttp2_hd_decode_length(uint32_t *res, size_t *shift_ptr, int *final, - uint32_t initial, size_t shift, uint8_t *in, - uint8_t *last, size_t prefix); - -/* Huffman encoding/decoding functions */ - -/* - * Counts the required bytes to encode |src| with length |len|. - * - * This function returns the number of required bytes to encode given - * data, including padding of prefix of terminal symbol code. This - * function always succeeds. - */ -size_t nghttp2_hd_huff_encode_count(const uint8_t *src, size_t len); - -/* - * Encodes the given data |src| with length |srclen| to the |bufs|. - * This function expands extra buffers in |bufs| if necessary. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP2_ERR_NOMEM - * Out of memory. - * NGHTTP2_ERR_BUFFER_ERROR - * Out of buffer space. - */ -int nghttp2_hd_huff_encode(nghttp2_bufs *bufs, const uint8_t *src, - size_t srclen); - -void nghttp2_hd_huff_decode_context_init(nghttp2_hd_huff_decode_context *ctx); - -/* - * Decodes the given data |src| with length |srclen|. The |ctx| must - * be initialized by nghttp2_hd_huff_decode_context_init(). The result - * will be added to |dest|. This function may expand |dest| as - * needed. The caller is responsible to release the memory of |dest| - * by calling nghttp2_bufs_free(). - * - * The caller must set the |final| to nonzero if the given input is - * the final block. - * - * This function returns the number of read bytes from the |in|. - * - * If this function fails, it returns one of the following negative - * return codes: - * - * NGHTTP2_ERR_NOMEM - * Out of memory. - * NGHTTP2_ERR_BUFFER_ERROR - * Maximum buffer capacity size exceeded. - * NGHTTP2_ERR_HEADER_COMP - * Decoding process has failed. - */ -ssize_t nghttp2_hd_huff_decode(nghttp2_hd_huff_decode_context *ctx, - nghttp2_bufs *bufs, const uint8_t *src, - size_t srclen, int final); - -#endif /* NGHTTP2_HD_H */ diff --git a/lib/nghttp2_hd_huffman.c b/lib/nghttp2_hd_huffman.c deleted file mode 100644 index 4df1cd0..0000000 --- a/lib/nghttp2_hd_huffman.c +++ /dev/null @@ -1,227 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2013 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#include "nghttp2_hd_huffman.h" - -#include -#include -#include - -#include "nghttp2_hd.h" - -extern const nghttp2_huff_sym huff_sym_table[]; -extern const nghttp2_huff_decode huff_decode_table[][16]; - -/* - * Encodes huffman code |sym| into |*dest_ptr|, whose least |rembits| - * bits are not filled yet. The |rembits| must be in range [1, 8], - * inclusive. At the end of the process, the |*dest_ptr| is updated - * and points where next output should be placed. The number of - * unfilled bits in the pointed location is returned. - */ -static ssize_t huff_encode_sym(nghttp2_bufs *bufs, size_t *avail_ptr, - size_t rembits, const nghttp2_huff_sym *sym) { - int rv; - size_t nbits = sym->nbits; - uint32_t code = sym->code; - - /* We assume that sym->nbits <= 32 */ - if (rembits > nbits) { - nghttp2_bufs_fast_orb_hold(bufs, code << (rembits - nbits)); - return (ssize_t)(rembits - nbits); - } - - if (rembits == nbits) { - nghttp2_bufs_fast_orb(bufs, code); - --*avail_ptr; - return 8; - } - - nghttp2_bufs_fast_orb(bufs, code >> (nbits - rembits)); - --*avail_ptr; - - nbits -= rembits; - if (nbits & 0x7) { - /* align code to MSB byte boundary */ - code <<= 8 - (nbits & 0x7); - } - - /* we lose at most 3 bytes, but it is not critical in practice */ - if (*avail_ptr < (nbits + 7) / 8) { - rv = nghttp2_bufs_advance(bufs); - if (rv != 0) { - return rv; - } - *avail_ptr = nghttp2_bufs_cur_avail(bufs); - /* we assume that we at least 3 buffer space available */ - assert(*avail_ptr >= 3); - } - - /* fast path, since most code is less than 8 */ - if (nbits < 8) { - nghttp2_bufs_fast_addb_hold(bufs, code); - *avail_ptr = nghttp2_bufs_cur_avail(bufs); - return (ssize_t)(8 - nbits); - } - - /* handle longer code path */ - if (nbits > 24) { - nghttp2_bufs_fast_addb(bufs, code >> 24); - nbits -= 8; - } - - if (nbits > 16) { - nghttp2_bufs_fast_addb(bufs, code >> 16); - nbits -= 8; - } - - if (nbits > 8) { - nghttp2_bufs_fast_addb(bufs, code >> 8); - nbits -= 8; - } - - if (nbits == 8) { - nghttp2_bufs_fast_addb(bufs, code); - *avail_ptr = nghttp2_bufs_cur_avail(bufs); - return 8; - } - - nghttp2_bufs_fast_addb_hold(bufs, code); - *avail_ptr = nghttp2_bufs_cur_avail(bufs); - return (ssize_t)(8 - nbits); -} - -size_t nghttp2_hd_huff_encode_count(const uint8_t *src, size_t len) { - size_t i; - size_t nbits = 0; - - for (i = 0; i < len; ++i) { - nbits += huff_sym_table[src[i]].nbits; - } - /* pad the prefix of EOS (256) */ - return (nbits + 7) / 8; -} - -int nghttp2_hd_huff_encode(nghttp2_bufs *bufs, const uint8_t *src, - size_t srclen) { - int rv; - ssize_t rembits = 8; - size_t i; - size_t avail; - - avail = nghttp2_bufs_cur_avail(bufs); - - for (i = 0; i < srclen; ++i) { - const nghttp2_huff_sym *sym = &huff_sym_table[src[i]]; - if (rembits == 8) { - if (avail) { - nghttp2_bufs_fast_addb_hold(bufs, 0); - } else { - rv = nghttp2_bufs_addb_hold(bufs, 0); - if (rv != 0) { - return rv; - } - avail = nghttp2_bufs_cur_avail(bufs); - } - } - rembits = huff_encode_sym(bufs, &avail, rembits, sym); - if (rembits < 0) { - return (int)rembits; - } - } - /* 256 is special terminal symbol, pad with its prefix */ - if (rembits < 8) { - /* if rembits < 8, we should have at least 1 buffer space - available */ - const nghttp2_huff_sym *sym = &huff_sym_table[256]; - assert(avail); - /* Caution we no longer adjust avail here */ - nghttp2_bufs_fast_orb(bufs, sym->code >> (sym->nbits - rembits)); - } - - return 0; -} - -void nghttp2_hd_huff_decode_context_init(nghttp2_hd_huff_decode_context *ctx) { - ctx->state = 0; - ctx->accept = 1; -} - -/* Use macro to make the code simpler..., but error case is tricky. - We spent most of the CPU in decoding, so we are doing this - thing. */ -#define hd_huff_decode_sym_emit(bufs, sym, avail) \ - do { \ - if ((avail)) { \ - nghttp2_bufs_fast_addb((bufs), (sym)); \ - --(avail); \ - } else { \ - rv = nghttp2_bufs_addb((bufs), (sym)); \ - if (rv != 0) { \ - return rv; \ - } \ - (avail) = nghttp2_bufs_cur_avail((bufs)); \ - } \ - } while (0) - -ssize_t nghttp2_hd_huff_decode(nghttp2_hd_huff_decode_context *ctx, - nghttp2_bufs *bufs, const uint8_t *src, - size_t srclen, int final) { - size_t i; - int rv; - size_t avail; - - avail = nghttp2_bufs_cur_avail(bufs); - - /* We use the decoding algorithm described in - http://graphics.ics.uci.edu/pub/Prefix.pdf */ - for (i = 0; i < srclen; ++i) { - const nghttp2_huff_decode *t; - - t = &huff_decode_table[ctx->state][src[i] >> 4]; - if (t->flags & NGHTTP2_HUFF_FAIL) { - return NGHTTP2_ERR_HEADER_COMP; - } - if (t->flags & NGHTTP2_HUFF_SYM) { - /* this is macro, and may return from this function on error */ - hd_huff_decode_sym_emit(bufs, t->sym, avail); - } - - t = &huff_decode_table[t->state][src[i] & 0xf]; - if (t->flags & NGHTTP2_HUFF_FAIL) { - return NGHTTP2_ERR_HEADER_COMP; - } - if (t->flags & NGHTTP2_HUFF_SYM) { - /* this is macro, and may return from this function on error */ - hd_huff_decode_sym_emit(bufs, t->sym, avail); - } - - ctx->state = t->state; - ctx->accept = (t->flags & NGHTTP2_HUFF_ACCEPTED) != 0; - } - if (final && !ctx->accept) { - return NGHTTP2_ERR_HEADER_COMP; - } - return (ssize_t)i; -} diff --git a/lib/nghttp2_hd_huffman.h b/lib/nghttp2_hd_huffman.h deleted file mode 100644 index 714b6b6..0000000 --- a/lib/nghttp2_hd_huffman.h +++ /dev/null @@ -1,74 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2013 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#ifndef NGHTTP2_HD_HUFFMAN_H -#define NGHTTP2_HD_HUFFMAN_H - -#ifdef HAVE_CONFIG_H -#include -#endif /* HAVE_CONFIG_H */ - -#include - -typedef enum { - /* FSA accepts this state as the end of huffman encoding - sequence. */ - NGHTTP2_HUFF_ACCEPTED = 1, - /* This state emits symbol */ - NGHTTP2_HUFF_SYM = (1 << 1), - /* If state machine reaches this state, decoding fails. */ - NGHTTP2_HUFF_FAIL = (1 << 2) -} nghttp2_huff_decode_flag; - -typedef struct { - /* huffman decoding state, which is actually the node ID of internal - huffman tree. We have 257 leaf nodes, but they are identical to - root node other than emitting a symbol, so we have 256 internal - nodes [1..255], inclusive. */ - uint8_t state; - /* bitwise OR of zero or more of the nghttp2_huff_decode_flag */ - uint8_t flags; - /* symbol if NGHTTP2_HUFF_SYM flag set */ - uint8_t sym; -} nghttp2_huff_decode; - -typedef nghttp2_huff_decode huff_decode_table_type[16]; - -typedef struct { - /* Current huffman decoding state. We stripped leaf nodes, so the - value range is [0..255], inclusive. */ - uint8_t state; - /* nonzero if we can say that the decoding process succeeds at this - state */ - uint8_t accept; -} nghttp2_hd_huff_decode_context; - -typedef struct { - /* The number of bits in this code */ - uint32_t nbits; - /* Huffman code aligned to LSB */ - uint32_t code; -} nghttp2_huff_sym; - -#endif /* NGHTTP2_HD_HUFFMAN_H */ diff --git a/lib/nghttp2_hd_huffman_data.c b/lib/nghttp2_hd_huffman_data.c deleted file mode 100644 index 4a4251b..0000000 --- a/lib/nghttp2_hd_huffman_data.c +++ /dev/null @@ -1,5152 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2013 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#include "nghttp2_hd_huffman.h" - -/* Generated by mkhufftbl.py */ - -const nghttp2_huff_sym huff_sym_table[] = {{13, 0x1ff8u}, - {23, 0x7fffd8u}, - {28, 0xfffffe2u}, - {28, 0xfffffe3u}, - {28, 0xfffffe4u}, - {28, 0xfffffe5u}, - {28, 0xfffffe6u}, - {28, 0xfffffe7u}, - {28, 0xfffffe8u}, - {24, 0xffffeau}, - {30, 0x3ffffffcu}, - {28, 0xfffffe9u}, - {28, 0xfffffeau}, - {30, 0x3ffffffdu}, - {28, 0xfffffebu}, - {28, 0xfffffecu}, - {28, 0xfffffedu}, - {28, 0xfffffeeu}, - {28, 0xfffffefu}, - {28, 0xffffff0u}, - {28, 0xffffff1u}, - {28, 0xffffff2u}, - {30, 0x3ffffffeu}, - {28, 0xffffff3u}, - {28, 0xffffff4u}, - {28, 0xffffff5u}, - {28, 0xffffff6u}, - {28, 0xffffff7u}, - {28, 0xffffff8u}, - {28, 0xffffff9u}, - {28, 0xffffffau}, - {28, 0xffffffbu}, - {6, 0x14u}, - {10, 0x3f8u}, - {10, 0x3f9u}, - {12, 0xffau}, - {13, 0x1ff9u}, - {6, 0x15u}, - {8, 0xf8u}, - {11, 0x7fau}, - {10, 0x3fau}, - {10, 0x3fbu}, - {8, 0xf9u}, - {11, 0x7fbu}, - {8, 0xfau}, - {6, 0x16u}, - {6, 0x17u}, - {6, 0x18u}, - {5, 0x0u}, - {5, 0x1u}, - {5, 0x2u}, - {6, 0x19u}, - {6, 0x1au}, - {6, 0x1bu}, - {6, 0x1cu}, - {6, 0x1du}, - {6, 0x1eu}, - {6, 0x1fu}, - {7, 0x5cu}, - {8, 0xfbu}, - {15, 0x7ffcu}, - {6, 0x20u}, - {12, 0xffbu}, - {10, 0x3fcu}, - {13, 0x1ffau}, - {6, 0x21u}, - {7, 0x5du}, - {7, 0x5eu}, - {7, 0x5fu}, - {7, 0x60u}, - {7, 0x61u}, - {7, 0x62u}, - {7, 0x63u}, - {7, 0x64u}, - {7, 0x65u}, - {7, 0x66u}, - {7, 0x67u}, - {7, 0x68u}, - {7, 0x69u}, - {7, 0x6au}, - {7, 0x6bu}, - {7, 0x6cu}, - {7, 0x6du}, - {7, 0x6eu}, - {7, 0x6fu}, - {7, 0x70u}, - {7, 0x71u}, - {7, 0x72u}, - {8, 0xfcu}, - {7, 0x73u}, - {8, 0xfdu}, - {13, 0x1ffbu}, - {19, 0x7fff0u}, - {13, 0x1ffcu}, - {14, 0x3ffcu}, - {6, 0x22u}, - {15, 0x7ffdu}, - {5, 0x3u}, - {6, 0x23u}, - {5, 0x4u}, - {6, 0x24u}, - {5, 0x5u}, - {6, 0x25u}, - {6, 0x26u}, - {6, 0x27u}, - {5, 0x6u}, - {7, 0x74u}, - {7, 0x75u}, - {6, 0x28u}, - {6, 0x29u}, - {6, 0x2au}, - {5, 0x7u}, - {6, 0x2bu}, - {7, 0x76u}, - {6, 0x2cu}, - {5, 0x8u}, - {5, 0x9u}, - {6, 0x2du}, - {7, 0x77u}, - {7, 0x78u}, - {7, 0x79u}, - {7, 0x7au}, - {7, 0x7bu}, - {15, 0x7ffeu}, - {11, 0x7fcu}, - {14, 0x3ffdu}, - {13, 0x1ffdu}, - {28, 0xffffffcu}, - {20, 0xfffe6u}, - {22, 0x3fffd2u}, - {20, 0xfffe7u}, - {20, 0xfffe8u}, - {22, 0x3fffd3u}, - {22, 0x3fffd4u}, - {22, 0x3fffd5u}, - {23, 0x7fffd9u}, - {22, 0x3fffd6u}, - {23, 0x7fffdau}, - {23, 0x7fffdbu}, - {23, 0x7fffdcu}, - {23, 0x7fffddu}, - {23, 0x7fffdeu}, - {24, 0xffffebu}, - {23, 0x7fffdfu}, - {24, 0xffffecu}, - {24, 0xffffedu}, - {22, 0x3fffd7u}, - {23, 0x7fffe0u}, - {24, 0xffffeeu}, - {23, 0x7fffe1u}, - {23, 0x7fffe2u}, - {23, 0x7fffe3u}, - {23, 0x7fffe4u}, - {21, 0x1fffdcu}, - {22, 0x3fffd8u}, - {23, 0x7fffe5u}, - {22, 0x3fffd9u}, - {23, 0x7fffe6u}, - {23, 0x7fffe7u}, - {24, 0xffffefu}, - {22, 0x3fffdau}, - {21, 0x1fffddu}, - {20, 0xfffe9u}, - {22, 0x3fffdbu}, - {22, 0x3fffdcu}, - {23, 0x7fffe8u}, - {23, 0x7fffe9u}, - {21, 0x1fffdeu}, - {23, 0x7fffeau}, - {22, 0x3fffddu}, - {22, 0x3fffdeu}, - {24, 0xfffff0u}, - {21, 0x1fffdfu}, - {22, 0x3fffdfu}, - {23, 0x7fffebu}, - {23, 0x7fffecu}, - {21, 0x1fffe0u}, - {21, 0x1fffe1u}, - {22, 0x3fffe0u}, - {21, 0x1fffe2u}, - {23, 0x7fffedu}, - {22, 0x3fffe1u}, - {23, 0x7fffeeu}, - {23, 0x7fffefu}, - {20, 0xfffeau}, - {22, 0x3fffe2u}, - {22, 0x3fffe3u}, - {22, 0x3fffe4u}, - {23, 0x7ffff0u}, - {22, 0x3fffe5u}, - {22, 0x3fffe6u}, - {23, 0x7ffff1u}, - {26, 0x3ffffe0u}, - {26, 0x3ffffe1u}, - {20, 0xfffebu}, - {19, 0x7fff1u}, - {22, 0x3fffe7u}, - {23, 0x7ffff2u}, - {22, 0x3fffe8u}, - {25, 0x1ffffecu}, - {26, 0x3ffffe2u}, - {26, 0x3ffffe3u}, - {26, 0x3ffffe4u}, - {27, 0x7ffffdeu}, - {27, 0x7ffffdfu}, - {26, 0x3ffffe5u}, - {24, 0xfffff1u}, - {25, 0x1ffffedu}, - {19, 0x7fff2u}, - {21, 0x1fffe3u}, - {26, 0x3ffffe6u}, - {27, 0x7ffffe0u}, - {27, 0x7ffffe1u}, - {26, 0x3ffffe7u}, - {27, 0x7ffffe2u}, - {24, 0xfffff2u}, - {21, 0x1fffe4u}, - {21, 0x1fffe5u}, - {26, 0x3ffffe8u}, - {26, 0x3ffffe9u}, - {28, 0xffffffdu}, - {27, 0x7ffffe3u}, - {27, 0x7ffffe4u}, - {27, 0x7ffffe5u}, - {20, 0xfffecu}, - {24, 0xfffff3u}, - {20, 0xfffedu}, - {21, 0x1fffe6u}, - {22, 0x3fffe9u}, - {21, 0x1fffe7u}, - {21, 0x1fffe8u}, - {23, 0x7ffff3u}, - {22, 0x3fffeau}, - {22, 0x3fffebu}, - {25, 0x1ffffeeu}, - {25, 0x1ffffefu}, - {24, 0xfffff4u}, - {24, 0xfffff5u}, - {26, 0x3ffffeau}, - {23, 0x7ffff4u}, - {26, 0x3ffffebu}, - {27, 0x7ffffe6u}, - {26, 0x3ffffecu}, - {26, 0x3ffffedu}, - {27, 0x7ffffe7u}, - {27, 0x7ffffe8u}, - {27, 0x7ffffe9u}, - {27, 0x7ffffeau}, - {27, 0x7ffffebu}, - {28, 0xffffffeu}, - {27, 0x7ffffecu}, - {27, 0x7ffffedu}, - {27, 0x7ffffeeu}, - {27, 0x7ffffefu}, - {27, 0x7fffff0u}, - {26, 0x3ffffeeu}, - {30, 0x3fffffffu}}; - -const nghttp2_huff_decode huff_decode_table[][16] = { - /* 0 */ - { - {4, 0x00, 0}, - {5, 0x00, 0}, - {7, 0x00, 0}, - {8, 0x00, 0}, - {11, 0x00, 0}, - {12, 0x00, 0}, - {16, 0x00, 0}, - {19, 0x00, 0}, - {25, 0x00, 0}, - {28, 0x00, 0}, - {32, 0x00, 0}, - {35, 0x00, 0}, - {42, 0x00, 0}, - {49, 0x00, 0}, - {57, 0x00, 0}, - {64, 0x01, 0}, - }, - /* 1 */ - { - {0, 0x03, 48}, - {0, 0x03, 49}, - {0, 0x03, 50}, - {0, 0x03, 97}, - {0, 0x03, 99}, - {0, 0x03, 101}, - {0, 0x03, 105}, - {0, 0x03, 111}, - {0, 0x03, 115}, - {0, 0x03, 116}, - {13, 0x00, 0}, - {14, 0x00, 0}, - {17, 0x00, 0}, - {18, 0x00, 0}, - {20, 0x00, 0}, - {21, 0x00, 0}, - }, - /* 2 */ - { - {1, 0x02, 48}, - {22, 0x03, 48}, - {1, 0x02, 49}, - {22, 0x03, 49}, - {1, 0x02, 50}, - {22, 0x03, 50}, - {1, 0x02, 97}, - {22, 0x03, 97}, - {1, 0x02, 99}, - {22, 0x03, 99}, - {1, 0x02, 101}, - {22, 0x03, 101}, - {1, 0x02, 105}, - {22, 0x03, 105}, - {1, 0x02, 111}, - {22, 0x03, 111}, - }, - /* 3 */ - { - {2, 0x02, 48}, - {9, 0x02, 48}, - {23, 0x02, 48}, - {40, 0x03, 48}, - {2, 0x02, 49}, - {9, 0x02, 49}, - {23, 0x02, 49}, - {40, 0x03, 49}, - {2, 0x02, 50}, - {9, 0x02, 50}, - {23, 0x02, 50}, - {40, 0x03, 50}, - {2, 0x02, 97}, - {9, 0x02, 97}, - {23, 0x02, 97}, - {40, 0x03, 97}, - }, - /* 4 */ - { - {3, 0x02, 48}, - {6, 0x02, 48}, - {10, 0x02, 48}, - {15, 0x02, 48}, - {24, 0x02, 48}, - {31, 0x02, 48}, - {41, 0x02, 48}, - {56, 0x03, 48}, - {3, 0x02, 49}, - {6, 0x02, 49}, - {10, 0x02, 49}, - {15, 0x02, 49}, - {24, 0x02, 49}, - {31, 0x02, 49}, - {41, 0x02, 49}, - {56, 0x03, 49}, - }, - /* 5 */ - { - {3, 0x02, 50}, - {6, 0x02, 50}, - {10, 0x02, 50}, - {15, 0x02, 50}, - {24, 0x02, 50}, - {31, 0x02, 50}, - {41, 0x02, 50}, - {56, 0x03, 50}, - {3, 0x02, 97}, - {6, 0x02, 97}, - {10, 0x02, 97}, - {15, 0x02, 97}, - {24, 0x02, 97}, - {31, 0x02, 97}, - {41, 0x02, 97}, - {56, 0x03, 97}, - }, - /* 6 */ - { - {2, 0x02, 99}, - {9, 0x02, 99}, - {23, 0x02, 99}, - {40, 0x03, 99}, - {2, 0x02, 101}, - {9, 0x02, 101}, - {23, 0x02, 101}, - {40, 0x03, 101}, - {2, 0x02, 105}, - {9, 0x02, 105}, - {23, 0x02, 105}, - {40, 0x03, 105}, - {2, 0x02, 111}, - {9, 0x02, 111}, - {23, 0x02, 111}, - {40, 0x03, 111}, - }, - /* 7 */ - { - {3, 0x02, 99}, - {6, 0x02, 99}, - {10, 0x02, 99}, - {15, 0x02, 99}, - {24, 0x02, 99}, - {31, 0x02, 99}, - {41, 0x02, 99}, - {56, 0x03, 99}, - {3, 0x02, 101}, - {6, 0x02, 101}, - {10, 0x02, 101}, - {15, 0x02, 101}, - {24, 0x02, 101}, - {31, 0x02, 101}, - {41, 0x02, 101}, - {56, 0x03, 101}, - }, - /* 8 */ - { - {3, 0x02, 105}, - {6, 0x02, 105}, - {10, 0x02, 105}, - {15, 0x02, 105}, - {24, 0x02, 105}, - {31, 0x02, 105}, - {41, 0x02, 105}, - {56, 0x03, 105}, - {3, 0x02, 111}, - {6, 0x02, 111}, - {10, 0x02, 111}, - {15, 0x02, 111}, - {24, 0x02, 111}, - {31, 0x02, 111}, - {41, 0x02, 111}, - {56, 0x03, 111}, - }, - /* 9 */ - { - {1, 0x02, 115}, - {22, 0x03, 115}, - {1, 0x02, 116}, - {22, 0x03, 116}, - {0, 0x03, 32}, - {0, 0x03, 37}, - {0, 0x03, 45}, - {0, 0x03, 46}, - {0, 0x03, 47}, - {0, 0x03, 51}, - {0, 0x03, 52}, - {0, 0x03, 53}, - {0, 0x03, 54}, - {0, 0x03, 55}, - {0, 0x03, 56}, - {0, 0x03, 57}, - }, - /* 10 */ - { - {2, 0x02, 115}, - {9, 0x02, 115}, - {23, 0x02, 115}, - {40, 0x03, 115}, - {2, 0x02, 116}, - {9, 0x02, 116}, - {23, 0x02, 116}, - {40, 0x03, 116}, - {1, 0x02, 32}, - {22, 0x03, 32}, - {1, 0x02, 37}, - {22, 0x03, 37}, - {1, 0x02, 45}, - {22, 0x03, 45}, - {1, 0x02, 46}, - {22, 0x03, 46}, - }, - /* 11 */ - { - {3, 0x02, 115}, - {6, 0x02, 115}, - {10, 0x02, 115}, - {15, 0x02, 115}, - {24, 0x02, 115}, - {31, 0x02, 115}, - {41, 0x02, 115}, - {56, 0x03, 115}, - {3, 0x02, 116}, - {6, 0x02, 116}, - {10, 0x02, 116}, - {15, 0x02, 116}, - {24, 0x02, 116}, - {31, 0x02, 116}, - {41, 0x02, 116}, - {56, 0x03, 116}, - }, - /* 12 */ - { - {2, 0x02, 32}, - {9, 0x02, 32}, - {23, 0x02, 32}, - {40, 0x03, 32}, - {2, 0x02, 37}, - {9, 0x02, 37}, - {23, 0x02, 37}, - {40, 0x03, 37}, - {2, 0x02, 45}, - {9, 0x02, 45}, - {23, 0x02, 45}, - {40, 0x03, 45}, - {2, 0x02, 46}, - {9, 0x02, 46}, - {23, 0x02, 46}, - {40, 0x03, 46}, - }, - /* 13 */ - { - {3, 0x02, 32}, - {6, 0x02, 32}, - {10, 0x02, 32}, - {15, 0x02, 32}, - {24, 0x02, 32}, - {31, 0x02, 32}, - {41, 0x02, 32}, - {56, 0x03, 32}, - {3, 0x02, 37}, - {6, 0x02, 37}, - {10, 0x02, 37}, - {15, 0x02, 37}, - {24, 0x02, 37}, - {31, 0x02, 37}, - {41, 0x02, 37}, - {56, 0x03, 37}, - }, - /* 14 */ - { - {3, 0x02, 45}, - {6, 0x02, 45}, - {10, 0x02, 45}, - {15, 0x02, 45}, - {24, 0x02, 45}, - {31, 0x02, 45}, - {41, 0x02, 45}, - {56, 0x03, 45}, - {3, 0x02, 46}, - {6, 0x02, 46}, - {10, 0x02, 46}, - {15, 0x02, 46}, - {24, 0x02, 46}, - {31, 0x02, 46}, - {41, 0x02, 46}, - {56, 0x03, 46}, - }, - /* 15 */ - { - {1, 0x02, 47}, - {22, 0x03, 47}, - {1, 0x02, 51}, - {22, 0x03, 51}, - {1, 0x02, 52}, - {22, 0x03, 52}, - {1, 0x02, 53}, - {22, 0x03, 53}, - {1, 0x02, 54}, - {22, 0x03, 54}, - {1, 0x02, 55}, - {22, 0x03, 55}, - {1, 0x02, 56}, - {22, 0x03, 56}, - {1, 0x02, 57}, - {22, 0x03, 57}, - }, - /* 16 */ - { - {2, 0x02, 47}, - {9, 0x02, 47}, - {23, 0x02, 47}, - {40, 0x03, 47}, - {2, 0x02, 51}, - {9, 0x02, 51}, - {23, 0x02, 51}, - {40, 0x03, 51}, - {2, 0x02, 52}, - {9, 0x02, 52}, - {23, 0x02, 52}, - {40, 0x03, 52}, - {2, 0x02, 53}, - {9, 0x02, 53}, - {23, 0x02, 53}, - {40, 0x03, 53}, - }, - /* 17 */ - { - {3, 0x02, 47}, - {6, 0x02, 47}, - {10, 0x02, 47}, - {15, 0x02, 47}, - {24, 0x02, 47}, - {31, 0x02, 47}, - {41, 0x02, 47}, - {56, 0x03, 47}, - {3, 0x02, 51}, - {6, 0x02, 51}, - {10, 0x02, 51}, - {15, 0x02, 51}, - {24, 0x02, 51}, - {31, 0x02, 51}, - {41, 0x02, 51}, - {56, 0x03, 51}, - }, - /* 18 */ - { - {3, 0x02, 52}, - {6, 0x02, 52}, - {10, 0x02, 52}, - {15, 0x02, 52}, - {24, 0x02, 52}, - {31, 0x02, 52}, - {41, 0x02, 52}, - {56, 0x03, 52}, - {3, 0x02, 53}, - {6, 0x02, 53}, - {10, 0x02, 53}, - {15, 0x02, 53}, - {24, 0x02, 53}, - {31, 0x02, 53}, - {41, 0x02, 53}, - {56, 0x03, 53}, - }, - /* 19 */ - { - {2, 0x02, 54}, - {9, 0x02, 54}, - {23, 0x02, 54}, - {40, 0x03, 54}, - {2, 0x02, 55}, - {9, 0x02, 55}, - {23, 0x02, 55}, - {40, 0x03, 55}, - {2, 0x02, 56}, - {9, 0x02, 56}, - {23, 0x02, 56}, - {40, 0x03, 56}, - {2, 0x02, 57}, - {9, 0x02, 57}, - {23, 0x02, 57}, - {40, 0x03, 57}, - }, - /* 20 */ - { - {3, 0x02, 54}, - {6, 0x02, 54}, - {10, 0x02, 54}, - {15, 0x02, 54}, - {24, 0x02, 54}, - {31, 0x02, 54}, - {41, 0x02, 54}, - {56, 0x03, 54}, - {3, 0x02, 55}, - {6, 0x02, 55}, - {10, 0x02, 55}, - {15, 0x02, 55}, - {24, 0x02, 55}, - {31, 0x02, 55}, - {41, 0x02, 55}, - {56, 0x03, 55}, - }, - /* 21 */ - { - {3, 0x02, 56}, - {6, 0x02, 56}, - {10, 0x02, 56}, - {15, 0x02, 56}, - {24, 0x02, 56}, - {31, 0x02, 56}, - {41, 0x02, 56}, - {56, 0x03, 56}, - {3, 0x02, 57}, - {6, 0x02, 57}, - {10, 0x02, 57}, - {15, 0x02, 57}, - {24, 0x02, 57}, - {31, 0x02, 57}, - {41, 0x02, 57}, - {56, 0x03, 57}, - }, - /* 22 */ - { - {26, 0x00, 0}, - {27, 0x00, 0}, - {29, 0x00, 0}, - {30, 0x00, 0}, - {33, 0x00, 0}, - {34, 0x00, 0}, - {36, 0x00, 0}, - {37, 0x00, 0}, - {43, 0x00, 0}, - {46, 0x00, 0}, - {50, 0x00, 0}, - {53, 0x00, 0}, - {58, 0x00, 0}, - {61, 0x00, 0}, - {65, 0x00, 0}, - {68, 0x01, 0}, - }, - /* 23 */ - { - {0, 0x03, 61}, - {0, 0x03, 65}, - {0, 0x03, 95}, - {0, 0x03, 98}, - {0, 0x03, 100}, - {0, 0x03, 102}, - {0, 0x03, 103}, - {0, 0x03, 104}, - {0, 0x03, 108}, - {0, 0x03, 109}, - {0, 0x03, 110}, - {0, 0x03, 112}, - {0, 0x03, 114}, - {0, 0x03, 117}, - {38, 0x00, 0}, - {39, 0x00, 0}, - }, - /* 24 */ - { - {1, 0x02, 61}, - {22, 0x03, 61}, - {1, 0x02, 65}, - {22, 0x03, 65}, - {1, 0x02, 95}, - {22, 0x03, 95}, - {1, 0x02, 98}, - {22, 0x03, 98}, - {1, 0x02, 100}, - {22, 0x03, 100}, - {1, 0x02, 102}, - {22, 0x03, 102}, - {1, 0x02, 103}, - {22, 0x03, 103}, - {1, 0x02, 104}, - {22, 0x03, 104}, - }, - /* 25 */ - { - {2, 0x02, 61}, - {9, 0x02, 61}, - {23, 0x02, 61}, - {40, 0x03, 61}, - {2, 0x02, 65}, - {9, 0x02, 65}, - {23, 0x02, 65}, - {40, 0x03, 65}, - {2, 0x02, 95}, - {9, 0x02, 95}, - {23, 0x02, 95}, - {40, 0x03, 95}, - {2, 0x02, 98}, - {9, 0x02, 98}, - {23, 0x02, 98}, - {40, 0x03, 98}, - }, - /* 26 */ - { - {3, 0x02, 61}, - {6, 0x02, 61}, - {10, 0x02, 61}, - {15, 0x02, 61}, - {24, 0x02, 61}, - {31, 0x02, 61}, - {41, 0x02, 61}, - {56, 0x03, 61}, - {3, 0x02, 65}, - {6, 0x02, 65}, - {10, 0x02, 65}, - {15, 0x02, 65}, - {24, 0x02, 65}, - {31, 0x02, 65}, - {41, 0x02, 65}, - {56, 0x03, 65}, - }, - /* 27 */ - { - {3, 0x02, 95}, - {6, 0x02, 95}, - {10, 0x02, 95}, - {15, 0x02, 95}, - {24, 0x02, 95}, - {31, 0x02, 95}, - {41, 0x02, 95}, - {56, 0x03, 95}, - {3, 0x02, 98}, - {6, 0x02, 98}, - {10, 0x02, 98}, - {15, 0x02, 98}, - {24, 0x02, 98}, - {31, 0x02, 98}, - {41, 0x02, 98}, - {56, 0x03, 98}, - }, - /* 28 */ - { - {2, 0x02, 100}, - {9, 0x02, 100}, - {23, 0x02, 100}, - {40, 0x03, 100}, - {2, 0x02, 102}, - {9, 0x02, 102}, - {23, 0x02, 102}, - {40, 0x03, 102}, - {2, 0x02, 103}, - {9, 0x02, 103}, - {23, 0x02, 103}, - {40, 0x03, 103}, - {2, 0x02, 104}, - {9, 0x02, 104}, - {23, 0x02, 104}, - {40, 0x03, 104}, - }, - /* 29 */ - { - {3, 0x02, 100}, - {6, 0x02, 100}, - {10, 0x02, 100}, - {15, 0x02, 100}, - {24, 0x02, 100}, - {31, 0x02, 100}, - {41, 0x02, 100}, - {56, 0x03, 100}, - {3, 0x02, 102}, - {6, 0x02, 102}, - {10, 0x02, 102}, - {15, 0x02, 102}, - {24, 0x02, 102}, - {31, 0x02, 102}, - {41, 0x02, 102}, - {56, 0x03, 102}, - }, - /* 30 */ - { - {3, 0x02, 103}, - {6, 0x02, 103}, - {10, 0x02, 103}, - {15, 0x02, 103}, - {24, 0x02, 103}, - {31, 0x02, 103}, - {41, 0x02, 103}, - {56, 0x03, 103}, - {3, 0x02, 104}, - {6, 0x02, 104}, - {10, 0x02, 104}, - {15, 0x02, 104}, - {24, 0x02, 104}, - {31, 0x02, 104}, - {41, 0x02, 104}, - {56, 0x03, 104}, - }, - /* 31 */ - { - {1, 0x02, 108}, - {22, 0x03, 108}, - {1, 0x02, 109}, - {22, 0x03, 109}, - {1, 0x02, 110}, - {22, 0x03, 110}, - {1, 0x02, 112}, - {22, 0x03, 112}, - {1, 0x02, 114}, - {22, 0x03, 114}, - {1, 0x02, 117}, - {22, 0x03, 117}, - {0, 0x03, 58}, - {0, 0x03, 66}, - {0, 0x03, 67}, - {0, 0x03, 68}, - }, - /* 32 */ - { - {2, 0x02, 108}, - {9, 0x02, 108}, - {23, 0x02, 108}, - {40, 0x03, 108}, - {2, 0x02, 109}, - {9, 0x02, 109}, - {23, 0x02, 109}, - {40, 0x03, 109}, - {2, 0x02, 110}, - {9, 0x02, 110}, - {23, 0x02, 110}, - {40, 0x03, 110}, - {2, 0x02, 112}, - {9, 0x02, 112}, - {23, 0x02, 112}, - {40, 0x03, 112}, - }, - /* 33 */ - { - {3, 0x02, 108}, - {6, 0x02, 108}, - {10, 0x02, 108}, - {15, 0x02, 108}, - {24, 0x02, 108}, - {31, 0x02, 108}, - {41, 0x02, 108}, - {56, 0x03, 108}, - {3, 0x02, 109}, - {6, 0x02, 109}, - {10, 0x02, 109}, - {15, 0x02, 109}, - {24, 0x02, 109}, - {31, 0x02, 109}, - {41, 0x02, 109}, - {56, 0x03, 109}, - }, - /* 34 */ - { - {3, 0x02, 110}, - {6, 0x02, 110}, - {10, 0x02, 110}, - {15, 0x02, 110}, - {24, 0x02, 110}, - {31, 0x02, 110}, - {41, 0x02, 110}, - {56, 0x03, 110}, - {3, 0x02, 112}, - {6, 0x02, 112}, - {10, 0x02, 112}, - {15, 0x02, 112}, - {24, 0x02, 112}, - {31, 0x02, 112}, - {41, 0x02, 112}, - {56, 0x03, 112}, - }, - /* 35 */ - { - {2, 0x02, 114}, - {9, 0x02, 114}, - {23, 0x02, 114}, - {40, 0x03, 114}, - {2, 0x02, 117}, - {9, 0x02, 117}, - {23, 0x02, 117}, - {40, 0x03, 117}, - {1, 0x02, 58}, - {22, 0x03, 58}, - {1, 0x02, 66}, - {22, 0x03, 66}, - {1, 0x02, 67}, - {22, 0x03, 67}, - {1, 0x02, 68}, - {22, 0x03, 68}, - }, - /* 36 */ - { - {3, 0x02, 114}, - {6, 0x02, 114}, - {10, 0x02, 114}, - {15, 0x02, 114}, - {24, 0x02, 114}, - {31, 0x02, 114}, - {41, 0x02, 114}, - {56, 0x03, 114}, - {3, 0x02, 117}, - {6, 0x02, 117}, - {10, 0x02, 117}, - {15, 0x02, 117}, - {24, 0x02, 117}, - {31, 0x02, 117}, - {41, 0x02, 117}, - {56, 0x03, 117}, - }, - /* 37 */ - { - {2, 0x02, 58}, - {9, 0x02, 58}, - {23, 0x02, 58}, - {40, 0x03, 58}, - {2, 0x02, 66}, - {9, 0x02, 66}, - {23, 0x02, 66}, - {40, 0x03, 66}, - {2, 0x02, 67}, - {9, 0x02, 67}, - {23, 0x02, 67}, - {40, 0x03, 67}, - {2, 0x02, 68}, - {9, 0x02, 68}, - {23, 0x02, 68}, - {40, 0x03, 68}, - }, - /* 38 */ - { - {3, 0x02, 58}, - {6, 0x02, 58}, - {10, 0x02, 58}, - {15, 0x02, 58}, - {24, 0x02, 58}, - {31, 0x02, 58}, - {41, 0x02, 58}, - {56, 0x03, 58}, - {3, 0x02, 66}, - {6, 0x02, 66}, - {10, 0x02, 66}, - {15, 0x02, 66}, - {24, 0x02, 66}, - {31, 0x02, 66}, - {41, 0x02, 66}, - {56, 0x03, 66}, - }, - /* 39 */ - { - {3, 0x02, 67}, - {6, 0x02, 67}, - {10, 0x02, 67}, - {15, 0x02, 67}, - {24, 0x02, 67}, - {31, 0x02, 67}, - {41, 0x02, 67}, - {56, 0x03, 67}, - {3, 0x02, 68}, - {6, 0x02, 68}, - {10, 0x02, 68}, - {15, 0x02, 68}, - {24, 0x02, 68}, - {31, 0x02, 68}, - {41, 0x02, 68}, - {56, 0x03, 68}, - }, - /* 40 */ - { - {44, 0x00, 0}, - {45, 0x00, 0}, - {47, 0x00, 0}, - {48, 0x00, 0}, - {51, 0x00, 0}, - {52, 0x00, 0}, - {54, 0x00, 0}, - {55, 0x00, 0}, - {59, 0x00, 0}, - {60, 0x00, 0}, - {62, 0x00, 0}, - {63, 0x00, 0}, - {66, 0x00, 0}, - {67, 0x00, 0}, - {69, 0x00, 0}, - {72, 0x01, 0}, - }, - /* 41 */ - { - {0, 0x03, 69}, - {0, 0x03, 70}, - {0, 0x03, 71}, - {0, 0x03, 72}, - {0, 0x03, 73}, - {0, 0x03, 74}, - {0, 0x03, 75}, - {0, 0x03, 76}, - {0, 0x03, 77}, - {0, 0x03, 78}, - {0, 0x03, 79}, - {0, 0x03, 80}, - {0, 0x03, 81}, - {0, 0x03, 82}, - {0, 0x03, 83}, - {0, 0x03, 84}, - }, - /* 42 */ - { - {1, 0x02, 69}, - {22, 0x03, 69}, - {1, 0x02, 70}, - {22, 0x03, 70}, - {1, 0x02, 71}, - {22, 0x03, 71}, - {1, 0x02, 72}, - {22, 0x03, 72}, - {1, 0x02, 73}, - {22, 0x03, 73}, - {1, 0x02, 74}, - {22, 0x03, 74}, - {1, 0x02, 75}, - {22, 0x03, 75}, - {1, 0x02, 76}, - {22, 0x03, 76}, - }, - /* 43 */ - { - {2, 0x02, 69}, - {9, 0x02, 69}, - {23, 0x02, 69}, - {40, 0x03, 69}, - {2, 0x02, 70}, - {9, 0x02, 70}, - {23, 0x02, 70}, - {40, 0x03, 70}, - {2, 0x02, 71}, - {9, 0x02, 71}, - {23, 0x02, 71}, - {40, 0x03, 71}, - {2, 0x02, 72}, - {9, 0x02, 72}, - {23, 0x02, 72}, - {40, 0x03, 72}, - }, - /* 44 */ - { - {3, 0x02, 69}, - {6, 0x02, 69}, - {10, 0x02, 69}, - {15, 0x02, 69}, - {24, 0x02, 69}, - {31, 0x02, 69}, - {41, 0x02, 69}, - {56, 0x03, 69}, - {3, 0x02, 70}, - {6, 0x02, 70}, - {10, 0x02, 70}, - {15, 0x02, 70}, - {24, 0x02, 70}, - {31, 0x02, 70}, - {41, 0x02, 70}, - {56, 0x03, 70}, - }, - /* 45 */ - { - {3, 0x02, 71}, - {6, 0x02, 71}, - {10, 0x02, 71}, - {15, 0x02, 71}, - {24, 0x02, 71}, - {31, 0x02, 71}, - {41, 0x02, 71}, - {56, 0x03, 71}, - {3, 0x02, 72}, - {6, 0x02, 72}, - {10, 0x02, 72}, - {15, 0x02, 72}, - {24, 0x02, 72}, - {31, 0x02, 72}, - {41, 0x02, 72}, - {56, 0x03, 72}, - }, - /* 46 */ - { - {2, 0x02, 73}, - {9, 0x02, 73}, - {23, 0x02, 73}, - {40, 0x03, 73}, - {2, 0x02, 74}, - {9, 0x02, 74}, - {23, 0x02, 74}, - {40, 0x03, 74}, - {2, 0x02, 75}, - {9, 0x02, 75}, - {23, 0x02, 75}, - {40, 0x03, 75}, - {2, 0x02, 76}, - {9, 0x02, 76}, - {23, 0x02, 76}, - {40, 0x03, 76}, - }, - /* 47 */ - { - {3, 0x02, 73}, - {6, 0x02, 73}, - {10, 0x02, 73}, - {15, 0x02, 73}, - {24, 0x02, 73}, - {31, 0x02, 73}, - {41, 0x02, 73}, - {56, 0x03, 73}, - {3, 0x02, 74}, - {6, 0x02, 74}, - {10, 0x02, 74}, - {15, 0x02, 74}, - {24, 0x02, 74}, - {31, 0x02, 74}, - {41, 0x02, 74}, - {56, 0x03, 74}, - }, - /* 48 */ - { - {3, 0x02, 75}, - {6, 0x02, 75}, - {10, 0x02, 75}, - {15, 0x02, 75}, - {24, 0x02, 75}, - {31, 0x02, 75}, - {41, 0x02, 75}, - {56, 0x03, 75}, - {3, 0x02, 76}, - {6, 0x02, 76}, - {10, 0x02, 76}, - {15, 0x02, 76}, - {24, 0x02, 76}, - {31, 0x02, 76}, - {41, 0x02, 76}, - {56, 0x03, 76}, - }, - /* 49 */ - { - {1, 0x02, 77}, - {22, 0x03, 77}, - {1, 0x02, 78}, - {22, 0x03, 78}, - {1, 0x02, 79}, - {22, 0x03, 79}, - {1, 0x02, 80}, - {22, 0x03, 80}, - {1, 0x02, 81}, - {22, 0x03, 81}, - {1, 0x02, 82}, - {22, 0x03, 82}, - {1, 0x02, 83}, - {22, 0x03, 83}, - {1, 0x02, 84}, - {22, 0x03, 84}, - }, - /* 50 */ - { - {2, 0x02, 77}, - {9, 0x02, 77}, - {23, 0x02, 77}, - {40, 0x03, 77}, - {2, 0x02, 78}, - {9, 0x02, 78}, - {23, 0x02, 78}, - {40, 0x03, 78}, - {2, 0x02, 79}, - {9, 0x02, 79}, - {23, 0x02, 79}, - {40, 0x03, 79}, - {2, 0x02, 80}, - {9, 0x02, 80}, - {23, 0x02, 80}, - {40, 0x03, 80}, - }, - /* 51 */ - { - {3, 0x02, 77}, - {6, 0x02, 77}, - {10, 0x02, 77}, - {15, 0x02, 77}, - {24, 0x02, 77}, - {31, 0x02, 77}, - {41, 0x02, 77}, - {56, 0x03, 77}, - {3, 0x02, 78}, - {6, 0x02, 78}, - {10, 0x02, 78}, - {15, 0x02, 78}, - {24, 0x02, 78}, - {31, 0x02, 78}, - {41, 0x02, 78}, - {56, 0x03, 78}, - }, - /* 52 */ - { - {3, 0x02, 79}, - {6, 0x02, 79}, - {10, 0x02, 79}, - {15, 0x02, 79}, - {24, 0x02, 79}, - {31, 0x02, 79}, - {41, 0x02, 79}, - {56, 0x03, 79}, - {3, 0x02, 80}, - {6, 0x02, 80}, - {10, 0x02, 80}, - {15, 0x02, 80}, - {24, 0x02, 80}, - {31, 0x02, 80}, - {41, 0x02, 80}, - {56, 0x03, 80}, - }, - /* 53 */ - { - {2, 0x02, 81}, - {9, 0x02, 81}, - {23, 0x02, 81}, - {40, 0x03, 81}, - {2, 0x02, 82}, - {9, 0x02, 82}, - {23, 0x02, 82}, - {40, 0x03, 82}, - {2, 0x02, 83}, - {9, 0x02, 83}, - {23, 0x02, 83}, - {40, 0x03, 83}, - {2, 0x02, 84}, - {9, 0x02, 84}, - {23, 0x02, 84}, - {40, 0x03, 84}, - }, - /* 54 */ - { - {3, 0x02, 81}, - {6, 0x02, 81}, - {10, 0x02, 81}, - {15, 0x02, 81}, - {24, 0x02, 81}, - {31, 0x02, 81}, - {41, 0x02, 81}, - {56, 0x03, 81}, - {3, 0x02, 82}, - {6, 0x02, 82}, - {10, 0x02, 82}, - {15, 0x02, 82}, - {24, 0x02, 82}, - {31, 0x02, 82}, - {41, 0x02, 82}, - {56, 0x03, 82}, - }, - /* 55 */ - { - {3, 0x02, 83}, - {6, 0x02, 83}, - {10, 0x02, 83}, - {15, 0x02, 83}, - {24, 0x02, 83}, - {31, 0x02, 83}, - {41, 0x02, 83}, - {56, 0x03, 83}, - {3, 0x02, 84}, - {6, 0x02, 84}, - {10, 0x02, 84}, - {15, 0x02, 84}, - {24, 0x02, 84}, - {31, 0x02, 84}, - {41, 0x02, 84}, - {56, 0x03, 84}, - }, - /* 56 */ - { - {0, 0x03, 85}, - {0, 0x03, 86}, - {0, 0x03, 87}, - {0, 0x03, 89}, - {0, 0x03, 106}, - {0, 0x03, 107}, - {0, 0x03, 113}, - {0, 0x03, 118}, - {0, 0x03, 119}, - {0, 0x03, 120}, - {0, 0x03, 121}, - {0, 0x03, 122}, - {70, 0x00, 0}, - {71, 0x00, 0}, - {73, 0x00, 0}, - {74, 0x01, 0}, - }, - /* 57 */ - { - {1, 0x02, 85}, - {22, 0x03, 85}, - {1, 0x02, 86}, - {22, 0x03, 86}, - {1, 0x02, 87}, - {22, 0x03, 87}, - {1, 0x02, 89}, - {22, 0x03, 89}, - {1, 0x02, 106}, - {22, 0x03, 106}, - {1, 0x02, 107}, - {22, 0x03, 107}, - {1, 0x02, 113}, - {22, 0x03, 113}, - {1, 0x02, 118}, - {22, 0x03, 118}, - }, - /* 58 */ - { - {2, 0x02, 85}, - {9, 0x02, 85}, - {23, 0x02, 85}, - {40, 0x03, 85}, - {2, 0x02, 86}, - {9, 0x02, 86}, - {23, 0x02, 86}, - {40, 0x03, 86}, - {2, 0x02, 87}, - {9, 0x02, 87}, - {23, 0x02, 87}, - {40, 0x03, 87}, - {2, 0x02, 89}, - {9, 0x02, 89}, - {23, 0x02, 89}, - {40, 0x03, 89}, - }, - /* 59 */ - { - {3, 0x02, 85}, - {6, 0x02, 85}, - {10, 0x02, 85}, - {15, 0x02, 85}, - {24, 0x02, 85}, - {31, 0x02, 85}, - {41, 0x02, 85}, - {56, 0x03, 85}, - {3, 0x02, 86}, - {6, 0x02, 86}, - {10, 0x02, 86}, - {15, 0x02, 86}, - {24, 0x02, 86}, - {31, 0x02, 86}, - {41, 0x02, 86}, - {56, 0x03, 86}, - }, - /* 60 */ - { - {3, 0x02, 87}, - {6, 0x02, 87}, - {10, 0x02, 87}, - {15, 0x02, 87}, - {24, 0x02, 87}, - {31, 0x02, 87}, - {41, 0x02, 87}, - {56, 0x03, 87}, - {3, 0x02, 89}, - {6, 0x02, 89}, - {10, 0x02, 89}, - {15, 0x02, 89}, - {24, 0x02, 89}, - {31, 0x02, 89}, - {41, 0x02, 89}, - {56, 0x03, 89}, - }, - /* 61 */ - { - {2, 0x02, 106}, - {9, 0x02, 106}, - {23, 0x02, 106}, - {40, 0x03, 106}, - {2, 0x02, 107}, - {9, 0x02, 107}, - {23, 0x02, 107}, - {40, 0x03, 107}, - {2, 0x02, 113}, - {9, 0x02, 113}, - {23, 0x02, 113}, - {40, 0x03, 113}, - {2, 0x02, 118}, - {9, 0x02, 118}, - {23, 0x02, 118}, - {40, 0x03, 118}, - }, - /* 62 */ - { - {3, 0x02, 106}, - {6, 0x02, 106}, - {10, 0x02, 106}, - {15, 0x02, 106}, - {24, 0x02, 106}, - {31, 0x02, 106}, - {41, 0x02, 106}, - {56, 0x03, 106}, - {3, 0x02, 107}, - {6, 0x02, 107}, - {10, 0x02, 107}, - {15, 0x02, 107}, - {24, 0x02, 107}, - {31, 0x02, 107}, - {41, 0x02, 107}, - {56, 0x03, 107}, - }, - /* 63 */ - { - {3, 0x02, 113}, - {6, 0x02, 113}, - {10, 0x02, 113}, - {15, 0x02, 113}, - {24, 0x02, 113}, - {31, 0x02, 113}, - {41, 0x02, 113}, - {56, 0x03, 113}, - {3, 0x02, 118}, - {6, 0x02, 118}, - {10, 0x02, 118}, - {15, 0x02, 118}, - {24, 0x02, 118}, - {31, 0x02, 118}, - {41, 0x02, 118}, - {56, 0x03, 118}, - }, - /* 64 */ - { - {1, 0x02, 119}, - {22, 0x03, 119}, - {1, 0x02, 120}, - {22, 0x03, 120}, - {1, 0x02, 121}, - {22, 0x03, 121}, - {1, 0x02, 122}, - {22, 0x03, 122}, - {0, 0x03, 38}, - {0, 0x03, 42}, - {0, 0x03, 44}, - {0, 0x03, 59}, - {0, 0x03, 88}, - {0, 0x03, 90}, - {75, 0x00, 0}, - {78, 0x00, 0}, - }, - /* 65 */ - { - {2, 0x02, 119}, - {9, 0x02, 119}, - {23, 0x02, 119}, - {40, 0x03, 119}, - {2, 0x02, 120}, - {9, 0x02, 120}, - {23, 0x02, 120}, - {40, 0x03, 120}, - {2, 0x02, 121}, - {9, 0x02, 121}, - {23, 0x02, 121}, - {40, 0x03, 121}, - {2, 0x02, 122}, - {9, 0x02, 122}, - {23, 0x02, 122}, - {40, 0x03, 122}, - }, - /* 66 */ - { - {3, 0x02, 119}, - {6, 0x02, 119}, - {10, 0x02, 119}, - {15, 0x02, 119}, - {24, 0x02, 119}, - {31, 0x02, 119}, - {41, 0x02, 119}, - {56, 0x03, 119}, - {3, 0x02, 120}, - {6, 0x02, 120}, - {10, 0x02, 120}, - {15, 0x02, 120}, - {24, 0x02, 120}, - {31, 0x02, 120}, - {41, 0x02, 120}, - {56, 0x03, 120}, - }, - /* 67 */ - { - {3, 0x02, 121}, - {6, 0x02, 121}, - {10, 0x02, 121}, - {15, 0x02, 121}, - {24, 0x02, 121}, - {31, 0x02, 121}, - {41, 0x02, 121}, - {56, 0x03, 121}, - {3, 0x02, 122}, - {6, 0x02, 122}, - {10, 0x02, 122}, - {15, 0x02, 122}, - {24, 0x02, 122}, - {31, 0x02, 122}, - {41, 0x02, 122}, - {56, 0x03, 122}, - }, - /* 68 */ - { - {1, 0x02, 38}, - {22, 0x03, 38}, - {1, 0x02, 42}, - {22, 0x03, 42}, - {1, 0x02, 44}, - {22, 0x03, 44}, - {1, 0x02, 59}, - {22, 0x03, 59}, - {1, 0x02, 88}, - {22, 0x03, 88}, - {1, 0x02, 90}, - {22, 0x03, 90}, - {76, 0x00, 0}, - {77, 0x00, 0}, - {79, 0x00, 0}, - {81, 0x00, 0}, - }, - /* 69 */ - { - {2, 0x02, 38}, - {9, 0x02, 38}, - {23, 0x02, 38}, - {40, 0x03, 38}, - {2, 0x02, 42}, - {9, 0x02, 42}, - {23, 0x02, 42}, - {40, 0x03, 42}, - {2, 0x02, 44}, - {9, 0x02, 44}, - {23, 0x02, 44}, - {40, 0x03, 44}, - {2, 0x02, 59}, - {9, 0x02, 59}, - {23, 0x02, 59}, - {40, 0x03, 59}, - }, - /* 70 */ - { - {3, 0x02, 38}, - {6, 0x02, 38}, - {10, 0x02, 38}, - {15, 0x02, 38}, - {24, 0x02, 38}, - {31, 0x02, 38}, - {41, 0x02, 38}, - {56, 0x03, 38}, - {3, 0x02, 42}, - {6, 0x02, 42}, - {10, 0x02, 42}, - {15, 0x02, 42}, - {24, 0x02, 42}, - {31, 0x02, 42}, - {41, 0x02, 42}, - {56, 0x03, 42}, - }, - /* 71 */ - { - {3, 0x02, 44}, - {6, 0x02, 44}, - {10, 0x02, 44}, - {15, 0x02, 44}, - {24, 0x02, 44}, - {31, 0x02, 44}, - {41, 0x02, 44}, - {56, 0x03, 44}, - {3, 0x02, 59}, - {6, 0x02, 59}, - {10, 0x02, 59}, - {15, 0x02, 59}, - {24, 0x02, 59}, - {31, 0x02, 59}, - {41, 0x02, 59}, - {56, 0x03, 59}, - }, - /* 72 */ - { - {2, 0x02, 88}, - {9, 0x02, 88}, - {23, 0x02, 88}, - {40, 0x03, 88}, - {2, 0x02, 90}, - {9, 0x02, 90}, - {23, 0x02, 90}, - {40, 0x03, 90}, - {0, 0x03, 33}, - {0, 0x03, 34}, - {0, 0x03, 40}, - {0, 0x03, 41}, - {0, 0x03, 63}, - {80, 0x00, 0}, - {82, 0x00, 0}, - {84, 0x00, 0}, - }, - /* 73 */ - { - {3, 0x02, 88}, - {6, 0x02, 88}, - {10, 0x02, 88}, - {15, 0x02, 88}, - {24, 0x02, 88}, - {31, 0x02, 88}, - {41, 0x02, 88}, - {56, 0x03, 88}, - {3, 0x02, 90}, - {6, 0x02, 90}, - {10, 0x02, 90}, - {15, 0x02, 90}, - {24, 0x02, 90}, - {31, 0x02, 90}, - {41, 0x02, 90}, - {56, 0x03, 90}, - }, - /* 74 */ - { - {1, 0x02, 33}, - {22, 0x03, 33}, - {1, 0x02, 34}, - {22, 0x03, 34}, - {1, 0x02, 40}, - {22, 0x03, 40}, - {1, 0x02, 41}, - {22, 0x03, 41}, - {1, 0x02, 63}, - {22, 0x03, 63}, - {0, 0x03, 39}, - {0, 0x03, 43}, - {0, 0x03, 124}, - {83, 0x00, 0}, - {85, 0x00, 0}, - {88, 0x00, 0}, - }, - /* 75 */ - { - {2, 0x02, 33}, - {9, 0x02, 33}, - {23, 0x02, 33}, - {40, 0x03, 33}, - {2, 0x02, 34}, - {9, 0x02, 34}, - {23, 0x02, 34}, - {40, 0x03, 34}, - {2, 0x02, 40}, - {9, 0x02, 40}, - {23, 0x02, 40}, - {40, 0x03, 40}, - {2, 0x02, 41}, - {9, 0x02, 41}, - {23, 0x02, 41}, - {40, 0x03, 41}, - }, - /* 76 */ - { - {3, 0x02, 33}, - {6, 0x02, 33}, - {10, 0x02, 33}, - {15, 0x02, 33}, - {24, 0x02, 33}, - {31, 0x02, 33}, - {41, 0x02, 33}, - {56, 0x03, 33}, - {3, 0x02, 34}, - {6, 0x02, 34}, - {10, 0x02, 34}, - {15, 0x02, 34}, - {24, 0x02, 34}, - {31, 0x02, 34}, - {41, 0x02, 34}, - {56, 0x03, 34}, - }, - /* 77 */ - { - {3, 0x02, 40}, - {6, 0x02, 40}, - {10, 0x02, 40}, - {15, 0x02, 40}, - {24, 0x02, 40}, - {31, 0x02, 40}, - {41, 0x02, 40}, - {56, 0x03, 40}, - {3, 0x02, 41}, - {6, 0x02, 41}, - {10, 0x02, 41}, - {15, 0x02, 41}, - {24, 0x02, 41}, - {31, 0x02, 41}, - {41, 0x02, 41}, - {56, 0x03, 41}, - }, - /* 78 */ - { - {2, 0x02, 63}, - {9, 0x02, 63}, - {23, 0x02, 63}, - {40, 0x03, 63}, - {1, 0x02, 39}, - {22, 0x03, 39}, - {1, 0x02, 43}, - {22, 0x03, 43}, - {1, 0x02, 124}, - {22, 0x03, 124}, - {0, 0x03, 35}, - {0, 0x03, 62}, - {86, 0x00, 0}, - {87, 0x00, 0}, - {89, 0x00, 0}, - {90, 0x00, 0}, - }, - /* 79 */ - { - {3, 0x02, 63}, - {6, 0x02, 63}, - {10, 0x02, 63}, - {15, 0x02, 63}, - {24, 0x02, 63}, - {31, 0x02, 63}, - {41, 0x02, 63}, - {56, 0x03, 63}, - {2, 0x02, 39}, - {9, 0x02, 39}, - {23, 0x02, 39}, - {40, 0x03, 39}, - {2, 0x02, 43}, - {9, 0x02, 43}, - {23, 0x02, 43}, - {40, 0x03, 43}, - }, - /* 80 */ - { - {3, 0x02, 39}, - {6, 0x02, 39}, - {10, 0x02, 39}, - {15, 0x02, 39}, - {24, 0x02, 39}, - {31, 0x02, 39}, - {41, 0x02, 39}, - {56, 0x03, 39}, - {3, 0x02, 43}, - {6, 0x02, 43}, - {10, 0x02, 43}, - {15, 0x02, 43}, - {24, 0x02, 43}, - {31, 0x02, 43}, - {41, 0x02, 43}, - {56, 0x03, 43}, - }, - /* 81 */ - { - {2, 0x02, 124}, - {9, 0x02, 124}, - {23, 0x02, 124}, - {40, 0x03, 124}, - {1, 0x02, 35}, - {22, 0x03, 35}, - {1, 0x02, 62}, - {22, 0x03, 62}, - {0, 0x03, 0}, - {0, 0x03, 36}, - {0, 0x03, 64}, - {0, 0x03, 91}, - {0, 0x03, 93}, - {0, 0x03, 126}, - {91, 0x00, 0}, - {92, 0x00, 0}, - }, - /* 82 */ - { - {3, 0x02, 124}, - {6, 0x02, 124}, - {10, 0x02, 124}, - {15, 0x02, 124}, - {24, 0x02, 124}, - {31, 0x02, 124}, - {41, 0x02, 124}, - {56, 0x03, 124}, - {2, 0x02, 35}, - {9, 0x02, 35}, - {23, 0x02, 35}, - {40, 0x03, 35}, - {2, 0x02, 62}, - {9, 0x02, 62}, - {23, 0x02, 62}, - {40, 0x03, 62}, - }, - /* 83 */ - { - {3, 0x02, 35}, - {6, 0x02, 35}, - {10, 0x02, 35}, - {15, 0x02, 35}, - {24, 0x02, 35}, - {31, 0x02, 35}, - {41, 0x02, 35}, - {56, 0x03, 35}, - {3, 0x02, 62}, - {6, 0x02, 62}, - {10, 0x02, 62}, - {15, 0x02, 62}, - {24, 0x02, 62}, - {31, 0x02, 62}, - {41, 0x02, 62}, - {56, 0x03, 62}, - }, - /* 84 */ - { - {1, 0x02, 0}, - {22, 0x03, 0}, - {1, 0x02, 36}, - {22, 0x03, 36}, - {1, 0x02, 64}, - {22, 0x03, 64}, - {1, 0x02, 91}, - {22, 0x03, 91}, - {1, 0x02, 93}, - {22, 0x03, 93}, - {1, 0x02, 126}, - {22, 0x03, 126}, - {0, 0x03, 94}, - {0, 0x03, 125}, - {93, 0x00, 0}, - {94, 0x00, 0}, - }, - /* 85 */ - { - {2, 0x02, 0}, - {9, 0x02, 0}, - {23, 0x02, 0}, - {40, 0x03, 0}, - {2, 0x02, 36}, - {9, 0x02, 36}, - {23, 0x02, 36}, - {40, 0x03, 36}, - {2, 0x02, 64}, - {9, 0x02, 64}, - {23, 0x02, 64}, - {40, 0x03, 64}, - {2, 0x02, 91}, - {9, 0x02, 91}, - {23, 0x02, 91}, - {40, 0x03, 91}, - }, - /* 86 */ - { - {3, 0x02, 0}, - {6, 0x02, 0}, - {10, 0x02, 0}, - {15, 0x02, 0}, - {24, 0x02, 0}, - {31, 0x02, 0}, - {41, 0x02, 0}, - {56, 0x03, 0}, - {3, 0x02, 36}, - {6, 0x02, 36}, - {10, 0x02, 36}, - {15, 0x02, 36}, - {24, 0x02, 36}, - {31, 0x02, 36}, - {41, 0x02, 36}, - {56, 0x03, 36}, - }, - /* 87 */ - { - {3, 0x02, 64}, - {6, 0x02, 64}, - {10, 0x02, 64}, - {15, 0x02, 64}, - {24, 0x02, 64}, - {31, 0x02, 64}, - {41, 0x02, 64}, - {56, 0x03, 64}, - {3, 0x02, 91}, - {6, 0x02, 91}, - {10, 0x02, 91}, - {15, 0x02, 91}, - {24, 0x02, 91}, - {31, 0x02, 91}, - {41, 0x02, 91}, - {56, 0x03, 91}, - }, - /* 88 */ - { - {2, 0x02, 93}, - {9, 0x02, 93}, - {23, 0x02, 93}, - {40, 0x03, 93}, - {2, 0x02, 126}, - {9, 0x02, 126}, - {23, 0x02, 126}, - {40, 0x03, 126}, - {1, 0x02, 94}, - {22, 0x03, 94}, - {1, 0x02, 125}, - {22, 0x03, 125}, - {0, 0x03, 60}, - {0, 0x03, 96}, - {0, 0x03, 123}, - {95, 0x00, 0}, - }, - /* 89 */ - { - {3, 0x02, 93}, - {6, 0x02, 93}, - {10, 0x02, 93}, - {15, 0x02, 93}, - {24, 0x02, 93}, - {31, 0x02, 93}, - {41, 0x02, 93}, - {56, 0x03, 93}, - {3, 0x02, 126}, - {6, 0x02, 126}, - {10, 0x02, 126}, - {15, 0x02, 126}, - {24, 0x02, 126}, - {31, 0x02, 126}, - {41, 0x02, 126}, - {56, 0x03, 126}, - }, - /* 90 */ - { - {2, 0x02, 94}, - {9, 0x02, 94}, - {23, 0x02, 94}, - {40, 0x03, 94}, - {2, 0x02, 125}, - {9, 0x02, 125}, - {23, 0x02, 125}, - {40, 0x03, 125}, - {1, 0x02, 60}, - {22, 0x03, 60}, - {1, 0x02, 96}, - {22, 0x03, 96}, - {1, 0x02, 123}, - {22, 0x03, 123}, - {96, 0x00, 0}, - {110, 0x00, 0}, - }, - /* 91 */ - { - {3, 0x02, 94}, - {6, 0x02, 94}, - {10, 0x02, 94}, - {15, 0x02, 94}, - {24, 0x02, 94}, - {31, 0x02, 94}, - {41, 0x02, 94}, - {56, 0x03, 94}, - {3, 0x02, 125}, - {6, 0x02, 125}, - {10, 0x02, 125}, - {15, 0x02, 125}, - {24, 0x02, 125}, - {31, 0x02, 125}, - {41, 0x02, 125}, - {56, 0x03, 125}, - }, - /* 92 */ - { - {2, 0x02, 60}, - {9, 0x02, 60}, - {23, 0x02, 60}, - {40, 0x03, 60}, - {2, 0x02, 96}, - {9, 0x02, 96}, - {23, 0x02, 96}, - {40, 0x03, 96}, - {2, 0x02, 123}, - {9, 0x02, 123}, - {23, 0x02, 123}, - {40, 0x03, 123}, - {97, 0x00, 0}, - {101, 0x00, 0}, - {111, 0x00, 0}, - {133, 0x00, 0}, - }, - /* 93 */ - { - {3, 0x02, 60}, - {6, 0x02, 60}, - {10, 0x02, 60}, - {15, 0x02, 60}, - {24, 0x02, 60}, - {31, 0x02, 60}, - {41, 0x02, 60}, - {56, 0x03, 60}, - {3, 0x02, 96}, - {6, 0x02, 96}, - {10, 0x02, 96}, - {15, 0x02, 96}, - {24, 0x02, 96}, - {31, 0x02, 96}, - {41, 0x02, 96}, - {56, 0x03, 96}, - }, - /* 94 */ - { - {3, 0x02, 123}, - {6, 0x02, 123}, - {10, 0x02, 123}, - {15, 0x02, 123}, - {24, 0x02, 123}, - {31, 0x02, 123}, - {41, 0x02, 123}, - {56, 0x03, 123}, - {98, 0x00, 0}, - {99, 0x00, 0}, - {102, 0x00, 0}, - {105, 0x00, 0}, - {112, 0x00, 0}, - {119, 0x00, 0}, - {134, 0x00, 0}, - {153, 0x00, 0}, - }, - /* 95 */ - { - {0, 0x03, 92}, - {0, 0x03, 195}, - {0, 0x03, 208}, - {100, 0x00, 0}, - {103, 0x00, 0}, - {104, 0x00, 0}, - {106, 0x00, 0}, - {107, 0x00, 0}, - {113, 0x00, 0}, - {116, 0x00, 0}, - {120, 0x00, 0}, - {126, 0x00, 0}, - {135, 0x00, 0}, - {142, 0x00, 0}, - {154, 0x00, 0}, - {169, 0x00, 0}, - }, - /* 96 */ - { - {1, 0x02, 92}, - {22, 0x03, 92}, - {1, 0x02, 195}, - {22, 0x03, 195}, - {1, 0x02, 208}, - {22, 0x03, 208}, - {0, 0x03, 128}, - {0, 0x03, 130}, - {0, 0x03, 131}, - {0, 0x03, 162}, - {0, 0x03, 184}, - {0, 0x03, 194}, - {0, 0x03, 224}, - {0, 0x03, 226}, - {108, 0x00, 0}, - {109, 0x00, 0}, - }, - /* 97 */ - { - {2, 0x02, 92}, - {9, 0x02, 92}, - {23, 0x02, 92}, - {40, 0x03, 92}, - {2, 0x02, 195}, - {9, 0x02, 195}, - {23, 0x02, 195}, - {40, 0x03, 195}, - {2, 0x02, 208}, - {9, 0x02, 208}, - {23, 0x02, 208}, - {40, 0x03, 208}, - {1, 0x02, 128}, - {22, 0x03, 128}, - {1, 0x02, 130}, - {22, 0x03, 130}, - }, - /* 98 */ - { - {3, 0x02, 92}, - {6, 0x02, 92}, - {10, 0x02, 92}, - {15, 0x02, 92}, - {24, 0x02, 92}, - {31, 0x02, 92}, - {41, 0x02, 92}, - {56, 0x03, 92}, - {3, 0x02, 195}, - {6, 0x02, 195}, - {10, 0x02, 195}, - {15, 0x02, 195}, - {24, 0x02, 195}, - {31, 0x02, 195}, - {41, 0x02, 195}, - {56, 0x03, 195}, - }, - /* 99 */ - { - {3, 0x02, 208}, - {6, 0x02, 208}, - {10, 0x02, 208}, - {15, 0x02, 208}, - {24, 0x02, 208}, - {31, 0x02, 208}, - {41, 0x02, 208}, - {56, 0x03, 208}, - {2, 0x02, 128}, - {9, 0x02, 128}, - {23, 0x02, 128}, - {40, 0x03, 128}, - {2, 0x02, 130}, - {9, 0x02, 130}, - {23, 0x02, 130}, - {40, 0x03, 130}, - }, - /* 100 */ - { - {3, 0x02, 128}, - {6, 0x02, 128}, - {10, 0x02, 128}, - {15, 0x02, 128}, - {24, 0x02, 128}, - {31, 0x02, 128}, - {41, 0x02, 128}, - {56, 0x03, 128}, - {3, 0x02, 130}, - {6, 0x02, 130}, - {10, 0x02, 130}, - {15, 0x02, 130}, - {24, 0x02, 130}, - {31, 0x02, 130}, - {41, 0x02, 130}, - {56, 0x03, 130}, - }, - /* 101 */ - { - {1, 0x02, 131}, - {22, 0x03, 131}, - {1, 0x02, 162}, - {22, 0x03, 162}, - {1, 0x02, 184}, - {22, 0x03, 184}, - {1, 0x02, 194}, - {22, 0x03, 194}, - {1, 0x02, 224}, - {22, 0x03, 224}, - {1, 0x02, 226}, - {22, 0x03, 226}, - {0, 0x03, 153}, - {0, 0x03, 161}, - {0, 0x03, 167}, - {0, 0x03, 172}, - }, - /* 102 */ - { - {2, 0x02, 131}, - {9, 0x02, 131}, - {23, 0x02, 131}, - {40, 0x03, 131}, - {2, 0x02, 162}, - {9, 0x02, 162}, - {23, 0x02, 162}, - {40, 0x03, 162}, - {2, 0x02, 184}, - {9, 0x02, 184}, - {23, 0x02, 184}, - {40, 0x03, 184}, - {2, 0x02, 194}, - {9, 0x02, 194}, - {23, 0x02, 194}, - {40, 0x03, 194}, - }, - /* 103 */ - { - {3, 0x02, 131}, - {6, 0x02, 131}, - {10, 0x02, 131}, - {15, 0x02, 131}, - {24, 0x02, 131}, - {31, 0x02, 131}, - {41, 0x02, 131}, - {56, 0x03, 131}, - {3, 0x02, 162}, - {6, 0x02, 162}, - {10, 0x02, 162}, - {15, 0x02, 162}, - {24, 0x02, 162}, - {31, 0x02, 162}, - {41, 0x02, 162}, - {56, 0x03, 162}, - }, - /* 104 */ - { - {3, 0x02, 184}, - {6, 0x02, 184}, - {10, 0x02, 184}, - {15, 0x02, 184}, - {24, 0x02, 184}, - {31, 0x02, 184}, - {41, 0x02, 184}, - {56, 0x03, 184}, - {3, 0x02, 194}, - {6, 0x02, 194}, - {10, 0x02, 194}, - {15, 0x02, 194}, - {24, 0x02, 194}, - {31, 0x02, 194}, - {41, 0x02, 194}, - {56, 0x03, 194}, - }, - /* 105 */ - { - {2, 0x02, 224}, - {9, 0x02, 224}, - {23, 0x02, 224}, - {40, 0x03, 224}, - {2, 0x02, 226}, - {9, 0x02, 226}, - {23, 0x02, 226}, - {40, 0x03, 226}, - {1, 0x02, 153}, - {22, 0x03, 153}, - {1, 0x02, 161}, - {22, 0x03, 161}, - {1, 0x02, 167}, - {22, 0x03, 167}, - {1, 0x02, 172}, - {22, 0x03, 172}, - }, - /* 106 */ - { - {3, 0x02, 224}, - {6, 0x02, 224}, - {10, 0x02, 224}, - {15, 0x02, 224}, - {24, 0x02, 224}, - {31, 0x02, 224}, - {41, 0x02, 224}, - {56, 0x03, 224}, - {3, 0x02, 226}, - {6, 0x02, 226}, - {10, 0x02, 226}, - {15, 0x02, 226}, - {24, 0x02, 226}, - {31, 0x02, 226}, - {41, 0x02, 226}, - {56, 0x03, 226}, - }, - /* 107 */ - { - {2, 0x02, 153}, - {9, 0x02, 153}, - {23, 0x02, 153}, - {40, 0x03, 153}, - {2, 0x02, 161}, - {9, 0x02, 161}, - {23, 0x02, 161}, - {40, 0x03, 161}, - {2, 0x02, 167}, - {9, 0x02, 167}, - {23, 0x02, 167}, - {40, 0x03, 167}, - {2, 0x02, 172}, - {9, 0x02, 172}, - {23, 0x02, 172}, - {40, 0x03, 172}, - }, - /* 108 */ - { - {3, 0x02, 153}, - {6, 0x02, 153}, - {10, 0x02, 153}, - {15, 0x02, 153}, - {24, 0x02, 153}, - {31, 0x02, 153}, - {41, 0x02, 153}, - {56, 0x03, 153}, - {3, 0x02, 161}, - {6, 0x02, 161}, - {10, 0x02, 161}, - {15, 0x02, 161}, - {24, 0x02, 161}, - {31, 0x02, 161}, - {41, 0x02, 161}, - {56, 0x03, 161}, - }, - /* 109 */ - { - {3, 0x02, 167}, - {6, 0x02, 167}, - {10, 0x02, 167}, - {15, 0x02, 167}, - {24, 0x02, 167}, - {31, 0x02, 167}, - {41, 0x02, 167}, - {56, 0x03, 167}, - {3, 0x02, 172}, - {6, 0x02, 172}, - {10, 0x02, 172}, - {15, 0x02, 172}, - {24, 0x02, 172}, - {31, 0x02, 172}, - {41, 0x02, 172}, - {56, 0x03, 172}, - }, - /* 110 */ - { - {114, 0x00, 0}, - {115, 0x00, 0}, - {117, 0x00, 0}, - {118, 0x00, 0}, - {121, 0x00, 0}, - {123, 0x00, 0}, - {127, 0x00, 0}, - {130, 0x00, 0}, - {136, 0x00, 0}, - {139, 0x00, 0}, - {143, 0x00, 0}, - {146, 0x00, 0}, - {155, 0x00, 0}, - {162, 0x00, 0}, - {170, 0x00, 0}, - {180, 0x00, 0}, - }, - /* 111 */ - { - {0, 0x03, 176}, - {0, 0x03, 177}, - {0, 0x03, 179}, - {0, 0x03, 209}, - {0, 0x03, 216}, - {0, 0x03, 217}, - {0, 0x03, 227}, - {0, 0x03, 229}, - {0, 0x03, 230}, - {122, 0x00, 0}, - {124, 0x00, 0}, - {125, 0x00, 0}, - {128, 0x00, 0}, - {129, 0x00, 0}, - {131, 0x00, 0}, - {132, 0x00, 0}, - }, - /* 112 */ - { - {1, 0x02, 176}, - {22, 0x03, 176}, - {1, 0x02, 177}, - {22, 0x03, 177}, - {1, 0x02, 179}, - {22, 0x03, 179}, - {1, 0x02, 209}, - {22, 0x03, 209}, - {1, 0x02, 216}, - {22, 0x03, 216}, - {1, 0x02, 217}, - {22, 0x03, 217}, - {1, 0x02, 227}, - {22, 0x03, 227}, - {1, 0x02, 229}, - {22, 0x03, 229}, - }, - /* 113 */ - { - {2, 0x02, 176}, - {9, 0x02, 176}, - {23, 0x02, 176}, - {40, 0x03, 176}, - {2, 0x02, 177}, - {9, 0x02, 177}, - {23, 0x02, 177}, - {40, 0x03, 177}, - {2, 0x02, 179}, - {9, 0x02, 179}, - {23, 0x02, 179}, - {40, 0x03, 179}, - {2, 0x02, 209}, - {9, 0x02, 209}, - {23, 0x02, 209}, - {40, 0x03, 209}, - }, - /* 114 */ - { - {3, 0x02, 176}, - {6, 0x02, 176}, - {10, 0x02, 176}, - {15, 0x02, 176}, - {24, 0x02, 176}, - {31, 0x02, 176}, - {41, 0x02, 176}, - {56, 0x03, 176}, - {3, 0x02, 177}, - {6, 0x02, 177}, - {10, 0x02, 177}, - {15, 0x02, 177}, - {24, 0x02, 177}, - {31, 0x02, 177}, - {41, 0x02, 177}, - {56, 0x03, 177}, - }, - /* 115 */ - { - {3, 0x02, 179}, - {6, 0x02, 179}, - {10, 0x02, 179}, - {15, 0x02, 179}, - {24, 0x02, 179}, - {31, 0x02, 179}, - {41, 0x02, 179}, - {56, 0x03, 179}, - {3, 0x02, 209}, - {6, 0x02, 209}, - {10, 0x02, 209}, - {15, 0x02, 209}, - {24, 0x02, 209}, - {31, 0x02, 209}, - {41, 0x02, 209}, - {56, 0x03, 209}, - }, - /* 116 */ - { - {2, 0x02, 216}, - {9, 0x02, 216}, - {23, 0x02, 216}, - {40, 0x03, 216}, - {2, 0x02, 217}, - {9, 0x02, 217}, - {23, 0x02, 217}, - {40, 0x03, 217}, - {2, 0x02, 227}, - {9, 0x02, 227}, - {23, 0x02, 227}, - {40, 0x03, 227}, - {2, 0x02, 229}, - {9, 0x02, 229}, - {23, 0x02, 229}, - {40, 0x03, 229}, - }, - /* 117 */ - { - {3, 0x02, 216}, - {6, 0x02, 216}, - {10, 0x02, 216}, - {15, 0x02, 216}, - {24, 0x02, 216}, - {31, 0x02, 216}, - {41, 0x02, 216}, - {56, 0x03, 216}, - {3, 0x02, 217}, - {6, 0x02, 217}, - {10, 0x02, 217}, - {15, 0x02, 217}, - {24, 0x02, 217}, - {31, 0x02, 217}, - {41, 0x02, 217}, - {56, 0x03, 217}, - }, - /* 118 */ - { - {3, 0x02, 227}, - {6, 0x02, 227}, - {10, 0x02, 227}, - {15, 0x02, 227}, - {24, 0x02, 227}, - {31, 0x02, 227}, - {41, 0x02, 227}, - {56, 0x03, 227}, - {3, 0x02, 229}, - {6, 0x02, 229}, - {10, 0x02, 229}, - {15, 0x02, 229}, - {24, 0x02, 229}, - {31, 0x02, 229}, - {41, 0x02, 229}, - {56, 0x03, 229}, - }, - /* 119 */ - { - {1, 0x02, 230}, - {22, 0x03, 230}, - {0, 0x03, 129}, - {0, 0x03, 132}, - {0, 0x03, 133}, - {0, 0x03, 134}, - {0, 0x03, 136}, - {0, 0x03, 146}, - {0, 0x03, 154}, - {0, 0x03, 156}, - {0, 0x03, 160}, - {0, 0x03, 163}, - {0, 0x03, 164}, - {0, 0x03, 169}, - {0, 0x03, 170}, - {0, 0x03, 173}, - }, - /* 120 */ - { - {2, 0x02, 230}, - {9, 0x02, 230}, - {23, 0x02, 230}, - {40, 0x03, 230}, - {1, 0x02, 129}, - {22, 0x03, 129}, - {1, 0x02, 132}, - {22, 0x03, 132}, - {1, 0x02, 133}, - {22, 0x03, 133}, - {1, 0x02, 134}, - {22, 0x03, 134}, - {1, 0x02, 136}, - {22, 0x03, 136}, - {1, 0x02, 146}, - {22, 0x03, 146}, - }, - /* 121 */ - { - {3, 0x02, 230}, - {6, 0x02, 230}, - {10, 0x02, 230}, - {15, 0x02, 230}, - {24, 0x02, 230}, - {31, 0x02, 230}, - {41, 0x02, 230}, - {56, 0x03, 230}, - {2, 0x02, 129}, - {9, 0x02, 129}, - {23, 0x02, 129}, - {40, 0x03, 129}, - {2, 0x02, 132}, - {9, 0x02, 132}, - {23, 0x02, 132}, - {40, 0x03, 132}, - }, - /* 122 */ - { - {3, 0x02, 129}, - {6, 0x02, 129}, - {10, 0x02, 129}, - {15, 0x02, 129}, - {24, 0x02, 129}, - {31, 0x02, 129}, - {41, 0x02, 129}, - {56, 0x03, 129}, - {3, 0x02, 132}, - {6, 0x02, 132}, - {10, 0x02, 132}, - {15, 0x02, 132}, - {24, 0x02, 132}, - {31, 0x02, 132}, - {41, 0x02, 132}, - {56, 0x03, 132}, - }, - /* 123 */ - { - {2, 0x02, 133}, - {9, 0x02, 133}, - {23, 0x02, 133}, - {40, 0x03, 133}, - {2, 0x02, 134}, - {9, 0x02, 134}, - {23, 0x02, 134}, - {40, 0x03, 134}, - {2, 0x02, 136}, - {9, 0x02, 136}, - {23, 0x02, 136}, - {40, 0x03, 136}, - {2, 0x02, 146}, - {9, 0x02, 146}, - {23, 0x02, 146}, - {40, 0x03, 146}, - }, - /* 124 */ - { - {3, 0x02, 133}, - {6, 0x02, 133}, - {10, 0x02, 133}, - {15, 0x02, 133}, - {24, 0x02, 133}, - {31, 0x02, 133}, - {41, 0x02, 133}, - {56, 0x03, 133}, - {3, 0x02, 134}, - {6, 0x02, 134}, - {10, 0x02, 134}, - {15, 0x02, 134}, - {24, 0x02, 134}, - {31, 0x02, 134}, - {41, 0x02, 134}, - {56, 0x03, 134}, - }, - /* 125 */ - { - {3, 0x02, 136}, - {6, 0x02, 136}, - {10, 0x02, 136}, - {15, 0x02, 136}, - {24, 0x02, 136}, - {31, 0x02, 136}, - {41, 0x02, 136}, - {56, 0x03, 136}, - {3, 0x02, 146}, - {6, 0x02, 146}, - {10, 0x02, 146}, - {15, 0x02, 146}, - {24, 0x02, 146}, - {31, 0x02, 146}, - {41, 0x02, 146}, - {56, 0x03, 146}, - }, - /* 126 */ - { - {1, 0x02, 154}, - {22, 0x03, 154}, - {1, 0x02, 156}, - {22, 0x03, 156}, - {1, 0x02, 160}, - {22, 0x03, 160}, - {1, 0x02, 163}, - {22, 0x03, 163}, - {1, 0x02, 164}, - {22, 0x03, 164}, - {1, 0x02, 169}, - {22, 0x03, 169}, - {1, 0x02, 170}, - {22, 0x03, 170}, - {1, 0x02, 173}, - {22, 0x03, 173}, - }, - /* 127 */ - { - {2, 0x02, 154}, - {9, 0x02, 154}, - {23, 0x02, 154}, - {40, 0x03, 154}, - {2, 0x02, 156}, - {9, 0x02, 156}, - {23, 0x02, 156}, - {40, 0x03, 156}, - {2, 0x02, 160}, - {9, 0x02, 160}, - {23, 0x02, 160}, - {40, 0x03, 160}, - {2, 0x02, 163}, - {9, 0x02, 163}, - {23, 0x02, 163}, - {40, 0x03, 163}, - }, - /* 128 */ - { - {3, 0x02, 154}, - {6, 0x02, 154}, - {10, 0x02, 154}, - {15, 0x02, 154}, - {24, 0x02, 154}, - {31, 0x02, 154}, - {41, 0x02, 154}, - {56, 0x03, 154}, - {3, 0x02, 156}, - {6, 0x02, 156}, - {10, 0x02, 156}, - {15, 0x02, 156}, - {24, 0x02, 156}, - {31, 0x02, 156}, - {41, 0x02, 156}, - {56, 0x03, 156}, - }, - /* 129 */ - { - {3, 0x02, 160}, - {6, 0x02, 160}, - {10, 0x02, 160}, - {15, 0x02, 160}, - {24, 0x02, 160}, - {31, 0x02, 160}, - {41, 0x02, 160}, - {56, 0x03, 160}, - {3, 0x02, 163}, - {6, 0x02, 163}, - {10, 0x02, 163}, - {15, 0x02, 163}, - {24, 0x02, 163}, - {31, 0x02, 163}, - {41, 0x02, 163}, - {56, 0x03, 163}, - }, - /* 130 */ - { - {2, 0x02, 164}, - {9, 0x02, 164}, - {23, 0x02, 164}, - {40, 0x03, 164}, - {2, 0x02, 169}, - {9, 0x02, 169}, - {23, 0x02, 169}, - {40, 0x03, 169}, - {2, 0x02, 170}, - {9, 0x02, 170}, - {23, 0x02, 170}, - {40, 0x03, 170}, - {2, 0x02, 173}, - {9, 0x02, 173}, - {23, 0x02, 173}, - {40, 0x03, 173}, - }, - /* 131 */ - { - {3, 0x02, 164}, - {6, 0x02, 164}, - {10, 0x02, 164}, - {15, 0x02, 164}, - {24, 0x02, 164}, - {31, 0x02, 164}, - {41, 0x02, 164}, - {56, 0x03, 164}, - {3, 0x02, 169}, - {6, 0x02, 169}, - {10, 0x02, 169}, - {15, 0x02, 169}, - {24, 0x02, 169}, - {31, 0x02, 169}, - {41, 0x02, 169}, - {56, 0x03, 169}, - }, - /* 132 */ - { - {3, 0x02, 170}, - {6, 0x02, 170}, - {10, 0x02, 170}, - {15, 0x02, 170}, - {24, 0x02, 170}, - {31, 0x02, 170}, - {41, 0x02, 170}, - {56, 0x03, 170}, - {3, 0x02, 173}, - {6, 0x02, 173}, - {10, 0x02, 173}, - {15, 0x02, 173}, - {24, 0x02, 173}, - {31, 0x02, 173}, - {41, 0x02, 173}, - {56, 0x03, 173}, - }, - /* 133 */ - { - {137, 0x00, 0}, - {138, 0x00, 0}, - {140, 0x00, 0}, - {141, 0x00, 0}, - {144, 0x00, 0}, - {145, 0x00, 0}, - {147, 0x00, 0}, - {150, 0x00, 0}, - {156, 0x00, 0}, - {159, 0x00, 0}, - {163, 0x00, 0}, - {166, 0x00, 0}, - {171, 0x00, 0}, - {174, 0x00, 0}, - {181, 0x00, 0}, - {190, 0x00, 0}, - }, - /* 134 */ - { - {0, 0x03, 178}, - {0, 0x03, 181}, - {0, 0x03, 185}, - {0, 0x03, 186}, - {0, 0x03, 187}, - {0, 0x03, 189}, - {0, 0x03, 190}, - {0, 0x03, 196}, - {0, 0x03, 198}, - {0, 0x03, 228}, - {0, 0x03, 232}, - {0, 0x03, 233}, - {148, 0x00, 0}, - {149, 0x00, 0}, - {151, 0x00, 0}, - {152, 0x00, 0}, - }, - /* 135 */ - { - {1, 0x02, 178}, - {22, 0x03, 178}, - {1, 0x02, 181}, - {22, 0x03, 181}, - {1, 0x02, 185}, - {22, 0x03, 185}, - {1, 0x02, 186}, - {22, 0x03, 186}, - {1, 0x02, 187}, - {22, 0x03, 187}, - {1, 0x02, 189}, - {22, 0x03, 189}, - {1, 0x02, 190}, - {22, 0x03, 190}, - {1, 0x02, 196}, - {22, 0x03, 196}, - }, - /* 136 */ - { - {2, 0x02, 178}, - {9, 0x02, 178}, - {23, 0x02, 178}, - {40, 0x03, 178}, - {2, 0x02, 181}, - {9, 0x02, 181}, - {23, 0x02, 181}, - {40, 0x03, 181}, - {2, 0x02, 185}, - {9, 0x02, 185}, - {23, 0x02, 185}, - {40, 0x03, 185}, - {2, 0x02, 186}, - {9, 0x02, 186}, - {23, 0x02, 186}, - {40, 0x03, 186}, - }, - /* 137 */ - { - {3, 0x02, 178}, - {6, 0x02, 178}, - {10, 0x02, 178}, - {15, 0x02, 178}, - {24, 0x02, 178}, - {31, 0x02, 178}, - {41, 0x02, 178}, - {56, 0x03, 178}, - {3, 0x02, 181}, - {6, 0x02, 181}, - {10, 0x02, 181}, - {15, 0x02, 181}, - {24, 0x02, 181}, - {31, 0x02, 181}, - {41, 0x02, 181}, - {56, 0x03, 181}, - }, - /* 138 */ - { - {3, 0x02, 185}, - {6, 0x02, 185}, - {10, 0x02, 185}, - {15, 0x02, 185}, - {24, 0x02, 185}, - {31, 0x02, 185}, - {41, 0x02, 185}, - {56, 0x03, 185}, - {3, 0x02, 186}, - {6, 0x02, 186}, - {10, 0x02, 186}, - {15, 0x02, 186}, - {24, 0x02, 186}, - {31, 0x02, 186}, - {41, 0x02, 186}, - {56, 0x03, 186}, - }, - /* 139 */ - { - {2, 0x02, 187}, - {9, 0x02, 187}, - {23, 0x02, 187}, - {40, 0x03, 187}, - {2, 0x02, 189}, - {9, 0x02, 189}, - {23, 0x02, 189}, - {40, 0x03, 189}, - {2, 0x02, 190}, - {9, 0x02, 190}, - {23, 0x02, 190}, - {40, 0x03, 190}, - {2, 0x02, 196}, - {9, 0x02, 196}, - {23, 0x02, 196}, - {40, 0x03, 196}, - }, - /* 140 */ - { - {3, 0x02, 187}, - {6, 0x02, 187}, - {10, 0x02, 187}, - {15, 0x02, 187}, - {24, 0x02, 187}, - {31, 0x02, 187}, - {41, 0x02, 187}, - {56, 0x03, 187}, - {3, 0x02, 189}, - {6, 0x02, 189}, - {10, 0x02, 189}, - {15, 0x02, 189}, - {24, 0x02, 189}, - {31, 0x02, 189}, - {41, 0x02, 189}, - {56, 0x03, 189}, - }, - /* 141 */ - { - {3, 0x02, 190}, - {6, 0x02, 190}, - {10, 0x02, 190}, - {15, 0x02, 190}, - {24, 0x02, 190}, - {31, 0x02, 190}, - {41, 0x02, 190}, - {56, 0x03, 190}, - {3, 0x02, 196}, - {6, 0x02, 196}, - {10, 0x02, 196}, - {15, 0x02, 196}, - {24, 0x02, 196}, - {31, 0x02, 196}, - {41, 0x02, 196}, - {56, 0x03, 196}, - }, - /* 142 */ - { - {1, 0x02, 198}, - {22, 0x03, 198}, - {1, 0x02, 228}, - {22, 0x03, 228}, - {1, 0x02, 232}, - {22, 0x03, 232}, - {1, 0x02, 233}, - {22, 0x03, 233}, - {0, 0x03, 1}, - {0, 0x03, 135}, - {0, 0x03, 137}, - {0, 0x03, 138}, - {0, 0x03, 139}, - {0, 0x03, 140}, - {0, 0x03, 141}, - {0, 0x03, 143}, - }, - /* 143 */ - { - {2, 0x02, 198}, - {9, 0x02, 198}, - {23, 0x02, 198}, - {40, 0x03, 198}, - {2, 0x02, 228}, - {9, 0x02, 228}, - {23, 0x02, 228}, - {40, 0x03, 228}, - {2, 0x02, 232}, - {9, 0x02, 232}, - {23, 0x02, 232}, - {40, 0x03, 232}, - {2, 0x02, 233}, - {9, 0x02, 233}, - {23, 0x02, 233}, - {40, 0x03, 233}, - }, - /* 144 */ - { - {3, 0x02, 198}, - {6, 0x02, 198}, - {10, 0x02, 198}, - {15, 0x02, 198}, - {24, 0x02, 198}, - {31, 0x02, 198}, - {41, 0x02, 198}, - {56, 0x03, 198}, - {3, 0x02, 228}, - {6, 0x02, 228}, - {10, 0x02, 228}, - {15, 0x02, 228}, - {24, 0x02, 228}, - {31, 0x02, 228}, - {41, 0x02, 228}, - {56, 0x03, 228}, - }, - /* 145 */ - { - {3, 0x02, 232}, - {6, 0x02, 232}, - {10, 0x02, 232}, - {15, 0x02, 232}, - {24, 0x02, 232}, - {31, 0x02, 232}, - {41, 0x02, 232}, - {56, 0x03, 232}, - {3, 0x02, 233}, - {6, 0x02, 233}, - {10, 0x02, 233}, - {15, 0x02, 233}, - {24, 0x02, 233}, - {31, 0x02, 233}, - {41, 0x02, 233}, - {56, 0x03, 233}, - }, - /* 146 */ - { - {1, 0x02, 1}, - {22, 0x03, 1}, - {1, 0x02, 135}, - {22, 0x03, 135}, - {1, 0x02, 137}, - {22, 0x03, 137}, - {1, 0x02, 138}, - {22, 0x03, 138}, - {1, 0x02, 139}, - {22, 0x03, 139}, - {1, 0x02, 140}, - {22, 0x03, 140}, - {1, 0x02, 141}, - {22, 0x03, 141}, - {1, 0x02, 143}, - {22, 0x03, 143}, - }, - /* 147 */ - { - {2, 0x02, 1}, - {9, 0x02, 1}, - {23, 0x02, 1}, - {40, 0x03, 1}, - {2, 0x02, 135}, - {9, 0x02, 135}, - {23, 0x02, 135}, - {40, 0x03, 135}, - {2, 0x02, 137}, - {9, 0x02, 137}, - {23, 0x02, 137}, - {40, 0x03, 137}, - {2, 0x02, 138}, - {9, 0x02, 138}, - {23, 0x02, 138}, - {40, 0x03, 138}, - }, - /* 148 */ - { - {3, 0x02, 1}, - {6, 0x02, 1}, - {10, 0x02, 1}, - {15, 0x02, 1}, - {24, 0x02, 1}, - {31, 0x02, 1}, - {41, 0x02, 1}, - {56, 0x03, 1}, - {3, 0x02, 135}, - {6, 0x02, 135}, - {10, 0x02, 135}, - {15, 0x02, 135}, - {24, 0x02, 135}, - {31, 0x02, 135}, - {41, 0x02, 135}, - {56, 0x03, 135}, - }, - /* 149 */ - { - {3, 0x02, 137}, - {6, 0x02, 137}, - {10, 0x02, 137}, - {15, 0x02, 137}, - {24, 0x02, 137}, - {31, 0x02, 137}, - {41, 0x02, 137}, - {56, 0x03, 137}, - {3, 0x02, 138}, - {6, 0x02, 138}, - {10, 0x02, 138}, - {15, 0x02, 138}, - {24, 0x02, 138}, - {31, 0x02, 138}, - {41, 0x02, 138}, - {56, 0x03, 138}, - }, - /* 150 */ - { - {2, 0x02, 139}, - {9, 0x02, 139}, - {23, 0x02, 139}, - {40, 0x03, 139}, - {2, 0x02, 140}, - {9, 0x02, 140}, - {23, 0x02, 140}, - {40, 0x03, 140}, - {2, 0x02, 141}, - {9, 0x02, 141}, - {23, 0x02, 141}, - {40, 0x03, 141}, - {2, 0x02, 143}, - {9, 0x02, 143}, - {23, 0x02, 143}, - {40, 0x03, 143}, - }, - /* 151 */ - { - {3, 0x02, 139}, - {6, 0x02, 139}, - {10, 0x02, 139}, - {15, 0x02, 139}, - {24, 0x02, 139}, - {31, 0x02, 139}, - {41, 0x02, 139}, - {56, 0x03, 139}, - {3, 0x02, 140}, - {6, 0x02, 140}, - {10, 0x02, 140}, - {15, 0x02, 140}, - {24, 0x02, 140}, - {31, 0x02, 140}, - {41, 0x02, 140}, - {56, 0x03, 140}, - }, - /* 152 */ - { - {3, 0x02, 141}, - {6, 0x02, 141}, - {10, 0x02, 141}, - {15, 0x02, 141}, - {24, 0x02, 141}, - {31, 0x02, 141}, - {41, 0x02, 141}, - {56, 0x03, 141}, - {3, 0x02, 143}, - {6, 0x02, 143}, - {10, 0x02, 143}, - {15, 0x02, 143}, - {24, 0x02, 143}, - {31, 0x02, 143}, - {41, 0x02, 143}, - {56, 0x03, 143}, - }, - /* 153 */ - { - {157, 0x00, 0}, - {158, 0x00, 0}, - {160, 0x00, 0}, - {161, 0x00, 0}, - {164, 0x00, 0}, - {165, 0x00, 0}, - {167, 0x00, 0}, - {168, 0x00, 0}, - {172, 0x00, 0}, - {173, 0x00, 0}, - {175, 0x00, 0}, - {177, 0x00, 0}, - {182, 0x00, 0}, - {185, 0x00, 0}, - {191, 0x00, 0}, - {207, 0x00, 0}, - }, - /* 154 */ - { - {0, 0x03, 147}, - {0, 0x03, 149}, - {0, 0x03, 150}, - {0, 0x03, 151}, - {0, 0x03, 152}, - {0, 0x03, 155}, - {0, 0x03, 157}, - {0, 0x03, 158}, - {0, 0x03, 165}, - {0, 0x03, 166}, - {0, 0x03, 168}, - {0, 0x03, 174}, - {0, 0x03, 175}, - {0, 0x03, 180}, - {0, 0x03, 182}, - {0, 0x03, 183}, - }, - /* 155 */ - { - {1, 0x02, 147}, - {22, 0x03, 147}, - {1, 0x02, 149}, - {22, 0x03, 149}, - {1, 0x02, 150}, - {22, 0x03, 150}, - {1, 0x02, 151}, - {22, 0x03, 151}, - {1, 0x02, 152}, - {22, 0x03, 152}, - {1, 0x02, 155}, - {22, 0x03, 155}, - {1, 0x02, 157}, - {22, 0x03, 157}, - {1, 0x02, 158}, - {22, 0x03, 158}, - }, - /* 156 */ - { - {2, 0x02, 147}, - {9, 0x02, 147}, - {23, 0x02, 147}, - {40, 0x03, 147}, - {2, 0x02, 149}, - {9, 0x02, 149}, - {23, 0x02, 149}, - {40, 0x03, 149}, - {2, 0x02, 150}, - {9, 0x02, 150}, - {23, 0x02, 150}, - {40, 0x03, 150}, - {2, 0x02, 151}, - {9, 0x02, 151}, - {23, 0x02, 151}, - {40, 0x03, 151}, - }, - /* 157 */ - { - {3, 0x02, 147}, - {6, 0x02, 147}, - {10, 0x02, 147}, - {15, 0x02, 147}, - {24, 0x02, 147}, - {31, 0x02, 147}, - {41, 0x02, 147}, - {56, 0x03, 147}, - {3, 0x02, 149}, - {6, 0x02, 149}, - {10, 0x02, 149}, - {15, 0x02, 149}, - {24, 0x02, 149}, - {31, 0x02, 149}, - {41, 0x02, 149}, - {56, 0x03, 149}, - }, - /* 158 */ - { - {3, 0x02, 150}, - {6, 0x02, 150}, - {10, 0x02, 150}, - {15, 0x02, 150}, - {24, 0x02, 150}, - {31, 0x02, 150}, - {41, 0x02, 150}, - {56, 0x03, 150}, - {3, 0x02, 151}, - {6, 0x02, 151}, - {10, 0x02, 151}, - {15, 0x02, 151}, - {24, 0x02, 151}, - {31, 0x02, 151}, - {41, 0x02, 151}, - {56, 0x03, 151}, - }, - /* 159 */ - { - {2, 0x02, 152}, - {9, 0x02, 152}, - {23, 0x02, 152}, - {40, 0x03, 152}, - {2, 0x02, 155}, - {9, 0x02, 155}, - {23, 0x02, 155}, - {40, 0x03, 155}, - {2, 0x02, 157}, - {9, 0x02, 157}, - {23, 0x02, 157}, - {40, 0x03, 157}, - {2, 0x02, 158}, - {9, 0x02, 158}, - {23, 0x02, 158}, - {40, 0x03, 158}, - }, - /* 160 */ - { - {3, 0x02, 152}, - {6, 0x02, 152}, - {10, 0x02, 152}, - {15, 0x02, 152}, - {24, 0x02, 152}, - {31, 0x02, 152}, - {41, 0x02, 152}, - {56, 0x03, 152}, - {3, 0x02, 155}, - {6, 0x02, 155}, - {10, 0x02, 155}, - {15, 0x02, 155}, - {24, 0x02, 155}, - {31, 0x02, 155}, - {41, 0x02, 155}, - {56, 0x03, 155}, - }, - /* 161 */ - { - {3, 0x02, 157}, - {6, 0x02, 157}, - {10, 0x02, 157}, - {15, 0x02, 157}, - {24, 0x02, 157}, - {31, 0x02, 157}, - {41, 0x02, 157}, - {56, 0x03, 157}, - {3, 0x02, 158}, - {6, 0x02, 158}, - {10, 0x02, 158}, - {15, 0x02, 158}, - {24, 0x02, 158}, - {31, 0x02, 158}, - {41, 0x02, 158}, - {56, 0x03, 158}, - }, - /* 162 */ - { - {1, 0x02, 165}, - {22, 0x03, 165}, - {1, 0x02, 166}, - {22, 0x03, 166}, - {1, 0x02, 168}, - {22, 0x03, 168}, - {1, 0x02, 174}, - {22, 0x03, 174}, - {1, 0x02, 175}, - {22, 0x03, 175}, - {1, 0x02, 180}, - {22, 0x03, 180}, - {1, 0x02, 182}, - {22, 0x03, 182}, - {1, 0x02, 183}, - {22, 0x03, 183}, - }, - /* 163 */ - { - {2, 0x02, 165}, - {9, 0x02, 165}, - {23, 0x02, 165}, - {40, 0x03, 165}, - {2, 0x02, 166}, - {9, 0x02, 166}, - {23, 0x02, 166}, - {40, 0x03, 166}, - {2, 0x02, 168}, - {9, 0x02, 168}, - {23, 0x02, 168}, - {40, 0x03, 168}, - {2, 0x02, 174}, - {9, 0x02, 174}, - {23, 0x02, 174}, - {40, 0x03, 174}, - }, - /* 164 */ - { - {3, 0x02, 165}, - {6, 0x02, 165}, - {10, 0x02, 165}, - {15, 0x02, 165}, - {24, 0x02, 165}, - {31, 0x02, 165}, - {41, 0x02, 165}, - {56, 0x03, 165}, - {3, 0x02, 166}, - {6, 0x02, 166}, - {10, 0x02, 166}, - {15, 0x02, 166}, - {24, 0x02, 166}, - {31, 0x02, 166}, - {41, 0x02, 166}, - {56, 0x03, 166}, - }, - /* 165 */ - { - {3, 0x02, 168}, - {6, 0x02, 168}, - {10, 0x02, 168}, - {15, 0x02, 168}, - {24, 0x02, 168}, - {31, 0x02, 168}, - {41, 0x02, 168}, - {56, 0x03, 168}, - {3, 0x02, 174}, - {6, 0x02, 174}, - {10, 0x02, 174}, - {15, 0x02, 174}, - {24, 0x02, 174}, - {31, 0x02, 174}, - {41, 0x02, 174}, - {56, 0x03, 174}, - }, - /* 166 */ - { - {2, 0x02, 175}, - {9, 0x02, 175}, - {23, 0x02, 175}, - {40, 0x03, 175}, - {2, 0x02, 180}, - {9, 0x02, 180}, - {23, 0x02, 180}, - {40, 0x03, 180}, - {2, 0x02, 182}, - {9, 0x02, 182}, - {23, 0x02, 182}, - {40, 0x03, 182}, - {2, 0x02, 183}, - {9, 0x02, 183}, - {23, 0x02, 183}, - {40, 0x03, 183}, - }, - /* 167 */ - { - {3, 0x02, 175}, - {6, 0x02, 175}, - {10, 0x02, 175}, - {15, 0x02, 175}, - {24, 0x02, 175}, - {31, 0x02, 175}, - {41, 0x02, 175}, - {56, 0x03, 175}, - {3, 0x02, 180}, - {6, 0x02, 180}, - {10, 0x02, 180}, - {15, 0x02, 180}, - {24, 0x02, 180}, - {31, 0x02, 180}, - {41, 0x02, 180}, - {56, 0x03, 180}, - }, - /* 168 */ - { - {3, 0x02, 182}, - {6, 0x02, 182}, - {10, 0x02, 182}, - {15, 0x02, 182}, - {24, 0x02, 182}, - {31, 0x02, 182}, - {41, 0x02, 182}, - {56, 0x03, 182}, - {3, 0x02, 183}, - {6, 0x02, 183}, - {10, 0x02, 183}, - {15, 0x02, 183}, - {24, 0x02, 183}, - {31, 0x02, 183}, - {41, 0x02, 183}, - {56, 0x03, 183}, - }, - /* 169 */ - { - {0, 0x03, 188}, - {0, 0x03, 191}, - {0, 0x03, 197}, - {0, 0x03, 231}, - {0, 0x03, 239}, - {176, 0x00, 0}, - {178, 0x00, 0}, - {179, 0x00, 0}, - {183, 0x00, 0}, - {184, 0x00, 0}, - {186, 0x00, 0}, - {187, 0x00, 0}, - {192, 0x00, 0}, - {199, 0x00, 0}, - {208, 0x00, 0}, - {223, 0x00, 0}, - }, - /* 170 */ - { - {1, 0x02, 188}, - {22, 0x03, 188}, - {1, 0x02, 191}, - {22, 0x03, 191}, - {1, 0x02, 197}, - {22, 0x03, 197}, - {1, 0x02, 231}, - {22, 0x03, 231}, - {1, 0x02, 239}, - {22, 0x03, 239}, - {0, 0x03, 9}, - {0, 0x03, 142}, - {0, 0x03, 144}, - {0, 0x03, 145}, - {0, 0x03, 148}, - {0, 0x03, 159}, - }, - /* 171 */ - { - {2, 0x02, 188}, - {9, 0x02, 188}, - {23, 0x02, 188}, - {40, 0x03, 188}, - {2, 0x02, 191}, - {9, 0x02, 191}, - {23, 0x02, 191}, - {40, 0x03, 191}, - {2, 0x02, 197}, - {9, 0x02, 197}, - {23, 0x02, 197}, - {40, 0x03, 197}, - {2, 0x02, 231}, - {9, 0x02, 231}, - {23, 0x02, 231}, - {40, 0x03, 231}, - }, - /* 172 */ - { - {3, 0x02, 188}, - {6, 0x02, 188}, - {10, 0x02, 188}, - {15, 0x02, 188}, - {24, 0x02, 188}, - {31, 0x02, 188}, - {41, 0x02, 188}, - {56, 0x03, 188}, - {3, 0x02, 191}, - {6, 0x02, 191}, - {10, 0x02, 191}, - {15, 0x02, 191}, - {24, 0x02, 191}, - {31, 0x02, 191}, - {41, 0x02, 191}, - {56, 0x03, 191}, - }, - /* 173 */ - { - {3, 0x02, 197}, - {6, 0x02, 197}, - {10, 0x02, 197}, - {15, 0x02, 197}, - {24, 0x02, 197}, - {31, 0x02, 197}, - {41, 0x02, 197}, - {56, 0x03, 197}, - {3, 0x02, 231}, - {6, 0x02, 231}, - {10, 0x02, 231}, - {15, 0x02, 231}, - {24, 0x02, 231}, - {31, 0x02, 231}, - {41, 0x02, 231}, - {56, 0x03, 231}, - }, - /* 174 */ - { - {2, 0x02, 239}, - {9, 0x02, 239}, - {23, 0x02, 239}, - {40, 0x03, 239}, - {1, 0x02, 9}, - {22, 0x03, 9}, - {1, 0x02, 142}, - {22, 0x03, 142}, - {1, 0x02, 144}, - {22, 0x03, 144}, - {1, 0x02, 145}, - {22, 0x03, 145}, - {1, 0x02, 148}, - {22, 0x03, 148}, - {1, 0x02, 159}, - {22, 0x03, 159}, - }, - /* 175 */ - { - {3, 0x02, 239}, - {6, 0x02, 239}, - {10, 0x02, 239}, - {15, 0x02, 239}, - {24, 0x02, 239}, - {31, 0x02, 239}, - {41, 0x02, 239}, - {56, 0x03, 239}, - {2, 0x02, 9}, - {9, 0x02, 9}, - {23, 0x02, 9}, - {40, 0x03, 9}, - {2, 0x02, 142}, - {9, 0x02, 142}, - {23, 0x02, 142}, - {40, 0x03, 142}, - }, - /* 176 */ - { - {3, 0x02, 9}, - {6, 0x02, 9}, - {10, 0x02, 9}, - {15, 0x02, 9}, - {24, 0x02, 9}, - {31, 0x02, 9}, - {41, 0x02, 9}, - {56, 0x03, 9}, - {3, 0x02, 142}, - {6, 0x02, 142}, - {10, 0x02, 142}, - {15, 0x02, 142}, - {24, 0x02, 142}, - {31, 0x02, 142}, - {41, 0x02, 142}, - {56, 0x03, 142}, - }, - /* 177 */ - { - {2, 0x02, 144}, - {9, 0x02, 144}, - {23, 0x02, 144}, - {40, 0x03, 144}, - {2, 0x02, 145}, - {9, 0x02, 145}, - {23, 0x02, 145}, - {40, 0x03, 145}, - {2, 0x02, 148}, - {9, 0x02, 148}, - {23, 0x02, 148}, - {40, 0x03, 148}, - {2, 0x02, 159}, - {9, 0x02, 159}, - {23, 0x02, 159}, - {40, 0x03, 159}, - }, - /* 178 */ - { - {3, 0x02, 144}, - {6, 0x02, 144}, - {10, 0x02, 144}, - {15, 0x02, 144}, - {24, 0x02, 144}, - {31, 0x02, 144}, - {41, 0x02, 144}, - {56, 0x03, 144}, - {3, 0x02, 145}, - {6, 0x02, 145}, - {10, 0x02, 145}, - {15, 0x02, 145}, - {24, 0x02, 145}, - {31, 0x02, 145}, - {41, 0x02, 145}, - {56, 0x03, 145}, - }, - /* 179 */ - { - {3, 0x02, 148}, - {6, 0x02, 148}, - {10, 0x02, 148}, - {15, 0x02, 148}, - {24, 0x02, 148}, - {31, 0x02, 148}, - {41, 0x02, 148}, - {56, 0x03, 148}, - {3, 0x02, 159}, - {6, 0x02, 159}, - {10, 0x02, 159}, - {15, 0x02, 159}, - {24, 0x02, 159}, - {31, 0x02, 159}, - {41, 0x02, 159}, - {56, 0x03, 159}, - }, - /* 180 */ - { - {0, 0x03, 171}, - {0, 0x03, 206}, - {0, 0x03, 215}, - {0, 0x03, 225}, - {0, 0x03, 236}, - {0, 0x03, 237}, - {188, 0x00, 0}, - {189, 0x00, 0}, - {193, 0x00, 0}, - {196, 0x00, 0}, - {200, 0x00, 0}, - {203, 0x00, 0}, - {209, 0x00, 0}, - {216, 0x00, 0}, - {224, 0x00, 0}, - {238, 0x00, 0}, - }, - /* 181 */ - { - {1, 0x02, 171}, - {22, 0x03, 171}, - {1, 0x02, 206}, - {22, 0x03, 206}, - {1, 0x02, 215}, - {22, 0x03, 215}, - {1, 0x02, 225}, - {22, 0x03, 225}, - {1, 0x02, 236}, - {22, 0x03, 236}, - {1, 0x02, 237}, - {22, 0x03, 237}, - {0, 0x03, 199}, - {0, 0x03, 207}, - {0, 0x03, 234}, - {0, 0x03, 235}, - }, - /* 182 */ - { - {2, 0x02, 171}, - {9, 0x02, 171}, - {23, 0x02, 171}, - {40, 0x03, 171}, - {2, 0x02, 206}, - {9, 0x02, 206}, - {23, 0x02, 206}, - {40, 0x03, 206}, - {2, 0x02, 215}, - {9, 0x02, 215}, - {23, 0x02, 215}, - {40, 0x03, 215}, - {2, 0x02, 225}, - {9, 0x02, 225}, - {23, 0x02, 225}, - {40, 0x03, 225}, - }, - /* 183 */ - { - {3, 0x02, 171}, - {6, 0x02, 171}, - {10, 0x02, 171}, - {15, 0x02, 171}, - {24, 0x02, 171}, - {31, 0x02, 171}, - {41, 0x02, 171}, - {56, 0x03, 171}, - {3, 0x02, 206}, - {6, 0x02, 206}, - {10, 0x02, 206}, - {15, 0x02, 206}, - {24, 0x02, 206}, - {31, 0x02, 206}, - {41, 0x02, 206}, - {56, 0x03, 206}, - }, - /* 184 */ - { - {3, 0x02, 215}, - {6, 0x02, 215}, - {10, 0x02, 215}, - {15, 0x02, 215}, - {24, 0x02, 215}, - {31, 0x02, 215}, - {41, 0x02, 215}, - {56, 0x03, 215}, - {3, 0x02, 225}, - {6, 0x02, 225}, - {10, 0x02, 225}, - {15, 0x02, 225}, - {24, 0x02, 225}, - {31, 0x02, 225}, - {41, 0x02, 225}, - {56, 0x03, 225}, - }, - /* 185 */ - { - {2, 0x02, 236}, - {9, 0x02, 236}, - {23, 0x02, 236}, - {40, 0x03, 236}, - {2, 0x02, 237}, - {9, 0x02, 237}, - {23, 0x02, 237}, - {40, 0x03, 237}, - {1, 0x02, 199}, - {22, 0x03, 199}, - {1, 0x02, 207}, - {22, 0x03, 207}, - {1, 0x02, 234}, - {22, 0x03, 234}, - {1, 0x02, 235}, - {22, 0x03, 235}, - }, - /* 186 */ - { - {3, 0x02, 236}, - {6, 0x02, 236}, - {10, 0x02, 236}, - {15, 0x02, 236}, - {24, 0x02, 236}, - {31, 0x02, 236}, - {41, 0x02, 236}, - {56, 0x03, 236}, - {3, 0x02, 237}, - {6, 0x02, 237}, - {10, 0x02, 237}, - {15, 0x02, 237}, - {24, 0x02, 237}, - {31, 0x02, 237}, - {41, 0x02, 237}, - {56, 0x03, 237}, - }, - /* 187 */ - { - {2, 0x02, 199}, - {9, 0x02, 199}, - {23, 0x02, 199}, - {40, 0x03, 199}, - {2, 0x02, 207}, - {9, 0x02, 207}, - {23, 0x02, 207}, - {40, 0x03, 207}, - {2, 0x02, 234}, - {9, 0x02, 234}, - {23, 0x02, 234}, - {40, 0x03, 234}, - {2, 0x02, 235}, - {9, 0x02, 235}, - {23, 0x02, 235}, - {40, 0x03, 235}, - }, - /* 188 */ - { - {3, 0x02, 199}, - {6, 0x02, 199}, - {10, 0x02, 199}, - {15, 0x02, 199}, - {24, 0x02, 199}, - {31, 0x02, 199}, - {41, 0x02, 199}, - {56, 0x03, 199}, - {3, 0x02, 207}, - {6, 0x02, 207}, - {10, 0x02, 207}, - {15, 0x02, 207}, - {24, 0x02, 207}, - {31, 0x02, 207}, - {41, 0x02, 207}, - {56, 0x03, 207}, - }, - /* 189 */ - { - {3, 0x02, 234}, - {6, 0x02, 234}, - {10, 0x02, 234}, - {15, 0x02, 234}, - {24, 0x02, 234}, - {31, 0x02, 234}, - {41, 0x02, 234}, - {56, 0x03, 234}, - {3, 0x02, 235}, - {6, 0x02, 235}, - {10, 0x02, 235}, - {15, 0x02, 235}, - {24, 0x02, 235}, - {31, 0x02, 235}, - {41, 0x02, 235}, - {56, 0x03, 235}, - }, - /* 190 */ - { - {194, 0x00, 0}, - {195, 0x00, 0}, - {197, 0x00, 0}, - {198, 0x00, 0}, - {201, 0x00, 0}, - {202, 0x00, 0}, - {204, 0x00, 0}, - {205, 0x00, 0}, - {210, 0x00, 0}, - {213, 0x00, 0}, - {217, 0x00, 0}, - {220, 0x00, 0}, - {225, 0x00, 0}, - {231, 0x00, 0}, - {239, 0x00, 0}, - {246, 0x00, 0}, - }, - /* 191 */ - { - {0, 0x03, 192}, - {0, 0x03, 193}, - {0, 0x03, 200}, - {0, 0x03, 201}, - {0, 0x03, 202}, - {0, 0x03, 205}, - {0, 0x03, 210}, - {0, 0x03, 213}, - {0, 0x03, 218}, - {0, 0x03, 219}, - {0, 0x03, 238}, - {0, 0x03, 240}, - {0, 0x03, 242}, - {0, 0x03, 243}, - {0, 0x03, 255}, - {206, 0x00, 0}, - }, - /* 192 */ - { - {1, 0x02, 192}, - {22, 0x03, 192}, - {1, 0x02, 193}, - {22, 0x03, 193}, - {1, 0x02, 200}, - {22, 0x03, 200}, - {1, 0x02, 201}, - {22, 0x03, 201}, - {1, 0x02, 202}, - {22, 0x03, 202}, - {1, 0x02, 205}, - {22, 0x03, 205}, - {1, 0x02, 210}, - {22, 0x03, 210}, - {1, 0x02, 213}, - {22, 0x03, 213}, - }, - /* 193 */ - { - {2, 0x02, 192}, - {9, 0x02, 192}, - {23, 0x02, 192}, - {40, 0x03, 192}, - {2, 0x02, 193}, - {9, 0x02, 193}, - {23, 0x02, 193}, - {40, 0x03, 193}, - {2, 0x02, 200}, - {9, 0x02, 200}, - {23, 0x02, 200}, - {40, 0x03, 200}, - {2, 0x02, 201}, - {9, 0x02, 201}, - {23, 0x02, 201}, - {40, 0x03, 201}, - }, - /* 194 */ - { - {3, 0x02, 192}, - {6, 0x02, 192}, - {10, 0x02, 192}, - {15, 0x02, 192}, - {24, 0x02, 192}, - {31, 0x02, 192}, - {41, 0x02, 192}, - {56, 0x03, 192}, - {3, 0x02, 193}, - {6, 0x02, 193}, - {10, 0x02, 193}, - {15, 0x02, 193}, - {24, 0x02, 193}, - {31, 0x02, 193}, - {41, 0x02, 193}, - {56, 0x03, 193}, - }, - /* 195 */ - { - {3, 0x02, 200}, - {6, 0x02, 200}, - {10, 0x02, 200}, - {15, 0x02, 200}, - {24, 0x02, 200}, - {31, 0x02, 200}, - {41, 0x02, 200}, - {56, 0x03, 200}, - {3, 0x02, 201}, - {6, 0x02, 201}, - {10, 0x02, 201}, - {15, 0x02, 201}, - {24, 0x02, 201}, - {31, 0x02, 201}, - {41, 0x02, 201}, - {56, 0x03, 201}, - }, - /* 196 */ - { - {2, 0x02, 202}, - {9, 0x02, 202}, - {23, 0x02, 202}, - {40, 0x03, 202}, - {2, 0x02, 205}, - {9, 0x02, 205}, - {23, 0x02, 205}, - {40, 0x03, 205}, - {2, 0x02, 210}, - {9, 0x02, 210}, - {23, 0x02, 210}, - {40, 0x03, 210}, - {2, 0x02, 213}, - {9, 0x02, 213}, - {23, 0x02, 213}, - {40, 0x03, 213}, - }, - /* 197 */ - { - {3, 0x02, 202}, - {6, 0x02, 202}, - {10, 0x02, 202}, - {15, 0x02, 202}, - {24, 0x02, 202}, - {31, 0x02, 202}, - {41, 0x02, 202}, - {56, 0x03, 202}, - {3, 0x02, 205}, - {6, 0x02, 205}, - {10, 0x02, 205}, - {15, 0x02, 205}, - {24, 0x02, 205}, - {31, 0x02, 205}, - {41, 0x02, 205}, - {56, 0x03, 205}, - }, - /* 198 */ - { - {3, 0x02, 210}, - {6, 0x02, 210}, - {10, 0x02, 210}, - {15, 0x02, 210}, - {24, 0x02, 210}, - {31, 0x02, 210}, - {41, 0x02, 210}, - {56, 0x03, 210}, - {3, 0x02, 213}, - {6, 0x02, 213}, - {10, 0x02, 213}, - {15, 0x02, 213}, - {24, 0x02, 213}, - {31, 0x02, 213}, - {41, 0x02, 213}, - {56, 0x03, 213}, - }, - /* 199 */ - { - {1, 0x02, 218}, - {22, 0x03, 218}, - {1, 0x02, 219}, - {22, 0x03, 219}, - {1, 0x02, 238}, - {22, 0x03, 238}, - {1, 0x02, 240}, - {22, 0x03, 240}, - {1, 0x02, 242}, - {22, 0x03, 242}, - {1, 0x02, 243}, - {22, 0x03, 243}, - {1, 0x02, 255}, - {22, 0x03, 255}, - {0, 0x03, 203}, - {0, 0x03, 204}, - }, - /* 200 */ - { - {2, 0x02, 218}, - {9, 0x02, 218}, - {23, 0x02, 218}, - {40, 0x03, 218}, - {2, 0x02, 219}, - {9, 0x02, 219}, - {23, 0x02, 219}, - {40, 0x03, 219}, - {2, 0x02, 238}, - {9, 0x02, 238}, - {23, 0x02, 238}, - {40, 0x03, 238}, - {2, 0x02, 240}, - {9, 0x02, 240}, - {23, 0x02, 240}, - {40, 0x03, 240}, - }, - /* 201 */ - { - {3, 0x02, 218}, - {6, 0x02, 218}, - {10, 0x02, 218}, - {15, 0x02, 218}, - {24, 0x02, 218}, - {31, 0x02, 218}, - {41, 0x02, 218}, - {56, 0x03, 218}, - {3, 0x02, 219}, - {6, 0x02, 219}, - {10, 0x02, 219}, - {15, 0x02, 219}, - {24, 0x02, 219}, - {31, 0x02, 219}, - {41, 0x02, 219}, - {56, 0x03, 219}, - }, - /* 202 */ - { - {3, 0x02, 238}, - {6, 0x02, 238}, - {10, 0x02, 238}, - {15, 0x02, 238}, - {24, 0x02, 238}, - {31, 0x02, 238}, - {41, 0x02, 238}, - {56, 0x03, 238}, - {3, 0x02, 240}, - {6, 0x02, 240}, - {10, 0x02, 240}, - {15, 0x02, 240}, - {24, 0x02, 240}, - {31, 0x02, 240}, - {41, 0x02, 240}, - {56, 0x03, 240}, - }, - /* 203 */ - { - {2, 0x02, 242}, - {9, 0x02, 242}, - {23, 0x02, 242}, - {40, 0x03, 242}, - {2, 0x02, 243}, - {9, 0x02, 243}, - {23, 0x02, 243}, - {40, 0x03, 243}, - {2, 0x02, 255}, - {9, 0x02, 255}, - {23, 0x02, 255}, - {40, 0x03, 255}, - {1, 0x02, 203}, - {22, 0x03, 203}, - {1, 0x02, 204}, - {22, 0x03, 204}, - }, - /* 204 */ - { - {3, 0x02, 242}, - {6, 0x02, 242}, - {10, 0x02, 242}, - {15, 0x02, 242}, - {24, 0x02, 242}, - {31, 0x02, 242}, - {41, 0x02, 242}, - {56, 0x03, 242}, - {3, 0x02, 243}, - {6, 0x02, 243}, - {10, 0x02, 243}, - {15, 0x02, 243}, - {24, 0x02, 243}, - {31, 0x02, 243}, - {41, 0x02, 243}, - {56, 0x03, 243}, - }, - /* 205 */ - { - {3, 0x02, 255}, - {6, 0x02, 255}, - {10, 0x02, 255}, - {15, 0x02, 255}, - {24, 0x02, 255}, - {31, 0x02, 255}, - {41, 0x02, 255}, - {56, 0x03, 255}, - {2, 0x02, 203}, - {9, 0x02, 203}, - {23, 0x02, 203}, - {40, 0x03, 203}, - {2, 0x02, 204}, - {9, 0x02, 204}, - {23, 0x02, 204}, - {40, 0x03, 204}, - }, - /* 206 */ - { - {3, 0x02, 203}, - {6, 0x02, 203}, - {10, 0x02, 203}, - {15, 0x02, 203}, - {24, 0x02, 203}, - {31, 0x02, 203}, - {41, 0x02, 203}, - {56, 0x03, 203}, - {3, 0x02, 204}, - {6, 0x02, 204}, - {10, 0x02, 204}, - {15, 0x02, 204}, - {24, 0x02, 204}, - {31, 0x02, 204}, - {41, 0x02, 204}, - {56, 0x03, 204}, - }, - /* 207 */ - { - {211, 0x00, 0}, - {212, 0x00, 0}, - {214, 0x00, 0}, - {215, 0x00, 0}, - {218, 0x00, 0}, - {219, 0x00, 0}, - {221, 0x00, 0}, - {222, 0x00, 0}, - {226, 0x00, 0}, - {228, 0x00, 0}, - {232, 0x00, 0}, - {235, 0x00, 0}, - {240, 0x00, 0}, - {243, 0x00, 0}, - {247, 0x00, 0}, - {250, 0x00, 0}, - }, - /* 208 */ - { - {0, 0x03, 211}, - {0, 0x03, 212}, - {0, 0x03, 214}, - {0, 0x03, 221}, - {0, 0x03, 222}, - {0, 0x03, 223}, - {0, 0x03, 241}, - {0, 0x03, 244}, - {0, 0x03, 245}, - {0, 0x03, 246}, - {0, 0x03, 247}, - {0, 0x03, 248}, - {0, 0x03, 250}, - {0, 0x03, 251}, - {0, 0x03, 252}, - {0, 0x03, 253}, - }, - /* 209 */ - { - {1, 0x02, 211}, - {22, 0x03, 211}, - {1, 0x02, 212}, - {22, 0x03, 212}, - {1, 0x02, 214}, - {22, 0x03, 214}, - {1, 0x02, 221}, - {22, 0x03, 221}, - {1, 0x02, 222}, - {22, 0x03, 222}, - {1, 0x02, 223}, - {22, 0x03, 223}, - {1, 0x02, 241}, - {22, 0x03, 241}, - {1, 0x02, 244}, - {22, 0x03, 244}, - }, - /* 210 */ - { - {2, 0x02, 211}, - {9, 0x02, 211}, - {23, 0x02, 211}, - {40, 0x03, 211}, - {2, 0x02, 212}, - {9, 0x02, 212}, - {23, 0x02, 212}, - {40, 0x03, 212}, - {2, 0x02, 214}, - {9, 0x02, 214}, - {23, 0x02, 214}, - {40, 0x03, 214}, - {2, 0x02, 221}, - {9, 0x02, 221}, - {23, 0x02, 221}, - {40, 0x03, 221}, - }, - /* 211 */ - { - {3, 0x02, 211}, - {6, 0x02, 211}, - {10, 0x02, 211}, - {15, 0x02, 211}, - {24, 0x02, 211}, - {31, 0x02, 211}, - {41, 0x02, 211}, - {56, 0x03, 211}, - {3, 0x02, 212}, - {6, 0x02, 212}, - {10, 0x02, 212}, - {15, 0x02, 212}, - {24, 0x02, 212}, - {31, 0x02, 212}, - {41, 0x02, 212}, - {56, 0x03, 212}, - }, - /* 212 */ - { - {3, 0x02, 214}, - {6, 0x02, 214}, - {10, 0x02, 214}, - {15, 0x02, 214}, - {24, 0x02, 214}, - {31, 0x02, 214}, - {41, 0x02, 214}, - {56, 0x03, 214}, - {3, 0x02, 221}, - {6, 0x02, 221}, - {10, 0x02, 221}, - {15, 0x02, 221}, - {24, 0x02, 221}, - {31, 0x02, 221}, - {41, 0x02, 221}, - {56, 0x03, 221}, - }, - /* 213 */ - { - {2, 0x02, 222}, - {9, 0x02, 222}, - {23, 0x02, 222}, - {40, 0x03, 222}, - {2, 0x02, 223}, - {9, 0x02, 223}, - {23, 0x02, 223}, - {40, 0x03, 223}, - {2, 0x02, 241}, - {9, 0x02, 241}, - {23, 0x02, 241}, - {40, 0x03, 241}, - {2, 0x02, 244}, - {9, 0x02, 244}, - {23, 0x02, 244}, - {40, 0x03, 244}, - }, - /* 214 */ - { - {3, 0x02, 222}, - {6, 0x02, 222}, - {10, 0x02, 222}, - {15, 0x02, 222}, - {24, 0x02, 222}, - {31, 0x02, 222}, - {41, 0x02, 222}, - {56, 0x03, 222}, - {3, 0x02, 223}, - {6, 0x02, 223}, - {10, 0x02, 223}, - {15, 0x02, 223}, - {24, 0x02, 223}, - {31, 0x02, 223}, - {41, 0x02, 223}, - {56, 0x03, 223}, - }, - /* 215 */ - { - {3, 0x02, 241}, - {6, 0x02, 241}, - {10, 0x02, 241}, - {15, 0x02, 241}, - {24, 0x02, 241}, - {31, 0x02, 241}, - {41, 0x02, 241}, - {56, 0x03, 241}, - {3, 0x02, 244}, - {6, 0x02, 244}, - {10, 0x02, 244}, - {15, 0x02, 244}, - {24, 0x02, 244}, - {31, 0x02, 244}, - {41, 0x02, 244}, - {56, 0x03, 244}, - }, - /* 216 */ - { - {1, 0x02, 245}, - {22, 0x03, 245}, - {1, 0x02, 246}, - {22, 0x03, 246}, - {1, 0x02, 247}, - {22, 0x03, 247}, - {1, 0x02, 248}, - {22, 0x03, 248}, - {1, 0x02, 250}, - {22, 0x03, 250}, - {1, 0x02, 251}, - {22, 0x03, 251}, - {1, 0x02, 252}, - {22, 0x03, 252}, - {1, 0x02, 253}, - {22, 0x03, 253}, - }, - /* 217 */ - { - {2, 0x02, 245}, - {9, 0x02, 245}, - {23, 0x02, 245}, - {40, 0x03, 245}, - {2, 0x02, 246}, - {9, 0x02, 246}, - {23, 0x02, 246}, - {40, 0x03, 246}, - {2, 0x02, 247}, - {9, 0x02, 247}, - {23, 0x02, 247}, - {40, 0x03, 247}, - {2, 0x02, 248}, - {9, 0x02, 248}, - {23, 0x02, 248}, - {40, 0x03, 248}, - }, - /* 218 */ - { - {3, 0x02, 245}, - {6, 0x02, 245}, - {10, 0x02, 245}, - {15, 0x02, 245}, - {24, 0x02, 245}, - {31, 0x02, 245}, - {41, 0x02, 245}, - {56, 0x03, 245}, - {3, 0x02, 246}, - {6, 0x02, 246}, - {10, 0x02, 246}, - {15, 0x02, 246}, - {24, 0x02, 246}, - {31, 0x02, 246}, - {41, 0x02, 246}, - {56, 0x03, 246}, - }, - /* 219 */ - { - {3, 0x02, 247}, - {6, 0x02, 247}, - {10, 0x02, 247}, - {15, 0x02, 247}, - {24, 0x02, 247}, - {31, 0x02, 247}, - {41, 0x02, 247}, - {56, 0x03, 247}, - {3, 0x02, 248}, - {6, 0x02, 248}, - {10, 0x02, 248}, - {15, 0x02, 248}, - {24, 0x02, 248}, - {31, 0x02, 248}, - {41, 0x02, 248}, - {56, 0x03, 248}, - }, - /* 220 */ - { - {2, 0x02, 250}, - {9, 0x02, 250}, - {23, 0x02, 250}, - {40, 0x03, 250}, - {2, 0x02, 251}, - {9, 0x02, 251}, - {23, 0x02, 251}, - {40, 0x03, 251}, - {2, 0x02, 252}, - {9, 0x02, 252}, - {23, 0x02, 252}, - {40, 0x03, 252}, - {2, 0x02, 253}, - {9, 0x02, 253}, - {23, 0x02, 253}, - {40, 0x03, 253}, - }, - /* 221 */ - { - {3, 0x02, 250}, - {6, 0x02, 250}, - {10, 0x02, 250}, - {15, 0x02, 250}, - {24, 0x02, 250}, - {31, 0x02, 250}, - {41, 0x02, 250}, - {56, 0x03, 250}, - {3, 0x02, 251}, - {6, 0x02, 251}, - {10, 0x02, 251}, - {15, 0x02, 251}, - {24, 0x02, 251}, - {31, 0x02, 251}, - {41, 0x02, 251}, - {56, 0x03, 251}, - }, - /* 222 */ - { - {3, 0x02, 252}, - {6, 0x02, 252}, - {10, 0x02, 252}, - {15, 0x02, 252}, - {24, 0x02, 252}, - {31, 0x02, 252}, - {41, 0x02, 252}, - {56, 0x03, 252}, - {3, 0x02, 253}, - {6, 0x02, 253}, - {10, 0x02, 253}, - {15, 0x02, 253}, - {24, 0x02, 253}, - {31, 0x02, 253}, - {41, 0x02, 253}, - {56, 0x03, 253}, - }, - /* 223 */ - { - {0, 0x03, 254}, - {227, 0x00, 0}, - {229, 0x00, 0}, - {230, 0x00, 0}, - {233, 0x00, 0}, - {234, 0x00, 0}, - {236, 0x00, 0}, - {237, 0x00, 0}, - {241, 0x00, 0}, - {242, 0x00, 0}, - {244, 0x00, 0}, - {245, 0x00, 0}, - {248, 0x00, 0}, - {249, 0x00, 0}, - {251, 0x00, 0}, - {252, 0x00, 0}, - }, - /* 224 */ - { - {1, 0x02, 254}, - {22, 0x03, 254}, - {0, 0x03, 2}, - {0, 0x03, 3}, - {0, 0x03, 4}, - {0, 0x03, 5}, - {0, 0x03, 6}, - {0, 0x03, 7}, - {0, 0x03, 8}, - {0, 0x03, 11}, - {0, 0x03, 12}, - {0, 0x03, 14}, - {0, 0x03, 15}, - {0, 0x03, 16}, - {0, 0x03, 17}, - {0, 0x03, 18}, - }, - /* 225 */ - { - {2, 0x02, 254}, - {9, 0x02, 254}, - {23, 0x02, 254}, - {40, 0x03, 254}, - {1, 0x02, 2}, - {22, 0x03, 2}, - {1, 0x02, 3}, - {22, 0x03, 3}, - {1, 0x02, 4}, - {22, 0x03, 4}, - {1, 0x02, 5}, - {22, 0x03, 5}, - {1, 0x02, 6}, - {22, 0x03, 6}, - {1, 0x02, 7}, - {22, 0x03, 7}, - }, - /* 226 */ - { - {3, 0x02, 254}, - {6, 0x02, 254}, - {10, 0x02, 254}, - {15, 0x02, 254}, - {24, 0x02, 254}, - {31, 0x02, 254}, - {41, 0x02, 254}, - {56, 0x03, 254}, - {2, 0x02, 2}, - {9, 0x02, 2}, - {23, 0x02, 2}, - {40, 0x03, 2}, - {2, 0x02, 3}, - {9, 0x02, 3}, - {23, 0x02, 3}, - {40, 0x03, 3}, - }, - /* 227 */ - { - {3, 0x02, 2}, - {6, 0x02, 2}, - {10, 0x02, 2}, - {15, 0x02, 2}, - {24, 0x02, 2}, - {31, 0x02, 2}, - {41, 0x02, 2}, - {56, 0x03, 2}, - {3, 0x02, 3}, - {6, 0x02, 3}, - {10, 0x02, 3}, - {15, 0x02, 3}, - {24, 0x02, 3}, - {31, 0x02, 3}, - {41, 0x02, 3}, - {56, 0x03, 3}, - }, - /* 228 */ - { - {2, 0x02, 4}, - {9, 0x02, 4}, - {23, 0x02, 4}, - {40, 0x03, 4}, - {2, 0x02, 5}, - {9, 0x02, 5}, - {23, 0x02, 5}, - {40, 0x03, 5}, - {2, 0x02, 6}, - {9, 0x02, 6}, - {23, 0x02, 6}, - {40, 0x03, 6}, - {2, 0x02, 7}, - {9, 0x02, 7}, - {23, 0x02, 7}, - {40, 0x03, 7}, - }, - /* 229 */ - { - {3, 0x02, 4}, - {6, 0x02, 4}, - {10, 0x02, 4}, - {15, 0x02, 4}, - {24, 0x02, 4}, - {31, 0x02, 4}, - {41, 0x02, 4}, - {56, 0x03, 4}, - {3, 0x02, 5}, - {6, 0x02, 5}, - {10, 0x02, 5}, - {15, 0x02, 5}, - {24, 0x02, 5}, - {31, 0x02, 5}, - {41, 0x02, 5}, - {56, 0x03, 5}, - }, - /* 230 */ - { - {3, 0x02, 6}, - {6, 0x02, 6}, - {10, 0x02, 6}, - {15, 0x02, 6}, - {24, 0x02, 6}, - {31, 0x02, 6}, - {41, 0x02, 6}, - {56, 0x03, 6}, - {3, 0x02, 7}, - {6, 0x02, 7}, - {10, 0x02, 7}, - {15, 0x02, 7}, - {24, 0x02, 7}, - {31, 0x02, 7}, - {41, 0x02, 7}, - {56, 0x03, 7}, - }, - /* 231 */ - { - {1, 0x02, 8}, - {22, 0x03, 8}, - {1, 0x02, 11}, - {22, 0x03, 11}, - {1, 0x02, 12}, - {22, 0x03, 12}, - {1, 0x02, 14}, - {22, 0x03, 14}, - {1, 0x02, 15}, - {22, 0x03, 15}, - {1, 0x02, 16}, - {22, 0x03, 16}, - {1, 0x02, 17}, - {22, 0x03, 17}, - {1, 0x02, 18}, - {22, 0x03, 18}, - }, - /* 232 */ - { - {2, 0x02, 8}, - {9, 0x02, 8}, - {23, 0x02, 8}, - {40, 0x03, 8}, - {2, 0x02, 11}, - {9, 0x02, 11}, - {23, 0x02, 11}, - {40, 0x03, 11}, - {2, 0x02, 12}, - {9, 0x02, 12}, - {23, 0x02, 12}, - {40, 0x03, 12}, - {2, 0x02, 14}, - {9, 0x02, 14}, - {23, 0x02, 14}, - {40, 0x03, 14}, - }, - /* 233 */ - { - {3, 0x02, 8}, - {6, 0x02, 8}, - {10, 0x02, 8}, - {15, 0x02, 8}, - {24, 0x02, 8}, - {31, 0x02, 8}, - {41, 0x02, 8}, - {56, 0x03, 8}, - {3, 0x02, 11}, - {6, 0x02, 11}, - {10, 0x02, 11}, - {15, 0x02, 11}, - {24, 0x02, 11}, - {31, 0x02, 11}, - {41, 0x02, 11}, - {56, 0x03, 11}, - }, - /* 234 */ - { - {3, 0x02, 12}, - {6, 0x02, 12}, - {10, 0x02, 12}, - {15, 0x02, 12}, - {24, 0x02, 12}, - {31, 0x02, 12}, - {41, 0x02, 12}, - {56, 0x03, 12}, - {3, 0x02, 14}, - {6, 0x02, 14}, - {10, 0x02, 14}, - {15, 0x02, 14}, - {24, 0x02, 14}, - {31, 0x02, 14}, - {41, 0x02, 14}, - {56, 0x03, 14}, - }, - /* 235 */ - { - {2, 0x02, 15}, - {9, 0x02, 15}, - {23, 0x02, 15}, - {40, 0x03, 15}, - {2, 0x02, 16}, - {9, 0x02, 16}, - {23, 0x02, 16}, - {40, 0x03, 16}, - {2, 0x02, 17}, - {9, 0x02, 17}, - {23, 0x02, 17}, - {40, 0x03, 17}, - {2, 0x02, 18}, - {9, 0x02, 18}, - {23, 0x02, 18}, - {40, 0x03, 18}, - }, - /* 236 */ - { - {3, 0x02, 15}, - {6, 0x02, 15}, - {10, 0x02, 15}, - {15, 0x02, 15}, - {24, 0x02, 15}, - {31, 0x02, 15}, - {41, 0x02, 15}, - {56, 0x03, 15}, - {3, 0x02, 16}, - {6, 0x02, 16}, - {10, 0x02, 16}, - {15, 0x02, 16}, - {24, 0x02, 16}, - {31, 0x02, 16}, - {41, 0x02, 16}, - {56, 0x03, 16}, - }, - /* 237 */ - { - {3, 0x02, 17}, - {6, 0x02, 17}, - {10, 0x02, 17}, - {15, 0x02, 17}, - {24, 0x02, 17}, - {31, 0x02, 17}, - {41, 0x02, 17}, - {56, 0x03, 17}, - {3, 0x02, 18}, - {6, 0x02, 18}, - {10, 0x02, 18}, - {15, 0x02, 18}, - {24, 0x02, 18}, - {31, 0x02, 18}, - {41, 0x02, 18}, - {56, 0x03, 18}, - }, - /* 238 */ - { - {0, 0x03, 19}, - {0, 0x03, 20}, - {0, 0x03, 21}, - {0, 0x03, 23}, - {0, 0x03, 24}, - {0, 0x03, 25}, - {0, 0x03, 26}, - {0, 0x03, 27}, - {0, 0x03, 28}, - {0, 0x03, 29}, - {0, 0x03, 30}, - {0, 0x03, 31}, - {0, 0x03, 127}, - {0, 0x03, 220}, - {0, 0x03, 249}, - {253, 0x00, 0}, - }, - /* 239 */ - { - {1, 0x02, 19}, - {22, 0x03, 19}, - {1, 0x02, 20}, - {22, 0x03, 20}, - {1, 0x02, 21}, - {22, 0x03, 21}, - {1, 0x02, 23}, - {22, 0x03, 23}, - {1, 0x02, 24}, - {22, 0x03, 24}, - {1, 0x02, 25}, - {22, 0x03, 25}, - {1, 0x02, 26}, - {22, 0x03, 26}, - {1, 0x02, 27}, - {22, 0x03, 27}, - }, - /* 240 */ - { - {2, 0x02, 19}, - {9, 0x02, 19}, - {23, 0x02, 19}, - {40, 0x03, 19}, - {2, 0x02, 20}, - {9, 0x02, 20}, - {23, 0x02, 20}, - {40, 0x03, 20}, - {2, 0x02, 21}, - {9, 0x02, 21}, - {23, 0x02, 21}, - {40, 0x03, 21}, - {2, 0x02, 23}, - {9, 0x02, 23}, - {23, 0x02, 23}, - {40, 0x03, 23}, - }, - /* 241 */ - { - {3, 0x02, 19}, - {6, 0x02, 19}, - {10, 0x02, 19}, - {15, 0x02, 19}, - {24, 0x02, 19}, - {31, 0x02, 19}, - {41, 0x02, 19}, - {56, 0x03, 19}, - {3, 0x02, 20}, - {6, 0x02, 20}, - {10, 0x02, 20}, - {15, 0x02, 20}, - {24, 0x02, 20}, - {31, 0x02, 20}, - {41, 0x02, 20}, - {56, 0x03, 20}, - }, - /* 242 */ - { - {3, 0x02, 21}, - {6, 0x02, 21}, - {10, 0x02, 21}, - {15, 0x02, 21}, - {24, 0x02, 21}, - {31, 0x02, 21}, - {41, 0x02, 21}, - {56, 0x03, 21}, - {3, 0x02, 23}, - {6, 0x02, 23}, - {10, 0x02, 23}, - {15, 0x02, 23}, - {24, 0x02, 23}, - {31, 0x02, 23}, - {41, 0x02, 23}, - {56, 0x03, 23}, - }, - /* 243 */ - { - {2, 0x02, 24}, - {9, 0x02, 24}, - {23, 0x02, 24}, - {40, 0x03, 24}, - {2, 0x02, 25}, - {9, 0x02, 25}, - {23, 0x02, 25}, - {40, 0x03, 25}, - {2, 0x02, 26}, - {9, 0x02, 26}, - {23, 0x02, 26}, - {40, 0x03, 26}, - {2, 0x02, 27}, - {9, 0x02, 27}, - {23, 0x02, 27}, - {40, 0x03, 27}, - }, - /* 244 */ - { - {3, 0x02, 24}, - {6, 0x02, 24}, - {10, 0x02, 24}, - {15, 0x02, 24}, - {24, 0x02, 24}, - {31, 0x02, 24}, - {41, 0x02, 24}, - {56, 0x03, 24}, - {3, 0x02, 25}, - {6, 0x02, 25}, - {10, 0x02, 25}, - {15, 0x02, 25}, - {24, 0x02, 25}, - {31, 0x02, 25}, - {41, 0x02, 25}, - {56, 0x03, 25}, - }, - /* 245 */ - { - {3, 0x02, 26}, - {6, 0x02, 26}, - {10, 0x02, 26}, - {15, 0x02, 26}, - {24, 0x02, 26}, - {31, 0x02, 26}, - {41, 0x02, 26}, - {56, 0x03, 26}, - {3, 0x02, 27}, - {6, 0x02, 27}, - {10, 0x02, 27}, - {15, 0x02, 27}, - {24, 0x02, 27}, - {31, 0x02, 27}, - {41, 0x02, 27}, - {56, 0x03, 27}, - }, - /* 246 */ - { - {1, 0x02, 28}, - {22, 0x03, 28}, - {1, 0x02, 29}, - {22, 0x03, 29}, - {1, 0x02, 30}, - {22, 0x03, 30}, - {1, 0x02, 31}, - {22, 0x03, 31}, - {1, 0x02, 127}, - {22, 0x03, 127}, - {1, 0x02, 220}, - {22, 0x03, 220}, - {1, 0x02, 249}, - {22, 0x03, 249}, - {254, 0x00, 0}, - {255, 0x00, 0}, - }, - /* 247 */ - { - {2, 0x02, 28}, - {9, 0x02, 28}, - {23, 0x02, 28}, - {40, 0x03, 28}, - {2, 0x02, 29}, - {9, 0x02, 29}, - {23, 0x02, 29}, - {40, 0x03, 29}, - {2, 0x02, 30}, - {9, 0x02, 30}, - {23, 0x02, 30}, - {40, 0x03, 30}, - {2, 0x02, 31}, - {9, 0x02, 31}, - {23, 0x02, 31}, - {40, 0x03, 31}, - }, - /* 248 */ - { - {3, 0x02, 28}, - {6, 0x02, 28}, - {10, 0x02, 28}, - {15, 0x02, 28}, - {24, 0x02, 28}, - {31, 0x02, 28}, - {41, 0x02, 28}, - {56, 0x03, 28}, - {3, 0x02, 29}, - {6, 0x02, 29}, - {10, 0x02, 29}, - {15, 0x02, 29}, - {24, 0x02, 29}, - {31, 0x02, 29}, - {41, 0x02, 29}, - {56, 0x03, 29}, - }, - /* 249 */ - { - {3, 0x02, 30}, - {6, 0x02, 30}, - {10, 0x02, 30}, - {15, 0x02, 30}, - {24, 0x02, 30}, - {31, 0x02, 30}, - {41, 0x02, 30}, - {56, 0x03, 30}, - {3, 0x02, 31}, - {6, 0x02, 31}, - {10, 0x02, 31}, - {15, 0x02, 31}, - {24, 0x02, 31}, - {31, 0x02, 31}, - {41, 0x02, 31}, - {56, 0x03, 31}, - }, - /* 250 */ - { - {2, 0x02, 127}, - {9, 0x02, 127}, - {23, 0x02, 127}, - {40, 0x03, 127}, - {2, 0x02, 220}, - {9, 0x02, 220}, - {23, 0x02, 220}, - {40, 0x03, 220}, - {2, 0x02, 249}, - {9, 0x02, 249}, - {23, 0x02, 249}, - {40, 0x03, 249}, - {0, 0x03, 10}, - {0, 0x03, 13}, - {0, 0x03, 22}, - {0, 0x04, 0}, - }, - /* 251 */ - { - {3, 0x02, 127}, - {6, 0x02, 127}, - {10, 0x02, 127}, - {15, 0x02, 127}, - {24, 0x02, 127}, - {31, 0x02, 127}, - {41, 0x02, 127}, - {56, 0x03, 127}, - {3, 0x02, 220}, - {6, 0x02, 220}, - {10, 0x02, 220}, - {15, 0x02, 220}, - {24, 0x02, 220}, - {31, 0x02, 220}, - {41, 0x02, 220}, - {56, 0x03, 220}, - }, - /* 252 */ - { - {3, 0x02, 249}, - {6, 0x02, 249}, - {10, 0x02, 249}, - {15, 0x02, 249}, - {24, 0x02, 249}, - {31, 0x02, 249}, - {41, 0x02, 249}, - {56, 0x03, 249}, - {1, 0x02, 10}, - {22, 0x03, 10}, - {1, 0x02, 13}, - {22, 0x03, 13}, - {1, 0x02, 22}, - {22, 0x03, 22}, - {0, 0x04, 0}, - {0, 0x04, 0}, - }, - /* 253 */ - { - {2, 0x02, 10}, - {9, 0x02, 10}, - {23, 0x02, 10}, - {40, 0x03, 10}, - {2, 0x02, 13}, - {9, 0x02, 13}, - {23, 0x02, 13}, - {40, 0x03, 13}, - {2, 0x02, 22}, - {9, 0x02, 22}, - {23, 0x02, 22}, - {40, 0x03, 22}, - {0, 0x04, 0}, - {0, 0x04, 0}, - {0, 0x04, 0}, - {0, 0x04, 0}, - }, - /* 254 */ - { - {3, 0x02, 10}, - {6, 0x02, 10}, - {10, 0x02, 10}, - {15, 0x02, 10}, - {24, 0x02, 10}, - {31, 0x02, 10}, - {41, 0x02, 10}, - {56, 0x03, 10}, - {3, 0x02, 13}, - {6, 0x02, 13}, - {10, 0x02, 13}, - {15, 0x02, 13}, - {24, 0x02, 13}, - {31, 0x02, 13}, - {41, 0x02, 13}, - {56, 0x03, 13}, - }, - /* 255 */ - { - {3, 0x02, 22}, - {6, 0x02, 22}, - {10, 0x02, 22}, - {15, 0x02, 22}, - {24, 0x02, 22}, - {31, 0x02, 22}, - {41, 0x02, 22}, - {56, 0x03, 22}, - {0, 0x04, 0}, - {0, 0x04, 0}, - {0, 0x04, 0}, - {0, 0x04, 0}, - {0, 0x04, 0}, - {0, 0x04, 0}, - {0, 0x04, 0}, - {0, 0x04, 0}, - }, -}; diff --git a/lib/nghttp2_helper.c b/lib/nghttp2_helper.c deleted file mode 100644 index 15f329c..0000000 --- a/lib/nghttp2_helper.c +++ /dev/null @@ -1,463 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2012 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#include "nghttp2_helper.h" - -#include -#include - -#include "nghttp2_net.h" - -void nghttp2_put_uint16be(uint8_t *buf, uint16_t n) { - uint16_t x = htons(n); - memcpy(buf, &x, sizeof(uint16_t)); -} - -void nghttp2_put_uint32be(uint8_t *buf, uint32_t n) { - uint32_t x = htonl(n); - memcpy(buf, &x, sizeof(uint32_t)); -} - -uint16_t nghttp2_get_uint16(const uint8_t *data) { - uint16_t n; - memcpy(&n, data, sizeof(uint16_t)); - return ntohs(n); -} - -uint32_t nghttp2_get_uint32(const uint8_t *data) { - uint32_t n; - memcpy(&n, data, sizeof(uint32_t)); - return ntohl(n); -} - -void *nghttp2_memdup(const void *src, size_t n, nghttp2_mem *mem) { - void *dest; - - if (n == 0) { - return NULL; - } - - dest = nghttp2_mem_malloc(mem, n); - if (dest == NULL) { - return NULL; - } - memcpy(dest, src, n); - return dest; -} - -/* Generated by gendowncasetbl.py */ -static const int DOWNCASE_TBL[] = { - 0 /* NUL */, 1 /* SOH */, 2 /* STX */, 3 /* ETX */, - 4 /* EOT */, 5 /* ENQ */, 6 /* ACK */, 7 /* BEL */, - 8 /* BS */, 9 /* HT */, 10 /* LF */, 11 /* VT */, - 12 /* FF */, 13 /* CR */, 14 /* SO */, 15 /* SI */, - 16 /* DLE */, 17 /* DC1 */, 18 /* DC2 */, 19 /* DC3 */, - 20 /* DC4 */, 21 /* NAK */, 22 /* SYN */, 23 /* ETB */, - 24 /* CAN */, 25 /* EM */, 26 /* SUB */, 27 /* ESC */, - 28 /* FS */, 29 /* GS */, 30 /* RS */, 31 /* US */, - 32 /* SPC */, 33 /* ! */, 34 /* " */, 35 /* # */, - 36 /* $ */, 37 /* % */, 38 /* & */, 39 /* ' */, - 40 /* ( */, 41 /* ) */, 42 /* * */, 43 /* + */, - 44 /* , */, 45 /* - */, 46 /* . */, 47 /* / */, - 48 /* 0 */, 49 /* 1 */, 50 /* 2 */, 51 /* 3 */, - 52 /* 4 */, 53 /* 5 */, 54 /* 6 */, 55 /* 7 */, - 56 /* 8 */, 57 /* 9 */, 58 /* : */, 59 /* ; */, - 60 /* < */, 61 /* = */, 62 /* > */, 63 /* ? */, - 64 /* @ */, 97 /* A */, 98 /* B */, 99 /* C */, - 100 /* D */, 101 /* E */, 102 /* F */, 103 /* G */, - 104 /* H */, 105 /* I */, 106 /* J */, 107 /* K */, - 108 /* L */, 109 /* M */, 110 /* N */, 111 /* O */, - 112 /* P */, 113 /* Q */, 114 /* R */, 115 /* S */, - 116 /* T */, 117 /* U */, 118 /* V */, 119 /* W */, - 120 /* X */, 121 /* Y */, 122 /* Z */, 91 /* [ */, - 92 /* \ */, 93 /* ] */, 94 /* ^ */, 95 /* _ */, - 96 /* ` */, 97 /* a */, 98 /* b */, 99 /* c */, - 100 /* d */, 101 /* e */, 102 /* f */, 103 /* g */, - 104 /* h */, 105 /* i */, 106 /* j */, 107 /* k */, - 108 /* l */, 109 /* m */, 110 /* n */, 111 /* o */, - 112 /* p */, 113 /* q */, 114 /* r */, 115 /* s */, - 116 /* t */, 117 /* u */, 118 /* v */, 119 /* w */, - 120 /* x */, 121 /* y */, 122 /* z */, 123 /* { */, - 124 /* | */, 125 /* } */, 126 /* ~ */, 127 /* DEL */, - 128 /* 0x80 */, 129 /* 0x81 */, 130 /* 0x82 */, 131 /* 0x83 */, - 132 /* 0x84 */, 133 /* 0x85 */, 134 /* 0x86 */, 135 /* 0x87 */, - 136 /* 0x88 */, 137 /* 0x89 */, 138 /* 0x8a */, 139 /* 0x8b */, - 140 /* 0x8c */, 141 /* 0x8d */, 142 /* 0x8e */, 143 /* 0x8f */, - 144 /* 0x90 */, 145 /* 0x91 */, 146 /* 0x92 */, 147 /* 0x93 */, - 148 /* 0x94 */, 149 /* 0x95 */, 150 /* 0x96 */, 151 /* 0x97 */, - 152 /* 0x98 */, 153 /* 0x99 */, 154 /* 0x9a */, 155 /* 0x9b */, - 156 /* 0x9c */, 157 /* 0x9d */, 158 /* 0x9e */, 159 /* 0x9f */, - 160 /* 0xa0 */, 161 /* 0xa1 */, 162 /* 0xa2 */, 163 /* 0xa3 */, - 164 /* 0xa4 */, 165 /* 0xa5 */, 166 /* 0xa6 */, 167 /* 0xa7 */, - 168 /* 0xa8 */, 169 /* 0xa9 */, 170 /* 0xaa */, 171 /* 0xab */, - 172 /* 0xac */, 173 /* 0xad */, 174 /* 0xae */, 175 /* 0xaf */, - 176 /* 0xb0 */, 177 /* 0xb1 */, 178 /* 0xb2 */, 179 /* 0xb3 */, - 180 /* 0xb4 */, 181 /* 0xb5 */, 182 /* 0xb6 */, 183 /* 0xb7 */, - 184 /* 0xb8 */, 185 /* 0xb9 */, 186 /* 0xba */, 187 /* 0xbb */, - 188 /* 0xbc */, 189 /* 0xbd */, 190 /* 0xbe */, 191 /* 0xbf */, - 192 /* 0xc0 */, 193 /* 0xc1 */, 194 /* 0xc2 */, 195 /* 0xc3 */, - 196 /* 0xc4 */, 197 /* 0xc5 */, 198 /* 0xc6 */, 199 /* 0xc7 */, - 200 /* 0xc8 */, 201 /* 0xc9 */, 202 /* 0xca */, 203 /* 0xcb */, - 204 /* 0xcc */, 205 /* 0xcd */, 206 /* 0xce */, 207 /* 0xcf */, - 208 /* 0xd0 */, 209 /* 0xd1 */, 210 /* 0xd2 */, 211 /* 0xd3 */, - 212 /* 0xd4 */, 213 /* 0xd5 */, 214 /* 0xd6 */, 215 /* 0xd7 */, - 216 /* 0xd8 */, 217 /* 0xd9 */, 218 /* 0xda */, 219 /* 0xdb */, - 220 /* 0xdc */, 221 /* 0xdd */, 222 /* 0xde */, 223 /* 0xdf */, - 224 /* 0xe0 */, 225 /* 0xe1 */, 226 /* 0xe2 */, 227 /* 0xe3 */, - 228 /* 0xe4 */, 229 /* 0xe5 */, 230 /* 0xe6 */, 231 /* 0xe7 */, - 232 /* 0xe8 */, 233 /* 0xe9 */, 234 /* 0xea */, 235 /* 0xeb */, - 236 /* 0xec */, 237 /* 0xed */, 238 /* 0xee */, 239 /* 0xef */, - 240 /* 0xf0 */, 241 /* 0xf1 */, 242 /* 0xf2 */, 243 /* 0xf3 */, - 244 /* 0xf4 */, 245 /* 0xf5 */, 246 /* 0xf6 */, 247 /* 0xf7 */, - 248 /* 0xf8 */, 249 /* 0xf9 */, 250 /* 0xfa */, 251 /* 0xfb */, - 252 /* 0xfc */, 253 /* 0xfd */, 254 /* 0xfe */, 255 /* 0xff */, -}; - -void nghttp2_downcase(uint8_t *s, size_t len) { - size_t i; - for (i = 0; i < len; ++i) { - s[i] = DOWNCASE_TBL[s[i]]; - } -} - -/* - * local_window_size - * ^ * - * | * recv_window_size - * | * * ^ - * | * * | - * 0+++++++++ - * | * * \ - * | * * | This rage is hidden in flow control. But it must be - * v * * / kept in order to restore it when window size is enlarged. - * recv_reduction - * (+ for negative direction) - * - * recv_window_size could be negative if we decrease - * local_window_size more than recv_window_size: - * - * local_window_size - * ^ * - * | * - * | * - * 0++++++++ - * | * ^ recv_window_size (negative) - * | * | - * v * * - * recv_reduction - */ -int nghttp2_adjust_local_window_size(int32_t *local_window_size_ptr, - int32_t *recv_window_size_ptr, - int32_t *recv_reduction_ptr, - int32_t *delta_ptr) { - if (*delta_ptr > 0) { - int32_t recv_reduction_delta; - int32_t delta; - int32_t new_recv_window_size = - nghttp2_max(0, *recv_window_size_ptr) - *delta_ptr; - - if (new_recv_window_size >= 0) { - *recv_window_size_ptr = new_recv_window_size; - return 0; - } - - delta = -new_recv_window_size; - - /* The delta size is strictly more than received bytes. Increase - local_window_size by that difference |delta|. */ - if (*local_window_size_ptr > NGHTTP2_MAX_WINDOW_SIZE - delta) { - return NGHTTP2_ERR_FLOW_CONTROL; - } - *local_window_size_ptr += delta; - /* If there is recv_reduction due to earlier window_size - reduction, we have to adjust it too. */ - recv_reduction_delta = nghttp2_min(*recv_reduction_ptr, delta); - *recv_reduction_ptr -= recv_reduction_delta; - if (*recv_window_size_ptr < 0) { - *recv_window_size_ptr += recv_reduction_delta; - } else { - /* If *recv_window_size_ptr > 0, then those bytes are going to - be returned to the remote peer (by WINDOW_UPDATE with the - adjusted *delta_ptr), so it is effectively 0 now. We set to - *recv_reduction_delta, because caller does not take into - account it in *delta_ptr. */ - *recv_window_size_ptr = recv_reduction_delta; - } - /* recv_reduction_delta must be paied from *delta_ptr, since it - was added in window size reduction (see below). */ - *delta_ptr -= recv_reduction_delta; - - return 0; - } - - if (*local_window_size_ptr + *delta_ptr < 0 || - *recv_window_size_ptr < INT32_MIN - *delta_ptr || - *recv_reduction_ptr > INT32_MAX + *delta_ptr) { - return NGHTTP2_ERR_FLOW_CONTROL; - } - /* Decreasing local window size. Note that we achieve this without - noticing to the remote peer. To do this, we cut - recv_window_size by -delta. This means that we don't send - WINDOW_UPDATE for -delta bytes. */ - *local_window_size_ptr += *delta_ptr; - *recv_window_size_ptr += *delta_ptr; - *recv_reduction_ptr -= *delta_ptr; - *delta_ptr = 0; - - return 0; -} - -int nghttp2_should_send_window_update(int32_t local_window_size, - int32_t recv_window_size) { - return recv_window_size >= local_window_size / 2; -} - -const char *nghttp2_strerror(int error_code) { - switch (error_code) { - case 0: - return "Success"; - case NGHTTP2_ERR_INVALID_ARGUMENT: - return "Invalid argument"; - case NGHTTP2_ERR_BUFFER_ERROR: - return "Out of buffer space"; - case NGHTTP2_ERR_UNSUPPORTED_VERSION: - return "Unsupported SPDY version"; - case NGHTTP2_ERR_WOULDBLOCK: - return "Operation would block"; - case NGHTTP2_ERR_PROTO: - return "Protocol error"; - case NGHTTP2_ERR_INVALID_FRAME: - return "Invalid frame octets"; - case NGHTTP2_ERR_EOF: - return "EOF"; - case NGHTTP2_ERR_DEFERRED: - return "Data transfer deferred"; - case NGHTTP2_ERR_STREAM_ID_NOT_AVAILABLE: - return "No more Stream ID available"; - case NGHTTP2_ERR_STREAM_CLOSED: - return "Stream was already closed or invalid"; - case NGHTTP2_ERR_STREAM_CLOSING: - return "Stream is closing"; - case NGHTTP2_ERR_STREAM_SHUT_WR: - return "The transmission is not allowed for this stream"; - case NGHTTP2_ERR_INVALID_STREAM_ID: - return "Stream ID is invalid"; - case NGHTTP2_ERR_INVALID_STREAM_STATE: - return "Invalid stream state"; - case NGHTTP2_ERR_DEFERRED_DATA_EXIST: - return "Another DATA frame has already been deferred"; - case NGHTTP2_ERR_START_STREAM_NOT_ALLOWED: - return "request HEADERS is not allowed"; - case NGHTTP2_ERR_GOAWAY_ALREADY_SENT: - return "GOAWAY has already been sent"; - case NGHTTP2_ERR_INVALID_HEADER_BLOCK: - return "Invalid header block"; - case NGHTTP2_ERR_INVALID_STATE: - return "Invalid state"; - case NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE: - return "The user callback function failed due to the temporal error"; - case NGHTTP2_ERR_FRAME_SIZE_ERROR: - return "The length of the frame is invalid"; - case NGHTTP2_ERR_HEADER_COMP: - return "Header compression/decompression error"; - case NGHTTP2_ERR_FLOW_CONTROL: - return "Flow control error"; - case NGHTTP2_ERR_INSUFF_BUFSIZE: - return "Insufficient buffer size given to function"; - case NGHTTP2_ERR_PAUSE: - return "Callback was paused by the application"; - case NGHTTP2_ERR_TOO_MANY_INFLIGHT_SETTINGS: - return "Too many inflight SETTINGS"; - case NGHTTP2_ERR_PUSH_DISABLED: - return "Server push is disabled by peer"; - case NGHTTP2_ERR_DATA_EXIST: - return "DATA frame already exists"; - case NGHTTP2_ERR_SESSION_CLOSING: - return "The current session is closing"; - case NGHTTP2_ERR_HTTP_HEADER: - return "Invalid HTTP header field was received"; - case NGHTTP2_ERR_HTTP_MESSAGING: - return "Violation in HTTP messaging rule"; - case NGHTTP2_ERR_REFUSED_STREAM: - return "Stream was refused"; - case NGHTTP2_ERR_INTERNAL: - return "Internal error"; - case NGHTTP2_ERR_NOMEM: - return "Out of memory"; - case NGHTTP2_ERR_CALLBACK_FAILURE: - return "The user callback function failed"; - case NGHTTP2_ERR_BAD_CLIENT_MAGIC: - return "Received bad clinet magic byte string"; - default: - return "Unknown error code"; - } -} - -/* Generated by gennmchartbl.py */ -static int VALID_HD_NAME_CHARS[] = { - 0 /* NUL */, 0 /* SOH */, 0 /* STX */, 0 /* ETX */, 0 /* EOT */, - 0 /* ENQ */, 0 /* ACK */, 0 /* BEL */, 0 /* BS */, 0 /* HT */, - 0 /* LF */, 0 /* VT */, 0 /* FF */, 0 /* CR */, 0 /* SO */, - 0 /* SI */, 0 /* DLE */, 0 /* DC1 */, 0 /* DC2 */, 0 /* DC3 */, - 0 /* DC4 */, 0 /* NAK */, 0 /* SYN */, 0 /* ETB */, 0 /* CAN */, - 0 /* EM */, 0 /* SUB */, 0 /* ESC */, 0 /* FS */, 0 /* GS */, - 0 /* RS */, 0 /* US */, 0 /* SPC */, 1 /* ! */, 0 /* " */, - 1 /* # */, 1 /* $ */, 1 /* % */, 1 /* & */, 1 /* ' */, - 0 /* ( */, 0 /* ) */, 1 /* * */, 1 /* + */, 0 /* , */, - 1 /* - */, 1 /* . */, 0 /* / */, 1 /* 0 */, 1 /* 1 */, - 1 /* 2 */, 1 /* 3 */, 1 /* 4 */, 1 /* 5 */, 1 /* 6 */, - 1 /* 7 */, 1 /* 8 */, 1 /* 9 */, 0 /* : */, 0 /* ; */, - 0 /* < */, 0 /* = */, 0 /* > */, 0 /* ? */, 0 /* @ */, - 0 /* A */, 0 /* B */, 0 /* C */, 0 /* D */, 0 /* E */, - 0 /* F */, 0 /* G */, 0 /* H */, 0 /* I */, 0 /* J */, - 0 /* K */, 0 /* L */, 0 /* M */, 0 /* N */, 0 /* O */, - 0 /* P */, 0 /* Q */, 0 /* R */, 0 /* S */, 0 /* T */, - 0 /* U */, 0 /* V */, 0 /* W */, 0 /* X */, 0 /* Y */, - 0 /* Z */, 0 /* [ */, 0 /* \ */, 0 /* ] */, 1 /* ^ */, - 1 /* _ */, 1 /* ` */, 1 /* a */, 1 /* b */, 1 /* c */, - 1 /* d */, 1 /* e */, 1 /* f */, 1 /* g */, 1 /* h */, - 1 /* i */, 1 /* j */, 1 /* k */, 1 /* l */, 1 /* m */, - 1 /* n */, 1 /* o */, 1 /* p */, 1 /* q */, 1 /* r */, - 1 /* s */, 1 /* t */, 1 /* u */, 1 /* v */, 1 /* w */, - 1 /* x */, 1 /* y */, 1 /* z */, 0 /* { */, 1 /* | */, - 0 /* } */, 1 /* ~ */, 0 /* DEL */, 0 /* 0x80 */, 0 /* 0x81 */, - 0 /* 0x82 */, 0 /* 0x83 */, 0 /* 0x84 */, 0 /* 0x85 */, 0 /* 0x86 */, - 0 /* 0x87 */, 0 /* 0x88 */, 0 /* 0x89 */, 0 /* 0x8a */, 0 /* 0x8b */, - 0 /* 0x8c */, 0 /* 0x8d */, 0 /* 0x8e */, 0 /* 0x8f */, 0 /* 0x90 */, - 0 /* 0x91 */, 0 /* 0x92 */, 0 /* 0x93 */, 0 /* 0x94 */, 0 /* 0x95 */, - 0 /* 0x96 */, 0 /* 0x97 */, 0 /* 0x98 */, 0 /* 0x99 */, 0 /* 0x9a */, - 0 /* 0x9b */, 0 /* 0x9c */, 0 /* 0x9d */, 0 /* 0x9e */, 0 /* 0x9f */, - 0 /* 0xa0 */, 0 /* 0xa1 */, 0 /* 0xa2 */, 0 /* 0xa3 */, 0 /* 0xa4 */, - 0 /* 0xa5 */, 0 /* 0xa6 */, 0 /* 0xa7 */, 0 /* 0xa8 */, 0 /* 0xa9 */, - 0 /* 0xaa */, 0 /* 0xab */, 0 /* 0xac */, 0 /* 0xad */, 0 /* 0xae */, - 0 /* 0xaf */, 0 /* 0xb0 */, 0 /* 0xb1 */, 0 /* 0xb2 */, 0 /* 0xb3 */, - 0 /* 0xb4 */, 0 /* 0xb5 */, 0 /* 0xb6 */, 0 /* 0xb7 */, 0 /* 0xb8 */, - 0 /* 0xb9 */, 0 /* 0xba */, 0 /* 0xbb */, 0 /* 0xbc */, 0 /* 0xbd */, - 0 /* 0xbe */, 0 /* 0xbf */, 0 /* 0xc0 */, 0 /* 0xc1 */, 0 /* 0xc2 */, - 0 /* 0xc3 */, 0 /* 0xc4 */, 0 /* 0xc5 */, 0 /* 0xc6 */, 0 /* 0xc7 */, - 0 /* 0xc8 */, 0 /* 0xc9 */, 0 /* 0xca */, 0 /* 0xcb */, 0 /* 0xcc */, - 0 /* 0xcd */, 0 /* 0xce */, 0 /* 0xcf */, 0 /* 0xd0 */, 0 /* 0xd1 */, - 0 /* 0xd2 */, 0 /* 0xd3 */, 0 /* 0xd4 */, 0 /* 0xd5 */, 0 /* 0xd6 */, - 0 /* 0xd7 */, 0 /* 0xd8 */, 0 /* 0xd9 */, 0 /* 0xda */, 0 /* 0xdb */, - 0 /* 0xdc */, 0 /* 0xdd */, 0 /* 0xde */, 0 /* 0xdf */, 0 /* 0xe0 */, - 0 /* 0xe1 */, 0 /* 0xe2 */, 0 /* 0xe3 */, 0 /* 0xe4 */, 0 /* 0xe5 */, - 0 /* 0xe6 */, 0 /* 0xe7 */, 0 /* 0xe8 */, 0 /* 0xe9 */, 0 /* 0xea */, - 0 /* 0xeb */, 0 /* 0xec */, 0 /* 0xed */, 0 /* 0xee */, 0 /* 0xef */, - 0 /* 0xf0 */, 0 /* 0xf1 */, 0 /* 0xf2 */, 0 /* 0xf3 */, 0 /* 0xf4 */, - 0 /* 0xf5 */, 0 /* 0xf6 */, 0 /* 0xf7 */, 0 /* 0xf8 */, 0 /* 0xf9 */, - 0 /* 0xfa */, 0 /* 0xfb */, 0 /* 0xfc */, 0 /* 0xfd */, 0 /* 0xfe */, - 0 /* 0xff */ -}; - -int nghttp2_check_header_name(const uint8_t *name, size_t len) { - const uint8_t *last; - if (len == 0) { - return 0; - } - if (*name == ':') { - if (len == 1) { - return 0; - } - ++name; - --len; - } - for (last = name + len; name != last; ++name) { - if (!VALID_HD_NAME_CHARS[*name]) { - return 0; - } - } - return 1; -} - -/* Generated by genvchartbl.py */ -static int VALID_HD_VALUE_CHARS[] = { - 0 /* NUL */, 0 /* SOH */, 0 /* STX */, 0 /* ETX */, 0 /* EOT */, - 0 /* ENQ */, 0 /* ACK */, 0 /* BEL */, 0 /* BS */, 1 /* HT */, - 0 /* LF */, 0 /* VT */, 0 /* FF */, 0 /* CR */, 0 /* SO */, - 0 /* SI */, 0 /* DLE */, 0 /* DC1 */, 0 /* DC2 */, 0 /* DC3 */, - 0 /* DC4 */, 0 /* NAK */, 0 /* SYN */, 0 /* ETB */, 0 /* CAN */, - 0 /* EM */, 0 /* SUB */, 0 /* ESC */, 0 /* FS */, 0 /* GS */, - 0 /* RS */, 0 /* US */, 1 /* SPC */, 1 /* ! */, 1 /* " */, - 1 /* # */, 1 /* $ */, 1 /* % */, 1 /* & */, 1 /* ' */, - 1 /* ( */, 1 /* ) */, 1 /* * */, 1 /* + */, 1 /* , */, - 1 /* - */, 1 /* . */, 1 /* / */, 1 /* 0 */, 1 /* 1 */, - 1 /* 2 */, 1 /* 3 */, 1 /* 4 */, 1 /* 5 */, 1 /* 6 */, - 1 /* 7 */, 1 /* 8 */, 1 /* 9 */, 1 /* : */, 1 /* ; */, - 1 /* < */, 1 /* = */, 1 /* > */, 1 /* ? */, 1 /* @ */, - 1 /* A */, 1 /* B */, 1 /* C */, 1 /* D */, 1 /* E */, - 1 /* F */, 1 /* G */, 1 /* H */, 1 /* I */, 1 /* J */, - 1 /* K */, 1 /* L */, 1 /* M */, 1 /* N */, 1 /* O */, - 1 /* P */, 1 /* Q */, 1 /* R */, 1 /* S */, 1 /* T */, - 1 /* U */, 1 /* V */, 1 /* W */, 1 /* X */, 1 /* Y */, - 1 /* Z */, 1 /* [ */, 1 /* \ */, 1 /* ] */, 1 /* ^ */, - 1 /* _ */, 1 /* ` */, 1 /* a */, 1 /* b */, 1 /* c */, - 1 /* d */, 1 /* e */, 1 /* f */, 1 /* g */, 1 /* h */, - 1 /* i */, 1 /* j */, 1 /* k */, 1 /* l */, 1 /* m */, - 1 /* n */, 1 /* o */, 1 /* p */, 1 /* q */, 1 /* r */, - 1 /* s */, 1 /* t */, 1 /* u */, 1 /* v */, 1 /* w */, - 1 /* x */, 1 /* y */, 1 /* z */, 1 /* { */, 1 /* | */, - 1 /* } */, 1 /* ~ */, 0 /* DEL */, 1 /* 0x80 */, 1 /* 0x81 */, - 1 /* 0x82 */, 1 /* 0x83 */, 1 /* 0x84 */, 1 /* 0x85 */, 1 /* 0x86 */, - 1 /* 0x87 */, 1 /* 0x88 */, 1 /* 0x89 */, 1 /* 0x8a */, 1 /* 0x8b */, - 1 /* 0x8c */, 1 /* 0x8d */, 1 /* 0x8e */, 1 /* 0x8f */, 1 /* 0x90 */, - 1 /* 0x91 */, 1 /* 0x92 */, 1 /* 0x93 */, 1 /* 0x94 */, 1 /* 0x95 */, - 1 /* 0x96 */, 1 /* 0x97 */, 1 /* 0x98 */, 1 /* 0x99 */, 1 /* 0x9a */, - 1 /* 0x9b */, 1 /* 0x9c */, 1 /* 0x9d */, 1 /* 0x9e */, 1 /* 0x9f */, - 1 /* 0xa0 */, 1 /* 0xa1 */, 1 /* 0xa2 */, 1 /* 0xa3 */, 1 /* 0xa4 */, - 1 /* 0xa5 */, 1 /* 0xa6 */, 1 /* 0xa7 */, 1 /* 0xa8 */, 1 /* 0xa9 */, - 1 /* 0xaa */, 1 /* 0xab */, 1 /* 0xac */, 1 /* 0xad */, 1 /* 0xae */, - 1 /* 0xaf */, 1 /* 0xb0 */, 1 /* 0xb1 */, 1 /* 0xb2 */, 1 /* 0xb3 */, - 1 /* 0xb4 */, 1 /* 0xb5 */, 1 /* 0xb6 */, 1 /* 0xb7 */, 1 /* 0xb8 */, - 1 /* 0xb9 */, 1 /* 0xba */, 1 /* 0xbb */, 1 /* 0xbc */, 1 /* 0xbd */, - 1 /* 0xbe */, 1 /* 0xbf */, 1 /* 0xc0 */, 1 /* 0xc1 */, 1 /* 0xc2 */, - 1 /* 0xc3 */, 1 /* 0xc4 */, 1 /* 0xc5 */, 1 /* 0xc6 */, 1 /* 0xc7 */, - 1 /* 0xc8 */, 1 /* 0xc9 */, 1 /* 0xca */, 1 /* 0xcb */, 1 /* 0xcc */, - 1 /* 0xcd */, 1 /* 0xce */, 1 /* 0xcf */, 1 /* 0xd0 */, 1 /* 0xd1 */, - 1 /* 0xd2 */, 1 /* 0xd3 */, 1 /* 0xd4 */, 1 /* 0xd5 */, 1 /* 0xd6 */, - 1 /* 0xd7 */, 1 /* 0xd8 */, 1 /* 0xd9 */, 1 /* 0xda */, 1 /* 0xdb */, - 1 /* 0xdc */, 1 /* 0xdd */, 1 /* 0xde */, 1 /* 0xdf */, 1 /* 0xe0 */, - 1 /* 0xe1 */, 1 /* 0xe2 */, 1 /* 0xe3 */, 1 /* 0xe4 */, 1 /* 0xe5 */, - 1 /* 0xe6 */, 1 /* 0xe7 */, 1 /* 0xe8 */, 1 /* 0xe9 */, 1 /* 0xea */, - 1 /* 0xeb */, 1 /* 0xec */, 1 /* 0xed */, 1 /* 0xee */, 1 /* 0xef */, - 1 /* 0xf0 */, 1 /* 0xf1 */, 1 /* 0xf2 */, 1 /* 0xf3 */, 1 /* 0xf4 */, - 1 /* 0xf5 */, 1 /* 0xf6 */, 1 /* 0xf7 */, 1 /* 0xf8 */, 1 /* 0xf9 */, - 1 /* 0xfa */, 1 /* 0xfb */, 1 /* 0xfc */, 1 /* 0xfd */, 1 /* 0xfe */, - 1 /* 0xff */ -}; - -int nghttp2_check_header_value(const uint8_t *value, size_t len) { - const uint8_t *last; - for (last = value + len; value != last; ++value) { - if (!VALID_HD_VALUE_CHARS[*value]) { - return 0; - } - } - return 1; -} - -uint8_t *nghttp2_cpymem(uint8_t *dest, const void *src, size_t len) { - memcpy(dest, src, len); - - return dest + len; -} diff --git a/lib/nghttp2_helper.h b/lib/nghttp2_helper.h deleted file mode 100644 index 0a7f8c8..0000000 --- a/lib/nghttp2_helper.h +++ /dev/null @@ -1,114 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2012 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#ifndef NGHTTP2_HELPER_H -#define NGHTTP2_HELPER_H - -#ifdef HAVE_CONFIG_H -#include -#endif /* HAVE_CONFIG_H */ - -#include - -#include -#include "nghttp2_mem.h" - -#define nghttp2_min(A, B) ((A) < (B) ? (A) : (B)) -#define nghttp2_max(A, B) ((A) > (B) ? (A) : (B)) - -#define lstreq(A, B, N) ((sizeof((A)) - 1) == (N) && memcmp((A), (B), (N)) == 0) - -/* - * Copies 2 byte unsigned integer |n| in host byte order to |buf| in - * network byte order. - */ -void nghttp2_put_uint16be(uint8_t *buf, uint16_t n); - -/* - * Copies 4 byte unsigned integer |n| in host byte order to |buf| in - * network byte order. - */ -void nghttp2_put_uint32be(uint8_t *buf, uint32_t n); - -/* - * Retrieves 2 byte unsigned integer stored in |data| in network byte - * order and returns it in host byte order. - */ -uint16_t nghttp2_get_uint16(const uint8_t *data); - -/* - * Retrieves 4 byte unsigned integer stored in |data| in network byte - * order and returns it in host byte order. - */ -uint32_t nghttp2_get_uint32(const uint8_t *data); - -/* - * Allocates |n| bytes of memory and copy the memory region pointed by - * |src| with the length |n| bytes into it. Returns the allocated memory. - * - * This function returns pointer to allocated memory, or one of the - * following negative error codes: - * - * NGHTTP2_ERR_NOMEM - * Out of memory. - */ -void *nghttp2_memdup(const void *src, size_t n, nghttp2_mem *mem); - -void nghttp2_downcase(uint8_t *s, size_t len); - -/* - * Adjusts |*local_window_size_ptr|, |*recv_window_size_ptr|, - * |*recv_reduction_ptr| with |*delta_ptr| which is the - * WINDOW_UPDATE's window_size_increment sent from local side. If - * |delta| is strictly larger than |*recv_window_size_ptr|, - * |*local_window_size_ptr| is increased by delta - - * *recv_window_size_ptr. If |delta| is negative, - * |*local_window_size_ptr| is decreased by delta. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP2_ERR_FLOW_CONTROL - * local_window_size overflow or gets negative. - */ -int nghttp2_adjust_local_window_size(int32_t *local_window_size_ptr, - int32_t *recv_window_size_ptr, - int32_t *recv_reduction_ptr, - int32_t *delta_ptr); - -/* - * Returns non-zero if the function decided that WINDOW_UPDATE should - * be sent. - */ -int nghttp2_should_send_window_update(int32_t local_window_size, - int32_t recv_window_size); - -/* - * Copies the buffer |src| of length |len| to the destination pointed - * by the |dest|, assuming that the |dest| is at lest |len| bytes long - * . Returns dest + len. - */ -uint8_t *nghttp2_cpymem(uint8_t *dest, const void *src, size_t len); - -#endif /* NGHTTP2_HELPER_H */ diff --git a/lib/nghttp2_http.c b/lib/nghttp2_http.c deleted file mode 100644 index 695307e..0000000 --- a/lib/nghttp2_http.c +++ /dev/null @@ -1,459 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2015 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#include "nghttp2_http.h" - -#include -#include -#include - -#include "nghttp2_hd.h" -#include "nghttp2_helper.h" - -static char downcase(char c) { - return 'A' <= c && c <= 'Z' ? (c - 'A' + 'a') : c; -} - -static int memieq(const void *a, const void *b, size_t n) { - size_t i; - const uint8_t *aa = a, *bb = b; - - for (i = 0; i < n; ++i) { - if (downcase(aa[i]) != downcase(bb[i])) { - return 0; - } - } - return 1; -} - -#define lstrieq(A, B, N) ((sizeof((A)) - 1) == (N) && memieq((A), (B), (N))) - -static int64_t parse_uint(const uint8_t *s, size_t len) { - int64_t n = 0; - size_t i; - if (len == 0) { - return -1; - } - for (i = 0; i < len; ++i) { - if ('0' <= s[i] && s[i] <= '9') { - if (n > INT64_MAX / 10) { - return -1; - } - n *= 10; - if (n > INT64_MAX - (s[i] - '0')) { - return -1; - } - n += s[i] - '0'; - continue; - } - return -1; - } - return n; -} - -static int lws(const uint8_t *s, size_t n) { - size_t i; - for (i = 0; i < n; ++i) { - if (s[i] != ' ' && s[i] != '\t') { - return 0; - } - } - return 1; -} - -static int check_pseudo_header(nghttp2_stream *stream, const nghttp2_nv *nv, - int flag) { - if (stream->http_flags & flag) { - return 0; - } - if (lws(nv->value, nv->valuelen)) { - return 0; - } - stream->http_flags |= flag; - return 1; -} - -static int expect_response_body(nghttp2_stream *stream) { - return (stream->http_flags & NGHTTP2_HTTP_FLAG_METH_HEAD) == 0 && - stream->status_code / 100 != 1 && stream->status_code != 304 && - stream->status_code != 204; -} - -/* For "http" or "https" URIs, OPTIONS request may have "*" in :path - header field to represent system-wide OPTIONS request. Otherwise, - :path header field value must start with "/". This function must - be called after ":method" header field was received. This function - returns nonzero if path is valid.*/ -static int check_path(nghttp2_stream *stream) { - return (stream->http_flags & NGHTTP2_HTTP_FLAG_SCHEME_HTTP) == 0 || - ((stream->http_flags & NGHTTP2_HTTP_FLAG_PATH_REGULAR) || - ((stream->http_flags & NGHTTP2_HTTP_FLAG_METH_OPTIONS) && - (stream->http_flags & NGHTTP2_HTTP_FLAG_PATH_ASTERISK))); -} - -static int http_request_on_header(nghttp2_stream *stream, nghttp2_nv *nv, - int token, int trailer) { - if (nv->name[0] == ':') { - if (trailer || - (stream->http_flags & NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED)) { - return NGHTTP2_ERR_HTTP_HEADER; - } - } - - switch (token) { - case NGHTTP2_TOKEN__AUTHORITY: - if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__AUTHORITY)) { - return NGHTTP2_ERR_HTTP_HEADER; - } - break; - case NGHTTP2_TOKEN__METHOD: - if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__METHOD)) { - return NGHTTP2_ERR_HTTP_HEADER; - } - switch (nv->valuelen) { - case 4: - if (lstreq("HEAD", nv->value, nv->valuelen)) { - stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_HEAD; - } - break; - case 7: - switch (nv->value[6]) { - case 'T': - if (lstreq("CONNECT", nv->value, nv->valuelen)) { - if (stream->stream_id % 2 == 0) { - /* we won't allow CONNECT for push */ - return NGHTTP2_ERR_HTTP_HEADER; - } - stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_CONNECT; - if (stream->http_flags & - (NGHTTP2_HTTP_FLAG__PATH | NGHTTP2_HTTP_FLAG__SCHEME)) { - return NGHTTP2_ERR_HTTP_HEADER; - } - } - break; - case 'S': - if (lstreq("OPTIONS", nv->value, nv->valuelen)) { - stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_OPTIONS; - } - break; - } - break; - } - break; - case NGHTTP2_TOKEN__PATH: - if (stream->http_flags & NGHTTP2_HTTP_FLAG_METH_CONNECT) { - return NGHTTP2_ERR_HTTP_HEADER; - } - if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__PATH)) { - return NGHTTP2_ERR_HTTP_HEADER; - } - if (nv->value[0] == '/') { - stream->http_flags |= NGHTTP2_HTTP_FLAG_PATH_REGULAR; - } else if (nv->valuelen == 1 && nv->value[0] == '*') { - stream->http_flags |= NGHTTP2_HTTP_FLAG_PATH_ASTERISK; - } - break; - case NGHTTP2_TOKEN__SCHEME: - if (stream->http_flags & NGHTTP2_HTTP_FLAG_METH_CONNECT) { - return NGHTTP2_ERR_HTTP_HEADER; - } - if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__SCHEME)) { - return NGHTTP2_ERR_HTTP_HEADER; - } - if ((nv->valuelen == 4 && memieq("http", nv->value, 4)) || - (nv->valuelen == 5 && memieq("https", nv->value, 5))) { - stream->http_flags |= NGHTTP2_HTTP_FLAG_SCHEME_HTTP; - } - break; - case NGHTTP2_TOKEN_HOST: - if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG_HOST)) { - return NGHTTP2_ERR_HTTP_HEADER; - } - break; - case NGHTTP2_TOKEN_CONTENT_LENGTH: { - if (stream->content_length != -1) { - return NGHTTP2_ERR_HTTP_HEADER; - } - stream->content_length = parse_uint(nv->value, nv->valuelen); - if (stream->content_length == -1) { - return NGHTTP2_ERR_HTTP_HEADER; - } - break; - } - /* disallowed header fields */ - case NGHTTP2_TOKEN_CONNECTION: - case NGHTTP2_TOKEN_KEEP_ALIVE: - case NGHTTP2_TOKEN_PROXY_CONNECTION: - case NGHTTP2_TOKEN_TRANSFER_ENCODING: - case NGHTTP2_TOKEN_UPGRADE: - return NGHTTP2_ERR_HTTP_HEADER; - case NGHTTP2_TOKEN_TE: - if (!lstrieq("trailers", nv->value, nv->valuelen)) { - return NGHTTP2_ERR_HTTP_HEADER; - } - break; - default: - if (nv->name[0] == ':') { - return NGHTTP2_ERR_HTTP_HEADER; - } - } - - if (nv->name[0] != ':') { - stream->http_flags |= NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED; - } - - return 0; -} - -static int http_response_on_header(nghttp2_stream *stream, nghttp2_nv *nv, - int token, int trailer) { - if (nv->name[0] == ':') { - if (trailer || - (stream->http_flags & NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED)) { - return NGHTTP2_ERR_HTTP_HEADER; - } - } - - switch (token) { - case NGHTTP2_TOKEN__STATUS: { - if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__STATUS)) { - return NGHTTP2_ERR_HTTP_HEADER; - } - if (nv->valuelen != 3) { - return NGHTTP2_ERR_HTTP_HEADER; - } - stream->status_code = parse_uint(nv->value, nv->valuelen); - if (stream->status_code == -1) { - return NGHTTP2_ERR_HTTP_HEADER; - } - break; - } - case NGHTTP2_TOKEN_CONTENT_LENGTH: { - if (stream->content_length != -1) { - return NGHTTP2_ERR_HTTP_HEADER; - } - stream->content_length = parse_uint(nv->value, nv->valuelen); - if (stream->content_length == -1) { - return NGHTTP2_ERR_HTTP_HEADER; - } - break; - } - /* disallowed header fields */ - case NGHTTP2_TOKEN_CONNECTION: - case NGHTTP2_TOKEN_KEEP_ALIVE: - case NGHTTP2_TOKEN_PROXY_CONNECTION: - case NGHTTP2_TOKEN_TRANSFER_ENCODING: - case NGHTTP2_TOKEN_UPGRADE: - return NGHTTP2_ERR_HTTP_HEADER; - case NGHTTP2_TOKEN_TE: - if (!lstrieq("trailers", nv->value, nv->valuelen)) { - return NGHTTP2_ERR_HTTP_HEADER; - } - break; - default: - if (nv->name[0] == ':') { - return NGHTTP2_ERR_HTTP_HEADER; - } - } - - if (nv->name[0] != ':') { - stream->http_flags |= NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED; - } - - return 0; -} - -int nghttp2_http_on_header(nghttp2_session *session, nghttp2_stream *stream, - nghttp2_frame *frame, nghttp2_nv *nv, int token, - int trailer) { - /* We are strict for pseudo header field. One bad character should - lead to fail. OTOH, we should be a bit forgiving for regular - headers, since existing public internet has so much illegal - headers floating around and if we kill the stream because of - this, we may disrupt many web sites and/or libraries. So we - become conservative here, and just ignore those illegal regular - headers. */ - if (!nghttp2_check_header_name(nv->name, nv->namelen)) { - size_t i; - if (nv->namelen > 0 && nv->name[0] == ':') { - return NGHTTP2_ERR_HTTP_HEADER; - } - /* header field name must be lower-cased without exception */ - for (i = 0; i < nv->namelen; ++i) { - char c = nv->name[i]; - if ('A' <= c && c <= 'Z') { - return NGHTTP2_ERR_HTTP_HEADER; - } - } - /* When ignoring regular headers, we set this flag so that we - still enforce header field ordering rule for pseudo header - fields. */ - stream->http_flags |= NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED; - return NGHTTP2_ERR_IGN_HTTP_HEADER; - } - - if (!nghttp2_check_header_value(nv->value, nv->valuelen)) { - assert(nv->namelen > 0); - if (nv->name[0] == ':') { - return NGHTTP2_ERR_HTTP_HEADER; - } - /* When ignoring regular headers, we set this flag so that we - still enforce header field ordering rule for pseudo header - fields. */ - stream->http_flags |= NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED; - return NGHTTP2_ERR_IGN_HTTP_HEADER; - } - - if (session->server || frame->hd.type == NGHTTP2_PUSH_PROMISE) { - return http_request_on_header(stream, nv, token, trailer); - } - - return http_response_on_header(stream, nv, token, trailer); -} - -int nghttp2_http_on_request_headers(nghttp2_stream *stream, - nghttp2_frame *frame) { - if (stream->http_flags & NGHTTP2_HTTP_FLAG_METH_CONNECT) { - if ((stream->http_flags & NGHTTP2_HTTP_FLAG__AUTHORITY) == 0) { - return -1; - } - stream->content_length = -1; - } else { - if ((stream->http_flags & NGHTTP2_HTTP_FLAG_REQ_HEADERS) != - NGHTTP2_HTTP_FLAG_REQ_HEADERS || - (stream->http_flags & - (NGHTTP2_HTTP_FLAG__AUTHORITY | NGHTTP2_HTTP_FLAG_HOST)) == 0) { - return -1; - } - if (!check_path(stream)) { - return -1; - } - } - - if (frame->hd.type == NGHTTP2_PUSH_PROMISE) { - /* we are going to reuse data fields for upcoming response. Clear - them now, except for method flags. */ - stream->http_flags &= NGHTTP2_HTTP_FLAG_METH_ALL; - stream->content_length = -1; - } - - return 0; -} - -int nghttp2_http_on_response_headers(nghttp2_stream *stream) { - if ((stream->http_flags & NGHTTP2_HTTP_FLAG__STATUS) == 0) { - return -1; - } - - if (stream->status_code / 100 == 1) { - /* non-final response */ - stream->http_flags = (stream->http_flags & NGHTTP2_HTTP_FLAG_METH_ALL) | - NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE; - stream->content_length = -1; - stream->status_code = -1; - return 0; - } - - stream->http_flags &= ~NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE; - - if (!expect_response_body(stream)) { - stream->content_length = 0; - } else if (stream->http_flags & NGHTTP2_HTTP_FLAG_METH_CONNECT) { - stream->content_length = -1; - } - - return 0; -} - -int nghttp2_http_on_trailer_headers(nghttp2_stream *stream _U_, - nghttp2_frame *frame) { - if ((frame->hd.flags & NGHTTP2_FLAG_END_STREAM) == 0) { - return -1; - } - - return 0; -} - -int nghttp2_http_on_remote_end_stream(nghttp2_stream *stream) { - if (stream->http_flags & NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE) { - return -1; - } - - if (stream->content_length != -1 && - stream->content_length != stream->recv_content_length) { - return -1; - } - - return 0; -} - -int nghttp2_http_on_data_chunk(nghttp2_stream *stream, size_t n) { - stream->recv_content_length += n; - - if ((stream->http_flags & NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE) || - (stream->content_length != -1 && - stream->recv_content_length > stream->content_length)) { - return -1; - } - - return 0; -} - -void nghttp2_http_record_request_method(nghttp2_stream *stream, - nghttp2_frame *frame) { - const nghttp2_nv *nva; - size_t nvlen; - size_t i; - - switch (frame->hd.type) { - case NGHTTP2_HEADERS: - nva = frame->headers.nva; - nvlen = frame->headers.nvlen; - break; - case NGHTTP2_PUSH_PROMISE: - nva = frame->push_promise.nva; - nvlen = frame->push_promise.nvlen; - break; - default: - return; - } - - /* TODO we should do this strictly. */ - for (i = 0; i < nvlen; ++i) { - const nghttp2_nv *nv = &nva[i]; - if (!(nv->namelen == 7 && nv->name[6] == 'd' && - memcmp(":metho", nv->name, nv->namelen - 1) == 0)) { - continue; - } - if (lstreq("CONNECT", nv->value, nv->valuelen)) { - stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_CONNECT; - return; - } - if (lstreq("HEAD", nv->value, nv->valuelen)) { - stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_HEAD; - return; - } - return; - } -} diff --git a/lib/nghttp2_http.h b/lib/nghttp2_http.h deleted file mode 100644 index f782058..0000000 --- a/lib/nghttp2_http.h +++ /dev/null @@ -1,98 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2015 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#ifndef NGHTTP2_HTTP_H -#define NGHTTP2_HTTP_H - -#ifdef HAVE_CONFIG_H -#include -#endif /* HAVE_CONFIG_H */ - -#include -#include "nghttp2_session.h" -#include "nghttp2_stream.h" - -/* - * This function is called when HTTP header field |nv| in |frame| is - * received for |stream|. This function will validate |nv| against - * the current state of stream. The |token| is nghttp2_token value - * for nv->name, or -1 if we don't have enum value for the name. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP2_ERR_HTTP_HEADER - * Invalid HTTP header field was received. - * NGHTTP2_ERR_IGN_HTTP_HEADER - * Invalid HTTP header field was received but it can be treated as - * if it was not received because of compatibility reasons. - */ -int nghttp2_http_on_header(nghttp2_session *session, nghttp2_stream *stream, - nghttp2_frame *frame, nghttp2_nv *nv, int token, - int trailer); - -/* - * This function is called when request header is received. This - * function performs validation and returns 0 if it succeeds, or -1. - */ -int nghttp2_http_on_request_headers(nghttp2_stream *stream, - nghttp2_frame *frame); - -/* - * This function is called when response header is received. This - * function performs validation and returns 0 if it succeeds, or -1. - */ -int nghttp2_http_on_response_headers(nghttp2_stream *stream); - -/* - * This function is called trailer header (for both request and - * response) is received. This function performs validation and - * returns 0 if it succeeds, or -1. - */ -int nghttp2_http_on_trailer_headers(nghttp2_stream *stream, - nghttp2_frame *frame); - -/* - * This function is called when END_STREAM flag is seen in incoming - * frame. This function performs validation and returns 0 if it - * succeeds, or -1. - */ -int nghttp2_http_on_remote_end_stream(nghttp2_stream *stream); - -/* - * This function is called when chunk of data is received. This - * function performs validation and returns 0 if it succeeds, or -1. - */ -int nghttp2_http_on_data_chunk(nghttp2_stream *stream, size_t n); - -/* - * This function inspects header field in |frame| and records its - * method in stream->http_flags. If frame->hd.type is neither - * NGHTTP2_HEADERS nor NGHTTP2_PUSH_PROMISE, this function does - * nothing. - */ -void nghttp2_http_record_request_method(nghttp2_stream *stream, - nghttp2_frame *frame); - -#endif /* NGHTTP2_HTTP_H */ diff --git a/lib/nghttp2_int.h b/lib/nghttp2_int.h deleted file mode 100644 index c26c8e9..0000000 --- a/lib/nghttp2_int.h +++ /dev/null @@ -1,58 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2012 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#ifndef NGHTTP2_INT_H -#define NGHTTP2_INT_H - -#ifdef HAVE_CONFIG_H -#include -#endif /* HAVE_CONFIG_H */ - -/* Macros, types and constants for internal use */ - -#ifdef DEBUGBUILD -#define DEBUGF(x) x -#else -#define DEBUGF(x) \ - do { \ - } while (0) -#endif - -/* "less" function, return nonzero if |lhs| is less than |rhs|. */ -typedef int (*nghttp2_less)(const void *lhs, const void *rhs); - -/* Internal error code. They must be in the range [-499, -100], - inclusive. */ -typedef enum { - NGHTTP2_ERR_CREDENTIAL_PENDING = -101, - NGHTTP2_ERR_IGN_HEADER_BLOCK = -103, - NGHTTP2_ERR_IGN_PAYLOAD = -104, - /* - * Invalid HTTP header field was received but it can be treated as - * if it was not received because of compatibility reasons. - */ - NGHTTP2_ERR_IGN_HTTP_HEADER = -105 -} nghttp2_internal_error; - -#endif /* NGHTTP2_INT_H */ diff --git a/lib/nghttp2_map.c b/lib/nghttp2_map.c deleted file mode 100644 index 5a38ee2..0000000 --- a/lib/nghttp2_map.c +++ /dev/null @@ -1,190 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2012 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#include "nghttp2_map.h" - -#include - -#define INITIAL_TABLE_LENGTH 256 - -int nghttp2_map_init(nghttp2_map *map, nghttp2_mem *mem) { - map->mem = mem; - map->tablelen = INITIAL_TABLE_LENGTH; - map->table = - nghttp2_mem_calloc(mem, map->tablelen, sizeof(nghttp2_map_entry *)); - if (map->table == NULL) { - return NGHTTP2_ERR_NOMEM; - } - - map->size = 0; - - return 0; -} - -void nghttp2_map_free(nghttp2_map *map) { - nghttp2_mem_free(map->mem, map->table); -} - -void nghttp2_map_each_free(nghttp2_map *map, - int (*func)(nghttp2_map_entry *entry, void *ptr), - void *ptr) { - size_t i; - for (i = 0; i < map->tablelen; ++i) { - nghttp2_map_entry *entry; - for (entry = map->table[i]; entry;) { - nghttp2_map_entry *next = entry->next; - func(entry, ptr); - entry = next; - } - map->table[i] = NULL; - } -} - -int nghttp2_map_each(nghttp2_map *map, - int (*func)(nghttp2_map_entry *entry, void *ptr), - void *ptr) { - int rv; - size_t i; - for (i = 0; i < map->tablelen; ++i) { - nghttp2_map_entry *entry; - for (entry = map->table[i]; entry; entry = entry->next) { - rv = func(entry, ptr); - if (rv != 0) { - return rv; - } - } - } - return 0; -} - -void nghttp2_map_entry_init(nghttp2_map_entry *entry, key_type key) { - entry->key = key; - entry->next = NULL; -} - -/* Same hash function in android HashMap source code. */ -/* The |mod| must be power of 2 */ -static int32_t hash(int32_t h, size_t mod) { - h ^= (h >> 20) ^ (h >> 12); - h ^= (h >> 7) ^ (h >> 4); - return h & (mod - 1); -} - -static int insert(nghttp2_map_entry **table, size_t tablelen, - nghttp2_map_entry *entry) { - int32_t h = hash(entry->key, tablelen); - if (table[h] == NULL) { - table[h] = entry; - } else { - nghttp2_map_entry *p; - /* We won't allow duplicated key, so check it out. */ - for (p = table[h]; p; p = p->next) { - if (p->key == entry->key) { - return NGHTTP2_ERR_INVALID_ARGUMENT; - } - } - entry->next = table[h]; - table[h] = entry; - } - return 0; -} - -/* new_tablelen must be power of 2 */ -static int resize(nghttp2_map *map, size_t new_tablelen) { - size_t i; - nghttp2_map_entry **new_table; - - new_table = - nghttp2_mem_calloc(map->mem, new_tablelen, sizeof(nghttp2_map_entry *)); - if (new_table == NULL) { - return NGHTTP2_ERR_NOMEM; - } - - for (i = 0; i < map->tablelen; ++i) { - nghttp2_map_entry *entry; - for (entry = map->table[i]; entry;) { - nghttp2_map_entry *next = entry->next; - entry->next = NULL; - /* This function must succeed */ - insert(new_table, new_tablelen, entry); - entry = next; - } - } - nghttp2_mem_free(map->mem, map->table); - map->tablelen = new_tablelen; - map->table = new_table; - - return 0; -} - -int nghttp2_map_insert(nghttp2_map *map, nghttp2_map_entry *new_entry) { - int rv; - /* Load factor is 0.75 */ - if ((map->size + 1) * 4 > map->tablelen * 3) { - rv = resize(map, map->tablelen * 2); - if (rv != 0) { - return rv; - } - } - rv = insert(map->table, map->tablelen, new_entry); - if (rv != 0) { - return rv; - } - ++map->size; - return 0; -} - -nghttp2_map_entry *nghttp2_map_find(nghttp2_map *map, key_type key) { - int32_t h; - nghttp2_map_entry *entry; - h = hash(key, map->tablelen); - for (entry = map->table[h]; entry; entry = entry->next) { - if (entry->key == key) { - return entry; - } - } - return NULL; -} - -int nghttp2_map_remove(nghttp2_map *map, key_type key) { - int32_t h; - nghttp2_map_entry *entry, *prev; - h = hash(key, map->tablelen); - prev = NULL; - for (entry = map->table[h]; entry; entry = entry->next) { - if (entry->key == key) { - if (prev == NULL) { - map->table[h] = entry->next; - } else { - prev->next = entry->next; - } - --map->size; - return 0; - } - prev = entry; - } - return NGHTTP2_ERR_INVALID_ARGUMENT; -} - -size_t nghttp2_map_size(nghttp2_map *map) { return map->size; } diff --git a/lib/nghttp2_map.h b/lib/nghttp2_map.h deleted file mode 100644 index 83f425d..0000000 --- a/lib/nghttp2_map.h +++ /dev/null @@ -1,144 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2012 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#ifndef NGHTTP2_MAP_H -#define NGHTTP2_MAP_H - -#ifdef HAVE_CONFIG_H -#include -#endif /* HAVE_CONFIG_H */ - -#include -#include "nghttp2_int.h" -#include "nghttp2_mem.h" - -/* Implementation of unordered map */ - -typedef uint32_t key_type; - -typedef struct nghttp2_map_entry { - struct nghttp2_map_entry *next; - key_type key; -#if SIZEOF_INT_P == 4 - /* we requires 8 bytes aligment */ - int64_t pad; -#endif -} nghttp2_map_entry; - -typedef struct { - nghttp2_map_entry **table; - nghttp2_mem *mem; - size_t tablelen; - size_t size; -} nghttp2_map; - -/* - * Initializes the map |map|. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP2_ERR_NOMEM - * Out of memory - */ -int nghttp2_map_init(nghttp2_map *map, nghttp2_mem *mem); - -/* - * Deallocates any resources allocated for |map|. The stored entries - * are not freed by this function. Use nghttp2_map_each_free() to free - * each entries. - */ -void nghttp2_map_free(nghttp2_map *map); - -/* - * Deallocates each entries using |func| function and any resources - * allocated for |map|. The |func| function is responsible for freeing - * given the |entry| object. The |ptr| will be passed to the |func| as - * send argument. The return value of the |func| will be ignored. - */ -void nghttp2_map_each_free(nghttp2_map *map, - int (*func)(nghttp2_map_entry *entry, void *ptr), - void *ptr); - -/* - * Initializes the |entry| with the |key|. All entries to be inserted - * to the map must be initialized with this function. - */ -void nghttp2_map_entry_init(nghttp2_map_entry *entry, key_type key); - -/* - * Inserts the new |entry| with the key |entry->key| to the map |map|. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP2_ERR_INVALID_ARGUMENT - * The item associated by |key| already exists. - * NGHTTP2_ERR_NOMEM - * Out of memory - */ -int nghttp2_map_insert(nghttp2_map *map, nghttp2_map_entry *entry); - -/* - * Returns the entry associated by the key |key|. If there is no such - * entry, this function returns NULL. - */ -nghttp2_map_entry *nghttp2_map_find(nghttp2_map *map, key_type key); - -/* - * Removes the entry associated by the key |key| from the |map|. The - * removed entry is not freed by this function. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP2_ERR_INVALID_ARGUMENT - * The entry associated by |key| does not exist. - */ -int nghttp2_map_remove(nghttp2_map *map, key_type key); - -/* - * Returns the number of items stored in the map |map|. - */ -size_t nghttp2_map_size(nghttp2_map *map); - -/* - * Applies the function |func| to each entry in the |map| with the - * optional user supplied pointer |ptr|. - * - * If the |func| returns 0, this function calls the |func| with the - * next entry. If the |func| returns nonzero, it will not call the - * |func| for further entries and return the return value of the - * |func| immediately. Thus, this function returns 0 if all the - * invocations of the |func| return 0, or nonzero value which the last - * invocation of |func| returns. - * - * Don't use this function to free each entry. Use - * nghttp2_map_each_free() instead. - */ -int nghttp2_map_each(nghttp2_map *map, - int (*func)(nghttp2_map_entry *entry, void *ptr), - void *ptr); - -#endif /* NGHTTP2_MAP_H */ diff --git a/lib/nghttp2_mem.c b/lib/nghttp2_mem.c deleted file mode 100644 index e7d5aae..0000000 --- a/lib/nghttp2_mem.c +++ /dev/null @@ -1,61 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2014 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#include "nghttp2_mem.h" - -static void *default_malloc(size_t size, void *mem_user_data _U_) { - return malloc(size); -} - -static void default_free(void *ptr, void *mem_user_data _U_) { free(ptr); } - -static void *default_calloc(size_t nmemb, size_t size, - void *mem_user_data _U_) { - return calloc(nmemb, size); -} - -static void *default_realloc(void *ptr, size_t size, void *mem_user_data _U_) { - return realloc(ptr, size); -} - -static nghttp2_mem mem_default = {NULL, default_malloc, default_free, - default_calloc, default_realloc}; - -nghttp2_mem *nghttp2_mem_default(void) { return &mem_default; } - -void *nghttp2_mem_malloc(nghttp2_mem *mem, size_t size) { - return mem->malloc(size, mem->mem_user_data); -} - -void nghttp2_mem_free(nghttp2_mem *mem, void *ptr) { - mem->free(ptr, mem->mem_user_data); -} - -void *nghttp2_mem_calloc(nghttp2_mem *mem, size_t nmemb, size_t size) { - return mem->calloc(nmemb, size, mem->mem_user_data); -} - -void *nghttp2_mem_realloc(nghttp2_mem *mem, void *ptr, size_t size) { - return mem->realloc(ptr, size, mem->mem_user_data); -} diff --git a/lib/nghttp2_mem.h b/lib/nghttp2_mem.h deleted file mode 100644 index d1fded4..0000000 --- a/lib/nghttp2_mem.h +++ /dev/null @@ -1,44 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2014 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#ifndef NGHTTP2_MEM_H -#define NGHTTP2_MEM_H - -#ifdef HAVE_CONFIG_H -#include -#endif /* HAVE_CONFIG_H */ - -#include - -/* The default, system standard memory allocator */ -nghttp2_mem *nghttp2_mem_default(void); - -/* Convenient wrapper functions to call allocator function in - |mem|. */ -void *nghttp2_mem_malloc(nghttp2_mem *mem, size_t size); -void nghttp2_mem_free(nghttp2_mem *mem, void *ptr); -void *nghttp2_mem_calloc(nghttp2_mem *mem, size_t nmemb, size_t size); -void *nghttp2_mem_realloc(nghttp2_mem *mem, void *ptr, size_t size); - -#endif /* NGHTTP2_MEM_H */ diff --git a/lib/nghttp2_net.h b/lib/nghttp2_net.h deleted file mode 100644 index 587f418..0000000 --- a/lib/nghttp2_net.h +++ /dev/null @@ -1,91 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2012 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#ifndef NGHTTP2_NET_H -#define NGHTTP2_NET_H - -#ifdef HAVE_CONFIG_H -#include -#endif /* HAVE_CONFIG_H */ - -#ifdef HAVE_ARPA_INET_H -#include -#endif /* HAVE_ARPA_INET_H */ - -#ifdef HAVE_NETINET_IN_H -#include -#endif /* HAVE_NETINET_IN_H */ - -#include - -#if defined(WIN32) -/* Windows requires ws2_32 library for ntonl family functions. We - define inline functions for those function so that we don't have - dependeny on that lib. */ - -#ifdef _MSC_VER -#define STIN static __inline -#else -#define STIN static inline -#endif - -STIN uint32_t htonl(uint32_t hostlong) { - uint32_t res; - unsigned char *p = (unsigned char *)&res; - *p++ = hostlong >> 24; - *p++ = (hostlong >> 16) & 0xffu; - *p++ = (hostlong >> 8) & 0xffu; - *p = hostlong & 0xffu; - return res; -} - -STIN uint16_t htons(uint16_t hostshort) { - uint16_t res; - unsigned char *p = (unsigned char *)&res; - *p++ = hostshort >> 8; - *p = hostshort & 0xffu; - return res; -} - -STIN uint32_t ntohl(uint32_t netlong) { - uint32_t res; - unsigned char *p = (unsigned char *)&netlong; - res = *p++ << 24; - res += *p++ << 16; - res += *p++ << 8; - res += *p; - return res; -} - -STIN uint16_t ntohs(uint16_t netshort) { - uint16_t res; - unsigned char *p = (unsigned char *)&netshort; - res = *p++ << 8; - res += *p; - return res; -} - -#endif /* WIN32 */ - -#endif /* NGHTTP2_NET_H */ diff --git a/lib/nghttp2_npn.c b/lib/nghttp2_npn.c deleted file mode 100644 index a8bdb23..0000000 --- a/lib/nghttp2_npn.c +++ /dev/null @@ -1,57 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2012 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#include "nghttp2_npn.h" - -#include - -static int select_next_protocol(unsigned char **out, unsigned char *outlen, - const unsigned char *in, unsigned int inlen, - const char *key, unsigned int keylen) { - unsigned int i; - for (i = 0; i + keylen <= inlen; i += in [i] + 1) { - if (memcmp(&in[i], key, keylen) == 0) { - *out = (unsigned char *)&in[i + 1]; - *outlen = in[i]; - return 0; - } - } - return -1; -} - -#define NGHTTP2_HTTP_1_1_ALPN "\x8http/1.1" -#define NGHTTP2_HTTP_1_1_ALPN_LEN (sizeof(NGHTTP2_HTTP_1_1_ALPN) - 1) - -int nghttp2_select_next_protocol(unsigned char **out, unsigned char *outlen, - const unsigned char *in, unsigned int inlen) { - if (select_next_protocol(out, outlen, in, inlen, NGHTTP2_PROTO_ALPN, - NGHTTP2_PROTO_ALPN_LEN) == 0) { - return 1; - } - if (select_next_protocol(out, outlen, in, inlen, NGHTTP2_HTTP_1_1_ALPN, - NGHTTP2_HTTP_1_1_ALPN_LEN) == 0) { - return 0; - } - return -1; -} diff --git a/lib/nghttp2_npn.h b/lib/nghttp2_npn.h deleted file mode 100644 index c4bdedb..0000000 --- a/lib/nghttp2_npn.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2012 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#ifndef NGHTTP2_NPN_H -#define NGHTTP2_NPN_H - -#ifdef HAVE_CONFIG -#include -#endif /* HAVE_CONFIG */ - -#include - -#endif /* NGHTTP2_NPN_H */ diff --git a/lib/nghttp2_option.c b/lib/nghttp2_option.c deleted file mode 100644 index a45b5e0..0000000 --- a/lib/nghttp2_option.c +++ /dev/null @@ -1,58 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2014 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#include "nghttp2_option.h" - -int nghttp2_option_new(nghttp2_option **option_ptr) { - *option_ptr = calloc(1, sizeof(nghttp2_option)); - - if (*option_ptr == NULL) { - return NGHTTP2_ERR_NOMEM; - } - - return 0; -} - -void nghttp2_option_del(nghttp2_option *option) { free(option); } - -void nghttp2_option_set_no_auto_window_update(nghttp2_option *option, int val) { - option->opt_set_mask |= NGHTTP2_OPT_NO_AUTO_WINDOW_UPDATE; - option->no_auto_window_update = val; -} - -void nghttp2_option_set_peer_max_concurrent_streams(nghttp2_option *option, - uint32_t val) { - option->opt_set_mask |= NGHTTP2_OPT_PEER_MAX_CONCURRENT_STREAMS; - option->peer_max_concurrent_streams = val; -} - -void nghttp2_option_set_no_recv_client_magic(nghttp2_option *option, int val) { - option->opt_set_mask |= NGHTTP2_OPT_NO_RECV_CLIENT_MAGIC; - option->no_recv_client_magic = val; -} - -void nghttp2_option_set_no_http_messaging(nghttp2_option *option, int val) { - option->opt_set_mask |= NGHTTP2_OPT_NO_HTTP_MESSAGING; - option->no_http_messaging = val; -} diff --git a/lib/nghttp2_option.h b/lib/nghttp2_option.h deleted file mode 100644 index 739bd85..0000000 --- a/lib/nghttp2_option.h +++ /dev/null @@ -1,91 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2014 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#ifndef NGHTTP2_OPTION_H -#define NGHTTP2_OPTION_H - -#ifdef HAVE_CONFIG_H -#include -#endif /* HAVE_CONFIG_H */ - -#include - -/** - * Configuration options - */ -typedef enum { - /** - * This option prevents the library from sending WINDOW_UPDATE for a - * connection automatically. If this option is set to nonzero, the - * library won't send WINDOW_UPDATE for DATA until application calls - * nghttp2_session_consume() to indicate the amount of consumed - * DATA. By default, this option is set to zero. - */ - NGHTTP2_OPT_NO_AUTO_WINDOW_UPDATE = 1, - /** - * This option sets the SETTINGS_MAX_CONCURRENT_STREAMS value of - * remote endpoint as if it is received in SETTINGS frame. Without - * specifying this option, before the local endpoint receives - * SETTINGS_MAX_CONCURRENT_STREAMS in SETTINGS frame from remote - * endpoint, SETTINGS_MAX_CONCURRENT_STREAMS is unlimited. This may - * cause problem if local endpoint submits lots of requests - * initially and sending them at once to the remote peer may lead to - * the rejection of some requests. Specifying this option to the - * sensible value, say 100, may avoid this kind of issue. This value - * will be overwritten if the local endpoint receives - * SETTINGS_MAX_CONCURRENT_STREAMS from the remote endpoint. - */ - NGHTTP2_OPT_PEER_MAX_CONCURRENT_STREAMS = 1 << 1, - NGHTTP2_OPT_NO_RECV_CLIENT_MAGIC = 1 << 2, - NGHTTP2_OPT_NO_HTTP_MESSAGING = 1 << 3 -} nghttp2_option_flag; - -/** - * Struct to store option values for nghttp2_session. - */ -struct nghttp2_option { - /** - * Bitwise OR of nghttp2_option_flag to determine that which fields - * are specified. - */ - uint32_t opt_set_mask; - /** - * NGHTTP2_OPT_PEER_MAX_CONCURRENT_STREAMS - */ - uint32_t peer_max_concurrent_streams; - /** - * NGHTTP2_OPT_NO_AUTO_WINDOW_UPDATE - */ - uint8_t no_auto_window_update; - /** - * NGHTTP2_OPT_NO_RECV_CLIENT_MAGIC - */ - uint8_t no_recv_client_magic; - /** - * NGHTTP2_OPT_NO_HTTP_MESSAGING - */ - uint8_t no_http_messaging; -}; - -#endif /* NGHTTP2_OPTION_H */ diff --git a/lib/nghttp2_outbound_item.c b/lib/nghttp2_outbound_item.c deleted file mode 100644 index c4ecab9..0000000 --- a/lib/nghttp2_outbound_item.c +++ /dev/null @@ -1,105 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2012 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#include "nghttp2_outbound_item.h" - -#include -#include - -void nghttp2_outbound_item_init(nghttp2_outbound_item *item) { - item->cycle = 0; - item->qnext = NULL; - item->queued = 0; - - memset(&item->aux_data, 0, sizeof(nghttp2_aux_data)); -} - -void nghttp2_outbound_item_free(nghttp2_outbound_item *item, nghttp2_mem *mem) { - nghttp2_frame *frame; - - if (item == NULL) { - return; - } - - frame = &item->frame; - - switch (frame->hd.type) { - case NGHTTP2_DATA: - nghttp2_frame_data_free(&frame->data); - break; - case NGHTTP2_HEADERS: - nghttp2_frame_headers_free(&frame->headers, mem); - break; - case NGHTTP2_PRIORITY: - nghttp2_frame_priority_free(&frame->priority); - break; - case NGHTTP2_RST_STREAM: - nghttp2_frame_rst_stream_free(&frame->rst_stream); - break; - case NGHTTP2_SETTINGS: - nghttp2_frame_settings_free(&frame->settings, mem); - break; - case NGHTTP2_PUSH_PROMISE: - nghttp2_frame_push_promise_free(&frame->push_promise, mem); - break; - case NGHTTP2_PING: - nghttp2_frame_ping_free(&frame->ping); - break; - case NGHTTP2_GOAWAY: - nghttp2_frame_goaway_free(&frame->goaway, mem); - break; - case NGHTTP2_WINDOW_UPDATE: - nghttp2_frame_window_update_free(&frame->window_update); - break; - } -} - -void nghttp2_outbound_queue_init(nghttp2_outbound_queue *q) { - q->head = q->tail = NULL; - q->n = 0; -} - -void nghttp2_outbound_queue_push(nghttp2_outbound_queue *q, - nghttp2_outbound_item *item) { - if (q->tail) { - q->tail = q->tail->qnext = item; - } else { - q->head = q->tail = item; - } - ++q->n; -} - -void nghttp2_outbound_queue_pop(nghttp2_outbound_queue *q) { - nghttp2_outbound_item *item; - if (!q->head) { - return; - } - item = q->head; - q->head = q->head->qnext; - item->qnext = NULL; - if (!q->head) { - q->tail = NULL; - } - --q->n; -} diff --git a/lib/nghttp2_outbound_item.h b/lib/nghttp2_outbound_item.h deleted file mode 100644 index 0be5cb7..0000000 --- a/lib/nghttp2_outbound_item.h +++ /dev/null @@ -1,157 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2012 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#ifndef NGHTTP2_OUTBOUND_ITEM_H -#define NGHTTP2_OUTBOUND_ITEM_H - -#ifdef HAVE_CONFIG_H -#include -#endif /* HAVE_CONFIG_H */ - -#include -#include "nghttp2_frame.h" -#include "nghttp2_mem.h" - -/* struct used for HEADERS and PUSH_PROMISE frame */ -typedef struct { - nghttp2_data_provider data_prd; - void *stream_user_data; - /* error code when request HEADERS is canceled by RST_STREAM while - it is in queue. */ - uint32_t error_code; - /* nonzero if request HEADERS is canceled. The error code is stored - in |error_code|. */ - uint8_t canceled; - /* nonzero if this item should be attached to stream object to make - it under priority control */ - uint8_t attach_stream; -} nghttp2_headers_aux_data; - -/* struct used for DATA frame */ -typedef struct { - /** - * The data to be sent for this DATA frame. - */ - nghttp2_data_provider data_prd; - /** - * The flags of DATA frame. We use separate flags here and - * nghttp2_data frame. The latter contains flags actually sent to - * peer. This |flags| may contain NGHTTP2_FLAG_END_STREAM and only - * when |eof| becomes nonzero, flags in nghttp2_data has - * NGHTTP2_FLAG_END_STREAM set. - */ - uint8_t flags; - /** - * The flag to indicate whether EOF was reached or not. Initially - * |eof| is 0. It becomes 1 after all data were read. - */ - uint8_t eof; - /** - * The flag to indicate that NGHTTP2_DATA_FLAG_NO_COPY is used. - */ - uint8_t no_copy; -} nghttp2_data_aux_data; - -typedef enum { - NGHTTP2_GOAWAY_AUX_NONE = 0x0, - /* indicates that session should be terminated after the - transmission of this frame. */ - NGHTTP2_GOAWAY_AUX_TERM_ON_SEND = 0x1, - /* indicates that this GOAWAY is just a notification for graceful - shutdown. No nghttp2_session.goaway_flags should be updated on - the reaction to this frame. */ - NGHTTP2_GOAWAY_AUX_SHUTDOWN_NOTICE = 0x2 -} nghttp2_goaway_aux_flag; - -/* struct used for GOAWAY frame */ -typedef struct { - /* bitwise-OR of one or more of nghttp2_goaway_aux_flag. */ - uint8_t flags; -} nghttp2_goaway_aux_data; - -/* Additional data which cannot be stored in nghttp2_frame struct */ -typedef union { - nghttp2_data_aux_data data; - nghttp2_headers_aux_data headers; - nghttp2_goaway_aux_data goaway; -} nghttp2_aux_data; - -struct nghttp2_outbound_item; -typedef struct nghttp2_outbound_item nghttp2_outbound_item; - -struct nghttp2_outbound_item { - nghttp2_frame frame; - nghttp2_aux_data aux_data; - /* The priority used in priority comparion. Smaller is served - ealier. For PING, SETTINGS and non-DATA frames (excluding - response HEADERS frame) have dedicated cycle value defined above. - For DATA frame, cycle is computed by taking into account of - effective weight and frame payload length previously sent, so - that the amount of transmission is distributed across streams - proportional to effective weight (inside a tree). */ - uint64_t cycle; - nghttp2_outbound_item *qnext; - /* nonzero if this object is queued. */ - uint8_t queued; -}; - -/* - * Initializes |item|. No memory allocation is done in this function. - * Don't call nghttp2_outbound_item_free() until frame member is - * initialized. - */ -void nghttp2_outbound_item_init(nghttp2_outbound_item *item); - -/* - * Deallocates resource for |item|. If |item| is NULL, this function - * does nothing. - */ -void nghttp2_outbound_item_free(nghttp2_outbound_item *item, nghttp2_mem *mem); - -/* - * queue for nghttp2_outbound_item. - */ -typedef struct { - nghttp2_outbound_item *head, *tail; - /* number of items in this queue. */ - size_t n; -} nghttp2_outbound_queue; - -void nghttp2_outbound_queue_init(nghttp2_outbound_queue *q); - -/* Pushes |item| into |q| */ -void nghttp2_outbound_queue_push(nghttp2_outbound_queue *q, - nghttp2_outbound_item *item); - -/* Pops |item| at the top from |q|. If |q| is empty, nothing - happens. */ -void nghttp2_outbound_queue_pop(nghttp2_outbound_queue *q); - -/* Returns the top item. */ -#define nghttp2_outbound_queue_top(Q) ((Q)->head) - -/* Returns the size of the queue */ -#define nghttp2_outbound_queue_size(Q) ((Q)->n) - -#endif /* NGHTTP2_OUTBOUND_ITEM_H */ diff --git a/lib/nghttp2_pq.c b/lib/nghttp2_pq.c deleted file mode 100644 index f04c189..0000000 --- a/lib/nghttp2_pq.c +++ /dev/null @@ -1,146 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2012 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#include "nghttp2_pq.h" - -int nghttp2_pq_init(nghttp2_pq *pq, nghttp2_less less, nghttp2_mem *mem) { - pq->mem = mem; - pq->capacity = 128; - pq->q = nghttp2_mem_malloc(mem, pq->capacity * sizeof(void *)); - if (pq->q == NULL) { - return NGHTTP2_ERR_NOMEM; - } - pq->length = 0; - pq->less = less; - return 0; -} - -void nghttp2_pq_free(nghttp2_pq *pq) { - nghttp2_mem_free(pq->mem, pq->q); - pq->q = NULL; -} - -static void swap(nghttp2_pq *pq, size_t i, size_t j) { - void *t = pq->q[i]; - pq->q[i] = pq->q[j]; - pq->q[j] = t; -} - -static void bubble_up(nghttp2_pq *pq, size_t index) { - if (index == 0) { - return; - } else { - size_t parent = (index - 1) / 2; - if (pq->less(pq->q[index], pq->q[parent])) { - swap(pq, parent, index); - bubble_up(pq, parent); - } - } -} - -int nghttp2_pq_push(nghttp2_pq *pq, void *item) { - if (pq->capacity <= pq->length) { - void *nq; - nq = nghttp2_mem_realloc(pq->mem, pq->q, - (pq->capacity * 2) * sizeof(void *)); - if (nq == NULL) { - return NGHTTP2_ERR_NOMEM; - } - pq->capacity *= 2; - pq->q = nq; - } - pq->q[pq->length] = item; - ++pq->length; - bubble_up(pq, pq->length - 1); - return 0; -} - -void *nghttp2_pq_top(nghttp2_pq *pq) { - if (pq->length == 0) { - return NULL; - } else { - return pq->q[0]; - } -} - -static void bubble_down(nghttp2_pq *pq, size_t index) { - size_t lchild = index * 2 + 1; - size_t minindex = index; - size_t i, j; - for (i = 0; i < 2; ++i) { - j = lchild + i; - if (j >= pq->length) { - break; - } - if (pq->less(pq->q[j], pq->q[minindex])) { - minindex = j; - } - } - if (minindex != index) { - swap(pq, index, minindex); - bubble_down(pq, minindex); - } -} - -void nghttp2_pq_pop(nghttp2_pq *pq) { - if (pq->length > 0) { - pq->q[0] = pq->q[pq->length - 1]; - --pq->length; - bubble_down(pq, 0); - } -} - -int nghttp2_pq_empty(nghttp2_pq *pq) { return pq->length == 0; } - -size_t nghttp2_pq_size(nghttp2_pq *pq) { return pq->length; } - -void nghttp2_pq_update(nghttp2_pq *pq, nghttp2_pq_item_cb fun, void *arg) { - size_t i; - int rv = 0; - if (pq->length == 0) { - return; - } - for (i = 0; i < pq->length; ++i) { - rv |= (*fun)(pq->q[i], arg); - } - if (rv) { - for (i = pq->length; i > 0; --i) { - bubble_down(pq, i - 1); - } - } -} - -int nghttp2_pq_each(nghttp2_pq *pq, nghttp2_pq_item_cb fun, void *arg) { - size_t i; - - if (pq->length == 0) { - return 0; - } - for (i = 0; i < pq->length; ++i) { - if ((*fun)(pq->q[i], arg)) { - return 1; - } - } - return 0; -} diff --git a/lib/nghttp2_pq.h b/lib/nghttp2_pq.h deleted file mode 100644 index 1775d03..0000000 --- a/lib/nghttp2_pq.h +++ /dev/null @@ -1,121 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2012 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#ifndef NGHTTP2_PQ_H -#define NGHTTP2_PQ_H - -#ifdef HAVE_CONFIG_H -#include -#endif /* HAVE_CONFIG_H */ - -#include -#include "nghttp2_int.h" -#include "nghttp2_mem.h" - -/* Implementation of priority queue */ - -typedef struct { - /* The pointer to the pointer to the item stored */ - void **q; - /* Memory allocator */ - nghttp2_mem *mem; - /* The number of items sotred */ - size_t length; - /* The maximum number of items this pq can store. This is - automatically extended when length is reached to this value. */ - size_t capacity; - /* The less function between items */ - nghttp2_less less; -} nghttp2_pq; - -/* - * Initializes priority queue |pq| with compare function |cmp|. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP2_ERR_NOMEM - * Out of memory. - */ -int nghttp2_pq_init(nghttp2_pq *pq, nghttp2_less less, nghttp2_mem *mem); - -/* - * Deallocates any resources allocated for |pq|. The stored items are - * not freed by this function. - */ -void nghttp2_pq_free(nghttp2_pq *pq); - -/* - * Adds |item| to the priority queue |pq|. - * - * This function returns 0 if it succeds, or one of the following - * negative error codes: - * - * NGHTTP2_ERR_NOMEM - * Out of memory. - */ -int nghttp2_pq_push(nghttp2_pq *pq, void *item); - -/* - * Returns item at the top of the queue |pq|. If the queue is empty, - * this function returns NULL. - */ -void *nghttp2_pq_top(nghttp2_pq *pq); - -/* - * Pops item at the top of the queue |pq|. The popped item is not - * freed by this function. - */ -void nghttp2_pq_pop(nghttp2_pq *pq); - -/* - * Returns nonzero if the queue |pq| is empty. - */ -int nghttp2_pq_empty(nghttp2_pq *pq); - -/* - * Returns the number of items in the queue |pq|. - */ -size_t nghttp2_pq_size(nghttp2_pq *pq); - -typedef int (*nghttp2_pq_item_cb)(void *item, void *arg); - -/* - * Updates each item in |pq| using function |fun| and re-construct - * priority queue. The |fun| must return non-zero if it modifies the - * item in a way that it affects ordering in the priority queue. The - * |arg| is passed to the 2nd parameter of |fun|. - */ -void nghttp2_pq_update(nghttp2_pq *pq, nghttp2_pq_item_cb fun, void *arg); - -/* - * Applys |fun| to each item in |pq|. The |arg| is passed as arg - * parameter to callback function. This function must not change the - * ordering key. If the return value from callback is nonzero, this - * function returns 1 immediately without iterating remaining items. - * Otherwise this function returns 0. - */ -int nghttp2_pq_each(nghttp2_pq *pq, nghttp2_pq_item_cb fun, void *arg); - -#endif /* NGHTTP2_PQ_H */ diff --git a/lib/nghttp2_priority_spec.c b/lib/nghttp2_priority_spec.c deleted file mode 100644 index cd254b1..0000000 --- a/lib/nghttp2_priority_spec.c +++ /dev/null @@ -1,44 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2014 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#include "nghttp2_priority_spec.h" - -void nghttp2_priority_spec_init(nghttp2_priority_spec *pri_spec, - int32_t stream_id, int32_t weight, - int exclusive) { - pri_spec->stream_id = stream_id; - pri_spec->weight = weight; - pri_spec->exclusive = exclusive != 0; -} - -void nghttp2_priority_spec_default_init(nghttp2_priority_spec *pri_spec) { - pri_spec->stream_id = 0; - pri_spec->weight = NGHTTP2_DEFAULT_WEIGHT; - pri_spec->exclusive = 0; -} - -int nghttp2_priority_spec_check_default(const nghttp2_priority_spec *pri_spec) { - return pri_spec->stream_id == 0 && - pri_spec->weight == NGHTTP2_DEFAULT_WEIGHT && pri_spec->exclusive == 0; -} diff --git a/lib/nghttp2_priority_spec.h b/lib/nghttp2_priority_spec.h deleted file mode 100644 index a325a58..0000000 --- a/lib/nghttp2_priority_spec.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2014 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#ifndef NGHTTP2_PRIORITY_SPEC_H -#define NGHTTP2_PRIORITY_SPEC_H - -#ifdef HAVE_CONFIG_H -#include -#endif /* HAVE_CONFIG_H */ - -#include - -#endif /* NGHTTP2_PRIORITY_SPEC_H */ diff --git a/lib/nghttp2_queue.c b/lib/nghttp2_queue.c deleted file mode 100644 index 055eb69..0000000 --- a/lib/nghttp2_queue.c +++ /dev/null @@ -1,85 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2012 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#include "nghttp2_queue.h" - -#include -#include - -void nghttp2_queue_init(nghttp2_queue *queue) { - queue->front = queue->back = NULL; -} - -void nghttp2_queue_free(nghttp2_queue *queue) { - if (!queue) { - return; - } else { - nghttp2_queue_cell *p = queue->front; - while (p) { - nghttp2_queue_cell *next = p->next; - free(p); - p = next; - } - } -} - -int nghttp2_queue_push(nghttp2_queue *queue, void *data) { - nghttp2_queue_cell *new_cell = - (nghttp2_queue_cell *)malloc(sizeof(nghttp2_queue_cell)); - if (!new_cell) { - return NGHTTP2_ERR_NOMEM; - } - new_cell->data = data; - new_cell->next = NULL; - if (queue->back) { - queue->back->next = new_cell; - queue->back = new_cell; - - } else { - queue->front = queue->back = new_cell; - } - return 0; -} - -void nghttp2_queue_pop(nghttp2_queue *queue) { - nghttp2_queue_cell *front = queue->front; - assert(front); - queue->front = front->next; - if (front == queue->back) { - queue->back = NULL; - } - free(front); -} - -void *nghttp2_queue_front(nghttp2_queue *queue) { - assert(queue->front); - return queue->front->data; -} - -void *nghttp2_queue_back(nghttp2_queue *queue) { - assert(queue->back); - return queue->back->data; -} - -int nghttp2_queue_empty(nghttp2_queue *queue) { return queue->front == NULL; } diff --git a/lib/nghttp2_queue.h b/lib/nghttp2_queue.h deleted file mode 100644 index d872b07..0000000 --- a/lib/nghttp2_queue.h +++ /dev/null @@ -1,49 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2012 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#ifndef NGHTTP2_QUEUE_H -#define NGHTTP2_QUEUE_H - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif /* HAVE_CONFIG_H */ - -#include - -typedef struct nghttp2_queue_cell { - void *data; - struct nghttp2_queue_cell *next; -} nghttp2_queue_cell; - -typedef struct { nghttp2_queue_cell *front, *back; } nghttp2_queue; - -void nghttp2_queue_init(nghttp2_queue *queue); -void nghttp2_queue_free(nghttp2_queue *queue); -int nghttp2_queue_push(nghttp2_queue *queue, void *data); -void nghttp2_queue_pop(nghttp2_queue *queue); -void *nghttp2_queue_front(nghttp2_queue *queue); -void *nghttp2_queue_back(nghttp2_queue *queue); -int nghttp2_queue_empty(nghttp2_queue *queue); - -#endif /* NGHTTP2_QUEUE_H */ diff --git a/lib/nghttp2_session.c b/lib/nghttp2_session.c deleted file mode 100644 index 7309aa0..0000000 --- a/lib/nghttp2_session.c +++ /dev/null @@ -1,6612 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2012 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#include "nghttp2_session.h" - -#include -#include -#include -#include - -#include "nghttp2_helper.h" -#include "nghttp2_net.h" -#include "nghttp2_priority_spec.h" -#include "nghttp2_option.h" -#include "nghttp2_http.h" - -/* - * Returns non-zero if the number of outgoing opened streams is larger - * than or equal to - * remote_settings.max_concurrent_streams. - */ -static int -session_is_outgoing_concurrent_streams_max(nghttp2_session *session) { - return session->remote_settings.max_concurrent_streams <= - session->num_outgoing_streams; -} - -/* - * Returns non-zero if the number of incoming opened streams is larger - * than or equal to - * local_settings.max_concurrent_streams. - */ -static int -session_is_incoming_concurrent_streams_max(nghttp2_session *session) { - return session->local_settings.max_concurrent_streams <= - session->num_incoming_streams; -} - -/* - * Returns non-zero if the number of incoming opened streams is larger - * than or equal to - * session->pending_local_max_concurrent_stream. - */ -static int -session_is_incoming_concurrent_streams_pending_max(nghttp2_session *session) { - return session->pending_local_max_concurrent_stream <= - session->num_incoming_streams; -} - -/* - * Returns non-zero if |lib_error| is non-fatal error. - */ -static int is_non_fatal(int lib_error_code) { - return lib_error_code < 0 && lib_error_code > NGHTTP2_ERR_FATAL; -} - -int nghttp2_is_fatal(int lib_error_code) { - return lib_error_code < NGHTTP2_ERR_FATAL; -} - -static int session_enforce_http_messaging(nghttp2_session *session) { - return (session->opt_flags & NGHTTP2_OPTMASK_NO_HTTP_MESSAGING) == 0; -} - -/* - * Returns nonzero if |frame| is trailer headers. - */ -static int session_trailer_headers(nghttp2_session *session, - nghttp2_stream *stream, - nghttp2_frame *frame) { - if (!stream || frame->hd.type != NGHTTP2_HEADERS) { - return 0; - } - if (session->server) { - return frame->headers.cat == NGHTTP2_HCAT_HEADERS; - } - - return frame->headers.cat == NGHTTP2_HCAT_HEADERS && - (stream->http_flags & NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE) == 0; -} - -/* Returns nonzero if the |stream| is in reserved(remote) state */ -static int state_reserved_remote(nghttp2_session *session, - nghttp2_stream *stream) { - return stream->state == NGHTTP2_STREAM_RESERVED && - !nghttp2_session_is_my_stream_id(session, stream->stream_id); -} - -/* Returns nonzero if the |stream| is in reserved(local) state */ -static int state_reserved_local(nghttp2_session *session, - nghttp2_stream *stream) { - return stream->state == NGHTTP2_STREAM_RESERVED && - nghttp2_session_is_my_stream_id(session, stream->stream_id); -} - -/* - * Checks whether received stream_id is valid. This function returns - * 1 if it succeeds, or 0. - */ -static int session_is_new_peer_stream_id(nghttp2_session *session, - int32_t stream_id) { - return stream_id != 0 && - !nghttp2_session_is_my_stream_id(session, stream_id) && - session->last_recv_stream_id < stream_id; -} - -static int session_detect_idle_stream(nghttp2_session *session, - int32_t stream_id) { - /* Assume that stream object with stream_id does not exist */ - if (nghttp2_session_is_my_stream_id(session, stream_id)) { - if (session->next_stream_id <= (uint32_t)stream_id) { - return 1; - } - return 0; - } - if (session_is_new_peer_stream_id(session, stream_id)) { - return 1; - } - return 0; -} - -static int session_terminate_session(nghttp2_session *session, - int32_t last_stream_id, - uint32_t error_code, const char *reason) { - int rv; - const uint8_t *debug_data; - size_t debug_datalen; - - if (session->goaway_flags & NGHTTP2_GOAWAY_TERM_ON_SEND) { - return 0; - } - - if (reason == NULL) { - debug_data = NULL; - debug_datalen = 0; - } else { - debug_data = (const uint8_t *)reason; - debug_datalen = strlen(reason); - } - - rv = nghttp2_session_add_goaway(session, last_stream_id, error_code, - debug_data, debug_datalen, - NGHTTP2_GOAWAY_AUX_TERM_ON_SEND); - - if (rv != 0) { - return rv; - } - - session->goaway_flags |= NGHTTP2_GOAWAY_TERM_ON_SEND; - - return 0; -} - -int nghttp2_session_terminate_session(nghttp2_session *session, - uint32_t error_code) { - return session_terminate_session(session, session->last_proc_stream_id, - error_code, NULL); -} - -int nghttp2_session_terminate_session2(nghttp2_session *session, - int32_t last_stream_id, - uint32_t error_code) { - return session_terminate_session(session, last_stream_id, error_code, NULL); -} - -int nghttp2_session_terminate_session_with_reason(nghttp2_session *session, - uint32_t error_code, - const char *reason) { - return session_terminate_session(session, session->last_proc_stream_id, - error_code, reason); -} - -int nghttp2_session_is_my_stream_id(nghttp2_session *session, - int32_t stream_id) { - int rem; - if (stream_id == 0) { - return 0; - } - rem = stream_id & 0x1; - if (session->server) { - return rem == 0; - } - return rem == 1; -} - -nghttp2_stream *nghttp2_session_get_stream(nghttp2_session *session, - int32_t stream_id) { - nghttp2_stream *stream; - - stream = (nghttp2_stream *)nghttp2_map_find(&session->streams, stream_id); - - if (stream == NULL || (stream->flags & NGHTTP2_STREAM_FLAG_CLOSED) || - stream->state == NGHTTP2_STREAM_IDLE) { - return NULL; - } - - return stream; -} - -nghttp2_stream *nghttp2_session_get_stream_raw(nghttp2_session *session, - int32_t stream_id) { - return (nghttp2_stream *)nghttp2_map_find(&session->streams, stream_id); -} - -static int outbound_item_less(const void *lhsx, const void *rhsx) { - const nghttp2_outbound_item *lhs, *rhs; - - lhs = (const nghttp2_outbound_item *)lhsx; - rhs = (const nghttp2_outbound_item *)rhsx; - - return (lhs->cycle < rhs->cycle) ? 1 : 0; -} - -static void session_inbound_frame_reset(nghttp2_session *session) { - nghttp2_inbound_frame *iframe = &session->iframe; - nghttp2_mem *mem = &session->mem; - /* A bit risky code, since if this function is called from - nghttp2_session_new(), we rely on the fact that - iframe->frame.hd.type is 0, so that no free is performed. */ - switch (iframe->frame.hd.type) { - case NGHTTP2_HEADERS: - nghttp2_frame_headers_free(&iframe->frame.headers, mem); - break; - case NGHTTP2_PRIORITY: - nghttp2_frame_priority_free(&iframe->frame.priority); - break; - case NGHTTP2_RST_STREAM: - nghttp2_frame_rst_stream_free(&iframe->frame.rst_stream); - break; - case NGHTTP2_SETTINGS: - nghttp2_frame_settings_free(&iframe->frame.settings, mem); - break; - case NGHTTP2_PUSH_PROMISE: - nghttp2_frame_push_promise_free(&iframe->frame.push_promise, mem); - break; - case NGHTTP2_PING: - nghttp2_frame_ping_free(&iframe->frame.ping); - break; - case NGHTTP2_GOAWAY: - nghttp2_frame_goaway_free(&iframe->frame.goaway, mem); - break; - case NGHTTP2_WINDOW_UPDATE: - nghttp2_frame_window_update_free(&iframe->frame.window_update); - break; - } - - memset(&iframe->frame, 0, sizeof(nghttp2_frame)); - memset(&iframe->ext_frame_payload, 0, sizeof(nghttp2_ext_frame_payload)); - - iframe->state = NGHTTP2_IB_READ_HEAD; - - nghttp2_buf_wrap_init(&iframe->sbuf, iframe->raw_sbuf, - sizeof(iframe->raw_sbuf)); - iframe->sbuf.mark += NGHTTP2_FRAME_HDLEN; - - nghttp2_buf_free(&iframe->lbuf, mem); - nghttp2_buf_wrap_init(&iframe->lbuf, NULL, 0); - - iframe->niv = 0; - iframe->payloadleft = 0; - iframe->padlen = 0; - iframe->iv[NGHTTP2_INBOUND_NUM_IV - 1].settings_id = - NGHTTP2_SETTINGS_HEADER_TABLE_SIZE; - iframe->iv[NGHTTP2_INBOUND_NUM_IV - 1].value = UINT32_MAX; -} - -static void init_settings(nghttp2_settings_storage *settings) { - settings->header_table_size = NGHTTP2_HD_DEFAULT_MAX_BUFFER_SIZE; - settings->enable_push = 1; - settings->max_concurrent_streams = NGHTTP2_INITIAL_MAX_CONCURRENT_STREAMS; - settings->initial_window_size = NGHTTP2_INITIAL_WINDOW_SIZE; - settings->max_frame_size = NGHTTP2_MAX_FRAME_SIZE_MIN; - settings->max_header_list_size = UINT32_MAX; -} - -static void active_outbound_item_reset(nghttp2_active_outbound_item *aob, - nghttp2_mem *mem) { - DEBUGF(fprintf(stderr, "send: reset nghttp2_active_outbound_item\n")); - DEBUGF(fprintf(stderr, "send: aob->item = %p\n", aob->item)); - nghttp2_outbound_item_free(aob->item, mem); - nghttp2_mem_free(mem, aob->item); - aob->item = NULL; - nghttp2_bufs_reset(&aob->framebufs); - aob->state = NGHTTP2_OB_POP_ITEM; -} - -/* The global variable for tests where we want to disable strict - preface handling. */ -int nghttp2_enable_strict_preface = 1; - -static int session_new(nghttp2_session **session_ptr, - const nghttp2_session_callbacks *callbacks, - void *user_data, int server, - const nghttp2_option *option, nghttp2_mem *mem) { - int rv; - - if (mem == NULL) { - mem = nghttp2_mem_default(); - } - - *session_ptr = nghttp2_mem_calloc(mem, 1, sizeof(nghttp2_session)); - if (*session_ptr == NULL) { - rv = NGHTTP2_ERR_NOMEM; - goto fail_session; - } - - (*session_ptr)->mem = *mem; - mem = &(*session_ptr)->mem; - - /* next_stream_id is initialized in either - nghttp2_session_client_new2 or nghttp2_session_server_new2 */ - - rv = nghttp2_pq_init(&(*session_ptr)->ob_da_pq, outbound_item_less, mem); - if (rv != 0) { - goto fail_ob_da_pq; - } - - rv = nghttp2_hd_deflate_init(&(*session_ptr)->hd_deflater, mem); - if (rv != 0) { - goto fail_hd_deflater; - } - rv = nghttp2_hd_inflate_init(&(*session_ptr)->hd_inflater, mem); - if (rv != 0) { - goto fail_hd_inflater; - } - rv = nghttp2_map_init(&(*session_ptr)->streams, mem); - if (rv != 0) { - goto fail_map; - } - - nghttp2_stream_roots_init(&(*session_ptr)->roots); - - (*session_ptr)->remote_window_size = NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE; - (*session_ptr)->recv_window_size = 0; - (*session_ptr)->consumed_size = 0; - (*session_ptr)->recv_reduction = 0; - (*session_ptr)->local_window_size = NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE; - - (*session_ptr)->goaway_flags = NGHTTP2_GOAWAY_NONE; - (*session_ptr)->local_last_stream_id = (1u << 31) - 1; - (*session_ptr)->remote_last_stream_id = (1u << 31) - 1; - - (*session_ptr)->inflight_niv = -1; - - (*session_ptr)->pending_local_max_concurrent_stream = - NGHTTP2_INITIAL_MAX_CONCURRENT_STREAMS; - (*session_ptr)->pending_enable_push = 1; - - if (server) { - (*session_ptr)->server = 1; - } - - /* 1 for Pad Field. */ - rv = nghttp2_bufs_init3(&(*session_ptr)->aob.framebufs, - NGHTTP2_FRAMEBUF_CHUNKLEN, NGHTTP2_FRAMEBUF_MAX_NUM, - 1, NGHTTP2_FRAME_HDLEN + 1, mem); - if (rv != 0) { - goto fail_aob_framebuf; - } - - active_outbound_item_reset(&(*session_ptr)->aob, mem); - - init_settings(&(*session_ptr)->remote_settings); - init_settings(&(*session_ptr)->local_settings); - - if (option) { - if ((option->opt_set_mask & NGHTTP2_OPT_NO_AUTO_WINDOW_UPDATE) && - option->no_auto_window_update) { - - (*session_ptr)->opt_flags |= NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE; - } - - if (option->opt_set_mask & NGHTTP2_OPT_PEER_MAX_CONCURRENT_STREAMS) { - - (*session_ptr)->remote_settings.max_concurrent_streams = - option->peer_max_concurrent_streams; - } - - if ((option->opt_set_mask & NGHTTP2_OPT_NO_RECV_CLIENT_MAGIC) && - option->no_recv_client_magic) { - - (*session_ptr)->opt_flags |= NGHTTP2_OPTMASK_NO_RECV_CLIENT_MAGIC; - } - - if ((option->opt_set_mask & NGHTTP2_OPT_NO_HTTP_MESSAGING) && - option->no_http_messaging) { - - (*session_ptr)->opt_flags |= NGHTTP2_OPTMASK_NO_HTTP_MESSAGING; - } - } - - (*session_ptr)->callbacks = *callbacks; - (*session_ptr)->user_data = user_data; - - session_inbound_frame_reset(*session_ptr); - - if (nghttp2_enable_strict_preface) { - nghttp2_inbound_frame *iframe = &(*session_ptr)->iframe; - - if (server && - ((*session_ptr)->opt_flags & NGHTTP2_OPTMASK_NO_RECV_CLIENT_MAGIC) == - 0) { - iframe->state = NGHTTP2_IB_READ_CLIENT_MAGIC; - iframe->payloadleft = NGHTTP2_CLIENT_MAGIC_LEN; - } else { - iframe->state = NGHTTP2_IB_READ_FIRST_SETTINGS; - } - - if (!server) { - (*session_ptr)->aob.state = NGHTTP2_OB_SEND_CLIENT_MAGIC; - nghttp2_bufs_add(&(*session_ptr)->aob.framebufs, NGHTTP2_CLIENT_MAGIC, - NGHTTP2_CLIENT_MAGIC_LEN); - } - } - - return 0; - -fail_aob_framebuf: - nghttp2_map_free(&(*session_ptr)->streams); -fail_map: - nghttp2_hd_inflate_free(&(*session_ptr)->hd_inflater); -fail_hd_inflater: - nghttp2_hd_deflate_free(&(*session_ptr)->hd_deflater); -fail_hd_deflater: - nghttp2_pq_free(&(*session_ptr)->ob_da_pq); -fail_ob_da_pq: - nghttp2_mem_free(mem, *session_ptr); -fail_session: - return rv; -} - -int nghttp2_session_client_new(nghttp2_session **session_ptr, - const nghttp2_session_callbacks *callbacks, - void *user_data) { - return nghttp2_session_client_new3(session_ptr, callbacks, user_data, NULL, - NULL); -} - -int nghttp2_session_client_new2(nghttp2_session **session_ptr, - const nghttp2_session_callbacks *callbacks, - void *user_data, const nghttp2_option *option) { - return nghttp2_session_client_new3(session_ptr, callbacks, user_data, option, - NULL); -} - -int nghttp2_session_client_new3(nghttp2_session **session_ptr, - const nghttp2_session_callbacks *callbacks, - void *user_data, const nghttp2_option *option, - nghttp2_mem *mem) { - int rv; - nghttp2_session *session; - - rv = session_new(&session, callbacks, user_data, 0, option, mem); - - if (rv != 0) { - return rv; - } - /* IDs for use in client */ - session->next_stream_id = 1; - - *session_ptr = session; - - return 0; -} - -int nghttp2_session_server_new(nghttp2_session **session_ptr, - const nghttp2_session_callbacks *callbacks, - void *user_data) { - return nghttp2_session_server_new3(session_ptr, callbacks, user_data, NULL, - NULL); -} - -int nghttp2_session_server_new2(nghttp2_session **session_ptr, - const nghttp2_session_callbacks *callbacks, - void *user_data, const nghttp2_option *option) { - return nghttp2_session_server_new3(session_ptr, callbacks, user_data, option, - NULL); -} - -int nghttp2_session_server_new3(nghttp2_session **session_ptr, - const nghttp2_session_callbacks *callbacks, - void *user_data, const nghttp2_option *option, - nghttp2_mem *mem) { - int rv; - nghttp2_session *session; - - rv = session_new(&session, callbacks, user_data, 1, option, mem); - - if (rv != 0) { - return rv; - } - /* IDs for use in client */ - session->next_stream_id = 2; - - *session_ptr = session; - - return 0; -} - -static int free_streams(nghttp2_map_entry *entry, void *ptr) { - nghttp2_session *session; - nghttp2_stream *stream; - nghttp2_outbound_item *item; - nghttp2_mem *mem; - - session = (nghttp2_session *)ptr; - mem = &session->mem; - stream = (nghttp2_stream *)entry; - item = stream->item; - - if (item && !item->queued && item != session->aob.item) { - nghttp2_outbound_item_free(item, mem); - nghttp2_mem_free(mem, item); - } - - nghttp2_stream_free(stream); - nghttp2_mem_free(mem, stream); - - return 0; -} - -static void ob_pq_free(nghttp2_pq *pq, nghttp2_mem *mem) { - while (!nghttp2_pq_empty(pq)) { - nghttp2_outbound_item *item = (nghttp2_outbound_item *)nghttp2_pq_top(pq); - nghttp2_outbound_item_free(item, mem); - nghttp2_mem_free(mem, item); - nghttp2_pq_pop(pq); - } - nghttp2_pq_free(pq); -} - -static void ob_q_free(nghttp2_outbound_queue *q, nghttp2_mem *mem) { - nghttp2_outbound_item *item, *next; - for (item = q->head; item;) { - next = item->qnext; - nghttp2_outbound_item_free(item, mem); - nghttp2_mem_free(mem, item); - item = next; - } -} - -void nghttp2_session_del(nghttp2_session *session) { - nghttp2_mem *mem; - - if (session == NULL) { - return; - } - - mem = &session->mem; - - nghttp2_mem_free(mem, session->inflight_iv); - - nghttp2_stream_roots_free(&session->roots); - - /* Have to free streams first, so that we can check - stream->item->queued */ - nghttp2_map_each_free(&session->streams, free_streams, session); - nghttp2_map_free(&session->streams); - - ob_q_free(&session->ob_urgent, mem); - ob_q_free(&session->ob_reg, mem); - ob_q_free(&session->ob_syn, mem); - ob_pq_free(&session->ob_da_pq, mem); - active_outbound_item_reset(&session->aob, mem); - session_inbound_frame_reset(session); - nghttp2_hd_deflate_free(&session->hd_deflater); - nghttp2_hd_inflate_free(&session->hd_inflater); - nghttp2_bufs_free(&session->aob.framebufs); - nghttp2_mem_free(mem, session); -} - -int -nghttp2_session_reprioritize_stream(nghttp2_session *session, - nghttp2_stream *stream, - const nghttp2_priority_spec *pri_spec_in) { - int rv; - nghttp2_stream *dep_stream = NULL; - nghttp2_stream *root_stream; - nghttp2_priority_spec pri_spec_default; - const nghttp2_priority_spec *pri_spec = pri_spec_in; - - if (!nghttp2_stream_in_dep_tree(stream)) { - return 0; - } - - if (pri_spec->stream_id == stream->stream_id) { - return nghttp2_session_terminate_session_with_reason( - session, NGHTTP2_PROTOCOL_ERROR, "depend on itself"); - } - - if (pri_spec->stream_id != 0) { - dep_stream = nghttp2_session_get_stream_raw(session, pri_spec->stream_id); - - if (session->server && !dep_stream && - session_detect_idle_stream(session, pri_spec->stream_id)) { - - nghttp2_priority_spec_default_init(&pri_spec_default); - - dep_stream = nghttp2_session_open_stream( - session, pri_spec->stream_id, NGHTTP2_FLAG_NONE, &pri_spec_default, - NGHTTP2_STREAM_IDLE, NULL); - - if (dep_stream == NULL) { - return NGHTTP2_ERR_NOMEM; - } - } else if (!dep_stream || !nghttp2_stream_in_dep_tree(dep_stream)) { - nghttp2_priority_spec_default_init(&pri_spec_default); - pri_spec = &pri_spec_default; - } - } - - if (pri_spec->stream_id == 0) { - nghttp2_stream_dep_remove_subtree(stream); - - /* We have to update weight after removing stream from tree */ - stream->weight = pri_spec->weight; - - if (pri_spec->exclusive && - session->roots.num_streams <= NGHTTP2_MAX_DEP_TREE_LENGTH) { - - rv = nghttp2_stream_dep_all_your_stream_are_belong_to_us(stream, session); - } else { - rv = nghttp2_stream_dep_make_root(stream, session); - } - - return rv; - } - - assert(dep_stream); - - if (nghttp2_stream_dep_subtree_find(stream, dep_stream)) { - DEBUGF(fprintf(stderr, "stream: cycle detected, dep_stream(%p)=%d " - "stream(%p)=%d\n", - dep_stream, dep_stream->stream_id, stream, - stream->stream_id)); - - nghttp2_stream_dep_remove_subtree(dep_stream); - nghttp2_stream_dep_make_root(dep_stream, session); - } - - nghttp2_stream_dep_remove_subtree(stream); - - /* We have to update weight after removing stream from tree */ - stream->weight = pri_spec->weight; - - root_stream = nghttp2_stream_get_dep_root(dep_stream); - - if (root_stream->num_substreams + stream->num_substreams > - NGHTTP2_MAX_DEP_TREE_LENGTH) { - stream->weight = NGHTTP2_DEFAULT_WEIGHT; - - rv = nghttp2_stream_dep_make_root(stream, session); - } else { - if (pri_spec->exclusive) { - rv = nghttp2_stream_dep_insert_subtree(dep_stream, stream, session); - } else { - rv = nghttp2_stream_dep_add_subtree(dep_stream, stream, session); - } - } - - if (rv != 0) { - return rv; - } - - return 0; -} - -int nghttp2_session_add_item(nghttp2_session *session, - nghttp2_outbound_item *item) { - /* TODO Return error if stream is not found for the frame requiring - stream presence. */ - int rv = 0; - nghttp2_stream *stream; - nghttp2_frame *frame; - - frame = &item->frame; - stream = nghttp2_session_get_stream(session, frame->hd.stream_id); - - if (frame->hd.type != NGHTTP2_DATA) { - - switch (frame->hd.type) { - case NGHTTP2_HEADERS: - /* We push request HEADERS and push response HEADERS to - dedicated queue because their transmission is affected by - SETTINGS_MAX_CONCURRENT_STREAMS */ - /* TODO If 2 HEADERS are submitted for reserved stream, then - both of them are queued into ob_syn, which is not - desirable. */ - if (frame->headers.cat == NGHTTP2_HCAT_REQUEST) { - nghttp2_outbound_queue_push(&session->ob_syn, item); - item->queued = 1; - break; - } - - if (stream && (stream->state == NGHTTP2_STREAM_RESERVED || - item->aux_data.headers.attach_stream)) { - rv = nghttp2_stream_attach_item(stream, item, session); - - if (rv != 0) { - return rv; - } - - break; - } - - nghttp2_outbound_queue_push(&session->ob_reg, item); - item->queued = 1; - break; - case NGHTTP2_SETTINGS: - case NGHTTP2_PING: - nghttp2_outbound_queue_push(&session->ob_urgent, item); - item->queued = 1; - break; - case NGHTTP2_RST_STREAM: - if (stream) { - stream->state = NGHTTP2_STREAM_CLOSING; - } - /* fall through */ - default: - nghttp2_outbound_queue_push(&session->ob_reg, item); - item->queued = 1; - } - - return 0; - } - - if (!stream) { - return NGHTTP2_ERR_STREAM_CLOSED; - } - - if (stream->item) { - return NGHTTP2_ERR_DATA_EXIST; - } - - rv = nghttp2_stream_attach_item(stream, item, session); - - if (rv != 0) { - return rv; - } - - return 0; -} - -int nghttp2_session_add_rst_stream(nghttp2_session *session, int32_t stream_id, - uint32_t error_code) { - int rv; - nghttp2_outbound_item *item; - nghttp2_frame *frame; - nghttp2_stream *stream; - nghttp2_mem *mem; - - mem = &session->mem; - stream = nghttp2_session_get_stream(session, stream_id); - if (stream && stream->state == NGHTTP2_STREAM_CLOSING) { - return 0; - } - - /* Cancel pending request HEADERS in ob_syn if this RST_STREAM - refers to that stream. */ - if (!session->server && nghttp2_session_is_my_stream_id(session, stream_id) && - nghttp2_outbound_queue_top(&session->ob_syn)) { - nghttp2_headers_aux_data *aux_data; - nghttp2_frame *headers_frame; - - headers_frame = &nghttp2_outbound_queue_top(&session->ob_syn)->frame; - assert(headers_frame->hd.type == NGHTTP2_HEADERS); - - if (headers_frame->hd.stream_id <= stream_id && - (uint32_t)stream_id < session->next_stream_id) { - - for (item = session->ob_syn.head; item; item = item->qnext) { - aux_data = &item->aux_data.headers; - - if (item->frame.hd.stream_id < stream_id) { - continue; - } - - /* stream_id in ob_syn queue must be strictly increasing. If - we found larger ID, then we can break here. */ - if (item->frame.hd.stream_id > stream_id || aux_data->canceled) { - break; - } - - aux_data->error_code = error_code; - aux_data->canceled = 1; - - return 0; - } - } - } - - item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item)); - if (item == NULL) { - return NGHTTP2_ERR_NOMEM; - } - - nghttp2_outbound_item_init(item); - - frame = &item->frame; - - nghttp2_frame_rst_stream_init(&frame->rst_stream, stream_id, error_code); - rv = nghttp2_session_add_item(session, item); - if (rv != 0) { - nghttp2_frame_rst_stream_free(&frame->rst_stream); - nghttp2_mem_free(mem, item); - return rv; - } - return 0; -} - -nghttp2_stream *nghttp2_session_open_stream(nghttp2_session *session, - int32_t stream_id, uint8_t flags, - nghttp2_priority_spec *pri_spec_in, - nghttp2_stream_state initial_state, - void *stream_user_data) { - int rv; - nghttp2_stream *stream; - nghttp2_stream *dep_stream = NULL; - nghttp2_stream *root_stream; - int stream_alloc = 0; - nghttp2_priority_spec pri_spec_default; - nghttp2_priority_spec *pri_spec = pri_spec_in; - nghttp2_mem *mem; - - mem = &session->mem; - stream = nghttp2_session_get_stream_raw(session, stream_id); - - if (stream) { - assert(stream->state == NGHTTP2_STREAM_IDLE); - assert(nghttp2_stream_in_dep_tree(stream)); - nghttp2_session_detach_idle_stream(session, stream); - nghttp2_stream_dep_remove(stream); - } else { - if (session->server && initial_state != NGHTTP2_STREAM_IDLE && - !nghttp2_session_is_my_stream_id(session, stream_id)) { - - nghttp2_session_adjust_closed_stream(session, 1); - } - - stream = nghttp2_mem_malloc(mem, sizeof(nghttp2_stream)); - if (stream == NULL) { - return NULL; - } - - stream_alloc = 1; - } - - if (pri_spec->stream_id != 0) { - dep_stream = nghttp2_session_get_stream_raw(session, pri_spec->stream_id); - - if (session->server && !dep_stream && - session_detect_idle_stream(session, pri_spec->stream_id)) { - /* Depends on idle stream, which does not exist in memory. - Assign default priority for it. */ - nghttp2_priority_spec_default_init(&pri_spec_default); - - dep_stream = nghttp2_session_open_stream( - session, pri_spec->stream_id, NGHTTP2_FLAG_NONE, &pri_spec_default, - NGHTTP2_STREAM_IDLE, NULL); - - if (dep_stream == NULL) { - if (stream_alloc) { - nghttp2_mem_free(mem, stream); - } - - return NULL; - } - } else if (!dep_stream || !nghttp2_stream_in_dep_tree(dep_stream)) { - /* If dep_stream is not part of dependency tree, stream will get - default priority. */ - nghttp2_priority_spec_default_init(&pri_spec_default); - pri_spec = &pri_spec_default; - } - } - - if (initial_state == NGHTTP2_STREAM_RESERVED) { - flags |= NGHTTP2_STREAM_FLAG_PUSH; - } - - nghttp2_stream_init( - stream, stream_id, flags, initial_state, pri_spec->weight, - &session->roots, session->remote_settings.initial_window_size, - session->local_settings.initial_window_size, stream_user_data); - - if (stream_alloc) { - rv = nghttp2_map_insert(&session->streams, &stream->map_entry); - if (rv != 0) { - nghttp2_mem_free(mem, stream); - return NULL; - } - } - - switch (initial_state) { - case NGHTTP2_STREAM_RESERVED: - if (nghttp2_session_is_my_stream_id(session, stream_id)) { - /* half closed (remote) */ - nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_RD); - } else { - /* half closed (local) */ - nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_WR); - } - /* Reserved stream does not count in the concurrent streams - limit. That is one of the DOS vector. */ - break; - case NGHTTP2_STREAM_IDLE: - /* Idle stream does not count toward the concurrent streams limit. - This is used as anchor node in dependency tree. */ - assert(session->server); - nghttp2_session_keep_idle_stream(session, stream); - break; - default: - if (nghttp2_session_is_my_stream_id(session, stream_id)) { - ++session->num_outgoing_streams; - } else { - ++session->num_incoming_streams; - } - } - - /* We don't have to track dependency of received reserved stream */ - if (stream->shut_flags & NGHTTP2_SHUT_WR) { - return stream; - } - - if (pri_spec->stream_id == 0) { - - ++session->roots.num_streams; - - if (pri_spec->exclusive && - session->roots.num_streams <= NGHTTP2_MAX_DEP_TREE_LENGTH) { - rv = nghttp2_stream_dep_all_your_stream_are_belong_to_us(stream, session); - - /* Since no dpri is changed in dependency tree, the above - function call never fail. */ - assert(rv == 0); - } else { - nghttp2_stream_roots_add(&session->roots, stream); - } - - return stream; - } - - /* TODO Client does not have to track dependencies of streams except - for those which have upload data. Currently, we just track - everything. */ - - assert(dep_stream); - - root_stream = nghttp2_stream_get_dep_root(dep_stream); - - if (root_stream->num_substreams < NGHTTP2_MAX_DEP_TREE_LENGTH) { - if (pri_spec->exclusive) { - nghttp2_stream_dep_insert(dep_stream, stream); - } else { - nghttp2_stream_dep_add(dep_stream, stream); - } - } else { - stream->weight = NGHTTP2_DEFAULT_WEIGHT; - - nghttp2_stream_roots_add(&session->roots, stream); - } - - return stream; -} - -int nghttp2_session_close_stream(nghttp2_session *session, int32_t stream_id, - uint32_t error_code) { - int rv; - nghttp2_stream *stream; - nghttp2_mem *mem; - - mem = &session->mem; - stream = nghttp2_session_get_stream(session, stream_id); - - if (!stream) { - return NGHTTP2_ERR_INVALID_ARGUMENT; - } - - DEBUGF(fprintf(stderr, "stream: stream(%p)=%d close\n", stream, - stream->stream_id)); - - if (stream->item) { - nghttp2_outbound_item *item; - - item = stream->item; - - rv = nghttp2_stream_detach_item(stream, session); - - if (rv != 0) { - return rv; - } - - /* If item is queued, it will be deleted when it is popped - (nghttp2_session_prep_frame() will fail). If session->aob.item - points to this item, let active_outbound_item_reset() - free the item. */ - if (!item->queued && item != session->aob.item) { - nghttp2_outbound_item_free(item, mem); - nghttp2_mem_free(mem, item); - } - } - - /* We call on_stream_close_callback even if stream->state is - NGHTTP2_STREAM_INITIAL. This will happen while sending request - HEADERS, a local endpoint receives RST_STREAM for that stream. It - may be PROTOCOL_ERROR, but without notifying stream closure will - hang the stream in a local endpoint. - */ - - if (session->callbacks.on_stream_close_callback) { - if (session->callbacks.on_stream_close_callback( - session, stream_id, error_code, session->user_data) != 0) { - - return NGHTTP2_ERR_CALLBACK_FAILURE; - } - } - - /* pushed streams which is not opened yet is not counted toward max - concurrent limits */ - if ((stream->flags & NGHTTP2_STREAM_FLAG_PUSH) == 0) { - if (nghttp2_session_is_my_stream_id(session, stream_id)) { - --session->num_outgoing_streams; - } else { - --session->num_incoming_streams; - } - } - - /* Closes both directions just in case they are not closed yet */ - stream->flags |= NGHTTP2_STREAM_FLAG_CLOSED; - - if (session->server && nghttp2_stream_in_dep_tree(stream)) { - /* On server side, retain stream at most MAX_CONCURRENT_STREAMS - combined with the current active incoming streams to make - dependency tree work better. */ - nghttp2_session_keep_closed_stream(session, stream); - } else { - nghttp2_session_destroy_stream(session, stream); - } - - return 0; -} - -void nghttp2_session_destroy_stream(nghttp2_session *session, - nghttp2_stream *stream) { - nghttp2_mem *mem; - - DEBUGF(fprintf(stderr, "stream: destroy closed stream(%p)=%d\n", stream, - stream->stream_id)); - - mem = &session->mem; - - nghttp2_stream_dep_remove(stream); - - nghttp2_map_remove(&session->streams, stream->stream_id); - nghttp2_stream_free(stream); - nghttp2_mem_free(mem, stream); -} - -void nghttp2_session_keep_closed_stream(nghttp2_session *session, - nghttp2_stream *stream) { - DEBUGF(fprintf(stderr, "stream: keep closed stream(%p)=%d, state=%d\n", - stream, stream->stream_id, stream->state)); - - if (session->closed_stream_tail) { - session->closed_stream_tail->closed_next = stream; - stream->closed_prev = session->closed_stream_tail; - } else { - session->closed_stream_head = stream; - } - session->closed_stream_tail = stream; - - ++session->num_closed_streams; - - nghttp2_session_adjust_closed_stream(session, 0); -} - -void nghttp2_session_keep_idle_stream(nghttp2_session *session, - nghttp2_stream *stream) { - DEBUGF(fprintf(stderr, "stream: keep idle stream(%p)=%d, state=%d\n", stream, - stream->stream_id, stream->state)); - - if (session->idle_stream_tail) { - session->idle_stream_tail->closed_next = stream; - stream->closed_prev = session->idle_stream_tail; - } else { - session->idle_stream_head = stream; - } - session->idle_stream_tail = stream; - - ++session->num_idle_streams; - - nghttp2_session_adjust_idle_stream(session); -} - -void nghttp2_session_detach_idle_stream(nghttp2_session *session, - nghttp2_stream *stream) { - nghttp2_stream *prev_stream, *next_stream; - - DEBUGF(fprintf(stderr, "stream: detach idle stream(%p)=%d, state=%d\n", - stream, stream->stream_id, stream->state)); - - prev_stream = stream->closed_prev; - next_stream = stream->closed_next; - - if (prev_stream) { - prev_stream->closed_next = next_stream; - } else { - session->idle_stream_head = next_stream; - } - - if (next_stream) { - next_stream->closed_prev = prev_stream; - } else { - session->idle_stream_tail = prev_stream; - } - - stream->closed_prev = NULL; - stream->closed_next = NULL; - - --session->num_idle_streams; -} - -void nghttp2_session_adjust_closed_stream(nghttp2_session *session, - ssize_t offset) { - size_t num_stream_max; - - num_stream_max = nghttp2_min(session->local_settings.max_concurrent_streams, - session->pending_local_max_concurrent_stream); - - DEBUGF(fprintf(stderr, "stream: adjusting kept closed streams " - "num_closed_streams=%zu, num_incoming_streams=%zu, " - "max_concurrent_streams=%zu\n", - session->num_closed_streams, session->num_incoming_streams, - num_stream_max)); - - while (session->num_closed_streams > 0 && - session->num_closed_streams + session->num_incoming_streams + offset > - num_stream_max) { - nghttp2_stream *head_stream; - - head_stream = session->closed_stream_head; - - assert(head_stream); - - session->closed_stream_head = head_stream->closed_next; - - if (session->closed_stream_head) { - session->closed_stream_head->closed_prev = NULL; - } else { - session->closed_stream_tail = NULL; - } - - nghttp2_session_destroy_stream(session, head_stream); - /* head_stream is now freed */ - --session->num_closed_streams; - } -} - -void nghttp2_session_adjust_idle_stream(nghttp2_session *session) { - size_t max; - - /* Make minimum number of idle streams 2 so that allocating 2 - streams at once is easy. This happens when PRIORITY frame to - idle stream, which depends on idle stream which does not - exist. */ - max = - nghttp2_max(2, nghttp2_min(session->local_settings.max_concurrent_streams, - session->pending_local_max_concurrent_stream)); - - DEBUGF(fprintf(stderr, "stream: adjusting kept idle streams " - "num_idle_streams=%zu, max=%zu\n", - session->num_idle_streams, max)); - - while (session->num_idle_streams > max) { - nghttp2_stream *head; - - head = session->idle_stream_head; - assert(head); - - session->idle_stream_head = head->closed_next; - - if (session->idle_stream_head) { - session->idle_stream_head->closed_prev = NULL; - } else { - session->idle_stream_tail = NULL; - } - - nghttp2_session_destroy_stream(session, head); - /* head is now destroyed */ - --session->num_idle_streams; - } -} - -/* - * Closes stream with stream ID |stream_id| if both transmission and - * reception of the stream were disallowed. The |error_code| indicates - * the reason of the closure. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP2_ERR_INVALID_ARGUMENT - * The stream is not found. - * NGHTTP2_ERR_CALLBACK_FAILURE - * The callback function failed. - */ -int nghttp2_session_close_stream_if_shut_rdwr(nghttp2_session *session, - nghttp2_stream *stream) { - if ((stream->shut_flags & NGHTTP2_SHUT_RDWR) == NGHTTP2_SHUT_RDWR) { - return nghttp2_session_close_stream(session, stream->stream_id, - NGHTTP2_NO_ERROR); - } - return 0; -} - -/* - * This function returns nonzero if session is closing. - */ -static int session_is_closing(nghttp2_session *session) { - return (session->goaway_flags & NGHTTP2_GOAWAY_TERM_ON_SEND) != 0; -} - -/* - * Check that we can send a frame to the |stream|. This function - * returns 0 if we can send a frame to the |frame|, or one of the - * following negative error codes: - * - * NGHTTP2_ERR_STREAM_CLOSED - * The stream is already closed. - * NGHTTP2_ERR_STREAM_SHUT_WR - * The stream is half-closed for transmission. - * NGHTTP2_ERR_SESSION_CLOSING - * This session is closing. - */ -static int session_predicate_for_stream_send(nghttp2_session *session, - nghttp2_stream *stream) { - if (stream == NULL) { - return NGHTTP2_ERR_STREAM_CLOSED; - } - if (session_is_closing(session)) { - return NGHTTP2_ERR_SESSION_CLOSING; - } - if (stream->shut_flags & NGHTTP2_SHUT_WR) { - return NGHTTP2_ERR_STREAM_SHUT_WR; - } - return 0; -} - -/* - * This function checks request HEADERS frame, which opens stream, can - * be sent at this time. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP2_ERR_START_STREAM_NOT_ALLOWED - * New stream cannot be created because of GOAWAY: session is - * going down or received last_stream_id is strictly less than - * frame->hd.stream_id. - * NGHTTP2_ERR_STREAM_CLOSING - * request HEADERS was canceled by RST_STREAM while it is in queue. - */ -static int session_predicate_request_headers_send(nghttp2_session *session, - nghttp2_outbound_item *item) { - if (item->aux_data.headers.canceled) { - return NGHTTP2_ERR_STREAM_CLOSING; - } - /* If we are terminating session (NGHTTP2_GOAWAY_TERM_ON_SEND) or - GOAWAY was received from peer, new request is not allowed. */ - if (session->goaway_flags & - (NGHTTP2_GOAWAY_TERM_ON_SEND | NGHTTP2_GOAWAY_RECV)) { - return NGHTTP2_ERR_START_STREAM_NOT_ALLOWED; - } - return 0; -} - -/* - * This function checks HEADERS, which is the first frame from the - * server, with the |stream| can be sent at this time. The |stream| - * can be NULL. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP2_ERR_STREAM_CLOSED - * The stream is already closed or does not exist. - * NGHTTP2_ERR_STREAM_SHUT_WR - * The transmission is not allowed for this stream (e.g., a frame - * with END_STREAM flag set has already sent) - * NGHTTP2_ERR_INVALID_STREAM_ID - * The stream ID is invalid. - * NGHTTP2_ERR_STREAM_CLOSING - * RST_STREAM was queued for this stream. - * NGHTTP2_ERR_INVALID_STREAM_STATE - * The state of the stream is not valid. - * NGHTTP2_ERR_SESSION_CLOSING - * This session is closing. - */ -static int session_predicate_response_headers_send(nghttp2_session *session, - nghttp2_stream *stream) { - int rv; - rv = session_predicate_for_stream_send(session, stream); - if (rv != 0) { - return rv; - } - assert(stream); - if (nghttp2_session_is_my_stream_id(session, stream->stream_id)) { - return NGHTTP2_ERR_INVALID_STREAM_ID; - } - if (stream->state == NGHTTP2_STREAM_OPENING) { - return 0; - } - if (stream->state == NGHTTP2_STREAM_CLOSING) { - return NGHTTP2_ERR_STREAM_CLOSING; - } - return NGHTTP2_ERR_INVALID_STREAM_STATE; -} - -/* - * This function checks HEADERS for reserved stream can be sent. The - * |stream| must be reserved state and the |session| is server side. - * The |stream| can be NULL. - * - * This function returns 0 if it succeeds, or one of the following - * error codes: - * - * NGHTTP2_ERR_STREAM_CLOSED - * The stream is already closed. - * NGHTTP2_ERR_STREAM_SHUT_WR - * The stream is half-closed for transmission. - * NGHTTP2_ERR_PROTO - * The stream is not reserved state - * NGHTTP2_ERR_STREAM_CLOSED - * RST_STREAM was queued for this stream. - * NGHTTP2_ERR_SESSION_CLOSING - * This session is closing. - */ -static int -session_predicate_push_response_headers_send(nghttp2_session *session, - nghttp2_stream *stream) { - int rv; - /* TODO Should disallow HEADERS if GOAWAY has already been issued? */ - rv = session_predicate_for_stream_send(session, stream); - if (rv != 0) { - return rv; - } - assert(stream); - if (stream->state != NGHTTP2_STREAM_RESERVED) { - return NGHTTP2_ERR_PROTO; - } - if (stream->state == NGHTTP2_STREAM_CLOSING) { - return NGHTTP2_ERR_STREAM_CLOSING; - } - return 0; -} - -/* - * This function checks HEADERS, which is neither stream-opening nor - * first response header, with the |stream| can be sent at this time. - * The |stream| can be NULL. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP2_ERR_STREAM_CLOSED - * The stream is already closed or does not exist. - * NGHTTP2_ERR_STREAM_SHUT_WR - * The transmission is not allowed for this stream (e.g., a frame - * with END_STREAM flag set has already sent) - * NGHTTP2_ERR_STREAM_CLOSING - * RST_STREAM was queued for this stream. - * NGHTTP2_ERR_INVALID_STREAM_STATE - * The state of the stream is not valid. - * NGHTTP2_ERR_SESSION_CLOSING - * This session is closing. - */ -static int session_predicate_headers_send(nghttp2_session *session, - nghttp2_stream *stream) { - int rv; - rv = session_predicate_for_stream_send(session, stream); - if (rv != 0) { - return rv; - } - assert(stream); - if (nghttp2_session_is_my_stream_id(session, stream->stream_id)) { - if (stream->state == NGHTTP2_STREAM_CLOSING) { - return NGHTTP2_ERR_STREAM_CLOSING; - } - return 0; - } - if (stream->state == NGHTTP2_STREAM_OPENED) { - return 0; - } - if (stream->state == NGHTTP2_STREAM_CLOSING) { - return NGHTTP2_ERR_STREAM_CLOSING; - } - return NGHTTP2_ERR_INVALID_STREAM_STATE; -} - -/* - * This function checks PUSH_PROMISE frame |frame| with the |stream| - * can be sent at this time. The |stream| can be NULL. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP2_ERR_START_STREAM_NOT_ALLOWED - * New stream cannot be created because GOAWAY is already sent or - * received. - * NGHTTP2_ERR_PROTO - * The client side attempts to send PUSH_PROMISE, or the server - * sends PUSH_PROMISE for the stream not initiated by the client. - * NGHTTP2_ERR_STREAM_CLOSED - * The stream is already closed or does not exist. - * NGHTTP2_ERR_STREAM_CLOSING - * RST_STREAM was queued for this stream. - * NGHTTP2_ERR_STREAM_SHUT_WR - * The transmission is not allowed for this stream (e.g., a frame - * with END_STREAM flag set has already sent) - * NGHTTP2_ERR_PUSH_DISABLED - * The remote peer disabled reception of PUSH_PROMISE. - * NGHTTP2_ERR_SESSION_CLOSING - * This session is closing. - */ -static int session_predicate_push_promise_send(nghttp2_session *session, - nghttp2_stream *stream) { - int rv; - - if (!session->server) { - return NGHTTP2_ERR_PROTO; - } - - rv = session_predicate_for_stream_send(session, stream); - if (rv != 0) { - return rv; - } - - assert(stream); - - if (session->remote_settings.enable_push == 0) { - return NGHTTP2_ERR_PUSH_DISABLED; - } - if (stream->state == NGHTTP2_STREAM_CLOSING) { - return NGHTTP2_ERR_STREAM_CLOSING; - } - if (session->goaway_flags & NGHTTP2_GOAWAY_RECV) { - return NGHTTP2_ERR_START_STREAM_NOT_ALLOWED; - } - return 0; -} - -/* - * This function checks WINDOW_UPDATE with the stream ID |stream_id| - * can be sent at this time. Note that END_STREAM flag of the previous - * frame does not affect the transmission of the WINDOW_UPDATE frame. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP2_ERR_STREAM_CLOSED - * The stream is already closed or does not exist. - * NGHTTP2_ERR_STREAM_CLOSING - * RST_STREAM was queued for this stream. - * NGHTTP2_ERR_INVALID_STREAM_STATE - * The state of the stream is not valid. - * NGHTTP2_ERR_SESSION_CLOSING - * This session is closing. - */ -static int session_predicate_window_update_send(nghttp2_session *session, - int32_t stream_id) { - nghttp2_stream *stream; - if (stream_id == 0) { - /* Connection-level window update */ - return 0; - } - stream = nghttp2_session_get_stream(session, stream_id); - if (stream == NULL) { - return NGHTTP2_ERR_STREAM_CLOSED; - } - if (session_is_closing(session)) { - return NGHTTP2_ERR_SESSION_CLOSING; - } - if (stream->state == NGHTTP2_STREAM_CLOSING) { - return NGHTTP2_ERR_STREAM_CLOSING; - } - if (state_reserved_local(session, stream)) { - return NGHTTP2_ERR_INVALID_STREAM_STATE; - } - return 0; -} - -/* Take into account settings max frame size and both connection-level - flow control here */ -static ssize_t -nghttp2_session_enforce_flow_control_limits(nghttp2_session *session, - nghttp2_stream *stream, - ssize_t requested_window_size) { - DEBUGF(fprintf(stderr, "send: remote windowsize connection=%d, " - "remote maxframsize=%u, stream(id %d)=%d\n", - session->remote_window_size, - session->remote_settings.max_frame_size, stream->stream_id, - stream->remote_window_size)); - - return nghttp2_min(nghttp2_min(nghttp2_min(requested_window_size, - stream->remote_window_size), - session->remote_window_size), - (int32_t)session->remote_settings.max_frame_size); -} - -/* - * Returns the maximum length of next data read. If the - * connection-level and/or stream-wise flow control are enabled, the - * return value takes into account those current window sizes. The remote - * settings for max frame size is also taken into account. - */ -static size_t nghttp2_session_next_data_read(nghttp2_session *session, - nghttp2_stream *stream) { - ssize_t window_size; - - window_size = nghttp2_session_enforce_flow_control_limits( - session, stream, NGHTTP2_DATA_PAYLOADLEN); - - DEBUGF(fprintf(stderr, "send: available window=%zd\n", window_size)); - - return window_size > 0 ? (size_t)window_size : 0; -} - -/* - * This function checks DATA with the |stream| can be sent at this - * time. The |stream| can be NULL. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP2_ERR_STREAM_CLOSED - * The stream is already closed or does not exist. - * NGHTTP2_ERR_STREAM_SHUT_WR - * The transmission is not allowed for this stream (e.g., a frame - * with END_STREAM flag set has already sent) - * NGHTTP2_ERR_STREAM_CLOSING - * RST_STREAM was queued for this stream. - * NGHTTP2_ERR_INVALID_STREAM_STATE - * The state of the stream is not valid. - * NGHTTP2_ERR_SESSION_CLOSING - * This session is closing. - */ -static int nghttp2_session_predicate_data_send(nghttp2_session *session, - nghttp2_stream *stream) { - int rv; - rv = session_predicate_for_stream_send(session, stream); - if (rv != 0) { - return rv; - } - assert(stream); - if (nghttp2_session_is_my_stream_id(session, stream->stream_id)) { - /* Request body data */ - /* If stream->state is NGHTTP2_STREAM_CLOSING, RST_STREAM was - queued but not yet sent. In this case, we won't send DATA - frames. */ - if (stream->state == NGHTTP2_STREAM_CLOSING) { - return NGHTTP2_ERR_STREAM_CLOSING; - } - if (stream->state == NGHTTP2_STREAM_RESERVED) { - return NGHTTP2_ERR_INVALID_STREAM_STATE; - } - return 0; - } - /* Response body data */ - if (stream->state == NGHTTP2_STREAM_OPENED) { - return 0; - } - if (stream->state == NGHTTP2_STREAM_CLOSING) { - return NGHTTP2_ERR_STREAM_CLOSING; - } - return NGHTTP2_ERR_INVALID_STREAM_STATE; -} - -static ssize_t session_call_select_padding(nghttp2_session *session, - const nghttp2_frame *frame, - size_t max_payloadlen) { - ssize_t rv; - - if (frame->hd.length >= max_payloadlen) { - return frame->hd.length; - } - - if (session->callbacks.select_padding_callback) { - size_t max_paddedlen; - - max_paddedlen = - nghttp2_min(frame->hd.length + NGHTTP2_MAX_PADLEN, max_payloadlen); - - rv = session->callbacks.select_padding_callback( - session, frame, max_paddedlen, session->user_data); - if (rv < (ssize_t)frame->hd.length || rv > (ssize_t)max_paddedlen) { - return NGHTTP2_ERR_CALLBACK_FAILURE; - } - return rv; - } - return frame->hd.length; -} - -/* Add padding to HEADERS or PUSH_PROMISE. We use - frame->headers.padlen in this function to use the fact that - frame->push_promise has also padlen in the same position. */ -static int session_headers_add_pad(nghttp2_session *session, - nghttp2_frame *frame) { - int rv; - ssize_t padded_payloadlen; - nghttp2_active_outbound_item *aob; - nghttp2_bufs *framebufs; - size_t padlen; - size_t max_payloadlen; - - aob = &session->aob; - framebufs = &aob->framebufs; - - max_payloadlen = nghttp2_min(NGHTTP2_MAX_PAYLOADLEN, - frame->hd.length + NGHTTP2_MAX_PADLEN); - - padded_payloadlen = - session_call_select_padding(session, frame, max_payloadlen); - - if (nghttp2_is_fatal((int)padded_payloadlen)) { - return (int)padded_payloadlen; - } - - padlen = padded_payloadlen - frame->hd.length; - - DEBUGF(fprintf(stderr, "send: padding selected: payloadlen=%zd, padlen=%zu\n", - padded_payloadlen, padlen)); - - rv = nghttp2_frame_add_pad(framebufs, &frame->hd, padlen, 0); - - if (rv != 0) { - return rv; - } - - frame->headers.padlen = padlen; - - return 0; -} - -static size_t session_estimate_headers_payload(nghttp2_session *session, - const nghttp2_nv *nva, - size_t nvlen, - size_t additional) { - return nghttp2_hd_deflate_bound(&session->hd_deflater, nva, nvlen) + - additional; -} - -/* - * This function serializes frame for transmission. - * - * This function returns 0 if it succeeds, or one of negative error - * codes, including both fatal and non-fatal ones. - */ -static int session_prep_frame(nghttp2_session *session, - nghttp2_outbound_item *item) { - int rv; - nghttp2_frame *frame; - nghttp2_mem *mem; - - mem = &session->mem; - frame = &item->frame; - - if (frame->hd.type != NGHTTP2_DATA) { - switch (frame->hd.type) { - case NGHTTP2_HEADERS: { - nghttp2_headers_aux_data *aux_data; - size_t estimated_payloadlen; - - aux_data = &item->aux_data.headers; - - estimated_payloadlen = session_estimate_headers_payload( - session, frame->headers.nva, frame->headers.nvlen, - NGHTTP2_PRIORITY_SPECLEN); - - if (estimated_payloadlen > NGHTTP2_MAX_HEADERSLEN) { - return NGHTTP2_ERR_FRAME_SIZE_ERROR; - } - - if (frame->headers.cat == NGHTTP2_HCAT_REQUEST) { - /* initial HEADERS, which opens stream */ - nghttp2_stream *stream; - - stream = nghttp2_session_open_stream( - session, frame->hd.stream_id, NGHTTP2_STREAM_FLAG_NONE, - &frame->headers.pri_spec, NGHTTP2_STREAM_INITIAL, - aux_data->stream_user_data); - - if (stream == NULL) { - return NGHTTP2_ERR_NOMEM; - } - - rv = session_predicate_request_headers_send(session, item); - if (rv != 0) { - return rv; - } - - if (session_enforce_http_messaging(session)) { - nghttp2_http_record_request_method(stream, frame); - } - } else { - nghttp2_stream *stream; - - stream = nghttp2_session_get_stream(session, frame->hd.stream_id); - - if (session_predicate_push_response_headers_send(session, stream) == - 0) { - frame->headers.cat = NGHTTP2_HCAT_PUSH_RESPONSE; - - if (aux_data->stream_user_data) { - stream->stream_user_data = aux_data->stream_user_data; - } - } else if (session_predicate_response_headers_send(session, stream) == - 0) { - frame->headers.cat = NGHTTP2_HCAT_RESPONSE; - } else { - frame->headers.cat = NGHTTP2_HCAT_HEADERS; - - rv = session_predicate_headers_send(session, stream); - - if (rv != 0) { - if (stream && stream->item == item) { - int rv2; - - rv2 = nghttp2_stream_detach_item(stream, session); - - if (nghttp2_is_fatal(rv2)) { - return rv2; - } - } - - return rv; - } - } - } - - rv = nghttp2_frame_pack_headers(&session->aob.framebufs, &frame->headers, - &session->hd_deflater); - - if (rv != 0) { - return rv; - } - - DEBUGF(fprintf(stderr, - "send: before padding, HEADERS serialized in %zd bytes\n", - nghttp2_bufs_len(&session->aob.framebufs))); - - rv = session_headers_add_pad(session, frame); - - if (rv != 0) { - return rv; - } - - DEBUGF(fprintf(stderr, "send: HEADERS finally serialized in %zd bytes\n", - nghttp2_bufs_len(&session->aob.framebufs))); - - break; - } - case NGHTTP2_PRIORITY: { - if (session_is_closing(session)) { - return NGHTTP2_ERR_SESSION_CLOSING; - } - /* PRIORITY frame can be sent at any time and to any stream - ID. */ - nghttp2_frame_pack_priority(&session->aob.framebufs, &frame->priority); - - /* Peer can send PRIORITY frame against idle stream to create - "anchor" in dependency tree. Only client can do this in - nghttp2. In nghttp2, only server retains non-active (closed - or idle) streams in memory, so we don't open stream here. */ - break; - } - case NGHTTP2_RST_STREAM: - if (session_is_closing(session)) { - return NGHTTP2_ERR_SESSION_CLOSING; - } - nghttp2_frame_pack_rst_stream(&session->aob.framebufs, - &frame->rst_stream); - break; - case NGHTTP2_SETTINGS: { - rv = nghttp2_frame_pack_settings(&session->aob.framebufs, - &frame->settings); - if (rv != 0) { - return rv; - } - break; - } - case NGHTTP2_PUSH_PROMISE: { - nghttp2_stream *stream; - nghttp2_headers_aux_data *aux_data; - nghttp2_priority_spec pri_spec; - size_t estimated_payloadlen; - - aux_data = &item->aux_data.headers; - - stream = nghttp2_session_get_stream(session, frame->hd.stream_id); - - /* stream could be NULL if associated stream was already - closed. */ - if (stream) { - nghttp2_priority_spec_init(&pri_spec, stream->stream_id, - NGHTTP2_DEFAULT_WEIGHT, 0); - } else { - nghttp2_priority_spec_default_init(&pri_spec); - } - - if (!nghttp2_session_open_stream( - session, frame->push_promise.promised_stream_id, - NGHTTP2_STREAM_FLAG_NONE, &pri_spec, NGHTTP2_STREAM_RESERVED, - aux_data->stream_user_data)) { - return NGHTTP2_ERR_NOMEM; - } - - estimated_payloadlen = session_estimate_headers_payload( - session, frame->push_promise.nva, frame->push_promise.nvlen, 0); - - if (estimated_payloadlen > NGHTTP2_MAX_HEADERSLEN) { - return NGHTTP2_ERR_FRAME_SIZE_ERROR; - } - - /* predicte should fail if stream is NULL. */ - rv = session_predicate_push_promise_send(session, stream); - if (rv != 0) { - return rv; - } - - assert(stream); - - rv = nghttp2_frame_pack_push_promise( - &session->aob.framebufs, &frame->push_promise, &session->hd_deflater); - if (rv != 0) { - return rv; - } - rv = session_headers_add_pad(session, frame); - if (rv != 0) { - return rv; - } - - break; - } - case NGHTTP2_PING: - if (session_is_closing(session)) { - return NGHTTP2_ERR_SESSION_CLOSING; - } - nghttp2_frame_pack_ping(&session->aob.framebufs, &frame->ping); - break; - case NGHTTP2_WINDOW_UPDATE: { - rv = session_predicate_window_update_send(session, frame->hd.stream_id); - if (rv != 0) { - return rv; - } - nghttp2_frame_pack_window_update(&session->aob.framebufs, - &frame->window_update); - break; - } - case NGHTTP2_GOAWAY: - rv = nghttp2_frame_pack_goaway(&session->aob.framebufs, &frame->goaway); - if (rv != 0) { - return rv; - } - session->local_last_stream_id = frame->goaway.last_stream_id; - - break; - default: - return NGHTTP2_ERR_INVALID_ARGUMENT; - } - return 0; - } else { - size_t next_readmax; - nghttp2_stream *stream; - - stream = nghttp2_session_get_stream(session, frame->hd.stream_id); - - if (stream) { - assert(stream->item == item); - } - - rv = nghttp2_session_predicate_data_send(session, stream); - if (rv != 0) { - if (stream) { - int rv2; - - rv2 = nghttp2_stream_detach_item(stream, session); - - if (nghttp2_is_fatal(rv2)) { - return rv2; - } - } - - return rv; - } - /* Assuming stream is not NULL */ - assert(stream); - next_readmax = nghttp2_session_next_data_read(session, stream); - - if (next_readmax == 0) { - - /* This must be true since we only pop DATA frame item from - queue when session->remote_window_size > 0 */ - assert(session->remote_window_size > 0); - - rv = nghttp2_stream_defer_item( - stream, NGHTTP2_STREAM_FLAG_DEFERRED_FLOW_CONTROL, session); - - if (nghttp2_is_fatal(rv)) { - return rv; - } - - session->aob.item = NULL; - active_outbound_item_reset(&session->aob, mem); - return NGHTTP2_ERR_DEFERRED; - } - - rv = nghttp2_session_pack_data(session, &session->aob.framebufs, - next_readmax, frame, &item->aux_data.data, - stream); - if (rv == NGHTTP2_ERR_DEFERRED) { - rv = nghttp2_stream_defer_item(stream, NGHTTP2_STREAM_FLAG_DEFERRED_USER, - session); - - if (nghttp2_is_fatal(rv)) { - return rv; - } - - session->aob.item = NULL; - active_outbound_item_reset(&session->aob, mem); - return NGHTTP2_ERR_DEFERRED; - } - if (rv == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) { - rv = nghttp2_stream_detach_item(stream, session); - - if (nghttp2_is_fatal(rv)) { - return rv; - } - - rv = nghttp2_session_add_rst_stream(session, frame->hd.stream_id, - NGHTTP2_INTERNAL_ERROR); - if (nghttp2_is_fatal(rv)) { - return rv; - } - return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; - } - if (rv != 0) { - int rv2; - - rv2 = nghttp2_stream_detach_item(stream, session); - - if (nghttp2_is_fatal(rv2)) { - return rv2; - } - - return rv; - } - return 0; - } -} - -nghttp2_outbound_item * -nghttp2_session_get_next_ob_item(nghttp2_session *session) { - if (nghttp2_outbound_queue_top(&session->ob_urgent)) { - return nghttp2_outbound_queue_top(&session->ob_urgent); - } - - if (nghttp2_outbound_queue_top(&session->ob_reg)) { - return nghttp2_outbound_queue_top(&session->ob_reg); - } - - if (!session_is_outgoing_concurrent_streams_max(session)) { - if (nghttp2_outbound_queue_top(&session->ob_syn)) { - return nghttp2_outbound_queue_top(&session->ob_syn); - } - } - - if (session->remote_window_size > 0 && - !nghttp2_pq_empty(&session->ob_da_pq)) { - return nghttp2_pq_top(&session->ob_da_pq); - } - - return NULL; -} - -nghttp2_outbound_item * -nghttp2_session_pop_next_ob_item(nghttp2_session *session) { - nghttp2_outbound_item *item; - - item = nghttp2_outbound_queue_top(&session->ob_urgent); - if (item) { - nghttp2_outbound_queue_pop(&session->ob_urgent); - item->queued = 0; - return item; - } - - item = nghttp2_outbound_queue_top(&session->ob_reg); - if (item) { - nghttp2_outbound_queue_pop(&session->ob_reg); - item->queued = 0; - return item; - } - - if (!session_is_outgoing_concurrent_streams_max(session)) { - item = nghttp2_outbound_queue_top(&session->ob_syn); - if (item) { - nghttp2_outbound_queue_pop(&session->ob_syn); - item->queued = 0; - return item; - } - } - - if (session->remote_window_size > 0 && - !nghttp2_pq_empty(&session->ob_da_pq)) { - item = nghttp2_pq_top(&session->ob_da_pq); - nghttp2_pq_pop(&session->ob_da_pq); - item->queued = 0; - return item; - } - - return NULL; -} - -static int session_call_before_frame_send(nghttp2_session *session, - nghttp2_frame *frame) { - int rv; - if (session->callbacks.before_frame_send_callback) { - rv = session->callbacks.before_frame_send_callback(session, frame, - session->user_data); - if (rv != 0) { - return NGHTTP2_ERR_CALLBACK_FAILURE; - } - } - return 0; -} - -static int session_call_on_frame_send(nghttp2_session *session, - nghttp2_frame *frame) { - int rv; - if (session->callbacks.on_frame_send_callback) { - rv = session->callbacks.on_frame_send_callback(session, frame, - session->user_data); - if (rv != 0) { - return NGHTTP2_ERR_CALLBACK_FAILURE; - } - } - return 0; -} - -static int find_stream_on_goaway_func(nghttp2_map_entry *entry, void *ptr) { - nghttp2_close_stream_on_goaway_arg *arg; - nghttp2_stream *stream; - - arg = (nghttp2_close_stream_on_goaway_arg *)ptr; - stream = (nghttp2_stream *)entry; - - if (nghttp2_session_is_my_stream_id(arg->session, stream->stream_id)) { - if (arg->incoming) { - return 0; - } - } else if (!arg->incoming) { - return 0; - } - - if (stream->state != NGHTTP2_STREAM_IDLE && - (stream->flags & NGHTTP2_STREAM_FLAG_CLOSED) == 0 && - stream->stream_id > arg->last_stream_id) { - /* We are collecting streams to close because we cannot call - nghttp2_session_close_stream() inside nghttp2_map_each(). - Reuse closed_next member.. bad choice? */ - assert(stream->closed_next == NULL); - assert(stream->closed_prev == NULL); - - if (arg->head) { - stream->closed_next = arg->head; - arg->head = stream; - } else { - arg->head = stream; - } - } - - return 0; -} - -/* Closes non-idle and non-closed streams whose stream ID > - last_stream_id. If incoming is nonzero, we are going to close - incoming streams. Otherwise, close outgoing streams. */ -static int session_close_stream_on_goaway(nghttp2_session *session, - int32_t last_stream_id, - int incoming) { - int rv; - nghttp2_stream *stream, *next_stream; - nghttp2_close_stream_on_goaway_arg arg = {session, NULL, last_stream_id, - incoming}; - - rv = nghttp2_map_each(&session->streams, find_stream_on_goaway_func, &arg); - assert(rv == 0); - - stream = arg.head; - while (stream) { - next_stream = stream->closed_next; - stream->closed_next = NULL; - rv = nghttp2_session_close_stream(session, stream->stream_id, - NGHTTP2_REFUSED_STREAM); - - /* stream may be deleted here */ - - stream = next_stream; - - if (nghttp2_is_fatal(rv)) { - /* Clean up closed_next member just in case */ - while (stream) { - next_stream = stream->closed_next; - stream->closed_next = NULL; - stream = next_stream; - } - return rv; - } - } - - return 0; -} - -static void session_outbound_item_schedule(nghttp2_session *session, - nghttp2_outbound_item *item, - int32_t weight) { - /* Schedule next write. Offset proportional to the write size. - Stream with heavier weight is scheduled earlier. */ - size_t delta = item->frame.hd.length * NGHTTP2_MAX_WEIGHT / weight; - - if (session->last_cycle < item->cycle) { - session->last_cycle = item->cycle; - } - - /* We pretend to ignore overflow given that the value range of - item->cycle, which is uint64_t. nghttp2 won't explode even when - overflow occurs, there might be some disturbance of priority. */ - item->cycle = session->last_cycle + delta; -} - -/* - * Called after a frame is sent. This function runs - * on_frame_send_callback and handles stream closure upon END_STREAM - * or RST_STREAM. This function does not reset session->aob. It is a - * responsibility of session_after_frame_sent2. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP2_ERR_NOMEM - * Out of memory. - * NGHTTP2_ERR_CALLBACK_FAILURE - * The callback function failed. - */ -static int session_after_frame_sent1(nghttp2_session *session) { - int rv; - nghttp2_active_outbound_item *aob = &session->aob; - nghttp2_outbound_item *item = aob->item; - nghttp2_bufs *framebufs = &aob->framebufs; - nghttp2_frame *frame; - - frame = &item->frame; - - if (frame->hd.type != NGHTTP2_DATA) { - - if (frame->hd.type == NGHTTP2_HEADERS || - frame->hd.type == NGHTTP2_PUSH_PROMISE) { - - if (nghttp2_bufs_next_present(framebufs)) { - DEBUGF(fprintf(stderr, "send: CONTINUATION exists, just return\n")); - return 0; - } - } - rv = session_call_on_frame_send(session, frame); - if (nghttp2_is_fatal(rv)) { - return rv; - } - switch (frame->hd.type) { - case NGHTTP2_HEADERS: { - nghttp2_headers_aux_data *aux_data; - nghttp2_stream *stream; - - stream = nghttp2_session_get_stream(session, frame->hd.stream_id); - if (!stream) { - break; - } - - if (stream->item == item) { - rv = nghttp2_stream_detach_item(stream, session); - - if (nghttp2_is_fatal(rv)) { - return rv; - } - } - - switch (frame->headers.cat) { - case NGHTTP2_HCAT_REQUEST: { - stream->state = NGHTTP2_STREAM_OPENING; - if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) { - nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_WR); - } - rv = nghttp2_session_close_stream_if_shut_rdwr(session, stream); - if (nghttp2_is_fatal(rv)) { - return rv; - } - /* We assume aux_data is a pointer to nghttp2_headers_aux_data */ - aux_data = &item->aux_data.headers; - if (aux_data->data_prd.read_callback) { - /* nghttp2_submit_data() makes a copy of aux_data->data_prd */ - rv = nghttp2_submit_data(session, NGHTTP2_FLAG_END_STREAM, - frame->hd.stream_id, &aux_data->data_prd); - if (nghttp2_is_fatal(rv)) { - return rv; - } - /* TODO nghttp2_submit_data() may fail if stream has already - DATA frame item. We might have to handle it here. */ - } - break; - } - case NGHTTP2_HCAT_PUSH_RESPONSE: - stream->flags &= ~NGHTTP2_STREAM_FLAG_PUSH; - ++session->num_outgoing_streams; - /* Fall through */ - case NGHTTP2_HCAT_RESPONSE: - stream->state = NGHTTP2_STREAM_OPENED; - /* Fall through */ - case NGHTTP2_HCAT_HEADERS: - if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) { - nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_WR); - } - rv = nghttp2_session_close_stream_if_shut_rdwr(session, stream); - if (nghttp2_is_fatal(rv)) { - return rv; - } - /* We assume aux_data is a pointer to nghttp2_headers_aux_data */ - aux_data = &item->aux_data.headers; - if (aux_data->data_prd.read_callback) { - rv = nghttp2_submit_data(session, NGHTTP2_FLAG_END_STREAM, - frame->hd.stream_id, &aux_data->data_prd); - if (nghttp2_is_fatal(rv)) { - return rv; - } - /* TODO nghttp2_submit_data() may fail if stream has already - DATA frame item. We might have to handle it here. */ - } - break; - } - break; - } - case NGHTTP2_PRIORITY: { - nghttp2_stream *stream; - - if (session->server) { - break; - } - - stream = nghttp2_session_get_stream_raw(session, frame->hd.stream_id); - - if (!stream) { - break; - } - - rv = nghttp2_session_reprioritize_stream(session, stream, - &frame->priority.pri_spec); - - if (nghttp2_is_fatal(rv)) { - return rv; - } - - break; - } - case NGHTTP2_RST_STREAM: - rv = nghttp2_session_close_stream(session, frame->hd.stream_id, - frame->rst_stream.error_code); - if (nghttp2_is_fatal(rv)) { - return rv; - } - break; - case NGHTTP2_GOAWAY: { - nghttp2_goaway_aux_data *aux_data; - - aux_data = &item->aux_data.goaway; - - if ((aux_data->flags & NGHTTP2_GOAWAY_AUX_SHUTDOWN_NOTICE) == 0) { - - if (aux_data->flags & NGHTTP2_GOAWAY_AUX_TERM_ON_SEND) { - session->goaway_flags |= NGHTTP2_GOAWAY_TERM_SENT; - } - - session->goaway_flags |= NGHTTP2_GOAWAY_SENT; - - rv = session_close_stream_on_goaway(session, - frame->goaway.last_stream_id, 1); - - if (nghttp2_is_fatal(rv)) { - return rv; - } - } - - break; - } - default: - break; - } - - return 0; - } else { - nghttp2_stream *stream; - nghttp2_data_aux_data *aux_data; - - aux_data = &item->aux_data.data; - - stream = nghttp2_session_get_stream(session, frame->hd.stream_id); - /* We update flow control window after a frame was completely - sent. This is possible because we choose payload length not to - exceed the window */ - session->remote_window_size -= frame->hd.length; - if (stream) { - stream->remote_window_size -= frame->hd.length; - } - - if (stream && aux_data->eof) { - rv = nghttp2_stream_detach_item(stream, session); - - if (nghttp2_is_fatal(rv)) { - return rv; - } - - /* Call on_frame_send_callback after - nghttp2_stream_detach_item(), so that application can issue - nghttp2_submit_data() in the callback. */ - if (session->callbacks.on_frame_send_callback) { - rv = session_call_on_frame_send(session, frame); - - if (nghttp2_is_fatal(rv)) { - return rv; - } - } - - if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) { - int stream_closed; - - stream_closed = - (stream->shut_flags & NGHTTP2_SHUT_RDWR) == NGHTTP2_SHUT_RDWR; - - nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_WR); - - rv = nghttp2_session_close_stream_if_shut_rdwr(session, stream); - if (nghttp2_is_fatal(rv)) { - return rv; - } - /* stream may be NULL if it was closed */ - if (stream_closed) { - stream = NULL; - } - } - return 0; - } - - if (session->callbacks.on_frame_send_callback) { - rv = session_call_on_frame_send(session, frame); - - if (nghttp2_is_fatal(rv)) { - return rv; - } - } - - return 0; - } - /* Unreachable */ - assert(0); - return 0; -} - -/* - * Called after a frame is sent and session_after_frame_sent1. This - * function is responsible to reset session->aob. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP2_ERR_NOMEM - * Out of memory. - * NGHTTP2_ERR_CALLBACK_FAILURE - * The callback function failed. - */ -static int session_after_frame_sent2(nghttp2_session *session) { - int rv; - nghttp2_active_outbound_item *aob = &session->aob; - nghttp2_outbound_item *item = aob->item; - nghttp2_bufs *framebufs = &aob->framebufs; - nghttp2_frame *frame; - nghttp2_mem *mem; - - mem = &session->mem; - frame = &item->frame; - - if (frame->hd.type != NGHTTP2_DATA) { - - if (frame->hd.type == NGHTTP2_HEADERS || - frame->hd.type == NGHTTP2_PUSH_PROMISE) { - - if (nghttp2_bufs_next_present(framebufs)) { - framebufs->cur = framebufs->cur->next; - - DEBUGF(fprintf(stderr, "send: next CONTINUATION frame, %zu bytes\n", - nghttp2_buf_len(&framebufs->cur->buf))); - - return 0; - } - } - - active_outbound_item_reset(&session->aob, mem); - - return 0; - } else { - nghttp2_outbound_item *next_item; - nghttp2_stream *stream; - nghttp2_data_aux_data *aux_data; - - aux_data = &item->aux_data.data; - - /* On EOF, we have already detached data. Please note that - application may issue nghttp2_submit_data() in - on_frame_send_callback (call from session_after_frame_sent1), - which attach data to stream. We don't want to detach it. */ - if (aux_data->eof) { - active_outbound_item_reset(aob, mem); - - return 0; - } - - /* Reset no_copy here because next write may not use this. */ - aux_data->no_copy = 0; - - stream = nghttp2_session_get_stream(session, frame->hd.stream_id); - - /* If session is closed or RST_STREAM was queued, we won't send - further data. */ - if (nghttp2_session_predicate_data_send(session, stream) != 0) { - if (stream) { - rv = nghttp2_stream_detach_item(stream, session); - - if (nghttp2_is_fatal(rv)) { - return rv; - } - } - - active_outbound_item_reset(aob, mem); - - return 0; - } - - /* Assuming stream is not NULL */ - assert(stream); - next_item = nghttp2_session_get_next_ob_item(session); - - /* If priority of this stream is higher or equal to other stream - waiting at the top of the queue, we continue to send this - data. */ - if (stream->dpri == NGHTTP2_STREAM_DPRI_TOP && - (next_item == NULL || (next_item->frame.hd.type == NGHTTP2_DATA && - outbound_item_less(item, next_item)))) { - size_t next_readmax; - - next_readmax = nghttp2_session_next_data_read(session, stream); - - if (next_readmax == 0) { - - if (session->remote_window_size == 0 && - stream->remote_window_size > 0) { - - /* If DATA cannot be sent solely due to connection level - window size, just push item to queue again. We never pop - DATA item while connection level window size is 0. */ - rv = nghttp2_pq_push(&session->ob_da_pq, aob->item); - - if (nghttp2_is_fatal(rv)) { - return rv; - } - - aob->item->queued = 1; - } else { - rv = nghttp2_stream_defer_item( - stream, NGHTTP2_STREAM_FLAG_DEFERRED_FLOW_CONTROL, session); - - if (nghttp2_is_fatal(rv)) { - return rv; - } - } - - aob->item = NULL; - active_outbound_item_reset(aob, mem); - - return 0; - } - - nghttp2_bufs_reset(framebufs); - - rv = nghttp2_session_pack_data(session, framebufs, next_readmax, frame, - aux_data, stream); - if (nghttp2_is_fatal(rv)) { - return rv; - } - if (rv == NGHTTP2_ERR_DEFERRED) { - rv = nghttp2_stream_defer_item( - stream, NGHTTP2_STREAM_FLAG_DEFERRED_USER, session); - - if (nghttp2_is_fatal(rv)) { - return rv; - } - - aob->item = NULL; - active_outbound_item_reset(aob, mem); - - return 0; - } - - if (rv == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) { - /* Stop DATA frame chain and issue RST_STREAM to close the - stream. We don't return - NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE intentionally. */ - rv = nghttp2_session_add_rst_stream(session, frame->hd.stream_id, - NGHTTP2_INTERNAL_ERROR); - - if (nghttp2_is_fatal(rv)) { - return rv; - } - - rv = nghttp2_stream_detach_item(stream, session); - - if (nghttp2_is_fatal(rv)) { - return rv; - } - - active_outbound_item_reset(aob, mem); - - return 0; - } - assert(rv == 0); - - if (aux_data->no_copy) { - aob->state = NGHTTP2_OB_SEND_NO_COPY; - } else { - aob->state = NGHTTP2_OB_SEND_DATA; - } - - return 0; - } - - if (stream->dpri == NGHTTP2_STREAM_DPRI_TOP) { - rv = nghttp2_pq_push(&session->ob_da_pq, aob->item); - - if (nghttp2_is_fatal(rv)) { - return rv; - } - - aob->item->queued = 1; - } - - aob->item = NULL; - active_outbound_item_reset(&session->aob, mem); - return 0; - } - /* Unreachable */ - assert(0); - return 0; -} - -static int session_call_send_data(nghttp2_session *session, - nghttp2_outbound_item *item, - nghttp2_bufs *framebufs) { - int rv; - nghttp2_buf *buf; - size_t length; - nghttp2_frame *frame; - nghttp2_data_aux_data *aux_data; - - buf = &framebufs->cur->buf; - frame = &item->frame; - length = frame->hd.length - frame->data.padlen; - aux_data = &item->aux_data.data; - - rv = session->callbacks.send_data_callback(session, frame, buf->pos, length, - &aux_data->data_prd.source, - session->user_data); - - if (rv == 0 || rv == NGHTTP2_ERR_WOULDBLOCK || - rv == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) { - return rv; - } - - return NGHTTP2_ERR_CALLBACK_FAILURE; -} - -static ssize_t nghttp2_session_mem_send_internal(nghttp2_session *session, - const uint8_t **data_ptr, - int fast_cb) { - int rv; - nghttp2_active_outbound_item *aob; - nghttp2_bufs *framebufs; - nghttp2_mem *mem; - - mem = &session->mem; - aob = &session->aob; - framebufs = &aob->framebufs; - - *data_ptr = NULL; - for (;;) { - switch (aob->state) { - case NGHTTP2_OB_POP_ITEM: { - nghttp2_outbound_item *item; - - item = nghttp2_session_pop_next_ob_item(session); - if (item == NULL) { - return 0; - } - - if (item->frame.hd.type == NGHTTP2_DATA || - item->frame.hd.type == NGHTTP2_HEADERS) { - nghttp2_frame *frame; - nghttp2_stream *stream; - - frame = &item->frame; - stream = nghttp2_session_get_stream(session, frame->hd.stream_id); - - if (stream && item == stream->item && - stream->dpri != NGHTTP2_STREAM_DPRI_TOP) { - /* We have DATA with higher priority in queue within the - same dependency tree. */ - break; - } - } - - rv = session_prep_frame(session, item); - if (rv == NGHTTP2_ERR_DEFERRED) { - DEBUGF(fprintf(stderr, "send: frame transmission deferred\n")); - break; - } - if (rv < 0) { - int32_t opened_stream_id = 0; - uint32_t error_code = NGHTTP2_INTERNAL_ERROR; - - DEBUGF(fprintf(stderr, "send: frame preparation failed with %s\n", - nghttp2_strerror(rv))); - /* TODO If the error comes from compressor, the connection - must be closed. */ - if (item->frame.hd.type != NGHTTP2_DATA && - session->callbacks.on_frame_not_send_callback && is_non_fatal(rv)) { - nghttp2_frame *frame = &item->frame; - /* The library is responsible for the transmission of - WINDOW_UPDATE frame, so we don't call error callback for - it. */ - if (frame->hd.type != NGHTTP2_WINDOW_UPDATE && - session->callbacks.on_frame_not_send_callback( - session, frame, rv, session->user_data) != 0) { - - nghttp2_outbound_item_free(item, mem); - nghttp2_mem_free(mem, item); - - return NGHTTP2_ERR_CALLBACK_FAILURE; - } - } - /* We have to close stream opened by failed request HEADERS - or PUSH_PROMISE. */ - switch (item->frame.hd.type) { - case NGHTTP2_HEADERS: - if (item->frame.headers.cat == NGHTTP2_HCAT_REQUEST) { - opened_stream_id = item->frame.hd.stream_id; - if (item->aux_data.headers.canceled) { - error_code = item->aux_data.headers.error_code; - } - } - break; - case NGHTTP2_PUSH_PROMISE: - opened_stream_id = item->frame.push_promise.promised_stream_id; - break; - } - if (opened_stream_id) { - /* careful not to override rv */ - int rv2; - rv2 = nghttp2_session_close_stream(session, opened_stream_id, - error_code); - - if (nghttp2_is_fatal(rv2)) { - return rv2; - } - } - - nghttp2_outbound_item_free(item, mem); - nghttp2_mem_free(mem, item); - active_outbound_item_reset(aob, mem); - - if (rv == NGHTTP2_ERR_HEADER_COMP) { - /* If header compression error occurred, should terminiate - connection. */ - rv = nghttp2_session_terminate_session(session, - NGHTTP2_INTERNAL_ERROR); - } - if (nghttp2_is_fatal(rv)) { - return rv; - } - break; - } - - aob->item = item; - - nghttp2_bufs_rewind(framebufs); - - if (item->frame.hd.type != NGHTTP2_DATA) { - nghttp2_frame *frame; - - frame = &item->frame; - - DEBUGF(fprintf(stderr, "send: next frame: payloadlen=%zu, type=%u, " - "flags=0x%02x, stream_id=%d\n", - frame->hd.length, frame->hd.type, frame->hd.flags, - frame->hd.stream_id)); - - rv = session_call_before_frame_send(session, frame); - if (nghttp2_is_fatal(rv)) { - return rv; - } - } else { - DEBUGF(fprintf(stderr, "send: next frame: DATA\n")); - - if (item->aux_data.data.no_copy) { - aob->state = NGHTTP2_OB_SEND_NO_COPY; - break; - } - } - - DEBUGF(fprintf(stderr, - "send: start transmitting frame type=%u, length=%zd\n", - framebufs->cur->buf.pos[3], - framebufs->cur->buf.last - framebufs->cur->buf.pos)); - - aob->state = NGHTTP2_OB_SEND_DATA; - - break; - } - case NGHTTP2_OB_SEND_DATA: { - size_t datalen; - nghttp2_buf *buf; - - buf = &framebufs->cur->buf; - - if (buf->pos == buf->last) { - DEBUGF(fprintf(stderr, "send: end transmission of a frame\n")); - - /* Frame has completely sent */ - if (fast_cb) { - rv = session_after_frame_sent2(session); - } else { - rv = session_after_frame_sent1(session); - if (rv < 0) { - /* FATAL */ - assert(nghttp2_is_fatal(rv)); - return rv; - } - rv = session_after_frame_sent2(session); - } - if (rv < 0) { - /* FATAL */ - assert(nghttp2_is_fatal(rv)); - return rv; - } - /* We have already adjusted the next state */ - break; - } - - *data_ptr = buf->pos; - datalen = nghttp2_buf_len(buf); - - /* We increment the offset here. If send_callback does not send - everything, we will adjust it. */ - buf->pos += datalen; - - return datalen; - } - case NGHTTP2_OB_SEND_NO_COPY: { - nghttp2_stream *stream; - nghttp2_frame *frame; - - DEBUGF(fprintf(stderr, "send: no copy DATA\n")); - - frame = &aob->item->frame; - - stream = nghttp2_session_get_stream(session, frame->hd.stream_id); - if (stream == NULL) { - DEBUGF(fprintf( - stderr, - "send: no copy DATA cancelled because stream was closed\n")); - - active_outbound_item_reset(aob, mem); - - break; - } - - rv = session_call_send_data(session, aob->item, framebufs); - if (nghttp2_is_fatal(rv)) { - return rv; - } - - if (rv == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) { - rv = nghttp2_stream_detach_item(stream, session); - - if (nghttp2_is_fatal(rv)) { - return rv; - } - - rv = nghttp2_session_add_rst_stream(session, frame->hd.stream_id, - NGHTTP2_INTERNAL_ERROR); - if (nghttp2_is_fatal(rv)) { - return rv; - } - - active_outbound_item_reset(aob, mem); - - break; - } - - if (rv == NGHTTP2_ERR_WOULDBLOCK) { - return 0; - } - - assert(rv == 0); - - rv = session_after_frame_sent1(session); - if (rv < 0) { - assert(nghttp2_is_fatal(rv)); - return rv; - } - rv = session_after_frame_sent2(session); - if (rv < 0) { - assert(nghttp2_is_fatal(rv)); - return rv; - } - - /* We have already adjusted the next state */ - - break; - } - case NGHTTP2_OB_SEND_CLIENT_MAGIC: { - size_t datalen; - nghttp2_buf *buf; - - buf = &framebufs->cur->buf; - - if (buf->pos == buf->last) { - DEBUGF(fprintf(stderr, "send: end transmission of client magic\n")); - active_outbound_item_reset(aob, mem); - break; - } - - *data_ptr = buf->pos; - datalen = nghttp2_buf_len(buf); - - buf->pos += datalen; - - return datalen; - } - } - } -} - -ssize_t nghttp2_session_mem_send(nghttp2_session *session, - const uint8_t **data_ptr) { - int rv; - ssize_t len; - - len = nghttp2_session_mem_send_internal(session, data_ptr, 1); - if (len <= 0) { - return len; - } - - if (session->aob.item) { - /* We have to call session_after_frame_sent1 here to handle stream - closure upon transmission of frames. Otherwise, END_STREAM may - be reached to client before we call nghttp2_session_mem_send - again and we may get exceeding number of incoming streams. */ - rv = session_after_frame_sent1(session); - if (rv < 0) { - assert(nghttp2_is_fatal(rv)); - return (ssize_t)rv; - } - } - - return len; -} - -int nghttp2_session_send(nghttp2_session *session) { - const uint8_t *data; - ssize_t datalen; - ssize_t sentlen; - nghttp2_bufs *framebufs; - - framebufs = &session->aob.framebufs; - - for (;;) { - datalen = nghttp2_session_mem_send_internal(session, &data, 0); - if (datalen <= 0) { - return (int)datalen; - } - sentlen = session->callbacks.send_callback(session, data, datalen, 0, - session->user_data); - if (sentlen < 0) { - if (sentlen == NGHTTP2_ERR_WOULDBLOCK) { - /* Transmission canceled. Rewind the offset */ - framebufs->cur->buf.pos -= datalen; - - return 0; - } - return NGHTTP2_ERR_CALLBACK_FAILURE; - } - /* Rewind the offset to the amount of unsent bytes */ - framebufs->cur->buf.pos -= datalen - sentlen; - } -} - -static ssize_t session_recv(nghttp2_session *session, uint8_t *buf, - size_t len) { - ssize_t rv; - rv = session->callbacks.recv_callback(session, buf, len, 0, - session->user_data); - if (rv > 0) { - if ((size_t)rv > len) { - return NGHTTP2_ERR_CALLBACK_FAILURE; - } - } else if (rv < 0 && rv != NGHTTP2_ERR_WOULDBLOCK && rv != NGHTTP2_ERR_EOF) { - return NGHTTP2_ERR_CALLBACK_FAILURE; - } - return rv; -} - -static int session_call_on_begin_frame(nghttp2_session *session, - const nghttp2_frame_hd *hd) { - int rv; - - if (session->callbacks.on_begin_frame_callback) { - - rv = session->callbacks.on_begin_frame_callback(session, hd, - session->user_data); - - if (rv != 0) { - return NGHTTP2_ERR_CALLBACK_FAILURE; - } - } - - return 0; -} - -static int session_call_on_frame_received(nghttp2_session *session, - nghttp2_frame *frame) { - int rv; - if (session->callbacks.on_frame_recv_callback) { - rv = session->callbacks.on_frame_recv_callback(session, frame, - session->user_data); - if (rv != 0) { - return NGHTTP2_ERR_CALLBACK_FAILURE; - } - } - return 0; -} - -static int session_call_on_begin_headers(nghttp2_session *session, - nghttp2_frame *frame) { - int rv; - DEBUGF(fprintf(stderr, "recv: call on_begin_headers callback stream_id=%d\n", - frame->hd.stream_id)); - if (session->callbacks.on_begin_headers_callback) { - rv = session->callbacks.on_begin_headers_callback(session, frame, - session->user_data); - if (rv == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) { - return rv; - } - if (rv != 0) { - return NGHTTP2_ERR_CALLBACK_FAILURE; - } - } - return 0; -} - -static int session_call_on_header(nghttp2_session *session, - const nghttp2_frame *frame, - const nghttp2_nv *nv) { - int rv; - if (session->callbacks.on_header_callback) { - rv = session->callbacks.on_header_callback( - session, frame, nv->name, nv->namelen, nv->value, nv->valuelen, - nv->flags, session->user_data); - if (rv == NGHTTP2_ERR_PAUSE || - rv == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) { - return rv; - } - if (rv != 0) { - return NGHTTP2_ERR_CALLBACK_FAILURE; - } - } - return 0; -} - -/* - * Handles frame size error. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP2_ERR_NOMEM - * Out of memory. - */ -static int session_handle_frame_size_error(nghttp2_session *session, - nghttp2_frame *frame _U_) { - /* TODO Currently no callback is called for this error, because we - call this callback before reading any payload */ - return nghttp2_session_terminate_session(session, NGHTTP2_FRAME_SIZE_ERROR); -} - -static int get_error_code_from_lib_error_code(int lib_error_code) { - switch (lib_error_code) { - case NGHTTP2_ERR_STREAM_CLOSED: - return NGHTTP2_STREAM_CLOSED; - case NGHTTP2_ERR_HEADER_COMP: - return NGHTTP2_COMPRESSION_ERROR; - case NGHTTP2_ERR_FRAME_SIZE_ERROR: - return NGHTTP2_FRAME_SIZE_ERROR; - case NGHTTP2_ERR_FLOW_CONTROL: - return NGHTTP2_FLOW_CONTROL_ERROR; - case NGHTTP2_ERR_REFUSED_STREAM: - return NGHTTP2_REFUSED_STREAM; - case NGHTTP2_ERR_PROTO: - case NGHTTP2_ERR_HTTP_HEADER: - case NGHTTP2_ERR_HTTP_MESSAGING: - return NGHTTP2_PROTOCOL_ERROR; - default: - return NGHTTP2_INTERNAL_ERROR; - } -} - -static int session_handle_invalid_stream2(nghttp2_session *session, - int32_t stream_id, - nghttp2_frame *frame, - int lib_error_code) { - int rv; - rv = nghttp2_session_add_rst_stream( - session, stream_id, get_error_code_from_lib_error_code(lib_error_code)); - if (rv != 0) { - return rv; - } - if (session->callbacks.on_invalid_frame_recv_callback) { - if (session->callbacks.on_invalid_frame_recv_callback( - session, frame, lib_error_code, session->user_data) != 0) { - return NGHTTP2_ERR_CALLBACK_FAILURE; - } - } - return 0; -} - -static int session_handle_invalid_stream(nghttp2_session *session, - nghttp2_frame *frame, - int lib_error_code) { - return session_handle_invalid_stream2(session, frame->hd.stream_id, frame, - lib_error_code); -} - -static int session_inflate_handle_invalid_stream(nghttp2_session *session, - nghttp2_frame *frame, - int lib_error_code) { - int rv; - rv = session_handle_invalid_stream(session, frame, lib_error_code); - if (nghttp2_is_fatal(rv)) { - return rv; - } - return NGHTTP2_ERR_IGN_HEADER_BLOCK; -} - -/* - * Handles invalid frame which causes connection error. - */ -static int session_handle_invalid_connection(nghttp2_session *session, - nghttp2_frame *frame, - int lib_error_code, - const char *reason) { - if (session->callbacks.on_invalid_frame_recv_callback) { - if (session->callbacks.on_invalid_frame_recv_callback( - session, frame, lib_error_code, session->user_data) != 0) { - return NGHTTP2_ERR_CALLBACK_FAILURE; - } - } - return nghttp2_session_terminate_session_with_reason( - session, get_error_code_from_lib_error_code(lib_error_code), reason); -} - -static int session_inflate_handle_invalid_connection(nghttp2_session *session, - nghttp2_frame *frame, - int lib_error_code, - const char *reason) { - int rv; - rv = - session_handle_invalid_connection(session, frame, lib_error_code, reason); - if (nghttp2_is_fatal(rv)) { - return rv; - } - return NGHTTP2_ERR_IGN_HEADER_BLOCK; -} - -/* - * Inflates header block in the memory pointed by |in| with |inlen| - * bytes. If this function returns NGHTTP2_ERR_PAUSE, the caller must - * call this function again, until it returns 0 or one of negative - * error code. If |call_header_cb| is zero, the on_header_callback - * are not invoked and the function never return NGHTTP2_ERR_PAUSE. If - * the given |in| is the last chunk of header block, the |final| must - * be nonzero. If header block is successfully processed (which is - * indicated by the return value 0, NGHTTP2_ERR_PAUSE or - * NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE), the number of processed - * input bytes is assigned to the |*readlen_ptr|. - * - * This function return 0 if it succeeds, or one of the negative error - * codes: - * - * NGHTTP2_ERR_CALLBACK_FAILURE - * The callback function failed. - * NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE - * The callback returns this error code, indicating that this - * stream should be RST_STREAMed. - * NGHTTP2_ERR_NOMEM - * Out of memory. - * NGHTTP2_ERR_PAUSE - * The callback function returned NGHTTP2_ERR_PAUSE - * NGHTTP2_ERR_HEADER_COMP - * Header decompression failed - */ -static int inflate_header_block(nghttp2_session *session, nghttp2_frame *frame, - size_t *readlen_ptr, uint8_t *in, size_t inlen, - int final, int call_header_cb) { - ssize_t proclen; - int rv; - int inflate_flags; - nghttp2_nv nv; - nghttp2_stream *stream; - nghttp2_stream *subject_stream; - int trailer = 0; - int token; - - *readlen_ptr = 0; - stream = nghttp2_session_get_stream(session, frame->hd.stream_id); - - if (frame->hd.type == NGHTTP2_PUSH_PROMISE) { - subject_stream = nghttp2_session_get_stream( - session, frame->push_promise.promised_stream_id); - } else { - subject_stream = stream; - trailer = session_trailer_headers(session, stream, frame); - } - - DEBUGF(fprintf(stderr, "recv: decoding header block %zu bytes\n", inlen)); - for (;;) { - inflate_flags = 0; - proclen = nghttp2_hd_inflate_hd2(&session->hd_inflater, &nv, &inflate_flags, - &token, in, inlen, final); - if (nghttp2_is_fatal((int)proclen)) { - return (int)proclen; - } - if (proclen < 0) { - if (session->iframe.state == NGHTTP2_IB_READ_HEADER_BLOCK) { - if (subject_stream && subject_stream->state != NGHTTP2_STREAM_CLOSING) { - /* Adding RST_STREAM here is very important. It prevents - from invoking subsequent callbacks for the same stream - ID. */ - rv = nghttp2_session_add_rst_stream( - session, subject_stream->stream_id, NGHTTP2_COMPRESSION_ERROR); - - if (nghttp2_is_fatal(rv)) { - return rv; - } - } - } - rv = - nghttp2_session_terminate_session(session, NGHTTP2_COMPRESSION_ERROR); - if (nghttp2_is_fatal(rv)) { - return rv; - } - - return NGHTTP2_ERR_HEADER_COMP; - } - in += proclen; - inlen -= proclen; - *readlen_ptr += proclen; - - DEBUGF(fprintf(stderr, "recv: proclen=%zd\n", proclen)); - - if (call_header_cb && (inflate_flags & NGHTTP2_HD_INFLATE_EMIT)) { - rv = 0; - if (subject_stream && session_enforce_http_messaging(session)) { - rv = nghttp2_http_on_header(session, subject_stream, frame, &nv, token, - trailer); - if (rv == NGHTTP2_ERR_HTTP_HEADER) { - DEBUGF(fprintf( - stderr, "recv: HTTP error: type=%d, id=%d, header %.*s: %.*s\n", - frame->hd.type, subject_stream->stream_id, (int)nv.namelen, - nv.name, (int)nv.valuelen, nv.value)); - - rv = - session_handle_invalid_stream2(session, subject_stream->stream_id, - frame, NGHTTP2_ERR_HTTP_HEADER); - if (nghttp2_is_fatal(rv)) { - return rv; - } - return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; - } - - if (rv == NGHTTP2_ERR_IGN_HTTP_HEADER) { - /* header is ignored */ - DEBUGF(fprintf( - stderr, "recv: HTTP ignored: type=%d, id=%d, header %.*s: %.*s\n", - frame->hd.type, subject_stream->stream_id, (int)nv.namelen, - nv.name, (int)nv.valuelen, nv.value)); - } - } - if (rv == 0) { - rv = session_call_on_header(session, frame, &nv); - /* This handles NGHTTP2_ERR_PAUSE and - NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE as well */ - if (rv != 0) { - return rv; - } - } - } - if (inflate_flags & NGHTTP2_HD_INFLATE_FINAL) { - nghttp2_hd_inflate_end_headers(&session->hd_inflater); - break; - } - if ((inflate_flags & NGHTTP2_HD_INFLATE_EMIT) == 0 && inlen == 0) { - break; - } - } - return 0; -} - -/* - * Decompress header blocks of incoming request HEADERS and also call - * additional callbacks. This function can be called again if this - * function returns NGHTTP2_ERR_PAUSE. - * - * This function returns 0 if it succeeds, or one of negative error - * codes: - * - * NGHTTP2_ERR_CALLBACK_FAILURE - * The callback function failed. - * NGHTTP2_ERR_NOMEM - * Out of memory. - */ -int nghttp2_session_end_request_headers_received(nghttp2_session *session _U_, - nghttp2_frame *frame, - nghttp2_stream *stream) { - if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) { - nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_RD); - } - /* Here we assume that stream is not shutdown in NGHTTP2_SHUT_WR */ - return 0; -} - -/* - * Decompress header blocks of incoming (push-)response HEADERS and - * also call additional callbacks. This function can be called again - * if this function returns NGHTTP2_ERR_PAUSE. - * - * This function returns 0 if it succeeds, or one of negative error - * codes: - * - * NGHTTP2_ERR_CALLBACK_FAILURE - * The callback function failed. - * NGHTTP2_ERR_NOMEM - * Out of memory. - */ -int nghttp2_session_end_response_headers_received(nghttp2_session *session, - nghttp2_frame *frame, - nghttp2_stream *stream) { - int rv; - if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) { - /* This is the last frame of this stream, so disallow - further receptions. */ - nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_RD); - rv = nghttp2_session_close_stream_if_shut_rdwr(session, stream); - if (nghttp2_is_fatal(rv)) { - return rv; - } - } - return 0; -} - -/* - * Decompress header blocks of incoming HEADERS and also call - * additional callbacks. This function can be called again if this - * function returns NGHTTP2_ERR_PAUSE. - * - * This function returns 0 if it succeeds, or one of negative error - * codes: - * - * NGHTTP2_ERR_CALLBACK_FAILURE - * The callback function failed. - * NGHTTP2_ERR_NOMEM - * Out of memory. - */ -int nghttp2_session_end_headers_received(nghttp2_session *session, - nghttp2_frame *frame, - nghttp2_stream *stream) { - int rv; - if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) { - if (!nghttp2_session_is_my_stream_id(session, frame->hd.stream_id)) { - } - nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_RD); - rv = nghttp2_session_close_stream_if_shut_rdwr(session, stream); - if (nghttp2_is_fatal(rv)) { - return rv; - } - } - return 0; -} - -static int session_after_header_block_received(nghttp2_session *session) { - int rv = 0; - int call_cb = 1; - nghttp2_frame *frame = &session->iframe.frame; - nghttp2_stream *stream; - - /* We don't call on_frame_recv_callback if stream has been closed - already or being closed. */ - stream = nghttp2_session_get_stream(session, frame->hd.stream_id); - if (!stream || stream->state == NGHTTP2_STREAM_CLOSING) { - return 0; - } - - if (session_enforce_http_messaging(session)) { - if (frame->hd.type == NGHTTP2_PUSH_PROMISE) { - nghttp2_stream *subject_stream; - - subject_stream = nghttp2_session_get_stream( - session, frame->push_promise.promised_stream_id); - if (subject_stream) { - rv = nghttp2_http_on_request_headers(subject_stream, frame); - } - } else { - assert(frame->hd.type == NGHTTP2_HEADERS); - switch (frame->headers.cat) { - case NGHTTP2_HCAT_REQUEST: - rv = nghttp2_http_on_request_headers(stream, frame); - break; - case NGHTTP2_HCAT_RESPONSE: - case NGHTTP2_HCAT_PUSH_RESPONSE: - rv = nghttp2_http_on_response_headers(stream); - break; - case NGHTTP2_HCAT_HEADERS: - if (stream->http_flags & NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE) { - assert(!session->server); - rv = nghttp2_http_on_response_headers(stream); - } else { - rv = nghttp2_http_on_trailer_headers(stream, frame); - } - break; - default: - assert(0); - } - if (rv == 0 && (frame->hd.flags & NGHTTP2_FLAG_END_STREAM)) { - rv = nghttp2_http_on_remote_end_stream(stream); - } - } - if (rv != 0) { - int32_t stream_id; - - if (frame->hd.type == NGHTTP2_PUSH_PROMISE) { - stream_id = frame->push_promise.promised_stream_id; - } else { - stream_id = frame->hd.stream_id; - } - - call_cb = 0; - - rv = session_handle_invalid_stream2(session, stream_id, frame, - NGHTTP2_ERR_HTTP_MESSAGING); - if (nghttp2_is_fatal(rv)) { - return rv; - } - } - } - - if (call_cb) { - rv = session_call_on_frame_received(session, frame); - if (nghttp2_is_fatal(rv)) { - return rv; - } - } - - if (frame->hd.type != NGHTTP2_HEADERS) { - return 0; - } - - switch (frame->headers.cat) { - case NGHTTP2_HCAT_REQUEST: - return nghttp2_session_end_request_headers_received(session, frame, stream); - case NGHTTP2_HCAT_RESPONSE: - case NGHTTP2_HCAT_PUSH_RESPONSE: - return nghttp2_session_end_response_headers_received(session, frame, - stream); - case NGHTTP2_HCAT_HEADERS: - return nghttp2_session_end_headers_received(session, frame, stream); - default: - assert(0); - } - return 0; -} - -int nghttp2_session_on_request_headers_received(nghttp2_session *session, - nghttp2_frame *frame) { - int rv = 0; - nghttp2_stream *stream; - if (frame->hd.stream_id == 0) { - return session_inflate_handle_invalid_connection( - session, frame, NGHTTP2_ERR_PROTO, "request HEADERS: stream_id == 0"); - } - - /* If client recieves idle stream from server, it is invalid - regardless stream ID is even or odd. This is because client is - not expected to receive request from server. */ - if (!session->server) { - if (session_detect_idle_stream(session, frame->hd.stream_id)) { - return session_inflate_handle_invalid_connection( - session, frame, NGHTTP2_ERR_PROTO, - "request HEADERS: client received request"); - } - - return NGHTTP2_ERR_IGN_HEADER_BLOCK; - } - - if (!session_is_new_peer_stream_id(session, frame->hd.stream_id)) { - /* The spec says if an endpoint receives a HEADERS with invalid - stream ID, it MUST issue connection error with error code - PROTOCOL_ERROR. But we could get trailer HEADERS after we have - sent RST_STREAM to this stream and peer have not received it. - Then connection error is too harsh. It means that we only use - connection error if stream ID refers idle stream. OTherwise we - just ignore HEADERS for now. */ - if (session_detect_idle_stream(session, frame->hd.stream_id)) { - return session_inflate_handle_invalid_connection( - session, frame, NGHTTP2_ERR_PROTO, - "request HEADERS: invalid stream_id"); - } - - return NGHTTP2_ERR_IGN_HEADER_BLOCK; - } - session->last_recv_stream_id = frame->hd.stream_id; - - if (session->goaway_flags & NGHTTP2_GOAWAY_SENT) { - /* We just ignore stream after GOAWAY was queued */ - return NGHTTP2_ERR_IGN_HEADER_BLOCK; - } - - if (session_is_incoming_concurrent_streams_max(session)) { - return session_inflate_handle_invalid_connection( - session, frame, NGHTTP2_ERR_PROTO, - "request HEADERS: max concurrent streams exceeded"); - } - - if (frame->headers.pri_spec.stream_id == frame->hd.stream_id) { - return session_inflate_handle_invalid_connection( - session, frame, NGHTTP2_ERR_PROTO, "request HEADERS: depend on itself"); - } - - if (session_is_incoming_concurrent_streams_pending_max(session)) { - return session_inflate_handle_invalid_stream(session, frame, - NGHTTP2_ERR_REFUSED_STREAM); - } - - stream = nghttp2_session_open_stream( - session, frame->hd.stream_id, NGHTTP2_STREAM_FLAG_NONE, - &frame->headers.pri_spec, NGHTTP2_STREAM_OPENING, NULL); - if (!stream) { - return NGHTTP2_ERR_NOMEM; - } - session->last_proc_stream_id = session->last_recv_stream_id; - rv = session_call_on_begin_headers(session, frame); - if (rv != 0) { - return rv; - } - return 0; -} - -int nghttp2_session_on_response_headers_received(nghttp2_session *session, - nghttp2_frame *frame, - nghttp2_stream *stream) { - int rv; - /* This function is only called if stream->state == - NGHTTP2_STREAM_OPENING and stream_id is local side initiated. */ - assert(stream->state == NGHTTP2_STREAM_OPENING && - nghttp2_session_is_my_stream_id(session, frame->hd.stream_id)); - if (frame->hd.stream_id == 0) { - return session_inflate_handle_invalid_connection( - session, frame, NGHTTP2_ERR_PROTO, "response HEADERS: stream_id == 0"); - } - if (stream->shut_flags & NGHTTP2_SHUT_RD) { - /* half closed (remote): from the spec: - - If an endpoint receives additional frames for a stream that is - in this state it MUST respond with a stream error (Section - 5.4.2) of type STREAM_CLOSED. - */ - return session_inflate_handle_invalid_stream(session, frame, - NGHTTP2_ERR_STREAM_CLOSED); - } - stream->state = NGHTTP2_STREAM_OPENED; - rv = session_call_on_begin_headers(session, frame); - if (rv != 0) { - return rv; - } - return 0; -} - -int nghttp2_session_on_push_response_headers_received(nghttp2_session *session, - nghttp2_frame *frame, - nghttp2_stream *stream) { - int rv = 0; - assert(stream->state == NGHTTP2_STREAM_RESERVED); - if (frame->hd.stream_id == 0) { - return session_inflate_handle_invalid_connection( - session, frame, NGHTTP2_ERR_PROTO, - "push response HEADERS: stream_id == 0"); - } - if (session->goaway_flags) { - /* We don't accept new stream after GOAWAY is sent or received. */ - return NGHTTP2_ERR_IGN_HEADER_BLOCK; - } - - if (session_is_incoming_concurrent_streams_max(session)) { - return session_inflate_handle_invalid_connection( - session, frame, NGHTTP2_ERR_PROTO, - "push response HEADERS: max concurrent streams exceeded"); - } - if (session_is_incoming_concurrent_streams_pending_max(session)) { - return session_inflate_handle_invalid_stream(session, frame, - NGHTTP2_ERR_REFUSED_STREAM); - } - - nghttp2_stream_promise_fulfilled(stream); - ++session->num_incoming_streams; - rv = session_call_on_begin_headers(session, frame); - if (rv != 0) { - return rv; - } - return 0; -} - -int nghttp2_session_on_headers_received(nghttp2_session *session, - nghttp2_frame *frame, - nghttp2_stream *stream) { - int rv = 0; - if (frame->hd.stream_id == 0) { - return session_inflate_handle_invalid_connection( - session, frame, NGHTTP2_ERR_PROTO, "HEADERS: stream_id == 0"); - } - if (stream->state == NGHTTP2_STREAM_RESERVED) { - /* reserved. The valid push response HEADERS is processed by - nghttp2_session_on_push_response_headers_received(). This - generic HEADERS is called invalid cases for HEADERS against - reserved state. */ - return session_inflate_handle_invalid_connection( - session, frame, NGHTTP2_ERR_PROTO, "HEADERS: stream in reserved"); - } - if ((stream->shut_flags & NGHTTP2_SHUT_RD)) { - /* half closed (remote): from the spec: - - If an endpoint receives additional frames for a stream that is - in this state it MUST respond with a stream error (Section - 5.4.2) of type STREAM_CLOSED. - */ - return session_inflate_handle_invalid_stream(session, frame, - NGHTTP2_ERR_STREAM_CLOSED); - } - if (nghttp2_session_is_my_stream_id(session, frame->hd.stream_id)) { - if (stream->state == NGHTTP2_STREAM_OPENED) { - rv = session_call_on_begin_headers(session, frame); - if (rv != 0) { - return rv; - } - return 0; - } else if (stream->state == NGHTTP2_STREAM_CLOSING) { - /* This is race condition. NGHTTP2_STREAM_CLOSING indicates - that we queued RST_STREAM but it has not been sent. It will - eventually sent, so we just ignore this frame. */ - return NGHTTP2_ERR_IGN_HEADER_BLOCK; - } else { - return session_inflate_handle_invalid_stream(session, frame, - NGHTTP2_ERR_PROTO); - } - } - /* If this is remote peer initiated stream, it is OK unless it - has sent END_STREAM frame already. But if stream is in - NGHTTP2_STREAM_CLOSING, we discard the frame. This is a race - condition. */ - if (stream->state != NGHTTP2_STREAM_CLOSING) { - rv = session_call_on_begin_headers(session, frame); - if (rv != 0) { - return rv; - } - return 0; - } - return NGHTTP2_ERR_IGN_HEADER_BLOCK; -} - -static int session_process_headers_frame(nghttp2_session *session) { - int rv; - nghttp2_inbound_frame *iframe = &session->iframe; - nghttp2_frame *frame = &iframe->frame; - nghttp2_stream *stream; - - rv = nghttp2_frame_unpack_headers_payload(&frame->headers, iframe->sbuf.pos, - nghttp2_buf_len(&iframe->sbuf)); - - if (rv != 0) { - return nghttp2_session_terminate_session_with_reason( - session, NGHTTP2_PROTOCOL_ERROR, "HEADERS: could not unpack"); - } - stream = nghttp2_session_get_stream(session, frame->hd.stream_id); - if (!stream) { - frame->headers.cat = NGHTTP2_HCAT_REQUEST; - return nghttp2_session_on_request_headers_received(session, frame); - } - - if (nghttp2_session_is_my_stream_id(session, frame->hd.stream_id)) { - if (stream->state == NGHTTP2_STREAM_OPENING) { - frame->headers.cat = NGHTTP2_HCAT_RESPONSE; - return nghttp2_session_on_response_headers_received(session, frame, - stream); - } - frame->headers.cat = NGHTTP2_HCAT_HEADERS; - return nghttp2_session_on_headers_received(session, frame, stream); - } - if (stream->state == NGHTTP2_STREAM_RESERVED) { - frame->headers.cat = NGHTTP2_HCAT_PUSH_RESPONSE; - return nghttp2_session_on_push_response_headers_received(session, frame, - stream); - } - frame->headers.cat = NGHTTP2_HCAT_HEADERS; - return nghttp2_session_on_headers_received(session, frame, stream); -} - -int nghttp2_session_on_priority_received(nghttp2_session *session, - nghttp2_frame *frame) { - int rv; - nghttp2_stream *stream; - - if (frame->hd.stream_id == 0) { - return session_handle_invalid_connection(session, frame, NGHTTP2_ERR_PROTO, - "PRIORITY: stream_id == 0"); - } - - if (!session->server) { - /* Re-prioritization works only in server */ - return session_call_on_frame_received(session, frame); - } - - stream = nghttp2_session_get_stream_raw(session, frame->hd.stream_id); - - if (!stream) { - /* PRIORITY against idle stream can create anchor node in - dependency tree. */ - if (!session_detect_idle_stream(session, frame->hd.stream_id)) { - return 0; - } - - stream = nghttp2_session_open_stream( - session, frame->hd.stream_id, NGHTTP2_STREAM_FLAG_NONE, - &frame->priority.pri_spec, NGHTTP2_STREAM_IDLE, NULL); - - if (stream == NULL) { - return NGHTTP2_ERR_NOMEM; - } - } else { - rv = nghttp2_session_reprioritize_stream(session, stream, - &frame->priority.pri_spec); - - if (nghttp2_is_fatal(rv)) { - return rv; - } - } - - return session_call_on_frame_received(session, frame); -} - -static int session_process_priority_frame(nghttp2_session *session) { - nghttp2_inbound_frame *iframe = &session->iframe; - nghttp2_frame *frame = &iframe->frame; - - nghttp2_frame_unpack_priority_payload(&frame->priority, iframe->sbuf.pos, - nghttp2_buf_len(&iframe->sbuf)); - - return nghttp2_session_on_priority_received(session, frame); -} - -int nghttp2_session_on_rst_stream_received(nghttp2_session *session, - nghttp2_frame *frame) { - int rv; - nghttp2_stream *stream; - if (frame->hd.stream_id == 0) { - return session_handle_invalid_connection(session, frame, NGHTTP2_ERR_PROTO, - "RST_STREAM: stream_id == 0"); - } - stream = nghttp2_session_get_stream(session, frame->hd.stream_id); - if (!stream) { - if (session_detect_idle_stream(session, frame->hd.stream_id)) { - return session_handle_invalid_connection( - session, frame, NGHTTP2_ERR_PROTO, "RST_STREAM: stream in idle"); - } - } - - rv = session_call_on_frame_received(session, frame); - if (rv != 0) { - return rv; - } - rv = nghttp2_session_close_stream(session, frame->hd.stream_id, - frame->rst_stream.error_code); - if (nghttp2_is_fatal(rv)) { - return rv; - } - return 0; -} - -static int session_process_rst_stream_frame(nghttp2_session *session) { - nghttp2_inbound_frame *iframe = &session->iframe; - nghttp2_frame *frame = &iframe->frame; - - nghttp2_frame_unpack_rst_stream_payload(&frame->rst_stream, iframe->sbuf.pos, - nghttp2_buf_len(&iframe->sbuf)); - - return nghttp2_session_on_rst_stream_received(session, frame); -} - -static int update_remote_initial_window_size_func(nghttp2_map_entry *entry, - void *ptr) { - int rv; - nghttp2_update_window_size_arg *arg; - nghttp2_stream *stream; - - arg = (nghttp2_update_window_size_arg *)ptr; - stream = (nghttp2_stream *)entry; - - rv = nghttp2_stream_update_remote_initial_window_size( - stream, arg->new_window_size, arg->old_window_size); - if (rv != 0) { - return nghttp2_session_terminate_session(arg->session, - NGHTTP2_FLOW_CONTROL_ERROR); - } - - /* If window size gets positive, push deferred DATA frame to - outbound queue. */ - if (stream->remote_window_size > 0 && - nghttp2_stream_check_deferred_by_flow_control(stream)) { - - rv = nghttp2_stream_resume_deferred_item( - stream, NGHTTP2_STREAM_FLAG_DEFERRED_FLOW_CONTROL, arg->session); - - if (nghttp2_is_fatal(rv)) { - return rv; - } - } - return 0; -} - -/* - * Updates the remote initial window size of all active streams. If - * error occurs, all streams may not be updated. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP2_ERR_NOMEM - * Out of memory. - */ -static int -session_update_remote_initial_window_size(nghttp2_session *session, - int32_t new_initial_window_size) { - nghttp2_update_window_size_arg arg; - - arg.session = session; - arg.new_window_size = new_initial_window_size; - arg.old_window_size = session->remote_settings.initial_window_size; - - return nghttp2_map_each(&session->streams, - update_remote_initial_window_size_func, &arg); -} - -static int update_local_initial_window_size_func(nghttp2_map_entry *entry, - void *ptr) { - int rv; - nghttp2_update_window_size_arg *arg; - nghttp2_stream *stream; - arg = (nghttp2_update_window_size_arg *)ptr; - stream = (nghttp2_stream *)entry; - rv = nghttp2_stream_update_local_initial_window_size( - stream, arg->new_window_size, arg->old_window_size); - if (rv != 0) { - return nghttp2_session_terminate_session(arg->session, - NGHTTP2_FLOW_CONTROL_ERROR); - } - if (!(arg->session->opt_flags & NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE)) { - - if (nghttp2_should_send_window_update(stream->local_window_size, - stream->recv_window_size)) { - - rv = nghttp2_session_add_window_update(arg->session, NGHTTP2_FLAG_NONE, - stream->stream_id, - stream->recv_window_size); - if (rv != 0) { - return rv; - } - stream->recv_window_size = 0; - } - } - return 0; -} - -/* - * Updates the local initial window size of all active streams. If - * error occurs, all streams may not be updated. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP2_ERR_NOMEM - * Out of memory. - */ -static int -session_update_local_initial_window_size(nghttp2_session *session, - int32_t new_initial_window_size, - int32_t old_initial_window_size) { - nghttp2_update_window_size_arg arg; - arg.session = session; - arg.new_window_size = new_initial_window_size; - arg.old_window_size = old_initial_window_size; - return nghttp2_map_each(&session->streams, - update_local_initial_window_size_func, &arg); -} - -/* - * Apply SETTINGS values |iv| having |niv| elements to the local - * settings. We assumes that all values in |iv| is correct, since we - * validated them in nghttp2_session_add_settings() already. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP2_ERR_HEADER_COMP - * The header table size is out of range - * NGHTTP2_ERR_NOMEM - * Out of memory - */ -int nghttp2_session_update_local_settings(nghttp2_session *session, - nghttp2_settings_entry *iv, - size_t niv) { - int rv; - size_t i; - int32_t new_initial_window_size = -1; - int32_t header_table_size = -1; - uint8_t header_table_size_seen = 0; - /* Use the value last seen. */ - for (i = 0; i < niv; ++i) { - switch (iv[i].settings_id) { - case NGHTTP2_SETTINGS_HEADER_TABLE_SIZE: - header_table_size_seen = 1; - header_table_size = iv[i].value; - break; - case NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE: - new_initial_window_size = iv[i].value; - break; - } - } - if (header_table_size_seen) { - rv = nghttp2_hd_inflate_change_table_size(&session->hd_inflater, - header_table_size); - if (rv != 0) { - return rv; - } - } - if (new_initial_window_size != -1) { - rv = session_update_local_initial_window_size( - session, new_initial_window_size, - session->local_settings.initial_window_size); - if (rv != 0) { - return rv; - } - } - - for (i = 0; i < niv; ++i) { - switch (iv[i].settings_id) { - case NGHTTP2_SETTINGS_HEADER_TABLE_SIZE: - session->local_settings.header_table_size = iv[i].value; - break; - case NGHTTP2_SETTINGS_ENABLE_PUSH: - session->local_settings.enable_push = iv[i].value; - break; - case NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS: - session->local_settings.max_concurrent_streams = iv[i].value; - break; - case NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE: - session->local_settings.initial_window_size = iv[i].value; - break; - case NGHTTP2_SETTINGS_MAX_FRAME_SIZE: - session->local_settings.max_frame_size = iv[i].value; - break; - case NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE: - session->local_settings.max_header_list_size = iv[i].value; - break; - } - } - - session->pending_local_max_concurrent_stream = - NGHTTP2_INITIAL_MAX_CONCURRENT_STREAMS; - session->pending_enable_push = 1; - - return 0; -} - -int nghttp2_session_on_settings_received(nghttp2_session *session, - nghttp2_frame *frame, int noack) { - int rv; - size_t i; - nghttp2_mem *mem; - - mem = &session->mem; - - if (frame->hd.stream_id != 0) { - return session_handle_invalid_connection(session, frame, NGHTTP2_ERR_PROTO, - "SETTINGS: stream_id != 0"); - } - if (frame->hd.flags & NGHTTP2_FLAG_ACK) { - if (frame->settings.niv != 0) { - return session_handle_invalid_connection( - session, frame, NGHTTP2_ERR_FRAME_SIZE_ERROR, - "SETTINGS: ACK and payload != 0"); - } - if (session->inflight_niv == -1) { - return session_handle_invalid_connection( - session, frame, NGHTTP2_ERR_PROTO, "SETTINGS: unexpected ACK"); - } - rv = nghttp2_session_update_local_settings(session, session->inflight_iv, - session->inflight_niv); - nghttp2_mem_free(mem, session->inflight_iv); - session->inflight_iv = NULL; - session->inflight_niv = -1; - if (rv != 0) { - if (nghttp2_is_fatal(rv)) { - return rv; - } - return session_handle_invalid_connection(session, frame, rv, NULL); - } - return session_call_on_frame_received(session, frame); - } - - for (i = 0; i < frame->settings.niv; ++i) { - nghttp2_settings_entry *entry = &frame->settings.iv[i]; - - switch (entry->settings_id) { - case NGHTTP2_SETTINGS_HEADER_TABLE_SIZE: - - if (entry->value > NGHTTP2_MAX_HEADER_TABLE_SIZE) { - return session_handle_invalid_connection( - session, frame, NGHTTP2_ERR_HEADER_COMP, - "SETTINGS: too large SETTINGS_HEADER_TABLE_SIZE"); - } - - rv = nghttp2_hd_deflate_change_table_size(&session->hd_deflater, - entry->value); - if (rv != 0) { - if (nghttp2_is_fatal(rv)) { - return rv; - } else { - return session_handle_invalid_connection( - session, frame, NGHTTP2_ERR_HEADER_COMP, NULL); - } - } - - session->remote_settings.header_table_size = entry->value; - - break; - case NGHTTP2_SETTINGS_ENABLE_PUSH: - - if (entry->value != 0 && entry->value != 1) { - return session_handle_invalid_connection( - session, frame, NGHTTP2_ERR_PROTO, - "SETTINGS: invalid SETTINGS_ENBLE_PUSH"); - } - - if (!session->server && entry->value != 0) { - return session_handle_invalid_connection( - session, frame, NGHTTP2_ERR_PROTO, - "SETTINGS: server attempted to enable push"); - } - - session->remote_settings.enable_push = entry->value; - - break; - case NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS: - - session->remote_settings.max_concurrent_streams = entry->value; - - break; - case NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE: - - /* Update the initial window size of the all active streams */ - /* Check that initial_window_size < (1u << 31) */ - if (entry->value > NGHTTP2_MAX_WINDOW_SIZE) { - return session_handle_invalid_connection( - session, frame, NGHTTP2_ERR_FLOW_CONTROL, - "SETTINGS: too large SETTINGS_INITIAL_WINDOW_SIZE"); - } - - rv = session_update_remote_initial_window_size(session, entry->value); - - if (nghttp2_is_fatal(rv)) { - return rv; - } - - if (rv != 0) { - return session_handle_invalid_connection( - session, frame, NGHTTP2_ERR_FLOW_CONTROL, NULL); - } - - session->remote_settings.initial_window_size = entry->value; - - break; - case NGHTTP2_SETTINGS_MAX_FRAME_SIZE: - - if (entry->value < NGHTTP2_MAX_FRAME_SIZE_MIN || - entry->value > NGHTTP2_MAX_FRAME_SIZE_MAX) { - return session_handle_invalid_connection( - session, frame, NGHTTP2_ERR_PROTO, - "SETTINGS: invalid SETTINGS_MAX_FRAME_SIZE"); - } - - session->remote_settings.max_frame_size = entry->value; - - break; - case NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE: - - session->remote_settings.max_header_list_size = entry->value; - - break; - } - } - - if (!noack && !session_is_closing(session)) { - rv = nghttp2_session_add_settings(session, NGHTTP2_FLAG_ACK, NULL, 0); - - if (rv != 0) { - if (nghttp2_is_fatal(rv)) { - return rv; - } - - return session_handle_invalid_connection(session, frame, - NGHTTP2_ERR_INTERNAL, NULL); - } - } - - return session_call_on_frame_received(session, frame); -} - -static int session_process_settings_frame(nghttp2_session *session) { - int rv; - nghttp2_inbound_frame *iframe = &session->iframe; - nghttp2_frame *frame = &iframe->frame; - size_t i; - nghttp2_settings_entry min_header_size_entry; - nghttp2_mem *mem; - - mem = &session->mem; - min_header_size_entry = iframe->iv[NGHTTP2_INBOUND_NUM_IV - 1]; - - if (min_header_size_entry.value < UINT32_MAX) { - /* If we have less value, then we must have - SETTINGS_HEADER_TABLE_SIZE in i < iframe->niv */ - for (i = 0; i < iframe->niv; ++i) { - if (iframe->iv[i].settings_id == NGHTTP2_SETTINGS_HEADER_TABLE_SIZE) { - break; - } - } - - assert(i < iframe->niv); - - if (min_header_size_entry.value != iframe->iv[i].value) { - iframe->iv[iframe->niv++] = iframe->iv[i]; - iframe->iv[i] = min_header_size_entry; - } - } - - rv = nghttp2_frame_unpack_settings_payload(&frame->settings, iframe->iv, - iframe->niv, mem); - if (rv != 0) { - assert(nghttp2_is_fatal(rv)); - return rv; - } - return nghttp2_session_on_settings_received(session, frame, 0 /* ACK */); -} - -int nghttp2_session_on_push_promise_received(nghttp2_session *session, - nghttp2_frame *frame) { - int rv; - nghttp2_stream *stream; - nghttp2_stream *promised_stream; - nghttp2_priority_spec pri_spec; - - if (frame->hd.stream_id == 0) { - return session_inflate_handle_invalid_connection( - session, frame, NGHTTP2_ERR_PROTO, "PUSH_PROMISE: stream_id == 0"); - } - if (session->server || session->local_settings.enable_push == 0) { - return session_inflate_handle_invalid_connection( - session, frame, NGHTTP2_ERR_PROTO, "PUSH_PROMISE: push disabled"); - } - if (session->goaway_flags) { - /* We just dicard PUSH_PROMISE after GOAWAY is sent or - received. */ - return NGHTTP2_ERR_IGN_HEADER_BLOCK; - } - - if (!nghttp2_session_is_my_stream_id(session, frame->hd.stream_id)) { - return session_inflate_handle_invalid_connection( - session, frame, NGHTTP2_ERR_PROTO, "PUSH_PROMISE: invalid stream_id"); - } - - if (!session_is_new_peer_stream_id(session, - frame->push_promise.promised_stream_id)) { - /* The spec says if an endpoint receives a PUSH_PROMISE with - illegal stream ID is subject to a connection error of type - PROTOCOL_ERROR. */ - return session_inflate_handle_invalid_connection( - session, frame, NGHTTP2_ERR_PROTO, - "PUSH_PROMISE: invalid promised_stream_id"); - } - session->last_recv_stream_id = frame->push_promise.promised_stream_id; - stream = nghttp2_session_get_stream(session, frame->hd.stream_id); - if (!stream || stream->state == NGHTTP2_STREAM_CLOSING || - !session->pending_enable_push) { - if (!stream) { - if (session_detect_idle_stream(session, frame->hd.stream_id)) { - return session_inflate_handle_invalid_connection( - session, frame, NGHTTP2_ERR_PROTO, "PUSH_PROMISE: stream in idle"); - } - } - rv = nghttp2_session_add_rst_stream(session, - frame->push_promise.promised_stream_id, - NGHTTP2_REFUSED_STREAM); - if (rv != 0) { - return rv; - } - return NGHTTP2_ERR_IGN_HEADER_BLOCK; - } - if (stream->shut_flags & NGHTTP2_SHUT_RD) { - if (session->callbacks.on_invalid_frame_recv_callback) { - if (session->callbacks.on_invalid_frame_recv_callback( - session, frame, NGHTTP2_PROTOCOL_ERROR, session->user_data) != - 0) { - return NGHTTP2_ERR_CALLBACK_FAILURE; - } - } - rv = nghttp2_session_add_rst_stream(session, - frame->push_promise.promised_stream_id, - NGHTTP2_PROTOCOL_ERROR); - if (rv != 0) { - return rv; - } - return NGHTTP2_ERR_IGN_HEADER_BLOCK; - } - - /* TODO It is unclear reserved stream dpeneds on associated - stream with or without exclusive flag set */ - nghttp2_priority_spec_init(&pri_spec, stream->stream_id, - NGHTTP2_DEFAULT_WEIGHT, 0); - - promised_stream = nghttp2_session_open_stream( - session, frame->push_promise.promised_stream_id, NGHTTP2_STREAM_FLAG_NONE, - &pri_spec, NGHTTP2_STREAM_RESERVED, NULL); - - if (!promised_stream) { - return NGHTTP2_ERR_NOMEM; - } - - session->last_proc_stream_id = session->last_recv_stream_id; - rv = session_call_on_begin_headers(session, frame); - if (rv != 0) { - return rv; - } - return 0; -} - -static int session_process_push_promise_frame(nghttp2_session *session) { - int rv; - nghttp2_inbound_frame *iframe = &session->iframe; - nghttp2_frame *frame = &iframe->frame; - - rv = nghttp2_frame_unpack_push_promise_payload( - &frame->push_promise, iframe->sbuf.pos, nghttp2_buf_len(&iframe->sbuf)); - - if (rv != 0) { - return nghttp2_session_terminate_session_with_reason( - session, NGHTTP2_PROTOCOL_ERROR, "PUSH_PROMISE: could not unpack"); - } - - return nghttp2_session_on_push_promise_received(session, frame); -} - -int nghttp2_session_on_ping_received(nghttp2_session *session, - nghttp2_frame *frame) { - int rv = 0; - if (frame->hd.stream_id != 0) { - return session_handle_invalid_connection(session, frame, NGHTTP2_ERR_PROTO, - "PING: stream_id != 0"); - } - if ((frame->hd.flags & NGHTTP2_FLAG_ACK) == 0 && - !session_is_closing(session)) { - /* Peer sent ping, so ping it back */ - rv = nghttp2_session_add_ping(session, NGHTTP2_FLAG_ACK, - frame->ping.opaque_data); - if (rv != 0) { - return rv; - } - } - return session_call_on_frame_received(session, frame); -} - -static int session_process_ping_frame(nghttp2_session *session) { - nghttp2_inbound_frame *iframe = &session->iframe; - nghttp2_frame *frame = &iframe->frame; - - nghttp2_frame_unpack_ping_payload(&frame->ping, iframe->sbuf.pos, - nghttp2_buf_len(&iframe->sbuf)); - - return nghttp2_session_on_ping_received(session, frame); -} - -int nghttp2_session_on_goaway_received(nghttp2_session *session, - nghttp2_frame *frame) { - int rv; - - if (frame->hd.stream_id != 0) { - return session_handle_invalid_connection(session, frame, NGHTTP2_ERR_PROTO, - "GOAWAY: stream_id != 0"); - } - /* Spec says Endpoints MUST NOT increase the value they send in the - last stream identifier. */ - if ((frame->goaway.last_stream_id > 0 && - !nghttp2_session_is_my_stream_id(session, - frame->goaway.last_stream_id)) || - session->remote_last_stream_id < frame->goaway.last_stream_id) { - return session_handle_invalid_connection(session, frame, NGHTTP2_ERR_PROTO, - "GOAWAY: invalid last_stream_id"); - } - - session->goaway_flags |= NGHTTP2_GOAWAY_RECV; - - session->remote_last_stream_id = frame->goaway.last_stream_id; - - rv = session_call_on_frame_received(session, frame); - - if (nghttp2_is_fatal(rv)) { - return rv; - } - - return session_close_stream_on_goaway(session, frame->goaway.last_stream_id, - 0); -} - -static int session_process_goaway_frame(nghttp2_session *session) { - nghttp2_inbound_frame *iframe = &session->iframe; - nghttp2_frame *frame = &iframe->frame; - - nghttp2_frame_unpack_goaway_payload( - &frame->goaway, iframe->sbuf.pos, nghttp2_buf_len(&iframe->sbuf), - iframe->lbuf.pos, nghttp2_buf_len(&iframe->lbuf)); - - nghttp2_buf_wrap_init(&iframe->lbuf, NULL, 0); - - return nghttp2_session_on_goaway_received(session, frame); -} - -static int -session_on_connection_window_update_received(nghttp2_session *session, - nghttp2_frame *frame) { - /* Handle connection-level flow control */ - if (frame->window_update.window_size_increment == 0) { - return session_handle_invalid_connection(session, frame, NGHTTP2_ERR_PROTO, - NULL); - } - - if (NGHTTP2_MAX_WINDOW_SIZE - frame->window_update.window_size_increment < - session->remote_window_size) { - return session_handle_invalid_connection(session, frame, - NGHTTP2_ERR_FLOW_CONTROL, NULL); - } - session->remote_window_size += frame->window_update.window_size_increment; - - return session_call_on_frame_received(session, frame); -} - -static int session_on_stream_window_update_received(nghttp2_session *session, - nghttp2_frame *frame) { - int rv; - nghttp2_stream *stream; - stream = nghttp2_session_get_stream(session, frame->hd.stream_id); - if (!stream) { - if (session_detect_idle_stream(session, frame->hd.stream_id)) { - return session_handle_invalid_connection( - session, frame, NGHTTP2_ERR_PROTO, "WINDOW_UPDATE to idle stream"); - } - return 0; - } - if (state_reserved_remote(session, stream)) { - return session_handle_invalid_connection( - session, frame, NGHTTP2_ERR_PROTO, "WINDOW_UPADATE to reserved stream"); - } - if (frame->window_update.window_size_increment == 0) { - return session_handle_invalid_stream(session, frame, NGHTTP2_ERR_PROTO); - } - if (NGHTTP2_MAX_WINDOW_SIZE - frame->window_update.window_size_increment < - stream->remote_window_size) { - return session_handle_invalid_stream(session, frame, - NGHTTP2_ERR_FLOW_CONTROL); - } - stream->remote_window_size += frame->window_update.window_size_increment; - - if (stream->remote_window_size > 0 && - nghttp2_stream_check_deferred_by_flow_control(stream)) { - - rv = nghttp2_stream_resume_deferred_item( - stream, NGHTTP2_STREAM_FLAG_DEFERRED_FLOW_CONTROL, session); - - if (nghttp2_is_fatal(rv)) { - return rv; - } - } - return session_call_on_frame_received(session, frame); -} - -int nghttp2_session_on_window_update_received(nghttp2_session *session, - nghttp2_frame *frame) { - if (frame->hd.stream_id == 0) { - return session_on_connection_window_update_received(session, frame); - } else { - return session_on_stream_window_update_received(session, frame); - } -} - -static int session_process_window_update_frame(nghttp2_session *session) { - nghttp2_inbound_frame *iframe = &session->iframe; - nghttp2_frame *frame = &iframe->frame; - - nghttp2_frame_unpack_window_update_payload( - &frame->window_update, iframe->sbuf.pos, nghttp2_buf_len(&iframe->sbuf)); - - return nghttp2_session_on_window_update_received(session, frame); -} - -int nghttp2_session_on_data_received(nghttp2_session *session, - nghttp2_frame *frame) { - int rv = 0; - int call_cb = 1; - nghttp2_stream *stream; - - /* We don't call on_frame_recv_callback if stream has been closed - already or being closed. */ - stream = nghttp2_session_get_stream(session, frame->hd.stream_id); - if (!stream || stream->state == NGHTTP2_STREAM_CLOSING) { - /* This should be treated as stream error, but it results in lots - of RST_STREAM. So just ignore frame against nonexistent stream - for now. */ - return 0; - } - - if (session_enforce_http_messaging(session) && - (frame->hd.flags & NGHTTP2_FLAG_END_STREAM)) { - if (nghttp2_http_on_remote_end_stream(stream) != 0) { - call_cb = 0; - rv = nghttp2_session_add_rst_stream(session, stream->stream_id, - NGHTTP2_PROTOCOL_ERROR); - if (nghttp2_is_fatal(rv)) { - return rv; - } - } - } - - if (call_cb) { - rv = session_call_on_frame_received(session, frame); - if (nghttp2_is_fatal(rv)) { - return rv; - } - } - - if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) { - nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_RD); - rv = nghttp2_session_close_stream_if_shut_rdwr(session, stream); - if (nghttp2_is_fatal(rv)) { - return rv; - } - } - return 0; -} - -/* For errors, this function only returns FATAL error. */ -static int session_process_data_frame(nghttp2_session *session) { - int rv; - nghttp2_frame *public_data_frame = &session->iframe.frame; - rv = nghttp2_session_on_data_received(session, public_data_frame); - if (nghttp2_is_fatal(rv)) { - return rv; - } - return 0; -} - -/* - * Now we have SETTINGS synchronization, flow control error can be - * detected strictly. If DATA frame is received with length > 0 and - * current received window size + delta length is strictly larger than - * local window size, it is subject to FLOW_CONTROL_ERROR, so return - * -1. Note that local_window_size is calculated after SETTINGS ACK is - * received from peer, so peer must honor this limit. If the resulting - * recv_window_size is strictly larger than NGHTTP2_MAX_WINDOW_SIZE, - * return -1 too. - */ -static int adjust_recv_window_size(int32_t *recv_window_size_ptr, size_t delta, - int32_t local_window_size) { - if (*recv_window_size_ptr > local_window_size - (int32_t)delta || - *recv_window_size_ptr > NGHTTP2_MAX_WINDOW_SIZE - (int32_t)delta) { - return -1; - } - *recv_window_size_ptr += delta; - return 0; -} - -/* - * Accumulates received bytes |delta_size| for stream-level flow - * control and decides whether to send WINDOW_UPDATE to that stream. - * If NGHTTP2_OPT_NO_AUTO_WINDOW_UPDATE is set, WINDOW_UPDATE will not - * be sent. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP2_ERR_NOMEM - * Out of memory. - */ -static int session_update_recv_stream_window_size(nghttp2_session *session, - nghttp2_stream *stream, - size_t delta_size, - int send_window_update) { - int rv; - rv = adjust_recv_window_size(&stream->recv_window_size, delta_size, - stream->local_window_size); - if (rv != 0) { - return nghttp2_session_add_rst_stream(session, stream->stream_id, - NGHTTP2_FLOW_CONTROL_ERROR); - } - /* We don't have to send WINDOW_UPDATE if the data received is the - last chunk in the incoming stream. */ - if (send_window_update && - !(session->opt_flags & NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE)) { - /* We have to use local_settings here because it is the constraint - the remote endpoint should honor. */ - if (nghttp2_should_send_window_update(stream->local_window_size, - stream->recv_window_size)) { - rv = nghttp2_session_add_window_update(session, NGHTTP2_FLAG_NONE, - stream->stream_id, - stream->recv_window_size); - if (rv == 0) { - stream->recv_window_size = 0; - } else { - return rv; - } - } - } - return 0; -} - -/* - * Accumulates received bytes |delta_size| for connection-level flow - * control and decides whether to send WINDOW_UPDATE to the - * connection. If NGHTTP2_OPT_NO_AUTO_WINDOW_UPDATE is set, - * WINDOW_UPDATE will not be sent. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP2_ERR_NOMEM - * Out of memory. - */ -static int session_update_recv_connection_window_size(nghttp2_session *session, - size_t delta_size) { - int rv; - rv = adjust_recv_window_size(&session->recv_window_size, delta_size, - session->local_window_size); - if (rv != 0) { - return nghttp2_session_terminate_session(session, - NGHTTP2_FLOW_CONTROL_ERROR); - } - if (!(session->opt_flags & NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE)) { - - if (nghttp2_should_send_window_update(session->local_window_size, - session->recv_window_size)) { - /* Use stream ID 0 to update connection-level flow control - window */ - rv = nghttp2_session_add_window_update(session, NGHTTP2_FLAG_NONE, 0, - session->recv_window_size); - if (rv != 0) { - return rv; - } - - session->recv_window_size = 0; - } - } - return 0; -} - -static int session_update_consumed_size(nghttp2_session *session, - int32_t *consumed_size_ptr, - int32_t *recv_window_size_ptr, - int32_t stream_id, size_t delta_size, - int32_t local_window_size) { - int32_t recv_size; - int rv; - - if ((size_t)*consumed_size_ptr > NGHTTP2_MAX_WINDOW_SIZE - delta_size) { - return nghttp2_session_terminate_session(session, - NGHTTP2_FLOW_CONTROL_ERROR); - } - - *consumed_size_ptr += delta_size; - - /* recv_window_size may be smaller than consumed_size, because it - may be decreased by negative value with - nghttp2_submit_window_update(). */ - recv_size = nghttp2_min(*consumed_size_ptr, *recv_window_size_ptr); - - if (nghttp2_should_send_window_update(local_window_size, recv_size)) { - rv = nghttp2_session_add_window_update(session, NGHTTP2_FLAG_NONE, - stream_id, recv_size); - - if (rv != 0) { - return rv; - } - - *recv_window_size_ptr -= recv_size; - *consumed_size_ptr -= recv_size; - } - - return 0; -} - -static int session_update_stream_consumed_size(nghttp2_session *session, - nghttp2_stream *stream, - size_t delta_size) { - return session_update_consumed_size( - session, &stream->consumed_size, &stream->recv_window_size, - stream->stream_id, delta_size, stream->local_window_size); -} - -static int session_update_connection_consumed_size(nghttp2_session *session, - size_t delta_size) { - return session_update_consumed_size(session, &session->consumed_size, - &session->recv_window_size, 0, delta_size, - session->local_window_size); -} - -/* - * Checks that we can receive the DATA frame for stream, which is - * indicated by |session->iframe.frame.hd.stream_id|. If it is a - * connection error situation, GOAWAY frame will be issued by this - * function. - * - * If the DATA frame is allowed, returns 0. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP2_ERR_IGN_PAYLOAD - * The reception of DATA frame is connection error; or should be - * ignored. - * NGHTTP2_ERR_NOMEM - * Out of memory. - */ -static int session_on_data_received_fail_fast(nghttp2_session *session) { - int rv; - nghttp2_stream *stream; - nghttp2_inbound_frame *iframe; - int32_t stream_id; - const char *failure_reason; - uint32_t error_code = NGHTTP2_PROTOCOL_ERROR; - - iframe = &session->iframe; - stream_id = iframe->frame.hd.stream_id; - - if (stream_id == 0) { - /* The spec says that if a DATA frame is received whose stream ID - is 0, the recipient MUST respond with a connection error of - type PROTOCOL_ERROR. */ - failure_reason = "DATA: stream_id == 0"; - goto fail; - } - stream = nghttp2_session_get_stream(session, stream_id); - if (!stream) { - if (session_detect_idle_stream(session, stream_id)) { - failure_reason = "DATA: stream in idle"; - error_code = NGHTTP2_STREAM_CLOSED; - goto fail; - } - return NGHTTP2_ERR_IGN_PAYLOAD; - } - if (stream->shut_flags & NGHTTP2_SHUT_RD) { - failure_reason = "DATA: stream in half-closed(remote)"; - error_code = NGHTTP2_STREAM_CLOSED; - goto fail; - } - - if (nghttp2_session_is_my_stream_id(session, stream_id)) { - if (stream->state == NGHTTP2_STREAM_CLOSING) { - return NGHTTP2_ERR_IGN_PAYLOAD; - } - if (stream->state != NGHTTP2_STREAM_OPENED) { - failure_reason = "DATA: stream not opened"; - goto fail; - } - return 0; - } - if (stream->state == NGHTTP2_STREAM_RESERVED) { - failure_reason = "DATA: stream in reserved"; - goto fail; - } - if (stream->state == NGHTTP2_STREAM_CLOSING) { - return NGHTTP2_ERR_IGN_PAYLOAD; - } - return 0; -fail: - rv = nghttp2_session_terminate_session_with_reason(session, error_code, - failure_reason); - if (nghttp2_is_fatal(rv)) { - return rv; - } - return NGHTTP2_ERR_IGN_PAYLOAD; -} - -static size_t inbound_frame_payload_readlen(nghttp2_inbound_frame *iframe, - const uint8_t *in, - const uint8_t *last) { - return nghttp2_min((size_t)(last - in), iframe->payloadleft); -} - -/* - * Resets iframe->sbuf and advance its mark pointer by |left| bytes. - */ -static void inbound_frame_set_mark(nghttp2_inbound_frame *iframe, size_t left) { - nghttp2_buf_reset(&iframe->sbuf); - iframe->sbuf.mark += left; -} - -static size_t inbound_frame_buf_read(nghttp2_inbound_frame *iframe, - const uint8_t *in, const uint8_t *last) { - size_t readlen; - - readlen = nghttp2_min(last - in, nghttp2_buf_mark_avail(&iframe->sbuf)); - - iframe->sbuf.last = nghttp2_cpymem(iframe->sbuf.last, in, readlen); - - return readlen; -} - -/* - * Unpacks SETTINGS entry in iframe->sbuf. - */ -static void inbound_frame_set_settings_entry(nghttp2_inbound_frame *iframe) { - nghttp2_settings_entry iv; - size_t i; - - nghttp2_frame_unpack_settings_entry(&iv, iframe->sbuf.pos); - - switch (iv.settings_id) { - case NGHTTP2_SETTINGS_HEADER_TABLE_SIZE: - case NGHTTP2_SETTINGS_ENABLE_PUSH: - case NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS: - case NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE: - case NGHTTP2_SETTINGS_MAX_FRAME_SIZE: - case NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE: - break; - default: - DEBUGF(fprintf(stderr, "recv: ignore unknown settings id=0x%02x\n", - iv.settings_id)); - return; - } - - for (i = 0; i < iframe->niv; ++i) { - if (iframe->iv[i].settings_id == iv.settings_id) { - iframe->iv[i] = iv; - break; - } - } - - if (i == iframe->niv) { - iframe->iv[iframe->niv++] = iv; - } - - if (iv.settings_id == NGHTTP2_SETTINGS_HEADER_TABLE_SIZE && - iv.value < iframe->iv[NGHTTP2_INBOUND_NUM_IV - 1].value) { - - iframe->iv[NGHTTP2_INBOUND_NUM_IV - 1] = iv; - } -} - -/* - * Checks PADDED flags and set iframe->sbuf to read them accordingly. - * If padding is set, this function returns 1. If no padding is set, - * this function returns 0. On error, returns -1. - */ -static int inbound_frame_handle_pad(nghttp2_inbound_frame *iframe, - nghttp2_frame_hd *hd) { - if (hd->flags & NGHTTP2_FLAG_PADDED) { - if (hd->length < 1) { - return -1; - } - inbound_frame_set_mark(iframe, 1); - return 1; - } - DEBUGF(fprintf(stderr, "recv: no padding in payload\n")); - return 0; -} - -/* - * Computes number of padding based on flags. This function returns - * the calculated length if it succeeds, or -1. - */ -static ssize_t inbound_frame_compute_pad(nghttp2_inbound_frame *iframe) { - size_t padlen; - - /* 1 for Pad Length field */ - padlen = iframe->sbuf.pos[0] + 1; - - DEBUGF(fprintf(stderr, "recv: padlen=%zu\n", padlen)); - - /* We cannot use iframe->frame.hd.length because of CONTINUATION */ - if (padlen - 1 > iframe->payloadleft) { - return -1; - } - - iframe->padlen = padlen; - - return padlen; -} - -/* - * This function returns the effective payload length in the data of - * length |readlen| when the remaning payload is |payloadleft|. The - * |payloadleft| does not include |readlen|. If padding was started - * strictly before this data chunk, this function returns -1. - */ -static ssize_t inbound_frame_effective_readlen(nghttp2_inbound_frame *iframe, - size_t payloadleft, - size_t readlen) { - size_t trail_padlen = - nghttp2_frame_trail_padlen(&iframe->frame, iframe->padlen); - - if (trail_padlen > payloadleft) { - size_t padlen; - padlen = trail_padlen - payloadleft; - if (readlen < padlen) { - return -1; - } else { - return readlen - padlen; - } - } - return readlen; -} - -ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in, - size_t inlen) { - const uint8_t *first = in, *last = in + inlen; - nghttp2_inbound_frame *iframe = &session->iframe; - size_t readlen; - ssize_t padlen; - int rv; - int busy = 0; - nghttp2_frame_hd cont_hd; - nghttp2_stream *stream; - size_t pri_fieldlen; - nghttp2_mem *mem; - - DEBUGF(fprintf(stderr, - "recv: connection recv_window_size=%d, local_window=%d\n", - session->recv_window_size, session->local_window_size)); - - mem = &session->mem; - - for (;;) { - switch (iframe->state) { - case NGHTTP2_IB_READ_CLIENT_MAGIC: - readlen = nghttp2_min(inlen, iframe->payloadleft); - - if (memcmp(NGHTTP2_CLIENT_MAGIC + NGHTTP2_CLIENT_MAGIC_LEN - - iframe->payloadleft, - in, readlen) != 0) { - return NGHTTP2_ERR_BAD_CLIENT_MAGIC; - } - - iframe->payloadleft -= readlen; - in += readlen; - - if (iframe->payloadleft == 0) { - session_inbound_frame_reset(session); - iframe->state = NGHTTP2_IB_READ_FIRST_SETTINGS; - } - - break; - case NGHTTP2_IB_READ_FIRST_SETTINGS: - DEBUGF(fprintf(stderr, "recv: [IB_READ_FIRST_SETTINGS]\n")); - - readlen = inbound_frame_buf_read(iframe, in, last); - in += readlen; - - if (nghttp2_buf_mark_avail(&iframe->sbuf)) { - return in - first; - } - - if (iframe->sbuf.pos[3] != NGHTTP2_SETTINGS || - (iframe->sbuf.pos[4] & NGHTTP2_FLAG_ACK)) { - - iframe->state = NGHTTP2_IB_IGN_ALL; - - rv = nghttp2_session_terminate_session_with_reason( - session, NGHTTP2_PROTOCOL_ERROR, "SETTINGS expected"); - - if (nghttp2_is_fatal(rv)) { - return rv; - } - - return inlen; - } - - iframe->state = NGHTTP2_IB_READ_HEAD; - - /* Fall through */ - case NGHTTP2_IB_READ_HEAD: { - int on_begin_frame_called = 0; - - DEBUGF(fprintf(stderr, "recv: [IB_READ_HEAD]\n")); - - readlen = inbound_frame_buf_read(iframe, in, last); - in += readlen; - - if (nghttp2_buf_mark_avail(&iframe->sbuf)) { - return in - first; - } - - nghttp2_frame_unpack_frame_hd(&iframe->frame.hd, iframe->sbuf.pos); - iframe->payloadleft = iframe->frame.hd.length; - - DEBUGF(fprintf(stderr, "recv: payloadlen=%zu, type=%u, flags=0x%02x, " - "stream_id=%d\n", - iframe->frame.hd.length, iframe->frame.hd.type, - iframe->frame.hd.flags, iframe->frame.hd.stream_id)); - - if (iframe->frame.hd.length > session->local_settings.max_frame_size) { - DEBUGF(fprintf(stderr, "recv: length is too large %zu > %u\n", - iframe->frame.hd.length, - session->local_settings.max_frame_size)); - - busy = 1; - - iframe->state = NGHTTP2_IB_IGN_PAYLOAD; - - rv = nghttp2_session_terminate_session_with_reason( - session, NGHTTP2_FRAME_SIZE_ERROR, "too large frame size"); - - if (nghttp2_is_fatal(rv)) { - return rv; - } - - break; - } - - switch (iframe->frame.hd.type) { - case NGHTTP2_DATA: { - DEBUGF(fprintf(stderr, "recv: DATA\n")); - - iframe->frame.hd.flags &= - (NGHTTP2_FLAG_END_STREAM | NGHTTP2_FLAG_PADDED); - /* Check stream is open. If it is not open or closing, - ignore payload. */ - busy = 1; - - rv = session_on_data_received_fail_fast(session); - if (rv == NGHTTP2_ERR_IGN_PAYLOAD) { - DEBUGF(fprintf(stderr, "recv: DATA not allowed stream_id=%d\n", - iframe->frame.hd.stream_id)); - iframe->state = NGHTTP2_IB_IGN_DATA; - break; - } - - if (nghttp2_is_fatal(rv)) { - return rv; - } - - rv = inbound_frame_handle_pad(iframe, &iframe->frame.hd); - if (rv < 0) { - iframe->state = NGHTTP2_IB_IGN_DATA; - rv = nghttp2_session_terminate_session_with_reason( - session, NGHTTP2_PROTOCOL_ERROR, - "DATA: insufficient padding space"); - - if (nghttp2_is_fatal(rv)) { - return rv; - } - break; - } - - if (rv == 1) { - iframe->state = NGHTTP2_IB_READ_PAD_DATA; - break; - } - - iframe->state = NGHTTP2_IB_READ_DATA; - break; - } - case NGHTTP2_HEADERS: - - DEBUGF(fprintf(stderr, "recv: HEADERS\n")); - - iframe->frame.hd.flags &= - (NGHTTP2_FLAG_END_STREAM | NGHTTP2_FLAG_END_HEADERS | - NGHTTP2_FLAG_PADDED | NGHTTP2_FLAG_PRIORITY); - - rv = inbound_frame_handle_pad(iframe, &iframe->frame.hd); - if (rv < 0) { - busy = 1; - - iframe->state = NGHTTP2_IB_IGN_PAYLOAD; - - rv = nghttp2_session_terminate_session_with_reason( - session, NGHTTP2_PROTOCOL_ERROR, - "HEADERS: insufficient padding space"); - if (nghttp2_is_fatal(rv)) { - return rv; - } - break; - } - - if (rv == 1) { - iframe->state = NGHTTP2_IB_READ_NBYTE; - break; - } - - pri_fieldlen = nghttp2_frame_priority_len(iframe->frame.hd.flags); - - if (pri_fieldlen > 0) { - if (iframe->payloadleft < pri_fieldlen) { - busy = 1; - iframe->state = NGHTTP2_IB_FRAME_SIZE_ERROR; - break; - } - - iframe->state = NGHTTP2_IB_READ_NBYTE; - - inbound_frame_set_mark(iframe, pri_fieldlen); - - break; - } - - /* Call on_begin_frame_callback here because - session_process_headers_frame() may call - on_begin_headers_callback */ - rv = session_call_on_begin_frame(session, &iframe->frame.hd); - - if (nghttp2_is_fatal(rv)) { - return rv; - } - - on_begin_frame_called = 1; - - rv = session_process_headers_frame(session); - if (nghttp2_is_fatal(rv)) { - return rv; - } - - busy = 1; - - if (rv == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) { - rv = nghttp2_session_add_rst_stream( - session, iframe->frame.hd.stream_id, NGHTTP2_INTERNAL_ERROR); - if (nghttp2_is_fatal(rv)) { - return rv; - } - iframe->state = NGHTTP2_IB_IGN_HEADER_BLOCK; - break; - } - - if (rv == NGHTTP2_ERR_IGN_HEADER_BLOCK) { - iframe->state = NGHTTP2_IB_IGN_HEADER_BLOCK; - break; - } - - iframe->state = NGHTTP2_IB_READ_HEADER_BLOCK; - - break; - case NGHTTP2_PRIORITY: - DEBUGF(fprintf(stderr, "recv: PRIORITY\n")); - - iframe->frame.hd.flags = NGHTTP2_FLAG_NONE; - - if (iframe->payloadleft != NGHTTP2_PRIORITY_SPECLEN) { - busy = 1; - - iframe->state = NGHTTP2_IB_FRAME_SIZE_ERROR; - - break; - } - - iframe->state = NGHTTP2_IB_READ_NBYTE; - - inbound_frame_set_mark(iframe, NGHTTP2_PRIORITY_SPECLEN); - - break; - case NGHTTP2_RST_STREAM: - case NGHTTP2_WINDOW_UPDATE: -#ifdef DEBUGBUILD - switch (iframe->frame.hd.type) { - case NGHTTP2_RST_STREAM: - DEBUGF(fprintf(stderr, "recv: RST_STREAM\n")); - break; - case NGHTTP2_WINDOW_UPDATE: - DEBUGF(fprintf(stderr, "recv: WINDOW_UPDATE\n")); - break; - } -#endif /* DEBUGBUILD */ - - iframe->frame.hd.flags = NGHTTP2_FLAG_NONE; - - if (iframe->payloadleft != 4) { - busy = 1; - iframe->state = NGHTTP2_IB_FRAME_SIZE_ERROR; - break; - } - - iframe->state = NGHTTP2_IB_READ_NBYTE; - - inbound_frame_set_mark(iframe, 4); - - break; - case NGHTTP2_SETTINGS: - DEBUGF(fprintf(stderr, "recv: SETTINGS\n")); - - iframe->frame.hd.flags &= NGHTTP2_FLAG_ACK; - - if ((iframe->frame.hd.length % NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH) || - ((iframe->frame.hd.flags & NGHTTP2_FLAG_ACK) && - iframe->payloadleft > 0)) { - busy = 1; - iframe->state = NGHTTP2_IB_FRAME_SIZE_ERROR; - break; - } - - iframe->state = NGHTTP2_IB_READ_SETTINGS; - - if (iframe->payloadleft) { - inbound_frame_set_mark(iframe, NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH); - break; - } - - busy = 1; - - inbound_frame_set_mark(iframe, 0); - - break; - case NGHTTP2_PUSH_PROMISE: - DEBUGF(fprintf(stderr, "recv: PUSH_PROMISE\n")); - - iframe->frame.hd.flags &= - (NGHTTP2_FLAG_END_HEADERS | NGHTTP2_FLAG_PADDED); - - rv = inbound_frame_handle_pad(iframe, &iframe->frame.hd); - if (rv < 0) { - busy = 1; - iframe->state = NGHTTP2_IB_IGN_PAYLOAD; - rv = nghttp2_session_terminate_session_with_reason( - session, NGHTTP2_PROTOCOL_ERROR, - "PUSH_PROMISE: insufficient padding space"); - if (nghttp2_is_fatal(rv)) { - return rv; - } - break; - } - - if (rv == 1) { - iframe->state = NGHTTP2_IB_READ_NBYTE; - break; - } - - if (iframe->payloadleft < 4) { - busy = 1; - iframe->state = NGHTTP2_IB_FRAME_SIZE_ERROR; - break; - } - - iframe->state = NGHTTP2_IB_READ_NBYTE; - - inbound_frame_set_mark(iframe, 4); - - break; - case NGHTTP2_PING: - DEBUGF(fprintf(stderr, "recv: PING\n")); - - iframe->frame.hd.flags &= NGHTTP2_FLAG_ACK; - - if (iframe->payloadleft != 8) { - busy = 1; - iframe->state = NGHTTP2_IB_FRAME_SIZE_ERROR; - break; - } - - iframe->state = NGHTTP2_IB_READ_NBYTE; - inbound_frame_set_mark(iframe, 8); - - break; - case NGHTTP2_GOAWAY: - DEBUGF(fprintf(stderr, "recv: GOAWAY\n")); - - iframe->frame.hd.flags = NGHTTP2_FLAG_NONE; - - if (iframe->payloadleft < 8) { - busy = 1; - iframe->state = NGHTTP2_IB_FRAME_SIZE_ERROR; - break; - } - - iframe->state = NGHTTP2_IB_READ_NBYTE; - inbound_frame_set_mark(iframe, 8); - - break; - case NGHTTP2_CONTINUATION: - DEBUGF(fprintf(stderr, "recv: unexpected CONTINUATION\n")); - - /* Receiving CONTINUATION in this state are subject to - connection error of type PROTOCOL_ERROR */ - rv = nghttp2_session_terminate_session_with_reason( - session, NGHTTP2_PROTOCOL_ERROR, "CONTINUATION: unexpected"); - if (nghttp2_is_fatal(rv)) { - return rv; - } - - busy = 1; - - iframe->state = NGHTTP2_IB_IGN_PAYLOAD; - - break; - default: - DEBUGF(fprintf(stderr, "recv: unknown frame\n")); - - /* Silently ignore unknown frame type. */ - - busy = 1; - - iframe->state = NGHTTP2_IB_IGN_PAYLOAD; - - break; - } - - if (!on_begin_frame_called) { - switch (iframe->state) { - case NGHTTP2_IB_IGN_HEADER_BLOCK: - case NGHTTP2_IB_IGN_PAYLOAD: - case NGHTTP2_IB_FRAME_SIZE_ERROR: - case NGHTTP2_IB_IGN_DATA: - break; - default: - rv = session_call_on_begin_frame(session, &iframe->frame.hd); - - if (nghttp2_is_fatal(rv)) { - return rv; - } - } - } - - break; - } - case NGHTTP2_IB_READ_NBYTE: - DEBUGF(fprintf(stderr, "recv: [IB_READ_NBYTE]\n")); - - readlen = inbound_frame_buf_read(iframe, in, last); - in += readlen; - iframe->payloadleft -= readlen; - - DEBUGF(fprintf(stderr, "recv: readlen=%zu, payloadleft=%zu, left=%zd\n", - readlen, iframe->payloadleft, - nghttp2_buf_mark_avail(&iframe->sbuf))); - - if (nghttp2_buf_mark_avail(&iframe->sbuf)) { - return in - first; - } - - switch (iframe->frame.hd.type) { - case NGHTTP2_HEADERS: - if (iframe->padlen == 0 && - (iframe->frame.hd.flags & NGHTTP2_FLAG_PADDED)) { - padlen = inbound_frame_compute_pad(iframe); - if (padlen < 0) { - busy = 1; - rv = nghttp2_session_terminate_session_with_reason( - session, NGHTTP2_PROTOCOL_ERROR, "HEADERS: invalid padding"); - if (nghttp2_is_fatal(rv)) { - return rv; - } - iframe->state = NGHTTP2_IB_IGN_PAYLOAD; - break; - } - iframe->frame.headers.padlen = padlen; - - pri_fieldlen = nghttp2_frame_priority_len(iframe->frame.hd.flags); - - if (pri_fieldlen > 0) { - if (iframe->payloadleft < pri_fieldlen) { - busy = 1; - iframe->state = NGHTTP2_IB_FRAME_SIZE_ERROR; - break; - } - iframe->state = NGHTTP2_IB_READ_NBYTE; - inbound_frame_set_mark(iframe, pri_fieldlen); - break; - } else { - /* Truncate buffers used for padding spec */ - inbound_frame_set_mark(iframe, 0); - } - } - - rv = session_process_headers_frame(session); - if (nghttp2_is_fatal(rv)) { - return rv; - } - - busy = 1; - - if (rv == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) { - rv = nghttp2_session_add_rst_stream( - session, iframe->frame.hd.stream_id, NGHTTP2_INTERNAL_ERROR); - if (nghttp2_is_fatal(rv)) { - return rv; - } - iframe->state = NGHTTP2_IB_IGN_HEADER_BLOCK; - break; - } - - if (rv == NGHTTP2_ERR_IGN_HEADER_BLOCK) { - iframe->state = NGHTTP2_IB_IGN_HEADER_BLOCK; - break; - } - - iframe->state = NGHTTP2_IB_READ_HEADER_BLOCK; - - break; - case NGHTTP2_PRIORITY: - rv = session_process_priority_frame(session); - if (nghttp2_is_fatal(rv)) { - return rv; - } - - session_inbound_frame_reset(session); - - break; - case NGHTTP2_RST_STREAM: - rv = session_process_rst_stream_frame(session); - if (nghttp2_is_fatal(rv)) { - return rv; - } - - session_inbound_frame_reset(session); - - break; - case NGHTTP2_PUSH_PROMISE: - if (iframe->padlen == 0 && - (iframe->frame.hd.flags & NGHTTP2_FLAG_PADDED)) { - padlen = inbound_frame_compute_pad(iframe); - if (padlen < 0) { - busy = 1; - rv = nghttp2_session_terminate_session_with_reason( - session, NGHTTP2_PROTOCOL_ERROR, - "PUSH_PROMISE: invalid padding"); - if (nghttp2_is_fatal(rv)) { - return rv; - } - iframe->state = NGHTTP2_IB_IGN_PAYLOAD; - break; - } - - iframe->frame.push_promise.padlen = padlen; - - if (iframe->payloadleft < 4) { - busy = 1; - iframe->state = NGHTTP2_IB_FRAME_SIZE_ERROR; - break; - } - - iframe->state = NGHTTP2_IB_READ_NBYTE; - - inbound_frame_set_mark(iframe, 4); - - break; - } - - rv = session_process_push_promise_frame(session); - if (nghttp2_is_fatal(rv)) { - return rv; - } - - busy = 1; - - if (rv == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) { - rv = nghttp2_session_add_rst_stream( - session, iframe->frame.push_promise.promised_stream_id, - NGHTTP2_INTERNAL_ERROR); - if (nghttp2_is_fatal(rv)) { - return rv; - } - iframe->state = NGHTTP2_IB_IGN_HEADER_BLOCK; - break; - } - - if (rv == NGHTTP2_ERR_IGN_HEADER_BLOCK) { - iframe->state = NGHTTP2_IB_IGN_HEADER_BLOCK; - break; - } - - iframe->state = NGHTTP2_IB_READ_HEADER_BLOCK; - - break; - case NGHTTP2_PING: - rv = session_process_ping_frame(session); - if (nghttp2_is_fatal(rv)) { - return rv; - } - - session_inbound_frame_reset(session); - - break; - case NGHTTP2_GOAWAY: { - size_t debuglen; - - /* 8 is Last-stream-ID + Error Code */ - debuglen = iframe->frame.hd.length - 8; - - if (debuglen > 0) { - iframe->raw_lbuf = nghttp2_mem_malloc(mem, debuglen); - - if (iframe->raw_lbuf == NULL) { - return NGHTTP2_ERR_NOMEM; - } - - nghttp2_buf_wrap_init(&iframe->lbuf, iframe->raw_lbuf, debuglen); - } - - busy = 1; - - iframe->state = NGHTTP2_IB_READ_GOAWAY_DEBUG; - - break; - } - case NGHTTP2_WINDOW_UPDATE: - rv = session_process_window_update_frame(session); - if (nghttp2_is_fatal(rv)) { - return rv; - } - - session_inbound_frame_reset(session); - - break; - default: - /* This is unknown frame */ - session_inbound_frame_reset(session); - - break; - } - break; - case NGHTTP2_IB_READ_HEADER_BLOCK: - case NGHTTP2_IB_IGN_HEADER_BLOCK: { - ssize_t data_readlen; -#ifdef DEBUGBUILD - if (iframe->state == NGHTTP2_IB_READ_HEADER_BLOCK) { - fprintf(stderr, "recv: [IB_READ_HEADER_BLOCK]\n"); - } else { - fprintf(stderr, "recv: [IB_IGN_HEADER_BLOCK]\n"); - } -#endif /* DEBUGBUILD */ - - readlen = inbound_frame_payload_readlen(iframe, in, last); - - DEBUGF(fprintf(stderr, "recv: readlen=%zu, payloadleft=%zu\n", readlen, - iframe->payloadleft - readlen)); - - data_readlen = inbound_frame_effective_readlen( - iframe, iframe->payloadleft - readlen, readlen); - if (data_readlen >= 0) { - size_t trail_padlen; - size_t hd_proclen = 0; - trail_padlen = - nghttp2_frame_trail_padlen(&iframe->frame, iframe->padlen); - DEBUGF(fprintf(stderr, "recv: block final=%d\n", - (iframe->frame.hd.flags & NGHTTP2_FLAG_END_HEADERS) && - iframe->payloadleft - data_readlen == trail_padlen)); - - rv = inflate_header_block( - session, &iframe->frame, &hd_proclen, (uint8_t *)in, data_readlen, - (iframe->frame.hd.flags & NGHTTP2_FLAG_END_HEADERS) && - iframe->payloadleft - data_readlen == trail_padlen, - iframe->state == NGHTTP2_IB_READ_HEADER_BLOCK); - - if (nghttp2_is_fatal(rv)) { - return rv; - } - - if (rv == NGHTTP2_ERR_PAUSE) { - in += hd_proclen; - iframe->payloadleft -= hd_proclen; - - return in - first; - } - - if (rv == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) { - /* The application says no more headers. We decompress the - rest of the header block but not invoke on_header_callback - and on_frame_recv_callback. */ - in += hd_proclen; - iframe->payloadleft -= hd_proclen; - - /* Use promised stream ID for PUSH_PROMISE */ - rv = nghttp2_session_add_rst_stream( - session, iframe->frame.hd.type == NGHTTP2_PUSH_PROMISE - ? iframe->frame.push_promise.promised_stream_id - : iframe->frame.hd.stream_id, - NGHTTP2_INTERNAL_ERROR); - if (nghttp2_is_fatal(rv)) { - return rv; - } - busy = 1; - iframe->state = NGHTTP2_IB_IGN_HEADER_BLOCK; - break; - } - - in += readlen; - iframe->payloadleft -= readlen; - - if (rv == NGHTTP2_ERR_HEADER_COMP) { - /* GOAWAY is already issued */ - if (iframe->payloadleft == 0) { - session_inbound_frame_reset(session); - } else { - busy = 1; - iframe->state = NGHTTP2_IB_IGN_PAYLOAD; - } - break; - } - } else { - in += readlen; - iframe->payloadleft -= readlen; - } - - if (iframe->payloadleft) { - break; - } - - if ((iframe->frame.hd.flags & NGHTTP2_FLAG_END_HEADERS) == 0) { - - inbound_frame_set_mark(iframe, NGHTTP2_FRAME_HDLEN); - - iframe->padlen = 0; - - if (iframe->state == NGHTTP2_IB_READ_HEADER_BLOCK) { - iframe->state = NGHTTP2_IB_EXPECT_CONTINUATION; - } else { - iframe->state = NGHTTP2_IB_IGN_CONTINUATION; - } - } else { - if (iframe->state == NGHTTP2_IB_READ_HEADER_BLOCK) { - rv = session_after_header_block_received(session); - if (nghttp2_is_fatal(rv)) { - return rv; - } - } - session_inbound_frame_reset(session); - } - break; - } - case NGHTTP2_IB_IGN_PAYLOAD: - DEBUGF(fprintf(stderr, "recv: [IB_IGN_PAYLOAD]\n")); - - readlen = inbound_frame_payload_readlen(iframe, in, last); - iframe->payloadleft -= readlen; - in += readlen; - - DEBUGF(fprintf(stderr, "recv: readlen=%zu, payloadleft=%zu\n", readlen, - iframe->payloadleft)); - - if (iframe->payloadleft) { - break; - } - - switch (iframe->frame.hd.type) { - case NGHTTP2_HEADERS: - case NGHTTP2_PUSH_PROMISE: - case NGHTTP2_CONTINUATION: - /* Mark inflater bad so that we won't perform further decoding */ - session->hd_inflater.ctx.bad = 1; - break; - default: - break; - } - - session_inbound_frame_reset(session); - - break; - case NGHTTP2_IB_FRAME_SIZE_ERROR: - DEBUGF(fprintf(stderr, "recv: [IB_FRAME_SIZE_ERROR]\n")); - - rv = session_handle_frame_size_error(session, &iframe->frame); - if (nghttp2_is_fatal(rv)) { - return rv; - } - - busy = 1; - - iframe->state = NGHTTP2_IB_IGN_PAYLOAD; - - break; - case NGHTTP2_IB_READ_SETTINGS: - DEBUGF(fprintf(stderr, "recv: [IB_READ_SETTINGS]\n")); - - readlen = inbound_frame_buf_read(iframe, in, last); - iframe->payloadleft -= readlen; - in += readlen; - - DEBUGF(fprintf(stderr, "recv: readlen=%zu, payloadleft=%zu\n", readlen, - iframe->payloadleft)); - - if (nghttp2_buf_mark_avail(&iframe->sbuf)) { - break; - } - - if (readlen > 0) { - inbound_frame_set_settings_entry(iframe); - } - if (iframe->payloadleft) { - inbound_frame_set_mark(iframe, NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH); - break; - } - - rv = session_process_settings_frame(session); - - if (nghttp2_is_fatal(rv)) { - return rv; - } - - session_inbound_frame_reset(session); - - break; - case NGHTTP2_IB_READ_GOAWAY_DEBUG: - DEBUGF(fprintf(stderr, "recv: [IB_READ_GOAWAY_DEBUG]\n")); - - readlen = inbound_frame_payload_readlen(iframe, in, last); - - iframe->lbuf.last = nghttp2_cpymem(iframe->lbuf.last, in, readlen); - - iframe->payloadleft -= readlen; - in += readlen; - - DEBUGF(fprintf(stderr, "recv: readlen=%zu, payloadleft=%zu\n", readlen, - iframe->payloadleft)); - - if (iframe->payloadleft) { - assert(nghttp2_buf_avail(&iframe->lbuf) > 0); - - break; - } - - rv = session_process_goaway_frame(session); - - if (nghttp2_is_fatal(rv)) { - return rv; - } - - session_inbound_frame_reset(session); - - break; - case NGHTTP2_IB_EXPECT_CONTINUATION: - case NGHTTP2_IB_IGN_CONTINUATION: -#ifdef DEBUGBUILD - if (iframe->state == NGHTTP2_IB_EXPECT_CONTINUATION) { - fprintf(stderr, "recv: [IB_EXPECT_CONTINUATION]\n"); - } else { - fprintf(stderr, "recv: [IB_IGN_CONTINUATION]\n"); - } -#endif /* DEBUGBUILD */ - - readlen = inbound_frame_buf_read(iframe, in, last); - in += readlen; - - if (nghttp2_buf_mark_avail(&iframe->sbuf)) { - return in - first; - } - - nghttp2_frame_unpack_frame_hd(&cont_hd, iframe->sbuf.pos); - iframe->payloadleft = cont_hd.length; - - DEBUGF(fprintf(stderr, "recv: payloadlen=%zu, type=%u, flags=0x%02x, " - "stream_id=%d\n", - cont_hd.length, cont_hd.type, cont_hd.flags, - cont_hd.stream_id)); - - if (cont_hd.type != NGHTTP2_CONTINUATION || - cont_hd.stream_id != iframe->frame.hd.stream_id) { - DEBUGF(fprintf(stderr, "recv: expected stream_id=%d, type=%d, but " - "got stream_id=%d, type=%d\n", - iframe->frame.hd.stream_id, NGHTTP2_CONTINUATION, - cont_hd.stream_id, cont_hd.type)); - rv = nghttp2_session_terminate_session_with_reason( - session, NGHTTP2_PROTOCOL_ERROR, - "unexpected non-CONTINUATION frame or stream_id is invalid"); - if (nghttp2_is_fatal(rv)) { - return rv; - } - - busy = 1; - - iframe->state = NGHTTP2_IB_IGN_PAYLOAD; - - break; - } - - /* CONTINUATION won't bear NGHTTP2_PADDED flag */ - - iframe->frame.hd.flags |= cont_hd.flags & NGHTTP2_FLAG_END_HEADERS; - iframe->frame.hd.length += cont_hd.length; - - busy = 1; - - if (iframe->state == NGHTTP2_IB_EXPECT_CONTINUATION) { - iframe->state = NGHTTP2_IB_READ_HEADER_BLOCK; - - rv = session_call_on_begin_frame(session, &cont_hd); - - if (nghttp2_is_fatal(rv)) { - return rv; - } - } else { - iframe->state = NGHTTP2_IB_IGN_HEADER_BLOCK; - } - - break; - case NGHTTP2_IB_READ_PAD_DATA: - DEBUGF(fprintf(stderr, "recv: [IB_READ_PAD_DATA]\n")); - - readlen = inbound_frame_buf_read(iframe, in, last); - in += readlen; - iframe->payloadleft -= readlen; - - DEBUGF(fprintf(stderr, "recv: readlen=%zu, payloadleft=%zu, left=%zu\n", - readlen, iframe->payloadleft, - nghttp2_buf_mark_avail(&iframe->sbuf))); - - if (nghttp2_buf_mark_avail(&iframe->sbuf)) { - return in - first; - } - - /* Pad Length field is subject to flow control */ - rv = session_update_recv_connection_window_size(session, readlen); - if (nghttp2_is_fatal(rv)) { - return rv; - } - - /* Pad Length field is consumed immediately */ - rv = - nghttp2_session_consume(session, iframe->frame.hd.stream_id, readlen); - - if (nghttp2_is_fatal(rv)) { - return rv; - } - - stream = nghttp2_session_get_stream(session, iframe->frame.hd.stream_id); - if (stream) { - rv = session_update_recv_stream_window_size( - session, stream, readlen, - iframe->payloadleft || - (iframe->frame.hd.flags & NGHTTP2_FLAG_END_STREAM) == 0); - if (nghttp2_is_fatal(rv)) { - return rv; - } - } - - busy = 1; - - padlen = inbound_frame_compute_pad(iframe); - if (padlen < 0) { - rv = nghttp2_session_terminate_session_with_reason( - session, NGHTTP2_PROTOCOL_ERROR, "DATA: invalid padding"); - if (nghttp2_is_fatal(rv)) { - return rv; - } - iframe->state = NGHTTP2_IB_IGN_DATA; - break; - } - - iframe->frame.data.padlen = padlen; - - iframe->state = NGHTTP2_IB_READ_DATA; - - break; - case NGHTTP2_IB_READ_DATA: - DEBUGF(fprintf(stderr, "recv: [IB_READ_DATA]\n")); - - readlen = inbound_frame_payload_readlen(iframe, in, last); - iframe->payloadleft -= readlen; - in += readlen; - - DEBUGF(fprintf(stderr, "recv: readlen=%zu, payloadleft=%zu\n", readlen, - iframe->payloadleft)); - - if (readlen > 0) { - ssize_t data_readlen; - - rv = session_update_recv_connection_window_size(session, readlen); - if (nghttp2_is_fatal(rv)) { - return rv; - } - - stream = - nghttp2_session_get_stream(session, iframe->frame.hd.stream_id); - if (stream) { - rv = session_update_recv_stream_window_size( - session, stream, readlen, - iframe->payloadleft || - (iframe->frame.hd.flags & NGHTTP2_FLAG_END_STREAM) == 0); - if (nghttp2_is_fatal(rv)) { - return rv; - } - } - - data_readlen = inbound_frame_effective_readlen( - iframe, iframe->payloadleft, readlen); - - padlen = readlen - data_readlen; - - if (padlen > 0) { - /* Padding is considered as "consumed" immediately */ - rv = nghttp2_session_consume(session, iframe->frame.hd.stream_id, - padlen); - - if (nghttp2_is_fatal(rv)) { - return rv; - } - } - - DEBUGF(fprintf(stderr, "recv: data_readlen=%zd\n", data_readlen)); - - if (stream && data_readlen > 0) { - if (session_enforce_http_messaging(session)) { - if (nghttp2_http_on_data_chunk(stream, data_readlen) != 0) { - rv = nghttp2_session_add_rst_stream( - session, iframe->frame.hd.stream_id, NGHTTP2_PROTOCOL_ERROR); - if (nghttp2_is_fatal(rv)) { - return rv; - } - busy = 1; - iframe->state = NGHTTP2_IB_IGN_DATA; - break; - } - } - if (session->callbacks.on_data_chunk_recv_callback) { - rv = session->callbacks.on_data_chunk_recv_callback( - session, iframe->frame.hd.flags, iframe->frame.hd.stream_id, - in - readlen, data_readlen, session->user_data); - if (rv == NGHTTP2_ERR_PAUSE) { - return in - first; - } - - if (nghttp2_is_fatal(rv)) { - return NGHTTP2_ERR_CALLBACK_FAILURE; - } - } - } - } - - if (iframe->payloadleft) { - break; - } - - rv = session_process_data_frame(session); - if (nghttp2_is_fatal(rv)) { - return rv; - } - - session_inbound_frame_reset(session); - - break; - case NGHTTP2_IB_IGN_DATA: - DEBUGF(fprintf(stderr, "recv: [IB_IGN_DATA]\n")); - - readlen = inbound_frame_payload_readlen(iframe, in, last); - iframe->payloadleft -= readlen; - in += readlen; - - DEBUGF(fprintf(stderr, "recv: readlen=%zu, payloadleft=%zu\n", readlen, - iframe->payloadleft)); - - if (readlen > 0) { - /* Update connection-level flow control window for ignored - DATA frame too */ - rv = session_update_recv_connection_window_size(session, readlen); - if (nghttp2_is_fatal(rv)) { - return rv; - } - - if (session->opt_flags & NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE) { - - /* Ignored DATA is considered as "consumed" immediately. */ - rv = session_update_connection_consumed_size(session, readlen); - - if (nghttp2_is_fatal(rv)) { - return rv; - } - } - } - - if (iframe->payloadleft) { - break; - } - - session_inbound_frame_reset(session); - - break; - case NGHTTP2_IB_IGN_ALL: - return inlen; - } - - if (!busy && in == last) { - break; - } - - busy = 0; - } - - assert(in == last); - - return in - first; -} - -int nghttp2_session_recv(nghttp2_session *session) { - uint8_t buf[NGHTTP2_INBOUND_BUFFER_LENGTH]; - while (1) { - ssize_t readlen; - readlen = session_recv(session, buf, sizeof(buf)); - if (readlen > 0) { - ssize_t proclen = nghttp2_session_mem_recv(session, buf, readlen); - if (proclen < 0) { - return (int)proclen; - } - assert(proclen == readlen); - } else if (readlen == 0 || readlen == NGHTTP2_ERR_WOULDBLOCK) { - return 0; - } else if (readlen == NGHTTP2_ERR_EOF) { - return NGHTTP2_ERR_EOF; - } else if (readlen < 0) { - return NGHTTP2_ERR_CALLBACK_FAILURE; - } - } -} - -/* - * Returns the number of active streams, which includes streams in - * reserved state. - */ -static size_t session_get_num_active_streams(nghttp2_session *session) { - return nghttp2_map_size(&session->streams) - session->num_closed_streams - - session->num_idle_streams; -} - -int nghttp2_session_want_read(nghttp2_session *session) { - size_t num_active_streams; - - /* If this flag is set, we don't want to read. The application - should drop the connection. */ - if (session->goaway_flags & NGHTTP2_GOAWAY_TERM_SENT) { - return 0; - } - - num_active_streams = session_get_num_active_streams(session); - - /* Unless termination GOAWAY is sent or received, we always want to - read incoming frames. */ - - if (num_active_streams > 0) { - return 1; - } - - /* If there is no active streams and GOAWAY has been sent or - received, we are done with this session. */ - return (session->goaway_flags & - (NGHTTP2_GOAWAY_SENT | NGHTTP2_GOAWAY_RECV)) == 0; -} - -int nghttp2_session_want_write(nghttp2_session *session) { - /* If these flag is set, we don't want to write any data. The - application should drop the connection. */ - if (session->goaway_flags & NGHTTP2_GOAWAY_TERM_SENT) { - return 0; - } - - /* - * Unless termination GOAWAY is sent or received, we want to write - * frames if there is pending ones. If pending frame is request/push - * response HEADERS and concurrent stream limit is reached, we don't - * want to write them. - */ - - if (session->aob.item == NULL && - nghttp2_outbound_queue_top(&session->ob_urgent) == NULL && - nghttp2_outbound_queue_top(&session->ob_reg) == NULL && - (nghttp2_pq_empty(&session->ob_da_pq) || - session->remote_window_size == 0) && - (nghttp2_outbound_queue_top(&session->ob_syn) == NULL || - session_is_outgoing_concurrent_streams_max(session))) { - return 0; - } - - /* If there is no active streams and GOAWAY has been sent or - received, we are done with this session. */ - return (session->goaway_flags & - (NGHTTP2_GOAWAY_SENT | NGHTTP2_GOAWAY_RECV)) == 0; -} - -int nghttp2_session_add_ping(nghttp2_session *session, uint8_t flags, - const uint8_t *opaque_data) { - int rv; - nghttp2_outbound_item *item; - nghttp2_frame *frame; - nghttp2_mem *mem; - - mem = &session->mem; - item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item)); - if (item == NULL) { - return NGHTTP2_ERR_NOMEM; - } - - nghttp2_outbound_item_init(item); - - frame = &item->frame; - - nghttp2_frame_ping_init(&frame->ping, flags, opaque_data); - - rv = nghttp2_session_add_item(session, item); - - if (rv != 0) { - nghttp2_frame_ping_free(&frame->ping); - nghttp2_mem_free(mem, item); - return rv; - } - return 0; -} - -int nghttp2_session_add_goaway(nghttp2_session *session, int32_t last_stream_id, - uint32_t error_code, const uint8_t *opaque_data, - size_t opaque_data_len, uint8_t aux_flags) { - int rv; - nghttp2_outbound_item *item; - nghttp2_frame *frame; - uint8_t *opaque_data_copy = NULL; - nghttp2_goaway_aux_data *aux_data; - nghttp2_mem *mem; - - mem = &session->mem; - - if (nghttp2_session_is_my_stream_id(session, last_stream_id)) { - return NGHTTP2_ERR_INVALID_ARGUMENT; - } - - if (opaque_data_len) { - if (opaque_data_len + 8 > NGHTTP2_MAX_PAYLOADLEN) { - return NGHTTP2_ERR_INVALID_ARGUMENT; - } - opaque_data_copy = nghttp2_mem_malloc(mem, opaque_data_len); - if (opaque_data_copy == NULL) { - return NGHTTP2_ERR_NOMEM; - } - memcpy(opaque_data_copy, opaque_data, opaque_data_len); - } - - item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item)); - if (item == NULL) { - nghttp2_mem_free(mem, opaque_data_copy); - return NGHTTP2_ERR_NOMEM; - } - - nghttp2_outbound_item_init(item); - - frame = &item->frame; - - /* last_stream_id must not be increased from the value previously - sent */ - last_stream_id = nghttp2_min(last_stream_id, session->local_last_stream_id); - - nghttp2_frame_goaway_init(&frame->goaway, last_stream_id, error_code, - opaque_data_copy, opaque_data_len); - - aux_data = &item->aux_data.goaway; - aux_data->flags = aux_flags; - - rv = nghttp2_session_add_item(session, item); - if (rv != 0) { - nghttp2_frame_goaway_free(&frame->goaway, mem); - nghttp2_mem_free(mem, item); - return rv; - } - return 0; -} - -int nghttp2_session_add_window_update(nghttp2_session *session, uint8_t flags, - int32_t stream_id, - int32_t window_size_increment) { - int rv; - nghttp2_outbound_item *item; - nghttp2_frame *frame; - nghttp2_mem *mem; - - mem = &session->mem; - item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item)); - if (item == NULL) { - return NGHTTP2_ERR_NOMEM; - } - - nghttp2_outbound_item_init(item); - - frame = &item->frame; - - nghttp2_frame_window_update_init(&frame->window_update, flags, stream_id, - window_size_increment); - - rv = nghttp2_session_add_item(session, item); - - if (rv != 0) { - nghttp2_frame_window_update_free(&frame->window_update); - nghttp2_mem_free(mem, item); - return rv; - } - return 0; -} - -int nghttp2_session_add_settings(nghttp2_session *session, uint8_t flags, - const nghttp2_settings_entry *iv, size_t niv) { - nghttp2_outbound_item *item; - nghttp2_frame *frame; - nghttp2_settings_entry *iv_copy; - size_t i; - int rv; - nghttp2_mem *mem; - - mem = &session->mem; - - if (flags & NGHTTP2_FLAG_ACK) { - if (niv != 0) { - return NGHTTP2_ERR_INVALID_ARGUMENT; - } - } else if (session->inflight_niv != -1) { - return NGHTTP2_ERR_TOO_MANY_INFLIGHT_SETTINGS; - } - - if (!nghttp2_iv_check(iv, niv)) { - return NGHTTP2_ERR_INVALID_ARGUMENT; - } - - item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item)); - if (item == NULL) { - return NGHTTP2_ERR_NOMEM; - } - - if (niv > 0) { - iv_copy = nghttp2_frame_iv_copy(iv, niv, mem); - if (iv_copy == NULL) { - nghttp2_mem_free(mem, item); - return NGHTTP2_ERR_NOMEM; - } - } else { - iv_copy = NULL; - } - - if ((flags & NGHTTP2_FLAG_ACK) == 0) { - if (niv > 0) { - session->inflight_iv = nghttp2_frame_iv_copy(iv, niv, mem); - - if (session->inflight_iv == NULL) { - nghttp2_mem_free(mem, iv_copy); - nghttp2_mem_free(mem, item); - return NGHTTP2_ERR_NOMEM; - } - } else { - session->inflight_iv = NULL; - } - - session->inflight_niv = niv; - } - - nghttp2_outbound_item_init(item); - - frame = &item->frame; - - nghttp2_frame_settings_init(&frame->settings, flags, iv_copy, niv); - rv = nghttp2_session_add_item(session, item); - if (rv != 0) { - /* The only expected error is fatal one */ - assert(nghttp2_is_fatal(rv)); - - if ((flags & NGHTTP2_FLAG_ACK) == 0) { - nghttp2_mem_free(mem, session->inflight_iv); - session->inflight_iv = NULL; - session->inflight_niv = -1; - } - - nghttp2_frame_settings_free(&frame->settings, mem); - nghttp2_mem_free(mem, item); - - return rv; - } - - /* Extract NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS and ENABLE_PUSH - here. We use it to refuse the incoming stream and PUSH_PROMISE - with RST_STREAM. */ - - for (i = niv; i > 0; --i) { - if (iv[i - 1].settings_id == NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS) { - session->pending_local_max_concurrent_stream = iv[i - 1].value; - break; - } - } - - for (i = niv; i > 0; --i) { - if (iv[i - 1].settings_id == NGHTTP2_SETTINGS_ENABLE_PUSH) { - session->pending_enable_push = iv[i - 1].value; - break; - } - } - - return 0; -} - -int nghttp2_session_pack_data(nghttp2_session *session, nghttp2_bufs *bufs, - size_t datamax, nghttp2_frame *frame, - nghttp2_data_aux_data *aux_data, - nghttp2_stream *stream) { - int rv; - uint32_t data_flags; - ssize_t payloadlen; - ssize_t padded_payloadlen; - nghttp2_buf *buf; - size_t max_payloadlen; - - assert(bufs->head == bufs->cur); - - buf = &bufs->cur->buf; - - if (session->callbacks.read_length_callback) { - - payloadlen = session->callbacks.read_length_callback( - session, frame->hd.type, stream->stream_id, session->remote_window_size, - stream->remote_window_size, session->remote_settings.max_frame_size, - session->user_data); - - DEBUGF(fprintf(stderr, "send: read_length_callback=%zd\n", payloadlen)); - - payloadlen = nghttp2_session_enforce_flow_control_limits(session, stream, - payloadlen); - - DEBUGF(fprintf(stderr, - "send: read_length_callback after flow control=%zd\n", - payloadlen)); - - if (payloadlen <= 0) { - return NGHTTP2_ERR_CALLBACK_FAILURE; - } - - if (payloadlen > nghttp2_buf_avail(buf)) { - /* Resize the current buffer(s). The reason why we do +1 for - buffer size is for possible padding field. */ - rv = nghttp2_bufs_realloc(&session->aob.framebufs, - NGHTTP2_FRAME_HDLEN + 1 + payloadlen); - - if (rv != 0) { - DEBUGF(fprintf(stderr, "send: realloc buffer failed rv=%d", rv)); - /* If reallocation failed, old buffers are still in tact. So - use safe limit. */ - payloadlen = datamax; - - DEBUGF( - fprintf(stderr, "send: use safe limit payloadlen=%zd", payloadlen)); - } else { - assert(&session->aob.framebufs == bufs); - - buf = &bufs->cur->buf; - } - } - datamax = (size_t)payloadlen; - } - - /* Current max DATA length is less then buffer chunk size */ - assert(nghttp2_buf_avail(buf) >= (ssize_t)datamax); - - data_flags = NGHTTP2_DATA_FLAG_NONE; - payloadlen = aux_data->data_prd.read_callback( - session, frame->hd.stream_id, buf->pos, datamax, &data_flags, - &aux_data->data_prd.source, session->user_data); - - if (payloadlen == NGHTTP2_ERR_DEFERRED || - payloadlen == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) { - DEBUGF(fprintf(stderr, "send: DATA postponed due to %s\n", - nghttp2_strerror((int)payloadlen))); - - return (int)payloadlen; - } - - if (payloadlen < 0 || datamax < (size_t)payloadlen) { - /* This is the error code when callback is failed. */ - return NGHTTP2_ERR_CALLBACK_FAILURE; - } - - buf->last = buf->pos + payloadlen; - buf->pos -= NGHTTP2_FRAME_HDLEN; - - /* Clear flags, because this may contain previous flags of previous - DATA */ - frame->hd.flags = NGHTTP2_FLAG_NONE; - - if (data_flags & NGHTTP2_DATA_FLAG_EOF) { - aux_data->eof = 1; - /* If NGHTTP2_DATA_FLAG_NO_END_STREAM is set, don't set - NGHTTP2_FLAG_END_STREAM */ - if ((aux_data->flags & NGHTTP2_FLAG_END_STREAM) && - (data_flags & NGHTTP2_DATA_FLAG_NO_END_STREAM) == 0) { - frame->hd.flags |= NGHTTP2_FLAG_END_STREAM; - } - } - - if (data_flags & NGHTTP2_DATA_FLAG_NO_COPY) { - if (session->callbacks.send_data_callback == NULL) { - DEBUGF(fprintf( - stderr, - "NGHTTP2_DATA_FLAG_NO_COPY requires send_data_callback set\n")); - - return NGHTTP2_ERR_CALLBACK_FAILURE; - } - aux_data->no_copy = 1; - } - - frame->hd.length = payloadlen; - frame->data.padlen = 0; - - max_payloadlen = nghttp2_min(datamax, frame->hd.length + NGHTTP2_MAX_PADLEN); - - padded_payloadlen = - session_call_select_padding(session, frame, max_payloadlen); - - if (nghttp2_is_fatal((int)padded_payloadlen)) { - return (int)padded_payloadlen; - } - - frame->data.padlen = padded_payloadlen - payloadlen; - - nghttp2_frame_pack_frame_hd(buf->pos, &frame->hd); - - rv = nghttp2_frame_add_pad(bufs, &frame->hd, frame->data.padlen, - aux_data->no_copy); - if (rv != 0) { - return rv; - } - - session_outbound_item_schedule(session, stream->item, - stream->effective_weight); - - return 0; -} - -void *nghttp2_session_get_stream_user_data(nghttp2_session *session, - int32_t stream_id) { - nghttp2_stream *stream; - stream = nghttp2_session_get_stream(session, stream_id); - if (stream) { - return stream->stream_user_data; - } else { - return NULL; - } -} - -int nghttp2_session_set_stream_user_data(nghttp2_session *session, - int32_t stream_id, - void *stream_user_data) { - nghttp2_stream *stream; - stream = nghttp2_session_get_stream(session, stream_id); - if (!stream) { - return NGHTTP2_ERR_INVALID_ARGUMENT; - } - stream->stream_user_data = stream_user_data; - return 0; -} - -int nghttp2_session_resume_data(nghttp2_session *session, int32_t stream_id) { - int rv; - nghttp2_stream *stream; - stream = nghttp2_session_get_stream(session, stream_id); - if (stream == NULL || !nghttp2_stream_check_deferred_item(stream)) { - return NGHTTP2_ERR_INVALID_ARGUMENT; - } - - rv = nghttp2_stream_resume_deferred_item( - stream, NGHTTP2_STREAM_FLAG_DEFERRED_USER, session); - - if (nghttp2_is_fatal(rv)) { - return rv; - } - - return rv; -} - -size_t nghttp2_session_get_outbound_queue_size(nghttp2_session *session) { - return nghttp2_outbound_queue_size(&session->ob_urgent) + - nghttp2_outbound_queue_size(&session->ob_reg) + - nghttp2_outbound_queue_size(&session->ob_syn) + - nghttp2_pq_size(&session->ob_da_pq); -} - -int32_t -nghttp2_session_get_stream_effective_recv_data_length(nghttp2_session *session, - int32_t stream_id) { - nghttp2_stream *stream; - stream = nghttp2_session_get_stream(session, stream_id); - if (stream == NULL) { - return -1; - } - return stream->recv_window_size < 0 ? 0 : stream->recv_window_size; -} - -int32_t -nghttp2_session_get_stream_effective_local_window_size(nghttp2_session *session, - int32_t stream_id) { - nghttp2_stream *stream; - stream = nghttp2_session_get_stream(session, stream_id); - if (stream == NULL) { - return -1; - } - return stream->local_window_size; -} - -int32_t -nghttp2_session_get_effective_recv_data_length(nghttp2_session *session) { - return session->recv_window_size < 0 ? 0 : session->recv_window_size; -} - -int32_t -nghttp2_session_get_effective_local_window_size(nghttp2_session *session) { - return session->local_window_size; -} - -int32_t nghttp2_session_get_stream_remote_window_size(nghttp2_session *session, - int32_t stream_id) { - nghttp2_stream *stream; - - stream = nghttp2_session_get_stream(session, stream_id); - if (stream == NULL) { - return -1; - } - - /* stream->remote_window_size can be negative when - SETTINGS_INITIAL_WINDOW_SIZE is changed. */ - return nghttp2_max(0, stream->remote_window_size); -} - -int32_t nghttp2_session_get_remote_window_size(nghttp2_session *session) { - return session->remote_window_size; -} - -uint32_t nghttp2_session_get_remote_settings(nghttp2_session *session, - nghttp2_settings_id id) { - switch (id) { - case NGHTTP2_SETTINGS_HEADER_TABLE_SIZE: - return session->remote_settings.header_table_size; - case NGHTTP2_SETTINGS_ENABLE_PUSH: - return session->remote_settings.enable_push; - case NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS: - return session->remote_settings.max_concurrent_streams; - case NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE: - return session->remote_settings.initial_window_size; - case NGHTTP2_SETTINGS_MAX_FRAME_SIZE: - return session->remote_settings.max_frame_size; - case NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE: - return session->remote_settings.max_header_list_size; - } - - assert(0); -} - -int nghttp2_session_upgrade(nghttp2_session *session, - const uint8_t *settings_payload, - size_t settings_payloadlen, - void *stream_user_data) { - nghttp2_stream *stream; - nghttp2_frame frame; - nghttp2_settings_entry *iv; - size_t niv; - int rv; - nghttp2_priority_spec pri_spec; - nghttp2_mem *mem; - - mem = &session->mem; - - if ((!session->server && session->next_stream_id != 1) || - (session->server && session->last_recv_stream_id >= 1)) { - return NGHTTP2_ERR_PROTO; - } - if (settings_payloadlen % NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH) { - return NGHTTP2_ERR_INVALID_ARGUMENT; - } - rv = nghttp2_frame_unpack_settings_payload2(&iv, &niv, settings_payload, - settings_payloadlen, mem); - if (rv != 0) { - return rv; - } - - if (session->server) { - nghttp2_frame_hd_init(&frame.hd, settings_payloadlen, NGHTTP2_SETTINGS, - NGHTTP2_FLAG_NONE, 0); - frame.settings.iv = iv; - frame.settings.niv = niv; - rv = nghttp2_session_on_settings_received(session, &frame, 1 /* No ACK */); - } else { - rv = nghttp2_submit_settings(session, NGHTTP2_FLAG_NONE, iv, niv); - } - nghttp2_mem_free(mem, iv); - if (rv != 0) { - return rv; - } - - nghttp2_priority_spec_default_init(&pri_spec); - - stream = nghttp2_session_open_stream( - session, 1, NGHTTP2_STREAM_FLAG_NONE, &pri_spec, NGHTTP2_STREAM_OPENING, - session->server ? NULL : stream_user_data); - if (stream == NULL) { - return NGHTTP2_ERR_NOMEM; - } - if (session->server) { - nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_RD); - session->last_recv_stream_id = 1; - session->last_proc_stream_id = 1; - } else { - nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_WR); - session->next_stream_id += 2; - } - return 0; -} - -int nghttp2_session_get_stream_local_close(nghttp2_session *session, - int32_t stream_id) { - nghttp2_stream *stream; - - stream = nghttp2_session_get_stream(session, stream_id); - - if (!stream) { - return -1; - } - - return (stream->shut_flags & NGHTTP2_SHUT_WR) != 0; -} - -int nghttp2_session_get_stream_remote_close(nghttp2_session *session, - int32_t stream_id) { - nghttp2_stream *stream; - - stream = nghttp2_session_get_stream(session, stream_id); - - if (!stream) { - return -1; - } - - return (stream->shut_flags & NGHTTP2_SHUT_RD) != 0; -} - -int nghttp2_session_consume(nghttp2_session *session, int32_t stream_id, - size_t size) { - int rv; - nghttp2_stream *stream; - - if (stream_id == 0) { - return NGHTTP2_ERR_INVALID_ARGUMENT; - } - - if (!(session->opt_flags & NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE)) { - return NGHTTP2_ERR_INVALID_STATE; - } - - rv = session_update_connection_consumed_size(session, size); - - if (nghttp2_is_fatal(rv)) { - return rv; - } - - stream = nghttp2_session_get_stream(session, stream_id); - - if (!stream) { - return 0; - } - - rv = session_update_stream_consumed_size(session, stream, size); - - if (nghttp2_is_fatal(rv)) { - return rv; - } - - return 0; -} - -int nghttp2_session_consume_connection(nghttp2_session *session, size_t size) { - int rv; - - if (!(session->opt_flags & NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE)) { - return NGHTTP2_ERR_INVALID_STATE; - } - - rv = session_update_connection_consumed_size(session, size); - - if (nghttp2_is_fatal(rv)) { - return rv; - } - - return 0; -} - -int nghttp2_session_consume_stream(nghttp2_session *session, int32_t stream_id, - size_t size) { - int rv; - nghttp2_stream *stream; - - if (stream_id == 0) { - return NGHTTP2_ERR_INVALID_ARGUMENT; - } - - if (!(session->opt_flags & NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE)) { - return NGHTTP2_ERR_INVALID_STATE; - } - - stream = nghttp2_session_get_stream(session, stream_id); - - if (!stream) { - return 0; - } - - rv = session_update_stream_consumed_size(session, stream, size); - - if (nghttp2_is_fatal(rv)) { - return rv; - } - - return 0; -} - -int nghttp2_session_set_next_stream_id(nghttp2_session *session, - int32_t next_stream_id) { - if (next_stream_id <= 0 || - session->next_stream_id > (uint32_t)next_stream_id) { - return NGHTTP2_ERR_INVALID_ARGUMENT; - } - - if (session->server) { - if (next_stream_id % 2) { - return NGHTTP2_ERR_INVALID_ARGUMENT; - } - } else if (next_stream_id % 2 == 0) { - return NGHTTP2_ERR_INVALID_ARGUMENT; - } - - session->next_stream_id = next_stream_id; - return 0; -} - -uint32_t nghttp2_session_get_next_stream_id(nghttp2_session *session) { - return session->next_stream_id; -} - -int32_t nghttp2_session_get_last_proc_stream_id(nghttp2_session *session) { - return session->last_proc_stream_id; -} diff --git a/lib/nghttp2_session.h b/lib/nghttp2_session.h deleted file mode 100644 index 41b1608..0000000 --- a/lib/nghttp2_session.h +++ /dev/null @@ -1,762 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2012 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#ifndef NGHTTP2_SESSION_H -#define NGHTTP2_SESSION_H - -#ifdef HAVE_CONFIG_H -#include -#endif /* HAVE_CONFIG_H */ - -#include -#include "nghttp2_pq.h" -#include "nghttp2_map.h" -#include "nghttp2_frame.h" -#include "nghttp2_hd.h" -#include "nghttp2_stream.h" -#include "nghttp2_outbound_item.h" -#include "nghttp2_int.h" -#include "nghttp2_buf.h" -#include "nghttp2_callbacks.h" -#include "nghttp2_mem.h" - -/* - * Option flags. - */ -typedef enum { - NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE = 1 << 0, - NGHTTP2_OPTMASK_NO_RECV_CLIENT_MAGIC = 1 << 1, - NGHTTP2_OPTMASK_NO_HTTP_MESSAGING = 1 << 2 -} nghttp2_optmask; - -typedef enum { - NGHTTP2_OB_POP_ITEM, - NGHTTP2_OB_SEND_DATA, - NGHTTP2_OB_SEND_NO_COPY, - NGHTTP2_OB_SEND_CLIENT_MAGIC -} nghttp2_outbound_state; - -typedef struct { - nghttp2_outbound_item *item; - nghttp2_bufs framebufs; - nghttp2_outbound_state state; -} nghttp2_active_outbound_item; - -/* Buffer length for inbound raw byte stream used in - nghttp2_session_recv(). */ -#define NGHTTP2_INBOUND_BUFFER_LENGTH 16384 - -/* Internal state when receiving incoming frame */ -typedef enum { - /* Receiving frame header */ - NGHTTP2_IB_READ_CLIENT_MAGIC, - NGHTTP2_IB_READ_FIRST_SETTINGS, - NGHTTP2_IB_READ_HEAD, - NGHTTP2_IB_READ_NBYTE, - NGHTTP2_IB_READ_HEADER_BLOCK, - NGHTTP2_IB_IGN_HEADER_BLOCK, - NGHTTP2_IB_IGN_PAYLOAD, - NGHTTP2_IB_FRAME_SIZE_ERROR, - NGHTTP2_IB_READ_SETTINGS, - NGHTTP2_IB_READ_GOAWAY_DEBUG, - NGHTTP2_IB_EXPECT_CONTINUATION, - NGHTTP2_IB_IGN_CONTINUATION, - NGHTTP2_IB_READ_PAD_DATA, - NGHTTP2_IB_READ_DATA, - NGHTTP2_IB_IGN_DATA, - NGHTTP2_IB_IGN_ALL -} nghttp2_inbound_state; - -#define NGHTTP2_INBOUND_NUM_IV 7 - -typedef struct { - nghttp2_frame frame; - /* Storage for extension frame payload. frame->ext.payload points - to this structure to avoid frequent memory allocation. */ - nghttp2_ext_frame_payload ext_frame_payload; - /* The received SETTINGS entry. The protocol says that we only cares - about the defined settings ID. If unknown ID is received, it is - ignored. We use last entry to hold minimum header table size if - same settings are multiple times. */ - nghttp2_settings_entry iv[NGHTTP2_INBOUND_NUM_IV]; - /* buffer pointers to small buffer, raw_sbuf */ - nghttp2_buf sbuf; - /* buffer pointers to large buffer, raw_lbuf */ - nghttp2_buf lbuf; - /* Large buffer, malloced on demand */ - uint8_t *raw_lbuf; - /* The number of entry filled in |iv| */ - size_t niv; - /* How many bytes we still need to receive for current frame */ - size_t payloadleft; - /* padding length for the current frame */ - size_t padlen; - nghttp2_inbound_state state; - /* Small buffer. Currently the largest contiguous chunk to buffer - is frame header. We buffer part of payload, but they are smaller - than frame header. */ - uint8_t raw_sbuf[NGHTTP2_FRAME_HDLEN]; -} nghttp2_inbound_frame; - -typedef struct { - uint32_t header_table_size; - uint32_t enable_push; - uint32_t max_concurrent_streams; - uint32_t initial_window_size; - uint32_t max_frame_size; - uint32_t max_header_list_size; -} nghttp2_settings_storage; - -typedef enum { - NGHTTP2_GOAWAY_NONE = 0, - /* Flag means that connection should be terminated after sending GOAWAY. */ - NGHTTP2_GOAWAY_TERM_ON_SEND = 0x1, - /* Flag means GOAWAY to terminate session has been sent */ - NGHTTP2_GOAWAY_TERM_SENT = 0x2, - /* Flag means GOAWAY was sent */ - NGHTTP2_GOAWAY_SENT = 0x4, - /* Flag means GOAWAY was received */ - NGHTTP2_GOAWAY_RECV = 0x8 -} nghttp2_goaway_flag; - -struct nghttp2_session { - nghttp2_map /* */ streams; - nghttp2_stream_roots roots; - /* Queue for outbound urgent frames (PING and SETTINGS) */ - nghttp2_outbound_queue ob_urgent; - /* Queue for non-DATA frames */ - nghttp2_outbound_queue ob_reg; - /* Queue for outbound stream-creating HEADERS (request or push - response) frame, which are subject to - SETTINGS_MAX_CONCURRENT_STREAMS limit. */ - nghttp2_outbound_queue ob_syn; - /* Queue for DATA frame */ - nghttp2_pq /* */ ob_da_pq; - nghttp2_active_outbound_item aob; - nghttp2_inbound_frame iframe; - nghttp2_hd_deflater hd_deflater; - nghttp2_hd_inflater hd_inflater; - nghttp2_session_callbacks callbacks; - /* Memory allocator */ - nghttp2_mem mem; - /* Base value when we schedule next DATA frame write. This is - updated when one frame was written. */ - uint64_t last_cycle; - void *user_data; - /* Points to the latest closed stream. NULL if there is no closed - stream. Only used when session is initialized as server. */ - nghttp2_stream *closed_stream_head; - /* Points to the oldest closed stream. NULL if there is no closed - stream. Only used when session is initialized as server. */ - nghttp2_stream *closed_stream_tail; - /* Points to the latest idle stream. NULL if there is no idle - stream. Only used when session is initialized as server .*/ - nghttp2_stream *idle_stream_head; - /* Points to the oldest idle stream. NULL if there is no idle - stream. Only used when session is initialized as erver. */ - nghttp2_stream *idle_stream_tail; - /* In-flight SETTINGS values. NULL does not necessarily mean there - is no in-flight SETTINGS. */ - nghttp2_settings_entry *inflight_iv; - /* The number of entries in |inflight_iv|. -1 if there is no - in-flight SETTINGS. */ - ssize_t inflight_niv; - /* The number of outgoing streams. This will be capped by - remote_settings.max_concurrent_streams. */ - size_t num_outgoing_streams; - /* The number of incoming streams. This will be capped by - local_settings.max_concurrent_streams. */ - size_t num_incoming_streams; - /* The number of closed streams still kept in |streams| hash. The - closed streams can be accessed through single linked list - |closed_stream_head|. The current implementation only keeps - incoming streams and session is initialized as server. */ - size_t num_closed_streams; - /* The number of idle streams kept in |streams| hash. The idle - streams can be accessed through doubly linked list - |idle_stream_head|. The current implementation only keeps idle - streams if session is initialized as server. */ - size_t num_idle_streams; - /* The number of bytes allocated for nvbuf */ - size_t nvbuflen; - /* Next Stream ID. Made unsigned int to detect >= (1 << 31). */ - uint32_t next_stream_id; - /* The largest stream ID received so far */ - int32_t last_recv_stream_id; - /* The largest stream ID which has been processed in some way. This - value will be used as last-stream-id when sending GOAWAY - frame. */ - int32_t last_proc_stream_id; - /* Counter of unique ID of PING. Wraps when it exceeds - NGHTTP2_MAX_UNIQUE_ID */ - uint32_t next_unique_id; - /* This is the last-stream-ID we have sent in GOAWAY */ - int32_t local_last_stream_id; - /* This is the value in GOAWAY frame received from remote endpoint. */ - int32_t remote_last_stream_id; - /* Current sender window size. This value is computed against the - current initial window size of remote endpoint. */ - int32_t remote_window_size; - /* Keep track of the number of bytes received without - WINDOW_UPDATE. This could be negative after submitting negative - value to WINDOW_UPDATE. */ - int32_t recv_window_size; - /* The number of bytes consumed by the application and now is - subject to WINDOW_UPDATE. This is only used when auto - WINDOW_UPDATE is turned off. */ - int32_t consumed_size; - /* The amount of recv_window_size cut using submitting negative - value to WINDOW_UPDATE */ - int32_t recv_reduction; - /* window size for local flow control. It is initially set to - NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE and could be - increased/decreased by submitting WINDOW_UPDATE. See - nghttp2_submit_window_update(). */ - int32_t local_window_size; - /* Settings value received from the remote endpoint. We just use ID - as index. The index = 0 is unused. */ - nghttp2_settings_storage remote_settings; - /* Settings value of the local endpoint. */ - nghttp2_settings_storage local_settings; - /* Option flags. This is bitwise-OR of 0 or more of nghttp2_optmask. */ - uint32_t opt_flags; - /* Unacked local SETTINGS_MAX_CONCURRENT_STREAMS value. We use this - to refuse the incoming stream if it exceeds this value. */ - uint32_t pending_local_max_concurrent_stream; - /* Unacked local ENABLE_PUSH value. We use this to refuse - PUSH_PROMISE before SETTINGS ACK is received. */ - uint8_t pending_enable_push; - /* Nonzero if the session is server side. */ - uint8_t server; - /* Flags indicating GOAWAY is sent and/or recieved. The flags are - composed by bitwise OR-ing nghttp2_goaway_flag. */ - uint8_t goaway_flags; -}; - -/* Struct used when updating initial window size of each active - stream. */ -typedef struct { - nghttp2_session *session; - int32_t new_window_size, old_window_size; -} nghttp2_update_window_size_arg; - -typedef struct { - nghttp2_session *session; - /* linked list of streams to close */ - nghttp2_stream *head; - int32_t last_stream_id; - /* nonzero if GOAWAY is sent to peer, which means we are going to - close incoming streams. zero if GOAWAY is received from peer and - we are going to close outgoing streams. */ - int incoming; -} nghttp2_close_stream_on_goaway_arg; - -/* TODO stream timeout etc */ - -/* - * Returns nonzero value if |stream_id| is initiated by local - * endpoint. - */ -int nghttp2_session_is_my_stream_id(nghttp2_session *session, - int32_t stream_id); - -/* - * Adds |item| to the outbound queue in |session|. When this function - * succeeds, it takes ownership of |item|. So caller must not free it - * on success. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP2_ERR_NOMEM - * Out of memory. - * NGHTTP2_ERR_STREAM_CLOSED - * Stream already closed (DATA frame only) - */ -int nghttp2_session_add_item(nghttp2_session *session, - nghttp2_outbound_item *item); - -/* - * Adds RST_STREAM frame for the stream |stream_id| with the error - * code |error_code|. This is a convenient function built on top of - * nghttp2_session_add_frame() to add RST_STREAM easily. - * - * This function simply returns 0 without adding RST_STREAM frame if - * given stream is in NGHTTP2_STREAM_CLOSING state, because multiple - * RST_STREAM for a stream is redundant. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP2_ERR_NOMEM - * Out of memory. - */ -int nghttp2_session_add_rst_stream(nghttp2_session *session, int32_t stream_id, - uint32_t error_code); - -/* - * Adds PING frame. This is a convenient functin built on top of - * nghttp2_session_add_frame() to add PING easily. - * - * If the |opaque_data| is not NULL, it must point to 8 bytes memory - * region of data. The data pointed by |opaque_data| is copied. It can - * be NULL. In this case, 8 bytes NULL is used. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP2_ERR_NOMEM - * Out of memory. - */ -int nghttp2_session_add_ping(nghttp2_session *session, uint8_t flags, - const uint8_t *opaque_data); - -/* - * Adds GOAWAY frame with the last-stream-ID |last_stream_id| and the - * error code |error_code|. This is a convenient function built on top - * of nghttp2_session_add_frame() to add GOAWAY easily. The - * |aux_flags| are bitwise-OR of one or more of - * nghttp2_goaway_aux_flag. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP2_ERR_NOMEM - * Out of memory. - * NGHTTP2_ERR_INVALID_ARGUMENT - * The |opaque_data_len| is too large. - */ -int nghttp2_session_add_goaway(nghttp2_session *session, int32_t last_stream_id, - uint32_t error_code, const uint8_t *opaque_data, - size_t opaque_data_len, uint8_t aux_flags); - -/* - * Adds WINDOW_UPDATE frame with stream ID |stream_id| and - * window-size-increment |window_size_increment|. This is a convenient - * function built on top of nghttp2_session_add_frame() to add - * WINDOW_UPDATE easily. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP2_ERR_NOMEM - * Out of memory. - */ -int nghttp2_session_add_window_update(nghttp2_session *session, uint8_t flags, - int32_t stream_id, - int32_t window_size_increment); - -/* - * Adds SETTINGS frame. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP2_ERR_NOMEM - * Out of memory. - */ -int nghttp2_session_add_settings(nghttp2_session *session, uint8_t flags, - const nghttp2_settings_entry *iv, size_t niv); - -/* - * Creates new stream in |session| with stream ID |stream_id|, - * priority |pri_spec| and flags |flags|. The |flags| is bitwise OR - * of nghttp2_stream_flag. Since this function is called when initial - * HEADERS is sent or received, these flags are taken from it. The - * state of stream is set to |initial_state|. The |stream_user_data| - * is a pointer to the arbitrary user supplied data to be associated - * to this stream. - * - * If |initial_state| is NGHTTP2_STREAM_RESERVED, this function sets - * NGHTTP2_STREAM_FLAG_PUSH flag set. - * - * This function returns a pointer to created new stream object, or - * NULL. - */ -nghttp2_stream *nghttp2_session_open_stream(nghttp2_session *session, - int32_t stream_id, uint8_t flags, - nghttp2_priority_spec *pri_spec, - nghttp2_stream_state initial_state, - void *stream_user_data); - -/* - * Closes stream whose stream ID is |stream_id|. The reason of closure - * is indicated by the |error_code|. When closing the stream, - * on_stream_close_callback will be called. - * - * If the session is initialized as server and |stream| is incoming - * stream, stream is just marked closed and this function calls - * nghttp2_session_keep_closed_stream() with |stream|. Otherwise, - * |stream| will be deleted from memory. - * - * This function returns 0 if it succeeds, or one the following - * negative error codes: - * - * NGHTTP2_ERR_NOMEM - * Out of memory - * NGHTTP2_ERR_INVALID_ARGUMENT - * The specified stream does not exist. - * NGHTTP2_ERR_CALLBACK_FAILURE - * The callback function failed. - */ -int nghttp2_session_close_stream(nghttp2_session *session, int32_t stream_id, - uint32_t error_code); - -/* - * Deletes |stream| from memory. After this function returns, stream - * cannot be accessed. - * - */ -void nghttp2_session_destroy_stream(nghttp2_session *session, - nghttp2_stream *stream); - -/* - * Tries to keep incoming closed stream |stream|. Due to the - * limitation of maximum number of streams in memory, |stream| is not - * closed and just deleted from memory (see - * nghttp2_session_destroy_stream). - */ -void nghttp2_session_keep_closed_stream(nghttp2_session *session, - nghttp2_stream *stream); - -/* - * Appends |stream| to linked list |session->idle_stream_head|. We - * apply fixed limit for list size. To fit into that limit, one or - * more oldest streams are removed from list as necessary. - */ -void nghttp2_session_keep_idle_stream(nghttp2_session *session, - nghttp2_stream *stream); - -/* - * Detaches |stream| from idle streams linked list. - */ -void nghttp2_session_detach_idle_stream(nghttp2_session *session, - nghttp2_stream *stream); - -/* - * Deletes closed stream to ensure that number of incoming streams - * including active and closed is in the maximum number of allowed - * stream. If |offset| is nonzero, it is decreased from the maximum - * number of allowed stream when comparing number of active and closed - * stream and the maximum number. - */ -void nghttp2_session_adjust_closed_stream(nghttp2_session *session, - ssize_t offset); - -/* - * Deletes idle stream to ensure that number of idle streams is in - * certain limit. - */ -void nghttp2_session_adjust_idle_stream(nghttp2_session *session); - -/* - * If further receptions and transmissions over the stream |stream_id| - * are disallowed, close the stream with error code NGHTTP2_NO_ERROR. - * - * This function returns 0 if it - * succeeds, or one of the following negative error codes: - * - * NGHTTP2_ERR_INVALID_ARGUMENT - * The specified stream does not exist. - */ -int nghttp2_session_close_stream_if_shut_rdwr(nghttp2_session *session, - nghttp2_stream *stream); - -int nghttp2_session_end_request_headers_received(nghttp2_session *session, - nghttp2_frame *frame, - nghttp2_stream *stream); - -int nghttp2_session_end_response_headers_received(nghttp2_session *session, - nghttp2_frame *frame, - nghttp2_stream *stream); - -int nghttp2_session_end_headers_received(nghttp2_session *session, - nghttp2_frame *frame, - nghttp2_stream *stream); - -int nghttp2_session_on_request_headers_received(nghttp2_session *session, - nghttp2_frame *frame); - -int nghttp2_session_on_response_headers_received(nghttp2_session *session, - nghttp2_frame *frame, - nghttp2_stream *stream); - -int nghttp2_session_on_push_response_headers_received(nghttp2_session *session, - nghttp2_frame *frame, - nghttp2_stream *stream); - -/* - * Called when HEADERS is received, assuming |frame| is properly - * initialized. This function does first validate received frame and - * then open stream and call callback functions. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP2_ERR_NOMEM - * Out of memory. - * NGHTTP2_ERR_IGN_HEADER_BLOCK - * Frame was rejected and header block must be decoded but - * result must be ignored. - * NGHTTP2_ERR_CALLBACK_FAILURE - * The read_callback failed - */ -int nghttp2_session_on_headers_received(nghttp2_session *session, - nghttp2_frame *frame, - nghttp2_stream *stream); - -/* - * Called when PRIORITY is received, assuming |frame| is properly - * initialized. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP2_ERR_NOMEM - * Out of memory. - * NGHTTP2_ERR_CALLBACK_FAILURE - * The read_callback failed - */ -int nghttp2_session_on_priority_received(nghttp2_session *session, - nghttp2_frame *frame); - -/* - * Called when RST_STREAM is received, assuming |frame| is properly - * initialized. - * - * This function returns 0 if it succeeds, or one the following - * negative error codes: - * - * NGHTTP2_ERR_NOMEM - * Out of memory - * NGHTTP2_ERR_CALLBACK_FAILURE - * The read_callback failed - */ -int nghttp2_session_on_rst_stream_received(nghttp2_session *session, - nghttp2_frame *frame); - -/* - * Called when SETTINGS is received, assuming |frame| is properly - * initialized. If |noack| is non-zero, SETTINGS with ACK will not be - * submitted. If |frame| has NGHTTP2_FLAG_ACK flag set, no SETTINGS - * with ACK will not be submitted regardless of |noack|. - * - * This function returns 0 if it succeeds, or one the following - * negative error codes: - * - * NGHTTP2_ERR_NOMEM - * Out of memory - * NGHTTP2_ERR_CALLBACK_FAILURE - * The read_callback failed - */ -int nghttp2_session_on_settings_received(nghttp2_session *session, - nghttp2_frame *frame, int noack); - -/* - * Called when PUSH_PROMISE is received, assuming |frame| is properly - * initialized. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP2_ERR_NOMEM - * Out of memory. - * NGHTTP2_ERR_IGN_HEADER_BLOCK - * Frame was rejected and header block must be decoded but - * result must be ignored. - * NGHTTP2_ERR_CALLBACK_FAILURE - * The read_callback failed - */ -int nghttp2_session_on_push_promise_received(nghttp2_session *session, - nghttp2_frame *frame); - -/* - * Called when PING is received, assuming |frame| is properly - * initialized. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP2_ERR_NOMEM - * Out of memory. - * NGHTTP2_ERR_CALLBACK_FAILURE - * The callback function failed. - */ -int nghttp2_session_on_ping_received(nghttp2_session *session, - nghttp2_frame *frame); - -/* - * Called when GOAWAY is received, assuming |frame| is properly - * initialized. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP2_ERR_NOMEM - * Out of memory. - * NGHTTP2_ERR_CALLBACK_FAILURE - * The callback function failed. - */ -int nghttp2_session_on_goaway_received(nghttp2_session *session, - nghttp2_frame *frame); - -/* - * Called when WINDOW_UPDATE is recieved, assuming |frame| is properly - * initialized. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP2_ERR_NOMEM - * Out of memory. - * NGHTTP2_ERR_CALLBACK_FAILURE - * The callback function failed. - */ -int nghttp2_session_on_window_update_received(nghttp2_session *session, - nghttp2_frame *frame); - -/* - * Called when DATA is received, assuming |frame| is properly - * initialized. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP2_ERR_NOMEM - * Out of memory. - * NGHTTP2_ERR_CALLBACK_FAILURE - * The callback function failed. - */ -int nghttp2_session_on_data_received(nghttp2_session *session, - nghttp2_frame *frame); - -/* - * Returns nghttp2_stream* object whose stream ID is |stream_id|. It - * could be NULL if such stream does not exist. This function returns - * NULL if stream is marked as closed. - */ -nghttp2_stream *nghttp2_session_get_stream(nghttp2_session *session, - int32_t stream_id); - -/* - * This function behaves like nghttp2_session_get_stream(), but it - * returns stream object even if it is marked as closed or in - * NGHTTP2_STREAM_IDLE state. - */ -nghttp2_stream *nghttp2_session_get_stream_raw(nghttp2_session *session, - int32_t stream_id); - -/* - * Packs DATA frame |frame| in wire frame format and stores it in - * |bufs|. Payload will be read using |aux_data->data_prd|. The - * length of payload is at most |datamax| bytes. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP2_ERR_DEFERRED - * The DATA frame is postponed. - * NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE - * The read_callback failed (stream error). - * NGHTTP2_ERR_NOMEM - * Out of memory. - * NGHTTP2_ERR_CALLBACK_FAILURE - * The read_callback failed (session error). - */ -int nghttp2_session_pack_data(nghttp2_session *session, nghttp2_bufs *bufs, - size_t datamax, nghttp2_frame *frame, - nghttp2_data_aux_data *aux_data, - nghttp2_stream *stream); - -/* - * Pops and returns next item to send. If there is no such item, - * returns NULL. This function takes into account max concurrent - * streams. That means if session->ob_pq is empty but - * session->ob_ss_pq has item and max concurrent streams is reached, - * then this function returns NULL. - */ -nghttp2_outbound_item * -nghttp2_session_pop_next_ob_item(nghttp2_session *session); - -/* - * Returns next item to send. If there is no such item, this function - * returns NULL. This function takes into account max concurrent - * streams. That means if session->ob_pq is empty but - * session->ob_ss_pq has item and max concurrent streams is reached, - * then this function returns NULL. - */ -nghttp2_outbound_item * -nghttp2_session_get_next_ob_item(nghttp2_session *session); - -/* - * Updates local settings with the |iv|. The number of elements in the - * array pointed by the |iv| is given by the |niv|. This function - * assumes that the all settings_id member in |iv| are in range 1 to - * NGHTTP2_SETTINGS_MAX, inclusive. - * - * While updating individual stream's local window size, if the window - * size becomes strictly larger than NGHTTP2_MAX_WINDOW_SIZE, - * RST_STREAM is issued against such a stream. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP2_ERR_NOMEM - * Out of memory - */ -int nghttp2_session_update_local_settings(nghttp2_session *session, - nghttp2_settings_entry *iv, - size_t niv); - -/* - * Re-prioritize |stream|. The new priority specification is - * |pri_spec|. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP2_ERR_NOMEM - * Out of memory - */ -int nghttp2_session_reprioritize_stream(nghttp2_session *session, - nghttp2_stream *stream, - const nghttp2_priority_spec *pri_spec); - -/* - * Terminates current |session| with the |error_code|. The |reason| - * is NULL-terminated debug string. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP2_ERR_NOMEM - * Out of memory. - * NGHTTP2_ERR_INVALID_ARGUMENT - * The |reason| is too long. - */ -int nghttp2_session_terminate_session_with_reason(nghttp2_session *session, - uint32_t error_code, - const char *reason); - -#endif /* NGHTTP2_SESSION_H */ diff --git a/lib/nghttp2_stream.c b/lib/nghttp2_stream.c deleted file mode 100644 index 753932f..0000000 --- a/lib/nghttp2_stream.c +++ /dev/null @@ -1,1010 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2012 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#include "nghttp2_stream.h" - -#include -#include - -#include "nghttp2_session.h" -#include "nghttp2_helper.h" - -void nghttp2_stream_init(nghttp2_stream *stream, int32_t stream_id, - uint8_t flags, nghttp2_stream_state initial_state, - int32_t weight, nghttp2_stream_roots *roots, - int32_t remote_initial_window_size, - int32_t local_initial_window_size, - void *stream_user_data) { - nghttp2_map_entry_init(&stream->map_entry, stream_id); - stream->stream_id = stream_id; - stream->flags = flags; - stream->state = initial_state; - stream->shut_flags = NGHTTP2_SHUT_NONE; - stream->stream_user_data = stream_user_data; - stream->item = NULL; - stream->remote_window_size = remote_initial_window_size; - stream->local_window_size = local_initial_window_size; - stream->recv_window_size = 0; - stream->consumed_size = 0; - stream->recv_reduction = 0; - - stream->dep_prev = NULL; - stream->dep_next = NULL; - stream->sib_prev = NULL; - stream->sib_next = NULL; - - stream->closed_prev = NULL; - stream->closed_next = NULL; - - stream->dpri = NGHTTP2_STREAM_DPRI_NO_ITEM; - stream->num_substreams = 1; - stream->weight = weight; - stream->effective_weight = stream->weight; - stream->sum_dep_weight = 0; - stream->sum_norest_weight = 0; - - stream->roots = roots; - stream->root_prev = NULL; - stream->root_next = NULL; - - stream->http_flags = NGHTTP2_HTTP_FLAG_NONE; - stream->content_length = -1; - stream->recv_content_length = 0; - stream->status_code = -1; -} - -void nghttp2_stream_free(nghttp2_stream *stream _U_) { - /* We don't free stream->item. If it is assigned to aob, then - active_outbound_item_reset() will delete it. If it is queued, - then it is deleted when pq is deleted in nghttp2_session_del(). - Otherwise, nghttp2_session_del() will delete it. */ -} - -void nghttp2_stream_shutdown(nghttp2_stream *stream, nghttp2_shut_flag flag) { - stream->shut_flags |= flag; -} - -static int stream_push_item(nghttp2_stream *stream, nghttp2_session *session) { - /* This is required for Android NDK r10d */ - int rv = 0; - nghttp2_outbound_item *item; - - assert(stream->item); - assert(stream->item->queued == 0); - - item = stream->item; - - /* If item is now sent, don't push it to the queue. Otherwise, we - may push same item twice. */ - if (session->aob.item == item) { - return 0; - } - - switch (item->frame.hd.type) { - case NGHTTP2_DATA: - /* Penalize item by delaying scheduling according to effective - weight. This will delay low priority stream, which is good. - OTOH, this may incur delay for high priority item. Will - see. */ - item->cycle = - session->last_cycle + - NGHTTP2_DATA_PAYLOADLEN * NGHTTP2_MAX_WEIGHT / stream->effective_weight; - - rv = nghttp2_pq_push(&session->ob_da_pq, item); - if (rv != 0) { - return rv; - } - break; - case NGHTTP2_HEADERS: - if (stream->state == NGHTTP2_STREAM_RESERVED) { - nghttp2_outbound_queue_push(&session->ob_syn, item); - } else { - nghttp2_outbound_queue_push(&session->ob_reg, item); - } - break; - default: - /* should not reach here */ - assert(0); - } - - item->queued = 1; - - return 0; -} - -static nghttp2_stream *stream_first_sib(nghttp2_stream *stream) { - for (; stream->sib_prev; stream = stream->sib_prev) - ; - - return stream; -} - -static nghttp2_stream *stream_last_sib(nghttp2_stream *stream) { - for (; stream->sib_next; stream = stream->sib_next) - ; - - return stream; -} - -static nghttp2_stream *stream_update_dep_length(nghttp2_stream *stream, - ssize_t delta) { - stream->num_substreams += delta; - - stream = stream_first_sib(stream); - - if (stream->dep_prev) { - return stream_update_dep_length(stream->dep_prev, delta); - } - - return stream; -} - -int32_t nghttp2_stream_dep_distributed_weight(nghttp2_stream *stream, - int32_t weight) { - weight = stream->weight * weight / stream->sum_dep_weight; - - return nghttp2_max(1, weight); -} - -int32_t nghttp2_stream_dep_distributed_effective_weight(nghttp2_stream *stream, - int32_t weight) { - if (stream->sum_norest_weight == 0) { - return stream->effective_weight; - } - - weight = stream->effective_weight * weight / stream->sum_norest_weight; - - return nghttp2_max(1, weight); -} - -static void stream_update_dep_set_rest(nghttp2_stream *stream); - -/* Updates effective_weight of descendant streams in subtree of - |stream|. We assume that stream->effective_weight is already set - right. */ -static void stream_update_dep_effective_weight(nghttp2_stream *stream) { - nghttp2_stream *si; - - DEBUGF(fprintf(stderr, "stream: update_dep_effective_weight " - "stream(%p)=%d, weight=%d, sum_norest_weight=%d\n", - stream, stream->stream_id, stream->weight, - stream->sum_norest_weight)); - - /* stream->sum_norest_weight == 0 means there is no - NGHTTP2_STREAM_DPRI_TOP under stream */ - if (stream->dpri != NGHTTP2_STREAM_DPRI_NO_ITEM || - stream->sum_norest_weight == 0) { - return; - } - - for (si = stream->dep_next; si; si = si->sib_next) { - if (si->dpri != NGHTTP2_STREAM_DPRI_REST) { - si->effective_weight = - nghttp2_stream_dep_distributed_effective_weight(stream, si->weight); - } - - stream_update_dep_effective_weight(si); - } -} - -static void stream_update_dep_set_rest(nghttp2_stream *stream) { - if (stream == NULL) { - return; - } - - DEBUGF(fprintf(stderr, "stream: stream=%d is rest\n", stream->stream_id)); - - if (stream->dpri == NGHTTP2_STREAM_DPRI_REST) { - return; - } - - if (stream->dpri == NGHTTP2_STREAM_DPRI_TOP) { - stream->dpri = NGHTTP2_STREAM_DPRI_REST; - - stream_update_dep_set_rest(stream->sib_next); - - return; - } - - stream_update_dep_set_rest(stream->sib_next); - stream_update_dep_set_rest(stream->dep_next); -} - -/* - * Performs dfs starting |stream|, search stream which can become - * NGHTTP2_STREAM_DPRI_TOP and set its dpri. - */ -static void stream_update_dep_set_top(nghttp2_stream *stream) { - nghttp2_stream *si; - - if (stream->dpri == NGHTTP2_STREAM_DPRI_TOP) { - return; - } - - if (stream->dpri == NGHTTP2_STREAM_DPRI_REST) { - DEBUGF( - fprintf(stderr, "stream: stream=%d item is top\n", stream->stream_id)); - - stream->dpri = NGHTTP2_STREAM_DPRI_TOP; - - return; - } - - for (si = stream->dep_next; si; si = si->sib_next) { - stream_update_dep_set_top(si); - } -} - -/* - * Performs dfs starting |stream|, and dueue stream whose dpri is - * NGHTTP2_STREAM_DPRI_TOP and has not been queued yet. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP2_ERR_NOMEM - * Out of memory. - */ -static int stream_update_dep_queue_top(nghttp2_stream *stream, - nghttp2_session *session) { - int rv; - nghttp2_stream *si; - - if (stream->dpri == NGHTTP2_STREAM_DPRI_REST) { - return 0; - } - - if (stream->dpri == NGHTTP2_STREAM_DPRI_TOP) { - if (!stream->item->queued) { - DEBUGF(fprintf(stderr, "stream: stream=%d enqueue\n", stream->stream_id)); - rv = stream_push_item(stream, session); - - if (rv != 0) { - return rv; - } - } - - return 0; - } - - for (si = stream->dep_next; si; si = si->sib_next) { - rv = stream_update_dep_queue_top(si, session); - - if (rv != 0) { - return rv; - } - } - - return 0; -} - -/* - * Updates stream->sum_norest_weight recursively. We have to gather - * effective sum of weight of descendants. If stream->dpri == - * NGHTTP2_STREAM_DPRI_NO_ITEM, we have to go deeper and check that - * any of its descendants has dpri value of NGHTTP2_STREAM_DPRI_TOP. - * If so, we have to add weight of its direct descendants to - * stream->sum_norest_weight. To make this work, this function - * returns 1 if any of its descendants has dpri value of - * NGHTTP2_STREAM_DPRI_TOP, otherwise 0. - */ -static int stream_update_dep_sum_norest_weight(nghttp2_stream *stream) { - nghttp2_stream *si; - int rv; - - stream->sum_norest_weight = 0; - - if (stream->dpri == NGHTTP2_STREAM_DPRI_TOP) { - return 1; - } - - if (stream->dpri == NGHTTP2_STREAM_DPRI_REST) { - return 0; - } - - rv = 0; - - for (si = stream->dep_next; si; si = si->sib_next) { - - if (stream_update_dep_sum_norest_weight(si)) { - rv = 1; - stream->sum_norest_weight += si->weight; - } - } - - return rv; -} - -static int stream_update_dep_on_attach_item(nghttp2_stream *stream, - nghttp2_session *session) { - nghttp2_stream *root_stream; - - stream->dpri = NGHTTP2_STREAM_DPRI_REST; - - stream_update_dep_set_rest(stream->dep_next); - - root_stream = nghttp2_stream_get_dep_root(stream); - - DEBUGF(fprintf(stderr, "root=%p, stream=%p\n", root_stream, stream)); - - stream_update_dep_set_top(root_stream); - - stream_update_dep_sum_norest_weight(root_stream); - stream_update_dep_effective_weight(root_stream); - - return stream_update_dep_queue_top(root_stream, session); -} - -static int stream_update_dep_on_detach_item(nghttp2_stream *stream, - nghttp2_session *session) { - nghttp2_stream *root_stream; - - stream->dpri = NGHTTP2_STREAM_DPRI_NO_ITEM; - - root_stream = nghttp2_stream_get_dep_root(stream); - - stream_update_dep_set_top(root_stream); - - stream_update_dep_sum_norest_weight(root_stream); - stream_update_dep_effective_weight(root_stream); - - return stream_update_dep_queue_top(root_stream, session); -} - -int nghttp2_stream_attach_item(nghttp2_stream *stream, - nghttp2_outbound_item *item, - nghttp2_session *session) { - assert((stream->flags & NGHTTP2_STREAM_FLAG_DEFERRED_ALL) == 0); - assert(stream->item == NULL); - - DEBUGF(fprintf(stderr, "stream: stream=%d attach item=%p\n", - stream->stream_id, item)); - - stream->item = item; - - return stream_update_dep_on_attach_item(stream, session); -} - -int nghttp2_stream_detach_item(nghttp2_stream *stream, - nghttp2_session *session) { - DEBUGF(fprintf(stderr, "stream: stream=%d detach item=%p\n", - stream->stream_id, stream->item)); - - stream->item = NULL; - stream->flags &= ~NGHTTP2_STREAM_FLAG_DEFERRED_ALL; - - return stream_update_dep_on_detach_item(stream, session); -} - -int nghttp2_stream_defer_item(nghttp2_stream *stream, uint8_t flags, - nghttp2_session *session) { - assert(stream->item); - - DEBUGF(fprintf(stderr, "stream: stream=%d defer item=%p cause=%02x\n", - stream->stream_id, stream->item, flags)); - - stream->flags |= flags; - - return stream_update_dep_on_detach_item(stream, session); -} - -int nghttp2_stream_resume_deferred_item(nghttp2_stream *stream, uint8_t flags, - nghttp2_session *session) { - assert(stream->item); - - DEBUGF(fprintf(stderr, "stream: stream=%d resume item=%p flags=%02x\n", - stream->stream_id, stream->item, flags)); - - stream->flags &= ~flags; - - if (stream->flags & NGHTTP2_STREAM_FLAG_DEFERRED_ALL) { - return 0; - } - - return stream_update_dep_on_attach_item(stream, session); -} - -int nghttp2_stream_check_deferred_item(nghttp2_stream *stream) { - return stream->item && (stream->flags & NGHTTP2_STREAM_FLAG_DEFERRED_ALL); -} - -int nghttp2_stream_check_deferred_by_flow_control(nghttp2_stream *stream) { - return stream->item && - (stream->flags & NGHTTP2_STREAM_FLAG_DEFERRED_FLOW_CONTROL); -} - -static int update_initial_window_size(int32_t *window_size_ptr, - int32_t new_initial_window_size, - int32_t old_initial_window_size) { - int64_t new_window_size = (int64_t)(*window_size_ptr) + - new_initial_window_size - old_initial_window_size; - if (INT32_MIN > new_window_size || - new_window_size > NGHTTP2_MAX_WINDOW_SIZE) { - return -1; - } - *window_size_ptr = (int32_t)new_window_size; - return 0; -} - -int nghttp2_stream_update_remote_initial_window_size( - nghttp2_stream *stream, int32_t new_initial_window_size, - int32_t old_initial_window_size) { - return update_initial_window_size(&stream->remote_window_size, - new_initial_window_size, - old_initial_window_size); -} - -int nghttp2_stream_update_local_initial_window_size( - nghttp2_stream *stream, int32_t new_initial_window_size, - int32_t old_initial_window_size) { - return update_initial_window_size(&stream->local_window_size, - new_initial_window_size, - old_initial_window_size); -} - -void nghttp2_stream_promise_fulfilled(nghttp2_stream *stream) { - stream->state = NGHTTP2_STREAM_OPENED; - stream->flags &= ~NGHTTP2_STREAM_FLAG_PUSH; -} - -nghttp2_stream *nghttp2_stream_get_dep_root(nghttp2_stream *stream) { - for (;;) { - if (stream->sib_prev) { - stream = stream->sib_prev; - - continue; - } - - if (stream->dep_prev) { - stream = stream->dep_prev; - - continue; - } - - break; - } - - return stream; -} - -int nghttp2_stream_dep_subtree_find(nghttp2_stream *stream, - nghttp2_stream *target) { - if (stream == NULL) { - return 0; - } - - if (stream == target) { - return 1; - } - - if (nghttp2_stream_dep_subtree_find(stream->sib_next, target)) { - return 1; - } - - return nghttp2_stream_dep_subtree_find(stream->dep_next, target); -} - -void nghttp2_stream_dep_insert(nghttp2_stream *dep_stream, - nghttp2_stream *stream) { - nghttp2_stream *si; - nghttp2_stream *root_stream; - - assert(stream->item == NULL); - - DEBUGF(fprintf(stderr, - "stream: dep_insert dep_stream(%p)=%d, stream(%p)=%d\n", - dep_stream, dep_stream->stream_id, stream, stream->stream_id)); - - stream->sum_dep_weight = dep_stream->sum_dep_weight; - dep_stream->sum_dep_weight = stream->weight; - - if (dep_stream->dep_next) { - for (si = dep_stream->dep_next; si; si = si->sib_next) { - stream->num_substreams += si->num_substreams; - } - - stream->dep_next = dep_stream->dep_next; - stream->dep_next->dep_prev = stream; - } - - dep_stream->dep_next = stream; - stream->dep_prev = dep_stream; - - root_stream = stream_update_dep_length(dep_stream, 1); - - stream_update_dep_sum_norest_weight(root_stream); - stream_update_dep_effective_weight(root_stream); - - ++stream->roots->num_streams; -} - -static void link_dep(nghttp2_stream *dep_stream, nghttp2_stream *stream) { - dep_stream->dep_next = stream; - stream->dep_prev = dep_stream; -} - -static void link_sib(nghttp2_stream *prev_stream, nghttp2_stream *stream) { - prev_stream->sib_next = stream; - stream->sib_prev = prev_stream; -} - -static void insert_link_dep(nghttp2_stream *dep_stream, - nghttp2_stream *stream) { - nghttp2_stream *sib_next; - - assert(stream->sib_prev == NULL); - - sib_next = dep_stream->dep_next; - - link_sib(stream, sib_next); - - sib_next->dep_prev = NULL; - - link_dep(dep_stream, stream); -} - -static void unlink_sib(nghttp2_stream *stream) { - nghttp2_stream *prev, *next, *dep_next; - - prev = stream->sib_prev; - dep_next = stream->dep_next; - - assert(prev); - - if (dep_next) { - /* - * prev--stream(--sib_next--...) - * | - * dep_next - */ - dep_next->dep_prev = NULL; - - link_sib(prev, dep_next); - - if (stream->sib_next) { - link_sib(stream_last_sib(dep_next), stream->sib_next); - } - } else { - /* - * prev--stream(--sib_next--...) - */ - next = stream->sib_next; - - prev->sib_next = next; - - if (next) { - next->sib_prev = prev; - } - } -} - -static void unlink_dep(nghttp2_stream *stream) { - nghttp2_stream *prev, *next, *dep_next; - - prev = stream->dep_prev; - dep_next = stream->dep_next; - - assert(prev); - - if (dep_next) { - /* - * prev - * | - * stream(--sib_next--...) - * | - * dep_next - */ - link_dep(prev, dep_next); - - if (stream->sib_next) { - link_sib(stream_last_sib(dep_next), stream->sib_next); - } - } else if (stream->sib_next) { - /* - * prev - * | - * stream--sib_next - */ - next = stream->sib_next; - - next->sib_prev = NULL; - - link_dep(prev, next); - } else { - prev->dep_next = NULL; - } -} - -void nghttp2_stream_dep_add(nghttp2_stream *dep_stream, - nghttp2_stream *stream) { - nghttp2_stream *root_stream; - - assert(stream->item == NULL); - - DEBUGF(fprintf(stderr, "stream: dep_add dep_stream(%p)=%d, stream(%p)=%d\n", - dep_stream, dep_stream->stream_id, stream, stream->stream_id)); - - root_stream = stream_update_dep_length(dep_stream, 1); - - dep_stream->sum_dep_weight += stream->weight; - - if (dep_stream->dep_next == NULL) { - link_dep(dep_stream, stream); - } else { - insert_link_dep(dep_stream, stream); - } - - stream_update_dep_sum_norest_weight(root_stream); - stream_update_dep_effective_weight(root_stream); - - ++stream->roots->num_streams; -} - -void nghttp2_stream_dep_remove(nghttp2_stream *stream) { - nghttp2_stream *prev, *next, *dep_prev, *si, *root_stream; - int32_t sum_dep_weight_delta; - - root_stream = NULL; - - DEBUGF(fprintf(stderr, "stream: dep_remove stream(%p)=%d\n", stream, - stream->stream_id)); - - /* Distribute weight of |stream| to direct descendants */ - sum_dep_weight_delta = -stream->weight; - - for (si = stream->dep_next; si; si = si->sib_next) { - si->weight = nghttp2_stream_dep_distributed_weight(stream, si->weight); - - sum_dep_weight_delta += si->weight; - } - - prev = stream_first_sib(stream); - - dep_prev = prev->dep_prev; - - if (dep_prev) { - root_stream = stream_update_dep_length(dep_prev, -1); - - dep_prev->sum_dep_weight += sum_dep_weight_delta; - } - - if (stream->sib_prev) { - unlink_sib(stream); - } else if (stream->dep_prev) { - unlink_dep(stream); - } else { - nghttp2_stream_roots_remove(stream->roots, stream); - - /* stream is a root of tree. Removing stream makes its - descendants a root of its own subtree. */ - - for (si = stream->dep_next; si;) { - next = si->sib_next; - - si->dep_prev = NULL; - si->sib_prev = NULL; - si->sib_next = NULL; - - /* We already distributed weight of |stream| to this. */ - si->effective_weight = si->weight; - - nghttp2_stream_roots_add(si->roots, si); - - si = next; - } - } - - if (root_stream) { - stream_update_dep_sum_norest_weight(root_stream); - stream_update_dep_effective_weight(root_stream); - } - - stream->num_substreams = 1; - stream->sum_dep_weight = 0; - - stream->dep_prev = NULL; - stream->dep_next = NULL; - stream->sib_prev = NULL; - stream->sib_next = NULL; - - --stream->roots->num_streams; -} - -int nghttp2_stream_dep_insert_subtree(nghttp2_stream *dep_stream, - nghttp2_stream *stream, - nghttp2_session *session) { - nghttp2_stream *last_sib; - nghttp2_stream *dep_next; - nghttp2_stream *root_stream; - size_t delta_substreams; - - DEBUGF(fprintf(stderr, "stream: dep_insert_subtree dep_stream(%p)=%d " - "stream(%p)=%d\n", - dep_stream, dep_stream->stream_id, stream, stream->stream_id)); - - delta_substreams = stream->num_substreams; - - stream_update_dep_set_rest(stream); - - if (dep_stream->dep_next) { - /* dep_stream->num_substreams includes dep_stream itself */ - stream->num_substreams += dep_stream->num_substreams - 1; - - stream->sum_dep_weight += dep_stream->sum_dep_weight; - dep_stream->sum_dep_weight = stream->weight; - - dep_next = dep_stream->dep_next; - - stream_update_dep_set_rest(dep_next); - - link_dep(dep_stream, stream); - - if (stream->dep_next) { - last_sib = stream_last_sib(stream->dep_next); - - link_sib(last_sib, dep_next); - - dep_next->dep_prev = NULL; - } else { - link_dep(stream, dep_next); - } - } else { - link_dep(dep_stream, stream); - - assert(dep_stream->sum_dep_weight == 0); - dep_stream->sum_dep_weight = stream->weight; - } - - root_stream = stream_update_dep_length(dep_stream, delta_substreams); - - stream_update_dep_set_top(root_stream); - - stream_update_dep_sum_norest_weight(root_stream); - stream_update_dep_effective_weight(root_stream); - - return stream_update_dep_queue_top(root_stream, session); -} - -int nghttp2_stream_dep_add_subtree(nghttp2_stream *dep_stream, - nghttp2_stream *stream, - nghttp2_session *session) { - nghttp2_stream *root_stream; - - DEBUGF(fprintf(stderr, "stream: dep_add_subtree dep_stream(%p)=%d " - "stream(%p)=%d\n", - dep_stream, dep_stream->stream_id, stream, stream->stream_id)); - - stream_update_dep_set_rest(stream); - - if (dep_stream->dep_next) { - dep_stream->sum_dep_weight += stream->weight; - - insert_link_dep(dep_stream, stream); - } else { - link_dep(dep_stream, stream); - - assert(dep_stream->sum_dep_weight == 0); - dep_stream->sum_dep_weight = stream->weight; - } - - root_stream = stream_update_dep_length(dep_stream, stream->num_substreams); - - stream_update_dep_set_top(root_stream); - - stream_update_dep_sum_norest_weight(root_stream); - stream_update_dep_effective_weight(root_stream); - - return stream_update_dep_queue_top(root_stream, session); -} - -void nghttp2_stream_dep_remove_subtree(nghttp2_stream *stream) { - nghttp2_stream *prev, *next, *dep_prev, *root_stream; - - DEBUGF(fprintf(stderr, "stream: dep_remove_subtree stream(%p)=%d\n", stream, - stream->stream_id)); - - if (stream->sib_prev) { - prev = stream->sib_prev; - - prev->sib_next = stream->sib_next; - if (prev->sib_next) { - prev->sib_next->sib_prev = prev; - } - - prev = stream_first_sib(prev); - - dep_prev = prev->dep_prev; - - } else if (stream->dep_prev) { - dep_prev = stream->dep_prev; - next = stream->sib_next; - - dep_prev->dep_next = next; - - if (next) { - next->dep_prev = dep_prev; - - next->sib_prev = NULL; - } - - } else { - nghttp2_stream_roots_remove(stream->roots, stream); - - dep_prev = NULL; - } - - if (dep_prev) { - dep_prev->sum_dep_weight -= stream->weight; - - root_stream = stream_update_dep_length(dep_prev, -stream->num_substreams); - - stream_update_dep_sum_norest_weight(root_stream); - stream_update_dep_effective_weight(root_stream); - } - - stream->sib_prev = NULL; - stream->sib_next = NULL; - stream->dep_prev = NULL; -} - -int nghttp2_stream_dep_make_root(nghttp2_stream *stream, - nghttp2_session *session) { - DEBUGF(fprintf(stderr, "stream: dep_make_root stream(%p)=%d\n", stream, - stream->stream_id)); - - nghttp2_stream_roots_add(stream->roots, stream); - - stream_update_dep_set_rest(stream); - - stream->effective_weight = stream->weight; - - stream_update_dep_set_top(stream); - - stream_update_dep_sum_norest_weight(stream); - stream_update_dep_effective_weight(stream); - - return stream_update_dep_queue_top(stream, session); -} - -int -nghttp2_stream_dep_all_your_stream_are_belong_to_us(nghttp2_stream *stream, - nghttp2_session *session) { - nghttp2_stream *first, *si; - - DEBUGF(fprintf(stderr, "stream: ALL YOUR STREAM ARE BELONG TO US " - "stream(%p)=%d\n", - stream, stream->stream_id)); - - first = stream->roots->head; - - /* stream must not be include in stream->roots->head list */ - assert(first != stream); - - if (first) { - nghttp2_stream *prev; - - prev = first; - - DEBUGF(fprintf(stderr, "stream: root stream(%p)=%d\n", first, - first->stream_id)); - - stream->sum_dep_weight += first->weight; - stream->num_substreams += first->num_substreams; - - for (si = first->root_next; si; si = si->root_next) { - - assert(si != stream); - - DEBUGF( - fprintf(stderr, "stream: root stream(%p)=%d\n", si, si->stream_id)); - - stream->sum_dep_weight += si->weight; - stream->num_substreams += si->num_substreams; - - link_sib(prev, si); - - prev = si; - } - - if (stream->dep_next) { - nghttp2_stream *sib_next; - - sib_next = stream->dep_next; - - sib_next->dep_prev = NULL; - - link_sib(first, sib_next); - link_dep(stream, prev); - } else { - link_dep(stream, first); - } - } - - nghttp2_stream_roots_remove_all(stream->roots); - - return nghttp2_stream_dep_make_root(stream, session); -} - -int nghttp2_stream_in_dep_tree(nghttp2_stream *stream) { - return stream->dep_prev || stream->dep_next || stream->sib_prev || - stream->sib_next || stream->root_next || stream->root_prev || - stream->roots->head == stream; -} - -void nghttp2_stream_roots_init(nghttp2_stream_roots *roots) { - roots->head = NULL; - roots->num_streams = 0; -} - -void nghttp2_stream_roots_free(nghttp2_stream_roots *roots _U_) {} - -void nghttp2_stream_roots_add(nghttp2_stream_roots *roots, - nghttp2_stream *stream) { - if (roots->head) { - stream->root_next = roots->head; - roots->head->root_prev = stream; - } - - roots->head = stream; -} - -void nghttp2_stream_roots_remove(nghttp2_stream_roots *roots, - nghttp2_stream *stream) { - nghttp2_stream *root_prev, *root_next; - - root_prev = stream->root_prev; - root_next = stream->root_next; - - if (root_prev) { - root_prev->root_next = root_next; - - if (root_next) { - root_next->root_prev = root_prev; - } - } else { - if (root_next) { - root_next->root_prev = NULL; - } - - roots->head = root_next; - } - - stream->root_prev = NULL; - stream->root_next = NULL; -} - -void nghttp2_stream_roots_remove_all(nghttp2_stream_roots *roots) { - nghttp2_stream *si, *next; - - for (si = roots->head; si;) { - next = si->root_next; - - si->root_prev = NULL; - si->root_next = NULL; - - si = next; - } - - roots->head = NULL; -} diff --git a/lib/nghttp2_stream.h b/lib/nghttp2_stream.h deleted file mode 100644 index 684459b..0000000 --- a/lib/nghttp2_stream.h +++ /dev/null @@ -1,486 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2012 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#ifndef NGHTTP2_STREAM_H -#define NGHTTP2_STREAM_H - -#ifdef HAVE_CONFIG_H -#include -#endif /* HAVE_CONFIG_H */ - -#include -#include "nghttp2_outbound_item.h" -#include "nghttp2_map.h" -#include "nghttp2_pq.h" -#include "nghttp2_int.h" - -/* - * Maximum number of streams in one dependency tree. - */ -#define NGHTTP2_MAX_DEP_TREE_LENGTH 100 - -/* - * If local peer is stream initiator: - * NGHTTP2_STREAM_OPENING : upon sending request HEADERS - * NGHTTP2_STREAM_OPENED : upon receiving response HEADERS - * NGHTTP2_STREAM_CLOSING : upon queuing RST_STREAM - * - * If remote peer is stream initiator: - * NGHTTP2_STREAM_OPENING : upon receiving request HEADERS - * NGHTTP2_STREAM_OPENED : upon sending response HEADERS - * NGHTTP2_STREAM_CLOSING : upon queuing RST_STREAM - */ -typedef enum { - /* Initial state */ - NGHTTP2_STREAM_INITIAL, - /* For stream initiator: request HEADERS has been sent, but response - HEADERS has not been received yet. For receiver: request HEADERS - has been received, but it does not send response HEADERS yet. */ - NGHTTP2_STREAM_OPENING, - /* For stream initiator: response HEADERS is received. For receiver: - response HEADERS is sent. */ - NGHTTP2_STREAM_OPENED, - /* RST_STREAM is received, but somehow we need to keep stream in - memory. */ - NGHTTP2_STREAM_CLOSING, - /* PUSH_PROMISE is received or sent */ - NGHTTP2_STREAM_RESERVED, - /* Stream is created in this state if it is used as anchor in - dependency tree. */ - NGHTTP2_STREAM_IDLE -} nghttp2_stream_state; - -typedef enum { - NGHTTP2_SHUT_NONE = 0, - /* Indicates further receptions will be disallowed. */ - NGHTTP2_SHUT_RD = 0x01, - /* Indicates further transmissions will be disallowed. */ - NGHTTP2_SHUT_WR = 0x02, - /* Indicates both further receptions and transmissions will be - disallowed. */ - NGHTTP2_SHUT_RDWR = NGHTTP2_SHUT_RD | NGHTTP2_SHUT_WR -} nghttp2_shut_flag; - -typedef enum { - NGHTTP2_STREAM_FLAG_NONE = 0, - /* Indicates that this stream is pushed stream and not opened - yet. */ - NGHTTP2_STREAM_FLAG_PUSH = 0x01, - /* Indicates that this stream was closed */ - NGHTTP2_STREAM_FLAG_CLOSED = 0x02, - /* Indicates the item is deferred due to flow control. */ - NGHTTP2_STREAM_FLAG_DEFERRED_FLOW_CONTROL = 0x04, - /* Indicates the item is deferred by user callback */ - NGHTTP2_STREAM_FLAG_DEFERRED_USER = 0x08, - /* bitwise OR of NGHTTP2_STREAM_FLAG_DEFERRED_FLOW_CONTROL and - NGHTTP2_STREAM_FLAG_DEFERRED_USER. */ - NGHTTP2_STREAM_FLAG_DEFERRED_ALL = 0x0c - -} nghttp2_stream_flag; - -/* HTTP related flags to enforce HTTP semantics */ -typedef enum { - NGHTTP2_HTTP_FLAG_NONE = 0, - /* header field seen so far */ - NGHTTP2_HTTP_FLAG__AUTHORITY = 1, - NGHTTP2_HTTP_FLAG__PATH = 1 << 1, - NGHTTP2_HTTP_FLAG__METHOD = 1 << 2, - NGHTTP2_HTTP_FLAG__SCHEME = 1 << 3, - /* host is not pseudo header, but we require either host or - :authority */ - NGHTTP2_HTTP_FLAG_HOST = 1 << 4, - NGHTTP2_HTTP_FLAG__STATUS = 1 << 5, - /* required header fields for HTTP request except for CONNECT - method. */ - NGHTTP2_HTTP_FLAG_REQ_HEADERS = NGHTTP2_HTTP_FLAG__METHOD | - NGHTTP2_HTTP_FLAG__PATH | - NGHTTP2_HTTP_FLAG__SCHEME, - NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED = 1 << 6, - /* HTTP method flags */ - NGHTTP2_HTTP_FLAG_METH_CONNECT = 1 << 7, - NGHTTP2_HTTP_FLAG_METH_HEAD = 1 << 8, - NGHTTP2_HTTP_FLAG_METH_OPTIONS = 1 << 9, - NGHTTP2_HTTP_FLAG_METH_ALL = NGHTTP2_HTTP_FLAG_METH_CONNECT | - NGHTTP2_HTTP_FLAG_METH_HEAD | - NGHTTP2_HTTP_FLAG_METH_OPTIONS, - /* :path category */ - /* path starts with "/" */ - NGHTTP2_HTTP_FLAG_PATH_REGULAR = 1 << 10, - /* path "*" */ - NGHTTP2_HTTP_FLAG_PATH_ASTERISK = 1 << 11, - /* scheme */ - /* "http" or "https" scheme */ - NGHTTP2_HTTP_FLAG_SCHEME_HTTP = 1 << 12, - /* set if final response is expected */ - NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE = 1 << 13 -} nghttp2_http_flag; - -typedef enum { - NGHTTP2_STREAM_DPRI_NONE = 0, - NGHTTP2_STREAM_DPRI_NO_ITEM = 0x01, - NGHTTP2_STREAM_DPRI_TOP = 0x02, - NGHTTP2_STREAM_DPRI_REST = 0x04 -} nghttp2_stream_dpri; - -struct nghttp2_stream_roots; - -typedef struct nghttp2_stream_roots nghttp2_stream_roots; - -struct nghttp2_stream; - -typedef struct nghttp2_stream nghttp2_stream; - -struct nghttp2_stream { - /* Intrusive Map */ - nghttp2_map_entry map_entry; - /* Content-Length of request/response body. -1 if unknown. */ - int64_t content_length; - /* Received body so far */ - int64_t recv_content_length; - /* pointers to form dependency tree. If multiple streams depend on - a stream, only one stream (left most) has non-NULL dep_prev which - points to the stream it depends on. The remaining streams are - linked using sib_prev and sib_next. The stream which has - non-NULL dep_prev always NULL sib_prev. The right most stream - has NULL sib_next. If this stream is a root of dependency tree, - dep_prev and sib_prev are NULL. */ - nghttp2_stream *dep_prev, *dep_next; - nghttp2_stream *sib_prev, *sib_next; - /* pointers to track dependency tree root streams. This is - doubly-linked list and first element is pointed by - roots->head. */ - nghttp2_stream *root_prev, *root_next; - /* When stream is kept after closure, it may be kept in doubly - linked list pointed by nghttp2_session closed_stream_head. - closed_next points to the next stream object if it is the element - of the list. */ - nghttp2_stream *closed_prev, *closed_next; - /* pointer to roots, which tracks dependency tree roots */ - nghttp2_stream_roots *roots; - /* The arbitrary data provided by user for this stream. */ - void *stream_user_data; - /* Item to send */ - nghttp2_outbound_item *item; - /* stream ID */ - int32_t stream_id; - /* categorized priority of this stream. Only stream bearing - NGHTTP2_STREAM_DPRI_TOP can send item. */ - nghttp2_stream_dpri dpri; - /* the number of streams in subtree */ - size_t num_substreams; - /* Current remote window size. This value is computed against the - current initial window size of remote endpoint. */ - int32_t remote_window_size; - /* Keep track of the number of bytes received without - WINDOW_UPDATE. This could be negative after submitting negative - value to WINDOW_UPDATE */ - int32_t recv_window_size; - /* The number of bytes consumed by the application and now is - subject to WINDOW_UPDATE. This is only used when auto - WINDOW_UPDATE is turned off. */ - int32_t consumed_size; - /* The amount of recv_window_size cut using submitting negative - value to WINDOW_UPDATE */ - int32_t recv_reduction; - /* window size for local flow control. It is initially set to - NGHTTP2_INITIAL_WINDOW_SIZE and could be increased/decreased by - submitting WINDOW_UPDATE. See nghttp2_submit_window_update(). */ - int32_t local_window_size; - /* weight of this stream */ - int32_t weight; - /* effective weight of this stream in belonging dependency tree */ - int32_t effective_weight; - /* sum of weight (not effective_weight) of direct descendants */ - int32_t sum_dep_weight; - /* sum of weight of direct descendants which have at least one - descendant with dpri == NGHTTP2_STREAM_DPRI_TOP. We use this - value to calculate effective weight. */ - int32_t sum_norest_weight; - nghttp2_stream_state state; - /* status code from remote server */ - int16_t status_code; - /* Bitwise OR of zero or more nghttp2_http_flag values */ - uint16_t http_flags; - /* This is bitwise-OR of 0 or more of nghttp2_stream_flag. */ - uint8_t flags; - /* Bitwise OR of zero or more nghttp2_shut_flag values */ - uint8_t shut_flags; -}; - -void nghttp2_stream_init(nghttp2_stream *stream, int32_t stream_id, - uint8_t flags, nghttp2_stream_state initial_state, - int32_t weight, nghttp2_stream_roots *roots, - int32_t remote_initial_window_size, - int32_t local_initial_window_size, - void *stream_user_data); - -void nghttp2_stream_free(nghttp2_stream *stream); - -/* - * Disallow either further receptions or transmissions, or both. - * |flag| is bitwise OR of one or more of nghttp2_shut_flag. - */ -void nghttp2_stream_shutdown(nghttp2_stream *stream, nghttp2_shut_flag flag); - -/* - * Defer |stream->item|. We won't call this function in the situation - * where |stream->item| == NULL. The |flags| is bitwise OR of zero or - * more of NGHTTP2_STREAM_FLAG_DEFERRED_USER and - * NGHTTP2_STREAM_FLAG_DEFERRED_FLOW_CONTROL. The |flags| indicates - * the reason of this action. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP2_ERR_NOMEM - * Out of memory - */ -int nghttp2_stream_defer_item(nghttp2_stream *stream, uint8_t flags, - nghttp2_session *session); - -/* - * Put back deferred data in this stream to active state. The |flags| - * are one or more of bitwise OR of the following values: - * NGHTTP2_STREAM_FLAG_DEFERRED_USER and - * NGHTTP2_STREAM_FLAG_DEFERRED_FLOW_CONTROL and given masks are - * cleared if they are set. So even if this function is called, if - * one of flag is still set, data does not become active. - */ -int nghttp2_stream_resume_deferred_item(nghttp2_stream *stream, uint8_t flags, - nghttp2_session *session); - -/* - * Returns nonzero if item is deferred by whatever reason. - */ -int nghttp2_stream_check_deferred_item(nghttp2_stream *stream); - -/* - * Returns nonzero if item is deferred by flow control. - */ -int nghttp2_stream_check_deferred_by_flow_control(nghttp2_stream *stream); - -/* - * Updates the remote window size with the new value - * |new_initial_window_size|. The |old_initial_window_size| is used to - * calculate the current window size. - * - * This function returns 0 if it succeeds or -1. The failure is due to - * overflow. - */ -int nghttp2_stream_update_remote_initial_window_size( - nghttp2_stream *stream, int32_t new_initial_window_size, - int32_t old_initial_window_size); - -/* - * Updates the local window size with the new value - * |new_initial_window_size|. The |old_initial_window_size| is used to - * calculate the current window size. - * - * This function returns 0 if it succeeds or -1. The failure is due to - * overflow. - */ -int nghttp2_stream_update_local_initial_window_size( - nghttp2_stream *stream, int32_t new_initial_window_size, - int32_t old_initial_window_size); - -/* - * Call this function if promised stream |stream| is replied with - * HEADERS. This function makes the state of the |stream| to - * NGHTTP2_STREAM_OPENED. - */ -void nghttp2_stream_promise_fulfilled(nghttp2_stream *stream); - -/* - * Returns the stream positioned in root of the dependency tree the - * |stream| belongs to. - */ -nghttp2_stream *nghttp2_stream_get_dep_root(nghttp2_stream *stream); - -/* - * Returns nonzero if |target| is found in subtree of |stream|. - */ -int nghttp2_stream_dep_subtree_find(nghttp2_stream *stream, - nghttp2_stream *target); - -/* - * Computes distributed weight of a stream of the |weight| under the - * |stream| if |stream| is removed from a dependency tree. The result - * is computed using stream->weight rather than - * stream->effective_weight. - */ -int32_t nghttp2_stream_dep_distributed_weight(nghttp2_stream *stream, - int32_t weight); - -/* - * Computes effective weight of a stream of the |weight| under the - * |stream|. The result is computed using stream->effective_weight - * rather than stream->weight. This function is used to determine - * weight in dependency tree. - */ -int32_t nghttp2_stream_dep_distributed_effective_weight(nghttp2_stream *stream, - int32_t weight); - -/* - * Makes the |stream| depend on the |dep_stream|. This dependency is - * exclusive. All existing direct descendants of |dep_stream| become - * the descendants of the |stream|. This function assumes - * |stream->data| is NULL and no dpri members are changed in this - * dependency tree. - */ -void nghttp2_stream_dep_insert(nghttp2_stream *dep_stream, - nghttp2_stream *stream); - -/* - * Makes the |stream| depend on the |dep_stream|. This dependency is - * not exclusive. This function assumes |stream->data| is NULL and no - * dpri members are changed in this dependency tree. - */ -void nghttp2_stream_dep_add(nghttp2_stream *dep_stream, nghttp2_stream *stream); - -/* - * Removes the |stream| from the current dependency tree. This - * function assumes |stream->data| is NULL. - */ -void nghttp2_stream_dep_remove(nghttp2_stream *stream); - -/* - * Attaches |item| to |stream|. Updates dpri members in this - * dependency tree. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP2_ERR_NOMEM - * Out of memory - */ -int nghttp2_stream_attach_item(nghttp2_stream *stream, - nghttp2_outbound_item *item, - nghttp2_session *session); - -/* - * Detaches |stream->item|. Updates dpri members in this dependency - * tree. This function does not free |stream->item|. The caller must - * free it. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP2_ERR_NOMEM - * Out of memory - */ -int nghttp2_stream_detach_item(nghttp2_stream *stream, - nghttp2_session *session); - -/* - * Makes the |stream| depend on the |dep_stream|. This dependency is - * exclusive. Updates dpri members in this dependency tree. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP2_ERR_NOMEM - * Out of memory - */ -int nghttp2_stream_dep_insert_subtree(nghttp2_stream *dep_stream, - nghttp2_stream *stream, - nghttp2_session *session); - -/* - * Makes the |stream| depend on the |dep_stream|. This dependency is - * not exclusive. Updates dpri members in this dependency tree. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP2_ERR_NOMEM - * Out of memory - */ -int nghttp2_stream_dep_add_subtree(nghttp2_stream *dep_stream, - nghttp2_stream *stream, - nghttp2_session *session); - -/* - * Removes subtree whose root stream is |stream|. Removing subtree - * does not change dpri values. The effective_weight of streams in - * removed subtree is not updated. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP2_ERR_NOMEM - * Out of memory - */ -void nghttp2_stream_dep_remove_subtree(nghttp2_stream *stream); - -/* - * Makes the |stream| as root. Updates dpri members in this - * dependency tree. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP2_ERR_NOMEM - * Out of memory - */ -int nghttp2_stream_dep_make_root(nghttp2_stream *stream, - nghttp2_session *session); - -/* - * Makes the |stream| as root and all existing root streams become - * direct children of |stream|. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP2_ERR_NOMEM - * Out of memory - */ -int -nghttp2_stream_dep_all_your_stream_are_belong_to_us(nghttp2_stream *stream, - nghttp2_session *session); - -/* - * Returns nonzero if |stream| is in any dependency tree. - */ -int nghttp2_stream_in_dep_tree(nghttp2_stream *stream); - -struct nghttp2_stream_roots { - nghttp2_stream *head; - - int32_t num_streams; -}; - -void nghttp2_stream_roots_init(nghttp2_stream_roots *roots); - -void nghttp2_stream_roots_free(nghttp2_stream_roots *roots); - -void nghttp2_stream_roots_add(nghttp2_stream_roots *roots, - nghttp2_stream *stream); - -void nghttp2_stream_roots_remove(nghttp2_stream_roots *roots, - nghttp2_stream *stream); - -void nghttp2_stream_roots_remove_all(nghttp2_stream_roots *roots); - -#endif /* NGHTTP2_STREAM */ diff --git a/lib/nghttp2_submit.c b/lib/nghttp2_submit.c deleted file mode 100644 index 0f6d9f5..0000000 --- a/lib/nghttp2_submit.c +++ /dev/null @@ -1,479 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2012, 2013 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#include "nghttp2_submit.h" - -#include -#include - -#include "nghttp2_session.h" -#include "nghttp2_frame.h" -#include "nghttp2_helper.h" -#include "nghttp2_priority_spec.h" - -/* This function takes ownership of |nva_copy|. Regardless of the - return value, the caller must not free |nva_copy| after this - function returns. */ -static int32_t submit_headers_shared(nghttp2_session *session, uint8_t flags, - int32_t stream_id, - const nghttp2_priority_spec *pri_spec, - nghttp2_nv *nva_copy, size_t nvlen, - const nghttp2_data_provider *data_prd, - void *stream_user_data, - uint8_t attach_stream) { - int rv; - uint8_t flags_copy; - nghttp2_outbound_item *item = NULL; - nghttp2_frame *frame = NULL; - nghttp2_headers_category hcat; - nghttp2_mem *mem; - - mem = &session->mem; - - if (stream_id == 0) { - rv = NGHTTP2_ERR_INVALID_ARGUMENT; - goto fail; - } - - item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item)); - if (item == NULL) { - rv = NGHTTP2_ERR_NOMEM; - goto fail; - } - - nghttp2_outbound_item_init(item); - - if (data_prd != NULL && data_prd->read_callback != NULL) { - item->aux_data.headers.data_prd = *data_prd; - } - - item->aux_data.headers.stream_user_data = stream_user_data; - item->aux_data.headers.attach_stream = attach_stream; - - flags_copy = (flags & (NGHTTP2_FLAG_END_STREAM | NGHTTP2_FLAG_PRIORITY)) | - NGHTTP2_FLAG_END_HEADERS; - - if (stream_id == -1) { - if (session->next_stream_id > INT32_MAX) { - rv = NGHTTP2_ERR_STREAM_ID_NOT_AVAILABLE; - goto fail; - } - - stream_id = session->next_stream_id; - session->next_stream_id += 2; - - hcat = NGHTTP2_HCAT_REQUEST; - } else { - /* More specific categorization will be done later. */ - hcat = NGHTTP2_HCAT_HEADERS; - } - - frame = &item->frame; - - nghttp2_frame_headers_init(&frame->headers, flags_copy, stream_id, hcat, - pri_spec, nva_copy, nvlen); - - rv = nghttp2_session_add_item(session, item); - - if (rv != 0) { - nghttp2_frame_headers_free(&frame->headers, mem); - goto fail2; - } - - if (hcat == NGHTTP2_HCAT_REQUEST) { - return stream_id; - } - - return 0; - -fail: - /* nghttp2_frame_headers_init() takes ownership of nva_copy. */ - nghttp2_nv_array_del(nva_copy, mem); -fail2: - nghttp2_mem_free(mem, item); - - return rv; -} - -static void adjust_priority_spec_weight(nghttp2_priority_spec *pri_spec) { - if (pri_spec->weight < NGHTTP2_MIN_WEIGHT) { - pri_spec->weight = NGHTTP2_MIN_WEIGHT; - } else if (pri_spec->weight > NGHTTP2_MAX_WEIGHT) { - pri_spec->weight = NGHTTP2_MAX_WEIGHT; - } -} - -static int32_t submit_headers_shared_nva(nghttp2_session *session, - uint8_t flags, int32_t stream_id, - const nghttp2_priority_spec *pri_spec, - const nghttp2_nv *nva, size_t nvlen, - const nghttp2_data_provider *data_prd, - void *stream_user_data, - uint8_t attach_stream) { - int rv; - nghttp2_nv *nva_copy; - nghttp2_priority_spec copy_pri_spec; - nghttp2_mem *mem; - - mem = &session->mem; - - if (pri_spec) { - copy_pri_spec = *pri_spec; - adjust_priority_spec_weight(©_pri_spec); - } else { - nghttp2_priority_spec_default_init(©_pri_spec); - } - - rv = nghttp2_nv_array_copy(&nva_copy, nva, nvlen, mem); - if (rv < 0) { - return rv; - } - - return submit_headers_shared(session, flags, stream_id, ©_pri_spec, - nva_copy, nvlen, data_prd, stream_user_data, - attach_stream); -} - -int32_t nghttp2_submit_trailer(nghttp2_session *session, int32_t stream_id, - const nghttp2_nv *nva, size_t nvlen) { - return submit_headers_shared_nva(session, NGHTTP2_FLAG_END_STREAM, stream_id, - NULL, nva, nvlen, NULL, NULL, 0); -} - -int32_t nghttp2_submit_headers(nghttp2_session *session, uint8_t flags, - int32_t stream_id, - const nghttp2_priority_spec *pri_spec, - const nghttp2_nv *nva, size_t nvlen, - void *stream_user_data) { - flags &= NGHTTP2_FLAG_END_STREAM; - - if (pri_spec && !nghttp2_priority_spec_check_default(pri_spec)) { - flags |= NGHTTP2_FLAG_PRIORITY; - } else { - pri_spec = NULL; - } - - return submit_headers_shared_nva(session, flags, stream_id, pri_spec, nva, - nvlen, NULL, stream_user_data, 0); -} - -int nghttp2_submit_ping(nghttp2_session *session, uint8_t flags _U_, - const uint8_t *opaque_data) { - return nghttp2_session_add_ping(session, NGHTTP2_FLAG_NONE, opaque_data); -} - -int nghttp2_submit_priority(nghttp2_session *session, uint8_t flags _U_, - int32_t stream_id, - const nghttp2_priority_spec *pri_spec) { - int rv; - nghttp2_outbound_item *item; - nghttp2_frame *frame; - nghttp2_priority_spec copy_pri_spec; - nghttp2_mem *mem; - - mem = &session->mem; - - if (stream_id == 0 || pri_spec == NULL) { - return NGHTTP2_ERR_INVALID_ARGUMENT; - } - - if (stream_id == pri_spec->stream_id) { - return NGHTTP2_ERR_INVALID_ARGUMENT; - } - - copy_pri_spec = *pri_spec; - - adjust_priority_spec_weight(©_pri_spec); - - item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item)); - - if (item == NULL) { - return NGHTTP2_ERR_NOMEM; - } - - nghttp2_outbound_item_init(item); - - frame = &item->frame; - - nghttp2_frame_priority_init(&frame->priority, stream_id, ©_pri_spec); - - rv = nghttp2_session_add_item(session, item); - - if (rv != 0) { - nghttp2_frame_priority_free(&frame->priority); - nghttp2_mem_free(mem, item); - - return rv; - } - - return 0; -} - -int nghttp2_submit_rst_stream(nghttp2_session *session, uint8_t flags _U_, - int32_t stream_id, uint32_t error_code) { - if (stream_id == 0) { - return NGHTTP2_ERR_INVALID_ARGUMENT; - } - - return nghttp2_session_add_rst_stream(session, stream_id, error_code); -} - -int nghttp2_submit_goaway(nghttp2_session *session, uint8_t flags _U_, - int32_t last_stream_id, uint32_t error_code, - const uint8_t *opaque_data, size_t opaque_data_len) { - if (session->goaway_flags & NGHTTP2_GOAWAY_TERM_ON_SEND) { - return 0; - } - return nghttp2_session_add_goaway(session, last_stream_id, error_code, - opaque_data, opaque_data_len, - NGHTTP2_GOAWAY_AUX_NONE); -} - -int nghttp2_submit_shutdown_notice(nghttp2_session *session) { - if (!session->server) { - return NGHTTP2_ERR_INVALID_STATE; - } - if (session->goaway_flags) { - return 0; - } - return nghttp2_session_add_goaway(session, (1u << 31) - 1, NGHTTP2_NO_ERROR, - NULL, 0, - NGHTTP2_GOAWAY_AUX_SHUTDOWN_NOTICE); -} - -int nghttp2_submit_settings(nghttp2_session *session, uint8_t flags _U_, - const nghttp2_settings_entry *iv, size_t niv) { - return nghttp2_session_add_settings(session, NGHTTP2_FLAG_NONE, iv, niv); -} - -int32_t nghttp2_submit_push_promise(nghttp2_session *session, uint8_t flags _U_, - int32_t stream_id, const nghttp2_nv *nva, - size_t nvlen, - void *promised_stream_user_data) { - nghttp2_outbound_item *item; - nghttp2_frame *frame; - nghttp2_nv *nva_copy; - uint8_t flags_copy; - int32_t promised_stream_id; - int rv; - nghttp2_mem *mem; - - mem = &session->mem; - - if (stream_id == 0 || nghttp2_session_is_my_stream_id(session, stream_id)) { - return NGHTTP2_ERR_INVALID_ARGUMENT; - } - - if (!session->server) { - return NGHTTP2_ERR_PROTO; - } - - /* All 32bit signed stream IDs are spent. */ - if (session->next_stream_id > INT32_MAX) { - return NGHTTP2_ERR_STREAM_ID_NOT_AVAILABLE; - } - - item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item)); - if (item == NULL) { - return NGHTTP2_ERR_NOMEM; - } - - nghttp2_outbound_item_init(item); - - item->aux_data.headers.stream_user_data = promised_stream_user_data; - - frame = &item->frame; - - rv = nghttp2_nv_array_copy(&nva_copy, nva, nvlen, mem); - if (rv < 0) { - nghttp2_mem_free(mem, item); - return rv; - } - - flags_copy = NGHTTP2_FLAG_END_HEADERS; - - promised_stream_id = session->next_stream_id; - session->next_stream_id += 2; - - nghttp2_frame_push_promise_init(&frame->push_promise, flags_copy, stream_id, - promised_stream_id, nva_copy, nvlen); - - rv = nghttp2_session_add_item(session, item); - - if (rv != 0) { - nghttp2_frame_push_promise_free(&frame->push_promise, mem); - nghttp2_mem_free(mem, item); - - return rv; - } - - return promised_stream_id; -} - -int nghttp2_submit_window_update(nghttp2_session *session, uint8_t flags, - int32_t stream_id, - int32_t window_size_increment) { - int rv; - nghttp2_stream *stream = 0; - if (window_size_increment == 0) { - return 0; - } - flags = 0; - if (stream_id == 0) { - rv = nghttp2_adjust_local_window_size( - &session->local_window_size, &session->recv_window_size, - &session->recv_reduction, &window_size_increment); - if (rv != 0) { - return rv; - } - } else { - stream = nghttp2_session_get_stream(session, stream_id); - if (!stream) { - return 0; - } - - rv = nghttp2_adjust_local_window_size( - &stream->local_window_size, &stream->recv_window_size, - &stream->recv_reduction, &window_size_increment); - if (rv != 0) { - return rv; - } - } - - if (window_size_increment > 0) { - if (stream_id == 0) { - session->consumed_size = - nghttp2_max(0, session->consumed_size - window_size_increment); - } else { - stream->consumed_size = - nghttp2_max(0, stream->consumed_size - window_size_increment); - } - - return nghttp2_session_add_window_update(session, flags, stream_id, - window_size_increment); - } - return 0; -} - -static uint8_t set_request_flags(const nghttp2_priority_spec *pri_spec, - const nghttp2_data_provider *data_prd) { - uint8_t flags = NGHTTP2_FLAG_NONE; - if (data_prd == NULL || data_prd->read_callback == NULL) { - flags |= NGHTTP2_FLAG_END_STREAM; - } - - if (pri_spec) { - flags |= NGHTTP2_FLAG_PRIORITY; - } - - return flags; -} - -int32_t nghttp2_submit_request(nghttp2_session *session, - const nghttp2_priority_spec *pri_spec, - const nghttp2_nv *nva, size_t nvlen, - const nghttp2_data_provider *data_prd, - void *stream_user_data) { - uint8_t flags; - - if (pri_spec && nghttp2_priority_spec_check_default(pri_spec)) { - pri_spec = NULL; - } - - flags = set_request_flags(pri_spec, data_prd); - - return submit_headers_shared_nva(session, flags, -1, pri_spec, nva, nvlen, - data_prd, stream_user_data, 0); -} - -static uint8_t set_response_flags(const nghttp2_data_provider *data_prd) { - uint8_t flags = NGHTTP2_FLAG_NONE; - if (data_prd == NULL || data_prd->read_callback == NULL) { - flags |= NGHTTP2_FLAG_END_STREAM; - } - return flags; -} - -int nghttp2_submit_response(nghttp2_session *session, int32_t stream_id, - const nghttp2_nv *nva, size_t nvlen, - const nghttp2_data_provider *data_prd) { - uint8_t flags = set_response_flags(data_prd); - return submit_headers_shared_nva(session, flags, stream_id, NULL, nva, nvlen, - data_prd, NULL, 1); -} - -int nghttp2_submit_data(nghttp2_session *session, uint8_t flags, - int32_t stream_id, - const nghttp2_data_provider *data_prd) { - int rv; - nghttp2_outbound_item *item; - nghttp2_frame *frame; - nghttp2_data_aux_data *aux_data; - uint8_t nflags = flags & NGHTTP2_FLAG_END_STREAM; - nghttp2_mem *mem; - - mem = &session->mem; - - if (stream_id == 0) { - return NGHTTP2_ERR_INVALID_ARGUMENT; - } - - item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item)); - if (item == NULL) { - return NGHTTP2_ERR_NOMEM; - } - - nghttp2_outbound_item_init(item); - - frame = &item->frame; - aux_data = &item->aux_data.data; - aux_data->data_prd = *data_prd; - aux_data->eof = 0; - aux_data->flags = nflags; - - /* flags are sent on transmission */ - nghttp2_frame_data_init(&frame->data, NGHTTP2_FLAG_NONE, stream_id); - - rv = nghttp2_session_add_item(session, item); - if (rv != 0) { - nghttp2_frame_data_free(&frame->data); - nghttp2_mem_free(mem, item); - return rv; - } - return 0; -} - -ssize_t nghttp2_pack_settings_payload(uint8_t *buf, size_t buflen, - const nghttp2_settings_entry *iv, - size_t niv) { - if (!nghttp2_iv_check(iv, niv)) { - return NGHTTP2_ERR_INVALID_ARGUMENT; - } - - if (buflen < (niv * NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH)) { - return NGHTTP2_ERR_INSUFF_BUFSIZE; - } - - return nghttp2_frame_pack_settings_payload(buf, iv, niv); -} diff --git a/lib/nghttp2_submit.h b/lib/nghttp2_submit.h deleted file mode 100644 index 545388c..0000000 --- a/lib/nghttp2_submit.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2012 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#ifndef NGHTTP2_SUBMIT_H -#define NGHTTP2_SUBMIT_H - -#ifdef HAVE_CONFIG_H -#include -#endif /* HAVE_CONFIG_H */ - -#include - -#endif /* NGHTTP2_SUBMIT_H */ diff --git a/lib/nghttp2_version.c b/lib/nghttp2_version.c deleted file mode 100644 index 8c5710d..0000000 --- a/lib/nghttp2_version.c +++ /dev/null @@ -1,38 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2012, 2013 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#ifdef HAVE_CONFIG_H -#include -#endif /* HAVE_CONFIG_H */ - -#include - -static nghttp2_info version = {NGHTTP2_VERSION_AGE, NGHTTP2_VERSION_NUM, - NGHTTP2_VERSION, NGHTTP2_PROTO_VERSION_ID}; - -nghttp2_info *nghttp2_version(int least_version) { - if (least_version > NGHTTP2_VERSION_NUM) - return NULL; - return &version; -} diff --git a/ltmain.sh b/ltmain.sh deleted file mode 100644 index bffda54..0000000 --- a/ltmain.sh +++ /dev/null @@ -1,9661 +0,0 @@ - -# libtool (GNU libtool) 2.4.2 -# Written by Gordon Matzigkeit , 1996 - -# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2003, 2004, 2005, 2006, -# 2007, 2008, 2009, 2010, 2011 Free Software Foundation, Inc. -# This is free software; see the source for copying conditions. There is NO -# warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - -# GNU Libtool 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 2 of the License, or -# (at your option) any later version. -# -# As a special exception to the GNU General Public License, -# if you distribute this file as part of a program or library that -# is built using GNU Libtool, you may include this file under the -# same distribution terms that you use for the rest of that program. -# -# GNU Libtool 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 GNU Libtool; see the file COPYING. If not, a copy -# can be downloaded from http://www.gnu.org/licenses/gpl.html, -# or obtained by writing to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - -# Usage: $progname [OPTION]... [MODE-ARG]... -# -# Provide generalized library-building support services. -# -# --config show all configuration variables -# --debug enable verbose shell tracing -# -n, --dry-run display commands without modifying any files -# --features display basic configuration information and exit -# --mode=MODE use operation mode MODE -# --preserve-dup-deps don't remove duplicate dependency libraries -# --quiet, --silent don't print informational messages -# --no-quiet, --no-silent -# print informational messages (default) -# --no-warn don't display warning messages -# --tag=TAG use configuration variables from tag TAG -# -v, --verbose print more informational messages than default -# --no-verbose don't print the extra informational messages -# --version print version information -# -h, --help, --help-all print short, long, or detailed help message -# -# MODE must be one of the following: -# -# clean remove files from the build directory -# compile compile a source file into a libtool object -# execute automatically set library path, then run a program -# finish complete the installation of libtool libraries -# install install libraries or executables -# link create a library or an executable -# uninstall remove libraries from an installed directory -# -# MODE-ARGS vary depending on the MODE. When passed as first option, -# `--mode=MODE' may be abbreviated as `MODE' or a unique abbreviation of that. -# Try `$progname --help --mode=MODE' for a more detailed description of MODE. -# -# When reporting a bug, please describe a test case to reproduce it and -# include the following information: -# -# host-triplet: $host -# shell: $SHELL -# compiler: $LTCC -# compiler flags: $LTCFLAGS -# linker: $LD (gnu? $with_gnu_ld) -# $progname: (GNU libtool) 2.4.2 Debian-2.4.2-1.11 -# automake: $automake_version -# autoconf: $autoconf_version -# -# Report bugs to . -# GNU libtool home page: . -# General help using GNU software: . - -PROGRAM=libtool -PACKAGE=libtool -VERSION="2.4.2 Debian-2.4.2-1.11" -TIMESTAMP="" -package_revision=1.3337 - -# Be Bourne compatible -if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then - emulate sh - NULLCMD=: - # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which - # is contrary to our usage. Disable this feature. - alias -g '${1+"$@"}'='"$@"' - setopt NO_GLOB_SUBST -else - case `(set -o) 2>/dev/null` in *posix*) set -o posix;; esac -fi -BIN_SH=xpg4; export BIN_SH # for Tru64 -DUALCASE=1; export DUALCASE # for MKS sh - -# A function that is used when there is no print builtin or printf. -func_fallback_echo () -{ - eval 'cat <<_LTECHO_EOF -$1 -_LTECHO_EOF' -} - -# NLS nuisances: We save the old values to restore during execute mode. -lt_user_locale= -lt_safe_locale= -for lt_var in LANG LANGUAGE LC_ALL LC_CTYPE LC_COLLATE LC_MESSAGES -do - eval "if test \"\${$lt_var+set}\" = set; then - save_$lt_var=\$$lt_var - $lt_var=C - export $lt_var - lt_user_locale=\"$lt_var=\\\$save_\$lt_var; \$lt_user_locale\" - lt_safe_locale=\"$lt_var=C; \$lt_safe_locale\" - fi" -done -LC_ALL=C -LANGUAGE=C -export LANGUAGE LC_ALL - -$lt_unset CDPATH - - -# Work around backward compatibility issue on IRIX 6.5. On IRIX 6.4+, sh -# is ksh but when the shell is invoked as "sh" and the current value of -# the _XPG environment variable is not equal to 1 (one), the special -# positional parameter $0, within a function call, is the name of the -# function. -progpath="$0" - - - -: ${CP="cp -f"} -test "${ECHO+set}" = set || ECHO=${as_echo-'printf %s\n'} -: ${MAKE="make"} -: ${MKDIR="mkdir"} -: ${MV="mv -f"} -: ${RM="rm -f"} -: ${SHELL="${CONFIG_SHELL-/bin/sh}"} -: ${Xsed="$SED -e 1s/^X//"} - -# Global variables: -EXIT_SUCCESS=0 -EXIT_FAILURE=1 -EXIT_MISMATCH=63 # $? = 63 is used to indicate version mismatch to missing. -EXIT_SKIP=77 # $? = 77 is used to indicate a skipped test to automake. - -exit_status=$EXIT_SUCCESS - -# Make sure IFS has a sensible default -lt_nl=' -' -IFS=" $lt_nl" - -dirname="s,/[^/]*$,," -basename="s,^.*/,," - -# func_dirname file append nondir_replacement -# Compute the dirname of FILE. If nonempty, add APPEND to the result, -# otherwise set result to NONDIR_REPLACEMENT. -func_dirname () -{ - func_dirname_result=`$ECHO "${1}" | $SED "$dirname"` - if test "X$func_dirname_result" = "X${1}"; then - func_dirname_result="${3}" - else - func_dirname_result="$func_dirname_result${2}" - fi -} # func_dirname may be replaced by extended shell implementation - - -# func_basename file -func_basename () -{ - func_basename_result=`$ECHO "${1}" | $SED "$basename"` -} # func_basename may be replaced by extended shell implementation - - -# func_dirname_and_basename file append nondir_replacement -# perform func_basename and func_dirname in a single function -# call: -# dirname: Compute the dirname of FILE. If nonempty, -# add APPEND to the result, otherwise set result -# to NONDIR_REPLACEMENT. -# value returned in "$func_dirname_result" -# basename: Compute filename of FILE. -# value retuned in "$func_basename_result" -# Implementation must be kept synchronized with func_dirname -# and func_basename. For efficiency, we do not delegate to -# those functions but instead duplicate the functionality here. -func_dirname_and_basename () -{ - # Extract subdirectory from the argument. - func_dirname_result=`$ECHO "${1}" | $SED -e "$dirname"` - if test "X$func_dirname_result" = "X${1}"; then - func_dirname_result="${3}" - else - func_dirname_result="$func_dirname_result${2}" - fi - func_basename_result=`$ECHO "${1}" | $SED -e "$basename"` -} # func_dirname_and_basename may be replaced by extended shell implementation - - -# func_stripname prefix suffix name -# strip PREFIX and SUFFIX off of NAME. -# PREFIX and SUFFIX must not contain globbing or regex special -# characters, hashes, percent signs, but SUFFIX may contain a leading -# dot (in which case that matches only a dot). -# func_strip_suffix prefix name -func_stripname () -{ - case ${2} in - .*) func_stripname_result=`$ECHO "${3}" | $SED "s%^${1}%%; s%\\\\${2}\$%%"`;; - *) func_stripname_result=`$ECHO "${3}" | $SED "s%^${1}%%; s%${2}\$%%"`;; - esac -} # func_stripname may be replaced by extended shell implementation - - -# These SED scripts presuppose an absolute path with a trailing slash. -pathcar='s,^/\([^/]*\).*$,\1,' -pathcdr='s,^/[^/]*,,' -removedotparts=':dotsl - s@/\./@/@g - t dotsl - s,/\.$,/,' -collapseslashes='s@/\{1,\}@/@g' -finalslash='s,/*$,/,' - -# func_normal_abspath PATH -# Remove doubled-up and trailing slashes, "." path components, -# and cancel out any ".." path components in PATH after making -# it an absolute path. -# value returned in "$func_normal_abspath_result" -func_normal_abspath () -{ - # Start from root dir and reassemble the path. - func_normal_abspath_result= - func_normal_abspath_tpath=$1 - func_normal_abspath_altnamespace= - case $func_normal_abspath_tpath in - "") - # Empty path, that just means $cwd. - func_stripname '' '/' "`pwd`" - func_normal_abspath_result=$func_stripname_result - return - ;; - # The next three entries are used to spot a run of precisely - # two leading slashes without using negated character classes; - # we take advantage of case's first-match behaviour. - ///*) - # Unusual form of absolute path, do nothing. - ;; - //*) - # Not necessarily an ordinary path; POSIX reserves leading '//' - # and for example Cygwin uses it to access remote file shares - # over CIFS/SMB, so we conserve a leading double slash if found. - func_normal_abspath_altnamespace=/ - ;; - /*) - # Absolute path, do nothing. - ;; - *) - # Relative path, prepend $cwd. - func_normal_abspath_tpath=`pwd`/$func_normal_abspath_tpath - ;; - esac - # Cancel out all the simple stuff to save iterations. We also want - # the path to end with a slash for ease of parsing, so make sure - # there is one (and only one) here. - func_normal_abspath_tpath=`$ECHO "$func_normal_abspath_tpath" | $SED \ - -e "$removedotparts" -e "$collapseslashes" -e "$finalslash"` - while :; do - # Processed it all yet? - if test "$func_normal_abspath_tpath" = / ; then - # If we ascended to the root using ".." the result may be empty now. - if test -z "$func_normal_abspath_result" ; then - func_normal_abspath_result=/ - fi - break - fi - func_normal_abspath_tcomponent=`$ECHO "$func_normal_abspath_tpath" | $SED \ - -e "$pathcar"` - func_normal_abspath_tpath=`$ECHO "$func_normal_abspath_tpath" | $SED \ - -e "$pathcdr"` - # Figure out what to do with it - case $func_normal_abspath_tcomponent in - "") - # Trailing empty path component, ignore it. - ;; - ..) - # Parent dir; strip last assembled component from result. - func_dirname "$func_normal_abspath_result" - func_normal_abspath_result=$func_dirname_result - ;; - *) - # Actual path component, append it. - func_normal_abspath_result=$func_normal_abspath_result/$func_normal_abspath_tcomponent - ;; - esac - done - # Restore leading double-slash if one was found on entry. - func_normal_abspath_result=$func_normal_abspath_altnamespace$func_normal_abspath_result -} - -# func_relative_path SRCDIR DSTDIR -# generates a relative path from SRCDIR to DSTDIR, with a trailing -# slash if non-empty, suitable for immediately appending a filename -# without needing to append a separator. -# value returned in "$func_relative_path_result" -func_relative_path () -{ - func_relative_path_result= - func_normal_abspath "$1" - func_relative_path_tlibdir=$func_normal_abspath_result - func_normal_abspath "$2" - func_relative_path_tbindir=$func_normal_abspath_result - - # Ascend the tree starting from libdir - while :; do - # check if we have found a prefix of bindir - case $func_relative_path_tbindir in - $func_relative_path_tlibdir) - # found an exact match - func_relative_path_tcancelled= - break - ;; - $func_relative_path_tlibdir*) - # found a matching prefix - func_stripname "$func_relative_path_tlibdir" '' "$func_relative_path_tbindir" - func_relative_path_tcancelled=$func_stripname_result - if test -z "$func_relative_path_result"; then - func_relative_path_result=. - fi - break - ;; - *) - func_dirname $func_relative_path_tlibdir - func_relative_path_tlibdir=${func_dirname_result} - if test "x$func_relative_path_tlibdir" = x ; then - # Have to descend all the way to the root! - func_relative_path_result=../$func_relative_path_result - func_relative_path_tcancelled=$func_relative_path_tbindir - break - fi - func_relative_path_result=../$func_relative_path_result - ;; - esac - done - - # Now calculate path; take care to avoid doubling-up slashes. - func_stripname '' '/' "$func_relative_path_result" - func_relative_path_result=$func_stripname_result - func_stripname '/' '/' "$func_relative_path_tcancelled" - if test "x$func_stripname_result" != x ; then - func_relative_path_result=${func_relative_path_result}/${func_stripname_result} - fi - - # Normalisation. If bindir is libdir, return empty string, - # else relative path ending with a slash; either way, target - # file name can be directly appended. - if test ! -z "$func_relative_path_result"; then - func_stripname './' '' "$func_relative_path_result/" - func_relative_path_result=$func_stripname_result - fi -} - -# The name of this program: -func_dirname_and_basename "$progpath" -progname=$func_basename_result - -# Make sure we have an absolute path for reexecution: -case $progpath in - [\\/]*|[A-Za-z]:\\*) ;; - *[\\/]*) - progdir=$func_dirname_result - progdir=`cd "$progdir" && pwd` - progpath="$progdir/$progname" - ;; - *) - save_IFS="$IFS" - IFS=${PATH_SEPARATOR-:} - for progdir in $PATH; do - IFS="$save_IFS" - test -x "$progdir/$progname" && break - done - IFS="$save_IFS" - test -n "$progdir" || progdir=`pwd` - progpath="$progdir/$progname" - ;; -esac - -# Sed substitution that helps us do robust quoting. It backslashifies -# metacharacters that are still active within double-quoted strings. -Xsed="${SED}"' -e 1s/^X//' -sed_quote_subst='s/\([`"$\\]\)/\\\1/g' - -# Same as above, but do not quote variable references. -double_quote_subst='s/\(["`\\]\)/\\\1/g' - -# Sed substitution that turns a string into a regex matching for the -# string literally. -sed_make_literal_regex='s,[].[^$\\*\/],\\&,g' - -# Sed substitution that converts a w32 file name or path -# which contains forward slashes, into one that contains -# (escaped) backslashes. A very naive implementation. -lt_sed_naive_backslashify='s|\\\\*|\\|g;s|/|\\|g;s|\\|\\\\|g' - -# Re-`\' parameter expansions in output of double_quote_subst that were -# `\'-ed in input to the same. If an odd number of `\' preceded a '$' -# in input to double_quote_subst, that '$' was protected from expansion. -# Since each input `\' is now two `\'s, look for any number of runs of -# four `\'s followed by two `\'s and then a '$'. `\' that '$'. -bs='\\' -bs2='\\\\' -bs4='\\\\\\\\' -dollar='\$' -sed_double_backslash="\ - s/$bs4/&\\ -/g - s/^$bs2$dollar/$bs&/ - s/\\([^$bs]\\)$bs2$dollar/\\1$bs2$bs$dollar/g - s/\n//g" - -# Standard options: -opt_dry_run=false -opt_help=false -opt_quiet=false -opt_verbose=false -opt_warning=: - -# func_echo arg... -# Echo program name prefixed message, along with the current mode -# name if it has been set yet. -func_echo () -{ - $ECHO "$progname: ${opt_mode+$opt_mode: }$*" -} - -# func_verbose arg... -# Echo program name prefixed message in verbose mode only. -func_verbose () -{ - $opt_verbose && func_echo ${1+"$@"} - - # A bug in bash halts the script if the last line of a function - # fails when set -e is in force, so we need another command to - # work around that: - : -} - -# func_echo_all arg... -# Invoke $ECHO with all args, space-separated. -func_echo_all () -{ - $ECHO "$*" -} - -# func_error arg... -# Echo program name prefixed message to standard error. -func_error () -{ - $ECHO "$progname: ${opt_mode+$opt_mode: }"${1+"$@"} 1>&2 -} - -# func_warning arg... -# Echo program name prefixed warning message to standard error. -func_warning () -{ - $opt_warning && $ECHO "$progname: ${opt_mode+$opt_mode: }warning: "${1+"$@"} 1>&2 - - # bash bug again: - : -} - -# func_fatal_error arg... -# Echo program name prefixed message to standard error, and exit. -func_fatal_error () -{ - func_error ${1+"$@"} - exit $EXIT_FAILURE -} - -# func_fatal_help arg... -# Echo program name prefixed message to standard error, followed by -# a help hint, and exit. -func_fatal_help () -{ - func_error ${1+"$@"} - func_fatal_error "$help" -} -help="Try \`$progname --help' for more information." ## default - - -# func_grep expression filename -# Check whether EXPRESSION matches any line of FILENAME, without output. -func_grep () -{ - $GREP "$1" "$2" >/dev/null 2>&1 -} - - -# func_mkdir_p directory-path -# Make sure the entire path to DIRECTORY-PATH is available. -func_mkdir_p () -{ - my_directory_path="$1" - my_dir_list= - - if test -n "$my_directory_path" && test "$opt_dry_run" != ":"; then - - # Protect directory names starting with `-' - case $my_directory_path in - -*) my_directory_path="./$my_directory_path" ;; - esac - - # While some portion of DIR does not yet exist... - while test ! -d "$my_directory_path"; do - # ...make a list in topmost first order. Use a colon delimited - # list incase some portion of path contains whitespace. - my_dir_list="$my_directory_path:$my_dir_list" - - # If the last portion added has no slash in it, the list is done - case $my_directory_path in */*) ;; *) break ;; esac - - # ...otherwise throw away the child directory and loop - my_directory_path=`$ECHO "$my_directory_path" | $SED -e "$dirname"` - done - my_dir_list=`$ECHO "$my_dir_list" | $SED 's,:*$,,'` - - save_mkdir_p_IFS="$IFS"; IFS=':' - for my_dir in $my_dir_list; do - IFS="$save_mkdir_p_IFS" - # mkdir can fail with a `File exist' error if two processes - # try to create one of the directories concurrently. Don't - # stop in that case! - $MKDIR "$my_dir" 2>/dev/null || : - done - IFS="$save_mkdir_p_IFS" - - # Bail out if we (or some other process) failed to create a directory. - test -d "$my_directory_path" || \ - func_fatal_error "Failed to create \`$1'" - fi -} - - -# func_mktempdir [string] -# Make a temporary directory that won't clash with other running -# libtool processes, and avoids race conditions if possible. If -# given, STRING is the basename for that directory. -func_mktempdir () -{ - my_template="${TMPDIR-/tmp}/${1-$progname}" - - if test "$opt_dry_run" = ":"; then - # Return a directory name, but don't create it in dry-run mode - my_tmpdir="${my_template}-$$" - else - - # If mktemp works, use that first and foremost - my_tmpdir=`mktemp -d "${my_template}-XXXXXXXX" 2>/dev/null` - - if test ! -d "$my_tmpdir"; then - # Failing that, at least try and use $RANDOM to avoid a race - my_tmpdir="${my_template}-${RANDOM-0}$$" - - save_mktempdir_umask=`umask` - umask 0077 - $MKDIR "$my_tmpdir" - umask $save_mktempdir_umask - fi - - # If we're not in dry-run mode, bomb out on failure - test -d "$my_tmpdir" || \ - func_fatal_error "cannot create temporary directory \`$my_tmpdir'" - fi - - $ECHO "$my_tmpdir" -} - - -# func_quote_for_eval arg -# Aesthetically quote ARG to be evaled later. -# This function returns two values: FUNC_QUOTE_FOR_EVAL_RESULT -# is double-quoted, suitable for a subsequent eval, whereas -# FUNC_QUOTE_FOR_EVAL_UNQUOTED_RESULT has merely all characters -# which are still active within double quotes backslashified. -func_quote_for_eval () -{ - case $1 in - *[\\\`\"\$]*) - func_quote_for_eval_unquoted_result=`$ECHO "$1" | $SED "$sed_quote_subst"` ;; - *) - func_quote_for_eval_unquoted_result="$1" ;; - esac - - case $func_quote_for_eval_unquoted_result in - # Double-quote args containing shell metacharacters to delay - # word splitting, command substitution and and variable - # expansion for a subsequent eval. - # Many Bourne shells cannot handle close brackets correctly - # in scan sets, so we specify it separately. - *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") - func_quote_for_eval_result="\"$func_quote_for_eval_unquoted_result\"" - ;; - *) - func_quote_for_eval_result="$func_quote_for_eval_unquoted_result" - esac -} - - -# func_quote_for_expand arg -# Aesthetically quote ARG to be evaled later; same as above, -# but do not quote variable references. -func_quote_for_expand () -{ - case $1 in - *[\\\`\"]*) - my_arg=`$ECHO "$1" | $SED \ - -e "$double_quote_subst" -e "$sed_double_backslash"` ;; - *) - my_arg="$1" ;; - esac - - case $my_arg in - # Double-quote args containing shell metacharacters to delay - # word splitting and command substitution for a subsequent eval. - # Many Bourne shells cannot handle close brackets correctly - # in scan sets, so we specify it separately. - *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") - my_arg="\"$my_arg\"" - ;; - esac - - func_quote_for_expand_result="$my_arg" -} - - -# func_show_eval cmd [fail_exp] -# Unless opt_silent is true, then output CMD. Then, if opt_dryrun is -# not true, evaluate CMD. If the evaluation of CMD fails, and FAIL_EXP -# is given, then evaluate it. -func_show_eval () -{ - my_cmd="$1" - my_fail_exp="${2-:}" - - ${opt_silent-false} || { - func_quote_for_expand "$my_cmd" - eval "func_echo $func_quote_for_expand_result" - } - - if ${opt_dry_run-false}; then :; else - eval "$my_cmd" - my_status=$? - if test "$my_status" -eq 0; then :; else - eval "(exit $my_status); $my_fail_exp" - fi - fi -} - - -# func_show_eval_locale cmd [fail_exp] -# Unless opt_silent is true, then output CMD. Then, if opt_dryrun is -# not true, evaluate CMD. If the evaluation of CMD fails, and FAIL_EXP -# is given, then evaluate it. Use the saved locale for evaluation. -func_show_eval_locale () -{ - my_cmd="$1" - my_fail_exp="${2-:}" - - ${opt_silent-false} || { - func_quote_for_expand "$my_cmd" - eval "func_echo $func_quote_for_expand_result" - } - - if ${opt_dry_run-false}; then :; else - eval "$lt_user_locale - $my_cmd" - my_status=$? - eval "$lt_safe_locale" - if test "$my_status" -eq 0; then :; else - eval "(exit $my_status); $my_fail_exp" - fi - fi -} - -# func_tr_sh -# Turn $1 into a string suitable for a shell variable name. -# Result is stored in $func_tr_sh_result. All characters -# not in the set a-zA-Z0-9_ are replaced with '_'. Further, -# if $1 begins with a digit, a '_' is prepended as well. -func_tr_sh () -{ - case $1 in - [0-9]* | *[!a-zA-Z0-9_]*) - func_tr_sh_result=`$ECHO "$1" | $SED 's/^\([0-9]\)/_\1/; s/[^a-zA-Z0-9_]/_/g'` - ;; - * ) - func_tr_sh_result=$1 - ;; - esac -} - - -# func_version -# Echo version message to standard output and exit. -func_version () -{ - $opt_debug - - $SED -n '/(C)/!b go - :more - /\./!{ - N - s/\n# / / - b more - } - :go - /^# '$PROGRAM' (GNU /,/# warranty; / { - s/^# // - s/^# *$// - s/\((C)\)[ 0-9,-]*\( [1-9][0-9]*\)/\1\2/ - p - }' < "$progpath" - exit $? -} - -# func_usage -# Echo short help message to standard output and exit. -func_usage () -{ - $opt_debug - - $SED -n '/^# Usage:/,/^# *.*--help/ { - s/^# // - s/^# *$// - s/\$progname/'$progname'/ - p - }' < "$progpath" - echo - $ECHO "run \`$progname --help | more' for full usage" - exit $? -} - -# func_help [NOEXIT] -# Echo long help message to standard output and exit, -# unless 'noexit' is passed as argument. -func_help () -{ - $opt_debug - - $SED -n '/^# Usage:/,/# Report bugs to/ { - :print - s/^# // - s/^# *$// - s*\$progname*'$progname'* - s*\$host*'"$host"'* - s*\$SHELL*'"$SHELL"'* - s*\$LTCC*'"$LTCC"'* - s*\$LTCFLAGS*'"$LTCFLAGS"'* - s*\$LD*'"$LD"'* - s/\$with_gnu_ld/'"$with_gnu_ld"'/ - s/\$automake_version/'"`(${AUTOMAKE-automake} --version) 2>/dev/null |$SED 1q`"'/ - s/\$autoconf_version/'"`(${AUTOCONF-autoconf} --version) 2>/dev/null |$SED 1q`"'/ - p - d - } - /^# .* home page:/b print - /^# General help using/b print - ' < "$progpath" - ret=$? - if test -z "$1"; then - exit $ret - fi -} - -# func_missing_arg argname -# Echo program name prefixed message to standard error and set global -# exit_cmd. -func_missing_arg () -{ - $opt_debug - - func_error "missing argument for $1." - exit_cmd=exit -} - - -# func_split_short_opt shortopt -# Set func_split_short_opt_name and func_split_short_opt_arg shell -# variables after splitting SHORTOPT after the 2nd character. -func_split_short_opt () -{ - my_sed_short_opt='1s/^\(..\).*$/\1/;q' - my_sed_short_rest='1s/^..\(.*\)$/\1/;q' - - func_split_short_opt_name=`$ECHO "$1" | $SED "$my_sed_short_opt"` - func_split_short_opt_arg=`$ECHO "$1" | $SED "$my_sed_short_rest"` -} # func_split_short_opt may be replaced by extended shell implementation - - -# func_split_long_opt longopt -# Set func_split_long_opt_name and func_split_long_opt_arg shell -# variables after splitting LONGOPT at the `=' sign. -func_split_long_opt () -{ - my_sed_long_opt='1s/^\(--[^=]*\)=.*/\1/;q' - my_sed_long_arg='1s/^--[^=]*=//' - - func_split_long_opt_name=`$ECHO "$1" | $SED "$my_sed_long_opt"` - func_split_long_opt_arg=`$ECHO "$1" | $SED "$my_sed_long_arg"` -} # func_split_long_opt may be replaced by extended shell implementation - -exit_cmd=: - - - - - -magic="%%%MAGIC variable%%%" -magic_exe="%%%MAGIC EXE variable%%%" - -# Global variables. -nonopt= -preserve_args= -lo2o="s/\\.lo\$/.${objext}/" -o2lo="s/\\.${objext}\$/.lo/" -extracted_archives= -extracted_serial=0 - -# If this variable is set in any of the actions, the command in it -# will be execed at the end. This prevents here-documents from being -# left over by shells. -exec_cmd= - -# func_append var value -# Append VALUE to the end of shell variable VAR. -func_append () -{ - eval "${1}=\$${1}\${2}" -} # func_append may be replaced by extended shell implementation - -# func_append_quoted var value -# Quote VALUE and append to the end of shell variable VAR, separated -# by a space. -func_append_quoted () -{ - func_quote_for_eval "${2}" - eval "${1}=\$${1}\\ \$func_quote_for_eval_result" -} # func_append_quoted may be replaced by extended shell implementation - - -# func_arith arithmetic-term... -func_arith () -{ - func_arith_result=`expr "${@}"` -} # func_arith may be replaced by extended shell implementation - - -# func_len string -# STRING may not start with a hyphen. -func_len () -{ - func_len_result=`expr "${1}" : ".*" 2>/dev/null || echo $max_cmd_len` -} # func_len may be replaced by extended shell implementation - - -# func_lo2o object -func_lo2o () -{ - func_lo2o_result=`$ECHO "${1}" | $SED "$lo2o"` -} # func_lo2o may be replaced by extended shell implementation - - -# func_xform libobj-or-source -func_xform () -{ - func_xform_result=`$ECHO "${1}" | $SED 's/\.[^.]*$/.lo/'` -} # func_xform may be replaced by extended shell implementation - - -# func_fatal_configuration arg... -# Echo program name prefixed message to standard error, followed by -# a configuration failure hint, and exit. -func_fatal_configuration () -{ - func_error ${1+"$@"} - func_error "See the $PACKAGE documentation for more information." - func_fatal_error "Fatal configuration error." -} - - -# func_config -# Display the configuration for all the tags in this script. -func_config () -{ - re_begincf='^# ### BEGIN LIBTOOL' - re_endcf='^# ### END LIBTOOL' - - # Default configuration. - $SED "1,/$re_begincf CONFIG/d;/$re_endcf CONFIG/,\$d" < "$progpath" - - # Now print the configurations for the tags. - for tagname in $taglist; do - $SED -n "/$re_begincf TAG CONFIG: $tagname\$/,/$re_endcf TAG CONFIG: $tagname\$/p" < "$progpath" - done - - exit $? -} - -# func_features -# Display the features supported by this script. -func_features () -{ - echo "host: $host" - if test "$build_libtool_libs" = yes; then - echo "enable shared libraries" - else - echo "disable shared libraries" - fi - if test "$build_old_libs" = yes; then - echo "enable static libraries" - else - echo "disable static libraries" - fi - - exit $? -} - -# func_enable_tag tagname -# Verify that TAGNAME is valid, and either flag an error and exit, or -# enable the TAGNAME tag. We also add TAGNAME to the global $taglist -# variable here. -func_enable_tag () -{ - # Global variable: - tagname="$1" - - re_begincf="^# ### BEGIN LIBTOOL TAG CONFIG: $tagname\$" - re_endcf="^# ### END LIBTOOL TAG CONFIG: $tagname\$" - sed_extractcf="/$re_begincf/,/$re_endcf/p" - - # Validate tagname. - case $tagname in - *[!-_A-Za-z0-9,/]*) - func_fatal_error "invalid tag name: $tagname" - ;; - esac - - # Don't test for the "default" C tag, as we know it's - # there but not specially marked. - case $tagname in - CC) ;; - *) - if $GREP "$re_begincf" "$progpath" >/dev/null 2>&1; then - taglist="$taglist $tagname" - - # Evaluate the configuration. Be careful to quote the path - # and the sed script, to avoid splitting on whitespace, but - # also don't use non-portable quotes within backquotes within - # quotes we have to do it in 2 steps: - extractedcf=`$SED -n -e "$sed_extractcf" < "$progpath"` - eval "$extractedcf" - else - func_error "ignoring unknown tag $tagname" - fi - ;; - esac -} - -# func_check_version_match -# Ensure that we are using m4 macros, and libtool script from the same -# release of libtool. -func_check_version_match () -{ - if test "$package_revision" != "$macro_revision"; then - if test "$VERSION" != "$macro_version"; then - if test -z "$macro_version"; then - cat >&2 <<_LT_EOF -$progname: Version mismatch error. This is $PACKAGE $VERSION, but the -$progname: definition of this LT_INIT comes from an older release. -$progname: You should recreate aclocal.m4 with macros from $PACKAGE $VERSION -$progname: and run autoconf again. -_LT_EOF - else - cat >&2 <<_LT_EOF -$progname: Version mismatch error. This is $PACKAGE $VERSION, but the -$progname: definition of this LT_INIT comes from $PACKAGE $macro_version. -$progname: You should recreate aclocal.m4 with macros from $PACKAGE $VERSION -$progname: and run autoconf again. -_LT_EOF - fi - else - cat >&2 <<_LT_EOF -$progname: Version mismatch error. This is $PACKAGE $VERSION, revision $package_revision, -$progname: but the definition of this LT_INIT comes from revision $macro_revision. -$progname: You should recreate aclocal.m4 with macros from revision $package_revision -$progname: of $PACKAGE $VERSION and run autoconf again. -_LT_EOF - fi - - exit $EXIT_MISMATCH - fi -} - - -# Shorthand for --mode=foo, only valid as the first argument -case $1 in -clean|clea|cle|cl) - shift; set dummy --mode clean ${1+"$@"}; shift - ;; -compile|compil|compi|comp|com|co|c) - shift; set dummy --mode compile ${1+"$@"}; shift - ;; -execute|execut|execu|exec|exe|ex|e) - shift; set dummy --mode execute ${1+"$@"}; shift - ;; -finish|finis|fini|fin|fi|f) - shift; set dummy --mode finish ${1+"$@"}; shift - ;; -install|instal|insta|inst|ins|in|i) - shift; set dummy --mode install ${1+"$@"}; shift - ;; -link|lin|li|l) - shift; set dummy --mode link ${1+"$@"}; shift - ;; -uninstall|uninstal|uninsta|uninst|unins|unin|uni|un|u) - shift; set dummy --mode uninstall ${1+"$@"}; shift - ;; -esac - - - -# Option defaults: -opt_debug=: -opt_dry_run=false -opt_config=false -opt_preserve_dup_deps=false -opt_features=false -opt_finish=false -opt_help=false -opt_help_all=false -opt_silent=: -opt_warning=: -opt_verbose=: -opt_silent=false -opt_verbose=false - - -# Parse options once, thoroughly. This comes as soon as possible in the -# script to make things like `--version' happen as quickly as we can. -{ - # this just eases exit handling - while test $# -gt 0; do - opt="$1" - shift - case $opt in - --debug|-x) opt_debug='set -x' - func_echo "enabling shell trace mode" - $opt_debug - ;; - --dry-run|--dryrun|-n) - opt_dry_run=: - ;; - --config) - opt_config=: -func_config - ;; - --dlopen|-dlopen) - optarg="$1" - opt_dlopen="${opt_dlopen+$opt_dlopen -}$optarg" - shift - ;; - --preserve-dup-deps) - opt_preserve_dup_deps=: - ;; - --features) - opt_features=: -func_features - ;; - --finish) - opt_finish=: -set dummy --mode finish ${1+"$@"}; shift - ;; - --help) - opt_help=: - ;; - --help-all) - opt_help_all=: -opt_help=': help-all' - ;; - --mode) - test $# = 0 && func_missing_arg $opt && break - optarg="$1" - opt_mode="$optarg" -case $optarg in - # Valid mode arguments: - clean|compile|execute|finish|install|link|relink|uninstall) ;; - - # Catch anything else as an error - *) func_error "invalid argument for $opt" - exit_cmd=exit - break - ;; -esac - shift - ;; - --no-silent|--no-quiet) - opt_silent=false -func_append preserve_args " $opt" - ;; - --no-warning|--no-warn) - opt_warning=false -func_append preserve_args " $opt" - ;; - --no-verbose) - opt_verbose=false -func_append preserve_args " $opt" - ;; - --silent|--quiet) - opt_silent=: -func_append preserve_args " $opt" - opt_verbose=false - ;; - --verbose|-v) - opt_verbose=: -func_append preserve_args " $opt" -opt_silent=false - ;; - --tag) - test $# = 0 && func_missing_arg $opt && break - optarg="$1" - opt_tag="$optarg" -func_append preserve_args " $opt $optarg" -func_enable_tag "$optarg" - shift - ;; - - -\?|-h) func_usage ;; - --help) func_help ;; - --version) func_version ;; - - # Separate optargs to long options: - --*=*) - func_split_long_opt "$opt" - set dummy "$func_split_long_opt_name" "$func_split_long_opt_arg" ${1+"$@"} - shift - ;; - - # Separate non-argument short options: - -\?*|-h*|-n*|-v*) - func_split_short_opt "$opt" - set dummy "$func_split_short_opt_name" "-$func_split_short_opt_arg" ${1+"$@"} - shift - ;; - - --) break ;; - -*) func_fatal_help "unrecognized option \`$opt'" ;; - *) set dummy "$opt" ${1+"$@"}; shift; break ;; - esac - done - - # Validate options: - - # save first non-option argument - if test "$#" -gt 0; then - nonopt="$opt" - shift - fi - - # preserve --debug - test "$opt_debug" = : || func_append preserve_args " --debug" - - case $host in - *cygwin* | *mingw* | *pw32* | *cegcc*) - # don't eliminate duplications in $postdeps and $predeps - opt_duplicate_compiler_generated_deps=: - ;; - *) - opt_duplicate_compiler_generated_deps=$opt_preserve_dup_deps - ;; - esac - - $opt_help || { - # Sanity checks first: - func_check_version_match - - if test "$build_libtool_libs" != yes && test "$build_old_libs" != yes; then - func_fatal_configuration "not configured to build any kind of library" - fi - - # Darwin sucks - eval std_shrext=\"$shrext_cmds\" - - # Only execute mode is allowed to have -dlopen flags. - if test -n "$opt_dlopen" && test "$opt_mode" != execute; then - func_error "unrecognized option \`-dlopen'" - $ECHO "$help" 1>&2 - exit $EXIT_FAILURE - fi - - # Change the help message to a mode-specific one. - generic_help="$help" - help="Try \`$progname --help --mode=$opt_mode' for more information." - } - - - # Bail if the options were screwed - $exit_cmd $EXIT_FAILURE -} - - - - -## ----------- ## -## Main. ## -## ----------- ## - -# func_lalib_p file -# True iff FILE is a libtool `.la' library or `.lo' object file. -# This function is only a basic sanity check; it will hardly flush out -# determined imposters. -func_lalib_p () -{ - test -f "$1" && - $SED -e 4q "$1" 2>/dev/null \ - | $GREP "^# Generated by .*$PACKAGE" > /dev/null 2>&1 -} - -# func_lalib_unsafe_p file -# True iff FILE is a libtool `.la' library or `.lo' object file. -# This function implements the same check as func_lalib_p without -# resorting to external programs. To this end, it redirects stdin and -# closes it afterwards, without saving the original file descriptor. -# As a safety measure, use it only where a negative result would be -# fatal anyway. Works if `file' does not exist. -func_lalib_unsafe_p () -{ - lalib_p=no - if test -f "$1" && test -r "$1" && exec 5<&0 <"$1"; then - for lalib_p_l in 1 2 3 4 - do - read lalib_p_line - case "$lalib_p_line" in - \#\ Generated\ by\ *$PACKAGE* ) lalib_p=yes; break;; - esac - done - exec 0<&5 5<&- - fi - test "$lalib_p" = yes -} - -# func_ltwrapper_script_p file -# True iff FILE is a libtool wrapper script -# This function is only a basic sanity check; it will hardly flush out -# determined imposters. -func_ltwrapper_script_p () -{ - func_lalib_p "$1" -} - -# func_ltwrapper_executable_p file -# True iff FILE is a libtool wrapper executable -# This function is only a basic sanity check; it will hardly flush out -# determined imposters. -func_ltwrapper_executable_p () -{ - func_ltwrapper_exec_suffix= - case $1 in - *.exe) ;; - *) func_ltwrapper_exec_suffix=.exe ;; - esac - $GREP "$magic_exe" "$1$func_ltwrapper_exec_suffix" >/dev/null 2>&1 -} - -# func_ltwrapper_scriptname file -# Assumes file is an ltwrapper_executable -# uses $file to determine the appropriate filename for a -# temporary ltwrapper_script. -func_ltwrapper_scriptname () -{ - func_dirname_and_basename "$1" "" "." - func_stripname '' '.exe' "$func_basename_result" - func_ltwrapper_scriptname_result="$func_dirname_result/$objdir/${func_stripname_result}_ltshwrapper" -} - -# func_ltwrapper_p file -# True iff FILE is a libtool wrapper script or wrapper executable -# This function is only a basic sanity check; it will hardly flush out -# determined imposters. -func_ltwrapper_p () -{ - func_ltwrapper_script_p "$1" || func_ltwrapper_executable_p "$1" -} - - -# func_execute_cmds commands fail_cmd -# Execute tilde-delimited COMMANDS. -# If FAIL_CMD is given, eval that upon failure. -# FAIL_CMD may read-access the current command in variable CMD! -func_execute_cmds () -{ - $opt_debug - save_ifs=$IFS; IFS='~' - for cmd in $1; do - IFS=$save_ifs - eval cmd=\"$cmd\" - func_show_eval "$cmd" "${2-:}" - done - IFS=$save_ifs -} - - -# func_source file -# Source FILE, adding directory component if necessary. -# Note that it is not necessary on cygwin/mingw to append a dot to -# FILE even if both FILE and FILE.exe exist: automatic-append-.exe -# behavior happens only for exec(3), not for open(2)! Also, sourcing -# `FILE.' does not work on cygwin managed mounts. -func_source () -{ - $opt_debug - case $1 in - */* | *\\*) . "$1" ;; - *) . "./$1" ;; - esac -} - - -# func_resolve_sysroot PATH -# Replace a leading = in PATH with a sysroot. Store the result into -# func_resolve_sysroot_result -func_resolve_sysroot () -{ - func_resolve_sysroot_result=$1 - case $func_resolve_sysroot_result in - =*) - func_stripname '=' '' "$func_resolve_sysroot_result" - func_resolve_sysroot_result=$lt_sysroot$func_stripname_result - ;; - esac -} - -# func_replace_sysroot PATH -# If PATH begins with the sysroot, replace it with = and -# store the result into func_replace_sysroot_result. -func_replace_sysroot () -{ - case "$lt_sysroot:$1" in - ?*:"$lt_sysroot"*) - func_stripname "$lt_sysroot" '' "$1" - func_replace_sysroot_result="=$func_stripname_result" - ;; - *) - # Including no sysroot. - func_replace_sysroot_result=$1 - ;; - esac -} - -# func_infer_tag arg -# Infer tagged configuration to use if any are available and -# if one wasn't chosen via the "--tag" command line option. -# Only attempt this if the compiler in the base compile -# command doesn't match the default compiler. -# arg is usually of the form 'gcc ...' -func_infer_tag () -{ - $opt_debug - if test -n "$available_tags" && test -z "$tagname"; then - CC_quoted= - for arg in $CC; do - func_append_quoted CC_quoted "$arg" - done - CC_expanded=`func_echo_all $CC` - CC_quoted_expanded=`func_echo_all $CC_quoted` - case $@ in - # Blanks in the command may have been stripped by the calling shell, - # but not from the CC environment variable when configure was run. - " $CC "* | "$CC "* | " $CC_expanded "* | "$CC_expanded "* | \ - " $CC_quoted"* | "$CC_quoted "* | " $CC_quoted_expanded "* | "$CC_quoted_expanded "*) ;; - # Blanks at the start of $base_compile will cause this to fail - # if we don't check for them as well. - *) - for z in $available_tags; do - if $GREP "^# ### BEGIN LIBTOOL TAG CONFIG: $z$" < "$progpath" > /dev/null; then - # Evaluate the configuration. - eval "`${SED} -n -e '/^# ### BEGIN LIBTOOL TAG CONFIG: '$z'$/,/^# ### END LIBTOOL TAG CONFIG: '$z'$/p' < $progpath`" - CC_quoted= - for arg in $CC; do - # Double-quote args containing other shell metacharacters. - func_append_quoted CC_quoted "$arg" - done - CC_expanded=`func_echo_all $CC` - CC_quoted_expanded=`func_echo_all $CC_quoted` - case "$@ " in - " $CC "* | "$CC "* | " $CC_expanded "* | "$CC_expanded "* | \ - " $CC_quoted"* | "$CC_quoted "* | " $CC_quoted_expanded "* | "$CC_quoted_expanded "*) - # The compiler in the base compile command matches - # the one in the tagged configuration. - # Assume this is the tagged configuration we want. - tagname=$z - break - ;; - esac - fi - done - # If $tagname still isn't set, then no tagged configuration - # was found and let the user know that the "--tag" command - # line option must be used. - if test -z "$tagname"; then - func_echo "unable to infer tagged configuration" - func_fatal_error "specify a tag with \`--tag'" -# else -# func_verbose "using $tagname tagged configuration" - fi - ;; - esac - fi -} - - - -# func_write_libtool_object output_name pic_name nonpic_name -# Create a libtool object file (analogous to a ".la" file), -# but don't create it if we're doing a dry run. -func_write_libtool_object () -{ - write_libobj=${1} - if test "$build_libtool_libs" = yes; then - write_lobj=\'${2}\' - else - write_lobj=none - fi - - if test "$build_old_libs" = yes; then - write_oldobj=\'${3}\' - else - write_oldobj=none - fi - - $opt_dry_run || { - cat >${write_libobj}T </dev/null` - if test "$?" -eq 0 && test -n "${func_convert_core_file_wine_to_w32_tmp}"; then - func_convert_core_file_wine_to_w32_result=`$ECHO "$func_convert_core_file_wine_to_w32_tmp" | - $SED -e "$lt_sed_naive_backslashify"` - else - func_convert_core_file_wine_to_w32_result= - fi - fi -} -# end: func_convert_core_file_wine_to_w32 - - -# func_convert_core_path_wine_to_w32 ARG -# Helper function used by path conversion functions when $build is *nix, and -# $host is mingw, cygwin, or some other w32 environment. Relies on a correctly -# configured wine environment available, with the winepath program in $build's -# $PATH. Assumes ARG has no leading or trailing path separator characters. -# -# ARG is path to be converted from $build format to win32. -# Result is available in $func_convert_core_path_wine_to_w32_result. -# Unconvertible file (directory) names in ARG are skipped; if no directory names -# are convertible, then the result may be empty. -func_convert_core_path_wine_to_w32 () -{ - $opt_debug - # unfortunately, winepath doesn't convert paths, only file names - func_convert_core_path_wine_to_w32_result="" - if test -n "$1"; then - oldIFS=$IFS - IFS=: - for func_convert_core_path_wine_to_w32_f in $1; do - IFS=$oldIFS - func_convert_core_file_wine_to_w32 "$func_convert_core_path_wine_to_w32_f" - if test -n "$func_convert_core_file_wine_to_w32_result" ; then - if test -z "$func_convert_core_path_wine_to_w32_result"; then - func_convert_core_path_wine_to_w32_result="$func_convert_core_file_wine_to_w32_result" - else - func_append func_convert_core_path_wine_to_w32_result ";$func_convert_core_file_wine_to_w32_result" - fi - fi - done - IFS=$oldIFS - fi -} -# end: func_convert_core_path_wine_to_w32 - - -# func_cygpath ARGS... -# Wrapper around calling the cygpath program via LT_CYGPATH. This is used when -# when (1) $build is *nix and Cygwin is hosted via a wine environment; or (2) -# $build is MSYS and $host is Cygwin, or (3) $build is Cygwin. In case (1) or -# (2), returns the Cygwin file name or path in func_cygpath_result (input -# file name or path is assumed to be in w32 format, as previously converted -# from $build's *nix or MSYS format). In case (3), returns the w32 file name -# or path in func_cygpath_result (input file name or path is assumed to be in -# Cygwin format). Returns an empty string on error. -# -# ARGS are passed to cygpath, with the last one being the file name or path to -# be converted. -# -# Specify the absolute *nix (or w32) name to cygpath in the LT_CYGPATH -# environment variable; do not put it in $PATH. -func_cygpath () -{ - $opt_debug - if test -n "$LT_CYGPATH" && test -f "$LT_CYGPATH"; then - func_cygpath_result=`$LT_CYGPATH "$@" 2>/dev/null` - if test "$?" -ne 0; then - # on failure, ensure result is empty - func_cygpath_result= - fi - else - func_cygpath_result= - func_error "LT_CYGPATH is empty or specifies non-existent file: \`$LT_CYGPATH'" - fi -} -#end: func_cygpath - - -# func_convert_core_msys_to_w32 ARG -# Convert file name or path ARG from MSYS format to w32 format. Return -# result in func_convert_core_msys_to_w32_result. -func_convert_core_msys_to_w32 () -{ - $opt_debug - # awkward: cmd appends spaces to result - func_convert_core_msys_to_w32_result=`( cmd //c echo "$1" ) 2>/dev/null | - $SED -e 's/[ ]*$//' -e "$lt_sed_naive_backslashify"` -} -#end: func_convert_core_msys_to_w32 - - -# func_convert_file_check ARG1 ARG2 -# Verify that ARG1 (a file name in $build format) was converted to $host -# format in ARG2. Otherwise, emit an error message, but continue (resetting -# func_to_host_file_result to ARG1). -func_convert_file_check () -{ - $opt_debug - if test -z "$2" && test -n "$1" ; then - func_error "Could not determine host file name corresponding to" - func_error " \`$1'" - func_error "Continuing, but uninstalled executables may not work." - # Fallback: - func_to_host_file_result="$1" - fi -} -# end func_convert_file_check - - -# func_convert_path_check FROM_PATHSEP TO_PATHSEP FROM_PATH TO_PATH -# Verify that FROM_PATH (a path in $build format) was converted to $host -# format in TO_PATH. Otherwise, emit an error message, but continue, resetting -# func_to_host_file_result to a simplistic fallback value (see below). -func_convert_path_check () -{ - $opt_debug - if test -z "$4" && test -n "$3"; then - func_error "Could not determine the host path corresponding to" - func_error " \`$3'" - func_error "Continuing, but uninstalled executables may not work." - # Fallback. This is a deliberately simplistic "conversion" and - # should not be "improved". See libtool.info. - if test "x$1" != "x$2"; then - lt_replace_pathsep_chars="s|$1|$2|g" - func_to_host_path_result=`echo "$3" | - $SED -e "$lt_replace_pathsep_chars"` - else - func_to_host_path_result="$3" - fi - fi -} -# end func_convert_path_check - - -# func_convert_path_front_back_pathsep FRONTPAT BACKPAT REPL ORIG -# Modifies func_to_host_path_result by prepending REPL if ORIG matches FRONTPAT -# and appending REPL if ORIG matches BACKPAT. -func_convert_path_front_back_pathsep () -{ - $opt_debug - case $4 in - $1 ) func_to_host_path_result="$3$func_to_host_path_result" - ;; - esac - case $4 in - $2 ) func_append func_to_host_path_result "$3" - ;; - esac -} -# end func_convert_path_front_back_pathsep - - -################################################## -# $build to $host FILE NAME CONVERSION FUNCTIONS # -################################################## -# invoked via `$to_host_file_cmd ARG' -# -# In each case, ARG is the path to be converted from $build to $host format. -# Result will be available in $func_to_host_file_result. - - -# func_to_host_file ARG -# Converts the file name ARG from $build format to $host format. Return result -# in func_to_host_file_result. -func_to_host_file () -{ - $opt_debug - $to_host_file_cmd "$1" -} -# end func_to_host_file - - -# func_to_tool_file ARG LAZY -# converts the file name ARG from $build format to toolchain format. Return -# result in func_to_tool_file_result. If the conversion in use is listed -# in (the comma separated) LAZY, no conversion takes place. -func_to_tool_file () -{ - $opt_debug - case ,$2, in - *,"$to_tool_file_cmd",*) - func_to_tool_file_result=$1 - ;; - *) - $to_tool_file_cmd "$1" - func_to_tool_file_result=$func_to_host_file_result - ;; - esac -} -# end func_to_tool_file - - -# func_convert_file_noop ARG -# Copy ARG to func_to_host_file_result. -func_convert_file_noop () -{ - func_to_host_file_result="$1" -} -# end func_convert_file_noop - - -# func_convert_file_msys_to_w32 ARG -# Convert file name ARG from (mingw) MSYS to (mingw) w32 format; automatic -# conversion to w32 is not available inside the cwrapper. Returns result in -# func_to_host_file_result. -func_convert_file_msys_to_w32 () -{ - $opt_debug - func_to_host_file_result="$1" - if test -n "$1"; then - func_convert_core_msys_to_w32 "$1" - func_to_host_file_result="$func_convert_core_msys_to_w32_result" - fi - func_convert_file_check "$1" "$func_to_host_file_result" -} -# end func_convert_file_msys_to_w32 - - -# func_convert_file_cygwin_to_w32 ARG -# Convert file name ARG from Cygwin to w32 format. Returns result in -# func_to_host_file_result. -func_convert_file_cygwin_to_w32 () -{ - $opt_debug - func_to_host_file_result="$1" - if test -n "$1"; then - # because $build is cygwin, we call "the" cygpath in $PATH; no need to use - # LT_CYGPATH in this case. - func_to_host_file_result=`cygpath -m "$1"` - fi - func_convert_file_check "$1" "$func_to_host_file_result" -} -# end func_convert_file_cygwin_to_w32 - - -# func_convert_file_nix_to_w32 ARG -# Convert file name ARG from *nix to w32 format. Requires a wine environment -# and a working winepath. Returns result in func_to_host_file_result. -func_convert_file_nix_to_w32 () -{ - $opt_debug - func_to_host_file_result="$1" - if test -n "$1"; then - func_convert_core_file_wine_to_w32 "$1" - func_to_host_file_result="$func_convert_core_file_wine_to_w32_result" - fi - func_convert_file_check "$1" "$func_to_host_file_result" -} -# end func_convert_file_nix_to_w32 - - -# func_convert_file_msys_to_cygwin ARG -# Convert file name ARG from MSYS to Cygwin format. Requires LT_CYGPATH set. -# Returns result in func_to_host_file_result. -func_convert_file_msys_to_cygwin () -{ - $opt_debug - func_to_host_file_result="$1" - if test -n "$1"; then - func_convert_core_msys_to_w32 "$1" - func_cygpath -u "$func_convert_core_msys_to_w32_result" - func_to_host_file_result="$func_cygpath_result" - fi - func_convert_file_check "$1" "$func_to_host_file_result" -} -# end func_convert_file_msys_to_cygwin - - -# func_convert_file_nix_to_cygwin ARG -# Convert file name ARG from *nix to Cygwin format. Requires Cygwin installed -# in a wine environment, working winepath, and LT_CYGPATH set. Returns result -# in func_to_host_file_result. -func_convert_file_nix_to_cygwin () -{ - $opt_debug - func_to_host_file_result="$1" - if test -n "$1"; then - # convert from *nix to w32, then use cygpath to convert from w32 to cygwin. - func_convert_core_file_wine_to_w32 "$1" - func_cygpath -u "$func_convert_core_file_wine_to_w32_result" - func_to_host_file_result="$func_cygpath_result" - fi - func_convert_file_check "$1" "$func_to_host_file_result" -} -# end func_convert_file_nix_to_cygwin - - -############################################# -# $build to $host PATH CONVERSION FUNCTIONS # -############################################# -# invoked via `$to_host_path_cmd ARG' -# -# In each case, ARG is the path to be converted from $build to $host format. -# The result will be available in $func_to_host_path_result. -# -# Path separators are also converted from $build format to $host format. If -# ARG begins or ends with a path separator character, it is preserved (but -# converted to $host format) on output. -# -# All path conversion functions are named using the following convention: -# file name conversion function : func_convert_file_X_to_Y () -# path conversion function : func_convert_path_X_to_Y () -# where, for any given $build/$host combination the 'X_to_Y' value is the -# same. If conversion functions are added for new $build/$host combinations, -# the two new functions must follow this pattern, or func_init_to_host_path_cmd -# will break. - - -# func_init_to_host_path_cmd -# Ensures that function "pointer" variable $to_host_path_cmd is set to the -# appropriate value, based on the value of $to_host_file_cmd. -to_host_path_cmd= -func_init_to_host_path_cmd () -{ - $opt_debug - if test -z "$to_host_path_cmd"; then - func_stripname 'func_convert_file_' '' "$to_host_file_cmd" - to_host_path_cmd="func_convert_path_${func_stripname_result}" - fi -} - - -# func_to_host_path ARG -# Converts the path ARG from $build format to $host format. Return result -# in func_to_host_path_result. -func_to_host_path () -{ - $opt_debug - func_init_to_host_path_cmd - $to_host_path_cmd "$1" -} -# end func_to_host_path - - -# func_convert_path_noop ARG -# Copy ARG to func_to_host_path_result. -func_convert_path_noop () -{ - func_to_host_path_result="$1" -} -# end func_convert_path_noop - - -# func_convert_path_msys_to_w32 ARG -# Convert path ARG from (mingw) MSYS to (mingw) w32 format; automatic -# conversion to w32 is not available inside the cwrapper. Returns result in -# func_to_host_path_result. -func_convert_path_msys_to_w32 () -{ - $opt_debug - func_to_host_path_result="$1" - if test -n "$1"; then - # Remove leading and trailing path separator characters from ARG. MSYS - # behavior is inconsistent here; cygpath turns them into '.;' and ';.'; - # and winepath ignores them completely. - func_stripname : : "$1" - func_to_host_path_tmp1=$func_stripname_result - func_convert_core_msys_to_w32 "$func_to_host_path_tmp1" - func_to_host_path_result="$func_convert_core_msys_to_w32_result" - func_convert_path_check : ";" \ - "$func_to_host_path_tmp1" "$func_to_host_path_result" - func_convert_path_front_back_pathsep ":*" "*:" ";" "$1" - fi -} -# end func_convert_path_msys_to_w32 - - -# func_convert_path_cygwin_to_w32 ARG -# Convert path ARG from Cygwin to w32 format. Returns result in -# func_to_host_file_result. -func_convert_path_cygwin_to_w32 () -{ - $opt_debug - func_to_host_path_result="$1" - if test -n "$1"; then - # See func_convert_path_msys_to_w32: - func_stripname : : "$1" - func_to_host_path_tmp1=$func_stripname_result - func_to_host_path_result=`cygpath -m -p "$func_to_host_path_tmp1"` - func_convert_path_check : ";" \ - "$func_to_host_path_tmp1" "$func_to_host_path_result" - func_convert_path_front_back_pathsep ":*" "*:" ";" "$1" - fi -} -# end func_convert_path_cygwin_to_w32 - - -# func_convert_path_nix_to_w32 ARG -# Convert path ARG from *nix to w32 format. Requires a wine environment and -# a working winepath. Returns result in func_to_host_file_result. -func_convert_path_nix_to_w32 () -{ - $opt_debug - func_to_host_path_result="$1" - if test -n "$1"; then - # See func_convert_path_msys_to_w32: - func_stripname : : "$1" - func_to_host_path_tmp1=$func_stripname_result - func_convert_core_path_wine_to_w32 "$func_to_host_path_tmp1" - func_to_host_path_result="$func_convert_core_path_wine_to_w32_result" - func_convert_path_check : ";" \ - "$func_to_host_path_tmp1" "$func_to_host_path_result" - func_convert_path_front_back_pathsep ":*" "*:" ";" "$1" - fi -} -# end func_convert_path_nix_to_w32 - - -# func_convert_path_msys_to_cygwin ARG -# Convert path ARG from MSYS to Cygwin format. Requires LT_CYGPATH set. -# Returns result in func_to_host_file_result. -func_convert_path_msys_to_cygwin () -{ - $opt_debug - func_to_host_path_result="$1" - if test -n "$1"; then - # See func_convert_path_msys_to_w32: - func_stripname : : "$1" - func_to_host_path_tmp1=$func_stripname_result - func_convert_core_msys_to_w32 "$func_to_host_path_tmp1" - func_cygpath -u -p "$func_convert_core_msys_to_w32_result" - func_to_host_path_result="$func_cygpath_result" - func_convert_path_check : : \ - "$func_to_host_path_tmp1" "$func_to_host_path_result" - func_convert_path_front_back_pathsep ":*" "*:" : "$1" - fi -} -# end func_convert_path_msys_to_cygwin - - -# func_convert_path_nix_to_cygwin ARG -# Convert path ARG from *nix to Cygwin format. Requires Cygwin installed in a -# a wine environment, working winepath, and LT_CYGPATH set. Returns result in -# func_to_host_file_result. -func_convert_path_nix_to_cygwin () -{ - $opt_debug - func_to_host_path_result="$1" - if test -n "$1"; then - # Remove leading and trailing path separator characters from - # ARG. msys behavior is inconsistent here, cygpath turns them - # into '.;' and ';.', and winepath ignores them completely. - func_stripname : : "$1" - func_to_host_path_tmp1=$func_stripname_result - func_convert_core_path_wine_to_w32 "$func_to_host_path_tmp1" - func_cygpath -u -p "$func_convert_core_path_wine_to_w32_result" - func_to_host_path_result="$func_cygpath_result" - func_convert_path_check : : \ - "$func_to_host_path_tmp1" "$func_to_host_path_result" - func_convert_path_front_back_pathsep ":*" "*:" : "$1" - fi -} -# end func_convert_path_nix_to_cygwin - - -# func_mode_compile arg... -func_mode_compile () -{ - $opt_debug - # Get the compilation command and the source file. - base_compile= - srcfile="$nonopt" # always keep a non-empty value in "srcfile" - suppress_opt=yes - suppress_output= - arg_mode=normal - libobj= - later= - pie_flag= - - for arg - do - case $arg_mode in - arg ) - # do not "continue". Instead, add this to base_compile - lastarg="$arg" - arg_mode=normal - ;; - - target ) - libobj="$arg" - arg_mode=normal - continue - ;; - - normal ) - # Accept any command-line options. - case $arg in - -o) - test -n "$libobj" && \ - func_fatal_error "you cannot specify \`-o' more than once" - arg_mode=target - continue - ;; - - -pie | -fpie | -fPIE) - func_append pie_flag " $arg" - continue - ;; - - -shared | -static | -prefer-pic | -prefer-non-pic) - func_append later " $arg" - continue - ;; - - -no-suppress) - suppress_opt=no - continue - ;; - - -Xcompiler) - arg_mode=arg # the next one goes into the "base_compile" arg list - continue # The current "srcfile" will either be retained or - ;; # replaced later. I would guess that would be a bug. - - -Wc,*) - func_stripname '-Wc,' '' "$arg" - args=$func_stripname_result - lastarg= - save_ifs="$IFS"; IFS=',' - for arg in $args; do - IFS="$save_ifs" - func_append_quoted lastarg "$arg" - done - IFS="$save_ifs" - func_stripname ' ' '' "$lastarg" - lastarg=$func_stripname_result - - # Add the arguments to base_compile. - func_append base_compile " $lastarg" - continue - ;; - - *) - # Accept the current argument as the source file. - # The previous "srcfile" becomes the current argument. - # - lastarg="$srcfile" - srcfile="$arg" - ;; - esac # case $arg - ;; - esac # case $arg_mode - - # Aesthetically quote the previous argument. - func_append_quoted base_compile "$lastarg" - done # for arg - - case $arg_mode in - arg) - func_fatal_error "you must specify an argument for -Xcompile" - ;; - target) - func_fatal_error "you must specify a target with \`-o'" - ;; - *) - # Get the name of the library object. - test -z "$libobj" && { - func_basename "$srcfile" - libobj="$func_basename_result" - } - ;; - esac - - # Recognize several different file suffixes. - # If the user specifies -o file.o, it is replaced with file.lo - case $libobj in - *.[cCFSifmso] | \ - *.ada | *.adb | *.ads | *.asm | \ - *.c++ | *.cc | *.ii | *.class | *.cpp | *.cxx | \ - *.[fF][09]? | *.for | *.java | *.go | *.obj | *.sx | *.cu | *.cup) - func_xform "$libobj" - libobj=$func_xform_result - ;; - esac - - case $libobj in - *.lo) func_lo2o "$libobj"; obj=$func_lo2o_result ;; - *) - func_fatal_error "cannot determine name of library object from \`$libobj'" - ;; - esac - - func_infer_tag $base_compile - - for arg in $later; do - case $arg in - -shared) - test "$build_libtool_libs" != yes && \ - func_fatal_configuration "can not build a shared library" - build_old_libs=no - continue - ;; - - -static) - build_libtool_libs=no - build_old_libs=yes - continue - ;; - - -prefer-pic) - pic_mode=yes - continue - ;; - - -prefer-non-pic) - pic_mode=no - continue - ;; - esac - done - - func_quote_for_eval "$libobj" - test "X$libobj" != "X$func_quote_for_eval_result" \ - && $ECHO "X$libobj" | $GREP '[]~#^*{};<>?"'"'"' &()|`$[]' \ - && func_warning "libobj name \`$libobj' may not contain shell special characters." - func_dirname_and_basename "$obj" "/" "" - objname="$func_basename_result" - xdir="$func_dirname_result" - lobj=${xdir}$objdir/$objname - - test -z "$base_compile" && \ - func_fatal_help "you must specify a compilation command" - - # Delete any leftover library objects. - if test "$build_old_libs" = yes; then - removelist="$obj $lobj $libobj ${libobj}T" - else - removelist="$lobj $libobj ${libobj}T" - fi - - # On Cygwin there's no "real" PIC flag so we must build both object types - case $host_os in - cygwin* | mingw* | pw32* | os2* | cegcc*) - pic_mode=default - ;; - esac - if test "$pic_mode" = no && test "$deplibs_check_method" != pass_all; then - # non-PIC code in shared libraries is not supported - pic_mode=default - fi - - # Calculate the filename of the output object if compiler does - # not support -o with -c - if test "$compiler_c_o" = no; then - output_obj=`$ECHO "$srcfile" | $SED 's%^.*/%%; s%\.[^.]*$%%'`.${objext} - lockfile="$output_obj.lock" - else - output_obj= - need_locks=no - lockfile= - fi - - # Lock this critical section if it is needed - # We use this script file to make the link, it avoids creating a new file - if test "$need_locks" = yes; then - until $opt_dry_run || ln "$progpath" "$lockfile" 2>/dev/null; do - func_echo "Waiting for $lockfile to be removed" - sleep 2 - done - elif test "$need_locks" = warn; then - if test -f "$lockfile"; then - $ECHO "\ -*** ERROR, $lockfile exists and contains: -`cat $lockfile 2>/dev/null` - -This indicates that another process is trying to use the same -temporary object file, and libtool could not work around it because -your compiler does not support \`-c' and \`-o' together. If you -repeat this compilation, it may succeed, by chance, but you had better -avoid parallel builds (make -j) in this platform, or get a better -compiler." - - $opt_dry_run || $RM $removelist - exit $EXIT_FAILURE - fi - func_append removelist " $output_obj" - $ECHO "$srcfile" > "$lockfile" - fi - - $opt_dry_run || $RM $removelist - func_append removelist " $lockfile" - trap '$opt_dry_run || $RM $removelist; exit $EXIT_FAILURE' 1 2 15 - - func_to_tool_file "$srcfile" func_convert_file_msys_to_w32 - srcfile=$func_to_tool_file_result - func_quote_for_eval "$srcfile" - qsrcfile=$func_quote_for_eval_result - - # Only build a PIC object if we are building libtool libraries. - if test "$build_libtool_libs" = yes; then - # Without this assignment, base_compile gets emptied. - fbsd_hideous_sh_bug=$base_compile - - if test "$pic_mode" != no; then - command="$base_compile $qsrcfile $pic_flag" - else - # Don't build PIC code - command="$base_compile $qsrcfile" - fi - - func_mkdir_p "$xdir$objdir" - - if test -z "$output_obj"; then - # Place PIC objects in $objdir - func_append command " -o $lobj" - fi - - func_show_eval_locale "$command" \ - 'test -n "$output_obj" && $RM $removelist; exit $EXIT_FAILURE' - - if test "$need_locks" = warn && - test "X`cat $lockfile 2>/dev/null`" != "X$srcfile"; then - $ECHO "\ -*** ERROR, $lockfile contains: -`cat $lockfile 2>/dev/null` - -but it should contain: -$srcfile - -This indicates that another process is trying to use the same -temporary object file, and libtool could not work around it because -your compiler does not support \`-c' and \`-o' together. If you -repeat this compilation, it may succeed, by chance, but you had better -avoid parallel builds (make -j) in this platform, or get a better -compiler." - - $opt_dry_run || $RM $removelist - exit $EXIT_FAILURE - fi - - # Just move the object if needed, then go on to compile the next one - if test -n "$output_obj" && test "X$output_obj" != "X$lobj"; then - func_show_eval '$MV "$output_obj" "$lobj"' \ - 'error=$?; $opt_dry_run || $RM $removelist; exit $error' - fi - - # Allow error messages only from the first compilation. - if test "$suppress_opt" = yes; then - suppress_output=' >/dev/null 2>&1' - fi - fi - - # Only build a position-dependent object if we build old libraries. - if test "$build_old_libs" = yes; then - if test "$pic_mode" != yes; then - # Don't build PIC code - command="$base_compile $qsrcfile$pie_flag" - else - command="$base_compile $qsrcfile $pic_flag" - fi - if test "$compiler_c_o" = yes; then - func_append command " -o $obj" - fi - - # Suppress compiler output if we already did a PIC compilation. - func_append command "$suppress_output" - func_show_eval_locale "$command" \ - '$opt_dry_run || $RM $removelist; exit $EXIT_FAILURE' - - if test "$need_locks" = warn && - test "X`cat $lockfile 2>/dev/null`" != "X$srcfile"; then - $ECHO "\ -*** ERROR, $lockfile contains: -`cat $lockfile 2>/dev/null` - -but it should contain: -$srcfile - -This indicates that another process is trying to use the same -temporary object file, and libtool could not work around it because -your compiler does not support \`-c' and \`-o' together. If you -repeat this compilation, it may succeed, by chance, but you had better -avoid parallel builds (make -j) in this platform, or get a better -compiler." - - $opt_dry_run || $RM $removelist - exit $EXIT_FAILURE - fi - - # Just move the object if needed - if test -n "$output_obj" && test "X$output_obj" != "X$obj"; then - func_show_eval '$MV "$output_obj" "$obj"' \ - 'error=$?; $opt_dry_run || $RM $removelist; exit $error' - fi - fi - - $opt_dry_run || { - func_write_libtool_object "$libobj" "$objdir/$objname" "$objname" - - # Unlock the critical section if it was locked - if test "$need_locks" != no; then - removelist=$lockfile - $RM "$lockfile" - fi - } - - exit $EXIT_SUCCESS -} - -$opt_help || { - test "$opt_mode" = compile && func_mode_compile ${1+"$@"} -} - -func_mode_help () -{ - # We need to display help for each of the modes. - case $opt_mode in - "") - # Generic help is extracted from the usage comments - # at the start of this file. - func_help - ;; - - clean) - $ECHO \ -"Usage: $progname [OPTION]... --mode=clean RM [RM-OPTION]... FILE... - -Remove files from the build directory. - -RM is the name of the program to use to delete files associated with each FILE -(typically \`/bin/rm'). RM-OPTIONS are options (such as \`-f') to be passed -to RM. - -If FILE is a libtool library, object or program, all the files associated -with it are deleted. Otherwise, only FILE itself is deleted using RM." - ;; - - compile) - $ECHO \ -"Usage: $progname [OPTION]... --mode=compile COMPILE-COMMAND... SOURCEFILE - -Compile a source file into a libtool library object. - -This mode accepts the following additional options: - - -o OUTPUT-FILE set the output file name to OUTPUT-FILE - -no-suppress do not suppress compiler output for multiple passes - -prefer-pic try to build PIC objects only - -prefer-non-pic try to build non-PIC objects only - -shared do not build a \`.o' file suitable for static linking - -static only build a \`.o' file suitable for static linking - -Wc,FLAG pass FLAG directly to the compiler - -COMPILE-COMMAND is a command to be used in creating a \`standard' object file -from the given SOURCEFILE. - -The output file name is determined by removing the directory component from -SOURCEFILE, then substituting the C source code suffix \`.c' with the -library object suffix, \`.lo'." - ;; - - execute) - $ECHO \ -"Usage: $progname [OPTION]... --mode=execute COMMAND [ARGS]... - -Automatically set library path, then run a program. - -This mode accepts the following additional options: - - -dlopen FILE add the directory containing FILE to the library path - -This mode sets the library path environment variable according to \`-dlopen' -flags. - -If any of the ARGS are libtool executable wrappers, then they are translated -into their corresponding uninstalled binary, and any of their required library -directories are added to the library path. - -Then, COMMAND is executed, with ARGS as arguments." - ;; - - finish) - $ECHO \ -"Usage: $progname [OPTION]... --mode=finish [LIBDIR]... - -Complete the installation of libtool libraries. - -Each LIBDIR is a directory that contains libtool libraries. - -The commands that this mode executes may require superuser privileges. Use -the \`--dry-run' option if you just want to see what would be executed." - ;; - - install) - $ECHO \ -"Usage: $progname [OPTION]... --mode=install INSTALL-COMMAND... - -Install executables or libraries. - -INSTALL-COMMAND is the installation command. The first component should be -either the \`install' or \`cp' program. - -The following components of INSTALL-COMMAND are treated specially: - - -inst-prefix-dir PREFIX-DIR Use PREFIX-DIR as a staging area for installation - -The rest of the components are interpreted as arguments to that command (only -BSD-compatible install options are recognized)." - ;; - - link) - $ECHO \ -"Usage: $progname [OPTION]... --mode=link LINK-COMMAND... - -Link object files or libraries together to form another library, or to -create an executable program. - -LINK-COMMAND is a command using the C compiler that you would use to create -a program from several object files. - -The following components of LINK-COMMAND are treated specially: - - -all-static do not do any dynamic linking at all - -avoid-version do not add a version suffix if possible - -bindir BINDIR specify path to binaries directory (for systems where - libraries must be found in the PATH setting at runtime) - -dlopen FILE \`-dlpreopen' FILE if it cannot be dlopened at runtime - -dlpreopen FILE link in FILE and add its symbols to lt_preloaded_symbols - -export-dynamic allow symbols from OUTPUT-FILE to be resolved with dlsym(3) - -export-symbols SYMFILE - try to export only the symbols listed in SYMFILE - -export-symbols-regex REGEX - try to export only the symbols matching REGEX - -LLIBDIR search LIBDIR for required installed libraries - -lNAME OUTPUT-FILE requires the installed library libNAME - -module build a library that can dlopened - -no-fast-install disable the fast-install mode - -no-install link a not-installable executable - -no-undefined declare that a library does not refer to external symbols - -o OUTPUT-FILE create OUTPUT-FILE from the specified objects - -objectlist FILE Use a list of object files found in FILE to specify objects - -precious-files-regex REGEX - don't remove output files matching REGEX - -release RELEASE specify package release information - -rpath LIBDIR the created library will eventually be installed in LIBDIR - -R[ ]LIBDIR add LIBDIR to the runtime path of programs and libraries - -shared only do dynamic linking of libtool libraries - -shrext SUFFIX override the standard shared library file extension - -static do not do any dynamic linking of uninstalled libtool libraries - -static-libtool-libs - do not do any dynamic linking of libtool libraries - -version-info CURRENT[:REVISION[:AGE]] - specify library version info [each variable defaults to 0] - -weak LIBNAME declare that the target provides the LIBNAME interface - -Wc,FLAG - -Xcompiler FLAG pass linker-specific FLAG directly to the compiler - -Wl,FLAG - -Xlinker FLAG pass linker-specific FLAG directly to the linker - -XCClinker FLAG pass link-specific FLAG to the compiler driver (CC) - -All other options (arguments beginning with \`-') are ignored. - -Every other argument is treated as a filename. Files ending in \`.la' are -treated as uninstalled libtool libraries, other files are standard or library -object files. - -If the OUTPUT-FILE ends in \`.la', then a libtool library is created, -only library objects (\`.lo' files) may be specified, and \`-rpath' is -required, except when creating a convenience library. - -If OUTPUT-FILE ends in \`.a' or \`.lib', then a standard library is created -using \`ar' and \`ranlib', or on Windows using \`lib'. - -If OUTPUT-FILE ends in \`.lo' or \`.${objext}', then a reloadable object file -is created, otherwise an executable program is created." - ;; - - uninstall) - $ECHO \ -"Usage: $progname [OPTION]... --mode=uninstall RM [RM-OPTION]... FILE... - -Remove libraries from an installation directory. - -RM is the name of the program to use to delete files associated with each FILE -(typically \`/bin/rm'). RM-OPTIONS are options (such as \`-f') to be passed -to RM. - -If FILE is a libtool library, all the files associated with it are deleted. -Otherwise, only FILE itself is deleted using RM." - ;; - - *) - func_fatal_help "invalid operation mode \`$opt_mode'" - ;; - esac - - echo - $ECHO "Try \`$progname --help' for more information about other modes." -} - -# Now that we've collected a possible --mode arg, show help if necessary -if $opt_help; then - if test "$opt_help" = :; then - func_mode_help - else - { - func_help noexit - for opt_mode in compile link execute install finish uninstall clean; do - func_mode_help - done - } | sed -n '1p; 2,$s/^Usage:/ or: /p' - { - func_help noexit - for opt_mode in compile link execute install finish uninstall clean; do - echo - func_mode_help - done - } | - sed '1d - /^When reporting/,/^Report/{ - H - d - } - $x - /information about other modes/d - /more detailed .*MODE/d - s/^Usage:.*--mode=\([^ ]*\) .*/Description of \1 mode:/' - fi - exit $? -fi - - -# func_mode_execute arg... -func_mode_execute () -{ - $opt_debug - # The first argument is the command name. - cmd="$nonopt" - test -z "$cmd" && \ - func_fatal_help "you must specify a COMMAND" - - # Handle -dlopen flags immediately. - for file in $opt_dlopen; do - test -f "$file" \ - || func_fatal_help "\`$file' is not a file" - - dir= - case $file in - *.la) - func_resolve_sysroot "$file" - file=$func_resolve_sysroot_result - - # Check to see that this really is a libtool archive. - func_lalib_unsafe_p "$file" \ - || func_fatal_help "\`$lib' is not a valid libtool archive" - - # Read the libtool library. - dlname= - library_names= - func_source "$file" - - # Skip this library if it cannot be dlopened. - if test -z "$dlname"; then - # Warn if it was a shared library. - test -n "$library_names" && \ - func_warning "\`$file' was not linked with \`-export-dynamic'" - continue - fi - - func_dirname "$file" "" "." - dir="$func_dirname_result" - - if test -f "$dir/$objdir/$dlname"; then - func_append dir "/$objdir" - else - if test ! -f "$dir/$dlname"; then - func_fatal_error "cannot find \`$dlname' in \`$dir' or \`$dir/$objdir'" - fi - fi - ;; - - *.lo) - # Just add the directory containing the .lo file. - func_dirname "$file" "" "." - dir="$func_dirname_result" - ;; - - *) - func_warning "\`-dlopen' is ignored for non-libtool libraries and objects" - continue - ;; - esac - - # Get the absolute pathname. - absdir=`cd "$dir" && pwd` - test -n "$absdir" && dir="$absdir" - - # Now add the directory to shlibpath_var. - if eval "test -z \"\$$shlibpath_var\""; then - eval "$shlibpath_var=\"\$dir\"" - else - eval "$shlibpath_var=\"\$dir:\$$shlibpath_var\"" - fi - done - - # This variable tells wrapper scripts just to set shlibpath_var - # rather than running their programs. - libtool_execute_magic="$magic" - - # Check if any of the arguments is a wrapper script. - args= - for file - do - case $file in - -* | *.la | *.lo ) ;; - *) - # Do a test to see if this is really a libtool program. - if func_ltwrapper_script_p "$file"; then - func_source "$file" - # Transform arg to wrapped name. - file="$progdir/$program" - elif func_ltwrapper_executable_p "$file"; then - func_ltwrapper_scriptname "$file" - func_source "$func_ltwrapper_scriptname_result" - # Transform arg to wrapped name. - file="$progdir/$program" - fi - ;; - esac - # Quote arguments (to preserve shell metacharacters). - func_append_quoted args "$file" - done - - if test "X$opt_dry_run" = Xfalse; then - if test -n "$shlibpath_var"; then - # Export the shlibpath_var. - eval "export $shlibpath_var" - fi - - # Restore saved environment variables - for lt_var in LANG LANGUAGE LC_ALL LC_CTYPE LC_COLLATE LC_MESSAGES - do - eval "if test \"\${save_$lt_var+set}\" = set; then - $lt_var=\$save_$lt_var; export $lt_var - else - $lt_unset $lt_var - fi" - done - - # Now prepare to actually exec the command. - exec_cmd="\$cmd$args" - else - # Display what would be done. - if test -n "$shlibpath_var"; then - eval "\$ECHO \"\$shlibpath_var=\$$shlibpath_var\"" - echo "export $shlibpath_var" - fi - $ECHO "$cmd$args" - exit $EXIT_SUCCESS - fi -} - -test "$opt_mode" = execute && func_mode_execute ${1+"$@"} - - -# func_mode_finish arg... -func_mode_finish () -{ - $opt_debug - libs= - libdirs= - admincmds= - - for opt in "$nonopt" ${1+"$@"} - do - if test -d "$opt"; then - func_append libdirs " $opt" - - elif test -f "$opt"; then - if func_lalib_unsafe_p "$opt"; then - func_append libs " $opt" - else - func_warning "\`$opt' is not a valid libtool archive" - fi - - else - func_fatal_error "invalid argument \`$opt'" - fi - done - - if test -n "$libs"; then - if test -n "$lt_sysroot"; then - sysroot_regex=`$ECHO "$lt_sysroot" | $SED "$sed_make_literal_regex"` - sysroot_cmd="s/\([ ']\)$sysroot_regex/\1/g;" - else - sysroot_cmd= - fi - - # Remove sysroot references - if $opt_dry_run; then - for lib in $libs; do - echo "removing references to $lt_sysroot and \`=' prefixes from $lib" - done - else - tmpdir=`func_mktempdir` - for lib in $libs; do - sed -e "${sysroot_cmd} s/\([ ']-[LR]\)=/\1/g; s/\([ ']\)=/\1/g" $lib \ - > $tmpdir/tmp-la - mv -f $tmpdir/tmp-la $lib - done - ${RM}r "$tmpdir" - fi - fi - - if test -n "$finish_cmds$finish_eval" && test -n "$libdirs"; then - for libdir in $libdirs; do - if test -n "$finish_cmds"; then - # Do each command in the finish commands. - func_execute_cmds "$finish_cmds" 'admincmds="$admincmds -'"$cmd"'"' - fi - if test -n "$finish_eval"; then - # Do the single finish_eval. - eval cmds=\"$finish_eval\" - $opt_dry_run || eval "$cmds" || func_append admincmds " - $cmds" - fi - done - fi - - # Exit here if they wanted silent mode. - $opt_silent && exit $EXIT_SUCCESS - - if test -n "$finish_cmds$finish_eval" && test -n "$libdirs"; then - echo "----------------------------------------------------------------------" - echo "Libraries have been installed in:" - for libdir in $libdirs; do - $ECHO " $libdir" - done - echo - echo "If you ever happen to want to link against installed libraries" - echo "in a given directory, LIBDIR, you must either use libtool, and" - echo "specify the full pathname of the library, or use the \`-LLIBDIR'" - echo "flag during linking and do at least one of the following:" - if test -n "$shlibpath_var"; then - echo " - add LIBDIR to the \`$shlibpath_var' environment variable" - echo " during execution" - fi - if test -n "$runpath_var"; then - echo " - add LIBDIR to the \`$runpath_var' environment variable" - echo " during linking" - fi - if test -n "$hardcode_libdir_flag_spec"; then - libdir=LIBDIR - eval flag=\"$hardcode_libdir_flag_spec\" - - $ECHO " - use the \`$flag' linker flag" - fi - if test -n "$admincmds"; then - $ECHO " - have your system administrator run these commands:$admincmds" - fi - if test -f /etc/ld.so.conf; then - echo " - have your system administrator add LIBDIR to \`/etc/ld.so.conf'" - fi - echo - - echo "See any operating system documentation about shared libraries for" - case $host in - solaris2.[6789]|solaris2.1[0-9]) - echo "more information, such as the ld(1), crle(1) and ld.so(8) manual" - echo "pages." - ;; - *) - echo "more information, such as the ld(1) and ld.so(8) manual pages." - ;; - esac - echo "----------------------------------------------------------------------" - fi - exit $EXIT_SUCCESS -} - -test "$opt_mode" = finish && func_mode_finish ${1+"$@"} - - -# func_mode_install arg... -func_mode_install () -{ - $opt_debug - # There may be an optional sh(1) argument at the beginning of - # install_prog (especially on Windows NT). - if test "$nonopt" = "$SHELL" || test "$nonopt" = /bin/sh || - # Allow the use of GNU shtool's install command. - case $nonopt in *shtool*) :;; *) false;; esac; then - # Aesthetically quote it. - func_quote_for_eval "$nonopt" - install_prog="$func_quote_for_eval_result " - arg=$1 - shift - else - install_prog= - arg=$nonopt - fi - - # The real first argument should be the name of the installation program. - # Aesthetically quote it. - func_quote_for_eval "$arg" - func_append install_prog "$func_quote_for_eval_result" - install_shared_prog=$install_prog - case " $install_prog " in - *[\\\ /]cp\ *) install_cp=: ;; - *) install_cp=false ;; - esac - - # We need to accept at least all the BSD install flags. - dest= - files= - opts= - prev= - install_type= - isdir=no - stripme= - no_mode=: - for arg - do - arg2= - if test -n "$dest"; then - func_append files " $dest" - dest=$arg - continue - fi - - case $arg in - -d) isdir=yes ;; - -f) - if $install_cp; then :; else - prev=$arg - fi - ;; - -g | -m | -o) - prev=$arg - ;; - -s) - stripme=" -s" - continue - ;; - -*) - ;; - *) - # If the previous option needed an argument, then skip it. - if test -n "$prev"; then - if test "x$prev" = x-m && test -n "$install_override_mode"; then - arg2=$install_override_mode - no_mode=false - fi - prev= - else - dest=$arg - continue - fi - ;; - esac - - # Aesthetically quote the argument. - func_quote_for_eval "$arg" - func_append install_prog " $func_quote_for_eval_result" - if test -n "$arg2"; then - func_quote_for_eval "$arg2" - fi - func_append install_shared_prog " $func_quote_for_eval_result" - done - - test -z "$install_prog" && \ - func_fatal_help "you must specify an install program" - - test -n "$prev" && \ - func_fatal_help "the \`$prev' option requires an argument" - - if test -n "$install_override_mode" && $no_mode; then - if $install_cp; then :; else - func_quote_for_eval "$install_override_mode" - func_append install_shared_prog " -m $func_quote_for_eval_result" - fi - fi - - if test -z "$files"; then - if test -z "$dest"; then - func_fatal_help "no file or destination specified" - else - func_fatal_help "you must specify a destination" - fi - fi - - # Strip any trailing slash from the destination. - func_stripname '' '/' "$dest" - dest=$func_stripname_result - - # Check to see that the destination is a directory. - test -d "$dest" && isdir=yes - if test "$isdir" = yes; then - destdir="$dest" - destname= - else - func_dirname_and_basename "$dest" "" "." - destdir="$func_dirname_result" - destname="$func_basename_result" - - # Not a directory, so check to see that there is only one file specified. - set dummy $files; shift - test "$#" -gt 1 && \ - func_fatal_help "\`$dest' is not a directory" - fi - case $destdir in - [\\/]* | [A-Za-z]:[\\/]*) ;; - *) - for file in $files; do - case $file in - *.lo) ;; - *) - func_fatal_help "\`$destdir' must be an absolute directory name" - ;; - esac - done - ;; - esac - - # This variable tells wrapper scripts just to set variables rather - # than running their programs. - libtool_install_magic="$magic" - - staticlibs= - future_libdirs= - current_libdirs= - for file in $files; do - - # Do each installation. - case $file in - *.$libext) - # Do the static libraries later. - func_append staticlibs " $file" - ;; - - *.la) - func_resolve_sysroot "$file" - file=$func_resolve_sysroot_result - - # Check to see that this really is a libtool archive. - func_lalib_unsafe_p "$file" \ - || func_fatal_help "\`$file' is not a valid libtool archive" - - library_names= - old_library= - relink_command= - func_source "$file" - - # Add the libdir to current_libdirs if it is the destination. - if test "X$destdir" = "X$libdir"; then - case "$current_libdirs " in - *" $libdir "*) ;; - *) func_append current_libdirs " $libdir" ;; - esac - else - # Note the libdir as a future libdir. - case "$future_libdirs " in - *" $libdir "*) ;; - *) func_append future_libdirs " $libdir" ;; - esac - fi - - func_dirname "$file" "/" "" - dir="$func_dirname_result" - func_append dir "$objdir" - - if test -n "$relink_command"; then - # Determine the prefix the user has applied to our future dir. - inst_prefix_dir=`$ECHO "$destdir" | $SED -e "s%$libdir\$%%"` - - # Don't allow the user to place us outside of our expected - # location b/c this prevents finding dependent libraries that - # are installed to the same prefix. - # At present, this check doesn't affect windows .dll's that - # are installed into $libdir/../bin (currently, that works fine) - # but it's something to keep an eye on. - test "$inst_prefix_dir" = "$destdir" && \ - func_fatal_error "error: cannot install \`$file' to a directory not ending in $libdir" - - if test -n "$inst_prefix_dir"; then - # Stick the inst_prefix_dir data into the link command. - relink_command=`$ECHO "$relink_command" | $SED "s%@inst_prefix_dir@%-inst-prefix-dir $inst_prefix_dir%"` - else - relink_command=`$ECHO "$relink_command" | $SED "s%@inst_prefix_dir@%%"` - fi - - func_warning "relinking \`$file'" - func_show_eval "$relink_command" \ - 'func_fatal_error "error: relink \`$file'\'' with the above command before installing it"' - fi - - # See the names of the shared library. - set dummy $library_names; shift - if test -n "$1"; then - realname="$1" - shift - - srcname="$realname" - test -n "$relink_command" && srcname="$realname"T - - # Install the shared library and build the symlinks. - func_show_eval "$install_shared_prog $dir/$srcname $destdir/$realname" \ - 'exit $?' - tstripme="$stripme" - case $host_os in - cygwin* | mingw* | pw32* | cegcc*) - case $realname in - *.dll.a) - tstripme="" - ;; - esac - ;; - esac - if test -n "$tstripme" && test -n "$striplib"; then - func_show_eval "$striplib $destdir/$realname" 'exit $?' - fi - - if test "$#" -gt 0; then - # Delete the old symlinks, and create new ones. - # Try `ln -sf' first, because the `ln' binary might depend on - # the symlink we replace! Solaris /bin/ln does not understand -f, - # so we also need to try rm && ln -s. - for linkname - do - test "$linkname" != "$realname" \ - && func_show_eval "(cd $destdir && { $LN_S -f $realname $linkname || { $RM $linkname && $LN_S $realname $linkname; }; })" - done - fi - - # Do each command in the postinstall commands. - lib="$destdir/$realname" - func_execute_cmds "$postinstall_cmds" 'exit $?' - fi - - # Install the pseudo-library for information purposes. - func_basename "$file" - name="$func_basename_result" - instname="$dir/$name"i - func_show_eval "$install_prog $instname $destdir/$name" 'exit $?' - - # Maybe install the static library, too. - test -n "$old_library" && func_append staticlibs " $dir/$old_library" - ;; - - *.lo) - # Install (i.e. copy) a libtool object. - - # Figure out destination file name, if it wasn't already specified. - if test -n "$destname"; then - destfile="$destdir/$destname" - else - func_basename "$file" - destfile="$func_basename_result" - destfile="$destdir/$destfile" - fi - - # Deduce the name of the destination old-style object file. - case $destfile in - *.lo) - func_lo2o "$destfile" - staticdest=$func_lo2o_result - ;; - *.$objext) - staticdest="$destfile" - destfile= - ;; - *) - func_fatal_help "cannot copy a libtool object to \`$destfile'" - ;; - esac - - # Install the libtool object if requested. - test -n "$destfile" && \ - func_show_eval "$install_prog $file $destfile" 'exit $?' - - # Install the old object if enabled. - if test "$build_old_libs" = yes; then - # Deduce the name of the old-style object file. - func_lo2o "$file" - staticobj=$func_lo2o_result - func_show_eval "$install_prog \$staticobj \$staticdest" 'exit $?' - fi - exit $EXIT_SUCCESS - ;; - - *) - # Figure out destination file name, if it wasn't already specified. - if test -n "$destname"; then - destfile="$destdir/$destname" - else - func_basename "$file" - destfile="$func_basename_result" - destfile="$destdir/$destfile" - fi - - # If the file is missing, and there is a .exe on the end, strip it - # because it is most likely a libtool script we actually want to - # install - stripped_ext="" - case $file in - *.exe) - if test ! -f "$file"; then - func_stripname '' '.exe' "$file" - file=$func_stripname_result - stripped_ext=".exe" - fi - ;; - esac - - # Do a test to see if this is really a libtool program. - case $host in - *cygwin* | *mingw*) - if func_ltwrapper_executable_p "$file"; then - func_ltwrapper_scriptname "$file" - wrapper=$func_ltwrapper_scriptname_result - else - func_stripname '' '.exe' "$file" - wrapper=$func_stripname_result - fi - ;; - *) - wrapper=$file - ;; - esac - if func_ltwrapper_script_p "$wrapper"; then - notinst_deplibs= - relink_command= - - func_source "$wrapper" - - # Check the variables that should have been set. - test -z "$generated_by_libtool_version" && \ - func_fatal_error "invalid libtool wrapper script \`$wrapper'" - - finalize=yes - for lib in $notinst_deplibs; do - # Check to see that each library is installed. - libdir= - if test -f "$lib"; then - func_source "$lib" - fi - libfile="$libdir/"`$ECHO "$lib" | $SED 's%^.*/%%g'` ### testsuite: skip nested quoting test - if test -n "$libdir" && test ! -f "$libfile"; then - func_warning "\`$lib' has not been installed in \`$libdir'" - finalize=no - fi - done - - relink_command= - func_source "$wrapper" - - outputname= - if test "$fast_install" = no && test -n "$relink_command"; then - $opt_dry_run || { - if test "$finalize" = yes; then - tmpdir=`func_mktempdir` - func_basename "$file$stripped_ext" - file="$func_basename_result" - outputname="$tmpdir/$file" - # Replace the output file specification. - relink_command=`$ECHO "$relink_command" | $SED 's%@OUTPUT@%'"$outputname"'%g'` - - $opt_silent || { - func_quote_for_expand "$relink_command" - eval "func_echo $func_quote_for_expand_result" - } - if eval "$relink_command"; then : - else - func_error "error: relink \`$file' with the above command before installing it" - $opt_dry_run || ${RM}r "$tmpdir" - continue - fi - file="$outputname" - else - func_warning "cannot relink \`$file'" - fi - } - else - # Install the binary that we compiled earlier. - file=`$ECHO "$file$stripped_ext" | $SED "s%\([^/]*\)$%$objdir/\1%"` - fi - fi - - # remove .exe since cygwin /usr/bin/install will append another - # one anyway - case $install_prog,$host in - */usr/bin/install*,*cygwin*) - case $file:$destfile in - *.exe:*.exe) - # this is ok - ;; - *.exe:*) - destfile=$destfile.exe - ;; - *:*.exe) - func_stripname '' '.exe' "$destfile" - destfile=$func_stripname_result - ;; - esac - ;; - esac - func_show_eval "$install_prog\$stripme \$file \$destfile" 'exit $?' - $opt_dry_run || if test -n "$outputname"; then - ${RM}r "$tmpdir" - fi - ;; - esac - done - - for file in $staticlibs; do - func_basename "$file" - name="$func_basename_result" - - # Set up the ranlib parameters. - oldlib="$destdir/$name" - func_to_tool_file "$oldlib" func_convert_file_msys_to_w32 - tool_oldlib=$func_to_tool_file_result - - func_show_eval "$install_prog \$file \$oldlib" 'exit $?' - - if test -n "$stripme" && test -n "$old_striplib"; then - func_show_eval "$old_striplib $tool_oldlib" 'exit $?' - fi - - # Do each command in the postinstall commands. - func_execute_cmds "$old_postinstall_cmds" 'exit $?' - done - - test -n "$future_libdirs" && \ - func_warning "remember to run \`$progname --finish$future_libdirs'" - - if test -n "$current_libdirs"; then - # Maybe just do a dry run. - $opt_dry_run && current_libdirs=" -n$current_libdirs" - exec_cmd='$SHELL $progpath $preserve_args --finish$current_libdirs' - else - exit $EXIT_SUCCESS - fi -} - -test "$opt_mode" = install && func_mode_install ${1+"$@"} - - -# func_generate_dlsyms outputname originator pic_p -# Extract symbols from dlprefiles and create ${outputname}S.o with -# a dlpreopen symbol table. -func_generate_dlsyms () -{ - $opt_debug - my_outputname="$1" - my_originator="$2" - my_pic_p="${3-no}" - my_prefix=`$ECHO "$my_originator" | sed 's%[^a-zA-Z0-9]%_%g'` - my_dlsyms= - - if test -n "$dlfiles$dlprefiles" || test "$dlself" != no; then - if test -n "$NM" && test -n "$global_symbol_pipe"; then - my_dlsyms="${my_outputname}S.c" - else - func_error "not configured to extract global symbols from dlpreopened files" - fi - fi - - if test -n "$my_dlsyms"; then - case $my_dlsyms in - "") ;; - *.c) - # Discover the nlist of each of the dlfiles. - nlist="$output_objdir/${my_outputname}.nm" - - func_show_eval "$RM $nlist ${nlist}S ${nlist}T" - - # Parse the name list into a source file. - func_verbose "creating $output_objdir/$my_dlsyms" - - $opt_dry_run || $ECHO > "$output_objdir/$my_dlsyms" "\ -/* $my_dlsyms - symbol resolution table for \`$my_outputname' dlsym emulation. */ -/* Generated by $PROGRAM (GNU $PACKAGE$TIMESTAMP) $VERSION */ - -#ifdef __cplusplus -extern \"C\" { -#endif - -#if defined(__GNUC__) && (((__GNUC__ == 4) && (__GNUC_MINOR__ >= 4)) || (__GNUC__ > 4)) -#pragma GCC diagnostic ignored \"-Wstrict-prototypes\" -#endif - -/* Keep this code in sync between libtool.m4, ltmain, lt_system.h, and tests. */ -#if defined(_WIN32) || defined(__CYGWIN__) || defined(_WIN32_WCE) -/* DATA imports from DLLs on WIN32 con't be const, because runtime - relocations are performed -- see ld's documentation on pseudo-relocs. */ -# define LT_DLSYM_CONST -#elif defined(__osf__) -/* This system does not cope well with relocations in const data. */ -# define LT_DLSYM_CONST -#else -# define LT_DLSYM_CONST const -#endif - -/* External symbol declarations for the compiler. */\ -" - - if test "$dlself" = yes; then - func_verbose "generating symbol list for \`$output'" - - $opt_dry_run || echo ': @PROGRAM@ ' > "$nlist" - - # Add our own program objects to the symbol list. - progfiles=`$ECHO "$objs$old_deplibs" | $SP2NL | $SED "$lo2o" | $NL2SP` - for progfile in $progfiles; do - func_to_tool_file "$progfile" func_convert_file_msys_to_w32 - func_verbose "extracting global C symbols from \`$func_to_tool_file_result'" - $opt_dry_run || eval "$NM $func_to_tool_file_result | $global_symbol_pipe >> '$nlist'" - done - - if test -n "$exclude_expsyms"; then - $opt_dry_run || { - eval '$EGREP -v " ($exclude_expsyms)$" "$nlist" > "$nlist"T' - eval '$MV "$nlist"T "$nlist"' - } - fi - - if test -n "$export_symbols_regex"; then - $opt_dry_run || { - eval '$EGREP -e "$export_symbols_regex" "$nlist" > "$nlist"T' - eval '$MV "$nlist"T "$nlist"' - } - fi - - # Prepare the list of exported symbols - if test -z "$export_symbols"; then - export_symbols="$output_objdir/$outputname.exp" - $opt_dry_run || { - $RM $export_symbols - eval "${SED} -n -e '/^: @PROGRAM@ $/d' -e 's/^.* \(.*\)$/\1/p' "'< "$nlist" > "$export_symbols"' - case $host in - *cygwin* | *mingw* | *cegcc* ) - eval "echo EXPORTS "'> "$output_objdir/$outputname.def"' - eval 'cat "$export_symbols" >> "$output_objdir/$outputname.def"' - ;; - esac - } - else - $opt_dry_run || { - eval "${SED} -e 's/\([].[*^$]\)/\\\\\1/g' -e 's/^/ /' -e 's/$/$/'"' < "$export_symbols" > "$output_objdir/$outputname.exp"' - eval '$GREP -f "$output_objdir/$outputname.exp" < "$nlist" > "$nlist"T' - eval '$MV "$nlist"T "$nlist"' - case $host in - *cygwin* | *mingw* | *cegcc* ) - eval "echo EXPORTS "'> "$output_objdir/$outputname.def"' - eval 'cat "$nlist" >> "$output_objdir/$outputname.def"' - ;; - esac - } - fi - fi - - for dlprefile in $dlprefiles; do - func_verbose "extracting global C symbols from \`$dlprefile'" - func_basename "$dlprefile" - name="$func_basename_result" - case $host in - *cygwin* | *mingw* | *cegcc* ) - # if an import library, we need to obtain dlname - if func_win32_import_lib_p "$dlprefile"; then - func_tr_sh "$dlprefile" - eval "curr_lafile=\$libfile_$func_tr_sh_result" - dlprefile_dlbasename="" - if test -n "$curr_lafile" && func_lalib_p "$curr_lafile"; then - # Use subshell, to avoid clobbering current variable values - dlprefile_dlname=`source "$curr_lafile" && echo "$dlname"` - if test -n "$dlprefile_dlname" ; then - func_basename "$dlprefile_dlname" - dlprefile_dlbasename="$func_basename_result" - else - # no lafile. user explicitly requested -dlpreopen . - $sharedlib_from_linklib_cmd "$dlprefile" - dlprefile_dlbasename=$sharedlib_from_linklib_result - fi - fi - $opt_dry_run || { - if test -n "$dlprefile_dlbasename" ; then - eval '$ECHO ": $dlprefile_dlbasename" >> "$nlist"' - else - func_warning "Could not compute DLL name from $name" - eval '$ECHO ": $name " >> "$nlist"' - fi - func_to_tool_file "$dlprefile" func_convert_file_msys_to_w32 - eval "$NM \"$func_to_tool_file_result\" 2>/dev/null | $global_symbol_pipe | - $SED -e '/I __imp/d' -e 's/I __nm_/D /;s/_nm__//' >> '$nlist'" - } - else # not an import lib - $opt_dry_run || { - eval '$ECHO ": $name " >> "$nlist"' - func_to_tool_file "$dlprefile" func_convert_file_msys_to_w32 - eval "$NM \"$func_to_tool_file_result\" 2>/dev/null | $global_symbol_pipe >> '$nlist'" - } - fi - ;; - *) - $opt_dry_run || { - eval '$ECHO ": $name " >> "$nlist"' - func_to_tool_file "$dlprefile" func_convert_file_msys_to_w32 - eval "$NM \"$func_to_tool_file_result\" 2>/dev/null | $global_symbol_pipe >> '$nlist'" - } - ;; - esac - done - - $opt_dry_run || { - # Make sure we have at least an empty file. - test -f "$nlist" || : > "$nlist" - - if test -n "$exclude_expsyms"; then - $EGREP -v " ($exclude_expsyms)$" "$nlist" > "$nlist"T - $MV "$nlist"T "$nlist" - fi - - # Try sorting and uniquifying the output. - if $GREP -v "^: " < "$nlist" | - if sort -k 3 /dev/null 2>&1; then - sort -k 3 - else - sort +2 - fi | - uniq > "$nlist"S; then - : - else - $GREP -v "^: " < "$nlist" > "$nlist"S - fi - - if test -f "$nlist"S; then - eval "$global_symbol_to_cdecl"' < "$nlist"S >> "$output_objdir/$my_dlsyms"' - else - echo '/* NONE */' >> "$output_objdir/$my_dlsyms" - fi - - echo >> "$output_objdir/$my_dlsyms" "\ - -/* The mapping between symbol names and symbols. */ -typedef struct { - const char *name; - void *address; -} lt_dlsymlist; -extern LT_DLSYM_CONST lt_dlsymlist -lt_${my_prefix}_LTX_preloaded_symbols[]; -LT_DLSYM_CONST lt_dlsymlist -lt_${my_prefix}_LTX_preloaded_symbols[] = -{\ - { \"$my_originator\", (void *) 0 }," - - case $need_lib_prefix in - no) - eval "$global_symbol_to_c_name_address" < "$nlist" >> "$output_objdir/$my_dlsyms" - ;; - *) - eval "$global_symbol_to_c_name_address_lib_prefix" < "$nlist" >> "$output_objdir/$my_dlsyms" - ;; - esac - echo >> "$output_objdir/$my_dlsyms" "\ - {0, (void *) 0} -}; - -/* This works around a problem in FreeBSD linker */ -#ifdef FREEBSD_WORKAROUND -static const void *lt_preloaded_setup() { - return lt_${my_prefix}_LTX_preloaded_symbols; -} -#endif - -#ifdef __cplusplus -} -#endif\ -" - } # !$opt_dry_run - - pic_flag_for_symtable= - case "$compile_command " in - *" -static "*) ;; - *) - case $host in - # compiling the symbol table file with pic_flag works around - # a FreeBSD bug that causes programs to crash when -lm is - # linked before any other PIC object. But we must not use - # pic_flag when linking with -static. The problem exists in - # FreeBSD 2.2.6 and is fixed in FreeBSD 3.1. - *-*-freebsd2.*|*-*-freebsd3.0*|*-*-freebsdelf3.0*) - pic_flag_for_symtable=" $pic_flag -DFREEBSD_WORKAROUND" ;; - *-*-hpux*) - pic_flag_for_symtable=" $pic_flag" ;; - *) - if test "X$my_pic_p" != Xno; then - pic_flag_for_symtable=" $pic_flag" - fi - ;; - esac - ;; - esac - symtab_cflags= - for arg in $LTCFLAGS; do - case $arg in - -pie | -fpie | -fPIE) ;; - *) func_append symtab_cflags " $arg" ;; - esac - done - - # Now compile the dynamic symbol file. - func_show_eval '(cd $output_objdir && $LTCC$symtab_cflags -c$no_builtin_flag$pic_flag_for_symtable "$my_dlsyms")' 'exit $?' - - # Clean up the generated files. - func_show_eval '$RM "$output_objdir/$my_dlsyms" "$nlist" "${nlist}S" "${nlist}T"' - - # Transform the symbol file into the correct name. - symfileobj="$output_objdir/${my_outputname}S.$objext" - case $host in - *cygwin* | *mingw* | *cegcc* ) - if test -f "$output_objdir/$my_outputname.def"; then - compile_command=`$ECHO "$compile_command" | $SED "s%@SYMFILE@%$output_objdir/$my_outputname.def $symfileobj%"` - finalize_command=`$ECHO "$finalize_command" | $SED "s%@SYMFILE@%$output_objdir/$my_outputname.def $symfileobj%"` - else - compile_command=`$ECHO "$compile_command" | $SED "s%@SYMFILE@%$symfileobj%"` - finalize_command=`$ECHO "$finalize_command" | $SED "s%@SYMFILE@%$symfileobj%"` - fi - ;; - *) - compile_command=`$ECHO "$compile_command" | $SED "s%@SYMFILE@%$symfileobj%"` - finalize_command=`$ECHO "$finalize_command" | $SED "s%@SYMFILE@%$symfileobj%"` - ;; - esac - ;; - *) - func_fatal_error "unknown suffix for \`$my_dlsyms'" - ;; - esac - else - # We keep going just in case the user didn't refer to - # lt_preloaded_symbols. The linker will fail if global_symbol_pipe - # really was required. - - # Nullify the symbol file. - compile_command=`$ECHO "$compile_command" | $SED "s% @SYMFILE@%%"` - finalize_command=`$ECHO "$finalize_command" | $SED "s% @SYMFILE@%%"` - fi -} - -# func_win32_libid arg -# return the library type of file 'arg' -# -# Need a lot of goo to handle *both* DLLs and import libs -# Has to be a shell function in order to 'eat' the argument -# that is supplied when $file_magic_command is called. -# Despite the name, also deal with 64 bit binaries. -func_win32_libid () -{ - $opt_debug - win32_libid_type="unknown" - win32_fileres=`file -L $1 2>/dev/null` - case $win32_fileres in - *ar\ archive\ import\ library*) # definitely import - win32_libid_type="x86 archive import" - ;; - *ar\ archive*) # could be an import, or static - # Keep the egrep pattern in sync with the one in _LT_CHECK_MAGIC_METHOD. - if eval $OBJDUMP -f $1 | $SED -e '10q' 2>/dev/null | - $EGREP 'file format (pei*-i386(.*architecture: i386)?|pe-arm-wince|pe-x86-64)' >/dev/null; then - func_to_tool_file "$1" func_convert_file_msys_to_w32 - win32_nmres=`eval $NM -f posix -A \"$func_to_tool_file_result\" | - $SED -n -e ' - 1,100{ - / I /{ - s,.*,import, - p - q - } - }'` - case $win32_nmres in - import*) win32_libid_type="x86 archive import";; - *) win32_libid_type="x86 archive static";; - esac - fi - ;; - *DLL*) - win32_libid_type="x86 DLL" - ;; - *executable*) # but shell scripts are "executable" too... - case $win32_fileres in - *MS\ Windows\ PE\ Intel*) - win32_libid_type="x86 DLL" - ;; - esac - ;; - esac - $ECHO "$win32_libid_type" -} - -# func_cygming_dll_for_implib ARG -# -# Platform-specific function to extract the -# name of the DLL associated with the specified -# import library ARG. -# Invoked by eval'ing the libtool variable -# $sharedlib_from_linklib_cmd -# Result is available in the variable -# $sharedlib_from_linklib_result -func_cygming_dll_for_implib () -{ - $opt_debug - sharedlib_from_linklib_result=`$DLLTOOL --identify-strict --identify "$1"` -} - -# func_cygming_dll_for_implib_fallback_core SECTION_NAME LIBNAMEs -# -# The is the core of a fallback implementation of a -# platform-specific function to extract the name of the -# DLL associated with the specified import library LIBNAME. -# -# SECTION_NAME is either .idata$6 or .idata$7, depending -# on the platform and compiler that created the implib. -# -# Echos the name of the DLL associated with the -# specified import library. -func_cygming_dll_for_implib_fallback_core () -{ - $opt_debug - match_literal=`$ECHO "$1" | $SED "$sed_make_literal_regex"` - $OBJDUMP -s --section "$1" "$2" 2>/dev/null | - $SED '/^Contents of section '"$match_literal"':/{ - # Place marker at beginning of archive member dllname section - s/.*/====MARK====/ - p - d - } - # These lines can sometimes be longer than 43 characters, but - # are always uninteresting - /:[ ]*file format pe[i]\{,1\}-/d - /^In archive [^:]*:/d - # Ensure marker is printed - /^====MARK====/p - # Remove all lines with less than 43 characters - /^.\{43\}/!d - # From remaining lines, remove first 43 characters - s/^.\{43\}//' | - $SED -n ' - # Join marker and all lines until next marker into a single line - /^====MARK====/ b para - H - $ b para - b - :para - x - s/\n//g - # Remove the marker - s/^====MARK====// - # Remove trailing dots and whitespace - s/[\. \t]*$// - # Print - /./p' | - # we now have a list, one entry per line, of the stringified - # contents of the appropriate section of all members of the - # archive which possess that section. Heuristic: eliminate - # all those which have a first or second character that is - # a '.' (that is, objdump's representation of an unprintable - # character.) This should work for all archives with less than - # 0x302f exports -- but will fail for DLLs whose name actually - # begins with a literal '.' or a single character followed by - # a '.'. - # - # Of those that remain, print the first one. - $SED -e '/^\./d;/^.\./d;q' -} - -# func_cygming_gnu_implib_p ARG -# This predicate returns with zero status (TRUE) if -# ARG is a GNU/binutils-style import library. Returns -# with nonzero status (FALSE) otherwise. -func_cygming_gnu_implib_p () -{ - $opt_debug - func_to_tool_file "$1" func_convert_file_msys_to_w32 - func_cygming_gnu_implib_tmp=`$NM "$func_to_tool_file_result" | eval "$global_symbol_pipe" | $EGREP ' (_head_[A-Za-z0-9_]+_[ad]l*|[A-Za-z0-9_]+_[ad]l*_iname)$'` - test -n "$func_cygming_gnu_implib_tmp" -} - -# func_cygming_ms_implib_p ARG -# This predicate returns with zero status (TRUE) if -# ARG is an MS-style import library. Returns -# with nonzero status (FALSE) otherwise. -func_cygming_ms_implib_p () -{ - $opt_debug - func_to_tool_file "$1" func_convert_file_msys_to_w32 - func_cygming_ms_implib_tmp=`$NM "$func_to_tool_file_result" | eval "$global_symbol_pipe" | $GREP '_NULL_IMPORT_DESCRIPTOR'` - test -n "$func_cygming_ms_implib_tmp" -} - -# func_cygming_dll_for_implib_fallback ARG -# Platform-specific function to extract the -# name of the DLL associated with the specified -# import library ARG. -# -# This fallback implementation is for use when $DLLTOOL -# does not support the --identify-strict option. -# Invoked by eval'ing the libtool variable -# $sharedlib_from_linklib_cmd -# Result is available in the variable -# $sharedlib_from_linklib_result -func_cygming_dll_for_implib_fallback () -{ - $opt_debug - if func_cygming_gnu_implib_p "$1" ; then - # binutils import library - sharedlib_from_linklib_result=`func_cygming_dll_for_implib_fallback_core '.idata$7' "$1"` - elif func_cygming_ms_implib_p "$1" ; then - # ms-generated import library - sharedlib_from_linklib_result=`func_cygming_dll_for_implib_fallback_core '.idata$6' "$1"` - else - # unknown - sharedlib_from_linklib_result="" - fi -} - - -# func_extract_an_archive dir oldlib -func_extract_an_archive () -{ - $opt_debug - f_ex_an_ar_dir="$1"; shift - f_ex_an_ar_oldlib="$1" - if test "$lock_old_archive_extraction" = yes; then - lockfile=$f_ex_an_ar_oldlib.lock - until $opt_dry_run || ln "$progpath" "$lockfile" 2>/dev/null; do - func_echo "Waiting for $lockfile to be removed" - sleep 2 - done - fi - func_show_eval "(cd \$f_ex_an_ar_dir && $AR x \"\$f_ex_an_ar_oldlib\")" \ - 'stat=$?; rm -f "$lockfile"; exit $stat' - if test "$lock_old_archive_extraction" = yes; then - $opt_dry_run || rm -f "$lockfile" - fi - if ($AR t "$f_ex_an_ar_oldlib" | sort | sort -uc >/dev/null 2>&1); then - : - else - func_fatal_error "object name conflicts in archive: $f_ex_an_ar_dir/$f_ex_an_ar_oldlib" - fi -} - - -# func_extract_archives gentop oldlib ... -func_extract_archives () -{ - $opt_debug - my_gentop="$1"; shift - my_oldlibs=${1+"$@"} - my_oldobjs="" - my_xlib="" - my_xabs="" - my_xdir="" - - for my_xlib in $my_oldlibs; do - # Extract the objects. - case $my_xlib in - [\\/]* | [A-Za-z]:[\\/]*) my_xabs="$my_xlib" ;; - *) my_xabs=`pwd`"/$my_xlib" ;; - esac - func_basename "$my_xlib" - my_xlib="$func_basename_result" - my_xlib_u=$my_xlib - while :; do - case " $extracted_archives " in - *" $my_xlib_u "*) - func_arith $extracted_serial + 1 - extracted_serial=$func_arith_result - my_xlib_u=lt$extracted_serial-$my_xlib ;; - *) break ;; - esac - done - extracted_archives="$extracted_archives $my_xlib_u" - my_xdir="$my_gentop/$my_xlib_u" - - func_mkdir_p "$my_xdir" - - case $host in - *-darwin*) - func_verbose "Extracting $my_xabs" - # Do not bother doing anything if just a dry run - $opt_dry_run || { - darwin_orig_dir=`pwd` - cd $my_xdir || exit $? - darwin_archive=$my_xabs - darwin_curdir=`pwd` - darwin_base_archive=`basename "$darwin_archive"` - darwin_arches=`$LIPO -info "$darwin_archive" 2>/dev/null | $GREP Architectures 2>/dev/null || true` - if test -n "$darwin_arches"; then - darwin_arches=`$ECHO "$darwin_arches" | $SED -e 's/.*are://'` - darwin_arch= - func_verbose "$darwin_base_archive has multiple architectures $darwin_arches" - for darwin_arch in $darwin_arches ; do - func_mkdir_p "unfat-$$/${darwin_base_archive}-${darwin_arch}" - $LIPO -thin $darwin_arch -output "unfat-$$/${darwin_base_archive}-${darwin_arch}/${darwin_base_archive}" "${darwin_archive}" - cd "unfat-$$/${darwin_base_archive}-${darwin_arch}" - func_extract_an_archive "`pwd`" "${darwin_base_archive}" - cd "$darwin_curdir" - $RM "unfat-$$/${darwin_base_archive}-${darwin_arch}/${darwin_base_archive}" - done # $darwin_arches - ## Okay now we've a bunch of thin objects, gotta fatten them up :) - darwin_filelist=`find unfat-$$ -type f -name \*.o -print -o -name \*.lo -print | $SED -e "$basename" | sort -u` - darwin_file= - darwin_files= - for darwin_file in $darwin_filelist; do - darwin_files=`find unfat-$$ -name $darwin_file -print | sort | $NL2SP` - $LIPO -create -output "$darwin_file" $darwin_files - done # $darwin_filelist - $RM -rf unfat-$$ - cd "$darwin_orig_dir" - else - cd $darwin_orig_dir - func_extract_an_archive "$my_xdir" "$my_xabs" - fi # $darwin_arches - } # !$opt_dry_run - ;; - *) - func_extract_an_archive "$my_xdir" "$my_xabs" - ;; - esac - my_oldobjs="$my_oldobjs "`find $my_xdir -name \*.$objext -print -o -name \*.lo -print | sort | $NL2SP` - done - - func_extract_archives_result="$my_oldobjs" -} - - -# func_emit_wrapper [arg=no] -# -# Emit a libtool wrapper script on stdout. -# Don't directly open a file because we may want to -# incorporate the script contents within a cygwin/mingw -# wrapper executable. Must ONLY be called from within -# func_mode_link because it depends on a number of variables -# set therein. -# -# ARG is the value that the WRAPPER_SCRIPT_BELONGS_IN_OBJDIR -# variable will take. If 'yes', then the emitted script -# will assume that the directory in which it is stored is -# the $objdir directory. This is a cygwin/mingw-specific -# behavior. -func_emit_wrapper () -{ - func_emit_wrapper_arg1=${1-no} - - $ECHO "\ -#! $SHELL - -# $output - temporary wrapper script for $objdir/$outputname -# Generated by $PROGRAM (GNU $PACKAGE$TIMESTAMP) $VERSION -# -# The $output program cannot be directly executed until all the libtool -# libraries that it depends on are installed. -# -# This wrapper script should never be moved out of the build directory. -# If it is, it will not operate correctly. - -# Sed substitution that helps us do robust quoting. It backslashifies -# metacharacters that are still active within double-quoted strings. -sed_quote_subst='$sed_quote_subst' - -# Be Bourne compatible -if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then - emulate sh - NULLCMD=: - # Zsh 3.x and 4.x performs word splitting on \${1+\"\$@\"}, which - # is contrary to our usage. Disable this feature. - alias -g '\${1+\"\$@\"}'='\"\$@\"' - setopt NO_GLOB_SUBST -else - case \`(set -o) 2>/dev/null\` in *posix*) set -o posix;; esac -fi -BIN_SH=xpg4; export BIN_SH # for Tru64 -DUALCASE=1; export DUALCASE # for MKS sh - -# The HP-UX ksh and POSIX shell print the target directory to stdout -# if CDPATH is set. -(unset CDPATH) >/dev/null 2>&1 && unset CDPATH - -relink_command=\"$relink_command\" - -# This environment variable determines our operation mode. -if test \"\$libtool_install_magic\" = \"$magic\"; then - # install mode needs the following variables: - generated_by_libtool_version='$macro_version' - notinst_deplibs='$notinst_deplibs' -else - # When we are sourced in execute mode, \$file and \$ECHO are already set. - if test \"\$libtool_execute_magic\" != \"$magic\"; then - file=\"\$0\"" - - qECHO=`$ECHO "$ECHO" | $SED "$sed_quote_subst"` - $ECHO "\ - -# A function that is used when there is no print builtin or printf. -func_fallback_echo () -{ - eval 'cat <<_LTECHO_EOF -\$1 -_LTECHO_EOF' -} - ECHO=\"$qECHO\" - fi - -# Very basic option parsing. These options are (a) specific to -# the libtool wrapper, (b) are identical between the wrapper -# /script/ and the wrapper /executable/ which is used only on -# windows platforms, and (c) all begin with the string "--lt-" -# (application programs are unlikely to have options which match -# this pattern). -# -# There are only two supported options: --lt-debug and -# --lt-dump-script. There is, deliberately, no --lt-help. -# -# The first argument to this parsing function should be the -# script's $0 value, followed by "$@". -lt_option_debug= -func_parse_lt_options () -{ - lt_script_arg0=\$0 - shift - for lt_opt - do - case \"\$lt_opt\" in - --lt-debug) lt_option_debug=1 ;; - --lt-dump-script) - lt_dump_D=\`\$ECHO \"X\$lt_script_arg0\" | $SED -e 's/^X//' -e 's%/[^/]*$%%'\` - test \"X\$lt_dump_D\" = \"X\$lt_script_arg0\" && lt_dump_D=. - lt_dump_F=\`\$ECHO \"X\$lt_script_arg0\" | $SED -e 's/^X//' -e 's%^.*/%%'\` - cat \"\$lt_dump_D/\$lt_dump_F\" - exit 0 - ;; - --lt-*) - \$ECHO \"Unrecognized --lt- option: '\$lt_opt'\" 1>&2 - exit 1 - ;; - esac - done - - # Print the debug banner immediately: - if test -n \"\$lt_option_debug\"; then - echo \"${outputname}:${output}:\${LINENO}: libtool wrapper (GNU $PACKAGE$TIMESTAMP) $VERSION\" 1>&2 - fi -} - -# Used when --lt-debug. Prints its arguments to stdout -# (redirection is the responsibility of the caller) -func_lt_dump_args () -{ - lt_dump_args_N=1; - for lt_arg - do - \$ECHO \"${outputname}:${output}:\${LINENO}: newargv[\$lt_dump_args_N]: \$lt_arg\" - lt_dump_args_N=\`expr \$lt_dump_args_N + 1\` - done -} - -# Core function for launching the target application -func_exec_program_core () -{ -" - case $host in - # Backslashes separate directories on plain windows - *-*-mingw | *-*-os2* | *-cegcc*) - $ECHO "\ - if test -n \"\$lt_option_debug\"; then - \$ECHO \"${outputname}:${output}:\${LINENO}: newargv[0]: \$progdir\\\\\$program\" 1>&2 - func_lt_dump_args \${1+\"\$@\"} 1>&2 - fi - exec \"\$progdir\\\\\$program\" \${1+\"\$@\"} -" - ;; - - *) - $ECHO "\ - if test -n \"\$lt_option_debug\"; then - \$ECHO \"${outputname}:${output}:\${LINENO}: newargv[0]: \$progdir/\$program\" 1>&2 - func_lt_dump_args \${1+\"\$@\"} 1>&2 - fi - exec \"\$progdir/\$program\" \${1+\"\$@\"} -" - ;; - esac - $ECHO "\ - \$ECHO \"\$0: cannot exec \$program \$*\" 1>&2 - exit 1 -} - -# A function to encapsulate launching the target application -# Strips options in the --lt-* namespace from \$@ and -# launches target application with the remaining arguments. -func_exec_program () -{ - case \" \$* \" in - *\\ --lt-*) - for lt_wr_arg - do - case \$lt_wr_arg in - --lt-*) ;; - *) set x \"\$@\" \"\$lt_wr_arg\"; shift;; - esac - shift - done ;; - esac - func_exec_program_core \${1+\"\$@\"} -} - - # Parse options - func_parse_lt_options \"\$0\" \${1+\"\$@\"} - - # Find the directory that this script lives in. - thisdir=\`\$ECHO \"\$file\" | $SED 's%/[^/]*$%%'\` - test \"x\$thisdir\" = \"x\$file\" && thisdir=. - - # Follow symbolic links until we get to the real thisdir. - file=\`ls -ld \"\$file\" | $SED -n 's/.*-> //p'\` - while test -n \"\$file\"; do - destdir=\`\$ECHO \"\$file\" | $SED 's%/[^/]*\$%%'\` - - # If there was a directory component, then change thisdir. - if test \"x\$destdir\" != \"x\$file\"; then - case \"\$destdir\" in - [\\\\/]* | [A-Za-z]:[\\\\/]*) thisdir=\"\$destdir\" ;; - *) thisdir=\"\$thisdir/\$destdir\" ;; - esac - fi - - file=\`\$ECHO \"\$file\" | $SED 's%^.*/%%'\` - file=\`ls -ld \"\$thisdir/\$file\" | $SED -n 's/.*-> //p'\` - done - - # Usually 'no', except on cygwin/mingw when embedded into - # the cwrapper. - WRAPPER_SCRIPT_BELONGS_IN_OBJDIR=$func_emit_wrapper_arg1 - if test \"\$WRAPPER_SCRIPT_BELONGS_IN_OBJDIR\" = \"yes\"; then - # special case for '.' - if test \"\$thisdir\" = \".\"; then - thisdir=\`pwd\` - fi - # remove .libs from thisdir - case \"\$thisdir\" in - *[\\\\/]$objdir ) thisdir=\`\$ECHO \"\$thisdir\" | $SED 's%[\\\\/][^\\\\/]*$%%'\` ;; - $objdir ) thisdir=. ;; - esac - fi - - # Try to get the absolute directory name. - absdir=\`cd \"\$thisdir\" && pwd\` - test -n \"\$absdir\" && thisdir=\"\$absdir\" -" - - if test "$fast_install" = yes; then - $ECHO "\ - program=lt-'$outputname'$exeext - progdir=\"\$thisdir/$objdir\" - - if test ! -f \"\$progdir/\$program\" || - { file=\`ls -1dt \"\$progdir/\$program\" \"\$progdir/../\$program\" 2>/dev/null | ${SED} 1q\`; \\ - test \"X\$file\" != \"X\$progdir/\$program\"; }; then - - file=\"\$\$-\$program\" - - if test ! -d \"\$progdir\"; then - $MKDIR \"\$progdir\" - else - $RM \"\$progdir/\$file\" - fi" - - $ECHO "\ - - # relink executable if necessary - if test -n \"\$relink_command\"; then - if relink_command_output=\`eval \$relink_command 2>&1\`; then : - else - $ECHO \"\$relink_command_output\" >&2 - $RM \"\$progdir/\$file\" - exit 1 - fi - fi - - $MV \"\$progdir/\$file\" \"\$progdir/\$program\" 2>/dev/null || - { $RM \"\$progdir/\$program\"; - $MV \"\$progdir/\$file\" \"\$progdir/\$program\"; } - $RM \"\$progdir/\$file\" - fi" - else - $ECHO "\ - program='$outputname' - progdir=\"\$thisdir/$objdir\" -" - fi - - $ECHO "\ - - if test -f \"\$progdir/\$program\"; then" - - # fixup the dll searchpath if we need to. - # - # Fix the DLL searchpath if we need to. Do this before prepending - # to shlibpath, because on Windows, both are PATH and uninstalled - # libraries must come first. - if test -n "$dllsearchpath"; then - $ECHO "\ - # Add the dll search path components to the executable PATH - PATH=$dllsearchpath:\$PATH -" - fi - - # Export our shlibpath_var if we have one. - if test "$shlibpath_overrides_runpath" = yes && test -n "$shlibpath_var" && test -n "$temp_rpath"; then - $ECHO "\ - # Add our own library path to $shlibpath_var - $shlibpath_var=\"$temp_rpath\$$shlibpath_var\" - - # Some systems cannot cope with colon-terminated $shlibpath_var - # The second colon is a workaround for a bug in BeOS R4 sed - $shlibpath_var=\`\$ECHO \"\$$shlibpath_var\" | $SED 's/::*\$//'\` - - export $shlibpath_var -" - fi - - $ECHO "\ - if test \"\$libtool_execute_magic\" != \"$magic\"; then - # Run the actual program with our arguments. - func_exec_program \${1+\"\$@\"} - fi - else - # The program doesn't exist. - \$ECHO \"\$0: error: \\\`\$progdir/\$program' does not exist\" 1>&2 - \$ECHO \"This script is just a wrapper for \$program.\" 1>&2 - \$ECHO \"See the $PACKAGE documentation for more information.\" 1>&2 - exit 1 - fi -fi\ -" -} - - -# func_emit_cwrapperexe_src -# emit the source code for a wrapper executable on stdout -# Must ONLY be called from within func_mode_link because -# it depends on a number of variable set therein. -func_emit_cwrapperexe_src () -{ - cat < -#include -#ifdef _MSC_VER -# include -# include -# include -#else -# include -# include -# ifdef __CYGWIN__ -# include -# endif -#endif -#include -#include -#include -#include -#include -#include -#include -#include - -/* declarations of non-ANSI functions */ -#if defined(__MINGW32__) -# ifdef __STRICT_ANSI__ -int _putenv (const char *); -# endif -#elif defined(__CYGWIN__) -# ifdef __STRICT_ANSI__ -char *realpath (const char *, char *); -int putenv (char *); -int setenv (const char *, const char *, int); -# endif -/* #elif defined (other platforms) ... */ -#endif - -/* portability defines, excluding path handling macros */ -#if defined(_MSC_VER) -# define setmode _setmode -# define stat _stat -# define chmod _chmod -# define getcwd _getcwd -# define putenv _putenv -# define S_IXUSR _S_IEXEC -# ifndef _INTPTR_T_DEFINED -# define _INTPTR_T_DEFINED -# define intptr_t int -# endif -#elif defined(__MINGW32__) -# define setmode _setmode -# define stat _stat -# define chmod _chmod -# define getcwd _getcwd -# define putenv _putenv -#elif defined(__CYGWIN__) -# define HAVE_SETENV -# define FOPEN_WB "wb" -/* #elif defined (other platforms) ... */ -#endif - -#if defined(PATH_MAX) -# define LT_PATHMAX PATH_MAX -#elif defined(MAXPATHLEN) -# define LT_PATHMAX MAXPATHLEN -#else -# define LT_PATHMAX 1024 -#endif - -#ifndef S_IXOTH -# define S_IXOTH 0 -#endif -#ifndef S_IXGRP -# define S_IXGRP 0 -#endif - -/* path handling portability macros */ -#ifndef DIR_SEPARATOR -# define DIR_SEPARATOR '/' -# define PATH_SEPARATOR ':' -#endif - -#if defined (_WIN32) || defined (__MSDOS__) || defined (__DJGPP__) || \ - defined (__OS2__) -# define HAVE_DOS_BASED_FILE_SYSTEM -# define FOPEN_WB "wb" -# ifndef DIR_SEPARATOR_2 -# define DIR_SEPARATOR_2 '\\' -# endif -# ifndef PATH_SEPARATOR_2 -# define PATH_SEPARATOR_2 ';' -# endif -#endif - -#ifndef DIR_SEPARATOR_2 -# define IS_DIR_SEPARATOR(ch) ((ch) == DIR_SEPARATOR) -#else /* DIR_SEPARATOR_2 */ -# define IS_DIR_SEPARATOR(ch) \ - (((ch) == DIR_SEPARATOR) || ((ch) == DIR_SEPARATOR_2)) -#endif /* DIR_SEPARATOR_2 */ - -#ifndef PATH_SEPARATOR_2 -# define IS_PATH_SEPARATOR(ch) ((ch) == PATH_SEPARATOR) -#else /* PATH_SEPARATOR_2 */ -# define IS_PATH_SEPARATOR(ch) ((ch) == PATH_SEPARATOR_2) -#endif /* PATH_SEPARATOR_2 */ - -#ifndef FOPEN_WB -# define FOPEN_WB "w" -#endif -#ifndef _O_BINARY -# define _O_BINARY 0 -#endif - -#define XMALLOC(type, num) ((type *) xmalloc ((num) * sizeof(type))) -#define XFREE(stale) do { \ - if (stale) { free ((void *) stale); stale = 0; } \ -} while (0) - -#if defined(LT_DEBUGWRAPPER) -static int lt_debug = 1; -#else -static int lt_debug = 0; -#endif - -const char *program_name = "libtool-wrapper"; /* in case xstrdup fails */ - -void *xmalloc (size_t num); -char *xstrdup (const char *string); -const char *base_name (const char *name); -char *find_executable (const char *wrapper); -char *chase_symlinks (const char *pathspec); -int make_executable (const char *path); -int check_executable (const char *path); -char *strendzap (char *str, const char *pat); -void lt_debugprintf (const char *file, int line, const char *fmt, ...); -void lt_fatal (const char *file, int line, const char *message, ...); -static const char *nonnull (const char *s); -static const char *nonempty (const char *s); -void lt_setenv (const char *name, const char *value); -char *lt_extend_str (const char *orig_value, const char *add, int to_end); -void lt_update_exe_path (const char *name, const char *value); -void lt_update_lib_path (const char *name, const char *value); -char **prepare_spawn (char **argv); -void lt_dump_script (FILE *f); -EOF - - cat <= 0) - && (st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))) - return 1; - else - return 0; -} - -int -make_executable (const char *path) -{ - int rval = 0; - struct stat st; - - lt_debugprintf (__FILE__, __LINE__, "(make_executable): %s\n", - nonempty (path)); - if ((!path) || (!*path)) - return 0; - - if (stat (path, &st) >= 0) - { - rval = chmod (path, st.st_mode | S_IXOTH | S_IXGRP | S_IXUSR); - } - return rval; -} - -/* Searches for the full path of the wrapper. Returns - newly allocated full path name if found, NULL otherwise - Does not chase symlinks, even on platforms that support them. -*/ -char * -find_executable (const char *wrapper) -{ - int has_slash = 0; - const char *p; - const char *p_next; - /* static buffer for getcwd */ - char tmp[LT_PATHMAX + 1]; - int tmp_len; - char *concat_name; - - lt_debugprintf (__FILE__, __LINE__, "(find_executable): %s\n", - nonempty (wrapper)); - - if ((wrapper == NULL) || (*wrapper == '\0')) - return NULL; - - /* Absolute path? */ -#if defined (HAVE_DOS_BASED_FILE_SYSTEM) - if (isalpha ((unsigned char) wrapper[0]) && wrapper[1] == ':') - { - concat_name = xstrdup (wrapper); - if (check_executable (concat_name)) - return concat_name; - XFREE (concat_name); - } - else - { -#endif - if (IS_DIR_SEPARATOR (wrapper[0])) - { - concat_name = xstrdup (wrapper); - if (check_executable (concat_name)) - return concat_name; - XFREE (concat_name); - } -#if defined (HAVE_DOS_BASED_FILE_SYSTEM) - } -#endif - - for (p = wrapper; *p; p++) - if (*p == '/') - { - has_slash = 1; - break; - } - if (!has_slash) - { - /* no slashes; search PATH */ - const char *path = getenv ("PATH"); - if (path != NULL) - { - for (p = path; *p; p = p_next) - { - const char *q; - size_t p_len; - for (q = p; *q; q++) - if (IS_PATH_SEPARATOR (*q)) - break; - p_len = q - p; - p_next = (*q == '\0' ? q : q + 1); - if (p_len == 0) - { - /* empty path: current directory */ - if (getcwd (tmp, LT_PATHMAX) == NULL) - lt_fatal (__FILE__, __LINE__, "getcwd failed: %s", - nonnull (strerror (errno))); - tmp_len = strlen (tmp); - concat_name = - XMALLOC (char, tmp_len + 1 + strlen (wrapper) + 1); - memcpy (concat_name, tmp, tmp_len); - concat_name[tmp_len] = '/'; - strcpy (concat_name + tmp_len + 1, wrapper); - } - else - { - concat_name = - XMALLOC (char, p_len + 1 + strlen (wrapper) + 1); - memcpy (concat_name, p, p_len); - concat_name[p_len] = '/'; - strcpy (concat_name + p_len + 1, wrapper); - } - if (check_executable (concat_name)) - return concat_name; - XFREE (concat_name); - } - } - /* not found in PATH; assume curdir */ - } - /* Relative path | not found in path: prepend cwd */ - if (getcwd (tmp, LT_PATHMAX) == NULL) - lt_fatal (__FILE__, __LINE__, "getcwd failed: %s", - nonnull (strerror (errno))); - tmp_len = strlen (tmp); - concat_name = XMALLOC (char, tmp_len + 1 + strlen (wrapper) + 1); - memcpy (concat_name, tmp, tmp_len); - concat_name[tmp_len] = '/'; - strcpy (concat_name + tmp_len + 1, wrapper); - - if (check_executable (concat_name)) - return concat_name; - XFREE (concat_name); - return NULL; -} - -char * -chase_symlinks (const char *pathspec) -{ -#ifndef S_ISLNK - return xstrdup (pathspec); -#else - char buf[LT_PATHMAX]; - struct stat s; - char *tmp_pathspec = xstrdup (pathspec); - char *p; - int has_symlinks = 0; - while (strlen (tmp_pathspec) && !has_symlinks) - { - lt_debugprintf (__FILE__, __LINE__, - "checking path component for symlinks: %s\n", - tmp_pathspec); - if (lstat (tmp_pathspec, &s) == 0) - { - if (S_ISLNK (s.st_mode) != 0) - { - has_symlinks = 1; - break; - } - - /* search backwards for last DIR_SEPARATOR */ - p = tmp_pathspec + strlen (tmp_pathspec) - 1; - while ((p > tmp_pathspec) && (!IS_DIR_SEPARATOR (*p))) - p--; - if ((p == tmp_pathspec) && (!IS_DIR_SEPARATOR (*p))) - { - /* no more DIR_SEPARATORS left */ - break; - } - *p = '\0'; - } - else - { - lt_fatal (__FILE__, __LINE__, - "error accessing file \"%s\": %s", - tmp_pathspec, nonnull (strerror (errno))); - } - } - XFREE (tmp_pathspec); - - if (!has_symlinks) - { - return xstrdup (pathspec); - } - - tmp_pathspec = realpath (pathspec, buf); - if (tmp_pathspec == 0) - { - lt_fatal (__FILE__, __LINE__, - "could not follow symlinks for %s", pathspec); - } - return xstrdup (tmp_pathspec); -#endif -} - -char * -strendzap (char *str, const char *pat) -{ - size_t len, patlen; - - assert (str != NULL); - assert (pat != NULL); - - len = strlen (str); - patlen = strlen (pat); - - if (patlen <= len) - { - str += len - patlen; - if (strcmp (str, pat) == 0) - *str = '\0'; - } - return str; -} - -void -lt_debugprintf (const char *file, int line, const char *fmt, ...) -{ - va_list args; - if (lt_debug) - { - (void) fprintf (stderr, "%s:%s:%d: ", program_name, file, line); - va_start (args, fmt); - (void) vfprintf (stderr, fmt, args); - va_end (args); - } -} - -static void -lt_error_core (int exit_status, const char *file, - int line, const char *mode, - const char *message, va_list ap) -{ - fprintf (stderr, "%s:%s:%d: %s: ", program_name, file, line, mode); - vfprintf (stderr, message, ap); - fprintf (stderr, ".\n"); - - if (exit_status >= 0) - exit (exit_status); -} - -void -lt_fatal (const char *file, int line, const char *message, ...) -{ - va_list ap; - va_start (ap, message); - lt_error_core (EXIT_FAILURE, file, line, "FATAL", message, ap); - va_end (ap); -} - -static const char * -nonnull (const char *s) -{ - return s ? s : "(null)"; -} - -static const char * -nonempty (const char *s) -{ - return (s && !*s) ? "(empty)" : nonnull (s); -} - -void -lt_setenv (const char *name, const char *value) -{ - lt_debugprintf (__FILE__, __LINE__, - "(lt_setenv) setting '%s' to '%s'\n", - nonnull (name), nonnull (value)); - { -#ifdef HAVE_SETENV - /* always make a copy, for consistency with !HAVE_SETENV */ - char *str = xstrdup (value); - setenv (name, str, 1); -#else - int len = strlen (name) + 1 + strlen (value) + 1; - char *str = XMALLOC (char, len); - sprintf (str, "%s=%s", name, value); - if (putenv (str) != EXIT_SUCCESS) - { - XFREE (str); - } -#endif - } -} - -char * -lt_extend_str (const char *orig_value, const char *add, int to_end) -{ - char *new_value; - if (orig_value && *orig_value) - { - int orig_value_len = strlen (orig_value); - int add_len = strlen (add); - new_value = XMALLOC (char, add_len + orig_value_len + 1); - if (to_end) - { - strcpy (new_value, orig_value); - strcpy (new_value + orig_value_len, add); - } - else - { - strcpy (new_value, add); - strcpy (new_value + add_len, orig_value); - } - } - else - { - new_value = xstrdup (add); - } - return new_value; -} - -void -lt_update_exe_path (const char *name, const char *value) -{ - lt_debugprintf (__FILE__, __LINE__, - "(lt_update_exe_path) modifying '%s' by prepending '%s'\n", - nonnull (name), nonnull (value)); - - if (name && *name && value && *value) - { - char *new_value = lt_extend_str (getenv (name), value, 0); - /* some systems can't cope with a ':'-terminated path #' */ - int len = strlen (new_value); - while (((len = strlen (new_value)) > 0) && IS_PATH_SEPARATOR (new_value[len-1])) - { - new_value[len-1] = '\0'; - } - lt_setenv (name, new_value); - XFREE (new_value); - } -} - -void -lt_update_lib_path (const char *name, const char *value) -{ - lt_debugprintf (__FILE__, __LINE__, - "(lt_update_lib_path) modifying '%s' by prepending '%s'\n", - nonnull (name), nonnull (value)); - - if (name && *name && value && *value) - { - char *new_value = lt_extend_str (getenv (name), value, 0); - lt_setenv (name, new_value); - XFREE (new_value); - } -} - -EOF - case $host_os in - mingw*) - cat <<"EOF" - -/* Prepares an argument vector before calling spawn(). - Note that spawn() does not by itself call the command interpreter - (getenv ("COMSPEC") != NULL ? getenv ("COMSPEC") : - ({ OSVERSIONINFO v; v.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); - GetVersionEx(&v); - v.dwPlatformId == VER_PLATFORM_WIN32_NT; - }) ? "cmd.exe" : "command.com"). - Instead it simply concatenates the arguments, separated by ' ', and calls - CreateProcess(). We must quote the arguments since Win32 CreateProcess() - interprets characters like ' ', '\t', '\\', '"' (but not '<' and '>') in a - special way: - - Space and tab are interpreted as delimiters. They are not treated as - delimiters if they are surrounded by double quotes: "...". - - Unescaped double quotes are removed from the input. Their only effect is - that within double quotes, space and tab are treated like normal - characters. - - Backslashes not followed by double quotes are not special. - - But 2*n+1 backslashes followed by a double quote become - n backslashes followed by a double quote (n >= 0): - \" -> " - \\\" -> \" - \\\\\" -> \\" - */ -#define SHELL_SPECIAL_CHARS "\"\\ \001\002\003\004\005\006\007\010\011\012\013\014\015\016\017\020\021\022\023\024\025\026\027\030\031\032\033\034\035\036\037" -#define SHELL_SPACE_CHARS " \001\002\003\004\005\006\007\010\011\012\013\014\015\016\017\020\021\022\023\024\025\026\027\030\031\032\033\034\035\036\037" -char ** -prepare_spawn (char **argv) -{ - size_t argc; - char **new_argv; - size_t i; - - /* Count number of arguments. */ - for (argc = 0; argv[argc] != NULL; argc++) - ; - - /* Allocate new argument vector. */ - new_argv = XMALLOC (char *, argc + 1); - - /* Put quoted arguments into the new argument vector. */ - for (i = 0; i < argc; i++) - { - const char *string = argv[i]; - - if (string[0] == '\0') - new_argv[i] = xstrdup ("\"\""); - else if (strpbrk (string, SHELL_SPECIAL_CHARS) != NULL) - { - int quote_around = (strpbrk (string, SHELL_SPACE_CHARS) != NULL); - size_t length; - unsigned int backslashes; - const char *s; - char *quoted_string; - char *p; - - length = 0; - backslashes = 0; - if (quote_around) - length++; - for (s = string; *s != '\0'; s++) - { - char c = *s; - if (c == '"') - length += backslashes + 1; - length++; - if (c == '\\') - backslashes++; - else - backslashes = 0; - } - if (quote_around) - length += backslashes + 1; - - quoted_string = XMALLOC (char, length + 1); - - p = quoted_string; - backslashes = 0; - if (quote_around) - *p++ = '"'; - for (s = string; *s != '\0'; s++) - { - char c = *s; - if (c == '"') - { - unsigned int j; - for (j = backslashes + 1; j > 0; j--) - *p++ = '\\'; - } - *p++ = c; - if (c == '\\') - backslashes++; - else - backslashes = 0; - } - if (quote_around) - { - unsigned int j; - for (j = backslashes; j > 0; j--) - *p++ = '\\'; - *p++ = '"'; - } - *p = '\0'; - - new_argv[i] = quoted_string; - } - else - new_argv[i] = (char *) string; - } - new_argv[argc] = NULL; - - return new_argv; -} -EOF - ;; - esac - - cat <<"EOF" -void lt_dump_script (FILE* f) -{ -EOF - func_emit_wrapper yes | - $SED -n -e ' -s/^\(.\{79\}\)\(..*\)/\1\ -\2/ -h -s/\([\\"]\)/\\\1/g -s/$/\\n/ -s/\([^\n]*\).*/ fputs ("\1", f);/p -g -D' - cat <<"EOF" -} -EOF -} -# end: func_emit_cwrapperexe_src - -# func_win32_import_lib_p ARG -# True if ARG is an import lib, as indicated by $file_magic_cmd -func_win32_import_lib_p () -{ - $opt_debug - case `eval $file_magic_cmd \"\$1\" 2>/dev/null | $SED -e 10q` in - *import*) : ;; - *) false ;; - esac -} - -# func_mode_link arg... -func_mode_link () -{ - $opt_debug - case $host in - *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-cegcc*) - # It is impossible to link a dll without this setting, and - # we shouldn't force the makefile maintainer to figure out - # which system we are compiling for in order to pass an extra - # flag for every libtool invocation. - # allow_undefined=no - - # FIXME: Unfortunately, there are problems with the above when trying - # to make a dll which has undefined symbols, in which case not - # even a static library is built. For now, we need to specify - # -no-undefined on the libtool link line when we can be certain - # that all symbols are satisfied, otherwise we get a static library. - allow_undefined=yes - ;; - *) - allow_undefined=yes - ;; - esac - libtool_args=$nonopt - base_compile="$nonopt $@" - compile_command=$nonopt - finalize_command=$nonopt - - compile_rpath= - finalize_rpath= - compile_shlibpath= - finalize_shlibpath= - convenience= - old_convenience= - deplibs= - old_deplibs= - compiler_flags= - linker_flags= - dllsearchpath= - lib_search_path=`pwd` - inst_prefix_dir= - new_inherited_linker_flags= - - avoid_version=no - bindir= - dlfiles= - dlprefiles= - dlself=no - export_dynamic=no - export_symbols= - export_symbols_regex= - generated= - libobjs= - ltlibs= - module=no - no_install=no - objs= - non_pic_objects= - precious_files_regex= - prefer_static_libs=no - preload=no - prev= - prevarg= - release= - rpath= - xrpath= - perm_rpath= - temp_rpath= - thread_safe=no - vinfo= - vinfo_number=no - weak_libs= - single_module="${wl}-single_module" - func_infer_tag $base_compile - - # We need to know -static, to get the right output filenames. - for arg - do - case $arg in - -shared) - test "$build_libtool_libs" != yes && \ - func_fatal_configuration "can not build a shared library" - build_old_libs=no - break - ;; - -all-static | -static | -static-libtool-libs) - case $arg in - -all-static) - if test "$build_libtool_libs" = yes && test -z "$link_static_flag"; then - func_warning "complete static linking is impossible in this configuration" - fi - if test -n "$link_static_flag"; then - dlopen_self=$dlopen_self_static - fi - prefer_static_libs=yes - ;; - -static) - if test -z "$pic_flag" && test -n "$link_static_flag"; then - dlopen_self=$dlopen_self_static - fi - prefer_static_libs=built - ;; - -static-libtool-libs) - if test -z "$pic_flag" && test -n "$link_static_flag"; then - dlopen_self=$dlopen_self_static - fi - prefer_static_libs=yes - ;; - esac - build_libtool_libs=no - build_old_libs=yes - break - ;; - esac - done - - # See if our shared archives depend on static archives. - test -n "$old_archive_from_new_cmds" && build_old_libs=yes - - # Go through the arguments, transforming them on the way. - while test "$#" -gt 0; do - arg="$1" - shift - func_quote_for_eval "$arg" - qarg=$func_quote_for_eval_unquoted_result - func_append libtool_args " $func_quote_for_eval_result" - - # If the previous option needs an argument, assign it. - if test -n "$prev"; then - case $prev in - output) - func_append compile_command " @OUTPUT@" - func_append finalize_command " @OUTPUT@" - ;; - esac - - case $prev in - bindir) - bindir="$arg" - prev= - continue - ;; - dlfiles|dlprefiles) - if test "$preload" = no; then - # Add the symbol object into the linking commands. - func_append compile_command " @SYMFILE@" - func_append finalize_command " @SYMFILE@" - preload=yes - fi - case $arg in - *.la | *.lo) ;; # We handle these cases below. - force) - if test "$dlself" = no; then - dlself=needless - export_dynamic=yes - fi - prev= - continue - ;; - self) - if test "$prev" = dlprefiles; then - dlself=yes - elif test "$prev" = dlfiles && test "$dlopen_self" != yes; then - dlself=yes - else - dlself=needless - export_dynamic=yes - fi - prev= - continue - ;; - *) - if test "$prev" = dlfiles; then - func_append dlfiles " $arg" - else - func_append dlprefiles " $arg" - fi - prev= - continue - ;; - esac - ;; - expsyms) - export_symbols="$arg" - test -f "$arg" \ - || func_fatal_error "symbol file \`$arg' does not exist" - prev= - continue - ;; - expsyms_regex) - export_symbols_regex="$arg" - prev= - continue - ;; - framework) - case $host in - *-*-darwin*) - case "$deplibs " in - *" $qarg.ltframework "*) ;; - *) func_append deplibs " $qarg.ltframework" # this is fixed later - ;; - esac - ;; - esac - prev= - continue - ;; - inst_prefix) - inst_prefix_dir="$arg" - prev= - continue - ;; - objectlist) - if test -f "$arg"; then - save_arg=$arg - moreargs= - for fil in `cat "$save_arg"` - do -# func_append moreargs " $fil" - arg=$fil - # A libtool-controlled object. - - # Check to see that this really is a libtool object. - if func_lalib_unsafe_p "$arg"; then - pic_object= - non_pic_object= - - # Read the .lo file - func_source "$arg" - - if test -z "$pic_object" || - test -z "$non_pic_object" || - test "$pic_object" = none && - test "$non_pic_object" = none; then - func_fatal_error "cannot find name of object for \`$arg'" - fi - - # Extract subdirectory from the argument. - func_dirname "$arg" "/" "" - xdir="$func_dirname_result" - - if test "$pic_object" != none; then - # Prepend the subdirectory the object is found in. - pic_object="$xdir$pic_object" - - if test "$prev" = dlfiles; then - if test "$build_libtool_libs" = yes && test "$dlopen_support" = yes; then - func_append dlfiles " $pic_object" - prev= - continue - else - # If libtool objects are unsupported, then we need to preload. - prev=dlprefiles - fi - fi - - # CHECK ME: I think I busted this. -Ossama - if test "$prev" = dlprefiles; then - # Preload the old-style object. - func_append dlprefiles " $pic_object" - prev= - fi - - # A PIC object. - func_append libobjs " $pic_object" - arg="$pic_object" - fi - - # Non-PIC object. - if test "$non_pic_object" != none; then - # Prepend the subdirectory the object is found in. - non_pic_object="$xdir$non_pic_object" - - # A standard non-PIC object - func_append non_pic_objects " $non_pic_object" - if test -z "$pic_object" || test "$pic_object" = none ; then - arg="$non_pic_object" - fi - else - # If the PIC object exists, use it instead. - # $xdir was prepended to $pic_object above. - non_pic_object="$pic_object" - func_append non_pic_objects " $non_pic_object" - fi - else - # Only an error if not doing a dry-run. - if $opt_dry_run; then - # Extract subdirectory from the argument. - func_dirname "$arg" "/" "" - xdir="$func_dirname_result" - - func_lo2o "$arg" - pic_object=$xdir$objdir/$func_lo2o_result - non_pic_object=$xdir$func_lo2o_result - func_append libobjs " $pic_object" - func_append non_pic_objects " $non_pic_object" - else - func_fatal_error "\`$arg' is not a valid libtool object" - fi - fi - done - else - func_fatal_error "link input file \`$arg' does not exist" - fi - arg=$save_arg - prev= - continue - ;; - precious_regex) - precious_files_regex="$arg" - prev= - continue - ;; - release) - release="-$arg" - prev= - continue - ;; - rpath | xrpath) - # We need an absolute path. - case $arg in - [\\/]* | [A-Za-z]:[\\/]*) ;; - *) - func_fatal_error "only absolute run-paths are allowed" - ;; - esac - if test "$prev" = rpath; then - case "$rpath " in - *" $arg "*) ;; - *) func_append rpath " $arg" ;; - esac - else - case "$xrpath " in - *" $arg "*) ;; - *) func_append xrpath " $arg" ;; - esac - fi - prev= - continue - ;; - shrext) - shrext_cmds="$arg" - prev= - continue - ;; - weak) - func_append weak_libs " $arg" - prev= - continue - ;; - xcclinker) - func_append linker_flags " $qarg" - func_append compiler_flags " $qarg" - prev= - func_append compile_command " $qarg" - func_append finalize_command " $qarg" - continue - ;; - xcompiler) - func_append compiler_flags " $qarg" - prev= - func_append compile_command " $qarg" - func_append finalize_command " $qarg" - continue - ;; - xlinker) - func_append linker_flags " $qarg" - func_append compiler_flags " $wl$qarg" - prev= - func_append compile_command " $wl$qarg" - func_append finalize_command " $wl$qarg" - continue - ;; - *) - eval "$prev=\"\$arg\"" - prev= - continue - ;; - esac - fi # test -n "$prev" - - prevarg="$arg" - - case $arg in - -all-static) - if test -n "$link_static_flag"; then - # See comment for -static flag below, for more details. - func_append compile_command " $link_static_flag" - func_append finalize_command " $link_static_flag" - fi - continue - ;; - - -allow-undefined) - # FIXME: remove this flag sometime in the future. - func_fatal_error "\`-allow-undefined' must not be used because it is the default" - ;; - - -avoid-version) - avoid_version=yes - continue - ;; - - -bindir) - prev=bindir - continue - ;; - - -dlopen) - prev=dlfiles - continue - ;; - - -dlpreopen) - prev=dlprefiles - continue - ;; - - -export-dynamic) - export_dynamic=yes - continue - ;; - - -export-symbols | -export-symbols-regex) - if test -n "$export_symbols" || test -n "$export_symbols_regex"; then - func_fatal_error "more than one -exported-symbols argument is not allowed" - fi - if test "X$arg" = "X-export-symbols"; then - prev=expsyms - else - prev=expsyms_regex - fi - continue - ;; - - -framework) - prev=framework - continue - ;; - - -inst-prefix-dir) - prev=inst_prefix - continue - ;; - - # The native IRIX linker understands -LANG:*, -LIST:* and -LNO:* - # so, if we see these flags be careful not to treat them like -L - -L[A-Z][A-Z]*:*) - case $with_gcc/$host in - no/*-*-irix* | /*-*-irix*) - func_append compile_command " $arg" - func_append finalize_command " $arg" - ;; - esac - continue - ;; - - -L*) - func_stripname "-L" '' "$arg" - if test -z "$func_stripname_result"; then - if test "$#" -gt 0; then - func_fatal_error "require no space between \`-L' and \`$1'" - else - func_fatal_error "need path for \`-L' option" - fi - fi - func_resolve_sysroot "$func_stripname_result" - dir=$func_resolve_sysroot_result - # We need an absolute path. - case $dir in - [\\/]* | [A-Za-z]:[\\/]*) ;; - *) - absdir=`cd "$dir" && pwd` - test -z "$absdir" && \ - func_fatal_error "cannot determine absolute directory name of \`$dir'" - dir="$absdir" - ;; - esac - case "$deplibs " in - *" -L$dir "* | *" $arg "*) - # Will only happen for absolute or sysroot arguments - ;; - *) - # Preserve sysroot, but never include relative directories - case $dir in - [\\/]* | [A-Za-z]:[\\/]* | =*) func_append deplibs " $arg" ;; - *) func_append deplibs " -L$dir" ;; - esac - func_append lib_search_path " $dir" - ;; - esac - case $host in - *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-cegcc*) - testbindir=`$ECHO "$dir" | $SED 's*/lib$*/bin*'` - case :$dllsearchpath: in - *":$dir:"*) ;; - ::) dllsearchpath=$dir;; - *) func_append dllsearchpath ":$dir";; - esac - case :$dllsearchpath: in - *":$testbindir:"*) ;; - ::) dllsearchpath=$testbindir;; - *) func_append dllsearchpath ":$testbindir";; - esac - ;; - esac - continue - ;; - - -l*) - if test "X$arg" = "X-lc" || test "X$arg" = "X-lm"; then - case $host in - *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-beos* | *-cegcc* | *-*-haiku*) - # These systems don't actually have a C or math library (as such) - continue - ;; - *-*-os2*) - # These systems don't actually have a C library (as such) - test "X$arg" = "X-lc" && continue - ;; - *-*-openbsd* | *-*-freebsd* | *-*-dragonfly*) - # Do not include libc due to us having libc/libc_r. - test "X$arg" = "X-lc" && continue - ;; - *-*-rhapsody* | *-*-darwin1.[012]) - # Rhapsody C and math libraries are in the System framework - func_append deplibs " System.ltframework" - continue - ;; - *-*-sco3.2v5* | *-*-sco5v6*) - # Causes problems with __ctype - test "X$arg" = "X-lc" && continue - ;; - *-*-sysv4.2uw2* | *-*-sysv5* | *-*-unixware* | *-*-OpenUNIX*) - # Compiler inserts libc in the correct place for threads to work - test "X$arg" = "X-lc" && continue - ;; - esac - elif test "X$arg" = "X-lc_r"; then - case $host in - *-*-openbsd* | *-*-freebsd* | *-*-dragonfly*) - # Do not include libc_r directly, use -pthread flag. - continue - ;; - esac - fi - func_append deplibs " $arg" - continue - ;; - - -module) - module=yes - continue - ;; - - # Tru64 UNIX uses -model [arg] to determine the layout of C++ - # classes, name mangling, and exception handling. - # Darwin uses the -arch flag to determine output architecture. - -model|-arch|-isysroot|--sysroot) - func_append compiler_flags " $arg" - func_append compile_command " $arg" - func_append finalize_command " $arg" - prev=xcompiler - continue - ;; - - -mt|-mthreads|-kthread|-Kthread|-pthread|-pthreads|--thread-safe \ - |-threads|-fopenmp|-openmp|-mp|-xopenmp|-omp|-qsmp=*) - func_append compiler_flags " $arg" - func_append compile_command " $arg" - func_append finalize_command " $arg" - case "$new_inherited_linker_flags " in - *" $arg "*) ;; - * ) func_append new_inherited_linker_flags " $arg" ;; - esac - continue - ;; - - -multi_module) - single_module="${wl}-multi_module" - continue - ;; - - -no-fast-install) - fast_install=no - continue - ;; - - -no-install) - case $host in - *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-*-darwin* | *-cegcc*) - # The PATH hackery in wrapper scripts is required on Windows - # and Darwin in order for the loader to find any dlls it needs. - func_warning "\`-no-install' is ignored for $host" - func_warning "assuming \`-no-fast-install' instead" - fast_install=no - ;; - *) no_install=yes ;; - esac - continue - ;; - - -no-undefined) - allow_undefined=no - continue - ;; - - -objectlist) - prev=objectlist - continue - ;; - - -o) prev=output ;; - - -precious-files-regex) - prev=precious_regex - continue - ;; - - -release) - prev=release - continue - ;; - - -rpath) - prev=rpath - continue - ;; - - -R) - prev=xrpath - continue - ;; - - -R*) - func_stripname '-R' '' "$arg" - dir=$func_stripname_result - # We need an absolute path. - case $dir in - [\\/]* | [A-Za-z]:[\\/]*) ;; - =*) - func_stripname '=' '' "$dir" - dir=$lt_sysroot$func_stripname_result - ;; - *) - func_fatal_error "only absolute run-paths are allowed" - ;; - esac - case "$xrpath " in - *" $dir "*) ;; - *) func_append xrpath " $dir" ;; - esac - continue - ;; - - -shared) - # The effects of -shared are defined in a previous loop. - continue - ;; - - -shrext) - prev=shrext - continue - ;; - - -static | -static-libtool-libs) - # The effects of -static are defined in a previous loop. - # We used to do the same as -all-static on platforms that - # didn't have a PIC flag, but the assumption that the effects - # would be equivalent was wrong. It would break on at least - # Digital Unix and AIX. - continue - ;; - - -thread-safe) - thread_safe=yes - continue - ;; - - -version-info) - prev=vinfo - continue - ;; - - -version-number) - prev=vinfo - vinfo_number=yes - continue - ;; - - -weak) - prev=weak - continue - ;; - - -Wc,*) - func_stripname '-Wc,' '' "$arg" - args=$func_stripname_result - arg= - save_ifs="$IFS"; IFS=',' - for flag in $args; do - IFS="$save_ifs" - func_quote_for_eval "$flag" - func_append arg " $func_quote_for_eval_result" - func_append compiler_flags " $func_quote_for_eval_result" - done - IFS="$save_ifs" - func_stripname ' ' '' "$arg" - arg=$func_stripname_result - ;; - - -Wl,*) - func_stripname '-Wl,' '' "$arg" - args=$func_stripname_result - arg= - save_ifs="$IFS"; IFS=',' - for flag in $args; do - IFS="$save_ifs" - func_quote_for_eval "$flag" - func_append arg " $wl$func_quote_for_eval_result" - func_append compiler_flags " $wl$func_quote_for_eval_result" - func_append linker_flags " $func_quote_for_eval_result" - done - IFS="$save_ifs" - func_stripname ' ' '' "$arg" - arg=$func_stripname_result - ;; - - -Xcompiler) - prev=xcompiler - continue - ;; - - -Xlinker) - prev=xlinker - continue - ;; - - -XCClinker) - prev=xcclinker - continue - ;; - - # -msg_* for osf cc - -msg_*) - func_quote_for_eval "$arg" - arg="$func_quote_for_eval_result" - ;; - - # Flags to be passed through unchanged, with rationale: - # -64, -mips[0-9] enable 64-bit mode for the SGI compiler - # -r[0-9][0-9]* specify processor for the SGI compiler - # -xarch=*, -xtarget=* enable 64-bit mode for the Sun compiler - # +DA*, +DD* enable 64-bit mode for the HP compiler - # -q* compiler args for the IBM compiler - # -m*, -t[45]*, -txscale* architecture-specific flags for GCC - # -F/path path to uninstalled frameworks, gcc on darwin - # -p, -pg, --coverage, -fprofile-* profiling flags for GCC - # @file GCC response files - # -tp=* Portland pgcc target processor selection - # --sysroot=* for sysroot support - # -O*, -flto*, -fwhopr*, -fuse-linker-plugin GCC link-time optimization - -64|-mips[0-9]|-r[0-9][0-9]*|-xarch=*|-xtarget=*|+DA*|+DD*|-q*|-m*| \ - -t[45]*|-txscale*|-p|-pg|--coverage|-fprofile-*|-F*|@*|-tp=*|--sysroot=*| \ - -O*|-flto*|-fwhopr*|-fuse-linker-plugin) - func_quote_for_eval "$arg" - arg="$func_quote_for_eval_result" - func_append compile_command " $arg" - func_append finalize_command " $arg" - func_append compiler_flags " $arg" - continue - ;; - - # Some other compiler flag. - -* | +*) - func_quote_for_eval "$arg" - arg="$func_quote_for_eval_result" - ;; - - *.$objext) - # A standard object. - func_append objs " $arg" - ;; - - *.lo) - # A libtool-controlled object. - - # Check to see that this really is a libtool object. - if func_lalib_unsafe_p "$arg"; then - pic_object= - non_pic_object= - - # Read the .lo file - func_source "$arg" - - if test -z "$pic_object" || - test -z "$non_pic_object" || - test "$pic_object" = none && - test "$non_pic_object" = none; then - func_fatal_error "cannot find name of object for \`$arg'" - fi - - # Extract subdirectory from the argument. - func_dirname "$arg" "/" "" - xdir="$func_dirname_result" - - if test "$pic_object" != none; then - # Prepend the subdirectory the object is found in. - pic_object="$xdir$pic_object" - - if test "$prev" = dlfiles; then - if test "$build_libtool_libs" = yes && test "$dlopen_support" = yes; then - func_append dlfiles " $pic_object" - prev= - continue - else - # If libtool objects are unsupported, then we need to preload. - prev=dlprefiles - fi - fi - - # CHECK ME: I think I busted this. -Ossama - if test "$prev" = dlprefiles; then - # Preload the old-style object. - func_append dlprefiles " $pic_object" - prev= - fi - - # A PIC object. - func_append libobjs " $pic_object" - arg="$pic_object" - fi - - # Non-PIC object. - if test "$non_pic_object" != none; then - # Prepend the subdirectory the object is found in. - non_pic_object="$xdir$non_pic_object" - - # A standard non-PIC object - func_append non_pic_objects " $non_pic_object" - if test -z "$pic_object" || test "$pic_object" = none ; then - arg="$non_pic_object" - fi - else - # If the PIC object exists, use it instead. - # $xdir was prepended to $pic_object above. - non_pic_object="$pic_object" - func_append non_pic_objects " $non_pic_object" - fi - else - # Only an error if not doing a dry-run. - if $opt_dry_run; then - # Extract subdirectory from the argument. - func_dirname "$arg" "/" "" - xdir="$func_dirname_result" - - func_lo2o "$arg" - pic_object=$xdir$objdir/$func_lo2o_result - non_pic_object=$xdir$func_lo2o_result - func_append libobjs " $pic_object" - func_append non_pic_objects " $non_pic_object" - else - func_fatal_error "\`$arg' is not a valid libtool object" - fi - fi - ;; - - *.$libext) - # An archive. - func_append deplibs " $arg" - func_append old_deplibs " $arg" - continue - ;; - - *.la) - # A libtool-controlled library. - - func_resolve_sysroot "$arg" - if test "$prev" = dlfiles; then - # This library was specified with -dlopen. - func_append dlfiles " $func_resolve_sysroot_result" - prev= - elif test "$prev" = dlprefiles; then - # The library was specified with -dlpreopen. - func_append dlprefiles " $func_resolve_sysroot_result" - prev= - else - func_append deplibs " $func_resolve_sysroot_result" - fi - continue - ;; - - # Some other compiler argument. - *) - # Unknown arguments in both finalize_command and compile_command need - # to be aesthetically quoted because they are evaled later. - func_quote_for_eval "$arg" - arg="$func_quote_for_eval_result" - ;; - esac # arg - - # Now actually substitute the argument into the commands. - if test -n "$arg"; then - func_append compile_command " $arg" - func_append finalize_command " $arg" - fi - done # argument parsing loop - - test -n "$prev" && \ - func_fatal_help "the \`$prevarg' option requires an argument" - - if test "$export_dynamic" = yes && test -n "$export_dynamic_flag_spec"; then - eval arg=\"$export_dynamic_flag_spec\" - func_append compile_command " $arg" - func_append finalize_command " $arg" - fi - - oldlibs= - # calculate the name of the file, without its directory - func_basename "$output" - outputname="$func_basename_result" - libobjs_save="$libobjs" - - if test -n "$shlibpath_var"; then - # get the directories listed in $shlibpath_var - eval shlib_search_path=\`\$ECHO \"\${$shlibpath_var}\" \| \$SED \'s/:/ /g\'\` - else - shlib_search_path= - fi - eval sys_lib_search_path=\"$sys_lib_search_path_spec\" - eval sys_lib_dlsearch_path=\"$sys_lib_dlsearch_path_spec\" - - func_dirname "$output" "/" "" - output_objdir="$func_dirname_result$objdir" - func_to_tool_file "$output_objdir/" - tool_output_objdir=$func_to_tool_file_result - # Create the object directory. - func_mkdir_p "$output_objdir" - - # Determine the type of output - case $output in - "") - func_fatal_help "you must specify an output file" - ;; - *.$libext) linkmode=oldlib ;; - *.lo | *.$objext) linkmode=obj ;; - *.la) linkmode=lib ;; - *) linkmode=prog ;; # Anything else should be a program. - esac - - specialdeplibs= - - libs= - # Find all interdependent deplibs by searching for libraries - # that are linked more than once (e.g. -la -lb -la) - for deplib in $deplibs; do - if $opt_preserve_dup_deps ; then - case "$libs " in - *" $deplib "*) func_append specialdeplibs " $deplib" ;; - esac - fi - func_append libs " $deplib" - done - - if test "$linkmode" = lib; then - libs="$predeps $libs $compiler_lib_search_path $postdeps" - - # Compute libraries that are listed more than once in $predeps - # $postdeps and mark them as special (i.e., whose duplicates are - # not to be eliminated). - pre_post_deps= - if $opt_duplicate_compiler_generated_deps; then - for pre_post_dep in $predeps $postdeps; do - case "$pre_post_deps " in - *" $pre_post_dep "*) func_append specialdeplibs " $pre_post_deps" ;; - esac - func_append pre_post_deps " $pre_post_dep" - done - fi - pre_post_deps= - fi - - deplibs= - newdependency_libs= - newlib_search_path= - need_relink=no # whether we're linking any uninstalled libtool libraries - notinst_deplibs= # not-installed libtool libraries - notinst_path= # paths that contain not-installed libtool libraries - - case $linkmode in - lib) - passes="conv dlpreopen link" - for file in $dlfiles $dlprefiles; do - case $file in - *.la) ;; - *) - func_fatal_help "libraries can \`-dlopen' only libtool libraries: $file" - ;; - esac - done - ;; - prog) - compile_deplibs= - finalize_deplibs= - alldeplibs=no - newdlfiles= - newdlprefiles= - passes="conv scan dlopen dlpreopen link" - ;; - *) passes="conv" - ;; - esac - - for pass in $passes; do - # The preopen pass in lib mode reverses $deplibs; put it back here - # so that -L comes before libs that need it for instance... - if test "$linkmode,$pass" = "lib,link"; then - ## FIXME: Find the place where the list is rebuilt in the wrong - ## order, and fix it there properly - tmp_deplibs= - for deplib in $deplibs; do - tmp_deplibs="$deplib $tmp_deplibs" - done - deplibs="$tmp_deplibs" - fi - - if test "$linkmode,$pass" = "lib,link" || - test "$linkmode,$pass" = "prog,scan"; then - libs="$deplibs" - deplibs= - fi - if test "$linkmode" = prog; then - case $pass in - dlopen) libs="$dlfiles" ;; - dlpreopen) libs="$dlprefiles" ;; - link) - libs="$deplibs %DEPLIBS%" - test "X$link_all_deplibs" != Xno && libs="$libs $dependency_libs" - ;; - esac - fi - if test "$linkmode,$pass" = "lib,dlpreopen"; then - # Collect and forward deplibs of preopened libtool libs - for lib in $dlprefiles; do - # Ignore non-libtool-libs - dependency_libs= - func_resolve_sysroot "$lib" - case $lib in - *.la) func_source "$func_resolve_sysroot_result" ;; - esac - - # Collect preopened libtool deplibs, except any this library - # has declared as weak libs - for deplib in $dependency_libs; do - func_basename "$deplib" - deplib_base=$func_basename_result - case " $weak_libs " in - *" $deplib_base "*) ;; - *) func_append deplibs " $deplib" ;; - esac - done - done - libs="$dlprefiles" - fi - if test "$pass" = dlopen; then - # Collect dlpreopened libraries - save_deplibs="$deplibs" - deplibs= - fi - - for deplib in $libs; do - lib= - found=no - case $deplib in - -mt|-mthreads|-kthread|-Kthread|-pthread|-pthreads|--thread-safe \ - |-threads|-fopenmp|-openmp|-mp|-xopenmp|-omp|-qsmp=*) - if test "$linkmode,$pass" = "prog,link"; then - compile_deplibs="$deplib $compile_deplibs" - finalize_deplibs="$deplib $finalize_deplibs" - else - func_append compiler_flags " $deplib" - if test "$linkmode" = lib ; then - case "$new_inherited_linker_flags " in - *" $deplib "*) ;; - * ) func_append new_inherited_linker_flags " $deplib" ;; - esac - fi - fi - continue - ;; - -l*) - if test "$linkmode" != lib && test "$linkmode" != prog; then - func_warning "\`-l' is ignored for archives/objects" - continue - fi - func_stripname '-l' '' "$deplib" - name=$func_stripname_result - if test "$linkmode" = lib; then - searchdirs="$newlib_search_path $lib_search_path $compiler_lib_search_dirs $sys_lib_search_path $shlib_search_path" - else - searchdirs="$newlib_search_path $lib_search_path $sys_lib_search_path $shlib_search_path" - fi - for searchdir in $searchdirs; do - for search_ext in .la $std_shrext .so .a; do - # Search the libtool library - lib="$searchdir/lib${name}${search_ext}" - if test -f "$lib"; then - if test "$search_ext" = ".la"; then - found=yes - else - found=no - fi - break 2 - fi - done - done - if test "$found" != yes; then - # deplib doesn't seem to be a libtool library - if test "$linkmode,$pass" = "prog,link"; then - compile_deplibs="$deplib $compile_deplibs" - finalize_deplibs="$deplib $finalize_deplibs" - else - deplibs="$deplib $deplibs" - test "$linkmode" = lib && newdependency_libs="$deplib $newdependency_libs" - fi - continue - else # deplib is a libtool library - # If $allow_libtool_libs_with_static_runtimes && $deplib is a stdlib, - # We need to do some special things here, and not later. - if test "X$allow_libtool_libs_with_static_runtimes" = "Xyes" ; then - case " $predeps $postdeps " in - *" $deplib "*) - if func_lalib_p "$lib"; then - library_names= - old_library= - func_source "$lib" - for l in $old_library $library_names; do - ll="$l" - done - if test "X$ll" = "X$old_library" ; then # only static version available - found=no - func_dirname "$lib" "" "." - ladir="$func_dirname_result" - lib=$ladir/$old_library - if test "$linkmode,$pass" = "prog,link"; then - compile_deplibs="$deplib $compile_deplibs" - finalize_deplibs="$deplib $finalize_deplibs" - else - deplibs="$deplib $deplibs" - test "$linkmode" = lib && newdependency_libs="$deplib $newdependency_libs" - fi - continue - fi - fi - ;; - *) ;; - esac - fi - fi - ;; # -l - *.ltframework) - if test "$linkmode,$pass" = "prog,link"; then - compile_deplibs="$deplib $compile_deplibs" - finalize_deplibs="$deplib $finalize_deplibs" - else - deplibs="$deplib $deplibs" - if test "$linkmode" = lib ; then - case "$new_inherited_linker_flags " in - *" $deplib "*) ;; - * ) func_append new_inherited_linker_flags " $deplib" ;; - esac - fi - fi - continue - ;; - -L*) - case $linkmode in - lib) - deplibs="$deplib $deplibs" - test "$pass" = conv && continue - newdependency_libs="$deplib $newdependency_libs" - func_stripname '-L' '' "$deplib" - func_resolve_sysroot "$func_stripname_result" - func_append newlib_search_path " $func_resolve_sysroot_result" - ;; - prog) - if test "$pass" = conv; then - deplibs="$deplib $deplibs" - continue - fi - if test "$pass" = scan; then - deplibs="$deplib $deplibs" - else - compile_deplibs="$deplib $compile_deplibs" - finalize_deplibs="$deplib $finalize_deplibs" - fi - func_stripname '-L' '' "$deplib" - func_resolve_sysroot "$func_stripname_result" - func_append newlib_search_path " $func_resolve_sysroot_result" - ;; - *) - func_warning "\`-L' is ignored for archives/objects" - ;; - esac # linkmode - continue - ;; # -L - -R*) - if test "$pass" = link; then - func_stripname '-R' '' "$deplib" - func_resolve_sysroot "$func_stripname_result" - dir=$func_resolve_sysroot_result - # Make sure the xrpath contains only unique directories. - case "$xrpath " in - *" $dir "*) ;; - *) func_append xrpath " $dir" ;; - esac - fi - deplibs="$deplib $deplibs" - continue - ;; - *.la) - func_resolve_sysroot "$deplib" - lib=$func_resolve_sysroot_result - ;; - *.$libext) - if test "$pass" = conv; then - deplibs="$deplib $deplibs" - continue - fi - case $linkmode in - lib) - # Linking convenience modules into shared libraries is allowed, - # but linking other static libraries is non-portable. - case " $dlpreconveniencelibs " in - *" $deplib "*) ;; - *) - valid_a_lib=no - case $deplibs_check_method in - match_pattern*) - set dummy $deplibs_check_method; shift - match_pattern_regex=`expr "$deplibs_check_method" : "$1 \(.*\)"` - if eval "\$ECHO \"$deplib\"" 2>/dev/null | $SED 10q \ - | $EGREP "$match_pattern_regex" > /dev/null; then - valid_a_lib=yes - fi - ;; - pass_all) - valid_a_lib=yes - ;; - esac - if test "$valid_a_lib" != yes; then - echo - $ECHO "*** Warning: Trying to link with static lib archive $deplib." - echo "*** I have the capability to make that library automatically link in when" - echo "*** you link to this library. But I can only do this if you have a" - echo "*** shared version of the library, which you do not appear to have" - echo "*** because the file extensions .$libext of this argument makes me believe" - echo "*** that it is just a static archive that I should not use here." - else - echo - $ECHO "*** Warning: Linking the shared library $output against the" - $ECHO "*** static library $deplib is not portable!" - deplibs="$deplib $deplibs" - fi - ;; - esac - continue - ;; - prog) - if test "$pass" != link; then - deplibs="$deplib $deplibs" - else - compile_deplibs="$deplib $compile_deplibs" - finalize_deplibs="$deplib $finalize_deplibs" - fi - continue - ;; - esac # linkmode - ;; # *.$libext - *.lo | *.$objext) - if test "$pass" = conv; then - deplibs="$deplib $deplibs" - elif test "$linkmode" = prog; then - if test "$pass" = dlpreopen || test "$dlopen_support" != yes || test "$build_libtool_libs" = no; then - # If there is no dlopen support or we're linking statically, - # we need to preload. - func_append newdlprefiles " $deplib" - compile_deplibs="$deplib $compile_deplibs" - finalize_deplibs="$deplib $finalize_deplibs" - else - func_append newdlfiles " $deplib" - fi - fi - continue - ;; - %DEPLIBS%) - alldeplibs=yes - continue - ;; - esac # case $deplib - - if test "$found" = yes || test -f "$lib"; then : - else - func_fatal_error "cannot find the library \`$lib' or unhandled argument \`$deplib'" - fi - - # Check to see that this really is a libtool archive. - func_lalib_unsafe_p "$lib" \ - || func_fatal_error "\`$lib' is not a valid libtool archive" - - func_dirname "$lib" "" "." - ladir="$func_dirname_result" - - dlname= - dlopen= - dlpreopen= - libdir= - library_names= - old_library= - inherited_linker_flags= - # If the library was installed with an old release of libtool, - # it will not redefine variables installed, or shouldnotlink - installed=yes - shouldnotlink=no - avoidtemprpath= - - - # Read the .la file - func_source "$lib" - - # Convert "-framework foo" to "foo.ltframework" - if test -n "$inherited_linker_flags"; then - tmp_inherited_linker_flags=`$ECHO "$inherited_linker_flags" | $SED 's/-framework \([^ $]*\)/\1.ltframework/g'` - for tmp_inherited_linker_flag in $tmp_inherited_linker_flags; do - case " $new_inherited_linker_flags " in - *" $tmp_inherited_linker_flag "*) ;; - *) func_append new_inherited_linker_flags " $tmp_inherited_linker_flag";; - esac - done - fi - dependency_libs=`$ECHO " $dependency_libs" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'` - if test "$linkmode,$pass" = "lib,link" || - test "$linkmode,$pass" = "prog,scan" || - { test "$linkmode" != prog && test "$linkmode" != lib; }; then - test -n "$dlopen" && func_append dlfiles " $dlopen" - test -n "$dlpreopen" && func_append dlprefiles " $dlpreopen" - fi - - if test "$pass" = conv; then - # Only check for convenience libraries - deplibs="$lib $deplibs" - if test -z "$libdir"; then - if test -z "$old_library"; then - func_fatal_error "cannot find name of link library for \`$lib'" - fi - # It is a libtool convenience library, so add in its objects. - func_append convenience " $ladir/$objdir/$old_library" - func_append old_convenience " $ladir/$objdir/$old_library" - tmp_libs= - for deplib in $dependency_libs; do - deplibs="$deplib $deplibs" - if $opt_preserve_dup_deps ; then - case "$tmp_libs " in - *" $deplib "*) func_append specialdeplibs " $deplib" ;; - esac - fi - func_append tmp_libs " $deplib" - done - elif test "$linkmode" != prog && test "$linkmode" != lib; then - func_fatal_error "\`$lib' is not a convenience library" - fi - continue - fi # $pass = conv - - - # Get the name of the library we link against. - linklib= - if test -n "$old_library" && - { test "$prefer_static_libs" = yes || - test "$prefer_static_libs,$installed" = "built,no"; }; then - linklib=$old_library - else - for l in $old_library $library_names; do - linklib="$l" - done - fi - if test -z "$linklib"; then - func_fatal_error "cannot find name of link library for \`$lib'" - fi - - # This library was specified with -dlopen. - if test "$pass" = dlopen; then - if test -z "$libdir"; then - func_fatal_error "cannot -dlopen a convenience library: \`$lib'" - fi - if test -z "$dlname" || - test "$dlopen_support" != yes || - test "$build_libtool_libs" = no; then - # If there is no dlname, no dlopen support or we're linking - # statically, we need to preload. We also need to preload any - # dependent libraries so libltdl's deplib preloader doesn't - # bomb out in the load deplibs phase. - func_append dlprefiles " $lib $dependency_libs" - else - func_append newdlfiles " $lib" - fi - continue - fi # $pass = dlopen - - # We need an absolute path. - case $ladir in - [\\/]* | [A-Za-z]:[\\/]*) abs_ladir="$ladir" ;; - *) - abs_ladir=`cd "$ladir" && pwd` - if test -z "$abs_ladir"; then - func_warning "cannot determine absolute directory name of \`$ladir'" - func_warning "passing it literally to the linker, although it might fail" - abs_ladir="$ladir" - fi - ;; - esac - func_basename "$lib" - laname="$func_basename_result" - - # Find the relevant object directory and library name. - if test "X$installed" = Xyes; then - if test ! -f "$lt_sysroot$libdir/$linklib" && test -f "$abs_ladir/$linklib"; then - func_warning "library \`$lib' was moved." - dir="$ladir" - absdir="$abs_ladir" - libdir="$abs_ladir" - else - dir="$lt_sysroot$libdir" - absdir="$lt_sysroot$libdir" - fi - test "X$hardcode_automatic" = Xyes && avoidtemprpath=yes - else - if test ! -f "$ladir/$objdir/$linklib" && test -f "$abs_ladir/$linklib"; then - dir="$ladir" - absdir="$abs_ladir" - # Remove this search path later - func_append notinst_path " $abs_ladir" - else - dir="$ladir/$objdir" - absdir="$abs_ladir/$objdir" - # Remove this search path later - func_append notinst_path " $abs_ladir" - fi - fi # $installed = yes - func_stripname 'lib' '.la' "$laname" - name=$func_stripname_result - - # This library was specified with -dlpreopen. - if test "$pass" = dlpreopen; then - if test -z "$libdir" && test "$linkmode" = prog; then - func_fatal_error "only libraries may -dlpreopen a convenience library: \`$lib'" - fi - case "$host" in - # special handling for platforms with PE-DLLs. - *cygwin* | *mingw* | *cegcc* ) - # Linker will automatically link against shared library if both - # static and shared are present. Therefore, ensure we extract - # symbols from the import library if a shared library is present - # (otherwise, the dlopen module name will be incorrect). We do - # this by putting the import library name into $newdlprefiles. - # We recover the dlopen module name by 'saving' the la file - # name in a special purpose variable, and (later) extracting the - # dlname from the la file. - if test -n "$dlname"; then - func_tr_sh "$dir/$linklib" - eval "libfile_$func_tr_sh_result=\$abs_ladir/\$laname" - func_append newdlprefiles " $dir/$linklib" - else - func_append newdlprefiles " $dir/$old_library" - # Keep a list of preopened convenience libraries to check - # that they are being used correctly in the link pass. - test -z "$libdir" && \ - func_append dlpreconveniencelibs " $dir/$old_library" - fi - ;; - * ) - # Prefer using a static library (so that no silly _DYNAMIC symbols - # are required to link). - if test -n "$old_library"; then - func_append newdlprefiles " $dir/$old_library" - # Keep a list of preopened convenience libraries to check - # that they are being used correctly in the link pass. - test -z "$libdir" && \ - func_append dlpreconveniencelibs " $dir/$old_library" - # Otherwise, use the dlname, so that lt_dlopen finds it. - elif test -n "$dlname"; then - func_append newdlprefiles " $dir/$dlname" - else - func_append newdlprefiles " $dir/$linklib" - fi - ;; - esac - fi # $pass = dlpreopen - - if test -z "$libdir"; then - # Link the convenience library - if test "$linkmode" = lib; then - deplibs="$dir/$old_library $deplibs" - elif test "$linkmode,$pass" = "prog,link"; then - compile_deplibs="$dir/$old_library $compile_deplibs" - finalize_deplibs="$dir/$old_library $finalize_deplibs" - else - deplibs="$lib $deplibs" # used for prog,scan pass - fi - continue - fi - - - if test "$linkmode" = prog && test "$pass" != link; then - func_append newlib_search_path " $ladir" - deplibs="$lib $deplibs" - - linkalldeplibs=no - if test "$link_all_deplibs" != no || test -z "$library_names" || - test "$build_libtool_libs" = no; then - linkalldeplibs=yes - fi - - tmp_libs= - for deplib in $dependency_libs; do - case $deplib in - -L*) func_stripname '-L' '' "$deplib" - func_resolve_sysroot "$func_stripname_result" - func_append newlib_search_path " $func_resolve_sysroot_result" - ;; - esac - # Need to link against all dependency_libs? - if test "$linkalldeplibs" = yes; then - deplibs="$deplib $deplibs" - else - # Need to hardcode shared library paths - # or/and link against static libraries - newdependency_libs="$deplib $newdependency_libs" - fi - if $opt_preserve_dup_deps ; then - case "$tmp_libs " in - *" $deplib "*) func_append specialdeplibs " $deplib" ;; - esac - fi - func_append tmp_libs " $deplib" - done # for deplib - continue - fi # $linkmode = prog... - - if test "$linkmode,$pass" = "prog,link"; then - if test -n "$library_names" && - { { test "$prefer_static_libs" = no || - test "$prefer_static_libs,$installed" = "built,yes"; } || - test -z "$old_library"; }; then - # We need to hardcode the library path - if test -n "$shlibpath_var" && test -z "$avoidtemprpath" ; then - # Make sure the rpath contains only unique directories. - case "$temp_rpath:" in - *"$absdir:"*) ;; - *) func_append temp_rpath "$absdir:" ;; - esac - fi - - # Hardcode the library path. - # Skip directories that are in the system default run-time - # search path. - case " $sys_lib_dlsearch_path " in - *" $absdir "*) ;; - *) - case "$compile_rpath " in - *" $absdir "*) ;; - *) func_append compile_rpath " $absdir" ;; - esac - ;; - esac - case " $sys_lib_dlsearch_path " in - *" $libdir "*) ;; - *) - case "$finalize_rpath " in - *" $libdir "*) ;; - *) func_append finalize_rpath " $libdir" ;; - esac - ;; - esac - fi # $linkmode,$pass = prog,link... - - if test "$alldeplibs" = yes && - { test "$deplibs_check_method" = pass_all || - { test "$build_libtool_libs" = yes && - test -n "$library_names"; }; }; then - # We only need to search for static libraries - continue - fi - fi - - link_static=no # Whether the deplib will be linked statically - use_static_libs=$prefer_static_libs - if test "$use_static_libs" = built && test "$installed" = yes; then - use_static_libs=no - fi - if test -n "$library_names" && - { test "$use_static_libs" = no || test -z "$old_library"; }; then - case $host in - *cygwin* | *mingw* | *cegcc*) - # No point in relinking DLLs because paths are not encoded - func_append notinst_deplibs " $lib" - need_relink=no - ;; - *) - if test "$installed" = no; then - func_append notinst_deplibs " $lib" - need_relink=yes - fi - ;; - esac - # This is a shared library - - # Warn about portability, can't link against -module's on some - # systems (darwin). Don't bleat about dlopened modules though! - dlopenmodule="" - for dlpremoduletest in $dlprefiles; do - if test "X$dlpremoduletest" = "X$lib"; then - dlopenmodule="$dlpremoduletest" - break - fi - done - if test -z "$dlopenmodule" && test "$shouldnotlink" = yes && test "$pass" = link; then - echo - if test "$linkmode" = prog; then - $ECHO "*** Warning: Linking the executable $output against the loadable module" - else - $ECHO "*** Warning: Linking the shared library $output against the loadable module" - fi - $ECHO "*** $linklib is not portable!" - fi - if test "$linkmode" = lib && - test "$hardcode_into_libs" = yes; then - # Hardcode the library path. - # Skip directories that are in the system default run-time - # search path. - case " $sys_lib_dlsearch_path " in - *" $absdir "*) ;; - *) - case "$compile_rpath " in - *" $absdir "*) ;; - *) func_append compile_rpath " $absdir" ;; - esac - ;; - esac - case " $sys_lib_dlsearch_path " in - *" $libdir "*) ;; - *) - case "$finalize_rpath " in - *" $libdir "*) ;; - *) func_append finalize_rpath " $libdir" ;; - esac - ;; - esac - fi - - if test -n "$old_archive_from_expsyms_cmds"; then - # figure out the soname - set dummy $library_names - shift - realname="$1" - shift - libname=`eval "\\$ECHO \"$libname_spec\""` - # use dlname if we got it. it's perfectly good, no? - if test -n "$dlname"; then - soname="$dlname" - elif test -n "$soname_spec"; then - # bleh windows - case $host in - *cygwin* | mingw* | *cegcc*) - func_arith $current - $age - major=$func_arith_result - versuffix="-$major" - ;; - esac - eval soname=\"$soname_spec\" - else - soname="$realname" - fi - - # Make a new name for the extract_expsyms_cmds to use - soroot="$soname" - func_basename "$soroot" - soname="$func_basename_result" - func_stripname 'lib' '.dll' "$soname" - newlib=libimp-$func_stripname_result.a - - # If the library has no export list, then create one now - if test -f "$output_objdir/$soname-def"; then : - else - func_verbose "extracting exported symbol list from \`$soname'" - func_execute_cmds "$extract_expsyms_cmds" 'exit $?' - fi - - # Create $newlib - if test -f "$output_objdir/$newlib"; then :; else - func_verbose "generating import library for \`$soname'" - func_execute_cmds "$old_archive_from_expsyms_cmds" 'exit $?' - fi - # make sure the library variables are pointing to the new library - dir=$output_objdir - linklib=$newlib - fi # test -n "$old_archive_from_expsyms_cmds" - - if test "$linkmode" = prog || test "$opt_mode" != relink; then - add_shlibpath= - add_dir= - add= - lib_linked=yes - case $hardcode_action in - immediate | unsupported) - if test "$hardcode_direct" = no; then - add="$dir/$linklib" - case $host in - *-*-sco3.2v5.0.[024]*) add_dir="-L$dir" ;; - *-*-sysv4*uw2*) add_dir="-L$dir" ;; - *-*-sysv5OpenUNIX* | *-*-sysv5UnixWare7.[01].[10]* | \ - *-*-unixware7*) add_dir="-L$dir" ;; - *-*-darwin* ) - # if the lib is a (non-dlopened) module then we can not - # link against it, someone is ignoring the earlier warnings - if /usr/bin/file -L $add 2> /dev/null | - $GREP ": [^:]* bundle" >/dev/null ; then - if test "X$dlopenmodule" != "X$lib"; then - $ECHO "*** Warning: lib $linklib is a module, not a shared library" - if test -z "$old_library" ; then - echo - echo "*** And there doesn't seem to be a static archive available" - echo "*** The link will probably fail, sorry" - else - add="$dir/$old_library" - fi - elif test -n "$old_library"; then - add="$dir/$old_library" - fi - fi - esac - elif test "$hardcode_minus_L" = no; then - case $host in - *-*-sunos*) add_shlibpath="$dir" ;; - esac - add_dir="-L$dir" - add="-l$name" - elif test "$hardcode_shlibpath_var" = no; then - add_shlibpath="$dir" - add="-l$name" - else - lib_linked=no - fi - ;; - relink) - if test "$hardcode_direct" = yes && - test "$hardcode_direct_absolute" = no; then - add="$dir/$linklib" - elif test "$hardcode_minus_L" = yes; then - add_dir="-L$absdir" - # Try looking first in the location we're being installed to. - if test -n "$inst_prefix_dir"; then - case $libdir in - [\\/]*) - func_append add_dir " -L$inst_prefix_dir$libdir" - ;; - esac - fi - add="-l$name" - elif test "$hardcode_shlibpath_var" = yes; then - add_shlibpath="$dir" - add="-l$name" - else - lib_linked=no - fi - ;; - *) lib_linked=no ;; - esac - - if test "$lib_linked" != yes; then - func_fatal_configuration "unsupported hardcode properties" - fi - - if test -n "$add_shlibpath"; then - case :$compile_shlibpath: in - *":$add_shlibpath:"*) ;; - *) func_append compile_shlibpath "$add_shlibpath:" ;; - esac - fi - if test "$linkmode" = prog; then - test -n "$add_dir" && compile_deplibs="$add_dir $compile_deplibs" - test -n "$add" && compile_deplibs="$add $compile_deplibs" - else - test -n "$add_dir" && deplibs="$add_dir $deplibs" - test -n "$add" && deplibs="$add $deplibs" - if test "$hardcode_direct" != yes && - test "$hardcode_minus_L" != yes && - test "$hardcode_shlibpath_var" = yes; then - case :$finalize_shlibpath: in - *":$libdir:"*) ;; - *) func_append finalize_shlibpath "$libdir:" ;; - esac - fi - fi - fi - - if test "$linkmode" = prog || test "$opt_mode" = relink; then - add_shlibpath= - add_dir= - add= - # Finalize command for both is simple: just hardcode it. - if test "$hardcode_direct" = yes && - test "$hardcode_direct_absolute" = no; then - add="$libdir/$linklib" - elif test "$hardcode_minus_L" = yes; then - add_dir="-L$libdir" - add="-l$name" - elif test "$hardcode_shlibpath_var" = yes; then - case :$finalize_shlibpath: in - *":$libdir:"*) ;; - *) func_append finalize_shlibpath "$libdir:" ;; - esac - add="-l$name" - elif test "$hardcode_automatic" = yes; then - if test -n "$inst_prefix_dir" && - test -f "$inst_prefix_dir$libdir/$linklib" ; then - add="$inst_prefix_dir$libdir/$linklib" - else - add="$libdir/$linklib" - fi - else - # We cannot seem to hardcode it, guess we'll fake it. - add_dir="-L$libdir" - # Try looking first in the location we're being installed to. - if test -n "$inst_prefix_dir"; then - case $libdir in - [\\/]*) - func_append add_dir " -L$inst_prefix_dir$libdir" - ;; - esac - fi - add="-l$name" - fi - - if test "$linkmode" = prog; then - test -n "$add_dir" && finalize_deplibs="$add_dir $finalize_deplibs" - test -n "$add" && finalize_deplibs="$add $finalize_deplibs" - else - test -n "$add_dir" && deplibs="$add_dir $deplibs" - test -n "$add" && deplibs="$add $deplibs" - fi - fi - elif test "$linkmode" = prog; then - # Here we assume that one of hardcode_direct or hardcode_minus_L - # is not unsupported. This is valid on all known static and - # shared platforms. - if test "$hardcode_direct" != unsupported; then - test -n "$old_library" && linklib="$old_library" - compile_deplibs="$dir/$linklib $compile_deplibs" - finalize_deplibs="$dir/$linklib $finalize_deplibs" - else - compile_deplibs="-l$name -L$dir $compile_deplibs" - finalize_deplibs="-l$name -L$dir $finalize_deplibs" - fi - elif test "$build_libtool_libs" = yes; then - # Not a shared library - if test "$deplibs_check_method" != pass_all; then - # We're trying link a shared library against a static one - # but the system doesn't support it. - - # Just print a warning and add the library to dependency_libs so - # that the program can be linked against the static library. - echo - $ECHO "*** Warning: This system can not link to static lib archive $lib." - echo "*** I have the capability to make that library automatically link in when" - echo "*** you link to this library. But I can only do this if you have a" - echo "*** shared version of the library, which you do not appear to have." - if test "$module" = yes; then - echo "*** But as you try to build a module library, libtool will still create " - echo "*** a static module, that should work as long as the dlopening application" - echo "*** is linked with the -dlopen flag to resolve symbols at runtime." - if test -z "$global_symbol_pipe"; then - echo - echo "*** However, this would only work if libtool was able to extract symbol" - echo "*** lists from a program, using \`nm' or equivalent, but libtool could" - echo "*** not find such a program. So, this module is probably useless." - echo "*** \`nm' from GNU binutils and a full rebuild may help." - fi - if test "$build_old_libs" = no; then - build_libtool_libs=module - build_old_libs=yes - else - build_libtool_libs=no - fi - fi - else - deplibs="$dir/$old_library $deplibs" - link_static=yes - fi - fi # link shared/static library? - - if test "$linkmode" = lib; then - if test -n "$dependency_libs" && - { test "$hardcode_into_libs" != yes || - test "$build_old_libs" = yes || - test "$link_static" = yes; }; then - # Extract -R from dependency_libs - temp_deplibs= - for libdir in $dependency_libs; do - case $libdir in - -R*) func_stripname '-R' '' "$libdir" - temp_xrpath=$func_stripname_result - case " $xrpath " in - *" $temp_xrpath "*) ;; - *) func_append xrpath " $temp_xrpath";; - esac;; - *) func_append temp_deplibs " $libdir";; - esac - done - dependency_libs="$temp_deplibs" - fi - - func_append newlib_search_path " $absdir" - # Link against this library - test "$link_static" = no && newdependency_libs="$abs_ladir/$laname $newdependency_libs" - # ... and its dependency_libs - tmp_libs= - for deplib in $dependency_libs; do - newdependency_libs="$deplib $newdependency_libs" - case $deplib in - -L*) func_stripname '-L' '' "$deplib" - func_resolve_sysroot "$func_stripname_result";; - *) func_resolve_sysroot "$deplib" ;; - esac - if $opt_preserve_dup_deps ; then - case "$tmp_libs " in - *" $func_resolve_sysroot_result "*) - func_append specialdeplibs " $func_resolve_sysroot_result" ;; - esac - fi - func_append tmp_libs " $func_resolve_sysroot_result" - done - - if test "$link_all_deplibs" != no; then - # Add the search paths of all dependency libraries - for deplib in $dependency_libs; do - path= - case $deplib in - -L*) path="$deplib" ;; - *.la) - func_resolve_sysroot "$deplib" - deplib=$func_resolve_sysroot_result - func_dirname "$deplib" "" "." - dir=$func_dirname_result - # We need an absolute path. - case $dir in - [\\/]* | [A-Za-z]:[\\/]*) absdir="$dir" ;; - *) - absdir=`cd "$dir" && pwd` - if test -z "$absdir"; then - func_warning "cannot determine absolute directory name of \`$dir'" - absdir="$dir" - fi - ;; - esac - if $GREP "^installed=no" $deplib > /dev/null; then - case $host in - *-*-darwin*) - depdepl= - eval deplibrary_names=`${SED} -n -e 's/^library_names=\(.*\)$/\1/p' $deplib` - if test -n "$deplibrary_names" ; then - for tmp in $deplibrary_names ; do - depdepl=$tmp - done - if test -f "$absdir/$objdir/$depdepl" ; then - depdepl="$absdir/$objdir/$depdepl" - darwin_install_name=`${OTOOL} -L $depdepl | awk '{if (NR == 2) {print $1;exit}}'` - if test -z "$darwin_install_name"; then - darwin_install_name=`${OTOOL64} -L $depdepl | awk '{if (NR == 2) {print $1;exit}}'` - fi - func_append compiler_flags " ${wl}-dylib_file ${wl}${darwin_install_name}:${depdepl}" - func_append linker_flags " -dylib_file ${darwin_install_name}:${depdepl}" - path= - fi - fi - ;; - *) - path="-L$absdir/$objdir" - ;; - esac - else - eval libdir=`${SED} -n -e 's/^libdir=\(.*\)$/\1/p' $deplib` - test -z "$libdir" && \ - func_fatal_error "\`$deplib' is not a valid libtool archive" - test "$absdir" != "$libdir" && \ - func_warning "\`$deplib' seems to be moved" - - path="-L$absdir" - fi - ;; - esac - case " $deplibs " in - *" $path "*) ;; - *) deplibs="$path $deplibs" ;; - esac - done - fi # link_all_deplibs != no - fi # linkmode = lib - done # for deplib in $libs - if test "$pass" = link; then - if test "$linkmode" = "prog"; then - compile_deplibs="$new_inherited_linker_flags $compile_deplibs" - finalize_deplibs="$new_inherited_linker_flags $finalize_deplibs" - else - compiler_flags="$compiler_flags "`$ECHO " $new_inherited_linker_flags" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'` - fi - fi - dependency_libs="$newdependency_libs" - if test "$pass" = dlpreopen; then - # Link the dlpreopened libraries before other libraries - for deplib in $save_deplibs; do - deplibs="$deplib $deplibs" - done - fi - if test "$pass" != dlopen; then - if test "$pass" != conv; then - # Make sure lib_search_path contains only unique directories. - lib_search_path= - for dir in $newlib_search_path; do - case "$lib_search_path " in - *" $dir "*) ;; - *) func_append lib_search_path " $dir" ;; - esac - done - newlib_search_path= - fi - - if test "$linkmode,$pass" != "prog,link"; then - vars="deplibs" - else - vars="compile_deplibs finalize_deplibs" - fi - for var in $vars dependency_libs; do - # Add libraries to $var in reverse order - eval tmp_libs=\"\$$var\" - new_libs= - for deplib in $tmp_libs; do - # FIXME: Pedantically, this is the right thing to do, so - # that some nasty dependency loop isn't accidentally - # broken: - #new_libs="$deplib $new_libs" - # Pragmatically, this seems to cause very few problems in - # practice: - case $deplib in - -L*) new_libs="$deplib $new_libs" ;; - -R*) ;; - *) - # And here is the reason: when a library appears more - # than once as an explicit dependence of a library, or - # is implicitly linked in more than once by the - # compiler, it is considered special, and multiple - # occurrences thereof are not removed. Compare this - # with having the same library being listed as a - # dependency of multiple other libraries: in this case, - # we know (pedantically, we assume) the library does not - # need to be listed more than once, so we keep only the - # last copy. This is not always right, but it is rare - # enough that we require users that really mean to play - # such unportable linking tricks to link the library - # using -Wl,-lname, so that libtool does not consider it - # for duplicate removal. - case " $specialdeplibs " in - *" $deplib "*) new_libs="$deplib $new_libs" ;; - *) - case " $new_libs " in - *" $deplib "*) ;; - *) new_libs="$deplib $new_libs" ;; - esac - ;; - esac - ;; - esac - done - tmp_libs= - for deplib in $new_libs; do - case $deplib in - -L*) - case " $tmp_libs " in - *" $deplib "*) ;; - *) func_append tmp_libs " $deplib" ;; - esac - ;; - *) func_append tmp_libs " $deplib" ;; - esac - done - eval $var=\"$tmp_libs\" - done # for var - fi - # Last step: remove runtime libs from dependency_libs - # (they stay in deplibs) - tmp_libs= - for i in $dependency_libs ; do - case " $predeps $postdeps $compiler_lib_search_path " in - *" $i "*) - i="" - ;; - esac - if test -n "$i" ; then - func_append tmp_libs " $i" - fi - done - dependency_libs=$tmp_libs - done # for pass - if test "$linkmode" = prog; then - dlfiles="$newdlfiles" - fi - if test "$linkmode" = prog || test "$linkmode" = lib; then - dlprefiles="$newdlprefiles" - fi - - case $linkmode in - oldlib) - if test -n "$dlfiles$dlprefiles" || test "$dlself" != no; then - func_warning "\`-dlopen' is ignored for archives" - fi - - case " $deplibs" in - *\ -l* | *\ -L*) - func_warning "\`-l' and \`-L' are ignored for archives" ;; - esac - - test -n "$rpath" && \ - func_warning "\`-rpath' is ignored for archives" - - test -n "$xrpath" && \ - func_warning "\`-R' is ignored for archives" - - test -n "$vinfo" && \ - func_warning "\`-version-info/-version-number' is ignored for archives" - - test -n "$release" && \ - func_warning "\`-release' is ignored for archives" - - test -n "$export_symbols$export_symbols_regex" && \ - func_warning "\`-export-symbols' is ignored for archives" - - # Now set the variables for building old libraries. - build_libtool_libs=no - oldlibs="$output" - func_append objs "$old_deplibs" - ;; - - lib) - # Make sure we only generate libraries of the form `libNAME.la'. - case $outputname in - lib*) - func_stripname 'lib' '.la' "$outputname" - name=$func_stripname_result - eval shared_ext=\"$shrext_cmds\" - eval libname=\"$libname_spec\" - ;; - *) - test "$module" = no && \ - func_fatal_help "libtool library \`$output' must begin with \`lib'" - - if test "$need_lib_prefix" != no; then - # Add the "lib" prefix for modules if required - func_stripname '' '.la' "$outputname" - name=$func_stripname_result - eval shared_ext=\"$shrext_cmds\" - eval libname=\"$libname_spec\" - else - func_stripname '' '.la' "$outputname" - libname=$func_stripname_result - fi - ;; - esac - - if test -n "$objs"; then - if test "$deplibs_check_method" != pass_all; then - func_fatal_error "cannot build libtool library \`$output' from non-libtool objects on this host:$objs" - else - echo - $ECHO "*** Warning: Linking the shared library $output against the non-libtool" - $ECHO "*** objects $objs is not portable!" - func_append libobjs " $objs" - fi - fi - - test "$dlself" != no && \ - func_warning "\`-dlopen self' is ignored for libtool libraries" - - set dummy $rpath - shift - test "$#" -gt 1 && \ - func_warning "ignoring multiple \`-rpath's for a libtool library" - - install_libdir="$1" - - oldlibs= - if test -z "$rpath"; then - if test "$build_libtool_libs" = yes; then - # Building a libtool convenience library. - # Some compilers have problems with a `.al' extension so - # convenience libraries should have the same extension an - # archive normally would. - oldlibs="$output_objdir/$libname.$libext $oldlibs" - build_libtool_libs=convenience - build_old_libs=yes - fi - - test -n "$vinfo" && \ - func_warning "\`-version-info/-version-number' is ignored for convenience libraries" - - test -n "$release" && \ - func_warning "\`-release' is ignored for convenience libraries" - else - - # Parse the version information argument. - save_ifs="$IFS"; IFS=':' - set dummy $vinfo 0 0 0 - shift - IFS="$save_ifs" - - test -n "$7" && \ - func_fatal_help "too many parameters to \`-version-info'" - - # convert absolute version numbers to libtool ages - # this retains compatibility with .la files and attempts - # to make the code below a bit more comprehensible - - case $vinfo_number in - yes) - number_major="$1" - number_minor="$2" - number_revision="$3" - # - # There are really only two kinds -- those that - # use the current revision as the major version - # and those that subtract age and use age as - # a minor version. But, then there is irix - # which has an extra 1 added just for fun - # - case $version_type in - # correct linux to gnu/linux during the next big refactor - darwin|linux|osf|windows|none) - func_arith $number_major + $number_minor - current=$func_arith_result - age="$number_minor" - revision="$number_revision" - ;; - freebsd-aout|freebsd-elf|qnx|sunos) - current="$number_major" - revision="$number_minor" - age="0" - ;; - irix|nonstopux) - func_arith $number_major + $number_minor - current=$func_arith_result - age="$number_minor" - revision="$number_minor" - lt_irix_increment=no - ;; - *) - func_fatal_configuration "$modename: unknown library version type \`$version_type'" - ;; - esac - ;; - no) - current="$1" - revision="$2" - age="$3" - ;; - esac - - # Check that each of the things are valid numbers. - case $current in - 0|[1-9]|[1-9][0-9]|[1-9][0-9][0-9]|[1-9][0-9][0-9][0-9]|[1-9][0-9][0-9][0-9][0-9]) ;; - *) - func_error "CURRENT \`$current' must be a nonnegative integer" - func_fatal_error "\`$vinfo' is not valid version information" - ;; - esac - - case $revision in - 0|[1-9]|[1-9][0-9]|[1-9][0-9][0-9]|[1-9][0-9][0-9][0-9]|[1-9][0-9][0-9][0-9][0-9]) ;; - *) - func_error "REVISION \`$revision' must be a nonnegative integer" - func_fatal_error "\`$vinfo' is not valid version information" - ;; - esac - - case $age in - 0|[1-9]|[1-9][0-9]|[1-9][0-9][0-9]|[1-9][0-9][0-9][0-9]|[1-9][0-9][0-9][0-9][0-9]) ;; - *) - func_error "AGE \`$age' must be a nonnegative integer" - func_fatal_error "\`$vinfo' is not valid version information" - ;; - esac - - if test "$age" -gt "$current"; then - func_error "AGE \`$age' is greater than the current interface number \`$current'" - func_fatal_error "\`$vinfo' is not valid version information" - fi - - # Calculate the version variables. - major= - versuffix= - verstring= - case $version_type in - none) ;; - - darwin) - # Like Linux, but with the current version available in - # verstring for coding it into the library header - func_arith $current - $age - major=.$func_arith_result - versuffix="$major.$age.$revision" - # Darwin ld doesn't like 0 for these options... - func_arith $current + 1 - minor_current=$func_arith_result - xlcverstring="${wl}-compatibility_version ${wl}$minor_current ${wl}-current_version ${wl}$minor_current.$revision" - verstring="-compatibility_version $minor_current -current_version $minor_current.$revision" - ;; - - freebsd-aout) - major=".$current" - versuffix=".$current.$revision"; - ;; - - freebsd-elf) - major=".$current" - versuffix=".$current" - ;; - - irix | nonstopux) - if test "X$lt_irix_increment" = "Xno"; then - func_arith $current - $age - else - func_arith $current - $age + 1 - fi - major=$func_arith_result - - case $version_type in - nonstopux) verstring_prefix=nonstopux ;; - *) verstring_prefix=sgi ;; - esac - verstring="$verstring_prefix$major.$revision" - - # Add in all the interfaces that we are compatible with. - loop=$revision - while test "$loop" -ne 0; do - func_arith $revision - $loop - iface=$func_arith_result - func_arith $loop - 1 - loop=$func_arith_result - verstring="$verstring_prefix$major.$iface:$verstring" - done - - # Before this point, $major must not contain `.'. - major=.$major - versuffix="$major.$revision" - ;; - - linux) # correct to gnu/linux during the next big refactor - func_arith $current - $age - major=.$func_arith_result - versuffix="$major.$age.$revision" - ;; - - osf) - func_arith $current - $age - major=.$func_arith_result - versuffix=".$current.$age.$revision" - verstring="$current.$age.$revision" - - # Add in all the interfaces that we are compatible with. - loop=$age - while test "$loop" -ne 0; do - func_arith $current - $loop - iface=$func_arith_result - func_arith $loop - 1 - loop=$func_arith_result - verstring="$verstring:${iface}.0" - done - - # Make executables depend on our current version. - func_append verstring ":${current}.0" - ;; - - qnx) - major=".$current" - versuffix=".$current" - ;; - - sunos) - major=".$current" - versuffix=".$current.$revision" - ;; - - windows) - # Use '-' rather than '.', since we only want one - # extension on DOS 8.3 filesystems. - func_arith $current - $age - major=$func_arith_result - versuffix="-$major" - ;; - - *) - func_fatal_configuration "unknown library version type \`$version_type'" - ;; - esac - - # Clear the version info if we defaulted, and they specified a release. - if test -z "$vinfo" && test -n "$release"; then - major= - case $version_type in - darwin) - # we can't check for "0.0" in archive_cmds due to quoting - # problems, so we reset it completely - verstring= - ;; - *) - verstring="0.0" - ;; - esac - if test "$need_version" = no; then - versuffix= - else - versuffix=".0.0" - fi - fi - - # Remove version info from name if versioning should be avoided - if test "$avoid_version" = yes && test "$need_version" = no; then - major= - versuffix= - verstring="" - fi - - # Check to see if the archive will have undefined symbols. - if test "$allow_undefined" = yes; then - if test "$allow_undefined_flag" = unsupported; then - func_warning "undefined symbols not allowed in $host shared libraries" - build_libtool_libs=no - build_old_libs=yes - fi - else - # Don't allow undefined symbols. - allow_undefined_flag="$no_undefined_flag" - fi - - fi - - func_generate_dlsyms "$libname" "$libname" "yes" - func_append libobjs " $symfileobj" - test "X$libobjs" = "X " && libobjs= - - if test "$opt_mode" != relink; then - # Remove our outputs, but don't remove object files since they - # may have been created when compiling PIC objects. - removelist= - tempremovelist=`$ECHO "$output_objdir/*"` - for p in $tempremovelist; do - case $p in - *.$objext | *.gcno) - ;; - $output_objdir/$outputname | $output_objdir/$libname.* | $output_objdir/${libname}${release}.*) - if test "X$precious_files_regex" != "X"; then - if $ECHO "$p" | $EGREP -e "$precious_files_regex" >/dev/null 2>&1 - then - continue - fi - fi - func_append removelist " $p" - ;; - *) ;; - esac - done - test -n "$removelist" && \ - func_show_eval "${RM}r \$removelist" - fi - - # Now set the variables for building old libraries. - if test "$build_old_libs" = yes && test "$build_libtool_libs" != convenience ; then - func_append oldlibs " $output_objdir/$libname.$libext" - - # Transform .lo files to .o files. - oldobjs="$objs "`$ECHO "$libobjs" | $SP2NL | $SED "/\.${libext}$/d; $lo2o" | $NL2SP` - fi - - # Eliminate all temporary directories. - #for path in $notinst_path; do - # lib_search_path=`$ECHO "$lib_search_path " | $SED "s% $path % %g"` - # deplibs=`$ECHO "$deplibs " | $SED "s% -L$path % %g"` - # dependency_libs=`$ECHO "$dependency_libs " | $SED "s% -L$path % %g"` - #done - - if test -n "$xrpath"; then - # If the user specified any rpath flags, then add them. - temp_xrpath= - for libdir in $xrpath; do - func_replace_sysroot "$libdir" - func_append temp_xrpath " -R$func_replace_sysroot_result" - case "$finalize_rpath " in - *" $libdir "*) ;; - *) func_append finalize_rpath " $libdir" ;; - esac - done - if test "$hardcode_into_libs" != yes || test "$build_old_libs" = yes; then - dependency_libs="$temp_xrpath $dependency_libs" - fi - fi - - # Make sure dlfiles contains only unique files that won't be dlpreopened - old_dlfiles="$dlfiles" - dlfiles= - for lib in $old_dlfiles; do - case " $dlprefiles $dlfiles " in - *" $lib "*) ;; - *) func_append dlfiles " $lib" ;; - esac - done - - # Make sure dlprefiles contains only unique files - old_dlprefiles="$dlprefiles" - dlprefiles= - for lib in $old_dlprefiles; do - case "$dlprefiles " in - *" $lib "*) ;; - *) func_append dlprefiles " $lib" ;; - esac - done - - if test "$build_libtool_libs" = yes; then - if test -n "$rpath"; then - case $host in - *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-*-beos* | *-cegcc* | *-*-haiku*) - # these systems don't actually have a c library (as such)! - ;; - *-*-rhapsody* | *-*-darwin1.[012]) - # Rhapsody C library is in the System framework - func_append deplibs " System.ltframework" - ;; - *-*-netbsd*) - # Don't link with libc until the a.out ld.so is fixed. - ;; - *-*-openbsd* | *-*-freebsd* | *-*-dragonfly*) - # Do not include libc due to us having libc/libc_r. - ;; - *-*-sco3.2v5* | *-*-sco5v6*) - # Causes problems with __ctype - ;; - *-*-sysv4.2uw2* | *-*-sysv5* | *-*-unixware* | *-*-OpenUNIX*) - # Compiler inserts libc in the correct place for threads to work - ;; - *) - # Add libc to deplibs on all other systems if necessary. - if test "$build_libtool_need_lc" = "yes"; then - func_append deplibs " -lc" - fi - ;; - esac - fi - - # Transform deplibs into only deplibs that can be linked in shared. - name_save=$name - libname_save=$libname - release_save=$release - versuffix_save=$versuffix - major_save=$major - # I'm not sure if I'm treating the release correctly. I think - # release should show up in the -l (ie -lgmp5) so we don't want to - # add it in twice. Is that correct? - release="" - versuffix="" - major="" - newdeplibs= - droppeddeps=no - case $deplibs_check_method in - pass_all) - # Don't check for shared/static. Everything works. - # This might be a little naive. We might want to check - # whether the library exists or not. But this is on - # osf3 & osf4 and I'm not really sure... Just - # implementing what was already the behavior. - newdeplibs=$deplibs - ;; - test_compile) - # This code stresses the "libraries are programs" paradigm to its - # limits. Maybe even breaks it. We compile a program, linking it - # against the deplibs as a proxy for the library. Then we can check - # whether they linked in statically or dynamically with ldd. - $opt_dry_run || $RM conftest.c - cat > conftest.c </dev/null` - $nocaseglob - else - potential_libs=`ls $i/$libnameglob[.-]* 2>/dev/null` - fi - for potent_lib in $potential_libs; do - # Follow soft links. - if ls -lLd "$potent_lib" 2>/dev/null | - $GREP " -> " >/dev/null; then - continue - fi - # The statement above tries to avoid entering an - # endless loop below, in case of cyclic links. - # We might still enter an endless loop, since a link - # loop can be closed while we follow links, - # but so what? - potlib="$potent_lib" - while test -h "$potlib" 2>/dev/null; do - potliblink=`ls -ld $potlib | ${SED} 's/.* -> //'` - case $potliblink in - [\\/]* | [A-Za-z]:[\\/]*) potlib="$potliblink";; - *) potlib=`$ECHO "$potlib" | $SED 's,[^/]*$,,'`"$potliblink";; - esac - done - if eval $file_magic_cmd \"\$potlib\" 2>/dev/null | - $SED -e 10q | - $EGREP "$file_magic_regex" > /dev/null; then - func_append newdeplibs " $a_deplib" - a_deplib="" - break 2 - fi - done - done - fi - if test -n "$a_deplib" ; then - droppeddeps=yes - echo - $ECHO "*** Warning: linker path does not have real file for library $a_deplib." - echo "*** I have the capability to make that library automatically link in when" - echo "*** you link to this library. But I can only do this if you have a" - echo "*** shared version of the library, which you do not appear to have" - echo "*** because I did check the linker path looking for a file starting" - if test -z "$potlib" ; then - $ECHO "*** with $libname but no candidates were found. (...for file magic test)" - else - $ECHO "*** with $libname and none of the candidates passed a file format test" - $ECHO "*** using a file magic. Last file checked: $potlib" - fi - fi - ;; - *) - # Add a -L argument. - func_append newdeplibs " $a_deplib" - ;; - esac - done # Gone through all deplibs. - ;; - match_pattern*) - set dummy $deplibs_check_method; shift - match_pattern_regex=`expr "$deplibs_check_method" : "$1 \(.*\)"` - for a_deplib in $deplibs; do - case $a_deplib in - -l*) - func_stripname -l '' "$a_deplib" - name=$func_stripname_result - if test "X$allow_libtool_libs_with_static_runtimes" = "Xyes" ; then - case " $predeps $postdeps " in - *" $a_deplib "*) - func_append newdeplibs " $a_deplib" - a_deplib="" - ;; - esac - fi - if test -n "$a_deplib" ; then - libname=`eval "\\$ECHO \"$libname_spec\""` - for i in $lib_search_path $sys_lib_search_path $shlib_search_path; do - potential_libs=`ls $i/$libname[.-]* 2>/dev/null` - for potent_lib in $potential_libs; do - potlib="$potent_lib" # see symlink-check above in file_magic test - if eval "\$ECHO \"$potent_lib\"" 2>/dev/null | $SED 10q | \ - $EGREP "$match_pattern_regex" > /dev/null; then - func_append newdeplibs " $a_deplib" - a_deplib="" - break 2 - fi - done - done - fi - if test -n "$a_deplib" ; then - droppeddeps=yes - echo - $ECHO "*** Warning: linker path does not have real file for library $a_deplib." - echo "*** I have the capability to make that library automatically link in when" - echo "*** you link to this library. But I can only do this if you have a" - echo "*** shared version of the library, which you do not appear to have" - echo "*** because I did check the linker path looking for a file starting" - if test -z "$potlib" ; then - $ECHO "*** with $libname but no candidates were found. (...for regex pattern test)" - else - $ECHO "*** with $libname and none of the candidates passed a file format test" - $ECHO "*** using a regex pattern. Last file checked: $potlib" - fi - fi - ;; - *) - # Add a -L argument. - func_append newdeplibs " $a_deplib" - ;; - esac - done # Gone through all deplibs. - ;; - none | unknown | *) - newdeplibs="" - tmp_deplibs=`$ECHO " $deplibs" | $SED 's/ -lc$//; s/ -[LR][^ ]*//g'` - if test "X$allow_libtool_libs_with_static_runtimes" = "Xyes" ; then - for i in $predeps $postdeps ; do - # can't use Xsed below, because $i might contain '/' - tmp_deplibs=`$ECHO " $tmp_deplibs" | $SED "s,$i,,"` - done - fi - case $tmp_deplibs in - *[!\ \ ]*) - echo - if test "X$deplibs_check_method" = "Xnone"; then - echo "*** Warning: inter-library dependencies are not supported in this platform." - else - echo "*** Warning: inter-library dependencies are not known to be supported." - fi - echo "*** All declared inter-library dependencies are being dropped." - droppeddeps=yes - ;; - esac - ;; - esac - versuffix=$versuffix_save - major=$major_save - release=$release_save - libname=$libname_save - name=$name_save - - case $host in - *-*-rhapsody* | *-*-darwin1.[012]) - # On Rhapsody replace the C library with the System framework - newdeplibs=`$ECHO " $newdeplibs" | $SED 's/ -lc / System.ltframework /'` - ;; - esac - - if test "$droppeddeps" = yes; then - if test "$module" = yes; then - echo - echo "*** Warning: libtool could not satisfy all declared inter-library" - $ECHO "*** dependencies of module $libname. Therefore, libtool will create" - echo "*** a static module, that should work as long as the dlopening" - echo "*** application is linked with the -dlopen flag." - if test -z "$global_symbol_pipe"; then - echo - echo "*** However, this would only work if libtool was able to extract symbol" - echo "*** lists from a program, using \`nm' or equivalent, but libtool could" - echo "*** not find such a program. So, this module is probably useless." - echo "*** \`nm' from GNU binutils and a full rebuild may help." - fi - if test "$build_old_libs" = no; then - oldlibs="$output_objdir/$libname.$libext" - build_libtool_libs=module - build_old_libs=yes - else - build_libtool_libs=no - fi - else - echo "*** The inter-library dependencies that have been dropped here will be" - echo "*** automatically added whenever a program is linked with this library" - echo "*** or is declared to -dlopen it." - - if test "$allow_undefined" = no; then - echo - echo "*** Since this library must not contain undefined symbols," - echo "*** because either the platform does not support them or" - echo "*** it was explicitly requested with -no-undefined," - echo "*** libtool will only create a static version of it." - if test "$build_old_libs" = no; then - oldlibs="$output_objdir/$libname.$libext" - build_libtool_libs=module - build_old_libs=yes - else - build_libtool_libs=no - fi - fi - fi - fi - # Done checking deplibs! - deplibs=$newdeplibs - fi - # Time to change all our "foo.ltframework" stuff back to "-framework foo" - case $host in - *-*-darwin*) - newdeplibs=`$ECHO " $newdeplibs" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'` - new_inherited_linker_flags=`$ECHO " $new_inherited_linker_flags" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'` - deplibs=`$ECHO " $deplibs" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'` - ;; - esac - - # move library search paths that coincide with paths to not yet - # installed libraries to the beginning of the library search list - new_libs= - for path in $notinst_path; do - case " $new_libs " in - *" -L$path/$objdir "*) ;; - *) - case " $deplibs " in - *" -L$path/$objdir "*) - func_append new_libs " -L$path/$objdir" ;; - esac - ;; - esac - done - for deplib in $deplibs; do - case $deplib in - -L*) - case " $new_libs " in - *" $deplib "*) ;; - *) func_append new_libs " $deplib" ;; - esac - ;; - *) func_append new_libs " $deplib" ;; - esac - done - deplibs="$new_libs" - - # All the library-specific variables (install_libdir is set above). - library_names= - old_library= - dlname= - - # Test again, we may have decided not to build it any more - if test "$build_libtool_libs" = yes; then - # Remove ${wl} instances when linking with ld. - # FIXME: should test the right _cmds variable. - case $archive_cmds in - *\$LD\ *) wl= ;; - esac - if test "$hardcode_into_libs" = yes; then - # Hardcode the library paths - hardcode_libdirs= - dep_rpath= - rpath="$finalize_rpath" - test "$opt_mode" != relink && rpath="$compile_rpath$rpath" - for libdir in $rpath; do - if test -n "$hardcode_libdir_flag_spec"; then - if test -n "$hardcode_libdir_separator"; then - func_replace_sysroot "$libdir" - libdir=$func_replace_sysroot_result - if test -z "$hardcode_libdirs"; then - hardcode_libdirs="$libdir" - else - # Just accumulate the unique libdirs. - case $hardcode_libdir_separator$hardcode_libdirs$hardcode_libdir_separator in - *"$hardcode_libdir_separator$libdir$hardcode_libdir_separator"*) - ;; - *) - func_append hardcode_libdirs "$hardcode_libdir_separator$libdir" - ;; - esac - fi - else - eval flag=\"$hardcode_libdir_flag_spec\" - func_append dep_rpath " $flag" - fi - elif test -n "$runpath_var"; then - case "$perm_rpath " in - *" $libdir "*) ;; - *) func_append perm_rpath " $libdir" ;; - esac - fi - done - # Substitute the hardcoded libdirs into the rpath. - if test -n "$hardcode_libdir_separator" && - test -n "$hardcode_libdirs"; then - libdir="$hardcode_libdirs" - eval "dep_rpath=\"$hardcode_libdir_flag_spec\"" - fi - if test -n "$runpath_var" && test -n "$perm_rpath"; then - # We should set the runpath_var. - rpath= - for dir in $perm_rpath; do - func_append rpath "$dir:" - done - eval "$runpath_var='$rpath\$$runpath_var'; export $runpath_var" - fi - test -n "$dep_rpath" && deplibs="$dep_rpath $deplibs" - fi - - shlibpath="$finalize_shlibpath" - test "$opt_mode" != relink && shlibpath="$compile_shlibpath$shlibpath" - if test -n "$shlibpath"; then - eval "$shlibpath_var='$shlibpath\$$shlibpath_var'; export $shlibpath_var" - fi - - # Get the real and link names of the library. - eval shared_ext=\"$shrext_cmds\" - eval library_names=\"$library_names_spec\" - set dummy $library_names - shift - realname="$1" - shift - - if test -n "$soname_spec"; then - eval soname=\"$soname_spec\" - else - soname="$realname" - fi - if test -z "$dlname"; then - dlname=$soname - fi - - lib="$output_objdir/$realname" - linknames= - for link - do - func_append linknames " $link" - done - - # Use standard objects if they are pic - test -z "$pic_flag" && libobjs=`$ECHO "$libobjs" | $SP2NL | $SED "$lo2o" | $NL2SP` - test "X$libobjs" = "X " && libobjs= - - delfiles= - if test -n "$export_symbols" && test -n "$include_expsyms"; then - $opt_dry_run || cp "$export_symbols" "$output_objdir/$libname.uexp" - export_symbols="$output_objdir/$libname.uexp" - func_append delfiles " $export_symbols" - fi - - orig_export_symbols= - case $host_os in - cygwin* | mingw* | cegcc*) - if test -n "$export_symbols" && test -z "$export_symbols_regex"; then - # exporting using user supplied symfile - if test "x`$SED 1q $export_symbols`" != xEXPORTS; then - # and it's NOT already a .def file. Must figure out - # which of the given symbols are data symbols and tag - # them as such. So, trigger use of export_symbols_cmds. - # export_symbols gets reassigned inside the "prepare - # the list of exported symbols" if statement, so the - # include_expsyms logic still works. - orig_export_symbols="$export_symbols" - export_symbols= - always_export_symbols=yes - fi - fi - ;; - esac - - # Prepare the list of exported symbols - if test -z "$export_symbols"; then - if test "$always_export_symbols" = yes || test -n "$export_symbols_regex"; then - func_verbose "generating symbol list for \`$libname.la'" - export_symbols="$output_objdir/$libname.exp" - $opt_dry_run || $RM $export_symbols - cmds=$export_symbols_cmds - save_ifs="$IFS"; IFS='~' - for cmd1 in $cmds; do - IFS="$save_ifs" - # Take the normal branch if the nm_file_list_spec branch - # doesn't work or if tool conversion is not needed. - case $nm_file_list_spec~$to_tool_file_cmd in - *~func_convert_file_noop | *~func_convert_file_msys_to_w32 | ~*) - try_normal_branch=yes - eval cmd=\"$cmd1\" - func_len " $cmd" - len=$func_len_result - ;; - *) - try_normal_branch=no - ;; - esac - if test "$try_normal_branch" = yes \ - && { test "$len" -lt "$max_cmd_len" \ - || test "$max_cmd_len" -le -1; } - then - func_show_eval "$cmd" 'exit $?' - skipped_export=false - elif test -n "$nm_file_list_spec"; then - func_basename "$output" - output_la=$func_basename_result - save_libobjs=$libobjs - save_output=$output - output=${output_objdir}/${output_la}.nm - func_to_tool_file "$output" - libobjs=$nm_file_list_spec$func_to_tool_file_result - func_append delfiles " $output" - func_verbose "creating $NM input file list: $output" - for obj in $save_libobjs; do - func_to_tool_file "$obj" - $ECHO "$func_to_tool_file_result" - done > "$output" - eval cmd=\"$cmd1\" - func_show_eval "$cmd" 'exit $?' - output=$save_output - libobjs=$save_libobjs - skipped_export=false - else - # The command line is too long to execute in one step. - func_verbose "using reloadable object file for export list..." - skipped_export=: - # Break out early, otherwise skipped_export may be - # set to false by a later but shorter cmd. - break - fi - done - IFS="$save_ifs" - if test -n "$export_symbols_regex" && test "X$skipped_export" != "X:"; then - func_show_eval '$EGREP -e "$export_symbols_regex" "$export_symbols" > "${export_symbols}T"' - func_show_eval '$MV "${export_symbols}T" "$export_symbols"' - fi - fi - fi - - if test -n "$export_symbols" && test -n "$include_expsyms"; then - tmp_export_symbols="$export_symbols" - test -n "$orig_export_symbols" && tmp_export_symbols="$orig_export_symbols" - $opt_dry_run || eval '$ECHO "$include_expsyms" | $SP2NL >> "$tmp_export_symbols"' - fi - - if test "X$skipped_export" != "X:" && test -n "$orig_export_symbols"; then - # The given exports_symbols file has to be filtered, so filter it. - func_verbose "filter symbol list for \`$libname.la' to tag DATA exports" - # FIXME: $output_objdir/$libname.filter potentially contains lots of - # 's' commands which not all seds can handle. GNU sed should be fine - # though. Also, the filter scales superlinearly with the number of - # global variables. join(1) would be nice here, but unfortunately - # isn't a blessed tool. - $opt_dry_run || $SED -e '/[ ,]DATA/!d;s,\(.*\)\([ \,].*\),s|^\1$|\1\2|,' < $export_symbols > $output_objdir/$libname.filter - func_append delfiles " $export_symbols $output_objdir/$libname.filter" - export_symbols=$output_objdir/$libname.def - $opt_dry_run || $SED -f $output_objdir/$libname.filter < $orig_export_symbols > $export_symbols - fi - - tmp_deplibs= - for test_deplib in $deplibs; do - case " $convenience " in - *" $test_deplib "*) ;; - *) - func_append tmp_deplibs " $test_deplib" - ;; - esac - done - deplibs="$tmp_deplibs" - - if test -n "$convenience"; then - if test -n "$whole_archive_flag_spec" && - test "$compiler_needs_object" = yes && - test -z "$libobjs"; then - # extract the archives, so we have objects to list. - # TODO: could optimize this to just extract one archive. - whole_archive_flag_spec= - fi - if test -n "$whole_archive_flag_spec"; then - save_libobjs=$libobjs - eval libobjs=\"\$libobjs $whole_archive_flag_spec\" - test "X$libobjs" = "X " && libobjs= - else - gentop="$output_objdir/${outputname}x" - func_append generated " $gentop" - - func_extract_archives $gentop $convenience - func_append libobjs " $func_extract_archives_result" - test "X$libobjs" = "X " && libobjs= - fi - fi - - if test "$thread_safe" = yes && test -n "$thread_safe_flag_spec"; then - eval flag=\"$thread_safe_flag_spec\" - func_append linker_flags " $flag" - fi - - # Make a backup of the uninstalled library when relinking - if test "$opt_mode" = relink; then - $opt_dry_run || eval '(cd $output_objdir && $RM ${realname}U && $MV $realname ${realname}U)' || exit $? - fi - - # Do each of the archive commands. - if test "$module" = yes && test -n "$module_cmds" ; then - if test -n "$export_symbols" && test -n "$module_expsym_cmds"; then - eval test_cmds=\"$module_expsym_cmds\" - cmds=$module_expsym_cmds - else - eval test_cmds=\"$module_cmds\" - cmds=$module_cmds - fi - else - if test -n "$export_symbols" && test -n "$archive_expsym_cmds"; then - eval test_cmds=\"$archive_expsym_cmds\" - cmds=$archive_expsym_cmds - else - eval test_cmds=\"$archive_cmds\" - cmds=$archive_cmds - fi - fi - - if test "X$skipped_export" != "X:" && - func_len " $test_cmds" && - len=$func_len_result && - test "$len" -lt "$max_cmd_len" || test "$max_cmd_len" -le -1; then - : - else - # The command line is too long to link in one step, link piecewise - # or, if using GNU ld and skipped_export is not :, use a linker - # script. - - # Save the value of $output and $libobjs because we want to - # use them later. If we have whole_archive_flag_spec, we - # want to use save_libobjs as it was before - # whole_archive_flag_spec was expanded, because we can't - # assume the linker understands whole_archive_flag_spec. - # This may have to be revisited, in case too many - # convenience libraries get linked in and end up exceeding - # the spec. - if test -z "$convenience" || test -z "$whole_archive_flag_spec"; then - save_libobjs=$libobjs - fi - save_output=$output - func_basename "$output" - output_la=$func_basename_result - - # Clear the reloadable object creation command queue and - # initialize k to one. - test_cmds= - concat_cmds= - objlist= - last_robj= - k=1 - - if test -n "$save_libobjs" && test "X$skipped_export" != "X:" && test "$with_gnu_ld" = yes; then - output=${output_objdir}/${output_la}.lnkscript - func_verbose "creating GNU ld script: $output" - echo 'INPUT (' > $output - for obj in $save_libobjs - do - func_to_tool_file "$obj" - $ECHO "$func_to_tool_file_result" >> $output - done - echo ')' >> $output - func_append delfiles " $output" - func_to_tool_file "$output" - output=$func_to_tool_file_result - elif test -n "$save_libobjs" && test "X$skipped_export" != "X:" && test "X$file_list_spec" != X; then - output=${output_objdir}/${output_la}.lnk - func_verbose "creating linker input file list: $output" - : > $output - set x $save_libobjs - shift - firstobj= - if test "$compiler_needs_object" = yes; then - firstobj="$1 " - shift - fi - for obj - do - func_to_tool_file "$obj" - $ECHO "$func_to_tool_file_result" >> $output - done - func_append delfiles " $output" - func_to_tool_file "$output" - output=$firstobj\"$file_list_spec$func_to_tool_file_result\" - else - if test -n "$save_libobjs"; then - func_verbose "creating reloadable object files..." - output=$output_objdir/$output_la-${k}.$objext - eval test_cmds=\"$reload_cmds\" - func_len " $test_cmds" - len0=$func_len_result - len=$len0 - - # Loop over the list of objects to be linked. - for obj in $save_libobjs - do - func_len " $obj" - func_arith $len + $func_len_result - len=$func_arith_result - if test "X$objlist" = X || - test "$len" -lt "$max_cmd_len"; then - func_append objlist " $obj" - else - # The command $test_cmds is almost too long, add a - # command to the queue. - if test "$k" -eq 1 ; then - # The first file doesn't have a previous command to add. - reload_objs=$objlist - eval concat_cmds=\"$reload_cmds\" - else - # All subsequent reloadable object files will link in - # the last one created. - reload_objs="$objlist $last_robj" - eval concat_cmds=\"\$concat_cmds~$reload_cmds~\$RM $last_robj\" - fi - last_robj=$output_objdir/$output_la-${k}.$objext - func_arith $k + 1 - k=$func_arith_result - output=$output_objdir/$output_la-${k}.$objext - objlist=" $obj" - func_len " $last_robj" - func_arith $len0 + $func_len_result - len=$func_arith_result - fi - done - # Handle the remaining objects by creating one last - # reloadable object file. All subsequent reloadable object - # files will link in the last one created. - test -z "$concat_cmds" || concat_cmds=$concat_cmds~ - reload_objs="$objlist $last_robj" - eval concat_cmds=\"\${concat_cmds}$reload_cmds\" - if test -n "$last_robj"; then - eval concat_cmds=\"\${concat_cmds}~\$RM $last_robj\" - fi - func_append delfiles " $output" - - else - output= - fi - - if ${skipped_export-false}; then - func_verbose "generating symbol list for \`$libname.la'" - export_symbols="$output_objdir/$libname.exp" - $opt_dry_run || $RM $export_symbols - libobjs=$output - # Append the command to create the export file. - test -z "$concat_cmds" || concat_cmds=$concat_cmds~ - eval concat_cmds=\"\$concat_cmds$export_symbols_cmds\" - if test -n "$last_robj"; then - eval concat_cmds=\"\$concat_cmds~\$RM $last_robj\" - fi - fi - - test -n "$save_libobjs" && - func_verbose "creating a temporary reloadable object file: $output" - - # Loop through the commands generated above and execute them. - save_ifs="$IFS"; IFS='~' - for cmd in $concat_cmds; do - IFS="$save_ifs" - $opt_silent || { - func_quote_for_expand "$cmd" - eval "func_echo $func_quote_for_expand_result" - } - $opt_dry_run || eval "$cmd" || { - lt_exit=$? - - # Restore the uninstalled library and exit - if test "$opt_mode" = relink; then - ( cd "$output_objdir" && \ - $RM "${realname}T" && \ - $MV "${realname}U" "$realname" ) - fi - - exit $lt_exit - } - done - IFS="$save_ifs" - - if test -n "$export_symbols_regex" && ${skipped_export-false}; then - func_show_eval '$EGREP -e "$export_symbols_regex" "$export_symbols" > "${export_symbols}T"' - func_show_eval '$MV "${export_symbols}T" "$export_symbols"' - fi - fi - - if ${skipped_export-false}; then - if test -n "$export_symbols" && test -n "$include_expsyms"; then - tmp_export_symbols="$export_symbols" - test -n "$orig_export_symbols" && tmp_export_symbols="$orig_export_symbols" - $opt_dry_run || eval '$ECHO "$include_expsyms" | $SP2NL >> "$tmp_export_symbols"' - fi - - if test -n "$orig_export_symbols"; then - # The given exports_symbols file has to be filtered, so filter it. - func_verbose "filter symbol list for \`$libname.la' to tag DATA exports" - # FIXME: $output_objdir/$libname.filter potentially contains lots of - # 's' commands which not all seds can handle. GNU sed should be fine - # though. Also, the filter scales superlinearly with the number of - # global variables. join(1) would be nice here, but unfortunately - # isn't a blessed tool. - $opt_dry_run || $SED -e '/[ ,]DATA/!d;s,\(.*\)\([ \,].*\),s|^\1$|\1\2|,' < $export_symbols > $output_objdir/$libname.filter - func_append delfiles " $export_symbols $output_objdir/$libname.filter" - export_symbols=$output_objdir/$libname.def - $opt_dry_run || $SED -f $output_objdir/$libname.filter < $orig_export_symbols > $export_symbols - fi - fi - - libobjs=$output - # Restore the value of output. - output=$save_output - - if test -n "$convenience" && test -n "$whole_archive_flag_spec"; then - eval libobjs=\"\$libobjs $whole_archive_flag_spec\" - test "X$libobjs" = "X " && libobjs= - fi - # Expand the library linking commands again to reset the - # value of $libobjs for piecewise linking. - - # Do each of the archive commands. - if test "$module" = yes && test -n "$module_cmds" ; then - if test -n "$export_symbols" && test -n "$module_expsym_cmds"; then - cmds=$module_expsym_cmds - else - cmds=$module_cmds - fi - else - if test -n "$export_symbols" && test -n "$archive_expsym_cmds"; then - cmds=$archive_expsym_cmds - else - cmds=$archive_cmds - fi - fi - fi - - if test -n "$delfiles"; then - # Append the command to remove temporary files to $cmds. - eval cmds=\"\$cmds~\$RM $delfiles\" - fi - - # Add any objects from preloaded convenience libraries - if test -n "$dlprefiles"; then - gentop="$output_objdir/${outputname}x" - func_append generated " $gentop" - - func_extract_archives $gentop $dlprefiles - func_append libobjs " $func_extract_archives_result" - test "X$libobjs" = "X " && libobjs= - fi - - save_ifs="$IFS"; IFS='~' - for cmd in $cmds; do - IFS="$save_ifs" - eval cmd=\"$cmd\" - $opt_silent || { - func_quote_for_expand "$cmd" - eval "func_echo $func_quote_for_expand_result" - } - $opt_dry_run || eval "$cmd" || { - lt_exit=$? - - # Restore the uninstalled library and exit - if test "$opt_mode" = relink; then - ( cd "$output_objdir" && \ - $RM "${realname}T" && \ - $MV "${realname}U" "$realname" ) - fi - - exit $lt_exit - } - done - IFS="$save_ifs" - - # Restore the uninstalled library and exit - if test "$opt_mode" = relink; then - $opt_dry_run || eval '(cd $output_objdir && $RM ${realname}T && $MV $realname ${realname}T && $MV ${realname}U $realname)' || exit $? - - if test -n "$convenience"; then - if test -z "$whole_archive_flag_spec"; then - func_show_eval '${RM}r "$gentop"' - fi - fi - - exit $EXIT_SUCCESS - fi - - # Create links to the real library. - for linkname in $linknames; do - if test "$realname" != "$linkname"; then - func_show_eval '(cd "$output_objdir" && $RM "$linkname" && $LN_S "$realname" "$linkname")' 'exit $?' - fi - done - - # If -module or -export-dynamic was specified, set the dlname. - if test "$module" = yes || test "$export_dynamic" = yes; then - # On all known operating systems, these are identical. - dlname="$soname" - fi - fi - ;; - - obj) - if test -n "$dlfiles$dlprefiles" || test "$dlself" != no; then - func_warning "\`-dlopen' is ignored for objects" - fi - - case " $deplibs" in - *\ -l* | *\ -L*) - func_warning "\`-l' and \`-L' are ignored for objects" ;; - esac - - test -n "$rpath" && \ - func_warning "\`-rpath' is ignored for objects" - - test -n "$xrpath" && \ - func_warning "\`-R' is ignored for objects" - - test -n "$vinfo" && \ - func_warning "\`-version-info' is ignored for objects" - - test -n "$release" && \ - func_warning "\`-release' is ignored for objects" - - case $output in - *.lo) - test -n "$objs$old_deplibs" && \ - func_fatal_error "cannot build library object \`$output' from non-libtool objects" - - libobj=$output - func_lo2o "$libobj" - obj=$func_lo2o_result - ;; - *) - libobj= - obj="$output" - ;; - esac - - # Delete the old objects. - $opt_dry_run || $RM $obj $libobj - - # Objects from convenience libraries. This assumes - # single-version convenience libraries. Whenever we create - # different ones for PIC/non-PIC, this we'll have to duplicate - # the extraction. - reload_conv_objs= - gentop= - # reload_cmds runs $LD directly, so let us get rid of - # -Wl from whole_archive_flag_spec and hope we can get by with - # turning comma into space.. - wl= - - if test -n "$convenience"; then - if test -n "$whole_archive_flag_spec"; then - eval tmp_whole_archive_flags=\"$whole_archive_flag_spec\" - reload_conv_objs=$reload_objs\ `$ECHO "$tmp_whole_archive_flags" | $SED 's|,| |g'` - else - gentop="$output_objdir/${obj}x" - func_append generated " $gentop" - - func_extract_archives $gentop $convenience - reload_conv_objs="$reload_objs $func_extract_archives_result" - fi - fi - - # If we're not building shared, we need to use non_pic_objs - test "$build_libtool_libs" != yes && libobjs="$non_pic_objects" - - # Create the old-style object. - reload_objs="$objs$old_deplibs "`$ECHO "$libobjs" | $SP2NL | $SED "/\.${libext}$/d; /\.lib$/d; $lo2o" | $NL2SP`" $reload_conv_objs" ### testsuite: skip nested quoting test - - output="$obj" - func_execute_cmds "$reload_cmds" 'exit $?' - - # Exit if we aren't doing a library object file. - if test -z "$libobj"; then - if test -n "$gentop"; then - func_show_eval '${RM}r "$gentop"' - fi - - exit $EXIT_SUCCESS - fi - - if test "$build_libtool_libs" != yes; then - if test -n "$gentop"; then - func_show_eval '${RM}r "$gentop"' - fi - - # Create an invalid libtool object if no PIC, so that we don't - # accidentally link it into a program. - # $show "echo timestamp > $libobj" - # $opt_dry_run || eval "echo timestamp > $libobj" || exit $? - exit $EXIT_SUCCESS - fi - - if test -n "$pic_flag" || test "$pic_mode" != default; then - # Only do commands if we really have different PIC objects. - reload_objs="$libobjs $reload_conv_objs" - output="$libobj" - func_execute_cmds "$reload_cmds" 'exit $?' - fi - - if test -n "$gentop"; then - func_show_eval '${RM}r "$gentop"' - fi - - exit $EXIT_SUCCESS - ;; - - prog) - case $host in - *cygwin*) func_stripname '' '.exe' "$output" - output=$func_stripname_result.exe;; - esac - test -n "$vinfo" && \ - func_warning "\`-version-info' is ignored for programs" - - test -n "$release" && \ - func_warning "\`-release' is ignored for programs" - - test "$preload" = yes \ - && test "$dlopen_support" = unknown \ - && test "$dlopen_self" = unknown \ - && test "$dlopen_self_static" = unknown && \ - func_warning "\`LT_INIT([dlopen])' not used. Assuming no dlopen support." - - case $host in - *-*-rhapsody* | *-*-darwin1.[012]) - # On Rhapsody replace the C library is the System framework - compile_deplibs=`$ECHO " $compile_deplibs" | $SED 's/ -lc / System.ltframework /'` - finalize_deplibs=`$ECHO " $finalize_deplibs" | $SED 's/ -lc / System.ltframework /'` - ;; - esac - - case $host in - *-*-darwin*) - # Don't allow lazy linking, it breaks C++ global constructors - # But is supposedly fixed on 10.4 or later (yay!). - if test "$tagname" = CXX ; then - case ${MACOSX_DEPLOYMENT_TARGET-10.0} in - 10.[0123]) - func_append compile_command " ${wl}-bind_at_load" - func_append finalize_command " ${wl}-bind_at_load" - ;; - esac - fi - # Time to change all our "foo.ltframework" stuff back to "-framework foo" - compile_deplibs=`$ECHO " $compile_deplibs" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'` - finalize_deplibs=`$ECHO " $finalize_deplibs" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'` - ;; - esac - - - # move library search paths that coincide with paths to not yet - # installed libraries to the beginning of the library search list - new_libs= - for path in $notinst_path; do - case " $new_libs " in - *" -L$path/$objdir "*) ;; - *) - case " $compile_deplibs " in - *" -L$path/$objdir "*) - func_append new_libs " -L$path/$objdir" ;; - esac - ;; - esac - done - for deplib in $compile_deplibs; do - case $deplib in - -L*) - case " $new_libs " in - *" $deplib "*) ;; - *) func_append new_libs " $deplib" ;; - esac - ;; - *) func_append new_libs " $deplib" ;; - esac - done - compile_deplibs="$new_libs" - - - func_append compile_command " $compile_deplibs" - func_append finalize_command " $finalize_deplibs" - - if test -n "$rpath$xrpath"; then - # If the user specified any rpath flags, then add them. - for libdir in $rpath $xrpath; do - # This is the magic to use -rpath. - case "$finalize_rpath " in - *" $libdir "*) ;; - *) func_append finalize_rpath " $libdir" ;; - esac - done - fi - - # Now hardcode the library paths - rpath= - hardcode_libdirs= - for libdir in $compile_rpath $finalize_rpath; do - if test -n "$hardcode_libdir_flag_spec"; then - if test -n "$hardcode_libdir_separator"; then - if test -z "$hardcode_libdirs"; then - hardcode_libdirs="$libdir" - else - # Just accumulate the unique libdirs. - case $hardcode_libdir_separator$hardcode_libdirs$hardcode_libdir_separator in - *"$hardcode_libdir_separator$libdir$hardcode_libdir_separator"*) - ;; - *) - func_append hardcode_libdirs "$hardcode_libdir_separator$libdir" - ;; - esac - fi - else - eval flag=\"$hardcode_libdir_flag_spec\" - func_append rpath " $flag" - fi - elif test -n "$runpath_var"; then - case "$perm_rpath " in - *" $libdir "*) ;; - *) func_append perm_rpath " $libdir" ;; - esac - fi - case $host in - *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-cegcc*) - testbindir=`${ECHO} "$libdir" | ${SED} -e 's*/lib$*/bin*'` - case :$dllsearchpath: in - *":$libdir:"*) ;; - ::) dllsearchpath=$libdir;; - *) func_append dllsearchpath ":$libdir";; - esac - case :$dllsearchpath: in - *":$testbindir:"*) ;; - ::) dllsearchpath=$testbindir;; - *) func_append dllsearchpath ":$testbindir";; - esac - ;; - esac - done - # Substitute the hardcoded libdirs into the rpath. - if test -n "$hardcode_libdir_separator" && - test -n "$hardcode_libdirs"; then - libdir="$hardcode_libdirs" - eval rpath=\" $hardcode_libdir_flag_spec\" - fi - compile_rpath="$rpath" - - rpath= - hardcode_libdirs= - for libdir in $finalize_rpath; do - if test -n "$hardcode_libdir_flag_spec"; then - if test -n "$hardcode_libdir_separator"; then - if test -z "$hardcode_libdirs"; then - hardcode_libdirs="$libdir" - else - # Just accumulate the unique libdirs. - case $hardcode_libdir_separator$hardcode_libdirs$hardcode_libdir_separator in - *"$hardcode_libdir_separator$libdir$hardcode_libdir_separator"*) - ;; - *) - func_append hardcode_libdirs "$hardcode_libdir_separator$libdir" - ;; - esac - fi - else - eval flag=\"$hardcode_libdir_flag_spec\" - func_append rpath " $flag" - fi - elif test -n "$runpath_var"; then - case "$finalize_perm_rpath " in - *" $libdir "*) ;; - *) func_append finalize_perm_rpath " $libdir" ;; - esac - fi - done - # Substitute the hardcoded libdirs into the rpath. - if test -n "$hardcode_libdir_separator" && - test -n "$hardcode_libdirs"; then - libdir="$hardcode_libdirs" - eval rpath=\" $hardcode_libdir_flag_spec\" - fi - finalize_rpath="$rpath" - - if test -n "$libobjs" && test "$build_old_libs" = yes; then - # Transform all the library objects into standard objects. - compile_command=`$ECHO "$compile_command" | $SP2NL | $SED "$lo2o" | $NL2SP` - finalize_command=`$ECHO "$finalize_command" | $SP2NL | $SED "$lo2o" | $NL2SP` - fi - - func_generate_dlsyms "$outputname" "@PROGRAM@" "no" - - # template prelinking step - if test -n "$prelink_cmds"; then - func_execute_cmds "$prelink_cmds" 'exit $?' - fi - - wrappers_required=yes - case $host in - *cegcc* | *mingw32ce*) - # Disable wrappers for cegcc and mingw32ce hosts, we are cross compiling anyway. - wrappers_required=no - ;; - *cygwin* | *mingw* ) - if test "$build_libtool_libs" != yes; then - wrappers_required=no - fi - ;; - *) - if test "$need_relink" = no || test "$build_libtool_libs" != yes; then - wrappers_required=no - fi - ;; - esac - if test "$wrappers_required" = no; then - # Replace the output file specification. - compile_command=`$ECHO "$compile_command" | $SED 's%@OUTPUT@%'"$output"'%g'` - link_command="$compile_command$compile_rpath" - - # We have no uninstalled library dependencies, so finalize right now. - exit_status=0 - func_show_eval "$link_command" 'exit_status=$?' - - if test -n "$postlink_cmds"; then - func_to_tool_file "$output" - postlink_cmds=`func_echo_all "$postlink_cmds" | $SED -e 's%@OUTPUT@%'"$output"'%g' -e 's%@TOOL_OUTPUT@%'"$func_to_tool_file_result"'%g'` - func_execute_cmds "$postlink_cmds" 'exit $?' - fi - - # Delete the generated files. - if test -f "$output_objdir/${outputname}S.${objext}"; then - func_show_eval '$RM "$output_objdir/${outputname}S.${objext}"' - fi - - exit $exit_status - fi - - if test -n "$compile_shlibpath$finalize_shlibpath"; then - compile_command="$shlibpath_var=\"$compile_shlibpath$finalize_shlibpath\$$shlibpath_var\" $compile_command" - fi - if test -n "$finalize_shlibpath"; then - finalize_command="$shlibpath_var=\"$finalize_shlibpath\$$shlibpath_var\" $finalize_command" - fi - - compile_var= - finalize_var= - if test -n "$runpath_var"; then - if test -n "$perm_rpath"; then - # We should set the runpath_var. - rpath= - for dir in $perm_rpath; do - func_append rpath "$dir:" - done - compile_var="$runpath_var=\"$rpath\$$runpath_var\" " - fi - if test -n "$finalize_perm_rpath"; then - # We should set the runpath_var. - rpath= - for dir in $finalize_perm_rpath; do - func_append rpath "$dir:" - done - finalize_var="$runpath_var=\"$rpath\$$runpath_var\" " - fi - fi - - if test "$no_install" = yes; then - # We don't need to create a wrapper script. - link_command="$compile_var$compile_command$compile_rpath" - # Replace the output file specification. - link_command=`$ECHO "$link_command" | $SED 's%@OUTPUT@%'"$output"'%g'` - # Delete the old output file. - $opt_dry_run || $RM $output - # Link the executable and exit - func_show_eval "$link_command" 'exit $?' - - if test -n "$postlink_cmds"; then - func_to_tool_file "$output" - postlink_cmds=`func_echo_all "$postlink_cmds" | $SED -e 's%@OUTPUT@%'"$output"'%g' -e 's%@TOOL_OUTPUT@%'"$func_to_tool_file_result"'%g'` - func_execute_cmds "$postlink_cmds" 'exit $?' - fi - - exit $EXIT_SUCCESS - fi - - if test "$hardcode_action" = relink; then - # Fast installation is not supported - link_command="$compile_var$compile_command$compile_rpath" - relink_command="$finalize_var$finalize_command$finalize_rpath" - - func_warning "this platform does not like uninstalled shared libraries" - func_warning "\`$output' will be relinked during installation" - else - if test "$fast_install" != no; then - link_command="$finalize_var$compile_command$finalize_rpath" - if test "$fast_install" = yes; then - relink_command=`$ECHO "$compile_var$compile_command$compile_rpath" | $SED 's%@OUTPUT@%\$progdir/\$file%g'` - else - # fast_install is set to needless - relink_command= - fi - else - link_command="$compile_var$compile_command$compile_rpath" - relink_command="$finalize_var$finalize_command$finalize_rpath" - fi - fi - - # Replace the output file specification. - link_command=`$ECHO "$link_command" | $SED 's%@OUTPUT@%'"$output_objdir/$outputname"'%g'` - - # Delete the old output files. - $opt_dry_run || $RM $output $output_objdir/$outputname $output_objdir/lt-$outputname - - func_show_eval "$link_command" 'exit $?' - - if test -n "$postlink_cmds"; then - func_to_tool_file "$output_objdir/$outputname" - postlink_cmds=`func_echo_all "$postlink_cmds" | $SED -e 's%@OUTPUT@%'"$output_objdir/$outputname"'%g' -e 's%@TOOL_OUTPUT@%'"$func_to_tool_file_result"'%g'` - func_execute_cmds "$postlink_cmds" 'exit $?' - fi - - # Now create the wrapper script. - func_verbose "creating $output" - - # Quote the relink command for shipping. - if test -n "$relink_command"; then - # Preserve any variables that may affect compiler behavior - for var in $variables_saved_for_relink; do - if eval test -z \"\${$var+set}\"; then - relink_command="{ test -z \"\${$var+set}\" || $lt_unset $var || { $var=; export $var; }; }; $relink_command" - elif eval var_value=\$$var; test -z "$var_value"; then - relink_command="$var=; export $var; $relink_command" - else - func_quote_for_eval "$var_value" - relink_command="$var=$func_quote_for_eval_result; export $var; $relink_command" - fi - done - relink_command="(cd `pwd`; $relink_command)" - relink_command=`$ECHO "$relink_command" | $SED "$sed_quote_subst"` - fi - - # Only actually do things if not in dry run mode. - $opt_dry_run || { - # win32 will think the script is a binary if it has - # a .exe suffix, so we strip it off here. - case $output in - *.exe) func_stripname '' '.exe' "$output" - output=$func_stripname_result ;; - esac - # test for cygwin because mv fails w/o .exe extensions - case $host in - *cygwin*) - exeext=.exe - func_stripname '' '.exe' "$outputname" - outputname=$func_stripname_result ;; - *) exeext= ;; - esac - case $host in - *cygwin* | *mingw* ) - func_dirname_and_basename "$output" "" "." - output_name=$func_basename_result - output_path=$func_dirname_result - cwrappersource="$output_path/$objdir/lt-$output_name.c" - cwrapper="$output_path/$output_name.exe" - $RM $cwrappersource $cwrapper - trap "$RM $cwrappersource $cwrapper; exit $EXIT_FAILURE" 1 2 15 - - func_emit_cwrapperexe_src > $cwrappersource - - # The wrapper executable is built using the $host compiler, - # because it contains $host paths and files. If cross- - # compiling, it, like the target executable, must be - # executed on the $host or under an emulation environment. - $opt_dry_run || { - $LTCC $LTCFLAGS -o $cwrapper $cwrappersource - $STRIP $cwrapper - } - - # Now, create the wrapper script for func_source use: - func_ltwrapper_scriptname $cwrapper - $RM $func_ltwrapper_scriptname_result - trap "$RM $func_ltwrapper_scriptname_result; exit $EXIT_FAILURE" 1 2 15 - $opt_dry_run || { - # note: this script will not be executed, so do not chmod. - if test "x$build" = "x$host" ; then - $cwrapper --lt-dump-script > $func_ltwrapper_scriptname_result - else - func_emit_wrapper no > $func_ltwrapper_scriptname_result - fi - } - ;; - * ) - $RM $output - trap "$RM $output; exit $EXIT_FAILURE" 1 2 15 - - func_emit_wrapper no > $output - chmod +x $output - ;; - esac - } - exit $EXIT_SUCCESS - ;; - esac - - # See if we need to build an old-fashioned archive. - for oldlib in $oldlibs; do - - if test "$build_libtool_libs" = convenience; then - oldobjs="$libobjs_save $symfileobj" - addlibs="$convenience" - build_libtool_libs=no - else - if test "$build_libtool_libs" = module; then - oldobjs="$libobjs_save" - build_libtool_libs=no - else - oldobjs="$old_deplibs $non_pic_objects" - if test "$preload" = yes && test -f "$symfileobj"; then - func_append oldobjs " $symfileobj" - fi - fi - addlibs="$old_convenience" - fi - - if test -n "$addlibs"; then - gentop="$output_objdir/${outputname}x" - func_append generated " $gentop" - - func_extract_archives $gentop $addlibs - func_append oldobjs " $func_extract_archives_result" - fi - - # Do each command in the archive commands. - if test -n "$old_archive_from_new_cmds" && test "$build_libtool_libs" = yes; then - cmds=$old_archive_from_new_cmds - else - - # Add any objects from preloaded convenience libraries - if test -n "$dlprefiles"; then - gentop="$output_objdir/${outputname}x" - func_append generated " $gentop" - - func_extract_archives $gentop $dlprefiles - func_append oldobjs " $func_extract_archives_result" - fi - - # POSIX demands no paths to be encoded in archives. We have - # to avoid creating archives with duplicate basenames if we - # might have to extract them afterwards, e.g., when creating a - # static archive out of a convenience library, or when linking - # the entirety of a libtool archive into another (currently - # not supported by libtool). - if (for obj in $oldobjs - do - func_basename "$obj" - $ECHO "$func_basename_result" - done | sort | sort -uc >/dev/null 2>&1); then - : - else - echo "copying selected object files to avoid basename conflicts..." - gentop="$output_objdir/${outputname}x" - func_append generated " $gentop" - func_mkdir_p "$gentop" - save_oldobjs=$oldobjs - oldobjs= - counter=1 - for obj in $save_oldobjs - do - func_basename "$obj" - objbase="$func_basename_result" - case " $oldobjs " in - " ") oldobjs=$obj ;; - *[\ /]"$objbase "*) - while :; do - # Make sure we don't pick an alternate name that also - # overlaps. - newobj=lt$counter-$objbase - func_arith $counter + 1 - counter=$func_arith_result - case " $oldobjs " in - *[\ /]"$newobj "*) ;; - *) if test ! -f "$gentop/$newobj"; then break; fi ;; - esac - done - func_show_eval "ln $obj $gentop/$newobj || cp $obj $gentop/$newobj" - func_append oldobjs " $gentop/$newobj" - ;; - *) func_append oldobjs " $obj" ;; - esac - done - fi - func_to_tool_file "$oldlib" func_convert_file_msys_to_w32 - tool_oldlib=$func_to_tool_file_result - eval cmds=\"$old_archive_cmds\" - - func_len " $cmds" - len=$func_len_result - if test "$len" -lt "$max_cmd_len" || test "$max_cmd_len" -le -1; then - cmds=$old_archive_cmds - elif test -n "$archiver_list_spec"; then - func_verbose "using command file archive linking..." - for obj in $oldobjs - do - func_to_tool_file "$obj" - $ECHO "$func_to_tool_file_result" - done > $output_objdir/$libname.libcmd - func_to_tool_file "$output_objdir/$libname.libcmd" - oldobjs=" $archiver_list_spec$func_to_tool_file_result" - cmds=$old_archive_cmds - else - # the command line is too long to link in one step, link in parts - func_verbose "using piecewise archive linking..." - save_RANLIB=$RANLIB - RANLIB=: - objlist= - concat_cmds= - save_oldobjs=$oldobjs - oldobjs= - # Is there a better way of finding the last object in the list? - for obj in $save_oldobjs - do - last_oldobj=$obj - done - eval test_cmds=\"$old_archive_cmds\" - func_len " $test_cmds" - len0=$func_len_result - len=$len0 - for obj in $save_oldobjs - do - func_len " $obj" - func_arith $len + $func_len_result - len=$func_arith_result - func_append objlist " $obj" - if test "$len" -lt "$max_cmd_len"; then - : - else - # the above command should be used before it gets too long - oldobjs=$objlist - if test "$obj" = "$last_oldobj" ; then - RANLIB=$save_RANLIB - fi - test -z "$concat_cmds" || concat_cmds=$concat_cmds~ - eval concat_cmds=\"\${concat_cmds}$old_archive_cmds\" - objlist= - len=$len0 - fi - done - RANLIB=$save_RANLIB - oldobjs=$objlist - if test "X$oldobjs" = "X" ; then - eval cmds=\"\$concat_cmds\" - else - eval cmds=\"\$concat_cmds~\$old_archive_cmds\" - fi - fi - fi - func_execute_cmds "$cmds" 'exit $?' - done - - test -n "$generated" && \ - func_show_eval "${RM}r$generated" - - # Now create the libtool archive. - case $output in - *.la) - old_library= - test "$build_old_libs" = yes && old_library="$libname.$libext" - func_verbose "creating $output" - - # Preserve any variables that may affect compiler behavior - for var in $variables_saved_for_relink; do - if eval test -z \"\${$var+set}\"; then - relink_command="{ test -z \"\${$var+set}\" || $lt_unset $var || { $var=; export $var; }; }; $relink_command" - elif eval var_value=\$$var; test -z "$var_value"; then - relink_command="$var=; export $var; $relink_command" - else - func_quote_for_eval "$var_value" - relink_command="$var=$func_quote_for_eval_result; export $var; $relink_command" - fi - done - # Quote the link command for shipping. - relink_command="(cd `pwd`; $SHELL $progpath $preserve_args --mode=relink $libtool_args @inst_prefix_dir@)" - relink_command=`$ECHO "$relink_command" | $SED "$sed_quote_subst"` - if test "$hardcode_automatic" = yes ; then - relink_command= - fi - - # Only create the output if not a dry run. - $opt_dry_run || { - for installed in no yes; do - if test "$installed" = yes; then - if test -z "$install_libdir"; then - break - fi - output="$output_objdir/$outputname"i - # Replace all uninstalled libtool libraries with the installed ones - newdependency_libs= - for deplib in $dependency_libs; do - case $deplib in - *.la) - func_basename "$deplib" - name="$func_basename_result" - func_resolve_sysroot "$deplib" - eval libdir=`${SED} -n -e 's/^libdir=\(.*\)$/\1/p' $func_resolve_sysroot_result` - test -z "$libdir" && \ - func_fatal_error "\`$deplib' is not a valid libtool archive" - func_append newdependency_libs " ${lt_sysroot:+=}$libdir/$name" - ;; - -L*) - func_stripname -L '' "$deplib" - func_replace_sysroot "$func_stripname_result" - func_append newdependency_libs " -L$func_replace_sysroot_result" - ;; - -R*) - func_stripname -R '' "$deplib" - func_replace_sysroot "$func_stripname_result" - func_append newdependency_libs " -R$func_replace_sysroot_result" - ;; - *) func_append newdependency_libs " $deplib" ;; - esac - done - dependency_libs="$newdependency_libs" - newdlfiles= - - for lib in $dlfiles; do - case $lib in - *.la) - func_basename "$lib" - name="$func_basename_result" - eval libdir=`${SED} -n -e 's/^libdir=\(.*\)$/\1/p' $lib` - test -z "$libdir" && \ - func_fatal_error "\`$lib' is not a valid libtool archive" - func_append newdlfiles " ${lt_sysroot:+=}$libdir/$name" - ;; - *) func_append newdlfiles " $lib" ;; - esac - done - dlfiles="$newdlfiles" - newdlprefiles= - for lib in $dlprefiles; do - case $lib in - *.la) - # Only pass preopened files to the pseudo-archive (for - # eventual linking with the app. that links it) if we - # didn't already link the preopened objects directly into - # the library: - func_basename "$lib" - name="$func_basename_result" - eval libdir=`${SED} -n -e 's/^libdir=\(.*\)$/\1/p' $lib` - test -z "$libdir" && \ - func_fatal_error "\`$lib' is not a valid libtool archive" - func_append newdlprefiles " ${lt_sysroot:+=}$libdir/$name" - ;; - esac - done - dlprefiles="$newdlprefiles" - else - newdlfiles= - for lib in $dlfiles; do - case $lib in - [\\/]* | [A-Za-z]:[\\/]*) abs="$lib" ;; - *) abs=`pwd`"/$lib" ;; - esac - func_append newdlfiles " $abs" - done - dlfiles="$newdlfiles" - newdlprefiles= - for lib in $dlprefiles; do - case $lib in - [\\/]* | [A-Za-z]:[\\/]*) abs="$lib" ;; - *) abs=`pwd`"/$lib" ;; - esac - func_append newdlprefiles " $abs" - done - dlprefiles="$newdlprefiles" - fi - $RM $output - # place dlname in correct position for cygwin - # In fact, it would be nice if we could use this code for all target - # systems that can't hard-code library paths into their executables - # and that have no shared library path variable independent of PATH, - # but it turns out we can't easily determine that from inspecting - # libtool variables, so we have to hard-code the OSs to which it - # applies here; at the moment, that means platforms that use the PE - # object format with DLL files. See the long comment at the top of - # tests/bindir.at for full details. - tdlname=$dlname - case $host,$output,$installed,$module,$dlname in - *cygwin*,*lai,yes,no,*.dll | *mingw*,*lai,yes,no,*.dll | *cegcc*,*lai,yes,no,*.dll) - # If a -bindir argument was supplied, place the dll there. - if test "x$bindir" != x ; - then - func_relative_path "$install_libdir" "$bindir" - tdlname=$func_relative_path_result$dlname - else - # Otherwise fall back on heuristic. - tdlname=../bin/$dlname - fi - ;; - esac - $ECHO > $output "\ -# $outputname - a libtool library file -# Generated by $PROGRAM (GNU $PACKAGE$TIMESTAMP) $VERSION -# -# Please DO NOT delete this file! -# It is necessary for linking the library. - -# The name that we can dlopen(3). -dlname='$tdlname' - -# Names of this library. -library_names='$library_names' - -# The name of the static archive. -old_library='$old_library' - -# Linker flags that can not go in dependency_libs. -inherited_linker_flags='$new_inherited_linker_flags' - -# Libraries that this one depends upon. -dependency_libs='$dependency_libs' - -# Names of additional weak libraries provided by this library -weak_library_names='$weak_libs' - -# Version information for $libname. -current=$current -age=$age -revision=$revision - -# Is this an already installed library? -installed=$installed - -# Should we warn about portability when linking against -modules? -shouldnotlink=$module - -# Files to dlopen/dlpreopen -dlopen='$dlfiles' -dlpreopen='$dlprefiles' - -# Directory that this library needs to be installed in: -libdir='$install_libdir'" - if test "$installed" = no && test "$need_relink" = yes; then - $ECHO >> $output "\ -relink_command=\"$relink_command\"" - fi - done - } - - # Do a symbolic link so that the libtool archive can be found in - # LD_LIBRARY_PATH before the program is installed. - func_show_eval '( cd "$output_objdir" && $RM "$outputname" && $LN_S "../$outputname" "$outputname" )' 'exit $?' - ;; - esac - exit $EXIT_SUCCESS -} - -{ test "$opt_mode" = link || test "$opt_mode" = relink; } && - func_mode_link ${1+"$@"} - - -# func_mode_uninstall arg... -func_mode_uninstall () -{ - $opt_debug - RM="$nonopt" - files= - rmforce= - exit_status=0 - - # This variable tells wrapper scripts just to set variables rather - # than running their programs. - libtool_install_magic="$magic" - - for arg - do - case $arg in - -f) func_append RM " $arg"; rmforce=yes ;; - -*) func_append RM " $arg" ;; - *) func_append files " $arg" ;; - esac - done - - test -z "$RM" && \ - func_fatal_help "you must specify an RM program" - - rmdirs= - - for file in $files; do - func_dirname "$file" "" "." - dir="$func_dirname_result" - if test "X$dir" = X.; then - odir="$objdir" - else - odir="$dir/$objdir" - fi - func_basename "$file" - name="$func_basename_result" - test "$opt_mode" = uninstall && odir="$dir" - - # Remember odir for removal later, being careful to avoid duplicates - if test "$opt_mode" = clean; then - case " $rmdirs " in - *" $odir "*) ;; - *) func_append rmdirs " $odir" ;; - esac - fi - - # Don't error if the file doesn't exist and rm -f was used. - if { test -L "$file"; } >/dev/null 2>&1 || - { test -h "$file"; } >/dev/null 2>&1 || - test -f "$file"; then - : - elif test -d "$file"; then - exit_status=1 - continue - elif test "$rmforce" = yes; then - continue - fi - - rmfiles="$file" - - case $name in - *.la) - # Possibly a libtool archive, so verify it. - if func_lalib_p "$file"; then - func_source $dir/$name - - # Delete the libtool libraries and symlinks. - for n in $library_names; do - func_append rmfiles " $odir/$n" - done - test -n "$old_library" && func_append rmfiles " $odir/$old_library" - - case "$opt_mode" in - clean) - case " $library_names " in - *" $dlname "*) ;; - *) test -n "$dlname" && func_append rmfiles " $odir/$dlname" ;; - esac - test -n "$libdir" && func_append rmfiles " $odir/$name $odir/${name}i" - ;; - uninstall) - if test -n "$library_names"; then - # Do each command in the postuninstall commands. - func_execute_cmds "$postuninstall_cmds" 'test "$rmforce" = yes || exit_status=1' - fi - - if test -n "$old_library"; then - # Do each command in the old_postuninstall commands. - func_execute_cmds "$old_postuninstall_cmds" 'test "$rmforce" = yes || exit_status=1' - fi - # FIXME: should reinstall the best remaining shared library. - ;; - esac - fi - ;; - - *.lo) - # Possibly a libtool object, so verify it. - if func_lalib_p "$file"; then - - # Read the .lo file - func_source $dir/$name - - # Add PIC object to the list of files to remove. - if test -n "$pic_object" && - test "$pic_object" != none; then - func_append rmfiles " $dir/$pic_object" - fi - - # Add non-PIC object to the list of files to remove. - if test -n "$non_pic_object" && - test "$non_pic_object" != none; then - func_append rmfiles " $dir/$non_pic_object" - fi - fi - ;; - - *) - if test "$opt_mode" = clean ; then - noexename=$name - case $file in - *.exe) - func_stripname '' '.exe' "$file" - file=$func_stripname_result - func_stripname '' '.exe' "$name" - noexename=$func_stripname_result - # $file with .exe has already been added to rmfiles, - # add $file without .exe - func_append rmfiles " $file" - ;; - esac - # Do a test to see if this is a libtool program. - if func_ltwrapper_p "$file"; then - if func_ltwrapper_executable_p "$file"; then - func_ltwrapper_scriptname "$file" - relink_command= - func_source $func_ltwrapper_scriptname_result - func_append rmfiles " $func_ltwrapper_scriptname_result" - else - relink_command= - func_source $dir/$noexename - fi - - # note $name still contains .exe if it was in $file originally - # as does the version of $file that was added into $rmfiles - func_append rmfiles " $odir/$name $odir/${name}S.${objext}" - if test "$fast_install" = yes && test -n "$relink_command"; then - func_append rmfiles " $odir/lt-$name" - fi - if test "X$noexename" != "X$name" ; then - func_append rmfiles " $odir/lt-${noexename}.c" - fi - fi - fi - ;; - esac - func_show_eval "$RM $rmfiles" 'exit_status=1' - done - - # Try to remove the ${objdir}s in the directories where we deleted files - for dir in $rmdirs; do - if test -d "$dir"; then - func_show_eval "rmdir $dir >/dev/null 2>&1" - fi - done - - exit $exit_status -} - -{ test "$opt_mode" = uninstall || test "$opt_mode" = clean; } && - func_mode_uninstall ${1+"$@"} - -test -z "$opt_mode" && { - help="$generic_help" - func_fatal_help "you must specify a MODE" -} - -test -z "$exec_cmd" && \ - func_fatal_help "invalid operation mode \`$opt_mode'" - -if test -n "$exec_cmd"; then - eval exec "$exec_cmd" - exit $EXIT_FAILURE -fi - -exit $exit_status - - -# The TAGs below are defined such that we never get into a situation -# in which we disable both kinds of libraries. Given conflicting -# choices, we go for a static library, that is the most portable, -# since we can't tell whether shared libraries were disabled because -# the user asked for that or because the platform doesn't support -# them. This is particularly important on AIX, because we don't -# support having both static and shared libraries enabled at the same -# time on that platform, so we default to a shared-only configuration. -# If a disable-shared tag is given, we'll fallback to a static-only -# configuration. But we'll never go from static-only to shared-only. - -# ### BEGIN LIBTOOL TAG CONFIG: disable-shared -build_libtool_libs=no -build_old_libs=yes -# ### END LIBTOOL TAG CONFIG: disable-shared - -# ### BEGIN LIBTOOL TAG CONFIG: disable-static -build_old_libs=`case $build_libtool_libs in yes) echo no;; *) echo yes;; esac` -# ### END LIBTOOL TAG CONFIG: disable-static - -# Local Variables: -# mode:shell-script -# sh-indentation:2 -# End: -# vi:sw=2 - diff --git a/m4/ax_boost_asio.m4 b/m4/ax_boost_asio.m4 deleted file mode 100644 index b57d487..0000000 --- a/m4/ax_boost_asio.m4 +++ /dev/null @@ -1,110 +0,0 @@ -# =========================================================================== -# http://www.gnu.org/software/autoconf-archive/ax_boost_asio.html -# =========================================================================== -# -# SYNOPSIS -# -# AX_BOOST_ASIO -# -# DESCRIPTION -# -# Test for Asio library from the Boost C++ libraries. The macro requires a -# preceding call to AX_BOOST_BASE. Further documentation is available at -# . -# -# This macro calls: -# -# AC_SUBST(BOOST_ASIO_LIB) -# -# And sets: -# -# HAVE_BOOST_ASIO -# -# LICENSE -# -# Copyright (c) 2008 Thomas Porschberg -# Copyright (c) 2008 Pete Greenwell -# -# 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 any -# warranty. - -#serial 16 - -AC_DEFUN([AX_BOOST_ASIO], -[ - AC_ARG_WITH([boost-asio], - AS_HELP_STRING([--with-boost-asio@<:@=special-lib@:>@], - [use the ASIO library from boost - it is possible to specify a certain library for the linker - e.g. --with-boost-asio=boost_system-gcc41-mt-1_34 ]), - [ - if test "$withval" = "no"; then - want_boost="no" - elif test "$withval" = "yes"; then - want_boost="yes" - ax_boost_user_asio_lib="" - else - want_boost="yes" - ax_boost_user_asio_lib="$withval" - fi - ], - [want_boost="yes"] - ) - - if test "x$want_boost" = "xyes"; then - AC_REQUIRE([AC_PROG_CC]) - CPPFLAGS_SAVED="$CPPFLAGS" - CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS" - export CPPFLAGS - - LDFLAGS_SAVED="$LDFLAGS" - LDFLAGS="$LDFLAGS $BOOST_LDFLAGS" - export LDFLAGS - - AC_CACHE_CHECK(whether the Boost::ASIO library is available, - ax_cv_boost_asio, - [AC_LANG_PUSH([C++]) - AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ @%:@include - ]], - [[ - - boost::asio::io_service io; - boost::system::error_code timer_result; - boost::asio::deadline_timer t(io); - t.cancel(); - io.run_one(); - return 0; - ]])], - ax_cv_boost_asio=yes, ax_cv_boost_asio=no) - AC_LANG_POP([C++]) - ]) - if test "x$ax_cv_boost_asio" = "xyes"; then - AC_DEFINE(HAVE_BOOST_ASIO,,[define if the Boost::ASIO library is available]) - BN=boost_system - BOOSTLIBDIR=`echo $BOOST_LDFLAGS | sed -e 's/@<:@^\/@:>@*//'` - if test "x$ax_boost_user_asio_lib" = "x"; then - for ax_lib in `ls $BOOSTLIBDIR/libboost_system*.so* $BOOSTLIBDIR/libboost_system*.dylib* $BOOSTLIBDIR/libboost_system*.a* 2>/dev/null | sed 's,.*/,,' | sed -e 's;^lib\(boost_system.*\)\.so.*$;\1;' -e 's;^lib\(boost_system.*\)\.dylib.*$;\1;' -e 's;^lib\(boost_system.*\)\.a.*$;\1;' ` ; do - AC_CHECK_LIB($ax_lib, main, [BOOST_ASIO_LIB="-l$ax_lib" AC_SUBST(BOOST_ASIO_LIB) link_thread="yes" break], - [link_thread="no"]) - done - else - for ax_lib in $ax_boost_user_asio_lib $BN-$ax_boost_user_asio_lib; do - AC_CHECK_LIB($ax_lib, main, - [BOOST_ASIO_LIB="-l$ax_lib" AC_SUBST(BOOST_ASIO_LIB) link_asio="yes" break], - [link_asio="no"]) - done - - fi - if test "x$ax_lib" = "x"; then - AC_MSG_ERROR(Could not find a version of the library!) - fi - if test "x$link_asio" = "xno"; then - AC_MSG_ERROR(Could not link against $ax_lib !) - fi - fi - - CPPFLAGS="$CPPFLAGS_SAVED" - LDFLAGS="$LDFLAGS_SAVED" - fi -]) diff --git a/m4/ax_boost_base.m4 b/m4/ax_boost_base.m4 deleted file mode 100644 index d7c9d0d..0000000 --- a/m4/ax_boost_base.m4 +++ /dev/null @@ -1,275 +0,0 @@ -# =========================================================================== -# http://www.gnu.org/software/autoconf-archive/ax_boost_base.html -# =========================================================================== -# -# SYNOPSIS -# -# AX_BOOST_BASE([MINIMUM-VERSION], [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) -# -# DESCRIPTION -# -# Test for the Boost C++ libraries of a particular version (or newer) -# -# If no path to the installed boost library is given the macro searchs -# under /usr, /usr/local, /opt and /opt/local and evaluates the -# $BOOST_ROOT environment variable. Further documentation is available at -# . -# -# This macro calls: -# -# AC_SUBST(BOOST_CPPFLAGS) / AC_SUBST(BOOST_LDFLAGS) -# -# And sets: -# -# HAVE_BOOST -# -# LICENSE -# -# Copyright (c) 2008 Thomas Porschberg -# Copyright (c) 2009 Peter Adolphs -# -# 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 any -# warranty. - -#serial 25 - -AC_DEFUN([AX_BOOST_BASE], -[ -AC_ARG_WITH([boost], - [AS_HELP_STRING([--with-boost@<:@=ARG@:>@], - [use Boost library from a standard location (ARG=yes), - from the specified location (ARG=), - or disable it (ARG=no) - @<:@ARG=yes@:>@ ])], - [ - if test "$withval" = "no"; then - want_boost="no" - elif test "$withval" = "yes"; then - want_boost="yes" - ac_boost_path="" - else - want_boost="yes" - ac_boost_path="$withval" - fi - ], - [want_boost="yes"]) - - -AC_ARG_WITH([boost-libdir], - AS_HELP_STRING([--with-boost-libdir=LIB_DIR], - [Force given directory for boost libraries. Note that this will override library path detection, so use this parameter only if default library detection fails and you know exactly where your boost libraries are located.]), - [ - if test -d "$withval" - then - ac_boost_lib_path="$withval" - else - AC_MSG_ERROR(--with-boost-libdir expected directory name) - fi - ], - [ac_boost_lib_path=""] -) - -if test "x$want_boost" = "xyes"; then - boost_lib_version_req=ifelse([$1], ,1.20.0,$1) - boost_lib_version_req_shorten=`expr $boost_lib_version_req : '\([[0-9]]*\.[[0-9]]*\)'` - boost_lib_version_req_major=`expr $boost_lib_version_req : '\([[0-9]]*\)'` - boost_lib_version_req_minor=`expr $boost_lib_version_req : '[[0-9]]*\.\([[0-9]]*\)'` - boost_lib_version_req_sub_minor=`expr $boost_lib_version_req : '[[0-9]]*\.[[0-9]]*\.\([[0-9]]*\)'` - if test "x$boost_lib_version_req_sub_minor" = "x" ; then - boost_lib_version_req_sub_minor="0" - fi - WANT_BOOST_VERSION=`expr $boost_lib_version_req_major \* 100000 \+ $boost_lib_version_req_minor \* 100 \+ $boost_lib_version_req_sub_minor` - AC_MSG_CHECKING(for boostlib >= $boost_lib_version_req) - succeeded=no - - dnl On 64-bit systems check for system libraries in both lib64 and lib. - dnl The former is specified by FHS, but e.g. Debian does not adhere to - dnl this (as it rises problems for generic multi-arch support). - dnl The last entry in the list is chosen by default when no libraries - dnl are found, e.g. when only header-only libraries are installed! - libsubdirs="lib" - ax_arch=`uname -m` - case $ax_arch in - x86_64) - libsubdirs="lib64 libx32 lib lib64" - ;; - ppc64|s390x|sparc64|aarch64|ppc64le) - libsubdirs="lib64 lib lib64 ppc64le" - ;; - esac - - dnl allow for real multi-arch paths e.g. /usr/lib/x86_64-linux-gnu. Give - dnl them priority over the other paths since, if libs are found there, they - dnl are almost assuredly the ones desired. - AC_REQUIRE([AC_CANONICAL_HOST]) - libsubdirs="lib/${host_cpu}-${host_os} $libsubdirs" - - case ${host_cpu} in - i?86) - libsubdirs="lib/i386-${host_os} $libsubdirs" - ;; - esac - - dnl first we check the system location for boost libraries - dnl this location ist chosen if boost libraries are installed with the --layout=system option - dnl or if you install boost with RPM - if test "$ac_boost_path" != ""; then - BOOST_CPPFLAGS="-I$ac_boost_path/include" - for ac_boost_path_tmp in $libsubdirs; do - if test -d "$ac_boost_path"/"$ac_boost_path_tmp" ; then - BOOST_LDFLAGS="-L$ac_boost_path/$ac_boost_path_tmp" - break - fi - done - elif test "$cross_compiling" != yes; then - for ac_boost_path_tmp in /usr /usr/local /opt /opt/local ; do - if test -d "$ac_boost_path_tmp/include/boost" && test -r "$ac_boost_path_tmp/include/boost"; then - for libsubdir in $libsubdirs ; do - if ls "$ac_boost_path_tmp/$libsubdir/libboost_"* >/dev/null 2>&1 ; then break; fi - done - BOOST_LDFLAGS="-L$ac_boost_path_tmp/$libsubdir" - BOOST_CPPFLAGS="-I$ac_boost_path_tmp/include" - break; - fi - done - fi - - dnl overwrite ld flags if we have required special directory with - dnl --with-boost-libdir parameter - if test "$ac_boost_lib_path" != ""; then - BOOST_LDFLAGS="-L$ac_boost_lib_path" - fi - - CPPFLAGS_SAVED="$CPPFLAGS" - CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS" - export CPPFLAGS - - LDFLAGS_SAVED="$LDFLAGS" - LDFLAGS="$LDFLAGS $BOOST_LDFLAGS" - export LDFLAGS - - AC_REQUIRE([AC_PROG_CXX]) - AC_LANG_PUSH(C++) - AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ - @%:@include - ]], [[ - #if BOOST_VERSION >= $WANT_BOOST_VERSION - // Everything is okay - #else - # error Boost version is too old - #endif - ]])],[ - AC_MSG_RESULT(yes) - succeeded=yes - found_system=yes - ],[ - ]) - AC_LANG_POP([C++]) - - - - dnl if we found no boost with system layout we search for boost libraries - dnl built and installed without the --layout=system option or for a staged(not installed) version - if test "x$succeeded" != "xyes"; then - _version=0 - if test "$ac_boost_path" != ""; then - if test -d "$ac_boost_path" && test -r "$ac_boost_path"; then - for i in `ls -d $ac_boost_path/include/boost-* 2>/dev/null`; do - _version_tmp=`echo $i | sed "s#$ac_boost_path##" | sed 's/\/include\/boost-//' | sed 's/_/./'` - V_CHECK=`expr $_version_tmp \> $_version` - if test "$V_CHECK" = "1" ; then - _version=$_version_tmp - fi - VERSION_UNDERSCORE=`echo $_version | sed 's/\./_/'` - BOOST_CPPFLAGS="-I$ac_boost_path/include/boost-$VERSION_UNDERSCORE" - done - fi - else - if test "$cross_compiling" != yes; then - for ac_boost_path in /usr /usr/local /opt /opt/local ; do - if test -d "$ac_boost_path" && test -r "$ac_boost_path"; then - for i in `ls -d $ac_boost_path/include/boost-* 2>/dev/null`; do - _version_tmp=`echo $i | sed "s#$ac_boost_path##" | sed 's/\/include\/boost-//' | sed 's/_/./'` - V_CHECK=`expr $_version_tmp \> $_version` - if test "$V_CHECK" = "1" ; then - _version=$_version_tmp - best_path=$ac_boost_path - fi - done - fi - done - - VERSION_UNDERSCORE=`echo $_version | sed 's/\./_/'` - BOOST_CPPFLAGS="-I$best_path/include/boost-$VERSION_UNDERSCORE" - if test "$ac_boost_lib_path" = ""; then - for libsubdir in $libsubdirs ; do - if ls "$best_path/$libsubdir/libboost_"* >/dev/null 2>&1 ; then break; fi - done - BOOST_LDFLAGS="-L$best_path/$libsubdir" - fi - fi - - if test "x$BOOST_ROOT" != "x"; then - for libsubdir in $libsubdirs ; do - if ls "$BOOST_ROOT/stage/$libsubdir/libboost_"* >/dev/null 2>&1 ; then break; fi - done - if test -d "$BOOST_ROOT" && test -r "$BOOST_ROOT" && test -d "$BOOST_ROOT/stage/$libsubdir" && test -r "$BOOST_ROOT/stage/$libsubdir"; then - version_dir=`expr //$BOOST_ROOT : '.*/\(.*\)'` - stage_version=`echo $version_dir | sed 's/boost_//' | sed 's/_/./g'` - stage_version_shorten=`expr $stage_version : '\([[0-9]]*\.[[0-9]]*\)'` - V_CHECK=`expr $stage_version_shorten \>\= $_version` - if test "$V_CHECK" = "1" -a "$ac_boost_lib_path" = "" ; then - AC_MSG_NOTICE(We will use a staged boost library from $BOOST_ROOT) - BOOST_CPPFLAGS="-I$BOOST_ROOT" - BOOST_LDFLAGS="-L$BOOST_ROOT/stage/$libsubdir" - fi - fi - fi - fi - - CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS" - export CPPFLAGS - LDFLAGS="$LDFLAGS $BOOST_LDFLAGS" - export LDFLAGS - - AC_LANG_PUSH(C++) - AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ - @%:@include - ]], [[ - #if BOOST_VERSION >= $WANT_BOOST_VERSION - // Everything is okay - #else - # error Boost version is too old - #endif - ]])],[ - AC_MSG_RESULT(yes) - succeeded=yes - found_system=yes - ],[ - ]) - AC_LANG_POP([C++]) - fi - - if test "$succeeded" != "yes" ; then - if test "$_version" = "0" ; then - AC_MSG_NOTICE([[We could not detect the boost libraries (version $boost_lib_version_req_shorten or higher). If you have a staged boost library (still not installed) please specify \$BOOST_ROOT in your environment and do not give a PATH to --with-boost option. If you are sure you have boost installed, then check your version number looking in . See http://randspringer.de/boost for more documentation.]]) - else - AC_MSG_NOTICE([Your boost libraries seems to old (version $_version).]) - fi - # execute ACTION-IF-NOT-FOUND (if present): - ifelse([$3], , :, [$3]) - else - AC_SUBST(BOOST_CPPFLAGS) - AC_SUBST(BOOST_LDFLAGS) - AC_DEFINE(HAVE_BOOST,,[define if the Boost library is available]) - # execute ACTION-IF-FOUND (if present): - ifelse([$2], , :, [$2]) - fi - - CPPFLAGS="$CPPFLAGS_SAVED" - LDFLAGS="$LDFLAGS_SAVED" -fi - -]) diff --git a/m4/ax_boost_system.m4 b/m4/ax_boost_system.m4 deleted file mode 100644 index c4c4555..0000000 --- a/m4/ax_boost_system.m4 +++ /dev/null @@ -1,120 +0,0 @@ -# =========================================================================== -# http://www.gnu.org/software/autoconf-archive/ax_boost_system.html -# =========================================================================== -# -# SYNOPSIS -# -# AX_BOOST_SYSTEM -# -# DESCRIPTION -# -# Test for System library from the Boost C++ libraries. The macro requires -# a preceding call to AX_BOOST_BASE. Further documentation is available at -# . -# -# This macro calls: -# -# AC_SUBST(BOOST_SYSTEM_LIB) -# -# And sets: -# -# HAVE_BOOST_SYSTEM -# -# LICENSE -# -# Copyright (c) 2008 Thomas Porschberg -# Copyright (c) 2008 Michael Tindal -# Copyright (c) 2008 Daniel Casimiro -# -# 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 any -# warranty. - -#serial 17 - -AC_DEFUN([AX_BOOST_SYSTEM], -[ - AC_ARG_WITH([boost-system], - AS_HELP_STRING([--with-boost-system@<:@=special-lib@:>@], - [use the System library from boost - it is possible to specify a certain library for the linker - e.g. --with-boost-system=boost_system-gcc-mt ]), - [ - if test "$withval" = "no"; then - want_boost="no" - elif test "$withval" = "yes"; then - want_boost="yes" - ax_boost_user_system_lib="" - else - want_boost="yes" - ax_boost_user_system_lib="$withval" - fi - ], - [want_boost="yes"] - ) - - if test "x$want_boost" = "xyes"; then - AC_REQUIRE([AC_PROG_CC]) - AC_REQUIRE([AC_CANONICAL_BUILD]) - CPPFLAGS_SAVED="$CPPFLAGS" - CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS" - export CPPFLAGS - - LDFLAGS_SAVED="$LDFLAGS" - LDFLAGS="$LDFLAGS $BOOST_LDFLAGS" - export LDFLAGS - - AC_CACHE_CHECK(whether the Boost::System library is available, - ax_cv_boost_system, - [AC_LANG_PUSH([C++]) - CXXFLAGS_SAVE=$CXXFLAGS - - AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[@%:@include ]], - [[boost::system::system_category]])], - ax_cv_boost_system=yes, ax_cv_boost_system=no) - CXXFLAGS=$CXXFLAGS_SAVE - AC_LANG_POP([C++]) - ]) - if test "x$ax_cv_boost_system" = "xyes"; then - AC_SUBST(BOOST_CPPFLAGS) - - AC_DEFINE(HAVE_BOOST_SYSTEM,,[define if the Boost::System library is available]) - BOOSTLIBDIR=`echo $BOOST_LDFLAGS | sed -e 's/@<:@^\/@:>@*//'` - - LDFLAGS_SAVE=$LDFLAGS - if test "x$ax_boost_user_system_lib" = "x"; then - for libextension in `ls -r $BOOSTLIBDIR/libboost_system* 2>/dev/null | sed 's,.*/lib,,' | sed 's,\..*,,'` ; do - ax_lib=${libextension} - AC_CHECK_LIB($ax_lib, exit, - [BOOST_SYSTEM_LIB="-l$ax_lib"; AC_SUBST(BOOST_SYSTEM_LIB) link_system="yes"; break], - [link_system="no"]) - done - if test "x$link_system" != "xyes"; then - for libextension in `ls -r $BOOSTLIBDIR/boost_system* 2>/dev/null | sed 's,.*/,,' | sed -e 's,\..*,,'` ; do - ax_lib=${libextension} - AC_CHECK_LIB($ax_lib, exit, - [BOOST_SYSTEM_LIB="-l$ax_lib"; AC_SUBST(BOOST_SYSTEM_LIB) link_system="yes"; break], - [link_system="no"]) - done - fi - - else - for ax_lib in $ax_boost_user_system_lib boost_system-$ax_boost_user_system_lib; do - AC_CHECK_LIB($ax_lib, exit, - [BOOST_SYSTEM_LIB="-l$ax_lib"; AC_SUBST(BOOST_SYSTEM_LIB) link_system="yes"; break], - [link_system="no"]) - done - - fi - if test "x$ax_lib" = "x"; then - AC_MSG_ERROR(Could not find a version of the library!) - fi - if test "x$link_system" = "xno"; then - AC_MSG_ERROR(Could not link against $ax_lib !) - fi - fi - - CPPFLAGS="$CPPFLAGS_SAVED" - LDFLAGS="$LDFLAGS_SAVED" - fi -]) diff --git a/m4/ax_boost_thread.m4 b/m4/ax_boost_thread.m4 deleted file mode 100644 index 79e12cd..0000000 --- a/m4/ax_boost_thread.m4 +++ /dev/null @@ -1,149 +0,0 @@ -# =========================================================================== -# http://www.gnu.org/software/autoconf-archive/ax_boost_thread.html -# =========================================================================== -# -# SYNOPSIS -# -# AX_BOOST_THREAD -# -# DESCRIPTION -# -# Test for Thread library from the Boost C++ libraries. The macro requires -# a preceding call to AX_BOOST_BASE. Further documentation is available at -# . -# -# This macro calls: -# -# AC_SUBST(BOOST_THREAD_LIB) -# -# And sets: -# -# HAVE_BOOST_THREAD -# -# LICENSE -# -# Copyright (c) 2009 Thomas Porschberg -# Copyright (c) 2009 Michael Tindal -# -# 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 any -# warranty. - -#serial 27 - -AC_DEFUN([AX_BOOST_THREAD], -[ - AC_ARG_WITH([boost-thread], - AS_HELP_STRING([--with-boost-thread@<:@=special-lib@:>@], - [use the Thread library from boost - it is possible to specify a certain library for the linker - e.g. --with-boost-thread=boost_thread-gcc-mt ]), - [ - if test "$withval" = "no"; then - want_boost="no" - elif test "$withval" = "yes"; then - want_boost="yes" - ax_boost_user_thread_lib="" - else - want_boost="yes" - ax_boost_user_thread_lib="$withval" - fi - ], - [want_boost="yes"] - ) - - if test "x$want_boost" = "xyes"; then - AC_REQUIRE([AC_PROG_CC]) - AC_REQUIRE([AC_CANONICAL_BUILD]) - CPPFLAGS_SAVED="$CPPFLAGS" - CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS" - export CPPFLAGS - - LDFLAGS_SAVED="$LDFLAGS" - LDFLAGS="$LDFLAGS $BOOST_LDFLAGS" - export LDFLAGS - - AC_CACHE_CHECK(whether the Boost::Thread library is available, - ax_cv_boost_thread, - [AC_LANG_PUSH([C++]) - CXXFLAGS_SAVE=$CXXFLAGS - - if test "x$host_os" = "xsolaris" ; then - CXXFLAGS="-pthreads $CXXFLAGS" - elif test "x$host_os" = "xmingw32" ; then - CXXFLAGS="-mthreads $CXXFLAGS" - else - CXXFLAGS="-pthread $CXXFLAGS" - fi - AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[@%:@include ]], - [[boost::thread_group thrds; - return 0;]])], - ax_cv_boost_thread=yes, ax_cv_boost_thread=no) - CXXFLAGS=$CXXFLAGS_SAVE - AC_LANG_POP([C++]) - ]) - if test "x$ax_cv_boost_thread" = "xyes"; then - if test "x$host_os" = "xsolaris" ; then - BOOST_CPPFLAGS="-pthreads $BOOST_CPPFLAGS" - elif test "x$host_os" = "xmingw32" ; then - BOOST_CPPFLAGS="-mthreads $BOOST_CPPFLAGS" - else - BOOST_CPPFLAGS="-pthread $BOOST_CPPFLAGS" - fi - - AC_SUBST(BOOST_CPPFLAGS) - - AC_DEFINE(HAVE_BOOST_THREAD,,[define if the Boost::Thread library is available]) - BOOSTLIBDIR=`echo $BOOST_LDFLAGS | sed -e 's/@<:@^\/@:>@*//'` - - LDFLAGS_SAVE=$LDFLAGS - case "x$host_os" in - *bsd* ) - LDFLAGS="-pthread $LDFLAGS" - break; - ;; - esac - if test "x$ax_boost_user_thread_lib" = "x"; then - for libextension in `ls -r $BOOSTLIBDIR/libboost_thread* 2>/dev/null | sed 's,.*/lib,,' | sed 's,\..*,,'`; do - ax_lib=${libextension} - AC_CHECK_LIB($ax_lib, exit, - [BOOST_THREAD_LIB="-l$ax_lib"; AC_SUBST(BOOST_THREAD_LIB) link_thread="yes"; break], - [link_thread="no"]) - done - if test "x$link_thread" != "xyes"; then - for libextension in `ls -r $BOOSTLIBDIR/boost_thread* 2>/dev/null | sed 's,.*/,,' | sed 's,\..*,,'`; do - ax_lib=${libextension} - AC_CHECK_LIB($ax_lib, exit, - [BOOST_THREAD_LIB="-l$ax_lib"; AC_SUBST(BOOST_THREAD_LIB) link_thread="yes"; break], - [link_thread="no"]) - done - fi - - else - for ax_lib in $ax_boost_user_thread_lib boost_thread-$ax_boost_user_thread_lib; do - AC_CHECK_LIB($ax_lib, exit, - [BOOST_THREAD_LIB="-l$ax_lib"; AC_SUBST(BOOST_THREAD_LIB) link_thread="yes"; break], - [link_thread="no"]) - done - - fi - if test "x$ax_lib" = "x"; then - AC_MSG_ERROR(Could not find a version of the library!) - fi - if test "x$link_thread" = "xno"; then - AC_MSG_ERROR(Could not link against $ax_lib !) - else - case "x$host_os" in - *bsd* ) - BOOST_LDFLAGS="-pthread $BOOST_LDFLAGS" - break; - ;; - esac - - fi - fi - - CPPFLAGS="$CPPFLAGS_SAVED" - LDFLAGS="$LDFLAGS_SAVED" - fi -]) diff --git a/m4/ax_check_compile_flag.m4 b/m4/ax_check_compile_flag.m4 deleted file mode 100644 index 51df0c0..0000000 --- a/m4/ax_check_compile_flag.m4 +++ /dev/null @@ -1,74 +0,0 @@ -# =========================================================================== -# 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 -# Copyright (c) 2011 Maarten Bosmans -# -# 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 . -# -# 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/m4/ax_cxx_compile_stdcxx_11.m4 b/m4/ax_cxx_compile_stdcxx_11.m4 deleted file mode 100644 index af37acd..0000000 --- a/m4/ax_cxx_compile_stdcxx_11.m4 +++ /dev/null @@ -1,133 +0,0 @@ -# ============================================================================ -# http://www.gnu.org/software/autoconf-archive/ax_cxx_compile_stdcxx_11.html -# ============================================================================ -# -# SYNOPSIS -# -# AX_CXX_COMPILE_STDCXX_11([ext|noext],[mandatory|optional]) -# -# DESCRIPTION -# -# Check for baseline language coverage in the compiler for the C++11 -# standard; if necessary, add switches to CXXFLAGS to enable support. -# -# The first argument, if specified, indicates whether you insist on an -# extended mode (e.g. -std=gnu++11) or a strict conformance mode (e.g. -# -std=c++11). If neither is specified, you get whatever works, with -# preference for an extended mode. -# -# The second argument, if specified 'mandatory' or if left unspecified, -# indicates that baseline C++11 support is required and that the macro -# should error out if no mode with that support is found. If specified -# 'optional', then configuration proceeds regardless, after defining -# HAVE_CXX11 if and only if a supporting mode is found. -# -# LICENSE -# -# Copyright (c) 2008 Benjamin Kosnik -# Copyright (c) 2012 Zack Weinberg -# Copyright (c) 2013 Roy Stogner -# -# 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 any -# warranty. - -#serial 3 - -m4_define([_AX_CXX_COMPILE_STDCXX_11_testbody], [ - template - struct check - { - static_assert(sizeof(int) <= sizeof(T), "not big enough"); - }; - - typedef check> right_angle_brackets; - - int a; - decltype(a) b; - - typedef check check_type; - check_type c; - check_type&& cr = static_cast(c); - - auto d = a; -]) - -AC_DEFUN([AX_CXX_COMPILE_STDCXX_11], [dnl - m4_if([$1], [], [], - [$1], [ext], [], - [$1], [noext], [], - [m4_fatal([invalid argument `$1' to AX_CXX_COMPILE_STDCXX_11])])dnl - m4_if([$2], [], [ax_cxx_compile_cxx11_required=true], - [$2], [mandatory], [ax_cxx_compile_cxx11_required=true], - [$2], [optional], [ax_cxx_compile_cxx11_required=false], - [m4_fatal([invalid second argument `$2' to AX_CXX_COMPILE_STDCXX_11])])dnl - AC_LANG_PUSH([C++])dnl - ac_success=no - AC_CACHE_CHECK(whether $CXX supports C++11 features by default, - ax_cv_cxx_compile_cxx11, - [AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_11_testbody])], - [ax_cv_cxx_compile_cxx11=yes], - [ax_cv_cxx_compile_cxx11=no])]) - if test x$ax_cv_cxx_compile_cxx11 = xyes; then - ac_success=yes - fi - - m4_if([$1], [noext], [], [dnl - if test x$ac_success = xno; then - for switch in -std=gnu++11 -std=gnu++0x; do - cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx11_$switch]) - AC_CACHE_CHECK(whether $CXX supports C++11 features with $switch, - $cachevar, - [ac_save_CXXFLAGS="$CXXFLAGS" - CXXFLAGS="$CXXFLAGS $switch" - AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_11_testbody])], - [eval $cachevar=yes], - [eval $cachevar=no]) - CXXFLAGS="$ac_save_CXXFLAGS"]) - if eval test x\$$cachevar = xyes; then - CXXFLAGS="$CXXFLAGS $switch" - ac_success=yes - break - fi - done - fi]) - - m4_if([$1], [ext], [], [dnl - if test x$ac_success = xno; then - for switch in -std=c++11 -std=c++0x; do - cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx11_$switch]) - AC_CACHE_CHECK(whether $CXX supports C++11 features with $switch, - $cachevar, - [ac_save_CXXFLAGS="$CXXFLAGS" - CXXFLAGS="$CXXFLAGS $switch" - AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_11_testbody])], - [eval $cachevar=yes], - [eval $cachevar=no]) - CXXFLAGS="$ac_save_CXXFLAGS"]) - if eval test x\$$cachevar = xyes; then - CXXFLAGS="$CXXFLAGS $switch" - ac_success=yes - break - fi - done - fi]) - AC_LANG_POP([C++]) - if test x$ax_cxx_compile_cxx11_required = xtrue; then - if test x$ac_success = xno; then - AC_MSG_ERROR([*** A compiler with support for C++11 language features is required.]) - fi - else - if test x$ac_success = xno; then - HAVE_CXX11=0 - AC_MSG_NOTICE([No compiler with C++11 support was found]) - else - HAVE_CXX11=1 - AC_DEFINE(HAVE_CXX11,1, - [define if the compiler supports basic C++11 syntax]) - fi - - AC_SUBST(HAVE_CXX11) - fi -]) diff --git a/m4/ax_have_epoll.m4 b/m4/ax_have_epoll.m4 deleted file mode 100644 index 07ceb49..0000000 --- a/m4/ax_have_epoll.m4 +++ /dev/null @@ -1,104 +0,0 @@ -# =========================================================================== -# http://www.gnu.org/software/autoconf-archive/ax_have_epoll.html -# =========================================================================== -# -# SYNOPSIS -# -# AX_HAVE_EPOLL([ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) -# AX_HAVE_EPOLL_PWAIT([ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) -# -# DESCRIPTION -# -# This macro determines whether the system supports the epoll I/O event -# interface. A neat usage example would be: -# -# AX_HAVE_EPOLL( -# [AX_CONFIG_FEATURE_ENABLE(epoll)], -# [AX_CONFIG_FEATURE_DISABLE(epoll)]) -# AX_CONFIG_FEATURE( -# [epoll], [This platform supports epoll(7)], -# [HAVE_EPOLL], [This platform supports epoll(7).]) -# -# The epoll interface was added to the Linux kernel in version 2.5.45, and -# the macro verifies that a kernel newer than this is installed. This -# check is somewhat unreliable if doesn't match the -# running kernel, but it is necessary regardless, because glibc comes with -# stubs for the epoll_create(), epoll_wait(), etc. that allow programs to -# compile and link even if the kernel is too old; the problem would then -# be detected only at runtime. -# -# Linux kernel version 2.6.19 adds the epoll_pwait() call in addition to -# epoll_wait(). The availability of that function can be tested with the -# second macro. Generally speaking, it is safe to assume that -# AX_HAVE_EPOLL would succeed if AX_HAVE_EPOLL_PWAIT has, but not the -# other way round. -# -# LICENSE -# -# Copyright (c) 2008 Peter Simons -# -# 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 any -# warranty. - -#serial 10 - -AC_DEFUN([AX_HAVE_EPOLL], [dnl - ax_have_epoll_cppflags="${CPPFLAGS}" - AC_CHECK_HEADER([linux/version.h], [CPPFLAGS="${CPPFLAGS} -DHAVE_LINUX_VERSION_H"]) - AC_MSG_CHECKING([for Linux epoll(7) interface]) - AC_CACHE_VAL([ax_cv_have_epoll], [dnl - AC_LINK_IFELSE([dnl - AC_LANG_PROGRAM([dnl -#include -#ifdef HAVE_LINUX_VERSION_H -# include -# if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,45) -# error linux kernel version is too old to have epoll -# endif -#endif -], [dnl -int fd, rc; -struct epoll_event ev; -fd = epoll_create(128); -rc = epoll_wait(fd, &ev, 1, 0);])], - [ax_cv_have_epoll=yes], - [ax_cv_have_epoll=no])]) - CPPFLAGS="${ax_have_epoll_cppflags}" - AS_IF([test "${ax_cv_have_epoll}" = "yes"], - [AC_MSG_RESULT([yes]) -$1],[AC_MSG_RESULT([no]) -$2]) -])dnl - -AC_DEFUN([AX_HAVE_EPOLL_PWAIT], [dnl - ax_have_epoll_cppflags="${CPPFLAGS}" - AC_CHECK_HEADER([linux/version.h], - [CPPFLAGS="${CPPFLAGS} -DHAVE_LINUX_VERSION_H"]) - AC_MSG_CHECKING([for Linux epoll(7) interface with signals extension]) - AC_CACHE_VAL([ax_cv_have_epoll_pwait], [dnl - AC_LINK_IFELSE([dnl - AC_LANG_PROGRAM([dnl -#ifdef HAVE_LINUX_VERSION_H -# include -# if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19) -# error linux kernel version is too old to have epoll_pwait -# endif -#endif -#include -#include -], [dnl -int fd, rc; -struct epoll_event ev; -fd = epoll_create(128); -rc = epoll_wait(fd, &ev, 1, 0); -rc = epoll_pwait(fd, &ev, 1, 0, (sigset_t const *)(0));])], - [ax_cv_have_epoll_pwait=yes], - [ax_cv_have_epoll_pwait=no])]) - CPPFLAGS="${ax_have_epoll_cppflags}" - AS_IF([test "${ax_cv_have_epoll_pwait}" = "yes"], - [AC_MSG_RESULT([yes]) -$1],[AC_MSG_RESULT([no]) -$2]) -])dnl diff --git a/m4/ax_python_devel.m4 b/m4/ax_python_devel.m4 deleted file mode 100644 index 067fbcf..0000000 --- a/m4/ax_python_devel.m4 +++ /dev/null @@ -1,344 +0,0 @@ -# =========================================================================== -# http://www.gnu.org/software/autoconf-archive/ax_python_devel.html -# =========================================================================== -# -# SYNOPSIS -# -# AX_PYTHON_DEVEL([version]) -# -# DESCRIPTION -# -# Note: Defines as a precious variable "PYTHON_VERSION". Don't override it -# in your configure.ac. -# -# This macro checks for Python and tries to get the include path to -# 'Python.h'. It provides the $(PYTHON_CPPFLAGS) and $(PYTHON_LDFLAGS) -# output variables. It also exports $(PYTHON_EXTRA_LIBS) and -# $(PYTHON_EXTRA_LDFLAGS) for embedding Python in your code. -# -# You can search for some particular version of Python by passing a -# parameter to this macro, for example ">= '2.3.1'", or "== '2.4'". Please -# note that you *have* to pass also an operator along with the version to -# match, and pay special attention to the single quotes surrounding the -# version number. Don't use "PYTHON_VERSION" for this: that environment -# variable is declared as precious and thus reserved for the end-user. -# -# This macro should work for all versions of Python >= 2.1.0. As an end -# user, you can disable the check for the python version by setting the -# PYTHON_NOVERSIONCHECK environment variable to something else than the -# empty string. -# -# If you need to use this macro for an older Python version, please -# contact the authors. We're always open for feedback. -# -# LICENSE -# -# Copyright (c) 2009 Sebastian Huber -# Copyright (c) 2009 Alan W. Irwin -# Copyright (c) 2009 Rafael Laboissiere -# Copyright (c) 2009 Andrew Collier -# Copyright (c) 2009 Matteo Settenvini -# Copyright (c) 2009 Horst Knorr -# Copyright (c) 2013 Daniel Mullner -# -# 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 . -# -# 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 16 - -AU_ALIAS([AC_PYTHON_DEVEL], [AX_PYTHON_DEVEL]) -AC_DEFUN([AX_PYTHON_DEVEL],[ - # - # Allow the use of a (user set) custom python version - # - AC_ARG_VAR([PYTHON_VERSION],[The installed Python - version to use, for example '2.3'. This string - will be appended to the Python interpreter - canonical name.]) - - AC_PATH_PROG([PYTHON],[python[$PYTHON_VERSION]]) - if test -z "$PYTHON"; then - AC_MSG_WARN([Cannot find python$PYTHON_VERSION in your system path]) - PYTHON_VERSION="" - no_python_devel=yes - fi - -AS_IF([test -z "$no_python_devel"], [ - # - # Check for a version of Python >= 2.1.0 - # - AC_MSG_CHECKING([for a version of Python >= '2.1.0']) - ac_supports_python_ver=`$PYTHON -c "import sys; \ - ver = sys.version.split ()[[0]]; \ - print (ver >= '2.1.0')"` - if test "$ac_supports_python_ver" != "True"; then - if test -z "$PYTHON_NOVERSIONCHECK"; then - AC_MSG_RESULT([no]) - AC_MSG_WARN([ -This version of the AC@&t@_PYTHON_DEVEL macro -doesn't work properly with versions of Python before -2.1.0. You may need to re-run configure, setting the -variables PYTHON_CPPFLAGS, PYTHON_LDFLAGS, PYTHON_SITE_PKG, -PYTHON_EXTRA_LIBS and PYTHON_EXTRA_LDFLAGS by hand. -Moreover, to disable this check, set PYTHON_NOVERSIONCHECK -to something else than an empty string. -]) - no_python_devel=yes - else - AC_MSG_RESULT([skip at user request]) - fi - else - AC_MSG_RESULT([yes]) - fi -]) # AS_IF - -AS_IF([test -z "$no_python_devel"], [ - # - # if the macro parameter ``version'' is set, honour it - # - if test -n "$1"; then - AC_MSG_CHECKING([for a version of Python $1]) - ac_supports_python_ver=`$PYTHON -c "import sys; \ - ver = sys.version.split ()[[0]]; \ - print (ver $1)"` - if test "$ac_supports_python_ver" = "True"; then - AC_MSG_RESULT([yes]) - else - AC_MSG_RESULT([no]) - AC_MSG_WARN([this package requires Python $1. -If you have it installed, but it isn't the default Python -interpreter in your system path, please pass the PYTHON_VERSION -variable to configure. See ``configure --help'' for reference. -]) - PYTHON_VERSION="" - no_python_devel=yes - fi - fi -]) # AS_IF - -AS_IF([test -z "$no_python_devel"], [ - # - # Check if you have distutils, else fail - # - AC_MSG_CHECKING([for the distutils Python package]) - ac_distutils_result=`$PYTHON -c "import distutils" 2>&1` - if test -z "$ac_distutils_result"; then - AC_MSG_RESULT([yes]) - else - AC_MSG_RESULT([no]) - AC_MSG_WARN([cannot import Python module "distutils". -Please check your Python installation. The error was: -$ac_distutils_result]) - PYTHON_VERSION="" - no_python_devel=yes - fi -]) # AS_IF - -AS_IF([test -z "$no_python_devel"], [ - # - # Check for Python include path - # - AC_MSG_CHECKING([for Python include path]) - if test -z "$PYTHON_CPPFLAGS"; then - python_path=`$PYTHON -c "import distutils.sysconfig; \ - print (distutils.sysconfig.get_python_inc ());"` - plat_python_path=`$PYTHON -c "import distutils.sysconfig; \ - print (distutils.sysconfig.get_python_inc (plat_specific=1));"` - if test -n "${python_path}"; then - if test "${plat_python_path}" != "${python_path}"; then - python_path="-I$python_path -I$plat_python_path" - else - python_path="-I$python_path" - fi - fi - PYTHON_CPPFLAGS=$python_path - fi - AC_MSG_RESULT([$PYTHON_CPPFLAGS]) - AC_SUBST([PYTHON_CPPFLAGS]) - - # - # Check for Python library path - # - AC_MSG_CHECKING([for Python library path]) - if test -z "$PYTHON_LDFLAGS"; then - # (makes two attempts to ensure we've got a version number - # from the interpreter) - ac_python_version=`cat<]], - [[Py_Initialize();]]) - ],[pythonexists=yes],[pythonexists=no]) - AC_LANG_POP([C]) - # turn back to default flags - CPPFLAGS="$ac_save_CPPFLAGS" - LIBS="$ac_save_LIBS" - - AC_MSG_RESULT([$pythonexists]) - - if test ! "x$pythonexists" = "xyes"; then - AC_MSG_WARN([ - Could not link test program to Python. Maybe the main Python library has been - installed in some non-standard library path. If so, pass it to configure, - via the LDFLAGS environment variable. - Example: ./configure LDFLAGS="-L/usr/non-standard-path/python/lib" - ============================================================================ - ERROR! - You probably have to install the development version of the Python package - for your distribution. The exact name of this package varies among them. - ============================================================================ - ]) - PYTHON_VERSION="" - no_python_devel=yes - fi - - # - # all done! - # -]) # AS_IF - -AS_IF([test -z "$no_python_devel"], - [have_python_dev=yes], [have_python_dev=no]) - -]) # AS_IF diff --git a/m4/libtool.m4 b/m4/libtool.m4 deleted file mode 100644 index d7c043f..0000000 --- a/m4/libtool.m4 +++ /dev/null @@ -1,7997 +0,0 @@ -# libtool.m4 - Configure libtool for the host system. -*-Autoconf-*- -# -# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2003, 2004, 2005, -# 2006, 2007, 2008, 2009, 2010, 2011 Free Software -# Foundation, Inc. -# Written by Gordon Matzigkeit, 1996 -# -# This file is free software; the Free Software Foundation gives -# unlimited permission to copy and/or distribute it, with or without -# modifications, as long as this notice is preserved. - -m4_define([_LT_COPYING], [dnl -# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2003, 2004, 2005, -# 2006, 2007, 2008, 2009, 2010, 2011 Free Software -# Foundation, Inc. -# Written by Gordon Matzigkeit, 1996 -# -# This file is part of GNU Libtool. -# -# GNU Libtool 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 2 of -# the License, or (at your option) any later version. -# -# As a special exception to the GNU General Public License, -# if you distribute this file as part of a program or library that -# is built using GNU Libtool, you may include this file under the -# same distribution terms that you use for the rest of that program. -# -# GNU Libtool 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 GNU Libtool; see the file COPYING. If not, a copy -# can be downloaded from http://www.gnu.org/licenses/gpl.html, or -# obtained by writing to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -]) - -# serial 57 LT_INIT - - -# LT_PREREQ(VERSION) -# ------------------ -# Complain and exit if this libtool version is less that VERSION. -m4_defun([LT_PREREQ], -[m4_if(m4_version_compare(m4_defn([LT_PACKAGE_VERSION]), [$1]), -1, - [m4_default([$3], - [m4_fatal([Libtool version $1 or higher is required], - 63)])], - [$2])]) - - -# _LT_CHECK_BUILDDIR -# ------------------ -# Complain if the absolute build directory name contains unusual characters -m4_defun([_LT_CHECK_BUILDDIR], -[case `pwd` in - *\ * | *\ *) - AC_MSG_WARN([Libtool does not cope well with whitespace in `pwd`]) ;; -esac -]) - - -# LT_INIT([OPTIONS]) -# ------------------ -AC_DEFUN([LT_INIT], -[AC_PREREQ([2.58])dnl We use AC_INCLUDES_DEFAULT -AC_REQUIRE([AC_CONFIG_AUX_DIR_DEFAULT])dnl -AC_BEFORE([$0], [LT_LANG])dnl -AC_BEFORE([$0], [LT_OUTPUT])dnl -AC_BEFORE([$0], [LTDL_INIT])dnl -m4_require([_LT_CHECK_BUILDDIR])dnl - -dnl Autoconf doesn't catch unexpanded LT_ macros by default: -m4_pattern_forbid([^_?LT_[A-Z_]+$])dnl -m4_pattern_allow([^(_LT_EOF|LT_DLGLOBAL|LT_DLLAZY_OR_NOW|LT_MULTI_MODULE)$])dnl -dnl aclocal doesn't pull ltoptions.m4, ltsugar.m4, or ltversion.m4 -dnl unless we require an AC_DEFUNed macro: -AC_REQUIRE([LTOPTIONS_VERSION])dnl -AC_REQUIRE([LTSUGAR_VERSION])dnl -AC_REQUIRE([LTVERSION_VERSION])dnl -AC_REQUIRE([LTOBSOLETE_VERSION])dnl -m4_require([_LT_PROG_LTMAIN])dnl - -_LT_SHELL_INIT([SHELL=${CONFIG_SHELL-/bin/sh}]) - -dnl Parse OPTIONS -_LT_SET_OPTIONS([$0], [$1]) - -# This can be used to rebuild libtool when needed -LIBTOOL_DEPS="$ltmain" - -# Always use our own libtool. -LIBTOOL='$(SHELL) $(top_builddir)/libtool' -AC_SUBST(LIBTOOL)dnl - -_LT_SETUP - -# Only expand once: -m4_define([LT_INIT]) -])# LT_INIT - -# Old names: -AU_ALIAS([AC_PROG_LIBTOOL], [LT_INIT]) -AU_ALIAS([AM_PROG_LIBTOOL], [LT_INIT]) -dnl aclocal-1.4 backwards compatibility: -dnl AC_DEFUN([AC_PROG_LIBTOOL], []) -dnl AC_DEFUN([AM_PROG_LIBTOOL], []) - - -# _LT_CC_BASENAME(CC) -# ------------------- -# Calculate cc_basename. Skip known compiler wrappers and cross-prefix. -m4_defun([_LT_CC_BASENAME], -[for cc_temp in $1""; do - case $cc_temp in - compile | *[[\\/]]compile | ccache | *[[\\/]]ccache ) ;; - distcc | *[[\\/]]distcc | purify | *[[\\/]]purify ) ;; - \-*) ;; - *) break;; - esac -done -cc_basename=`$ECHO "$cc_temp" | $SED "s%.*/%%; s%^$host_alias-%%"` -]) - - -# _LT_FILEUTILS_DEFAULTS -# ---------------------- -# It is okay to use these file commands and assume they have been set -# sensibly after `m4_require([_LT_FILEUTILS_DEFAULTS])'. -m4_defun([_LT_FILEUTILS_DEFAULTS], -[: ${CP="cp -f"} -: ${MV="mv -f"} -: ${RM="rm -f"} -])# _LT_FILEUTILS_DEFAULTS - - -# _LT_SETUP -# --------- -m4_defun([_LT_SETUP], -[AC_REQUIRE([AC_CANONICAL_HOST])dnl -AC_REQUIRE([AC_CANONICAL_BUILD])dnl -AC_REQUIRE([_LT_PREPARE_SED_QUOTE_VARS])dnl -AC_REQUIRE([_LT_PROG_ECHO_BACKSLASH])dnl - -_LT_DECL([], [PATH_SEPARATOR], [1], [The PATH separator for the build system])dnl -dnl -_LT_DECL([], [host_alias], [0], [The host system])dnl -_LT_DECL([], [host], [0])dnl -_LT_DECL([], [host_os], [0])dnl -dnl -_LT_DECL([], [build_alias], [0], [The build system])dnl -_LT_DECL([], [build], [0])dnl -_LT_DECL([], [build_os], [0])dnl -dnl -AC_REQUIRE([AC_PROG_CC])dnl -AC_REQUIRE([LT_PATH_LD])dnl -AC_REQUIRE([LT_PATH_NM])dnl -dnl -AC_REQUIRE([AC_PROG_LN_S])dnl -test -z "$LN_S" && LN_S="ln -s" -_LT_DECL([], [LN_S], [1], [Whether we need soft or hard links])dnl -dnl -AC_REQUIRE([LT_CMD_MAX_LEN])dnl -_LT_DECL([objext], [ac_objext], [0], [Object file suffix (normally "o")])dnl -_LT_DECL([], [exeext], [0], [Executable file suffix (normally "")])dnl -dnl -m4_require([_LT_FILEUTILS_DEFAULTS])dnl -m4_require([_LT_CHECK_SHELL_FEATURES])dnl -m4_require([_LT_PATH_CONVERSION_FUNCTIONS])dnl -m4_require([_LT_CMD_RELOAD])dnl -m4_require([_LT_CHECK_MAGIC_METHOD])dnl -m4_require([_LT_CHECK_SHAREDLIB_FROM_LINKLIB])dnl -m4_require([_LT_CMD_OLD_ARCHIVE])dnl -m4_require([_LT_CMD_GLOBAL_SYMBOLS])dnl -m4_require([_LT_WITH_SYSROOT])dnl - -_LT_CONFIG_LIBTOOL_INIT([ -# See if we are running on zsh, and set the options which allow our -# commands through without removal of \ escapes INIT. -if test -n "\${ZSH_VERSION+set}" ; then - setopt NO_GLOB_SUBST -fi -]) -if test -n "${ZSH_VERSION+set}" ; then - setopt NO_GLOB_SUBST -fi - -_LT_CHECK_OBJDIR - -m4_require([_LT_TAG_COMPILER])dnl - -case $host_os in -aix3*) - # AIX sometimes has problems with the GCC collect2 program. For some - # reason, if we set the COLLECT_NAMES environment variable, the problems - # vanish in a puff of smoke. - if test "X${COLLECT_NAMES+set}" != Xset; then - COLLECT_NAMES= - export COLLECT_NAMES - fi - ;; -esac - -# Global variables: -ofile=libtool -can_build_shared=yes - -# All known linkers require a `.a' archive for static linking (except MSVC, -# which needs '.lib'). -libext=a - -with_gnu_ld="$lt_cv_prog_gnu_ld" - -old_CC="$CC" -old_CFLAGS="$CFLAGS" - -# Set sane defaults for various variables -test -z "$CC" && CC=cc -test -z "$LTCC" && LTCC=$CC -test -z "$LTCFLAGS" && LTCFLAGS=$CFLAGS -test -z "$LD" && LD=ld -test -z "$ac_objext" && ac_objext=o - -_LT_CC_BASENAME([$compiler]) - -# Only perform the check for file, if the check method requires it -test -z "$MAGIC_CMD" && MAGIC_CMD=file -case $deplibs_check_method in -file_magic*) - if test "$file_magic_cmd" = '$MAGIC_CMD'; then - _LT_PATH_MAGIC - fi - ;; -esac - -# Use C for the default configuration in the libtool script -LT_SUPPORTED_TAG([CC]) -_LT_LANG_C_CONFIG -_LT_LANG_DEFAULT_CONFIG -_LT_CONFIG_COMMANDS -])# _LT_SETUP - - -# _LT_PREPARE_SED_QUOTE_VARS -# -------------------------- -# Define a few sed substitution that help us do robust quoting. -m4_defun([_LT_PREPARE_SED_QUOTE_VARS], -[# Backslashify metacharacters that are still active within -# double-quoted strings. -sed_quote_subst='s/\([["`$\\]]\)/\\\1/g' - -# Same as above, but do not quote variable references. -double_quote_subst='s/\([["`\\]]\)/\\\1/g' - -# Sed substitution to delay expansion of an escaped shell variable in a -# double_quote_subst'ed string. -delay_variable_subst='s/\\\\\\\\\\\$/\\\\\\$/g' - -# Sed substitution to delay expansion of an escaped single quote. -delay_single_quote_subst='s/'\''/'\'\\\\\\\'\''/g' - -# Sed substitution to avoid accidental globbing in evaled expressions -no_glob_subst='s/\*/\\\*/g' -]) - -# _LT_PROG_LTMAIN -# --------------- -# Note that this code is called both from `configure', and `config.status' -# now that we use AC_CONFIG_COMMANDS to generate libtool. Notably, -# `config.status' has no value for ac_aux_dir unless we are using Automake, -# so we pass a copy along to make sure it has a sensible value anyway. -m4_defun([_LT_PROG_LTMAIN], -[m4_ifdef([AC_REQUIRE_AUX_FILE], [AC_REQUIRE_AUX_FILE([ltmain.sh])])dnl -_LT_CONFIG_LIBTOOL_INIT([ac_aux_dir='$ac_aux_dir']) -ltmain="$ac_aux_dir/ltmain.sh" -])# _LT_PROG_LTMAIN - - -## ------------------------------------- ## -## Accumulate code for creating libtool. ## -## ------------------------------------- ## - -# So that we can recreate a full libtool script including additional -# tags, we accumulate the chunks of code to send to AC_CONFIG_COMMANDS -# in macros and then make a single call at the end using the `libtool' -# label. - - -# _LT_CONFIG_LIBTOOL_INIT([INIT-COMMANDS]) -# ---------------------------------------- -# Register INIT-COMMANDS to be passed to AC_CONFIG_COMMANDS later. -m4_define([_LT_CONFIG_LIBTOOL_INIT], -[m4_ifval([$1], - [m4_append([_LT_OUTPUT_LIBTOOL_INIT], - [$1 -])])]) - -# Initialize. -m4_define([_LT_OUTPUT_LIBTOOL_INIT]) - - -# _LT_CONFIG_LIBTOOL([COMMANDS]) -# ------------------------------ -# Register COMMANDS to be passed to AC_CONFIG_COMMANDS later. -m4_define([_LT_CONFIG_LIBTOOL], -[m4_ifval([$1], - [m4_append([_LT_OUTPUT_LIBTOOL_COMMANDS], - [$1 -])])]) - -# Initialize. -m4_define([_LT_OUTPUT_LIBTOOL_COMMANDS]) - - -# _LT_CONFIG_SAVE_COMMANDS([COMMANDS], [INIT_COMMANDS]) -# ----------------------------------------------------- -m4_defun([_LT_CONFIG_SAVE_COMMANDS], -[_LT_CONFIG_LIBTOOL([$1]) -_LT_CONFIG_LIBTOOL_INIT([$2]) -]) - - -# _LT_FORMAT_COMMENT([COMMENT]) -# ----------------------------- -# Add leading comment marks to the start of each line, and a trailing -# full-stop to the whole comment if one is not present already. -m4_define([_LT_FORMAT_COMMENT], -[m4_ifval([$1], [ -m4_bpatsubst([m4_bpatsubst([$1], [^ *], [# ])], - [['`$\]], [\\\&])]m4_bmatch([$1], [[!?.]$], [], [.]) -)]) - - - -## ------------------------ ## -## FIXME: Eliminate VARNAME ## -## ------------------------ ## - - -# _LT_DECL([CONFIGNAME], VARNAME, VALUE, [DESCRIPTION], [IS-TAGGED?]) -# ------------------------------------------------------------------- -# CONFIGNAME is the name given to the value in the libtool script. -# VARNAME is the (base) name used in the configure script. -# VALUE may be 0, 1 or 2 for a computed quote escaped value based on -# VARNAME. Any other value will be used directly. -m4_define([_LT_DECL], -[lt_if_append_uniq([lt_decl_varnames], [$2], [, ], - [lt_dict_add_subkey([lt_decl_dict], [$2], [libtool_name], - [m4_ifval([$1], [$1], [$2])]) - lt_dict_add_subkey([lt_decl_dict], [$2], [value], [$3]) - m4_ifval([$4], - [lt_dict_add_subkey([lt_decl_dict], [$2], [description], [$4])]) - lt_dict_add_subkey([lt_decl_dict], [$2], - [tagged?], [m4_ifval([$5], [yes], [no])])]) -]) - - -# _LT_TAGDECL([CONFIGNAME], VARNAME, VALUE, [DESCRIPTION]) -# -------------------------------------------------------- -m4_define([_LT_TAGDECL], [_LT_DECL([$1], [$2], [$3], [$4], [yes])]) - - -# lt_decl_tag_varnames([SEPARATOR], [VARNAME1...]) -# ------------------------------------------------ -m4_define([lt_decl_tag_varnames], -[_lt_decl_filter([tagged?], [yes], $@)]) - - -# _lt_decl_filter(SUBKEY, VALUE, [SEPARATOR], [VARNAME1..]) -# --------------------------------------------------------- -m4_define([_lt_decl_filter], -[m4_case([$#], - [0], [m4_fatal([$0: too few arguments: $#])], - [1], [m4_fatal([$0: too few arguments: $#: $1])], - [2], [lt_dict_filter([lt_decl_dict], [$1], [$2], [], lt_decl_varnames)], - [3], [lt_dict_filter([lt_decl_dict], [$1], [$2], [$3], lt_decl_varnames)], - [lt_dict_filter([lt_decl_dict], $@)])[]dnl -]) - - -# lt_decl_quote_varnames([SEPARATOR], [VARNAME1...]) -# -------------------------------------------------- -m4_define([lt_decl_quote_varnames], -[_lt_decl_filter([value], [1], $@)]) - - -# lt_decl_dquote_varnames([SEPARATOR], [VARNAME1...]) -# --------------------------------------------------- -m4_define([lt_decl_dquote_varnames], -[_lt_decl_filter([value], [2], $@)]) - - -# lt_decl_varnames_tagged([SEPARATOR], [VARNAME1...]) -# --------------------------------------------------- -m4_define([lt_decl_varnames_tagged], -[m4_assert([$# <= 2])dnl -_$0(m4_quote(m4_default([$1], [[, ]])), - m4_ifval([$2], [[$2]], [m4_dquote(lt_decl_tag_varnames)]), - m4_split(m4_normalize(m4_quote(_LT_TAGS)), [ ]))]) -m4_define([_lt_decl_varnames_tagged], -[m4_ifval([$3], [lt_combine([$1], [$2], [_], $3)])]) - - -# lt_decl_all_varnames([SEPARATOR], [VARNAME1...]) -# ------------------------------------------------ -m4_define([lt_decl_all_varnames], -[_$0(m4_quote(m4_default([$1], [[, ]])), - m4_if([$2], [], - m4_quote(lt_decl_varnames), - m4_quote(m4_shift($@))))[]dnl -]) -m4_define([_lt_decl_all_varnames], -[lt_join($@, lt_decl_varnames_tagged([$1], - lt_decl_tag_varnames([[, ]], m4_shift($@))))dnl -]) - - -# _LT_CONFIG_STATUS_DECLARE([VARNAME]) -# ------------------------------------ -# Quote a variable value, and forward it to `config.status' so that its -# declaration there will have the same value as in `configure'. VARNAME -# must have a single quote delimited value for this to work. -m4_define([_LT_CONFIG_STATUS_DECLARE], -[$1='`$ECHO "$][$1" | $SED "$delay_single_quote_subst"`']) - - -# _LT_CONFIG_STATUS_DECLARATIONS -# ------------------------------ -# We delimit libtool config variables with single quotes, so when -# we write them to config.status, we have to be sure to quote all -# embedded single quotes properly. In configure, this macro expands -# each variable declared with _LT_DECL (and _LT_TAGDECL) into: -# -# ='`$ECHO "$" | $SED "$delay_single_quote_subst"`' -m4_defun([_LT_CONFIG_STATUS_DECLARATIONS], -[m4_foreach([_lt_var], m4_quote(lt_decl_all_varnames), - [m4_n([_LT_CONFIG_STATUS_DECLARE(_lt_var)])])]) - - -# _LT_LIBTOOL_TAGS -# ---------------- -# Output comment and list of tags supported by the script -m4_defun([_LT_LIBTOOL_TAGS], -[_LT_FORMAT_COMMENT([The names of the tagged configurations supported by this script])dnl -available_tags="_LT_TAGS"dnl -]) - - -# _LT_LIBTOOL_DECLARE(VARNAME, [TAG]) -# ----------------------------------- -# Extract the dictionary values for VARNAME (optionally with TAG) and -# expand to a commented shell variable setting: -# -# # Some comment about what VAR is for. -# visible_name=$lt_internal_name -m4_define([_LT_LIBTOOL_DECLARE], -[_LT_FORMAT_COMMENT(m4_quote(lt_dict_fetch([lt_decl_dict], [$1], - [description])))[]dnl -m4_pushdef([_libtool_name], - m4_quote(lt_dict_fetch([lt_decl_dict], [$1], [libtool_name])))[]dnl -m4_case(m4_quote(lt_dict_fetch([lt_decl_dict], [$1], [value])), - [0], [_libtool_name=[$]$1], - [1], [_libtool_name=$lt_[]$1], - [2], [_libtool_name=$lt_[]$1], - [_libtool_name=lt_dict_fetch([lt_decl_dict], [$1], [value])])[]dnl -m4_ifval([$2], [_$2])[]m4_popdef([_libtool_name])[]dnl -]) - - -# _LT_LIBTOOL_CONFIG_VARS -# ----------------------- -# Produce commented declarations of non-tagged libtool config variables -# suitable for insertion in the LIBTOOL CONFIG section of the `libtool' -# script. Tagged libtool config variables (even for the LIBTOOL CONFIG -# section) are produced by _LT_LIBTOOL_TAG_VARS. -m4_defun([_LT_LIBTOOL_CONFIG_VARS], -[m4_foreach([_lt_var], - m4_quote(_lt_decl_filter([tagged?], [no], [], lt_decl_varnames)), - [m4_n([_LT_LIBTOOL_DECLARE(_lt_var)])])]) - - -# _LT_LIBTOOL_TAG_VARS(TAG) -# ------------------------- -m4_define([_LT_LIBTOOL_TAG_VARS], -[m4_foreach([_lt_var], m4_quote(lt_decl_tag_varnames), - [m4_n([_LT_LIBTOOL_DECLARE(_lt_var, [$1])])])]) - - -# _LT_TAGVAR(VARNAME, [TAGNAME]) -# ------------------------------ -m4_define([_LT_TAGVAR], [m4_ifval([$2], [$1_$2], [$1])]) - - -# _LT_CONFIG_COMMANDS -# ------------------- -# Send accumulated output to $CONFIG_STATUS. Thanks to the lists of -# variables for single and double quote escaping we saved from calls -# to _LT_DECL, we can put quote escaped variables declarations -# into `config.status', and then the shell code to quote escape them in -# for loops in `config.status'. Finally, any additional code accumulated -# from calls to _LT_CONFIG_LIBTOOL_INIT is expanded. -m4_defun([_LT_CONFIG_COMMANDS], -[AC_PROVIDE_IFELSE([LT_OUTPUT], - dnl If the libtool generation code has been placed in $CONFIG_LT, - dnl instead of duplicating it all over again into config.status, - dnl then we will have config.status run $CONFIG_LT later, so it - dnl needs to know what name is stored there: - [AC_CONFIG_COMMANDS([libtool], - [$SHELL $CONFIG_LT || AS_EXIT(1)], [CONFIG_LT='$CONFIG_LT'])], - dnl If the libtool generation code is destined for config.status, - dnl expand the accumulated commands and init code now: - [AC_CONFIG_COMMANDS([libtool], - [_LT_OUTPUT_LIBTOOL_COMMANDS], [_LT_OUTPUT_LIBTOOL_COMMANDS_INIT])]) -])#_LT_CONFIG_COMMANDS - - -# Initialize. -m4_define([_LT_OUTPUT_LIBTOOL_COMMANDS_INIT], -[ - -# The HP-UX ksh and POSIX shell print the target directory to stdout -# if CDPATH is set. -(unset CDPATH) >/dev/null 2>&1 && unset CDPATH - -sed_quote_subst='$sed_quote_subst' -double_quote_subst='$double_quote_subst' -delay_variable_subst='$delay_variable_subst' -_LT_CONFIG_STATUS_DECLARATIONS -LTCC='$LTCC' -LTCFLAGS='$LTCFLAGS' -compiler='$compiler_DEFAULT' - -# A function that is used when there is no print builtin or printf. -func_fallback_echo () -{ - eval 'cat <<_LTECHO_EOF -\$[]1 -_LTECHO_EOF' -} - -# Quote evaled strings. -for var in lt_decl_all_varnames([[ \ -]], lt_decl_quote_varnames); do - case \`eval \\\\\$ECHO \\\\""\\\\\$\$var"\\\\"\` in - *[[\\\\\\\`\\"\\\$]]*) - eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"\\\$\$var\\" | \\\$SED \\"\\\$sed_quote_subst\\"\\\`\\\\\\"" - ;; - *) - eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\"" - ;; - esac -done - -# Double-quote double-evaled strings. -for var in lt_decl_all_varnames([[ \ -]], lt_decl_dquote_varnames); do - case \`eval \\\\\$ECHO \\\\""\\\\\$\$var"\\\\"\` in - *[[\\\\\\\`\\"\\\$]]*) - eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"\\\$\$var\\" | \\\$SED -e \\"\\\$double_quote_subst\\" -e \\"\\\$sed_quote_subst\\" -e \\"\\\$delay_variable_subst\\"\\\`\\\\\\"" - ;; - *) - eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\"" - ;; - esac -done - -_LT_OUTPUT_LIBTOOL_INIT -]) - -# _LT_GENERATED_FILE_INIT(FILE, [COMMENT]) -# ------------------------------------ -# Generate a child script FILE with all initialization necessary to -# reuse the environment learned by the parent script, and make the -# file executable. If COMMENT is supplied, it is inserted after the -# `#!' sequence but before initialization text begins. After this -# macro, additional text can be appended to FILE to form the body of -# the child script. The macro ends with non-zero status if the -# file could not be fully written (such as if the disk is full). -m4_ifdef([AS_INIT_GENERATED], -[m4_defun([_LT_GENERATED_FILE_INIT],[AS_INIT_GENERATED($@)])], -[m4_defun([_LT_GENERATED_FILE_INIT], -[m4_require([AS_PREPARE])]dnl -[m4_pushdef([AS_MESSAGE_LOG_FD])]dnl -[lt_write_fail=0 -cat >$1 <<_ASEOF || lt_write_fail=1 -#! $SHELL -# Generated by $as_me. -$2 -SHELL=\${CONFIG_SHELL-$SHELL} -export SHELL -_ASEOF -cat >>$1 <<\_ASEOF || lt_write_fail=1 -AS_SHELL_SANITIZE -_AS_PREPARE -exec AS_MESSAGE_FD>&1 -_ASEOF -test $lt_write_fail = 0 && chmod +x $1[]dnl -m4_popdef([AS_MESSAGE_LOG_FD])])])# _LT_GENERATED_FILE_INIT - -# LT_OUTPUT -# --------- -# This macro allows early generation of the libtool script (before -# AC_OUTPUT is called), incase it is used in configure for compilation -# tests. -AC_DEFUN([LT_OUTPUT], -[: ${CONFIG_LT=./config.lt} -AC_MSG_NOTICE([creating $CONFIG_LT]) -_LT_GENERATED_FILE_INIT(["$CONFIG_LT"], -[# Run this file to recreate a libtool stub with the current configuration.]) - -cat >>"$CONFIG_LT" <<\_LTEOF -lt_cl_silent=false -exec AS_MESSAGE_LOG_FD>>config.log -{ - echo - AS_BOX([Running $as_me.]) -} >&AS_MESSAGE_LOG_FD - -lt_cl_help="\ -\`$as_me' creates a local libtool stub from the current configuration, -for use in further configure time tests before the real libtool is -generated. - -Usage: $[0] [[OPTIONS]] - - -h, --help print this help, then exit - -V, --version print version number, then exit - -q, --quiet do not print progress messages - -d, --debug don't remove temporary files - -Report bugs to ." - -lt_cl_version="\ -m4_ifset([AC_PACKAGE_NAME], [AC_PACKAGE_NAME ])config.lt[]dnl -m4_ifset([AC_PACKAGE_VERSION], [ AC_PACKAGE_VERSION]) -configured by $[0], generated by m4_PACKAGE_STRING. - -Copyright (C) 2011 Free Software Foundation, Inc. -This config.lt script is free software; the Free Software Foundation -gives unlimited permision to copy, distribute and modify it." - -while test $[#] != 0 -do - case $[1] in - --version | --v* | -V ) - echo "$lt_cl_version"; exit 0 ;; - --help | --h* | -h ) - echo "$lt_cl_help"; exit 0 ;; - --debug | --d* | -d ) - debug=: ;; - --quiet | --q* | --silent | --s* | -q ) - lt_cl_silent=: ;; - - -*) AC_MSG_ERROR([unrecognized option: $[1] -Try \`$[0] --help' for more information.]) ;; - - *) AC_MSG_ERROR([unrecognized argument: $[1] -Try \`$[0] --help' for more information.]) ;; - esac - shift -done - -if $lt_cl_silent; then - exec AS_MESSAGE_FD>/dev/null -fi -_LTEOF - -cat >>"$CONFIG_LT" <<_LTEOF -_LT_OUTPUT_LIBTOOL_COMMANDS_INIT -_LTEOF - -cat >>"$CONFIG_LT" <<\_LTEOF -AC_MSG_NOTICE([creating $ofile]) -_LT_OUTPUT_LIBTOOL_COMMANDS -AS_EXIT(0) -_LTEOF -chmod +x "$CONFIG_LT" - -# configure is writing to config.log, but config.lt does its own redirection, -# appending to config.log, which fails on DOS, as config.log is still kept -# open by configure. Here we exec the FD to /dev/null, effectively closing -# config.log, so it can be properly (re)opened and appended to by config.lt. -lt_cl_success=: -test "$silent" = yes && - lt_config_lt_args="$lt_config_lt_args --quiet" -exec AS_MESSAGE_LOG_FD>/dev/null -$SHELL "$CONFIG_LT" $lt_config_lt_args || lt_cl_success=false -exec AS_MESSAGE_LOG_FD>>config.log -$lt_cl_success || AS_EXIT(1) -])# LT_OUTPUT - - -# _LT_CONFIG(TAG) -# --------------- -# If TAG is the built-in tag, create an initial libtool script with a -# default configuration from the untagged config vars. Otherwise add code -# to config.status for appending the configuration named by TAG from the -# matching tagged config vars. -m4_defun([_LT_CONFIG], -[m4_require([_LT_FILEUTILS_DEFAULTS])dnl -_LT_CONFIG_SAVE_COMMANDS([ - m4_define([_LT_TAG], m4_if([$1], [], [C], [$1]))dnl - m4_if(_LT_TAG, [C], [ - # See if we are running on zsh, and set the options which allow our - # commands through without removal of \ escapes. - if test -n "${ZSH_VERSION+set}" ; then - setopt NO_GLOB_SUBST - fi - - cfgfile="${ofile}T" - trap "$RM \"$cfgfile\"; exit 1" 1 2 15 - $RM "$cfgfile" - - cat <<_LT_EOF >> "$cfgfile" -#! $SHELL - -# `$ECHO "$ofile" | sed 's%^.*/%%'` - Provide generalized library-building support services. -# Generated automatically by $as_me ($PACKAGE$TIMESTAMP) $VERSION -# Libtool was configured on host `(hostname || uname -n) 2>/dev/null | sed 1q`: -# NOTE: Changes made to this file will be lost: look at ltmain.sh. -# -_LT_COPYING -_LT_LIBTOOL_TAGS - -# ### BEGIN LIBTOOL CONFIG -_LT_LIBTOOL_CONFIG_VARS -_LT_LIBTOOL_TAG_VARS -# ### END LIBTOOL CONFIG - -_LT_EOF - - case $host_os in - aix3*) - cat <<\_LT_EOF >> "$cfgfile" -# AIX sometimes has problems with the GCC collect2 program. For some -# reason, if we set the COLLECT_NAMES environment variable, the problems -# vanish in a puff of smoke. -if test "X${COLLECT_NAMES+set}" != Xset; then - COLLECT_NAMES= - export COLLECT_NAMES -fi -_LT_EOF - ;; - esac - - _LT_PROG_LTMAIN - - # We use sed instead of cat because bash on DJGPP gets confused if - # if finds mixed CR/LF and LF-only lines. Since sed operates in - # text mode, it properly converts lines to CR/LF. This bash problem - # is reportedly fixed, but why not run on old versions too? - sed '$q' "$ltmain" >> "$cfgfile" \ - || (rm -f "$cfgfile"; exit 1) - - _LT_PROG_REPLACE_SHELLFNS - - mv -f "$cfgfile" "$ofile" || - (rm -f "$ofile" && cp "$cfgfile" "$ofile" && rm -f "$cfgfile") - chmod +x "$ofile" -], -[cat <<_LT_EOF >> "$ofile" - -dnl Unfortunately we have to use $1 here, since _LT_TAG is not expanded -dnl in a comment (ie after a #). -# ### BEGIN LIBTOOL TAG CONFIG: $1 -_LT_LIBTOOL_TAG_VARS(_LT_TAG) -# ### END LIBTOOL TAG CONFIG: $1 -_LT_EOF -])dnl /m4_if -], -[m4_if([$1], [], [ - PACKAGE='$PACKAGE' - VERSION='$VERSION' - TIMESTAMP='$TIMESTAMP' - RM='$RM' - ofile='$ofile'], []) -])dnl /_LT_CONFIG_SAVE_COMMANDS -])# _LT_CONFIG - - -# LT_SUPPORTED_TAG(TAG) -# --------------------- -# Trace this macro to discover what tags are supported by the libtool -# --tag option, using: -# autoconf --trace 'LT_SUPPORTED_TAG:$1' -AC_DEFUN([LT_SUPPORTED_TAG], []) - - -# C support is built-in for now -m4_define([_LT_LANG_C_enabled], []) -m4_define([_LT_TAGS], []) - - -# LT_LANG(LANG) -# ------------- -# Enable libtool support for the given language if not already enabled. -AC_DEFUN([LT_LANG], -[AC_BEFORE([$0], [LT_OUTPUT])dnl -m4_case([$1], - [C], [_LT_LANG(C)], - [C++], [_LT_LANG(CXX)], - [Go], [_LT_LANG(GO)], - [Java], [_LT_LANG(GCJ)], - [Fortran 77], [_LT_LANG(F77)], - [Fortran], [_LT_LANG(FC)], - [Windows Resource], [_LT_LANG(RC)], - [m4_ifdef([_LT_LANG_]$1[_CONFIG], - [_LT_LANG($1)], - [m4_fatal([$0: unsupported language: "$1"])])])dnl -])# LT_LANG - - -# _LT_LANG(LANGNAME) -# ------------------ -m4_defun([_LT_LANG], -[m4_ifdef([_LT_LANG_]$1[_enabled], [], - [LT_SUPPORTED_TAG([$1])dnl - m4_append([_LT_TAGS], [$1 ])dnl - m4_define([_LT_LANG_]$1[_enabled], [])dnl - _LT_LANG_$1_CONFIG($1)])dnl -])# _LT_LANG - - -m4_ifndef([AC_PROG_GO], [ -############################################################ -# NOTE: This macro has been submitted for inclusion into # -# GNU Autoconf as AC_PROG_GO. When it is available in # -# a released version of Autoconf we should remove this # -# macro and use it instead. # -############################################################ -m4_defun([AC_PROG_GO], -[AC_LANG_PUSH(Go)dnl -AC_ARG_VAR([GOC], [Go compiler command])dnl -AC_ARG_VAR([GOFLAGS], [Go compiler flags])dnl -_AC_ARG_VAR_LDFLAGS()dnl -AC_CHECK_TOOL(GOC, gccgo) -if test -z "$GOC"; then - if test -n "$ac_tool_prefix"; then - AC_CHECK_PROG(GOC, [${ac_tool_prefix}gccgo], [${ac_tool_prefix}gccgo]) - fi -fi -if test -z "$GOC"; then - AC_CHECK_PROG(GOC, gccgo, gccgo, false) -fi -])#m4_defun -])#m4_ifndef - - -# _LT_LANG_DEFAULT_CONFIG -# ----------------------- -m4_defun([_LT_LANG_DEFAULT_CONFIG], -[AC_PROVIDE_IFELSE([AC_PROG_CXX], - [LT_LANG(CXX)], - [m4_define([AC_PROG_CXX], defn([AC_PROG_CXX])[LT_LANG(CXX)])]) - -AC_PROVIDE_IFELSE([AC_PROG_F77], - [LT_LANG(F77)], - [m4_define([AC_PROG_F77], defn([AC_PROG_F77])[LT_LANG(F77)])]) - -AC_PROVIDE_IFELSE([AC_PROG_FC], - [LT_LANG(FC)], - [m4_define([AC_PROG_FC], defn([AC_PROG_FC])[LT_LANG(FC)])]) - -dnl The call to [A][M_PROG_GCJ] is quoted like that to stop aclocal -dnl pulling things in needlessly. -AC_PROVIDE_IFELSE([AC_PROG_GCJ], - [LT_LANG(GCJ)], - [AC_PROVIDE_IFELSE([A][M_PROG_GCJ], - [LT_LANG(GCJ)], - [AC_PROVIDE_IFELSE([LT_PROG_GCJ], - [LT_LANG(GCJ)], - [m4_ifdef([AC_PROG_GCJ], - [m4_define([AC_PROG_GCJ], defn([AC_PROG_GCJ])[LT_LANG(GCJ)])]) - m4_ifdef([A][M_PROG_GCJ], - [m4_define([A][M_PROG_GCJ], defn([A][M_PROG_GCJ])[LT_LANG(GCJ)])]) - m4_ifdef([LT_PROG_GCJ], - [m4_define([LT_PROG_GCJ], defn([LT_PROG_GCJ])[LT_LANG(GCJ)])])])])]) - -AC_PROVIDE_IFELSE([AC_PROG_GO], - [LT_LANG(GO)], - [m4_define([AC_PROG_GO], defn([AC_PROG_GO])[LT_LANG(GO)])]) - -AC_PROVIDE_IFELSE([LT_PROG_RC], - [LT_LANG(RC)], - [m4_define([LT_PROG_RC], defn([LT_PROG_RC])[LT_LANG(RC)])]) -])# _LT_LANG_DEFAULT_CONFIG - -# Obsolete macros: -AU_DEFUN([AC_LIBTOOL_CXX], [LT_LANG(C++)]) -AU_DEFUN([AC_LIBTOOL_F77], [LT_LANG(Fortran 77)]) -AU_DEFUN([AC_LIBTOOL_FC], [LT_LANG(Fortran)]) -AU_DEFUN([AC_LIBTOOL_GCJ], [LT_LANG(Java)]) -AU_DEFUN([AC_LIBTOOL_RC], [LT_LANG(Windows Resource)]) -dnl aclocal-1.4 backwards compatibility: -dnl AC_DEFUN([AC_LIBTOOL_CXX], []) -dnl AC_DEFUN([AC_LIBTOOL_F77], []) -dnl AC_DEFUN([AC_LIBTOOL_FC], []) -dnl AC_DEFUN([AC_LIBTOOL_GCJ], []) -dnl AC_DEFUN([AC_LIBTOOL_RC], []) - - -# _LT_TAG_COMPILER -# ---------------- -m4_defun([_LT_TAG_COMPILER], -[AC_REQUIRE([AC_PROG_CC])dnl - -_LT_DECL([LTCC], [CC], [1], [A C compiler])dnl -_LT_DECL([LTCFLAGS], [CFLAGS], [1], [LTCC compiler flags])dnl -_LT_TAGDECL([CC], [compiler], [1], [A language specific compiler])dnl -_LT_TAGDECL([with_gcc], [GCC], [0], [Is the compiler the GNU compiler?])dnl - -# If no C compiler was specified, use CC. -LTCC=${LTCC-"$CC"} - -# If no C compiler flags were specified, use CFLAGS. -LTCFLAGS=${LTCFLAGS-"$CFLAGS"} - -# Allow CC to be a program name with arguments. -compiler=$CC -])# _LT_TAG_COMPILER - - -# _LT_COMPILER_BOILERPLATE -# ------------------------ -# Check for compiler boilerplate output or warnings with -# the simple compiler test code. -m4_defun([_LT_COMPILER_BOILERPLATE], -[m4_require([_LT_DECL_SED])dnl -ac_outfile=conftest.$ac_objext -echo "$lt_simple_compile_test_code" >conftest.$ac_ext -eval "$ac_compile" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err -_lt_compiler_boilerplate=`cat conftest.err` -$RM conftest* -])# _LT_COMPILER_BOILERPLATE - - -# _LT_LINKER_BOILERPLATE -# ---------------------- -# Check for linker boilerplate output or warnings with -# the simple link test code. -m4_defun([_LT_LINKER_BOILERPLATE], -[m4_require([_LT_DECL_SED])dnl -ac_outfile=conftest.$ac_objext -echo "$lt_simple_link_test_code" >conftest.$ac_ext -eval "$ac_link" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err -_lt_linker_boilerplate=`cat conftest.err` -$RM -r conftest* -])# _LT_LINKER_BOILERPLATE - -# _LT_REQUIRED_DARWIN_CHECKS -# ------------------------- -m4_defun_once([_LT_REQUIRED_DARWIN_CHECKS],[ - case $host_os in - rhapsody* | darwin*) - AC_CHECK_TOOL([DSYMUTIL], [dsymutil], [:]) - AC_CHECK_TOOL([NMEDIT], [nmedit], [:]) - AC_CHECK_TOOL([LIPO], [lipo], [:]) - AC_CHECK_TOOL([OTOOL], [otool], [:]) - AC_CHECK_TOOL([OTOOL64], [otool64], [:]) - _LT_DECL([], [DSYMUTIL], [1], - [Tool to manipulate archived DWARF debug symbol files on Mac OS X]) - _LT_DECL([], [NMEDIT], [1], - [Tool to change global to local symbols on Mac OS X]) - _LT_DECL([], [LIPO], [1], - [Tool to manipulate fat objects and archives on Mac OS X]) - _LT_DECL([], [OTOOL], [1], - [ldd/readelf like tool for Mach-O binaries on Mac OS X]) - _LT_DECL([], [OTOOL64], [1], - [ldd/readelf like tool for 64 bit Mach-O binaries on Mac OS X 10.4]) - - AC_CACHE_CHECK([for -single_module linker flag],[lt_cv_apple_cc_single_mod], - [lt_cv_apple_cc_single_mod=no - if test -z "${LT_MULTI_MODULE}"; then - # By default we will add the -single_module flag. You can override - # by either setting the environment variable LT_MULTI_MODULE - # non-empty at configure time, or by adding -multi_module to the - # link flags. - rm -rf libconftest.dylib* - echo "int foo(void){return 1;}" > conftest.c - echo "$LTCC $LTCFLAGS $LDFLAGS -o libconftest.dylib \ --dynamiclib -Wl,-single_module conftest.c" >&AS_MESSAGE_LOG_FD - $LTCC $LTCFLAGS $LDFLAGS -o libconftest.dylib \ - -dynamiclib -Wl,-single_module conftest.c 2>conftest.err - _lt_result=$? - # If there is a non-empty error log, and "single_module" - # appears in it, assume the flag caused a linker warning - if test -s conftest.err && $GREP single_module conftest.err; then - cat conftest.err >&AS_MESSAGE_LOG_FD - # Otherwise, if the output was created with a 0 exit code from - # the compiler, it worked. - elif test -f libconftest.dylib && test $_lt_result -eq 0; then - lt_cv_apple_cc_single_mod=yes - else - cat conftest.err >&AS_MESSAGE_LOG_FD - fi - rm -rf libconftest.dylib* - rm -f conftest.* - fi]) - - AC_CACHE_CHECK([for -exported_symbols_list linker flag], - [lt_cv_ld_exported_symbols_list], - [lt_cv_ld_exported_symbols_list=no - save_LDFLAGS=$LDFLAGS - echo "_main" > conftest.sym - LDFLAGS="$LDFLAGS -Wl,-exported_symbols_list,conftest.sym" - AC_LINK_IFELSE([AC_LANG_PROGRAM([],[])], - [lt_cv_ld_exported_symbols_list=yes], - [lt_cv_ld_exported_symbols_list=no]) - LDFLAGS="$save_LDFLAGS" - ]) - - AC_CACHE_CHECK([for -force_load linker flag],[lt_cv_ld_force_load], - [lt_cv_ld_force_load=no - cat > conftest.c << _LT_EOF -int forced_loaded() { return 2;} -_LT_EOF - echo "$LTCC $LTCFLAGS -c -o conftest.o conftest.c" >&AS_MESSAGE_LOG_FD - $LTCC $LTCFLAGS -c -o conftest.o conftest.c 2>&AS_MESSAGE_LOG_FD - echo "$AR cru libconftest.a conftest.o" >&AS_MESSAGE_LOG_FD - $AR cru libconftest.a conftest.o 2>&AS_MESSAGE_LOG_FD - echo "$RANLIB libconftest.a" >&AS_MESSAGE_LOG_FD - $RANLIB libconftest.a 2>&AS_MESSAGE_LOG_FD - cat > conftest.c << _LT_EOF -int main() { return 0;} -_LT_EOF - echo "$LTCC $LTCFLAGS $LDFLAGS -o conftest conftest.c -Wl,-force_load,./libconftest.a" >&AS_MESSAGE_LOG_FD - $LTCC $LTCFLAGS $LDFLAGS -o conftest conftest.c -Wl,-force_load,./libconftest.a 2>conftest.err - _lt_result=$? - if test -s conftest.err && $GREP force_load conftest.err; then - cat conftest.err >&AS_MESSAGE_LOG_FD - elif test -f conftest && test $_lt_result -eq 0 && $GREP forced_load conftest >/dev/null 2>&1 ; then - lt_cv_ld_force_load=yes - else - cat conftest.err >&AS_MESSAGE_LOG_FD - fi - rm -f conftest.err libconftest.a conftest conftest.c - rm -rf conftest.dSYM - ]) - case $host_os in - rhapsody* | darwin1.[[012]]) - _lt_dar_allow_undefined='${wl}-undefined ${wl}suppress' ;; - darwin1.*) - _lt_dar_allow_undefined='${wl}-flat_namespace ${wl}-undefined ${wl}suppress' ;; - darwin*) # darwin 5.x on - # if running on 10.5 or later, the deployment target defaults - # to the OS version, if on x86, and 10.4, the deployment - # target defaults to 10.4. Don't you love it? - case ${MACOSX_DEPLOYMENT_TARGET-10.0},$host in - 10.0,*86*-darwin8*|10.0,*-darwin[[91]]*) - _lt_dar_allow_undefined='${wl}-undefined ${wl}dynamic_lookup' ;; - 10.[[012]]*) - _lt_dar_allow_undefined='${wl}-flat_namespace ${wl}-undefined ${wl}suppress' ;; - 10.*) - _lt_dar_allow_undefined='${wl}-undefined ${wl}dynamic_lookup' ;; - esac - ;; - esac - if test "$lt_cv_apple_cc_single_mod" = "yes"; then - _lt_dar_single_mod='$single_module' - fi - if test "$lt_cv_ld_exported_symbols_list" = "yes"; then - _lt_dar_export_syms=' ${wl}-exported_symbols_list,$output_objdir/${libname}-symbols.expsym' - else - _lt_dar_export_syms='~$NMEDIT -s $output_objdir/${libname}-symbols.expsym ${lib}' - fi - if test "$DSYMUTIL" != ":" && test "$lt_cv_ld_force_load" = "no"; then - _lt_dsymutil='~$DSYMUTIL $lib || :' - else - _lt_dsymutil= - fi - ;; - esac -]) - - -# _LT_DARWIN_LINKER_FEATURES([TAG]) -# --------------------------------- -# Checks for linker and compiler features on darwin -m4_defun([_LT_DARWIN_LINKER_FEATURES], -[ - m4_require([_LT_REQUIRED_DARWIN_CHECKS]) - _LT_TAGVAR(archive_cmds_need_lc, $1)=no - _LT_TAGVAR(hardcode_direct, $1)=no - _LT_TAGVAR(hardcode_automatic, $1)=yes - _LT_TAGVAR(hardcode_shlibpath_var, $1)=unsupported - if test "$lt_cv_ld_force_load" = "yes"; then - _LT_TAGVAR(whole_archive_flag_spec, $1)='`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience ${wl}-force_load,$conv\"; done; func_echo_all \"$new_convenience\"`' - m4_case([$1], [F77], [_LT_TAGVAR(compiler_needs_object, $1)=yes], - [FC], [_LT_TAGVAR(compiler_needs_object, $1)=yes]) - else - _LT_TAGVAR(whole_archive_flag_spec, $1)='' - fi - _LT_TAGVAR(link_all_deplibs, $1)=yes - _LT_TAGVAR(allow_undefined_flag, $1)="$_lt_dar_allow_undefined" - case $cc_basename in - ifort*) _lt_dar_can_shared=yes ;; - *) _lt_dar_can_shared=$GCC ;; - esac - if test "$_lt_dar_can_shared" = "yes"; then - output_verbose_link_cmd=func_echo_all - _LT_TAGVAR(archive_cmds, $1)="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod${_lt_dsymutil}" - _LT_TAGVAR(module_cmds, $1)="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dsymutil}" - _LT_TAGVAR(archive_expsym_cmds, $1)="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring ${_lt_dar_single_mod}${_lt_dar_export_syms}${_lt_dsymutil}" - _LT_TAGVAR(module_expsym_cmds, $1)="sed -e 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dar_export_syms}${_lt_dsymutil}" - m4_if([$1], [CXX], -[ if test "$lt_cv_apple_cc_single_mod" != "yes"; then - _LT_TAGVAR(archive_cmds, $1)="\$CC -r -keep_private_externs -nostdlib -o \${lib}-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \${lib}-master.o \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring${_lt_dsymutil}" - _LT_TAGVAR(archive_expsym_cmds, $1)="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -r -keep_private_externs -nostdlib -o \${lib}-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \${lib}-master.o \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring${_lt_dar_export_syms}${_lt_dsymutil}" - fi -],[]) - else - _LT_TAGVAR(ld_shlibs, $1)=no - fi -]) - -# _LT_SYS_MODULE_PATH_AIX([TAGNAME]) -# ---------------------------------- -# Links a minimal program and checks the executable -# for the system default hardcoded library path. In most cases, -# this is /usr/lib:/lib, but when the MPI compilers are used -# the location of the communication and MPI libs are included too. -# If we don't find anything, use the default library path according -# to the aix ld manual. -# Store the results from the different compilers for each TAGNAME. -# Allow to override them for all tags through lt_cv_aix_libpath. -m4_defun([_LT_SYS_MODULE_PATH_AIX], -[m4_require([_LT_DECL_SED])dnl -if test "${lt_cv_aix_libpath+set}" = set; then - aix_libpath=$lt_cv_aix_libpath -else - AC_CACHE_VAL([_LT_TAGVAR([lt_cv_aix_libpath_], [$1])], - [AC_LINK_IFELSE([AC_LANG_PROGRAM],[ - lt_aix_libpath_sed='[ - /Import File Strings/,/^$/ { - /^0/ { - s/^0 *\([^ ]*\) *$/\1/ - p - } - }]' - _LT_TAGVAR([lt_cv_aix_libpath_], [$1])=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` - # Check for a 64-bit object if we didn't find anything. - if test -z "$_LT_TAGVAR([lt_cv_aix_libpath_], [$1])"; then - _LT_TAGVAR([lt_cv_aix_libpath_], [$1])=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` - fi],[]) - if test -z "$_LT_TAGVAR([lt_cv_aix_libpath_], [$1])"; then - _LT_TAGVAR([lt_cv_aix_libpath_], [$1])="/usr/lib:/lib" - fi - ]) - aix_libpath=$_LT_TAGVAR([lt_cv_aix_libpath_], [$1]) -fi -])# _LT_SYS_MODULE_PATH_AIX - - -# _LT_SHELL_INIT(ARG) -# ------------------- -m4_define([_LT_SHELL_INIT], -[m4_divert_text([M4SH-INIT], [$1 -])])# _LT_SHELL_INIT - - - -# _LT_PROG_ECHO_BACKSLASH -# ----------------------- -# Find how we can fake an echo command that does not interpret backslash. -# In particular, with Autoconf 2.60 or later we add some code to the start -# of the generated configure script which will find a shell with a builtin -# printf (which we can use as an echo command). -m4_defun([_LT_PROG_ECHO_BACKSLASH], -[ECHO='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' -ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO -ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO$ECHO - -AC_MSG_CHECKING([how to print strings]) -# Test print first, because it will be a builtin if present. -if test "X`( print -r -- -n ) 2>/dev/null`" = X-n && \ - test "X`print -r -- $ECHO 2>/dev/null`" = "X$ECHO"; then - ECHO='print -r --' -elif test "X`printf %s $ECHO 2>/dev/null`" = "X$ECHO"; then - ECHO='printf %s\n' -else - # Use this function as a fallback that always works. - func_fallback_echo () - { - eval 'cat <<_LTECHO_EOF -$[]1 -_LTECHO_EOF' - } - ECHO='func_fallback_echo' -fi - -# func_echo_all arg... -# Invoke $ECHO with all args, space-separated. -func_echo_all () -{ - $ECHO "$*" -} - -case "$ECHO" in - printf*) AC_MSG_RESULT([printf]) ;; - print*) AC_MSG_RESULT([print -r]) ;; - *) AC_MSG_RESULT([cat]) ;; -esac - -m4_ifdef([_AS_DETECT_SUGGESTED], -[_AS_DETECT_SUGGESTED([ - test -n "${ZSH_VERSION+set}${BASH_VERSION+set}" || ( - ECHO='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' - ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO - ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO$ECHO - PATH=/empty FPATH=/empty; export PATH FPATH - test "X`printf %s $ECHO`" = "X$ECHO" \ - || test "X`print -r -- $ECHO`" = "X$ECHO" )])]) - -_LT_DECL([], [SHELL], [1], [Shell to use when invoking shell scripts]) -_LT_DECL([], [ECHO], [1], [An echo program that protects backslashes]) -])# _LT_PROG_ECHO_BACKSLASH - - -# _LT_WITH_SYSROOT -# ---------------- -AC_DEFUN([_LT_WITH_SYSROOT], -[AC_MSG_CHECKING([for sysroot]) -AC_ARG_WITH([sysroot], -[ --with-sysroot[=DIR] Search for dependent libraries within DIR - (or the compiler's sysroot if not specified).], -[], [with_sysroot=no]) - -dnl lt_sysroot will always be passed unquoted. We quote it here -dnl in case the user passed a directory name. -lt_sysroot= -case ${with_sysroot} in #( - yes) - if test "$GCC" = yes; then - lt_sysroot=`$CC --print-sysroot 2>/dev/null` - fi - ;; #( - /*) - lt_sysroot=`echo "$with_sysroot" | sed -e "$sed_quote_subst"` - ;; #( - no|'') - ;; #( - *) - AC_MSG_RESULT([${with_sysroot}]) - AC_MSG_ERROR([The sysroot must be an absolute path.]) - ;; -esac - - AC_MSG_RESULT([${lt_sysroot:-no}]) -_LT_DECL([], [lt_sysroot], [0], [The root where to search for ]dnl -[dependent libraries, and in which our libraries should be installed.])]) - -# _LT_ENABLE_LOCK -# --------------- -m4_defun([_LT_ENABLE_LOCK], -[AC_ARG_ENABLE([libtool-lock], - [AS_HELP_STRING([--disable-libtool-lock], - [avoid locking (might break parallel builds)])]) -test "x$enable_libtool_lock" != xno && enable_libtool_lock=yes - -# Some flags need to be propagated to the compiler or linker for good -# libtool support. -case $host in -ia64-*-hpux*) - # Find out which ABI we are using. - echo 'int i;' > conftest.$ac_ext - if AC_TRY_EVAL(ac_compile); then - case `/usr/bin/file conftest.$ac_objext` in - *ELF-32*) - HPUX_IA64_MODE="32" - ;; - *ELF-64*) - HPUX_IA64_MODE="64" - ;; - esac - fi - rm -rf conftest* - ;; -*-*-irix6*) - # Find out which ABI we are using. - echo '[#]line '$LINENO' "configure"' > conftest.$ac_ext - if AC_TRY_EVAL(ac_compile); then - if test "$lt_cv_prog_gnu_ld" = yes; then - case `/usr/bin/file conftest.$ac_objext` in - *32-bit*) - LD="${LD-ld} -melf32bsmip" - ;; - *N32*) - LD="${LD-ld} -melf32bmipn32" - ;; - *64-bit*) - LD="${LD-ld} -melf64bmip" - ;; - esac - else - case `/usr/bin/file conftest.$ac_objext` in - *32-bit*) - LD="${LD-ld} -32" - ;; - *N32*) - LD="${LD-ld} -n32" - ;; - *64-bit*) - LD="${LD-ld} -64" - ;; - esac - fi - fi - rm -rf conftest* - ;; - -x86_64-*kfreebsd*-gnu|x86_64-*linux*|powerpc*-*linux*| \ -s390*-*linux*|s390*-*tpf*|sparc*-*linux*) - # Find out which ABI we are using. - echo 'int i;' > conftest.$ac_ext - if AC_TRY_EVAL(ac_compile); then - case `/usr/bin/file conftest.o` in - *32-bit*) - case $host in - x86_64-*kfreebsd*-gnu) - LD="${LD-ld} -m elf_i386_fbsd" - ;; - x86_64-*linux*) - case `/usr/bin/file conftest.o` in - *x86-64*) - LD="${LD-ld} -m elf32_x86_64" - ;; - *) - LD="${LD-ld} -m elf_i386" - ;; - esac - ;; - powerpc64le-*) - LD="${LD-ld} -m elf32lppclinux" - ;; - powerpc64-*) - LD="${LD-ld} -m elf32ppclinux" - ;; - s390x-*linux*) - LD="${LD-ld} -m elf_s390" - ;; - sparc64-*linux*) - LD="${LD-ld} -m elf32_sparc" - ;; - esac - ;; - *64-bit*) - case $host in - x86_64-*kfreebsd*-gnu) - LD="${LD-ld} -m elf_x86_64_fbsd" - ;; - x86_64-*linux*) - LD="${LD-ld} -m elf_x86_64" - ;; - powerpcle-*) - LD="${LD-ld} -m elf64lppc" - ;; - powerpc-*) - LD="${LD-ld} -m elf64ppc" - ;; - s390*-*linux*|s390*-*tpf*) - LD="${LD-ld} -m elf64_s390" - ;; - sparc*-*linux*) - LD="${LD-ld} -m elf64_sparc" - ;; - esac - ;; - esac - fi - rm -rf conftest* - ;; - -*-*-sco3.2v5*) - # On SCO OpenServer 5, we need -belf to get full-featured binaries. - SAVE_CFLAGS="$CFLAGS" - CFLAGS="$CFLAGS -belf" - AC_CACHE_CHECK([whether the C compiler needs -belf], lt_cv_cc_needs_belf, - [AC_LANG_PUSH(C) - AC_LINK_IFELSE([AC_LANG_PROGRAM([[]],[[]])],[lt_cv_cc_needs_belf=yes],[lt_cv_cc_needs_belf=no]) - AC_LANG_POP]) - if test x"$lt_cv_cc_needs_belf" != x"yes"; then - # this is probably gcc 2.8.0, egcs 1.0 or newer; no need for -belf - CFLAGS="$SAVE_CFLAGS" - fi - ;; -*-*solaris*) - # Find out which ABI we are using. - echo 'int i;' > conftest.$ac_ext - if AC_TRY_EVAL(ac_compile); then - case `/usr/bin/file conftest.o` in - *64-bit*) - case $lt_cv_prog_gnu_ld in - yes*) - case $host in - i?86-*-solaris*) - LD="${LD-ld} -m elf_x86_64" - ;; - sparc*-*-solaris*) - LD="${LD-ld} -m elf64_sparc" - ;; - esac - # GNU ld 2.21 introduced _sol2 emulations. Use them if available. - if ${LD-ld} -V | grep _sol2 >/dev/null 2>&1; then - LD="${LD-ld}_sol2" - fi - ;; - *) - if ${LD-ld} -64 -r -o conftest2.o conftest.o >/dev/null 2>&1; then - LD="${LD-ld} -64" - fi - ;; - esac - ;; - esac - fi - rm -rf conftest* - ;; -esac - -need_locks="$enable_libtool_lock" -])# _LT_ENABLE_LOCK - - -# _LT_PROG_AR -# ----------- -m4_defun([_LT_PROG_AR], -[AC_CHECK_TOOLS(AR, [ar], false) -: ${AR=ar} -: ${AR_FLAGS=cru} -_LT_DECL([], [AR], [1], [The archiver]) -_LT_DECL([], [AR_FLAGS], [1], [Flags to create an archive]) - -AC_CACHE_CHECK([for archiver @FILE support], [lt_cv_ar_at_file], - [lt_cv_ar_at_file=no - AC_COMPILE_IFELSE([AC_LANG_PROGRAM], - [echo conftest.$ac_objext > conftest.lst - lt_ar_try='$AR $AR_FLAGS libconftest.a @conftest.lst >&AS_MESSAGE_LOG_FD' - AC_TRY_EVAL([lt_ar_try]) - if test "$ac_status" -eq 0; then - # Ensure the archiver fails upon bogus file names. - rm -f conftest.$ac_objext libconftest.a - AC_TRY_EVAL([lt_ar_try]) - if test "$ac_status" -ne 0; then - lt_cv_ar_at_file=@ - fi - fi - rm -f conftest.* libconftest.a - ]) - ]) - -if test "x$lt_cv_ar_at_file" = xno; then - archiver_list_spec= -else - archiver_list_spec=$lt_cv_ar_at_file -fi -_LT_DECL([], [archiver_list_spec], [1], - [How to feed a file listing to the archiver]) -])# _LT_PROG_AR - - -# _LT_CMD_OLD_ARCHIVE -# ------------------- -m4_defun([_LT_CMD_OLD_ARCHIVE], -[_LT_PROG_AR - -AC_CHECK_TOOL(STRIP, strip, :) -test -z "$STRIP" && STRIP=: -_LT_DECL([], [STRIP], [1], [A symbol stripping program]) - -AC_CHECK_TOOL(RANLIB, ranlib, :) -test -z "$RANLIB" && RANLIB=: -_LT_DECL([], [RANLIB], [1], - [Commands used to install an old-style archive]) - -# Determine commands to create old-style static archives. -old_archive_cmds='$AR $AR_FLAGS $oldlib$oldobjs' -old_postinstall_cmds='chmod 644 $oldlib' -old_postuninstall_cmds= - -if test -n "$RANLIB"; then - case $host_os in - openbsd*) - old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB -t \$tool_oldlib" - ;; - *) - old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB \$tool_oldlib" - ;; - esac - old_archive_cmds="$old_archive_cmds~\$RANLIB \$tool_oldlib" -fi - -case $host_os in - darwin*) - lock_old_archive_extraction=yes ;; - *) - lock_old_archive_extraction=no ;; -esac -_LT_DECL([], [old_postinstall_cmds], [2]) -_LT_DECL([], [old_postuninstall_cmds], [2]) -_LT_TAGDECL([], [old_archive_cmds], [2], - [Commands used to build an old-style archive]) -_LT_DECL([], [lock_old_archive_extraction], [0], - [Whether to use a lock for old archive extraction]) -])# _LT_CMD_OLD_ARCHIVE - - -# _LT_COMPILER_OPTION(MESSAGE, VARIABLE-NAME, FLAGS, -# [OUTPUT-FILE], [ACTION-SUCCESS], [ACTION-FAILURE]) -# ---------------------------------------------------------------- -# Check whether the given compiler option works -AC_DEFUN([_LT_COMPILER_OPTION], -[m4_require([_LT_FILEUTILS_DEFAULTS])dnl -m4_require([_LT_DECL_SED])dnl -AC_CACHE_CHECK([$1], [$2], - [$2=no - m4_if([$4], , [ac_outfile=conftest.$ac_objext], [ac_outfile=$4]) - echo "$lt_simple_compile_test_code" > conftest.$ac_ext - lt_compiler_flag="$3" - # Insert the option either (1) after the last *FLAGS variable, or - # (2) before a word containing "conftest.", or (3) at the end. - # Note that $ac_compile itself does not contain backslashes and begins - # with a dollar sign (not a hyphen), so the echo should work correctly. - # The option is referenced via a variable to avoid confusing sed. - lt_compile=`echo "$ac_compile" | $SED \ - -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ - -e 's: [[^ ]]*conftest\.: $lt_compiler_flag&:; t' \ - -e 's:$: $lt_compiler_flag:'` - (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&AS_MESSAGE_LOG_FD) - (eval "$lt_compile" 2>conftest.err) - ac_status=$? - cat conftest.err >&AS_MESSAGE_LOG_FD - echo "$as_me:$LINENO: \$? = $ac_status" >&AS_MESSAGE_LOG_FD - if (exit $ac_status) && test -s "$ac_outfile"; then - # The compiler can only warn and ignore the option if not recognized - # So say no if there are warnings other than the usual output. - $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' >conftest.exp - $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 - if test ! -s conftest.er2 || diff conftest.exp conftest.er2 >/dev/null; then - $2=yes - fi - fi - $RM conftest* -]) - -if test x"[$]$2" = xyes; then - m4_if([$5], , :, [$5]) -else - m4_if([$6], , :, [$6]) -fi -])# _LT_COMPILER_OPTION - -# Old name: -AU_ALIAS([AC_LIBTOOL_COMPILER_OPTION], [_LT_COMPILER_OPTION]) -dnl aclocal-1.4 backwards compatibility: -dnl AC_DEFUN([AC_LIBTOOL_COMPILER_OPTION], []) - - -# _LT_LINKER_OPTION(MESSAGE, VARIABLE-NAME, FLAGS, -# [ACTION-SUCCESS], [ACTION-FAILURE]) -# ---------------------------------------------------- -# Check whether the given linker option works -AC_DEFUN([_LT_LINKER_OPTION], -[m4_require([_LT_FILEUTILS_DEFAULTS])dnl -m4_require([_LT_DECL_SED])dnl -AC_CACHE_CHECK([$1], [$2], - [$2=no - save_LDFLAGS="$LDFLAGS" - LDFLAGS="$LDFLAGS $3" - echo "$lt_simple_link_test_code" > conftest.$ac_ext - if (eval $ac_link 2>conftest.err) && test -s conftest$ac_exeext; then - # The linker can only warn and ignore the option if not recognized - # So say no if there are warnings - if test -s conftest.err; then - # Append any errors to the config.log. - cat conftest.err 1>&AS_MESSAGE_LOG_FD - $ECHO "$_lt_linker_boilerplate" | $SED '/^$/d' > conftest.exp - $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 - if diff conftest.exp conftest.er2 >/dev/null; then - $2=yes - fi - else - $2=yes - fi - fi - $RM -r conftest* - LDFLAGS="$save_LDFLAGS" -]) - -if test x"[$]$2" = xyes; then - m4_if([$4], , :, [$4]) -else - m4_if([$5], , :, [$5]) -fi -])# _LT_LINKER_OPTION - -# Old name: -AU_ALIAS([AC_LIBTOOL_LINKER_OPTION], [_LT_LINKER_OPTION]) -dnl aclocal-1.4 backwards compatibility: -dnl AC_DEFUN([AC_LIBTOOL_LINKER_OPTION], []) - - -# LT_CMD_MAX_LEN -#--------------- -AC_DEFUN([LT_CMD_MAX_LEN], -[AC_REQUIRE([AC_CANONICAL_HOST])dnl -# find the maximum length of command line arguments -AC_MSG_CHECKING([the maximum length of command line arguments]) -AC_CACHE_VAL([lt_cv_sys_max_cmd_len], [dnl - i=0 - teststring="ABCD" - - case $build_os in - msdosdjgpp*) - # On DJGPP, this test can blow up pretty badly due to problems in libc - # (any single argument exceeding 2000 bytes causes a buffer overrun - # during glob expansion). Even if it were fixed, the result of this - # check would be larger than it should be. - lt_cv_sys_max_cmd_len=12288; # 12K is about right - ;; - - gnu*) - # Under GNU Hurd, this test is not required because there is - # no limit to the length of command line arguments. - # Libtool will interpret -1 as no limit whatsoever - lt_cv_sys_max_cmd_len=-1; - ;; - - cygwin* | mingw* | cegcc*) - # On Win9x/ME, this test blows up -- it succeeds, but takes - # about 5 minutes as the teststring grows exponentially. - # Worse, since 9x/ME are not pre-emptively multitasking, - # you end up with a "frozen" computer, even though with patience - # the test eventually succeeds (with a max line length of 256k). - # Instead, let's just punt: use the minimum linelength reported by - # all of the supported platforms: 8192 (on NT/2K/XP). - lt_cv_sys_max_cmd_len=8192; - ;; - - mint*) - # On MiNT this can take a long time and run out of memory. - lt_cv_sys_max_cmd_len=8192; - ;; - - amigaos*) - # On AmigaOS with pdksh, this test takes hours, literally. - # So we just punt and use a minimum line length of 8192. - lt_cv_sys_max_cmd_len=8192; - ;; - - netbsd* | freebsd* | openbsd* | darwin* | dragonfly*) - # This has been around since 386BSD, at least. Likely further. - if test -x /sbin/sysctl; then - lt_cv_sys_max_cmd_len=`/sbin/sysctl -n kern.argmax` - elif test -x /usr/sbin/sysctl; then - lt_cv_sys_max_cmd_len=`/usr/sbin/sysctl -n kern.argmax` - else - lt_cv_sys_max_cmd_len=65536 # usable default for all BSDs - fi - # And add a safety zone - lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4` - lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3` - ;; - - interix*) - # We know the value 262144 and hardcode it with a safety zone (like BSD) - lt_cv_sys_max_cmd_len=196608 - ;; - - os2*) - # The test takes a long time on OS/2. - lt_cv_sys_max_cmd_len=8192 - ;; - - osf*) - # Dr. Hans Ekkehard Plesser reports seeing a kernel panic running configure - # due to this test when exec_disable_arg_limit is 1 on Tru64. It is not - # nice to cause kernel panics so lets avoid the loop below. - # First set a reasonable default. - lt_cv_sys_max_cmd_len=16384 - # - if test -x /sbin/sysconfig; then - case `/sbin/sysconfig -q proc exec_disable_arg_limit` in - *1*) lt_cv_sys_max_cmd_len=-1 ;; - esac - fi - ;; - sco3.2v5*) - lt_cv_sys_max_cmd_len=102400 - ;; - sysv5* | sco5v6* | sysv4.2uw2*) - kargmax=`grep ARG_MAX /etc/conf/cf.d/stune 2>/dev/null` - if test -n "$kargmax"; then - lt_cv_sys_max_cmd_len=`echo $kargmax | sed 's/.*[[ ]]//'` - else - lt_cv_sys_max_cmd_len=32768 - fi - ;; - *) - lt_cv_sys_max_cmd_len=`(getconf ARG_MAX) 2> /dev/null` - if test -n "$lt_cv_sys_max_cmd_len" && \ - test undefined != "$lt_cv_sys_max_cmd_len"; then - lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4` - lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3` - else - # Make teststring a little bigger before we do anything with it. - # a 1K string should be a reasonable start. - for i in 1 2 3 4 5 6 7 8 ; do - teststring=$teststring$teststring - done - SHELL=${SHELL-${CONFIG_SHELL-/bin/sh}} - # If test is not a shell built-in, we'll probably end up computing a - # maximum length that is only half of the actual maximum length, but - # we can't tell. - while { test "X"`env echo "$teststring$teststring" 2>/dev/null` \ - = "X$teststring$teststring"; } >/dev/null 2>&1 && - test $i != 17 # 1/2 MB should be enough - do - i=`expr $i + 1` - teststring=$teststring$teststring - done - # Only check the string length outside the loop. - lt_cv_sys_max_cmd_len=`expr "X$teststring" : ".*" 2>&1` - teststring= - # Add a significant safety factor because C++ compilers can tack on - # massive amounts of additional arguments before passing them to the - # linker. It appears as though 1/2 is a usable value. - lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 2` - fi - ;; - esac -]) -if test -n $lt_cv_sys_max_cmd_len ; then - AC_MSG_RESULT($lt_cv_sys_max_cmd_len) -else - AC_MSG_RESULT(none) -fi -max_cmd_len=$lt_cv_sys_max_cmd_len -_LT_DECL([], [max_cmd_len], [0], - [What is the maximum length of a command?]) -])# LT_CMD_MAX_LEN - -# Old name: -AU_ALIAS([AC_LIBTOOL_SYS_MAX_CMD_LEN], [LT_CMD_MAX_LEN]) -dnl aclocal-1.4 backwards compatibility: -dnl AC_DEFUN([AC_LIBTOOL_SYS_MAX_CMD_LEN], []) - - -# _LT_HEADER_DLFCN -# ---------------- -m4_defun([_LT_HEADER_DLFCN], -[AC_CHECK_HEADERS([dlfcn.h], [], [], [AC_INCLUDES_DEFAULT])dnl -])# _LT_HEADER_DLFCN - - -# _LT_TRY_DLOPEN_SELF (ACTION-IF-TRUE, ACTION-IF-TRUE-W-USCORE, -# ACTION-IF-FALSE, ACTION-IF-CROSS-COMPILING) -# ---------------------------------------------------------------- -m4_defun([_LT_TRY_DLOPEN_SELF], -[m4_require([_LT_HEADER_DLFCN])dnl -if test "$cross_compiling" = yes; then : - [$4] -else - lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 - lt_status=$lt_dlunknown - cat > conftest.$ac_ext <<_LT_EOF -[#line $LINENO "configure" -#include "confdefs.h" - -#if HAVE_DLFCN_H -#include -#endif - -#include - -#ifdef RTLD_GLOBAL -# define LT_DLGLOBAL RTLD_GLOBAL -#else -# ifdef DL_GLOBAL -# define LT_DLGLOBAL DL_GLOBAL -# else -# define LT_DLGLOBAL 0 -# endif -#endif - -/* We may have to define LT_DLLAZY_OR_NOW in the command line if we - find out it does not work in some platform. */ -#ifndef LT_DLLAZY_OR_NOW -# ifdef RTLD_LAZY -# define LT_DLLAZY_OR_NOW RTLD_LAZY -# else -# ifdef DL_LAZY -# define LT_DLLAZY_OR_NOW DL_LAZY -# else -# ifdef RTLD_NOW -# define LT_DLLAZY_OR_NOW RTLD_NOW -# else -# ifdef DL_NOW -# define LT_DLLAZY_OR_NOW DL_NOW -# else -# define LT_DLLAZY_OR_NOW 0 -# endif -# endif -# endif -# endif -#endif - -/* When -fvisbility=hidden is used, assume the code has been annotated - correspondingly for the symbols needed. */ -#if defined(__GNUC__) && (((__GNUC__ == 3) && (__GNUC_MINOR__ >= 3)) || (__GNUC__ > 3)) -int fnord () __attribute__((visibility("default"))); -#endif - -int fnord () { return 42; } -int main () -{ - void *self = dlopen (0, LT_DLGLOBAL|LT_DLLAZY_OR_NOW); - int status = $lt_dlunknown; - - if (self) - { - if (dlsym (self,"fnord")) status = $lt_dlno_uscore; - else - { - if (dlsym( self,"_fnord")) status = $lt_dlneed_uscore; - else puts (dlerror ()); - } - /* dlclose (self); */ - } - else - puts (dlerror ()); - - return status; -}] -_LT_EOF - if AC_TRY_EVAL(ac_link) && test -s conftest${ac_exeext} 2>/dev/null; then - (./conftest; exit; ) >&AS_MESSAGE_LOG_FD 2>/dev/null - lt_status=$? - case x$lt_status in - x$lt_dlno_uscore) $1 ;; - x$lt_dlneed_uscore) $2 ;; - x$lt_dlunknown|x*) $3 ;; - esac - else : - # compilation failed - $3 - fi -fi -rm -fr conftest* -])# _LT_TRY_DLOPEN_SELF - - -# LT_SYS_DLOPEN_SELF -# ------------------ -AC_DEFUN([LT_SYS_DLOPEN_SELF], -[m4_require([_LT_HEADER_DLFCN])dnl -if test "x$enable_dlopen" != xyes; then - enable_dlopen=unknown - enable_dlopen_self=unknown - enable_dlopen_self_static=unknown -else - lt_cv_dlopen=no - lt_cv_dlopen_libs= - - case $host_os in - beos*) - lt_cv_dlopen="load_add_on" - lt_cv_dlopen_libs= - lt_cv_dlopen_self=yes - ;; - - mingw* | pw32* | cegcc*) - lt_cv_dlopen="LoadLibrary" - lt_cv_dlopen_libs= - ;; - - cygwin*) - lt_cv_dlopen="dlopen" - lt_cv_dlopen_libs= - ;; - - darwin*) - # if libdl is installed we need to link against it - AC_CHECK_LIB([dl], [dlopen], - [lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-ldl"],[ - lt_cv_dlopen="dyld" - lt_cv_dlopen_libs= - lt_cv_dlopen_self=yes - ]) - ;; - - *) - AC_CHECK_FUNC([shl_load], - [lt_cv_dlopen="shl_load"], - [AC_CHECK_LIB([dld], [shl_load], - [lt_cv_dlopen="shl_load" lt_cv_dlopen_libs="-ldld"], - [AC_CHECK_FUNC([dlopen], - [lt_cv_dlopen="dlopen"], - [AC_CHECK_LIB([dl], [dlopen], - [lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-ldl"], - [AC_CHECK_LIB([svld], [dlopen], - [lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-lsvld"], - [AC_CHECK_LIB([dld], [dld_link], - [lt_cv_dlopen="dld_link" lt_cv_dlopen_libs="-ldld"]) - ]) - ]) - ]) - ]) - ]) - ;; - esac - - if test "x$lt_cv_dlopen" != xno; then - enable_dlopen=yes - else - enable_dlopen=no - fi - - case $lt_cv_dlopen in - dlopen) - save_CPPFLAGS="$CPPFLAGS" - test "x$ac_cv_header_dlfcn_h" = xyes && CPPFLAGS="$CPPFLAGS -DHAVE_DLFCN_H" - - save_LDFLAGS="$LDFLAGS" - wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $export_dynamic_flag_spec\" - - save_LIBS="$LIBS" - LIBS="$lt_cv_dlopen_libs $LIBS" - - AC_CACHE_CHECK([whether a program can dlopen itself], - lt_cv_dlopen_self, [dnl - _LT_TRY_DLOPEN_SELF( - lt_cv_dlopen_self=yes, lt_cv_dlopen_self=yes, - lt_cv_dlopen_self=no, lt_cv_dlopen_self=cross) - ]) - - if test "x$lt_cv_dlopen_self" = xyes; then - wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $lt_prog_compiler_static\" - AC_CACHE_CHECK([whether a statically linked program can dlopen itself], - lt_cv_dlopen_self_static, [dnl - _LT_TRY_DLOPEN_SELF( - lt_cv_dlopen_self_static=yes, lt_cv_dlopen_self_static=yes, - lt_cv_dlopen_self_static=no, lt_cv_dlopen_self_static=cross) - ]) - fi - - CPPFLAGS="$save_CPPFLAGS" - LDFLAGS="$save_LDFLAGS" - LIBS="$save_LIBS" - ;; - esac - - case $lt_cv_dlopen_self in - yes|no) enable_dlopen_self=$lt_cv_dlopen_self ;; - *) enable_dlopen_self=unknown ;; - esac - - case $lt_cv_dlopen_self_static in - yes|no) enable_dlopen_self_static=$lt_cv_dlopen_self_static ;; - *) enable_dlopen_self_static=unknown ;; - esac -fi -_LT_DECL([dlopen_support], [enable_dlopen], [0], - [Whether dlopen is supported]) -_LT_DECL([dlopen_self], [enable_dlopen_self], [0], - [Whether dlopen of programs is supported]) -_LT_DECL([dlopen_self_static], [enable_dlopen_self_static], [0], - [Whether dlopen of statically linked programs is supported]) -])# LT_SYS_DLOPEN_SELF - -# Old name: -AU_ALIAS([AC_LIBTOOL_DLOPEN_SELF], [LT_SYS_DLOPEN_SELF]) -dnl aclocal-1.4 backwards compatibility: -dnl AC_DEFUN([AC_LIBTOOL_DLOPEN_SELF], []) - - -# _LT_COMPILER_C_O([TAGNAME]) -# --------------------------- -# Check to see if options -c and -o are simultaneously supported by compiler. -# This macro does not hard code the compiler like AC_PROG_CC_C_O. -m4_defun([_LT_COMPILER_C_O], -[m4_require([_LT_DECL_SED])dnl -m4_require([_LT_FILEUTILS_DEFAULTS])dnl -m4_require([_LT_TAG_COMPILER])dnl -AC_CACHE_CHECK([if $compiler supports -c -o file.$ac_objext], - [_LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)], - [_LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)=no - $RM -r conftest 2>/dev/null - mkdir conftest - cd conftest - mkdir out - echo "$lt_simple_compile_test_code" > conftest.$ac_ext - - lt_compiler_flag="-o out/conftest2.$ac_objext" - # Insert the option either (1) after the last *FLAGS variable, or - # (2) before a word containing "conftest.", or (3) at the end. - # Note that $ac_compile itself does not contain backslashes and begins - # with a dollar sign (not a hyphen), so the echo should work correctly. - lt_compile=`echo "$ac_compile" | $SED \ - -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ - -e 's: [[^ ]]*conftest\.: $lt_compiler_flag&:; t' \ - -e 's:$: $lt_compiler_flag:'` - (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&AS_MESSAGE_LOG_FD) - (eval "$lt_compile" 2>out/conftest.err) - ac_status=$? - cat out/conftest.err >&AS_MESSAGE_LOG_FD - echo "$as_me:$LINENO: \$? = $ac_status" >&AS_MESSAGE_LOG_FD - if (exit $ac_status) && test -s out/conftest2.$ac_objext - then - # The compiler can only warn and ignore the option if not recognized - # So say no if there are warnings - $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' > out/conftest.exp - $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2 - if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then - _LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)=yes - fi - fi - chmod u+w . 2>&AS_MESSAGE_LOG_FD - $RM conftest* - # SGI C++ compiler will create directory out/ii_files/ for - # template instantiation - test -d out/ii_files && $RM out/ii_files/* && rmdir out/ii_files - $RM out/* && rmdir out - cd .. - $RM -r conftest - $RM conftest* -]) -_LT_TAGDECL([compiler_c_o], [lt_cv_prog_compiler_c_o], [1], - [Does compiler simultaneously support -c and -o options?]) -])# _LT_COMPILER_C_O - - -# _LT_COMPILER_FILE_LOCKS([TAGNAME]) -# ---------------------------------- -# Check to see if we can do hard links to lock some files if needed -m4_defun([_LT_COMPILER_FILE_LOCKS], -[m4_require([_LT_ENABLE_LOCK])dnl -m4_require([_LT_FILEUTILS_DEFAULTS])dnl -_LT_COMPILER_C_O([$1]) - -hard_links="nottested" -if test "$_LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)" = no && test "$need_locks" != no; then - # do not overwrite the value of need_locks provided by the user - AC_MSG_CHECKING([if we can lock with hard links]) - hard_links=yes - $RM conftest* - ln conftest.a conftest.b 2>/dev/null && hard_links=no - touch conftest.a - ln conftest.a conftest.b 2>&5 || hard_links=no - ln conftest.a conftest.b 2>/dev/null && hard_links=no - AC_MSG_RESULT([$hard_links]) - if test "$hard_links" = no; then - AC_MSG_WARN([`$CC' does not support `-c -o', so `make -j' may be unsafe]) - need_locks=warn - fi -else - need_locks=no -fi -_LT_DECL([], [need_locks], [1], [Must we lock files when doing compilation?]) -])# _LT_COMPILER_FILE_LOCKS - - -# _LT_CHECK_OBJDIR -# ---------------- -m4_defun([_LT_CHECK_OBJDIR], -[AC_CACHE_CHECK([for objdir], [lt_cv_objdir], -[rm -f .libs 2>/dev/null -mkdir .libs 2>/dev/null -if test -d .libs; then - lt_cv_objdir=.libs -else - # MS-DOS does not allow filenames that begin with a dot. - lt_cv_objdir=_libs -fi -rmdir .libs 2>/dev/null]) -objdir=$lt_cv_objdir -_LT_DECL([], [objdir], [0], - [The name of the directory that contains temporary libtool files])dnl -m4_pattern_allow([LT_OBJDIR])dnl -AC_DEFINE_UNQUOTED(LT_OBJDIR, "$lt_cv_objdir/", - [Define to the sub-directory in which libtool stores uninstalled libraries.]) -])# _LT_CHECK_OBJDIR - - -# _LT_LINKER_HARDCODE_LIBPATH([TAGNAME]) -# -------------------------------------- -# Check hardcoding attributes. -m4_defun([_LT_LINKER_HARDCODE_LIBPATH], -[AC_MSG_CHECKING([how to hardcode library paths into programs]) -_LT_TAGVAR(hardcode_action, $1)= -if test -n "$_LT_TAGVAR(hardcode_libdir_flag_spec, $1)" || - test -n "$_LT_TAGVAR(runpath_var, $1)" || - test "X$_LT_TAGVAR(hardcode_automatic, $1)" = "Xyes" ; then - - # We can hardcode non-existent directories. - if test "$_LT_TAGVAR(hardcode_direct, $1)" != no && - # If the only mechanism to avoid hardcoding is shlibpath_var, we - # have to relink, otherwise we might link with an installed library - # when we should be linking with a yet-to-be-installed one - ## test "$_LT_TAGVAR(hardcode_shlibpath_var, $1)" != no && - test "$_LT_TAGVAR(hardcode_minus_L, $1)" != no; then - # Linking always hardcodes the temporary library directory. - _LT_TAGVAR(hardcode_action, $1)=relink - else - # We can link without hardcoding, and we can hardcode nonexisting dirs. - _LT_TAGVAR(hardcode_action, $1)=immediate - fi -else - # We cannot hardcode anything, or else we can only hardcode existing - # directories. - _LT_TAGVAR(hardcode_action, $1)=unsupported -fi -AC_MSG_RESULT([$_LT_TAGVAR(hardcode_action, $1)]) - -if test "$_LT_TAGVAR(hardcode_action, $1)" = relink || - test "$_LT_TAGVAR(inherit_rpath, $1)" = yes; then - # Fast installation is not supported - enable_fast_install=no -elif test "$shlibpath_overrides_runpath" = yes || - test "$enable_shared" = no; then - # Fast installation is not necessary - enable_fast_install=needless -fi -_LT_TAGDECL([], [hardcode_action], [0], - [How to hardcode a shared library path into an executable]) -])# _LT_LINKER_HARDCODE_LIBPATH - - -# _LT_CMD_STRIPLIB -# ---------------- -m4_defun([_LT_CMD_STRIPLIB], -[m4_require([_LT_DECL_EGREP]) -striplib= -old_striplib= -AC_MSG_CHECKING([whether stripping libraries is possible]) -if test -n "$STRIP" && $STRIP -V 2>&1 | $GREP "GNU strip" >/dev/null; then - test -z "$old_striplib" && old_striplib="$STRIP --strip-debug" - test -z "$striplib" && striplib="$STRIP --strip-unneeded" - AC_MSG_RESULT([yes]) -else -# FIXME - insert some real tests, host_os isn't really good enough - case $host_os in - darwin*) - if test -n "$STRIP" ; then - striplib="$STRIP -x" - old_striplib="$STRIP -S" - AC_MSG_RESULT([yes]) - else - AC_MSG_RESULT([no]) - fi - ;; - *) - AC_MSG_RESULT([no]) - ;; - esac -fi -_LT_DECL([], [old_striplib], [1], [Commands to strip libraries]) -_LT_DECL([], [striplib], [1]) -])# _LT_CMD_STRIPLIB - - -# _LT_SYS_DYNAMIC_LINKER([TAG]) -# ----------------------------- -# PORTME Fill in your ld.so characteristics -m4_defun([_LT_SYS_DYNAMIC_LINKER], -[AC_REQUIRE([AC_CANONICAL_HOST])dnl -m4_require([_LT_DECL_EGREP])dnl -m4_require([_LT_FILEUTILS_DEFAULTS])dnl -m4_require([_LT_DECL_OBJDUMP])dnl -m4_require([_LT_DECL_SED])dnl -m4_require([_LT_CHECK_SHELL_FEATURES])dnl -AC_MSG_CHECKING([dynamic linker characteristics]) -m4_if([$1], - [], [ -if test "$GCC" = yes; then - case $host_os in - darwin*) lt_awk_arg="/^libraries:/,/LR/" ;; - *) lt_awk_arg="/^libraries:/" ;; - esac - case $host_os in - mingw* | cegcc*) lt_sed_strip_eq="s,=\([[A-Za-z]]:\),\1,g" ;; - *) lt_sed_strip_eq="s,=/,/,g" ;; - esac - lt_search_path_spec=`$CC -print-search-dirs | awk $lt_awk_arg | $SED -e "s/^libraries://" -e $lt_sed_strip_eq` - case $lt_search_path_spec in - *\;*) - # if the path contains ";" then we assume it to be the separator - # otherwise default to the standard path separator (i.e. ":") - it is - # assumed that no part of a normal pathname contains ";" but that should - # okay in the real world where ";" in dirpaths is itself problematic. - lt_search_path_spec=`$ECHO "$lt_search_path_spec" | $SED 's/;/ /g'` - ;; - *) - lt_search_path_spec=`$ECHO "$lt_search_path_spec" | $SED "s/$PATH_SEPARATOR/ /g"` - ;; - esac - # Ok, now we have the path, separated by spaces, we can step through it - # and add multilib dir if necessary. - lt_tmp_lt_search_path_spec= - lt_multi_os_dir=`$CC $CPPFLAGS $CFLAGS $LDFLAGS -print-multi-os-directory 2>/dev/null` - for lt_sys_path in $lt_search_path_spec; do - if test -d "$lt_sys_path/$lt_multi_os_dir"; then - lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec $lt_sys_path/$lt_multi_os_dir" - else - test -d "$lt_sys_path" && \ - lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec $lt_sys_path" - fi - done - lt_search_path_spec=`$ECHO "$lt_tmp_lt_search_path_spec" | awk ' -BEGIN {RS=" "; FS="/|\n";} { - lt_foo=""; - lt_count=0; - for (lt_i = NF; lt_i > 0; lt_i--) { - if ($lt_i != "" && $lt_i != ".") { - if ($lt_i == "..") { - lt_count++; - } else { - if (lt_count == 0) { - lt_foo="/" $lt_i lt_foo; - } else { - lt_count--; - } - } - } - } - if (lt_foo != "") { lt_freq[[lt_foo]]++; } - if (lt_freq[[lt_foo]] == 1) { print lt_foo; } -}'` - # AWK program above erroneously prepends '/' to C:/dos/paths - # for these hosts. - case $host_os in - mingw* | cegcc*) lt_search_path_spec=`$ECHO "$lt_search_path_spec" |\ - $SED 's,/\([[A-Za-z]]:\),\1,g'` ;; - esac - sys_lib_search_path_spec=`$ECHO "$lt_search_path_spec" | $lt_NL2SP` -else - sys_lib_search_path_spec="/lib /usr/lib /usr/local/lib" -fi]) -library_names_spec= -libname_spec='lib$name' -soname_spec= -shrext_cmds=".so" -postinstall_cmds= -postuninstall_cmds= -finish_cmds= -finish_eval= -shlibpath_var= -shlibpath_overrides_runpath=unknown -version_type=none -dynamic_linker="$host_os ld.so" -sys_lib_dlsearch_path_spec="/lib /usr/lib" -need_lib_prefix=unknown -hardcode_into_libs=no - -# when you set need_version to no, make sure it does not cause -set_version -# flags to be left without arguments -need_version=unknown - -case $host_os in -aix3*) - version_type=linux # correct to gnu/linux during the next big refactor - library_names_spec='${libname}${release}${shared_ext}$versuffix $libname.a' - shlibpath_var=LIBPATH - - # AIX 3 has no versioning support, so we append a major version to the name. - soname_spec='${libname}${release}${shared_ext}$major' - ;; - -aix[[4-9]]*) - version_type=linux # correct to gnu/linux during the next big refactor - need_lib_prefix=no - need_version=no - hardcode_into_libs=yes - if test "$host_cpu" = ia64; then - # AIX 5 supports IA64 - library_names_spec='${libname}${release}${shared_ext}$major ${libname}${release}${shared_ext}$versuffix $libname${shared_ext}' - shlibpath_var=LD_LIBRARY_PATH - else - # With GCC up to 2.95.x, collect2 would create an import file - # for dependence libraries. The import file would start with - # the line `#! .'. This would cause the generated library to - # depend on `.', always an invalid library. This was fixed in - # development snapshots of GCC prior to 3.0. - case $host_os in - aix4 | aix4.[[01]] | aix4.[[01]].*) - if { echo '#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 97)' - echo ' yes ' - echo '#endif'; } | ${CC} -E - | $GREP yes > /dev/null; then - : - else - can_build_shared=no - fi - ;; - esac - # AIX (on Power*) has no versioning support, so currently we can not hardcode correct - # soname into executable. Probably we can add versioning support to - # collect2, so additional links can be useful in future. - if test "$aix_use_runtimelinking" = yes; then - # If using run time linking (on AIX 4.2 or later) use lib.so - # instead of lib.a to let people know that these are not - # typical AIX shared libraries. - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' - else - # We preserve .a as extension for shared libraries through AIX4.2 - # and later when we are not doing run time linking. - library_names_spec='${libname}${release}.a $libname.a' - soname_spec='${libname}${release}${shared_ext}$major' - fi - shlibpath_var=LIBPATH - fi - ;; - -amigaos*) - case $host_cpu in - powerpc) - # Since July 2007 AmigaOS4 officially supports .so libraries. - # When compiling the executable, add -use-dynld -Lsobjs: to the compileline. - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' - ;; - m68k) - library_names_spec='$libname.ixlibrary $libname.a' - # Create ${libname}_ixlibrary.a entries in /sys/libs. - finish_eval='for lib in `ls $libdir/*.ixlibrary 2>/dev/null`; do libname=`func_echo_all "$lib" | $SED '\''s%^.*/\([[^/]]*\)\.ixlibrary$%\1%'\''`; test $RM /sys/libs/${libname}_ixlibrary.a; $show "cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a"; cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a || exit 1; done' - ;; - esac - ;; - -beos*) - library_names_spec='${libname}${shared_ext}' - dynamic_linker="$host_os ld.so" - shlibpath_var=LIBRARY_PATH - ;; - -bsdi[[45]]*) - version_type=linux # correct to gnu/linux during the next big refactor - need_version=no - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - finish_cmds='PATH="\$PATH:/sbin" ldconfig $libdir' - shlibpath_var=LD_LIBRARY_PATH - sys_lib_search_path_spec="/shlib /usr/lib /usr/X11/lib /usr/contrib/lib /lib /usr/local/lib" - sys_lib_dlsearch_path_spec="/shlib /usr/lib /usr/local/lib" - # the default ld.so.conf also contains /usr/contrib/lib and - # /usr/X11R6/lib (/usr/X11 is a link to /usr/X11R6), but let us allow - # libtool to hard-code these into programs - ;; - -cygwin* | mingw* | pw32* | cegcc*) - version_type=windows - shrext_cmds=".dll" - need_version=no - need_lib_prefix=no - - case $GCC,$cc_basename in - yes,*) - # gcc - library_names_spec='$libname.dll.a' - # DLL is installed to $(libdir)/../bin by postinstall_cmds - postinstall_cmds='base_file=`basename \${file}`~ - dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\${base_file}'\''i; echo \$dlname'\''`~ - dldir=$destdir/`dirname \$dlpath`~ - test -d \$dldir || mkdir -p \$dldir~ - $install_prog $dir/$dlname \$dldir/$dlname~ - chmod a+x \$dldir/$dlname~ - if test -n '\''$stripme'\'' && test -n '\''$striplib'\''; then - eval '\''$striplib \$dldir/$dlname'\'' || exit \$?; - fi' - postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~ - dlpath=$dir/\$dldll~ - $RM \$dlpath' - shlibpath_overrides_runpath=yes - - case $host_os in - cygwin*) - # Cygwin DLLs use 'cyg' prefix rather than 'lib' - soname_spec='`echo ${libname} | sed -e 's/^lib/cyg/'``echo ${release} | $SED -e 's/[[.]]/-/g'`${versuffix}${shared_ext}' -m4_if([$1], [],[ - sys_lib_search_path_spec="$sys_lib_search_path_spec /usr/lib/w32api"]) - ;; - mingw* | cegcc*) - # MinGW DLLs use traditional 'lib' prefix - soname_spec='${libname}`echo ${release} | $SED -e 's/[[.]]/-/g'`${versuffix}${shared_ext}' - ;; - pw32*) - # pw32 DLLs use 'pw' prefix rather than 'lib' - library_names_spec='`echo ${libname} | sed -e 's/^lib/pw/'``echo ${release} | $SED -e 's/[[.]]/-/g'`${versuffix}${shared_ext}' - ;; - esac - dynamic_linker='Win32 ld.exe' - ;; - - *,cl*) - # Native MSVC - libname_spec='$name' - soname_spec='${libname}`echo ${release} | $SED -e 's/[[.]]/-/g'`${versuffix}${shared_ext}' - library_names_spec='${libname}.dll.lib' - - case $build_os in - mingw*) - sys_lib_search_path_spec= - lt_save_ifs=$IFS - IFS=';' - for lt_path in $LIB - do - IFS=$lt_save_ifs - # Let DOS variable expansion print the short 8.3 style file name. - lt_path=`cd "$lt_path" 2>/dev/null && cmd //C "for %i in (".") do @echo %~si"` - sys_lib_search_path_spec="$sys_lib_search_path_spec $lt_path" - done - IFS=$lt_save_ifs - # Convert to MSYS style. - sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | sed -e 's|\\\\|/|g' -e 's| \\([[a-zA-Z]]\\):| /\\1|g' -e 's|^ ||'` - ;; - cygwin*) - # Convert to unix form, then to dos form, then back to unix form - # but this time dos style (no spaces!) so that the unix form looks - # like /cygdrive/c/PROGRA~1:/cygdr... - sys_lib_search_path_spec=`cygpath --path --unix "$LIB"` - sys_lib_search_path_spec=`cygpath --path --dos "$sys_lib_search_path_spec" 2>/dev/null` - sys_lib_search_path_spec=`cygpath --path --unix "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"` - ;; - *) - sys_lib_search_path_spec="$LIB" - if $ECHO "$sys_lib_search_path_spec" | [$GREP ';[c-zC-Z]:/' >/dev/null]; then - # It is most probably a Windows format PATH. - sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e 's/;/ /g'` - else - sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"` - fi - # FIXME: find the short name or the path components, as spaces are - # common. (e.g. "Program Files" -> "PROGRA~1") - ;; - esac - - # DLL is installed to $(libdir)/../bin by postinstall_cmds - postinstall_cmds='base_file=`basename \${file}`~ - dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\${base_file}'\''i; echo \$dlname'\''`~ - dldir=$destdir/`dirname \$dlpath`~ - test -d \$dldir || mkdir -p \$dldir~ - $install_prog $dir/$dlname \$dldir/$dlname' - postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~ - dlpath=$dir/\$dldll~ - $RM \$dlpath' - shlibpath_overrides_runpath=yes - dynamic_linker='Win32 link.exe' - ;; - - *) - # Assume MSVC wrapper - library_names_spec='${libname}`echo ${release} | $SED -e 's/[[.]]/-/g'`${versuffix}${shared_ext} $libname.lib' - dynamic_linker='Win32 ld.exe' - ;; - esac - # FIXME: first we should search . and the directory the executable is in - shlibpath_var=PATH - ;; - -darwin* | rhapsody*) - dynamic_linker="$host_os dyld" - version_type=darwin - need_lib_prefix=no - need_version=no - library_names_spec='${libname}${release}${major}$shared_ext ${libname}$shared_ext' - soname_spec='${libname}${release}${major}$shared_ext' - shlibpath_overrides_runpath=yes - shlibpath_var=DYLD_LIBRARY_PATH - shrext_cmds='`test .$module = .yes && echo .so || echo .dylib`' -m4_if([$1], [],[ - sys_lib_search_path_spec="$sys_lib_search_path_spec /usr/local/lib"]) - sys_lib_dlsearch_path_spec='/usr/local/lib /lib /usr/lib' - ;; - -dgux*) - version_type=linux # correct to gnu/linux during the next big refactor - need_lib_prefix=no - need_version=no - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname$shared_ext' - soname_spec='${libname}${release}${shared_ext}$major' - shlibpath_var=LD_LIBRARY_PATH - ;; - -freebsd* | dragonfly*) - # DragonFly does not have aout. When/if they implement a new - # versioning mechanism, adjust this. - if test -x /usr/bin/objformat; then - objformat=`/usr/bin/objformat` - else - case $host_os in - freebsd[[23]].*) objformat=aout ;; - *) objformat=elf ;; - esac - fi - version_type=freebsd-$objformat - case $version_type in - freebsd-elf*) - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext} $libname${shared_ext}' - need_version=no - need_lib_prefix=no - ;; - freebsd-*) - library_names_spec='${libname}${release}${shared_ext}$versuffix $libname${shared_ext}$versuffix' - need_version=yes - ;; - esac - shlibpath_var=LD_LIBRARY_PATH - case $host_os in - freebsd2.*) - shlibpath_overrides_runpath=yes - ;; - freebsd3.[[01]]* | freebsdelf3.[[01]]*) - shlibpath_overrides_runpath=yes - hardcode_into_libs=yes - ;; - freebsd3.[[2-9]]* | freebsdelf3.[[2-9]]* | \ - freebsd4.[[0-5]] | freebsdelf4.[[0-5]] | freebsd4.1.1 | freebsdelf4.1.1) - shlibpath_overrides_runpath=no - hardcode_into_libs=yes - ;; - *) # from 4.6 on, and DragonFly - shlibpath_overrides_runpath=yes - hardcode_into_libs=yes - ;; - esac - ;; - -haiku*) - version_type=linux # correct to gnu/linux during the next big refactor - need_lib_prefix=no - need_version=no - dynamic_linker="$host_os runtime_loader" - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}${major} ${libname}${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - shlibpath_var=LIBRARY_PATH - shlibpath_overrides_runpath=yes - sys_lib_dlsearch_path_spec='/boot/home/config/lib /boot/common/lib /boot/system/lib' - hardcode_into_libs=yes - ;; - -hpux9* | hpux10* | hpux11*) - # Give a soname corresponding to the major version so that dld.sl refuses to - # link against other versions. - version_type=sunos - need_lib_prefix=no - need_version=no - case $host_cpu in - ia64*) - shrext_cmds='.so' - hardcode_into_libs=yes - dynamic_linker="$host_os dld.so" - shlibpath_var=LD_LIBRARY_PATH - shlibpath_overrides_runpath=yes # Unless +noenvvar is specified. - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - if test "X$HPUX_IA64_MODE" = X32; then - sys_lib_search_path_spec="/usr/lib/hpux32 /usr/local/lib/hpux32 /usr/local/lib" - else - sys_lib_search_path_spec="/usr/lib/hpux64 /usr/local/lib/hpux64" - fi - sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec - ;; - hppa*64*) - shrext_cmds='.sl' - hardcode_into_libs=yes - dynamic_linker="$host_os dld.sl" - shlibpath_var=LD_LIBRARY_PATH # How should we handle SHLIB_PATH - shlibpath_overrides_runpath=yes # Unless +noenvvar is specified. - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - sys_lib_search_path_spec="/usr/lib/pa20_64 /usr/ccs/lib/pa20_64" - sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec - ;; - *) - shrext_cmds='.sl' - dynamic_linker="$host_os dld.sl" - shlibpath_var=SHLIB_PATH - shlibpath_overrides_runpath=no # +s is required to enable SHLIB_PATH - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - ;; - esac - # HP-UX runs *really* slowly unless shared libraries are mode 555, ... - postinstall_cmds='chmod 555 $lib' - # or fails outright, so override atomically: - install_override_mode=555 - ;; - -interix[[3-9]]*) - version_type=linux # correct to gnu/linux during the next big refactor - need_lib_prefix=no - need_version=no - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - dynamic_linker='Interix 3.x ld.so.1 (PE, like ELF)' - shlibpath_var=LD_LIBRARY_PATH - shlibpath_overrides_runpath=no - hardcode_into_libs=yes - ;; - -irix5* | irix6* | nonstopux*) - case $host_os in - nonstopux*) version_type=nonstopux ;; - *) - if test "$lt_cv_prog_gnu_ld" = yes; then - version_type=linux # correct to gnu/linux during the next big refactor - else - version_type=irix - fi ;; - esac - need_lib_prefix=no - need_version=no - soname_spec='${libname}${release}${shared_ext}$major' - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${release}${shared_ext} $libname${shared_ext}' - case $host_os in - irix5* | nonstopux*) - libsuff= shlibsuff= - ;; - *) - case $LD in # libtool.m4 will add one of these switches to LD - *-32|*"-32 "|*-melf32bsmip|*"-melf32bsmip ") - libsuff= shlibsuff= libmagic=32-bit;; - *-n32|*"-n32 "|*-melf32bmipn32|*"-melf32bmipn32 ") - libsuff=32 shlibsuff=N32 libmagic=N32;; - *-64|*"-64 "|*-melf64bmip|*"-melf64bmip ") - libsuff=64 shlibsuff=64 libmagic=64-bit;; - *) libsuff= shlibsuff= libmagic=never-match;; - esac - ;; - esac - shlibpath_var=LD_LIBRARY${shlibsuff}_PATH - shlibpath_overrides_runpath=no - sys_lib_search_path_spec="/usr/lib${libsuff} /lib${libsuff} /usr/local/lib${libsuff}" - sys_lib_dlsearch_path_spec="/usr/lib${libsuff} /lib${libsuff}" - hardcode_into_libs=yes - ;; - -# No shared lib support for Linux oldld, aout, or coff. -linux*oldld* | linux*aout* | linux*coff*) - dynamic_linker=no - ;; - -# This must be glibc/ELF. -linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) - version_type=linux # correct to gnu/linux during the next big refactor - need_lib_prefix=no - need_version=no - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - finish_cmds='PATH="\$PATH:/sbin" ldconfig -n $libdir' - shlibpath_var=LD_LIBRARY_PATH - shlibpath_overrides_runpath=no - - # Some binutils ld are patched to set DT_RUNPATH - AC_CACHE_VAL([lt_cv_shlibpath_overrides_runpath], - [lt_cv_shlibpath_overrides_runpath=no - save_LDFLAGS=$LDFLAGS - save_libdir=$libdir - eval "libdir=/foo; wl=\"$_LT_TAGVAR(lt_prog_compiler_wl, $1)\"; \ - LDFLAGS=\"\$LDFLAGS $_LT_TAGVAR(hardcode_libdir_flag_spec, $1)\"" - AC_LINK_IFELSE([AC_LANG_PROGRAM([],[])], - [AS_IF([ ($OBJDUMP -p conftest$ac_exeext) 2>/dev/null | grep "RUNPATH.*$libdir" >/dev/null], - [lt_cv_shlibpath_overrides_runpath=yes])]) - LDFLAGS=$save_LDFLAGS - libdir=$save_libdir - ]) - shlibpath_overrides_runpath=$lt_cv_shlibpath_overrides_runpath - - # This implies no fast_install, which is unacceptable. - # Some rework will be needed to allow for fast_install - # before this can be enabled. - hardcode_into_libs=yes - - # Append ld.so.conf contents to the search path - if test -f /etc/ld.so.conf; then - lt_ld_extra=`awk '/^include / { system(sprintf("cd /etc; cat %s 2>/dev/null", \[$]2)); skip = 1; } { if (!skip) print \[$]0; skip = 0; }' < /etc/ld.so.conf | $SED -e 's/#.*//;/^[ ]*hwcap[ ]/d;s/[:, ]/ /g;s/=[^=]*$//;s/=[^= ]* / /g;s/"//g;/^$/d' | tr '\n' ' '` - sys_lib_dlsearch_path_spec="/lib /usr/lib $lt_ld_extra" - fi - - # We used to test for /lib/ld.so.1 and disable shared libraries on - # powerpc, because MkLinux only supported shared libraries with the - # GNU dynamic linker. Since this was broken with cross compilers, - # most powerpc-linux boxes support dynamic linking these days and - # people can always --disable-shared, the test was removed, and we - # assume the GNU/Linux dynamic linker is in use. - dynamic_linker='GNU/Linux ld.so' - ;; - -netbsdelf*-gnu) - version_type=linux - need_lib_prefix=no - need_version=no - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - shlibpath_var=LD_LIBRARY_PATH - shlibpath_overrides_runpath=no - hardcode_into_libs=yes - dynamic_linker='NetBSD ld.elf_so' - ;; - -netbsd*) - version_type=sunos - need_lib_prefix=no - need_version=no - if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix' - finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' - dynamic_linker='NetBSD (a.out) ld.so' - else - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - dynamic_linker='NetBSD ld.elf_so' - fi - shlibpath_var=LD_LIBRARY_PATH - shlibpath_overrides_runpath=yes - hardcode_into_libs=yes - ;; - -newsos6) - version_type=linux # correct to gnu/linux during the next big refactor - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' - shlibpath_var=LD_LIBRARY_PATH - shlibpath_overrides_runpath=yes - ;; - -*nto* | *qnx*) - version_type=qnx - need_lib_prefix=no - need_version=no - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - shlibpath_var=LD_LIBRARY_PATH - shlibpath_overrides_runpath=no - hardcode_into_libs=yes - dynamic_linker='ldqnx.so' - ;; - -openbsd*) - version_type=sunos - sys_lib_dlsearch_path_spec="/usr/lib" - need_lib_prefix=no - # Some older versions of OpenBSD (3.3 at least) *do* need versioned libs. - case $host_os in - openbsd3.3 | openbsd3.3.*) need_version=yes ;; - *) need_version=no ;; - esac - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix' - finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' - shlibpath_var=LD_LIBRARY_PATH - if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then - case $host_os in - openbsd2.[[89]] | openbsd2.[[89]].*) - shlibpath_overrides_runpath=no - ;; - *) - shlibpath_overrides_runpath=yes - ;; - esac - else - shlibpath_overrides_runpath=yes - fi - ;; - -os2*) - libname_spec='$name' - shrext_cmds=".dll" - need_lib_prefix=no - library_names_spec='$libname${shared_ext} $libname.a' - dynamic_linker='OS/2 ld.exe' - shlibpath_var=LIBPATH - ;; - -osf3* | osf4* | osf5*) - version_type=osf - need_lib_prefix=no - need_version=no - soname_spec='${libname}${release}${shared_ext}$major' - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' - shlibpath_var=LD_LIBRARY_PATH - sys_lib_search_path_spec="/usr/shlib /usr/ccs/lib /usr/lib/cmplrs/cc /usr/lib /usr/local/lib /var/shlib" - sys_lib_dlsearch_path_spec="$sys_lib_search_path_spec" - ;; - -rdos*) - dynamic_linker=no - ;; - -solaris*) - version_type=linux # correct to gnu/linux during the next big refactor - need_lib_prefix=no - need_version=no - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - shlibpath_var=LD_LIBRARY_PATH - shlibpath_overrides_runpath=yes - hardcode_into_libs=yes - # ldd complains unless libraries are executable - postinstall_cmds='chmod +x $lib' - ;; - -sunos4*) - version_type=sunos - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix' - finish_cmds='PATH="\$PATH:/usr/etc" ldconfig $libdir' - shlibpath_var=LD_LIBRARY_PATH - shlibpath_overrides_runpath=yes - if test "$with_gnu_ld" = yes; then - need_lib_prefix=no - fi - need_version=yes - ;; - -sysv4 | sysv4.3*) - version_type=linux # correct to gnu/linux during the next big refactor - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - shlibpath_var=LD_LIBRARY_PATH - case $host_vendor in - sni) - shlibpath_overrides_runpath=no - need_lib_prefix=no - runpath_var=LD_RUN_PATH - ;; - siemens) - need_lib_prefix=no - ;; - motorola) - need_lib_prefix=no - need_version=no - shlibpath_overrides_runpath=no - sys_lib_search_path_spec='/lib /usr/lib /usr/ccs/lib' - ;; - esac - ;; - -sysv4*MP*) - if test -d /usr/nec ;then - version_type=linux # correct to gnu/linux during the next big refactor - library_names_spec='$libname${shared_ext}.$versuffix $libname${shared_ext}.$major $libname${shared_ext}' - soname_spec='$libname${shared_ext}.$major' - shlibpath_var=LD_LIBRARY_PATH - fi - ;; - -sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*) - version_type=freebsd-elf - need_lib_prefix=no - need_version=no - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext} $libname${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - shlibpath_var=LD_LIBRARY_PATH - shlibpath_overrides_runpath=yes - hardcode_into_libs=yes - if test "$with_gnu_ld" = yes; then - sys_lib_search_path_spec='/usr/local/lib /usr/gnu/lib /usr/ccs/lib /usr/lib /lib' - else - sys_lib_search_path_spec='/usr/ccs/lib /usr/lib' - case $host_os in - sco3.2v5*) - sys_lib_search_path_spec="$sys_lib_search_path_spec /lib" - ;; - esac - fi - sys_lib_dlsearch_path_spec='/usr/lib' - ;; - -tpf*) - # TPF is a cross-target only. Preferred cross-host = GNU/Linux. - version_type=linux # correct to gnu/linux during the next big refactor - need_lib_prefix=no - need_version=no - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' - shlibpath_var=LD_LIBRARY_PATH - shlibpath_overrides_runpath=no - hardcode_into_libs=yes - ;; - -uts4*) - version_type=linux # correct to gnu/linux during the next big refactor - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - shlibpath_var=LD_LIBRARY_PATH - ;; - -*) - dynamic_linker=no - ;; -esac -AC_MSG_RESULT([$dynamic_linker]) -test "$dynamic_linker" = no && can_build_shared=no - -variables_saved_for_relink="PATH $shlibpath_var $runpath_var" -if test "$GCC" = yes; then - variables_saved_for_relink="$variables_saved_for_relink GCC_EXEC_PREFIX COMPILER_PATH LIBRARY_PATH" -fi - -if test "${lt_cv_sys_lib_search_path_spec+set}" = set; then - sys_lib_search_path_spec="$lt_cv_sys_lib_search_path_spec" -fi -if test "${lt_cv_sys_lib_dlsearch_path_spec+set}" = set; then - sys_lib_dlsearch_path_spec="$lt_cv_sys_lib_dlsearch_path_spec" -fi - -_LT_DECL([], [variables_saved_for_relink], [1], - [Variables whose values should be saved in libtool wrapper scripts and - restored at link time]) -_LT_DECL([], [need_lib_prefix], [0], - [Do we need the "lib" prefix for modules?]) -_LT_DECL([], [need_version], [0], [Do we need a version for libraries?]) -_LT_DECL([], [version_type], [0], [Library versioning type]) -_LT_DECL([], [runpath_var], [0], [Shared library runtime path variable]) -_LT_DECL([], [shlibpath_var], [0],[Shared library path variable]) -_LT_DECL([], [shlibpath_overrides_runpath], [0], - [Is shlibpath searched before the hard-coded library search path?]) -_LT_DECL([], [libname_spec], [1], [Format of library name prefix]) -_LT_DECL([], [library_names_spec], [1], - [[List of archive names. First name is the real one, the rest are links. - The last name is the one that the linker finds with -lNAME]]) -_LT_DECL([], [soname_spec], [1], - [[The coded name of the library, if different from the real name]]) -_LT_DECL([], [install_override_mode], [1], - [Permission mode override for installation of shared libraries]) -_LT_DECL([], [postinstall_cmds], [2], - [Command to use after installation of a shared archive]) -_LT_DECL([], [postuninstall_cmds], [2], - [Command to use after uninstallation of a shared archive]) -_LT_DECL([], [finish_cmds], [2], - [Commands used to finish a libtool library installation in a directory]) -_LT_DECL([], [finish_eval], [1], - [[As "finish_cmds", except a single script fragment to be evaled but - not shown]]) -_LT_DECL([], [hardcode_into_libs], [0], - [Whether we should hardcode library paths into libraries]) -_LT_DECL([], [sys_lib_search_path_spec], [2], - [Compile-time system search path for libraries]) -_LT_DECL([], [sys_lib_dlsearch_path_spec], [2], - [Run-time system search path for libraries]) -])# _LT_SYS_DYNAMIC_LINKER - - -# _LT_PATH_TOOL_PREFIX(TOOL) -# -------------------------- -# find a file program which can recognize shared library -AC_DEFUN([_LT_PATH_TOOL_PREFIX], -[m4_require([_LT_DECL_EGREP])dnl -AC_MSG_CHECKING([for $1]) -AC_CACHE_VAL(lt_cv_path_MAGIC_CMD, -[case $MAGIC_CMD in -[[\\/*] | ?:[\\/]*]) - lt_cv_path_MAGIC_CMD="$MAGIC_CMD" # Let the user override the test with a path. - ;; -*) - lt_save_MAGIC_CMD="$MAGIC_CMD" - lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR -dnl $ac_dummy forces splitting on constant user-supplied paths. -dnl POSIX.2 word splitting is done only on the output of word expansions, -dnl not every word. This closes a longstanding sh security hole. - ac_dummy="m4_if([$2], , $PATH, [$2])" - for ac_dir in $ac_dummy; do - IFS="$lt_save_ifs" - test -z "$ac_dir" && ac_dir=. - if test -f $ac_dir/$1; then - lt_cv_path_MAGIC_CMD="$ac_dir/$1" - if test -n "$file_magic_test_file"; then - case $deplibs_check_method in - "file_magic "*) - file_magic_regex=`expr "$deplibs_check_method" : "file_magic \(.*\)"` - MAGIC_CMD="$lt_cv_path_MAGIC_CMD" - if eval $file_magic_cmd \$file_magic_test_file 2> /dev/null | - $EGREP "$file_magic_regex" > /dev/null; then - : - else - cat <<_LT_EOF 1>&2 - -*** Warning: the command libtool uses to detect shared libraries, -*** $file_magic_cmd, produces output that libtool cannot recognize. -*** The result is that libtool may fail to recognize shared libraries -*** as such. This will affect the creation of libtool libraries that -*** depend on shared libraries, but programs linked with such libtool -*** libraries will work regardless of this problem. Nevertheless, you -*** may want to report the problem to your system manager and/or to -*** bug-libtool@gnu.org - -_LT_EOF - fi ;; - esac - fi - break - fi - done - IFS="$lt_save_ifs" - MAGIC_CMD="$lt_save_MAGIC_CMD" - ;; -esac]) -MAGIC_CMD="$lt_cv_path_MAGIC_CMD" -if test -n "$MAGIC_CMD"; then - AC_MSG_RESULT($MAGIC_CMD) -else - AC_MSG_RESULT(no) -fi -_LT_DECL([], [MAGIC_CMD], [0], - [Used to examine libraries when file_magic_cmd begins with "file"])dnl -])# _LT_PATH_TOOL_PREFIX - -# Old name: -AU_ALIAS([AC_PATH_TOOL_PREFIX], [_LT_PATH_TOOL_PREFIX]) -dnl aclocal-1.4 backwards compatibility: -dnl AC_DEFUN([AC_PATH_TOOL_PREFIX], []) - - -# _LT_PATH_MAGIC -# -------------- -# find a file program which can recognize a shared library -m4_defun([_LT_PATH_MAGIC], -[_LT_PATH_TOOL_PREFIX(${ac_tool_prefix}file, /usr/bin$PATH_SEPARATOR$PATH) -if test -z "$lt_cv_path_MAGIC_CMD"; then - if test -n "$ac_tool_prefix"; then - _LT_PATH_TOOL_PREFIX(file, /usr/bin$PATH_SEPARATOR$PATH) - else - MAGIC_CMD=: - fi -fi -])# _LT_PATH_MAGIC - - -# LT_PATH_LD -# ---------- -# find the pathname to the GNU or non-GNU linker -AC_DEFUN([LT_PATH_LD], -[AC_REQUIRE([AC_PROG_CC])dnl -AC_REQUIRE([AC_CANONICAL_HOST])dnl -AC_REQUIRE([AC_CANONICAL_BUILD])dnl -m4_require([_LT_DECL_SED])dnl -m4_require([_LT_DECL_EGREP])dnl -m4_require([_LT_PROG_ECHO_BACKSLASH])dnl - -AC_ARG_WITH([gnu-ld], - [AS_HELP_STRING([--with-gnu-ld], - [assume the C compiler uses GNU ld @<:@default=no@:>@])], - [test "$withval" = no || with_gnu_ld=yes], - [with_gnu_ld=no])dnl - -ac_prog=ld -if test "$GCC" = yes; then - # Check if gcc -print-prog-name=ld gives a path. - AC_MSG_CHECKING([for ld used by $CC]) - case $host in - *-*-mingw*) - # gcc leaves a trailing carriage return which upsets mingw - ac_prog=`($CC -print-prog-name=ld) 2>&5 | tr -d '\015'` ;; - *) - ac_prog=`($CC -print-prog-name=ld) 2>&5` ;; - esac - case $ac_prog in - # Accept absolute paths. - [[\\/]]* | ?:[[\\/]]*) - re_direlt='/[[^/]][[^/]]*/\.\./' - # Canonicalize the pathname of ld - ac_prog=`$ECHO "$ac_prog"| $SED 's%\\\\%/%g'` - while $ECHO "$ac_prog" | $GREP "$re_direlt" > /dev/null 2>&1; do - ac_prog=`$ECHO $ac_prog| $SED "s%$re_direlt%/%"` - done - test -z "$LD" && LD="$ac_prog" - ;; - "") - # If it fails, then pretend we aren't using GCC. - ac_prog=ld - ;; - *) - # If it is relative, then search for the first ld in PATH. - with_gnu_ld=unknown - ;; - esac -elif test "$with_gnu_ld" = yes; then - AC_MSG_CHECKING([for GNU ld]) -else - AC_MSG_CHECKING([for non-GNU ld]) -fi -AC_CACHE_VAL(lt_cv_path_LD, -[if test -z "$LD"; then - lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR - for ac_dir in $PATH; do - IFS="$lt_save_ifs" - test -z "$ac_dir" && ac_dir=. - if test -f "$ac_dir/$ac_prog" || test -f "$ac_dir/$ac_prog$ac_exeext"; then - lt_cv_path_LD="$ac_dir/$ac_prog" - # Check to see if the program is GNU ld. I'd rather use --version, - # but apparently some variants of GNU ld only accept -v. - # Break only if it was the GNU/non-GNU ld that we prefer. - case `"$lt_cv_path_LD" -v 2>&1 &1 /dev/null 2>&1; then - lt_cv_deplibs_check_method='file_magic ^x86 archive import|^x86 DLL' - lt_cv_file_magic_cmd='func_win32_libid' - else - # Keep this pattern in sync with the one in func_win32_libid. - lt_cv_deplibs_check_method='file_magic file format (pei*-i386(.*architecture: i386)?|pe-arm-wince|pe-x86-64)' - lt_cv_file_magic_cmd='$OBJDUMP -f' - fi - ;; - -cegcc*) - # use the weaker test based on 'objdump'. See mingw*. - lt_cv_deplibs_check_method='file_magic file format pe-arm-.*little(.*architecture: arm)?' - lt_cv_file_magic_cmd='$OBJDUMP -f' - ;; - -darwin* | rhapsody*) - lt_cv_deplibs_check_method=pass_all - ;; - -freebsd* | dragonfly*) - if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then - case $host_cpu in - i*86 ) - # Not sure whether the presence of OpenBSD here was a mistake. - # Let's accept both of them until this is cleared up. - lt_cv_deplibs_check_method='file_magic (FreeBSD|OpenBSD|DragonFly)/i[[3-9]]86 (compact )?demand paged shared library' - lt_cv_file_magic_cmd=/usr/bin/file - lt_cv_file_magic_test_file=`echo /usr/lib/libc.so.*` - ;; - esac - else - lt_cv_deplibs_check_method=pass_all - fi - ;; - -haiku*) - lt_cv_deplibs_check_method=pass_all - ;; - -hpux10.20* | hpux11*) - lt_cv_file_magic_cmd=/usr/bin/file - case $host_cpu in - ia64*) - lt_cv_deplibs_check_method='file_magic (s[[0-9]][[0-9]][[0-9]]|ELF-[[0-9]][[0-9]]) shared object file - IA64' - lt_cv_file_magic_test_file=/usr/lib/hpux32/libc.so - ;; - hppa*64*) - [lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|ELF[ -][0-9][0-9])(-bit)?( [LM]SB)? shared object( file)?[, -]* PA-RISC [0-9]\.[0-9]'] - lt_cv_file_magic_test_file=/usr/lib/pa20_64/libc.sl - ;; - *) - lt_cv_deplibs_check_method='file_magic (s[[0-9]][[0-9]][[0-9]]|PA-RISC[[0-9]]\.[[0-9]]) shared library' - lt_cv_file_magic_test_file=/usr/lib/libc.sl - ;; - esac - ;; - -interix[[3-9]]*) - # PIC code is broken on Interix 3.x, that's why |\.a not |_pic\.a here - lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so|\.a)$' - ;; - -irix5* | irix6* | nonstopux*) - case $LD in - *-32|*"-32 ") libmagic=32-bit;; - *-n32|*"-n32 ") libmagic=N32;; - *-64|*"-64 ") libmagic=64-bit;; - *) libmagic=never-match;; - esac - lt_cv_deplibs_check_method=pass_all - ;; - -# This must be glibc/ELF. -linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) - lt_cv_deplibs_check_method=pass_all - ;; - -netbsd* | netbsdelf*-gnu) - if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then - lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so\.[[0-9]]+\.[[0-9]]+|_pic\.a)$' - else - lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so|_pic\.a)$' - fi - ;; - -newos6*) - lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[ML]]SB (executable|dynamic lib)' - lt_cv_file_magic_cmd=/usr/bin/file - lt_cv_file_magic_test_file=/usr/lib/libnls.so - ;; - -*nto* | *qnx*) - lt_cv_deplibs_check_method=pass_all - ;; - -openbsd*) - if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then - lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so\.[[0-9]]+\.[[0-9]]+|\.so|_pic\.a)$' - else - lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so\.[[0-9]]+\.[[0-9]]+|_pic\.a)$' - fi - ;; - -osf3* | osf4* | osf5*) - lt_cv_deplibs_check_method=pass_all - ;; - -rdos*) - lt_cv_deplibs_check_method=pass_all - ;; - -solaris*) - lt_cv_deplibs_check_method=pass_all - ;; - -sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*) - lt_cv_deplibs_check_method=pass_all - ;; - -sysv4 | sysv4.3*) - case $host_vendor in - motorola) - lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[ML]]SB (shared object|dynamic lib) M[[0-9]][[0-9]]* Version [[0-9]]' - lt_cv_file_magic_test_file=`echo /usr/lib/libc.so*` - ;; - ncr) - lt_cv_deplibs_check_method=pass_all - ;; - sequent) - lt_cv_file_magic_cmd='/bin/file' - lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[LM]]SB (shared object|dynamic lib )' - ;; - sni) - lt_cv_file_magic_cmd='/bin/file' - lt_cv_deplibs_check_method="file_magic ELF [[0-9]][[0-9]]*-bit [[LM]]SB dynamic lib" - lt_cv_file_magic_test_file=/lib/libc.so - ;; - siemens) - lt_cv_deplibs_check_method=pass_all - ;; - pc) - lt_cv_deplibs_check_method=pass_all - ;; - esac - ;; - -tpf*) - lt_cv_deplibs_check_method=pass_all - ;; -esac -]) - -file_magic_glob= -want_nocaseglob=no -if test "$build" = "$host"; then - case $host_os in - mingw* | pw32*) - if ( shopt | grep nocaseglob ) >/dev/null 2>&1; then - want_nocaseglob=yes - else - file_magic_glob=`echo aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ | $SED -e "s/\(..\)/s\/[[\1]]\/[[\1]]\/g;/g"` - fi - ;; - esac -fi - -file_magic_cmd=$lt_cv_file_magic_cmd -deplibs_check_method=$lt_cv_deplibs_check_method -test -z "$deplibs_check_method" && deplibs_check_method=unknown - -_LT_DECL([], [deplibs_check_method], [1], - [Method to check whether dependent libraries are shared objects]) -_LT_DECL([], [file_magic_cmd], [1], - [Command to use when deplibs_check_method = "file_magic"]) -_LT_DECL([], [file_magic_glob], [1], - [How to find potential files when deplibs_check_method = "file_magic"]) -_LT_DECL([], [want_nocaseglob], [1], - [Find potential files using nocaseglob when deplibs_check_method = "file_magic"]) -])# _LT_CHECK_MAGIC_METHOD - - -# LT_PATH_NM -# ---------- -# find the pathname to a BSD- or MS-compatible name lister -AC_DEFUN([LT_PATH_NM], -[AC_REQUIRE([AC_PROG_CC])dnl -AC_CACHE_CHECK([for BSD- or MS-compatible name lister (nm)], lt_cv_path_NM, -[if test -n "$NM"; then - # Let the user override the test. - lt_cv_path_NM="$NM" -else - lt_nm_to_check="${ac_tool_prefix}nm" - if test -n "$ac_tool_prefix" && test "$build" = "$host"; then - lt_nm_to_check="$lt_nm_to_check nm" - fi - for lt_tmp_nm in $lt_nm_to_check; do - lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR - for ac_dir in $PATH /usr/ccs/bin/elf /usr/ccs/bin /usr/ucb /bin; do - IFS="$lt_save_ifs" - test -z "$ac_dir" && ac_dir=. - tmp_nm="$ac_dir/$lt_tmp_nm" - if test -f "$tmp_nm" || test -f "$tmp_nm$ac_exeext" ; then - # Check to see if the nm accepts a BSD-compat flag. - # Adding the `sed 1q' prevents false positives on HP-UX, which says: - # nm: unknown option "B" ignored - # Tru64's nm complains that /dev/null is an invalid object file - case `"$tmp_nm" -B /dev/null 2>&1 | sed '1q'` in - */dev/null* | *'Invalid file or object type'*) - lt_cv_path_NM="$tmp_nm -B" - break - ;; - *) - case `"$tmp_nm" -p /dev/null 2>&1 | sed '1q'` in - */dev/null*) - lt_cv_path_NM="$tmp_nm -p" - break - ;; - *) - lt_cv_path_NM=${lt_cv_path_NM="$tmp_nm"} # keep the first match, but - continue # so that we can try to find one that supports BSD flags - ;; - esac - ;; - esac - fi - done - IFS="$lt_save_ifs" - done - : ${lt_cv_path_NM=no} -fi]) -if test "$lt_cv_path_NM" != "no"; then - NM="$lt_cv_path_NM" -else - # Didn't find any BSD compatible name lister, look for dumpbin. - if test -n "$DUMPBIN"; then : - # Let the user override the test. - else - AC_CHECK_TOOLS(DUMPBIN, [dumpbin "link -dump"], :) - case `$DUMPBIN -symbols /dev/null 2>&1 | sed '1q'` in - *COFF*) - DUMPBIN="$DUMPBIN -symbols" - ;; - *) - DUMPBIN=: - ;; - esac - fi - AC_SUBST([DUMPBIN]) - if test "$DUMPBIN" != ":"; then - NM="$DUMPBIN" - fi -fi -test -z "$NM" && NM=nm -AC_SUBST([NM]) -_LT_DECL([], [NM], [1], [A BSD- or MS-compatible name lister])dnl - -AC_CACHE_CHECK([the name lister ($NM) interface], [lt_cv_nm_interface], - [lt_cv_nm_interface="BSD nm" - echo "int some_variable = 0;" > conftest.$ac_ext - (eval echo "\"\$as_me:$LINENO: $ac_compile\"" >&AS_MESSAGE_LOG_FD) - (eval "$ac_compile" 2>conftest.err) - cat conftest.err >&AS_MESSAGE_LOG_FD - (eval echo "\"\$as_me:$LINENO: $NM \\\"conftest.$ac_objext\\\"\"" >&AS_MESSAGE_LOG_FD) - (eval "$NM \"conftest.$ac_objext\"" 2>conftest.err > conftest.out) - cat conftest.err >&AS_MESSAGE_LOG_FD - (eval echo "\"\$as_me:$LINENO: output\"" >&AS_MESSAGE_LOG_FD) - cat conftest.out >&AS_MESSAGE_LOG_FD - if $GREP 'External.*some_variable' conftest.out > /dev/null; then - lt_cv_nm_interface="MS dumpbin" - fi - rm -f conftest*]) -])# LT_PATH_NM - -# Old names: -AU_ALIAS([AM_PROG_NM], [LT_PATH_NM]) -AU_ALIAS([AC_PROG_NM], [LT_PATH_NM]) -dnl aclocal-1.4 backwards compatibility: -dnl AC_DEFUN([AM_PROG_NM], []) -dnl AC_DEFUN([AC_PROG_NM], []) - -# _LT_CHECK_SHAREDLIB_FROM_LINKLIB -# -------------------------------- -# how to determine the name of the shared library -# associated with a specific link library. -# -- PORTME fill in with the dynamic library characteristics -m4_defun([_LT_CHECK_SHAREDLIB_FROM_LINKLIB], -[m4_require([_LT_DECL_EGREP]) -m4_require([_LT_DECL_OBJDUMP]) -m4_require([_LT_DECL_DLLTOOL]) -AC_CACHE_CHECK([how to associate runtime and link libraries], -lt_cv_sharedlib_from_linklib_cmd, -[lt_cv_sharedlib_from_linklib_cmd='unknown' - -case $host_os in -cygwin* | mingw* | pw32* | cegcc*) - # two different shell functions defined in ltmain.sh - # decide which to use based on capabilities of $DLLTOOL - case `$DLLTOOL --help 2>&1` in - *--identify-strict*) - lt_cv_sharedlib_from_linklib_cmd=func_cygming_dll_for_implib - ;; - *) - lt_cv_sharedlib_from_linklib_cmd=func_cygming_dll_for_implib_fallback - ;; - esac - ;; -*) - # fallback: assume linklib IS sharedlib - lt_cv_sharedlib_from_linklib_cmd="$ECHO" - ;; -esac -]) -sharedlib_from_linklib_cmd=$lt_cv_sharedlib_from_linklib_cmd -test -z "$sharedlib_from_linklib_cmd" && sharedlib_from_linklib_cmd=$ECHO - -_LT_DECL([], [sharedlib_from_linklib_cmd], [1], - [Command to associate shared and link libraries]) -])# _LT_CHECK_SHAREDLIB_FROM_LINKLIB - - -# _LT_PATH_MANIFEST_TOOL -# ---------------------- -# locate the manifest tool -m4_defun([_LT_PATH_MANIFEST_TOOL], -[AC_CHECK_TOOL(MANIFEST_TOOL, mt, :) -test -z "$MANIFEST_TOOL" && MANIFEST_TOOL=mt -AC_CACHE_CHECK([if $MANIFEST_TOOL is a manifest tool], [lt_cv_path_mainfest_tool], - [lt_cv_path_mainfest_tool=no - echo "$as_me:$LINENO: $MANIFEST_TOOL '-?'" >&AS_MESSAGE_LOG_FD - $MANIFEST_TOOL '-?' 2>conftest.err > conftest.out - cat conftest.err >&AS_MESSAGE_LOG_FD - if $GREP 'Manifest Tool' conftest.out > /dev/null; then - lt_cv_path_mainfest_tool=yes - fi - rm -f conftest*]) -if test "x$lt_cv_path_mainfest_tool" != xyes; then - MANIFEST_TOOL=: -fi -_LT_DECL([], [MANIFEST_TOOL], [1], [Manifest tool])dnl -])# _LT_PATH_MANIFEST_TOOL - - -# LT_LIB_M -# -------- -# check for math library -AC_DEFUN([LT_LIB_M], -[AC_REQUIRE([AC_CANONICAL_HOST])dnl -LIBM= -case $host in -*-*-beos* | *-*-cegcc* | *-*-cygwin* | *-*-haiku* | *-*-pw32* | *-*-darwin*) - # These system don't have libm, or don't need it - ;; -*-ncr-sysv4.3*) - AC_CHECK_LIB(mw, _mwvalidcheckl, LIBM="-lmw") - AC_CHECK_LIB(m, cos, LIBM="$LIBM -lm") - ;; -*) - AC_CHECK_LIB(m, cos, LIBM="-lm") - ;; -esac -AC_SUBST([LIBM]) -])# LT_LIB_M - -# Old name: -AU_ALIAS([AC_CHECK_LIBM], [LT_LIB_M]) -dnl aclocal-1.4 backwards compatibility: -dnl AC_DEFUN([AC_CHECK_LIBM], []) - - -# _LT_COMPILER_NO_RTTI([TAGNAME]) -# ------------------------------- -m4_defun([_LT_COMPILER_NO_RTTI], -[m4_require([_LT_TAG_COMPILER])dnl - -_LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)= - -if test "$GCC" = yes; then - case $cc_basename in - nvcc*) - _LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)=' -Xcompiler -fno-builtin' ;; - *) - _LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)=' -fno-builtin' ;; - esac - - _LT_COMPILER_OPTION([if $compiler supports -fno-rtti -fno-exceptions], - lt_cv_prog_compiler_rtti_exceptions, - [-fno-rtti -fno-exceptions], [], - [_LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)="$_LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1) -fno-rtti -fno-exceptions"]) -fi -_LT_TAGDECL([no_builtin_flag], [lt_prog_compiler_no_builtin_flag], [1], - [Compiler flag to turn off builtin functions]) -])# _LT_COMPILER_NO_RTTI - - -# _LT_CMD_GLOBAL_SYMBOLS -# ---------------------- -m4_defun([_LT_CMD_GLOBAL_SYMBOLS], -[AC_REQUIRE([AC_CANONICAL_HOST])dnl -AC_REQUIRE([AC_PROG_CC])dnl -AC_REQUIRE([AC_PROG_AWK])dnl -AC_REQUIRE([LT_PATH_NM])dnl -AC_REQUIRE([LT_PATH_LD])dnl -m4_require([_LT_DECL_SED])dnl -m4_require([_LT_DECL_EGREP])dnl -m4_require([_LT_TAG_COMPILER])dnl - -# Check for command to grab the raw symbol name followed by C symbol from nm. -AC_MSG_CHECKING([command to parse $NM output from $compiler object]) -AC_CACHE_VAL([lt_cv_sys_global_symbol_pipe], -[ -# These are sane defaults that work on at least a few old systems. -# [They come from Ultrix. What could be older than Ultrix?!! ;)] - -# Character class describing NM global symbol codes. -symcode='[[BCDEGRST]]' - -# Regexp to match symbols that can be accessed directly from C. -sympat='\([[_A-Za-z]][[_A-Za-z0-9]]*\)' - -# Define system-specific variables. -case $host_os in -aix*) - symcode='[[BCDT]]' - ;; -cygwin* | mingw* | pw32* | cegcc*) - symcode='[[ABCDGISTW]]' - ;; -hpux*) - if test "$host_cpu" = ia64; then - symcode='[[ABCDEGRST]]' - fi - ;; -irix* | nonstopux*) - symcode='[[BCDEGRST]]' - ;; -osf*) - symcode='[[BCDEGQRST]]' - ;; -solaris*) - symcode='[[BDRT]]' - ;; -sco3.2v5*) - symcode='[[DT]]' - ;; -sysv4.2uw2*) - symcode='[[DT]]' - ;; -sysv5* | sco5v6* | unixware* | OpenUNIX*) - symcode='[[ABDT]]' - ;; -sysv4) - symcode='[[DFNSTU]]' - ;; -esac - -# If we're using GNU nm, then use its standard symbol codes. -case `$NM -V 2>&1` in -*GNU* | *'with BFD'*) - symcode='[[ABCDGIRSTW]]' ;; -esac - -# Transform an extracted symbol line into a proper C declaration. -# Some systems (esp. on ia64) link data and code symbols differently, -# so use this general approach. -lt_cv_sys_global_symbol_to_cdecl="sed -n -e 's/^T .* \(.*\)$/extern int \1();/p' -e 's/^$symcode* .* \(.*\)$/extern char \1;/p'" - -# Transform an extracted symbol line into symbol name and symbol address -lt_cv_sys_global_symbol_to_c_name_address="sed -n -e 's/^: \([[^ ]]*\)[[ ]]*$/ {\\\"\1\\\", (void *) 0},/p' -e 's/^$symcode* \([[^ ]]*\) \([[^ ]]*\)$/ {\"\2\", (void *) \&\2},/p'" -lt_cv_sys_global_symbol_to_c_name_address_lib_prefix="sed -n -e 's/^: \([[^ ]]*\)[[ ]]*$/ {\\\"\1\\\", (void *) 0},/p' -e 's/^$symcode* \([[^ ]]*\) \(lib[[^ ]]*\)$/ {\"\2\", (void *) \&\2},/p' -e 's/^$symcode* \([[^ ]]*\) \([[^ ]]*\)$/ {\"lib\2\", (void *) \&\2},/p'" - -# Handle CRLF in mingw tool chain -opt_cr= -case $build_os in -mingw*) - opt_cr=`$ECHO 'x\{0,1\}' | tr x '\015'` # option cr in regexp - ;; -esac - -# Try without a prefix underscore, then with it. -for ac_symprfx in "" "_"; do - - # Transform symcode, sympat, and symprfx into a raw symbol and a C symbol. - symxfrm="\\1 $ac_symprfx\\2 \\2" - - # Write the raw and C identifiers. - if test "$lt_cv_nm_interface" = "MS dumpbin"; then - # Fake it for dumpbin and say T for any non-static function - # and D for any global variable. - # Also find C++ and __fastcall symbols from MSVC++, - # which start with @ or ?. - lt_cv_sys_global_symbol_pipe="$AWK ['"\ -" {last_section=section; section=\$ 3};"\ -" /^COFF SYMBOL TABLE/{for(i in hide) delete hide[i]};"\ -" /Section length .*#relocs.*(pick any)/{hide[last_section]=1};"\ -" \$ 0!~/External *\|/{next};"\ -" / 0+ UNDEF /{next}; / UNDEF \([^|]\)*()/{next};"\ -" {if(hide[section]) next};"\ -" {f=0}; \$ 0~/\(\).*\|/{f=1}; {printf f ? \"T \" : \"D \"};"\ -" {split(\$ 0, a, /\||\r/); split(a[2], s)};"\ -" s[1]~/^[@?]/{print s[1], s[1]; next};"\ -" s[1]~prfx {split(s[1],t,\"@\"); print t[1], substr(t[1],length(prfx))}"\ -" ' prfx=^$ac_symprfx]" - else - lt_cv_sys_global_symbol_pipe="sed -n -e 's/^.*[[ ]]\($symcode$symcode*\)[[ ]][[ ]]*$ac_symprfx$sympat$opt_cr$/$symxfrm/p'" - fi - lt_cv_sys_global_symbol_pipe="$lt_cv_sys_global_symbol_pipe | sed '/ __gnu_lto/d'" - - # Check to see that the pipe works correctly. - pipe_works=no - - rm -f conftest* - cat > conftest.$ac_ext <<_LT_EOF -#ifdef __cplusplus -extern "C" { -#endif -char nm_test_var; -void nm_test_func(void); -void nm_test_func(void){} -#ifdef __cplusplus -} -#endif -int main(){nm_test_var='a';nm_test_func();return(0);} -_LT_EOF - - if AC_TRY_EVAL(ac_compile); then - # Now try to grab the symbols. - nlist=conftest.nm - if AC_TRY_EVAL(NM conftest.$ac_objext \| "$lt_cv_sys_global_symbol_pipe" \> $nlist) && test -s "$nlist"; then - # Try sorting and uniquifying the output. - if sort "$nlist" | uniq > "$nlist"T; then - mv -f "$nlist"T "$nlist" - else - rm -f "$nlist"T - fi - - # Make sure that we snagged all the symbols we need. - if $GREP ' nm_test_var$' "$nlist" >/dev/null; then - if $GREP ' nm_test_func$' "$nlist" >/dev/null; then - cat <<_LT_EOF > conftest.$ac_ext -/* Keep this code in sync between libtool.m4, ltmain, lt_system.h, and tests. */ -#if defined(_WIN32) || defined(__CYGWIN__) || defined(_WIN32_WCE) -/* DATA imports from DLLs on WIN32 con't be const, because runtime - relocations are performed -- see ld's documentation on pseudo-relocs. */ -# define LT@&t@_DLSYM_CONST -#elif defined(__osf__) -/* This system does not cope well with relocations in const data. */ -# define LT@&t@_DLSYM_CONST -#else -# define LT@&t@_DLSYM_CONST const -#endif - -#ifdef __cplusplus -extern "C" { -#endif - -_LT_EOF - # Now generate the symbol file. - eval "$lt_cv_sys_global_symbol_to_cdecl"' < "$nlist" | $GREP -v main >> conftest.$ac_ext' - - cat <<_LT_EOF >> conftest.$ac_ext - -/* The mapping between symbol names and symbols. */ -LT@&t@_DLSYM_CONST struct { - const char *name; - void *address; -} -lt__PROGRAM__LTX_preloaded_symbols[[]] = -{ - { "@PROGRAM@", (void *) 0 }, -_LT_EOF - $SED "s/^$symcode$symcode* \(.*\) \(.*\)$/ {\"\2\", (void *) \&\2},/" < "$nlist" | $GREP -v main >> conftest.$ac_ext - cat <<\_LT_EOF >> conftest.$ac_ext - {0, (void *) 0} -}; - -/* This works around a problem in FreeBSD linker */ -#ifdef FREEBSD_WORKAROUND -static const void *lt_preloaded_setup() { - return lt__PROGRAM__LTX_preloaded_symbols; -} -#endif - -#ifdef __cplusplus -} -#endif -_LT_EOF - # Now try linking the two files. - mv conftest.$ac_objext conftstm.$ac_objext - lt_globsym_save_LIBS=$LIBS - lt_globsym_save_CFLAGS=$CFLAGS - LIBS="conftstm.$ac_objext" - CFLAGS="$CFLAGS$_LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)" - if AC_TRY_EVAL(ac_link) && test -s conftest${ac_exeext}; then - pipe_works=yes - fi - LIBS=$lt_globsym_save_LIBS - CFLAGS=$lt_globsym_save_CFLAGS - else - echo "cannot find nm_test_func in $nlist" >&AS_MESSAGE_LOG_FD - fi - else - echo "cannot find nm_test_var in $nlist" >&AS_MESSAGE_LOG_FD - fi - else - echo "cannot run $lt_cv_sys_global_symbol_pipe" >&AS_MESSAGE_LOG_FD - fi - else - echo "$progname: failed program was:" >&AS_MESSAGE_LOG_FD - cat conftest.$ac_ext >&5 - fi - rm -rf conftest* conftst* - - # Do not use the global_symbol_pipe unless it works. - if test "$pipe_works" = yes; then - break - else - lt_cv_sys_global_symbol_pipe= - fi -done -]) -if test -z "$lt_cv_sys_global_symbol_pipe"; then - lt_cv_sys_global_symbol_to_cdecl= -fi -if test -z "$lt_cv_sys_global_symbol_pipe$lt_cv_sys_global_symbol_to_cdecl"; then - AC_MSG_RESULT(failed) -else - AC_MSG_RESULT(ok) -fi - -# Response file support. -if test "$lt_cv_nm_interface" = "MS dumpbin"; then - nm_file_list_spec='@' -elif $NM --help 2>/dev/null | grep '[[@]]FILE' >/dev/null; then - nm_file_list_spec='@' -fi - -_LT_DECL([global_symbol_pipe], [lt_cv_sys_global_symbol_pipe], [1], - [Take the output of nm and produce a listing of raw symbols and C names]) -_LT_DECL([global_symbol_to_cdecl], [lt_cv_sys_global_symbol_to_cdecl], [1], - [Transform the output of nm in a proper C declaration]) -_LT_DECL([global_symbol_to_c_name_address], - [lt_cv_sys_global_symbol_to_c_name_address], [1], - [Transform the output of nm in a C name address pair]) -_LT_DECL([global_symbol_to_c_name_address_lib_prefix], - [lt_cv_sys_global_symbol_to_c_name_address_lib_prefix], [1], - [Transform the output of nm in a C name address pair when lib prefix is needed]) -_LT_DECL([], [nm_file_list_spec], [1], - [Specify filename containing input files for $NM]) -]) # _LT_CMD_GLOBAL_SYMBOLS - - -# _LT_COMPILER_PIC([TAGNAME]) -# --------------------------- -m4_defun([_LT_COMPILER_PIC], -[m4_require([_LT_TAG_COMPILER])dnl -_LT_TAGVAR(lt_prog_compiler_wl, $1)= -_LT_TAGVAR(lt_prog_compiler_pic, $1)= -_LT_TAGVAR(lt_prog_compiler_static, $1)= - -m4_if([$1], [CXX], [ - # C++ specific cases for pic, static, wl, etc. - if test "$GXX" = yes; then - _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' - _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' - - case $host_os in - aix*) - # All AIX code is PIC. - if test "$host_cpu" = ia64; then - # AIX 5 now supports IA64 processor - _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' - fi - ;; - - amigaos*) - case $host_cpu in - powerpc) - # see comment about AmigaOS4 .so support - _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' - ;; - m68k) - # FIXME: we need at least 68020 code to build shared libraries, but - # adding the `-m68020' flag to GCC prevents building anything better, - # like `-m68040'. - _LT_TAGVAR(lt_prog_compiler_pic, $1)='-m68020 -resident32 -malways-restore-a4' - ;; - esac - ;; - - beos* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*) - # PIC is the default for these OSes. - ;; - mingw* | cygwin* | os2* | pw32* | cegcc*) - # This hack is so that the source file can tell whether it is being - # built for inclusion in a dll (and should export symbols for example). - # Although the cygwin gcc ignores -fPIC, still need this for old-style - # (--disable-auto-import) libraries - m4_if([$1], [GCJ], [], - [_LT_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT']) - ;; - darwin* | rhapsody*) - # PIC is the default on this platform - # Common symbols not allowed in MH_DYLIB files - _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fno-common' - ;; - *djgpp*) - # DJGPP does not support shared libraries at all - _LT_TAGVAR(lt_prog_compiler_pic, $1)= - ;; - haiku*) - # PIC is the default for Haiku. - # The "-static" flag exists, but is broken. - _LT_TAGVAR(lt_prog_compiler_static, $1)= - ;; - interix[[3-9]]*) - # Interix 3.x gcc -fpic/-fPIC options generate broken code. - # Instead, we relocate shared libraries at runtime. - ;; - sysv4*MP*) - if test -d /usr/nec; then - _LT_TAGVAR(lt_prog_compiler_pic, $1)=-Kconform_pic - fi - ;; - hpux*) - # PIC is the default for 64-bit PA HP-UX, but not for 32-bit - # PA HP-UX. On IA64 HP-UX, PIC is the default but the pic flag - # sets the default TLS model and affects inlining. - case $host_cpu in - hppa*64*) - ;; - *) - _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' - ;; - esac - ;; - *qnx* | *nto*) - # QNX uses GNU C++, but need to define -shared option too, otherwise - # it will coredump. - _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC -shared' - ;; - *) - _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' - ;; - esac - else - case $host_os in - aix[[4-9]]*) - # All AIX code is PIC. - if test "$host_cpu" = ia64; then - # AIX 5 now supports IA64 processor - _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' - else - _LT_TAGVAR(lt_prog_compiler_static, $1)='-bnso -bI:/lib/syscalls.exp' - fi - ;; - chorus*) - case $cc_basename in - cxch68*) - # Green Hills C++ Compiler - # _LT_TAGVAR(lt_prog_compiler_static, $1)="--no_auto_instantiation -u __main -u __premain -u _abort -r $COOL_DIR/lib/libOrb.a $MVME_DIR/lib/CC/libC.a $MVME_DIR/lib/classix/libcx.s.a" - ;; - esac - ;; - mingw* | cygwin* | os2* | pw32* | cegcc*) - # This hack is so that the source file can tell whether it is being - # built for inclusion in a dll (and should export symbols for example). - m4_if([$1], [GCJ], [], - [_LT_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT']) - ;; - dgux*) - case $cc_basename in - ec++*) - _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' - ;; - ghcx*) - # Green Hills C++ Compiler - _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic' - ;; - *) - ;; - esac - ;; - freebsd* | dragonfly*) - # FreeBSD uses GNU C++ - ;; - hpux9* | hpux10* | hpux11*) - case $cc_basename in - CC*) - _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' - _LT_TAGVAR(lt_prog_compiler_static, $1)='${wl}-a ${wl}archive' - if test "$host_cpu" != ia64; then - _LT_TAGVAR(lt_prog_compiler_pic, $1)='+Z' - fi - ;; - aCC*) - _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' - _LT_TAGVAR(lt_prog_compiler_static, $1)='${wl}-a ${wl}archive' - case $host_cpu in - hppa*64*|ia64*) - # +Z the default - ;; - *) - _LT_TAGVAR(lt_prog_compiler_pic, $1)='+Z' - ;; - esac - ;; - *) - ;; - esac - ;; - interix*) - # This is c89, which is MS Visual C++ (no shared libs) - # Anyone wants to do a port? - ;; - irix5* | irix6* | nonstopux*) - case $cc_basename in - CC*) - _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' - _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' - # CC pic flag -KPIC is the default. - ;; - *) - ;; - esac - ;; - linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) - case $cc_basename in - KCC*) - # KAI C++ Compiler - _LT_TAGVAR(lt_prog_compiler_wl, $1)='--backend -Wl,' - _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' - ;; - ecpc* ) - # old Intel C++ for x86_64 which still supported -KPIC. - _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' - _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' - _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' - ;; - icpc* ) - # Intel C++, used to be incompatible with GCC. - # ICC 10 doesn't accept -KPIC any more. - _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' - _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' - _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' - ;; - pgCC* | pgcpp*) - # Portland Group C++ compiler - _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' - _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fpic' - _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' - ;; - cxx*) - # Compaq C++ - # Make sure the PIC flag is empty. It appears that all Alpha - # Linux and Compaq Tru64 Unix objects are PIC. - _LT_TAGVAR(lt_prog_compiler_pic, $1)= - _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' - ;; - xlc* | xlC* | bgxl[[cC]]* | mpixl[[cC]]*) - # IBM XL 8.0, 9.0 on PPC and BlueGene - _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' - _LT_TAGVAR(lt_prog_compiler_pic, $1)='-qpic' - _LT_TAGVAR(lt_prog_compiler_static, $1)='-qstaticlink' - ;; - *) - case `$CC -V 2>&1 | sed 5q` in - *Sun\ C*) - # Sun C++ 5.9 - _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' - _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' - _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld ' - ;; - esac - ;; - esac - ;; - lynxos*) - ;; - m88k*) - ;; - mvs*) - case $cc_basename in - cxx*) - _LT_TAGVAR(lt_prog_compiler_pic, $1)='-W c,exportall' - ;; - *) - ;; - esac - ;; - netbsd* | netbsdelf*-gnu) - ;; - *qnx* | *nto*) - # QNX uses GNU C++, but need to define -shared option too, otherwise - # it will coredump. - _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC -shared' - ;; - osf3* | osf4* | osf5*) - case $cc_basename in - KCC*) - _LT_TAGVAR(lt_prog_compiler_wl, $1)='--backend -Wl,' - ;; - RCC*) - # Rational C++ 2.4.1 - _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic' - ;; - cxx*) - # Digital/Compaq C++ - _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' - # Make sure the PIC flag is empty. It appears that all Alpha - # Linux and Compaq Tru64 Unix objects are PIC. - _LT_TAGVAR(lt_prog_compiler_pic, $1)= - _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' - ;; - *) - ;; - esac - ;; - psos*) - ;; - solaris*) - case $cc_basename in - CC* | sunCC*) - # Sun C++ 4.2, 5.x and Centerline C++ - _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' - _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' - _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld ' - ;; - gcx*) - # Green Hills C++ Compiler - _LT_TAGVAR(lt_prog_compiler_pic, $1)='-PIC' - ;; - *) - ;; - esac - ;; - sunos4*) - case $cc_basename in - CC*) - # Sun C++ 4.x - _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic' - _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' - ;; - lcc*) - # Lucid - _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic' - ;; - *) - ;; - esac - ;; - sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*) - case $cc_basename in - CC*) - _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' - _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' - _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' - ;; - esac - ;; - tandem*) - case $cc_basename in - NCC*) - # NonStop-UX NCC 3.20 - _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' - ;; - *) - ;; - esac - ;; - vxworks*) - ;; - *) - _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no - ;; - esac - fi -], -[ - if test "$GCC" = yes; then - _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' - _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' - - case $host_os in - aix*) - # All AIX code is PIC. - if test "$host_cpu" = ia64; then - # AIX 5 now supports IA64 processor - _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' - fi - ;; - - amigaos*) - case $host_cpu in - powerpc) - # see comment about AmigaOS4 .so support - _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' - ;; - m68k) - # FIXME: we need at least 68020 code to build shared libraries, but - # adding the `-m68020' flag to GCC prevents building anything better, - # like `-m68040'. - _LT_TAGVAR(lt_prog_compiler_pic, $1)='-m68020 -resident32 -malways-restore-a4' - ;; - esac - ;; - - beos* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*) - # PIC is the default for these OSes. - ;; - - mingw* | cygwin* | pw32* | os2* | cegcc*) - # This hack is so that the source file can tell whether it is being - # built for inclusion in a dll (and should export symbols for example). - # Although the cygwin gcc ignores -fPIC, still need this for old-style - # (--disable-auto-import) libraries - m4_if([$1], [GCJ], [], - [_LT_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT']) - ;; - - darwin* | rhapsody*) - # PIC is the default on this platform - # Common symbols not allowed in MH_DYLIB files - _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fno-common' - ;; - - haiku*) - # PIC is the default for Haiku. - # The "-static" flag exists, but is broken. - _LT_TAGVAR(lt_prog_compiler_static, $1)= - ;; - - hpux*) - # PIC is the default for 64-bit PA HP-UX, but not for 32-bit - # PA HP-UX. On IA64 HP-UX, PIC is the default but the pic flag - # sets the default TLS model and affects inlining. - case $host_cpu in - hppa*64*) - # +Z the default - ;; - *) - _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' - ;; - esac - ;; - - interix[[3-9]]*) - # Interix 3.x gcc -fpic/-fPIC options generate broken code. - # Instead, we relocate shared libraries at runtime. - ;; - - msdosdjgpp*) - # Just because we use GCC doesn't mean we suddenly get shared libraries - # on systems that don't support them. - _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no - enable_shared=no - ;; - - *nto* | *qnx*) - # QNX uses GNU C++, but need to define -shared option too, otherwise - # it will coredump. - _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC -shared' - ;; - - sysv4*MP*) - if test -d /usr/nec; then - _LT_TAGVAR(lt_prog_compiler_pic, $1)=-Kconform_pic - fi - ;; - - *) - _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' - ;; - esac - - case $cc_basename in - nvcc*) # Cuda Compiler Driver 2.2 - _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Xlinker ' - if test -n "$_LT_TAGVAR(lt_prog_compiler_pic, $1)"; then - _LT_TAGVAR(lt_prog_compiler_pic, $1)="-Xcompiler $_LT_TAGVAR(lt_prog_compiler_pic, $1)" - fi - ;; - esac - else - # PORTME Check for flag to pass linker flags through the system compiler. - case $host_os in - aix*) - _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' - if test "$host_cpu" = ia64; then - # AIX 5 now supports IA64 processor - _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' - else - _LT_TAGVAR(lt_prog_compiler_static, $1)='-bnso -bI:/lib/syscalls.exp' - fi - ;; - - mingw* | cygwin* | pw32* | os2* | cegcc*) - # This hack is so that the source file can tell whether it is being - # built for inclusion in a dll (and should export symbols for example). - m4_if([$1], [GCJ], [], - [_LT_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT']) - ;; - - hpux9* | hpux10* | hpux11*) - _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' - # PIC is the default for IA64 HP-UX and 64-bit HP-UX, but - # not for PA HP-UX. - case $host_cpu in - hppa*64*|ia64*) - # +Z the default - ;; - *) - _LT_TAGVAR(lt_prog_compiler_pic, $1)='+Z' - ;; - esac - # Is there a better lt_prog_compiler_static that works with the bundled CC? - _LT_TAGVAR(lt_prog_compiler_static, $1)='${wl}-a ${wl}archive' - ;; - - irix5* | irix6* | nonstopux*) - _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' - # PIC (with -KPIC) is the default. - _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' - ;; - - linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) - case $cc_basename in - # old Intel for x86_64 which still supported -KPIC. - ecc*) - _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' - _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' - _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' - ;; - # icc used to be incompatible with GCC. - # ICC 10 doesn't accept -KPIC any more. - icc* | ifort*) - _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' - _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' - _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' - ;; - # Lahey Fortran 8.1. - lf95*) - _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' - _LT_TAGVAR(lt_prog_compiler_pic, $1)='--shared' - _LT_TAGVAR(lt_prog_compiler_static, $1)='--static' - ;; - nagfor*) - # NAG Fortran compiler - _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,-Wl,,' - _LT_TAGVAR(lt_prog_compiler_pic, $1)='-PIC' - _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' - ;; - pgcc* | pgf77* | pgf90* | pgf95* | pgfortran*) - # Portland Group compilers (*not* the Pentium gcc compiler, - # which looks to be a dead project) - _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' - _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fpic' - _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' - ;; - ccc*) - _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' - # All Alpha code is PIC. - _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' - ;; - xl* | bgxl* | bgf* | mpixl*) - # IBM XL C 8.0/Fortran 10.1, 11.1 on PPC and BlueGene - _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' - _LT_TAGVAR(lt_prog_compiler_pic, $1)='-qpic' - _LT_TAGVAR(lt_prog_compiler_static, $1)='-qstaticlink' - ;; - *) - case `$CC -V 2>&1 | sed 5q` in - *Sun\ Ceres\ Fortran* | *Sun*Fortran*\ [[1-7]].* | *Sun*Fortran*\ 8.[[0-3]]*) - # Sun Fortran 8.3 passes all unrecognized flags to the linker - _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' - _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' - _LT_TAGVAR(lt_prog_compiler_wl, $1)='' - ;; - *Sun\ F* | *Sun*Fortran*) - _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' - _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' - _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld ' - ;; - *Sun\ C*) - # Sun C 5.9 - _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' - _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' - _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' - ;; - *Intel*\ [[CF]]*Compiler*) - _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' - _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' - _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' - ;; - *Portland\ Group*) - _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' - _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fpic' - _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' - ;; - esac - ;; - esac - ;; - - newsos6) - _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' - _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' - ;; - - *nto* | *qnx*) - # QNX uses GNU C++, but need to define -shared option too, otherwise - # it will coredump. - _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC -shared' - ;; - - osf3* | osf4* | osf5*) - _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' - # All OSF/1 code is PIC. - _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' - ;; - - rdos*) - _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' - ;; - - solaris*) - _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' - _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' - case $cc_basename in - f77* | f90* | f95* | sunf77* | sunf90* | sunf95*) - _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld ';; - *) - _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,';; - esac - ;; - - sunos4*) - _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld ' - _LT_TAGVAR(lt_prog_compiler_pic, $1)='-PIC' - _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' - ;; - - sysv4 | sysv4.2uw2* | sysv4.3*) - _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' - _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' - _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' - ;; - - sysv4*MP*) - if test -d /usr/nec ;then - _LT_TAGVAR(lt_prog_compiler_pic, $1)='-Kconform_pic' - _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' - fi - ;; - - sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*) - _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' - _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' - _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' - ;; - - unicos*) - _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' - _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no - ;; - - uts4*) - _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic' - _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' - ;; - - *) - _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no - ;; - esac - fi -]) -case $host_os in - # For platforms which do not support PIC, -DPIC is meaningless: - *djgpp*) - _LT_TAGVAR(lt_prog_compiler_pic, $1)= - ;; - *) - _LT_TAGVAR(lt_prog_compiler_pic, $1)="$_LT_TAGVAR(lt_prog_compiler_pic, $1)@&t@m4_if([$1],[],[ -DPIC],[m4_if([$1],[CXX],[ -DPIC],[])])" - ;; -esac - -AC_CACHE_CHECK([for $compiler option to produce PIC], - [_LT_TAGVAR(lt_cv_prog_compiler_pic, $1)], - [_LT_TAGVAR(lt_cv_prog_compiler_pic, $1)=$_LT_TAGVAR(lt_prog_compiler_pic, $1)]) -_LT_TAGVAR(lt_prog_compiler_pic, $1)=$_LT_TAGVAR(lt_cv_prog_compiler_pic, $1) - -# -# Check to make sure the PIC flag actually works. -# -if test -n "$_LT_TAGVAR(lt_prog_compiler_pic, $1)"; then - _LT_COMPILER_OPTION([if $compiler PIC flag $_LT_TAGVAR(lt_prog_compiler_pic, $1) works], - [_LT_TAGVAR(lt_cv_prog_compiler_pic_works, $1)], - [$_LT_TAGVAR(lt_prog_compiler_pic, $1)@&t@m4_if([$1],[],[ -DPIC],[m4_if([$1],[CXX],[ -DPIC],[])])], [], - [case $_LT_TAGVAR(lt_prog_compiler_pic, $1) in - "" | " "*) ;; - *) _LT_TAGVAR(lt_prog_compiler_pic, $1)=" $_LT_TAGVAR(lt_prog_compiler_pic, $1)" ;; - esac], - [_LT_TAGVAR(lt_prog_compiler_pic, $1)= - _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no]) -fi -_LT_TAGDECL([pic_flag], [lt_prog_compiler_pic], [1], - [Additional compiler flags for building library objects]) - -_LT_TAGDECL([wl], [lt_prog_compiler_wl], [1], - [How to pass a linker flag through the compiler]) -# -# Check to make sure the static flag actually works. -# -wl=$_LT_TAGVAR(lt_prog_compiler_wl, $1) eval lt_tmp_static_flag=\"$_LT_TAGVAR(lt_prog_compiler_static, $1)\" -_LT_LINKER_OPTION([if $compiler static flag $lt_tmp_static_flag works], - _LT_TAGVAR(lt_cv_prog_compiler_static_works, $1), - $lt_tmp_static_flag, - [], - [_LT_TAGVAR(lt_prog_compiler_static, $1)=]) -_LT_TAGDECL([link_static_flag], [lt_prog_compiler_static], [1], - [Compiler flag to prevent dynamic linking]) -])# _LT_COMPILER_PIC - - -# _LT_LINKER_SHLIBS([TAGNAME]) -# ---------------------------- -# See if the linker supports building shared libraries. -m4_defun([_LT_LINKER_SHLIBS], -[AC_REQUIRE([LT_PATH_LD])dnl -AC_REQUIRE([LT_PATH_NM])dnl -m4_require([_LT_PATH_MANIFEST_TOOL])dnl -m4_require([_LT_FILEUTILS_DEFAULTS])dnl -m4_require([_LT_DECL_EGREP])dnl -m4_require([_LT_DECL_SED])dnl -m4_require([_LT_CMD_GLOBAL_SYMBOLS])dnl -m4_require([_LT_TAG_COMPILER])dnl -AC_MSG_CHECKING([whether the $compiler linker ($LD) supports shared libraries]) -m4_if([$1], [CXX], [ - _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' - _LT_TAGVAR(exclude_expsyms, $1)=['_GLOBAL_OFFSET_TABLE_|_GLOBAL__F[ID]_.*'] - case $host_os in - aix[[4-9]]*) - # If we're using GNU nm, then we don't want the "-C" option. - # -C means demangle to AIX nm, but means don't demangle with GNU nm - # Also, AIX nm treats weak defined symbols like other global defined - # symbols, whereas GNU nm marks them as "W". - if $NM -V 2>&1 | $GREP 'GNU' > /dev/null; then - _LT_TAGVAR(export_symbols_cmds, $1)='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W")) && ([substr](\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols' - else - _LT_TAGVAR(export_symbols_cmds, $1)='$NM -BCpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B")) && ([substr](\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols' - fi - ;; - pw32*) - _LT_TAGVAR(export_symbols_cmds, $1)="$ltdll_cmds" - ;; - cygwin* | mingw* | cegcc*) - case $cc_basename in - cl*) - _LT_TAGVAR(exclude_expsyms, $1)='_NULL_IMPORT_DESCRIPTOR|_IMPORT_DESCRIPTOR_.*' - ;; - *) - _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[[BCDGRS]][[ ]]/s/.*[[ ]]\([[^ ]]*\)/\1 DATA/;s/^.*[[ ]]__nm__\([[^ ]]*\)[[ ]][[^ ]]*/\1 DATA/;/^I[[ ]]/d;/^[[AITW]][[ ]]/s/.* //'\'' | sort | uniq > $export_symbols' - _LT_TAGVAR(exclude_expsyms, $1)=['[_]+GLOBAL_OFFSET_TABLE_|[_]+GLOBAL__[FID]_.*|[_]+head_[A-Za-z0-9_]+_dll|[A-Za-z0-9_]+_dll_iname'] - ;; - esac - ;; - linux* | k*bsd*-gnu | gnu*) - _LT_TAGVAR(link_all_deplibs, $1)=no - ;; - *) - _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' - ;; - esac -], [ - runpath_var= - _LT_TAGVAR(allow_undefined_flag, $1)= - _LT_TAGVAR(always_export_symbols, $1)=no - _LT_TAGVAR(archive_cmds, $1)= - _LT_TAGVAR(archive_expsym_cmds, $1)= - _LT_TAGVAR(compiler_needs_object, $1)=no - _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=no - _LT_TAGVAR(export_dynamic_flag_spec, $1)= - _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' - _LT_TAGVAR(hardcode_automatic, $1)=no - _LT_TAGVAR(hardcode_direct, $1)=no - _LT_TAGVAR(hardcode_direct_absolute, $1)=no - _LT_TAGVAR(hardcode_libdir_flag_spec, $1)= - _LT_TAGVAR(hardcode_libdir_separator, $1)= - _LT_TAGVAR(hardcode_minus_L, $1)=no - _LT_TAGVAR(hardcode_shlibpath_var, $1)=unsupported - _LT_TAGVAR(inherit_rpath, $1)=no - _LT_TAGVAR(link_all_deplibs, $1)=unknown - _LT_TAGVAR(module_cmds, $1)= - _LT_TAGVAR(module_expsym_cmds, $1)= - _LT_TAGVAR(old_archive_from_new_cmds, $1)= - _LT_TAGVAR(old_archive_from_expsyms_cmds, $1)= - _LT_TAGVAR(thread_safe_flag_spec, $1)= - _LT_TAGVAR(whole_archive_flag_spec, $1)= - # include_expsyms should be a list of space-separated symbols to be *always* - # included in the symbol list - _LT_TAGVAR(include_expsyms, $1)= - # exclude_expsyms can be an extended regexp of symbols to exclude - # it will be wrapped by ` (' and `)$', so one must not match beginning or - # end of line. Example: `a|bc|.*d.*' will exclude the symbols `a' and `bc', - # as well as any symbol that contains `d'. - _LT_TAGVAR(exclude_expsyms, $1)=['_GLOBAL_OFFSET_TABLE_|_GLOBAL__F[ID]_.*'] - # Although _GLOBAL_OFFSET_TABLE_ is a valid symbol C name, most a.out - # platforms (ab)use it in PIC code, but their linkers get confused if - # the symbol is explicitly referenced. Since portable code cannot - # rely on this symbol name, it's probably fine to never include it in - # preloaded symbol tables. - # Exclude shared library initialization/finalization symbols. -dnl Note also adjust exclude_expsyms for C++ above. - extract_expsyms_cmds= - - case $host_os in - cygwin* | mingw* | pw32* | cegcc*) - # FIXME: the MSVC++ port hasn't been tested in a loooong time - # When not using gcc, we currently assume that we are using - # Microsoft Visual C++. - if test "$GCC" != yes; then - with_gnu_ld=no - fi - ;; - interix*) - # we just hope/assume this is gcc and not c89 (= MSVC++) - with_gnu_ld=yes - ;; - openbsd*) - with_gnu_ld=no - ;; - linux* | k*bsd*-gnu | gnu*) - _LT_TAGVAR(link_all_deplibs, $1)=no - ;; - esac - - _LT_TAGVAR(ld_shlibs, $1)=yes - - # On some targets, GNU ld is compatible enough with the native linker - # that we're better off using the native interface for both. - lt_use_gnu_ld_interface=no - if test "$with_gnu_ld" = yes; then - case $host_os in - aix*) - # The AIX port of GNU ld has always aspired to compatibility - # with the native linker. However, as the warning in the GNU ld - # block says, versions before 2.19.5* couldn't really create working - # shared libraries, regardless of the interface used. - case `$LD -v 2>&1` in - *\ \(GNU\ Binutils\)\ 2.19.5*) ;; - *\ \(GNU\ Binutils\)\ 2.[[2-9]]*) ;; - *\ \(GNU\ Binutils\)\ [[3-9]]*) ;; - *) - lt_use_gnu_ld_interface=yes - ;; - esac - ;; - *) - lt_use_gnu_ld_interface=yes - ;; - esac - fi - - if test "$lt_use_gnu_ld_interface" = yes; then - # If archive_cmds runs LD, not CC, wlarc should be empty - wlarc='${wl}' - - # Set some defaults for GNU ld with shared library support. These - # are reset later if shared libraries are not supported. Putting them - # here allows them to be overridden if necessary. - runpath_var=LD_RUN_PATH - _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' - _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic' - # ancient GNU ld didn't support --whole-archive et. al. - if $LD --help 2>&1 | $GREP 'no-whole-archive' > /dev/null; then - _LT_TAGVAR(whole_archive_flag_spec, $1)="$wlarc"'--whole-archive$convenience '"$wlarc"'--no-whole-archive' - else - _LT_TAGVAR(whole_archive_flag_spec, $1)= - fi - supports_anon_versioning=no - case `$LD -v 2>&1` in - *GNU\ gold*) supports_anon_versioning=yes ;; - *\ [[01]].* | *\ 2.[[0-9]].* | *\ 2.10.*) ;; # catch versions < 2.11 - *\ 2.11.93.0.2\ *) supports_anon_versioning=yes ;; # RH7.3 ... - *\ 2.11.92.0.12\ *) supports_anon_versioning=yes ;; # Mandrake 8.2 ... - *\ 2.11.*) ;; # other 2.11 versions - *) supports_anon_versioning=yes ;; - esac - - # See if GNU ld supports shared libraries. - case $host_os in - aix[[3-9]]*) - # On AIX/PPC, the GNU linker is very broken - if test "$host_cpu" != ia64; then - _LT_TAGVAR(ld_shlibs, $1)=no - cat <<_LT_EOF 1>&2 - -*** Warning: the GNU linker, at least up to release 2.19, is reported -*** to be unable to reliably create shared libraries on AIX. -*** Therefore, libtool is disabling shared libraries support. If you -*** really care for shared libraries, you may want to install binutils -*** 2.20 or above, or modify your PATH so that a non-GNU linker is found. -*** You will then need to restart the configuration process. - -_LT_EOF - fi - ;; - - amigaos*) - case $host_cpu in - powerpc) - # see comment about AmigaOS4 .so support - _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' - _LT_TAGVAR(archive_expsym_cmds, $1)='' - ;; - m68k) - _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/a2ixlibrary.data~$ECHO "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$ECHO "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$ECHO "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$ECHO "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)' - _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' - _LT_TAGVAR(hardcode_minus_L, $1)=yes - ;; - esac - ;; - - beos*) - if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then - _LT_TAGVAR(allow_undefined_flag, $1)=unsupported - # Joseph Beckenbach says some releases of gcc - # support --undefined. This deserves some investigation. FIXME - _LT_TAGVAR(archive_cmds, $1)='$CC -nostart $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' - else - _LT_TAGVAR(ld_shlibs, $1)=no - fi - ;; - - cygwin* | mingw* | pw32* | cegcc*) - # _LT_TAGVAR(hardcode_libdir_flag_spec, $1) is actually meaningless, - # as there is no search path for DLLs. - _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' - _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-all-symbols' - _LT_TAGVAR(allow_undefined_flag, $1)=unsupported - _LT_TAGVAR(always_export_symbols, $1)=no - _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes - _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[[BCDGRS]][[ ]]/s/.*[[ ]]\([[^ ]]*\)/\1 DATA/;s/^.*[[ ]]__nm__\([[^ ]]*\)[[ ]][[^ ]]*/\1 DATA/;/^I[[ ]]/d;/^[[AITW]][[ ]]/s/.* //'\'' | sort | uniq > $export_symbols' - _LT_TAGVAR(exclude_expsyms, $1)=['[_]+GLOBAL_OFFSET_TABLE_|[_]+GLOBAL__[FID]_.*|[_]+head_[A-Za-z0-9_]+_dll|[A-Za-z0-9_]+_dll_iname'] - - if $LD --help 2>&1 | $GREP 'auto-import' > /dev/null; then - _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' - # If the export-symbols file already is a .def file (1st line - # is EXPORTS), use it as is; otherwise, prepend... - _LT_TAGVAR(archive_expsym_cmds, $1)='if test "x`$SED 1q $export_symbols`" = xEXPORTS; then - cp $export_symbols $output_objdir/$soname.def; - else - echo EXPORTS > $output_objdir/$soname.def; - cat $export_symbols >> $output_objdir/$soname.def; - fi~ - $CC -shared $output_objdir/$soname.def $libobjs $deplibs $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' - else - _LT_TAGVAR(ld_shlibs, $1)=no - fi - ;; - - haiku*) - _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' - _LT_TAGVAR(link_all_deplibs, $1)=yes - ;; - - interix[[3-9]]*) - _LT_TAGVAR(hardcode_direct, $1)=no - _LT_TAGVAR(hardcode_shlibpath_var, $1)=no - _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir' - _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' - # Hack: On Interix 3.x, we cannot compile PIC because of a broken gcc. - # Instead, shared libraries are loaded at an image base (0x10000000 by - # default) and relocated if they conflict, which is a slow very memory - # consuming and fragmenting process. To avoid this, we pick a random, - # 256 KiB-aligned image base between 0x50000000 and 0x6FFC0000 at link - # time. Moving up from 0x10000000 also allows more sbrk(2) space. - _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' - _LT_TAGVAR(archive_expsym_cmds, $1)='sed "s,^,_," $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--retain-symbols-file,$output_objdir/$soname.expsym ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' - ;; - - gnu* | linux* | tpf* | k*bsd*-gnu | kopensolaris*-gnu) - tmp_diet=no - if test "$host_os" = linux-dietlibc; then - case $cc_basename in - diet\ *) tmp_diet=yes;; # linux-dietlibc with static linking (!diet-dyn) - esac - fi - if $LD --help 2>&1 | $EGREP ': supported targets:.* elf' > /dev/null \ - && test "$tmp_diet" = no - then - tmp_addflag=' $pic_flag' - tmp_sharedflag='-shared' - case $cc_basename,$host_cpu in - pgcc*) # Portland Group C compiler - _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` ${wl}--no-whole-archive' - tmp_addflag=' $pic_flag' - ;; - pgf77* | pgf90* | pgf95* | pgfortran*) - # Portland Group f77 and f90 compilers - _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` ${wl}--no-whole-archive' - tmp_addflag=' $pic_flag -Mnomain' ;; - ecc*,ia64* | icc*,ia64*) # Intel C compiler on ia64 - tmp_addflag=' -i_dynamic' ;; - efc*,ia64* | ifort*,ia64*) # Intel Fortran compiler on ia64 - tmp_addflag=' -i_dynamic -nofor_main' ;; - ifc* | ifort*) # Intel Fortran compiler - tmp_addflag=' -nofor_main' ;; - lf95*) # Lahey Fortran 8.1 - _LT_TAGVAR(whole_archive_flag_spec, $1)= - tmp_sharedflag='--shared' ;; - xl[[cC]]* | bgxl[[cC]]* | mpixl[[cC]]*) # IBM XL C 8.0 on PPC (deal with xlf below) - tmp_sharedflag='-qmkshrobj' - tmp_addflag= ;; - nvcc*) # Cuda Compiler Driver 2.2 - _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` ${wl}--no-whole-archive' - _LT_TAGVAR(compiler_needs_object, $1)=yes - ;; - esac - case `$CC -V 2>&1 | sed 5q` in - *Sun\ C*) # Sun C 5.9 - _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive`new_convenience=; for conv in $convenience\"\"; do test -z \"$conv\" || new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` ${wl}--no-whole-archive' - _LT_TAGVAR(compiler_needs_object, $1)=yes - tmp_sharedflag='-G' ;; - *Sun\ F*) # Sun Fortran 8.3 - tmp_sharedflag='-G' ;; - esac - _LT_TAGVAR(archive_cmds, $1)='$CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' - - if test "x$supports_anon_versioning" = xyes; then - _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $output_objdir/$libname.ver~ - cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ - echo "local: *; };" >> $output_objdir/$libname.ver~ - $CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-version-script ${wl}$output_objdir/$libname.ver -o $lib' - fi - - case $cc_basename in - xlf* | bgf* | bgxlf* | mpixlf*) - # IBM XL Fortran 10.1 on PPC cannot create shared libs itself - _LT_TAGVAR(whole_archive_flag_spec, $1)='--whole-archive$convenience --no-whole-archive' - _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' - _LT_TAGVAR(archive_cmds, $1)='$LD -shared $libobjs $deplibs $linker_flags -soname $soname -o $lib' - if test "x$supports_anon_versioning" = xyes; then - _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $output_objdir/$libname.ver~ - cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ - echo "local: *; };" >> $output_objdir/$libname.ver~ - $LD -shared $libobjs $deplibs $linker_flags -soname $soname -version-script $output_objdir/$libname.ver -o $lib' - fi - ;; - esac - else - _LT_TAGVAR(ld_shlibs, $1)=no - fi - ;; - - netbsd* | netbsdelf*-gnu) - if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then - _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable $libobjs $deplibs $linker_flags -o $lib' - wlarc= - else - _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' - _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' - fi - ;; - - solaris*) - if $LD -v 2>&1 | $GREP 'BFD 2\.8' > /dev/null; then - _LT_TAGVAR(ld_shlibs, $1)=no - cat <<_LT_EOF 1>&2 - -*** Warning: The releases 2.8.* of the GNU linker cannot reliably -*** create shared libraries on Solaris systems. Therefore, libtool -*** is disabling shared libraries support. We urge you to upgrade GNU -*** binutils to release 2.9.1 or newer. Another option is to modify -*** your PATH or compiler configuration so that the native linker is -*** used, and then restart. - -_LT_EOF - elif $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then - _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' - _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' - else - _LT_TAGVAR(ld_shlibs, $1)=no - fi - ;; - - sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX*) - case `$LD -v 2>&1` in - *\ [[01]].* | *\ 2.[[0-9]].* | *\ 2.1[[0-5]].*) - _LT_TAGVAR(ld_shlibs, $1)=no - cat <<_LT_EOF 1>&2 - -*** Warning: Releases of the GNU linker prior to 2.16.91.0.3 can not -*** reliably create shared libraries on SCO systems. Therefore, libtool -*** is disabling shared libraries support. We urge you to upgrade GNU -*** binutils to release 2.16.91.0.3 or newer. Another option is to modify -*** your PATH or compiler configuration so that the native linker is -*** used, and then restart. - -_LT_EOF - ;; - *) - # For security reasons, it is highly recommended that you always - # use absolute paths for naming shared libraries, and exclude the - # DT_RUNPATH tag from executables and libraries. But doing so - # requires that you compile everything twice, which is a pain. - if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then - _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' - _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' - _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' - else - _LT_TAGVAR(ld_shlibs, $1)=no - fi - ;; - esac - ;; - - sunos4*) - _LT_TAGVAR(archive_cmds, $1)='$LD -assert pure-text -Bshareable -o $lib $libobjs $deplibs $linker_flags' - wlarc= - _LT_TAGVAR(hardcode_direct, $1)=yes - _LT_TAGVAR(hardcode_shlibpath_var, $1)=no - ;; - - *) - if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then - _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' - _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' - else - _LT_TAGVAR(ld_shlibs, $1)=no - fi - ;; - esac - - if test "$_LT_TAGVAR(ld_shlibs, $1)" = no; then - runpath_var= - _LT_TAGVAR(hardcode_libdir_flag_spec, $1)= - _LT_TAGVAR(export_dynamic_flag_spec, $1)= - _LT_TAGVAR(whole_archive_flag_spec, $1)= - fi - else - # PORTME fill in a description of your system's linker (not GNU ld) - case $host_os in - aix3*) - _LT_TAGVAR(allow_undefined_flag, $1)=unsupported - _LT_TAGVAR(always_export_symbols, $1)=yes - _LT_TAGVAR(archive_expsym_cmds, $1)='$LD -o $output_objdir/$soname $libobjs $deplibs $linker_flags -bE:$export_symbols -T512 -H512 -bM:SRE~$AR $AR_FLAGS $lib $output_objdir/$soname' - # Note: this linker hardcodes the directories in LIBPATH if there - # are no directories specified by -L. - _LT_TAGVAR(hardcode_minus_L, $1)=yes - if test "$GCC" = yes && test -z "$lt_prog_compiler_static"; then - # Neither direct hardcoding nor static linking is supported with a - # broken collect2. - _LT_TAGVAR(hardcode_direct, $1)=unsupported - fi - ;; - - aix[[4-9]]*) - if test "$host_cpu" = ia64; then - # On IA64, the linker does run time linking by default, so we don't - # have to do anything special. - aix_use_runtimelinking=no - exp_sym_flag='-Bexport' - no_entry_flag="" - else - # If we're using GNU nm, then we don't want the "-C" option. - # -C means demangle to AIX nm, but means don't demangle with GNU nm - # Also, AIX nm treats weak defined symbols like other global - # defined symbols, whereas GNU nm marks them as "W". - if $NM -V 2>&1 | $GREP 'GNU' > /dev/null; then - _LT_TAGVAR(export_symbols_cmds, $1)='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W")) && ([substr](\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols' - else - _LT_TAGVAR(export_symbols_cmds, $1)='$NM -BCpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B")) && ([substr](\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols' - fi - aix_use_runtimelinking=no - - # Test if we are trying to use run time linking or normal - # AIX style linking. If -brtl is somewhere in LDFLAGS, we - # need to do runtime linking. - case $host_os in aix4.[[23]]|aix4.[[23]].*|aix[[5-9]]*) - for ld_flag in $LDFLAGS; do - if (test $ld_flag = "-brtl" || test $ld_flag = "-Wl,-brtl"); then - aix_use_runtimelinking=yes - break - fi - done - ;; - esac - - exp_sym_flag='-bexport' - no_entry_flag='-bnoentry' - fi - - # When large executables or shared objects are built, AIX ld can - # have problems creating the table of contents. If linking a library - # or program results in "error TOC overflow" add -mminimal-toc to - # CXXFLAGS/CFLAGS for g++/gcc. In the cases where that is not - # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS. - - _LT_TAGVAR(archive_cmds, $1)='' - _LT_TAGVAR(hardcode_direct, $1)=yes - _LT_TAGVAR(hardcode_direct_absolute, $1)=yes - _LT_TAGVAR(hardcode_libdir_separator, $1)=':' - _LT_TAGVAR(link_all_deplibs, $1)=yes - _LT_TAGVAR(file_list_spec, $1)='${wl}-f,' - - if test "$GCC" = yes; then - case $host_os in aix4.[[012]]|aix4.[[012]].*) - # We only want to do this on AIX 4.2 and lower, the check - # below for broken collect2 doesn't work under 4.3+ - collect2name=`${CC} -print-prog-name=collect2` - if test -f "$collect2name" && - strings "$collect2name" | $GREP resolve_lib_name >/dev/null - then - # We have reworked collect2 - : - else - # We have old collect2 - _LT_TAGVAR(hardcode_direct, $1)=unsupported - # It fails to find uninstalled libraries when the uninstalled - # path is not listed in the libpath. Setting hardcode_minus_L - # to unsupported forces relinking - _LT_TAGVAR(hardcode_minus_L, $1)=yes - _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' - _LT_TAGVAR(hardcode_libdir_separator, $1)= - fi - ;; - esac - shared_flag='-shared' - if test "$aix_use_runtimelinking" = yes; then - shared_flag="$shared_flag "'${wl}-G' - fi - _LT_TAGVAR(link_all_deplibs, $1)=no - else - # not using gcc - if test "$host_cpu" = ia64; then - # VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release - # chokes on -Wl,-G. The following line is correct: - shared_flag='-G' - else - if test "$aix_use_runtimelinking" = yes; then - shared_flag='${wl}-G' - else - shared_flag='${wl}-bM:SRE' - fi - fi - fi - - _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-bexpall' - # It seems that -bexpall does not export symbols beginning with - # underscore (_), so it is better to generate a list of symbols to export. - _LT_TAGVAR(always_export_symbols, $1)=yes - if test "$aix_use_runtimelinking" = yes; then - # Warning - without using the other runtime loading flags (-brtl), - # -berok will link without error, but may produce a broken library. - _LT_TAGVAR(allow_undefined_flag, $1)='-berok' - # Determine the default libpath from the value encoded in an - # empty executable. - _LT_SYS_MODULE_PATH_AIX([$1]) - _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-blibpath:$libdir:'"$aix_libpath" - _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags `if test "x${allow_undefined_flag}" != "x"; then func_echo_all "${wl}${allow_undefined_flag}"; else :; fi` '"\${wl}$exp_sym_flag:\$export_symbols $shared_flag" - else - if test "$host_cpu" = ia64; then - _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-R $libdir:/usr/lib:/lib' - _LT_TAGVAR(allow_undefined_flag, $1)="-z nodefs" - _LT_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags ${wl}${allow_undefined_flag} '"\${wl}$exp_sym_flag:\$export_symbols" - else - # Determine the default libpath from the value encoded in an - # empty executable. - _LT_SYS_MODULE_PATH_AIX([$1]) - _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-blibpath:$libdir:'"$aix_libpath" - # Warning - without using the other run time loading flags, - # -berok will link without error, but may produce a broken library. - _LT_TAGVAR(no_undefined_flag, $1)=' ${wl}-bernotok' - _LT_TAGVAR(allow_undefined_flag, $1)=' ${wl}-berok' - if test "$with_gnu_ld" = yes; then - # We only use this code for GNU lds that support --whole-archive. - _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive$convenience ${wl}--no-whole-archive' - else - # Exported symbols can be pulled into shared objects from archives - _LT_TAGVAR(whole_archive_flag_spec, $1)='$convenience' - fi - _LT_TAGVAR(archive_cmds_need_lc, $1)=yes - # This is similar to how AIX traditionally builds its shared libraries. - _LT_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs ${wl}-bnoentry $compiler_flags ${wl}-bE:$export_symbols${allow_undefined_flag}~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$soname' - fi - fi - ;; - - amigaos*) - case $host_cpu in - powerpc) - # see comment about AmigaOS4 .so support - _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' - _LT_TAGVAR(archive_expsym_cmds, $1)='' - ;; - m68k) - _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/a2ixlibrary.data~$ECHO "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$ECHO "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$ECHO "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$ECHO "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)' - _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' - _LT_TAGVAR(hardcode_minus_L, $1)=yes - ;; - esac - ;; - - bsdi[[45]]*) - _LT_TAGVAR(export_dynamic_flag_spec, $1)=-rdynamic - ;; - - cygwin* | mingw* | pw32* | cegcc*) - # When not using gcc, we currently assume that we are using - # Microsoft Visual C++. - # hardcode_libdir_flag_spec is actually meaningless, as there is - # no search path for DLLs. - case $cc_basename in - cl*) - # Native MSVC - _LT_TAGVAR(hardcode_libdir_flag_spec, $1)=' ' - _LT_TAGVAR(allow_undefined_flag, $1)=unsupported - _LT_TAGVAR(always_export_symbols, $1)=yes - _LT_TAGVAR(file_list_spec, $1)='@' - # Tell ltmain to make .lib files, not .a files. - libext=lib - # Tell ltmain to make .dll files, not .so files. - shrext_cmds=".dll" - # FIXME: Setting linknames here is a bad hack. - _LT_TAGVAR(archive_cmds, $1)='$CC -o $output_objdir/$soname $libobjs $compiler_flags $deplibs -Wl,-dll~linknames=' - _LT_TAGVAR(archive_expsym_cmds, $1)='if test "x`$SED 1q $export_symbols`" = xEXPORTS; then - sed -n -e 's/\\\\\\\(.*\\\\\\\)/-link\\\ -EXPORT:\\\\\\\1/' -e '1\\\!p' < $export_symbols > $output_objdir/$soname.exp; - else - sed -e 's/\\\\\\\(.*\\\\\\\)/-link\\\ -EXPORT:\\\\\\\1/' < $export_symbols > $output_objdir/$soname.exp; - fi~ - $CC -o $tool_output_objdir$soname $libobjs $compiler_flags $deplibs "@$tool_output_objdir$soname.exp" -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~ - linknames=' - # The linker will not automatically build a static lib if we build a DLL. - # _LT_TAGVAR(old_archive_from_new_cmds, $1)='true' - _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes - _LT_TAGVAR(exclude_expsyms, $1)='_NULL_IMPORT_DESCRIPTOR|_IMPORT_DESCRIPTOR_.*' - _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[[BCDGRS]][[ ]]/s/.*[[ ]]\([[^ ]]*\)/\1,DATA/'\'' | $SED -e '\''/^[[AITW]][[ ]]/s/.*[[ ]]//'\'' | sort | uniq > $export_symbols' - # Don't use ranlib - _LT_TAGVAR(old_postinstall_cmds, $1)='chmod 644 $oldlib' - _LT_TAGVAR(postlink_cmds, $1)='lt_outputfile="@OUTPUT@"~ - lt_tool_outputfile="@TOOL_OUTPUT@"~ - case $lt_outputfile in - *.exe|*.EXE) ;; - *) - lt_outputfile="$lt_outputfile.exe" - lt_tool_outputfile="$lt_tool_outputfile.exe" - ;; - esac~ - if test "$MANIFEST_TOOL" != ":" && test -f "$lt_outputfile.manifest"; then - $MANIFEST_TOOL -manifest "$lt_tool_outputfile.manifest" -outputresource:"$lt_tool_outputfile" || exit 1; - $RM "$lt_outputfile.manifest"; - fi' - ;; - *) - # Assume MSVC wrapper - _LT_TAGVAR(hardcode_libdir_flag_spec, $1)=' ' - _LT_TAGVAR(allow_undefined_flag, $1)=unsupported - # Tell ltmain to make .lib files, not .a files. - libext=lib - # Tell ltmain to make .dll files, not .so files. - shrext_cmds=".dll" - # FIXME: Setting linknames here is a bad hack. - _LT_TAGVAR(archive_cmds, $1)='$CC -o $lib $libobjs $compiler_flags `func_echo_all "$deplibs" | $SED '\''s/ -lc$//'\''` -link -dll~linknames=' - # The linker will automatically build a .lib file if we build a DLL. - _LT_TAGVAR(old_archive_from_new_cmds, $1)='true' - # FIXME: Should let the user specify the lib program. - _LT_TAGVAR(old_archive_cmds, $1)='lib -OUT:$oldlib$oldobjs$old_deplibs' - _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes - ;; - esac - ;; - - darwin* | rhapsody*) - _LT_DARWIN_LINKER_FEATURES($1) - ;; - - dgux*) - _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' - _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' - _LT_TAGVAR(hardcode_shlibpath_var, $1)=no - ;; - - # FreeBSD 2.2.[012] allows us to include c++rt0.o to get C++ constructor - # support. Future versions do this automatically, but an explicit c++rt0.o - # does not break anything, and helps significantly (at the cost of a little - # extra space). - freebsd2.2*) - _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags /usr/lib/c++rt0.o' - _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' - _LT_TAGVAR(hardcode_direct, $1)=yes - _LT_TAGVAR(hardcode_shlibpath_var, $1)=no - ;; - - # Unfortunately, older versions of FreeBSD 2 do not have this feature. - freebsd2.*) - _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' - _LT_TAGVAR(hardcode_direct, $1)=yes - _LT_TAGVAR(hardcode_minus_L, $1)=yes - _LT_TAGVAR(hardcode_shlibpath_var, $1)=no - ;; - - # FreeBSD 3 and greater uses gcc -shared to do shared libraries. - freebsd* | dragonfly*) - _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' - _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' - _LT_TAGVAR(hardcode_direct, $1)=yes - _LT_TAGVAR(hardcode_shlibpath_var, $1)=no - ;; - - hpux9*) - if test "$GCC" = yes; then - _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$CC -shared $pic_flag ${wl}+b ${wl}$install_libdir -o $output_objdir/$soname $libobjs $deplibs $compiler_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib' - else - _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$LD -b +b $install_libdir -o $output_objdir/$soname $libobjs $deplibs $linker_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib' - fi - _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}+b ${wl}$libdir' - _LT_TAGVAR(hardcode_libdir_separator, $1)=: - _LT_TAGVAR(hardcode_direct, $1)=yes - - # hardcode_minus_L: Not really in the search PATH, - # but as the default location of the library. - _LT_TAGVAR(hardcode_minus_L, $1)=yes - _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' - ;; - - hpux10*) - if test "$GCC" = yes && test "$with_gnu_ld" = no; then - _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags' - else - _LT_TAGVAR(archive_cmds, $1)='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags' - fi - if test "$with_gnu_ld" = no; then - _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}+b ${wl}$libdir' - _LT_TAGVAR(hardcode_libdir_separator, $1)=: - _LT_TAGVAR(hardcode_direct, $1)=yes - _LT_TAGVAR(hardcode_direct_absolute, $1)=yes - _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' - # hardcode_minus_L: Not really in the search PATH, - # but as the default location of the library. - _LT_TAGVAR(hardcode_minus_L, $1)=yes - fi - ;; - - hpux11*) - if test "$GCC" = yes && test "$with_gnu_ld" = no; then - case $host_cpu in - hppa*64*) - _LT_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}+h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags' - ;; - ia64*) - _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags' - ;; - *) - _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags' - ;; - esac - else - case $host_cpu in - hppa*64*) - _LT_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags' - ;; - ia64*) - _LT_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags' - ;; - *) - m4_if($1, [], [ - # Older versions of the 11.00 compiler do not understand -b yet - # (HP92453-01 A.11.01.20 doesn't, HP92453-01 B.11.X.35175-35176.GP does) - _LT_LINKER_OPTION([if $CC understands -b], - _LT_TAGVAR(lt_cv_prog_compiler__b, $1), [-b], - [_LT_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags'], - [_LT_TAGVAR(archive_cmds, $1)='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags'])], - [_LT_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags']) - ;; - esac - fi - if test "$with_gnu_ld" = no; then - _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}+b ${wl}$libdir' - _LT_TAGVAR(hardcode_libdir_separator, $1)=: - - case $host_cpu in - hppa*64*|ia64*) - _LT_TAGVAR(hardcode_direct, $1)=no - _LT_TAGVAR(hardcode_shlibpath_var, $1)=no - ;; - *) - _LT_TAGVAR(hardcode_direct, $1)=yes - _LT_TAGVAR(hardcode_direct_absolute, $1)=yes - _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' - - # hardcode_minus_L: Not really in the search PATH, - # but as the default location of the library. - _LT_TAGVAR(hardcode_minus_L, $1)=yes - ;; - esac - fi - ;; - - irix5* | irix6* | nonstopux*) - if test "$GCC" = yes; then - _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' - # Try to use the -exported_symbol ld option, if it does not - # work, assume that -exports_file does not work either and - # implicitly export all symbols. - # This should be the same for all languages, so no per-tag cache variable. - AC_CACHE_CHECK([whether the $host_os linker accepts -exported_symbol], - [lt_cv_irix_exported_symbol], - [save_LDFLAGS="$LDFLAGS" - LDFLAGS="$LDFLAGS -shared ${wl}-exported_symbol ${wl}foo ${wl}-update_registry ${wl}/dev/null" - AC_LINK_IFELSE( - [AC_LANG_SOURCE( - [AC_LANG_CASE([C], [[int foo (void) { return 0; }]], - [C++], [[int foo (void) { return 0; }]], - [Fortran 77], [[ - subroutine foo - end]], - [Fortran], [[ - subroutine foo - end]])])], - [lt_cv_irix_exported_symbol=yes], - [lt_cv_irix_exported_symbol=no]) - LDFLAGS="$save_LDFLAGS"]) - if test "$lt_cv_irix_exported_symbol" = yes; then - _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations ${wl}-exports_file ${wl}$export_symbols -o $lib' - fi - else - _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib' - _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -exports_file $export_symbols -o $lib' - fi - _LT_TAGVAR(archive_cmds_need_lc, $1)='no' - _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' - _LT_TAGVAR(hardcode_libdir_separator, $1)=: - _LT_TAGVAR(inherit_rpath, $1)=yes - _LT_TAGVAR(link_all_deplibs, $1)=yes - ;; - - netbsd* | netbsdelf*-gnu) - if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then - _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' # a.out - else - _LT_TAGVAR(archive_cmds, $1)='$LD -shared -o $lib $libobjs $deplibs $linker_flags' # ELF - fi - _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' - _LT_TAGVAR(hardcode_direct, $1)=yes - _LT_TAGVAR(hardcode_shlibpath_var, $1)=no - ;; - - newsos6) - _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' - _LT_TAGVAR(hardcode_direct, $1)=yes - _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' - _LT_TAGVAR(hardcode_libdir_separator, $1)=: - _LT_TAGVAR(hardcode_shlibpath_var, $1)=no - ;; - - *nto* | *qnx*) - ;; - - openbsd*) - if test -f /usr/libexec/ld.so; then - _LT_TAGVAR(hardcode_direct, $1)=yes - _LT_TAGVAR(hardcode_shlibpath_var, $1)=no - _LT_TAGVAR(hardcode_direct_absolute, $1)=yes - if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then - _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' - _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags ${wl}-retain-symbols-file,$export_symbols' - _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir' - _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' - else - case $host_os in - openbsd[[01]].* | openbsd2.[[0-7]] | openbsd2.[[0-7]].*) - _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' - _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' - ;; - *) - _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' - _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir' - ;; - esac - fi - else - _LT_TAGVAR(ld_shlibs, $1)=no - fi - ;; - - os2*) - _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' - _LT_TAGVAR(hardcode_minus_L, $1)=yes - _LT_TAGVAR(allow_undefined_flag, $1)=unsupported - _LT_TAGVAR(archive_cmds, $1)='$ECHO "LIBRARY $libname INITINSTANCE" > $output_objdir/$libname.def~$ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~echo DATA >> $output_objdir/$libname.def~echo " SINGLE NONSHARED" >> $output_objdir/$libname.def~echo EXPORTS >> $output_objdir/$libname.def~emxexp $libobjs >> $output_objdir/$libname.def~$CC -Zdll -Zcrtdll -o $lib $libobjs $deplibs $compiler_flags $output_objdir/$libname.def' - _LT_TAGVAR(old_archive_from_new_cmds, $1)='emximp -o $output_objdir/$libname.a $output_objdir/$libname.def' - ;; - - osf3*) - if test "$GCC" = yes; then - _LT_TAGVAR(allow_undefined_flag, $1)=' ${wl}-expect_unresolved ${wl}\*' - _LT_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' - else - _LT_TAGVAR(allow_undefined_flag, $1)=' -expect_unresolved \*' - _LT_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib' - fi - _LT_TAGVAR(archive_cmds_need_lc, $1)='no' - _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' - _LT_TAGVAR(hardcode_libdir_separator, $1)=: - ;; - - osf4* | osf5*) # as osf3* with the addition of -msym flag - if test "$GCC" = yes; then - _LT_TAGVAR(allow_undefined_flag, $1)=' ${wl}-expect_unresolved ${wl}\*' - _LT_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $pic_flag $libobjs $deplibs $compiler_flags ${wl}-msym ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' - _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' - else - _LT_TAGVAR(allow_undefined_flag, $1)=' -expect_unresolved \*' - _LT_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags -msym -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib' - _LT_TAGVAR(archive_expsym_cmds, $1)='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done; printf "%s\\n" "-hidden">> $lib.exp~ - $CC -shared${allow_undefined_flag} ${wl}-input ${wl}$lib.exp $compiler_flags $libobjs $deplibs -soname $soname `test -n "$verstring" && $ECHO "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib~$RM $lib.exp' - - # Both c and cxx compiler support -rpath directly - _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-rpath $libdir' - fi - _LT_TAGVAR(archive_cmds_need_lc, $1)='no' - _LT_TAGVAR(hardcode_libdir_separator, $1)=: - ;; - - solaris*) - _LT_TAGVAR(no_undefined_flag, $1)=' -z defs' - if test "$GCC" = yes; then - wlarc='${wl}' - _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag ${wl}-z ${wl}text ${wl}-h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags' - _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ - $CC -shared $pic_flag ${wl}-z ${wl}text ${wl}-M ${wl}$lib.exp ${wl}-h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp' - else - case `$CC -V 2>&1` in - *"Compilers 5.0"*) - wlarc='' - _LT_TAGVAR(archive_cmds, $1)='$LD -G${allow_undefined_flag} -h $soname -o $lib $libobjs $deplibs $linker_flags' - _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ - $LD -G${allow_undefined_flag} -M $lib.exp -h $soname -o $lib $libobjs $deplibs $linker_flags~$RM $lib.exp' - ;; - *) - wlarc='${wl}' - _LT_TAGVAR(archive_cmds, $1)='$CC -G${allow_undefined_flag} -h $soname -o $lib $libobjs $deplibs $compiler_flags' - _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ - $CC -G${allow_undefined_flag} -M $lib.exp -h $soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp' - ;; - esac - fi - _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' - _LT_TAGVAR(hardcode_shlibpath_var, $1)=no - case $host_os in - solaris2.[[0-5]] | solaris2.[[0-5]].*) ;; - *) - # The compiler driver will combine and reorder linker options, - # but understands `-z linker_flag'. GCC discards it without `$wl', - # but is careful enough not to reorder. - # Supported since Solaris 2.6 (maybe 2.5.1?) - if test "$GCC" = yes; then - _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}-z ${wl}allextract$convenience ${wl}-z ${wl}defaultextract' - else - _LT_TAGVAR(whole_archive_flag_spec, $1)='-z allextract$convenience -z defaultextract' - fi - ;; - esac - _LT_TAGVAR(link_all_deplibs, $1)=yes - ;; - - sunos4*) - if test "x$host_vendor" = xsequent; then - # Use $CC to link under sequent, because it throws in some extra .o - # files that make .init and .fini sections work. - _LT_TAGVAR(archive_cmds, $1)='$CC -G ${wl}-h $soname -o $lib $libobjs $deplibs $compiler_flags' - else - _LT_TAGVAR(archive_cmds, $1)='$LD -assert pure-text -Bstatic -o $lib $libobjs $deplibs $linker_flags' - fi - _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' - _LT_TAGVAR(hardcode_direct, $1)=yes - _LT_TAGVAR(hardcode_minus_L, $1)=yes - _LT_TAGVAR(hardcode_shlibpath_var, $1)=no - ;; - - sysv4) - case $host_vendor in - sni) - _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' - _LT_TAGVAR(hardcode_direct, $1)=yes # is this really true??? - ;; - siemens) - ## LD is ld it makes a PLAMLIB - ## CC just makes a GrossModule. - _LT_TAGVAR(archive_cmds, $1)='$LD -G -o $lib $libobjs $deplibs $linker_flags' - _LT_TAGVAR(reload_cmds, $1)='$CC -r -o $output$reload_objs' - _LT_TAGVAR(hardcode_direct, $1)=no - ;; - motorola) - _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' - _LT_TAGVAR(hardcode_direct, $1)=no #Motorola manual says yes, but my tests say they lie - ;; - esac - runpath_var='LD_RUN_PATH' - _LT_TAGVAR(hardcode_shlibpath_var, $1)=no - ;; - - sysv4.3*) - _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' - _LT_TAGVAR(hardcode_shlibpath_var, $1)=no - _LT_TAGVAR(export_dynamic_flag_spec, $1)='-Bexport' - ;; - - sysv4*MP*) - if test -d /usr/nec; then - _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' - _LT_TAGVAR(hardcode_shlibpath_var, $1)=no - runpath_var=LD_RUN_PATH - hardcode_runpath_var=yes - _LT_TAGVAR(ld_shlibs, $1)=yes - fi - ;; - - sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[[01]].[[10]]* | unixware7* | sco3.2v5.0.[[024]]*) - _LT_TAGVAR(no_undefined_flag, $1)='${wl}-z,text' - _LT_TAGVAR(archive_cmds_need_lc, $1)=no - _LT_TAGVAR(hardcode_shlibpath_var, $1)=no - runpath_var='LD_RUN_PATH' - - if test "$GCC" = yes; then - _LT_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' - _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' - else - _LT_TAGVAR(archive_cmds, $1)='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' - _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' - fi - ;; - - sysv5* | sco3.2v5* | sco5v6*) - # Note: We can NOT use -z defs as we might desire, because we do not - # link with -lc, and that would cause any symbols used from libc to - # always be unresolved, which means just about no library would - # ever link correctly. If we're not using GNU ld we use -z text - # though, which does catch some bad symbols but isn't as heavy-handed - # as -z defs. - _LT_TAGVAR(no_undefined_flag, $1)='${wl}-z,text' - _LT_TAGVAR(allow_undefined_flag, $1)='${wl}-z,nodefs' - _LT_TAGVAR(archive_cmds_need_lc, $1)=no - _LT_TAGVAR(hardcode_shlibpath_var, $1)=no - _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-R,$libdir' - _LT_TAGVAR(hardcode_libdir_separator, $1)=':' - _LT_TAGVAR(link_all_deplibs, $1)=yes - _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-Bexport' - runpath_var='LD_RUN_PATH' - - if test "$GCC" = yes; then - _LT_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' - _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' - else - _LT_TAGVAR(archive_cmds, $1)='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' - _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' - fi - ;; - - uts4*) - _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' - _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' - _LT_TAGVAR(hardcode_shlibpath_var, $1)=no - ;; - - *) - _LT_TAGVAR(ld_shlibs, $1)=no - ;; - esac - - if test x$host_vendor = xsni; then - case $host in - sysv4 | sysv4.2uw2* | sysv4.3* | sysv5*) - _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-Blargedynsym' - ;; - esac - fi - fi -]) -AC_MSG_RESULT([$_LT_TAGVAR(ld_shlibs, $1)]) -test "$_LT_TAGVAR(ld_shlibs, $1)" = no && can_build_shared=no - -_LT_TAGVAR(with_gnu_ld, $1)=$with_gnu_ld - -_LT_DECL([], [libext], [0], [Old archive suffix (normally "a")])dnl -_LT_DECL([], [shrext_cmds], [1], [Shared library suffix (normally ".so")])dnl -_LT_DECL([], [extract_expsyms_cmds], [2], - [The commands to extract the exported symbol list from a shared archive]) - -# -# Do we need to explicitly link libc? -# -case "x$_LT_TAGVAR(archive_cmds_need_lc, $1)" in -x|xyes) - # Assume -lc should be added - _LT_TAGVAR(archive_cmds_need_lc, $1)=yes - - if test "$enable_shared" = yes && test "$GCC" = yes; then - case $_LT_TAGVAR(archive_cmds, $1) in - *'~'*) - # FIXME: we may have to deal with multi-command sequences. - ;; - '$CC '*) - # Test whether the compiler implicitly links with -lc since on some - # systems, -lgcc has to come before -lc. If gcc already passes -lc - # to ld, don't add -lc before -lgcc. - AC_CACHE_CHECK([whether -lc should be explicitly linked in], - [lt_cv_]_LT_TAGVAR(archive_cmds_need_lc, $1), - [$RM conftest* - echo "$lt_simple_compile_test_code" > conftest.$ac_ext - - if AC_TRY_EVAL(ac_compile) 2>conftest.err; then - soname=conftest - lib=conftest - libobjs=conftest.$ac_objext - deplibs= - wl=$_LT_TAGVAR(lt_prog_compiler_wl, $1) - pic_flag=$_LT_TAGVAR(lt_prog_compiler_pic, $1) - compiler_flags=-v - linker_flags=-v - verstring= - output_objdir=. - libname=conftest - lt_save_allow_undefined_flag=$_LT_TAGVAR(allow_undefined_flag, $1) - _LT_TAGVAR(allow_undefined_flag, $1)= - if AC_TRY_EVAL(_LT_TAGVAR(archive_cmds, $1) 2\>\&1 \| $GREP \" -lc \" \>/dev/null 2\>\&1) - then - lt_cv_[]_LT_TAGVAR(archive_cmds_need_lc, $1)=no - else - lt_cv_[]_LT_TAGVAR(archive_cmds_need_lc, $1)=yes - fi - _LT_TAGVAR(allow_undefined_flag, $1)=$lt_save_allow_undefined_flag - else - cat conftest.err 1>&5 - fi - $RM conftest* - ]) - _LT_TAGVAR(archive_cmds_need_lc, $1)=$lt_cv_[]_LT_TAGVAR(archive_cmds_need_lc, $1) - ;; - esac - fi - ;; -esac - -_LT_TAGDECL([build_libtool_need_lc], [archive_cmds_need_lc], [0], - [Whether or not to add -lc for building shared libraries]) -_LT_TAGDECL([allow_libtool_libs_with_static_runtimes], - [enable_shared_with_static_runtimes], [0], - [Whether or not to disallow shared libs when runtime libs are static]) -_LT_TAGDECL([], [export_dynamic_flag_spec], [1], - [Compiler flag to allow reflexive dlopens]) -_LT_TAGDECL([], [whole_archive_flag_spec], [1], - [Compiler flag to generate shared objects directly from archives]) -_LT_TAGDECL([], [compiler_needs_object], [1], - [Whether the compiler copes with passing no objects directly]) -_LT_TAGDECL([], [old_archive_from_new_cmds], [2], - [Create an old-style archive from a shared archive]) -_LT_TAGDECL([], [old_archive_from_expsyms_cmds], [2], - [Create a temporary old-style archive to link instead of a shared archive]) -_LT_TAGDECL([], [archive_cmds], [2], [Commands used to build a shared archive]) -_LT_TAGDECL([], [archive_expsym_cmds], [2]) -_LT_TAGDECL([], [module_cmds], [2], - [Commands used to build a loadable module if different from building - a shared archive.]) -_LT_TAGDECL([], [module_expsym_cmds], [2]) -_LT_TAGDECL([], [with_gnu_ld], [1], - [Whether we are building with GNU ld or not]) -_LT_TAGDECL([], [allow_undefined_flag], [1], - [Flag that allows shared libraries with undefined symbols to be built]) -_LT_TAGDECL([], [no_undefined_flag], [1], - [Flag that enforces no undefined symbols]) -_LT_TAGDECL([], [hardcode_libdir_flag_spec], [1], - [Flag to hardcode $libdir into a binary during linking. - This must work even if $libdir does not exist]) -_LT_TAGDECL([], [hardcode_libdir_separator], [1], - [Whether we need a single "-rpath" flag with a separated argument]) -_LT_TAGDECL([], [hardcode_direct], [0], - [Set to "yes" if using DIR/libNAME${shared_ext} during linking hardcodes - DIR into the resulting binary]) -_LT_TAGDECL([], [hardcode_direct_absolute], [0], - [Set to "yes" if using DIR/libNAME${shared_ext} during linking hardcodes - DIR into the resulting binary and the resulting library dependency is - "absolute", i.e impossible to change by setting ${shlibpath_var} if the - library is relocated]) -_LT_TAGDECL([], [hardcode_minus_L], [0], - [Set to "yes" if using the -LDIR flag during linking hardcodes DIR - into the resulting binary]) -_LT_TAGDECL([], [hardcode_shlibpath_var], [0], - [Set to "yes" if using SHLIBPATH_VAR=DIR during linking hardcodes DIR - into the resulting binary]) -_LT_TAGDECL([], [hardcode_automatic], [0], - [Set to "yes" if building a shared library automatically hardcodes DIR - into the library and all subsequent libraries and executables linked - against it]) -_LT_TAGDECL([], [inherit_rpath], [0], - [Set to yes if linker adds runtime paths of dependent libraries - to runtime path list]) -_LT_TAGDECL([], [link_all_deplibs], [0], - [Whether libtool must link a program against all its dependency libraries]) -_LT_TAGDECL([], [always_export_symbols], [0], - [Set to "yes" if exported symbols are required]) -_LT_TAGDECL([], [export_symbols_cmds], [2], - [The commands to list exported symbols]) -_LT_TAGDECL([], [exclude_expsyms], [1], - [Symbols that should not be listed in the preloaded symbols]) -_LT_TAGDECL([], [include_expsyms], [1], - [Symbols that must always be exported]) -_LT_TAGDECL([], [prelink_cmds], [2], - [Commands necessary for linking programs (against libraries) with templates]) -_LT_TAGDECL([], [postlink_cmds], [2], - [Commands necessary for finishing linking programs]) -_LT_TAGDECL([], [file_list_spec], [1], - [Specify filename containing input files]) -dnl FIXME: Not yet implemented -dnl _LT_TAGDECL([], [thread_safe_flag_spec], [1], -dnl [Compiler flag to generate thread safe objects]) -])# _LT_LINKER_SHLIBS - - -# _LT_LANG_C_CONFIG([TAG]) -# ------------------------ -# Ensure that the configuration variables for a C compiler are suitably -# defined. These variables are subsequently used by _LT_CONFIG to write -# the compiler configuration to `libtool'. -m4_defun([_LT_LANG_C_CONFIG], -[m4_require([_LT_DECL_EGREP])dnl -lt_save_CC="$CC" -AC_LANG_PUSH(C) - -# Source file extension for C test sources. -ac_ext=c - -# Object file extension for compiled C test sources. -objext=o -_LT_TAGVAR(objext, $1)=$objext - -# Code to be used in simple compile tests -lt_simple_compile_test_code="int some_variable = 0;" - -# Code to be used in simple link tests -lt_simple_link_test_code='int main(){return(0);}' - -_LT_TAG_COMPILER -# Save the default compiler, since it gets overwritten when the other -# tags are being tested, and _LT_TAGVAR(compiler, []) is a NOP. -compiler_DEFAULT=$CC - -# save warnings/boilerplate of simple test code -_LT_COMPILER_BOILERPLATE -_LT_LINKER_BOILERPLATE - -## CAVEAT EMPTOR: -## There is no encapsulation within the following macros, do not change -## the running order or otherwise move them around unless you know exactly -## what you are doing... -if test -n "$compiler"; then - _LT_COMPILER_NO_RTTI($1) - _LT_COMPILER_PIC($1) - _LT_COMPILER_C_O($1) - _LT_COMPILER_FILE_LOCKS($1) - _LT_LINKER_SHLIBS($1) - _LT_SYS_DYNAMIC_LINKER($1) - _LT_LINKER_HARDCODE_LIBPATH($1) - LT_SYS_DLOPEN_SELF - _LT_CMD_STRIPLIB - - # Report which library types will actually be built - AC_MSG_CHECKING([if libtool supports shared libraries]) - AC_MSG_RESULT([$can_build_shared]) - - AC_MSG_CHECKING([whether to build shared libraries]) - test "$can_build_shared" = "no" && enable_shared=no - - # On AIX, shared libraries and static libraries use the same namespace, and - # are all built from PIC. - case $host_os in - aix3*) - test "$enable_shared" = yes && enable_static=no - if test -n "$RANLIB"; then - archive_cmds="$archive_cmds~\$RANLIB \$lib" - postinstall_cmds='$RANLIB $lib' - fi - ;; - - aix[[4-9]]*) - if test "$host_cpu" != ia64 && test "$aix_use_runtimelinking" = no ; then - test "$enable_shared" = yes && enable_static=no - fi - ;; - esac - AC_MSG_RESULT([$enable_shared]) - - AC_MSG_CHECKING([whether to build static libraries]) - # Make sure either enable_shared or enable_static is yes. - test "$enable_shared" = yes || enable_static=yes - AC_MSG_RESULT([$enable_static]) - - _LT_CONFIG($1) -fi -AC_LANG_POP -CC="$lt_save_CC" -])# _LT_LANG_C_CONFIG - - -# _LT_LANG_CXX_CONFIG([TAG]) -# -------------------------- -# Ensure that the configuration variables for a C++ compiler are suitably -# defined. These variables are subsequently used by _LT_CONFIG to write -# the compiler configuration to `libtool'. -m4_defun([_LT_LANG_CXX_CONFIG], -[m4_require([_LT_FILEUTILS_DEFAULTS])dnl -m4_require([_LT_DECL_EGREP])dnl -m4_require([_LT_PATH_MANIFEST_TOOL])dnl -if test -n "$CXX" && ( test "X$CXX" != "Xno" && - ( (test "X$CXX" = "Xg++" && `g++ -v >/dev/null 2>&1` ) || - (test "X$CXX" != "Xg++"))) ; then - AC_PROG_CXXCPP -else - _lt_caught_CXX_error=yes -fi - -AC_LANG_PUSH(C++) -_LT_TAGVAR(archive_cmds_need_lc, $1)=no -_LT_TAGVAR(allow_undefined_flag, $1)= -_LT_TAGVAR(always_export_symbols, $1)=no -_LT_TAGVAR(archive_expsym_cmds, $1)= -_LT_TAGVAR(compiler_needs_object, $1)=no -_LT_TAGVAR(export_dynamic_flag_spec, $1)= -_LT_TAGVAR(hardcode_direct, $1)=no -_LT_TAGVAR(hardcode_direct_absolute, $1)=no -_LT_TAGVAR(hardcode_libdir_flag_spec, $1)= -_LT_TAGVAR(hardcode_libdir_separator, $1)= -_LT_TAGVAR(hardcode_minus_L, $1)=no -_LT_TAGVAR(hardcode_shlibpath_var, $1)=unsupported -_LT_TAGVAR(hardcode_automatic, $1)=no -_LT_TAGVAR(inherit_rpath, $1)=no -_LT_TAGVAR(module_cmds, $1)= -_LT_TAGVAR(module_expsym_cmds, $1)= -_LT_TAGVAR(link_all_deplibs, $1)=unknown -_LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds -_LT_TAGVAR(reload_flag, $1)=$reload_flag -_LT_TAGVAR(reload_cmds, $1)=$reload_cmds -_LT_TAGVAR(no_undefined_flag, $1)= -_LT_TAGVAR(whole_archive_flag_spec, $1)= -_LT_TAGVAR(enable_shared_with_static_runtimes, $1)=no - -# Source file extension for C++ test sources. -ac_ext=cpp - -# Object file extension for compiled C++ test sources. -objext=o -_LT_TAGVAR(objext, $1)=$objext - -# No sense in running all these tests if we already determined that -# the CXX compiler isn't working. Some variables (like enable_shared) -# are currently assumed to apply to all compilers on this platform, -# and will be corrupted by setting them based on a non-working compiler. -if test "$_lt_caught_CXX_error" != yes; then - # Code to be used in simple compile tests - lt_simple_compile_test_code="int some_variable = 0;" - - # Code to be used in simple link tests - lt_simple_link_test_code='int main(int, char *[[]]) { return(0); }' - - # ltmain only uses $CC for tagged configurations so make sure $CC is set. - _LT_TAG_COMPILER - - # save warnings/boilerplate of simple test code - _LT_COMPILER_BOILERPLATE - _LT_LINKER_BOILERPLATE - - # Allow CC to be a program name with arguments. - lt_save_CC=$CC - lt_save_CFLAGS=$CFLAGS - lt_save_LD=$LD - lt_save_GCC=$GCC - GCC=$GXX - lt_save_with_gnu_ld=$with_gnu_ld - lt_save_path_LD=$lt_cv_path_LD - if test -n "${lt_cv_prog_gnu_ldcxx+set}"; then - lt_cv_prog_gnu_ld=$lt_cv_prog_gnu_ldcxx - else - $as_unset lt_cv_prog_gnu_ld - fi - if test -n "${lt_cv_path_LDCXX+set}"; then - lt_cv_path_LD=$lt_cv_path_LDCXX - else - $as_unset lt_cv_path_LD - fi - test -z "${LDCXX+set}" || LD=$LDCXX - CC=${CXX-"c++"} - CFLAGS=$CXXFLAGS - compiler=$CC - _LT_TAGVAR(compiler, $1)=$CC - _LT_CC_BASENAME([$compiler]) - - if test -n "$compiler"; then - # We don't want -fno-exception when compiling C++ code, so set the - # no_builtin_flag separately - if test "$GXX" = yes; then - _LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)=' -fno-builtin' - else - _LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)= - fi - - if test "$GXX" = yes; then - # Set up default GNU C++ configuration - - LT_PATH_LD - - # Check if GNU C++ uses GNU ld as the underlying linker, since the - # archiving commands below assume that GNU ld is being used. - if test "$with_gnu_ld" = yes; then - _LT_TAGVAR(archive_cmds, $1)='$CC $pic_flag -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib' - _LT_TAGVAR(archive_expsym_cmds, $1)='$CC $pic_flag -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' - - _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' - _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic' - - # If archive_cmds runs LD, not CC, wlarc should be empty - # XXX I think wlarc can be eliminated in ltcf-cxx, but I need to - # investigate it a little bit more. (MM) - wlarc='${wl}' - - # ancient GNU ld didn't support --whole-archive et. al. - if eval "`$CC -print-prog-name=ld` --help 2>&1" | - $GREP 'no-whole-archive' > /dev/null; then - _LT_TAGVAR(whole_archive_flag_spec, $1)="$wlarc"'--whole-archive$convenience '"$wlarc"'--no-whole-archive' - else - _LT_TAGVAR(whole_archive_flag_spec, $1)= - fi - else - with_gnu_ld=no - wlarc= - - # A generic and very simple default shared library creation - # command for GNU C++ for the case where it uses the native - # linker, instead of GNU ld. If possible, this setting should - # overridden to take advantage of the native linker features on - # the platform it is being used on. - _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $lib' - fi - - # Commands to make compiler produce verbose output that lists - # what "hidden" libraries, object files and flags are used when - # linking a shared library. - output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"' - - else - GXX=no - with_gnu_ld=no - wlarc= - fi - - # PORTME: fill in a description of your system's C++ link characteristics - AC_MSG_CHECKING([whether the $compiler linker ($LD) supports shared libraries]) - _LT_TAGVAR(ld_shlibs, $1)=yes - case $host_os in - aix3*) - # FIXME: insert proper C++ library support - _LT_TAGVAR(ld_shlibs, $1)=no - ;; - aix[[4-9]]*) - if test "$host_cpu" = ia64; then - # On IA64, the linker does run time linking by default, so we don't - # have to do anything special. - aix_use_runtimelinking=no - exp_sym_flag='-Bexport' - no_entry_flag="" - else - aix_use_runtimelinking=no - - # Test if we are trying to use run time linking or normal - # AIX style linking. If -brtl is somewhere in LDFLAGS, we - # need to do runtime linking. - case $host_os in aix4.[[23]]|aix4.[[23]].*|aix[[5-9]]*) - for ld_flag in $LDFLAGS; do - case $ld_flag in - *-brtl*) - aix_use_runtimelinking=yes - break - ;; - esac - done - ;; - esac - - exp_sym_flag='-bexport' - no_entry_flag='-bnoentry' - fi - - # When large executables or shared objects are built, AIX ld can - # have problems creating the table of contents. If linking a library - # or program results in "error TOC overflow" add -mminimal-toc to - # CXXFLAGS/CFLAGS for g++/gcc. In the cases where that is not - # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS. - - _LT_TAGVAR(archive_cmds, $1)='' - _LT_TAGVAR(hardcode_direct, $1)=yes - _LT_TAGVAR(hardcode_direct_absolute, $1)=yes - _LT_TAGVAR(hardcode_libdir_separator, $1)=':' - _LT_TAGVAR(link_all_deplibs, $1)=yes - _LT_TAGVAR(file_list_spec, $1)='${wl}-f,' - - if test "$GXX" = yes; then - case $host_os in aix4.[[012]]|aix4.[[012]].*) - # We only want to do this on AIX 4.2 and lower, the check - # below for broken collect2 doesn't work under 4.3+ - collect2name=`${CC} -print-prog-name=collect2` - if test -f "$collect2name" && - strings "$collect2name" | $GREP resolve_lib_name >/dev/null - then - # We have reworked collect2 - : - else - # We have old collect2 - _LT_TAGVAR(hardcode_direct, $1)=unsupported - # It fails to find uninstalled libraries when the uninstalled - # path is not listed in the libpath. Setting hardcode_minus_L - # to unsupported forces relinking - _LT_TAGVAR(hardcode_minus_L, $1)=yes - _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' - _LT_TAGVAR(hardcode_libdir_separator, $1)= - fi - esac - shared_flag='-shared' - if test "$aix_use_runtimelinking" = yes; then - shared_flag="$shared_flag "'${wl}-G' - fi - else - # not using gcc - if test "$host_cpu" = ia64; then - # VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release - # chokes on -Wl,-G. The following line is correct: - shared_flag='-G' - else - if test "$aix_use_runtimelinking" = yes; then - shared_flag='${wl}-G' - else - shared_flag='${wl}-bM:SRE' - fi - fi - fi - - _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-bexpall' - # It seems that -bexpall does not export symbols beginning with - # underscore (_), so it is better to generate a list of symbols to - # export. - _LT_TAGVAR(always_export_symbols, $1)=yes - if test "$aix_use_runtimelinking" = yes; then - # Warning - without using the other runtime loading flags (-brtl), - # -berok will link without error, but may produce a broken library. - _LT_TAGVAR(allow_undefined_flag, $1)='-berok' - # Determine the default libpath from the value encoded in an empty - # executable. - _LT_SYS_MODULE_PATH_AIX([$1]) - _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-blibpath:$libdir:'"$aix_libpath" - - _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags `if test "x${allow_undefined_flag}" != "x"; then func_echo_all "${wl}${allow_undefined_flag}"; else :; fi` '"\${wl}$exp_sym_flag:\$export_symbols $shared_flag" - else - if test "$host_cpu" = ia64; then - _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-R $libdir:/usr/lib:/lib' - _LT_TAGVAR(allow_undefined_flag, $1)="-z nodefs" - _LT_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags ${wl}${allow_undefined_flag} '"\${wl}$exp_sym_flag:\$export_symbols" - else - # Determine the default libpath from the value encoded in an - # empty executable. - _LT_SYS_MODULE_PATH_AIX([$1]) - _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-blibpath:$libdir:'"$aix_libpath" - # Warning - without using the other run time loading flags, - # -berok will link without error, but may produce a broken library. - _LT_TAGVAR(no_undefined_flag, $1)=' ${wl}-bernotok' - _LT_TAGVAR(allow_undefined_flag, $1)=' ${wl}-berok' - if test "$with_gnu_ld" = yes; then - # We only use this code for GNU lds that support --whole-archive. - _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive$convenience ${wl}--no-whole-archive' - else - # Exported symbols can be pulled into shared objects from archives - _LT_TAGVAR(whole_archive_flag_spec, $1)='$convenience' - fi - _LT_TAGVAR(archive_cmds_need_lc, $1)=yes - # This is similar to how AIX traditionally builds its shared - # libraries. - _LT_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs ${wl}-bnoentry $compiler_flags ${wl}-bE:$export_symbols${allow_undefined_flag}~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$soname' - fi - fi - ;; - - beos*) - if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then - _LT_TAGVAR(allow_undefined_flag, $1)=unsupported - # Joseph Beckenbach says some releases of gcc - # support --undefined. This deserves some investigation. FIXME - _LT_TAGVAR(archive_cmds, $1)='$CC -nostart $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' - else - _LT_TAGVAR(ld_shlibs, $1)=no - fi - ;; - - chorus*) - case $cc_basename in - *) - # FIXME: insert proper C++ library support - _LT_TAGVAR(ld_shlibs, $1)=no - ;; - esac - ;; - - cygwin* | mingw* | pw32* | cegcc*) - case $GXX,$cc_basename in - ,cl* | no,cl*) - # Native MSVC - # hardcode_libdir_flag_spec is actually meaningless, as there is - # no search path for DLLs. - _LT_TAGVAR(hardcode_libdir_flag_spec, $1)=' ' - _LT_TAGVAR(allow_undefined_flag, $1)=unsupported - _LT_TAGVAR(always_export_symbols, $1)=yes - _LT_TAGVAR(file_list_spec, $1)='@' - # Tell ltmain to make .lib files, not .a files. - libext=lib - # Tell ltmain to make .dll files, not .so files. - shrext_cmds=".dll" - # FIXME: Setting linknames here is a bad hack. - _LT_TAGVAR(archive_cmds, $1)='$CC -o $output_objdir/$soname $libobjs $compiler_flags $deplibs -Wl,-dll~linknames=' - _LT_TAGVAR(archive_expsym_cmds, $1)='if test "x`$SED 1q $export_symbols`" = xEXPORTS; then - $SED -n -e 's/\\\\\\\(.*\\\\\\\)/-link\\\ -EXPORT:\\\\\\\1/' -e '1\\\!p' < $export_symbols > $output_objdir/$soname.exp; - else - $SED -e 's/\\\\\\\(.*\\\\\\\)/-link\\\ -EXPORT:\\\\\\\1/' < $export_symbols > $output_objdir/$soname.exp; - fi~ - $CC -o $tool_output_objdir$soname $libobjs $compiler_flags $deplibs "@$tool_output_objdir$soname.exp" -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~ - linknames=' - # The linker will not automatically build a static lib if we build a DLL. - # _LT_TAGVAR(old_archive_from_new_cmds, $1)='true' - _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes - # Don't use ranlib - _LT_TAGVAR(old_postinstall_cmds, $1)='chmod 644 $oldlib' - _LT_TAGVAR(postlink_cmds, $1)='lt_outputfile="@OUTPUT@"~ - lt_tool_outputfile="@TOOL_OUTPUT@"~ - case $lt_outputfile in - *.exe|*.EXE) ;; - *) - lt_outputfile="$lt_outputfile.exe" - lt_tool_outputfile="$lt_tool_outputfile.exe" - ;; - esac~ - func_to_tool_file "$lt_outputfile"~ - if test "$MANIFEST_TOOL" != ":" && test -f "$lt_outputfile.manifest"; then - $MANIFEST_TOOL -manifest "$lt_tool_outputfile.manifest" -outputresource:"$lt_tool_outputfile" || exit 1; - $RM "$lt_outputfile.manifest"; - fi' - ;; - *) - # g++ - # _LT_TAGVAR(hardcode_libdir_flag_spec, $1) is actually meaningless, - # as there is no search path for DLLs. - _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' - _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-all-symbols' - _LT_TAGVAR(allow_undefined_flag, $1)=unsupported - _LT_TAGVAR(always_export_symbols, $1)=no - _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes - - if $LD --help 2>&1 | $GREP 'auto-import' > /dev/null; then - _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' - # If the export-symbols file already is a .def file (1st line - # is EXPORTS), use it as is; otherwise, prepend... - _LT_TAGVAR(archive_expsym_cmds, $1)='if test "x`$SED 1q $export_symbols`" = xEXPORTS; then - cp $export_symbols $output_objdir/$soname.def; - else - echo EXPORTS > $output_objdir/$soname.def; - cat $export_symbols >> $output_objdir/$soname.def; - fi~ - $CC -shared -nostdlib $output_objdir/$soname.def $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' - else - _LT_TAGVAR(ld_shlibs, $1)=no - fi - ;; - esac - ;; - darwin* | rhapsody*) - _LT_DARWIN_LINKER_FEATURES($1) - ;; - - dgux*) - case $cc_basename in - ec++*) - # FIXME: insert proper C++ library support - _LT_TAGVAR(ld_shlibs, $1)=no - ;; - ghcx*) - # Green Hills C++ Compiler - # FIXME: insert proper C++ library support - _LT_TAGVAR(ld_shlibs, $1)=no - ;; - *) - # FIXME: insert proper C++ library support - _LT_TAGVAR(ld_shlibs, $1)=no - ;; - esac - ;; - - freebsd2.*) - # C++ shared libraries reported to be fairly broken before - # switch to ELF - _LT_TAGVAR(ld_shlibs, $1)=no - ;; - - freebsd-elf*) - _LT_TAGVAR(archive_cmds_need_lc, $1)=no - ;; - - freebsd* | dragonfly*) - # FreeBSD 3 and later use GNU C++ and GNU ld with standard ELF - # conventions - _LT_TAGVAR(ld_shlibs, $1)=yes - ;; - - haiku*) - _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' - _LT_TAGVAR(link_all_deplibs, $1)=yes - ;; - - hpux9*) - _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}+b ${wl}$libdir' - _LT_TAGVAR(hardcode_libdir_separator, $1)=: - _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' - _LT_TAGVAR(hardcode_direct, $1)=yes - _LT_TAGVAR(hardcode_minus_L, $1)=yes # Not in the search PATH, - # but as the default - # location of the library. - - case $cc_basename in - CC*) - # FIXME: insert proper C++ library support - _LT_TAGVAR(ld_shlibs, $1)=no - ;; - aCC*) - _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$CC -b ${wl}+b ${wl}$install_libdir -o $output_objdir/$soname $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib' - # Commands to make compiler produce verbose output that lists - # what "hidden" libraries, object files and flags are used when - # linking a shared library. - # - # There doesn't appear to be a way to prevent this compiler from - # explicitly linking system object files so we need to strip them - # from the output so that they don't get included in the library - # dependencies. - output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | $EGREP "\-L"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"' - ;; - *) - if test "$GXX" = yes; then - _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$CC -shared -nostdlib $pic_flag ${wl}+b ${wl}$install_libdir -o $output_objdir/$soname $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib' - else - # FIXME: insert proper C++ library support - _LT_TAGVAR(ld_shlibs, $1)=no - fi - ;; - esac - ;; - - hpux10*|hpux11*) - if test $with_gnu_ld = no; then - _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}+b ${wl}$libdir' - _LT_TAGVAR(hardcode_libdir_separator, $1)=: - - case $host_cpu in - hppa*64*|ia64*) - ;; - *) - _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' - ;; - esac - fi - case $host_cpu in - hppa*64*|ia64*) - _LT_TAGVAR(hardcode_direct, $1)=no - _LT_TAGVAR(hardcode_shlibpath_var, $1)=no - ;; - *) - _LT_TAGVAR(hardcode_direct, $1)=yes - _LT_TAGVAR(hardcode_direct_absolute, $1)=yes - _LT_TAGVAR(hardcode_minus_L, $1)=yes # Not in the search PATH, - # but as the default - # location of the library. - ;; - esac - - case $cc_basename in - CC*) - # FIXME: insert proper C++ library support - _LT_TAGVAR(ld_shlibs, $1)=no - ;; - aCC*) - case $host_cpu in - hppa*64*) - _LT_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' - ;; - ia64*) - _LT_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' - ;; - *) - _LT_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' - ;; - esac - # Commands to make compiler produce verbose output that lists - # what "hidden" libraries, object files and flags are used when - # linking a shared library. - # - # There doesn't appear to be a way to prevent this compiler from - # explicitly linking system object files so we need to strip them - # from the output so that they don't get included in the library - # dependencies. - output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | $GREP "\-L"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"' - ;; - *) - if test "$GXX" = yes; then - if test $with_gnu_ld = no; then - case $host_cpu in - hppa*64*) - _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib -fPIC ${wl}+h ${wl}$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' - ;; - ia64*) - _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $pic_flag ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' - ;; - *) - _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $pic_flag ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' - ;; - esac - fi - else - # FIXME: insert proper C++ library support - _LT_TAGVAR(ld_shlibs, $1)=no - fi - ;; - esac - ;; - - interix[[3-9]]*) - _LT_TAGVAR(hardcode_direct, $1)=no - _LT_TAGVAR(hardcode_shlibpath_var, $1)=no - _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir' - _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' - # Hack: On Interix 3.x, we cannot compile PIC because of a broken gcc. - # Instead, shared libraries are loaded at an image base (0x10000000 by - # default) and relocated if they conflict, which is a slow very memory - # consuming and fragmenting process. To avoid this, we pick a random, - # 256 KiB-aligned image base between 0x50000000 and 0x6FFC0000 at link - # time. Moving up from 0x10000000 also allows more sbrk(2) space. - _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' - _LT_TAGVAR(archive_expsym_cmds, $1)='sed "s,^,_," $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--retain-symbols-file,$output_objdir/$soname.expsym ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' - ;; - irix5* | irix6*) - case $cc_basename in - CC*) - # SGI C++ - _LT_TAGVAR(archive_cmds, $1)='$CC -shared -all -multigot $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib' - - # Archives containing C++ object files must be created using - # "CC -ar", where "CC" is the IRIX C++ compiler. This is - # necessary to make sure instantiated templates are included - # in the archive. - _LT_TAGVAR(old_archive_cmds, $1)='$CC -ar -WR,-u -o $oldlib $oldobjs' - ;; - *) - if test "$GXX" = yes; then - if test "$with_gnu_ld" = no; then - _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' - else - _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` -o $lib' - fi - fi - _LT_TAGVAR(link_all_deplibs, $1)=yes - ;; - esac - _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' - _LT_TAGVAR(hardcode_libdir_separator, $1)=: - _LT_TAGVAR(inherit_rpath, $1)=yes - ;; - - linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) - case $cc_basename in - KCC*) - # Kuck and Associates, Inc. (KAI) C++ Compiler - - # KCC will only create a shared library if the output file - # ends with ".so" (or ".sl" for HP-UX), so rename the library - # to its proper name (with version) after linking. - _LT_TAGVAR(archive_cmds, $1)='tempext=`echo $shared_ext | $SED -e '\''s/\([[^()0-9A-Za-z{}]]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\${tempext}\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib; mv \$templib $lib' - _LT_TAGVAR(archive_expsym_cmds, $1)='tempext=`echo $shared_ext | $SED -e '\''s/\([[^()0-9A-Za-z{}]]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\${tempext}\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib ${wl}-retain-symbols-file,$export_symbols; mv \$templib $lib' - # Commands to make compiler produce verbose output that lists - # what "hidden" libraries, object files and flags are used when - # linking a shared library. - # - # There doesn't appear to be a way to prevent this compiler from - # explicitly linking system object files so we need to strip them - # from the output so that they don't get included in the library - # dependencies. - output_verbose_link_cmd='templist=`$CC $CFLAGS -v conftest.$objext -o libconftest$shared_ext 2>&1 | $GREP "ld"`; rm -f libconftest$shared_ext; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"' - - _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir' - _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic' - - # Archives containing C++ object files must be created using - # "CC -Bstatic", where "CC" is the KAI C++ compiler. - _LT_TAGVAR(old_archive_cmds, $1)='$CC -Bstatic -o $oldlib $oldobjs' - ;; - icpc* | ecpc* ) - # Intel C++ - with_gnu_ld=yes - # version 8.0 and above of icpc choke on multiply defined symbols - # if we add $predep_objects and $postdep_objects, however 7.1 and - # earlier do not add the objects themselves. - case `$CC -V 2>&1` in - *"Version 7."*) - _LT_TAGVAR(archive_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib' - _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' - ;; - *) # Version 8.0 or newer - tmp_idyn= - case $host_cpu in - ia64*) tmp_idyn=' -i_dynamic';; - esac - _LT_TAGVAR(archive_cmds, $1)='$CC -shared'"$tmp_idyn"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' - _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared'"$tmp_idyn"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' - ;; - esac - _LT_TAGVAR(archive_cmds_need_lc, $1)=no - _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir' - _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic' - _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive$convenience ${wl}--no-whole-archive' - ;; - pgCC* | pgcpp*) - # Portland Group C++ compiler - case `$CC -V` in - *pgCC\ [[1-5]].* | *pgcpp\ [[1-5]].*) - _LT_TAGVAR(prelink_cmds, $1)='tpldir=Template.dir~ - rm -rf $tpldir~ - $CC --prelink_objects --instantiation_dir $tpldir $objs $libobjs $compile_deplibs~ - compile_command="$compile_command `find $tpldir -name \*.o | sort | $NL2SP`"' - _LT_TAGVAR(old_archive_cmds, $1)='tpldir=Template.dir~ - rm -rf $tpldir~ - $CC --prelink_objects --instantiation_dir $tpldir $oldobjs$old_deplibs~ - $AR $AR_FLAGS $oldlib$oldobjs$old_deplibs `find $tpldir -name \*.o | sort | $NL2SP`~ - $RANLIB $oldlib' - _LT_TAGVAR(archive_cmds, $1)='tpldir=Template.dir~ - rm -rf $tpldir~ - $CC --prelink_objects --instantiation_dir $tpldir $predep_objects $libobjs $deplibs $convenience $postdep_objects~ - $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | sort | $NL2SP` $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname -o $lib' - _LT_TAGVAR(archive_expsym_cmds, $1)='tpldir=Template.dir~ - rm -rf $tpldir~ - $CC --prelink_objects --instantiation_dir $tpldir $predep_objects $libobjs $deplibs $convenience $postdep_objects~ - $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | sort | $NL2SP` $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname ${wl}-retain-symbols-file ${wl}$export_symbols -o $lib' - ;; - *) # Version 6 and above use weak symbols - _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname -o $lib' - _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname ${wl}-retain-symbols-file ${wl}$export_symbols -o $lib' - ;; - esac - - _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}--rpath ${wl}$libdir' - _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic' - _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` ${wl}--no-whole-archive' - ;; - cxx*) - # Compaq C++ - _LT_TAGVAR(archive_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib' - _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib ${wl}-retain-symbols-file $wl$export_symbols' - - runpath_var=LD_RUN_PATH - _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-rpath $libdir' - _LT_TAGVAR(hardcode_libdir_separator, $1)=: - - # Commands to make compiler produce verbose output that lists - # what "hidden" libraries, object files and flags are used when - # linking a shared library. - # - # There doesn't appear to be a way to prevent this compiler from - # explicitly linking system object files so we need to strip them - # from the output so that they don't get included in the library - # dependencies. - output_verbose_link_cmd='templist=`$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP "ld"`; templist=`func_echo_all "$templist" | $SED "s/\(^.*ld.*\)\( .*ld .*$\)/\1/"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "X$list" | $Xsed' - ;; - xl* | mpixl* | bgxl*) - # IBM XL 8.0 on PPC, with GNU ld - _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' - _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic' - _LT_TAGVAR(archive_cmds, $1)='$CC -qmkshrobj $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' - if test "x$supports_anon_versioning" = xyes; then - _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $output_objdir/$libname.ver~ - cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ - echo "local: *; };" >> $output_objdir/$libname.ver~ - $CC -qmkshrobj $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-version-script ${wl}$output_objdir/$libname.ver -o $lib' - fi - ;; - *) - case `$CC -V 2>&1 | sed 5q` in - *Sun\ C*) - # Sun C++ 5.9 - _LT_TAGVAR(no_undefined_flag, $1)=' -zdefs' - _LT_TAGVAR(archive_cmds, $1)='$CC -G${allow_undefined_flag} -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' - _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G${allow_undefined_flag} -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-retain-symbols-file ${wl}$export_symbols' - _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' - _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive`new_convenience=; for conv in $convenience\"\"; do test -z \"$conv\" || new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` ${wl}--no-whole-archive' - _LT_TAGVAR(compiler_needs_object, $1)=yes - - # Not sure whether something based on - # $CC $CFLAGS -v conftest.$objext -o libconftest$shared_ext 2>&1 - # would be better. - output_verbose_link_cmd='func_echo_all' - - # Archives containing C++ object files must be created using - # "CC -xar", where "CC" is the Sun C++ compiler. This is - # necessary to make sure instantiated templates are included - # in the archive. - _LT_TAGVAR(old_archive_cmds, $1)='$CC -xar -o $oldlib $oldobjs' - ;; - esac - ;; - esac - ;; - - lynxos*) - # FIXME: insert proper C++ library support - _LT_TAGVAR(ld_shlibs, $1)=no - ;; - - m88k*) - # FIXME: insert proper C++ library support - _LT_TAGVAR(ld_shlibs, $1)=no - ;; - - mvs*) - case $cc_basename in - cxx*) - # FIXME: insert proper C++ library support - _LT_TAGVAR(ld_shlibs, $1)=no - ;; - *) - # FIXME: insert proper C++ library support - _LT_TAGVAR(ld_shlibs, $1)=no - ;; - esac - ;; - - netbsd*) - if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then - _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $predep_objects $libobjs $deplibs $postdep_objects $linker_flags' - wlarc= - _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' - _LT_TAGVAR(hardcode_direct, $1)=yes - _LT_TAGVAR(hardcode_shlibpath_var, $1)=no - fi - # Workaround some broken pre-1.5 toolchains - output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP conftest.$objext | $SED -e "s:-lgcc -lc -lgcc::"' - ;; - - *nto* | *qnx*) - _LT_TAGVAR(ld_shlibs, $1)=yes - ;; - - openbsd2*) - # C++ shared libraries are fairly broken - _LT_TAGVAR(ld_shlibs, $1)=no - ;; - - openbsd*) - if test -f /usr/libexec/ld.so; then - _LT_TAGVAR(hardcode_direct, $1)=yes - _LT_TAGVAR(hardcode_shlibpath_var, $1)=no - _LT_TAGVAR(hardcode_direct_absolute, $1)=yes - _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $lib' - _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir' - if test -z "`echo __ELF__ | $CC -E - | grep __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then - _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-retain-symbols-file,$export_symbols -o $lib' - _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' - _LT_TAGVAR(whole_archive_flag_spec, $1)="$wlarc"'--whole-archive$convenience '"$wlarc"'--no-whole-archive' - fi - output_verbose_link_cmd=func_echo_all - else - _LT_TAGVAR(ld_shlibs, $1)=no - fi - ;; - - osf3* | osf4* | osf5*) - case $cc_basename in - KCC*) - # Kuck and Associates, Inc. (KAI) C++ Compiler - - # KCC will only create a shared library if the output file - # ends with ".so" (or ".sl" for HP-UX), so rename the library - # to its proper name (with version) after linking. - _LT_TAGVAR(archive_cmds, $1)='tempext=`echo $shared_ext | $SED -e '\''s/\([[^()0-9A-Za-z{}]]\)/\\\\\1/g'\''`; templib=`echo "$lib" | $SED -e "s/\${tempext}\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib; mv \$templib $lib' - - _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir' - _LT_TAGVAR(hardcode_libdir_separator, $1)=: - - # Archives containing C++ object files must be created using - # the KAI C++ compiler. - case $host in - osf3*) _LT_TAGVAR(old_archive_cmds, $1)='$CC -Bstatic -o $oldlib $oldobjs' ;; - *) _LT_TAGVAR(old_archive_cmds, $1)='$CC -o $oldlib $oldobjs' ;; - esac - ;; - RCC*) - # Rational C++ 2.4.1 - # FIXME: insert proper C++ library support - _LT_TAGVAR(ld_shlibs, $1)=no - ;; - cxx*) - case $host in - osf3*) - _LT_TAGVAR(allow_undefined_flag, $1)=' ${wl}-expect_unresolved ${wl}\*' - _LT_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $soname `test -n "$verstring" && func_echo_all "${wl}-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib' - _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' - ;; - *) - _LT_TAGVAR(allow_undefined_flag, $1)=' -expect_unresolved \*' - _LT_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -msym -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib' - _LT_TAGVAR(archive_expsym_cmds, $1)='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done~ - echo "-hidden">> $lib.exp~ - $CC -shared$allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -msym -soname $soname ${wl}-input ${wl}$lib.exp `test -n "$verstring" && $ECHO "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib~ - $RM $lib.exp' - _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-rpath $libdir' - ;; - esac - - _LT_TAGVAR(hardcode_libdir_separator, $1)=: - - # Commands to make compiler produce verbose output that lists - # what "hidden" libraries, object files and flags are used when - # linking a shared library. - # - # There doesn't appear to be a way to prevent this compiler from - # explicitly linking system object files so we need to strip them - # from the output so that they don't get included in the library - # dependencies. - output_verbose_link_cmd='templist=`$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP "ld" | $GREP -v "ld:"`; templist=`func_echo_all "$templist" | $SED "s/\(^.*ld.*\)\( .*ld.*$\)/\1/"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"' - ;; - *) - if test "$GXX" = yes && test "$with_gnu_ld" = no; then - _LT_TAGVAR(allow_undefined_flag, $1)=' ${wl}-expect_unresolved ${wl}\*' - case $host in - osf3*) - _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib ${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' - ;; - *) - _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -nostdlib ${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-msym ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' - ;; - esac - - _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' - _LT_TAGVAR(hardcode_libdir_separator, $1)=: - - # Commands to make compiler produce verbose output that lists - # what "hidden" libraries, object files and flags are used when - # linking a shared library. - output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"' - - else - # FIXME: insert proper C++ library support - _LT_TAGVAR(ld_shlibs, $1)=no - fi - ;; - esac - ;; - - psos*) - # FIXME: insert proper C++ library support - _LT_TAGVAR(ld_shlibs, $1)=no - ;; - - sunos4*) - case $cc_basename in - CC*) - # Sun C++ 4.x - # FIXME: insert proper C++ library support - _LT_TAGVAR(ld_shlibs, $1)=no - ;; - lcc*) - # Lucid - # FIXME: insert proper C++ library support - _LT_TAGVAR(ld_shlibs, $1)=no - ;; - *) - # FIXME: insert proper C++ library support - _LT_TAGVAR(ld_shlibs, $1)=no - ;; - esac - ;; - - solaris*) - case $cc_basename in - CC* | sunCC*) - # Sun C++ 4.2, 5.x and Centerline C++ - _LT_TAGVAR(archive_cmds_need_lc,$1)=yes - _LT_TAGVAR(no_undefined_flag, $1)=' -zdefs' - _LT_TAGVAR(archive_cmds, $1)='$CC -G${allow_undefined_flag} -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' - _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ - $CC -G${allow_undefined_flag} ${wl}-M ${wl}$lib.exp -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp' - - _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' - _LT_TAGVAR(hardcode_shlibpath_var, $1)=no - case $host_os in - solaris2.[[0-5]] | solaris2.[[0-5]].*) ;; - *) - # The compiler driver will combine and reorder linker options, - # but understands `-z linker_flag'. - # Supported since Solaris 2.6 (maybe 2.5.1?) - _LT_TAGVAR(whole_archive_flag_spec, $1)='-z allextract$convenience -z defaultextract' - ;; - esac - _LT_TAGVAR(link_all_deplibs, $1)=yes - - output_verbose_link_cmd='func_echo_all' - - # Archives containing C++ object files must be created using - # "CC -xar", where "CC" is the Sun C++ compiler. This is - # necessary to make sure instantiated templates are included - # in the archive. - _LT_TAGVAR(old_archive_cmds, $1)='$CC -xar -o $oldlib $oldobjs' - ;; - gcx*) - # Green Hills C++ Compiler - _LT_TAGVAR(archive_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-h $wl$soname -o $lib' - - # The C++ compiler must be used to create the archive. - _LT_TAGVAR(old_archive_cmds, $1)='$CC $LDFLAGS -archive -o $oldlib $oldobjs' - ;; - *) - # GNU C++ compiler with Solaris linker - if test "$GXX" = yes && test "$with_gnu_ld" = no; then - _LT_TAGVAR(no_undefined_flag, $1)=' ${wl}-z ${wl}defs' - if $CC --version | $GREP -v '^2\.7' > /dev/null; then - _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -nostdlib $LDFLAGS $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-h $wl$soname -o $lib' - _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ - $CC -shared $pic_flag -nostdlib ${wl}-M $wl$lib.exp -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp' - - # Commands to make compiler produce verbose output that lists - # what "hidden" libraries, object files and flags are used when - # linking a shared library. - output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"' - else - # g++ 2.7 appears to require `-G' NOT `-shared' on this - # platform. - _LT_TAGVAR(archive_cmds, $1)='$CC -G -nostdlib $LDFLAGS $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-h $wl$soname -o $lib' - _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ - $CC -G -nostdlib ${wl}-M $wl$lib.exp -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp' - - # Commands to make compiler produce verbose output that lists - # what "hidden" libraries, object files and flags are used when - # linking a shared library. - output_verbose_link_cmd='$CC -G $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"' - fi - - _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-R $wl$libdir' - case $host_os in - solaris2.[[0-5]] | solaris2.[[0-5]].*) ;; - *) - _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}-z ${wl}allextract$convenience ${wl}-z ${wl}defaultextract' - ;; - esac - fi - ;; - esac - ;; - - sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[[01]].[[10]]* | unixware7* | sco3.2v5.0.[[024]]*) - _LT_TAGVAR(no_undefined_flag, $1)='${wl}-z,text' - _LT_TAGVAR(archive_cmds_need_lc, $1)=no - _LT_TAGVAR(hardcode_shlibpath_var, $1)=no - runpath_var='LD_RUN_PATH' - - case $cc_basename in - CC*) - _LT_TAGVAR(archive_cmds, $1)='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' - _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' - ;; - *) - _LT_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' - _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' - ;; - esac - ;; - - sysv5* | sco3.2v5* | sco5v6*) - # Note: We can NOT use -z defs as we might desire, because we do not - # link with -lc, and that would cause any symbols used from libc to - # always be unresolved, which means just about no library would - # ever link correctly. If we're not using GNU ld we use -z text - # though, which does catch some bad symbols but isn't as heavy-handed - # as -z defs. - _LT_TAGVAR(no_undefined_flag, $1)='${wl}-z,text' - _LT_TAGVAR(allow_undefined_flag, $1)='${wl}-z,nodefs' - _LT_TAGVAR(archive_cmds_need_lc, $1)=no - _LT_TAGVAR(hardcode_shlibpath_var, $1)=no - _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-R,$libdir' - _LT_TAGVAR(hardcode_libdir_separator, $1)=':' - _LT_TAGVAR(link_all_deplibs, $1)=yes - _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-Bexport' - runpath_var='LD_RUN_PATH' - - case $cc_basename in - CC*) - _LT_TAGVAR(archive_cmds, $1)='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' - _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' - _LT_TAGVAR(old_archive_cmds, $1)='$CC -Tprelink_objects $oldobjs~ - '"$_LT_TAGVAR(old_archive_cmds, $1)" - _LT_TAGVAR(reload_cmds, $1)='$CC -Tprelink_objects $reload_objs~ - '"$_LT_TAGVAR(reload_cmds, $1)" - ;; - *) - _LT_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' - _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' - ;; - esac - ;; - - tandem*) - case $cc_basename in - NCC*) - # NonStop-UX NCC 3.20 - # FIXME: insert proper C++ library support - _LT_TAGVAR(ld_shlibs, $1)=no - ;; - *) - # FIXME: insert proper C++ library support - _LT_TAGVAR(ld_shlibs, $1)=no - ;; - esac - ;; - - vxworks*) - # FIXME: insert proper C++ library support - _LT_TAGVAR(ld_shlibs, $1)=no - ;; - - *) - # FIXME: insert proper C++ library support - _LT_TAGVAR(ld_shlibs, $1)=no - ;; - esac - - AC_MSG_RESULT([$_LT_TAGVAR(ld_shlibs, $1)]) - test "$_LT_TAGVAR(ld_shlibs, $1)" = no && can_build_shared=no - - _LT_TAGVAR(GCC, $1)="$GXX" - _LT_TAGVAR(LD, $1)="$LD" - - ## CAVEAT EMPTOR: - ## There is no encapsulation within the following macros, do not change - ## the running order or otherwise move them around unless you know exactly - ## what you are doing... - _LT_SYS_HIDDEN_LIBDEPS($1) - _LT_COMPILER_PIC($1) - _LT_COMPILER_C_O($1) - _LT_COMPILER_FILE_LOCKS($1) - _LT_LINKER_SHLIBS($1) - _LT_SYS_DYNAMIC_LINKER($1) - _LT_LINKER_HARDCODE_LIBPATH($1) - - _LT_CONFIG($1) - fi # test -n "$compiler" - - CC=$lt_save_CC - CFLAGS=$lt_save_CFLAGS - LDCXX=$LD - LD=$lt_save_LD - GCC=$lt_save_GCC - with_gnu_ld=$lt_save_with_gnu_ld - lt_cv_path_LDCXX=$lt_cv_path_LD - lt_cv_path_LD=$lt_save_path_LD - lt_cv_prog_gnu_ldcxx=$lt_cv_prog_gnu_ld - lt_cv_prog_gnu_ld=$lt_save_with_gnu_ld -fi # test "$_lt_caught_CXX_error" != yes - -AC_LANG_POP -])# _LT_LANG_CXX_CONFIG - - -# _LT_FUNC_STRIPNAME_CNF -# ---------------------- -# func_stripname_cnf prefix suffix name -# strip PREFIX and SUFFIX off of NAME. -# PREFIX and SUFFIX must not contain globbing or regex special -# characters, hashes, percent signs, but SUFFIX may contain a leading -# dot (in which case that matches only a dot). -# -# This function is identical to the (non-XSI) version of func_stripname, -# except this one can be used by m4 code that may be executed by configure, -# rather than the libtool script. -m4_defun([_LT_FUNC_STRIPNAME_CNF],[dnl -AC_REQUIRE([_LT_DECL_SED]) -AC_REQUIRE([_LT_PROG_ECHO_BACKSLASH]) -func_stripname_cnf () -{ - case ${2} in - .*) func_stripname_result=`$ECHO "${3}" | $SED "s%^${1}%%; s%\\\\${2}\$%%"`;; - *) func_stripname_result=`$ECHO "${3}" | $SED "s%^${1}%%; s%${2}\$%%"`;; - esac -} # func_stripname_cnf -])# _LT_FUNC_STRIPNAME_CNF - -# _LT_SYS_HIDDEN_LIBDEPS([TAGNAME]) -# --------------------------------- -# Figure out "hidden" library dependencies from verbose -# compiler output when linking a shared library. -# Parse the compiler output and extract the necessary -# objects, libraries and library flags. -m4_defun([_LT_SYS_HIDDEN_LIBDEPS], -[m4_require([_LT_FILEUTILS_DEFAULTS])dnl -AC_REQUIRE([_LT_FUNC_STRIPNAME_CNF])dnl -# Dependencies to place before and after the object being linked: -_LT_TAGVAR(predep_objects, $1)= -_LT_TAGVAR(postdep_objects, $1)= -_LT_TAGVAR(predeps, $1)= -_LT_TAGVAR(postdeps, $1)= -_LT_TAGVAR(compiler_lib_search_path, $1)= - -dnl we can't use the lt_simple_compile_test_code here, -dnl because it contains code intended for an executable, -dnl not a library. It's possible we should let each -dnl tag define a new lt_????_link_test_code variable, -dnl but it's only used here... -m4_if([$1], [], [cat > conftest.$ac_ext <<_LT_EOF -int a; -void foo (void) { a = 0; } -_LT_EOF -], [$1], [CXX], [cat > conftest.$ac_ext <<_LT_EOF -class Foo -{ -public: - Foo (void) { a = 0; } -private: - int a; -}; -_LT_EOF -], [$1], [F77], [cat > conftest.$ac_ext <<_LT_EOF - subroutine foo - implicit none - integer*4 a - a=0 - return - end -_LT_EOF -], [$1], [FC], [cat > conftest.$ac_ext <<_LT_EOF - subroutine foo - implicit none - integer a - a=0 - return - end -_LT_EOF -], [$1], [GCJ], [cat > conftest.$ac_ext <<_LT_EOF -public class foo { - private int a; - public void bar (void) { - a = 0; - } -}; -_LT_EOF -], [$1], [GO], [cat > conftest.$ac_ext <<_LT_EOF -package foo -func foo() { -} -_LT_EOF -]) - -_lt_libdeps_save_CFLAGS=$CFLAGS -case "$CC $CFLAGS " in #( -*\ -flto*\ *) CFLAGS="$CFLAGS -fno-lto" ;; -*\ -fwhopr*\ *) CFLAGS="$CFLAGS -fno-whopr" ;; -*\ -fuse-linker-plugin*\ *) CFLAGS="$CFLAGS -fno-use-linker-plugin" ;; -esac - -dnl Parse the compiler output and extract the necessary -dnl objects, libraries and library flags. -if AC_TRY_EVAL(ac_compile); then - # Parse the compiler output and extract the necessary - # objects, libraries and library flags. - - # Sentinel used to keep track of whether or not we are before - # the conftest object file. - pre_test_object_deps_done=no - - for p in `eval "$output_verbose_link_cmd"`; do - case ${prev}${p} in - - -L* | -R* | -l*) - # Some compilers place space between "-{L,R}" and the path. - # Remove the space. - if test $p = "-L" || - test $p = "-R"; then - prev=$p - continue - fi - - # Expand the sysroot to ease extracting the directories later. - if test -z "$prev"; then - case $p in - -L*) func_stripname_cnf '-L' '' "$p"; prev=-L; p=$func_stripname_result ;; - -R*) func_stripname_cnf '-R' '' "$p"; prev=-R; p=$func_stripname_result ;; - -l*) func_stripname_cnf '-l' '' "$p"; prev=-l; p=$func_stripname_result ;; - esac - fi - case $p in - =*) func_stripname_cnf '=' '' "$p"; p=$lt_sysroot$func_stripname_result ;; - esac - if test "$pre_test_object_deps_done" = no; then - case ${prev} in - -L | -R) - # Internal compiler library paths should come after those - # provided the user. The postdeps already come after the - # user supplied libs so there is no need to process them. - if test -z "$_LT_TAGVAR(compiler_lib_search_path, $1)"; then - _LT_TAGVAR(compiler_lib_search_path, $1)="${prev}${p}" - else - _LT_TAGVAR(compiler_lib_search_path, $1)="${_LT_TAGVAR(compiler_lib_search_path, $1)} ${prev}${p}" - fi - ;; - # The "-l" case would never come before the object being - # linked, so don't bother handling this case. - esac - else - if test -z "$_LT_TAGVAR(postdeps, $1)"; then - _LT_TAGVAR(postdeps, $1)="${prev}${p}" - else - _LT_TAGVAR(postdeps, $1)="${_LT_TAGVAR(postdeps, $1)} ${prev}${p}" - fi - fi - prev= - ;; - - *.lto.$objext) ;; # Ignore GCC LTO objects - *.$objext) - # This assumes that the test object file only shows up - # once in the compiler output. - if test "$p" = "conftest.$objext"; then - pre_test_object_deps_done=yes - continue - fi - - if test "$pre_test_object_deps_done" = no; then - if test -z "$_LT_TAGVAR(predep_objects, $1)"; then - _LT_TAGVAR(predep_objects, $1)="$p" - else - _LT_TAGVAR(predep_objects, $1)="$_LT_TAGVAR(predep_objects, $1) $p" - fi - else - if test -z "$_LT_TAGVAR(postdep_objects, $1)"; then - _LT_TAGVAR(postdep_objects, $1)="$p" - else - _LT_TAGVAR(postdep_objects, $1)="$_LT_TAGVAR(postdep_objects, $1) $p" - fi - fi - ;; - - *) ;; # Ignore the rest. - - esac - done - - # Clean up. - rm -f a.out a.exe -else - echo "libtool.m4: error: problem compiling $1 test program" -fi - -$RM -f confest.$objext -CFLAGS=$_lt_libdeps_save_CFLAGS - -# PORTME: override above test on systems where it is broken -m4_if([$1], [CXX], -[case $host_os in -interix[[3-9]]*) - # Interix 3.5 installs completely hosed .la files for C++, so rather than - # hack all around it, let's just trust "g++" to DTRT. - _LT_TAGVAR(predep_objects,$1)= - _LT_TAGVAR(postdep_objects,$1)= - _LT_TAGVAR(postdeps,$1)= - ;; - -linux*) - case `$CC -V 2>&1 | sed 5q` in - *Sun\ C*) - # Sun C++ 5.9 - - # The more standards-conforming stlport4 library is - # incompatible with the Cstd library. Avoid specifying - # it if it's in CXXFLAGS. Ignore libCrun as - # -library=stlport4 depends on it. - case " $CXX $CXXFLAGS " in - *" -library=stlport4 "*) - solaris_use_stlport4=yes - ;; - esac - - if test "$solaris_use_stlport4" != yes; then - _LT_TAGVAR(postdeps,$1)='-library=Cstd -library=Crun' - fi - ;; - esac - ;; - -solaris*) - case $cc_basename in - CC* | sunCC*) - # The more standards-conforming stlport4 library is - # incompatible with the Cstd library. Avoid specifying - # it if it's in CXXFLAGS. Ignore libCrun as - # -library=stlport4 depends on it. - case " $CXX $CXXFLAGS " in - *" -library=stlport4 "*) - solaris_use_stlport4=yes - ;; - esac - - # Adding this requires a known-good setup of shared libraries for - # Sun compiler versions before 5.6, else PIC objects from an old - # archive will be linked into the output, leading to subtle bugs. - if test "$solaris_use_stlport4" != yes; then - _LT_TAGVAR(postdeps,$1)='-library=Cstd -library=Crun' - fi - ;; - esac - ;; -esac -]) - -case " $_LT_TAGVAR(postdeps, $1) " in -*" -lc "*) _LT_TAGVAR(archive_cmds_need_lc, $1)=no ;; -esac - _LT_TAGVAR(compiler_lib_search_dirs, $1)= -if test -n "${_LT_TAGVAR(compiler_lib_search_path, $1)}"; then - _LT_TAGVAR(compiler_lib_search_dirs, $1)=`echo " ${_LT_TAGVAR(compiler_lib_search_path, $1)}" | ${SED} -e 's! -L! !g' -e 's!^ !!'` -fi -_LT_TAGDECL([], [compiler_lib_search_dirs], [1], - [The directories searched by this compiler when creating a shared library]) -_LT_TAGDECL([], [predep_objects], [1], - [Dependencies to place before and after the objects being linked to - create a shared library]) -_LT_TAGDECL([], [postdep_objects], [1]) -_LT_TAGDECL([], [predeps], [1]) -_LT_TAGDECL([], [postdeps], [1]) -_LT_TAGDECL([], [compiler_lib_search_path], [1], - [The library search path used internally by the compiler when linking - a shared library]) -])# _LT_SYS_HIDDEN_LIBDEPS - - -# _LT_LANG_F77_CONFIG([TAG]) -# -------------------------- -# Ensure that the configuration variables for a Fortran 77 compiler are -# suitably defined. These variables are subsequently used by _LT_CONFIG -# to write the compiler configuration to `libtool'. -m4_defun([_LT_LANG_F77_CONFIG], -[AC_LANG_PUSH(Fortran 77) -if test -z "$F77" || test "X$F77" = "Xno"; then - _lt_disable_F77=yes -fi - -_LT_TAGVAR(archive_cmds_need_lc, $1)=no -_LT_TAGVAR(allow_undefined_flag, $1)= -_LT_TAGVAR(always_export_symbols, $1)=no -_LT_TAGVAR(archive_expsym_cmds, $1)= -_LT_TAGVAR(export_dynamic_flag_spec, $1)= -_LT_TAGVAR(hardcode_direct, $1)=no -_LT_TAGVAR(hardcode_direct_absolute, $1)=no -_LT_TAGVAR(hardcode_libdir_flag_spec, $1)= -_LT_TAGVAR(hardcode_libdir_separator, $1)= -_LT_TAGVAR(hardcode_minus_L, $1)=no -_LT_TAGVAR(hardcode_automatic, $1)=no -_LT_TAGVAR(inherit_rpath, $1)=no -_LT_TAGVAR(module_cmds, $1)= -_LT_TAGVAR(module_expsym_cmds, $1)= -_LT_TAGVAR(link_all_deplibs, $1)=unknown -_LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds -_LT_TAGVAR(reload_flag, $1)=$reload_flag -_LT_TAGVAR(reload_cmds, $1)=$reload_cmds -_LT_TAGVAR(no_undefined_flag, $1)= -_LT_TAGVAR(whole_archive_flag_spec, $1)= -_LT_TAGVAR(enable_shared_with_static_runtimes, $1)=no - -# Source file extension for f77 test sources. -ac_ext=f - -# Object file extension for compiled f77 test sources. -objext=o -_LT_TAGVAR(objext, $1)=$objext - -# No sense in running all these tests if we already determined that -# the F77 compiler isn't working. Some variables (like enable_shared) -# are currently assumed to apply to all compilers on this platform, -# and will be corrupted by setting them based on a non-working compiler. -if test "$_lt_disable_F77" != yes; then - # Code to be used in simple compile tests - lt_simple_compile_test_code="\ - subroutine t - return - end -" - - # Code to be used in simple link tests - lt_simple_link_test_code="\ - program t - end -" - - # ltmain only uses $CC for tagged configurations so make sure $CC is set. - _LT_TAG_COMPILER - - # save warnings/boilerplate of simple test code - _LT_COMPILER_BOILERPLATE - _LT_LINKER_BOILERPLATE - - # Allow CC to be a program name with arguments. - lt_save_CC="$CC" - lt_save_GCC=$GCC - lt_save_CFLAGS=$CFLAGS - CC=${F77-"f77"} - CFLAGS=$FFLAGS - compiler=$CC - _LT_TAGVAR(compiler, $1)=$CC - _LT_CC_BASENAME([$compiler]) - GCC=$G77 - if test -n "$compiler"; then - AC_MSG_CHECKING([if libtool supports shared libraries]) - AC_MSG_RESULT([$can_build_shared]) - - AC_MSG_CHECKING([whether to build shared libraries]) - test "$can_build_shared" = "no" && enable_shared=no - - # On AIX, shared libraries and static libraries use the same namespace, and - # are all built from PIC. - case $host_os in - aix3*) - test "$enable_shared" = yes && enable_static=no - if test -n "$RANLIB"; then - archive_cmds="$archive_cmds~\$RANLIB \$lib" - postinstall_cmds='$RANLIB $lib' - fi - ;; - aix[[4-9]]*) - if test "$host_cpu" != ia64 && test "$aix_use_runtimelinking" = no ; then - test "$enable_shared" = yes && enable_static=no - fi - ;; - esac - AC_MSG_RESULT([$enable_shared]) - - AC_MSG_CHECKING([whether to build static libraries]) - # Make sure either enable_shared or enable_static is yes. - test "$enable_shared" = yes || enable_static=yes - AC_MSG_RESULT([$enable_static]) - - _LT_TAGVAR(GCC, $1)="$G77" - _LT_TAGVAR(LD, $1)="$LD" - - ## CAVEAT EMPTOR: - ## There is no encapsulation within the following macros, do not change - ## the running order or otherwise move them around unless you know exactly - ## what you are doing... - _LT_COMPILER_PIC($1) - _LT_COMPILER_C_O($1) - _LT_COMPILER_FILE_LOCKS($1) - _LT_LINKER_SHLIBS($1) - _LT_SYS_DYNAMIC_LINKER($1) - _LT_LINKER_HARDCODE_LIBPATH($1) - - _LT_CONFIG($1) - fi # test -n "$compiler" - - GCC=$lt_save_GCC - CC="$lt_save_CC" - CFLAGS="$lt_save_CFLAGS" -fi # test "$_lt_disable_F77" != yes - -AC_LANG_POP -])# _LT_LANG_F77_CONFIG - - -# _LT_LANG_FC_CONFIG([TAG]) -# ------------------------- -# Ensure that the configuration variables for a Fortran compiler are -# suitably defined. These variables are subsequently used by _LT_CONFIG -# to write the compiler configuration to `libtool'. -m4_defun([_LT_LANG_FC_CONFIG], -[AC_LANG_PUSH(Fortran) - -if test -z "$FC" || test "X$FC" = "Xno"; then - _lt_disable_FC=yes -fi - -_LT_TAGVAR(archive_cmds_need_lc, $1)=no -_LT_TAGVAR(allow_undefined_flag, $1)= -_LT_TAGVAR(always_export_symbols, $1)=no -_LT_TAGVAR(archive_expsym_cmds, $1)= -_LT_TAGVAR(export_dynamic_flag_spec, $1)= -_LT_TAGVAR(hardcode_direct, $1)=no -_LT_TAGVAR(hardcode_direct_absolute, $1)=no -_LT_TAGVAR(hardcode_libdir_flag_spec, $1)= -_LT_TAGVAR(hardcode_libdir_separator, $1)= -_LT_TAGVAR(hardcode_minus_L, $1)=no -_LT_TAGVAR(hardcode_automatic, $1)=no -_LT_TAGVAR(inherit_rpath, $1)=no -_LT_TAGVAR(module_cmds, $1)= -_LT_TAGVAR(module_expsym_cmds, $1)= -_LT_TAGVAR(link_all_deplibs, $1)=unknown -_LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds -_LT_TAGVAR(reload_flag, $1)=$reload_flag -_LT_TAGVAR(reload_cmds, $1)=$reload_cmds -_LT_TAGVAR(no_undefined_flag, $1)= -_LT_TAGVAR(whole_archive_flag_spec, $1)= -_LT_TAGVAR(enable_shared_with_static_runtimes, $1)=no - -# Source file extension for fc test sources. -ac_ext=${ac_fc_srcext-f} - -# Object file extension for compiled fc test sources. -objext=o -_LT_TAGVAR(objext, $1)=$objext - -# No sense in running all these tests if we already determined that -# the FC compiler isn't working. Some variables (like enable_shared) -# are currently assumed to apply to all compilers on this platform, -# and will be corrupted by setting them based on a non-working compiler. -if test "$_lt_disable_FC" != yes; then - # Code to be used in simple compile tests - lt_simple_compile_test_code="\ - subroutine t - return - end -" - - # Code to be used in simple link tests - lt_simple_link_test_code="\ - program t - end -" - - # ltmain only uses $CC for tagged configurations so make sure $CC is set. - _LT_TAG_COMPILER - - # save warnings/boilerplate of simple test code - _LT_COMPILER_BOILERPLATE - _LT_LINKER_BOILERPLATE - - # Allow CC to be a program name with arguments. - lt_save_CC="$CC" - lt_save_GCC=$GCC - lt_save_CFLAGS=$CFLAGS - CC=${FC-"f95"} - CFLAGS=$FCFLAGS - compiler=$CC - GCC=$ac_cv_fc_compiler_gnu - - _LT_TAGVAR(compiler, $1)=$CC - _LT_CC_BASENAME([$compiler]) - - if test -n "$compiler"; then - AC_MSG_CHECKING([if libtool supports shared libraries]) - AC_MSG_RESULT([$can_build_shared]) - - AC_MSG_CHECKING([whether to build shared libraries]) - test "$can_build_shared" = "no" && enable_shared=no - - # On AIX, shared libraries and static libraries use the same namespace, and - # are all built from PIC. - case $host_os in - aix3*) - test "$enable_shared" = yes && enable_static=no - if test -n "$RANLIB"; then - archive_cmds="$archive_cmds~\$RANLIB \$lib" - postinstall_cmds='$RANLIB $lib' - fi - ;; - aix[[4-9]]*) - if test "$host_cpu" != ia64 && test "$aix_use_runtimelinking" = no ; then - test "$enable_shared" = yes && enable_static=no - fi - ;; - esac - AC_MSG_RESULT([$enable_shared]) - - AC_MSG_CHECKING([whether to build static libraries]) - # Make sure either enable_shared or enable_static is yes. - test "$enable_shared" = yes || enable_static=yes - AC_MSG_RESULT([$enable_static]) - - _LT_TAGVAR(GCC, $1)="$ac_cv_fc_compiler_gnu" - _LT_TAGVAR(LD, $1)="$LD" - - ## CAVEAT EMPTOR: - ## There is no encapsulation within the following macros, do not change - ## the running order or otherwise move them around unless you know exactly - ## what you are doing... - _LT_SYS_HIDDEN_LIBDEPS($1) - _LT_COMPILER_PIC($1) - _LT_COMPILER_C_O($1) - _LT_COMPILER_FILE_LOCKS($1) - _LT_LINKER_SHLIBS($1) - _LT_SYS_DYNAMIC_LINKER($1) - _LT_LINKER_HARDCODE_LIBPATH($1) - - _LT_CONFIG($1) - fi # test -n "$compiler" - - GCC=$lt_save_GCC - CC=$lt_save_CC - CFLAGS=$lt_save_CFLAGS -fi # test "$_lt_disable_FC" != yes - -AC_LANG_POP -])# _LT_LANG_FC_CONFIG - - -# _LT_LANG_GCJ_CONFIG([TAG]) -# -------------------------- -# Ensure that the configuration variables for the GNU Java Compiler compiler -# are suitably defined. These variables are subsequently used by _LT_CONFIG -# to write the compiler configuration to `libtool'. -m4_defun([_LT_LANG_GCJ_CONFIG], -[AC_REQUIRE([LT_PROG_GCJ])dnl -AC_LANG_SAVE - -# Source file extension for Java test sources. -ac_ext=java - -# Object file extension for compiled Java test sources. -objext=o -_LT_TAGVAR(objext, $1)=$objext - -# Code to be used in simple compile tests -lt_simple_compile_test_code="class foo {}" - -# Code to be used in simple link tests -lt_simple_link_test_code='public class conftest { public static void main(String[[]] argv) {}; }' - -# ltmain only uses $CC for tagged configurations so make sure $CC is set. -_LT_TAG_COMPILER - -# save warnings/boilerplate of simple test code -_LT_COMPILER_BOILERPLATE -_LT_LINKER_BOILERPLATE - -# Allow CC to be a program name with arguments. -lt_save_CC=$CC -lt_save_CFLAGS=$CFLAGS -lt_save_GCC=$GCC -GCC=yes -CC=${GCJ-"gcj"} -CFLAGS=$GCJFLAGS -compiler=$CC -_LT_TAGVAR(compiler, $1)=$CC -_LT_TAGVAR(LD, $1)="$LD" -_LT_CC_BASENAME([$compiler]) - -# GCJ did not exist at the time GCC didn't implicitly link libc in. -_LT_TAGVAR(archive_cmds_need_lc, $1)=no - -_LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds -_LT_TAGVAR(reload_flag, $1)=$reload_flag -_LT_TAGVAR(reload_cmds, $1)=$reload_cmds - -## CAVEAT EMPTOR: -## There is no encapsulation within the following macros, do not change -## the running order or otherwise move them around unless you know exactly -## what you are doing... -if test -n "$compiler"; then - _LT_COMPILER_NO_RTTI($1) - _LT_COMPILER_PIC($1) - _LT_COMPILER_C_O($1) - _LT_COMPILER_FILE_LOCKS($1) - _LT_LINKER_SHLIBS($1) - _LT_LINKER_HARDCODE_LIBPATH($1) - - _LT_CONFIG($1) -fi - -AC_LANG_RESTORE - -GCC=$lt_save_GCC -CC=$lt_save_CC -CFLAGS=$lt_save_CFLAGS -])# _LT_LANG_GCJ_CONFIG - - -# _LT_LANG_GO_CONFIG([TAG]) -# -------------------------- -# Ensure that the configuration variables for the GNU Go compiler -# are suitably defined. These variables are subsequently used by _LT_CONFIG -# to write the compiler configuration to `libtool'. -m4_defun([_LT_LANG_GO_CONFIG], -[AC_REQUIRE([LT_PROG_GO])dnl -AC_LANG_SAVE - -# Source file extension for Go test sources. -ac_ext=go - -# Object file extension for compiled Go test sources. -objext=o -_LT_TAGVAR(objext, $1)=$objext - -# Code to be used in simple compile tests -lt_simple_compile_test_code="package main; func main() { }" - -# Code to be used in simple link tests -lt_simple_link_test_code='package main; func main() { }' - -# ltmain only uses $CC for tagged configurations so make sure $CC is set. -_LT_TAG_COMPILER - -# save warnings/boilerplate of simple test code -_LT_COMPILER_BOILERPLATE -_LT_LINKER_BOILERPLATE - -# Allow CC to be a program name with arguments. -lt_save_CC=$CC -lt_save_CFLAGS=$CFLAGS -lt_save_GCC=$GCC -GCC=yes -CC=${GOC-"gccgo"} -CFLAGS=$GOFLAGS -compiler=$CC -_LT_TAGVAR(compiler, $1)=$CC -_LT_TAGVAR(LD, $1)="$LD" -_LT_CC_BASENAME([$compiler]) - -# Go did not exist at the time GCC didn't implicitly link libc in. -_LT_TAGVAR(archive_cmds_need_lc, $1)=no - -_LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds -_LT_TAGVAR(reload_flag, $1)=$reload_flag -_LT_TAGVAR(reload_cmds, $1)=$reload_cmds - -## CAVEAT EMPTOR: -## There is no encapsulation within the following macros, do not change -## the running order or otherwise move them around unless you know exactly -## what you are doing... -if test -n "$compiler"; then - _LT_COMPILER_NO_RTTI($1) - _LT_COMPILER_PIC($1) - _LT_COMPILER_C_O($1) - _LT_COMPILER_FILE_LOCKS($1) - _LT_LINKER_SHLIBS($1) - _LT_LINKER_HARDCODE_LIBPATH($1) - - _LT_CONFIG($1) -fi - -AC_LANG_RESTORE - -GCC=$lt_save_GCC -CC=$lt_save_CC -CFLAGS=$lt_save_CFLAGS -])# _LT_LANG_GO_CONFIG - - -# _LT_LANG_RC_CONFIG([TAG]) -# ------------------------- -# Ensure that the configuration variables for the Windows resource compiler -# are suitably defined. These variables are subsequently used by _LT_CONFIG -# to write the compiler configuration to `libtool'. -m4_defun([_LT_LANG_RC_CONFIG], -[AC_REQUIRE([LT_PROG_RC])dnl -AC_LANG_SAVE - -# Source file extension for RC test sources. -ac_ext=rc - -# Object file extension for compiled RC test sources. -objext=o -_LT_TAGVAR(objext, $1)=$objext - -# Code to be used in simple compile tests -lt_simple_compile_test_code='sample MENU { MENUITEM "&Soup", 100, CHECKED }' - -# Code to be used in simple link tests -lt_simple_link_test_code="$lt_simple_compile_test_code" - -# ltmain only uses $CC for tagged configurations so make sure $CC is set. -_LT_TAG_COMPILER - -# save warnings/boilerplate of simple test code -_LT_COMPILER_BOILERPLATE -_LT_LINKER_BOILERPLATE - -# Allow CC to be a program name with arguments. -lt_save_CC="$CC" -lt_save_CFLAGS=$CFLAGS -lt_save_GCC=$GCC -GCC= -CC=${RC-"windres"} -CFLAGS= -compiler=$CC -_LT_TAGVAR(compiler, $1)=$CC -_LT_CC_BASENAME([$compiler]) -_LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)=yes - -if test -n "$compiler"; then - : - _LT_CONFIG($1) -fi - -GCC=$lt_save_GCC -AC_LANG_RESTORE -CC=$lt_save_CC -CFLAGS=$lt_save_CFLAGS -])# _LT_LANG_RC_CONFIG - - -# LT_PROG_GCJ -# ----------- -AC_DEFUN([LT_PROG_GCJ], -[m4_ifdef([AC_PROG_GCJ], [AC_PROG_GCJ], - [m4_ifdef([A][M_PROG_GCJ], [A][M_PROG_GCJ], - [AC_CHECK_TOOL(GCJ, gcj,) - test "x${GCJFLAGS+set}" = xset || GCJFLAGS="-g -O2" - AC_SUBST(GCJFLAGS)])])[]dnl -]) - -# Old name: -AU_ALIAS([LT_AC_PROG_GCJ], [LT_PROG_GCJ]) -dnl aclocal-1.4 backwards compatibility: -dnl AC_DEFUN([LT_AC_PROG_GCJ], []) - - -# LT_PROG_GO -# ---------- -AC_DEFUN([LT_PROG_GO], -[AC_CHECK_TOOL(GOC, gccgo,) -]) - - -# LT_PROG_RC -# ---------- -AC_DEFUN([LT_PROG_RC], -[AC_CHECK_TOOL(RC, windres,) -]) - -# Old name: -AU_ALIAS([LT_AC_PROG_RC], [LT_PROG_RC]) -dnl aclocal-1.4 backwards compatibility: -dnl AC_DEFUN([LT_AC_PROG_RC], []) - - -# _LT_DECL_EGREP -# -------------- -# If we don't have a new enough Autoconf to choose the best grep -# available, choose the one first in the user's PATH. -m4_defun([_LT_DECL_EGREP], -[AC_REQUIRE([AC_PROG_EGREP])dnl -AC_REQUIRE([AC_PROG_FGREP])dnl -test -z "$GREP" && GREP=grep -_LT_DECL([], [GREP], [1], [A grep program that handles long lines]) -_LT_DECL([], [EGREP], [1], [An ERE matcher]) -_LT_DECL([], [FGREP], [1], [A literal string matcher]) -dnl Non-bleeding-edge autoconf doesn't subst GREP, so do it here too -AC_SUBST([GREP]) -]) - - -# _LT_DECL_OBJDUMP -# -------------- -# If we don't have a new enough Autoconf to choose the best objdump -# available, choose the one first in the user's PATH. -m4_defun([_LT_DECL_OBJDUMP], -[AC_CHECK_TOOL(OBJDUMP, objdump, false) -test -z "$OBJDUMP" && OBJDUMP=objdump -_LT_DECL([], [OBJDUMP], [1], [An object symbol dumper]) -AC_SUBST([OBJDUMP]) -]) - -# _LT_DECL_DLLTOOL -# ---------------- -# Ensure DLLTOOL variable is set. -m4_defun([_LT_DECL_DLLTOOL], -[AC_CHECK_TOOL(DLLTOOL, dlltool, false) -test -z "$DLLTOOL" && DLLTOOL=dlltool -_LT_DECL([], [DLLTOOL], [1], [DLL creation program]) -AC_SUBST([DLLTOOL]) -]) - -# _LT_DECL_SED -# ------------ -# Check for a fully-functional sed program, that truncates -# as few characters as possible. Prefer GNU sed if found. -m4_defun([_LT_DECL_SED], -[AC_PROG_SED -test -z "$SED" && SED=sed -Xsed="$SED -e 1s/^X//" -_LT_DECL([], [SED], [1], [A sed program that does not truncate output]) -_LT_DECL([], [Xsed], ["\$SED -e 1s/^X//"], - [Sed that helps us avoid accidentally triggering echo(1) options like -n]) -])# _LT_DECL_SED - -m4_ifndef([AC_PROG_SED], [ -############################################################ -# NOTE: This macro has been submitted for inclusion into # -# GNU Autoconf as AC_PROG_SED. When it is available in # -# a released version of Autoconf we should remove this # -# macro and use it instead. # -############################################################ - -m4_defun([AC_PROG_SED], -[AC_MSG_CHECKING([for a sed that does not truncate output]) -AC_CACHE_VAL(lt_cv_path_SED, -[# Loop through the user's path and test for sed and gsed. -# Then use that list of sed's as ones to test for truncation. -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for lt_ac_prog in sed gsed; do - for ac_exec_ext in '' $ac_executable_extensions; do - if $as_executable_p "$as_dir/$lt_ac_prog$ac_exec_ext"; then - lt_ac_sed_list="$lt_ac_sed_list $as_dir/$lt_ac_prog$ac_exec_ext" - fi - done - done -done -IFS=$as_save_IFS -lt_ac_max=0 -lt_ac_count=0 -# Add /usr/xpg4/bin/sed as it is typically found on Solaris -# along with /bin/sed that truncates output. -for lt_ac_sed in $lt_ac_sed_list /usr/xpg4/bin/sed; do - test ! -f $lt_ac_sed && continue - cat /dev/null > conftest.in - lt_ac_count=0 - echo $ECHO_N "0123456789$ECHO_C" >conftest.in - # Check for GNU sed and select it if it is found. - if "$lt_ac_sed" --version 2>&1 < /dev/null | grep 'GNU' > /dev/null; then - lt_cv_path_SED=$lt_ac_sed - break - fi - while true; do - cat conftest.in conftest.in >conftest.tmp - mv conftest.tmp conftest.in - cp conftest.in conftest.nl - echo >>conftest.nl - $lt_ac_sed -e 's/a$//' < conftest.nl >conftest.out || break - cmp -s conftest.out conftest.nl || break - # 10000 chars as input seems more than enough - test $lt_ac_count -gt 10 && break - lt_ac_count=`expr $lt_ac_count + 1` - if test $lt_ac_count -gt $lt_ac_max; then - lt_ac_max=$lt_ac_count - lt_cv_path_SED=$lt_ac_sed - fi - done -done -]) -SED=$lt_cv_path_SED -AC_SUBST([SED]) -AC_MSG_RESULT([$SED]) -])#AC_PROG_SED -])#m4_ifndef - -# Old name: -AU_ALIAS([LT_AC_PROG_SED], [AC_PROG_SED]) -dnl aclocal-1.4 backwards compatibility: -dnl AC_DEFUN([LT_AC_PROG_SED], []) - - -# _LT_CHECK_SHELL_FEATURES -# ------------------------ -# Find out whether the shell is Bourne or XSI compatible, -# or has some other useful features. -m4_defun([_LT_CHECK_SHELL_FEATURES], -[AC_MSG_CHECKING([whether the shell understands some XSI constructs]) -# Try some XSI features -xsi_shell=no -( _lt_dummy="a/b/c" - test "${_lt_dummy##*/},${_lt_dummy%/*},${_lt_dummy#??}"${_lt_dummy%"$_lt_dummy"}, \ - = c,a/b,b/c, \ - && eval 'test $(( 1 + 1 )) -eq 2 \ - && test "${#_lt_dummy}" -eq 5' ) >/dev/null 2>&1 \ - && xsi_shell=yes -AC_MSG_RESULT([$xsi_shell]) -_LT_CONFIG_LIBTOOL_INIT([xsi_shell='$xsi_shell']) - -AC_MSG_CHECKING([whether the shell understands "+="]) -lt_shell_append=no -( foo=bar; set foo baz; eval "$[1]+=\$[2]" && test "$foo" = barbaz ) \ - >/dev/null 2>&1 \ - && lt_shell_append=yes -AC_MSG_RESULT([$lt_shell_append]) -_LT_CONFIG_LIBTOOL_INIT([lt_shell_append='$lt_shell_append']) - -if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then - lt_unset=unset -else - lt_unset=false -fi -_LT_DECL([], [lt_unset], [0], [whether the shell understands "unset"])dnl - -# test EBCDIC or ASCII -case `echo X|tr X '\101'` in - A) # ASCII based system - # \n is not interpreted correctly by Solaris 8 /usr/ucb/tr - lt_SP2NL='tr \040 \012' - lt_NL2SP='tr \015\012 \040\040' - ;; - *) # EBCDIC based system - lt_SP2NL='tr \100 \n' - lt_NL2SP='tr \r\n \100\100' - ;; -esac -_LT_DECL([SP2NL], [lt_SP2NL], [1], [turn spaces into newlines])dnl -_LT_DECL([NL2SP], [lt_NL2SP], [1], [turn newlines into spaces])dnl -])# _LT_CHECK_SHELL_FEATURES - - -# _LT_PROG_FUNCTION_REPLACE (FUNCNAME, REPLACEMENT-BODY) -# ------------------------------------------------------ -# In `$cfgfile', look for function FUNCNAME delimited by `^FUNCNAME ()$' and -# '^} FUNCNAME ', and replace its body with REPLACEMENT-BODY. -m4_defun([_LT_PROG_FUNCTION_REPLACE], -[dnl { -sed -e '/^$1 ()$/,/^} # $1 /c\ -$1 ()\ -{\ -m4_bpatsubsts([$2], [$], [\\], [^\([ ]\)], [\\\1]) -} # Extended-shell $1 implementation' "$cfgfile" > $cfgfile.tmp \ - && mv -f "$cfgfile.tmp" "$cfgfile" \ - || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") -test 0 -eq $? || _lt_function_replace_fail=: -]) - - -# _LT_PROG_REPLACE_SHELLFNS -# ------------------------- -# Replace existing portable implementations of several shell functions with -# equivalent extended shell implementations where those features are available.. -m4_defun([_LT_PROG_REPLACE_SHELLFNS], -[if test x"$xsi_shell" = xyes; then - _LT_PROG_FUNCTION_REPLACE([func_dirname], [dnl - case ${1} in - */*) func_dirname_result="${1%/*}${2}" ;; - * ) func_dirname_result="${3}" ;; - esac]) - - _LT_PROG_FUNCTION_REPLACE([func_basename], [dnl - func_basename_result="${1##*/}"]) - - _LT_PROG_FUNCTION_REPLACE([func_dirname_and_basename], [dnl - case ${1} in - */*) func_dirname_result="${1%/*}${2}" ;; - * ) func_dirname_result="${3}" ;; - esac - func_basename_result="${1##*/}"]) - - _LT_PROG_FUNCTION_REPLACE([func_stripname], [dnl - # pdksh 5.2.14 does not do ${X%$Y} correctly if both X and Y are - # positional parameters, so assign one to ordinary parameter first. - func_stripname_result=${3} - func_stripname_result=${func_stripname_result#"${1}"} - func_stripname_result=${func_stripname_result%"${2}"}]) - - _LT_PROG_FUNCTION_REPLACE([func_split_long_opt], [dnl - func_split_long_opt_name=${1%%=*} - func_split_long_opt_arg=${1#*=}]) - - _LT_PROG_FUNCTION_REPLACE([func_split_short_opt], [dnl - func_split_short_opt_arg=${1#??} - func_split_short_opt_name=${1%"$func_split_short_opt_arg"}]) - - _LT_PROG_FUNCTION_REPLACE([func_lo2o], [dnl - case ${1} in - *.lo) func_lo2o_result=${1%.lo}.${objext} ;; - *) func_lo2o_result=${1} ;; - esac]) - - _LT_PROG_FUNCTION_REPLACE([func_xform], [ func_xform_result=${1%.*}.lo]) - - _LT_PROG_FUNCTION_REPLACE([func_arith], [ func_arith_result=$(( $[*] ))]) - - _LT_PROG_FUNCTION_REPLACE([func_len], [ func_len_result=${#1}]) -fi - -if test x"$lt_shell_append" = xyes; then - _LT_PROG_FUNCTION_REPLACE([func_append], [ eval "${1}+=\\${2}"]) - - _LT_PROG_FUNCTION_REPLACE([func_append_quoted], [dnl - func_quote_for_eval "${2}" -dnl m4 expansion turns \\\\ into \\, and then the shell eval turns that into \ - eval "${1}+=\\\\ \\$func_quote_for_eval_result"]) - - # Save a `func_append' function call where possible by direct use of '+=' - sed -e 's%func_append \([[a-zA-Z_]]\{1,\}\) "%\1+="%g' $cfgfile > $cfgfile.tmp \ - && mv -f "$cfgfile.tmp" "$cfgfile" \ - || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") - test 0 -eq $? || _lt_function_replace_fail=: -else - # Save a `func_append' function call even when '+=' is not available - sed -e 's%func_append \([[a-zA-Z_]]\{1,\}\) "%\1="$\1%g' $cfgfile > $cfgfile.tmp \ - && mv -f "$cfgfile.tmp" "$cfgfile" \ - || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") - test 0 -eq $? || _lt_function_replace_fail=: -fi - -if test x"$_lt_function_replace_fail" = x":"; then - AC_MSG_WARN([Unable to substitute extended shell functions in $ofile]) -fi -]) - -# _LT_PATH_CONVERSION_FUNCTIONS -# ----------------------------- -# Determine which file name conversion functions should be used by -# func_to_host_file (and, implicitly, by func_to_host_path). These are needed -# for certain cross-compile configurations and native mingw. -m4_defun([_LT_PATH_CONVERSION_FUNCTIONS], -[AC_REQUIRE([AC_CANONICAL_HOST])dnl -AC_REQUIRE([AC_CANONICAL_BUILD])dnl -AC_MSG_CHECKING([how to convert $build file names to $host format]) -AC_CACHE_VAL(lt_cv_to_host_file_cmd, -[case $host in - *-*-mingw* ) - case $build in - *-*-mingw* ) # actually msys - lt_cv_to_host_file_cmd=func_convert_file_msys_to_w32 - ;; - *-*-cygwin* ) - lt_cv_to_host_file_cmd=func_convert_file_cygwin_to_w32 - ;; - * ) # otherwise, assume *nix - lt_cv_to_host_file_cmd=func_convert_file_nix_to_w32 - ;; - esac - ;; - *-*-cygwin* ) - case $build in - *-*-mingw* ) # actually msys - lt_cv_to_host_file_cmd=func_convert_file_msys_to_cygwin - ;; - *-*-cygwin* ) - lt_cv_to_host_file_cmd=func_convert_file_noop - ;; - * ) # otherwise, assume *nix - lt_cv_to_host_file_cmd=func_convert_file_nix_to_cygwin - ;; - esac - ;; - * ) # unhandled hosts (and "normal" native builds) - lt_cv_to_host_file_cmd=func_convert_file_noop - ;; -esac -]) -to_host_file_cmd=$lt_cv_to_host_file_cmd -AC_MSG_RESULT([$lt_cv_to_host_file_cmd]) -_LT_DECL([to_host_file_cmd], [lt_cv_to_host_file_cmd], - [0], [convert $build file names to $host format])dnl - -AC_MSG_CHECKING([how to convert $build file names to toolchain format]) -AC_CACHE_VAL(lt_cv_to_tool_file_cmd, -[#assume ordinary cross tools, or native build. -lt_cv_to_tool_file_cmd=func_convert_file_noop -case $host in - *-*-mingw* ) - case $build in - *-*-mingw* ) # actually msys - lt_cv_to_tool_file_cmd=func_convert_file_msys_to_w32 - ;; - esac - ;; -esac -]) -to_tool_file_cmd=$lt_cv_to_tool_file_cmd -AC_MSG_RESULT([$lt_cv_to_tool_file_cmd]) -_LT_DECL([to_tool_file_cmd], [lt_cv_to_tool_file_cmd], - [0], [convert $build files to toolchain format])dnl -])# _LT_PATH_CONVERSION_FUNCTIONS diff --git a/m4/libxml2.m4 b/m4/libxml2.m4 deleted file mode 100644 index 68cd824..0000000 --- a/m4/libxml2.m4 +++ /dev/null @@ -1,188 +0,0 @@ -# Configure paths for LIBXML2 -# Mike Hommey 2004-06-19 -# use CPPFLAGS instead of CFLAGS -# Toshio Kuratomi 2001-04-21 -# Adapted from: -# Configure paths for GLIB -# Owen Taylor 97-11-3 - -dnl AM_PATH_XML2([MINIMUM-VERSION, [ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND]]]) -dnl Test for XML, and define XML_CPPFLAGS and XML_LIBS -dnl -AC_DEFUN([AM_PATH_XML2],[ -AC_ARG_WITH(xml-prefix, - [ --with-xml-prefix=PFX Prefix where libxml is installed (optional)], - xml_config_prefix="$withval", xml_config_prefix="") -AC_ARG_WITH(xml-exec-prefix, - [ --with-xml-exec-prefix=PFX Exec prefix where libxml is installed (optional)], - xml_config_exec_prefix="$withval", xml_config_exec_prefix="") -AC_ARG_ENABLE(xmltest, - [ --disable-xmltest Do not try to compile and run a test LIBXML program],, - enable_xmltest=yes) - - if test x$xml_config_exec_prefix != x ; then - xml_config_args="$xml_config_args" - if test x${XML2_CONFIG+set} != xset ; then - XML2_CONFIG=$xml_config_exec_prefix/bin/xml2-config - fi - fi - if test x$xml_config_prefix != x ; then - xml_config_args="$xml_config_args --prefix=$xml_config_prefix" - if test x${XML2_CONFIG+set} != xset ; then - XML2_CONFIG=$xml_config_prefix/bin/xml2-config - fi - fi - - AC_PATH_PROG(XML2_CONFIG, xml2-config, no) - min_xml_version=ifelse([$1], ,2.0.0,[$1]) - AC_MSG_CHECKING(for libxml - version >= $min_xml_version) - no_xml="" - if test "$XML2_CONFIG" = "no" ; then - no_xml=yes - else - XML_CPPFLAGS=`$XML2_CONFIG $xml_config_args --cflags` - XML_LIBS=`$XML2_CONFIG $xml_config_args --libs` - xml_config_major_version=`$XML2_CONFIG $xml_config_args --version | \ - sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\1/'` - xml_config_minor_version=`$XML2_CONFIG $xml_config_args --version | \ - sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\2/'` - xml_config_micro_version=`$XML2_CONFIG $xml_config_args --version | \ - sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\3/'` - if test "x$enable_xmltest" = "xyes" ; then - ac_save_CPPFLAGS="$CPPFLAGS" - ac_save_LIBS="$LIBS" - CPPFLAGS="$CPPFLAGS $XML_CPPFLAGS" - LIBS="$XML_LIBS $LIBS" -dnl -dnl Now check if the installed libxml is sufficiently new. -dnl (Also sanity checks the results of xml2-config to some extent) -dnl - rm -f conf.xmltest - AC_TRY_RUN([ -#include -#include -#include -#include - -int -main() -{ - int xml_major_version, xml_minor_version, xml_micro_version; - int major, minor, micro; - char *tmp_version; - - system("touch conf.xmltest"); - - /* Capture xml2-config output via autoconf/configure variables */ - /* HP/UX 9 (%@#!) writes to sscanf strings */ - tmp_version = (char *)strdup("$min_xml_version"); - if (sscanf(tmp_version, "%d.%d.%d", &major, &minor, µ) != 3) { - printf("%s, bad version string from xml2-config\n", "$min_xml_version"); - exit(1); - } - free(tmp_version); - - /* Capture the version information from the header files */ - tmp_version = (char *)strdup(LIBXML_DOTTED_VERSION); - if (sscanf(tmp_version, "%d.%d.%d", &xml_major_version, &xml_minor_version, &xml_micro_version) != 3) { - printf("%s, bad version string from libxml includes\n", "LIBXML_DOTTED_VERSION"); - exit(1); - } - free(tmp_version); - - /* Compare xml2-config output to the libxml headers */ - if ((xml_major_version != $xml_config_major_version) || - (xml_minor_version != $xml_config_minor_version) || - (xml_micro_version != $xml_config_micro_version)) - { - printf("*** libxml header files (version %d.%d.%d) do not match\n", - xml_major_version, xml_minor_version, xml_micro_version); - printf("*** xml2-config (version %d.%d.%d)\n", - $xml_config_major_version, $xml_config_minor_version, $xml_config_micro_version); - return 1; - } -/* Compare the headers to the library to make sure we match */ - /* Less than ideal -- doesn't provide us with return value feedback, - * only exits if there's a serious mismatch between header and library. - */ - LIBXML_TEST_VERSION; - - /* Test that the library is greater than our minimum version */ - if ((xml_major_version > major) || - ((xml_major_version == major) && (xml_minor_version > minor)) || - ((xml_major_version == major) && (xml_minor_version == minor) && - (xml_micro_version >= micro))) - { - return 0; - } - else - { - printf("\n*** An old version of libxml (%d.%d.%d) was found.\n", - xml_major_version, xml_minor_version, xml_micro_version); - printf("*** You need a version of libxml newer than %d.%d.%d. The latest version of\n", - major, minor, micro); - printf("*** libxml is always available from ftp://ftp.xmlsoft.org.\n"); - printf("***\n"); - printf("*** If you have already installed a sufficiently new version, this error\n"); - printf("*** probably means that the wrong copy of the xml2-config shell script is\n"); - printf("*** being found. The easiest way to fix this is to remove the old version\n"); - printf("*** of LIBXML, but you can also set the XML2_CONFIG environment to point to the\n"); - printf("*** correct copy of xml2-config. (In this case, you will have to\n"); - printf("*** modify your LD_LIBRARY_PATH enviroment variable, or edit /etc/ld.so.conf\n"); - printf("*** so that the correct libraries are found at run-time))\n"); - } - return 1; -} -],, no_xml=yes,[echo $ac_n "cross compiling; assumed OK... $ac_c"]) - CPPFLAGS="$ac_save_CPPFLAGS" - LIBS="$ac_save_LIBS" - fi - fi - - if test "x$no_xml" = x ; then - AC_MSG_RESULT(yes (version $xml_config_major_version.$xml_config_minor_version.$xml_config_micro_version)) - ifelse([$2], , :, [$2]) - else - AC_MSG_RESULT(no) - if test "$XML2_CONFIG" = "no" ; then - echo "*** The xml2-config script installed by LIBXML could not be found" - echo "*** If libxml was installed in PREFIX, make sure PREFIX/bin is in" - echo "*** your path, or set the XML2_CONFIG environment variable to the" - echo "*** full path to xml2-config." - else - if test -f conf.xmltest ; then - : - else - echo "*** Could not run libxml test program, checking why..." - CPPFLAGS="$CPPFLAGS $XML_CPPFLAGS" - LIBS="$LIBS $XML_LIBS" - AC_TRY_LINK([ -#include -#include -], [ LIBXML_TEST_VERSION; return 0;], - [ echo "*** The test program compiled, but did not run. This usually means" - echo "*** that the run-time linker is not finding LIBXML or finding the wrong" - echo "*** version of LIBXML. If it is not finding LIBXML, you'll need to set your" - echo "*** LD_LIBRARY_PATH environment variable, or edit /etc/ld.so.conf to point" - echo "*** to the installed location Also, make sure you have run ldconfig if that" - echo "*** is required on your system" - echo "***" - echo "*** If you have an old version installed, it is best to remove it, although" - echo "*** you may also be able to get things to work by modifying LD_LIBRARY_PATH" ], - [ echo "*** The test program failed to compile or link. See the file config.log for the" - echo "*** exact error that occured. This usually means LIBXML was incorrectly installed" - echo "*** or that you have moved LIBXML since it was installed. In the latter case, you" - echo "*** may want to edit the xml2-config script: $XML2_CONFIG" ]) - CPPFLAGS="$ac_save_CPPFLAGS" - LIBS="$ac_save_LIBS" - fi - fi - - XML_CPPFLAGS="" - XML_LIBS="" - ifelse([$3], , :, [$3]) - fi - AC_SUBST(XML_CPPFLAGS) - AC_SUBST(XML_LIBS) - rm -f conf.xmltest -]) diff --git a/m4/ltoptions.m4 b/m4/ltoptions.m4 deleted file mode 100644 index 5d9acd8..0000000 --- a/m4/ltoptions.m4 +++ /dev/null @@ -1,384 +0,0 @@ -# Helper functions for option handling. -*- Autoconf -*- -# -# Copyright (C) 2004, 2005, 2007, 2008, 2009 Free Software Foundation, -# Inc. -# Written by Gary V. Vaughan, 2004 -# -# This file is free software; the Free Software Foundation gives -# unlimited permission to copy and/or distribute it, with or without -# modifications, as long as this notice is preserved. - -# serial 7 ltoptions.m4 - -# This is to help aclocal find these macros, as it can't see m4_define. -AC_DEFUN([LTOPTIONS_VERSION], [m4_if([1])]) - - -# _LT_MANGLE_OPTION(MACRO-NAME, OPTION-NAME) -# ------------------------------------------ -m4_define([_LT_MANGLE_OPTION], -[[_LT_OPTION_]m4_bpatsubst($1__$2, [[^a-zA-Z0-9_]], [_])]) - - -# _LT_SET_OPTION(MACRO-NAME, OPTION-NAME) -# --------------------------------------- -# Set option OPTION-NAME for macro MACRO-NAME, and if there is a -# matching handler defined, dispatch to it. Other OPTION-NAMEs are -# saved as a flag. -m4_define([_LT_SET_OPTION], -[m4_define(_LT_MANGLE_OPTION([$1], [$2]))dnl -m4_ifdef(_LT_MANGLE_DEFUN([$1], [$2]), - _LT_MANGLE_DEFUN([$1], [$2]), - [m4_warning([Unknown $1 option `$2'])])[]dnl -]) - - -# _LT_IF_OPTION(MACRO-NAME, OPTION-NAME, IF-SET, [IF-NOT-SET]) -# ------------------------------------------------------------ -# Execute IF-SET if OPTION is set, IF-NOT-SET otherwise. -m4_define([_LT_IF_OPTION], -[m4_ifdef(_LT_MANGLE_OPTION([$1], [$2]), [$3], [$4])]) - - -# _LT_UNLESS_OPTIONS(MACRO-NAME, OPTION-LIST, IF-NOT-SET) -# ------------------------------------------------------- -# Execute IF-NOT-SET unless all options in OPTION-LIST for MACRO-NAME -# are set. -m4_define([_LT_UNLESS_OPTIONS], -[m4_foreach([_LT_Option], m4_split(m4_normalize([$2])), - [m4_ifdef(_LT_MANGLE_OPTION([$1], _LT_Option), - [m4_define([$0_found])])])[]dnl -m4_ifdef([$0_found], [m4_undefine([$0_found])], [$3 -])[]dnl -]) - - -# _LT_SET_OPTIONS(MACRO-NAME, OPTION-LIST) -# ---------------------------------------- -# OPTION-LIST is a space-separated list of Libtool options associated -# with MACRO-NAME. If any OPTION has a matching handler declared with -# LT_OPTION_DEFINE, dispatch to that macro; otherwise complain about -# the unknown option and exit. -m4_defun([_LT_SET_OPTIONS], -[# Set options -m4_foreach([_LT_Option], m4_split(m4_normalize([$2])), - [_LT_SET_OPTION([$1], _LT_Option)]) - -m4_if([$1],[LT_INIT],[ - dnl - dnl Simply set some default values (i.e off) if boolean options were not - dnl specified: - _LT_UNLESS_OPTIONS([LT_INIT], [dlopen], [enable_dlopen=no - ]) - _LT_UNLESS_OPTIONS([LT_INIT], [win32-dll], [enable_win32_dll=no - ]) - dnl - dnl If no reference was made to various pairs of opposing options, then - dnl we run the default mode handler for the pair. For example, if neither - dnl `shared' nor `disable-shared' was passed, we enable building of shared - dnl archives by default: - _LT_UNLESS_OPTIONS([LT_INIT], [shared disable-shared], [_LT_ENABLE_SHARED]) - _LT_UNLESS_OPTIONS([LT_INIT], [static disable-static], [_LT_ENABLE_STATIC]) - _LT_UNLESS_OPTIONS([LT_INIT], [pic-only no-pic], [_LT_WITH_PIC]) - _LT_UNLESS_OPTIONS([LT_INIT], [fast-install disable-fast-install], - [_LT_ENABLE_FAST_INSTALL]) - ]) -])# _LT_SET_OPTIONS - - -## --------------------------------- ## -## Macros to handle LT_INIT options. ## -## --------------------------------- ## - -# _LT_MANGLE_DEFUN(MACRO-NAME, OPTION-NAME) -# ----------------------------------------- -m4_define([_LT_MANGLE_DEFUN], -[[_LT_OPTION_DEFUN_]m4_bpatsubst(m4_toupper([$1__$2]), [[^A-Z0-9_]], [_])]) - - -# LT_OPTION_DEFINE(MACRO-NAME, OPTION-NAME, CODE) -# ----------------------------------------------- -m4_define([LT_OPTION_DEFINE], -[m4_define(_LT_MANGLE_DEFUN([$1], [$2]), [$3])[]dnl -])# LT_OPTION_DEFINE - - -# dlopen -# ------ -LT_OPTION_DEFINE([LT_INIT], [dlopen], [enable_dlopen=yes -]) - -AU_DEFUN([AC_LIBTOOL_DLOPEN], -[_LT_SET_OPTION([LT_INIT], [dlopen]) -AC_DIAGNOSE([obsolete], -[$0: Remove this warning and the call to _LT_SET_OPTION when you -put the `dlopen' option into LT_INIT's first parameter.]) -]) - -dnl aclocal-1.4 backwards compatibility: -dnl AC_DEFUN([AC_LIBTOOL_DLOPEN], []) - - -# win32-dll -# --------- -# Declare package support for building win32 dll's. -LT_OPTION_DEFINE([LT_INIT], [win32-dll], -[enable_win32_dll=yes - -case $host in -*-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-cegcc*) - AC_CHECK_TOOL(AS, as, false) - AC_CHECK_TOOL(DLLTOOL, dlltool, false) - AC_CHECK_TOOL(OBJDUMP, objdump, false) - ;; -esac - -test -z "$AS" && AS=as -_LT_DECL([], [AS], [1], [Assembler program])dnl - -test -z "$DLLTOOL" && DLLTOOL=dlltool -_LT_DECL([], [DLLTOOL], [1], [DLL creation program])dnl - -test -z "$OBJDUMP" && OBJDUMP=objdump -_LT_DECL([], [OBJDUMP], [1], [Object dumper program])dnl -])# win32-dll - -AU_DEFUN([AC_LIBTOOL_WIN32_DLL], -[AC_REQUIRE([AC_CANONICAL_HOST])dnl -_LT_SET_OPTION([LT_INIT], [win32-dll]) -AC_DIAGNOSE([obsolete], -[$0: Remove this warning and the call to _LT_SET_OPTION when you -put the `win32-dll' option into LT_INIT's first parameter.]) -]) - -dnl aclocal-1.4 backwards compatibility: -dnl AC_DEFUN([AC_LIBTOOL_WIN32_DLL], []) - - -# _LT_ENABLE_SHARED([DEFAULT]) -# ---------------------------- -# implement the --enable-shared flag, and supports the `shared' and -# `disable-shared' LT_INIT options. -# DEFAULT is either `yes' or `no'. If omitted, it defaults to `yes'. -m4_define([_LT_ENABLE_SHARED], -[m4_define([_LT_ENABLE_SHARED_DEFAULT], [m4_if($1, no, no, yes)])dnl -AC_ARG_ENABLE([shared], - [AS_HELP_STRING([--enable-shared@<:@=PKGS@:>@], - [build shared libraries @<:@default=]_LT_ENABLE_SHARED_DEFAULT[@:>@])], - [p=${PACKAGE-default} - case $enableval in - yes) enable_shared=yes ;; - no) enable_shared=no ;; - *) - enable_shared=no - # Look at the argument we got. We use all the common list separators. - lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR," - for pkg in $enableval; do - IFS="$lt_save_ifs" - if test "X$pkg" = "X$p"; then - enable_shared=yes - fi - done - IFS="$lt_save_ifs" - ;; - esac], - [enable_shared=]_LT_ENABLE_SHARED_DEFAULT) - - _LT_DECL([build_libtool_libs], [enable_shared], [0], - [Whether or not to build shared libraries]) -])# _LT_ENABLE_SHARED - -LT_OPTION_DEFINE([LT_INIT], [shared], [_LT_ENABLE_SHARED([yes])]) -LT_OPTION_DEFINE([LT_INIT], [disable-shared], [_LT_ENABLE_SHARED([no])]) - -# Old names: -AC_DEFUN([AC_ENABLE_SHARED], -[_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[shared]) -]) - -AC_DEFUN([AC_DISABLE_SHARED], -[_LT_SET_OPTION([LT_INIT], [disable-shared]) -]) - -AU_DEFUN([AM_ENABLE_SHARED], [AC_ENABLE_SHARED($@)]) -AU_DEFUN([AM_DISABLE_SHARED], [AC_DISABLE_SHARED($@)]) - -dnl aclocal-1.4 backwards compatibility: -dnl AC_DEFUN([AM_ENABLE_SHARED], []) -dnl AC_DEFUN([AM_DISABLE_SHARED], []) - - - -# _LT_ENABLE_STATIC([DEFAULT]) -# ---------------------------- -# implement the --enable-static flag, and support the `static' and -# `disable-static' LT_INIT options. -# DEFAULT is either `yes' or `no'. If omitted, it defaults to `yes'. -m4_define([_LT_ENABLE_STATIC], -[m4_define([_LT_ENABLE_STATIC_DEFAULT], [m4_if($1, no, no, yes)])dnl -AC_ARG_ENABLE([static], - [AS_HELP_STRING([--enable-static@<:@=PKGS@:>@], - [build static libraries @<:@default=]_LT_ENABLE_STATIC_DEFAULT[@:>@])], - [p=${PACKAGE-default} - case $enableval in - yes) enable_static=yes ;; - no) enable_static=no ;; - *) - enable_static=no - # Look at the argument we got. We use all the common list separators. - lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR," - for pkg in $enableval; do - IFS="$lt_save_ifs" - if test "X$pkg" = "X$p"; then - enable_static=yes - fi - done - IFS="$lt_save_ifs" - ;; - esac], - [enable_static=]_LT_ENABLE_STATIC_DEFAULT) - - _LT_DECL([build_old_libs], [enable_static], [0], - [Whether or not to build static libraries]) -])# _LT_ENABLE_STATIC - -LT_OPTION_DEFINE([LT_INIT], [static], [_LT_ENABLE_STATIC([yes])]) -LT_OPTION_DEFINE([LT_INIT], [disable-static], [_LT_ENABLE_STATIC([no])]) - -# Old names: -AC_DEFUN([AC_ENABLE_STATIC], -[_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[static]) -]) - -AC_DEFUN([AC_DISABLE_STATIC], -[_LT_SET_OPTION([LT_INIT], [disable-static]) -]) - -AU_DEFUN([AM_ENABLE_STATIC], [AC_ENABLE_STATIC($@)]) -AU_DEFUN([AM_DISABLE_STATIC], [AC_DISABLE_STATIC($@)]) - -dnl aclocal-1.4 backwards compatibility: -dnl AC_DEFUN([AM_ENABLE_STATIC], []) -dnl AC_DEFUN([AM_DISABLE_STATIC], []) - - - -# _LT_ENABLE_FAST_INSTALL([DEFAULT]) -# ---------------------------------- -# implement the --enable-fast-install flag, and support the `fast-install' -# and `disable-fast-install' LT_INIT options. -# DEFAULT is either `yes' or `no'. If omitted, it defaults to `yes'. -m4_define([_LT_ENABLE_FAST_INSTALL], -[m4_define([_LT_ENABLE_FAST_INSTALL_DEFAULT], [m4_if($1, no, no, yes)])dnl -AC_ARG_ENABLE([fast-install], - [AS_HELP_STRING([--enable-fast-install@<:@=PKGS@:>@], - [optimize for fast installation @<:@default=]_LT_ENABLE_FAST_INSTALL_DEFAULT[@:>@])], - [p=${PACKAGE-default} - case $enableval in - yes) enable_fast_install=yes ;; - no) enable_fast_install=no ;; - *) - enable_fast_install=no - # Look at the argument we got. We use all the common list separators. - lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR," - for pkg in $enableval; do - IFS="$lt_save_ifs" - if test "X$pkg" = "X$p"; then - enable_fast_install=yes - fi - done - IFS="$lt_save_ifs" - ;; - esac], - [enable_fast_install=]_LT_ENABLE_FAST_INSTALL_DEFAULT) - -_LT_DECL([fast_install], [enable_fast_install], [0], - [Whether or not to optimize for fast installation])dnl -])# _LT_ENABLE_FAST_INSTALL - -LT_OPTION_DEFINE([LT_INIT], [fast-install], [_LT_ENABLE_FAST_INSTALL([yes])]) -LT_OPTION_DEFINE([LT_INIT], [disable-fast-install], [_LT_ENABLE_FAST_INSTALL([no])]) - -# Old names: -AU_DEFUN([AC_ENABLE_FAST_INSTALL], -[_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[fast-install]) -AC_DIAGNOSE([obsolete], -[$0: Remove this warning and the call to _LT_SET_OPTION when you put -the `fast-install' option into LT_INIT's first parameter.]) -]) - -AU_DEFUN([AC_DISABLE_FAST_INSTALL], -[_LT_SET_OPTION([LT_INIT], [disable-fast-install]) -AC_DIAGNOSE([obsolete], -[$0: Remove this warning and the call to _LT_SET_OPTION when you put -the `disable-fast-install' option into LT_INIT's first parameter.]) -]) - -dnl aclocal-1.4 backwards compatibility: -dnl AC_DEFUN([AC_ENABLE_FAST_INSTALL], []) -dnl AC_DEFUN([AM_DISABLE_FAST_INSTALL], []) - - -# _LT_WITH_PIC([MODE]) -# -------------------- -# implement the --with-pic flag, and support the `pic-only' and `no-pic' -# LT_INIT options. -# MODE is either `yes' or `no'. If omitted, it defaults to `both'. -m4_define([_LT_WITH_PIC], -[AC_ARG_WITH([pic], - [AS_HELP_STRING([--with-pic@<:@=PKGS@:>@], - [try to use only PIC/non-PIC objects @<:@default=use both@:>@])], - [lt_p=${PACKAGE-default} - case $withval in - yes|no) pic_mode=$withval ;; - *) - pic_mode=default - # Look at the argument we got. We use all the common list separators. - lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR," - for lt_pkg in $withval; do - IFS="$lt_save_ifs" - if test "X$lt_pkg" = "X$lt_p"; then - pic_mode=yes - fi - done - IFS="$lt_save_ifs" - ;; - esac], - [pic_mode=default]) - -test -z "$pic_mode" && pic_mode=m4_default([$1], [default]) - -_LT_DECL([], [pic_mode], [0], [What type of objects to build])dnl -])# _LT_WITH_PIC - -LT_OPTION_DEFINE([LT_INIT], [pic-only], [_LT_WITH_PIC([yes])]) -LT_OPTION_DEFINE([LT_INIT], [no-pic], [_LT_WITH_PIC([no])]) - -# Old name: -AU_DEFUN([AC_LIBTOOL_PICMODE], -[_LT_SET_OPTION([LT_INIT], [pic-only]) -AC_DIAGNOSE([obsolete], -[$0: Remove this warning and the call to _LT_SET_OPTION when you -put the `pic-only' option into LT_INIT's first parameter.]) -]) - -dnl aclocal-1.4 backwards compatibility: -dnl AC_DEFUN([AC_LIBTOOL_PICMODE], []) - -## ----------------- ## -## LTDL_INIT Options ## -## ----------------- ## - -m4_define([_LTDL_MODE], []) -LT_OPTION_DEFINE([LTDL_INIT], [nonrecursive], - [m4_define([_LTDL_MODE], [nonrecursive])]) -LT_OPTION_DEFINE([LTDL_INIT], [recursive], - [m4_define([_LTDL_MODE], [recursive])]) -LT_OPTION_DEFINE([LTDL_INIT], [subproject], - [m4_define([_LTDL_MODE], [subproject])]) - -m4_define([_LTDL_TYPE], []) -LT_OPTION_DEFINE([LTDL_INIT], [installable], - [m4_define([_LTDL_TYPE], [installable])]) -LT_OPTION_DEFINE([LTDL_INIT], [convenience], - [m4_define([_LTDL_TYPE], [convenience])]) diff --git a/m4/ltsugar.m4 b/m4/ltsugar.m4 deleted file mode 100644 index 9000a05..0000000 --- a/m4/ltsugar.m4 +++ /dev/null @@ -1,123 +0,0 @@ -# ltsugar.m4 -- libtool m4 base layer. -*-Autoconf-*- -# -# Copyright (C) 2004, 2005, 2007, 2008 Free Software Foundation, Inc. -# Written by Gary V. Vaughan, 2004 -# -# This file is free software; the Free Software Foundation gives -# unlimited permission to copy and/or distribute it, with or without -# modifications, as long as this notice is preserved. - -# serial 6 ltsugar.m4 - -# This is to help aclocal find these macros, as it can't see m4_define. -AC_DEFUN([LTSUGAR_VERSION], [m4_if([0.1])]) - - -# lt_join(SEP, ARG1, [ARG2...]) -# ----------------------------- -# Produce ARG1SEPARG2...SEPARGn, omitting [] arguments and their -# associated separator. -# Needed until we can rely on m4_join from Autoconf 2.62, since all earlier -# versions in m4sugar had bugs. -m4_define([lt_join], -[m4_if([$#], [1], [], - [$#], [2], [[$2]], - [m4_if([$2], [], [], [[$2]_])$0([$1], m4_shift(m4_shift($@)))])]) -m4_define([_lt_join], -[m4_if([$#$2], [2], [], - [m4_if([$2], [], [], [[$1$2]])$0([$1], m4_shift(m4_shift($@)))])]) - - -# lt_car(LIST) -# lt_cdr(LIST) -# ------------ -# Manipulate m4 lists. -# These macros are necessary as long as will still need to support -# Autoconf-2.59 which quotes differently. -m4_define([lt_car], [[$1]]) -m4_define([lt_cdr], -[m4_if([$#], 0, [m4_fatal([$0: cannot be called without arguments])], - [$#], 1, [], - [m4_dquote(m4_shift($@))])]) -m4_define([lt_unquote], $1) - - -# lt_append(MACRO-NAME, STRING, [SEPARATOR]) -# ------------------------------------------ -# Redefine MACRO-NAME to hold its former content plus `SEPARATOR'`STRING'. -# Note that neither SEPARATOR nor STRING are expanded; they are appended -# to MACRO-NAME as is (leaving the expansion for when MACRO-NAME is invoked). -# No SEPARATOR is output if MACRO-NAME was previously undefined (different -# than defined and empty). -# -# This macro is needed until we can rely on Autoconf 2.62, since earlier -# versions of m4sugar mistakenly expanded SEPARATOR but not STRING. -m4_define([lt_append], -[m4_define([$1], - m4_ifdef([$1], [m4_defn([$1])[$3]])[$2])]) - - - -# lt_combine(SEP, PREFIX-LIST, INFIX, SUFFIX1, [SUFFIX2...]) -# ---------------------------------------------------------- -# Produce a SEP delimited list of all paired combinations of elements of -# PREFIX-LIST with SUFFIX1 through SUFFIXn. Each element of the list -# has the form PREFIXmINFIXSUFFIXn. -# Needed until we can rely on m4_combine added in Autoconf 2.62. -m4_define([lt_combine], -[m4_if(m4_eval([$# > 3]), [1], - [m4_pushdef([_Lt_sep], [m4_define([_Lt_sep], m4_defn([lt_car]))])]]dnl -[[m4_foreach([_Lt_prefix], [$2], - [m4_foreach([_Lt_suffix], - ]m4_dquote(m4_dquote(m4_shift(m4_shift(m4_shift($@)))))[, - [_Lt_sep([$1])[]m4_defn([_Lt_prefix])[$3]m4_defn([_Lt_suffix])])])])]) - - -# lt_if_append_uniq(MACRO-NAME, VARNAME, [SEPARATOR], [UNIQ], [NOT-UNIQ]) -# ----------------------------------------------------------------------- -# Iff MACRO-NAME does not yet contain VARNAME, then append it (delimited -# by SEPARATOR if supplied) and expand UNIQ, else NOT-UNIQ. -m4_define([lt_if_append_uniq], -[m4_ifdef([$1], - [m4_if(m4_index([$3]m4_defn([$1])[$3], [$3$2$3]), [-1], - [lt_append([$1], [$2], [$3])$4], - [$5])], - [lt_append([$1], [$2], [$3])$4])]) - - -# lt_dict_add(DICT, KEY, VALUE) -# ----------------------------- -m4_define([lt_dict_add], -[m4_define([$1($2)], [$3])]) - - -# lt_dict_add_subkey(DICT, KEY, SUBKEY, VALUE) -# -------------------------------------------- -m4_define([lt_dict_add_subkey], -[m4_define([$1($2:$3)], [$4])]) - - -# lt_dict_fetch(DICT, KEY, [SUBKEY]) -# ---------------------------------- -m4_define([lt_dict_fetch], -[m4_ifval([$3], - m4_ifdef([$1($2:$3)], [m4_defn([$1($2:$3)])]), - m4_ifdef([$1($2)], [m4_defn([$1($2)])]))]) - - -# lt_if_dict_fetch(DICT, KEY, [SUBKEY], VALUE, IF-TRUE, [IF-FALSE]) -# ----------------------------------------------------------------- -m4_define([lt_if_dict_fetch], -[m4_if(lt_dict_fetch([$1], [$2], [$3]), [$4], - [$5], - [$6])]) - - -# lt_dict_filter(DICT, [SUBKEY], VALUE, [SEPARATOR], KEY, [...]) -# -------------------------------------------------------------- -m4_define([lt_dict_filter], -[m4_if([$5], [], [], - [lt_join(m4_quote(m4_default([$4], [[, ]])), - lt_unquote(m4_split(m4_normalize(m4_foreach(_Lt_key, lt_car([m4_shiftn(4, $@)]), - [lt_if_dict_fetch([$1], _Lt_key, [$2], [$3], [_Lt_key ])])))))])[]dnl -]) diff --git a/m4/ltversion.m4 b/m4/ltversion.m4 deleted file mode 100644 index 07a8602..0000000 --- a/m4/ltversion.m4 +++ /dev/null @@ -1,23 +0,0 @@ -# ltversion.m4 -- version numbers -*- Autoconf -*- -# -# Copyright (C) 2004 Free Software Foundation, Inc. -# Written by Scott James Remnant, 2004 -# -# This file is free software; the Free Software Foundation gives -# unlimited permission to copy and/or distribute it, with or without -# modifications, as long as this notice is preserved. - -# @configure_input@ - -# serial 3337 ltversion.m4 -# This file is part of GNU Libtool - -m4_define([LT_PACKAGE_VERSION], [2.4.2]) -m4_define([LT_PACKAGE_REVISION], [1.3337]) - -AC_DEFUN([LTVERSION_VERSION], -[macro_version='2.4.2' -macro_revision='1.3337' -_LT_DECL(, macro_version, 0, [Which release of libtool.m4 was used?]) -_LT_DECL(, macro_revision, 0) -]) diff --git a/m4/lt~obsolete.m4 b/m4/lt~obsolete.m4 deleted file mode 100644 index c573da9..0000000 --- a/m4/lt~obsolete.m4 +++ /dev/null @@ -1,98 +0,0 @@ -# lt~obsolete.m4 -- aclocal satisfying obsolete definitions. -*-Autoconf-*- -# -# Copyright (C) 2004, 2005, 2007, 2009 Free Software Foundation, Inc. -# Written by Scott James Remnant, 2004. -# -# This file is free software; the Free Software Foundation gives -# unlimited permission to copy and/or distribute it, with or without -# modifications, as long as this notice is preserved. - -# serial 5 lt~obsolete.m4 - -# These exist entirely to fool aclocal when bootstrapping libtool. -# -# In the past libtool.m4 has provided macros via AC_DEFUN (or AU_DEFUN) -# which have later been changed to m4_define as they aren't part of the -# exported API, or moved to Autoconf or Automake where they belong. -# -# The trouble is, aclocal is a bit thick. It'll see the old AC_DEFUN -# in /usr/share/aclocal/libtool.m4 and remember it, then when it sees us -# using a macro with the same name in our local m4/libtool.m4 it'll -# pull the old libtool.m4 in (it doesn't see our shiny new m4_define -# and doesn't know about Autoconf macros at all.) -# -# So we provide this file, which has a silly filename so it's always -# included after everything else. This provides aclocal with the -# AC_DEFUNs it wants, but when m4 processes it, it doesn't do anything -# because those macros already exist, or will be overwritten later. -# We use AC_DEFUN over AU_DEFUN for compatibility with aclocal-1.6. -# -# Anytime we withdraw an AC_DEFUN or AU_DEFUN, remember to add it here. -# Yes, that means every name once taken will need to remain here until -# we give up compatibility with versions before 1.7, at which point -# we need to keep only those names which we still refer to. - -# This is to help aclocal find these macros, as it can't see m4_define. -AC_DEFUN([LTOBSOLETE_VERSION], [m4_if([1])]) - -m4_ifndef([AC_LIBTOOL_LINKER_OPTION], [AC_DEFUN([AC_LIBTOOL_LINKER_OPTION])]) -m4_ifndef([AC_PROG_EGREP], [AC_DEFUN([AC_PROG_EGREP])]) -m4_ifndef([_LT_AC_PROG_ECHO_BACKSLASH], [AC_DEFUN([_LT_AC_PROG_ECHO_BACKSLASH])]) -m4_ifndef([_LT_AC_SHELL_INIT], [AC_DEFUN([_LT_AC_SHELL_INIT])]) -m4_ifndef([_LT_AC_SYS_LIBPATH_AIX], [AC_DEFUN([_LT_AC_SYS_LIBPATH_AIX])]) -m4_ifndef([_LT_PROG_LTMAIN], [AC_DEFUN([_LT_PROG_LTMAIN])]) -m4_ifndef([_LT_AC_TAGVAR], [AC_DEFUN([_LT_AC_TAGVAR])]) -m4_ifndef([AC_LTDL_ENABLE_INSTALL], [AC_DEFUN([AC_LTDL_ENABLE_INSTALL])]) -m4_ifndef([AC_LTDL_PREOPEN], [AC_DEFUN([AC_LTDL_PREOPEN])]) -m4_ifndef([_LT_AC_SYS_COMPILER], [AC_DEFUN([_LT_AC_SYS_COMPILER])]) -m4_ifndef([_LT_AC_LOCK], [AC_DEFUN([_LT_AC_LOCK])]) -m4_ifndef([AC_LIBTOOL_SYS_OLD_ARCHIVE], [AC_DEFUN([AC_LIBTOOL_SYS_OLD_ARCHIVE])]) -m4_ifndef([_LT_AC_TRY_DLOPEN_SELF], [AC_DEFUN([_LT_AC_TRY_DLOPEN_SELF])]) -m4_ifndef([AC_LIBTOOL_PROG_CC_C_O], [AC_DEFUN([AC_LIBTOOL_PROG_CC_C_O])]) -m4_ifndef([AC_LIBTOOL_SYS_HARD_LINK_LOCKS], [AC_DEFUN([AC_LIBTOOL_SYS_HARD_LINK_LOCKS])]) -m4_ifndef([AC_LIBTOOL_OBJDIR], [AC_DEFUN([AC_LIBTOOL_OBJDIR])]) -m4_ifndef([AC_LTDL_OBJDIR], [AC_DEFUN([AC_LTDL_OBJDIR])]) -m4_ifndef([AC_LIBTOOL_PROG_LD_HARDCODE_LIBPATH], [AC_DEFUN([AC_LIBTOOL_PROG_LD_HARDCODE_LIBPATH])]) -m4_ifndef([AC_LIBTOOL_SYS_LIB_STRIP], [AC_DEFUN([AC_LIBTOOL_SYS_LIB_STRIP])]) -m4_ifndef([AC_PATH_MAGIC], [AC_DEFUN([AC_PATH_MAGIC])]) -m4_ifndef([AC_PROG_LD_GNU], [AC_DEFUN([AC_PROG_LD_GNU])]) -m4_ifndef([AC_PROG_LD_RELOAD_FLAG], [AC_DEFUN([AC_PROG_LD_RELOAD_FLAG])]) -m4_ifndef([AC_DEPLIBS_CHECK_METHOD], [AC_DEFUN([AC_DEPLIBS_CHECK_METHOD])]) -m4_ifndef([AC_LIBTOOL_PROG_COMPILER_NO_RTTI], [AC_DEFUN([AC_LIBTOOL_PROG_COMPILER_NO_RTTI])]) -m4_ifndef([AC_LIBTOOL_SYS_GLOBAL_SYMBOL_PIPE], [AC_DEFUN([AC_LIBTOOL_SYS_GLOBAL_SYMBOL_PIPE])]) -m4_ifndef([AC_LIBTOOL_PROG_COMPILER_PIC], [AC_DEFUN([AC_LIBTOOL_PROG_COMPILER_PIC])]) -m4_ifndef([AC_LIBTOOL_PROG_LD_SHLIBS], [AC_DEFUN([AC_LIBTOOL_PROG_LD_SHLIBS])]) -m4_ifndef([AC_LIBTOOL_POSTDEP_PREDEP], [AC_DEFUN([AC_LIBTOOL_POSTDEP_PREDEP])]) -m4_ifndef([LT_AC_PROG_EGREP], [AC_DEFUN([LT_AC_PROG_EGREP])]) -m4_ifndef([LT_AC_PROG_SED], [AC_DEFUN([LT_AC_PROG_SED])]) -m4_ifndef([_LT_CC_BASENAME], [AC_DEFUN([_LT_CC_BASENAME])]) -m4_ifndef([_LT_COMPILER_BOILERPLATE], [AC_DEFUN([_LT_COMPILER_BOILERPLATE])]) -m4_ifndef([_LT_LINKER_BOILERPLATE], [AC_DEFUN([_LT_LINKER_BOILERPLATE])]) -m4_ifndef([_AC_PROG_LIBTOOL], [AC_DEFUN([_AC_PROG_LIBTOOL])]) -m4_ifndef([AC_LIBTOOL_SETUP], [AC_DEFUN([AC_LIBTOOL_SETUP])]) -m4_ifndef([_LT_AC_CHECK_DLFCN], [AC_DEFUN([_LT_AC_CHECK_DLFCN])]) -m4_ifndef([AC_LIBTOOL_SYS_DYNAMIC_LINKER], [AC_DEFUN([AC_LIBTOOL_SYS_DYNAMIC_LINKER])]) -m4_ifndef([_LT_AC_TAGCONFIG], [AC_DEFUN([_LT_AC_TAGCONFIG])]) -m4_ifndef([AC_DISABLE_FAST_INSTALL], [AC_DEFUN([AC_DISABLE_FAST_INSTALL])]) -m4_ifndef([_LT_AC_LANG_CXX], [AC_DEFUN([_LT_AC_LANG_CXX])]) -m4_ifndef([_LT_AC_LANG_F77], [AC_DEFUN([_LT_AC_LANG_F77])]) -m4_ifndef([_LT_AC_LANG_GCJ], [AC_DEFUN([_LT_AC_LANG_GCJ])]) -m4_ifndef([AC_LIBTOOL_LANG_C_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_C_CONFIG])]) -m4_ifndef([_LT_AC_LANG_C_CONFIG], [AC_DEFUN([_LT_AC_LANG_C_CONFIG])]) -m4_ifndef([AC_LIBTOOL_LANG_CXX_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_CXX_CONFIG])]) -m4_ifndef([_LT_AC_LANG_CXX_CONFIG], [AC_DEFUN([_LT_AC_LANG_CXX_CONFIG])]) -m4_ifndef([AC_LIBTOOL_LANG_F77_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_F77_CONFIG])]) -m4_ifndef([_LT_AC_LANG_F77_CONFIG], [AC_DEFUN([_LT_AC_LANG_F77_CONFIG])]) -m4_ifndef([AC_LIBTOOL_LANG_GCJ_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_GCJ_CONFIG])]) -m4_ifndef([_LT_AC_LANG_GCJ_CONFIG], [AC_DEFUN([_LT_AC_LANG_GCJ_CONFIG])]) -m4_ifndef([AC_LIBTOOL_LANG_RC_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_RC_CONFIG])]) -m4_ifndef([_LT_AC_LANG_RC_CONFIG], [AC_DEFUN([_LT_AC_LANG_RC_CONFIG])]) -m4_ifndef([AC_LIBTOOL_CONFIG], [AC_DEFUN([AC_LIBTOOL_CONFIG])]) -m4_ifndef([_LT_AC_FILE_LTDLL_C], [AC_DEFUN([_LT_AC_FILE_LTDLL_C])]) -m4_ifndef([_LT_REQUIRED_DARWIN_CHECKS], [AC_DEFUN([_LT_REQUIRED_DARWIN_CHECKS])]) -m4_ifndef([_LT_AC_PROG_CXXCPP], [AC_DEFUN([_LT_AC_PROG_CXXCPP])]) -m4_ifndef([_LT_PREPARE_SED_QUOTE_VARS], [AC_DEFUN([_LT_PREPARE_SED_QUOTE_VARS])]) -m4_ifndef([_LT_PROG_ECHO_BACKSLASH], [AC_DEFUN([_LT_PROG_ECHO_BACKSLASH])]) -m4_ifndef([_LT_PROG_F77], [AC_DEFUN([_LT_PROG_F77])]) -m4_ifndef([_LT_PROG_FC], [AC_DEFUN([_LT_PROG_FC])]) -m4_ifndef([_LT_PROG_CXX], [AC_DEFUN([_LT_PROG_CXX])]) diff --git a/missing b/missing deleted file mode 100755 index db98974..0000000 --- a/missing +++ /dev/null @@ -1,215 +0,0 @@ -#! /bin/sh -# Common wrapper for a few potentially missing GNU programs. - -scriptversion=2013-10-28.13; # UTC - -# Copyright (C) 1996-2013 Free Software Foundation, Inc. -# Originally written by Fran,cois Pinard , 1996. - -# 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 2, 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 . - -# As a special exception to the GNU General Public License, if you -# distribute this file as part of a program that contains a -# configuration script generated by Autoconf, you may include it under -# the same distribution terms that you use for the rest of that program. - -if test $# -eq 0; then - echo 1>&2 "Try '$0 --help' for more information" - exit 1 -fi - -case $1 in - - --is-lightweight) - # Used by our autoconf macros to check whether the available missing - # script is modern enough. - exit 0 - ;; - - --run) - # Back-compat with the calling convention used by older automake. - shift - ;; - - -h|--h|--he|--hel|--help) - echo "\ -$0 [OPTION]... PROGRAM [ARGUMENT]... - -Run 'PROGRAM [ARGUMENT]...', returning a proper advice when this fails due -to PROGRAM being missing or too old. - -Options: - -h, --help display this help and exit - -v, --version output version information and exit - -Supported PROGRAM values: - aclocal autoconf autoheader autom4te automake makeinfo - bison yacc flex lex help2man - -Version suffixes to PROGRAM as well as the prefixes 'gnu-', 'gnu', and -'g' are ignored when checking the name. - -Send bug reports to ." - exit $? - ;; - - -v|--v|--ve|--ver|--vers|--versi|--versio|--version) - echo "missing $scriptversion (GNU Automake)" - exit $? - ;; - - -*) - echo 1>&2 "$0: unknown '$1' option" - echo 1>&2 "Try '$0 --help' for more information" - exit 1 - ;; - -esac - -# Run the given program, remember its exit status. -"$@"; st=$? - -# If it succeeded, we are done. -test $st -eq 0 && exit 0 - -# Also exit now if we it failed (or wasn't found), and '--version' was -# passed; such an option is passed most likely to detect whether the -# program is present and works. -case $2 in --version|--help) exit $st;; esac - -# Exit code 63 means version mismatch. This often happens when the user -# tries to use an ancient version of a tool on a file that requires a -# minimum version. -if test $st -eq 63; then - msg="probably too old" -elif test $st -eq 127; then - # Program was missing. - msg="missing on your system" -else - # Program was found and executed, but failed. Give up. - exit $st -fi - -perl_URL=http://www.perl.org/ -flex_URL=http://flex.sourceforge.net/ -gnu_software_URL=http://www.gnu.org/software - -program_details () -{ - case $1 in - aclocal|automake) - echo "The '$1' program is part of the GNU Automake package:" - echo "<$gnu_software_URL/automake>" - echo "It also requires GNU Autoconf, GNU m4 and Perl in order to run:" - echo "<$gnu_software_URL/autoconf>" - echo "<$gnu_software_URL/m4/>" - echo "<$perl_URL>" - ;; - autoconf|autom4te|autoheader) - echo "The '$1' program is part of the GNU Autoconf package:" - echo "<$gnu_software_URL/autoconf/>" - echo "It also requires GNU m4 and Perl in order to run:" - echo "<$gnu_software_URL/m4/>" - echo "<$perl_URL>" - ;; - esac -} - -give_advice () -{ - # Normalize program name to check for. - normalized_program=`echo "$1" | sed ' - s/^gnu-//; t - s/^gnu//; t - s/^g//; t'` - - printf '%s\n' "'$1' is $msg." - - configure_deps="'configure.ac' or m4 files included by 'configure.ac'" - case $normalized_program in - autoconf*) - echo "You should only need it if you modified 'configure.ac'," - echo "or m4 files included by it." - program_details 'autoconf' - ;; - autoheader*) - echo "You should only need it if you modified 'acconfig.h' or" - echo "$configure_deps." - program_details 'autoheader' - ;; - automake*) - echo "You should only need it if you modified 'Makefile.am' or" - echo "$configure_deps." - program_details 'automake' - ;; - aclocal*) - echo "You should only need it if you modified 'acinclude.m4' or" - echo "$configure_deps." - program_details 'aclocal' - ;; - autom4te*) - echo "You might have modified some maintainer files that require" - echo "the 'autom4te' program to be rebuilt." - program_details 'autom4te' - ;; - bison*|yacc*) - echo "You should only need it if you modified a '.y' file." - echo "You may want to install the GNU Bison package:" - echo "<$gnu_software_URL/bison/>" - ;; - lex*|flex*) - echo "You should only need it if you modified a '.l' file." - echo "You may want to install the Fast Lexical Analyzer package:" - echo "<$flex_URL>" - ;; - help2man*) - echo "You should only need it if you modified a dependency" \ - "of a man page." - echo "You may want to install the GNU Help2man package:" - echo "<$gnu_software_URL/help2man/>" - ;; - makeinfo*) - echo "You should only need it if you modified a '.texi' file, or" - echo "any other file indirectly affecting the aspect of the manual." - echo "You might want to install the Texinfo package:" - echo "<$gnu_software_URL/texinfo/>" - echo "The spurious makeinfo call might also be the consequence of" - echo "using a buggy 'make' (AIX, DU, IRIX), in which case you might" - echo "want to install GNU make:" - echo "<$gnu_software_URL/make/>" - ;; - *) - echo "You might have modified some files without having the proper" - echo "tools for further handling them. Check the 'README' file, it" - echo "often tells you about the needed prerequisites for installing" - echo "this package. You may also peek at any GNU archive site, in" - echo "case some other package contains this missing '$1' program." - ;; - esac -} - -give_advice "$1" | sed -e '1s/^/WARNING: /' \ - -e '2,$s/^/ /' >&2 - -# Propagate the correct exit status (expected to be 127 for a program -# not found, 63 for a program that failed due to version mismatch). -exit $st - -# Local variables: -# eval: (add-hook 'write-file-hooks 'time-stamp) -# time-stamp-start: "scriptversion=" -# time-stamp-format: "%:y-%02m-%02d.%02H" -# time-stamp-time-zone: "UTC" -# time-stamp-end: "; # UTC" -# End: diff --git a/nghttpx.conf.sample b/nghttpx.conf.sample deleted file mode 100644 index 97d7954..0000000 --- a/nghttpx.conf.sample +++ /dev/null @@ -1,29 +0,0 @@ -# -# Sample configuration file for nghttpx. -# -# * Line staring '#' is treated as comment. -# -# * The option name in the configuration file is the long command-line -# option name with leading '--' stripped (e.g., frontend). Put '=' -# between option name and value. Don't put extra leading or trailing -# spaces. -# -# * The options which do not take argument in the command-line *take* -# argument in the configuration file. Specify 'yes' as argument -# (e.g., http2-proxy=yes). If other string is given, it disables the -# option. -# -# * To specify private key and certificate file, use private-key-file -# and certificate-file. See the examples below. -# -# * conf option cannot be used in the configuration file. It will be -# ignored. -# -# Examples: -# -# frontend=0.0.0.0,3000 -# backend=127.0.0.1,80 -# private-key-file=/path/to/server.key -# certificate-file=/path/to/server.crt -# http2-proxy=no -# workers=1 diff --git a/proxy.pac.sample b/proxy.pac.sample deleted file mode 100644 index 9283920..0000000 --- a/proxy.pac.sample +++ /dev/null @@ -1,6 +0,0 @@ -function FindProxyForURL(url, host) { - // For SPDY proxy - return "HTTPS localhost:3000"; - // For conventional HTTP proxy - // return "PROXY localhost:3000"; -} diff --git a/python/Makefile.am b/python/Makefile.am deleted file mode 100644 index 911888f..0000000 --- a/python/Makefile.am +++ /dev/null @@ -1,48 +0,0 @@ -# nghttp2 - HTTP/2 C Library - -# Copyright (c) 2013 Tatsuhiro Tsujikawa - -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: - -# The above copyright notice and this permission notice shall be -# included in all copies or substantial portions of the Software. - -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -# This will avoid that setup.py gets deleted before it is executed in -# clean-local in parallel build. -.NOTPARALLEL: - -EXTRA_DIST = cnghttp2.pxd nghttp2.pyx - -if ENABLE_PYTHON_BINDINGS - -all-local: nghttp2.c - $(PYTHON) setup.py build - -install-exec-local: - $(PYTHON) setup.py install --prefix=$(DESTDIR)$(prefix) - -uninstall-local: - rm -rf $(DESTDIR)$(libdir)/python*/site-packages/*nghttp2* - -clean-local: - $(PYTHON) setup.py clean --all - -rm -f $(builddir)/nghttp2.c - -.pyx.c: - $(CYTHON) -o $@ $< - -endif # ENABLE_PYTHON_BINDINGS diff --git a/python/Makefile.in b/python/Makefile.in deleted file mode 100644 index ea9c308..0000000 --- a/python/Makefile.in +++ /dev/null @@ -1,541 +0,0 @@ -# Makefile.in generated by automake 1.14.1 from Makefile.am. -# @configure_input@ - -# Copyright (C) 1994-2013 Free Software Foundation, Inc. - -# This Makefile.in is free software; the Free Software Foundation -# gives unlimited permission to copy and/or distribute it, -# with or without modifications, as long as this notice is preserved. - -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY, to the extent permitted by law; without -# even the implied warranty of MERCHANTABILITY or FITNESS FOR A -# PARTICULAR PURPOSE. - -@SET_MAKE@ - -# nghttp2 - HTTP/2 C Library - -# Copyright (c) 2013 Tatsuhiro Tsujikawa - -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: - -# The above copyright notice and this permission notice shall be -# included in all copies or substantial portions of the Software. - -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -VPATH = @srcdir@ -am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)' -am__make_running_with_option = \ - case $${target_option-} in \ - ?) ;; \ - *) echo "am__make_running_with_option: internal error: invalid" \ - "target option '$${target_option-}' specified" >&2; \ - exit 1;; \ - esac; \ - has_opt=no; \ - sane_makeflags=$$MAKEFLAGS; \ - if $(am__is_gnu_make); then \ - sane_makeflags=$$MFLAGS; \ - else \ - case $$MAKEFLAGS in \ - *\\[\ \ ]*) \ - bs=\\; \ - sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ - | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ - esac; \ - fi; \ - skip_next=no; \ - strip_trailopt () \ - { \ - flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ - }; \ - for flg in $$sane_makeflags; do \ - test $$skip_next = yes && { skip_next=no; continue; }; \ - case $$flg in \ - *=*|--*) continue;; \ - -*I) strip_trailopt 'I'; skip_next=yes;; \ - -*I?*) strip_trailopt 'I';; \ - -*O) strip_trailopt 'O'; skip_next=yes;; \ - -*O?*) strip_trailopt 'O';; \ - -*l) strip_trailopt 'l'; skip_next=yes;; \ - -*l?*) strip_trailopt 'l';; \ - -[dEDm]) skip_next=yes;; \ - -[JT]) skip_next=yes;; \ - esac; \ - case $$flg in \ - *$$target_option*) has_opt=yes; break;; \ - esac; \ - done; \ - test $$has_opt = yes -am__make_dryrun = (target_option=n; $(am__make_running_with_option)) -am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) -pkgdatadir = $(datadir)/@PACKAGE@ -pkgincludedir = $(includedir)/@PACKAGE@ -pkglibdir = $(libdir)/@PACKAGE@ -pkglibexecdir = $(libexecdir)/@PACKAGE@ -am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd -install_sh_DATA = $(install_sh) -c -m 644 -install_sh_PROGRAM = $(install_sh) -c -install_sh_SCRIPT = $(install_sh) -c -INSTALL_HEADER = $(INSTALL_DATA) -transform = $(program_transform_name) -NORMAL_INSTALL = : -PRE_INSTALL = : -POST_INSTALL = : -NORMAL_UNINSTALL = : -PRE_UNINSTALL = : -POST_UNINSTALL = : -build_triplet = @build@ -host_triplet = @host@ -target_triplet = @target@ -subdir = python -DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \ - $(srcdir)/setup.py.in -ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 -am__aclocal_m4_deps = $(top_srcdir)/m4/ax_boost_asio.m4 \ - $(top_srcdir)/m4/ax_boost_base.m4 \ - $(top_srcdir)/m4/ax_boost_system.m4 \ - $(top_srcdir)/m4/ax_boost_thread.m4 \ - $(top_srcdir)/m4/ax_check_compile_flag.m4 \ - $(top_srcdir)/m4/ax_cxx_compile_stdcxx_11.m4 \ - $(top_srcdir)/m4/ax_have_epoll.m4 \ - $(top_srcdir)/m4/ax_python_devel.m4 \ - $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/libxml2.m4 \ - $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ - $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ - $(top_srcdir)/configure.ac -am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ - $(ACLOCAL_M4) -mkinstalldirs = $(install_sh) -d -CONFIG_HEADER = $(top_builddir)/config.h -CONFIG_CLEAN_FILES = setup.py -CONFIG_CLEAN_VPATH_FILES = -AM_V_P = $(am__v_P_@AM_V@) -am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) -am__v_P_0 = false -am__v_P_1 = : -AM_V_GEN = $(am__v_GEN_@AM_V@) -am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) -am__v_GEN_0 = @echo " GEN " $@; -am__v_GEN_1 = -AM_V_at = $(am__v_at_@AM_V@) -am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) -am__v_at_0 = @ -am__v_at_1 = -SOURCES = -DIST_SOURCES = -am__can_run_installinfo = \ - case $$AM_UPDATE_INFO_DIR in \ - n|no|NO) false;; \ - *) (install-info --version) >/dev/null 2>&1;; \ - esac -am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) -DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) -ACLOCAL = @ACLOCAL@ -AMTAR = @AMTAR@ -AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ -APPLDFLAGS = @APPLDFLAGS@ -AR = @AR@ -AUTOCONF = @AUTOCONF@ -AUTOHEADER = @AUTOHEADER@ -AUTOMAKE = @AUTOMAKE@ -AWK = @AWK@ -BOOST_ASIO_LIB = @BOOST_ASIO_LIB@ -BOOST_CPPFLAGS = @BOOST_CPPFLAGS@ -BOOST_LDFLAGS = @BOOST_LDFLAGS@ -BOOST_SYSTEM_LIB = @BOOST_SYSTEM_LIB@ -BOOST_THREAD_LIB = @BOOST_THREAD_LIB@ -CC = @CC@ -CCDEPMODE = @CCDEPMODE@ -CFLAGS = @CFLAGS@ -CPP = @CPP@ -CPPFLAGS = @CPPFLAGS@ -CUNIT_CFLAGS = @CUNIT_CFLAGS@ -CUNIT_LIBS = @CUNIT_LIBS@ -CXX = @CXX@ -CXXCPP = @CXXCPP@ -CXXDEPMODE = @CXXDEPMODE@ -CXXFLAGS = @CXXFLAGS@ -CYGPATH_W = @CYGPATH_W@ -CYTHON = @CYTHON@ -DEFS = @DEFS@ -DEPDIR = @DEPDIR@ -DLLTOOL = @DLLTOOL@ -DSYMUTIL = @DSYMUTIL@ -DUMPBIN = @DUMPBIN@ -ECHO_C = @ECHO_C@ -ECHO_N = @ECHO_N@ -ECHO_T = @ECHO_T@ -EGREP = @EGREP@ -EXEEXT = @EXEEXT@ -FGREP = @FGREP@ -GREP = @GREP@ -HAVE_CXX11 = @HAVE_CXX11@ -INSTALL = @INSTALL@ -INSTALL_DATA = @INSTALL_DATA@ -INSTALL_PROGRAM = @INSTALL_PROGRAM@ -INSTALL_SCRIPT = @INSTALL_SCRIPT@ -INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ -JANSSON_CFLAGS = @JANSSON_CFLAGS@ -JANSSON_LIBS = @JANSSON_LIBS@ -JEMALLOC_LIBS = @JEMALLOC_LIBS@ -LD = @LD@ -LDFLAGS = @LDFLAGS@ -LIBEVENT_OPENSSL_CFLAGS = @LIBEVENT_OPENSSL_CFLAGS@ -LIBEVENT_OPENSSL_LIBS = @LIBEVENT_OPENSSL_LIBS@ -LIBEV_CFLAGS = @LIBEV_CFLAGS@ -LIBEV_LIBS = @LIBEV_LIBS@ -LIBOBJS = @LIBOBJS@ -LIBS = @LIBS@ -LIBSPDYLAY_CFLAGS = @LIBSPDYLAY_CFLAGS@ -LIBSPDYLAY_LIBS = @LIBSPDYLAY_LIBS@ -LIBTOOL = @LIBTOOL@ -LIPO = @LIPO@ -LN_S = @LN_S@ -LTLIBOBJS = @LTLIBOBJS@ -LT_AGE = @LT_AGE@ -LT_CURRENT = @LT_CURRENT@ -LT_REVISION = @LT_REVISION@ -MAKEINFO = @MAKEINFO@ -MANIFEST_TOOL = @MANIFEST_TOOL@ -MKDIR_P = @MKDIR_P@ -NM = @NM@ -NMEDIT = @NMEDIT@ -OBJDUMP = @OBJDUMP@ -OBJEXT = @OBJEXT@ -OPENSSL_CFLAGS = @OPENSSL_CFLAGS@ -OPENSSL_LIBS = @OPENSSL_LIBS@ -OTOOL = @OTOOL@ -OTOOL64 = @OTOOL64@ -PACKAGE = @PACKAGE@ -PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ -PACKAGE_NAME = @PACKAGE_NAME@ -PACKAGE_STRING = @PACKAGE_STRING@ -PACKAGE_TARNAME = @PACKAGE_TARNAME@ -PACKAGE_URL = @PACKAGE_URL@ -PACKAGE_VERSION = @PACKAGE_VERSION@ -PACKAGE_VERSION_NUM = @PACKAGE_VERSION_NUM@ -PATH_SEPARATOR = @PATH_SEPARATOR@ -PKG_CONFIG = @PKG_CONFIG@ -PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ -PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ -PYTHON = @PYTHON@ -PYTHON_CPPFLAGS = @PYTHON_CPPFLAGS@ -PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@ -PYTHON_EXTRA_LDFLAGS = @PYTHON_EXTRA_LDFLAGS@ -PYTHON_EXTRA_LIBS = @PYTHON_EXTRA_LIBS@ -PYTHON_LDFLAGS = @PYTHON_LDFLAGS@ -PYTHON_PLATFORM = @PYTHON_PLATFORM@ -PYTHON_PREFIX = @PYTHON_PREFIX@ -PYTHON_SITE_PKG = @PYTHON_SITE_PKG@ -PYTHON_VERSION = @PYTHON_VERSION@ -RANLIB = @RANLIB@ -SED = @SED@ -SET_MAKE = @SET_MAKE@ -SHELL = @SHELL@ -STRIP = @STRIP@ -TESTLDADD = @TESTLDADD@ -VERSION = @VERSION@ -WARNCFLAGS = @WARNCFLAGS@ -XML2_CONFIG = @XML2_CONFIG@ -XML_CPPFLAGS = @XML_CPPFLAGS@ -XML_LIBS = @XML_LIBS@ -ZLIB_CFLAGS = @ZLIB_CFLAGS@ -ZLIB_LIBS = @ZLIB_LIBS@ -abs_builddir = @abs_builddir@ -abs_srcdir = @abs_srcdir@ -abs_top_builddir = @abs_top_builddir@ -abs_top_srcdir = @abs_top_srcdir@ -ac_ct_AR = @ac_ct_AR@ -ac_ct_CC = @ac_ct_CC@ -ac_ct_CXX = @ac_ct_CXX@ -ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ -am__include = @am__include@ -am__leading_dot = @am__leading_dot@ -am__quote = @am__quote@ -am__tar = @am__tar@ -am__untar = @am__untar@ -bindir = @bindir@ -build = @build@ -build_alias = @build_alias@ -build_cpu = @build_cpu@ -build_os = @build_os@ -build_vendor = @build_vendor@ -builddir = @builddir@ -datadir = @datadir@ -datarootdir = @datarootdir@ -docdir = @docdir@ -dvidir = @dvidir@ -exec_prefix = @exec_prefix@ -host = @host@ -host_alias = @host_alias@ -host_cpu = @host_cpu@ -host_os = @host_os@ -host_vendor = @host_vendor@ -htmldir = @htmldir@ -includedir = @includedir@ -infodir = @infodir@ -install_sh = @install_sh@ -libdir = @libdir@ -libexecdir = @libexecdir@ -localedir = @localedir@ -localstatedir = @localstatedir@ -mandir = @mandir@ -mkdir_p = @mkdir_p@ -oldincludedir = @oldincludedir@ -pdfdir = @pdfdir@ -pkgpyexecdir = @pkgpyexecdir@ -pkgpythondir = @pkgpythondir@ -prefix = @prefix@ -program_transform_name = @program_transform_name@ -psdir = @psdir@ -pyexecdir = @pyexecdir@ -pythondir = @pythondir@ -sbindir = @sbindir@ -sharedstatedir = @sharedstatedir@ -srcdir = @srcdir@ -sysconfdir = @sysconfdir@ -target = @target@ -target_alias = @target_alias@ -target_cpu = @target_cpu@ -target_os = @target_os@ -target_vendor = @target_vendor@ -top_build_prefix = @top_build_prefix@ -top_builddir = @top_builddir@ -top_srcdir = @top_srcdir@ -EXTRA_DIST = cnghttp2.pxd nghttp2.pyx -all: all-am - -.SUFFIXES: -.SUFFIXES: .c .pyx -$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) - @for dep in $?; do \ - case '$(am__configure_deps)' in \ - *$$dep*) \ - ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ - && { if test -f $@; then exit 0; else break; fi; }; \ - exit 1;; \ - esac; \ - done; \ - echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu python/Makefile'; \ - $(am__cd) $(top_srcdir) && \ - $(AUTOMAKE) --gnu python/Makefile -.PRECIOUS: Makefile -Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status - @case '$?' in \ - *config.status*) \ - cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ - *) \ - echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ - cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ - esac; - -$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) - cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh - -$(top_srcdir)/configure: $(am__configure_deps) - cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh -$(ACLOCAL_M4): $(am__aclocal_m4_deps) - cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh -$(am__aclocal_m4_deps): -setup.py: $(top_builddir)/config.status $(srcdir)/setup.py.in - cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ - -mostlyclean-libtool: - -rm -f *.lo - -clean-libtool: - -rm -rf .libs _libs -tags TAGS: - -ctags CTAGS: - -cscope cscopelist: - - -distdir: $(DISTFILES) - @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ - topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ - list='$(DISTFILES)'; \ - dist_files=`for file in $$list; do echo $$file; done | \ - sed -e "s|^$$srcdirstrip/||;t" \ - -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ - case $$dist_files in \ - */*) $(MKDIR_P) `echo "$$dist_files" | \ - sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ - sort -u` ;; \ - esac; \ - for file in $$dist_files; do \ - if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ - if test -d $$d/$$file; then \ - dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ - if test -d "$(distdir)/$$file"; then \ - find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ - fi; \ - if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ - cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ - find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ - fi; \ - cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ - else \ - test -f "$(distdir)/$$file" \ - || cp -p $$d/$$file "$(distdir)/$$file" \ - || exit 1; \ - fi; \ - done -check-am: all-am -check: check-am -@ENABLE_PYTHON_BINDINGS_FALSE@all-local: -all-am: Makefile all-local -installdirs: -install: install-am -install-exec: install-exec-am -install-data: install-data-am -uninstall: uninstall-am - -install-am: all-am - @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am - -installcheck: installcheck-am -install-strip: - if test -z '$(STRIP)'; then \ - $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ - install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ - install; \ - else \ - $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ - install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ - "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ - fi -mostlyclean-generic: - -clean-generic: - -distclean-generic: - -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) - -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) - -maintainer-clean-generic: - @echo "This command is intended for maintainers to use" - @echo "it deletes files that may require special tools to rebuild." -@ENABLE_PYTHON_BINDINGS_FALSE@clean-local: -@ENABLE_PYTHON_BINDINGS_FALSE@install-exec-local: -@ENABLE_PYTHON_BINDINGS_FALSE@uninstall-local: -clean: clean-am - -clean-am: clean-generic clean-libtool clean-local mostlyclean-am - -distclean: distclean-am - -rm -f Makefile -distclean-am: clean-am distclean-generic - -dvi: dvi-am - -dvi-am: - -html: html-am - -html-am: - -info: info-am - -info-am: - -install-data-am: - -install-dvi: install-dvi-am - -install-dvi-am: - -install-exec-am: install-exec-local - -install-html: install-html-am - -install-html-am: - -install-info: install-info-am - -install-info-am: - -install-man: - -install-pdf: install-pdf-am - -install-pdf-am: - -install-ps: install-ps-am - -install-ps-am: - -installcheck-am: - -maintainer-clean: maintainer-clean-am - -rm -f Makefile -maintainer-clean-am: distclean-am maintainer-clean-generic - -mostlyclean: mostlyclean-am - -mostlyclean-am: mostlyclean-generic mostlyclean-libtool - -pdf: pdf-am - -pdf-am: - -ps: ps-am - -ps-am: - -uninstall-am: uninstall-local - -.MAKE: install-am install-strip - -.PHONY: all all-am all-local check check-am clean clean-generic \ - clean-libtool clean-local cscopelist-am ctags-am distclean \ - distclean-generic distclean-libtool distdir dvi dvi-am html \ - html-am info info-am install install-am install-data \ - install-data-am install-dvi install-dvi-am install-exec \ - install-exec-am install-exec-local install-html \ - install-html-am install-info install-info-am install-man \ - install-pdf install-pdf-am install-ps install-ps-am \ - install-strip installcheck installcheck-am installdirs \ - maintainer-clean maintainer-clean-generic mostlyclean \ - mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ - tags-am uninstall uninstall-am uninstall-local - - -# This will avoid that setup.py gets deleted before it is executed in -# clean-local in parallel build. -.NOTPARALLEL: - -@ENABLE_PYTHON_BINDINGS_TRUE@all-local: nghttp2.c -@ENABLE_PYTHON_BINDINGS_TRUE@ $(PYTHON) setup.py build - -@ENABLE_PYTHON_BINDINGS_TRUE@install-exec-local: -@ENABLE_PYTHON_BINDINGS_TRUE@ $(PYTHON) setup.py install --prefix=$(DESTDIR)$(prefix) - -@ENABLE_PYTHON_BINDINGS_TRUE@uninstall-local: -@ENABLE_PYTHON_BINDINGS_TRUE@ rm -rf $(DESTDIR)$(libdir)/python*/site-packages/*nghttp2* - -@ENABLE_PYTHON_BINDINGS_TRUE@clean-local: -@ENABLE_PYTHON_BINDINGS_TRUE@ $(PYTHON) setup.py clean --all -@ENABLE_PYTHON_BINDINGS_TRUE@ -rm -f $(builddir)/nghttp2.c - -@ENABLE_PYTHON_BINDINGS_TRUE@.pyx.c: -@ENABLE_PYTHON_BINDINGS_TRUE@ $(CYTHON) -o $@ $< - -# Tell versions [3.59,3.63) of GNU make to not export all variables. -# Otherwise a system limit (for SysV at least) may be exceeded. -.NOEXPORT: diff --git a/python/cnghttp2.pxd b/python/cnghttp2.pxd deleted file mode 100644 index 2718304..0000000 --- a/python/cnghttp2.pxd +++ /dev/null @@ -1,345 +0,0 @@ -# nghttp2 - HTTP/2 C Library - -# Copyright (c) 2013 Tatsuhiro Tsujikawa - -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: - -# The above copyright notice and this permission notice shall be -# included in all copies or substantial portions of the Software. - -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -from libc.stdint cimport uint8_t, uint16_t, uint32_t, int32_t - -cdef extern from 'nghttp2/nghttp2.h': - - const char NGHTTP2_PROTO_VERSION_ID[] - const char NGHTTP2_CLIENT_CONNECTION_PREFACE[] - const size_t NGHTTP2_INITIAL_WINDOW_SIZE - const size_t NGHTTP2_DEFAULT_HEADER_TABLE_SIZE - - ctypedef struct nghttp2_session: - pass - - ctypedef enum nghttp2_error: - NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE - NGHTTP2_ERR_DEFERRED - - ctypedef enum nghttp2_flag: - NGHTTP2_FLAG_NONE - NGHTTP2_FLAG_END_STREAM - NGHTTP2_FLAG_ACK - - ctypedef enum nghttp2_error_code: - NGHTTP2_NO_ERROR - NGHTTP2_PROTOCOL_ERROR - NGHTTP2_INTERNAL_ERROR - NGHTTP2_SETTINGS_TIMEOUT - - ctypedef enum nghttp2_frame_type: - NGHTTP2_DATA - NGHTTP2_HEADERS - NGHTTP2_RST_STREAM - NGHTTP2_SETTINGS - NGHTTP2_PUSH_PROMISE - NGHTTP2_GOAWAY - - ctypedef enum nghttp2_nv_flag: - NGHTTP2_NV_FLAG_NONE - NGHTTP2_NV_FLAG_NO_INDEX - - ctypedef struct nghttp2_nv: - uint8_t *name - uint8_t *value - uint16_t namelen - uint16_t valuelen - uint8_t flags - - ctypedef enum nghttp2_settings_id: - SETTINGS_HEADER_TABLE_SIZE - NGHTTP2_SETTINGS_HEADER_TABLE_SIZE - NGHTTP2_SETTINGS_ENABLE_PUSH - NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS - NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE - - ctypedef struct nghttp2_settings_entry: - int32_t settings_id - uint32_t value - - ctypedef struct nghttp2_frame_hd: - size_t length - int32_t stream_id - uint8_t type - uint8_t flags - - ctypedef struct nghttp2_data: - nghttp2_frame_hd hd - size_t padlen - - ctypedef enum nghttp2_headers_category: - NGHTTP2_HCAT_REQUEST - NGHTTP2_HCAT_RESPONSE - NGHTTP2_HCAT_PUSH_RESPONSE - NGHTTP2_HCAT_HEADERS - - ctypedef struct nghttp2_headers: - nghttp2_frame_hd hd - size_t padlen - nghttp2_nv *nva - size_t nvlen - nghttp2_headers_category cat - int32_t pri - - ctypedef struct nghttp2_rst_stream: - nghttp2_frame_hd hd - uint32_t error_code - - - ctypedef struct nghttp2_push_promise: - nghttp2_frame_hd hd - nghttp2_nv *nva - size_t nvlen - int32_t promised_stream_id - - ctypedef struct nghttp2_goaway: - nghttp2_frame_hd hd - int32_t last_stream_id - uint32_t error_code - uint8_t *opaque_data - size_t opaque_data_len - - ctypedef union nghttp2_frame: - nghttp2_frame_hd hd - nghttp2_data data - nghttp2_headers headers - nghttp2_rst_stream rst_stream - nghttp2_push_promise push_promise - nghttp2_goaway goaway - - ctypedef ssize_t (*nghttp2_send_callback)\ - (nghttp2_session *session, const uint8_t *data, size_t length, - int flags, void *user_data) - - ctypedef int (*nghttp2_on_frame_recv_callback)\ - (nghttp2_session *session, const nghttp2_frame *frame, void *user_data) - - ctypedef int (*nghttp2_on_data_chunk_recv_callback)\ - (nghttp2_session *session, uint8_t flags, int32_t stream_id, - const uint8_t *data, size_t length, void *user_data) - - ctypedef int (*nghttp2_before_frame_send_callback)\ - (nghttp2_session *session, const nghttp2_frame *frame, void *user_data) - - ctypedef int (*nghttp2_on_stream_close_callback)\ - (nghttp2_session *session, int32_t stream_id, - uint32_t error_code, void *user_data) - - ctypedef int (*nghttp2_on_begin_headers_callback)\ - (nghttp2_session *session, const nghttp2_frame *frame, void *user_data) - - ctypedef int (*nghttp2_on_header_callback)\ - (nghttp2_session *session, - const nghttp2_frame *frame, - const uint8_t *name, size_t namelen, - const uint8_t *value, size_t valuelen, - uint8_t flags, - void *user_data) - - ctypedef int (*nghttp2_on_frame_send_callback)\ - (nghttp2_session *session, const nghttp2_frame *frame, void *user_data) - - ctypedef int (*nghttp2_on_frame_not_send_callback)\ - (nghttp2_session *session, const nghttp2_frame *frame, - int lib_error_code, void *user_data) - - ctypedef struct nghttp2_session_callbacks: - pass - - int nghttp2_session_callbacks_new( - nghttp2_session_callbacks **callbacks_ptr) - - void nghttp2_session_callbacks_del(nghttp2_session_callbacks *callbacks) - - void nghttp2_session_callbacks_set_send_callback( - nghttp2_session_callbacks *cbs, nghttp2_send_callback send_callback) - - void nghttp2_session_callbacks_set_on_frame_recv_callback( - nghttp2_session_callbacks *cbs, - nghttp2_on_frame_recv_callback on_frame_recv_callback) - - void nghttp2_session_callbacks_set_on_data_chunk_recv_callback( - nghttp2_session_callbacks *cbs, - nghttp2_on_data_chunk_recv_callback on_data_chunk_recv_callback) - - void nghttp2_session_callbacks_set_before_frame_send_callback( - nghttp2_session_callbacks *cbs, - nghttp2_before_frame_send_callback before_frame_send_callback) - - void nghttp2_session_callbacks_set_on_frame_send_callback( - nghttp2_session_callbacks *cbs, - nghttp2_on_frame_send_callback on_frame_send_callback) - - void nghttp2_session_callbacks_set_on_frame_not_send_callback( - nghttp2_session_callbacks *cbs, - nghttp2_on_frame_not_send_callback on_frame_not_send_callback) - - void nghttp2_session_callbacks_set_on_stream_close_callback( - nghttp2_session_callbacks *cbs, - nghttp2_on_stream_close_callback on_stream_close_callback) - - void nghttp2_session_callbacks_set_on_begin_headers_callback( - nghttp2_session_callbacks *cbs, - nghttp2_on_begin_headers_callback on_begin_headers_callback) - - void nghttp2_session_callbacks_set_on_header_callback( - nghttp2_session_callbacks *cbs, - nghttp2_on_header_callback on_header_callback) - - int nghttp2_session_client_new(nghttp2_session **session_ptr, - const nghttp2_session_callbacks *callbacks, - void *user_data) - - int nghttp2_session_server_new(nghttp2_session **session_ptr, - const nghttp2_session_callbacks *callbacks, - void *user_data) - - void nghttp2_session_del(nghttp2_session *session) - - - ssize_t nghttp2_session_mem_recv(nghttp2_session *session, - const uint8_t *data, size_t datalen) - - ssize_t nghttp2_session_mem_send(nghttp2_session *session, - const uint8_t **data_ptr) - - int nghttp2_session_send(nghttp2_session *session) - - int nghttp2_session_want_read(nghttp2_session *session) - - int nghttp2_session_want_write(nghttp2_session *session) - - ctypedef union nghttp2_data_source: - int fd - void *ptr - - ctypedef enum nghttp2_data_flag: - NGHTTP2_DATA_FLAG_NONE - NGHTTP2_DATA_FLAG_EOF - - ctypedef ssize_t (*nghttp2_data_source_read_callback)\ - (nghttp2_session *session, int32_t stream_id, - uint8_t *buf, size_t length, uint32_t *data_flags, - nghttp2_data_source *source, void *user_data) - - ctypedef struct nghttp2_data_provider: - nghttp2_data_source source - nghttp2_data_source_read_callback read_callback - - ctypedef struct nghttp2_priority_spec: - int32_t stream_id - int32_t weight - uint8_t exclusive - - int nghttp2_submit_request(nghttp2_session *session, const nghttp2_priority_spec *pri_spec, - const nghttp2_nv *nva, size_t nvlen, - const nghttp2_data_provider *data_prd, - void *stream_user_data) - - int nghttp2_submit_response(nghttp2_session *session, - int32_t stream_id, - const nghttp2_nv *nva, size_t nvlen, - const nghttp2_data_provider *data_prd) - - int nghttp2_submit_push_promise(nghttp2_session *session, uint8_t flags, - int32_t stream_id, - const nghttp2_nv *nva, size_t nvlen, - void *stream_user_data) - - int nghttp2_submit_settings(nghttp2_session *session, uint8_t flags, - const nghttp2_settings_entry *iv, size_t niv) - - int nghttp2_submit_rst_stream(nghttp2_session *session, uint8_t flags, - int32_t stream_id, - uint32_t error_code) - - void* nghttp2_session_get_stream_user_data(nghttp2_session *session, - uint32_t stream_id) - - int nghttp2_session_set_stream_user_data(nghttp2_session *session, - uint32_t stream_id, - void *stream_user_data) - - int nghttp2_session_terminate_session(nghttp2_session *session, - uint32_t error_code) - - int nghttp2_session_resume_data(nghttp2_session *session, - int32_t stream_id) - - const char* nghttp2_strerror(int lib_error_code) - - int nghttp2_hd_deflate_new(nghttp2_hd_deflater **deflater_ptr, - size_t deflate_hd_table_bufsize_max) - - void nghttp2_hd_deflate_del(nghttp2_hd_deflater *deflater) - - int nghttp2_hd_deflate_change_table_size(nghttp2_hd_deflater *deflater, - size_t hd_table_bufsize_max) - - ssize_t nghttp2_hd_deflate_hd(nghttp2_hd_deflater *deflater, - uint8_t *buf, size_t buflen, - const nghttp2_nv *nva, size_t nvlen) - - size_t nghttp2_hd_deflate_bound(nghttp2_hd_deflater *deflater, - const nghttp2_nv *nva, size_t nvlen) - - int nghttp2_hd_inflate_new(nghttp2_hd_inflater **inflater_ptr) - - void nghttp2_hd_inflate_del(nghttp2_hd_inflater *inflater) - - int nghttp2_hd_inflate_change_table_size(nghttp2_hd_inflater *inflater, - size_t hd_table_bufsize_max) - - ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_inflater *inflater, - nghttp2_nv *nv_out, int *inflate_flags, - uint8_t *input, size_t inlen, int in_final) - - int nghttp2_hd_inflate_end_headers(nghttp2_hd_inflater *inflater) - -cdef extern from 'nghttp2_hd.h': - - # This is macro - int NGHTTP2_HD_ENTRY_OVERHEAD - - ctypedef enum nghttp2_hd_inflate_flag: - NGHTTP2_HD_INFLATE_EMIT - NGHTTP2_HD_INFLATE_FINAL - - ctypedef struct nghttp2_hd_entry: - nghttp2_nv nv - uint8_t flags - - ctypedef struct nghttp2_hd_ringbuf: - size_t len - - ctypedef struct nghttp2_hd_context: - nghttp2_hd_ringbuf hd_table - - ctypedef struct nghttp2_hd_deflater: - nghttp2_hd_context ctx - - ctypedef struct nghttp2_hd_inflater: - nghttp2_hd_context ctx - - nghttp2_hd_entry* nghttp2_hd_table_get(nghttp2_hd_context *context, - size_t index) diff --git a/python/nghttp2.pyx b/python/nghttp2.pyx deleted file mode 100644 index c6b9b66..0000000 --- a/python/nghttp2.pyx +++ /dev/null @@ -1,1591 +0,0 @@ -# nghttp2 - HTTP/2 C Library - -# Copyright (c) 2013 Tatsuhiro Tsujikawa - -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: - -# The above copyright notice and this permission notice shall be -# included in all copies or substantial portions of the Software. - -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -cimport cnghttp2 - -from libc.stdlib cimport malloc, free -from libc.string cimport memcpy, memset -from libc.stdint cimport uint8_t, uint16_t, uint32_t, int32_t -import logging - - -DEFAULT_HEADER_TABLE_SIZE = cnghttp2.NGHTTP2_DEFAULT_HEADER_TABLE_SIZE -DEFLATE_MAX_HEADER_TABLE_SIZE = 4096 - -HD_ENTRY_OVERHEAD = cnghttp2.NGHTTP2_HD_ENTRY_OVERHEAD - -class HDTableEntry: - - def __init__(self, name, namelen, value, valuelen): - self.name = name - self.namelen = namelen - self.value = value - self.valuelen = valuelen - - def space(self): - return self.namelen + self.valuelen + HD_ENTRY_OVERHEAD - -cdef _get_hd_table(cnghttp2.nghttp2_hd_context *ctx): - cdef int length = ctx.hd_table.len - cdef cnghttp2.nghttp2_hd_entry *entry - res = [] - for i in range(length): - entry = cnghttp2.nghttp2_hd_table_get(ctx, i) - k = _get_pybytes(entry.nv.name, entry.nv.namelen) - v = _get_pybytes(entry.nv.value, entry.nv.valuelen) - res.append(HDTableEntry(k, entry.nv.namelen, - v, entry.nv.valuelen)) - return res - -cdef _get_pybytes(uint8_t *b, uint16_t blen): - return b[:blen] - -cdef class HDDeflater: - '''Performs header compression. The constructor takes - |hd_table_bufsize_max| parameter, which limits the usage of header - table in the given amount of bytes. This is necessary because the - header compressor and decompressor share the same amount of - header table and the decompressor decides that number. The - compressor may not want to use all header table size because of - limited memory availability. In that case, the - |hd_table_bufsize_max| can be used to cap the upper limit of table - size whatever the header table size is chosen by the decompressor. - The default value of |hd_table_bufsize_max| is 4096 bytes. - - The following example shows how to compress request header sets: - - import binascii, nghttp2 - - deflater = nghttp2.HDDeflater() - res = deflater.deflate([(b'foo', b'bar'), - (b'baz', b'buz')]) - print(binascii.b2a_hex(res)) - - ''' - - cdef cnghttp2.nghttp2_hd_deflater *_deflater - - def __cinit__(self, hd_table_bufsize_max = DEFLATE_MAX_HEADER_TABLE_SIZE): - rv = cnghttp2.nghttp2_hd_deflate_new(&self._deflater, - hd_table_bufsize_max) - if rv != 0: - raise Exception(_strerror(rv)) - - def __dealloc__(self): - cnghttp2.nghttp2_hd_deflate_del(self._deflater) - - def deflate(self, headers): - '''Compresses the |headers|. The |headers| must be sequence of tuple - of name/value pair, which are sequence of bytes (not unicode - string). - - This function returns the encoded header block in byte string. - An exception will be raised on error. - - ''' - cdef cnghttp2.nghttp2_nv *nva = \ - malloc(sizeof(cnghttp2.nghttp2_nv)*\ - len(headers)) - cdef cnghttp2.nghttp2_nv *nvap = nva - - for k, v in headers: - nvap[0].name = k - nvap[0].namelen = len(k) - nvap[0].value = v - nvap[0].valuelen = len(v) - nvap[0].flags = cnghttp2.NGHTTP2_NV_FLAG_NONE - nvap += 1 - - cdef size_t outcap = 0 - cdef ssize_t rv - cdef uint8_t *out - cdef size_t outlen - - outlen = cnghttp2.nghttp2_hd_deflate_bound(self._deflater, - nva, len(headers)) - - out = malloc(outlen) - - rv = cnghttp2.nghttp2_hd_deflate_hd(self._deflater, out, outlen, - nva, len(headers)) - free(nva) - - if rv < 0: - free(out) - - raise Exception(_strerror(rv)) - - cdef bytes res - - try: - res = out[:rv] - finally: - free(out) - - return res - - def change_table_size(self, hd_table_bufsize_max): - '''Changes header table size to |hd_table_bufsize_max| byte. - - An exception will be raised on error. - - ''' - cdef int rv - rv = cnghttp2.nghttp2_hd_deflate_change_table_size(self._deflater, - hd_table_bufsize_max) - if rv != 0: - raise Exception(_strerror(rv)) - - def get_hd_table(self): - '''Returns copy of current dynamic header table.''' - return _get_hd_table(&self._deflater.ctx) - -cdef class HDInflater: - '''Performs header decompression. - - The following example shows how to compress request header sets: - - data = b'0082c5ad82bd0f000362617a0362757a' - inflater = nghttp2.HDInflater() - hdrs = inflater.inflate(data) - print(hdrs) - - ''' - - cdef cnghttp2.nghttp2_hd_inflater *_inflater - - def __cinit__(self): - rv = cnghttp2.nghttp2_hd_inflate_new(&self._inflater) - if rv != 0: - raise Exception(_strerror(rv)) - - def __dealloc__(self): - cnghttp2.nghttp2_hd_inflate_del(self._inflater) - - def inflate(self, data): - '''Decompresses the compressed header block |data|. The |data| must be - byte string (not unicode string). - - ''' - cdef cnghttp2.nghttp2_nv nv - cdef int inflate_flags - cdef ssize_t rv - cdef uint8_t *buf = data - cdef size_t buflen = len(data) - res = [] - while True: - inflate_flags = 0 - rv = cnghttp2.nghttp2_hd_inflate_hd(self._inflater, &nv, - &inflate_flags, - buf, buflen, 1) - if rv < 0: - raise Exception(_strerror(rv)) - buf += rv - buflen -= rv - if inflate_flags & cnghttp2.NGHTTP2_HD_INFLATE_EMIT: - # may throw - res.append((nv.name[:nv.namelen], nv.value[:nv.valuelen])) - if inflate_flags & cnghttp2.NGHTTP2_HD_INFLATE_FINAL: - break - - cnghttp2.nghttp2_hd_inflate_end_headers(self._inflater) - return res - - def change_table_size(self, hd_table_bufsize_max): - '''Changes header table size to |hd_table_bufsize_max| byte. - - An exception will be raised on error. - - ''' - cdef int rv - rv = cnghttp2.nghttp2_hd_inflate_change_table_size(self._inflater, - hd_table_bufsize_max) - if rv != 0: - raise Exception(_strerror(rv)) - - def get_hd_table(self): - '''Returns copy of current dynamic header table.''' - return _get_hd_table(&self._inflater.ctx) - -cdef _strerror(int liberror_code): - return cnghttp2.nghttp2_strerror(liberror_code).decode('utf-8') - -def print_hd_table(hdtable): - '''Convenient function to print |hdtable| to the standard output. This - function does not work if header name/value cannot be decoded using - UTF-8 encoding. - - s=N means the entry occupies N bytes in header table. - - ''' - idx = 0 - for entry in hdtable: - idx += 1 - print('[{}] (s={}) {}: {}'\ - .format(idx, entry.space(), - entry.name.decode('utf-8'), - entry.value.decode('utf-8'))) - -try: - import socket - import io - import asyncio - import traceback - import sys - import email.utils - import datetime - import time - from urllib.parse import urlparse -except ImportError: - asyncio = None - -# body generator flags -DATA_OK = 0 -DATA_EOF = 1 -DATA_DEFERRED = 2 - -class _ByteIOWrapper: - - def __init__(self, b): - self.b = b - - def generate(self, n): - data = self.b.read1(n) - if not data: - return None, DATA_EOF - return data, DATA_OK - -def wrap_body(body): - if body is None: - return body - elif isinstance(body, str): - return _ByteIOWrapper(io.BytesIO(body.encode('utf-8'))).generate - elif isinstance(body, bytes): - return _ByteIOWrapper(io.BytesIO(body)).generate - elif isinstance(body, io.IOBase): - return _ByteIOWrapper(body).generate - else: - # assume that callable in the form f(n) returning tuple byte - # string and flag. - return body - -cdef _get_stream_user_data(cnghttp2.nghttp2_session *session, - int32_t stream_id): - cdef void *stream_user_data - - stream_user_data = cnghttp2.nghttp2_session_get_stream_user_data\ - (session, stream_id) - if stream_user_data == NULL: - return None - - return stream_user_data - -cdef size_t _make_nva(cnghttp2.nghttp2_nv **nva_ptr, headers): - cdef cnghttp2.nghttp2_nv *nva - cdef size_t nvlen - - nvlen = len(headers) - nva = malloc(sizeof(cnghttp2.nghttp2_nv) * nvlen) - for i, (k, v) in enumerate(headers): - nva[i].name = k - nva[i].namelen = len(k) - nva[i].value = v - nva[i].valuelen = len(v) - nva[i].flags = cnghttp2.NGHTTP2_NV_FLAG_NONE - - nva_ptr[0] = nva - - return nvlen - -cdef int server_on_header(cnghttp2.nghttp2_session *session, - const cnghttp2.nghttp2_frame *frame, - const uint8_t *name, size_t namelen, - const uint8_t *value, size_t valuelen, - uint8_t flags, - void *user_data): - cdef http2 = <_HTTP2SessionCoreBase>user_data - logging.debug('server_on_header, type:%s, stream_id:%s', frame.hd.type, frame.hd.stream_id) - - handler = _get_stream_user_data(session, frame.hd.stream_id) - return on_header(name, namelen, value, valuelen, flags, handler) - -cdef int client_on_header(cnghttp2.nghttp2_session *session, - const cnghttp2.nghttp2_frame *frame, - const uint8_t *name, size_t namelen, - const uint8_t *value, size_t valuelen, - uint8_t flags, - void *user_data): - cdef http2 = <_HTTP2SessionCoreBase>user_data - logging.debug('client_on_header, type:%s, stream_id:%s', frame.hd.type, frame.hd.stream_id) - - if frame.hd.type == cnghttp2.NGHTTP2_HEADERS: - handler = _get_stream_user_data(session, frame.hd.stream_id) - elif frame.hd.type == cnghttp2.NGHTTP2_PUSH_PROMISE: - handler = _get_stream_user_data(session, frame.push_promise.promised_stream_id) - - return on_header(name, namelen, value, valuelen, flags, handler) - - -cdef int on_header(const uint8_t *name, size_t namelen, - const uint8_t *value, size_t valuelen, - uint8_t flags, - object handler): - if not handler: - return 0 - - key = name[:namelen] - values = value[:valuelen].split(b'\x00') - if key == b':scheme': - handler.scheme = values[0] - elif key == b':method': - handler.method = values[0] - elif key == b':authority' or key == b'host': - handler.host = values[0] - elif key == b':path': - handler.path = values[0] - elif key == b':status': - handler.status = values[0] - - if key == b'cookie': - handler.cookies.extend(values) - else: - for v in values: - handler.headers.append((key, v)) - - return 0 - -cdef int server_on_begin_request_headers(cnghttp2.nghttp2_session *session, - const cnghttp2.nghttp2_frame *frame, - void *user_data): - cdef http2 = <_HTTP2SessionCore>user_data - - handler = http2._make_handler(frame.hd.stream_id) - cnghttp2.nghttp2_session_set_stream_user_data(session, frame.hd.stream_id, - handler) - - return 0 - -cdef int server_on_begin_headers(cnghttp2.nghttp2_session *session, - const cnghttp2.nghttp2_frame *frame, - void *user_data): - if frame.hd.type == cnghttp2.NGHTTP2_HEADERS: - if frame.headers.cat == cnghttp2.NGHTTP2_HCAT_REQUEST: - return server_on_begin_request_headers(session, frame, user_data) - - return 0 - -cdef int server_on_frame_recv(cnghttp2.nghttp2_session *session, - const cnghttp2.nghttp2_frame *frame, - void *user_data): - cdef http2 = <_HTTP2SessionCore>user_data - logging.debug('server_on_frame_recv, type:%s, stream_id:%s', frame.hd.type, frame.hd.stream_id) - - if frame.hd.type == cnghttp2.NGHTTP2_DATA: - if frame.hd.flags & cnghttp2.NGHTTP2_FLAG_END_STREAM: - handler = _get_stream_user_data(session, frame.hd.stream_id) - if not handler: - return 0 - try: - handler.on_request_done() - except: - sys.stderr.write(traceback.format_exc()) - return http2._rst_stream(frame.hd.stream_id) - elif frame.hd.type == cnghttp2.NGHTTP2_HEADERS: - if frame.headers.cat == cnghttp2.NGHTTP2_HCAT_REQUEST: - handler = _get_stream_user_data(session, frame.hd.stream_id) - if not handler: - return 0 - if handler.cookies: - handler.headers.append((b'cookie', - b'; '.join(handler.cookies))) - handler.cookies = None - try: - handler.on_headers() - if frame.hd.flags & cnghttp2.NGHTTP2_FLAG_END_STREAM: - handler.on_request_done() - except: - sys.stderr.write(traceback.format_exc()) - return http2._rst_stream(frame.hd.stream_id) - elif frame.hd.type == cnghttp2.NGHTTP2_SETTINGS: - if (frame.hd.flags & cnghttp2.NGHTTP2_FLAG_ACK): - http2._stop_settings_timer() - - return 0 - -cdef int on_data_chunk_recv(cnghttp2.nghttp2_session *session, - uint8_t flags, - int32_t stream_id, const uint8_t *data, - size_t length, void *user_data): - cdef http2 = <_HTTP2SessionCoreBase>user_data - - handler = _get_stream_user_data(session, stream_id) - if not handler: - return 0 - - try: - handler.on_data(data[:length]) - except: - sys.stderr.write(traceback.format_exc()) - return http2._rst_stream(stream_id) - - return 0 - -cdef int server_on_frame_send(cnghttp2.nghttp2_session *session, - const cnghttp2.nghttp2_frame *frame, - void *user_data): - cdef http2 = <_HTTP2SessionCore>user_data - logging.debug('server_on_frame_send, type:%s, stream_id:%s', frame.hd.type, frame.hd.stream_id) - - if frame.hd.type == cnghttp2.NGHTTP2_PUSH_PROMISE: - # For PUSH_PROMISE, send push response immediately - handler = _get_stream_user_data\ - (session, frame.push_promise.promised_stream_id) - if not handler: - return 0 - - http2.send_response(handler) - elif frame.hd.type == cnghttp2.NGHTTP2_SETTINGS: - if (frame.hd.flags & cnghttp2.NGHTTP2_FLAG_ACK) != 0: - return 0 - http2._start_settings_timer() - -cdef int server_on_frame_not_send(cnghttp2.nghttp2_session *session, - const cnghttp2.nghttp2_frame *frame, - int lib_error_code, - void *user_data): - cdef http2 = <_HTTP2SessionCore>user_data - logging.debug('server_on_frame_not_send, type:%s, stream_id:%s', frame.hd.type, frame.hd.stream_id) - - if frame.hd.type == cnghttp2.NGHTTP2_PUSH_PROMISE: - # We have to remove handler here. Without this, it is not - # removed until session is terminated. - handler = _get_stream_user_data\ - (session, frame.push_promise.promised_stream_id) - if not handler: - return 0 - http2._remove_handler(handler) - -cdef int on_stream_close(cnghttp2.nghttp2_session *session, - int32_t stream_id, - uint32_t error_code, - void *user_data): - cdef http2 = <_HTTP2SessionCoreBase>user_data - logging.debug('on_stream_close, stream_id:%s', stream_id) - - handler = _get_stream_user_data(session, stream_id) - if not handler: - return 0 - - try: - handler.on_close(error_code) - except: - sys.stderr.write(traceback.format_exc()) - - http2._remove_handler(handler) - - return 0 - -cdef ssize_t data_source_read(cnghttp2.nghttp2_session *session, - int32_t stream_id, - uint8_t *buf, size_t length, - uint32_t *data_flags, - cnghttp2.nghttp2_data_source *source, - void *user_data): - cdef http2 = <_HTTP2SessionCoreBase>user_data - generator = source.ptr - - http2.enter_callback() - try: - data, flag = generator(length) - except: - sys.stderr.write(traceback.format_exc()) - return cnghttp2.NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; - finally: - http2.leave_callback() - - if flag == DATA_DEFERRED: - return cnghttp2.NGHTTP2_ERR_DEFERRED - - if data: - nread = len(data) - memcpy(buf, data, nread) - else: - nread = 0 - - if flag == DATA_EOF: - data_flags[0] = cnghttp2.NGHTTP2_DATA_FLAG_EOF - elif flag != DATA_OK: - return cnghttp2.NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE - - return nread - -cdef int client_on_begin_headers(cnghttp2.nghttp2_session *session, - const cnghttp2.nghttp2_frame *frame, - void *user_data): - cdef http2 = <_HTTP2ClientSessionCore>user_data - - if frame.hd.type == cnghttp2.NGHTTP2_PUSH_PROMISE: - # Generate a temporary handler until the headers are all received - push_handler = BaseResponseHandler() - http2._add_handler(push_handler, frame.push_promise.promised_stream_id) - cnghttp2.nghttp2_session_set_stream_user_data(session, frame.push_promise.promised_stream_id, - push_handler) - - return 0 - -cdef int client_on_frame_recv(cnghttp2.nghttp2_session *session, - const cnghttp2.nghttp2_frame *frame, - void *user_data): - cdef http2 = <_HTTP2ClientSessionCore>user_data - logging.debug('client_on_frame_recv, type:%s, stream_id:%s', frame.hd.type, frame.hd.stream_id) - - if frame.hd.type == cnghttp2.NGHTTP2_DATA: - if frame.hd.flags & cnghttp2.NGHTTP2_FLAG_END_STREAM: - handler = _get_stream_user_data(session, frame.hd.stream_id) - if not handler: - return 0 - try: - handler.on_response_done() - except: - sys.stderr.write(traceback.format_exc()) - return http2._rst_stream(frame.hd.stream_id) - elif frame.hd.type == cnghttp2.NGHTTP2_HEADERS: - if frame.headers.cat == cnghttp2.NGHTTP2_HCAT_RESPONSE or frame.headers.cat == cnghttp2.NGHTTP2_HCAT_PUSH_RESPONSE: - handler = _get_stream_user_data(session, frame.hd.stream_id) - - if not handler: - return 0 - # TODO handle 1xx non-final response - if handler.cookies: - handler.headers.append((b'cookie', - b'; '.join(handler.cookies))) - handler.cookies = None - try: - handler.on_headers() - if frame.hd.flags & cnghttp2.NGHTTP2_FLAG_END_STREAM: - handler.on_response_done() - except: - sys.stderr.write(traceback.format_exc()) - return http2._rst_stream(frame.hd.stream_id) - elif frame.hd.type == cnghttp2.NGHTTP2_SETTINGS: - if (frame.hd.flags & cnghttp2.NGHTTP2_FLAG_ACK): - http2._stop_settings_timer() - elif frame.hd.type == cnghttp2.NGHTTP2_PUSH_PROMISE: - handler = _get_stream_user_data(session, frame.hd.stream_id) - if not handler: - return 0 - # Get the temporary push_handler which now should have all of the header data - push_handler = _get_stream_user_data(session, frame.push_promise.promised_stream_id) - if not push_handler: - return 0 - # Remove the temporary handler - http2._remove_handler(push_handler) - cnghttp2.nghttp2_session_set_stream_user_data(session, frame.push_promise.promised_stream_id, - NULL) - - try: - handler.on_push_promise(push_handler) - except: - sys.stderr.write(traceback.format_exc()) - return http2._rst_stream(frame.hd.stream_id) - - return 0 - -cdef int client_on_frame_send(cnghttp2.nghttp2_session *session, - const cnghttp2.nghttp2_frame *frame, - void *user_data): - cdef http2 = <_HTTP2ClientSessionCore>user_data - logging.debug('client_on_frame_send, type:%s, stream_id:%s', frame.hd.type, frame.hd.stream_id) - - if frame.hd.type == cnghttp2.NGHTTP2_SETTINGS: - if (frame.hd.flags & cnghttp2.NGHTTP2_FLAG_ACK) != 0: - return 0 - http2._start_settings_timer() - -cdef class _HTTP2SessionCoreBase: - cdef cnghttp2.nghttp2_session *session - cdef transport - cdef handler_class - cdef handlers - cdef settings_timer - cdef inside_callback - - def __cinit__(self, transport, handler_class=None): - self.session = NULL - self.transport = transport - self.handler_class = handler_class - self.handlers = set() - self.settings_timer = None - self.inside_callback = False - - def __dealloc__(self): - cnghttp2.nghttp2_session_del(self.session) - - def data_received(self, data): - cdef ssize_t rv - - rv = cnghttp2.nghttp2_session_mem_recv(self.session, data, len(data)) - if rv < 0: - raise Exception('nghttp2_session_mem_recv failed: {}'.format\ - (_strerror(rv))) - self.send_data() - - OUTBUF_MAX = 65535 - SETTINGS_TIMEOUT = 5.0 - - def send_data(self): - cdef ssize_t outbuflen - cdef const uint8_t *outbuf - - while True: - if self.transport.get_write_buffer_size() > self.OUTBUF_MAX: - break - outbuflen = cnghttp2.nghttp2_session_mem_send(self.session, &outbuf) - if outbuflen == 0: - break - if outbuflen < 0: - raise Exception('nghttp2_session_mem_send faild: {}'.format\ - (_strerror(outbuflen))) - self.transport.write(outbuf[:outbuflen]) - - if self.transport.get_write_buffer_size() == 0 and \ - cnghttp2.nghttp2_session_want_read(self.session) == 0 and \ - cnghttp2.nghttp2_session_want_write(self.session) == 0: - self.transport.close() - - def resume(self, stream_id): - cnghttp2.nghttp2_session_resume_data(self.session, stream_id) - if not self.inside_callback: - self.send_data() - - def enter_callback(self): - self.inside_callback = True - - def leave_callback(self): - self.inside_callback = False - - def _make_handler(self, stream_id): - logging.debug('_make_handler, stream_id:%s', stream_id) - handler = self.handler_class(self, stream_id) - self.handlers.add(handler) - return handler - - def _remove_handler(self, handler): - logging.debug('_remove_handler, stream_id:%s', handler.stream_id) - self.handlers.remove(handler) - - def _add_handler(self, handler, stream_id): - logging.debug('_add_handler, stream_id:%s', stream_id) - handler.stream_id = stream_id - handler.http2 = self - handler.remote_address = self._get_remote_address() - self.handlers.add(handler) - - def _rst_stream(self, stream_id, - error_code=cnghttp2.NGHTTP2_INTERNAL_ERROR): - cdef int rv - - rv = cnghttp2.nghttp2_submit_rst_stream\ - (self.session, cnghttp2.NGHTTP2_FLAG_NONE, - stream_id, error_code) - - return rv - - def _get_remote_address(self): - return self.transport.get_extra_info('peername') - - def _start_settings_timer(self): - loop = asyncio.get_event_loop() - self.settings_timer = loop.call_later(self.SETTINGS_TIMEOUT, - self._settings_timeout) - - def _stop_settings_timer(self): - if self.settings_timer: - self.settings_timer.cancel() - self.settings_timer = None - - def _settings_timeout(self): - cdef int rv - - logging.debug('_settings_timeout') - - self.settings_timer = None - - rv = cnghttp2.nghttp2_session_terminate_session\ - (self.session, cnghttp2.NGHTTP2_SETTINGS_TIMEOUT) - try: - self.send_data() - except Exception as err: - sys.stderr.write(traceback.format_exc()) - self.transport.close() - return - - def _log_request(self, handler): - now = datetime.datetime.now() - tv = time.mktime(now.timetuple()) - datestr = email.utils.formatdate(timeval=tv, localtime=False, - usegmt=True) - try: - method = handler.method.decode('utf-8') - except: - method = handler.method - try: - path = handler.path.decode('utf-8') - except: - path = handler.path - logging.info('%s - - [%s] "%s %s HTTP/2" %s - %s', handler.remote_address[0], - datestr, method, path, handler.status, - 'P' if handler.pushed else '-') - - def close(self): - rv = cnghttp2.nghttp2_session_terminate_session\ - (self.session, cnghttp2.NGHTTP2_NO_ERROR) - try: - self.send_data() - except Exception as err: - sys.stderr.write(traceback.format_exc()) - self.transport.close() - return - -cdef class _HTTP2SessionCore(_HTTP2SessionCoreBase): - def __cinit__(self, *args, **kwargs): - cdef cnghttp2.nghttp2_session_callbacks *callbacks - cdef cnghttp2.nghttp2_settings_entry iv[2] - cdef int rv - - super(_HTTP2SessionCore, self).__init__(*args, **kwargs) - - rv = cnghttp2.nghttp2_session_callbacks_new(&callbacks) - - if rv != 0: - raise Exception('nghttp2_session_callbacks_new failed: {}'.format\ - (_strerror(rv))) - - cnghttp2.nghttp2_session_callbacks_set_on_header_callback( - callbacks, server_on_header) - cnghttp2.nghttp2_session_callbacks_set_on_begin_headers_callback( - callbacks, server_on_begin_headers) - cnghttp2.nghttp2_session_callbacks_set_on_frame_recv_callback( - callbacks, server_on_frame_recv) - cnghttp2.nghttp2_session_callbacks_set_on_stream_close_callback( - callbacks, on_stream_close) - cnghttp2.nghttp2_session_callbacks_set_on_frame_send_callback( - callbacks, server_on_frame_send) - cnghttp2.nghttp2_session_callbacks_set_on_frame_not_send_callback( - callbacks, server_on_frame_not_send) - cnghttp2.nghttp2_session_callbacks_set_on_data_chunk_recv_callback( - callbacks, on_data_chunk_recv) - - rv = cnghttp2.nghttp2_session_server_new(&self.session, callbacks, - self) - - cnghttp2.nghttp2_session_callbacks_del(callbacks) - - if rv != 0: - raise Exception('nghttp2_session_server_new failed: {}'.format\ - (_strerror(rv))) - - iv[0].settings_id = cnghttp2.NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS - iv[0].value = 100 - iv[1].settings_id = cnghttp2.NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE - iv[1].value = cnghttp2.NGHTTP2_INITIAL_WINDOW_SIZE - - rv = cnghttp2.nghttp2_submit_settings(self.session, - cnghttp2.NGHTTP2_FLAG_NONE, - iv, sizeof(iv) / sizeof(iv[0])) - - if rv != 0: - raise Exception('nghttp2_submit_settings failed: {}'.format\ - (_strerror(rv))) - - def send_response(self, handler): - cdef cnghttp2.nghttp2_data_provider prd - cdef cnghttp2.nghttp2_data_provider *prd_ptr - cdef cnghttp2.nghttp2_nv *nva - cdef size_t nvlen - cdef int rv - - logging.debug('send_response, stream_id:%s', handler.stream_id) - - nva = NULL - nvlen = _make_nva(&nva, handler.response_headers) - - if handler.response_body: - prd.source.ptr = handler.response_body - prd.read_callback = data_source_read - prd_ptr = &prd - else: - prd_ptr = NULL - - rv = cnghttp2.nghttp2_submit_response(self.session, handler.stream_id, - nva, nvlen, prd_ptr) - - free(nva) - - if rv != 0: - # TODO Ignore return value - self._rst_stream(handler.stream_id) - raise Exception('nghttp2_submit_response failed: {}'.format\ - (_strerror(rv))) - - self._log_request(handler) - - def push(self, handler, promised_handler): - cdef cnghttp2.nghttp2_nv *nva - cdef size_t nvlen - cdef int32_t promised_stream_id - - self.handlers.add(promised_handler) - - nva = NULL - nvlen = _make_nva(&nva, promised_handler.headers) - - promised_stream_id = cnghttp2.nghttp2_submit_push_promise\ - (self.session, - cnghttp2.NGHTTP2_FLAG_NONE, - handler.stream_id, - nva, nvlen, - promised_handler) - if promised_stream_id < 0: - raise Exception('nghttp2_submit_push_promise failed: {}'.format\ - (_strerror(promised_stream_id))) - - promised_handler.stream_id = promised_stream_id - - logging.debug('push, stream_id:%s', promised_stream_id) - - return promised_handler - -cdef class _HTTP2ClientSessionCore(_HTTP2SessionCoreBase): - def __cinit__(self, *args, **kwargs): - cdef cnghttp2.nghttp2_session_callbacks *callbacks - cdef cnghttp2.nghttp2_settings_entry iv[2] - cdef int rv - - super(_HTTP2ClientSessionCore, self).__init__(*args, **kwargs) - - rv = cnghttp2.nghttp2_session_callbacks_new(&callbacks) - - if rv != 0: - raise Exception('nghttp2_session_callbacks_new failed: {}'.format\ - (_strerror(rv))) - - cnghttp2.nghttp2_session_callbacks_set_on_header_callback( - callbacks, client_on_header) - cnghttp2.nghttp2_session_callbacks_set_on_begin_headers_callback( - callbacks, client_on_begin_headers) - cnghttp2.nghttp2_session_callbacks_set_on_frame_recv_callback( - callbacks, client_on_frame_recv) - cnghttp2.nghttp2_session_callbacks_set_on_stream_close_callback( - callbacks, on_stream_close) - cnghttp2.nghttp2_session_callbacks_set_on_frame_send_callback( - callbacks, client_on_frame_send) - cnghttp2.nghttp2_session_callbacks_set_on_data_chunk_recv_callback( - callbacks, on_data_chunk_recv) - - rv = cnghttp2.nghttp2_session_client_new(&self.session, callbacks, - self) - - cnghttp2.nghttp2_session_callbacks_del(callbacks) - - if rv != 0: - raise Exception('nghttp2_session_client_new failed: {}'.format\ - (_strerror(rv))) - - iv[0].settings_id = cnghttp2.NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS - iv[0].value = 100 - iv[1].settings_id = cnghttp2.NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE - iv[1].value = cnghttp2.NGHTTP2_INITIAL_WINDOW_SIZE - - rv = cnghttp2.nghttp2_submit_settings(self.session, - cnghttp2.NGHTTP2_FLAG_NONE, - iv, sizeof(iv) / sizeof(iv[0])) - - if rv != 0: - raise Exception('nghttp2_submit_settings failed: {}'.format\ - (_strerror(rv))) - - def send_request(self, method, scheme, host, path, headers, body, handler): - cdef cnghttp2.nghttp2_data_provider prd - cdef cnghttp2.nghttp2_data_provider *prd_ptr - cdef cnghttp2.nghttp2_priority_spec *pri_ptr - cdef cnghttp2.nghttp2_nv *nva - cdef size_t nvlen - cdef int32_t stream_id - - body = wrap_body(body) - - custom_headers = _encode_headers(headers) - headers = [ - (b':method', method.encode('utf-8')), - (b':scheme', scheme.encode('utf-8')), - (b':authority', host.encode('utf-8')), - (b':path', path.encode('utf-8')) - ] - headers.extend(custom_headers) - - nva = NULL - nvlen = _make_nva(&nva, headers) - - if body: - prd.source.ptr = body - prd.read_callback = data_source_read - prd_ptr = &prd - else: - prd_ptr = NULL - - # TODO: Enable priorities - pri_ptr = NULL - - stream_id = cnghttp2.nghttp2_submit_request\ - (self.session, pri_ptr, - nva, nvlen, prd_ptr, - handler) - free(nva) - - if stream_id < 0: - raise Exception('nghttp2_submit_request failed: {}'.format\ - (_strerror(stream_id))) - - logging.debug('request, stream_id:%s', stream_id) - - self._add_handler(handler, stream_id) - cnghttp2.nghttp2_session_set_stream_user_data(self.session, stream_id, - handler) - - return handler - - def push(self, push_promise, handler): - if handler: - # push_promise accepted, fill in the handler with the stored - # headers from the push_promise - handler.status = push_promise.status - handler.scheme = push_promise.scheme - handler.method = push_promise.method - handler.host = push_promise.host - handler.path = push_promise.path - handler.cookies = push_promise.cookies - handler.stream_id = push_promise.stream_id - handler.http2 = self - handler.pushed = True - - self._add_handler(handler, handler.stream_id) - - cnghttp2.nghttp2_session_set_stream_user_data(self.session, handler.stream_id, - handler) - else: - # push_promise rejected, reset the stream - self._rst_stream(push_promise.stream_id, - error_code=cnghttp2.NGHTTP2_NO_ERROR) - -if asyncio: - - class BaseRequestHandler: - - """HTTP/2 request (stream) handler base class. - - The class is used to handle the HTTP/2 stream. By default, it does - not nothing. It must be subclassed to handle each event callback - method. - - The first callback method invoked is on_headers(). It is called - when HEADERS frame, which includes request header fields, is - arrived. - - If request has request body, on_data(data) is invoked for each - chunk of received data. - - When whole request is received, on_request_done() is invoked. - - When stream is closed, on_close(error_code) is called. - - The application can send response using send_response() method. It - can be used in on_headers(), on_data() or on_request_done(). - - The application can push resource using push() method. It must be - used before send_response() call. - - The following instance variables are available: - - client_address - Contains a tuple of the form (host, port) referring to the client's - address. - - stream_id - Stream ID of this stream - - scheme - Scheme of the request URI. This is a value of :scheme header field. - - method - Method of this stream. This is a value of :method header field. - - host - This is a value of :authority or host header field. - - path - This is a value of :path header field. - - headers - Request header fields - - """ - - def __init__(self, http2, stream_id): - self.headers = [] - self.cookies = [] - # Stream ID. For promised stream, it is initially -1. - self.stream_id = stream_id - self.http2 = http2 - # address of the client - self.remote_address = self.http2._get_remote_address() - # :scheme header field in request - self.scheme = None - # :method header field in request - self.method = None - # :authority or host header field in request - self.host = None - # :path header field in request - self.path = None - # HTTP status - self.status = None - # True if this is a handler for pushed resource - self.pushed = False - - @property - def client_address(self): - return self.remote_address - - def on_headers(self): - - '''Called when request HEADERS is arrived. - - ''' - pass - - def on_data(self, data): - - '''Called when a chunk of request body is arrived. This method - will be called multiple times until all data are received. - - ''' - pass - - def on_request_done(self): - - '''Called when whole request was received - - ''' - pass - - def on_close(self, error_code): - - '''Called when stream is about to close. - - ''' - pass - - def send_response(self, status=200, headers=None, body=None): - - '''Send response. The status is HTTP status code. The headers is - additional response headers. The :status header field is - appended by the library. The body is the response body. It - could be None if response body is empty. Or it must be - instance of either str, bytes, io.IOBase or callable, - called body generator, which takes one parameter, - size. The body generator generates response body. It can - pause generation of response so that it can wait for slow - backend data generation. When invoked, it should return - tuple, byte string and flag. The flag is either DATA_OK, - DATA_EOF and DATA_DEFERRED. For non-empty byte string and - it is not the last chunk of response, DATA_OK is returned - as flag. If this is the last chunk of the response (byte - string is possibly None), DATA_EOF must be returned as - flag. If there is no data available right now, but - additional data are anticipated, return tuple (None, - DATA_DEFERRD). When data arrived, call resume() and - restart response body transmission. - - Only the body generator can pause response body - generation; instance of io.IOBase must not block. - - If instance of str is specified as body, it is encoded - using UTF-8. - - The headers is a list of tuple of the form (name, - value). The name and value can be either unicode string or - byte string. - - On error, exception will be thrown. - - ''' - if self.status is not None: - raise Exception('response has already been sent') - - if not status: - raise Exception('status must not be empty') - - body = wrap_body(body) - - self._set_response_prop(status, headers, body) - self.http2.send_response(self) - - def push(self, path, method='GET', request_headers=None, - status=200, headers=None, body=None): - - '''Push a resource. The path is a path portion of request URI - for this - resource. The method is a method to access this - resource. The request_headers is additional request - headers to access this resource. The :scheme, :method, - :authority and :path are appended by the library. The - :scheme and :authority are inherited from the request (not - request_headers parameter). - - The status is HTTP status code. The headers is additional - response headers. The :status header field is appended by - the library. The body is the response body. It has the - same semantics of body parameter of send_response(). - - The headers and request_headers are a list of tuple of the - form (name, value). The name and value can be either - unicode string or byte string. - - On error, exception will be thrown. - - ''' - if not status: - raise Exception('status must not be empty') - - if not method: - raise Exception('method must not be empty') - - if not path: - raise Exception('path must not be empty') - - body = wrap_body(body) - - promised_handler = self.http2._make_handler(-1) - promised_handler.pushed = True - promised_handler.scheme = self.scheme - promised_handler.method = method.encode('utf-8') - promised_handler.host = self.host - promised_handler.path = path.encode('utf-8') - promised_handler._set_response_prop(status, headers, body) - - headers = [ - (b':method', promised_handler.method), - (b':scheme', promised_handler.scheme), - (b':authority', promised_handler.host), - (b':path', promised_handler.path) - ] - headers.extend(_encode_headers(request_headers)) - - promised_handler.headers = headers - - return self.http2.push(self, promised_handler) - - def _set_response_prop(self, status, headers, body): - self.status = status - - if headers is None: - headers = [] - - self.response_headers = [(b':status', str(status).encode('utf-8'))] - self.response_headers.extend(_encode_headers(headers)) - - self.response_body = body - - def resume(self): - self.http2.resume(self.stream_id) - - def _encode_headers(headers): - if not headers: - return [] - return [(k if isinstance(k, bytes) else k.encode('utf-8'), - v if isinstance(v, bytes) else v.encode('utf-8')) \ - for k, v in headers] - - class _HTTP2Session(asyncio.Protocol): - - def __init__(self, RequestHandlerClass): - asyncio.Protocol.__init__(self) - self.RequestHandlerClass = RequestHandlerClass - self.http2 = None - - def connection_made(self, transport): - address = transport.get_extra_info('peername') - logging.info('connection_made, address:%s, port:%s', address[0], address[1]) - - self.transport = transport - sock = self.transport.get_extra_info('socket') - sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) - ssl_ctx = self.transport.get_extra_info('sslcontext') - if ssl_ctx: - protocol = sock.selected_npn_protocol() - logging.info('npn, protocol:%s', protocol) - if protocol.encode('utf-8') != \ - cnghttp2.NGHTTP2_PROTO_VERSION_ID: - self.transport.abort() - return - try: - self.http2 = _HTTP2SessionCore\ - (self.transport, - self.RequestHandlerClass) - except Exception as err: - sys.stderr.write(traceback.format_exc()) - self.transport.abort() - return - - - def connection_lost(self, exc): - logging.info('connection_lost') - if self.http2: - self.http2 = None - - def data_received(self, data): - try: - self.http2.data_received(data) - except Exception as err: - sys.stderr.write(traceback.format_exc()) - self.transport.close() - return - - def resume_writing(self): - try: - self.http2.send_data() - except Exception as err: - sys.stderr.write(traceback.format_exc()) - self.transport.close() - return - - class HTTP2Server: - - '''HTTP/2 server. - - This class builds on top of the asyncio event loop. On - construction, RequestHandlerClass must be given, which must be a - subclass of BaseRequestHandler class. - - ''' - def __init__(self, address, RequestHandlerClass, ssl=None): - - '''address is a tuple of the listening address and port (e.g., - ('127.0.0.1', 8080)). RequestHandlerClass must be a subclass - of BaseRequestHandler class to handle a HTTP/2 stream. The - ssl can be ssl.SSLContext instance. If it is not None, the - resulting server is SSL/TLS capable. - - ''' - def session_factory(): - return _HTTP2Session(RequestHandlerClass) - - self.loop = asyncio.get_event_loop() - - if ssl: - ssl.set_npn_protocols([cnghttp2.NGHTTP2_PROTO_VERSION_ID\ - .decode('utf-8')]) - - coro = self.loop.create_server(session_factory, - host=address[0], port=address[1], - ssl=ssl) - self.server = self.loop.run_until_complete(coro) - logging.info('listen, address:%s, port:%s', address[0], address[1]) - - def serve_forever(self): - try: - self.loop.run_forever() - finally: - self.server.close() - self.loop.close() - - - - class BaseResponseHandler: - - """HTTP/2 response (stream) handler base class. - - The class is used to handle the HTTP/2 stream. By default, it does - not nothing. It must be subclassed to handle each event callback - method. - - The first callback method invoked is on_headers(). It is called - when HEADERS frame, which includes response header fields, is - arrived. - - If response has a body, on_data(data) is invoked for each - chunk of received data. - - When whole response is received, on_response_done() is invoked. - - When stream is closed, on_close(error_code) is called. - - The application can send follow up requests using HTTP2Client.send_request() method. - - The application can handle push resource using on_push_promise() method. - - The following instance variables are available: - - server_address - Contains a tuple of the form (host, port) referring to the server's - address. - - stream_id - Stream ID of this stream - - scheme - Scheme of the request URI. This is a value of :scheme header field. - - method - Method of this stream. This is a value of :method header field. - - host - This is a value of :authority or host header field. - - path - This is a value of :path header field. - - headers - Response header fields. There is a special exception. If this - object is passed to push_promise(), this instance variable contains - pushed request header fields. - - """ - - def __init__(self, http2=None, stream_id=-1): - self.headers = [] - self.cookies = [] - # Stream ID. For promised stream, it is initially -1. - self.stream_id = stream_id - self.http2 = http2 - # address of the server - self.remote_address = None - # :scheme header field in request - self.scheme = None - # :method header field in request - self.method = None - # :authority or host header field in request - self.host = None - # :path header field in request - self.path = None - # HTTP status - self.status = None - # True if this is a handler for pushed resource - self.pushed = False - - @property - def server_address(self): - return self.remote_address - - def on_headers(self): - - '''Called when response HEADERS is arrived. - - ''' - pass - - def on_data(self, data): - - '''Called when a chunk of response body is arrived. This method - will be called multiple times until all data are received. - - ''' - pass - - def on_response_done(self): - - '''Called when whole response was received - - ''' - pass - - def on_close(self, error_code): - - '''Called when stream is about to close. - - ''' - pass - - def on_push_promise(self, push_promise): - - '''Called when a push is promised. Default behavior is to - cancel the push. If application overrides this method, - it should call either accept_push or reject_push. - - ''' - self.reject_push(push_promise) - - def reject_push(self, push_promise): - - '''Convenience method equivalent to calling accept_push - with a falsy value. - - ''' - self.http2.push(push_promise, None) - - def accept_push(self, push_promise, handler=None): - - '''Accept a push_promise and provider a handler for the - new stream. If a falsy value is supplied for the handler, - the push is rejected. - - ''' - self.http2.push(push_promise, handler) - - def resume(self): - self.http2.resume(self.stream_id) - - class _HTTP2ClientSession(asyncio.Protocol): - - def __init__(self, client): - asyncio.Protocol.__init__(self) - self.http2 = None - self.pending = [] - self.client = client - - def connection_made(self, transport): - address = transport.get_extra_info('peername') - logging.info('connection_made, address:%s, port:%s', address[0], address[1]) - - self.transport = transport - sock = self.transport.get_extra_info('socket') - sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) - ssl_ctx = self.transport.get_extra_info('sslcontext') - if ssl_ctx: - protocol = sock.selected_npn_protocol() - logging.info('npn, protocol:%s', protocol) - if protocol is None or protocol.encode('utf-8') != \ - cnghttp2.NGHTTP2_PROTO_VERSION_ID: - self.transport.abort() - - self.http2 = _HTTP2ClientSessionCore(self.transport) - - # Clear pending requests - send_pending = self.pending - self.pending = [] - for method,scheme,host,path,headers,body,handler in send_pending: - self.send_request(method=method, scheme=scheme, host=host, path=path,\ - headers=headers, body=body, handler=handler) - self.http2.send_data() - - def connection_lost(self, exc): - logging.info('connection_lost') - if self.http2: - self.http2 = None - self.client.close() - - def data_received(self, data): - try: - self.http2.data_received(data) - except Exception as err: - sys.stderr.write(traceback.format_exc()) - self.transport.close() - return - - def resume_writing(self): - try: - self.http2.send_data() - except Exception as err: - sys.stderr.write(traceback.format_exc()) - self.transport.close() - return - - def send_request(self, method, scheme, host, path, headers, body, handler): - try: - # Waiting until connection established - if not self.http2: - self.pending.append([method, scheme, host, path, headers, body, handler]) - return - - self.http2.send_request(method=method, scheme=scheme, host=host, path=path,\ - headers=headers, body=body, handler=handler) - self.http2.send_data() - except Exception as err: - sys.stderr.write(traceback.format_exc()) - self.transport.close() - return - - def close(self): - if self.http2: - self.http2.close() - - - class HTTP2Client: - - '''HTTP/2 client. - - This class builds on top of the asyncio event loop. - - ''' - def __init__(self, address, loop=None, ssl=None): - - '''address is a tuple of the connect address and port (e.g., - ('127.0.0.1', 8080)). The ssl can be ssl.SSLContext instance. - If it is not None, the resulting client is SSL/TLS capable. - ''' - - self.address = address - self.session = _HTTP2ClientSession(self) - def session_factory(): - return self.session - - if ssl: - ssl.set_npn_protocols([cnghttp2.NGHTTP2_PROTO_VERSION_ID\ - .decode('utf-8')]) - - self.loop = loop - if not self.loop: - self.loop = asyncio.get_event_loop() - - coro = self.loop.create_connection(session_factory, - host=address[0], port=address[1], - ssl=ssl) - - if ssl: - self.scheme = 'https' - else: - self.scheme = 'http' - - self.transport,_ = self.loop.run_until_complete(coro) - logging.info('connect, address:%s, port:%s', self.address[0], self.address[1]) - - @property - def io_loop(self): - return self.loop - - def close(self): - self.session.close() - - def send_request(self, method='GET', url='/', headers=None, body=None, handler=None): - url = urlparse(url) - scheme = url.scheme if url.scheme else self.scheme - host = url.netloc if url.netloc else self.address[0]+':'+str(self.address[1]) - path = url.path - if url.params: - path += ';'+url.params - if url.query: - path += '?'+url.query - if url.fragment: - path += '#'+url.fragment - - self.session.send_request(method=method, scheme=scheme, host=host, path=path,\ - headers=headers, body=body, handler=handler) diff --git a/python/setup.py.in b/python/setup.py.in deleted file mode 100644 index 7f9de48..0000000 --- a/python/setup.py.in +++ /dev/null @@ -1,49 +0,0 @@ -# nghttp2 - HTTP/2 C Library - -# Copyright (c) 2013 Tatsuhiro Tsujikawa - -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: - -# The above copyright notice and this permission notice shall be -# included in all copies or substantial portions of the Software. - -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -import sys -from distutils.core import setup -from distutils.extension import Extension - -if sys.platform == "win32": - LIBS = ['nghttp2_imp', 'ws2_32'] -else: - LIBS = ['nghttp2'] - -setup( - name = 'python-nghttp2', - description = 'Python HTTP/2 library on top of nghttp2', - author = 'Tatsuhiro Tsujikawa', - version = '@PACKAGE_VERSION@', - author_email = 'tatsuhiro.t@gmail.com', - url = 'https://nghttp2.org/', - keywords = [], - ext_modules = [Extension("nghttp2", - ["nghttp2.c"], - include_dirs=['@top_srcdir@/lib', - '@top_srcdir@/lib/includes', - '@top_builddir@/lib/includes'], - library_dirs=['@top_builddir@/lib/.libs'], - libraries=LIBS)], - long_description='TBD' - ) diff --git a/src/HtmlParser.cc b/src/HtmlParser.cc deleted file mode 100644 index bcbfd13..0000000 --- a/src/HtmlParser.cc +++ /dev/null @@ -1,187 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2012 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#include "HtmlParser.h" - -#include - -#include "util.h" - -namespace nghttp2 { - -ParserData::ParserData(const std::string &base_uri) - : base_uri(base_uri), inside_head(0) {} - -HtmlParser::HtmlParser(const std::string &base_uri) - : base_uri_(base_uri), parser_ctx_(nullptr), parser_data_(base_uri) {} - -HtmlParser::~HtmlParser() { htmlFreeParserCtxt(parser_ctx_); } - -namespace { -const char *get_attr(const xmlChar **attrs, const char *name) { - if (attrs == nullptr) { - return nullptr; - } - for (; *attrs; attrs += 2) { - if (util::strieq(reinterpret_cast(attrs[0]), name)) { - return reinterpret_cast(attrs[1]); - } - } - return nullptr; -} -} // namespace - -namespace { -void add_link(ParserData *parser_data, const char *uri, ResourceType res_type) { - auto u = xmlBuildURI( - reinterpret_cast(uri), - reinterpret_cast(parser_data->base_uri.c_str())); - if (u) { - parser_data->links.push_back( - std::make_pair(reinterpret_cast(u), res_type)); - free(u); - } -} -} // namespace - -namespace { -void start_element_func(void *user_data, const xmlChar *name, - const xmlChar **attrs) { - auto parser_data = static_cast(user_data); - if (util::strieq(reinterpret_cast(name), "head")) { - ++parser_data->inside_head; - } - if (util::strieq(reinterpret_cast(name), "link")) { - auto rel_attr = get_attr(attrs, "rel"); - auto href_attr = get_attr(attrs, "href"); - if (!href_attr) { - return; - } - if (util::strieq(rel_attr, "shortcut icon")) { - add_link(parser_data, href_attr, REQ_OTHERS); - } else if (util::strieq(rel_attr, "stylesheet")) { - add_link(parser_data, href_attr, REQ_CSS); - } - } else if (util::strieq(reinterpret_cast(name), "img")) { - auto src_attr = get_attr(attrs, "src"); - if (!src_attr) { - return; - } - add_link(parser_data, src_attr, REQ_IMG); - } else if (util::strieq(reinterpret_cast(name), "script")) { - auto src_attr = get_attr(attrs, "src"); - if (!src_attr) { - return; - } - if (parser_data->inside_head) { - add_link(parser_data, src_attr, REQ_JS); - } else { - add_link(parser_data, src_attr, REQ_UNBLOCK_JS); - } - } -} -} // namespace - -namespace { -void end_element_func(void *user_data, const xmlChar *name) { - auto parser_data = static_cast(user_data); - if (util::strieq(reinterpret_cast(name), "head")) { - --parser_data->inside_head; - } -} -} // namespace - -namespace { -xmlSAXHandler saxHandler = { - nullptr, // internalSubsetSAXFunc - nullptr, // isStandaloneSAXFunc - nullptr, // hasInternalSubsetSAXFunc - nullptr, // hasExternalSubsetSAXFunc - nullptr, // resolveEntitySAXFunc - nullptr, // getEntitySAXFunc - nullptr, // entityDeclSAXFunc - nullptr, // notationDeclSAXFunc - nullptr, // attributeDeclSAXFunc - nullptr, // elementDeclSAXFunc - nullptr, // unparsedEntityDeclSAXFunc - nullptr, // setDocumentLocatorSAXFunc - nullptr, // startDocumentSAXFunc - nullptr, // endDocumentSAXFunc - &start_element_func, // startElementSAXFunc - &end_element_func, // endElementSAXFunc - nullptr, // referenceSAXFunc - nullptr, // charactersSAXFunc - nullptr, // ignorableWhitespaceSAXFunc - nullptr, // processingInstructionSAXFunc - nullptr, // commentSAXFunc - nullptr, // warningSAXFunc - nullptr, // errorSAXFunc - nullptr, // fatalErrorSAXFunc - nullptr, // getParameterEntitySAXFunc - nullptr, // cdataBlockSAXFunc - nullptr, // externalSubsetSAXFunc - 0, // unsigned int initialized - nullptr, // void * _private - nullptr, // startElementNsSAX2Func - nullptr, // endElementNsSAX2Func - nullptr, // xmlStructuredErrorFunc -}; -} // namespace - -int HtmlParser::parse_chunk(const char *chunk, size_t size, int fin) { - if (!parser_ctx_) { - parser_ctx_ = - htmlCreatePushParserCtxt(&saxHandler, &parser_data_, chunk, size, - base_uri_.c_str(), XML_CHAR_ENCODING_NONE); - if (!parser_ctx_) { - return -1; - } else { - if (fin) { - return parse_chunk_internal(nullptr, 0, fin); - } else { - return 0; - } - } - } else { - return parse_chunk_internal(chunk, size, fin); - } -} - -int HtmlParser::parse_chunk_internal(const char *chunk, size_t size, int fin) { - int rv = htmlParseChunk(parser_ctx_, chunk, size, fin); - if (rv == 0) { - return 0; - } else { - return -1; - } -} - -const std::vector> & -HtmlParser::get_links() const { - return parser_data_.links; -} - -void HtmlParser::clear_links() { parser_data_.links.clear(); } - -} // namespace nghttp2 diff --git a/src/HtmlParser.h b/src/HtmlParser.h deleted file mode 100644 index fc2f2e5..0000000 --- a/src/HtmlParser.h +++ /dev/null @@ -1,94 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2012 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#ifndef HTML_PARSER_H -#define HTML_PARSER_H - -#include "nghttp2_config.h" - -#include -#include - -#ifdef HAVE_LIBXML2 - -#include - -#endif // HAVE_LIBXML2 - -namespace nghttp2 { - -enum ResourceType { - REQ_CSS = 1, - REQ_JS, - REQ_UNBLOCK_JS, - REQ_IMG, - REQ_OTHERS, -}; - -struct ParserData { - std::string base_uri; - std::vector> links; - // > 0 if we are inside "head" element. - int inside_head; - ParserData(const std::string &base_uri); -}; - -#ifdef HAVE_LIBXML2 - -class HtmlParser { -public: - HtmlParser(const std::string &base_uri); - ~HtmlParser(); - int parse_chunk(const char *chunk, size_t size, int fin); - const std::vector> &get_links() const; - void clear_links(); - -private: - int parse_chunk_internal(const char *chunk, size_t size, int fin); - - std::string base_uri_; - htmlParserCtxtPtr parser_ctx_; - ParserData parser_data_; -}; - -#else // !HAVE_LIBXML2 - -class HtmlParser { -public: - HtmlParser(const std::string &base_uri) {} - int parse_chunk(const char *chunk, size_t size, int fin) { return 0; } - const std::vector> &get_links() const { - return links_; - } - void clear_links() {} - -private: - std::vector> links_; -}; - -#endif // !HAVE_LIBXML2 - -} // namespace nghttp2 - -#endif // HTML_PARSER_H diff --git a/src/HttpServer.cc b/src/HttpServer.cc deleted file mode 100644 index 0465395..0000000 --- a/src/HttpServer.cc +++ /dev/null @@ -1,1889 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2013 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#include "HttpServer.h" - -#include -#ifdef HAVE_SYS_SOCKET_H -#include -#endif // HAVE_SYS_SOCKET_H -#ifdef HAVE_NETDB_H -#include -#endif // HAVE_NETDB_H -#ifdef HAVE_UNISTD_H -#include -#endif // HAVE_UNISTD_H -#ifdef HAVE_FCNTL_H -#include -#endif // HAVE_FCNTL_H -#ifdef HAVE_NETINET_IN_H -#include -#endif // HAVE_NETINET_IN_H -#include -#ifdef HAVE_ARPA_INET_H -#include -#endif // HAVE_ARPA_INET_H - -#include -#include -#include -#include -#include -#include - -#include - -#include - -#include "app_helper.h" -#include "http2.h" -#include "util.h" -#include "ssl.h" -#include "template.h" - -#ifndef O_BINARY -#define O_BINARY (0) -#endif // O_BINARY - -namespace nghttp2 { - -namespace { -const std::string DEFAULT_HTML = "index.html"; -const std::string NGHTTPD_SERVER = "nghttpd nghttp2/" NGHTTP2_VERSION; -} // namespace - -namespace { -void delete_handler(Http2Handler *handler) { - handler->remove_self(); - delete handler; -} -} // namespace - -namespace { -void print_session_id(int64_t id) { std::cout << "[id=" << id << "] "; } -} // namespace - -namespace { -template void append_nv(Stream *stream, const Array &nva) { - for (size_t i = 0; i < nva.size(); ++i) { - auto &nv = nva[i]; - auto token = http2::lookup_token(nv.name, nv.namelen); - if (token != -1) { - http2::index_header(stream->hdidx, token, i); - } - http2::add_header(stream->headers, nv.name, nv.namelen, nv.value, - nv.valuelen, nv.flags & NGHTTP2_NV_FLAG_NO_INDEX, token); - } -} -} // namespace - -Config::Config() - : stream_read_timeout(60.), stream_write_timeout(60.), data_ptr(nullptr), - padding(0), num_worker(1), max_concurrent_streams(100), - header_table_size(-1), port(0), verbose(false), daemon(false), - verify_client(false), no_tls(false), error_gzip(false), - early_response(false), hexdump(false), echo_upload(false) {} - -Config::~Config() {} - -namespace { -void stream_timeout_cb(struct ev_loop *loop, ev_timer *w, int revents) { - int rv; - auto stream = static_cast(w->data); - auto hd = stream->handler; - auto config = hd->get_config(); - - ev_timer_stop(hd->get_loop(), &stream->rtimer); - ev_timer_stop(hd->get_loop(), &stream->wtimer); - - if (config->verbose) { - print_session_id(hd->session_id()); - print_timer(); - std::cout << " timeout stream_id=" << stream->stream_id << std::endl; - } - - hd->submit_rst_stream(stream, NGHTTP2_INTERNAL_ERROR); - - rv = hd->on_write(); - if (rv == -1) { - delete_handler(hd); - } -} -} // namespace - -namespace { -void add_stream_read_timeout(Stream *stream) { - auto hd = stream->handler; - ev_timer_again(hd->get_loop(), &stream->rtimer); -} -} // namespace - -namespace { -void add_stream_read_timeout_if_pending(Stream *stream) { - auto hd = stream->handler; - if (ev_is_active(&stream->rtimer)) { - ev_timer_again(hd->get_loop(), &stream->rtimer); - } -} -} // namespace - -namespace { -void add_stream_write_timeout(Stream *stream) { - auto hd = stream->handler; - ev_timer_again(hd->get_loop(), &stream->wtimer); -} -} // namespace - -namespace { -void remove_stream_read_timeout(Stream *stream) { - auto hd = stream->handler; - ev_timer_stop(hd->get_loop(), &stream->rtimer); -} -} // namespace - -namespace { -void remove_stream_write_timeout(Stream *stream) { - auto hd = stream->handler; - ev_timer_stop(hd->get_loop(), &stream->wtimer); -} -} // namespace - -namespace { -void fill_callback(nghttp2_session_callbacks *callbacks, const Config *config); -} // namespace - -class Sessions { -public: - Sessions(HttpServer *sv, struct ev_loop *loop, const Config *config, - SSL_CTX *ssl_ctx) - : sv_(sv), loop_(loop), config_(config), ssl_ctx_(ssl_ctx), - callbacks_(nullptr), next_session_id_(1), tstamp_cached_(ev_now(loop)), - cached_date_(util::http_date(tstamp_cached_)) { - nghttp2_session_callbacks_new(&callbacks_); - - fill_callback(callbacks_, config_); - } - ~Sessions() { - for (auto handler : handlers_) { - delete handler; - } - nghttp2_session_callbacks_del(callbacks_); - } - void add_handler(Http2Handler *handler) { handlers_.insert(handler); } - void remove_handler(Http2Handler *handler) { handlers_.erase(handler); } - SSL_CTX *get_ssl_ctx() const { return ssl_ctx_; } - SSL *ssl_session_new(int fd) { - SSL *ssl = SSL_new(ssl_ctx_); - if (!ssl) { - std::cerr << "SSL_new() failed" << std::endl; - return nullptr; - } - if (SSL_set_fd(ssl, fd) == 0) { - std::cerr << "SSL_set_fd() failed" << std::endl; - SSL_free(ssl); - return nullptr; - } - return ssl; - } - const Config *get_config() const { return config_; } - struct ev_loop *get_loop() const { - return loop_; - } - int64_t get_next_session_id() { - auto session_id = next_session_id_; - if (next_session_id_ == std::numeric_limits::max()) { - next_session_id_ = 1; - } else { - ++next_session_id_; - } - return session_id; - } - const nghttp2_session_callbacks *get_callbacks() const { return callbacks_; } - void accept_connection(int fd) { - util::make_socket_nodelay(fd); - SSL *ssl = nullptr; - if (ssl_ctx_) { - ssl = ssl_session_new(fd); - if (!ssl) { - close(fd); - return; - } - } - auto handler = - make_unique(this, fd, ssl, get_next_session_id()); - handler->setup_bev(); - if (!ssl) { - if (handler->connection_made() != 0) { - return; - } - } - add_handler(handler.release()); - } - void update_cached_date() { cached_date_ = util::http_date(tstamp_cached_); } - const std::string &get_cached_date() { - auto t = ev_now(loop_); - if (t != tstamp_cached_) { - tstamp_cached_ = t; - update_cached_date(); - } - return cached_date_; - } - FileEntry *get_cached_fd(const std::string &path) { - auto i = fd_cache_.find(path); - if (i == std::end(fd_cache_)) { - return nullptr; - } - auto &ent = (*i).second; - ++ent.usecount; - return &ent; - } - FileEntry *cache_fd(const std::string &path, const FileEntry &ent) { - auto rv = fd_cache_.emplace(path, ent); - return &(*rv.first).second; - } - void release_fd(const std::string &path) { - auto i = fd_cache_.find(path); - if (i == std::end(fd_cache_)) { - return; - } - auto &ent = (*i).second; - if (--ent.usecount == 0) { - close(ent.fd); - fd_cache_.erase(i); - } - } - const HttpServer *get_server() const { return sv_; } - -private: - std::set handlers_; - // cache for file descriptors to read file. - std::map fd_cache_; - HttpServer *sv_; - struct ev_loop *loop_; - const Config *config_; - SSL_CTX *ssl_ctx_; - nghttp2_session_callbacks *callbacks_; - int64_t next_session_id_; - ev_tstamp tstamp_cached_; - std::string cached_date_; -}; - -Stream::Stream(Http2Handler *handler, int32_t stream_id) - : handler(handler), file_ent(nullptr), body_length(0), body_offset(0), - stream_id(stream_id), echo_upload(false) { - auto config = handler->get_config(); - ev_timer_init(&rtimer, stream_timeout_cb, 0., config->stream_read_timeout); - ev_timer_init(&wtimer, stream_timeout_cb, 0., config->stream_write_timeout); - rtimer.data = this; - wtimer.data = this; - - headers.reserve(10); - - http2::init_hdidx(hdidx); -} - -Stream::~Stream() { - if (file_ent != nullptr) { - auto sessions = handler->get_sessions(); - sessions->release_fd(file_ent->path); - } - - auto loop = handler->get_loop(); - ev_timer_stop(loop, &rtimer); - ev_timer_stop(loop, &wtimer); -} - -namespace { -void on_session_closed(Http2Handler *hd, int64_t session_id) { - if (hd->get_config()->verbose) { - print_session_id(session_id); - print_timer(); - std::cout << " closed" << std::endl; - } -} -} // namespace - -namespace { -void settings_timeout_cb(struct ev_loop *loop, ev_timer *w, int revents) { - auto hd = static_cast(w->data); - hd->terminate_session(NGHTTP2_SETTINGS_TIMEOUT); - hd->on_write(); -} -} // namespace - -namespace { -void readcb(struct ev_loop *loop, ev_io *w, int revents) { - int rv; - auto handler = static_cast(w->data); - - rv = handler->on_read(); - if (rv == -1) { - delete_handler(handler); - } -} -} // namespace - -namespace { -void writecb(struct ev_loop *loop, ev_io *w, int revents) { - int rv; - auto handler = static_cast(w->data); - - rv = handler->on_write(); - if (rv == -1) { - delete_handler(handler); - } -} -} // namespace - -Http2Handler::Http2Handler(Sessions *sessions, int fd, SSL *ssl, - int64_t session_id) - : session_id_(session_id), session_(nullptr), sessions_(sessions), - ssl_(ssl), data_pending_(nullptr), data_pendinglen_(0), fd_(fd) { - ev_timer_init(&settings_timerev_, settings_timeout_cb, 10., 0.); - ev_io_init(&wev_, writecb, fd, EV_WRITE); - ev_io_init(&rev_, readcb, fd, EV_READ); - - settings_timerev_.data = this; - wev_.data = this; - rev_.data = this; - - auto loop = sessions_->get_loop(); - ev_io_start(loop, &rev_); - - if (ssl) { - SSL_set_accept_state(ssl); - read_ = &Http2Handler::tls_handshake; - write_ = &Http2Handler::tls_handshake; - } else { - read_ = &Http2Handler::read_clear; - write_ = &Http2Handler::write_clear; - } -} - -Http2Handler::~Http2Handler() { - on_session_closed(this, session_id_); - nghttp2_session_del(session_); - if (ssl_) { - SSL_set_shutdown(ssl_, SSL_RECEIVED_SHUTDOWN); - ERR_clear_error(); - SSL_shutdown(ssl_); - } - auto loop = sessions_->get_loop(); - ev_timer_stop(loop, &settings_timerev_); - ev_io_stop(loop, &rev_); - ev_io_stop(loop, &wev_); - if (ssl_) { - SSL_free(ssl_); - } - shutdown(fd_, SHUT_WR); - close(fd_); -} - -void Http2Handler::remove_self() { sessions_->remove_handler(this); } - -struct ev_loop *Http2Handler::get_loop() const { - return sessions_->get_loop(); -} - -Http2Handler::WriteBuf *Http2Handler::get_wb() { return &wb_; } - -int Http2Handler::setup_bev() { return 0; } - -int Http2Handler::fill_wb() { - if (data_pending_) { - auto n = std::min(wb_.wleft(), data_pendinglen_); - wb_.write(data_pending_, n); - if (n < data_pendinglen_) { - data_pending_ += n; - data_pendinglen_ -= n; - return 0; - } - - data_pending_ = nullptr; - data_pendinglen_ = 0; - } - - for (;;) { - const uint8_t *data; - auto datalen = nghttp2_session_mem_send(session_, &data); - - if (datalen < 0) { - std::cerr << "nghttp2_session_mem_send() returned error: " - << nghttp2_strerror(datalen) << std::endl; - return -1; - } - if (datalen == 0) { - break; - } - auto n = wb_.write(data, datalen); - if (n < static_cast(datalen)) { - data_pending_ = data + n; - data_pendinglen_ = datalen - n; - break; - } - } - return 0; -} - -int Http2Handler::read_clear() { - int rv; - std::array buf; - - for (;;) { - ssize_t nread; - while ((nread = read(fd_, buf.data(), buf.size())) == -1 && errno == EINTR) - ; - if (nread == -1) { - if (errno == EAGAIN || errno == EWOULDBLOCK) { - break; - } - return -1; - } - if (nread == 0) { - return -1; - } - - if (get_config()->hexdump) { - util::hexdump(stdout, buf.data(), nread); - } - - rv = nghttp2_session_mem_recv(session_, buf.data(), nread); - if (rv < 0) { - if (rv != NGHTTP2_ERR_BAD_CLIENT_MAGIC) { - std::cerr << "nghttp2_session_mem_recv() returned error: " - << nghttp2_strerror(rv) << std::endl; - } - return -1; - } - } - - return write_(*this); -} - -int Http2Handler::write_clear() { - auto loop = sessions_->get_loop(); - for (;;) { - if (wb_.rleft() > 0) { - ssize_t nwrite; - while ((nwrite = write(fd_, wb_.pos, wb_.rleft())) == -1 && - errno == EINTR) - ; - if (nwrite == -1) { - if (errno == EAGAIN || errno == EWOULDBLOCK) { - ev_io_start(loop, &wev_); - return 0; - } - return -1; - } - wb_.drain(nwrite); - continue; - } - wb_.reset(); - if (fill_wb() != 0) { - return -1; - } - if (wb_.rleft() == 0) { - break; - } - } - - if (wb_.rleft() == 0) { - ev_io_stop(loop, &wev_); - } else { - ev_io_start(loop, &wev_); - } - - if (nghttp2_session_want_read(session_) == 0 && - nghttp2_session_want_write(session_) == 0 && wb_.rleft() == 0) { - return -1; - } - - return 0; -} - -int Http2Handler::tls_handshake() { - ev_io_stop(sessions_->get_loop(), &wev_); - - ERR_clear_error(); - - auto rv = SSL_do_handshake(ssl_); - - if (rv == 0) { - return -1; - } - - if (rv < 0) { - auto err = SSL_get_error(ssl_, rv); - switch (err) { - case SSL_ERROR_WANT_READ: - return 0; - case SSL_ERROR_WANT_WRITE: - ev_io_start(sessions_->get_loop(), &wev_); - return 0; - default: - return -1; - } - } - - if (sessions_->get_config()->verbose) { - std::cerr << "SSL/TLS handshake completed" << std::endl; - } - - if (verify_npn_result() != 0) { - return -1; - } - - read_ = &Http2Handler::read_tls; - write_ = &Http2Handler::write_tls; - - if (connection_made() != 0) { - return -1; - } - - return 0; -} - -int Http2Handler::read_tls() { - std::array buf; - - ERR_clear_error(); - - for (;;) { - auto rv = SSL_read(ssl_, buf.data(), buf.size()); - - if (rv == 0) { - return -1; - } - - if (rv < 0) { - auto err = SSL_get_error(ssl_, rv); - switch (err) { - case SSL_ERROR_WANT_READ: - goto fin; - case SSL_ERROR_WANT_WRITE: - // renegotiation started - return -1; - default: - return -1; - } - } - - auto nread = rv; - - if (get_config()->hexdump) { - util::hexdump(stdout, buf.data(), nread); - } - - rv = nghttp2_session_mem_recv(session_, buf.data(), nread); - if (rv < 0) { - if (rv != NGHTTP2_ERR_BAD_CLIENT_MAGIC) { - std::cerr << "nghttp2_session_mem_recv() returned error: " - << nghttp2_strerror(rv) << std::endl; - } - return -1; - } - } - -fin: - return write_(*this); -} - -int Http2Handler::write_tls() { - auto loop = sessions_->get_loop(); - - ERR_clear_error(); - - for (;;) { - if (wb_.rleft() > 0) { - auto rv = SSL_write(ssl_, wb_.pos, wb_.rleft()); - - if (rv == 0) { - return -1; - } - - if (rv < 0) { - auto err = SSL_get_error(ssl_, rv); - switch (err) { - case SSL_ERROR_WANT_READ: - // renegotiation started - return -1; - case SSL_ERROR_WANT_WRITE: - ev_io_start(sessions_->get_loop(), &wev_); - return 0; - default: - return -1; - } - } - - wb_.drain(rv); - continue; - } - wb_.reset(); - if (fill_wb() != 0) { - return -1; - } - if (wb_.rleft() == 0) { - break; - } - } - - if (wb_.rleft() == 0) { - ev_io_stop(loop, &wev_); - } else { - ev_io_start(loop, &wev_); - } - - if (nghttp2_session_want_read(session_) == 0 && - nghttp2_session_want_write(session_) == 0 && wb_.rleft() == 0) { - return -1; - } - - return 0; -} - -int Http2Handler::on_read() { return read_(*this); } - -int Http2Handler::on_write() { return write_(*this); } - -int Http2Handler::connection_made() { - int r; - - r = nghttp2_session_server_new(&session_, sessions_->get_callbacks(), this); - - if (r != 0) { - return r; - } - - auto config = sessions_->get_config(); - std::array entry; - size_t niv = 1; - - entry[0].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS; - entry[0].value = config->max_concurrent_streams; - - if (config->header_table_size >= 0) { - entry[niv].settings_id = NGHTTP2_SETTINGS_HEADER_TABLE_SIZE; - entry[niv].value = config->header_table_size; - ++niv; - } - r = nghttp2_submit_settings(session_, NGHTTP2_FLAG_NONE, entry.data(), niv); - if (r != 0) { - return r; - } - - ev_timer_start(sessions_->get_loop(), &settings_timerev_); - - return on_write(); -} - -int Http2Handler::verify_npn_result() { - const unsigned char *next_proto = nullptr; - unsigned int next_proto_len; - // Check the negotiated protocol in NPN or ALPN - SSL_get0_next_proto_negotiated(ssl_, &next_proto, &next_proto_len); - for (int i = 0; i < 2; ++i) { - if (next_proto) { - if (sessions_->get_config()->verbose) { - std::string proto(next_proto, next_proto + next_proto_len); - std::cout << "The negotiated protocol: " << proto << std::endl; - } - if (util::check_h2_is_selected(next_proto, next_proto_len)) { - return 0; - } - break; - } else { -#if OPENSSL_VERSION_NUMBER >= 0x10002000L - SSL_get0_alpn_selected(ssl_, &next_proto, &next_proto_len); -#else // OPENSSL_VERSION_NUMBER < 0x10002000L - break; -#endif // OPENSSL_VERSION_NUMBER < 0x10002000L - } - } - if (sessions_->get_config()->verbose) { - std::cerr << "Client did not advertise HTTP/2 protocol." - << " (nghttp2 expects " << NGHTTP2_PROTO_VERSION_ID << ")" - << std::endl; - } - return -1; -} - -int Http2Handler::submit_file_response(const std::string &status, - Stream *stream, time_t last_modified, - off_t file_length, - nghttp2_data_provider *data_prd) { - std::string content_length = util::utos(file_length); - std::string last_modified_str; - auto nva = make_array(http2::make_nv_ls(":status", status), - http2::make_nv_ls("server", NGHTTPD_SERVER), - http2::make_nv_ls("content-length", content_length), - http2::make_nv_ll("cache-control", "max-age=3600"), - http2::make_nv_ls("date", sessions_->get_cached_date()), - http2::make_nv_ll("", ""), http2::make_nv_ll("", "")); - size_t nvlen = 5; - if (last_modified != 0) { - last_modified_str = util::http_date(last_modified); - nva[nvlen++] = http2::make_nv_ls("last-modified", last_modified_str); - } - auto &trailer = get_config()->trailer; - std::string trailer_names; - if (!trailer.empty()) { - trailer_names = trailer[0].name; - for (size_t i = 1; i < trailer.size(); ++i) { - trailer_names += ", "; - trailer_names += trailer[i].name; - } - nva[nvlen++] = http2::make_nv_ls("trailer", trailer_names); - } - return nghttp2_submit_response(session_, stream->stream_id, nva.data(), nvlen, - data_prd); -} - -int Http2Handler::submit_response(const std::string &status, int32_t stream_id, - const Headers &headers, - nghttp2_data_provider *data_prd) { - auto nva = std::vector(); - nva.reserve(3 + headers.size()); - nva.push_back(http2::make_nv_ls(":status", status)); - nva.push_back(http2::make_nv_ls("server", NGHTTPD_SERVER)); - nva.push_back(http2::make_nv_ls("date", sessions_->get_cached_date())); - for (auto &nv : headers) { - nva.push_back(http2::make_nv(nv.name, nv.value, nv.no_index)); - } - int r = nghttp2_submit_response(session_, stream_id, nva.data(), nva.size(), - data_prd); - return r; -} - -int Http2Handler::submit_response(const std::string &status, int32_t stream_id, - nghttp2_data_provider *data_prd) { - auto nva = make_array(http2::make_nv_ls(":status", status), - http2::make_nv_ls("server", NGHTTPD_SERVER)); - return nghttp2_submit_response(session_, stream_id, nva.data(), nva.size(), - data_prd); -} - -int Http2Handler::submit_non_final_response(const std::string &status, - int32_t stream_id) { - auto nva = make_array(http2::make_nv_ls(":status", status)); - return nghttp2_submit_headers(session_, NGHTTP2_FLAG_NONE, stream_id, nullptr, - nva.data(), nva.size(), nullptr); -} - -int Http2Handler::submit_push_promise(Stream *stream, - const std::string &push_path) { - auto authority = - http2::get_header(stream->hdidx, http2::HD__AUTHORITY, stream->headers); - - if (!authority) { - authority = - http2::get_header(stream->hdidx, http2::HD_HOST, stream->headers); - } - - auto nva = - make_array(http2::make_nv_ll(":method", "GET"), - http2::make_nv_ls(":path", push_path), - get_config()->no_tls ? http2::make_nv_ll(":scheme", "http") - : http2::make_nv_ll(":scheme", "https"), - http2::make_nv_ls(":authority", authority->value)); - - auto promised_stream_id = nghttp2_submit_push_promise( - session_, NGHTTP2_FLAG_END_HEADERS, stream->stream_id, nva.data(), - nva.size(), nullptr); - - if (promised_stream_id < 0) { - return promised_stream_id; - } - - auto promised_stream = make_unique(this, promised_stream_id); - - append_nv(promised_stream.get(), nva); - add_stream(promised_stream_id, std::move(promised_stream)); - - return 0; -} - -int Http2Handler::submit_rst_stream(Stream *stream, uint32_t error_code) { - remove_stream_read_timeout(stream); - remove_stream_write_timeout(stream); - - return nghttp2_submit_rst_stream(session_, NGHTTP2_FLAG_NONE, - stream->stream_id, error_code); -} - -void Http2Handler::add_stream(int32_t stream_id, - std::unique_ptr stream) { - id2stream_[stream_id] = std::move(stream); -} - -void Http2Handler::remove_stream(int32_t stream_id) { - id2stream_.erase(stream_id); -} - -Stream *Http2Handler::get_stream(int32_t stream_id) { - auto itr = id2stream_.find(stream_id); - if (itr == std::end(id2stream_)) { - return nullptr; - } else { - return (*itr).second.get(); - } -} - -int64_t Http2Handler::session_id() const { return session_id_; } - -Sessions *Http2Handler::get_sessions() const { return sessions_; } - -const Config *Http2Handler::get_config() const { - return sessions_->get_config(); -} - -void Http2Handler::remove_settings_timer() { - ev_timer_stop(sessions_->get_loop(), &settings_timerev_); -} - -void Http2Handler::terminate_session(uint32_t error_code) { - nghttp2_session_terminate_session(session_, error_code); -} - -ssize_t file_read_callback(nghttp2_session *session, int32_t stream_id, - uint8_t *buf, size_t length, uint32_t *data_flags, - nghttp2_data_source *source, void *user_data) { - int rv; - auto hd = static_cast(user_data); - auto stream = hd->get_stream(stream_id); - - auto nread = std::min(stream->body_length - stream->body_offset, - static_cast(length)); - - *data_flags |= NGHTTP2_DATA_FLAG_NO_COPY; - - if (nread == 0 || stream->body_length == stream->body_offset + nread) { - *data_flags |= NGHTTP2_DATA_FLAG_EOF; - - auto config = hd->get_config(); - if (!config->trailer.empty()) { - std::vector nva; - nva.reserve(config->trailer.size()); - for (auto &kv : config->trailer) { - nva.push_back(http2::make_nv(kv.name, kv.value, kv.no_index)); - } - rv = nghttp2_submit_trailer(session, stream_id, nva.data(), nva.size()); - if (rv != 0) { - if (nghttp2_is_fatal(rv)) { - return NGHTTP2_ERR_CALLBACK_FAILURE; - } - } else { - *data_flags |= NGHTTP2_DATA_FLAG_NO_END_STREAM; - } - } - - if (nghttp2_session_get_stream_remote_close(session, stream_id) == 0) { - remove_stream_read_timeout(stream); - remove_stream_write_timeout(stream); - - hd->submit_rst_stream(stream, NGHTTP2_NO_ERROR); - } - } - - return nread; -} - -namespace { -void prepare_status_response(Stream *stream, Http2Handler *hd, int status) { - auto sessions = hd->get_sessions(); - auto status_page = sessions->get_server()->get_status_page(status); - auto file_ent = &status_page->file_ent; - - // we don't set stream->file_ent since we don't want to expire it. - stream->body_length = file_ent->length; - nghttp2_data_provider data_prd; - data_prd.source.fd = file_ent->fd; - data_prd.read_callback = file_read_callback; - - Headers headers; - headers.emplace_back("content-type", "text/html; charset=UTF-8"); - hd->submit_response(status_page->status, stream->stream_id, headers, - &data_prd); -} -} // namespace - -namespace { -void prepare_echo_response(Stream *stream, Http2Handler *hd) { - auto length = lseek(stream->file_ent->fd, 0, SEEK_END); - if (length == -1) { - hd->submit_rst_stream(stream, NGHTTP2_INTERNAL_ERROR); - return; - } - stream->body_length = length; - if (lseek(stream->file_ent->fd, 0, SEEK_SET) == -1) { - hd->submit_rst_stream(stream, NGHTTP2_INTERNAL_ERROR); - return; - } - nghttp2_data_provider data_prd; - data_prd.source.fd = stream->file_ent->fd; - data_prd.read_callback = file_read_callback; - - Headers headers; - headers.emplace_back("nghttpd-response", "echo"); - headers.emplace_back("content-length", util::utos(length)); - - hd->submit_response("200", stream->stream_id, headers, &data_prd); -} -} // namespace - -namespace { -bool prepare_upload_temp_store(Stream *stream, Http2Handler *hd) { - auto sessions = hd->get_sessions(); - - char tempfn[] = "/tmp/nghttpd.temp.XXXXXX"; - auto fd = mkstemp(tempfn); - if (fd == -1) { - return false; - } - unlink(tempfn); - // Ordinary request never start with "echo:". The length is 0 for - // now. We will update it when we get whole request body. - stream->file_ent = sessions->cache_fd(std::string("echo:") + tempfn, - FileEntry(tempfn, 0, 0, fd)); - stream->echo_upload = true; - return true; -} -} // namespace - -namespace { -void prepare_redirect_response(Stream *stream, Http2Handler *hd, - const std::string &path, int status) { - auto scheme = - http2::get_header(stream->hdidx, http2::HD__SCHEME, stream->headers); - auto authority = - http2::get_header(stream->hdidx, http2::HD__AUTHORITY, stream->headers); - if (!authority) { - authority = - http2::get_header(stream->hdidx, http2::HD_HOST, stream->headers); - } - - auto redirect_url = scheme->value; - redirect_url += "://"; - redirect_url += authority->value; - redirect_url += path; - - auto headers = Headers{{"location", redirect_url}}; - - auto sessions = hd->get_sessions(); - auto status_page = sessions->get_server()->get_status_page(status); - - hd->submit_response(status_page->status, stream->stream_id, headers, nullptr); -} -} // namespace - -namespace { -void prepare_response(Stream *stream, Http2Handler *hd, - bool allow_push = true) { - int rv; - auto reqpath = - http2::get_header(stream->hdidx, http2::HD__PATH, stream->headers)->value; - auto ims = - get_header(stream->hdidx, http2::HD_IF_MODIFIED_SINCE, stream->headers); - - time_t last_mod = 0; - bool last_mod_found = false; - if (ims) { - last_mod_found = true; - last_mod = util::parse_http_date(ims->value); - } - auto query_pos = reqpath.find("?"); - std::string url; - if (query_pos != std::string::npos) { - // Do not response to this request to allow clients to test timeouts. - if (reqpath.find("nghttpd_do_not_respond_to_req=yes", query_pos) != - std::string::npos) { - return; - } - url = reqpath.substr(0, query_pos); - } else { - url = reqpath; - } - - auto sessions = hd->get_sessions(); - - url = util::percentDecode(std::begin(url), std::end(url)); - if (!util::check_path(url)) { - if (stream->file_ent) { - sessions->release_fd(stream->file_ent->path); - stream->file_ent = nullptr; - } - prepare_status_response(stream, hd, 404); - return; - } - auto push_itr = hd->get_config()->push.find(url); - if (allow_push && push_itr != std::end(hd->get_config()->push)) { - for (auto &push_path : (*push_itr).second) { - rv = hd->submit_push_promise(stream, push_path); - if (rv != 0) { - std::cerr << "nghttp2_submit_push_promise() returned error: " - << nghttp2_strerror(rv) << std::endl; - } - } - } - - std::string path = hd->get_config()->htdocs + url; - if (path[path.size() - 1] == '/') { - path += DEFAULT_HTML; - } - - if (stream->echo_upload) { - assert(stream->file_ent); - prepare_echo_response(stream, hd); - return; - } - - auto file_ent = sessions->get_cached_fd(path); - - if (file_ent == nullptr) { - int file = open(path.c_str(), O_RDONLY | O_BINARY); - if (file == -1) { - prepare_status_response(stream, hd, 404); - - return; - } - - struct stat buf; - - if (fstat(file, &buf) == -1) { - close(file); - prepare_status_response(stream, hd, 404); - - return; - } - - if (buf.st_mode & S_IFDIR) { - close(file); - - if (query_pos == std::string::npos) { - reqpath += "/"; - } else { - reqpath.insert(query_pos, "/"); - } - - prepare_redirect_response(stream, hd, reqpath, 301); - - return; - } - - if (last_mod_found && buf.st_mtime <= last_mod) { - close(file); - prepare_status_response(stream, hd, 304); - - return; - } - - file_ent = sessions->cache_fd( - path, FileEntry(path, buf.st_size, buf.st_mtime, file)); - } else if (last_mod_found && file_ent->mtime <= last_mod) { - sessions->release_fd(file_ent->path); - prepare_status_response(stream, hd, 304); - - return; - } - - stream->file_ent = file_ent; - stream->body_length = file_ent->length; - - nghttp2_data_provider data_prd; - - data_prd.source.fd = file_ent->fd; - data_prd.read_callback = file_read_callback; - - hd->submit_file_response("200", stream, file_ent->mtime, file_ent->length, - &data_prd); -} -} // namespace - -namespace { -int on_header_callback(nghttp2_session *session, const nghttp2_frame *frame, - const uint8_t *name, size_t namelen, - const uint8_t *value, size_t valuelen, uint8_t flags, - void *user_data) { - auto hd = static_cast(user_data); - if (hd->get_config()->verbose) { - print_session_id(hd->session_id()); - verbose_on_header_callback(session, frame, name, namelen, value, valuelen, - flags, user_data); - } - if (frame->hd.type != NGHTTP2_HEADERS || - frame->headers.cat != NGHTTP2_HCAT_REQUEST) { - return 0; - } - auto stream = hd->get_stream(frame->hd.stream_id); - if (!stream) { - return 0; - } - - auto token = http2::lookup_token(name, namelen); - - http2::index_header(stream->hdidx, token, stream->headers.size()); - http2::add_header(stream->headers, name, namelen, value, valuelen, - flags & NGHTTP2_NV_FLAG_NO_INDEX, token); - return 0; -} -} // namespace - -namespace { -int on_begin_headers_callback(nghttp2_session *session, - const nghttp2_frame *frame, void *user_data) { - auto hd = static_cast(user_data); - - if (frame->hd.type != NGHTTP2_HEADERS || - frame->headers.cat != NGHTTP2_HCAT_REQUEST) { - return 0; - } - - auto stream = make_unique(hd, frame->hd.stream_id); - - add_stream_read_timeout(stream.get()); - - hd->add_stream(frame->hd.stream_id, std::move(stream)); - - return 0; -} -} // namespace - -namespace { -int hd_on_frame_recv_callback(nghttp2_session *session, - const nghttp2_frame *frame, void *user_data) { - auto hd = static_cast(user_data); - if (hd->get_config()->verbose) { - print_session_id(hd->session_id()); - verbose_on_frame_recv_callback(session, frame, user_data); - } - switch (frame->hd.type) { - case NGHTTP2_DATA: { - // TODO Handle POST - auto stream = hd->get_stream(frame->hd.stream_id); - if (!stream) { - return 0; - } - - if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) { - remove_stream_read_timeout(stream); - if (stream->echo_upload || !hd->get_config()->early_response) { - prepare_response(stream, hd); - } - } else { - add_stream_read_timeout(stream); - } - - break; - } - case NGHTTP2_HEADERS: { - auto stream = hd->get_stream(frame->hd.stream_id); - if (!stream) { - return 0; - } - - if (frame->headers.cat == NGHTTP2_HCAT_REQUEST) { - - auto expect100 = - http2::get_header(stream->hdidx, http2::HD_EXPECT, stream->headers); - - if (expect100 && util::strieq_l("100-continue", expect100->value)) { - hd->submit_non_final_response("100", frame->hd.stream_id); - } - - auto &method = http2::get_header(stream->hdidx, http2::HD__METHOD, - stream->headers)->value; - if (hd->get_config()->echo_upload && - (method == "POST" || method == "PUT")) { - if (!prepare_upload_temp_store(stream, hd)) { - hd->submit_rst_stream(stream, NGHTTP2_INTERNAL_ERROR); - return 0; - } - } else if (hd->get_config()->early_response) { - prepare_response(stream, hd); - } - } - - if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) { - remove_stream_read_timeout(stream); - if (stream->echo_upload || !hd->get_config()->early_response) { - prepare_response(stream, hd); - } - } else { - add_stream_read_timeout(stream); - } - - break; - } - case NGHTTP2_SETTINGS: - if (frame->hd.flags & NGHTTP2_FLAG_ACK) { - hd->remove_settings_timer(); - } - break; - default: - break; - } - return 0; -} -} // namespace - -namespace { -int hd_on_frame_send_callback(nghttp2_session *session, - const nghttp2_frame *frame, void *user_data) { - auto hd = static_cast(user_data); - - if (hd->get_config()->verbose) { - print_session_id(hd->session_id()); - verbose_on_frame_send_callback(session, frame, user_data); - } - - switch (frame->hd.type) { - case NGHTTP2_DATA: - case NGHTTP2_HEADERS: { - auto stream = hd->get_stream(frame->hd.stream_id); - - if (!stream) { - return 0; - } - - if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) { - remove_stream_write_timeout(stream); - } else if (std::min(nghttp2_session_get_stream_remote_window_size( - session, frame->hd.stream_id), - nghttp2_session_get_remote_window_size(session)) <= 0) { - // If stream is blocked by flow control, enable write timeout. - add_stream_read_timeout_if_pending(stream); - add_stream_write_timeout(stream); - } else { - add_stream_read_timeout_if_pending(stream); - remove_stream_write_timeout(stream); - } - - break; - } - case NGHTTP2_PUSH_PROMISE: { - auto promised_stream_id = frame->push_promise.promised_stream_id; - auto promised_stream = hd->get_stream(promised_stream_id); - auto stream = hd->get_stream(frame->hd.stream_id); - - if (!stream || !promised_stream) { - return 0; - } - - add_stream_read_timeout_if_pending(stream); - add_stream_write_timeout(stream); - - prepare_response(promised_stream, hd, /*allow_push */ false); - } - } - return 0; -} -} // namespace - -namespace { -int send_data_callback(nghttp2_session *session, nghttp2_frame *frame, - const uint8_t *framehd, size_t length, - nghttp2_data_source *source, void *user_data) { - auto hd = static_cast(user_data); - auto wb = hd->get_wb(); - auto padlen = frame->data.padlen; - auto stream = hd->get_stream(frame->hd.stream_id); - - if (wb->wleft() < 9 + length + padlen) { - return NGHTTP2_ERR_WOULDBLOCK; - } - - int fd = source->fd; - - auto p = wb->last; - - p = std::copy_n(framehd, 9, p); - - if (padlen) { - *p++ = padlen - 1; - } - - while (length) { - ssize_t nread; - while ((nread = pread(fd, p, length, stream->body_offset)) == -1 && - errno == EINTR) - ; - - if (nread == -1) { - remove_stream_read_timeout(stream); - remove_stream_write_timeout(stream); - - return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; - } - - stream->body_offset += nread; - length -= nread; - p += nread; - } - - if (padlen) { - std::fill(p, p + padlen - 1, 0); - p += padlen - 1; - } - - wb->last = p; - - return 0; -} -} // namespace - -namespace { -ssize_t select_padding_callback(nghttp2_session *session, - const nghttp2_frame *frame, size_t max_payload, - void *user_data) { - auto hd = static_cast(user_data); - return std::min(max_payload, frame->hd.length + hd->get_config()->padding); -} -} // namespace - -namespace { -int on_data_chunk_recv_callback(nghttp2_session *session, uint8_t flags, - int32_t stream_id, const uint8_t *data, - size_t len, void *user_data) { - auto hd = static_cast(user_data); - auto stream = hd->get_stream(stream_id); - - if (!stream) { - return 0; - } - - if (stream->echo_upload) { - assert(stream->file_ent); - while (len) { - ssize_t n; - while ((n = write(stream->file_ent->fd, data, len)) == -1 && - errno == EINTR) - ; - if (n == -1) { - hd->submit_rst_stream(stream, NGHTTP2_INTERNAL_ERROR); - return 0; - } - len -= n; - data += n; - } - } - // TODO Handle POST - - add_stream_read_timeout(stream); - - return 0; -} -} // namespace - -namespace { -int on_stream_close_callback(nghttp2_session *session, int32_t stream_id, - uint32_t error_code, void *user_data) { - auto hd = static_cast(user_data); - hd->remove_stream(stream_id); - if (hd->get_config()->verbose) { - print_session_id(hd->session_id()); - print_timer(); - printf(" stream_id=%d closed\n", stream_id); - fflush(stdout); - } - return 0; -} -} // namespace - -namespace { -void fill_callback(nghttp2_session_callbacks *callbacks, const Config *config) { - nghttp2_session_callbacks_set_on_stream_close_callback( - callbacks, on_stream_close_callback); - - nghttp2_session_callbacks_set_on_frame_recv_callback( - callbacks, hd_on_frame_recv_callback); - - nghttp2_session_callbacks_set_on_frame_send_callback( - callbacks, hd_on_frame_send_callback); - - if (config->verbose) { - nghttp2_session_callbacks_set_on_invalid_frame_recv_callback( - callbacks, verbose_on_invalid_frame_recv_callback); - } - - nghttp2_session_callbacks_set_on_data_chunk_recv_callback( - callbacks, on_data_chunk_recv_callback); - - nghttp2_session_callbacks_set_on_header_callback(callbacks, - on_header_callback); - - nghttp2_session_callbacks_set_on_begin_headers_callback( - callbacks, on_begin_headers_callback); - - nghttp2_session_callbacks_set_send_data_callback(callbacks, - send_data_callback); - - if (config->padding) { - nghttp2_session_callbacks_set_select_padding_callback( - callbacks, select_padding_callback); - } -} -} // namespace - -struct ClientInfo { - int fd; -}; - -struct Worker { - std::unique_ptr sessions; - ev_async w; - // protectes q - std::mutex m; - std::deque q; -}; - -namespace { -void worker_acceptcb(struct ev_loop *loop, ev_async *w, int revents) { - auto worker = static_cast(w->data); - auto &sessions = worker->sessions; - - std::deque q; - { - std::lock_guard lock(worker->m); - q.swap(worker->q); - } - - for (auto c : q) { - sessions->accept_connection(c.fd); - } -} -} // namespace - -namespace { -void run_worker(Worker *worker) { - auto loop = worker->sessions->get_loop(); - - ev_run(loop, 0); -} -} // namespace - -class AcceptHandler { -public: - AcceptHandler(HttpServer *sv, Sessions *sessions, const Config *config) - : sessions_(sessions), config_(config), next_worker_(0) { - if (config_->num_worker == 1) { - return; - } - for (size_t i = 0; i < config_->num_worker; ++i) { - if (config_->verbose) { - std::cerr << "spawning thread #" << i << std::endl; - } - auto worker = make_unique(); - auto loop = ev_loop_new(0); - worker->sessions = - make_unique(sv, loop, config_, sessions_->get_ssl_ctx()); - ev_async_init(&worker->w, worker_acceptcb); - worker->w.data = worker.get(); - ev_async_start(loop, &worker->w); - - auto t = std::thread(run_worker, worker.get()); - t.detach(); - workers_.push_back(std::move(worker)); - } - } - void accept_connection(int fd) { - if (config_->num_worker == 1) { - sessions_->accept_connection(fd); - return; - } - - // Dispatch client to the one of the worker threads, in a round - // robin manner. - auto &worker = workers_[next_worker_]; - if (next_worker_ == config_->num_worker - 1) { - next_worker_ = 0; - } else { - ++next_worker_; - } - { - std::lock_guard lock(worker->m); - worker->q.push_back({fd}); - } - ev_async_send(worker->sessions->get_loop(), &worker->w); - } - -private: - std::vector> workers_; - Sessions *sessions_; - const Config *config_; - // In multi threading mode, this points to the next thread that - // client will be dispatched. - size_t next_worker_; -}; - -namespace { -void acceptcb(struct ev_loop *loop, ev_io *w, int revents); -} // namespace - -class ListenEventHandler { -public: - ListenEventHandler(Sessions *sessions, int fd, - std::shared_ptr acceptor) - : acceptor_(acceptor), sessions_(sessions), fd_(fd) { - ev_io_init(&w_, acceptcb, fd, EV_READ); - w_.data = this; - ev_io_start(sessions_->get_loop(), &w_); - } - void accept_connection() { - for (;;) { -#ifdef HAVE_ACCEPT4 - auto fd = accept4(fd_, nullptr, nullptr, SOCK_NONBLOCK); -#else // !HAVE_ACCEPT4 - auto fd = accept(fd_, nullptr, nullptr); -#endif // !HAVE_ACCEPT4 - if (fd == -1) { - break; - } -#ifndef HAVE_ACCEPT4 - util::make_socket_nonblocking(fd); -#endif // !HAVE_ACCEPT4 - acceptor_->accept_connection(fd); - } - } - -private: - ev_io w_; - std::shared_ptr acceptor_; - Sessions *sessions_; - int fd_; -}; - -namespace { -void acceptcb(struct ev_loop *loop, ev_io *w, int revents) { - auto handler = static_cast(w->data); - handler->accept_connection(); -} -} // namespace - -namespace { -FileEntry make_status_body(int status, uint16_t port) { - std::string body; - body = ""; - body += http2::get_status_string(status); - body += "

      "; - body += http2::get_status_string(status); - body += "


      "; - body += NGHTTPD_SERVER; - body += " at port "; - body += util::utos(port); - body += "
      "; - body += ""; - - char tempfn[] = "/tmp/nghttpd.temp.XXXXXX"; - int fd = mkstemp(tempfn); - if (fd == -1) { - auto error = errno; - std::cerr << "Could not open status response body file: errno=" << error; - assert(0); - } - unlink(tempfn); - ssize_t nwrite; - while ((nwrite = write(fd, body.c_str(), body.size())) == -1 && - errno == EINTR) - ; - if (nwrite == -1) { - auto error = errno; - std::cerr << "Could not write status response body into file: errno=" - << error; - assert(0); - } - - return FileEntry(util::utos(status), nwrite, 0, fd); -} -} // namespace - -// index into HttpServer::status_pages_ -enum { - IDX_200, - IDX_301, - IDX_304, - IDX_400, - IDX_404, -}; - -HttpServer::HttpServer(const Config *config) : config_(config) { - status_pages_ = std::vector{ - {"200", make_status_body(200, config_->port)}, - {"301", make_status_body(301, config_->port)}, - {"304", make_status_body(304, config_->port)}, - {"400", make_status_body(400, config_->port)}, - {"404", make_status_body(404, config_->port)}, - }; -} - -namespace { -int next_proto_cb(SSL *s, const unsigned char **data, unsigned int *len, - void *arg) { - auto next_proto = static_cast *>(arg); - *data = next_proto->data(); - *len = next_proto->size(); - return SSL_TLSEXT_ERR_OK; -} -} // namespace - -namespace { -int verify_callback(int preverify_ok, X509_STORE_CTX *ctx) { - // We don't verify the client certificate. Just request it for the - // testing purpose. - return 1; -} -} // namespace - -namespace { -int start_listen(HttpServer *sv, struct ev_loop *loop, Sessions *sessions, - const Config *config) { - addrinfo hints; - int r; - bool ok = false; - const char *addr = nullptr; - - auto acceptor = std::make_shared(sv, sessions, config); - auto service = util::utos(config->port); - - memset(&hints, 0, sizeof(addrinfo)); - hints.ai_family = AF_UNSPEC; - hints.ai_socktype = SOCK_STREAM; - hints.ai_flags = AI_PASSIVE; -#ifdef AI_ADDRCONFIG - hints.ai_flags |= AI_ADDRCONFIG; -#endif // AI_ADDRCONFIG - - if (!config->address.empty()) { - addr = config->address.c_str(); - } - - addrinfo *res, *rp; - r = getaddrinfo(addr, service.c_str(), &hints, &res); - if (r != 0) { - std::cerr << "getaddrinfo() failed: " << gai_strerror(r) << std::endl; - return -1; - } - - for (rp = res; rp; rp = rp->ai_next) { - int fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); - if (fd == -1) { - continue; - } - int val = 1; - if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &val, - static_cast(sizeof(val))) == -1) { - close(fd); - continue; - } - (void)util::make_socket_nonblocking(fd); -#ifdef IPV6_V6ONLY - if (rp->ai_family == AF_INET6) { - if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &val, - static_cast(sizeof(val))) == -1) { - close(fd); - continue; - } - } -#endif // IPV6_V6ONLY - if (bind(fd, rp->ai_addr, rp->ai_addrlen) == 0 && listen(fd, 1000) == 0) { - new ListenEventHandler(sessions, fd, acceptor); - - if (config->verbose) { - std::string s = util::numeric_name(rp->ai_addr, rp->ai_addrlen); - std::cout << (rp->ai_family == AF_INET ? "IPv4" : "IPv6") << ": listen " - << s << ":" << config->port << std::endl; - } - ok = true; - continue; - } else { - std::cerr << strerror(errno) << std::endl; - } - close(fd); - } - freeaddrinfo(res); - - if (!ok) { - return -1; - } - return 0; -} -} // namespace - -#if OPENSSL_VERSION_NUMBER >= 0x10002000L -namespace { -int alpn_select_proto_cb(SSL *ssl, const unsigned char **out, - unsigned char *outlen, const unsigned char *in, - unsigned int inlen, void *arg) { - auto config = static_cast(arg)->get_config(); - if (config->verbose) { - std::cout << "[ALPN] client offers:" << std::endl; - } - if (config->verbose) { - for (unsigned int i = 0; i < inlen; i += in [i] + 1) { - std::cout << " * "; - std::cout.write(reinterpret_cast(&in[i + 1]), in[i]); - std::cout << std::endl; - } - } - if (!util::select_h2(out, outlen, in, inlen)) { - return SSL_TLSEXT_ERR_NOACK; - } - return SSL_TLSEXT_ERR_OK; -} -} // namespace -#endif // OPENSSL_VERSION_NUMBER >= 0x10002000L - -int HttpServer::run() { - SSL_CTX *ssl_ctx = nullptr; - std::vector next_proto; - - if (!config_->no_tls) { - ssl_ctx = SSL_CTX_new(SSLv23_server_method()); - if (!ssl_ctx) { - std::cerr << ERR_error_string(ERR_get_error(), nullptr) << std::endl; - return -1; - } - - SSL_CTX_set_options(ssl_ctx, - SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | - SSL_OP_NO_COMPRESSION | - SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION | - SSL_OP_SINGLE_ECDH_USE | SSL_OP_NO_TICKET | - SSL_OP_CIPHER_SERVER_PREFERENCE); - SSL_CTX_set_mode(ssl_ctx, SSL_MODE_AUTO_RETRY); - SSL_CTX_set_mode(ssl_ctx, SSL_MODE_RELEASE_BUFFERS); - - if (SSL_CTX_set_cipher_list(ssl_ctx, ssl::DEFAULT_CIPHER_LIST) == 0) { - std::cerr << ERR_error_string(ERR_get_error(), nullptr) << std::endl; - return -1; - } - - const unsigned char sid_ctx[] = "nghttpd"; - SSL_CTX_set_session_id_context(ssl_ctx, sid_ctx, sizeof(sid_ctx) - 1); - SSL_CTX_set_session_cache_mode(ssl_ctx, SSL_SESS_CACHE_SERVER); - -#ifndef OPENSSL_NO_EC - - // Disabled SSL_CTX_set_ecdh_auto, because computational cost of - // chosen curve is much higher than P-256. - - // #if OPENSSL_VERSION_NUMBER >= 0x10002000L - // SSL_CTX_set_ecdh_auto(ssl_ctx, 1); - // #else // OPENSSL_VERSION_NUBMER < 0x10002000L - // Use P-256, which is sufficiently secure at the time of this - // writing. - auto ecdh = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1); - if (ecdh == nullptr) { - std::cerr << "EC_KEY_new_by_curv_name failed: " - << ERR_error_string(ERR_get_error(), nullptr); - return -1; - } - SSL_CTX_set_tmp_ecdh(ssl_ctx, ecdh); - EC_KEY_free(ecdh); -// #endif // OPENSSL_VERSION_NUBMER < 0x10002000L - -#endif // OPENSSL_NO_EC - - if (!config_->dh_param_file.empty()) { - // Read DH parameters from file - auto bio = BIO_new_file(config_->dh_param_file.c_str(), "r"); - if (bio == nullptr) { - std::cerr << "BIO_new_file() failed: " - << ERR_error_string(ERR_get_error(), nullptr) << std::endl; - return -1; - } - - auto dh = PEM_read_bio_DHparams(bio, nullptr, nullptr, nullptr); - - if (dh == nullptr) { - std::cerr << "PEM_read_bio_DHparams() failed: " - << ERR_error_string(ERR_get_error(), nullptr) << std::endl; - return -1; - } - - SSL_CTX_set_tmp_dh(ssl_ctx, dh); - DH_free(dh); - BIO_free(bio); - } - - if (SSL_CTX_use_PrivateKey_file(ssl_ctx, config_->private_key_file.c_str(), - SSL_FILETYPE_PEM) != 1) { - std::cerr << "SSL_CTX_use_PrivateKey_file failed." << std::endl; - return -1; - } - if (SSL_CTX_use_certificate_chain_file(ssl_ctx, - config_->cert_file.c_str()) != 1) { - std::cerr << "SSL_CTX_use_certificate_file failed." << std::endl; - return -1; - } - if (SSL_CTX_check_private_key(ssl_ctx) != 1) { - std::cerr << "SSL_CTX_check_private_key failed." << std::endl; - return -1; - } - if (config_->verify_client) { - SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE | - SSL_VERIFY_FAIL_IF_NO_PEER_CERT, - verify_callback); - } - - next_proto = util::get_default_alpn(); - - SSL_CTX_set_next_protos_advertised_cb(ssl_ctx, next_proto_cb, &next_proto); -#if OPENSSL_VERSION_NUMBER >= 0x10002000L - // ALPN selection callback - SSL_CTX_set_alpn_select_cb(ssl_ctx, alpn_select_proto_cb, this); -#endif // OPENSSL_VERSION_NUMBER >= 0x10002000L - } - - auto loop = EV_DEFAULT; - - Sessions sessions(this, loop, config_, ssl_ctx); - if (start_listen(this, loop, &sessions, config_) != 0) { - std::cerr << "Could not listen" << std::endl; - return -1; - } - - ev_run(loop, 0); - return 0; -} - -const Config *HttpServer::get_config() const { return config_; } - -const StatusPage *HttpServer::get_status_page(int status) const { - switch (status) { - case 200: - return &status_pages_[IDX_200]; - case 301: - return &status_pages_[IDX_301]; - case 304: - return &status_pages_[IDX_304]; - case 400: - return &status_pages_[IDX_400]; - case 404: - return &status_pages_[IDX_404]; - default: - assert(0); - } - return nullptr; -} - -} // namespace nghttp2 diff --git a/src/HttpServer.h b/src/HttpServer.h deleted file mode 100644 index 212f12a..0000000 --- a/src/HttpServer.h +++ /dev/null @@ -1,202 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2013 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#ifndef HTTP_SERVER_H -#define HTTP_SERVER_H - -#include "nghttp2_config.h" - -#include - -#include -#include - -#include -#include -#include -#include - -#include - -#include - -#include - -#include "http2.h" -#include "buffer.h" - -namespace nghttp2 { - -struct Config { - std::map> push; - Headers trailer; - std::string htdocs; - std::string host; - std::string private_key_file; - std::string cert_file; - std::string dh_param_file; - std::string address; - ev_tstamp stream_read_timeout; - ev_tstamp stream_write_timeout; - void *data_ptr; - size_t padding; - size_t num_worker; - size_t max_concurrent_streams; - ssize_t header_table_size; - uint16_t port; - bool verbose; - bool daemon; - bool verify_client; - bool no_tls; - bool error_gzip; - bool early_response; - bool hexdump; - bool echo_upload; - Config(); - ~Config(); -}; - -class Http2Handler; - -struct FileEntry { - FileEntry(std::string path, int64_t length, int64_t mtime, int fd) - : path(std::move(path)), length(length), mtime(mtime), dlprev(nullptr), - dlnext(nullptr), fd(fd), usecount(1) {} - std::string path; - int64_t length; - int64_t mtime; - FileEntry *dlprev, *dlnext; - int fd; - int usecount; -}; - -struct Stream { - Headers headers; - Http2Handler *handler; - FileEntry *file_ent; - ev_timer rtimer; - ev_timer wtimer; - int64_t body_length; - int64_t body_offset; - int32_t stream_id; - http2::HeaderIndex hdidx; - bool echo_upload; - Stream(Http2Handler *handler, int32_t stream_id); - ~Stream(); -}; - -class Sessions; - -class Http2Handler { -public: - Http2Handler(Sessions *sessions, int fd, SSL *ssl, int64_t session_id); - ~Http2Handler(); - - void remove_self(); - int setup_bev(); - int on_read(); - int on_write(); - int connection_made(); - int verify_npn_result(); - - int submit_file_response(const std::string &status, Stream *stream, - time_t last_modified, off_t file_length, - nghttp2_data_provider *data_prd); - - int submit_response(const std::string &status, int32_t stream_id, - nghttp2_data_provider *data_prd); - - int submit_response(const std::string &status, int32_t stream_id, - const Headers &headers, nghttp2_data_provider *data_prd); - - int submit_non_final_response(const std::string &status, int32_t stream_id); - - int submit_push_promise(Stream *stream, const std::string &push_path); - - int submit_rst_stream(Stream *stream, uint32_t error_code); - - void add_stream(int32_t stream_id, std::unique_ptr stream); - void remove_stream(int32_t stream_id); - Stream *get_stream(int32_t stream_id); - int64_t session_id() const; - Sessions *get_sessions() const; - const Config *get_config() const; - void remove_settings_timer(); - void terminate_session(uint32_t error_code); - - int fill_wb(); - - int read_clear(); - int write_clear(); - int tls_handshake(); - int read_tls(); - int write_tls(); - - struct ev_loop *get_loop() const; - - using WriteBuf = Buffer<65536>; - - WriteBuf *get_wb(); - -private: - ev_io wev_; - ev_io rev_; - ev_timer settings_timerev_; - std::map> id2stream_; - WriteBuf wb_; - std::function read_, write_; - int64_t session_id_; - nghttp2_session *session_; - Sessions *sessions_; - SSL *ssl_; - const uint8_t *data_pending_; - size_t data_pendinglen_; - int fd_; -}; - -struct StatusPage { - std::string status; - FileEntry file_ent; -}; - -class HttpServer { -public: - HttpServer(const Config *config); - int listen(); - int run(); - const Config *get_config() const; - const StatusPage *get_status_page(int status) const; - -private: - std::vector status_pages_; - const Config *config_; -}; - -ssize_t file_read_callback(nghttp2_session *session, int32_t stream_id, - uint8_t *buf, size_t length, int *eof, - nghttp2_data_source *source, void *user_data); - -} // namespace nghttp2 - -#endif // HTTP_SERVER_H diff --git a/src/Makefile.am b/src/Makefile.am deleted file mode 100644 index b1336ae..0000000 --- a/src/Makefile.am +++ /dev/null @@ -1,216 +0,0 @@ -# nghttp2 - HTTP/2 C Library - -# Copyright (c) 2012 Tatsuhiro Tsujikawa - -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: - -# The above copyright notice and this permission notice shall be -# included in all copies or substantial portions of the Software. - -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -SUBDIRS = includes - -bin_PROGRAMS = -check_PROGRAMS = -TESTS = - -AM_CFLAGS = $(WARNCFLAGS) -AM_CPPFLAGS = \ - -DPKGDATADIR='"$(pkgdatadir)"' \ - -Wall \ - -I$(top_srcdir)/lib/includes \ - -I$(top_builddir)/lib/includes \ - -I$(top_srcdir)/lib \ - -I$(top_srcdir)/src/includes \ - -I$(top_srcdir)/third-party \ - @LIBSPDYLAY_CFLAGS@ \ - @XML_CPPFLAGS@ \ - @LIBEV_CFLAGS@ \ - @OPENSSL_CFLAGS@ \ - @JANSSON_CFLAGS@ \ - @ZLIB_CFLAGS@ \ - @DEFS@ - -LDADD = $(top_builddir)/lib/libnghttp2.la \ - $(top_builddir)/third-party/libhttp-parser.la \ - @JEMALLOC_LIBS@ \ - @LIBSPDYLAY_LIBS@ \ - @XML_LIBS@ \ - @LIBEV_LIBS@ \ - @OPENSSL_LIBS@ \ - @JANSSON_LIBS@ \ - @ZLIB_LIBS@ \ - @APPLDFLAGS@ - -if ENABLE_APP - -bin_PROGRAMS += nghttp nghttpd nghttpx - -HELPER_OBJECTS = util.cc \ - http2.cc timegm.c app_helper.cc nghttp2_gzip.c -HELPER_HFILES = util.h \ - http2.h timegm.h app_helper.h nghttp2_config.h \ - nghttp2_gzip.h - -HTML_PARSER_OBJECTS = -HTML_PARSER_HFILES = HtmlParser.h - -if HAVE_LIBXML2 -HTML_PARSER_OBJECTS += HtmlParser.cc -endif # HAVE_LIBXML2 - -nghttp_SOURCES = ${HELPER_OBJECTS} ${HELPER_HFILES} nghttp.cc nghttp.h \ - ${HTML_PARSER_OBJECTS} ${HTML_PARSER_HFILES} \ - ssl.cc ssl.h - -nghttpd_SOURCES = ${HELPER_OBJECTS} ${HELPER_HFILES} nghttpd.cc \ - ssl.cc ssl.h \ - HttpServer.cc HttpServer.h - -bin_PROGRAMS += h2load - -h2load_SOURCES = util.cc util.h \ - http2.cc http2.h h2load.cc h2load.h \ - timegm.c timegm.h \ - ssl.cc ssl.h \ - h2load_session.h \ - h2load_http2_session.cc h2load_http2_session.h - -if HAVE_SPDYLAY -h2load_SOURCES += h2load_spdy_session.cc h2load_spdy_session.h -endif # HAVE_SPDYLAY - -NGHTTPX_SRCS = \ - util.cc util.h http2.cc http2.h timegm.c timegm.h base64.h \ - app_helper.cc app_helper.h \ - ssl.cc ssl.h \ - shrpx_config.cc shrpx_config.h \ - shrpx_error.h \ - shrpx_accept_handler.cc shrpx_accept_handler.h \ - shrpx_connection_handler.cc shrpx_connection_handler.h \ - shrpx_client_handler.cc shrpx_client_handler.h \ - shrpx_upstream.h \ - shrpx_http2_upstream.cc shrpx_http2_upstream.h \ - shrpx_https_upstream.cc shrpx_https_upstream.h \ - shrpx_downstream.cc shrpx_downstream.h \ - shrpx_downstream_connection.cc shrpx_downstream_connection.h \ - shrpx_http_downstream_connection.cc shrpx_http_downstream_connection.h \ - shrpx_http2_downstream_connection.cc shrpx_http2_downstream_connection.h \ - shrpx_http2_session.cc shrpx_http2_session.h \ - shrpx_downstream_queue.cc shrpx_downstream_queue.h \ - shrpx_log.cc shrpx_log.h \ - shrpx_http.cc shrpx_http.h \ - shrpx_io_control.cc shrpx_io_control.h \ - shrpx_ssl.cc shrpx_ssl.h \ - shrpx_worker.cc shrpx_worker.h \ - shrpx_log_config.cc shrpx_log_config.h \ - shrpx_connect_blocker.cc shrpx_connect_blocker.h \ - shrpx_downstream_connection_pool.cc shrpx_downstream_connection_pool.h \ - shrpx_rate_limit.cc shrpx_rate_limit.h \ - shrpx_connection.cc shrpx_connection.h \ - buffer.h memchunk.h template.h - -if HAVE_SPDYLAY -NGHTTPX_SRCS += shrpx_spdy_upstream.cc shrpx_spdy_upstream.h -endif # HAVE_SPDYLAY - -noinst_LIBRARIES = libnghttpx.a -libnghttpx_a_SOURCES = ${NGHTTPX_SRCS} - -nghttpx_SOURCES = shrpx.cc shrpx.h -nghttpx_LDADD = libnghttpx.a ${LDADD} - -if HAVE_CUNIT -check_PROGRAMS += nghttpx-unittest -nghttpx_unittest_SOURCES = shrpx-unittest.cc \ - shrpx_ssl_test.cc shrpx_ssl_test.h \ - shrpx_downstream_test.cc shrpx_downstream_test.h \ - shrpx_config_test.cc shrpx_config_test.h \ - http2_test.cc http2_test.h \ - util_test.cc util_test.h \ - nghttp2_gzip_test.c nghttp2_gzip_test.h \ - nghttp2_gzip.c nghttp2_gzip.h \ - buffer_test.cc buffer_test.h \ - memchunk_test.cc memchunk_test.h -nghttpx_unittest_CPPFLAGS = ${AM_CPPFLAGS}\ - -DNGHTTP2_TESTS_DIR=\"$(top_srcdir)/tests\" -nghttpx_unittest_LDADD = libnghttpx.a ${LDADD} @CUNIT_LIBS@ @TESTLDADD@ - -TESTS += nghttpx-unittest -endif # HAVE_CUNIT - -endif # ENABLE_APP - -if ENABLE_HPACK_TOOLS - -bin_PROGRAMS += inflatehd deflatehd - -HPACK_TOOLS_COMMON_SRCS = comp_helper.c comp_helper.h - -inflatehd_SOURCES = inflatehd.cc $(HPACK_TOOLS_COMMON_SRCS) - -deflatehd_SOURCES = deflatehd.cc $(HPACK_TOOLS_COMMON_SRCS) - -endif # ENABLE_HPACK_TOOLS - -if ENABLE_ASIO_LIB - -pkgconfigdir = $(libdir)/pkgconfig -pkgconfig_DATA = libnghttp2_asio.pc -DISTCLEANFILES = $(pkgconfig_DATA) - -lib_LTLIBRARIES = libnghttp2_asio.la - -libnghttp2_asio_la_SOURCES = \ - util.cc util.h http2.cc http2.h \ - ssl.cc ssl.h \ - asio_common.cc asio_common.h \ - asio_io_service_pool.cc asio_io_service_pool.h \ - asio_server_http2.cc \ - asio_server_http2_impl.cc asio_server_http2_impl.h \ - asio_server.cc asio_server.h \ - asio_server_http2_handler.cc asio_server_http2_handler.h \ - asio_server_connection.h \ - asio_server_request.cc \ - asio_server_request_impl.cc asio_server_request_impl.h \ - asio_server_response.cc \ - asio_server_response_impl.cc asio_server_response_impl.h \ - asio_server_stream.cc asio_server_stream.h \ - asio_server_serve_mux.cc asio_server_serve_mux.h \ - asio_server_request_handler.cc asio_server_request_handler.h \ - asio_server_tls_context.cc asio_server_tls_context.h \ - asio_client_session.cc \ - asio_client_session_impl.cc asio_client_session_impl.h \ - asio_client_session_tcp_impl.cc asio_client_session_tcp_impl.h \ - asio_client_session_tls_impl.cc asio_client_session_tls_impl.h \ - asio_client_response.cc \ - asio_client_response_impl.cc asio_client_response_impl.h \ - asio_client_request.cc \ - asio_client_request_impl.cc asio_client_request_impl.h \ - asio_client_stream.cc asio_client_stream.h \ - asio_client_tls_context.cc asio_client_tls_context.h - -libnghttp2_asio_la_CPPFLAGS = ${AM_CPPFLAGS} ${BOOST_CPPFLAGS} -libnghttp2_asio_la_LDFLAGS = -no-undefined -version-info 1:0:0 -libnghttp2_asio_la_LIBADD = \ - $(top_builddir)/lib/libnghttp2.la \ - $(top_builddir)/third-party/libhttp-parser.la \ - ${BOOST_LDFLAGS} \ - ${BOOST_ASIO_LIB} \ - ${BOOST_THREAD_LIB} \ - ${BOOST_SYSTEM_LIB} \ - @OPENSSL_LIBS@ - -endif # ENABLE_ASIO_LIB diff --git a/src/Makefile.in b/src/Makefile.in deleted file mode 100644 index 5702edb..0000000 --- a/src/Makefile.in +++ /dev/null @@ -1,2137 +0,0 @@ -# Makefile.in generated by automake 1.14.1 from Makefile.am. -# @configure_input@ - -# Copyright (C) 1994-2013 Free Software Foundation, Inc. - -# This Makefile.in is free software; the Free Software Foundation -# gives unlimited permission to copy and/or distribute it, -# with or without modifications, as long as this notice is preserved. - -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY, to the extent permitted by law; without -# even the implied warranty of MERCHANTABILITY or FITNESS FOR A -# PARTICULAR PURPOSE. - -@SET_MAKE@ - -# nghttp2 - HTTP/2 C Library - -# Copyright (c) 2012 Tatsuhiro Tsujikawa - -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: - -# The above copyright notice and this permission notice shall be -# included in all copies or substantial portions of the Software. - - - - -VPATH = @srcdir@ -am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)' -am__make_running_with_option = \ - case $${target_option-} in \ - ?) ;; \ - *) echo "am__make_running_with_option: internal error: invalid" \ - "target option '$${target_option-}' specified" >&2; \ - exit 1;; \ - esac; \ - has_opt=no; \ - sane_makeflags=$$MAKEFLAGS; \ - if $(am__is_gnu_make); then \ - sane_makeflags=$$MFLAGS; \ - else \ - case $$MAKEFLAGS in \ - *\\[\ \ ]*) \ - bs=\\; \ - sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ - | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ - esac; \ - fi; \ - skip_next=no; \ - strip_trailopt () \ - { \ - flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ - }; \ - for flg in $$sane_makeflags; do \ - test $$skip_next = yes && { skip_next=no; continue; }; \ - case $$flg in \ - *=*|--*) continue;; \ - -*I) strip_trailopt 'I'; skip_next=yes;; \ - -*I?*) strip_trailopt 'I';; \ - -*O) strip_trailopt 'O'; skip_next=yes;; \ - -*O?*) strip_trailopt 'O';; \ - -*l) strip_trailopt 'l'; skip_next=yes;; \ - -*l?*) strip_trailopt 'l';; \ - -[dEDm]) skip_next=yes;; \ - -[JT]) skip_next=yes;; \ - esac; \ - case $$flg in \ - *$$target_option*) has_opt=yes; break;; \ - esac; \ - done; \ - test $$has_opt = yes -am__make_dryrun = (target_option=n; $(am__make_running_with_option)) -am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) -pkgdatadir = $(datadir)/@PACKAGE@ -pkgincludedir = $(includedir)/@PACKAGE@ -pkglibdir = $(libdir)/@PACKAGE@ -pkglibexecdir = $(libexecdir)/@PACKAGE@ -am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd -install_sh_DATA = $(install_sh) -c -m 644 -install_sh_PROGRAM = $(install_sh) -c -install_sh_SCRIPT = $(install_sh) -c -INSTALL_HEADER = $(INSTALL_DATA) -transform = $(program_transform_name) -NORMAL_INSTALL = : -PRE_INSTALL = : -POST_INSTALL = : -NORMAL_UNINSTALL = : -PRE_UNINSTALL = : -POST_UNINSTALL = : -build_triplet = @build@ -host_triplet = @host@ -target_triplet = @target@ -bin_PROGRAMS = $(am__EXEEXT_1) $(am__EXEEXT_2) -check_PROGRAMS = $(am__EXEEXT_3) -TESTS = $(am__EXEEXT_3) -@ENABLE_APP_TRUE@am__append_1 = nghttp nghttpd nghttpx h2load -@ENABLE_APP_TRUE@@HAVE_LIBXML2_TRUE@am__append_2 = HtmlParser.cc -@ENABLE_APP_TRUE@@HAVE_SPDYLAY_TRUE@am__append_3 = h2load_spdy_session.cc h2load_spdy_session.h -@ENABLE_APP_TRUE@@HAVE_SPDYLAY_TRUE@am__append_4 = shrpx_spdy_upstream.cc shrpx_spdy_upstream.h -@ENABLE_APP_TRUE@@HAVE_CUNIT_TRUE@am__append_5 = nghttpx-unittest -@ENABLE_APP_TRUE@@HAVE_CUNIT_TRUE@am__append_6 = nghttpx-unittest -@ENABLE_HPACK_TOOLS_TRUE@am__append_7 = inflatehd deflatehd -subdir = src -DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \ - $(srcdir)/libnghttp2_asio.pc.in $(top_srcdir)/depcomp \ - $(top_srcdir)/test-driver -ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 -am__aclocal_m4_deps = $(top_srcdir)/m4/ax_boost_asio.m4 \ - $(top_srcdir)/m4/ax_boost_base.m4 \ - $(top_srcdir)/m4/ax_boost_system.m4 \ - $(top_srcdir)/m4/ax_boost_thread.m4 \ - $(top_srcdir)/m4/ax_check_compile_flag.m4 \ - $(top_srcdir)/m4/ax_cxx_compile_stdcxx_11.m4 \ - $(top_srcdir)/m4/ax_have_epoll.m4 \ - $(top_srcdir)/m4/ax_python_devel.m4 \ - $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/libxml2.m4 \ - $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ - $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ - $(top_srcdir)/configure.ac -am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ - $(ACLOCAL_M4) -mkinstalldirs = $(install_sh) -d -CONFIG_HEADER = $(top_builddir)/config.h -CONFIG_CLEAN_FILES = libnghttp2_asio.pc -CONFIG_CLEAN_VPATH_FILES = -LIBRARIES = $(noinst_LIBRARIES) -ARFLAGS = cru -AM_V_AR = $(am__v_AR_@AM_V@) -am__v_AR_ = $(am__v_AR_@AM_DEFAULT_V@) -am__v_AR_0 = @echo " AR " $@; -am__v_AR_1 = -libnghttpx_a_AR = $(AR) $(ARFLAGS) -libnghttpx_a_LIBADD = -am__libnghttpx_a_SOURCES_DIST = util.cc util.h http2.cc http2.h \ - timegm.c timegm.h base64.h app_helper.cc app_helper.h ssl.cc \ - ssl.h shrpx_config.cc shrpx_config.h shrpx_error.h \ - shrpx_accept_handler.cc shrpx_accept_handler.h \ - shrpx_connection_handler.cc shrpx_connection_handler.h \ - shrpx_client_handler.cc shrpx_client_handler.h \ - shrpx_upstream.h shrpx_http2_upstream.cc \ - shrpx_http2_upstream.h shrpx_https_upstream.cc \ - shrpx_https_upstream.h shrpx_downstream.cc shrpx_downstream.h \ - shrpx_downstream_connection.cc shrpx_downstream_connection.h \ - shrpx_http_downstream_connection.cc \ - shrpx_http_downstream_connection.h \ - shrpx_http2_downstream_connection.cc \ - shrpx_http2_downstream_connection.h shrpx_http2_session.cc \ - shrpx_http2_session.h shrpx_downstream_queue.cc \ - shrpx_downstream_queue.h shrpx_log.cc shrpx_log.h \ - shrpx_http.cc shrpx_http.h shrpx_io_control.cc \ - shrpx_io_control.h shrpx_ssl.cc shrpx_ssl.h shrpx_worker.cc \ - shrpx_worker.h shrpx_log_config.cc shrpx_log_config.h \ - shrpx_connect_blocker.cc shrpx_connect_blocker.h \ - shrpx_downstream_connection_pool.cc \ - shrpx_downstream_connection_pool.h shrpx_rate_limit.cc \ - shrpx_rate_limit.h shrpx_connection.cc shrpx_connection.h \ - buffer.h memchunk.h template.h shrpx_spdy_upstream.cc \ - shrpx_spdy_upstream.h -@ENABLE_APP_TRUE@@HAVE_SPDYLAY_TRUE@am__objects_1 = shrpx_spdy_upstream.$(OBJEXT) -@ENABLE_APP_TRUE@am__objects_2 = util.$(OBJEXT) http2.$(OBJEXT) \ -@ENABLE_APP_TRUE@ timegm.$(OBJEXT) app_helper.$(OBJEXT) \ -@ENABLE_APP_TRUE@ ssl.$(OBJEXT) shrpx_config.$(OBJEXT) \ -@ENABLE_APP_TRUE@ shrpx_accept_handler.$(OBJEXT) \ -@ENABLE_APP_TRUE@ shrpx_connection_handler.$(OBJEXT) \ -@ENABLE_APP_TRUE@ shrpx_client_handler.$(OBJEXT) \ -@ENABLE_APP_TRUE@ shrpx_http2_upstream.$(OBJEXT) \ -@ENABLE_APP_TRUE@ shrpx_https_upstream.$(OBJEXT) \ -@ENABLE_APP_TRUE@ shrpx_downstream.$(OBJEXT) \ -@ENABLE_APP_TRUE@ shrpx_downstream_connection.$(OBJEXT) \ -@ENABLE_APP_TRUE@ shrpx_http_downstream_connection.$(OBJEXT) \ -@ENABLE_APP_TRUE@ shrpx_http2_downstream_connection.$(OBJEXT) \ -@ENABLE_APP_TRUE@ shrpx_http2_session.$(OBJEXT) \ -@ENABLE_APP_TRUE@ shrpx_downstream_queue.$(OBJEXT) \ -@ENABLE_APP_TRUE@ shrpx_log.$(OBJEXT) shrpx_http.$(OBJEXT) \ -@ENABLE_APP_TRUE@ shrpx_io_control.$(OBJEXT) \ -@ENABLE_APP_TRUE@ shrpx_ssl.$(OBJEXT) shrpx_worker.$(OBJEXT) \ -@ENABLE_APP_TRUE@ shrpx_log_config.$(OBJEXT) \ -@ENABLE_APP_TRUE@ shrpx_connect_blocker.$(OBJEXT) \ -@ENABLE_APP_TRUE@ shrpx_downstream_connection_pool.$(OBJEXT) \ -@ENABLE_APP_TRUE@ shrpx_rate_limit.$(OBJEXT) \ -@ENABLE_APP_TRUE@ shrpx_connection.$(OBJEXT) $(am__objects_1) -@ENABLE_APP_TRUE@am_libnghttpx_a_OBJECTS = $(am__objects_2) -libnghttpx_a_OBJECTS = $(am_libnghttpx_a_OBJECTS) -am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; -am__vpath_adj = case $$p in \ - $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ - *) f=$$p;; \ - esac; -am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; -am__install_max = 40 -am__nobase_strip_setup = \ - srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` -am__nobase_strip = \ - for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" -am__nobase_list = $(am__nobase_strip_setup); \ - for p in $$list; do echo "$$p $$p"; done | \ - sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ - $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ - if (++n[$$2] == $(am__install_max)) \ - { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ - END { for (dir in files) print dir, files[dir] }' -am__base_list = \ - sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ - sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' -am__uninstall_files_from_dir = { \ - test -z "$$files" \ - || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ - || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ - $(am__cd) "$$dir" && rm -f $$files; }; \ - } -am__installdirs = "$(DESTDIR)$(libdir)" "$(DESTDIR)$(bindir)" \ - "$(DESTDIR)$(pkgconfigdir)" -LTLIBRARIES = $(lib_LTLIBRARIES) -am__DEPENDENCIES_1 = -@ENABLE_ASIO_LIB_TRUE@libnghttp2_asio_la_DEPENDENCIES = \ -@ENABLE_ASIO_LIB_TRUE@ $(top_builddir)/lib/libnghttp2.la \ -@ENABLE_ASIO_LIB_TRUE@ $(top_builddir)/third-party/libhttp-parser.la \ -@ENABLE_ASIO_LIB_TRUE@ $(am__DEPENDENCIES_1) \ -@ENABLE_ASIO_LIB_TRUE@ $(am__DEPENDENCIES_1) \ -@ENABLE_ASIO_LIB_TRUE@ $(am__DEPENDENCIES_1) \ -@ENABLE_ASIO_LIB_TRUE@ $(am__DEPENDENCIES_1) -am__libnghttp2_asio_la_SOURCES_DIST = util.cc util.h http2.cc http2.h \ - ssl.cc ssl.h asio_common.cc asio_common.h \ - asio_io_service_pool.cc asio_io_service_pool.h \ - asio_server_http2.cc asio_server_http2_impl.cc \ - asio_server_http2_impl.h asio_server.cc asio_server.h \ - asio_server_http2_handler.cc asio_server_http2_handler.h \ - asio_server_connection.h asio_server_request.cc \ - asio_server_request_impl.cc asio_server_request_impl.h \ - asio_server_response.cc asio_server_response_impl.cc \ - asio_server_response_impl.h asio_server_stream.cc \ - asio_server_stream.h asio_server_serve_mux.cc \ - asio_server_serve_mux.h asio_server_request_handler.cc \ - asio_server_request_handler.h asio_server_tls_context.cc \ - asio_server_tls_context.h asio_client_session.cc \ - asio_client_session_impl.cc asio_client_session_impl.h \ - asio_client_session_tcp_impl.cc asio_client_session_tcp_impl.h \ - asio_client_session_tls_impl.cc asio_client_session_tls_impl.h \ - asio_client_response.cc asio_client_response_impl.cc \ - asio_client_response_impl.h asio_client_request.cc \ - asio_client_request_impl.cc asio_client_request_impl.h \ - asio_client_stream.cc asio_client_stream.h \ - asio_client_tls_context.cc asio_client_tls_context.h -@ENABLE_ASIO_LIB_TRUE@am_libnghttp2_asio_la_OBJECTS = \ -@ENABLE_ASIO_LIB_TRUE@ libnghttp2_asio_la-util.lo \ -@ENABLE_ASIO_LIB_TRUE@ libnghttp2_asio_la-http2.lo \ -@ENABLE_ASIO_LIB_TRUE@ libnghttp2_asio_la-ssl.lo \ -@ENABLE_ASIO_LIB_TRUE@ libnghttp2_asio_la-asio_common.lo \ -@ENABLE_ASIO_LIB_TRUE@ libnghttp2_asio_la-asio_io_service_pool.lo \ -@ENABLE_ASIO_LIB_TRUE@ libnghttp2_asio_la-asio_server_http2.lo \ -@ENABLE_ASIO_LIB_TRUE@ libnghttp2_asio_la-asio_server_http2_impl.lo \ -@ENABLE_ASIO_LIB_TRUE@ libnghttp2_asio_la-asio_server.lo \ -@ENABLE_ASIO_LIB_TRUE@ libnghttp2_asio_la-asio_server_http2_handler.lo \ -@ENABLE_ASIO_LIB_TRUE@ libnghttp2_asio_la-asio_server_request.lo \ -@ENABLE_ASIO_LIB_TRUE@ libnghttp2_asio_la-asio_server_request_impl.lo \ -@ENABLE_ASIO_LIB_TRUE@ libnghttp2_asio_la-asio_server_response.lo \ -@ENABLE_ASIO_LIB_TRUE@ libnghttp2_asio_la-asio_server_response_impl.lo \ -@ENABLE_ASIO_LIB_TRUE@ libnghttp2_asio_la-asio_server_stream.lo \ -@ENABLE_ASIO_LIB_TRUE@ libnghttp2_asio_la-asio_server_serve_mux.lo \ -@ENABLE_ASIO_LIB_TRUE@ libnghttp2_asio_la-asio_server_request_handler.lo \ -@ENABLE_ASIO_LIB_TRUE@ libnghttp2_asio_la-asio_server_tls_context.lo \ -@ENABLE_ASIO_LIB_TRUE@ libnghttp2_asio_la-asio_client_session.lo \ -@ENABLE_ASIO_LIB_TRUE@ libnghttp2_asio_la-asio_client_session_impl.lo \ -@ENABLE_ASIO_LIB_TRUE@ libnghttp2_asio_la-asio_client_session_tcp_impl.lo \ -@ENABLE_ASIO_LIB_TRUE@ libnghttp2_asio_la-asio_client_session_tls_impl.lo \ -@ENABLE_ASIO_LIB_TRUE@ libnghttp2_asio_la-asio_client_response.lo \ -@ENABLE_ASIO_LIB_TRUE@ libnghttp2_asio_la-asio_client_response_impl.lo \ -@ENABLE_ASIO_LIB_TRUE@ libnghttp2_asio_la-asio_client_request.lo \ -@ENABLE_ASIO_LIB_TRUE@ libnghttp2_asio_la-asio_client_request_impl.lo \ -@ENABLE_ASIO_LIB_TRUE@ libnghttp2_asio_la-asio_client_stream.lo \ -@ENABLE_ASIO_LIB_TRUE@ libnghttp2_asio_la-asio_client_tls_context.lo -libnghttp2_asio_la_OBJECTS = $(am_libnghttp2_asio_la_OBJECTS) -AM_V_lt = $(am__v_lt_@AM_V@) -am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) -am__v_lt_0 = --silent -am__v_lt_1 = -libnghttp2_asio_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX \ - $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CXXLD) \ - $(AM_CXXFLAGS) $(CXXFLAGS) $(libnghttp2_asio_la_LDFLAGS) \ - $(LDFLAGS) -o $@ -@ENABLE_ASIO_LIB_TRUE@am_libnghttp2_asio_la_rpath = -rpath $(libdir) -@ENABLE_APP_TRUE@am__EXEEXT_1 = nghttp$(EXEEXT) nghttpd$(EXEEXT) \ -@ENABLE_APP_TRUE@ nghttpx$(EXEEXT) h2load$(EXEEXT) -@ENABLE_HPACK_TOOLS_TRUE@am__EXEEXT_2 = inflatehd$(EXEEXT) \ -@ENABLE_HPACK_TOOLS_TRUE@ deflatehd$(EXEEXT) -@ENABLE_APP_TRUE@@HAVE_CUNIT_TRUE@am__EXEEXT_3 = \ -@ENABLE_APP_TRUE@@HAVE_CUNIT_TRUE@ nghttpx-unittest$(EXEEXT) -PROGRAMS = $(bin_PROGRAMS) -am__deflatehd_SOURCES_DIST = deflatehd.cc comp_helper.c comp_helper.h -@ENABLE_HPACK_TOOLS_TRUE@am__objects_3 = comp_helper.$(OBJEXT) -@ENABLE_HPACK_TOOLS_TRUE@am_deflatehd_OBJECTS = deflatehd.$(OBJEXT) \ -@ENABLE_HPACK_TOOLS_TRUE@ $(am__objects_3) -deflatehd_OBJECTS = $(am_deflatehd_OBJECTS) -deflatehd_LDADD = $(LDADD) -deflatehd_DEPENDENCIES = $(top_builddir)/lib/libnghttp2.la \ - $(top_builddir)/third-party/libhttp-parser.la -am__h2load_SOURCES_DIST = util.cc util.h http2.cc http2.h h2load.cc \ - h2load.h timegm.c timegm.h ssl.cc ssl.h h2load_session.h \ - h2load_http2_session.cc h2load_http2_session.h \ - h2load_spdy_session.cc h2load_spdy_session.h -@ENABLE_APP_TRUE@@HAVE_SPDYLAY_TRUE@am__objects_4 = h2load_spdy_session.$(OBJEXT) -@ENABLE_APP_TRUE@am_h2load_OBJECTS = util.$(OBJEXT) http2.$(OBJEXT) \ -@ENABLE_APP_TRUE@ h2load.$(OBJEXT) timegm.$(OBJEXT) \ -@ENABLE_APP_TRUE@ ssl.$(OBJEXT) h2load_http2_session.$(OBJEXT) \ -@ENABLE_APP_TRUE@ $(am__objects_4) -h2load_OBJECTS = $(am_h2load_OBJECTS) -h2load_LDADD = $(LDADD) -h2load_DEPENDENCIES = $(top_builddir)/lib/libnghttp2.la \ - $(top_builddir)/third-party/libhttp-parser.la -am__inflatehd_SOURCES_DIST = inflatehd.cc comp_helper.c comp_helper.h -@ENABLE_HPACK_TOOLS_TRUE@am_inflatehd_OBJECTS = inflatehd.$(OBJEXT) \ -@ENABLE_HPACK_TOOLS_TRUE@ $(am__objects_3) -inflatehd_OBJECTS = $(am_inflatehd_OBJECTS) -inflatehd_LDADD = $(LDADD) -inflatehd_DEPENDENCIES = $(top_builddir)/lib/libnghttp2.la \ - $(top_builddir)/third-party/libhttp-parser.la -am__nghttp_SOURCES_DIST = util.cc http2.cc timegm.c app_helper.cc \ - nghttp2_gzip.c util.h http2.h timegm.h app_helper.h \ - nghttp2_config.h nghttp2_gzip.h nghttp.cc nghttp.h \ - HtmlParser.cc HtmlParser.h ssl.cc ssl.h -@ENABLE_APP_TRUE@am__objects_5 = util.$(OBJEXT) http2.$(OBJEXT) \ -@ENABLE_APP_TRUE@ timegm.$(OBJEXT) app_helper.$(OBJEXT) \ -@ENABLE_APP_TRUE@ nghttp2_gzip.$(OBJEXT) -am__objects_6 = -@ENABLE_APP_TRUE@@HAVE_LIBXML2_TRUE@am__objects_7 = \ -@ENABLE_APP_TRUE@@HAVE_LIBXML2_TRUE@ HtmlParser.$(OBJEXT) -@ENABLE_APP_TRUE@am__objects_8 = $(am__objects_7) -@ENABLE_APP_TRUE@am_nghttp_OBJECTS = $(am__objects_5) $(am__objects_6) \ -@ENABLE_APP_TRUE@ nghttp.$(OBJEXT) $(am__objects_8) \ -@ENABLE_APP_TRUE@ $(am__objects_6) ssl.$(OBJEXT) -nghttp_OBJECTS = $(am_nghttp_OBJECTS) -nghttp_LDADD = $(LDADD) -nghttp_DEPENDENCIES = $(top_builddir)/lib/libnghttp2.la \ - $(top_builddir)/third-party/libhttp-parser.la -am__nghttpd_SOURCES_DIST = util.cc http2.cc timegm.c app_helper.cc \ - nghttp2_gzip.c util.h http2.h timegm.h app_helper.h \ - nghttp2_config.h nghttp2_gzip.h nghttpd.cc ssl.cc ssl.h \ - HttpServer.cc HttpServer.h -@ENABLE_APP_TRUE@am_nghttpd_OBJECTS = $(am__objects_5) \ -@ENABLE_APP_TRUE@ $(am__objects_6) nghttpd.$(OBJEXT) \ -@ENABLE_APP_TRUE@ ssl.$(OBJEXT) HttpServer.$(OBJEXT) -nghttpd_OBJECTS = $(am_nghttpd_OBJECTS) -nghttpd_LDADD = $(LDADD) -nghttpd_DEPENDENCIES = $(top_builddir)/lib/libnghttp2.la \ - $(top_builddir)/third-party/libhttp-parser.la -am__nghttpx_SOURCES_DIST = shrpx.cc shrpx.h -@ENABLE_APP_TRUE@am_nghttpx_OBJECTS = shrpx.$(OBJEXT) -nghttpx_OBJECTS = $(am_nghttpx_OBJECTS) -am__DEPENDENCIES_2 = $(top_builddir)/lib/libnghttp2.la \ - $(top_builddir)/third-party/libhttp-parser.la -@ENABLE_APP_TRUE@nghttpx_DEPENDENCIES = libnghttpx.a \ -@ENABLE_APP_TRUE@ $(am__DEPENDENCIES_2) -am__nghttpx_unittest_SOURCES_DIST = shrpx-unittest.cc \ - shrpx_ssl_test.cc shrpx_ssl_test.h shrpx_downstream_test.cc \ - shrpx_downstream_test.h shrpx_config_test.cc \ - shrpx_config_test.h http2_test.cc http2_test.h util_test.cc \ - util_test.h nghttp2_gzip_test.c nghttp2_gzip_test.h \ - nghttp2_gzip.c nghttp2_gzip.h buffer_test.cc buffer_test.h \ - memchunk_test.cc memchunk_test.h -@ENABLE_APP_TRUE@@HAVE_CUNIT_TRUE@am_nghttpx_unittest_OBJECTS = nghttpx_unittest-shrpx-unittest.$(OBJEXT) \ -@ENABLE_APP_TRUE@@HAVE_CUNIT_TRUE@ nghttpx_unittest-shrpx_ssl_test.$(OBJEXT) \ -@ENABLE_APP_TRUE@@HAVE_CUNIT_TRUE@ nghttpx_unittest-shrpx_downstream_test.$(OBJEXT) \ -@ENABLE_APP_TRUE@@HAVE_CUNIT_TRUE@ nghttpx_unittest-shrpx_config_test.$(OBJEXT) \ -@ENABLE_APP_TRUE@@HAVE_CUNIT_TRUE@ nghttpx_unittest-http2_test.$(OBJEXT) \ -@ENABLE_APP_TRUE@@HAVE_CUNIT_TRUE@ nghttpx_unittest-util_test.$(OBJEXT) \ -@ENABLE_APP_TRUE@@HAVE_CUNIT_TRUE@ nghttpx_unittest-nghttp2_gzip_test.$(OBJEXT) \ -@ENABLE_APP_TRUE@@HAVE_CUNIT_TRUE@ nghttpx_unittest-nghttp2_gzip.$(OBJEXT) \ -@ENABLE_APP_TRUE@@HAVE_CUNIT_TRUE@ nghttpx_unittest-buffer_test.$(OBJEXT) \ -@ENABLE_APP_TRUE@@HAVE_CUNIT_TRUE@ nghttpx_unittest-memchunk_test.$(OBJEXT) -nghttpx_unittest_OBJECTS = $(am_nghttpx_unittest_OBJECTS) -@ENABLE_APP_TRUE@@HAVE_CUNIT_TRUE@nghttpx_unittest_DEPENDENCIES = \ -@ENABLE_APP_TRUE@@HAVE_CUNIT_TRUE@ libnghttpx.a \ -@ENABLE_APP_TRUE@@HAVE_CUNIT_TRUE@ $(am__DEPENDENCIES_2) -AM_V_P = $(am__v_P_@AM_V@) -am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) -am__v_P_0 = false -am__v_P_1 = : -AM_V_GEN = $(am__v_GEN_@AM_V@) -am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) -am__v_GEN_0 = @echo " GEN " $@; -am__v_GEN_1 = -AM_V_at = $(am__v_at_@AM_V@) -am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) -am__v_at_0 = @ -am__v_at_1 = -DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) -depcomp = $(SHELL) $(top_srcdir)/depcomp -am__depfiles_maybe = depfiles -am__mv = mv -f -COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ - $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ - $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ - $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ - $(AM_CFLAGS) $(CFLAGS) -AM_V_CC = $(am__v_CC_@AM_V@) -am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) -am__v_CC_0 = @echo " CC " $@; -am__v_CC_1 = -CCLD = $(CC) -LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ - $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ - $(AM_LDFLAGS) $(LDFLAGS) -o $@ -AM_V_CCLD = $(am__v_CCLD_@AM_V@) -am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) -am__v_CCLD_0 = @echo " CCLD " $@; -am__v_CCLD_1 = -CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ - $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -LTCXXCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \ - $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) \ - $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ - $(AM_CXXFLAGS) $(CXXFLAGS) -AM_V_CXX = $(am__v_CXX_@AM_V@) -am__v_CXX_ = $(am__v_CXX_@AM_DEFAULT_V@) -am__v_CXX_0 = @echo " CXX " $@; -am__v_CXX_1 = -CXXLD = $(CXX) -CXXLINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \ - $(LIBTOOLFLAGS) --mode=link $(CXXLD) $(AM_CXXFLAGS) \ - $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ -AM_V_CXXLD = $(am__v_CXXLD_@AM_V@) -am__v_CXXLD_ = $(am__v_CXXLD_@AM_DEFAULT_V@) -am__v_CXXLD_0 = @echo " CXXLD " $@; -am__v_CXXLD_1 = -SOURCES = $(libnghttpx_a_SOURCES) $(libnghttp2_asio_la_SOURCES) \ - $(deflatehd_SOURCES) $(h2load_SOURCES) $(inflatehd_SOURCES) \ - $(nghttp_SOURCES) $(nghttpd_SOURCES) $(nghttpx_SOURCES) \ - $(nghttpx_unittest_SOURCES) -DIST_SOURCES = $(am__libnghttpx_a_SOURCES_DIST) \ - $(am__libnghttp2_asio_la_SOURCES_DIST) \ - $(am__deflatehd_SOURCES_DIST) $(am__h2load_SOURCES_DIST) \ - $(am__inflatehd_SOURCES_DIST) $(am__nghttp_SOURCES_DIST) \ - $(am__nghttpd_SOURCES_DIST) $(am__nghttpx_SOURCES_DIST) \ - $(am__nghttpx_unittest_SOURCES_DIST) -RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \ - ctags-recursive dvi-recursive html-recursive info-recursive \ - install-data-recursive install-dvi-recursive \ - install-exec-recursive install-html-recursive \ - install-info-recursive install-pdf-recursive \ - install-ps-recursive install-recursive installcheck-recursive \ - installdirs-recursive pdf-recursive ps-recursive \ - tags-recursive uninstall-recursive -am__can_run_installinfo = \ - case $$AM_UPDATE_INFO_DIR in \ - n|no|NO) false;; \ - *) (install-info --version) >/dev/null 2>&1;; \ - esac -DATA = $(pkgconfig_DATA) -RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \ - distclean-recursive maintainer-clean-recursive -am__recursive_targets = \ - $(RECURSIVE_TARGETS) \ - $(RECURSIVE_CLEAN_TARGETS) \ - $(am__extra_recursive_targets) -AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \ - check recheck distdir -am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) -# Read a list of newline-separated strings from the standard input, -# and print each of them once, without duplicates. Input order is -# *not* preserved. -am__uniquify_input = $(AWK) '\ - BEGIN { nonempty = 0; } \ - { items[$$0] = 1; nonempty = 1; } \ - END { if (nonempty) { for (i in items) print i; }; } \ -' -# Make sure the list of sources is unique. This is necessary because, -# e.g., the same source file might be shared among _SOURCES variables -# for different programs/libraries. -am__define_uniq_tagged_files = \ - list='$(am__tagged_files)'; \ - unique=`for i in $$list; do \ - if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ - done | $(am__uniquify_input)` -ETAGS = etags -CTAGS = ctags -am__tty_colors_dummy = \ - mgn= red= grn= lgn= blu= brg= std=; \ - am__color_tests=no -am__tty_colors = { \ - $(am__tty_colors_dummy); \ - if test "X$(AM_COLOR_TESTS)" = Xno; then \ - am__color_tests=no; \ - elif test "X$(AM_COLOR_TESTS)" = Xalways; then \ - am__color_tests=yes; \ - elif test "X$$TERM" != Xdumb && { test -t 1; } 2>/dev/null; then \ - am__color_tests=yes; \ - fi; \ - if test $$am__color_tests = yes; then \ - red=''; \ - grn=''; \ - lgn=''; \ - blu=''; \ - mgn=''; \ - brg=''; \ - std=''; \ - fi; \ -} -am__recheck_rx = ^[ ]*:recheck:[ ]* -am__global_test_result_rx = ^[ ]*:global-test-result:[ ]* -am__copy_in_global_log_rx = ^[ ]*:copy-in-global-log:[ ]* -# A command that, given a newline-separated list of test names on the -# standard input, print the name of the tests that are to be re-run -# upon "make recheck". -am__list_recheck_tests = $(AWK) '{ \ - recheck = 1; \ - while ((rc = (getline line < ($$0 ".trs"))) != 0) \ - { \ - if (rc < 0) \ - { \ - if ((getline line2 < ($$0 ".log")) < 0) \ - recheck = 0; \ - break; \ - } \ - else if (line ~ /$(am__recheck_rx)[nN][Oo]/) \ - { \ - recheck = 0; \ - break; \ - } \ - else if (line ~ /$(am__recheck_rx)[yY][eE][sS]/) \ - { \ - break; \ - } \ - }; \ - if (recheck) \ - print $$0; \ - close ($$0 ".trs"); \ - close ($$0 ".log"); \ -}' -# A command that, given a newline-separated list of test names on the -# standard input, create the global log from their .trs and .log files. -am__create_global_log = $(AWK) ' \ -function fatal(msg) \ -{ \ - print "fatal: making $@: " msg | "cat >&2"; \ - exit 1; \ -} \ -function rst_section(header) \ -{ \ - print header; \ - len = length(header); \ - for (i = 1; i <= len; i = i + 1) \ - printf "="; \ - printf "\n\n"; \ -} \ -{ \ - copy_in_global_log = 1; \ - global_test_result = "RUN"; \ - while ((rc = (getline line < ($$0 ".trs"))) != 0) \ - { \ - if (rc < 0) \ - fatal("failed to read from " $$0 ".trs"); \ - if (line ~ /$(am__global_test_result_rx)/) \ - { \ - sub("$(am__global_test_result_rx)", "", line); \ - sub("[ ]*$$", "", line); \ - global_test_result = line; \ - } \ - else if (line ~ /$(am__copy_in_global_log_rx)[nN][oO]/) \ - copy_in_global_log = 0; \ - }; \ - if (copy_in_global_log) \ - { \ - rst_section(global_test_result ": " $$0); \ - while ((rc = (getline line < ($$0 ".log"))) != 0) \ - { \ - if (rc < 0) \ - fatal("failed to read from " $$0 ".log"); \ - print line; \ - }; \ - printf "\n"; \ - }; \ - close ($$0 ".trs"); \ - close ($$0 ".log"); \ -}' -# Restructured Text title. -am__rst_title = { sed 's/.*/ & /;h;s/./=/g;p;x;s/ *$$//;p;g' && echo; } -# Solaris 10 'make', and several other traditional 'make' implementations, -# pass "-e" to $(SHELL), and POSIX 2008 even requires this. Work around it -# by disabling -e (using the XSI extension "set +e") if it's set. -am__sh_e_setup = case $$- in *e*) set +e;; esac -# Default flags passed to test drivers. -am__common_driver_flags = \ - --color-tests "$$am__color_tests" \ - --enable-hard-errors "$$am__enable_hard_errors" \ - --expect-failure "$$am__expect_failure" -# To be inserted before the command running the test. Creates the -# directory for the log if needed. Stores in $dir the directory -# containing $f, in $tst the test, in $log the log. Executes the -# developer- defined test setup AM_TESTS_ENVIRONMENT (if any), and -# passes TESTS_ENVIRONMENT. Set up options for the wrapper that -# will run the test scripts (or their associated LOG_COMPILER, if -# thy have one). -am__check_pre = \ -$(am__sh_e_setup); \ -$(am__vpath_adj_setup) $(am__vpath_adj) \ -$(am__tty_colors); \ -srcdir=$(srcdir); export srcdir; \ -case "$@" in \ - */*) am__odir=`echo "./$@" | sed 's|/[^/]*$$||'`;; \ - *) am__odir=.;; \ -esac; \ -test "x$$am__odir" = x"." || test -d "$$am__odir" \ - || $(MKDIR_P) "$$am__odir" || exit $$?; \ -if test -f "./$$f"; then dir=./; \ -elif test -f "$$f"; then dir=; \ -else dir="$(srcdir)/"; fi; \ -tst=$$dir$$f; log='$@'; \ -if test -n '$(DISABLE_HARD_ERRORS)'; then \ - am__enable_hard_errors=no; \ -else \ - am__enable_hard_errors=yes; \ -fi; \ -case " $(XFAIL_TESTS) " in \ - *[\ \ ]$$f[\ \ ]* | *[\ \ ]$$dir$$f[\ \ ]*) \ - am__expect_failure=yes;; \ - *) \ - am__expect_failure=no;; \ -esac; \ -$(AM_TESTS_ENVIRONMENT) $(TESTS_ENVIRONMENT) -# A shell command to get the names of the tests scripts with any registered -# extension removed (i.e., equivalently, the names of the test logs, with -# the '.log' extension removed). The result is saved in the shell variable -# '$bases'. This honors runtime overriding of TESTS and TEST_LOGS. Sadly, -# we cannot use something simpler, involving e.g., "$(TEST_LOGS:.log=)", -# since that might cause problem with VPATH rewrites for suffix-less tests. -# See also 'test-harness-vpath-rewrite.sh' and 'test-trs-basic.sh'. -am__set_TESTS_bases = \ - bases='$(TEST_LOGS)'; \ - bases=`for i in $$bases; do echo $$i; done | sed 's/\.log$$//'`; \ - bases=`echo $$bases` -RECHECK_LOGS = $(TEST_LOGS) -TEST_SUITE_LOG = test-suite.log -TEST_EXTENSIONS = @EXEEXT@ .test -LOG_DRIVER = $(SHELL) $(top_srcdir)/test-driver -LOG_COMPILE = $(LOG_COMPILER) $(AM_LOG_FLAGS) $(LOG_FLAGS) -am__set_b = \ - case '$@' in \ - */*) \ - case '$*' in \ - */*) b='$*';; \ - *) b=`echo '$@' | sed 's/\.log$$//'`; \ - esac;; \ - *) \ - b='$*';; \ - esac -am__test_logs1 = $(TESTS:=.log) -am__test_logs2 = $(am__test_logs1:@EXEEXT@.log=.log) -TEST_LOGS = $(am__test_logs2:.test.log=.log) -TEST_LOG_DRIVER = $(SHELL) $(top_srcdir)/test-driver -TEST_LOG_COMPILE = $(TEST_LOG_COMPILER) $(AM_TEST_LOG_FLAGS) \ - $(TEST_LOG_FLAGS) -DIST_SUBDIRS = $(SUBDIRS) -DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) -am__relativize = \ - dir0=`pwd`; \ - sed_first='s,^\([^/]*\)/.*$$,\1,'; \ - sed_rest='s,^[^/]*/*,,'; \ - sed_last='s,^.*/\([^/]*\)$$,\1,'; \ - sed_butlast='s,/*[^/]*$$,,'; \ - while test -n "$$dir1"; do \ - first=`echo "$$dir1" | sed -e "$$sed_first"`; \ - if test "$$first" != "."; then \ - if test "$$first" = ".."; then \ - dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \ - dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \ - else \ - first2=`echo "$$dir2" | sed -e "$$sed_first"`; \ - if test "$$first2" = "$$first"; then \ - dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \ - else \ - dir2="../$$dir2"; \ - fi; \ - dir0="$$dir0"/"$$first"; \ - fi; \ - fi; \ - dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \ - done; \ - reldir="$$dir2" -ACLOCAL = @ACLOCAL@ -AMTAR = @AMTAR@ -AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ -APPLDFLAGS = @APPLDFLAGS@ -AR = @AR@ -AUTOCONF = @AUTOCONF@ -AUTOHEADER = @AUTOHEADER@ -AUTOMAKE = @AUTOMAKE@ -AWK = @AWK@ -BOOST_ASIO_LIB = @BOOST_ASIO_LIB@ -BOOST_CPPFLAGS = @BOOST_CPPFLAGS@ -BOOST_LDFLAGS = @BOOST_LDFLAGS@ -BOOST_SYSTEM_LIB = @BOOST_SYSTEM_LIB@ -BOOST_THREAD_LIB = @BOOST_THREAD_LIB@ -CC = @CC@ -CCDEPMODE = @CCDEPMODE@ -CFLAGS = @CFLAGS@ -CPP = @CPP@ -CPPFLAGS = @CPPFLAGS@ -CUNIT_CFLAGS = @CUNIT_CFLAGS@ -CUNIT_LIBS = @CUNIT_LIBS@ -CXX = @CXX@ -CXXCPP = @CXXCPP@ -CXXDEPMODE = @CXXDEPMODE@ -CXXFLAGS = @CXXFLAGS@ -CYGPATH_W = @CYGPATH_W@ -CYTHON = @CYTHON@ -DEFS = @DEFS@ -DEPDIR = @DEPDIR@ -DLLTOOL = @DLLTOOL@ -DSYMUTIL = @DSYMUTIL@ -DUMPBIN = @DUMPBIN@ -ECHO_C = @ECHO_C@ -ECHO_N = @ECHO_N@ -ECHO_T = @ECHO_T@ -EGREP = @EGREP@ -EXEEXT = @EXEEXT@ -FGREP = @FGREP@ -GREP = @GREP@ -HAVE_CXX11 = @HAVE_CXX11@ -INSTALL = @INSTALL@ -INSTALL_DATA = @INSTALL_DATA@ -INSTALL_PROGRAM = @INSTALL_PROGRAM@ -INSTALL_SCRIPT = @INSTALL_SCRIPT@ -INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ -JANSSON_CFLAGS = @JANSSON_CFLAGS@ -JANSSON_LIBS = @JANSSON_LIBS@ -JEMALLOC_LIBS = @JEMALLOC_LIBS@ -LD = @LD@ -LDFLAGS = @LDFLAGS@ -LIBEVENT_OPENSSL_CFLAGS = @LIBEVENT_OPENSSL_CFLAGS@ -LIBEVENT_OPENSSL_LIBS = @LIBEVENT_OPENSSL_LIBS@ -LIBEV_CFLAGS = @LIBEV_CFLAGS@ -LIBEV_LIBS = @LIBEV_LIBS@ -LIBOBJS = @LIBOBJS@ -LIBS = @LIBS@ -LIBSPDYLAY_CFLAGS = @LIBSPDYLAY_CFLAGS@ -LIBSPDYLAY_LIBS = @LIBSPDYLAY_LIBS@ -LIBTOOL = @LIBTOOL@ -LIPO = @LIPO@ -LN_S = @LN_S@ -LTLIBOBJS = @LTLIBOBJS@ -LT_AGE = @LT_AGE@ -LT_CURRENT = @LT_CURRENT@ -LT_REVISION = @LT_REVISION@ -MAKEINFO = @MAKEINFO@ -MANIFEST_TOOL = @MANIFEST_TOOL@ -MKDIR_P = @MKDIR_P@ -NM = @NM@ -NMEDIT = @NMEDIT@ -OBJDUMP = @OBJDUMP@ -OBJEXT = @OBJEXT@ -OPENSSL_CFLAGS = @OPENSSL_CFLAGS@ -OPENSSL_LIBS = @OPENSSL_LIBS@ -OTOOL = @OTOOL@ -OTOOL64 = @OTOOL64@ -PACKAGE = @PACKAGE@ -PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ -PACKAGE_NAME = @PACKAGE_NAME@ -PACKAGE_STRING = @PACKAGE_STRING@ -PACKAGE_TARNAME = @PACKAGE_TARNAME@ -PACKAGE_URL = @PACKAGE_URL@ -PACKAGE_VERSION = @PACKAGE_VERSION@ -PACKAGE_VERSION_NUM = @PACKAGE_VERSION_NUM@ -PATH_SEPARATOR = @PATH_SEPARATOR@ -PKG_CONFIG = @PKG_CONFIG@ -PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ -PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ -PYTHON = @PYTHON@ -PYTHON_CPPFLAGS = @PYTHON_CPPFLAGS@ -PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@ -PYTHON_EXTRA_LDFLAGS = @PYTHON_EXTRA_LDFLAGS@ -PYTHON_EXTRA_LIBS = @PYTHON_EXTRA_LIBS@ -PYTHON_LDFLAGS = @PYTHON_LDFLAGS@ -PYTHON_PLATFORM = @PYTHON_PLATFORM@ -PYTHON_PREFIX = @PYTHON_PREFIX@ -PYTHON_SITE_PKG = @PYTHON_SITE_PKG@ -PYTHON_VERSION = @PYTHON_VERSION@ -RANLIB = @RANLIB@ -SED = @SED@ -SET_MAKE = @SET_MAKE@ -SHELL = @SHELL@ -STRIP = @STRIP@ -TESTLDADD = @TESTLDADD@ -VERSION = @VERSION@ -WARNCFLAGS = @WARNCFLAGS@ -XML2_CONFIG = @XML2_CONFIG@ -XML_CPPFLAGS = @XML_CPPFLAGS@ -XML_LIBS = @XML_LIBS@ -ZLIB_CFLAGS = @ZLIB_CFLAGS@ -ZLIB_LIBS = @ZLIB_LIBS@ -abs_builddir = @abs_builddir@ -abs_srcdir = @abs_srcdir@ -abs_top_builddir = @abs_top_builddir@ -abs_top_srcdir = @abs_top_srcdir@ -ac_ct_AR = @ac_ct_AR@ -ac_ct_CC = @ac_ct_CC@ -ac_ct_CXX = @ac_ct_CXX@ -ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ -am__include = @am__include@ -am__leading_dot = @am__leading_dot@ -am__quote = @am__quote@ -am__tar = @am__tar@ -am__untar = @am__untar@ -bindir = @bindir@ -build = @build@ -build_alias = @build_alias@ -build_cpu = @build_cpu@ -build_os = @build_os@ -build_vendor = @build_vendor@ -builddir = @builddir@ -datadir = @datadir@ -datarootdir = @datarootdir@ -docdir = @docdir@ -dvidir = @dvidir@ -exec_prefix = @exec_prefix@ -host = @host@ -host_alias = @host_alias@ -host_cpu = @host_cpu@ -host_os = @host_os@ -host_vendor = @host_vendor@ -htmldir = @htmldir@ -includedir = @includedir@ -infodir = @infodir@ -install_sh = @install_sh@ -libdir = @libdir@ -libexecdir = @libexecdir@ -localedir = @localedir@ -localstatedir = @localstatedir@ -mandir = @mandir@ -mkdir_p = @mkdir_p@ -oldincludedir = @oldincludedir@ -pdfdir = @pdfdir@ -pkgpyexecdir = @pkgpyexecdir@ -pkgpythondir = @pkgpythondir@ -prefix = @prefix@ -program_transform_name = @program_transform_name@ -psdir = @psdir@ -pyexecdir = @pyexecdir@ -pythondir = @pythondir@ -sbindir = @sbindir@ -sharedstatedir = @sharedstatedir@ -srcdir = @srcdir@ -sysconfdir = @sysconfdir@ -target = @target@ -target_alias = @target_alias@ -target_cpu = @target_cpu@ -target_os = @target_os@ -target_vendor = @target_vendor@ -top_build_prefix = @top_build_prefix@ -top_builddir = @top_builddir@ -top_srcdir = @top_srcdir@ - -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -SUBDIRS = includes -AM_CFLAGS = $(WARNCFLAGS) -AM_CPPFLAGS = \ - -DPKGDATADIR='"$(pkgdatadir)"' \ - -Wall \ - -I$(top_srcdir)/lib/includes \ - -I$(top_builddir)/lib/includes \ - -I$(top_srcdir)/lib \ - -I$(top_srcdir)/src/includes \ - -I$(top_srcdir)/third-party \ - @LIBSPDYLAY_CFLAGS@ \ - @XML_CPPFLAGS@ \ - @LIBEV_CFLAGS@ \ - @OPENSSL_CFLAGS@ \ - @JANSSON_CFLAGS@ \ - @ZLIB_CFLAGS@ \ - @DEFS@ - -LDADD = $(top_builddir)/lib/libnghttp2.la \ - $(top_builddir)/third-party/libhttp-parser.la \ - @JEMALLOC_LIBS@ \ - @LIBSPDYLAY_LIBS@ \ - @XML_LIBS@ \ - @LIBEV_LIBS@ \ - @OPENSSL_LIBS@ \ - @JANSSON_LIBS@ \ - @ZLIB_LIBS@ \ - @APPLDFLAGS@ - -@ENABLE_APP_TRUE@HELPER_OBJECTS = util.cc \ -@ENABLE_APP_TRUE@ http2.cc timegm.c app_helper.cc nghttp2_gzip.c - -@ENABLE_APP_TRUE@HELPER_HFILES = util.h \ -@ENABLE_APP_TRUE@ http2.h timegm.h app_helper.h nghttp2_config.h \ -@ENABLE_APP_TRUE@ nghttp2_gzip.h - -@ENABLE_APP_TRUE@HTML_PARSER_OBJECTS = $(am__append_2) -@ENABLE_APP_TRUE@HTML_PARSER_HFILES = HtmlParser.h -@ENABLE_APP_TRUE@nghttp_SOURCES = ${HELPER_OBJECTS} ${HELPER_HFILES} nghttp.cc nghttp.h \ -@ENABLE_APP_TRUE@ ${HTML_PARSER_OBJECTS} ${HTML_PARSER_HFILES} \ -@ENABLE_APP_TRUE@ ssl.cc ssl.h - -@ENABLE_APP_TRUE@nghttpd_SOURCES = ${HELPER_OBJECTS} ${HELPER_HFILES} nghttpd.cc \ -@ENABLE_APP_TRUE@ ssl.cc ssl.h \ -@ENABLE_APP_TRUE@ HttpServer.cc HttpServer.h - -@ENABLE_APP_TRUE@h2load_SOURCES = util.cc util.h http2.cc http2.h \ -@ENABLE_APP_TRUE@ h2load.cc h2load.h timegm.c timegm.h ssl.cc \ -@ENABLE_APP_TRUE@ ssl.h h2load_session.h \ -@ENABLE_APP_TRUE@ h2load_http2_session.cc \ -@ENABLE_APP_TRUE@ h2load_http2_session.h $(am__append_3) -@ENABLE_APP_TRUE@NGHTTPX_SRCS = util.cc util.h http2.cc http2.h \ -@ENABLE_APP_TRUE@ timegm.c timegm.h base64.h app_helper.cc \ -@ENABLE_APP_TRUE@ app_helper.h ssl.cc ssl.h shrpx_config.cc \ -@ENABLE_APP_TRUE@ shrpx_config.h shrpx_error.h \ -@ENABLE_APP_TRUE@ shrpx_accept_handler.cc \ -@ENABLE_APP_TRUE@ shrpx_accept_handler.h \ -@ENABLE_APP_TRUE@ shrpx_connection_handler.cc \ -@ENABLE_APP_TRUE@ shrpx_connection_handler.h \ -@ENABLE_APP_TRUE@ shrpx_client_handler.cc \ -@ENABLE_APP_TRUE@ shrpx_client_handler.h shrpx_upstream.h \ -@ENABLE_APP_TRUE@ shrpx_http2_upstream.cc \ -@ENABLE_APP_TRUE@ shrpx_http2_upstream.h \ -@ENABLE_APP_TRUE@ shrpx_https_upstream.cc \ -@ENABLE_APP_TRUE@ shrpx_https_upstream.h shrpx_downstream.cc \ -@ENABLE_APP_TRUE@ shrpx_downstream.h \ -@ENABLE_APP_TRUE@ shrpx_downstream_connection.cc \ -@ENABLE_APP_TRUE@ shrpx_downstream_connection.h \ -@ENABLE_APP_TRUE@ shrpx_http_downstream_connection.cc \ -@ENABLE_APP_TRUE@ shrpx_http_downstream_connection.h \ -@ENABLE_APP_TRUE@ shrpx_http2_downstream_connection.cc \ -@ENABLE_APP_TRUE@ shrpx_http2_downstream_connection.h \ -@ENABLE_APP_TRUE@ shrpx_http2_session.cc shrpx_http2_session.h \ -@ENABLE_APP_TRUE@ shrpx_downstream_queue.cc \ -@ENABLE_APP_TRUE@ shrpx_downstream_queue.h shrpx_log.cc \ -@ENABLE_APP_TRUE@ shrpx_log.h shrpx_http.cc shrpx_http.h \ -@ENABLE_APP_TRUE@ shrpx_io_control.cc shrpx_io_control.h \ -@ENABLE_APP_TRUE@ shrpx_ssl.cc shrpx_ssl.h shrpx_worker.cc \ -@ENABLE_APP_TRUE@ shrpx_worker.h shrpx_log_config.cc \ -@ENABLE_APP_TRUE@ shrpx_log_config.h shrpx_connect_blocker.cc \ -@ENABLE_APP_TRUE@ shrpx_connect_blocker.h \ -@ENABLE_APP_TRUE@ shrpx_downstream_connection_pool.cc \ -@ENABLE_APP_TRUE@ shrpx_downstream_connection_pool.h \ -@ENABLE_APP_TRUE@ shrpx_rate_limit.cc shrpx_rate_limit.h \ -@ENABLE_APP_TRUE@ shrpx_connection.cc shrpx_connection.h \ -@ENABLE_APP_TRUE@ buffer.h memchunk.h template.h \ -@ENABLE_APP_TRUE@ $(am__append_4) -@ENABLE_APP_TRUE@noinst_LIBRARIES = libnghttpx.a -@ENABLE_APP_TRUE@libnghttpx_a_SOURCES = ${NGHTTPX_SRCS} -@ENABLE_APP_TRUE@nghttpx_SOURCES = shrpx.cc shrpx.h -@ENABLE_APP_TRUE@nghttpx_LDADD = libnghttpx.a ${LDADD} -@ENABLE_APP_TRUE@@HAVE_CUNIT_TRUE@nghttpx_unittest_SOURCES = shrpx-unittest.cc \ -@ENABLE_APP_TRUE@@HAVE_CUNIT_TRUE@ shrpx_ssl_test.cc shrpx_ssl_test.h \ -@ENABLE_APP_TRUE@@HAVE_CUNIT_TRUE@ shrpx_downstream_test.cc shrpx_downstream_test.h \ -@ENABLE_APP_TRUE@@HAVE_CUNIT_TRUE@ shrpx_config_test.cc shrpx_config_test.h \ -@ENABLE_APP_TRUE@@HAVE_CUNIT_TRUE@ http2_test.cc http2_test.h \ -@ENABLE_APP_TRUE@@HAVE_CUNIT_TRUE@ util_test.cc util_test.h \ -@ENABLE_APP_TRUE@@HAVE_CUNIT_TRUE@ nghttp2_gzip_test.c nghttp2_gzip_test.h \ -@ENABLE_APP_TRUE@@HAVE_CUNIT_TRUE@ nghttp2_gzip.c nghttp2_gzip.h \ -@ENABLE_APP_TRUE@@HAVE_CUNIT_TRUE@ buffer_test.cc buffer_test.h \ -@ENABLE_APP_TRUE@@HAVE_CUNIT_TRUE@ memchunk_test.cc memchunk_test.h - -@ENABLE_APP_TRUE@@HAVE_CUNIT_TRUE@nghttpx_unittest_CPPFLAGS = ${AM_CPPFLAGS}\ -@ENABLE_APP_TRUE@@HAVE_CUNIT_TRUE@ -DNGHTTP2_TESTS_DIR=\"$(top_srcdir)/tests\" - -@ENABLE_APP_TRUE@@HAVE_CUNIT_TRUE@nghttpx_unittest_LDADD = libnghttpx.a ${LDADD} @CUNIT_LIBS@ @TESTLDADD@ -@ENABLE_HPACK_TOOLS_TRUE@HPACK_TOOLS_COMMON_SRCS = comp_helper.c comp_helper.h -@ENABLE_HPACK_TOOLS_TRUE@inflatehd_SOURCES = inflatehd.cc $(HPACK_TOOLS_COMMON_SRCS) -@ENABLE_HPACK_TOOLS_TRUE@deflatehd_SOURCES = deflatehd.cc $(HPACK_TOOLS_COMMON_SRCS) -@ENABLE_ASIO_LIB_TRUE@pkgconfigdir = $(libdir)/pkgconfig -@ENABLE_ASIO_LIB_TRUE@pkgconfig_DATA = libnghttp2_asio.pc -@ENABLE_ASIO_LIB_TRUE@DISTCLEANFILES = $(pkgconfig_DATA) -@ENABLE_ASIO_LIB_TRUE@lib_LTLIBRARIES = libnghttp2_asio.la -@ENABLE_ASIO_LIB_TRUE@libnghttp2_asio_la_SOURCES = \ -@ENABLE_ASIO_LIB_TRUE@ util.cc util.h http2.cc http2.h \ -@ENABLE_ASIO_LIB_TRUE@ ssl.cc ssl.h \ -@ENABLE_ASIO_LIB_TRUE@ asio_common.cc asio_common.h \ -@ENABLE_ASIO_LIB_TRUE@ asio_io_service_pool.cc asio_io_service_pool.h \ -@ENABLE_ASIO_LIB_TRUE@ asio_server_http2.cc \ -@ENABLE_ASIO_LIB_TRUE@ asio_server_http2_impl.cc asio_server_http2_impl.h \ -@ENABLE_ASIO_LIB_TRUE@ asio_server.cc asio_server.h \ -@ENABLE_ASIO_LIB_TRUE@ asio_server_http2_handler.cc asio_server_http2_handler.h \ -@ENABLE_ASIO_LIB_TRUE@ asio_server_connection.h \ -@ENABLE_ASIO_LIB_TRUE@ asio_server_request.cc \ -@ENABLE_ASIO_LIB_TRUE@ asio_server_request_impl.cc asio_server_request_impl.h \ -@ENABLE_ASIO_LIB_TRUE@ asio_server_response.cc \ -@ENABLE_ASIO_LIB_TRUE@ asio_server_response_impl.cc asio_server_response_impl.h \ -@ENABLE_ASIO_LIB_TRUE@ asio_server_stream.cc asio_server_stream.h \ -@ENABLE_ASIO_LIB_TRUE@ asio_server_serve_mux.cc asio_server_serve_mux.h \ -@ENABLE_ASIO_LIB_TRUE@ asio_server_request_handler.cc asio_server_request_handler.h \ -@ENABLE_ASIO_LIB_TRUE@ asio_server_tls_context.cc asio_server_tls_context.h \ -@ENABLE_ASIO_LIB_TRUE@ asio_client_session.cc \ -@ENABLE_ASIO_LIB_TRUE@ asio_client_session_impl.cc asio_client_session_impl.h \ -@ENABLE_ASIO_LIB_TRUE@ asio_client_session_tcp_impl.cc asio_client_session_tcp_impl.h \ -@ENABLE_ASIO_LIB_TRUE@ asio_client_session_tls_impl.cc asio_client_session_tls_impl.h \ -@ENABLE_ASIO_LIB_TRUE@ asio_client_response.cc \ -@ENABLE_ASIO_LIB_TRUE@ asio_client_response_impl.cc asio_client_response_impl.h \ -@ENABLE_ASIO_LIB_TRUE@ asio_client_request.cc \ -@ENABLE_ASIO_LIB_TRUE@ asio_client_request_impl.cc asio_client_request_impl.h \ -@ENABLE_ASIO_LIB_TRUE@ asio_client_stream.cc asio_client_stream.h \ -@ENABLE_ASIO_LIB_TRUE@ asio_client_tls_context.cc asio_client_tls_context.h - -@ENABLE_ASIO_LIB_TRUE@libnghttp2_asio_la_CPPFLAGS = ${AM_CPPFLAGS} ${BOOST_CPPFLAGS} -@ENABLE_ASIO_LIB_TRUE@libnghttp2_asio_la_LDFLAGS = -no-undefined -version-info 1:0:0 -@ENABLE_ASIO_LIB_TRUE@libnghttp2_asio_la_LIBADD = \ -@ENABLE_ASIO_LIB_TRUE@ $(top_builddir)/lib/libnghttp2.la \ -@ENABLE_ASIO_LIB_TRUE@ $(top_builddir)/third-party/libhttp-parser.la \ -@ENABLE_ASIO_LIB_TRUE@ ${BOOST_LDFLAGS} \ -@ENABLE_ASIO_LIB_TRUE@ ${BOOST_ASIO_LIB} \ -@ENABLE_ASIO_LIB_TRUE@ ${BOOST_THREAD_LIB} \ -@ENABLE_ASIO_LIB_TRUE@ ${BOOST_SYSTEM_LIB} \ -@ENABLE_ASIO_LIB_TRUE@ @OPENSSL_LIBS@ - -all: all-recursive - -.SUFFIXES: -.SUFFIXES: .c .cc .lo .log .o .obj .test .test$(EXEEXT) .trs -$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) - @for dep in $?; do \ - case '$(am__configure_deps)' in \ - *$$dep*) \ - ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ - && { if test -f $@; then exit 0; else break; fi; }; \ - exit 1;; \ - esac; \ - done; \ - echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/Makefile'; \ - $(am__cd) $(top_srcdir) && \ - $(AUTOMAKE) --gnu src/Makefile -.PRECIOUS: Makefile -Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status - @case '$?' in \ - *config.status*) \ - cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ - *) \ - echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ - cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ - esac; - -$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) - cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh - -$(top_srcdir)/configure: $(am__configure_deps) - cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh -$(ACLOCAL_M4): $(am__aclocal_m4_deps) - cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh -$(am__aclocal_m4_deps): -libnghttp2_asio.pc: $(top_builddir)/config.status $(srcdir)/libnghttp2_asio.pc.in - cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ - -clean-noinstLIBRARIES: - -test -z "$(noinst_LIBRARIES)" || rm -f $(noinst_LIBRARIES) - -libnghttpx.a: $(libnghttpx_a_OBJECTS) $(libnghttpx_a_DEPENDENCIES) $(EXTRA_libnghttpx_a_DEPENDENCIES) - $(AM_V_at)-rm -f libnghttpx.a - $(AM_V_AR)$(libnghttpx_a_AR) libnghttpx.a $(libnghttpx_a_OBJECTS) $(libnghttpx_a_LIBADD) - $(AM_V_at)$(RANLIB) libnghttpx.a - -install-libLTLIBRARIES: $(lib_LTLIBRARIES) - @$(NORMAL_INSTALL) - @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \ - list2=; for p in $$list; do \ - if test -f $$p; then \ - list2="$$list2 $$p"; \ - else :; fi; \ - done; \ - test -z "$$list2" || { \ - echo " $(MKDIR_P) '$(DESTDIR)$(libdir)'"; \ - $(MKDIR_P) "$(DESTDIR)$(libdir)" || exit 1; \ - echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(libdir)'"; \ - $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(libdir)"; \ - } - -uninstall-libLTLIBRARIES: - @$(NORMAL_UNINSTALL) - @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \ - for p in $$list; do \ - $(am__strip_dir) \ - echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$f'"; \ - $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$f"; \ - done - -clean-libLTLIBRARIES: - -test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES) - @list='$(lib_LTLIBRARIES)'; \ - locs=`for p in $$list; do echo $$p; done | \ - sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ - sort -u`; \ - test -z "$$locs" || { \ - echo rm -f $${locs}; \ - rm -f $${locs}; \ - } - -libnghttp2_asio.la: $(libnghttp2_asio_la_OBJECTS) $(libnghttp2_asio_la_DEPENDENCIES) $(EXTRA_libnghttp2_asio_la_DEPENDENCIES) - $(AM_V_CXXLD)$(libnghttp2_asio_la_LINK) $(am_libnghttp2_asio_la_rpath) $(libnghttp2_asio_la_OBJECTS) $(libnghttp2_asio_la_LIBADD) $(LIBS) -install-binPROGRAMS: $(bin_PROGRAMS) - @$(NORMAL_INSTALL) - @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \ - if test -n "$$list"; then \ - echo " $(MKDIR_P) '$(DESTDIR)$(bindir)'"; \ - $(MKDIR_P) "$(DESTDIR)$(bindir)" || exit 1; \ - fi; \ - for p in $$list; do echo "$$p $$p"; done | \ - sed 's/$(EXEEXT)$$//' | \ - while read p p1; do if test -f $$p \ - || test -f $$p1 \ - ; then echo "$$p"; echo "$$p"; else :; fi; \ - done | \ - sed -e 'p;s,.*/,,;n;h' \ - -e 's|.*|.|' \ - -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \ - sed 'N;N;N;s,\n, ,g' | \ - $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \ - { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ - if ($$2 == $$4) files[d] = files[d] " " $$1; \ - else { print "f", $$3 "/" $$4, $$1; } } \ - END { for (d in files) print "f", d, files[d] }' | \ - while read type dir files; do \ - if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ - test -z "$$files" || { \ - echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(bindir)$$dir'"; \ - $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(bindir)$$dir" || exit $$?; \ - } \ - ; done - -uninstall-binPROGRAMS: - @$(NORMAL_UNINSTALL) - @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \ - files=`for p in $$list; do echo "$$p"; done | \ - sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \ - -e 's/$$/$(EXEEXT)/' \ - `; \ - test -n "$$list" || exit 0; \ - echo " ( cd '$(DESTDIR)$(bindir)' && rm -f" $$files ")"; \ - cd "$(DESTDIR)$(bindir)" && rm -f $$files - -clean-binPROGRAMS: - @list='$(bin_PROGRAMS)'; test -n "$$list" || exit 0; \ - echo " rm -f" $$list; \ - rm -f $$list || exit $$?; \ - test -n "$(EXEEXT)" || exit 0; \ - list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ - echo " rm -f" $$list; \ - rm -f $$list - -clean-checkPROGRAMS: - @list='$(check_PROGRAMS)'; test -n "$$list" || exit 0; \ - echo " rm -f" $$list; \ - rm -f $$list || exit $$?; \ - test -n "$(EXEEXT)" || exit 0; \ - list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ - echo " rm -f" $$list; \ - rm -f $$list - -deflatehd$(EXEEXT): $(deflatehd_OBJECTS) $(deflatehd_DEPENDENCIES) $(EXTRA_deflatehd_DEPENDENCIES) - @rm -f deflatehd$(EXEEXT) - $(AM_V_CXXLD)$(CXXLINK) $(deflatehd_OBJECTS) $(deflatehd_LDADD) $(LIBS) - -h2load$(EXEEXT): $(h2load_OBJECTS) $(h2load_DEPENDENCIES) $(EXTRA_h2load_DEPENDENCIES) - @rm -f h2load$(EXEEXT) - $(AM_V_CXXLD)$(CXXLINK) $(h2load_OBJECTS) $(h2load_LDADD) $(LIBS) - -inflatehd$(EXEEXT): $(inflatehd_OBJECTS) $(inflatehd_DEPENDENCIES) $(EXTRA_inflatehd_DEPENDENCIES) - @rm -f inflatehd$(EXEEXT) - $(AM_V_CXXLD)$(CXXLINK) $(inflatehd_OBJECTS) $(inflatehd_LDADD) $(LIBS) - -nghttp$(EXEEXT): $(nghttp_OBJECTS) $(nghttp_DEPENDENCIES) $(EXTRA_nghttp_DEPENDENCIES) - @rm -f nghttp$(EXEEXT) - $(AM_V_CXXLD)$(CXXLINK) $(nghttp_OBJECTS) $(nghttp_LDADD) $(LIBS) - -nghttpd$(EXEEXT): $(nghttpd_OBJECTS) $(nghttpd_DEPENDENCIES) $(EXTRA_nghttpd_DEPENDENCIES) - @rm -f nghttpd$(EXEEXT) - $(AM_V_CXXLD)$(CXXLINK) $(nghttpd_OBJECTS) $(nghttpd_LDADD) $(LIBS) - -nghttpx$(EXEEXT): $(nghttpx_OBJECTS) $(nghttpx_DEPENDENCIES) $(EXTRA_nghttpx_DEPENDENCIES) - @rm -f nghttpx$(EXEEXT) - $(AM_V_CXXLD)$(CXXLINK) $(nghttpx_OBJECTS) $(nghttpx_LDADD) $(LIBS) - -nghttpx-unittest$(EXEEXT): $(nghttpx_unittest_OBJECTS) $(nghttpx_unittest_DEPENDENCIES) $(EXTRA_nghttpx_unittest_DEPENDENCIES) - @rm -f nghttpx-unittest$(EXEEXT) - $(AM_V_CXXLD)$(CXXLINK) $(nghttpx_unittest_OBJECTS) $(nghttpx_unittest_LDADD) $(LIBS) - -mostlyclean-compile: - -rm -f *.$(OBJEXT) - -distclean-compile: - -rm -f *.tab.c - -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/HtmlParser.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/HttpServer.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/app_helper.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/comp_helper.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/deflatehd.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/h2load.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/h2load_http2_session.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/h2load_spdy_session.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/http2.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/inflatehd.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttp2_asio_la-asio_client_request.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttp2_asio_la-asio_client_request_impl.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttp2_asio_la-asio_client_response.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttp2_asio_la-asio_client_response_impl.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttp2_asio_la-asio_client_session.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttp2_asio_la-asio_client_session_impl.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttp2_asio_la-asio_client_session_tcp_impl.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttp2_asio_la-asio_client_session_tls_impl.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttp2_asio_la-asio_client_stream.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttp2_asio_la-asio_client_tls_context.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttp2_asio_la-asio_common.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttp2_asio_la-asio_io_service_pool.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttp2_asio_la-asio_server.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttp2_asio_la-asio_server_http2.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttp2_asio_la-asio_server_http2_handler.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttp2_asio_la-asio_server_http2_impl.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttp2_asio_la-asio_server_request.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttp2_asio_la-asio_server_request_handler.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttp2_asio_la-asio_server_request_impl.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttp2_asio_la-asio_server_response.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttp2_asio_la-asio_server_response_impl.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttp2_asio_la-asio_server_serve_mux.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttp2_asio_la-asio_server_stream.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttp2_asio_la-asio_server_tls_context.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttp2_asio_la-http2.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttp2_asio_la-ssl.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttp2_asio_la-util.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_gzip.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttpd.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttpx_unittest-buffer_test.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttpx_unittest-http2_test.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttpx_unittest-memchunk_test.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttpx_unittest-nghttp2_gzip.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttpx_unittest-nghttp2_gzip_test.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttpx_unittest-shrpx-unittest.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttpx_unittest-shrpx_config_test.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttpx_unittest-shrpx_downstream_test.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttpx_unittest-shrpx_ssl_test.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttpx_unittest-util_test.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/shrpx.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/shrpx_accept_handler.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/shrpx_client_handler.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/shrpx_config.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/shrpx_connect_blocker.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/shrpx_connection.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/shrpx_connection_handler.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/shrpx_downstream.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/shrpx_downstream_connection.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/shrpx_downstream_connection_pool.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/shrpx_downstream_queue.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/shrpx_http.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/shrpx_http2_downstream_connection.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/shrpx_http2_session.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/shrpx_http2_upstream.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/shrpx_http_downstream_connection.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/shrpx_https_upstream.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/shrpx_io_control.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/shrpx_log.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/shrpx_log_config.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/shrpx_rate_limit.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/shrpx_spdy_upstream.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/shrpx_ssl.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/shrpx_worker.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ssl.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/timegm.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/util.Po@am__quote@ - -.c.o: -@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\ -@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ -@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< - -.c.obj: -@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\ -@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\ -@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` - -.c.lo: -@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\ -@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ -@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< - -nghttpx_unittest-nghttp2_gzip_test.o: nghttp2_gzip_test.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT nghttpx_unittest-nghttp2_gzip_test.o -MD -MP -MF $(DEPDIR)/nghttpx_unittest-nghttp2_gzip_test.Tpo -c -o nghttpx_unittest-nghttp2_gzip_test.o `test -f 'nghttp2_gzip_test.c' || echo '$(srcdir)/'`nghttp2_gzip_test.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nghttpx_unittest-nghttp2_gzip_test.Tpo $(DEPDIR)/nghttpx_unittest-nghttp2_gzip_test.Po -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='nghttp2_gzip_test.c' object='nghttpx_unittest-nghttp2_gzip_test.o' libtool=no @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o nghttpx_unittest-nghttp2_gzip_test.o `test -f 'nghttp2_gzip_test.c' || echo '$(srcdir)/'`nghttp2_gzip_test.c - -nghttpx_unittest-nghttp2_gzip_test.obj: nghttp2_gzip_test.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT nghttpx_unittest-nghttp2_gzip_test.obj -MD -MP -MF $(DEPDIR)/nghttpx_unittest-nghttp2_gzip_test.Tpo -c -o nghttpx_unittest-nghttp2_gzip_test.obj `if test -f 'nghttp2_gzip_test.c'; then $(CYGPATH_W) 'nghttp2_gzip_test.c'; else $(CYGPATH_W) '$(srcdir)/nghttp2_gzip_test.c'; fi` -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nghttpx_unittest-nghttp2_gzip_test.Tpo $(DEPDIR)/nghttpx_unittest-nghttp2_gzip_test.Po -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='nghttp2_gzip_test.c' object='nghttpx_unittest-nghttp2_gzip_test.obj' libtool=no @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o nghttpx_unittest-nghttp2_gzip_test.obj `if test -f 'nghttp2_gzip_test.c'; then $(CYGPATH_W) 'nghttp2_gzip_test.c'; else $(CYGPATH_W) '$(srcdir)/nghttp2_gzip_test.c'; fi` - -nghttpx_unittest-nghttp2_gzip.o: nghttp2_gzip.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT nghttpx_unittest-nghttp2_gzip.o -MD -MP -MF $(DEPDIR)/nghttpx_unittest-nghttp2_gzip.Tpo -c -o nghttpx_unittest-nghttp2_gzip.o `test -f 'nghttp2_gzip.c' || echo '$(srcdir)/'`nghttp2_gzip.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nghttpx_unittest-nghttp2_gzip.Tpo $(DEPDIR)/nghttpx_unittest-nghttp2_gzip.Po -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='nghttp2_gzip.c' object='nghttpx_unittest-nghttp2_gzip.o' libtool=no @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o nghttpx_unittest-nghttp2_gzip.o `test -f 'nghttp2_gzip.c' || echo '$(srcdir)/'`nghttp2_gzip.c - -nghttpx_unittest-nghttp2_gzip.obj: nghttp2_gzip.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT nghttpx_unittest-nghttp2_gzip.obj -MD -MP -MF $(DEPDIR)/nghttpx_unittest-nghttp2_gzip.Tpo -c -o nghttpx_unittest-nghttp2_gzip.obj `if test -f 'nghttp2_gzip.c'; then $(CYGPATH_W) 'nghttp2_gzip.c'; else $(CYGPATH_W) '$(srcdir)/nghttp2_gzip.c'; fi` -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nghttpx_unittest-nghttp2_gzip.Tpo $(DEPDIR)/nghttpx_unittest-nghttp2_gzip.Po -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='nghttp2_gzip.c' object='nghttpx_unittest-nghttp2_gzip.obj' libtool=no @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o nghttpx_unittest-nghttp2_gzip.obj `if test -f 'nghttp2_gzip.c'; then $(CYGPATH_W) 'nghttp2_gzip.c'; else $(CYGPATH_W) '$(srcdir)/nghttp2_gzip.c'; fi` - -.cc.o: -@am__fastdepCXX_TRUE@ $(AM_V_CXX)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\ -@am__fastdepCXX_TRUE@ $(CXXCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ -@am__fastdepCXX_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po -@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ $< - -.cc.obj: -@am__fastdepCXX_TRUE@ $(AM_V_CXX)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\ -@am__fastdepCXX_TRUE@ $(CXXCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\ -@am__fastdepCXX_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po -@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'` - -.cc.lo: -@am__fastdepCXX_TRUE@ $(AM_V_CXX)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\ -@am__fastdepCXX_TRUE@ $(LTCXXCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ -@am__fastdepCXX_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Plo -@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LTCXXCOMPILE) -c -o $@ $< - -libnghttp2_asio_la-util.lo: util.cc -@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttp2_asio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttp2_asio_la-util.lo -MD -MP -MF $(DEPDIR)/libnghttp2_asio_la-util.Tpo -c -o libnghttp2_asio_la-util.lo `test -f 'util.cc' || echo '$(srcdir)/'`util.cc -@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttp2_asio_la-util.Tpo $(DEPDIR)/libnghttp2_asio_la-util.Plo -@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='util.cc' object='libnghttp2_asio_la-util.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttp2_asio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttp2_asio_la-util.lo `test -f 'util.cc' || echo '$(srcdir)/'`util.cc - -libnghttp2_asio_la-http2.lo: http2.cc -@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttp2_asio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttp2_asio_la-http2.lo -MD -MP -MF $(DEPDIR)/libnghttp2_asio_la-http2.Tpo -c -o libnghttp2_asio_la-http2.lo `test -f 'http2.cc' || echo '$(srcdir)/'`http2.cc -@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttp2_asio_la-http2.Tpo $(DEPDIR)/libnghttp2_asio_la-http2.Plo -@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='http2.cc' object='libnghttp2_asio_la-http2.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttp2_asio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttp2_asio_la-http2.lo `test -f 'http2.cc' || echo '$(srcdir)/'`http2.cc - -libnghttp2_asio_la-ssl.lo: ssl.cc -@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttp2_asio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttp2_asio_la-ssl.lo -MD -MP -MF $(DEPDIR)/libnghttp2_asio_la-ssl.Tpo -c -o libnghttp2_asio_la-ssl.lo `test -f 'ssl.cc' || echo '$(srcdir)/'`ssl.cc -@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttp2_asio_la-ssl.Tpo $(DEPDIR)/libnghttp2_asio_la-ssl.Plo -@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='ssl.cc' object='libnghttp2_asio_la-ssl.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttp2_asio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttp2_asio_la-ssl.lo `test -f 'ssl.cc' || echo '$(srcdir)/'`ssl.cc - -libnghttp2_asio_la-asio_common.lo: asio_common.cc -@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttp2_asio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttp2_asio_la-asio_common.lo -MD -MP -MF $(DEPDIR)/libnghttp2_asio_la-asio_common.Tpo -c -o libnghttp2_asio_la-asio_common.lo `test -f 'asio_common.cc' || echo '$(srcdir)/'`asio_common.cc -@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttp2_asio_la-asio_common.Tpo $(DEPDIR)/libnghttp2_asio_la-asio_common.Plo -@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='asio_common.cc' object='libnghttp2_asio_la-asio_common.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttp2_asio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttp2_asio_la-asio_common.lo `test -f 'asio_common.cc' || echo '$(srcdir)/'`asio_common.cc - -libnghttp2_asio_la-asio_io_service_pool.lo: asio_io_service_pool.cc -@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttp2_asio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttp2_asio_la-asio_io_service_pool.lo -MD -MP -MF $(DEPDIR)/libnghttp2_asio_la-asio_io_service_pool.Tpo -c -o libnghttp2_asio_la-asio_io_service_pool.lo `test -f 'asio_io_service_pool.cc' || echo '$(srcdir)/'`asio_io_service_pool.cc -@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttp2_asio_la-asio_io_service_pool.Tpo $(DEPDIR)/libnghttp2_asio_la-asio_io_service_pool.Plo -@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='asio_io_service_pool.cc' object='libnghttp2_asio_la-asio_io_service_pool.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttp2_asio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttp2_asio_la-asio_io_service_pool.lo `test -f 'asio_io_service_pool.cc' || echo '$(srcdir)/'`asio_io_service_pool.cc - -libnghttp2_asio_la-asio_server_http2.lo: asio_server_http2.cc -@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttp2_asio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttp2_asio_la-asio_server_http2.lo -MD -MP -MF $(DEPDIR)/libnghttp2_asio_la-asio_server_http2.Tpo -c -o libnghttp2_asio_la-asio_server_http2.lo `test -f 'asio_server_http2.cc' || echo '$(srcdir)/'`asio_server_http2.cc -@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttp2_asio_la-asio_server_http2.Tpo $(DEPDIR)/libnghttp2_asio_la-asio_server_http2.Plo -@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='asio_server_http2.cc' object='libnghttp2_asio_la-asio_server_http2.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttp2_asio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttp2_asio_la-asio_server_http2.lo `test -f 'asio_server_http2.cc' || echo '$(srcdir)/'`asio_server_http2.cc - -libnghttp2_asio_la-asio_server_http2_impl.lo: asio_server_http2_impl.cc -@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttp2_asio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttp2_asio_la-asio_server_http2_impl.lo -MD -MP -MF $(DEPDIR)/libnghttp2_asio_la-asio_server_http2_impl.Tpo -c -o libnghttp2_asio_la-asio_server_http2_impl.lo `test -f 'asio_server_http2_impl.cc' || echo '$(srcdir)/'`asio_server_http2_impl.cc -@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttp2_asio_la-asio_server_http2_impl.Tpo $(DEPDIR)/libnghttp2_asio_la-asio_server_http2_impl.Plo -@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='asio_server_http2_impl.cc' object='libnghttp2_asio_la-asio_server_http2_impl.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttp2_asio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttp2_asio_la-asio_server_http2_impl.lo `test -f 'asio_server_http2_impl.cc' || echo '$(srcdir)/'`asio_server_http2_impl.cc - -libnghttp2_asio_la-asio_server.lo: asio_server.cc -@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttp2_asio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttp2_asio_la-asio_server.lo -MD -MP -MF $(DEPDIR)/libnghttp2_asio_la-asio_server.Tpo -c -o libnghttp2_asio_la-asio_server.lo `test -f 'asio_server.cc' || echo '$(srcdir)/'`asio_server.cc -@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttp2_asio_la-asio_server.Tpo $(DEPDIR)/libnghttp2_asio_la-asio_server.Plo -@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='asio_server.cc' object='libnghttp2_asio_la-asio_server.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttp2_asio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttp2_asio_la-asio_server.lo `test -f 'asio_server.cc' || echo '$(srcdir)/'`asio_server.cc - -libnghttp2_asio_la-asio_server_http2_handler.lo: asio_server_http2_handler.cc -@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttp2_asio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttp2_asio_la-asio_server_http2_handler.lo -MD -MP -MF $(DEPDIR)/libnghttp2_asio_la-asio_server_http2_handler.Tpo -c -o libnghttp2_asio_la-asio_server_http2_handler.lo `test -f 'asio_server_http2_handler.cc' || echo '$(srcdir)/'`asio_server_http2_handler.cc -@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttp2_asio_la-asio_server_http2_handler.Tpo $(DEPDIR)/libnghttp2_asio_la-asio_server_http2_handler.Plo -@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='asio_server_http2_handler.cc' object='libnghttp2_asio_la-asio_server_http2_handler.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttp2_asio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttp2_asio_la-asio_server_http2_handler.lo `test -f 'asio_server_http2_handler.cc' || echo '$(srcdir)/'`asio_server_http2_handler.cc - -libnghttp2_asio_la-asio_server_request.lo: asio_server_request.cc -@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttp2_asio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttp2_asio_la-asio_server_request.lo -MD -MP -MF $(DEPDIR)/libnghttp2_asio_la-asio_server_request.Tpo -c -o libnghttp2_asio_la-asio_server_request.lo `test -f 'asio_server_request.cc' || echo '$(srcdir)/'`asio_server_request.cc -@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttp2_asio_la-asio_server_request.Tpo $(DEPDIR)/libnghttp2_asio_la-asio_server_request.Plo -@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='asio_server_request.cc' object='libnghttp2_asio_la-asio_server_request.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttp2_asio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttp2_asio_la-asio_server_request.lo `test -f 'asio_server_request.cc' || echo '$(srcdir)/'`asio_server_request.cc - -libnghttp2_asio_la-asio_server_request_impl.lo: asio_server_request_impl.cc -@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttp2_asio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttp2_asio_la-asio_server_request_impl.lo -MD -MP -MF $(DEPDIR)/libnghttp2_asio_la-asio_server_request_impl.Tpo -c -o libnghttp2_asio_la-asio_server_request_impl.lo `test -f 'asio_server_request_impl.cc' || echo '$(srcdir)/'`asio_server_request_impl.cc -@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttp2_asio_la-asio_server_request_impl.Tpo $(DEPDIR)/libnghttp2_asio_la-asio_server_request_impl.Plo -@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='asio_server_request_impl.cc' object='libnghttp2_asio_la-asio_server_request_impl.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttp2_asio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttp2_asio_la-asio_server_request_impl.lo `test -f 'asio_server_request_impl.cc' || echo '$(srcdir)/'`asio_server_request_impl.cc - -libnghttp2_asio_la-asio_server_response.lo: asio_server_response.cc -@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttp2_asio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttp2_asio_la-asio_server_response.lo -MD -MP -MF $(DEPDIR)/libnghttp2_asio_la-asio_server_response.Tpo -c -o libnghttp2_asio_la-asio_server_response.lo `test -f 'asio_server_response.cc' || echo '$(srcdir)/'`asio_server_response.cc -@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttp2_asio_la-asio_server_response.Tpo $(DEPDIR)/libnghttp2_asio_la-asio_server_response.Plo -@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='asio_server_response.cc' object='libnghttp2_asio_la-asio_server_response.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttp2_asio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttp2_asio_la-asio_server_response.lo `test -f 'asio_server_response.cc' || echo '$(srcdir)/'`asio_server_response.cc - -libnghttp2_asio_la-asio_server_response_impl.lo: asio_server_response_impl.cc -@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttp2_asio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttp2_asio_la-asio_server_response_impl.lo -MD -MP -MF $(DEPDIR)/libnghttp2_asio_la-asio_server_response_impl.Tpo -c -o libnghttp2_asio_la-asio_server_response_impl.lo `test -f 'asio_server_response_impl.cc' || echo '$(srcdir)/'`asio_server_response_impl.cc -@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttp2_asio_la-asio_server_response_impl.Tpo $(DEPDIR)/libnghttp2_asio_la-asio_server_response_impl.Plo -@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='asio_server_response_impl.cc' object='libnghttp2_asio_la-asio_server_response_impl.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttp2_asio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttp2_asio_la-asio_server_response_impl.lo `test -f 'asio_server_response_impl.cc' || echo '$(srcdir)/'`asio_server_response_impl.cc - -libnghttp2_asio_la-asio_server_stream.lo: asio_server_stream.cc -@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttp2_asio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttp2_asio_la-asio_server_stream.lo -MD -MP -MF $(DEPDIR)/libnghttp2_asio_la-asio_server_stream.Tpo -c -o libnghttp2_asio_la-asio_server_stream.lo `test -f 'asio_server_stream.cc' || echo '$(srcdir)/'`asio_server_stream.cc -@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttp2_asio_la-asio_server_stream.Tpo $(DEPDIR)/libnghttp2_asio_la-asio_server_stream.Plo -@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='asio_server_stream.cc' object='libnghttp2_asio_la-asio_server_stream.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttp2_asio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttp2_asio_la-asio_server_stream.lo `test -f 'asio_server_stream.cc' || echo '$(srcdir)/'`asio_server_stream.cc - -libnghttp2_asio_la-asio_server_serve_mux.lo: asio_server_serve_mux.cc -@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttp2_asio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttp2_asio_la-asio_server_serve_mux.lo -MD -MP -MF $(DEPDIR)/libnghttp2_asio_la-asio_server_serve_mux.Tpo -c -o libnghttp2_asio_la-asio_server_serve_mux.lo `test -f 'asio_server_serve_mux.cc' || echo '$(srcdir)/'`asio_server_serve_mux.cc -@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttp2_asio_la-asio_server_serve_mux.Tpo $(DEPDIR)/libnghttp2_asio_la-asio_server_serve_mux.Plo -@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='asio_server_serve_mux.cc' object='libnghttp2_asio_la-asio_server_serve_mux.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttp2_asio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttp2_asio_la-asio_server_serve_mux.lo `test -f 'asio_server_serve_mux.cc' || echo '$(srcdir)/'`asio_server_serve_mux.cc - -libnghttp2_asio_la-asio_server_request_handler.lo: asio_server_request_handler.cc -@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttp2_asio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttp2_asio_la-asio_server_request_handler.lo -MD -MP -MF $(DEPDIR)/libnghttp2_asio_la-asio_server_request_handler.Tpo -c -o libnghttp2_asio_la-asio_server_request_handler.lo `test -f 'asio_server_request_handler.cc' || echo '$(srcdir)/'`asio_server_request_handler.cc -@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttp2_asio_la-asio_server_request_handler.Tpo $(DEPDIR)/libnghttp2_asio_la-asio_server_request_handler.Plo -@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='asio_server_request_handler.cc' object='libnghttp2_asio_la-asio_server_request_handler.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttp2_asio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttp2_asio_la-asio_server_request_handler.lo `test -f 'asio_server_request_handler.cc' || echo '$(srcdir)/'`asio_server_request_handler.cc - -libnghttp2_asio_la-asio_server_tls_context.lo: asio_server_tls_context.cc -@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttp2_asio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttp2_asio_la-asio_server_tls_context.lo -MD -MP -MF $(DEPDIR)/libnghttp2_asio_la-asio_server_tls_context.Tpo -c -o libnghttp2_asio_la-asio_server_tls_context.lo `test -f 'asio_server_tls_context.cc' || echo '$(srcdir)/'`asio_server_tls_context.cc -@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttp2_asio_la-asio_server_tls_context.Tpo $(DEPDIR)/libnghttp2_asio_la-asio_server_tls_context.Plo -@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='asio_server_tls_context.cc' object='libnghttp2_asio_la-asio_server_tls_context.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttp2_asio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttp2_asio_la-asio_server_tls_context.lo `test -f 'asio_server_tls_context.cc' || echo '$(srcdir)/'`asio_server_tls_context.cc - -libnghttp2_asio_la-asio_client_session.lo: asio_client_session.cc -@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttp2_asio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttp2_asio_la-asio_client_session.lo -MD -MP -MF $(DEPDIR)/libnghttp2_asio_la-asio_client_session.Tpo -c -o libnghttp2_asio_la-asio_client_session.lo `test -f 'asio_client_session.cc' || echo '$(srcdir)/'`asio_client_session.cc -@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttp2_asio_la-asio_client_session.Tpo $(DEPDIR)/libnghttp2_asio_la-asio_client_session.Plo -@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='asio_client_session.cc' object='libnghttp2_asio_la-asio_client_session.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttp2_asio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttp2_asio_la-asio_client_session.lo `test -f 'asio_client_session.cc' || echo '$(srcdir)/'`asio_client_session.cc - -libnghttp2_asio_la-asio_client_session_impl.lo: asio_client_session_impl.cc -@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttp2_asio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttp2_asio_la-asio_client_session_impl.lo -MD -MP -MF $(DEPDIR)/libnghttp2_asio_la-asio_client_session_impl.Tpo -c -o libnghttp2_asio_la-asio_client_session_impl.lo `test -f 'asio_client_session_impl.cc' || echo '$(srcdir)/'`asio_client_session_impl.cc -@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttp2_asio_la-asio_client_session_impl.Tpo $(DEPDIR)/libnghttp2_asio_la-asio_client_session_impl.Plo -@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='asio_client_session_impl.cc' object='libnghttp2_asio_la-asio_client_session_impl.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttp2_asio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttp2_asio_la-asio_client_session_impl.lo `test -f 'asio_client_session_impl.cc' || echo '$(srcdir)/'`asio_client_session_impl.cc - -libnghttp2_asio_la-asio_client_session_tcp_impl.lo: asio_client_session_tcp_impl.cc -@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttp2_asio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttp2_asio_la-asio_client_session_tcp_impl.lo -MD -MP -MF $(DEPDIR)/libnghttp2_asio_la-asio_client_session_tcp_impl.Tpo -c -o libnghttp2_asio_la-asio_client_session_tcp_impl.lo `test -f 'asio_client_session_tcp_impl.cc' || echo '$(srcdir)/'`asio_client_session_tcp_impl.cc -@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttp2_asio_la-asio_client_session_tcp_impl.Tpo $(DEPDIR)/libnghttp2_asio_la-asio_client_session_tcp_impl.Plo -@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='asio_client_session_tcp_impl.cc' object='libnghttp2_asio_la-asio_client_session_tcp_impl.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttp2_asio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttp2_asio_la-asio_client_session_tcp_impl.lo `test -f 'asio_client_session_tcp_impl.cc' || echo '$(srcdir)/'`asio_client_session_tcp_impl.cc - -libnghttp2_asio_la-asio_client_session_tls_impl.lo: asio_client_session_tls_impl.cc -@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttp2_asio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttp2_asio_la-asio_client_session_tls_impl.lo -MD -MP -MF $(DEPDIR)/libnghttp2_asio_la-asio_client_session_tls_impl.Tpo -c -o libnghttp2_asio_la-asio_client_session_tls_impl.lo `test -f 'asio_client_session_tls_impl.cc' || echo '$(srcdir)/'`asio_client_session_tls_impl.cc -@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttp2_asio_la-asio_client_session_tls_impl.Tpo $(DEPDIR)/libnghttp2_asio_la-asio_client_session_tls_impl.Plo -@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='asio_client_session_tls_impl.cc' object='libnghttp2_asio_la-asio_client_session_tls_impl.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttp2_asio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttp2_asio_la-asio_client_session_tls_impl.lo `test -f 'asio_client_session_tls_impl.cc' || echo '$(srcdir)/'`asio_client_session_tls_impl.cc - -libnghttp2_asio_la-asio_client_response.lo: asio_client_response.cc -@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttp2_asio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttp2_asio_la-asio_client_response.lo -MD -MP -MF $(DEPDIR)/libnghttp2_asio_la-asio_client_response.Tpo -c -o libnghttp2_asio_la-asio_client_response.lo `test -f 'asio_client_response.cc' || echo '$(srcdir)/'`asio_client_response.cc -@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttp2_asio_la-asio_client_response.Tpo $(DEPDIR)/libnghttp2_asio_la-asio_client_response.Plo -@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='asio_client_response.cc' object='libnghttp2_asio_la-asio_client_response.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttp2_asio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttp2_asio_la-asio_client_response.lo `test -f 'asio_client_response.cc' || echo '$(srcdir)/'`asio_client_response.cc - -libnghttp2_asio_la-asio_client_response_impl.lo: asio_client_response_impl.cc -@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttp2_asio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttp2_asio_la-asio_client_response_impl.lo -MD -MP -MF $(DEPDIR)/libnghttp2_asio_la-asio_client_response_impl.Tpo -c -o libnghttp2_asio_la-asio_client_response_impl.lo `test -f 'asio_client_response_impl.cc' || echo '$(srcdir)/'`asio_client_response_impl.cc -@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttp2_asio_la-asio_client_response_impl.Tpo $(DEPDIR)/libnghttp2_asio_la-asio_client_response_impl.Plo -@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='asio_client_response_impl.cc' object='libnghttp2_asio_la-asio_client_response_impl.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttp2_asio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttp2_asio_la-asio_client_response_impl.lo `test -f 'asio_client_response_impl.cc' || echo '$(srcdir)/'`asio_client_response_impl.cc - -libnghttp2_asio_la-asio_client_request.lo: asio_client_request.cc -@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttp2_asio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttp2_asio_la-asio_client_request.lo -MD -MP -MF $(DEPDIR)/libnghttp2_asio_la-asio_client_request.Tpo -c -o libnghttp2_asio_la-asio_client_request.lo `test -f 'asio_client_request.cc' || echo '$(srcdir)/'`asio_client_request.cc -@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttp2_asio_la-asio_client_request.Tpo $(DEPDIR)/libnghttp2_asio_la-asio_client_request.Plo -@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='asio_client_request.cc' object='libnghttp2_asio_la-asio_client_request.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttp2_asio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttp2_asio_la-asio_client_request.lo `test -f 'asio_client_request.cc' || echo '$(srcdir)/'`asio_client_request.cc - -libnghttp2_asio_la-asio_client_request_impl.lo: asio_client_request_impl.cc -@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttp2_asio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttp2_asio_la-asio_client_request_impl.lo -MD -MP -MF $(DEPDIR)/libnghttp2_asio_la-asio_client_request_impl.Tpo -c -o libnghttp2_asio_la-asio_client_request_impl.lo `test -f 'asio_client_request_impl.cc' || echo '$(srcdir)/'`asio_client_request_impl.cc -@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttp2_asio_la-asio_client_request_impl.Tpo $(DEPDIR)/libnghttp2_asio_la-asio_client_request_impl.Plo -@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='asio_client_request_impl.cc' object='libnghttp2_asio_la-asio_client_request_impl.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttp2_asio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttp2_asio_la-asio_client_request_impl.lo `test -f 'asio_client_request_impl.cc' || echo '$(srcdir)/'`asio_client_request_impl.cc - -libnghttp2_asio_la-asio_client_stream.lo: asio_client_stream.cc -@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttp2_asio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttp2_asio_la-asio_client_stream.lo -MD -MP -MF $(DEPDIR)/libnghttp2_asio_la-asio_client_stream.Tpo -c -o libnghttp2_asio_la-asio_client_stream.lo `test -f 'asio_client_stream.cc' || echo '$(srcdir)/'`asio_client_stream.cc -@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttp2_asio_la-asio_client_stream.Tpo $(DEPDIR)/libnghttp2_asio_la-asio_client_stream.Plo -@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='asio_client_stream.cc' object='libnghttp2_asio_la-asio_client_stream.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttp2_asio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttp2_asio_la-asio_client_stream.lo `test -f 'asio_client_stream.cc' || echo '$(srcdir)/'`asio_client_stream.cc - -libnghttp2_asio_la-asio_client_tls_context.lo: asio_client_tls_context.cc -@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttp2_asio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttp2_asio_la-asio_client_tls_context.lo -MD -MP -MF $(DEPDIR)/libnghttp2_asio_la-asio_client_tls_context.Tpo -c -o libnghttp2_asio_la-asio_client_tls_context.lo `test -f 'asio_client_tls_context.cc' || echo '$(srcdir)/'`asio_client_tls_context.cc -@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttp2_asio_la-asio_client_tls_context.Tpo $(DEPDIR)/libnghttp2_asio_la-asio_client_tls_context.Plo -@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='asio_client_tls_context.cc' object='libnghttp2_asio_la-asio_client_tls_context.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttp2_asio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttp2_asio_la-asio_client_tls_context.lo `test -f 'asio_client_tls_context.cc' || echo '$(srcdir)/'`asio_client_tls_context.cc - -nghttpx_unittest-shrpx-unittest.o: shrpx-unittest.cc -@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT nghttpx_unittest-shrpx-unittest.o -MD -MP -MF $(DEPDIR)/nghttpx_unittest-shrpx-unittest.Tpo -c -o nghttpx_unittest-shrpx-unittest.o `test -f 'shrpx-unittest.cc' || echo '$(srcdir)/'`shrpx-unittest.cc -@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nghttpx_unittest-shrpx-unittest.Tpo $(DEPDIR)/nghttpx_unittest-shrpx-unittest.Po -@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='shrpx-unittest.cc' object='nghttpx_unittest-shrpx-unittest.o' libtool=no @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o nghttpx_unittest-shrpx-unittest.o `test -f 'shrpx-unittest.cc' || echo '$(srcdir)/'`shrpx-unittest.cc - -nghttpx_unittest-shrpx-unittest.obj: shrpx-unittest.cc -@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT nghttpx_unittest-shrpx-unittest.obj -MD -MP -MF $(DEPDIR)/nghttpx_unittest-shrpx-unittest.Tpo -c -o nghttpx_unittest-shrpx-unittest.obj `if test -f 'shrpx-unittest.cc'; then $(CYGPATH_W) 'shrpx-unittest.cc'; else $(CYGPATH_W) '$(srcdir)/shrpx-unittest.cc'; fi` -@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nghttpx_unittest-shrpx-unittest.Tpo $(DEPDIR)/nghttpx_unittest-shrpx-unittest.Po -@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='shrpx-unittest.cc' object='nghttpx_unittest-shrpx-unittest.obj' libtool=no @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o nghttpx_unittest-shrpx-unittest.obj `if test -f 'shrpx-unittest.cc'; then $(CYGPATH_W) 'shrpx-unittest.cc'; else $(CYGPATH_W) '$(srcdir)/shrpx-unittest.cc'; fi` - -nghttpx_unittest-shrpx_ssl_test.o: shrpx_ssl_test.cc -@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT nghttpx_unittest-shrpx_ssl_test.o -MD -MP -MF $(DEPDIR)/nghttpx_unittest-shrpx_ssl_test.Tpo -c -o nghttpx_unittest-shrpx_ssl_test.o `test -f 'shrpx_ssl_test.cc' || echo '$(srcdir)/'`shrpx_ssl_test.cc -@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nghttpx_unittest-shrpx_ssl_test.Tpo $(DEPDIR)/nghttpx_unittest-shrpx_ssl_test.Po -@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='shrpx_ssl_test.cc' object='nghttpx_unittest-shrpx_ssl_test.o' libtool=no @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o nghttpx_unittest-shrpx_ssl_test.o `test -f 'shrpx_ssl_test.cc' || echo '$(srcdir)/'`shrpx_ssl_test.cc - -nghttpx_unittest-shrpx_ssl_test.obj: shrpx_ssl_test.cc -@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT nghttpx_unittest-shrpx_ssl_test.obj -MD -MP -MF $(DEPDIR)/nghttpx_unittest-shrpx_ssl_test.Tpo -c -o nghttpx_unittest-shrpx_ssl_test.obj `if test -f 'shrpx_ssl_test.cc'; then $(CYGPATH_W) 'shrpx_ssl_test.cc'; else $(CYGPATH_W) '$(srcdir)/shrpx_ssl_test.cc'; fi` -@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nghttpx_unittest-shrpx_ssl_test.Tpo $(DEPDIR)/nghttpx_unittest-shrpx_ssl_test.Po -@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='shrpx_ssl_test.cc' object='nghttpx_unittest-shrpx_ssl_test.obj' libtool=no @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o nghttpx_unittest-shrpx_ssl_test.obj `if test -f 'shrpx_ssl_test.cc'; then $(CYGPATH_W) 'shrpx_ssl_test.cc'; else $(CYGPATH_W) '$(srcdir)/shrpx_ssl_test.cc'; fi` - -nghttpx_unittest-shrpx_downstream_test.o: shrpx_downstream_test.cc -@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT nghttpx_unittest-shrpx_downstream_test.o -MD -MP -MF $(DEPDIR)/nghttpx_unittest-shrpx_downstream_test.Tpo -c -o nghttpx_unittest-shrpx_downstream_test.o `test -f 'shrpx_downstream_test.cc' || echo '$(srcdir)/'`shrpx_downstream_test.cc -@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nghttpx_unittest-shrpx_downstream_test.Tpo $(DEPDIR)/nghttpx_unittest-shrpx_downstream_test.Po -@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='shrpx_downstream_test.cc' object='nghttpx_unittest-shrpx_downstream_test.o' libtool=no @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o nghttpx_unittest-shrpx_downstream_test.o `test -f 'shrpx_downstream_test.cc' || echo '$(srcdir)/'`shrpx_downstream_test.cc - -nghttpx_unittest-shrpx_downstream_test.obj: shrpx_downstream_test.cc -@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT nghttpx_unittest-shrpx_downstream_test.obj -MD -MP -MF $(DEPDIR)/nghttpx_unittest-shrpx_downstream_test.Tpo -c -o nghttpx_unittest-shrpx_downstream_test.obj `if test -f 'shrpx_downstream_test.cc'; then $(CYGPATH_W) 'shrpx_downstream_test.cc'; else $(CYGPATH_W) '$(srcdir)/shrpx_downstream_test.cc'; fi` -@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nghttpx_unittest-shrpx_downstream_test.Tpo $(DEPDIR)/nghttpx_unittest-shrpx_downstream_test.Po -@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='shrpx_downstream_test.cc' object='nghttpx_unittest-shrpx_downstream_test.obj' libtool=no @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o nghttpx_unittest-shrpx_downstream_test.obj `if test -f 'shrpx_downstream_test.cc'; then $(CYGPATH_W) 'shrpx_downstream_test.cc'; else $(CYGPATH_W) '$(srcdir)/shrpx_downstream_test.cc'; fi` - -nghttpx_unittest-shrpx_config_test.o: shrpx_config_test.cc -@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT nghttpx_unittest-shrpx_config_test.o -MD -MP -MF $(DEPDIR)/nghttpx_unittest-shrpx_config_test.Tpo -c -o nghttpx_unittest-shrpx_config_test.o `test -f 'shrpx_config_test.cc' || echo '$(srcdir)/'`shrpx_config_test.cc -@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nghttpx_unittest-shrpx_config_test.Tpo $(DEPDIR)/nghttpx_unittest-shrpx_config_test.Po -@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='shrpx_config_test.cc' object='nghttpx_unittest-shrpx_config_test.o' libtool=no @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o nghttpx_unittest-shrpx_config_test.o `test -f 'shrpx_config_test.cc' || echo '$(srcdir)/'`shrpx_config_test.cc - -nghttpx_unittest-shrpx_config_test.obj: shrpx_config_test.cc -@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT nghttpx_unittest-shrpx_config_test.obj -MD -MP -MF $(DEPDIR)/nghttpx_unittest-shrpx_config_test.Tpo -c -o nghttpx_unittest-shrpx_config_test.obj `if test -f 'shrpx_config_test.cc'; then $(CYGPATH_W) 'shrpx_config_test.cc'; else $(CYGPATH_W) '$(srcdir)/shrpx_config_test.cc'; fi` -@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nghttpx_unittest-shrpx_config_test.Tpo $(DEPDIR)/nghttpx_unittest-shrpx_config_test.Po -@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='shrpx_config_test.cc' object='nghttpx_unittest-shrpx_config_test.obj' libtool=no @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o nghttpx_unittest-shrpx_config_test.obj `if test -f 'shrpx_config_test.cc'; then $(CYGPATH_W) 'shrpx_config_test.cc'; else $(CYGPATH_W) '$(srcdir)/shrpx_config_test.cc'; fi` - -nghttpx_unittest-http2_test.o: http2_test.cc -@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT nghttpx_unittest-http2_test.o -MD -MP -MF $(DEPDIR)/nghttpx_unittest-http2_test.Tpo -c -o nghttpx_unittest-http2_test.o `test -f 'http2_test.cc' || echo '$(srcdir)/'`http2_test.cc -@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nghttpx_unittest-http2_test.Tpo $(DEPDIR)/nghttpx_unittest-http2_test.Po -@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='http2_test.cc' object='nghttpx_unittest-http2_test.o' libtool=no @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o nghttpx_unittest-http2_test.o `test -f 'http2_test.cc' || echo '$(srcdir)/'`http2_test.cc - -nghttpx_unittest-http2_test.obj: http2_test.cc -@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT nghttpx_unittest-http2_test.obj -MD -MP -MF $(DEPDIR)/nghttpx_unittest-http2_test.Tpo -c -o nghttpx_unittest-http2_test.obj `if test -f 'http2_test.cc'; then $(CYGPATH_W) 'http2_test.cc'; else $(CYGPATH_W) '$(srcdir)/http2_test.cc'; fi` -@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nghttpx_unittest-http2_test.Tpo $(DEPDIR)/nghttpx_unittest-http2_test.Po -@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='http2_test.cc' object='nghttpx_unittest-http2_test.obj' libtool=no @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o nghttpx_unittest-http2_test.obj `if test -f 'http2_test.cc'; then $(CYGPATH_W) 'http2_test.cc'; else $(CYGPATH_W) '$(srcdir)/http2_test.cc'; fi` - -nghttpx_unittest-util_test.o: util_test.cc -@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT nghttpx_unittest-util_test.o -MD -MP -MF $(DEPDIR)/nghttpx_unittest-util_test.Tpo -c -o nghttpx_unittest-util_test.o `test -f 'util_test.cc' || echo '$(srcdir)/'`util_test.cc -@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nghttpx_unittest-util_test.Tpo $(DEPDIR)/nghttpx_unittest-util_test.Po -@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='util_test.cc' object='nghttpx_unittest-util_test.o' libtool=no @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o nghttpx_unittest-util_test.o `test -f 'util_test.cc' || echo '$(srcdir)/'`util_test.cc - -nghttpx_unittest-util_test.obj: util_test.cc -@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT nghttpx_unittest-util_test.obj -MD -MP -MF $(DEPDIR)/nghttpx_unittest-util_test.Tpo -c -o nghttpx_unittest-util_test.obj `if test -f 'util_test.cc'; then $(CYGPATH_W) 'util_test.cc'; else $(CYGPATH_W) '$(srcdir)/util_test.cc'; fi` -@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nghttpx_unittest-util_test.Tpo $(DEPDIR)/nghttpx_unittest-util_test.Po -@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='util_test.cc' object='nghttpx_unittest-util_test.obj' libtool=no @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o nghttpx_unittest-util_test.obj `if test -f 'util_test.cc'; then $(CYGPATH_W) 'util_test.cc'; else $(CYGPATH_W) '$(srcdir)/util_test.cc'; fi` - -nghttpx_unittest-buffer_test.o: buffer_test.cc -@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT nghttpx_unittest-buffer_test.o -MD -MP -MF $(DEPDIR)/nghttpx_unittest-buffer_test.Tpo -c -o nghttpx_unittest-buffer_test.o `test -f 'buffer_test.cc' || echo '$(srcdir)/'`buffer_test.cc -@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nghttpx_unittest-buffer_test.Tpo $(DEPDIR)/nghttpx_unittest-buffer_test.Po -@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='buffer_test.cc' object='nghttpx_unittest-buffer_test.o' libtool=no @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o nghttpx_unittest-buffer_test.o `test -f 'buffer_test.cc' || echo '$(srcdir)/'`buffer_test.cc - -nghttpx_unittest-buffer_test.obj: buffer_test.cc -@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT nghttpx_unittest-buffer_test.obj -MD -MP -MF $(DEPDIR)/nghttpx_unittest-buffer_test.Tpo -c -o nghttpx_unittest-buffer_test.obj `if test -f 'buffer_test.cc'; then $(CYGPATH_W) 'buffer_test.cc'; else $(CYGPATH_W) '$(srcdir)/buffer_test.cc'; fi` -@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nghttpx_unittest-buffer_test.Tpo $(DEPDIR)/nghttpx_unittest-buffer_test.Po -@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='buffer_test.cc' object='nghttpx_unittest-buffer_test.obj' libtool=no @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o nghttpx_unittest-buffer_test.obj `if test -f 'buffer_test.cc'; then $(CYGPATH_W) 'buffer_test.cc'; else $(CYGPATH_W) '$(srcdir)/buffer_test.cc'; fi` - -nghttpx_unittest-memchunk_test.o: memchunk_test.cc -@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT nghttpx_unittest-memchunk_test.o -MD -MP -MF $(DEPDIR)/nghttpx_unittest-memchunk_test.Tpo -c -o nghttpx_unittest-memchunk_test.o `test -f 'memchunk_test.cc' || echo '$(srcdir)/'`memchunk_test.cc -@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nghttpx_unittest-memchunk_test.Tpo $(DEPDIR)/nghttpx_unittest-memchunk_test.Po -@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='memchunk_test.cc' object='nghttpx_unittest-memchunk_test.o' libtool=no @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o nghttpx_unittest-memchunk_test.o `test -f 'memchunk_test.cc' || echo '$(srcdir)/'`memchunk_test.cc - -nghttpx_unittest-memchunk_test.obj: memchunk_test.cc -@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT nghttpx_unittest-memchunk_test.obj -MD -MP -MF $(DEPDIR)/nghttpx_unittest-memchunk_test.Tpo -c -o nghttpx_unittest-memchunk_test.obj `if test -f 'memchunk_test.cc'; then $(CYGPATH_W) 'memchunk_test.cc'; else $(CYGPATH_W) '$(srcdir)/memchunk_test.cc'; fi` -@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nghttpx_unittest-memchunk_test.Tpo $(DEPDIR)/nghttpx_unittest-memchunk_test.Po -@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='memchunk_test.cc' object='nghttpx_unittest-memchunk_test.obj' libtool=no @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o nghttpx_unittest-memchunk_test.obj `if test -f 'memchunk_test.cc'; then $(CYGPATH_W) 'memchunk_test.cc'; else $(CYGPATH_W) '$(srcdir)/memchunk_test.cc'; fi` - -mostlyclean-libtool: - -rm -f *.lo - -clean-libtool: - -rm -rf .libs _libs -install-pkgconfigDATA: $(pkgconfig_DATA) - @$(NORMAL_INSTALL) - @list='$(pkgconfig_DATA)'; test -n "$(pkgconfigdir)" || list=; \ - if test -n "$$list"; then \ - echo " $(MKDIR_P) '$(DESTDIR)$(pkgconfigdir)'"; \ - $(MKDIR_P) "$(DESTDIR)$(pkgconfigdir)" || exit 1; \ - fi; \ - for p in $$list; do \ - if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ - echo "$$d$$p"; \ - done | $(am__base_list) | \ - while read files; do \ - echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(pkgconfigdir)'"; \ - $(INSTALL_DATA) $$files "$(DESTDIR)$(pkgconfigdir)" || exit $$?; \ - done - -uninstall-pkgconfigDATA: - @$(NORMAL_UNINSTALL) - @list='$(pkgconfig_DATA)'; test -n "$(pkgconfigdir)" || list=; \ - files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ - dir='$(DESTDIR)$(pkgconfigdir)'; $(am__uninstall_files_from_dir) - -# This directory's subdirectories are mostly independent; you can cd -# into them and run 'make' without going through this Makefile. -# To change the values of 'make' variables: instead of editing Makefiles, -# (1) if the variable is set in 'config.status', edit 'config.status' -# (which will cause the Makefiles to be regenerated when you run 'make'); -# (2) otherwise, pass the desired values on the 'make' command line. -$(am__recursive_targets): - @fail=; \ - if $(am__make_keepgoing); then \ - failcom='fail=yes'; \ - else \ - failcom='exit 1'; \ - fi; \ - dot_seen=no; \ - target=`echo $@ | sed s/-recursive//`; \ - case "$@" in \ - distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ - *) list='$(SUBDIRS)' ;; \ - esac; \ - for subdir in $$list; do \ - echo "Making $$target in $$subdir"; \ - if test "$$subdir" = "."; then \ - dot_seen=yes; \ - local_target="$$target-am"; \ - else \ - local_target="$$target"; \ - fi; \ - ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ - || eval $$failcom; \ - done; \ - if test "$$dot_seen" = "no"; then \ - $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ - fi; test -z "$$fail" - -ID: $(am__tagged_files) - $(am__define_uniq_tagged_files); mkid -fID $$unique -tags: tags-recursive -TAGS: tags - -tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) - set x; \ - here=`pwd`; \ - if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \ - include_option=--etags-include; \ - empty_fix=.; \ - else \ - include_option=--include; \ - empty_fix=; \ - fi; \ - list='$(SUBDIRS)'; for subdir in $$list; do \ - if test "$$subdir" = .; then :; else \ - test ! -f $$subdir/TAGS || \ - set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \ - fi; \ - done; \ - $(am__define_uniq_tagged_files); \ - shift; \ - if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ - test -n "$$unique" || unique=$$empty_fix; \ - if test $$# -gt 0; then \ - $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ - "$$@" $$unique; \ - else \ - $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ - $$unique; \ - fi; \ - fi -ctags: ctags-recursive - -CTAGS: ctags -ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) - $(am__define_uniq_tagged_files); \ - test -z "$(CTAGS_ARGS)$$unique" \ - || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ - $$unique - -GTAGS: - here=`$(am__cd) $(top_builddir) && pwd` \ - && $(am__cd) $(top_srcdir) \ - && gtags -i $(GTAGS_ARGS) "$$here" -cscopelist: cscopelist-recursive - -cscopelist-am: $(am__tagged_files) - list='$(am__tagged_files)'; \ - case "$(srcdir)" in \ - [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ - *) sdir=$(subdir)/$(srcdir) ;; \ - esac; \ - for i in $$list; do \ - if test -f "$$i"; then \ - echo "$(subdir)/$$i"; \ - else \ - echo "$$sdir/$$i"; \ - fi; \ - done >> $(top_builddir)/cscope.files - -distclean-tags: - -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags - -# Recover from deleted '.trs' file; this should ensure that -# "rm -f foo.log; make foo.trs" re-run 'foo.test', and re-create -# both 'foo.log' and 'foo.trs'. Break the recipe in two subshells -# to avoid problems with "make -n". -.log.trs: - rm -f $< $@ - $(MAKE) $(AM_MAKEFLAGS) $< - -# Leading 'am--fnord' is there to ensure the list of targets does not -# expand to empty, as could happen e.g. with make check TESTS=''. -am--fnord $(TEST_LOGS) $(TEST_LOGS:.log=.trs): $(am__force_recheck) -am--force-recheck: - @: - -$(TEST_SUITE_LOG): $(TEST_LOGS) - @$(am__set_TESTS_bases); \ - am__f_ok () { test -f "$$1" && test -r "$$1"; }; \ - redo_bases=`for i in $$bases; do \ - am__f_ok $$i.trs && am__f_ok $$i.log || echo $$i; \ - done`; \ - if test -n "$$redo_bases"; then \ - redo_logs=`for i in $$redo_bases; do echo $$i.log; done`; \ - redo_results=`for i in $$redo_bases; do echo $$i.trs; done`; \ - if $(am__make_dryrun); then :; else \ - rm -f $$redo_logs && rm -f $$redo_results || exit 1; \ - fi; \ - fi; \ - if test -n "$$am__remaking_logs"; then \ - echo "fatal: making $(TEST_SUITE_LOG): possible infinite" \ - "recursion detected" >&2; \ - else \ - am__remaking_logs=yes $(MAKE) $(AM_MAKEFLAGS) $$redo_logs; \ - fi; \ - if $(am__make_dryrun); then :; else \ - st=0; \ - errmsg="fatal: making $(TEST_SUITE_LOG): failed to create"; \ - for i in $$redo_bases; do \ - test -f $$i.trs && test -r $$i.trs \ - || { echo "$$errmsg $$i.trs" >&2; st=1; }; \ - test -f $$i.log && test -r $$i.log \ - || { echo "$$errmsg $$i.log" >&2; st=1; }; \ - done; \ - test $$st -eq 0 || exit 1; \ - fi - @$(am__sh_e_setup); $(am__tty_colors); $(am__set_TESTS_bases); \ - ws='[ ]'; \ - results=`for b in $$bases; do echo $$b.trs; done`; \ - test -n "$$results" || results=/dev/null; \ - all=` grep "^$$ws*:test-result:" $$results | wc -l`; \ - pass=` grep "^$$ws*:test-result:$$ws*PASS" $$results | wc -l`; \ - fail=` grep "^$$ws*:test-result:$$ws*FAIL" $$results | wc -l`; \ - skip=` grep "^$$ws*:test-result:$$ws*SKIP" $$results | wc -l`; \ - xfail=`grep "^$$ws*:test-result:$$ws*XFAIL" $$results | wc -l`; \ - xpass=`grep "^$$ws*:test-result:$$ws*XPASS" $$results | wc -l`; \ - error=`grep "^$$ws*:test-result:$$ws*ERROR" $$results | wc -l`; \ - if test `expr $$fail + $$xpass + $$error` -eq 0; then \ - success=true; \ - else \ - success=false; \ - fi; \ - br='==================='; br=$$br$$br$$br$$br; \ - result_count () \ - { \ - if test x"$$1" = x"--maybe-color"; then \ - maybe_colorize=yes; \ - elif test x"$$1" = x"--no-color"; then \ - maybe_colorize=no; \ - else \ - echo "$@: invalid 'result_count' usage" >&2; exit 4; \ - fi; \ - shift; \ - desc=$$1 count=$$2; \ - if test $$maybe_colorize = yes && test $$count -gt 0; then \ - color_start=$$3 color_end=$$std; \ - else \ - color_start= color_end=; \ - fi; \ - echo "$${color_start}# $$desc $$count$${color_end}"; \ - }; \ - create_testsuite_report () \ - { \ - result_count $$1 "TOTAL:" $$all "$$brg"; \ - result_count $$1 "PASS: " $$pass "$$grn"; \ - result_count $$1 "SKIP: " $$skip "$$blu"; \ - result_count $$1 "XFAIL:" $$xfail "$$lgn"; \ - result_count $$1 "FAIL: " $$fail "$$red"; \ - result_count $$1 "XPASS:" $$xpass "$$red"; \ - result_count $$1 "ERROR:" $$error "$$mgn"; \ - }; \ - { \ - echo "$(PACKAGE_STRING): $(subdir)/$(TEST_SUITE_LOG)" | \ - $(am__rst_title); \ - create_testsuite_report --no-color; \ - echo; \ - echo ".. contents:: :depth: 2"; \ - echo; \ - for b in $$bases; do echo $$b; done \ - | $(am__create_global_log); \ - } >$(TEST_SUITE_LOG).tmp || exit 1; \ - mv $(TEST_SUITE_LOG).tmp $(TEST_SUITE_LOG); \ - if $$success; then \ - col="$$grn"; \ - else \ - col="$$red"; \ - test x"$$VERBOSE" = x || cat $(TEST_SUITE_LOG); \ - fi; \ - echo "$${col}$$br$${std}"; \ - echo "$${col}Testsuite summary for $(PACKAGE_STRING)$${std}"; \ - echo "$${col}$$br$${std}"; \ - create_testsuite_report --maybe-color; \ - echo "$$col$$br$$std"; \ - if $$success; then :; else \ - echo "$${col}See $(subdir)/$(TEST_SUITE_LOG)$${std}"; \ - if test -n "$(PACKAGE_BUGREPORT)"; then \ - echo "$${col}Please report to $(PACKAGE_BUGREPORT)$${std}"; \ - fi; \ - echo "$$col$$br$$std"; \ - fi; \ - $$success || exit 1 - -check-TESTS: - @list='$(RECHECK_LOGS)'; test -z "$$list" || rm -f $$list - @list='$(RECHECK_LOGS:.log=.trs)'; test -z "$$list" || rm -f $$list - @test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG) - @set +e; $(am__set_TESTS_bases); \ - log_list=`for i in $$bases; do echo $$i.log; done`; \ - trs_list=`for i in $$bases; do echo $$i.trs; done`; \ - log_list=`echo $$log_list`; trs_list=`echo $$trs_list`; \ - $(MAKE) $(AM_MAKEFLAGS) $(TEST_SUITE_LOG) TEST_LOGS="$$log_list"; \ - exit $$?; -recheck: all $(check_PROGRAMS) - @test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG) - @set +e; $(am__set_TESTS_bases); \ - bases=`for i in $$bases; do echo $$i; done \ - | $(am__list_recheck_tests)` || exit 1; \ - log_list=`for i in $$bases; do echo $$i.log; done`; \ - log_list=`echo $$log_list`; \ - $(MAKE) $(AM_MAKEFLAGS) $(TEST_SUITE_LOG) \ - am__force_recheck=am--force-recheck \ - TEST_LOGS="$$log_list"; \ - exit $$? -nghttpx-unittest.log: nghttpx-unittest$(EXEEXT) - @p='nghttpx-unittest$(EXEEXT)'; \ - b='nghttpx-unittest'; \ - $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ - --log-file $$b.log --trs-file $$b.trs \ - $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ - "$$tst" $(AM_TESTS_FD_REDIRECT) -.test.log: - @p='$<'; \ - $(am__set_b); \ - $(am__check_pre) $(TEST_LOG_DRIVER) --test-name "$$f" \ - --log-file $$b.log --trs-file $$b.trs \ - $(am__common_driver_flags) $(AM_TEST_LOG_DRIVER_FLAGS) $(TEST_LOG_DRIVER_FLAGS) -- $(TEST_LOG_COMPILE) \ - "$$tst" $(AM_TESTS_FD_REDIRECT) -@am__EXEEXT_TRUE@.test$(EXEEXT).log: -@am__EXEEXT_TRUE@ @p='$<'; \ -@am__EXEEXT_TRUE@ $(am__set_b); \ -@am__EXEEXT_TRUE@ $(am__check_pre) $(TEST_LOG_DRIVER) --test-name "$$f" \ -@am__EXEEXT_TRUE@ --log-file $$b.log --trs-file $$b.trs \ -@am__EXEEXT_TRUE@ $(am__common_driver_flags) $(AM_TEST_LOG_DRIVER_FLAGS) $(TEST_LOG_DRIVER_FLAGS) -- $(TEST_LOG_COMPILE) \ -@am__EXEEXT_TRUE@ "$$tst" $(AM_TESTS_FD_REDIRECT) - -distdir: $(DISTFILES) - @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ - topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ - list='$(DISTFILES)'; \ - dist_files=`for file in $$list; do echo $$file; done | \ - sed -e "s|^$$srcdirstrip/||;t" \ - -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ - case $$dist_files in \ - */*) $(MKDIR_P) `echo "$$dist_files" | \ - sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ - sort -u` ;; \ - esac; \ - for file in $$dist_files; do \ - if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ - if test -d $$d/$$file; then \ - dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ - if test -d "$(distdir)/$$file"; then \ - find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ - fi; \ - if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ - cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ - find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ - fi; \ - cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ - else \ - test -f "$(distdir)/$$file" \ - || cp -p $$d/$$file "$(distdir)/$$file" \ - || exit 1; \ - fi; \ - done - @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ - if test "$$subdir" = .; then :; else \ - $(am__make_dryrun) \ - || test -d "$(distdir)/$$subdir" \ - || $(MKDIR_P) "$(distdir)/$$subdir" \ - || exit 1; \ - dir1=$$subdir; dir2="$(distdir)/$$subdir"; \ - $(am__relativize); \ - new_distdir=$$reldir; \ - dir1=$$subdir; dir2="$(top_distdir)"; \ - $(am__relativize); \ - new_top_distdir=$$reldir; \ - echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \ - echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \ - ($(am__cd) $$subdir && \ - $(MAKE) $(AM_MAKEFLAGS) \ - top_distdir="$$new_top_distdir" \ - distdir="$$new_distdir" \ - am__remove_distdir=: \ - am__skip_length_check=: \ - am__skip_mode_fix=: \ - distdir) \ - || exit 1; \ - fi; \ - done -check-am: all-am - $(MAKE) $(AM_MAKEFLAGS) $(check_PROGRAMS) - $(MAKE) $(AM_MAKEFLAGS) check-TESTS -check: check-recursive -all-am: Makefile $(LIBRARIES) $(LTLIBRARIES) $(PROGRAMS) $(DATA) -install-binPROGRAMS: install-libLTLIBRARIES - -installdirs: installdirs-recursive -installdirs-am: - for dir in "$(DESTDIR)$(libdir)" "$(DESTDIR)$(bindir)" "$(DESTDIR)$(pkgconfigdir)"; do \ - test -z "$$dir" || $(MKDIR_P) "$$dir"; \ - done -install: install-recursive -install-exec: install-exec-recursive -install-data: install-data-recursive -uninstall: uninstall-recursive - -install-am: all-am - @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am - -installcheck: installcheck-recursive -install-strip: - if test -z '$(STRIP)'; then \ - $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ - install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ - install; \ - else \ - $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ - install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ - "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ - fi -mostlyclean-generic: - -test -z "$(TEST_LOGS)" || rm -f $(TEST_LOGS) - -test -z "$(TEST_LOGS:.log=.trs)" || rm -f $(TEST_LOGS:.log=.trs) - -test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG) - -clean-generic: - -distclean-generic: - -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) - -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) - -test -z "$(DISTCLEANFILES)" || rm -f $(DISTCLEANFILES) - -maintainer-clean-generic: - @echo "This command is intended for maintainers to use" - @echo "it deletes files that may require special tools to rebuild." -clean: clean-recursive - -clean-am: clean-binPROGRAMS clean-checkPROGRAMS clean-generic \ - clean-libLTLIBRARIES clean-libtool clean-noinstLIBRARIES \ - mostlyclean-am - -distclean: distclean-recursive - -rm -rf ./$(DEPDIR) - -rm -f Makefile -distclean-am: clean-am distclean-compile distclean-generic \ - distclean-tags - -dvi: dvi-recursive - -dvi-am: - -html: html-recursive - -html-am: - -info: info-recursive - -info-am: - -install-data-am: install-pkgconfigDATA - -install-dvi: install-dvi-recursive - -install-dvi-am: - -install-exec-am: install-binPROGRAMS install-libLTLIBRARIES - -install-html: install-html-recursive - -install-html-am: - -install-info: install-info-recursive - -install-info-am: - -install-man: - -install-pdf: install-pdf-recursive - -install-pdf-am: - -install-ps: install-ps-recursive - -install-ps-am: - -installcheck-am: - -maintainer-clean: maintainer-clean-recursive - -rm -rf ./$(DEPDIR) - -rm -f Makefile -maintainer-clean-am: distclean-am maintainer-clean-generic - -mostlyclean: mostlyclean-recursive - -mostlyclean-am: mostlyclean-compile mostlyclean-generic \ - mostlyclean-libtool - -pdf: pdf-recursive - -pdf-am: - -ps: ps-recursive - -ps-am: - -uninstall-am: uninstall-binPROGRAMS uninstall-libLTLIBRARIES \ - uninstall-pkgconfigDATA - -.MAKE: $(am__recursive_targets) check-am install-am install-strip - -.PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am check \ - check-TESTS check-am clean clean-binPROGRAMS \ - clean-checkPROGRAMS clean-generic clean-libLTLIBRARIES \ - clean-libtool clean-noinstLIBRARIES cscopelist-am ctags \ - ctags-am distclean distclean-compile distclean-generic \ - distclean-libtool distclean-tags distdir dvi dvi-am html \ - html-am info info-am install install-am install-binPROGRAMS \ - install-data install-data-am install-dvi install-dvi-am \ - install-exec install-exec-am install-html install-html-am \ - install-info install-info-am install-libLTLIBRARIES \ - install-man install-pdf install-pdf-am install-pkgconfigDATA \ - install-ps install-ps-am install-strip installcheck \ - installcheck-am installdirs installdirs-am maintainer-clean \ - maintainer-clean-generic mostlyclean mostlyclean-compile \ - mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ - recheck tags tags-am uninstall uninstall-am \ - uninstall-binPROGRAMS uninstall-libLTLIBRARIES \ - uninstall-pkgconfigDATA - - -# Tell versions [3.59,3.63) of GNU make to not export all variables. -# Otherwise a system limit (for SysV at least) may be exceeded. -.NOEXPORT: diff --git a/src/app_helper.cc b/src/app_helper.cc deleted file mode 100644 index 58f8439..0000000 --- a/src/app_helper.cc +++ /dev/null @@ -1,505 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2012 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#include -#ifdef HAVE_SYS_SOCKET_H -#include -#endif // HAVE_SYS_SOCKET_H -#ifdef HAVE_NETDB_H -#include -#endif // HAVE_NETDB_H -#ifdef HAVE_UNISTD_H -#include -#endif // HAVE_UNISTD_H -#ifdef HAVE_FCNTL_H -#include -#endif // HAVE_FCNTL_H -#ifdef HAVE_NETINET_IN_H -#include -#endif // HAVE_NETINET_IN_H -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "app_helper.h" -#include "util.h" -#include "http2.h" - -namespace nghttp2 { - -namespace { -const char *strstatus(uint32_t error_code) { - switch (error_code) { - case NGHTTP2_NO_ERROR: - return "NO_ERROR"; - case NGHTTP2_PROTOCOL_ERROR: - return "PROTOCOL_ERROR"; - case NGHTTP2_INTERNAL_ERROR: - return "INTERNAL_ERROR"; - case NGHTTP2_FLOW_CONTROL_ERROR: - return "FLOW_CONTROL_ERROR"; - case NGHTTP2_SETTINGS_TIMEOUT: - return "SETTINGS_TIMEOUT"; - case NGHTTP2_STREAM_CLOSED: - return "STREAM_CLOSED"; - case NGHTTP2_FRAME_SIZE_ERROR: - return "FRAME_SIZE_ERROR"; - case NGHTTP2_REFUSED_STREAM: - return "REFUSED_STREAM"; - case NGHTTP2_CANCEL: - return "CANCEL"; - case NGHTTP2_COMPRESSION_ERROR: - return "COMPRESSION_ERROR"; - case NGHTTP2_CONNECT_ERROR: - return "CONNECT_ERROR"; - case NGHTTP2_ENHANCE_YOUR_CALM: - return "ENHANCE_YOUR_CALM"; - case NGHTTP2_INADEQUATE_SECURITY: - return "INADEQUATE_SECURITY"; - case NGHTTP2_HTTP_1_1_REQUIRED: - return "HTTP_1_1_REQUIRED"; - default: - return "UNKNOWN"; - } -} -} // namespace - -namespace { -const char *strsettingsid(int32_t id) { - switch (id) { - case NGHTTP2_SETTINGS_HEADER_TABLE_SIZE: - return "SETTINGS_HEADER_TABLE_SIZE"; - case NGHTTP2_SETTINGS_ENABLE_PUSH: - return "SETTINGS_ENABLE_PUSH"; - case NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS: - return "SETTINGS_MAX_CONCURRENT_STREAMS"; - case NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE: - return "SETTINGS_INITIAL_WINDOW_SIZE"; - case NGHTTP2_SETTINGS_MAX_FRAME_SIZE: - return "SETTINGS_MAX_FRAME_SIZE"; - case NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE: - return "SETTINGS_MAX_HEADER_LIST_SIZE"; - default: - return "UNKNOWN"; - } -} -} // namespace - -namespace { -const char *strframetype(uint8_t type) { - switch (type) { - case NGHTTP2_DATA: - return "DATA"; - case NGHTTP2_HEADERS: - return "HEADERS"; - case NGHTTP2_PRIORITY: - return "PRIORITY"; - case NGHTTP2_RST_STREAM: - return "RST_STREAM"; - case NGHTTP2_SETTINGS: - return "SETTINGS"; - case NGHTTP2_PUSH_PROMISE: - return "PUSH_PROMISE"; - case NGHTTP2_PING: - return "PING"; - case NGHTTP2_GOAWAY: - return "GOAWAY"; - case NGHTTP2_WINDOW_UPDATE: - return "WINDOW_UPDATE"; - default: - return "UNKNOWN"; - } -}; -} // namespace - -namespace { -bool color_output = false; -} // namespace - -void set_color_output(bool f) { color_output = f; } - -namespace { -FILE *outfile = stdout; -} // namespace - -void set_output(FILE *file) { outfile = file; } - -namespace { -void print_frame_attr_indent() { fprintf(outfile, " "); } -} // namespace - -namespace { -const char *ansi_esc(const char *code) { return color_output ? code : ""; } -} // namespace - -namespace { -const char *ansi_escend() { return color_output ? "\033[0m" : ""; } -} // namespace - -namespace { -void print_nv(nghttp2_nv *nv) { - fprintf(outfile, "%s%s%s: %s\n", ansi_esc("\033[1;34m"), nv->name, - ansi_escend(), nv->value); -} -} // namespace -namespace { -void print_nv(nghttp2_nv *nva, size_t nvlen) { - auto end = nva + nvlen; - for (; nva != end; ++nva) { - print_frame_attr_indent(); - - print_nv(nva); - } -} -} // namelen - -void print_timer() { - auto millis = get_timer(); - fprintf(outfile, "%s[%3ld.%03ld]%s", ansi_esc("\033[33m"), - (long int)(millis.count() / 1000), (long int)(millis.count() % 1000), - ansi_escend()); -} - -namespace { -void print_frame_hd(const nghttp2_frame_hd &hd) { - fprintf(outfile, "\n", hd.length, - hd.flags, hd.stream_id); -} -} // namespace - -namespace { -void print_flags(const nghttp2_frame_hd &hd) { - std::string s; - switch (hd.type) { - case NGHTTP2_DATA: - if (hd.flags & NGHTTP2_FLAG_END_STREAM) { - s += "END_STREAM"; - } - if (hd.flags & NGHTTP2_FLAG_PADDED) { - if (!s.empty()) { - s += " | "; - } - s += "PADDED"; - } - break; - case NGHTTP2_HEADERS: - if (hd.flags & NGHTTP2_FLAG_END_STREAM) { - s += "END_STREAM"; - } - if (hd.flags & NGHTTP2_FLAG_END_HEADERS) { - if (!s.empty()) { - s += " | "; - } - s += "END_HEADERS"; - } - if (hd.flags & NGHTTP2_FLAG_PADDED) { - if (!s.empty()) { - s += " | "; - } - s += "PADDED"; - } - if (hd.flags & NGHTTP2_FLAG_PRIORITY) { - if (!s.empty()) { - s += " | "; - } - s += "PRIORITY"; - } - - break; - case NGHTTP2_PRIORITY: - break; - case NGHTTP2_SETTINGS: - if (hd.flags & NGHTTP2_FLAG_ACK) { - s += "ACK"; - } - break; - case NGHTTP2_PUSH_PROMISE: - if (hd.flags & NGHTTP2_FLAG_END_HEADERS) { - s += "END_HEADERS"; - } - if (hd.flags & NGHTTP2_FLAG_PADDED) { - if (!s.empty()) { - s += " | "; - } - s += "PADDED"; - } - break; - case NGHTTP2_PING: - if (hd.flags & NGHTTP2_FLAG_ACK) { - s += "ACK"; - } - break; - } - fprintf(outfile, "; %s\n", s.c_str()); -} -} // namespace - -enum print_type { PRINT_SEND, PRINT_RECV }; - -namespace { -const char *frame_name_ansi_esc(print_type ptype) { - return ansi_esc(ptype == PRINT_SEND ? "\033[1;35m" : "\033[1;36m"); -} -} // namespace - -namespace { -void print_frame(print_type ptype, const nghttp2_frame *frame) { - fprintf(outfile, "%s%s%s frame ", frame_name_ansi_esc(ptype), - strframetype(frame->hd.type), ansi_escend()); - print_frame_hd(frame->hd); - if (frame->hd.flags) { - print_frame_attr_indent(); - print_flags(frame->hd); - } - switch (frame->hd.type) { - case NGHTTP2_DATA: - if (frame->data.padlen > 0) { - print_frame_attr_indent(); - fprintf(outfile, "(padlen=%zu)\n", frame->data.padlen); - } - break; - case NGHTTP2_HEADERS: - print_frame_attr_indent(); - fprintf(outfile, "(padlen=%zu", frame->headers.padlen); - if (frame->hd.flags & NGHTTP2_FLAG_PRIORITY) { - fprintf(outfile, ", dep_stream_id=%d, weight=%u, exclusive=%d", - frame->headers.pri_spec.stream_id, frame->headers.pri_spec.weight, - frame->headers.pri_spec.exclusive); - } - fprintf(outfile, ")\n"); - switch (frame->headers.cat) { - case NGHTTP2_HCAT_REQUEST: - print_frame_attr_indent(); - fprintf(outfile, "; Open new stream\n"); - break; - case NGHTTP2_HCAT_RESPONSE: - print_frame_attr_indent(); - fprintf(outfile, "; First response header\n"); - break; - case NGHTTP2_HCAT_PUSH_RESPONSE: - print_frame_attr_indent(); - fprintf(outfile, "; First push response header\n"); - break; - default: - break; - } - print_nv(frame->headers.nva, frame->headers.nvlen); - break; - case NGHTTP2_PRIORITY: - print_frame_attr_indent(); - - fprintf(outfile, "(dep_stream_id=%d, weight=%u, exclusive=%d)\n", - frame->priority.pri_spec.stream_id, frame->priority.pri_spec.weight, - frame->priority.pri_spec.exclusive); - - break; - case NGHTTP2_RST_STREAM: - print_frame_attr_indent(); - fprintf(outfile, "(error_code=%s(0x%02x))\n", - strstatus(frame->rst_stream.error_code), - frame->rst_stream.error_code); - break; - case NGHTTP2_SETTINGS: - print_frame_attr_indent(); - fprintf(outfile, "(niv=%lu)\n", - static_cast(frame->settings.niv)); - for (size_t i = 0; i < frame->settings.niv; ++i) { - print_frame_attr_indent(); - fprintf(outfile, "[%s(0x%02x):%u]\n", - strsettingsid(frame->settings.iv[i].settings_id), - frame->settings.iv[i].settings_id, frame->settings.iv[i].value); - } - break; - case NGHTTP2_PUSH_PROMISE: - print_frame_attr_indent(); - fprintf(outfile, "(padlen=%zu, promised_stream_id=%d)\n", - frame->push_promise.padlen, frame->push_promise.promised_stream_id); - print_nv(frame->push_promise.nva, frame->push_promise.nvlen); - break; - case NGHTTP2_PING: - print_frame_attr_indent(); - fprintf(outfile, "(opaque_data=%s)\n", - util::format_hex(frame->ping.opaque_data, 8).c_str()); - break; - case NGHTTP2_GOAWAY: - print_frame_attr_indent(); - fprintf(outfile, "(last_stream_id=%d, error_code=%s(0x%02x), " - "opaque_data(%u)=[%s])\n", - frame->goaway.last_stream_id, strstatus(frame->goaway.error_code), - frame->goaway.error_code, - static_cast(frame->goaway.opaque_data_len), - util::ascii_dump(frame->goaway.opaque_data, - frame->goaway.opaque_data_len).c_str()); - break; - case NGHTTP2_WINDOW_UPDATE: - print_frame_attr_indent(); - fprintf(outfile, "(window_size_increment=%d)\n", - frame->window_update.window_size_increment); - break; - default: - break; - } -} -} // namespace - -int verbose_on_header_callback(nghttp2_session *session, - const nghttp2_frame *frame, const uint8_t *name, - size_t namelen, const uint8_t *value, - size_t valuelen, uint8_t flags, - void *user_data) { - nghttp2_nv nv = {const_cast(name), const_cast(value), - namelen, valuelen}; - - print_timer(); - fprintf(outfile, " recv (stream_id=%d", frame->hd.stream_id); - if (flags & NGHTTP2_NV_FLAG_NO_INDEX) { - fprintf(outfile, ", sensitive"); - } - fprintf(outfile, ") "); - - print_nv(&nv); - fflush(outfile); - - return 0; -} - -int verbose_on_frame_recv_callback(nghttp2_session *session, - const nghttp2_frame *frame, - void *user_data) { - print_timer(); - fprintf(outfile, " recv "); - print_frame(PRINT_RECV, frame); - fflush(outfile); - return 0; -} - -int verbose_on_invalid_frame_recv_callback(nghttp2_session *session, - const nghttp2_frame *frame, - int lib_error_code, - void *user_data) { - print_timer(); - fprintf(outfile, " [INVALID; error=%s] recv ", - nghttp2_strerror(lib_error_code)); - print_frame(PRINT_RECV, frame); - fflush(outfile); - return 0; -} - -int verbose_on_frame_send_callback(nghttp2_session *session, - const nghttp2_frame *frame, - void *user_data) { - print_timer(); - fprintf(outfile, " send "); - print_frame(PRINT_SEND, frame); - fflush(outfile); - return 0; -} - -int verbose_on_data_chunk_recv_callback(nghttp2_session *session, uint8_t flags, - int32_t stream_id, const uint8_t *data, - size_t len, void *user_data) { - print_timer(); - auto srecv = - nghttp2_session_get_stream_effective_recv_data_length(session, stream_id); - auto crecv = nghttp2_session_get_effective_recv_data_length(session); - - fprintf(outfile, - " recv (stream_id=%d, length=%zu, srecv=%d, crecv=%d) DATA\n", - stream_id, len, srecv, crecv); - fflush(outfile); - - return 0; -} - -namespace { -std::chrono::steady_clock::time_point base_tv; -} // namespace - -void reset_timer() { base_tv = std::chrono::steady_clock::now(); } - -std::chrono::milliseconds get_timer() { - return time_delta(std::chrono::steady_clock::now(), base_tv); -} - -std::chrono::steady_clock::time_point get_time() { - return std::chrono::steady_clock::now(); -} - -ssize_t deflate_data(uint8_t *out, size_t outlen, const uint8_t *in, - size_t inlen) { - int rv; - z_stream zst; - uint8_t temp_out[8192]; - auto temp_outlen = sizeof(temp_out); - - zst.next_in = Z_NULL; - zst.zalloc = Z_NULL; - zst.zfree = Z_NULL; - zst.opaque = Z_NULL; - - rv = deflateInit2(&zst, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 31, 9, - Z_DEFAULT_STRATEGY); - - if (rv != Z_OK) { - return -1; - } - - zst.avail_in = inlen; - zst.next_in = (uint8_t *)in; - zst.avail_out = temp_outlen; - zst.next_out = temp_out; - - rv = deflate(&zst, Z_FINISH); - - deflateEnd(&zst); - - if (rv != Z_STREAM_END) { - return -1; - } - - temp_outlen -= zst.avail_out; - - if (temp_outlen > outlen) { - return -1; - } - - memcpy(out, temp_out, temp_outlen); - - return temp_outlen; -} - -} // namespace nghttp2 diff --git a/src/app_helper.h b/src/app_helper.h deleted file mode 100644 index 408adac..0000000 --- a/src/app_helper.h +++ /dev/null @@ -1,95 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2012 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#ifndef APP_HELPER_H -#define APP_HELPER_H - -#include "nghttp2_config.h" - -#include -#include -#ifdef HAVE_SYS_TIME_H -#include -#endif // HAVE_SYS_TIME_H -#include - -#include -#include - -#include - -namespace nghttp2 { - -int verbose_on_header_callback(nghttp2_session *session, - const nghttp2_frame *frame, const uint8_t *name, - size_t namelen, const uint8_t *value, - size_t valuelen, uint8_t flags, void *user_data); - -int verbose_on_frame_recv_callback(nghttp2_session *session, - const nghttp2_frame *frame, void *user_data); - -int verbose_on_invalid_frame_recv_callback(nghttp2_session *session, - const nghttp2_frame *frame, - int lib_error_code, void *user_data); - -int verbose_on_frame_send_callback(nghttp2_session *session, - const nghttp2_frame *frame, void *user_data); - -int verbose_on_data_chunk_recv_callback(nghttp2_session *session, uint8_t flags, - int32_t stream_id, const uint8_t *data, - size_t len, void *user_data); - -// Returns difference between |a| and |b| in milliseconds, assuming -// |a| is more recent than |b|. -template -std::chrono::milliseconds time_delta(const TimePoint &a, const TimePoint &b) { - return std::chrono::duration_cast(a - b); -} - -// Resets timer -void reset_timer(); - -// Returns the duration since timer reset. -std::chrono::milliseconds get_timer(); - -// Returns current time point. -std::chrono::steady_clock::time_point get_time(); - -void print_timer(); - -// Setting true will print characters with ANSI color escape codes -// when printing HTTP2 frames. This function changes a static -// variable. -void set_color_output(bool f); - -// Set output file when printing HTTP2 frames. By default, stdout is -// used. -void set_output(FILE *file); - -ssize_t deflate_data(uint8_t *out, size_t outlen, const uint8_t *in, - size_t inlen); - -} // namespace nghttp2 - -#endif // APP_HELPER_H diff --git a/src/asio_client_request.cc b/src/asio_client_request.cc deleted file mode 100644 index 1fa93ca..0000000 --- a/src/asio_client_request.cc +++ /dev/null @@ -1,67 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2015 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#include "nghttp2_config.h" - -#include - -#include "asio_client_request_impl.h" - -#include "template.h" - -namespace nghttp2 { -namespace asio_http2 { -namespace client { - -request::request() : impl_(make_unique()) {} - -request::~request() {} - -void request::write_trailer(header_map h) const { - impl_->write_trailer(std::move(h)); -} - -void request::cancel(uint32_t error_code) const { impl_->cancel(error_code); } - -void request::on_response(response_cb cb) const { - impl_->on_response(std::move(cb)); -} - -void request::on_push(request_cb cb) const { impl_->on_push(std::move(cb)); } - -void request::on_close(close_cb cb) const { impl_->on_close(std::move(cb)); } - -const uri_ref &request::uri() const { return impl_->uri(); } - -const std::string &request::method() const { return impl_->method(); } - -const header_map &request::header() const { return impl_->header(); } - -void request::resume() const { impl_->resume(); } - -request_impl &request::impl() const { return *impl_; } - -} // namespace client -} // namespace asio_http2 -} // namespace nghttp2 diff --git a/src/asio_client_request_impl.cc b/src/asio_client_request_impl.cc deleted file mode 100644 index 5512c96..0000000 --- a/src/asio_client_request_impl.cc +++ /dev/null @@ -1,110 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2015 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#include "asio_client_request_impl.h" - -#include "asio_client_stream.h" -#include "asio_client_session_impl.h" -#include "template.h" - -namespace nghttp2 { -namespace asio_http2 { -namespace client { - -request_impl::request_impl() : strm_(nullptr) {} - -void request_impl::write_trailer(header_map h) { - auto sess = strm_->session(); - sess->write_trailer(*strm_, std::move(h)); -} - -void request_impl::cancel(uint32_t error_code) { - auto sess = strm_->session(); - sess->cancel(*strm_, error_code); -} - -void request_impl::on_response(response_cb cb) { response_cb_ = std::move(cb); } - -void request_impl::call_on_response(response &res) { - if (response_cb_) { - response_cb_(res); - } -} - -void request_impl::on_push(request_cb cb) { push_request_cb_ = std::move(cb); } - -void request_impl::call_on_push(request &push_req) { - if (push_request_cb_) { - push_request_cb_(push_req); - } -}; - -void request_impl::on_close(close_cb cb) { close_cb_ = std::move(cb); } - -void request_impl::call_on_close(uint32_t error_code) { - if (close_cb_) { - close_cb_(error_code); - } -} - -void request_impl::on_read(generator_cb cb) { generator_cb_ = std::move(cb); } - -generator_cb::result_type request_impl::call_on_read(uint8_t *buf, - std::size_t len, - uint32_t *data_flags) { - if (generator_cb_) { - return generator_cb_(buf, len, data_flags); - } - - *data_flags |= NGHTTP2_DATA_FLAG_EOF; - - return 0; -} - -void request_impl::resume() { - auto sess = strm_->session(); - sess->resume(*strm_); -} - -void request_impl::header(header_map h) { header_ = std::move(h); } - -header_map &request_impl::header() { return header_; } - -const header_map &request_impl::header() const { return header_; } - -void request_impl::stream(class stream *strm) { strm_ = strm; } - -void request_impl::uri(uri_ref uri) { uri_ = std::move(uri); } - -const uri_ref &request_impl::uri() const { return uri_; } - -uri_ref &request_impl::uri() { return uri_; } - -void request_impl::method(std::string s) { method_ = std::move(s); } - -const std::string &request_impl::method() const { return method_; } - -} // namespace client -} // namespace asio_http2 -} // namespace nghttp2 diff --git a/src/asio_client_request_impl.h b/src/asio_client_request_impl.h deleted file mode 100644 index 802a83d..0000000 --- a/src/asio_client_request_impl.h +++ /dev/null @@ -1,93 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2015 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#ifndef ASIO_CLIENT_REQUEST_IMPL_H -#define ASIO_CLIENT_REQUEST_IMPL_H - -#include "nghttp2_config.h" - -#include - -namespace nghttp2 { -namespace asio_http2 { -namespace client { - -class response; -class stream; - -class request_impl { -public: - request_impl(); - - request_impl(const request_impl &) = delete; - request_impl &operator=(const request_impl &) = delete; - - void write_trailer(header_map h); - - void cancel(uint32_t error_code); - - void on_response(response_cb cb); - void call_on_response(response &res); - - void on_push(request_cb cb); - void call_on_push(request &push_req); - - void on_close(close_cb cb); - void call_on_close(uint32_t error_code); - - void on_read(generator_cb cb); - generator_cb::result_type call_on_read(uint8_t *buf, std::size_t len, - uint32_t *data_flags); - - void resume(); - - void header(header_map h); - header_map &header(); - const header_map &header() const; - - void stream(class stream *strm); - - void uri(uri_ref uri); - const uri_ref &uri() const; - uri_ref &uri(); - - void method(std::string s); - const std::string &method() const; - -private: - header_map header_; - response_cb response_cb_; - request_cb push_request_cb_; - close_cb close_cb_; - generator_cb generator_cb_; - class stream *strm_; - uri_ref uri_; - std::string method_; -}; - -} // namespace client -} // namespace asio_http2 -} // namespace nghttp2 - -#endif // ASIO_CLIENT_REQUEST_IMPL_H diff --git a/src/asio_client_response.cc b/src/asio_client_response.cc deleted file mode 100644 index 4805422..0000000 --- a/src/asio_client_response.cc +++ /dev/null @@ -1,53 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2015 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#include "nghttp2_config.h" - -#include - -#include "asio_client_response_impl.h" - -#include "template.h" - -namespace nghttp2 { -namespace asio_http2 { -namespace client { - -response::response() : impl_(make_unique()) {} - -response::~response() {} - -void response::on_data(data_cb cb) const { impl_->on_data(std::move(cb)); } - -int response::status_code() const { return impl_->status_code(); } - -int64_t response::content_length() const { return impl_->content_length(); } - -const header_map &response::header() const { return impl_->header(); } - -response_impl &response::impl() const { return *impl_; } - -} // namespace client -} // namespace asio_http2 -} // namespace nghttp2 diff --git a/src/asio_client_response_impl.cc b/src/asio_client_response_impl.cc deleted file mode 100644 index fce25a4..0000000 --- a/src/asio_client_response_impl.cc +++ /dev/null @@ -1,57 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2015 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#include "asio_client_response_impl.h" - -#include "template.h" - -namespace nghttp2 { -namespace asio_http2 { -namespace client { - -response_impl::response_impl() : content_length_(-1), status_code_(0) {} - -void response_impl::on_data(data_cb cb) { data_cb_ = std::move(cb); } - -void response_impl::call_on_data(const uint8_t *data, std::size_t len) { - if (data_cb_) { - data_cb_(data, len); - } -} - -void response_impl::status_code(int sc) { status_code_ = sc; } - -int response_impl::status_code() const { return status_code_; } - -void response_impl::content_length(int64_t n) { content_length_ = n; } - -int64_t response_impl::content_length() const { return content_length_; } - -header_map &response_impl::header() { return header_; } - -const header_map &response_impl::header() const { return header_; } - -} // namespace client -} // namespace asio_http2 -} // namespace nghttp2 diff --git a/src/asio_client_response_impl.h b/src/asio_client_response_impl.h deleted file mode 100644 index e2c2286..0000000 --- a/src/asio_client_response_impl.h +++ /dev/null @@ -1,69 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2015 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#ifndef ASIO_CLIENT_RESPONSE_IMPL_H -#define ASIO_CLIENT_RESPONSE_IMPL_H - -#include "nghttp2_config.h" - -#include - -namespace nghttp2 { -namespace asio_http2 { -namespace client { - -class response_impl { -public: - response_impl(); - - response_impl(const response_impl &) = delete; - response_impl &operator=(const response_impl &) = delete; - - void on_data(data_cb cb); - - void call_on_data(const uint8_t *data, std::size_t len); - - void status_code(int sc); - int status_code() const; - - void content_length(int64_t n); - int64_t content_length() const; - - header_map &header(); - const header_map &header() const; - -private: - data_cb data_cb_; - - header_map header_; - - int64_t content_length_; - int status_code_; -}; - -} // namespace client -} // namespace asio_http2 -} // namespace nghttp2 - -#endif // ASIO_CLIENT_RESPONSE_IMPL_H diff --git a/src/asio_client_session.cc b/src/asio_client_session.cc deleted file mode 100644 index aaee1e7..0000000 --- a/src/asio_client_session.cc +++ /dev/null @@ -1,98 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2015 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#include "nghttp2_config.h" - -#include - -#include "asio_client_session_tcp_impl.h" -#include "asio_client_session_tls_impl.h" -#include "asio_common.h" -#include "template.h" - -namespace nghttp2 { -namespace asio_http2 { -namespace client { - -using boost::asio::ip::tcp; - -session::session(boost::asio::io_service &io_service, const std::string &host, - const std::string &service) - : impl_(make_unique(io_service, host, service)) {} - -session::session(boost::asio::io_service &io_service, - boost::asio::ssl::context &tls_ctx, const std::string &host, - const std::string &service) - : impl_(make_unique(io_service, tls_ctx, host, service)) { -} - -session::~session() {} - -session::session(session &&other) noexcept : impl_(std::move(other.impl_)) {} - -session &session::operator=(session &&other) noexcept { - if (this == &other) { - return *this; - } - - impl_ = std::move(other.impl_); - return *this; -} - -void session::on_connect(connect_cb cb) const { - impl_->on_connect(std::move(cb)); -} - -void session::on_error(error_cb cb) const { impl_->on_error(std::move(cb)); } - -void session::shutdown() const { impl_->shutdown(); } - -boost::asio::io_service &session::io_service() const { - return impl_->io_service(); -} - -const request *session::submit(boost::system::error_code &ec, - const std::string &method, - const std::string &uri, header_map h) const { - return impl_->submit(ec, method, uri, generator_cb(), std::move(h)); -} - -const request *session::submit(boost::system::error_code &ec, - const std::string &method, - const std::string &uri, std::string data, - header_map h) const { - return impl_->submit(ec, method, uri, string_generator(std::move(data)), - std::move(h)); -} - -const request *session::submit(boost::system::error_code &ec, - const std::string &method, - const std::string &uri, generator_cb cb, - header_map h) const { - return impl_->submit(ec, method, uri, std::move(cb), std::move(h)); -} - -} // namespace client -} // namespace asio_http2 -} // nghttp2 diff --git a/src/asio_client_session_impl.cc b/src/asio_client_session_impl.cc deleted file mode 100644 index 9cb57c9..0000000 --- a/src/asio_client_session_impl.cc +++ /dev/null @@ -1,625 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2015 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#include "asio_client_session_impl.h" - -#include - -#include "asio_client_stream.h" -#include "asio_client_request_impl.h" -#include "asio_client_response_impl.h" -#include "asio_common.h" -#include "template.h" -#include "util.h" -#include "http2.h" - -namespace nghttp2 { -namespace asio_http2 { -namespace client { - -session_impl::session_impl(boost::asio::io_service &io_service) - : wblen_(0), io_service_(io_service), resolver_(io_service), - session_(nullptr), data_pending_(nullptr), data_pendinglen_(0), - writing_(false), inside_callback_(false) {} - -session_impl::~session_impl() { - // finish up all active stream - for (auto &p : streams_) { - auto &strm = p.second; - auto &req = strm->request().impl(); - req.call_on_close(NGHTTP2_INTERNAL_ERROR); - } - - nghttp2_session_del(session_); -} - -void session_impl::start_resolve(const std::string &host, - const std::string &service) { - resolver_.async_resolve({host, service}, - [this](const boost::system::error_code &ec, - tcp::resolver::iterator endpoint_it) { - if (ec) { - not_connected(ec); - return; - } - - start_connect(endpoint_it); - }); -} - -void session_impl::connected(tcp::resolver::iterator endpoint_it) { - if (!setup_session()) { - return; - } - - socket().set_option(boost::asio::ip::tcp::no_delay(true)); - - do_write(); - do_read(); - - auto &connect_cb = on_connect(); - if (connect_cb) { - connect_cb(endpoint_it); - } -} - -void session_impl::not_connected(const boost::system::error_code &ec) { - call_error_cb(ec); -} - -void session_impl::on_connect(connect_cb cb) { connect_cb_ = std::move(cb); } - -void session_impl::on_error(error_cb cb) { error_cb_ = std::move(cb); } - -const connect_cb &session_impl::on_connect() const { return connect_cb_; } - -const error_cb &session_impl::on_error() const { return error_cb_; } - -void session_impl::call_error_cb(const boost::system::error_code &ec) { - auto &error_cb = on_error(); - if (!error_cb) { - return; - } - error_cb(ec); -} - -namespace { -int on_begin_headers_callback(nghttp2_session *session, - const nghttp2_frame *frame, void *user_data) { - if (frame->hd.type != NGHTTP2_PUSH_PROMISE) { - return 0; - } - - auto sess = static_cast(user_data); - sess->create_push_stream(frame->push_promise.promised_stream_id); - - return 0; -} -} // namespace - -namespace { -int on_header_callback(nghttp2_session *session, const nghttp2_frame *frame, - const uint8_t *name, size_t namelen, - const uint8_t *value, size_t valuelen, uint8_t flags, - void *user_data) { - auto sess = static_cast(user_data); - stream *strm; - - switch (frame->hd.type) { - case NGHTTP2_HEADERS: { - strm = sess->find_stream(frame->hd.stream_id); - if (!strm) { - return 0; - } - - // ignore trailers - if (frame->headers.cat == NGHTTP2_HCAT_HEADERS && - !strm->expect_final_response()) { - return 0; - } - - auto token = http2::lookup_token(name, namelen); - - auto &res = strm->response().impl(); - if (token == http2::HD__STATUS) { - res.status_code(util::parse_uint(value, valuelen)); - } else { - - if (token == http2::HD_CONTENT_LENGTH) { - res.content_length(util::parse_uint(value, valuelen)); - } - - res.header().emplace( - std::string(name, name + namelen), - header_value{std::string(value, value + valuelen), - (flags & NGHTTP2_NV_FLAG_NO_INDEX) != 0}); - } - break; - } - case NGHTTP2_PUSH_PROMISE: { - strm = sess->find_stream(frame->push_promise.promised_stream_id); - if (!strm) { - return 0; - } - - auto &req = strm->request().impl(); - auto &uri = req.uri(); - - switch (http2::lookup_token(name, namelen)) { - case http2::HD__METHOD: - req.method(std::string(value, value + valuelen)); - break; - case http2::HD__SCHEME: - uri.scheme.assign(value, value + valuelen); - break; - case http2::HD__PATH: - split_path(uri, value, value + valuelen); - break; - case http2::HD__AUTHORITY: - uri.host.assign(value, value + valuelen); - break; - case http2::HD_HOST: - if (uri.host.empty()) { - uri.host.assign(value, value + valuelen); - } - // fall through - default: - req.header().emplace( - std::string(name, name + namelen), - header_value{std::string(value, value + valuelen), - (flags & NGHTTP2_NV_FLAG_NO_INDEX) != 0}); - } - - break; - } - default: - return 0; - } - - return 0; -} -} // namespace - -namespace { -int on_frame_recv_callback(nghttp2_session *session, const nghttp2_frame *frame, - void *user_data) { - auto sess = static_cast(user_data); - auto strm = sess->find_stream(frame->hd.stream_id); - - switch (frame->hd.type) { - case NGHTTP2_DATA: { - if (!strm) { - return 0; - } - if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) { - strm->response().impl().call_on_data(nullptr, 0); - } - break; - } - case NGHTTP2_HEADERS: { - if (!strm) { - return 0; - } - - // ignore trailers - if (frame->headers.cat == NGHTTP2_HCAT_HEADERS && - !strm->expect_final_response()) { - return 0; - } - - if (strm->expect_final_response()) { - // wait for final response - return 0; - } - - auto &req = strm->request().impl(); - req.call_on_response(strm->response()); - if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) { - strm->response().impl().call_on_data(nullptr, 0); - } - break; - } - case NGHTTP2_PUSH_PROMISE: { - if (!strm) { - return 0; - } - - auto push_strm = sess->find_stream(frame->push_promise.promised_stream_id); - if (!push_strm) { - return 0; - } - - strm->request().impl().call_on_push(push_strm->request()); - - break; - } - } - return 0; -} -} // namespace - -namespace { -int on_data_chunk_recv_callback(nghttp2_session *session, uint8_t flags, - int32_t stream_id, const uint8_t *data, - size_t len, void *user_data) { - auto sess = static_cast(user_data); - auto strm = sess->find_stream(stream_id); - if (!strm) { - return 0; - } - - auto &res = strm->response().impl(); - res.call_on_data(data, len); - - return 0; -} -} // namespace - -namespace { -int on_stream_close_callback(nghttp2_session *session, int32_t stream_id, - uint32_t error_code, void *user_data) { - auto sess = static_cast(user_data); - auto strm = sess->pop_stream(stream_id); - if (!strm) { - return 0; - } - - strm->request().impl().call_on_close(error_code); - - return 0; -} -} // namespace - -bool session_impl::setup_session() { - nghttp2_session_callbacks *callbacks; - nghttp2_session_callbacks_new(&callbacks); - auto cb_del = defer(nghttp2_session_callbacks_del, callbacks); - - nghttp2_session_callbacks_set_on_begin_headers_callback( - callbacks, on_begin_headers_callback); - nghttp2_session_callbacks_set_on_header_callback(callbacks, - on_header_callback); - nghttp2_session_callbacks_set_on_frame_recv_callback(callbacks, - on_frame_recv_callback); - nghttp2_session_callbacks_set_on_data_chunk_recv_callback( - callbacks, on_data_chunk_recv_callback); - nghttp2_session_callbacks_set_on_stream_close_callback( - callbacks, on_stream_close_callback); - - auto rv = nghttp2_session_client_new(&session_, callbacks, this); - if (rv != 0) { - call_error_cb(make_error_code(static_cast(rv))); - return false; - } - - const uint32_t window_size = 256 * 1024 * 1024; - - std::array iv{ - {{NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100}, - // typically client is just a *sink* and just process data as - // much as possible. Use large window size by default. - {NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE, window_size}}}; - nghttp2_submit_settings(session_, NGHTTP2_FLAG_NONE, iv.data(), iv.size()); - // increase connection window size up to window_size - nghttp2_submit_window_update(session_, NGHTTP2_FLAG_NONE, 0, - window_size - - NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE); - return true; -} - -int session_impl::write_trailer(stream &strm, header_map h) { - int rv; - auto nva = std::vector(); - nva.reserve(h.size()); - for (auto &hd : h) { - nva.push_back(nghttp2::http2::make_nv(hd.first, hd.second.value, - hd.second.sensitive)); - } - - rv = nghttp2_submit_trailer(session_, strm.stream_id(), nva.data(), - nva.size()); - - if (rv != 0) { - return -1; - } - - signal_write(); - - return 0; -} - -void session_impl::cancel(stream &strm, uint32_t error_code) { - nghttp2_submit_rst_stream(session_, NGHTTP2_FLAG_NONE, strm.stream_id(), - error_code); - signal_write(); -} - -void session_impl::resume(stream &strm) { - nghttp2_session_resume_data(session_, strm.stream_id()); - signal_write(); -} - -stream *session_impl::find_stream(int32_t stream_id) { - auto it = streams_.find(stream_id); - if (it == std::end(streams_)) { - return nullptr; - } - return (*it).second.get(); -} - -std::unique_ptr session_impl::pop_stream(int32_t stream_id) { - auto it = streams_.find(stream_id); - if (it == std::end(streams_)) { - return nullptr; - } - auto strm = std::move((*it).second); - streams_.erase(it); - return strm; -} - -stream *session_impl::create_push_stream(int32_t stream_id) { - auto strm = create_stream(); - strm->stream_id(stream_id); - auto p = streams_.emplace(stream_id, std::move(strm)); - assert(p.second); - return (*p.first).second.get(); -} - -std::unique_ptr session_impl::create_stream() { - return make_unique(this); -} - -const request *session_impl::submit(boost::system::error_code &ec, - const std::string &method, - const std::string &uri, generator_cb cb, - header_map h) { - ec.clear(); - - http_parser_url u{}; - // TODO Handle CONNECT method - if (http_parser_parse_url(uri.c_str(), uri.size(), 0, &u) != 0) { - ec = make_error_code(boost::system::errc::invalid_argument); - return nullptr; - } - - if ((u.field_set & (1 << UF_SCHEMA)) == 0 || - (u.field_set & (1 << UF_HOST)) == 0) { - ec = make_error_code(boost::system::errc::invalid_argument); - return nullptr; - } - - auto strm = create_stream(); - auto &req = strm->request().impl(); - auto &uref = req.uri(); - - http2::copy_url_component(uref.scheme, &u, UF_SCHEMA, uri.c_str()); - http2::copy_url_component(uref.host, &u, UF_HOST, uri.c_str()); - http2::copy_url_component(uref.raw_path, &u, UF_PATH, uri.c_str()); - http2::copy_url_component(uref.raw_query, &u, UF_QUERY, uri.c_str()); - - if (util::ipv6_numeric_addr(uref.host.c_str())) { - uref.host = "[" + uref.host; - uref.host += "]"; - } - if (u.field_set & (1 << UF_PORT)) { - uref.host += ":"; - uref.host += util::utos(u.port); - } - - if (uref.raw_path.empty()) { - uref.raw_path = "/"; - } - - uref.path = percent_decode(uref.raw_path); - - auto path = uref.raw_path; - if (u.field_set & (1 << UF_QUERY)) { - path += "?"; - path += uref.raw_query; - } - - auto nva = std::vector(); - nva.reserve(3 + h.size()); - nva.push_back(http2::make_nv_ls(":method", method)); - nva.push_back(http2::make_nv_ls(":scheme", uref.scheme)); - nva.push_back(http2::make_nv_ls(":path", path)); - nva.push_back(http2::make_nv_ls(":authority", uref.host)); - for (auto &kv : h) { - nva.push_back( - http2::make_nv(kv.first, kv.second.value, kv.second.sensitive)); - } - - req.header(std::move(h)); - - nghttp2_data_provider *prdptr = nullptr; - nghttp2_data_provider prd; - - if (cb) { - strm->request().impl().on_read(std::move(cb)); - prd.source.ptr = strm.get(); - prd.read_callback = - [](nghttp2_session *session, int32_t stream_id, uint8_t *buf, - size_t length, uint32_t *data_flags, nghttp2_data_source *source, - void *user_data) -> ssize_t { - auto strm = static_cast(source->ptr); - return strm->request().impl().call_on_read(buf, length, data_flags); - }; - prdptr = &prd; - } - - auto stream_id = nghttp2_submit_request(session_, nullptr, nva.data(), - nva.size(), prdptr, strm.get()); - if (stream_id < 0) { - ec = make_error_code(static_cast(stream_id)); - return nullptr; - } - - signal_write(); - - strm->stream_id(stream_id); - - auto p = streams_.emplace(stream_id, std::move(strm)); - assert(p.second); - return &(*p.first).second->request(); -} - -void session_impl::shutdown() { - nghttp2_session_terminate_session(session_, NGHTTP2_NO_ERROR); - signal_write(); -} - -boost::asio::io_service &session_impl::io_service() { return io_service_; } - -void session_impl::signal_write() { - if (!inside_callback_) { - do_write(); - } -} - -bool session_impl::should_stop() const { - return !writing_ && !nghttp2_session_want_read(session_) && - !nghttp2_session_want_write(session_); -} - -namespace { -struct callback_guard { - callback_guard(session_impl &sess) : sess(sess) { sess.enter_callback(); } - ~callback_guard() { sess.leave_callback(); } - - session_impl &sess; -}; -} // namespace - -void session_impl::enter_callback() { - assert(!inside_callback_); - inside_callback_ = true; -} - -void session_impl::leave_callback() { - assert(inside_callback_); - inside_callback_ = false; -} - -void session_impl::do_read() { - read_socket([this](const boost::system::error_code &ec, - std::size_t bytes_transferred) { - if (ec) { - if (ec.value() == boost::asio::error::operation_aborted) { - call_error_cb(ec); - shutdown_socket(); - } - return; - } - - { - callback_guard cg(*this); - - auto rv = - nghttp2_session_mem_recv(session_, rb_.data(), bytes_transferred); - - if (rv != static_cast(bytes_transferred)) { - call_error_cb(make_error_code( - static_cast(rv < 0 ? rv : NGHTTP2_ERR_PROTO))); - shutdown_socket(); - return; - } - } - - do_write(); - - if (should_stop()) { - shutdown_socket(); - return; - } - - do_read(); - }); -} - -void session_impl::do_write() { - if (writing_) { - return; - } - - if (data_pending_) { - std::copy_n(data_pending_, data_pendinglen_, std::begin(wb_) + wblen_); - - wblen_ += data_pendinglen_; - - data_pending_ = nullptr; - data_pendinglen_ = 0; - } - - { - callback_guard cg(*this); - - for (;;) { - const uint8_t *data; - auto n = nghttp2_session_mem_send(session_, &data); - if (n < 0) { - call_error_cb(make_error_code(static_cast(n))); - shutdown_socket(); - return; - } - - if (n == 0) { - break; - } - - if (wblen_ + n > wb_.size()) { - data_pending_ = data; - data_pendinglen_ = n; - - break; - } - - std::copy_n(data, n, std::begin(wb_) + wblen_); - - wblen_ += n; - } - } - - if (wblen_ == 0) { - return; - } - - writing_ = true; - - write_socket([this](const boost::system::error_code &ec, std::size_t n) { - if (ec) { - call_error_cb(ec); - shutdown_socket(); - return; - } - - wblen_ = 0; - writing_ = false; - - do_write(); - }); -} - -} // namespace client -} // namespace asio_http2 -} // nghttp2 diff --git a/src/asio_client_session_impl.h b/src/asio_client_session_impl.h deleted file mode 100644 index 88bbad8..0000000 --- a/src/asio_client_session_impl.h +++ /dev/null @@ -1,123 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2015 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#ifndef ASIO_CLIENT_SESSION_IMPL_H -#define ASIO_CLIENT_SESSION_IMPL_H - -#include "nghttp2_config.h" - -#include - -#include - -namespace nghttp2 { -namespace asio_http2 { -namespace client { - -class stream; - -using boost::asio::ip::tcp; - -class session_impl { -public: - session_impl(boost::asio::io_service &io_service); - virtual ~session_impl(); - - void start_resolve(const std::string &host, const std::string &service); - - void connected(tcp::resolver::iterator endpoint_it); - void not_connected(const boost::system::error_code &ec); - - void on_connect(connect_cb cb); - void on_error(error_cb cb); - - const connect_cb &on_connect() const; - const error_cb &on_error() const; - - int write_trailer(stream &strm, header_map h); - - void cancel(stream &strm, uint32_t error_code); - void resume(stream &strm); - - std::unique_ptr create_stream(); - std::unique_ptr pop_stream(int32_t stream_id); - stream *create_push_stream(int32_t stream_id); - stream *find_stream(int32_t stream_id); - - const request *submit(boost::system::error_code &ec, - const std::string &method, const std::string &uri, - generator_cb cb, header_map h); - - virtual void start_connect(tcp::resolver::iterator endpoint_it) = 0; - virtual tcp::socket &socket() = 0; - virtual void read_socket(std::function< - void(const boost::system::error_code &ec, std::size_t n)> h) = 0; - virtual void write_socket(std::function< - void(const boost::system::error_code &ec, std::size_t n)> h) = 0; - virtual void shutdown_socket() = 0; - - void shutdown(); - - boost::asio::io_service &io_service(); - - void signal_write(); - - void enter_callback(); - void leave_callback(); - - void do_read(); - void do_write(); - -protected: - boost::array rb_; - boost::array wb_; - std::size_t wblen_; - -private: - bool should_stop() const; - bool setup_session(); - void call_error_cb(const boost::system::error_code &ec); - - boost::asio::io_service &io_service_; - tcp::resolver resolver_; - - std::map> streams_; - - connect_cb connect_cb_; - error_cb error_cb_; - - nghttp2_session *session_; - - const uint8_t *data_pending_; - std::size_t data_pendinglen_; - - bool writing_; - bool inside_callback_; -}; - -} // namespace client -} // namespace asio_http2 -} // namespace nghttp2 - -#endif // ASIO_CLIENT_SESSION_IMPL_H diff --git a/src/asio_client_session_tcp_impl.cc b/src/asio_client_session_tcp_impl.cc deleted file mode 100644 index 41195ba..0000000 --- a/src/asio_client_session_tcp_impl.cc +++ /dev/null @@ -1,69 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2015 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#include "asio_client_session_tcp_impl.h" - -namespace nghttp2 { -namespace asio_http2 { -namespace client { - -session_tcp_impl::session_tcp_impl(boost::asio::io_service &io_service, - const std::string &host, - const std::string &service) - : session_impl(io_service), socket_(io_service) { - start_resolve(host, service); -} - -session_tcp_impl::~session_tcp_impl() {} - -void session_tcp_impl::start_connect(tcp::resolver::iterator endpoint_it) { - boost::asio::async_connect(socket_, endpoint_it, - [this](const boost::system::error_code &ec, - tcp::resolver::iterator endpoint_it) { - if (ec) { - not_connected(ec); - return; - } - - connected(endpoint_it); - }); -} - -tcp::socket &session_tcp_impl::socket() { return socket_; } - -void session_tcp_impl::read_socket( - std::function h) { - socket_.async_read_some(boost::asio::buffer(rb_), h); -} - -void session_tcp_impl::write_socket( - std::function h) { - boost::asio::async_write(socket_, boost::asio::buffer(wb_, wblen_), h); -} - -void session_tcp_impl::shutdown_socket() { socket_.close(); } - -} // namespace client -} // namespace asio_http2 -} // namespace nghttp2 diff --git a/src/asio_client_session_tcp_impl.h b/src/asio_client_session_tcp_impl.h deleted file mode 100644 index 6138828..0000000 --- a/src/asio_client_session_tcp_impl.h +++ /dev/null @@ -1,60 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2015 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#ifndef ASIO_CLIENT_SESSION_TCP_IMPL_H -#define ASIO_CLIENT_SESSION_TCP_IMPL_H - -#include "asio_client_session_impl.h" - -#include - -namespace nghttp2 { -namespace asio_http2 { -namespace client { - -using boost::asio::ip::tcp; - -class session_tcp_impl : public session_impl { -public: - session_tcp_impl(boost::asio::io_service &io_service, const std::string &host, - const std::string &service); - virtual ~session_tcp_impl(); - - virtual void start_connect(tcp::resolver::iterator endpoint_it); - virtual tcp::socket &socket(); - virtual void read_socket(std::function< - void(const boost::system::error_code &ec, std::size_t n)> h); - virtual void write_socket(std::function< - void(const boost::system::error_code &ec, std::size_t n)> h); - virtual void shutdown_socket(); - -private: - tcp::socket socket_; -}; - -} // namespace client -} // namespace asio_http2 -} // namespace nghttp2 - -#endif // ASIO_CLIENT_SESSION_TCP_IMPL_H diff --git a/src/asio_client_session_tls_impl.cc b/src/asio_client_session_tls_impl.cc deleted file mode 100644 index cde2ab3..0000000 --- a/src/asio_client_session_tls_impl.cc +++ /dev/null @@ -1,85 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2015 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#include "asio_client_session_tls_impl.h" - -namespace nghttp2 { -namespace asio_http2 { -namespace client { - -session_tls_impl::session_tls_impl(boost::asio::io_service &io_service, - boost::asio::ssl::context &tls_ctx, - const std::string &host, - const std::string &service) - : session_impl(io_service), socket_(io_service, tls_ctx) { - // this callback setting is no effect is - // ssl::context::set_verify_mode(boost::asio::ssl::verify_peer) is - // not used, which is what we want. - socket_.set_verify_callback(boost::asio::ssl::rfc2818_verification(host)); - - start_resolve(host, service); -} - -session_tls_impl::~session_tls_impl() {} - -void session_tls_impl::start_connect(tcp::resolver::iterator endpoint_it) { - boost::asio::async_connect(socket(), endpoint_it, - [this](const boost::system::error_code &ec, - tcp::resolver::iterator endpoint_it) { - if (ec) { - not_connected(ec); - return; - } - - socket_.async_handshake( - boost::asio::ssl::stream_base::client, - [this, endpoint_it](const boost::system::error_code &ec) { - if (ec) { - not_connected(ec); - return; - } - connected(endpoint_it); - }); - }); -} - -tcp::socket &session_tls_impl::socket() { return socket_.next_layer(); } - -void session_tls_impl::read_socket( - std::function h) { - socket_.async_read_some(boost::asio::buffer(rb_), h); -} - -void session_tls_impl::write_socket( - std::function h) { - boost::asio::async_write(socket_, boost::asio::buffer(wb_, wblen_), h); -} - -void session_tls_impl::shutdown_socket() { - socket_.async_shutdown([](const boost::system::error_code &ec) {}); -} - -} // namespace client -} // namespace asio_http2 -} // namespace nghttp2 diff --git a/src/asio_client_session_tls_impl.h b/src/asio_client_session_tls_impl.h deleted file mode 100644 index 484e3d4..0000000 --- a/src/asio_client_session_tls_impl.h +++ /dev/null @@ -1,63 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2015 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#ifndef ASIO_CLIENT_SESSION_TLS_IMPL_H -#define ASIO_CLIENT_SESSION_TLS_IMPL_H - -#include "asio_client_session_impl.h" - -#include - -namespace nghttp2 { -namespace asio_http2 { -namespace client { - -using boost::asio::ip::tcp; - -using ssl_socket = boost::asio::ssl::stream; - -class session_tls_impl : public session_impl { -public: - session_tls_impl(boost::asio::io_service &io_service, - boost::asio::ssl::context &tls_ctx, const std::string &host, - const std::string &service); - virtual ~session_tls_impl(); - - virtual void start_connect(tcp::resolver::iterator endpoint_it); - virtual tcp::socket &socket(); - virtual void read_socket(std::function< - void(const boost::system::error_code &ec, std::size_t n)> h); - virtual void write_socket(std::function< - void(const boost::system::error_code &ec, std::size_t n)> h); - virtual void shutdown_socket(); - -private: - ssl_socket socket_; -}; - -} // namespace client -} // namespace asio_http2 -} // namespace nghttp2 - -#endif // ASIO_CLIENT_SESSION_TLS_IMPL_H diff --git a/src/asio_client_stream.cc b/src/asio_client_stream.cc deleted file mode 100644 index 2d3aa5b..0000000 --- a/src/asio_client_stream.cc +++ /dev/null @@ -1,59 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2015 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#include "asio_client_stream.h" - -#include "asio_client_request_impl.h" -#include "asio_client_response_impl.h" -#include "asio_client_session_impl.h" - -namespace nghttp2 { -namespace asio_http2 { -namespace client { - -stream::stream(session_impl *sess) : sess_(sess), stream_id_(0) { - request_.impl().stream(this); -} - -void stream::stream_id(int32_t stream_id) { stream_id_ = stream_id; } - -int32_t stream::stream_id() const { return stream_id_; } - -class request &stream::request() { - return request_; -} - -class response &stream::response() { - return response_; -} - -session_impl *stream::session() const { return sess_; } - -bool stream::expect_final_response() const { - return response_.status_code() / 100 == 1; -} - -} // namespace client -} // namespace asio_http2 -} // namespace nghttp2 diff --git a/src/asio_client_stream.h b/src/asio_client_stream.h deleted file mode 100644 index e3e027a..0000000 --- a/src/asio_client_stream.h +++ /dev/null @@ -1,68 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2015 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#ifndef ASIO_CLIENT_STREAM_H -#define ASIO_CLIENT_STREAM_H - -#include "nghttp2_config.h" - -#include - -namespace nghttp2 { -namespace asio_http2 { -namespace client { - -class request; -class response; -class session_impl; - -class stream { -public: - stream(session_impl *sess); - - stream(const stream &) = delete; - stream &operator=(const stream &) = delete; - - void stream_id(int32_t stream_id); - int32_t stream_id() const; - - class request &request(); - class response &response(); - - session_impl *session() const; - - bool expect_final_response() const; - -private: - nghttp2::asio_http2::client::request request_; - nghttp2::asio_http2::client::response response_; - session_impl *sess_; - uint32_t stream_id_; -}; - -} // namespace client -} // namespace asio_http2 -} // namespace nghttp2 - -#endif // ASIO_CLIENT_STREAM_H diff --git a/src/asio_client_tls_context.cc b/src/asio_client_tls_context.cc deleted file mode 100644 index 5513834..0000000 --- a/src/asio_client_tls_context.cc +++ /dev/null @@ -1,64 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2015 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#include "asio_client_tls_context.h" - -#include - -#include - -#include "ssl.h" -#include "util.h" - -namespace nghttp2 { -namespace asio_http2 { -namespace client { - -namespace { -int client_select_next_proto_cb(SSL *ssl, unsigned char **out, - unsigned char *outlen, const unsigned char *in, - unsigned int inlen, void *arg) { - if (!util::select_h2(const_cast(out), outlen, in, - inlen)) { - return SSL_TLSEXT_ERR_NOACK; - } - return SSL_TLSEXT_ERR_OK; -} -} // namespace - -boost::system::error_code -configure_tls_context(boost::system::error_code &ec, - boost::asio::ssl::context &tls_ctx) { - ec.clear(); - - auto ctx = tls_ctx.native_handle(); - - SSL_CTX_set_next_proto_select_cb(ctx, client_select_next_proto_cb, nullptr); - - return ec; -} - -} // namespace client -} // namespace asio_http2 -} // namespace nghttp2 diff --git a/src/asio_client_tls_context.h b/src/asio_client_tls_context.h deleted file mode 100644 index 6287dab..0000000 --- a/src/asio_client_tls_context.h +++ /dev/null @@ -1,32 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2015 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#ifndef ASIO_CLIENT_TLS_CONTEXT_H -#define ASIO_CLIENT_TLS_CONTEXT_H - -#include "nghttp2_config.h" - -#include - -#endif // ASIO_CLIENT_TLS_CONTEXT_H diff --git a/src/asio_common.cc b/src/asio_common.cc deleted file mode 100644 index c3f98e7..0000000 --- a/src/asio_common.cc +++ /dev/null @@ -1,148 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2015 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#include "asio_common.h" - -#include - -#include "util.h" -#include "template.h" -#include "http2.h" - -namespace nghttp2 { -namespace asio_http2 { - -class nghttp2_category_impl : public boost::system::error_category { -public: - const char *name() const noexcept { return "nghttp2"; } - std::string message(int ev) const { return nghttp2_strerror(ev); } -}; - -const boost::system::error_category &nghttp2_category() noexcept { - static nghttp2_category_impl cat; - return cat; -} - -boost::system::error_code make_error_code(nghttp2_error ev) { - return boost::system::error_code(static_cast(ev), nghttp2_category()); -} - -generator_cb string_generator(std::string data) { - auto strio = std::make_shared>(std::move(data), - data.size()); - return [strio](uint8_t *buf, size_t len, uint32_t *data_flags) { - auto &data = strio->first; - auto &left = strio->second; - auto n = std::min(len, left); - std::copy_n(data.c_str() + data.size() - left, n, buf); - left -= n; - if (left == 0) { - *data_flags |= NGHTTP2_DATA_FLAG_EOF; - } - return n; - }; -} - -generator_cb deferred_generator() { - return [](uint8_t *buf, size_t len, - uint32_t *data_flags) { return NGHTTP2_ERR_DEFERRED; }; -} - -template -std::shared_ptr> defer_shared(F &&f, T &&... t) { - return std::make_shared>(std::forward(f), - std::forward(t)...); -} - -generator_cb file_generator(const std::string &path) { - auto fd = open(path.c_str(), O_RDONLY); - if (fd == -1) { - return generator_cb(); - } - - return file_generator_from_fd(fd); -} - -generator_cb file_generator_from_fd(int fd) { - auto d = defer_shared(close, fd); - - return [fd, d](uint8_t *buf, size_t len, uint32_t *data_flags) - -> generator_cb::result_type { - ssize_t n; - while ((n = read(fd, buf, len)) == -1 && errno == EINTR) - ; - - if (n == -1) { - return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; - } - - if (n == 0) { - *data_flags |= NGHTTP2_DATA_FLAG_EOF; - } - - return n; - }; -} - -bool check_path(const std::string &path) { return util::check_path(path); } - -std::string percent_decode(const std::string &s) { - return util::percentDecode(std::begin(s), std::end(s)); -} - -std::string http_date(int64_t t) { return util::http_date(t); } - -boost::system::error_code host_service_from_uri(boost::system::error_code &ec, - std::string &scheme, - std::string &host, - std::string &service, - const std::string &uri) { - ec.clear(); - - http_parser_url u{}; - if (http_parser_parse_url(uri.c_str(), uri.size(), 0, &u) != 0) { - ec = make_error_code(boost::system::errc::invalid_argument); - return ec; - } - - if ((u.field_set & (1 << UF_SCHEMA)) == 0 || - (u.field_set & (1 << UF_HOST)) == 0) { - ec = make_error_code(boost::system::errc::invalid_argument); - return ec; - } - - http2::copy_url_component(scheme, &u, UF_SCHEMA, uri.c_str()); - http2::copy_url_component(host, &u, UF_HOST, uri.c_str()); - - if (u.field_set & (1 << UF_PORT)) { - http2::copy_url_component(service, &u, UF_PORT, uri.c_str()); - } else { - service = scheme; - } - - return ec; -} - -} // namespace asio_http2 -} // namespace nghttp2 diff --git a/src/asio_common.h b/src/asio_common.h deleted file mode 100644 index 8f59a0d..0000000 --- a/src/asio_common.h +++ /dev/null @@ -1,65 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2015 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#ifndef ASIO_COMMON_H -#define ASIO_COMMON_H - -#include "nghttp2_config.h" - -#include - -#include - -#include "util.h" - -namespace nghttp2 { - -namespace asio_http2 { - -boost::system::error_code make_error_code(nghttp2_error ev); - -generator_cb string_generator(std::string data); - -// Returns generator_cb, which just returns NGHTTP2_ERR_DEFERRED -generator_cb deferred_generator(); - -template -void split_path(uri_ref &dst, InputIt first, InputIt last) { - auto path_last = std::find(first, last, '?'); - InputIt query_first; - if (path_last == last) { - query_first = path_last = last; - } else { - query_first = path_last + 1; - } - dst.path = util::percentDecode(first, path_last); - dst.raw_path.assign(first, path_last); - dst.raw_query.assign(query_first, last); -} - -} // namespace asio_http2 - -} // namespace nghttp2 - -#endif // ASIO_COMMON_H diff --git a/src/asio_io_service_pool.cc b/src/asio_io_service_pool.cc deleted file mode 100644 index ba40541..0000000 --- a/src/asio_io_service_pool.cc +++ /dev/null @@ -1,97 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2014 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -// We wrote this code based on the original code which has the -// following license: -// -// io_service_pool.cpp -// ~~~~~~~~~~~~~~~~~~~ -// -// Copyright (c) 2003-2013 Christopher M. Kohlhoff (chris at kohlhoff dot com) -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// -#include "asio_io_service_pool.h" - -namespace nghttp2 { - -namespace asio_http2 { - -io_service_pool::io_service_pool(std::size_t pool_size) : next_io_service_(0) { - if (pool_size == 0) { - throw std::runtime_error("io_service_pool size is 0"); - } - - // Give all the io_services work to do so that their run() functions will not - // exit until they are explicitly stopped. - for (std::size_t i = 0; i < pool_size; ++i) { - auto io_service = std::make_shared(); - auto work = std::make_shared(*io_service); - io_services_.push_back(io_service); - work_.push_back(work); - } -} - -void io_service_pool::run(bool asynchronous) { - // Create a pool of threads to run all of the io_services. - for (std::size_t i = 0; i < io_services_.size(); ++i) { - futures_.push_back(std::async(std::launch::async, - (size_t (boost::asio::io_service::*)(void)) & - boost::asio::io_service::run, - io_services_[i])); - } - - if (!asynchronous) { - join(); - } -} - -void io_service_pool::join() { - // Wait for all threads in the pool to exit. - for (auto &fut : futures_) { - fut.get(); - } -} - -void io_service_pool::stop() { - // Explicitly stop all io_services. - for (auto &iosv : io_services_) { - iosv->stop(); - } -} - -boost::asio::io_service &io_service_pool::get_io_service() { - // Use a round-robin scheme to choose the next io_service to use. - auto &io_service = *io_services_[next_io_service_]; - ++next_io_service_; - if (next_io_service_ == io_services_.size()) { - next_io_service_ = 0; - } - return io_service; -} - -} // namespace asio_http2 - -} // namespace nghttp2 diff --git a/src/asio_io_service_pool.h b/src/asio_io_service_pool.h deleted file mode 100644 index 242cac8..0000000 --- a/src/asio_io_service_pool.h +++ /dev/null @@ -1,91 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2014 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -// We wrote this code based on the original code which has the -// following license: -// -// io_service_pool.hpp -// ~~~~~~~~~~~~~~~~~~~ -// -// Copyright (c) 2003-2013 Christopher M. Kohlhoff (chris at kohlhoff dot com) -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// - -#ifndef ASIO_IO_SERVICE_POOL_H -#define ASIO_IO_SERVICE_POOL_H - -#include "nghttp2_config.h" - -#include -#include -#include - -#include -#include - -#include - -namespace nghttp2 { - -namespace asio_http2 { - -/// A pool of io_service objects. -class io_service_pool : private boost::noncopyable { -public: - /// Construct the io_service pool. - explicit io_service_pool(std::size_t pool_size); - - /// Run all io_service objects in the pool. - void run(bool asynchronous = false); - - /// Stop all io_service objects in the pool. - void stop(); - - /// Join on all io_service objects in the pool. - void join(); - - /// Get an io_service to use. - boost::asio::io_service &get_io_service(); - -private: - /// The pool of io_services. - std::vector> io_services_; - - /// The work that keeps the io_services running. - std::vector> work_; - - /// The next io_service to use for a connection. - std::size_t next_io_service_; - - /// Futures to all the io_service objects - std::vector> futures_; -}; - -} // namespace asio_http2 - -} // namespace nghttp2 - -#endif // ASIO_IO_SERVICE_POOL_H diff --git a/src/asio_server.cc b/src/asio_server.cc deleted file mode 100644 index 310e672..0000000 --- a/src/asio_server.cc +++ /dev/null @@ -1,165 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2014 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -// We wrote this code based on the original code which has the -// following license: -// -// server.cpp -// ~~~~~~~~~~ -// -// Copyright (c) 2003-2013 Christopher M. Kohlhoff (chris at kohlhoff dot com) -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// - -#include "asio_server.h" - -#include "asio_server_connection.h" -#include "util.h" - -namespace nghttp2 { -namespace asio_http2 { -namespace server { - -server::server(std::size_t io_service_pool_size) - : io_service_pool_(io_service_pool_size) {} - -boost::system::error_code -server::listen_and_serve(boost::system::error_code &ec, - boost::asio::ssl::context *tls_context, - const std::string &address, const std::string &port, - int backlog, serve_mux &mux, bool asynchronous) { - ec.clear(); - - if (bind_and_listen(ec, address, port, backlog)) { - return ec; - } - - for (auto &acceptor : acceptors_) { - if (tls_context) { - start_accept(*tls_context, acceptor, mux); - } else { - start_accept(acceptor, mux); - } - } - - io_service_pool_.run(asynchronous); - - return ec; -} - -boost::system::error_code server::bind_and_listen(boost::system::error_code &ec, - const std::string &address, - const std::string &port, - int backlog) { - // Open the acceptor with the option to reuse the address (i.e. - // SO_REUSEADDR). - tcp::resolver resolver(io_service_pool_.get_io_service()); - tcp::resolver::query query(address, port); - auto it = resolver.resolve(query, ec); - if (ec) { - return ec; - } - - for (; it != tcp::resolver::iterator(); ++it) { - tcp::endpoint endpoint = *it; - auto acceptor = tcp::acceptor(io_service_pool_.get_io_service()); - - if (acceptor.open(endpoint.protocol(), ec)) { - continue; - } - - acceptor.set_option(tcp::acceptor::reuse_address(true)); - - if (acceptor.bind(endpoint, ec)) { - continue; - } - - if (acceptor.listen( - backlog == -1 ? boost::asio::socket_base::max_connections : backlog, - ec)) { - continue; - } - - acceptors_.push_back(std::move(acceptor)); - } - - if (acceptors_.empty()) { - return ec; - } - - // ec could have some errors since we may have failed to bind some - // interfaces. - ec.clear(); - - return ec; -} - -void server::start_accept(boost::asio::ssl::context &tls_context, - tcp::acceptor &acceptor, serve_mux &mux) { - auto new_connection = std::make_shared>( - mux, io_service_pool_.get_io_service(), tls_context); - - acceptor.async_accept(new_connection->socket().lowest_layer(), - [this, &tls_context, &acceptor, &mux, new_connection]( - const boost::system::error_code &e) { - if (!e) { - new_connection->socket().lowest_layer().set_option(tcp::no_delay(true)); - new_connection->socket().async_handshake( - boost::asio::ssl::stream_base::server, - [new_connection](const boost::system::error_code &e) { - if (!e) { - new_connection->start(); - } - }); - } - - start_accept(tls_context, acceptor, mux); - }); -} - -void server::start_accept(tcp::acceptor &acceptor, serve_mux &mux) { - auto new_connection = std::make_shared>( - mux, io_service_pool_.get_io_service()); - - acceptor.async_accept(new_connection->socket(), - [this, &acceptor, &mux, new_connection]( - const boost::system::error_code &e) { - if (!e) { - new_connection->socket().set_option(tcp::no_delay(true)); - new_connection->start(); - } - - start_accept(acceptor, mux); - }); -} - -void server::stop() { io_service_pool_.stop(); } - -void server::join() { io_service_pool_.join(); } - -} // namespace server -} // namespace asio_http2 -} // namespace nghttp2 diff --git a/src/asio_server.h b/src/asio_server.h deleted file mode 100644 index 7f60120..0000000 --- a/src/asio_server.h +++ /dev/null @@ -1,105 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2014 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -// We wrote this code based on the original code which has the -// following license: -// -// server.hpp -// ~~~~~~~~~~ -// -// Copyright (c) 2003-2013 Christopher M. Kohlhoff (chris at kohlhoff dot com) -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// - -#ifndef ASIO_SERVER_H -#define ASIO_SERVER_H - -#include "nghttp2_config.h" - -#include -#include -#include - -#include - -#include - -#include "asio_io_service_pool.h" - -namespace nghttp2 { - -namespace asio_http2 { - -namespace server { - -class serve_mux; - -using boost::asio::ip::tcp; - -using ssl_socket = boost::asio::ssl::stream; - -class server : private boost::noncopyable { -public: - explicit server(std::size_t io_service_pool_size); - - boost::system::error_code - listen_and_serve(boost::system::error_code &ec, - boost::asio::ssl::context *tls_context, - const std::string &address, const std::string &port, - int backlog, serve_mux &mux, bool asynchronous = false); - void join(); - void stop(); - -private: - /// Initiate an asynchronous accept operation. - void start_accept(tcp::acceptor &acceptor, serve_mux &mux); - /// Same as above but with tls_context - void start_accept(boost::asio::ssl::context &tls_context, - tcp::acceptor &acceptor, serve_mux &mux); - - /// Resolves address and bind socket to the resolved addresses. - boost::system::error_code bind_and_listen(boost::system::error_code &ec, - const std::string &address, - const std::string &port, - int backlog); - - /// The pool of io_service objects used to perform asynchronous - /// operations. - io_service_pool io_service_pool_; - - /// Acceptor used to listen for incoming connections. - std::vector acceptors_; - - std::unique_ptr ssl_ctx_; -}; - -} // namespace server - -} // namespace asio_http2 - -} // namespace nghttp2 - -#endif // ASIO_SERVER_H diff --git a/src/asio_server_connection.h b/src/asio_server_connection.h deleted file mode 100644 index 5ce48d9..0000000 --- a/src/asio_server_connection.h +++ /dev/null @@ -1,169 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2014 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -// We wrote this code based on the original code which has the -// following license: -// -// connection.hpp -// ~~~~~~~~~~~~~~ -// -// Copyright (c) 2003-2013 Christopher M. Kohlhoff (chris at kohlhoff dot com) -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// - -#ifndef ASIO_SERVER_CONNECTION_H -#define ASIO_SERVER_CONNECTION_H - -#include "nghttp2_config.h" - -#include - -#include -#include - -#include - -#include "asio_server_http2_handler.h" -#include "asio_server_serve_mux.h" -#include "util.h" - -namespace nghttp2 { - -namespace asio_http2 { - -namespace server { - -/// Represents a single connection from a client. -template -class connection : public std::enable_shared_from_this>, - private boost::noncopyable { -public: - /// Construct a connection with the given io_service. - template - explicit connection(serve_mux &mux, SocketArgs &&... args) - : socket_(std::forward(args)...), mux_(mux), writing_(false) { - } - - /// Start the first asynchronous operation for the connection. - void start() { - handler_ = std::make_shared(socket_.get_io_service(), - [this]() { do_write(); }, mux_); - if (handler_->start() != 0) { - return; - } - do_read(); - } - - socket_type &socket() { return socket_; } - - void do_read() { - auto self = this->shared_from_this(); - - socket_.async_read_some(boost::asio::buffer(buffer_), - [this, self](const boost::system::error_code &e, - std::size_t bytes_transferred) { - if (!e) { - if (handler_->on_read(buffer_, bytes_transferred) != 0) { - return; - } - - do_write(); - - if (!writing_ && handler_->should_stop()) { - return; - } - - do_read(); - } - - // If an error occurs then no new asynchronous operations are - // started. This means that all shared_ptr references to the - // connection object will disappear and the object will be - // destroyed automatically after this handler returns. The - // connection class's destructor closes the socket. - }); - } - - void do_write() { - auto self = this->shared_from_this(); - - if (writing_) { - return; - } - - int rv; - std::size_t nwrite; - - rv = handler_->on_write(outbuf_, nwrite); - - if (rv != 0) { - return; - } - - if (nwrite == 0) { - return; - } - - writing_ = true; - - boost::asio::async_write( - socket_, boost::asio::buffer(outbuf_, nwrite), - [this, self](const boost::system::error_code &e, std::size_t) { - if (!e) { - writing_ = false; - - do_write(); - } - }); - - // No new asynchronous operations are started. This means that all - // shared_ptr references to the connection object will disappear and - // the object will be destroyed automatically after this handler - // returns. The connection class's destructor closes the socket. - } - -private: - socket_type socket_; - - serve_mux &mux_; - - std::shared_ptr handler_; - - /// Buffer for incoming data. - boost::array buffer_; - - boost::array outbuf_; - - bool writing_; -}; - -} // namespace server - -} // namespace asio_http2 - -} // namespace nghttp2 - -#endif // ASIO_SERVER_CONNECTION_H diff --git a/src/asio_server_http2.cc b/src/asio_server_http2.cc deleted file mode 100644 index 7d162e3..0000000 --- a/src/asio_server_http2.cc +++ /dev/null @@ -1,84 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2014 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#include "nghttp2_config.h" - -#include - -#include "asio_server_http2_impl.h" -#include "asio_server.h" -#include "template.h" - -namespace nghttp2 { - -namespace asio_http2 { - -namespace server { - -http2::http2() : impl_(make_unique()) {} - -http2::~http2() {} - -http2::http2(http2 &&other) noexcept : impl_(std::move(other.impl_)) {} - -http2 &http2::operator=(http2 &&other) noexcept { - if (this == &other) { - return *this; - } - - impl_ = std::move(other.impl_); - - return *this; -} - -boost::system::error_code http2::listen_and_serve(boost::system::error_code &ec, - const std::string &address, - const std::string &port, - bool asynchronous) { - return impl_->listen_and_serve(ec, nullptr, address, port, asynchronous); -} - -boost::system::error_code http2::listen_and_serve( - boost::system::error_code &ec, boost::asio::ssl::context &tls_context, - const std::string &address, const std::string &port, bool asynchronous) { - return impl_->listen_and_serve(ec, &tls_context, address, port, asynchronous); -} - -void http2::num_threads(size_t num_threads) { impl_->num_threads(num_threads); } - -void http2::backlog(int backlog) { impl_->backlog(backlog); } - -bool http2::handle(std::string pattern, request_cb cb) { - return impl_->handle(std::move(pattern), std::move(cb)); -} - -void http2::stop() { impl_->stop(); } - -void http2::join() { return impl_->join(); } - -} // namespace server - -} // namespace asio_http2 - -} // namespace nghttp2 diff --git a/src/asio_server_http2_handler.cc b/src/asio_server_http2_handler.cc deleted file mode 100644 index c55cb32..0000000 --- a/src/asio_server_http2_handler.cc +++ /dev/null @@ -1,462 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2014 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#include "asio_server_http2_handler.h" - -#include - -#include "asio_common.h" -#include "asio_server_serve_mux.h" -#include "asio_server_stream.h" -#include "asio_server_request_impl.h" -#include "asio_server_response_impl.h" -#include "http2.h" -#include "util.h" -#include "template.h" - -namespace nghttp2 { - -namespace asio_http2 { - -namespace server { - -namespace { -int stream_error(nghttp2_session *session, int32_t stream_id, - uint32_t error_code) { - return nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, stream_id, - error_code); -} -} // namespace - -namespace { -int on_begin_headers_callback(nghttp2_session *session, - const nghttp2_frame *frame, void *user_data) { - auto handler = static_cast(user_data); - - if (frame->hd.type != NGHTTP2_HEADERS || - frame->headers.cat != NGHTTP2_HCAT_REQUEST) { - return 0; - } - - handler->create_stream(frame->hd.stream_id); - - return 0; -} -} // namespace - -namespace { -int on_header_callback(nghttp2_session *session, const nghttp2_frame *frame, - const uint8_t *name, size_t namelen, - const uint8_t *value, size_t valuelen, uint8_t flags, - void *user_data) { - auto handler = static_cast(user_data); - auto stream_id = frame->hd.stream_id; - - if (frame->hd.type != NGHTTP2_HEADERS || - frame->headers.cat != NGHTTP2_HCAT_REQUEST) { - return 0; - } - - auto strm = handler->find_stream(stream_id); - if (!strm) { - return 0; - } - - auto &req = strm->request().impl(); - auto &uref = req.uri(); - - switch (nghttp2::http2::lookup_token(name, namelen)) { - case nghttp2::http2::HD__METHOD: - req.method(std::string(value, value + valuelen)); - break; - case nghttp2::http2::HD__SCHEME: - uref.scheme.assign(value, value + valuelen); - break; - case nghttp2::http2::HD__AUTHORITY: - uref.host.assign(value, value + valuelen); - break; - case nghttp2::http2::HD__PATH: - split_path(uref, value, value + valuelen); - break; - case nghttp2::http2::HD_HOST: - if (uref.host.empty()) { - uref.host.assign(value, value + valuelen); - } - // fall through - default: - req.header().emplace(std::string(name, name + namelen), - header_value{std::string(value, value + valuelen), - (flags & NGHTTP2_NV_FLAG_NO_INDEX) != 0}); - } - - return 0; -} -} // namespace - -namespace { -int on_frame_recv_callback(nghttp2_session *session, const nghttp2_frame *frame, - void *user_data) { - auto handler = static_cast(user_data); - auto strm = handler->find_stream(frame->hd.stream_id); - - switch (frame->hd.type) { - case NGHTTP2_DATA: - if (!strm) { - break; - } - - if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) { - strm->request().impl().call_on_data(nullptr, 0); - } - - break; - case NGHTTP2_HEADERS: { - if (!strm || frame->headers.cat != NGHTTP2_HCAT_REQUEST) { - break; - } - - handler->call_on_request(*strm); - - if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) { - strm->request().impl().call_on_data(nullptr, 0); - } - - break; - } - } - - return 0; -} -} // namespace - -namespace { -int on_data_chunk_recv_callback(nghttp2_session *session, uint8_t flags, - int32_t stream_id, const uint8_t *data, - size_t len, void *user_data) { - auto handler = static_cast(user_data); - auto strm = handler->find_stream(stream_id); - - if (!strm) { - return 0; - } - - strm->request().impl().call_on_data(data, len); - - return 0; -} - -} // namespace - -namespace { -int on_stream_close_callback(nghttp2_session *session, int32_t stream_id, - uint32_t error_code, void *user_data) { - auto handler = static_cast(user_data); - - auto strm = handler->find_stream(stream_id); - if (!strm) { - return 0; - } - - strm->response().impl().call_on_close(error_code); - - handler->close_stream(stream_id); - - return 0; -} -} // namespace - -namespace { -int on_frame_send_callback(nghttp2_session *session, const nghttp2_frame *frame, - void *user_data) { - auto handler = static_cast(user_data); - - if (frame->hd.type != NGHTTP2_PUSH_PROMISE) { - return 0; - } - - auto strm = handler->find_stream(frame->push_promise.promised_stream_id); - - if (!strm) { - return 0; - } - - auto &res = strm->response().impl(); - res.push_promise_sent(); - - return 0; -} -} // namespace - -namespace { -int on_frame_not_send_callback(nghttp2_session *session, - const nghttp2_frame *frame, int lib_error_code, - void *user_data) { - if (frame->hd.type != NGHTTP2_HEADERS) { - return 0; - } - - // Issue RST_STREAM so that stream does not hang around. - nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, frame->hd.stream_id, - NGHTTP2_INTERNAL_ERROR); - - return 0; -} -} // namespace - -http2_handler::http2_handler(boost::asio::io_service &io_service, - connection_write writefun, serve_mux &mux) - : writefun_(writefun), mux_(mux), io_service_(io_service), - session_(nullptr), buf_(nullptr), buflen_(0), inside_callback_(false), - tstamp_cached_(time(nullptr)), - formatted_date_(util::http_date(tstamp_cached_)) {} - -http2_handler::~http2_handler() { nghttp2_session_del(session_); } - -const std::string &http2_handler::http_date() { - auto t = time(nullptr); - if (t != tstamp_cached_) { - tstamp_cached_ = t; - formatted_date_ = util::http_date(t); - } - return formatted_date_; -} - -int http2_handler::start() { - int rv; - - nghttp2_session_callbacks *callbacks; - rv = nghttp2_session_callbacks_new(&callbacks); - if (rv != 0) { - return -1; - } - - auto cb_del = defer(nghttp2_session_callbacks_del, callbacks); - - nghttp2_session_callbacks_set_on_begin_headers_callback( - callbacks, on_begin_headers_callback); - nghttp2_session_callbacks_set_on_header_callback(callbacks, - on_header_callback); - nghttp2_session_callbacks_set_on_frame_recv_callback(callbacks, - on_frame_recv_callback); - nghttp2_session_callbacks_set_on_data_chunk_recv_callback( - callbacks, on_data_chunk_recv_callback); - nghttp2_session_callbacks_set_on_stream_close_callback( - callbacks, on_stream_close_callback); - nghttp2_session_callbacks_set_on_frame_send_callback(callbacks, - on_frame_send_callback); - nghttp2_session_callbacks_set_on_frame_not_send_callback( - callbacks, on_frame_not_send_callback); - - rv = nghttp2_session_server_new(&session_, callbacks, this); - if (rv != 0) { - return -1; - } - - nghttp2_settings_entry ent{NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100}; - nghttp2_submit_settings(session_, NGHTTP2_FLAG_NONE, &ent, 1); - - return 0; -} - -stream *http2_handler::create_stream(int32_t stream_id) { - auto p = streams_.emplace(stream_id, make_unique(this, stream_id)); - assert(p.second); - return (*p.first).second.get(); -} - -void http2_handler::close_stream(int32_t stream_id) { - streams_.erase(stream_id); -} - -stream *http2_handler::find_stream(int32_t stream_id) { - auto i = streams_.find(stream_id); - if (i == std::end(streams_)) { - return nullptr; - } - - return (*i).second.get(); -} - -void http2_handler::call_on_request(stream &strm) { - auto cb = mux_.handler(strm.request().impl()); - cb(strm.request(), strm.response()); -} - -bool http2_handler::should_stop() const { - return !nghttp2_session_want_read(session_) && - !nghttp2_session_want_write(session_); -} - -int http2_handler::start_response(stream &strm) { - int rv; - - auto &res = strm.response().impl(); - auto &header = res.header(); - auto nva = std::vector(); - nva.reserve(2 + header.size()); - auto status = util::utos(res.status_code()); - auto date = http_date(); - nva.push_back(nghttp2::http2::make_nv_ls(":status", status)); - nva.push_back(nghttp2::http2::make_nv_ls("date", date)); - for (auto &hd : header) { - nva.push_back(nghttp2::http2::make_nv(hd.first, hd.second.value, - hd.second.sensitive)); - } - - nghttp2_data_provider *prd_ptr = nullptr, prd; - auto &req = strm.request().impl(); - if (::nghttp2::http2::expect_response_body(req.method(), res.status_code())) { - prd.source.ptr = &strm; - prd.read_callback = - [](nghttp2_session *session, int32_t stream_id, uint8_t *buf, - size_t length, uint32_t *data_flags, nghttp2_data_source *source, - void *user_data) -> ssize_t { - auto &strm = *static_cast(source->ptr); - return strm.response().impl().call_read(buf, length, data_flags); - }; - prd_ptr = &prd; - } - rv = nghttp2_submit_response(session_, strm.get_stream_id(), nva.data(), - nva.size(), prd_ptr); - - if (rv != 0) { - return -1; - } - - signal_write(); - - return 0; -} - -int http2_handler::submit_trailer(stream &strm, header_map h) { - int rv; - auto nva = std::vector(); - nva.reserve(h.size()); - for (auto &hd : h) { - nva.push_back(nghttp2::http2::make_nv(hd.first, hd.second.value, - hd.second.sensitive)); - } - - rv = nghttp2_submit_trailer(session_, strm.get_stream_id(), nva.data(), - nva.size()); - - if (rv != 0) { - return -1; - } - - signal_write(); - - return 0; -} - -void http2_handler::enter_callback() { - assert(!inside_callback_); - inside_callback_ = true; -} - -void http2_handler::leave_callback() { - assert(inside_callback_); - inside_callback_ = false; -} - -void http2_handler::stream_error(int32_t stream_id, uint32_t error_code) { - ::nghttp2::asio_http2::server::stream_error(session_, stream_id, error_code); - signal_write(); -} - -void http2_handler::signal_write() { - if (!inside_callback_) { - initiate_write(); - } -} - -void http2_handler::initiate_write() { writefun_(); } - -void http2_handler::resume(stream &strm) { - nghttp2_session_resume_data(session_, strm.get_stream_id()); - signal_write(); -} - -response *http2_handler::push_promise(boost::system::error_code &ec, - stream &strm, std::string method, - std::string raw_path_query, - header_map h) { - int rv; - - ec.clear(); - - auto &req = strm.request().impl(); - - auto nva = std::vector(); - nva.reserve(4 + h.size()); - nva.push_back(nghttp2::http2::make_nv_ls(":method", method)); - nva.push_back(nghttp2::http2::make_nv_ls(":scheme", req.uri().scheme)); - nva.push_back(nghttp2::http2::make_nv_ls(":authority", req.uri().host)); - nva.push_back(nghttp2::http2::make_nv_ls(":path", raw_path_query)); - - for (auto &hd : h) { - nva.push_back(nghttp2::http2::make_nv(hd.first, hd.second.value, - hd.second.sensitive)); - } - - rv = nghttp2_submit_push_promise(session_, NGHTTP2_FLAG_NONE, - strm.get_stream_id(), nva.data(), nva.size(), - nullptr); - - if (rv < 0) { - ec = make_error_code(static_cast(rv)); - return nullptr; - } - - auto promised_strm = create_stream(rv); - auto &promised_req = promised_strm->request().impl(); - promised_req.header(std::move(h)); - promised_req.method(std::move(method)); - - auto &uref = promised_req.uri(); - uref.scheme = req.uri().scheme; - uref.host = req.uri().host; - split_path(uref, std::begin(raw_path_query), std::end(raw_path_query)); - - auto &promised_res = promised_strm->response().impl(); - promised_res.pushed(true); - - signal_write(); - - return &promised_strm->response(); -} - -boost::asio::io_service &http2_handler::io_service() { return io_service_; } - -callback_guard::callback_guard(http2_handler &h) : handler(h) { - handler.enter_callback(); -} - -callback_guard::~callback_guard() { handler.leave_callback(); } - -} // namespace server - -} // namespace asio_http2 - -} // namespace nghttp2 diff --git a/src/asio_server_http2_handler.h b/src/asio_server_http2_handler.h deleted file mode 100644 index 068d452..0000000 --- a/src/asio_server_http2_handler.h +++ /dev/null @@ -1,167 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2014 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#ifndef ASIO_SERVER_HTTP2_HANDLER_H -#define ASIO_SERVER_HTTP2_HANDLER_H - -#include "nghttp2_config.h" - -#include -#include -#include - -#include - -#include - -namespace nghttp2 { -namespace asio_http2 { -namespace server { - -class http2_handler; -class stream; -class serve_mux; - -struct callback_guard { - callback_guard(http2_handler &h); - ~callback_guard(); - http2_handler &handler; -}; - -using connection_write = std::function; - -class http2_handler : public std::enable_shared_from_this { -public: - http2_handler(boost::asio::io_service &io_service, connection_write writefun, - serve_mux &mux); - - ~http2_handler(); - - int start(); - - stream *create_stream(int32_t stream_id); - void close_stream(int32_t stream_id); - stream *find_stream(int32_t stream_id); - - void call_on_request(stream &s); - - bool should_stop() const; - - int start_response(stream &s); - - int submit_trailer(stream &s, header_map h); - - void stream_error(int32_t stream_id, uint32_t error_code); - - void initiate_write(); - - void enter_callback(); - void leave_callback(); - - void resume(stream &s); - - response *push_promise(boost::system::error_code &ec, stream &s, - std::string method, std::string raw_path_query, - header_map h); - - void signal_write(); - - boost::asio::io_service &io_service(); - - const std::string &http_date(); - - template - int on_read(const boost::array &buffer, std::size_t len) { - callback_guard cg(*this); - - int rv; - - rv = nghttp2_session_mem_recv(session_, buffer.data(), len); - - if (rv < 0) { - return -1; - } - - return 0; - } - - template - int on_write(boost::array &buffer, std::size_t &len) { - callback_guard cg(*this); - - len = 0; - - if (buf_) { - std::copy_n(buf_, buflen_, std::begin(buffer)); - - len += buflen_; - - buf_ = nullptr; - buflen_ = 0; - } - - for (;;) { - const uint8_t *data; - auto nread = nghttp2_session_mem_send(session_, &data); - if (nread < 0) { - return -1; - } - - if (nread == 0) { - break; - } - - if (len + nread > buffer.size()) { - buf_ = data; - buflen_ = nread; - - break; - } - - std::copy_n(data, nread, std::begin(buffer) + len); - - len += nread; - } - - return 0; - } - -private: - std::map> streams_; - connection_write writefun_; - serve_mux &mux_; - boost::asio::io_service &io_service_; - nghttp2_session *session_; - const uint8_t *buf_; - std::size_t buflen_; - bool inside_callback_; - time_t tstamp_cached_; - std::string formatted_date_; -}; - -} // namespace server -} // namespace asio_http2 -} // namespace nghttp - -#endif // ASIO_SERVER_HTTP2_HANDLER_H diff --git a/src/asio_server_http2_impl.cc b/src/asio_server_http2_impl.cc deleted file mode 100644 index 79994a8..0000000 --- a/src/asio_server_http2_impl.cc +++ /dev/null @@ -1,66 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2014 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#include "asio_server_http2_impl.h" - -#include - -#include "asio_server.h" -#include "util.h" -#include "ssl.h" -#include "template.h" - -namespace nghttp2 { - -namespace asio_http2 { - -namespace server { - -http2_impl::http2_impl() : num_threads_(1), backlog_(-1) {} - -boost::system::error_code http2_impl::listen_and_serve( - boost::system::error_code &ec, boost::asio::ssl::context *tls_context, - const std::string &address, const std::string &port, bool asynchronous) { - server_.reset(new server(num_threads_)); - return server_->listen_and_serve(ec, tls_context, address, port, backlog_, - mux_, asynchronous); -} - -void http2_impl::num_threads(size_t num_threads) { num_threads_ = num_threads; } - -void http2_impl::backlog(int backlog) { backlog_ = backlog; } - -bool http2_impl::handle(std::string pattern, request_cb cb) { - return mux_.handle(std::move(pattern), std::move(cb)); -} - -void http2_impl::stop() { return server_->stop(); } - -void http2_impl::join() { return server_->join(); } - -} // namespace server - -} // namespace asio_http2 - -} // namespace nghttp2 diff --git a/src/asio_server_http2_impl.h b/src/asio_server_http2_impl.h deleted file mode 100644 index e406869..0000000 --- a/src/asio_server_http2_impl.h +++ /dev/null @@ -1,67 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2014 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#ifndef ASIO_SERVER_HTTP2_IMPL_H -#define ASIO_SERVER_HTTP2_IMPL_H - -#include "nghttp2_config.h" - -#include - -#include "asio_server_serve_mux.h" - -namespace nghttp2 { - -namespace asio_http2 { - -namespace server { - -class server; - -class http2_impl { -public: - http2_impl(); - boost::system::error_code listen_and_serve( - boost::system::error_code &ec, boost::asio::ssl::context *tls_context, - const std::string &address, const std::string &port, bool asynchronous); - void num_threads(size_t num_threads); - void backlog(int backlog); - bool handle(std::string pattern, request_cb cb); - void stop(); - void join(); - -private: - std::unique_ptr server_; - std::size_t num_threads_; - int backlog_; - serve_mux mux_; -}; - -} // namespace server - -} // namespace asio_http2 - -} // namespace nghttp2 - -#endif // ASIO_SERVER_HTTP2_IMPL_H diff --git a/src/asio_server_request.cc b/src/asio_server_request.cc deleted file mode 100644 index 0241c48..0000000 --- a/src/asio_server_request.cc +++ /dev/null @@ -1,55 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2015 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#include "nghttp2_config.h" - -#include - -#include "asio_server_request_impl.h" - -#include "template.h" - -namespace nghttp2 { -namespace asio_http2 { -namespace server { - -request::request() : impl_(make_unique()) {} - -request::~request() {} - -const header_map &request::header() const { return impl_->header(); } - -const std::string &request::method() const { return impl_->method(); } - -const uri_ref &request::uri() const { return impl_->uri(); } - -void request::on_data(data_cb cb) const { - return impl_->on_data(std::move(cb)); -} - -request_impl &request::impl() const { return *impl_; } - -} // namespace server -} // namespace asio_http2 -} // namespace nghttp2 diff --git a/src/asio_server_request_handler.cc b/src/asio_server_request_handler.cc deleted file mode 100644 index 7f113bd..0000000 --- a/src/asio_server_request_handler.cc +++ /dev/null @@ -1,84 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2015 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#include "asio_server_request_handler.h" - -#include "util.h" -#include "http2.h" - -namespace nghttp2 { -namespace asio_http2 { -namespace server { - -namespace { -std::string create_html(int status_code) { - std::string res; - res.reserve(512); - auto status = ::nghttp2::http2::get_status_string(status_code); - res += R"()"; - res += status; - res += "

      "; - res += status; - res += "

      "; - return res; -} -} // namespace - -request_cb redirect_handler(int status_code, std::string uri) { - return [status_code, uri](const request &req, const response &res) { - header_map h; - h.emplace("location", header_value{std::move(uri)}); - std::string html; - if (req.method() == "GET") { - html = create_html(status_code); - } - h.emplace("content-length", header_value{util::utos(html.size())}); - - res.write_head(status_code, std::move(h)); - res.end(std::move(html)); - }; -} - -request_cb status_handler(int status_code) { - return [status_code](const request &req, const response &res) { - if (!::nghttp2::http2::expect_response_body(status_code)) { - res.write_head(status_code); - res.end(); - return; - } - // we supply content-length for HEAD request, but body will not be - // sent. - auto html = create_html(status_code); - header_map h; - h.emplace("content-length", header_value{util::utos(html.size())}); - h.emplace("content-type", header_value{"text/html; charset=utf-8"}); - - res.write_head(status_code, std::move(h)); - res.end(std::move(html)); - }; -} - -} // namespace server -} // namespace asio_http2 -} // namespace nghttp2 diff --git a/src/asio_server_request_handler.h b/src/asio_server_request_handler.h deleted file mode 100644 index 5eefcfd..0000000 --- a/src/asio_server_request_handler.h +++ /dev/null @@ -1,32 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2015 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#ifndef ASIO_SERVER_REQUEST_HANDLER_H -#define ASIO_SERVER_REQUEST_HANDLER_H - -#include "nghttp2_config.h" - -#include - -#endif // ASIO_SERVER_REQUEST_HANDLER_H diff --git a/src/asio_server_request_impl.cc b/src/asio_server_request_impl.cc deleted file mode 100644 index 3a78208..0000000 --- a/src/asio_server_request_impl.cc +++ /dev/null @@ -1,59 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2015 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#include "asio_server_request_impl.h" - -namespace nghttp2 { -namespace asio_http2 { -namespace server { - -request_impl::request_impl() : strm_(nullptr) {} - -const header_map &request_impl::header() const { return header_; } - -const std::string &request_impl::method() const { return method_; } - -const uri_ref &request_impl::uri() const { return uri_; } - -uri_ref &request_impl::uri() { return uri_; } - -void request_impl::header(header_map h) { header_ = std::move(h); } - -header_map &request_impl::header() { return header_; } - -void request_impl::method(std::string arg) { method_ = std::move(arg); } - -void request_impl::on_data(data_cb cb) { on_data_cb_ = std::move(cb); } - -void request_impl::stream(class stream *s) { strm_ = s; } - -void request_impl::call_on_data(const uint8_t *data, std::size_t len) { - if (on_data_cb_) { - on_data_cb_(data, len); - } -} - -} // namespace server -} // namespace asio_http2 -} // namespace nghttp2 diff --git a/src/asio_server_request_impl.h b/src/asio_server_request_impl.h deleted file mode 100644 index 8d4b014..0000000 --- a/src/asio_server_request_impl.h +++ /dev/null @@ -1,69 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2015 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#ifndef ASIO_SERVER_REQUEST_IMPL_H -#define ASIO_SERVER_REQUEST_IMPL_H - -#include "nghttp2_config.h" - -#include - -namespace nghttp2 { -namespace asio_http2 { -namespace server { - -class stream; - -class request_impl { -public: - request_impl(); - - void header(header_map h); - const header_map &header() const; - header_map &header(); - - void method(std::string method); - const std::string &method() const; - - const uri_ref &uri() const; - uri_ref &uri(); - - void on_data(data_cb cb); - - void stream(class stream *s); - void call_on_data(const uint8_t *data, std::size_t len); - -private: - class stream *strm_; - header_map header_; - std::string method_; - uri_ref uri_; - data_cb on_data_cb_; -}; - -} // namespace server -} // namespace asio_http2 -} // namespace nghttp2 - -#endif // ASIO_SERVER_REQUEST_IMPL_H diff --git a/src/asio_server_response.cc b/src/asio_server_response.cc deleted file mode 100644 index f420693..0000000 --- a/src/asio_server_response.cc +++ /dev/null @@ -1,75 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2015 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#include "nghttp2_config.h" - -#include - -#include "asio_server_response_impl.h" - -#include "template.h" - -namespace nghttp2 { -namespace asio_http2 { -namespace server { - -response::response() : impl_(make_unique()) {} - -response::~response() {} - -void response::write_head(unsigned int status_code, header_map h) const { - impl_->write_head(status_code, std::move(h)); -} - -void response::end(std::string data) const { impl_->end(std::move(data)); } - -void response::end(generator_cb cb) const { impl_->end(std::move(cb)); } - -void response::write_trailer(header_map h) const { - impl_->write_trailer(std::move(h)); -} - -void response::on_close(close_cb cb) const { impl_->on_close(std::move(cb)); } - -void response::cancel(uint32_t error_code) const { impl_->cancel(error_code); } - -const response *response::push(boost::system::error_code &ec, - std::string method, std::string path, - header_map h) const { - return impl_->push(ec, std::move(method), std::move(path), std::move(h)); -} - -void response::resume() const { impl_->resume(); } - -unsigned int response::status_code() const { return impl_->status_code(); } - -boost::asio::io_service &response::io_service() const { - return impl_->io_service(); -} - -response_impl &response::impl() const { return *impl_; } - -} // namespace server -} // namespace asio_http2 -} // namespace nghttp2 diff --git a/src/asio_server_response_impl.cc b/src/asio_server_response_impl.cc deleted file mode 100644 index 1cdbc59..0000000 --- a/src/asio_server_response_impl.cc +++ /dev/null @@ -1,163 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2015 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#include "asio_server_response_impl.h" - -#include "asio_server_stream.h" -#include "asio_server_request_impl.h" -#include "asio_server_http2_handler.h" -#include "asio_common.h" - -#include "http2.h" - -namespace nghttp2 { -namespace asio_http2 { -namespace server { - -response_impl::response_impl() - : strm_(nullptr), generator_cb_(deferred_generator()), status_code_(200), - state_(response_state::INITIAL), pushed_(false), - push_promise_sent_(false) {} - -unsigned int response_impl::status_code() const { return status_code_; } - -void response_impl::write_head(unsigned int status_code, header_map h) { - if (state_ != response_state::INITIAL) { - return; - } - - status_code_ = status_code; - header_ = std::move(h); - - state_ = response_state::HEADER_DONE; - - if (pushed_ && !push_promise_sent_) { - return; - } - - start_response(); -} - -void response_impl::end(std::string data) { - end(string_generator(std::move(data))); -} - -void response_impl::end(generator_cb cb) { - if (state_ == response_state::BODY_STARTED) { - return; - } - - generator_cb_ = std::move(cb); - - if (state_ == response_state::INITIAL) { - write_head(status_code_); - } else { - // generator_cb is changed, start writing in case it is deferred. - auto handler = strm_->handler(); - handler->resume(*strm_); - } - - state_ = response_state::BODY_STARTED; -} - -void response_impl::write_trailer(header_map h) { - auto handler = strm_->handler(); - handler->submit_trailer(*strm_, std::move(h)); -} - -void response_impl::start_response() { - auto handler = strm_->handler(); - - auto &req = strm_->request().impl(); - - if (!::nghttp2::http2::expect_response_body(req.method(), status_code_)) { - state_ = response_state::BODY_STARTED; - } - - if (handler->start_response(*strm_) != 0) { - handler->stream_error(strm_->get_stream_id(), NGHTTP2_INTERNAL_ERROR); - return; - } -} - -void response_impl::on_close(close_cb cb) { close_cb_ = std::move(cb); } - -void response_impl::call_on_close(uint32_t error_code) { - if (close_cb_) { - close_cb_(error_code); - } -} - -void response_impl::cancel(uint32_t error_code) { - auto handler = strm_->handler(); - handler->stream_error(strm_->get_stream_id(), error_code); -} - -response *response_impl::push(boost::system::error_code &ec, std::string method, - std::string raw_path_query, header_map h) const { - auto handler = strm_->handler(); - return handler->push_promise(ec, *strm_, std::move(method), - std::move(raw_path_query), std::move(h)); -} - -void response_impl::resume() { - auto handler = strm_->handler(); - handler->resume(*strm_); -} - -boost::asio::io_service &response_impl::io_service() { - return strm_->handler()->io_service(); -} - -void response_impl::pushed(bool f) { pushed_ = f; } - -void response_impl::push_promise_sent() { - if (push_promise_sent_) { - return; - } - push_promise_sent_ = true; - if (state_ == response_state::INITIAL) { - return; - } - start_response(); -} - -const header_map &response_impl::header() const { return header_; } - -void response_impl::stream(class stream *s) { strm_ = s; } - -generator_cb::result_type -response_impl::call_read(uint8_t *data, std::size_t len, uint32_t *data_flags) { - if (generator_cb_) { - return generator_cb_(data, len, data_flags); - } - - *data_flags |= NGHTTP2_DATA_FLAG_EOF; - - return 0; -} - -} // namespace server -} // namespace asio_http2 -} // namespace nghttp2 diff --git a/src/asio_server_response_impl.h b/src/asio_server_response_impl.h deleted file mode 100644 index 41df7a6..0000000 --- a/src/asio_server_response_impl.h +++ /dev/null @@ -1,92 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2015 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#ifndef ASIO_SERVER_RESPONSE_IMPL_H -#define ASIO_SERVER_RESPONSE_IMPL_H - -#include "nghttp2_config.h" - -#include - -namespace nghttp2 { -namespace asio_http2 { -namespace server { - -class stream; - -enum class response_state { - INITIAL, - // response_impl::write_head() was called - HEADER_DONE, - // response_impl::end() was called - BODY_STARTED, -}; - -class response_impl { -public: - response_impl(); - void write_head(unsigned int status_code, header_map h = header_map{}); - void end(std::string data = ""); - void end(generator_cb cb); - void write_trailer(header_map h); - void on_close(close_cb cb); - void resume(); - - void cancel(uint32_t error_code); - - response *push(boost::system::error_code &ec, std::string method, - std::string raw_path_query, header_map) const; - - boost::asio::io_service &io_service(); - - void start_response(); - - unsigned int status_code() const; - const header_map &header() const; - void pushed(bool f); - void push_promise_sent(); - void stream(class stream *s); - generator_cb::result_type call_read(uint8_t *data, std::size_t len, - uint32_t *data_flags); - void call_on_close(uint32_t error_code); - -private: - class stream *strm_; - header_map header_; - generator_cb generator_cb_; - close_cb close_cb_; - unsigned int status_code_; - response_state state_; - // true if this is pushed stream's response - bool pushed_; - // true if PUSH_PROMISE is sent if this is response of a pushed - // stream - bool push_promise_sent_; -}; - -} // namespace server -} // namespace asio_http2 -} // namespace nghttp2 - -#endif // ASIO_SERVER_RESPONSE_IMPL_H diff --git a/src/asio_server_serve_mux.cc b/src/asio_server_serve_mux.cc deleted file mode 100644 index 829568b..0000000 --- a/src/asio_server_serve_mux.cc +++ /dev/null @@ -1,138 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2015 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#include "asio_server_serve_mux.h" - -#include "asio_server_request_impl.h" -#include "asio_server_request_handler.h" -#include "util.h" -#include "http2.h" - -namespace nghttp2 { - -namespace asio_http2 { - -namespace server { - -bool serve_mux::handle(std::string pattern, request_cb cb) { - if (pattern.empty() || !cb) { - return false; - } - - auto it = mux_.find(pattern); - if (it != std::end(mux_) && (*it).second.user_defined) { - return false; - } - - // if pattern ends with '/' (e.g., /foo/), add implicit permanent - // redirect for '/foo'. - if (pattern.size() >= 2 && pattern.back() == '/') { - auto redirect_pattern = pattern.substr(0, pattern.size() - 1); - auto it = mux_.find(redirect_pattern); - if (it == std::end(mux_) || !(*it).second.user_defined) { - std::string path; - if (pattern[0] == '/') { - path = pattern; - } else { - // skip host part - path = pattern.substr(pattern.find('/')); - } - if (it == std::end(mux_)) { - mux_.emplace(std::move(redirect_pattern), - handler_entry{false, - redirect_handler(301, std::move(path)), - pattern}); - } else { - (*it).second = handler_entry{ - false, redirect_handler(301, std::move(path)), pattern}; - } - } - } - mux_.emplace(pattern, handler_entry{true, std::move(cb), pattern}); - - return true; -} - -request_cb serve_mux::handler(request_impl &req) const { - auto &path = req.uri().path; - if (req.method() != "CONNECT") { - auto clean_path = ::nghttp2::http2::path_join( - nullptr, 0, nullptr, 0, path.c_str(), path.size(), nullptr, 0); - if (clean_path != path) { - auto new_uri = util::percent_encode_path(clean_path); - auto &uref = req.uri(); - if (!uref.raw_query.empty()) { - new_uri += "?"; - new_uri += uref.raw_query; - } - - return redirect_handler(301, std::move(new_uri)); - } - } - auto &host = req.uri().host; - - auto cb = match(host + path); - if (cb) { - return cb; - } - cb = match(path); - if (cb) { - return cb; - } - return status_handler(404); -} - -namespace { -bool path_match(const std::string &pattern, const std::string &path) { - if (pattern.back() != '/') { - return pattern == path; - } - return util::startsWith(path, pattern); -} -} // namespace - -request_cb serve_mux::match(const std::string &path) const { - const handler_entry *ent = nullptr; - size_t best = 0; - for (auto &kv : mux_) { - auto &pattern = kv.first; - if (!path_match(pattern, path)) { - continue; - } - if (!ent || best < pattern.size()) { - best = pattern.size(); - ent = &kv.second; - } - } - if (ent) { - return ent->cb; - } - return request_cb(); -} - -} // namespace server - -} // namespace asio_http2 - -} // namespace nghttp2 diff --git a/src/asio_server_serve_mux.h b/src/asio_server_serve_mux.h deleted file mode 100644 index 017a6bc..0000000 --- a/src/asio_server_serve_mux.h +++ /dev/null @@ -1,64 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2015 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#ifndef ASIO_SERVER_SERVE_MUX_H -#define ASIO_SERVER_SERVE_MUX_H - -#include "nghttp2_config.h" - -#include - -namespace nghttp2 { - -namespace asio_http2 { - -namespace server { - -class request_impl; - -// port from go's ServeMux - -struct handler_entry { - bool user_defined; - request_cb cb; - std::string pattern; -}; - -class serve_mux { -public: - bool handle(std::string pattern, request_cb cb); - request_cb handler(request_impl &req) const; - request_cb match(const std::string &path) const; - -private: - std::map mux_; -}; - -} // namespace server - -} // namespace asio_http2 - -} // namespace nghttp2 - -#endif // ASIO_SERVER_SERVE_MUX_H diff --git a/src/asio_server_stream.cc b/src/asio_server_stream.cc deleted file mode 100644 index f763c1e..0000000 --- a/src/asio_server_stream.cc +++ /dev/null @@ -1,55 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2015 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#include "asio_server_stream.h" - -#include "asio_server_http2_handler.h" -#include "asio_server_request_impl.h" -#include "asio_server_response_impl.h" - -namespace nghttp2 { -namespace asio_http2 { -namespace server { - -stream::stream(http2_handler *h, int32_t stream_id) - : handler_(h), stream_id_(stream_id) { - request_.impl().stream(this); - response_.impl().stream(this); -} - -int32_t stream::get_stream_id() const { return stream_id_; } - -class request &stream::request() { - return request_; -} - -class response &stream::response() { - return response_; -} - -http2_handler *stream::handler() const { return handler_; } - -} // namespace server -} // namespace asio_http2 -} // namespace nghttp2 diff --git a/src/asio_server_stream.h b/src/asio_server_stream.h deleted file mode 100644 index cd7e081..0000000 --- a/src/asio_server_stream.h +++ /dev/null @@ -1,59 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2015 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#ifndef ASIO_SERVER_STREAM_H -#define ASIO_SERVER_STREAM_H - -#include "nghttp2_config.h" - -#include - -namespace nghttp2 { -namespace asio_http2 { -namespace server { - -class http2_handler; - -class stream { -public: - stream(http2_handler *h, int32_t stream_id); - - int32_t get_stream_id() const; - class request &request(); - class response &response(); - - http2_handler *handler() const; - -private: - http2_handler *handler_; - class request request_; - class response response_; - int32_t stream_id_; -}; - -} // namespace server -} // namespace asio_http2 -} // namespace nghttp2 - -#endif // ASIO_SERVER_STREAM_H diff --git a/src/asio_server_tls_context.cc b/src/asio_server_tls_context.cc deleted file mode 100644 index 11336c9..0000000 --- a/src/asio_server_tls_context.cc +++ /dev/null @@ -1,85 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2015 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#include "asio_server_tls_context.h" - -#include - -#include - -#include "ssl.h" -#include "util.h" - -namespace nghttp2 { -namespace asio_http2 { -namespace server { - -namespace { -std::vector &get_alpn_token() { - static auto alpn_token = util::get_default_alpn(); - return alpn_token; -} -} // namespace - -boost::system::error_code -configure_tls_context_easy(boost::system::error_code &ec, - boost::asio::ssl::context &tls_context) { - ec.clear(); - - auto ctx = tls_context.native_handle(); - - SSL_CTX_set_options(ctx, SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | - SSL_OP_NO_COMPRESSION | - SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION | - SSL_OP_SINGLE_ECDH_USE | SSL_OP_NO_TICKET | - SSL_OP_CIPHER_SERVER_PREFERENCE); - SSL_CTX_set_mode(ctx, SSL_MODE_AUTO_RETRY); - SSL_CTX_set_mode(ctx, SSL_MODE_RELEASE_BUFFERS); - - SSL_CTX_set_cipher_list(ctx, ssl::DEFAULT_CIPHER_LIST); - - auto ecdh = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1); - if (ecdh) { - SSL_CTX_set_tmp_ecdh(ctx, ecdh); - EC_KEY_free(ecdh); - } - - SSL_CTX_set_next_protos_advertised_cb( - ctx, - [](SSL *s, const unsigned char **data, unsigned int *len, void *arg) { - auto &token = get_alpn_token(); - - *data = token.data(); - *len = token.size(); - - return SSL_TLSEXT_ERR_OK; - }, - nullptr); - - return ec; -} - -} // namespace server -} // namespace asio_http2 -} // namespace nghttp2 diff --git a/src/asio_server_tls_context.h b/src/asio_server_tls_context.h deleted file mode 100644 index 0f9b563..0000000 --- a/src/asio_server_tls_context.h +++ /dev/null @@ -1,32 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2015 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#ifndef ASIO_SERVER_TLS_CONTEXT_H -#define ASIO_SERVER_TLS_CONTEXT_H - -#include "nghttp2_config.h" - -#include - -#endif // ASIO_SERVER_TLS_CONTEXT_H diff --git a/src/base64.h b/src/base64.h deleted file mode 100644 index 88e3add..0000000 --- a/src/base64.h +++ /dev/null @@ -1,170 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2013 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#ifndef BASE64_H -#define BASE64_H - -#include "nghttp2_config.h" - -#include - -namespace nghttp2 { - -namespace base64 { - -template -std::string encode(InputIterator first, InputIterator last) { - static const char CHAR_TABLE[] = { - 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', - 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', - 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', - 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', - '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/', - }; - std::string res; - size_t len = last - first; - if (len == 0) { - return res; - } - size_t r = len % 3; - InputIterator j = last - r; - char temp[4]; - while (first != j) { - int n = static_cast(*first++) << 16; - n += static_cast(*first++) << 8; - n += static_cast(*first++); - temp[0] = CHAR_TABLE[n >> 18]; - temp[1] = CHAR_TABLE[(n >> 12) & 0x3fu]; - temp[2] = CHAR_TABLE[(n >> 6) & 0x3fu]; - temp[3] = CHAR_TABLE[n & 0x3fu]; - res.append(temp, sizeof(temp)); - } - if (r == 2) { - int n = static_cast(*first++) << 16; - n += static_cast(*first++) << 8; - temp[0] = CHAR_TABLE[n >> 18]; - temp[1] = CHAR_TABLE[(n >> 12) & 0x3fu]; - temp[2] = CHAR_TABLE[(n >> 6) & 0x3fu]; - temp[3] = '='; - res.append(temp, sizeof(temp)); - } else if (r == 1) { - int n = static_cast(*first++) << 16; - temp[0] = CHAR_TABLE[n >> 18]; - temp[1] = CHAR_TABLE[(n >> 12) & 0x3fu]; - temp[2] = '='; - temp[3] = '='; - res.append(temp, sizeof(temp)); - } - return res; -} - -template -InputIterator getNext(InputIterator first, InputIterator last, const int *tbl) { - for (; first != last; ++first) { - if (tbl[static_cast(*first)] != -1 || *first == '=') { - break; - } - } - return first; -} - -template -std::string decode(InputIterator first, InputIterator last) { - static const int INDEX_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}; - std::string res; - InputIterator k[4]; - int eq = 0; - for (; first != last;) { - for (int i = 1; i <= 4; ++i) { - k[i - 1] = getNext(first, last, INDEX_TABLE); - if (k[i - 1] == last) { - // If i == 1, input may look like this: "TWFu\n" (i.e., - // garbage at the end) - if (i != 1) { - res.clear(); - } - return res; - } else if (*k[i - 1] == '=' && eq == 0) { - eq = i; - } - first = k[i - 1] + 1; - } - if (eq) { - break; - } - int n = (INDEX_TABLE[static_cast(*k[0])] << 18) + - (INDEX_TABLE[static_cast(*k[1])] << 12) + - (INDEX_TABLE[static_cast(*k[2])] << 6) + - INDEX_TABLE[static_cast(*k[3])]; - res += n >> 16; - res += n >> 8 & 0xffu; - res += n & 0xffu; - } - if (eq) { - if (eq <= 2) { - res.clear(); - return res; - } else { - for (int i = eq; i <= 4; ++i) { - if (*k[i - 1] != '=') { - res.clear(); - return res; - } - } - if (eq == 3) { - int n = (INDEX_TABLE[static_cast(*k[0])] << 18) + - (INDEX_TABLE[static_cast(*k[1])] << 12); - res += n >> 16; - } else if (eq == 4) { - int n = (INDEX_TABLE[static_cast(*k[0])] << 18) + - (INDEX_TABLE[static_cast(*k[1])] << 12) + - (INDEX_TABLE[static_cast(*k[2])] << 6); - res += n >> 16; - res += n >> 8 & 0xffu; - } - } - } - return res; -} - -} // namespace base64 - -} // namespace nghttp2 - -#endif // BASE64_H diff --git a/src/buffer.h b/src/buffer.h deleted file mode 100644 index e8c9a5e..0000000 --- a/src/buffer.h +++ /dev/null @@ -1,68 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2014 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#ifndef BUFFER_H -#define BUFFER_H - -#include "nghttp2_config.h" - -#include -#include -#include - -namespace nghttp2 { - -template struct Buffer { - Buffer() : pos(std::begin(buf)), last(pos) {} - // Returns the number of bytes to read. - size_t rleft() const { return last - pos; } - // Returns the number of bytes this buffer can store. - size_t wleft() const { return std::end(buf) - last; } - // Writes up to min(wleft(), |count|) bytes from buffer pointed by - // |src|. Returns number of bytes written. - size_t write(const void *src, size_t count) { - count = std::min(count, wleft()); - auto p = static_cast(src); - last = std::copy_n(p, count, last); - return count; - } - size_t write(size_t count) { - count = std::min(count, wleft()); - last += count; - return count; - } - // Drains min(rleft(), |count|) bytes from start of the buffer. - size_t drain(size_t count) { - count = std::min(count, rleft()); - pos += count; - return count; - } - void reset() { pos = last = std::begin(buf); } - std::array buf; - uint8_t *pos, *last; -}; - -} // namespace nghttp2 - -#endif // BUFFER_H diff --git a/src/buffer_test.cc b/src/buffer_test.cc deleted file mode 100644 index 38688ed..0000000 --- a/src/buffer_test.cc +++ /dev/null @@ -1,78 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2015 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#include "buffer_test.h" - -#include -#include -#include - -#include - -#include - -#include "buffer.h" - -namespace nghttp2 { - -void test_buffer_write(void) { - Buffer<16> b; - CU_ASSERT(0 == b.rleft()); - CU_ASSERT(16 == b.wleft()); - - b.write("012", 3); - - CU_ASSERT(3 == b.rleft()); - CU_ASSERT(13 == b.wleft()); - CU_ASSERT(b.pos == std::begin(b.buf)); - - b.drain(3); - - CU_ASSERT(0 == b.rleft()); - CU_ASSERT(13 == b.wleft()); - CU_ASSERT(3 == b.pos - std::begin(b.buf)); - - auto n = b.write("0123456789ABCDEF", 16); - - CU_ASSERT(n == 13); - - CU_ASSERT(13 == b.rleft()); - CU_ASSERT(0 == b.wleft()); - CU_ASSERT(3 == b.pos - std::begin(b.buf)); - CU_ASSERT(0 == memcmp(b.pos, "0123456789ABC", 13)); - - b.reset(); - - CU_ASSERT(0 == b.rleft()); - CU_ASSERT(16 == b.wleft()); - CU_ASSERT(b.pos == std::begin(b.buf)); - - b.write(5); - - CU_ASSERT(5 == b.rleft()); - CU_ASSERT(11 == b.wleft()); - CU_ASSERT(b.pos == std::begin(b.buf)); -} - -} // namespace nghttp2 diff --git a/src/buffer_test.h b/src/buffer_test.h deleted file mode 100644 index abdc987..0000000 --- a/src/buffer_test.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2015 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#ifndef BUFFER_TEST_H -#define BUFFER_TEST_H - -#ifdef HAVE_CONFIG_H -#include -#endif // HAVE_CONFIG_H - -namespace nghttp2 { - -void test_buffer_write(void); - -} // namespace nghttp2 - -#endif // BUFFER_TEST_H diff --git a/src/comp_helper.c b/src/comp_helper.c deleted file mode 100644 index 8e0c03d..0000000 --- a/src/comp_helper.c +++ /dev/null @@ -1,91 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2013 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#include "comp_helper.h" -#include - -static void dump_val(json_t *jent, const char *key, uint8_t *val, size_t len) { - json_object_set_new(jent, key, json_pack("s#", val, len)); -} - -json_t *dump_header_table(nghttp2_hd_context *context) { - json_t *obj, *entries; - size_t i; - - obj = json_object(); - entries = json_array(); - for (i = 0; i < context->hd_table.len; ++i) { - nghttp2_hd_entry *ent = nghttp2_hd_table_get(context, i); - json_t *outent = json_object(); - json_object_set_new(outent, "index", json_integer(i + 1)); - dump_val(outent, "name", ent->nv.name, ent->nv.namelen); - dump_val(outent, "value", ent->nv.value, ent->nv.valuelen); - json_object_set_new(outent, "size", - json_integer(ent->nv.namelen + ent->nv.valuelen + - NGHTTP2_HD_ENTRY_OVERHEAD)); - json_array_append_new(entries, outent); - } - json_object_set_new(obj, "entries", entries); - json_object_set_new(obj, "size", json_integer(context->hd_table_bufsize)); - json_object_set_new(obj, "max_size", - json_integer(context->hd_table_bufsize_max)); - return obj; -} - -json_t *dump_header(const uint8_t *name, size_t namelen, const uint8_t *value, - size_t valuelen) { - json_t *nv_pair = json_object(); - char *cname = malloc(namelen + 1); - if (cname == NULL) { - return NULL; - } - memcpy(cname, name, namelen); - cname[namelen] = '\0'; - json_object_set_new(nv_pair, cname, json_pack("s#", value, valuelen)); - free(cname); - return nv_pair; -} - -json_t *dump_headers(const nghttp2_nv *nva, size_t nvlen) { - json_t *headers; - size_t i; - - headers = json_array(); - for (i = 0; i < nvlen; ++i) { - json_array_append_new(headers, dump_header(nva[i].name, nva[i].namelen, - nva[i].value, nva[i].valuelen)); - } - return headers; -} - -void output_json_header(void) { - printf("{\n" - " \"cases\":\n" - " [\n"); -} - -void output_json_footer(void) { - printf(" ]\n" - "}\n"); -} diff --git a/src/comp_helper.h b/src/comp_helper.h deleted file mode 100644 index e86defc..0000000 --- a/src/comp_helper.h +++ /dev/null @@ -1,47 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2013 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#ifndef NGHTTP2_COMP_HELPER_H -#define NGHTTP2_COMP_HELPER_H - -#ifdef HAVE_CONFIG_H -#include -#endif // HAVE_CONFIG_H - -#include - -#include "nghttp2_hd.h" - -json_t *dump_header_table(nghttp2_hd_context *context); - -json_t *dump_header(const uint8_t *name, size_t namelen, const uint8_t *value, - size_t vlauelen); - -json_t *dump_headers(const nghttp2_nv *nva, size_t nvlen); - -void output_json_header(void); - -void output_json_footer(void); - -#endif // NGHTTP2_COMP_HELPER_H diff --git a/src/deflatehd.cc b/src/deflatehd.cc deleted file mode 100644 index cd0c5d5..0000000 --- a/src/deflatehd.cc +++ /dev/null @@ -1,450 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2013 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#ifdef HAVE_CONFIG_H -#include -#endif // HAVE_CONFIG_H - -#ifdef HAVE_UNISTD_H -#include -#endif // HAVE_UNISTD_H -#include - -#include -#include -#include -#include -#include -#include -#include - -#include - -extern "C" { - -#include "nghttp2_hd.h" -#include "nghttp2_frame.h" - -#include "comp_helper.h" -} - -typedef struct { - size_t table_size; - size_t deflate_table_size; - int http1text; - int dump_header_table; -} deflate_config; - -static deflate_config config; - -static size_t input_sum; -static size_t output_sum; - -static char to_hex_digit(uint8_t n) { - if (n > 9) { - return n - 10 + 'a'; - } - return n + '0'; -} - -static void to_hex(char *dest, const uint8_t *src, size_t len) { - size_t i; - for (i = 0; i < len; ++i) { - *dest++ = to_hex_digit(src[i] >> 4); - *dest++ = to_hex_digit(src[i] & 0xf); - } -} - -static void output_to_json(nghttp2_hd_deflater *deflater, nghttp2_bufs *bufs, - size_t inputlen, const std::vector &nva, - int seq) { - auto len = nghttp2_bufs_len(bufs); - auto hex = std::vector(len * 2); - auto obj = json_object(); - auto comp_ratio = inputlen == 0 ? 0.0 : (double)len / inputlen * 100; - - json_object_set_new(obj, "seq", json_integer(seq)); - json_object_set_new(obj, "input_length", json_integer(inputlen)); - json_object_set_new(obj, "output_length", json_integer(len)); - json_object_set_new(obj, "percentage_of_original_size", - json_real(comp_ratio)); - - auto hexp = hex.data(); - for (auto ci = bufs->head; ci; ci = ci->next) { - auto buf = &ci->buf; - to_hex(hexp, buf->pos, nghttp2_buf_len(buf)); - hexp += nghttp2_buf_len(buf); - } - - if (len == 0) { - json_object_set_new(obj, "wire", json_string("")); - } else { - json_object_set_new(obj, "wire", json_pack("s#", hex.data(), hex.size())); - } - json_object_set_new(obj, "headers", dump_headers(nva.data(), nva.size())); - if (seq == 0) { - // We only change the header table size only once at the beginning - json_object_set_new(obj, "header_table_size", - json_integer(config.table_size)); - } - if (config.dump_header_table) { - json_object_set_new(obj, "header_table", dump_header_table(&deflater->ctx)); - } - json_dumpf(obj, stdout, JSON_PRESERVE_ORDER | JSON_INDENT(2)); - printf("\n"); - json_decref(obj); -} - -static void deflate_hd(nghttp2_hd_deflater *deflater, - const std::vector &nva, size_t inputlen, - int seq) { - ssize_t rv; - nghttp2_bufs bufs; - - nghttp2_bufs_init2(&bufs, 4096, 16, 0, nghttp2_mem_default()); - - rv = nghttp2_hd_deflate_hd_bufs(deflater, &bufs, (nghttp2_nv *)nva.data(), - nva.size()); - if (rv < 0) { - fprintf(stderr, "deflate failed with error code %zd at %d\n", rv, seq); - exit(EXIT_FAILURE); - } - - input_sum += inputlen; - output_sum += nghttp2_bufs_len(&bufs); - - output_to_json(deflater, &bufs, inputlen, nva, seq); - nghttp2_bufs_free(&bufs); -} - -static int deflate_hd_json(json_t *obj, nghttp2_hd_deflater *deflater, - int seq) { - size_t inputlen = 0; - - auto js = json_object_get(obj, "headers"); - if (js == nullptr) { - fprintf(stderr, "'headers' key is missing at %d\n", seq); - return -1; - } - if (!json_is_array(js)) { - fprintf(stderr, "The value of 'headers' key must be an array at %d\n", seq); - return -1; - } - - auto len = json_array_size(js); - auto nva = std::vector(len); - - for (size_t i = 0; i < len; ++i) { - auto nv_pair = json_array_get(js, i); - const char *name; - json_t *value; - - if (!json_is_object(nv_pair) || json_object_size(nv_pair) != 1) { - fprintf(stderr, "bad formatted name/value pair object at %d\n", seq); - return -1; - } - - json_object_foreach(nv_pair, name, value) { - nva[i].name = (uint8_t *)name; - nva[i].namelen = strlen(name); - - if (!json_is_string(value)) { - fprintf(stderr, "value is not string at %d\n", seq); - return -1; - } - - nva[i].value = (uint8_t *)json_string_value(value); - nva[i].valuelen = strlen(json_string_value(value)); - - nva[i].flags = NGHTTP2_NV_FLAG_NONE; - } - - inputlen += nva[i].namelen + nva[i].valuelen; - } - - deflate_hd(deflater, nva, inputlen, seq); - - return 0; -} - -static nghttp2_hd_deflater *init_deflater() { - nghttp2_hd_deflater *deflater; - nghttp2_hd_deflate_new(&deflater, config.deflate_table_size); - nghttp2_hd_deflate_change_table_size(deflater, config.table_size); - return deflater; -} - -static void deinit_deflater(nghttp2_hd_deflater *deflater) { - nghttp2_hd_deflate_del(deflater); -} - -static int perform(void) { - json_error_t error; - - auto json = json_loadf(stdin, 0, &error); - - if (json == nullptr) { - fprintf(stderr, "JSON loading failed\n"); - exit(EXIT_FAILURE); - } - - auto cases = json_object_get(json, "cases"); - - if (cases == nullptr) { - fprintf(stderr, "Missing 'cases' key in root object\n"); - exit(EXIT_FAILURE); - } - - if (!json_is_array(cases)) { - fprintf(stderr, "'cases' must be JSON array\n"); - exit(EXIT_FAILURE); - } - - auto deflater = init_deflater(); - output_json_header(); - auto len = json_array_size(cases); - - for (size_t i = 0; i < len; ++i) { - auto obj = json_array_get(cases, i); - if (!json_is_object(obj)) { - fprintf(stderr, "Unexpected JSON type at %zu. It should be object.\n", i); - continue; - } - if (deflate_hd_json(obj, deflater, i) != 0) { - continue; - } - if (i + 1 < len) { - printf(",\n"); - } - } - output_json_footer(); - deinit_deflater(deflater); - json_decref(json); - return 0; -} - -static int perform_from_http1text(void) { - char line[1 << 14]; - int seq = 0; - - auto deflater = init_deflater(); - output_json_header(); - for (;;) { - std::vector nva; - int end = 0; - size_t inputlen = 0; - - for (;;) { - char *rv = fgets(line, sizeof(line), stdin); - char *val, *val_end; - if (rv == nullptr) { - end = 1; - break; - } else if (line[0] == '\n') { - break; - } - - nva.emplace_back(); - auto &nv = nva.back(); - - val = strchr(line + 1, ':'); - if (val == nullptr) { - fprintf(stderr, "Bad HTTP/1 header field format at %d.\n", seq); - exit(EXIT_FAILURE); - } - *val = '\0'; - ++val; - for (; *val && (*val == ' ' || *val == '\t'); ++val) - ; - for (val_end = val; *val_end && (*val_end != '\r' && *val_end != '\n'); - ++val_end) - ; - *val_end = '\0'; - - nv.namelen = strlen(line); - nv.valuelen = strlen(val); - nv.name = (uint8_t *)strdup(line); - nv.value = (uint8_t *)strdup(val); - nv.flags = NGHTTP2_NV_FLAG_NONE; - - inputlen += nv.namelen + nv.valuelen; - } - - if (!end) { - if (seq > 0) { - printf(",\n"); - } - deflate_hd(deflater, nva, inputlen, seq); - } - - for (auto &nv : nva) { - free(nv.name); - free(nv.value); - } - - if (end) - break; - ++seq; - } - output_json_footer(); - deinit_deflater(deflater); - return 0; -} - -static void print_help(void) { - std::cout << R"(HPACK HTTP/2 header encoder -Usage: deflatehd [OPTIONS] < INPUT - -Reads JSON data or HTTP/1-style header fields from stdin and outputs -deflated header block in JSON array. - -For the JSON input, the root JSON object must contain "context" key, -which indicates which compression context is used. If it is -"request", request compression context is used. Otherwise, response -compression context is used. The value of "cases" key contains the -sequence of input header set. They share the same compression context -and are processed in the order they appear. Each item in the sequence -is a JSON object and it must have at least "headers" key. Its value -is an array of a JSON object containing exactly one name/value pair. - -Example: -{ - "context": "request", - "cases": - [ - { - "headers": [ - { ":method": "GET" }, - { ":path": "/" } - ] - }, - { - "headers": [ - { ":method": "POST" }, - { ":path": "/" } - ] - } - ] -} - -With -t option, the program can accept more familiar HTTP/1 style -header field block. Each header set must be followed by one empty -line: - -Example: - -:method: GET -:scheme: https -:path: / - -:method: POST -user-agent: nghttp2 - -The output of this program can be used as input for inflatehd. - -OPTIONS: - -t, --http1text Use HTTP/1 style header field text as input. - Each header set is delimited by single empty - line. - -s, --table-size= - Set dynamic table size. In the HPACK - specification, this value is denoted by - SETTINGS_HEADER_TABLE_SIZE. - Default: 4096 - -S, --deflate-table-size= - Use first N bytes of dynamic header table - buffer. - Default: 4096 - -d, --dump-header-table - Output dynamic header table.)" << std::endl; -} - -static struct option long_options[] = { - {"http1text", no_argument, nullptr, 't'}, - {"table-size", required_argument, nullptr, 's'}, - {"deflate-table-size", required_argument, nullptr, 'S'}, - {"dump-header-table", no_argument, nullptr, 'd'}, - {nullptr, 0, nullptr, 0}}; - -int main(int argc, char **argv) { - char *end; - - config.table_size = NGHTTP2_HD_DEFAULT_MAX_BUFFER_SIZE; - config.deflate_table_size = NGHTTP2_HD_DEFAULT_MAX_DEFLATE_BUFFER_SIZE; - config.http1text = 0; - config.dump_header_table = 0; - while (1) { - int option_index = 0; - int c = getopt_long(argc, argv, "S:dhs:t", long_options, &option_index); - if (c == -1) { - break; - } - switch (c) { - case 'h': - print_help(); - exit(EXIT_SUCCESS); - case 't': - // --http1text - config.http1text = 1; - break; - case 's': - // --table-size - errno = 0; - config.table_size = strtoul(optarg, &end, 10); - if (errno == ERANGE || *end != '\0') { - fprintf(stderr, "-s: Bad option value\n"); - exit(EXIT_FAILURE); - } - break; - case 'S': - // --deflate-table-size - errno = 0; - config.deflate_table_size = strtoul(optarg, &end, 10); - if (errno == ERANGE || *end != '\0') { - fprintf(stderr, "-S: Bad option value\n"); - exit(EXIT_FAILURE); - } - break; - case 'd': - // --dump-header-table - config.dump_header_table = 1; - break; - case '?': - exit(EXIT_FAILURE); - default: - break; - } - } - if (config.http1text) { - perform_from_http1text(); - } else { - perform(); - } - - auto comp_ratio = input_sum == 0 ? 0.0 : (double)output_sum / input_sum; - - fprintf(stderr, "Overall: input=%zu output=%zu ratio=%.02f\n", input_sum, - output_sum, comp_ratio); - return 0; -} diff --git a/src/h2load.cc b/src/h2load.cc deleted file mode 100644 index 5578415..0000000 --- a/src/h2load.cc +++ /dev/null @@ -1,1528 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2014 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#include "h2load.h" - -#include -#include -#ifdef HAVE_NETINET_IN_H -#include -#endif // HAVE_NETINET_IN_H -#include -#include -#ifdef HAVE_FCNTL_H -#include -#endif // HAVE_FCNTL_H - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef HAVE_SPDYLAY -#include -#endif // HAVE_SPDYLAY - -#include -#include - -#include "http-parser/http_parser.h" - -#include "h2load_http2_session.h" -#ifdef HAVE_SPDYLAY -#include "h2load_spdy_session.h" -#endif // HAVE_SPDYLAY -#include "ssl.h" -#include "http2.h" -#include "util.h" -#include "template.h" - -#ifndef O_BINARY -#define O_BINARY (0) -#endif // O_BINARY - -using namespace nghttp2; - -namespace h2load { - -Config::Config() - : data_length(-1), addrs(nullptr), nreqs(1), nclients(1), nthreads(1), - max_concurrent_streams(-1), window_bits(30), connection_window_bits(30), - no_tls_proto(PROTO_HTTP2), data_fd(-1), port(0), default_port(0), - verbose(false) {} - -Config::~Config() { - freeaddrinfo(addrs); - - if (data_fd != -1) { - close(data_fd); - } -} - -Config config; - -namespace { -void debug(const char *format, ...) { - if (config.verbose) { - fprintf(stderr, "[DEBUG] "); - va_list ap; - va_start(ap, format); - vfprintf(stderr, format, ap); - va_end(ap); - } -} -} // namespace - -namespace { -void debug_nextproto_error() { -#ifdef HAVE_SPDYLAY - debug("no supported protocol was negotiated, expected: %s, " - "spdy/2, spdy/3, spdy/3.1\n", - NGHTTP2_PROTO_VERSION_ID); -#else // !HAVE_SPDYLAY - debug("no supported protocol was negotiated, expected: %s\n", - NGHTTP2_PROTO_VERSION_ID); -#endif // !HAVE_SPDYLAY -} -} // namespace - -RequestStat::RequestStat() : data_offset(0), completed(false) {} - -Stats::Stats(size_t req_todo) - : req_todo(0), req_started(0), req_done(0), req_success(0), - req_status_success(0), req_failed(0), req_error(0), bytes_total(0), - bytes_head(0), bytes_body(0), status(), req_stats(req_todo) {} - -Stream::Stream() : status_success(-1) {} - -namespace { -void writecb(struct ev_loop *loop, ev_io *w, int revents) { - auto client = static_cast(w->data); - auto rv = client->do_write(); - if (rv == Client::ERR_CONNECT_FAIL) { - client->disconnect(); - rv = client->connect(); - if (rv != 0) { - client->fail(); - return; - } - return; - } - if (rv != 0) { - client->fail(); - } -} -} // namespace - -namespace { -void readcb(struct ev_loop *loop, ev_io *w, int revents) { - auto client = static_cast(w->data); - if (client->do_read() != 0) { - client->fail(); - return; - } - writecb(loop, &client->wev, revents); - // client->disconnect() and client->fail() may be called -} -} // namespace - -Client::Client(Worker *worker, size_t req_todo) - : worker(worker), ssl(nullptr), next_addr(config.addrs), reqidx(0), - state(CLIENT_IDLE), first_byte_received(false), req_todo(req_todo), - req_started(0), req_done(0), fd(-1) { - ev_io_init(&wev, writecb, 0, EV_WRITE); - ev_io_init(&rev, readcb, 0, EV_READ); - - wev.data = this; - rev.data = this; -} - -Client::~Client() { disconnect(); } - -int Client::do_read() { return readfn(*this); } -int Client::do_write() { return writefn(*this); } - -int Client::connect() { - record_start_time(&worker->stats); - - while (next_addr) { - auto addr = next_addr; - next_addr = next_addr->ai_next; - fd = util::create_nonblock_socket(addr->ai_family); - if (fd == -1) { - continue; - } - if (config.scheme == "https") { - ssl = SSL_new(worker->ssl_ctx); - - auto config = worker->config; - - if (!util::numeric_host(config->host.c_str())) { - SSL_set_tlsext_host_name(ssl, config->host.c_str()); - } - - SSL_set_fd(ssl, fd); - SSL_set_connect_state(ssl); - } - - auto rv = ::connect(fd, addr->ai_addr, addr->ai_addrlen); - if (rv != 0 && errno != EINPROGRESS) { - if (ssl) { - SSL_free(ssl); - ssl = nullptr; - } - close(fd); - fd = -1; - continue; - } - break; - } - - if (fd == -1) { - return -1; - } - - writefn = &Client::connected; - - ev_io_set(&rev, fd, EV_READ); - ev_io_set(&wev, fd, EV_WRITE); - - ev_io_start(worker->loop, &wev); - - return 0; -} - -void Client::fail() { - process_abandoned_streams(); - - disconnect(); -} - -void Client::disconnect() { - streams.clear(); - session.reset(); - state = CLIENT_IDLE; - ev_io_stop(worker->loop, &wev); - ev_io_stop(worker->loop, &rev); - if (ssl) { - SSL_set_shutdown(ssl, SSL_RECEIVED_SHUTDOWN); - ERR_clear_error(); - SSL_shutdown(ssl); - SSL_free(ssl); - ssl = nullptr; - } - if (fd != -1) { - shutdown(fd, SHUT_WR); - close(fd); - fd = -1; - } -} - -void Client::submit_request() { - auto req_stat = &worker->stats.req_stats[worker->stats.req_started++]; - session->submit_request(req_stat); - ++req_started; -} - -void Client::process_abandoned_streams() { - auto req_abandoned = req_todo - req_done; - - worker->stats.req_failed += req_abandoned; - worker->stats.req_error += req_abandoned; - worker->stats.req_done += req_abandoned; - - req_done = req_todo; -} - -void Client::report_progress() { - if (worker->id == 0 && - worker->stats.req_done % worker->progress_interval == 0) { - std::cout << "progress: " - << worker->stats.req_done * 100 / worker->stats.req_todo - << "% done" << std::endl; - } -} - -namespace { -const char *get_tls_protocol(SSL *ssl) { - auto session = SSL_get_session(ssl); - - switch (session->ssl_version) { - case SSL2_VERSION: - return "SSLv2"; - case SSL3_VERSION: - return "SSLv3"; - case TLS1_2_VERSION: - return "TLSv1.2"; - case TLS1_1_VERSION: - return "TLSv1.1"; - case TLS1_VERSION: - return "TLSv1"; - default: - return "unknown"; - } -} -} // namespace - -namespace { -void print_server_tmp_key(SSL *ssl) { -#if OPENSSL_VERSION_NUMBER >= 0x10002000L - EVP_PKEY *key; - - if (!SSL_get_server_tmp_key(ssl, &key)) { - return; - } - - auto key_del = defer(EVP_PKEY_free, key); - - std::cout << "Server Temp Key: "; - - switch (EVP_PKEY_id(key)) { - case EVP_PKEY_RSA: - std::cout << "RSA " << EVP_PKEY_bits(key) << " bits" << std::endl; - break; - case EVP_PKEY_DH: - std::cout << "DH " << EVP_PKEY_bits(key) << " bits" << std::endl; - break; - case EVP_PKEY_EC: { - auto ec = EVP_PKEY_get1_EC_KEY(key); - auto ec_del = defer(EC_KEY_free, ec); - auto nid = EC_GROUP_get_curve_name(EC_KEY_get0_group(ec)); - auto cname = EC_curve_nid2nist(nid); - if (!cname) { - cname = OBJ_nid2sn(nid); - } - - std::cout << "ECDH " << cname << " " << EVP_PKEY_bits(key) << " bits" - << std::endl; - break; - } - } -#endif // OPENSSL_VERSION_NUMBER >= 0x10002000L -} -} // namespace - -void Client::report_tls_info() { - if (worker->id == 0 && !worker->tls_info_report_done) { - worker->tls_info_report_done = true; - auto cipher = SSL_get_current_cipher(ssl); - std::cout << "Protocol: " << get_tls_protocol(ssl) << "\n" - << "Cipher: " << SSL_CIPHER_get_name(cipher) << std::endl; - print_server_tmp_key(ssl); - } -} - -void Client::terminate_session() { session->terminate(); } - -void Client::on_request(int32_t stream_id) { streams[stream_id] = Stream(); } - -void Client::on_header(int32_t stream_id, const uint8_t *name, size_t namelen, - const uint8_t *value, size_t valuelen) { - auto itr = streams.find(stream_id); - if (itr == std::end(streams)) { - return; - } - auto &stream = (*itr).second; - if (stream.status_success == -1 && namelen == 7 && - util::streq_l(":status", name, namelen)) { - int status = 0; - for (size_t i = 0; i < valuelen; ++i) { - if ('0' <= value[i] && value[i] <= '9') { - status *= 10; - status += value[i] - '0'; - if (status > 999) { - stream.status_success = 0; - return; - } - } else { - break; - } - } - - if (status >= 200 && status < 300) { - ++worker->stats.status[2]; - stream.status_success = 1; - } else if (status < 400) { - ++worker->stats.status[3]; - stream.status_success = 1; - } else if (status < 600) { - ++worker->stats.status[status / 100]; - stream.status_success = 0; - } else { - stream.status_success = 0; - } - } -} - -void Client::on_stream_close(int32_t stream_id, bool success, - RequestStat *req_stat) { - req_stat->stream_close_time = std::chrono::steady_clock::now(); - if (success) { - req_stat->completed = true; - ++worker->stats.req_success; - } - ++worker->stats.req_done; - ++req_done; - if (success && streams[stream_id].status_success == 1) { - ++worker->stats.req_status_success; - } else { - ++worker->stats.req_failed; - } - report_progress(); - streams.erase(stream_id); - if (req_done == req_todo) { - terminate_session(); - return; - } - - if (req_started < req_todo) { - submit_request(); - return; - } -} - -int Client::connection_made() { - if (ssl) { - report_tls_info(); - - const unsigned char *next_proto = nullptr; - unsigned int next_proto_len; - SSL_get0_next_proto_negotiated(ssl, &next_proto, &next_proto_len); - for (int i = 0; i < 2; ++i) { - if (next_proto) { - if (util::check_h2_is_selected(next_proto, next_proto_len)) { - session = make_unique(this); - } else { -#ifdef HAVE_SPDYLAY - auto spdy_version = - spdylay_npn_get_version(next_proto, next_proto_len); - if (spdy_version) { - session = make_unique(this, spdy_version); - } else { - debug_nextproto_error(); - fail(); - return -1; - } -#else // !HAVE_SPDYLAY - debug_nextproto_error(); - fail(); - return -1; -#endif // !HAVE_SPDYLAY - } - } - -#if OPENSSL_VERSION_NUMBER >= 0x10002000L - SSL_get0_alpn_selected(ssl, &next_proto, &next_proto_len); -#else // OPENSSL_VERSION_NUMBER < 0x10002000L - break; -#endif // OPENSSL_VERSION_NUMBER < 0x10002000L - } - - if (!next_proto) { - debug_nextproto_error(); - fail(); - return -1; - } - } else { - switch (config.no_tls_proto) { - case Config::PROTO_HTTP2: - session = make_unique(this); - break; -#ifdef HAVE_SPDYLAY - case Config::PROTO_SPDY2: - session = make_unique(this, SPDYLAY_PROTO_SPDY2); - break; - case Config::PROTO_SPDY3: - session = make_unique(this, SPDYLAY_PROTO_SPDY3); - break; - case Config::PROTO_SPDY3_1: - session = make_unique(this, SPDYLAY_PROTO_SPDY3_1); - break; -#endif // HAVE_SPDYLAY - default: - // unreachable - assert(0); - } - } - - state = CLIENT_CONNECTED; - - session->on_connect(); - - record_connect_time(&worker->stats); - - auto nreq = - std::min(req_todo - req_started, (size_t)config.max_concurrent_streams); - - for (; nreq > 0; --nreq) { - submit_request(); - } - - signal_write(); - - return 0; -} - -int Client::on_read(const uint8_t *data, size_t len) { - auto rv = session->on_read(data, len); - if (rv != 0) { - return -1; - } - worker->stats.bytes_total += len; - signal_write(); - return 0; -} - -int Client::on_write() { - if (session->on_write() != 0) { - return -1; - } - return 0; -} - -int Client::read_clear() { - uint8_t buf[8192]; - - for (;;) { - ssize_t nread; - while ((nread = read(fd, buf, sizeof(buf))) == -1 && errno == EINTR) - ; - if (nread == -1) { - if (errno == EAGAIN || errno == EWOULDBLOCK) { - return 0; - } - return -1; - } - - if (nread == 0) { - return -1; - } - - if (on_read(buf, nread) != 0) { - return -1; - } - - if (!first_byte_received) { - first_byte_received = true; - record_ttfb(&worker->stats); - } - } - - return 0; -} - -int Client::write_clear() { - for (;;) { - if (wb.rleft() > 0) { - ssize_t nwrite; - while ((nwrite = write(fd, wb.pos, wb.rleft())) == -1 && errno == EINTR) - ; - if (nwrite == -1) { - if (errno == EAGAIN || errno == EWOULDBLOCK) { - ev_io_start(worker->loop, &wev); - return 0; - } - return -1; - } - wb.drain(nwrite); - continue; - } - wb.reset(); - if (on_write() != 0) { - return -1; - } - if (wb.rleft() == 0) { - break; - } - } - - ev_io_stop(worker->loop, &wev); - - return 0; -} - -int Client::connected() { - if (!util::check_socket_connected(fd)) { - return ERR_CONNECT_FAIL; - } - ev_io_start(worker->loop, &rev); - ev_io_stop(worker->loop, &wev); - - if (ssl) { - readfn = &Client::tls_handshake; - writefn = &Client::tls_handshake; - - return do_write(); - } - - readfn = &Client::read_clear; - writefn = &Client::write_clear; - - if (connection_made() != 0) { - return -1; - } - - return 0; -} - -int Client::tls_handshake() { - ERR_clear_error(); - - auto rv = SSL_do_handshake(ssl); - - if (rv == 0) { - return -1; - } - - if (rv < 0) { - auto err = SSL_get_error(ssl, rv); - switch (err) { - case SSL_ERROR_WANT_READ: - ev_io_stop(worker->loop, &wev); - return 0; - case SSL_ERROR_WANT_WRITE: - ev_io_start(worker->loop, &wev); - return 0; - default: - return -1; - } - } - - ev_io_stop(worker->loop, &wev); - - readfn = &Client::read_tls; - writefn = &Client::write_tls; - - if (connection_made() != 0) { - return -1; - } - - return 0; -} - -int Client::read_tls() { - uint8_t buf[8192]; - - ERR_clear_error(); - - for (;;) { - auto rv = SSL_read(ssl, buf, sizeof(buf)); - - if (rv == 0) { - return -1; - } - - if (rv < 0) { - auto err = SSL_get_error(ssl, rv); - switch (err) { - case SSL_ERROR_WANT_READ: - return 0; - case SSL_ERROR_WANT_WRITE: - // renegotiation started - return -1; - default: - return -1; - } - } - - if (on_read(buf, rv) != 0) { - return -1; - } - - if (!first_byte_received) { - first_byte_received = true; - record_ttfb(&worker->stats); - } - } -} - -int Client::write_tls() { - ERR_clear_error(); - - for (;;) { - if (wb.rleft() > 0) { - auto rv = SSL_write(ssl, wb.pos, wb.rleft()); - - if (rv == 0) { - return -1; - } - - if (rv < 0) { - auto err = SSL_get_error(ssl, rv); - switch (err) { - case SSL_ERROR_WANT_READ: - // renegotiation started - return -1; - case SSL_ERROR_WANT_WRITE: - ev_io_start(worker->loop, &wev); - return 0; - default: - return -1; - } - } - - wb.drain(rv); - - continue; - } - wb.reset(); - if (on_write() != 0) { - return -1; - } - if (wb.rleft() == 0) { - break; - } - } - - ev_io_stop(worker->loop, &wev); - - return 0; -} - -void Client::record_request_time(RequestStat *req_stat) { - req_stat->request_time = std::chrono::steady_clock::now(); -} - -void Client::record_start_time(Stats *stat) { - stat->start_times.push_back(std::chrono::steady_clock::now()); -} - -void Client::record_connect_time(Stats *stat) { - stat->connect_times.push_back(std::chrono::steady_clock::now()); -} - -void Client::record_ttfb(Stats *stat) { - stat->ttfbs.push_back(std::chrono::steady_clock::now()); -} - -void Client::signal_write() { ev_io_start(worker->loop, &wev); } - -Worker::Worker(uint32_t id, SSL_CTX *ssl_ctx, size_t req_todo, size_t nclients, - Config *config) - : stats(req_todo), loop(ev_loop_new(0)), ssl_ctx(ssl_ctx), config(config), - id(id), tls_info_report_done(false) { - stats.req_todo = req_todo; - progress_interval = std::max((size_t)1, req_todo / 10); - - auto nreqs_per_client = req_todo / nclients; - auto nreqs_rem = req_todo % nclients; - - for (size_t i = 0; i < nclients; ++i) { - auto req_todo = nreqs_per_client; - if (nreqs_rem > 0) { - ++req_todo; - --nreqs_rem; - } - clients.push_back(make_unique(this, req_todo)); - } -} - -Worker::~Worker() { - // first clear clients so that io watchers are stopped before - // destructing ev_loop. - clients.clear(); - ev_loop_destroy(loop); -} - -void Worker::run() { - for (auto &client : clients) { - if (client->connect() != 0) { - std::cerr << "client could not connect to host" << std::endl; - client->fail(); - } - } - ev_run(loop, 0); -} - -namespace { -// Returns percentage of number of samples within mean +/- sd. -template -double within_sd(const std::vector &samples, const Duration &mean, - const Duration &sd) { - if (samples.size() == 0) { - return 0.0; - } - auto lower = mean - sd; - auto upper = mean + sd; - auto m = std::count_if( - std::begin(samples), std::end(samples), - [&lower, &upper](const Duration &t) { return lower <= t && t <= upper; }); - return (m / static_cast(samples.size())) * 100; -} -} // namespace - -namespace { -// Computes statistics using |samples|. The min, max, mean, sd, and -// percentage of number of samples within mean +/- sd are computed. -template -TimeStat compute_time_stat(const std::vector &samples) { - if (samples.size() == 0) { - return {Duration::zero(), Duration::zero(), Duration::zero(), - Duration::zero(), 0.0}; - } - // standard deviation calculated using Rapid calculation method: - // http://en.wikipedia.org/wiki/Standard_deviation#Rapid_calculation_methods - double a = 0, q = 0; - size_t n = 0; - int64_t sum = 0; - auto res = TimeStat{Duration::max(), Duration::min()}; - for (const auto &t : samples) { - ++n; - res.min = std::min(res.min, t); - res.max = std::max(res.max, t); - sum += t.count(); - - auto na = a + (t.count() - a) / n; - q += (t.count() - a) * (t.count() - na); - a = na; - } - - res.mean = Duration(sum / n); - res.sd = Duration(static_cast(sqrt(q / n))); - res.within_sd = within_sd(samples, res.mean, res.sd); - - return res; -} -} // namespace - -namespace { -TimeStats -process_time_stats(const std::vector> &workers) { - size_t nrequest_times = 0, nttfb_times = 0; - for (const auto &w : workers) { - nrequest_times += w->stats.req_stats.size(); - nttfb_times += w->stats.ttfbs.size(); - } - - std::vector request_times; - request_times.reserve(nrequest_times); - std::vector connect_times, ttfb_times; - connect_times.reserve(nttfb_times); - ttfb_times.reserve(nttfb_times); - - for (const auto &w : workers) { - for (const auto &req_stat : w->stats.req_stats) { - if (!req_stat.completed) { - continue; - } - request_times.push_back( - std::chrono::duration_cast( - req_stat.stream_close_time - req_stat.request_time)); - } - - const auto &stat = w->stats; - // rule out cases where we started but didn't connect or get the - // first byte (errors). We will get connect event before FFTB. - assert(stat.start_times.size() >= stat.ttfbs.size()); - assert(stat.connect_times.size() >= stat.ttfbs.size()); - for (size_t i = 0; i < stat.ttfbs.size(); ++i) { - connect_times.push_back( - std::chrono::duration_cast( - stat.connect_times[i] - stat.start_times[i])); - - ttfb_times.push_back( - std::chrono::duration_cast( - stat.ttfbs[i] - stat.start_times[i])); - } - } - - return {compute_time_stat(request_times), compute_time_stat(connect_times), - compute_time_stat(ttfb_times)}; -} -} // namespace - -namespace { -void resolve_host() { - int rv; - addrinfo hints, *res; - - memset(&hints, 0, sizeof(hints)); - hints.ai_family = AF_UNSPEC; - hints.ai_socktype = SOCK_STREAM; - hints.ai_protocol = 0; - hints.ai_flags = AI_ADDRCONFIG; - - rv = getaddrinfo(config.host.c_str(), util::utos(config.port).c_str(), &hints, - &res); - if (rv != 0) { - std::cerr << "getaddrinfo() failed: " << gai_strerror(rv) << std::endl; - exit(EXIT_FAILURE); - } - if (res == nullptr) { - std::cerr << "No address returned" << std::endl; - exit(EXIT_FAILURE); - } - config.addrs = res; -} -} // namespace - -namespace { -std::string get_reqline(const char *uri, const http_parser_url &u) { - std::string reqline; - - if (util::has_uri_field(u, UF_PATH)) { - reqline = util::get_uri_field(uri, u, UF_PATH); - } else { - reqline = "/"; - } - - if (util::has_uri_field(u, UF_QUERY)) { - reqline += "?"; - reqline += util::get_uri_field(uri, u, UF_QUERY); - } - - return reqline; -} -} // namespace - -namespace { -int client_select_next_proto_cb(SSL *ssl, unsigned char **out, - unsigned char *outlen, const unsigned char *in, - unsigned int inlen, void *arg) { - if (util::select_h2(const_cast(out), outlen, in, - inlen)) { - return SSL_TLSEXT_ERR_OK; - } -#ifdef HAVE_SPDYLAY - if (spdylay_select_next_protocol(out, outlen, in, inlen) > 0) { - return SSL_TLSEXT_ERR_OK; - } -#endif - return SSL_TLSEXT_ERR_NOACK; -} -} // namespace - -namespace { -template -std::vector parse_uris(Iterator first, Iterator last) { - std::vector reqlines; - - // First URI is treated specially. We use scheme, host and port of - // this URI and ignore those in the remaining URIs if present. - http_parser_url u; - memset(&u, 0, sizeof(u)); - - if (first == last) { - std::cerr << "no URI available" << std::endl; - exit(EXIT_FAILURE); - } - - auto uri = (*first).c_str(); - ++first; - - if (http_parser_parse_url(uri, strlen(uri), 0, &u) != 0 || - !util::has_uri_field(u, UF_SCHEMA) || !util::has_uri_field(u, UF_HOST)) { - std::cerr << "invalid URI: " << uri << std::endl; - exit(EXIT_FAILURE); - } - - config.scheme = util::get_uri_field(uri, u, UF_SCHEMA); - config.host = util::get_uri_field(uri, u, UF_HOST); - config.default_port = util::get_default_port(uri, u); - if (util::has_uri_field(u, UF_PORT)) { - config.port = u.port; - } else { - config.port = config.default_port; - } - - reqlines.push_back(get_reqline(uri, u)); - - for (; first != last; ++first) { - http_parser_url u; - memset(&u, 0, sizeof(u)); - - auto uri = (*first).c_str(); - - if (http_parser_parse_url(uri, strlen(uri), 0, &u) != 0) { - std::cerr << "invalid URI: " << uri << std::endl; - exit(EXIT_FAILURE); - } - - reqlines.push_back(get_reqline(uri, u)); - } - - return reqlines; -} -} // namespace - -namespace { -std::vector read_uri_from_file(std::istream &infile) { - std::vector uris; - std::string line_uri; - while (std::getline(infile, line_uri)) { - uris.push_back(line_uri); - } - - return uris; -} -} // namespace - -namespace { -void print_version(std::ostream &out) { - out << "h2load nghttp2/" NGHTTP2_VERSION << std::endl; -} -} // namespace - -namespace { -void print_usage(std::ostream &out) { - out << R"(Usage: h2load [OPTIONS]... [URI]... -benchmarking tool for HTTP/2 and SPDY server)" << std::endl; -} -} // namespace - -namespace { -void print_help(std::ostream &out) { - print_usage(out); - - out << R"( - Specify URI to access. Multiple URIs can be specified. - URIs are used in this order for each client. All URIs - are used, then first URI is used and then 2nd URI, and - so on. The scheme, host and port in the subsequent - URIs, if present, are ignored. Those in the first URI - are used solely. -Options: - -n, --requests= - Number of requests. - Default: )" << config.nreqs << R"( - -c, --clients= - Number of concurrent clients. - Default: )" << config.nclients << R"( - -t, --threads= - Number of native threads. - Default: )" << config.nthreads << R"( - -i, --input-file= - Path of a file with multiple URIs are separated by EOLs. - This option will disable URIs getting from command-line. - If '-' is given as , URIs will be read from stdin. - URIs are used in this order for each client. All URIs - are used, then first URI is used and then 2nd URI, and - so on. The scheme, host and port in the subsequent - URIs, if present, are ignored. Those in the first URI - are used solely. - -m, --max-concurrent-streams=(auto|) - Max concurrent streams to issue per session. If "auto" - is given, the number of given URIs is used. - Default: auto - -w, --window-bits= - Sets the stream level initial window size to (2**)-1. - For SPDY, 2** is used instead. - Default: )" << config.window_bits << R"( - -W, --connection-window-bits= - Sets the connection level initial window size to - (2**)-1. For SPDY, if is strictly less than 16, - this option is ignored. Otherwise 2** is used for - SPDY. - Default: )" << config.connection_window_bits << R"( - -H, --header=
      - Add/Override a header to the requests. - -p, --no-tls-proto= - Specify ALPN identifier of the protocol to be used when - accessing http URI without SSL/TLS.)"; -#ifdef HAVE_SPDYLAY - out << R"( - Available protocols: spdy/2, spdy/3, spdy/3.1 and )"; -#else // !HAVE_SPDYLAY - out << R"( - Available protocol: )"; -#endif // !HAVE_SPDYLAY - out << NGHTTP2_CLEARTEXT_PROTO_VERSION_ID << R"( - Default: )" << NGHTTP2_CLEARTEXT_PROTO_VERSION_ID << R"( - -d, --data= - Post FILE to server. The request method is changed to - POST. - -v, --verbose - Output debug information. - --version Display version information and exit. - -h, --help Display this help and exit.)" << std::endl; -} -} // namespace - -int main(int argc, char **argv) { - std::string datafile; - while (1) { - static int flag = 0; - static option long_options[] = { - {"requests", required_argument, nullptr, 'n'}, - {"clients", required_argument, nullptr, 'c'}, - {"data", required_argument, nullptr, 'd'}, - {"threads", required_argument, nullptr, 't'}, - {"max-concurrent-streams", required_argument, nullptr, 'm'}, - {"window-bits", required_argument, nullptr, 'w'}, - {"connection-window-bits", required_argument, nullptr, 'W'}, - {"input-file", required_argument, nullptr, 'i'}, - {"header", required_argument, nullptr, 'H'}, - {"no-tls-proto", required_argument, nullptr, 'p'}, - {"verbose", no_argument, nullptr, 'v'}, - {"help", no_argument, nullptr, 'h'}, - {"version", no_argument, &flag, 1}, - {nullptr, 0, nullptr, 0}}; - int option_index = 0; - auto c = getopt_long(argc, argv, "hvW:c:d:m:n:p:t:w:H:i:", long_options, - &option_index); - if (c == -1) { - break; - } - switch (c) { - case 'n': - config.nreqs = strtoul(optarg, nullptr, 10); - break; - case 'c': - config.nclients = strtoul(optarg, nullptr, 10); - break; - case 'd': - datafile = optarg; - break; - case 't': -#ifdef NOTHREADS - std::cerr << "-t: WARNING: Threading disabled at build time, " - << "no threads created." << std::endl; -#else - config.nthreads = strtoul(optarg, nullptr, 10); -#endif // NOTHREADS - break; - case 'm': - if (util::strieq("auto", optarg)) { - config.max_concurrent_streams = -1; - } else { - config.max_concurrent_streams = strtoul(optarg, nullptr, 10); - } - break; - case 'w': - case 'W': { - errno = 0; - char *endptr = nullptr; - auto n = strtoul(optarg, &endptr, 10); - if (errno == 0 && *endptr == '\0' && n < 31) { - if (c == 'w') { - config.window_bits = n; - } else { - config.connection_window_bits = n; - } - } else { - std::cerr << "-" << static_cast(c) - << ": specify the integer in the range [0, 30], inclusive" - << std::endl; - exit(EXIT_FAILURE); - } - break; - } - case 'H': { - char *header = optarg; - // Skip first possible ':' in the header name - char *value = strchr(optarg + 1, ':'); - if (!value || (header[0] == ':' && header + 1 == value)) { - std::cerr << "-H: invalid header: " << optarg << std::endl; - exit(EXIT_FAILURE); - } - *value = 0; - value++; - while (isspace(*value)) { - value++; - } - if (*value == 0) { - // This could also be a valid case for suppressing a header - // similar to curl - std::cerr << "-H: invalid header - value missing: " << optarg - << std::endl; - exit(EXIT_FAILURE); - } - // Note that there is no processing currently to handle multiple - // message-header fields with the same field name - config.custom_headers.emplace_back(header, value); - util::inp_strlower(config.custom_headers.back().name); - break; - } - case 'i': { - config.ifile = std::string(optarg); - break; - } - case 'p': - if (util::strieq(NGHTTP2_CLEARTEXT_PROTO_VERSION_ID, optarg)) { - config.no_tls_proto = Config::PROTO_HTTP2; -#ifdef HAVE_SPDYLAY - } else if (util::strieq("spdy/2", optarg)) { - config.no_tls_proto = Config::PROTO_SPDY2; - } else if (util::strieq("spdy/3", optarg)) { - config.no_tls_proto = Config::PROTO_SPDY3; - } else if (util::strieq("spdy/3.1", optarg)) { - config.no_tls_proto = Config::PROTO_SPDY3_1; -#endif // HAVE_SPDYLAY - } else { - std::cerr << "-p: unsupported protocol " << optarg << std::endl; - exit(EXIT_FAILURE); - } - break; - case 'v': - config.verbose = true; - break; - case 'h': - print_help(std::cout); - exit(EXIT_SUCCESS); - case '?': - util::show_candidates(argv[optind - 1], long_options); - exit(EXIT_FAILURE); - case 0: - switch (flag) { - case 1: - // version option - print_version(std::cout); - exit(EXIT_SUCCESS); - } - break; - default: - break; - } - } - - if (argc == optind) { - if (config.ifile.empty()) { - std::cerr << "no URI or input file given" << std::endl; - exit(EXIT_FAILURE); - } - } - - if (config.nreqs == 0) { - std::cerr << "-n: the number of requests must be strictly greater than 0." - << std::endl; - exit(EXIT_FAILURE); - } - - if (config.max_concurrent_streams == 0) { - std::cerr << "-m: the max concurrent streams must be strictly greater " - << "than 0." << std::endl; - exit(EXIT_FAILURE); - } - - if (config.nthreads == 0) { - std::cerr << "-t: the number of threads must be strictly greater than 0." - << std::endl; - exit(EXIT_FAILURE); - } - - if (config.nreqs < config.nclients) { - std::cerr << "-n, -c: the number of requests must be greater than or " - << "equal to the concurrent clients." << std::endl; - exit(EXIT_FAILURE); - } - - if (config.nclients < config.nthreads) { - std::cerr << "-c, -t: the number of client must be greater than or equal " - "to the number of threads." << std::endl; - exit(EXIT_FAILURE); - } - - if (config.nthreads > std::thread::hardware_concurrency()) { - std::cerr << "-t: warning: the number of threads is greater than hardware " - << "cores." << std::endl; - } - - if (!datafile.empty()) { - config.data_fd = open(datafile.c_str(), O_RDONLY | O_BINARY); - if (config.data_fd == -1) { - std::cerr << "-d: Could not open file " << datafile << std::endl; - exit(EXIT_FAILURE); - } - struct stat data_stat; - if (fstat(config.data_fd, &data_stat) == -1) { - std::cerr << "-d: Could not stat file " << datafile << std::endl; - exit(EXIT_FAILURE); - } - config.data_length = data_stat.st_size; - } - - struct sigaction act; - memset(&act, 0, sizeof(struct sigaction)); - act.sa_handler = SIG_IGN; - sigaction(SIGPIPE, &act, nullptr); - OPENSSL_config(nullptr); - OpenSSL_add_all_algorithms(); - SSL_load_error_strings(); - SSL_library_init(); - -#ifndef NOTHREADS - ssl::LibsslGlobalLock lock; -#endif // NOTHREADS - - auto ssl_ctx = SSL_CTX_new(SSLv23_client_method()); - if (!ssl_ctx) { - std::cerr << "Failed to create SSL_CTX: " - << ERR_error_string(ERR_get_error(), nullptr) << std::endl; - exit(EXIT_FAILURE); - } - - SSL_CTX_set_options(ssl_ctx, - SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | - SSL_OP_NO_COMPRESSION | - SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION); - SSL_CTX_set_mode(ssl_ctx, SSL_MODE_AUTO_RETRY); - SSL_CTX_set_mode(ssl_ctx, SSL_MODE_RELEASE_BUFFERS); - - if (SSL_CTX_set_cipher_list(ssl_ctx, ssl::DEFAULT_CIPHER_LIST) == 0) { - std::cerr << "SSL_CTX_set_cipher_list failed: " - << ERR_error_string(ERR_get_error(), nullptr) << std::endl; - exit(EXIT_FAILURE); - } - - SSL_CTX_set_next_proto_select_cb(ssl_ctx, client_select_next_proto_cb, - nullptr); - -#if OPENSSL_VERSION_NUMBER >= 0x10002000L - auto proto_list = util::get_default_alpn(); -#ifdef HAVE_SPDYLAY - static const char spdy_proto_list[] = "\x8spdy/3.1\x6spdy/3\x6spdy/2"; - std::copy_n(spdy_proto_list, sizeof(spdy_proto_list) - 1, - std::back_inserter(proto_list)); -#endif // HAVE_SPDYLAY - SSL_CTX_set_alpn_protos(ssl_ctx, proto_list.data(), proto_list.size()); -#endif // OPENSSL_VERSION_NUMBER >= 0x10002000L - - std::vector reqlines; - - if (config.ifile.empty()) { - std::vector uris; - std::copy(&argv[optind], &argv[argc], std::back_inserter(uris)); - reqlines = parse_uris(std::begin(uris), std::end(uris)); - } else { - std::vector uris; - if (config.ifile == "-") { - uris = read_uri_from_file(std::cin); - } else { - std::ifstream infile(config.ifile); - if (!infile) { - std::cerr << "cannot read input file: " << config.ifile << std::endl; - exit(EXIT_FAILURE); - } - - uris = read_uri_from_file(infile); - } - - reqlines = parse_uris(std::begin(uris), std::end(uris)); - } - - if (config.max_concurrent_streams == -1) { - config.max_concurrent_streams = reqlines.size(); - } - - Headers shared_nva; - shared_nva.emplace_back(":scheme", config.scheme); - if (config.port != config.default_port) { - shared_nva.emplace_back(":authority", - config.host + ":" + util::utos(config.port)); - } else { - shared_nva.emplace_back(":authority", config.host); - } - shared_nva.emplace_back(":method", config.data_fd == -1 ? "GET" : "POST"); - - // list overridalbe headers - auto override_hdrs = - make_array(":authority", ":host", ":method", ":scheme"); - - for (auto &kv : config.custom_headers) { - if (std::find(std::begin(override_hdrs), std::end(override_hdrs), - kv.name) != std::end(override_hdrs)) { - // override header - for (auto &nv : shared_nva) { - if ((nv.name == ":authority" && kv.name == ":host") || - (nv.name == kv.name)) { - nv.value = kv.value; - } - } - } else { - // add additional headers - shared_nva.push_back(kv); - } - } - - for (auto &req : reqlines) { - // For nghttp2 - std::vector nva; - - nva.push_back(http2::make_nv_ls(":path", req)); - - for (auto &nv : shared_nva) { - nva.push_back(http2::make_nv(nv.name, nv.value, false)); - } - - config.nva.push_back(std::move(nva)); - - // For spdylay - std::vector cva; - - cva.push_back(":path"); - cva.push_back(req.c_str()); - - for (auto &nv : shared_nva) { - if (nv.name == ":authority") { - cva.push_back(":host"); - } else { - cva.push_back(nv.name.c_str()); - } - cva.push_back(nv.value.c_str()); - } - cva.push_back(":version"); - cva.push_back("HTTP/1.1"); - cva.push_back(nullptr); - - config.nv.push_back(std::move(cva)); - } - - resolve_host(); - - if (config.nclients == 1) { - config.nthreads = 1; - } - - size_t nreqs_per_thread = config.nreqs / config.nthreads; - ssize_t nreqs_rem = config.nreqs % config.nthreads; - - size_t nclients_per_thread = config.nclients / config.nthreads; - ssize_t nclients_rem = config.nclients % config.nthreads; - - std::cout << "starting benchmark..." << std::endl; - - auto start = std::chrono::steady_clock::now(); - - std::vector> workers; - workers.reserve(config.nthreads); - -#ifndef NOTHREADS - std::vector> futures; - for (size_t i = 0; i < config.nthreads - 1; ++i) { - auto nreqs = nreqs_per_thread + (nreqs_rem-- > 0); - auto nclients = nclients_per_thread + (nclients_rem-- > 0); - std::cout << "spawning thread #" << i << ": " << nclients - << " concurrent clients, " << nreqs << " total requests" - << std::endl; - workers.push_back( - make_unique(i, ssl_ctx, nreqs, nclients, &config)); - auto &worker = workers.back(); - futures.push_back( - std::async(std::launch::async, [&worker]() { worker->run(); })); - } -#endif // NOTHREADS - - auto nreqs_last = nreqs_per_thread + (nreqs_rem-- > 0); - auto nclients_last = nclients_per_thread + (nclients_rem-- > 0); - std::cout << "spawning thread #" << (config.nthreads - 1) << ": " - << nclients_last << " concurrent clients, " << nreqs_last - << " total requests" << std::endl; - workers.push_back(make_unique(config.nthreads - 1, ssl_ctx, - nreqs_last, nclients_last, &config)); - workers.back()->run(); - -#ifndef NOTHREADS - for (auto &fut : futures) { - fut.get(); - } -#endif // NOTHREADS - - auto end = std::chrono::steady_clock::now(); - auto duration = - std::chrono::duration_cast(end - start); - - Stats stats(0); - for (const auto &w : workers) { - const auto &s = w->stats; - - stats.req_todo += s.req_todo; - stats.req_started += s.req_started; - stats.req_done += s.req_done; - stats.req_success += s.req_success; - stats.req_status_success += s.req_status_success; - stats.req_failed += s.req_failed; - stats.req_error += s.req_error; - stats.bytes_total += s.bytes_total; - stats.bytes_head += s.bytes_head; - stats.bytes_body += s.bytes_body; - - for (size_t i = 0; i < stats.status.size(); ++i) { - stats.status[i] += s.status[i]; - } - } - - auto ts = process_time_stats(workers); - - // Requests which have not been issued due to connection errors, are - // counted towards req_failed and req_error. - auto req_not_issued = - stats.req_todo - stats.req_status_success - stats.req_failed; - stats.req_failed += req_not_issued; - stats.req_error += req_not_issued; - - // UI is heavily inspired by weighttp[1] and wrk[2] - // - // [1] https://github.com/lighttpd/weighttp - // [2] https://github.com/wg/wrk - size_t rps = 0; - int64_t bps = 0; - if (duration.count() > 0) { - auto secd = static_cast(duration.count()) / (1000 * 1000); - rps = stats.req_success / secd; - bps = stats.bytes_total / secd; - } - - std::cout << R"( -finished in )" << util::format_duration(duration) << ", " << rps << " req/s, " - << util::utos_with_funit(bps) << R"(B/s -requests: )" << stats.req_todo << " total, " << stats.req_started - << " started, " << stats.req_done << " done, " - << stats.req_status_success << " succeeded, " << stats.req_failed - << " failed, " << stats.req_error << R"( errored -status codes: )" << stats.status[2] << " 2xx, " << stats.status[3] << " 3xx, " - << stats.status[4] << " 4xx, " << stats.status[5] << R"( 5xx -traffic: )" << stats.bytes_total << " bytes total, " << stats.bytes_head - << " bytes headers, " << stats.bytes_body << R"( bytes data - min max mean sd +/- sd -time for request: )" << std::setw(10) << util::format_duration(ts.request.min) - << " " << std::setw(10) << util::format_duration(ts.request.max) - << " " << std::setw(10) << util::format_duration(ts.request.mean) - << " " << std::setw(10) << util::format_duration(ts.request.sd) - << std::setw(9) << util::dtos(ts.request.within_sd) << "%" - << "\ntime for connect: " << std::setw(10) - << util::format_duration(ts.connect.min) << " " << std::setw(10) - << util::format_duration(ts.connect.max) << " " << std::setw(10) - << util::format_duration(ts.connect.mean) << " " << std::setw(10) - << util::format_duration(ts.connect.sd) << std::setw(9) - << util::dtos(ts.connect.within_sd) << "%" - << "\ntime to 1st byte: " << std::setw(10) - << util::format_duration(ts.ttfb.min) << " " << std::setw(10) - << util::format_duration(ts.ttfb.max) << " " << std::setw(10) - << util::format_duration(ts.ttfb.mean) << " " << std::setw(10) - << util::format_duration(ts.ttfb.sd) << std::setw(9) - << util::dtos(ts.ttfb.within_sd) << "%" << std::endl; - - SSL_CTX_free(ssl_ctx); - - return 0; -} - -} // namespace h2load - -int main(int argc, char **argv) { return h2load::main(argc, argv); } diff --git a/src/h2load.h b/src/h2load.h deleted file mode 100644 index 78a2038..0000000 --- a/src/h2load.h +++ /dev/null @@ -1,249 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2014 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#ifndef H2LOAD_H -#define H2LOAD_H - -#include "nghttp2_config.h" - -#include -#ifdef HAVE_SYS_SOCKET_H -#include -#endif // HAVE_SYS_SOCKET_H -#ifdef HAVE_NETDB_H -#include -#endif // HAVE_NETDB_H - -#include -#include -#include -#include -#include -#include - -#include - -#include - -#include - -#include "http2.h" -#include "buffer.h" - -using namespace nghttp2; - -namespace h2load { - -class Session; - -struct Config { - std::vector> nva; - std::vector> nv; - nghttp2::Headers custom_headers; - std::string scheme; - std::string host; - std::string ifile; - // length of upload data - int64_t data_length; - addrinfo *addrs; - size_t nreqs; - size_t nclients; - size_t nthreads; - // The maximum number of concurrent streams per session. - ssize_t max_concurrent_streams; - size_t window_bits; - size_t connection_window_bits; - enum { PROTO_HTTP2, PROTO_SPDY2, PROTO_SPDY3, PROTO_SPDY3_1 } no_tls_proto; - // file descriptor for upload data - int data_fd; - uint16_t port; - uint16_t default_port; - bool verbose; - - Config(); - ~Config(); -}; - -struct RequestStat { - RequestStat(); - // time point when request was sent - std::chrono::steady_clock::time_point request_time; - // time point when stream was closed - std::chrono::steady_clock::time_point stream_close_time; - // upload data length sent so far - int64_t data_offset; - // true if stream was successfully closed. This means stream was - // not reset, but it does not mean HTTP level error (e.g., 404). - bool completed; -}; - -template struct TimeStat { - // min, max, mean and sd (standard deviation) - Duration min, max, mean, sd; - // percentage of samples inside mean -/+ sd - double within_sd; -}; - -struct TimeStats { - // time for request - TimeStat request; - // time for connect - TimeStat connect; - // time to first byte (TTFB) - TimeStat ttfb; -}; - -enum TimeStatType { STAT_REQUEST, STAT_CONNECT, STAT_FIRST_BYTE }; - -struct Stats { - Stats(size_t req_todo); - // The total number of requests - size_t req_todo; - // The number of requests issued so far - size_t req_started; - // The number of requests finished - size_t req_done; - // The number of requests completed successfull, but not necessarily - // means successful HTTP status code. - size_t req_success; - // The number of requests marked as success. HTTP status code is - // also considered as success. This is subset of req_done. - size_t req_status_success; - // The number of requests failed. This is subset of req_done. - size_t req_failed; - // The number of requests failed due to network errors. This is - // subset of req_failed. - size_t req_error; - // The number of bytes received on the "wire". If SSL/TLS is used, - // this is the number of decrypted bytes the application received. - int64_t bytes_total; - // The number of bytes received in HEADERS frame payload. - int64_t bytes_head; - // The number of bytes received in DATA frame. - int64_t bytes_body; - // The number of each HTTP status category, status[i] is status code - // in the range [i*100, (i+1)*100). - std::array status; - // The statistics per request - std::vector req_stats; - // time connect starts - std::vector start_times; - // time to connect - std::vector connect_times; - // time to first byte (TTFB) - std::vector ttfbs; -}; - -enum ClientState { CLIENT_IDLE, CLIENT_CONNECTED }; - -struct Client; - -struct Worker { - std::vector> clients; - Stats stats; - struct ev_loop *loop; - SSL_CTX *ssl_ctx; - Config *config; - size_t progress_interval; - uint32_t id; - bool tls_info_report_done; - - Worker(uint32_t id, SSL_CTX *ssl_ctx, size_t nreq_todo, size_t nclients, - Config *config); - ~Worker(); - Worker(Worker &&o) = default; - void run(); -}; - -struct Stream { - int status_success; - Stream(); -}; - -struct Client { - std::unordered_map streams; - std::unique_ptr session; - ev_io wev; - ev_io rev; - std::function readfn, writefn; - Worker *worker; - SSL *ssl; - addrinfo *next_addr; - size_t reqidx; - ClientState state; - bool first_byte_received; - // The number of requests this client has to issue. - size_t req_todo; - // The number of requests this client has issued so far. - size_t req_started; - // The number of requests this client has done so far. - size_t req_done; - int fd; - Buffer<65536> wb; - - enum { ERR_CONNECT_FAIL = -100 }; - - Client(Worker *worker, size_t req_todo); - ~Client(); - int connect(); - void disconnect(); - void fail(); - void submit_request(); - void process_abandoned_streams(); - void report_progress(); - void report_tls_info(); - void terminate_session(); - - int do_read(); - int do_write(); - - // low-level I/O callback functions called by do_read/do_write - int connected(); - int read_clear(); - int write_clear(); - int tls_handshake(); - int read_tls(); - int write_tls(); - - int on_read(const uint8_t *data, size_t len); - int on_write(); - - int connection_made(); - - void on_request(int32_t stream_id); - void on_header(int32_t stream_id, const uint8_t *name, size_t namelen, - const uint8_t *value, size_t valuelen); - void on_stream_close(int32_t stream_id, bool success, RequestStat *req_stat); - - void record_request_time(RequestStat *req_stat); - void record_start_time(Stats *stat); - void record_connect_time(Stats *stat); - void record_ttfb(Stats *stat); - - void signal_write(); -}; - -} // namespace h2load - -#endif // H2LOAD_H diff --git a/src/h2load_http2_session.cc b/src/h2load_http2_session.cc deleted file mode 100644 index 0cbb702..0000000 --- a/src/h2load_http2_session.cc +++ /dev/null @@ -1,260 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2014 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#include "h2load_http2_session.h" - -#include -#include - -#include "h2load.h" -#include "util.h" -#include "template.h" - -using namespace nghttp2; - -namespace h2load { - -Http2Session::Http2Session(Client *client) - : client_(client), session_(nullptr) {} - -Http2Session::~Http2Session() { nghttp2_session_del(session_); } - -namespace { -int on_header_callback(nghttp2_session *session, const nghttp2_frame *frame, - const uint8_t *name, size_t namelen, - const uint8_t *value, size_t valuelen, uint8_t flags, - void *user_data) { - auto client = static_cast(user_data); - if (frame->hd.type != NGHTTP2_HEADERS || - frame->headers.cat != NGHTTP2_HCAT_RESPONSE) { - return 0; - } - client->on_header(frame->hd.stream_id, name, namelen, value, valuelen); - return 0; -} -} // namespace - -namespace { -int on_frame_recv_callback(nghttp2_session *session, const nghttp2_frame *frame, - void *user_data) { - auto client = static_cast(user_data); - if (frame->hd.type != NGHTTP2_HEADERS || - frame->headers.cat != NGHTTP2_HCAT_RESPONSE) { - return 0; - } - client->worker->stats.bytes_head += frame->hd.length; - return 0; -} -} // namespace - -namespace { -int on_data_chunk_recv_callback(nghttp2_session *session, uint8_t flags, - int32_t stream_id, const uint8_t *data, - size_t len, void *user_data) { - auto client = static_cast(user_data); - client->worker->stats.bytes_body += len; - return 0; -} -} // namespace - -namespace { -int on_stream_close_callback(nghttp2_session *session, int32_t stream_id, - uint32_t error_code, void *user_data) { - auto client = static_cast(user_data); - auto req_stat = static_cast( - nghttp2_session_get_stream_user_data(session, stream_id)); - if (!req_stat) { - return 0; - } - client->on_stream_close(stream_id, error_code == NGHTTP2_NO_ERROR, req_stat); - return 0; -} -} // namespace - -namespace { -int before_frame_send_callback(nghttp2_session *session, - const nghttp2_frame *frame, void *user_data) { - if (frame->hd.type != NGHTTP2_HEADERS || - frame->headers.cat != NGHTTP2_HCAT_REQUEST) { - return 0; - } - - auto client = static_cast(user_data); - client->on_request(frame->hd.stream_id); - auto req_stat = static_cast( - nghttp2_session_get_stream_user_data(session, frame->hd.stream_id)); - assert(req_stat); - client->record_request_time(req_stat); - - return 0; -} -} // namespace - -namespace { -ssize_t file_read_callback(nghttp2_session *session, int32_t stream_id, - uint8_t *buf, size_t length, uint32_t *data_flags, - nghttp2_data_source *source, void *user_data) { - auto client = static_cast(user_data); - auto config = client->worker->config; - auto req_stat = static_cast( - nghttp2_session_get_stream_user_data(session, stream_id)); - assert(req_stat); - ssize_t nread; - while ((nread = pread(config->data_fd, buf, length, req_stat->data_offset)) == - -1 && - errno == EINTR) - ; - - if (nread == -1) { - return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; - } - - req_stat->data_offset += nread; - - if (nread == 0 || req_stat->data_offset == config->data_length) { - *data_flags |= NGHTTP2_DATA_FLAG_EOF; - } - - return nread; -} - -} // namespace - -namespace { -ssize_t send_callback(nghttp2_session *session, const uint8_t *data, - size_t length, int flags, void *user_data) { - auto client = static_cast(user_data); - auto &wb = client->wb; - - if (wb.wleft() == 0) { - return NGHTTP2_ERR_WOULDBLOCK; - } - - return wb.write(data, length); -} -} // namespace - -void Http2Session::on_connect() { - int rv; - - nghttp2_session_callbacks *callbacks; - - nghttp2_session_callbacks_new(&callbacks); - - auto callbacks_deleter = defer(nghttp2_session_callbacks_del, callbacks); - - nghttp2_session_callbacks_set_on_frame_recv_callback(callbacks, - on_frame_recv_callback); - - nghttp2_session_callbacks_set_on_data_chunk_recv_callback( - callbacks, on_data_chunk_recv_callback); - - nghttp2_session_callbacks_set_on_stream_close_callback( - callbacks, on_stream_close_callback); - - nghttp2_session_callbacks_set_on_header_callback(callbacks, - on_header_callback); - - nghttp2_session_callbacks_set_before_frame_send_callback( - callbacks, before_frame_send_callback); - - nghttp2_session_callbacks_set_send_callback(callbacks, send_callback); - - nghttp2_session_client_new(&session_, callbacks, client_); - - std::array iv; - iv[0].settings_id = NGHTTP2_SETTINGS_ENABLE_PUSH; - iv[0].value = 0; - iv[1].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE; - iv[1].value = (1 << client_->worker->config->window_bits) - 1; - - rv = nghttp2_submit_settings(session_, NGHTTP2_FLAG_NONE, iv.data(), - iv.size()); - - assert(rv == 0); - - auto extra_connection_window = - (1 << client_->worker->config->connection_window_bits) - 1 - - NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE; - if (extra_connection_window != 0) { - nghttp2_submit_window_update(session_, NGHTTP2_FLAG_NONE, 0, - extra_connection_window); - } - - client_->signal_write(); -} - -void Http2Session::submit_request(RequestStat *req_stat) { - auto config = client_->worker->config; - auto &nva = config->nva[client_->reqidx++]; - - if (client_->reqidx == config->nva.size()) { - client_->reqidx = 0; - } - - nghttp2_data_provider prd{{0}, file_read_callback}; - - auto stream_id = - nghttp2_submit_request(session_, nullptr, nva.data(), nva.size(), - config->data_fd == -1 ? nullptr : &prd, req_stat); - assert(stream_id > 0); -} - -int Http2Session::on_read(const uint8_t *data, size_t len) { - auto rv = nghttp2_session_mem_recv(session_, data, len); - if (rv < 0) { - return -1; - } - - assert(static_cast(rv) == len); - - if (nghttp2_session_want_read(session_) == 0 && - nghttp2_session_want_write(session_) == 0 && client_->wb.rleft() == 0) { - return -1; - } - - client_->signal_write(); - - return 0; -} - -int Http2Session::on_write() { - auto rv = nghttp2_session_send(session_); - if (rv != 0) { - return -1; - } - - if (nghttp2_session_want_read(session_) == 0 && - nghttp2_session_want_write(session_) == 0 && client_->wb.rleft() == 0) { - return -1; - } - - return 0; -} - -void Http2Session::terminate() { - nghttp2_session_terminate_session(session_, NGHTTP2_NO_ERROR); -} - -} // namespace h2load diff --git a/src/h2load_http2_session.h b/src/h2load_http2_session.h deleted file mode 100644 index a06e411..0000000 --- a/src/h2load_http2_session.h +++ /dev/null @@ -1,53 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2014 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#ifndef H2LOAD_HTTP2_SESSION_H -#define H2LOAD_HTTP2_SESSION_H - -#include "h2load_session.h" - -#include - -namespace h2load { - -struct Client; - -class Http2Session : public Session { -public: - Http2Session(Client *client); - virtual ~Http2Session(); - virtual void on_connect(); - virtual void submit_request(RequestStat *req_stat); - virtual int on_read(const uint8_t *data, size_t len); - virtual int on_write(); - virtual void terminate(); - -private: - Client *client_; - nghttp2_session *session_; -}; - -} // namespace h2load - -#endif // H2LOAD_HTTP2_SESSION_H diff --git a/src/h2load_session.h b/src/h2load_session.h deleted file mode 100644 index 1817574..0000000 --- a/src/h2load_session.h +++ /dev/null @@ -1,57 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2014 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#ifndef H2LOAD_SESSION_H -#define H2LOAD_SESSION_H - -#include "nghttp2_config.h" - -#include - -#include - -#include "h2load.h" - -namespace h2load { - -class Session { -public: - virtual ~Session() {} - // Called when the connection was made. - virtual void on_connect() = 0; - // Called when one request must be issued. - virtual void submit_request(RequestStat *req_stat) = 0; - // Called when incoming bytes are available. The subclass has to - // return the number of bytes read. - virtual int on_read(const uint8_t *data, size_t len) = 0; - // Called when write is available. Returns 0 on success, otherwise - // return -1. - virtual int on_write() = 0; - // Called when the underlying session must be terminated. - virtual void terminate() = 0; -}; - -} // namespace h2load - -#endif // H2LOAD_SESSION_H diff --git a/src/h2load_spdy_session.cc b/src/h2load_spdy_session.cc deleted file mode 100644 index 60258b7..0000000 --- a/src/h2load_spdy_session.cc +++ /dev/null @@ -1,270 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2014 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#include "h2load_spdy_session.h" - -#include -#include - -#include "h2load.h" -#include "util.h" - -using namespace nghttp2; - -namespace h2load { - -SpdySession::SpdySession(Client *client, uint16_t spdy_version) - : client_(client), session_(nullptr), spdy_version_(spdy_version) {} - -SpdySession::~SpdySession() { spdylay_session_del(session_); } - -namespace { -void before_ctrl_send_callback(spdylay_session *session, - spdylay_frame_type type, spdylay_frame *frame, - void *user_data) { - auto client = static_cast(user_data); - if (type != SPDYLAY_SYN_STREAM) { - return; - } - client->on_request(frame->syn_stream.stream_id); - auto req_stat = - static_cast(spdylay_session_get_stream_user_data( - session, frame->syn_stream.stream_id)); - client->record_request_time(req_stat); -} -} // namespace - -namespace { -void on_ctrl_recv_callback(spdylay_session *session, spdylay_frame_type type, - spdylay_frame *frame, void *user_data) { - auto client = static_cast(user_data); - if (type != SPDYLAY_SYN_REPLY) { - return; - } - for (auto p = frame->syn_reply.nv; *p; p += 2) { - auto name = *p; - auto value = *(p + 1); - client->on_header(frame->syn_reply.stream_id, - reinterpret_cast(name), strlen(name), - reinterpret_cast(value), strlen(value)); - } - client->worker->stats.bytes_head += frame->syn_reply.hd.length; -} -} // namespace - -namespace { -void on_data_chunk_recv_callback(spdylay_session *session, uint8_t flags, - int32_t stream_id, const uint8_t *data, - size_t len, void *user_data) { - auto client = static_cast(user_data); - client->worker->stats.bytes_body += len; - - auto spdy_session = static_cast(client->session.get()); - - spdy_session->handle_window_update(stream_id, len); -} -} // namespace - -namespace { -void on_stream_close_callback(spdylay_session *session, int32_t stream_id, - spdylay_status_code status_code, - void *user_data) { - auto client = static_cast(user_data); - auto req_stat = static_cast( - spdylay_session_get_stream_user_data(session, stream_id)); - client->on_stream_close(stream_id, status_code == SPDYLAY_OK, req_stat); -} -} // namespace - -namespace { -ssize_t send_callback(spdylay_session *session, const uint8_t *data, - size_t length, int flags, void *user_data) { - auto client = static_cast(user_data); - auto &wb = client->wb; - - if (wb.wleft() == 0) { - return SPDYLAY_ERR_DEFERRED; - } - - return wb.write(data, length); -} -} // namespace - -namespace { -ssize_t file_read_callback(spdylay_session *session, int32_t stream_id, - uint8_t *buf, size_t length, int *eof, - spdylay_data_source *source, void *user_data) { - auto client = static_cast(user_data); - auto config = client->worker->config; - auto req_stat = static_cast( - spdylay_session_get_stream_user_data(session, stream_id)); - - ssize_t nread; - while ((nread = pread(config->data_fd, buf, length, req_stat->data_offset)) == - -1 && - errno == EINTR) - ; - - if (nread == -1) { - return SPDYLAY_ERR_TEMPORAL_CALLBACK_FAILURE; - } - - req_stat->data_offset += nread; - - if (nread == 0 || req_stat->data_offset == config->data_length) { - *eof = 1; - } - - return nread; -} -} // namespace - -void SpdySession::on_connect() { - spdylay_session_callbacks callbacks = {0}; - callbacks.send_callback = send_callback; - callbacks.before_ctrl_send_callback = before_ctrl_send_callback; - callbacks.on_data_chunk_recv_callback = on_data_chunk_recv_callback; - callbacks.on_stream_close_callback = on_stream_close_callback; - callbacks.on_ctrl_recv_callback = on_ctrl_recv_callback; - - spdylay_session_client_new(&session_, spdy_version_, &callbacks, client_); - - int val = 1; - spdylay_session_set_option(session_, SPDYLAY_OPT_NO_AUTO_WINDOW_UPDATE, &val, - sizeof(val)); - - spdylay_settings_entry iv; - iv.settings_id = SPDYLAY_SETTINGS_INITIAL_WINDOW_SIZE; - iv.flags = SPDYLAY_ID_FLAG_SETTINGS_NONE; - iv.value = (1 << client_->worker->config->window_bits); - spdylay_submit_settings(session_, SPDYLAY_FLAG_SETTINGS_NONE, &iv, 1); - - auto config = client_->worker->config; - - if (spdy_version_ >= SPDYLAY_PROTO_SPDY3_1 && - config->connection_window_bits > 16) { - auto delta = - (1 << config->connection_window_bits) - SPDYLAY_INITIAL_WINDOW_SIZE; - spdylay_submit_window_update(session_, 0, delta); - } - - client_->signal_write(); -} - -void SpdySession::submit_request(RequestStat *req_stat) { - auto config = client_->worker->config; - auto &nv = config->nv[client_->reqidx++]; - - if (client_->reqidx == config->nv.size()) { - client_->reqidx = 0; - } - - spdylay_data_provider prd{{0}, file_read_callback}; - - spdylay_submit_request(session_, 0, nv.data(), - config->data_fd == -1 ? nullptr : &prd, req_stat); -} - -int SpdySession::on_read(const uint8_t *data, size_t len) { - auto rv = spdylay_session_mem_recv(session_, data, len); - if (rv < 0) { - return -1; - } - - assert(static_cast(rv) == len); - - if (spdylay_session_want_read(session_) == 0 && - spdylay_session_want_write(session_) == 0 && client_->wb.rleft() == 0) { - return -1; - } - - client_->signal_write(); - - return 0; -} - -int SpdySession::on_write() { - auto rv = spdylay_session_send(session_); - if (rv != 0) { - return -1; - } - - if (spdylay_session_want_read(session_) == 0 && - spdylay_session_want_write(session_) == 0 && client_->wb.rleft() == 0) { - return -1; - } - return 0; -} - -void SpdySession::terminate() { - spdylay_session_fail_session(session_, SPDYLAY_OK); -} - -namespace { -int32_t determine_window_update_transmission(spdylay_session *session, - int32_t stream_id, - size_t window_bits) { - int32_t recv_length; - - if (stream_id == 0) { - recv_length = spdylay_session_get_recv_data_length(session); - } else { - recv_length = - spdylay_session_get_stream_recv_data_length(session, stream_id); - } - - auto window_size = 1 << window_bits; - - if (recv_length != -1 && recv_length >= window_size / 2) { - return recv_length; - } - - return -1; -} -} // namespace - -void SpdySession::handle_window_update(int32_t stream_id, size_t recvlen) { - auto config = client_->worker->config; - size_t connection_window_bits; - - if (config->connection_window_bits > 16) { - connection_window_bits = config->connection_window_bits; - } else { - connection_window_bits = 16; - } - - auto delta = - determine_window_update_transmission(session_, 0, connection_window_bits); - if (delta > 0) { - spdylay_submit_window_update(session_, 0, delta); - } - - delta = determine_window_update_transmission(session_, stream_id, - config->window_bits); - if (delta > 0) { - spdylay_submit_window_update(session_, stream_id, delta); - } -} - -} // namespace h2load diff --git a/src/h2load_spdy_session.h b/src/h2load_spdy_session.h deleted file mode 100644 index f76f25a..0000000 --- a/src/h2load_spdy_session.h +++ /dev/null @@ -1,57 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2014 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#ifndef H2LOAD_SPDY_SESSION_H -#define H2LOAD_SPDY_SESSION_H - -#include "h2load_session.h" - -#include - -#include "util.h" - -namespace h2load { - -struct Client; - -class SpdySession : public Session { -public: - SpdySession(Client *client, uint16_t spdy_version); - virtual ~SpdySession(); - virtual void on_connect(); - virtual void submit_request(RequestStat *req_stat); - virtual int on_read(const uint8_t *data, size_t len); - virtual int on_write(); - virtual void terminate(); - void handle_window_update(int32_t stream_id, size_t recvlen); - -private: - Client *client_; - spdylay_session *session_; - uint16_t spdy_version_; -}; - -} // namespace h2load - -#endif // H2LOAD_SPDY_SESSION_H diff --git a/src/http2.cc b/src/http2.cc deleted file mode 100644 index 65b2cee..0000000 --- a/src/http2.cc +++ /dev/null @@ -1,1118 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2012 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#include "http2.h" - -#include "util.h" - -namespace nghttp2 { - -namespace http2 { - -std::string get_status_string(unsigned int status_code) { - switch (status_code) { - case 100: - return "100 Continue"; - case 101: - return "101 Switching Protocols"; - case 200: - return "200 OK"; - case 201: - return "201 Created"; - case 202: - return "202 Accepted"; - case 203: - return "203 Non-Authoritative Information"; - case 204: - return "204 No Content"; - case 205: - return "205 Reset Content"; - case 206: - return "206 Partial Content"; - case 300: - return "300 Multiple Choices"; - case 301: - return "301 Moved Permanently"; - case 302: - return "302 Found"; - case 303: - return "303 See Other"; - case 304: - return "304 Not Modified"; - case 305: - return "305 Use Proxy"; - // case 306: return "306 (Unused)"; - case 307: - return "307 Temporary Redirect"; - case 308: - return "308 Permanent Redirect"; - case 400: - return "400 Bad Request"; - case 401: - return "401 Unauthorized"; - case 402: - return "402 Payment Required"; - case 403: - return "403 Forbidden"; - case 404: - return "404 Not Found"; - case 405: - return "405 Method Not Allowed"; - case 406: - return "406 Not Acceptable"; - case 407: - return "407 Proxy Authentication Required"; - case 408: - return "408 Request Timeout"; - case 409: - return "409 Conflict"; - case 410: - return "410 Gone"; - case 411: - return "411 Length Required"; - case 412: - return "412 Precondition Failed"; - case 413: - return "413 Payload Too Large"; - case 414: - return "414 URI Too Long"; - case 415: - return "415 Unsupported Media Type"; - case 416: - return "416 Requested Range Not Satisfiable"; - case 417: - return "417 Expectation Failed"; - case 421: - return "421 Misdirected Request"; - case 426: - return "426 Upgrade Required"; - case 428: - return "428 Precondition Required"; - case 429: - return "429 Too Many Requests"; - case 431: - return "431 Request Header Fields Too Large"; - case 500: - return "500 Internal Server Error"; - case 501: - return "501 Not Implemented"; - case 502: - return "502 Bad Gateway"; - case 503: - return "503 Service Unavailable"; - case 504: - return "504 Gateway Timeout"; - case 505: - return "505 HTTP Version Not Supported"; - case 511: - return "511 Network Authentication Required"; - default: - return util::utos(status_code); - } -} - -void capitalize(std::string &s, size_t offset) { - s[offset] = util::upcase(s[offset]); - for (size_t i = offset + 1, eoi = s.size(); i < eoi; ++i) { - if (s[i - 1] == '-') { - s[i] = util::upcase(s[i]); - } else { - s[i] = util::lowcase(s[i]); - } - } -} - -bool lws(const char *value) { - for (; *value; ++value) { - switch (*value) { - case '\t': - case ' ': - continue; - default: - return false; - } - } - return true; -} - -void copy_url_component(std::string &dest, const http_parser_url *u, int field, - const char *url) { - if (u->field_set & (1 << field)) { - dest.assign(url + u->field_data[field].off, u->field_data[field].len); - } -} - -Headers::value_type to_header(const uint8_t *name, size_t namelen, - const uint8_t *value, size_t valuelen, - bool no_index, int16_t token) { - return Header(std::string(reinterpret_cast(name), namelen), - std::string(reinterpret_cast(value), valuelen), - no_index, token); -} - -void add_header(Headers &nva, const uint8_t *name, size_t namelen, - const uint8_t *value, size_t valuelen, bool no_index, - int16_t token) { - if (valuelen > 0) { - size_t i, j; - for (i = 0; i < valuelen && (value[i] == ' ' || value[i] == '\t'); ++i) - ; - for (j = valuelen - 1; j > i && (value[j] == ' ' || value[j] == '\t'); --j) - ; - value += i; - valuelen -= i + (valuelen - j - 1); - } - nva.push_back(to_header(name, namelen, value, valuelen, no_index, token)); -} - -const Headers::value_type *get_header(const Headers &nva, const char *name) { - const Headers::value_type *res = nullptr; - for (auto &nv : nva) { - if (nv.name == name) { - res = &nv; - } - } - return res; -} - -std::string value_to_str(const Headers::value_type *nv) { - if (nv) { - return nv->value; - } - return ""; -} - -bool non_empty_value(const Headers::value_type *nv) { - return nv && !nv->value.empty(); -} - -nghttp2_nv make_nv(const std::string &name, const std::string &value, - bool no_index) { - uint8_t flags; - - flags = no_index ? NGHTTP2_NV_FLAG_NO_INDEX : NGHTTP2_NV_FLAG_NONE; - - return {(uint8_t *)name.c_str(), (uint8_t *)value.c_str(), name.size(), - value.size(), flags}; -} - -void copy_headers_to_nva(std::vector &nva, const Headers &headers) { - for (auto &kv : headers) { - if (kv.name.empty() || kv.name[0] == ':') { - continue; - } - switch (kv.token) { - case HD_COOKIE: - case HD_CONNECTION: - case HD_HOST: - case HD_HTTP2_SETTINGS: - case HD_KEEP_ALIVE: - case HD_PROXY_CONNECTION: - case HD_SERVER: - case HD_TE: - case HD_TRANSFER_ENCODING: - case HD_UPGRADE: - case HD_VIA: - case HD_X_FORWARDED_FOR: - case HD_X_FORWARDED_PROTO: - continue; - } - nva.push_back(make_nv(kv.name, kv.value, kv.no_index)); - } -} - -void build_http1_headers_from_headers(std::string &hdrs, - const Headers &headers) { - for (auto &kv : headers) { - if (kv.name.empty() || kv.name[0] == ':') { - continue; - } - switch (kv.token) { - case HD_CONNECTION: - case HD_COOKIE: - case HD_HOST: - case HD_HTTP2_SETTINGS: - case HD_KEEP_ALIVE: - case HD_PROXY_CONNECTION: - case HD_SERVER: - case HD_UPGRADE: - case HD_VIA: - case HD_X_FORWARDED_FOR: - case HD_X_FORWARDED_PROTO: - continue; - } - hdrs += kv.name; - capitalize(hdrs, hdrs.size() - kv.name.size()); - hdrs += ": "; - hdrs += kv.value; - hdrs += "\r\n"; - } -} - -int32_t determine_window_update_transmission(nghttp2_session *session, - int32_t stream_id) { - int32_t recv_length, window_size; - if (stream_id == 0) { - recv_length = nghttp2_session_get_effective_recv_data_length(session); - window_size = nghttp2_session_get_effective_local_window_size(session); - } else { - recv_length = nghttp2_session_get_stream_effective_recv_data_length( - session, stream_id); - window_size = nghttp2_session_get_stream_effective_local_window_size( - session, stream_id); - } - if (recv_length != -1 && window_size != -1) { - if (recv_length >= window_size / 2) { - return recv_length; - } - } - return -1; -} - -void dump_nv(FILE *out, const char **nv) { - for (size_t i = 0; nv[i]; i += 2) { - fprintf(out, "%s: %s\n", nv[i], nv[i + 1]); - } - fputc('\n', out); - fflush(out); -} - -void dump_nv(FILE *out, const nghttp2_nv *nva, size_t nvlen) { - auto end = nva + nvlen; - for (; nva != end; ++nva) { - fprintf(out, "%s: %s\n", nva->name, nva->value); - } - fputc('\n', out); - fflush(out); -} - -void dump_nv(FILE *out, const Headers &nva) { - for (auto &nv : nva) { - fprintf(out, "%s: %s\n", nv.name.c_str(), nv.value.c_str()); - } - fputc('\n', out); - fflush(out); -} - -std::string rewrite_location_uri(const std::string &uri, - const http_parser_url &u, - const std::string &match_host, - const std::string &request_authority, - const std::string &upstream_scheme) { - // We just rewrite scheme and authority. - if ((u.field_set & (1 << UF_HOST)) == 0) { - return ""; - } - auto field = &u.field_data[UF_HOST]; - if (!util::startsWith(std::begin(match_host), std::end(match_host), - &uri[field->off], &uri[field->off] + field->len) || - (match_host.size() != field->len && match_host[field->len] != ':')) { - return ""; - } - std::string res; - if (!request_authority.empty()) { - res += upstream_scheme; - res += "://"; - res += request_authority; - } - if (u.field_set & (1 << UF_PATH)) { - field = &u.field_data[UF_PATH]; - res.append(&uri[field->off], field->len); - } - if (u.field_set & (1 << UF_QUERY)) { - field = &u.field_data[UF_QUERY]; - res += "?"; - res.append(&uri[field->off], field->len); - } - if (u.field_set & (1 << UF_FRAGMENT)) { - field = &u.field_data[UF_FRAGMENT]; - res += "#"; - res.append(&uri[field->off], field->len); - } - return res; -} - -int check_nv(const uint8_t *name, size_t namelen, const uint8_t *value, - size_t valuelen) { - if (!nghttp2_check_header_name(name, namelen)) { - return 0; - } - if (!nghttp2_check_header_value(value, valuelen)) { - return 0; - } - return 1; -} - -int parse_http_status_code(const std::string &src) { - if (src.size() != 3) { - return -1; - } - - int status = 0; - for (auto c : src) { - if (!isdigit(c)) { - return -1; - } - status *= 10; - status += c - '0'; - } - - if (status < 100) { - return -1; - } - - return status; -} - -int lookup_token(const std::string &name) { - return lookup_token(reinterpret_cast(name.c_str()), - name.size()); -} - -// This function was generated by genheaderfunc.py. Inspired by h2o -// header lookup. https://github.com/h2o/h2o -int lookup_token(const uint8_t *name, size_t namelen) { - switch (namelen) { - case 2: - switch (name[1]) { - case 'e': - if (util::streq_l("t", name, 1)) { - return HD_TE; - } - break; - } - break; - case 3: - switch (name[2]) { - case 'a': - if (util::streq_l("vi", name, 2)) { - return HD_VIA; - } - break; - } - break; - case 4: - switch (name[3]) { - case 'k': - if (util::streq_l("lin", name, 3)) { - return HD_LINK; - } - break; - case 't': - if (util::streq_l("hos", name, 3)) { - return HD_HOST; - } - break; - } - break; - case 5: - switch (name[4]) { - case 'h': - if (util::streq_l(":pat", name, 4)) { - return HD__PATH; - } - break; - case 't': - if (util::streq_l(":hos", name, 4)) { - return HD__HOST; - } - break; - } - break; - case 6: - switch (name[5]) { - case 'e': - if (util::streq_l("cooki", name, 5)) { - return HD_COOKIE; - } - break; - case 'r': - if (util::streq_l("serve", name, 5)) { - return HD_SERVER; - } - break; - case 't': - if (util::streq_l("expec", name, 5)) { - return HD_EXPECT; - } - break; - } - break; - case 7: - switch (name[6]) { - case 'c': - if (util::streq_l("alt-sv", name, 6)) { - return HD_ALT_SVC; - } - break; - case 'd': - if (util::streq_l(":metho", name, 6)) { - return HD__METHOD; - } - break; - case 'e': - if (util::streq_l(":schem", name, 6)) { - return HD__SCHEME; - } - if (util::streq_l("upgrad", name, 6)) { - return HD_UPGRADE; - } - break; - case 'r': - if (util::streq_l("traile", name, 6)) { - return HD_TRAILER; - } - break; - case 's': - if (util::streq_l(":statu", name, 6)) { - return HD__STATUS; - } - break; - } - break; - case 8: - switch (name[7]) { - case 'n': - if (util::streq_l("locatio", name, 7)) { - return HD_LOCATION; - } - break; - } - break; - case 10: - switch (name[9]) { - case 'e': - if (util::streq_l("keep-aliv", name, 9)) { - return HD_KEEP_ALIVE; - } - break; - case 'n': - if (util::streq_l("connectio", name, 9)) { - return HD_CONNECTION; - } - break; - case 't': - if (util::streq_l("user-agen", name, 9)) { - return HD_USER_AGENT; - } - break; - case 'y': - if (util::streq_l(":authorit", name, 9)) { - return HD__AUTHORITY; - } - break; - } - break; - case 13: - switch (name[12]) { - case 'l': - if (util::streq_l("cache-contro", name, 12)) { - return HD_CACHE_CONTROL; - } - break; - } - break; - case 14: - switch (name[13]) { - case 'h': - if (util::streq_l("content-lengt", name, 13)) { - return HD_CONTENT_LENGTH; - } - break; - case 's': - if (util::streq_l("http2-setting", name, 13)) { - return HD_HTTP2_SETTINGS; - } - break; - } - break; - case 15: - switch (name[14]) { - case 'e': - if (util::streq_l("accept-languag", name, 14)) { - return HD_ACCEPT_LANGUAGE; - } - break; - case 'g': - if (util::streq_l("accept-encodin", name, 14)) { - return HD_ACCEPT_ENCODING; - } - break; - case 'r': - if (util::streq_l("x-forwarded-fo", name, 14)) { - return HD_X_FORWARDED_FOR; - } - break; - } - break; - case 16: - switch (name[15]) { - case 'n': - if (util::streq_l("proxy-connectio", name, 15)) { - return HD_PROXY_CONNECTION; - } - break; - } - break; - case 17: - switch (name[16]) { - case 'e': - if (util::streq_l("if-modified-sinc", name, 16)) { - return HD_IF_MODIFIED_SINCE; - } - break; - case 'g': - if (util::streq_l("transfer-encodin", name, 16)) { - return HD_TRANSFER_ENCODING; - } - break; - case 'o': - if (util::streq_l("x-forwarded-prot", name, 16)) { - return HD_X_FORWARDED_PROTO; - } - break; - } - break; - } - return -1; -} - -void init_hdidx(HeaderIndex &hdidx) { - std::fill(std::begin(hdidx), std::end(hdidx), -1); -} - -void index_header(HeaderIndex &hdidx, int16_t token, size_t idx) { - if (token == -1) { - return; - } - assert(token < HD_MAXIDX); - hdidx[token] = idx; -} - -bool check_http2_request_pseudo_header(const HeaderIndex &hdidx, - int16_t token) { - switch (token) { - case HD__AUTHORITY: - case HD__METHOD: - case HD__PATH: - case HD__SCHEME: - return hdidx[token] == -1; - default: - return false; - } -} - -bool check_http2_response_pseudo_header(const HeaderIndex &hdidx, - int16_t token) { - switch (token) { - case HD__STATUS: - return hdidx[token] == -1; - default: - return false; - } -} - -bool http2_header_allowed(int16_t token) { - switch (token) { - case HD_CONNECTION: - case HD_KEEP_ALIVE: - case HD_PROXY_CONNECTION: - case HD_TRANSFER_ENCODING: - case HD_UPGRADE: - return false; - default: - return true; - } -} - -bool http2_mandatory_request_headers_presence(const HeaderIndex &hdidx) { - if (hdidx[HD__METHOD] == -1 || hdidx[HD__PATH] == -1 || - hdidx[HD__SCHEME] == -1 || - (hdidx[HD__AUTHORITY] == -1 && hdidx[HD_HOST] == -1)) { - return false; - } - return true; -} - -const Headers::value_type *get_header(const HeaderIndex &hdidx, int16_t token, - const Headers &nva) { - auto i = hdidx[token]; - if (i == -1) { - return nullptr; - } - return &nva[i]; -} - -namespace { -template InputIt skip_lws(InputIt first, InputIt last) { - for (; first != last; ++first) { - switch (*first) { - case ' ': - case '\t': - continue; - default: - return first; - } - } - return first; -} -} // namespace - -namespace { -template -InputIt skip_to_next_field(InputIt first, InputIt last) { - for (; first != last; ++first) { - switch (*first) { - case ' ': - case '\t': - case ',': - continue; - default: - return first; - } - } - return first; -} -} // namespace - -namespace { -// Skip to the right dquote ('"'), handling backslash escapes. -// Returns |last| if input is not terminated with '"'. -template -InputIt skip_to_right_dquote(InputIt first, InputIt last) { - for (; first != last;) { - switch (*first) { - case '"': - return first; - case '\\': - ++first; - if (first == last) { - return first; - } - break; - } - ++first; - } - return first; -} -} // namespace - -namespace { -// Returns true if link-param does not match pattern |pat| of length -// |patlen| or it has empty value (""). |pat| should be parmname -// followed by "=". -bool check_link_param_empty(const char *first, const char *last, - const char *pat, size_t patlen) { - if (first + patlen <= last) { - if (std::equal(pat, pat + patlen, first, util::CaseCmp())) { - // we only accept URI if pat is followd by "" (e.g., - // loadpolicy="") here. - if (first + patlen + 2 <= last) { - if (*(first + patlen) != '"' || *(first + patlen + 1) != '"') { - return false; - } - } else { - // here we got invalid production (anchor=") or anchor=? - return false; - } - } - } - return true; -} -} // namespace - -namespace { -std::pair -parse_next_link_header_once(const char *first, const char *last) { - first = skip_to_next_field(first, last); - if (first == last || *first != '<') { - return {{{nullptr, nullptr}}, last}; - } - auto url_first = ++first; - first = std::find(first, last, '>'); - if (first == last) { - return {{{nullptr, nullptr}}, first}; - } - auto url_last = first++; - if (first == last) { - return {{{nullptr, nullptr}}, first}; - } - // we expect ';' or ',' here - switch (*first) { - case ',': - return {{{nullptr, nullptr}}, ++first}; - case ';': - ++first; - break; - default: - return {{{nullptr, nullptr}}, last}; - } - - auto ok = false; - auto ign = false; - for (;;) { - first = skip_lws(first, last); - if (first == last) { - return {{{nullptr, nullptr}}, first}; - } - // we expect link-param - - // rel can take several relations using quoted form. - static constexpr char PLP[] = "rel=\""; - static constexpr size_t PLPLEN = sizeof(PLP) - 1; - - static constexpr char PLT[] = "preload"; - static constexpr size_t PLTLEN = sizeof(PLT) - 1; - if (first + PLPLEN < last && *(first + PLPLEN - 1) == '"' && - std::equal(PLP, PLP + PLPLEN, first, util::CaseCmp())) { - // we have to search preload in whitespace separated list: - // rel="preload something http://example.org/foo" - first += PLPLEN; - auto start = first; - for (; first != last;) { - if (*first != ' ' && *first != '"') { - ++first; - continue; - } - - if (start == first) { - return {{{nullptr, nullptr}}, last}; - } - - if (!ok && start + PLTLEN == first && - std::equal(PLT, PLT + PLTLEN, start, util::CaseCmp())) { - ok = true; - } - - if (*first == '"') { - break; - } - first = skip_lws(first, last); - start = first; - } - if (first == last) { - return {{{nullptr, nullptr}}, first}; - } - assert(*first == '"'); - ++first; - if (first == last || *first == ',') { - goto almost_done; - } - if (*first == ';') { - ++first; - // parse next link-param - continue; - } - return {{{nullptr, nullptr}}, last}; - } - // we are only interested in rel=preload parameter. Others are - // simply skipped. - static constexpr char PL[] = "rel=preload"; - static constexpr size_t PLLEN = sizeof(PL) - 1; - if (first + PLLEN == last) { - if (std::equal(PL, PL + PLLEN, first, util::CaseCmp())) { - ok = true; - // this is the end of sequence - return {{{url_first, url_last}}, last}; - } - } else if (first + PLLEN + 1 <= last) { - switch (*(first + PLLEN)) { - case ',': - if (!std::equal(PL, PL + PLLEN, first, util::CaseCmp())) { - break; - } - ok = true; - // skip including ',' - first += PLLEN + 1; - return {{{url_first, url_last}}, first}; - case ';': - if (!std::equal(PL, PL + PLLEN, first, util::CaseCmp())) { - break; - } - ok = true; - // skip including ';' - first += PLLEN + 1; - // continue parse next link-param - continue; - } - } - // we have to reject URI if we have nonempty anchor parameter. - static constexpr char ANCHOR[] = "anchor="; - static constexpr size_t ANCHORLEN = sizeof(ANCHOR) - 1; - if (!ign && !check_link_param_empty(first, last, ANCHOR, ANCHORLEN)) { - ign = true; - } - - // reject URI if we have non-empty loadpolicy. This could be - // tightened up to just pick up "next" or "insert". - static constexpr char LOADPOLICY[] = "loadpolicy="; - static constexpr size_t LOADPOLICYLEN = sizeof(LOADPOLICY) - 1; - if (!ign && - !check_link_param_empty(first, last, LOADPOLICY, LOADPOLICYLEN)) { - ign = true; - } - - auto param_first = first; - for (; first != last;) { - if (util::in_attr_char(*first)) { - ++first; - continue; - } - // '*' is only allowed at the end of parameter name and must be - // followed by '=' - if (last - first >= 2 && first != param_first) { - if (*first == '*' && *(first + 1) == '=') { - ++first; - break; - } - } - if (*first == '=' || *first == ';' || *first == ',') { - break; - } - return {{{nullptr, nullptr}}, last}; - } - if (param_first == first) { - // empty parmname - return {{{nullptr, nullptr}}, last}; - } - // link-param without value is acceptable (see link-extension) if - // it is not followed by '=' - if (first == last || *first == ',') { - goto almost_done; - } - if (*first == ';') { - ++first; - // parse next link-param - continue; - } - // now parsing link-param value - assert(*first == '='); - ++first; - if (first == last) { - // empty value is not acceptable - return {{{nullptr, nullptr}}, first}; - } - if (*first == '"') { - // quoted-string - first = skip_to_right_dquote(first + 1, last); - if (first == last) { - return {{{nullptr, nullptr}}, first}; - } - ++first; - if (first == last || *first == ',') { - goto almost_done; - } - if (*first == ';') { - ++first; - // parse next link-param - continue; - } - return {{{nullptr, nullptr}}, last}; - } - // not quoted-string, skip to next ',' or ';' - if (*first == ',' || *first == ';') { - // empty value - return {{{nullptr, nullptr}}, last}; - } - for (; first != last; ++first) { - if (*first == ',' || *first == ';') { - break; - } - } - if (first == last || *first == ',') { - goto almost_done; - } - assert(*first == ';'); - ++first; - // parse next link-param - } - -almost_done: - assert(first == last || *first == ','); - - if (first != last) { - ++first; - } - if (ok && !ign) { - return {{{url_first, url_last}}, first}; - } - return {{{nullptr, nullptr}}, first}; -} -} // namespace - -std::vector parse_link_header(const char *src, size_t len) { - auto first = src; - auto last = src + len; - std::vector res; - for (; first != last;) { - auto rv = parse_next_link_header_once(first, last); - first = rv.second; - if (rv.first.uri.first != nullptr && rv.first.uri.second != nullptr) { - res.push_back(rv.first); - } - } - return res; -} - -namespace { -void eat_file(std::string &path) { - if (path.empty()) { - path = "/"; - return; - } - auto p = path.size() - 1; - if (path[p] == '/') { - return; - } - p = path.rfind('/', p); - if (p == std::string::npos) { - // this should not happend in normal case, where we expect path - // starts with '/' - path = "/"; - return; - } - path.erase(std::begin(path) + p + 1, std::end(path)); -} -} // namespace - -namespace { -void eat_dir(std::string &path) { - if (path.empty()) { - path = "/"; - return; - } - auto p = path.size() - 1; - if (path[p] != '/') { - p = path.rfind('/', p); - if (p == std::string::npos) { - // this should not happend in normal case, where we expect path - // starts with '/' - path = "/"; - return; - } - } - if (path[p] == '/') { - if (p == 0) { - return; - } - --p; - } - p = path.rfind('/', p); - if (p == std::string::npos) { - // this should not happend in normal case, where we expect path - // starts with '/' - path = "/"; - return; - } - path.erase(std::begin(path) + p + 1, std::end(path)); -} -} // namespace - -std::string path_join(const char *base_path, size_t base_pathlen, - const char *base_query, size_t base_querylen, - const char *rel_path, size_t rel_pathlen, - const char *rel_query, size_t rel_querylen) { - std::string res; - if (rel_pathlen == 0) { - if (base_pathlen == 0) { - res = "/"; - } else { - res.assign(base_path, base_pathlen); - } - if (rel_querylen == 0) { - if (base_querylen) { - res += "?"; - res.append(base_query, base_querylen); - } - return res; - } - res += "?"; - res.append(rel_query, rel_querylen); - return res; - } - - auto first = rel_path; - auto last = rel_path + rel_pathlen; - - if (rel_path[0] == '/') { - res = "/"; - ++first; - } else if (base_pathlen == 0) { - res = "/"; - } else { - res.assign(base_path, base_pathlen); - } - - for (; first != last;) { - if (*first == '.') { - if (first + 1 == last) { - break; - } - if (*(first + 1) == '/') { - first += 2; - continue; - } - if (*(first + 1) == '.') { - if (first + 2 == last) { - eat_dir(res); - break; - } - if (*(first + 2) == '/') { - eat_dir(res); - first += 3; - continue; - } - } - } - if (res.back() != '/') { - eat_file(res); - } - auto slash = std::find(first, last, '/'); - if (slash == last) { - res.append(first, last); - break; - } - res.append(first, slash + 1); - first = slash + 1; - for (; first != last && *first == '/'; ++first) - ; - } - if (rel_querylen) { - res += "?"; - res.append(rel_query, rel_querylen); - } - return res; -} - -bool expect_response_body(int status_code) { - return status_code / 100 != 1 && status_code != 304 && status_code != 204; -} - -bool expect_response_body(const std::string &method, int status_code) { - return method != "HEAD" && expect_response_body(status_code); -} - -} // namespace http2 - -} // namespace nghttp2 diff --git a/src/http2.h b/src/http2.h deleted file mode 100644 index af8fd10..0000000 --- a/src/http2.h +++ /dev/null @@ -1,296 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2013 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#ifndef HTTP2_H -#define HTTP2_H - -#include "nghttp2_config.h" - -#include -#include -#include -#include -#include - -#include - -#include "http-parser/http_parser.h" - -namespace nghttp2 { - -struct Header { - Header(std::string name, std::string value, bool no_index = false, - int16_t token = -1) - : name(std::move(name)), value(std::move(value)), token(token), - no_index(no_index) {} - - Header() : token(-1), no_index(false) {} - - bool operator==(const Header &other) const { - return name == other.name && value == other.value; - } - - bool operator<(const Header &rhs) const { - return name < rhs.name || (name == rhs.name && value < rhs.value); - } - - std::string name; - std::string value; - int16_t token; - bool no_index; -}; - -typedef std::vector
      Headers; - -namespace http2 { - -std::string get_status_string(unsigned int status_code); - -void capitalize(std::string &s, size_t offset); - -// Returns true if |value| is LWS -bool lws(const char *value); - -// Copies the |field| component value from |u| and |url| to the -// |dest|. If |u| does not have |field|, then this function does -// nothing. -void copy_url_component(std::string &dest, const http_parser_url *u, int field, - const char *url); - -Headers::value_type to_header(const uint8_t *name, size_t namelen, - const uint8_t *value, size_t valuelen, - bool no_index, int16_t token); - -// Add name/value pairs to |nva|. If |no_index| is true, this -// name/value pair won't be indexed when it is forwarded to the next -// hop. This function strips white spaces around |value|. -void add_header(Headers &nva, const uint8_t *name, size_t namelen, - const uint8_t *value, size_t valuelen, bool no_index, - int16_t token); - -// Returns pointer to the entry in |nva| which has name |name|. If -// more than one entries which have the name |name|, last occurrence -// in |nva| is returned. If no such entry exist, returns nullptr. -const Headers::value_type *get_header(const Headers &nva, const char *name); - -// Returns nv->second if nv is not nullptr. Otherwise, returns "". -std::string value_to_str(const Headers::value_type *nv); - -// Returns true if the value of |nv| is not empty. -bool non_empty_value(const Headers::value_type *nv); - -// Creates nghttp2_nv using |name| and |value| and returns it. The -// returned value only references the data pointer to name.c_str() and -// value.c_str(). If |no_index| is true, nghttp2_nv flags member has -// NGHTTP2_NV_FLAG_NO_INDEX flag set. -nghttp2_nv make_nv(const std::string &name, const std::string &value, - bool no_index = false); - -// Create nghttp2_nv from string literal |name| and |value|. -template -nghttp2_nv make_nv_ll(const char (&name)[N], const char (&value)[M]) { - return {(uint8_t *)name, (uint8_t *)value, N - 1, M - 1, - NGHTTP2_NV_FLAG_NONE}; -} - -// Create nghttp2_nv from string literal |name| and c-string |value|. -template -nghttp2_nv make_nv_lc(const char (&name)[N], const char *value) { - return {(uint8_t *)name, (uint8_t *)value, N - 1, strlen(value), - NGHTTP2_NV_FLAG_NONE}; -} - -// Create nghttp2_nv from string literal |name| and std::string -// |value|. -template -nghttp2_nv make_nv_ls(const char (&name)[N], const std::string &value) { - return {(uint8_t *)name, (uint8_t *)value.c_str(), N - 1, value.size(), - NGHTTP2_NV_FLAG_NONE}; -} - -// Appends headers in |headers| to |nv|. |headers| must be indexed -// before this call (its element's token field is assigned). Certain -// headers, including disallowed headers in HTTP/2 spec and headers -// which require special handling (i.e. via), are not copied. -void copy_headers_to_nva(std::vector &nva, const Headers &headers); - -// Appends HTTP/1.1 style header lines to |hdrs| from headers in -// |headers|. |headers| must be indexed before this call (its -// element's token field is assigned). Certain headers, which -// requires special handling (i.e. via and cookie), are not appended. -void build_http1_headers_from_headers(std::string &hdrs, - const Headers &headers); - -// Return positive window_size_increment if WINDOW_UPDATE should be -// sent for the stream |stream_id|. If |stream_id| == 0, this function -// determines the necessity of the WINDOW_UPDATE for a connection. -// -// If the function determines WINDOW_UPDATE is not necessary at the -// moment, it returns -1. -int32_t determine_window_update_transmission(nghttp2_session *session, - int32_t stream_id); - -// Dumps name/value pairs in |nv| to |out|. The |nv| must be -// terminated by nullptr. -void dump_nv(FILE *out, const char **nv); - -// Dumps name/value pairs in |nva| to |out|. -void dump_nv(FILE *out, const nghttp2_nv *nva, size_t nvlen); - -// Dumps name/value pairs in |nva| to |out|. -void dump_nv(FILE *out, const Headers &nva); - -// Rewrites redirection URI which usually appears in location header -// field. The |uri| is the URI in the location header field. The |u| -// stores the result of parsed |uri|. The |request_authority| is the -// host or :authority header field value in the request. The -// |upstream_scheme| is either "https" or "http" in the upstream -// interface. Rewrite is done only if location header field value -// contains |match_host| as host excluding port. The |match_host| and -// |request_authority| could be different. If |request_authority| is -// empty, strip authority. -// -// This function returns the new rewritten URI on success. If the -// location URI is not subject to the rewrite, this function returns -// emtpy string. -std::string rewrite_location_uri(const std::string &uri, - const http_parser_url &u, - const std::string &match_host, - const std::string &request_authority, - const std::string &upstream_scheme); - -// Checks the header name/value pair using nghttp2_check_header_name() -// and nghttp2_check_header_value(). If both function returns nonzero, -// this function returns nonzero. -int check_nv(const uint8_t *name, size_t namelen, const uint8_t *value, - size_t valuelen); - -// Returns parsed HTTP status code. Returns -1 on failure. -int parse_http_status_code(const std::string &src); - -// Header fields to be indexed, except HD_MAXIDX which is convenient -// member to get maximum value. -enum { - HD__AUTHORITY, - HD__HOST, - HD__METHOD, - HD__PATH, - HD__SCHEME, - HD__STATUS, - HD_ACCEPT_ENCODING, - HD_ACCEPT_LANGUAGE, - HD_ALT_SVC, - HD_CACHE_CONTROL, - HD_CONNECTION, - HD_CONTENT_LENGTH, - HD_COOKIE, - HD_EXPECT, - HD_HOST, - HD_HTTP2_SETTINGS, - HD_IF_MODIFIED_SINCE, - HD_KEEP_ALIVE, - HD_LINK, - HD_LOCATION, - HD_PROXY_CONNECTION, - HD_SERVER, - HD_TE, - HD_TRAILER, - HD_TRANSFER_ENCODING, - HD_UPGRADE, - HD_USER_AGENT, - HD_VIA, - HD_X_FORWARDED_FOR, - HD_X_FORWARDED_PROTO, - HD_MAXIDX, -}; - -using HeaderIndex = std::array; - -// Looks up header token for header name |name| of length |namelen|. -// Only headers we are interested in are tokenized. If header name -// cannot be tokenized, returns -1. -int lookup_token(const uint8_t *name, size_t namelen); -int lookup_token(const std::string &name); - -// Initializes |hdidx|, header index. The |hdidx| must point to the -// array containing at least HD_MAXIDX elements. -void init_hdidx(HeaderIndex &hdidx); -// Indexes header |token| using index |idx|. -void index_header(HeaderIndex &hdidx, int16_t token, size_t idx); - -// Returns true if HTTP/2 request pseudo header |token| is not indexed -// yet and not -1. -bool check_http2_request_pseudo_header(const HeaderIndex &hdidx, int16_t token); - -// Returns true if HTTP/2 response pseudo header |token| is not -// indexed yet and not -1. -bool check_http2_response_pseudo_header(const HeaderIndex &hdidx, - int16_t token); - -// Returns true if header field denoted by |token| is allowed for -// HTTP/2. -bool http2_header_allowed(int16_t token); - -// Returns true that |hdidx| contains mandatory HTTP/2 request -// headers. -bool http2_mandatory_request_headers_presence(const HeaderIndex &hdidx); - -// Returns header denoted by |token| using index |hdidx|. -const Headers::value_type *get_header(const HeaderIndex &hdidx, int16_t token, - const Headers &nva); - -struct LinkHeader { - // The region of URI is [uri.first, uri.second). - std::pair uri; -}; - -// Returns next URI-reference in Link header field value |src| of -// length |len|. If no URI-reference found after searching all input, -// returned uri field is empty. This imply that empty URI-reference -// is ignored during parsing. -std::vector parse_link_header(const char *src, size_t len); - -// Constructs path by combining base path |base_path| of length -// |base_pathlen| with another path |rel_path| of length -// |rel_pathlen|. The base path and another path can have optional -// query component. This function assumes |base_path| is normalized. -// In other words, it does not contain ".." or "." path components -// and starts with "/" if it is not empty. -std::string path_join(const char *base_path, size_t base_pathlen, - const char *base_query, size_t base_querylen, - const char *rel_path, size_t rel_pathlen, - const char *rel_query, size_t rel_querylen); - -// true if response has body, taking into account the request method -// and status code. -bool expect_response_body(const std::string &method, int status_code); - -// true if response has body, taking into account status code only. -bool expect_response_body(int status_code); - -} // namespace http2 - -} // namespace nghttp2 - -#endif // HTTP2_H diff --git a/src/http2_test.cc b/src/http2_test.cc deleted file mode 100644 index e2975d1..0000000 --- a/src/http2_test.cc +++ /dev/null @@ -1,826 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2013 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#include "http2_test.h" - -#include -#include -#include - -#include - -#include "http-parser/http_parser.h" - -#include "http2.h" -#include "util.h" - -using namespace nghttp2; - -#define MAKE_NV(K, V) \ - { \ - (uint8_t *) K, (uint8_t *)V, sizeof(K) - 1, sizeof(V) - 1, \ - NGHTTP2_NV_FLAG_NONE \ - } - -namespace shrpx { - -namespace { -void check_nv(const Header &a, const nghttp2_nv *b) { - CU_ASSERT(a.name.size() == b->namelen); - CU_ASSERT(a.value.size() == b->valuelen); - CU_ASSERT(memcmp(a.name.c_str(), b->name, b->namelen) == 0); - CU_ASSERT(memcmp(a.value.c_str(), b->value, b->valuelen) == 0); -} -} // namespace - -void test_http2_add_header(void) { - auto nva = Headers(); - - http2::add_header(nva, (const uint8_t *)"alpha", 5, (const uint8_t *)"123", 3, - false, -1); - CU_ASSERT(Headers::value_type("alpha", "123") == nva[0]); - CU_ASSERT(!nva[0].no_index); - - nva.clear(); - - http2::add_header(nva, (const uint8_t *)"alpha", 5, (const uint8_t *)"", 0, - true, -1); - CU_ASSERT(Headers::value_type("alpha", "") == nva[0]); - CU_ASSERT(nva[0].no_index); - - nva.clear(); - - http2::add_header(nva, (const uint8_t *)"a", 1, (const uint8_t *)" b", 2, - false, -1); - CU_ASSERT(Headers::value_type("a", "b") == nva[0]); - - nva.clear(); - - http2::add_header(nva, (const uint8_t *)"a", 1, (const uint8_t *)"b ", 2, - false, -1); - CU_ASSERT(Headers::value_type("a", "b") == nva[0]); - - nva.clear(); - - http2::add_header(nva, (const uint8_t *)"a", 1, (const uint8_t *)" b ", 5, - false, -1); - CU_ASSERT(Headers::value_type("a", "b") == nva[0]); - - nva.clear(); - - http2::add_header(nva, (const uint8_t *)"a", 1, (const uint8_t *)" bravo ", - 9, false, -1); - CU_ASSERT(Headers::value_type("a", "bravo") == nva[0]); - - nva.clear(); - - http2::add_header(nva, (const uint8_t *)"a", 1, (const uint8_t *)" ", 4, - false, -1); - CU_ASSERT(Headers::value_type("a", "") == nva[0]); - - nva.clear(); - - http2::add_header(nva, (const uint8_t *)"te", 2, (const uint8_t *)"trailers", - 8, false, http2::HD_TE); - CU_ASSERT(http2::HD_TE == nva[0].token); -} - -void test_http2_get_header(void) { - auto nva = Headers{{"alpha", "1"}, - {"bravo", "2"}, - {"bravo", "3"}, - {"charlie", "4"}, - {"delta", "5"}, - {"echo", "6"}, - {"content-length", "7"}}; - const Headers::value_type *rv; - rv = http2::get_header(nva, "delta"); - CU_ASSERT(rv != nullptr); - CU_ASSERT("delta" == rv->name); - - rv = http2::get_header(nva, "bravo"); - CU_ASSERT(rv != nullptr); - CU_ASSERT("bravo" == rv->name); - - rv = http2::get_header(nva, "foxtrot"); - CU_ASSERT(rv == nullptr); - - http2::HeaderIndex hdidx; - http2::init_hdidx(hdidx); - hdidx[http2::HD_CONTENT_LENGTH] = 6; - rv = http2::get_header(hdidx, http2::HD_CONTENT_LENGTH, nva); - CU_ASSERT("content-length" == rv->name); -} - -namespace { -auto headers = - Headers{{"alpha", "0", true}, - {"bravo", "1"}, - {"connection", "2", false, http2::HD_CONNECTION}, - {"connection", "3", false, http2::HD_CONNECTION}, - {"delta", "4"}, - {"expect", "5"}, - {"foxtrot", "6"}, - {"tango", "7"}, - {"te", "8", false, http2::HD_TE}, - {"te", "9", false, http2::HD_TE}, - {"x-forwarded-proto", "10", false, http2::HD_X_FORWARDED_FOR}, - {"x-forwarded-proto", "11", false, http2::HD_X_FORWARDED_FOR}, - {"zulu", "12"}}; -} // namespace - -void test_http2_copy_headers_to_nva(void) { - std::vector nva; - http2::copy_headers_to_nva(nva, headers); - CU_ASSERT(7 == nva.size()); - auto ans = std::vector{0, 1, 4, 5, 6, 7, 12}; - for (size_t i = 0; i < ans.size(); ++i) { - check_nv(headers[ans[i]], &nva[i]); - - if (ans[i] == 0) { - CU_ASSERT(nva[i].flags & NGHTTP2_NV_FLAG_NO_INDEX); - } else { - CU_ASSERT(NGHTTP2_NV_FLAG_NONE == nva[i].flags); - } - } -} - -void test_http2_build_http1_headers_from_headers(void) { - std::string hdrs; - http2::build_http1_headers_from_headers(hdrs, headers); - CU_ASSERT(hdrs == "Alpha: 0\r\n" - "Bravo: 1\r\n" - "Delta: 4\r\n" - "Expect: 5\r\n" - "Foxtrot: 6\r\n" - "Tango: 7\r\n" - "Te: 8\r\n" - "Te: 9\r\n" - "Zulu: 12\r\n"); -} - -void test_http2_lws(void) { - CU_ASSERT(!http2::lws("alpha")); - CU_ASSERT(http2::lws(" ")); - CU_ASSERT(http2::lws("")); -} - -namespace { -void check_rewrite_location_uri(const std::string &want, const std::string &uri, - const std::string &match_host, - const std::string &req_authority, - const std::string &upstream_scheme) { - http_parser_url u; - memset(&u, 0, sizeof(u)); - CU_ASSERT(0 == http_parser_parse_url(uri.c_str(), uri.size(), 0, &u)); - auto got = http2::rewrite_location_uri(uri, u, match_host, req_authority, - upstream_scheme); - CU_ASSERT(want == got); -} -} // namespace - -void test_http2_rewrite_location_uri(void) { - check_rewrite_location_uri("https://localhost:3000/alpha?bravo#charlie", - "http://localhost:3001/alpha?bravo#charlie", - "localhost:3001", "localhost:3000", "https"); - check_rewrite_location_uri("https://localhost/", "http://localhost:3001/", - "localhost", "localhost", "https"); - check_rewrite_location_uri("http://localhost/", "http://localhost:3001/", - "localhost", "localhost", "http"); - check_rewrite_location_uri("http://localhost:443/", "http://localhost:3001/", - "localhost", "localhost:443", "http"); - check_rewrite_location_uri("https://localhost:80/", "http://localhost:3001/", - "localhost", "localhost:80", "https"); - check_rewrite_location_uri("", "http://localhost:3001/", "127.0.0.1", - "127.0.0.1", "https"); - check_rewrite_location_uri("https://localhost:3000/", - "http://localhost:3001/", "localhost", - "localhost:3000", "https"); - check_rewrite_location_uri("https://localhost:3000/", "http://localhost/", - "localhost", "localhost:3000", "https"); - - // match_host != req_authority - check_rewrite_location_uri("https://example.org", "http://127.0.0.1:8080", - "127.0.0.1", "example.org", "https"); - check_rewrite_location_uri("", "http://example.org", "127.0.0.1", - "example.org", "https"); -} - -void test_http2_parse_http_status_code(void) { - CU_ASSERT(200 == http2::parse_http_status_code("200")); - CU_ASSERT(102 == http2::parse_http_status_code("102")); - CU_ASSERT(-1 == http2::parse_http_status_code("099")); - CU_ASSERT(-1 == http2::parse_http_status_code("99")); - CU_ASSERT(-1 == http2::parse_http_status_code("-1")); - CU_ASSERT(-1 == http2::parse_http_status_code("20a")); - CU_ASSERT(-1 == http2::parse_http_status_code("")); -} - -void test_http2_index_header(void) { - http2::HeaderIndex hdidx; - http2::init_hdidx(hdidx); - - http2::index_header(hdidx, http2::HD__AUTHORITY, 0); - http2::index_header(hdidx, -1, 1); - - CU_ASSERT(0 == hdidx[http2::HD__AUTHORITY]); -} - -void test_http2_lookup_token(void) { - CU_ASSERT(http2::HD__AUTHORITY == http2::lookup_token(":authority")); - CU_ASSERT(-1 == http2::lookup_token(":authorit")); - CU_ASSERT(-1 == http2::lookup_token(":Authority")); - CU_ASSERT(http2::HD_EXPECT == http2::lookup_token("expect")); -} - -void test_http2_check_http2_pseudo_header(void) { - http2::HeaderIndex hdidx; - http2::init_hdidx(hdidx); - - CU_ASSERT(http2::check_http2_request_pseudo_header(hdidx, http2::HD__METHOD)); - hdidx[http2::HD__PATH] = 0; - CU_ASSERT(http2::check_http2_request_pseudo_header(hdidx, http2::HD__METHOD)); - hdidx[http2::HD__METHOD] = 1; - CU_ASSERT( - !http2::check_http2_request_pseudo_header(hdidx, http2::HD__METHOD)); - CU_ASSERT(!http2::check_http2_request_pseudo_header(hdidx, http2::HD_VIA)); - - http2::init_hdidx(hdidx); - - CU_ASSERT( - http2::check_http2_response_pseudo_header(hdidx, http2::HD__STATUS)); - hdidx[http2::HD__STATUS] = 0; - CU_ASSERT( - !http2::check_http2_response_pseudo_header(hdidx, http2::HD__STATUS)); - CU_ASSERT(!http2::check_http2_response_pseudo_header(hdidx, http2::HD_VIA)); -} - -void test_http2_http2_header_allowed(void) { - CU_ASSERT(http2::http2_header_allowed(http2::HD__PATH)); - CU_ASSERT(http2::http2_header_allowed(http2::HD_CONTENT_LENGTH)); - CU_ASSERT(!http2::http2_header_allowed(http2::HD_CONNECTION)); -} - -void test_http2_mandatory_request_headers_presence(void) { - http2::HeaderIndex hdidx; - http2::init_hdidx(hdidx); - - CU_ASSERT(!http2::http2_mandatory_request_headers_presence(hdidx)); - hdidx[http2::HD__AUTHORITY] = 0; - CU_ASSERT(!http2::http2_mandatory_request_headers_presence(hdidx)); - hdidx[http2::HD__METHOD] = 1; - CU_ASSERT(!http2::http2_mandatory_request_headers_presence(hdidx)); - hdidx[http2::HD__PATH] = 2; - CU_ASSERT(!http2::http2_mandatory_request_headers_presence(hdidx)); - hdidx[http2::HD__SCHEME] = 3; - CU_ASSERT(http2::http2_mandatory_request_headers_presence(hdidx)); - - hdidx[http2::HD__AUTHORITY] = -1; - hdidx[http2::HD_HOST] = 0; - CU_ASSERT(http2::http2_mandatory_request_headers_presence(hdidx)); -} - -void test_http2_parse_link_header(void) { - { - // only URI appears; we don't extract URI unless it bears rel=preload - const char s[] = ""; - auto res = http2::parse_link_header(s, sizeof(s) - 1); - CU_ASSERT(0 == res.size()); - } - { - // URI url should be extracted - const char s[] = "; rel=preload"; - auto res = http2::parse_link_header(s, sizeof(s) - 1); - CU_ASSERT(1 == res.size()); - CU_ASSERT(std::make_pair(&s[1], &s[4]) == res[0].uri); - } - { - // With extra link-param. URI url should be extracted - const char s[] = "; rel=preload; as=file"; - auto res = http2::parse_link_header(s, sizeof(s) - 1); - CU_ASSERT(1 == res.size()); - CU_ASSERT(std::make_pair(&s[1], &s[4]) == res[0].uri); - } - { - // With extra link-param. URI url should be extracted - const char s[] = "; as=file; rel=preload"; - auto res = http2::parse_link_header(s, sizeof(s) - 1); - CU_ASSERT(1 == res.size()); - CU_ASSERT(std::make_pair(&s[1], &s[4]) == res[0].uri); - } - { - // With extra link-param and quote-string. URI url should be - // extracted - const char s[] = R"(; rel=preload; title="foo,bar")"; - auto res = http2::parse_link_header(s, sizeof(s) - 1); - CU_ASSERT(1 == res.size()); - CU_ASSERT(std::make_pair(&s[1], &s[4]) == res[0].uri); - } - { - // With extra link-param and quote-string. URI url should be - // extracted - const char s[] = R"(; title="foo,bar"; rel=preload)"; - auto res = http2::parse_link_header(s, sizeof(s) - 1); - CU_ASSERT(1 == res.size()); - CU_ASSERT(std::make_pair(&s[1], &s[4]) == res[0].uri); - } - { - // ',' after quote-string - const char s[] = R"(; title="foo,bar", ; rel=preload)"; - auto res = http2::parse_link_header(s, sizeof(s) - 1); - CU_ASSERT(1 == res.size()); - CU_ASSERT(std::make_pair(&s[25], &s[28]) == res[0].uri); - } - { - // Only first URI should be extracted. - const char s[] = "; rel=preload, "; - auto res = http2::parse_link_header(s, sizeof(s) - 1); - CU_ASSERT(1 == res.size()); - CU_ASSERT(std::make_pair(&s[1], &s[4]) == res[0].uri); - } - { - // Both have rel=preload, so both urls should be extracted - const char s[] = "; rel=preload, ; rel=preload"; - auto res = http2::parse_link_header(s, sizeof(s) - 1); - CU_ASSERT(2 == res.size()); - CU_ASSERT(std::make_pair(&s[1], &s[4]) == res[0].uri); - CU_ASSERT(std::make_pair(&s[21], &s[24]) == res[1].uri); - } - { - // Second URI uri should be extracted. - const char s[] = ", ;rel=preload"; - auto res = http2::parse_link_header(s, sizeof(s) - 1); - CU_ASSERT(1 == res.size()); - CU_ASSERT(std::make_pair(&s[8], &s[11]) == res[0].uri); - } - { - // Error if input ends with ';' - const char s[] = ";rel=preload;"; - auto res = http2::parse_link_header(s, sizeof(s) - 1); - CU_ASSERT(0 == res.size()); - } - { - // OK if input ends with ',' - const char s[] = ";rel=preload,"; - auto res = http2::parse_link_header(s, sizeof(s) - 1); - CU_ASSERT(1 == res.size()); - CU_ASSERT(std::make_pair(&s[1], &s[4]) == res[0].uri); - } - { - // Multiple repeated ','s between fields is OK - const char s[] = ",,,;rel=preload"; - auto res = http2::parse_link_header(s, sizeof(s) - 1); - CU_ASSERT(1 == res.size()); - CU_ASSERT(std::make_pair(&s[9], &s[12]) == res[0].uri); - } - { - // Error if url is not enclosed by <> - const char s[] = "url>;rel=preload;"; - auto res = http2::parse_link_header(s, sizeof(s) - 1); - CU_ASSERT(0 == res.size()); - } - { - // Error if url is not enclosed by <> - const char s[] = "' is not followed by ';' - const char s[] = " rel=preload"; - auto res = http2::parse_link_header(s, sizeof(s) - 1); - CU_ASSERT(0 == res.size()); - } - { - // Starting with whitespace is no problem. - const char s[] = " ; rel=preload"; - auto res = http2::parse_link_header(s, sizeof(s) - 1); - CU_ASSERT(1 == res.size()); - CU_ASSERT(std::make_pair(&s[3], &s[6]) == res[0].uri); - } - { - // preload is a prefix of bogus rel parameter value - const char s[] = "; rel=preloadx"; - auto res = http2::parse_link_header(s, sizeof(s) - 1); - CU_ASSERT(0 == res.size()); - } - { - // preload in relation-types list - const char s[] = R"(; rel="preload")"; - auto res = http2::parse_link_header(s, sizeof(s) - 1); - CU_ASSERT(1 == res.size()); - CU_ASSERT(std::make_pair(&s[1], &s[4]) == res[0].uri); - } - { - // preload in relation-types list followed by another parameter - const char s[] = R"(; rel="preload foo")"; - auto res = http2::parse_link_header(s, sizeof(s) - 1); - CU_ASSERT(1 == res.size()); - CU_ASSERT(std::make_pair(&s[1], &s[4]) == res[0].uri); - } - { - // preload in relation-types list following another parameter - const char s[] = R"(; rel="foo preload")"; - auto res = http2::parse_link_header(s, sizeof(s) - 1); - CU_ASSERT(1 == res.size()); - CU_ASSERT(std::make_pair(&s[1], &s[4]) == res[0].uri); - } - { - // preload in relation-types list between other parameters - const char s[] = R"(; rel="foo preload bar")"; - auto res = http2::parse_link_header(s, sizeof(s) - 1); - CU_ASSERT(1 == res.size()); - CU_ASSERT(std::make_pair(&s[1], &s[4]) == res[0].uri); - } - { - // preload in relation-types list between other parameters - const char s[] = R"(; rel="foo preload bar")"; - auto res = http2::parse_link_header(s, sizeof(s) - 1); - CU_ASSERT(1 == res.size()); - CU_ASSERT(std::make_pair(&s[1], &s[4]) == res[0].uri); - } - { - // no preload in relation-types list - const char s[] = R"(; rel="foo")"; - auto res = http2::parse_link_header(s, sizeof(s) - 1); - CU_ASSERT(0 == res.size()); - } - { - // no preload in relation-types list, multiple unrelated elements. - const char s[] = R"(; rel="foo bar")"; - auto res = http2::parse_link_header(s, sizeof(s) - 1); - CU_ASSERT(0 == res.size()); - } - { - // preload in relation-types list, followed by another link-value. - const char s[] = R"(; rel="preload", )"; - auto res = http2::parse_link_header(s, sizeof(s) - 1); - CU_ASSERT(1 == res.size()); - CU_ASSERT(std::make_pair(&s[1], &s[4]) == res[0].uri); - } - { - // preload in relation-types list, following another link-value. - const char s[] = R"(, ; rel="preload")"; - auto res = http2::parse_link_header(s, sizeof(s) - 1); - CU_ASSERT(1 == res.size()); - CU_ASSERT(std::make_pair(&s[8], &s[11]) == res[0].uri); - } - { - // preload in relation-types list, followed by another link-param. - const char s[] = R"(; rel="preload"; as="font")"; - auto res = http2::parse_link_header(s, sizeof(s) - 1); - CU_ASSERT(1 == res.size()); - CU_ASSERT(std::make_pair(&s[1], &s[4]) == res[0].uri); - } - { - // preload in relation-types list, followed by character other - // than ';' or ',' - const char s[] = R"(; rel="preload".)"; - auto res = http2::parse_link_header(s, sizeof(s) - 1); - CU_ASSERT(0 == res.size()); - } - { - // preload in relation-types list, followed by ';' but it - // terminates input - const char s[] = R"(; rel="preload";)"; - auto res = http2::parse_link_header(s, sizeof(s) - 1); - CU_ASSERT(0 == res.size()); - } - { - // preload in relation-types list, followed by ',' but it - // terminates input - const char s[] = R"(; rel="preload",)"; - auto res = http2::parse_link_header(s, sizeof(s) - 1); - CU_ASSERT(1 == res.size()); - CU_ASSERT(std::make_pair(&s[1], &s[4]) == res[0].uri); - } - { - // preload in relation-types list but there is preceding white - // space. - const char s[] = R"(; rel=" preload")"; - auto res = http2::parse_link_header(s, sizeof(s) - 1); - CU_ASSERT(0 == res.size()); - } - { - // preload in relation-types list but there is trailing white - // space. - const char s[] = R"(; rel="preload ")"; - auto res = http2::parse_link_header(s, sizeof(s) - 1); - CU_ASSERT(0 == res.size()); - } - { - // backslash escaped characters in quoted-string - const char s[] = R"(; rel=preload; title="foo\"baz\"bar")"; - auto res = http2::parse_link_header(s, sizeof(s) - 1); - CU_ASSERT(1 == res.size()); - CU_ASSERT(std::make_pair(&s[1], &s[4]) == res[0].uri); - } - { - // anchor="" is acceptable - const char s[] = R"(; rel=preload; anchor="")"; - auto res = http2::parse_link_header(s, sizeof(s) - 1); - CU_ASSERT(1 == res.size()); - CU_ASSERT(std::make_pair(&s[1], &s[4]) == res[0].uri); - } - { - // With anchor="#foo", url should be ignored - const char s[] = R"(; rel=preload; anchor="#foo")"; - auto res = http2::parse_link_header(s, sizeof(s) - 1); - CU_ASSERT(0 == res.size()); - } - { - // With anchor=f, url should be ignored - const char s[] = "; rel=preload; anchor=f"; - auto res = http2::parse_link_header(s, sizeof(s) - 1); - CU_ASSERT(0 == res.size()); - } - { - // First url is ignored With anchor="#foo", but url should be - // accepted. - const char s[] = R"(; rel=preload; anchor="#foo", ; rel=preload)"; - auto res = http2::parse_link_header(s, sizeof(s) - 1); - CU_ASSERT(1 == res.size()); - CU_ASSERT(std::make_pair(&s[36], &s[39]) == res[0].uri); - } - { - // With loadpolicy="next", url should be ignored - const char s[] = R"(; rel=preload; loadpolicy="next")"; - auto res = http2::parse_link_header(s, sizeof(s) - 1); - CU_ASSERT(0 == res.size()); - } - { - // url should be picked up if empty loadpolicy is specified - const char s[] = R"(; rel=preload; loadpolicy="")"; - auto res = http2::parse_link_header(s, sizeof(s) - 1); - CU_ASSERT(1 == res.size()); - CU_ASSERT(std::make_pair(&s[1], &s[4]) == res[0].uri); - } - { - // case-insensitive match - const char s[] = R"(; rel=preload; ANCHOR="#foo", ; )" - R"(REL=PRELOAD, ; REL="foo PRELOAD bar")"; - auto res = http2::parse_link_header(s, sizeof(s) - 1); - CU_ASSERT(2 == res.size()); - CU_ASSERT(std::make_pair(&s[36], &s[39]) == res[0].uri); - CU_ASSERT(std::make_pair(&s[42 + 14], &s[42 + 17]) == res[1].uri); - } -} - -void test_http2_path_join(void) { - { - const char base[] = "/"; - const char rel[] = "/"; - CU_ASSERT("/" == http2::path_join(base, sizeof(base) - 1, nullptr, 0, rel, - sizeof(rel) - 1, nullptr, 0)); - } - { - const char base[] = "/"; - const char rel[] = "/alpha"; - CU_ASSERT("/alpha" == http2::path_join(base, sizeof(base) - 1, nullptr, 0, - rel, sizeof(rel) - 1, nullptr, 0)); - } - { - // rel ends with trailing '/' - const char base[] = "/"; - const char rel[] = "/alpha/"; - CU_ASSERT("/alpha/" == http2::path_join(base, sizeof(base) - 1, nullptr, 0, - rel, sizeof(rel) - 1, nullptr, 0)); - } - { - // rel contains multiple components - const char base[] = "/"; - const char rel[] = "/alpha/bravo"; - CU_ASSERT("/alpha/bravo" == http2::path_join(base, sizeof(base) - 1, - nullptr, 0, rel, - sizeof(rel) - 1, nullptr, 0)); - } - { - // rel is relative - const char base[] = "/"; - const char rel[] = "alpha/bravo"; - CU_ASSERT("/alpha/bravo" == http2::path_join(base, sizeof(base) - 1, - nullptr, 0, rel, - sizeof(rel) - 1, nullptr, 0)); - } - { - // rel is relative and base ends without /, which means it refers - // to file. - const char base[] = "/alpha"; - const char rel[] = "bravo/charlie"; - CU_ASSERT("/bravo/charlie" == - http2::path_join(base, sizeof(base) - 1, nullptr, 0, rel, - sizeof(rel) - 1, nullptr, 0)); - } - { - // rel contains repeated '/'s - const char base[] = "/"; - const char rel[] = "/alpha/////bravo/////"; - CU_ASSERT("/alpha/bravo/" == http2::path_join(base, sizeof(base) - 1, - nullptr, 0, rel, - sizeof(rel) - 1, nullptr, 0)); - } - { - // base ends with '/', so '..' eats 'bravo' - const char base[] = "/alpha/bravo/"; - const char rel[] = "../charlie/delta"; - CU_ASSERT("/alpha/charlie/delta" == - http2::path_join(base, sizeof(base) - 1, nullptr, 0, rel, - sizeof(rel) - 1, nullptr, 0)); - } - { - // base does not end with '/', so '..' eats 'alpha/bravo' - const char base[] = "/alpha/bravo"; - const char rel[] = "../charlie"; - CU_ASSERT("/charlie" == http2::path_join(base, sizeof(base) - 1, nullptr, 0, - rel, sizeof(rel) - 1, nullptr, 0)); - } - { - // 'charlie' is eaten by following '..' - const char base[] = "/alpha/bravo/"; - const char rel[] = "../charlie/../delta"; - CU_ASSERT("/alpha/delta" == http2::path_join(base, sizeof(base) - 1, - nullptr, 0, rel, - sizeof(rel) - 1, nullptr, 0)); - } - { - // excessive '..' results in '/' - const char base[] = "/alpha/bravo/"; - const char rel[] = "../../../"; - CU_ASSERT("/" == http2::path_join(base, sizeof(base) - 1, nullptr, 0, rel, - sizeof(rel) - 1, nullptr, 0)); - } - { - // excessive '..' and path component - const char base[] = "/alpha/bravo/"; - const char rel[] = "../../../charlie"; - CU_ASSERT("/charlie" == http2::path_join(base, sizeof(base) - 1, nullptr, 0, - rel, sizeof(rel) - 1, nullptr, 0)); - } - { - // rel ends with '..' - const char base[] = "/alpha/bravo/"; - const char rel[] = "charlie/.."; - CU_ASSERT("/alpha/bravo/" == http2::path_join(base, sizeof(base) - 1, - nullptr, 0, rel, - sizeof(rel) - 1, nullptr, 0)); - } - { - // base empty and rel contains '..' - const char base[] = ""; - const char rel[] = "charlie/.."; - CU_ASSERT("/" == http2::path_join(base, sizeof(base) - 1, nullptr, 0, rel, - sizeof(rel) - 1, nullptr, 0)); - } - { - // '.' is ignored - const char base[] = "/"; - const char rel[] = "charlie/././././delta"; - CU_ASSERT("/charlie/delta" == - http2::path_join(base, sizeof(base) - 1, nullptr, 0, rel, - sizeof(rel) - 1, nullptr, 0)); - } - { - // trailing '.' is ignored - const char base[] = "/"; - const char rel[] = "charlie/."; - CU_ASSERT("/charlie/" == http2::path_join(base, sizeof(base) - 1, nullptr, - 0, rel, sizeof(rel) - 1, nullptr, - 0)); - } - { - // query - const char base[] = "/"; - const char rel[] = "/"; - const char relq[] = "q"; - CU_ASSERT("/?q" == http2::path_join(base, sizeof(base) - 1, nullptr, 0, rel, - sizeof(rel) - 1, relq, - sizeof(relq) - 1)); - } - { - // empty rel and query - const char base[] = "/alpha"; - const char rel[] = ""; - const char relq[] = "q"; - CU_ASSERT("/alpha?q" == http2::path_join(base, sizeof(base) - 1, nullptr, 0, - rel, sizeof(rel) - 1, relq, - sizeof(relq) - 1)); - } - { - // both rel and query are empty - const char base[] = "/alpha"; - const char baseq[] = "r"; - const char rel[] = ""; - const char relq[] = ""; - CU_ASSERT("/alpha?r" == - http2::path_join(base, sizeof(base) - 1, baseq, sizeof(baseq) - 1, - rel, sizeof(rel) - 1, relq, sizeof(relq) - 1)); - } - { - // empty base - const char base[] = ""; - const char rel[] = "/alpha"; - CU_ASSERT("/alpha" == http2::path_join(base, sizeof(base) - 1, nullptr, 0, - rel, sizeof(rel) - 1, nullptr, 0)); - } - { - // everything is empty - CU_ASSERT("/" == - http2::path_join(nullptr, 0, nullptr, 0, nullptr, 0, nullptr, 0)); - } - { - // only baseq is not empty - const char base[] = ""; - const char baseq[] = "r"; - const char rel[] = ""; - CU_ASSERT("/?r" == http2::path_join(base, sizeof(base) - 1, baseq, - sizeof(baseq) - 1, rel, sizeof(rel) - 1, - nullptr, 0)); - } -} - -} // namespace shrpx diff --git a/src/http2_test.h b/src/http2_test.h deleted file mode 100644 index 36c5a59..0000000 --- a/src/http2_test.h +++ /dev/null @@ -1,51 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2013 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#ifndef SHRPX_HTTP2_TEST_H -#define SHRPX_HTTP2_TEST_H - -#ifdef HAVE_CONFIG_H -#include -#endif // HAVE_CONFIG_H - -namespace shrpx { - -void test_http2_add_header(void); -void test_http2_get_header(void); -void test_http2_copy_headers_to_nva(void); -void test_http2_build_http1_headers_from_headers(void); -void test_http2_lws(void); -void test_http2_rewrite_location_uri(void); -void test_http2_parse_http_status_code(void); -void test_http2_index_header(void); -void test_http2_lookup_token(void); -void test_http2_check_http2_pseudo_header(void); -void test_http2_http2_header_allowed(void); -void test_http2_mandatory_request_headers_presence(void); -void test_http2_parse_link_header(void); -void test_http2_path_join(void); - -} // namespace shrpx - -#endif // SHRPX_HTTP2_TEST_H diff --git a/src/includes/Makefile.am b/src/includes/Makefile.am deleted file mode 100644 index abc93c1..0000000 --- a/src/includes/Makefile.am +++ /dev/null @@ -1,27 +0,0 @@ -# nghttp2 - HTTP/2 C Library - -# Copyright (c) 2014 Tatsuhiro Tsujikawa - -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: - -# The above copyright notice and this permission notice shall be -# included in all copies or substantial portions of the Software. - -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -if ENABLE_ASIO_LIB -nobase_include_HEADERS = nghttp2/asio_http2.h nghttp2/asio_http2_client.h \ - nghttp2/asio_http2_server.h -endif # ENABLE_ASIO_LIB diff --git a/src/includes/Makefile.in b/src/includes/Makefile.in deleted file mode 100644 index 9892cef..0000000 --- a/src/includes/Makefile.in +++ /dev/null @@ -1,640 +0,0 @@ -# Makefile.in generated by automake 1.14.1 from Makefile.am. -# @configure_input@ - -# Copyright (C) 1994-2013 Free Software Foundation, Inc. - -# This Makefile.in is free software; the Free Software Foundation -# gives unlimited permission to copy and/or distribute it, -# with or without modifications, as long as this notice is preserved. - -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY, to the extent permitted by law; without -# even the implied warranty of MERCHANTABILITY or FITNESS FOR A -# PARTICULAR PURPOSE. - -@SET_MAKE@ - -# nghttp2 - HTTP/2 C Library - -# Copyright (c) 2014 Tatsuhiro Tsujikawa - -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: - -# The above copyright notice and this permission notice shall be -# included in all copies or substantial portions of the Software. - -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -VPATH = @srcdir@ -am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)' -am__make_running_with_option = \ - case $${target_option-} in \ - ?) ;; \ - *) echo "am__make_running_with_option: internal error: invalid" \ - "target option '$${target_option-}' specified" >&2; \ - exit 1;; \ - esac; \ - has_opt=no; \ - sane_makeflags=$$MAKEFLAGS; \ - if $(am__is_gnu_make); then \ - sane_makeflags=$$MFLAGS; \ - else \ - case $$MAKEFLAGS in \ - *\\[\ \ ]*) \ - bs=\\; \ - sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ - | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ - esac; \ - fi; \ - skip_next=no; \ - strip_trailopt () \ - { \ - flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ - }; \ - for flg in $$sane_makeflags; do \ - test $$skip_next = yes && { skip_next=no; continue; }; \ - case $$flg in \ - *=*|--*) continue;; \ - -*I) strip_trailopt 'I'; skip_next=yes;; \ - -*I?*) strip_trailopt 'I';; \ - -*O) strip_trailopt 'O'; skip_next=yes;; \ - -*O?*) strip_trailopt 'O';; \ - -*l) strip_trailopt 'l'; skip_next=yes;; \ - -*l?*) strip_trailopt 'l';; \ - -[dEDm]) skip_next=yes;; \ - -[JT]) skip_next=yes;; \ - esac; \ - case $$flg in \ - *$$target_option*) has_opt=yes; break;; \ - esac; \ - done; \ - test $$has_opt = yes -am__make_dryrun = (target_option=n; $(am__make_running_with_option)) -am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) -pkgdatadir = $(datadir)/@PACKAGE@ -pkgincludedir = $(includedir)/@PACKAGE@ -pkglibdir = $(libdir)/@PACKAGE@ -pkglibexecdir = $(libexecdir)/@PACKAGE@ -am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd -install_sh_DATA = $(install_sh) -c -m 644 -install_sh_PROGRAM = $(install_sh) -c -install_sh_SCRIPT = $(install_sh) -c -INSTALL_HEADER = $(INSTALL_DATA) -transform = $(program_transform_name) -NORMAL_INSTALL = : -PRE_INSTALL = : -POST_INSTALL = : -NORMAL_UNINSTALL = : -PRE_UNINSTALL = : -POST_UNINSTALL = : -build_triplet = @build@ -host_triplet = @host@ -target_triplet = @target@ -subdir = src/includes -DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \ - $(am__nobase_include_HEADERS_DIST) -ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 -am__aclocal_m4_deps = $(top_srcdir)/m4/ax_boost_asio.m4 \ - $(top_srcdir)/m4/ax_boost_base.m4 \ - $(top_srcdir)/m4/ax_boost_system.m4 \ - $(top_srcdir)/m4/ax_boost_thread.m4 \ - $(top_srcdir)/m4/ax_check_compile_flag.m4 \ - $(top_srcdir)/m4/ax_cxx_compile_stdcxx_11.m4 \ - $(top_srcdir)/m4/ax_have_epoll.m4 \ - $(top_srcdir)/m4/ax_python_devel.m4 \ - $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/libxml2.m4 \ - $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ - $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ - $(top_srcdir)/configure.ac -am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ - $(ACLOCAL_M4) -mkinstalldirs = $(install_sh) -d -CONFIG_HEADER = $(top_builddir)/config.h -CONFIG_CLEAN_FILES = -CONFIG_CLEAN_VPATH_FILES = -AM_V_P = $(am__v_P_@AM_V@) -am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) -am__v_P_0 = false -am__v_P_1 = : -AM_V_GEN = $(am__v_GEN_@AM_V@) -am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) -am__v_GEN_0 = @echo " GEN " $@; -am__v_GEN_1 = -AM_V_at = $(am__v_at_@AM_V@) -am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) -am__v_at_0 = @ -am__v_at_1 = -SOURCES = -DIST_SOURCES = -am__can_run_installinfo = \ - case $$AM_UPDATE_INFO_DIR in \ - n|no|NO) false;; \ - *) (install-info --version) >/dev/null 2>&1;; \ - esac -am__nobase_include_HEADERS_DIST = nghttp2/asio_http2.h \ - nghttp2/asio_http2_client.h nghttp2/asio_http2_server.h -am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; -am__vpath_adj = case $$p in \ - $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ - *) f=$$p;; \ - esac; -am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; -am__install_max = 40 -am__nobase_strip_setup = \ - srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` -am__nobase_strip = \ - for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" -am__nobase_list = $(am__nobase_strip_setup); \ - for p in $$list; do echo "$$p $$p"; done | \ - sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ - $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ - if (++n[$$2] == $(am__install_max)) \ - { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ - END { for (dir in files) print dir, files[dir] }' -am__base_list = \ - sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ - sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' -am__uninstall_files_from_dir = { \ - test -z "$$files" \ - || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ - || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ - $(am__cd) "$$dir" && rm -f $$files; }; \ - } -am__installdirs = "$(DESTDIR)$(includedir)" -HEADERS = $(nobase_include_HEADERS) -am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) -# Read a list of newline-separated strings from the standard input, -# and print each of them once, without duplicates. Input order is -# *not* preserved. -am__uniquify_input = $(AWK) '\ - BEGIN { nonempty = 0; } \ - { items[$$0] = 1; nonempty = 1; } \ - END { if (nonempty) { for (i in items) print i; }; } \ -' -# Make sure the list of sources is unique. This is necessary because, -# e.g., the same source file might be shared among _SOURCES variables -# for different programs/libraries. -am__define_uniq_tagged_files = \ - list='$(am__tagged_files)'; \ - unique=`for i in $$list; do \ - if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ - done | $(am__uniquify_input)` -ETAGS = etags -CTAGS = ctags -DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) -ACLOCAL = @ACLOCAL@ -AMTAR = @AMTAR@ -AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ -APPLDFLAGS = @APPLDFLAGS@ -AR = @AR@ -AUTOCONF = @AUTOCONF@ -AUTOHEADER = @AUTOHEADER@ -AUTOMAKE = @AUTOMAKE@ -AWK = @AWK@ -BOOST_ASIO_LIB = @BOOST_ASIO_LIB@ -BOOST_CPPFLAGS = @BOOST_CPPFLAGS@ -BOOST_LDFLAGS = @BOOST_LDFLAGS@ -BOOST_SYSTEM_LIB = @BOOST_SYSTEM_LIB@ -BOOST_THREAD_LIB = @BOOST_THREAD_LIB@ -CC = @CC@ -CCDEPMODE = @CCDEPMODE@ -CFLAGS = @CFLAGS@ -CPP = @CPP@ -CPPFLAGS = @CPPFLAGS@ -CUNIT_CFLAGS = @CUNIT_CFLAGS@ -CUNIT_LIBS = @CUNIT_LIBS@ -CXX = @CXX@ -CXXCPP = @CXXCPP@ -CXXDEPMODE = @CXXDEPMODE@ -CXXFLAGS = @CXXFLAGS@ -CYGPATH_W = @CYGPATH_W@ -CYTHON = @CYTHON@ -DEFS = @DEFS@ -DEPDIR = @DEPDIR@ -DLLTOOL = @DLLTOOL@ -DSYMUTIL = @DSYMUTIL@ -DUMPBIN = @DUMPBIN@ -ECHO_C = @ECHO_C@ -ECHO_N = @ECHO_N@ -ECHO_T = @ECHO_T@ -EGREP = @EGREP@ -EXEEXT = @EXEEXT@ -FGREP = @FGREP@ -GREP = @GREP@ -HAVE_CXX11 = @HAVE_CXX11@ -INSTALL = @INSTALL@ -INSTALL_DATA = @INSTALL_DATA@ -INSTALL_PROGRAM = @INSTALL_PROGRAM@ -INSTALL_SCRIPT = @INSTALL_SCRIPT@ -INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ -JANSSON_CFLAGS = @JANSSON_CFLAGS@ -JANSSON_LIBS = @JANSSON_LIBS@ -JEMALLOC_LIBS = @JEMALLOC_LIBS@ -LD = @LD@ -LDFLAGS = @LDFLAGS@ -LIBEVENT_OPENSSL_CFLAGS = @LIBEVENT_OPENSSL_CFLAGS@ -LIBEVENT_OPENSSL_LIBS = @LIBEVENT_OPENSSL_LIBS@ -LIBEV_CFLAGS = @LIBEV_CFLAGS@ -LIBEV_LIBS = @LIBEV_LIBS@ -LIBOBJS = @LIBOBJS@ -LIBS = @LIBS@ -LIBSPDYLAY_CFLAGS = @LIBSPDYLAY_CFLAGS@ -LIBSPDYLAY_LIBS = @LIBSPDYLAY_LIBS@ -LIBTOOL = @LIBTOOL@ -LIPO = @LIPO@ -LN_S = @LN_S@ -LTLIBOBJS = @LTLIBOBJS@ -LT_AGE = @LT_AGE@ -LT_CURRENT = @LT_CURRENT@ -LT_REVISION = @LT_REVISION@ -MAKEINFO = @MAKEINFO@ -MANIFEST_TOOL = @MANIFEST_TOOL@ -MKDIR_P = @MKDIR_P@ -NM = @NM@ -NMEDIT = @NMEDIT@ -OBJDUMP = @OBJDUMP@ -OBJEXT = @OBJEXT@ -OPENSSL_CFLAGS = @OPENSSL_CFLAGS@ -OPENSSL_LIBS = @OPENSSL_LIBS@ -OTOOL = @OTOOL@ -OTOOL64 = @OTOOL64@ -PACKAGE = @PACKAGE@ -PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ -PACKAGE_NAME = @PACKAGE_NAME@ -PACKAGE_STRING = @PACKAGE_STRING@ -PACKAGE_TARNAME = @PACKAGE_TARNAME@ -PACKAGE_URL = @PACKAGE_URL@ -PACKAGE_VERSION = @PACKAGE_VERSION@ -PACKAGE_VERSION_NUM = @PACKAGE_VERSION_NUM@ -PATH_SEPARATOR = @PATH_SEPARATOR@ -PKG_CONFIG = @PKG_CONFIG@ -PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ -PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ -PYTHON = @PYTHON@ -PYTHON_CPPFLAGS = @PYTHON_CPPFLAGS@ -PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@ -PYTHON_EXTRA_LDFLAGS = @PYTHON_EXTRA_LDFLAGS@ -PYTHON_EXTRA_LIBS = @PYTHON_EXTRA_LIBS@ -PYTHON_LDFLAGS = @PYTHON_LDFLAGS@ -PYTHON_PLATFORM = @PYTHON_PLATFORM@ -PYTHON_PREFIX = @PYTHON_PREFIX@ -PYTHON_SITE_PKG = @PYTHON_SITE_PKG@ -PYTHON_VERSION = @PYTHON_VERSION@ -RANLIB = @RANLIB@ -SED = @SED@ -SET_MAKE = @SET_MAKE@ -SHELL = @SHELL@ -STRIP = @STRIP@ -TESTLDADD = @TESTLDADD@ -VERSION = @VERSION@ -WARNCFLAGS = @WARNCFLAGS@ -XML2_CONFIG = @XML2_CONFIG@ -XML_CPPFLAGS = @XML_CPPFLAGS@ -XML_LIBS = @XML_LIBS@ -ZLIB_CFLAGS = @ZLIB_CFLAGS@ -ZLIB_LIBS = @ZLIB_LIBS@ -abs_builddir = @abs_builddir@ -abs_srcdir = @abs_srcdir@ -abs_top_builddir = @abs_top_builddir@ -abs_top_srcdir = @abs_top_srcdir@ -ac_ct_AR = @ac_ct_AR@ -ac_ct_CC = @ac_ct_CC@ -ac_ct_CXX = @ac_ct_CXX@ -ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ -am__include = @am__include@ -am__leading_dot = @am__leading_dot@ -am__quote = @am__quote@ -am__tar = @am__tar@ -am__untar = @am__untar@ -bindir = @bindir@ -build = @build@ -build_alias = @build_alias@ -build_cpu = @build_cpu@ -build_os = @build_os@ -build_vendor = @build_vendor@ -builddir = @builddir@ -datadir = @datadir@ -datarootdir = @datarootdir@ -docdir = @docdir@ -dvidir = @dvidir@ -exec_prefix = @exec_prefix@ -host = @host@ -host_alias = @host_alias@ -host_cpu = @host_cpu@ -host_os = @host_os@ -host_vendor = @host_vendor@ -htmldir = @htmldir@ -includedir = @includedir@ -infodir = @infodir@ -install_sh = @install_sh@ -libdir = @libdir@ -libexecdir = @libexecdir@ -localedir = @localedir@ -localstatedir = @localstatedir@ -mandir = @mandir@ -mkdir_p = @mkdir_p@ -oldincludedir = @oldincludedir@ -pdfdir = @pdfdir@ -pkgpyexecdir = @pkgpyexecdir@ -pkgpythondir = @pkgpythondir@ -prefix = @prefix@ -program_transform_name = @program_transform_name@ -psdir = @psdir@ -pyexecdir = @pyexecdir@ -pythondir = @pythondir@ -sbindir = @sbindir@ -sharedstatedir = @sharedstatedir@ -srcdir = @srcdir@ -sysconfdir = @sysconfdir@ -target = @target@ -target_alias = @target_alias@ -target_cpu = @target_cpu@ -target_os = @target_os@ -target_vendor = @target_vendor@ -top_build_prefix = @top_build_prefix@ -top_builddir = @top_builddir@ -top_srcdir = @top_srcdir@ -@ENABLE_ASIO_LIB_TRUE@nobase_include_HEADERS = nghttp2/asio_http2.h nghttp2/asio_http2_client.h \ -@ENABLE_ASIO_LIB_TRUE@ nghttp2/asio_http2_server.h - -all: all-am - -.SUFFIXES: -$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) - @for dep in $?; do \ - case '$(am__configure_deps)' in \ - *$$dep*) \ - ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ - && { if test -f $@; then exit 0; else break; fi; }; \ - exit 1;; \ - esac; \ - done; \ - echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/includes/Makefile'; \ - $(am__cd) $(top_srcdir) && \ - $(AUTOMAKE) --gnu src/includes/Makefile -.PRECIOUS: Makefile -Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status - @case '$?' in \ - *config.status*) \ - cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ - *) \ - echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ - cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ - esac; - -$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) - cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh - -$(top_srcdir)/configure: $(am__configure_deps) - cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh -$(ACLOCAL_M4): $(am__aclocal_m4_deps) - cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh -$(am__aclocal_m4_deps): - -mostlyclean-libtool: - -rm -f *.lo - -clean-libtool: - -rm -rf .libs _libs -install-nobase_includeHEADERS: $(nobase_include_HEADERS) - @$(NORMAL_INSTALL) - @list='$(nobase_include_HEADERS)'; test -n "$(includedir)" || list=; \ - if test -n "$$list"; then \ - echo " $(MKDIR_P) '$(DESTDIR)$(includedir)'"; \ - $(MKDIR_P) "$(DESTDIR)$(includedir)" || exit 1; \ - fi; \ - $(am__nobase_list) | while read dir files; do \ - xfiles=; for file in $$files; do \ - if test -f "$$file"; then xfiles="$$xfiles $$file"; \ - else xfiles="$$xfiles $(srcdir)/$$file"; fi; done; \ - test -z "$$xfiles" || { \ - test "x$$dir" = x. || { \ - echo " $(MKDIR_P) '$(DESTDIR)$(includedir)/$$dir'"; \ - $(MKDIR_P) "$(DESTDIR)$(includedir)/$$dir"; }; \ - echo " $(INSTALL_HEADER) $$xfiles '$(DESTDIR)$(includedir)/$$dir'"; \ - $(INSTALL_HEADER) $$xfiles "$(DESTDIR)$(includedir)/$$dir" || exit $$?; }; \ - done - -uninstall-nobase_includeHEADERS: - @$(NORMAL_UNINSTALL) - @list='$(nobase_include_HEADERS)'; test -n "$(includedir)" || list=; \ - $(am__nobase_strip_setup); files=`$(am__nobase_strip)`; \ - dir='$(DESTDIR)$(includedir)'; $(am__uninstall_files_from_dir) - -ID: $(am__tagged_files) - $(am__define_uniq_tagged_files); mkid -fID $$unique -tags: tags-am -TAGS: tags - -tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) - set x; \ - here=`pwd`; \ - $(am__define_uniq_tagged_files); \ - shift; \ - if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ - test -n "$$unique" || unique=$$empty_fix; \ - if test $$# -gt 0; then \ - $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ - "$$@" $$unique; \ - else \ - $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ - $$unique; \ - fi; \ - fi -ctags: ctags-am - -CTAGS: ctags -ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) - $(am__define_uniq_tagged_files); \ - test -z "$(CTAGS_ARGS)$$unique" \ - || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ - $$unique - -GTAGS: - here=`$(am__cd) $(top_builddir) && pwd` \ - && $(am__cd) $(top_srcdir) \ - && gtags -i $(GTAGS_ARGS) "$$here" -cscopelist: cscopelist-am - -cscopelist-am: $(am__tagged_files) - list='$(am__tagged_files)'; \ - case "$(srcdir)" in \ - [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ - *) sdir=$(subdir)/$(srcdir) ;; \ - esac; \ - for i in $$list; do \ - if test -f "$$i"; then \ - echo "$(subdir)/$$i"; \ - else \ - echo "$$sdir/$$i"; \ - fi; \ - done >> $(top_builddir)/cscope.files - -distclean-tags: - -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags - -distdir: $(DISTFILES) - @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ - topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ - list='$(DISTFILES)'; \ - dist_files=`for file in $$list; do echo $$file; done | \ - sed -e "s|^$$srcdirstrip/||;t" \ - -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ - case $$dist_files in \ - */*) $(MKDIR_P) `echo "$$dist_files" | \ - sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ - sort -u` ;; \ - esac; \ - for file in $$dist_files; do \ - if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ - if test -d $$d/$$file; then \ - dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ - if test -d "$(distdir)/$$file"; then \ - find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ - fi; \ - if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ - cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ - find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ - fi; \ - cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ - else \ - test -f "$(distdir)/$$file" \ - || cp -p $$d/$$file "$(distdir)/$$file" \ - || exit 1; \ - fi; \ - done -check-am: all-am -check: check-am -all-am: Makefile $(HEADERS) -installdirs: - for dir in "$(DESTDIR)$(includedir)"; do \ - test -z "$$dir" || $(MKDIR_P) "$$dir"; \ - done -install: install-am -install-exec: install-exec-am -install-data: install-data-am -uninstall: uninstall-am - -install-am: all-am - @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am - -installcheck: installcheck-am -install-strip: - if test -z '$(STRIP)'; then \ - $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ - install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ - install; \ - else \ - $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ - install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ - "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ - fi -mostlyclean-generic: - -clean-generic: - -distclean-generic: - -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) - -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) - -maintainer-clean-generic: - @echo "This command is intended for maintainers to use" - @echo "it deletes files that may require special tools to rebuild." -clean: clean-am - -clean-am: clean-generic clean-libtool mostlyclean-am - -distclean: distclean-am - -rm -f Makefile -distclean-am: clean-am distclean-generic distclean-tags - -dvi: dvi-am - -dvi-am: - -html: html-am - -html-am: - -info: info-am - -info-am: - -install-data-am: install-nobase_includeHEADERS - -install-dvi: install-dvi-am - -install-dvi-am: - -install-exec-am: - -install-html: install-html-am - -install-html-am: - -install-info: install-info-am - -install-info-am: - -install-man: - -install-pdf: install-pdf-am - -install-pdf-am: - -install-ps: install-ps-am - -install-ps-am: - -installcheck-am: - -maintainer-clean: maintainer-clean-am - -rm -f Makefile -maintainer-clean-am: distclean-am maintainer-clean-generic - -mostlyclean: mostlyclean-am - -mostlyclean-am: mostlyclean-generic mostlyclean-libtool - -pdf: pdf-am - -pdf-am: - -ps: ps-am - -ps-am: - -uninstall-am: uninstall-nobase_includeHEADERS - -.MAKE: install-am install-strip - -.PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ - clean-libtool cscopelist-am ctags ctags-am distclean \ - distclean-generic distclean-libtool distclean-tags distdir dvi \ - dvi-am html html-am info info-am install install-am \ - install-data install-data-am install-dvi install-dvi-am \ - install-exec install-exec-am install-html install-html-am \ - install-info install-info-am install-man \ - install-nobase_includeHEADERS install-pdf install-pdf-am \ - install-ps install-ps-am install-strip installcheck \ - installcheck-am installdirs maintainer-clean \ - maintainer-clean-generic mostlyclean mostlyclean-generic \ - mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \ - uninstall-am uninstall-nobase_includeHEADERS - - -# Tell versions [3.59,3.63) of GNU make to not export all variables. -# Otherwise a system limit (for SysV at least) may be exceeded. -.NOEXPORT: diff --git a/src/includes/nghttp2/asio_http2.h b/src/includes/nghttp2/asio_http2.h deleted file mode 100644 index a54a9e8..0000000 --- a/src/includes/nghttp2/asio_http2.h +++ /dev/null @@ -1,139 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2014 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#ifndef ASIO_HTTP2_H -#define ASIO_HTTP2_H - -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include - -namespace boost { -namespace system { - -template <> struct is_error_code_enum { - BOOST_STATIC_CONSTANT(bool, value = true); -}; - -} // namespace system -} // namespace boost - -namespace nghttp2 { - -namespace asio_http2 { - -struct header_value { - // header field value - std::string value; - // true if the header field value is sensitive information, such as - // authorization information or short length secret cookies. If - // true, those header fields are not indexed by HPACK (but still - // huffman-encoded), which results in lesser compression. - bool sensitive; -}; - -// header fields. The header field name must be lower-cased. -using header_map = std::multimap; - -const boost::system::error_category &nghttp2_category() noexcept; - -struct uri_ref { - std::string scheme; - std::string host; - // form after percent-encoding decoded - std::string path; - // original path, percent-encoded - std::string raw_path; - // original query, percent-encoded - std::string raw_query; - std::string fragment; -}; - -// Callback function when data is arrived. EOF is indicated by -// passing 0 to the second parameter. -typedef std::function data_cb; -typedef std::function void_cb; -typedef std::function error_cb; -// Callback function when request and response are finished. The -// parameter indicates the cause of closure. -typedef std::function close_cb; - -// Callback function to generate response body. This function has the -// same semantics with nghttp2_data_source_read_callback. Just source -// and user_data parameters are removed. -// -// Basically, write at most |len| bytes to |data| and returns the -// number of bytes written. If there is no data left to send, set -// NGHTTP2_DATA_FLAG_EOF to *data_flags (e.g., *data_flags |= -// NGHTTP2_DATA_FLAG_EOF). If there is still data to send but they -// are not available right now, return NGHTTP2_ERR_DEFERRED. In case -// of the error and request/response must be closed, return -// NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE. -typedef std::function< - ssize_t(uint8_t *buf, std::size_t len, uint32_t *data_flags)> generator_cb; - -// Convenient function to create function to read file denoted by -// |path|. This can be passed to response::end(). -generator_cb file_generator(const std::string &path); - -// Like file_generator(const std::string&), but it takes opened file -// descriptor. The passed descriptor will be closed when returned -// function object is destroyed. -generator_cb file_generator_from_fd(int fd); - -// Validates path so that it does not contain directory traversal -// vector. Returns true if path is safe. The |path| must start with -// "/" otherwise returns false. This function should be called after -// percent-decode was performed. -bool check_path(const std::string &path); - -// Performs percent-decode against string |s|. -std::string percent_decode(const std::string &s); - -// Returns HTTP date representation of current posix time |t|. -std::string http_date(int64_t t); - -// Parses |uri| and extract scheme, host and service. The service is -// port component of URI (e.g., "8443") if available, otherwise it is -// scheme (e.g., "https"). -boost::system::error_code host_service_from_uri(boost::system::error_code &ec, - std::string &scheme, - std::string &host, - std::string &service, - const std::string &uri); - -} // namespace asio_http2 - -} // namespace nghttp2 - -#endif // ASIO_HTTP2_H diff --git a/src/includes/nghttp2/asio_http2_client.h b/src/includes/nghttp2/asio_http2_client.h deleted file mode 100644 index a2b51ad..0000000 --- a/src/includes/nghttp2/asio_http2_client.h +++ /dev/null @@ -1,195 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2015 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#ifndef ASIO_HTTP2_CLIENT_H -#define ASIO_HTTP2_CLIENT_H - -#include - -namespace nghttp2 { - -namespace asio_http2 { - -namespace client { - -class response_impl; - -class response { -public: - // Application must not call this directly. - response(); - ~response(); - - // Sets callback which is invoked when chunk of response body is - // received. - void on_data(data_cb cb) const; - - // Returns status code. - int status_code() const; - - // Returns content-length. -1 if it is unknown. - int64_t content_length() const; - - // Returns the response header fields. The pusedo header fields, - // which start with colon (:), are exluced from this list. - const header_map &header() const; - - // Application must not call this directly. - response_impl &impl() const; - -private: - std::unique_ptr impl_; -}; - -class request; - -using response_cb = std::function; -using request_cb = std::function; -using connect_cb = - std::function; - -class request_impl; - -class request { -public: - // Application must not call this directly. - request(); - ~request(); - - // Sets callback which is invoked when response header is received. - void on_response(response_cb cb) const; - - // Sets callback which is invoked when push request header is - // received. - void on_push(request_cb cb) const; - - // Sets callback which is invoked when this request and response are - // finished. After the invocation of this callback, the application - // must not access request and response object. - void on_close(close_cb cb) const; - - // Write trailer part. This must be called after setting both - // NGHTTP2_DATA_FLAG_EOF and NGHTTP2_DATA_FLAG_NO_END_STREAM set in - // *data_flag parameter in generator_cb passed to session::submit() - // function. - void write_trailer(header_map h) const; - - // Cancels this request and response with given error code. - void cancel(uint32_t error_code = NGHTTP2_INTERNAL_ERROR) const; - - // Resumes deferred uploading. - void resume() const; - - // Returns method (e.g., GET). - const std::string &method() const; - - // Returns request URI, split into components. - const uri_ref &uri() const; - - // Returns request header fields. The pusedo header fields, which - // start with colon (:), are exluced from this list. - const header_map &header() const; - - // Application must not call this directly. - request_impl &impl() const; - -private: - std::unique_ptr impl_; -}; - -class session_impl; - -class session { -public: - // Starts HTTP/2 session by connecting to |host| and |service| - // (e.g., "80") using clear text TCP connection. - session(boost::asio::io_service &io_service, const std::string &host, - const std::string &service); - - // Starts HTTP/2 session by connecting to |host| and |service| - // (e.g., "443") using encrypted SSL/TLS connection. - session(boost::asio::io_service &io_service, - boost::asio::ssl::context &tls_context, const std::string &host, - const std::string &service); - ~session(); - - session(session &&other) noexcept; - session &operator=(session &&other) noexcept; - - // Sets callback which is invoked after connection is established. - void on_connect(connect_cb cb) const; - - // Sets callback which is invoked there is connection level error - // and session is terminated. - void on_error(error_cb cb) const; - - // Shutdowns connection. - void shutdown() const; - - // Returns underlying io_service object. - boost::asio::io_service &io_service() const; - - // Submits request to server using |method| (e.g., "GET"), |uri| - // (e.g., "http://localhost/") and optionally additional header - // fields. This function returns pointer to request object if it - // succeeds, or nullptr and |ec| contains error message. - const request *submit(boost::system::error_code &ec, - const std::string &method, const std::string &uri, - header_map h = header_map{}) const; - - // Submits request to server using |method| (e.g., "GET"), |uri| - // (e.g., "http://localhost/") and optionally additional header - // fields. The |data| is request body. This function returns - // pointer to request object if it succeeds, or nullptr and |ec| - // contains error message. - const request *submit(boost::system::error_code &ec, - const std::string &method, const std::string &uri, - std::string data, header_map h = header_map{}) const; - - // Submits request to server using |method| (e.g., "GET"), |uri| - // (e.g., "http://localhost/") and optionally additional header - // fields. The |cb| is used to generate request body. This - // function returns pointer to request object if it succeeds, or - // nullptr and |ec| contains error message. - const request *submit(boost::system::error_code &ec, - const std::string &method, const std::string &uri, - generator_cb cb, header_map h = header_map{}) const; - -private: - std::unique_ptr impl_; -}; - -// configure |tls_ctx| for client use. Currently, we just set NPN -// callback for HTTP/2. -boost::system::error_code -configure_tls_context(boost::system::error_code &ec, - boost::asio::ssl::context &tls_ctx); - -} // namespace client - -} // namespace asio_http2 - -} // namespace nghttp2 - -#endif // ASIO_HTTP2_CLIENT_H diff --git a/src/includes/nghttp2/asio_http2_server.h b/src/includes/nghttp2/asio_http2_server.h deleted file mode 100644 index 1c5a71f..0000000 --- a/src/includes/nghttp2/asio_http2_server.h +++ /dev/null @@ -1,229 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2015 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#ifndef ASIO_HTTP2_SERVER_H -#define ASIO_HTTP2_SERVER_H - -#include - -namespace nghttp2 { - -namespace asio_http2 { - -namespace server { - -class request_impl; -class response_impl; - -class request { -public: - // Application must not call this directly. - request(); - ~request(); - - // Returns request header fields. The pusedo header fields, which - // start with colon (:), are exluced from this list. - const header_map &header() const; - - // Returns method (e.g., GET). - const std::string &method() const; - - // Returns request URI, split into components. - const uri_ref &uri() const; - - // Sets callback which is invoked when chunk of request body is - // received. - void on_data(data_cb cb) const; - - // Application must not call this directly. - request_impl &impl() const; - -private: - std::unique_ptr impl_; -}; - -class response { -public: - // Application must not call this directly. - response(); - ~response(); - - // Write response header using |status_code| (e.g., 200) and - // additional header fields in |h|. - void write_head(unsigned int status_code, header_map h = header_map{}) const; - - // Sends |data| as request body. No further call of end() is - // allowed. - void end(std::string data = "") const; - - // Sets callback as a generator of the response body. No further - // call of end() is allowed. - void end(generator_cb cb) const; - - // Write trailer part. This must be called after setting both - // NGHTTP2_DATA_FLAG_EOF and NGHTTP2_DATA_FLAG_NO_END_STREAM set in - // *data_flag parameter in generator_cb passed to end() function. - void write_trailer(header_map h) const; - - // Sets callback which is invoked when this request and response are - // finished. After the invocation of this callback, the application - // must not access request and response object. - void on_close(close_cb cb) const; - - // Cancels this request and response with given error code. - void cancel(uint32_t error_code = NGHTTP2_INTERNAL_ERROR) const; - - // Resumes deferred response. - void resume() const; - - // Pushes resource denoted by |raw_path_query| using |method|. The - // additional header fields can be given in |h|. This function - // returns pointer to response object for promised stream, otherwise - // nullptr and error code is filled in |ec|. Be aware that the - // header field name given in |h| must be lower-cased. - const response *push(boost::system::error_code &ec, std::string method, - std::string raw_path_query, - header_map h = header_map{}) const; - - // Returns status code. - unsigned int status_code() const; - - // Returns boost::asio::io_service this response is running on. - boost::asio::io_service &io_service() const; - - // Application must not call this directly. - response_impl &impl() const; - -private: - std::unique_ptr impl_; -}; - -// This is so called request callback. Called every time request is -// received. The life time of |request| and |response| objects end -// when callback set by response::on_close() is called. After that, -// the application must not access to those objects. -typedef std::function request_cb; - -class http2_impl; - -class http2 { -public: - http2(); - ~http2(); - - http2(http2 &&other) noexcept; - http2 &operator=(http2 &&other) noexcept; - - // Starts listening connection on given address and port and serves - // incoming requests in cleartext TCP connection. If |asynchronous| - // is false, this function blocks forever unless there is an error. - // If it is true, after server has started, this function returns - // immediately, and the caller should call join() to shutdown server - // gracefully. - boost::system::error_code listen_and_serve(boost::system::error_code &ec, - const std::string &address, - const std::string &port, - bool asynchronous = false); - - // Starts listening connection on given address and port and serves - // incoming requests in SSL/TLS encrypted connection. For - // |asynchronous| parameter, see cleartext version - // |listen_and_serve|. - boost::system::error_code - listen_and_serve(boost::system::error_code &ec, - boost::asio::ssl::context &tls_context, - const std::string &address, const std::string &port, - bool asynchronous = false); - - // Registers request handler |cb| with path pattern |pattern|. This - // function will fail and returns false if same pattern has been - // already registered or |pattern| is empty string. Otherwise - // returns true. The pattern match rule is the same as - // net/http/ServeMux in golang. Quoted from golang manual - // (http://golang.org/pkg/net/http/#ServeMux): - // - // Patterns name fixed, rooted paths, like "/favicon.ico", or - // rooted subtrees, like "/images/" (note the trailing - // slash). Longer patterns take precedence over shorter ones, so - // that if there are handlers registered for both "/images/" and - // "/images/thumbnails/", the latter handler will be called for - // paths beginning "/images/thumbnails/" and the former will - // receive requests for any other paths in the "/images/" subtree. - // - // Note that since a pattern ending in a slash names a rooted - // subtree, the pattern "/" matches all paths not matched by other - // registered patterns, not just the URL with Path == "/". - // - // Patterns may optionally begin with a host name, restricting - // matches to URLs on that host only. Host-specific patterns take - // precedence over general patterns, so that a handler might - // register for the two patterns "/codesearch" and - // "codesearch.google.com/" without also taking over requests for - // "http://www.google.com/". - // - // Just like ServeMux in golang, URL request path is sanitized and - // if they contains . or .. elements, they are redirected to an - // equivalent .- and ..-free URL. - bool handle(std::string pattern, request_cb cb); - - // Sets number of native threads to handle incoming HTTP request. - // It defaults to 1. - void num_threads(size_t num_threads); - - // Sets the maximum length to which the queue of pending - // connections. - void backlog(int backlog); - - // Gracefully stop http2 server - void stop(); - - // Join on http2 server and wait for it to fully stop - void join(); - -private: - std::unique_ptr impl_; -}; - -// Configures |tls_context| for server use. This function sets couple -// of OpenSSL options (disables SSLv2 and SSLv3 and compression) and -// enables ECDHE ciphers. NPN callback is also configured. -boost::system::error_code -configure_tls_context_easy(boost::system::error_code &ec, - boost::asio::ssl::context &tls_context); - -// Returns request handler to do redirect to |uri| using -// |status_code|. The |uri| appears in "location" header field as is. -request_cb redirect_handler(int status_code, std::string uri); - -// Returns request handler to reply with given |status_code| and HTML -// including message about status code. -request_cb status_handler(int status_code); - -} // namespace server - -} // namespace asio_http2 - -} // namespace nghttp2 - -#endif // ASIO_HTTP2_SERVER_H diff --git a/src/inflatehd.cc b/src/inflatehd.cc deleted file mode 100644 index 98338be..0000000 --- a/src/inflatehd.cc +++ /dev/null @@ -1,277 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2013 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#ifdef HAVE_CONFIG_H -#include -#endif // HAVE_CONFIG_H - -#ifdef HAVE_UNISTD_H -#include -#endif // HAVE_UNISTD_H -#include - -#include -#include -#include -#include -#include -#include -#include - -#include - -extern "C" { - -#include "nghttp2_hd.h" -#include "nghttp2_frame.h" - -#include "comp_helper.h" -} - -typedef struct { int dump_header_table; } inflate_config; - -static inflate_config config; - -static uint8_t to_ud(char c) { - if (c >= 'A' && c <= 'Z') { - return c - 'A' + 10; - } else if (c >= 'a' && c <= 'z') { - return c - 'a' + 10; - } else { - return c - '0'; - } -} - -static void decode_hex(uint8_t *dest, const char *src, size_t len) { - size_t i; - for (i = 0; i < len; i += 2) { - *dest++ = to_ud(src[i]) << 4 | to_ud(src[i + 1]); - } -} - -static void to_json(nghttp2_hd_inflater *inflater, json_t *headers, - json_t *wire, int seq, size_t old_settings_table_size) { - auto obj = json_object(); - json_object_set_new(obj, "seq", json_integer(seq)); - json_object_set(obj, "wire", wire); - json_object_set(obj, "headers", headers); - if (old_settings_table_size != inflater->settings_hd_table_bufsize_max) { - json_object_set_new(obj, "header_table_size", - json_integer(inflater->settings_hd_table_bufsize_max)); - } - if (config.dump_header_table) { - json_object_set_new(obj, "header_table", dump_header_table(&inflater->ctx)); - } - json_dumpf(obj, stdout, JSON_INDENT(2) | JSON_PRESERVE_ORDER); - json_decref(obj); - printf("\n"); -} - -static int inflate_hd(json_t *obj, nghttp2_hd_inflater *inflater, int seq) { - ssize_t rv; - nghttp2_nv nv; - int inflate_flags; - size_t old_settings_table_size = inflater->settings_hd_table_bufsize_max; - - auto wire = json_object_get(obj, "wire"); - - if (wire == nullptr) { - fprintf(stderr, "'wire' key is missing at %d\n", seq); - return -1; - } - - if (!json_is_string(wire)) { - fprintf(stderr, "'wire' value is not string at %d\n", seq); - return -1; - } - - auto table_size = json_object_get(obj, "header_table_size"); - - if (table_size) { - if (!json_is_integer(table_size)) { - fprintf(stderr, - "The value of 'header_table_size key' is not integer at %d\n", - seq); - return -1; - } - rv = nghttp2_hd_inflate_change_table_size(inflater, - json_integer_value(table_size)); - if (rv != 0) { - fprintf(stderr, - "nghttp2_hd_change_table_size() failed with error %s at %d\n", - nghttp2_strerror(rv), seq); - return -1; - } - } - - auto inputlen = strlen(json_string_value(wire)); - - if (inputlen & 1) { - fprintf(stderr, "Badly formatted output value at %d\n", seq); - exit(EXIT_FAILURE); - } - - auto buflen = inputlen / 2; - auto buf = std::vector(buflen); - - decode_hex(buf.data(), json_string_value(wire), inputlen); - - auto headers = json_array(); - - auto p = buf.data(); - for (;;) { - inflate_flags = 0; - rv = nghttp2_hd_inflate_hd(inflater, &nv, &inflate_flags, p, buflen, 1); - if (rv < 0) { - fprintf(stderr, "inflate failed with error code %zd at %d\n", rv, seq); - exit(EXIT_FAILURE); - } - p += rv; - buflen -= rv; - if (inflate_flags & NGHTTP2_HD_INFLATE_EMIT) { - json_array_append_new( - headers, dump_header(nv.name, nv.namelen, nv.value, nv.valuelen)); - } - if (inflate_flags & NGHTTP2_HD_INFLATE_FINAL) { - break; - } - } - assert(buflen == 0); - nghttp2_hd_inflate_end_headers(inflater); - to_json(inflater, headers, wire, seq, old_settings_table_size); - json_decref(headers); - - return 0; -} - -static int perform(void) { - nghttp2_hd_inflater *inflater = NULL; - json_error_t error; - - auto json = json_loadf(stdin, 0, &error); - - if (json == nullptr) { - fprintf(stderr, "JSON loading failed\n"); - exit(EXIT_FAILURE); - } - - auto cases = json_object_get(json, "cases"); - - if (cases == nullptr) { - fprintf(stderr, "Missing 'cases' key in root object\n"); - exit(EXIT_FAILURE); - } - - if (!json_is_array(cases)) { - fprintf(stderr, "'cases' must be JSON array\n"); - exit(EXIT_FAILURE); - } - - nghttp2_hd_inflate_new(&inflater); - output_json_header(); - auto len = json_array_size(cases); - - for (size_t i = 0; i < len; ++i) { - auto obj = json_array_get(cases, i); - if (!json_is_object(obj)) { - fprintf(stderr, "Unexpected JSON type at %zu. It should be object.\n", i); - continue; - } - if (inflate_hd(obj, inflater, i) != 0) { - continue; - } - if (i + 1 < len) { - printf(",\n"); - } - } - output_json_footer(); - nghttp2_hd_inflate_del(inflater); - json_decref(json); - - return 0; -} - -static void print_help(void) { - std::cout << R"(HPACK HTTP/2 header decoder -Usage: inflatehd [OPTIONS] < INPUT - -Reads JSON data from stdin and outputs inflated name/value pairs in -JSON. - -The root JSON object must contain "context" key, which indicates which -compression context is used. If it is "request", request compression -context is used. Otherwise, response compression context is used. -The value of "cases" key contains the sequence of compressed header -block. They share the same compression context and are processed in -the order they appear. Each item in the sequence is a JSON object and -it must have at least "wire" key. Its value is a string containing -compressed header block in hex string. - -Example: - -{ - "context": "request", - "cases": - [ - { "wire": "0284f77778ff" }, - { "wire": "0185fafd3c3c7f81" } - ] -} - -The output of this program can be used as input for deflatehd. - -OPTIONS: - -d, --dump-header-table - Output dynamic header table.)" << std::endl; - ; -} - -static struct option long_options[] = { - {"dump-header-table", no_argument, nullptr, 'd'}, {nullptr, 0, nullptr, 0}}; - -int main(int argc, char **argv) { - config.dump_header_table = 0; - while (1) { - int option_index = 0; - int c = getopt_long(argc, argv, "dh", long_options, &option_index); - if (c == -1) { - break; - } - switch (c) { - case 'h': - print_help(); - exit(EXIT_SUCCESS); - case 'd': - // --dump-header-table - config.dump_header_table = 1; - break; - case '?': - exit(EXIT_FAILURE); - default: - break; - } - } - perform(); - return 0; -} diff --git a/src/libnghttp2_asio.pc.in b/src/libnghttp2_asio.pc.in deleted file mode 100644 index ec4fbbb..0000000 --- a/src/libnghttp2_asio.pc.in +++ /dev/null @@ -1,33 +0,0 @@ -# nghttp2 - HTTP/2 C Library - -# Copyright (c) 2014 Tatsuhiro Tsujikawa - -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: - -# The above copyright notice and this permission notice shall be -# included in all copies or substantial portions of the Software. - -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -prefix=@prefix@ -exec_prefix=@exec_prefix@ -libdir=@libdir@ -includedir=@includedir@ - -Name: libnghttp2_asio -Description: HTTP/2 C++ library -URL: https://github.com/tatsuhiro-t/nghttp2 -Version: @VERSION@ -Libs: -L${libdir} -lnghttp2_asio -Cflags: -I${includedir} diff --git a/src/memchunk.h b/src/memchunk.h deleted file mode 100644 index 0f12c15..0000000 --- a/src/memchunk.h +++ /dev/null @@ -1,260 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2014 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#ifndef MEMCHUNK_H -#define MEMCHUNK_H - -#include "nghttp2_config.h" - -#include - -#include -#include -#include -#include -#include - -#include "template.h" - -namespace nghttp2 { - -template struct Memchunk { - Memchunk(std::unique_ptr next_chunk) - : pos(std::begin(buf)), last(pos), knext(std::move(next_chunk)), - kprev(nullptr), next(nullptr) { - if (knext) { - knext->kprev = this; - } - } - size_t len() const { return last - pos; } - size_t left() const { return std::end(buf) - last; } - void reset() { pos = last = std::begin(buf); } - std::array buf; - uint8_t *pos, *last; - std::unique_ptr knext; - Memchunk *kprev; - Memchunk *next; - static const size_t size = N; -}; - -template struct Pool { - Pool() : pool(nullptr), freelist(nullptr), poolsize(0) {} - T *get() { - if (freelist) { - auto m = freelist; - freelist = freelist->next; - m->next = nullptr; - m->reset(); - return m; - } - - pool = make_unique(std::move(pool)); - poolsize += T::size; - return pool.get(); - } - void recycle(T *m) { - if (freelist) { - m->next = freelist; - } else { - m->next = nullptr; - } - freelist = m; - } - void shrink(size_t max) { - auto m = freelist; - for (; m && poolsize > max;) { - auto next = m->next; - poolsize -= T::size; - auto p = m->kprev; - if (p) { - p->knext = std::move(m->knext); - if (p->knext) { - p->knext->kprev = p; - } - } else { - pool = std::move(m->knext); - if (pool) { - pool->kprev = nullptr; - } - } - m = next; - } - freelist = m; - } - void clear() { - freelist = nullptr; - pool = nullptr; - poolsize = 0; - } - using value_type = T; - std::unique_ptr pool; - T *freelist; - size_t poolsize; -}; - -template struct Memchunks { - Memchunks(Pool *pool) - : pool(pool), head(nullptr), tail(nullptr), len(0) {} - ~Memchunks() { - if (!pool) { - return; - } - for (auto m = head; m;) { - auto next = m->next; - pool->recycle(m); - m = next; - } - } - size_t append(const void *src, size_t count) { - if (count == 0) { - return 0; - } - - auto first = static_cast(src); - auto last = first + count; - - if (!tail) { - head = tail = pool->get(); - } - - for (;;) { - auto n = std::min(static_cast(last - first), tail->left()); - tail->last = std::copy_n(first, n, tail->last); - first += n; - len += n; - if (first == last) { - break; - } - - tail->next = pool->get(); - tail = tail->next; - } - - return count; - } - template size_t append(const char (&s)[N]) { - return append(s, N - 1); - } - size_t remove(void *dest, size_t count) { - if (!tail || count == 0) { - return 0; - } - - auto first = static_cast(dest); - auto last = first + count; - - auto m = head; - - while (m) { - auto next = m->next; - auto n = std::min(static_cast(last - first), m->len()); - - assert(m->len()); - first = std::copy_n(m->pos, n, first); - m->pos += n; - len -= n; - if (m->len() > 0) { - break; - } - pool->recycle(m); - m = next; - } - head = m; - if (head == nullptr) { - tail = nullptr; - } - - return first - static_cast(dest); - } - size_t drain(size_t count) { - auto ndata = count; - auto m = head; - while (m) { - auto next = m->next; - auto n = std::min(count, m->len()); - m->pos += n; - count -= n; - len -= n; - if (m->len() > 0) { - break; - } - - pool->recycle(m); - m = next; - } - head = m; - if (head == nullptr) { - tail = nullptr; - } - return ndata - count; - } - int riovec(struct iovec *iov, int iovcnt) { - if (!head) { - return 0; - } - auto m = head; - int i; - for (i = 0; i < iovcnt && m; ++i, m = m->next) { - iov[i].iov_base = m->pos; - iov[i].iov_len = m->len(); - } - return i; - } - size_t rleft() const { return len; } - - Pool *pool; - Memchunk *head, *tail; - size_t len; -}; - -using Memchunk16K = Memchunk<16384>; -using MemchunkPool = Pool; -using DefaultMemchunks = Memchunks; - -#define DEFAULT_WR_IOVCNT 16 - -#if defined(IOV_MAX) && IOV_MAX < DEFAULT_WR_IOVCNT -#define MAX_WR_IOVCNT IOV_MAX -#else // !defined(IOV_MAX) || IOV_MAX >= DEFAULT_WR_IOVCNT -#define MAX_WR_IOVCNT DEFAULT_WR_IOVCNT -#endif // !defined(IOV_MAX) || IOV_MAX >= DEFAULT_WR_IOVCNT - -inline int limit_iovec(struct iovec *iov, int iovcnt, size_t max) { - if (max == 0) { - return 0; - } - for (int i = 0; i < iovcnt; ++i) { - auto d = std::min(max, iov[i].iov_len); - iov[i].iov_len = d; - max -= d; - if (max == 0) { - return i + 1; - } - } - return iovcnt; -} - -} // namespace nghttp2 - -#endif // MEMCHUNK_H diff --git a/src/memchunk_test.cc b/src/memchunk_test.cc deleted file mode 100644 index f95f303..0000000 --- a/src/memchunk_test.cc +++ /dev/null @@ -1,199 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2015 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#include "memchunk_test.h" - -#include - -#include - -#include "memchunk.h" -#include "util.h" - -namespace nghttp2 { - -void test_pool_recycle(void) { - MemchunkPool pool; - - CU_ASSERT(!pool.pool); - CU_ASSERT(0 == pool.poolsize); - CU_ASSERT(nullptr == pool.freelist); - - auto m1 = pool.get(); - - CU_ASSERT(m1 == pool.pool.get()); - CU_ASSERT(MemchunkPool::value_type::size == pool.poolsize); - CU_ASSERT(nullptr == pool.freelist); - - auto m2 = pool.get(); - - CU_ASSERT(m2 == pool.pool.get()); - CU_ASSERT(2 * MemchunkPool::value_type::size == pool.poolsize); - CU_ASSERT(nullptr == pool.freelist); - CU_ASSERT(m1 == m2->knext.get()); - CU_ASSERT(nullptr == m1->knext.get()); - - auto m3 = pool.get(); - - CU_ASSERT(m3 == pool.pool.get()); - CU_ASSERT(3 * MemchunkPool::value_type::size == pool.poolsize); - CU_ASSERT(nullptr == pool.freelist); - - pool.recycle(m3); - - CU_ASSERT(m3 == pool.pool.get()); - CU_ASSERT(3 * MemchunkPool::value_type::size == pool.poolsize); - CU_ASSERT(m3 == pool.freelist); - - auto m4 = pool.get(); - - CU_ASSERT(m3 == m4); - CU_ASSERT(m4 == pool.pool.get()); - CU_ASSERT(3 * MemchunkPool::value_type::size == pool.poolsize); - CU_ASSERT(nullptr == pool.freelist); - - pool.recycle(m2); - pool.recycle(m1); - - CU_ASSERT(m1 == pool.freelist); - CU_ASSERT(m2 == m1->next); - CU_ASSERT(nullptr == m2->next); -} - -using Memchunk16 = Memchunk<16>; -using MemchunkPool16 = Pool; -using Memchunks16 = Memchunks; - -void test_memchunks_append(void) { - MemchunkPool16 pool; - Memchunks16 chunks(&pool); - - chunks.append("012"); - - auto m = chunks.tail; - - CU_ASSERT(3 == m->len()); - CU_ASSERT(13 == m->left()); - - chunks.append("3456789abcdef@"); - - CU_ASSERT(16 == m->len()); - CU_ASSERT(0 == m->left()); - - m = chunks.tail; - - CU_ASSERT(1 == m->len()); - CU_ASSERT(15 == m->left()); - CU_ASSERT(17 == chunks.rleft()); - - char buf[16]; - size_t nread; - - nread = chunks.remove(buf, 8); - - CU_ASSERT(8 == nread); - CU_ASSERT(0 == memcmp("01234567", buf, nread)); - CU_ASSERT(9 == chunks.rleft()); - - nread = chunks.remove(buf, sizeof(buf)); - - CU_ASSERT(9 == nread); - CU_ASSERT(0 == memcmp("89abcdef@", buf, nread)); - CU_ASSERT(0 == chunks.rleft()); - CU_ASSERT(nullptr == chunks.head); - CU_ASSERT(nullptr == chunks.tail); - CU_ASSERT(32 == pool.poolsize); -} - -void test_memchunks_drain(void) { - MemchunkPool16 pool; - Memchunks16 chunks(&pool); - - chunks.append("0123456789"); - - size_t nread; - - nread = chunks.drain(3); - - CU_ASSERT(3 == nread); - - char buf[16]; - - nread = chunks.remove(buf, sizeof(buf)); - - CU_ASSERT(7 == nread); - CU_ASSERT(0 == memcmp("3456789", buf, nread)); -} - -void test_memchunks_riovec(void) { - MemchunkPool16 pool; - Memchunks16 chunks(&pool); - - char buf[3 * 16]; - - chunks.append(buf, sizeof(buf)); - - std::array iov; - auto iovcnt = chunks.riovec(iov.data(), iov.size()); - - auto m = chunks.head; - - CU_ASSERT(2 == iovcnt); - CU_ASSERT(m->buf.data() == iov[0].iov_base); - CU_ASSERT(m->len() == iov[0].iov_len); - - m = m->next; - - CU_ASSERT(m->buf.data() == iov[1].iov_base); - CU_ASSERT(m->len() == iov[1].iov_len); - - chunks.drain(2 * 16); - - iovcnt = chunks.riovec(iov.data(), iov.size()); - - CU_ASSERT(1 == iovcnt); - - m = chunks.head; - CU_ASSERT(m->buf.data() == iov[0].iov_base); - CU_ASSERT(m->len() == iov[0].iov_len); -} - -void test_memchunks_recycle(void) { - MemchunkPool16 pool; - { - Memchunks16 chunks(&pool); - char buf[32]; - chunks.append(buf, sizeof(buf)); - } - CU_ASSERT(32 == pool.poolsize); - CU_ASSERT(nullptr != pool.freelist); - - auto m = pool.freelist; - m = m->next; - - CU_ASSERT(nullptr != m); - CU_ASSERT(nullptr == m->next); -} - -} // namespace nghttp2 diff --git a/src/memchunk_test.h b/src/memchunk_test.h deleted file mode 100644 index 9d170a4..0000000 --- a/src/memchunk_test.h +++ /dev/null @@ -1,42 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2015 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#ifndef MEMCHUNK_TEST_H -#define MEMCHUNK_TEST_H - -#ifdef HAVE_CONFIG_H -#include -#endif // HAVE_CONFIG_H - -namespace nghttp2 { - -void test_pool_recycle(void); -void test_memchunks_append(void); -void test_memchunks_drain(void); -void test_memchunks_riovec(void); -void test_memchunks_recycle(void); - -} // namespace nghttp2 - -#endif // MEMCHUNK_TEST_H diff --git a/src/nghttp.cc b/src/nghttp.cc deleted file mode 100644 index 8f6361e..0000000 --- a/src/nghttp.cc +++ /dev/null @@ -1,2652 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2013 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#include "nghttp.h" - -#include -#ifdef HAVE_UNISTD_H -#include -#endif // HAVE_UNISTD_H -#ifdef HAVE_FCNTL_H -#include -#endif // HAVE_FCNTL_H -#ifdef HAVE_NETINET_IN_H -#include -#endif // HAVE_NETINET_IN_H -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#ifdef HAVE_JANSSON -#include -#endif // HAVE_JANSSON - -#include "app_helper.h" -#include "HtmlParser.h" -#include "util.h" -#include "base64.h" -#include "ssl.h" -#include "template.h" - -#ifndef O_BINARY -#define O_BINARY (0) -#endif // O_BINARY - -namespace nghttp2 { - -// The anchor stream nodes when --no-dep is not used. The stream ID = -// 1 is excluded since it is used as first stream in upgrade case. We -// follows the same dependency anchor nodes as Firefox does. -struct Anchor { - int32_t stream_id; - // stream ID this anchor depends on - int32_t dep_stream_id; - // .. with this weight. - int32_t weight; -}; - -// This is index into anchors. Firefox uses ANCHOR_FOLLOWERS for html -// file. -enum { - ANCHOR_LEADERS, - ANCHOR_UNBLOCKED, - ANCHOR_BACKGROUND, - ANCHOR_SPECULATIVE, - ANCHOR_FOLLOWERS, -}; - -namespace { -auto anchors = std::array{{ - {3, 0, 201}, {5, 0, 101}, {7, 0, 1}, {9, 7, 1}, {11, 3, 1}, -}}; -} // namespace - -Config::Config() - : output_upper_thres(1024 * 1024), padding(0), - peer_max_concurrent_streams(NGHTTP2_INITIAL_MAX_CONCURRENT_STREAMS), - header_table_size(-1), weight(NGHTTP2_DEFAULT_WEIGHT), multiply(1), - timeout(0.), window_bits(-1), connection_window_bits(-1), verbose(0), - null_out(false), remote_name(false), get_assets(false), stat(false), - upgrade(false), continuation(false), no_content_length(false), - no_dep(false), hexdump(false), no_push(false) { - nghttp2_option_new(&http2_option); - nghttp2_option_set_peer_max_concurrent_streams(http2_option, - peer_max_concurrent_streams); -} - -Config::~Config() { nghttp2_option_del(http2_option); } - -namespace { -Config config; -} // namespace - -namespace { -void print_protocol_nego_error() { - std::cerr << "[ERROR] HTTP/2 protocol was not selected." - << " (nghttp2 expects " << NGHTTP2_PROTO_VERSION_ID << ")" - << std::endl; -} -} // namespace - -namespace { -std::string strip_fragment(const char *raw_uri) { - const char *end; - for (end = raw_uri; *end && *end != '#'; ++end) - ; - size_t len = end - raw_uri; - return std::string(raw_uri, len); -} -} // namespace - -Request::Request(const std::string &uri, const http_parser_url &u, - const nghttp2_data_provider *data_prd, int64_t data_length, - const nghttp2_priority_spec &pri_spec, int level) - : uri(uri), u(u), pri_spec(pri_spec), data_length(data_length), - data_offset(0), response_len(0), inflater(nullptr), html_parser(nullptr), - data_prd(data_prd), stream_id(-1), status(0), level(level), - expect_final_response(false) { - http2::init_hdidx(res_hdidx); - http2::init_hdidx(req_hdidx); -} - -Request::~Request() { - nghttp2_gzip_inflate_del(inflater); - delete html_parser; -} - -void Request::init_inflater() { - int rv; - rv = nghttp2_gzip_inflate_new(&inflater); - assert(rv == 0); -} - -void Request::init_html_parser() { html_parser = new HtmlParser(uri); } - -int Request::update_html_parser(const uint8_t *data, size_t len, int fin) { - if (!html_parser) { - return 0; - } - return html_parser->parse_chunk(reinterpret_cast(data), len, - fin); -} - -std::string Request::make_reqpath() const { - std::string path = util::has_uri_field(u, UF_PATH) - ? util::get_uri_field(uri.c_str(), u, UF_PATH) - : "/"; - if (util::has_uri_field(u, UF_QUERY)) { - path += "?"; - path.append(uri.c_str() + u.field_data[UF_QUERY].off, - u.field_data[UF_QUERY].len); - } - return path; -} - -namespace { -nghttp2_priority_spec resolve_dep(int res_type) { - nghttp2_priority_spec pri_spec; - - if (config.no_dep) { - nghttp2_priority_spec_default_init(&pri_spec); - - return pri_spec; - } - - int32_t anchor_id; - int32_t weight; - switch (res_type) { - case REQ_CSS: - case REQ_JS: - anchor_id = anchors[ANCHOR_LEADERS].stream_id; - weight = 2; - break; - case REQ_UNBLOCK_JS: - anchor_id = anchors[ANCHOR_UNBLOCKED].stream_id; - weight = 2; - break; - case REQ_IMG: - anchor_id = anchors[ANCHOR_FOLLOWERS].stream_id; - weight = 12; - break; - default: - anchor_id = anchors[ANCHOR_FOLLOWERS].stream_id; - weight = 2; - } - - nghttp2_priority_spec_init(&pri_spec, anchor_id, weight, 0); - return pri_spec; -} -} // namespace - -bool Request::is_ipv6_literal_addr() const { - if (util::has_uri_field(u, UF_HOST)) { - return memchr(uri.c_str() + u.field_data[UF_HOST].off, ':', - u.field_data[UF_HOST].len); - } else { - return false; - } -} - -bool Request::response_pseudo_header_allowed(int16_t token) const { - if (!res_nva.empty() && res_nva.back().name.c_str()[0] != ':') { - return false; - } - switch (token) { - case http2::HD__STATUS: - return res_hdidx[token] == -1; - default: - return false; - } -} - -bool Request::push_request_pseudo_header_allowed(int16_t token) const { - if (!req_nva.empty() && req_nva.back().name.c_str()[0] != ':') { - return false; - } - switch (token) { - case http2::HD__AUTHORITY: - case http2::HD__METHOD: - case http2::HD__PATH: - case http2::HD__SCHEME: - return req_hdidx[token] == -1; - default: - return false; - } -} - -Headers::value_type *Request::get_res_header(int16_t token) { - auto idx = res_hdidx[token]; - if (idx == -1) { - return nullptr; - } - return &res_nva[idx]; -} - -Headers::value_type *Request::get_req_header(int16_t token) { - auto idx = req_hdidx[token]; - if (idx == -1) { - return nullptr; - } - return &req_nva[idx]; -} - -void Request::record_request_start_time() { - timing.state = RequestState::ON_REQUEST; - timing.request_start_time = get_time(); -} - -void Request::record_response_start_time() { - timing.state = RequestState::ON_RESPONSE; - timing.response_start_time = get_time(); -} - -void Request::record_response_end_time() { - timing.state = RequestState::ON_COMPLETE; - timing.response_end_time = get_time(); -} - -namespace { -int htp_msg_begincb(http_parser *htp) { - if (config.verbose) { - print_timer(); - std::cout << " HTTP Upgrade response" << std::endl; - } - return 0; -} -} // namespace - -namespace { -int htp_statuscb(http_parser *htp, const char *at, size_t length) { - auto client = static_cast(htp->data); - client->upgrade_response_status_code = htp->status_code; - return 0; -} -} // namespace - -namespace { -int htp_msg_completecb(http_parser *htp) { - auto client = static_cast(htp->data); - client->upgrade_response_complete = true; - return 0; -} -} // namespace - -namespace { -http_parser_settings htp_hooks = { - htp_msg_begincb, // http_cb on_message_begin; - nullptr, // http_data_cb on_url; - htp_statuscb, // http_data_cb on_status; - nullptr, // http_data_cb on_header_field; - nullptr, // http_data_cb on_header_value; - nullptr, // http_cb on_headers_complete; - nullptr, // http_data_cb on_body; - htp_msg_completecb // http_cb on_message_complete; -}; -} // namespace - -namespace { -int submit_request(HttpClient *client, const Headers &headers, Request *req) { - auto path = req->make_reqpath(); - auto scheme = util::get_uri_field(req->uri.c_str(), req->u, UF_SCHEMA); - auto build_headers = Headers{{":method", req->data_prd ? "POST" : "GET"}, - {":path", path}, - {":scheme", scheme}, - {":authority", client->hostport}, - {"accept", "*/*"}, - {"accept-encoding", "gzip, deflate"}, - {"user-agent", "nghttp2/" NGHTTP2_VERSION}}; - if (config.continuation) { - for (size_t i = 0; i < 6; ++i) { - build_headers.emplace_back("continuation-test-" + util::utos(i + 1), - std::string(4096, '-')); - } - } - auto num_initial_headers = build_headers.size(); - if (!config.no_content_length && req->data_prd) { - build_headers.emplace_back("content-length", util::utos(req->data_length)); - } - for (auto &kv : headers) { - size_t i; - for (i = 0; i < num_initial_headers; ++i) { - if (kv.name == build_headers[i].name) { - build_headers[i].value = kv.value; - break; - } - } - if (i < num_initial_headers) { - continue; - } - - build_headers.emplace_back(kv.name, kv.value, kv.no_index); - } - - auto nva = std::vector(); - nva.reserve(build_headers.size()); - - for (auto &kv : build_headers) { - nva.push_back(http2::make_nv(kv.name, kv.value, kv.no_index)); - } - - std::string trailer_names; - if (!config.trailer.empty()) { - trailer_names = config.trailer[0].name; - for (size_t i = 1; i < config.trailer.size(); ++i) { - trailer_names += ", "; - trailer_names += config.trailer[i].name; - } - nva.push_back(http2::make_nv_ls("trailer", trailer_names)); - } - - auto stream_id = - nghttp2_submit_request(client->session, &req->pri_spec, nva.data(), - nva.size(), req->data_prd, req); - if (stream_id < 0) { - std::cerr << "[ERROR] nghttp2_submit_request() returned error: " - << nghttp2_strerror(stream_id) << std::endl; - return -1; - } - - req->stream_id = stream_id; - client->request_done(req); - - req->req_nva = std::move(build_headers); - - return 0; -} -} // namespace - -namespace { -void readcb(struct ev_loop *loop, ev_io *w, int revents) { - auto client = static_cast(w->data); - if (client->do_read() != 0) { - client->disconnect(); - } -} -} // namespace - -namespace { -void writecb(struct ev_loop *loop, ev_io *w, int revents) { - auto client = static_cast(w->data); - auto rv = client->do_write(); - if (rv == HttpClient::ERR_CONNECT_FAIL) { - client->connect_fail(); - return; - } - if (rv != 0) { - client->disconnect(); - } -} -} // namespace - -namespace { -void timeoutcb(struct ev_loop *loop, ev_timer *w, int revents) { - auto client = static_cast(w->data); - std::cerr << "[ERROR] Timeout" << std::endl; - client->disconnect(); -} -} // namespace - -namespace { -void settings_timeout_cb(struct ev_loop *loop, ev_timer *w, int revents) { - auto client = static_cast(w->data); - ev_timer_stop(loop, w); - - nghttp2_session_terminate_session(client->session, NGHTTP2_SETTINGS_TIMEOUT); - - client->signal_write(); -} -} // namespace - -HttpClient::HttpClient(const nghttp2_session_callbacks *callbacks, - struct ev_loop *loop, SSL_CTX *ssl_ctx) - : session(nullptr), callbacks(callbacks), loop(loop), ssl_ctx(ssl_ctx), - ssl(nullptr), addrs(nullptr), next_addr(nullptr), cur_addr(nullptr), - complete(0), success(0), settings_payloadlen(0), state(ClientState::IDLE), - upgrade_response_status_code(0), fd(-1), - upgrade_response_complete(false) { - ev_io_init(&wev, writecb, 0, EV_WRITE); - ev_io_init(&rev, readcb, 0, EV_READ); - - wev.data = this; - rev.data = this; - - ev_timer_init(&wt, timeoutcb, 0., config.timeout); - ev_timer_init(&rt, timeoutcb, 0., config.timeout); - - wt.data = this; - rt.data = this; - - ev_timer_init(&settings_timer, settings_timeout_cb, 0., 10.); - - settings_timer.data = this; -} - -HttpClient::~HttpClient() { - disconnect(); - - if (addrs) { - freeaddrinfo(addrs); - addrs = nullptr; - next_addr = nullptr; - } -} - -bool HttpClient::need_upgrade() const { - return config.upgrade && scheme == "http"; -} - -int HttpClient::resolve_host(const std::string &host, uint16_t port) { - int rv; - addrinfo hints; - this->host = host; - memset(&hints, 0, sizeof(hints)); - hints.ai_family = AF_UNSPEC; - hints.ai_socktype = SOCK_STREAM; - hints.ai_protocol = 0; - hints.ai_flags = AI_ADDRCONFIG; - rv = getaddrinfo(host.c_str(), util::utos(port).c_str(), &hints, &addrs); - if (rv != 0) { - std::cerr << "[ERROR] getaddrinfo() failed: " << gai_strerror(rv) - << std::endl; - return -1; - } - if (addrs == nullptr) { - std::cerr << "[ERROR] No address returned" << std::endl; - return -1; - } - next_addr = addrs; - return 0; -} - -int HttpClient::initiate_connection() { - int rv; - - cur_addr = nullptr; - while (next_addr) { - cur_addr = next_addr; - next_addr = next_addr->ai_next; - fd = util::create_nonblock_socket(cur_addr->ai_family); - if (fd == -1) { - continue; - } - - if (ssl_ctx) { - // We are establishing TLS connection. - ssl = SSL_new(ssl_ctx); - if (!ssl) { - std::cerr << "[ERROR] SSL_new() failed: " - << ERR_error_string(ERR_get_error(), nullptr) << std::endl; - return -1; - } - - SSL_set_fd(ssl, fd); - SSL_set_connect_state(ssl); - - // If the user overrode the :authority or host header, use that - // value for the SNI extension - const char *host_string = nullptr; - auto i = std::find_if(std::begin(config.headers), - std::end(config.headers), [](const Header &nv) { - return ":authority" == nv.name || "host" == nv.name; - }); - if (i != std::end(config.headers)) { - host_string = (*i).value.c_str(); - } else { - host_string = host.c_str(); - } - - if (!util::numeric_host(host_string)) { - SSL_set_tlsext_host_name(ssl, host_string); - } - } - - rv = connect(fd, cur_addr->ai_addr, cur_addr->ai_addrlen); - - if (rv != 0 && errno != EINPROGRESS) { - if (ssl) { - SSL_free(ssl); - ssl = nullptr; - } - close(fd); - fd = -1; - continue; - } - break; - } - - if (fd == -1) { - return -1; - } - - writefn = &HttpClient::connected; - - if (need_upgrade()) { - on_readfn = &HttpClient::on_upgrade_read; - on_writefn = &HttpClient::on_upgrade_connect; - } else { - on_readfn = &HttpClient::on_read; - on_writefn = &HttpClient::on_write; - } - - ev_io_set(&rev, fd, EV_READ); - ev_io_set(&wev, fd, EV_WRITE); - - ev_io_start(loop, &wev); - - ev_timer_again(loop, &wt); - - return 0; -} - -void HttpClient::disconnect() { - state = ClientState::IDLE; - - ev_timer_stop(loop, &settings_timer); - - ev_timer_stop(loop, &rt); - ev_timer_stop(loop, &wt); - - ev_io_stop(loop, &rev); - ev_io_stop(loop, &wev); - - nghttp2_session_del(session); - session = nullptr; - - if (ssl) { - SSL_set_shutdown(ssl, SSL_RECEIVED_SHUTDOWN); - ERR_clear_error(); - SSL_shutdown(ssl); - SSL_free(ssl); - ssl = nullptr; - } - - if (fd != -1) { - shutdown(fd, SHUT_WR); - close(fd); - fd = -1; - } -} - -int HttpClient::read_clear() { - ev_timer_again(loop, &rt); - - std::array buf; - - for (;;) { - ssize_t nread; - while ((nread = read(fd, buf.data(), buf.size())) == -1 && errno == EINTR) - ; - if (nread == -1) { - if (errno == EAGAIN || errno == EWOULDBLOCK) { - return 0; - } - return -1; - } - - if (nread == 0) { - return -1; - } - - if (on_readfn(*this, buf.data(), nread) != 0) { - return -1; - } - } - - return 0; -} - -int HttpClient::write_clear() { - ev_timer_again(loop, &rt); - - for (;;) { - if (wb.rleft() > 0) { - ssize_t nwrite; - while ((nwrite = write(fd, wb.pos, wb.rleft())) == -1 && errno == EINTR) - ; - if (nwrite == -1) { - if (errno == EAGAIN || errno == EWOULDBLOCK) { - ev_io_start(loop, &wev); - ev_timer_again(loop, &wt); - return 0; - } - return -1; - } - wb.drain(nwrite); - continue; - } - wb.reset(); - if (on_writefn(*this) != 0) { - return -1; - } - if (wb.rleft() == 0) { - break; - } - } - - ev_io_stop(loop, &wev); - ev_timer_stop(loop, &wt); - - return 0; -} - -int HttpClient::noop() { return 0; } - -void HttpClient::connect_fail() { - if (state == ClientState::IDLE) { - std::cerr << "[ERROR] Could not connect to the address " - << util::numeric_name(cur_addr->ai_addr, cur_addr->ai_addrlen) - << std::endl; - } - auto cur_state = state; - disconnect(); - if (cur_state == ClientState::IDLE) { - if (initiate_connection() == 0) { - std::cerr << "Trying next address " - << util::numeric_name(cur_addr->ai_addr, cur_addr->ai_addrlen) - << std::endl; - } - } -} - -int HttpClient::connected() { - if (!util::check_socket_connected(fd)) { - return ERR_CONNECT_FAIL; - } - - if (config.verbose) { - print_timer(); - std::cout << " Connected" << std::endl; - } - - state = ClientState::CONNECTED; - - ev_io_start(loop, &rev); - ev_io_stop(loop, &wev); - - ev_timer_again(loop, &rt); - ev_timer_stop(loop, &wt); - - if (ssl) { - readfn = &HttpClient::tls_handshake; - writefn = &HttpClient::tls_handshake; - - return do_write(); - } - - readfn = &HttpClient::read_clear; - writefn = &HttpClient::write_clear; - - if (need_upgrade()) { - htp = make_unique(); - http_parser_init(htp.get(), HTTP_RESPONSE); - htp->data = this; - - return do_write(); - } - - if (connection_made() != 0) { - return -1; - } - - return 0; -} - -namespace { -size_t populate_settings(nghttp2_settings_entry *iv) { - size_t niv = 2; - - iv[0].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS; - iv[0].value = 100; - - iv[1].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE; - if (config.window_bits != -1) { - iv[1].value = (1 << config.window_bits) - 1; - } else { - iv[1].value = NGHTTP2_INITIAL_WINDOW_SIZE; - } - - if (config.header_table_size >= 0) { - iv[niv].settings_id = NGHTTP2_SETTINGS_HEADER_TABLE_SIZE; - iv[niv].value = config.header_table_size; - ++niv; - } - - if (config.no_push) { - iv[niv].settings_id = NGHTTP2_SETTINGS_ENABLE_PUSH; - iv[niv].value = 0; - ++niv; - } - - return niv; -} -} // namespace - -int HttpClient::on_upgrade_connect() { - ssize_t rv; - record_connect_end_time(); - assert(!reqvec.empty()); - std::array iv; - size_t niv = populate_settings(iv.data()); - assert(settings_payload.size() >= 8 * niv); - rv = nghttp2_pack_settings_payload(settings_payload.data(), - settings_payload.size(), iv.data(), niv); - if (rv < 0) { - return -1; - } - settings_payloadlen = rv; - auto token68 = - base64::encode(std::begin(settings_payload), - std::begin(settings_payload) + settings_payloadlen); - util::to_token68(token68); - std::string req; - if (reqvec[0]->data_prd) { - // If the request contains upload data, use OPTIONS * to upgrade - req = "OPTIONS *"; - } else { - req = "GET "; - req += reqvec[0]->make_reqpath(); - } - - auto headers = Headers{{"Host", hostport}, - {"Connection", "Upgrade, HTTP2-Settings"}, - {"Upgrade", NGHTTP2_CLEARTEXT_PROTO_VERSION_ID}, - {"HTTP2-Settings", token68}, - {"Accept", "*/*"}, - {"User-Agent", "nghttp2/" NGHTTP2_VERSION}}; - auto initial_headerslen = headers.size(); - - for (auto &kv : config.headers) { - size_t i; - for (i = 0; i < initial_headerslen; ++i) { - if (util::strieq(kv.name, headers[i].name)) { - headers[i].value = kv.value; - break; - } - } - if (i < initial_headerslen) { - continue; - } - if (kv.name.size() != 0 && kv.name[0] != ':') { - headers.emplace_back(kv.name, kv.value, kv.no_index); - } - } - - req += " HTTP/1.1\r\n"; - - for (auto &kv : headers) { - req += kv.name; - req += ": "; - req += kv.value; - req += "\r\n"; - } - req += "\r\n"; - - wb.write(req.c_str(), req.size()); - - if (config.verbose) { - print_timer(); - std::cout << " HTTP Upgrade request\n" << req << std::endl; - } - - // record request time if this is GET request - if (!reqvec[0]->data_prd) { - reqvec[0]->record_request_start_time(); - } - - on_writefn = &HttpClient::noop; - - signal_write(); - - return 0; -} - -int HttpClient::on_upgrade_read(const uint8_t *data, size_t len) { - int rv; - - auto nread = http_parser_execute(htp.get(), &htp_hooks, - reinterpret_cast(data), len); - - if (config.verbose) { - std::cout.write(reinterpret_cast(data), nread); - } - - auto htperr = HTTP_PARSER_ERRNO(htp.get()); - - if (htperr != HPE_OK) { - std::cerr << "[ERROR] Failed to parse HTTP Upgrade response header: " - << "(" << http_errno_name(htperr) << ") " - << http_errno_description(htperr) << std::endl; - return -1; - } - - if (!upgrade_response_complete) { - return 0; - } - - if (config.verbose) { - std::cout << std::endl; - } - - if (upgrade_response_status_code != 101) { - std::cerr << "[ERROR] HTTP Upgrade failed" << std::endl; - - return -1; - } - - if (config.verbose) { - print_timer(); - std::cout << " HTTP Upgrade success" << std::endl; - } - - on_readfn = &HttpClient::on_read; - on_writefn = &HttpClient::on_write; - - rv = connection_made(); - if (rv != 0) { - return rv; - } - - // Read remaining data in the buffer because it is not notified - // callback anymore. - rv = on_readfn(*this, data + nread, len - nread); - if (rv != 0) { - return rv; - } - - return 0; -} - -int HttpClient::do_read() { return readfn(*this); } -int HttpClient::do_write() { return writefn(*this); } - -int HttpClient::connection_made() { - int rv; - - if (!need_upgrade()) { - record_connect_end_time(); - } - - if (ssl) { - // Check NPN or ALPN result - const unsigned char *next_proto = nullptr; - unsigned int next_proto_len; - SSL_get0_next_proto_negotiated(ssl, &next_proto, &next_proto_len); - for (int i = 0; i < 2; ++i) { - if (next_proto) { - if (config.verbose) { - std::cout << "The negotiated protocol: "; - std::cout.write(reinterpret_cast(next_proto), - next_proto_len); - std::cout << std::endl; - } - if (!util::check_h2_is_selected(next_proto, next_proto_len)) { - next_proto = nullptr; - } - break; - } -#if OPENSSL_VERSION_NUMBER >= 0x10002000L - SSL_get0_alpn_selected(ssl, &next_proto, &next_proto_len); -#else // OPENSSL_VERSION_NUMBER < 0x10002000L - break; -#endif // OPENSSL_VERSION_NUMBER < 0x10002000L - } - if (!next_proto) { - print_protocol_nego_error(); - return -1; - } - } - - rv = nghttp2_session_client_new2(&session, callbacks, this, - config.http2_option); - - if (rv != 0) { - return -1; - } - if (need_upgrade()) { - // Adjust stream user-data depending on the existence of upload - // data - Request *stream_user_data = nullptr; - if (!reqvec[0]->data_prd) { - stream_user_data = reqvec[0].get(); - } - rv = nghttp2_session_upgrade(session, settings_payload.data(), - settings_payloadlen, stream_user_data); - if (rv != 0) { - std::cerr << "[ERROR] nghttp2_session_upgrade() returned error: " - << nghttp2_strerror(rv) << std::endl; - return -1; - } - if (stream_user_data) { - stream_user_data->stream_id = 1; - request_done(stream_user_data); - } - } - // If upgrade succeeds, the SETTINGS value sent with - // HTTP2-Settings header field has already been submitted to - // session object. - if (!need_upgrade()) { - std::array iv; - auto niv = populate_settings(iv.data()); - rv = nghttp2_submit_settings(session, NGHTTP2_FLAG_NONE, iv.data(), niv); - if (rv != 0) { - return -1; - } - } - if (!config.no_dep) { - // Create anchor stream nodes - nghttp2_priority_spec pri_spec; - - for (auto &anchor : anchors) { - nghttp2_priority_spec_init(&pri_spec, anchor.dep_stream_id, anchor.weight, - 0); - rv = nghttp2_submit_priority(session, NGHTTP2_FLAG_NONE, anchor.stream_id, - &pri_spec); - if (rv != 0) { - return -1; - } - } - - rv = nghttp2_session_set_next_stream_id( - session, anchors[ANCHOR_FOLLOWERS].stream_id + 2); - if (rv != 0) { - return -1; - } - - if (need_upgrade()) { - // Amend the priority because we cannot send priority in - // HTTP/1.1 Upgrade. - auto &anchor = anchors[ANCHOR_FOLLOWERS]; - nghttp2_priority_spec_init(&pri_spec, anchor.stream_id, config.weight, 0); - - rv = nghttp2_submit_priority(session, NGHTTP2_FLAG_NONE, 1, &pri_spec); - if (rv != 0) { - return -1; - } - } - } else if (need_upgrade() && config.weight != NGHTTP2_DEFAULT_WEIGHT) { - // Amend the priority because we cannot send priority in - // HTTP/1.1 Upgrade. - nghttp2_priority_spec pri_spec; - - nghttp2_priority_spec_init(&pri_spec, 0, config.weight, 0); - - rv = nghttp2_submit_priority(session, NGHTTP2_FLAG_NONE, 1, &pri_spec); - if (rv != 0) { - return -1; - } - } - - ev_timer_again(loop, &settings_timer); - - if (config.connection_window_bits != -1) { - int32_t wininc = (1 << config.connection_window_bits) - 1 - - NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE; - rv = nghttp2_submit_window_update(session, NGHTTP2_FLAG_NONE, 0, wininc); - if (rv != 0) { - return -1; - } - } - // Adjust first request depending on the existence of the upload - // data - for (auto i = std::begin(reqvec) + (need_upgrade() && !reqvec[0]->data_prd); - i != std::end(reqvec); ++i) { - if (submit_request(this, config.headers, (*i).get()) != 0) { - return -1; - } - } - - signal_write(); - - return 0; -} - -int HttpClient::on_read(const uint8_t *data, size_t len) { - if (config.hexdump) { - util::hexdump(stdout, data, len); - } - - auto rv = nghttp2_session_mem_recv(session, data, len); - if (rv < 0) { - std::cerr << "[ERROR] nghttp2_session_mem_recv() returned error: " - << nghttp2_strerror(rv) << std::endl; - return -1; - } - - assert(static_cast(rv) == len); - - if (nghttp2_session_want_read(session) == 0 && - nghttp2_session_want_write(session) == 0 && wb.rleft() == 0) { - return -1; - } - - signal_write(); - - return 0; -} - -int HttpClient::on_write() { - auto rv = nghttp2_session_send(session); - if (rv != 0) { - std::cerr << "[ERROR] nghttp2_session_send() returned error: " - << nghttp2_strerror(rv) << std::endl; - return -1; - } - - if (nghttp2_session_want_read(session) == 0 && - nghttp2_session_want_write(session) == 0 && wb.rleft() == 0) { - return -1; - } - - return 0; -} - -int HttpClient::tls_handshake() { - ev_timer_again(loop, &rt); - - ERR_clear_error(); - - auto rv = SSL_do_handshake(ssl); - - if (rv == 0) { - return -1; - } - - if (rv < 0) { - auto err = SSL_get_error(ssl, rv); - switch (err) { - case SSL_ERROR_WANT_READ: - ev_io_stop(loop, &wev); - ev_timer_stop(loop, &wt); - return 0; - case SSL_ERROR_WANT_WRITE: - ev_io_start(loop, &wev); - ev_timer_again(loop, &wt); - return 0; - default: - return -1; - } - } - - ev_io_stop(loop, &wev); - ev_timer_stop(loop, &wt); - - readfn = &HttpClient::read_tls; - writefn = &HttpClient::write_tls; - - if (connection_made() != 0) { - return -1; - } - - return 0; -} - -int HttpClient::read_tls() { - ev_timer_again(loop, &rt); - - ERR_clear_error(); - - std::array buf; - for (;;) { - auto rv = SSL_read(ssl, buf.data(), buf.size()); - - if (rv == 0) { - return -1; - } - - if (rv < 0) { - auto err = SSL_get_error(ssl, rv); - switch (err) { - case SSL_ERROR_WANT_READ: - return 0; - case SSL_ERROR_WANT_WRITE: - // renegotiation started - return -1; - default: - return -1; - } - } - - if (on_readfn(*this, buf.data(), rv) != 0) { - return -1; - } - } -} - -int HttpClient::write_tls() { - ev_timer_again(loop, &rt); - - ERR_clear_error(); - - for (;;) { - if (wb.rleft() > 0) { - auto rv = SSL_write(ssl, wb.pos, wb.rleft()); - - if (rv == 0) { - return -1; - } - - if (rv < 0) { - auto err = SSL_get_error(ssl, rv); - switch (err) { - case SSL_ERROR_WANT_READ: - // renegotiation started - return -1; - case SSL_ERROR_WANT_WRITE: - ev_io_start(loop, &wev); - ev_timer_again(loop, &wt); - return 0; - default: - return -1; - } - } - - wb.drain(rv); - - continue; - } - wb.reset(); - if (on_writefn(*this) != 0) { - return -1; - } - if (wb.rleft() == 0) { - break; - } - } - - ev_io_stop(loop, &wev); - ev_timer_stop(loop, &wt); - - return 0; -} - -void HttpClient::signal_write() { ev_io_start(loop, &wev); } - -bool HttpClient::all_requests_processed() const { - return complete == reqvec.size(); -} - -void HttpClient::update_hostport() { - if (reqvec.empty()) { - return; - } - scheme = util::get_uri_field(reqvec[0]->uri.c_str(), reqvec[0]->u, UF_SCHEMA); - std::stringstream ss; - if (reqvec[0]->is_ipv6_literal_addr()) { - ss << "["; - util::write_uri_field(ss, reqvec[0]->uri.c_str(), reqvec[0]->u, UF_HOST); - ss << "]"; - } else { - util::write_uri_field(ss, reqvec[0]->uri.c_str(), reqvec[0]->u, UF_HOST); - } - if (util::has_uri_field(reqvec[0]->u, UF_PORT) && - reqvec[0]->u.port != - util::get_default_port(reqvec[0]->uri.c_str(), reqvec[0]->u)) { - ss << ":" << reqvec[0]->u.port; - } - hostport = ss.str(); -} - -bool HttpClient::add_request(const std::string &uri, - const nghttp2_data_provider *data_prd, - int64_t data_length, - const nghttp2_priority_spec &pri_spec, int level) { - http_parser_url u; - memset(&u, 0, sizeof(u)); - if (http_parser_parse_url(uri.c_str(), uri.size(), 0, &u) != 0) { - return false; - } - if (path_cache.count(uri)) { - return false; - } - - if (config.multiply == 1) { - path_cache.insert(uri); - } - - reqvec.push_back( - make_unique(uri, u, data_prd, data_length, pri_spec, level)); - return true; -} - -void HttpClient::record_start_time() { - timing.system_start_time = std::chrono::system_clock::now(); - timing.start_time = get_time(); -} - -void HttpClient::record_domain_lookup_end_time() { - timing.domain_lookup_end_time = get_time(); -} - -void HttpClient::record_connect_end_time() { - timing.connect_end_time = get_time(); -} - -void HttpClient::request_done(Request *req) { - if (req->stream_id % 2 == 0) { - return; - } -} - -#ifdef HAVE_JANSSON -void HttpClient::output_har(FILE *outfile) { - static auto PAGE_ID = "page_0"; - - auto root = json_object(); - auto log = json_object(); - json_object_set_new(root, "log", log); - json_object_set_new(log, "version", json_string("1.2")); - - auto creator = json_object(); - json_object_set_new(log, "creator", creator); - - json_object_set_new(creator, "name", json_string("nghttp")); - json_object_set_new(creator, "version", json_string(NGHTTP2_VERSION)); - - auto pages = json_array(); - json_object_set_new(log, "pages", pages); - - auto page = json_object(); - json_array_append_new(pages, page); - - json_object_set_new( - page, "startedDateTime", - json_string(util::format_iso8601(timing.system_start_time).c_str())); - json_object_set_new(page, "id", json_string(PAGE_ID)); - json_object_set_new(page, "title", json_string("")); - - json_object_set_new(page, "pageTimings", json_object()); - - auto entries = json_array(); - json_object_set_new(log, "entries", entries); - - auto dns_delta = - std::chrono::duration_cast( - timing.domain_lookup_end_time - timing.start_time).count() / - 1000.0; - auto connect_delta = - std::chrono::duration_cast( - timing.connect_end_time - timing.domain_lookup_end_time).count() / - 1000.0; - - for (size_t i = 0; i < reqvec.size(); ++i) { - auto &req = reqvec[i]; - - if (req->timing.state != RequestState::ON_COMPLETE) { - continue; - } - - auto entry = json_object(); - json_array_append_new(entries, entry); - - auto &req_timing = req->timing; - auto request_time = - (i == 0) ? timing.system_start_time - : timing.system_start_time + - std::chrono::duration_cast< - std::chrono::system_clock::duration>( - req_timing.request_start_time - timing.start_time); - - auto wait_delta = std::chrono::duration_cast( - req_timing.response_start_time - - req_timing.request_start_time).count() / - 1000.0; - auto receive_delta = std::chrono::duration_cast( - req_timing.response_end_time - - req_timing.response_start_time).count() / - 1000.0; - - auto time_sum = - std::chrono::duration_cast( - (i == 0) ? (req_timing.response_end_time - timing.start_time) - : (req_timing.response_end_time - - req_timing.request_start_time)).count() / - 1000.0; - - json_object_set_new( - entry, "startedDateTime", - json_string(util::format_iso8601(request_time).c_str())); - json_object_set_new(entry, "time", json_real(time_sum)); - - auto request = json_object(); - json_object_set_new(entry, "request", request); - - auto method_ptr = http2::get_header(req->req_nva, ":method"); - - const char *method = "GET"; - if (method_ptr) { - method = (*method_ptr).value.c_str(); - } - - auto req_headers = json_array(); - json_object_set_new(request, "headers", req_headers); - - for (auto &nv : req->req_nva) { - auto hd = json_object(); - json_array_append_new(req_headers, hd); - - json_object_set_new(hd, "name", json_string(nv.name.c_str())); - json_object_set_new(hd, "value", json_string(nv.value.c_str())); - } - - json_object_set_new(request, "method", json_string(method)); - json_object_set_new(request, "url", json_string(req->uri.c_str())); - json_object_set_new(request, "httpVersion", json_string("HTTP/2.0")); - json_object_set_new(request, "cookies", json_array()); - json_object_set_new(request, "queryString", json_array()); - json_object_set_new(request, "headersSize", json_integer(-1)); - json_object_set_new(request, "bodySize", json_integer(-1)); - - auto response = json_object(); - json_object_set_new(entry, "response", response); - - auto res_headers = json_array(); - json_object_set_new(response, "headers", res_headers); - - for (auto &nv : req->res_nva) { - auto hd = json_object(); - json_array_append_new(res_headers, hd); - - json_object_set_new(hd, "name", json_string(nv.name.c_str())); - json_object_set_new(hd, "value", json_string(nv.value.c_str())); - } - - json_object_set_new(response, "status", json_integer(req->status)); - json_object_set_new(response, "statusText", json_string("")); - json_object_set_new(response, "httpVersion", json_string("HTTP/2.0")); - json_object_set_new(response, "cookies", json_array()); - - auto content = json_object(); - json_object_set_new(response, "content", content); - - json_object_set_new(content, "size", json_integer(req->response_len)); - - auto content_type_ptr = http2::get_header(req->res_nva, "content-type"); - - const char *content_type = ""; - if (content_type_ptr) { - content_type = content_type_ptr->value.c_str(); - } - - json_object_set_new(content, "mimeType", json_string(content_type)); - - json_object_set_new(response, "redirectURL", json_string("")); - json_object_set_new(response, "headersSize", json_integer(-1)); - json_object_set_new(response, "bodySize", json_integer(-1)); - - json_object_set_new(entry, "cache", json_object()); - - auto timings = json_object(); - json_object_set_new(entry, "timings", timings); - - auto dns_timing = (i == 0) ? dns_delta : 0; - auto connect_timing = (i == 0) ? connect_delta : 0; - - json_object_set_new(timings, "dns", json_real(dns_timing)); - json_object_set_new(timings, "connect", json_real(connect_timing)); - - json_object_set_new(timings, "blocked", json_real(0.0)); - json_object_set_new(timings, "send", json_real(0.0)); - json_object_set_new(timings, "wait", json_real(wait_delta)); - json_object_set_new(timings, "receive", json_real(receive_delta)); - - json_object_set_new(entry, "pageref", json_string(PAGE_ID)); - } - - json_dumpf(root, outfile, JSON_PRESERVE_ORDER | JSON_INDENT(2)); - json_decref(root); -} -#endif // HAVE_JANSSON - -namespace { -void update_html_parser(HttpClient *client, Request *req, const uint8_t *data, - size_t len, int fin) { - if (!req->html_parser) { - return; - } - req->update_html_parser(data, len, fin); - - for (auto &p : req->html_parser->get_links()) { - auto uri = strip_fragment(p.first.c_str()); - auto res_type = p.second; - - http_parser_url u; - memset(&u, 0, sizeof(u)); - if (http_parser_parse_url(uri.c_str(), uri.size(), 0, &u) == 0 && - util::fieldeq(uri.c_str(), u, req->uri.c_str(), req->u, UF_SCHEMA) && - util::fieldeq(uri.c_str(), u, req->uri.c_str(), req->u, UF_HOST) && - util::porteq(uri.c_str(), u, req->uri.c_str(), req->u)) { - // No POST data for assets - auto pri_spec = resolve_dep(res_type); - - if (client->add_request(uri, nullptr, 0, pri_spec, req->level + 1)) { - - submit_request(client, config.headers, client->reqvec.back().get()); - } - } - } - req->html_parser->clear_links(); -} -} // namespace - -namespace { -HttpClient *get_client(void *user_data) { - return static_cast(user_data); -} -} // namespace - -namespace { -int on_data_chunk_recv_callback(nghttp2_session *session, uint8_t flags, - int32_t stream_id, const uint8_t *data, - size_t len, void *user_data) { - auto client = get_client(user_data); - auto req = static_cast( - nghttp2_session_get_stream_user_data(session, stream_id)); - - if (!req) { - return 0; - } - - if (config.verbose >= 2) { - verbose_on_data_chunk_recv_callback(session, flags, stream_id, data, len, - user_data); - } - - req->response_len += len; - - if (req->inflater) { - while (len > 0) { - const size_t MAX_OUTLEN = 4096; - std::array out; - size_t outlen = MAX_OUTLEN; - size_t tlen = len; - int rv = - nghttp2_gzip_inflate(req->inflater, out.data(), &outlen, data, &tlen); - if (rv != 0) { - nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, stream_id, - NGHTTP2_INTERNAL_ERROR); - break; - } - - if (!config.null_out) { - std::cout.write(reinterpret_cast(out.data()), outlen); - } - - update_html_parser(client, req, out.data(), outlen, 0); - data += tlen; - len -= tlen; - } - - return 0; - } - - if (!config.null_out) { - std::cout.write(reinterpret_cast(data), len); - } - - update_html_parser(client, req, data, len, 0); - - return 0; -} -} // namespace - -namespace { -ssize_t select_padding_callback(nghttp2_session *session, - const nghttp2_frame *frame, size_t max_payload, - void *user_data) { - return std::min(max_payload, frame->hd.length + config.padding); -} -} // namespace - -namespace { -void check_response_header(nghttp2_session *session, Request *req) { - bool gzip = false; - - req->expect_final_response = false; - - auto status_hd = req->get_res_header(http2::HD__STATUS); - - if (!status_hd) { - nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, req->stream_id, - NGHTTP2_PROTOCOL_ERROR); - return; - } - - auto status = http2::parse_http_status_code(status_hd->value); - if (status == -1) { - nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, req->stream_id, - NGHTTP2_PROTOCOL_ERROR); - return; - } - - req->status = status; - - for (auto &nv : req->res_nva) { - if ("content-encoding" == nv.name) { - gzip = util::strieq_l("gzip", nv.value) || - util::strieq_l("deflate", nv.value); - continue; - } - } - - if (req->status / 100 == 1) { - req->expect_final_response = true; - req->status = 0; - req->res_nva.clear(); - http2::init_hdidx(req->res_hdidx); - return; - } - - if (gzip) { - if (!req->inflater) { - req->init_inflater(); - } - } - if (config.get_assets && req->level == 0) { - if (!req->html_parser) { - req->init_html_parser(); - } - } -} -} // namespace - -namespace { -int on_begin_headers_callback(nghttp2_session *session, - const nghttp2_frame *frame, void *user_data) { - auto client = get_client(user_data); - switch (frame->hd.type) { - case NGHTTP2_HEADERS: { - auto req = static_cast( - nghttp2_session_get_stream_user_data(session, frame->hd.stream_id)); - if (!req) { - break; - } - - switch (frame->headers.cat) { - case NGHTTP2_HCAT_RESPONSE: - case NGHTTP2_HCAT_PUSH_RESPONSE: - req->record_response_start_time(); - break; - default: - break; - } - - break; - } - case NGHTTP2_PUSH_PROMISE: { - auto stream_id = frame->push_promise.promised_stream_id; - http_parser_url u; - memset(&u, 0, sizeof(u)); - // TODO Set pri and level - nghttp2_priority_spec pri_spec; - - nghttp2_priority_spec_default_init(&pri_spec); - - auto req = make_unique("", u, nullptr, 0, pri_spec); - req->stream_id = stream_id; - - nghttp2_session_set_stream_user_data(session, stream_id, req.get()); - - client->request_done(req.get()); - req->record_request_start_time(); - client->reqvec.push_back(std::move(req)); - - break; - } - } - return 0; -} -} // namespace - -namespace { -int on_header_callback(nghttp2_session *session, const nghttp2_frame *frame, - const uint8_t *name, size_t namelen, - const uint8_t *value, size_t valuelen, uint8_t flags, - void *user_data) { - if (config.verbose) { - verbose_on_header_callback(session, frame, name, namelen, value, valuelen, - flags, user_data); - } - - switch (frame->hd.type) { - case NGHTTP2_HEADERS: { - auto req = static_cast( - nghttp2_session_get_stream_user_data(session, frame->hd.stream_id)); - - if (!req) { - break; - } - - /* ignore trailer header */ - if (frame->headers.cat == NGHTTP2_HCAT_HEADERS && - !req->expect_final_response) { - break; - } - - auto token = http2::lookup_token(name, namelen); - - http2::index_header(req->res_hdidx, token, req->res_nva.size()); - http2::add_header(req->res_nva, name, namelen, value, valuelen, - flags & NGHTTP2_NV_FLAG_NO_INDEX, token); - break; - } - case NGHTTP2_PUSH_PROMISE: { - auto req = static_cast(nghttp2_session_get_stream_user_data( - session, frame->push_promise.promised_stream_id)); - - if (!req) { - break; - } - - auto token = http2::lookup_token(name, namelen); - - http2::index_header(req->req_hdidx, token, req->req_nva.size()); - http2::add_header(req->req_nva, name, namelen, value, valuelen, - flags & NGHTTP2_NV_FLAG_NO_INDEX, token); - break; - } - } - return 0; -} -} // namespace - -namespace { -int on_frame_recv_callback2(nghttp2_session *session, - const nghttp2_frame *frame, void *user_data) { - int rv = 0; - - if (config.verbose) { - verbose_on_frame_recv_callback(session, frame, user_data); - } - - auto client = get_client(user_data); - switch (frame->hd.type) { - case NGHTTP2_DATA: { - auto req = static_cast( - nghttp2_session_get_stream_user_data(session, frame->hd.stream_id)); - if (!req) { - return 0; - ; - } - - if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) { - req->record_response_end_time(); - ++client->success; - } - - break; - } - case NGHTTP2_HEADERS: { - auto req = static_cast( - nghttp2_session_get_stream_user_data(session, frame->hd.stream_id)); - // If this is the HTTP Upgrade with OPTIONS method to avoid POST, - // req is nullptr. - if (!req) { - return 0; - ; - } - - switch (frame->headers.cat) { - case NGHTTP2_HCAT_RESPONSE: - case NGHTTP2_HCAT_PUSH_RESPONSE: - check_response_header(session, req); - break; - case NGHTTP2_HCAT_HEADERS: - if (req->expect_final_response) { - check_response_header(session, req); - break; - } - if ((frame->hd.flags & NGHTTP2_FLAG_END_STREAM) == 0) { - nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, - frame->hd.stream_id, NGHTTP2_PROTOCOL_ERROR); - return 0; - } - break; - default: - assert(0); - } - - if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) { - req->record_response_end_time(); - ++client->success; - } - - break; - } - case NGHTTP2_SETTINGS: - if ((frame->hd.flags & NGHTTP2_FLAG_ACK) == 0) { - break; - } - ev_timer_stop(client->loop, &client->settings_timer); - break; - case NGHTTP2_PUSH_PROMISE: { - auto req = static_cast(nghttp2_session_get_stream_user_data( - session, frame->push_promise.promised_stream_id)); - if (!req) { - break; - } - auto scheme = req->get_req_header(http2::HD__SCHEME); - auto authority = req->get_req_header(http2::HD__AUTHORITY); - auto path = req->get_req_header(http2::HD__PATH); - - if (!authority) { - authority = req->get_req_header(http2::HD_HOST); - } - - // libnghttp2 guarantees :scheme, :method, :path and (:authority | - // host) exist and non-empty. - if (path->value[0] != '/') { - nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, - frame->push_promise.promised_stream_id, - NGHTTP2_PROTOCOL_ERROR); - break; - } - std::string uri = scheme->value; - uri += "://"; - uri += authority->value; - uri += path->value; - http_parser_url u; - memset(&u, 0, sizeof(u)); - if (http_parser_parse_url(uri.c_str(), uri.size(), 0, &u) != 0) { - nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, - frame->push_promise.promised_stream_id, - NGHTTP2_PROTOCOL_ERROR); - break; - } - req->uri = uri; - req->u = u; - - if (client->path_cache.count(uri)) { - nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, - frame->push_promise.promised_stream_id, - NGHTTP2_CANCEL); - break; - } - - if (config.multiply == 1) { - client->path_cache.insert(uri); - } - - break; - } - } - return rv; -} -} // namespace - -namespace { -int before_frame_send_callback(nghttp2_session *session, - const nghttp2_frame *frame, void *user_data) { - if (frame->hd.type != NGHTTP2_HEADERS || - frame->headers.cat != NGHTTP2_HCAT_REQUEST) { - return 0; - } - auto req = static_cast( - nghttp2_session_get_stream_user_data(session, frame->hd.stream_id)); - assert(req); - req->record_request_start_time(); - return 0; -} - -} // namespace - -namespace { -int on_stream_close_callback(nghttp2_session *session, int32_t stream_id, - uint32_t error_code, void *user_data) { - auto client = get_client(user_data); - auto req = static_cast( - nghttp2_session_get_stream_user_data(session, stream_id)); - - if (!req) { - return 0; - } - - update_html_parser(client, req, nullptr, 0, 1); - ++client->complete; - - if (client->all_requests_processed()) { - nghttp2_session_terminate_session(session, NGHTTP2_NO_ERROR); - } - - return 0; -} -} // namespace - -struct RequestResult { - std::chrono::microseconds time; -}; - -namespace { -void print_stats(const HttpClient &client) { - std::cout << "***** Statistics *****" << std::endl; - - std::vector reqs; - reqs.reserve(client.reqvec.size()); - for (const auto &req : client.reqvec) { - if (req->timing.state == RequestState::ON_COMPLETE) { - reqs.push_back(req.get()); - } - } - - std::sort(std::begin(reqs), std::end(reqs), - [](const Request *lhs, const Request *rhs) { - const auto <iming = lhs->timing; - const auto &rtiming = rhs->timing; - return ltiming.response_end_time < rtiming.response_end_time || - (ltiming.response_end_time == rtiming.response_end_time && - ltiming.request_start_time < rtiming.request_start_time); - }); - - std::cout << R"( -Request timing: - responseEnd: the time when last byte of response was received - relative to connectEnd - requestStart: the time just before first byte of request was sent - relative to connectEnd. If '*' is shown, this was - pushed by server. - process: responseEnd - requestStart - code: HTTP status code - size: number of bytes received as response body without - inflation. - URI: request URI - -see http://www.w3.org/TR/resource-timing/#processing-model - -sorted by 'complete' - -id responseEnd requestStart process code size request path)" << std::endl; - - const auto &base = client.timing.connect_end_time; - for (const auto &req : reqs) { - auto response_end = std::chrono::duration_cast( - req->timing.response_end_time - base); - auto request_start = std::chrono::duration_cast( - req->timing.request_start_time - base); - auto total = std::chrono::duration_cast( - req->timing.response_end_time - req->timing.request_start_time); - auto pushed = req->stream_id % 2 == 0; - - std::cout << std::setw(3) << req->stream_id << " " << std::setw(11) - << ("+" + util::format_duration(response_end)) << " " - << (pushed ? "*" : " ") << std::setw(11) - << ("+" + util::format_duration(request_start)) << " " - << std::setw(8) << util::format_duration(total) << " " - << std::setw(4) << req->status << " " << std::setw(4) - << util::utos_with_unit(req->response_len) << " " - << req->make_reqpath() << std::endl; - } -} -} // namespace - -namespace { -int client_select_next_proto_cb(SSL *ssl, unsigned char **out, - unsigned char *outlen, const unsigned char *in, - unsigned int inlen, void *arg) { - if (config.verbose) { - print_timer(); - std::cout << "[NPN] server offers:" << std::endl; - } - for (unsigned int i = 0; i < inlen; i += in [i] + 1) { - if (config.verbose) { - std::cout << " * "; - std::cout.write(reinterpret_cast(&in[i + 1]), in[i]); - std::cout << std::endl; - } - } - if (!util::select_h2(const_cast(out), outlen, in, - inlen)) { - print_protocol_nego_error(); - return SSL_TLSEXT_ERR_NOACK; - } - return SSL_TLSEXT_ERR_OK; -} -} // namespace - -namespace { -// Recommended general purpose "Intermediate compatibility" cipher by -// mozilla. -// -// https://wiki.mozilla.org/Security/Server_Side_TLS -const char *const CIPHER_LIST = - "ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-" - "AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:" - "DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-" - "AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-" - "AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-" - "AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:" - "DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-" - "SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-" - "SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!" - "aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA"; -} // namespace - -namespace { -int communicate( - const std::string &scheme, const std::string &host, uint16_t port, - std::vector> - requests, const nghttp2_session_callbacks *callbacks) { - int result = 0; - auto loop = EV_DEFAULT; - SSL_CTX *ssl_ctx = nullptr; - if (scheme == "https") { - ssl_ctx = SSL_CTX_new(SSLv23_client_method()); - if (!ssl_ctx) { - std::cerr << "[ERROR] Failed to create SSL_CTX: " - << ERR_error_string(ERR_get_error(), nullptr) << std::endl; - result = -1; - goto fin; - } - SSL_CTX_set_options(ssl_ctx, - SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | - SSL_OP_NO_COMPRESSION | - SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION); - SSL_CTX_set_mode(ssl_ctx, SSL_MODE_AUTO_RETRY); - SSL_CTX_set_mode(ssl_ctx, SSL_MODE_RELEASE_BUFFERS); - if (SSL_CTX_set_cipher_list(ssl_ctx, CIPHER_LIST) == 0) { - std::cerr << "[ERROR] " << ERR_error_string(ERR_get_error(), nullptr) - << std::endl; - result = -1; - goto fin; - } - if (!config.keyfile.empty()) { - if (SSL_CTX_use_PrivateKey_file(ssl_ctx, config.keyfile.c_str(), - SSL_FILETYPE_PEM) != 1) { - std::cerr << "[ERROR] " << ERR_error_string(ERR_get_error(), nullptr) - << std::endl; - result = -1; - goto fin; - } - } - if (!config.certfile.empty()) { - if (SSL_CTX_use_certificate_chain_file(ssl_ctx, - config.certfile.c_str()) != 1) { - std::cerr << "[ERROR] " << ERR_error_string(ERR_get_error(), nullptr) - << std::endl; - result = -1; - goto fin; - } - } - SSL_CTX_set_next_proto_select_cb(ssl_ctx, client_select_next_proto_cb, - nullptr); - -#if OPENSSL_VERSION_NUMBER >= 0x10002000L - auto proto_list = util::get_default_alpn(); - - SSL_CTX_set_alpn_protos(ssl_ctx, proto_list.data(), proto_list.size()); -#endif // OPENSSL_VERSION_NUMBER >= 0x10002000L - } - { - HttpClient client{callbacks, loop, ssl_ctx}; - - nghttp2_priority_spec pri_spec; - int32_t dep_stream_id = 0; - - if (!config.no_dep) { - dep_stream_id = anchors[ANCHOR_FOLLOWERS].stream_id; - } - - nghttp2_priority_spec_init(&pri_spec, dep_stream_id, config.weight, 0); - - for (auto req : requests) { - for (int i = 0; i < config.multiply; ++i) { - client.add_request(std::get<0>(req), std::get<1>(req), std::get<2>(req), - pri_spec); - } - } - client.update_hostport(); - - client.record_start_time(); - - if (client.resolve_host(host, port) != 0) { - goto fin; - } - - client.record_domain_lookup_end_time(); - - if (client.initiate_connection() != 0) { - goto fin; - } - ev_run(loop, 0); - -#ifdef HAVE_JANSSON - if (!config.harfile.empty()) { - FILE *outfile; - if (config.harfile == "-") { - outfile = stdout; - } else { - outfile = fopen(config.harfile.c_str(), "wb"); - } - - if (outfile) { - client.output_har(outfile); - - if (outfile != stdout) { - fclose(outfile); - } - } else { - std::cerr << "Cannot open file " << config.harfile << ". " - << "har file could not be created." << std::endl; - } - } -#endif // HAVE_JANSSON - - if (client.success != client.reqvec.size()) { - std::cerr << "Some requests were not processed. total=" - << client.reqvec.size() << ", processed=" << client.success - << std::endl; - } - if (config.stat) { - print_stats(client); - } - } -fin: - if (ssl_ctx) { - SSL_CTX_free(ssl_ctx); - } - return result; -} -} // namespace - -namespace { -ssize_t file_read_callback(nghttp2_session *session, int32_t stream_id, - uint8_t *buf, size_t length, uint32_t *data_flags, - nghttp2_data_source *source, void *user_data) { - int rv; - auto req = static_cast( - nghttp2_session_get_stream_user_data(session, stream_id)); - assert(req); - int fd = source->fd; - ssize_t nread; - - while ((nread = pread(fd, buf, length, req->data_offset)) == -1 && - errno == EINTR) - ; - - if (nread == -1) { - return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; - } - - if (nread == 0) { - *data_flags |= NGHTTP2_DATA_FLAG_EOF; - if (!config.trailer.empty()) { - std::vector nva; - nva.reserve(config.trailer.size()); - for (auto &kv : config.trailer) { - nva.push_back(http2::make_nv(kv.name, kv.value, kv.no_index)); - } - rv = nghttp2_submit_trailer(session, stream_id, nva.data(), nva.size()); - if (rv != 0) { - if (nghttp2_is_fatal(rv)) { - return NGHTTP2_ERR_CALLBACK_FAILURE; - } - } else { - *data_flags |= NGHTTP2_DATA_FLAG_NO_END_STREAM; - } - } - } else { - req->data_offset += nread; - } - - return nread; -} -} // namespace - -namespace { -ssize_t send_callback(nghttp2_session *session, const uint8_t *data, - size_t length, int flags, void *user_data) { - auto client = static_cast(user_data); - auto &wb = client->wb; - - if (wb.wleft() == 0) { - return NGHTTP2_ERR_WOULDBLOCK; - } - - return wb.write(data, length); -} -} // namespace - -namespace { -int run(char **uris, int n) { - nghttp2_session_callbacks *callbacks; - - nghttp2_session_callbacks_new(&callbacks); - auto cbsdel = defer(nghttp2_session_callbacks_del, callbacks); - - nghttp2_session_callbacks_set_on_stream_close_callback( - callbacks, on_stream_close_callback); - - nghttp2_session_callbacks_set_on_frame_recv_callback(callbacks, - on_frame_recv_callback2); - - if (config.verbose) { - nghttp2_session_callbacks_set_on_frame_send_callback( - callbacks, verbose_on_frame_send_callback); - - nghttp2_session_callbacks_set_on_invalid_frame_recv_callback( - callbacks, verbose_on_invalid_frame_recv_callback); - } - - nghttp2_session_callbacks_set_on_data_chunk_recv_callback( - callbacks, on_data_chunk_recv_callback); - - nghttp2_session_callbacks_set_on_begin_headers_callback( - callbacks, on_begin_headers_callback); - - nghttp2_session_callbacks_set_on_header_callback(callbacks, - on_header_callback); - - nghttp2_session_callbacks_set_before_frame_send_callback( - callbacks, before_frame_send_callback); - - nghttp2_session_callbacks_set_send_callback(callbacks, send_callback); - - if (config.padding) { - nghttp2_session_callbacks_set_select_padding_callback( - callbacks, select_padding_callback); - } - - std::string prev_scheme; - std::string prev_host; - uint16_t prev_port = 0; - int failures = 0; - int data_fd = -1; - nghttp2_data_provider data_prd; - struct stat data_stat; - - if (!config.datafile.empty()) { - if (config.datafile == "-") { - if (fstat(0, &data_stat) == 0 && - (data_stat.st_mode & S_IFMT) == S_IFREG) { - // use STDIN if it is a regular file - data_fd = 0; - } else { - // copy the contents of STDIN to a temporary file - char tempfn[] = "/tmp/nghttp.temp.XXXXXX"; - data_fd = mkstemp(tempfn); - if (data_fd == -1) { - std::cerr << "[ERROR] Could not create a temporary file in /tmp" - << std::endl; - return 1; - } - if (unlink(tempfn) != 0) { - std::cerr << "[WARNING] failed to unlink temporary file:" << tempfn - << std::endl; - } - while (1) { - std::array buf; - ssize_t rret, wret; - while ((rret = read(0, buf.data(), buf.size())) == -1 && - errno == EINTR) - ; - if (rret == 0) - break; - if (rret == -1) { - std::cerr << "[ERROR] I/O error while reading from STDIN" - << std::endl; - return 1; - } - while ((wret = write(data_fd, buf.data(), rret)) == -1 && - errno == EINTR) - ; - if (wret != rret) { - std::cerr << "[ERROR] I/O error while writing to temporary file" - << std::endl; - return 1; - } - } - if (fstat(data_fd, &data_stat) == -1) { - close(data_fd); - std::cerr << "[ERROR] Could not stat temporary file" << std::endl; - return 1; - } - } - } else { - data_fd = open(config.datafile.c_str(), O_RDONLY | O_BINARY); - if (data_fd == -1) { - std::cerr << "[ERROR] Could not open file " << config.datafile - << std::endl; - return 1; - } - if (fstat(data_fd, &data_stat) == -1) { - close(data_fd); - std::cerr << "[ERROR] Could not stat file " << config.datafile - << std::endl; - return 1; - } - } - data_prd.source.fd = data_fd; - data_prd.read_callback = file_read_callback; - } - std::vector> - requests; - for (int i = 0; i < n; ++i) { - http_parser_url u; - memset(&u, 0, sizeof(u)); - auto uri = strip_fragment(uris[i]); - if (http_parser_parse_url(uri.c_str(), uri.size(), 0, &u) == 0 && - util::has_uri_field(u, UF_SCHEMA)) { - uint16_t port = util::has_uri_field(u, UF_PORT) - ? u.port - : util::get_default_port(uri.c_str(), u); - if (!util::fieldeq(uri.c_str(), u, UF_SCHEMA, prev_scheme.c_str()) || - !util::fieldeq(uri.c_str(), u, UF_HOST, prev_host.c_str()) || - port != prev_port) { - if (!requests.empty()) { - if (communicate(prev_scheme, prev_host, prev_port, - std::move(requests), callbacks) != 0) { - ++failures; - } - requests.clear(); - } - prev_scheme = util::get_uri_field(uri.c_str(), u, UF_SCHEMA); - prev_host = util::get_uri_field(uri.c_str(), u, UF_HOST); - prev_port = port; - } - requests.emplace_back(uri, data_fd == -1 ? nullptr : &data_prd, - data_stat.st_size); - } - } - if (!requests.empty()) { - if (communicate(prev_scheme, prev_host, prev_port, std::move(requests), - callbacks) != 0) { - ++failures; - } - } - return failures; -} -} // namespace - -namespace { -void print_version(std::ostream &out) { - out << "nghttp nghttp2/" NGHTTP2_VERSION << std::endl; -} -} // namespace - -namespace { -void print_usage(std::ostream &out) { - out << R"(Usage: nghttp [OPTIONS]... ... -HTTP/2 experimental client)" << std::endl; -} -} // namespace - -namespace { -void print_help(std::ostream &out) { - print_usage(out); - out << R"( - Specify URI to access. -Options: - -v, --verbose - Print debug information such as reception and - transmission of frames and name/value pairs. Specifying - this option multiple times increases verbosity. - -n, --null-out - Discard downloaded data. - -O, --remote-name - Save download data in the current directory. The - filename is dereived from URI. If URI ends with '/', - 'index.html' is used as a filename. Not implemented - yet. - -t, --timeout= - Timeout each request after . Set 0 to disable - timeout. - -w, --window-bits= - Sets the stream level initial window size to 2**-1. - -W, --connection-window-bits= - Sets the connection level initial window size to - 2**-1. - -a, --get-assets - Download assets such as stylesheets, images and script - files linked from the downloaded resource. Only links - whose origins are the same with the linking resource - will be downloaded. nghttp prioritizes resources using - HTTP/2 dependency based priority. The priority order, - from highest to lowest, is html itself, css, javascript - and images. - -s, --stat Print statistics. - -H, --header=
      - Add a header to the requests. Example: -H':method: PUT' - --trailer=
      - Add a trailer header to the requests.
      must not - include pseudo header field (header field name starting - with ':'). To send trailer, one must use -d option to - send request body. Example: --trailer 'foo: bar'. - --cert= - Use the specified client certificate file. The file - must be in PEM format. - --key= Use the client private key file. The file must be in - PEM format. - -d, --data= - Post FILE to server. If '-' is given, data will be read - from stdin. - -m, --multiply= - Request each URI times. By default, same URI is not - requested twice. This option disables it too. - -u, --upgrade - Perform HTTP Upgrade for HTTP/2. This option is ignored - if the request URI has https scheme. If -d is used, the - HTTP upgrade request is performed with OPTIONS method. - -p, --weight= - Sets priority group weight. The valid value range is - [)" << NGHTTP2_MIN_WEIGHT << ", " << NGHTTP2_MAX_WEIGHT - << R"(], inclusive. - Default: )" << NGHTTP2_DEFAULT_WEIGHT << R"( - -M, --peer-max-concurrent-streams= - Use as SETTINGS_MAX_CONCURRENT_STREAMS value of - remote endpoint as if it is received in SETTINGS frame. - The default is large enough as it is seen as unlimited. - -c, --header-table-size= - Specify decoder header table size. - -b, --padding= - Add at most bytes to a frame payload as padding. - Specify 0 to disable padding. - -r, --har= - Output HTTP transactions in HAR format. If '-' - is given, data is written to stdout. - --color Force colored log output. - --continuation - Send large header to test CONTINUATION. - --no-content-length - Don't send content-length header field. - --no-dep Don't send dependency based priority hint to server. - --hexdump Display the incoming traffic in hexadecimal (Canonical - hex+ASCII display). If SSL/TLS is used, decrypted data - are used. - --no-push Disable server push. - --version Display version information and exit. - -h, --help Display this help and exit. - --- - - The argument is an integer and an optional unit (e.g., 10K is - 10 * 1024). Units are K, M and G (powers of 1024). - - The argument is an integer and an optional unit (e.g., 1s - is 1 second and 500ms is 500 milliseconds). Units are h, m, s or ms - (hours, minutes, seconds and milliseconds, respectively). If a unit - is omitted, a second is used as unit.)" << std::endl; -} -} // namespace - -int main(int argc, char **argv) { - bool color = false; - while (1) { - static int flag = 0; - static option long_options[] = { - {"verbose", no_argument, nullptr, 'v'}, - {"null-out", no_argument, nullptr, 'n'}, - {"remote-name", no_argument, nullptr, 'O'}, - {"timeout", required_argument, nullptr, 't'}, - {"window-bits", required_argument, nullptr, 'w'}, - {"connection-window-bits", required_argument, nullptr, 'W'}, - {"get-assets", no_argument, nullptr, 'a'}, - {"stat", no_argument, nullptr, 's'}, - {"help", no_argument, nullptr, 'h'}, - {"header", required_argument, nullptr, 'H'}, - {"data", required_argument, nullptr, 'd'}, - {"multiply", required_argument, nullptr, 'm'}, - {"upgrade", no_argument, nullptr, 'u'}, - {"weight", required_argument, nullptr, 'p'}, - {"peer-max-concurrent-streams", required_argument, nullptr, 'M'}, - {"header-table-size", required_argument, nullptr, 'c'}, - {"padding", required_argument, nullptr, 'b'}, - {"har", required_argument, nullptr, 'r'}, - {"cert", required_argument, &flag, 1}, - {"key", required_argument, &flag, 2}, - {"color", no_argument, &flag, 3}, - {"continuation", no_argument, &flag, 4}, - {"version", no_argument, &flag, 5}, - {"no-content-length", no_argument, &flag, 6}, - {"no-dep", no_argument, &flag, 7}, - {"trailer", required_argument, &flag, 9}, - {"hexdump", no_argument, &flag, 10}, - {"no-push", no_argument, &flag, 11}, - {nullptr, 0, nullptr, 0}}; - int option_index = 0; - int c = getopt_long(argc, argv, "M:Oab:c:d:gm:np:r:hH:vst:uw:W:", - long_options, &option_index); - if (c == -1) { - break; - } - switch (c) { - case 'M': - // peer-max-concurrent-streams option - config.peer_max_concurrent_streams = strtoul(optarg, nullptr, 10); - break; - case 'O': - config.remote_name = true; - break; - case 'h': - print_help(std::cout); - exit(EXIT_SUCCESS); - case 'b': - config.padding = strtol(optarg, nullptr, 10); - break; - case 'n': - config.null_out = true; - break; - case 'p': { - errno = 0; - auto n = strtoul(optarg, nullptr, 10); - if (errno == 0 && NGHTTP2_MIN_WEIGHT <= n && n <= NGHTTP2_MAX_WEIGHT) { - config.weight = n; - } else { - std::cerr << "-p: specify the integer in the range [" - << NGHTTP2_MIN_WEIGHT << ", " << NGHTTP2_MAX_WEIGHT - << "], inclusive" << std::endl; - exit(EXIT_FAILURE); - } - break; - } - case 'r': -#ifdef HAVE_JANSSON - config.harfile = optarg; -#else // !HAVE_JANSSON - std::cerr << "[WARNING]: -r, --har option is ignored because\n" - << "the binary was not compiled with libjansson." << std::endl; -#endif // !HAVE_JANSSON - break; - case 'v': - ++config.verbose; - break; - case 't': - config.timeout = util::parse_duration_with_unit(optarg); - if (config.timeout == std::numeric_limits::infinity()) { - std::cerr << "-t: bad timeout value: " << optarg << std::endl; - exit(EXIT_FAILURE); - } - break; - case 'u': - config.upgrade = true; - break; - case 'w': - case 'W': { - errno = 0; - char *endptr = nullptr; - unsigned long int n = strtoul(optarg, &endptr, 10); - if (errno == 0 && *endptr == '\0' && n < 31) { - if (c == 'w') { - config.window_bits = n; - } else { - config.connection_window_bits = n; - } - } else { - std::cerr << "-" << static_cast(c) - << ": specify the integer in the range [0, 30], inclusive" - << std::endl; - exit(EXIT_FAILURE); - } - break; - } - case 'H': { - char *header = optarg; - // Skip first possible ':' in the header name - char *value = strchr(optarg + 1, ':'); - if (!value || (header[0] == ':' && header + 1 == value)) { - std::cerr << "-H: invalid header: " << optarg << std::endl; - exit(EXIT_FAILURE); - } - *value = 0; - value++; - while (isspace(*value)) { - value++; - } - if (*value == 0) { - // This could also be a valid case for suppressing a header - // similar to curl - std::cerr << "-H: invalid header - value missing: " << optarg - << std::endl; - exit(EXIT_FAILURE); - } - config.headers.emplace_back(header, value, false); - util::inp_strlower(config.headers.back().name); - break; - } - case 'a': -#ifdef HAVE_LIBXML2 - config.get_assets = true; -#else // !HAVE_LIBXML2 - std::cerr << "[WARNING]: -a, --get-assets option is ignored because\n" - << "the binary was not compiled with libxml2." << std::endl; -#endif // !HAVE_LIBXML2 - break; - case 's': - config.stat = true; - break; - case 'd': - config.datafile = optarg; - break; - case 'm': - config.multiply = strtoul(optarg, nullptr, 10); - break; - case 'c': - errno = 0; - config.header_table_size = util::parse_uint_with_unit(optarg); - if (config.header_table_size == -1) { - std::cerr << "-c: Bad option value: " << optarg << std::endl; - exit(EXIT_FAILURE); - } - break; - case '?': - util::show_candidates(argv[optind - 1], long_options); - exit(EXIT_FAILURE); - case 0: - switch (flag) { - case 1: - // cert option - config.certfile = optarg; - break; - case 2: - // key option - config.keyfile = optarg; - break; - case 3: - // color option - color = true; - break; - case 4: - // continuation option - config.continuation = true; - break; - case 5: - // version option - print_version(std::cout); - exit(EXIT_SUCCESS); - case 6: - // no-content-length option - config.no_content_length = true; - break; - case 7: - // no-dep option - config.no_dep = true; - break; - case 9: { - // trailer option - auto header = optarg; - auto value = strchr(optarg, ':'); - if (!value) { - std::cerr << "--trailer: invalid header: " << optarg << std::endl; - exit(EXIT_FAILURE); - } - *value = 0; - value++; - while (isspace(*value)) { - value++; - } - if (*value == 0) { - // This could also be a valid case for suppressing a header - // similar to curl - std::cerr << "--trailer: invalid header - value missing: " << optarg - << std::endl; - exit(EXIT_FAILURE); - } - config.trailer.emplace_back(header, value, false); - util::inp_strlower(config.trailer.back().name); - break; - } - case 10: - // hexdump option - config.hexdump = true; - break; - case 11: - // no-push option - config.no_push = true; - break; - } - break; - default: - break; - } - } - - set_color_output(color || isatty(fileno(stdout))); - - nghttp2_option_set_peer_max_concurrent_streams( - config.http2_option, config.peer_max_concurrent_streams); - - struct sigaction act; - memset(&act, 0, sizeof(struct sigaction)); - act.sa_handler = SIG_IGN; - sigaction(SIGPIPE, &act, nullptr); - OPENSSL_config(nullptr); - OpenSSL_add_all_algorithms(); - SSL_load_error_strings(); - SSL_library_init(); - reset_timer(); - return run(argv + optind, argc - optind); -} - -} // namespace nghttp2 - -int main(int argc, char **argv) { return nghttp2::main(argc, argv); } diff --git a/src/nghttp.h b/src/nghttp.h deleted file mode 100644 index 0a7ee35..0000000 --- a/src/nghttp.h +++ /dev/null @@ -1,272 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2015 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#ifndef NGHTTP_H -#define NGHTTP_H - -#include "nghttp2_config.h" - -#include -#ifdef HAVE_SYS_SOCKET_H -#include -#endif // HAVE_SYS_SOCKET_H -#ifdef HAVE_NETDB_H -#include -#endif // HAVE_NETDB_H - -#include -#include -#include -#include -#include - -#include - -#include - -#include - -#include "http-parser/http_parser.h" - -#include "buffer.h" -#include "http2.h" -#include "nghttp2_gzip.h" - -namespace nghttp2 { - -class HtmlParser; - -struct Config { - Config(); - ~Config(); - - Headers headers; - Headers trailer; - std::string certfile; - std::string keyfile; - std::string datafile; - std::string harfile; - nghttp2_option *http2_option; - size_t output_upper_thres; - size_t padding; - ssize_t peer_max_concurrent_streams; - ssize_t header_table_size; - int32_t weight; - int multiply; - // milliseconds - ev_tstamp timeout; - int window_bits; - int connection_window_bits; - int verbose; - bool null_out; - bool remote_name; - bool get_assets; - bool stat; - bool upgrade; - bool continuation; - bool no_content_length; - bool no_dep; - bool hexdump; - bool no_push; -}; - -enum class RequestState { INITIAL, ON_REQUEST, ON_RESPONSE, ON_COMPLETE }; - -struct RequestTiming { - // The point in time when request is started to be sent. - // Corresponds to requestStart in Resource Timing TR. - std::chrono::steady_clock::time_point request_start_time; - // The point in time when first byte of response is received. - // Corresponds to responseStart in Resource Timing TR. - std::chrono::steady_clock::time_point response_start_time; - // The point in time when last byte of response is received. - // Corresponds to responseEnd in Resource Timing TR. - std::chrono::steady_clock::time_point response_end_time; - RequestState state; - RequestTiming() : state(RequestState::INITIAL) {} -}; - -struct Request { - // For pushed request, |uri| is empty and |u| is zero-cleared. - Request(const std::string &uri, const http_parser_url &u, - const nghttp2_data_provider *data_prd, int64_t data_length, - const nghttp2_priority_spec &pri_spec, int level = 0); - ~Request(); - - void init_inflater(); - - void init_html_parser(); - int update_html_parser(const uint8_t *data, size_t len, int fin); - - std::string make_reqpath() const; - - bool is_ipv6_literal_addr() const; - - bool response_pseudo_header_allowed(int16_t token) const; - bool push_request_pseudo_header_allowed(int16_t token) const; - - Headers::value_type *get_res_header(int16_t token); - Headers::value_type *get_req_header(int16_t token); - - void record_request_start_time(); - void record_response_start_time(); - void record_response_end_time(); - - Headers res_nva; - Headers req_nva; - // URI without fragment - std::string uri; - http_parser_url u; - nghttp2_priority_spec pri_spec; - RequestTiming timing; - int64_t data_length; - int64_t data_offset; - // Number of bytes received from server - int64_t response_len; - nghttp2_gzip *inflater; - HtmlParser *html_parser; - const nghttp2_data_provider *data_prd; - int32_t stream_id; - int status; - // Recursion level: 0: first entity, 1: entity linked from first entity - int level; - http2::HeaderIndex res_hdidx; - // used for incoming PUSH_PROMISE - http2::HeaderIndex req_hdidx; - bool expect_final_response; -}; - -struct SessionTiming { - // The point in time when operation was started. Corresponds to - // startTime in Resource Timing TR, but recorded in system clock time. - std::chrono::system_clock::time_point system_start_time; - // Same as above, but recorded in steady clock time. - std::chrono::steady_clock::time_point start_time; - // The point in time when DNS resolution was completed. Corresponds - // to domainLookupEnd in Resource Timing TR. - std::chrono::steady_clock::time_point domain_lookup_end_time; - // The point in time when connection was established or SSL/TLS - // handshake was completed. Corresponds to connectEnd in Resource - // Timing TR. - std::chrono::steady_clock::time_point connect_end_time; -}; - -enum class ClientState { IDLE, CONNECTED }; - -struct HttpClient { - HttpClient(const nghttp2_session_callbacks *callbacks, struct ev_loop *loop, - SSL_CTX *ssl_ctx); - ~HttpClient(); - - bool need_upgrade() const; - int resolve_host(const std::string &host, uint16_t port); - int initiate_connection(); - void disconnect(); - - int noop(); - int read_clear(); - int write_clear(); - int connected(); - int tls_handshake(); - int read_tls(); - int write_tls(); - - int do_read(); - int do_write(); - - int on_upgrade_connect(); - int on_upgrade_read(const uint8_t *data, size_t len); - int on_read(const uint8_t *data, size_t len); - int on_write(); - - int connection_made(); - void connect_fail(); - void request_done(Request *req); - - void signal_write(); - - bool all_requests_processed() const; - void update_hostport(); - bool add_request(const std::string &uri, - const nghttp2_data_provider *data_prd, int64_t data_length, - const nghttp2_priority_spec &pri_spec, int level = 0); - - void record_start_time(); - void record_domain_lookup_end_time(); - void record_connect_end_time(); - -#ifdef HAVE_JANSSON - void output_har(FILE *outfile); -#endif // HAVE_JANSSON - - std::vector> reqvec; - // Insert path already added in reqvec to prevent multiple request - // for 1 resource. - std::set path_cache; - std::string scheme; - std::string host; - std::string hostport; - // Used for parse the HTTP upgrade response from server - std::unique_ptr htp; - SessionTiming timing; - ev_io wev; - ev_io rev; - ev_timer wt; - ev_timer rt; - ev_timer settings_timer; - std::function readfn, writefn; - std::function on_readfn; - std::function on_writefn; - nghttp2_session *session; - const nghttp2_session_callbacks *callbacks; - struct ev_loop *loop; - SSL_CTX *ssl_ctx; - SSL *ssl; - addrinfo *addrs; - addrinfo *next_addr; - addrinfo *cur_addr; - // The number of completed requests, including failed ones. - size_t complete; - // The number of requests that local endpoint received END_STREAM - // from peer. - size_t success; - // The length of settings_payload - size_t settings_payloadlen; - ClientState state; - // The HTTP status code of the response message of HTTP Upgrade. - unsigned int upgrade_response_status_code; - int fd; - // true if the response message of HTTP Upgrade request is fully - // received. It is not relevant the upgrade succeeds, or not. - bool upgrade_response_complete; - Buffer<65536> wb; - // SETTINGS payload sent as token68 in HTTP Upgrade - std::array settings_payload; - - enum { ERR_CONNECT_FAIL = -100 }; -}; - -} // namespace nghttp2 - -#endif // NGHTTP_H diff --git a/src/nghttp2_config.h b/src/nghttp2_config.h deleted file mode 100644 index 9dede35..0000000 --- a/src/nghttp2_config.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2012 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#ifndef NGHTTP2_CONFIG_H -#define NGHTTP2_CONFIG_H - -#ifdef HAVE_CONFIG_H -#include -#endif // HAVE_CONFIG_H - -// gcc 4.6 has std::chrono::monotonic_clock, which was renamed as -// std::chrono::steady_clock in C++11 standard. -#ifndef HAVE_STEADY_CLOCK -#define steady_clock monotonic_clock -#endif // !HAVE_STEADY_CLOCK - -#endif // NGHTTP2_CONFIG_H diff --git a/src/nghttp2_gzip.c b/src/nghttp2_gzip.c deleted file mode 100644 index b12f311..0000000 --- a/src/nghttp2_gzip.c +++ /dev/null @@ -1,92 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2012 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#include "nghttp2_gzip.h" - -#include - -int nghttp2_gzip_inflate_new(nghttp2_gzip **inflater_ptr) { - int rv; - *inflater_ptr = malloc(sizeof(nghttp2_gzip)); - if (*inflater_ptr == NULL) { - return -1; - } - (*inflater_ptr)->finished = 0; - (*inflater_ptr)->zst.next_in = Z_NULL; - (*inflater_ptr)->zst.avail_in = 0; - (*inflater_ptr)->zst.zalloc = Z_NULL; - (*inflater_ptr)->zst.zfree = Z_NULL; - (*inflater_ptr)->zst.opaque = Z_NULL; - rv = inflateInit2(&(*inflater_ptr)->zst, 47); - if (rv != Z_OK) { - free(*inflater_ptr); - return -1; - } - return 0; -} - -void nghttp2_gzip_inflate_del(nghttp2_gzip *inflater) { - if (inflater != NULL) { - inflateEnd(&inflater->zst); - free(inflater); - } -} - -int nghttp2_gzip_inflate(nghttp2_gzip *inflater, uint8_t *out, - size_t *outlen_ptr, const uint8_t *in, - size_t *inlen_ptr) { - int rv; - if (inflater->finished) { - return -1; - } - inflater->zst.avail_in = (unsigned int)*inlen_ptr; - inflater->zst.next_in = (unsigned char *)in; - inflater->zst.avail_out = (unsigned int)*outlen_ptr; - inflater->zst.next_out = out; - - rv = inflate(&inflater->zst, Z_NO_FLUSH); - - *inlen_ptr -= inflater->zst.avail_in; - *outlen_ptr -= inflater->zst.avail_out; - switch (rv) { - case Z_STREAM_END: - inflater->finished = 1; - case Z_OK: - case Z_BUF_ERROR: - return 0; - case Z_DATA_ERROR: - case Z_STREAM_ERROR: - case Z_NEED_DICT: - case Z_MEM_ERROR: - return -1; - default: - assert(0); - /* We need this for some compilers */ - return 0; - } -} - -int nghttp2_gzip_inflate_finished(nghttp2_gzip *inflater) { - return inflater->finished; -} diff --git a/src/nghttp2_gzip.h b/src/nghttp2_gzip.h deleted file mode 100644 index 2fa905a..0000000 --- a/src/nghttp2_gzip.h +++ /dev/null @@ -1,122 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2012 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#ifndef NGHTTP2_GZIP_H - -#ifdef HAVE_CONFIG_H -#include -#endif /* HAVE_CONFIG_H */ -#include - -#include - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * @struct - * - * The gzip stream to inflate data. - */ -typedef struct { - z_stream zst; - int8_t finished; -} nghttp2_gzip; - -/** - * @function - * - * A helper function to set up a per request gzip stream to inflate - * data. - * - * This function returns 0 if it succeeds, or -1. - */ -int nghttp2_gzip_inflate_new(nghttp2_gzip **inflater_ptr); - -/** - * @function - * - * Frees the inflate stream. The |inflater| may be ``NULL``. - */ -void nghttp2_gzip_inflate_del(nghttp2_gzip *inflater); - -/** - * @function - * - * Inflates data in |in| with the length |*inlen_ptr| and stores the - * inflated data to |out| which has allocated size at least - * |*outlen_ptr|. On return, |*outlen_ptr| is updated to represent - * the number of data written in |out|. Similarly, |*inlen_ptr| is - * updated to represent the number of input bytes processed. - * - * This function returns 0 if it succeeds, or -1. - * - * The example follows:: - * - * void on_data_chunk_recv_callback(nghttp2_session *session, - * uint8_t flags, - * int32_t stream_id, - * const uint8_t *data, size_t len, - * void *user_data) - * { - * ... - * req = nghttp2_session_get_stream_user_data(session, stream_id); - * nghttp2_gzip *inflater = req->inflater; - * while(len > 0) { - * uint8_t out[MAX_OUTLEN]; - * size_t outlen = MAX_OUTLEN; - * size_t tlen = len; - * int rv; - * rv = nghttp2_gzip_inflate(inflater, out, &outlen, data, &tlen); - * if(rv != 0) { - * nghttp2_submit_rst_stream(session, stream_id, - * NGHTTP2_INTERNAL_ERROR); - * break; - * } - * ... Do stuff ... - * data += tlen; - * len -= tlen; - * } - * .... - * } - */ -int nghttp2_gzip_inflate(nghttp2_gzip *inflater, uint8_t *out, - size_t *outlen_ptr, const uint8_t *in, - size_t *inlen_ptr); - -/** - * @function - * - * Returns nonzero if |inflater| sees the end of deflate stream. - * After this function returns nonzero, `nghttp2_gzip_inflate()` with - * |inflater| gets to return error. - */ -int nghttp2_gzip_inflate_finished(nghttp2_gzip *inflater); - -#ifdef __cplusplus -} -#endif - -#endif /* NGHTTP2_GZIP_H */ diff --git a/src/nghttp2_gzip_test.c b/src/nghttp2_gzip_test.c deleted file mode 100644 index eb43ae6..0000000 --- a/src/nghttp2_gzip_test.c +++ /dev/null @@ -1,115 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2012 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#include "nghttp2_gzip_test.h" - -#include -#include - -#include - -#include - -#include "nghttp2_gzip.h" - -static ssize_t deflate_data(uint8_t *out, size_t outlen, const uint8_t *in, - size_t inlen) { - int rv; - z_stream zst; - zst.next_in = Z_NULL; - zst.zalloc = Z_NULL; - zst.zfree = Z_NULL; - zst.opaque = Z_NULL; - - rv = deflateInit(&zst, Z_DEFAULT_COMPRESSION); - assert(rv == Z_OK); - - zst.avail_in = (unsigned int)inlen; - zst.next_in = (uint8_t *)in; - zst.avail_out = (unsigned int)outlen; - zst.next_out = out; - rv = deflate(&zst, Z_SYNC_FLUSH); - assert(rv == Z_OK); - - deflateEnd(&zst); - - return outlen - zst.avail_out; -} - -static const char input[] = - "THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND " - "EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF " - "MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND " - "NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE " - "LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION " - "OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION " - "WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE."; - -void test_nghttp2_gzip_inflate(void) { - nghttp2_gzip *inflater; - uint8_t in[4096], out[4096], *inptr; - size_t inlen = sizeof(in); - size_t inproclen, outproclen; - const char *inputptr = input; - - inlen = deflate_data(in, inlen, (const uint8_t *)input, sizeof(input) - 1); - - CU_ASSERT(0 == nghttp2_gzip_inflate_new(&inflater)); - /* First 16 bytes */ - inptr = in; - inproclen = inlen; - outproclen = 16; - CU_ASSERT( - 0 == nghttp2_gzip_inflate(inflater, out, &outproclen, inptr, &inproclen)); - CU_ASSERT(16 == outproclen); - CU_ASSERT(inproclen > 0); - CU_ASSERT(0 == memcmp(inputptr, out, outproclen)); - /* Next 32 bytes */ - inptr += inproclen; - inlen -= inproclen; - inproclen = inlen; - inputptr += outproclen; - outproclen = 32; - CU_ASSERT( - 0 == nghttp2_gzip_inflate(inflater, out, &outproclen, inptr, &inproclen)); - CU_ASSERT(32 == outproclen); - CU_ASSERT(inproclen > 0); - CU_ASSERT(0 == memcmp(inputptr, out, outproclen)); - /* Rest */ - inptr += inproclen; - inlen -= inproclen; - inproclen = inlen; - inputptr += outproclen; - outproclen = sizeof(out); - CU_ASSERT( - 0 == nghttp2_gzip_inflate(inflater, out, &outproclen, inptr, &inproclen)); - CU_ASSERT(sizeof(input) - 49 == outproclen); - CU_ASSERT(inproclen > 0); - CU_ASSERT(0 == memcmp(inputptr, out, outproclen)); - - inlen -= inproclen; - CU_ASSERT(0 == inlen); - - nghttp2_gzip_inflate_del(inflater); -} diff --git a/src/nghttp2_gzip_test.h b/src/nghttp2_gzip_test.h deleted file mode 100644 index 2defcdc..0000000 --- a/src/nghttp2_gzip_test.h +++ /dev/null @@ -1,42 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2012 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#ifndef NGHTTP2_GZIP_TEST_H -#define NGHTTP2_GZIP_TEST_H - -#ifdef HAVE_CONFIG_H -#include -#endif /* HAVE_CONFIG_H */ - -#ifdef __cplusplus -extern "C" { -#endif - -void test_nghttp2_gzip_inflate(void); - -#ifdef __cplusplus -} -#endif - -#endif /* NGHTTP2_GZIP_TEST_H */ diff --git a/src/nghttpd.cc b/src/nghttpd.cc deleted file mode 100644 index 4ffc0bc..0000000 --- a/src/nghttpd.cc +++ /dev/null @@ -1,381 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2012 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#include "nghttp2_config.h" - -#ifdef HAVE_UNISTD_H -#include -#endif // HAVE_UNISTD_H -#include -#include - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "app_helper.h" -#include "HttpServer.h" -#include "util.h" -#include "ssl.h" - -namespace nghttp2 { - -namespace { -int parse_push_config(Config &config, const char *optarg) { - const char *eq = strchr(optarg, '='); - if (eq == NULL) { - return -1; - } - auto &paths = config.push[std::string(optarg, eq)]; - auto optarg_end = optarg + strlen(optarg); - auto i = eq + 1; - for (;;) { - const char *j = strchr(i, ','); - if (j == NULL) { - j = optarg_end; - } - paths.emplace_back(i, j); - if (j == optarg_end) { - break; - } - i = j; - ++i; - } - - return 0; -} -} // namespace - -namespace { -void print_version(std::ostream &out) { - out << "nghttpd nghttp2/" NGHTTP2_VERSION << std::endl; -} -} // namespace - -namespace { -void print_usage(std::ostream &out) { - out << "Usage: nghttpd [OPTION]... [ ]\n" - << "HTTP/2 experimental server" << std::endl; -} -} // namespace - -namespace { -void print_help(std::ostream &out) { - Config config; - print_usage(out); - out << R"( - Specify listening port number. - - Set path to server's private key. Required unless - --no-tls is specified. - Set path to server's certificate. Required unless - --no-tls is specified. -Options: - -a, --address= - The address to bind to. If not specified the default IP - address determined by getaddrinfo is used. - -D, --daemon - Run in a background. If -D is used, the current working - directory is changed to '/'. Therefore if this option - is used, -d option must be specified. - -V, --verify-client - The server sends a client certificate request. If the - client did not return a certificate, the handshake is - terminated. Currently, this option just requests a - client certificate and does not verify it. - -d, --htdocs= - Specify document root. If this option is not specified, - the document root is the current working directory. - -v, --verbose - Print debug information such as reception/ transmission - of frames and name/value pairs. - --no-tls Disable SSL/TLS. - -c, --header-table-size= - Specify decoder header table size. - --color Force colored log output. - -p, --push== - Push resources s when is requested. - This option can be used repeatedly to specify multiple - push configurations. and s are - relative to document root. See --htdocs option. - Example: -p/=/foo.png -p/doc=/bar.css - -b, --padding= - Add at most bytes to a frame payload as padding. - Specify 0 to disable padding. - -m, --max-concurrent-streams= - Set the maximum number of the concurrent streams in one - HTTP/2 session. - Default: )" << config.max_concurrent_streams << R"( - -n, --workers= - Set the number of worker threads. - Default: 1 - -e, --error-gzip - Make error response gzipped. - --dh-param-file= - Path to file that contains DH parameters in PEM format. - Without this option, DHE cipher suites are not - available. - --early-response - Start sending response when request HEADERS is received, - rather than complete request is received. - --trailer=
      - Add a trailer header to a response.
      must not - include pseudo header field (header field name starting - with ':'). The trailer is sent only if a response has - body part. Example: --trailer 'foo: bar'. - --hexdump Display the incoming traffic in hexadecimal (Canonical - hex+ASCII display). If SSL/TLS is used, decrypted data - are used. - --echo-upload - Send back uploaded content if method is POST or PUT. - --version Display version information and exit. - -h, --help Display this help and exit. - --- - - The argument is an integer and an optional unit (e.g., 10K is - 10 * 1024). Units are K, M and G (powers of 1024).)" << std::endl; -} -} // namespace - -int main(int argc, char **argv) { - Config config; - bool color = false; - while (1) { - static int flag = 0; - static option long_options[] = { - {"address", required_argument, nullptr, 'a'}, - {"daemon", no_argument, nullptr, 'D'}, - {"htdocs", required_argument, nullptr, 'd'}, - {"help", no_argument, nullptr, 'h'}, - {"verbose", no_argument, nullptr, 'v'}, - {"verify-client", no_argument, nullptr, 'V'}, - {"header-table-size", required_argument, nullptr, 'c'}, - {"push", required_argument, nullptr, 'p'}, - {"padding", required_argument, nullptr, 'b'}, - {"max-concurrent-streams", required_argument, nullptr, 'm'}, - {"workers", required_argument, nullptr, 'n'}, - {"error-gzip", no_argument, nullptr, 'e'}, - {"no-tls", no_argument, &flag, 1}, - {"color", no_argument, &flag, 2}, - {"version", no_argument, &flag, 3}, - {"dh-param-file", required_argument, &flag, 4}, - {"early-response", no_argument, &flag, 5}, - {"trailer", required_argument, &flag, 6}, - {"hexdump", no_argument, &flag, 7}, - {"echo-upload", no_argument, &flag, 8}, - {nullptr, 0, nullptr, 0}}; - int option_index = 0; - int c = getopt_long(argc, argv, "DVb:c:d:ehm:n:p:va:", long_options, - &option_index); - char *end; - if (c == -1) { - break; - } - switch (c) { - case 'a': - config.address = optarg; - break; - case 'D': - config.daemon = true; - break; - case 'V': - config.verify_client = true; - break; - case 'b': - config.padding = strtol(optarg, nullptr, 10); - break; - case 'd': - config.htdocs = optarg; - break; - case 'e': - config.error_gzip = true; - break; - case 'm': { - // max-concurrent-streams option - auto n = util::parse_uint(optarg); - if (n == -1) { - std::cerr << "-m: invalid argument: " << optarg << std::endl; - exit(EXIT_FAILURE); - } - config.max_concurrent_streams = n; - break; - } - case 'n': -#ifdef NOTHREADS - std::cerr << "-n: WARNING: Threading disabled at build time, " - << "no threads created." << std::endl; -#else - errno = 0; - config.num_worker = strtoul(optarg, &end, 10); - if (errno == ERANGE || *end != '\0' || config.num_worker == 0) { - std::cerr << "-n: Bad option value: " << optarg << std::endl; - exit(EXIT_FAILURE); - } -#endif // NOTHREADS - break; - case 'h': - print_help(std::cout); - exit(EXIT_SUCCESS); - case 'v': - config.verbose = true; - break; - case 'c': - errno = 0; - config.header_table_size = util::parse_uint_with_unit(optarg); - if (config.header_table_size == -1) { - std::cerr << "-c: Bad option value: " << optarg << std::endl; - exit(EXIT_FAILURE); - } - break; - case 'p': - if (parse_push_config(config, optarg) != 0) { - std::cerr << "-p: Bad option value: " << optarg << std::endl; - } - break; - case '?': - util::show_candidates(argv[optind - 1], long_options); - exit(EXIT_FAILURE); - case 0: - switch (flag) { - case 1: - // no-tls option - config.no_tls = true; - break; - case 2: - // color option - color = true; - break; - case 3: - // version - print_version(std::cout); - exit(EXIT_SUCCESS); - case 4: - // dh-param-file - config.dh_param_file = optarg; - break; - case 5: - // early-response - config.early_response = true; - break; - case 6: { - // trailer option - auto header = optarg; - auto value = strchr(optarg, ':'); - if (!value) { - std::cerr << "--trailer: invalid header: " << optarg << std::endl; - exit(EXIT_FAILURE); - } - *value = 0; - value++; - while (isspace(*value)) { - value++; - } - if (*value == 0) { - // This could also be a valid case for suppressing a header - // similar to curl - std::cerr << "--trailer: invalid header - value missing: " << optarg - << std::endl; - exit(EXIT_FAILURE); - } - config.trailer.emplace_back(header, value, false); - util::inp_strlower(config.trailer.back().name); - break; - } - case 7: - // hexdump option - config.hexdump = true; - break; - case 8: - // echo-upload option - config.echo_upload = true; - break; - } - break; - default: - break; - } - } - if (argc - optind < (config.no_tls ? 1 : 3)) { - print_usage(std::cerr); - std::cerr << "Too few arguments" << std::endl; - exit(EXIT_FAILURE); - } - - config.port = strtol(argv[optind++], nullptr, 10); - - if (!config.no_tls) { - config.private_key_file = argv[optind++]; - config.cert_file = argv[optind++]; - } - - if (config.daemon) { - if (config.htdocs.empty()) { - print_usage(std::cerr); - std::cerr << "-d option must be specified when -D is used." << std::endl; - exit(EXIT_FAILURE); - } - if (daemon(0, 0) == -1) { - perror("daemon"); - exit(EXIT_FAILURE); - } - } - if (config.htdocs.empty()) { - config.htdocs = "./"; - } - - set_color_output(color || isatty(fileno(stdout))); - - struct sigaction act; - memset(&act, 0, sizeof(struct sigaction)); - act.sa_handler = SIG_IGN; - sigaction(SIGPIPE, &act, nullptr); - OPENSSL_config(nullptr); - OpenSSL_add_all_algorithms(); - SSL_load_error_strings(); - SSL_library_init(); -#ifndef NOTHREADS - ssl::LibsslGlobalLock lock; -#endif // NOTHREADS - - reset_timer(); - - HttpServer server(&config); - if (server.run() != 0) { - exit(EXIT_FAILURE); - } - return 0; -} - -} // namespace nghttp2 - -int main(int argc, char **argv) { return nghttp2::main(argc, argv); } diff --git a/src/shrpx-unittest.cc b/src/shrpx-unittest.cc deleted file mode 100644 index 86edb1e..0000000 --- a/src/shrpx-unittest.cc +++ /dev/null @@ -1,178 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2013 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#ifdef HAVE_CONFIG_H -#include -#endif // HAVE_CONFIG_H - -#include -#include -#include -#include -#include -// include test cases' include files here -#include "shrpx_ssl_test.h" -#include "shrpx_downstream_test.h" -#include "shrpx_config_test.h" -#include "http2_test.h" -#include "util_test.h" -#include "nghttp2_gzip_test.h" -#include "buffer_test.h" -#include "memchunk_test.h" -#include "shrpx_config.h" - -static int init_suite1(void) { return 0; } - -static int clean_suite1(void) { return 0; } - -int main(int argc, char *argv[]) { - CU_pSuite pSuite = NULL; - unsigned int num_tests_failed; - - OpenSSL_add_all_algorithms(); - SSL_load_error_strings(); - SSL_library_init(); - - shrpx::create_config(); - - // initialize the CUnit test registry - if (CUE_SUCCESS != CU_initialize_registry()) - return CU_get_error(); - - // add a suite to the registry - pSuite = CU_add_suite("shrpx_TestSuite", init_suite1, clean_suite1); - if (NULL == pSuite) { - CU_cleanup_registry(); - return CU_get_error(); - } - - // add the tests to the suite - if (!CU_add_test(pSuite, "ssl_create_lookup_tree", - shrpx::test_shrpx_ssl_create_lookup_tree) || - !CU_add_test(pSuite, "ssl_cert_lookup_tree_add_cert_from_file", - shrpx::test_shrpx_ssl_cert_lookup_tree_add_cert_from_file) || - !CU_add_test(pSuite, "http2_add_header", shrpx::test_http2_add_header) || - !CU_add_test(pSuite, "http2_get_header", shrpx::test_http2_get_header) || - !CU_add_test(pSuite, "http2_copy_headers_to_nva", - shrpx::test_http2_copy_headers_to_nva) || - !CU_add_test(pSuite, "http2_build_http1_headers_from_headers", - shrpx::test_http2_build_http1_headers_from_headers) || - !CU_add_test(pSuite, "http2_lws", shrpx::test_http2_lws) || - !CU_add_test(pSuite, "http2_rewrite_location_uri", - shrpx::test_http2_rewrite_location_uri) || - !CU_add_test(pSuite, "http2_parse_http_status_code", - shrpx::test_http2_parse_http_status_code) || - !CU_add_test(pSuite, "http2_index_header", - shrpx::test_http2_index_header) || - !CU_add_test(pSuite, "http2_lookup_token", - shrpx::test_http2_lookup_token) || - !CU_add_test(pSuite, "http2_check_http2_pseudo_header", - shrpx::test_http2_check_http2_pseudo_header) || - !CU_add_test(pSuite, "http2_http2_header_allowed", - shrpx::test_http2_http2_header_allowed) || - !CU_add_test(pSuite, "http2_mandatory_request_headers_presence", - shrpx::test_http2_mandatory_request_headers_presence) || - !CU_add_test(pSuite, "http2_parse_link_header", - shrpx::test_http2_parse_link_header) || - !CU_add_test(pSuite, "http2_path_join", shrpx::test_http2_path_join) || - !CU_add_test(pSuite, "downstream_index_request_headers", - shrpx::test_downstream_index_request_headers) || - !CU_add_test(pSuite, "downstream_index_response_headers", - shrpx::test_downstream_index_response_headers) || - !CU_add_test(pSuite, "downstream_get_request_header", - shrpx::test_downstream_get_request_header) || - !CU_add_test(pSuite, "downstream_get_response_header", - shrpx::test_downstream_get_response_header) || - !CU_add_test(pSuite, "downstream_crumble_request_cookie", - shrpx::test_downstream_crumble_request_cookie) || - !CU_add_test(pSuite, "downstream_assemble_request_cookie", - shrpx::test_downstream_assemble_request_cookie) || - !CU_add_test(pSuite, "downstream_rewrite_location_response_header", - shrpx::test_downstream_rewrite_location_response_header) || - !CU_add_test(pSuite, "config_parse_config_str_list", - shrpx::test_shrpx_config_parse_config_str_list) || - !CU_add_test(pSuite, "config_parse_header", - shrpx::test_shrpx_config_parse_header) || - !CU_add_test(pSuite, "config_parse_log_format", - shrpx::test_shrpx_config_parse_log_format) || - !CU_add_test(pSuite, "config_read_tls_ticket_key_file", - shrpx::test_shrpx_config_read_tls_ticket_key_file) || - !CU_add_test(pSuite, "util_streq", shrpx::test_util_streq) || - !CU_add_test(pSuite, "util_strieq", shrpx::test_util_strieq) || - !CU_add_test(pSuite, "util_inp_strlower", - shrpx::test_util_inp_strlower) || - !CU_add_test(pSuite, "util_to_base64", shrpx::test_util_to_base64) || - !CU_add_test(pSuite, "util_to_token68", shrpx::test_util_to_token68) || - !CU_add_test(pSuite, "util_percent_encode_token", - shrpx::test_util_percent_encode_token) || - !CU_add_test(pSuite, "util_percent_encode_path", - shrpx::test_util_percent_encode_path) || - !CU_add_test(pSuite, "util_percent_decode", - shrpx::test_util_percent_decode) || - !CU_add_test(pSuite, "util_quote_string", - shrpx::test_util_quote_string) || - !CU_add_test(pSuite, "util_utox", shrpx::test_util_utox) || - !CU_add_test(pSuite, "util_http_date", shrpx::test_util_http_date) || - !CU_add_test(pSuite, "util_select_h2", shrpx::test_util_select_h2) || - !CU_add_test(pSuite, "util_ipv6_numeric_addr", - shrpx::test_util_ipv6_numeric_addr) || - !CU_add_test(pSuite, "util_utos_with_unit", - shrpx::test_util_utos_with_unit) || - !CU_add_test(pSuite, "util_utos_with_funit", - shrpx::test_util_utos_with_funit) || - !CU_add_test(pSuite, "util_parse_uint_with_unit", - shrpx::test_util_parse_uint_with_unit) || - !CU_add_test(pSuite, "util_parse_uint", shrpx::test_util_parse_uint) || - !CU_add_test(pSuite, "util_parse_duration_with_unit", - shrpx::test_util_parse_duration_with_unit) || - !CU_add_test(pSuite, "util_duration_str", - shrpx::test_util_duration_str) || - !CU_add_test(pSuite, "util_format_duration", - shrpx::test_util_format_duration) || - !CU_add_test(pSuite, "util_starts_with", shrpx::test_util_starts_with) || - !CU_add_test(pSuite, "util_ends_with", shrpx::test_util_ends_with) || - !CU_add_test(pSuite, "gzip_inflate", test_nghttp2_gzip_inflate) || - !CU_add_test(pSuite, "buffer_write", nghttp2::test_buffer_write) || - !CU_add_test(pSuite, "pool_recycle", nghttp2::test_pool_recycle) || - !CU_add_test(pSuite, "memchunk_append", nghttp2::test_memchunks_append) || - !CU_add_test(pSuite, "memchunk_drain", nghttp2::test_memchunks_drain) || - !CU_add_test(pSuite, "memchunk_riovec", nghttp2::test_memchunks_riovec) || - !CU_add_test(pSuite, "memchunk_recycle", - nghttp2::test_memchunks_recycle)) { - CU_cleanup_registry(); - return CU_get_error(); - } - - // Run all tests using the CUnit Basic interface - CU_basic_set_mode(CU_BRM_VERBOSE); - CU_basic_run_tests(); - num_tests_failed = CU_get_number_of_tests_failed(); - CU_cleanup_registry(); - if (CU_get_error() == CUE_SUCCESS) { - return num_tests_failed; - } else { - printf("CUnit Error: %s\n", CU_get_error_msg()); - return CU_get_error(); - } -} diff --git a/src/shrpx.cc b/src/shrpx.cc deleted file mode 100644 index 5becb79..0000000 --- a/src/shrpx.cc +++ /dev/null @@ -1,2165 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2012 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#include "shrpx.h" - -#include -#include -#ifdef HAVE_SYS_SOCKET_H -#include -#endif // HAVE_SYS_SOCKET_H -#include -#ifdef HAVE_NETDB_H -#include -#endif // HAVE_NETDB_H -#include -#ifdef HAVE_NETINET_IN_H -#include -#endif // HAVE_NETINET_IN_H -#include -#ifdef HAVE_ARPA_INET_H -#include -#endif // HAVE_ARPA_INET_H -#ifdef HAVE_UNISTD_H -#include -#endif // HAVE_UNISTD_H -#include -#ifdef HAVE_SYSLOG_H -#include -#endif // HAVE_SYSLOG_H -#include -#ifdef HAVE_LIMITS_H -#include -#endif // HAVE_LIMITS_H -#ifdef HAVE_SYS_TIME_H -#include -#endif // HAVE_SYS_TIME_H -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include - -#include - -#include "shrpx_config.h" -#include "shrpx_connection_handler.h" -#include "shrpx_ssl.h" -#include "shrpx_log_config.h" -#include "shrpx_worker.h" -#include "shrpx_accept_handler.h" -#include "shrpx_http2_upstream.h" -#include "shrpx_http2_session.h" -#include "util.h" -#include "app_helper.h" -#include "ssl.h" -#include "template.h" - -extern char **environ; - -using namespace nghttp2; - -namespace shrpx { - -namespace { -const int REOPEN_LOG_SIGNAL = SIGUSR1; -const int EXEC_BINARY_SIGNAL = SIGUSR2; -const int GRACEFUL_SHUTDOWN_SIGNAL = SIGQUIT; -} // namespace - -// Environment variables to tell new binary the listening socket's -// file descriptors. They are not close-on-exec. -#define ENV_LISTENER4_FD "NGHTTPX_LISTENER4_FD" -#define ENV_LISTENER6_FD "NGHTTPX_LISTENER6_FD" - -// Environment variable to tell new binary the port number the current -// binary is listening to. -#define ENV_PORT "NGHTTPX_PORT" - -// Environment variable to tell new binary the listening socket's file -// descriptor if frontend listens UNIX domain socket. -#define ENV_UNIX_FD "NGHTTP2_UNIX_FD" -// Environment variable to tell new binary the UNIX domain socket -// path. -#define ENV_UNIX_PATH "NGHTTP2_UNIX_PATH" - -namespace { -int resolve_hostname(sockaddr_union *addr, size_t *addrlen, - const char *hostname, uint16_t port, int family) { - addrinfo hints; - int rv; - - auto service = util::utos(port); - memset(&hints, 0, sizeof(addrinfo)); - - hints.ai_family = family; - hints.ai_socktype = SOCK_STREAM; -#ifdef AI_ADDRCONFIG - hints.ai_flags |= AI_ADDRCONFIG; -#endif // AI_ADDRCONFIG - addrinfo *res; - - rv = getaddrinfo(hostname, service.c_str(), &hints, &res); - if (rv != 0) { - LOG(FATAL) << "Unable to resolve address for " << hostname << ": " - << gai_strerror(rv); - return -1; - } - - char host[NI_MAXHOST]; - rv = getnameinfo(res->ai_addr, res->ai_addrlen, host, sizeof(host), 0, 0, - NI_NUMERICHOST); - if (rv != 0) { - LOG(FATAL) << "Address resolution for " << hostname - << " failed: " << gai_strerror(rv); - - freeaddrinfo(res); - - return -1; - } - - if (LOG_ENABLED(INFO)) { - LOG(INFO) << "Address resolution for " << hostname - << " succeeded: " << host; - } - - memcpy(addr, res->ai_addr, res->ai_addrlen); - *addrlen = res->ai_addrlen; - freeaddrinfo(res); - return 0; -} -} // namespace - -namespace { -void close_env_fd(std::initializer_list envnames) { - for (auto envname : envnames) { - auto envfd = getenv(envname); - if (!envfd) { - continue; - } - auto fd = strtol(envfd, nullptr, 10); - close(fd); - } -} -} // namespace - -namespace { -std::unique_ptr -create_unix_domain_acceptor(ConnectionHandler *handler) { - auto path = get_config()->host.get(); - auto pathlen = strlen(path); - { - auto envfd = getenv(ENV_UNIX_FD); - auto envpath = getenv(ENV_UNIX_PATH); - if (envfd && envpath) { - auto fd = strtoul(envfd, nullptr, 10); - - if (util::streq(envpath, path)) { - LOG(NOTICE) << "Listening on UNIX domain socket " << path; - - return make_unique(fd, handler); - } - - LOG(WARN) << "UNIX domain socket path was changed between old binary (" - << envpath << ") and new binary (" << path << ")"; - close(fd); - } - } - -#ifdef SOCK_NONBLOCK - auto fd = socket(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK, 0); - if (fd == -1) { - return nullptr; - } -#else // !SOCK_NONBLOCK - auto fd = socket(AF_UNIX, SOCK_STREAM, 0); - if (fd == -1) { - return nullptr; - } - util::make_socket_nonblocking(fd); -#endif // !SOCK_NONBLOCK - int val = 1; - if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &val, - static_cast(sizeof(val))) == -1) { - close(fd); - return nullptr; - } - - sockaddr_union addr; - addr.un.sun_family = AF_UNIX; - if (pathlen + 1 > sizeof(addr.un.sun_path)) { - LOG(FATAL) << "UNIX domain socket path " << path << " is too long > " - << sizeof(addr.un.sun_path); - close(fd); - return nullptr; - } - // copy path including terminal NULL - std::copy_n(path, pathlen + 1, addr.un.sun_path); - - // unlink (remove) already existing UNIX domain socket path - unlink(path); - - if (bind(fd, &addr.sa, sizeof(addr.un)) != 0) { - auto error = errno; - LOG(FATAL) << "Failed to bind UNIX domain socket, error=" << error; - close(fd); - return nullptr; - } - - if (listen(fd, get_config()->backlog) != 0) { - auto error = errno; - LOG(FATAL) << "Failed to listen to UNIX domain socket, error=" << error; - close(fd); - return nullptr; - } - - LOG(NOTICE) << "Listening on UNIX domain socket " << path; - - return make_unique(fd, handler); -} -} // namespace - -namespace { -std::unique_ptr create_acceptor(ConnectionHandler *handler, - int family) { - { - auto envfd = - getenv(family == AF_INET ? ENV_LISTENER4_FD : ENV_LISTENER6_FD); - auto envport = getenv(ENV_PORT); - - if (envfd && envport) { - auto fd = strtoul(envfd, nullptr, 10); - auto port = strtoul(envport, nullptr, 10); - - // Only do this iff NGHTTPX_PORT == get_config()->port. - // Otherwise, close fd, and create server socket as usual. - - if (port == get_config()->port) { - LOG(NOTICE) << "Listening on port " << get_config()->port; - - return make_unique(fd, handler); - } - - LOG(WARN) << "Port was changed between old binary (" << port - << ") and new binary (" << get_config()->port << ")"; - close(fd); - } - } - - addrinfo hints; - int fd = -1; - int rv; - - auto service = util::utos(get_config()->port); - memset(&hints, 0, sizeof(addrinfo)); - hints.ai_family = family; - hints.ai_socktype = SOCK_STREAM; - hints.ai_flags = AI_PASSIVE; -#ifdef AI_ADDRCONFIG - hints.ai_flags |= AI_ADDRCONFIG; -#endif // AI_ADDRCONFIG - - auto node = strcmp("*", get_config()->host.get()) == 0 - ? nullptr - : get_config()->host.get(); - - addrinfo *res, *rp; - rv = getaddrinfo(node, service.c_str(), &hints, &res); - if (rv != 0) { - if (LOG_ENABLED(INFO)) { - LOG(INFO) << "Unable to get IPv" << (family == AF_INET ? "4" : "6") - << " address for " << get_config()->host.get() << ": " - << gai_strerror(rv); - } - return nullptr; - } - for (rp = res; rp; rp = rp->ai_next) { -#ifdef SOCK_NONBLOCK - fd = - socket(rp->ai_family, rp->ai_socktype | SOCK_NONBLOCK, rp->ai_protocol); - if (fd == -1) { - continue; - } -#else // !SOCK_NONBLOCK - fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); - if (fd == -1) { - continue; - } - util::make_socket_nonblocking(fd); -#endif // !SOCK_NONBLOCK - int val = 1; - if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &val, - static_cast(sizeof(val))) == -1) { - close(fd); - continue; - } - -#ifdef IPV6_V6ONLY - if (family == AF_INET6) { - if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &val, - static_cast(sizeof(val))) == -1) { - close(fd); - continue; - } - } -#endif // IPV6_V6ONLY - -#ifdef TCP_DEFER_ACCEPT - val = 3; - if (setsockopt(fd, IPPROTO_TCP, TCP_DEFER_ACCEPT, &val, - static_cast(sizeof(val))) == -1) { - LOG(WARN) << "Failed to set TCP_DEFER_ACCEPT option to listener socket"; - } -#endif // TCP_DEFER_ACCEPT - - if (bind(fd, rp->ai_addr, rp->ai_addrlen) == 0 && - listen(fd, get_config()->backlog) == 0) { - break; - } - close(fd); - } - - if (!rp) { - LOG(WARN) << "Listening " << (family == AF_INET ? "IPv4" : "IPv6") - << " socket failed"; - - freeaddrinfo(res); - - return nullptr; - } - - char host[NI_MAXHOST]; - rv = getnameinfo(rp->ai_addr, rp->ai_addrlen, host, sizeof(host), nullptr, 0, - NI_NUMERICHOST); - - freeaddrinfo(res); - - if (rv != 0) { - LOG(WARN) << gai_strerror(rv); - - close(fd); - - return nullptr; - } - - LOG(NOTICE) << "Listening on " << host << ", port " << get_config()->port; - - return make_unique(fd, handler); -} -} // namespace - -namespace { -void drop_privileges() { - if (getuid() == 0 && get_config()->uid != 0) { - if (initgroups(get_config()->user.get(), get_config()->gid) != 0) { - auto error = errno; - LOG(FATAL) << "Could not change supplementary groups: " - << strerror(error); - exit(EXIT_FAILURE); - } - if (setgid(get_config()->gid) != 0) { - auto error = errno; - LOG(FATAL) << "Could not change gid: " << strerror(error); - exit(EXIT_FAILURE); - } - if (setuid(get_config()->uid) != 0) { - auto error = errno; - LOG(FATAL) << "Could not change uid: " << strerror(error); - exit(EXIT_FAILURE); - } - if (setuid(0) != -1) { - LOG(FATAL) << "Still have root privileges?"; - exit(EXIT_FAILURE); - } - } -} -} // namespace - -namespace { -void save_pid() { - std::ofstream out(get_config()->pid_file.get(), std::ios::binary); - out << get_config()->pid << "\n"; - out.close(); - if (!out) { - LOG(ERROR) << "Could not save PID to file " << get_config()->pid_file.get(); - exit(EXIT_FAILURE); - } - - if (get_config()->uid != 0) { - if (chown(get_config()->pid_file.get(), get_config()->uid, - get_config()->gid) == -1) { - auto error = errno; - LOG(WARN) << "Changing owner of pid file " << get_config()->pid_file.get() - << " failed: " << strerror(error); - } - } -} -} // namespace - -namespace { -void reopen_log_signal_cb(struct ev_loop *loop, ev_signal *w, int revents) { - auto conn_handler = static_cast(w->data); - - LOG(NOTICE) << "Reopening log files: main"; - - (void)reopen_log_files(); - redirect_stderr_to_errorlog(); - - if (get_config()->num_worker > 1) { - conn_handler->worker_reopen_log_files(); - } -} -} // namespace - -namespace { -void exec_binary_signal_cb(struct ev_loop *loop, ev_signal *w, int revents) { - auto conn_handler = static_cast(w->data); - - LOG(NOTICE) << "Executing new binary"; - - auto pid = fork(); - - if (pid == -1) { - auto error = errno; - LOG(ERROR) << "fork() failed errno=" << error; - return; - } - - if (pid != 0) { - return; - } - - auto exec_path = util::get_exec_path(get_config()->argc, get_config()->argv, - get_config()->cwd); - - if (!exec_path) { - LOG(ERROR) << "Could not resolve the executable path"; - return; - } - - auto argv = make_unique(get_config()->argc + 1); - - argv[0] = exec_path; - for (int i = 1; i < get_config()->argc; ++i) { - argv[i] = strdup(get_config()->argv[i]); - } - argv[get_config()->argc] = nullptr; - - size_t envlen = 0; - for (char **p = environ; *p; ++p, ++envlen) - ; - // 3 for missing (fd4, fd6 and port) or (unix fd and unix path) - auto envp = make_unique(envlen + 3 + 1); - size_t envidx = 0; - - if (get_config()->host_unix) { - auto acceptor = conn_handler->get_acceptor(); - std::string fd = ENV_UNIX_FD "="; - fd += util::utos(acceptor->get_fd()); - envp[envidx++] = strdup(fd.c_str()); - - std::string path = ENV_UNIX_PATH "="; - path += get_config()->host.get(); - envp[envidx++] = strdup(path.c_str()); - } else { - auto acceptor4 = conn_handler->get_acceptor(); - if (acceptor4) { - std::string fd4 = ENV_LISTENER4_FD "="; - fd4 += util::utos(acceptor4->get_fd()); - envp[envidx++] = strdup(fd4.c_str()); - } - - auto acceptor6 = conn_handler->get_acceptor6(); - if (acceptor6) { - std::string fd6 = ENV_LISTENER6_FD "="; - fd6 += util::utos(acceptor6->get_fd()); - envp[envidx++] = strdup(fd6.c_str()); - } - - std::string port = ENV_PORT "="; - port += util::utos(get_config()->port); - envp[envidx++] = strdup(port.c_str()); - } - - for (size_t i = 0; i < envlen; ++i) { - if (util::startsWith(environ[i], ENV_LISTENER4_FD) || - util::startsWith(environ[i], ENV_LISTENER6_FD) || - util::startsWith(environ[i], ENV_PORT) || - util::startsWith(environ[i], ENV_UNIX_FD) || - util::startsWith(environ[i], ENV_UNIX_PATH)) { - continue; - } - - envp[envidx++] = environ[i]; - } - - envp[envidx++] = nullptr; - - if (LOG_ENABLED(INFO)) { - LOG(INFO) << "cmdline"; - for (int i = 0; argv[i]; ++i) { - LOG(INFO) << i << ": " << argv[i]; - } - LOG(INFO) << "environ"; - for (int i = 0; envp[i]; ++i) { - LOG(INFO) << i << ": " << envp[i]; - } - } - - if (execve(argv[0], argv.get(), envp.get()) == -1) { - auto error = errno; - LOG(ERROR) << "execve failed: errno=" << error; - _Exit(EXIT_FAILURE); - } -} -} // namespace - -namespace { -void graceful_shutdown_signal_cb(struct ev_loop *loop, ev_signal *w, - int revents) { - auto conn_handler = static_cast(w->data); - - if (conn_handler->get_graceful_shutdown()) { - return; - } - - LOG(NOTICE) << "Graceful shutdown signal received"; - - conn_handler->set_graceful_shutdown(true); - - conn_handler->disable_acceptor(); - - // After disabling accepting new connection, disptach incoming - // connection in backlog. - - conn_handler->accept_pending_connection(); - - conn_handler->graceful_shutdown_worker(); - - if (get_config()->num_worker == 1 && - conn_handler->get_single_worker()->get_worker_stat()->num_connections > - 0) { - return; - } - - // We have accepted all pending connections. Shutdown main event - // loop. - ev_break(loop); -} -} // namespace - -namespace { -void renew_ticket_key_cb(struct ev_loop *loop, ev_timer *w, int revents) { - auto conn_handler = static_cast(w->data); - const auto &old_ticket_keys = conn_handler->get_ticket_keys(); - - auto ticket_keys = std::make_shared(); - LOG(NOTICE) << "Renew ticket keys: main"; - - // We store at most 2 ticket keys - if (old_ticket_keys) { - auto &old_keys = old_ticket_keys->keys; - auto &new_keys = ticket_keys->keys; - - assert(!old_keys.empty()); - - new_keys.resize(2); - new_keys[1] = old_keys[0]; - } else { - ticket_keys->keys.resize(1); - } - - if (RAND_bytes(reinterpret_cast(&ticket_keys->keys[0]), - sizeof(ticket_keys->keys[0])) == 0) { - if (LOG_ENABLED(INFO)) { - LOG(INFO) << "failed to renew ticket key"; - } - return; - } - - if (LOG_ENABLED(INFO)) { - LOG(INFO) << "ticket keys generation done"; - for (auto &key : ticket_keys->keys) { - LOG(INFO) << "name: " << util::format_hex(key.name, sizeof(key.name)); - } - } - - conn_handler->set_ticket_keys(ticket_keys); - conn_handler->worker_renew_ticket_keys(ticket_keys); -} -} // namespace - -namespace { -int event_loop() { - auto loop = EV_DEFAULT; - - auto conn_handler = make_unique(loop); - if (get_config()->daemon) { - if (daemon(0, 0) == -1) { - auto error = errno; - LOG(FATAL) << "Failed to daemonize: " << strerror(error); - exit(EXIT_FAILURE); - } - - // We get new PID after successful daemon(). - mod_config()->pid = getpid(); - - // daemon redirects stderr file descriptor to /dev/null, so we - // need this. - redirect_stderr_to_errorlog(); - } - - if (get_config()->pid_file) { - save_pid(); - } - - if (get_config()->host_unix) { - close_env_fd({ENV_LISTENER4_FD, ENV_LISTENER6_FD}); - auto acceptor = create_unix_domain_acceptor(conn_handler.get()); - if (!acceptor) { - LOG(FATAL) << "Failed to listen on UNIX domain socket " - << get_config()->host.get(); - exit(EXIT_FAILURE); - } - - conn_handler->set_acceptor(std::move(acceptor)); - } else { - close_env_fd({ENV_UNIX_FD}); - auto acceptor6 = create_acceptor(conn_handler.get(), AF_INET6); - auto acceptor4 = create_acceptor(conn_handler.get(), AF_INET); - if (!acceptor6 && !acceptor4) { - LOG(FATAL) << "Failed to listen on address " << get_config()->host.get() - << ", port " << get_config()->port; - exit(EXIT_FAILURE); - } - - conn_handler->set_acceptor(std::move(acceptor4)); - conn_handler->set_acceptor6(std::move(acceptor6)); - } - - ev_timer renew_ticket_key_timer; - if (!get_config()->upstream_no_tls) { - bool auto_tls_ticket_key = true; - if (!get_config()->tls_ticket_key_files.empty()) { - auto ticket_keys = - read_tls_ticket_key_file(get_config()->tls_ticket_key_files); - if (!ticket_keys) { - LOG(WARN) << "Use internal session ticket key generator"; - } else { - conn_handler->set_ticket_keys(std::move(ticket_keys)); - auto_tls_ticket_key = false; - } - } - if (auto_tls_ticket_key) { - // Renew ticket key every 12hrs - ev_timer_init(&renew_ticket_key_timer, renew_ticket_key_cb, 0., - 12 * 3600.); - renew_ticket_key_timer.data = conn_handler.get(); - ev_timer_again(loop, &renew_ticket_key_timer); - - // Generate first session ticket key before running workers. - renew_ticket_key_cb(loop, &renew_ticket_key_timer, 0); - } - } - - // ListenHandler loads private key, and we listen on a priveleged port. - // After that, we drop the root privileges if needed. - drop_privileges(); - -#ifndef NOTHREADS - int rv; - sigset_t signals; - sigemptyset(&signals); - sigaddset(&signals, REOPEN_LOG_SIGNAL); - sigaddset(&signals, EXEC_BINARY_SIGNAL); - sigaddset(&signals, GRACEFUL_SHUTDOWN_SIGNAL); - rv = pthread_sigmask(SIG_BLOCK, &signals, nullptr); - if (rv != 0) { - LOG(ERROR) << "Blocking signals failed: " << strerror(rv); - } -#endif // !NOTHREADS - - if (get_config()->num_worker == 1) { - conn_handler->create_single_worker(); - } else { - conn_handler->create_worker_thread(get_config()->num_worker); - } - -#ifndef NOTHREADS - rv = pthread_sigmask(SIG_UNBLOCK, &signals, nullptr); - if (rv != 0) { - LOG(ERROR) << "Unblocking signals failed: " << strerror(rv); - } -#endif // !NOTHREADS - - ev_signal reopen_log_sig; - ev_signal_init(&reopen_log_sig, reopen_log_signal_cb, REOPEN_LOG_SIGNAL); - reopen_log_sig.data = conn_handler.get(); - ev_signal_start(loop, &reopen_log_sig); - - ev_signal exec_bin_sig; - ev_signal_init(&exec_bin_sig, exec_binary_signal_cb, EXEC_BINARY_SIGNAL); - exec_bin_sig.data = conn_handler.get(); - ev_signal_start(loop, &exec_bin_sig); - - ev_signal graceful_shutdown_sig; - ev_signal_init(&graceful_shutdown_sig, graceful_shutdown_signal_cb, - GRACEFUL_SHUTDOWN_SIGNAL); - graceful_shutdown_sig.data = conn_handler.get(); - ev_signal_start(loop, &graceful_shutdown_sig); - - if (!get_config()->upstream_no_tls && !get_config()->no_ocsp) { - conn_handler->proceed_next_cert_ocsp(); - } - - if (LOG_ENABLED(INFO)) { - LOG(INFO) << "Entering event loop"; - } - - ev_run(loop, 0); - - conn_handler->join_worker(); - conn_handler->cancel_ocsp_update(); - - return 0; -} -} // namespace - -namespace { -// Returns true if regular file or symbolic link |path| exists. -bool conf_exists(const char *path) { - struct stat buf; - int rv = stat(path, &buf); - return rv == 0 && (buf.st_mode & (S_IFREG | S_IFLNK)); -} -} // namespace - -namespace { -const char *DEFAULT_NPN_LIST = "h2,h2-16,h2-14," -#ifdef HAVE_SPDYLAY - "spdy/3.1," -#endif // HAVE_SPDYLAY - "http/1.1"; -} // namespace - -namespace { -const char *DEFAULT_TLS_PROTO_LIST = "TLSv1.2,TLSv1.1"; -} // namespace - -namespace { -const char *DEFAULT_ACCESSLOG_FORMAT = "$remote_addr - - [$time_local] " - "\"$request\" $status $body_bytes_sent " - "\"$http_referer\" \"$http_user_agent\""; -} // namespace - -namespace { -auto DEFAULT_DOWNSTREAM_HOST = "127.0.0.1"; -int16_t DEFAULT_DOWNSTREAM_PORT = 80; -} // namespace; - -namespace { -void fill_default_config() { - memset(mod_config(), 0, sizeof(*mod_config())); - - mod_config()->verbose = false; - mod_config()->daemon = false; - - mod_config()->server_name = "nghttpx nghttp2/" NGHTTP2_VERSION; - mod_config()->host = strcopy("*"); - mod_config()->port = 3000; - mod_config()->private_key_file = nullptr; - mod_config()->private_key_passwd = nullptr; - mod_config()->cert_file = nullptr; - - // Read timeout for HTTP2 upstream connection - mod_config()->http2_upstream_read_timeout = 180.; - - // Read timeout for non-HTTP2 upstream connection - mod_config()->upstream_read_timeout = 180.; - - // Write timeout for HTTP2/non-HTTP2 upstream connection - mod_config()->upstream_write_timeout = 30.; - - // Read/Write timeouts for downstream connection - mod_config()->downstream_read_timeout = 180.; - mod_config()->downstream_write_timeout = 30.; - - // Read timeout for HTTP/2 stream - mod_config()->stream_read_timeout = 0.; - - // Write timeout for HTTP/2 stream - mod_config()->stream_write_timeout = 0.; - - // Timeout for pooled (idle) connections - mod_config()->downstream_idle_read_timeout = 2.; - - // window bits for HTTP/2 and SPDY upstream/downstream connection - // per stream. 2**16-1 = 64KiB-1, which is HTTP/2 default. Please - // note that SPDY/3 default is 64KiB. - mod_config()->http2_upstream_window_bits = 16; - mod_config()->http2_downstream_window_bits = 16; - - // HTTP/2 SPDY/3.1 has connection-level flow control. The default - // window size for HTTP/2 is 64KiB - 1. SPDY/3's default is 64KiB - mod_config()->http2_upstream_connection_window_bits = 16; - mod_config()->http2_downstream_connection_window_bits = 16; - - mod_config()->upstream_no_tls = false; - mod_config()->downstream_no_tls = false; - - mod_config()->num_worker = 1; - mod_config()->http2_max_concurrent_streams = 100; - mod_config()->add_x_forwarded_for = false; - mod_config()->strip_incoming_x_forwarded_for = false; - mod_config()->no_via = false; - mod_config()->accesslog_file = nullptr; - mod_config()->accesslog_syslog = false; - mod_config()->accesslog_format = parse_log_format(DEFAULT_ACCESSLOG_FORMAT); -#if defined(__ANDROID__) || defined(ANDROID) - // Android does not have /dev/stderr. Use /proc/self/fd/2 instead. - mod_config()->errorlog_file = strcopy("/proc/self/fd/2"); -#else // !__ANDROID__ && ANDROID - mod_config()->errorlog_file = strcopy("/dev/stderr"); -#endif // !__ANDROID__ && ANDROID - mod_config()->errorlog_syslog = false; - mod_config()->conf_path = strcopy("/etc/nghttpx/nghttpx.conf"); - mod_config()->syslog_facility = LOG_DAEMON; - // Default accept() backlog - mod_config()->backlog = 512; - mod_config()->ciphers = nullptr; - mod_config()->http2_proxy = false; - mod_config()->http2_bridge = false; - mod_config()->client_proxy = false; - mod_config()->client = false; - mod_config()->client_mode = false; - mod_config()->insecure = false; - mod_config()->cacert = nullptr; - mod_config()->pid_file = nullptr; - mod_config()->user = nullptr; - mod_config()->uid = 0; - mod_config()->gid = 0; - mod_config()->pid = getpid(); - mod_config()->backend_ipv4 = false; - mod_config()->backend_ipv6 = false; - mod_config()->downstream_http_proxy_userinfo = nullptr; - mod_config()->downstream_http_proxy_host = nullptr; - mod_config()->downstream_http_proxy_port = 0; - mod_config()->downstream_http_proxy_addrlen = 0; - mod_config()->read_rate = 0; - mod_config()->read_burst = 0; - mod_config()->write_rate = 0; - mod_config()->write_burst = 0; - mod_config()->worker_read_rate = 0; - mod_config()->worker_read_burst = 0; - mod_config()->worker_write_rate = 0; - mod_config()->worker_write_burst = 0; - mod_config()->verify_client = false; - mod_config()->verify_client_cacert = nullptr; - mod_config()->client_private_key_file = nullptr; - mod_config()->client_cert_file = nullptr; - mod_config()->http2_upstream_dump_request_header = nullptr; - mod_config()->http2_upstream_dump_response_header = nullptr; - mod_config()->http2_no_cookie_crumbling = false; - mod_config()->upstream_frame_debug = false; - mod_config()->padding = 0; - mod_config()->worker_frontend_connections = 0; - - mod_config()->http2_upstream_callbacks = create_http2_upstream_callbacks(); - mod_config()->http2_downstream_callbacks = - create_http2_downstream_callbacks(); - - nghttp2_option_new(&mod_config()->http2_option); - nghttp2_option_set_no_auto_window_update(get_config()->http2_option, 1); - nghttp2_option_set_no_recv_client_magic(get_config()->http2_option, 1); - - nghttp2_option_new(&mod_config()->http2_client_option); - nghttp2_option_set_no_auto_window_update(get_config()->http2_client_option, - 1); - nghttp2_option_set_peer_max_concurrent_streams( - get_config()->http2_client_option, 100); - - mod_config()->tls_proto_mask = 0; - mod_config()->no_location_rewrite = false; - mod_config()->no_host_rewrite = false; - mod_config()->argc = 0; - mod_config()->argv = nullptr; - mod_config()->downstream_connections_per_host = 8; - mod_config()->downstream_connections_per_frontend = 0; - mod_config()->listener_disable_timeout = 0.; - mod_config()->downstream_request_buffer_size = 16 * 1024; - mod_config()->downstream_response_buffer_size = 16 * 1024; - mod_config()->no_server_push = false; - mod_config()->host_unix = false; - mod_config()->http2_downstream_connections_per_worker = 0; - // ocsp update interval = 14400 secs = 4 hours, borrowed from h2o - mod_config()->ocsp_update_interval = 14400.; - mod_config()->fetch_ocsp_response_file = - strcopy(PKGDATADIR "/fetch-ocsp-response"); - mod_config()->no_ocsp = false; - mod_config()->header_field_buffer = 64 * 1024; - mod_config()->max_header_fields = 100; -} -} // namespace - -namespace { -void print_version(std::ostream &out) { - out << get_config()->server_name << std::endl; -} -} // namespace - -namespace { -void print_usage(std::ostream &out) { - out << R"(Usage: nghttpx [OPTIONS]... [ ] -A reverse proxy for HTTP/2, HTTP/1 and SPDY.)" << std::endl; -} -} // namespace - -namespace { -void print_help(std::ostream &out) { - print_usage(out); - out << R"( - - Set path to server's private key. Required unless -p, - --client or --frontend-no-tls are given. - Set path to server's certificate. Required unless -p, - --client or --frontend-no-tls are given. To make OCSP - stapling work, this must be absolute path. - -Options: - The options are categorized into several groups. - -Connections: - -b, --backend= - Set backend host and port. The multiple backend - addresses are accepted by repeating this option. UNIX - domain socket can be specified by prefixing path name - with "unix:" (e.g., unix:/var/run/backend.sock) - Default: )" << DEFAULT_DOWNSTREAM_HOST << "," - << DEFAULT_DOWNSTREAM_PORT << R"( - -f, --frontend= - Set frontend host and port. If is '*', it - assumes all addresses including both IPv4 and IPv6. - UNIX domain socket can be specified by prefixing path - name with "unix:" (e.g., unix:/var/run/nghttpx.sock) - Default: )" << get_config()->host.get() << "," - << get_config()->port << R"( - --backlog= - Set listen backlog size. - Default: )" << get_config()->backlog << R"( - --backend-ipv4 - Resolve backend hostname to IPv4 address only. - --backend-ipv6 - Resolve backend hostname to IPv6 address only. - --backend-http-proxy-uri= - Specify proxy URI in the form - http://[:@]:. If a proxy - requires authentication, specify and . - Note that they must be properly percent-encoded. This - proxy is used when the backend connection is HTTP/2. - First, make a CONNECT request to the proxy and it - connects to the backend on behalf of nghttpx. This - forms tunnel. After that, nghttpx performs SSL/TLS - handshake with the downstream through the tunnel. The - timeouts when connecting and making CONNECT request can - be specified by --backend-read-timeout and - --backend-write-timeout options. - -Performance: - -n, --workers= - Set the number of worker threads. - Default: )" << get_config()->num_worker << R"( - --read-rate= - Set maximum average read rate on frontend connection. - Setting 0 to this option means read rate is unlimited. - Default: )" << get_config()->read_rate << R"( - --read-burst= - Set maximum read burst size on frontend connection. - Setting 0 to this option means read burst size is - unlimited. - Default: )" << get_config()->read_burst << R"( - --write-rate= - Set maximum average write rate on frontend connection. - Setting 0 to this option means write rate is unlimited. - Default: )" << get_config()->write_rate << R"( - --write-burst= - Set maximum write burst size on frontend connection. - Setting 0 to this option means write burst size is - unlimited. - Default: )" << get_config()->write_burst << R"( - --worker-read-rate= - Set maximum average read rate on frontend connection per - worker. Setting 0 to this option means read rate is - unlimited. Not implemented yet. - Default: )" << get_config()->worker_read_rate << R"( - --worker-read-burst= - Set maximum read burst size on frontend connection per - worker. Setting 0 to this option means read burst size - is unlimited. Not implemented yet. - Default: )" << get_config()->worker_read_burst << R"( - --worker-write-rate= - Set maximum average write rate on frontend connection - per worker. Setting 0 to this option means write rate - is unlimited. Not implemented yet. - Default: )" << get_config()->worker_write_rate << R"( - --worker-write-burst= - Set maximum write burst size on frontend connection per - worker. Setting 0 to this option means write burst size - is unlimited. Not implemented yet. - Default: )" << get_config()->worker_write_burst << R"( - --worker-frontend-connections= - Set maximum number of simultaneous connections frontend - accepts. Setting 0 means unlimited. - Default: )" << get_config()->worker_frontend_connections << R"( - --backend-http2-connections-per-worker= - Set maximum number of HTTP/2 connections per worker. - The default value is 0, which means the number of - backend addresses specified by -b option. - --backend-http1-connections-per-host= - Set maximum number of backend concurrent HTTP/1 - connections per host. This option is meaningful when -s - option is used. To limit the number of connections per - frontend for default mode, use - --backend-http1-connections-per-frontend. - Default: )" << get_config()->downstream_connections_per_host - << R"( - --backend-http1-connections-per-frontend= - Set maximum number of backend concurrent HTTP/1 - connections per frontend. This option is only used for - default mode. 0 means unlimited. To limit the number - of connections per host for HTTP/2 or SPDY proxy mode - (-s option), use --backend-http1-connections-per-host. - Default: )" << get_config()->downstream_connections_per_frontend - << R"( - --rlimit-nofile= - Set maximum number of open files (RLIMIT_NOFILE) to . - If 0 is given, nghttpx does not set the limit. - Default: )" << get_config()->rlimit_nofile << R"( - --backend-request-buffer= - Set buffer size used to store backend request. - Default: )" - << util::utos_with_unit(get_config()->downstream_request_buffer_size) - << R"( - --backend-response-buffer= - Set buffer size used to store backend response. - Default: )" - << util::utos_with_unit(get_config()->downstream_response_buffer_size) - << R"( - -Timeout: - --frontend-http2-read-timeout= - Specify read timeout for HTTP/2 and SPDY frontend - connection. - Default: )" - << util::duration_str(get_config()->http2_upstream_read_timeout) << R"( - --frontend-read-timeout= - Specify read timeout for HTTP/1.1 frontend connection. - Default: )" - << util::duration_str(get_config()->upstream_read_timeout) << R"( - --frontend-write-timeout= - Specify write timeout for all frontend connections. - Default: )" - << util::duration_str(get_config()->upstream_write_timeout) << R"( - --stream-read-timeout= - Specify read timeout for HTTP/2 and SPDY streams. 0 - means no timeout. - Default: )" - << util::duration_str(get_config()->stream_read_timeout) << R"( - --stream-write-timeout= - Specify write timeout for HTTP/2 and SPDY streams. 0 - means no timeout. - Default: )" - << util::duration_str(get_config()->stream_write_timeout) << R"( - --backend-read-timeout= - Specify read timeout for backend connection. - Default: )" - << util::duration_str(get_config()->downstream_read_timeout) << R"( - --backend-write-timeout= - Specify write timeout for backend connection. - Default: )" - << util::duration_str(get_config()->downstream_write_timeout) << R"( - --backend-keep-alive-timeout= - Specify keep-alive timeout for backend connection. - Default: )" - << util::duration_str(get_config()->downstream_idle_read_timeout) << R"( - --listener-disable-timeout= - After accepting connection failed, connection listener - is disabled for a given amount of time. Specifying 0 - disables this feature. - Default: )" - << util::duration_str(get_config()->listener_disable_timeout) << R"( - -SSL/TLS: - --ciphers= - Set allowed cipher list. The format of the string is - described in OpenSSL ciphers(1). - -k, --insecure - Don't verify backend server's certificate if -p, - --client or --http2-bridge are given and - --backend-no-tls is not given. - --cacert= - Set path to trusted CA certificate file if -p, --client - or --http2-bridge are given and --backend-no-tls is not - given. The file must be in PEM format. It can contain - multiple certificates. If the linked OpenSSL is - configured to load system wide certificates, they are - loaded at startup regardless of this option. - --private-key-passwd-file= - Path to file that contains password for the server's - private key. If none is given and the private key is - password protected it'll be requested interactively. - --subcert=: - Specify additional certificate and private key file. - nghttpx will choose certificates based on the hostname - indicated by client using TLS SNI extension. This - option can be used multiple times. To make OCSP - stapling work, must be absolute path. - --backend-tls-sni-field= - Explicitly set the content of the TLS SNI extension. - This will default to the backend HOST name. - --dh-param-file= - Path to file that contains DH parameters in PEM format. - Without this option, DHE cipher suites are not - available. - --npn-list= - Comma delimited list of ALPN protocol identifier sorted - in the order of preference. That means most desirable - protocol comes first. This is used in both ALPN and - NPN. The parameter must be delimited by a single comma - only and any white spaces are treated as a part of - protocol string. - Default: )" << DEFAULT_NPN_LIST << R"( - --verify-client - Require and verify client certificate. - --verify-client-cacert= - Path to file that contains CA certificates to verify - client certificate. The file must be in PEM format. It - can contain multiple certificates. - --client-private-key-file= - Path to file that contains client private key used in - backend client authentication. - --client-cert-file= - Path to file that contains client certificate used in - backend client authentication. - --tls-proto-list= - Comma delimited list of SSL/TLS protocol to be enabled. - The following protocols are available: TLSv1.2, TLSv1.1 - and TLSv1.0. The name matching is done in - case-insensitive manner. The parameter must be - delimited by a single comma only and any white spaces - are treated as a part of protocol string. - Default: )" << DEFAULT_TLS_PROTO_LIST << R"( - --tls-ticket-key-file= - Path to file that contains 48 bytes random data to - construct TLS session ticket parameters. This options - can be used repeatedly to specify multiple ticket - parameters. If several files are given, only the first - key is used to encrypt TLS session tickets. Other keys - are accepted but server will issue new session ticket - with first key. This allows session key rotation. - Please note that key rotation does not occur - automatically. User should rearrange files or change - options values and restart nghttpx gracefully. If - opening or reading given file fails, all loaded keys are - discarded and it is treated as if none of this option is - given. If this option is not given or an error occurred - while opening or reading a file, key is generated - automatically and renewed every 12hrs. At most 2 keys - are stored in memory. - --fetch-ocsp-response-file= - Path to fetch-ocsp-response script file. It should be - absolute path. - Default: )" << get_config()->fetch_ocsp_response_file.get() << R"( - --ocsp-update-interval= - Set interval to update OCSP response cache. - Default: )" - << util::duration_str(get_config()->ocsp_update_interval) << R"( - --no-ocsp Disable OCSP stapling. - -HTTP/2 and SPDY: - -c, --http2-max-concurrent-streams= - Set the maximum number of the concurrent streams in one - HTTP/2 and SPDY session. - Default: )" << get_config()->http2_max_concurrent_streams << R"( - --frontend-http2-window-bits= - Sets the per-stream initial window size of HTTP/2 SPDY - frontend connection. For HTTP/2, the size is 2**-1. - For SPDY, the size is 2**. - Default: )" << get_config()->http2_upstream_window_bits << R"( - --frontend-http2-connection-window-bits= - Sets the per-connection window size of HTTP/2 and SPDY - frontend connection. For HTTP/2, the size is - 2**-1. For SPDY, the size is 2**. - Default: )" << get_config()->http2_upstream_connection_window_bits - << R"( - --frontend-no-tls - Disable SSL/TLS on frontend connections. - --backend-http2-window-bits= - Sets the initial window size of HTTP/2 backend - connection to 2**-1. - Default: )" << get_config()->http2_downstream_window_bits << R"( - --backend-http2-connection-window-bits= - Sets the per-connection window size of HTTP/2 backend - connection to 2**-1. - Default: )" - << get_config()->http2_downstream_connection_window_bits << R"( - --backend-no-tls - Disable SSL/TLS on backend connections. - --http2-no-cookie-crumbling - Don't crumble cookie header field. - --padding= - Add at most bytes to a HTTP/2 frame payload as - padding. Specify 0 to disable padding. This option is - meant for debugging purpose and not intended to enhance - protocol security. - --no-server-push - Disable HTTP/2 server push. Server push is only - supported by default mode and HTTP/2 frontend. SPDY - frontend does not support server push. - -Mode: - (default mode) - Accept HTTP/2, SPDY and HTTP/1.1 over SSL/TLS. If - --frontend-no-tls is used, accept HTTP/2 and HTTP/1.1. - The incoming HTTP/1.1 connection can be upgraded to - HTTP/2 through HTTP Upgrade. The protocol to the - backend is HTTP/1.1. - -s, --http2-proxy - Like default mode, but enable secure proxy mode. - --http2-bridge - Like default mode, but communicate with the backend in - HTTP/2 over SSL/TLS. Thus the incoming all connections - are converted to HTTP/2 connection and relayed to the - backend. See --backend-http-proxy-uri option if you are - behind the proxy and want to connect to the outside - HTTP/2 proxy. - --client Accept HTTP/2 and HTTP/1.1 without SSL/TLS. The - incoming HTTP/1.1 connection can be upgraded to HTTP/2 - connection through HTTP Upgrade. The protocol to the - backend is HTTP/2. To use nghttpx as a forward proxy, - use -p option instead. - -p, --client-proxy - Like --client option, but it also requires the request - path from frontend must be an absolute URI, suitable for - use as a forward proxy. - -Logging: - -L, --log-level= - Set the severity level of log output. must be - one of INFO, NOTICE, WARN, ERROR and FATAL. - Default: NOTICE - --accesslog-file= - Set path to write access log. To reopen file, send USR1 - signal to nghttpx. - --accesslog-syslog - Send access log to syslog. If this option is used, - --accesslog-file option is ignored. - --accesslog-format= - Specify format string for access log. The default - format is combined format. The following variables are - available: - - * $remote_addr: client IP address. - * $time_local: local time in Common Log format. - * $time_iso8601: local time in ISO 8601 format. - * $request: HTTP request line. - * $status: HTTP response status code. - * $body_bytes_sent: the number of bytes sent to client - as response body. - * $http_: value of HTTP request header where - '_' in is replaced with '-'. - * $remote_port: client port. - * $server_port: server port. - * $request_time: request processing time in seconds with - milliseconds resolution. - * $pid: PID of the running process. - * $alpn: ALPN identifier of the protocol which generates - the response. For HTTP/1, ALPN is always http/1.1, - regardless of minor version. - - Default: )" << DEFAULT_ACCESSLOG_FORMAT << R"( - --errorlog-file= - Set path to write error log. To reopen file, send USR1 - signal to nghttpx. stderr will be redirected to the - error log file unless --errorlog-syslog is used. - Default: )" << get_config()->errorlog_file.get() << R"( - --errorlog-syslog - Send error log to syslog. If this option is used, - --errorlog-file option is ignored. - --syslog-facility= - Set syslog facility to . - Default: )" << str_syslog_facility(get_config()->syslog_facility) - << R"( - -HTTP: - --add-x-forwarded-for - Append X-Forwarded-For header field to the downstream - request. - --strip-incoming-x-forwarded-for - Strip X-Forwarded-For header field from inbound client - requests. - --no-via Don't append to Via header field. If Via header field - is received, it is left unaltered. - --no-location-rewrite - Don't rewrite location header field on --http2-bridge, - --client and default mode. For --http2-proxy and - --client-proxy mode, location header field will not be - altered regardless of this option. - --no-host-rewrite - Don't rewrite host and :authority header fields on - --http2-bridge, --client and default mode. For - --http2-proxy and --client-proxy mode, these headers - will not be altered regardless of this option. - --altsvc= - Specify protocol ID, port, host and origin of - alternative service. and are optional. - They are advertised in alt-svc header field only in - HTTP/1.1 frontend. This option can be used multiple - times to specify multiple alternative services. - Example: --altsvc=h2,443 - --add-response-header=
      - Specify additional header field to add to response - header set. This option just appends header field and - won't replace anything already set. This option can be - used several times to specify multiple header fields. - Example: --add-response-header="foo: bar" - --header-field-buffer= - Set maximum buffer size for incoming HTTP header field - list. This is the sum of header name and value in - bytes. - Default: )" - << util::utos_with_unit(get_config()->header_field_buffer) << R"( - --max-header-fields= - Set maximum number of incoming HTTP header fields, which - appear in one request or response header field list. - Default: )" << get_config()->max_header_fields << R"( - -Debug: - --frontend-http2-dump-request-header= - Dumps request headers received by HTTP/2 frontend to the - file denoted in . The output is done in HTTP/1 - header field format and each header block is followed by - an empty line. This option is not thread safe and MUST - NOT be used with option -n, where >= 2. - --frontend-http2-dump-response-header= - Dumps response headers sent from HTTP/2 frontend to the - file denoted in . The output is done in HTTP/1 - header field format and each header block is followed by - an empty line. This option is not thread safe and MUST - NOT be used with option -n, where >= 2. - -o, --frontend-frame-debug - Print HTTP/2 frames in frontend to stderr. This option - is not thread safe and MUST NOT be used with option - -n=N, where N >= 2. - -Process: - -D, --daemon - Run in a background. If -D is used, the current working - directory is changed to '/'. - --pid-file= - Set path to save PID of this program. - --user= - Run this program as . This option is intended to - be used to drop root privileges. - -Misc: - --conf= - Load configuration from . - Default: )" << get_config()->conf_path.get() << R"( - -v, --version - Print version and exit. - -h, --help Print this help and exit. - --- - - The argument is an integer and an optional unit (e.g., 10K is - 10 * 1024). Units are K, M and G (powers of 1024). - - The argument is an integer and an optional unit (e.g., 1s - is 1 second and 500ms is 500 milliseconds). Units are h, m, s or ms - (hours, minutes, seconds and milliseconds, respectively). If a unit - is omitted, a second is used as unit.)" << std::endl; -} -} // namespace - -int main(int argc, char **argv) { - Log::set_severity_level(NOTICE); - create_config(); - fill_default_config(); - - // We have to copy argv, since getopt_long may change its content. - mod_config()->argc = argc; - mod_config()->argv = new char *[argc]; - - for (int i = 0; i < argc; ++i) { - mod_config()->argv[i] = strdup(argv[i]); - } - - mod_config()->cwd = getcwd(nullptr, 0); - if (mod_config()->cwd == nullptr) { - auto error = errno; - LOG(FATAL) << "failed to get current working directory: errno=" << error; - exit(EXIT_FAILURE); - } - - std::vector> cmdcfgs; - while (1) { - static int flag = 0; - static option long_options[] = { - {"daemon", no_argument, nullptr, 'D'}, - {"log-level", required_argument, nullptr, 'L'}, - {"backend", required_argument, nullptr, 'b'}, - {"http2-max-concurrent-streams", required_argument, nullptr, 'c'}, - {"frontend", required_argument, nullptr, 'f'}, - {"help", no_argument, nullptr, 'h'}, - {"insecure", no_argument, nullptr, 'k'}, - {"workers", required_argument, nullptr, 'n'}, - {"client-proxy", no_argument, nullptr, 'p'}, - {"http2-proxy", no_argument, nullptr, 's'}, - {"version", no_argument, nullptr, 'v'}, - {"frontend-frame-debug", no_argument, nullptr, 'o'}, - {"add-x-forwarded-for", no_argument, &flag, 1}, - {"frontend-http2-read-timeout", required_argument, &flag, 2}, - {"frontend-read-timeout", required_argument, &flag, 3}, - {"frontend-write-timeout", required_argument, &flag, 4}, - {"backend-read-timeout", required_argument, &flag, 5}, - {"backend-write-timeout", required_argument, &flag, 6}, - {"accesslog-file", required_argument, &flag, 7}, - {"backend-keep-alive-timeout", required_argument, &flag, 8}, - {"frontend-http2-window-bits", required_argument, &flag, 9}, - {"pid-file", required_argument, &flag, 10}, - {"user", required_argument, &flag, 11}, - {"conf", required_argument, &flag, 12}, - {"syslog-facility", required_argument, &flag, 14}, - {"backlog", required_argument, &flag, 15}, - {"ciphers", required_argument, &flag, 16}, - {"client", no_argument, &flag, 17}, - {"backend-http2-window-bits", required_argument, &flag, 18}, - {"cacert", required_argument, &flag, 19}, - {"backend-ipv4", no_argument, &flag, 20}, - {"backend-ipv6", no_argument, &flag, 21}, - {"private-key-passwd-file", required_argument, &flag, 22}, - {"no-via", no_argument, &flag, 23}, - {"subcert", required_argument, &flag, 24}, - {"http2-bridge", no_argument, &flag, 25}, - {"backend-http-proxy-uri", required_argument, &flag, 26}, - {"backend-no-tls", no_argument, &flag, 27}, - {"frontend-no-tls", no_argument, &flag, 29}, - {"backend-tls-sni-field", required_argument, &flag, 31}, - {"dh-param-file", required_argument, &flag, 33}, - {"read-rate", required_argument, &flag, 34}, - {"read-burst", required_argument, &flag, 35}, - {"write-rate", required_argument, &flag, 36}, - {"write-burst", required_argument, &flag, 37}, - {"npn-list", required_argument, &flag, 38}, - {"verify-client", no_argument, &flag, 39}, - {"verify-client-cacert", required_argument, &flag, 40}, - {"client-private-key-file", required_argument, &flag, 41}, - {"client-cert-file", required_argument, &flag, 42}, - {"frontend-http2-dump-request-header", required_argument, &flag, 43}, - {"frontend-http2-dump-response-header", required_argument, &flag, 44}, - {"http2-no-cookie-crumbling", no_argument, &flag, 45}, - {"frontend-http2-connection-window-bits", required_argument, &flag, 46}, - {"backend-http2-connection-window-bits", required_argument, &flag, 47}, - {"tls-proto-list", required_argument, &flag, 48}, - {"padding", required_argument, &flag, 49}, - {"worker-read-rate", required_argument, &flag, 50}, - {"worker-read-burst", required_argument, &flag, 51}, - {"worker-write-rate", required_argument, &flag, 52}, - {"worker-write-burst", required_argument, &flag, 53}, - {"altsvc", required_argument, &flag, 54}, - {"add-response-header", required_argument, &flag, 55}, - {"worker-frontend-connections", required_argument, &flag, 56}, - {"accesslog-syslog", no_argument, &flag, 57}, - {"errorlog-file", required_argument, &flag, 58}, - {"errorlog-syslog", no_argument, &flag, 59}, - {"stream-read-timeout", required_argument, &flag, 60}, - {"stream-write-timeout", required_argument, &flag, 61}, - {"no-location-rewrite", no_argument, &flag, 62}, - {"backend-http1-connections-per-host", required_argument, &flag, 63}, - {"listener-disable-timeout", required_argument, &flag, 64}, - {"strip-incoming-x-forwarded-for", no_argument, &flag, 65}, - {"accesslog-format", required_argument, &flag, 66}, - {"backend-http1-connections-per-frontend", required_argument, &flag, - 67}, - {"tls-ticket-key-file", required_argument, &flag, 68}, - {"rlimit-nofile", required_argument, &flag, 69}, - {"tls-ctx-per-worker", no_argument, &flag, 70}, - {"backend-response-buffer", required_argument, &flag, 71}, - {"backend-request-buffer", required_argument, &flag, 72}, - {"no-host-rewrite", no_argument, &flag, 73}, - {"no-server-push", no_argument, &flag, 74}, - {"backend-http2-connections-per-worker", required_argument, &flag, 76}, - {"fetch-ocsp-response-file", required_argument, &flag, 77}, - {"ocsp-update-interval", required_argument, &flag, 78}, - {"no-ocsp", no_argument, &flag, 79}, - {"header-field-buffer", required_argument, &flag, 80}, - {"max-header-fields", required_argument, &flag, 81}, - {nullptr, 0, nullptr, 0}}; - - int option_index = 0; - int c = getopt_long(argc, argv, "DL:b:c:f:hkn:opsv", long_options, - &option_index); - if (c == -1) { - break; - } - switch (c) { - case 'D': - cmdcfgs.emplace_back(SHRPX_OPT_DAEMON, "yes"); - break; - case 'L': - cmdcfgs.emplace_back(SHRPX_OPT_LOG_LEVEL, optarg); - break; - case 'b': - cmdcfgs.emplace_back(SHRPX_OPT_BACKEND, optarg); - break; - case 'c': - cmdcfgs.emplace_back(SHRPX_OPT_HTTP2_MAX_CONCURRENT_STREAMS, optarg); - break; - case 'f': - cmdcfgs.emplace_back(SHRPX_OPT_FRONTEND, optarg); - break; - case 'h': - print_help(std::cout); - exit(EXIT_SUCCESS); - case 'k': - cmdcfgs.emplace_back(SHRPX_OPT_INSECURE, "yes"); - break; - case 'n': -#ifdef NOTHREADS - LOG(WARN) << "Threading disabled at build time, no threads created."; -#else - cmdcfgs.emplace_back(SHRPX_OPT_WORKERS, optarg); -#endif // NOTHREADS - break; - case 'o': - cmdcfgs.emplace_back(SHRPX_OPT_FRONTEND_FRAME_DEBUG, "yes"); - break; - case 'p': - cmdcfgs.emplace_back(SHRPX_OPT_CLIENT_PROXY, "yes"); - break; - case 's': - cmdcfgs.emplace_back(SHRPX_OPT_HTTP2_PROXY, "yes"); - break; - case 'v': - print_version(std::cout); - exit(EXIT_SUCCESS); - case '?': - util::show_candidates(argv[optind - 1], long_options); - exit(EXIT_FAILURE); - case 0: - switch (flag) { - case 1: - // --add-x-forwarded-for - cmdcfgs.emplace_back(SHRPX_OPT_ADD_X_FORWARDED_FOR, "yes"); - break; - case 2: - // --frontend-http2-read-timeout - cmdcfgs.emplace_back(SHRPX_OPT_FRONTEND_HTTP2_READ_TIMEOUT, optarg); - break; - case 3: - // --frontend-read-timeout - cmdcfgs.emplace_back(SHRPX_OPT_FRONTEND_READ_TIMEOUT, optarg); - break; - case 4: - // --frontend-write-timeout - cmdcfgs.emplace_back(SHRPX_OPT_FRONTEND_WRITE_TIMEOUT, optarg); - break; - case 5: - // --backend-read-timeout - cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_READ_TIMEOUT, optarg); - break; - case 6: - // --backend-write-timeout - cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_WRITE_TIMEOUT, optarg); - break; - case 7: - cmdcfgs.emplace_back(SHRPX_OPT_ACCESSLOG_FILE, optarg); - break; - case 8: - // --backend-keep-alive-timeout - cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_KEEP_ALIVE_TIMEOUT, optarg); - break; - case 9: - // --frontend-http2-window-bits - cmdcfgs.emplace_back(SHRPX_OPT_FRONTEND_HTTP2_WINDOW_BITS, optarg); - break; - case 10: - cmdcfgs.emplace_back(SHRPX_OPT_PID_FILE, optarg); - break; - case 11: - cmdcfgs.emplace_back(SHRPX_OPT_USER, optarg); - break; - case 12: - // --conf - mod_config()->conf_path = strcopy(optarg); - break; - case 14: - // --syslog-facility - cmdcfgs.emplace_back(SHRPX_OPT_SYSLOG_FACILITY, optarg); - break; - case 15: - // --backlog - cmdcfgs.emplace_back(SHRPX_OPT_BACKLOG, optarg); - break; - case 16: - // --ciphers - cmdcfgs.emplace_back(SHRPX_OPT_CIPHERS, optarg); - break; - case 17: - // --client - cmdcfgs.emplace_back(SHRPX_OPT_CLIENT, "yes"); - break; - case 18: - // --backend-http2-window-bits - cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_HTTP2_WINDOW_BITS, optarg); - break; - case 19: - // --cacert - cmdcfgs.emplace_back(SHRPX_OPT_CACERT, optarg); - break; - case 20: - // --backend-ipv4 - cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_IPV4, "yes"); - break; - case 21: - // --backend-ipv6 - cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_IPV6, "yes"); - break; - case 22: - // --private-key-passwd-file - cmdcfgs.emplace_back(SHRPX_OPT_PRIVATE_KEY_PASSWD_FILE, optarg); - break; - case 23: - // --no-via - cmdcfgs.emplace_back(SHRPX_OPT_NO_VIA, "yes"); - break; - case 24: - // --subcert - cmdcfgs.emplace_back(SHRPX_OPT_SUBCERT, optarg); - break; - case 25: - // --http2-bridge - cmdcfgs.emplace_back(SHRPX_OPT_HTTP2_BRIDGE, "yes"); - break; - case 26: - // --backend-http-proxy-uri - cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_HTTP_PROXY_URI, optarg); - break; - case 27: - // --backend-no-tls - cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_NO_TLS, "yes"); - break; - case 29: - // --frontend-no-tls - cmdcfgs.emplace_back(SHRPX_OPT_FRONTEND_NO_TLS, "yes"); - break; - case 31: - // --backend-tls-sni-field - cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_TLS_SNI_FIELD, optarg); - break; - case 33: - // --dh-param-file - cmdcfgs.emplace_back(SHRPX_OPT_DH_PARAM_FILE, optarg); - break; - case 34: - // --read-rate - cmdcfgs.emplace_back(SHRPX_OPT_READ_RATE, optarg); - break; - case 35: - // --read-burst - cmdcfgs.emplace_back(SHRPX_OPT_READ_BURST, optarg); - break; - case 36: - // --write-rate - cmdcfgs.emplace_back(SHRPX_OPT_WRITE_RATE, optarg); - break; - case 37: - // --write-burst - cmdcfgs.emplace_back(SHRPX_OPT_WRITE_BURST, optarg); - break; - case 38: - // --npn-list - cmdcfgs.emplace_back(SHRPX_OPT_NPN_LIST, optarg); - break; - case 39: - // --verify-client - cmdcfgs.emplace_back(SHRPX_OPT_VERIFY_CLIENT, "yes"); - break; - case 40: - // --verify-client-cacert - cmdcfgs.emplace_back(SHRPX_OPT_VERIFY_CLIENT_CACERT, optarg); - break; - case 41: - // --client-private-key-file - cmdcfgs.emplace_back(SHRPX_OPT_CLIENT_PRIVATE_KEY_FILE, optarg); - break; - case 42: - // --client-cert-file - cmdcfgs.emplace_back(SHRPX_OPT_CLIENT_CERT_FILE, optarg); - break; - case 43: - // --frontend-http2-dump-request-header - cmdcfgs.emplace_back(SHRPX_OPT_FRONTEND_HTTP2_DUMP_REQUEST_HEADER, - optarg); - break; - case 44: - // --frontend-http2-dump-response-header - cmdcfgs.emplace_back(SHRPX_OPT_FRONTEND_HTTP2_DUMP_RESPONSE_HEADER, - optarg); - break; - case 45: - // --http2-no-cookie-crumbling - cmdcfgs.emplace_back(SHRPX_OPT_HTTP2_NO_COOKIE_CRUMBLING, "yes"); - break; - case 46: - // --frontend-http2-connection-window-bits - cmdcfgs.emplace_back(SHRPX_OPT_FRONTEND_HTTP2_CONNECTION_WINDOW_BITS, - optarg); - break; - case 47: - // --backend-http2-connection-window-bits - cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_HTTP2_CONNECTION_WINDOW_BITS, - optarg); - break; - case 48: - // --tls-proto-list - cmdcfgs.emplace_back(SHRPX_OPT_TLS_PROTO_LIST, optarg); - break; - case 49: - // --padding - cmdcfgs.emplace_back(SHRPX_OPT_PADDING, optarg); - break; - case 50: - // --worker-read-rate - cmdcfgs.emplace_back(SHRPX_OPT_WORKER_READ_RATE, optarg); - break; - case 51: - // --worker-read-burst - cmdcfgs.emplace_back(SHRPX_OPT_WORKER_READ_BURST, optarg); - break; - case 52: - // --worker-write-rate - cmdcfgs.emplace_back(SHRPX_OPT_WORKER_WRITE_RATE, optarg); - break; - case 53: - // --worker-write-burst - cmdcfgs.emplace_back(SHRPX_OPT_WORKER_WRITE_BURST, optarg); - break; - case 54: - // --altsvc - cmdcfgs.emplace_back(SHRPX_OPT_ALTSVC, optarg); - break; - case 55: - // --add-response-header - cmdcfgs.emplace_back(SHRPX_OPT_ADD_RESPONSE_HEADER, optarg); - break; - case 56: - // --worker-frontend-connections - cmdcfgs.emplace_back(SHRPX_OPT_WORKER_FRONTEND_CONNECTIONS, optarg); - break; - case 57: - // --accesslog-syslog - cmdcfgs.emplace_back(SHRPX_OPT_ACCESSLOG_SYSLOG, "yes"); - break; - case 58: - // --errorlog-file - cmdcfgs.emplace_back(SHRPX_OPT_ERRORLOG_FILE, optarg); - break; - case 59: - // --errorlog-syslog - cmdcfgs.emplace_back(SHRPX_OPT_ERRORLOG_SYSLOG, "yes"); - break; - case 60: - // --stream-read-timeout - cmdcfgs.emplace_back(SHRPX_OPT_STREAM_READ_TIMEOUT, optarg); - break; - case 61: - // --stream-write-timeout - cmdcfgs.emplace_back(SHRPX_OPT_STREAM_WRITE_TIMEOUT, optarg); - break; - case 62: - // --no-location-rewrite - cmdcfgs.emplace_back(SHRPX_OPT_NO_LOCATION_REWRITE, "yes"); - break; - case 63: - // --backend-http1-connections-per-host - cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_HTTP1_CONNECTIONS_PER_HOST, - optarg); - break; - case 64: - // --listener-disable-timeout - cmdcfgs.emplace_back(SHRPX_OPT_LISTENER_DISABLE_TIMEOUT, optarg); - break; - case 65: - // --strip-incoming-x-forwarded-for - cmdcfgs.emplace_back(SHRPX_OPT_STRIP_INCOMING_X_FORWARDED_FOR, "yes"); - break; - case 66: - // --accesslog-format - cmdcfgs.emplace_back(SHRPX_OPT_ACCESSLOG_FORMAT, optarg); - break; - case 67: - // --backend-http1-connections-per-frontend - cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_HTTP1_CONNECTIONS_PER_FRONTEND, - optarg); - break; - case 68: - // --tls-ticket-key-file - cmdcfgs.emplace_back(SHRPX_OPT_TLS_TICKET_KEY_FILE, optarg); - break; - case 69: - // --rlimit-nofile - cmdcfgs.emplace_back(SHRPX_OPT_RLIMIT_NOFILE, optarg); - break; - case 71: - // --backend-response-buffer - cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_RESPONSE_BUFFER, optarg); - break; - case 72: - // --backend-request-buffer - cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_REQUEST_BUFFER, optarg); - break; - case 73: - // --no-host-rewrite - cmdcfgs.emplace_back(SHRPX_OPT_NO_HOST_REWRITE, "yes"); - break; - case 74: - // --no-server-push - cmdcfgs.emplace_back(SHRPX_OPT_NO_SERVER_PUSH, "yes"); - break; - case 76: - // --backend-http2-connections-per-worker - cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_HTTP2_CONNECTIONS_PER_WORKER, - optarg); - break; - case 77: - // --fetch-ocsp-response-file - cmdcfgs.emplace_back(SHRPX_OPT_FETCH_OCSP_RESPONSE_FILE, optarg); - break; - case 78: - // --ocsp-update-interval - cmdcfgs.emplace_back(SHRPX_OPT_OCSP_UPDATE_INTERVAL, optarg); - break; - case 79: - // --no-ocsp - cmdcfgs.emplace_back(SHRPX_OPT_NO_OCSP, "yes"); - break; - case 80: - // --header-field-buffer - cmdcfgs.emplace_back(SHRPX_OPT_HEADER_FIELD_BUFFER, optarg); - break; - case 81: - // --max-header-fields - cmdcfgs.emplace_back(SHRPX_OPT_MAX_HEADER_FIELDS, optarg); - break; - default: - break; - } - break; - default: - break; - } - } - - // Initialize OpenSSL before parsing options because we create - // SSL_CTX there. - OPENSSL_config(nullptr); - OpenSSL_add_all_algorithms(); - SSL_load_error_strings(); - SSL_library_init(); - - if (conf_exists(get_config()->conf_path.get())) { - if (load_config(get_config()->conf_path.get()) == -1) { - LOG(FATAL) << "Failed to load configuration from " - << get_config()->conf_path.get(); - exit(EXIT_FAILURE); - } - } - - if (argc - optind >= 2) { - cmdcfgs.emplace_back(SHRPX_OPT_PRIVATE_KEY_FILE, argv[optind++]); - cmdcfgs.emplace_back(SHRPX_OPT_CERTIFICATE_FILE, argv[optind++]); - } - - // First open default log files to deal with errors occurred while - // parsing option values. - reopen_log_files(); - - for (size_t i = 0, len = cmdcfgs.size(); i < len; ++i) { - if (parse_config(cmdcfgs[i].first, cmdcfgs[i].second) == -1) { - LOG(FATAL) << "Failed to parse command-line argument."; - exit(EXIT_FAILURE); - } - } - -#ifndef NOTHREADS - auto lock = make_unique(); -#endif // NOTHREADS - - if (get_config()->accesslog_syslog || get_config()->errorlog_syslog) { - openlog("nghttpx", LOG_NDELAY | LOG_NOWAIT | LOG_PID, - get_config()->syslog_facility); - } - - if (reopen_log_files() != 0) { - LOG(FATAL) << "Failed to open log file"; - exit(EXIT_FAILURE); - } - - redirect_stderr_to_errorlog(); - - if (get_config()->uid != 0) { - if (log_config()->accesslog_fd != -1 && - fchown(log_config()->accesslog_fd, get_config()->uid, - get_config()->gid) == -1) { - auto error = errno; - LOG(WARN) << "Changing owner of access log file failed: " - << strerror(error); - } - if (log_config()->errorlog_fd != -1 && - fchown(log_config()->errorlog_fd, get_config()->uid, - get_config()->gid) == -1) { - auto error = errno; - LOG(WARN) << "Changing owner of error log file failed: " - << strerror(error); - } - } - - if (get_config()->http2_upstream_dump_request_header_file) { - auto path = get_config()->http2_upstream_dump_request_header_file.get(); - auto f = open_file_for_write(path); - - if (f == nullptr) { - LOG(FATAL) << "Failed to open http2 upstream request header file: " - << path; - exit(EXIT_FAILURE); - } - - mod_config()->http2_upstream_dump_request_header = f; - - if (get_config()->uid != 0) { - if (chown(path, get_config()->uid, get_config()->gid) == -1) { - auto error = errno; - LOG(WARN) << "Changing owner of http2 upstream request header file " - << path << " failed: " << strerror(error); - } - } - } - - if (get_config()->http2_upstream_dump_response_header_file) { - auto path = get_config()->http2_upstream_dump_response_header_file.get(); - auto f = open_file_for_write(path); - - if (f == nullptr) { - LOG(FATAL) << "Failed to open http2 upstream response header file: " - << path; - exit(EXIT_FAILURE); - } - - mod_config()->http2_upstream_dump_response_header = f; - - if (get_config()->uid != 0) { - if (chown(path, get_config()->uid, get_config()->gid) == -1) { - auto error = errno; - LOG(WARN) << "Changing owner of http2 upstream response header file" - << " " << path << " failed: " << strerror(error); - } - } - } - - if (get_config()->npn_list.empty()) { - mod_config()->npn_list = parse_config_str_list(DEFAULT_NPN_LIST); - } - if (get_config()->tls_proto_list.empty()) { - mod_config()->tls_proto_list = - parse_config_str_list(DEFAULT_TLS_PROTO_LIST); - } - - mod_config()->tls_proto_mask = - ssl::create_tls_proto_mask(get_config()->tls_proto_list); - - mod_config()->alpn_prefs = ssl::set_alpn_prefs(get_config()->npn_list); - - if (get_config()->backend_ipv4 && get_config()->backend_ipv6) { - LOG(FATAL) << "--backend-ipv4 and --backend-ipv6 cannot be used at the " - << "same time."; - exit(EXIT_FAILURE); - } - - if (get_config()->worker_frontend_connections == 0) { - mod_config()->worker_frontend_connections = - std::numeric_limits::max(); - } - - if (get_config()->http2_proxy + get_config()->http2_bridge + - get_config()->client_proxy + get_config()->client > - 1) { - LOG(FATAL) << "--http2-proxy, --http2-bridge, --client-proxy and --client " - << "cannot be used at the same time."; - exit(EXIT_FAILURE); - } - - if (get_config()->client || get_config()->client_proxy) { - mod_config()->client_mode = true; - mod_config()->upstream_no_tls = true; - } - - if (get_config()->client_mode || get_config()->http2_bridge) { - mod_config()->downstream_proto = PROTO_HTTP2; - } else { - mod_config()->downstream_proto = PROTO_HTTP; - } - - if (!get_config()->upstream_no_tls && - (!get_config()->private_key_file || !get_config()->cert_file)) { - print_usage(std::cerr); - LOG(FATAL) << "Too few arguments"; - exit(EXIT_FAILURE); - } - - if (!get_config()->upstream_no_tls && !get_config()->no_ocsp) { - struct stat buf; - if (stat(get_config()->fetch_ocsp_response_file.get(), &buf) != 0) { - mod_config()->no_ocsp = true; - LOG(WARN) << "--fetch-ocsp-response-file: " - << get_config()->fetch_ocsp_response_file.get() - << " not found. OCSP stapling has been disabled."; - } - } - - if (get_config()->downstream_addrs.empty()) { - DownstreamAddr addr; - addr.host = strcopy(DEFAULT_DOWNSTREAM_HOST); - addr.port = DEFAULT_DOWNSTREAM_PORT; - - mod_config()->downstream_addrs.push_back(std::move(addr)); - } - - if (LOG_ENABLED(INFO)) { - LOG(INFO) << "Resolving backend address"; - } - - for (auto &addr : mod_config()->downstream_addrs) { - - if (addr.host_unix) { - // for AF_UNIX socket, we use "localhost" as host for backend - // hostport. This is used as Host header field to backend and - // not going to be passed to any syscalls. - addr.hostport = - strcopy(util::make_hostport("localhost", get_config()->port)); - - auto path = addr.host.get(); - auto pathlen = strlen(path); - - if (pathlen + 1 > sizeof(addr.addr.un.sun_path)) { - LOG(FATAL) << "UNIX domain socket path " << path << " is too long > " - << sizeof(addr.addr.un.sun_path); - exit(EXIT_FAILURE); - } - - LOG(INFO) << "Use UNIX domain socket path " << path - << " for backend connection"; - - addr.addr.un.sun_family = AF_UNIX; - // copy path including terminal NULL - std::copy_n(path, pathlen + 1, addr.addr.un.sun_path); - addr.addrlen = sizeof(addr.addr.un); - - continue; - } - - addr.hostport = strcopy(util::make_hostport(addr.host.get(), addr.port)); - - if (resolve_hostname( - &addr.addr, &addr.addrlen, addr.host.get(), addr.port, - get_config()->backend_ipv4 - ? AF_INET - : (get_config()->backend_ipv6 ? AF_INET6 : AF_UNSPEC)) == -1) { - exit(EXIT_FAILURE); - } - } - - if (get_config()->downstream_http_proxy_host) { - if (LOG_ENABLED(INFO)) { - LOG(INFO) << "Resolving backend http proxy address"; - } - if (resolve_hostname(&mod_config()->downstream_http_proxy_addr, - &mod_config()->downstream_http_proxy_addrlen, - get_config()->downstream_http_proxy_host.get(), - get_config()->downstream_http_proxy_port, - AF_UNSPEC) == -1) { - exit(EXIT_FAILURE); - } - } - - if (get_config()->http2_downstream_connections_per_worker == 0) { - mod_config()->http2_downstream_connections_per_worker = - get_config()->downstream_addrs.size(); - } - - if (get_config()->rlimit_nofile) { - struct rlimit lim = {static_cast(get_config()->rlimit_nofile), - static_cast(get_config()->rlimit_nofile)}; - if (setrlimit(RLIMIT_NOFILE, &lim) != 0) { - auto error = errno; - LOG(WARN) << "Setting rlimit-nofile failed: " << strerror(error); - } - } - - if (get_config()->upstream_frame_debug) { - // To make it sync to logging - set_output(stderr); - if (isatty(fileno(stdout))) { - set_color_output(true); - } - reset_timer(); - } - - struct sigaction act; - memset(&act, 0, sizeof(struct sigaction)); - act.sa_handler = SIG_IGN; - sigaction(SIGPIPE, &act, nullptr); - - event_loop(); - - LOG(NOTICE) << "Shutdown momentarily"; - - return 0; -} - -} // namespace shrpx - -int main(int argc, char **argv) { return shrpx::main(argc, argv); } diff --git a/src/shrpx.h b/src/shrpx.h deleted file mode 100644 index bd91c76..0000000 --- a/src/shrpx.h +++ /dev/null @@ -1,49 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2012 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#ifndef SHRPX_H -#define SHRPX_H - -#ifdef HAVE_CONFIG_H -#include -#endif // HAVE_CONFIG_H - -#include -#ifdef HAVE_SYS_SOCKET_H -#include -#endif // HAVE_SYS_SOCKET_H - -#include - -#include "shrpx_log.h" - -#ifndef HAVE__EXIT -#define _Exit(status) _exit(status) -#endif // !HAVE__EXIT - -#define DIE() exit(EXIT_FAILURE) - -#define SHRPX_READ_WATERMARK (16 * 1024) - -#endif // SHRPX_H diff --git a/src/shrpx_accept_handler.cc b/src/shrpx_accept_handler.cc deleted file mode 100644 index 0d44830..0000000 --- a/src/shrpx_accept_handler.cc +++ /dev/null @@ -1,114 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2014 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#include "shrpx_accept_handler.h" - -#ifdef HAVE_UNISTD_H -#include -#endif // HAVE_UNISTD_H - -#include - -#include "shrpx_connection_handler.h" -#include "shrpx_config.h" -#include "util.h" - -using namespace nghttp2; - -namespace shrpx { - -namespace { -void acceptcb(struct ev_loop *loop, ev_io *w, int revent) { - auto h = static_cast(w->data); - h->accept_connection(); -} -} // namespace - -AcceptHandler::AcceptHandler(int fd, ConnectionHandler *h) - : conn_hnr_(h), fd_(fd) { - ev_io_init(&wev_, acceptcb, fd_, EV_READ); - wev_.data = this; - ev_io_start(conn_hnr_->get_loop(), &wev_); -} - -AcceptHandler::~AcceptHandler() { - ev_io_stop(conn_hnr_->get_loop(), &wev_); - close(fd_); -} - -void AcceptHandler::accept_connection() { - for (;;) { - sockaddr_union sockaddr; - socklen_t addrlen = sizeof(sockaddr); - -#ifdef HAVE_ACCEPT4 - auto cfd = - accept4(fd_, &sockaddr.sa, &addrlen, SOCK_NONBLOCK | SOCK_CLOEXEC); -#else // !HAVE_ACCEPT4 - auto cfd = accept(fd_, &sockaddr.sa, &addrlen); -#endif // !HAVE_ACCEPT4 - - if (cfd == -1) { - switch (errno) { - case EINTR: - case ENETDOWN: - case EPROTO: - case ENOPROTOOPT: - case EHOSTDOWN: -#ifdef ENONET - case ENONET: -#endif // ENONET - case EHOSTUNREACH: - case EOPNOTSUPP: - case ENETUNREACH: - continue; - case EMFILE: - case ENFILE: - LOG(WARN) << "acceptor: running out file descriptor; disable acceptor " - "temporarily"; - conn_hnr_->disable_acceptor_temporary(30.); - break; - } - - break; - } - -#ifndef HAVE_ACCEPT4 - util::make_socket_nonblocking(cfd); - util::make_socket_closeonexec(cfd); -#endif // !HAVE_ACCEPT4 - - util::make_socket_nodelay(cfd); - - conn_hnr_->handle_connection(cfd, &sockaddr.sa, addrlen); - } -} - -void AcceptHandler::enable() { ev_io_start(conn_hnr_->get_loop(), &wev_); } - -void AcceptHandler::disable() { ev_io_stop(conn_hnr_->get_loop(), &wev_); } - -int AcceptHandler::get_fd() const { return fd_; } - -} // namespace shrpx diff --git a/src/shrpx_accept_handler.h b/src/shrpx_accept_handler.h deleted file mode 100644 index 194788b..0000000 --- a/src/shrpx_accept_handler.h +++ /dev/null @@ -1,53 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2014 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#ifndef SHRPX_ACCEPT_HANDLER_H -#define SHRPX_ACCEPT_HANDLER_H - -#include "shrpx.h" - -#include - -namespace shrpx { - -class ConnectionHandler; - -class AcceptHandler { -public: - AcceptHandler(int fd, ConnectionHandler *h); - ~AcceptHandler(); - void accept_connection(); - void enable(); - void disable(); - int get_fd() const; - -private: - ev_io wev_; - ConnectionHandler *conn_hnr_; - int fd_; -}; - -} // namespace shrpx - -#endif // SHRPX_ACCEPT_HANDLER_H diff --git a/src/shrpx_client_handler.cc b/src/shrpx_client_handler.cc deleted file mode 100644 index 354b95e..0000000 --- a/src/shrpx_client_handler.cc +++ /dev/null @@ -1,793 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2012 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#include "shrpx_client_handler.h" - -#ifdef HAVE_UNISTD_H -#include -#endif // HAVE_UNISTD_H -#include - -#include "shrpx_upstream.h" -#include "shrpx_http2_upstream.h" -#include "shrpx_https_upstream.h" -#include "shrpx_config.h" -#include "shrpx_http_downstream_connection.h" -#include "shrpx_http2_downstream_connection.h" -#include "shrpx_ssl.h" -#include "shrpx_worker.h" -#include "shrpx_downstream_connection_pool.h" -#include "shrpx_downstream.h" -#ifdef HAVE_SPDYLAY -#include "shrpx_spdy_upstream.h" -#endif // HAVE_SPDYLAY -#include "util.h" -#include "template.h" - -using namespace nghttp2; - -namespace shrpx { - -namespace { -void timeoutcb(struct ev_loop *loop, ev_timer *w, int revents) { - auto conn = static_cast(w->data); - auto handler = static_cast(conn->data); - - if (LOG_ENABLED(INFO)) { - CLOG(INFO, handler) << "Time out"; - } - - delete handler; -} -} // namespace - -namespace { -void shutdowncb(struct ev_loop *loop, ev_timer *w, int revents) { - auto handler = static_cast(w->data); - - if (LOG_ENABLED(INFO)) { - CLOG(INFO, handler) << "Close connection due to TLS renegotiation"; - } - - delete handler; -} -} // namespace - -namespace { -void readcb(struct ev_loop *loop, ev_io *w, int revents) { - auto conn = static_cast(w->data); - auto handler = static_cast(conn->data); - - if (handler->do_read() != 0) { - delete handler; - return; - } - if (handler->do_write() != 0) { - delete handler; - return; - } -} -} // namespace - -namespace { -void writecb(struct ev_loop *loop, ev_io *w, int revents) { - auto conn = static_cast(w->data); - auto handler = static_cast(conn->data); - - if (handler->do_write() != 0) { - delete handler; - return; - } -} -} // namespace - -int ClientHandler::read_clear() { - ev_timer_again(conn_.loop, &conn_.rt); - - for (;;) { - if (rb_.rleft() && on_read() != 0) { - return -1; - } - if (rb_.rleft() == 0) { - rb_.reset(); - } else if (rb_.wleft() == 0) { - conn_.rlimit.stopw(); - return 0; - } - - auto nread = conn_.read_clear(rb_.last, rb_.wleft()); - - if (nread == 0) { - return 0; - } - - if (nread < 0) { - return -1; - } - - rb_.write(nread); - } -} - -int ClientHandler::write_clear() { - ev_timer_again(conn_.loop, &conn_.rt); - - for (;;) { - if (wb_.rleft() > 0) { - auto nwrite = conn_.write_clear(wb_.pos, wb_.rleft()); - if (nwrite == 0) { - return 0; - } - if (nwrite < 0) { - return -1; - } - wb_.drain(nwrite); - continue; - } - wb_.reset(); - if (on_write() != 0) { - return -1; - } - if (wb_.rleft() == 0) { - break; - } - } - - conn_.wlimit.stopw(); - ev_timer_stop(conn_.loop, &conn_.wt); - - return 0; -} - -int ClientHandler::tls_handshake() { - ev_timer_again(conn_.loop, &conn_.rt); - - ERR_clear_error(); - - auto rv = conn_.tls_handshake(); - - if (rv == SHRPX_ERR_INPROGRESS) { - return 0; - } - - if (rv < 0) { - return -1; - } - - if (LOG_ENABLED(INFO)) { - CLOG(INFO, this) << "SSL/TLS handshake completed"; - } - - if (validate_next_proto() != 0) { - return -1; - } - - read_ = &ClientHandler::read_tls; - write_ = &ClientHandler::write_tls; - - return 0; -} - -int ClientHandler::read_tls() { - ev_timer_again(conn_.loop, &conn_.rt); - - ERR_clear_error(); - - for (;;) { - // we should process buffered data first before we read EOF. - if (rb_.rleft() && on_read() != 0) { - return -1; - } - if (rb_.rleft() == 0) { - rb_.reset(); - } else if (rb_.wleft() == 0) { - conn_.rlimit.stopw(); - return 0; - } - - auto nread = conn_.read_tls(rb_.last, rb_.wleft()); - - if (nread == 0) { - return 0; - } - - if (nread < 0) { - return -1; - } - - rb_.write(nread); - } -} - -int ClientHandler::write_tls() { - ev_timer_again(conn_.loop, &conn_.rt); - - ERR_clear_error(); - - for (;;) { - if (wb_.rleft() > 0) { - auto nwrite = conn_.write_tls(wb_.pos, wb_.rleft()); - - if (nwrite == 0) { - return 0; - } - - if (nwrite < 0) { - return -1; - } - - wb_.drain(nwrite); - - continue; - } - wb_.reset(); - if (on_write() != 0) { - return -1; - } - if (wb_.rleft() == 0) { - break; - } - } - - conn_.wlimit.stopw(); - ev_timer_stop(conn_.loop, &conn_.wt); - - return 0; -} - -int ClientHandler::upstream_noop() { return 0; } - -int ClientHandler::upstream_read() { - assert(upstream_); - if (upstream_->on_read() != 0) { - return -1; - } - return 0; -} - -int ClientHandler::upstream_write() { - assert(upstream_); - if (upstream_->on_write() != 0) { - return -1; - } - - if (get_should_close_after_write() && wb_.rleft() == 0) { - return -1; - } - - return 0; -} - -int ClientHandler::upstream_http2_connhd_read() { - auto nread = std::min(left_connhd_len_, rb_.rleft()); - if (memcmp(NGHTTP2_CLIENT_MAGIC + NGHTTP2_CLIENT_MAGIC_LEN - left_connhd_len_, - rb_.pos, nread) != 0) { - // There is no downgrade path here. Just drop the connection. - if (LOG_ENABLED(INFO)) { - CLOG(INFO, this) << "invalid client connection header"; - } - - return -1; - } - - left_connhd_len_ -= nread; - rb_.drain(nread); - conn_.rlimit.startw(); - - if (left_connhd_len_ == 0) { - on_read_ = &ClientHandler::upstream_read; - // Run on_read to process data left in buffer since they are not - // notified further - if (on_read() != 0) { - return -1; - } - return 0; - } - - return 0; -} - -int ClientHandler::upstream_http1_connhd_read() { - auto nread = std::min(left_connhd_len_, rb_.rleft()); - if (memcmp(NGHTTP2_CLIENT_MAGIC + NGHTTP2_CLIENT_MAGIC_LEN - left_connhd_len_, - rb_.pos, nread) != 0) { - if (LOG_ENABLED(INFO)) { - CLOG(INFO, this) << "This is HTTP/1.1 connection, " - << "but may be upgraded to HTTP/2 later."; - } - - // Reset header length for later HTTP/2 upgrade - left_connhd_len_ = NGHTTP2_CLIENT_MAGIC_LEN; - on_read_ = &ClientHandler::upstream_read; - on_write_ = &ClientHandler::upstream_write; - - if (on_read() != 0) { - return -1; - } - - return 0; - } - - left_connhd_len_ -= nread; - rb_.drain(nread); - conn_.rlimit.startw(); - - if (left_connhd_len_ == 0) { - if (LOG_ENABLED(INFO)) { - CLOG(INFO, this) << "direct HTTP/2 connection"; - } - - direct_http2_upgrade(); - on_read_ = &ClientHandler::upstream_read; - on_write_ = &ClientHandler::upstream_write; - - // Run on_read to process data left in buffer since they are not - // notified further - if (on_read() != 0) { - return -1; - } - - return 0; - } - - return 0; -} - -ClientHandler::ClientHandler(Worker *worker, int fd, SSL *ssl, - const char *ipaddr, const char *port) - : conn_(worker->get_loop(), fd, ssl, get_config()->upstream_write_timeout, - get_config()->upstream_read_timeout, get_config()->write_rate, - get_config()->write_burst, get_config()->read_rate, - get_config()->read_burst, writecb, readcb, timeoutcb, this), - ipaddr_(ipaddr), port_(port), worker_(worker), - http2session_(worker_->next_http2_session()), - left_connhd_len_(NGHTTP2_CLIENT_MAGIC_LEN), - should_close_after_write_(false) { - - ++worker_->get_worker_stat()->num_connections; - - ev_timer_init(&reneg_shutdown_timer_, shutdowncb, 0., 0.); - - reneg_shutdown_timer_.data = this; - - conn_.rlimit.startw(); - ev_timer_again(conn_.loop, &conn_.rt); - - if (conn_.tls.ssl) { - SSL_set_app_data(conn_.tls.ssl, &conn_); - read_ = write_ = &ClientHandler::tls_handshake; - on_read_ = &ClientHandler::upstream_noop; - on_write_ = &ClientHandler::upstream_write; - } else { - // For non-TLS version, first create HttpsUpstream. It may be - // upgraded to HTTP/2 through HTTP Upgrade or direct HTTP/2 - // connection. - upstream_ = make_unique(this); - alpn_ = "http/1.1"; - read_ = &ClientHandler::read_clear; - write_ = &ClientHandler::write_clear; - on_read_ = &ClientHandler::upstream_http1_connhd_read; - on_write_ = &ClientHandler::upstream_noop; - } -} - -ClientHandler::~ClientHandler() { - if (LOG_ENABLED(INFO)) { - CLOG(INFO, this) << "Deleting"; - } - - if (upstream_) { - upstream_->on_handler_delete(); - } - - auto worker_stat = worker_->get_worker_stat(); - --worker_stat->num_connections; - - if (worker_stat->num_connections == 0) { - worker_->schedule_clear_mcpool(); - } - - ev_timer_stop(conn_.loop, &reneg_shutdown_timer_); - - // TODO If backend is http/2, and it is in CONNECTED state, signal - // it and make it loopbreak when output is zero. - if (worker_->get_graceful_shutdown() && worker_stat->num_connections == 0) { - ev_break(conn_.loop); - } - - if (LOG_ENABLED(INFO)) { - CLOG(INFO, this) << "Deleted"; - } -} - -Upstream *ClientHandler::get_upstream() { return upstream_.get(); } - -struct ev_loop *ClientHandler::get_loop() const { - return conn_.loop; -} - -void ClientHandler::reset_upstream_read_timeout(ev_tstamp t) { - conn_.rt.repeat = t; - if (ev_is_active(&conn_.rt)) { - ev_timer_again(conn_.loop, &conn_.rt); - } -} - -void ClientHandler::reset_upstream_write_timeout(ev_tstamp t) { - conn_.wt.repeat = t; - if (ev_is_active(&conn_.wt)) { - ev_timer_again(conn_.loop, &conn_.wt); - } -} - -int ClientHandler::validate_next_proto() { - const unsigned char *next_proto = nullptr; - unsigned int next_proto_len; - int rv; - - // First set callback for catch all cases - on_read_ = &ClientHandler::upstream_read; - - SSL_get0_next_proto_negotiated(conn_.tls.ssl, &next_proto, &next_proto_len); - for (int i = 0; i < 2; ++i) { - if (next_proto) { - if (LOG_ENABLED(INFO)) { - std::string proto(next_proto, next_proto + next_proto_len); - CLOG(INFO, this) << "The negotiated next protocol: " << proto; - } - if (!ssl::in_proto_list(get_config()->npn_list, next_proto, - next_proto_len)) { - break; - } - if (util::check_h2_is_selected(next_proto, next_proto_len)) { - - on_read_ = &ClientHandler::upstream_http2_connhd_read; - - auto http2_upstream = make_unique(this); - - if (!ssl::check_http2_requirement(conn_.tls.ssl)) { - rv = http2_upstream->terminate_session(NGHTTP2_INADEQUATE_SECURITY); - - if (rv != 0) { - return -1; - } - } - - upstream_ = std::move(http2_upstream); - alpn_.assign(next_proto, next_proto + next_proto_len); - - // At this point, input buffer is already filled with some - // bytes. The read callback is not called until new data - // come. So consume input buffer here. - if (on_read() != 0) { - return -1; - } - - return 0; - } else { -#ifdef HAVE_SPDYLAY - uint16_t version = spdylay_npn_get_version(next_proto, next_proto_len); - if (version) { - upstream_ = make_unique(version, this); - - switch (version) { - case SPDYLAY_PROTO_SPDY2: - alpn_ = "spdy/2"; - break; - case SPDYLAY_PROTO_SPDY3: - alpn_ = "spdy/3"; - break; - case SPDYLAY_PROTO_SPDY3_1: - alpn_ = "spdy/3.1"; - break; - default: - alpn_ = "spdy/unknown"; - } - - // At this point, input buffer is already filled with some - // bytes. The read callback is not called until new data - // come. So consume input buffer here. - if (on_read() != 0) { - return -1; - } - - return 0; - } -#endif // HAVE_SPDYLAY - if (next_proto_len == 8 && memcmp("http/1.1", next_proto, 8) == 0) { - upstream_ = make_unique(this); - alpn_ = "http/1.1"; - - // At this point, input buffer is already filled with some - // bytes. The read callback is not called until new data - // come. So consume input buffer here. - if (on_read() != 0) { - return -1; - } - - return 0; - } - } - break; - } -#if OPENSSL_VERSION_NUMBER >= 0x10002000L - SSL_get0_alpn_selected(conn_.tls.ssl, &next_proto, &next_proto_len); -#else // OPENSSL_VERSION_NUMBER < 0x10002000L - break; -#endif // OPENSSL_VERSION_NUMBER < 0x10002000L - } - if (!next_proto) { - if (LOG_ENABLED(INFO)) { - CLOG(INFO, this) << "No protocol negotiated. Fallback to HTTP/1.1"; - } - upstream_ = make_unique(this); - alpn_ = "http/1.1"; - - // At this point, input buffer is already filled with some bytes. - // The read callback is not called until new data come. So consume - // input buffer here. - if (on_read() != 0) { - return -1; - } - - return 0; - } - if (LOG_ENABLED(INFO)) { - CLOG(INFO, this) << "The negotiated protocol is not supported"; - } - return -1; -} - -int ClientHandler::do_read() { return read_(*this); } -int ClientHandler::do_write() { return write_(*this); } - -int ClientHandler::on_read() { - auto rv = on_read_(*this); - if (rv != 0) { - return rv; - } - conn_.handle_tls_pending_read(); - return 0; -} -int ClientHandler::on_write() { return on_write_(*this); } - -const std::string &ClientHandler::get_ipaddr() const { return ipaddr_; } - -bool ClientHandler::get_should_close_after_write() const { - return should_close_after_write_; -} - -void ClientHandler::set_should_close_after_write(bool f) { - should_close_after_write_ = f; -} - -void ClientHandler::pool_downstream_connection( - std::unique_ptr dconn) { - if (!dconn->poolable()) { - return; - } - if (LOG_ENABLED(INFO)) { - CLOG(INFO, this) << "Pooling downstream connection DCONN:" << dconn.get(); - } - dconn->set_client_handler(nullptr); - auto dconn_pool = worker_->get_dconn_pool(); - dconn_pool->add_downstream_connection(std::move(dconn)); -} - -void ClientHandler::remove_downstream_connection(DownstreamConnection *dconn) { - if (LOG_ENABLED(INFO)) { - CLOG(INFO, this) << "Removing downstream connection DCONN:" << dconn - << " from pool"; - } - auto dconn_pool = worker_->get_dconn_pool(); - dconn_pool->remove_downstream_connection(dconn); -} - -std::unique_ptr -ClientHandler::get_downstream_connection() { - auto dconn_pool = worker_->get_dconn_pool(); - auto dconn = dconn_pool->pop_downstream_connection(); - - if (!dconn) { - if (LOG_ENABLED(INFO)) { - CLOG(INFO, this) << "Downstream connection pool is empty." - << " Create new one"; - } - - auto dconn_pool = worker_->get_dconn_pool(); - - if (http2session_) { - dconn = make_unique(dconn_pool, http2session_); - } else { - dconn = make_unique(dconn_pool, conn_.loop); - } - dconn->set_client_handler(this); - return dconn; - } - - dconn->set_client_handler(this); - - if (LOG_ENABLED(INFO)) { - CLOG(INFO, this) << "Reuse downstream connection DCONN:" << dconn.get() - << " from pool"; - } - - return dconn; -} - -MemchunkPool *ClientHandler::get_mcpool() { return worker_->get_mcpool(); } - -SSL *ClientHandler::get_ssl() const { return conn_.tls.ssl; } - -ConnectBlocker *ClientHandler::get_connect_blocker() const { - return worker_->get_connect_blocker(); -} - -void ClientHandler::direct_http2_upgrade() { - upstream_ = make_unique(this); - alpn_ = NGHTTP2_CLEARTEXT_PROTO_VERSION_ID; - on_read_ = &ClientHandler::upstream_read; -} - -int ClientHandler::perform_http2_upgrade(HttpsUpstream *http) { - auto upstream = make_unique(this); - if (upstream->upgrade_upstream(http) != 0) { - return -1; - } - // http pointer is now owned by upstream. - upstream_.release(); - upstream_ = std::move(upstream); - // TODO We might get other version id in HTTP2-settings, if we - // support aliasing for h2, but we just use library default for now. - alpn_ = NGHTTP2_CLEARTEXT_PROTO_VERSION_ID; - on_read_ = &ClientHandler::upstream_http2_connhd_read; - - static char res[] = "HTTP/1.1 101 Switching Protocols\r\n" - "Connection: Upgrade\r\n" - "Upgrade: " NGHTTP2_CLEARTEXT_PROTO_VERSION_ID "\r\n" - "\r\n"; - wb_.write(res, sizeof(res) - 1); - signal_write(); - return 0; -} - -bool ClientHandler::get_http2_upgrade_allowed() const { return !conn_.tls.ssl; } - -std::string ClientHandler::get_upstream_scheme() const { - if (conn_.tls.ssl) { - return "https"; - } else { - return "http"; - } -} - -void ClientHandler::start_immediate_shutdown() { - ev_timer_start(conn_.loop, &reneg_shutdown_timer_); -} - -namespace { -// Construct absolute request URI from |downstream|, mainly to log -// request URI for proxy request (HTTP/2 proxy or client proxy). This -// is mostly same routine found in -// HttpDownstreamConnection::push_request_headers(), but vastly -// simplified since we only care about absolute URI. -std::string construct_absolute_request_uri(Downstream *downstream) { - const char *authority = nullptr, *host = nullptr; - if (!downstream->get_request_http2_authority().empty()) { - authority = downstream->get_request_http2_authority().c_str(); - } - auto h = downstream->get_request_header(http2::HD_HOST); - if (h) { - host = h->value.c_str(); - } - if (!authority && !host) { - return downstream->get_request_path(); - } - std::string uri; - if (downstream->get_request_http2_scheme().empty()) { - // this comes from HTTP/1 upstream without scheme. Just use http. - uri += "http://"; - } else { - uri += downstream->get_request_http2_scheme(); - uri += "://"; - } - if (authority) { - uri += authority; - } else { - uri += host; - } - - // Server-wide OPTIONS takes following form in proxy request: - // - // OPTIONS http://example.org HTTP/1.1 - // - // Notice that no slash after authority. See - // http://tools.ietf.org/html/rfc7230#section-5.3.4 - if (downstream->get_request_path() != "*") { - uri += downstream->get_request_path(); - } - return uri; -} -} // namespace - -void ClientHandler::write_accesslog(Downstream *downstream) { - upstream_accesslog( - get_config()->accesslog_format, - LogSpec{ - downstream, ipaddr_.c_str(), downstream->get_request_method().c_str(), - - (downstream->get_request_method() != "CONNECT" && - (get_config()->http2_proxy || get_config()->client_proxy)) - ? construct_absolute_request_uri(downstream).c_str() - : downstream->get_request_path().empty() - ? downstream->get_request_http2_authority().c_str() - : downstream->get_request_path().c_str(), - - alpn_.c_str(), - - std::chrono::system_clock::now(), // time_now - downstream->get_request_start_time(), // request_start_time - std::chrono::high_resolution_clock::now(), // request_end_time - - downstream->get_request_major(), downstream->get_request_minor(), - downstream->get_response_http_status(), - downstream->get_response_sent_bodylen(), port_.c_str(), - get_config()->port, get_config()->pid, - }); -} - -void ClientHandler::write_accesslog(int major, int minor, unsigned int status, - int64_t body_bytes_sent) { - auto time_now = std::chrono::system_clock::now(); - auto highres_now = std::chrono::high_resolution_clock::now(); - - upstream_accesslog(get_config()->accesslog_format, - LogSpec{ - nullptr, ipaddr_.c_str(), - "-", // method - "-", // path, - alpn_.c_str(), time_now, - highres_now, // request_start_time TODO is - // there a better value? - highres_now, // request_end_time - major, minor, // major, minor - status, body_bytes_sent, port_.c_str(), - get_config()->port, get_config()->pid, - }); -} - -ClientHandler::WriteBuf *ClientHandler::get_wb() { return &wb_; } - -ClientHandler::ReadBuf *ClientHandler::get_rb() { return &rb_; } - -void ClientHandler::signal_write() { conn_.wlimit.startw(); } - -RateLimit *ClientHandler::get_rlimit() { return &conn_.rlimit; } -RateLimit *ClientHandler::get_wlimit() { return &conn_.wlimit; } - -ev_io *ClientHandler::get_wev() { return &conn_.wev; } - -Worker *ClientHandler::get_worker() const { return worker_; } - -} // namespace shrpx diff --git a/src/shrpx_client_handler.h b/src/shrpx_client_handler.h deleted file mode 100644 index 4367c2b..0000000 --- a/src/shrpx_client_handler.h +++ /dev/null @@ -1,154 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2012 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#ifndef SHRPX_CLIENT_HANDLER_H -#define SHRPX_CLIENT_HANDLER_H - -#include "shrpx.h" - -#include - -#include - -#include - -#include "shrpx_rate_limit.h" -#include "shrpx_connection.h" -#include "buffer.h" -#include "memchunk.h" - -using namespace nghttp2; - -namespace shrpx { - -class Upstream; -class DownstreamConnection; -class Http2Session; -class HttpsUpstream; -class ConnectBlocker; -class DownstreamConnectionPool; -class Worker; -struct WorkerStat; - -class ClientHandler { -public: - ClientHandler(Worker *worker, int fd, SSL *ssl, const char *ipaddr, - const char *port); - ~ClientHandler(); - - // Performs clear text I/O - int read_clear(); - int write_clear(); - // Performs TLS handshake - int tls_handshake(); - // Performs TLS I/O - int read_tls(); - int write_tls(); - - int upstream_noop(); - int upstream_read(); - int upstream_http2_connhd_read(); - int upstream_http1_connhd_read(); - int upstream_write(); - - // Performs I/O operation. Internally calls on_read()/on_write(). - int do_read(); - int do_write(); - - // Processes buffers. No underlying I/O operation will be done. - int on_read(); - int on_write(); - - struct ev_loop *get_loop() const; - void reset_upstream_read_timeout(ev_tstamp t); - void reset_upstream_write_timeout(ev_tstamp t); - int validate_next_proto(); - const std::string &get_ipaddr() const; - const std::string &get_port() const; - bool get_should_close_after_write() const; - void set_should_close_after_write(bool f); - Upstream *get_upstream(); - - void pool_downstream_connection(std::unique_ptr dconn); - void remove_downstream_connection(DownstreamConnection *dconn); - std::unique_ptr get_downstream_connection(); - MemchunkPool *get_mcpool(); - SSL *get_ssl() const; - ConnectBlocker *get_connect_blocker() const; - // Call this function when HTTP/2 connection header is received at - // the start of the connection. - void direct_http2_upgrade(); - // Performs HTTP/2 Upgrade from the connection managed by - // |http|. If this function fails, the connection must be - // terminated. This function returns 0 if it succeeds, or -1. - int perform_http2_upgrade(HttpsUpstream *http); - bool get_http2_upgrade_allowed() const; - // Returns upstream scheme, either "http" or "https" - std::string get_upstream_scheme() const; - void start_immediate_shutdown(); - - // Writes upstream accesslog using |downstream|. The |downstream| - // must not be nullptr. - void write_accesslog(Downstream *downstream); - - // Writes upstream accesslog. This function is used if - // corresponding Downstream object is not available. - void write_accesslog(int major, int minor, unsigned int status, - int64_t body_bytes_sent); - Worker *get_worker() const; - - using WriteBuf = Buffer<32768>; - using ReadBuf = Buffer<8192>; - - WriteBuf *get_wb(); - ReadBuf *get_rb(); - - RateLimit *get_rlimit(); - RateLimit *get_wlimit(); - - void signal_write(); - ev_io *get_wev(); - -private: - Connection conn_; - ev_timer reneg_shutdown_timer_; - std::unique_ptr upstream_; - std::string ipaddr_; - std::string port_; - // The ALPN identifier negotiated for this connection. - std::string alpn_; - std::function read_, write_; - std::function on_read_, on_write_; - Worker *worker_; - Http2Session *http2session_; - // The number of bytes of HTTP/2 client connection header to read - size_t left_connhd_len_; - bool should_close_after_write_; - WriteBuf wb_; - ReadBuf rb_; -}; - -} // namespace shrpx - -#endif // SHRPX_CLIENT_HANDLER_H diff --git a/src/shrpx_config.cc b/src/shrpx_config.cc deleted file mode 100644 index 6577343..0000000 --- a/src/shrpx_config.cc +++ /dev/null @@ -1,1406 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2012 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#include "shrpx_config.h" - -#ifdef HAVE_PWD_H -#include -#endif // HAVE_PWD_H -#ifdef HAVE_NETDB_H -#include -#endif // HAVE_NETDB_H -#ifdef HAVE_SYSLOG_H -#include -#endif // HAVE_SYSLOG_H -#include -#include -#ifdef HAVE_FCNTL_H -#include -#endif // HAVE_FCNTL_H -#ifdef HAVE_UNISTD_H -#include -#endif // HAVE_UNISTD_H - -#include -#include -#include -#include - -#include - -#include "http-parser/http_parser.h" - -#include "shrpx_log.h" -#include "shrpx_ssl.h" -#include "shrpx_http.h" -#include "http2.h" -#include "util.h" -#include "template.h" - -using namespace nghttp2; - -namespace shrpx { - -const char SHRPX_OPT_PRIVATE_KEY_FILE[] = "private-key-file"; -const char SHRPX_OPT_PRIVATE_KEY_PASSWD_FILE[] = "private-key-passwd-file"; -const char SHRPX_OPT_CERTIFICATE_FILE[] = "certificate-file"; -const char SHRPX_OPT_DH_PARAM_FILE[] = "dh-param-file"; -const char SHRPX_OPT_SUBCERT[] = "subcert"; - -const char SHRPX_OPT_BACKEND[] = "backend"; -const char SHRPX_OPT_FRONTEND[] = "frontend"; -const char SHRPX_OPT_WORKERS[] = "workers"; -const char SHRPX_OPT_HTTP2_MAX_CONCURRENT_STREAMS[] = - "http2-max-concurrent-streams"; -const char SHRPX_OPT_LOG_LEVEL[] = "log-level"; -const char SHRPX_OPT_DAEMON[] = "daemon"; -const char SHRPX_OPT_HTTP2_PROXY[] = "http2-proxy"; -const char SHRPX_OPT_HTTP2_BRIDGE[] = "http2-bridge"; -const char SHRPX_OPT_CLIENT_PROXY[] = "client-proxy"; -const char SHRPX_OPT_ADD_X_FORWARDED_FOR[] = "add-x-forwarded-for"; -const char SHRPX_OPT_STRIP_INCOMING_X_FORWARDED_FOR[] = - "strip-incoming-x-forwarded-for"; -const char SHRPX_OPT_NO_VIA[] = "no-via"; -const char SHRPX_OPT_FRONTEND_HTTP2_READ_TIMEOUT[] = - "frontend-http2-read-timeout"; -const char SHRPX_OPT_FRONTEND_READ_TIMEOUT[] = "frontend-read-timeout"; -const char SHRPX_OPT_FRONTEND_WRITE_TIMEOUT[] = "frontend-write-timeout"; -const char SHRPX_OPT_BACKEND_READ_TIMEOUT[] = "backend-read-timeout"; -const char SHRPX_OPT_BACKEND_WRITE_TIMEOUT[] = "backend-write-timeout"; -const char SHRPX_OPT_STREAM_READ_TIMEOUT[] = "stream-read-timeout"; -const char SHRPX_OPT_STREAM_WRITE_TIMEOUT[] = "stream-write-timeout"; -const char SHRPX_OPT_ACCESSLOG_FILE[] = "accesslog-file"; -const char SHRPX_OPT_ACCESSLOG_SYSLOG[] = "accesslog-syslog"; -const char SHRPX_OPT_ACCESSLOG_FORMAT[] = "accesslog-format"; -const char SHRPX_OPT_ERRORLOG_FILE[] = "errorlog-file"; -const char SHRPX_OPT_ERRORLOG_SYSLOG[] = "errorlog-syslog"; -const char SHRPX_OPT_BACKEND_KEEP_ALIVE_TIMEOUT[] = - "backend-keep-alive-timeout"; -const char SHRPX_OPT_FRONTEND_HTTP2_WINDOW_BITS[] = - "frontend-http2-window-bits"; -const char SHRPX_OPT_BACKEND_HTTP2_WINDOW_BITS[] = "backend-http2-window-bits"; -const char SHRPX_OPT_FRONTEND_HTTP2_CONNECTION_WINDOW_BITS[] = - "frontend-http2-connection-window-bits"; -const char SHRPX_OPT_BACKEND_HTTP2_CONNECTION_WINDOW_BITS[] = - "backend-http2-connection-window-bits"; -const char SHRPX_OPT_FRONTEND_NO_TLS[] = "frontend-no-tls"; -const char SHRPX_OPT_BACKEND_NO_TLS[] = "backend-no-tls"; -const char SHRPX_OPT_BACKEND_TLS_SNI_FIELD[] = "backend-tls-sni-field"; -const char SHRPX_OPT_PID_FILE[] = "pid-file"; -const char SHRPX_OPT_USER[] = "user"; -const char SHRPX_OPT_SYSLOG_FACILITY[] = "syslog-facility"; -const char SHRPX_OPT_BACKLOG[] = "backlog"; -const char SHRPX_OPT_CIPHERS[] = "ciphers"; -const char SHRPX_OPT_CLIENT[] = "client"; -const char SHRPX_OPT_INSECURE[] = "insecure"; -const char SHRPX_OPT_CACERT[] = "cacert"; -const char SHRPX_OPT_BACKEND_IPV4[] = "backend-ipv4"; -const char SHRPX_OPT_BACKEND_IPV6[] = "backend-ipv6"; -const char SHRPX_OPT_BACKEND_HTTP_PROXY_URI[] = "backend-http-proxy-uri"; -const char SHRPX_OPT_READ_RATE[] = "read-rate"; -const char SHRPX_OPT_READ_BURST[] = "read-burst"; -const char SHRPX_OPT_WRITE_RATE[] = "write-rate"; -const char SHRPX_OPT_WRITE_BURST[] = "write-burst"; -const char SHRPX_OPT_WORKER_READ_RATE[] = "worker-read-rate"; -const char SHRPX_OPT_WORKER_READ_BURST[] = "worker-read-burst"; -const char SHRPX_OPT_WORKER_WRITE_RATE[] = "worker-write-rate"; -const char SHRPX_OPT_WORKER_WRITE_BURST[] = "worker-write-burst"; -const char SHRPX_OPT_NPN_LIST[] = "npn-list"; -const char SHRPX_OPT_TLS_PROTO_LIST[] = "tls-proto-list"; -const char SHRPX_OPT_VERIFY_CLIENT[] = "verify-client"; -const char SHRPX_OPT_VERIFY_CLIENT_CACERT[] = "verify-client-cacert"; -const char SHRPX_OPT_CLIENT_PRIVATE_KEY_FILE[] = "client-private-key-file"; -const char SHRPX_OPT_CLIENT_CERT_FILE[] = "client-cert-file"; -const char SHRPX_OPT_FRONTEND_HTTP2_DUMP_REQUEST_HEADER[] = - "frontend-http2-dump-request-header"; -const char SHRPX_OPT_FRONTEND_HTTP2_DUMP_RESPONSE_HEADER[] = - "frontend-http2-dump-response-header"; -const char SHRPX_OPT_HTTP2_NO_COOKIE_CRUMBLING[] = "http2-no-cookie-crumbling"; -const char SHRPX_OPT_FRONTEND_FRAME_DEBUG[] = "frontend-frame-debug"; -const char SHRPX_OPT_PADDING[] = "padding"; -const char SHRPX_OPT_ALTSVC[] = "altsvc"; -const char SHRPX_OPT_ADD_RESPONSE_HEADER[] = "add-response-header"; -const char SHRPX_OPT_WORKER_FRONTEND_CONNECTIONS[] = - "worker-frontend-connections"; -const char SHRPX_OPT_NO_LOCATION_REWRITE[] = "no-location-rewrite"; -const char SHRPX_OPT_NO_HOST_REWRITE[] = "no-host-rewrite"; -const char SHRPX_OPT_BACKEND_HTTP1_CONNECTIONS_PER_HOST[] = - "backend-http1-connections-per-host"; -const char SHRPX_OPT_BACKEND_HTTP1_CONNECTIONS_PER_FRONTEND[] = - "backend-http1-connections-per-frontend"; -const char SHRPX_OPT_LISTENER_DISABLE_TIMEOUT[] = "listener-disable-timeout"; -const char SHRPX_OPT_TLS_TICKET_KEY_FILE[] = "tls-ticket-key-file"; -const char SHRPX_OPT_RLIMIT_NOFILE[] = "rlimit-nofile"; -const char SHRPX_OPT_BACKEND_REQUEST_BUFFER[] = "backend-request-buffer"; -const char SHRPX_OPT_BACKEND_RESPONSE_BUFFER[] = "backend-response-buffer"; -const char SHRPX_OPT_NO_SERVER_PUSH[] = "no-server-push"; -const char SHRPX_OPT_BACKEND_HTTP2_CONNECTIONS_PER_WORKER[] = - "backend-http2-connections-per-worker"; -const char SHRPX_OPT_FETCH_OCSP_RESPONSE_FILE[] = "fetch-ocsp-response-file"; -const char SHRPX_OPT_OCSP_UPDATE_INTERVAL[] = "ocsp-update-interval"; -const char SHRPX_OPT_NO_OCSP[] = "no-ocsp"; -const char SHRPX_OPT_HEADER_FIELD_BUFFER[] = "header-field-buffer"; -const char SHRPX_OPT_MAX_HEADER_FIELDS[] = "max-header-fields"; - -namespace { -Config *config = nullptr; -} // namespace - -const Config *get_config() { return config; } - -Config *mod_config() { return config; } - -void create_config() { config = new Config(); } - -TicketKeys::~TicketKeys() { - /* Erase keys from memory */ - for (auto &key : keys) { - memset(&key, 0, sizeof(key)); - } -} - -namespace { -int split_host_port(char *host, size_t hostlen, uint16_t *port_ptr, - const char *hostport) { - // host and port in |hostport| is separated by single ','. - const char *p = strchr(hostport, ','); - if (!p) { - LOG(ERROR) << "Invalid host, port: " << hostport; - return -1; - } - size_t len = p - hostport; - if (hostlen < len + 1) { - LOG(ERROR) << "Hostname too long: " << hostport; - return -1; - } - memcpy(host, hostport, len); - host[len] = '\0'; - - errno = 0; - unsigned long d = strtoul(p + 1, nullptr, 10); - if (errno == 0 && 1 <= d && d <= std::numeric_limits::max()) { - *port_ptr = d; - return 0; - } else { - LOG(ERROR) << "Port is invalid: " << p + 1; - return -1; - } -} -} // namespace - -namespace { -bool is_secure(const char *filename) { - struct stat buf; - int rv = stat(filename, &buf); - if (rv == 0) { - if ((buf.st_mode & S_IRWXU) && !(buf.st_mode & S_IRWXG) && - !(buf.st_mode & S_IRWXO)) { - return true; - } - } - - return false; -} -} // namespace - -std::unique_ptr -read_tls_ticket_key_file(const std::vector &files) { - auto ticket_keys = make_unique(); - auto &keys = ticket_keys->keys; - keys.resize(files.size()); - size_t i = 0; - for (auto &file : files) { - std::ifstream f(file.c_str()); - if (!f) { - LOG(ERROR) << "tls-ticket-key-file: could not open file " << file; - return nullptr; - } - char buf[48]; - f.read(buf, sizeof(buf)); - if (f.gcount() != sizeof(buf)) { - LOG(ERROR) << "tls-ticket-key-file: want to read 48 bytes but read " - << f.gcount() << " bytes from " << file; - return nullptr; - } - - auto &key = keys[i++]; - auto p = buf; - memcpy(key.name, p, sizeof(key.name)); - p += sizeof(key.name); - memcpy(key.aes_key, p, sizeof(key.aes_key)); - p += sizeof(key.aes_key); - memcpy(key.hmac_key, p, sizeof(key.hmac_key)); - - if (LOG_ENABLED(INFO)) { - LOG(INFO) << "session ticket key: " << util::format_hex(key.name, - sizeof(key.name)); - } - } - return ticket_keys; -} - -FILE *open_file_for_write(const char *filename) { -#if defined O_CLOEXEC - auto fd = open(filename, O_WRONLY | O_CLOEXEC | O_CREAT | O_TRUNC, - S_IRUSR | S_IWUSR); -#else - auto fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR); - - // We get race condition if execve is called at the same time. - if (fd != -1) { - util::make_socket_closeonexec(fd); - } -#endif - if (fd == -1) { - LOG(ERROR) << "Failed to open " << filename - << " for writing. Cause: " << strerror(errno); - return nullptr; - } - auto f = fdopen(fd, "wb"); - if (f == nullptr) { - LOG(ERROR) << "Failed to open " << filename - << " for writing. Cause: " << strerror(errno); - return nullptr; - } - - return f; -} - -std::string read_passwd_from_file(const char *filename) { - std::string line; - - if (!is_secure(filename)) { - LOG(ERROR) << "Private key passwd file " << filename - << " has insecure mode."; - return line; - } - - std::ifstream in(filename, std::ios::binary); - if (!in) { - LOG(ERROR) << "Could not open key passwd file " << filename; - return line; - } - - std::getline(in, line); - return line; -} - -std::unique_ptr strcopy(const char *val) { - return strcopy(val, strlen(val)); -} - -std::unique_ptr strcopy(const char *val, size_t len) { - auto res = make_unique(len + 1); - memcpy(res.get(), val, len); - res[len] = '\0'; - return res; -} - -std::unique_ptr strcopy(const std::string &val) { - return strcopy(val.c_str(), val.size()); -} - -std::vector parse_config_str_list(const char *s) { - size_t len = 1; - for (const char *first = s, *p = nullptr; (p = strchr(first, ',')); - ++len, first = p + 1) - ; - auto list = std::vector(len); - auto first = strdup(s); - len = 0; - for (;;) { - auto p = strchr(first, ','); - if (p == nullptr) { - break; - } - list[len++] = first; - *p = '\0'; - first = p + 1; - } - list[len++] = first; - - return list; -} - -void clear_config_str_list(std::vector &list) { - if (list.empty()) { - return; - } - - free(list[0]); - list.clear(); -} - -std::pair parse_header(const char *optarg) { - // We skip possible ":" at the start of optarg. - const auto *colon = strchr(optarg + 1, ':'); - - // name = ":" is not allowed - if (colon == nullptr || (optarg[0] == ':' && colon == optarg + 1)) { - return {"", ""}; - } - - auto value = colon + 1; - for (; *value == '\t' || *value == ' '; ++value) - ; - - return {std::string(optarg, colon), std::string(value, strlen(value))}; -} - -template -int parse_uint(T *dest, const char *opt, const char *optarg) { - char *end = nullptr; - - errno = 0; - - auto val = strtol(optarg, &end, 10); - - if (!optarg[0] || errno != 0 || *end || val < 0) { - LOG(ERROR) << opt << ": bad value. Specify an integer >= 0."; - return -1; - } - - *dest = val; - - return 0; -} - -namespace { -template -int parse_uint_with_unit(T *dest, const char *opt, const char *optarg) { - auto n = util::parse_uint_with_unit(optarg); - if (n == -1) { - LOG(ERROR) << opt << ": bad value: '" << optarg << "'"; - return -1; - } - - *dest = n; - - return 0; -} -} // namespace - -template -int parse_int(T *dest, const char *opt, const char *optarg) { - char *end = nullptr; - - errno = 0; - - auto val = strtol(optarg, &end, 10); - - if (!optarg[0] || errno != 0 || *end) { - LOG(ERROR) << opt << ": bad value. Specify an integer."; - return -1; - } - - *dest = val; - - return 0; -} - -namespace { -LogFragment make_log_fragment(LogFragmentType type, - std::unique_ptr value = nullptr) { - return LogFragment{type, std::move(value)}; -} -} // namespace - -namespace { -bool var_token(char c) { - return util::isAlpha(c) || util::isDigit(c) || c == '_'; -} -} // namespace - -std::vector parse_log_format(const char *optarg) { - auto literal_start = optarg; - auto p = optarg; - auto eop = p + strlen(optarg); - - auto res = std::vector(); - - for (; p != eop;) { - if (*p != '$') { - ++p; - continue; - } - - auto var_start = p; - - ++p; - - for (; p != eop && var_token(*p); ++p) - ; - - auto varlen = p - var_start; - - auto type = SHRPX_LOGF_NONE; - const char *value = nullptr; - size_t valuelen = 0; - - if (util::strieq_l("$remote_addr", var_start, varlen)) { - type = SHRPX_LOGF_REMOTE_ADDR; - } else if (util::strieq_l("$time_local", var_start, varlen)) { - type = SHRPX_LOGF_TIME_LOCAL; - } else if (util::strieq_l("$time_iso8601", var_start, varlen)) { - type = SHRPX_LOGF_TIME_ISO8601; - } else if (util::strieq_l("$request", var_start, varlen)) { - type = SHRPX_LOGF_REQUEST; - } else if (util::strieq_l("$status", var_start, varlen)) { - type = SHRPX_LOGF_STATUS; - } else if (util::strieq_l("$body_bytes_sent", var_start, varlen)) { - type = SHRPX_LOGF_BODY_BYTES_SENT; - } else if (util::istartsWith(var_start, varlen, "$http_")) { - type = SHRPX_LOGF_HTTP; - value = var_start + sizeof("$http_") - 1; - valuelen = varlen - (sizeof("$http_") - 1); - } else if (util::strieq_l("$remote_port", var_start, varlen)) { - type = SHRPX_LOGF_REMOTE_PORT; - } else if (util::strieq_l("$server_port", var_start, varlen)) { - type = SHRPX_LOGF_SERVER_PORT; - } else if (util::strieq_l("$request_time", var_start, varlen)) { - type = SHRPX_LOGF_REQUEST_TIME; - } else if (util::strieq_l("$pid", var_start, varlen)) { - type = SHRPX_LOGF_PID; - } else if (util::strieq_l("$alpn", var_start, varlen)) { - type = SHRPX_LOGF_ALPN; - } else { - LOG(WARN) << "Unrecognized log format variable: " - << std::string(var_start, varlen); - continue; - } - - if (literal_start < var_start) { - res.push_back( - make_log_fragment(SHRPX_LOGF_LITERAL, - strcopy(literal_start, var_start - literal_start))); - } - - if (value == nullptr) { - res.push_back(make_log_fragment(type)); - } else { - res.push_back(make_log_fragment(type, strcopy(value, valuelen))); - auto &v = res.back().value; - for (size_t i = 0; v[i]; ++i) { - if (v[i] == '_') { - v[i] = '-'; - } - } - } - - literal_start = var_start + varlen; - } - - if (literal_start != eop) { - res.push_back(make_log_fragment( - SHRPX_LOGF_LITERAL, strcopy(literal_start, eop - literal_start))); - } - - return res; -} - -namespace { -int parse_duration(ev_tstamp *dest, const char *opt, const char *optarg) { - auto t = util::parse_duration_with_unit(optarg); - if (t == std::numeric_limits::infinity()) { - LOG(ERROR) << opt << ": bad value: '" << optarg << "'"; - return -1; - } - - *dest = t; - - return 0; -} -} // namespace - -int parse_config(const char *opt, const char *optarg) { - char host[NI_MAXHOST]; - uint16_t port; - if (util::strieq(opt, SHRPX_OPT_BACKEND)) { - if (util::istartsWith(optarg, SHRPX_UNIX_PATH_PREFIX)) { - DownstreamAddr addr; - auto path = optarg + str_size(SHRPX_UNIX_PATH_PREFIX); - addr.host = strcopy(path); - addr.host_unix = true; - - mod_config()->downstream_addrs.push_back(std::move(addr)); - - return 0; - } - - if (split_host_port(host, sizeof(host), &port, optarg) == -1) { - return -1; - } - - DownstreamAddr addr; - addr.host = strcopy(host); - addr.port = port; - - mod_config()->downstream_addrs.push_back(std::move(addr)); - - return 0; - } - - if (util::strieq(opt, SHRPX_OPT_FRONTEND)) { - if (util::istartsWith(optarg, SHRPX_UNIX_PATH_PREFIX)) { - auto path = optarg + str_size(SHRPX_UNIX_PATH_PREFIX); - mod_config()->host = strcopy(path); - mod_config()->port = 0; - mod_config()->host_unix = true; - - return 0; - } - - if (split_host_port(host, sizeof(host), &port, optarg) == -1) { - return -1; - } - - mod_config()->host = strcopy(host); - mod_config()->port = port; - mod_config()->host_unix = false; - - return 0; - } - - if (util::strieq(opt, SHRPX_OPT_WORKERS)) { - return parse_uint(&mod_config()->num_worker, opt, optarg); - } - - if (util::strieq(opt, SHRPX_OPT_HTTP2_MAX_CONCURRENT_STREAMS)) { - return parse_uint(&mod_config()->http2_max_concurrent_streams, opt, optarg); - } - - if (util::strieq(opt, SHRPX_OPT_LOG_LEVEL)) { - if (Log::set_severity_level_by_name(optarg) == -1) { - LOG(ERROR) << opt << ": Invalid severity level: " << optarg; - return -1; - } - - return 0; - } - - if (util::strieq(opt, SHRPX_OPT_DAEMON)) { - mod_config()->daemon = util::strieq(optarg, "yes"); - - return 0; - } - - if (util::strieq(opt, SHRPX_OPT_HTTP2_PROXY)) { - mod_config()->http2_proxy = util::strieq(optarg, "yes"); - - return 0; - } - - if (util::strieq(opt, SHRPX_OPT_HTTP2_BRIDGE)) { - mod_config()->http2_bridge = util::strieq(optarg, "yes"); - - return 0; - } - - if (util::strieq(opt, SHRPX_OPT_CLIENT_PROXY)) { - mod_config()->client_proxy = util::strieq(optarg, "yes"); - - return 0; - } - - if (util::strieq(opt, SHRPX_OPT_ADD_X_FORWARDED_FOR)) { - mod_config()->add_x_forwarded_for = util::strieq(optarg, "yes"); - - return 0; - } - - if (util::strieq(opt, SHRPX_OPT_STRIP_INCOMING_X_FORWARDED_FOR)) { - mod_config()->strip_incoming_x_forwarded_for = util::strieq(optarg, "yes"); - - return 0; - } - - if (util::strieq(opt, SHRPX_OPT_NO_VIA)) { - mod_config()->no_via = util::strieq(optarg, "yes"); - - return 0; - } - - if (util::strieq(opt, SHRPX_OPT_FRONTEND_HTTP2_READ_TIMEOUT)) { - return parse_duration(&mod_config()->http2_upstream_read_timeout, opt, - optarg); - } - - if (util::strieq(opt, SHRPX_OPT_FRONTEND_READ_TIMEOUT)) { - return parse_duration(&mod_config()->upstream_read_timeout, opt, optarg); - } - - if (util::strieq(opt, SHRPX_OPT_FRONTEND_WRITE_TIMEOUT)) { - return parse_duration(&mod_config()->upstream_write_timeout, opt, optarg); - } - - if (util::strieq(opt, SHRPX_OPT_BACKEND_READ_TIMEOUT)) { - return parse_duration(&mod_config()->downstream_read_timeout, opt, optarg); - } - - if (util::strieq(opt, SHRPX_OPT_BACKEND_WRITE_TIMEOUT)) { - return parse_duration(&mod_config()->downstream_write_timeout, opt, optarg); - } - - if (util::strieq(opt, SHRPX_OPT_STREAM_READ_TIMEOUT)) { - return parse_duration(&mod_config()->stream_read_timeout, opt, optarg); - } - - if (util::strieq(opt, SHRPX_OPT_STREAM_WRITE_TIMEOUT)) { - return parse_duration(&mod_config()->stream_write_timeout, opt, optarg); - } - - if (util::strieq(opt, SHRPX_OPT_ACCESSLOG_FILE)) { - mod_config()->accesslog_file = strcopy(optarg); - - return 0; - } - - if (util::strieq(opt, SHRPX_OPT_ACCESSLOG_SYSLOG)) { - mod_config()->accesslog_syslog = util::strieq(optarg, "yes"); - - return 0; - } - - if (util::strieq(opt, SHRPX_OPT_ACCESSLOG_FORMAT)) { - mod_config()->accesslog_format = parse_log_format(optarg); - - return 0; - } - - if (util::strieq(opt, SHRPX_OPT_ERRORLOG_FILE)) { - mod_config()->errorlog_file = strcopy(optarg); - - return 0; - } - - if (util::strieq(opt, SHRPX_OPT_ERRORLOG_SYSLOG)) { - mod_config()->errorlog_syslog = util::strieq(optarg, "yes"); - - return 0; - } - - if (util::strieq(opt, SHRPX_OPT_BACKEND_KEEP_ALIVE_TIMEOUT)) { - return parse_duration(&mod_config()->downstream_idle_read_timeout, opt, - optarg); - } - - if (util::strieq(opt, SHRPX_OPT_FRONTEND_HTTP2_WINDOW_BITS) || - util::strieq(opt, SHRPX_OPT_BACKEND_HTTP2_WINDOW_BITS)) { - - size_t *resp; - - if (util::strieq(opt, SHRPX_OPT_FRONTEND_HTTP2_WINDOW_BITS)) { - resp = &mod_config()->http2_upstream_window_bits; - } else { - resp = &mod_config()->http2_downstream_window_bits; - } - - errno = 0; - - int n; - - if (parse_uint(&n, opt, optarg) != 0) { - return -1; - } - - if (n >= 31) { - LOG(ERROR) << opt - << ": specify the integer in the range [0, 30], inclusive"; - return -1; - } - - *resp = n; - - return 0; - } - - if (util::strieq(opt, SHRPX_OPT_FRONTEND_HTTP2_CONNECTION_WINDOW_BITS) || - util::strieq(opt, SHRPX_OPT_BACKEND_HTTP2_CONNECTION_WINDOW_BITS)) { - - size_t *resp; - - if (util::strieq(opt, SHRPX_OPT_FRONTEND_HTTP2_CONNECTION_WINDOW_BITS)) { - resp = &mod_config()->http2_upstream_connection_window_bits; - } else { - resp = &mod_config()->http2_downstream_connection_window_bits; - } - - errno = 0; - - int n; - - if (parse_uint(&n, opt, optarg) != 0) { - return -1; - } - - if (n < 16 || n >= 31) { - LOG(ERROR) << opt - << ": specify the integer in the range [16, 30], inclusive"; - return -1; - } - - *resp = n; - - return 0; - } - - if (util::strieq(opt, SHRPX_OPT_FRONTEND_NO_TLS)) { - mod_config()->upstream_no_tls = util::strieq(optarg, "yes"); - - return 0; - } - - if (util::strieq(opt, SHRPX_OPT_BACKEND_NO_TLS)) { - mod_config()->downstream_no_tls = util::strieq(optarg, "yes"); - - return 0; - } - - if (util::strieq(opt, SHRPX_OPT_BACKEND_TLS_SNI_FIELD)) { - mod_config()->backend_tls_sni_name = strcopy(optarg); - - return 0; - } - - if (util::strieq(opt, SHRPX_OPT_PID_FILE)) { - mod_config()->pid_file = strcopy(optarg); - - return 0; - } - - if (util::strieq(opt, SHRPX_OPT_USER)) { - auto pwd = getpwnam(optarg); - if (!pwd) { - LOG(ERROR) << opt << ": failed to get uid from " << optarg << ": " - << strerror(errno); - return -1; - } - mod_config()->user = strcopy(pwd->pw_name); - mod_config()->uid = pwd->pw_uid; - mod_config()->gid = pwd->pw_gid; - - return 0; - } - - if (util::strieq(opt, SHRPX_OPT_PRIVATE_KEY_FILE)) { - mod_config()->private_key_file = strcopy(optarg); - - return 0; - } - - if (util::strieq(opt, SHRPX_OPT_PRIVATE_KEY_PASSWD_FILE)) { - auto passwd = read_passwd_from_file(optarg); - if (passwd.empty()) { - LOG(ERROR) << opt << ": Couldn't read key file's passwd from " << optarg; - return -1; - } - mod_config()->private_key_passwd = strcopy(passwd); - - return 0; - } - - if (util::strieq(opt, SHRPX_OPT_CERTIFICATE_FILE)) { - mod_config()->cert_file = strcopy(optarg); - - return 0; - } - - if (util::strieq(opt, SHRPX_OPT_DH_PARAM_FILE)) { - mod_config()->dh_param_file = strcopy(optarg); - - return 0; - } - - if (util::strieq(opt, SHRPX_OPT_SUBCERT)) { - // Private Key file and certificate file separated by ':'. - const char *sp = strchr(optarg, ':'); - if (sp) { - std::string keyfile(optarg, sp); - // TODO Do we need private key for subcert? - mod_config()->subcerts.emplace_back(keyfile, sp + 1); - } - - return 0; - } - - if (util::strieq(opt, SHRPX_OPT_SYSLOG_FACILITY)) { - int facility = int_syslog_facility(optarg); - if (facility == -1) { - LOG(ERROR) << opt << ": Unknown syslog facility: " << optarg; - return -1; - } - mod_config()->syslog_facility = facility; - - return 0; - } - - if (util::strieq(opt, SHRPX_OPT_BACKLOG)) { - int n; - if (parse_int(&n, opt, optarg) != 0) { - return -1; - } - - if (n < -1) { - LOG(ERROR) << opt << ": " << optarg << " is not allowed"; - - return -1; - } - - mod_config()->backlog = n; - - return 0; - } - - if (util::strieq(opt, SHRPX_OPT_CIPHERS)) { - mod_config()->ciphers = strcopy(optarg); - - return 0; - } - - if (util::strieq(opt, SHRPX_OPT_CLIENT)) { - mod_config()->client = util::strieq(optarg, "yes"); - - return 0; - } - - if (util::strieq(opt, SHRPX_OPT_INSECURE)) { - mod_config()->insecure = util::strieq(optarg, "yes"); - - return 0; - } - - if (util::strieq(opt, SHRPX_OPT_CACERT)) { - mod_config()->cacert = strcopy(optarg); - - return 0; - } - - if (util::strieq(opt, SHRPX_OPT_BACKEND_IPV4)) { - mod_config()->backend_ipv4 = util::strieq(optarg, "yes"); - - return 0; - } - - if (util::strieq(opt, SHRPX_OPT_BACKEND_IPV6)) { - mod_config()->backend_ipv6 = util::strieq(optarg, "yes"); - - return 0; - } - - if (util::strieq(opt, SHRPX_OPT_BACKEND_HTTP_PROXY_URI)) { - // parse URI and get hostname, port and optionally userinfo. - http_parser_url u; - memset(&u, 0, sizeof(u)); - int rv = http_parser_parse_url(optarg, strlen(optarg), 0, &u); - if (rv == 0) { - std::string val; - if (u.field_set & UF_USERINFO) { - http2::copy_url_component(val, &u, UF_USERINFO, optarg); - // Surprisingly, u.field_set & UF_USERINFO is nonzero even if - // userinfo component is empty string. - if (!val.empty()) { - val = util::percentDecode(val.begin(), val.end()); - mod_config()->downstream_http_proxy_userinfo = strcopy(val); - } - } - if (u.field_set & UF_HOST) { - http2::copy_url_component(val, &u, UF_HOST, optarg); - mod_config()->downstream_http_proxy_host = strcopy(val); - } else { - LOG(ERROR) << opt << ": no hostname specified"; - return -1; - } - if (u.field_set & UF_PORT) { - mod_config()->downstream_http_proxy_port = u.port; - } else { - LOG(ERROR) << opt << ": no port specified"; - return -1; - } - } else { - LOG(ERROR) << opt << ": parse error"; - return -1; - } - - return 0; - } - - if (util::strieq(opt, SHRPX_OPT_READ_RATE)) { - return parse_uint_with_unit(&mod_config()->read_rate, opt, optarg); - } - - if (util::strieq(opt, SHRPX_OPT_READ_BURST)) { - return parse_uint_with_unit(&mod_config()->read_burst, opt, optarg); - } - - if (util::strieq(opt, SHRPX_OPT_WRITE_RATE)) { - return parse_uint_with_unit(&mod_config()->write_rate, opt, optarg); - } - - if (util::strieq(opt, SHRPX_OPT_WRITE_BURST)) { - return parse_uint_with_unit(&mod_config()->write_burst, opt, optarg); - } - - if (util::strieq(opt, SHRPX_OPT_WORKER_READ_RATE)) { - LOG(WARN) << opt << ": not implemented yet"; - return parse_uint_with_unit(&mod_config()->worker_read_rate, opt, optarg); - } - - if (util::strieq(opt, SHRPX_OPT_WORKER_READ_BURST)) { - LOG(WARN) << opt << ": not implemented yet"; - return parse_uint_with_unit(&mod_config()->worker_read_burst, opt, optarg); - } - - if (util::strieq(opt, SHRPX_OPT_WORKER_WRITE_RATE)) { - LOG(WARN) << opt << ": not implemented yet"; - return parse_uint_with_unit(&mod_config()->worker_write_rate, opt, optarg); - } - - if (util::strieq(opt, SHRPX_OPT_WORKER_WRITE_BURST)) { - LOG(WARN) << opt << ": not implemented yet"; - return parse_uint_with_unit(&mod_config()->worker_write_burst, opt, optarg); - } - - if (util::strieq(opt, SHRPX_OPT_NPN_LIST)) { - clear_config_str_list(mod_config()->npn_list); - - mod_config()->npn_list = parse_config_str_list(optarg); - - return 0; - } - - if (util::strieq(opt, SHRPX_OPT_TLS_PROTO_LIST)) { - clear_config_str_list(mod_config()->tls_proto_list); - - mod_config()->tls_proto_list = parse_config_str_list(optarg); - - return 0; - } - - if (util::strieq(opt, SHRPX_OPT_VERIFY_CLIENT)) { - mod_config()->verify_client = util::strieq(optarg, "yes"); - - return 0; - } - - if (util::strieq(opt, SHRPX_OPT_VERIFY_CLIENT_CACERT)) { - mod_config()->verify_client_cacert = strcopy(optarg); - - return 0; - } - - if (util::strieq(opt, SHRPX_OPT_CLIENT_PRIVATE_KEY_FILE)) { - mod_config()->client_private_key_file = strcopy(optarg); - - return 0; - } - - if (util::strieq(opt, SHRPX_OPT_CLIENT_CERT_FILE)) { - mod_config()->client_cert_file = strcopy(optarg); - - return 0; - } - - if (util::strieq(opt, SHRPX_OPT_FRONTEND_HTTP2_DUMP_REQUEST_HEADER)) { - mod_config()->http2_upstream_dump_request_header_file = strcopy(optarg); - - return 0; - } - - if (util::strieq(opt, SHRPX_OPT_FRONTEND_HTTP2_DUMP_RESPONSE_HEADER)) { - mod_config()->http2_upstream_dump_response_header_file = strcopy(optarg); - - return 0; - } - - if (util::strieq(opt, SHRPX_OPT_HTTP2_NO_COOKIE_CRUMBLING)) { - mod_config()->http2_no_cookie_crumbling = util::strieq(optarg, "yes"); - - return 0; - } - - if (util::strieq(opt, SHRPX_OPT_FRONTEND_FRAME_DEBUG)) { - mod_config()->upstream_frame_debug = util::strieq(optarg, "yes"); - - return 0; - } - - if (util::strieq(opt, SHRPX_OPT_PADDING)) { - return parse_uint(&mod_config()->padding, opt, optarg); - } - - if (util::strieq(opt, SHRPX_OPT_ALTSVC)) { - auto tokens = parse_config_str_list(optarg); - - if (tokens.size() < 2) { - // Requires at least protocol_id and port - LOG(ERROR) << opt << ": too few parameters: " << optarg; - return -1; - } - - if (tokens.size() > 4) { - // We only need protocol_id, port, host and origin - LOG(ERROR) << opt << ": too many parameters: " << optarg; - return -1; - } - - int port; - - if (parse_uint(&port, opt, tokens[1]) != 0) { - return -1; - } - - if (port < 1 || - port > static_cast(std::numeric_limits::max())) { - LOG(ERROR) << opt << ": port is invalid: " << tokens[1]; - return -1; - } - - AltSvc altsvc; - - altsvc.port = port; - - altsvc.protocol_id = tokens[0]; - altsvc.protocol_id_len = strlen(altsvc.protocol_id); - - if (tokens.size() > 2) { - altsvc.host = tokens[2]; - altsvc.host_len = strlen(altsvc.host); - - if (tokens.size() > 3) { - altsvc.origin = tokens[3]; - altsvc.origin_len = strlen(altsvc.origin); - } - } - - mod_config()->altsvcs.push_back(std::move(altsvc)); - - return 0; - } - - if (util::strieq(opt, SHRPX_OPT_ADD_RESPONSE_HEADER)) { - auto p = parse_header(optarg); - if (p.first.empty()) { - LOG(ERROR) << opt << ": header field name is empty: " << optarg; - return -1; - } - mod_config()->add_response_headers.push_back(std::move(p)); - - return 0; - } - - if (util::strieq(opt, SHRPX_OPT_WORKER_FRONTEND_CONNECTIONS)) { - return parse_uint(&mod_config()->worker_frontend_connections, opt, optarg); - } - - if (util::strieq(opt, SHRPX_OPT_NO_LOCATION_REWRITE)) { - mod_config()->no_location_rewrite = util::strieq(optarg, "yes"); - - return 0; - } - - if (util::strieq(opt, SHRPX_OPT_NO_HOST_REWRITE)) { - mod_config()->no_host_rewrite = util::strieq(optarg, "yes"); - - return 0; - } - - if (util::strieq(opt, SHRPX_OPT_BACKEND_HTTP1_CONNECTIONS_PER_HOST)) { - int n; - - if (parse_uint(&n, opt, optarg) != 0) { - return -1; - } - - if (n == 0) { - LOG(ERROR) << opt << ": specify an integer strictly more than 0"; - - return -1; - } - - mod_config()->downstream_connections_per_host = n; - - return 0; - } - - if (util::strieq(opt, SHRPX_OPT_BACKEND_HTTP1_CONNECTIONS_PER_FRONTEND)) { - return parse_uint(&mod_config()->downstream_connections_per_frontend, opt, - optarg); - } - - if (util::strieq(opt, SHRPX_OPT_LISTENER_DISABLE_TIMEOUT)) { - return parse_duration(&mod_config()->listener_disable_timeout, opt, optarg); - } - - if (util::strieq(opt, SHRPX_OPT_TLS_TICKET_KEY_FILE)) { - mod_config()->tls_ticket_key_files.push_back(optarg); - return 0; - } - - if (util::strieq(opt, SHRPX_OPT_RLIMIT_NOFILE)) { - int n; - - if (parse_uint(&n, opt, optarg) != 0) { - return -1; - } - - if (n < 0) { - LOG(ERROR) << opt << ": specify the integer more than or equal to 0"; - - return -1; - } - - mod_config()->rlimit_nofile = n; - - return 0; - } - - if (util::strieq(opt, SHRPX_OPT_BACKEND_REQUEST_BUFFER) || - util::strieq(opt, SHRPX_OPT_BACKEND_RESPONSE_BUFFER)) { - size_t n; - if (parse_uint_with_unit(&n, opt, optarg) != 0) { - return -1; - } - - if (n == 0) { - LOG(ERROR) << opt << ": specify an integer strictly more than 0"; - - return -1; - } - - if (util::strieq(opt, SHRPX_OPT_BACKEND_REQUEST_BUFFER)) { - mod_config()->downstream_request_buffer_size = n; - } else { - mod_config()->downstream_response_buffer_size = n; - } - - return 0; - } - - if (util::strieq(opt, SHRPX_OPT_NO_SERVER_PUSH)) { - mod_config()->no_server_push = util::strieq(optarg, "yes"); - - return 0; - } - - if (util::strieq(opt, SHRPX_OPT_BACKEND_HTTP2_CONNECTIONS_PER_WORKER)) { - return parse_uint(&mod_config()->http2_downstream_connections_per_worker, - opt, optarg); - } - - if (util::strieq(opt, SHRPX_OPT_FETCH_OCSP_RESPONSE_FILE)) { - mod_config()->fetch_ocsp_response_file = strcopy(optarg); - - return 0; - } - - if (util::strieq(opt, SHRPX_OPT_OCSP_UPDATE_INTERVAL)) { - return parse_duration(&mod_config()->ocsp_update_interval, opt, optarg); - } - - if (util::strieq(opt, SHRPX_OPT_NO_OCSP)) { - mod_config()->no_ocsp = util::strieq(optarg, "yes"); - - return 0; - } - - if (util::strieq(opt, SHRPX_OPT_HEADER_FIELD_BUFFER)) { - return parse_uint_with_unit(&mod_config()->header_field_buffer, opt, - optarg); - } - - if (util::strieq(opt, SHRPX_OPT_MAX_HEADER_FIELDS)) { - return parse_uint(&mod_config()->max_header_fields, opt, optarg); - } - - if (util::strieq(opt, "conf")) { - LOG(WARN) << "conf: ignored"; - - return 0; - } - - LOG(ERROR) << "Unknown option: " << opt; - - return -1; -} - -int load_config(const char *filename) { - std::ifstream in(filename, std::ios::binary); - if (!in) { - LOG(ERROR) << "Could not open config file " << filename; - return -1; - } - std::string line; - int linenum = 0; - while (std::getline(in, line)) { - ++linenum; - if (line.empty() || line[0] == '#') { - continue; - } - size_t i; - size_t size = line.size(); - for (i = 0; i < size && line[i] != '='; ++i) - ; - if (i == size) { - LOG(ERROR) << "Bad configuration format at line " << linenum; - return -1; - } - line[i] = '\0'; - auto s = line.c_str(); - if (parse_config(s, s + i + 1) == -1) { - return -1; - } - } - return 0; -} - -const char *str_syslog_facility(int facility) { - switch (facility) { - case (LOG_AUTH): - return "auth"; - case (LOG_AUTHPRIV): - return "authpriv"; - case (LOG_CRON): - return "cron"; - case (LOG_DAEMON): - return "daemon"; - case (LOG_FTP): - return "ftp"; - case (LOG_KERN): - return "kern"; - case (LOG_LOCAL0): - return "local0"; - case (LOG_LOCAL1): - return "local1"; - case (LOG_LOCAL2): - return "local2"; - case (LOG_LOCAL3): - return "local3"; - case (LOG_LOCAL4): - return "local4"; - case (LOG_LOCAL5): - return "local5"; - case (LOG_LOCAL6): - return "local6"; - case (LOG_LOCAL7): - return "local7"; - case (LOG_LPR): - return "lpr"; - case (LOG_MAIL): - return "mail"; - case (LOG_SYSLOG): - return "syslog"; - case (LOG_USER): - return "user"; - case (LOG_UUCP): - return "uucp"; - default: - return "(unknown)"; - } -} - -int int_syslog_facility(const char *strfacility) { - if (util::strieq(strfacility, "auth")) { - return LOG_AUTH; - } - - if (util::strieq(strfacility, "authpriv")) { - return LOG_AUTHPRIV; - } - - if (util::strieq(strfacility, "cron")) { - return LOG_CRON; - } - - if (util::strieq(strfacility, "daemon")) { - return LOG_DAEMON; - } - - if (util::strieq(strfacility, "ftp")) { - return LOG_FTP; - } - - if (util::strieq(strfacility, "kern")) { - return LOG_KERN; - } - - if (util::strieq(strfacility, "local0")) { - return LOG_LOCAL0; - } - - if (util::strieq(strfacility, "local1")) { - return LOG_LOCAL1; - } - - if (util::strieq(strfacility, "local2")) { - return LOG_LOCAL2; - } - - if (util::strieq(strfacility, "local3")) { - return LOG_LOCAL3; - } - - if (util::strieq(strfacility, "local4")) { - return LOG_LOCAL4; - } - - if (util::strieq(strfacility, "local5")) { - return LOG_LOCAL5; - } - - if (util::strieq(strfacility, "local6")) { - return LOG_LOCAL6; - } - - if (util::strieq(strfacility, "local7")) { - return LOG_LOCAL7; - } - - if (util::strieq(strfacility, "lpr")) { - return LOG_LPR; - } - - if (util::strieq(strfacility, "mail")) { - return LOG_MAIL; - } - - if (util::strieq(strfacility, "news")) { - return LOG_NEWS; - } - - if (util::strieq(strfacility, "syslog")) { - return LOG_SYSLOG; - } - - if (util::strieq(strfacility, "user")) { - return LOG_USER; - } - - if (util::strieq(strfacility, "uucp")) { - return LOG_UUCP; - } - - return -1; -} - -} // namespace shrpx diff --git a/src/shrpx_config.h b/src/shrpx_config.h deleted file mode 100644 index 8afccd5..0000000 --- a/src/shrpx_config.h +++ /dev/null @@ -1,409 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2012 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#ifndef SHRPX_CONFIG_H -#define SHRPX_CONFIG_H - -#include "shrpx.h" - -#include -#ifdef HAVE_SYS_SOCKET_H -#include -#endif // HAVE_SYS_SOCKET_H -#include -#ifdef HAVE_NETINET_IN_H -#include -#endif // HAVE_NETINET_IN_H -#ifdef HAVE_ARPA_INET_H -#include -#endif // HAVE_ARPA_INET_H -#include -#include -#include -#include - -#include - -#include - -#include - -namespace shrpx { - -struct LogFragment; - -namespace ssl { - -class CertLookupTree; - -} // namespace ssl - -#define SHRPX_UNIX_PATH_PREFIX "unix:" - -extern const char SHRPX_OPT_PRIVATE_KEY_FILE[]; -extern const char SHRPX_OPT_PRIVATE_KEY_PASSWD_FILE[]; -extern const char SHRPX_OPT_CERTIFICATE_FILE[]; -extern const char SHRPX_OPT_DH_PARAM_FILE[]; -extern const char SHRPX_OPT_SUBCERT[]; -extern const char SHRPX_OPT_BACKEND[]; -extern const char SHRPX_OPT_FRONTEND[]; -extern const char SHRPX_OPT_WORKERS[]; -extern const char SHRPX_OPT_HTTP2_MAX_CONCURRENT_STREAMS[]; -extern const char SHRPX_OPT_LOG_LEVEL[]; -extern const char SHRPX_OPT_DAEMON[]; -extern const char SHRPX_OPT_HTTP2_PROXY[]; -extern const char SHRPX_OPT_HTTP2_BRIDGE[]; -extern const char SHRPX_OPT_CLIENT_PROXY[]; -extern const char SHRPX_OPT_ADD_X_FORWARDED_FOR[]; -extern const char SHRPX_OPT_STRIP_INCOMING_X_FORWARDED_FOR[]; -extern const char SHRPX_OPT_NO_VIA[]; -extern const char SHRPX_OPT_FRONTEND_HTTP2_READ_TIMEOUT[]; -extern const char SHRPX_OPT_FRONTEND_READ_TIMEOUT[]; -extern const char SHRPX_OPT_FRONTEND_WRITE_TIMEOUT[]; -extern const char SHRPX_OPT_BACKEND_READ_TIMEOUT[]; -extern const char SHRPX_OPT_BACKEND_WRITE_TIMEOUT[]; -extern const char SHRPX_OPT_STREAM_READ_TIMEOUT[]; -extern const char SHRPX_OPT_STREAM_WRITE_TIMEOUT[]; -extern const char SHRPX_OPT_ACCESSLOG_FILE[]; -extern const char SHRPX_OPT_ACCESSLOG_SYSLOG[]; -extern const char SHRPX_OPT_ACCESSLOG_FORMAT[]; -extern const char SHRPX_OPT_ERRORLOG_FILE[]; -extern const char SHRPX_OPT_ERRORLOG_SYSLOG[]; -extern const char SHRPX_OPT_BACKEND_KEEP_ALIVE_TIMEOUT[]; -extern const char SHRPX_OPT_FRONTEND_HTTP2_WINDOW_BITS[]; -extern const char SHRPX_OPT_BACKEND_HTTP2_WINDOW_BITS[]; -extern const char SHRPX_OPT_FRONTEND_HTTP2_CONNECTION_WINDOW_BITS[]; -extern const char SHRPX_OPT_BACKEND_HTTP2_CONNECTION_WINDOW_BITS[]; -extern const char SHRPX_OPT_FRONTEND_NO_TLS[]; -extern const char SHRPX_OPT_BACKEND_NO_TLS[]; -extern const char SHRPX_OPT_PID_FILE[]; -extern const char SHRPX_OPT_USER[]; -extern const char SHRPX_OPT_SYSLOG_FACILITY[]; -extern const char SHRPX_OPT_BACKLOG[]; -extern const char SHRPX_OPT_CIPHERS[]; -extern const char SHRPX_OPT_CLIENT[]; -extern const char SHRPX_OPT_INSECURE[]; -extern const char SHRPX_OPT_CACERT[]; -extern const char SHRPX_OPT_BACKEND_IPV4[]; -extern const char SHRPX_OPT_BACKEND_IPV6[]; -extern const char SHRPX_OPT_BACKEND_HTTP_PROXY_URI[]; -extern const char SHRPX_OPT_BACKEND_TLS_SNI_FIELD[]; -extern const char SHRPX_OPT_READ_RATE[]; -extern const char SHRPX_OPT_READ_BURST[]; -extern const char SHRPX_OPT_WRITE_RATE[]; -extern const char SHRPX_OPT_WRITE_BURST[]; -extern const char SHRPX_OPT_WORKER_READ_RATE[]; -extern const char SHRPX_OPT_WORKER_READ_BURST[]; -extern const char SHRPX_OPT_WORKER_WRITE_RATE[]; -extern const char SHRPX_OPT_WORKER_WRITE_BURST[]; -extern const char SHRPX_OPT_NPN_LIST[]; -extern const char SHRPX_OPT_TLS_PROTO_LIST[]; -extern const char SHRPX_OPT_VERIFY_CLIENT[]; -extern const char SHRPX_OPT_VERIFY_CLIENT_CACERT[]; -extern const char SHRPX_OPT_CLIENT_PRIVATE_KEY_FILE[]; -extern const char SHRPX_OPT_CLIENT_CERT_FILE[]; -extern const char SHRPX_OPT_FRONTEND_HTTP2_DUMP_REQUEST_HEADER[]; -extern const char SHRPX_OPT_FRONTEND_HTTP2_DUMP_RESPONSE_HEADER[]; -extern const char SHRPX_OPT_HTTP2_NO_COOKIE_CRUMBLING[]; -extern const char SHRPX_OPT_FRONTEND_FRAME_DEBUG[]; -extern const char SHRPX_OPT_PADDING[]; -extern const char SHRPX_OPT_ALTSVC[]; -extern const char SHRPX_OPT_ADD_RESPONSE_HEADER[]; -extern const char SHRPX_OPT_WORKER_FRONTEND_CONNECTIONS[]; -extern const char SHRPX_OPT_NO_LOCATION_REWRITE[]; -extern const char SHRPX_OPT_NO_HOST_REWRITE[]; -extern const char SHRPX_OPT_BACKEND_HTTP1_CONNECTIONS_PER_HOST[]; -extern const char SHRPX_OPT_BACKEND_HTTP1_CONNECTIONS_PER_FRONTEND[]; -extern const char SHRPX_OPT_LISTENER_DISABLE_TIMEOUT[]; -extern const char SHRPX_OPT_TLS_TICKET_KEY_FILE[]; -extern const char SHRPX_OPT_RLIMIT_NOFILE[]; -extern const char SHRPX_OPT_BACKEND_REQUEST_BUFFER[]; -extern const char SHRPX_OPT_BACKEND_RESPONSE_BUFFER[]; -extern const char SHRPX_OPT_NO_SERVER_PUSH[]; -extern const char SHRPX_OPT_BACKEND_HTTP2_CONNECTIONS_PER_WORKER[]; -extern const char SHRPX_OPT_FETCH_OCSP_RESPONSE_FILE[]; -extern const char SHRPX_OPT_OCSP_UPDATE_INTERVAL[]; -extern const char SHRPX_OPT_NO_OCSP[]; -extern const char SHRPX_OPT_HEADER_FIELD_BUFFER[]; -extern const char SHRPX_OPT_MAX_HEADER_FIELDS[]; - -union sockaddr_union { - sockaddr_storage storage; - sockaddr sa; - sockaddr_in6 in6; - sockaddr_in in; - sockaddr_un un; -}; - -enum shrpx_proto { PROTO_HTTP2, PROTO_HTTP }; - -struct AltSvc { - AltSvc() - : protocol_id(nullptr), host(nullptr), origin(nullptr), - protocol_id_len(0), host_len(0), origin_len(0), port(0) {} - - char *protocol_id; - char *host; - char *origin; - - size_t protocol_id_len; - size_t host_len; - size_t origin_len; - - uint16_t port; -}; - -struct DownstreamAddr { - DownstreamAddr() : addr{{0}}, addrlen(0), port(0), host_unix(false) {} - sockaddr_union addr; - // backend address. If |host_unix| is true, this is UNIX domain - // socket path. - std::unique_ptr host; - std::unique_ptr hostport; - size_t addrlen; - // backend port. 0 if |host_unix| is true. - uint16_t port; - // true if |host| contains UNIX domain socket path. - bool host_unix; -}; - -struct TicketKey { - uint8_t name[16]; - uint8_t aes_key[16]; - uint8_t hmac_key[16]; -}; - -struct TicketKeys { - ~TicketKeys(); - std::vector keys; -}; - -struct Config { - // The list of (private key file, certificate file) pair - std::vector> subcerts; - std::vector altsvcs; - std::vector> add_response_headers; - std::vector alpn_prefs; - std::vector accesslog_format; - std::vector downstream_addrs; - std::vector tls_ticket_key_files; - // binary form of http proxy host and port - sockaddr_union downstream_http_proxy_addr; - ev_tstamp http2_upstream_read_timeout; - ev_tstamp upstream_read_timeout; - ev_tstamp upstream_write_timeout; - ev_tstamp downstream_read_timeout; - ev_tstamp downstream_write_timeout; - ev_tstamp stream_read_timeout; - ev_tstamp stream_write_timeout; - ev_tstamp downstream_idle_read_timeout; - ev_tstamp listener_disable_timeout; - ev_tstamp ocsp_update_interval; - // address of frontend connection. This could be a path to UNIX - // domain socket. In this case, |host_unix| must be true. - std::unique_ptr host; - std::unique_ptr private_key_file; - std::unique_ptr private_key_passwd; - std::unique_ptr cert_file; - std::unique_ptr dh_param_file; - const char *server_name; - std::unique_ptr backend_tls_sni_name; - std::unique_ptr pid_file; - std::unique_ptr conf_path; - std::unique_ptr ciphers; - std::unique_ptr cacert; - // userinfo in http proxy URI, not percent-encoded form - std::unique_ptr downstream_http_proxy_userinfo; - // host in http proxy URI - std::unique_ptr downstream_http_proxy_host; - std::unique_ptr http2_upstream_dump_request_header_file; - std::unique_ptr http2_upstream_dump_response_header_file; - // // Rate limit configuration per connection - // ev_token_bucket_cfg *rate_limit_cfg; - // // Rate limit configuration per worker (thread) - // ev_token_bucket_cfg *worker_rate_limit_cfg; - // list of supported NPN/ALPN protocol strings in the order of - // preference. The each element of this list is a NULL-terminated - // string. - std::vector npn_list; - // list of supported SSL/TLS protocol strings. The each element of - // this list is a NULL-terminated string. - std::vector tls_proto_list; - // Path to file containing CA certificate solely used for client - // certificate validation - std::unique_ptr verify_client_cacert; - std::unique_ptr client_private_key_file; - std::unique_ptr client_cert_file; - std::unique_ptr accesslog_file; - std::unique_ptr errorlog_file; - std::unique_ptr fetch_ocsp_response_file; - FILE *http2_upstream_dump_request_header; - FILE *http2_upstream_dump_response_header; - nghttp2_session_callbacks *http2_upstream_callbacks; - nghttp2_session_callbacks *http2_downstream_callbacks; - nghttp2_option *http2_option; - nghttp2_option *http2_client_option; - char **argv; - char *cwd; - size_t num_worker; - size_t http2_max_concurrent_streams; - size_t http2_upstream_window_bits; - size_t http2_downstream_window_bits; - size_t http2_upstream_connection_window_bits; - size_t http2_downstream_connection_window_bits; - size_t http2_downstream_connections_per_worker; - size_t downstream_connections_per_host; - size_t downstream_connections_per_frontend; - // actual size of downstream_http_proxy_addr - size_t downstream_http_proxy_addrlen; - size_t read_rate; - size_t read_burst; - size_t write_rate; - size_t write_burst; - size_t worker_read_rate; - size_t worker_read_burst; - size_t worker_write_rate; - size_t worker_write_burst; - size_t padding; - size_t worker_frontend_connections; - size_t rlimit_nofile; - size_t downstream_request_buffer_size; - size_t downstream_response_buffer_size; - size_t header_field_buffer; - size_t max_header_fields; - // Bit mask to disable SSL/TLS protocol versions. This will be - // passed to SSL_CTX_set_options(). - long int tls_proto_mask; - // downstream protocol; this will be determined by given options. - shrpx_proto downstream_proto; - int syslog_facility; - int backlog; - int argc; - std::unique_ptr user; - uid_t uid; - gid_t gid; - pid_t pid; - // frontend listening port. 0 if frontend listens on UNIX domain - // socket, in this case |host_unix| must be true. - uint16_t port; - // port in http proxy URI - uint16_t downstream_http_proxy_port; - bool verbose; - bool daemon; - bool verify_client; - bool http2_proxy; - bool http2_bridge; - bool client_proxy; - bool add_x_forwarded_for; - bool strip_incoming_x_forwarded_for; - bool no_via; - bool upstream_no_tls; - bool downstream_no_tls; - // Send accesslog to syslog, ignoring accesslog_file. - bool accesslog_syslog; - // Send errorlog to syslog, ignoring errorlog_file. - bool errorlog_syslog; - bool client; - // true if --client or --client-proxy are enabled. - bool client_mode; - bool insecure; - bool backend_ipv4; - bool backend_ipv6; - bool http2_no_cookie_crumbling; - bool upstream_frame_debug; - bool no_location_rewrite; - bool no_host_rewrite; - bool no_server_push; - // true if host contains UNIX domain socket path - bool host_unix; - bool no_ocsp; -}; - -const Config *get_config(); -Config *mod_config(); -void create_config(); - -// Parses option name |opt| and value |optarg|. The results are -// stored into statically allocated Config object. This function -// returns 0 if it succeeds, or -1. -int parse_config(const char *opt, const char *optarg); - -// Loads configurations from |filename| and stores them in statically -// allocated Config object. This function returns 0 if it succeeds, or -// -1. -int load_config(const char *filename); - -// Read passwd from |filename| -std::string read_passwd_from_file(const char *filename); - -// Parses comma delimited strings in |s| and returns the array of -// pointers, each element points to the each substring in |s|. The -// |s| must be comma delimited list of strings. The strings must be -// delimited by a single comma and any white spaces around it are -// treated as a part of protocol strings. This function may modify -// |s| and the caller must leave it as is after this call. This -// function copies |s| and first element in the return value points to -// it. It is caller's responsibility to deallocate its memory. -std::vector parse_config_str_list(const char *s); - -// Clears all elements of |list|, which is returned by -// parse_config_str_list(). If list is not empty, list[0] is freed by -// free(2). After this call, list.empty() must be true. -void clear_config_str_list(std::vector &list); - -// Parses header field in |optarg|. We expect header field is formed -// like "NAME: VALUE". We require that NAME is non empty string. ":" -// is allowed at the start of the NAME, but NAME == ":" is not -// allowed. This function returns pair of NAME and VALUE. -std::pair parse_header(const char *optarg); - -std::vector parse_log_format(const char *optarg); - -// Returns a copy of NULL-terminated string |val|. -std::unique_ptr strcopy(const char *val); - -// Returns a copy of string |val| of length |n|. The returned string -// will be NULL-terminated. -std::unique_ptr strcopy(const char *val, size_t n); - -// Returns a copy of val.c_str(). -std::unique_ptr strcopy(const std::string &val); - -// Returns string for syslog |facility|. -const char *str_syslog_facility(int facility); - -// Returns integer value of syslog |facility| string. -int int_syslog_facility(const char *strfacility); - -FILE *open_file_for_write(const char *filename); - -// Reads TLS ticket key file in |files| and returns TicketKey which -// stores read key data. This function returns TicketKey if it -// succeeds, or nullptr. -std::unique_ptr -read_tls_ticket_key_file(const std::vector &files); - -} // namespace shrpx - -#endif // SHRPX_CONFIG_H diff --git a/src/shrpx_config_test.cc b/src/shrpx_config_test.cc deleted file mode 100644 index ad570bb..0000000 --- a/src/shrpx_config_test.cc +++ /dev/null @@ -1,175 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2014 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#include "shrpx_config_test.h" - -#ifdef HAVE_UNISTD_H -#include -#endif // HAVE_UNISTD_H - -#include - -#include - -#include "shrpx_config.h" - -namespace shrpx { - -void test_shrpx_config_parse_config_str_list(void) { - auto res = parse_config_str_list("a"); - CU_ASSERT(1 == res.size()); - CU_ASSERT(0 == strcmp("a", res[0])); - clear_config_str_list(res); - - res = parse_config_str_list("a,"); - CU_ASSERT(2 == res.size()); - CU_ASSERT(0 == strcmp("a", res[0])); - CU_ASSERT(0 == strcmp("", res[1])); - clear_config_str_list(res); - - res = parse_config_str_list(",a,,"); - CU_ASSERT(4 == res.size()); - CU_ASSERT(0 == strcmp("", res[0])); - CU_ASSERT(0 == strcmp("a", res[1])); - CU_ASSERT(0 == strcmp("", res[2])); - CU_ASSERT(0 == strcmp("", res[3])); - clear_config_str_list(res); - - res = parse_config_str_list(""); - CU_ASSERT(1 == res.size()); - CU_ASSERT(0 == strcmp("", res[0])); - clear_config_str_list(res); - - res = parse_config_str_list("alpha,bravo,charlie"); - CU_ASSERT(3 == res.size()); - CU_ASSERT(0 == strcmp("alpha", res[0])); - CU_ASSERT(0 == strcmp("bravo", res[1])); - CU_ASSERT(0 == strcmp("charlie", res[2])); - clear_config_str_list(res); -} - -void test_shrpx_config_parse_header(void) { - auto p = parse_header("a: b"); - CU_ASSERT("a" == p.first); - CU_ASSERT("b" == p.second); - - p = parse_header("a: b"); - CU_ASSERT("a" == p.first); - CU_ASSERT("b" == p.second); - - p = parse_header(":a: b"); - CU_ASSERT(":a" == p.first); - CU_ASSERT("b" == p.second); - - p = parse_header("a: :b"); - CU_ASSERT("a" == p.first); - CU_ASSERT(":b" == p.second); - - p = parse_header(": b"); - CU_ASSERT(p.first.empty()); - - p = parse_header("alpha: bravo charlie"); - CU_ASSERT("alpha" == p.first); - CU_ASSERT("bravo charlie" == p.second); -} - -void test_shrpx_config_parse_log_format(void) { - auto res = parse_log_format("$remote_addr - $remote_user [$time_local] " - "\"$request\" $status $body_bytes_sent " - "\"$http_referer\" \"$http_user_agent\""); - CU_ASSERT(14 == res.size()); - - CU_ASSERT(SHRPX_LOGF_REMOTE_ADDR == res[0].type); - - CU_ASSERT(SHRPX_LOGF_LITERAL == res[1].type); - CU_ASSERT(0 == strcmp(" - $remote_user [", res[1].value.get())); - - CU_ASSERT(SHRPX_LOGF_TIME_LOCAL == res[2].type); - - CU_ASSERT(SHRPX_LOGF_LITERAL == res[3].type); - CU_ASSERT(0 == strcmp("] \"", res[3].value.get())); - - CU_ASSERT(SHRPX_LOGF_REQUEST == res[4].type); - - CU_ASSERT(SHRPX_LOGF_LITERAL == res[5].type); - CU_ASSERT(0 == strcmp("\" ", res[5].value.get())); - - CU_ASSERT(SHRPX_LOGF_STATUS == res[6].type); - - CU_ASSERT(SHRPX_LOGF_LITERAL == res[7].type); - CU_ASSERT(0 == strcmp(" ", res[7].value.get())); - - CU_ASSERT(SHRPX_LOGF_BODY_BYTES_SENT == res[8].type); - - CU_ASSERT(SHRPX_LOGF_LITERAL == res[9].type); - CU_ASSERT(0 == strcmp(" \"", res[9].value.get())); - - CU_ASSERT(SHRPX_LOGF_HTTP == res[10].type); - CU_ASSERT(0 == strcmp("referer", res[10].value.get())); - - CU_ASSERT(SHRPX_LOGF_LITERAL == res[11].type); - CU_ASSERT(0 == strcmp("\" \"", res[11].value.get())); - - CU_ASSERT(SHRPX_LOGF_HTTP == res[12].type); - CU_ASSERT(0 == strcmp("user-agent", res[12].value.get())); - - CU_ASSERT(SHRPX_LOGF_LITERAL == res[13].type); - CU_ASSERT(0 == strcmp("\"", res[13].value.get())); -} - -void test_shrpx_config_read_tls_ticket_key_file(void) { - char file1[] = "/tmp/nghttpx-unittest.XXXXXX"; - auto fd1 = mkstemp(file1); - assert(fd1 != -1); - assert(48 == - write(fd1, "0..............12..............34..............5", 48)); - char file2[] = "/tmp/nghttpx-unittest.XXXXXX"; - auto fd2 = mkstemp(file2); - assert(fd2 != -1); - assert(48 == - write(fd2, "6..............78..............9a..............b", 48)); - - close(fd1); - close(fd2); - auto ticket_keys = read_tls_ticket_key_file({file1, file2}); - unlink(file1); - unlink(file2); - CU_ASSERT(ticket_keys.get() != nullptr); - CU_ASSERT(2 == ticket_keys->keys.size()); - auto key = &ticket_keys->keys[0]; - CU_ASSERT(0 == memcmp("0..............1", key->name, sizeof(key->name))); - CU_ASSERT(0 == - memcmp("2..............3", key->aes_key, sizeof(key->aes_key))); - CU_ASSERT(0 == - memcmp("4..............5", key->hmac_key, sizeof(key->hmac_key))); - - key = &ticket_keys->keys[1]; - CU_ASSERT(0 == memcmp("6..............7", key->name, sizeof(key->name))); - CU_ASSERT(0 == - memcmp("8..............9", key->aes_key, sizeof(key->aes_key))); - CU_ASSERT(0 == - memcmp("a..............b", key->hmac_key, sizeof(key->hmac_key))); -} - -} // namespace shrpx diff --git a/src/shrpx_config_test.h b/src/shrpx_config_test.h deleted file mode 100644 index 6d6d035..0000000 --- a/src/shrpx_config_test.h +++ /dev/null @@ -1,41 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2014 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#ifndef SHRPX_CONFIG_TEST_H -#define SHRPX_CONFIG_TEST_H - -#ifdef HAVE_CONFIG_H -#include -#endif // HAVE_CONFIG_H - -namespace shrpx { - -void test_shrpx_config_parse_config_str_list(void); -void test_shrpx_config_parse_header(void); -void test_shrpx_config_parse_log_format(void); -void test_shrpx_config_read_tls_ticket_key_file(void); - -} // namespace shrpx - -#endif // SHRPX_CONFIG_TEST_H diff --git a/src/shrpx_connect_blocker.cc b/src/shrpx_connect_blocker.cc deleted file mode 100644 index 3eb6de1..0000000 --- a/src/shrpx_connect_blocker.cc +++ /dev/null @@ -1,65 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2014 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#include "shrpx_connect_blocker.h" - -namespace shrpx { - -namespace { -const ev_tstamp INITIAL_SLEEP = 2.; -} // namespace - -namespace { -void connect_blocker_cb(struct ev_loop *loop, ev_timer *w, int revents) { - if (LOG_ENABLED(INFO)) { - LOG(INFO) << "unblock downstream connection"; - } -} -} // namespace - -ConnectBlocker::ConnectBlocker(struct ev_loop *loop) - : loop_(loop), sleep_(INITIAL_SLEEP) { - ev_timer_init(&timer_, connect_blocker_cb, 0., 0.); -} - -ConnectBlocker::~ConnectBlocker() { ev_timer_stop(loop_, &timer_); } - -bool ConnectBlocker::blocked() const { return ev_is_active(&timer_); } - -void ConnectBlocker::on_success() { sleep_ = INITIAL_SLEEP; } - -void ConnectBlocker::on_failure() { - if (ev_is_active(&timer_)) { - return; - } - - sleep_ = std::min(128., sleep_ * 2); - - LOG(WARN) << "connect failure, start sleeping " << sleep_; - - ev_timer_set(&timer_, sleep_, 0.); - ev_timer_start(loop_, &timer_); -} - -} // namespace shrpx diff --git a/src/shrpx_connect_blocker.h b/src/shrpx_connect_blocker.h deleted file mode 100644 index af44564..0000000 --- a/src/shrpx_connect_blocker.h +++ /dev/null @@ -1,56 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2014 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#ifndef SHRPX_CONNECT_BLOCKER_H -#define SHRPX_CONNECT_BLOCKER_H - -#include "shrpx.h" - -#include - -namespace shrpx { - -class ConnectBlocker { -public: - ConnectBlocker(struct ev_loop *loop); - ~ConnectBlocker(); - - // Returns true if making connection is not allowed. - bool blocked() const; - // Call this function if connect operation succeeded. This will - // reset sleep_ to minimum value. - void on_success(); - // Call this function if connect operation failed. This will start - // timer and blocks connection establishment for sleep_ seconds. - void on_failure(); - -private: - ev_timer timer_; - struct ev_loop *loop_; - ev_tstamp sleep_; -}; - -} // namespace - -#endif // SHRPX_CONNECT_BLOCKER_H diff --git a/src/shrpx_connection.cc b/src/shrpx_connection.cc deleted file mode 100644 index 30c2ec9..0000000 --- a/src/shrpx_connection.cc +++ /dev/null @@ -1,333 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2015 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#include "shrpx_connection.h" - -#ifdef HAVE_UNISTD_H -#include -#endif // HAVE_UNISTD_H - -#include - -#include - -#include "memchunk.h" - -using namespace nghttp2; - -namespace shrpx { -Connection::Connection(struct ev_loop *loop, int fd, SSL *ssl, - ev_tstamp write_timeout, ev_tstamp read_timeout, - size_t write_rate, size_t write_burst, size_t read_rate, - size_t read_burst, IOCb writecb, IOCb readcb, - TimerCb timeoutcb, void *data) - : tls{ssl}, wlimit(loop, &wev, write_rate, write_burst), - rlimit(loop, &rev, read_rate, read_burst, ssl), writecb(writecb), - readcb(readcb), timeoutcb(timeoutcb), loop(loop), data(data), fd(fd) { - - ev_io_init(&wev, writecb, fd, EV_WRITE); - ev_io_init(&rev, readcb, fd, EV_READ); - - wev.data = this; - rev.data = this; - - ev_timer_init(&wt, timeoutcb, 0., write_timeout); - ev_timer_init(&rt, timeoutcb, 0., read_timeout); - - wt.data = this; - rt.data = this; - - // set 0. to double field explicitly just in case - tls.last_write_time = 0.; -} - -Connection::~Connection() { disconnect(); } - -void Connection::disconnect() { - ev_timer_stop(loop, &rt); - ev_timer_stop(loop, &wt); - - rlimit.stopw(); - wlimit.stopw(); - - if (tls.ssl) { - SSL_set_app_data(tls.ssl, nullptr); - SSL_set_shutdown(tls.ssl, SSL_RECEIVED_SHUTDOWN); - ERR_clear_error(); - SSL_shutdown(tls.ssl); - SSL_free(tls.ssl); - tls.ssl = nullptr; - } - - if (fd != -1) { - shutdown(fd, SHUT_WR); - close(fd); - fd = -1; - } -} - -int Connection::tls_handshake() { - auto rv = SSL_do_handshake(tls.ssl); - - if (rv == 0) { - return SHRPX_ERR_NETWORK; - } - - if (rv < 0) { - auto err = SSL_get_error(tls.ssl, rv); - switch (err) { - case SSL_ERROR_WANT_READ: - wlimit.stopw(); - ev_timer_stop(loop, &wt); - return SHRPX_ERR_INPROGRESS; - case SSL_ERROR_WANT_WRITE: - wlimit.startw(); - ev_timer_again(loop, &wt); - return SHRPX_ERR_INPROGRESS; - default: - return SHRPX_ERR_NETWORK; - } - } - - wlimit.stopw(); - ev_timer_stop(loop, &wt); - - tls.initial_handshake_done = true; - - if (LOG_ENABLED(INFO)) { - LOG(INFO) << "SSL/TLS handshake completed"; - if (SSL_session_reused(tls.ssl)) { - LOG(INFO) << "SSL/TLS session reused"; - } - } - - return 0; -} - -namespace { -const size_t SHRPX_SMALL_WRITE_LIMIT = 1300; -const size_t SHRPX_WARMUP_THRESHOLD = 1 << 20; -} // namespace - -size_t Connection::get_tls_write_limit() { - auto t = ev_now(loop); - - if (t - tls.last_write_time > 1.) { - // Time out, use small record size - tls.warmup_writelen = 0; - return SHRPX_SMALL_WRITE_LIMIT; - } - - if (tls.warmup_writelen >= SHRPX_WARMUP_THRESHOLD) { - return std::numeric_limits::max(); - } - - return SHRPX_SMALL_WRITE_LIMIT; -} - -void Connection::update_tls_warmup_writelen(size_t n) { - if (tls.warmup_writelen < SHRPX_WARMUP_THRESHOLD) { - tls.warmup_writelen += n; - } -} - -ssize_t Connection::write_tls(const void *data, size_t len) { - // SSL_write requires the same arguments (buf pointer and its - // length) on SSL_ERROR_WANT_READ or SSL_ERROR_WANT_WRITE. - // get_write_limit() may return smaller length than previously - // passed to SSL_write, which violates OpenSSL assumption. To avoid - // this, we keep last legnth passed to SSL_write to - // tls.last_writelen if SSL_write indicated I/O blocking. - if (tls.last_writelen == 0) { - len = std::min(len, wlimit.avail()); - len = std::min(len, get_tls_write_limit()); - if (len == 0) { - return 0; - } - } else { - len = tls.last_writelen; - tls.last_writelen = 0; - } - - auto rv = SSL_write(tls.ssl, data, len); - - if (rv == 0) { - return SHRPX_ERR_NETWORK; - } - - tls.last_write_time = ev_now(loop); - - if (rv < 0) { - auto err = SSL_get_error(tls.ssl, rv); - switch (err) { - case SSL_ERROR_WANT_READ: - if (LOG_ENABLED(INFO)) { - LOG(INFO) << "Close connection due to TLS renegotiation"; - } - return SHRPX_ERR_NETWORK; - case SSL_ERROR_WANT_WRITE: - tls.last_writelen = len; - wlimit.startw(); - ev_timer_again(loop, &wt); - return 0; - default: - if (LOG_ENABLED(INFO)) { - LOG(INFO) << "SSL_write: SSL_get_error returned " << err; - } - return SHRPX_ERR_NETWORK; - } - } - - wlimit.drain(rv); - - update_tls_warmup_writelen(rv); - - return rv; -} - -ssize_t Connection::read_tls(void *data, size_t len) { - // SSL_read requires the same arguments (buf pointer and its - // length) on SSL_ERROR_WANT_READ or SSL_ERROR_WANT_WRITE. - // rlimit_.avail() or rlimit_.avail() may return different length - // than the length previously passed to SSL_read, which violates - // OpenSSL assumption. To avoid this, we keep last legnth passed - // to SSL_read to tls_last_readlen_ if SSL_read indicated I/O - // blocking. - if (tls.last_readlen == 0) { - len = std::min(len, rlimit.avail()); - if (len == 0) { - return 0; - } - } else { - len = tls.last_readlen; - tls.last_readlen = 0; - } - - auto rv = SSL_read(tls.ssl, data, len); - - if (rv <= 0) { - auto err = SSL_get_error(tls.ssl, rv); - switch (err) { - case SSL_ERROR_WANT_READ: - tls.last_readlen = len; - return 0; - case SSL_ERROR_WANT_WRITE: - if (LOG_ENABLED(INFO)) { - LOG(INFO) << "Close connection due to TLS renegotiation"; - } - return SHRPX_ERR_NETWORK; - case SSL_ERROR_ZERO_RETURN: - return SHRPX_ERR_EOF; - default: - if (LOG_ENABLED(INFO)) { - LOG(INFO) << "SSL_read: SSL_get_error returned " << err; - } - return SHRPX_ERR_NETWORK; - } - } - - rlimit.drain(rv); - - return rv; -} - -ssize_t Connection::write_clear(const void *data, size_t len) { - len = std::min(len, wlimit.avail()); - if (len == 0) { - return 0; - } - - ssize_t nwrite; - while ((nwrite = write(fd, data, len)) == -1 && errno == EINTR) - ; - if (nwrite == -1) { - if (errno == EAGAIN || errno == EWOULDBLOCK) { - wlimit.startw(); - ev_timer_again(loop, &wt); - return 0; - } - return SHRPX_ERR_NETWORK; - } - - wlimit.drain(nwrite); - - return nwrite; -} - -ssize_t Connection::writev_clear(struct iovec *iov, int iovcnt) { - iovcnt = limit_iovec(iov, iovcnt, wlimit.avail()); - if (iovcnt == 0) { - return 0; - } - - ssize_t nwrite; - while ((nwrite = writev(fd, iov, iovcnt)) == -1 && errno == EINTR) - ; - if (nwrite == -1) { - if (errno == EAGAIN || errno == EWOULDBLOCK) { - wlimit.startw(); - ev_timer_again(loop, &wt); - return 0; - } - return SHRPX_ERR_NETWORK; - } - - wlimit.drain(nwrite); - - return nwrite; -} - -ssize_t Connection::read_clear(void *data, size_t len) { - len = std::min(len, rlimit.avail()); - if (len == 0) { - return 0; - } - - ssize_t nread; - while ((nread = read(fd, data, len)) == -1 && errno == EINTR) - ; - if (nread == -1) { - if (errno == EAGAIN || errno == EWOULDBLOCK) { - return 0; - } - return SHRPX_ERR_NETWORK; - } - - if (nread == 0) { - return SHRPX_ERR_EOF; - } - - rlimit.drain(nread); - - return nread; -} - -void Connection::handle_tls_pending_read() { - if (!ev_is_active(&rev)) { - return; - } - rlimit.handle_tls_pending_read(); -} - -} // namespace shrpx diff --git a/src/shrpx_connection.h b/src/shrpx_connection.h deleted file mode 100644 index 055d32c..0000000 --- a/src/shrpx_connection.h +++ /dev/null @@ -1,109 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2015 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#ifndef SHRPX_CONNECTION_H -#define SHRPX_CONNECTION_H - -#include "shrpx_config.h" - -#include - -#include - -#include - -#include "shrpx_rate_limit.h" -#include "shrpx_error.h" - -namespace shrpx { - -struct TLSConnection { - SSL *ssl; - ev_tstamp last_write_time; - size_t warmup_writelen; - // length passed to SSL_write and SSL_read last time. This is - // required since these functions require the exact same parameters - // on non-blocking I/O. - size_t last_writelen, last_readlen; - bool initial_handshake_done; - bool reneg_started; -}; - -template using EVCb = void (*)(struct ev_loop *, T *, int); - -using IOCb = EVCb; -using TimerCb = EVCb; - -struct Connection { - Connection(struct ev_loop *loop, int fd, SSL *ssl, ev_tstamp write_timeout, - ev_tstamp read_timeout, size_t write_rate, size_t write_burst, - size_t read_rate, size_t read_burst, IOCb writecb, IOCb readcb, - TimerCb timeoutcb, void *data); - ~Connection(); - - void disconnect(); - - int tls_handshake(); - - // All write_* and writev_clear functions return number of bytes - // written. If nothing cannot be written (e.g., there is no - // allowance in RateLimit or underlying connection blocks), return - // 0. SHRPX_ERR_NETWORK is returned in case of error. - // - // All read_* functions return number of bytes read. If nothing - // cannot be read (e.g., there is no allowance in Ratelimit or - // underlying connection blocks), return 0. SHRPX_ERR_EOF is - // returned in case of EOF and no data was read. Otherwise - // SHRPX_ERR_NETWORK is return in case of error. - ssize_t write_tls(const void *data, size_t len); - ssize_t read_tls(void *data, size_t len); - - size_t get_tls_write_limit(); - // Updates the number of bytes written in warm up period. - void update_tls_warmup_writelen(size_t n); - - ssize_t write_clear(const void *data, size_t len); - ssize_t writev_clear(struct iovec *iov, int iovcnt); - ssize_t read_clear(void *data, size_t len); - - void handle_tls_pending_read(); - - TLSConnection tls; - ev_io wev; - ev_io rev; - ev_timer wt; - ev_timer rt; - RateLimit wlimit; - RateLimit rlimit; - IOCb writecb; - IOCb readcb; - TimerCb timeoutcb; - struct ev_loop *loop; - void *data; - int fd; -}; - -} // namespace shrpx - -#endif // SHRPX_CONNECTION_H diff --git a/src/shrpx_connection_handler.cc b/src/shrpx_connection_handler.cc deleted file mode 100644 index ceebf61..0000000 --- a/src/shrpx_connection_handler.cc +++ /dev/null @@ -1,539 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2012 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#include "shrpx_connection_handler.h" - -#ifdef HAVE_UNISTD_H -#include -#endif // HAVE_UNISTD_H -#include -#include - -#include -#include - -#include "shrpx_client_handler.h" -#include "shrpx_ssl.h" -#include "shrpx_worker.h" -#include "shrpx_config.h" -#include "shrpx_http2_session.h" -#include "shrpx_connect_blocker.h" -#include "shrpx_downstream_connection.h" -#include "shrpx_accept_handler.h" -#include "util.h" -#include "template.h" - -using namespace nghttp2; - -namespace shrpx { - -namespace { -void acceptor_disable_cb(struct ev_loop *loop, ev_timer *w, int revent) { - auto h = static_cast(w->data); - - // If we are in graceful shutdown period, we must not enable - // acceptors again. - if (h->get_graceful_shutdown()) { - return; - } - - h->enable_acceptor(); -} -} // namespace - -namespace { -void ocsp_cb(struct ev_loop *loop, ev_timer *w, int revent) { - auto h = static_cast(w->data); - - // If we are in graceful shutdown period, we won't do ocsp query. - if (h->get_graceful_shutdown()) { - return; - } - - LOG(NOTICE) << "Start ocsp update"; - - h->proceed_next_cert_ocsp(); -} -} // namespace - -namespace { -void ocsp_read_cb(struct ev_loop *loop, ev_io *w, int revent) { - auto h = static_cast(w->data); - - h->read_ocsp_chunk(); -} -} // namespace - -namespace { -void ocsp_chld_cb(struct ev_loop *loop, ev_child *w, int revent) { - auto h = static_cast(w->data); - - h->handle_ocsp_complete(); -} -} // namespace - -ConnectionHandler::ConnectionHandler(struct ev_loop *loop) - : single_worker_(nullptr), loop_(loop), worker_round_robin_cnt_(0), - graceful_shutdown_(false) { - ev_timer_init(&disable_acceptor_timer_, acceptor_disable_cb, 0., 0.); - disable_acceptor_timer_.data = this; - - ev_timer_init(&ocsp_timer_, ocsp_cb, 0., 0.); - ocsp_timer_.data = this; - - ev_io_init(&ocsp_.rev, ocsp_read_cb, -1, EV_READ); - ocsp_.rev.data = this; - - ev_child_init(&ocsp_.chldev, ocsp_chld_cb, 0, 0); - ocsp_.chldev.data = this; - - ocsp_.next = 0; - ocsp_.fd = -1; - - reset_ocsp(); -} - -ConnectionHandler::~ConnectionHandler() { - ev_timer_stop(loop_, &disable_acceptor_timer_); - ev_timer_stop(loop_, &ocsp_timer_); -} - -void ConnectionHandler::worker_reopen_log_files() { - WorkerEvent wev; - - memset(&wev, 0, sizeof(wev)); - wev.type = REOPEN_LOG; - - for (auto &worker : workers_) { - worker->send(wev); - } -} - -void ConnectionHandler::worker_renew_ticket_keys( - const std::shared_ptr &ticket_keys) { - WorkerEvent wev; - - memset(&wev, 0, sizeof(wev)); - wev.type = RENEW_TICKET_KEYS; - wev.ticket_keys = ticket_keys; - - for (auto &worker : workers_) { - worker->send(wev); - } -} - -void ConnectionHandler::create_single_worker() { - auto cert_tree = ssl::create_cert_lookup_tree(); - auto sv_ssl_ctx = ssl::setup_server_ssl_context(all_ssl_ctx_, cert_tree); - auto cl_ssl_ctx = ssl::setup_client_ssl_context(); - - single_worker_ = make_unique(loop_, sv_ssl_ctx, cl_ssl_ctx, cert_tree, - ticket_keys_); -} - -void ConnectionHandler::create_worker_thread(size_t num) { -#ifndef NOTHREADS - assert(workers_.size() == 0); - - auto cert_tree = ssl::create_cert_lookup_tree(); - auto sv_ssl_ctx = ssl::setup_server_ssl_context(all_ssl_ctx_, cert_tree); - auto cl_ssl_ctx = ssl::setup_client_ssl_context(); - - for (size_t i = 0; i < num; ++i) { - auto loop = ev_loop_new(0); - - auto worker = make_unique(loop, sv_ssl_ctx, cl_ssl_ctx, cert_tree, - ticket_keys_); - worker->run_async(); - workers_.push_back(std::move(worker)); - - if (LOG_ENABLED(INFO)) { - LLOG(INFO, this) << "Created thread #" << workers_.size() - 1; - } - } -#endif // NOTHREADS -} - -void ConnectionHandler::join_worker() { -#ifndef NOTHREADS - int n = 0; - - if (LOG_ENABLED(INFO)) { - LLOG(INFO, this) << "Waiting for worker thread to join: n=" - << workers_.size(); - } - - for (auto &worker : workers_) { - worker->wait(); - if (LOG_ENABLED(INFO)) { - LLOG(INFO, this) << "Thread #" << n << " joined"; - } - ++n; - } -#endif // NOTHREADS -} - -void ConnectionHandler::graceful_shutdown_worker() { - if (get_config()->num_worker == 1) { - return; - } - - WorkerEvent wev; - memset(&wev, 0, sizeof(wev)); - wev.type = GRACEFUL_SHUTDOWN; - - if (LOG_ENABLED(INFO)) { - LLOG(INFO, this) << "Sending graceful shutdown signal to worker"; - } - - for (auto &worker : workers_) { - - worker->send(wev); - } -} - -int ConnectionHandler::handle_connection(int fd, sockaddr *addr, int addrlen) { - if (LOG_ENABLED(INFO)) { - LLOG(INFO, this) << "Accepted connection. fd=" << fd; - } - - if (get_config()->num_worker == 1) { - - if (single_worker_->get_worker_stat()->num_connections >= - get_config()->worker_frontend_connections) { - - if (LOG_ENABLED(INFO)) { - LLOG(INFO, this) << "Too many connections >=" - << get_config()->worker_frontend_connections; - } - - close(fd); - return -1; - } - - auto client = - ssl::accept_connection(single_worker_.get(), fd, addr, addrlen); - if (!client) { - LLOG(ERROR, this) << "ClientHandler creation failed"; - - close(fd); - return -1; - } - - return 0; - } - - size_t idx = worker_round_robin_cnt_ % workers_.size(); - if (LOG_ENABLED(INFO)) { - LOG(INFO) << "Dispatch connection to worker #" << idx; - } - ++worker_round_robin_cnt_; - WorkerEvent wev; - memset(&wev, 0, sizeof(wev)); - wev.type = NEW_CONNECTION; - wev.client_fd = fd; - memcpy(&wev.client_addr, addr, addrlen); - wev.client_addrlen = addrlen; - - workers_[idx]->send(wev); - - return 0; -} - -struct ev_loop *ConnectionHandler::get_loop() const { - return loop_; -} - -Worker *ConnectionHandler::get_single_worker() const { - return single_worker_.get(); -} - -void ConnectionHandler::set_acceptor(std::unique_ptr h) { - acceptor_ = std::move(h); -} - -AcceptHandler *ConnectionHandler::get_acceptor() const { - return acceptor_.get(); -} - -void ConnectionHandler::set_acceptor6(std::unique_ptr h) { - acceptor6_ = std::move(h); -} - -AcceptHandler *ConnectionHandler::get_acceptor6() const { - return acceptor6_.get(); -} - -void ConnectionHandler::enable_acceptor() { - if (acceptor_) { - acceptor_->enable(); - } - - if (acceptor6_) { - acceptor6_->enable(); - } -} - -void ConnectionHandler::disable_acceptor() { - if (acceptor_) { - acceptor_->disable(); - } - - if (acceptor6_) { - acceptor6_->disable(); - } -} - -void ConnectionHandler::disable_acceptor_temporary(ev_tstamp t) { - if (t == 0. || ev_is_active(&disable_acceptor_timer_)) { - return; - } - - disable_acceptor(); - - ev_timer_set(&disable_acceptor_timer_, t, 0.); - ev_timer_start(loop_, &disable_acceptor_timer_); -} - -void ConnectionHandler::accept_pending_connection() { - if (acceptor_) { - acceptor_->accept_connection(); - } - if (acceptor6_) { - acceptor6_->accept_connection(); - } -} - -void -ConnectionHandler::set_ticket_keys(std::shared_ptr ticket_keys) { - ticket_keys_ = std::move(ticket_keys); - if (single_worker_) { - single_worker_->set_ticket_keys(ticket_keys_); - } -} - -const std::shared_ptr &ConnectionHandler::get_ticket_keys() const { - return ticket_keys_; -} - -void ConnectionHandler::set_graceful_shutdown(bool f) { - graceful_shutdown_ = f; - if (single_worker_) { - single_worker_->set_graceful_shutdown(f); - } -} - -bool ConnectionHandler::get_graceful_shutdown() const { - return graceful_shutdown_; -} - -void ConnectionHandler::cancel_ocsp_update() { - if (ocsp_.pid == 0) { - return; - } - - kill(ocsp_.pid, SIGTERM); -} - -// inspired by h2o_read_command function from h2o project: -// https://github.com/h2o/h2o -int ConnectionHandler::start_ocsp_update(const char *cert_file) { - int rv; - int pfd[2]; - - if (LOG_ENABLED(INFO)) { - LOG(INFO) << "Start ocsp update for " << cert_file; - } - - assert(!ev_is_active(&ocsp_.rev)); - assert(!ev_is_active(&ocsp_.chldev)); - - char *const argv[] = { - const_cast(get_config()->fetch_ocsp_response_file.get()), - const_cast(cert_file), nullptr}; - char *const envp[] = {nullptr}; - -#ifdef O_CLOEXEC - if (pipe2(pfd, O_CLOEXEC) == -1) { - return -1; - } -#else // !O_CLOEXEC - if (pipe(pfd) == -1) { - return -1; - } - util::make_socket_closeonexec(pfd[0]); - util::make_socket_closeonexec(pfd[1]); -#endif // !O_CLOEXEC - - auto closer = defer([&pfd]() { - if (pfd[0] != -1) { - close(pfd[0]); - } - - if (pfd[1] != -1) { - close(pfd[1]); - } - }); - - auto pid = fork(); - if (pid == -1) { - auto error = errno; - LOG(WARN) << "Could not execute ocsp query command for " << cert_file - << ": " << argv[0] << ", fork() failed, errno=" << error; - return -1; - } - - if (pid == 0) { - // child process - dup2(pfd[1], 1); - close(pfd[0]); - - rv = execve(argv[0], argv, envp); - if (rv == -1) { - auto error = errno; - LOG(WARN) << "Could not execute ocsp query command: " << argv[0] - << ", execve() faild, errno=" << error; - _Exit(EXIT_FAILURE); - } - // unreachable - } - - // parent process - close(pfd[1]); - pfd[1] = -1; - - ocsp_.pid = pid; - ocsp_.fd = pfd[0]; - pfd[0] = -1; - - util::make_socket_nonblocking(ocsp_.fd); - ev_io_set(&ocsp_.rev, ocsp_.fd, EV_READ); - ev_io_start(loop_, &ocsp_.rev); - - ev_child_set(&ocsp_.chldev, ocsp_.pid, 0); - ev_child_start(loop_, &ocsp_.chldev); - - return 0; -} - -void ConnectionHandler::read_ocsp_chunk() { - std::array buf; - for (;;) { - ssize_t n; - while ((n = read(ocsp_.fd, buf.data(), buf.size())) == -1 && errno == EINTR) - ; - - if (n == -1) { - if (errno == EAGAIN || errno == EWOULDBLOCK) { - return; - } - auto error = errno; - LOG(WARN) << "Reading from ocsp query command failed: errno=" << error; - ocsp_.error = error; - - break; - } - - if (n == 0) { - break; - } - - std::copy_n(std::begin(buf), n, std::back_inserter(ocsp_.resp)); - } - - ev_io_stop(loop_, &ocsp_.rev); -} - -void ConnectionHandler::handle_ocsp_complete() { - ev_io_stop(loop_, &ocsp_.rev); - ev_child_stop(loop_, &ocsp_.chldev); - - assert(ocsp_.next < all_ssl_ctx_.size()); - - auto ssl_ctx = all_ssl_ctx_[ocsp_.next]; - auto tls_ctx_data = - static_cast(SSL_CTX_get_app_data(ssl_ctx)); - - auto rstatus = ocsp_.chldev.rstatus; - auto status = WEXITSTATUS(rstatus); - if (ocsp_.error || !WIFEXITED(rstatus) || status != 0) { - LOG(WARN) << "ocsp query command for " << tls_ctx_data->cert_file - << " failed: error=" << ocsp_.error << ", rstatus=" << rstatus - << ", status=" << status; - ++ocsp_.next; - proceed_next_cert_ocsp(); - return; - } - - if (LOG_ENABLED(INFO)) { - LOG(INFO) << "ocsp update for " << tls_ctx_data->cert_file - << " finished successfully"; - } - - { - std::lock_guard g(tls_ctx_data->mu); - tls_ctx_data->ocsp_data = std::move(ocsp_.resp); - } - - ++ocsp_.next; - proceed_next_cert_ocsp(); -} - -void ConnectionHandler::reset_ocsp() { - if (ocsp_.fd != -1) { - close(ocsp_.fd); - } - - ocsp_.fd = -1; - ocsp_.pid = 0; - ocsp_.error = 0; - ocsp_.resp = std::vector(); -} - -void ConnectionHandler::proceed_next_cert_ocsp() { - for (;;) { - reset_ocsp(); - if (ocsp_.next == all_ssl_ctx_.size()) { - ocsp_.next = 0; - // We have updated all ocsp response, and schedule next update. - ev_timer_set(&ocsp_timer_, get_config()->ocsp_update_interval, 0.); - ev_timer_start(loop_, &ocsp_timer_); - return; - } - - auto ssl_ctx = all_ssl_ctx_[ocsp_.next]; - auto tls_ctx_data = - static_cast(SSL_CTX_get_app_data(ssl_ctx)); - auto cert_file = tls_ctx_data->cert_file; - - if (start_ocsp_update(cert_file) != 0) { - ++ocsp_.next; - continue; - } - - break; - } -} - -} // namespace shrpx diff --git a/src/shrpx_connection_handler.h b/src/shrpx_connection_handler.h deleted file mode 100644 index 00e4be4..0000000 --- a/src/shrpx_connection_handler.h +++ /dev/null @@ -1,138 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2012 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#ifndef SHRPX_CONNECTION_HANDLER_H -#define SHRPX_CONNECTION_HANDLER_H - -#include "shrpx.h" - -#include -#ifdef HAVE_SYS_SOCKET_H -#include -#endif // HAVE_SYS_SOCKET_H - -#include -#include - -#include - -#include - -#include "shrpx_downstream_connection_pool.h" - -namespace shrpx { - -class Http2Session; -class ConnectBlocker; -class AcceptHandler; -class Worker; -struct WorkerStat; -struct TicketKeys; - -struct OCSPUpdateContext { - // ocsp response buffer - std::vector resp; - // index to ConnectionHandler::all_ssl_ctx_, which points to next - // SSL_CTX to update ocsp response cache. - size_t next; - ev_child chldev; - ev_io rev; - // fd to read response from fetch-ocsp-response script - int fd; - // errno encountered while processing response - int error; - // pid of forked fetch-ocsp-response script process - pid_t pid; -}; - -class ConnectionHandler { -public: - ConnectionHandler(struct ev_loop *loop); - ~ConnectionHandler(); - int handle_connection(int fd, sockaddr *addr, int addrlen); - // Creates Worker object for single threaded configuration. - void create_single_worker(); - // Creates |num| Worker objects for multi threaded configuration. - // The |num| must be strictly more than 1. - void create_worker_thread(size_t num); - void worker_reopen_log_files(); - void worker_renew_ticket_keys(const std::shared_ptr &ticket_keys); - void set_ticket_keys(std::shared_ptr ticket_keys); - const std::shared_ptr &get_ticket_keys() const; - struct ev_loop *get_loop() const; - Worker *get_single_worker() const; - void set_acceptor(std::unique_ptr h); - AcceptHandler *get_acceptor() const; - void set_acceptor6(std::unique_ptr h); - AcceptHandler *get_acceptor6() const; - void enable_acceptor(); - void disable_acceptor(); - void disable_acceptor_temporary(ev_tstamp t); - void accept_pending_connection(); - void graceful_shutdown_worker(); - void set_graceful_shutdown(bool f); - bool get_graceful_shutdown() const; - void join_worker(); - - // Cancels ocsp update process - void cancel_ocsp_update(); - // Starts ocsp update for certficate |cert_file|. - int start_ocsp_update(const char *cert_file); - // Reads incoming data from ocsp update process - void read_ocsp_chunk(); - // Handles the completion of one ocsp update - void handle_ocsp_complete(); - // Resets ocsp_; - void reset_ocsp(); - // Proceeds to the next certificate's ocsp update. If all - // certificates' ocsp update has been done, schedule next ocsp - // update. - void proceed_next_cert_ocsp(); - -private: - std::vector all_ssl_ctx_; - OCSPUpdateContext ocsp_; - // Worker instances when multi threaded mode (-nN, N >= 2) is used. - std::vector> workers_; - // Worker instance used when single threaded mode (-n1) is used. - // Otherwise, nullptr and workers_ has instances of Worker instead. - std::unique_ptr single_worker_; - // Current TLS session ticket keys. Note that TLS connection does - // not refer to this field directly. They use TicketKeys object in - // Worker object. - std::shared_ptr ticket_keys_; - struct ev_loop *loop_; - // acceptor for IPv4 address or UNIX domain socket. - std::unique_ptr acceptor_; - // acceptor for IPv6 address - std::unique_ptr acceptor6_; - ev_timer disable_acceptor_timer_; - ev_timer ocsp_timer_; - unsigned int worker_round_robin_cnt_; - bool graceful_shutdown_; -}; - -} // namespace shrpx - -#endif // SHRPX_CONNECTION_HANDLER_H diff --git a/src/shrpx_downstream.cc b/src/shrpx_downstream.cc deleted file mode 100644 index 6042878..0000000 --- a/src/shrpx_downstream.cc +++ /dev/null @@ -1,1218 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2012 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#include "shrpx_downstream.h" - -#include - -#include "http-parser/http_parser.h" - -#include "shrpx_upstream.h" -#include "shrpx_client_handler.h" -#include "shrpx_config.h" -#include "shrpx_error.h" -#include "shrpx_downstream_connection.h" -#include "shrpx_downstream_queue.h" -#include "util.h" -#include "http2.h" - -namespace shrpx { - -namespace { -void upstream_timeoutcb(struct ev_loop *loop, ev_timer *w, int revents) { - auto downstream = static_cast(w->data); - auto upstream = downstream->get_upstream(); - - auto which = revents == EV_READ ? "read" : "write"; - - if (LOG_ENABLED(INFO)) { - DLOG(INFO, downstream) << "upstream timeout stream_id=" - << downstream->get_stream_id() << " event=" << which; - } - - downstream->disable_upstream_rtimer(); - downstream->disable_upstream_wtimer(); - - upstream->on_timeout(downstream); -} -} // namespace - -namespace { -void upstream_rtimeoutcb(struct ev_loop *loop, ev_timer *w, int revents) { - upstream_timeoutcb(loop, w, EV_READ); -} -} // namespace - -namespace { -void upstream_wtimeoutcb(struct ev_loop *loop, ev_timer *w, int revents) { - upstream_timeoutcb(loop, w, EV_WRITE); -} -} // namespace - -namespace { -void downstream_timeoutcb(struct ev_loop *loop, ev_timer *w, int revents) { - auto downstream = static_cast(w->data); - - auto which = revents == EV_READ ? "read" : "write"; - - if (LOG_ENABLED(INFO)) { - DLOG(INFO, downstream) << "downstream timeout stream_id=" - << downstream->get_downstream_stream_id() - << " event=" << which; - } - - downstream->disable_downstream_rtimer(); - downstream->disable_downstream_wtimer(); - - auto dconn = downstream->get_downstream_connection(); - - if (dconn) { - dconn->on_timeout(); - } -} -} // namespace - -namespace { -void downstream_rtimeoutcb(struct ev_loop *loop, ev_timer *w, int revents) { - downstream_timeoutcb(loop, w, EV_READ); -} -} // namespace - -namespace { -void downstream_wtimeoutcb(struct ev_loop *loop, ev_timer *w, int revents) { - downstream_timeoutcb(loop, w, EV_WRITE); -} -} // namespace - -// upstream could be nullptr for unittests -Downstream::Downstream(Upstream *upstream, MemchunkPool *mcpool, - int32_t stream_id, int32_t priority) - : dlnext(nullptr), dlprev(nullptr), - request_start_time_(std::chrono::high_resolution_clock::now()), - request_buf_(mcpool), response_buf_(mcpool), request_bodylen_(0), - response_bodylen_(0), response_sent_bodylen_(0), - request_content_length_(-1), response_content_length_(-1), - upstream_(upstream), blocked_link_(nullptr), request_headers_sum_(0), - response_headers_sum_(0), request_datalen_(0), response_datalen_(0), - num_retry_(0), stream_id_(stream_id), priority_(priority), - downstream_stream_id_(-1), - response_rst_stream_error_code_(NGHTTP2_NO_ERROR), - request_state_(INITIAL), request_major_(1), request_minor_(1), - response_state_(INITIAL), response_http_status_(0), response_major_(1), - response_minor_(1), dispatch_state_(DISPATCH_NONE), - upgrade_request_(false), upgraded_(false), http2_upgrade_seen_(false), - chunked_request_(false), request_connection_close_(false), - request_header_key_prev_(false), request_trailer_key_prev_(false), - request_http2_expect_body_(false), chunked_response_(false), - response_connection_close_(false), response_header_key_prev_(false), - response_trailer_key_prev_(false), expect_final_response_(false), - request_pending_(false) { - - ev_timer_init(&upstream_rtimer_, &upstream_rtimeoutcb, 0., - get_config()->stream_read_timeout); - ev_timer_init(&upstream_wtimer_, &upstream_wtimeoutcb, 0., - get_config()->stream_write_timeout); - ev_timer_init(&downstream_rtimer_, &downstream_rtimeoutcb, 0., - get_config()->stream_read_timeout); - ev_timer_init(&downstream_wtimer_, &downstream_wtimeoutcb, 0., - get_config()->stream_write_timeout); - - upstream_rtimer_.data = this; - upstream_wtimer_.data = this; - downstream_rtimer_.data = this; - downstream_wtimer_.data = this; - - http2::init_hdidx(request_hdidx_); - http2::init_hdidx(response_hdidx_); -} - -Downstream::~Downstream() { - if (LOG_ENABLED(INFO)) { - DLOG(INFO, this) << "Deleting"; - } - - if (blocked_link_) { - detach_blocked_link(blocked_link_); - } - - // check nullptr for unittest - if (upstream_) { - auto loop = upstream_->get_client_handler()->get_loop(); - - ev_timer_stop(loop, &upstream_rtimer_); - ev_timer_stop(loop, &upstream_wtimer_); - ev_timer_stop(loop, &downstream_rtimer_); - ev_timer_stop(loop, &downstream_wtimer_); - } - - if (LOG_ENABLED(INFO)) { - DLOG(INFO, this) << "Deleted"; - } -} - -int Downstream::attach_downstream_connection( - std::unique_ptr dconn) { - if (dconn->attach_downstream(this) != 0) { - return -1; - } - - dconn_ = std::move(dconn); - - return 0; -} - -void Downstream::detach_downstream_connection() { - if (!dconn_) { - return; - } - - dconn_->detach_downstream(this); - - auto handler = dconn_->get_client_handler(); - - handler->pool_downstream_connection( - std::unique_ptr(dconn_.release())); -} - -void Downstream::release_downstream_connection() { dconn_.release(); } - -DownstreamConnection *Downstream::get_downstream_connection() { - return dconn_.get(); -} - -std::unique_ptr Downstream::pop_downstream_connection() { - return std::unique_ptr(dconn_.release()); -} - -void Downstream::pause_read(IOCtrlReason reason) { - if (dconn_) { - dconn_->pause_read(reason); - } -} - -int Downstream::resume_read(IOCtrlReason reason, size_t consumed) { - if (dconn_) { - return dconn_->resume_read(reason, consumed); - } - - return 0; -} - -void Downstream::force_resume_read() { - if (dconn_) { - dconn_->force_resume_read(); - } -} - -namespace { -const Headers::value_type *get_header_linear(const Headers &headers, - const std::string &name) { - const Headers::value_type *res = nullptr; - for (auto &kv : headers) { - if (kv.name == name) { - res = &kv; - } - } - return res; -} -} // namespace - -const Headers &Downstream::get_request_headers() const { - return request_headers_; -} - -void Downstream::assemble_request_cookie() { - std::string &cookie = assembled_request_cookie_; - cookie = ""; - for (auto &kv : request_headers_) { - if (kv.name.size() != 6 || kv.name[5] != 'e' || - !util::streq_l("cooki", kv.name.c_str(), 5)) { - continue; - } - - auto end = kv.value.find_last_not_of(" ;"); - if (end == std::string::npos) { - cookie += kv.value; - } else { - cookie.append(std::begin(kv.value), std::begin(kv.value) + end + 1); - } - cookie += "; "; - } - if (cookie.size() >= 2) { - cookie.erase(cookie.size() - 2); - } -} - -Headers Downstream::crumble_request_cookie() { - Headers cookie_hdrs; - for (auto &kv : request_headers_) { - if (kv.name.size() != 6 || kv.name[5] != 'e' || - !util::streq_l("cooki", kv.name.c_str(), 5)) { - continue; - } - size_t last = kv.value.size(); - - for (size_t j = 0; j < last;) { - j = kv.value.find_first_not_of("\t ;", j); - if (j == std::string::npos) { - break; - } - auto first = j; - - j = kv.value.find(';', j); - if (j == std::string::npos) { - j = last; - } - - cookie_hdrs.push_back( - Header("cookie", kv.value.substr(first, j - first), kv.no_index)); - } - } - return cookie_hdrs; -} - -const std::string &Downstream::get_assembled_request_cookie() const { - return assembled_request_cookie_; -} - -namespace { -void add_header(bool &key_prev, size_t &sum, Headers &headers, std::string name, - std::string value) { - key_prev = true; - sum += name.size() + value.size(); - headers.emplace_back(std::move(name), std::move(value)); -} -} // namespace - -namespace { -void append_last_header_key(bool key_prev, size_t &sum, Headers &headers, - const char *data, size_t len) { - assert(key_prev); - sum += len; - auto &item = headers.back(); - item.name.append(data, len); -} -} // namespace - -namespace { -void append_last_header_value(bool key_prev, size_t &sum, Headers &headers, - const char *data, size_t len) { - assert(!key_prev); - sum += len; - auto &item = headers.back(); - item.value.append(data, len); -} -} // namespace - -namespace { -void set_last_header_value(bool &key_prev, size_t &sum, Headers &headers, - const char *data, size_t len) { - key_prev = false; - sum += len; - auto &item = headers.back(); - item.value.assign(data, len); -} -} // namespace - -namespace { -int index_headers(http2::HeaderIndex &hdidx, Headers &headers, - int64_t &content_length) { - for (size_t i = 0; i < headers.size(); ++i) { - auto &kv = headers[i]; - util::inp_strlower(kv.name); - - auto token = http2::lookup_token( - reinterpret_cast(kv.name.c_str()), kv.name.size()); - if (token < 0) { - continue; - } - - kv.token = token; - http2::index_header(hdidx, token, i); - - if (token == http2::HD_CONTENT_LENGTH) { - auto len = util::parse_uint(kv.value); - if (len == -1) { - return -1; - } - if (content_length != -1) { - return -1; - } - content_length = len; - } - } - return 0; -} -} // namespace - -int Downstream::index_request_headers() { - return index_headers(request_hdidx_, request_headers_, - request_content_length_); -} - -const Headers::value_type *Downstream::get_request_header(int16_t token) const { - return http2::get_header(request_hdidx_, token, request_headers_); -} - -const Headers::value_type * -Downstream::get_request_header(const std::string &name) const { - return get_header_linear(request_headers_, name); -} - -void Downstream::add_request_header(std::string name, std::string value) { - add_header(request_header_key_prev_, request_headers_sum_, request_headers_, - std::move(name), std::move(value)); -} - -void Downstream::set_last_request_header_value(const char *data, size_t len) { - set_last_header_value(request_header_key_prev_, request_headers_sum_, - request_headers_, data, len); -} - -void Downstream::add_request_header(std::string name, std::string value, - int16_t token) { - http2::index_header(request_hdidx_, token, request_headers_.size()); - request_headers_sum_ += name.size() + value.size(); - request_headers_.emplace_back(std::move(name), std::move(value), false, - token); -} - -void Downstream::add_request_header(const uint8_t *name, size_t namelen, - const uint8_t *value, size_t valuelen, - bool no_index, int16_t token) { - http2::index_header(request_hdidx_, token, request_headers_.size()); - request_headers_sum_ += namelen + valuelen; - http2::add_header(request_headers_, name, namelen, value, valuelen, no_index, - token); -} - -bool Downstream::get_request_header_key_prev() const { - return request_header_key_prev_; -} - -void Downstream::append_last_request_header_key(const char *data, size_t len) { - append_last_header_key(request_header_key_prev_, request_headers_sum_, - request_headers_, data, len); -} - -void Downstream::append_last_request_header_value(const char *data, - size_t len) { - append_last_header_value(request_header_key_prev_, request_headers_sum_, - request_headers_, data, len); -} - -void Downstream::clear_request_headers() { - Headers().swap(request_headers_); - http2::init_hdidx(request_hdidx_); -} - -size_t Downstream::get_request_headers_sum() const { - return request_headers_sum_; -} - -void Downstream::add_request_trailer(const uint8_t *name, size_t namelen, - const uint8_t *value, size_t valuelen, - bool no_index, int16_t token) { - // we never index trailer part. Header size limit should be applied - // to all request header fields combined. - request_headers_sum_ += namelen + valuelen; - http2::add_header(request_trailers_, name, namelen, value, valuelen, no_index, - -1); -} - -const Headers &Downstream::get_request_trailers() const { - return request_trailers_; -} - -void Downstream::add_request_trailer(std::string name, std::string value) { - add_header(request_trailer_key_prev_, request_headers_sum_, request_trailers_, - std::move(name), std::move(value)); -} - -void Downstream::set_last_request_trailer_value(const char *data, size_t len) { - set_last_header_value(request_trailer_key_prev_, request_headers_sum_, - request_trailers_, data, len); -} - -bool Downstream::get_request_trailer_key_prev() const { - return request_trailer_key_prev_; -} - -void Downstream::append_last_request_trailer_key(const char *data, size_t len) { - append_last_header_key(request_trailer_key_prev_, request_headers_sum_, - request_trailers_, data, len); -} - -void Downstream::append_last_request_trailer_value(const char *data, - size_t len) { - append_last_header_value(request_trailer_key_prev_, request_headers_sum_, - request_trailers_, data, len); -} - -void Downstream::set_request_method(std::string method) { - request_method_ = std::move(method); -} - -const std::string &Downstream::get_request_method() const { - return request_method_; -} - -void Downstream::set_request_path(std::string path) { - request_path_ = std::move(path); -} - -void Downstream::append_request_path(const char *data, size_t len) { - request_path_.append(data, len); -} - -const std::string &Downstream::get_request_path() const { - return request_path_; -} - -void Downstream::set_request_start_time( - std::chrono::high_resolution_clock::time_point time) { - request_start_time_ = std::move(time); -} - -const std::chrono::high_resolution_clock::time_point & -Downstream::get_request_start_time() const { - return request_start_time_; -} - -const std::string &Downstream::get_request_http2_scheme() const { - return request_http2_scheme_; -} - -void Downstream::set_request_http2_scheme(std::string scheme) { - request_http2_scheme_ = std::move(scheme); -} - -const std::string &Downstream::get_request_http2_authority() const { - return request_http2_authority_; -} - -void Downstream::set_request_http2_authority(std::string authority) { - request_http2_authority_ = std::move(authority); -} - -void Downstream::set_request_major(int major) { request_major_ = major; } - -void Downstream::set_request_minor(int minor) { request_minor_ = minor; } - -int Downstream::get_request_major() const { return request_major_; } - -int Downstream::get_request_minor() const { return request_minor_; } - -void Downstream::reset_upstream(Upstream *upstream) { - upstream_ = upstream; - if (dconn_) { - dconn_->on_upstream_change(upstream); - } -} - -Upstream *Downstream::get_upstream() const { return upstream_; } - -void Downstream::set_stream_id(int32_t stream_id) { stream_id_ = stream_id; } - -int32_t Downstream::get_stream_id() const { return stream_id_; } - -void Downstream::set_request_state(int state) { request_state_ = state; } - -int Downstream::get_request_state() const { return request_state_; } - -bool Downstream::get_chunked_request() const { return chunked_request_; } - -void Downstream::set_chunked_request(bool f) { chunked_request_ = f; } - -bool Downstream::get_request_connection_close() const { - return request_connection_close_; -} - -void Downstream::set_request_connection_close(bool f) { - request_connection_close_ = f; -} - -bool Downstream::get_request_http2_expect_body() const { - return request_http2_expect_body_; -} - -void Downstream::set_request_http2_expect_body(bool f) { - request_http2_expect_body_ = f; -} - -bool Downstream::request_buf_full() { - if (dconn_) { - return request_buf_.rleft() >= get_config()->downstream_request_buffer_size; - } else { - return false; - } -} - -DefaultMemchunks *Downstream::get_request_buf() { return &request_buf_; } - -// Call this function after this object is attached to -// Downstream. Otherwise, the program will crash. -int Downstream::push_request_headers() { - if (!dconn_) { - DLOG(INFO, this) << "dconn_ is NULL"; - return -1; - } - return dconn_->push_request_headers(); -} - -int Downstream::push_upload_data_chunk(const uint8_t *data, size_t datalen) { - // Assumes that request headers have already been pushed to output - // buffer using push_request_headers(). - if (!dconn_) { - DLOG(INFO, this) << "dconn_ is NULL"; - return -1; - } - request_bodylen_ += datalen; - if (dconn_->push_upload_data_chunk(data, datalen) != 0) { - return -1; - } - - request_datalen_ += datalen; - - return 0; -} - -int Downstream::end_upload_data() { - if (!dconn_) { - DLOG(INFO, this) << "dconn_ is NULL"; - return -1; - } - return dconn_->end_upload_data(); -} - -const Headers &Downstream::get_response_headers() const { - return response_headers_; -} - -int Downstream::index_response_headers() { - return index_headers(response_hdidx_, response_headers_, - response_content_length_); -} - -const Headers::value_type * -Downstream::get_response_header(int16_t token) const { - return http2::get_header(response_hdidx_, token, response_headers_); -} - -void Downstream::rewrite_location_response_header( - const std::string &upstream_scheme) { - auto hd = - http2::get_header(response_hdidx_, http2::HD_LOCATION, response_headers_); - if (!hd) { - return; - } - http_parser_url u; - memset(&u, 0, sizeof(u)); - int rv = - http_parser_parse_url((*hd).value.c_str(), (*hd).value.size(), 0, &u); - if (rv != 0) { - return; - } - std::string new_uri; - if (get_config()->no_host_rewrite || request_method_ == "CONNECT") { - if (!request_http2_authority_.empty()) { - new_uri = http2::rewrite_location_uri( - (*hd).value, u, request_http2_authority_, request_http2_authority_, - upstream_scheme); - } - if (new_uri.empty()) { - auto host = get_request_header(http2::HD_HOST); - if (host) { - new_uri = http2::rewrite_location_uri((*hd).value, u, (*host).value, - (*host).value, upstream_scheme); - } else if (!request_downstream_host_.empty()) { - new_uri = http2::rewrite_location_uri( - (*hd).value, u, request_downstream_host_, "", upstream_scheme); - } else { - return; - } - } - } else { - if (request_downstream_host_.empty()) { - return; - } - if (!request_http2_authority_.empty()) { - new_uri = http2::rewrite_location_uri( - (*hd).value, u, request_downstream_host_, request_http2_authority_, - upstream_scheme); - } else { - auto host = get_request_header(http2::HD_HOST); - if (host) { - new_uri = http2::rewrite_location_uri((*hd).value, u, - request_downstream_host_, - (*host).value, upstream_scheme); - } else { - new_uri = http2::rewrite_location_uri( - (*hd).value, u, request_downstream_host_, "", upstream_scheme); - } - } - } - if (!new_uri.empty()) { - auto idx = response_hdidx_[http2::HD_LOCATION]; - response_headers_[idx].value = std::move(new_uri); - } -} - -void Downstream::add_response_header(std::string name, std::string value) { - add_header(response_header_key_prev_, response_headers_sum_, - response_headers_, std::move(name), std::move(value)); -} - -void Downstream::set_last_response_header_value(const char *data, size_t len) { - set_last_header_value(response_header_key_prev_, response_headers_sum_, - response_headers_, data, len); -} - -void Downstream::add_response_header(std::string name, std::string value, - int16_t token) { - http2::index_header(response_hdidx_, token, response_headers_.size()); - response_headers_sum_ += name.size() + value.size(); - response_headers_.emplace_back(std::move(name), std::move(value), false, - token); -} - -void Downstream::add_response_header(const uint8_t *name, size_t namelen, - const uint8_t *value, size_t valuelen, - bool no_index, int16_t token) { - http2::index_header(response_hdidx_, token, response_headers_.size()); - response_headers_sum_ += namelen + valuelen; - http2::add_header(response_headers_, name, namelen, value, valuelen, no_index, - token); -} - -bool Downstream::get_response_header_key_prev() const { - return response_header_key_prev_; -} - -void Downstream::append_last_response_header_key(const char *data, size_t len) { - append_last_header_key(response_header_key_prev_, response_headers_sum_, - response_headers_, data, len); -} - -void Downstream::append_last_response_header_value(const char *data, - size_t len) { - append_last_header_value(response_header_key_prev_, response_headers_sum_, - response_headers_, data, len); -} - -void Downstream::clear_response_headers() { - Headers().swap(response_headers_); - http2::init_hdidx(response_hdidx_); -} - -size_t Downstream::get_response_headers_sum() const { - return response_headers_sum_; -} - -const Headers &Downstream::get_response_trailers() const { - return response_trailers_; -} - -void Downstream::add_response_trailer(const uint8_t *name, size_t namelen, - const uint8_t *value, size_t valuelen, - bool no_index, int16_t token) { - response_headers_sum_ += namelen + valuelen; - http2::add_header(response_trailers_, name, namelen, value, valuelen, - no_index, -1); -} - -unsigned int Downstream::get_response_http_status() const { - return response_http_status_; -} - -void Downstream::add_response_trailer(std::string name, std::string value) { - add_header(response_trailer_key_prev_, response_headers_sum_, - response_trailers_, std::move(name), std::move(value)); -} - -void Downstream::set_last_response_trailer_value(const char *data, size_t len) { - set_last_header_value(response_trailer_key_prev_, response_headers_sum_, - response_trailers_, data, len); -} - -bool Downstream::get_response_trailer_key_prev() const { - return response_trailer_key_prev_; -} - -void Downstream::append_last_response_trailer_key(const char *data, - size_t len) { - append_last_header_key(response_trailer_key_prev_, response_headers_sum_, - response_trailers_, data, len); -} - -void Downstream::append_last_response_trailer_value(const char *data, - size_t len) { - append_last_header_value(response_trailer_key_prev_, response_headers_sum_, - response_trailers_, data, len); -} - -void Downstream::set_response_http_status(unsigned int status) { - response_http_status_ = status; -} - -void Downstream::set_response_major(int major) { response_major_ = major; } - -void Downstream::set_response_minor(int minor) { response_minor_ = minor; } - -int Downstream::get_response_major() const { return response_major_; } - -int Downstream::get_response_minor() const { return response_minor_; } - -int Downstream::get_response_version() const { - return response_major_ * 100 + response_minor_; -} - -bool Downstream::get_chunked_response() const { return chunked_response_; } - -void Downstream::set_chunked_response(bool f) { chunked_response_ = f; } - -bool Downstream::get_response_connection_close() const { - return response_connection_close_; -} - -void Downstream::set_response_connection_close(bool f) { - response_connection_close_ = f; -} - -int Downstream::on_read() { - if (!dconn_) { - DLOG(INFO, this) << "dconn_ is NULL"; - return -1; - } - return dconn_->on_read(); -} - -int Downstream::change_priority(int32_t pri) { - if (!dconn_) { - DLOG(INFO, this) << "dconn_ is NULL"; - return -1; - } - return dconn_->on_priority_change(pri); -} - -void Downstream::set_response_state(int state) { response_state_ = state; } - -int Downstream::get_response_state() const { return response_state_; } - -DefaultMemchunks *Downstream::get_response_buf() { return &response_buf_; } - -bool Downstream::response_buf_full() { - if (dconn_) { - return response_buf_.rleft() >= - get_config()->downstream_response_buffer_size; - } else { - return false; - } -} - -void Downstream::add_response_bodylen(size_t amount) { - response_bodylen_ += amount; -} - -int64_t Downstream::get_response_bodylen() const { return response_bodylen_; } - -void Downstream::add_response_sent_bodylen(size_t amount) { - response_sent_bodylen_ += amount; -} - -int64_t Downstream::get_response_sent_bodylen() const { - return response_sent_bodylen_; -} - -int64_t Downstream::get_response_content_length() const { - return response_content_length_; -} - -void Downstream::set_response_content_length(int64_t len) { - response_content_length_ = len; -} - -int64_t Downstream::get_request_content_length() const { - return request_content_length_; -} - -void Downstream::set_request_content_length(int64_t len) { - request_content_length_ = len; -} - -bool Downstream::validate_request_bodylen() const { - if (request_content_length_ == -1) { - return true; - } - - if (request_content_length_ != request_bodylen_) { - if (LOG_ENABLED(INFO)) { - DLOG(INFO, this) << "request invalid bodylen: content-length=" - << request_content_length_ - << ", received=" << request_bodylen_; - } - return false; - } - - return true; -} - -bool Downstream::validate_response_bodylen() const { - if (!expect_response_body() || response_content_length_ == -1) { - return true; - } - - if (response_content_length_ != response_bodylen_) { - if (LOG_ENABLED(INFO)) { - DLOG(INFO, this) << "response invalid bodylen: content-length=" - << response_content_length_ - << ", received=" << response_bodylen_; - } - return false; - } - - return true; -} - -void Downstream::set_priority(int32_t pri) { priority_ = pri; } - -int32_t Downstream::get_priority() const { return priority_; } - -void Downstream::check_upgrade_fulfilled() { - if (request_method_ == "CONNECT") { - upgraded_ = 200 <= response_http_status_ && response_http_status_ < 300; - - return; - } - - if (response_http_status_ == 101) { - // TODO Do more strict checking for upgrade headers - upgraded_ = upgrade_request_; - - return; - } -} - -void Downstream::inspect_http2_request() { - if (request_method_ == "CONNECT") { - upgrade_request_ = true; - } -} - -void Downstream::inspect_http1_request() { - if (request_method_ == "CONNECT") { - upgrade_request_ = true; - } - - if (!upgrade_request_) { - auto idx = request_hdidx_[http2::HD_UPGRADE]; - if (idx != -1) { - upgrade_request_ = true; - - auto &val = request_headers_[idx].value; - // TODO Perform more strict checking for upgrade headers - if (util::streq_l(NGHTTP2_CLEARTEXT_PROTO_VERSION_ID, val.c_str(), - val.size())) { - http2_upgrade_seen_ = true; - } - } - } - auto idx = request_hdidx_[http2::HD_TRANSFER_ENCODING]; - if (idx != -1) { - request_content_length_ = -1; - if (util::strifind(request_headers_[idx].value.c_str(), "chunked")) { - chunked_request_ = true; - } - } -} - -void Downstream::inspect_http1_response() { - auto idx = response_hdidx_[http2::HD_TRANSFER_ENCODING]; - if (idx != -1) { - response_content_length_ = -1; - if (util::strifind(response_headers_[idx].value.c_str(), "chunked")) { - chunked_response_ = true; - } - } -} - -void Downstream::reset_response() { - response_http_status_ = 0; - response_major_ = 1; - response_minor_ = 1; -} - -bool Downstream::get_non_final_response() const { - return response_http_status_ / 100 == 1; -} - -bool Downstream::get_upgraded() const { return upgraded_; } - -bool Downstream::get_upgrade_request() const { return upgrade_request_; } - -bool Downstream::get_http2_upgrade_request() const { - return request_bodylen_ == 0 && http2_upgrade_seen_ && - request_hdidx_[http2::HD_HTTP2_SETTINGS] != -1; -} - -namespace { -const std::string EMPTY; -} // namespace - -const std::string &Downstream::get_http2_settings() const { - auto idx = request_hdidx_[http2::HD_HTTP2_SETTINGS]; - if (idx == -1) { - return EMPTY; - } - return request_headers_[idx].value; -} - -void Downstream::set_downstream_stream_id(int32_t stream_id) { - downstream_stream_id_ = stream_id; -} - -int32_t Downstream::get_downstream_stream_id() const { - return downstream_stream_id_; -} - -uint32_t Downstream::get_response_rst_stream_error_code() const { - return response_rst_stream_error_code_; -} - -void Downstream::set_response_rst_stream_error_code(uint32_t error_code) { - response_rst_stream_error_code_ = error_code; -} - -void Downstream::set_expect_final_response(bool f) { - expect_final_response_ = f; -} - -bool Downstream::get_expect_final_response() const { - return expect_final_response_; -} - -size_t Downstream::get_request_datalen() const { return request_datalen_; } - -void Downstream::dec_request_datalen(size_t len) { - assert(request_datalen_ >= len); - request_datalen_ -= len; -} - -void Downstream::reset_request_datalen() { request_datalen_ = 0; } - -void Downstream::add_response_datalen(size_t len) { response_datalen_ += len; } - -void Downstream::dec_response_datalen(size_t len) { - assert(response_datalen_ >= len); - response_datalen_ -= len; -} - -size_t Downstream::get_response_datalen() const { return response_datalen_; } - -void Downstream::reset_response_datalen() { response_datalen_ = 0; } - -bool Downstream::expect_response_body() const { - return http2::expect_response_body(request_method_, response_http_status_); -} - -namespace { -bool pseudo_header_allowed(const Headers &headers) { - if (headers.empty()) { - return true; - } - - return headers.back().name.c_str()[0] == ':'; -} -} // namespace - -bool Downstream::request_pseudo_header_allowed(int16_t token) const { - if (!pseudo_header_allowed(request_headers_)) { - return false; - } - return http2::check_http2_request_pseudo_header(request_hdidx_, token); -} - -bool Downstream::response_pseudo_header_allowed(int16_t token) const { - if (!pseudo_header_allowed(response_headers_)) { - return false; - } - return http2::check_http2_response_pseudo_header(response_hdidx_, token); -} - -namespace { -void reset_timer(struct ev_loop *loop, ev_timer *w) { ev_timer_again(loop, w); } -} // namespace - -namespace { -void try_reset_timer(struct ev_loop *loop, ev_timer *w) { - if (!ev_is_active(w)) { - return; - } - ev_timer_again(loop, w); -} -} // namespace - -namespace { -void ensure_timer(struct ev_loop *loop, ev_timer *w) { - if (ev_is_active(w)) { - return; - } - ev_timer_again(loop, w); -} -} // namespace - -namespace { -void disable_timer(struct ev_loop *loop, ev_timer *w) { - ev_timer_stop(loop, w); -} -} // namespace - -void Downstream::reset_upstream_rtimer() { - if (get_config()->stream_read_timeout == 0.) { - return; - } - auto loop = upstream_->get_client_handler()->get_loop(); - reset_timer(loop, &upstream_rtimer_); -} - -void Downstream::reset_upstream_wtimer() { - auto loop = upstream_->get_client_handler()->get_loop(); - if (get_config()->stream_write_timeout != 0.) { - reset_timer(loop, &upstream_wtimer_); - } - if (get_config()->stream_read_timeout != 0.) { - try_reset_timer(loop, &upstream_rtimer_); - } -} - -void Downstream::ensure_upstream_wtimer() { - if (get_config()->stream_write_timeout == 0.) { - return; - } - auto loop = upstream_->get_client_handler()->get_loop(); - ensure_timer(loop, &upstream_wtimer_); -} - -void Downstream::disable_upstream_rtimer() { - if (get_config()->stream_read_timeout == 0.) { - return; - } - auto loop = upstream_->get_client_handler()->get_loop(); - disable_timer(loop, &upstream_rtimer_); -} - -void Downstream::disable_upstream_wtimer() { - if (get_config()->stream_write_timeout == 0.) { - return; - } - auto loop = upstream_->get_client_handler()->get_loop(); - disable_timer(loop, &upstream_wtimer_); -} - -void Downstream::reset_downstream_rtimer() { - if (get_config()->stream_read_timeout == 0.) { - return; - } - auto loop = upstream_->get_client_handler()->get_loop(); - reset_timer(loop, &downstream_rtimer_); -} - -void Downstream::reset_downstream_wtimer() { - auto loop = upstream_->get_client_handler()->get_loop(); - if (get_config()->stream_write_timeout != 0.) { - reset_timer(loop, &downstream_wtimer_); - } - if (get_config()->stream_read_timeout != 0.) { - try_reset_timer(loop, &downstream_rtimer_); - } -} - -void Downstream::ensure_downstream_wtimer() { - if (get_config()->stream_write_timeout == 0.) { - return; - } - auto loop = upstream_->get_client_handler()->get_loop(); - ensure_timer(loop, &downstream_wtimer_); -} - -void Downstream::disable_downstream_rtimer() { - if (get_config()->stream_read_timeout == 0.) { - return; - } - auto loop = upstream_->get_client_handler()->get_loop(); - disable_timer(loop, &downstream_rtimer_); -} - -void Downstream::disable_downstream_wtimer() { - if (get_config()->stream_write_timeout == 0.) { - return; - } - auto loop = upstream_->get_client_handler()->get_loop(); - disable_timer(loop, &downstream_wtimer_); -} - -bool Downstream::accesslog_ready() const { return response_http_status_ > 0; } - -void Downstream::add_retry() { ++num_retry_; } - -bool Downstream::no_more_retry() const { return num_retry_ > 5; } - -void Downstream::set_request_downstream_host(std::string host) { - request_downstream_host_ = std::move(host); -} - -void Downstream::set_request_pending(bool f) { request_pending_ = f; } - -bool Downstream::get_request_pending() const { return request_pending_; } - -bool Downstream::request_submission_ready() const { - return (request_state_ == Downstream::HEADER_COMPLETE || - request_state_ == Downstream::MSG_COMPLETE) && - request_pending_ && response_state_ == Downstream::INITIAL; -} - -int Downstream::get_dispatch_state() const { return dispatch_state_; } - -void Downstream::set_dispatch_state(int s) { dispatch_state_ = s; } - -void Downstream::attach_blocked_link(BlockedLink *l) { - assert(!blocked_link_); - - l->downstream = this; - blocked_link_ = l; -} - -void Downstream::detach_blocked_link(BlockedLink *l) { - assert(blocked_link_); - assert(l->downstream == this); - - l->downstream = nullptr; - blocked_link_ = nullptr; -} - -void Downstream::add_request_headers_sum(size_t amount) { - request_headers_sum_ += amount; -} - -} // namespace shrpx diff --git a/src/shrpx_downstream.h b/src/shrpx_downstream.h deleted file mode 100644 index 2bd2931..0000000 --- a/src/shrpx_downstream.h +++ /dev/null @@ -1,456 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2012 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#ifndef SHRPX_DOWNSTREAM_H -#define SHRPX_DOWNSTREAM_H - -#include "shrpx.h" - -#include -#include -#include -#include -#include - -#include - -#include - -#include "shrpx_io_control.h" -#include "http2.h" -#include "memchunk.h" - -using namespace nghttp2; - -namespace shrpx { - -class Upstream; -class DownstreamConnection; -struct BlockedLink; - -class Downstream { -public: - Downstream(Upstream *upstream, MemchunkPool *mcpool, int32_t stream_id, - int32_t priority); - ~Downstream(); - void reset_upstream(Upstream *upstream); - Upstream *get_upstream() const; - void set_stream_id(int32_t stream_id); - int32_t get_stream_id() const; - void set_priority(int32_t pri); - int32_t get_priority() const; - void pause_read(IOCtrlReason reason); - int resume_read(IOCtrlReason reason, size_t consumed); - void force_resume_read(); - // Set stream ID for downstream HTTP2 connection. - void set_downstream_stream_id(int32_t stream_id); - int32_t get_downstream_stream_id() const; - - int attach_downstream_connection(std::unique_ptr dconn); - void detach_downstream_connection(); - // Releases dconn_, without freeing it. - void release_downstream_connection(); - DownstreamConnection *get_downstream_connection(); - // Returns dconn_ and nullifies dconn_. - std::unique_ptr pop_downstream_connection(); - - // Returns true if output buffer is full. If underlying dconn_ is - // NULL, this function always returns false. - bool request_buf_full(); - // Returns true if upgrade (HTTP Upgrade or CONNECT) is succeeded. - void check_upgrade_fulfilled(); - // Returns true if the request is upgrade. - bool get_upgrade_request() const; - // Returns true if the upgrade is succeded as a result of the call - // check_upgrade_fulfilled(). - bool get_upgraded() const; - // Inspects HTTP/2 request. - void inspect_http2_request(); - // Inspects HTTP/1 request. This checks whether the request is - // upgrade request and tranfer-encoding etc. - void inspect_http1_request(); - // Returns true if the request is HTTP Upgrade for HTTP/2 - bool get_http2_upgrade_request() const; - // Returns the value of HTTP2-Settings request header field. - const std::string &get_http2_settings() const; - // downstream request API - const Headers &get_request_headers() const; - // Crumbles (split cookie by ";") in request_headers_ and returns - // them. Headers::no_index is inherited. - Headers crumble_request_cookie(); - void assemble_request_cookie(); - const std::string &get_assembled_request_cookie() const; - // Lower the request header field names and indexes request headers. - // If there is any invalid headers (e.g., multiple Content-Length - // having different values), returns -1. - int index_request_headers(); - // Returns pointer to the request header with the name |name|. If - // multiple header have |name| as name, return last occurrence from - // the beginning. If no such header is found, returns nullptr. - // This function must be called after headers are indexed - const Headers::value_type *get_request_header(int16_t token) const; - // Returns pointer to the request header with the name |name|. If - // no such header is found, returns nullptr. - const Headers::value_type *get_request_header(const std::string &name) const; - void add_request_header(std::string name, std::string value); - void set_last_request_header_value(const char *data, size_t len); - - void add_request_header(std::string name, std::string value, int16_t token); - void add_request_header(const uint8_t *name, size_t namelen, - const uint8_t *value, size_t valuelen, bool no_index, - int16_t token); - - bool get_request_header_key_prev() const; - void append_last_request_header_key(const char *data, size_t len); - void append_last_request_header_value(const char *data, size_t len); - // Empties request headers. - void clear_request_headers(); - - size_t get_request_headers_sum() const; - - const Headers &get_request_trailers() const; - void add_request_trailer(const uint8_t *name, size_t namelen, - const uint8_t *value, size_t valuelen, bool no_index, - int16_t token); - void add_request_trailer(std::string name, std::string value); - void set_last_request_trailer_value(const char *data, size_t len); - bool get_request_trailer_key_prev() const; - void append_last_request_trailer_key(const char *data, size_t len); - void append_last_request_trailer_value(const char *data, size_t len); - - void set_request_method(std::string method); - const std::string &get_request_method() const; - void set_request_path(std::string path); - void add_request_headers_sum(size_t amount); - void - set_request_start_time(std::chrono::high_resolution_clock::time_point time); - const std::chrono::high_resolution_clock::time_point & - get_request_start_time() const; - void append_request_path(const char *data, size_t len); - // Returns request path. For HTTP/1.1, this is request-target. For - // HTTP/2, this is :path header field value. - const std::string &get_request_path() const; - // Returns HTTP/2 :scheme header field value. - const std::string &get_request_http2_scheme() const; - void set_request_http2_scheme(std::string scheme); - // Returns HTTP/2 :authority header field value. We also set the - // value retrieved from absolute-form HTTP/1 request. - const std::string &get_request_http2_authority() const; - void set_request_http2_authority(std::string authority); - void set_request_major(int major); - void set_request_minor(int minor); - int get_request_major() const; - int get_request_minor() const; - int push_request_headers(); - bool get_chunked_request() const; - void set_chunked_request(bool f); - bool get_request_connection_close() const; - void set_request_connection_close(bool f); - bool get_request_http2_expect_body() const; - void set_request_http2_expect_body(bool f); - int push_upload_data_chunk(const uint8_t *data, size_t datalen); - int end_upload_data(); - size_t get_request_datalen() const; - void dec_request_datalen(size_t len); - void reset_request_datalen(); - // Validates that received request body length and content-length - // matches. - bool validate_request_bodylen() const; - int64_t get_request_content_length() const; - void set_request_content_length(int64_t len); - bool request_pseudo_header_allowed(int16_t token) const; - void set_request_downstream_host(std::string host); - bool expect_response_body() const; - enum { - INITIAL, - HEADER_COMPLETE, - MSG_COMPLETE, - STREAM_CLOSED, - CONNECT_FAIL, - IDLE, - MSG_RESET, - // header contains invalid header field. We can safely send error - // response (502) to a client. - MSG_BAD_HEADER, - // header fields in HTTP/1 request exceed the configuration limit. - // This state is only transitioned from INITIAL state, and solely - // used to signal 431 status code to the client. - HTTP1_REQUEST_HEADER_TOO_LARGE, - }; - void set_request_state(int state); - int get_request_state() const; - DefaultMemchunks *get_request_buf(); - void set_request_pending(bool f); - bool get_request_pending() const; - // Returns true if request is ready to be submitted to downstream. - bool request_submission_ready() const; - // downstream response API - const Headers &get_response_headers() const; - // Lower the response header field names and indexes response - // headers. If there are invalid headers (e.g., multiple - // Content-Length with different values), returns -1. - int index_response_headers(); - // Returns pointer to the response header with the name |name|. If - // multiple header have |name| as name, return last occurrence from - // the beginning. If no such header is found, returns nullptr. - // This function must be called after response headers are indexed. - const Headers::value_type *get_response_header(int16_t token) const; - // Rewrites the location response header field. - void rewrite_location_response_header(const std::string &upstream_scheme); - void add_response_header(std::string name, std::string value); - void set_last_response_header_value(const char *data, size_t len); - - void add_response_header(std::string name, std::string value, int16_t token); - void add_response_header(const uint8_t *name, size_t namelen, - const uint8_t *value, size_t valuelen, bool no_index, - int16_t token); - - bool get_response_header_key_prev() const; - void append_last_response_header_key(const char *data, size_t len); - void append_last_response_header_value(const char *data, size_t len); - // Empties response headers. - void clear_response_headers(); - - size_t get_response_headers_sum() const; - - const Headers &get_response_trailers() const; - void add_response_trailer(const uint8_t *name, size_t namelen, - const uint8_t *value, size_t valuelen, - bool no_index, int16_t token); - void add_response_trailer(std::string name, std::string value); - void set_last_response_trailer_value(const char *data, size_t len); - bool get_response_trailer_key_prev() const; - void append_last_response_trailer_key(const char *data, size_t len); - void append_last_response_trailer_value(const char *data, size_t len); - - unsigned int get_response_http_status() const; - void set_response_http_status(unsigned int status); - void set_response_major(int major); - void set_response_minor(int minor); - int get_response_major() const; - int get_response_minor() const; - int get_response_version() const; - bool get_chunked_response() const; - void set_chunked_response(bool f); - bool get_response_connection_close() const; - void set_response_connection_close(bool f); - void set_response_state(int state); - int get_response_state() const; - DefaultMemchunks *get_response_buf(); - bool response_buf_full(); - void add_response_bodylen(size_t amount); - int64_t get_response_bodylen() const; - void add_response_sent_bodylen(size_t amount); - int64_t get_response_sent_bodylen() const; - int64_t get_response_content_length() const; - void set_response_content_length(int64_t len); - // Validates that received response body length and content-length - // matches. - bool validate_response_bodylen() const; - uint32_t get_response_rst_stream_error_code() const; - void set_response_rst_stream_error_code(uint32_t error_code); - // Inspects HTTP/1 response. This checks tranfer-encoding etc. - void inspect_http1_response(); - // Clears some of member variables for response. - void reset_response(); - bool get_non_final_response() const; - void set_expect_final_response(bool f); - bool get_expect_final_response() const; - void add_response_datalen(size_t len); - void dec_response_datalen(size_t len); - size_t get_response_datalen() const; - void reset_response_datalen(); - bool response_pseudo_header_allowed(int16_t token) const; - - // Call this method when there is incoming data in downstream - // connection. - int on_read(); - - // Change the priority of downstream - int change_priority(int32_t pri); - - bool get_rst_stream_after_end_stream() const; - void set_rst_stream_after_end_stream(bool f); - - // Resets upstream read timer. If it is active, timeout value is - // reset. If it is not active, timer will be started. - void reset_upstream_rtimer(); - // Resets upstream write timer. If it is active, timeout value is - // reset. If it is not active, timer will be started. This - // function also resets read timer if it has been started. - void reset_upstream_wtimer(); - // Makes sure that upstream write timer is started. If it has been - // started, do nothing. Otherwise, write timer will be started. - void ensure_upstream_wtimer(); - // Disables upstream read timer. - void disable_upstream_rtimer(); - // Disables upstream write timer. - void disable_upstream_wtimer(); - - // Downstream timer functions. They works in a similar way just - // like the upstream timer function. - void reset_downstream_rtimer(); - void reset_downstream_wtimer(); - void ensure_downstream_wtimer(); - void disable_downstream_rtimer(); - void disable_downstream_wtimer(); - - // Returns true if accesslog can be written for this downstream. - bool accesslog_ready() const; - - // Increment retry count - void add_retry(); - // true if retry attempt should not be done. - bool no_more_retry() const; - - int get_dispatch_state() const; - void set_dispatch_state(int s); - - void attach_blocked_link(BlockedLink *l); - void detach_blocked_link(BlockedLink *l); - - enum { - EVENT_ERROR = 0x1, - EVENT_TIMEOUT = 0x2, - }; - - enum { - DISPATCH_NONE, - DISPATCH_PENDING, - DISPATCH_BLOCKED, - DISPATCH_ACTIVE, - DISPATCH_FAILURE, - }; - - Downstream *dlnext, *dlprev; - -private: - Headers request_headers_; - Headers response_headers_; - - // trailer part. For HTTP/1.1, trailer part is only included with - // chunked encoding. For HTTP/2, there is no such limit. - Headers request_trailers_; - Headers response_trailers_; - - std::chrono::high_resolution_clock::time_point request_start_time_; - - std::string request_method_; - std::string request_path_; - std::string request_http2_scheme_; - std::string request_http2_authority_; - // host we requested to downstream. This is used to rewrite - // location header field to decide the location should be rewritten - // or not. - std::string request_downstream_host_; - std::string assembled_request_cookie_; - - DefaultMemchunks request_buf_; - DefaultMemchunks response_buf_; - - ev_timer upstream_rtimer_; - ev_timer upstream_wtimer_; - - ev_timer downstream_rtimer_; - ev_timer downstream_wtimer_; - - // the length of request body received so far - int64_t request_bodylen_; - // the length of response body received so far - int64_t response_bodylen_; - - // the length of response body sent to upstream client - int64_t response_sent_bodylen_; - - // content-length of request body, -1 if it is unknown. - int64_t request_content_length_; - // content-length of response body, -1 if it is unknown. - int64_t response_content_length_; - - Upstream *upstream_; - std::unique_ptr dconn_; - - // only used by HTTP/2 or SPDY upstream - BlockedLink *blocked_link_; - - size_t request_headers_sum_; - size_t response_headers_sum_; - - // The number of bytes not consumed by the application yet. - size_t request_datalen_; - size_t response_datalen_; - - size_t num_retry_; - - int32_t stream_id_; - int32_t priority_; - // stream ID in backend connection - int32_t downstream_stream_id_; - - // RST_STREAM error_code from downstream HTTP2 connection - uint32_t response_rst_stream_error_code_; - - int request_state_; - int request_major_; - int request_minor_; - - int response_state_; - unsigned int response_http_status_; - int response_major_; - int response_minor_; - - // only used by HTTP/2 or SPDY upstream - int dispatch_state_; - - http2::HeaderIndex request_hdidx_; - http2::HeaderIndex response_hdidx_; - - // true if the request contains upgrade token (HTTP Upgrade or - // CONNECT) - bool upgrade_request_; - // true if the connection is upgraded (HTTP Upgrade or CONNECT) - bool upgraded_; - - bool http2_upgrade_seen_; - - bool chunked_request_; - bool request_connection_close_; - bool request_header_key_prev_; - bool request_trailer_key_prev_; - bool request_http2_expect_body_; - - bool chunked_response_; - bool response_connection_close_; - bool response_header_key_prev_; - bool response_trailer_key_prev_; - bool expect_final_response_; - // true if downstream request is pending because backend connection - // has not been established or should be checked before use; - // currently used only with HTTP/2 connection. - bool request_pending_; -}; - -} // namespace shrpx - -#endif // SHRPX_DOWNSTREAM_H diff --git a/src/shrpx_downstream_connection.cc b/src/shrpx_downstream_connection.cc deleted file mode 100644 index 77dcd44..0000000 --- a/src/shrpx_downstream_connection.cc +++ /dev/null @@ -1,52 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2012 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#include "shrpx_downstream_connection.h" - -#include "shrpx_client_handler.h" -#include "shrpx_downstream.h" -#include "shrpx_downstream_connection_pool.h" - -namespace shrpx { - -DownstreamConnection::DownstreamConnection(DownstreamConnectionPool *dconn_pool) - : dconn_pool_(dconn_pool), client_handler_(nullptr), downstream_(nullptr) {} - -DownstreamConnection::~DownstreamConnection() {} - -void DownstreamConnection::set_client_handler(ClientHandler *handler) { - client_handler_ = handler; -} - -ClientHandler *DownstreamConnection::get_client_handler() { - return client_handler_; -} - -Downstream *DownstreamConnection::get_downstream() { return downstream_; } - -DownstreamConnectionPool *DownstreamConnection::get_dconn_pool() const { - return dconn_pool_; -} - -} // namespace shrpx diff --git a/src/shrpx_downstream_connection.h b/src/shrpx_downstream_connection.h deleted file mode 100644 index 49ca1be..0000000 --- a/src/shrpx_downstream_connection.h +++ /dev/null @@ -1,77 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2012 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#ifndef SHRPX_DOWNSTREAM_CONNECTION_H -#define SHRPX_DOWNSTREAM_CONNECTION_H - -#include "shrpx.h" - -#include "shrpx_io_control.h" - -namespace shrpx { - -class ClientHandler; -class Upstream; -class Downstream; -class DownstreamConnectionPool; - -class DownstreamConnection { -public: - DownstreamConnection(DownstreamConnectionPool *dconn_pool); - virtual ~DownstreamConnection(); - virtual int attach_downstream(Downstream *downstream) = 0; - virtual void detach_downstream(Downstream *downstream) = 0; - - virtual int push_request_headers() = 0; - virtual int push_upload_data_chunk(const uint8_t *data, size_t datalen) = 0; - virtual int end_upload_data() = 0; - - virtual void pause_read(IOCtrlReason reason) = 0; - virtual int resume_read(IOCtrlReason reason, size_t consumed) = 0; - virtual void force_resume_read() = 0; - - virtual int on_read() = 0; - virtual int on_write() = 0; - virtual int on_timeout() { return 0; } - - virtual void on_upstream_change(Upstream *uptream) = 0; - virtual int on_priority_change(int32_t pri) = 0; - - // true if this object is poolable. - virtual bool poolable() const = 0; - - void set_client_handler(ClientHandler *client_handler); - ClientHandler *get_client_handler(); - Downstream *get_downstream(); - DownstreamConnectionPool *get_dconn_pool() const; - -protected: - DownstreamConnectionPool *dconn_pool_; - ClientHandler *client_handler_; - Downstream *downstream_; -}; - -} // namespace shrpx - -#endif // SHRPX_DOWNSTREAM_CONNECTION_H diff --git a/src/shrpx_downstream_connection_pool.cc b/src/shrpx_downstream_connection_pool.cc deleted file mode 100644 index d762b77..0000000 --- a/src/shrpx_downstream_connection_pool.cc +++ /dev/null @@ -1,60 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2014 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#include "shrpx_downstream_connection_pool.h" -#include "shrpx_downstream_connection.h" - -namespace shrpx { - -DownstreamConnectionPool::DownstreamConnectionPool() {} - -DownstreamConnectionPool::~DownstreamConnectionPool() { - for (auto dconn : pool_) { - delete dconn; - } -} - -void DownstreamConnectionPool::add_downstream_connection( - std::unique_ptr dconn) { - pool_.insert(dconn.release()); -} - -std::unique_ptr -DownstreamConnectionPool::pop_downstream_connection() { - if (pool_.empty()) { - return nullptr; - } - - auto dconn = std::unique_ptr(*std::begin(pool_)); - pool_.erase(std::begin(pool_)); - return dconn; -} - -void DownstreamConnectionPool::remove_downstream_connection( - DownstreamConnection *dconn) { - pool_.erase(dconn); - delete dconn; -} - -} // namespace shrpx diff --git a/src/shrpx_downstream_connection_pool.h b/src/shrpx_downstream_connection_pool.h deleted file mode 100644 index c2edce4..0000000 --- a/src/shrpx_downstream_connection_pool.h +++ /dev/null @@ -1,52 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2014 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#ifndef SHRPX_DOWNSTREAM_CONNECTION_POOL_H -#define SHRPX_DOWNSTREAM_CONNECTION_POOL_H - -#include "shrpx.h" - -#include -#include - -namespace shrpx { - -class DownstreamConnection; - -class DownstreamConnectionPool { -public: - DownstreamConnectionPool(); - ~DownstreamConnectionPool(); - - void add_downstream_connection(std::unique_ptr dconn); - std::unique_ptr pop_downstream_connection(); - void remove_downstream_connection(DownstreamConnection *dconn); - -private: - std::set pool_; -}; - -} // namespace shrpx - -#endif // SHRPX_DOWNSTREAM_CONNECTION_POOL_H diff --git a/src/shrpx_downstream_queue.cc b/src/shrpx_downstream_queue.cc deleted file mode 100644 index 32ff4e6..0000000 --- a/src/shrpx_downstream_queue.cc +++ /dev/null @@ -1,167 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2012 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#include "shrpx_downstream_queue.h" - -#include -#include - -#include "shrpx_downstream.h" - -namespace shrpx { - -DownstreamQueue::HostEntry::HostEntry() : num_active(0) {} - -DownstreamQueue::DownstreamQueue(size_t conn_max_per_host, bool unified_host) - : conn_max_per_host_(conn_max_per_host == 0 - ? std::numeric_limits::max() - : conn_max_per_host), - unified_host_(unified_host) {} - -DownstreamQueue::~DownstreamQueue() { - dlist_delete_all(downstreams_); - for (auto &p : host_entries_) { - auto &ent = p.second; - dlist_delete_all(ent.blocked); - } -} - -void DownstreamQueue::add_pending(std::unique_ptr downstream) { - downstream->set_dispatch_state(Downstream::DISPATCH_PENDING); - downstreams_.append(downstream.release()); -} - -void DownstreamQueue::mark_failure(Downstream *downstream) { - downstream->set_dispatch_state(Downstream::DISPATCH_FAILURE); -} - -DownstreamQueue::HostEntry & -DownstreamQueue::find_host_entry(const std::string &host) { - auto itr = host_entries_.find(host); - if (itr == std::end(host_entries_)) { -#ifdef HAVE_STD_MAP_EMPLACE - std::tie(itr, std::ignore) = host_entries_.emplace(host, HostEntry()); -#else // !HAVE_STD_MAP_EMPLACE - // for g++-4.7 - std::tie(itr, std::ignore) = host_entries_.insert({host, HostEntry()}); -#endif // !HAVE_STD_MAP_EMPLACE - } - return (*itr).second; -} - -const std::string & -DownstreamQueue::make_host_key(const std::string &host) const { - static std::string empty_key; - return unified_host_ ? empty_key : host; -} - -const std::string & -DownstreamQueue::make_host_key(Downstream *downstream) const { - return make_host_key(downstream->get_request_http2_authority()); -} - -void DownstreamQueue::mark_active(Downstream *downstream) { - auto &ent = find_host_entry(make_host_key(downstream)); - ++ent.num_active; - - downstream->set_dispatch_state(Downstream::DISPATCH_ACTIVE); -} - -void DownstreamQueue::mark_blocked(Downstream *downstream) { - auto &ent = find_host_entry(make_host_key(downstream)); - - downstream->set_dispatch_state(Downstream::DISPATCH_BLOCKED); - - auto link = new BlockedLink{}; - downstream->attach_blocked_link(link); - ent.blocked.append(link); -} - -bool DownstreamQueue::can_activate(const std::string &host) const { - auto itr = host_entries_.find(make_host_key(host)); - if (itr == std::end(host_entries_)) { - return true; - } - auto &ent = (*itr).second; - return ent.num_active < conn_max_per_host_; -} - -namespace { -bool remove_host_entry_if_empty(const DownstreamQueue::HostEntry &ent, - DownstreamQueue::HostEntryMap &host_entries, - const std::string &host) { - if (ent.blocked.empty() && ent.num_active == 0) { - host_entries.erase(host); - return true; - } - return false; -} -} // namespace - -Downstream *DownstreamQueue::remove_and_get_blocked(Downstream *downstream) { - // Delete downstream when this function returns. - auto delptr = std::unique_ptr(downstream); - - if (downstream->get_dispatch_state() != Downstream::DISPATCH_ACTIVE) { - assert(downstream->get_dispatch_state() != Downstream::DISPATCH_NONE); - downstreams_.remove(downstream); - return nullptr; - } - - downstreams_.remove(downstream); - - auto &host = make_host_key(downstream); - auto &ent = find_host_entry(host); - --ent.num_active; - - if (remove_host_entry_if_empty(ent, host_entries_, host)) { - return nullptr; - } - - if (ent.num_active >= conn_max_per_host_) { - return nullptr; - } - - for (auto link = ent.blocked.head; link;) { - auto next = link->dlnext; - if (!link->downstream) { - ent.blocked.remove(link); - link = next; - continue; - } - auto next_downstream = link->downstream; - next_downstream->detach_blocked_link(link); - ent.blocked.remove(link); - delete link; - remove_host_entry_if_empty(ent, host_entries_, host); - return next_downstream; - } - return nullptr; -} - -Downstream *DownstreamQueue::get_downstreams() const { - return downstreams_.head; -} - -} // namespace shrpx diff --git a/src/shrpx_downstream_queue.h b/src/shrpx_downstream_queue.h deleted file mode 100644 index d327a05..0000000 --- a/src/shrpx_downstream_queue.h +++ /dev/null @@ -1,105 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2012 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#ifndef SHRPX_DOWNSTREAM_QUEUE_H -#define SHRPX_DOWNSTREAM_QUEUE_H - -#include "shrpx.h" - -#include -#include -#include -#include - -#include "template.h" - -using namespace nghttp2; - -namespace shrpx { - -class Downstream; - -// Link entry in HostEntry.blocked and downstream because downstream -// could be deleted in anytime and we'd like to find Downstream in -// O(1). Downstream has field to link back to this object. -struct BlockedLink { - Downstream *downstream; - BlockedLink *dlnext, *dlprev; -}; - -class DownstreamQueue { -public: - struct HostEntry { - // Set of stream ID that blocked by conn_max_per_host_. - DList blocked; - // The number of connections currently made to this host. - size_t num_active; - HostEntry(); - }; - - typedef std::map HostEntryMap; - - // conn_max_per_host == 0 means no limit for downstream connection. - DownstreamQueue(size_t conn_max_per_host = 0, bool unified_host = true); - ~DownstreamQueue(); - // Add |downstream| to this queue. This is entry point for - // Downstream object. - void add_pending(std::unique_ptr downstream); - // Set |downstream| to failure state, which means that downstream - // failed to connect to backend. - void mark_failure(Downstream *downstream); - // Set |downstream| to active state, which means that downstream - // connection has started. - void mark_active(Downstream *downstream); - // Set |downstream| to blocked state, which means that download - // connection was blocked because conn_max_per_host_ limit. - void mark_blocked(Downstream *downstream); - // Returns true if we can make downstream connection to given - // |host|. - bool can_activate(const std::string &host) const; - // Removes and frees |downstream| object. If |downstream| is in - // Downstream::DISPATCH_ACTIVE, this function may return Downstream - // object with the same target host in Downstream::DISPATCH_BLOCKED - // if its connection is now not blocked by conn_max_per_host_ limit. - Downstream *remove_and_get_blocked(Downstream *downstream); - Downstream *get_downstreams() const; - HostEntry &find_host_entry(const std::string &host); - const std::string &make_host_key(const std::string &host) const; - const std::string &make_host_key(Downstream *downstream) const; - -private: - // Per target host structure to keep track of the number of - // connections to the same host. - std::map host_entries_; - DList downstreams_; - // Maximum number of concurrent connections to the same host. - size_t conn_max_per_host_; - // true if downstream host is treated as the same. Used for reverse - // proxying. - bool unified_host_; -}; - -} // namespace shrpx - -#endif // SHRPX_DOWNSTREAM_QUEUE_H diff --git a/src/shrpx_downstream_test.cc b/src/shrpx_downstream_test.cc deleted file mode 100644 index 2e7a696..0000000 --- a/src/shrpx_downstream_test.cc +++ /dev/null @@ -1,160 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2013 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#include "shrpx_downstream_test.h" - -#include - -#include - -#include "shrpx_downstream.h" - -namespace shrpx { - -void test_downstream_index_request_headers(void) { - Downstream d(nullptr, nullptr, 0, 0); - d.add_request_header("1", "0"); - d.add_request_header("2", "1"); - d.add_request_header("Charlie", "2"); - d.add_request_header("Alpha", "3"); - d.add_request_header("Delta", "4"); - d.add_request_header("BravO", "5"); - d.add_request_header(":method", "6"); - d.add_request_header(":authority", "7"); - d.index_request_headers(); - - auto ans = Headers{{"1", "0"}, - {"2", "1"}, - {"charlie", "2"}, - {"alpha", "3"}, - {"delta", "4"}, - {"bravo", "5"}, - {":method", "6"}, - {":authority", "7"}}; - CU_ASSERT(ans == d.get_request_headers()); -} - -void test_downstream_index_response_headers(void) { - Downstream d(nullptr, nullptr, 0, 0); - d.add_response_header("Charlie", "0"); - d.add_response_header("Alpha", "1"); - d.add_response_header("Delta", "2"); - d.add_response_header("BravO", "3"); - d.index_response_headers(); - - auto ans = - Headers{{"charlie", "0"}, {"alpha", "1"}, {"delta", "2"}, {"bravo", "3"}}; - CU_ASSERT(ans == d.get_response_headers()); -} - -void test_downstream_get_request_header(void) { - Downstream d(nullptr, nullptr, 0, 0); - d.add_request_header("alpha", "0"); - d.add_request_header(":authority", "1"); - d.add_request_header("content-length", "2"); - d.index_request_headers(); - - // By token - CU_ASSERT(Header(":authority", "1") == - *d.get_request_header(http2::HD__AUTHORITY)); - CU_ASSERT(nullptr == d.get_request_header(http2::HD__METHOD)); - - // By name - CU_ASSERT(Header("alpha", "0") == *d.get_request_header("alpha")); - CU_ASSERT(nullptr == d.get_request_header("bravo")); -} - -void test_downstream_get_response_header(void) { - Downstream d(nullptr, nullptr, 0, 0); - d.add_response_header("alpha", "0"); - d.add_response_header(":status", "1"); - d.add_response_header("content-length", "2"); - d.index_response_headers(); - - // By token - CU_ASSERT(Header(":status", "1") == - *d.get_response_header(http2::HD__STATUS)); - CU_ASSERT(nullptr == d.get_response_header(http2::HD__METHOD)); -} - -void test_downstream_crumble_request_cookie(void) { - Downstream d(nullptr, nullptr, 0, 0); - d.add_request_header(":method", "get"); - d.add_request_header(":path", "/"); - auto val = "alpha; bravo; ; ;; charlie;;"; - d.add_request_header( - reinterpret_cast("cookie"), sizeof("cookie") - 1, - reinterpret_cast(val), strlen(val), true, -1); - d.add_request_header("cookie", ";delta"); - d.add_request_header("cookie", "echo"); - auto cookies = d.crumble_request_cookie(); - - Headers ans = {{"cookie", "alpha"}, - {"cookie", "bravo"}, - {"cookie", "charlie"}, - {"cookie", "delta"}, - {"cookie", "echo"}}; - CU_ASSERT(ans == cookies); - CU_ASSERT(cookies[0].no_index); - CU_ASSERT(cookies[1].no_index); - CU_ASSERT(cookies[2].no_index); -} - -void test_downstream_assemble_request_cookie(void) { - Downstream d(nullptr, nullptr, 0, 0); - d.add_request_header(":method", "get"); - d.add_request_header(":path", "/"); - d.add_request_header("cookie", "alpha"); - d.add_request_header("cookie", "bravo;"); - d.add_request_header("cookie", "charlie; "); - d.add_request_header("cookie", "delta;;"); - d.assemble_request_cookie(); - CU_ASSERT("alpha; bravo; charlie; delta" == d.get_assembled_request_cookie()); -} - -void test_downstream_rewrite_location_response_header(void) { - { - Downstream d(nullptr, nullptr, 0, 0); - d.set_request_downstream_host("localhost:3000"); - d.add_request_header("host", "localhost"); - d.add_response_header("location", "http://localhost:3000/"); - d.index_request_headers(); - d.index_response_headers(); - d.rewrite_location_response_header("https"); - auto location = d.get_response_header(http2::HD_LOCATION); - CU_ASSERT("https://localhost/" == (*location).value); - } - { - Downstream d(nullptr, nullptr, 0, 0); - d.set_request_downstream_host("localhost"); - d.set_request_http2_authority("localhost"); - d.add_response_header("location", "http://localhost:3000/"); - d.index_response_headers(); - d.rewrite_location_response_header("https"); - auto location = d.get_response_header(http2::HD_LOCATION); - CU_ASSERT("https://localhost/" == (*location).value); - } -} - -} // namespace shrpx diff --git a/src/shrpx_downstream_test.h b/src/shrpx_downstream_test.h deleted file mode 100644 index 67e8f6b..0000000 --- a/src/shrpx_downstream_test.h +++ /dev/null @@ -1,44 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2013 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#ifndef SHRPX_DOWNSTREAM_TEST_H -#define SHRPX_DOWNSTREAM_TEST_H - -#ifdef HAVE_CONFIG_H -#include -#endif // HAVE_CONFIG_H - -namespace shrpx { - -void test_downstream_index_request_headers(void); -void test_downstream_index_response_headers(void); -void test_downstream_get_request_header(void); -void test_downstream_get_response_header(void); -void test_downstream_crumble_request_cookie(void); -void test_downstream_assemble_request_cookie(void); -void test_downstream_rewrite_location_response_header(void); - -} // namespace shrpx - -#endif // SHRPX_DOWNSTREAM_TEST_H diff --git a/src/shrpx_error.h b/src/shrpx_error.h deleted file mode 100644 index f1d1bff..0000000 --- a/src/shrpx_error.h +++ /dev/null @@ -1,43 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2012 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#ifndef SHRPX_ERROR_H -#define SHRPX_ERROR_H - -#include "shrpx.h" - -namespace shrpx { - -// Deprecated, do not use. -enum ErrorCode { - SHRPX_ERR_SUCCESS = 0, - SHRPX_ERR_ERROR = -1, - SHRPX_ERR_NETWORK = -100, - SHRPX_ERR_EOF = -101, - SHRPX_ERR_INPROGRESS = -102, -}; - -} // namespace shrpx - -#endif // SHRPX_ERROR_H diff --git a/src/shrpx_http.cc b/src/shrpx_http.cc deleted file mode 100644 index c3f1aa7..0000000 --- a/src/shrpx_http.cc +++ /dev/null @@ -1,103 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2012 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#include "shrpx_http.h" - -#include "shrpx_config.h" -#include "shrpx_log.h" -#include "http2.h" -#include "util.h" - -using namespace nghttp2; - -namespace shrpx { - -namespace http { - -std::string create_error_html(unsigned int status_code) { - std::string res; - res.reserve(512); - auto status = http2::get_status_string(status_code); - res += R"()"; - res += status; - res += "

      "; - res += status; - res += "

      "; - res += get_config()->server_name; - res += " at port "; - res += util::utos(get_config()->port); - res += "
      "; - return res; -} - -std::string create_via_header_value(int major, int minor) { - std::string hdrs; - hdrs += static_cast(major + '0'); - if (major < 2) { - hdrs += "."; - hdrs += static_cast(minor + '0'); - } - hdrs += " nghttpx"; - return hdrs; -} - -std::string colorizeHeaders(const char *hdrs) { - std::string nhdrs; - const char *p = strchr(hdrs, '\n'); - if (!p) { - // Not valid HTTP header - return hdrs; - } - nhdrs.append(hdrs, p + 1); - ++p; - while (1) { - const char *np = strchr(p, ':'); - if (!np) { - nhdrs.append(p); - break; - } - nhdrs += TTY_HTTP_HD; - nhdrs.append(p, np); - nhdrs += TTY_RST; - p = np; - np = strchr(p, '\n'); - if (!np) { - nhdrs.append(p); - break; - } - nhdrs.append(p, np + 1); - p = np + 1; - } - return nhdrs; -} - -ssize_t select_padding_callback(nghttp2_session *session, - const nghttp2_frame *frame, size_t max_payload, - void *user_data) { - return std::min(max_payload, frame->hd.length + get_config()->padding); -} - -} // namespace http - -} // namespace shrpx diff --git a/src/shrpx_http.h b/src/shrpx_http.h deleted file mode 100644 index 65dbe0e..0000000 --- a/src/shrpx_http.h +++ /dev/null @@ -1,53 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2012 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#ifndef SHRPX_HTTP_H -#define SHRPX_HTTP_H - -#include "shrpx.h" - -#include - -#include - -namespace shrpx { - -namespace http { - -std::string create_error_html(unsigned int status_code); - -std::string create_via_header_value(int major, int minor); - -// Adds ANSI color codes to HTTP headers |hdrs|. -std::string colorizeHeaders(const char *hdrs); - -ssize_t select_padding_callback(nghttp2_session *session, - const nghttp2_frame *frame, size_t max_payload, - void *user_data); - -} // namespace http - -} // namespace shrpx - -#endif // SHRPX_HTTP_H diff --git a/src/shrpx_http2_downstream_connection.cc b/src/shrpx_http2_downstream_connection.cc deleted file mode 100644 index f5cba83..0000000 --- a/src/shrpx_http2_downstream_connection.cc +++ /dev/null @@ -1,569 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2012 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#include "shrpx_http2_downstream_connection.h" - -#ifdef HAVE_UNISTD_H -#include -#endif // HAVE_UNISTD_H - -#include "http-parser/http_parser.h" - -#include "shrpx_client_handler.h" -#include "shrpx_upstream.h" -#include "shrpx_downstream.h" -#include "shrpx_config.h" -#include "shrpx_error.h" -#include "shrpx_http.h" -#include "shrpx_http2_session.h" -#include "http2.h" -#include "util.h" - -using namespace nghttp2; - -namespace shrpx { - -Http2DownstreamConnection::Http2DownstreamConnection( - DownstreamConnectionPool *dconn_pool, Http2Session *http2session) - : DownstreamConnection(dconn_pool), dlnext(nullptr), dlprev(nullptr), - http2session_(http2session), sd_(nullptr) {} - -Http2DownstreamConnection::~Http2DownstreamConnection() { - if (LOG_ENABLED(INFO)) { - DCLOG(INFO, this) << "Deleting"; - } - if (downstream_) { - downstream_->disable_downstream_rtimer(); - downstream_->disable_downstream_wtimer(); - - uint32_t error_code; - if (downstream_->get_request_state() == Downstream::STREAM_CLOSED && - downstream_->get_upgraded()) { - // For upgraded connection, send NO_ERROR. Should we consider - // request states other than Downstream::STREAM_CLOSED ? - error_code = NGHTTP2_NO_ERROR; - } else { - error_code = NGHTTP2_INTERNAL_ERROR; - } - - if (downstream_->get_downstream_stream_id() != -1) { - if (LOG_ENABLED(INFO)) { - DCLOG(INFO, this) << "Submit RST_STREAM for DOWNSTREAM:" << downstream_ - << ", stream_id=" - << downstream_->get_downstream_stream_id() - << ", error_code=" << error_code; - } - - submit_rst_stream(downstream_, error_code); - - http2session_->consume(downstream_->get_downstream_stream_id(), - downstream_->get_response_datalen()); - - downstream_->reset_response_datalen(); - - http2session_->signal_write(); - } - } - http2session_->remove_downstream_connection(this); - // Downstream and DownstreamConnection may be deleted - // asynchronously. - if (downstream_) { - downstream_->release_downstream_connection(); - } - if (LOG_ENABLED(INFO)) { - DCLOG(INFO, this) << "Deleted"; - } -} - -int Http2DownstreamConnection::attach_downstream(Downstream *downstream) { - if (LOG_ENABLED(INFO)) { - DCLOG(INFO, this) << "Attaching to DOWNSTREAM:" << downstream; - } - http2session_->add_downstream_connection(this); - if (http2session_->get_state() == Http2Session::DISCONNECTED) { - http2session_->signal_write(); - } - - downstream_ = downstream; - downstream_->reset_downstream_rtimer(); - - return 0; -} - -void Http2DownstreamConnection::detach_downstream(Downstream *downstream) { - if (LOG_ENABLED(INFO)) { - DCLOG(INFO, this) << "Detaching from DOWNSTREAM:" << downstream; - } - if (submit_rst_stream(downstream) == 0) { - http2session_->signal_write(); - } - - if (downstream_->get_downstream_stream_id() != -1) { - http2session_->consume(downstream_->get_downstream_stream_id(), - downstream_->get_response_datalen()); - - downstream_->reset_response_datalen(); - - http2session_->signal_write(); - } - - downstream->disable_downstream_rtimer(); - downstream->disable_downstream_wtimer(); - downstream_ = nullptr; -} - -int Http2DownstreamConnection::submit_rst_stream(Downstream *downstream, - uint32_t error_code) { - int rv = -1; - if (http2session_->get_state() == Http2Session::CONNECTED && - downstream->get_downstream_stream_id() != -1) { - switch (downstream->get_response_state()) { - case Downstream::MSG_RESET: - case Downstream::MSG_BAD_HEADER: - case Downstream::MSG_COMPLETE: - break; - default: - if (LOG_ENABLED(INFO)) { - DCLOG(INFO, this) << "Submit RST_STREAM for DOWNSTREAM:" << downstream - << ", stream_id=" - << downstream->get_downstream_stream_id(); - } - rv = http2session_->submit_rst_stream( - downstream->get_downstream_stream_id(), error_code); - } - } - return rv; -} - -namespace { -ssize_t http2_data_read_callback(nghttp2_session *session, int32_t stream_id, - uint8_t *buf, size_t length, - uint32_t *data_flags, - nghttp2_data_source *source, void *user_data) { - int rv; - auto sd = static_cast( - nghttp2_session_get_stream_user_data(session, stream_id)); - if (!sd || !sd->dconn) { - return NGHTTP2_ERR_DEFERRED; - } - auto dconn = static_cast(source->ptr); - auto downstream = dconn->get_downstream(); - if (!downstream) { - // In this case, RST_STREAM should have been issued. But depending - // on the priority, DATA frame may come first. - return NGHTTP2_ERR_DEFERRED; - } - auto input = downstream->get_request_buf(); - auto nread = input->remove(buf, length); - auto input_empty = input->rleft() == 0; - - if (nread > 0) { - // This is important because it will handle flow control - // stuff. - if (downstream->get_upstream()->resume_read(SHRPX_NO_BUFFER, downstream, - nread) != 0) { - // In this case, downstream may be deleted. - return NGHTTP2_ERR_CALLBACK_FAILURE; - } - - // Check dconn is still alive because Upstream::resume_read() - // may delete downstream which will delete dconn. - if (sd->dconn == nullptr) { - return NGHTTP2_ERR_DEFERRED; - } - } - - if (input_empty && - downstream->get_request_state() == Downstream::MSG_COMPLETE && - // If connection is upgraded, don't set EOF flag, since HTTP/1 - // will set MSG_COMPLETE to request state after upgrade response - // header is seen. - (!downstream->get_upgrade_request() || - (downstream->get_response_state() == Downstream::HEADER_COMPLETE && - !downstream->get_upgraded()))) { - - *data_flags |= NGHTTP2_DATA_FLAG_EOF; - - auto &trailers = downstream->get_request_trailers(); - if (!trailers.empty()) { - std::vector nva; - nva.reserve(trailers.size()); - http2::copy_headers_to_nva(nva, trailers); - if (!nva.empty()) { - rv = nghttp2_submit_trailer(session, stream_id, nva.data(), nva.size()); - if (rv != 0) { - if (nghttp2_is_fatal(rv)) { - return NGHTTP2_ERR_CALLBACK_FAILURE; - } - } else { - *data_flags |= NGHTTP2_DATA_FLAG_NO_END_STREAM; - } - } - } - } - - if (!input_empty) { - downstream->reset_downstream_wtimer(); - } else { - downstream->disable_downstream_wtimer(); - } - - if (nread == 0 && (*data_flags & NGHTTP2_DATA_FLAG_EOF) == 0) { - downstream->disable_downstream_wtimer(); - - return NGHTTP2_ERR_DEFERRED; - } - - return nread; -} -} // namespace - -int Http2DownstreamConnection::push_request_headers() { - int rv; - if (!downstream_) { - return 0; - } - if (!http2session_->can_push_request()) { - // The HTTP2 session to the backend has not been established or - // connection is now being checked. This function will be called - // again just after it is established. - downstream_->set_request_pending(true); - http2session_->start_checking_connection(); - return 0; - } - - downstream_->set_request_pending(false); - - auto no_host_rewrite = get_config()->no_host_rewrite || - get_config()->http2_proxy || - get_config()->client_proxy || - downstream_->get_request_method() == "CONNECT"; - - // http2session_ has already in CONNECTED state, so we can get - // addr_idx here. - auto addr_idx = http2session_->get_addr_idx(); - auto downstream_hostport = - get_config()->downstream_addrs[addr_idx].hostport.get(); - - const char *authority = nullptr, *host = nullptr; - if (!no_host_rewrite) { - if (!downstream_->get_request_http2_authority().empty()) { - authority = downstream_hostport; - } - if (downstream_->get_request_header(http2::HD_HOST)) { - host = downstream_hostport; - } - } else { - if (!downstream_->get_request_http2_authority().empty()) { - authority = downstream_->get_request_http2_authority().c_str(); - } - auto h = downstream_->get_request_header(http2::HD_HOST); - if (h) { - host = h->value.c_str(); - } - } - - if (!authority && !host) { - // upstream is HTTP/1.0. We use backend server's host - // nonetheless. - host = downstream_hostport; - } - - if (authority) { - downstream_->set_request_downstream_host(authority); - } else { - downstream_->set_request_downstream_host(host); - } - - size_t nheader = downstream_->get_request_headers().size(); - - Headers cookies; - if (!get_config()->http2_no_cookie_crumbling) { - cookies = downstream_->crumble_request_cookie(); - } - - // 8 means: - // 1. :method - // 2. :scheme - // 3. :path - // 4. :authority or host (at least either of them exists) - // 5. via (optional) - // 6. x-forwarded-for (optional) - // 7. x-forwarded-proto (optional) - // 8. te (optional) - auto nva = std::vector(); - nva.reserve(nheader + 8 + cookies.size()); - - std::string via_value; - std::string xff_value; - std::string scheme, uri_authority, path, query; - - nva.push_back( - http2::make_nv_ls(":method", downstream_->get_request_method())); - - if (downstream_->get_request_method() == "CONNECT") { - if (authority) { - nva.push_back(http2::make_nv_lc(":authority", authority)); - } else { - nva.push_back( - http2::make_nv_ls(":authority", downstream_->get_request_path())); - } - } else { - if (!downstream_->get_request_http2_scheme().empty()) { - nva.push_back(http2::make_nv_ls(":scheme", - downstream_->get_request_http2_scheme())); - } else if (client_handler_->get_ssl()) { - nva.push_back(http2::make_nv_ll(":scheme", "https")); - } else { - nva.push_back(http2::make_nv_ll(":scheme", "http")); - } - - if (authority) { - nva.push_back(http2::make_nv_lc(":authority", authority)); - } - - nva.push_back(http2::make_nv_ls(":path", downstream_->get_request_path())); - } - - // only emit host header field if :authority is not emitted. They - // both must be the same value. - if (!authority && host) { - nva.push_back(http2::make_nv_lc("host", host)); - } - - http2::copy_headers_to_nva(nva, downstream_->get_request_headers()); - - bool chunked_encoding = false; - auto transfer_encoding = - downstream_->get_request_header(http2::HD_TRANSFER_ENCODING); - if (transfer_encoding && - util::strieq_l("chunked", (*transfer_encoding).value)) { - chunked_encoding = true; - } - - for (auto &nv : cookies) { - nva.push_back(http2::make_nv(nv.name, nv.value, nv.no_index)); - } - - auto xff = downstream_->get_request_header(http2::HD_X_FORWARDED_FOR); - if (get_config()->add_x_forwarded_for) { - if (xff && !get_config()->strip_incoming_x_forwarded_for) { - xff_value = (*xff).value; - xff_value += ", "; - } - xff_value += - downstream_->get_upstream()->get_client_handler()->get_ipaddr(); - nva.push_back(http2::make_nv_ls("x-forwarded-for", xff_value)); - } else if (xff && !get_config()->strip_incoming_x_forwarded_for) { - nva.push_back(http2::make_nv_ls("x-forwarded-for", (*xff).value)); - } - - if (!get_config()->http2_proxy && !get_config()->client_proxy && - downstream_->get_request_method() != "CONNECT") { - // We use same protocol with :scheme header field - if (scheme.empty()) { - if (client_handler_->get_ssl()) { - nva.push_back(http2::make_nv_ll("x-forwarded-proto", "https")); - } else { - nva.push_back(http2::make_nv_ll("x-forwarded-proto", "http")); - } - } else { - nva.push_back(http2::make_nv_ls("x-forwarded-proto", scheme)); - } - } - - auto via = downstream_->get_request_header(http2::HD_VIA); - if (get_config()->no_via) { - if (via) { - nva.push_back(http2::make_nv_ls("via", (*via).value)); - } - } else { - if (via) { - via_value = (*via).value; - via_value += ", "; - } - via_value += http::create_via_header_value( - downstream_->get_request_major(), downstream_->get_request_minor()); - nva.push_back(http2::make_nv_ls("via", via_value)); - } - - auto te = downstream_->get_request_header(http2::HD_TE); - // HTTP/1 upstream request can contain keyword other than - // "trailers". We just forward "trailers". - // TODO more strict handling required here. - if (te && util::strifind(te->value.c_str(), "trailers")) { - nva.push_back(http2::make_nv_ll("te", "trailers")); - } - - if (LOG_ENABLED(INFO)) { - std::stringstream ss; - for (auto &nv : nva) { - ss << TTY_HTTP_HD << nv.name << TTY_RST << ": " << nv.value << "\n"; - } - DCLOG(INFO, this) << "HTTP request headers\n" << ss.str(); - } - - auto content_length = - downstream_->get_request_header(http2::HD_CONTENT_LENGTH); - // TODO check content-length: 0 case - - if (downstream_->get_request_method() == "CONNECT" || chunked_encoding || - content_length || downstream_->get_request_http2_expect_body()) { - // Request-body is expected. - nghttp2_data_provider data_prd; - data_prd.source.ptr = this; - data_prd.read_callback = http2_data_read_callback; - rv = http2session_->submit_request(this, downstream_->get_priority(), - nva.data(), nva.size(), &data_prd); - } else { - rv = http2session_->submit_request(this, downstream_->get_priority(), - nva.data(), nva.size(), nullptr); - } - if (rv != 0) { - DCLOG(FATAL, this) << "nghttp2_submit_request() failed"; - return -1; - } - - downstream_->reset_downstream_wtimer(); - - http2session_->signal_write(); - return 0; -} - -int Http2DownstreamConnection::push_upload_data_chunk(const uint8_t *data, - size_t datalen) { - int rv; - auto output = downstream_->get_request_buf(); - output->append(data, datalen); - if (downstream_->get_downstream_stream_id() != -1) { - rv = http2session_->resume_data(this); - if (rv != 0) { - return -1; - } - - downstream_->ensure_downstream_wtimer(); - - http2session_->signal_write(); - } - return 0; -} - -int Http2DownstreamConnection::end_upload_data() { - int rv; - if (downstream_->get_downstream_stream_id() != -1) { - rv = http2session_->resume_data(this); - if (rv != 0) { - return -1; - } - - downstream_->ensure_downstream_wtimer(); - - http2session_->signal_write(); - } - return 0; -} - -int Http2DownstreamConnection::resume_read(IOCtrlReason reason, - size_t consumed) { - int rv; - - if (http2session_->get_state() != Http2Session::CONNECTED || - !http2session_->get_flow_control()) { - return 0; - } - - if (!downstream_ || downstream_->get_downstream_stream_id() == -1) { - return 0; - } - - if (consumed > 0) { - assert(downstream_->get_response_datalen() >= consumed); - - rv = http2session_->consume(downstream_->get_downstream_stream_id(), - consumed); - - if (rv != 0) { - return -1; - } - - downstream_->dec_response_datalen(consumed); - - http2session_->signal_write(); - } - - return 0; -} - -int Http2DownstreamConnection::on_read() { return 0; } - -int Http2DownstreamConnection::on_write() { return 0; } - -void Http2DownstreamConnection::attach_stream_data(StreamData *sd) { - // It is possible sd->dconn is not NULL. sd is detached when - // on_stream_close_callback. Before that, after MSG_COMPLETE is set - // to Downstream::set_response_state(), upstream's readcb is called - // and execution path eventually could reach here. Since the - // response was already handled, we just detach sd. - detach_stream_data(); - sd_ = sd; - sd_->dconn = this; -} - -StreamData *Http2DownstreamConnection::detach_stream_data() { - if (sd_) { - auto sd = sd_; - sd_ = nullptr; - sd->dconn = nullptr; - return sd; - } - return nullptr; -} - -int Http2DownstreamConnection::on_priority_change(int32_t pri) { - int rv; - if (downstream_->get_priority() == pri) { - return 0; - } - downstream_->set_priority(pri); - if (http2session_->get_state() != Http2Session::CONNECTED) { - return 0; - } - rv = http2session_->submit_priority(this, pri); - if (rv != 0) { - DLOG(FATAL, this) << "nghttp2_submit_priority() failed"; - return -1; - } - http2session_->signal_write(); - return 0; -} - -int Http2DownstreamConnection::on_timeout() { - if (!downstream_) { - return 0; - } - - return submit_rst_stream(downstream_, NGHTTP2_NO_ERROR); -} - -} // namespace shrpx diff --git a/src/shrpx_http2_downstream_connection.h b/src/shrpx_http2_downstream_connection.h deleted file mode 100644 index a2a78a3..0000000 --- a/src/shrpx_http2_downstream_connection.h +++ /dev/null @@ -1,86 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2012 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#ifndef SHRPX_HTTP2_DOWNSTREAM_CONNECTION_H -#define SHRPX_HTTP2_DOWNSTREAM_CONNECTION_H - -#include "shrpx.h" - -#include - -#include - -#include "shrpx_downstream_connection.h" - -namespace shrpx { - -struct StreamData; -class Http2Session; -class DownstreamConnectionPool; - -class Http2DownstreamConnection : public DownstreamConnection { -public: - Http2DownstreamConnection(DownstreamConnectionPool *dconn_pool, - Http2Session *http2session); - virtual ~Http2DownstreamConnection(); - virtual int attach_downstream(Downstream *downstream); - virtual void detach_downstream(Downstream *downstream); - - virtual int push_request_headers(); - virtual int push_upload_data_chunk(const uint8_t *data, size_t datalen); - virtual int end_upload_data(); - - virtual void pause_read(IOCtrlReason reason) {} - virtual int resume_read(IOCtrlReason reason, size_t consumed); - virtual void force_resume_read() {} - - virtual int on_read(); - virtual int on_write(); - virtual int on_timeout(); - - virtual void on_upstream_change(Upstream *upstream) {} - virtual int on_priority_change(int32_t pri); - - // This object is not poolable because we dont' have facility to - // migrate to another Http2Session object. - virtual bool poolable() const { return false; } - - int send(); - - void attach_stream_data(StreamData *sd); - StreamData *detach_stream_data(); - - int submit_rst_stream(Downstream *downstream, - uint32_t error_code = NGHTTP2_INTERNAL_ERROR); - - Http2DownstreamConnection *dlnext, *dlprev; - -private: - Http2Session *http2session_; - StreamData *sd_; -}; - -} // namespace shrpx - -#endif // SHRPX_HTTP2_DOWNSTREAM_CONNECTION_H diff --git a/src/shrpx_http2_session.cc b/src/shrpx_http2_session.cc deleted file mode 100644 index c071a72..0000000 --- a/src/shrpx_http2_session.cc +++ /dev/null @@ -1,1747 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2012 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#include "shrpx_http2_session.h" - -#include -#ifdef HAVE_UNISTD_H -#include -#endif // HAVE_UNISTD_H - -#include - -#include - -#include "shrpx_upstream.h" -#include "shrpx_downstream.h" -#include "shrpx_config.h" -#include "shrpx_error.h" -#include "shrpx_http2_downstream_connection.h" -#include "shrpx_client_handler.h" -#include "shrpx_ssl.h" -#include "shrpx_http.h" -#include "shrpx_worker.h" -#include "shrpx_connect_blocker.h" -#include "http2.h" -#include "util.h" -#include "base64.h" - -using namespace nghttp2; - -namespace shrpx { - -namespace { -const ev_tstamp CONNCHK_TIMEOUT = 5.; -const ev_tstamp CONNCHK_PING_TIMEOUT = 1.; -} // namespace - -namespace { -void connchk_timeout_cb(struct ev_loop *loop, ev_timer *w, int revents) { - auto http2session = static_cast(w->data); - - ev_timer_stop(loop, w); - - switch (http2session->get_connection_check_state()) { - case Http2Session::CONNECTION_CHECK_STARTED: - // ping timeout; disconnect - if (LOG_ENABLED(INFO)) { - SSLOG(INFO, http2session) << "ping timeout"; - } - http2session->disconnect(); - return; - default: - if (LOG_ENABLED(INFO)) { - SSLOG(INFO, http2session) << "connection check required"; - } - http2session->set_connection_check_state( - Http2Session::CONNECTION_CHECK_REQUIRED); - } -} -} // namespace - -namespace { -void settings_timeout_cb(struct ev_loop *loop, ev_timer *w, int revents) { - auto http2session = static_cast(w->data); - http2session->stop_settings_timer(); - SSLOG(INFO, http2session) << "SETTINGS timeout"; - if (http2session->terminate_session(NGHTTP2_SETTINGS_TIMEOUT) != 0) { - http2session->disconnect(); - return; - } - http2session->signal_write(); -} -} // namespace - -namespace { -void timeoutcb(struct ev_loop *loop, ev_timer *w, int revents) { - auto conn = static_cast(w->data); - auto http2session = static_cast(conn->data); - - if (LOG_ENABLED(INFO)) { - SSLOG(INFO, http2session) << "Timeout"; - } - - http2session->disconnect(http2session->get_state() == - Http2Session::CONNECTING); -} -} // namespace - -namespace { -void readcb(struct ev_loop *loop, ev_io *w, int revents) { - int rv; - auto conn = static_cast(w->data); - auto http2session = static_cast(conn->data); - rv = http2session->do_read(); - if (rv != 0) { - http2session->disconnect(http2session->should_hard_fail()); - return; - } - http2session->connection_alive(); - - rv = http2session->do_write(); - if (rv != 0) { - http2session->disconnect(http2session->should_hard_fail()); - return; - } -} -} // namespace - -namespace { -void writecb(struct ev_loop *loop, ev_io *w, int revents) { - int rv; - auto conn = static_cast(w->data); - auto http2session = static_cast(conn->data); - rv = http2session->do_write(); - if (rv != 0) { - http2session->disconnect(http2session->should_hard_fail()); - return; - } - http2session->reset_connection_check_timer_if_not_checking(); -} -} // namespace - -Http2Session::Http2Session(struct ev_loop *loop, SSL_CTX *ssl_ctx, - ConnectBlocker *connect_blocker, Worker *worker) - : conn_(loop, -1, nullptr, get_config()->downstream_write_timeout, - get_config()->downstream_read_timeout, 0, 0, 0, 0, writecb, readcb, - timeoutcb, this), - worker_(worker), connect_blocker_(connect_blocker), ssl_ctx_(ssl_ctx), - session_(nullptr), data_pending_(nullptr), data_pendinglen_(0), - addr_idx_(0), state_(DISCONNECTED), - connection_check_state_(CONNECTION_CHECK_NONE), flow_control_(false) { - - read_ = write_ = &Http2Session::noop; - on_read_ = on_write_ = &Http2Session::noop; - - // We will resuse this many times, so use repeat timeout value. The - // timeout value is set later. - ev_timer_init(&connchk_timer_, connchk_timeout_cb, 0., 0.); - - connchk_timer_.data = this; - - // SETTINGS ACK timeout is 10 seconds for now. We will resuse this - // many times, so use repeat timeout value. - ev_timer_init(&settings_timer_, settings_timeout_cb, 0., 10.); - - settings_timer_.data = this; -} - -Http2Session::~Http2Session() { disconnect(); } - -int Http2Session::disconnect(bool hard) { - if (LOG_ENABLED(INFO)) { - SSLOG(INFO, this) << "Disconnecting"; - } - nghttp2_session_del(session_); - session_ = nullptr; - - rb_.reset(); - wb_.reset(); - - conn_.rlimit.stopw(); - conn_.wlimit.stopw(); - - ev_timer_stop(conn_.loop, &settings_timer_); - ev_timer_stop(conn_.loop, &connchk_timer_); - - read_ = write_ = &Http2Session::noop; - on_read_ = on_write_ = &Http2Session::noop; - - conn_.disconnect(); - - addr_idx_ = 0; - - if (proxy_htp_) { - proxy_htp_.reset(); - } - - connection_check_state_ = CONNECTION_CHECK_NONE; - state_ = DISCONNECTED; - - // Delete all client handler associated to Downstream. When deleting - // Http2DownstreamConnection, it calls this object's - // remove_downstream_connection(). The multiple - // Http2DownstreamConnection objects belong to the same - // ClientHandler object. So first dump ClientHandler objects. We - // want to allow creating new pending Http2DownstreamConnection with - // this object. In order to achieve this, we first swap dconns_ and - // streams_. Upstream::on_downstream_reset() may add - // Http2DownstreamConnection. - auto dconns = std::move(dconns_); - auto streams = std::move(streams_); - - std::set handlers; - for (auto dc = dconns.head; dc; dc = dc->dlnext) { - if (!dc->get_client_handler()) { - continue; - } - handlers.insert(dc->get_client_handler()); - } - for (auto h : handlers) { - if (h->get_upstream()->on_downstream_reset(hard) != 0) { - delete h; - } - } - - for (auto s = streams.head; s;) { - auto next = s->dlnext; - delete s; - s = next; - } - - return 0; -} - -int Http2Session::check_cert() { return ssl::check_cert(conn_.tls.ssl); } - -int Http2Session::initiate_connection() { - int rv = 0; - - if (state_ == DISCONNECTED) { - if (connect_blocker_->blocked()) { - if (LOG_ENABLED(INFO)) { - DCLOG(INFO, this) - << "Downstream connection was blocked by connect_blocker"; - } - return -1; - } - - auto worker_stat = worker_->get_worker_stat(); - addr_idx_ = worker_stat->next_downstream; - ++worker_stat->next_downstream; - worker_stat->next_downstream %= get_config()->downstream_addrs.size(); - - if (LOG_ENABLED(INFO)) { - SSLOG(INFO, this) << "Using downstream address idx=" << addr_idx_ - << " out of " << get_config()->downstream_addrs.size(); - } - } - - auto &downstream_addr = get_config()->downstream_addrs[addr_idx_]; - - if (get_config()->downstream_http_proxy_host && state_ == DISCONNECTED) { - if (LOG_ENABLED(INFO)) { - SSLOG(INFO, this) << "Connecting to the proxy " - << get_config()->downstream_http_proxy_host.get() << ":" - << get_config()->downstream_http_proxy_port; - } - - conn_.fd = util::create_nonblock_socket( - get_config()->downstream_http_proxy_addr.storage.ss_family); - - if (conn_.fd == -1) { - connect_blocker_->on_failure(); - return -1; - } - - rv = connect(conn_.fd, &get_config()->downstream_http_proxy_addr.sa, - get_config()->downstream_http_proxy_addrlen); - if (rv != 0 && errno != EINPROGRESS) { - SSLOG(ERROR, this) << "Failed to connect to the proxy " - << get_config()->downstream_http_proxy_host.get() - << ":" << get_config()->downstream_http_proxy_port; - connect_blocker_->on_failure(); - return -1; - } - - ev_io_set(&conn_.rev, conn_.fd, EV_READ); - ev_io_set(&conn_.wev, conn_.fd, EV_WRITE); - - conn_.wlimit.startw(); - - // TODO we should have timeout for connection establishment - ev_timer_again(conn_.loop, &conn_.wt); - - write_ = &Http2Session::connected; - - on_read_ = &Http2Session::downstream_read_proxy; - on_write_ = &Http2Session::downstream_connect_proxy; - - proxy_htp_ = make_unique(); - http_parser_init(proxy_htp_.get(), HTTP_RESPONSE); - proxy_htp_->data = this; - - state_ = PROXY_CONNECTING; - - return 0; - } - - if (state_ == DISCONNECTED || state_ == PROXY_CONNECTED) { - if (LOG_ENABLED(INFO)) { - SSLOG(INFO, this) << "Connecting to downstream server"; - } - if (ssl_ctx_) { - // We are establishing TLS connection. - conn_.tls.ssl = SSL_new(ssl_ctx_); - if (!conn_.tls.ssl) { - SSLOG(ERROR, this) << "SSL_new() failed: " - << ERR_error_string(ERR_get_error(), NULL); - return -1; - } - - const char *sni_name = nullptr; - if (get_config()->backend_tls_sni_name) { - sni_name = get_config()->backend_tls_sni_name.get(); - } else { - sni_name = downstream_addr.host.get(); - } - - if (sni_name && !util::numeric_host(sni_name)) { - // TLS extensions: SNI. There is no documentation about the return - // code for this function (actually this is macro wrapping SSL_ctrl - // at the time of this writing). - SSL_set_tlsext_host_name(conn_.tls.ssl, sni_name); - } - // If state_ == PROXY_CONNECTED, we has connected to the proxy - // using conn_.fd and tunnel has been established. - if (state_ == DISCONNECTED) { - assert(conn_.fd == -1); - - conn_.fd = util::create_nonblock_socket( - downstream_addr.addr.storage.ss_family); - if (conn_.fd == -1) { - connect_blocker_->on_failure(); - return -1; - } - - rv = connect(conn_.fd, - // TODO maybe not thread-safe? - const_cast(&downstream_addr.addr.sa), - downstream_addr.addrlen); - if (rv != 0 && errno != EINPROGRESS) { - connect_blocker_->on_failure(); - return -1; - } - - ev_io_set(&conn_.rev, conn_.fd, EV_READ); - ev_io_set(&conn_.wev, conn_.fd, EV_WRITE); - } - - if (SSL_set_fd(conn_.tls.ssl, conn_.fd) == 0) { - return -1; - } - - SSL_set_connect_state(conn_.tls.ssl); - } else { - if (state_ == DISCONNECTED) { - // Without TLS and proxy. - assert(conn_.fd == -1); - - conn_.fd = util::create_nonblock_socket( - downstream_addr.addr.storage.ss_family); - - if (conn_.fd == -1) { - connect_blocker_->on_failure(); - return -1; - } - - rv = connect(conn_.fd, const_cast(&downstream_addr.addr.sa), - downstream_addr.addrlen); - if (rv != 0 && errno != EINPROGRESS) { - connect_blocker_->on_failure(); - return -1; - } - - ev_io_set(&conn_.rev, conn_.fd, EV_READ); - ev_io_set(&conn_.wev, conn_.fd, EV_WRITE); - } - } - - write_ = &Http2Session::connected; - - on_write_ = &Http2Session::downstream_write; - on_read_ = &Http2Session::downstream_read; - - // We have been already connected when no TLS and proxy is used. - if (state_ != CONNECTED) { - state_ = CONNECTING; - conn_.wlimit.startw(); - // TODO we should have timeout for connection establishment - ev_timer_again(conn_.loop, &conn_.wt); - } else { - conn_.rlimit.startw(); - ev_timer_again(conn_.loop, &conn_.rt); - } - - return 0; - } - - // Unreachable - DIE(); - return 0; -} - -namespace { -int htp_hdrs_completecb(http_parser *htp) { - auto http2session = static_cast(htp->data); - - // We only read HTTP header part. If tunneling succeeds, response - // body is a different protocol (HTTP/2 in this case), we don't read - // them here. - // - // Here is a caveat: http-parser returns 1 less bytes if we pause - // here. The reason why they do this is probably they want to eat - // last 1 byte in s_headers_done state, on the other hand, this - // callback is called its previous state s_headers_almost_done. We - // will do "+ 1" to the return value to workaround this. - http_parser_pause(htp, 1); - - // We just check status code here - if (htp->status_code == 200) { - if (LOG_ENABLED(INFO)) { - SSLOG(INFO, http2session) << "Tunneling success"; - } - http2session->set_state(Http2Session::PROXY_CONNECTED); - - return 0; - } - - SSLOG(WARN, http2session) << "Tunneling failed: " << htp->status_code; - http2session->set_state(Http2Session::PROXY_FAILED); - - return 0; -} -} // namespace - -namespace { -http_parser_settings htp_hooks = { - nullptr, // http_cb on_message_begin; - nullptr, // http_data_cb on_url; - nullptr, // http_data_cb on_status; - nullptr, // http_data_cb on_header_field; - nullptr, // http_data_cb on_header_value; - htp_hdrs_completecb, // http_cb on_headers_complete; - nullptr, // http_data_cb on_body; - nullptr // http_cb on_message_complete; -}; -} // namespace - -int Http2Session::downstream_read_proxy() { - if (rb_.rleft() == 0) { - return 0; - } - - size_t nread = - http_parser_execute(proxy_htp_.get(), &htp_hooks, - reinterpret_cast(rb_.pos), rb_.rleft()); - - rb_.drain(nread); - - auto htperr = HTTP_PARSER_ERRNO(proxy_htp_.get()); - - if (htperr == HPE_PAUSED) { - switch (state_) { - case Http2Session::PROXY_CONNECTED: - // we need to increment nread by 1 since http_parser_execute() - // returns 1 less value we expect. This means taht - // rb_.pos[nread] points to \x0a (LF), which is last byte of - // empty line to terminate headers. We want to eat that byte - // here. - rb_.drain(1); - - // Initiate SSL/TLS handshake through established tunnel. - if (initiate_connection() != 0) { - return -1; - } - return 0; - case Http2Session::PROXY_FAILED: - return -1; - } - // should not be here - assert(0); - } - - if (htperr != HPE_OK) { - return -1; - } - - return 0; -} - -int Http2Session::downstream_connect_proxy() { - if (LOG_ENABLED(INFO)) { - SSLOG(INFO, this) << "Connected to the proxy"; - } - auto &downstream_addr = get_config()->downstream_addrs[addr_idx_]; - - std::string req = "CONNECT "; - req += downstream_addr.hostport.get(); - if (downstream_addr.port == 80 || downstream_addr.port == 443) { - req += ":"; - req += util::utos(downstream_addr.port); - } - req += " HTTP/1.1\r\nHost: "; - req += downstream_addr.host.get(); - req += "\r\n"; - if (get_config()->downstream_http_proxy_userinfo) { - req += "Proxy-Authorization: Basic "; - size_t len = strlen(get_config()->downstream_http_proxy_userinfo.get()); - req += base64::encode(get_config()->downstream_http_proxy_userinfo.get(), - get_config()->downstream_http_proxy_userinfo.get() + - len); - req += "\r\n"; - } - req += "\r\n"; - if (LOG_ENABLED(INFO)) { - SSLOG(INFO, this) << "HTTP proxy request headers\n" << req; - } - auto nwrite = wb_.write(req.c_str(), req.size()); - if (nwrite != req.size()) { - SSLOG(WARN, this) << "HTTP proxy request is too large"; - return -1; - } - on_write_ = &Http2Session::noop; - - signal_write(); - return 0; -} - -void Http2Session::add_downstream_connection(Http2DownstreamConnection *dconn) { - dconns_.append(dconn); -} - -void -Http2Session::remove_downstream_connection(Http2DownstreamConnection *dconn) { - dconns_.remove(dconn); - dconn->detach_stream_data(); -} - -void Http2Session::remove_stream_data(StreamData *sd) { - streams_.remove(sd); - if (sd->dconn) { - sd->dconn->detach_stream_data(); - } - delete sd; -} - -int Http2Session::submit_request(Http2DownstreamConnection *dconn, int32_t pri, - const nghttp2_nv *nva, size_t nvlen, - const nghttp2_data_provider *data_prd) { - assert(state_ == CONNECTED); - auto sd = make_unique(); - sd->dlnext = sd->dlprev = nullptr; - // TODO Specify nullptr to pri_spec for now - auto stream_id = - nghttp2_submit_request(session_, nullptr, nva, nvlen, data_prd, sd.get()); - if (stream_id < 0) { - SSLOG(FATAL, this) << "nghttp2_submit_request() failed: " - << nghttp2_strerror(stream_id); - return -1; - } - - dconn->attach_stream_data(sd.get()); - dconn->get_downstream()->set_downstream_stream_id(stream_id); - streams_.append(sd.release()); - - return 0; -} - -int Http2Session::submit_rst_stream(int32_t stream_id, uint32_t error_code) { - assert(state_ == CONNECTED); - if (LOG_ENABLED(INFO)) { - SSLOG(INFO, this) << "RST_STREAM stream_id=" << stream_id - << " with error_code=" << error_code; - } - int rv = nghttp2_submit_rst_stream(session_, NGHTTP2_FLAG_NONE, stream_id, - error_code); - if (rv != 0) { - SSLOG(FATAL, this) << "nghttp2_submit_rst_stream() failed: " - << nghttp2_strerror(rv); - return -1; - } - return 0; -} - -int Http2Session::submit_priority(Http2DownstreamConnection *dconn, - int32_t pri) { - assert(state_ == CONNECTED); - if (!dconn) { - return 0; - } - int rv; - - // TODO Disabled temporarily - - // rv = nghttp2_submit_priority(session_, NGHTTP2_FLAG_NONE, - // dconn->get_downstream()-> - // get_downstream_stream_id(), pri); - - rv = 0; - - if (rv < NGHTTP2_ERR_FATAL) { - SSLOG(FATAL, this) << "nghttp2_submit_priority() failed: " - << nghttp2_strerror(rv); - return -1; - } - return 0; -} - -nghttp2_session *Http2Session::get_session() const { return session_; } - -bool Http2Session::get_flow_control() const { return flow_control_; } - -int Http2Session::resume_data(Http2DownstreamConnection *dconn) { - assert(state_ == CONNECTED); - auto downstream = dconn->get_downstream(); - int rv = nghttp2_session_resume_data(session_, - downstream->get_downstream_stream_id()); - switch (rv) { - case 0: - case NGHTTP2_ERR_INVALID_ARGUMENT: - return 0; - default: - SSLOG(FATAL, this) << "nghttp2_resume_session() failed: " - << nghttp2_strerror(rv); - return -1; - } -} - -namespace { -void call_downstream_readcb(Http2Session *http2session, - Downstream *downstream) { - auto upstream = downstream->get_upstream(); - if (!upstream) { - return; - } - if (upstream->downstream_read(downstream->get_downstream_connection()) != 0) { - delete upstream->get_client_handler(); - } -} -} // namespace - -namespace { -int on_stream_close_callback(nghttp2_session *session, int32_t stream_id, - uint32_t error_code, void *user_data) { - auto http2session = static_cast(user_data); - if (LOG_ENABLED(INFO)) { - SSLOG(INFO, http2session) << "Stream stream_id=" << stream_id - << " is being closed with error code " - << error_code; - } - auto sd = static_cast( - nghttp2_session_get_stream_user_data(session, stream_id)); - if (sd == 0) { - // We might get this close callback when pushed streams are - // closed. - return 0; - } - auto dconn = sd->dconn; - if (dconn) { - auto downstream = dconn->get_downstream(); - if (downstream && downstream->get_downstream_stream_id() == stream_id) { - - if (downstream->get_upgraded() && - downstream->get_response_state() == Downstream::HEADER_COMPLETE) { - // For tunneled connection, we have to submit RST_STREAM to - // upstream *after* whole response body is sent. We just set - // MSG_COMPLETE here. Upstream will take care of that. - downstream->get_upstream()->on_downstream_body_complete(downstream); - downstream->set_response_state(Downstream::MSG_COMPLETE); - } else if (error_code == NGHTTP2_NO_ERROR) { - switch (downstream->get_response_state()) { - case Downstream::MSG_COMPLETE: - case Downstream::MSG_BAD_HEADER: - break; - default: - downstream->set_response_state(Downstream::MSG_RESET); - } - } else if (downstream->get_response_state() != - Downstream::MSG_BAD_HEADER) { - downstream->set_response_state(Downstream::MSG_RESET); - } - if (downstream->get_response_state() == Downstream::MSG_RESET && - downstream->get_response_rst_stream_error_code() == - NGHTTP2_NO_ERROR) { - downstream->set_response_rst_stream_error_code(error_code); - } - call_downstream_readcb(http2session, downstream); - // dconn may be deleted - } - } - // The life time of StreamData ends here - http2session->remove_stream_data(sd); - return 0; -} -} // namespace - -void Http2Session::start_settings_timer() { - ev_timer_again(conn_.loop, &settings_timer_); -} - -void Http2Session::stop_settings_timer() { - ev_timer_stop(conn_.loop, &settings_timer_); -} - -namespace { -int on_header_callback(nghttp2_session *session, const nghttp2_frame *frame, - const uint8_t *name, size_t namelen, - const uint8_t *value, size_t valuelen, uint8_t flags, - void *user_data) { - auto sd = static_cast( - nghttp2_session_get_stream_user_data(session, frame->hd.stream_id)); - if (!sd || !sd->dconn) { - return 0; - } - auto downstream = sd->dconn->get_downstream(); - if (!downstream) { - return 0; - } - - if (frame->hd.type != NGHTTP2_HEADERS) { - return 0; - } - - auto trailer = frame->headers.cat != NGHTTP2_HCAT_RESPONSE && - !downstream->get_expect_final_response(); - - if (downstream->get_response_headers_sum() + namelen + valuelen > - get_config()->header_field_buffer || - downstream->get_response_headers().size() >= - get_config()->max_header_fields) { - if (LOG_ENABLED(INFO)) { - DLOG(INFO, downstream) - << "Too large or many header field size=" - << downstream->get_response_headers_sum() + namelen + valuelen - << ", num=" << downstream->get_response_headers().size() + 1; - } - - if (trailer) { - // we don't care trailer part exceeds header size limit; just - // discard it. - return 0; - } - - return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; - } - - if (trailer) { - // just store header fields for trailer part - downstream->add_response_trailer(name, namelen, value, valuelen, - flags & NGHTTP2_NV_FLAG_NO_INDEX, -1); - return 0; - } - - auto token = http2::lookup_token(name, namelen); - - downstream->add_response_header(name, namelen, value, valuelen, - flags & NGHTTP2_NV_FLAG_NO_INDEX, token); - return 0; -} -} // namespace - -namespace { -int on_begin_headers_callback(nghttp2_session *session, - const nghttp2_frame *frame, void *user_data) { - auto http2session = static_cast(user_data); - if (frame->hd.type != NGHTTP2_HEADERS || - frame->headers.cat != NGHTTP2_HCAT_RESPONSE) { - return 0; - } - auto sd = static_cast( - nghttp2_session_get_stream_user_data(session, frame->hd.stream_id)); - if (!sd || !sd->dconn) { - http2session->submit_rst_stream(frame->hd.stream_id, - NGHTTP2_INTERNAL_ERROR); - return 0; - } - auto downstream = sd->dconn->get_downstream(); - if (!downstream || - downstream->get_downstream_stream_id() != frame->hd.stream_id) { - http2session->submit_rst_stream(frame->hd.stream_id, - NGHTTP2_INTERNAL_ERROR); - return 0; - } - return 0; -} -} // namespace - -namespace { -int on_response_headers(Http2Session *http2session, Downstream *downstream, - nghttp2_session *session, const nghttp2_frame *frame) { - int rv; - - auto upstream = downstream->get_upstream(); - - auto &nva = downstream->get_response_headers(); - - downstream->set_expect_final_response(false); - - auto status = downstream->get_response_header(http2::HD__STATUS); - // libnghttp2 guarantees this exists and can be parsed - auto status_code = http2::parse_http_status_code(status->value); - - downstream->set_response_http_status(status_code); - downstream->set_response_major(2); - downstream->set_response_minor(0); - - if (LOG_ENABLED(INFO)) { - std::stringstream ss; - for (auto &nv : nva) { - ss << TTY_HTTP_HD << nv.name << TTY_RST << ": " << nv.value << "\n"; - } - SSLOG(INFO, http2session) - << "HTTP response headers. stream_id=" << frame->hd.stream_id << "\n" - << ss.str(); - } - - if (downstream->get_non_final_response()) { - - if (LOG_ENABLED(INFO)) { - SSLOG(INFO, http2session) << "This is non-final response."; - } - - downstream->set_expect_final_response(true); - rv = upstream->on_downstream_header_complete(downstream); - - // Now Dowstream's response headers are erased. - - if (rv != 0) { - http2session->submit_rst_stream(frame->hd.stream_id, - NGHTTP2_PROTOCOL_ERROR); - downstream->set_response_state(Downstream::MSG_RESET); - } - - return 0; - } - - downstream->set_response_state(Downstream::HEADER_COMPLETE); - downstream->check_upgrade_fulfilled(); - - if (downstream->get_upgraded()) { - downstream->set_response_connection_close(true); - // On upgrade sucess, both ends can send data - if (upstream->resume_read(SHRPX_NO_BUFFER, downstream, 0) != 0) { - // If resume_read fails, just drop connection. Not ideal. - delete upstream->get_client_handler(); - return -1; - } - downstream->set_request_state(Downstream::HEADER_COMPLETE); - if (LOG_ENABLED(INFO)) { - SSLOG(INFO, http2session) - << "HTTP upgrade success. stream_id=" << frame->hd.stream_id; - } - } else { - auto content_length = - downstream->get_response_header(http2::HD_CONTENT_LENGTH); - if (content_length) { - // libnghttp2 guarantees this can be parsed - auto len = util::parse_uint(content_length->value); - downstream->set_response_content_length(len); - } - - if (downstream->get_response_content_length() == -1 && - downstream->expect_response_body()) { - // Here we have response body but Content-Length is not known in - // advance. - if (downstream->get_request_major() <= 0 || - (downstream->get_request_major() == 1 && - downstream->get_request_minor() == 0)) { - // We simply close connection for pre-HTTP/1.1 in this case. - downstream->set_response_connection_close(true); - } else { - // Otherwise, use chunked encoding to keep upstream connection - // open. In HTTP2, we are supporsed not to receive - // transfer-encoding. - downstream->add_response_header("transfer-encoding", "chunked", - http2::HD_TRANSFER_ENCODING); - downstream->set_chunked_response(true); - } - } - } - - rv = upstream->on_downstream_header_complete(downstream); - if (rv != 0) { - http2session->submit_rst_stream(frame->hd.stream_id, - NGHTTP2_PROTOCOL_ERROR); - downstream->set_response_state(Downstream::MSG_RESET); - } - - return 0; -} -} // namespace - -namespace { -int on_frame_recv_callback(nghttp2_session *session, const nghttp2_frame *frame, - void *user_data) { - int rv; - auto http2session = static_cast(user_data); - - switch (frame->hd.type) { - case NGHTTP2_DATA: { - auto sd = static_cast( - nghttp2_session_get_stream_user_data(session, frame->hd.stream_id)); - if (!sd || !sd->dconn) { - return 0; - } - auto downstream = sd->dconn->get_downstream(); - if (!downstream || - downstream->get_downstream_stream_id() != frame->hd.stream_id) { - return 0; - } - - auto upstream = downstream->get_upstream(); - rv = upstream->on_downstream_body(downstream, nullptr, 0, true); - if (rv != 0) { - http2session->submit_rst_stream(frame->hd.stream_id, - NGHTTP2_INTERNAL_ERROR); - downstream->set_response_state(Downstream::MSG_RESET); - - } else if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) { - - downstream->disable_downstream_rtimer(); - - if (downstream->get_response_state() == Downstream::HEADER_COMPLETE) { - - downstream->set_response_state(Downstream::MSG_COMPLETE); - - rv = upstream->on_downstream_body_complete(downstream); - - if (rv != 0) { - downstream->set_response_state(Downstream::MSG_RESET); - } - } - } - - call_downstream_readcb(http2session, downstream); - return 0; - } - case NGHTTP2_HEADERS: { - auto sd = static_cast( - nghttp2_session_get_stream_user_data(session, frame->hd.stream_id)); - if (!sd || !sd->dconn) { - return 0; - } - auto downstream = sd->dconn->get_downstream(); - - if (!downstream) { - return 0; - } - - if (frame->headers.cat == NGHTTP2_HCAT_RESPONSE) { - rv = on_response_headers(http2session, downstream, session, frame); - - if (rv != 0) { - return 0; - } - } else if (frame->headers.cat == NGHTTP2_HCAT_HEADERS) { - if (downstream->get_expect_final_response()) { - rv = on_response_headers(http2session, downstream, session, frame); - - if (rv != 0) { - return 0; - } - } - } - - if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) { - downstream->disable_downstream_rtimer(); - - if (downstream->get_response_state() == Downstream::HEADER_COMPLETE) { - downstream->set_response_state(Downstream::MSG_COMPLETE); - - auto upstream = downstream->get_upstream(); - - rv = upstream->on_downstream_body_complete(downstream); - - if (rv != 0) { - downstream->set_response_state(Downstream::MSG_RESET); - } - } - } else { - downstream->reset_downstream_rtimer(); - } - - // This may delete downstream - call_downstream_readcb(http2session, downstream); - - return 0; - } - case NGHTTP2_RST_STREAM: { - auto sd = static_cast( - nghttp2_session_get_stream_user_data(session, frame->hd.stream_id)); - if (sd && sd->dconn) { - auto downstream = sd->dconn->get_downstream(); - if (downstream && - downstream->get_downstream_stream_id() == frame->hd.stream_id) { - - downstream->set_response_rst_stream_error_code( - frame->rst_stream.error_code); - call_downstream_readcb(http2session, downstream); - } - } - return 0; - } - case NGHTTP2_SETTINGS: - if ((frame->hd.flags & NGHTTP2_FLAG_ACK) == 0) { - return 0; - } - http2session->stop_settings_timer(); - return 0; - case NGHTTP2_PING: - if (frame->hd.flags & NGHTTP2_FLAG_ACK) { - if (LOG_ENABLED(INFO)) { - LOG(INFO) << "PING ACK received"; - } - http2session->connection_alive(); - } - return 0; - case NGHTTP2_PUSH_PROMISE: - if (LOG_ENABLED(INFO)) { - SSLOG(INFO, http2session) - << "Received downstream PUSH_PROMISE stream_id=" - << frame->hd.stream_id - << ", promised_stream_id=" << frame->push_promise.promised_stream_id; - } - // We just respond with RST_STREAM. - http2session->submit_rst_stream(frame->push_promise.promised_stream_id, - NGHTTP2_REFUSED_STREAM); - return 0; - default: - return 0; - } -} -} // namespace - -namespace { -int on_data_chunk_recv_callback(nghttp2_session *session, uint8_t flags, - int32_t stream_id, const uint8_t *data, - size_t len, void *user_data) { - int rv; - auto http2session = static_cast(user_data); - auto sd = static_cast( - nghttp2_session_get_stream_user_data(session, stream_id)); - if (!sd || !sd->dconn) { - http2session->submit_rst_stream(stream_id, NGHTTP2_INTERNAL_ERROR); - - if (http2session->consume(stream_id, len) != 0) { - return NGHTTP2_ERR_CALLBACK_FAILURE; - } - - return 0; - } - auto downstream = sd->dconn->get_downstream(); - if (!downstream || downstream->get_downstream_stream_id() != stream_id || - !downstream->expect_response_body()) { - - http2session->submit_rst_stream(stream_id, NGHTTP2_INTERNAL_ERROR); - - if (http2session->consume(stream_id, len) != 0) { - return NGHTTP2_ERR_CALLBACK_FAILURE; - } - - return 0; - } - - // We don't want DATA after non-final response, which is illegal in - // HTTP. - if (downstream->get_non_final_response()) { - http2session->submit_rst_stream(stream_id, NGHTTP2_PROTOCOL_ERROR); - - if (http2session->consume(stream_id, len) != 0) { - return NGHTTP2_ERR_CALLBACK_FAILURE; - } - - return 0; - } - - downstream->reset_downstream_rtimer(); - - downstream->add_response_bodylen(len); - - auto upstream = downstream->get_upstream(); - rv = upstream->on_downstream_body(downstream, data, len, false); - if (rv != 0) { - http2session->submit_rst_stream(stream_id, NGHTTP2_INTERNAL_ERROR); - - if (http2session->consume(stream_id, len) != 0) { - return NGHTTP2_ERR_CALLBACK_FAILURE; - } - - downstream->set_response_state(Downstream::MSG_RESET); - } - - downstream->add_response_datalen(len); - - call_downstream_readcb(http2session, downstream); - return 0; -} -} // namespace - -namespace { -int on_frame_send_callback(nghttp2_session *session, const nghttp2_frame *frame, - void *user_data) { - auto http2session = static_cast(user_data); - - if (frame->hd.type == NGHTTP2_DATA || frame->hd.type == NGHTTP2_HEADERS) { - if ((frame->hd.flags & NGHTTP2_FLAG_END_STREAM) == 0) { - return 0; - } - - auto sd = static_cast( - nghttp2_session_get_stream_user_data(session, frame->hd.stream_id)); - - if (!sd || !sd->dconn) { - return 0; - } - - auto downstream = sd->dconn->get_downstream(); - - if (!downstream || - downstream->get_downstream_stream_id() != frame->hd.stream_id) { - return 0; - } - - downstream->reset_downstream_rtimer(); - - return 0; - } - - if (frame->hd.type == NGHTTP2_SETTINGS && - (frame->hd.flags & NGHTTP2_FLAG_ACK) == 0) { - http2session->start_settings_timer(); - } - return 0; -} -} // namespace - -namespace { -int on_frame_not_send_callback(nghttp2_session *session, - const nghttp2_frame *frame, int lib_error_code, - void *user_data) { - auto http2session = static_cast(user_data); - if (LOG_ENABLED(INFO)) { - SSLOG(INFO, http2session) << "Failed to send control frame type=" - << static_cast(frame->hd.type) - << "lib_error_code=" << lib_error_code << ":" - << nghttp2_strerror(lib_error_code); - } - if (frame->hd.type == NGHTTP2_HEADERS && - lib_error_code != NGHTTP2_ERR_STREAM_CLOSED && - lib_error_code != NGHTTP2_ERR_STREAM_CLOSING) { - // To avoid stream hanging around, flag Downstream::MSG_RESET. - auto sd = static_cast( - nghttp2_session_get_stream_user_data(session, frame->hd.stream_id)); - if (!sd) { - return 0; - } - if (!sd->dconn) { - return 0; - } - auto downstream = sd->dconn->get_downstream(); - if (!downstream || - downstream->get_downstream_stream_id() != frame->hd.stream_id) { - return 0; - } - downstream->set_response_state(Downstream::MSG_RESET); - call_downstream_readcb(http2session, downstream); - } - return 0; -} -} // namespace - -nghttp2_session_callbacks *create_http2_downstream_callbacks() { - int rv; - nghttp2_session_callbacks *callbacks; - - rv = nghttp2_session_callbacks_new(&callbacks); - - if (rv != 0) { - return nullptr; - } - - nghttp2_session_callbacks_set_on_stream_close_callback( - callbacks, on_stream_close_callback); - - nghttp2_session_callbacks_set_on_frame_recv_callback(callbacks, - on_frame_recv_callback); - - nghttp2_session_callbacks_set_on_data_chunk_recv_callback( - callbacks, on_data_chunk_recv_callback); - - nghttp2_session_callbacks_set_on_frame_send_callback(callbacks, - on_frame_send_callback); - - nghttp2_session_callbacks_set_on_frame_not_send_callback( - callbacks, on_frame_not_send_callback); - - nghttp2_session_callbacks_set_on_header_callback(callbacks, - on_header_callback); - - nghttp2_session_callbacks_set_on_begin_headers_callback( - callbacks, on_begin_headers_callback); - - if (get_config()->padding) { - nghttp2_session_callbacks_set_select_padding_callback( - callbacks, http::select_padding_callback); - } - - return callbacks; -} - -int Http2Session::connection_made() { - int rv; - - state_ = Http2Session::CONNECTED; - - if (ssl_ctx_) { - const unsigned char *next_proto = nullptr; - unsigned int next_proto_len; - SSL_get0_next_proto_negotiated(conn_.tls.ssl, &next_proto, &next_proto_len); - for (int i = 0; i < 2; ++i) { - if (next_proto) { - if (LOG_ENABLED(INFO)) { - std::string proto(next_proto, next_proto + next_proto_len); - SSLOG(INFO, this) << "Negotiated next protocol: " << proto; - } - if (!util::check_h2_is_selected(next_proto, next_proto_len)) { - return -1; - } - break; - } -#if OPENSSL_VERSION_NUMBER >= 0x10002000L - SSL_get0_alpn_selected(conn_.tls.ssl, &next_proto, &next_proto_len); -#else // OPENSSL_VERSION_NUMBER < 0x10002000L - break; -#endif // OPENSSL_VERSION_NUMBER < 0x10002000L - } - if (!next_proto) { - return -1; - } - } - - rv = nghttp2_session_client_new2(&session_, - get_config()->http2_downstream_callbacks, - this, get_config()->http2_client_option); - - if (rv != 0) { - return -1; - } - - flow_control_ = true; - - std::array entry; - entry[0].settings_id = NGHTTP2_SETTINGS_ENABLE_PUSH; - entry[0].value = 0; - entry[1].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS; - entry[1].value = get_config()->http2_max_concurrent_streams; - - entry[2].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE; - entry[2].value = (1 << get_config()->http2_downstream_window_bits) - 1; - - rv = nghttp2_submit_settings(session_, NGHTTP2_FLAG_NONE, entry.data(), - entry.size()); - if (rv != 0) { - return -1; - } - - if (get_config()->http2_downstream_connection_window_bits > 16) { - int32_t delta = - (1 << get_config()->http2_downstream_connection_window_bits) - 1 - - NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE; - rv = nghttp2_submit_window_update(session_, NGHTTP2_FLAG_NONE, 0, delta); - if (rv != 0) { - return -1; - } - } - - auto must_terminate = !get_config()->downstream_no_tls && - !ssl::check_http2_requirement(conn_.tls.ssl); - - if (must_terminate) { - rv = terminate_session(NGHTTP2_INADEQUATE_SECURITY); - - if (rv != 0) { - return -1; - } - } - - if (must_terminate) { - return 0; - } - - reset_connection_check_timer(CONNCHK_TIMEOUT); - - submit_pending_requests(); - - signal_write(); - return 0; -} - -int Http2Session::do_read() { return read_(*this); } -int Http2Session::do_write() { return write_(*this); } - -int Http2Session::on_read() { return on_read_(*this); } -int Http2Session::on_write() { return on_write_(*this); } - -int Http2Session::downstream_read() { - ssize_t rv = 0; - - if (rb_.rleft() > 0) { - rv = nghttp2_session_mem_recv( - session_, reinterpret_cast(rb_.pos), rb_.rleft()); - - if (rv < 0) { - SSLOG(ERROR, this) << "nghttp2_session_recv() returned error: " - << nghttp2_strerror(rv); - return -1; - } - - // nghttp2_session_mem_recv() should consume all input data in - // case of success. - rb_.reset(); - } - - if (nghttp2_session_want_read(session_) == 0 && - nghttp2_session_want_write(session_) == 0 && wb_.rleft() == 0) { - if (LOG_ENABLED(INFO)) { - SSLOG(INFO, this) << "No more read/write for this HTTP2 session"; - } - return -1; - } - - signal_write(); - return 0; -} - -int Http2Session::downstream_write() { - if (data_pending_) { - auto n = std::min(wb_.wleft(), data_pendinglen_); - wb_.write(data_pending_, n); - if (n < data_pendinglen_) { - data_pending_ += n; - data_pendinglen_ -= n; - return 0; - } - - data_pending_ = nullptr; - data_pendinglen_ = 0; - } - - for (;;) { - const uint8_t *data; - auto datalen = nghttp2_session_mem_send(session_, &data); - - if (datalen < 0) { - SSLOG(ERROR, this) << "nghttp2_session_mem_send() returned error: " - << nghttp2_strerror(datalen); - return -1; - } - if (datalen == 0) { - break; - } - auto n = wb_.write(data, datalen); - if (n < static_cast(datalen)) { - data_pending_ = data + n; - data_pendinglen_ = datalen - n; - return 0; - } - } - - if (nghttp2_session_want_read(session_) == 0 && - nghttp2_session_want_write(session_) == 0 && wb_.rleft() == 0) { - if (LOG_ENABLED(INFO)) { - SSLOG(INFO, this) << "No more read/write for this session"; - } - return -1; - } - - return 0; -} - -void Http2Session::signal_write() { - switch (state_) { - case Http2Session::DISCONNECTED: - if (LOG_ENABLED(INFO)) { - LOG(INFO) << "Start connecting to backend server"; - } - if (initiate_connection() != 0) { - if (LOG_ENABLED(INFO)) { - SSLOG(INFO, this) << "Could not initiate backend connection"; - } - disconnect(true); - } - break; - case Http2Session::CONNECTED: - conn_.wlimit.startw(); - break; - } -} - -struct ev_loop *Http2Session::get_loop() const { - return conn_.loop; -} - -ev_io *Http2Session::get_wev() { return &conn_.wev; } - -int Http2Session::get_state() const { return state_; } - -void Http2Session::set_state(int state) { state_ = state; } - -int Http2Session::terminate_session(uint32_t error_code) { - int rv; - rv = nghttp2_session_terminate_session(session_, error_code); - if (rv != 0) { - return -1; - } - return 0; -} - -SSL *Http2Session::get_ssl() const { return conn_.tls.ssl; } - -int Http2Session::consume(int32_t stream_id, size_t len) { - int rv; - - if (!session_) { - return 0; - } - - rv = nghttp2_session_consume(session_, stream_id, len); - - if (rv != 0) { - SSLOG(WARN, this) << "nghttp2_session_consume() returned error: " - << nghttp2_strerror(rv); - - return -1; - } - - return 0; -} - -bool Http2Session::can_push_request() const { - return state_ == CONNECTED && - connection_check_state_ == CONNECTION_CHECK_NONE; -} - -void Http2Session::start_checking_connection() { - if (state_ != CONNECTED || - connection_check_state_ != CONNECTION_CHECK_REQUIRED) { - return; - } - connection_check_state_ = CONNECTION_CHECK_STARTED; - - SSLOG(INFO, this) << "Start checking connection"; - // If connection is down, we may get error when writing data. Issue - // ping frame to see whether connection is alive. - nghttp2_submit_ping(session_, NGHTTP2_FLAG_NONE, NULL); - - // set ping timeout and start timer again - reset_connection_check_timer(CONNCHK_PING_TIMEOUT); - - signal_write(); -} - -void Http2Session::reset_connection_check_timer(ev_tstamp t) { - connchk_timer_.repeat = t; - ev_timer_again(conn_.loop, &connchk_timer_); -} - -void Http2Session::reset_connection_check_timer_if_not_checking() { - if (connection_check_state_ != CONNECTION_CHECK_NONE) { - return; - } - - reset_connection_check_timer(CONNCHK_TIMEOUT); -} - -void Http2Session::connection_alive() { - reset_connection_check_timer(CONNCHK_TIMEOUT); - - if (connection_check_state_ == CONNECTION_CHECK_NONE) { - return; - } - - if (LOG_ENABLED(INFO)) { - SSLOG(INFO, this) << "Connection alive"; - } - - connection_check_state_ = CONNECTION_CHECK_NONE; - - submit_pending_requests(); -} - -void Http2Session::submit_pending_requests() { - for (auto dconn = dconns_.head; dconn; dconn = dconn->dlnext) { - auto downstream = dconn->get_downstream(); - - if (!downstream || !downstream->request_submission_ready()) { - continue; - } - - auto upstream = downstream->get_upstream(); - - if (dconn->push_request_headers() != 0) { - if (LOG_ENABLED(INFO)) { - SSLOG(INFO, this) << "backend request failed"; - } - - upstream->on_downstream_abort_request(downstream, 400); - - continue; - } - - upstream->resume_read(SHRPX_NO_BUFFER, downstream, 0); - } -} - -void Http2Session::set_connection_check_state(int state) { - connection_check_state_ = state; -} - -int Http2Session::get_connection_check_state() const { - return connection_check_state_; -} - -int Http2Session::noop() { return 0; } - -int Http2Session::connected() { - if (!util::check_socket_connected(conn_.fd)) { - return -1; - } - - connect_blocker_->on_success(); - - if (LOG_ENABLED(INFO)) { - SSLOG(INFO, this) << "Connection established"; - } - - conn_.rlimit.startw(); - - if (conn_.tls.ssl) { - read_ = &Http2Session::tls_handshake; - write_ = &Http2Session::tls_handshake; - - return do_write(); - } - - read_ = &Http2Session::read_clear; - write_ = &Http2Session::write_clear; - - if (state_ == PROXY_CONNECTING) { - return do_write(); - } - - if (connection_made() != 0) { - state_ = CONNECT_FAILING; - return -1; - } - - return 0; -} - -int Http2Session::read_clear() { - ev_timer_again(conn_.loop, &conn_.rt); - - for (;;) { - // we should process buffered data first before we read EOF. - if (rb_.rleft() && on_read() != 0) { - return -1; - } - if (rb_.rleft()) { - return 0; - } - rb_.reset(); - - auto nread = conn_.read_clear(rb_.last, rb_.wleft()); - - if (nread == 0) { - return 0; - } - - if (nread < 0) { - return nread; - } - - rb_.write(nread); - } -} - -int Http2Session::write_clear() { - ev_timer_again(conn_.loop, &conn_.rt); - - for (;;) { - if (wb_.rleft() > 0) { - auto nwrite = conn_.write_clear(wb_.pos, wb_.rleft()); - - if (nwrite == 0) { - return 0; - } - - if (nwrite < 0) { - return nwrite; - } - - wb_.drain(nwrite); - continue; - } - - wb_.reset(); - if (on_write() != 0) { - return -1; - } - if (wb_.rleft() == 0) { - break; - } - } - - conn_.wlimit.stopw(); - ev_timer_stop(conn_.loop, &conn_.wt); - - return 0; -} - -int Http2Session::tls_handshake() { - ev_timer_again(conn_.loop, &conn_.rt); - - ERR_clear_error(); - - auto rv = conn_.tls_handshake(); - - if (rv == SHRPX_ERR_INPROGRESS) { - return 0; - } - - if (rv < 0) { - return rv; - } - - if (LOG_ENABLED(INFO)) { - SSLOG(INFO, this) << "SSL/TLS handshake completed"; - } - - if (!get_config()->downstream_no_tls && !get_config()->insecure && - check_cert() != 0) { - return -1; - } - - read_ = &Http2Session::read_tls; - write_ = &Http2Session::write_tls; - - if (connection_made() != 0) { - state_ = CONNECT_FAILING; - return -1; - } - - return 0; -} - -int Http2Session::read_tls() { - ev_timer_again(conn_.loop, &conn_.rt); - - ERR_clear_error(); - - for (;;) { - // we should process buffered data first before we read EOF. - if (rb_.rleft() && on_read() != 0) { - return -1; - } - if (rb_.rleft()) { - return 0; - } - rb_.reset(); - - auto nread = conn_.read_tls(rb_.last, rb_.wleft()); - - if (nread == 0) { - return 0; - } - - if (nread < 0) { - return nread; - } - - rb_.write(nread); - } -} - -int Http2Session::write_tls() { - ev_timer_again(conn_.loop, &conn_.rt); - - ERR_clear_error(); - - for (;;) { - if (wb_.rleft() > 0) { - auto nwrite = conn_.write_tls(wb_.pos, wb_.rleft()); - - if (nwrite == 0) { - return 0; - } - - if (nwrite < 0) { - return nwrite; - } - - wb_.drain(nwrite); - - continue; - } - wb_.reset(); - if (on_write() != 0) { - return -1; - } - if (wb_.rleft() == 0) { - break; - } - } - - conn_.wlimit.stopw(); - ev_timer_stop(conn_.loop, &conn_.wt); - - return 0; -} - -bool Http2Session::should_hard_fail() const { - switch (state_) { - case PROXY_CONNECTING: - case PROXY_FAILED: - case CONNECTING: - case CONNECT_FAILING: - return true; - default: - return false; - } -} - -size_t Http2Session::get_addr_idx() const { return addr_idx_; } - -} // namespace shrpx diff --git a/src/shrpx_http2_session.h b/src/shrpx_http2_session.h deleted file mode 100644 index 35d3376..0000000 --- a/src/shrpx_http2_session.h +++ /dev/null @@ -1,219 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2012 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#ifndef SHRPX_HTTP2_SESSION_H -#define SHRPX_HTTP2_SESSION_H - -#include "shrpx.h" - -#include -#include - -#include - -#include - -#include - -#include "http-parser/http_parser.h" - -#include "shrpx_connection.h" -#include "buffer.h" -#include "template.h" - -using namespace nghttp2; - -namespace shrpx { - -class Http2DownstreamConnection; -class Worker; -class ConnectBlocker; - -struct StreamData { - StreamData *dlnext, *dlprev; - Http2DownstreamConnection *dconn; -}; - -class Http2Session { -public: - Http2Session(struct ev_loop *loop, SSL_CTX *ssl_ctx, - ConnectBlocker *connect_blocker, Worker *worker); - ~Http2Session(); - - int check_cert(); - - // If hard is true, all pending requests are abandoned and - // associated ClientHandlers will be deleted. - int disconnect(bool hard = false); - int initiate_connection(); - - void add_downstream_connection(Http2DownstreamConnection *dconn); - void remove_downstream_connection(Http2DownstreamConnection *dconn); - - void remove_stream_data(StreamData *sd); - - int submit_request(Http2DownstreamConnection *dconn, int32_t pri, - const nghttp2_nv *nva, size_t nvlen, - const nghttp2_data_provider *data_prd); - - int submit_rst_stream(int32_t stream_id, uint32_t error_code); - - int submit_priority(Http2DownstreamConnection *dconn, int32_t pri); - - int terminate_session(uint32_t error_code); - - nghttp2_session *get_session() const; - - bool get_flow_control() const; - - int resume_data(Http2DownstreamConnection *dconn); - - int connection_made(); - - int do_read(); - int do_write(); - - int on_read(); - int on_write(); - - int connected(); - int read_clear(); - int write_clear(); - int tls_handshake(); - int read_tls(); - int write_tls(); - - int downstream_read_proxy(); - int downstream_connect_proxy(); - - int downstream_read(); - int downstream_write(); - - int noop(); - - void signal_write(); - - struct ev_loop *get_loop() const; - - ev_io *get_wev(); - - int get_state() const; - void set_state(int state); - - void start_settings_timer(); - void stop_settings_timer(); - - SSL *get_ssl() const; - - int consume(int32_t stream_id, size_t len); - - // Returns true if request can be issued on downstream connection. - bool can_push_request() const; - // Initiates the connection checking if downstream connection has - // been established and connection checking is required. - void start_checking_connection(); - // Resets connection check timer to timeout |t|. After timeout, we - // require connection checking. If connection checking is already - // enabled, this timeout is for PING ACK timeout. - void reset_connection_check_timer(ev_tstamp t); - void reset_connection_check_timer_if_not_checking(); - // Signals that connection is alive. Internally - // reset_connection_check_timer() is called. - void connection_alive(); - // Change connection check state. - void set_connection_check_state(int state); - int get_connection_check_state() const; - - bool should_hard_fail() const; - - void submit_pending_requests(); - - size_t get_addr_idx() const; - - enum { - // Disconnected - DISCONNECTED, - // Connecting proxy and making CONNECT request - PROXY_CONNECTING, - // Tunnel is established with proxy - PROXY_CONNECTED, - // Establishing tunnel is failed - PROXY_FAILED, - // Connecting to downstream and/or performing SSL/TLS handshake - CONNECTING, - // Connected to downstream - CONNECTED, - // Connection is started to fail - CONNECT_FAILING, - }; - - static const size_t OUTBUF_MAX_THRES = 64 * 1024; - - enum { - // Connection checking is not required - CONNECTION_CHECK_NONE, - // Connection checking is required - CONNECTION_CHECK_REQUIRED, - // Connection checking has been started - CONNECTION_CHECK_STARTED - }; - - using ReadBuf = Buffer<8192>; - using WriteBuf = Buffer<32768>; - -private: - Connection conn_; - ev_timer settings_timer_; - // This timer has 2 purpose: when it first timeout, set - // connection_check_state_ = CONNECTION_CHECK_REQUIRED. After - // connection check has started, this timer is started again and - // traps PING ACK timeout. - ev_timer connchk_timer_; - DList dconns_; - DList streams_; - std::function read_, write_; - std::function on_read_, on_write_; - // Used to parse the response from HTTP proxy - std::unique_ptr proxy_htp_; - Worker *worker_; - ConnectBlocker *connect_blocker_; - // NULL if no TLS is configured - SSL_CTX *ssl_ctx_; - nghttp2_session *session_; - const uint8_t *data_pending_; - size_t data_pendinglen_; - // index of get_config()->downstream_addrs this object uses - size_t addr_idx_; - int state_; - int connection_check_state_; - bool flow_control_; - WriteBuf wb_; - ReadBuf rb_; -}; - -nghttp2_session_callbacks *create_http2_downstream_callbacks(); - -} // namespace shrpx - -#endif // SHRPX_HTTP2_SESSION_H diff --git a/src/shrpx_http2_upstream.cc b/src/shrpx_http2_upstream.cc deleted file mode 100644 index e3aa67f..0000000 --- a/src/shrpx_http2_upstream.cc +++ /dev/null @@ -1,1661 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2012 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#include "shrpx_http2_upstream.h" - -#include -#include -#include -#include - -#include "shrpx_client_handler.h" -#include "shrpx_https_upstream.h" -#include "shrpx_downstream.h" -#include "shrpx_downstream_connection.h" -#include "shrpx_config.h" -#include "shrpx_http.h" -#include "shrpx_worker.h" -#include "http2.h" -#include "util.h" -#include "base64.h" -#include "app_helper.h" -#include "template.h" - -using namespace nghttp2; - -namespace shrpx { - -namespace { -int on_stream_close_callback(nghttp2_session *session, int32_t stream_id, - uint32_t error_code, void *user_data) { - auto upstream = static_cast(user_data); - if (LOG_ENABLED(INFO)) { - ULOG(INFO, upstream) << "Stream stream_id=" << stream_id - << " is being closed"; - } - - auto downstream = static_cast( - nghttp2_session_get_stream_user_data(session, stream_id)); - - if (!downstream) { - return 0; - } - - upstream->consume(stream_id, downstream->get_request_datalen()); - - downstream->reset_request_datalen(); - - if (downstream->get_request_state() == Downstream::CONNECT_FAIL) { - upstream->remove_downstream(downstream); - // downstream was deleted - - return 0; - } - - downstream->set_request_state(Downstream::STREAM_CLOSED); - - if (downstream->get_response_state() == Downstream::MSG_COMPLETE) { - // At this point, downstream response was read - if (!downstream->get_upgraded() && - !downstream->get_response_connection_close()) { - // Keep-alive - downstream->detach_downstream_connection(); - } - - upstream->remove_downstream(downstream); - // downstream was deleted - - return 0; - } - - // At this point, downstream read may be paused. - - // If shrpx_downstream::push_request_headers() failed, the - // error is handled here. - upstream->remove_downstream(downstream); - // downstream was deleted - - // How to test this case? Request sufficient large download - // and make client send RST_STREAM after it gets first DATA - // frame chunk. - - return 0; -} -} // namespace - -int Http2Upstream::upgrade_upstream(HttpsUpstream *http) { - int rv; - - // to deduce :scheme and :authority, first we have to parse request - // URI. - http_parser_url u = {}; - auto &url = http->get_downstream()->get_request_path(); - - std::string scheme, authority; - rv = http_parser_parse_url(url.c_str(), url.size(), 0, &u); - if (rv == 0) { - http2::copy_url_component(scheme, &u, UF_SCHEMA, url.c_str()); - http2::copy_url_component(authority, &u, UF_HOST, url.c_str()); - } - if (scheme.empty()) { - if (handler_->get_ssl()) { - scheme = "https"; - } else { - scheme = "http"; - } - } - if (!authority.empty()) { - if (authority.find(":") != std::string::npos) { - authority = "[" + authority; - authority += "]"; - } - if (u.field_set & (1 << UF_PORT)) { - authority += ":"; - authority += util::utos(u.port); - } - } - - auto http2_settings = http->get_downstream()->get_http2_settings(); - util::to_base64(http2_settings); - - auto settings_payload = - base64::decode(std::begin(http2_settings), std::end(http2_settings)); - - rv = nghttp2_session_upgrade( - session_, reinterpret_cast(settings_payload.c_str()), - settings_payload.size(), nullptr); - if (rv != 0) { - if (LOG_ENABLED(INFO)) { - ULOG(INFO, this) << "nghttp2_session_upgrade() returned error: " - << nghttp2_strerror(rv); - } - return -1; - } - pre_upstream_.reset(http); - auto downstream = http->pop_downstream(); - downstream->reset_upstream(this); - downstream->set_stream_id(1); - downstream->reset_upstream_rtimer(); - downstream->set_stream_id(1); - downstream->set_priority(0); - downstream->set_request_http2_authority(authority); - downstream->set_request_http2_scheme(scheme); - - auto ptr = downstream.get(); - - nghttp2_session_set_stream_user_data(session_, 1, ptr); - downstream_queue_.add_pending(std::move(downstream)); - downstream_queue_.mark_active(ptr); - - if (LOG_ENABLED(INFO)) { - ULOG(INFO, this) << "Connection upgraded to HTTP/2"; - } - - return 0; -} - -void Http2Upstream::start_settings_timer() { - ev_timer_start(handler_->get_loop(), &settings_timer_); -} - -void Http2Upstream::stop_settings_timer() { - ev_timer_stop(handler_->get_loop(), &settings_timer_); -} - -namespace { -int on_header_callback(nghttp2_session *session, const nghttp2_frame *frame, - const uint8_t *name, size_t namelen, - const uint8_t *value, size_t valuelen, uint8_t flags, - void *user_data) { - if (get_config()->upstream_frame_debug) { - verbose_on_header_callback(session, frame, name, namelen, value, valuelen, - flags, user_data); - } - if (frame->hd.type != NGHTTP2_HEADERS) { - return 0; - } - auto upstream = static_cast(user_data); - auto downstream = static_cast( - nghttp2_session_get_stream_user_data(session, frame->hd.stream_id)); - if (!downstream) { - return 0; - } - - if (downstream->get_request_headers_sum() + namelen + valuelen > - get_config()->header_field_buffer || - downstream->get_request_headers().size() >= - get_config()->max_header_fields) { - if (downstream->get_response_state() == Downstream::MSG_COMPLETE) { - return 0; - } - - if (LOG_ENABLED(INFO)) { - ULOG(INFO, upstream) << "Too large or many header field size=" - << downstream->get_request_headers_sum() + namelen + - valuelen << ", num=" - << downstream->get_request_headers().size() + 1; - } - - // just ignore header fields if this is trailer part. - if (frame->headers.cat == NGHTTP2_HCAT_HEADERS) { - return 0; - } - - if (upstream->error_reply(downstream, 431) != 0) { - return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; - } - - return 0; - } - - if (frame->headers.cat == NGHTTP2_HCAT_HEADERS) { - // just store header fields for trailer part - downstream->add_request_trailer(name, namelen, value, valuelen, - flags & NGHTTP2_NV_FLAG_NO_INDEX, -1); - return 0; - } - - auto token = http2::lookup_token(name, namelen); - - downstream->add_request_header(name, namelen, value, valuelen, - flags & NGHTTP2_NV_FLAG_NO_INDEX, token); - return 0; -} -} // namespace - -namespace { -int on_begin_headers_callback(nghttp2_session *session, - const nghttp2_frame *frame, void *user_data) { - auto upstream = static_cast(user_data); - - if (frame->headers.cat != NGHTTP2_HCAT_REQUEST) { - return 0; - } - if (LOG_ENABLED(INFO)) { - ULOG(INFO, upstream) << "Received upstream request HEADERS stream_id=" - << frame->hd.stream_id; - } - - auto handler = upstream->get_client_handler(); - - // TODO Use priority 0 for now - auto downstream = make_unique(upstream, handler->get_mcpool(), - frame->hd.stream_id, 0); - nghttp2_session_set_stream_user_data(session, frame->hd.stream_id, - downstream.get()); - - downstream->reset_upstream_rtimer(); - - // Although, we deprecated minor version from HTTP/2, we supply - // minor version 0 to use via header field in a conventional way. - downstream->set_request_major(2); - downstream->set_request_minor(0); - - upstream->add_pending_downstream(std::move(downstream)); - - return 0; -} -} // namespace - -int Http2Upstream::on_request_headers(Downstream *downstream, - const nghttp2_frame *frame) { - if (downstream->get_response_state() == Downstream::MSG_COMPLETE) { - return 0; - } - - auto &nva = downstream->get_request_headers(); - - if (LOG_ENABLED(INFO)) { - std::stringstream ss; - for (auto &nv : nva) { - ss << TTY_HTTP_HD << nv.name << TTY_RST << ": " << nv.value << "\n"; - } - ULOG(INFO, this) << "HTTP request headers. stream_id=" - << downstream->get_stream_id() << "\n" << ss.str(); - } - - if (get_config()->http2_upstream_dump_request_header) { - http2::dump_nv(get_config()->http2_upstream_dump_request_header, nva); - } - - auto content_length = - downstream->get_request_header(http2::HD_CONTENT_LENGTH); - if (content_length) { - // libnghttp2 guarantees this can be parsed - auto len = util::parse_uint(content_length->value); - downstream->set_request_content_length(len); - } - - auto authority = downstream->get_request_header(http2::HD__AUTHORITY); - auto path = downstream->get_request_header(http2::HD__PATH); - auto method = downstream->get_request_header(http2::HD__METHOD); - auto scheme = downstream->get_request_header(http2::HD__SCHEME); - - // presence of mandatory header fields are guaranteed by libnghttp2. - - // For HTTP/2 proxy, we request :authority. - if (method->value != "CONNECT" && get_config()->http2_proxy && !authority) { - rst_stream(downstream, NGHTTP2_PROTOCOL_ERROR); - return 0; - } - - downstream->set_request_method(http2::value_to_str(method)); - downstream->set_request_http2_scheme(http2::value_to_str(scheme)); - downstream->set_request_http2_authority(http2::value_to_str(authority)); - downstream->set_request_path(http2::value_to_str(path)); - - if (!(frame->hd.flags & NGHTTP2_FLAG_END_STREAM)) { - downstream->set_request_http2_expect_body(true); - } - - downstream->inspect_http2_request(); - - downstream->set_request_state(Downstream::HEADER_COMPLETE); - if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) { - downstream->disable_upstream_rtimer(); - - downstream->set_request_state(Downstream::MSG_COMPLETE); - } - - start_downstream(downstream); - - return 0; -} - -void Http2Upstream::start_downstream(Downstream *downstream) { - if (downstream_queue_.can_activate( - downstream->get_request_http2_authority())) { - initiate_downstream(downstream); - return; - } - - downstream_queue_.mark_blocked(downstream); -} - -void Http2Upstream::initiate_downstream(Downstream *downstream) { - int rv; - - rv = downstream->attach_downstream_connection( - handler_->get_downstream_connection()); - if (rv != 0) { - // downstream connection fails, send error page - if (error_reply(downstream, 503) != 0) { - rst_stream(downstream, NGHTTP2_INTERNAL_ERROR); - } - - downstream->set_request_state(Downstream::CONNECT_FAIL); - - downstream_queue_.mark_failure(downstream); - - return; - } - rv = downstream->push_request_headers(); - if (rv != 0) { - - if (error_reply(downstream, 503) != 0) { - rst_stream(downstream, NGHTTP2_INTERNAL_ERROR); - } - - downstream_queue_.mark_failure(downstream); - - return; - } - - downstream_queue_.mark_active(downstream); - - return; -} - -namespace { -int on_frame_recv_callback(nghttp2_session *session, const nghttp2_frame *frame, - void *user_data) { - if (get_config()->upstream_frame_debug) { - verbose_on_frame_recv_callback(session, frame, user_data); - } - auto upstream = static_cast(user_data); - - switch (frame->hd.type) { - case NGHTTP2_DATA: { - auto downstream = static_cast( - nghttp2_session_get_stream_user_data(session, frame->hd.stream_id)); - if (!downstream) { - return 0; - } - - if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) { - downstream->disable_upstream_rtimer(); - - downstream->end_upload_data(); - downstream->set_request_state(Downstream::MSG_COMPLETE); - } - - return 0; - } - case NGHTTP2_HEADERS: { - auto downstream = static_cast( - nghttp2_session_get_stream_user_data(session, frame->hd.stream_id)); - if (!downstream) { - return 0; - } - - if (frame->headers.cat == NGHTTP2_HCAT_REQUEST) { - downstream->reset_upstream_rtimer(); - - return upstream->on_request_headers(downstream, frame); - } - - if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) { - downstream->disable_upstream_rtimer(); - - downstream->end_upload_data(); - downstream->set_request_state(Downstream::MSG_COMPLETE); - } - - return 0; - } - case NGHTTP2_SETTINGS: - if ((frame->hd.flags & NGHTTP2_FLAG_ACK) == 0) { - return 0; - } - upstream->stop_settings_timer(); - return 0; - case NGHTTP2_GOAWAY: - if (LOG_ENABLED(INFO)) { - auto debug_data = util::ascii_dump(frame->goaway.opaque_data, - frame->goaway.opaque_data_len); - - ULOG(INFO, upstream) << "GOAWAY received: last-stream-id=" - << frame->goaway.last_stream_id - << ", error_code=" << frame->goaway.error_code - << ", debug_data=" << debug_data; - } - return 0; - default: - return 0; - } -} -} // namespace - -namespace { -int on_data_chunk_recv_callback(nghttp2_session *session, uint8_t flags, - int32_t stream_id, const uint8_t *data, - size_t len, void *user_data) { - auto upstream = static_cast(user_data); - auto downstream = static_cast( - nghttp2_session_get_stream_user_data(session, stream_id)); - - if (!downstream || !downstream->get_downstream_connection()) { - if (upstream->consume(stream_id, len) != 0) { - return NGHTTP2_ERR_CALLBACK_FAILURE; - } - - return 0; - } - - downstream->reset_upstream_rtimer(); - - if (downstream->push_upload_data_chunk(data, len) != 0) { - upstream->rst_stream(downstream, NGHTTP2_INTERNAL_ERROR); - - if (upstream->consume(stream_id, len) != 0) { - return NGHTTP2_ERR_CALLBACK_FAILURE; - } - - return 0; - } - - return 0; -} -} // namespace - -namespace { -int on_frame_send_callback(nghttp2_session *session, const nghttp2_frame *frame, - void *user_data) { - if (get_config()->upstream_frame_debug) { - verbose_on_frame_send_callback(session, frame, user_data); - } - auto upstream = static_cast(user_data); - auto handler = upstream->get_client_handler(); - - switch (frame->hd.type) { - case NGHTTP2_DATA: - case NGHTTP2_HEADERS: { - if ((frame->hd.flags & NGHTTP2_FLAG_END_STREAM) == 0) { - return 0; - } - // RST_STREAM if request is still incomplete. - auto stream_id = frame->hd.stream_id; - auto downstream = static_cast( - nghttp2_session_get_stream_user_data(session, stream_id)); - - if (!downstream) { - return 0; - } - - // For tunneling, issue RST_STREAM to finish the stream. - if (downstream->get_upgraded() || - nghttp2_session_get_stream_remote_close(session, stream_id) == 0) { - if (LOG_ENABLED(INFO)) { - ULOG(INFO, upstream) - << "Send RST_STREAM to " - << (downstream->get_upgraded() ? "tunneled " : "") - << "stream stream_id=" << downstream->get_stream_id() - << " to finish off incomplete request"; - } - - upstream->rst_stream(downstream, NGHTTP2_NO_ERROR); - } - - return 0; - } - case NGHTTP2_SETTINGS: - if ((frame->hd.flags & NGHTTP2_FLAG_ACK) == 0) { - upstream->start_settings_timer(); - } - return 0; - case NGHTTP2_PUSH_PROMISE: { - auto promised_stream_id = frame->push_promise.promised_stream_id; - auto downstream = make_unique(upstream, handler->get_mcpool(), - promised_stream_id, 0); - - nghttp2_session_set_stream_user_data(session, promised_stream_id, - downstream.get()); - - downstream->disable_upstream_rtimer(); - - downstream->set_request_major(2); - downstream->set_request_minor(0); - - for (size_t i = 0; i < frame->push_promise.nvlen; ++i) { - auto &nv = frame->push_promise.nva[i]; - auto token = http2::lookup_token(nv.name, nv.namelen); - switch (token) { - case http2::HD__METHOD: - downstream->set_request_method({nv.value, nv.value + nv.valuelen}); - break; - case http2::HD__SCHEME: - downstream->set_request_http2_scheme( - {nv.value, nv.value + nv.valuelen}); - break; - case http2::HD__AUTHORITY: - downstream->set_request_http2_authority( - {nv.value, nv.value + nv.valuelen}); - break; - case http2::HD__PATH: - downstream->set_request_path({nv.value, nv.value + nv.valuelen}); - break; - } - downstream->add_request_header(nv.name, nv.namelen, nv.value, nv.valuelen, - nv.flags & NGHTTP2_NV_FLAG_NO_INDEX, - token); - } - - downstream->inspect_http2_request(); - - downstream->set_request_state(Downstream::MSG_COMPLETE); - - // a bit weird but start_downstream() expects that given - // downstream is in pending queue. - auto ptr = downstream.get(); - upstream->add_pending_downstream(std::move(downstream)); - upstream->start_downstream(ptr); - - return 0; - } - case NGHTTP2_GOAWAY: - if (LOG_ENABLED(INFO)) { - auto debug_data = util::ascii_dump(frame->goaway.opaque_data, - frame->goaway.opaque_data_len); - - ULOG(INFO, upstream) << "Sending GOAWAY: last-stream-id=" - << frame->goaway.last_stream_id - << ", error_code=" << frame->goaway.error_code - << ", debug_data=" << debug_data; - } - return 0; - default: - return 0; - } -} -} // namespace - -namespace { -int on_frame_not_send_callback(nghttp2_session *session, - const nghttp2_frame *frame, int lib_error_code, - void *user_data) { - auto upstream = static_cast(user_data); - if (LOG_ENABLED(INFO)) { - ULOG(INFO, upstream) << "Failed to send control frame type=" - << static_cast(frame->hd.type) - << ", lib_error_code=" << lib_error_code << ":" - << nghttp2_strerror(lib_error_code); - } - if (frame->hd.type == NGHTTP2_HEADERS && - lib_error_code != NGHTTP2_ERR_STREAM_CLOSED && - lib_error_code != NGHTTP2_ERR_STREAM_CLOSING) { - // To avoid stream hanging around, issue RST_STREAM. - auto downstream = static_cast( - nghttp2_session_get_stream_user_data(session, frame->hd.stream_id)); - if (downstream) { - upstream->rst_stream(downstream, NGHTTP2_INTERNAL_ERROR); - } - } - return 0; -} -} // namespace - -namespace { -uint32_t infer_upstream_rst_stream_error_code(uint32_t downstream_error_code) { - // NGHTTP2_REFUSED_STREAM is important because it tells upstream - // client to retry. - switch (downstream_error_code) { - case NGHTTP2_NO_ERROR: - case NGHTTP2_REFUSED_STREAM: - return downstream_error_code; - default: - return NGHTTP2_INTERNAL_ERROR; - } -} -} // namespace - -namespace { -void settings_timeout_cb(struct ev_loop *loop, ev_timer *w, int revents) { - auto upstream = static_cast(w->data); - auto handler = upstream->get_client_handler(); - ULOG(INFO, upstream) << "SETTINGS timeout"; - if (upstream->terminate_session(NGHTTP2_SETTINGS_TIMEOUT) != 0) { - delete handler; - return; - } - handler->signal_write(); -} -} // namespace - -namespace { -void shutdown_timeout_cb(struct ev_loop *loop, ev_timer *w, int revents) { - auto upstream = static_cast(w->data); - auto handler = upstream->get_client_handler(); - upstream->submit_goaway(); - handler->signal_write(); -} -} // namespace - -namespace { -void prepare_cb(struct ev_loop *loop, ev_prepare *w, int revents) { - auto upstream = static_cast(w->data); - upstream->check_shutdown(); -} -} // namespace - -void Http2Upstream::submit_goaway() { - auto last_stream_id = nghttp2_session_get_last_proc_stream_id(session_); - nghttp2_submit_goaway(session_, NGHTTP2_FLAG_NONE, last_stream_id, - NGHTTP2_NO_ERROR, nullptr, 0); -} - -void Http2Upstream::check_shutdown() { - int rv; - if (shutdown_handled_) { - return; - } - - auto worker = handler_->get_worker(); - - if (worker->get_graceful_shutdown()) { - shutdown_handled_ = true; - rv = nghttp2_submit_shutdown_notice(session_); - if (rv != 0) { - ULOG(FATAL, this) << "nghttp2_submit_shutdown_notice() failed: " - << nghttp2_strerror(rv); - return; - } - handler_->signal_write(); - ev_timer_start(handler_->get_loop(), &shutdown_timer_); - } -} - -nghttp2_session_callbacks *create_http2_upstream_callbacks() { - int rv; - nghttp2_session_callbacks *callbacks; - - rv = nghttp2_session_callbacks_new(&callbacks); - - if (rv != 0) { - return nullptr; - } - - nghttp2_session_callbacks_set_on_stream_close_callback( - callbacks, on_stream_close_callback); - - nghttp2_session_callbacks_set_on_frame_recv_callback(callbacks, - on_frame_recv_callback); - - nghttp2_session_callbacks_set_on_data_chunk_recv_callback( - callbacks, on_data_chunk_recv_callback); - - nghttp2_session_callbacks_set_on_frame_send_callback(callbacks, - on_frame_send_callback); - - nghttp2_session_callbacks_set_on_frame_not_send_callback( - callbacks, on_frame_not_send_callback); - - nghttp2_session_callbacks_set_on_header_callback(callbacks, - on_header_callback); - - nghttp2_session_callbacks_set_on_begin_headers_callback( - callbacks, on_begin_headers_callback); - - if (get_config()->padding) { - nghttp2_session_callbacks_set_select_padding_callback( - callbacks, http::select_padding_callback); - } - - return callbacks; -} - -Http2Upstream::Http2Upstream(ClientHandler *handler) - : downstream_queue_( - get_config()->http2_proxy - ? get_config()->downstream_connections_per_host - : get_config()->downstream_proto == PROTO_HTTP - ? get_config()->downstream_connections_per_frontend - : 0, - !get_config()->http2_proxy), - handler_(handler), session_(nullptr), data_pending_(nullptr), - data_pendinglen_(0), shutdown_handled_(false) { - - int rv; - - rv = nghttp2_session_server_new2(&session_, - get_config()->http2_upstream_callbacks, this, - get_config()->http2_option); - - assert(rv == 0); - - flow_control_ = true; - - // TODO Maybe call from outside? - std::array entry; - entry[0].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS; - entry[0].value = get_config()->http2_max_concurrent_streams; - - entry[1].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE; - entry[1].value = (1 << get_config()->http2_upstream_window_bits) - 1; - - rv = nghttp2_submit_settings(session_, NGHTTP2_FLAG_NONE, entry.data(), - entry.size()); - if (rv != 0) { - ULOG(ERROR, this) << "nghttp2_submit_settings() returned error: " - << nghttp2_strerror(rv); - } - - if (get_config()->http2_upstream_connection_window_bits > 16) { - int32_t delta = (1 << get_config()->http2_upstream_connection_window_bits) - - 1 - NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE; - rv = nghttp2_submit_window_update(session_, NGHTTP2_FLAG_NONE, 0, delta); - - if (rv != 0) { - ULOG(ERROR, this) << "nghttp2_submit_window_update() returned error: " - << nghttp2_strerror(rv); - } - } - - // We wait for SETTINGS ACK at least 10 seconds. - ev_timer_init(&settings_timer_, settings_timeout_cb, 10., 0.); - - settings_timer_.data = this; - - // timer for 2nd GOAWAY. HTTP/2 spec recommend 1 RTT. We wait for - // 2 seconds. - ev_timer_init(&shutdown_timer_, shutdown_timeout_cb, 2., 0); - shutdown_timer_.data = this; - - ev_prepare_init(&prep_, prepare_cb); - prep_.data = this; - ev_prepare_start(handler_->get_loop(), &prep_); - - handler_->reset_upstream_read_timeout( - get_config()->http2_upstream_read_timeout); - - handler_->signal_write(); -} - -Http2Upstream::~Http2Upstream() { - nghttp2_session_del(session_); - ev_prepare_stop(handler_->get_loop(), &prep_); - ev_timer_stop(handler_->get_loop(), &shutdown_timer_); - ev_timer_stop(handler_->get_loop(), &settings_timer_); -} - -int Http2Upstream::on_read() { - ssize_t rv = 0; - auto rb = handler_->get_rb(); - auto rlimit = handler_->get_rlimit(); - - if (rb->rleft()) { - rv = nghttp2_session_mem_recv(session_, rb->pos, rb->rleft()); - if (rv < 0) { - if (rv != NGHTTP2_ERR_BAD_CLIENT_MAGIC) { - ULOG(ERROR, this) << "nghttp2_session_recv() returned error: " - << nghttp2_strerror(rv); - } - return -1; - } - - // nghttp2_session_mem_recv should consume all input bytes on - // success. - assert(static_cast(rv) == rb->rleft()); - rb->reset(); - rlimit->startw(); - } - - auto wb = handler_->get_wb(); - if (nghttp2_session_want_read(session_) == 0 && - nghttp2_session_want_write(session_) == 0 && wb->rleft() == 0) { - if (LOG_ENABLED(INFO)) { - ULOG(INFO, this) << "No more read/write for this HTTP2 session"; - } - return -1; - } - - handler_->signal_write(); - return 0; -} - -// After this function call, downstream may be deleted. -int Http2Upstream::on_write() { - auto wb = handler_->get_wb(); - - if (data_pending_) { - auto n = std::min(wb->wleft(), data_pendinglen_); - wb->write(data_pending_, n); - if (n < data_pendinglen_) { - data_pending_ += n; - data_pendinglen_ -= n; - return 0; - } - - data_pending_ = nullptr; - data_pendinglen_ = 0; - } - - for (;;) { - const uint8_t *data; - auto datalen = nghttp2_session_mem_send(session_, &data); - - if (datalen < 0) { - ULOG(ERROR, this) << "nghttp2_session_mem_send() returned error: " - << nghttp2_strerror(datalen); - return -1; - } - if (datalen == 0) { - break; - } - auto n = wb->write(data, datalen); - if (n < static_cast(datalen)) { - data_pending_ = data + n; - data_pendinglen_ = datalen - n; - return 0; - } - } - - if (nghttp2_session_want_read(session_) == 0 && - nghttp2_session_want_write(session_) == 0 && wb->rleft() == 0) { - if (LOG_ENABLED(INFO)) { - ULOG(INFO, this) << "No more read/write for this HTTP2 session"; - } - return -1; - } - - return 0; -} - -ClientHandler *Http2Upstream::get_client_handler() const { return handler_; } - -int Http2Upstream::downstream_read(DownstreamConnection *dconn) { - auto downstream = dconn->get_downstream(); - - if (downstream->get_request_state() == Downstream::STREAM_CLOSED) { - // If upstream HTTP2 stream was closed, we just close downstream, - // because there is no consumer now. Downstream connection is also - // closed in this case. - remove_downstream(downstream); - // downstream was deleted - - return 0; - } - - if (downstream->get_response_state() == Downstream::MSG_RESET) { - // The downstream stream was reset (canceled). In this case, - // RST_STREAM to the upstream and delete downstream connection - // here. Deleting downstream will be taken place at - // on_stream_close_callback. - rst_stream(downstream, - infer_upstream_rst_stream_error_code( - downstream->get_response_rst_stream_error_code())); - downstream->pop_downstream_connection(); - // dconn was deleted - dconn = nullptr; - } else if (downstream->get_response_state() == Downstream::MSG_BAD_HEADER) { - if (error_reply(downstream, 502) != 0) { - return -1; - } - downstream->pop_downstream_connection(); - // dconn was deleted - dconn = nullptr; - } else { - auto rv = downstream->on_read(); - if (rv == SHRPX_ERR_EOF) { - return downstream_eof(dconn); - } - if (rv != 0) { - if (rv != SHRPX_ERR_NETWORK) { - if (LOG_ENABLED(INFO)) { - DCLOG(INFO, dconn) << "HTTP parser failure"; - } - } - return downstream_error(dconn, Downstream::EVENT_ERROR); - } - // Detach downstream connection early so that it could be reused - // without hitting server's request timeout. - if (downstream->get_response_state() == Downstream::MSG_COMPLETE && - !downstream->get_response_connection_close()) { - // Keep-alive - downstream->detach_downstream_connection(); - } - } - - handler_->signal_write(); - - // At this point, downstream may be deleted. - - return 0; -} - -int Http2Upstream::downstream_write(DownstreamConnection *dconn) { - int rv; - rv = dconn->on_write(); - if (rv == SHRPX_ERR_NETWORK) { - return downstream_error(dconn, Downstream::EVENT_ERROR); - } - if (rv != 0) { - return -1; - } - return 0; -} - -int Http2Upstream::downstream_eof(DownstreamConnection *dconn) { - auto downstream = dconn->get_downstream(); - - if (LOG_ENABLED(INFO)) { - DCLOG(INFO, dconn) << "EOF. stream_id=" << downstream->get_stream_id(); - } - if (downstream->get_request_state() == Downstream::STREAM_CLOSED) { - // If stream was closed already, we don't need to send reply at - // the first place. We can delete downstream. - remove_downstream(downstream); - // downstream was deleted - - return 0; - } - - // Delete downstream connection. If we don't delete it here, it will - // be pooled in on_stream_close_callback. - downstream->pop_downstream_connection(); - // dconn was deleted - dconn = nullptr; - // downstream wil be deleted in on_stream_close_callback. - if (downstream->get_response_state() == Downstream::HEADER_COMPLETE) { - // Server may indicate the end of the request by EOF - if (LOG_ENABLED(INFO)) { - ULOG(INFO, this) << "Downstream body was ended by EOF"; - } - downstream->set_response_state(Downstream::MSG_COMPLETE); - - // For tunneled connection, MSG_COMPLETE signals - // downstream_data_read_callback to send RST_STREAM after pending - // response body is sent. This is needed to ensure that RST_STREAM - // is sent after all pending data are sent. - on_downstream_body_complete(downstream); - } else if (downstream->get_response_state() != Downstream::MSG_COMPLETE) { - // If stream was not closed, then we set MSG_COMPLETE and let - // on_stream_close_callback delete downstream. - if (error_reply(downstream, 502) != 0) { - return -1; - } - } - handler_->signal_write(); - // At this point, downstream may be deleted. - return 0; -} - -int Http2Upstream::downstream_error(DownstreamConnection *dconn, int events) { - auto downstream = dconn->get_downstream(); - - if (LOG_ENABLED(INFO)) { - if (events & Downstream::EVENT_ERROR) { - DCLOG(INFO, dconn) << "Downstream network/general error"; - } else { - DCLOG(INFO, dconn) << "Timeout"; - } - if (downstream->get_upgraded()) { - DCLOG(INFO, dconn) << "Note: this is tunnel connection"; - } - } - - if (downstream->get_request_state() == Downstream::STREAM_CLOSED) { - remove_downstream(downstream); - // downstream was deleted - - return 0; - } - - // Delete downstream connection. If we don't delete it here, it will - // be pooled in on_stream_close_callback. - downstream->pop_downstream_connection(); - // dconn was deleted - dconn = nullptr; - - if (downstream->get_response_state() == Downstream::MSG_COMPLETE) { - // For SSL tunneling, we issue RST_STREAM. For other types of - // stream, we don't have to do anything since response was - // complete. - if (downstream->get_upgraded()) { - rst_stream(downstream, NGHTTP2_NO_ERROR); - } - } else { - if (downstream->get_response_state() == Downstream::HEADER_COMPLETE) { - if (downstream->get_upgraded()) { - on_downstream_body_complete(downstream); - } else { - rst_stream(downstream, NGHTTP2_INTERNAL_ERROR); - } - } else { - unsigned int status; - if (events & Downstream::EVENT_TIMEOUT) { - status = 504; - } else { - status = 502; - } - if (error_reply(downstream, status) != 0) { - return -1; - } - } - downstream->set_response_state(Downstream::MSG_COMPLETE); - } - handler_->signal_write(); - // At this point, downstream may be deleted. - return 0; -} - -int Http2Upstream::rst_stream(Downstream *downstream, uint32_t error_code) { - if (LOG_ENABLED(INFO)) { - ULOG(INFO, this) << "RST_STREAM stream_id=" << downstream->get_stream_id() - << " with error_code=" << error_code; - } - int rv; - rv = nghttp2_submit_rst_stream(session_, NGHTTP2_FLAG_NONE, - downstream->get_stream_id(), error_code); - if (rv < NGHTTP2_ERR_FATAL) { - ULOG(FATAL, this) << "nghttp2_submit_rst_stream() failed: " - << nghttp2_strerror(rv); - DIE(); - } - return 0; -} - -int Http2Upstream::terminate_session(uint32_t error_code) { - int rv; - rv = nghttp2_session_terminate_session(session_, error_code); - if (rv != 0) { - return -1; - } - return 0; -} - -namespace { -ssize_t downstream_data_read_callback(nghttp2_session *session, - int32_t stream_id, uint8_t *buf, - size_t length, uint32_t *data_flags, - nghttp2_data_source *source, - void *user_data) { - int rv; - auto downstream = static_cast(source->ptr); - auto upstream = static_cast(downstream->get_upstream()); - auto body = downstream->get_response_buf(); - assert(body); - - auto dconn = downstream->get_downstream_connection(); - - if (body->rleft() == 0 && dconn && - downstream->get_response_state() != Downstream::MSG_COMPLETE) { - // Try to read more if buffer is empty. This will help small - // buffer and make priority handling a bit better. - if (upstream->downstream_read(dconn) != 0) { - return NGHTTP2_ERR_CALLBACK_FAILURE; - } - } - - auto nread = body->remove(buf, length); - auto body_empty = body->rleft() == 0; - - if (body_empty && - downstream->get_response_state() == Downstream::MSG_COMPLETE) { - - *data_flags |= NGHTTP2_DATA_FLAG_EOF; - - if (!downstream->get_upgraded()) { - auto &trailers = downstream->get_response_trailers(); - if (!trailers.empty()) { - std::vector nva; - nva.reserve(trailers.size()); - http2::copy_headers_to_nva(nva, trailers); - if (!nva.empty()) { - rv = nghttp2_submit_trailer(session, stream_id, nva.data(), - nva.size()); - if (rv != 0) { - if (nghttp2_is_fatal(rv)) { - return NGHTTP2_ERR_CALLBACK_FAILURE; - } - } else { - *data_flags |= NGHTTP2_DATA_FLAG_NO_END_STREAM; - } - } - } - } - } - - if (body_empty) { - downstream->disable_upstream_wtimer(); - } else { - downstream->reset_upstream_wtimer(); - } - - if (nread > 0 && downstream->resume_read(SHRPX_NO_BUFFER, nread) != 0) { - return NGHTTP2_ERR_CALLBACK_FAILURE; - } - - if (nread == 0 && ((*data_flags) & NGHTTP2_DATA_FLAG_EOF) == 0) { - return NGHTTP2_ERR_DEFERRED; - } - - if (nread > 0) { - downstream->add_response_sent_bodylen(nread); - } - - return nread; -} -} // namespace - -int Http2Upstream::error_reply(Downstream *downstream, - unsigned int status_code) { - int rv; - auto html = http::create_error_html(status_code); - downstream->set_response_http_status(status_code); - auto body = downstream->get_response_buf(); - body->append(html.c_str(), html.size()); - downstream->set_response_state(Downstream::MSG_COMPLETE); - - nghttp2_data_provider data_prd; - data_prd.source.ptr = downstream; - data_prd.read_callback = downstream_data_read_callback; - - auto content_length = util::utos(html.size()); - auto status_code_str = util::utos(status_code); - auto nva = - make_array(http2::make_nv_ls(":status", status_code_str), - http2::make_nv_ll("content-type", "text/html; charset=UTF-8"), - http2::make_nv_lc("server", get_config()->server_name), - http2::make_nv_ls("content-length", content_length)); - - rv = nghttp2_submit_response(session_, downstream->get_stream_id(), - nva.data(), nva.size(), &data_prd); - if (rv < NGHTTP2_ERR_FATAL) { - ULOG(FATAL, this) << "nghttp2_submit_response() failed: " - << nghttp2_strerror(rv); - return -1; - } - - return 0; -} - -void -Http2Upstream::add_pending_downstream(std::unique_ptr downstream) { - downstream_queue_.add_pending(std::move(downstream)); -} - -void Http2Upstream::remove_downstream(Downstream *downstream) { - if (downstream->accesslog_ready()) { - handler_->write_accesslog(downstream); - } - - nghttp2_session_set_stream_user_data(session_, downstream->get_stream_id(), - nullptr); - - auto next_downstream = downstream_queue_.remove_and_get_blocked(downstream); - - if (next_downstream) { - initiate_downstream(next_downstream); - } -} - -// WARNING: Never call directly or indirectly nghttp2_session_send or -// nghttp2_session_recv. These calls may delete downstream. -int Http2Upstream::on_downstream_header_complete(Downstream *downstream) { - int rv; - - if (LOG_ENABLED(INFO)) { - if (downstream->get_non_final_response()) { - DLOG(INFO, downstream) << "HTTP non-final response header"; - } else { - DLOG(INFO, downstream) << "HTTP response header completed"; - } - } - - if (!get_config()->http2_proxy && !get_config()->client_proxy && - !get_config()->no_location_rewrite) { - downstream->rewrite_location_response_header( - downstream->get_request_http2_scheme()); - } - - size_t nheader = downstream->get_response_headers().size(); - auto nva = std::vector(); - // 3 means :status and possible server and via header field. - nva.reserve(nheader + 3 + get_config()->add_response_headers.size()); - std::string via_value; - auto response_status = util::utos(downstream->get_response_http_status()); - nva.push_back(http2::make_nv_ls(":status", response_status)); - - http2::copy_headers_to_nva(nva, downstream->get_response_headers()); - - if (downstream->get_non_final_response()) { - if (LOG_ENABLED(INFO)) { - log_response_headers(downstream, nva); - } - - rv = nghttp2_submit_headers(session_, NGHTTP2_FLAG_NONE, - downstream->get_stream_id(), nullptr, - nva.data(), nva.size(), nullptr); - - downstream->clear_response_headers(); - - if (rv != 0) { - ULOG(FATAL, this) << "nghttp2_submit_headers() failed"; - return -1; - } - - return 0; - } - - if (!get_config()->http2_proxy && !get_config()->client_proxy) { - nva.push_back(http2::make_nv_lc("server", get_config()->server_name)); - } else { - auto server = downstream->get_response_header(http2::HD_SERVER); - if (server) { - nva.push_back(http2::make_nv_ls("server", (*server).value)); - } - } - - auto via = downstream->get_response_header(http2::HD_VIA); - if (get_config()->no_via) { - if (via) { - nva.push_back(http2::make_nv_ls("via", (*via).value)); - } - } else { - if (via) { - via_value = (*via).value; - via_value += ", "; - } - via_value += http::create_via_header_value( - downstream->get_response_major(), downstream->get_response_minor()); - nva.push_back(http2::make_nv_ls("via", via_value)); - } - - for (auto &p : get_config()->add_response_headers) { - nva.push_back(http2::make_nv(p.first, p.second)); - } - - if (LOG_ENABLED(INFO)) { - log_response_headers(downstream, nva); - } - - if (get_config()->http2_upstream_dump_response_header) { - http2::dump_nv(get_config()->http2_upstream_dump_response_header, - nva.data(), nva.size()); - } - - nghttp2_data_provider data_prd; - data_prd.source.ptr = downstream; - data_prd.read_callback = downstream_data_read_callback; - - nghttp2_data_provider *data_prdptr; - - if (downstream->expect_response_body()) { - data_prdptr = &data_prd; - } else { - data_prdptr = nullptr; - } - - rv = nghttp2_submit_response(session_, downstream->get_stream_id(), - nva.data(), nva.size(), data_prdptr); - if (rv != 0) { - ULOG(FATAL, this) << "nghttp2_submit_response() failed"; - return -1; - } - - // We need some conditions that must be fulfilled to initiate server - // push. - // - // * Server push is disabled for http2 proxy, since incoming headers - // are mixed origins. We don't know how to reliably determine the - // authority yet. - // - // * If downstream is http/2, it is likely that PUSH_PROMISE is - // coming from there, so we don't initiate PUSH_RPOMISE here. - // - // * We need 200 response code for associated resource. This is too - // restrictive, we will review this later. - // - // * We requires GET or POST for associated resource. Probably we - // don't want to push for HEAD request. Not sure other methods - // are also eligible for push. - if (!get_config()->no_server_push && - get_config()->downstream_proto == PROTO_HTTP && - !get_config()->http2_proxy && (downstream->get_stream_id() % 2) && - downstream->get_response_header(http2::HD_LINK) && - downstream->get_response_http_status() == 200 && - (downstream->get_request_method() == "GET" || - downstream->get_request_method() == "POST")) { - - if (prepare_push_promise(downstream) != 0) { - return -1; - } - } - - return 0; -} - -// WARNING: Never call directly or indirectly nghttp2_session_send or -// nghttp2_session_recv. These calls may delete downstream. -int Http2Upstream::on_downstream_body(Downstream *downstream, - const uint8_t *data, size_t len, - bool flush) { - auto body = downstream->get_response_buf(); - body->append(data, len); - - if (flush) { - nghttp2_session_resume_data(session_, downstream->get_stream_id()); - - downstream->ensure_upstream_wtimer(); - } - - return 0; -} - -// WARNING: Never call directly or indirectly nghttp2_session_send or -// nghttp2_session_recv. These calls may delete downstream. -int Http2Upstream::on_downstream_body_complete(Downstream *downstream) { - if (LOG_ENABLED(INFO)) { - DLOG(INFO, downstream) << "HTTP response completed"; - } - - if (!downstream->validate_response_bodylen()) { - rst_stream(downstream, NGHTTP2_PROTOCOL_ERROR); - downstream->set_response_connection_close(true); - return 0; - } - - nghttp2_session_resume_data(session_, downstream->get_stream_id()); - downstream->ensure_upstream_wtimer(); - - return 0; -} - -bool Http2Upstream::get_flow_control() const { return flow_control_; } - -void Http2Upstream::pause_read(IOCtrlReason reason) {} - -int Http2Upstream::resume_read(IOCtrlReason reason, Downstream *downstream, - size_t consumed) { - if (get_flow_control()) { - assert(downstream->get_request_datalen() >= consumed); - - if (consume(downstream->get_stream_id(), consumed) != 0) { - return -1; - } - - downstream->dec_request_datalen(consumed); - } - - handler_->signal_write(); - return 0; -} - -int Http2Upstream::on_downstream_abort_request(Downstream *downstream, - unsigned int status_code) { - int rv; - - rv = error_reply(downstream, status_code); - - if (rv != 0) { - return -1; - } - - handler_->signal_write(); - return 0; -} - -int Http2Upstream::consume(int32_t stream_id, size_t len) { - int rv; - - rv = nghttp2_session_consume(session_, stream_id, len); - - if (rv != 0) { - ULOG(WARN, this) << "nghttp2_session_consume() returned error: " - << nghttp2_strerror(rv); - return -1; - } - - return 0; -} - -void -Http2Upstream::log_response_headers(Downstream *downstream, - const std::vector &nva) const { - std::stringstream ss; - for (auto &nv : nva) { - ss << TTY_HTTP_HD << nv.name << TTY_RST << ": " << nv.value << "\n"; - } - ULOG(INFO, this) << "HTTP response headers. stream_id=" - << downstream->get_stream_id() << "\n" << ss.str(); -} - -int Http2Upstream::on_timeout(Downstream *downstream) { - if (LOG_ENABLED(INFO)) { - ULOG(INFO, this) << "Stream timeout stream_id=" - << downstream->get_stream_id(); - } - - rst_stream(downstream, NGHTTP2_NO_ERROR); - - return 0; -} - -void Http2Upstream::on_handler_delete() { - for (auto d = downstream_queue_.get_downstreams(); d; d = d->dlnext) { - if (d->get_dispatch_state() == Downstream::DISPATCH_ACTIVE && - d->accesslog_ready()) { - handler_->write_accesslog(d); - } - } -} - -int Http2Upstream::on_downstream_reset(bool no_retry) { - int rv; - - for (auto downstream = downstream_queue_.get_downstreams(); downstream; - downstream = downstream->dlnext) { - if (downstream->get_dispatch_state() != Downstream::DISPATCH_ACTIVE) { - continue; - } - - if (!downstream->request_submission_ready()) { - rst_stream(downstream, NGHTTP2_INTERNAL_ERROR); - downstream->pop_downstream_connection(); - continue; - } - - downstream->pop_downstream_connection(); - - downstream->add_retry(); - - if (no_retry || downstream->no_more_retry()) { - goto fail; - } - - // downstream connection is clean; we can retry with new - // downstream connection. - - rv = downstream->attach_downstream_connection( - handler_->get_downstream_connection()); - if (rv != 0) { - goto fail; - } - - continue; - - fail: - if (on_downstream_abort_request(downstream, 503) != 0) { - return -1; - } - downstream->pop_downstream_connection(); - } - - handler_->signal_write(); - - return 0; -} - -int Http2Upstream::prepare_push_promise(Downstream *downstream) { - int rv; - http_parser_url u; - memset(&u, 0, sizeof(u)); - rv = http_parser_parse_url(downstream->get_request_path().c_str(), - downstream->get_request_path().size(), 0, &u); - if (rv != 0) { - return 0; - } - const char *base; - size_t baselen; - if (u.field_set & (1 << UF_PATH)) { - auto &f = u.field_data[UF_PATH]; - base = downstream->get_request_path().c_str() + f.off; - baselen = f.len; - } else { - base = "/"; - baselen = 1; - } - for (auto &kv : downstream->get_response_headers()) { - if (kv.token != http2::HD_LINK) { - continue; - } - for (auto &link : - http2::parse_link_header(kv.value.c_str(), kv.value.size())) { - auto link_url = link.uri.first; - auto link_urllen = link.uri.second - link.uri.first; - - const char *rel; - size_t rellen; - const char *relq = nullptr; - size_t relqlen = 0; - - http_parser_url v; - memset(&v, 0, sizeof(v)); - rv = http_parser_parse_url(link_url, link_urllen, 0, &v); - if (rv != 0) { - assert(link_urllen); - if (link_url[0] == '/') { - continue; - } - // treat link_url as relative URI. - auto end = std::find(link_url, link_url + link_urllen, '#'); - auto q = std::find(link_url, end, '?'); - rel = link_url; - rellen = q - link_url; - if (q != end) { - relq = q + 1; - relqlen = end - relq; - } - } else { - if (v.field_set & (1 << UF_HOST)) { - continue; - } - if (v.field_set & (1 << UF_PATH)) { - auto &f = v.field_data[UF_PATH]; - rel = link_url + f.off; - rellen = f.len; - } else { - rel = "/"; - rellen = 1; - } - - if (v.field_set & (1 << UF_QUERY)) { - auto &f = v.field_data[UF_QUERY]; - relq = link_url + f.off; - relqlen = f.len; - } - } - auto path = http2::path_join(base, baselen, nullptr, 0, rel, rellen, relq, - relqlen); - rv = submit_push_promise(path, downstream); - if (rv != 0) { - return -1; - } - } - } - return 0; -} - -int Http2Upstream::submit_push_promise(const std::string &path, - Downstream *downstream) { - int rv; - std::vector nva; - nva.reserve(downstream->get_request_headers().size()); - - // juse use "GET" for now - nva.push_back(http2::make_nv_ll(":method", "GET")); - nva.push_back( - http2::make_nv_ls(":scheme", downstream->get_request_http2_scheme())); - nva.push_back(http2::make_nv_ls(":path", path)); - auto &authority = downstream->get_request_http2_authority(); - if (!authority.empty()) { - nva.push_back(http2::make_nv_ls(":authority", authority)); - } - - for (auto &kv : downstream->get_request_headers()) { - switch (kv.token) { - // TODO generate referer - case http2::HD__AUTHORITY: - case http2::HD__SCHEME: - case http2::HD__METHOD: - case http2::HD__PATH: - continue; - case http2::HD_ACCEPT_ENCODING: - case http2::HD_ACCEPT_LANGUAGE: - case http2::HD_CACHE_CONTROL: - case http2::HD_HOST: - case http2::HD_USER_AGENT: - nva.push_back(http2::make_nv(kv.name, kv.value, kv.no_index)); - break; - } - } - - rv = nghttp2_submit_push_promise(session_, NGHTTP2_FLAG_NONE, - downstream->get_stream_id(), nva.data(), - nva.size(), nullptr); - - if (rv < 0) { - if (LOG_ENABLED(INFO)) { - ULOG(INFO, this) << "nghttp2_submit_push_promise() failed: " - << nghttp2_strerror(rv); - } - if (nghttp2_is_fatal(rv)) { - return -1; - } - return 0; - } - - if (LOG_ENABLED(INFO)) { - std::stringstream ss; - for (auto &nv : nva) { - ss << TTY_HTTP_HD << nv.name << TTY_RST << ": " << nv.value << "\n"; - } - ULOG(INFO, this) << "HTTP push request headers. promised_stream_id=" << rv - << "\n" << ss.str(); - } - - return 0; -} - -} // namespace shrpx diff --git a/src/shrpx_http2_upstream.h b/src/shrpx_http2_upstream.h deleted file mode 100644 index ef8c0be..0000000 --- a/src/shrpx_http2_upstream.h +++ /dev/null @@ -1,121 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2012 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#ifndef SHRPX_HTTP2_UPSTREAM_H -#define SHRPX_HTTP2_UPSTREAM_H - -#include "shrpx.h" - -#include - -#include - -#include - -#include "shrpx_upstream.h" -#include "shrpx_downstream_queue.h" -#include "memchunk.h" - -using namespace nghttp2; - -namespace shrpx { - -class ClientHandler; -class HttpsUpstream; - -class Http2Upstream : public Upstream { -public: - Http2Upstream(ClientHandler *handler); - virtual ~Http2Upstream(); - virtual int on_read(); - virtual int on_write(); - virtual int on_timeout(Downstream *downstream); - virtual int on_downstream_abort_request(Downstream *downstream, - unsigned int status_code); - virtual ClientHandler *get_client_handler() const; - - virtual int downstream_read(DownstreamConnection *dconn); - virtual int downstream_write(DownstreamConnection *dconn); - virtual int downstream_eof(DownstreamConnection *dconn); - virtual int downstream_error(DownstreamConnection *dconn, int events); - - void add_pending_downstream(std::unique_ptr downstream); - void remove_downstream(Downstream *downstream); - - int rst_stream(Downstream *downstream, uint32_t error_code); - int terminate_session(uint32_t error_code); - int error_reply(Downstream *downstream, unsigned int status_code); - - virtual void pause_read(IOCtrlReason reason); - virtual int resume_read(IOCtrlReason reason, Downstream *downstream, - size_t consumed); - - virtual int on_downstream_header_complete(Downstream *downstream); - virtual int on_downstream_body(Downstream *downstream, const uint8_t *data, - size_t len, bool flush); - virtual int on_downstream_body_complete(Downstream *downstream); - - virtual void on_handler_delete(); - virtual int on_downstream_reset(bool no_retry); - - bool get_flow_control() const; - // Perform HTTP/2 upgrade from |upstream|. On success, this object - // takes ownership of the |upstream|. This function returns 0 if it - // succeeds, or -1. - int upgrade_upstream(HttpsUpstream *upstream); - void start_settings_timer(); - void stop_settings_timer(); - int consume(int32_t stream_id, size_t len); - void log_response_headers(Downstream *downstream, - const std::vector &nva) const; - void start_downstream(Downstream *downstream); - void initiate_downstream(Downstream *downstream); - - void submit_goaway(); - void check_shutdown(); - - int prepare_push_promise(Downstream *downstream); - int submit_push_promise(const std::string &path, Downstream *downstream); - - int on_request_headers(Downstream *downstream, const nghttp2_frame *frame); - -private: - std::unique_ptr pre_upstream_; - DownstreamQueue downstream_queue_; - ev_timer settings_timer_; - ev_timer shutdown_timer_; - ev_prepare prep_; - ClientHandler *handler_; - nghttp2_session *session_; - const uint8_t *data_pending_; - size_t data_pendinglen_; - bool flow_control_; - bool shutdown_handled_; -}; - -nghttp2_session_callbacks *create_http2_upstream_callbacks(); - -} // namespace shrpx - -#endif // SHRPX_HTTP2_UPSTREAM_H diff --git a/src/shrpx_http_downstream_connection.cc b/src/shrpx_http_downstream_connection.cc deleted file mode 100644 index 1840f75..0000000 --- a/src/shrpx_http_downstream_connection.cc +++ /dev/null @@ -1,844 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2012 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#include "shrpx_http_downstream_connection.h" - -#include "shrpx_client_handler.h" -#include "shrpx_upstream.h" -#include "shrpx_downstream.h" -#include "shrpx_config.h" -#include "shrpx_error.h" -#include "shrpx_http.h" -#include "shrpx_log_config.h" -#include "shrpx_connect_blocker.h" -#include "shrpx_downstream_connection_pool.h" -#include "shrpx_worker.h" -#include "http2.h" -#include "util.h" - -using namespace nghttp2; - -namespace shrpx { - -namespace { -void timeoutcb(struct ev_loop *loop, ev_timer *w, int revents) { - auto conn = static_cast(w->data); - auto dconn = static_cast(conn->data); - - if (LOG_ENABLED(INFO)) { - DCLOG(INFO, dconn) << "Time out"; - } - - auto downstream = dconn->get_downstream(); - auto upstream = downstream->get_upstream(); - auto handler = upstream->get_client_handler(); - - // Do this so that dconn is not pooled - downstream->set_response_connection_close(true); - - if (upstream->downstream_error(dconn, Downstream::EVENT_TIMEOUT) != 0) { - delete handler; - } -} -} // namespace - -namespace { -void readcb(struct ev_loop *loop, ev_io *w, int revents) { - auto conn = static_cast(w->data); - auto dconn = static_cast(conn->data); - auto downstream = dconn->get_downstream(); - auto upstream = downstream->get_upstream(); - auto handler = upstream->get_client_handler(); - - if (upstream->downstream_read(dconn) != 0) { - delete handler; - } -} -} // namespace - -namespace { -void writecb(struct ev_loop *loop, ev_io *w, int revents) { - auto conn = static_cast(w->data); - auto dconn = static_cast(conn->data); - auto downstream = dconn->get_downstream(); - auto upstream = downstream->get_upstream(); - auto handler = upstream->get_client_handler(); - - if (upstream->downstream_write(dconn) != 0) { - delete handler; - } -} -} // namespace - -namespace { -void connectcb(struct ev_loop *loop, ev_io *w, int revents) { - auto conn = static_cast(w->data); - auto dconn = static_cast(conn->data); - auto downstream = dconn->get_downstream(); - auto upstream = downstream->get_upstream(); - auto handler = upstream->get_client_handler(); - if (dconn->on_connect() != 0) { - if (upstream->on_downstream_abort_request(downstream, 503) != 0) { - delete handler; - } - return; - } - writecb(loop, w, revents); -} -} // namespace - -HttpDownstreamConnection::HttpDownstreamConnection( - DownstreamConnectionPool *dconn_pool, struct ev_loop *loop) - : DownstreamConnection(dconn_pool), - conn_(loop, -1, nullptr, get_config()->downstream_write_timeout, - get_config()->downstream_read_timeout, 0, 0, 0, 0, connectcb, - readcb, timeoutcb, this), - ioctrl_(&conn_.rlimit), response_htp_{0}, addr_idx_(0), - connected_(false) {} - -HttpDownstreamConnection::~HttpDownstreamConnection() { - // Downstream and DownstreamConnection may be deleted - // asynchronously. - if (downstream_) { - downstream_->release_downstream_connection(); - } -} - -int HttpDownstreamConnection::attach_downstream(Downstream *downstream) { - if (LOG_ENABLED(INFO)) { - DCLOG(INFO, this) << "Attaching to DOWNSTREAM:" << downstream; - } - - if (conn_.fd == -1) { - auto connect_blocker = client_handler_->get_connect_blocker(); - - if (connect_blocker->blocked()) { - if (LOG_ENABLED(INFO)) { - DCLOG(INFO, this) - << "Downstream connection was blocked by connect_blocker"; - } - return -1; - } - - auto worker = client_handler_->get_worker(); - auto worker_stat = worker->get_worker_stat(); - auto end = worker_stat->next_downstream; - for (;;) { - auto i = worker_stat->next_downstream; - ++worker_stat->next_downstream; - worker_stat->next_downstream %= get_config()->downstream_addrs.size(); - - conn_.fd = util::create_nonblock_socket( - get_config()->downstream_addrs[i].addr.storage.ss_family); - - if (conn_.fd == -1) { - auto error = errno; - DCLOG(WARN, this) << "socket() failed; errno=" << error; - - connect_blocker->on_failure(); - - return SHRPX_ERR_NETWORK; - } - - int rv; - rv = connect(conn_.fd, &get_config()->downstream_addrs[i].addr.sa, - get_config()->downstream_addrs[i].addrlen); - if (rv != 0 && errno != EINPROGRESS) { - auto error = errno; - DCLOG(WARN, this) << "connect() failed; errno=" << error; - - connect_blocker->on_failure(); - close(conn_.fd); - conn_.fd = -1; - - if (end == worker_stat->next_downstream) { - return SHRPX_ERR_NETWORK; - } - - // Try again with the next downstream server - continue; - } - - if (LOG_ENABLED(INFO)) { - DCLOG(INFO, this) << "Connecting to downstream server"; - } - - addr_idx_ = i; - - ev_io_set(&conn_.wev, conn_.fd, EV_WRITE); - ev_io_set(&conn_.rev, conn_.fd, EV_READ); - - conn_.wlimit.startw(); - - break; - } - } - - downstream_ = downstream; - - http_parser_init(&response_htp_, HTTP_RESPONSE); - response_htp_.data = downstream_; - - ev_set_cb(&conn_.rev, readcb); - - conn_.rt.repeat = get_config()->downstream_read_timeout; - ev_timer_again(conn_.loop, &conn_.rt); - // TODO we should have timeout for connection establishment - ev_timer_again(conn_.loop, &conn_.wt); - - return 0; -} - -int HttpDownstreamConnection::push_request_headers() { - const char *authority = nullptr, *host = nullptr; - auto downstream_hostport = - get_config()->downstream_addrs[addr_idx_].hostport.get(); - - if (!get_config()->no_host_rewrite && !get_config()->http2_proxy && - !get_config()->client_proxy && - downstream_->get_request_method() != "CONNECT") { - if (!downstream_->get_request_http2_authority().empty()) { - authority = downstream_hostport; - } - if (downstream_->get_request_header(http2::HD_HOST)) { - host = downstream_hostport; - } - } else { - if (!downstream_->get_request_http2_authority().empty()) { - authority = downstream_->get_request_http2_authority().c_str(); - } - auto h = downstream_->get_request_header(http2::HD_HOST); - if (h) { - host = h->value.c_str(); - } - } - - if (!authority && !host) { - // upstream is HTTP/1.0. We use backend server's host - // nonetheless. - host = downstream_hostport; - } - - if (authority) { - downstream_->set_request_downstream_host(authority); - } else { - downstream_->set_request_downstream_host(host); - } - - downstream_->assemble_request_cookie(); - - // Assume that method and request path do not contain \r\n. - std::string hdrs = downstream_->get_request_method(); - hdrs += ' '; - if (downstream_->get_request_method() == "CONNECT") { - if (authority) { - hdrs += authority; - } else { - hdrs += downstream_->get_request_path(); - } - } else if (get_config()->http2_proxy || get_config()->client_proxy) { - // Construct absolute-form request target because we are going to - // send a request to a HTTP/1 proxy. - if (downstream_->get_request_http2_scheme().empty()) { - // this comes from HTTP/1 upstream, use http scheme. We don't - // support https forward link yet. - hdrs += "http://"; - } else { - hdrs += downstream_->get_request_http2_scheme(); - hdrs += "://"; - } - if (authority) { - hdrs += authority; - } else { - hdrs += host; - } - - // Server-wide OPTIONS takes following form in proxy request: - // - // OPTIONS http://example.org HTTP/1.1 - // - // Notice that no slash after authority. See - // http://tools.ietf.org/html/rfc7230#section-5.3.4 - if (downstream_->get_request_path() != "*") { - hdrs += downstream_->get_request_path(); - } - } else { - // No proxy case. - hdrs += downstream_->get_request_path(); - } - hdrs += " HTTP/1.1\r\nHost: "; - if (authority) { - hdrs += authority; - } else { - hdrs += host; - } - hdrs += "\r\n"; - - http2::build_http1_headers_from_headers(hdrs, - downstream_->get_request_headers()); - - if (!downstream_->get_assembled_request_cookie().empty()) { - hdrs += "Cookie: "; - hdrs += downstream_->get_assembled_request_cookie(); - hdrs += "\r\n"; - } - - if (downstream_->get_request_method() != "CONNECT" && - downstream_->get_request_http2_expect_body() && - !downstream_->get_request_header(http2::HD_CONTENT_LENGTH)) { - - downstream_->set_chunked_request(true); - hdrs += "Transfer-Encoding: chunked\r\n"; - } - - if (downstream_->get_request_connection_close()) { - hdrs += "Connection: close\r\n"; - } - auto xff = downstream_->get_request_header(http2::HD_X_FORWARDED_FOR); - if (get_config()->add_x_forwarded_for) { - hdrs += "X-Forwarded-For: "; - if (xff && !get_config()->strip_incoming_x_forwarded_for) { - hdrs += (*xff).value; - hdrs += ", "; - } - hdrs += client_handler_->get_ipaddr(); - hdrs += "\r\n"; - } else if (xff && !get_config()->strip_incoming_x_forwarded_for) { - hdrs += "X-Forwarded-For: "; - hdrs += (*xff).value; - hdrs += "\r\n"; - } - if (!get_config()->http2_proxy && !get_config()->client_proxy && - downstream_->get_request_method() != "CONNECT") { - hdrs += "X-Forwarded-Proto: "; - if (!downstream_->get_request_http2_scheme().empty()) { - hdrs += downstream_->get_request_http2_scheme(); - hdrs += "\r\n"; - } else if (client_handler_->get_ssl()) { - hdrs += "https\r\n"; - } else { - hdrs += "http\r\n"; - } - } - auto expect = downstream_->get_request_header(http2::HD_EXPECT); - if (expect && !util::strifind((*expect).value.c_str(), "100-continue")) { - hdrs += "Expect: "; - hdrs += (*expect).value; - hdrs += "\r\n"; - } - auto via = downstream_->get_request_header(http2::HD_VIA); - if (get_config()->no_via) { - if (via) { - hdrs += "Via: "; - hdrs += (*via).value; - hdrs += "\r\n"; - } - } else { - hdrs += "Via: "; - if (via) { - hdrs += (*via).value; - hdrs += ", "; - } - hdrs += http::create_via_header_value(downstream_->get_request_major(), - downstream_->get_request_minor()); - hdrs += "\r\n"; - } - - hdrs += "\r\n"; - if (LOG_ENABLED(INFO)) { - const char *hdrp; - std::string nhdrs; - if (log_config()->errorlog_tty) { - nhdrs = http::colorizeHeaders(hdrs.c_str()); - hdrp = nhdrs.c_str(); - } else { - hdrp = hdrs.c_str(); - } - DCLOG(INFO, this) << "HTTP request headers. stream_id=" - << downstream_->get_stream_id() << "\n" << hdrp; - } - auto output = downstream_->get_request_buf(); - output->append(hdrs.c_str(), hdrs.size()); - - signal_write(); - - return 0; -} - -int HttpDownstreamConnection::push_upload_data_chunk(const uint8_t *data, - size_t datalen) { - auto chunked = downstream_->get_chunked_request(); - auto output = downstream_->get_request_buf(); - - if (chunked) { - auto chunk_size_hex = util::utox(datalen); - output->append(chunk_size_hex.c_str(), chunk_size_hex.size()); - output->append("\r\n"); - } - - output->append(data, datalen); - - if (chunked) { - output->append("\r\n"); - } - - signal_write(); - - return 0; -} - -int HttpDownstreamConnection::end_upload_data() { - if (!downstream_->get_chunked_request()) { - return 0; - } - - auto output = downstream_->get_request_buf(); - auto &trailers = downstream_->get_request_trailers(); - if (trailers.empty()) { - output->append("0\r\n\r\n"); - } else { - output->append("0\r\n"); - std::string trailer_part; - http2::build_http1_headers_from_headers(trailer_part, trailers); - output->append(trailer_part.c_str(), trailer_part.size()); - output->append("\r\n"); - } - - signal_write(); - - return 0; -} - -namespace { -void idle_readcb(struct ev_loop *loop, ev_io *w, int revents) { - auto conn = static_cast(w->data); - auto dconn = static_cast(conn->data); - if (LOG_ENABLED(INFO)) { - DCLOG(INFO, dconn) << "Idle connection EOF"; - } - auto dconn_pool = dconn->get_dconn_pool(); - dconn_pool->remove_downstream_connection(dconn); - // dconn was deleted -} -} // namespace - -namespace { -void idle_timeoutcb(struct ev_loop *loop, ev_timer *w, int revents) { - auto conn = static_cast(w->data); - auto dconn = static_cast(conn->data); - if (LOG_ENABLED(INFO)) { - DCLOG(INFO, dconn) << "Idle connection timeout"; - } - auto dconn_pool = dconn->get_dconn_pool(); - dconn_pool->remove_downstream_connection(dconn); - // dconn was deleted -} -} // namespace - -void HttpDownstreamConnection::detach_downstream(Downstream *downstream) { - if (LOG_ENABLED(INFO)) { - DCLOG(INFO, this) << "Detaching from DOWNSTREAM:" << downstream; - } - downstream_ = nullptr; - ioctrl_.force_resume_read(); - - conn_.rlimit.startw(); - conn_.wlimit.stopw(); - - ev_set_cb(&conn_.rev, idle_readcb); - - ev_timer_stop(conn_.loop, &conn_.wt); - - conn_.rt.repeat = get_config()->downstream_idle_read_timeout; - ev_set_cb(&conn_.rt, idle_timeoutcb); - ev_timer_again(conn_.loop, &conn_.rt); -} - -void HttpDownstreamConnection::pause_read(IOCtrlReason reason) { - ioctrl_.pause_read(reason); -} - -int HttpDownstreamConnection::resume_read(IOCtrlReason reason, - size_t consumed) { - if (!downstream_->response_buf_full()) { - ioctrl_.resume_read(reason); - } - - return 0; -} - -void HttpDownstreamConnection::force_resume_read() { - ioctrl_.force_resume_read(); -} - -namespace { -int htp_msg_begincb(http_parser *htp) { - auto downstream = static_cast(htp->data); - - if (downstream->get_response_state() != Downstream::INITIAL) { - return -1; - } - - return 0; -} -} // namespace - -namespace { -int htp_hdrs_completecb(http_parser *htp) { - auto downstream = static_cast(htp->data); - auto upstream = downstream->get_upstream(); - int rv; - - downstream->set_response_http_status(htp->status_code); - downstream->set_response_major(htp->http_major); - downstream->set_response_minor(htp->http_minor); - - if (downstream->index_response_headers() != 0) { - downstream->set_response_state(Downstream::MSG_BAD_HEADER); - return -1; - } - - if (downstream->get_non_final_response()) { - // Reset content-length because we reuse same Downstream for the - // next response. - downstream->set_response_content_length(-1); - // For non-final response code, we just call - // on_downstream_header_complete() without changing response - // state. - rv = upstream->on_downstream_header_complete(downstream); - - if (rv != 0) { - return -1; - } - - return 0; - } - - downstream->set_response_connection_close(!http_should_keep_alive(htp)); - downstream->set_response_state(Downstream::HEADER_COMPLETE); - downstream->inspect_http1_response(); - downstream->check_upgrade_fulfilled(); - if (downstream->get_upgraded()) { - // content-length must be ignored for upgraded connection. - downstream->set_response_content_length(-1); - downstream->set_response_connection_close(true); - // transfer-encoding not applied to upgraded connection - downstream->set_chunked_response(false); - } - if (upstream->on_downstream_header_complete(downstream) != 0) { - return -1; - } - - if (downstream->get_upgraded()) { - // Upgrade complete, read until EOF in both ends - if (upstream->resume_read(SHRPX_NO_BUFFER, downstream, 0) != 0) { - return -1; - } - downstream->set_request_state(Downstream::HEADER_COMPLETE); - if (LOG_ENABLED(INFO)) { - LOG(INFO) << "HTTP upgrade success. stream_id=" - << downstream->get_stream_id(); - } - } - - unsigned int status = downstream->get_response_http_status(); - // Ignore the response body. HEAD response may contain - // Content-Length or Transfer-Encoding: chunked. Some server send - // 304 status code with nonzero Content-Length, but without response - // body. See - // https://tools.ietf.org/html/rfc7230#section-3.3 - - // TODO It seems that the cases other than HEAD are handled by - // http-parser. Need test. - return downstream->get_request_method() == "HEAD" || - (100 <= status && status <= 199) || status == 204 || - status == 304 - ? 1 - : 0; -} -} // namespace - -namespace { -int htp_hdr_keycb(http_parser *htp, const char *data, size_t len) { - auto downstream = static_cast(htp->data); - - if (downstream->get_response_headers_sum() + len > - get_config()->header_field_buffer) { - if (LOG_ENABLED(INFO)) { - DLOG(INFO, downstream) << "Too large header block size=" - << downstream->get_response_headers_sum() + len; - } - return -1; - } - - if (downstream->get_response_state() == Downstream::INITIAL) { - if (downstream->get_response_header_key_prev()) { - downstream->append_last_response_header_key(data, len); - } else { - if (downstream->get_response_headers().size() >= - get_config()->max_header_fields) { - if (LOG_ENABLED(INFO)) { - DLOG(INFO, downstream) - << "Too many header field num=" - << downstream->get_response_headers().size() + 1; - } - return -1; - } - downstream->add_response_header(std::string(data, len), ""); - } - } else { - // trailer part - if (downstream->get_response_trailer_key_prev()) { - downstream->append_last_response_trailer_key(data, len); - } else { - if (downstream->get_response_headers().size() >= - get_config()->max_header_fields) { - if (LOG_ENABLED(INFO)) { - DLOG(INFO, downstream) - << "Too many header field num=" - << downstream->get_response_headers().size() + 1; - } - return -1; - } - downstream->add_response_trailer(std::string(data, len), ""); - } - } - return 0; -} -} // namespace - -namespace { -int htp_hdr_valcb(http_parser *htp, const char *data, size_t len) { - auto downstream = static_cast(htp->data); - if (downstream->get_response_headers_sum() + len > - get_config()->header_field_buffer) { - if (LOG_ENABLED(INFO)) { - DLOG(INFO, downstream) << "Too large header block size=" - << downstream->get_response_headers_sum() + len; - } - return -1; - } - if (downstream->get_response_state() == Downstream::INITIAL) { - if (downstream->get_response_header_key_prev()) { - downstream->set_last_response_header_value(data, len); - } else { - downstream->append_last_response_header_value(data, len); - } - } else { - if (downstream->get_response_trailer_key_prev()) { - downstream->set_last_response_trailer_value(data, len); - } else { - downstream->append_last_response_trailer_value(data, len); - } - } - return 0; -} -} // namespace - -namespace { -int htp_bodycb(http_parser *htp, const char *data, size_t len) { - auto downstream = static_cast(htp->data); - - downstream->add_response_bodylen(len); - - return downstream->get_upstream()->on_downstream_body( - downstream, reinterpret_cast(data), len, true); -} -} // namespace - -namespace { -int htp_msg_completecb(http_parser *htp) { - auto downstream = static_cast(htp->data); - - if (downstream->get_non_final_response()) { - downstream->reset_response(); - - return 0; - } - - downstream->set_response_state(Downstream::MSG_COMPLETE); - // Block reading another response message from (broken?) - // server. This callback is not called if the connection is - // tunneled. - downstream->pause_read(SHRPX_MSG_BLOCK); - return downstream->get_upstream()->on_downstream_body_complete(downstream); -} -} // namespace - -namespace { -http_parser_settings htp_hooks = { - htp_msg_begincb, // http_cb on_message_begin; - nullptr, // http_data_cb on_url; - nullptr, // http_data_cb on_status; - htp_hdr_keycb, // http_data_cb on_header_field; - htp_hdr_valcb, // http_data_cb on_header_value; - htp_hdrs_completecb, // http_cb on_headers_complete; - htp_bodycb, // http_data_cb on_body; - htp_msg_completecb // http_cb on_message_complete; -}; -} // namespace - -int HttpDownstreamConnection::on_read() { - if (!connected_) { - return 0; - } - - ev_timer_again(conn_.loop, &conn_.rt); - std::array buf; - int rv; - - if (downstream_->get_upgraded()) { - // For upgraded connection, just pass data to the upstream. - for (;;) { - auto nread = conn_.read_clear(buf.data(), buf.size()); - - if (nread == 0) { - return 0; - } - - if (nread < 0) { - return nread; - } - - rv = downstream_->get_upstream()->on_downstream_body( - downstream_, buf.data(), nread, true); - if (rv != 0) { - return rv; - } - - if (downstream_->response_buf_full()) { - downstream_->pause_read(SHRPX_NO_BUFFER); - return 0; - } - } - } - - for (;;) { - auto nread = conn_.read_clear(buf.data(), buf.size()); - - if (nread == 0) { - return 0; - } - - if (nread < 0) { - return nread; - } - - auto nproc = - http_parser_execute(&response_htp_, &htp_hooks, - reinterpret_cast(buf.data()), nread); - - auto htperr = HTTP_PARSER_ERRNO(&response_htp_); - - if (htperr != HPE_OK) { - if (LOG_ENABLED(INFO)) { - DCLOG(INFO, this) << "HTTP parser failure: " - << "(" << http_errno_name(htperr) << ") " - << http_errno_description(htperr); - } - - return -1; - } - - if (nproc != static_cast(nread)) { - if (LOG_ENABLED(INFO)) { - DCLOG(INFO, this) << "nproc != nread"; - } - return -1; - } - - if (downstream_->response_buf_full()) { - downstream_->pause_read(SHRPX_NO_BUFFER); - return 0; - } - } -} - -int HttpDownstreamConnection::on_write() { - if (!connected_) { - return 0; - } - - ev_timer_again(conn_.loop, &conn_.rt); - - auto upstream = downstream_->get_upstream(); - auto input = downstream_->get_request_buf(); - - std::array iov; - - while (input->rleft() > 0) { - auto iovcnt = input->riovec(iov.data(), iov.size()); - - auto nwrite = conn_.writev_clear(iov.data(), iovcnt); - - if (nwrite == 0) { - return 0; - } - - if (nwrite < 0) { - return nwrite; - } - - input->drain(nwrite); - } - - conn_.wlimit.stopw(); - ev_timer_stop(conn_.loop, &conn_.wt); - - if (input->rleft() == 0) { - upstream->resume_read(SHRPX_NO_BUFFER, downstream_, - downstream_->get_request_datalen()); - } - - return 0; -} - -int HttpDownstreamConnection::on_connect() { - auto connect_blocker = client_handler_->get_connect_blocker(); - - if (!util::check_socket_connected(conn_.fd)) { - conn_.wlimit.stopw(); - - if (LOG_ENABLED(INFO)) { - DLOG(INFO, this) << "downstream connect failed"; - } - - return -1; - } - - connected_ = true; - - connect_blocker->on_success(); - - conn_.rlimit.startw(); - ev_set_cb(&conn_.wev, writecb); - - return 0; -} - -void HttpDownstreamConnection::on_upstream_change(Upstream *upstream) {} - -void HttpDownstreamConnection::signal_write() { conn_.wlimit.startw(); } - -} // namespace shrpx diff --git a/src/shrpx_http_downstream_connection.h b/src/shrpx_http_downstream_connection.h deleted file mode 100644 index 02480d5..0000000 --- a/src/shrpx_http_downstream_connection.h +++ /dev/null @@ -1,78 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2012 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#ifndef SHRPX_HTTP_DOWNSTREAM_CONNECTION_H -#define SHRPX_HTTP_DOWNSTREAM_CONNECTION_H - -#include "shrpx.h" - -#include "http-parser/http_parser.h" - -#include "shrpx_downstream_connection.h" -#include "shrpx_io_control.h" -#include "shrpx_connection.h" - -namespace shrpx { - -class DownstreamConnectionPool; - -class HttpDownstreamConnection : public DownstreamConnection { -public: - HttpDownstreamConnection(DownstreamConnectionPool *dconn_pool, - struct ev_loop *loop); - virtual ~HttpDownstreamConnection(); - virtual int attach_downstream(Downstream *downstream); - virtual void detach_downstream(Downstream *downstream); - - virtual int push_request_headers(); - virtual int push_upload_data_chunk(const uint8_t *data, size_t datalen); - virtual int end_upload_data(); - - virtual void pause_read(IOCtrlReason reason); - virtual int resume_read(IOCtrlReason reason, size_t consumed); - virtual void force_resume_read(); - - virtual int on_read(); - virtual int on_write(); - - virtual void on_upstream_change(Upstream *upstream); - virtual int on_priority_change(int32_t pri) { return 0; } - - virtual bool poolable() const { return true; } - - int on_connect(); - void signal_write(); - -private: - Connection conn_; - IOControl ioctrl_; - http_parser response_htp_; - // index of get_config()->downstream_addrs this object is using - size_t addr_idx_; - bool connected_; -}; - -} // namespace shrpx - -#endif // SHRPX_HTTP_DOWNSTREAM_CONNECTION_H diff --git a/src/shrpx_https_upstream.cc b/src/shrpx_https_upstream.cc deleted file mode 100644 index 674f17b..0000000 --- a/src/shrpx_https_upstream.cc +++ /dev/null @@ -1,983 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2012 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#include "shrpx_https_upstream.h" - -#include -#include -#include - -#include "shrpx_client_handler.h" -#include "shrpx_downstream.h" -#include "shrpx_downstream_connection.h" -#include "shrpx_http.h" -#include "shrpx_config.h" -#include "shrpx_error.h" -#include "shrpx_log_config.h" -#include "shrpx_worker.h" -#include "http2.h" -#include "util.h" -#include "template.h" - -using namespace nghttp2; - -namespace shrpx { - -HttpsUpstream::HttpsUpstream(ClientHandler *handler) - : handler_(handler), current_header_length_(0), - ioctrl_(handler->get_rlimit()) { - http_parser_init(&htp_, HTTP_REQUEST); - htp_.data = this; -} - -HttpsUpstream::~HttpsUpstream() {} - -void HttpsUpstream::reset_current_header_length() { - current_header_length_ = 0; -} - -namespace { -int htp_msg_begin(http_parser *htp) { - auto upstream = static_cast(htp->data); - if (LOG_ENABLED(INFO)) { - ULOG(INFO, upstream) << "HTTP request started"; - } - upstream->reset_current_header_length(); - - auto handler = upstream->get_client_handler(); - - // TODO specify 0 as priority for now - upstream->attach_downstream( - make_unique(upstream, handler->get_mcpool(), 0, 0)); - return 0; -} -} // namespace - -namespace { -int htp_uricb(http_parser *htp, const char *data, size_t len) { - auto upstream = static_cast(htp->data); - auto downstream = upstream->get_downstream(); - if (downstream->get_request_headers_sum() + len > - get_config()->header_field_buffer) { - if (LOG_ENABLED(INFO)) { - ULOG(INFO, upstream) << "Too large URI size=" - << downstream->get_request_headers_sum() + len; - } - assert(downstream->get_request_state() == Downstream::INITIAL); - downstream->set_request_state(Downstream::HTTP1_REQUEST_HEADER_TOO_LARGE); - return -1; - } - downstream->add_request_headers_sum(len); - downstream->append_request_path(data, len); - return 0; -} -} // namespace - -namespace { -int htp_hdr_keycb(http_parser *htp, const char *data, size_t len) { - auto upstream = static_cast(htp->data); - auto downstream = upstream->get_downstream(); - if (downstream->get_request_headers_sum() + len > - get_config()->header_field_buffer) { - if (LOG_ENABLED(INFO)) { - ULOG(INFO, upstream) << "Too large header block size=" - << downstream->get_request_headers_sum() + len; - } - if (downstream->get_request_state() == Downstream::INITIAL) { - downstream->set_request_state(Downstream::HTTP1_REQUEST_HEADER_TOO_LARGE); - } - return -1; - } - if (downstream->get_request_state() == Downstream::INITIAL) { - if (downstream->get_request_header_key_prev()) { - downstream->append_last_request_header_key(data, len); - } else { - if (downstream->get_request_headers().size() >= - get_config()->max_header_fields) { - if (LOG_ENABLED(INFO)) { - ULOG(INFO, upstream) << "Too many header field num=" - << downstream->get_request_headers().size() + 1; - } - downstream->set_request_state( - Downstream::HTTP1_REQUEST_HEADER_TOO_LARGE); - return -1; - } - downstream->add_request_header(std::string(data, len), ""); - } - } else { - // trailer part - if (downstream->get_request_trailer_key_prev()) { - downstream->append_last_request_trailer_key(data, len); - } else { - if (downstream->get_request_headers().size() >= - get_config()->max_header_fields) { - if (LOG_ENABLED(INFO)) { - ULOG(INFO, upstream) << "Too many header field num=" - << downstream->get_request_headers().size() + 1; - } - return -1; - } - downstream->add_request_trailer(std::string(data, len), ""); - } - } - return 0; -} -} // namespace - -namespace { -int htp_hdr_valcb(http_parser *htp, const char *data, size_t len) { - auto upstream = static_cast(htp->data); - auto downstream = upstream->get_downstream(); - if (downstream->get_request_headers_sum() + len > - get_config()->header_field_buffer) { - if (LOG_ENABLED(INFO)) { - ULOG(INFO, upstream) << "Too large header block size=" - << downstream->get_request_headers_sum() + len; - } - if (downstream->get_request_state() == Downstream::INITIAL) { - downstream->set_request_state(Downstream::HTTP1_REQUEST_HEADER_TOO_LARGE); - } - return -1; - } - if (downstream->get_request_state() == Downstream::INITIAL) { - if (downstream->get_request_header_key_prev()) { - downstream->set_last_request_header_value(data, len); - } else { - downstream->append_last_request_header_value(data, len); - } - } else { - if (downstream->get_request_trailer_key_prev()) { - downstream->set_last_request_trailer_value(data, len); - } else { - downstream->append_last_request_trailer_value(data, len); - } - } - return 0; -} -} // namespace - -namespace { -void rewrite_request_host_path_from_uri(Downstream *downstream, const char *uri, - http_parser_url &u) { - assert(u.field_set & (1 << UF_HOST)); - - // As per https://tools.ietf.org/html/rfc7230#section-5.4, we - // rewrite host header field with authority component. - std::string authority; - http2::copy_url_component(authority, &u, UF_HOST, uri); - // TODO properly check IPv6 numeric address - if (authority.find(':') != std::string::npos) { - authority = '[' + authority; - authority += ']'; - } - if (u.field_set & (1 << UF_PORT)) { - authority += ':'; - authority += util::utos(u.port); - } - downstream->set_request_http2_authority(authority); - - std::string path; - if (u.field_set & (1 << UF_PATH)) { - http2::copy_url_component(path, &u, UF_PATH, uri); - } else if (downstream->get_request_method() == "OPTIONS") { - // Server-wide OPTIONS takes following form in proxy request: - // - // OPTIONS http://example.org HTTP/1.1 - // - // Notice that no slash after authority. See - // http://tools.ietf.org/html/rfc7230#section-5.3.4 - downstream->set_request_path("*"); - // we ignore query component here - return; - } else { - path = "/"; - } - if (u.field_set & (1 << UF_QUERY)) { - auto &fdata = u.field_data[UF_QUERY]; - path += '?'; - path.append(uri + fdata.off, fdata.len); - } - downstream->set_request_path(std::move(path)); - if (get_config()->http2_proxy || get_config()->client_proxy) { - std::string scheme; - http2::copy_url_component(scheme, &u, UF_SCHEMA, uri); - downstream->set_request_http2_scheme(std::move(scheme)); - } -} -} // namespace - -namespace { -int htp_hdrs_completecb(http_parser *htp) { - int rv; - auto upstream = static_cast(htp->data); - if (LOG_ENABLED(INFO)) { - ULOG(INFO, upstream) << "HTTP request headers completed"; - } - auto downstream = upstream->get_downstream(); - - downstream->set_request_method( - http_method_str((enum http_method)htp->method)); - downstream->set_request_major(htp->http_major); - downstream->set_request_minor(htp->http_minor); - - downstream->set_request_connection_close(!http_should_keep_alive(htp)); - - if (LOG_ENABLED(INFO)) { - std::stringstream ss; - ss << downstream->get_request_method() << " " - << downstream->get_request_path() << " " - << "HTTP/" << downstream->get_request_major() << "." - << downstream->get_request_minor() << "\n"; - const auto &headers = downstream->get_request_headers(); - for (size_t i = 0; i < headers.size(); ++i) { - ss << TTY_HTTP_HD << headers[i].name << TTY_RST << ": " - << headers[i].value << "\n"; - } - ULOG(INFO, upstream) << "HTTP request headers\n" << ss.str(); - } - - if (downstream->index_request_headers() != 0) { - return -1; - } - - if (downstream->get_request_major() == 1 && - downstream->get_request_minor() == 1 && - !downstream->get_request_header(http2::HD_HOST)) { - return -1; - } - - downstream->inspect_http1_request(); - - if (downstream->get_request_method() != "CONNECT") { - http_parser_url u{}; - // make a copy of request path, since we may set request path - // while we are refering to original request path. - auto uri = downstream->get_request_path(); - rv = http_parser_parse_url(uri.c_str(), - downstream->get_request_path().size(), 0, &u); - if (rv != 0) { - // Expect to respond with 400 bad request - return -1; - } - // checking UF_HOST could be redundant, but just in case ... - if (!(u.field_set & (1 << UF_SCHEMA)) || !(u.field_set & (1 << UF_HOST))) { - if (get_config()->http2_proxy || get_config()->client_proxy) { - // Request URI should be absolute-form for client proxy mode - return -1; - } - } else { - rewrite_request_host_path_from_uri(downstream, uri.c_str(), u); - } - } - - rv = downstream->attach_downstream_connection( - upstream->get_client_handler()->get_downstream_connection()); - - if (rv != 0) { - downstream->set_request_state(Downstream::CONNECT_FAIL); - - return -1; - } - - rv = downstream->push_request_headers(); - - if (rv != 0) { - return -1; - } - - downstream->set_request_state(Downstream::HEADER_COMPLETE); - - return 0; -} -} // namespace - -namespace { -int htp_bodycb(http_parser *htp, const char *data, size_t len) { - int rv; - auto upstream = static_cast(htp->data); - auto downstream = upstream->get_downstream(); - rv = downstream->push_upload_data_chunk( - reinterpret_cast(data), len); - if (rv != 0) { - return -1; - } - return 0; -} -} // namespace - -namespace { -int htp_msg_completecb(http_parser *htp) { - int rv; - auto upstream = static_cast(htp->data); - if (LOG_ENABLED(INFO)) { - ULOG(INFO, upstream) << "HTTP request completed"; - } - auto handler = upstream->get_client_handler(); - auto downstream = upstream->get_downstream(); - downstream->set_request_state(Downstream::MSG_COMPLETE); - rv = downstream->end_upload_data(); - if (rv != 0) { - return -1; - } - - if (handler->get_http2_upgrade_allowed() && - downstream->get_http2_upgrade_request() && - handler->perform_http2_upgrade(upstream) != 0) { - if (LOG_ENABLED(INFO)) { - ULOG(INFO, upstream) << "HTTP Upgrade to HTTP/2 failed"; - } - } - - // Stop further processing to complete this request - http_parser_pause(htp, 1); - return 0; -} -} // namespace - -namespace { -http_parser_settings htp_hooks = { - htp_msg_begin, // http_cb on_message_begin; - htp_uricb, // http_data_cb on_url; - nullptr, // http_data_cb on_status; - htp_hdr_keycb, // http_data_cb on_header_field; - htp_hdr_valcb, // http_data_cb on_header_value; - htp_hdrs_completecb, // http_cb on_headers_complete; - htp_bodycb, // http_data_cb on_body; - htp_msg_completecb // http_cb on_message_complete; -}; -} // namespace - -// on_read() does not consume all available data in input buffer if -// one http request is fully received. -int HttpsUpstream::on_read() { - auto rb = handler_->get_rb(); - auto rlimit = handler_->get_rlimit(); - auto downstream = get_downstream(); - - if (rb->rleft() == 0) { - return 0; - } - - // downstream can be nullptr here, because it is initialized in the - // callback chain called by http_parser_execute() - if (downstream && downstream->get_upgraded()) { - - auto rv = downstream->push_upload_data_chunk(rb->pos, rb->rleft()); - - if (rv != 0) { - return -1; - } - - rb->reset(); - rlimit->startw(); - - if (downstream->request_buf_full()) { - if (LOG_ENABLED(INFO)) { - ULOG(INFO, this) << "Downstream request buf is full"; - } - pause_read(SHRPX_NO_BUFFER); - - return 0; - } - - return 0; - } - - if (downstream) { - // To avoid reading next pipelined request - switch (downstream->get_request_state()) { - case Downstream::INITIAL: - case Downstream::HEADER_COMPLETE: - break; - default: - return 0; - } - } - - // http_parser_execute() does nothing once it entered error state. - auto nread = http_parser_execute( - &htp_, &htp_hooks, reinterpret_cast(rb->pos), rb->rleft()); - - rb->drain(nread); - rlimit->startw(); - - // Well, actually header length + some body bytes - current_header_length_ += nread; - - // Get downstream again because it may be initialized in http parser - // execution - downstream = get_downstream(); - - auto htperr = HTTP_PARSER_ERRNO(&htp_); - - if (htperr == HPE_PAUSED) { - return 0; - } - - if (htperr != HPE_OK) { - if (LOG_ENABLED(INFO)) { - ULOG(INFO, this) << "HTTP parse failure: " - << "(" << http_errno_name(htperr) << ") " - << http_errno_description(htperr); - } - - if (downstream && downstream->get_response_state() != Downstream::INITIAL) { - handler_->set_should_close_after_write(true); - handler_->signal_write(); - return 0; - } - - unsigned int status_code; - - if (downstream) { - if (downstream->get_request_state() == Downstream::CONNECT_FAIL) { - status_code = 503; - } else if (downstream->get_request_state() == - Downstream::HTTP1_REQUEST_HEADER_TOO_LARGE) { - status_code = 431; - } else { - status_code = 400; - } - } else { - status_code = 400; - } - - error_reply(status_code); - - handler_->signal_write(); - - return 0; - } - - // downstream can be NULL here. - if (downstream && downstream->request_buf_full()) { - if (LOG_ENABLED(INFO)) { - ULOG(INFO, this) << "Downstream request buffer is full"; - } - - pause_read(SHRPX_NO_BUFFER); - - return 0; - } - - return 0; -} - -int HttpsUpstream::on_write() { - auto downstream = get_downstream(); - if (!downstream) { - return 0; - } - auto wb = handler_->get_wb(); - if (wb->wleft() == 0) { - return 0; - } - - auto dconn = downstream->get_downstream_connection(); - auto output = downstream->get_response_buf(); - - if (output->rleft() == 0 && dconn && - downstream->get_response_state() != Downstream::MSG_COMPLETE) { - if (downstream->resume_read(SHRPX_NO_BUFFER, - downstream->get_response_datalen()) != 0) { - return -1; - } - - if (downstream_read(dconn) != 0) { - return -1; - } - } - - auto n = output->remove(wb->last, wb->wleft()); - wb->write(n); - - if (wb->rleft() > 0) { - return 0; - } - - // We need to postpone detachment until all data are sent so that - // we can notify nghttp2 library all data consumed. - if (downstream->get_response_state() == Downstream::MSG_COMPLETE) { - if (downstream->get_response_connection_close()) { - // Connection close - downstream->pop_downstream_connection(); - // dconn was deleted - } else { - // Keep-alive - downstream->detach_downstream_connection(); - } - // We need this if response ends before request. - if (downstream->get_request_state() == Downstream::MSG_COMPLETE) { - delete_downstream(); - return resume_read(SHRPX_NO_BUFFER, nullptr, 0); - } - } - - return downstream->resume_read(SHRPX_NO_BUFFER, - downstream->get_response_datalen()); -} - -int HttpsUpstream::on_event() { return 0; } - -ClientHandler *HttpsUpstream::get_client_handler() const { return handler_; } - -void HttpsUpstream::pause_read(IOCtrlReason reason) { - ioctrl_.pause_read(reason); -} - -int HttpsUpstream::resume_read(IOCtrlReason reason, Downstream *downstream, - size_t consumed) { - // downstream could be nullptr - if (downstream && downstream->request_buf_full()) { - return 0; - } - if (ioctrl_.resume_read(reason)) { - // Process remaining data in input buffer here because these bytes - // are not notified by readcb until new data arrive. - http_parser_pause(&htp_, 0); - return on_read(); - } - - return 0; -} - -int HttpsUpstream::downstream_read(DownstreamConnection *dconn) { - auto downstream = dconn->get_downstream(); - int rv; - - rv = downstream->on_read(); - - if (rv == SHRPX_ERR_EOF) { - return downstream_eof(dconn); - } - - if (rv < 0) { - return downstream_error(dconn, Downstream::EVENT_ERROR); - } - - if (downstream->get_response_state() == Downstream::MSG_RESET) { - return -1; - } - - if (downstream->get_response_state() == Downstream::MSG_BAD_HEADER) { - error_reply(502); - downstream->pop_downstream_connection(); - goto end; - } - - // Detach downstream connection early so that it could be reused - // without hitting server's request timeout. - if (downstream->get_response_state() == Downstream::MSG_COMPLETE && - !downstream->get_response_connection_close()) { - // Keep-alive - downstream->detach_downstream_connection(); - } - -end: - handler_->signal_write(); - - return 0; -} - -int HttpsUpstream::downstream_write(DownstreamConnection *dconn) { - int rv; - rv = dconn->on_write(); - if (rv == SHRPX_ERR_NETWORK) { - return downstream_error(dconn, Downstream::EVENT_ERROR); - } - - if (rv != 0) { - return -1; - } - - return 0; -} - -int HttpsUpstream::downstream_eof(DownstreamConnection *dconn) { - auto downstream = dconn->get_downstream(); - - if (LOG_ENABLED(INFO)) { - DCLOG(INFO, dconn) << "EOF"; - } - - if (downstream->get_response_state() == Downstream::MSG_COMPLETE) { - goto end; - } - - if (downstream->get_response_state() == Downstream::HEADER_COMPLETE) { - // Server may indicate the end of the request by EOF - if (LOG_ENABLED(INFO)) { - DCLOG(INFO, dconn) << "The end of the response body was indicated by " - << "EOF"; - } - on_downstream_body_complete(downstream); - downstream->set_response_state(Downstream::MSG_COMPLETE); - downstream->pop_downstream_connection(); - goto end; - } - - if (downstream->get_response_state() == Downstream::INITIAL) { - // we did not send any response headers, so we can reply error - // message. - if (LOG_ENABLED(INFO)) { - DCLOG(INFO, dconn) << "Return error reply"; - } - error_reply(502); - downstream->pop_downstream_connection(); - goto end; - } - - // Otherwise, we don't know how to recover from this situation. Just - // drop connection. - return -1; -end: - handler_->signal_write(); - - return 0; -} - -int HttpsUpstream::downstream_error(DownstreamConnection *dconn, int events) { - auto downstream = dconn->get_downstream(); - if (LOG_ENABLED(INFO)) { - if (events & Downstream::EVENT_ERROR) { - DCLOG(INFO, dconn) << "Network error/general error"; - } else { - DCLOG(INFO, dconn) << "Timeout"; - } - } - if (downstream->get_response_state() != Downstream::INITIAL) { - return -1; - } - - unsigned int status; - if (events & Downstream::EVENT_TIMEOUT) { - status = 504; - } else { - status = 502; - } - error_reply(status); - - downstream->pop_downstream_connection(); - - handler_->signal_write(); - return 0; -} - -void HttpsUpstream::error_reply(unsigned int status_code) { - auto html = http::create_error_html(status_code); - auto downstream = get_downstream(); - - if (!downstream) { - attach_downstream( - make_unique(this, handler_->get_mcpool(), 1, 1)); - downstream = get_downstream(); - } - - downstream->set_response_http_status(status_code); - // we are going to close connection for both frontend and backend in - // error condition. This is safest option. - downstream->set_response_connection_close(true); - handler_->set_should_close_after_write(true); - - auto output = downstream->get_response_buf(); - - output->append("HTTP/1.1 "); - auto status_str = http2::get_status_string(status_code); - output->append(status_str.c_str(), status_str.size()); - output->append("\r\nServer: "); - output->append(get_config()->server_name, strlen(get_config()->server_name)); - output->append("\r\nContent-Length: "); - auto cl = util::utos(html.size()); - output->append(cl.c_str(), cl.size()); - output->append("\r\nContent-Type: text/html; " - "charset=UTF-8\r\nConnection: close\r\n\r\n"); - output->append(html.c_str(), html.size()); - - downstream->add_response_sent_bodylen(html.size()); - downstream->set_response_state(Downstream::MSG_COMPLETE); -} - -void HttpsUpstream::attach_downstream(std::unique_ptr downstream) { - assert(!downstream_); - downstream_ = std::move(downstream); -} - -void HttpsUpstream::delete_downstream() { - if (downstream_ && downstream_->accesslog_ready()) { - handler_->write_accesslog(downstream_.get()); - } - - downstream_.reset(); -} - -Downstream *HttpsUpstream::get_downstream() const { return downstream_.get(); } - -std::unique_ptr HttpsUpstream::pop_downstream() { - return std::unique_ptr(downstream_.release()); -} - -int HttpsUpstream::on_downstream_header_complete(Downstream *downstream) { - if (LOG_ENABLED(INFO)) { - if (downstream->get_non_final_response()) { - DLOG(INFO, downstream) << "HTTP non-final response header"; - } else { - DLOG(INFO, downstream) << "HTTP response header completed"; - } - } - - std::string hdrs = "HTTP/"; - hdrs += util::utos(downstream->get_request_major()); - hdrs += "."; - hdrs += util::utos(downstream->get_request_minor()); - hdrs += " "; - hdrs += http2::get_status_string(downstream->get_response_http_status()); - hdrs += "\r\n"; - - if (!get_config()->http2_proxy && !get_config()->client_proxy && - !get_config()->no_location_rewrite) { - downstream->rewrite_location_response_header( - get_client_handler()->get_upstream_scheme()); - } - - http2::build_http1_headers_from_headers(hdrs, - downstream->get_response_headers()); - - auto output = downstream->get_response_buf(); - - if (downstream->get_non_final_response()) { - hdrs += "\r\n"; - - if (LOG_ENABLED(INFO)) { - log_response_headers(hdrs); - } - - output->append(hdrs.c_str(), hdrs.size()); - - downstream->clear_response_headers(); - - return 0; - } - - auto worker = handler_->get_worker(); - - // after graceful shutdown commenced, add connection: close header - // field. - if (worker->get_graceful_shutdown()) { - downstream->set_response_connection_close(true); - } - - // We check downstream->get_response_connection_close() in case when - // the Content-Length is not available. - if (!downstream->get_request_connection_close() && - !downstream->get_response_connection_close()) { - if (downstream->get_request_major() <= 0 || - downstream->get_request_minor() <= 0) { - // We add this header for HTTP/1.0 or HTTP/0.9 clients - hdrs += "Connection: Keep-Alive\r\n"; - } - } else if (!downstream->get_upgraded() || - downstream->get_request_method() != "CONNECT") { - hdrs += "Connection: close\r\n"; - } - - if (!downstream->get_response_header(http2::HD_ALT_SVC)) { - // We won't change or alter alt-svc from backend for now - if (!get_config()->altsvcs.empty()) { - hdrs += "Alt-Svc: "; - - for (auto &altsvc : get_config()->altsvcs) { - hdrs += util::percent_encode_token(altsvc.protocol_id); - hdrs += "=\""; - hdrs += util::quote_string(std::string(altsvc.host, altsvc.host_len)); - hdrs += ":"; - hdrs += util::utos(altsvc.port); - hdrs += "\", "; - } - - hdrs[hdrs.size() - 2] = '\r'; - hdrs[hdrs.size() - 1] = '\n'; - } - } - - if (!get_config()->http2_proxy && !get_config()->client_proxy) { - hdrs += "Server: "; - hdrs += get_config()->server_name; - hdrs += "\r\n"; - } else { - auto server = downstream->get_response_header(http2::HD_SERVER); - if (server) { - hdrs += "Server: "; - hdrs += (*server).value; - hdrs += "\r\n"; - } - } - - auto via = downstream->get_response_header(http2::HD_VIA); - if (get_config()->no_via) { - if (via) { - hdrs += "Via: "; - hdrs += (*via).value; - hdrs += "\r\n"; - } - } else { - hdrs += "Via: "; - if (via) { - hdrs += (*via).value; - hdrs += ", "; - } - hdrs += http::create_via_header_value(downstream->get_response_major(), - downstream->get_response_minor()); - hdrs += "\r\n"; - } - - for (auto &p : get_config()->add_response_headers) { - hdrs += p.first; - hdrs += ": "; - hdrs += p.second; - hdrs += "\r\n"; - } - - hdrs += "\r\n"; - - if (LOG_ENABLED(INFO)) { - log_response_headers(hdrs); - } - - output->append(hdrs.c_str(), hdrs.size()); - - return 0; -} - -int HttpsUpstream::on_downstream_body(Downstream *downstream, - const uint8_t *data, size_t len, - bool flush) { - if (len == 0) { - return 0; - } - auto output = downstream->get_response_buf(); - if (downstream->get_chunked_response()) { - auto chunk_size_hex = util::utox(len); - chunk_size_hex += "\r\n"; - - output->append(chunk_size_hex.c_str(), chunk_size_hex.size()); - } - output->append(data, len); - - downstream->add_response_sent_bodylen(len); - - if (downstream->get_chunked_response()) { - output->append("\r\n"); - } - return 0; -} - -int HttpsUpstream::on_downstream_body_complete(Downstream *downstream) { - if (downstream->get_chunked_response()) { - auto output = downstream->get_response_buf(); - auto &trailers = downstream->get_response_trailers(); - if (trailers.empty()) { - output->append("0\r\n\r\n"); - } else { - output->append("0\r\n"); - std::string trailer_part; - http2::build_http1_headers_from_headers(trailer_part, trailers); - output->append(trailer_part.c_str(), trailer_part.size()); - output->append("\r\n"); - } - } - if (LOG_ENABLED(INFO)) { - DLOG(INFO, downstream) << "HTTP response completed"; - } - - if (!downstream->validate_response_bodylen()) { - downstream->set_response_connection_close(true); - } - - if (downstream->get_request_connection_close() || - downstream->get_response_connection_close()) { - auto handler = get_client_handler(); - handler->set_should_close_after_write(true); - } - return 0; -} - -int HttpsUpstream::on_downstream_abort_request(Downstream *downstream, - unsigned int status_code) { - error_reply(status_code); - handler_->signal_write(); - return 0; -} - -void HttpsUpstream::log_response_headers(const std::string &hdrs) const { - const char *hdrp; - std::string nhdrs; - if (log_config()->errorlog_tty) { - nhdrs = http::colorizeHeaders(hdrs.c_str()); - hdrp = nhdrs.c_str(); - } else { - hdrp = hdrs.c_str(); - } - ULOG(INFO, this) << "HTTP response headers\n" << hdrp; -} - -void HttpsUpstream::on_handler_delete() { - if (downstream_ && downstream_->accesslog_ready()) { - handler_->write_accesslog(downstream_.get()); - } -} - -int HttpsUpstream::on_downstream_reset(bool no_retry) { - int rv; - - if (!downstream_->request_submission_ready()) { - // Return error so that caller can delete handler - return -1; - } - - downstream_->pop_downstream_connection(); - - downstream_->add_retry(); - - if (no_retry || downstream_->no_more_retry()) { - goto fail; - } - - rv = downstream_->attach_downstream_connection( - handler_->get_downstream_connection()); - if (rv != 0) { - goto fail; - } - - return 0; - -fail: - if (on_downstream_abort_request(downstream_.get(), 503) != 0) { - return -1; - } - downstream_->pop_downstream_connection(); - - return 0; -} - -} // namespace shrpx diff --git a/src/shrpx_https_upstream.h b/src/shrpx_https_upstream.h deleted file mode 100644 index 3b9d532..0000000 --- a/src/shrpx_https_upstream.h +++ /dev/null @@ -1,91 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2012 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#ifndef SHRPX_HTTPS_UPSTREAM_H -#define SHRPX_HTTPS_UPSTREAM_H - -#include "shrpx.h" - -#include -#include - -#include "http-parser/http_parser.h" - -#include "shrpx_upstream.h" -#include "memchunk.h" - -using namespace nghttp2; - -namespace shrpx { - -class ClientHandler; - -class HttpsUpstream : public Upstream { -public: - HttpsUpstream(ClientHandler *handler); - virtual ~HttpsUpstream(); - virtual int on_read(); - virtual int on_write(); - virtual int on_event(); - virtual int on_downstream_abort_request(Downstream *downstream, - unsigned int status_code); - virtual ClientHandler *get_client_handler() const; - - virtual int downstream_read(DownstreamConnection *dconn); - virtual int downstream_write(DownstreamConnection *dconn); - virtual int downstream_eof(DownstreamConnection *dconn); - virtual int downstream_error(DownstreamConnection *dconn, int events); - - void attach_downstream(std::unique_ptr downstream); - void delete_downstream(); - Downstream *get_downstream() const; - std::unique_ptr pop_downstream(); - void error_reply(unsigned int status_code); - - virtual void pause_read(IOCtrlReason reason); - virtual int resume_read(IOCtrlReason reason, Downstream *downstream, - size_t consumed); - - virtual int on_downstream_header_complete(Downstream *downstream); - virtual int on_downstream_body(Downstream *downstream, const uint8_t *data, - size_t len, bool flush); - virtual int on_downstream_body_complete(Downstream *downstream); - - virtual void on_handler_delete(); - virtual int on_downstream_reset(bool no_retry); - - void reset_current_header_length(); - void log_response_headers(const std::string &hdrs) const; - -private: - ClientHandler *handler_; - http_parser htp_; - size_t current_header_length_; - std::unique_ptr downstream_; - IOControl ioctrl_; -}; - -} // namespace shrpx - -#endif // SHRPX_HTTPS_UPSTREAM_H diff --git a/src/shrpx_io_control.cc b/src/shrpx_io_control.cc deleted file mode 100644 index f43a257..0000000 --- a/src/shrpx_io_control.cc +++ /dev/null @@ -1,66 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2012 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#include "shrpx_io_control.h" - -#include - -#include "shrpx_rate_limit.h" -#include "util.h" - -using namespace nghttp2; - -namespace shrpx { - -IOControl::IOControl(RateLimit *lim) : lim_(lim), rdbits_(0) {} - -IOControl::~IOControl() {} - -void IOControl::pause_read(IOCtrlReason reason) { - rdbits_ |= reason; - if (lim_) { - lim_->stopw(); - } -} - -bool IOControl::resume_read(IOCtrlReason reason) { - rdbits_ &= ~reason; - if (rdbits_ == 0) { - if (lim_) { - lim_->startw(); - } - return true; - } - - return false; -} - -void IOControl::force_resume_read() { - rdbits_ = 0; - if (lim_) { - lim_->startw(); - } -} - -} // namespace shrpx diff --git a/src/shrpx_io_control.h b/src/shrpx_io_control.h deleted file mode 100644 index 9baf95a..0000000 --- a/src/shrpx_io_control.h +++ /dev/null @@ -1,57 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2012 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#ifndef SHRPX_IO_CONTROL_H -#define SHRPX_IO_CONTROL_H - -#include "shrpx.h" - -#include - -#include - -#include "shrpx_rate_limit.h" - -namespace shrpx { - -enum IOCtrlReason { SHRPX_NO_BUFFER = 1 << 0, SHRPX_MSG_BLOCK = 1 << 1 }; - -class IOControl { -public: - IOControl(RateLimit *lim); - ~IOControl(); - void pause_read(IOCtrlReason reason); - // Returns true if read operation is enabled after this call - bool resume_read(IOCtrlReason reason); - // Clear all pause flags and enable read - void force_resume_read(); - -private: - RateLimit *lim_; - uint32_t rdbits_; -}; - -} // namespace shrpx - -#endif // SHRPX_IO_CONTROL_H diff --git a/src/shrpx_log.cc b/src/shrpx_log.cc deleted file mode 100644 index 1446df6..0000000 --- a/src/shrpx_log.cc +++ /dev/null @@ -1,343 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2012 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#include "shrpx_log.h" - -#ifdef HAVE_SYSLOG_H -#include -#endif // HAVE_SYSLOG_H -#ifdef HAVE_UNISTD_H -#include -#endif // HAVE_UNISTD_H -#ifdef HAVE_INTTYPES_H -#include -#endif // HAVE_INTTYPES_H - -#include -#include -#include -#include -#include -#include - -#include "shrpx_config.h" -#include "shrpx_downstream.h" -#include "util.h" -#include "template.h" - -using namespace nghttp2; - -namespace shrpx { - -namespace { -const char *SEVERITY_STR[] = {"INFO", "NOTICE", "WARN", "ERROR", "FATAL"}; -} // namespace - -namespace { -const char *SEVERITY_COLOR[] = { - "\033[1;32m", // INFO - "\033[1;36m", // NOTICE - "\033[1;33m", // WARN - "\033[1;31m", // ERROR - "\033[1;35m", // FATAL -}; -} // namespace - -int Log::severity_thres_ = NOTICE; - -void Log::set_severity_level(int severity) { severity_thres_ = severity; } - -int Log::set_severity_level_by_name(const char *name) { - for (size_t i = 0, max = array_size(SEVERITY_STR); i < max; ++i) { - if (strcmp(SEVERITY_STR[i], name) == 0) { - severity_thres_ = i; - return 0; - } - } - return -1; -} - -int severity_to_syslog_level(int severity) { - switch (severity) { - case (INFO): - return LOG_INFO; - case (NOTICE): - return LOG_NOTICE; - case (WARN): - return LOG_WARNING; - case (ERROR): - return LOG_ERR; - case (FATAL): - return LOG_CRIT; - default: - return -1; - } -} - -Log::Log(int severity, const char *filename, int linenum) - : filename_(filename), severity_(severity), linenum_(linenum) {} - -Log::~Log() { - int rv; - - if (!get_config()) { - return; - } - - auto lgconf = log_config(); - - if (!log_enabled(severity_) || - (lgconf->errorlog_fd == -1 && !get_config()->errorlog_syslog)) { - return; - } - - if (get_config()->errorlog_syslog) { - if (severity_ == NOTICE) { - syslog(severity_to_syslog_level(severity_), "[%s] %s", - SEVERITY_STR[severity_], stream_.str().c_str()); - } else { - syslog(severity_to_syslog_level(severity_), "[%s] %s (%s:%d)", - SEVERITY_STR[severity_], stream_.str().c_str(), filename_, - linenum_); - } - - return; - } - - char buf[4096]; - auto tty = lgconf->errorlog_tty; - - lgconf->update_tstamp(std::chrono::system_clock::now()); - auto &time_local = lgconf->time_local_str; - - if (severity_ == NOTICE) { - rv = snprintf(buf, sizeof(buf), "%s PID%d [%s%s%s] %s\n", - time_local.c_str(), get_config()->pid, - tty ? SEVERITY_COLOR[severity_] : "", SEVERITY_STR[severity_], - tty ? "\033[0m" : "", stream_.str().c_str()); - } else { - rv = snprintf(buf, sizeof(buf), "%s PID%d [%s%s%s] %s%s:%d%s %s\n", - time_local.c_str(), get_config()->pid, - tty ? SEVERITY_COLOR[severity_] : "", SEVERITY_STR[severity_], - tty ? "\033[0m" : "", tty ? "\033[1;30m" : "", filename_, - linenum_, tty ? "\033[0m" : "", stream_.str().c_str()); - } - - if (rv < 0) { - return; - } - - auto nwrite = std::min(static_cast(rv), sizeof(buf) - 1); - - while (write(lgconf->errorlog_fd, buf, nwrite) == -1 && errno == EINTR) - ; -} - -namespace { -template -std::pair copy(const char *src, size_t avail, - OutputIterator oitr) { - auto nwrite = std::min(strlen(src), avail); - auto noitr = std::copy_n(src, nwrite, oitr); - return std::make_pair(noitr, avail - nwrite); -} -} // namespace - -void upstream_accesslog(const std::vector &lfv, - const LogSpec &lgsp) { - auto lgconf = log_config(); - - if (lgconf->accesslog_fd == -1 && !get_config()->accesslog_syslog) { - return; - } - - char buf[4096]; - - auto downstream = lgsp.downstream; - - auto p = buf; - auto avail = sizeof(buf) - 2; - - lgconf->update_tstamp(lgsp.time_now); - auto &time_local = lgconf->time_local_str; - auto &time_iso8601 = lgconf->time_iso8601_str; - - for (auto &lf : lfv) { - switch (lf.type) { - case SHRPX_LOGF_LITERAL: - std::tie(p, avail) = copy(lf.value.get(), avail, p); - break; - case SHRPX_LOGF_REMOTE_ADDR: - std::tie(p, avail) = copy(lgsp.remote_addr, avail, p); - break; - case SHRPX_LOGF_TIME_LOCAL: - std::tie(p, avail) = copy(time_local.c_str(), avail, p); - break; - case SHRPX_LOGF_TIME_ISO8601: - std::tie(p, avail) = copy(time_iso8601.c_str(), avail, p); - break; - case SHRPX_LOGF_REQUEST: - std::tie(p, avail) = copy(lgsp.method, avail, p); - std::tie(p, avail) = copy(" ", avail, p); - std::tie(p, avail) = copy(lgsp.path, avail, p); - std::tie(p, avail) = copy(" HTTP/", avail, p); - std::tie(p, avail) = copy(util::utos(lgsp.major).c_str(), avail, p); - if (lgsp.major < 2) { - std::tie(p, avail) = copy(".", avail, p); - std::tie(p, avail) = copy(util::utos(lgsp.minor).c_str(), avail, p); - } - break; - case SHRPX_LOGF_STATUS: - std::tie(p, avail) = copy(util::utos(lgsp.status).c_str(), avail, p); - break; - case SHRPX_LOGF_BODY_BYTES_SENT: - std::tie(p, avail) = - copy(util::utos(lgsp.body_bytes_sent).c_str(), avail, p); - break; - case SHRPX_LOGF_HTTP: - if (downstream) { - auto hd = downstream->get_request_header(lf.value.get()); - if (hd) { - std::tie(p, avail) = copy((*hd).value.c_str(), avail, p); - break; - } - } - - std::tie(p, avail) = copy("-", avail, p); - - break; - case SHRPX_LOGF_REMOTE_PORT: - std::tie(p, avail) = copy(lgsp.remote_port, avail, p); - break; - case SHRPX_LOGF_SERVER_PORT: - std::tie(p, avail) = copy(util::utos(lgsp.server_port).c_str(), avail, p); - break; - case SHRPX_LOGF_REQUEST_TIME: { - auto t = std::chrono::duration_cast( - lgsp.request_end_time - lgsp.request_start_time).count(); - - auto frac = util::utos(t % 1000); - auto sec = util::utos(t / 1000); - if (frac.size() < 3) { - frac = std::string(3 - frac.size(), '0') + frac; - } - sec += "."; - sec += frac; - - std::tie(p, avail) = copy(sec.c_str(), avail, p); - } break; - case SHRPX_LOGF_PID: - std::tie(p, avail) = copy(util::utos(lgsp.pid).c_str(), avail, p); - break; - case SHRPX_LOGF_ALPN: - std::tie(p, avail) = copy(lgsp.alpn, avail, p); - break; - case SHRPX_LOGF_NONE: - break; - default: - break; - } - } - - *p = '\0'; - - if (get_config()->accesslog_syslog) { - syslog(LOG_INFO, "%s", buf); - - return; - } - - *p++ = '\n'; - - auto nwrite = p - buf; - while (write(lgconf->accesslog_fd, buf, nwrite) == -1 && errno == EINTR) - ; -} - -int reopen_log_files() { - int res = 0; - - auto lgconf = log_config(); - - if (lgconf->accesslog_fd != -1) { - close(lgconf->accesslog_fd); - lgconf->accesslog_fd = -1; - } - - if (!get_config()->accesslog_syslog && get_config()->accesslog_file) { - - lgconf->accesslog_fd = - util::reopen_log_file(get_config()->accesslog_file.get()); - - if (lgconf->accesslog_fd == -1) { - LOG(ERROR) << "Failed to open accesslog file " - << get_config()->accesslog_file.get(); - res = -1; - } - } - - int new_errorlog_fd = -1; - - if (!get_config()->errorlog_syslog && get_config()->errorlog_file) { - - new_errorlog_fd = util::reopen_log_file(get_config()->errorlog_file.get()); - - if (new_errorlog_fd == -1) { - if (lgconf->errorlog_fd != -1) { - LOG(ERROR) << "Failed to open errorlog file " - << get_config()->errorlog_file.get(); - } else { - std::cerr << "Failed to open errorlog file " - << get_config()->errorlog_file.get() << std::endl; - } - - res = -1; - } - } - - if (lgconf->errorlog_fd != -1) { - close(lgconf->errorlog_fd); - lgconf->errorlog_fd = -1; - lgconf->errorlog_tty = false; - } - - if (new_errorlog_fd != -1) { - lgconf->errorlog_fd = new_errorlog_fd; - lgconf->errorlog_tty = isatty(lgconf->errorlog_fd); - } - - return res; -} - -void redirect_stderr_to_errorlog() { - auto lgconf = log_config(); - - if (get_config()->errorlog_syslog || lgconf->errorlog_fd == -1) { - return; - } - - dup2(lgconf->errorlog_fd, STDERR_FILENO); -} - -} // namespace shrpx diff --git a/src/shrpx_log.h b/src/shrpx_log.h deleted file mode 100644 index 41645d8..0000000 --- a/src/shrpx_log.h +++ /dev/null @@ -1,151 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2012 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#ifndef SHRPX_LOG_H -#define SHRPX_LOG_H - -#include "shrpx.h" - -#include - -#include -#include -#include -#include - -#include "shrpx_log_config.h" - -namespace shrpx { - -class Downstream; - -#define ENABLE_LOG 1 - -#define LOG_ENABLED(SEVERITY) (ENABLE_LOG && Log::log_enabled(SEVERITY)) - -#define LOG(SEVERITY) Log(SEVERITY, __FILE__, __LINE__) - -// Listener log -#define LLOG(SEVERITY, LISTEN) \ - (Log(SEVERITY, __FILE__, __LINE__) << "[LISTEN:" << LISTEN << "] ") - -// Worker log -#define WLOG(SEVERITY, WORKER) \ - (Log(SEVERITY, __FILE__, __LINE__) << "[WORKER:" << WORKER << "] ") - -// ClientHandler log -#define CLOG(SEVERITY, CLIENT_HANDLER) \ - (Log(SEVERITY, __FILE__, __LINE__) << "[CLIENT_HANDLER:" << CLIENT_HANDLER \ - << "] ") - -// Upstream log -#define ULOG(SEVERITY, UPSTREAM) \ - (Log(SEVERITY, __FILE__, __LINE__) << "[UPSTREAM:" << UPSTREAM << "] ") - -// Downstream log -#define DLOG(SEVERITY, DOWNSTREAM) \ - (Log(SEVERITY, __FILE__, __LINE__) << "[DOWNSTREAM:" << DOWNSTREAM << "] ") - -// Downstream connection log -#define DCLOG(SEVERITY, DCONN) \ - (Log(SEVERITY, __FILE__, __LINE__) << "[DCONN:" << DCONN << "] ") - -// Downstream HTTP2 session log -#define SSLOG(SEVERITY, HTTP2) \ - (Log(SEVERITY, __FILE__, __LINE__) << "[DHTTP2:" << HTTP2 << "] ") - -enum SeverityLevel { INFO, NOTICE, WARN, ERROR, FATAL }; - -class Log { -public: - Log(int severity, const char *filename, int linenum); - ~Log(); - template Log &operator<<(Type s) { - stream_ << s; - return *this; - } - static void set_severity_level(int severity); - static int set_severity_level_by_name(const char *name); - static bool log_enabled(int severity) { return severity >= severity_thres_; } - -private: - std::stringstream stream_; - const char *filename_; - int severity_; - int linenum_; - static int severity_thres_; -}; - -#define TTY_HTTP_HD (log_config()->errorlog_tty ? "\033[1;34m" : "") -#define TTY_RST (log_config()->errorlog_tty ? "\033[0m" : "") - -enum LogFragmentType { - SHRPX_LOGF_NONE, - SHRPX_LOGF_LITERAL, - SHRPX_LOGF_REMOTE_ADDR, - SHRPX_LOGF_TIME_LOCAL, - SHRPX_LOGF_TIME_ISO8601, - SHRPX_LOGF_REQUEST, - SHRPX_LOGF_STATUS, - SHRPX_LOGF_BODY_BYTES_SENT, - SHRPX_LOGF_HTTP, - SHRPX_LOGF_REMOTE_PORT, - SHRPX_LOGF_SERVER_PORT, - SHRPX_LOGF_REQUEST_TIME, - SHRPX_LOGF_PID, - SHRPX_LOGF_ALPN, -}; - -struct LogFragment { - LogFragmentType type; - std::unique_ptr value; -}; - -struct LogSpec { - Downstream *downstream; - const char *remote_addr; - const char *method; - const char *path; - const char *alpn; - std::chrono::system_clock::time_point time_now; - std::chrono::high_resolution_clock::time_point request_start_time; - std::chrono::high_resolution_clock::time_point request_end_time; - int major, minor; - unsigned int status; - int64_t body_bytes_sent; - const char *remote_port; - uint16_t server_port; - pid_t pid; -}; - -void upstream_accesslog(const std::vector &lf, - const LogSpec &lgsp); - -int reopen_log_files(); - -void redirect_stderr_to_errorlog(); - -} // namespace shrpx - -#endif // SHRPX_LOG_H diff --git a/src/shrpx_log_config.cc b/src/shrpx_log_config.cc deleted file mode 100644 index 6c21bc0..0000000 --- a/src/shrpx_log_config.cc +++ /dev/null @@ -1,69 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2014 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#include "shrpx_log_config.h" -#include "util.h" - -using namespace nghttp2; - -namespace shrpx { - -LogConfig::LogConfig() - : accesslog_fd(-1), errorlog_fd(-1), errorlog_tty(false) {} - -#ifndef NOTHREADS -static pthread_key_t lckey; -static pthread_once_t lckey_once = PTHREAD_ONCE_INIT; - -static void make_key(void) { pthread_key_create(&lckey, NULL); } - -LogConfig *log_config(void) { - pthread_once(&lckey_once, make_key); - LogConfig *config = (LogConfig *)pthread_getspecific(lckey); - if (!config) { - config = new LogConfig(); - pthread_setspecific(lckey, config); - } - return config; -} -#else -static LogConfig *config = new LogConfig(); -LogConfig *log_config(void) { return config; } -#endif // NOTHREADS - -void -LogConfig::update_tstamp(const std::chrono::system_clock::time_point &now) { - auto t0 = std::chrono::system_clock::to_time_t(time_str_updated_); - auto t1 = std::chrono::system_clock::to_time_t(now); - if (t0 == t1) { - return; - } - - time_str_updated_ = now; - - time_local_str = util::format_common_log(now); - time_iso8601_str = util::format_iso8601(now); -} - -} // namespace shrpx diff --git a/src/shrpx_log_config.h b/src/shrpx_log_config.h deleted file mode 100644 index 46b6357..0000000 --- a/src/shrpx_log_config.h +++ /dev/null @@ -1,53 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2014 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#ifndef SHRPX_LOG_CONFIG_H -#define SHRPX_LOG_CONFIG_H - -#include "shrpx.h" - -#include - -namespace shrpx { - -struct LogConfig { - std::chrono::system_clock::time_point time_str_updated_; - std::string time_local_str; - std::string time_iso8601_str; - int accesslog_fd; - int errorlog_fd; - // true if errorlog_fd is referring to a terminal. - bool errorlog_tty; - - LogConfig(); - void update_tstamp(const std::chrono::system_clock::time_point &now); -}; - -// We need LogConfig per thread to avoid data race around opening file -// descriptor for log files. -extern LogConfig *log_config(void); - -} // namespace shrpx - -#endif // SHRPX_LOG_CONFIG_H diff --git a/src/shrpx_rate_limit.cc b/src/shrpx_rate_limit.cc deleted file mode 100644 index 85836c1..0000000 --- a/src/shrpx_rate_limit.cc +++ /dev/null @@ -1,109 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2015 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#include "shrpx_rate_limit.h" - -#include - -namespace shrpx { - -namespace { -void regencb(struct ev_loop *loop, ev_timer *w, int revents) { - auto r = static_cast(w->data); - r->regen(); -} -} // namespace - -RateLimit::RateLimit(struct ev_loop *loop, ev_io *w, size_t rate, size_t burst, - SSL *ssl) - : w_(w), loop_(loop), ssl_(ssl), rate_(rate), burst_(burst), avail_(burst), - startw_req_(false) { - ev_timer_init(&t_, regencb, 0., 1.); - t_.data = this; - if (rate_ > 0) { - ev_timer_again(loop_, &t_); - } -} - -RateLimit::~RateLimit() { ev_timer_stop(loop_, &t_); } - -size_t RateLimit::avail() const { - if (rate_ == 0) { - return std::numeric_limits::max(); - } - return avail_; -} - -void RateLimit::drain(size_t n) { - if (rate_ == 0) { - return; - } - n = std::min(avail_, n); - avail_ -= n; - if (avail_ == 0) { - ev_io_stop(loop_, w_); - } -} - -void RateLimit::regen() { - if (rate_ == 0) { - return; - } - if (avail_ + rate_ > burst_) { - avail_ = burst_; - } else { - avail_ += rate_; - } - - if (avail_ > 0 && startw_req_) { - ev_io_start(loop_, w_); - handle_tls_pending_read(); - } -} - -void RateLimit::startw() { - startw_req_ = true; - if (rate_ == 0 || avail_ > 0) { - ev_io_start(loop_, w_); - handle_tls_pending_read(); - return; - } -} - -void RateLimit::stopw() { - startw_req_ = false; - ev_io_stop(loop_, w_); -} - -void RateLimit::handle_tls_pending_read() { - if (!ssl_ || SSL_pending(ssl_) == 0) { - return; - } - - // Note that ev_feed_event works without starting watcher, but we - // only call this function if watcher is active. - ev_feed_event(loop_, w_, EV_READ); -} - -} // namespace shrpx diff --git a/src/shrpx_rate_limit.h b/src/shrpx_rate_limit.h deleted file mode 100644 index 4550d01..0000000 --- a/src/shrpx_rate_limit.h +++ /dev/null @@ -1,65 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2015 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#ifndef SHRPX_RATE_LIMIT_H -#define SHRPX_RATE_LIMIT_H - -#include "shrpx.h" - -#include - -#include - -namespace shrpx { - -class RateLimit { -public: - // We need |ssl| object to check that it has unread decrypted bytes. - RateLimit(struct ev_loop *loop, ev_io *w, size_t rate, size_t burst, - SSL *ssl = nullptr); - ~RateLimit(); - size_t avail() const; - void drain(size_t n); - void regen(); - void startw(); - void stopw(); - // Feeds event if ssl_ object has unread decrypted bytes. This is - // required since it is buffered in ssl_ object, io event is not - // generated unless new incoming data is received. - void handle_tls_pending_read(); - -private: - ev_timer t_; - ev_io *w_; - struct ev_loop *loop_; - SSL *ssl_; - size_t rate_; - size_t burst_; - size_t avail_; - bool startw_req_; -}; - -} // namespace shrpx - -#endif // SHRPX_RATE_LIMIT_H diff --git a/src/shrpx_spdy_upstream.cc b/src/shrpx_spdy_upstream.cc deleted file mode 100644 index a14b0ed..0000000 --- a/src/shrpx_spdy_upstream.cc +++ /dev/null @@ -1,1103 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2012 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#include "shrpx_spdy_upstream.h" - -#include -#include -#include -#include - -#include - -#include "shrpx_client_handler.h" -#include "shrpx_downstream.h" -#include "shrpx_downstream_connection.h" -#include "shrpx_config.h" -#include "shrpx_http.h" -#include "http2.h" -#include "util.h" -#include "template.h" - -using namespace nghttp2; - -namespace shrpx { - -namespace { -ssize_t send_callback(spdylay_session *session, const uint8_t *data, size_t len, - int flags, void *user_data) { - auto upstream = static_cast(user_data); - auto handler = upstream->get_client_handler(); - auto wb = handler->get_wb(); - - if (wb->wleft() == 0) { - return SPDYLAY_ERR_WOULDBLOCK; - } - - auto nread = wb->write(data, len); - - return nread; -} -} // namespace - -namespace { -ssize_t recv_callback(spdylay_session *session, uint8_t *buf, size_t len, - int flags, void *user_data) { - auto upstream = static_cast(user_data); - auto handler = upstream->get_client_handler(); - auto rb = handler->get_rb(); - auto rlimit = handler->get_rlimit(); - - if (rb->rleft() == 0) { - return SPDYLAY_ERR_WOULDBLOCK; - } - - auto nread = std::min(rb->rleft(), len); - - memcpy(buf, rb->pos, nread); - rb->drain(nread); - rlimit->startw(); - - return nread; -} -} // namespace - -namespace { -void on_stream_close_callback(spdylay_session *session, int32_t stream_id, - spdylay_status_code status_code, - void *user_data) { - auto upstream = static_cast(user_data); - if (LOG_ENABLED(INFO)) { - ULOG(INFO, upstream) << "Stream stream_id=" << stream_id - << " is being closed"; - } - auto downstream = static_cast( - spdylay_session_get_stream_user_data(session, stream_id)); - if (!downstream) { - return; - } - - upstream->consume(stream_id, downstream->get_request_datalen()); - - downstream->reset_request_datalen(); - - if (downstream->get_request_state() == Downstream::CONNECT_FAIL) { - upstream->remove_downstream(downstream); - // downstrea was deleted - - return; - } - - downstream->set_request_state(Downstream::STREAM_CLOSED); - if (downstream->get_response_state() == Downstream::MSG_COMPLETE) { - // At this point, downstream response was read - if (!downstream->get_upgraded() && - !downstream->get_response_connection_close()) { - // Keep-alive - downstream->detach_downstream_connection(); - } - upstream->remove_downstream(downstream); - // downstrea was deleted - - return; - } - - // At this point, downstream read may be paused. - - // If shrpx_downstream::push_request_headers() failed, the - // error is handled here. - upstream->remove_downstream(downstream); - // downstrea was deleted - - // How to test this case? Request sufficient large download - // and make client send RST_STREAM after it gets first DATA - // frame chunk. -} -} // namespace - -namespace { -void on_ctrl_recv_callback(spdylay_session *session, spdylay_frame_type type, - spdylay_frame *frame, void *user_data) { - auto upstream = static_cast(user_data); - switch (type) { - case SPDYLAY_SYN_STREAM: { - if (LOG_ENABLED(INFO)) { - ULOG(INFO, upstream) << "Received upstream SYN_STREAM stream_id=" - << frame->syn_stream.stream_id; - } - - auto downstream = upstream->add_pending_downstream( - frame->syn_stream.stream_id, frame->syn_stream.pri); - - downstream->reset_upstream_rtimer(); - - auto nv = frame->syn_stream.nv; - - if (LOG_ENABLED(INFO)) { - std::stringstream ss; - for (size_t i = 0; nv[i]; i += 2) { - ss << TTY_HTTP_HD << nv[i] << TTY_RST << ": " << nv[i + 1] << "\n"; - } - ULOG(INFO, upstream) << "HTTP request headers. stream_id=" - << downstream->get_stream_id() << "\n" << ss.str(); - } - - size_t num_headers = 0; - size_t header_buffer = 0; - for (size_t i = 0; nv[i]; i += 2) { - ++num_headers; - header_buffer += strlen(nv[i]) + strlen(nv[i + 1]); - } - - if (header_buffer > get_config()->header_field_buffer || - num_headers > get_config()->max_header_fields) { - upstream->rst_stream(downstream, SPDYLAY_INTERNAL_ERROR); - return; - } - - for (size_t i = 0; nv[i]; i += 2) { - downstream->add_request_header(nv[i], nv[i + 1]); - } - - if (downstream->index_request_headers() != 0) { - if (upstream->error_reply(downstream, 400) != 0) { - ULOG(FATAL, upstream) << "error_reply failed"; - } - return; - } - - auto path = downstream->get_request_header(http2::HD__PATH); - auto scheme = downstream->get_request_header(http2::HD__SCHEME); - auto host = downstream->get_request_header(http2::HD__HOST); - auto method = downstream->get_request_header(http2::HD__METHOD); - - bool is_connect = method && "CONNECT" == method->value; - if (!path || !host || !method || !http2::non_empty_value(host) || - !http2::non_empty_value(path) || !http2::non_empty_value(method) || - (!is_connect && (!scheme || !http2::non_empty_value(scheme)))) { - upstream->rst_stream(downstream, SPDYLAY_INTERNAL_ERROR); - return; - } - - downstream->set_request_method(method->value); - if (is_connect) { - downstream->set_request_http2_authority(path->value); - } else { - downstream->set_request_http2_scheme(scheme->value); - downstream->set_request_http2_authority(host->value); - downstream->set_request_path(path->value); - } - - if (!(frame->syn_stream.hd.flags & SPDYLAY_CTRL_FLAG_FIN)) { - downstream->set_request_http2_expect_body(true); - } - - downstream->inspect_http2_request(); - - downstream->set_request_state(Downstream::HEADER_COMPLETE); - if (frame->syn_stream.hd.flags & SPDYLAY_CTRL_FLAG_FIN) { - if (!downstream->validate_request_bodylen()) { - upstream->rst_stream(downstream, SPDYLAY_PROTOCOL_ERROR); - return; - } - - downstream->disable_upstream_rtimer(); - downstream->set_request_state(Downstream::MSG_COMPLETE); - } - - upstream->start_downstream(downstream); - - break; - } - default: - break; - } -} -} // namespace - -void SpdyUpstream::start_downstream(Downstream *downstream) { - if (downstream_queue_.can_activate( - downstream->get_request_http2_authority())) { - initiate_downstream(downstream); - return; - } - - downstream_queue_.mark_blocked(downstream); -} - -void SpdyUpstream::initiate_downstream(Downstream *downstream) { - int rv = downstream->attach_downstream_connection( - handler_->get_downstream_connection()); - if (rv != 0) { - // If downstream connection fails, issue RST_STREAM. - rst_stream(downstream, SPDYLAY_INTERNAL_ERROR); - downstream->set_request_state(Downstream::CONNECT_FAIL); - - downstream_queue_.mark_failure(downstream); - - return; - } - rv = downstream->push_request_headers(); - if (rv != 0) { - rst_stream(downstream, SPDYLAY_INTERNAL_ERROR); - - downstream_queue_.mark_failure(downstream); - - return; - } - - downstream_queue_.mark_active(downstream); -} - -namespace { -void on_data_chunk_recv_callback(spdylay_session *session, uint8_t flags, - int32_t stream_id, const uint8_t *data, - size_t len, void *user_data) { - auto upstream = static_cast(user_data); - auto downstream = static_cast( - spdylay_session_get_stream_user_data(session, stream_id)); - - if (!downstream) { - upstream->consume(stream_id, len); - - return; - } - - downstream->reset_upstream_rtimer(); - - if (downstream->push_upload_data_chunk(data, len) != 0) { - upstream->rst_stream(downstream, SPDYLAY_INTERNAL_ERROR); - - upstream->consume(stream_id, len); - - return; - } - - if (!upstream->get_flow_control()) { - return; - } - - // If connection-level window control is not enabled (e.g, - // spdy/3), spdylay_session_get_recv_data_length() is always - // returns 0. - if (spdylay_session_get_recv_data_length(session) > - std::max(SPDYLAY_INITIAL_WINDOW_SIZE, - 1 << get_config()->http2_upstream_connection_window_bits)) { - if (LOG_ENABLED(INFO)) { - ULOG(INFO, upstream) - << "Flow control error on connection: " - << "recv_window_size=" - << spdylay_session_get_recv_data_length(session) << ", window_size=" - << (1 << get_config()->http2_upstream_connection_window_bits); - } - spdylay_session_fail_session(session, SPDYLAY_GOAWAY_PROTOCOL_ERROR); - return; - } - if (spdylay_session_get_stream_recv_data_length(session, stream_id) > - std::max(SPDYLAY_INITIAL_WINDOW_SIZE, - 1 << get_config()->http2_upstream_window_bits)) { - if (LOG_ENABLED(INFO)) { - ULOG(INFO, upstream) << "Flow control error: recv_window_size=" - << spdylay_session_get_stream_recv_data_length( - session, stream_id) - << ", initial_window_size=" - << (1 << get_config()->http2_upstream_window_bits); - } - upstream->rst_stream(downstream, SPDYLAY_FLOW_CONTROL_ERROR); - return; - } -} -} // namespace - -namespace { -void on_data_recv_callback(spdylay_session *session, uint8_t flags, - int32_t stream_id, int32_t length, void *user_data) { - auto upstream = static_cast(user_data); - auto downstream = static_cast( - spdylay_session_get_stream_user_data(session, stream_id)); - if (downstream && (flags & SPDYLAY_DATA_FLAG_FIN)) { - if (!downstream->validate_request_bodylen()) { - upstream->rst_stream(downstream, SPDYLAY_PROTOCOL_ERROR); - return; - } - - downstream->disable_upstream_rtimer(); - downstream->end_upload_data(); - downstream->set_request_state(Downstream::MSG_COMPLETE); - } -} -} // namespace - -namespace { -void on_ctrl_not_send_callback(spdylay_session *session, - spdylay_frame_type type, spdylay_frame *frame, - int error_code, void *user_data) { - auto upstream = static_cast(user_data); - if (LOG_ENABLED(INFO)) { - ULOG(INFO, upstream) << "Failed to send control frame type=" << type - << ", error_code=" << error_code << ":" - << spdylay_strerror(error_code); - } - if (type == SPDYLAY_SYN_REPLY && error_code != SPDYLAY_ERR_STREAM_CLOSED && - error_code != SPDYLAY_ERR_STREAM_CLOSING) { - // To avoid stream hanging around, issue RST_STREAM. - auto stream_id = frame->syn_reply.stream_id; - // TODO Could be always nullptr - auto downstream = static_cast( - spdylay_session_get_stream_user_data(session, stream_id)); - if (downstream) { - upstream->rst_stream(downstream, SPDYLAY_INTERNAL_ERROR); - } - } -} -} // namespace - -namespace { -void on_ctrl_recv_parse_error_callback(spdylay_session *session, - spdylay_frame_type type, - const uint8_t *head, size_t headlen, - const uint8_t *payload, - size_t payloadlen, int error_code, - void *user_data) { - auto upstream = static_cast(user_data); - if (LOG_ENABLED(INFO)) { - ULOG(INFO, upstream) << "Failed to parse received control frame. type=" - << type << ", error_code=" << error_code << ":" - << spdylay_strerror(error_code); - } -} -} // namespace - -namespace { -void on_unknown_ctrl_recv_callback(spdylay_session *session, - const uint8_t *head, size_t headlen, - const uint8_t *payload, size_t payloadlen, - void *user_data) { - auto upstream = static_cast(user_data); - if (LOG_ENABLED(INFO)) { - ULOG(INFO, upstream) << "Received unknown control frame."; - } -} -} // namespace - -namespace { -// Infer upstream RST_STREAM status code from downstream HTTP/2 -// error code. -uint32_t infer_upstream_rst_stream_status_code(uint32_t downstream_error_code) { - // Only propagate *_REFUSED_STREAM so that upstream client can - // resend request. - if (downstream_error_code == NGHTTP2_REFUSED_STREAM) { - return SPDYLAY_REFUSED_STREAM; - } else { - return SPDYLAY_INTERNAL_ERROR; - } -} -} // namespace - -SpdyUpstream::SpdyUpstream(uint16_t version, ClientHandler *handler) - : downstream_queue_( - get_config()->http2_proxy - ? get_config()->downstream_connections_per_host - : get_config()->downstream_proto == PROTO_HTTP - ? get_config()->downstream_connections_per_frontend - : 0, - !get_config()->http2_proxy), - handler_(handler), session_(nullptr) { - spdylay_session_callbacks callbacks; - memset(&callbacks, 0, sizeof(callbacks)); - callbacks.send_callback = send_callback; - callbacks.recv_callback = recv_callback; - callbacks.on_stream_close_callback = on_stream_close_callback; - callbacks.on_ctrl_recv_callback = on_ctrl_recv_callback; - callbacks.on_data_chunk_recv_callback = on_data_chunk_recv_callback; - callbacks.on_data_recv_callback = on_data_recv_callback; - callbacks.on_ctrl_not_send_callback = on_ctrl_not_send_callback; - callbacks.on_ctrl_recv_parse_error_callback = - on_ctrl_recv_parse_error_callback; - callbacks.on_unknown_ctrl_recv_callback = on_unknown_ctrl_recv_callback; - - int rv; - rv = spdylay_session_server_new(&session_, version, &callbacks, this); - assert(rv == 0); - - uint32_t max_buffer = 65536; - rv = spdylay_session_set_option(session_, - SPDYLAY_OPT_MAX_RECV_CTRL_FRAME_BUFFER, - &max_buffer, sizeof(max_buffer)); - assert(rv == 0); - - if (version >= SPDYLAY_PROTO_SPDY3) { - int val = 1; - flow_control_ = true; - initial_window_size_ = 1 << get_config()->http2_upstream_window_bits; - rv = spdylay_session_set_option( - session_, SPDYLAY_OPT_NO_AUTO_WINDOW_UPDATE2, &val, sizeof(val)); - assert(rv == 0); - } else { - flow_control_ = false; - initial_window_size_ = 0; - } - // TODO Maybe call from outside? - std::array entry; - entry[0].settings_id = SPDYLAY_SETTINGS_MAX_CONCURRENT_STREAMS; - entry[0].value = get_config()->http2_max_concurrent_streams; - entry[0].flags = SPDYLAY_ID_FLAG_SETTINGS_NONE; - - entry[1].settings_id = SPDYLAY_SETTINGS_INITIAL_WINDOW_SIZE; - entry[1].value = initial_window_size_; - entry[1].flags = SPDYLAY_ID_FLAG_SETTINGS_NONE; - - rv = spdylay_submit_settings(session_, SPDYLAY_FLAG_SETTINGS_NONE, - entry.data(), entry.size()); - assert(rv == 0); - - if (version >= SPDYLAY_PROTO_SPDY3_1 && - get_config()->http2_upstream_connection_window_bits > 16) { - int32_t delta = (1 << get_config()->http2_upstream_connection_window_bits) - - SPDYLAY_INITIAL_WINDOW_SIZE; - rv = spdylay_submit_window_update(session_, 0, delta); - assert(rv == 0); - } - - handler_->reset_upstream_read_timeout( - get_config()->http2_upstream_read_timeout); - - handler_->signal_write(); -} - -SpdyUpstream::~SpdyUpstream() { spdylay_session_del(session_); } - -int SpdyUpstream::on_read() { - int rv = 0; - - rv = spdylay_session_recv(session_); - if (rv < 0) { - if (rv != SPDYLAY_ERR_EOF) { - ULOG(ERROR, this) << "spdylay_session_recv() returned error: " - << spdylay_strerror(rv); - } - return rv; - } - - handler_->signal_write(); - - return 0; -} - -// After this function call, downstream may be deleted. -int SpdyUpstream::on_write() { - int rv = 0; - - rv = spdylay_session_send(session_); - if (rv != 0) { - ULOG(ERROR, this) << "spdylay_session_send() returned error: " - << spdylay_strerror(rv); - return rv; - } - - if (spdylay_session_want_read(session_) == 0 && - spdylay_session_want_write(session_) == 0 && - handler_->get_wb()->rleft() == 0) { - if (LOG_ENABLED(INFO)) { - ULOG(INFO, this) << "No more read/write for this SPDY session"; - } - return -1; - } - return 0; -} - -ClientHandler *SpdyUpstream::get_client_handler() const { return handler_; } - -int SpdyUpstream::downstream_read(DownstreamConnection *dconn) { - auto downstream = dconn->get_downstream(); - - if (downstream->get_request_state() == Downstream::STREAM_CLOSED) { - // If upstream SPDY stream was closed, we just close downstream, - // because there is no consumer now. Downstream connection is also - // closed in this case. - remove_downstream(downstream); - // downstrea was deleted - - return 0; - } - - if (downstream->get_response_state() == Downstream::MSG_RESET) { - // The downstream stream was reset (canceled). In this case, - // RST_STREAM to the upstream and delete downstream connection - // here. Deleting downstream will be taken place at - // on_stream_close_callback. - rst_stream(downstream, - infer_upstream_rst_stream_status_code( - downstream->get_response_rst_stream_error_code())); - downstream->pop_downstream_connection(); - dconn = nullptr; - } else if (downstream->get_response_state() == Downstream::MSG_BAD_HEADER) { - if (error_reply(downstream, 502) != 0) { - return -1; - } - downstream->pop_downstream_connection(); - // dconn was deleted - dconn = nullptr; - } else { - auto rv = downstream->on_read(); - if (rv == SHRPX_ERR_EOF) { - return downstream_eof(dconn); - } - if (rv != 0) { - if (rv != SHRPX_ERR_NETWORK) { - if (LOG_ENABLED(INFO)) { - DCLOG(INFO, dconn) << "HTTP parser failure"; - } - } - return downstream_error(dconn, Downstream::EVENT_ERROR); - } - // Detach downstream connection early so that it could be reused - // without hitting server's request timeout. - if (downstream->get_response_state() == Downstream::MSG_COMPLETE && - !downstream->get_response_connection_close()) { - // Keep-alive - downstream->detach_downstream_connection(); - } - } - - handler_->signal_write(); - // At this point, downstream may be deleted. - - return 0; -} - -int SpdyUpstream::downstream_write(DownstreamConnection *dconn) { - int rv; - rv = dconn->on_write(); - if (rv == SHRPX_ERR_NETWORK) { - return downstream_error(dconn, Downstream::EVENT_ERROR); - } - if (rv != 0) { - return -1; - } - return 0; -} - -int SpdyUpstream::downstream_eof(DownstreamConnection *dconn) { - auto downstream = dconn->get_downstream(); - - if (LOG_ENABLED(INFO)) { - DCLOG(INFO, dconn) << "EOF. stream_id=" << downstream->get_stream_id(); - } - if (downstream->get_request_state() == Downstream::STREAM_CLOSED) { - // If stream was closed already, we don't need to send reply at - // the first place. We can delete downstream. - remove_downstream(downstream); - // downstream was deleted - - return 0; - } - - // Delete downstream connection. If we don't delete it here, it will - // be pooled in on_stream_close_callback. - downstream->pop_downstream_connection(); - // dconn was deleted - dconn = nullptr; - // downstream wil be deleted in on_stream_close_callback. - if (downstream->get_response_state() == Downstream::HEADER_COMPLETE) { - // Server may indicate the end of the request by EOF - if (LOG_ENABLED(INFO)) { - ULOG(INFO, this) << "Downstream body was ended by EOF"; - } - downstream->set_response_state(Downstream::MSG_COMPLETE); - - // For tunneled connection, MSG_COMPLETE signals - // downstream_data_read_callback to send RST_STREAM after pending - // response body is sent. This is needed to ensure that RST_STREAM - // is sent after all pending data are sent. - on_downstream_body_complete(downstream); - } else if (downstream->get_response_state() != Downstream::MSG_COMPLETE) { - // If stream was not closed, then we set MSG_COMPLETE and let - // on_stream_close_callback delete downstream. - if (error_reply(downstream, 502) != 0) { - return -1; - } - } - handler_->signal_write(); - // At this point, downstream may be deleted. - return 0; -} - -int SpdyUpstream::downstream_error(DownstreamConnection *dconn, int events) { - auto downstream = dconn->get_downstream(); - - if (LOG_ENABLED(INFO)) { - if (events & Downstream::EVENT_ERROR) { - DCLOG(INFO, dconn) << "Downstream network/general error"; - } else { - DCLOG(INFO, dconn) << "Timeout"; - } - if (downstream->get_upgraded()) { - DCLOG(INFO, dconn) << "Note: this is tunnel connection"; - } - } - - if (downstream->get_request_state() == Downstream::STREAM_CLOSED) { - remove_downstream(downstream); - // downstream was deleted - - return 0; - } - - // Delete downstream connection. If we don't delete it here, it will - // be pooled in on_stream_close_callback. - downstream->pop_downstream_connection(); - // dconn was deleted - dconn = nullptr; - - if (downstream->get_response_state() == Downstream::MSG_COMPLETE) { - // For SSL tunneling, we issue RST_STREAM. For other types of - // stream, we don't have to do anything since response was - // complete. - if (downstream->get_upgraded()) { - // We want "NO_ERROR" error code but SPDY does not have such - // code for RST_STREAM. - rst_stream(downstream, SPDYLAY_INTERNAL_ERROR); - } - } else { - if (downstream->get_response_state() == Downstream::HEADER_COMPLETE) { - if (downstream->get_upgraded()) { - on_downstream_body_complete(downstream); - } else { - rst_stream(downstream, SPDYLAY_INTERNAL_ERROR); - } - } else { - unsigned int status; - if (events & Downstream::EVENT_TIMEOUT) { - status = 504; - } else { - status = 502; - } - if (error_reply(downstream, status) != 0) { - return -1; - } - } - downstream->set_response_state(Downstream::MSG_COMPLETE); - } - handler_->signal_write(); - // At this point, downstream may be deleted. - return 0; -} - -int SpdyUpstream::rst_stream(Downstream *downstream, int status_code) { - if (LOG_ENABLED(INFO)) { - ULOG(INFO, this) << "RST_STREAM stream_id=" << downstream->get_stream_id(); - } - int rv; - rv = spdylay_submit_rst_stream(session_, downstream->get_stream_id(), - status_code); - if (rv < SPDYLAY_ERR_FATAL) { - ULOG(FATAL, this) << "spdylay_submit_rst_stream() failed: " - << spdylay_strerror(rv); - DIE(); - } - return 0; -} - -namespace { -ssize_t spdy_data_read_callback(spdylay_session *session, int32_t stream_id, - uint8_t *buf, size_t length, int *eof, - spdylay_data_source *source, void *user_data) { - auto downstream = static_cast(source->ptr); - auto upstream = static_cast(downstream->get_upstream()); - auto body = downstream->get_response_buf(); - assert(body); - - auto dconn = downstream->get_downstream_connection(); - - if (body->rleft() == 0 && dconn && - downstream->get_response_state() != Downstream::MSG_COMPLETE) { - // Try to read more if buffer is empty. This will help small - // buffer and make priority handling a bit better. - if (upstream->downstream_read(dconn) != 0) { - return SPDYLAY_ERR_CALLBACK_FAILURE; - } - } - - auto nread = body->remove(buf, length); - auto body_empty = body->rleft() == 0; - - if (nread == 0 && - downstream->get_response_state() == Downstream::MSG_COMPLETE) { - if (!downstream->get_upgraded()) { - *eof = 1; - } else { - // For tunneling, issue RST_STREAM to finish the stream. - if (LOG_ENABLED(INFO)) { - ULOG(INFO, upstream) - << "RST_STREAM to tunneled stream stream_id=" << stream_id; - } - upstream->rst_stream( - downstream, infer_upstream_rst_stream_status_code( - downstream->get_response_rst_stream_error_code())); - } - } - - if (body_empty) { - downstream->disable_upstream_wtimer(); - } else { - downstream->reset_upstream_wtimer(); - } - - if (nread > 0 && downstream->resume_read(SHRPX_NO_BUFFER, nread) != 0) { - return SPDYLAY_ERR_CALLBACK_FAILURE; - } - - if (nread == 0 && *eof != 1) { - return SPDYLAY_ERR_DEFERRED; - } - - if (nread > 0) { - downstream->add_response_sent_bodylen(nread); - } - - return nread; -} -} // namespace - -int SpdyUpstream::error_reply(Downstream *downstream, - unsigned int status_code) { - int rv; - auto html = http::create_error_html(status_code); - downstream->set_response_http_status(status_code); - auto body = downstream->get_response_buf(); - body->append(html.c_str(), html.size()); - downstream->set_response_state(Downstream::MSG_COMPLETE); - - spdylay_data_provider data_prd; - data_prd.source.ptr = downstream; - data_prd.read_callback = spdy_data_read_callback; - - std::string content_length = util::utos(html.size()); - std::string status_string = http2::get_status_string(status_code); - const char *nv[] = {":status", status_string.c_str(), - ":version", "http/1.1", - "content-type", "text/html; charset=UTF-8", - "server", get_config()->server_name, - "content-length", content_length.c_str(), - nullptr}; - - rv = spdylay_submit_response(session_, downstream->get_stream_id(), nv, - &data_prd); - if (rv < SPDYLAY_ERR_FATAL) { - ULOG(FATAL, this) << "spdylay_submit_response() failed: " - << spdylay_strerror(rv); - return -1; - } - - return 0; -} - -Downstream *SpdyUpstream::add_pending_downstream(int32_t stream_id, - int32_t priority) { - auto downstream = make_unique(this, handler_->get_mcpool(), - stream_id, priority); - spdylay_session_set_stream_user_data(session_, stream_id, downstream.get()); - auto res = downstream.get(); - - downstream_queue_.add_pending(std::move(downstream)); - - return res; -} - -void SpdyUpstream::remove_downstream(Downstream *downstream) { - if (downstream->accesslog_ready()) { - handler_->write_accesslog(downstream); - } - - spdylay_session_set_stream_user_data(session_, downstream->get_stream_id(), - nullptr); - - auto next_downstream = downstream_queue_.remove_and_get_blocked(downstream); - - if (next_downstream) { - initiate_downstream(next_downstream); - } -} - -// WARNING: Never call directly or indirectly spdylay_session_send or -// spdylay_session_recv. These calls may delete downstream. -int SpdyUpstream::on_downstream_header_complete(Downstream *downstream) { - if (downstream->get_non_final_response()) { - // SPDY does not support non-final response. We could send it - // with HEADERS and final response in SYN_REPLY, but it is not - // official way. - downstream->clear_response_headers(); - - return 0; - } - - if (LOG_ENABLED(INFO)) { - DLOG(INFO, downstream) << "HTTP response header completed"; - } - - if (!get_config()->http2_proxy && !get_config()->client_proxy && - !get_config()->no_location_rewrite) { - downstream->rewrite_location_response_header( - downstream->get_request_http2_scheme()); - } - size_t nheader = downstream->get_response_headers().size(); - // 8 means server, :status, :version and possible via header field. - auto nv = make_unique( - nheader * 2 + 8 + get_config()->add_response_headers.size() * 2 + 1); - - size_t hdidx = 0; - std::string via_value; - std::string status_string = - http2::get_status_string(downstream->get_response_http_status()); - nv[hdidx++] = ":status"; - nv[hdidx++] = status_string.c_str(); - nv[hdidx++] = ":version"; - nv[hdidx++] = "HTTP/1.1"; - for (auto &hd : downstream->get_response_headers()) { - if (hd.name.empty() || hd.name.c_str()[0] == ':') { - continue; - } - switch (hd.token) { - case http2::HD_CONNECTION: - case http2::HD_KEEP_ALIVE: - case http2::HD_PROXY_CONNECTION: - case http2::HD_TRANSFER_ENCODING: - case http2::HD_VIA: - case http2::HD_SERVER: - continue; - } - - nv[hdidx++] = hd.name.c_str(); - nv[hdidx++] = hd.value.c_str(); - } - - if (!get_config()->http2_proxy && !get_config()->client_proxy) { - nv[hdidx++] = "server"; - nv[hdidx++] = get_config()->server_name; - } else { - auto server = downstream->get_response_header(http2::HD_SERVER); - if (server) { - nv[hdidx++] = "server"; - nv[hdidx++] = server->value.c_str(); - } - } - - auto via = downstream->get_response_header(http2::HD_VIA); - if (get_config()->no_via) { - if (via) { - nv[hdidx++] = "via"; - nv[hdidx++] = via->value.c_str(); - } - } else { - if (via) { - via_value = via->value; - via_value += ", "; - } - via_value += http::create_via_header_value( - downstream->get_response_major(), downstream->get_response_minor()); - nv[hdidx++] = "via"; - nv[hdidx++] = via_value.c_str(); - } - - for (auto &p : get_config()->add_response_headers) { - nv[hdidx++] = p.first.c_str(); - nv[hdidx++] = p.second.c_str(); - } - - nv[hdidx++] = 0; - if (LOG_ENABLED(INFO)) { - std::stringstream ss; - for (size_t i = 0; nv[i]; i += 2) { - ss << TTY_HTTP_HD << nv[i] << TTY_RST << ": " << nv[i + 1] << "\n"; - } - ULOG(INFO, this) << "HTTP response headers. stream_id=" - << downstream->get_stream_id() << "\n" << ss.str(); - } - spdylay_data_provider data_prd; - data_prd.source.ptr = downstream; - data_prd.read_callback = spdy_data_read_callback; - - int rv; - rv = spdylay_submit_response(session_, downstream->get_stream_id(), nv.get(), - &data_prd); - if (rv != 0) { - ULOG(FATAL, this) << "spdylay_submit_response() failed"; - return -1; - } - - return 0; -} - -// WARNING: Never call directly or indirectly spdylay_session_send or -// spdylay_session_recv. These calls may delete downstream. -int SpdyUpstream::on_downstream_body(Downstream *downstream, - const uint8_t *data, size_t len, - bool flush) { - auto body = downstream->get_response_buf(); - body->append(data, len); - - if (flush) { - spdylay_session_resume_data(session_, downstream->get_stream_id()); - - downstream->ensure_upstream_wtimer(); - } - - return 0; -} - -// WARNING: Never call directly or indirectly spdylay_session_send or -// spdylay_session_recv. These calls may delete downstream. -int SpdyUpstream::on_downstream_body_complete(Downstream *downstream) { - if (LOG_ENABLED(INFO)) { - DLOG(INFO, downstream) << "HTTP response completed"; - } - - if (!downstream->validate_response_bodylen()) { - rst_stream(downstream, SPDYLAY_PROTOCOL_ERROR); - downstream->set_response_connection_close(true); - return 0; - } - - spdylay_session_resume_data(session_, downstream->get_stream_id()); - downstream->ensure_upstream_wtimer(); - - return 0; -} - -bool SpdyUpstream::get_flow_control() const { return flow_control_; } - -void SpdyUpstream::pause_read(IOCtrlReason reason) {} - -int SpdyUpstream::resume_read(IOCtrlReason reason, Downstream *downstream, - size_t consumed) { - if (get_flow_control()) { - assert(downstream->get_request_datalen() >= consumed); - - if (consume(downstream->get_stream_id(), consumed) != 0) { - return -1; - } - - downstream->dec_request_datalen(consumed); - } - - handler_->signal_write(); - return 0; -} - -int SpdyUpstream::on_downstream_abort_request(Downstream *downstream, - unsigned int status_code) { - int rv; - - rv = error_reply(downstream, status_code); - - if (rv != 0) { - return -1; - } - - handler_->signal_write(); - return 0; -} - -int SpdyUpstream::consume(int32_t stream_id, size_t len) { - int rv; - - rv = spdylay_session_consume(session_, stream_id, len); - - if (rv != 0) { - ULOG(WARN, this) << "spdylay_session_consume() returned error: " - << spdylay_strerror(rv); - return -1; - } - - return 0; -} - -int SpdyUpstream::on_timeout(Downstream *downstream) { - if (LOG_ENABLED(INFO)) { - ULOG(INFO, this) << "Stream timeout stream_id=" - << downstream->get_stream_id(); - } - - rst_stream(downstream, SPDYLAY_INTERNAL_ERROR); - - return 0; -} - -void SpdyUpstream::on_handler_delete() { - for (auto d = downstream_queue_.get_downstreams(); d; d = d->dlnext) { - if (d->get_dispatch_state() == Downstream::DISPATCH_ACTIVE && - d->accesslog_ready()) { - handler_->write_accesslog(d); - } - } -} - -int SpdyUpstream::on_downstream_reset(bool no_retry) { - int rv; - - for (auto downstream = downstream_queue_.get_downstreams(); downstream; - downstream = downstream->dlnext) { - if (downstream->get_dispatch_state() != Downstream::DISPATCH_ACTIVE) { - continue; - } - - if (!downstream->request_submission_ready()) { - rst_stream(downstream, SPDYLAY_INTERNAL_ERROR); - downstream->pop_downstream_connection(); - continue; - } - - downstream->pop_downstream_connection(); - - downstream->add_retry(); - - if (no_retry || downstream->no_more_retry()) { - goto fail; - } - - // downstream connection is clean; we can retry with new - // downstream connection. - - rv = downstream->attach_downstream_connection( - handler_->get_downstream_connection()); - if (rv != 0) { - goto fail; - } - - continue; - - fail: - if (on_downstream_abort_request(downstream, 503) != 0) { - return -1; - } - downstream->pop_downstream_connection(); - } - - handler_->signal_write(); - - return 0; -} - -} // namespace shrpx diff --git a/src/shrpx_spdy_upstream.h b/src/shrpx_spdy_upstream.h deleted file mode 100644 index 2e4aec4..0000000 --- a/src/shrpx_spdy_upstream.h +++ /dev/null @@ -1,94 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2012 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#ifndef SHRPX_SPDY_UPSTREAM_H -#define SHRPX_SPDY_UPSTREAM_H - -#include "shrpx.h" - -#include - -#include - -#include - -#include "shrpx_upstream.h" -#include "shrpx_downstream_queue.h" -#include "memchunk.h" -#include "util.h" - -namespace shrpx { - -class ClientHandler; - -class SpdyUpstream : public Upstream { -public: - SpdyUpstream(uint16_t version, ClientHandler *handler); - virtual ~SpdyUpstream(); - virtual int on_read(); - virtual int on_write(); - virtual int on_timeout(Downstream *downstream); - virtual int on_downstream_abort_request(Downstream *downstream, - unsigned int status_code); - virtual ClientHandler *get_client_handler() const; - virtual int downstream_read(DownstreamConnection *dconn); - virtual int downstream_write(DownstreamConnection *dconn); - virtual int downstream_eof(DownstreamConnection *dconn); - virtual int downstream_error(DownstreamConnection *dconn, int events); - Downstream *add_pending_downstream(int32_t stream_id, int32_t priority); - void remove_downstream(Downstream *downstream); - - int rst_stream(Downstream *downstream, int status_code); - int error_reply(Downstream *downstream, unsigned int status_code); - - virtual void pause_read(IOCtrlReason reason); - virtual int resume_read(IOCtrlReason reason, Downstream *downstream, - size_t consumed); - - virtual int on_downstream_header_complete(Downstream *downstream); - virtual int on_downstream_body(Downstream *downstream, const uint8_t *data, - size_t len, bool flush); - virtual int on_downstream_body_complete(Downstream *downstream); - - virtual void on_handler_delete(); - virtual int on_downstream_reset(bool no_retry); - - bool get_flow_control() const; - - int consume(int32_t stream_id, size_t len); - - void start_downstream(Downstream *downstream); - void initiate_downstream(Downstream *downstream); - -private: - DownstreamQueue downstream_queue_; - ClientHandler *handler_; - spdylay_session *session_; - int32_t initial_window_size_; - bool flow_control_; -}; - -} // namespace shrpx - -#endif // SHRPX_SPDY_UPSTREAM_H diff --git a/src/shrpx_ssl.cc b/src/shrpx_ssl.cc deleted file mode 100644 index 3d3687e..0000000 --- a/src/shrpx_ssl.cc +++ /dev/null @@ -1,1028 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2012 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#include "shrpx_ssl.h" - -#ifdef HAVE_SYS_SOCKET_H -#include -#endif // HAVE_SYS_SOCKET_H -#ifdef HAVE_NETDB_H -#include -#endif // HAVE_NETDB_H -#include -#include -#include - -#include -#include - -#include -#include -#include -#include - -#include - -#ifdef HAVE_SPDYLAY -#include -#endif // HAVE_SPDYLAY - -#include "shrpx_log.h" -#include "shrpx_client_handler.h" -#include "shrpx_config.h" -#include "shrpx_worker.h" -#include "shrpx_downstream_connection_pool.h" -#include "util.h" -#include "ssl.h" -#include "template.h" - -using namespace nghttp2; - -namespace shrpx { - -namespace ssl { - -namespace { -int next_proto_cb(SSL *s, const unsigned char **data, unsigned int *len, - void *arg) { - auto &prefs = get_config()->alpn_prefs; - *data = prefs.data(); - *len = prefs.size(); - return SSL_TLSEXT_ERR_OK; -} -} // namespace - -namespace { -int verify_callback(int preverify_ok, X509_STORE_CTX *ctx) { - if (!preverify_ok) { - int err = X509_STORE_CTX_get_error(ctx); - int depth = X509_STORE_CTX_get_error_depth(ctx); - LOG(ERROR) << "client certificate verify error:num=" << err << ":" - << X509_verify_cert_error_string(err) << ":depth=" << depth; - } - return preverify_ok; -} -} // namespace - -std::vector set_alpn_prefs(const std::vector &protos) { - size_t len = 0; - - for (auto proto : protos) { - auto n = strlen(proto); - - if (n > 255) { - LOG(FATAL) << "Too long ALPN identifier: " << n; - DIE(); - } - - len += 1 + n; - } - - if (len > (1 << 16) - 1) { - LOG(FATAL) << "Too long ALPN identifier list: " << len; - DIE(); - } - - auto out = std::vector(len); - auto ptr = out.data(); - - for (auto proto : protos) { - auto proto_len = strlen(proto); - - *ptr++ = proto_len; - memcpy(ptr, proto, proto_len); - ptr += proto_len; - } - - return out; -} - -namespace { -int ssl_pem_passwd_cb(char *buf, int size, int rwflag, void *user_data) { - auto config = static_cast(user_data); - int len = (int)strlen(config->private_key_passwd.get()); - if (size < len + 1) { - LOG(ERROR) << "ssl_pem_passwd_cb: buf is too small " << size; - return 0; - } - // Copy string including last '\0'. - memcpy(buf, config->private_key_passwd.get(), len + 1); - return len; -} -} // namespace - -namespace { -int servername_callback(SSL *ssl, int *al, void *arg) { - auto handler = static_cast(SSL_get_app_data(ssl)); - auto worker = handler->get_worker(); - auto cert_tree = worker->get_cert_lookup_tree(); - if (cert_tree) { - const char *hostname = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name); - if (hostname) { - auto ssl_ctx = cert_tree->lookup(hostname, strlen(hostname)); - if (ssl_ctx) { - SSL_set_SSL_CTX(ssl, ssl_ctx); - } - } - } - return SSL_TLSEXT_ERR_OK; -} -} // namespace - -namespace { -int ocsp_resp_cb(SSL *ssl, void *arg) { - auto ssl_ctx = SSL_get_SSL_CTX(ssl); - auto tls_ctx_data = - static_cast(SSL_CTX_get_app_data(ssl_ctx)); - { - std::lock_guard g(tls_ctx_data->mu); - auto &data = tls_ctx_data->ocsp_data; - - if (!data.empty()) { - auto buf = static_cast( - CRYPTO_malloc(data.size(), __FILE__, __LINE__)); - - if (!buf) { - return SSL_TLSEXT_ERR_OK; - } - - std::copy(std::begin(data), std::end(data), buf); - - SSL_set_tlsext_status_ocsp_resp(ssl, buf, data.size()); - } - } - return SSL_TLSEXT_ERR_OK; -} -} // namespace - -namespace { -int ticket_key_cb(SSL *ssl, unsigned char *key_name, unsigned char *iv, - EVP_CIPHER_CTX *ctx, HMAC_CTX *hctx, int enc) { - auto handler = static_cast(SSL_get_app_data(ssl)); - auto worker = handler->get_worker(); - const auto &ticket_keys = worker->get_ticket_keys(); - - if (!ticket_keys) { - // No ticket keys available. - return -1; - } - - auto &keys = ticket_keys->keys; - assert(!keys.empty()); - - if (enc) { - if (RAND_bytes(iv, EVP_MAX_IV_LENGTH) == 0) { - if (LOG_ENABLED(INFO)) { - CLOG(INFO, handler) << "session ticket key: RAND_bytes failed"; - } - return -1; - } - - auto &key = keys[0]; - - if (LOG_ENABLED(INFO)) { - CLOG(INFO, handler) << "encrypt session ticket key: " - << util::format_hex(key.name, 16); - } - - memcpy(key_name, key.name, sizeof(key.name)); - - EVP_EncryptInit_ex(ctx, EVP_aes_128_cbc(), nullptr, key.aes_key, iv); - HMAC_Init_ex(hctx, key.hmac_key, sizeof(key.hmac_key), EVP_sha256(), - nullptr); - return 1; - } - - size_t i; - for (i = 0; i < keys.size(); ++i) { - auto &key = keys[0]; - if (memcmp(key.name, key_name, sizeof(key.name)) == 0) { - break; - } - } - - if (i == keys.size()) { - if (LOG_ENABLED(INFO)) { - CLOG(INFO, handler) << "session ticket key " - << util::format_hex(key_name, 16) << " not found"; - } - return 0; - } - - if (LOG_ENABLED(INFO)) { - CLOG(INFO, handler) << "decrypt session ticket key: " - << util::format_hex(key_name, 16); - } - - auto &key = keys[i]; - HMAC_Init_ex(hctx, key.hmac_key, sizeof(key.hmac_key), EVP_sha256(), nullptr); - EVP_DecryptInit_ex(ctx, EVP_aes_128_cbc(), nullptr, key.aes_key, iv); - - return i == 0 ? 1 : 2; -} -} // namespace - -namespace { -void info_callback(const SSL *ssl, int where, int ret) { - // To mitigate possible DOS attack using lots of renegotiations, we - // disable renegotiation. Since OpenSSL does not provide an easy way - // to disable it, we check that renegotiation is started in this - // callback. - if (where & SSL_CB_HANDSHAKE_START) { - auto conn = static_cast(SSL_get_app_data(ssl)); - if (conn && conn->tls.initial_handshake_done) { - // We only set SSL_get_app_data for ClientHandler for now. - auto handler = static_cast(conn->data); - if (LOG_ENABLED(INFO)) { - CLOG(INFO, handler) << "TLS renegotiation started"; - } - handler->start_immediate_shutdown(); - } - } -} -} // namespace - -#if OPENSSL_VERSION_NUMBER >= 0x10002000L -namespace { -int alpn_select_proto_cb(SSL *ssl, const unsigned char **out, - unsigned char *outlen, const unsigned char *in, - unsigned int inlen, void *arg) { - // We assume that get_config()->npn_list contains ALPN protocol - // identifier sorted by preference order. So we just break when we - // found the first overlap. - for (auto target_proto_id : get_config()->npn_list) { - auto target_proto_len = - strlen(reinterpret_cast(target_proto_id)); - - for (auto p = in, end = in + inlen; p < end;) { - auto proto_id = p + 1; - auto proto_len = *p; - - if (proto_id + proto_len <= end && target_proto_len == proto_len && - memcmp(target_proto_id, proto_id, proto_len) == 0) { - - *out = reinterpret_cast(proto_id); - *outlen = proto_len; - - return SSL_TLSEXT_ERR_OK; - } - - p += 1 + proto_len; - } - } - - return SSL_TLSEXT_ERR_NOACK; -} -} // namespace -#endif // OPENSSL_VERSION_NUMBER >= 0x10002000L - -namespace { -const char *tls_names[] = {"TLSv1.2", "TLSv1.1", "TLSv1.0"}; -const size_t tls_namelen = array_size(tls_names); -const long int tls_masks[] = {SSL_OP_NO_TLSv1_2, SSL_OP_NO_TLSv1_1, - SSL_OP_NO_TLSv1}; -} // namespace - -long int create_tls_proto_mask(const std::vector &tls_proto_list) { - long int res = 0; - - for (size_t i = 0; i < tls_namelen; ++i) { - size_t j; - for (j = 0; j < tls_proto_list.size(); ++j) { - if (util::strieq(tls_names[i], tls_proto_list[j])) { - break; - } - } - if (j == tls_proto_list.size()) { - res |= tls_masks[i]; - } - } - return res; -} - -SSL_CTX *create_ssl_context(const char *private_key_file, - const char *cert_file) { - auto ssl_ctx = SSL_CTX_new(SSLv23_server_method()); - if (!ssl_ctx) { - LOG(FATAL) << ERR_error_string(ERR_get_error(), nullptr); - DIE(); - } - - SSL_CTX_set_options( - ssl_ctx, - SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_COMPRESSION | - SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION | - SSL_OP_SINGLE_ECDH_USE | SSL_OP_SINGLE_DH_USE | - SSL_OP_CIPHER_SERVER_PREFERENCE | get_config()->tls_proto_mask); - - const unsigned char sid_ctx[] = "shrpx"; - SSL_CTX_set_session_id_context(ssl_ctx, sid_ctx, sizeof(sid_ctx) - 1); - SSL_CTX_set_session_cache_mode(ssl_ctx, SSL_SESS_CACHE_SERVER); - - const char *ciphers; - if (get_config()->ciphers) { - ciphers = get_config()->ciphers.get(); - } else { - ciphers = nghttp2::ssl::DEFAULT_CIPHER_LIST; - } - - if (SSL_CTX_set_cipher_list(ssl_ctx, ciphers) == 0) { - LOG(FATAL) << "SSL_CTX_set_cipher_list " << ciphers - << " failed: " << ERR_error_string(ERR_get_error(), nullptr); - DIE(); - } - -#ifndef OPENSSL_NO_EC - - // Disabled SSL_CTX_set_ecdh_auto, because computational cost of - // chosen curve is much higher than P-256. - - // #if OPENSSL_VERSION_NUMBER >= 0x10002000L - // SSL_CTX_set_ecdh_auto(ssl_ctx, 1); - // #else // OPENSSL_VERSION_NUBMER < 0x10002000L - // Use P-256, which is sufficiently secure at the time of this - // writing. - auto ecdh = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1); - if (ecdh == nullptr) { - LOG(FATAL) << "EC_KEY_new_by_curv_name failed: " - << ERR_error_string(ERR_get_error(), nullptr); - DIE(); - } - SSL_CTX_set_tmp_ecdh(ssl_ctx, ecdh); - EC_KEY_free(ecdh); -// #endif // OPENSSL_VERSION_NUBMER < 0x10002000L - -#endif // OPENSSL_NO_EC - - if (get_config()->dh_param_file) { - // Read DH parameters from file - auto bio = BIO_new_file(get_config()->dh_param_file.get(), "r"); - if (bio == nullptr) { - LOG(FATAL) << "BIO_new_file() failed: " - << ERR_error_string(ERR_get_error(), nullptr); - DIE(); - } - auto dh = PEM_read_bio_DHparams(bio, nullptr, nullptr, nullptr); - if (dh == nullptr) { - LOG(FATAL) << "PEM_read_bio_DHparams() failed: " - << ERR_error_string(ERR_get_error(), nullptr); - DIE(); - } - SSL_CTX_set_tmp_dh(ssl_ctx, dh); - DH_free(dh); - BIO_free(bio); - } - - SSL_CTX_set_mode(ssl_ctx, SSL_MODE_AUTO_RETRY); - SSL_CTX_set_mode(ssl_ctx, SSL_MODE_RELEASE_BUFFERS); - if (get_config()->private_key_passwd) { - SSL_CTX_set_default_passwd_cb(ssl_ctx, ssl_pem_passwd_cb); - SSL_CTX_set_default_passwd_cb_userdata(ssl_ctx, (void *)get_config()); - } - if (SSL_CTX_use_PrivateKey_file(ssl_ctx, private_key_file, - SSL_FILETYPE_PEM) != 1) { - LOG(FATAL) << "SSL_CTX_use_PrivateKey_file failed: " - << ERR_error_string(ERR_get_error(), nullptr); - DIE(); - } - if (SSL_CTX_use_certificate_chain_file(ssl_ctx, cert_file) != 1) { - LOG(FATAL) << "SSL_CTX_use_certificate_file failed: " - << ERR_error_string(ERR_get_error(), nullptr); - DIE(); - } - if (SSL_CTX_check_private_key(ssl_ctx) != 1) { - LOG(FATAL) << "SSL_CTX_check_private_key failed: " - << ERR_error_string(ERR_get_error(), nullptr); - DIE(); - } - if (get_config()->verify_client) { - if (get_config()->verify_client_cacert) { - if (SSL_CTX_load_verify_locations( - ssl_ctx, get_config()->verify_client_cacert.get(), nullptr) != - 1) { - - LOG(FATAL) << "Could not load trusted ca certificates from " - << get_config()->verify_client_cacert.get() << ": " - << ERR_error_string(ERR_get_error(), nullptr); - DIE(); - } - // It is heard that SSL_CTX_load_verify_locations() may leave - // error even though it returns success. See - // http://forum.nginx.org/read.php?29,242540 - ERR_clear_error(); - auto list = - SSL_load_client_CA_file(get_config()->verify_client_cacert.get()); - if (!list) { - LOG(FATAL) << "Could not load ca certificates from " - << get_config()->verify_client_cacert.get() << ": " - << ERR_error_string(ERR_get_error(), nullptr); - DIE(); - } - SSL_CTX_set_client_CA_list(ssl_ctx, list); - } - SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE | - SSL_VERIFY_FAIL_IF_NO_PEER_CERT, - verify_callback); - } - SSL_CTX_set_tlsext_servername_callback(ssl_ctx, servername_callback); - SSL_CTX_set_tlsext_ticket_key_cb(ssl_ctx, ticket_key_cb); - SSL_CTX_set_tlsext_status_cb(ssl_ctx, ocsp_resp_cb); - SSL_CTX_set_info_callback(ssl_ctx, info_callback); - - // NPN advertisement - SSL_CTX_set_next_protos_advertised_cb(ssl_ctx, next_proto_cb, nullptr); -#if OPENSSL_VERSION_NUMBER >= 0x10002000L - // ALPN selection callback - SSL_CTX_set_alpn_select_cb(ssl_ctx, alpn_select_proto_cb, nullptr); -#endif // OPENSSL_VERSION_NUMBER >= 0x10002000L - - auto tls_ctx_data = new TLSContextData(); - tls_ctx_data->cert_file = cert_file; - - SSL_CTX_set_app_data(ssl_ctx, tls_ctx_data); - - return ssl_ctx; -} - -namespace { -int select_next_proto_cb(SSL *ssl, unsigned char **out, unsigned char *outlen, - const unsigned char *in, unsigned int inlen, - void *arg) { - if (!util::select_h2(const_cast(out), outlen, in, - inlen)) { - return SSL_TLSEXT_ERR_NOACK; - } - - return SSL_TLSEXT_ERR_OK; -} -} // namespace - -SSL_CTX *create_ssl_client_context() { - auto ssl_ctx = SSL_CTX_new(SSLv23_client_method()); - if (!ssl_ctx) { - LOG(FATAL) << ERR_error_string(ERR_get_error(), nullptr); - DIE(); - } - SSL_CTX_set_options(ssl_ctx, - SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | - SSL_OP_NO_COMPRESSION | - SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION | - get_config()->tls_proto_mask); - - const char *ciphers; - if (get_config()->ciphers) { - ciphers = get_config()->ciphers.get(); - } else { - ciphers = "HIGH:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!3DES:!MD5:!PSK"; - } - if (SSL_CTX_set_cipher_list(ssl_ctx, ciphers) == 0) { - LOG(FATAL) << "SSL_CTX_set_cipher_list " << ciphers - << " failed: " << ERR_error_string(ERR_get_error(), nullptr); - DIE(); - } - - SSL_CTX_set_mode(ssl_ctx, SSL_MODE_AUTO_RETRY); - SSL_CTX_set_mode(ssl_ctx, SSL_MODE_RELEASE_BUFFERS); - - if (SSL_CTX_set_default_verify_paths(ssl_ctx) != 1) { - LOG(WARN) << "Could not load system trusted ca certificates: " - << ERR_error_string(ERR_get_error(), nullptr); - } - - if (get_config()->cacert) { - if (SSL_CTX_load_verify_locations(ssl_ctx, get_config()->cacert.get(), - nullptr) != 1) { - - LOG(FATAL) << "Could not load trusted ca certificates from " - << get_config()->cacert.get() << ": " - << ERR_error_string(ERR_get_error(), nullptr); - DIE(); - } - } - - if (get_config()->client_private_key_file) { - if (SSL_CTX_use_PrivateKey_file(ssl_ctx, - get_config()->client_private_key_file.get(), - SSL_FILETYPE_PEM) != 1) { - LOG(FATAL) << "Could not load client private key from " - << get_config()->client_private_key_file.get() << ": " - << ERR_error_string(ERR_get_error(), nullptr); - DIE(); - } - } - if (get_config()->client_cert_file) { - if (SSL_CTX_use_certificate_chain_file( - ssl_ctx, get_config()->client_cert_file.get()) != 1) { - - LOG(FATAL) << "Could not load client certificate from " - << get_config()->client_cert_file.get() << ": " - << ERR_error_string(ERR_get_error(), nullptr); - DIE(); - } - } - // NPN selection callback - SSL_CTX_set_next_proto_select_cb(ssl_ctx, select_next_proto_cb, nullptr); - -#if OPENSSL_VERSION_NUMBER >= 0x10002000L - // ALPN advertisement; We only advertise HTTP/2 - auto proto_list = util::get_default_alpn(); - - SSL_CTX_set_alpn_protos(ssl_ctx, proto_list.data(), proto_list.size()); -#endif // OPENSSL_VERSION_NUMBER >= 0x10002000L - - return ssl_ctx; -} - -ClientHandler *accept_connection(Worker *worker, int fd, sockaddr *addr, - int addrlen) { - char host[NI_MAXHOST]; - char service[NI_MAXSERV]; - int rv; - rv = getnameinfo(addr, addrlen, host, sizeof(host), service, sizeof(service), - NI_NUMERICHOST | NI_NUMERICSERV); - if (rv != 0) { - LOG(ERROR) << "getnameinfo() failed: " << gai_strerror(rv); - - return nullptr; - } - - int val = 1; - rv = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, reinterpret_cast(&val), - sizeof(val)); - if (rv == -1) { - LOG(WARN) << "Setting option TCP_NODELAY failed: errno=" << errno; - } - SSL *ssl = nullptr; - auto ssl_ctx = worker->get_sv_ssl_ctx(); - if (ssl_ctx) { - ssl = SSL_new(ssl_ctx); - if (!ssl) { - LOG(ERROR) << "SSL_new() failed: " << ERR_error_string(ERR_get_error(), - nullptr); - return nullptr; - } - - if (SSL_set_fd(ssl, fd) == 0) { - LOG(ERROR) << "SSL_set_fd() failed: " << ERR_error_string(ERR_get_error(), - nullptr); - SSL_free(ssl); - return nullptr; - } - - SSL_set_accept_state(ssl); - } - - return new ClientHandler(worker, fd, ssl, host, service); -} - -namespace { -bool tls_hostname_match(const char *pattern, const char *hostname) { - const char *ptWildcard = strchr(pattern, '*'); - if (ptWildcard == nullptr) { - return util::strieq(pattern, hostname); - } - const char *ptLeftLabelEnd = strchr(pattern, '.'); - bool wildcardEnabled = true; - // Do case-insensitive match. At least 2 dots are required to enable - // wildcard match. Also wildcard must be in the left-most label. - // Don't attempt to match a presented identifier where the wildcard - // character is embedded within an A-label. - if (ptLeftLabelEnd == 0 || strchr(ptLeftLabelEnd + 1, '.') == 0 || - ptLeftLabelEnd < ptWildcard || util::istartsWith(pattern, "xn--")) { - wildcardEnabled = false; - } - if (!wildcardEnabled) { - return util::strieq(pattern, hostname); - } - const char *hnLeftLabelEnd = strchr(hostname, '.'); - if (hnLeftLabelEnd == 0 || !util::strieq(ptLeftLabelEnd, hnLeftLabelEnd)) { - return false; - } - // Perform wildcard match. Here '*' must match at least one - // character. - if (hnLeftLabelEnd - hostname < ptLeftLabelEnd - pattern) { - return false; - } - return util::istartsWith(hostname, hnLeftLabelEnd, pattern, ptWildcard) && - util::iendsWith(hostname, hnLeftLabelEnd, ptWildcard + 1, - ptLeftLabelEnd); -} -} // namespace - -namespace { -int verify_hostname(const char *hostname, const sockaddr_union *su, - size_t salen, const std::vector &dns_names, - const std::vector &ip_addrs, - const std::string &common_name) { - if (util::numeric_host(hostname)) { - if (ip_addrs.empty()) { - return util::strieq(common_name.c_str(), hostname) ? 0 : -1; - } - const void *saddr; - switch (su->storage.ss_family) { - case AF_INET: - saddr = &su->in.sin_addr; - break; - case AF_INET6: - saddr = &su->in6.sin6_addr; - break; - default: - return -1; - } - for (size_t i = 0; i < ip_addrs.size(); ++i) { - if (salen == ip_addrs[i].size() && - memcmp(saddr, ip_addrs[i].c_str(), salen) == 0) { - return 0; - } - } - } else { - if (dns_names.empty()) { - return tls_hostname_match(common_name.c_str(), hostname) ? 0 : -1; - } - for (size_t i = 0; i < dns_names.size(); ++i) { - if (tls_hostname_match(dns_names[i].c_str(), hostname)) { - return 0; - } - } - } - return -1; -} -} // namespace - -void get_altnames(X509 *cert, std::vector &dns_names, - std::vector &ip_addrs, - std::string &common_name) { - GENERAL_NAMES *altnames = static_cast( - X509_get_ext_d2i(cert, NID_subject_alt_name, nullptr, nullptr)); - if (altnames) { - auto altnames_deleter = defer(GENERAL_NAMES_free, altnames); - size_t n = sk_GENERAL_NAME_num(altnames); - for (size_t i = 0; i < n; ++i) { - const GENERAL_NAME *altname = sk_GENERAL_NAME_value(altnames, i); - if (altname->type == GEN_DNS) { - const char *name; - name = reinterpret_cast(ASN1_STRING_data(altname->d.ia5)); - if (!name) { - continue; - } - size_t len = ASN1_STRING_length(altname->d.ia5); - if (std::find(name, name + len, '\0') != name + len) { - // Embedded NULL is not permitted. - continue; - } - dns_names.push_back(std::string(name, len)); - } else if (altname->type == GEN_IPADD) { - const unsigned char *ip_addr = altname->d.iPAddress->data; - if (!ip_addr) { - continue; - } - size_t len = altname->d.iPAddress->length; - ip_addrs.push_back( - std::string(reinterpret_cast(ip_addr), len)); - } - } - } - X509_NAME *subjectname = X509_get_subject_name(cert); - if (!subjectname) { - LOG(WARN) << "Could not get X509 name object from the certificate."; - return; - } - int lastpos = -1; - while (1) { - lastpos = X509_NAME_get_index_by_NID(subjectname, NID_commonName, lastpos); - if (lastpos == -1) { - break; - } - X509_NAME_ENTRY *entry = X509_NAME_get_entry(subjectname, lastpos); - unsigned char *out; - int outlen = ASN1_STRING_to_UTF8(&out, X509_NAME_ENTRY_get_data(entry)); - if (outlen < 0) { - continue; - } - if (std::find(out, out + outlen, '\0') != out + outlen) { - // Embedded NULL is not permitted. - continue; - } - common_name.assign(&out[0], &out[outlen]); - OPENSSL_free(out); - break; - } -} - -int check_cert(SSL *ssl) { - auto cert = SSL_get_peer_certificate(ssl); - if (!cert) { - LOG(ERROR) << "No certificate found"; - return -1; - } - auto cert_deleter = defer(X509_free, cert); - long verify_res = SSL_get_verify_result(ssl); - if (verify_res != X509_V_OK) { - LOG(ERROR) << "Certificate verification failed: " - << X509_verify_cert_error_string(verify_res); - return -1; - } - std::string common_name; - std::vector dns_names; - std::vector ip_addrs; - get_altnames(cert, dns_names, ip_addrs, common_name); - if (verify_hostname(get_config()->downstream_addrs[0].host.get(), - &get_config()->downstream_addrs[0].addr, - get_config()->downstream_addrs[0].addrlen, dns_names, - ip_addrs, common_name) != 0) { - LOG(ERROR) << "Certificate verification failed: hostname does not match"; - return -1; - } - return 0; -} - -CertLookupTree::CertLookupTree() { - root_.ssl_ctx = nullptr; - root_.str = nullptr; - root_.first = root_.last = 0; -} - -namespace { -// The |offset| is the index in the hostname we are examining. We are -// going to scan from |offset| in backwards. -void cert_lookup_tree_add_cert(CertNode *node, SSL_CTX *ssl_ctx, char *hostname, - size_t len, int offset) { - int i, next_len = node->next.size(); - char c = hostname[offset]; - CertNode *cn = nullptr; - for (i = 0; i < next_len; ++i) { - cn = node->next[i].get(); - if (cn->str[cn->first] == c) { - break; - } - } - if (i == next_len) { - if (c == '*') { - // We assume hostname as wildcard hostname when first '*' is - // encountered. Note that as per RFC 6125 (6.4.3), there are - // some restrictions for wildcard hostname. We just ignore - // these rules here but do the proper check when we do the - // match. - node->wildcard_certs.emplace_back(hostname, ssl_ctx); - return; - } - - int j; - auto new_node = make_unique(); - new_node->str = hostname; - new_node->first = offset; - // If wildcard is found, set the region before it because we - // don't include it in [first, last). - for (j = offset; j >= 0 && hostname[j] != '*'; --j) - ; - new_node->last = j; - if (j == -1) { - new_node->ssl_ctx = ssl_ctx; - } else { - new_node->ssl_ctx = nullptr; - new_node->wildcard_certs.emplace_back(hostname, ssl_ctx); - } - node->next.push_back(std::move(new_node)); - return; - } - - int j; - for (i = cn->first, j = offset; - i > cn->last && j >= 0 && cn->str[i] == hostname[j]; --i, --j) - ; - if (i == cn->last) { - if (j == -1) { - // If the same hostname already exists, we don't overwrite - // exiting ssl_ctx - if (!cn->ssl_ctx) { - cn->ssl_ctx = ssl_ctx; - } - return; - } - - // The existing hostname is a suffix of this hostname. Continue - // matching at potion j. - cert_lookup_tree_add_cert(cn, ssl_ctx, hostname, len, j); - return; - } - - { - auto new_node = make_unique(); - new_node->ssl_ctx = cn->ssl_ctx; - new_node->str = cn->str; - new_node->first = i; - new_node->last = cn->last; - new_node->wildcard_certs.swap(cn->wildcard_certs); - new_node->next.swap(cn->next); - - cn->next.push_back(std::move(new_node)); - } - - cn->last = i; - if (j == -1) { - // This hostname is a suffix of the existing hostname. - cn->ssl_ctx = ssl_ctx; - return; - } - - // This hostname and existing one share suffix. - cn->ssl_ctx = nullptr; - cert_lookup_tree_add_cert(cn, ssl_ctx, hostname, len, j); -} -} // namespace - -void CertLookupTree::add_cert(SSL_CTX *ssl_ctx, const char *hostname, - size_t len) { - if (len == 0) { - return; - } - // Copy hostname including terminal NULL - hosts_.push_back(make_unique(len + 1)); - const auto &host_copy = hosts_.back(); - for (size_t i = 0; i < len; ++i) { - host_copy[i] = util::lowcase(hostname[i]); - } - host_copy[len] = '\0'; - cert_lookup_tree_add_cert(&root_, ssl_ctx, host_copy.get(), len, len - 1); -} - -namespace { -SSL_CTX *cert_lookup_tree_lookup(CertNode *node, const char *hostname, - size_t len, int offset) { - int i, j; - for (i = node->first, j = offset; - i > node->last && j >= 0 && node->str[i] == util::lowcase(hostname[j]); - --i, --j) - ; - if (i != node->last) { - return nullptr; - } - if (j == -1) { - if (node->ssl_ctx) { - // exact match - return node->ssl_ctx; - } - - // Do not perform wildcard-match because '*' must match at least - // one character. - return nullptr; - } - for (const auto &wildcert : node->wildcard_certs) { - if (tls_hostname_match(wildcert.first, hostname)) { - return wildcert.second; - } - } - auto c = util::lowcase(hostname[j]); - for (const auto &next_node : node->next) { - if (next_node->str[next_node->first] == c) { - return cert_lookup_tree_lookup(next_node.get(), hostname, len, j); - } - } - return nullptr; -} -} // namespace - -SSL_CTX *CertLookupTree::lookup(const char *hostname, size_t len) { - return cert_lookup_tree_lookup(&root_, hostname, len, len - 1); -} - -int cert_lookup_tree_add_cert_from_file(CertLookupTree *lt, SSL_CTX *ssl_ctx, - const char *certfile) { - auto bio = BIO_new(BIO_s_file()); - if (!bio) { - LOG(ERROR) << "BIO_new failed"; - return -1; - } - auto bio_deleter = defer(BIO_vfree, bio); - if (!BIO_read_filename(bio, certfile)) { - LOG(ERROR) << "Could not read certificate file '" << certfile << "'"; - return -1; - } - auto cert = PEM_read_bio_X509(bio, nullptr, nullptr, nullptr); - if (!cert) { - LOG(ERROR) << "Could not read X509 structure from file '" << certfile - << "'"; - return -1; - } - auto cert_deleter = defer(X509_free, cert); - std::string common_name; - std::vector dns_names; - std::vector ip_addrs; - get_altnames(cert, dns_names, ip_addrs, common_name); - for (auto &dns_name : dns_names) { - lt->add_cert(ssl_ctx, dns_name.c_str(), dns_name.size()); - } - lt->add_cert(ssl_ctx, common_name.c_str(), common_name.size()); - return 0; -} - -bool in_proto_list(const std::vector &protos, - const unsigned char *needle, size_t len) { - for (auto proto : protos) { - if (strlen(proto) == len && memcmp(proto, needle, len) == 0) { - return true; - } - } - return false; -} - -bool check_http2_requirement(SSL *ssl) { - auto tls_ver = SSL_version(ssl); - - switch (tls_ver) { - case TLS1_2_VERSION: - break; - default: - if (LOG_ENABLED(INFO)) { - LOG(INFO) << "TLSv1.2 was not negotiated. " - << "HTTP/2 must not be negotiated."; - } - return false; - } - - return true; -} - -SSL_CTX *setup_server_ssl_context(std::vector &all_ssl_ctx, - CertLookupTree *cert_tree) { - if (get_config()->upstream_no_tls) { - return nullptr; - } - - auto ssl_ctx = ssl::create_ssl_context(get_config()->private_key_file.get(), - get_config()->cert_file.get()); - - all_ssl_ctx.push_back(ssl_ctx); - - if (get_config()->subcerts.empty()) { - return ssl_ctx; - } - - if (!cert_tree) { - LOG(WARN) << "We have multiple additional certificates (--subcert), but " - "cert_tree is not given. SNI may not work."; - return ssl_ctx; - } - - for (auto &keycert : get_config()->subcerts) { - auto ssl_ctx = - ssl::create_ssl_context(keycert.first.c_str(), keycert.second.c_str()); - all_ssl_ctx.push_back(ssl_ctx); - if (ssl::cert_lookup_tree_add_cert_from_file( - cert_tree, ssl_ctx, keycert.second.c_str()) == -1) { - LOG(FATAL) << "Failed to add sub certificate."; - DIE(); - } - } - - if (ssl::cert_lookup_tree_add_cert_from_file( - cert_tree, ssl_ctx, get_config()->cert_file.get()) == -1) { - LOG(FATAL) << "Failed to add default certificate."; - DIE(); - } - - return ssl_ctx; -} - -SSL_CTX *setup_client_ssl_context() { - if (get_config()->client_mode) { - return get_config()->downstream_no_tls ? nullptr - : ssl::create_ssl_client_context(); - } - - return get_config()->http2_bridge && !get_config()->downstream_no_tls - ? ssl::create_ssl_client_context() - : nullptr; -} - -CertLookupTree *create_cert_lookup_tree() { - if (get_config()->upstream_no_tls || get_config()->subcerts.empty()) { - return nullptr; - } - return new ssl::CertLookupTree(); -} - -} // namespace ssl - -} // namespace shrpx diff --git a/src/shrpx_ssl.h b/src/shrpx_ssl.h deleted file mode 100644 index 5f808cb..0000000 --- a/src/shrpx_ssl.h +++ /dev/null @@ -1,177 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2012 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#ifndef SHRPX_SSL_H -#define SHRPX_SSL_H - -#include "shrpx.h" - -#include -#include - -#include -#include - -#include - -namespace shrpx { - -class ClientHandler; -class Worker; -class DownstreamConnectionPool; - -namespace ssl { - -// This struct stores the additional information per SSL_CTX. This is -// attached to SSL_CTX using SSL_CTX_set_app_data(). -struct TLSContextData { - // Protects ocsp_data; - std::mutex mu; - // OCSP resonse - std::vector ocsp_data; - - // Path to certificate file - const char *cert_file; -}; - -// Create server side SSL_CTX -SSL_CTX *create_ssl_context(const char *private_key_file, - const char *cert_file); - -// Create client side SSL_CTX -SSL_CTX *create_ssl_client_context(); - -ClientHandler *accept_connection(Worker *worker, int fd, sockaddr *addr, - int addrlen); - -// Check peer's certificate against first downstream address in -// Config::downstream_addrs. We only consider first downstream since -// we use this function for HTTP/2 downstream link only. -int check_cert(SSL *ssl); - -// Retrieves DNS and IP address in subjectAltNames and commonName from -// the |cert|. -void get_altnames(X509 *cert, std::vector &dns_names, - std::vector &ip_addrs, std::string &common_name); - -// CertLookupTree forms lookup tree to get SSL_CTX whose DNS or -// commonName matches hostname in query. The tree is patricia trie -// data structure formed from the tail of the hostname pattern. Each -// CertNode contains part of hostname str member in range [first, -// last) member and the next member contains the following CertNode -// pointers ('following' means character before the current one). The -// CertNode where a hostname pattern ends contains its SSL_CTX pointer -// in the ssl_ctx member. For wildcard hostname pattern, we store the -// its pattern and SSL_CTX in CertNode one before first "*" found from -// the tail. -// -// When querying SSL_CTX with particular hostname, we match from its -// tail in our lookup tree. If the query goes to the first character -// of the hostname and current CertNode has non-NULL ssl_ctx member, -// then it is the exact match. The ssl_ctx member is returned. Along -// the way, if CertNode which contains non-empty wildcard_certs member -// is encountered, wildcard hostname matching is performed against -// them. If there is a match, its SSL_CTX is returned. If none -// matches, query is continued to the next character. - -struct CertNode { - // list of wildcard domain name and its SSL_CTX pair, the wildcard - // '*' appears in this position. - std::vector> wildcard_certs; - // Next CertNode index of CertLookupTree::nodes - std::vector> next; - // SSL_CTX for exact match - SSL_CTX *ssl_ctx; - char *str; - // [first, last) in the reverse direction in str, first >= - // last. This indices only work for str member. - int first, last; -}; - -class CertLookupTree { -public: - CertLookupTree(); - - // Adds |ssl_ctx| with hostname pattern |hostname| with length |len| - // to the lookup tree. The |hostname| must be NULL-terminated. - void add_cert(SSL_CTX *ssl_ctx, const char *hostname, size_t len); - - // Looks up SSL_CTX using the given |hostname| with length |len|. - // If more than one SSL_CTX which matches the query, it is undefined - // which one is returned. The |hostname| must be NULL-terminated. - // If no matching SSL_CTX found, returns NULL. - SSL_CTX *lookup(const char *hostname, size_t len); - -private: - CertNode root_; - // Stores pointers to copied hostname when adding hostname and - // ssl_ctx pair. - std::vector> hosts_; -}; - -// Adds |ssl_ctx| to lookup tree |lt| using hostnames read from -// |certfile|. The subjectAltNames and commonName are considered as -// eligible hostname. This function returns 0 if it succeeds, or -1. -// Even if no ssl_ctx is added to tree, this function returns 0. -int cert_lookup_tree_add_cert_from_file(CertLookupTree *lt, SSL_CTX *ssl_ctx, - const char *certfile); - -// Returns true if |needle| which has |len| bytes is included in the -// protocol list |protos|. -bool in_proto_list(const std::vector &protos, - const unsigned char *needle, size_t len); - -// Returns true if security requirement for HTTP/2 is fulfilled. -bool check_http2_requirement(SSL *ssl); - -// Returns SSL/TLS option mask to disable SSL/TLS protocol version not -// included in |tls_proto_list|. The returned mask can be directly -// passed to SSL_CTX_set_options(). -long int create_tls_proto_mask(const std::vector &tls_proto_list); - -std::vector set_alpn_prefs(const std::vector &protos); - -// Setups server side SSL_CTX. This function inspects get_config() -// and if upstream_no_tls is true, returns nullptr. Otherwise -// construct default SSL_CTX. If subcerts are available -// (get_config()->subcerts), caller should provide CertLookupTree -// object as |cert_tree| parameter, otherwise SNI does not work. All -// the created SSL_CTX is stored into |all_ssl_ctx|. -SSL_CTX *setup_server_ssl_context(std::vector &all_ssl_ctx, - CertLookupTree *cert_tree); - -// Setups client side SSL_CTX. This function inspects get_config() -// and if downstream_no_tls is true, returns nullptr. Otherwise, only -// construct SSL_CTX if either client_mode or http2_bridge is true. -SSL_CTX *setup_client_ssl_context(); - -// Creates CertLookupTree. If frontend is configured not to use TLS, -// this function returns nullptr. -CertLookupTree *create_cert_lookup_tree(); - -} // namespace ssl - -} // namespace shrpx - -#endif // SHRPX_SSL_H diff --git a/src/shrpx_ssl_test.cc b/src/shrpx_ssl_test.cc deleted file mode 100644 index 22bac63..0000000 --- a/src/shrpx_ssl_test.cc +++ /dev/null @@ -1,119 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2013 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#include "shrpx_ssl_test.h" - -#include - -#include "shrpx_ssl.h" -#include "util.h" -#include "template.h" - -using namespace nghttp2; - -namespace shrpx { - -void test_shrpx_ssl_create_lookup_tree(void) { - auto tree = make_unique(); - SSL_CTX *ctxs[] = { - SSL_CTX_new(SSLv23_method()), SSL_CTX_new(SSLv23_method()), - SSL_CTX_new(SSLv23_method()), SSL_CTX_new(SSLv23_method()), - SSL_CTX_new(SSLv23_method()), SSL_CTX_new(SSLv23_method()), - SSL_CTX_new(SSLv23_method()), SSL_CTX_new(SSLv23_method()), - SSL_CTX_new(SSLv23_method()), SSL_CTX_new(SSLv23_method())}; - - const char *hostnames[] = {"example.com", "www.example.org", - "*www.example.org", "x*.host.domain", - "*yy.host.domain", "nghttp2.sourceforge.net", - "sourceforge.net", - "sourceforge.net", // duplicate - "*.foo.bar", // oo.bar is suffix of *.foo.bar - "oo.bar"}; - int num = array_size(ctxs); - for (int i = 0; i < num; ++i) { - tree->add_cert(ctxs[i], hostnames[i], strlen(hostnames[i])); - } - - CU_ASSERT(ctxs[0] == tree->lookup(hostnames[0], strlen(hostnames[0]))); - CU_ASSERT(ctxs[1] == tree->lookup(hostnames[1], strlen(hostnames[1]))); - const char h1[] = "2www.example.org"; - CU_ASSERT(ctxs[2] == tree->lookup(h1, strlen(h1))); - const char h2[] = "www2.example.org"; - CU_ASSERT(0 == tree->lookup(h2, strlen(h2))); - const char h3[] = "x1.host.domain"; - CU_ASSERT(ctxs[3] == tree->lookup(h3, strlen(h3))); - // Does not match *yy.host.domain, because * must match at least 1 - // character. - const char h4[] = "yy.Host.domain"; - CU_ASSERT(0 == tree->lookup(h4, strlen(h4))); - const char h5[] = "zyy.host.domain"; - CU_ASSERT(ctxs[4] == tree->lookup(h5, strlen(h5))); - CU_ASSERT(0 == tree->lookup("", 0)); - CU_ASSERT(ctxs[5] == tree->lookup(hostnames[5], strlen(hostnames[5]))); - CU_ASSERT(ctxs[6] == tree->lookup(hostnames[6], strlen(hostnames[6]))); - const char h6[] = "pdylay.sourceforge.net"; - for (int i = 0; i < 7; ++i) { - CU_ASSERT(0 == tree->lookup(h6 + i, strlen(h6) - i)); - } - const char h7[] = "x.foo.bar"; - CU_ASSERT(ctxs[8] == tree->lookup(h7, strlen(h7))); - CU_ASSERT(ctxs[9] == tree->lookup(hostnames[9], strlen(hostnames[9]))); - - for (int i = 0; i < num; ++i) { - SSL_CTX_free(ctxs[i]); - } - - SSL_CTX *ctxs2[] = { - SSL_CTX_new(SSLv23_method()), SSL_CTX_new(SSLv23_method()), - SSL_CTX_new(SSLv23_method()), SSL_CTX_new(SSLv23_method())}; - const char *names[] = {"rab", "zab", "zzub", "ab"}; - num = array_size(ctxs2); - - tree = make_unique(); - for (int i = 0; i < num; ++i) { - tree->add_cert(ctxs2[i], names[i], strlen(names[i])); - } - for (int i = 0; i < num; ++i) { - CU_ASSERT(ctxs2[i] == tree->lookup(names[i], strlen(names[i]))); - } - - for (int i = 0; i < num; ++i) { - SSL_CTX_free(ctxs2[i]); - } -} - -void test_shrpx_ssl_cert_lookup_tree_add_cert_from_file(void) { - int rv; - ssl::CertLookupTree tree; - auto ssl_ctx = SSL_CTX_new(SSLv23_method()); - const char certfile[] = NGHTTP2_TESTS_DIR "/testdata/cacert.pem"; - rv = ssl::cert_lookup_tree_add_cert_from_file(&tree, ssl_ctx, certfile); - CU_ASSERT(0 == rv); - const char localhost[] = "localhost"; - CU_ASSERT(ssl_ctx == tree.lookup(localhost, sizeof(localhost) - 1)); - - SSL_CTX_free(ssl_ctx); -} - -} // namespace shrpx diff --git a/src/shrpx_ssl_test.h b/src/shrpx_ssl_test.h deleted file mode 100644 index 89b0044..0000000 --- a/src/shrpx_ssl_test.h +++ /dev/null @@ -1,39 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2013 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#ifndef SHRPX_SSL_TEST_H -#define SHRPX_SSL_TEST_H - -#ifdef HAVE_CONFIG_H -#include -#endif // HAVE_CONFIG_H - -namespace shrpx { - -void test_shrpx_ssl_create_lookup_tree(void); -void test_shrpx_ssl_cert_lookup_tree_add_cert_from_file(void); - -} // namespace shrpx - -#endif // SHRPX_SSL_TEST_H diff --git a/src/shrpx_upstream.h b/src/shrpx_upstream.h deleted file mode 100644 index fe9a673..0000000 --- a/src/shrpx_upstream.h +++ /dev/null @@ -1,69 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2012 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#ifndef SHRPX_UPSTREAM_H -#define SHRPX_UPSTREAM_H - -#include "shrpx.h" -#include "shrpx_io_control.h" - -namespace shrpx { - -class ClientHandler; -class Downstream; -class DownstreamConnection; - -class Upstream { -public: - virtual ~Upstream() {} - virtual int on_read() = 0; - virtual int on_write() = 0; - virtual int on_timeout(Downstream *downstream) { return 0; }; - virtual int on_downstream_abort_request(Downstream *downstream, - unsigned int status_code) = 0; - virtual int downstream_read(DownstreamConnection *dconn) = 0; - virtual int downstream_write(DownstreamConnection *dconn) = 0; - virtual int downstream_eof(DownstreamConnection *dconn) = 0; - virtual int downstream_error(DownstreamConnection *dconn, int events) = 0; - virtual ClientHandler *get_client_handler() const = 0; - - virtual int on_downstream_header_complete(Downstream *downstream) = 0; - virtual int on_downstream_body(Downstream *downstream, const uint8_t *data, - size_t len, bool flush) = 0; - virtual int on_downstream_body_complete(Downstream *downstream) = 0; - - virtual void on_handler_delete() = 0; - // Called when downstream connection is reset. Currently this is - // only used by Http2Session. If |no_retry| is true, another - // connection attempt using new DownstreamConnection is not allowed. - virtual int on_downstream_reset(bool no_retry) = 0; - - virtual void pause_read(IOCtrlReason reason) = 0; - virtual int resume_read(IOCtrlReason reason, Downstream *downstream, - size_t consumed) = 0; -}; - -} // namespace shrpx - -#endif // SHRPX_UPSTREAM_H diff --git a/src/shrpx_worker.cc b/src/shrpx_worker.cc deleted file mode 100644 index a9bf6e6..0000000 --- a/src/shrpx_worker.cc +++ /dev/null @@ -1,242 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2012 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#include "shrpx_worker.h" - -#ifdef HAVE_UNISTD_H -#include -#endif // HAVE_UNISTD_H - -#include - -#include "shrpx_ssl.h" -#include "shrpx_log.h" -#include "shrpx_client_handler.h" -#include "shrpx_http2_session.h" -#include "shrpx_log_config.h" -#include "shrpx_connect_blocker.h" -#include "util.h" -#include "template.h" - -namespace shrpx { - -namespace { -void eventcb(struct ev_loop *loop, ev_async *w, int revents) { - auto worker = static_cast(w->data); - worker->process_events(); -} -} // namespace - -namespace { -void mcpool_clear_cb(struct ev_loop *loop, ev_timer *w, int revents) { - auto worker = static_cast(w->data); - if (worker->get_worker_stat()->num_connections != 0) { - return; - } - worker->get_mcpool()->clear(); -} -} // namespace - -Worker::Worker(struct ev_loop *loop, SSL_CTX *sv_ssl_ctx, SSL_CTX *cl_ssl_ctx, - ssl::CertLookupTree *cert_tree, - const std::shared_ptr &ticket_keys) - : next_http2session_(0), loop_(loop), sv_ssl_ctx_(sv_ssl_ctx), - cl_ssl_ctx_(cl_ssl_ctx), cert_tree_(cert_tree), ticket_keys_(ticket_keys), - connect_blocker_(make_unique(loop_)), - graceful_shutdown_(false) { - ev_async_init(&w_, eventcb); - w_.data = this; - ev_async_start(loop_, &w_); - - ev_timer_init(&mcpool_clear_timer_, mcpool_clear_cb, 0., 0.); - mcpool_clear_timer_.data = this; - - if (get_config()->downstream_proto == PROTO_HTTP2) { - auto n = get_config()->http2_downstream_connections_per_worker; - for (; n > 0; --n) { - http2sessions_.push_back(make_unique( - loop_, cl_ssl_ctx, connect_blocker_.get(), this)); - } - } -} - -Worker::~Worker() { - ev_async_stop(loop_, &w_); - ev_timer_stop(loop_, &mcpool_clear_timer_); -} - -void Worker::schedule_clear_mcpool() { - // libev manual says: "If the watcher is already active nothing will - // happen." Since we don't change any timeout here, we don't have - // to worry about querying ev_is_active. - ev_timer_start(loop_, &mcpool_clear_timer_); -} - -void Worker::wait() { -#ifndef NOTHREADS - fut_.get(); -#endif // !NOTHREADS -} - -void Worker::run_async() { -#ifndef NOTHREADS - fut_ = std::async(std::launch::async, [this] { - (void)reopen_log_files(); - ev_run(loop_); - }); -#endif // !NOTHREADS -} - -void Worker::send(const WorkerEvent &event) { - { - std::lock_guard g(m_); - - q_.push_back(event); - } - - ev_async_send(loop_, &w_); -} - -void Worker::process_events() { - std::deque q; - { - std::lock_guard g(m_); - q.swap(q_); - } - for (auto &wev : q) { - switch (wev.type) { - case NEW_CONNECTION: { - if (LOG_ENABLED(INFO)) { - WLOG(INFO, this) << "WorkerEvent: client_fd=" << wev.client_fd - << ", addrlen=" << wev.client_addrlen; - } - - if (worker_stat_.num_connections >= - get_config()->worker_frontend_connections) { - - if (LOG_ENABLED(INFO)) { - WLOG(INFO, this) << "Too many connections >= " - << get_config()->worker_frontend_connections; - } - - close(wev.client_fd); - - break; - } - - auto client_handler = ssl::accept_connection( - this, wev.client_fd, &wev.client_addr.sa, wev.client_addrlen); - if (!client_handler) { - if (LOG_ENABLED(INFO)) { - WLOG(ERROR, this) << "ClientHandler creation failed"; - } - close(wev.client_fd); - break; - } - - if (LOG_ENABLED(INFO)) { - WLOG(INFO, this) << "CLIENT_HANDLER:" << client_handler << " created "; - } - - break; - } - case RENEW_TICKET_KEYS: - WLOG(NOTICE, this) << "Renew ticket keys: worker(" << this << ")"; - - ticket_keys_ = wev.ticket_keys; - - break; - case REOPEN_LOG: - WLOG(NOTICE, this) << "Reopening log files: worker(" << this << ")"; - - reopen_log_files(); - - break; - case GRACEFUL_SHUTDOWN: - WLOG(NOTICE, this) << "Graceful shutdown commencing"; - - graceful_shutdown_ = true; - - if (worker_stat_.num_connections == 0) { - ev_break(loop_); - - return; - } - - break; - default: - if (LOG_ENABLED(INFO)) { - WLOG(INFO, this) << "unknown event type " << wev.type; - } - } - } -} - -ssl::CertLookupTree *Worker::get_cert_lookup_tree() const { return cert_tree_; } - -const std::shared_ptr &Worker::get_ticket_keys() const { - return ticket_keys_; -} - -void Worker::set_ticket_keys(std::shared_ptr ticket_keys) { - ticket_keys_ = std::move(ticket_keys); -} - -WorkerStat *Worker::get_worker_stat() { return &worker_stat_; } - -DownstreamConnectionPool *Worker::get_dconn_pool() { return &dconn_pool_; } - -Http2Session *Worker::next_http2_session() { - if (http2sessions_.empty()) { - return nullptr; - } - - auto res = http2sessions_[next_http2session_].get(); - ++next_http2session_; - if (next_http2session_ >= http2sessions_.size()) { - next_http2session_ = 0; - } - - return res; -} - -ConnectBlocker *Worker::get_connect_blocker() const { - return connect_blocker_.get(); -} - -struct ev_loop *Worker::get_loop() const { - return loop_; -} - -SSL_CTX *Worker::get_sv_ssl_ctx() const { return sv_ssl_ctx_; } - -SSL_CTX *Worker::get_cl_ssl_ctx() const { return cl_ssl_ctx_; } - -void Worker::set_graceful_shutdown(bool f) { graceful_shutdown_ = f; } - -bool Worker::get_graceful_shutdown() const { return graceful_shutdown_; } - -MemchunkPool *Worker::get_mcpool() { return &mcpool_; } - -} // namespace shrpx diff --git a/src/shrpx_worker.h b/src/shrpx_worker.h deleted file mode 100644 index 4721371..0000000 --- a/src/shrpx_worker.h +++ /dev/null @@ -1,142 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2012 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#ifndef SHRPX_WORKER_H -#define SHRPX_WORKER_H - -#include "shrpx.h" - -#include -#include -#include -#include -#ifndef NOTHREADS -#include -#endif // NOTHREADS - -#include -#include - -#include - -#include "shrpx_config.h" -#include "shrpx_downstream_connection_pool.h" -#include "memchunk.h" - -using namespace nghttp2; - -namespace shrpx { - -class Http2Session; -class ConnectBlocker; - -namespace ssl { -class CertLookupTree; -} // namespace ssl - -struct WorkerStat { - WorkerStat() : num_connections(0), next_downstream(0) {} - - size_t num_connections; - // Next downstream index in Config::downstream_addrs. For HTTP/2 - // downstream connections, this is always 0. For HTTP/1, this is - // used as load balancing. - size_t next_downstream; -}; - -enum WorkerEventType { - NEW_CONNECTION = 0x01, - REOPEN_LOG = 0x02, - GRACEFUL_SHUTDOWN = 0x03, - RENEW_TICKET_KEYS = 0x04, -}; - -struct WorkerEvent { - WorkerEventType type; - struct { - sockaddr_union client_addr; - size_t client_addrlen; - int client_fd; - }; - std::shared_ptr ticket_keys; -}; - -class Worker { -public: - Worker(struct ev_loop *loop, SSL_CTX *sv_ssl_ctx, SSL_CTX *cl_ssl_ctx, - ssl::CertLookupTree *cert_tree, - const std::shared_ptr &ticket_keys); - ~Worker(); - void run_async(); - void wait(); - void process_events(); - void send(const WorkerEvent &event); - - ssl::CertLookupTree *get_cert_lookup_tree() const; - const std::shared_ptr &get_ticket_keys() const; - void set_ticket_keys(std::shared_ptr ticket_keys); - WorkerStat *get_worker_stat(); - DownstreamConnectionPool *get_dconn_pool(); - Http2Session *next_http2_session(); - ConnectBlocker *get_connect_blocker() const; - struct ev_loop *get_loop() const; - SSL_CTX *get_sv_ssl_ctx() const; - SSL_CTX *get_cl_ssl_ctx() const; - - void set_graceful_shutdown(bool f); - bool get_graceful_shutdown() const; - - MemchunkPool *get_mcpool(); - void schedule_clear_mcpool(); - -private: - std::vector> http2sessions_; - size_t next_http2session_; -#ifndef NOTHREADS - std::future fut_; -#endif // NOTHREADS - std::mutex m_; - std::deque q_; - ev_async w_; - ev_timer mcpool_clear_timer_; - MemchunkPool mcpool_; - DownstreamConnectionPool dconn_pool_; - WorkerStat worker_stat_; - struct ev_loop *loop_; - - // Following fields are shared across threads if - // get_config()->tls_ctx_per_worker == true. - SSL_CTX *sv_ssl_ctx_; - SSL_CTX *cl_ssl_ctx_; - ssl::CertLookupTree *cert_tree_; - - std::shared_ptr ticket_keys_; - std::unique_ptr connect_blocker_; - - bool graceful_shutdown_; -}; - -} // namespace shrpx - -#endif // SHRPX_WORKER_H diff --git a/src/ssl.cc b/src/ssl.cc deleted file mode 100644 index 8696801..0000000 --- a/src/ssl.cc +++ /dev/null @@ -1,83 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2014 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#include "ssl.h" - -#include -#include -#include -#include - -#include - -namespace nghttp2 { - -namespace ssl { - -// Recommended general purpose "Non-Backward Compatible" cipher by -// mozilla. -// -// https://wiki.mozilla.org/Security/Server_Side_TLS -const char *const DEFAULT_CIPHER_LIST = - "ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-" - "AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:" - "DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-" - "AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-" - "AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-" - "AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:" - "DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:!aNULL:!eNULL:" - "!EXPORT:!DES:!RC4:!3DES:!MD5:!PSK"; - -namespace { -std::vector ssl_global_locks; -} // namespace - -namespace { -void ssl_locking_cb(int mode, int type, const char *file, int line) { - if (mode & CRYPTO_LOCK) { - ssl_global_locks[type].lock(); - } else { - ssl_global_locks[type].unlock(); - } -} -} // namespace - -LibsslGlobalLock::LibsslGlobalLock() { - if (!ssl_global_locks.empty()) { - std::cerr << "OpenSSL global lock has been already set" << std::endl; - assert(0); - } - ssl_global_locks = std::vector(CRYPTO_num_locks()); - // CRYPTO_set_id_callback(ssl_thread_id); OpenSSL manual says that - // if threadid_func is not specified using - // CRYPTO_THREADID_set_callback(), then default implementation is - // used. We use this default one. - CRYPTO_set_locking_callback(ssl_locking_cb); -} - -LibsslGlobalLock::~LibsslGlobalLock() { ssl_global_locks.clear(); } - -} // namespace ssl - -} // namespace nghttp2 diff --git a/src/ssl.h b/src/ssl.h deleted file mode 100644 index 97f3933..0000000 --- a/src/ssl.h +++ /dev/null @@ -1,45 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2012 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#include "nghttp2_config.h" - -namespace nghttp2 { - -namespace ssl { - -// Acquire OpenSSL global lock to share SSL_CTX across multiple -// threads. The constructor acquires lock and destructor unlocks. -class LibsslGlobalLock { -public: - LibsslGlobalLock(); - ~LibsslGlobalLock(); - LibsslGlobalLock(const LibsslGlobalLock &) = delete; - LibsslGlobalLock &operator=(const LibsslGlobalLock &) = delete; -}; - -extern const char *const DEFAULT_CIPHER_LIST; - -} // namespace ssl - -} // namespace nghttp2 diff --git a/src/template.h b/src/template.h deleted file mode 100644 index 144aed1..0000000 --- a/src/template.h +++ /dev/null @@ -1,150 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2015 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#ifndef TEMPLATE_H -#define TEMPLATE_H - -#include "nghttp2_config.h" - -#include -#include -#include - -namespace nghttp2 { - -template -typename std::enable_if::value, std::unique_ptr>::type -make_unique(U &&... u) { - return std::unique_ptr(new T(std::forward(u)...)); -} - -template -typename std::enable_if::value, std::unique_ptr>::type -make_unique(size_t size) { - return std::unique_ptr(new typename std::remove_extent::type[size]()); -} - -template -std::array make_array(T &&t, Rest &&... rest) { - return std::array{ - {std::forward(t), std::forward(rest)...}}; -} - -template constexpr size_t array_size(T (&)[N]) { - return N; -} - -template constexpr size_t str_size(T (&)[N]) { - return N - 1; -} - -// inspired by , but our -// template can take functions returning other than void. -template struct Defer { - Defer(F &&f, T &&... t) - : f(std::bind(std::forward(f), std::forward(t)...)) {} - Defer(Defer &&o) : f(std::move(o.f)) {} - ~Defer() { f(); } - - using ResultType = typename std::result_of< - typename std::decay::type(typename std::decay::type...)>::type; - std::function f; -}; - -template Defer defer(F &&f, T &&... t) { - return Defer(std::forward(f), std::forward(t)...); -} - -template bool test_flags(T t, F flags) { - return (t & flags) == flags; -} - -// doubly linked list of element T*. T must have field T *dlprev and -// T *dlnext, which point to previous element and next element in the -// list respectively. -template struct DList { - DList() : head(nullptr), tail(nullptr) {} - - DList(const DList &) = delete; - - DList &operator=(const DList &) = delete; - - DList(DList &&other) : head(other.head), tail(other.tail) { - other.head = other.tail = nullptr; - } - - DList &operator=(DList &&other) { - if (this == &other) { - return *this; - } - head = other.head; - tail = other.tail; - other.head = other.tail = nullptr; - return *this; - } - - void append(T *t) { - if (tail) { - tail->dlnext = t; - t->dlprev = tail; - tail = t; - return; - } - head = tail = t; - } - - void remove(T *t) { - auto p = t->dlprev; - auto n = t->dlnext; - if (p) { - p->dlnext = n; - } - if (head == t) { - head = n; - } - if (n) { - n->dlprev = p; - } - if (tail == t) { - tail = p; - } - t->dlprev = t->dlnext = nullptr; - } - - bool empty() const { return head == nullptr; } - - T *head, *tail; -}; - -template void dlist_delete_all(DList &dl) { - for (auto e = dl.head; e;) { - auto next = e->dlnext; - delete e; - e = next; - } -} - -} // namespace nghttp2 - -#endif // TEMPLATE_H diff --git a/src/timegm.c b/src/timegm.c deleted file mode 100644 index 90bf53f..0000000 --- a/src/timegm.c +++ /dev/null @@ -1,70 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2013 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#include "timegm.h" - -#ifndef HAVE_TIMEGM - -#include - -/* Counter the number of leap year in the range [0, y). The |y| is the - year, including century (e.g., 2012) */ -static int count_leap_year(int y) { - y -= 1; - return y / 4 - y / 100 + y / 400; -} - -/* Returns nonzero if the |y| is the leap year. The |y| is the year, - including century (e.g., 2012) */ -static int is_leap_year(int y) { - return y % 4 == 0 && (y % 100 != 0 || y % 400 == 0); -} - -/* The number of days before ith month begins */ -static int daysum[] = {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334}; - -/* Based on the algorithm of Python 2.7 calendar.timegm. */ -time_t timegm(struct tm *tm) { - int days; - int num_leap_year; - int64_t t; - if (tm->tm_mon > 11) { - return -1; - } - num_leap_year = count_leap_year(tm->tm_year + 1900) - count_leap_year(1970); - days = (tm->tm_year - 70) * 365 + num_leap_year + daysum[tm->tm_mon] + - tm->tm_mday - 1; - if (tm->tm_mon >= 2 && is_leap_year(tm->tm_year + 1900)) { - ++days; - } - t = ((int64_t)days * 24 + tm->tm_hour) * 3600 + tm->tm_min * 60 + tm->tm_sec; - if (sizeof(time_t) == 4) { - if (t < INT32_MIN || t > INT32_MAX) { - return -1; - } - } - return (time_t)t; -} - -#endif /* !HAVE_TIMEGM */ diff --git a/src/timegm.h b/src/timegm.h deleted file mode 100644 index 32a9526..0000000 --- a/src/timegm.h +++ /dev/null @@ -1,50 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2013 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#ifndef TIMEGM_H -#define TIMEGM_H - -#ifdef HAVE_CONFIG_H -#include -#endif /* HAVE_CONFIG_H */ - -#ifdef __cplusplus -extern "C" { -#endif /* __cplusplus */ - -#ifdef HAVE_TIME_H -#include -#endif // HAVE_TIME_H - -#ifndef HAVE_TIMEGM - -time_t timegm(struct tm *tm); - -#endif /* HAVE_TIMEGM */ - -#ifdef __cplusplus -} -#endif /* __cplusplus */ - -#endif /* TIMEGM_H */ diff --git a/src/util.cc b/src/util.cc deleted file mode 100644 index 9eb5258..0000000 --- a/src/util.cc +++ /dev/null @@ -1,1161 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2012 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#include "util.h" - -#ifdef HAVE_TIME_H -#include -#endif // HAVE_TIME_H -#include -#ifdef HAVE_SYS_SOCKET_H -#include -#endif // HAVE_SYS_SOCKET_H -#ifdef HAVE_NETDB_H -#include -#endif // HAVE_NETDB_H -#include -#ifdef HAVE_FCNTL_H -#include -#endif // HAVE_FCNTL_H -#ifdef HAVE_NETINET_IN_H -#include -#endif // HAVE_NETINET_IN_H -#include -#ifdef HAVE_ARPA_INET_H -#include -#endif // HAVE_ARPA_INET_H - -#include -#include -#include -#include -#include -#include - -#include - -#include "timegm.h" -#include "template.h" - -namespace nghttp2 { - -namespace util { - -const char DEFAULT_STRIP_CHARSET[] = "\r\n\t "; - -const char UPPER_XDIGITS[] = "0123456789ABCDEF"; - -bool isAlpha(const char c) { - return ('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z'); -} - -bool isDigit(const char c) { return '0' <= c && c <= '9'; } - -bool isHexDigit(const char c) { - return isDigit(c) || ('A' <= c && c <= 'F') || ('a' <= c && c <= 'f'); -} - -bool inRFC3986UnreservedChars(const char c) { - static const char unreserved[] = {'-', '.', '_', '~'}; - return isAlpha(c) || isDigit(c) || - std::find(&unreserved[0], &unreserved[4], c) != &unreserved[4]; -} - -bool in_rfc3986_sub_delims(const char c) { - static const char sub_delims[] = {'!', '$', '&', '\'', '(', ')', - '*', '+', ',', ';', '='}; - return std::find(std::begin(sub_delims), std::end(sub_delims), c) != - std::end(sub_delims); -} - -std::string percentEncode(const unsigned char *target, size_t len) { - std::string dest; - for (size_t i = 0; i < len; ++i) { - unsigned char c = target[i]; - - if (inRFC3986UnreservedChars(c)) { - dest += c; - } else { - dest += "%"; - dest += UPPER_XDIGITS[c >> 4]; - dest += UPPER_XDIGITS[(c & 0x0f)]; - } - } - return dest; -} - -std::string percentEncode(const std::string &target) { - return percentEncode(reinterpret_cast(target.c_str()), - target.size()); -} - -std::string percent_encode_path(const std::string &s) { - std::string dest; - for (auto c : s) { - if (inRFC3986UnreservedChars(c) || in_rfc3986_sub_delims(c) || c == '/') { - dest += c; - continue; - } - - dest += "%"; - dest += UPPER_XDIGITS[(c >> 4) & 0x0f]; - dest += UPPER_XDIGITS[(c & 0x0f)]; - } - return dest; -} - -bool in_token(char c) { - static const char extra[] = {'!', '#', '$', '%', '&', '\'', '*', '+', - '-', '.', '^', '_', '`', '|', '~'}; - - return isAlpha(c) || isDigit(c) || - std::find(&extra[0], &extra[sizeof(extra)], c) != - &extra[sizeof(extra)]; -} - -bool in_attr_char(char c) { - static const char bad[] = {'*', '\'', '%'}; - return util::in_token(c) && - std::find(std::begin(bad), std::end(bad) - 1, c) == std::end(bad) - 1; -} - -std::string percent_encode_token(const std::string &target) { - auto len = target.size(); - std::string dest; - - for (size_t i = 0; i < len; ++i) { - unsigned char c = target[i]; - - if (c != '%' && in_token(c)) { - dest += c; - } else { - dest += "%"; - dest += UPPER_XDIGITS[c >> 4]; - dest += UPPER_XDIGITS[(c & 0x0f)]; - } - } - return dest; -} - -uint32_t hex_to_uint(char c) { - if (c <= '9') { - return c - '0'; - } - if (c <= 'Z') { - return c - 'A' + 10; - } - if (c <= 'z') { - return c - 'a' + 10; - } - return c; -} - -std::string quote_string(const std::string &target) { - auto cnt = std::count(std::begin(target), std::end(target), '"'); - - if (cnt == 0) { - return target; - } - - std::string res; - res.reserve(target.size() + cnt); - - for (auto c : target) { - if (c == '"') { - res += "\\\""; - } else { - res += c; - } - } - - return res; -} - -namespace { -template -Iterator cpydig(Iterator d, uint32_t n, size_t len) { - auto p = d + len - 1; - - do { - *p-- = (n % 10) + '0'; - n /= 10; - } while (p >= d); - - return d + len; -} -} // namespace - -namespace { -const char *MONTH[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", - "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; -const char *DAY_OF_WEEK[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}; -} // namespace - -std::string http_date(time_t t) { - struct tm tms; - std::string res; - - if (gmtime_r(&t, &tms) == nullptr) { - return res; - } - - /* Sat, 27 Sep 2014 06:31:15 GMT */ - res.resize(29); - - auto p = std::begin(res); - - auto s = DAY_OF_WEEK[tms.tm_wday]; - p = std::copy_n(s, 3, p); - *p++ = ','; - *p++ = ' '; - p = cpydig(p, tms.tm_mday, 2); - *p++ = ' '; - s = MONTH[tms.tm_mon]; - p = std::copy_n(s, 3, p); - *p++ = ' '; - p = cpydig(p, tms.tm_year + 1900, 4); - *p++ = ' '; - p = cpydig(p, tms.tm_hour, 2); - *p++ = ':'; - p = cpydig(p, tms.tm_min, 2); - *p++ = ':'; - p = cpydig(p, tms.tm_sec, 2); - s = " GMT"; - p = std::copy_n(s, 4, p); - - return res; -} - -std::string common_log_date(time_t t) { - struct tm tms; - - if (localtime_r(&t, &tms) == nullptr) { - return ""; - } - -#ifdef HAVE_STRUCT_TM_TM_GMTOFF - // Format data like this: - // 03/Jul/2014:00:19:38 +0900 - std::string res; - res.resize(26); - - auto p = std::begin(res); - - p = cpydig(p, tms.tm_mday, 2); - *p++ = '/'; - auto s = MONTH[tms.tm_mon]; - p = std::copy_n(s, 3, p); - *p++ = '/'; - p = cpydig(p, tms.tm_year + 1900, 4); - *p++ = ':'; - p = cpydig(p, tms.tm_hour, 2); - *p++ = ':'; - p = cpydig(p, tms.tm_min, 2); - *p++ = ':'; - p = cpydig(p, tms.tm_sec, 2); - *p++ = ' '; - - auto gmtoff = tms.tm_gmtoff; - if (gmtoff >= 0) { - *p++ = '+'; - } else { - *p++ = '-'; - gmtoff = -gmtoff; - } - - p = cpydig(p, gmtoff / 3600, 2); - p = cpydig(p, (gmtoff % 3600) / 60, 2); - - return res; -#else // !HAVE_STRUCT_TM_TM_GMTOFF - char buf[32]; - - strftime(buf, sizeof(buf), "%d/%b/%Y:%T %z", &tms); - - return buf; -#endif // !HAVE_STRUCT_TM_TM_GMTOFF -} - -std::string iso8601_date(int64_t ms) { - time_t sec = ms / 1000; - - tm tms; - if (localtime_r(&sec, &tms) == nullptr) { - return ""; - } - -#ifdef HAVE_STRUCT_TM_TM_GMTOFF - // Format data like this: - // 2014-11-15T12:58:24.741Z - // 2014-11-15T12:58:24.741+09:00 - std::string res; - res.resize(29); - - auto p = std::begin(res); - - p = cpydig(p, tms.tm_year + 1900, 4); - *p++ = '-'; - p = cpydig(p, tms.tm_mon + 1, 2); - *p++ = '-'; - p = cpydig(p, tms.tm_mday, 2); - *p++ = 'T'; - p = cpydig(p, tms.tm_hour, 2); - *p++ = ':'; - p = cpydig(p, tms.tm_min, 2); - *p++ = ':'; - p = cpydig(p, tms.tm_sec, 2); - *p++ = '.'; - p = cpydig(p, ms % 1000, 3); - - auto gmtoff = tms.tm_gmtoff; - if (gmtoff == 0) { - *p++ = 'Z'; - } else { - if (gmtoff > 0) { - *p++ = '+'; - } else { - *p++ = '-'; - gmtoff = -gmtoff; - } - p = cpydig(p, gmtoff / 3600, 2); - *p++ = ':'; - p = cpydig(p, (gmtoff % 3600) / 60, 2); - } - - res.resize(p - std::begin(res)); - - return res; -#else // !HAVE_STRUCT_TM_TM_GMTOFF - char buf[128]; - - auto nwrite = strftime(buf, sizeof(buf), "%Y-%m-%dT%H:%M:%S", &tms); - nwrite += snprintf(&buf[nwrite], sizeof(buf) - nwrite, ".%03d", - static_cast(ms % 1000)); - auto nzone = strftime(&buf[nwrite], sizeof(buf) - nwrite, "%z", &tms); - - // %z of strftime writes +hhmm or -hhmm not Z, +hh:mm or -hh:mm. Do - // %nothing if nzone is not 5. we don't know how to cope with this. - if (nzone == 5) { - if (memcmp(&buf[nwrite], "+0000", 5) == 0) { - // 0000 should be Z - memcpy(&buf[nwrite], "Z", 2); - } else { - // Move mm part to right by 1 including terminal \0 - memmove(&buf[nwrite + 4], &buf[nwrite + 3], 3); - // Insert ':' between hh and mm - buf[nwrite + 3] = ':'; - } - } - return buf; -#endif // !HAVE_STRUCT_TM_TM_GMTOFF -} - -time_t parse_http_date(const std::string &s) { - tm tm; - memset(&tm, 0, sizeof(tm)); - char *r = strptime(s.c_str(), "%a, %d %b %Y %H:%M:%S GMT", &tm); - if (r == 0) { - return 0; - } - return timegm(&tm); -} - -namespace { -void streq_advance(const char **ap, const char **bp) { - for (; **ap && **bp && lowcase(**ap) == lowcase(**bp); ++*ap, ++*bp) - ; -} -} // namespace - -bool istartsWith(const char *a, const char *b) { - if (!a || !b) { - return false; - } - streq_advance(&a, &b); - return !*b; -} - -bool strieq(const char *a, const char *b) { - if (!a || !b) { - return false; - } - for (; *a && *b && lowcase(*a) == lowcase(*b); ++a, ++b) - ; - return !*a && !*b; -} - -int strcompare(const char *a, const uint8_t *b, size_t bn) { - assert(a && b); - const uint8_t *blast = b + bn; - for (; *a && b != blast; ++a, ++b) { - if (*a < *b) { - return -1; - } else if (*a > *b) { - return 1; - } - } - if (!*a && b == blast) { - return 0; - } else if (b == blast) { - return 1; - } else { - return -1; - } -} - -bool strifind(const char *a, const char *b) { - if (!a || !b) { - return false; - } - for (size_t i = 0; a[i]; ++i) { - const char *ap = &a[i], *bp = b; - for (; *ap && *bp && lowcase(*ap) == lowcase(*bp); ++ap, ++bp) - ; - if (!*bp) { - return true; - } - } - return false; -} - -char upcase(char c) { - if ('a' <= c && c <= 'z') { - return c - 'a' + 'A'; - } else { - return c; - } -} - -namespace { -const char LOWER_XDIGITS[] = "0123456789abcdef"; -} // namespace - -std::string format_hex(const unsigned char *s, size_t len) { - std::string res; - res.resize(len * 2); - - for (size_t i = 0; i < len; ++i) { - unsigned char c = s[i]; - - res[i * 2] = LOWER_XDIGITS[c >> 4]; - res[i * 2 + 1] = LOWER_XDIGITS[c & 0x0f]; - } - return res; -} - -void to_token68(std::string &base64str) { - std::transform(std::begin(base64str), std::end(base64str), - std::begin(base64str), [](char c) { - switch (c) { - case '+': - return '-'; - case '/': - return '_'; - default: - return c; - } - }); - base64str.erase(std::find(std::begin(base64str), std::end(base64str), '='), - std::end(base64str)); -} - -void to_base64(std::string &token68str) { - std::transform(std::begin(token68str), std::end(token68str), - std::begin(token68str), [](char c) { - switch (c) { - case '-': - return '+'; - case '_': - return '/'; - default: - return c; - } - }); - if (token68str.size() & 0x3) { - token68str.append(4 - (token68str.size() & 0x3), '='); - } - return; -} - -namespace { -// Calculates Damerau–Levenshtein distance between c-string a and b -// with given costs. swapcost, subcost, addcost and delcost are cost -// to swap 2 adjacent characters, substitute characters, add character -// and delete character respectively. -int levenshtein(const char *a, int alen, const char *b, int blen, int swapcost, - int subcost, int addcost, int delcost) { - auto dp = std::vector>(3, std::vector(blen + 1)); - for (int i = 0; i <= blen; ++i) { - dp[1][i] = i; - } - for (int i = 1; i <= alen; ++i) { - dp[0][0] = i; - for (int j = 1; j <= blen; ++j) { - dp[0][j] = dp[1][j - 1] + (a[i - 1] == b[j - 1] ? 0 : subcost); - if (i >= 2 && j >= 2 && a[i - 1] != b[j - 1] && a[i - 2] == b[j - 1] && - a[i - 1] == b[j - 2]) { - dp[0][j] = std::min(dp[0][j], dp[2][j - 2] + swapcost); - } - dp[0][j] = std::min(dp[0][j], - std::min(dp[1][j] + delcost, dp[0][j - 1] + addcost)); - } - std::rotate(std::begin(dp), std::begin(dp) + 2, std::end(dp)); - } - return dp[1][blen]; -} -} // namespace - -void show_candidates(const char *unkopt, option *options) { - for (; *unkopt == '-'; ++unkopt) - ; - if (*unkopt == '\0') { - return; - } - auto unkoptend = unkopt; - for (; *unkoptend && *unkoptend != '='; ++unkoptend) - ; - auto unkoptlen = unkoptend - unkopt; - if (unkoptlen == 0) { - return; - } - int prefix_match = 0; - auto cands = std::vector>(); - for (size_t i = 0; options[i].name != nullptr; ++i) { - auto optnamelen = strlen(options[i].name); - // Use cost 0 for prefix match - if (istartsWith(options[i].name, options[i].name + optnamelen, unkopt, - unkopt + unkoptlen)) { - if (optnamelen == static_cast(unkoptlen)) { - // Exact match, then we don't show any condidates. - return; - } - ++prefix_match; - cands.emplace_back(0, options[i].name); - continue; - } - // Use cost 0 for suffix match, but match at least 3 characters - if (unkoptlen >= 3 && - iendsWith(options[i].name, options[i].name + optnamelen, unkopt, - unkopt + unkoptlen)) { - cands.emplace_back(0, options[i].name); - continue; - } - // cost values are borrowed from git, help.c. - int sim = - levenshtein(unkopt, unkoptlen, options[i].name, optnamelen, 0, 2, 1, 3); - cands.emplace_back(sim, options[i].name); - } - if (prefix_match == 1 || cands.empty()) { - return; - } - std::sort(std::begin(cands), std::end(cands)); - int threshold = cands[0].first; - // threshold value is a magic value. - if (threshold > 6) { - return; - } - std::cerr << "\nDid you mean:\n"; - for (auto &item : cands) { - if (item.first > threshold) { - break; - } - std::cerr << "\t--" << item.second << "\n"; - } -} - -bool has_uri_field(const http_parser_url &u, http_parser_url_fields field) { - return u.field_set & (1 << field); -} - -bool fieldeq(const char *uri1, const http_parser_url &u1, const char *uri2, - const http_parser_url &u2, http_parser_url_fields field) { - if (!has_uri_field(u1, field)) { - if (!has_uri_field(u2, field)) { - return true; - } else { - return false; - } - } else if (!has_uri_field(u2, field)) { - return false; - } - if (u1.field_data[field].len != u2.field_data[field].len) { - return false; - } - return memcmp(uri1 + u1.field_data[field].off, - uri2 + u2.field_data[field].off, u1.field_data[field].len) == 0; -} - -bool fieldeq(const char *uri, const http_parser_url &u, - http_parser_url_fields field, const char *t) { - if (!has_uri_field(u, field)) { - if (!t[0]) { - return true; - } else { - return false; - } - } else if (!t[0]) { - return false; - } - int i, len = u.field_data[field].len; - const char *p = uri + u.field_data[field].off; - for (i = 0; i < len && t[i] && p[i] == t[i]; ++i) - ; - return i == len && !t[i]; -} - -std::string get_uri_field(const char *uri, const http_parser_url &u, - http_parser_url_fields field) { - if (util::has_uri_field(u, field)) { - return std::string(uri + u.field_data[field].off, u.field_data[field].len); - } else { - return ""; - } -} - -uint16_t get_default_port(const char *uri, const http_parser_url &u) { - if (util::fieldeq(uri, u, UF_SCHEMA, "https")) { - return 443; - } else if (util::fieldeq(uri, u, UF_SCHEMA, "http")) { - return 80; - } else { - return 443; - } -} - -bool porteq(const char *uri1, const http_parser_url &u1, const char *uri2, - const http_parser_url &u2) { - uint16_t port1, port2; - port1 = - util::has_uri_field(u1, UF_PORT) ? u1.port : get_default_port(uri1, u1); - port2 = - util::has_uri_field(u2, UF_PORT) ? u2.port : get_default_port(uri2, u2); - return port1 == port2; -} - -void write_uri_field(std::ostream &o, const char *uri, const http_parser_url &u, - http_parser_url_fields field) { - if (util::has_uri_field(u, field)) { - o.write(uri + u.field_data[field].off, u.field_data[field].len); - } -} - -bool numeric_host(const char *hostname) { - struct addrinfo hints; - struct addrinfo *res; - memset(&hints, 0, sizeof(hints)); - hints.ai_family = AF_UNSPEC; - hints.ai_flags = AI_NUMERICHOST; - if (getaddrinfo(hostname, nullptr, &hints, &res)) { - return false; - } - freeaddrinfo(res); - return true; -} - -std::string numeric_name(const struct sockaddr *sa, socklen_t salen) { - std::array host; - auto rv = getnameinfo(sa, salen, host.data(), host.size(), nullptr, 0, - NI_NUMERICHOST); - if (rv != 0) { - return "unknown"; - } - return host.data(); -} - -int reopen_log_file(const char *path) { -#if defined(__ANDROID__) || defined(ANDROID) - int fd; - - if (strcmp("/proc/self/fd/1", path) == 0 || - strcmp("/proc/self/fd/2", path) == 0) { - - // We will get permission denied error when O_APPEND is used for - // these paths. - fd = - open(path, O_WRONLY | O_CREAT | O_CLOEXEC, S_IRUSR | S_IWUSR | S_IRGRP); - } else { - fd = open(path, O_WRONLY | O_APPEND | O_CREAT | O_CLOEXEC, - S_IRUSR | S_IWUSR | S_IRGRP); - } -#elif defined O_CLOEXEC - - auto fd = open(path, O_WRONLY | O_APPEND | O_CREAT | O_CLOEXEC, - S_IRUSR | S_IWUSR | S_IRGRP); -#else // !O_CLOEXEC - - auto fd = - open(path, O_WRONLY | O_APPEND | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP); - - // We get race condition if execve is called at the same time. - if (fd != -1) { - make_socket_closeonexec(fd); - } - -#endif // !O_CLOEXEC - - if (fd == -1) { - return -1; - } - - return fd; -} - -std::string ascii_dump(const uint8_t *data, size_t len) { - std::string res; - - for (size_t i = 0; i < len; ++i) { - auto c = data[i]; - - if (c >= 0x20 && c < 0x7f) { - res += c; - } else { - res += "."; - } - } - - return res; -} - -char *get_exec_path(int argc, char **const argv, const char *cwd) { - if (argc == 0 || cwd == nullptr) { - return nullptr; - } - - auto argv0 = argv[0]; - auto len = strlen(argv0); - - char *path; - - if (argv0[0] == '/') { - path = static_cast(malloc(len + 1)); - if (path == nullptr) { - return nullptr; - } - memcpy(path, argv0, len + 1); - } else { - auto cwdlen = strlen(cwd); - path = static_cast(malloc(len + 1 + cwdlen + 1)); - if (path == nullptr) { - return nullptr; - } - memcpy(path, cwd, cwdlen); - path[cwdlen] = '/'; - memcpy(path + cwdlen + 1, argv0, len + 1); - } - - return path; -} - -bool check_path(const std::string &path) { - // We don't like '\' in path. - return !path.empty() && path[0] == '/' && - path.find('\\') == std::string::npos && - path.find("/../") == std::string::npos && - path.find("/./") == std::string::npos && - !util::endsWith(path, "/..") && !util::endsWith(path, "/."); -} - -int64_t to_time64(const timeval &tv) { - return tv.tv_sec * 1000000 + tv.tv_usec; -} - -bool check_h2_is_selected(const unsigned char *proto, size_t len) { - return streq_l(NGHTTP2_PROTO_VERSION_ID, proto, len) || - streq_l(NGHTTP2_H2_16, proto, len) || - streq_l(NGHTTP2_H2_14, proto, len); -} - -namespace { -bool select_h2(const unsigned char **out, unsigned char *outlen, - const unsigned char *in, unsigned int inlen, const char *key, - unsigned int keylen) { - for (auto p = in, end = in + inlen; p + keylen <= end; p += *p + 1) { - if (std::equal(key, key + keylen, p)) { - *out = p + 1; - *outlen = *p; - return true; - } - } - return false; -} -} // namespace - -bool select_h2(const unsigned char **out, unsigned char *outlen, - const unsigned char *in, unsigned int inlen) { - return select_h2(out, outlen, in, inlen, NGHTTP2_PROTO_ALPN, - str_size(NGHTTP2_PROTO_ALPN)) || - select_h2(out, outlen, in, inlen, NGHTTP2_H2_16_ALPN, - str_size(NGHTTP2_H2_16_ALPN)) || - select_h2(out, outlen, in, inlen, NGHTTP2_H2_14_ALPN, - str_size(NGHTTP2_H2_14_ALPN)); -} - -std::vector get_default_alpn() { - auto res = std::vector(str_size(NGHTTP2_PROTO_ALPN) + - str_size(NGHTTP2_H2_16_ALPN) + - str_size(NGHTTP2_H2_14_ALPN)); - auto p = std::begin(res); - - p = std::copy_n(NGHTTP2_PROTO_ALPN, str_size(NGHTTP2_PROTO_ALPN), p); - p = std::copy_n(NGHTTP2_H2_16_ALPN, str_size(NGHTTP2_H2_16_ALPN), p); - p = std::copy_n(NGHTTP2_H2_14_ALPN, str_size(NGHTTP2_H2_14_ALPN), p); - - return res; -} - -int make_socket_closeonexec(int fd) { - int flags; - int rv; - while ((flags = fcntl(fd, F_GETFD)) == -1 && errno == EINTR) - ; - while ((rv = fcntl(fd, F_SETFD, flags | FD_CLOEXEC)) == -1 && errno == EINTR) - ; - return rv; -} - -int make_socket_nonblocking(int fd) { - int flags; - int rv; - while ((flags = fcntl(fd, F_GETFL, 0)) == -1 && errno == EINTR) - ; - while ((rv = fcntl(fd, F_SETFL, flags | O_NONBLOCK)) == -1 && errno == EINTR) - ; - return rv; -} - -int make_socket_nodelay(int fd) { - int val = 1; - if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, reinterpret_cast(&val), - sizeof(val)) == -1) { - return -1; - } - return 0; -} - -int create_nonblock_socket(int family) { -#ifdef SOCK_NONBLOCK - auto fd = socket(family, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0); - - if (fd == -1) { - return -1; - } -#else // !SOCK_NONBLOCK - auto fd = socket(family, SOCK_STREAM, 0); - - if (fd == -1) { - return -1; - } - - make_socket_nonblocking(fd); - make_socket_closeonexec(fd); -#endif // !SOCK_NONBLOCK - - if (family == AF_INET || family == AF_INET6) { - make_socket_nodelay(fd); - } - - return fd; -} - -bool check_socket_connected(int fd) { - int error; - socklen_t len = sizeof(error); - if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &error, &len) == 0) { - if (error != 0) { - return false; - } - } - return true; -} - -bool ipv6_numeric_addr(const char *host) { - uint8_t dst[16]; - return inet_pton(AF_INET6, host, dst) == 1; -} - -namespace { -std::pair parse_uint_digits(const void *ss, size_t len) { - const uint8_t *s = static_cast(ss); - int64_t n = 0; - size_t i; - if (len == 0) { - return {-1, 0}; - } - constexpr int64_t max = std::numeric_limits::max(); - for (i = 0; i < len; ++i) { - if ('0' <= s[i] && s[i] <= '9') { - if (n > max / 10) { - return {-1, 0}; - } - n *= 10; - if (n > max - (s[i] - '0')) { - return {-1, 0}; - } - n += s[i] - '0'; - continue; - } - break; - } - if (i == 0) { - return {-1, 0}; - } - return {n, i}; -} -} // namespace - -int64_t parse_uint_with_unit(const char *s) { - int64_t n; - size_t i; - auto len = strlen(s); - std::tie(n, i) = parse_uint_digits(s, len); - if (n == -1) { - return -1; - } - if (i == len) { - return n; - } - if (i + 1 != len) { - return -1; - } - int mul = 1; - switch (s[i]) { - case 'K': - case 'k': - mul = 1 << 10; - break; - case 'M': - case 'm': - mul = 1 << 20; - break; - case 'G': - case 'g': - mul = 1 << 30; - break; - default: - return -1; - } - constexpr int64_t max = std::numeric_limits::max(); - if (n > max / mul) { - return -1; - } - return n * mul; -} - -int64_t parse_uint(const char *s) { - return parse_uint(reinterpret_cast(s), strlen(s)); -} - -int64_t parse_uint(const std::string &s) { - return parse_uint(reinterpret_cast(s.c_str()), s.size()); -} - -int64_t parse_uint(const uint8_t *s, size_t len) { - int64_t n; - size_t i; - std::tie(n, i) = parse_uint_digits(s, len); - if (n == -1 || i != len) { - return -1; - } - return n; -} - -double parse_duration_with_unit(const char *s) { - constexpr auto max = std::numeric_limits::max(); - int64_t n; - size_t i; - auto len = strlen(s); - std::tie(n, i) = parse_uint_digits(s, len); - if (n == -1) { - goto fail; - } - if (i == len) { - return static_cast(n); - } - switch (s[i]) { - case 'S': - case 's': - // seconds - if (i + 1 != len) { - goto fail; - } - return static_cast(n); - case 'M': - case 'm': - if (i + 1 == len) { - // minutes - if (n > max / 60) { - goto fail; - } - return static_cast(n) * 60; - } - - if (i + 2 != len || (s[i + 1] != 's' && s[i + 1] != 'S')) { - goto fail; - } - // milliseconds - return static_cast(n) / 1000.; - case 'H': - case 'h': - // hours - if (i + 1 != len) { - goto fail; - } - if (n > max / 3600) { - goto fail; - } - return static_cast(n) * 3600; - } -fail: - return std::numeric_limits::infinity(); -} - -std::string duration_str(double t) { - if (t == 0.) { - return "0"; - } - auto frac = static_cast(t * 1000) % 1000; - if (frac > 0) { - return utos(static_cast(t * 1000)) + "ms"; - } - auto v = static_cast(t); - if (v % 60) { - return utos(v) + "s"; - } - v /= 60; - if (v % 60) { - return utos(v) + "m"; - } - v /= 60; - return utos(v) + "h"; -} - -std::string format_duration(const std::chrono::microseconds &u) { - const char *unit = "us"; - int d = 0; - auto t = u.count(); - if (t >= 1000000) { - d = 1000000; - unit = "s"; - } else if (t >= 1000) { - d = 1000; - unit = "ms"; - } else { - return utos(t) + unit; - } - return dtos(static_cast(t) / d) + unit; -} - -std::string dtos(double n) { - auto f = utos(static_cast(round(100. * n)) % 100); - return utos(static_cast(n)) + "." + (f.size() == 1 ? "0" : "") + f; -} - -std::string make_hostport(const char *host, uint16_t port) { - auto ipv6 = ipv6_numeric_addr(host); - std::string hostport; - - if (ipv6) { - hostport += "["; - } - - hostport += host; - - if (ipv6) { - hostport += "]"; - } - - if (port != 80 && port != 443) { - hostport += ":"; - hostport += utos(port); - } - - return hostport; -} - -namespace { -void hexdump8(FILE *out, const uint8_t *first, const uint8_t *last) { - auto stop = std::min(first + 8, last); - for (auto k = first; k != stop; ++k) { - fprintf(out, "%02x ", *k); - } - // each byte needs 3 spaces (2 hex value and space) - for (; stop != first + 8; ++stop) { - fputs(" ", out); - } - // we have extra space after 8 bytes - fputc(' ', out); -} -} // namespace - -void hexdump(FILE *out, const uint8_t *src, size_t len) { - if (len == 0) { - return; - } - size_t buflen = 0; - auto repeated = false; - std::array buf{}; - auto end = src + len; - auto i = src; - for (;;) { - auto nextlen = - std::min(static_cast(16), static_cast(end - i)); - if (nextlen == buflen && - std::equal(std::begin(buf), std::begin(buf) + buflen, i)) { - // as long as adjacent 16 bytes block are the same, we just - // print single '*'. - if (!repeated) { - repeated = true; - fputs("*\n", out); - } - i += nextlen; - continue; - } - repeated = false; - fprintf(out, "%08lx", static_cast(i - src)); - if (i == end) { - fputc('\n', out); - break; - } - fputs(" ", out); - hexdump8(out, i, end); - hexdump8(out, i + 8, std::max(i + 8, end)); - fputc('|', out); - auto stop = std::min(i + 16, end); - buflen = stop - i; - auto p = buf.data(); - for (; i != stop; ++i) { - *p++ = *i; - if (0x20 <= *i && *i <= 0x7e) { - fputc(*i, out); - } else { - fputc('.', out); - } - } - fputs("|\n", out); - } -} - -} // namespace util - -} // namespace nghttp2 diff --git a/src/util.h b/src/util.h deleted file mode 100644 index 88e1ff5..0000000 --- a/src/util.h +++ /dev/null @@ -1,626 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2012 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#ifndef UTIL_H -#define UTIL_H - -#include "nghttp2_config.h" - -#ifdef HAVE_UNISTD_H -#include -#endif // HAVE_UNISTD_H -#include -#ifdef HAVE_NETDB_H -#include -#endif // HAVE_NETDB_H - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "http-parser/http_parser.h" - -namespace nghttp2 { - -// The additional HTTP/2 protocol ALPN protocol identifier we also -// supports for our applications to make smooth migration into final -// h2 ALPN ID. -#define NGHTTP2_H2_16_ALPN "\x5h2-16" -#define NGHTTP2_H2_16 "h2-16" - -#define NGHTTP2_H2_14_ALPN "\x5h2-14" -#define NGHTTP2_H2_14 "h2-14" - -namespace util { - -extern const char DEFAULT_STRIP_CHARSET[]; - -template -std::pair -stripIter(InputIterator first, InputIterator last, - const char *chars = DEFAULT_STRIP_CHARSET) { - for (; first != last && strchr(chars, *first) != 0; ++first) - ; - if (first == last) { - return std::make_pair(first, last); - } - InputIterator left = last - 1; - for (; left != first && strchr(chars, *left) != 0; --left) - ; - return std::make_pair(first, left + 1); -} - -template -OutputIterator splitIter(InputIterator first, InputIterator last, - OutputIterator out, char delim, bool doStrip = false, - bool allowEmpty = false) { - for (InputIterator i = first; i != last;) { - InputIterator j = std::find(i, last, delim); - std::pair p(i, j); - if (doStrip) { - p = stripIter(i, j); - } - if (allowEmpty || p.first != p.second) { - *out++ = p; - } - i = j; - if (j != last) { - ++i; - } - } - if (allowEmpty && (first == last || *(last - 1) == delim)) { - *out++ = std::make_pair(last, last); - } - return out; -} - -template -OutputIterator split(InputIterator first, InputIterator last, - OutputIterator out, char delim, bool doStrip = false, - bool allowEmpty = false) { - for (InputIterator i = first; i != last;) { - InputIterator j = std::find(i, last, delim); - std::pair p(i, j); - if (doStrip) { - p = stripIter(i, j); - } - if (allowEmpty || p.first != p.second) { - *out++ = std::string(p.first, p.second); - } - i = j; - if (j != last) { - ++i; - } - } - if (allowEmpty && (first == last || *(last - 1) == delim)) { - *out++ = std::string(last, last); - } - return out; -} - -template -std::string strjoin(InputIterator first, InputIterator last, - const DelimiterType &delim) { - std::string result; - if (first == last) { - return result; - } - InputIterator beforeLast = last - 1; - for (; first != beforeLast; ++first) { - result += *first; - result += delim; - } - result += *beforeLast; - return result; -} - -template -std::string joinPath(InputIterator first, InputIterator last) { - std::vector elements; - for (; first != last; ++first) { - if (*first == "..") { - if (!elements.empty()) { - elements.pop_back(); - } - } else if (*first == ".") { - // do nothing - } else { - elements.push_back(*first); - } - } - return strjoin(elements.begin(), elements.end(), "/"); -} - -bool isAlpha(const char c); - -bool isDigit(const char c); - -bool isHexDigit(const char c); - -bool inRFC3986UnreservedChars(const char c); - -bool in_rfc3986_sub_delims(const char c); - -// Returns true if |c| is in token (HTTP-p1, Section 3.2.6) -bool in_token(char c); - -bool in_attr_char(char c); - -// Returns integer corresponding to hex notation |c|. It is undefined -// if isHexDigit(c) is false. -uint32_t hex_to_uint(char c); - -std::string percentEncode(const unsigned char *target, size_t len); - -std::string percentEncode(const std::string &target); - -// percent-encode path component of URI |s|. -std::string percent_encode_path(const std::string &s); - -template -std::string percentDecode(InputIt first, InputIt last) { - std::string result; - for (; first != last; ++first) { - if (*first == '%') { - if (first + 1 != last && first + 2 != last && isHexDigit(*(first + 1)) && - isHexDigit(*(first + 2))) { - result += (hex_to_uint(*(first + 1)) << 4) + hex_to_uint(*(first + 2)); - first += 2; - continue; - } - result += *first; - continue; - } - result += *first; - } - return result; -} - -// Percent encode |target| if character is not in token or '%'. -std::string percent_encode_token(const std::string &target); - -// Returns quotedString version of |target|. Currently, this function -// just replace '"' with '\"'. -std::string quote_string(const std::string &target); - -std::string format_hex(const unsigned char *s, size_t len); - -std::string http_date(time_t t); - -// Returns given time |t| from epoch in Common Log format (e.g., -// 03/Jul/2014:00:19:38 +0900) -std::string common_log_date(time_t t); - -// Returns given millisecond |ms| from epoch in ISO 8601 format (e.g., -// 2014-11-15T12:58:24.741Z) -std::string iso8601_date(int64_t ms); - -time_t parse_http_date(const std::string &s); - -char upcase(char c); - -inline char lowcase(char c) { - static unsigned char tbl[] = { - 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, 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, 52, 53, 54, 55, 56, 57, 58, 59, - 60, 61, 62, 63, 64, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', - 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', - 'z', 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, - 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, - 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, - 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, - 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, - 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, - 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, - 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, - 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, - 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, - 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, - 255, - }; - return tbl[static_cast(c)]; -} - -template -bool startsWith(InputIterator1 first1, InputIterator1 last1, - InputIterator2 first2, InputIterator2 last2) { - if (last1 - first1 < last2 - first2) { - return false; - } - return std::equal(first2, last2, first1); -} - -inline bool startsWith(const std::string &a, const std::string &b) { - return startsWith(std::begin(a), std::end(a), std::begin(b), std::end(b)); -} - -inline bool startsWith(const char *a, const char *b) { - return startsWith(a, a + strlen(a), b, b + strlen(b)); -} - -struct CaseCmp { - bool operator()(char lhs, char rhs) const { - return lowcase(lhs) == lowcase(rhs); - } -}; - -template -bool istartsWith(InputIterator1 first1, InputIterator1 last1, - InputIterator2 first2, InputIterator2 last2) { - if (last1 - first1 < last2 - first2) { - return false; - } - return std::equal(first2, last2, first1, CaseCmp()); -} - -inline bool istartsWith(const std::string &a, const std::string &b) { - return istartsWith(std::begin(a), std::end(a), std::begin(b), std::end(b)); -} - -template -bool istartsWith(InputIt a, size_t an, const char *b) { - return istartsWith(a, a + an, b, b + strlen(b)); -} - -bool istartsWith(const char *a, const char *b); - -template -bool endsWith(InputIterator1 first1, InputIterator1 last1, - InputIterator2 first2, InputIterator2 last2) { - if (last1 - first1 < last2 - first2) { - return false; - } - return std::equal(first2, last2, last1 - (last2 - first2)); -} - -inline bool endsWith(const std::string &a, const std::string &b) { - return endsWith(std::begin(a), std::end(a), std::begin(b), std::end(b)); -} - -template -bool iendsWith(InputIterator1 first1, InputIterator1 last1, - InputIterator2 first2, InputIterator2 last2) { - if (last1 - first1 < last2 - first2) { - return false; - } - return std::equal(first2, last2, last1 - (last2 - first2), CaseCmp()); -} - -inline bool iendsWith(const std::string &a, const std::string &b) { - return iendsWith(std::begin(a), std::end(a), std::begin(b), std::end(b)); -} - -int strcompare(const char *a, const uint8_t *b, size_t n); - -template bool strieq(const char *a, InputIt b, size_t bn) { - if (!a) { - return false; - } - auto blast = b + bn; - for (; *a && b != blast && lowcase(*a) == lowcase(*b); ++a, ++b) - ; - return !*a && b == blast; -} - -template -bool strieq(InputIt1 a, size_t alen, InputIt2 b, size_t blen) { - if (alen != blen) { - return false; - } - return std::equal(a, a + alen, b, CaseCmp()); -} - -inline bool strieq(const std::string &a, const std::string &b) { - return strieq(std::begin(a), a.size(), std::begin(b), b.size()); -} - -bool strieq(const char *a, const char *b); - -template -bool strieq_l(const char (&a)[N], InputIt b, size_t blen) { - return strieq(a, N - 1, b, blen); -} - -template bool strieq_l(const char (&a)[N], const std::string &b) { - return strieq(a, N - 1, std::begin(b), b.size()); -} - -template bool streq(const char *a, InputIt b, size_t bn) { - if (!a) { - return false; - } - auto blast = b + bn; - for (; *a && b != blast && *a == *b; ++a, ++b) - ; - return !*a && b == blast; -} - -template -bool streq(InputIt1 a, size_t alen, InputIt2 b, size_t blen) { - if (alen != blen) { - return false; - } - return std::equal(a, a + alen, b); -} - -inline bool streq(const char *a, const char *b) { - if (!a || !b) { - return false; - } - return streq(a, strlen(a), b, strlen(b)); -} - -template -bool streq_l(const char (&a)[N], InputIt b, size_t blen) { - return streq(a, N - 1, b, blen); -} - -bool strifind(const char *a, const char *b); - -// Lowercase |s| in place. -inline void inp_strlower(std::string &s) { - std::transform(std::begin(s), std::end(s), std::begin(s), lowcase); -} - -// Returns string representation of |n| with 2 fractional digits. -std::string dtos(double n); - -template std::string utos(T n) { - std::string res; - if (n == 0) { - res = "0"; - return res; - } - int i = 0; - T t = n; - for (; t; t /= 10, ++i) - ; - res.resize(i); - --i; - for (; n; --i, n /= 10) { - res[i] = (n % 10) + '0'; - } - return res; -} - -template std::string utos_with_unit(T n) { - char u = 0; - if (n >= (1 << 30)) { - u = 'G'; - n /= (1 << 30); - } else if (n >= (1 << 20)) { - u = 'M'; - n /= (1 << 20); - } else if (n >= (1 << 10)) { - u = 'K'; - n /= (1 << 10); - } - if (u == 0) { - return utos(n); - } - return utos(n) + u; -} - -// Like utos_with_unit(), but 2 digits fraction part is followed. -template std::string utos_with_funit(T n) { - char u = 0; - int b = 0; - if (n >= (1 << 30)) { - u = 'G'; - b = 30; - } else if (n >= (1 << 20)) { - u = 'M'; - b = 20; - } else if (n >= (1 << 10)) { - u = 'K'; - b = 10; - } - if (b == 0) { - return utos(n); - } - return dtos(static_cast(n) / (1 << b)) + u; -} - -extern const char UPPER_XDIGITS[]; - -template std::string utox(T n) { - std::string res; - if (n == 0) { - res = "0"; - return res; - } - int i = 0; - T t = n; - for (; t; t /= 16, ++i) - ; - res.resize(i); - --i; - for (; n; --i, n /= 16) { - res[i] = UPPER_XDIGITS[(n & 0x0f)]; - } - return res; -} - -void to_token68(std::string &base64str); -void to_base64(std::string &token68str); - -void show_candidates(const char *unkopt, option *options); - -bool has_uri_field(const http_parser_url &u, http_parser_url_fields field); - -bool fieldeq(const char *uri1, const http_parser_url &u1, const char *uri2, - const http_parser_url &u2, http_parser_url_fields field); - -bool fieldeq(const char *uri, const http_parser_url &u, - http_parser_url_fields field, const char *t); - -std::string get_uri_field(const char *uri, const http_parser_url &u, - http_parser_url_fields field); - -uint16_t get_default_port(const char *uri, const http_parser_url &u); - -bool porteq(const char *uri1, const http_parser_url &u1, const char *uri2, - const http_parser_url &u2); - -void write_uri_field(std::ostream &o, const char *uri, const http_parser_url &u, - http_parser_url_fields field); - -bool numeric_host(const char *hostname); - -// Returns numeric address string of |addr|. If getnameinfo() is -// failed, "unknown" is returned. -std::string numeric_name(const struct sockaddr *sa, socklen_t salen); - -// Opens |path| with O_APPEND enabled. If file does not exist, it is -// created first. This function returns file descriptor referring the -// opened file if it succeeds, or -1. -int reopen_log_file(const char *path); - -// Returns ASCII dump of |data| of length |len|. Only ASCII printable -// characters are preserved. Other characters are replaced with ".". -std::string ascii_dump(const uint8_t *data, size_t len); - -// Returns absolute path of executable path. If argc == 0 or |cwd| is -// nullptr, this function returns nullptr. If argv[0] starts with -// '/', this function returns argv[0]. Oterwise return cwd + "/" + -// argv[0]. If non-null is returned, it is NULL-terminated string and -// dynamically allocated by malloc. The caller is responsible to free -// it. -char *get_exec_path(int argc, char **const argv, const char *cwd); - -// Validates path so that it does not contain directory traversal -// vector. Returns true if path is safe. The |path| must start with -// "/" otherwise returns false. This function should be called after -// percent-decode was performed. -bool check_path(const std::string &path); - -// Returns the |tv| value as 64 bit integer using a microsecond as an -// unit. -int64_t to_time64(const timeval &tv); - -// Returns true if ALPN ID |proto| of length |len| is supported HTTP/2 -// protocol identifier. -bool check_h2_is_selected(const unsigned char *alpn, size_t len); - -// Selects h2 protocol ALPN ID if one of supported h2 versions are -// present in |in| of length inlen. Returns true if h2 version is -// selected. -bool select_h2(const unsigned char **out, unsigned char *outlen, - const unsigned char *in, unsigned int inlen); - -// Returns default ALPN protocol list, which only contains supported -// HTTP/2 protocol identifier. -std::vector get_default_alpn(); - -// Returns given time |tp| in Common Log format (e.g., -// 03/Jul/2014:00:19:38 +0900) -// Expected type of |tp| is std::chrono::timepoint -template std::string format_common_log(const T &tp) { - auto t = - std::chrono::duration_cast(tp.time_since_epoch()); - return common_log_date(t.count()); -} - -// Returns given time |tp| in ISO 8601 format (e.g., -// 2014-11-15T12:58:24.741Z) -// Expected type of |tp| is std::chrono::timepoint -template std::string format_iso8601(const T &tp) { - auto t = std::chrono::duration_cast( - tp.time_since_epoch()); - return iso8601_date(t.count()); -} - -// Return the system precision of the template parameter |Clock| as -// a nanosecond value of type |Rep| -template Rep clock_precision() { - std::chrono::duration duration = typename Clock::duration(1); - - return duration.count(); -} - -int make_socket_closeonexec(int fd); -int make_socket_nonblocking(int fd); -int make_socket_nodelay(int fd); - -int create_nonblock_socket(int family); - -bool check_socket_connected(int fd); - -// Returns true if |host| is IPv6 numeric address (e.g., ::1) -bool ipv6_numeric_addr(const char *host); - -// Parses NULL terminated string |s| as unsigned integer and returns -// the parsed integer. Additionally, if |s| ends with 'k', 'm', 'g' -// and its upper case characters, multiply the integer by 1024, 1024 * -// 1024 and 1024 * 1024 respectively. If there is an error, returns -// -1. -int64_t parse_uint_with_unit(const char *s); - -// Parses NULL terminated string |s| as unsigned integer and returns -// the parsed integer. If there is an error, returns -1. -int64_t parse_uint(const char *s); -int64_t parse_uint(const uint8_t *s, size_t len); -int64_t parse_uint(const std::string &s); - -// Parses NULL terminated string |s| as unsigned integer and returns -// the parsed integer casted to double. If |s| ends with "s", the -// parsed value's unit is a second. If |s| ends with "ms", the unit -// is millisecond. If none of them are given, the unit is second. -// This function returns std::numeric_limits::infinity() if -// error occurs. -double parse_duration_with_unit(const char *s); - -// Returns string representation of time duration |t|. If t has -// fractional part (at least more than or equal to 1e-3), |t| is -// multiplied by 1000 and the unit "ms" is appended. Otherwise, |t| -// is left as is and "s" is appended. -std::string duration_str(double t); - -// Returns string representation of time duration |t|. It appends -// unit after the formatting. The available units are s, ms and us. -// The unit which is equal to or less than |t| is used and 2 -// fractional digits follow. -std::string format_duration(const std::chrono::microseconds &u); - -// Creates "host:port" string using given |host| and |port|. If -// |host| is numeric IPv6 address (e.g., ::1), it is enclosed by "[" -// and "]". If |port| is 80 or 443, port part is omitted. -std::string make_hostport(const char *host, uint16_t port); - -// Dumps |src| of length |len| in the format similar to `hexdump -C`. -void hexdump(FILE *out, const uint8_t *src, size_t len); - -} // namespace util - -} // namespace nghttp2 - -#endif // UTIL_H diff --git a/src/util_test.cc b/src/util_test.cc deleted file mode 100644 index c1100b7..0000000 --- a/src/util_test.cc +++ /dev/null @@ -1,370 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2013 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#include "util_test.h" - -#include -#include - -#include - -#include - -#include "util.h" -#include "template.h" - -using namespace nghttp2; - -namespace shrpx { - -void test_util_streq(void) { - CU_ASSERT(util::streq("alpha", "alpha", 5)); - CU_ASSERT(util::streq("alpha", "alphabravo", 5)); - CU_ASSERT(!util::streq("alpha", "alphabravo", 6)); - CU_ASSERT(!util::streq("alphabravo", "alpha", 5)); - CU_ASSERT(!util::streq("alpha", "alphA", 5)); - CU_ASSERT(!util::streq("", "a", 1)); - CU_ASSERT(util::streq("", "", 0)); - CU_ASSERT(!util::streq("alpha", "", 0)); - - CU_ASSERT(util::streq("alpha", 5, "alpha", 5)); - CU_ASSERT(!util::streq("alpha", 4, "alpha", 5)); - CU_ASSERT(!util::streq("alpha", 5, "alpha", 4)); - CU_ASSERT(!util::streq("alpha", 5, "alphA", 5)); - char *a = nullptr; - char *b = nullptr; - CU_ASSERT(util::streq(a, 0, b, 0)); - - CU_ASSERT(util::streq_l("alpha", "alpha", 5)); - CU_ASSERT(util::streq_l("alpha", "alphabravo", 5)); - CU_ASSERT(!util::streq_l("alpha", "alphabravo", 6)); - CU_ASSERT(!util::streq_l("alphabravo", "alpha", 5)); - CU_ASSERT(!util::streq_l("alpha", "alphA", 5)); - CU_ASSERT(!util::streq_l("", "a", 1)); - CU_ASSERT(util::streq_l("", "", 0)); - CU_ASSERT(!util::streq_l("alpha", "", 0)); -} - -void test_util_strieq(void) { - CU_ASSERT(util::strieq(std::string("alpha"), std::string("alpha"))); - CU_ASSERT(util::strieq(std::string("alpha"), std::string("AlPhA"))); - CU_ASSERT(util::strieq(std::string(), std::string())); - CU_ASSERT(!util::strieq(std::string("alpha"), std::string("AlPhA "))); - CU_ASSERT(!util::strieq(std::string(), std::string("AlPhA "))); - - CU_ASSERT(util::strieq("alpha", "alpha", 5)); - CU_ASSERT(util::strieq("alpha", "AlPhA", 5)); - CU_ASSERT(util::strieq("", static_cast(nullptr), 0)); - CU_ASSERT(!util::strieq("alpha", "AlPhA ", 6)); - CU_ASSERT(!util::strieq("", "AlPhA ", 6)); - - CU_ASSERT(util::strieq("alpha", "alpha")); - CU_ASSERT(util::strieq("alpha", "AlPhA")); - CU_ASSERT(util::strieq("", "")); - CU_ASSERT(!util::strieq("alpha", "AlPhA ")); - CU_ASSERT(!util::strieq("", "AlPhA ")); - - CU_ASSERT(util::strieq_l("alpha", "alpha", 5)); - CU_ASSERT(util::strieq_l("alpha", "AlPhA", 5)); - CU_ASSERT(util::strieq_l("", static_cast(nullptr), 0)); - CU_ASSERT(!util::strieq_l("alpha", "AlPhA ", 6)); - CU_ASSERT(!util::strieq_l("", "AlPhA ", 6)); - - CU_ASSERT(util::strieq_l("alpha", "alpha")); - CU_ASSERT(util::strieq_l("alpha", "AlPhA")); - CU_ASSERT(util::strieq_l("", "")); - CU_ASSERT(!util::strieq_l("alpha", "AlPhA ")); - CU_ASSERT(!util::strieq_l("", "AlPhA ")); -} - -void test_util_inp_strlower(void) { - std::string a("alPha"); - util::inp_strlower(a); - CU_ASSERT("alpha" == a); - - a = "ALPHA123BRAVO"; - util::inp_strlower(a); - CU_ASSERT("alpha123bravo" == a); - - a = ""; - util::inp_strlower(a); - CU_ASSERT("" == a); -} - -void test_util_to_base64(void) { - std::string x = "AAA--B_"; - util::to_base64(x); - CU_ASSERT("AAA++B/=" == x); - - x = "AAA--B_B"; - util::to_base64(x); - CU_ASSERT("AAA++B/B" == x); -} - -void test_util_to_token68(void) { - std::string x = "AAA++B/="; - util::to_token68(x); - CU_ASSERT("AAA--B_" == x); - - x = "AAA++B/B"; - util::to_token68(x); - CU_ASSERT("AAA--B_B" == x); -} - -void test_util_percent_encode_token(void) { - CU_ASSERT("h2" == util::percent_encode_token("h2")); - CU_ASSERT("h3~" == util::percent_encode_token("h3~")); - CU_ASSERT("100%25" == util::percent_encode_token("100%")); - CU_ASSERT("http%202" == util::percent_encode_token("http 2")); -} - -void test_util_percent_encode_path(void) { - CU_ASSERT("/foo1/bar%3F&/%0A" == util::percent_encode_path("/foo1/bar?&/" - "\x0a")); -} - -void test_util_percent_decode(void) { - { - std::string s = "%66%6F%6f%62%61%72"; - CU_ASSERT("foobar" == util::percentDecode(std::begin(s), std::end(s))); - } - { - std::string s = "%66%6"; - CU_ASSERT("f%6" == util::percentDecode(std::begin(s), std::end(s))); - } - { - std::string s = "%66%"; - CU_ASSERT("f%" == util::percentDecode(std::begin(s), std::end(s))); - } -} - -void test_util_quote_string(void) { - CU_ASSERT("alpha" == util::quote_string("alpha")); - CU_ASSERT("" == util::quote_string("")); - CU_ASSERT("\\\"alpha\\\"" == util::quote_string("\"alpha\"")); -} - -void test_util_utox(void) { - CU_ASSERT("0" == util::utox(0)); - CU_ASSERT("1" == util::utox(1)); - CU_ASSERT("F" == util::utox(15)); - CU_ASSERT("10" == util::utox(16)); - CU_ASSERT("3B9ACA07" == util::utox(1000000007)); - CU_ASSERT("100000000" == util::utox(1LL << 32)); -} - -void test_util_http_date(void) { - CU_ASSERT("Thu, 01 Jan 1970 00:00:00 GMT" == util::http_date(0)); - CU_ASSERT("Wed, 29 Feb 2012 09:15:16 GMT" == util::http_date(1330506916)); -} - -void test_util_select_h2(void) { - const unsigned char *out = NULL; - unsigned char outlen = 0; - - // Check single entry and select it. - const unsigned char t1[] = "\x2h2"; - CU_ASSERT(util::select_h2(&out, &outlen, t1, sizeof(t1) - 1)); - CU_ASSERT( - memcmp(NGHTTP2_PROTO_VERSION_ID, out, NGHTTP2_PROTO_VERSION_ID_LEN) == 0); - CU_ASSERT(NGHTTP2_PROTO_VERSION_ID_LEN == outlen); - - out = NULL; - outlen = 0; - - // Check the case where id is correct but length is invalid and too - // long. - const unsigned char t2[] = "\x6h2-14"; - CU_ASSERT(!util::select_h2(&out, &outlen, t2, sizeof(t2) - 1)); - - // Check the case where h2 is located after bogus ID. - const unsigned char t3[] = "\x2h3\x2h2"; - CU_ASSERT(util::select_h2(&out, &outlen, t3, sizeof(t3) - 1)); - - CU_ASSERT( - memcmp(NGHTTP2_PROTO_VERSION_ID, out, NGHTTP2_PROTO_VERSION_ID_LEN) == 0); - CU_ASSERT(NGHTTP2_PROTO_VERSION_ID_LEN == outlen); - - out = NULL; - outlen = 0; - - // Check the case that last entry's length is invalid and too long. - const unsigned char t4[] = "\x2h3\x6h2-14"; - CU_ASSERT(!util::select_h2(&out, &outlen, t4, sizeof(t4) - 1)); - - // Check the case that all entries are not supported. - const unsigned char t5[] = "\x2h3\x2h4"; - CU_ASSERT(!util::select_h2(&out, &outlen, t5, sizeof(t5) - 1)); - - // Check the case where 2 values are eligible, but last one is - // picked up because it has precedence over the other. - const unsigned char t6[] = "\x5h2-14\x5h2-16"; - CU_ASSERT(util::select_h2(&out, &outlen, t6, sizeof(t6) - 1)); - CU_ASSERT(memcmp(NGHTTP2_H2_16, out, str_size(NGHTTP2_H2_16)) == 0); - CU_ASSERT(str_size(NGHTTP2_H2_16) == outlen); -} - -void test_util_ipv6_numeric_addr(void) { - CU_ASSERT(util::ipv6_numeric_addr("::1")); - CU_ASSERT(util::ipv6_numeric_addr("2001:0db8:85a3:0042:1000:8a2e:0370:7334")); - // IPv4 - CU_ASSERT(!util::ipv6_numeric_addr("127.0.0.1")); - // not numeric address - CU_ASSERT(!util::ipv6_numeric_addr("localhost")); -} - -void test_util_utos_with_unit(void) { - CU_ASSERT("0" == util::utos_with_unit(0)); - CU_ASSERT("1023" == util::utos_with_unit(1023)); - CU_ASSERT("1K" == util::utos_with_unit(1024)); - CU_ASSERT("1K" == util::utos_with_unit(1025)); - CU_ASSERT("1M" == util::utos_with_unit(1 << 20)); - CU_ASSERT("1G" == util::utos_with_unit(1 << 30)); - CU_ASSERT("1024G" == util::utos_with_unit(1LL << 40)); -} - -void test_util_utos_with_funit(void) { - CU_ASSERT("0" == util::utos_with_funit(0)); - CU_ASSERT("1023" == util::utos_with_funit(1023)); - CU_ASSERT("1.00K" == util::utos_with_funit(1024)); - CU_ASSERT("1.00K" == util::utos_with_funit(1025)); - CU_ASSERT("1.09K" == util::utos_with_funit(1119)); - CU_ASSERT("1.27K" == util::utos_with_funit(1300)); - CU_ASSERT("1.00M" == util::utos_with_funit(1 << 20)); - CU_ASSERT("1.18M" == util::utos_with_funit(1234567)); - CU_ASSERT("1.00G" == util::utos_with_funit(1 << 30)); - CU_ASSERT("4492450797.23G" == util::utos_with_funit(4823732313248234343LL)); - CU_ASSERT("1024.00G" == util::utos_with_funit(1LL << 40)); -} - -void test_util_parse_uint_with_unit(void) { - CU_ASSERT(0 == util::parse_uint_with_unit("0")); - CU_ASSERT(1023 == util::parse_uint_with_unit("1023")); - CU_ASSERT(1024 == util::parse_uint_with_unit("1k")); - CU_ASSERT(2048 == util::parse_uint_with_unit("2K")); - CU_ASSERT(1 << 20 == util::parse_uint_with_unit("1m")); - CU_ASSERT(1 << 21 == util::parse_uint_with_unit("2M")); - CU_ASSERT(1 << 30 == util::parse_uint_with_unit("1g")); - CU_ASSERT(1LL << 31 == util::parse_uint_with_unit("2G")); - CU_ASSERT(9223372036854775807LL == - util::parse_uint_with_unit("9223372036854775807")); - // check overflow case - CU_ASSERT(-1 == util::parse_uint_with_unit("9223372036854775808")); - CU_ASSERT(-1 == util::parse_uint_with_unit("10000000000000000000")); - CU_ASSERT(-1 == util::parse_uint_with_unit("9223372036854775807G")); - // bad characters - CU_ASSERT(-1 == util::parse_uint_with_unit("1.1")); - CU_ASSERT(-1 == util::parse_uint_with_unit("1a")); - CU_ASSERT(-1 == util::parse_uint_with_unit("a1")); - CU_ASSERT(-1 == util::parse_uint_with_unit("1T")); - CU_ASSERT(-1 == util::parse_uint_with_unit("")); -} - -void test_util_parse_uint(void) { - CU_ASSERT(0 == util::parse_uint("0")); - CU_ASSERT(1023 == util::parse_uint("1023")); - CU_ASSERT(-1 == util::parse_uint("1k")); - CU_ASSERT(9223372036854775807LL == util::parse_uint("9223372036854775807")); - // check overflow case - CU_ASSERT(-1 == util::parse_uint("9223372036854775808")); - CU_ASSERT(-1 == util::parse_uint("10000000000000000000")); - // bad characters - CU_ASSERT(-1 == util::parse_uint("1.1")); - CU_ASSERT(-1 == util::parse_uint("1a")); - CU_ASSERT(-1 == util::parse_uint("a1")); - CU_ASSERT(-1 == util::parse_uint("1T")); - CU_ASSERT(-1 == util::parse_uint("")); -} - -void test_util_parse_duration_with_unit(void) { - CU_ASSERT(0. == util::parse_duration_with_unit("0")); - CU_ASSERT(123. == util::parse_duration_with_unit("123")); - CU_ASSERT(123. == util::parse_duration_with_unit("123s")); - CU_ASSERT(0.500 == util::parse_duration_with_unit("500ms")); - CU_ASSERT(123. == util::parse_duration_with_unit("123S")); - CU_ASSERT(0.500 == util::parse_duration_with_unit("500MS")); - CU_ASSERT(180 == util::parse_duration_with_unit("3m")); - CU_ASSERT(3600 * 5 == util::parse_duration_with_unit("5h")); - - auto err = std::numeric_limits::infinity(); - // check overflow case - CU_ASSERT(err == util::parse_duration_with_unit("9223372036854775808")); - // bad characters - CU_ASSERT(err == util::parse_duration_with_unit("0u")); - CU_ASSERT(err == util::parse_duration_with_unit("0xs")); - CU_ASSERT(err == util::parse_duration_with_unit("0mt")); - CU_ASSERT(err == util::parse_duration_with_unit("0mss")); - CU_ASSERT(err == util::parse_duration_with_unit("s")); - CU_ASSERT(err == util::parse_duration_with_unit("ms")); -} - -void test_util_duration_str(void) { - CU_ASSERT("0" == util::duration_str(0.)); - CU_ASSERT("1s" == util::duration_str(1.)); - CU_ASSERT("500ms" == util::duration_str(0.5)); - CU_ASSERT("1500ms" == util::duration_str(1.5)); - CU_ASSERT("2m" == util::duration_str(120.)); - CU_ASSERT("121s" == util::duration_str(121.)); - CU_ASSERT("1h" == util::duration_str(3600.)); -} - -void test_util_format_duration(void) { - CU_ASSERT("0us" == util::format_duration(std::chrono::microseconds(0))); - CU_ASSERT("999us" == util::format_duration(std::chrono::microseconds(999))); - CU_ASSERT("1.00ms" == util::format_duration(std::chrono::microseconds(1000))); - CU_ASSERT("1.09ms" == util::format_duration(std::chrono::microseconds(1090))); - CU_ASSERT("1.01ms" == util::format_duration(std::chrono::microseconds(1009))); - CU_ASSERT("999.99ms" == - util::format_duration(std::chrono::microseconds(999990))); - CU_ASSERT("1.00s" == - util::format_duration(std::chrono::microseconds(1000000))); - CU_ASSERT("1.05s" == - util::format_duration(std::chrono::microseconds(1050000))); -} - -void test_util_starts_with(void) { - CU_ASSERT(util::startsWith("foo", "foo")); - CU_ASSERT(util::startsWith("fooo", "foo")); - CU_ASSERT(util::startsWith("ofoo", "")); - CU_ASSERT(!util::startsWith("ofoo", "foo")); - - CU_ASSERT(util::istartsWith("FOO", "fOO")); - CU_ASSERT(util::startsWith("ofoo", "")); - CU_ASSERT(util::istartsWith("fOOo", "Foo")); - CU_ASSERT(!util::istartsWith("ofoo", "foo")); -} - -void test_util_ends_with(void) { - CU_ASSERT(util::endsWith("foo", "foo")); - CU_ASSERT(util::endsWith("foo", "")); - CU_ASSERT(util::endsWith("ofoo", "foo")); - CU_ASSERT(!util::endsWith("ofoo", "fo")); - - CU_ASSERT(util::iendsWith("fOo", "Foo")); - CU_ASSERT(util::iendsWith("foo", "")); - CU_ASSERT(util::iendsWith("oFoo", "fOO")); - CU_ASSERT(!util::iendsWith("ofoo", "fo")); -} - -} // namespace shrpx diff --git a/src/util_test.h b/src/util_test.h deleted file mode 100644 index 00290b8..0000000 --- a/src/util_test.h +++ /dev/null @@ -1,59 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2013 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#ifndef UTIL_TEST_H -#define UTIL_TEST_H - -#ifdef HAVE_CONFIG_H -#include -#endif // HAVE_CONFIG_H - -namespace shrpx { - -void test_util_streq(void); -void test_util_strieq(void); -void test_util_inp_strlower(void); -void test_util_to_base64(void); -void test_util_to_token68(void); -void test_util_percent_encode_token(void); -void test_util_percent_encode_path(void); -void test_util_percent_decode(void); -void test_util_quote_string(void); -void test_util_utox(void); -void test_util_http_date(void); -void test_util_select_h2(void); -void test_util_ipv6_numeric_addr(void); -void test_util_utos_with_unit(void); -void test_util_utos_with_funit(void); -void test_util_parse_uint_with_unit(void); -void test_util_parse_uint(void); -void test_util_parse_duration_with_unit(void); -void test_util_duration_str(void); -void test_util_format_duration(void); -void test_util_starts_with(void); -void test_util_ends_with(void); - -} // namespace shrpx - -#endif // UTIL_TEST_H diff --git a/test-driver b/test-driver deleted file mode 100755 index d306056..0000000 --- a/test-driver +++ /dev/null @@ -1,139 +0,0 @@ -#! /bin/sh -# test-driver - basic testsuite driver script. - -scriptversion=2013-07-13.22; # UTC - -# Copyright (C) 2011-2013 Free Software Foundation, Inc. -# -# 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 2, 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 . - -# As a special exception to the GNU General Public License, if you -# distribute this file as part of a program that contains a -# configuration script generated by Autoconf, you may include it under -# the same distribution terms that you use for the rest of that program. - -# This file is maintained in Automake, please report -# bugs to or send patches to -# . - -# Make unconditional expansion of undefined variables an error. This -# helps a lot in preventing typo-related bugs. -set -u - -usage_error () -{ - echo "$0: $*" >&2 - print_usage >&2 - exit 2 -} - -print_usage () -{ - cat <$log_file 2>&1 -estatus=$? -if test $enable_hard_errors = no && test $estatus -eq 99; then - estatus=1 -fi - -case $estatus:$expect_failure in - 0:yes) col=$red res=XPASS recheck=yes gcopy=yes;; - 0:*) col=$grn res=PASS recheck=no gcopy=no;; - 77:*) col=$blu res=SKIP recheck=no gcopy=yes;; - 99:*) col=$mgn res=ERROR recheck=yes gcopy=yes;; - *:yes) col=$lgn res=XFAIL recheck=no gcopy=yes;; - *:*) col=$red res=FAIL recheck=yes gcopy=yes;; -esac - -# Report outcome to console. -echo "${col}${res}${std}: $test_name" - -# Register the test result, and other relevant metadata. -echo ":test-result: $res" > $trs_file -echo ":global-test-result: $res" >> $trs_file -echo ":recheck: $recheck" >> $trs_file -echo ":copy-in-global-log: $gcopy" >> $trs_file - -# Local Variables: -# mode: shell-script -# sh-indentation: 2 -# eval: (add-hook 'write-file-hooks 'time-stamp) -# time-stamp-start: "scriptversion=" -# time-stamp-format: "%:y-%02m-%02d.%02H" -# time-stamp-time-zone: "UTC" -# time-stamp-end: "; # UTC" -# End: diff --git a/tests/Makefile.am b/tests/Makefile.am deleted file mode 100644 index 4ef17bb..0000000 --- a/tests/Makefile.am +++ /dev/null @@ -1,82 +0,0 @@ -# nghttp2 - HTTP/2 C Library - -# Copyright (c) 2012 Tatsuhiro Tsujikawa - -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: - -# The above copyright notice and this permission notice shall be -# included in all copies or substantial portions of the Software. - -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -SUBDIRS = testdata - -if HAVE_CUNIT - -check_PROGRAMS = main - -if ENABLE_FAILMALLOC -check_PROGRAMS += failmalloc -endif # ENABLE_FAILMALLOC - -OBJECTS = main.c nghttp2_pq_test.c nghttp2_map_test.c nghttp2_queue_test.c \ - nghttp2_test_helper.c \ - nghttp2_frame_test.c \ - nghttp2_stream_test.c \ - nghttp2_session_test.c \ - nghttp2_hd_test.c \ - nghttp2_npn_test.c \ - nghttp2_helper_test.c \ - nghttp2_buf_test.c - -HFILES = nghttp2_pq_test.h nghttp2_map_test.h nghttp2_queue_test.h \ - nghttp2_session_test.h \ - nghttp2_frame_test.h nghttp2_stream_test.h nghttp2_hd_test.h \ - nghttp2_npn_test.h nghttp2_helper_test.h \ - nghttp2_test_helper.h \ - nghttp2_buf_test.h - -main_SOURCES = $(HFILES) $(OBJECTS) - -main_LDADD = ${top_builddir}/lib/libnghttp2.la @CUNIT_LIBS@ @TESTLDADD@ -main_LDFLAGS = -static - -if ENABLE_FAILMALLOC -failmalloc_SOURCES = failmalloc.c failmalloc_test.c failmalloc_test.h \ - malloc_wrapper.c malloc_wrapper.h \ - nghttp2_test_helper.c nghttp2_test_helper.h -failmalloc_LDADD = $(main_LDADD) -failmalloc_LDFLAGS = $(main_LDFLAGS) -endif # ENABLE_FAILMALLOC - -AM_CFLAGS = $(WARNCFLAGS) \ - -I${top_srcdir}/lib \ - -I${top_srcdir}/lib/includes \ - -I${top_builddir}/lib/includes \ - @CUNIT_CFLAGS@ @DEFS@ - -TESTS = main - -if ENABLE_FAILMALLOC -TESTS += failmalloc -endif # ENABLE_FAILMALLOC - -if ENABLE_APP - -# EXTRA_DIST = end_to_end.py -# TESTS += end_to_end.py - -endif # ENABLE_APP - -endif # HAVE_CUNIT diff --git a/tests/Makefile.in b/tests/Makefile.in deleted file mode 100644 index 3c9a4c2..0000000 --- a/tests/Makefile.in +++ /dev/null @@ -1,1253 +0,0 @@ -# Makefile.in generated by automake 1.14.1 from Makefile.am. -# @configure_input@ - -# Copyright (C) 1994-2013 Free Software Foundation, Inc. - -# This Makefile.in is free software; the Free Software Foundation -# gives unlimited permission to copy and/or distribute it, -# with or without modifications, as long as this notice is preserved. - -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY, to the extent permitted by law; without -# even the implied warranty of MERCHANTABILITY or FITNESS FOR A -# PARTICULAR PURPOSE. - -@SET_MAKE@ - -# nghttp2 - HTTP/2 C Library - -# Copyright (c) 2012 Tatsuhiro Tsujikawa - -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: - -# The above copyright notice and this permission notice shall be -# included in all copies or substantial portions of the Software. -VPATH = @srcdir@ -am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)' -am__make_running_with_option = \ - case $${target_option-} in \ - ?) ;; \ - *) echo "am__make_running_with_option: internal error: invalid" \ - "target option '$${target_option-}' specified" >&2; \ - exit 1;; \ - esac; \ - has_opt=no; \ - sane_makeflags=$$MAKEFLAGS; \ - if $(am__is_gnu_make); then \ - sane_makeflags=$$MFLAGS; \ - else \ - case $$MAKEFLAGS in \ - *\\[\ \ ]*) \ - bs=\\; \ - sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ - | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ - esac; \ - fi; \ - skip_next=no; \ - strip_trailopt () \ - { \ - flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ - }; \ - for flg in $$sane_makeflags; do \ - test $$skip_next = yes && { skip_next=no; continue; }; \ - case $$flg in \ - *=*|--*) continue;; \ - -*I) strip_trailopt 'I'; skip_next=yes;; \ - -*I?*) strip_trailopt 'I';; \ - -*O) strip_trailopt 'O'; skip_next=yes;; \ - -*O?*) strip_trailopt 'O';; \ - -*l) strip_trailopt 'l'; skip_next=yes;; \ - -*l?*) strip_trailopt 'l';; \ - -[dEDm]) skip_next=yes;; \ - -[JT]) skip_next=yes;; \ - esac; \ - case $$flg in \ - *$$target_option*) has_opt=yes; break;; \ - esac; \ - done; \ - test $$has_opt = yes -am__make_dryrun = (target_option=n; $(am__make_running_with_option)) -am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) -pkgdatadir = $(datadir)/@PACKAGE@ -pkgincludedir = $(includedir)/@PACKAGE@ -pkglibdir = $(libdir)/@PACKAGE@ -pkglibexecdir = $(libexecdir)/@PACKAGE@ -am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd -install_sh_DATA = $(install_sh) -c -m 644 -install_sh_PROGRAM = $(install_sh) -c -install_sh_SCRIPT = $(install_sh) -c -INSTALL_HEADER = $(INSTALL_DATA) -transform = $(program_transform_name) -NORMAL_INSTALL = : -PRE_INSTALL = : -POST_INSTALL = : -NORMAL_UNINSTALL = : -PRE_UNINSTALL = : -POST_UNINSTALL = : -build_triplet = @build@ -host_triplet = @host@ -target_triplet = @target@ -@HAVE_CUNIT_TRUE@check_PROGRAMS = main$(EXEEXT) $(am__EXEEXT_1) -@ENABLE_FAILMALLOC_TRUE@@HAVE_CUNIT_TRUE@am__append_1 = failmalloc -@HAVE_CUNIT_TRUE@TESTS = main$(EXEEXT) $(am__EXEEXT_1) -@ENABLE_FAILMALLOC_TRUE@@HAVE_CUNIT_TRUE@am__append_2 = failmalloc -subdir = tests -DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \ - $(top_srcdir)/depcomp $(top_srcdir)/test-driver -ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 -am__aclocal_m4_deps = $(top_srcdir)/m4/ax_boost_asio.m4 \ - $(top_srcdir)/m4/ax_boost_base.m4 \ - $(top_srcdir)/m4/ax_boost_system.m4 \ - $(top_srcdir)/m4/ax_boost_thread.m4 \ - $(top_srcdir)/m4/ax_check_compile_flag.m4 \ - $(top_srcdir)/m4/ax_cxx_compile_stdcxx_11.m4 \ - $(top_srcdir)/m4/ax_have_epoll.m4 \ - $(top_srcdir)/m4/ax_python_devel.m4 \ - $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/libxml2.m4 \ - $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ - $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ - $(top_srcdir)/configure.ac -am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ - $(ACLOCAL_M4) -mkinstalldirs = $(install_sh) -d -CONFIG_HEADER = $(top_builddir)/config.h -CONFIG_CLEAN_FILES = -CONFIG_CLEAN_VPATH_FILES = -@ENABLE_FAILMALLOC_TRUE@@HAVE_CUNIT_TRUE@am__EXEEXT_1 = \ -@ENABLE_FAILMALLOC_TRUE@@HAVE_CUNIT_TRUE@ failmalloc$(EXEEXT) -am__failmalloc_SOURCES_DIST = failmalloc.c failmalloc_test.c \ - failmalloc_test.h malloc_wrapper.c malloc_wrapper.h \ - nghttp2_test_helper.c nghttp2_test_helper.h -@ENABLE_FAILMALLOC_TRUE@@HAVE_CUNIT_TRUE@am_failmalloc_OBJECTS = \ -@ENABLE_FAILMALLOC_TRUE@@HAVE_CUNIT_TRUE@ failmalloc.$(OBJEXT) \ -@ENABLE_FAILMALLOC_TRUE@@HAVE_CUNIT_TRUE@ failmalloc_test.$(OBJEXT) \ -@ENABLE_FAILMALLOC_TRUE@@HAVE_CUNIT_TRUE@ malloc_wrapper.$(OBJEXT) \ -@ENABLE_FAILMALLOC_TRUE@@HAVE_CUNIT_TRUE@ nghttp2_test_helper.$(OBJEXT) -failmalloc_OBJECTS = $(am_failmalloc_OBJECTS) -@HAVE_CUNIT_TRUE@am__DEPENDENCIES_1 = \ -@HAVE_CUNIT_TRUE@ ${top_builddir}/lib/libnghttp2.la -@ENABLE_FAILMALLOC_TRUE@@HAVE_CUNIT_TRUE@failmalloc_DEPENDENCIES = $(am__DEPENDENCIES_1) -AM_V_lt = $(am__v_lt_@AM_V@) -am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) -am__v_lt_0 = --silent -am__v_lt_1 = -failmalloc_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ - $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ - $(failmalloc_LDFLAGS) $(LDFLAGS) -o $@ -am__main_SOURCES_DIST = nghttp2_pq_test.h nghttp2_map_test.h \ - nghttp2_queue_test.h nghttp2_session_test.h \ - nghttp2_frame_test.h nghttp2_stream_test.h nghttp2_hd_test.h \ - nghttp2_npn_test.h nghttp2_helper_test.h nghttp2_test_helper.h \ - nghttp2_buf_test.h main.c nghttp2_pq_test.c nghttp2_map_test.c \ - nghttp2_queue_test.c nghttp2_test_helper.c \ - nghttp2_frame_test.c nghttp2_stream_test.c \ - nghttp2_session_test.c nghttp2_hd_test.c nghttp2_npn_test.c \ - nghttp2_helper_test.c nghttp2_buf_test.c -am__objects_1 = -@HAVE_CUNIT_TRUE@am__objects_2 = main.$(OBJEXT) \ -@HAVE_CUNIT_TRUE@ nghttp2_pq_test.$(OBJEXT) \ -@HAVE_CUNIT_TRUE@ nghttp2_map_test.$(OBJEXT) \ -@HAVE_CUNIT_TRUE@ nghttp2_queue_test.$(OBJEXT) \ -@HAVE_CUNIT_TRUE@ nghttp2_test_helper.$(OBJEXT) \ -@HAVE_CUNIT_TRUE@ nghttp2_frame_test.$(OBJEXT) \ -@HAVE_CUNIT_TRUE@ nghttp2_stream_test.$(OBJEXT) \ -@HAVE_CUNIT_TRUE@ nghttp2_session_test.$(OBJEXT) \ -@HAVE_CUNIT_TRUE@ nghttp2_hd_test.$(OBJEXT) \ -@HAVE_CUNIT_TRUE@ nghttp2_npn_test.$(OBJEXT) \ -@HAVE_CUNIT_TRUE@ nghttp2_helper_test.$(OBJEXT) \ -@HAVE_CUNIT_TRUE@ nghttp2_buf_test.$(OBJEXT) -@HAVE_CUNIT_TRUE@am_main_OBJECTS = $(am__objects_1) $(am__objects_2) -main_OBJECTS = $(am_main_OBJECTS) -@HAVE_CUNIT_TRUE@main_DEPENDENCIES = \ -@HAVE_CUNIT_TRUE@ ${top_builddir}/lib/libnghttp2.la -main_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ - $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ - $(main_LDFLAGS) $(LDFLAGS) -o $@ -AM_V_P = $(am__v_P_@AM_V@) -am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) -am__v_P_0 = false -am__v_P_1 = : -AM_V_GEN = $(am__v_GEN_@AM_V@) -am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) -am__v_GEN_0 = @echo " GEN " $@; -am__v_GEN_1 = -AM_V_at = $(am__v_at_@AM_V@) -am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) -am__v_at_0 = @ -am__v_at_1 = -DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) -depcomp = $(SHELL) $(top_srcdir)/depcomp -am__depfiles_maybe = depfiles -am__mv = mv -f -COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ - $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ - $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ - $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ - $(AM_CFLAGS) $(CFLAGS) -AM_V_CC = $(am__v_CC_@AM_V@) -am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) -am__v_CC_0 = @echo " CC " $@; -am__v_CC_1 = -CCLD = $(CC) -LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ - $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ - $(AM_LDFLAGS) $(LDFLAGS) -o $@ -AM_V_CCLD = $(am__v_CCLD_@AM_V@) -am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) -am__v_CCLD_0 = @echo " CCLD " $@; -am__v_CCLD_1 = -SOURCES = $(failmalloc_SOURCES) $(main_SOURCES) -DIST_SOURCES = $(am__failmalloc_SOURCES_DIST) $(am__main_SOURCES_DIST) -RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \ - ctags-recursive dvi-recursive html-recursive info-recursive \ - install-data-recursive install-dvi-recursive \ - install-exec-recursive install-html-recursive \ - install-info-recursive install-pdf-recursive \ - install-ps-recursive install-recursive installcheck-recursive \ - installdirs-recursive pdf-recursive ps-recursive \ - tags-recursive uninstall-recursive -am__can_run_installinfo = \ - case $$AM_UPDATE_INFO_DIR in \ - n|no|NO) false;; \ - *) (install-info --version) >/dev/null 2>&1;; \ - esac -RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \ - distclean-recursive maintainer-clean-recursive -am__recursive_targets = \ - $(RECURSIVE_TARGETS) \ - $(RECURSIVE_CLEAN_TARGETS) \ - $(am__extra_recursive_targets) -AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \ - check recheck distdir -am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) -# Read a list of newline-separated strings from the standard input, -# and print each of them once, without duplicates. Input order is -# *not* preserved. -am__uniquify_input = $(AWK) '\ - BEGIN { nonempty = 0; } \ - { items[$$0] = 1; nonempty = 1; } \ - END { if (nonempty) { for (i in items) print i; }; } \ -' -# Make sure the list of sources is unique. This is necessary because, -# e.g., the same source file might be shared among _SOURCES variables -# for different programs/libraries. -am__define_uniq_tagged_files = \ - list='$(am__tagged_files)'; \ - unique=`for i in $$list; do \ - if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ - done | $(am__uniquify_input)` -ETAGS = etags -CTAGS = ctags -am__tty_colors_dummy = \ - mgn= red= grn= lgn= blu= brg= std=; \ - am__color_tests=no -am__tty_colors = { \ - $(am__tty_colors_dummy); \ - if test "X$(AM_COLOR_TESTS)" = Xno; then \ - am__color_tests=no; \ - elif test "X$(AM_COLOR_TESTS)" = Xalways; then \ - am__color_tests=yes; \ - elif test "X$$TERM" != Xdumb && { test -t 1; } 2>/dev/null; then \ - am__color_tests=yes; \ - fi; \ - if test $$am__color_tests = yes; then \ - red=''; \ - grn=''; \ - lgn=''; \ - blu=''; \ - mgn=''; \ - brg=''; \ - std=''; \ - fi; \ -} -am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; -am__vpath_adj = case $$p in \ - $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ - *) f=$$p;; \ - esac; -am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; -am__install_max = 40 -am__nobase_strip_setup = \ - srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` -am__nobase_strip = \ - for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" -am__nobase_list = $(am__nobase_strip_setup); \ - for p in $$list; do echo "$$p $$p"; done | \ - sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ - $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ - if (++n[$$2] == $(am__install_max)) \ - { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ - END { for (dir in files) print dir, files[dir] }' -am__base_list = \ - sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ - sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' -am__uninstall_files_from_dir = { \ - test -z "$$files" \ - || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ - || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ - $(am__cd) "$$dir" && rm -f $$files; }; \ - } -am__recheck_rx = ^[ ]*:recheck:[ ]* -am__global_test_result_rx = ^[ ]*:global-test-result:[ ]* -am__copy_in_global_log_rx = ^[ ]*:copy-in-global-log:[ ]* -# A command that, given a newline-separated list of test names on the -# standard input, print the name of the tests that are to be re-run -# upon "make recheck". -am__list_recheck_tests = $(AWK) '{ \ - recheck = 1; \ - while ((rc = (getline line < ($$0 ".trs"))) != 0) \ - { \ - if (rc < 0) \ - { \ - if ((getline line2 < ($$0 ".log")) < 0) \ - recheck = 0; \ - break; \ - } \ - else if (line ~ /$(am__recheck_rx)[nN][Oo]/) \ - { \ - recheck = 0; \ - break; \ - } \ - else if (line ~ /$(am__recheck_rx)[yY][eE][sS]/) \ - { \ - break; \ - } \ - }; \ - if (recheck) \ - print $$0; \ - close ($$0 ".trs"); \ - close ($$0 ".log"); \ -}' -# A command that, given a newline-separated list of test names on the -# standard input, create the global log from their .trs and .log files. -am__create_global_log = $(AWK) ' \ -function fatal(msg) \ -{ \ - print "fatal: making $@: " msg | "cat >&2"; \ - exit 1; \ -} \ -function rst_section(header) \ -{ \ - print header; \ - len = length(header); \ - for (i = 1; i <= len; i = i + 1) \ - printf "="; \ - printf "\n\n"; \ -} \ -{ \ - copy_in_global_log = 1; \ - global_test_result = "RUN"; \ - while ((rc = (getline line < ($$0 ".trs"))) != 0) \ - { \ - if (rc < 0) \ - fatal("failed to read from " $$0 ".trs"); \ - if (line ~ /$(am__global_test_result_rx)/) \ - { \ - sub("$(am__global_test_result_rx)", "", line); \ - sub("[ ]*$$", "", line); \ - global_test_result = line; \ - } \ - else if (line ~ /$(am__copy_in_global_log_rx)[nN][oO]/) \ - copy_in_global_log = 0; \ - }; \ - if (copy_in_global_log) \ - { \ - rst_section(global_test_result ": " $$0); \ - while ((rc = (getline line < ($$0 ".log"))) != 0) \ - { \ - if (rc < 0) \ - fatal("failed to read from " $$0 ".log"); \ - print line; \ - }; \ - printf "\n"; \ - }; \ - close ($$0 ".trs"); \ - close ($$0 ".log"); \ -}' -# Restructured Text title. -am__rst_title = { sed 's/.*/ & /;h;s/./=/g;p;x;s/ *$$//;p;g' && echo; } -# Solaris 10 'make', and several other traditional 'make' implementations, -# pass "-e" to $(SHELL), and POSIX 2008 even requires this. Work around it -# by disabling -e (using the XSI extension "set +e") if it's set. -am__sh_e_setup = case $$- in *e*) set +e;; esac -# Default flags passed to test drivers. -am__common_driver_flags = \ - --color-tests "$$am__color_tests" \ - --enable-hard-errors "$$am__enable_hard_errors" \ - --expect-failure "$$am__expect_failure" -# To be inserted before the command running the test. Creates the -# directory for the log if needed. Stores in $dir the directory -# containing $f, in $tst the test, in $log the log. Executes the -# developer- defined test setup AM_TESTS_ENVIRONMENT (if any), and -# passes TESTS_ENVIRONMENT. Set up options for the wrapper that -# will run the test scripts (or their associated LOG_COMPILER, if -# thy have one). -am__check_pre = \ -$(am__sh_e_setup); \ -$(am__vpath_adj_setup) $(am__vpath_adj) \ -$(am__tty_colors); \ -srcdir=$(srcdir); export srcdir; \ -case "$@" in \ - */*) am__odir=`echo "./$@" | sed 's|/[^/]*$$||'`;; \ - *) am__odir=.;; \ -esac; \ -test "x$$am__odir" = x"." || test -d "$$am__odir" \ - || $(MKDIR_P) "$$am__odir" || exit $$?; \ -if test -f "./$$f"; then dir=./; \ -elif test -f "$$f"; then dir=; \ -else dir="$(srcdir)/"; fi; \ -tst=$$dir$$f; log='$@'; \ -if test -n '$(DISABLE_HARD_ERRORS)'; then \ - am__enable_hard_errors=no; \ -else \ - am__enable_hard_errors=yes; \ -fi; \ -case " $(XFAIL_TESTS) " in \ - *[\ \ ]$$f[\ \ ]* | *[\ \ ]$$dir$$f[\ \ ]*) \ - am__expect_failure=yes;; \ - *) \ - am__expect_failure=no;; \ -esac; \ -$(AM_TESTS_ENVIRONMENT) $(TESTS_ENVIRONMENT) -# A shell command to get the names of the tests scripts with any registered -# extension removed (i.e., equivalently, the names of the test logs, with -# the '.log' extension removed). The result is saved in the shell variable -# '$bases'. This honors runtime overriding of TESTS and TEST_LOGS. Sadly, -# we cannot use something simpler, involving e.g., "$(TEST_LOGS:.log=)", -# since that might cause problem with VPATH rewrites for suffix-less tests. -# See also 'test-harness-vpath-rewrite.sh' and 'test-trs-basic.sh'. -am__set_TESTS_bases = \ - bases='$(TEST_LOGS)'; \ - bases=`for i in $$bases; do echo $$i; done | sed 's/\.log$$//'`; \ - bases=`echo $$bases` -RECHECK_LOGS = $(TEST_LOGS) -TEST_SUITE_LOG = test-suite.log -TEST_EXTENSIONS = @EXEEXT@ .test -LOG_DRIVER = $(SHELL) $(top_srcdir)/test-driver -LOG_COMPILE = $(LOG_COMPILER) $(AM_LOG_FLAGS) $(LOG_FLAGS) -am__set_b = \ - case '$@' in \ - */*) \ - case '$*' in \ - */*) b='$*';; \ - *) b=`echo '$@' | sed 's/\.log$$//'`; \ - esac;; \ - *) \ - b='$*';; \ - esac -am__test_logs1 = $(TESTS:=.log) -am__test_logs2 = $(am__test_logs1:@EXEEXT@.log=.log) -TEST_LOGS = $(am__test_logs2:.test.log=.log) -TEST_LOG_DRIVER = $(SHELL) $(top_srcdir)/test-driver -TEST_LOG_COMPILE = $(TEST_LOG_COMPILER) $(AM_TEST_LOG_FLAGS) \ - $(TEST_LOG_FLAGS) -DIST_SUBDIRS = $(SUBDIRS) -DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) -am__relativize = \ - dir0=`pwd`; \ - sed_first='s,^\([^/]*\)/.*$$,\1,'; \ - sed_rest='s,^[^/]*/*,,'; \ - sed_last='s,^.*/\([^/]*\)$$,\1,'; \ - sed_butlast='s,/*[^/]*$$,,'; \ - while test -n "$$dir1"; do \ - first=`echo "$$dir1" | sed -e "$$sed_first"`; \ - if test "$$first" != "."; then \ - if test "$$first" = ".."; then \ - dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \ - dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \ - else \ - first2=`echo "$$dir2" | sed -e "$$sed_first"`; \ - if test "$$first2" = "$$first"; then \ - dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \ - else \ - dir2="../$$dir2"; \ - fi; \ - dir0="$$dir0"/"$$first"; \ - fi; \ - fi; \ - dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \ - done; \ - reldir="$$dir2" -ACLOCAL = @ACLOCAL@ -AMTAR = @AMTAR@ -AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ -APPLDFLAGS = @APPLDFLAGS@ -AR = @AR@ -AUTOCONF = @AUTOCONF@ -AUTOHEADER = @AUTOHEADER@ -AUTOMAKE = @AUTOMAKE@ -AWK = @AWK@ -BOOST_ASIO_LIB = @BOOST_ASIO_LIB@ -BOOST_CPPFLAGS = @BOOST_CPPFLAGS@ -BOOST_LDFLAGS = @BOOST_LDFLAGS@ -BOOST_SYSTEM_LIB = @BOOST_SYSTEM_LIB@ -BOOST_THREAD_LIB = @BOOST_THREAD_LIB@ -CC = @CC@ -CCDEPMODE = @CCDEPMODE@ -CFLAGS = @CFLAGS@ -CPP = @CPP@ -CPPFLAGS = @CPPFLAGS@ -CUNIT_CFLAGS = @CUNIT_CFLAGS@ -CUNIT_LIBS = @CUNIT_LIBS@ -CXX = @CXX@ -CXXCPP = @CXXCPP@ -CXXDEPMODE = @CXXDEPMODE@ -CXXFLAGS = @CXXFLAGS@ -CYGPATH_W = @CYGPATH_W@ -CYTHON = @CYTHON@ -DEFS = @DEFS@ -DEPDIR = @DEPDIR@ -DLLTOOL = @DLLTOOL@ -DSYMUTIL = @DSYMUTIL@ -DUMPBIN = @DUMPBIN@ -ECHO_C = @ECHO_C@ -ECHO_N = @ECHO_N@ -ECHO_T = @ECHO_T@ -EGREP = @EGREP@ -EXEEXT = @EXEEXT@ -FGREP = @FGREP@ -GREP = @GREP@ -HAVE_CXX11 = @HAVE_CXX11@ -INSTALL = @INSTALL@ -INSTALL_DATA = @INSTALL_DATA@ -INSTALL_PROGRAM = @INSTALL_PROGRAM@ -INSTALL_SCRIPT = @INSTALL_SCRIPT@ -INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ -JANSSON_CFLAGS = @JANSSON_CFLAGS@ -JANSSON_LIBS = @JANSSON_LIBS@ -JEMALLOC_LIBS = @JEMALLOC_LIBS@ -LD = @LD@ -LDFLAGS = @LDFLAGS@ -LIBEVENT_OPENSSL_CFLAGS = @LIBEVENT_OPENSSL_CFLAGS@ -LIBEVENT_OPENSSL_LIBS = @LIBEVENT_OPENSSL_LIBS@ -LIBEV_CFLAGS = @LIBEV_CFLAGS@ -LIBEV_LIBS = @LIBEV_LIBS@ -LIBOBJS = @LIBOBJS@ -LIBS = @LIBS@ -LIBSPDYLAY_CFLAGS = @LIBSPDYLAY_CFLAGS@ -LIBSPDYLAY_LIBS = @LIBSPDYLAY_LIBS@ -LIBTOOL = @LIBTOOL@ -LIPO = @LIPO@ -LN_S = @LN_S@ -LTLIBOBJS = @LTLIBOBJS@ -LT_AGE = @LT_AGE@ -LT_CURRENT = @LT_CURRENT@ -LT_REVISION = @LT_REVISION@ -MAKEINFO = @MAKEINFO@ -MANIFEST_TOOL = @MANIFEST_TOOL@ -MKDIR_P = @MKDIR_P@ -NM = @NM@ -NMEDIT = @NMEDIT@ -OBJDUMP = @OBJDUMP@ -OBJEXT = @OBJEXT@ -OPENSSL_CFLAGS = @OPENSSL_CFLAGS@ -OPENSSL_LIBS = @OPENSSL_LIBS@ -OTOOL = @OTOOL@ -OTOOL64 = @OTOOL64@ -PACKAGE = @PACKAGE@ -PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ -PACKAGE_NAME = @PACKAGE_NAME@ -PACKAGE_STRING = @PACKAGE_STRING@ -PACKAGE_TARNAME = @PACKAGE_TARNAME@ -PACKAGE_URL = @PACKAGE_URL@ -PACKAGE_VERSION = @PACKAGE_VERSION@ -PACKAGE_VERSION_NUM = @PACKAGE_VERSION_NUM@ -PATH_SEPARATOR = @PATH_SEPARATOR@ -PKG_CONFIG = @PKG_CONFIG@ -PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ -PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ -PYTHON = @PYTHON@ -PYTHON_CPPFLAGS = @PYTHON_CPPFLAGS@ -PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@ -PYTHON_EXTRA_LDFLAGS = @PYTHON_EXTRA_LDFLAGS@ -PYTHON_EXTRA_LIBS = @PYTHON_EXTRA_LIBS@ -PYTHON_LDFLAGS = @PYTHON_LDFLAGS@ -PYTHON_PLATFORM = @PYTHON_PLATFORM@ -PYTHON_PREFIX = @PYTHON_PREFIX@ -PYTHON_SITE_PKG = @PYTHON_SITE_PKG@ -PYTHON_VERSION = @PYTHON_VERSION@ -RANLIB = @RANLIB@ -SED = @SED@ -SET_MAKE = @SET_MAKE@ -SHELL = @SHELL@ -STRIP = @STRIP@ -TESTLDADD = @TESTLDADD@ -VERSION = @VERSION@ -WARNCFLAGS = @WARNCFLAGS@ -XML2_CONFIG = @XML2_CONFIG@ -XML_CPPFLAGS = @XML_CPPFLAGS@ -XML_LIBS = @XML_LIBS@ -ZLIB_CFLAGS = @ZLIB_CFLAGS@ -ZLIB_LIBS = @ZLIB_LIBS@ -abs_builddir = @abs_builddir@ -abs_srcdir = @abs_srcdir@ -abs_top_builddir = @abs_top_builddir@ -abs_top_srcdir = @abs_top_srcdir@ -ac_ct_AR = @ac_ct_AR@ -ac_ct_CC = @ac_ct_CC@ -ac_ct_CXX = @ac_ct_CXX@ -ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ -am__include = @am__include@ -am__leading_dot = @am__leading_dot@ -am__quote = @am__quote@ -am__tar = @am__tar@ -am__untar = @am__untar@ -bindir = @bindir@ -build = @build@ -build_alias = @build_alias@ -build_cpu = @build_cpu@ -build_os = @build_os@ -build_vendor = @build_vendor@ -builddir = @builddir@ -datadir = @datadir@ -datarootdir = @datarootdir@ -docdir = @docdir@ -dvidir = @dvidir@ -exec_prefix = @exec_prefix@ -host = @host@ -host_alias = @host_alias@ -host_cpu = @host_cpu@ -host_os = @host_os@ -host_vendor = @host_vendor@ -htmldir = @htmldir@ -includedir = @includedir@ -infodir = @infodir@ -install_sh = @install_sh@ -libdir = @libdir@ -libexecdir = @libexecdir@ -localedir = @localedir@ -localstatedir = @localstatedir@ -mandir = @mandir@ -mkdir_p = @mkdir_p@ -oldincludedir = @oldincludedir@ -pdfdir = @pdfdir@ -pkgpyexecdir = @pkgpyexecdir@ -pkgpythondir = @pkgpythondir@ -prefix = @prefix@ -program_transform_name = @program_transform_name@ -psdir = @psdir@ -pyexecdir = @pyexecdir@ -pythondir = @pythondir@ -sbindir = @sbindir@ -sharedstatedir = @sharedstatedir@ -srcdir = @srcdir@ -sysconfdir = @sysconfdir@ -target = @target@ -target_alias = @target_alias@ -target_cpu = @target_cpu@ -target_os = @target_os@ -target_vendor = @target_vendor@ -top_build_prefix = @top_build_prefix@ -top_builddir = @top_builddir@ -top_srcdir = @top_srcdir@ - -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -SUBDIRS = testdata -@HAVE_CUNIT_TRUE@OBJECTS = main.c nghttp2_pq_test.c nghttp2_map_test.c nghttp2_queue_test.c \ -@HAVE_CUNIT_TRUE@ nghttp2_test_helper.c \ -@HAVE_CUNIT_TRUE@ nghttp2_frame_test.c \ -@HAVE_CUNIT_TRUE@ nghttp2_stream_test.c \ -@HAVE_CUNIT_TRUE@ nghttp2_session_test.c \ -@HAVE_CUNIT_TRUE@ nghttp2_hd_test.c \ -@HAVE_CUNIT_TRUE@ nghttp2_npn_test.c \ -@HAVE_CUNIT_TRUE@ nghttp2_helper_test.c \ -@HAVE_CUNIT_TRUE@ nghttp2_buf_test.c - -@HAVE_CUNIT_TRUE@HFILES = nghttp2_pq_test.h nghttp2_map_test.h nghttp2_queue_test.h \ -@HAVE_CUNIT_TRUE@ nghttp2_session_test.h \ -@HAVE_CUNIT_TRUE@ nghttp2_frame_test.h nghttp2_stream_test.h nghttp2_hd_test.h \ -@HAVE_CUNIT_TRUE@ nghttp2_npn_test.h nghttp2_helper_test.h \ -@HAVE_CUNIT_TRUE@ nghttp2_test_helper.h \ -@HAVE_CUNIT_TRUE@ nghttp2_buf_test.h - -@HAVE_CUNIT_TRUE@main_SOURCES = $(HFILES) $(OBJECTS) -@HAVE_CUNIT_TRUE@main_LDADD = ${top_builddir}/lib/libnghttp2.la @CUNIT_LIBS@ @TESTLDADD@ -@HAVE_CUNIT_TRUE@main_LDFLAGS = -static -@ENABLE_FAILMALLOC_TRUE@@HAVE_CUNIT_TRUE@failmalloc_SOURCES = failmalloc.c failmalloc_test.c failmalloc_test.h \ -@ENABLE_FAILMALLOC_TRUE@@HAVE_CUNIT_TRUE@ malloc_wrapper.c malloc_wrapper.h \ -@ENABLE_FAILMALLOC_TRUE@@HAVE_CUNIT_TRUE@ nghttp2_test_helper.c nghttp2_test_helper.h - -@ENABLE_FAILMALLOC_TRUE@@HAVE_CUNIT_TRUE@failmalloc_LDADD = $(main_LDADD) -@ENABLE_FAILMALLOC_TRUE@@HAVE_CUNIT_TRUE@failmalloc_LDFLAGS = $(main_LDFLAGS) -@HAVE_CUNIT_TRUE@AM_CFLAGS = $(WARNCFLAGS) \ -@HAVE_CUNIT_TRUE@ -I${top_srcdir}/lib \ -@HAVE_CUNIT_TRUE@ -I${top_srcdir}/lib/includes \ -@HAVE_CUNIT_TRUE@ -I${top_builddir}/lib/includes \ -@HAVE_CUNIT_TRUE@ @CUNIT_CFLAGS@ @DEFS@ - -all: all-recursive - -.SUFFIXES: -.SUFFIXES: .c .lo .log .o .obj .test .test$(EXEEXT) .trs -$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) - @for dep in $?; do \ - case '$(am__configure_deps)' in \ - *$$dep*) \ - ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ - && { if test -f $@; then exit 0; else break; fi; }; \ - exit 1;; \ - esac; \ - done; \ - echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu tests/Makefile'; \ - $(am__cd) $(top_srcdir) && \ - $(AUTOMAKE) --gnu tests/Makefile -.PRECIOUS: Makefile -Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status - @case '$?' in \ - *config.status*) \ - cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ - *) \ - echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ - cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ - esac; - -$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) - cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh - -$(top_srcdir)/configure: $(am__configure_deps) - cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh -$(ACLOCAL_M4): $(am__aclocal_m4_deps) - cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh -$(am__aclocal_m4_deps): - -clean-checkPROGRAMS: - @list='$(check_PROGRAMS)'; test -n "$$list" || exit 0; \ - echo " rm -f" $$list; \ - rm -f $$list || exit $$?; \ - test -n "$(EXEEXT)" || exit 0; \ - list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ - echo " rm -f" $$list; \ - rm -f $$list - -failmalloc$(EXEEXT): $(failmalloc_OBJECTS) $(failmalloc_DEPENDENCIES) $(EXTRA_failmalloc_DEPENDENCIES) - @rm -f failmalloc$(EXEEXT) - $(AM_V_CCLD)$(failmalloc_LINK) $(failmalloc_OBJECTS) $(failmalloc_LDADD) $(LIBS) - -main$(EXEEXT): $(main_OBJECTS) $(main_DEPENDENCIES) $(EXTRA_main_DEPENDENCIES) - @rm -f main$(EXEEXT) - $(AM_V_CCLD)$(main_LINK) $(main_OBJECTS) $(main_LDADD) $(LIBS) - -mostlyclean-compile: - -rm -f *.$(OBJEXT) - -distclean-compile: - -rm -f *.tab.c - -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/failmalloc.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/failmalloc_test.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/main.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/malloc_wrapper.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_buf_test.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_frame_test.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_hd_test.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_helper_test.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_map_test.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_npn_test.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_pq_test.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_queue_test.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_session_test.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_stream_test.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_test_helper.Po@am__quote@ - -.c.o: -@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\ -@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ -@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< - -.c.obj: -@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\ -@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\ -@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` - -.c.lo: -@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\ -@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ -@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< - -mostlyclean-libtool: - -rm -f *.lo - -clean-libtool: - -rm -rf .libs _libs - -# This directory's subdirectories are mostly independent; you can cd -# into them and run 'make' without going through this Makefile. -# To change the values of 'make' variables: instead of editing Makefiles, -# (1) if the variable is set in 'config.status', edit 'config.status' -# (which will cause the Makefiles to be regenerated when you run 'make'); -# (2) otherwise, pass the desired values on the 'make' command line. -$(am__recursive_targets): - @fail=; \ - if $(am__make_keepgoing); then \ - failcom='fail=yes'; \ - else \ - failcom='exit 1'; \ - fi; \ - dot_seen=no; \ - target=`echo $@ | sed s/-recursive//`; \ - case "$@" in \ - distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ - *) list='$(SUBDIRS)' ;; \ - esac; \ - for subdir in $$list; do \ - echo "Making $$target in $$subdir"; \ - if test "$$subdir" = "."; then \ - dot_seen=yes; \ - local_target="$$target-am"; \ - else \ - local_target="$$target"; \ - fi; \ - ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ - || eval $$failcom; \ - done; \ - if test "$$dot_seen" = "no"; then \ - $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ - fi; test -z "$$fail" - -ID: $(am__tagged_files) - $(am__define_uniq_tagged_files); mkid -fID $$unique -tags: tags-recursive -TAGS: tags - -tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) - set x; \ - here=`pwd`; \ - if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \ - include_option=--etags-include; \ - empty_fix=.; \ - else \ - include_option=--include; \ - empty_fix=; \ - fi; \ - list='$(SUBDIRS)'; for subdir in $$list; do \ - if test "$$subdir" = .; then :; else \ - test ! -f $$subdir/TAGS || \ - set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \ - fi; \ - done; \ - $(am__define_uniq_tagged_files); \ - shift; \ - if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ - test -n "$$unique" || unique=$$empty_fix; \ - if test $$# -gt 0; then \ - $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ - "$$@" $$unique; \ - else \ - $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ - $$unique; \ - fi; \ - fi -ctags: ctags-recursive - -CTAGS: ctags -ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) - $(am__define_uniq_tagged_files); \ - test -z "$(CTAGS_ARGS)$$unique" \ - || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ - $$unique - -GTAGS: - here=`$(am__cd) $(top_builddir) && pwd` \ - && $(am__cd) $(top_srcdir) \ - && gtags -i $(GTAGS_ARGS) "$$here" -cscopelist: cscopelist-recursive - -cscopelist-am: $(am__tagged_files) - list='$(am__tagged_files)'; \ - case "$(srcdir)" in \ - [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ - *) sdir=$(subdir)/$(srcdir) ;; \ - esac; \ - for i in $$list; do \ - if test -f "$$i"; then \ - echo "$(subdir)/$$i"; \ - else \ - echo "$$sdir/$$i"; \ - fi; \ - done >> $(top_builddir)/cscope.files - -distclean-tags: - -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags - -# Recover from deleted '.trs' file; this should ensure that -# "rm -f foo.log; make foo.trs" re-run 'foo.test', and re-create -# both 'foo.log' and 'foo.trs'. Break the recipe in two subshells -# to avoid problems with "make -n". -.log.trs: - rm -f $< $@ - $(MAKE) $(AM_MAKEFLAGS) $< - -# Leading 'am--fnord' is there to ensure the list of targets does not -# expand to empty, as could happen e.g. with make check TESTS=''. -am--fnord $(TEST_LOGS) $(TEST_LOGS:.log=.trs): $(am__force_recheck) -am--force-recheck: - @: - -$(TEST_SUITE_LOG): $(TEST_LOGS) - @$(am__set_TESTS_bases); \ - am__f_ok () { test -f "$$1" && test -r "$$1"; }; \ - redo_bases=`for i in $$bases; do \ - am__f_ok $$i.trs && am__f_ok $$i.log || echo $$i; \ - done`; \ - if test -n "$$redo_bases"; then \ - redo_logs=`for i in $$redo_bases; do echo $$i.log; done`; \ - redo_results=`for i in $$redo_bases; do echo $$i.trs; done`; \ - if $(am__make_dryrun); then :; else \ - rm -f $$redo_logs && rm -f $$redo_results || exit 1; \ - fi; \ - fi; \ - if test -n "$$am__remaking_logs"; then \ - echo "fatal: making $(TEST_SUITE_LOG): possible infinite" \ - "recursion detected" >&2; \ - else \ - am__remaking_logs=yes $(MAKE) $(AM_MAKEFLAGS) $$redo_logs; \ - fi; \ - if $(am__make_dryrun); then :; else \ - st=0; \ - errmsg="fatal: making $(TEST_SUITE_LOG): failed to create"; \ - for i in $$redo_bases; do \ - test -f $$i.trs && test -r $$i.trs \ - || { echo "$$errmsg $$i.trs" >&2; st=1; }; \ - test -f $$i.log && test -r $$i.log \ - || { echo "$$errmsg $$i.log" >&2; st=1; }; \ - done; \ - test $$st -eq 0 || exit 1; \ - fi - @$(am__sh_e_setup); $(am__tty_colors); $(am__set_TESTS_bases); \ - ws='[ ]'; \ - results=`for b in $$bases; do echo $$b.trs; done`; \ - test -n "$$results" || results=/dev/null; \ - all=` grep "^$$ws*:test-result:" $$results | wc -l`; \ - pass=` grep "^$$ws*:test-result:$$ws*PASS" $$results | wc -l`; \ - fail=` grep "^$$ws*:test-result:$$ws*FAIL" $$results | wc -l`; \ - skip=` grep "^$$ws*:test-result:$$ws*SKIP" $$results | wc -l`; \ - xfail=`grep "^$$ws*:test-result:$$ws*XFAIL" $$results | wc -l`; \ - xpass=`grep "^$$ws*:test-result:$$ws*XPASS" $$results | wc -l`; \ - error=`grep "^$$ws*:test-result:$$ws*ERROR" $$results | wc -l`; \ - if test `expr $$fail + $$xpass + $$error` -eq 0; then \ - success=true; \ - else \ - success=false; \ - fi; \ - br='==================='; br=$$br$$br$$br$$br; \ - result_count () \ - { \ - if test x"$$1" = x"--maybe-color"; then \ - maybe_colorize=yes; \ - elif test x"$$1" = x"--no-color"; then \ - maybe_colorize=no; \ - else \ - echo "$@: invalid 'result_count' usage" >&2; exit 4; \ - fi; \ - shift; \ - desc=$$1 count=$$2; \ - if test $$maybe_colorize = yes && test $$count -gt 0; then \ - color_start=$$3 color_end=$$std; \ - else \ - color_start= color_end=; \ - fi; \ - echo "$${color_start}# $$desc $$count$${color_end}"; \ - }; \ - create_testsuite_report () \ - { \ - result_count $$1 "TOTAL:" $$all "$$brg"; \ - result_count $$1 "PASS: " $$pass "$$grn"; \ - result_count $$1 "SKIP: " $$skip "$$blu"; \ - result_count $$1 "XFAIL:" $$xfail "$$lgn"; \ - result_count $$1 "FAIL: " $$fail "$$red"; \ - result_count $$1 "XPASS:" $$xpass "$$red"; \ - result_count $$1 "ERROR:" $$error "$$mgn"; \ - }; \ - { \ - echo "$(PACKAGE_STRING): $(subdir)/$(TEST_SUITE_LOG)" | \ - $(am__rst_title); \ - create_testsuite_report --no-color; \ - echo; \ - echo ".. contents:: :depth: 2"; \ - echo; \ - for b in $$bases; do echo $$b; done \ - | $(am__create_global_log); \ - } >$(TEST_SUITE_LOG).tmp || exit 1; \ - mv $(TEST_SUITE_LOG).tmp $(TEST_SUITE_LOG); \ - if $$success; then \ - col="$$grn"; \ - else \ - col="$$red"; \ - test x"$$VERBOSE" = x || cat $(TEST_SUITE_LOG); \ - fi; \ - echo "$${col}$$br$${std}"; \ - echo "$${col}Testsuite summary for $(PACKAGE_STRING)$${std}"; \ - echo "$${col}$$br$${std}"; \ - create_testsuite_report --maybe-color; \ - echo "$$col$$br$$std"; \ - if $$success; then :; else \ - echo "$${col}See $(subdir)/$(TEST_SUITE_LOG)$${std}"; \ - if test -n "$(PACKAGE_BUGREPORT)"; then \ - echo "$${col}Please report to $(PACKAGE_BUGREPORT)$${std}"; \ - fi; \ - echo "$$col$$br$$std"; \ - fi; \ - $$success || exit 1 - -check-TESTS: - @list='$(RECHECK_LOGS)'; test -z "$$list" || rm -f $$list - @list='$(RECHECK_LOGS:.log=.trs)'; test -z "$$list" || rm -f $$list - @test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG) - @set +e; $(am__set_TESTS_bases); \ - log_list=`for i in $$bases; do echo $$i.log; done`; \ - trs_list=`for i in $$bases; do echo $$i.trs; done`; \ - log_list=`echo $$log_list`; trs_list=`echo $$trs_list`; \ - $(MAKE) $(AM_MAKEFLAGS) $(TEST_SUITE_LOG) TEST_LOGS="$$log_list"; \ - exit $$?; -recheck: all $(check_PROGRAMS) - @test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG) - @set +e; $(am__set_TESTS_bases); \ - bases=`for i in $$bases; do echo $$i; done \ - | $(am__list_recheck_tests)` || exit 1; \ - log_list=`for i in $$bases; do echo $$i.log; done`; \ - log_list=`echo $$log_list`; \ - $(MAKE) $(AM_MAKEFLAGS) $(TEST_SUITE_LOG) \ - am__force_recheck=am--force-recheck \ - TEST_LOGS="$$log_list"; \ - exit $$? -main.log: main$(EXEEXT) - @p='main$(EXEEXT)'; \ - b='main'; \ - $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ - --log-file $$b.log --trs-file $$b.trs \ - $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ - "$$tst" $(AM_TESTS_FD_REDIRECT) -failmalloc.log: failmalloc$(EXEEXT) - @p='failmalloc$(EXEEXT)'; \ - b='failmalloc'; \ - $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ - --log-file $$b.log --trs-file $$b.trs \ - $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ - "$$tst" $(AM_TESTS_FD_REDIRECT) -.test.log: - @p='$<'; \ - $(am__set_b); \ - $(am__check_pre) $(TEST_LOG_DRIVER) --test-name "$$f" \ - --log-file $$b.log --trs-file $$b.trs \ - $(am__common_driver_flags) $(AM_TEST_LOG_DRIVER_FLAGS) $(TEST_LOG_DRIVER_FLAGS) -- $(TEST_LOG_COMPILE) \ - "$$tst" $(AM_TESTS_FD_REDIRECT) -@am__EXEEXT_TRUE@.test$(EXEEXT).log: -@am__EXEEXT_TRUE@ @p='$<'; \ -@am__EXEEXT_TRUE@ $(am__set_b); \ -@am__EXEEXT_TRUE@ $(am__check_pre) $(TEST_LOG_DRIVER) --test-name "$$f" \ -@am__EXEEXT_TRUE@ --log-file $$b.log --trs-file $$b.trs \ -@am__EXEEXT_TRUE@ $(am__common_driver_flags) $(AM_TEST_LOG_DRIVER_FLAGS) $(TEST_LOG_DRIVER_FLAGS) -- $(TEST_LOG_COMPILE) \ -@am__EXEEXT_TRUE@ "$$tst" $(AM_TESTS_FD_REDIRECT) - -distdir: $(DISTFILES) - @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ - topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ - list='$(DISTFILES)'; \ - dist_files=`for file in $$list; do echo $$file; done | \ - sed -e "s|^$$srcdirstrip/||;t" \ - -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ - case $$dist_files in \ - */*) $(MKDIR_P) `echo "$$dist_files" | \ - sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ - sort -u` ;; \ - esac; \ - for file in $$dist_files; do \ - if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ - if test -d $$d/$$file; then \ - dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ - if test -d "$(distdir)/$$file"; then \ - find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ - fi; \ - if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ - cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ - find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ - fi; \ - cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ - else \ - test -f "$(distdir)/$$file" \ - || cp -p $$d/$$file "$(distdir)/$$file" \ - || exit 1; \ - fi; \ - done - @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ - if test "$$subdir" = .; then :; else \ - $(am__make_dryrun) \ - || test -d "$(distdir)/$$subdir" \ - || $(MKDIR_P) "$(distdir)/$$subdir" \ - || exit 1; \ - dir1=$$subdir; dir2="$(distdir)/$$subdir"; \ - $(am__relativize); \ - new_distdir=$$reldir; \ - dir1=$$subdir; dir2="$(top_distdir)"; \ - $(am__relativize); \ - new_top_distdir=$$reldir; \ - echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \ - echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \ - ($(am__cd) $$subdir && \ - $(MAKE) $(AM_MAKEFLAGS) \ - top_distdir="$$new_top_distdir" \ - distdir="$$new_distdir" \ - am__remove_distdir=: \ - am__skip_length_check=: \ - am__skip_mode_fix=: \ - distdir) \ - || exit 1; \ - fi; \ - done -check-am: all-am - $(MAKE) $(AM_MAKEFLAGS) $(check_PROGRAMS) - $(MAKE) $(AM_MAKEFLAGS) check-TESTS -check: check-recursive -all-am: Makefile -installdirs: installdirs-recursive -installdirs-am: -install: install-recursive -install-exec: install-exec-recursive -install-data: install-data-recursive -uninstall: uninstall-recursive - -install-am: all-am - @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am - -installcheck: installcheck-recursive -install-strip: - if test -z '$(STRIP)'; then \ - $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ - install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ - install; \ - else \ - $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ - install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ - "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ - fi -mostlyclean-generic: - -test -z "$(TEST_LOGS)" || rm -f $(TEST_LOGS) - -test -z "$(TEST_LOGS:.log=.trs)" || rm -f $(TEST_LOGS:.log=.trs) - -test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG) - -clean-generic: - -distclean-generic: - -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) - -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) - -maintainer-clean-generic: - @echo "This command is intended for maintainers to use" - @echo "it deletes files that may require special tools to rebuild." -clean: clean-recursive - -clean-am: clean-checkPROGRAMS clean-generic clean-libtool \ - mostlyclean-am - -distclean: distclean-recursive - -rm -rf ./$(DEPDIR) - -rm -f Makefile -distclean-am: clean-am distclean-compile distclean-generic \ - distclean-tags - -dvi: dvi-recursive - -dvi-am: - -html: html-recursive - -html-am: - -info: info-recursive - -info-am: - -install-data-am: - -install-dvi: install-dvi-recursive - -install-dvi-am: - -install-exec-am: - -install-html: install-html-recursive - -install-html-am: - -install-info: install-info-recursive - -install-info-am: - -install-man: - -install-pdf: install-pdf-recursive - -install-pdf-am: - -install-ps: install-ps-recursive - -install-ps-am: - -installcheck-am: - -maintainer-clean: maintainer-clean-recursive - -rm -rf ./$(DEPDIR) - -rm -f Makefile -maintainer-clean-am: distclean-am maintainer-clean-generic - -mostlyclean: mostlyclean-recursive - -mostlyclean-am: mostlyclean-compile mostlyclean-generic \ - mostlyclean-libtool - -pdf: pdf-recursive - -pdf-am: - -ps: ps-recursive - -ps-am: - -uninstall-am: - -.MAKE: $(am__recursive_targets) check-am install-am install-strip - -.PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am check \ - check-TESTS check-am clean clean-checkPROGRAMS clean-generic \ - clean-libtool cscopelist-am ctags ctags-am distclean \ - distclean-compile distclean-generic distclean-libtool \ - distclean-tags distdir dvi dvi-am html html-am info info-am \ - install install-am install-data install-data-am install-dvi \ - install-dvi-am install-exec install-exec-am install-html \ - install-html-am install-info install-info-am install-man \ - install-pdf install-pdf-am install-ps install-ps-am \ - install-strip installcheck installcheck-am installdirs \ - installdirs-am maintainer-clean maintainer-clean-generic \ - mostlyclean mostlyclean-compile mostlyclean-generic \ - mostlyclean-libtool pdf pdf-am ps ps-am recheck tags tags-am \ - uninstall uninstall-am - - -# EXTRA_DIST = end_to_end.py -# TESTS += end_to_end.py - -# Tell versions [3.59,3.63) of GNU make to not export all variables. -# Otherwise a system limit (for SysV at least) may be exceeded. -.NOEXPORT: diff --git a/tests/failmalloc.c b/tests/failmalloc.c deleted file mode 100644 index 8c9f16d..0000000 --- a/tests/failmalloc.c +++ /dev/null @@ -1,77 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2012 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#ifdef HAVE_CONFIG_H -#include -#endif /* HAVE_CONFIG_H */ - -#include -#include -#include -/* include test cases' include files here */ - -#include "failmalloc_test.h" - -static int init_suite1(void) { return 0; } - -static int clean_suite1(void) { return 0; } - -int main(int argc _U_, char *argv[] _U_) { - CU_pSuite pSuite = NULL; - unsigned int num_tests_failed; - - /* initialize the CUnit test registry */ - if (CUE_SUCCESS != CU_initialize_registry()) - return CU_get_error(); - - /* add a suite to the registry */ - pSuite = CU_add_suite("libnghttp2_TestSuite", init_suite1, clean_suite1); - if (NULL == pSuite) { - CU_cleanup_registry(); - return CU_get_error(); - } - - /* add the tests to the suite */ - if (!CU_add_test(pSuite, "failmalloc_session_send", - test_nghttp2_session_send) || - !CU_add_test(pSuite, "failmalloc_session_recv", - test_nghttp2_session_recv) || - !CU_add_test(pSuite, "failmalloc_frame", test_nghttp2_frame) || - !CU_add_test(pSuite, "failmalloc_hd", test_nghttp2_hd)) { - CU_cleanup_registry(); - return CU_get_error(); - } - - /* Run all tests using the CUnit Basic interface */ - CU_basic_set_mode(CU_BRM_VERBOSE); - CU_basic_run_tests(); - num_tests_failed = CU_get_number_of_tests_failed(); - CU_cleanup_registry(); - if (CU_get_error() == CUE_SUCCESS) { - return num_tests_failed; - } else { - printf("CUnit Error: %s\n", CU_get_error_msg()); - return CU_get_error(); - } -} diff --git a/tests/failmalloc_test.c b/tests/failmalloc_test.c deleted file mode 100644 index 42062d3..0000000 --- a/tests/failmalloc_test.c +++ /dev/null @@ -1,509 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2012, 2014 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#include "failmalloc_test.h" - -#include - -#include -#include - -#include "nghttp2_session.h" -#include "nghttp2_stream.h" -#include "nghttp2_frame.h" -#include "nghttp2_helper.h" -#include "malloc_wrapper.h" -#include "nghttp2_test_helper.h" - -typedef struct { - uint8_t data[8192]; - uint8_t *datamark, *datalimit; -} data_feed; - -typedef struct { - data_feed *df; - size_t data_source_length; -} my_user_data; - -static void data_feed_init(data_feed *df, nghttp2_bufs *bufs) { - nghttp2_buf *buf; - size_t data_length; - - buf = &bufs->head->buf; - data_length = nghttp2_buf_len(buf); - - assert(data_length <= sizeof(df->data)); - memcpy(df->data, buf->pos, data_length); - df->datamark = df->data; - df->datalimit = df->data + data_length; -} - -static ssize_t null_send_callback(nghttp2_session *session _U_, - const uint8_t *data _U_, size_t len, - int flags _U_, void *user_data _U_) { - return len; -} - -static ssize_t data_feed_recv_callback(nghttp2_session *session _U_, - uint8_t *data, size_t len, int flags _U_, - void *user_data) { - data_feed *df = ((my_user_data *)user_data)->df; - size_t avail = df->datalimit - df->datamark; - size_t wlen = nghttp2_min(avail, len); - memcpy(data, df->datamark, wlen); - df->datamark += wlen; - return wlen; -} - -static ssize_t fixed_length_data_source_read_callback( - nghttp2_session *session _U_, int32_t stream_id _U_, uint8_t *buf _U_, - size_t len, uint32_t *data_flags, nghttp2_data_source *source _U_, - void *user_data) { - my_user_data *ud = (my_user_data *)user_data; - size_t wlen; - if (len < ud->data_source_length) { - wlen = len; - } else { - wlen = ud->data_source_length; - } - ud->data_source_length -= wlen; - if (ud->data_source_length == 0) { - *data_flags = NGHTTP2_DATA_FLAG_EOF; - } - return wlen; -} - -#define TEST_FAILMALLOC_RUN(FUN) \ - do { \ - int nmalloc, i; \ - \ - nghttp2_failmalloc = 0; \ - nghttp2_nmalloc = 0; \ - FUN(); \ - nmalloc = nghttp2_nmalloc; \ - \ - nghttp2_failmalloc = 1; \ - for (i = 0; i < nmalloc; ++i) { \ - nghttp2_nmalloc = 0; \ - nghttp2_failstart = i; \ - /* printf("i=%zu\n", i); */ \ - FUN(); \ - /* printf("nmalloc=%d\n", nghttp2_nmalloc); */ \ - } \ - nghttp2_failmalloc = 0; \ - } while (0) - -static void run_nghttp2_session_send(void) { - nghttp2_session *session; - nghttp2_session_callbacks callbacks; - nghttp2_nv nv[] = {MAKE_NV(":host", "example.org"), - MAKE_NV(":scheme", "https")}; - nghttp2_data_provider data_prd; - nghttp2_settings_entry iv[2]; - my_user_data ud; - int rv; - memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); - callbacks.send_callback = null_send_callback; - - data_prd.read_callback = fixed_length_data_source_read_callback; - ud.data_source_length = 64 * 1024; - - iv[0].settings_id = NGHTTP2_SETTINGS_HEADER_TABLE_SIZE; - iv[0].value = 4096; - iv[1].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS; - iv[1].value = 100; - - rv = nghttp2_session_client_new3(&session, &callbacks, &ud, NULL, - nghttp2_mem_fm()); - if (rv != 0) { - goto client_new_fail; - } - rv = nghttp2_submit_request(session, NULL, nv, ARRLEN(nv), &data_prd, NULL); - if (rv < 0) { - goto fail; - } - rv = nghttp2_submit_headers(session, NGHTTP2_FLAG_NONE, -1, NULL, nv, - ARRLEN(nv), NULL); - if (rv < 0) { - goto fail; - } - rv = nghttp2_session_send(session); - if (rv != 0) { - goto fail; - } - /* The HEADERS submitted by the previous nghttp2_submit_headers will - have stream ID 3. Send HEADERS to that stream. */ - rv = nghttp2_submit_headers(session, NGHTTP2_FLAG_NONE, 3, NULL, nv, - ARRLEN(nv), NULL); - if (rv != 0) { - goto fail; - } - rv = nghttp2_submit_data(session, NGHTTP2_FLAG_END_STREAM, 3, &data_prd); - if (rv != 0) { - goto fail; - } - rv = nghttp2_session_send(session); - if (rv != 0) { - goto fail; - } - rv = nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, 3, NGHTTP2_CANCEL); - if (rv != 0) { - goto fail; - } - rv = nghttp2_session_send(session); - if (rv != 0) { - goto fail; - } - /* Sending against half-closed stream */ - rv = nghttp2_submit_headers(session, NGHTTP2_FLAG_NONE, 3, NULL, nv, - ARRLEN(nv), NULL); - if (rv != 0) { - goto fail; - } - rv = nghttp2_submit_data(session, NGHTTP2_FLAG_END_STREAM, 3, &data_prd); - if (rv != 0) { - goto fail; - } - rv = nghttp2_submit_ping(session, NGHTTP2_FLAG_NONE, NULL); - if (rv != 0) { - goto fail; - } - rv = nghttp2_submit_settings(session, NGHTTP2_FLAG_NONE, iv, 2); - if (rv != 0) { - goto fail; - } - rv = nghttp2_session_send(session); - if (rv != 0) { - goto fail; - } - rv = nghttp2_submit_goaway(session, NGHTTP2_FLAG_NONE, 100, NGHTTP2_NO_ERROR, - NULL, 0); - if (rv != 0) { - goto fail; - } - rv = nghttp2_session_send(session); - if (rv != 0) { - goto fail; - } - -fail: - nghttp2_session_del(session); -client_new_fail: - ; -} - -void test_nghttp2_session_send(void) { - TEST_FAILMALLOC_RUN(run_nghttp2_session_send); -} - -static void run_nghttp2_session_recv(void) { - nghttp2_session *session; - nghttp2_session_callbacks callbacks; - nghttp2_hd_deflater deflater; - nghttp2_frame frame; - nghttp2_bufs bufs; - nghttp2_nv nv[] = {MAKE_NV(":authority", "example.org"), - MAKE_NV(":scheme", "https")}; - nghttp2_settings_entry iv[2]; - my_user_data ud; - data_feed df; - int rv; - nghttp2_nv *nva; - ssize_t nvlen; - - rv = frame_pack_bufs_init(&bufs); - - if (rv != 0) { - return; - } - - memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); - callbacks.recv_callback = data_feed_recv_callback; - ud.df = &df; - - nghttp2_failmalloc_pause(); - nvlen = ARRLEN(nv); - nghttp2_nv_array_copy(&nva, nv, nvlen, nghttp2_mem_fm()); - nghttp2_hd_deflate_init(&deflater, nghttp2_mem_fm()); - nghttp2_session_server_new3(&session, &callbacks, &ud, NULL, - nghttp2_mem_fm()); - nghttp2_failmalloc_unpause(); - - /* HEADERS */ - nghttp2_failmalloc_pause(); - nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_END_STREAM, 1, - NGHTTP2_HCAT_REQUEST, NULL, nva, nvlen); - nghttp2_frame_pack_headers(&bufs, &frame.headers, &deflater); - nghttp2_frame_headers_free(&frame.headers, nghttp2_mem_fm()); - data_feed_init(&df, &bufs); - nghttp2_bufs_reset(&bufs); - - nghttp2_failmalloc_unpause(); - - rv = nghttp2_session_recv(session); - if (rv != 0) { - goto fail; - } - - /* PING */ - nghttp2_failmalloc_pause(); - nghttp2_frame_ping_init(&frame.ping, NGHTTP2_FLAG_NONE, NULL); - nghttp2_frame_pack_ping(&bufs, &frame.ping); - nghttp2_frame_ping_free(&frame.ping); - data_feed_init(&df, &bufs); - nghttp2_bufs_reset(&bufs); - - nghttp2_failmalloc_unpause(); - - rv = nghttp2_session_recv(session); - if (rv != 0) { - goto fail; - } - - /* RST_STREAM */ - nghttp2_failmalloc_pause(); - nghttp2_frame_rst_stream_init(&frame.rst_stream, 1, NGHTTP2_PROTOCOL_ERROR); - nghttp2_frame_pack_rst_stream(&bufs, &frame.rst_stream); - nghttp2_frame_rst_stream_free(&frame.rst_stream); - nghttp2_bufs_reset(&bufs); - - nghttp2_failmalloc_unpause(); - - rv = nghttp2_session_recv(session); - if (rv != 0) { - goto fail; - } - - /* SETTINGS */ - nghttp2_failmalloc_pause(); - iv[0].settings_id = NGHTTP2_SETTINGS_HEADER_TABLE_SIZE; - iv[0].value = 4096; - iv[1].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS; - iv[1].value = 100; - nghttp2_frame_settings_init(&frame.settings, NGHTTP2_FLAG_NONE, - nghttp2_frame_iv_copy(iv, 2, nghttp2_mem_fm()), - 2); - nghttp2_frame_pack_settings(&bufs, &frame.settings); - nghttp2_frame_settings_free(&frame.settings, nghttp2_mem_fm()); - nghttp2_bufs_reset(&bufs); - - nghttp2_failmalloc_unpause(); - - rv = nghttp2_session_recv(session); - if (rv != 0) { - goto fail; - } - -fail: - nghttp2_bufs_free(&bufs); - nghttp2_session_del(session); - nghttp2_hd_deflate_free(&deflater); -} - -void test_nghttp2_session_recv(void) { - TEST_FAILMALLOC_RUN(run_nghttp2_session_recv); -} - -static void run_nghttp2_frame_pack_headers(void) { - nghttp2_hd_deflater deflater; - nghttp2_hd_inflater inflater; - nghttp2_frame frame, oframe; - nghttp2_bufs bufs; - nghttp2_nv nv[] = {MAKE_NV(":host", "example.org"), - MAKE_NV(":scheme", "https")}; - int rv; - nghttp2_nv *nva; - ssize_t nvlen; - - rv = frame_pack_bufs_init(&bufs); - - if (rv != 0) { - return; - } - - rv = nghttp2_hd_deflate_init(&deflater, nghttp2_mem_fm()); - if (rv != 0) { - goto deflate_init_fail; - } - rv = nghttp2_hd_inflate_init(&inflater, nghttp2_mem_fm()); - if (rv != 0) { - goto inflate_init_fail; - } - nvlen = ARRLEN(nv); - rv = nghttp2_nv_array_copy(&nva, nv, nvlen, nghttp2_mem_fm()); - if (rv < 0) { - goto nv_copy_fail; - } - nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_END_STREAM, 1, - NGHTTP2_HCAT_REQUEST, NULL, nva, nvlen); - rv = nghttp2_frame_pack_headers(&bufs, &frame.headers, &deflater); - if (rv != 0) { - goto fail; - } - rv = unpack_framebuf(&oframe, &bufs); - if (rv != 0) { - goto fail; - } - nghttp2_frame_headers_free(&oframe.headers, nghttp2_mem_fm()); - -fail: - nghttp2_frame_headers_free(&frame.headers, nghttp2_mem_fm()); -nv_copy_fail: - nghttp2_hd_inflate_free(&inflater); -inflate_init_fail: - nghttp2_hd_deflate_free(&deflater); -deflate_init_fail: - nghttp2_bufs_free(&bufs); -} - -static void run_nghttp2_frame_pack_settings(void) { - nghttp2_frame frame, oframe; - nghttp2_bufs bufs; - nghttp2_buf *buf; - nghttp2_settings_entry iv[2], *iv_copy; - int rv; - - rv = frame_pack_bufs_init(&bufs); - - if (rv != 0) { - return; - } - - iv[0].settings_id = NGHTTP2_SETTINGS_HEADER_TABLE_SIZE; - iv[0].value = 4096; - iv[1].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS; - iv[1].value = 100; - - iv_copy = nghttp2_frame_iv_copy(iv, 2, nghttp2_mem_fm()); - - if (iv_copy == NULL) { - goto iv_copy_fail; - } - - nghttp2_frame_settings_init(&frame.settings, NGHTTP2_FLAG_NONE, iv_copy, 2); - - rv = nghttp2_frame_pack_settings(&bufs, &frame.settings); - - if (rv != 0) { - goto fail; - } - - buf = &bufs.head->buf; - - rv = nghttp2_frame_unpack_settings_payload2( - &oframe.settings.iv, &oframe.settings.niv, buf->pos + NGHTTP2_FRAME_HDLEN, - nghttp2_buf_len(buf) - NGHTTP2_FRAME_HDLEN, nghttp2_mem_fm()); - - if (rv != 0) { - goto fail; - } - nghttp2_frame_settings_free(&oframe.settings, nghttp2_mem_fm()); - -fail: - nghttp2_frame_settings_free(&frame.settings, nghttp2_mem_fm()); -iv_copy_fail: - nghttp2_bufs_free(&bufs); -} - -void test_nghttp2_frame(void) { - TEST_FAILMALLOC_RUN(run_nghttp2_frame_pack_headers); - TEST_FAILMALLOC_RUN(run_nghttp2_frame_pack_settings); -} - -static int deflate_inflate(nghttp2_hd_deflater *deflater, - nghttp2_hd_inflater *inflater, nghttp2_bufs *bufs, - nghttp2_nv *nva, size_t nvlen, nghttp2_mem *mem) { - int rv; - - rv = nghttp2_hd_deflate_hd_bufs(deflater, bufs, nva, nvlen); - - if (rv != 0) { - return rv; - } - - rv = (int)inflate_hd(inflater, NULL, bufs, 0, mem); - - if (rv < 0) { - return rv; - } - - nghttp2_bufs_reset(bufs); - - return 0; -} - -static void run_nghttp2_hd(void) { - nghttp2_hd_deflater deflater; - nghttp2_hd_inflater inflater; - nghttp2_bufs bufs; - int rv; - nghttp2_nv nva1[] = {MAKE_NV(":scheme", "https"), - MAKE_NV(":authority", "example.org"), - MAKE_NV(":path", "/slashdot"), - MAKE_NV("accept-encoding", "gzip, deflate")}; - nghttp2_nv nva2[] = { - MAKE_NV(":scheme", "https"), MAKE_NV(":authority", "example.org"), - MAKE_NV(":path", "/style.css"), MAKE_NV("cookie", "nghttp2=FTW")}; - - rv = frame_pack_bufs_init(&bufs); - - if (rv != 0) { - return; - } - - rv = nghttp2_hd_deflate_init(&deflater, nghttp2_mem_fm()); - - if (rv != 0) { - goto deflate_init_fail; - } - - rv = nghttp2_hd_inflate_init(&inflater, nghttp2_mem_fm()); - - if (rv != 0) { - goto inflate_init_fail; - } - - rv = deflate_inflate(&deflater, &inflater, &bufs, nva1, ARRLEN(nva1), - nghttp2_mem_fm()); - - if (rv != 0) { - goto deflate_hd_fail; - } - - rv = deflate_inflate(&deflater, &inflater, &bufs, nva2, ARRLEN(nva2), - nghttp2_mem_fm()); - - if (rv != 0) { - goto deflate_hd_fail; - } - -deflate_hd_fail: - nghttp2_hd_inflate_free(&inflater); -inflate_init_fail: - nghttp2_hd_deflate_free(&deflater); -deflate_init_fail: - nghttp2_bufs_free(&bufs); -} - -void test_nghttp2_hd(void) { TEST_FAILMALLOC_RUN(run_nghttp2_hd); } diff --git a/tests/failmalloc_test.h b/tests/failmalloc_test.h deleted file mode 100644 index 1969bc1..0000000 --- a/tests/failmalloc_test.h +++ /dev/null @@ -1,33 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2012 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#ifndef FAILMALLOC_TEST_H -#define FAILMALLOC_TEST_H - -void test_nghttp2_session_send(void); -void test_nghttp2_session_recv(void); -void test_nghttp2_frame(void); -void test_nghttp2_hd(void); - -#endif /* FAILMALLOC_TEST_H */ diff --git a/tests/main.c b/tests/main.c deleted file mode 100644 index 1d187f7..0000000 --- a/tests/main.c +++ /dev/null @@ -1,368 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2012 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#ifdef HAVE_CONFIG_H -#include -#endif /* HAVE_CONFIG_H */ - -#include -#include -#include -/* include test cases' include files here */ -#include "nghttp2_pq_test.h" -#include "nghttp2_map_test.h" -#include "nghttp2_queue_test.h" -#include "nghttp2_session_test.h" -#include "nghttp2_frame_test.h" -#include "nghttp2_stream_test.h" -#include "nghttp2_hd_test.h" -#include "nghttp2_npn_test.h" -#include "nghttp2_helper_test.h" -#include "nghttp2_buf_test.h" - -extern int nghttp2_enable_strict_preface; - -static int init_suite1(void) { return 0; } - -static int clean_suite1(void) { return 0; } - -int main(int argc _U_, char *argv[] _U_) { - CU_pSuite pSuite = NULL; - unsigned int num_tests_failed; - - nghttp2_enable_strict_preface = 0; - - /* initialize the CUnit test registry */ - if (CUE_SUCCESS != CU_initialize_registry()) - return CU_get_error(); - - /* add a suite to the registry */ - pSuite = CU_add_suite("libnghttp2_TestSuite", init_suite1, clean_suite1); - if (NULL == pSuite) { - CU_cleanup_registry(); - return CU_get_error(); - } - - /* add the tests to the suite */ - if (!CU_add_test(pSuite, "pq", test_nghttp2_pq) || - !CU_add_test(pSuite, "pq_update", test_nghttp2_pq_update) || - !CU_add_test(pSuite, "map", test_nghttp2_map) || - !CU_add_test(pSuite, "map_functional", test_nghttp2_map_functional) || - !CU_add_test(pSuite, "map_each_free", test_nghttp2_map_each_free) || - !CU_add_test(pSuite, "queue", test_nghttp2_queue) || - !CU_add_test(pSuite, "npn", test_nghttp2_npn) || - !CU_add_test(pSuite, "session_recv", test_nghttp2_session_recv) || - !CU_add_test(pSuite, "session_recv_invalid_stream_id", - test_nghttp2_session_recv_invalid_stream_id) || - !CU_add_test(pSuite, "session_recv_invalid_frame", - test_nghttp2_session_recv_invalid_frame) || - !CU_add_test(pSuite, "session_recv_eof", test_nghttp2_session_recv_eof) || - !CU_add_test(pSuite, "session_recv_data", - test_nghttp2_session_recv_data) || - !CU_add_test(pSuite, "session_recv_continuation", - test_nghttp2_session_recv_continuation) || - !CU_add_test(pSuite, "session_recv_headers_with_priority", - test_nghttp2_session_recv_headers_with_priority) || - !CU_add_test(pSuite, "session_recv_premature_headers", - test_nghttp2_session_recv_premature_headers) || - !CU_add_test(pSuite, "session_recv_unknown_frame", - test_nghttp2_session_recv_unknown_frame) || - !CU_add_test(pSuite, "session_recv_unexpected_continuation", - test_nghttp2_session_recv_unexpected_continuation) || - !CU_add_test(pSuite, "session_recv_settings_header_table_size", - test_nghttp2_session_recv_settings_header_table_size) || - !CU_add_test(pSuite, "session_recv_too_large_frame_length", - test_nghttp2_session_recv_too_large_frame_length) || - !CU_add_test(pSuite, "session_continue", test_nghttp2_session_continue) || - !CU_add_test(pSuite, "session_add_frame", - test_nghttp2_session_add_frame) || - !CU_add_test(pSuite, "session_on_request_headers_received", - test_nghttp2_session_on_request_headers_received) || - !CU_add_test(pSuite, "session_on_response_headers_received", - test_nghttp2_session_on_response_headers_received) || - !CU_add_test(pSuite, "session_on_headers_received", - test_nghttp2_session_on_headers_received) || - !CU_add_test(pSuite, "session_on_push_response_headers_received", - test_nghttp2_session_on_push_response_headers_received) || - !CU_add_test(pSuite, "session_on_priority_received", - test_nghttp2_session_on_priority_received) || - !CU_add_test(pSuite, "session_on_rst_stream_received", - test_nghttp2_session_on_rst_stream_received) || - !CU_add_test(pSuite, "session_on_settings_received", - test_nghttp2_session_on_settings_received) || - !CU_add_test(pSuite, "session_on_push_promise_received", - test_nghttp2_session_on_push_promise_received) || - !CU_add_test(pSuite, "session_on_ping_received", - test_nghttp2_session_on_ping_received) || - !CU_add_test(pSuite, "session_on_goaway_received", - test_nghttp2_session_on_goaway_received) || - !CU_add_test(pSuite, "session_on_window_update_received", - test_nghttp2_session_on_window_update_received) || - !CU_add_test(pSuite, "session_on_data_received", - test_nghttp2_session_on_data_received) || - !CU_add_test(pSuite, "session_send_headers_start_stream", - test_nghttp2_session_send_headers_start_stream) || - !CU_add_test(pSuite, "session_send_headers_reply", - test_nghttp2_session_send_headers_reply) || - !CU_add_test(pSuite, "session_send_headers_frame_size_error", - test_nghttp2_session_send_headers_frame_size_error) || - !CU_add_test(pSuite, "session_send_headers_push_reply", - test_nghttp2_session_send_headers_push_reply) || - !CU_add_test(pSuite, "session_send_rst_stream", - test_nghttp2_session_send_rst_stream) || - !CU_add_test(pSuite, "session_send_push_promise", - test_nghttp2_session_send_push_promise) || - !CU_add_test(pSuite, "session_is_my_stream_id", - test_nghttp2_session_is_my_stream_id) || - !CU_add_test(pSuite, "session_upgrade", test_nghttp2_session_upgrade) || - !CU_add_test(pSuite, "session_reprioritize_stream", - test_nghttp2_session_reprioritize_stream) || - !CU_add_test( - pSuite, "session_reprioritize_stream_with_idle_stream_dep", - test_nghttp2_session_reprioritize_stream_with_idle_stream_dep) || - !CU_add_test(pSuite, "submit_data", test_nghttp2_submit_data) || - !CU_add_test(pSuite, "submit_data_read_length_too_large", - test_nghttp2_submit_data_read_length_too_large) || - !CU_add_test(pSuite, "submit_data_twice", - test_nghttp2_submit_data_twice) || - !CU_add_test(pSuite, "submit_request_with_data", - test_nghttp2_submit_request_with_data) || - !CU_add_test(pSuite, "submit_request_without_data", - test_nghttp2_submit_request_without_data) || - !CU_add_test(pSuite, "submit_response_with_data", - test_nghttp2_submit_response_with_data) || - !CU_add_test(pSuite, "submit_response_without_data", - test_nghttp2_submit_response_without_data) || - !CU_add_test(pSuite, "submit_trailer", test_nghttp2_submit_trailer) || - !CU_add_test(pSuite, "submit_headers_start_stream", - test_nghttp2_submit_headers_start_stream) || - !CU_add_test(pSuite, "submit_headers_reply", - test_nghttp2_submit_headers_reply) || - !CU_add_test(pSuite, "submit_headers_push_reply", - test_nghttp2_submit_headers_push_reply) || - !CU_add_test(pSuite, "submit_headers", test_nghttp2_submit_headers) || - !CU_add_test(pSuite, "submit_headers_continuation", - test_nghttp2_submit_headers_continuation) || - !CU_add_test(pSuite, "submit_priority", test_nghttp2_submit_priority) || - !CU_add_test(pSuite, "session_submit_settings", - test_nghttp2_submit_settings) || - !CU_add_test(pSuite, "session_submit_settings_update_local_window_size", - test_nghttp2_submit_settings_update_local_window_size) || - !CU_add_test(pSuite, "session_submit_push_promise", - test_nghttp2_submit_push_promise) || - !CU_add_test(pSuite, "submit_window_update", - test_nghttp2_submit_window_update) || - !CU_add_test(pSuite, "submit_window_update_local_window_size", - test_nghttp2_submit_window_update_local_window_size) || - !CU_add_test(pSuite, "submit_shutdown_notice", - test_nghttp2_submit_shutdown_notice) || - !CU_add_test(pSuite, "submit_invalid_nv", - test_nghttp2_submit_invalid_nv) || - !CU_add_test(pSuite, "session_open_stream", - test_nghttp2_session_open_stream) || - !CU_add_test(pSuite, "session_open_stream_with_idle_stream_dep", - test_nghttp2_session_open_stream_with_idle_stream_dep) || - !CU_add_test(pSuite, "session_get_next_ob_item", - test_nghttp2_session_get_next_ob_item) || - !CU_add_test(pSuite, "session_pop_next_ob_item", - test_nghttp2_session_pop_next_ob_item) || - !CU_add_test(pSuite, "session_reply_fail", - test_nghttp2_session_reply_fail) || - !CU_add_test(pSuite, "session_max_concurrent_streams", - test_nghttp2_session_max_concurrent_streams) || - !CU_add_test(pSuite, "session_stream_close_on_headers_push", - test_nghttp2_session_stream_close_on_headers_push) || - !CU_add_test(pSuite, "session_stop_data_with_rst_stream", - test_nghttp2_session_stop_data_with_rst_stream) || - !CU_add_test(pSuite, "session_defer_data", - test_nghttp2_session_defer_data) || - !CU_add_test(pSuite, "session_flow_control", - test_nghttp2_session_flow_control) || - !CU_add_test(pSuite, "session_flow_control_data_recv", - test_nghttp2_session_flow_control_data_recv) || - !CU_add_test(pSuite, "session_flow_control_data_with_padding_recv", - test_nghttp2_session_flow_control_data_with_padding_recv) || - !CU_add_test(pSuite, "session_data_read_temporal_failure", - test_nghttp2_session_data_read_temporal_failure) || - !CU_add_test(pSuite, "session_on_stream_close", - test_nghttp2_session_on_stream_close) || - !CU_add_test(pSuite, "session_on_ctrl_not_send", - test_nghttp2_session_on_ctrl_not_send) || - !CU_add_test(pSuite, "session_get_outbound_queue_size", - test_nghttp2_session_get_outbound_queue_size) || - !CU_add_test(pSuite, "session_get_effective_local_window_size", - test_nghttp2_session_get_effective_local_window_size) || - !CU_add_test(pSuite, "session_set_option", - test_nghttp2_session_set_option) || - !CU_add_test(pSuite, "session_data_backoff_by_high_pri_frame", - test_nghttp2_session_data_backoff_by_high_pri_frame) || - !CU_add_test(pSuite, "session_pack_data_with_padding", - test_nghttp2_session_pack_data_with_padding) || - !CU_add_test(pSuite, "session_pack_headers_with_padding", - test_nghttp2_session_pack_headers_with_padding) || - !CU_add_test(pSuite, "pack_settings_payload", - test_nghttp2_pack_settings_payload) || - !CU_add_test(pSuite, "session_stream_dep_add", - test_nghttp2_session_stream_dep_add) || - !CU_add_test(pSuite, "session_stream_dep_remove", - test_nghttp2_session_stream_dep_remove) || - !CU_add_test(pSuite, "session_stream_dep_add_subtree", - test_nghttp2_session_stream_dep_add_subtree) || - !CU_add_test(pSuite, "session_stream_dep_remove_subtree", - test_nghttp2_session_stream_dep_remove_subtree) || - !CU_add_test( - pSuite, "session_stream_dep_all_your_stream_are_belong_to_us", - test_nghttp2_session_stream_dep_all_your_stream_are_belong_to_us) || - !CU_add_test(pSuite, "session_stream_attach_item", - test_nghttp2_session_stream_attach_item) || - !CU_add_test(pSuite, "session_stream_attach_item_subtree", - test_nghttp2_session_stream_attach_item_subtree) || - !CU_add_test(pSuite, "session_stream_keep_closed_stream", - test_nghttp2_session_keep_closed_stream) || - !CU_add_test(pSuite, "session_stream_keep_idle_stream", - test_nghttp2_session_keep_idle_stream) || - !CU_add_test(pSuite, "session_detach_idle_stream", - test_nghttp2_session_detach_idle_stream) || - !CU_add_test(pSuite, "session_large_dep_tree", - test_nghttp2_session_large_dep_tree) || - !CU_add_test(pSuite, "session_graceful_shutdown", - test_nghttp2_session_graceful_shutdown) || - !CU_add_test(pSuite, "session_on_header_temporal_failure", - test_nghttp2_session_on_header_temporal_failure) || - !CU_add_test(pSuite, "session_recv_client_magic", - test_nghttp2_session_recv_client_magic) || - !CU_add_test(pSuite, "session_delete_data_item", - test_nghttp2_session_delete_data_item) || - !CU_add_test(pSuite, "session_open_idle_stream", - test_nghttp2_session_open_idle_stream) || - !CU_add_test(pSuite, "session_cancel_reserved_remote", - test_nghttp2_session_cancel_reserved_remote) || - !CU_add_test(pSuite, "session_reset_pending_headers", - test_nghttp2_session_reset_pending_headers) || - !CU_add_test(pSuite, "session_send_data_callback", - test_nghttp2_session_send_data_callback) || - !CU_add_test(pSuite, "session_on_begin_headers_temporal_failure", - test_nghttp2_session_on_begin_headers_temporal_failure) || - !CU_add_test(pSuite, "http_mandatory_headers", - test_nghttp2_http_mandatory_headers) || - !CU_add_test(pSuite, "http_content_length", - test_nghttp2_http_content_length) || - !CU_add_test(pSuite, "http_content_length_mismatch", - test_nghttp2_http_content_length_mismatch) || - !CU_add_test(pSuite, "http_non_final_response", - test_nghttp2_http_non_final_response) || - !CU_add_test(pSuite, "http_trailer_headers", - test_nghttp2_http_trailer_headers) || - !CU_add_test(pSuite, "http_ignore_regular_header", - test_nghttp2_http_ignore_regular_header) || - !CU_add_test(pSuite, "http_ignore_content_length", - test_nghttp2_http_ignore_content_length) || - !CU_add_test(pSuite, "http_record_request_method", - test_nghttp2_http_record_request_method) || - !CU_add_test(pSuite, "http_push_promise", - test_nghttp2_http_push_promise) || - !CU_add_test(pSuite, "frame_pack_headers", - test_nghttp2_frame_pack_headers) || - !CU_add_test(pSuite, "frame_pack_headers_frame_too_large", - test_nghttp2_frame_pack_headers_frame_too_large) || - !CU_add_test(pSuite, "frame_pack_headers_frame_smallest", - test_nghttp2_submit_data_read_length_smallest) || - !CU_add_test(pSuite, "frame_pack_priority", - test_nghttp2_frame_pack_priority) || - !CU_add_test(pSuite, "frame_pack_rst_stream", - test_nghttp2_frame_pack_rst_stream) || - !CU_add_test(pSuite, "frame_pack_settings", - test_nghttp2_frame_pack_settings) || - !CU_add_test(pSuite, "frame_pack_push_promise", - test_nghttp2_frame_pack_push_promise) || - !CU_add_test(pSuite, "frame_pack_ping", test_nghttp2_frame_pack_ping) || - !CU_add_test(pSuite, "frame_pack_goaway", - test_nghttp2_frame_pack_goaway) || - !CU_add_test(pSuite, "frame_pack_window_update", - test_nghttp2_frame_pack_window_update) || - !CU_add_test(pSuite, "nv_array_copy", test_nghttp2_nv_array_copy) || - !CU_add_test(pSuite, "iv_check", test_nghttp2_iv_check) || - !CU_add_test(pSuite, "hd_deflate", test_nghttp2_hd_deflate) || - !CU_add_test(pSuite, "hd_deflate_same_indexed_repr", - test_nghttp2_hd_deflate_same_indexed_repr) || - !CU_add_test(pSuite, "hd_inflate_indexed", - test_nghttp2_hd_inflate_indexed) || - !CU_add_test(pSuite, "hd_inflate_indname_noinc", - test_nghttp2_hd_inflate_indname_noinc) || - !CU_add_test(pSuite, "hd_inflate_indname_inc", - test_nghttp2_hd_inflate_indname_inc) || - !CU_add_test(pSuite, "hd_inflate_indname_inc_eviction", - test_nghttp2_hd_inflate_indname_inc_eviction) || - !CU_add_test(pSuite, "hd_inflate_newname_noinc", - test_nghttp2_hd_inflate_newname_noinc) || - !CU_add_test(pSuite, "hd_inflate_newname_inc", - test_nghttp2_hd_inflate_newname_inc) || - !CU_add_test(pSuite, "hd_inflate_clearall_inc", - test_nghttp2_hd_inflate_clearall_inc) || - !CU_add_test(pSuite, "hd_inflate_zero_length_huffman", - test_nghttp2_hd_inflate_zero_length_huffman) || - !CU_add_test(pSuite, "hd_ringbuf_reserve", - test_nghttp2_hd_ringbuf_reserve) || - !CU_add_test(pSuite, "hd_change_table_size", - test_nghttp2_hd_change_table_size) || - !CU_add_test(pSuite, "hd_deflate_inflate", - test_nghttp2_hd_deflate_inflate) || - !CU_add_test(pSuite, "hd_no_index", test_nghttp2_hd_no_index) || - !CU_add_test(pSuite, "hd_deflate_bound", test_nghttp2_hd_deflate_bound) || - !CU_add_test(pSuite, "hd_public_api", test_nghttp2_hd_public_api) || - !CU_add_test(pSuite, "hd_decode_length", test_nghttp2_hd_decode_length) || - !CU_add_test(pSuite, "hd_huff_encode", test_nghttp2_hd_huff_encode) || - !CU_add_test(pSuite, "adjust_local_window_size", - test_nghttp2_adjust_local_window_size) || - !CU_add_test(pSuite, "check_header_name", - test_nghttp2_check_header_name) || - !CU_add_test(pSuite, "check_header_value", - test_nghttp2_check_header_value) || - !CU_add_test(pSuite, "bufs_add", test_nghttp2_bufs_add) || - !CU_add_test(pSuite, "bufs_add_stack_buffer_overflow_bug", - test_nghttp2_bufs_add_stack_buffer_overflow_bug) || - !CU_add_test(pSuite, "bufs_addb", test_nghttp2_bufs_addb) || - !CU_add_test(pSuite, "bufs_orb", test_nghttp2_bufs_orb) || - !CU_add_test(pSuite, "bufs_remove", test_nghttp2_bufs_remove) || - !CU_add_test(pSuite, "bufs_reset", test_nghttp2_bufs_reset) || - !CU_add_test(pSuite, "bufs_advance", test_nghttp2_bufs_advance) || - !CU_add_test(pSuite, "bufs_next_present", - test_nghttp2_bufs_next_present) || - !CU_add_test(pSuite, "bufs_realloc", test_nghttp2_bufs_realloc)) { - CU_cleanup_registry(); - return CU_get_error(); - } - - /* Run all tests using the CUnit Basic interface */ - CU_basic_set_mode(CU_BRM_VERBOSE); - CU_basic_run_tests(); - num_tests_failed = CU_get_number_of_tests_failed(); - CU_cleanup_registry(); - if (CU_get_error() == CUE_SUCCESS) { - return num_tests_failed; - } else { - printf("CUnit Error: %s\n", CU_get_error_msg()); - return CU_get_error(); - } -} diff --git a/tests/malloc_wrapper.c b/tests/malloc_wrapper.c deleted file mode 100644 index 6873bff..0000000 --- a/tests/malloc_wrapper.c +++ /dev/null @@ -1,75 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2012 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#include "malloc_wrapper.h" - -int nghttp2_failmalloc = 0; -int nghttp2_failstart = 0; -int nghttp2_countmalloc = 1; -int nghttp2_nmalloc = 0; - -#define CHECK_PREREQ \ - do { \ - if (nghttp2_failmalloc && nghttp2_nmalloc >= nghttp2_failstart) { \ - return NULL; \ - } \ - if (nghttp2_countmalloc) { \ - ++nghttp2_nmalloc; \ - } \ - } while (0) - -static void *my_malloc(size_t size, void *mud _U_) { - CHECK_PREREQ; - return malloc(size); -} - -static void my_free(void *ptr, void *mud _U_) { free(ptr); } - -static void *my_calloc(size_t nmemb, size_t size, void *mud _U_) { - CHECK_PREREQ; - return calloc(nmemb, size); -} - -static void *my_realloc(void *ptr, size_t size, void *mud _U_) { - CHECK_PREREQ; - return realloc(ptr, size); -} - -static nghttp2_mem mem = {NULL, my_malloc, my_free, my_calloc, my_realloc}; - -nghttp2_mem *nghttp2_mem_fm(void) { return &mem; } - -static int failmalloc_bk, countmalloc_bk; - -void nghttp2_failmalloc_pause(void) { - failmalloc_bk = nghttp2_failmalloc; - countmalloc_bk = nghttp2_countmalloc; - nghttp2_failmalloc = 0; - nghttp2_countmalloc = 0; -} - -void nghttp2_failmalloc_unpause(void) { - nghttp2_failmalloc = failmalloc_bk; - nghttp2_countmalloc = countmalloc_bk; -} diff --git a/tests/malloc_wrapper.h b/tests/malloc_wrapper.h deleted file mode 100644 index 5405d09..0000000 --- a/tests/malloc_wrapper.h +++ /dev/null @@ -1,61 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2012 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#ifndef MALLOC_WRAPPER_H -#define MALLOC_WRAPPER_H - -#include - -#include "nghttp2_mem.h" - -/* Global variables to control the behavior of malloc() */ - -/* If nonzero, malloc failure mode is on */ -extern int nghttp2_failmalloc; -/* If nghttp2_failstart <= nghttp2_nmalloc and nghttp2_failmalloc is - nonzero, malloc() fails. */ -extern int nghttp2_failstart; -/* If nonzero, nghttp2_nmalloc is incremented if malloc() succeeds. */ -extern int nghttp2_countmalloc; -/* The number of successful invocation of malloc(). This value is only - incremented if nghttp2_nmalloc is nonzero. */ -extern int nghttp2_nmalloc; - -/* Returns pointer to nghttp2_mem, which, when dereferenced, contains - specifically instrumented memory allocators for failmalloc - tests. */ -nghttp2_mem *nghttp2_mem_fm(void); - -/* Copies nghttp2_failmalloc and nghttp2_countmalloc to statically - allocated space and sets 0 to them. This will effectively make - malloc() work like normal malloc(). This is useful when you want to - disable malloc() failure mode temporarily. */ -void nghttp2_failmalloc_pause(void); - -/* Restores the values of nghttp2_failmalloc and nghttp2_countmalloc - with the values saved by the previous - nghttp2_failmalloc_pause(). */ -void nghttp2_failmalloc_unpause(void); - -#endif /* MALLOC_WRAPPER_H */ diff --git a/tests/nghttp2_buf_test.c b/tests/nghttp2_buf_test.c deleted file mode 100644 index 8860554..0000000 --- a/tests/nghttp2_buf_test.c +++ /dev/null @@ -1,342 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2014 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#include "nghttp2_buf_test.h" - -#include - -#include "nghttp2_buf.h" -#include "nghttp2_test_helper.h" - -void test_nghttp2_bufs_add(void) { - int rv; - nghttp2_bufs bufs; - uint8_t data[2048]; - nghttp2_mem *mem; - - mem = nghttp2_mem_default(); - - rv = nghttp2_bufs_init(&bufs, 1000, 3, mem); - CU_ASSERT(0 == rv); - - CU_ASSERT(bufs.cur->buf.pos == bufs.cur->buf.last); - - rv = nghttp2_bufs_add(&bufs, data, 493); - CU_ASSERT(0 == rv); - CU_ASSERT(493 == nghttp2_buf_len(&bufs.cur->buf)); - CU_ASSERT(493 == nghttp2_bufs_len(&bufs)); - CU_ASSERT(507 == nghttp2_bufs_cur_avail(&bufs)); - - rv = nghttp2_bufs_add(&bufs, data, 507); - CU_ASSERT(0 == rv); - CU_ASSERT(1000 == nghttp2_buf_len(&bufs.cur->buf)); - CU_ASSERT(1000 == nghttp2_bufs_len(&bufs)); - CU_ASSERT(bufs.cur == bufs.head); - - rv = nghttp2_bufs_add(&bufs, data, 1); - CU_ASSERT(0 == rv); - CU_ASSERT(1 == nghttp2_buf_len(&bufs.cur->buf)); - CU_ASSERT(1001 == nghttp2_bufs_len(&bufs)); - CU_ASSERT(bufs.cur == bufs.head->next); - - nghttp2_bufs_free(&bufs); -} - -/* Test for GH-232, stack-buffer-overflow */ -void test_nghttp2_bufs_add_stack_buffer_overflow_bug(void) { - int rv; - nghttp2_bufs bufs; - uint8_t data[1024]; - nghttp2_mem *mem; - - mem = nghttp2_mem_default(); - - rv = nghttp2_bufs_init(&bufs, 100, 200, mem); - CU_ASSERT(0 == rv); - - rv = nghttp2_bufs_add(&bufs, data, sizeof(data)); - - CU_ASSERT(0 == rv); - CU_ASSERT(sizeof(data) == nghttp2_bufs_len(&bufs)); - - nghttp2_bufs_free(&bufs); -} - -void test_nghttp2_bufs_addb(void) { - int rv; - nghttp2_bufs bufs; - ssize_t i; - nghttp2_mem *mem; - - mem = nghttp2_mem_default(); - - rv = nghttp2_bufs_init(&bufs, 1000, 3, mem); - CU_ASSERT(0 == rv); - - rv = nghttp2_bufs_addb(&bufs, 14); - CU_ASSERT(0 == rv); - CU_ASSERT(1 == nghttp2_buf_len(&bufs.cur->buf)); - CU_ASSERT(1 == nghttp2_bufs_len(&bufs)); - CU_ASSERT(14 == *bufs.cur->buf.pos); - - for (i = 0; i < 999; ++i) { - rv = nghttp2_bufs_addb(&bufs, 254); - - CU_ASSERT(0 == rv); - CU_ASSERT(i + 2 == nghttp2_buf_len(&bufs.cur->buf)); - CU_ASSERT(i + 2 == nghttp2_bufs_len(&bufs)); - CU_ASSERT(254 == *(bufs.cur->buf.last - 1)); - CU_ASSERT(bufs.cur == bufs.head); - } - - rv = nghttp2_bufs_addb(&bufs, 253); - CU_ASSERT(0 == rv); - CU_ASSERT(1 == nghttp2_buf_len(&bufs.cur->buf)); - CU_ASSERT(1001 == nghttp2_bufs_len(&bufs)); - CU_ASSERT(253 == *(bufs.cur->buf.last - 1)); - CU_ASSERT(bufs.cur == bufs.head->next); - - rv = nghttp2_bufs_addb_hold(&bufs, 15); - CU_ASSERT(0 == rv); - CU_ASSERT(1 == nghttp2_buf_len(&bufs.cur->buf)); - CU_ASSERT(1001 == nghttp2_bufs_len(&bufs)); - CU_ASSERT(15 == *(bufs.cur->buf.last)); - - /* test fast version */ - - nghttp2_bufs_fast_addb(&bufs, 240); - - CU_ASSERT(2 == nghttp2_buf_len(&bufs.cur->buf)); - CU_ASSERT(1002 == nghttp2_bufs_len(&bufs)); - CU_ASSERT(240 == *(bufs.cur->buf.last - 1)); - - nghttp2_bufs_fast_addb_hold(&bufs, 113); - - CU_ASSERT(2 == nghttp2_buf_len(&bufs.cur->buf)); - CU_ASSERT(1002 == nghttp2_bufs_len(&bufs)); - CU_ASSERT(113 == *(bufs.cur->buf.last)); - - /* addb_hold when last == end */ - bufs.cur->buf.last = bufs.cur->buf.end; - - rv = nghttp2_bufs_addb_hold(&bufs, 19); - CU_ASSERT(0 == rv); - CU_ASSERT(0 == nghttp2_buf_len(&bufs.cur->buf)); - CU_ASSERT(2000 == nghttp2_bufs_len(&bufs)); - CU_ASSERT(19 == *(bufs.cur->buf.last)); - - nghttp2_bufs_free(&bufs); -} - -void test_nghttp2_bufs_orb(void) { - int rv; - nghttp2_bufs bufs; - nghttp2_mem *mem; - - mem = nghttp2_mem_default(); - - rv = nghttp2_bufs_init(&bufs, 1000, 3, mem); - CU_ASSERT(0 == rv); - - *(bufs.cur->buf.last) = 0; - - rv = nghttp2_bufs_orb_hold(&bufs, 15); - CU_ASSERT(0 == rv); - CU_ASSERT(0 == nghttp2_buf_len(&bufs.cur->buf)); - CU_ASSERT(0 == nghttp2_bufs_len(&bufs)); - CU_ASSERT(15 == *(bufs.cur->buf.last)); - - rv = nghttp2_bufs_orb(&bufs, 240); - CU_ASSERT(0 == rv); - CU_ASSERT(1 == nghttp2_buf_len(&bufs.cur->buf)); - CU_ASSERT(1 == nghttp2_bufs_len(&bufs)); - CU_ASSERT(255 == *(bufs.cur->buf.last - 1)); - - *(bufs.cur->buf.last) = 0; - nghttp2_bufs_fast_orb_hold(&bufs, 240); - CU_ASSERT(240 == *(bufs.cur->buf.last)); - - nghttp2_bufs_fast_orb(&bufs, 15); - CU_ASSERT(255 == *(bufs.cur->buf.last - 1)); - - nghttp2_bufs_free(&bufs); -} - -void test_nghttp2_bufs_remove(void) { - int rv; - nghttp2_bufs bufs; - nghttp2_buf_chain *chain; - int i; - uint8_t *out; - ssize_t outlen; - nghttp2_mem *mem; - - mem = nghttp2_mem_default(); - - rv = nghttp2_bufs_init(&bufs, 1000, 3, mem); - CU_ASSERT(0 == rv); - - nghttp2_buf_shift_right(&bufs.cur->buf, 10); - - rv = nghttp2_bufs_add(&bufs, "hello ", 6); - CU_ASSERT(0 == rv); - - for (i = 0; i < 2; ++i) { - chain = bufs.cur; - - rv = nghttp2_bufs_advance(&bufs); - CU_ASSERT(0 == rv); - - CU_ASSERT(chain->next == bufs.cur); - } - - rv = nghttp2_bufs_add(&bufs, "world", 5); - CU_ASSERT(0 == rv); - - outlen = nghttp2_bufs_remove(&bufs, &out); - CU_ASSERT(11 == outlen); - - CU_ASSERT(0 == memcmp("hello world", out, outlen)); - CU_ASSERT(11 == nghttp2_bufs_len(&bufs)); - - mem->free(out, NULL); - nghttp2_bufs_free(&bufs); -} - -void test_nghttp2_bufs_reset(void) { - int rv; - nghttp2_bufs bufs; - nghttp2_buf_chain *ci; - ssize_t offset = 9; - nghttp2_mem *mem; - - mem = nghttp2_mem_default(); - - rv = nghttp2_bufs_init3(&bufs, 250, 3, 1, offset, mem); - CU_ASSERT(0 == rv); - - rv = nghttp2_bufs_add(&bufs, "foo", 3); - CU_ASSERT(0 == rv); - - rv = nghttp2_bufs_advance(&bufs); - CU_ASSERT(0 == rv); - - rv = nghttp2_bufs_add(&bufs, "bar", 3); - CU_ASSERT(0 == rv); - - CU_ASSERT(6 == nghttp2_bufs_len(&bufs)); - - nghttp2_bufs_reset(&bufs); - - CU_ASSERT(0 == nghttp2_bufs_len(&bufs)); - CU_ASSERT(bufs.cur == bufs.head); - - for (ci = bufs.head; ci; ci = ci->next) { - CU_ASSERT(offset == ci->buf.pos - ci->buf.begin); - CU_ASSERT(ci->buf.pos == ci->buf.last); - } - - CU_ASSERT(bufs.head->next == NULL); - - nghttp2_bufs_free(&bufs); -} - -void test_nghttp2_bufs_advance(void) { - int rv; - nghttp2_bufs bufs; - int i; - nghttp2_mem *mem; - - mem = nghttp2_mem_default(); - - rv = nghttp2_bufs_init(&bufs, 250, 3, mem); - CU_ASSERT(0 == rv); - - for (i = 0; i < 2; ++i) { - rv = nghttp2_bufs_advance(&bufs); - CU_ASSERT(0 == rv); - } - - rv = nghttp2_bufs_advance(&bufs); - CU_ASSERT(NGHTTP2_ERR_BUFFER_ERROR == rv); - - nghttp2_bufs_free(&bufs); -} - -void test_nghttp2_bufs_next_present(void) { - int rv; - nghttp2_bufs bufs; - nghttp2_mem *mem; - - mem = nghttp2_mem_default(); - - rv = nghttp2_bufs_init(&bufs, 250, 3, mem); - CU_ASSERT(0 == rv); - - CU_ASSERT(0 == nghttp2_bufs_next_present(&bufs)); - - rv = nghttp2_bufs_advance(&bufs); - CU_ASSERT(0 == rv); - - nghttp2_bufs_rewind(&bufs); - - CU_ASSERT(0 == nghttp2_bufs_next_present(&bufs)); - - bufs.cur = bufs.head->next; - - rv = nghttp2_bufs_addb(&bufs, 1); - CU_ASSERT(0 == rv); - - nghttp2_bufs_rewind(&bufs); - - CU_ASSERT(0 != nghttp2_bufs_next_present(&bufs)); - - nghttp2_bufs_free(&bufs); -} - -void test_nghttp2_bufs_realloc(void) { - int rv; - nghttp2_bufs bufs; - nghttp2_mem *mem; - - mem = nghttp2_mem_default(); - - rv = nghttp2_bufs_init3(&bufs, 266, 3, 1, 10, mem); - CU_ASSERT(0 == rv); - - /* Create new buffer to see that these buffers are deallocated on - realloc */ - rv = nghttp2_bufs_advance(&bufs); - CU_ASSERT(0 == rv); - - rv = nghttp2_bufs_realloc(&bufs, 522); - CU_ASSERT(0 == rv); - - CU_ASSERT(512 == nghttp2_bufs_cur_avail(&bufs)); - - rv = nghttp2_bufs_realloc(&bufs, 9); - CU_ASSERT(NGHTTP2_ERR_INVALID_ARGUMENT == rv); - - nghttp2_bufs_free(&bufs); -} diff --git a/tests/nghttp2_buf_test.h b/tests/nghttp2_buf_test.h deleted file mode 100644 index 0b94a3f..0000000 --- a/tests/nghttp2_buf_test.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2014 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#ifndef NGHTTP2_BUF_TEST_H -#define NGHTTP2_BUF_TEST_H - -void test_nghttp2_bufs_add(void); -void test_nghttp2_bufs_add_stack_buffer_overflow_bug(void); -void test_nghttp2_bufs_addb(void); -void test_nghttp2_bufs_orb(void); -void test_nghttp2_bufs_remove(void); -void test_nghttp2_bufs_reset(void); -void test_nghttp2_bufs_advance(void); -void test_nghttp2_bufs_next_present(void); -void test_nghttp2_bufs_realloc(void); - -#endif /* NGHTTP2_BUF_TEST_H */ diff --git a/tests/nghttp2_frame_test.c b/tests/nghttp2_frame_test.c deleted file mode 100644 index 0e0d1be..0000000 --- a/tests/nghttp2_frame_test.c +++ /dev/null @@ -1,561 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2012 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#include "nghttp2_frame_test.h" - -#include -#include - -#include - -#include "nghttp2_frame.h" -#include "nghttp2_helper.h" -#include "nghttp2_test_helper.h" -#include "nghttp2_priority_spec.h" - -static nghttp2_nv make_nv(const char *name, const char *value) { - nghttp2_nv nv; - nv.name = (uint8_t *)name; - nv.value = (uint8_t *)value; - nv.namelen = strlen(name); - nv.valuelen = strlen(value); - nv.flags = NGHTTP2_NV_FLAG_NONE; - - return nv; -} - -#define HEADERS_LENGTH 7 - -static nghttp2_nv *headers(nghttp2_mem *mem) { - nghttp2_nv *nva = mem->malloc(sizeof(nghttp2_nv) * HEADERS_LENGTH, NULL); - nva[0] = make_nv("method", "GET"); - nva[1] = make_nv("scheme", "https"); - nva[2] = make_nv("url", "/"); - nva[3] = make_nv("x-head", "foo"); - nva[4] = make_nv("x-head", "bar"); - nva[5] = make_nv("version", "HTTP/1.1"); - nva[6] = make_nv("x-empty", ""); - return nva; -} - -static void check_frame_header(size_t length, uint8_t type, uint8_t flags, - int32_t stream_id, nghttp2_frame_hd *hd) { - CU_ASSERT(length == hd->length); - CU_ASSERT(type == hd->type); - CU_ASSERT(flags == hd->flags); - CU_ASSERT(stream_id == hd->stream_id); - CU_ASSERT(0 == hd->reserved); -} - -void test_nghttp2_frame_pack_headers() { - nghttp2_hd_deflater deflater; - nghttp2_hd_inflater inflater; - nghttp2_headers frame, oframe; - nghttp2_bufs bufs; - nghttp2_nv *nva; - nghttp2_priority_spec pri_spec; - ssize_t nvlen; - nva_out out; - ssize_t hdblocklen; - int rv; - nghttp2_mem *mem; - - mem = nghttp2_mem_default(); - frame_pack_bufs_init(&bufs); - - nva_out_init(&out); - nghttp2_hd_deflate_init(&deflater, mem); - nghttp2_hd_inflate_init(&inflater, mem); - - nva = headers(mem); - nvlen = HEADERS_LENGTH; - - nghttp2_priority_spec_default_init(&pri_spec); - - nghttp2_frame_headers_init( - &frame, NGHTTP2_FLAG_END_STREAM | NGHTTP2_FLAG_END_HEADERS, 1000000007, - NGHTTP2_HCAT_REQUEST, &pri_spec, nva, nvlen); - rv = nghttp2_frame_pack_headers(&bufs, &frame, &deflater); - - nghttp2_bufs_rewind(&bufs); - - CU_ASSERT(0 == rv); - CU_ASSERT(nghttp2_bufs_len(&bufs) > 0); - CU_ASSERT(0 == unpack_framebuf((nghttp2_frame *)&oframe, &bufs)); - - check_frame_header(nghttp2_bufs_len(&bufs) - NGHTTP2_FRAME_HDLEN, - NGHTTP2_HEADERS, - NGHTTP2_FLAG_END_STREAM | NGHTTP2_FLAG_END_HEADERS, - 1000000007, &oframe.hd); - /* We did not include PRIORITY flag */ - CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT == oframe.pri_spec.weight); - - hdblocklen = nghttp2_bufs_len(&bufs) - NGHTTP2_FRAME_HDLEN; - CU_ASSERT(hdblocklen == - inflate_hd(&inflater, &out, &bufs, NGHTTP2_FRAME_HDLEN, mem)); - - CU_ASSERT(7 == out.nvlen); - CU_ASSERT(nvnameeq("method", &out.nva[0])); - CU_ASSERT(nvvalueeq("GET", &out.nva[0])); - - nghttp2_frame_headers_free(&oframe, mem); - nva_out_reset(&out, mem); - nghttp2_bufs_reset(&bufs); - - memset(&oframe, 0, sizeof(oframe)); - /* Next, include NGHTTP2_FLAG_PRIORITY */ - nghttp2_priority_spec_init(&frame.pri_spec, 1000000009, 12, 1); - frame.hd.flags |= NGHTTP2_FLAG_PRIORITY; - - rv = nghttp2_frame_pack_headers(&bufs, &frame, &deflater); - - CU_ASSERT(0 == rv); - CU_ASSERT(nghttp2_bufs_len(&bufs) > 0); - CU_ASSERT(0 == unpack_framebuf((nghttp2_frame *)&oframe, &bufs)); - - check_frame_header(nghttp2_bufs_len(&bufs) - NGHTTP2_FRAME_HDLEN, - NGHTTP2_HEADERS, - NGHTTP2_FLAG_END_STREAM | NGHTTP2_FLAG_END_HEADERS | - NGHTTP2_FLAG_PRIORITY, - 1000000007, &oframe.hd); - - CU_ASSERT(1000000009 == oframe.pri_spec.stream_id); - CU_ASSERT(12 == oframe.pri_spec.weight); - CU_ASSERT(1 == oframe.pri_spec.exclusive); - - hdblocklen = nghttp2_bufs_len(&bufs) - NGHTTP2_FRAME_HDLEN - - nghttp2_frame_priority_len(oframe.hd.flags); - CU_ASSERT(hdblocklen == - inflate_hd(&inflater, &out, &bufs, - NGHTTP2_FRAME_HDLEN + - nghttp2_frame_priority_len(oframe.hd.flags), - mem)); - - nghttp2_nv_array_sort(out.nva, out.nvlen); - CU_ASSERT(nvnameeq("method", &out.nva[0])); - - nghttp2_frame_headers_free(&oframe, mem); - nva_out_reset(&out, mem); - nghttp2_bufs_reset(&bufs); - - nghttp2_bufs_free(&bufs); - nghttp2_frame_headers_free(&frame, mem); - nghttp2_hd_inflate_free(&inflater); - nghttp2_hd_deflate_free(&deflater); -} - -void test_nghttp2_frame_pack_headers_frame_too_large(void) { - nghttp2_hd_deflater deflater; - nghttp2_headers frame; - nghttp2_bufs bufs; - nghttp2_nv *nva; - size_t big_vallen = NGHTTP2_HD_MAX_NV; - nghttp2_nv big_hds[16]; - size_t big_hdslen = ARRLEN(big_hds); - size_t i; - int rv; - nghttp2_mem *mem; - - mem = nghttp2_mem_default(); - frame_pack_bufs_init(&bufs); - - for (i = 0; i < big_hdslen; ++i) { - big_hds[i].name = (uint8_t *)"header"; - big_hds[i].value = mem->malloc(big_vallen + 1, NULL); - memset(big_hds[i].value, '0' + (int)i, big_vallen); - big_hds[i].value[big_vallen] = '\0'; - big_hds[i].namelen = strlen((char *)big_hds[i].name); - big_hds[i].valuelen = big_vallen; - big_hds[i].flags = NGHTTP2_NV_FLAG_NONE; - } - - nghttp2_nv_array_copy(&nva, big_hds, big_hdslen, mem); - nghttp2_hd_deflate_init(&deflater, mem); - nghttp2_frame_headers_init( - &frame, NGHTTP2_FLAG_END_STREAM | NGHTTP2_FLAG_END_HEADERS, 1000000007, - NGHTTP2_HCAT_REQUEST, NULL, nva, big_hdslen); - rv = nghttp2_frame_pack_headers(&bufs, &frame, &deflater); - CU_ASSERT(NGHTTP2_ERR_HEADER_COMP == rv); - - nghttp2_frame_headers_free(&frame, mem); - nghttp2_bufs_free(&bufs); - for (i = 0; i < big_hdslen; ++i) { - mem->free(big_hds[i].value, NULL); - } - nghttp2_hd_deflate_free(&deflater); -} - -void test_nghttp2_frame_pack_priority(void) { - nghttp2_priority frame, oframe; - nghttp2_bufs bufs; - nghttp2_priority_spec pri_spec; - int rv; - - frame_pack_bufs_init(&bufs); - - /* First, pack priority with priority group and weight */ - nghttp2_priority_spec_init(&pri_spec, 1000000009, 12, 1); - - nghttp2_frame_priority_init(&frame, 1000000007, &pri_spec); - rv = nghttp2_frame_pack_priority(&bufs, &frame); - - CU_ASSERT(0 == rv); - CU_ASSERT(NGHTTP2_FRAME_HDLEN + 5 == nghttp2_bufs_len(&bufs)); - CU_ASSERT(0 == unpack_framebuf((nghttp2_frame *)&oframe, &bufs)); - check_frame_header(5, NGHTTP2_PRIORITY, NGHTTP2_FLAG_NONE, 1000000007, - &oframe.hd); - - CU_ASSERT(1000000009 == oframe.pri_spec.stream_id); - CU_ASSERT(12 == oframe.pri_spec.weight); - CU_ASSERT(1 == oframe.pri_spec.exclusive); - - nghttp2_frame_priority_free(&oframe); - nghttp2_bufs_reset(&bufs); - - nghttp2_bufs_free(&bufs); - nghttp2_frame_priority_free(&frame); -} - -void test_nghttp2_frame_pack_rst_stream(void) { - nghttp2_rst_stream frame, oframe; - nghttp2_bufs bufs; - int rv; - - frame_pack_bufs_init(&bufs); - - nghttp2_frame_rst_stream_init(&frame, 1000000007, NGHTTP2_PROTOCOL_ERROR); - rv = nghttp2_frame_pack_rst_stream(&bufs, &frame); - - CU_ASSERT(0 == rv); - CU_ASSERT(NGHTTP2_FRAME_HDLEN + 4 == nghttp2_bufs_len(&bufs)); - CU_ASSERT(0 == unpack_framebuf((nghttp2_frame *)&oframe, &bufs)); - check_frame_header(4, NGHTTP2_RST_STREAM, NGHTTP2_FLAG_NONE, 1000000007, - &oframe.hd); - CU_ASSERT(NGHTTP2_PROTOCOL_ERROR == oframe.error_code); - - nghttp2_frame_rst_stream_free(&oframe); - nghttp2_bufs_reset(&bufs); - - /* Unknown error code is passed to callback as is */ - frame.error_code = 1000000009; - rv = nghttp2_frame_pack_rst_stream(&bufs, &frame); - - CU_ASSERT(0 == rv); - CU_ASSERT(0 == unpack_framebuf((nghttp2_frame *)&oframe, &bufs)); - - check_frame_header(4, NGHTTP2_RST_STREAM, NGHTTP2_FLAG_NONE, 1000000007, - &oframe.hd); - - CU_ASSERT(1000000009 == oframe.error_code); - - nghttp2_frame_rst_stream_free(&oframe); - - nghttp2_frame_rst_stream_free(&frame); - - nghttp2_bufs_free(&bufs); -} - -void test_nghttp2_frame_pack_settings() { - nghttp2_settings frame, oframe; - nghttp2_bufs bufs; - int i; - int rv; - nghttp2_settings_entry iv[] = {{NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 256}, - {NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE, 16384}, - {NGHTTP2_SETTINGS_HEADER_TABLE_SIZE, 4096}}; - nghttp2_mem *mem; - - mem = nghttp2_mem_default(); - frame_pack_bufs_init(&bufs); - - nghttp2_frame_settings_init(&frame, NGHTTP2_FLAG_NONE, - nghttp2_frame_iv_copy(iv, 3, mem), 3); - rv = nghttp2_frame_pack_settings(&bufs, &frame); - - CU_ASSERT(0 == rv); - CU_ASSERT(NGHTTP2_FRAME_HDLEN + 3 * NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH == - nghttp2_bufs_len(&bufs)); - - CU_ASSERT(0 == unpack_framebuf((nghttp2_frame *)&oframe, &bufs)); - check_frame_header(3 * NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH, NGHTTP2_SETTINGS, - NGHTTP2_FLAG_NONE, 0, &oframe.hd); - CU_ASSERT(3 == oframe.niv); - for (i = 0; i < 3; ++i) { - CU_ASSERT(iv[i].settings_id == oframe.iv[i].settings_id); - CU_ASSERT(iv[i].value == oframe.iv[i].value); - } - - nghttp2_bufs_free(&bufs); - nghttp2_frame_settings_free(&frame, mem); - nghttp2_frame_settings_free(&oframe, mem); -} - -void test_nghttp2_frame_pack_push_promise() { - nghttp2_hd_deflater deflater; - nghttp2_hd_inflater inflater; - nghttp2_push_promise frame, oframe; - nghttp2_bufs bufs; - nghttp2_nv *nva; - ssize_t nvlen; - nva_out out; - ssize_t hdblocklen; - int rv; - nghttp2_mem *mem; - - mem = nghttp2_mem_default(); - frame_pack_bufs_init(&bufs); - - nva_out_init(&out); - nghttp2_hd_deflate_init(&deflater, mem); - nghttp2_hd_inflate_init(&inflater, mem); - - nva = headers(mem); - nvlen = HEADERS_LENGTH; - nghttp2_frame_push_promise_init(&frame, NGHTTP2_FLAG_END_HEADERS, 1000000007, - (1U << 31) - 1, nva, nvlen); - rv = nghttp2_frame_pack_push_promise(&bufs, &frame, &deflater); - - CU_ASSERT(0 == rv); - CU_ASSERT(nghttp2_bufs_len(&bufs) > 0); - CU_ASSERT(0 == unpack_framebuf((nghttp2_frame *)&oframe, &bufs)); - - check_frame_header(nghttp2_bufs_len(&bufs) - NGHTTP2_FRAME_HDLEN, - NGHTTP2_PUSH_PROMISE, NGHTTP2_FLAG_END_HEADERS, 1000000007, - &oframe.hd); - CU_ASSERT((1U << 31) - 1 == oframe.promised_stream_id); - - hdblocklen = nghttp2_bufs_len(&bufs) - NGHTTP2_FRAME_HDLEN - 4; - CU_ASSERT(hdblocklen == - inflate_hd(&inflater, &out, &bufs, NGHTTP2_FRAME_HDLEN + 4, mem)); - - CU_ASSERT(7 == out.nvlen); - CU_ASSERT(nvnameeq("method", &out.nva[0])); - CU_ASSERT(nvvalueeq("GET", &out.nva[0])); - - nva_out_reset(&out, mem); - nghttp2_bufs_free(&bufs); - nghttp2_frame_push_promise_free(&oframe, mem); - nghttp2_frame_push_promise_free(&frame, mem); - nghttp2_hd_inflate_free(&inflater); - nghttp2_hd_deflate_free(&deflater); -} - -void test_nghttp2_frame_pack_ping(void) { - nghttp2_ping frame, oframe; - nghttp2_bufs bufs; - const uint8_t opaque_data[] = "01234567"; - int rv; - - frame_pack_bufs_init(&bufs); - - nghttp2_frame_ping_init(&frame, NGHTTP2_FLAG_ACK, opaque_data); - rv = nghttp2_frame_pack_ping(&bufs, &frame); - - CU_ASSERT(0 == rv); - CU_ASSERT(NGHTTP2_FRAME_HDLEN + 8 == nghttp2_bufs_len(&bufs)); - CU_ASSERT(0 == unpack_framebuf((nghttp2_frame *)&oframe, &bufs)); - check_frame_header(8, NGHTTP2_PING, NGHTTP2_FLAG_ACK, 0, &oframe.hd); - CU_ASSERT(memcmp(opaque_data, oframe.opaque_data, sizeof(opaque_data) - 1) == - 0); - - nghttp2_bufs_free(&bufs); - nghttp2_frame_ping_free(&oframe); - nghttp2_frame_ping_free(&frame); -} - -void test_nghttp2_frame_pack_goaway() { - nghttp2_goaway frame, oframe; - nghttp2_bufs bufs; - size_t opaque_data_len = 16; - uint8_t *opaque_data; - int rv; - nghttp2_mem *mem; - - mem = nghttp2_mem_default(); - frame_pack_bufs_init(&bufs); - - opaque_data = mem->malloc(opaque_data_len, NULL); - memcpy(opaque_data, "0123456789abcdef", opaque_data_len); - nghttp2_frame_goaway_init(&frame, 1000000007, NGHTTP2_PROTOCOL_ERROR, - opaque_data, opaque_data_len); - rv = nghttp2_frame_pack_goaway(&bufs, &frame); - - CU_ASSERT(0 == rv); - CU_ASSERT((ssize_t)(NGHTTP2_FRAME_HDLEN + 8 + opaque_data_len) == - nghttp2_bufs_len(&bufs)); - CU_ASSERT(0 == unpack_framebuf((nghttp2_frame *)&oframe, &bufs)); - check_frame_header(24, NGHTTP2_GOAWAY, NGHTTP2_FLAG_NONE, 0, &oframe.hd); - CU_ASSERT(1000000007 == oframe.last_stream_id); - CU_ASSERT(NGHTTP2_PROTOCOL_ERROR == oframe.error_code); - - CU_ASSERT(opaque_data_len == oframe.opaque_data_len); - CU_ASSERT(memcmp(opaque_data, oframe.opaque_data, opaque_data_len) == 0); - - nghttp2_frame_goaway_free(&oframe, mem); - nghttp2_bufs_reset(&bufs); - - /* Unknown error code is passed to callback as is */ - frame.error_code = 1000000009; - - rv = nghttp2_frame_pack_goaway(&bufs, &frame); - - CU_ASSERT(0 == rv); - CU_ASSERT(0 == unpack_framebuf((nghttp2_frame *)&oframe, &bufs)); - check_frame_header(24, NGHTTP2_GOAWAY, NGHTTP2_FLAG_NONE, 0, &oframe.hd); - CU_ASSERT(1000000009 == oframe.error_code); - - nghttp2_frame_goaway_free(&oframe, mem); - - nghttp2_frame_goaway_free(&frame, mem); - - nghttp2_bufs_free(&bufs); -} - -void test_nghttp2_frame_pack_window_update(void) { - nghttp2_window_update frame, oframe; - nghttp2_bufs bufs; - int rv; - - frame_pack_bufs_init(&bufs); - - nghttp2_frame_window_update_init(&frame, NGHTTP2_FLAG_NONE, 1000000007, 4096); - rv = nghttp2_frame_pack_window_update(&bufs, &frame); - - CU_ASSERT(0 == rv); - CU_ASSERT(NGHTTP2_FRAME_HDLEN + 4 == nghttp2_bufs_len(&bufs)); - CU_ASSERT(0 == unpack_framebuf((nghttp2_frame *)&oframe, &bufs)); - check_frame_header(4, NGHTTP2_WINDOW_UPDATE, NGHTTP2_FLAG_NONE, 1000000007, - &oframe.hd); - CU_ASSERT(4096 == oframe.window_size_increment); - - nghttp2_bufs_free(&bufs); - nghttp2_frame_window_update_free(&oframe); - nghttp2_frame_window_update_free(&frame); -} - -void test_nghttp2_nv_array_copy(void) { - nghttp2_nv *nva; - ssize_t rv; - nghttp2_nv emptynv[] = {MAKE_NV("", ""), MAKE_NV("", "")}; - nghttp2_nv nv[] = {MAKE_NV("alpha", "bravo"), MAKE_NV("charlie", "delta")}; - nghttp2_nv bignv; - nghttp2_mem *mem; - - mem = nghttp2_mem_default(); - - bignv.name = (uint8_t *)"echo"; - bignv.namelen = strlen("echo"); - bignv.valuelen = (1 << 14) - 1; - bignv.value = mem->malloc(bignv.valuelen, NULL); - memset(bignv.value, '0', bignv.valuelen); - - rv = nghttp2_nv_array_copy(&nva, NULL, 0, mem); - CU_ASSERT(0 == rv); - CU_ASSERT(NULL == nva); - - rv = nghttp2_nv_array_copy(&nva, emptynv, ARRLEN(emptynv), mem); - CU_ASSERT(0 == rv); - CU_ASSERT(nva[0].namelen == 0); - CU_ASSERT(nva[0].valuelen == 0); - CU_ASSERT(nva[1].namelen == 0); - CU_ASSERT(nva[1].valuelen == 0); - - nghttp2_nv_array_del(nva, mem); - - rv = nghttp2_nv_array_copy(&nva, nv, ARRLEN(nv), mem); - CU_ASSERT(0 == rv); - CU_ASSERT(nva[0].namelen == 5); - CU_ASSERT(0 == memcmp("alpha", nva[0].name, 5)); - CU_ASSERT(nva[0].valuelen = 5); - CU_ASSERT(0 == memcmp("bravo", nva[0].value, 5)); - CU_ASSERT(nva[1].namelen == 7); - CU_ASSERT(0 == memcmp("charlie", nva[1].name, 7)); - CU_ASSERT(nva[1].valuelen == 5); - CU_ASSERT(0 == memcmp("delta", nva[1].value, 5)); - - nghttp2_nv_array_del(nva, mem); - - /* Large header field is acceptable */ - rv = nghttp2_nv_array_copy(&nva, &bignv, 1, mem); - CU_ASSERT(0 == rv); - - nghttp2_nv_array_del(nva, mem); - - mem->free(bignv.value, NULL); -} - -void test_nghttp2_iv_check(void) { - nghttp2_settings_entry iv[5]; - - iv[0].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS; - iv[0].value = 100; - iv[1].settings_id = NGHTTP2_SETTINGS_HEADER_TABLE_SIZE; - iv[1].value = 1024; - - CU_ASSERT(nghttp2_iv_check(iv, 2)); - - iv[1].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE; - iv[1].value = NGHTTP2_MAX_WINDOW_SIZE; - CU_ASSERT(nghttp2_iv_check(iv, 2)); - - /* Too large window size */ - iv[1].value = (uint32_t)NGHTTP2_MAX_WINDOW_SIZE + 1; - CU_ASSERT(0 == nghttp2_iv_check(iv, 2)); - - /* ENABLE_PUSH only allows 0 or 1 */ - iv[1].settings_id = NGHTTP2_SETTINGS_ENABLE_PUSH; - iv[1].value = 0; - CU_ASSERT(nghttp2_iv_check(iv, 2)); - iv[1].value = 1; - CU_ASSERT(nghttp2_iv_check(iv, 2)); - iv[1].value = 3; - CU_ASSERT(!nghttp2_iv_check(iv, 2)); - - /* Undefined SETTINGS ID is allowed */ - iv[1].settings_id = 1000000009; - iv[1].value = 0; - CU_ASSERT(nghttp2_iv_check(iv, 2)); - - /* Too large SETTINGS_HEADER_TABLE_SIZE */ - iv[1].settings_id = NGHTTP2_SETTINGS_HEADER_TABLE_SIZE; - iv[1].value = UINT32_MAX; - CU_ASSERT(!nghttp2_iv_check(iv, 2)); - - /* Too small SETTINGS_MAX_FRAME_SIZE */ - iv[0].settings_id = NGHTTP2_SETTINGS_MAX_FRAME_SIZE; - iv[0].value = NGHTTP2_MAX_FRAME_SIZE_MIN - 1; - CU_ASSERT(!nghttp2_iv_check(iv, 1)); - - /* Too large SETTINGS_MAX_FRAME_SIZE */ - iv[0].settings_id = NGHTTP2_SETTINGS_MAX_FRAME_SIZE; - iv[0].value = NGHTTP2_MAX_FRAME_SIZE_MAX + 1; - CU_ASSERT(!nghttp2_iv_check(iv, 1)); - - /* Max and min SETTINGS_MAX_FRAME_SIZE */ - iv[0].settings_id = NGHTTP2_SETTINGS_MAX_FRAME_SIZE; - iv[0].value = NGHTTP2_MAX_FRAME_SIZE_MIN; - iv[1].settings_id = NGHTTP2_SETTINGS_MAX_FRAME_SIZE; - iv[1].value = NGHTTP2_MAX_FRAME_SIZE_MAX; - CU_ASSERT(nghttp2_iv_check(iv, 2)); -} diff --git a/tests/nghttp2_frame_test.h b/tests/nghttp2_frame_test.h deleted file mode 100644 index a0ce37b..0000000 --- a/tests/nghttp2_frame_test.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2012 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#ifndef NGHTTP2_FRAME_TEST_H -#define NGHTTP2_FRAME_TEST_H - -void test_nghttp2_frame_pack_headers(void); -void test_nghttp2_frame_pack_headers_frame_too_large(void); -void test_nghttp2_frame_pack_priority(void); -void test_nghttp2_frame_pack_rst_stream(void); -void test_nghttp2_frame_pack_settings(void); -void test_nghttp2_frame_pack_push_promise(void); -void test_nghttp2_frame_pack_ping(void); -void test_nghttp2_frame_pack_goaway(void); -void test_nghttp2_frame_pack_window_update(void); -void test_nghttp2_nv_array_copy(void); -void test_nghttp2_iv_check(void); - -#endif /* NGHTTP2_FRAME_TEST_H */ diff --git a/tests/nghttp2_hd_test.c b/tests/nghttp2_hd_test.c deleted file mode 100644 index ecd7bc1..0000000 --- a/tests/nghttp2_hd_test.c +++ /dev/null @@ -1,1257 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2013 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#include "nghttp2_hd_test.h" - -#include -#include - -#include - -#include "nghttp2_hd.h" -#include "nghttp2_frame.h" -#include "nghttp2_test_helper.h" - -#define GET_TABLE_ENT(context, index) nghttp2_hd_table_get(context, index) - -void test_nghttp2_hd_deflate(void) { - nghttp2_hd_deflater deflater; - nghttp2_hd_inflater inflater; - nghttp2_nv nva1[] = {MAKE_NV(":path", "/my-example/index.html"), - MAKE_NV(":scheme", "https"), MAKE_NV("hello", "world")}; - nghttp2_nv nva2[] = {MAKE_NV(":path", "/script.js"), - MAKE_NV(":scheme", "https")}; - nghttp2_nv nva3[] = {MAKE_NV("cookie", "k1=v1"), MAKE_NV("cookie", "k2=v2"), - MAKE_NV("via", "proxy")}; - nghttp2_nv nva4[] = {MAKE_NV(":path", "/style.css"), - MAKE_NV("cookie", "k1=v1"), MAKE_NV("cookie", "k1=v1")}; - nghttp2_nv nva5[] = {MAKE_NV(":path", "/style.css"), - MAKE_NV("x-nghttp2", "")}; - nghttp2_bufs bufs; - ssize_t blocklen; - nva_out out; - int rv; - nghttp2_mem *mem; - - mem = nghttp2_mem_default(); - frame_pack_bufs_init(&bufs); - - nva_out_init(&out); - CU_ASSERT(0 == nghttp2_hd_deflate_init(&deflater, mem)); - CU_ASSERT(0 == nghttp2_hd_inflate_init(&inflater, mem)); - - rv = nghttp2_hd_deflate_hd_bufs(&deflater, &bufs, nva1, ARRLEN(nva1)); - blocklen = nghttp2_bufs_len(&bufs); - - CU_ASSERT(0 == rv); - CU_ASSERT(blocklen > 0); - CU_ASSERT(blocklen == inflate_hd(&inflater, &out, &bufs, 0, mem)); - - CU_ASSERT(3 == out.nvlen); - assert_nv_equal(nva1, out.nva, 3, mem); - - nva_out_reset(&out, mem); - nghttp2_bufs_reset(&bufs); - - /* Second headers */ - rv = nghttp2_hd_deflate_hd_bufs(&deflater, &bufs, nva2, ARRLEN(nva2)); - blocklen = nghttp2_bufs_len(&bufs); - - CU_ASSERT(0 == rv); - CU_ASSERT(blocklen > 0); - CU_ASSERT(blocklen == inflate_hd(&inflater, &out, &bufs, 0, mem)); - - CU_ASSERT(2 == out.nvlen); - assert_nv_equal(nva2, out.nva, 2, mem); - - nva_out_reset(&out, mem); - nghttp2_bufs_reset(&bufs); - - /* Third headers, including same header field name, but value is not - the same. */ - rv = nghttp2_hd_deflate_hd_bufs(&deflater, &bufs, nva3, ARRLEN(nva3)); - blocklen = nghttp2_bufs_len(&bufs); - - CU_ASSERT(0 == rv); - CU_ASSERT(blocklen > 0); - CU_ASSERT(blocklen == inflate_hd(&inflater, &out, &bufs, 0, mem)); - - CU_ASSERT(3 == out.nvlen); - assert_nv_equal(nva3, out.nva, 3, mem); - - nva_out_reset(&out, mem); - nghttp2_bufs_reset(&bufs); - - /* Fourth headers, including duplicate header fields. */ - rv = nghttp2_hd_deflate_hd_bufs(&deflater, &bufs, nva4, ARRLEN(nva4)); - blocklen = nghttp2_bufs_len(&bufs); - - CU_ASSERT(0 == rv); - CU_ASSERT(blocklen > 0); - CU_ASSERT(blocklen == inflate_hd(&inflater, &out, &bufs, 0, mem)); - - CU_ASSERT(3 == out.nvlen); - assert_nv_equal(nva4, out.nva, 3, mem); - - nva_out_reset(&out, mem); - nghttp2_bufs_reset(&bufs); - - /* Fifth headers includes empty value */ - rv = nghttp2_hd_deflate_hd_bufs(&deflater, &bufs, nva5, ARRLEN(nva5)); - blocklen = nghttp2_bufs_len(&bufs); - - CU_ASSERT(0 == rv); - CU_ASSERT(blocklen > 0); - CU_ASSERT(blocklen == inflate_hd(&inflater, &out, &bufs, 0, mem)); - - CU_ASSERT(2 == out.nvlen); - assert_nv_equal(nva5, out.nva, 2, mem); - - nva_out_reset(&out, mem); - nghttp2_bufs_reset(&bufs); - - /* Cleanup */ - nghttp2_bufs_free(&bufs); - nghttp2_hd_inflate_free(&inflater); - nghttp2_hd_deflate_free(&deflater); -} - -void test_nghttp2_hd_deflate_same_indexed_repr(void) { - nghttp2_hd_deflater deflater; - nghttp2_hd_inflater inflater; - nghttp2_nv nva1[] = {MAKE_NV("host", "alpha"), MAKE_NV("host", "alpha")}; - nghttp2_nv nva2[] = {MAKE_NV("host", "alpha"), MAKE_NV("host", "alpha"), - MAKE_NV("host", "alpha")}; - nghttp2_bufs bufs; - ssize_t blocklen; - nva_out out; - int rv; - nghttp2_mem *mem; - - mem = nghttp2_mem_default(); - frame_pack_bufs_init(&bufs); - - nva_out_init(&out); - CU_ASSERT(0 == nghttp2_hd_deflate_init(&deflater, mem)); - CU_ASSERT(0 == nghttp2_hd_inflate_init(&inflater, mem)); - - /* Encode 2 same headers. Emit 1 literal reprs and 1 index repr. */ - rv = nghttp2_hd_deflate_hd_bufs(&deflater, &bufs, nva1, ARRLEN(nva1)); - blocklen = nghttp2_bufs_len(&bufs); - - CU_ASSERT(0 == rv); - CU_ASSERT(blocklen > 0); - CU_ASSERT(blocklen == inflate_hd(&inflater, &out, &bufs, 0, mem)); - - CU_ASSERT(2 == out.nvlen); - assert_nv_equal(nva1, out.nva, 2, mem); - - nva_out_reset(&out, mem); - nghttp2_bufs_reset(&bufs); - - /* Encode 3 same headers. This time, emits 3 index reprs. */ - rv = nghttp2_hd_deflate_hd_bufs(&deflater, &bufs, nva2, ARRLEN(nva2)); - blocklen = nghttp2_bufs_len(&bufs); - - CU_ASSERT(0 == rv); - CU_ASSERT(blocklen == 3); - CU_ASSERT(blocklen == inflate_hd(&inflater, &out, &bufs, 0, mem)); - - CU_ASSERT(3 == out.nvlen); - assert_nv_equal(nva2, out.nva, 3, mem); - - nva_out_reset(&out, mem); - nghttp2_bufs_reset(&bufs); - - /* Cleanup */ - nghttp2_bufs_free(&bufs); - nghttp2_hd_inflate_free(&inflater); - nghttp2_hd_deflate_free(&deflater); -} - -void test_nghttp2_hd_inflate_indexed(void) { - nghttp2_hd_inflater inflater; - nghttp2_bufs bufs; - ssize_t blocklen; - nghttp2_nv nv = MAKE_NV(":path", "/"); - nva_out out; - nghttp2_mem *mem; - - mem = nghttp2_mem_default(); - frame_pack_bufs_init(&bufs); - - nva_out_init(&out); - nghttp2_hd_inflate_init(&inflater, mem); - - nghttp2_bufs_addb(&bufs, (1 << 7) | 4); - - blocklen = nghttp2_bufs_len(&bufs); - - CU_ASSERT(1 == blocklen); - CU_ASSERT(blocklen == inflate_hd(&inflater, &out, &bufs, 0, mem)); - - CU_ASSERT(1 == out.nvlen); - - assert_nv_equal(&nv, out.nva, 1, mem); - - nva_out_reset(&out, mem); - nghttp2_bufs_reset(&bufs); - - /* index = 0 is error */ - nghttp2_bufs_addb(&bufs, 1 << 7); - - blocklen = nghttp2_bufs_len(&bufs); - - CU_ASSERT(1 == blocklen); - CU_ASSERT(NGHTTP2_ERR_HEADER_COMP == - inflate_hd(&inflater, &out, &bufs, 0, mem)); - - nghttp2_bufs_free(&bufs); - nghttp2_hd_inflate_free(&inflater); -} - -void test_nghttp2_hd_inflate_indname_noinc(void) { - nghttp2_hd_inflater inflater; - nghttp2_bufs bufs; - ssize_t blocklen; - nghttp2_nv nv[] = {/* Huffman */ - MAKE_NV("user-agent", "nghttp2"), - /* Expecting no huffman */ - MAKE_NV("user-agent", "x")}; - size_t i; - nva_out out; - nghttp2_mem *mem; - - mem = nghttp2_mem_default(); - frame_pack_bufs_init(&bufs); - - nva_out_init(&out); - nghttp2_hd_inflate_init(&inflater, mem); - - for (i = 0; i < ARRLEN(nv); ++i) { - CU_ASSERT(0 == nghttp2_hd_emit_indname_block(&bufs, 57, &nv[i], - NGHTTP2_HD_WITHOUT_INDEXING)); - - blocklen = nghttp2_bufs_len(&bufs); - - CU_ASSERT(blocklen > 0); - CU_ASSERT(blocklen == inflate_hd(&inflater, &out, &bufs, 0, mem)); - - CU_ASSERT(1 == out.nvlen); - assert_nv_equal(&nv[i], out.nva, 1, mem); - CU_ASSERT(0 == inflater.ctx.hd_table.len); - - nva_out_reset(&out, mem); - nghttp2_bufs_reset(&bufs); - } - - nghttp2_bufs_free(&bufs); - nghttp2_hd_inflate_free(&inflater); -} - -void test_nghttp2_hd_inflate_indname_inc(void) { - nghttp2_hd_inflater inflater; - nghttp2_bufs bufs; - ssize_t blocklen; - nghttp2_nv nv = MAKE_NV("user-agent", "nghttp2"); - nva_out out; - nghttp2_mem *mem; - - mem = nghttp2_mem_default(); - frame_pack_bufs_init(&bufs); - - nva_out_init(&out); - nghttp2_hd_inflate_init(&inflater, mem); - - CU_ASSERT(0 == nghttp2_hd_emit_indname_block(&bufs, 57, &nv, - NGHTTP2_HD_WITH_INDEXING)); - - blocklen = nghttp2_bufs_len(&bufs); - - CU_ASSERT(blocklen > 0); - CU_ASSERT(blocklen == inflate_hd(&inflater, &out, &bufs, 0, mem)); - - CU_ASSERT(1 == out.nvlen); - assert_nv_equal(&nv, out.nva, 1, mem); - CU_ASSERT(1 == inflater.ctx.hd_table.len); - assert_nv_equal( - &nv, &GET_TABLE_ENT(&inflater.ctx, NGHTTP2_STATIC_TABLE_LENGTH + - inflater.ctx.hd_table.len - 1)->nv, - 1, mem); - - nva_out_reset(&out, mem); - nghttp2_bufs_free(&bufs); - nghttp2_hd_inflate_free(&inflater); -} - -void test_nghttp2_hd_inflate_indname_inc_eviction(void) { - nghttp2_hd_inflater inflater; - nghttp2_bufs bufs; - ssize_t blocklen; - uint8_t value[1025]; - nva_out out; - nghttp2_nv nv; - nghttp2_mem *mem; - - mem = nghttp2_mem_default(); - frame_pack_bufs_init(&bufs); - - nva_out_init(&out); - nghttp2_hd_inflate_init(&inflater, mem); - - memset(value, '0', sizeof(value)); - value[sizeof(value) - 1] = '\0'; - nv.value = value; - nv.valuelen = sizeof(value) - 1; - - nv.flags = NGHTTP2_NV_FLAG_NONE; - - CU_ASSERT(0 == nghttp2_hd_emit_indname_block(&bufs, 14, &nv, - NGHTTP2_HD_WITH_INDEXING)); - CU_ASSERT(0 == nghttp2_hd_emit_indname_block(&bufs, 15, &nv, - NGHTTP2_HD_WITH_INDEXING)); - CU_ASSERT(0 == nghttp2_hd_emit_indname_block(&bufs, 16, &nv, - NGHTTP2_HD_WITH_INDEXING)); - CU_ASSERT(0 == nghttp2_hd_emit_indname_block(&bufs, 17, &nv, - NGHTTP2_HD_WITH_INDEXING)); - - blocklen = nghttp2_bufs_len(&bufs); - - CU_ASSERT(blocklen > 0); - - CU_ASSERT(blocklen == inflate_hd(&inflater, &out, &bufs, 0, mem)); - - CU_ASSERT(4 == out.nvlen); - CU_ASSERT(14 == out.nva[0].namelen); - CU_ASSERT(0 == memcmp("accept-charset", out.nva[0].name, out.nva[0].namelen)); - CU_ASSERT(sizeof(value) - 1 == out.nva[0].valuelen); - - nva_out_reset(&out, mem); - nghttp2_bufs_reset(&bufs); - - CU_ASSERT(3 == inflater.ctx.hd_table.len); - - nghttp2_bufs_free(&bufs); - nghttp2_hd_inflate_free(&inflater); -} - -void test_nghttp2_hd_inflate_newname_noinc(void) { - nghttp2_hd_inflater inflater; - nghttp2_bufs bufs; - ssize_t blocklen; - nghttp2_nv nv[] = {/* Expecting huffman for both */ - MAKE_NV("my-long-content-length", "nghttp2"), - /* Expecting no huffman for both */ - MAKE_NV("x", "y"), - /* Huffman for key only */ - MAKE_NV("my-long-content-length", "y"), - /* Huffman for value only */ - MAKE_NV("x", "nghttp2")}; - size_t i; - nva_out out; - nghttp2_mem *mem; - - mem = nghttp2_mem_default(); - frame_pack_bufs_init(&bufs); - - nva_out_init(&out); - nghttp2_hd_inflate_init(&inflater, mem); - for (i = 0; i < ARRLEN(nv); ++i) { - CU_ASSERT(0 == nghttp2_hd_emit_newname_block(&bufs, &nv[i], - NGHTTP2_HD_WITHOUT_INDEXING)); - - blocklen = nghttp2_bufs_len(&bufs); - - CU_ASSERT(blocklen > 0); - CU_ASSERT(blocklen == inflate_hd(&inflater, &out, &bufs, 0, mem)); - - CU_ASSERT(1 == out.nvlen); - assert_nv_equal(&nv[i], out.nva, 1, mem); - CU_ASSERT(0 == inflater.ctx.hd_table.len); - - nva_out_reset(&out, mem); - nghttp2_bufs_reset(&bufs); - } - - nghttp2_bufs_free(&bufs); - nghttp2_hd_inflate_free(&inflater); -} - -void test_nghttp2_hd_inflate_newname_inc(void) { - nghttp2_hd_inflater inflater; - nghttp2_bufs bufs; - ssize_t blocklen; - nghttp2_nv nv = MAKE_NV("x-rel", "nghttp2"); - nva_out out; - nghttp2_mem *mem; - - mem = nghttp2_mem_default(); - frame_pack_bufs_init(&bufs); - - nva_out_init(&out); - nghttp2_hd_inflate_init(&inflater, mem); - - CU_ASSERT( - 0 == nghttp2_hd_emit_newname_block(&bufs, &nv, NGHTTP2_HD_WITH_INDEXING)); - - blocklen = nghttp2_bufs_len(&bufs); - - CU_ASSERT(blocklen > 0); - CU_ASSERT(blocklen == inflate_hd(&inflater, &out, &bufs, 0, mem)); - - CU_ASSERT(1 == out.nvlen); - assert_nv_equal(&nv, out.nva, 1, mem); - CU_ASSERT(1 == inflater.ctx.hd_table.len); - assert_nv_equal( - &nv, &GET_TABLE_ENT(&inflater.ctx, NGHTTP2_STATIC_TABLE_LENGTH + - inflater.ctx.hd_table.len - 1)->nv, - 1, mem); - - nva_out_reset(&out, mem); - nghttp2_bufs_free(&bufs); - nghttp2_hd_inflate_free(&inflater); -} - -void test_nghttp2_hd_inflate_clearall_inc(void) { - nghttp2_hd_inflater inflater; - nghttp2_bufs bufs; - ssize_t blocklen; - nghttp2_nv nv; - uint8_t value[4061]; - nva_out out; - nghttp2_mem *mem; - - mem = nghttp2_mem_default(); - bufs_large_init(&bufs, 8192); - - nva_out_init(&out); - /* Total 4097 bytes space required to hold this entry */ - nv.name = (uint8_t *)"alpha"; - nv.namelen = strlen((char *)nv.name); - memset(value, '0', sizeof(value)); - value[sizeof(value) - 1] = '\0'; - nv.value = value; - nv.valuelen = sizeof(value) - 1; - - nv.flags = NGHTTP2_NV_FLAG_NONE; - - nghttp2_hd_inflate_init(&inflater, mem); - - CU_ASSERT( - 0 == nghttp2_hd_emit_newname_block(&bufs, &nv, NGHTTP2_HD_WITH_INDEXING)); - - blocklen = nghttp2_bufs_len(&bufs); - - CU_ASSERT(blocklen > 0); - CU_ASSERT(blocklen == inflate_hd(&inflater, &out, &bufs, 0, mem)); - - CU_ASSERT(1 == out.nvlen); - assert_nv_equal(&nv, out.nva, 1, mem); - CU_ASSERT(0 == inflater.ctx.hd_table.len); - - nva_out_reset(&out, mem); - - /* Do it again */ - CU_ASSERT(blocklen == inflate_hd(&inflater, &out, &bufs, 0, mem)); - - CU_ASSERT(1 == out.nvlen); - assert_nv_equal(&nv, out.nva, 1, mem); - CU_ASSERT(0 == inflater.ctx.hd_table.len); - - nva_out_reset(&out, mem); - nghttp2_bufs_reset(&bufs); - - /* This time, 4096 bytes space required, which is just fits in the - header table */ - nv.valuelen = sizeof(value) - 2; - - CU_ASSERT( - 0 == nghttp2_hd_emit_newname_block(&bufs, &nv, NGHTTP2_HD_WITH_INDEXING)); - - blocklen = nghttp2_bufs_len(&bufs); - - CU_ASSERT(blocklen > 0); - CU_ASSERT(blocklen == inflate_hd(&inflater, &out, &bufs, 0, mem)); - - CU_ASSERT(1 == out.nvlen); - assert_nv_equal(&nv, out.nva, 1, mem); - CU_ASSERT(1 == inflater.ctx.hd_table.len); - - nva_out_reset(&out, mem); - nghttp2_bufs_reset(&bufs); - - nghttp2_bufs_free(&bufs); - nghttp2_hd_inflate_free(&inflater); -} - -void test_nghttp2_hd_inflate_zero_length_huffman(void) { - nghttp2_hd_inflater inflater; - nghttp2_bufs bufs; - /* Literal header without indexing - new name */ - uint8_t data[] = {0x40, 0x01, 0x78 /* 'x' */, 0x80}; - nva_out out; - nghttp2_mem *mem; - - mem = nghttp2_mem_default(); - frame_pack_bufs_init(&bufs); - - nva_out_init(&out); - - nghttp2_bufs_add(&bufs, data, sizeof(data)); - - /* /\* Literal header without indexing - new name *\/ */ - /* ptr[0] = 0x40; */ - /* ptr[1] = 1; */ - /* ptr[2] = 'x'; */ - /* ptr[3] = 0x80; */ - - nghttp2_hd_inflate_init(&inflater, mem); - CU_ASSERT(4 == inflate_hd(&inflater, &out, &bufs, 0, mem)); - - CU_ASSERT(1 == out.nvlen); - CU_ASSERT(1 == out.nva[0].namelen); - CU_ASSERT('x' == out.nva[0].name[0]); - CU_ASSERT(NULL == out.nva[0].value); - CU_ASSERT(0 == out.nva[0].valuelen); - - nva_out_reset(&out, mem); - nghttp2_bufs_free(&bufs); - nghttp2_hd_inflate_free(&inflater); -} - -void test_nghttp2_hd_ringbuf_reserve(void) { - nghttp2_hd_deflater deflater; - nghttp2_hd_inflater inflater; - nghttp2_nv nv; - nghttp2_bufs bufs; - nva_out out; - int i; - ssize_t rv; - ssize_t blocklen; - nghttp2_mem *mem; - - mem = nghttp2_mem_default(); - frame_pack_bufs_init(&bufs); - nva_out_init(&out); - - nv.flags = NGHTTP2_NV_FLAG_NONE; - nv.name = (uint8_t *)"a"; - nv.namelen = strlen((const char *)nv.name); - nv.valuelen = 4; - nv.value = mem->malloc(nv.valuelen + 1, NULL); - memset(nv.value, 0, nv.valuelen); - - nghttp2_hd_deflate_init2(&deflater, 8000, mem); - nghttp2_hd_inflate_init(&inflater, mem); - - nghttp2_hd_inflate_change_table_size(&inflater, 8000); - nghttp2_hd_deflate_change_table_size(&deflater, 8000); - - for (i = 0; i < 150; ++i) { - memcpy(nv.value, &i, sizeof(i)); - rv = nghttp2_hd_deflate_hd_bufs(&deflater, &bufs, &nv, 1); - blocklen = nghttp2_bufs_len(&bufs); - - CU_ASSERT(0 == rv); - CU_ASSERT(blocklen > 0); - - CU_ASSERT(blocklen == inflate_hd(&inflater, &out, &bufs, 0, mem)); - - CU_ASSERT(1 == out.nvlen); - assert_nv_equal(&nv, out.nva, 1, mem); - - nva_out_reset(&out, mem); - nghttp2_bufs_reset(&bufs); - } - - nghttp2_bufs_free(&bufs); - nghttp2_hd_inflate_free(&inflater); - nghttp2_hd_deflate_free(&deflater); - - mem->free(nv.value, NULL); -} - -void test_nghttp2_hd_change_table_size(void) { - nghttp2_hd_deflater deflater; - nghttp2_hd_inflater inflater; - nghttp2_nv nva[] = {MAKE_NV("alpha", "bravo"), MAKE_NV("charlie", "delta")}; - nghttp2_nv nva2[] = {MAKE_NV(":path", "/")}; - nghttp2_bufs bufs; - ssize_t rv; - nva_out out; - ssize_t blocklen; - nghttp2_mem *mem; - - mem = nghttp2_mem_default(); - frame_pack_bufs_init(&bufs); - - nva_out_init(&out); - - nghttp2_hd_deflate_init(&deflater, mem); - nghttp2_hd_inflate_init(&inflater, mem); - - /* inflater changes notifies 8000 max header table size */ - CU_ASSERT(0 == nghttp2_hd_inflate_change_table_size(&inflater, 8000)); - CU_ASSERT(0 == nghttp2_hd_deflate_change_table_size(&deflater, 8000)); - - CU_ASSERT(4096 == deflater.ctx.hd_table_bufsize_max); - - CU_ASSERT(8000 == inflater.ctx.hd_table_bufsize_max); - CU_ASSERT(8000 == inflater.settings_hd_table_bufsize_max); - - /* This will emit encoding context update with header table size 4096 */ - rv = nghttp2_hd_deflate_hd_bufs(&deflater, &bufs, nva, 2); - blocklen = nghttp2_bufs_len(&bufs); - - CU_ASSERT(0 == rv); - CU_ASSERT(blocklen > 0); - CU_ASSERT(2 == deflater.ctx.hd_table.len); - CU_ASSERT(4096 == deflater.ctx.hd_table_bufsize_max); - - CU_ASSERT(blocklen == inflate_hd(&inflater, &out, &bufs, 0, mem)); - CU_ASSERT(2 == inflater.ctx.hd_table.len); - CU_ASSERT(4096 == inflater.ctx.hd_table_bufsize_max); - CU_ASSERT(8000 == inflater.settings_hd_table_bufsize_max); - - nva_out_reset(&out, mem); - nghttp2_bufs_reset(&bufs); - - /* inflater changes header table size to 1024 */ - CU_ASSERT(0 == nghttp2_hd_inflate_change_table_size(&inflater, 1024)); - CU_ASSERT(0 == nghttp2_hd_deflate_change_table_size(&deflater, 1024)); - - CU_ASSERT(1024 == deflater.ctx.hd_table_bufsize_max); - - CU_ASSERT(1024 == inflater.ctx.hd_table_bufsize_max); - CU_ASSERT(1024 == inflater.settings_hd_table_bufsize_max); - - rv = nghttp2_hd_deflate_hd_bufs(&deflater, &bufs, nva, 2); - blocklen = nghttp2_bufs_len(&bufs); - - CU_ASSERT(0 == rv); - CU_ASSERT(blocklen > 0); - CU_ASSERT(2 == deflater.ctx.hd_table.len); - CU_ASSERT(1024 == deflater.ctx.hd_table_bufsize_max); - - CU_ASSERT(blocklen == inflate_hd(&inflater, &out, &bufs, 0, mem)); - CU_ASSERT(2 == inflater.ctx.hd_table.len); - CU_ASSERT(1024 == inflater.ctx.hd_table_bufsize_max); - CU_ASSERT(1024 == inflater.settings_hd_table_bufsize_max); - - nva_out_reset(&out, mem); - nghttp2_bufs_reset(&bufs); - - /* inflater changes header table size to 0 */ - CU_ASSERT(0 == nghttp2_hd_inflate_change_table_size(&inflater, 0)); - CU_ASSERT(0 == nghttp2_hd_deflate_change_table_size(&deflater, 0)); - - CU_ASSERT(0 == deflater.ctx.hd_table.len); - CU_ASSERT(0 == deflater.ctx.hd_table_bufsize_max); - - CU_ASSERT(0 == inflater.ctx.hd_table.len); - CU_ASSERT(0 == inflater.ctx.hd_table_bufsize_max); - CU_ASSERT(0 == inflater.settings_hd_table_bufsize_max); - - rv = nghttp2_hd_deflate_hd_bufs(&deflater, &bufs, nva, 2); - blocklen = nghttp2_bufs_len(&bufs); - - CU_ASSERT(0 == rv); - CU_ASSERT(blocklen > 0); - CU_ASSERT(0 == deflater.ctx.hd_table.len); - CU_ASSERT(0 == deflater.ctx.hd_table_bufsize_max); - - CU_ASSERT(blocklen == inflate_hd(&inflater, &out, &bufs, 0, mem)); - CU_ASSERT(0 == inflater.ctx.hd_table.len); - CU_ASSERT(0 == inflater.ctx.hd_table_bufsize_max); - CU_ASSERT(0 == inflater.settings_hd_table_bufsize_max); - - nva_out_reset(&out, mem); - nghttp2_bufs_reset(&bufs); - - nghttp2_bufs_free(&bufs); - nghttp2_hd_inflate_free(&inflater); - nghttp2_hd_deflate_free(&deflater); - - /* Check table buffer is expanded */ - frame_pack_bufs_init(&bufs); - - nghttp2_hd_deflate_init2(&deflater, 8192, mem); - nghttp2_hd_inflate_init(&inflater, mem); - - /* First inflater changes header table size to 8000 */ - CU_ASSERT(0 == nghttp2_hd_inflate_change_table_size(&inflater, 8000)); - CU_ASSERT(0 == nghttp2_hd_deflate_change_table_size(&deflater, 8000)); - - CU_ASSERT(8000 == deflater.ctx.hd_table_bufsize_max); - - CU_ASSERT(8000 == inflater.ctx.hd_table_bufsize_max); - CU_ASSERT(8000 == inflater.settings_hd_table_bufsize_max); - - rv = nghttp2_hd_deflate_hd_bufs(&deflater, &bufs, nva, 2); - blocklen = nghttp2_bufs_len(&bufs); - - CU_ASSERT(0 == rv); - CU_ASSERT(blocklen > 0); - CU_ASSERT(2 == deflater.ctx.hd_table.len); - CU_ASSERT(8000 == deflater.ctx.hd_table_bufsize_max); - - CU_ASSERT(blocklen == inflate_hd(&inflater, &out, &bufs, 0, mem)); - CU_ASSERT(2 == inflater.ctx.hd_table.len); - CU_ASSERT(8000 == inflater.ctx.hd_table_bufsize_max); - CU_ASSERT(8000 == inflater.settings_hd_table_bufsize_max); - - nva_out_reset(&out, mem); - nghttp2_bufs_reset(&bufs); - - CU_ASSERT(0 == nghttp2_hd_inflate_change_table_size(&inflater, 16383)); - CU_ASSERT(0 == nghttp2_hd_deflate_change_table_size(&deflater, 16383)); - - CU_ASSERT(8192 == deflater.ctx.hd_table_bufsize_max); - - CU_ASSERT(16383 == inflater.ctx.hd_table_bufsize_max); - CU_ASSERT(16383 == inflater.settings_hd_table_bufsize_max); - - rv = nghttp2_hd_deflate_hd_bufs(&deflater, &bufs, nva, 2); - blocklen = nghttp2_bufs_len(&bufs); - - CU_ASSERT(0 == rv); - CU_ASSERT(blocklen > 0); - CU_ASSERT(2 == deflater.ctx.hd_table.len); - CU_ASSERT(8192 == deflater.ctx.hd_table_bufsize_max); - - CU_ASSERT(blocklen == inflate_hd(&inflater, &out, &bufs, 0, mem)); - CU_ASSERT(2 == inflater.ctx.hd_table.len); - CU_ASSERT(8192 == inflater.ctx.hd_table_bufsize_max); - CU_ASSERT(16383 == inflater.settings_hd_table_bufsize_max); - - nva_out_reset(&out, mem); - nghttp2_bufs_reset(&bufs); - - /* Lastly, check the error condition */ - - rv = nghttp2_hd_emit_table_size(&bufs, 25600); - CU_ASSERT(rv == 0); - CU_ASSERT(NGHTTP2_ERR_HEADER_COMP == - inflate_hd(&inflater, &out, &bufs, 0, mem)); - - nva_out_reset(&out, mem); - nghttp2_bufs_reset(&bufs); - - nghttp2_hd_inflate_free(&inflater); - nghttp2_hd_deflate_free(&deflater); - - /* Check that encoder can handle the case where its allowable buffer - size is less than default size, 4096 */ - nghttp2_hd_deflate_init2(&deflater, 1024, mem); - nghttp2_hd_inflate_init(&inflater, mem); - - CU_ASSERT(1024 == deflater.ctx.hd_table_bufsize_max); - - /* This emits context update with buffer size 1024 */ - rv = nghttp2_hd_deflate_hd_bufs(&deflater, &bufs, nva, 2); - blocklen = nghttp2_bufs_len(&bufs); - - CU_ASSERT(0 == rv); - CU_ASSERT(blocklen > 0); - CU_ASSERT(2 == deflater.ctx.hd_table.len); - CU_ASSERT(1024 == deflater.ctx.hd_table_bufsize_max); - - CU_ASSERT(blocklen == inflate_hd(&inflater, &out, &bufs, 0, mem)); - CU_ASSERT(2 == inflater.ctx.hd_table.len); - CU_ASSERT(1024 == inflater.ctx.hd_table_bufsize_max); - CU_ASSERT(4096 == inflater.settings_hd_table_bufsize_max); - - nva_out_reset(&out, mem); - nghttp2_bufs_reset(&bufs); - - nghttp2_hd_inflate_free(&inflater); - nghttp2_hd_deflate_free(&deflater); - - /* Check that table size UINT32_MAX can be received */ - nghttp2_hd_deflate_init2(&deflater, UINT32_MAX, mem); - nghttp2_hd_inflate_init(&inflater, mem); - - CU_ASSERT(0 == nghttp2_hd_inflate_change_table_size(&inflater, UINT32_MAX)); - CU_ASSERT(0 == nghttp2_hd_deflate_change_table_size(&deflater, UINT32_MAX)); - - rv = nghttp2_hd_deflate_hd_bufs(&deflater, &bufs, nva, 2); - blocklen = nghttp2_bufs_len(&bufs); - - CU_ASSERT(0 == rv); - CU_ASSERT(UINT32_MAX == deflater.ctx.hd_table_bufsize_max); - - CU_ASSERT(blocklen == inflate_hd(&inflater, &out, &bufs, 0, mem)); - CU_ASSERT(UINT32_MAX == inflater.ctx.hd_table_bufsize_max); - CU_ASSERT(UINT32_MAX == inflater.settings_hd_table_bufsize_max); - - nva_out_reset(&out, mem); - nghttp2_bufs_reset(&bufs); - - nghttp2_hd_inflate_free(&inflater); - nghttp2_hd_deflate_free(&deflater); - - /* Check that context update emitted twice */ - nghttp2_hd_deflate_init2(&deflater, 4096, mem); - nghttp2_hd_inflate_init(&inflater, mem); - - CU_ASSERT(0 == nghttp2_hd_inflate_change_table_size(&inflater, 0)); - CU_ASSERT(0 == nghttp2_hd_inflate_change_table_size(&inflater, 3000)); - CU_ASSERT(0 == nghttp2_hd_deflate_change_table_size(&deflater, 0)); - CU_ASSERT(0 == nghttp2_hd_deflate_change_table_size(&deflater, 3000)); - - CU_ASSERT(0 == deflater.min_hd_table_bufsize_max); - CU_ASSERT(3000 == deflater.ctx.hd_table_bufsize_max); - - rv = nghttp2_hd_deflate_hd_bufs(&deflater, &bufs, nva2, 1); - blocklen = nghttp2_bufs_len(&bufs); - - CU_ASSERT(0 == rv); - CU_ASSERT(3 < blocklen); - CU_ASSERT(3000 == deflater.ctx.hd_table_bufsize_max); - CU_ASSERT(UINT32_MAX == deflater.min_hd_table_bufsize_max); - - CU_ASSERT(blocklen == inflate_hd(&inflater, &out, &bufs, 0, mem)); - CU_ASSERT(3000 == inflater.ctx.hd_table_bufsize_max); - CU_ASSERT(3000 == inflater.settings_hd_table_bufsize_max); - - nva_out_reset(&out, mem); - nghttp2_bufs_reset(&bufs); - - nghttp2_hd_inflate_free(&inflater); - nghttp2_hd_deflate_free(&deflater); - - nghttp2_bufs_free(&bufs); -} - -static void check_deflate_inflate(nghttp2_hd_deflater *deflater, - nghttp2_hd_inflater *inflater, - nghttp2_nv *nva, size_t nvlen, - nghttp2_mem *mem) { - nghttp2_bufs bufs; - ssize_t blocklen; - nva_out out; - int rv; - - frame_pack_bufs_init(&bufs); - - nva_out_init(&out); - rv = nghttp2_hd_deflate_hd_bufs(deflater, &bufs, nva, nvlen); - blocklen = nghttp2_bufs_len(&bufs); - - CU_ASSERT(0 == rv); - CU_ASSERT(blocklen >= 0); - - CU_ASSERT(blocklen == inflate_hd(inflater, &out, &bufs, 0, mem)); - - CU_ASSERT(nvlen == out.nvlen); - assert_nv_equal(nva, out.nva, nvlen, mem); - - nva_out_reset(&out, mem); - nghttp2_bufs_free(&bufs); -} - -void test_nghttp2_hd_deflate_inflate(void) { - nghttp2_hd_deflater deflater; - nghttp2_hd_inflater inflater; - nghttp2_nv nv1[] = { - MAKE_NV(":status", "200 OK"), - MAKE_NV("access-control-allow-origin", "*"), - MAKE_NV("cache-control", "private, max-age=0, must-revalidate"), - MAKE_NV("content-length", "76073"), - MAKE_NV("content-type", "text/html"), - MAKE_NV("date", "Sat, 27 Jul 2013 06:22:12 GMT"), - MAKE_NV("expires", "Sat, 27 Jul 2013 06:22:12 GMT"), - MAKE_NV("server", "Apache"), - MAKE_NV("vary", "foobar"), - MAKE_NV("via", "1.1 alphabravo (squid/3.x.x), 1.1 nghttpx"), - MAKE_NV("x-cache", "MISS from alphabravo"), - MAKE_NV("x-cache-action", "MISS"), - MAKE_NV("x-cache-age", "0"), - MAKE_NV("x-cache-lookup", "MISS from alphabravo:3128"), - MAKE_NV("x-lb-nocache", "true"), - }; - nghttp2_nv nv2[] = { - MAKE_NV(":status", "304 Not Modified"), - MAKE_NV("age", "0"), - MAKE_NV("cache-control", "max-age=56682045"), - MAKE_NV("content-type", "text/css"), - MAKE_NV("date", "Sat, 27 Jul 2013 06:22:12 GMT"), - MAKE_NV("expires", "Thu, 14 May 2015 07:22:57 GMT"), - MAKE_NV("last-modified", "Tue, 14 May 2013 07:22:15 GMT"), - MAKE_NV("vary", "Accept-Encoding"), - MAKE_NV("via", "1.1 alphabravo (squid/3.x.x), 1.1 nghttpx"), - MAKE_NV("x-cache", "HIT from alphabravo"), - MAKE_NV("x-cache-lookup", "HIT from alphabravo:3128")}; - nghttp2_nv nv3[] = { - MAKE_NV(":status", "304 Not Modified"), - MAKE_NV("age", "0"), - MAKE_NV("cache-control", "max-age=56682072"), - MAKE_NV("content-type", "text/css"), - MAKE_NV("date", "Sat, 27 Jul 2013 06:22:12 GMT"), - MAKE_NV("expires", "Thu, 14 May 2015 07:23:24 GMT"), - MAKE_NV("last-modified", "Tue, 14 May 2013 07:22:13 GMT"), - MAKE_NV("vary", "Accept-Encoding"), - MAKE_NV("via", "1.1 alphabravo (squid/3.x.x), 1.1 nghttpx"), - MAKE_NV("x-cache", "HIT from alphabravo"), - MAKE_NV("x-cache-lookup", "HIT from alphabravo:3128"), - }; - nghttp2_nv nv4[] = { - MAKE_NV(":status", "304 Not Modified"), - MAKE_NV("age", "0"), - MAKE_NV("cache-control", "max-age=56682022"), - MAKE_NV("content-type", "text/css"), - MAKE_NV("date", "Sat, 27 Jul 2013 06:22:12 GMT"), - MAKE_NV("expires", "Thu, 14 May 2015 07:22:34 GMT"), - MAKE_NV("last-modified", "Tue, 14 May 2013 07:22:14 GMT"), - MAKE_NV("vary", "Accept-Encoding"), - MAKE_NV("via", "1.1 alphabravo (squid/3.x.x), 1.1 nghttpx"), - MAKE_NV("x-cache", "HIT from alphabravo"), - MAKE_NV("x-cache-lookup", "HIT from alphabravo:3128"), - }; - nghttp2_nv nv5[] = { - MAKE_NV(":status", "304 Not Modified"), - MAKE_NV("age", "0"), - MAKE_NV("cache-control", "max-age=4461139"), - MAKE_NV("content-type", "application/x-javascript"), - MAKE_NV("date", "Sat, 27 Jul 2013 06:22:12 GMT"), - MAKE_NV("expires", "Mon, 16 Sep 2013 21:34:31 GMT"), - MAKE_NV("last-modified", "Thu, 05 May 2011 09:15:59 GMT"), - MAKE_NV("vary", "Accept-Encoding"), - MAKE_NV("via", "1.1 alphabravo (squid/3.x.x), 1.1 nghttpx"), - MAKE_NV("x-cache", "HIT from alphabravo"), - MAKE_NV("x-cache-lookup", "HIT from alphabravo:3128"), - }; - nghttp2_nv nv6[] = { - MAKE_NV(":status", "304 Not Modified"), - MAKE_NV("age", "0"), - MAKE_NV("cache-control", "max-age=18645951"), - MAKE_NV("content-type", "application/x-javascript"), - MAKE_NV("date", "Sat, 27 Jul 2013 06:22:12 GMT"), - MAKE_NV("expires", "Fri, 28 Feb 2014 01:48:03 GMT"), - MAKE_NV("last-modified", "Tue, 12 Jul 2011 16:02:59 GMT"), - MAKE_NV("vary", "Accept-Encoding"), - MAKE_NV("via", "1.1 alphabravo (squid/3.x.x), 1.1 nghttpx"), - MAKE_NV("x-cache", "HIT from alphabravo"), - MAKE_NV("x-cache-lookup", "HIT from alphabravo:3128"), - }; - nghttp2_nv nv7[] = { - MAKE_NV(":status", "304 Not Modified"), - MAKE_NV("age", "0"), - MAKE_NV("cache-control", "max-age=31536000"), - MAKE_NV("content-type", "application/javascript"), - MAKE_NV("date", "Sat, 27 Jul 2013 06:22:12 GMT"), - MAKE_NV("etag", "\"6807-4dc5b54e0dcc0\""), - MAKE_NV("expires", "Wed, 21 May 2014 08:32:17 GMT"), - MAKE_NV("last-modified", "Fri, 10 May 2013 11:18:51 GMT"), - MAKE_NV("via", "1.1 alphabravo (squid/3.x.x), 1.1 nghttpx"), - MAKE_NV("x-cache", "HIT from alphabravo"), - MAKE_NV("x-cache-lookup", "HIT from alphabravo:3128"), - }; - nghttp2_nv nv8[] = { - MAKE_NV(":status", "304 Not Modified"), - MAKE_NV("age", "0"), - MAKE_NV("cache-control", "max-age=31536000"), - MAKE_NV("content-type", "application/javascript"), - MAKE_NV("date", "Sat, 27 Jul 2013 06:22:12 GMT"), - MAKE_NV("etag", "\"41c6-4de7d28585b00\""), - MAKE_NV("expires", "Thu, 12 Jun 2014 10:00:58 GMT"), - MAKE_NV("last-modified", "Thu, 06 Jun 2013 14:30:36 GMT"), - MAKE_NV("via", "1.1 alphabravo (squid/3.x.x), 1.1 nghttpx"), - MAKE_NV("x-cache", "HIT from alphabravo"), - MAKE_NV("x-cache-lookup", "HIT from alphabravo:3128"), - }; - nghttp2_nv nv9[] = { - MAKE_NV(":status", "304 Not Modified"), - MAKE_NV("age", "0"), - MAKE_NV("cache-control", "max-age=31536000"), - MAKE_NV("content-type", "application/javascript"), - MAKE_NV("date", "Sat, 27 Jul 2013 06:22:12 GMT"), - MAKE_NV("etag", "\"19d6e-4dc5b35a541c0\""), - MAKE_NV("expires", "Wed, 21 May 2014 08:32:18 GMT"), - MAKE_NV("last-modified", "Fri, 10 May 2013 11:10:07 GMT"), - MAKE_NV("via", "1.1 alphabravo (squid/3.x.x), 1.1 nghttpx"), - MAKE_NV("x-cache", "HIT from alphabravo"), - MAKE_NV("x-cache-lookup", "HIT from alphabravo:3128"), - }; - nghttp2_nv nv10[] = { - MAKE_NV(":status", "304 Not Modified"), - MAKE_NV("age", "0"), - MAKE_NV("cache-control", "max-age=56682045"), - MAKE_NV("content-type", "text/css"), - MAKE_NV("date", "Sat, 27 Jul 2013 06:22:12 GMT"), - MAKE_NV("expires", "Thu, 14 May 2015 07:22:57 GMT"), - MAKE_NV("last-modified", "Tue, 14 May 2013 07:21:53 GMT"), - MAKE_NV("vary", "Accept-Encoding"), - MAKE_NV("via", "1.1 alphabravo (squid/3.x.x), 1.1 nghttpx"), - MAKE_NV("x-cache", "HIT from alphabravo"), - MAKE_NV("x-cache-lookup", "HIT from alphabravo:3128"), - }; - nghttp2_mem *mem; - - mem = nghttp2_mem_default(); - - nghttp2_hd_deflate_init(&deflater, mem); - nghttp2_hd_inflate_init(&inflater, mem); - - check_deflate_inflate(&deflater, &inflater, nv1, ARRLEN(nv1), mem); - check_deflate_inflate(&deflater, &inflater, nv2, ARRLEN(nv2), mem); - check_deflate_inflate(&deflater, &inflater, nv3, ARRLEN(nv3), mem); - check_deflate_inflate(&deflater, &inflater, nv4, ARRLEN(nv4), mem); - check_deflate_inflate(&deflater, &inflater, nv5, ARRLEN(nv5), mem); - check_deflate_inflate(&deflater, &inflater, nv6, ARRLEN(nv6), mem); - check_deflate_inflate(&deflater, &inflater, nv7, ARRLEN(nv7), mem); - check_deflate_inflate(&deflater, &inflater, nv8, ARRLEN(nv8), mem); - check_deflate_inflate(&deflater, &inflater, nv9, ARRLEN(nv9), mem); - check_deflate_inflate(&deflater, &inflater, nv10, ARRLEN(nv10), mem); - - nghttp2_hd_inflate_free(&inflater); - nghttp2_hd_deflate_free(&deflater); -} - -void test_nghttp2_hd_no_index(void) { - nghttp2_hd_deflater deflater; - nghttp2_hd_inflater inflater; - nghttp2_bufs bufs; - ssize_t blocklen; - nghttp2_nv nva[] = { - MAKE_NV(":method", "GET"), MAKE_NV(":method", "POST"), - MAKE_NV(":path", "/foo"), MAKE_NV("version", "HTTP/1.1"), - MAKE_NV(":method", "GET"), - }; - size_t i; - nva_out out; - int rv; - nghttp2_mem *mem; - - mem = nghttp2_mem_default(); - - /* 1st :method: GET can be indexable, last one is not */ - for (i = 1; i < ARRLEN(nva); ++i) { - nva[i].flags = NGHTTP2_NV_FLAG_NO_INDEX; - } - - frame_pack_bufs_init(&bufs); - - nva_out_init(&out); - - nghttp2_hd_deflate_init(&deflater, mem); - nghttp2_hd_inflate_init(&inflater, mem); - - rv = nghttp2_hd_deflate_hd_bufs(&deflater, &bufs, nva, ARRLEN(nva)); - blocklen = nghttp2_bufs_len(&bufs); - - CU_ASSERT(0 == rv); - CU_ASSERT(blocklen > 0); - CU_ASSERT(blocklen == inflate_hd(&inflater, &out, &bufs, 0, mem)); - - CU_ASSERT(ARRLEN(nva) == out.nvlen); - assert_nv_equal(nva, out.nva, ARRLEN(nva), mem); - - CU_ASSERT(out.nva[0].flags == NGHTTP2_NV_FLAG_NONE); - for (i = 1; i < ARRLEN(nva); ++i) { - CU_ASSERT(out.nva[i].flags == NGHTTP2_NV_FLAG_NO_INDEX); - } - - nva_out_reset(&out, mem); - - nghttp2_bufs_free(&bufs); - nghttp2_hd_inflate_free(&inflater); - nghttp2_hd_deflate_free(&deflater); -} - -void test_nghttp2_hd_deflate_bound(void) { - nghttp2_hd_deflater deflater; - nghttp2_nv nva[] = {MAKE_NV(":method", "GET"), MAKE_NV("alpha", "bravo")}; - nghttp2_bufs bufs; - size_t bound, bound2; - nghttp2_mem *mem; - - mem = nghttp2_mem_default(); - frame_pack_bufs_init(&bufs); - - nghttp2_hd_deflate_init(&deflater, mem); - - bound = nghttp2_hd_deflate_bound(&deflater, nva, ARRLEN(nva)); - - CU_ASSERT(12 + 6 * 2 * 2 + nva[0].namelen + nva[0].valuelen + nva[1].namelen + - nva[1].valuelen == - bound); - - nghttp2_hd_deflate_hd_bufs(&deflater, &bufs, nva, ARRLEN(nva)); - - CU_ASSERT(bound > (size_t)nghttp2_bufs_len(&bufs)); - - bound2 = nghttp2_hd_deflate_bound(&deflater, nva, ARRLEN(nva)); - - CU_ASSERT(bound == bound2); - - nghttp2_bufs_free(&bufs); - nghttp2_hd_deflate_free(&deflater); -} - -void test_nghttp2_hd_public_api(void) { - nghttp2_hd_deflater *deflater; - nghttp2_hd_inflater *inflater; - nghttp2_nv nva[] = {MAKE_NV("alpha", "bravo"), MAKE_NV("charlie", "delta")}; - uint8_t buf[4096]; - size_t buflen; - ssize_t blocklen; - nghttp2_bufs bufs; - nghttp2_mem *mem; - - mem = nghttp2_mem_default(); - - CU_ASSERT(0 == nghttp2_hd_deflate_new(&deflater, 4096)); - CU_ASSERT(0 == nghttp2_hd_inflate_new(&inflater)); - - buflen = nghttp2_hd_deflate_bound(deflater, nva, ARRLEN(nva)); - - blocklen = nghttp2_hd_deflate_hd(deflater, buf, buflen, nva, ARRLEN(nva)); - - CU_ASSERT(blocklen > 0); - - nghttp2_bufs_wrap_init(&bufs, buf, blocklen, mem); - bufs.head->buf.last += blocklen; - - CU_ASSERT(blocklen == inflate_hd(inflater, NULL, &bufs, 0, mem)); - - nghttp2_bufs_wrap_free(&bufs); - - nghttp2_hd_inflate_del(inflater); - nghttp2_hd_deflate_del(deflater); - - /* See NGHTTP2_ERR_INSUFF_BUFSIZE */ - CU_ASSERT(0 == nghttp2_hd_deflate_new(&deflater, 4096)); - - blocklen = - nghttp2_hd_deflate_hd(deflater, buf, blocklen - 1, nva, ARRLEN(nva)); - - CU_ASSERT(NGHTTP2_ERR_INSUFF_BUFSIZE == blocklen); - - nghttp2_hd_deflate_del(deflater); -} - -static size_t encode_length(uint8_t *buf, uint64_t n, size_t prefix) { - size_t k = (1 << prefix) - 1; - size_t len = 0; - *buf &= ~k; - if (n >= k) { - *buf++ |= k; - n -= k; - ++len; - } else { - *buf++ |= n; - return 1; - } - do { - ++len; - if (n >= 128) { - *buf++ = (1 << 7) | (n & 0x7f); - n >>= 7; - } else { - *buf++ = (uint8_t)n; - break; - } - } while (n); - return len; -} - -void test_nghttp2_hd_decode_length(void) { - uint32_t out; - size_t shift; - int final; - uint8_t buf[16]; - uint8_t *bufp; - size_t len; - ssize_t rv; - size_t i; - - memset(buf, 0, sizeof(buf)); - len = encode_length(buf, UINT32_MAX, 7); - - rv = nghttp2_hd_decode_length(&out, &shift, &final, 0, 0, buf, buf + len, 7); - - CU_ASSERT((ssize_t)len == rv); - CU_ASSERT(0 != final); - CU_ASSERT(UINT32_MAX == out); - - /* Make sure that we can decode integer if we feed 1 byte at a - time */ - out = 0; - shift = 0; - final = 0; - bufp = buf; - - for (i = 0; i < len; ++i, ++bufp) { - rv = nghttp2_hd_decode_length(&out, &shift, &final, out, shift, bufp, - bufp + 1, 7); - - CU_ASSERT(rv == 1); - - if (final) { - break; - } - } - - CU_ASSERT(i == len - 1); - CU_ASSERT(0 != final); - CU_ASSERT(UINT32_MAX == out); - - /* Check overflow case */ - memset(buf, 0, sizeof(buf)); - len = encode_length(buf, 1ll << 32, 7); - - rv = nghttp2_hd_decode_length(&out, &shift, &final, 0, 0, buf, buf + len, 7); - - CU_ASSERT(-1 == rv); -} - -void test_nghttp2_hd_huff_encode(void) { - int rv; - ssize_t len; - nghttp2_bufs bufs, outbufs; - nghttp2_hd_huff_decode_context ctx; - const uint8_t t1[] = {22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, - 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0}; - - frame_pack_bufs_init(&bufs); - frame_pack_bufs_init(&outbufs); - - rv = nghttp2_hd_huff_encode(&bufs, t1, sizeof(t1)); - - CU_ASSERT(rv == 0); - - nghttp2_hd_huff_decode_context_init(&ctx); - - len = nghttp2_hd_huff_decode(&ctx, &outbufs, bufs.cur->buf.pos, - nghttp2_bufs_len(&bufs), 1); - - CU_ASSERT(nghttp2_bufs_len(&bufs) == len); - CU_ASSERT((ssize_t)sizeof(t1) == nghttp2_bufs_len(&outbufs)); - - CU_ASSERT(0 == memcmp(t1, outbufs.cur->buf.pos, sizeof(t1))); - - nghttp2_bufs_free(&bufs); - nghttp2_bufs_free(&outbufs); -} diff --git a/tests/nghttp2_hd_test.h b/tests/nghttp2_hd_test.h deleted file mode 100644 index e41fad7..0000000 --- a/tests/nghttp2_hd_test.h +++ /dev/null @@ -1,47 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2013 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#ifndef NGHTTP2_HD_TEST_H -#define NGHTTP2_HD_TEST_H - -void test_nghttp2_hd_deflate(void); -void test_nghttp2_hd_deflate_same_indexed_repr(void); -void test_nghttp2_hd_inflate_indexed(void); -void test_nghttp2_hd_inflate_indname_noinc(void); -void test_nghttp2_hd_inflate_indname_inc(void); -void test_nghttp2_hd_inflate_indname_inc_eviction(void); -void test_nghttp2_hd_inflate_newname_noinc(void); -void test_nghttp2_hd_inflate_newname_inc(void); -void test_nghttp2_hd_inflate_clearall_inc(void); -void test_nghttp2_hd_inflate_zero_length_huffman(void); -void test_nghttp2_hd_ringbuf_reserve(void); -void test_nghttp2_hd_change_table_size(void); -void test_nghttp2_hd_deflate_inflate(void); -void test_nghttp2_hd_no_index(void); -void test_nghttp2_hd_deflate_bound(void); -void test_nghttp2_hd_public_api(void); -void test_nghttp2_hd_decode_length(void); -void test_nghttp2_hd_huff_encode(void); - -#endif /* NGHTTP2_HD_TEST_H */ diff --git a/tests/nghttp2_helper_test.c b/tests/nghttp2_helper_test.c deleted file mode 100644 index b29e67b..0000000 --- a/tests/nghttp2_helper_test.c +++ /dev/null @@ -1,169 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2013 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#include "nghttp2_helper_test.h" - -#include - -#include "nghttp2_helper.h" - -void test_nghttp2_adjust_local_window_size(void) { - int32_t local_window_size = 100; - int32_t recv_window_size = 50; - int32_t recv_reduction = 0; - int32_t delta; - - delta = 0; - CU_ASSERT(0 == nghttp2_adjust_local_window_size(&local_window_size, - &recv_window_size, - &recv_reduction, &delta)); - CU_ASSERT(100 == local_window_size); - CU_ASSERT(50 == recv_window_size); - CU_ASSERT(0 == recv_reduction); - CU_ASSERT(0 == delta); - - delta = 49; - CU_ASSERT(0 == nghttp2_adjust_local_window_size(&local_window_size, - &recv_window_size, - &recv_reduction, &delta)); - CU_ASSERT(100 == local_window_size); - CU_ASSERT(1 == recv_window_size); - CU_ASSERT(0 == recv_reduction); - CU_ASSERT(49 == delta); - - delta = 1; - CU_ASSERT(0 == nghttp2_adjust_local_window_size(&local_window_size, - &recv_window_size, - &recv_reduction, &delta)); - CU_ASSERT(100 == local_window_size); - CU_ASSERT(0 == recv_window_size); - CU_ASSERT(0 == recv_reduction); - CU_ASSERT(1 == delta); - - delta = 1; - CU_ASSERT(0 == nghttp2_adjust_local_window_size(&local_window_size, - &recv_window_size, - &recv_reduction, &delta)); - CU_ASSERT(101 == local_window_size); - CU_ASSERT(0 == recv_window_size); - CU_ASSERT(0 == recv_reduction); - CU_ASSERT(1 == delta); - - delta = -1; - CU_ASSERT(0 == nghttp2_adjust_local_window_size(&local_window_size, - &recv_window_size, - &recv_reduction, &delta)); - CU_ASSERT(100 == local_window_size); - CU_ASSERT(-1 == recv_window_size); - CU_ASSERT(1 == recv_reduction); - CU_ASSERT(0 == delta); - - delta = 1; - CU_ASSERT(0 == nghttp2_adjust_local_window_size(&local_window_size, - &recv_window_size, - &recv_reduction, &delta)); - CU_ASSERT(101 == local_window_size); - CU_ASSERT(0 == recv_window_size); - CU_ASSERT(0 == recv_reduction); - CU_ASSERT(0 == delta); - - delta = 100; - CU_ASSERT(0 == nghttp2_adjust_local_window_size(&local_window_size, - &recv_window_size, - &recv_reduction, &delta)); - CU_ASSERT(201 == local_window_size); - CU_ASSERT(0 == recv_window_size); - CU_ASSERT(0 == recv_reduction); - CU_ASSERT(100 == delta); - - delta = -3; - CU_ASSERT(0 == nghttp2_adjust_local_window_size(&local_window_size, - &recv_window_size, - &recv_reduction, &delta)); - CU_ASSERT(198 == local_window_size); - CU_ASSERT(-3 == recv_window_size); - CU_ASSERT(3 == recv_reduction); - CU_ASSERT(0 == delta); - - recv_window_size += 3; - - delta = 3; - CU_ASSERT(0 == nghttp2_adjust_local_window_size(&local_window_size, - &recv_window_size, - &recv_reduction, &delta)); - CU_ASSERT(201 == local_window_size); - CU_ASSERT(3 == recv_window_size); - CU_ASSERT(0 == recv_reduction); - CU_ASSERT(0 == delta); - - local_window_size = 100; - recv_window_size = 50; - recv_reduction = 0; - delta = INT32_MAX; - CU_ASSERT(NGHTTP2_ERR_FLOW_CONTROL == - nghttp2_adjust_local_window_size(&local_window_size, - &recv_window_size, &recv_reduction, - &delta)); - CU_ASSERT(100 == local_window_size); - CU_ASSERT(50 == recv_window_size); - CU_ASSERT(0 == recv_reduction); - CU_ASSERT(INT32_MAX == delta); - - delta = INT32_MIN; - CU_ASSERT(NGHTTP2_ERR_FLOW_CONTROL == - nghttp2_adjust_local_window_size(&local_window_size, - &recv_window_size, &recv_reduction, - &delta)); - CU_ASSERT(100 == local_window_size); - CU_ASSERT(50 == recv_window_size); - CU_ASSERT(0 == recv_reduction); - CU_ASSERT(INT32_MIN == delta); -} - -#define check_header_name(S) \ - nghttp2_check_header_name((const uint8_t *)S, sizeof(S) - 1) - -void test_nghttp2_check_header_name(void) { - CU_ASSERT(check_header_name(":path")); - CU_ASSERT(check_header_name("path")); - CU_ASSERT(check_header_name("!#$%&'*+-.^_`|~")); - CU_ASSERT(!check_header_name(":PATH")); - CU_ASSERT(!check_header_name("path:")); - CU_ASSERT(!check_header_name("")); - CU_ASSERT(!check_header_name(":")); -} - -#define check_header_value(S) \ - nghttp2_check_header_value((const uint8_t *)S, sizeof(S) - 1) - -void test_nghttp2_check_header_value(void) { - uint8_t goodval[] = {'a', 'b', 0x80u, 'c', 0xffu, 'd', '\t', ' '}; - uint8_t badval1[] = {'a', 0x1fu, 'b'}; - uint8_t badval2[] = {'a', 0x7fu, 'b'}; - - CU_ASSERT(check_header_value(" !|}~")); - CU_ASSERT(check_header_value(goodval)); - CU_ASSERT(!check_header_value(badval1)); - CU_ASSERT(!check_header_value(badval2)); -} diff --git a/tests/nghttp2_helper_test.h b/tests/nghttp2_helper_test.h deleted file mode 100644 index 173fde6..0000000 --- a/tests/nghttp2_helper_test.h +++ /dev/null @@ -1,32 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2013 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#ifndef NGHTTP2_HELPER_TEST_H -#define NGHTTP2_HELPER_TEST_H - -void test_nghttp2_adjust_local_window_size(void); -void test_nghttp2_check_header_name(void); -void test_nghttp2_check_header_value(void); - -#endif /* NGHTTP2_HELPER_TEST_H */ diff --git a/tests/nghttp2_map_test.c b/tests/nghttp2_map_test.c deleted file mode 100644 index c2ec825..0000000 --- a/tests/nghttp2_map_test.c +++ /dev/null @@ -1,176 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2012 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#include "nghttp2_map_test.h" - -#include - -#include "nghttp2_map.h" - -typedef struct strentry { - nghttp2_map_entry map_entry; - const char *str; -} strentry; - -static void strentry_init(strentry *entry, key_type key, const char *str) { - nghttp2_map_entry_init(&entry->map_entry, key); - entry->str = str; -} - -void test_nghttp2_map(void) { - strentry foo, FOO, bar, baz, shrubbery; - nghttp2_map map; - nghttp2_map_init(&map, nghttp2_mem_default()); - - strentry_init(&foo, 1, "foo"); - strentry_init(&FOO, 1, "FOO"); - strentry_init(&bar, 2, "bar"); - strentry_init(&baz, 3, "baz"); - strentry_init(&shrubbery, 4, "shrubbery"); - - CU_ASSERT(0 == nghttp2_map_insert(&map, &foo.map_entry)); - CU_ASSERT(strcmp("foo", ((strentry *)nghttp2_map_find(&map, 1))->str) == 0); - CU_ASSERT(1 == nghttp2_map_size(&map)); - - CU_ASSERT(NGHTTP2_ERR_INVALID_ARGUMENT == - nghttp2_map_insert(&map, &FOO.map_entry)); - - CU_ASSERT(1 == nghttp2_map_size(&map)); - CU_ASSERT(strcmp("foo", ((strentry *)nghttp2_map_find(&map, 1))->str) == 0); - - CU_ASSERT(0 == nghttp2_map_insert(&map, &bar.map_entry)); - CU_ASSERT(2 == nghttp2_map_size(&map)); - - CU_ASSERT(0 == nghttp2_map_insert(&map, &baz.map_entry)); - CU_ASSERT(3 == nghttp2_map_size(&map)); - - CU_ASSERT(0 == nghttp2_map_insert(&map, &shrubbery.map_entry)); - CU_ASSERT(4 == nghttp2_map_size(&map)); - - CU_ASSERT(strcmp("baz", ((strentry *)nghttp2_map_find(&map, 3))->str) == 0); - - nghttp2_map_remove(&map, 3); - CU_ASSERT(3 == nghttp2_map_size(&map)); - CU_ASSERT(NULL == nghttp2_map_find(&map, 3)); - - nghttp2_map_remove(&map, 1); - CU_ASSERT(2 == nghttp2_map_size(&map)); - CU_ASSERT(NULL == nghttp2_map_find(&map, 1)); - - /* Erasing non-existent entry */ - nghttp2_map_remove(&map, 1); - CU_ASSERT(2 == nghttp2_map_size(&map)); - CU_ASSERT(NULL == nghttp2_map_find(&map, 1)); - - CU_ASSERT(strcmp("bar", ((strentry *)nghttp2_map_find(&map, 2))->str) == 0); - CU_ASSERT(strcmp("shrubbery", ((strentry *)nghttp2_map_find(&map, 4))->str) == - 0); - - nghttp2_map_free(&map); -} - -static void shuffle(int *a, int n) { - int i; - for (i = n - 1; i >= 1; --i) { - size_t j = (int)((double)(i + 1) * rand() / (RAND_MAX + 1.0)); - int t = a[j]; - a[j] = a[i]; - a[i] = t; - } -} - -static int eachfun(nghttp2_map_entry *entry _U_, void *ptr _U_) { return 0; } - -#define NUM_ENT 6000 -strentry arr[NUM_ENT]; -int order[NUM_ENT]; - -void test_nghttp2_map_functional(void) { - nghttp2_map map; - int i; - - nghttp2_map_init(&map, nghttp2_mem_default()); - for (i = 0; i < NUM_ENT; ++i) { - strentry_init(&arr[i], i + 1, "foo"); - order[i] = i + 1; - } - /* insertion */ - shuffle(order, NUM_ENT); - for (i = 0; i < NUM_ENT; ++i) { - CU_ASSERT(0 == nghttp2_map_insert(&map, &arr[order[i] - 1].map_entry)); - } - /* traverse */ - nghttp2_map_each(&map, eachfun, NULL); - /* find */ - shuffle(order, NUM_ENT); - for (i = 0; i < NUM_ENT; ++i) { - nghttp2_map_find(&map, order[i]); - } - /* remove */ - shuffle(order, NUM_ENT); - for (i = 0; i < NUM_ENT; ++i) { - CU_ASSERT(0 == nghttp2_map_remove(&map, order[i])); - } - - /* each_free (but no op function for testing purpose) */ - for (i = 0; i < NUM_ENT; ++i) { - strentry_init(&arr[i], i + 1, "foo"); - } - /* insert once again */ - for (i = 0; i < NUM_ENT; ++i) { - CU_ASSERT(0 == nghttp2_map_insert(&map, &arr[i].map_entry)); - } - nghttp2_map_each_free(&map, eachfun, NULL); - nghttp2_map_free(&map); -} - -static int entry_free(nghttp2_map_entry *entry, void *ptr) { - nghttp2_mem *mem = ptr; - - mem->free(entry, NULL); - return 0; -} - -void test_nghttp2_map_each_free(void) { - nghttp2_mem *mem = nghttp2_mem_default(); - strentry *foo = mem->malloc(sizeof(strentry), NULL), - *bar = mem->malloc(sizeof(strentry), NULL), - *baz = mem->malloc(sizeof(strentry), NULL), - *shrubbery = mem->malloc(sizeof(strentry), NULL); - nghttp2_map map; - nghttp2_map_init(&map, nghttp2_mem_default()); - - strentry_init(foo, 1, "foo"); - strentry_init(bar, 2, "bar"); - strentry_init(baz, 3, "baz"); - strentry_init(shrubbery, 4, "shrubbery"); - - nghttp2_map_insert(&map, &foo->map_entry); - nghttp2_map_insert(&map, &bar->map_entry); - nghttp2_map_insert(&map, &baz->map_entry); - nghttp2_map_insert(&map, &shrubbery->map_entry); - - nghttp2_map_each_free(&map, entry_free, mem); - nghttp2_map_free(&map); -} diff --git a/tests/nghttp2_map_test.h b/tests/nghttp2_map_test.h deleted file mode 100644 index f0b723b..0000000 --- a/tests/nghttp2_map_test.h +++ /dev/null @@ -1,32 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2012 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#ifndef NGHTTP2_MAP_TEST_H -#define NGHTTP2_MAP_TEST_H - -void test_nghttp2_map(void); -void test_nghttp2_map_functional(void); -void test_nghttp2_map_each_free(void); - -#endif /* NGHTTP2_MAP_TEST_H */ diff --git a/tests/nghttp2_npn_test.c b/tests/nghttp2_npn_test.c deleted file mode 100644 index cbd65b7..0000000 --- a/tests/nghttp2_npn_test.c +++ /dev/null @@ -1,72 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2012 Twist Inc. - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#include "nghttp2_npn_test.h" - -#include - -#include -#include - -static void http2(void) { - const unsigned char p[] = {8, 'h', 't', 't', 'p', '/', '1', '.', '1', 2, - 'h', '2', 6, 's', 'p', 'd', 'y', '/', '3'}; - unsigned char outlen; - unsigned char *out; - CU_ASSERT(1 == nghttp2_select_next_protocol(&out, &outlen, p, sizeof(p))); - CU_ASSERT(NGHTTP2_PROTO_VERSION_ID_LEN == outlen); - CU_ASSERT(memcmp(NGHTTP2_PROTO_VERSION_ID, out, outlen) == 0); -} - -static void http11(void) { - const unsigned char spdy[] = { - 6, 's', 'p', 'd', 'y', '/', '4', 8, 's', 'p', 'd', 'y', '/', - '2', '.', '1', 8, 'h', 't', 't', 'p', '/', '1', '.', '1', - }; - unsigned char outlen; - unsigned char *out; - CU_ASSERT(0 == - nghttp2_select_next_protocol(&out, &outlen, spdy, sizeof(spdy))); - CU_ASSERT(8 == outlen); - CU_ASSERT(memcmp("http/1.1", out, outlen) == 0); -} - -static void no_overlap(void) { - const unsigned char spdy[] = { - 6, 's', 'p', 'd', 'y', '/', '4', 8, 's', 'p', 'd', 'y', '/', - '2', '.', '1', 8, 'h', 't', 't', 'p', '/', '1', '.', '0', - }; - unsigned char outlen = 0; - unsigned char *out = NULL; - CU_ASSERT(-1 == - nghttp2_select_next_protocol(&out, &outlen, spdy, sizeof(spdy))); - CU_ASSERT(0 == outlen); - CU_ASSERT(NULL == out); -} - -void test_nghttp2_npn(void) { - http2(); - http11(); - no_overlap(); -} diff --git a/tests/nghttp2_npn_test.h b/tests/nghttp2_npn_test.h deleted file mode 100644 index e806f3a..0000000 --- a/tests/nghttp2_npn_test.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2012 Twist Inc. - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#ifndef NGHTTP2_NPN_TEST_H -#define NGHTTP2_NPN_TEST_H - -void test_nghttp2_npn(void); - -#endif /* NGHTTP2_NPN_TEST_H */ diff --git a/tests/nghttp2_pq_test.c b/tests/nghttp2_pq_test.c deleted file mode 100644 index a88f75b..0000000 --- a/tests/nghttp2_pq_test.c +++ /dev/null @@ -1,123 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2012 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#include "nghttp2_pq_test.h" - -#include - -#include "nghttp2_pq.h" - -static int pq_less(const void *lhs, const void *rhs) { - return strcmp(lhs, rhs) < 0; -} - -void test_nghttp2_pq(void) { - int i; - nghttp2_pq pq; - nghttp2_pq_init(&pq, pq_less, nghttp2_mem_default()); - CU_ASSERT(nghttp2_pq_empty(&pq)); - CU_ASSERT(0 == nghttp2_pq_size(&pq)); - CU_ASSERT(0 == nghttp2_pq_push(&pq, (void *)"foo")); - CU_ASSERT(0 == nghttp2_pq_empty(&pq)); - CU_ASSERT(1 == nghttp2_pq_size(&pq)); - CU_ASSERT(strcmp("foo", nghttp2_pq_top(&pq)) == 0); - CU_ASSERT(0 == nghttp2_pq_push(&pq, (void *)"bar")); - CU_ASSERT(strcmp("bar", nghttp2_pq_top(&pq)) == 0); - CU_ASSERT(0 == nghttp2_pq_push(&pq, (void *)"baz")); - CU_ASSERT(strcmp("bar", nghttp2_pq_top(&pq)) == 0); - CU_ASSERT(0 == nghttp2_pq_push(&pq, (void *)"C")); - CU_ASSERT(4 == nghttp2_pq_size(&pq)); - CU_ASSERT(strcmp("C", nghttp2_pq_top(&pq)) == 0); - nghttp2_pq_pop(&pq); - CU_ASSERT(3 == nghttp2_pq_size(&pq)); - CU_ASSERT(strcmp("bar", nghttp2_pq_top(&pq)) == 0); - nghttp2_pq_pop(&pq); - CU_ASSERT(strcmp("baz", nghttp2_pq_top(&pq)) == 0); - nghttp2_pq_pop(&pq); - CU_ASSERT(strcmp("foo", nghttp2_pq_top(&pq)) == 0); - nghttp2_pq_pop(&pq); - CU_ASSERT(nghttp2_pq_empty(&pq)); - CU_ASSERT(0 == nghttp2_pq_size(&pq)); - CU_ASSERT(NULL == nghttp2_pq_top(&pq)); - - /* Add bunch of entry to see realloc works */ - for (i = 0; i < 10000; ++i) { - CU_ASSERT(0 == nghttp2_pq_push(&pq, (void *)"foo")); - CU_ASSERT((size_t)(i + 1) == nghttp2_pq_size(&pq)); - } - for (i = 10000; i > 0; --i) { - CU_ASSERT(NULL != nghttp2_pq_top(&pq)); - nghttp2_pq_pop(&pq); - CU_ASSERT((size_t)(i - 1) == nghttp2_pq_size(&pq)); - } - - nghttp2_pq_free(&pq); -} - -typedef struct { - int key; - int val; -} node; - -static int node_less(const void *lhs, const void *rhs) { - node *ln = (node *)lhs; - node *rn = (node *)rhs; - return ln->key < rn->key; -} - -static int node_update(void *item, void *arg _U_) { - node *nd = (node *)item; - if ((nd->key % 2) == 0) { - nd->key *= -1; - return 1; - } else { - return 0; - } -} - -void test_nghttp2_pq_update(void) { - nghttp2_pq pq; - node nodes[10]; - int i; - node *nd; - int ans[] = {-8, -6, -4, -2, 0, 1, 3, 5, 7, 9}; - - nghttp2_pq_init(&pq, node_less, nghttp2_mem_default()); - - for (i = 0; i < (int)(sizeof(nodes) / sizeof(nodes[0])); ++i) { - nodes[i].key = i; - nodes[i].val = i; - nghttp2_pq_push(&pq, &nodes[i]); - } - - nghttp2_pq_update(&pq, node_update, NULL); - - for (i = 0; i < (int)(sizeof(nodes) / sizeof(nodes[0])); ++i) { - nd = nghttp2_pq_top(&pq); - CU_ASSERT(ans[i] == nd->key); - nghttp2_pq_pop(&pq); - } - - nghttp2_pq_free(&pq); -} diff --git a/tests/nghttp2_pq_test.h b/tests/nghttp2_pq_test.h deleted file mode 100644 index 7818194..0000000 --- a/tests/nghttp2_pq_test.h +++ /dev/null @@ -1,31 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2012 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#ifndef NGHTTP2_PQ_TEST_H -#define NGHTTP2_PQ_TEST_H - -void test_nghttp2_pq(void); -void test_nghttp2_pq_update(void); - -#endif /* NGHTTP2_PQ_TEST_H */ diff --git a/tests/nghttp2_queue_test.c b/tests/nghttp2_queue_test.c deleted file mode 100644 index 76cc98a..0000000 --- a/tests/nghttp2_queue_test.c +++ /dev/null @@ -1,48 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2012 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#include "nghttp2_queue_test.h" - -#include - -#include "nghttp2_queue.h" - -void test_nghttp2_queue(void) { - int ints[] = {1, 2, 3, 4, 5}; - int i; - nghttp2_queue queue; - nghttp2_queue_init(&queue); - CU_ASSERT(nghttp2_queue_empty(&queue)); - for (i = 0; i < 5; ++i) { - nghttp2_queue_push(&queue, &ints[i]); - CU_ASSERT_EQUAL(ints[0], *(int *)(nghttp2_queue_front(&queue))); - CU_ASSERT(!nghttp2_queue_empty(&queue)); - } - for (i = 0; i < 5; ++i) { - CU_ASSERT_EQUAL(ints[i], *(int *)(nghttp2_queue_front(&queue))); - nghttp2_queue_pop(&queue); - } - CU_ASSERT(nghttp2_queue_empty(&queue)); - nghttp2_queue_free(&queue); -} diff --git a/tests/nghttp2_queue_test.h b/tests/nghttp2_queue_test.h deleted file mode 100644 index 944697e..0000000 --- a/tests/nghttp2_queue_test.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2012 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#ifndef NGHTTP2_QUEUE_TEST_H -#define NGHTTP2_QUEUE_TEST_H - -void test_nghttp2_queue(void); - -#endif /* NGHTTP2_QUEUE_TEST_H */ diff --git a/tests/nghttp2_session_test.c b/tests/nghttp2_session_test.c deleted file mode 100644 index 7783e17..0000000 --- a/tests/nghttp2_session_test.c +++ /dev/null @@ -1,8125 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2013 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#include "nghttp2_session_test.h" - -#include -#include - -#include - -#include "nghttp2_session.h" -#include "nghttp2_stream.h" -#include "nghttp2_net.h" -#include "nghttp2_helper.h" -#include "nghttp2_test_helper.h" -#include "nghttp2_priority_spec.h" - -extern int nghttp2_enable_strict_preface; - -#define OB_CTRL(ITEM) nghttp2_outbound_item_get_ctrl_frame(ITEM) -#define OB_CTRL_TYPE(ITEM) nghttp2_outbound_item_get_ctrl_frame_type(ITEM) -#define OB_DATA(ITEM) nghttp2_outbound_item_get_data_frame(ITEM) - -typedef struct { - uint8_t buf[65535]; - size_t length; -} accumulator; - -typedef struct { - uint8_t data[8192]; - uint8_t *datamark; - uint8_t *datalimit; - size_t feedseq[8192]; - size_t seqidx; -} scripted_data_feed; - -typedef struct { - accumulator *acc; - scripted_data_feed *df; - int frame_recv_cb_called, invalid_frame_recv_cb_called; - uint8_t recv_frame_type; - int frame_send_cb_called; - uint8_t sent_frame_type; - int frame_not_send_cb_called; - uint8_t not_sent_frame_type; - int not_sent_error; - int stream_close_cb_called; - uint32_t stream_close_error_code; - size_t data_source_length; - int32_t stream_id; - size_t block_count; - int data_chunk_recv_cb_called; - const nghttp2_frame *frame; - size_t fixed_sendlen; - int header_cb_called; - int begin_headers_cb_called; - nghttp2_nv nv; - size_t data_chunk_len; - size_t padlen; - int begin_frame_cb_called; -} my_user_data; - -static const nghttp2_nv reqnv[] = { - MAKE_NV(":method", "GET"), MAKE_NV(":path", "/"), - MAKE_NV(":scheme", "https"), MAKE_NV(":authority", "localhost"), -}; - -static const nghttp2_nv resnv[] = { - MAKE_NV(":status", "200"), -}; - -static const nghttp2_nv trailernv[] = { - // from http://tools.ietf.org/html/rfc6249#section-7 - MAKE_NV("digest", "SHA-256=" - "MWVkMWQxYTRiMzk5MDQ0MzI3NGU5NDEyZTk5OWY1ZGFmNzgyZTJlODYz" - "YjRjYzFhOTlmNTQwYzI2M2QwM2U2MQ=="), -}; - -static void scripted_data_feed_init2(scripted_data_feed *df, - nghttp2_bufs *bufs) { - nghttp2_buf_chain *ci; - nghttp2_buf *buf; - uint8_t *ptr; - size_t len; - - memset(df, 0, sizeof(scripted_data_feed)); - ptr = df->data; - len = 0; - - for (ci = bufs->head; ci; ci = ci->next) { - buf = &ci->buf; - ptr = nghttp2_cpymem(ptr, buf->pos, nghttp2_buf_len(buf)); - len += nghttp2_buf_len(buf); - } - - df->datamark = df->data; - df->datalimit = df->data + len; - df->feedseq[0] = len; -} - -static ssize_t null_send_callback(nghttp2_session *session _U_, - const uint8_t *data _U_, size_t len, - int flags _U_, void *user_data _U_) { - return len; -} - -static ssize_t fail_send_callback(nghttp2_session *session _U_, - const uint8_t *data _U_, size_t len _U_, - int flags _U_, void *user_data _U_) { - return NGHTTP2_ERR_CALLBACK_FAILURE; -} - -static ssize_t fixed_bytes_send_callback(nghttp2_session *session _U_, - const uint8_t *data _U_, size_t len, - int flags _U_, void *user_data) { - size_t fixed_sendlen = ((my_user_data *)user_data)->fixed_sendlen; - return fixed_sendlen < len ? fixed_sendlen : len; -} - -static ssize_t scripted_recv_callback(nghttp2_session *session _U_, - uint8_t *data, size_t len, int flags _U_, - void *user_data) { - scripted_data_feed *df = ((my_user_data *)user_data)->df; - size_t wlen = df->feedseq[df->seqidx] > len ? len : df->feedseq[df->seqidx]; - memcpy(data, df->datamark, wlen); - df->datamark += wlen; - df->feedseq[df->seqidx] -= wlen; - if (df->feedseq[df->seqidx] == 0) { - ++df->seqidx; - } - return wlen; -} - -static ssize_t eof_recv_callback(nghttp2_session *session _U_, - uint8_t *data _U_, size_t len _U_, - int flags _U_, void *user_data _U_) { - return NGHTTP2_ERR_EOF; -} - -static ssize_t accumulator_send_callback(nghttp2_session *session _U_, - const uint8_t *buf, size_t len, - int flags _U_, void *user_data) { - accumulator *acc = ((my_user_data *)user_data)->acc; - assert(acc->length + len < sizeof(acc->buf)); - memcpy(acc->buf + acc->length, buf, len); - acc->length += len; - return len; -} - -static int on_begin_frame_callback(nghttp2_session *session _U_, - const nghttp2_frame_hd *hd _U_, - void *user_data) { - my_user_data *ud = (my_user_data *)user_data; - ++ud->begin_frame_cb_called; - return 0; -} - -static int on_frame_recv_callback(nghttp2_session *session _U_, - const nghttp2_frame *frame, void *user_data) { - my_user_data *ud = (my_user_data *)user_data; - ++ud->frame_recv_cb_called; - ud->recv_frame_type = frame->hd.type; - return 0; -} - -static int on_invalid_frame_recv_callback(nghttp2_session *session _U_, - const nghttp2_frame *frame _U_, - int lib_error_code _U_, - void *user_data) { - my_user_data *ud = (my_user_data *)user_data; - ++ud->invalid_frame_recv_cb_called; - return 0; -} - -static int on_frame_send_callback(nghttp2_session *session _U_, - const nghttp2_frame *frame, void *user_data) { - my_user_data *ud = (my_user_data *)user_data; - ++ud->frame_send_cb_called; - ud->sent_frame_type = frame->hd.type; - return 0; -} - -static int on_frame_not_send_callback(nghttp2_session *session _U_, - const nghttp2_frame *frame, int lib_error, - void *user_data) { - my_user_data *ud = (my_user_data *)user_data; - ++ud->frame_not_send_cb_called; - ud->not_sent_frame_type = frame->hd.type; - ud->not_sent_error = lib_error; - return 0; -} - -static int on_data_chunk_recv_callback(nghttp2_session *session _U_, - uint8_t flags _U_, int32_t stream_id _U_, - const uint8_t *data _U_, size_t len, - void *user_data) { - my_user_data *ud = (my_user_data *)user_data; - ++ud->data_chunk_recv_cb_called; - ud->data_chunk_len = len; - return 0; -} - -static int pause_on_data_chunk_recv_callback(nghttp2_session *session _U_, - uint8_t flags _U_, - int32_t stream_id _U_, - const uint8_t *data _U_, - size_t len _U_, void *user_data) { - my_user_data *ud = (my_user_data *)user_data; - ++ud->data_chunk_recv_cb_called; - return NGHTTP2_ERR_PAUSE; -} - -static ssize_t select_padding_callback(nghttp2_session *session _U_, - const nghttp2_frame *frame, - size_t max_payloadlen, void *user_data) { - my_user_data *ud = (my_user_data *)user_data; - return nghttp2_min(max_payloadlen, frame->hd.length + ud->padlen); -} - -static ssize_t too_large_data_source_length_callback( - nghttp2_session *session _U_, uint8_t frame_type _U_, int32_t stream_id _U_, - int32_t session_remote_window_size _U_, - int32_t stream_remote_window_size _U_, uint32_t remote_max_frame_size _U_, - void *user_data _U_) { - return NGHTTP2_MAX_FRAME_SIZE_MAX + 1; -} - -static ssize_t smallest_length_data_source_length_callback( - nghttp2_session *session _U_, uint8_t frame_type _U_, int32_t stream_id _U_, - int32_t session_remote_window_size _U_, - int32_t stream_remote_window_size _U_, uint32_t remote_max_frame_size _U_, - void *user_data _U_) { - return 1; -} - -static ssize_t fixed_length_data_source_read_callback( - nghttp2_session *session _U_, int32_t stream_id _U_, uint8_t *buf _U_, - size_t len, uint32_t *data_flags, nghttp2_data_source *source _U_, - void *user_data) { - my_user_data *ud = (my_user_data *)user_data; - size_t wlen; - if (len < ud->data_source_length) { - wlen = len; - } else { - wlen = ud->data_source_length; - } - ud->data_source_length -= wlen; - if (ud->data_source_length == 0) { - *data_flags |= NGHTTP2_DATA_FLAG_EOF; - } - return wlen; -} - -static ssize_t temporal_failure_data_source_read_callback( - nghttp2_session *session _U_, int32_t stream_id _U_, uint8_t *buf _U_, - size_t len _U_, uint32_t *data_flags _U_, nghttp2_data_source *source _U_, - void *user_data _U_) { - return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; -} - -static ssize_t fail_data_source_read_callback(nghttp2_session *session _U_, - int32_t stream_id _U_, - uint8_t *buf _U_, size_t len _U_, - uint32_t *data_flags _U_, - nghttp2_data_source *source _U_, - void *user_data _U_) { - return NGHTTP2_ERR_CALLBACK_FAILURE; -} - -static ssize_t no_end_stream_data_source_read_callback( - nghttp2_session *session _U_, int32_t stream_id _U_, uint8_t *buf _U_, - size_t len _U_, uint32_t *data_flags, nghttp2_data_source *source _U_, - void *user_data _U_) { - *data_flags |= NGHTTP2_DATA_FLAG_EOF | NGHTTP2_DATA_FLAG_NO_END_STREAM; - return 0; -} - -static ssize_t no_copy_data_source_read_callback( - nghttp2_session *session _U_, int32_t stream_id _U_, uint8_t *buf _U_, - size_t len, uint32_t *data_flags, nghttp2_data_source *source _U_, - void *user_data) { - my_user_data *ud = (my_user_data *)user_data; - size_t wlen; - if (len < ud->data_source_length) { - wlen = len; - } else { - wlen = ud->data_source_length; - } - - ud->data_source_length -= wlen; - - *data_flags |= NGHTTP2_DATA_FLAG_NO_COPY; - - if (ud->data_source_length == 0) { - *data_flags |= NGHTTP2_DATA_FLAG_EOF; - } - return wlen; -} - -static int send_data_callback(nghttp2_session *session _U_, - nghttp2_frame *frame, const uint8_t *framehd, - size_t length, nghttp2_data_source *source _U_, - void *user_data) { - accumulator *acc = ((my_user_data *)user_data)->acc; - - memcpy(acc->buf + acc->length, framehd, NGHTTP2_FRAME_HDLEN); - acc->length += NGHTTP2_FRAME_HDLEN; - - if (frame->data.padlen) { - *(acc->buf + acc->length++) = frame->data.padlen - 1; - } - - acc->length += length; - - if (frame->data.padlen) { - acc->length += frame->data.padlen - 1; - } - - return 0; -} - -/* static void no_stream_user_data_stream_close_callback */ -/* (nghttp2_session *session, */ -/* int32_t stream_id, */ -/* nghttp2_error_code error_code, */ -/* void *user_data) */ -/* { */ -/* my_user_data* my_data = (my_user_data*)user_data; */ -/* ++my_data->stream_close_cb_called; */ -/* } */ - -static ssize_t block_count_send_callback(nghttp2_session *session _U_, - const uint8_t *data _U_, size_t len, - int flags _U_, void *user_data) { - my_user_data *ud = (my_user_data *)user_data; - ssize_t r; - if (ud->block_count == 0) { - r = NGHTTP2_ERR_WOULDBLOCK; - } else { - --ud->block_count; - r = len; - } - return r; -} - -static int on_header_callback(nghttp2_session *session _U_, - const nghttp2_frame *frame, const uint8_t *name, - size_t namelen, const uint8_t *value, - size_t valuelen, uint8_t flags _U_, - void *user_data) { - my_user_data *ud = (my_user_data *)user_data; - ++ud->header_cb_called; - ud->nv.name = (uint8_t *)name; - ud->nv.namelen = namelen; - ud->nv.value = (uint8_t *)value; - ud->nv.valuelen = valuelen; - - ud->frame = frame; - return 0; -} - -static int pause_on_header_callback(nghttp2_session *session, - const nghttp2_frame *frame, - const uint8_t *name, size_t namelen, - const uint8_t *value, size_t valuelen, - uint8_t flags, void *user_data) { - on_header_callback(session, frame, name, namelen, value, valuelen, flags, - user_data); - return NGHTTP2_ERR_PAUSE; -} - -static int temporal_failure_on_header_callback( - nghttp2_session *session, const nghttp2_frame *frame, const uint8_t *name, - size_t namelen, const uint8_t *value, size_t valuelen, uint8_t flags, - void *user_data) { - on_header_callback(session, frame, name, namelen, value, valuelen, flags, - user_data); - return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; -} - -static int on_begin_headers_callback(nghttp2_session *session _U_, - const nghttp2_frame *frame _U_, - void *user_data) { - my_user_data *ud = (my_user_data *)user_data; - ++ud->begin_headers_cb_called; - return 0; -} - -static int temporal_failure_on_begin_headers_callback( - nghttp2_session *session, const nghttp2_frame *frame, void *user_data) { - on_begin_headers_callback(session, frame, user_data); - return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; -} - -static ssize_t defer_data_source_read_callback(nghttp2_session *session _U_, - int32_t stream_id _U_, - uint8_t *buf _U_, size_t len _U_, - uint32_t *data_flags _U_, - nghttp2_data_source *source _U_, - void *user_data _U_) { - return NGHTTP2_ERR_DEFERRED; -} - -static int on_stream_close_callback(nghttp2_session *session _U_, - int32_t stream_id _U_, - nghttp2_error_code error_code _U_, - void *user_data) { - my_user_data *my_data = (my_user_data *)user_data; - ++my_data->stream_close_cb_called; - my_data->stream_close_error_code = error_code; - - return 0; -} - -static nghttp2_settings_entry *dup_iv(const nghttp2_settings_entry *iv, - size_t niv) { - return nghttp2_frame_iv_copy(iv, niv, nghttp2_mem_default()); -} - -static nghttp2_priority_spec pri_spec_default = {0, NGHTTP2_DEFAULT_WEIGHT, 0}; - -void test_nghttp2_session_recv(void) { - nghttp2_session *session; - nghttp2_session_callbacks callbacks; - scripted_data_feed df; - my_user_data user_data; - nghttp2_bufs bufs; - ssize_t framelen; - nghttp2_frame frame; - ssize_t i; - nghttp2_outbound_item *item; - nghttp2_nv *nva; - ssize_t nvlen; - nghttp2_hd_deflater deflater; - int rv; - nghttp2_mem *mem; - - mem = nghttp2_mem_default(); - frame_pack_bufs_init(&bufs); - - memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); - callbacks.send_callback = null_send_callback; - callbacks.recv_callback = scripted_recv_callback; - callbacks.on_frame_recv_callback = on_frame_recv_callback; - callbacks.on_begin_frame_callback = on_begin_frame_callback; - - user_data.df = &df; - - nghttp2_session_server_new(&session, &callbacks, &user_data); - nghttp2_hd_deflate_init(&deflater, mem); - - nvlen = ARRLEN(reqnv); - nghttp2_nv_array_copy(&nva, reqnv, nvlen, mem); - nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_END_HEADERS, 1, - NGHTTP2_HCAT_HEADERS, NULL, nva, nvlen); - rv = nghttp2_frame_pack_headers(&bufs, &frame.headers, &deflater); - - CU_ASSERT(0 == rv); - - scripted_data_feed_init2(&df, &bufs); - - framelen = nghttp2_bufs_len(&bufs); - - /* Send 1 byte per each read */ - for (i = 0; i < framelen; ++i) { - df.feedseq[i] = 1; - } - - nghttp2_frame_headers_free(&frame.headers, mem); - - user_data.frame_recv_cb_called = 0; - user_data.begin_frame_cb_called = 0; - - while ((ssize_t)df.seqidx < framelen) { - CU_ASSERT(0 == nghttp2_session_recv(session)); - } - CU_ASSERT(1 == user_data.frame_recv_cb_called); - CU_ASSERT(1 == user_data.begin_frame_cb_called); - - nghttp2_bufs_reset(&bufs); - - /* Receive PRIORITY */ - nghttp2_frame_priority_init(&frame.priority, 5, &pri_spec_default); - - rv = nghttp2_frame_pack_priority(&bufs, &frame.priority); - - CU_ASSERT(0 == rv); - - nghttp2_frame_priority_free(&frame.priority); - - scripted_data_feed_init2(&df, &bufs); - - user_data.frame_recv_cb_called = 0; - user_data.begin_frame_cb_called = 0; - - CU_ASSERT(0 == nghttp2_session_recv(session)); - CU_ASSERT(1 == user_data.frame_recv_cb_called); - CU_ASSERT(1 == user_data.begin_frame_cb_called); - - nghttp2_bufs_reset(&bufs); - - nghttp2_hd_deflate_free(&deflater); - nghttp2_session_del(session); - - /* Some tests for frame too large */ - nghttp2_session_server_new(&session, &callbacks, &user_data); - - /* Receive PING with too large payload */ - nghttp2_frame_ping_init(&frame.ping, NGHTTP2_FLAG_NONE, NULL); - - rv = nghttp2_frame_pack_ping(&bufs, &frame.ping); - - CU_ASSERT(0 == rv); - - /* Add extra 16 bytes */ - nghttp2_bufs_seek_last_present(&bufs); - assert(nghttp2_buf_len(&bufs.cur->buf) >= 16); - - bufs.cur->buf.last += 16; - nghttp2_put_uint32be( - bufs.cur->buf.pos, - (uint32_t)(((frame.hd.length + 16) << 8) + bufs.cur->buf.pos[3])); - - nghttp2_frame_ping_free(&frame.ping); - - scripted_data_feed_init2(&df, &bufs); - user_data.frame_recv_cb_called = 0; - user_data.begin_frame_cb_called = 0; - - CU_ASSERT(0 == nghttp2_session_recv(session)); - CU_ASSERT(0 == user_data.frame_recv_cb_called); - CU_ASSERT(0 == user_data.begin_frame_cb_called); - - item = nghttp2_session_get_next_ob_item(session); - CU_ASSERT(NGHTTP2_GOAWAY == item->frame.hd.type); - CU_ASSERT(NGHTTP2_FRAME_SIZE_ERROR == item->frame.goaway.error_code); - CU_ASSERT(0 == nghttp2_session_send(session)); - - nghttp2_bufs_free(&bufs); - nghttp2_session_del(session); -} - -void test_nghttp2_session_recv_invalid_stream_id(void) { - nghttp2_session *session; - nghttp2_session_callbacks callbacks; - scripted_data_feed df; - my_user_data user_data; - nghttp2_bufs bufs; - nghttp2_frame frame; - nghttp2_hd_deflater deflater; - int rv; - nghttp2_mem *mem; - nghttp2_nv *nva; - size_t nvlen; - - mem = nghttp2_mem_default(); - frame_pack_bufs_init(&bufs); - - memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); - callbacks.recv_callback = scripted_recv_callback; - callbacks.on_invalid_frame_recv_callback = on_invalid_frame_recv_callback; - - user_data.df = &df; - user_data.invalid_frame_recv_cb_called = 0; - nghttp2_session_server_new(&session, &callbacks, &user_data); - nghttp2_hd_deflate_init(&deflater, mem); - - nvlen = ARRLEN(reqnv); - nghttp2_nv_array_copy(&nva, reqnv, nvlen, mem); - nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_END_HEADERS, 2, - NGHTTP2_HCAT_HEADERS, NULL, nva, nvlen); - rv = nghttp2_frame_pack_headers(&bufs, &frame.headers, &deflater); - - CU_ASSERT(0 == rv); - CU_ASSERT(nghttp2_bufs_len(&bufs) > 0); - - scripted_data_feed_init2(&df, &bufs); - nghttp2_frame_headers_free(&frame.headers, mem); - - CU_ASSERT(0 == nghttp2_session_recv(session)); - CU_ASSERT(1 == user_data.invalid_frame_recv_cb_called); - - nghttp2_bufs_free(&bufs); - nghttp2_hd_deflate_free(&deflater); - nghttp2_session_del(session); -} - -void test_nghttp2_session_recv_invalid_frame(void) { - nghttp2_session *session; - nghttp2_session_callbacks callbacks; - scripted_data_feed df; - my_user_data user_data; - nghttp2_bufs bufs; - nghttp2_frame frame; - nghttp2_nv *nva; - ssize_t nvlen; - nghttp2_hd_deflater deflater; - int rv; - nghttp2_mem *mem; - - mem = nghttp2_mem_default(); - frame_pack_bufs_init(&bufs); - - memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); - callbacks.recv_callback = scripted_recv_callback; - callbacks.send_callback = null_send_callback; - callbacks.on_frame_send_callback = on_frame_send_callback; - - user_data.df = &df; - user_data.frame_send_cb_called = 0; - nghttp2_session_server_new(&session, &callbacks, &user_data); - nghttp2_hd_deflate_init(&deflater, mem); - nvlen = ARRLEN(reqnv); - nghttp2_nv_array_copy(&nva, reqnv, nvlen, mem); - nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_END_HEADERS, 1, - NGHTTP2_HCAT_HEADERS, NULL, nva, nvlen); - rv = nghttp2_frame_pack_headers(&bufs, &frame.headers, &deflater); - - CU_ASSERT(0 == rv); - CU_ASSERT(nghttp2_bufs_len(&bufs) > 0); - - scripted_data_feed_init2(&df, &bufs); - - CU_ASSERT(0 == nghttp2_session_recv(session)); - CU_ASSERT(0 == nghttp2_session_send(session)); - CU_ASSERT(0 == user_data.frame_send_cb_called); - - /* Receive exactly same bytes of HEADERS is treated as error, because it has - * pseudo headers and without END_STREAM flag set */ - scripted_data_feed_init2(&df, &bufs); - - CU_ASSERT(0 == nghttp2_session_recv(session)); - CU_ASSERT(0 == nghttp2_session_send(session)); - CU_ASSERT(1 == user_data.frame_send_cb_called); - CU_ASSERT(NGHTTP2_RST_STREAM == user_data.sent_frame_type); - - nghttp2_bufs_free(&bufs); - nghttp2_frame_headers_free(&frame.headers, mem); - - nghttp2_hd_deflate_free(&deflater); - nghttp2_session_del(session); -} - -void test_nghttp2_session_recv_eof(void) { - nghttp2_session *session; - nghttp2_session_callbacks callbacks; - - memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); - callbacks.send_callback = null_send_callback; - callbacks.recv_callback = eof_recv_callback; - - nghttp2_session_client_new(&session, &callbacks, NULL); - CU_ASSERT(NGHTTP2_ERR_EOF == nghttp2_session_recv(session)); - - nghttp2_session_del(session); -} - -void test_nghttp2_session_recv_data(void) { - nghttp2_session *session; - nghttp2_session_callbacks callbacks; - my_user_data ud; - uint8_t data[8092]; - ssize_t rv; - nghttp2_outbound_item *item; - nghttp2_stream *stream; - nghttp2_frame_hd hd; - int i; - - memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); - callbacks.send_callback = null_send_callback; - callbacks.on_data_chunk_recv_callback = on_data_chunk_recv_callback; - callbacks.on_frame_recv_callback = on_frame_recv_callback; - - nghttp2_session_client_new(&session, &callbacks, &ud); - - /* Create DATA frame with length 4KiB */ - memset(data, 0, sizeof(data)); - hd.length = 4096; - hd.type = NGHTTP2_DATA; - hd.flags = NGHTTP2_FLAG_NONE; - hd.stream_id = 1; - nghttp2_frame_pack_frame_hd(data, &hd); - - /* stream 1 is not opened, so it must be responded with connection - error. This is not mandated by the spec */ - ud.data_chunk_recv_cb_called = 0; - ud.frame_recv_cb_called = 0; - rv = nghttp2_session_mem_recv(session, data, NGHTTP2_FRAME_HDLEN + 4096); - CU_ASSERT(NGHTTP2_FRAME_HDLEN + 4096 == rv); - - CU_ASSERT(0 == ud.data_chunk_recv_cb_called); - CU_ASSERT(0 == ud.frame_recv_cb_called); - item = nghttp2_session_get_next_ob_item(session); - CU_ASSERT(NGHTTP2_GOAWAY == item->frame.hd.type); - - nghttp2_session_del(session); - - nghttp2_session_client_new(&session, &callbacks, &ud); - - /* Create stream 1 with CLOSING state. DATA is ignored. */ - stream = nghttp2_session_open_stream(session, 1, NGHTTP2_STREAM_FLAG_NONE, - &pri_spec_default, - NGHTTP2_STREAM_CLOSING, NULL); - /* Set initial window size 16383 to check stream flow control, - isolating it from the conneciton flow control */ - stream->local_window_size = 16383; - - ud.data_chunk_recv_cb_called = 0; - ud.frame_recv_cb_called = 0; - rv = nghttp2_session_mem_recv(session, data, NGHTTP2_FRAME_HDLEN + 4096); - CU_ASSERT(NGHTTP2_FRAME_HDLEN + 4096 == rv); - - CU_ASSERT(0 == ud.data_chunk_recv_cb_called); - CU_ASSERT(0 == ud.frame_recv_cb_called); - item = nghttp2_session_get_next_ob_item(session); - CU_ASSERT(NULL == item); - - /* This is normal case. DATA is acceptable. */ - stream->state = NGHTTP2_STREAM_OPENED; - - ud.data_chunk_recv_cb_called = 0; - ud.frame_recv_cb_called = 0; - rv = nghttp2_session_mem_recv(session, data, NGHTTP2_FRAME_HDLEN + 4096); - CU_ASSERT(NGHTTP2_FRAME_HDLEN + 4096 == rv); - - CU_ASSERT(1 == ud.data_chunk_recv_cb_called); - CU_ASSERT(1 == ud.frame_recv_cb_called); - - CU_ASSERT(NULL == nghttp2_session_get_next_ob_item(session)); - - ud.data_chunk_recv_cb_called = 0; - ud.frame_recv_cb_called = 0; - rv = nghttp2_session_mem_recv(session, data, NGHTTP2_FRAME_HDLEN + 4096); - CU_ASSERT(NGHTTP2_FRAME_HDLEN + 4096 == rv); - - /* Now we got data more than initial-window-size / 2, WINDOW_UPDATE - must be queued */ - CU_ASSERT(1 == ud.data_chunk_recv_cb_called); - CU_ASSERT(1 == ud.frame_recv_cb_called); - item = nghttp2_session_get_next_ob_item(session); - CU_ASSERT(NGHTTP2_WINDOW_UPDATE == item->frame.hd.type); - CU_ASSERT(1 == item->frame.window_update.hd.stream_id); - CU_ASSERT(0 == nghttp2_session_send(session)); - - /* Set initial window size to 1MiB, so that we can check connection - flow control individually */ - stream->local_window_size = 1 << 20; - /* Connection flow control takes into account DATA which is received - in the error condition. We have received 4096 * 4 bytes of - DATA. Additional 4 DATA frames, connection flow control will kick - in. */ - for (i = 0; i < 5; ++i) { - rv = nghttp2_session_mem_recv(session, data, NGHTTP2_FRAME_HDLEN + 4096); - CU_ASSERT(NGHTTP2_FRAME_HDLEN + 4096 == rv); - } - item = nghttp2_session_get_next_ob_item(session); - CU_ASSERT(NGHTTP2_WINDOW_UPDATE == item->frame.hd.type); - CU_ASSERT(0 == item->frame.window_update.hd.stream_id); - CU_ASSERT(0 == nghttp2_session_send(session)); - - /* Reception of DATA with stream ID = 0 causes connection error */ - hd.length = 4096; - hd.type = NGHTTP2_DATA; - hd.flags = NGHTTP2_FLAG_NONE; - hd.stream_id = 0; - nghttp2_frame_pack_frame_hd(data, &hd); - - ud.data_chunk_recv_cb_called = 0; - ud.frame_recv_cb_called = 0; - rv = nghttp2_session_mem_recv(session, data, NGHTTP2_FRAME_HDLEN + 4096); - CU_ASSERT(NGHTTP2_FRAME_HDLEN + 4096 == rv); - - CU_ASSERT(0 == ud.data_chunk_recv_cb_called); - CU_ASSERT(0 == ud.frame_recv_cb_called); - item = nghttp2_session_get_next_ob_item(session); - CU_ASSERT(NGHTTP2_GOAWAY == item->frame.hd.type); - CU_ASSERT(NGHTTP2_PROTOCOL_ERROR == item->frame.goaway.error_code); - - nghttp2_session_del(session); -} - -void test_nghttp2_session_recv_continuation(void) { - nghttp2_session *session; - nghttp2_session_callbacks callbacks; - nghttp2_nv *nva; - size_t nvlen; - nghttp2_frame frame; - nghttp2_bufs bufs; - nghttp2_buf *buf; - ssize_t rv; - my_user_data ud; - nghttp2_hd_deflater deflater; - uint8_t data[1024]; - size_t datalen; - nghttp2_frame_hd cont_hd; - nghttp2_priority_spec pri_spec; - nghttp2_mem *mem; - - mem = nghttp2_mem_default(); - frame_pack_bufs_init(&bufs); - - memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); - callbacks.on_header_callback = on_header_callback; - callbacks.on_begin_headers_callback = on_begin_headers_callback; - callbacks.on_begin_frame_callback = on_begin_frame_callback; - - nghttp2_session_server_new(&session, &callbacks, &ud); - - nghttp2_hd_deflate_init(&deflater, mem); - - /* Make 1 HEADERS and insert CONTINUATION header */ - nvlen = ARRLEN(reqnv); - nghttp2_nv_array_copy(&nva, reqnv, nvlen, mem); - nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_NONE, 1, - NGHTTP2_HCAT_HEADERS, NULL, nva, nvlen); - rv = nghttp2_frame_pack_headers(&bufs, &frame.headers, &deflater); - - CU_ASSERT(0 == rv); - CU_ASSERT(nghttp2_bufs_len(&bufs) > 0); - - /* make sure that all data is in the first buf */ - buf = &bufs.head->buf; - assert(nghttp2_bufs_len(&bufs) == nghttp2_buf_len(buf)); - - nghttp2_frame_headers_free(&frame.headers, mem); - - /* HEADERS's payload is 1 byte */ - memcpy(data, buf->pos, NGHTTP2_FRAME_HDLEN + 1); - datalen = NGHTTP2_FRAME_HDLEN + 1; - buf->pos += NGHTTP2_FRAME_HDLEN + 1; - - nghttp2_put_uint32be(data, (1 << 8) + data[3]); - - /* First CONTINUATION, 2 bytes */ - nghttp2_frame_hd_init(&cont_hd, 2, NGHTTP2_CONTINUATION, NGHTTP2_FLAG_NONE, - 1); - - nghttp2_frame_pack_frame_hd(data + datalen, &cont_hd); - datalen += NGHTTP2_FRAME_HDLEN; - - memcpy(data + datalen, buf->pos, cont_hd.length); - datalen += cont_hd.length; - buf->pos += cont_hd.length; - - /* Second CONTINUATION, rest of the bytes */ - nghttp2_frame_hd_init(&cont_hd, nghttp2_buf_len(buf), NGHTTP2_CONTINUATION, - NGHTTP2_FLAG_END_HEADERS, 1); - - nghttp2_frame_pack_frame_hd(data + datalen, &cont_hd); - datalen += NGHTTP2_FRAME_HDLEN; - - memcpy(data + datalen, buf->pos, cont_hd.length); - datalen += cont_hd.length; - buf->pos += cont_hd.length; - - CU_ASSERT(0 == nghttp2_buf_len(buf)); - - ud.header_cb_called = 0; - ud.begin_frame_cb_called = 0; - - rv = nghttp2_session_mem_recv(session, data, datalen); - CU_ASSERT((ssize_t)datalen == rv); - CU_ASSERT(4 == ud.header_cb_called); - CU_ASSERT(3 == ud.begin_frame_cb_called); - - nghttp2_hd_deflate_free(&deflater); - nghttp2_session_del(session); - - /* Expecting CONTINUATION, but get the other frame */ - nghttp2_session_server_new(&session, &callbacks, &ud); - - nghttp2_hd_deflate_init(&deflater, mem); - - /* HEADERS without END_HEADERS flag */ - nvlen = ARRLEN(reqnv); - nghttp2_nv_array_copy(&nva, reqnv, nvlen, mem); - nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_NONE, 1, - NGHTTP2_HCAT_HEADERS, NULL, nva, nvlen); - nghttp2_bufs_reset(&bufs); - rv = nghttp2_frame_pack_headers(&bufs, &frame.headers, &deflater); - - CU_ASSERT(0 == rv); - CU_ASSERT(nghttp2_bufs_len(&bufs) > 0); - - nghttp2_frame_headers_free(&frame.headers, mem); - - /* make sure that all data is in the first buf */ - buf = &bufs.head->buf; - assert(nghttp2_bufs_len(&bufs) == nghttp2_buf_len(buf)); - - memcpy(data, buf->pos, nghttp2_buf_len(buf)); - datalen = nghttp2_buf_len(buf); - - /* Followed by PRIORITY */ - nghttp2_priority_spec_default_init(&pri_spec); - - nghttp2_frame_priority_init(&frame.priority, 1, &pri_spec); - nghttp2_bufs_reset(&bufs); - - rv = nghttp2_frame_pack_priority(&bufs, &frame.priority); - - CU_ASSERT(0 == rv); - CU_ASSERT(nghttp2_bufs_len(&bufs) > 0); - - memcpy(data + datalen, buf->pos, nghttp2_buf_len(buf)); - datalen += nghttp2_buf_len(buf); - - ud.begin_headers_cb_called = 0; - rv = nghttp2_session_mem_recv(session, data, datalen); - CU_ASSERT((ssize_t)datalen == rv); - - CU_ASSERT(1 == ud.begin_headers_cb_called); - CU_ASSERT(NGHTTP2_GOAWAY == - nghttp2_session_get_next_ob_item(session)->frame.hd.type); - - nghttp2_bufs_free(&bufs); - nghttp2_hd_deflate_free(&deflater); - nghttp2_session_del(session); -} - -void test_nghttp2_session_recv_headers_with_priority(void) { - nghttp2_session *session; - nghttp2_session_callbacks callbacks; - nghttp2_nv *nva; - size_t nvlen; - nghttp2_frame frame; - nghttp2_bufs bufs; - nghttp2_buf *buf; - ssize_t rv; - my_user_data ud; - nghttp2_hd_deflater deflater; - nghttp2_outbound_item *item; - nghttp2_priority_spec pri_spec; - nghttp2_stream *stream; - nghttp2_mem *mem; - - mem = nghttp2_mem_default(); - frame_pack_bufs_init(&bufs); - - memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); - callbacks.on_frame_recv_callback = on_frame_recv_callback; - - nghttp2_session_server_new(&session, &callbacks, &ud); - - nghttp2_hd_deflate_init(&deflater, mem); - - open_stream(session, 1); - - /* With NGHTTP2_FLAG_PRIORITY without exclusive flag set */ - nvlen = ARRLEN(reqnv); - nghttp2_nv_array_copy(&nva, reqnv, nvlen, mem); - - nghttp2_priority_spec_init(&pri_spec, 1, 99, 0); - - nghttp2_frame_headers_init(&frame.headers, - NGHTTP2_FLAG_END_HEADERS | NGHTTP2_FLAG_PRIORITY, - 3, NGHTTP2_HCAT_HEADERS, &pri_spec, nva, nvlen); - - rv = nghttp2_frame_pack_headers(&bufs, &frame.headers, &deflater); - - CU_ASSERT(0 == rv); - CU_ASSERT(nghttp2_bufs_len(&bufs) > 0); - - nghttp2_frame_headers_free(&frame.headers, mem); - - buf = &bufs.head->buf; - assert(nghttp2_bufs_len(&bufs) == nghttp2_buf_len(buf)); - - ud.frame_recv_cb_called = 0; - - rv = nghttp2_session_mem_recv(session, buf->pos, nghttp2_buf_len(buf)); - - CU_ASSERT(nghttp2_buf_len(buf) == rv); - CU_ASSERT(1 == ud.frame_recv_cb_called); - - stream = nghttp2_session_get_stream(session, 3); - - CU_ASSERT(99 == stream->weight); - CU_ASSERT(1 == stream->dep_prev->stream_id); - - nghttp2_bufs_reset(&bufs); - - /* With NGHTTP2_FLAG_PRIORITY, but cut last 1 byte to make it - invalid. */ - nvlen = ARRLEN(reqnv); - nghttp2_nv_array_copy(&nva, reqnv, nvlen, mem); - - nghttp2_priority_spec_init(&pri_spec, 0, 99, 0); - - nghttp2_frame_headers_init(&frame.headers, - NGHTTP2_FLAG_END_HEADERS | NGHTTP2_FLAG_PRIORITY, - 5, NGHTTP2_HCAT_HEADERS, &pri_spec, nva, nvlen); - - rv = nghttp2_frame_pack_headers(&bufs, &frame.headers, &deflater); - - CU_ASSERT(0 == rv); - CU_ASSERT(nghttp2_bufs_len(&bufs) > NGHTTP2_FRAME_HDLEN + 5); - - nghttp2_frame_headers_free(&frame.headers, mem); - - buf = &bufs.head->buf; - /* Make payload shorter than required length to store priority - group */ - nghttp2_put_uint32be(buf->pos, (4 << 8) + buf->pos[3]); - - ud.frame_recv_cb_called = 0; - - rv = nghttp2_session_mem_recv(session, buf->pos, nghttp2_buf_len(buf)); - - CU_ASSERT(nghttp2_buf_len(buf) == rv); - CU_ASSERT(0 == ud.frame_recv_cb_called); - - stream = nghttp2_session_get_stream(session, 5); - - CU_ASSERT(NULL == stream); - - item = nghttp2_session_get_next_ob_item(session); - CU_ASSERT(NULL != item); - CU_ASSERT(NGHTTP2_GOAWAY == item->frame.hd.type); - CU_ASSERT(NGHTTP2_FRAME_SIZE_ERROR == item->frame.goaway.error_code); - - nghttp2_bufs_reset(&bufs); - - nghttp2_hd_deflate_free(&deflater); - nghttp2_session_del(session); - - /* Check dep_stream_id == stream_id */ - nghttp2_session_server_new(&session, &callbacks, &ud); - - nghttp2_hd_deflate_init(&deflater, mem); - - nvlen = ARRLEN(reqnv); - nghttp2_nv_array_copy(&nva, reqnv, nvlen, mem); - - nghttp2_priority_spec_init(&pri_spec, 1, 0, 0); - - nghttp2_frame_headers_init(&frame.headers, - NGHTTP2_FLAG_END_HEADERS | NGHTTP2_FLAG_PRIORITY, - 1, NGHTTP2_HCAT_HEADERS, &pri_spec, nva, nvlen); - - rv = nghttp2_frame_pack_headers(&bufs, &frame.headers, &deflater); - - CU_ASSERT(0 == rv); - CU_ASSERT(nghttp2_bufs_len(&bufs) > 0); - - nghttp2_frame_headers_free(&frame.headers, mem); - - buf = &bufs.head->buf; - assert(nghttp2_bufs_len(&bufs) == nghttp2_buf_len(buf)); - - ud.frame_recv_cb_called = 0; - - rv = nghttp2_session_mem_recv(session, buf->pos, nghttp2_buf_len(buf)); - - CU_ASSERT(nghttp2_buf_len(buf) == rv); - CU_ASSERT(0 == ud.frame_recv_cb_called); - - stream = nghttp2_session_get_stream(session, 1); - - CU_ASSERT(NULL == stream); - - item = nghttp2_session_get_next_ob_item(session); - CU_ASSERT(NULL != item); - CU_ASSERT(NGHTTP2_GOAWAY == item->frame.hd.type); - CU_ASSERT(NGHTTP2_PROTOCOL_ERROR == item->frame.goaway.error_code); - - nghttp2_bufs_reset(&bufs); - - nghttp2_bufs_free(&bufs); - nghttp2_hd_deflate_free(&deflater); - nghttp2_session_del(session); -} - -void test_nghttp2_session_recv_premature_headers(void) { - nghttp2_session *session; - nghttp2_session_callbacks callbacks; - nghttp2_bufs bufs; - nghttp2_buf *buf; - ssize_t rv; - my_user_data ud; - nghttp2_hd_deflater deflater; - nghttp2_outbound_item *item; - nghttp2_mem *mem; - uint32_t payloadlen; - - mem = nghttp2_mem_default(); - frame_pack_bufs_init(&bufs); - - memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); - callbacks.send_callback = null_send_callback; - - nghttp2_session_server_new(&session, &callbacks, &ud); - - nghttp2_hd_deflate_init(&deflater, mem); - - rv = pack_headers(&bufs, &deflater, 1, NGHTTP2_FLAG_END_HEADERS, reqnv, - ARRLEN(reqnv), mem); - - buf = &bufs.head->buf; - /* Intentionally feed payload cutting last 1 byte off */ - payloadlen = nghttp2_get_uint32(buf->pos) >> 8; - nghttp2_put_uint32be(buf->pos, ((payloadlen - 1) << 8) + buf->pos[3]); - rv = nghttp2_session_mem_recv(session, buf->pos, nghttp2_buf_len(buf) - 1); - - CU_ASSERT(rv == nghttp2_buf_len(buf) - 1); - - item = nghttp2_session_get_next_ob_item(session); - - CU_ASSERT(NULL != item); - CU_ASSERT(NGHTTP2_RST_STREAM == item->frame.hd.type); - CU_ASSERT(NGHTTP2_COMPRESSION_ERROR == item->frame.rst_stream.error_code); - CU_ASSERT(1 == item->frame.hd.stream_id); - CU_ASSERT(0 == nghttp2_session_send(session)); - - nghttp2_bufs_reset(&bufs); - nghttp2_hd_deflate_free(&deflater); - nghttp2_session_del(session); - - /* Test for PUSH_PROMISE */ - nghttp2_session_client_new(&session, &callbacks, &ud); - nghttp2_hd_deflate_init(&deflater, mem); - - nghttp2_session_open_stream(session, 1, NGHTTP2_STREAM_FLAG_NONE, - &pri_spec_default, NGHTTP2_STREAM_OPENING, NULL); - - rv = pack_push_promise(&bufs, &deflater, 1, NGHTTP2_FLAG_END_HEADERS, 2, - reqnv, ARRLEN(reqnv), mem); - - CU_ASSERT(0 == rv); - - buf = &bufs.head->buf; - payloadlen = nghttp2_get_uint32(buf->pos) >> 8; - /* Intentionally feed payload cutting last 1 byte off */ - nghttp2_put_uint32be(buf->pos, ((payloadlen - 1) << 8) + buf->pos[3]); - rv = nghttp2_session_mem_recv(session, buf->pos, nghttp2_buf_len(buf) - 1); - - CU_ASSERT(rv == nghttp2_buf_len(buf) - 1); - - item = nghttp2_session_get_next_ob_item(session); - - CU_ASSERT(NULL != item); - CU_ASSERT(NGHTTP2_RST_STREAM == item->frame.hd.type); - CU_ASSERT(NGHTTP2_COMPRESSION_ERROR == item->frame.rst_stream.error_code); - CU_ASSERT(2 == item->frame.hd.stream_id); - CU_ASSERT(0 == nghttp2_session_send(session)); - - nghttp2_hd_deflate_free(&deflater); - nghttp2_session_del(session); - nghttp2_bufs_free(&bufs); -} - -void test_nghttp2_session_recv_unknown_frame(void) { - nghttp2_session *session; - nghttp2_session_callbacks callbacks; - my_user_data ud; - uint8_t data[16384]; - size_t datalen; - nghttp2_frame_hd hd; - ssize_t rv; - - nghttp2_frame_hd_init(&hd, 16000, 99, NGHTTP2_FLAG_NONE, 0); - - nghttp2_frame_pack_frame_hd(data, &hd); - datalen = NGHTTP2_FRAME_HDLEN + hd.length; - - memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); - callbacks.on_frame_recv_callback = on_frame_recv_callback; - - nghttp2_session_server_new(&session, &callbacks, &ud); - - ud.frame_recv_cb_called = 0; - - /* Unknown frame must be ignored */ - rv = nghttp2_session_mem_recv(session, data, datalen); - - CU_ASSERT(rv == (ssize_t)datalen); - CU_ASSERT(0 == ud.frame_recv_cb_called); - CU_ASSERT(NULL == nghttp2_session_get_next_ob_item(session)); - - nghttp2_session_del(session); -} - -void test_nghttp2_session_recv_unexpected_continuation(void) { - nghttp2_session *session; - nghttp2_session_callbacks callbacks; - my_user_data ud; - uint8_t data[16384]; - size_t datalen; - nghttp2_frame_hd hd; - ssize_t rv; - nghttp2_outbound_item *item; - - nghttp2_frame_hd_init(&hd, 16000, NGHTTP2_CONTINUATION, - NGHTTP2_FLAG_END_HEADERS, 1); - - nghttp2_frame_pack_frame_hd(data, &hd); - datalen = NGHTTP2_FRAME_HDLEN + hd.length; - - memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); - callbacks.on_frame_recv_callback = on_frame_recv_callback; - - nghttp2_session_server_new(&session, &callbacks, &ud); - - open_stream(session, 1); - - ud.frame_recv_cb_called = 0; - - /* unexpected CONTINUATION must be treated as connection error */ - rv = nghttp2_session_mem_recv(session, data, datalen); - - CU_ASSERT(rv == (ssize_t)datalen); - CU_ASSERT(0 == ud.frame_recv_cb_called); - - item = nghttp2_session_get_next_ob_item(session); - - CU_ASSERT(NGHTTP2_GOAWAY == item->frame.hd.type); - - nghttp2_session_del(session); -} - -void test_nghttp2_session_recv_settings_header_table_size(void) { - nghttp2_session *session; - nghttp2_session_callbacks callbacks; - nghttp2_frame frame; - nghttp2_bufs bufs; - nghttp2_buf *buf; - ssize_t rv; - my_user_data ud; - nghttp2_settings_entry iv[3]; - nghttp2_nv nv = MAKE_NV(":authority", "example.org"); - nghttp2_mem *mem; - - mem = nghttp2_mem_default(); - frame_pack_bufs_init(&bufs); - - memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); - callbacks.on_frame_recv_callback = on_frame_recv_callback; - callbacks.send_callback = null_send_callback; - - nghttp2_session_client_new(&session, &callbacks, &ud); - - iv[0].settings_id = NGHTTP2_SETTINGS_HEADER_TABLE_SIZE; - iv[0].value = 3000; - - iv[1].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE; - iv[1].value = 16384; - - nghttp2_frame_settings_init(&frame.settings, NGHTTP2_FLAG_NONE, dup_iv(iv, 2), - 2); - - rv = nghttp2_frame_pack_settings(&bufs, &frame.settings); - - CU_ASSERT(0 == rv); - CU_ASSERT(nghttp2_bufs_len(&bufs) > 0); - - nghttp2_frame_settings_free(&frame.settings, mem); - - buf = &bufs.head->buf; - assert(nghttp2_bufs_len(&bufs) == nghttp2_buf_len(buf)); - - ud.frame_recv_cb_called = 0; - - rv = nghttp2_session_mem_recv(session, buf->pos, nghttp2_buf_len(buf)); - - CU_ASSERT(rv == nghttp2_buf_len(buf)); - CU_ASSERT(1 == ud.frame_recv_cb_called); - - CU_ASSERT(3000 == session->remote_settings.header_table_size); - CU_ASSERT(16384 == session->remote_settings.initial_window_size); - - nghttp2_bufs_reset(&bufs); - - /* 2 SETTINGS_HEADER_TABLE_SIZE */ - iv[0].settings_id = NGHTTP2_SETTINGS_HEADER_TABLE_SIZE; - iv[0].value = 3001; - - iv[1].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE; - iv[1].value = 16383; - - iv[2].settings_id = NGHTTP2_SETTINGS_HEADER_TABLE_SIZE; - iv[2].value = 3001; - - nghttp2_frame_settings_init(&frame.settings, NGHTTP2_FLAG_NONE, dup_iv(iv, 3), - 3); - - rv = nghttp2_frame_pack_settings(&bufs, &frame.settings); - - CU_ASSERT(0 == rv); - CU_ASSERT(nghttp2_bufs_len(&bufs) > 0); - - nghttp2_frame_settings_free(&frame.settings, mem); - - buf = &bufs.head->buf; - assert(nghttp2_bufs_len(&bufs) == nghttp2_buf_len(buf)); - - ud.frame_recv_cb_called = 0; - - rv = nghttp2_session_mem_recv(session, buf->pos, nghttp2_buf_len(buf)); - - CU_ASSERT(rv == nghttp2_buf_len(buf)); - CU_ASSERT(1 == ud.frame_recv_cb_called); - - CU_ASSERT(3001 == session->remote_settings.header_table_size); - CU_ASSERT(16383 == session->remote_settings.initial_window_size); - - nghttp2_bufs_reset(&bufs); - - /* 2 SETTINGS_HEADER_TABLE_SIZE; first entry clears dynamic header - table. */ - - nghttp2_submit_request(session, NULL, &nv, 1, NULL, NULL); - nghttp2_session_send(session); - - CU_ASSERT(0 < session->hd_deflater.ctx.hd_table.len); - - iv[0].settings_id = NGHTTP2_SETTINGS_HEADER_TABLE_SIZE; - iv[0].value = 0; - - iv[1].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE; - iv[1].value = 16382; - - iv[2].settings_id = NGHTTP2_SETTINGS_HEADER_TABLE_SIZE; - iv[2].value = 4096; - - nghttp2_frame_settings_init(&frame.settings, NGHTTP2_FLAG_NONE, dup_iv(iv, 3), - 3); - - rv = nghttp2_frame_pack_settings(&bufs, &frame.settings); - - CU_ASSERT(0 == rv); - CU_ASSERT(nghttp2_bufs_len(&bufs) > 0); - - nghttp2_frame_settings_free(&frame.settings, mem); - - buf = &bufs.head->buf; - assert(nghttp2_bufs_len(&bufs) == nghttp2_buf_len(buf)); - - ud.frame_recv_cb_called = 0; - - rv = nghttp2_session_mem_recv(session, buf->pos, nghttp2_buf_len(buf)); - - CU_ASSERT(rv == nghttp2_buf_len(buf)); - CU_ASSERT(1 == ud.frame_recv_cb_called); - - CU_ASSERT(4096 == session->remote_settings.header_table_size); - CU_ASSERT(16382 == session->remote_settings.initial_window_size); - CU_ASSERT(0 == session->hd_deflater.ctx.hd_table.len); - - nghttp2_bufs_reset(&bufs); - - /* 2 SETTINGS_HEADER_TABLE_SIZE; second entry clears dynamic header - table. */ - - nghttp2_submit_request(session, NULL, &nv, 1, NULL, NULL); - nghttp2_session_send(session); - - CU_ASSERT(0 < session->hd_deflater.ctx.hd_table.len); - - iv[0].settings_id = NGHTTP2_SETTINGS_HEADER_TABLE_SIZE; - iv[0].value = 3000; - - iv[1].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE; - iv[1].value = 16381; - - iv[2].settings_id = NGHTTP2_SETTINGS_HEADER_TABLE_SIZE; - iv[2].value = 0; - - nghttp2_frame_settings_init(&frame.settings, NGHTTP2_FLAG_NONE, dup_iv(iv, 3), - 3); - - rv = nghttp2_frame_pack_settings(&bufs, &frame.settings); - - CU_ASSERT(0 == rv); - CU_ASSERT(nghttp2_bufs_len(&bufs) > 0); - - nghttp2_frame_settings_free(&frame.settings, mem); - - buf = &bufs.head->buf; - assert(nghttp2_bufs_len(&bufs) == nghttp2_buf_len(buf)); - - ud.frame_recv_cb_called = 0; - - rv = nghttp2_session_mem_recv(session, buf->pos, nghttp2_buf_len(buf)); - - CU_ASSERT(rv == nghttp2_buf_len(buf)); - CU_ASSERT(1 == ud.frame_recv_cb_called); - - CU_ASSERT(0 == session->remote_settings.header_table_size); - CU_ASSERT(16381 == session->remote_settings.initial_window_size); - CU_ASSERT(0 == session->hd_deflater.ctx.hd_table.len); - - nghttp2_bufs_reset(&bufs); - - nghttp2_bufs_free(&bufs); - nghttp2_session_del(session); -} - -void test_nghttp2_session_recv_too_large_frame_length(void) { - nghttp2_session *session; - nghttp2_session_callbacks callbacks; - uint8_t buf[NGHTTP2_FRAME_HDLEN]; - nghttp2_outbound_item *item; - nghttp2_frame_hd hd; - - /* Initial max frame size is NGHTTP2_MAX_FRAME_SIZE_MIN */ - nghttp2_frame_hd_init(&hd, NGHTTP2_MAX_FRAME_SIZE_MIN + 1, NGHTTP2_HEADERS, - NGHTTP2_FLAG_NONE, 1); - - memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); - - nghttp2_session_server_new(&session, &callbacks, NULL); - - nghttp2_frame_pack_frame_hd(buf, &hd); - - CU_ASSERT(sizeof(buf) == nghttp2_session_mem_recv(session, buf, sizeof(buf))); - - item = nghttp2_session_get_next_ob_item(session); - - CU_ASSERT(item != NULL); - CU_ASSERT(NGHTTP2_GOAWAY == item->frame.hd.type); - - nghttp2_session_del(session); -} - -void test_nghttp2_session_continue(void) { - nghttp2_session *session; - nghttp2_session_callbacks callbacks; - my_user_data user_data; - const nghttp2_nv nv1[] = {MAKE_NV(":method", "GET"), MAKE_NV(":path", "/")}; - const nghttp2_nv nv2[] = {MAKE_NV("user-agent", "nghttp2/1.0.0"), - MAKE_NV("alpha", "bravo")}; - nghttp2_bufs bufs; - nghttp2_buf *buf; - size_t framelen1, framelen2; - ssize_t rv; - uint8_t buffer[4096]; - nghttp2_buf databuf; - nghttp2_frame frame; - nghttp2_nv *nva; - ssize_t nvlen; - const nghttp2_frame *recv_frame; - nghttp2_frame_hd data_hd; - nghttp2_hd_deflater deflater; - nghttp2_mem *mem; - - mem = nghttp2_mem_default(); - frame_pack_bufs_init(&bufs); - nghttp2_buf_wrap_init(&databuf, buffer, sizeof(buffer)); - - memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); - callbacks.send_callback = null_send_callback; - callbacks.on_frame_recv_callback = on_frame_recv_callback; - callbacks.on_data_chunk_recv_callback = pause_on_data_chunk_recv_callback; - callbacks.on_header_callback = pause_on_header_callback; - callbacks.on_begin_headers_callback = on_begin_headers_callback; - - nghttp2_session_server_new(&session, &callbacks, &user_data); - /* disable strict HTTP layering checks */ - session->opt_flags |= NGHTTP2_OPTMASK_NO_HTTP_MESSAGING; - - nghttp2_hd_deflate_init(&deflater, mem); - - /* Make 2 HEADERS frames */ - nvlen = ARRLEN(nv1); - nghttp2_nv_array_copy(&nva, nv1, nvlen, mem); - nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_END_HEADERS, 1, - NGHTTP2_HCAT_HEADERS, NULL, nva, nvlen); - rv = nghttp2_frame_pack_headers(&bufs, &frame.headers, &deflater); - - CU_ASSERT(0 == rv); - CU_ASSERT(nghttp2_bufs_len(&bufs) > 0); - - nghttp2_frame_headers_free(&frame.headers, mem); - - buf = &bufs.head->buf; - assert(nghttp2_bufs_len(&bufs) == nghttp2_buf_len(buf)); - - framelen1 = nghttp2_buf_len(buf); - databuf.last = nghttp2_cpymem(databuf.last, buf->pos, nghttp2_buf_len(buf)); - - nvlen = ARRLEN(nv2); - nghttp2_nv_array_copy(&nva, nv2, nvlen, mem); - nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_END_HEADERS, 3, - NGHTTP2_HCAT_HEADERS, NULL, nva, nvlen); - nghttp2_bufs_reset(&bufs); - rv = nghttp2_frame_pack_headers(&bufs, &frame.headers, &deflater); - - CU_ASSERT(0 == rv); - CU_ASSERT(nghttp2_bufs_len(&bufs) > 0); - - nghttp2_frame_headers_free(&frame.headers, mem); - - assert(nghttp2_bufs_len(&bufs) == nghttp2_buf_len(buf)); - - framelen2 = nghttp2_buf_len(buf); - databuf.last = nghttp2_cpymem(databuf.last, buf->pos, nghttp2_buf_len(buf)); - - /* Receive 1st HEADERS and pause */ - user_data.begin_headers_cb_called = 0; - user_data.header_cb_called = 0; - rv = - nghttp2_session_mem_recv(session, databuf.pos, nghttp2_buf_len(&databuf)); - - CU_ASSERT(rv >= 0); - databuf.pos += rv; - - recv_frame = user_data.frame; - CU_ASSERT(NGHTTP2_HEADERS == recv_frame->hd.type); - CU_ASSERT(framelen1 - NGHTTP2_FRAME_HDLEN == recv_frame->hd.length); - - CU_ASSERT(1 == user_data.begin_headers_cb_called); - CU_ASSERT(1 == user_data.header_cb_called); - - CU_ASSERT(nghttp2_nv_equal(&nv1[0], &user_data.nv)); - - /* get 2nd header field */ - user_data.begin_headers_cb_called = 0; - user_data.header_cb_called = 0; - rv = - nghttp2_session_mem_recv(session, databuf.pos, nghttp2_buf_len(&databuf)); - - CU_ASSERT(rv >= 0); - databuf.pos += rv; - - CU_ASSERT(0 == user_data.begin_headers_cb_called); - CU_ASSERT(1 == user_data.header_cb_called); - - CU_ASSERT(nghttp2_nv_equal(&nv1[1], &user_data.nv)); - - /* will call end_headers_callback and receive 2nd HEADERS and pause */ - user_data.begin_headers_cb_called = 0; - user_data.header_cb_called = 0; - rv = - nghttp2_session_mem_recv(session, databuf.pos, nghttp2_buf_len(&databuf)); - - CU_ASSERT(rv >= 0); - databuf.pos += rv; - - recv_frame = user_data.frame; - CU_ASSERT(NGHTTP2_HEADERS == recv_frame->hd.type); - CU_ASSERT(framelen2 - NGHTTP2_FRAME_HDLEN == recv_frame->hd.length); - - CU_ASSERT(1 == user_data.begin_headers_cb_called); - CU_ASSERT(1 == user_data.header_cb_called); - - CU_ASSERT(nghttp2_nv_equal(&nv2[0], &user_data.nv)); - - /* get 2nd header field */ - user_data.begin_headers_cb_called = 0; - user_data.header_cb_called = 0; - rv = - nghttp2_session_mem_recv(session, databuf.pos, nghttp2_buf_len(&databuf)); - - CU_ASSERT(rv >= 0); - databuf.pos += rv; - - CU_ASSERT(0 == user_data.begin_headers_cb_called); - CU_ASSERT(1 == user_data.header_cb_called); - - CU_ASSERT(nghttp2_nv_equal(&nv2[1], &user_data.nv)); - - /* No input data, frame_recv_callback is called */ - user_data.begin_headers_cb_called = 0; - user_data.header_cb_called = 0; - user_data.frame_recv_cb_called = 0; - rv = - nghttp2_session_mem_recv(session, databuf.pos, nghttp2_buf_len(&databuf)); - - CU_ASSERT(rv >= 0); - databuf.pos += rv; - - CU_ASSERT(0 == user_data.begin_headers_cb_called); - CU_ASSERT(0 == user_data.header_cb_called); - CU_ASSERT(1 == user_data.frame_recv_cb_called); - - /* Receive DATA */ - nghttp2_frame_hd_init(&data_hd, 16, NGHTTP2_DATA, NGHTTP2_FLAG_NONE, 1); - - nghttp2_buf_reset(&databuf); - nghttp2_frame_pack_frame_hd(databuf.pos, &data_hd); - - /* Intentionally specify larger buffer size to see pause is kicked - in. */ - databuf.last = databuf.end; - - user_data.frame_recv_cb_called = 0; - rv = - nghttp2_session_mem_recv(session, databuf.pos, nghttp2_buf_len(&databuf)); - - CU_ASSERT(16 + NGHTTP2_FRAME_HDLEN == rv); - CU_ASSERT(0 == user_data.frame_recv_cb_called); - - /* Next nghttp2_session_mem_recv invokes on_frame_recv_callback and - pause again in on_data_chunk_recv_callback since we pass same - DATA frame. */ - user_data.frame_recv_cb_called = 0; - rv = - nghttp2_session_mem_recv(session, databuf.pos, nghttp2_buf_len(&databuf)); - CU_ASSERT(16 + NGHTTP2_FRAME_HDLEN == rv); - CU_ASSERT(1 == user_data.frame_recv_cb_called); - - /* And finally call on_frame_recv_callback with 0 size input */ - user_data.frame_recv_cb_called = 0; - rv = nghttp2_session_mem_recv(session, NULL, 0); - CU_ASSERT(0 == rv); - CU_ASSERT(1 == user_data.frame_recv_cb_called); - - nghttp2_bufs_free(&bufs); - nghttp2_hd_deflate_free(&deflater); - nghttp2_session_del(session); -} - -void test_nghttp2_session_add_frame(void) { - nghttp2_session *session; - nghttp2_session_callbacks callbacks; - accumulator acc; - my_user_data user_data; - nghttp2_outbound_item *item; - nghttp2_frame *frame; - nghttp2_nv *nva; - ssize_t nvlen; - nghttp2_mem *mem; - - mem = nghttp2_mem_default(); - memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); - callbacks.send_callback = accumulator_send_callback; - - acc.length = 0; - user_data.acc = &acc; - - CU_ASSERT(0 == nghttp2_session_client_new(&session, &callbacks, &user_data)); - - item = mem->malloc(sizeof(nghttp2_outbound_item), NULL); - - nghttp2_outbound_item_init(item); - - frame = &item->frame; - - nvlen = ARRLEN(reqnv); - nghttp2_nv_array_copy(&nva, reqnv, nvlen, mem); - - nghttp2_frame_headers_init( - &frame->headers, NGHTTP2_FLAG_END_HEADERS | NGHTTP2_FLAG_PRIORITY, - session->next_stream_id, NGHTTP2_HCAT_REQUEST, NULL, nva, nvlen); - - session->next_stream_id += 2; - - CU_ASSERT(0 == nghttp2_session_add_item(session, item)); - CU_ASSERT(NULL != nghttp2_outbound_queue_top(&session->ob_syn)); - CU_ASSERT(0 == nghttp2_session_send(session)); - CU_ASSERT(NGHTTP2_HEADERS == acc.buf[3]); - CU_ASSERT((NGHTTP2_FLAG_END_HEADERS | NGHTTP2_FLAG_PRIORITY) == acc.buf[4]); - /* check stream id */ - CU_ASSERT(1 == nghttp2_get_uint32(&acc.buf[5])); - - nghttp2_session_del(session); -} - -void test_nghttp2_session_on_request_headers_received(void) { - nghttp2_session *session; - nghttp2_session_callbacks callbacks; - my_user_data user_data; - nghttp2_frame frame; - nghttp2_stream *stream; - int32_t stream_id = 1; - nghttp2_nv malformed_nva[] = {MAKE_NV(":path", "\x01")}; - nghttp2_nv *nva; - size_t nvlen; - nghttp2_priority_spec pri_spec; - nghttp2_mem *mem; - - mem = nghttp2_mem_default(); - memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); - callbacks.on_begin_headers_callback = on_begin_headers_callback; - callbacks.on_invalid_frame_recv_callback = on_invalid_frame_recv_callback; - - nghttp2_session_server_new(&session, &callbacks, &user_data); - - nghttp2_priority_spec_init(&pri_spec, 0, 255, 0); - - nghttp2_frame_headers_init( - &frame.headers, NGHTTP2_FLAG_END_HEADERS | NGHTTP2_FLAG_PRIORITY, - stream_id, NGHTTP2_HCAT_REQUEST, &pri_spec, NULL, 0); - - user_data.begin_headers_cb_called = 0; - user_data.invalid_frame_recv_cb_called = 0; - - CU_ASSERT(0 == nghttp2_session_on_request_headers_received(session, &frame)); - CU_ASSERT(1 == user_data.begin_headers_cb_called); - stream = nghttp2_session_get_stream(session, stream_id); - CU_ASSERT(NGHTTP2_STREAM_OPENING == stream->state); - CU_ASSERT(255 == stream->weight); - - nghttp2_frame_headers_free(&frame.headers, mem); - - /* More than un-ACKed max concurrent streams leads REFUSED_STREAM */ - session->pending_local_max_concurrent_stream = 1; - nghttp2_frame_headers_init(&frame.headers, - NGHTTP2_FLAG_END_HEADERS | NGHTTP2_FLAG_PRIORITY, - 3, NGHTTP2_HCAT_HEADERS, NULL, NULL, 0); - user_data.invalid_frame_recv_cb_called = 0; - CU_ASSERT(NGHTTP2_ERR_IGN_HEADER_BLOCK == - nghttp2_session_on_request_headers_received(session, &frame)); - CU_ASSERT(1 == user_data.invalid_frame_recv_cb_called); - CU_ASSERT(0 == (session->goaway_flags & NGHTTP2_GOAWAY_TERM_ON_SEND)); - - nghttp2_frame_headers_free(&frame.headers, mem); - session->local_settings.max_concurrent_streams = - NGHTTP2_INITIAL_MAX_CONCURRENT_STREAMS; - - /* Stream ID less than or equal to the previouly received request - HEADERS is just ignored due to race condition */ - nghttp2_frame_headers_init(&frame.headers, - NGHTTP2_FLAG_END_HEADERS | NGHTTP2_FLAG_PRIORITY, - 3, NGHTTP2_HCAT_HEADERS, NULL, NULL, 0); - user_data.invalid_frame_recv_cb_called = 0; - CU_ASSERT(NGHTTP2_ERR_IGN_HEADER_BLOCK == - nghttp2_session_on_request_headers_received(session, &frame)); - CU_ASSERT(0 == user_data.invalid_frame_recv_cb_called); - CU_ASSERT(0 == (session->goaway_flags & NGHTTP2_GOAWAY_TERM_ON_SEND)); - - nghttp2_frame_headers_free(&frame.headers, mem); - - /* Stream ID is our side and it is idle stream ID, then treat it as - connection error */ - nghttp2_frame_headers_init(&frame.headers, - NGHTTP2_FLAG_END_HEADERS | NGHTTP2_FLAG_PRIORITY, - 2, NGHTTP2_HCAT_HEADERS, NULL, NULL, 0); - user_data.invalid_frame_recv_cb_called = 0; - CU_ASSERT(NGHTTP2_ERR_IGN_HEADER_BLOCK == - nghttp2_session_on_request_headers_received(session, &frame)); - CU_ASSERT(1 == user_data.invalid_frame_recv_cb_called); - CU_ASSERT(session->goaway_flags & NGHTTP2_GOAWAY_TERM_ON_SEND); - - nghttp2_frame_headers_free(&frame.headers, mem); - - nghttp2_session_del(session); - - /* Check malformed headers. The library accept it. */ - nghttp2_session_server_new(&session, &callbacks, &user_data); - - nvlen = ARRLEN(malformed_nva); - nghttp2_nv_array_copy(&nva, malformed_nva, nvlen, mem); - nghttp2_frame_headers_init(&frame.headers, - NGHTTP2_FLAG_END_HEADERS | NGHTTP2_FLAG_PRIORITY, - 1, NGHTTP2_HCAT_HEADERS, NULL, nva, nvlen); - user_data.begin_headers_cb_called = 0; - user_data.invalid_frame_recv_cb_called = 0; - CU_ASSERT(0 == nghttp2_session_on_request_headers_received(session, &frame)); - CU_ASSERT(1 == user_data.begin_headers_cb_called); - CU_ASSERT(0 == user_data.invalid_frame_recv_cb_called); - - nghttp2_frame_headers_free(&frame.headers, mem); - - nghttp2_session_del(session); - - /* Check client side */ - nghttp2_session_client_new(&session, &callbacks, &user_data); - - /* Receiving peer's idle stream ID is subject to connection error */ - nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_END_HEADERS, 2, - NGHTTP2_HCAT_REQUEST, NULL, NULL, 0); - - user_data.invalid_frame_recv_cb_called = 0; - CU_ASSERT(NGHTTP2_ERR_IGN_HEADER_BLOCK == - nghttp2_session_on_request_headers_received(session, &frame)); - CU_ASSERT(1 == user_data.invalid_frame_recv_cb_called); - CU_ASSERT(session->goaway_flags & NGHTTP2_GOAWAY_TERM_ON_SEND); - - nghttp2_frame_headers_free(&frame.headers, mem); - - nghttp2_session_del(session); - - nghttp2_session_client_new(&session, &callbacks, &user_data); - - /* Receiving our's idle stream ID is subject to connection error */ - nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_END_HEADERS, 1, - NGHTTP2_HCAT_REQUEST, NULL, NULL, 0); - - user_data.invalid_frame_recv_cb_called = 0; - CU_ASSERT(NGHTTP2_ERR_IGN_HEADER_BLOCK == - nghttp2_session_on_request_headers_received(session, &frame)); - CU_ASSERT(1 == user_data.invalid_frame_recv_cb_called); - CU_ASSERT(session->goaway_flags & NGHTTP2_GOAWAY_TERM_ON_SEND); - - nghttp2_frame_headers_free(&frame.headers, mem); - - nghttp2_session_del(session); - - nghttp2_session_client_new(&session, &callbacks, &user_data); - - session->next_stream_id = 5; - - /* Stream ID which is not idle and not in stream map is just - ignored */ - nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_END_HEADERS, 3, - NGHTTP2_HCAT_REQUEST, NULL, NULL, 0); - - user_data.invalid_frame_recv_cb_called = 0; - CU_ASSERT(NGHTTP2_ERR_IGN_HEADER_BLOCK == - nghttp2_session_on_request_headers_received(session, &frame)); - CU_ASSERT(0 == user_data.invalid_frame_recv_cb_called); - CU_ASSERT(0 == (session->goaway_flags & NGHTTP2_GOAWAY_TERM_ON_SEND)); - - nghttp2_frame_headers_free(&frame.headers, mem); - - nghttp2_session_del(session); - - nghttp2_session_server_new(&session, &callbacks, &user_data); - - /* Stream ID which is equal to local_last_stream_id is ok. */ - session->local_last_stream_id = 3; - - nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_END_HEADERS, 3, - NGHTTP2_HCAT_REQUEST, NULL, NULL, 0); - - CU_ASSERT(0 == nghttp2_session_on_request_headers_received(session, &frame)); - - nghttp2_frame_headers_free(&frame.headers, mem); - - /* If GOAWAY has been sent, new stream is ignored */ - nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_END_HEADERS, 5, - NGHTTP2_HCAT_REQUEST, NULL, NULL, 0); - - session->goaway_flags |= NGHTTP2_GOAWAY_SENT; - user_data.invalid_frame_recv_cb_called = 0; - CU_ASSERT(NGHTTP2_ERR_IGN_HEADER_BLOCK == - nghttp2_session_on_request_headers_received(session, &frame)); - CU_ASSERT(0 == user_data.invalid_frame_recv_cb_called); - CU_ASSERT(0 == (session->goaway_flags & NGHTTP2_GOAWAY_TERM_ON_SEND)); - - nghttp2_frame_headers_free(&frame.headers, mem); - - nghttp2_session_del(session); -} - -void test_nghttp2_session_on_response_headers_received(void) { - nghttp2_session *session; - nghttp2_session_callbacks callbacks; - my_user_data user_data; - nghttp2_frame frame; - nghttp2_stream *stream; - nghttp2_mem *mem; - - mem = nghttp2_mem_default(); - memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); - callbacks.on_begin_headers_callback = on_begin_headers_callback; - callbacks.on_invalid_frame_recv_callback = on_invalid_frame_recv_callback; - - nghttp2_session_client_new(&session, &callbacks, &user_data); - stream = nghttp2_session_open_stream(session, 1, NGHTTP2_STREAM_FLAG_NONE, - &pri_spec_default, - NGHTTP2_STREAM_OPENING, NULL); - nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_END_HEADERS, 1, - NGHTTP2_HCAT_HEADERS, NULL, NULL, 0); - - user_data.begin_headers_cb_called = 0; - user_data.invalid_frame_recv_cb_called = 0; - - CU_ASSERT(0 == nghttp2_session_on_response_headers_received(session, &frame, - stream)); - CU_ASSERT(1 == user_data.begin_headers_cb_called); - CU_ASSERT(NGHTTP2_STREAM_OPENED == stream->state); - - nghttp2_frame_headers_free(&frame.headers, mem); - nghttp2_session_del(session); -} - -void test_nghttp2_session_on_headers_received(void) { - nghttp2_session *session; - nghttp2_session_callbacks callbacks; - my_user_data user_data; - nghttp2_frame frame; - nghttp2_stream *stream; - nghttp2_mem *mem; - - mem = nghttp2_mem_default(); - memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); - callbacks.on_begin_headers_callback = on_begin_headers_callback; - callbacks.on_invalid_frame_recv_callback = on_invalid_frame_recv_callback; - - nghttp2_session_client_new(&session, &callbacks, &user_data); - stream = nghttp2_session_open_stream(session, 1, NGHTTP2_STREAM_FLAG_NONE, - &pri_spec_default, NGHTTP2_STREAM_OPENED, - NULL); - nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_WR); - nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_END_HEADERS, 1, - NGHTTP2_HCAT_HEADERS, NULL, NULL, 0); - - user_data.begin_headers_cb_called = 0; - user_data.invalid_frame_recv_cb_called = 0; - - CU_ASSERT(0 == nghttp2_session_on_headers_received(session, &frame, stream)); - CU_ASSERT(1 == user_data.begin_headers_cb_called); - CU_ASSERT(NGHTTP2_STREAM_OPENED == stream->state); - - /* stream closed */ - frame.hd.flags |= NGHTTP2_FLAG_END_STREAM; - - CU_ASSERT(0 == nghttp2_session_on_headers_received(session, &frame, stream)); - CU_ASSERT(2 == user_data.begin_headers_cb_called); - - /* Check to see when NGHTTP2_STREAM_CLOSING, incoming HEADERS is - discarded. */ - stream = nghttp2_session_open_stream(session, 3, NGHTTP2_STREAM_FLAG_NONE, - &pri_spec_default, - NGHTTP2_STREAM_CLOSING, NULL); - frame.hd.stream_id = 3; - frame.hd.flags = NGHTTP2_FLAG_END_HEADERS; - CU_ASSERT(NGHTTP2_ERR_IGN_HEADER_BLOCK == - nghttp2_session_on_headers_received(session, &frame, stream)); - /* See no counters are updated */ - CU_ASSERT(2 == user_data.begin_headers_cb_called); - CU_ASSERT(0 == user_data.invalid_frame_recv_cb_called); - - /* Server initiated stream */ - stream = nghttp2_session_open_stream(session, 2, NGHTTP2_STREAM_FLAG_NONE, - &pri_spec_default, - NGHTTP2_STREAM_OPENING, NULL); - - /* half closed (remote) */ - frame.hd.flags = NGHTTP2_FLAG_END_HEADERS | NGHTTP2_FLAG_END_STREAM; - frame.hd.stream_id = 2; - - CU_ASSERT(0 == nghttp2_session_on_headers_received(session, &frame, stream)); - CU_ASSERT(3 == user_data.begin_headers_cb_called); - CU_ASSERT(NGHTTP2_STREAM_OPENING == stream->state); - - nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_RD); - - /* Further reception of HEADERS is subject to stream error */ - CU_ASSERT(NGHTTP2_ERR_IGN_HEADER_BLOCK == - nghttp2_session_on_headers_received(session, &frame, stream)); - CU_ASSERT(1 == user_data.invalid_frame_recv_cb_called); - - nghttp2_frame_headers_free(&frame.headers, mem); - - nghttp2_session_del(session); -} - -void test_nghttp2_session_on_push_response_headers_received(void) { - nghttp2_session *session; - nghttp2_session_callbacks callbacks; - my_user_data user_data; - nghttp2_frame frame; - nghttp2_stream *stream; - nghttp2_outbound_item *item; - nghttp2_mem *mem; - - mem = nghttp2_mem_default(); - memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); - callbacks.send_callback = null_send_callback; - callbacks.on_begin_headers_callback = on_begin_headers_callback; - callbacks.on_invalid_frame_recv_callback = on_invalid_frame_recv_callback; - - nghttp2_session_client_new(&session, &callbacks, &user_data); - stream = nghttp2_session_open_stream(session, 2, NGHTTP2_STREAM_FLAG_NONE, - &pri_spec_default, - NGHTTP2_STREAM_RESERVED, NULL); - nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_END_HEADERS, 2, - NGHTTP2_HCAT_HEADERS, NULL, NULL, 0); - /* nghttp2_session_on_push_response_headers_received assumes - stream's state is NGHTTP2_STREAM_RESERVED and session->server is - 0. */ - - user_data.begin_headers_cb_called = 0; - user_data.invalid_frame_recv_cb_called = 0; - - CU_ASSERT(0 == nghttp2_session_on_push_response_headers_received( - session, &frame, stream)); - CU_ASSERT(1 == user_data.begin_headers_cb_called); - CU_ASSERT(NGHTTP2_STREAM_OPENED == stream->state); - CU_ASSERT(1 == session->num_incoming_streams); - CU_ASSERT(0 == (stream->flags & NGHTTP2_STREAM_FLAG_PUSH)); - - /* If un-ACKed max concurrent streams limit is exceeded, - RST_STREAMed */ - session->pending_local_max_concurrent_stream = 1; - stream = nghttp2_session_open_stream(session, 4, NGHTTP2_STREAM_FLAG_NONE, - &pri_spec_default, - NGHTTP2_STREAM_RESERVED, NULL); - frame.hd.stream_id = 4; - CU_ASSERT(NGHTTP2_ERR_IGN_HEADER_BLOCK == - nghttp2_session_on_push_response_headers_received(session, &frame, - stream)); - item = nghttp2_session_get_next_ob_item(session); - CU_ASSERT(NGHTTP2_RST_STREAM == item->frame.hd.type); - CU_ASSERT(NGHTTP2_REFUSED_STREAM == item->frame.rst_stream.error_code); - CU_ASSERT(1 == session->num_incoming_streams); - - CU_ASSERT(0 == nghttp2_session_send(session)); - CU_ASSERT(1 == session->num_incoming_streams); - - /* If ACKed max concurrent streams limit is exceeded, GOAWAY is - issued */ - session->local_settings.max_concurrent_streams = 1; - - stream = nghttp2_session_open_stream(session, 6, NGHTTP2_STREAM_FLAG_NONE, - &pri_spec_default, - NGHTTP2_STREAM_RESERVED, NULL); - frame.hd.stream_id = 6; - - CU_ASSERT(NGHTTP2_ERR_IGN_HEADER_BLOCK == - nghttp2_session_on_push_response_headers_received(session, &frame, - stream)); - item = nghttp2_session_get_next_ob_item(session); - CU_ASSERT(NGHTTP2_GOAWAY == item->frame.hd.type); - CU_ASSERT(NGHTTP2_PROTOCOL_ERROR == item->frame.goaway.error_code); - CU_ASSERT(1 == session->num_incoming_streams); - - nghttp2_frame_headers_free(&frame.headers, mem); - nghttp2_session_del(session); -} - -void test_nghttp2_session_on_priority_received(void) { - nghttp2_session *session; - nghttp2_session_callbacks callbacks; - my_user_data user_data; - nghttp2_frame frame; - nghttp2_stream *stream, *dep_stream; - nghttp2_priority_spec pri_spec; - nghttp2_outbound_item *item; - - memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); - callbacks.on_frame_recv_callback = on_frame_recv_callback; - callbacks.on_invalid_frame_recv_callback = on_invalid_frame_recv_callback; - - nghttp2_session_server_new(&session, &callbacks, &user_data); - stream = nghttp2_session_open_stream(session, 1, NGHTTP2_STREAM_FLAG_NONE, - &pri_spec_default, - NGHTTP2_STREAM_OPENING, NULL); - - nghttp2_priority_spec_init(&pri_spec, 0, 2, 0); - - nghttp2_frame_priority_init(&frame.priority, 1, &pri_spec); - - /* depend on stream 0 */ - CU_ASSERT(0 == nghttp2_session_on_priority_received(session, &frame)); - - CU_ASSERT(2 == stream->weight); - - stream = nghttp2_session_open_stream(session, 2, NGHTTP2_STREAM_FLAG_NONE, - &pri_spec_default, - NGHTTP2_STREAM_OPENING, NULL); - - dep_stream = nghttp2_session_open_stream(session, 3, NGHTTP2_STREAM_FLAG_NONE, - &pri_spec_default, - NGHTTP2_STREAM_OPENING, NULL); - - frame.hd.stream_id = 2; - - /* using dependency stream */ - nghttp2_priority_spec_init(&frame.priority.pri_spec, 3, 1, 0); - - CU_ASSERT(0 == nghttp2_session_on_priority_received(session, &frame)); - CU_ASSERT(dep_stream == stream->dep_prev); - - /* PRIORITY against idle stream */ - - frame.hd.stream_id = 100; - - CU_ASSERT(0 == nghttp2_session_on_priority_received(session, &frame)); - - stream = nghttp2_session_get_stream_raw(session, frame.hd.stream_id); - - CU_ASSERT(NGHTTP2_STREAM_IDLE == stream->state); - CU_ASSERT(dep_stream == stream->dep_prev); - - nghttp2_frame_priority_free(&frame.priority); - nghttp2_session_del(session); - - /* Check dep_stream_id == stream_id case */ - nghttp2_session_server_new(&session, &callbacks, &user_data); - nghttp2_session_open_stream(session, 1, NGHTTP2_STREAM_FLAG_NONE, - &pri_spec_default, NGHTTP2_STREAM_OPENED, NULL); - - nghttp2_priority_spec_init(&pri_spec, 1, 0, 0); - - nghttp2_frame_priority_init(&frame.priority, 1, &pri_spec); - - CU_ASSERT(0 == nghttp2_session_on_priority_received(session, &frame)); - - item = nghttp2_session_get_next_ob_item(session); - - CU_ASSERT(NGHTTP2_GOAWAY == item->frame.hd.type); - - nghttp2_frame_priority_free(&frame.priority); - nghttp2_session_del(session); -} - -void test_nghttp2_session_on_rst_stream_received(void) { - nghttp2_session *session; - nghttp2_session_callbacks callbacks; - my_user_data user_data; - nghttp2_frame frame; - memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); - nghttp2_session_server_new(&session, &callbacks, &user_data); - nghttp2_session_open_stream(session, 1, NGHTTP2_STREAM_FLAG_NONE, - &pri_spec_default, NGHTTP2_STREAM_OPENING, NULL); - - nghttp2_frame_rst_stream_init(&frame.rst_stream, 1, NGHTTP2_PROTOCOL_ERROR); - - CU_ASSERT(0 == nghttp2_session_on_rst_stream_received(session, &frame)); - CU_ASSERT(NULL == nghttp2_session_get_stream(session, 1)); - - nghttp2_frame_rst_stream_free(&frame.rst_stream); - nghttp2_session_del(session); -} - -void test_nghttp2_session_on_settings_received(void) { - nghttp2_session *session; - nghttp2_session_callbacks callbacks; - my_user_data user_data; - nghttp2_stream *stream1, *stream2; - nghttp2_frame frame; - const size_t niv = 5; - nghttp2_settings_entry iv[255]; - nghttp2_outbound_item *item; - nghttp2_nv nv = MAKE_NV(":authority", "example.org"); - nghttp2_mem *mem; - - mem = nghttp2_mem_default(); - - iv[0].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS; - iv[0].value = 50; - - iv[1].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS; - iv[1].value = 1000000009; - - iv[2].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE; - iv[2].value = 64 * 1024; - - iv[3].settings_id = NGHTTP2_SETTINGS_HEADER_TABLE_SIZE; - iv[3].value = 1024; - - iv[4].settings_id = NGHTTP2_SETTINGS_ENABLE_PUSH; - iv[4].value = 0; - - memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); - callbacks.send_callback = null_send_callback; - - nghttp2_session_client_new(&session, &callbacks, &user_data); - session->remote_settings.initial_window_size = 16 * 1024; - - stream1 = nghttp2_session_open_stream(session, 1, NGHTTP2_STREAM_FLAG_NONE, - &pri_spec_default, - NGHTTP2_STREAM_OPENING, NULL); - stream2 = nghttp2_session_open_stream(session, 2, NGHTTP2_STREAM_FLAG_NONE, - &pri_spec_default, - NGHTTP2_STREAM_OPENING, NULL); - /* Set window size for each streams and will see how settings - updates these values */ - stream1->remote_window_size = 16 * 1024; - stream2->remote_window_size = -48 * 1024; - - nghttp2_frame_settings_init(&frame.settings, NGHTTP2_FLAG_NONE, - dup_iv(iv, niv), niv); - - CU_ASSERT(0 == nghttp2_session_on_settings_received(session, &frame, 0)); - CU_ASSERT(1000000009 == session->remote_settings.max_concurrent_streams); - CU_ASSERT(64 * 1024 == session->remote_settings.initial_window_size); - CU_ASSERT(1024 == session->remote_settings.header_table_size); - CU_ASSERT(0 == session->remote_settings.enable_push); - - CU_ASSERT(64 * 1024 == stream1->remote_window_size); - CU_ASSERT(0 == stream2->remote_window_size); - - frame.settings.iv[2].value = 16 * 1024; - - CU_ASSERT(0 == nghttp2_session_on_settings_received(session, &frame, 0)); - - CU_ASSERT(16 * 1024 == stream1->remote_window_size); - CU_ASSERT(-48 * 1024 == stream2->remote_window_size); - - CU_ASSERT(16 * 1024 == nghttp2_session_get_stream_remote_window_size( - session, stream1->stream_id)); - CU_ASSERT(0 == nghttp2_session_get_stream_remote_window_size( - session, stream2->stream_id)); - - nghttp2_frame_settings_free(&frame.settings, mem); - - nghttp2_session_del(session); - - /* Check ACK with niv > 0 */ - nghttp2_session_server_new(&session, &callbacks, NULL); - nghttp2_frame_settings_init(&frame.settings, NGHTTP2_FLAG_ACK, dup_iv(iv, 1), - 1); - /* Specify inflight_iv deliberately */ - session->inflight_iv = frame.settings.iv; - session->inflight_niv = frame.settings.niv; - - CU_ASSERT(0 == nghttp2_session_on_settings_received(session, &frame, 0)); - item = nghttp2_session_get_next_ob_item(session); - CU_ASSERT(item != NULL); - CU_ASSERT(NGHTTP2_GOAWAY == item->frame.hd.type); - - session->inflight_iv = NULL; - session->inflight_niv = -1; - - nghttp2_frame_settings_free(&frame.settings, mem); - nghttp2_session_del(session); - - /* Check ACK against no inflight SETTINGS */ - nghttp2_session_server_new(&session, &callbacks, NULL); - nghttp2_frame_settings_init(&frame.settings, NGHTTP2_FLAG_ACK, NULL, 0); - - CU_ASSERT(0 == nghttp2_session_on_settings_received(session, &frame, 0)); - item = nghttp2_session_get_next_ob_item(session); - CU_ASSERT(item != NULL); - CU_ASSERT(NGHTTP2_GOAWAY == item->frame.hd.type); - - nghttp2_frame_settings_free(&frame.settings, mem); - nghttp2_session_del(session); - - /* Check that 2 SETTINGS_HEADER_TABLE_SIZE 0 and 4096 are included - and header table size is once cleared to 0. */ - nghttp2_session_client_new(&session, &callbacks, NULL); - - nghttp2_submit_request(session, NULL, &nv, 1, NULL, NULL); - - nghttp2_session_send(session); - - CU_ASSERT(session->hd_deflater.ctx.hd_table.len > 0); - - iv[0].settings_id = NGHTTP2_SETTINGS_HEADER_TABLE_SIZE; - iv[0].value = 0; - - iv[1].settings_id = NGHTTP2_SETTINGS_HEADER_TABLE_SIZE; - iv[1].value = 2048; - - nghttp2_frame_settings_init(&frame.settings, NGHTTP2_FLAG_NONE, dup_iv(iv, 2), - 2); - - CU_ASSERT(0 == nghttp2_session_on_settings_received(session, &frame, 0)); - - CU_ASSERT(0 == session->hd_deflater.ctx.hd_table.len); - CU_ASSERT(2048 == session->hd_deflater.ctx.hd_table_bufsize_max); - CU_ASSERT(2048 == session->remote_settings.header_table_size); - - nghttp2_frame_settings_free(&frame.settings, mem); - nghttp2_session_del(session); - - /* Check too large SETTINGS_MAX_FRAME_SIZE */ - nghttp2_session_server_new(&session, &callbacks, NULL); - - iv[0].settings_id = NGHTTP2_SETTINGS_MAX_FRAME_SIZE; - iv[0].value = NGHTTP2_MAX_FRAME_SIZE_MAX + 1; - - nghttp2_frame_settings_init(&frame.settings, NGHTTP2_FLAG_NONE, dup_iv(iv, 1), - 1); - - CU_ASSERT(0 == nghttp2_session_on_settings_received(session, &frame, 0)); - - item = nghttp2_session_get_next_ob_item(session); - - CU_ASSERT(item != NULL); - CU_ASSERT(NGHTTP2_GOAWAY == item->frame.hd.type); - - nghttp2_frame_settings_free(&frame.settings, mem); - nghttp2_session_del(session); -} - -void test_nghttp2_session_on_push_promise_received(void) { - nghttp2_session *session; - nghttp2_session_callbacks callbacks; - my_user_data user_data; - nghttp2_frame frame; - nghttp2_stream *stream, *promised_stream; - nghttp2_outbound_item *item; - nghttp2_nv malformed_nva[] = {MAKE_NV(":path", "\x01")}; - nghttp2_nv *nva; - size_t nvlen; - nghttp2_mem *mem; - nghttp2_settings_entry iv = {NGHTTP2_SETTINGS_ENABLE_PUSH, 0}; - - mem = nghttp2_mem_default(); - memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); - callbacks.send_callback = null_send_callback; - callbacks.on_begin_headers_callback = on_begin_headers_callback; - callbacks.on_invalid_frame_recv_callback = on_invalid_frame_recv_callback; - - nghttp2_session_client_new(&session, &callbacks, &user_data); - - stream = nghttp2_session_open_stream(session, 1, NGHTTP2_STREAM_FLAG_NONE, - &pri_spec_default, - NGHTTP2_STREAM_OPENING, NULL); - nghttp2_frame_push_promise_init(&frame.push_promise, NGHTTP2_FLAG_END_HEADERS, - 1, 2, NULL, 0); - - user_data.begin_headers_cb_called = 0; - user_data.invalid_frame_recv_cb_called = 0; - - CU_ASSERT(0 == nghttp2_session_on_push_promise_received(session, &frame)); - - CU_ASSERT(1 == user_data.begin_headers_cb_called); - promised_stream = nghttp2_session_get_stream(session, 2); - CU_ASSERT(NGHTTP2_STREAM_RESERVED == promised_stream->state); - CU_ASSERT(2 == session->last_recv_stream_id); - - /* Attempt to PUSH_PROMISE against half close (remote) */ - nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_RD); - frame.push_promise.promised_stream_id = 4; - - user_data.begin_headers_cb_called = 0; - user_data.invalid_frame_recv_cb_called = 0; - CU_ASSERT(NGHTTP2_ERR_IGN_HEADER_BLOCK == - nghttp2_session_on_push_promise_received(session, &frame)); - - CU_ASSERT(0 == user_data.begin_headers_cb_called); - CU_ASSERT(1 == user_data.invalid_frame_recv_cb_called); - CU_ASSERT(NULL == nghttp2_session_get_stream(session, 4)); - item = nghttp2_session_get_next_ob_item(session); - CU_ASSERT(NGHTTP2_RST_STREAM == item->frame.hd.type); - CU_ASSERT(4 == item->frame.hd.stream_id); - CU_ASSERT(NGHTTP2_PROTOCOL_ERROR == item->frame.rst_stream.error_code); - CU_ASSERT(0 == nghttp2_session_send(session)); - CU_ASSERT(4 == session->last_recv_stream_id); - - /* Attempt to PUSH_PROMISE against stream in closing state */ - stream->shut_flags = NGHTTP2_SHUT_NONE; - stream->state = NGHTTP2_STREAM_CLOSING; - frame.push_promise.promised_stream_id = 6; - - user_data.begin_headers_cb_called = 0; - user_data.invalid_frame_recv_cb_called = 0; - CU_ASSERT(NGHTTP2_ERR_IGN_HEADER_BLOCK == - nghttp2_session_on_push_promise_received(session, &frame)); - - CU_ASSERT(0 == user_data.begin_headers_cb_called); - CU_ASSERT(NULL == nghttp2_session_get_stream(session, 6)); - item = nghttp2_session_get_next_ob_item(session); - CU_ASSERT(NGHTTP2_RST_STREAM == item->frame.hd.type); - CU_ASSERT(6 == item->frame.hd.stream_id); - CU_ASSERT(NGHTTP2_REFUSED_STREAM == item->frame.rst_stream.error_code); - CU_ASSERT(0 == nghttp2_session_send(session)); - - /* Attempt to PUSH_PROMISE against non-existent stream */ - frame.hd.stream_id = 3; - frame.push_promise.promised_stream_id = 8; - - user_data.begin_headers_cb_called = 0; - user_data.invalid_frame_recv_cb_called = 0; - CU_ASSERT(NGHTTP2_ERR_IGN_HEADER_BLOCK == - nghttp2_session_on_push_promise_received(session, &frame)); - - CU_ASSERT(0 == user_data.begin_headers_cb_called); - CU_ASSERT(NULL == nghttp2_session_get_stream(session, 8)); - item = nghttp2_session_get_next_ob_item(session); - CU_ASSERT(NGHTTP2_GOAWAY == item->frame.hd.type); - CU_ASSERT(0 == item->frame.hd.stream_id); - CU_ASSERT(NGHTTP2_PROTOCOL_ERROR == item->frame.goaway.error_code); - CU_ASSERT(0 == nghttp2_session_send(session)); - - nghttp2_session_del(session); - - nghttp2_session_client_new(&session, &callbacks, &user_data); - - stream = nghttp2_session_open_stream(session, 1, NGHTTP2_STREAM_FLAG_NONE, - &pri_spec_default, - NGHTTP2_STREAM_OPENING, NULL); - - /* Same ID twice */ - stream->state = NGHTTP2_STREAM_OPENING; - - user_data.begin_headers_cb_called = 0; - user_data.invalid_frame_recv_cb_called = 0; - CU_ASSERT(NGHTTP2_ERR_IGN_HEADER_BLOCK == - nghttp2_session_on_push_promise_received(session, &frame)); - - CU_ASSERT(0 == user_data.begin_headers_cb_called); - CU_ASSERT(NULL == nghttp2_session_get_stream(session, 8)); - item = nghttp2_session_get_next_ob_item(session); - CU_ASSERT(NGHTTP2_GOAWAY == item->frame.hd.type); - CU_ASSERT(NGHTTP2_PROTOCOL_ERROR == item->frame.goaway.error_code); - CU_ASSERT(0 == nghttp2_session_send(session)); - - /* After GOAWAY, PUSH_PROMISE will be discarded */ - frame.push_promise.promised_stream_id = 10; - - user_data.begin_headers_cb_called = 0; - user_data.invalid_frame_recv_cb_called = 0; - CU_ASSERT(NGHTTP2_ERR_IGN_HEADER_BLOCK == - nghttp2_session_on_push_promise_received(session, &frame)); - - CU_ASSERT(0 == user_data.begin_headers_cb_called); - CU_ASSERT(NULL == nghttp2_session_get_stream(session, 10)); - CU_ASSERT(NULL == nghttp2_session_get_next_ob_item(session)); - - nghttp2_frame_push_promise_free(&frame.push_promise, mem); - nghttp2_session_del(session); - - nghttp2_session_client_new(&session, &callbacks, &user_data); - - nghttp2_session_open_stream(session, 2, NGHTTP2_STREAM_FLAG_NONE, - &pri_spec_default, NGHTTP2_STREAM_RESERVED, NULL); - /* Attempt to PUSH_PROMISE against reserved (remote) stream */ - nghttp2_frame_push_promise_init(&frame.push_promise, NGHTTP2_FLAG_END_HEADERS, - 2, 4, NULL, 0); - - user_data.begin_headers_cb_called = 0; - user_data.invalid_frame_recv_cb_called = 0; - CU_ASSERT(NGHTTP2_ERR_IGN_HEADER_BLOCK == - nghttp2_session_on_push_promise_received(session, &frame)); - - CU_ASSERT(0 == user_data.begin_headers_cb_called); - CU_ASSERT(1 == user_data.invalid_frame_recv_cb_called); - - nghttp2_frame_push_promise_free(&frame.push_promise, mem); - nghttp2_session_del(session); - - /* Disable PUSH */ - nghttp2_session_client_new(&session, &callbacks, &user_data); - - nghttp2_session_open_stream(session, 1, NGHTTP2_STREAM_FLAG_NONE, - &pri_spec_default, NGHTTP2_STREAM_OPENING, NULL); - - session->local_settings.enable_push = 0; - - nghttp2_frame_push_promise_init(&frame.push_promise, NGHTTP2_FLAG_END_HEADERS, - 1, 2, NULL, 0); - - user_data.begin_headers_cb_called = 0; - user_data.invalid_frame_recv_cb_called = 0; - CU_ASSERT(NGHTTP2_ERR_IGN_HEADER_BLOCK == - nghttp2_session_on_push_promise_received(session, &frame)); - - CU_ASSERT(0 == user_data.begin_headers_cb_called); - CU_ASSERT(1 == user_data.invalid_frame_recv_cb_called); - - nghttp2_frame_push_promise_free(&frame.push_promise, mem); - nghttp2_session_del(session); - - /* Check malformed headers. We accept malformed headers */ - nghttp2_session_client_new(&session, &callbacks, &user_data); - - nghttp2_session_open_stream(session, 1, NGHTTP2_STREAM_FLAG_NONE, - &pri_spec_default, NGHTTP2_STREAM_OPENING, NULL); - nvlen = ARRLEN(malformed_nva); - nghttp2_nv_array_copy(&nva, malformed_nva, nvlen, mem); - nghttp2_frame_push_promise_init(&frame.push_promise, NGHTTP2_FLAG_END_HEADERS, - 1, 2, nva, nvlen); - user_data.begin_headers_cb_called = 0; - user_data.invalid_frame_recv_cb_called = 0; - CU_ASSERT(0 == nghttp2_session_on_push_promise_received(session, &frame)); - - CU_ASSERT(1 == user_data.begin_headers_cb_called); - CU_ASSERT(0 == user_data.invalid_frame_recv_cb_called); - - nghttp2_frame_push_promise_free(&frame.push_promise, mem); - nghttp2_session_del(session); - - /* If local_settings.enable_push = 0 is pending, but not acked from - peer, incoming PUSH_PROMISE is rejected */ - nghttp2_session_client_new(&session, &callbacks, &user_data); - - nghttp2_session_open_stream(session, 1, NGHTTP2_STREAM_FLAG_NONE, - &pri_spec_default, NGHTTP2_STREAM_OPENING, NULL); - /* Submit settings with ENABLE_PUSH = 0 (thus disabling push) */ - nghttp2_submit_settings(session, NGHTTP2_FLAG_NONE, &iv, 1); - - nghttp2_frame_push_promise_init(&frame.push_promise, NGHTTP2_FLAG_END_HEADERS, - 1, 2, NULL, 0); - - CU_ASSERT(NGHTTP2_ERR_IGN_HEADER_BLOCK == - nghttp2_session_on_push_promise_received(session, &frame)); - - nghttp2_frame_push_promise_free(&frame.push_promise, mem); - nghttp2_session_del(session); -} - -void test_nghttp2_session_on_ping_received(void) { - nghttp2_session *session; - nghttp2_session_callbacks callbacks; - my_user_data user_data; - nghttp2_frame frame; - nghttp2_outbound_item *top; - const uint8_t opaque_data[] = "01234567"; - - user_data.frame_recv_cb_called = 0; - user_data.invalid_frame_recv_cb_called = 0; - - memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); - callbacks.on_frame_recv_callback = on_frame_recv_callback; - callbacks.on_invalid_frame_recv_callback = on_invalid_frame_recv_callback; - - nghttp2_session_client_new(&session, &callbacks, &user_data); - nghttp2_frame_ping_init(&frame.ping, NGHTTP2_FLAG_ACK, opaque_data); - - CU_ASSERT(0 == nghttp2_session_on_ping_received(session, &frame)); - CU_ASSERT(1 == user_data.frame_recv_cb_called); - - /* Since this ping frame has ACK flag set, no further action is - performed. */ - CU_ASSERT(NULL == nghttp2_outbound_queue_top(&session->ob_urgent)); - - /* Clear the flag, and receive it again */ - frame.hd.flags = NGHTTP2_FLAG_NONE; - - CU_ASSERT(0 == nghttp2_session_on_ping_received(session, &frame)); - CU_ASSERT(2 == user_data.frame_recv_cb_called); - top = nghttp2_outbound_queue_top(&session->ob_urgent); - CU_ASSERT(NGHTTP2_PING == top->frame.hd.type); - CU_ASSERT(NGHTTP2_FLAG_ACK == top->frame.hd.flags); - CU_ASSERT(memcmp(opaque_data, top->frame.ping.opaque_data, 8) == 0); - - nghttp2_frame_ping_free(&frame.ping); - nghttp2_session_del(session); -} - -void test_nghttp2_session_on_goaway_received(void) { - nghttp2_session *session; - nghttp2_session_callbacks callbacks; - my_user_data user_data; - nghttp2_frame frame; - int i; - nghttp2_mem *mem; - - mem = nghttp2_mem_default(); - user_data.frame_recv_cb_called = 0; - user_data.invalid_frame_recv_cb_called = 0; - - memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); - callbacks.on_frame_recv_callback = on_frame_recv_callback; - callbacks.on_invalid_frame_recv_callback = on_invalid_frame_recv_callback; - callbacks.on_stream_close_callback = on_stream_close_callback; - - nghttp2_session_client_new(&session, &callbacks, &user_data); - - for (i = 1; i <= 7; ++i) { - open_stream(session, i); - } - - nghttp2_frame_goaway_init(&frame.goaway, 3, NGHTTP2_PROTOCOL_ERROR, NULL, 0); - - user_data.stream_close_cb_called = 0; - - CU_ASSERT(0 == nghttp2_session_on_goaway_received(session, &frame)); - - CU_ASSERT(1 == user_data.frame_recv_cb_called); - CU_ASSERT(3 == session->remote_last_stream_id); - /* on_stream_close should be callsed for 2 times (stream 5 and 7) */ - CU_ASSERT(2 == user_data.stream_close_cb_called); - - CU_ASSERT(NULL != nghttp2_session_get_stream(session, 1)); - CU_ASSERT(NULL != nghttp2_session_get_stream(session, 2)); - CU_ASSERT(NULL != nghttp2_session_get_stream(session, 3)); - CU_ASSERT(NULL != nghttp2_session_get_stream(session, 4)); - CU_ASSERT(NULL == nghttp2_session_get_stream(session, 5)); - CU_ASSERT(NULL != nghttp2_session_get_stream(session, 6)); - CU_ASSERT(NULL == nghttp2_session_get_stream(session, 7)); - - nghttp2_frame_goaway_free(&frame.goaway, mem); - nghttp2_session_del(session); -} - -void test_nghttp2_session_on_window_update_received(void) { - nghttp2_session *session; - nghttp2_session_callbacks callbacks; - my_user_data user_data; - nghttp2_frame frame; - nghttp2_stream *stream; - nghttp2_outbound_item *data_item; - nghttp2_mem *mem; - - mem = nghttp2_mem_default(); - - memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); - callbacks.on_frame_recv_callback = on_frame_recv_callback; - callbacks.on_invalid_frame_recv_callback = on_invalid_frame_recv_callback; - user_data.frame_recv_cb_called = 0; - user_data.invalid_frame_recv_cb_called = 0; - - nghttp2_session_client_new(&session, &callbacks, &user_data); - - stream = nghttp2_session_open_stream(session, 1, NGHTTP2_STREAM_FLAG_NONE, - &pri_spec_default, NGHTTP2_STREAM_OPENED, - NULL); - - data_item = create_data_ob_item(mem); - - CU_ASSERT(0 == nghttp2_stream_attach_item(stream, data_item, session)); - - nghttp2_frame_window_update_init(&frame.window_update, NGHTTP2_FLAG_NONE, 1, - 16 * 1024); - - CU_ASSERT(0 == nghttp2_session_on_window_update_received(session, &frame)); - CU_ASSERT(1 == user_data.frame_recv_cb_called); - CU_ASSERT(NGHTTP2_INITIAL_WINDOW_SIZE + 16 * 1024 == - stream->remote_window_size); - - CU_ASSERT(0 == - nghttp2_stream_defer_item( - stream, NGHTTP2_STREAM_FLAG_DEFERRED_FLOW_CONTROL, session)); - - CU_ASSERT(0 == nghttp2_session_on_window_update_received(session, &frame)); - CU_ASSERT(2 == user_data.frame_recv_cb_called); - CU_ASSERT(NGHTTP2_INITIAL_WINDOW_SIZE + 16 * 1024 * 2 == - stream->remote_window_size); - CU_ASSERT(0 == (stream->flags & NGHTTP2_STREAM_FLAG_DEFERRED_ALL)); - - nghttp2_frame_window_update_free(&frame.window_update); - - /* Receiving WINDOW_UPDATE on reserved (remote) stream is a - connection error */ - nghttp2_session_open_stream(session, 2, NGHTTP2_STREAM_FLAG_NONE, - &pri_spec_default, NGHTTP2_STREAM_RESERVED, NULL); - - nghttp2_frame_window_update_init(&frame.window_update, NGHTTP2_FLAG_NONE, 2, - 4096); - - CU_ASSERT(!(session->goaway_flags & NGHTTP2_GOAWAY_TERM_ON_SEND)); - CU_ASSERT(0 == nghttp2_session_on_window_update_received(session, &frame)); - CU_ASSERT(session->goaway_flags & NGHTTP2_GOAWAY_TERM_ON_SEND); - - nghttp2_frame_window_update_free(&frame.window_update); - - nghttp2_session_del(session); - - /* Receiving WINDOW_UPDATE on reserved (local) stream is allowed */ - nghttp2_session_server_new(&session, &callbacks, &user_data); - - stream = nghttp2_session_open_stream(session, 2, NGHTTP2_STREAM_FLAG_NONE, - &pri_spec_default, - NGHTTP2_STREAM_RESERVED, NULL); - - nghttp2_frame_window_update_init(&frame.window_update, NGHTTP2_FLAG_NONE, 2, - 4096); - - CU_ASSERT(0 == nghttp2_session_on_window_update_received(session, &frame)); - CU_ASSERT(!(session->goaway_flags & NGHTTP2_GOAWAY_TERM_ON_SEND)); - - CU_ASSERT(NGHTTP2_INITIAL_WINDOW_SIZE + 4096 == stream->remote_window_size); - - nghttp2_frame_window_update_free(&frame.window_update); - - nghttp2_session_del(session); -} - -void test_nghttp2_session_on_data_received(void) { - nghttp2_session *session; - nghttp2_session_callbacks callbacks; - my_user_data user_data; - nghttp2_outbound_item *top; - nghttp2_stream *stream; - nghttp2_frame frame; - - memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); - - nghttp2_session_client_new(&session, &callbacks, &user_data); - stream = nghttp2_session_open_stream(session, 2, NGHTTP2_STREAM_FLAG_NONE, - &pri_spec_default, - NGHTTP2_STREAM_OPENING, NULL); - - nghttp2_frame_hd_init(&frame.hd, 4096, NGHTTP2_DATA, NGHTTP2_FLAG_NONE, 2); - - CU_ASSERT(0 == nghttp2_session_on_data_received(session, &frame)); - CU_ASSERT(0 == stream->shut_flags); - - frame.hd.flags = NGHTTP2_FLAG_END_STREAM; - - CU_ASSERT(0 == nghttp2_session_on_data_received(session, &frame)); - CU_ASSERT(NGHTTP2_SHUT_RD == stream->shut_flags); - - /* If NGHTTP2_STREAM_CLOSING state, DATA frame is discarded. */ - nghttp2_session_open_stream(session, 4, NGHTTP2_STREAM_FLAG_NONE, - &pri_spec_default, NGHTTP2_STREAM_CLOSING, NULL); - - frame.hd.flags = NGHTTP2_FLAG_NONE; - frame.hd.stream_id = 4; - - CU_ASSERT(0 == nghttp2_session_on_data_received(session, &frame)); - CU_ASSERT(NULL == nghttp2_outbound_queue_top(&session->ob_reg)); - - /* Check INVALID_STREAM case: DATA frame with stream ID which does - not exist. */ - - frame.hd.stream_id = 6; - - CU_ASSERT(0 == nghttp2_session_on_data_received(session, &frame)); - top = nghttp2_outbound_queue_top(&session->ob_reg); - /* DATA against nonexistent stream is just ignored for now */ - CU_ASSERT(top == NULL); - /* CU_ASSERT(NGHTTP2_RST_STREAM == top->frame.hd.type); */ - /* CU_ASSERT(NGHTTP2_PROTOCOL_ERROR == top->frame.rst_stream.error_code); */ - - nghttp2_session_del(session); -} - -void test_nghttp2_session_send_headers_start_stream(void) { - nghttp2_session *session; - nghttp2_session_callbacks callbacks; - nghttp2_outbound_item *item; - nghttp2_frame *frame; - nghttp2_stream *stream; - nghttp2_mem *mem; - - mem = nghttp2_mem_default(); - - memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); - callbacks.send_callback = null_send_callback; - - nghttp2_session_client_new(&session, &callbacks, NULL); - - item = mem->malloc(sizeof(nghttp2_outbound_item), NULL); - - nghttp2_outbound_item_init(item); - - frame = &item->frame; - - nghttp2_frame_headers_init(&frame->headers, NGHTTP2_FLAG_END_HEADERS, - session->next_stream_id, NGHTTP2_HCAT_REQUEST, - NULL, NULL, 0); - session->next_stream_id += 2; - - nghttp2_session_add_item(session, item); - CU_ASSERT(0 == nghttp2_session_send(session)); - stream = nghttp2_session_get_stream(session, 1); - CU_ASSERT(NGHTTP2_STREAM_OPENING == stream->state); - - nghttp2_session_del(session); -} - -void test_nghttp2_session_send_headers_reply(void) { - nghttp2_session *session; - nghttp2_session_callbacks callbacks; - nghttp2_outbound_item *item; - nghttp2_frame *frame; - nghttp2_stream *stream; - nghttp2_mem *mem; - - mem = nghttp2_mem_default(); - - memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); - callbacks.send_callback = null_send_callback; - - CU_ASSERT(0 == nghttp2_session_client_new(&session, &callbacks, NULL)); - nghttp2_session_open_stream(session, 2, NGHTTP2_STREAM_FLAG_NONE, - &pri_spec_default, NGHTTP2_STREAM_OPENING, NULL); - - item = mem->malloc(sizeof(nghttp2_outbound_item), NULL); - - nghttp2_outbound_item_init(item); - - frame = &item->frame; - - nghttp2_frame_headers_init(&frame->headers, NGHTTP2_FLAG_END_HEADERS, 2, - NGHTTP2_HCAT_HEADERS, NULL, NULL, 0); - nghttp2_session_add_item(session, item); - CU_ASSERT(0 == nghttp2_session_send(session)); - stream = nghttp2_session_get_stream(session, 2); - CU_ASSERT(NGHTTP2_STREAM_OPENED == stream->state); - - nghttp2_session_del(session); -} - -void test_nghttp2_session_send_headers_frame_size_error(void) { - nghttp2_session *session; - nghttp2_session_callbacks callbacks; - nghttp2_outbound_item *item; - nghttp2_frame *frame; - nghttp2_nv *nva; - ssize_t nvlen; - size_t vallen = NGHTTP2_HD_MAX_NV; - nghttp2_nv nv[28]; - size_t nnv = ARRLEN(nv); - size_t i; - my_user_data ud; - nghttp2_mem *mem; - - mem = nghttp2_mem_default(); - - for (i = 0; i < nnv; ++i) { - nv[i].name = (uint8_t *)"header"; - nv[i].namelen = strlen((const char *)nv[i].name); - nv[i].value = mem->malloc(vallen + 1, NULL); - memset(nv[i].value, '0' + (int)i, vallen); - nv[i].value[vallen] = '\0'; - nv[i].valuelen = vallen; - nv[i].flags = NGHTTP2_NV_FLAG_NONE; - } - - memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); - callbacks.send_callback = null_send_callback; - callbacks.on_frame_not_send_callback = on_frame_not_send_callback; - - nghttp2_session_client_new(&session, &callbacks, &ud); - nvlen = nnv; - nghttp2_nv_array_copy(&nva, nv, nvlen, mem); - - item = mem->malloc(sizeof(nghttp2_outbound_item), NULL); - - nghttp2_outbound_item_init(item); - - frame = &item->frame; - - nghttp2_frame_headers_init(&frame->headers, NGHTTP2_FLAG_END_HEADERS, - session->next_stream_id, NGHTTP2_HCAT_REQUEST, - NULL, nva, nvlen); - - session->next_stream_id += 2; - - nghttp2_session_add_item(session, item); - - ud.frame_not_send_cb_called = 0; - - CU_ASSERT(0 == nghttp2_session_send(session)); - - CU_ASSERT(1 == ud.frame_not_send_cb_called); - CU_ASSERT(NGHTTP2_HEADERS == ud.not_sent_frame_type); - CU_ASSERT(NGHTTP2_ERR_FRAME_SIZE_ERROR == ud.not_sent_error); - - for (i = 0; i < nnv; ++i) { - mem->free(nv[i].value, NULL); - } - nghttp2_session_del(session); -} - -void test_nghttp2_session_send_headers_push_reply(void) { - nghttp2_session *session; - nghttp2_session_callbacks callbacks; - nghttp2_outbound_item *item; - nghttp2_frame *frame; - nghttp2_stream *stream; - nghttp2_mem *mem; - - mem = nghttp2_mem_default(); - - memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); - callbacks.send_callback = null_send_callback; - - CU_ASSERT(0 == nghttp2_session_server_new(&session, &callbacks, NULL)); - nghttp2_session_open_stream(session, 2, NGHTTP2_STREAM_FLAG_NONE, - &pri_spec_default, NGHTTP2_STREAM_RESERVED, NULL); - - item = mem->malloc(sizeof(nghttp2_outbound_item), NULL); - - nghttp2_outbound_item_init(item); - - frame = &item->frame; - - nghttp2_frame_headers_init(&frame->headers, NGHTTP2_FLAG_END_HEADERS, 2, - NGHTTP2_HCAT_HEADERS, NULL, NULL, 0); - nghttp2_session_add_item(session, item); - CU_ASSERT(0 == session->num_outgoing_streams); - CU_ASSERT(0 == nghttp2_session_send(session)); - CU_ASSERT(1 == session->num_outgoing_streams); - stream = nghttp2_session_get_stream(session, 2); - CU_ASSERT(NGHTTP2_STREAM_OPENED == stream->state); - CU_ASSERT(0 == (stream->flags & NGHTTP2_STREAM_FLAG_PUSH)); - nghttp2_session_del(session); -} - -void test_nghttp2_session_send_rst_stream(void) { - nghttp2_session *session; - nghttp2_session_callbacks callbacks; - my_user_data user_data; - nghttp2_outbound_item *item; - nghttp2_frame *frame; - nghttp2_mem *mem; - - mem = nghttp2_mem_default(); - - memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); - callbacks.send_callback = null_send_callback; - nghttp2_session_client_new(&session, &callbacks, &user_data); - nghttp2_session_open_stream(session, 1, NGHTTP2_STREAM_FLAG_NONE, - &pri_spec_default, NGHTTP2_STREAM_OPENING, NULL); - - item = mem->malloc(sizeof(nghttp2_outbound_item), NULL); - - nghttp2_outbound_item_init(item); - - frame = &item->frame; - - nghttp2_frame_rst_stream_init(&frame->rst_stream, 1, NGHTTP2_PROTOCOL_ERROR); - nghttp2_session_add_item(session, item); - CU_ASSERT(0 == nghttp2_session_send(session)); - - CU_ASSERT(NULL == nghttp2_session_get_stream(session, 1)); - - nghttp2_session_del(session); -} - -void test_nghttp2_session_send_push_promise(void) { - nghttp2_session *session; - nghttp2_session_callbacks callbacks; - nghttp2_outbound_item *item; - nghttp2_frame *frame; - nghttp2_stream *stream; - nghttp2_settings_entry iv; - my_user_data ud; - nghttp2_mem *mem; - - mem = nghttp2_mem_default(); - memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); - callbacks.send_callback = null_send_callback; - callbacks.on_frame_not_send_callback = on_frame_not_send_callback; - - nghttp2_session_server_new(&session, &callbacks, &ud); - nghttp2_session_open_stream(session, 1, NGHTTP2_STREAM_FLAG_NONE, - &pri_spec_default, NGHTTP2_STREAM_OPENING, NULL); - - item = mem->malloc(sizeof(nghttp2_outbound_item), NULL); - - nghttp2_outbound_item_init(item); - - frame = &item->frame; - - nghttp2_frame_push_promise_init(&frame->push_promise, - NGHTTP2_FLAG_END_HEADERS, 1, - session->next_stream_id, NULL, 0); - - session->next_stream_id += 2; - - nghttp2_session_add_item(session, item); - - CU_ASSERT(0 == nghttp2_session_send(session)); - stream = nghttp2_session_get_stream(session, 2); - CU_ASSERT(NGHTTP2_STREAM_RESERVED == stream->state); - - /* Received ENABLE_PUSH = 0 */ - iv.settings_id = NGHTTP2_SETTINGS_ENABLE_PUSH; - iv.value = 0; - frame = mem->malloc(sizeof(nghttp2_frame), NULL); - nghttp2_frame_settings_init(&frame->settings, NGHTTP2_FLAG_NONE, - dup_iv(&iv, 1), 1); - nghttp2_session_on_settings_received(session, frame, 1); - nghttp2_frame_settings_free(&frame->settings, mem); - mem->free(frame, NULL); - - item = mem->malloc(sizeof(nghttp2_outbound_item), NULL); - - nghttp2_outbound_item_init(item); - - frame = &item->frame; - - nghttp2_frame_push_promise_init(&frame->push_promise, - NGHTTP2_FLAG_END_HEADERS, 1, -1, NULL, 0); - nghttp2_session_add_item(session, item); - - ud.frame_not_send_cb_called = 0; - CU_ASSERT(0 == nghttp2_session_send(session)); - - CU_ASSERT(1 == ud.frame_not_send_cb_called); - CU_ASSERT(NGHTTP2_PUSH_PROMISE == ud.not_sent_frame_type); - CU_ASSERT(NGHTTP2_ERR_PUSH_DISABLED == ud.not_sent_error); - - nghttp2_session_del(session); - - /* PUSH_PROMISE from client is error */ - nghttp2_session_client_new(&session, &callbacks, &ud); - nghttp2_session_open_stream(session, 1, NGHTTP2_STREAM_FLAG_NONE, - &pri_spec_default, NGHTTP2_STREAM_OPENING, NULL); - item = mem->malloc(sizeof(nghttp2_outbound_item), NULL); - - nghttp2_outbound_item_init(item); - - frame = &item->frame; - - nghttp2_frame_push_promise_init(&frame->push_promise, - NGHTTP2_FLAG_END_HEADERS, 1, -1, NULL, 0); - nghttp2_session_add_item(session, item); - - CU_ASSERT(0 == nghttp2_session_send(session)); - CU_ASSERT(NULL == nghttp2_session_get_stream(session, 3)); - - nghttp2_session_del(session); -} - -void test_nghttp2_session_is_my_stream_id(void) { - nghttp2_session *session; - nghttp2_session_callbacks callbacks; - memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); - nghttp2_session_server_new(&session, &callbacks, NULL); - - CU_ASSERT(0 == nghttp2_session_is_my_stream_id(session, 0)); - CU_ASSERT(0 == nghttp2_session_is_my_stream_id(session, 1)); - CU_ASSERT(1 == nghttp2_session_is_my_stream_id(session, 2)); - - nghttp2_session_del(session); - - nghttp2_session_client_new(&session, &callbacks, NULL); - - CU_ASSERT(0 == nghttp2_session_is_my_stream_id(session, 0)); - CU_ASSERT(1 == nghttp2_session_is_my_stream_id(session, 1)); - CU_ASSERT(0 == nghttp2_session_is_my_stream_id(session, 2)); - - nghttp2_session_del(session); -} - -void test_nghttp2_session_upgrade(void) { - nghttp2_session *session; - nghttp2_session_callbacks callbacks; - uint8_t settings_payload[128]; - size_t settings_payloadlen; - nghttp2_settings_entry iv[16]; - nghttp2_stream *stream; - nghttp2_outbound_item *item; - - memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); - callbacks.send_callback = null_send_callback; - iv[0].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS; - iv[0].value = 1; - iv[1].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE; - iv[1].value = 4095; - settings_payloadlen = nghttp2_pack_settings_payload( - settings_payload, sizeof(settings_payload), iv, 2); - - /* Check client side */ - nghttp2_session_client_new(&session, &callbacks, NULL); - CU_ASSERT(0 == nghttp2_session_upgrade(session, settings_payload, - settings_payloadlen, &callbacks)); - stream = nghttp2_session_get_stream(session, 1); - CU_ASSERT(stream != NULL); - CU_ASSERT(&callbacks == stream->stream_user_data); - CU_ASSERT(NGHTTP2_SHUT_WR == stream->shut_flags); - item = nghttp2_session_get_next_ob_item(session); - CU_ASSERT(NGHTTP2_SETTINGS == item->frame.hd.type); - CU_ASSERT(2 == item->frame.settings.niv); - CU_ASSERT(NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS == - item->frame.settings.iv[0].settings_id); - CU_ASSERT(1 == item->frame.settings.iv[0].value); - CU_ASSERT(NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE == - item->frame.settings.iv[1].settings_id); - CU_ASSERT(4095 == item->frame.settings.iv[1].value); - - /* Call nghttp2_session_upgrade() again is error */ - CU_ASSERT(NGHTTP2_ERR_PROTO == - nghttp2_session_upgrade(session, settings_payload, - settings_payloadlen, &callbacks)); - nghttp2_session_del(session); - - /* Check server side */ - nghttp2_session_server_new(&session, &callbacks, NULL); - CU_ASSERT(0 == nghttp2_session_upgrade(session, settings_payload, - settings_payloadlen, &callbacks)); - stream = nghttp2_session_get_stream(session, 1); - CU_ASSERT(stream != NULL); - CU_ASSERT(NULL == stream->stream_user_data); - CU_ASSERT(NGHTTP2_SHUT_RD == stream->shut_flags); - CU_ASSERT(NULL == nghttp2_session_get_next_ob_item(session)); - CU_ASSERT(1 == session->remote_settings.max_concurrent_streams); - CU_ASSERT(4095 == session->remote_settings.initial_window_size); - /* Call nghttp2_session_upgrade() again is error */ - CU_ASSERT(NGHTTP2_ERR_PROTO == - nghttp2_session_upgrade(session, settings_payload, - settings_payloadlen, &callbacks)); - nghttp2_session_del(session); - - /* Empty SETTINGS is OK */ - settings_payloadlen = nghttp2_pack_settings_payload( - settings_payload, sizeof(settings_payload), NULL, 0); - - nghttp2_session_client_new(&session, &callbacks, NULL); - CU_ASSERT(0 == nghttp2_session_upgrade(session, settings_payload, - settings_payloadlen, NULL)); - nghttp2_session_del(session); -} - -void test_nghttp2_session_reprioritize_stream(void) { - nghttp2_session *session; - nghttp2_session_callbacks callbacks; - my_user_data ud; - nghttp2_stream *stream; - nghttp2_stream *dep_stream; - nghttp2_priority_spec pri_spec; - - memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); - callbacks.send_callback = block_count_send_callback; - - nghttp2_session_server_new(&session, &callbacks, &ud); - - stream = nghttp2_session_open_stream(session, 1, NGHTTP2_STREAM_FLAG_NONE, - &pri_spec_default, - NGHTTP2_STREAM_OPENING, NULL); - - nghttp2_priority_spec_init(&pri_spec, 0, 10, 0); - - nghttp2_session_reprioritize_stream(session, stream, &pri_spec); - - CU_ASSERT(10 == stream->weight); - CU_ASSERT(NULL == stream->dep_prev); - - /* If depenency to idle stream which is not in depdenency tree yet */ - - nghttp2_priority_spec_init(&pri_spec, 3, 99, 0); - - nghttp2_session_reprioritize_stream(session, stream, &pri_spec); - - CU_ASSERT(99 == stream->weight); - CU_ASSERT(3 == stream->dep_prev->stream_id); - - dep_stream = nghttp2_session_get_stream_raw(session, 3); - - CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT == dep_stream->weight); - - dep_stream = open_stream(session, 3); - - /* Change weight */ - pri_spec.weight = 128; - - nghttp2_session_reprioritize_stream(session, stream, &pri_spec); - - CU_ASSERT(128 == stream->weight); - CU_ASSERT(dep_stream == stream->dep_prev); - - /* Test circular dependency; stream 1 is first removed and becomes - root. Then stream 3 depends on it. */ - nghttp2_priority_spec_init(&pri_spec, 1, 1, 0); - - nghttp2_session_reprioritize_stream(session, dep_stream, &pri_spec); - - CU_ASSERT(1 == dep_stream->weight); - CU_ASSERT(stream == dep_stream->dep_prev); - - /* Making priority to closed stream will result in default - priority */ - session->last_recv_stream_id = 9; - - nghttp2_priority_spec_init(&pri_spec, 5, 5, 0); - - nghttp2_session_reprioritize_stream(session, stream, &pri_spec); - - CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT == stream->weight); - - nghttp2_session_del(session); -} - -void test_nghttp2_session_reprioritize_stream_with_idle_stream_dep(void) { - nghttp2_session *session; - nghttp2_session_callbacks callbacks; - nghttp2_stream *stream; - nghttp2_priority_spec pri_spec; - - memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); - callbacks.send_callback = block_count_send_callback; - - nghttp2_session_server_new(&session, &callbacks, NULL); - - stream = nghttp2_session_open_stream(session, 1, NGHTTP2_STREAM_FLAG_NONE, - &pri_spec_default, - NGHTTP2_STREAM_OPENING, NULL); - - session->pending_local_max_concurrent_stream = 1; - - nghttp2_priority_spec_init(&pri_spec, 101, 10, 0); - - nghttp2_session_reprioritize_stream(session, stream, &pri_spec); - - /* idle stream is not counteed to max concurrent streams */ - - CU_ASSERT(10 == stream->weight); - CU_ASSERT(101 == stream->dep_prev->stream_id); - - stream = nghttp2_session_get_stream_raw(session, 101); - - CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT == stream->weight); - - nghttp2_session_del(session); -} - -void test_nghttp2_submit_data(void) { - nghttp2_session *session; - nghttp2_session_callbacks callbacks; - nghttp2_data_provider data_prd; - my_user_data ud; - nghttp2_frame *frame; - nghttp2_frame_hd hd; - nghttp2_active_outbound_item *aob; - nghttp2_bufs *framebufs; - nghttp2_buf *buf; - - memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); - callbacks.send_callback = block_count_send_callback; - - data_prd.read_callback = fixed_length_data_source_read_callback; - ud.data_source_length = NGHTTP2_DATA_PAYLOADLEN * 2; - CU_ASSERT(0 == nghttp2_session_client_new(&session, &callbacks, &ud)); - aob = &session->aob; - framebufs = &aob->framebufs; - - nghttp2_session_open_stream(session, 1, NGHTTP2_STREAM_FLAG_NONE, - &pri_spec_default, NGHTTP2_STREAM_OPENING, NULL); - CU_ASSERT( - 0 == nghttp2_submit_data(session, NGHTTP2_FLAG_END_STREAM, 1, &data_prd)); - - ud.block_count = 0; - CU_ASSERT(0 == nghttp2_session_send(session)); - frame = &aob->item->frame; - - buf = &framebufs->head->buf; - nghttp2_frame_unpack_frame_hd(&hd, buf->pos); - - CU_ASSERT(NGHTTP2_FLAG_NONE == hd.flags); - CU_ASSERT(NGHTTP2_FLAG_NONE == frame->hd.flags); - /* aux_data.data.flags has these flags */ - CU_ASSERT(NGHTTP2_FLAG_END_STREAM == aob->item->aux_data.data.flags); - - nghttp2_session_del(session); -} - -void test_nghttp2_submit_data_read_length_too_large(void) { - nghttp2_session *session; - nghttp2_session_callbacks callbacks; - nghttp2_data_provider data_prd; - my_user_data ud; - nghttp2_frame *frame; - nghttp2_frame_hd hd; - nghttp2_active_outbound_item *aob; - nghttp2_bufs *framebufs; - nghttp2_buf *buf; - size_t payloadlen; - - memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); - callbacks.send_callback = block_count_send_callback; - callbacks.read_length_callback = too_large_data_source_length_callback; - - data_prd.read_callback = fixed_length_data_source_read_callback; - ud.data_source_length = NGHTTP2_DATA_PAYLOADLEN * 2; - CU_ASSERT(0 == nghttp2_session_client_new(&session, &callbacks, &ud)); - aob = &session->aob; - framebufs = &aob->framebufs; - - nghttp2_session_open_stream(session, 1, NGHTTP2_STREAM_FLAG_NONE, - &pri_spec_default, NGHTTP2_STREAM_OPENING, NULL); - CU_ASSERT( - 0 == nghttp2_submit_data(session, NGHTTP2_FLAG_END_STREAM, 1, &data_prd)); - - ud.block_count = 0; - CU_ASSERT(0 == nghttp2_session_send(session)); - frame = &aob->item->frame; - - buf = &framebufs->head->buf; - nghttp2_frame_unpack_frame_hd(&hd, buf->pos); - - CU_ASSERT(NGHTTP2_FLAG_NONE == hd.flags); - CU_ASSERT(NGHTTP2_FLAG_NONE == frame->hd.flags); - CU_ASSERT(16384 == hd.length) - /* aux_data.data.flags has these flags */ - CU_ASSERT(NGHTTP2_FLAG_END_STREAM == aob->item->aux_data.data.flags); - - nghttp2_session_del(session); - - /* Check that buffers are expanded */ - CU_ASSERT(0 == nghttp2_session_client_new(&session, &callbacks, &ud)); - - ud.data_source_length = NGHTTP2_MAX_FRAME_SIZE_MAX; - - session->remote_settings.max_frame_size = NGHTTP2_MAX_FRAME_SIZE_MAX; - - nghttp2_session_open_stream(session, 1, NGHTTP2_STREAM_FLAG_NONE, - &pri_spec_default, NGHTTP2_STREAM_OPENING, NULL); - CU_ASSERT( - 0 == nghttp2_submit_data(session, NGHTTP2_FLAG_END_STREAM, 1, &data_prd)); - - ud.block_count = 0; - CU_ASSERT(0 == nghttp2_session_send(session)); - - aob = &session->aob; - - frame = &aob->item->frame; - - framebufs = &aob->framebufs; - - buf = &framebufs->head->buf; - nghttp2_frame_unpack_frame_hd(&hd, buf->pos); - - payloadlen = nghttp2_min(NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE, - NGHTTP2_INITIAL_WINDOW_SIZE); - - CU_ASSERT(NGHTTP2_FRAME_HDLEN + 1 + payloadlen == - (size_t)nghttp2_buf_cap(buf)); - CU_ASSERT(NGHTTP2_FLAG_NONE == hd.flags); - CU_ASSERT(NGHTTP2_FLAG_NONE == frame->hd.flags); - CU_ASSERT(payloadlen == hd.length); - /* aux_data.data.flags has these flags */ - CU_ASSERT(NGHTTP2_FLAG_END_STREAM == aob->item->aux_data.data.flags); - - nghttp2_session_del(session); -} - -void test_nghttp2_submit_data_read_length_smallest(void) { - nghttp2_session *session; - nghttp2_session_callbacks callbacks; - nghttp2_data_provider data_prd; - my_user_data ud; - nghttp2_frame *frame; - nghttp2_frame_hd hd; - nghttp2_active_outbound_item *aob; - nghttp2_bufs *framebufs; - nghttp2_buf *buf; - - memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); - callbacks.send_callback = block_count_send_callback; - callbacks.read_length_callback = smallest_length_data_source_length_callback; - - data_prd.read_callback = fixed_length_data_source_read_callback; - ud.data_source_length = NGHTTP2_DATA_PAYLOADLEN * 2; - CU_ASSERT(0 == nghttp2_session_client_new(&session, &callbacks, &ud)); - aob = &session->aob; - framebufs = &aob->framebufs; - - nghttp2_session_open_stream(session, 1, NGHTTP2_STREAM_FLAG_NONE, - &pri_spec_default, NGHTTP2_STREAM_OPENING, NULL); - CU_ASSERT( - 0 == nghttp2_submit_data(session, NGHTTP2_FLAG_END_STREAM, 1, &data_prd)); - - ud.block_count = 0; - CU_ASSERT(0 == nghttp2_session_send(session)); - frame = &aob->item->frame; - - buf = &framebufs->head->buf; - nghttp2_frame_unpack_frame_hd(&hd, buf->pos); - - CU_ASSERT(NGHTTP2_FLAG_NONE == hd.flags); - CU_ASSERT(NGHTTP2_FLAG_NONE == frame->hd.flags); - CU_ASSERT(1 == hd.length) - /* aux_data.data.flags has these flags */ - CU_ASSERT(NGHTTP2_FLAG_END_STREAM == aob->item->aux_data.data.flags); - - nghttp2_session_del(session); -} - -static ssize_t submit_data_twice_data_source_read_callback( - nghttp2_session *session _U_, int32_t stream_id _U_, uint8_t *buf _U_, - size_t len, uint32_t *data_flags, nghttp2_data_source *source _U_, - void *user_data _U_) { - *data_flags |= NGHTTP2_DATA_FLAG_EOF; - return nghttp2_min(len, 16); -} - -static int submit_data_twice_on_frame_send_callback(nghttp2_session *session, - const nghttp2_frame *frame, - void *user_data _U_) { - static int called = 0; - int rv; - nghttp2_data_provider data_prd; - - if (called == 0) { - called = 1; - - data_prd.read_callback = submit_data_twice_data_source_read_callback; - - rv = nghttp2_submit_data(session, NGHTTP2_FLAG_END_STREAM, - frame->hd.stream_id, &data_prd); - CU_ASSERT(0 == rv); - } - - return 0; -} - -void test_nghttp2_submit_data_twice(void) { - nghttp2_session *session; - nghttp2_session_callbacks callbacks; - nghttp2_data_provider data_prd; - my_user_data ud; - accumulator acc; - - memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); - callbacks.send_callback = accumulator_send_callback; - callbacks.on_frame_send_callback = submit_data_twice_on_frame_send_callback; - - data_prd.read_callback = submit_data_twice_data_source_read_callback; - - acc.length = 0; - ud.acc = &acc; - - CU_ASSERT(0 == nghttp2_session_client_new(&session, &callbacks, &ud)); - - nghttp2_session_open_stream(session, 1, NGHTTP2_STREAM_FLAG_NONE, - &pri_spec_default, NGHTTP2_STREAM_OPENING, NULL); - - CU_ASSERT(0 == nghttp2_submit_data(session, NGHTTP2_FLAG_NONE, 1, &data_prd)); - - CU_ASSERT(0 == nghttp2_session_send(session)); - - /* We should have sent 2 DATA frame with 16 bytes payload each */ - CU_ASSERT(NGHTTP2_FRAME_HDLEN * 2 + 16 * 2 == acc.length); - - nghttp2_session_del(session); -} - -void test_nghttp2_submit_request_with_data(void) { - nghttp2_session *session; - nghttp2_session_callbacks callbacks; - nghttp2_data_provider data_prd; - my_user_data ud; - nghttp2_outbound_item *item; - nghttp2_mem *mem; - - mem = nghttp2_mem_default(); - - memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); - callbacks.send_callback = null_send_callback; - - data_prd.read_callback = fixed_length_data_source_read_callback; - ud.data_source_length = 64 * 1024 - 1; - CU_ASSERT(0 == nghttp2_session_client_new(&session, &callbacks, &ud)); - CU_ASSERT(1 == nghttp2_submit_request(session, NULL, reqnv, ARRLEN(reqnv), - &data_prd, NULL)); - item = nghttp2_session_get_next_ob_item(session); - CU_ASSERT(ARRLEN(reqnv) == item->frame.headers.nvlen); - assert_nv_equal(reqnv, item->frame.headers.nva, item->frame.headers.nvlen, - mem); - CU_ASSERT(0 == nghttp2_session_send(session)); - CU_ASSERT(0 == ud.data_source_length); - - nghttp2_session_del(session); -} - -void test_nghttp2_submit_request_without_data(void) { - nghttp2_session *session; - nghttp2_session_callbacks callbacks; - accumulator acc; - nghttp2_data_provider data_prd = {{-1}, NULL}; - nghttp2_outbound_item *item; - my_user_data ud; - nghttp2_frame frame; - nghttp2_hd_inflater inflater; - nva_out out; - nghttp2_bufs bufs; - nghttp2_mem *mem; - - mem = nghttp2_mem_default(); - frame_pack_bufs_init(&bufs); - - nva_out_init(&out); - acc.length = 0; - ud.acc = &acc; - memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); - callbacks.send_callback = accumulator_send_callback; - CU_ASSERT(0 == nghttp2_session_client_new(&session, &callbacks, &ud)); - - nghttp2_hd_inflate_init(&inflater, mem); - CU_ASSERT(1 == nghttp2_submit_request(session, NULL, reqnv, ARRLEN(reqnv), - &data_prd, NULL)); - item = nghttp2_session_get_next_ob_item(session); - CU_ASSERT(ARRLEN(reqnv) == item->frame.headers.nvlen); - assert_nv_equal(reqnv, item->frame.headers.nva, item->frame.headers.nvlen, - mem); - CU_ASSERT(item->frame.hd.flags & NGHTTP2_FLAG_END_STREAM); - - CU_ASSERT(0 == nghttp2_session_send(session)); - CU_ASSERT(0 == unpack_frame(&frame, acc.buf, acc.length)); - - nghttp2_bufs_add(&bufs, acc.buf, acc.length); - inflate_hd(&inflater, &out, &bufs, NGHTTP2_FRAME_HDLEN, mem); - - CU_ASSERT(ARRLEN(reqnv) == out.nvlen); - assert_nv_equal(reqnv, out.nva, out.nvlen, mem); - nghttp2_frame_headers_free(&frame.headers, mem); - nva_out_reset(&out, mem); - - nghttp2_bufs_free(&bufs); - nghttp2_hd_inflate_free(&inflater); - nghttp2_session_del(session); -} - -void test_nghttp2_submit_response_with_data(void) { - nghttp2_session *session; - nghttp2_session_callbacks callbacks; - nghttp2_data_provider data_prd; - my_user_data ud; - nghttp2_outbound_item *item; - nghttp2_mem *mem; - - mem = nghttp2_mem_default(); - - memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); - callbacks.send_callback = null_send_callback; - - data_prd.read_callback = fixed_length_data_source_read_callback; - ud.data_source_length = 64 * 1024 - 1; - CU_ASSERT(0 == nghttp2_session_server_new(&session, &callbacks, &ud)); - nghttp2_session_open_stream(session, 1, NGHTTP2_FLAG_END_STREAM, - &pri_spec_default, NGHTTP2_STREAM_OPENING, NULL); - CU_ASSERT(0 == nghttp2_submit_response(session, 1, resnv, ARRLEN(resnv), - &data_prd)); - item = nghttp2_session_get_next_ob_item(session); - CU_ASSERT(ARRLEN(resnv) == item->frame.headers.nvlen); - assert_nv_equal(resnv, item->frame.headers.nva, item->frame.headers.nvlen, - mem); - CU_ASSERT(0 == nghttp2_session_send(session)); - CU_ASSERT(0 == ud.data_source_length); - - nghttp2_session_del(session); -} - -void test_nghttp2_submit_response_without_data(void) { - nghttp2_session *session; - nghttp2_session_callbacks callbacks; - accumulator acc; - nghttp2_data_provider data_prd = {{-1}, NULL}; - nghttp2_outbound_item *item; - my_user_data ud; - nghttp2_frame frame; - nghttp2_hd_inflater inflater; - nva_out out; - nghttp2_bufs bufs; - nghttp2_mem *mem; - - mem = nghttp2_mem_default(); - frame_pack_bufs_init(&bufs); - - nva_out_init(&out); - acc.length = 0; - ud.acc = &acc; - memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); - callbacks.send_callback = accumulator_send_callback; - CU_ASSERT(0 == nghttp2_session_server_new(&session, &callbacks, &ud)); - - nghttp2_hd_inflate_init(&inflater, mem); - nghttp2_session_open_stream(session, 1, NGHTTP2_FLAG_END_STREAM, - &pri_spec_default, NGHTTP2_STREAM_OPENING, NULL); - CU_ASSERT(0 == nghttp2_submit_response(session, 1, resnv, ARRLEN(resnv), - &data_prd)); - item = nghttp2_session_get_next_ob_item(session); - CU_ASSERT(ARRLEN(resnv) == item->frame.headers.nvlen); - assert_nv_equal(resnv, item->frame.headers.nva, item->frame.headers.nvlen, - mem); - CU_ASSERT(item->frame.hd.flags & NGHTTP2_FLAG_END_STREAM); - - CU_ASSERT(0 == nghttp2_session_send(session)); - CU_ASSERT(0 == unpack_frame(&frame, acc.buf, acc.length)); - - nghttp2_bufs_add(&bufs, acc.buf, acc.length); - inflate_hd(&inflater, &out, &bufs, NGHTTP2_FRAME_HDLEN, mem); - - CU_ASSERT(ARRLEN(resnv) == out.nvlen); - assert_nv_equal(resnv, out.nva, out.nvlen, mem); - - nva_out_reset(&out, mem); - nghttp2_bufs_free(&bufs); - nghttp2_frame_headers_free(&frame.headers, mem); - nghttp2_hd_inflate_free(&inflater); - nghttp2_session_del(session); -} - -void test_nghttp2_submit_trailer(void) { - nghttp2_session *session; - nghttp2_session_callbacks callbacks; - accumulator acc; - nghttp2_data_provider data_prd; - nghttp2_outbound_item *item; - my_user_data ud; - nghttp2_frame frame; - nghttp2_hd_inflater inflater; - nva_out out; - nghttp2_bufs bufs; - nghttp2_mem *mem; - - mem = nghttp2_mem_default(); - frame_pack_bufs_init(&bufs); - - data_prd.read_callback = no_end_stream_data_source_read_callback; - nva_out_init(&out); - acc.length = 0; - ud.acc = &acc; - memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); - callbacks.send_callback = null_send_callback; - CU_ASSERT(0 == nghttp2_session_server_new(&session, &callbacks, &ud)); - - nghttp2_hd_inflate_init(&inflater, mem); - nghttp2_session_open_stream(session, 1, NGHTTP2_FLAG_END_STREAM, - &pri_spec_default, NGHTTP2_STREAM_OPENING, NULL); - CU_ASSERT(0 == nghttp2_submit_response(session, 1, resnv, ARRLEN(resnv), - &data_prd)); - CU_ASSERT(0 == nghttp2_session_send(session)); - - CU_ASSERT(0 == - nghttp2_submit_trailer(session, 1, trailernv, ARRLEN(trailernv))); - - session->callbacks.send_callback = accumulator_send_callback; - - item = nghttp2_session_get_next_ob_item(session); - CU_ASSERT(NGHTTP2_HEADERS == item->frame.hd.type); - CU_ASSERT(NGHTTP2_HCAT_HEADERS == item->frame.headers.cat); - CU_ASSERT(item->frame.hd.flags & NGHTTP2_FLAG_END_STREAM); - - CU_ASSERT(0 == nghttp2_session_send(session)); - CU_ASSERT(0 == unpack_frame(&frame, acc.buf, acc.length)); - - nghttp2_bufs_add(&bufs, acc.buf, acc.length); - inflate_hd(&inflater, &out, &bufs, NGHTTP2_FRAME_HDLEN, mem); - - CU_ASSERT(ARRLEN(trailernv) == out.nvlen); - assert_nv_equal(trailernv, out.nva, out.nvlen, mem); - - nva_out_reset(&out, mem); - nghttp2_bufs_free(&bufs); - nghttp2_frame_headers_free(&frame.headers, mem); - nghttp2_hd_inflate_free(&inflater); - nghttp2_session_del(session); -} - -void test_nghttp2_submit_headers_start_stream(void) { - nghttp2_session *session; - nghttp2_session_callbacks callbacks; - nghttp2_outbound_item *item; - nghttp2_mem *mem; - - mem = nghttp2_mem_default(); - - memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); - CU_ASSERT(0 == nghttp2_session_client_new(&session, &callbacks, NULL)); - CU_ASSERT(1 == nghttp2_submit_headers(session, NGHTTP2_FLAG_END_STREAM, -1, - NULL, reqnv, ARRLEN(reqnv), NULL)); - item = nghttp2_session_get_next_ob_item(session); - CU_ASSERT(ARRLEN(reqnv) == item->frame.headers.nvlen); - assert_nv_equal(reqnv, item->frame.headers.nva, item->frame.headers.nvlen, - mem); - CU_ASSERT((NGHTTP2_FLAG_END_HEADERS | NGHTTP2_FLAG_END_STREAM) == - item->frame.hd.flags); - CU_ASSERT(0 == (item->frame.hd.flags & NGHTTP2_FLAG_PRIORITY)); - - nghttp2_session_del(session); -} - -void test_nghttp2_submit_headers_reply(void) { - nghttp2_session *session; - nghttp2_session_callbacks callbacks; - my_user_data ud; - nghttp2_outbound_item *item; - nghttp2_stream *stream; - nghttp2_mem *mem; - - mem = nghttp2_mem_default(); - - memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); - callbacks.send_callback = null_send_callback; - callbacks.on_frame_send_callback = on_frame_send_callback; - - CU_ASSERT(0 == nghttp2_session_server_new(&session, &callbacks, &ud)); - CU_ASSERT(0 == nghttp2_submit_headers(session, NGHTTP2_FLAG_END_STREAM, 1, - NULL, resnv, ARRLEN(resnv), NULL)); - item = nghttp2_session_get_next_ob_item(session); - CU_ASSERT(ARRLEN(resnv) == item->frame.headers.nvlen); - assert_nv_equal(resnv, item->frame.headers.nva, item->frame.headers.nvlen, - mem); - CU_ASSERT((NGHTTP2_FLAG_END_STREAM | NGHTTP2_FLAG_END_HEADERS) == - item->frame.hd.flags); - - ud.frame_send_cb_called = 0; - ud.sent_frame_type = 0; - /* The transimission will be canceled because the stream 1 is not - open. */ - CU_ASSERT(0 == nghttp2_session_send(session)); - CU_ASSERT(0 == ud.frame_send_cb_called); - - stream = nghttp2_session_open_stream(session, 1, NGHTTP2_STREAM_FLAG_NONE, - &pri_spec_default, - NGHTTP2_STREAM_OPENING, NULL); - - CU_ASSERT(0 == nghttp2_submit_headers(session, NGHTTP2_FLAG_END_STREAM, 1, - NULL, resnv, ARRLEN(resnv), NULL)); - CU_ASSERT(0 == nghttp2_session_send(session)); - CU_ASSERT(1 == ud.frame_send_cb_called); - CU_ASSERT(NGHTTP2_HEADERS == ud.sent_frame_type); - CU_ASSERT(stream->shut_flags & NGHTTP2_SHUT_WR); - - nghttp2_session_del(session); -} - -void test_nghttp2_submit_headers_push_reply(void) { - nghttp2_session *session; - nghttp2_session_callbacks callbacks; - my_user_data ud; - nghttp2_stream *stream; - int foo; - - memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); - callbacks.send_callback = null_send_callback; - callbacks.on_frame_send_callback = on_frame_send_callback; - - CU_ASSERT(0 == nghttp2_session_server_new(&session, &callbacks, &ud)); - stream = nghttp2_session_open_stream(session, 2, NGHTTP2_STREAM_FLAG_NONE, - &pri_spec_default, - NGHTTP2_STREAM_RESERVED, NULL); - CU_ASSERT(0 == nghttp2_submit_headers(session, NGHTTP2_FLAG_NONE, 2, NULL, - resnv, ARRLEN(resnv), &foo)); - - ud.frame_send_cb_called = 0; - ud.sent_frame_type = 0; - CU_ASSERT(0 == nghttp2_session_send(session)); - CU_ASSERT(1 == ud.frame_send_cb_called); - CU_ASSERT(NGHTTP2_HEADERS == ud.sent_frame_type); - CU_ASSERT(NGHTTP2_STREAM_OPENED == stream->state); - CU_ASSERT(&foo == stream->stream_user_data); - - nghttp2_session_del(session); - - /* Sending HEADERS from client against stream in reserved state is - error */ - CU_ASSERT(0 == nghttp2_session_client_new(&session, &callbacks, &ud)); - nghttp2_session_open_stream(session, 2, NGHTTP2_STREAM_FLAG_NONE, - &pri_spec_default, NGHTTP2_STREAM_RESERVED, NULL); - CU_ASSERT(0 == nghttp2_submit_headers(session, NGHTTP2_FLAG_NONE, 2, NULL, - reqnv, ARRLEN(reqnv), NULL)); - - ud.frame_send_cb_called = 0; - ud.sent_frame_type = 0; - CU_ASSERT(0 == nghttp2_session_send(session)); - CU_ASSERT(0 == ud.frame_send_cb_called); - - nghttp2_session_del(session); -} - -void test_nghttp2_submit_headers(void) { - nghttp2_session *session; - nghttp2_session_callbacks callbacks; - my_user_data ud; - nghttp2_outbound_item *item; - nghttp2_stream *stream; - accumulator acc; - nghttp2_frame frame; - nghttp2_hd_inflater inflater; - nva_out out; - nghttp2_bufs bufs; - nghttp2_mem *mem; - - mem = nghttp2_mem_default(); - frame_pack_bufs_init(&bufs); - - nva_out_init(&out); - acc.length = 0; - ud.acc = &acc; - memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); - callbacks.send_callback = accumulator_send_callback; - callbacks.on_frame_send_callback = on_frame_send_callback; - - CU_ASSERT(0 == nghttp2_session_client_new(&session, &callbacks, &ud)); - - nghttp2_hd_inflate_init(&inflater, mem); - CU_ASSERT(0 == nghttp2_submit_headers(session, NGHTTP2_FLAG_END_STREAM, 1, - NULL, reqnv, ARRLEN(reqnv), NULL)); - item = nghttp2_session_get_next_ob_item(session); - CU_ASSERT(ARRLEN(reqnv) == item->frame.headers.nvlen); - assert_nv_equal(reqnv, item->frame.headers.nva, item->frame.headers.nvlen, - mem); - CU_ASSERT((NGHTTP2_FLAG_END_STREAM | NGHTTP2_FLAG_END_HEADERS) == - item->frame.hd.flags); - - ud.frame_send_cb_called = 0; - ud.sent_frame_type = 0; - /* The transimission will be canceled because the stream 1 is not - open. */ - CU_ASSERT(0 == nghttp2_session_send(session)); - CU_ASSERT(0 == ud.frame_send_cb_called); - - stream = nghttp2_session_open_stream(session, 1, NGHTTP2_STREAM_FLAG_NONE, - &pri_spec_default, - NGHTTP2_STREAM_OPENING, NULL); - - CU_ASSERT(0 == nghttp2_submit_headers(session, NGHTTP2_FLAG_END_STREAM, 1, - NULL, reqnv, ARRLEN(reqnv), NULL)); - CU_ASSERT(0 == nghttp2_session_send(session)); - CU_ASSERT(1 == ud.frame_send_cb_called); - CU_ASSERT(NGHTTP2_HEADERS == ud.sent_frame_type); - CU_ASSERT(stream->shut_flags & NGHTTP2_SHUT_WR); - - CU_ASSERT(0 == unpack_frame(&frame, acc.buf, acc.length)); - - nghttp2_bufs_add(&bufs, acc.buf, acc.length); - inflate_hd(&inflater, &out, &bufs, NGHTTP2_FRAME_HDLEN, mem); - - CU_ASSERT(ARRLEN(reqnv) == out.nvlen); - assert_nv_equal(reqnv, out.nva, out.nvlen, mem); - - nva_out_reset(&out, mem); - nghttp2_bufs_free(&bufs); - nghttp2_frame_headers_free(&frame.headers, mem); - - nghttp2_hd_inflate_free(&inflater); - nghttp2_session_del(session); -} - -void test_nghttp2_submit_headers_continuation(void) { - nghttp2_session *session; - nghttp2_session_callbacks callbacks; - nghttp2_nv nv[] = { - MAKE_NV("h1", ""), MAKE_NV("h1", ""), MAKE_NV("h1", ""), - MAKE_NV("h1", ""), MAKE_NV("h1", ""), MAKE_NV("h1", ""), - MAKE_NV("h1", ""), - }; - nghttp2_outbound_item *item; - uint8_t data[4096]; - size_t i; - my_user_data ud; - - memset(data, '0', sizeof(data)); - for (i = 0; i < ARRLEN(nv); ++i) { - nv[i].valuelen = sizeof(data); - nv[i].value = data; - } - - memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); - callbacks.send_callback = null_send_callback; - callbacks.on_frame_send_callback = on_frame_send_callback; - - CU_ASSERT(0 == nghttp2_session_client_new(&session, &callbacks, &ud)); - CU_ASSERT(1 == nghttp2_submit_headers(session, NGHTTP2_FLAG_END_STREAM, -1, - NULL, nv, ARRLEN(nv), NULL)); - item = nghttp2_session_get_next_ob_item(session); - CU_ASSERT(NGHTTP2_HEADERS == item->frame.hd.type); - CU_ASSERT((NGHTTP2_FLAG_END_STREAM | NGHTTP2_FLAG_END_HEADERS) == - item->frame.hd.flags); - CU_ASSERT(0 == (item->frame.hd.flags & NGHTTP2_FLAG_PRIORITY)); - - ud.frame_send_cb_called = 0; - CU_ASSERT(0 == nghttp2_session_send(session)); - CU_ASSERT(1 == ud.frame_send_cb_called); - - nghttp2_session_del(session); -} - -void test_nghttp2_submit_priority(void) { - nghttp2_session *session; - nghttp2_session_callbacks callbacks; - nghttp2_stream *stream; - my_user_data ud; - nghttp2_priority_spec pri_spec; - - memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); - callbacks.send_callback = null_send_callback; - callbacks.on_frame_send_callback = on_frame_send_callback; - - nghttp2_session_client_new(&session, &callbacks, &ud); - stream = nghttp2_session_open_stream(session, 1, NGHTTP2_STREAM_FLAG_NONE, - &pri_spec_default, - NGHTTP2_STREAM_OPENING, NULL); - - nghttp2_priority_spec_init(&pri_spec, 0, 3, 0); - - /* depends on stream 0 */ - CU_ASSERT(0 == - nghttp2_submit_priority(session, NGHTTP2_FLAG_NONE, 1, &pri_spec)); - CU_ASSERT(0 == nghttp2_session_send(session)); - CU_ASSERT(3 == stream->weight); - - /* submit against idle stream */ - CU_ASSERT(0 == - nghttp2_submit_priority(session, NGHTTP2_FLAG_NONE, 3, &pri_spec)); - - ud.frame_send_cb_called = 0; - CU_ASSERT(0 == nghttp2_session_send(session)); - CU_ASSERT(1 == ud.frame_send_cb_called); - - nghttp2_session_del(session); -} - -void test_nghttp2_submit_settings(void) { - nghttp2_session *session; - nghttp2_session_callbacks callbacks; - my_user_data ud; - nghttp2_outbound_item *item; - nghttp2_frame *frame; - nghttp2_settings_entry iv[7]; - nghttp2_frame ack_frame; - const int32_t UNKNOWN_ID = 1000000007; - nghttp2_mem *mem; - - mem = nghttp2_mem_default(); - - iv[0].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS; - iv[0].value = 5; - - iv[1].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE; - iv[1].value = 16 * 1024; - - iv[2].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS; - iv[2].value = 50; - - iv[3].settings_id = NGHTTP2_SETTINGS_HEADER_TABLE_SIZE; - iv[3].value = 0; - - iv[4].settings_id = UNKNOWN_ID; - iv[4].value = 999; - - iv[5].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE; - iv[5].value = (uint32_t)NGHTTP2_MAX_WINDOW_SIZE + 1; - - memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); - callbacks.send_callback = null_send_callback; - callbacks.on_frame_send_callback = on_frame_send_callback; - nghttp2_session_server_new(&session, &callbacks, &ud); - - CU_ASSERT(NGHTTP2_ERR_INVALID_ARGUMENT == - nghttp2_submit_settings(session, NGHTTP2_FLAG_NONE, iv, 6)); - - /* Make sure that local settings are not changed */ - CU_ASSERT(NGHTTP2_INITIAL_MAX_CONCURRENT_STREAMS == - session->local_settings.max_concurrent_streams); - CU_ASSERT(NGHTTP2_INITIAL_WINDOW_SIZE == - session->local_settings.initial_window_size); - - /* Now sends without 6th one */ - CU_ASSERT(0 == nghttp2_submit_settings(session, NGHTTP2_FLAG_NONE, iv, 5)); - - item = nghttp2_session_get_next_ob_item(session); - - CU_ASSERT(NGHTTP2_SETTINGS == item->frame.hd.type); - - frame = &item->frame; - CU_ASSERT(5 == frame->settings.niv); - CU_ASSERT(5 == frame->settings.iv[0].value); - CU_ASSERT(NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS == - frame->settings.iv[0].settings_id); - - CU_ASSERT(16 * 1024 == frame->settings.iv[1].value); - CU_ASSERT(NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE == - frame->settings.iv[1].settings_id); - - CU_ASSERT(UNKNOWN_ID == frame->settings.iv[4].settings_id); - CU_ASSERT(999 == frame->settings.iv[4].value); - - ud.frame_send_cb_called = 0; - CU_ASSERT(0 == nghttp2_session_send(session)); - CU_ASSERT(1 == ud.frame_send_cb_called); - - CU_ASSERT(50 == session->pending_local_max_concurrent_stream); - - nghttp2_frame_settings_init(&ack_frame.settings, NGHTTP2_FLAG_ACK, NULL, 0); - CU_ASSERT(0 == nghttp2_session_on_settings_received(session, &ack_frame, 0)); - nghttp2_frame_settings_free(&ack_frame.settings, mem); - - CU_ASSERT(16 * 1024 == session->local_settings.initial_window_size); - CU_ASSERT(0 == session->hd_inflater.ctx.hd_table_bufsize_max); - CU_ASSERT(50 == session->local_settings.max_concurrent_streams); - CU_ASSERT(NGHTTP2_INITIAL_MAX_CONCURRENT_STREAMS == - session->pending_local_max_concurrent_stream); - - nghttp2_session_del(session); -} - -void test_nghttp2_submit_settings_update_local_window_size(void) { - nghttp2_session *session; - nghttp2_session_callbacks callbacks; - nghttp2_outbound_item *item; - nghttp2_settings_entry iv[4]; - nghttp2_stream *stream; - nghttp2_frame ack_frame; - nghttp2_mem *mem; - - mem = nghttp2_mem_default(); - nghttp2_frame_settings_init(&ack_frame.settings, NGHTTP2_FLAG_ACK, NULL, 0); - - iv[0].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE; - iv[0].value = 16 * 1024; - - memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); - callbacks.send_callback = null_send_callback; - - nghttp2_session_server_new(&session, &callbacks, NULL); - - stream = nghttp2_session_open_stream(session, 1, NGHTTP2_STREAM_FLAG_NONE, - &pri_spec_default, NGHTTP2_STREAM_OPENED, - NULL); - stream->local_window_size = NGHTTP2_INITIAL_WINDOW_SIZE + 100; - stream->recv_window_size = 32768; - - nghttp2_session_open_stream(session, 3, NGHTTP2_STREAM_FLAG_NONE, - &pri_spec_default, NGHTTP2_STREAM_OPENED, NULL); - - CU_ASSERT(0 == nghttp2_submit_settings(session, NGHTTP2_FLAG_NONE, iv, 1)); - CU_ASSERT(0 == nghttp2_session_send(session)); - CU_ASSERT(0 == nghttp2_session_on_settings_received(session, &ack_frame, 0)); - - stream = nghttp2_session_get_stream(session, 1); - CU_ASSERT(0 == stream->recv_window_size); - CU_ASSERT(16 * 1024 + 100 == stream->local_window_size); - - stream = nghttp2_session_get_stream(session, 3); - CU_ASSERT(16 * 1024 == stream->local_window_size); - - item = nghttp2_session_get_next_ob_item(session); - CU_ASSERT(NGHTTP2_WINDOW_UPDATE == item->frame.hd.type); - CU_ASSERT(32768 == item->frame.window_update.window_size_increment); - - nghttp2_session_del(session); - - /* Check overflow case */ - iv[0].value = 128 * 1024; - nghttp2_session_server_new(&session, &callbacks, NULL); - stream = nghttp2_session_open_stream(session, 1, NGHTTP2_STREAM_FLAG_NONE, - &pri_spec_default, NGHTTP2_STREAM_OPENED, - NULL); - stream->local_window_size = NGHTTP2_MAX_WINDOW_SIZE; - - CU_ASSERT(0 == nghttp2_submit_settings(session, NGHTTP2_FLAG_NONE, iv, 1)); - CU_ASSERT(0 == nghttp2_session_send(session)); - CU_ASSERT(0 == nghttp2_session_on_settings_received(session, &ack_frame, 0)); - - item = nghttp2_session_get_next_ob_item(session); - CU_ASSERT(NGHTTP2_GOAWAY == item->frame.hd.type); - CU_ASSERT(NGHTTP2_FLOW_CONTROL_ERROR == item->frame.goaway.error_code); - - nghttp2_session_del(session); - nghttp2_frame_settings_free(&ack_frame.settings, mem); -} - -void test_nghttp2_submit_push_promise(void) { - nghttp2_session *session; - nghttp2_session_callbacks callbacks; - my_user_data ud; - nghttp2_stream *stream; - - memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); - callbacks.send_callback = null_send_callback; - callbacks.on_frame_send_callback = on_frame_send_callback; - callbacks.on_frame_not_send_callback = on_frame_not_send_callback; - - CU_ASSERT(0 == nghttp2_session_server_new(&session, &callbacks, &ud)); - nghttp2_session_open_stream(session, 1, NGHTTP2_STREAM_FLAG_NONE, - &pri_spec_default, NGHTTP2_STREAM_OPENING, NULL); - CU_ASSERT(2 == nghttp2_submit_push_promise(session, NGHTTP2_FLAG_NONE, 1, - reqnv, ARRLEN(reqnv), &ud)); - - ud.frame_send_cb_called = 0; - ud.sent_frame_type = 0; - CU_ASSERT(0 == nghttp2_session_send(session)); - CU_ASSERT(1 == ud.frame_send_cb_called); - CU_ASSERT(NGHTTP2_PUSH_PROMISE == ud.sent_frame_type); - stream = nghttp2_session_get_stream(session, 2); - CU_ASSERT(NGHTTP2_STREAM_RESERVED == stream->state); - CU_ASSERT(&ud == nghttp2_session_get_stream_user_data(session, 2)); - - /* submit PUSH_PROMISE while associated stream is not opened */ - CU_ASSERT(4 == nghttp2_submit_push_promise(session, NGHTTP2_FLAG_NONE, 3, - reqnv, ARRLEN(reqnv), &ud)); - - ud.frame_not_send_cb_called = 0; - - CU_ASSERT(0 == nghttp2_session_send(session)); - CU_ASSERT(1 == ud.frame_not_send_cb_called); - CU_ASSERT(NGHTTP2_PUSH_PROMISE == ud.not_sent_frame_type); - - stream = nghttp2_session_get_stream(session, 4); - - CU_ASSERT(NULL == stream); - - nghttp2_session_del(session); -} - -void test_nghttp2_submit_window_update(void) { - nghttp2_session *session; - nghttp2_session_callbacks callbacks; - my_user_data ud; - nghttp2_outbound_item *item; - nghttp2_stream *stream; - - memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); - callbacks.send_callback = null_send_callback; - - nghttp2_session_client_new(&session, &callbacks, &ud); - stream = nghttp2_session_open_stream(session, 2, NGHTTP2_STREAM_FLAG_NONE, - &pri_spec_default, NGHTTP2_STREAM_OPENED, - NULL); - stream->recv_window_size = 4096; - - CU_ASSERT(0 == - nghttp2_submit_window_update(session, NGHTTP2_FLAG_NONE, 2, 1024)); - item = nghttp2_session_get_next_ob_item(session); - CU_ASSERT(NGHTTP2_WINDOW_UPDATE == item->frame.hd.type); - CU_ASSERT(1024 == item->frame.window_update.window_size_increment); - CU_ASSERT(0 == nghttp2_session_send(session)); - CU_ASSERT(3072 == stream->recv_window_size); - - CU_ASSERT(0 == - nghttp2_submit_window_update(session, NGHTTP2_FLAG_NONE, 2, 4096)); - item = nghttp2_session_get_next_ob_item(session); - CU_ASSERT(NGHTTP2_WINDOW_UPDATE == item->frame.hd.type); - CU_ASSERT(4096 == item->frame.window_update.window_size_increment); - CU_ASSERT(0 == nghttp2_session_send(session)); - CU_ASSERT(0 == stream->recv_window_size); - - CU_ASSERT(0 == - nghttp2_submit_window_update(session, NGHTTP2_FLAG_NONE, 2, 4096)); - item = nghttp2_session_get_next_ob_item(session); - CU_ASSERT(NGHTTP2_WINDOW_UPDATE == item->frame.hd.type); - CU_ASSERT(4096 == item->frame.window_update.window_size_increment); - CU_ASSERT(0 == nghttp2_session_send(session)); - CU_ASSERT(0 == stream->recv_window_size); - - CU_ASSERT(0 == - nghttp2_submit_window_update(session, NGHTTP2_FLAG_NONE, 2, 0)); - /* It is ok if stream is closed or does not exist at the call - time */ - CU_ASSERT(0 == - nghttp2_submit_window_update(session, NGHTTP2_FLAG_NONE, 4, 4096)); - - nghttp2_session_del(session); -} - -void test_nghttp2_submit_window_update_local_window_size(void) { - nghttp2_session *session; - nghttp2_session_callbacks callbacks; - nghttp2_outbound_item *item; - nghttp2_stream *stream; - - memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); - callbacks.send_callback = null_send_callback; - - nghttp2_session_client_new(&session, &callbacks, NULL); - stream = nghttp2_session_open_stream(session, 2, NGHTTP2_STREAM_FLAG_NONE, - &pri_spec_default, NGHTTP2_STREAM_OPENED, - NULL); - stream->recv_window_size = 4096; - - CU_ASSERT(0 == nghttp2_submit_window_update(session, NGHTTP2_FLAG_NONE, 2, - stream->recv_window_size + 1)); - CU_ASSERT(NGHTTP2_INITIAL_WINDOW_SIZE + 1 == stream->local_window_size); - CU_ASSERT(0 == stream->recv_window_size); - item = nghttp2_session_get_next_ob_item(session); - CU_ASSERT(NGHTTP2_WINDOW_UPDATE == item->frame.hd.type); - CU_ASSERT(4097 == item->frame.window_update.window_size_increment); - - CU_ASSERT(0 == nghttp2_session_send(session)); - - /* Let's decrement local window size */ - stream->recv_window_size = 4096; - CU_ASSERT(0 == nghttp2_submit_window_update(session, NGHTTP2_FLAG_NONE, 2, - -stream->local_window_size / 2)); - CU_ASSERT(32768 == stream->local_window_size); - CU_ASSERT(-28672 == stream->recv_window_size); - CU_ASSERT(32768 == stream->recv_reduction); - - item = nghttp2_session_get_next_ob_item(session); - CU_ASSERT(item == NULL); - - /* Increase local window size */ - CU_ASSERT(0 == - nghttp2_submit_window_update(session, NGHTTP2_FLAG_NONE, 2, 16384)); - CU_ASSERT(49152 == stream->local_window_size); - CU_ASSERT(-12288 == stream->recv_window_size); - CU_ASSERT(16384 == stream->recv_reduction); - CU_ASSERT(NULL == nghttp2_session_get_next_ob_item(session)); - - CU_ASSERT(NGHTTP2_ERR_FLOW_CONTROL == - nghttp2_submit_window_update(session, NGHTTP2_FLAG_NONE, 2, - NGHTTP2_MAX_WINDOW_SIZE)); - - CU_ASSERT(0 == nghttp2_session_send(session)); - - /* Check connection-level flow control */ - session->recv_window_size = 4096; - CU_ASSERT(0 == nghttp2_submit_window_update(session, NGHTTP2_FLAG_NONE, 0, - session->recv_window_size + 1)); - CU_ASSERT(NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE + 1 == - session->local_window_size); - CU_ASSERT(0 == session->recv_window_size); - item = nghttp2_session_get_next_ob_item(session); - CU_ASSERT(NGHTTP2_WINDOW_UPDATE == item->frame.hd.type); - CU_ASSERT(4097 == item->frame.window_update.window_size_increment); - - CU_ASSERT(0 == nghttp2_session_send(session)); - - /* Go decrement part */ - session->recv_window_size = 4096; - CU_ASSERT(0 == nghttp2_submit_window_update(session, NGHTTP2_FLAG_NONE, 0, - -session->local_window_size / 2)); - CU_ASSERT(32768 == session->local_window_size); - CU_ASSERT(-28672 == session->recv_window_size); - CU_ASSERT(32768 == session->recv_reduction); - item = nghttp2_session_get_next_ob_item(session); - CU_ASSERT(item == NULL); - - /* Increase local window size */ - CU_ASSERT(0 == - nghttp2_submit_window_update(session, NGHTTP2_FLAG_NONE, 0, 16384)); - CU_ASSERT(49152 == session->local_window_size); - CU_ASSERT(-12288 == session->recv_window_size); - CU_ASSERT(16384 == session->recv_reduction); - CU_ASSERT(NULL == nghttp2_session_get_next_ob_item(session)); - - CU_ASSERT(NGHTTP2_ERR_FLOW_CONTROL == - nghttp2_submit_window_update(session, NGHTTP2_FLAG_NONE, 0, - NGHTTP2_MAX_WINDOW_SIZE)); - - nghttp2_session_del(session); -} - -void test_nghttp2_submit_shutdown_notice(void) { - nghttp2_session *session; - nghttp2_session_callbacks callbacks; - my_user_data ud; - - memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); - callbacks.send_callback = null_send_callback; - callbacks.on_frame_send_callback = on_frame_send_callback; - callbacks.on_frame_not_send_callback = on_frame_not_send_callback; - - nghttp2_session_server_new(&session, &callbacks, &ud); - - CU_ASSERT(0 == nghttp2_submit_shutdown_notice(session)); - - ud.frame_send_cb_called = 0; - - nghttp2_session_send(session); - - CU_ASSERT(1 == ud.frame_send_cb_called); - CU_ASSERT(NGHTTP2_GOAWAY == ud.sent_frame_type); - CU_ASSERT((1u << 31) - 1 == session->local_last_stream_id); - - /* After another GOAWAY, nghttp2_submit_shutdown_notice() is - noop. */ - CU_ASSERT(0 == nghttp2_session_terminate_session(session, NGHTTP2_NO_ERROR)); - - ud.frame_send_cb_called = 0; - - nghttp2_session_send(session); - - CU_ASSERT(1 == ud.frame_send_cb_called); - CU_ASSERT(NGHTTP2_GOAWAY == ud.sent_frame_type); - CU_ASSERT(0 == session->local_last_stream_id); - - CU_ASSERT(0 == nghttp2_submit_shutdown_notice(session)); - - ud.frame_send_cb_called = 0; - ud.frame_not_send_cb_called = 0; - - nghttp2_session_send(session); - - CU_ASSERT(0 == ud.frame_send_cb_called); - CU_ASSERT(0 == ud.frame_not_send_cb_called); - - nghttp2_session_del(session); - - /* Using nghttp2_submit_shutdown_notice() with client side session - is error */ - nghttp2_session_client_new(&session, &callbacks, NULL); - - CU_ASSERT(NGHTTP2_ERR_INVALID_STATE == - nghttp2_submit_shutdown_notice(session)); - - nghttp2_session_del(session); -} - -void test_nghttp2_submit_invalid_nv(void) { - nghttp2_session *session; - nghttp2_session_callbacks callbacks; - nghttp2_nv empty_name_nv[] = {MAKE_NV("Version", "HTTP/1.1"), - MAKE_NV("", "empty name")}; - - /* Now invalid header name/value pair in HTTP/1.1 is accepted in - nghttp2 */ - - memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); - - CU_ASSERT(0 == nghttp2_session_server_new(&session, &callbacks, NULL)); - - /* nghttp2_submit_request */ - CU_ASSERT(0 < nghttp2_submit_request(session, NULL, empty_name_nv, - ARRLEN(empty_name_nv), NULL, NULL)); - - /* nghttp2_submit_response */ - CU_ASSERT(0 == nghttp2_submit_response(session, 2, empty_name_nv, - ARRLEN(empty_name_nv), NULL)); - - /* nghttp2_submit_headers */ - CU_ASSERT(0 < nghttp2_submit_headers(session, NGHTTP2_FLAG_NONE, -1, NULL, - empty_name_nv, ARRLEN(empty_name_nv), - NULL)); - - /* nghttp2_submit_push_promise */ - open_stream(session, 1); - - CU_ASSERT(0 < nghttp2_submit_push_promise(session, NGHTTP2_FLAG_NONE, 1, - empty_name_nv, - ARRLEN(empty_name_nv), NULL)); - - nghttp2_session_del(session); -} - -void test_nghttp2_session_open_stream(void) { - nghttp2_session *session; - nghttp2_session_callbacks callbacks; - nghttp2_stream *stream; - nghttp2_priority_spec pri_spec; - - memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); - nghttp2_session_server_new(&session, &callbacks, NULL); - - nghttp2_priority_spec_init(&pri_spec, 0, 245, 0); - - stream = nghttp2_session_open_stream(session, 1, NGHTTP2_STREAM_FLAG_NONE, - &pri_spec, NGHTTP2_STREAM_OPENED, NULL); - CU_ASSERT(1 == session->num_incoming_streams); - CU_ASSERT(0 == session->num_outgoing_streams); - CU_ASSERT(NGHTTP2_STREAM_OPENED == stream->state); - CU_ASSERT(245 == stream->weight); - CU_ASSERT(NULL == stream->dep_prev); - CU_ASSERT(NGHTTP2_SHUT_NONE == stream->shut_flags); - - stream = nghttp2_session_open_stream(session, 2, NGHTTP2_STREAM_FLAG_NONE, - &pri_spec_default, - NGHTTP2_STREAM_OPENING, NULL); - CU_ASSERT(1 == session->num_incoming_streams); - CU_ASSERT(1 == session->num_outgoing_streams); - CU_ASSERT(NULL == stream->dep_prev); - CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT == stream->weight); - CU_ASSERT(NGHTTP2_SHUT_NONE == stream->shut_flags); - - stream = nghttp2_session_open_stream(session, 4, NGHTTP2_STREAM_FLAG_NONE, - &pri_spec_default, - NGHTTP2_STREAM_RESERVED, NULL); - CU_ASSERT(1 == session->num_incoming_streams); - CU_ASSERT(1 == session->num_outgoing_streams); - CU_ASSERT(NULL == stream->dep_prev); - CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT == stream->weight); - CU_ASSERT(NGHTTP2_SHUT_RD == stream->shut_flags); - - nghttp2_priority_spec_init(&pri_spec, 1, 17, 1); - - stream = nghttp2_session_open_stream(session, 3, NGHTTP2_STREAM_FLAG_NONE, - &pri_spec, NGHTTP2_STREAM_OPENED, NULL); - CU_ASSERT(17 == stream->weight); - CU_ASSERT(1 == stream->dep_prev->stream_id); - - /* Dependency to idle stream */ - nghttp2_priority_spec_init(&pri_spec, 1000000007, 240, 1); - - stream = nghttp2_session_open_stream(session, 5, NGHTTP2_STREAM_FLAG_NONE, - &pri_spec, NGHTTP2_STREAM_OPENED, NULL); - CU_ASSERT(240 == stream->weight); - CU_ASSERT(1000000007 == stream->dep_prev->stream_id); - - stream = nghttp2_session_get_stream_raw(session, 1000000007); - - CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT == stream->weight); - CU_ASSERT(NULL != stream->root_next); - - /* Dependency to closed stream which is not in dependency tree */ - session->last_recv_stream_id = 7; - - nghttp2_priority_spec_init(&pri_spec, 7, 10, 0); - - stream = nghttp2_session_open_stream(session, 9, NGHTTP2_FLAG_NONE, &pri_spec, - NGHTTP2_STREAM_OPENED, NULL); - - CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT == stream->weight); - - nghttp2_session_del(session); - - nghttp2_session_client_new(&session, &callbacks, NULL); - stream = nghttp2_session_open_stream(session, 4, NGHTTP2_STREAM_FLAG_NONE, - &pri_spec_default, - NGHTTP2_STREAM_RESERVED, NULL); - CU_ASSERT(0 == session->num_incoming_streams); - CU_ASSERT(0 == session->num_outgoing_streams); - CU_ASSERT(NULL == stream->dep_prev); - CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT == stream->weight); - CU_ASSERT(NGHTTP2_SHUT_WR == stream->shut_flags); - - nghttp2_session_del(session); -} - -void test_nghttp2_session_open_stream_with_idle_stream_dep(void) { - nghttp2_session *session; - nghttp2_session_callbacks callbacks; - nghttp2_stream *stream; - nghttp2_priority_spec pri_spec; - - memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); - nghttp2_session_server_new(&session, &callbacks, NULL); - - /* Dependency to idle stream */ - nghttp2_priority_spec_init(&pri_spec, 101, 245, 0); - - stream = nghttp2_session_open_stream(session, 1, NGHTTP2_STREAM_FLAG_NONE, - &pri_spec, NGHTTP2_STREAM_OPENED, NULL); - - CU_ASSERT(245 == stream->weight); - CU_ASSERT(101 == stream->dep_prev->stream_id); - - stream = nghttp2_session_get_stream_raw(session, 101); - - CU_ASSERT(NGHTTP2_STREAM_IDLE == stream->state); - CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT == stream->weight); - - nghttp2_priority_spec_init(&pri_spec, 211, 1, 0); - - /* stream 101 was already created as idle. */ - stream = nghttp2_session_open_stream(session, 101, NGHTTP2_STREAM_FLAG_NONE, - &pri_spec, NGHTTP2_STREAM_OPENED, NULL); - - CU_ASSERT(1 == stream->weight); - CU_ASSERT(211 == stream->dep_prev->stream_id); - - stream = nghttp2_session_get_stream_raw(session, 211); - - CU_ASSERT(NGHTTP2_STREAM_IDLE == stream->state); - CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT == stream->weight); - - nghttp2_session_del(session); -} - -void test_nghttp2_session_get_next_ob_item(void) { - nghttp2_session *session; - nghttp2_session_callbacks callbacks; - nghttp2_priority_spec pri_spec; - - memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); - callbacks.send_callback = null_send_callback; - - nghttp2_session_server_new(&session, &callbacks, NULL); - session->remote_settings.max_concurrent_streams = 2; - - CU_ASSERT(NULL == nghttp2_session_get_next_ob_item(session)); - nghttp2_submit_ping(session, NGHTTP2_FLAG_NONE, NULL); - CU_ASSERT(NGHTTP2_PING == - nghttp2_session_get_next_ob_item(session)->frame.hd.type); - - nghttp2_submit_request(session, NULL, NULL, 0, NULL, NULL); - CU_ASSERT(NGHTTP2_PING == - nghttp2_session_get_next_ob_item(session)->frame.hd.type); - - CU_ASSERT(0 == nghttp2_session_send(session)); - CU_ASSERT(NULL == nghttp2_session_get_next_ob_item(session)); - - /* Incoming stream does not affect the number of outgoing max - concurrent streams. */ - nghttp2_session_open_stream(session, 1, NGHTTP2_STREAM_FLAG_NONE, - &pri_spec_default, NGHTTP2_STREAM_OPENING, NULL); - - nghttp2_priority_spec_init(&pri_spec, 0, NGHTTP2_MAX_WEIGHT, 0); - - nghttp2_submit_request(session, &pri_spec, NULL, 0, NULL, NULL); - CU_ASSERT(NGHTTP2_HEADERS == - nghttp2_session_get_next_ob_item(session)->frame.hd.type); - CU_ASSERT(0 == nghttp2_session_send(session)); - - nghttp2_submit_request(session, &pri_spec, NULL, 0, NULL, NULL); - CU_ASSERT(NULL == nghttp2_session_get_next_ob_item(session)); - - session->remote_settings.max_concurrent_streams = 3; - - CU_ASSERT(NGHTTP2_HEADERS == - nghttp2_session_get_next_ob_item(session)->frame.hd.type); - - nghttp2_session_del(session); -} - -void test_nghttp2_session_pop_next_ob_item(void) { - nghttp2_session *session; - nghttp2_session_callbacks callbacks; - nghttp2_outbound_item *item; - nghttp2_priority_spec pri_spec; - nghttp2_stream *stream; - nghttp2_mem *mem; - - mem = nghttp2_mem_default(); - memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); - callbacks.send_callback = null_send_callback; - - nghttp2_session_server_new(&session, &callbacks, NULL); - session->remote_settings.max_concurrent_streams = 1; - - CU_ASSERT(NULL == nghttp2_session_pop_next_ob_item(session)); - - nghttp2_submit_ping(session, NGHTTP2_FLAG_NONE, NULL); - - nghttp2_priority_spec_init(&pri_spec, 0, 254, 0); - - nghttp2_submit_request(session, &pri_spec, NULL, 0, NULL, NULL); - - item = nghttp2_session_pop_next_ob_item(session); - CU_ASSERT(NGHTTP2_PING == item->frame.hd.type); - nghttp2_outbound_item_free(item, mem); - mem->free(item, NULL); - - item = nghttp2_session_pop_next_ob_item(session); - CU_ASSERT(NGHTTP2_HEADERS == item->frame.hd.type); - nghttp2_outbound_item_free(item, mem); - mem->free(item, NULL); - - CU_ASSERT(NULL == nghttp2_session_pop_next_ob_item(session)); - - /* Incoming stream does not affect the number of outgoing max - concurrent streams. */ - nghttp2_session_open_stream(session, 1, NGHTTP2_STREAM_FLAG_NONE, - &pri_spec_default, NGHTTP2_STREAM_OPENING, NULL); - /* In-flight outgoing stream */ - nghttp2_session_open_stream(session, 4, NGHTTP2_STREAM_FLAG_NONE, - &pri_spec_default, NGHTTP2_STREAM_OPENING, NULL); - - nghttp2_priority_spec_init(&pri_spec, 0, NGHTTP2_MAX_WEIGHT, 0); - - nghttp2_submit_request(session, &pri_spec, NULL, 0, NULL, NULL); - nghttp2_submit_response(session, 1, NULL, 0, NULL); - - item = nghttp2_session_pop_next_ob_item(session); - CU_ASSERT(NGHTTP2_HEADERS == item->frame.hd.type); - CU_ASSERT(1 == item->frame.hd.stream_id); - - stream = nghttp2_session_get_stream(session, 1); - - nghttp2_stream_detach_item(stream, session); - - nghttp2_outbound_item_free(item, mem); - mem->free(item, NULL); - - CU_ASSERT(NULL == nghttp2_session_pop_next_ob_item(session)); - - session->remote_settings.max_concurrent_streams = 2; - - item = nghttp2_session_pop_next_ob_item(session); - CU_ASSERT(NGHTTP2_HEADERS == item->frame.hd.type); - nghttp2_outbound_item_free(item, mem); - mem->free(item, NULL); - - nghttp2_session_del(session); - - /* Check that push reply HEADERS are queued into ob_ss_pq */ - nghttp2_session_server_new(&session, &callbacks, NULL); - session->remote_settings.max_concurrent_streams = 0; - nghttp2_session_open_stream(session, 2, NGHTTP2_STREAM_FLAG_NONE, - &pri_spec_default, NGHTTP2_STREAM_RESERVED, NULL); - CU_ASSERT(0 == nghttp2_submit_headers(session, NGHTTP2_FLAG_END_STREAM, 2, - NULL, NULL, 0, NULL)); - CU_ASSERT(NULL == nghttp2_session_pop_next_ob_item(session)); - CU_ASSERT(1 == nghttp2_outbound_queue_size(&session->ob_syn)); - nghttp2_session_del(session); -} - -void test_nghttp2_session_reply_fail(void) { - nghttp2_session *session; - nghttp2_session_callbacks callbacks; - nghttp2_data_provider data_prd; - my_user_data ud; - - memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); - callbacks.send_callback = fail_send_callback; - - data_prd.read_callback = fixed_length_data_source_read_callback; - ud.data_source_length = 4 * 1024; - CU_ASSERT(0 == nghttp2_session_server_new(&session, &callbacks, &ud)); - nghttp2_session_open_stream(session, 1, NGHTTP2_STREAM_FLAG_NONE, - &pri_spec_default, NGHTTP2_STREAM_OPENING, NULL); - CU_ASSERT(0 == nghttp2_submit_response(session, 1, NULL, 0, &data_prd)); - CU_ASSERT(NGHTTP2_ERR_CALLBACK_FAILURE == nghttp2_session_send(session)); - nghttp2_session_del(session); -} - -void test_nghttp2_session_max_concurrent_streams(void) { - nghttp2_session *session; - nghttp2_session_callbacks callbacks; - nghttp2_frame frame; - nghttp2_outbound_item *item; - nghttp2_mem *mem; - - mem = nghttp2_mem_default(); - memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); - callbacks.send_callback = null_send_callback; - - nghttp2_session_server_new(&session, &callbacks, NULL); - nghttp2_session_open_stream(session, 1, NGHTTP2_STREAM_FLAG_NONE, - &pri_spec_default, NGHTTP2_STREAM_OPENED, NULL); - - /* Check un-ACKed SETTINGS_MAX_CONCURRENT_STREAMS */ - nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_END_HEADERS, 3, - NGHTTP2_HCAT_HEADERS, NULL, NULL, 0); - session->pending_local_max_concurrent_stream = 1; - - CU_ASSERT(NGHTTP2_ERR_IGN_HEADER_BLOCK == - nghttp2_session_on_request_headers_received(session, &frame)); - - item = nghttp2_outbound_queue_top(&session->ob_reg); - CU_ASSERT(NGHTTP2_RST_STREAM == item->frame.hd.type); - CU_ASSERT(NGHTTP2_REFUSED_STREAM == item->frame.rst_stream.error_code); - - CU_ASSERT(0 == nghttp2_session_send(session)); - - /* Check ACKed SETTINGS_MAX_CONCURRENT_STREAMS */ - session->local_settings.max_concurrent_streams = 1; - frame.hd.stream_id = 5; - - CU_ASSERT(NGHTTP2_ERR_IGN_HEADER_BLOCK == - nghttp2_session_on_request_headers_received(session, &frame)); - - item = nghttp2_outbound_queue_top(&session->ob_reg); - CU_ASSERT(NGHTTP2_GOAWAY == item->frame.hd.type); - CU_ASSERT(NGHTTP2_PROTOCOL_ERROR == item->frame.goaway.error_code); - - nghttp2_frame_headers_free(&frame.headers, mem); - nghttp2_session_del(session); -} - -/* - * Check that on_stream_close_callback is called when server pushed - * HEADERS have NGHTTP2_FLAG_END_STREAM. - */ -void test_nghttp2_session_stream_close_on_headers_push(void) { - /* nghttp2_session *session; */ - /* nghttp2_session_callbacks callbacks; */ - /* const char *nv[] = { NULL }; */ - /* my_user_data ud; */ - /* nghttp2_frame frame; */ - - /* memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); */ - /* callbacks.on_stream_close_callback = */ - /* no_stream_user_data_stream_close_callback; */ - /* ud.stream_close_cb_called = 0; */ - - /* nghttp2_session_client_new(&session, NGHTTP2_PROTO_SPDY2, &callbacks, &ud); - */ - /* nghttp2_session_open_stream(session, 1, NGHTTP2_CTRL_FLAG_NONE, 3, */ - /* NGHTTP2_STREAM_OPENING, NULL); */ - /* nghttp2_frame_syn_stream_init(&frame.syn_stream, NGHTTP2_PROTO_SPDY2, */ - /* NGHTTP2_CTRL_FLAG_FIN | */ - /* NGHTTP2_CTRL_FLAG_UNIDIRECTIONAL, */ - /* 2, 1, 3, dup_nv(nv)); */ - - /* CU_ASSERT(0 == nghttp2_session_on_request_headers_received(session, - * &frame)); */ - - /* nghttp2_frame_syn_stream_free(&frame.syn_stream); */ - /* nghttp2_session_del(session); */ -} - -void test_nghttp2_session_stop_data_with_rst_stream(void) { - nghttp2_session *session; - nghttp2_session_callbacks callbacks; - my_user_data ud; - nghttp2_data_provider data_prd; - nghttp2_frame frame; - - memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); - callbacks.on_frame_send_callback = on_frame_send_callback; - callbacks.send_callback = block_count_send_callback; - data_prd.read_callback = fixed_length_data_source_read_callback; - - ud.frame_send_cb_called = 0; - ud.data_source_length = NGHTTP2_DATA_PAYLOADLEN * 4; - - nghttp2_session_server_new(&session, &callbacks, &ud); - nghttp2_session_open_stream(session, 1, NGHTTP2_STREAM_FLAG_NONE, - &pri_spec_default, NGHTTP2_STREAM_OPENING, NULL); - nghttp2_submit_response(session, 1, NULL, 0, &data_prd); - - ud.block_count = 2; - /* Sends response HEADERS + DATA[0] */ - CU_ASSERT(0 == nghttp2_session_send(session)); - CU_ASSERT(NGHTTP2_DATA == ud.sent_frame_type); - /* data for DATA[1] is read from data_prd but it is not sent */ - CU_ASSERT(ud.data_source_length == NGHTTP2_DATA_PAYLOADLEN * 2); - - nghttp2_frame_rst_stream_init(&frame.rst_stream, 1, NGHTTP2_CANCEL); - CU_ASSERT(0 == nghttp2_session_on_rst_stream_received(session, &frame)); - nghttp2_frame_rst_stream_free(&frame.rst_stream); - - /* Big enough number to send all DATA frames potentially. */ - ud.block_count = 100; - /* Nothing will be sent in the following call. */ - CU_ASSERT(0 == nghttp2_session_send(session)); - /* With RST_STREAM, stream is canceled and further DATA on that - stream are not sent. */ - CU_ASSERT(ud.data_source_length == NGHTTP2_DATA_PAYLOADLEN * 2); - - CU_ASSERT(NULL == nghttp2_session_get_stream(session, 1)); - - nghttp2_session_del(session); -} - -void test_nghttp2_session_defer_data(void) { - nghttp2_session *session; - nghttp2_session_callbacks callbacks; - my_user_data ud; - nghttp2_data_provider data_prd; - nghttp2_outbound_item *item; - nghttp2_stream *stream; - - memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); - callbacks.on_frame_send_callback = on_frame_send_callback; - callbacks.send_callback = block_count_send_callback; - data_prd.read_callback = defer_data_source_read_callback; - - ud.frame_send_cb_called = 0; - ud.data_source_length = NGHTTP2_DATA_PAYLOADLEN * 4; - - nghttp2_session_server_new(&session, &callbacks, &ud); - stream = nghttp2_session_open_stream(session, 1, NGHTTP2_STREAM_FLAG_NONE, - &pri_spec_default, - NGHTTP2_STREAM_OPENING, NULL); - - session->remote_window_size = 1 << 20; - stream->remote_window_size = 1 << 20; - - nghttp2_submit_response(session, 1, NULL, 0, &data_prd); - - ud.block_count = 1; - /* Sends HEADERS reply */ - CU_ASSERT(0 == nghttp2_session_send(session)); - CU_ASSERT(NGHTTP2_HEADERS == ud.sent_frame_type); - /* No data is read */ - CU_ASSERT(ud.data_source_length == NGHTTP2_DATA_PAYLOADLEN * 4); - - ud.block_count = 1; - nghttp2_submit_ping(session, NGHTTP2_FLAG_NONE, NULL); - /* Sends PING */ - CU_ASSERT(0 == nghttp2_session_send(session)); - CU_ASSERT(NGHTTP2_PING == ud.sent_frame_type); - - /* Resume deferred DATA */ - CU_ASSERT(0 == nghttp2_session_resume_data(session, 1)); - item = (nghttp2_outbound_item *)nghttp2_pq_top(&session->ob_da_pq); - item->aux_data.data.data_prd.read_callback = - fixed_length_data_source_read_callback; - ud.block_count = 1; - /* Reads 2 DATA chunks */ - CU_ASSERT(0 == nghttp2_session_send(session)); - CU_ASSERT(ud.data_source_length == NGHTTP2_DATA_PAYLOADLEN * 2); - - /* Deferred again */ - item->aux_data.data.data_prd.read_callback = defer_data_source_read_callback; - /* This is needed since 16KiB block is already read and waiting to be - sent. No read_callback invocation. */ - ud.block_count = 1; - CU_ASSERT(0 == nghttp2_session_send(session)); - CU_ASSERT(ud.data_source_length == NGHTTP2_DATA_PAYLOADLEN * 2); - - /* Resume deferred DATA */ - CU_ASSERT(0 == nghttp2_session_resume_data(session, 1)); - item = (nghttp2_outbound_item *)nghttp2_pq_top(&session->ob_da_pq); - item->aux_data.data.data_prd.read_callback = - fixed_length_data_source_read_callback; - ud.block_count = 1; - /* Reads 2 16KiB blocks */ - CU_ASSERT(0 == nghttp2_session_send(session)); - CU_ASSERT(ud.data_source_length == 0); - - nghttp2_session_del(session); -} - -void test_nghttp2_session_flow_control(void) { - nghttp2_session *session; - nghttp2_session_callbacks callbacks; - my_user_data ud; - nghttp2_data_provider data_prd; - nghttp2_frame frame; - nghttp2_stream *stream; - int32_t new_initial_window_size; - nghttp2_settings_entry iv[1]; - nghttp2_frame settings_frame; - nghttp2_mem *mem; - - mem = nghttp2_mem_default(); - memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); - callbacks.send_callback = fixed_bytes_send_callback; - callbacks.on_frame_send_callback = on_frame_send_callback; - data_prd.read_callback = fixed_length_data_source_read_callback; - - ud.frame_send_cb_called = 0; - ud.data_source_length = 128 * 1024; - /* Use smaller emission count so that we can check outbound flow - control window calculation is correct. */ - ud.fixed_sendlen = 2 * 1024; - - /* Initial window size to 64KiB - 1*/ - nghttp2_session_client_new(&session, &callbacks, &ud); - /* Change it to 64KiB for easy calculation */ - session->remote_window_size = 64 * 1024; - session->remote_settings.initial_window_size = 64 * 1024; - - nghttp2_submit_request(session, NULL, NULL, 0, &data_prd, NULL); - - /* Sends 64KiB - 1 data */ - CU_ASSERT(0 == nghttp2_session_send(session)); - CU_ASSERT(64 * 1024 == ud.data_source_length); - - /* Back 32KiB in stream window */ - nghttp2_frame_window_update_init(&frame.window_update, NGHTTP2_FLAG_NONE, 1, - 32 * 1024); - nghttp2_session_on_window_update_received(session, &frame); - - /* Send nothing because of connection-level window */ - CU_ASSERT(0 == nghttp2_session_send(session)); - CU_ASSERT(64 * 1024 == ud.data_source_length); - - /* Back 32KiB in connection-level window */ - frame.hd.stream_id = 0; - nghttp2_session_on_window_update_received(session, &frame); - - /* Sends another 32KiB data */ - CU_ASSERT(0 == nghttp2_session_send(session)); - CU_ASSERT(32 * 1024 == ud.data_source_length); - - stream = nghttp2_session_get_stream(session, 1); - /* Change initial window size to 16KiB. The window_size becomes - negative. */ - new_initial_window_size = 16 * 1024; - stream->remote_window_size = - new_initial_window_size - (session->remote_settings.initial_window_size - - stream->remote_window_size); - session->remote_settings.initial_window_size = new_initial_window_size; - CU_ASSERT(-48 * 1024 == stream->remote_window_size); - - /* Back 48KiB to stream window */ - frame.hd.stream_id = 1; - frame.window_update.window_size_increment = 48 * 1024; - nghttp2_session_on_window_update_received(session, &frame); - - /* Nothing is sent because window_size is 0 */ - CU_ASSERT(0 == nghttp2_session_send(session)); - CU_ASSERT(32 * 1024 == ud.data_source_length); - - /* Back 16KiB in stream window */ - frame.hd.stream_id = 1; - frame.window_update.window_size_increment = 16 * 1024; - nghttp2_session_on_window_update_received(session, &frame); - - /* Back 24KiB in connection-level window */ - frame.hd.stream_id = 0; - frame.window_update.window_size_increment = 24 * 1024; - nghttp2_session_on_window_update_received(session, &frame); - - /* Sends another 16KiB data */ - CU_ASSERT(0 == nghttp2_session_send(session)); - CU_ASSERT(16 * 1024 == ud.data_source_length); - - /* Increase initial window size to 32KiB */ - iv[0].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE; - iv[0].value = 32 * 1024; - - nghttp2_frame_settings_init(&settings_frame.settings, NGHTTP2_FLAG_NONE, - dup_iv(iv, 1), 1); - nghttp2_session_on_settings_received(session, &settings_frame, 1); - nghttp2_frame_settings_free(&settings_frame.settings, mem); - - /* Sends another 8KiB data */ - CU_ASSERT(0 == nghttp2_session_send(session)); - CU_ASSERT(8 * 1024 == ud.data_source_length); - - /* Back 8KiB in connection-level window */ - frame.hd.stream_id = 0; - frame.window_update.window_size_increment = 8 * 1024; - nghttp2_session_on_window_update_received(session, &frame); - - /* Sends last 8KiB data */ - CU_ASSERT(0 == nghttp2_session_send(session)); - CU_ASSERT(0 == ud.data_source_length); - CU_ASSERT(nghttp2_session_get_stream(session, 1)->shut_flags & - NGHTTP2_SHUT_WR); - - nghttp2_frame_window_update_free(&frame.window_update); - nghttp2_session_del(session); -} - -void test_nghttp2_session_flow_control_data_recv(void) { - nghttp2_session *session; - nghttp2_session_callbacks callbacks; - uint8_t data[64 * 1024 + 16]; - nghttp2_frame_hd hd; - nghttp2_outbound_item *item; - nghttp2_stream *stream; - - memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); - callbacks.send_callback = null_send_callback; - - /* Initial window size to 64KiB - 1*/ - nghttp2_session_client_new(&session, &callbacks, NULL); - - stream = nghttp2_session_open_stream(session, 1, NGHTTP2_STREAM_FLAG_NONE, - &pri_spec_default, NGHTTP2_STREAM_OPENED, - NULL); - - session->next_stream_id = 3; - - nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_WR); - - session->local_window_size = NGHTTP2_MAX_PAYLOADLEN; - stream->local_window_size = NGHTTP2_MAX_PAYLOADLEN; - - /* Create DATA frame */ - memset(data, 0, sizeof(data)); - nghttp2_frame_hd_init(&hd, NGHTTP2_MAX_PAYLOADLEN, NGHTTP2_DATA, - NGHTTP2_FLAG_END_STREAM, 1); - - nghttp2_frame_pack_frame_hd(data, &hd); - CU_ASSERT(NGHTTP2_MAX_PAYLOADLEN + NGHTTP2_FRAME_HDLEN == - nghttp2_session_mem_recv(session, data, NGHTTP2_MAX_PAYLOADLEN + - NGHTTP2_FRAME_HDLEN)); - - item = nghttp2_session_get_next_ob_item(session); - /* Since this is the last frame, stream-level WINDOW_UPDATE is not - issued, but connection-level is. */ - CU_ASSERT(NGHTTP2_WINDOW_UPDATE == item->frame.hd.type); - CU_ASSERT(0 == item->frame.hd.stream_id); - CU_ASSERT(NGHTTP2_MAX_PAYLOADLEN == - item->frame.window_update.window_size_increment); - - CU_ASSERT(0 == nghttp2_session_send(session)); - - /* Receive DATA for closed stream. They are still subject to under - connection-level flow control, since this situation arises when - RST_STREAM is issued by the remote, but the local side keeps - sending DATA frames. Without calculating connection-level window, - the subsequent flow control gets confused. */ - CU_ASSERT(NGHTTP2_MAX_PAYLOADLEN + NGHTTP2_FRAME_HDLEN == - nghttp2_session_mem_recv(session, data, NGHTTP2_MAX_PAYLOADLEN + - NGHTTP2_FRAME_HDLEN)); - - item = nghttp2_session_get_next_ob_item(session); - CU_ASSERT(NGHTTP2_WINDOW_UPDATE == item->frame.hd.type); - CU_ASSERT(0 == item->frame.hd.stream_id); - CU_ASSERT(NGHTTP2_MAX_PAYLOADLEN == - item->frame.window_update.window_size_increment); - - nghttp2_session_del(session); -} - -void test_nghttp2_session_flow_control_data_with_padding_recv(void) { - nghttp2_session *session; - nghttp2_session_callbacks callbacks; - uint8_t data[1024]; - nghttp2_frame_hd hd; - nghttp2_stream *stream; - nghttp2_option *option; - - memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); - callbacks.send_callback = null_send_callback; - - nghttp2_option_new(&option); - /* Disable auto window update so that we can check padding is - consumed automatically */ - nghttp2_option_set_no_auto_window_update(option, 1); - - /* Initial window size to 64KiB - 1*/ - nghttp2_session_client_new2(&session, &callbacks, NULL, option); - - nghttp2_option_del(option); - - stream = nghttp2_session_open_stream(session, 1, NGHTTP2_STREAM_FLAG_NONE, - &pri_spec_default, NGHTTP2_STREAM_OPENED, - NULL); - - /* Create DATA frame */ - memset(data, 0, sizeof(data)); - nghttp2_frame_hd_init(&hd, 357, NGHTTP2_DATA, - NGHTTP2_FLAG_END_STREAM | NGHTTP2_FLAG_PADDED, 1); - - nghttp2_frame_pack_frame_hd(data, &hd); - /* Set Pad Length field, which itself is padding */ - data[NGHTTP2_FRAME_HDLEN] = 255; - - CU_ASSERT( - (ssize_t)(NGHTTP2_FRAME_HDLEN + hd.length) == - nghttp2_session_mem_recv(session, data, NGHTTP2_FRAME_HDLEN + hd.length)); - - CU_ASSERT((int32_t)hd.length == session->recv_window_size); - CU_ASSERT((int32_t)hd.length == stream->recv_window_size); - CU_ASSERT(256 == session->consumed_size); - CU_ASSERT(256 == stream->consumed_size); - - nghttp2_session_del(session); -} - -void test_nghttp2_session_data_read_temporal_failure(void) { - nghttp2_session *session; - nghttp2_session_callbacks callbacks; - my_user_data ud; - nghttp2_data_provider data_prd; - nghttp2_frame frame; - nghttp2_stream *stream; - size_t data_size = 128 * 1024; - - memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); - callbacks.send_callback = null_send_callback; - callbacks.on_frame_send_callback = on_frame_send_callback; - data_prd.read_callback = fixed_length_data_source_read_callback; - - ud.data_source_length = data_size; - - /* Initial window size is 64KiB - 1 */ - nghttp2_session_client_new(&session, &callbacks, &ud); - nghttp2_submit_request(session, NULL, NULL, 0, &data_prd, NULL); - - /* Sends NGHTTP2_INITIAL_WINDOW_SIZE data, assuming, it is equal to - or smaller than NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE */ - CU_ASSERT(0 == nghttp2_session_send(session)); - CU_ASSERT(data_size - NGHTTP2_INITIAL_WINDOW_SIZE == ud.data_source_length); - - stream = nghttp2_session_get_stream(session, 1); - CU_ASSERT(nghttp2_stream_check_deferred_by_flow_control(stream)); - CU_ASSERT(NGHTTP2_DATA == stream->item->frame.hd.type); - - stream->item->aux_data.data.data_prd.read_callback = - temporal_failure_data_source_read_callback; - - /* Back NGHTTP2_INITIAL_WINDOW_SIZE to both connection-level and - stream-wise window */ - nghttp2_frame_window_update_init(&frame.window_update, NGHTTP2_FLAG_NONE, 1, - NGHTTP2_INITIAL_WINDOW_SIZE); - nghttp2_session_on_window_update_received(session, &frame); - frame.hd.stream_id = 0; - nghttp2_session_on_window_update_received(session, &frame); - nghttp2_frame_window_update_free(&frame.window_update); - - /* Sending data will fail (soft fail) and treated as stream error */ - ud.frame_send_cb_called = 0; - CU_ASSERT(0 == nghttp2_session_send(session)); - CU_ASSERT(data_size - NGHTTP2_INITIAL_WINDOW_SIZE == ud.data_source_length); - - CU_ASSERT(1 == ud.frame_send_cb_called); - CU_ASSERT(NGHTTP2_RST_STREAM == ud.sent_frame_type); - - data_prd.read_callback = fail_data_source_read_callback; - nghttp2_submit_request(session, NULL, NULL, 0, &data_prd, NULL); - /* Sending data will fail (hard fail) and session tear down */ - CU_ASSERT(NGHTTP2_ERR_CALLBACK_FAILURE == nghttp2_session_send(session)); - - nghttp2_session_del(session); -} - -void test_nghttp2_session_on_stream_close(void) { - nghttp2_session *session; - nghttp2_session_callbacks callbacks; - my_user_data user_data; - nghttp2_stream *stream; - - memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); - callbacks.on_stream_close_callback = on_stream_close_callback; - user_data.stream_close_cb_called = 0; - - nghttp2_session_client_new(&session, &callbacks, &user_data); - stream = nghttp2_session_open_stream(session, 1, NGHTTP2_STREAM_FLAG_NONE, - &pri_spec_default, NGHTTP2_STREAM_OPENED, - &user_data); - CU_ASSERT(stream != NULL); - CU_ASSERT(nghttp2_session_close_stream(session, 1, NGHTTP2_NO_ERROR) == 0); - CU_ASSERT(user_data.stream_close_cb_called == 1); - nghttp2_session_del(session); -} - -void test_nghttp2_session_on_ctrl_not_send(void) { - nghttp2_session *session; - nghttp2_session_callbacks callbacks; - my_user_data user_data; - nghttp2_stream *stream; - - memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); - callbacks.on_frame_not_send_callback = on_frame_not_send_callback; - callbacks.send_callback = null_send_callback; - user_data.frame_not_send_cb_called = 0; - user_data.not_sent_frame_type = 0; - user_data.not_sent_error = 0; - - nghttp2_session_server_new(&session, &callbacks, &user_data); - stream = nghttp2_session_open_stream(session, 1, NGHTTP2_STREAM_FLAG_NONE, - &pri_spec_default, - NGHTTP2_STREAM_OPENING, &user_data); - - /* Check response HEADERS */ - /* Send bogus stream ID */ - CU_ASSERT(0 == nghttp2_submit_headers(session, NGHTTP2_FLAG_END_STREAM, 3, - NULL, NULL, 0, NULL)); - CU_ASSERT(0 == nghttp2_session_send(session)); - CU_ASSERT(1 == user_data.frame_not_send_cb_called); - CU_ASSERT(NGHTTP2_HEADERS == user_data.not_sent_frame_type); - CU_ASSERT(NGHTTP2_ERR_STREAM_CLOSED == user_data.not_sent_error); - - user_data.frame_not_send_cb_called = 0; - /* Shutdown transmission */ - stream->shut_flags |= NGHTTP2_SHUT_WR; - CU_ASSERT(0 == nghttp2_submit_headers(session, NGHTTP2_FLAG_END_STREAM, 1, - NULL, NULL, 0, NULL)); - CU_ASSERT(0 == nghttp2_session_send(session)); - CU_ASSERT(1 == user_data.frame_not_send_cb_called); - CU_ASSERT(NGHTTP2_HEADERS == user_data.not_sent_frame_type); - CU_ASSERT(NGHTTP2_ERR_STREAM_SHUT_WR == user_data.not_sent_error); - - stream->shut_flags = NGHTTP2_SHUT_NONE; - user_data.frame_not_send_cb_called = 0; - /* Queue RST_STREAM */ - CU_ASSERT(0 == nghttp2_submit_headers(session, NGHTTP2_FLAG_END_STREAM, 1, - NULL, NULL, 0, NULL)); - CU_ASSERT(0 == nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, 1, - NGHTTP2_INTERNAL_ERROR)); - CU_ASSERT(0 == nghttp2_session_send(session)); - CU_ASSERT(1 == user_data.frame_not_send_cb_called); - CU_ASSERT(NGHTTP2_HEADERS == user_data.not_sent_frame_type); - CU_ASSERT(NGHTTP2_ERR_STREAM_CLOSING == user_data.not_sent_error); - - nghttp2_session_del(session); - - /* Check request HEADERS */ - user_data.frame_not_send_cb_called = 0; - CU_ASSERT(nghttp2_session_client_new(&session, &callbacks, &user_data) == 0); - /* Maximum Stream ID is reached */ - session->next_stream_id = (1u << 31) + 1; - CU_ASSERT(NGHTTP2_ERR_STREAM_ID_NOT_AVAILABLE == - nghttp2_submit_headers(session, NGHTTP2_FLAG_END_STREAM, -1, NULL, - NULL, 0, NULL)); - - user_data.frame_not_send_cb_called = 0; - /* GOAWAY received */ - session->goaway_flags |= NGHTTP2_GOAWAY_RECV; - session->next_stream_id = 9; - - CU_ASSERT(0 < nghttp2_submit_headers(session, NGHTTP2_FLAG_END_STREAM, -1, - NULL, NULL, 0, NULL)); - CU_ASSERT(0 == nghttp2_session_send(session)); - CU_ASSERT(1 == user_data.frame_not_send_cb_called); - CU_ASSERT(NGHTTP2_HEADERS == user_data.not_sent_frame_type); - CU_ASSERT(NGHTTP2_ERR_START_STREAM_NOT_ALLOWED == user_data.not_sent_error); - - nghttp2_session_del(session); -} - -void test_nghttp2_session_get_outbound_queue_size(void) { - nghttp2_session *session; - nghttp2_session_callbacks callbacks; - - memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); - CU_ASSERT(0 == nghttp2_session_client_new(&session, &callbacks, NULL)); - CU_ASSERT(0 == nghttp2_session_get_outbound_queue_size(session)); - - CU_ASSERT(0 == nghttp2_submit_ping(session, NGHTTP2_FLAG_NONE, NULL)); - CU_ASSERT(1 == nghttp2_session_get_outbound_queue_size(session)); - - CU_ASSERT(0 == nghttp2_submit_goaway(session, NGHTTP2_FLAG_NONE, 2, - NGHTTP2_NO_ERROR, NULL, 0)); - CU_ASSERT(2 == nghttp2_session_get_outbound_queue_size(session)); - - nghttp2_session_del(session); -} - -void test_nghttp2_session_get_effective_local_window_size(void) { - nghttp2_session *session; - nghttp2_session_callbacks callbacks; - nghttp2_stream *stream; - - memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); - CU_ASSERT(0 == nghttp2_session_client_new(&session, &callbacks, NULL)); - - stream = nghttp2_session_open_stream(session, 1, NGHTTP2_STREAM_FLAG_NONE, - &pri_spec_default, NGHTTP2_STREAM_OPENED, - NULL); - - CU_ASSERT(NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE == - nghttp2_session_get_effective_local_window_size(session)); - CU_ASSERT(0 == nghttp2_session_get_effective_recv_data_length(session)); - - CU_ASSERT(NGHTTP2_INITIAL_WINDOW_SIZE == - nghttp2_session_get_stream_effective_local_window_size(session, 1)); - CU_ASSERT(0 == - nghttp2_session_get_stream_effective_recv_data_length(session, 1)); - - /* Check connection flow control */ - session->recv_window_size = 100; - nghttp2_submit_window_update(session, NGHTTP2_FLAG_NONE, 0, 1100); - - CU_ASSERT(NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE + 1000 == - nghttp2_session_get_effective_local_window_size(session)); - CU_ASSERT(0 == nghttp2_session_get_effective_recv_data_length(session)); - - nghttp2_submit_window_update(session, NGHTTP2_FLAG_NONE, 0, -50); - /* Now session->recv_window_size = -50 */ - CU_ASSERT(NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE + 950 == - nghttp2_session_get_effective_local_window_size(session)); - CU_ASSERT(0 == nghttp2_session_get_effective_recv_data_length(session)); - - session->recv_window_size += 50; - /* Now session->recv_window_size = 0 */ - nghttp2_submit_window_update(session, NGHTTP2_FLAG_NONE, 0, 100); - CU_ASSERT(NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE + 1050 == - nghttp2_session_get_effective_local_window_size(session)); - CU_ASSERT(50 == nghttp2_session_get_effective_recv_data_length(session)); - - /* Check stream flow control */ - stream->recv_window_size = 100; - nghttp2_submit_window_update(session, NGHTTP2_FLAG_NONE, 1, 1100); - - CU_ASSERT(NGHTTP2_INITIAL_WINDOW_SIZE + 1000 == - nghttp2_session_get_stream_effective_local_window_size(session, 1)); - CU_ASSERT(0 == - nghttp2_session_get_stream_effective_recv_data_length(session, 1)); - - nghttp2_submit_window_update(session, NGHTTP2_FLAG_NONE, 1, -50); - /* Now stream->recv_window_size = -50 */ - CU_ASSERT(NGHTTP2_INITIAL_WINDOW_SIZE + 950 == - nghttp2_session_get_stream_effective_local_window_size(session, 1)); - CU_ASSERT(0 == - nghttp2_session_get_stream_effective_recv_data_length(session, 1)); - - stream->recv_window_size += 50; - /* Now stream->recv_window_size = 0 */ - nghttp2_submit_window_update(session, NGHTTP2_FLAG_NONE, 1, 100); - CU_ASSERT(NGHTTP2_INITIAL_WINDOW_SIZE + 1050 == - nghttp2_session_get_stream_effective_local_window_size(session, 1)); - CU_ASSERT(50 == - nghttp2_session_get_stream_effective_recv_data_length(session, 1)); - - nghttp2_session_del(session); -} - -void test_nghttp2_session_set_option(void) { - nghttp2_session *session; - nghttp2_session_callbacks callbacks; - nghttp2_option *option; - - nghttp2_option_new(&option); - - nghttp2_option_set_no_auto_window_update(option, 1); - - memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); - nghttp2_session_client_new2(&session, &callbacks, NULL, option); - - CU_ASSERT(session->opt_flags & NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE); - - nghttp2_session_del(session); - - nghttp2_option_set_peer_max_concurrent_streams(option, 100); - - nghttp2_session_client_new2(&session, &callbacks, NULL, option); - - CU_ASSERT(100 == session->remote_settings.max_concurrent_streams); - nghttp2_session_del(session); - - nghttp2_option_del(option); -} - -void test_nghttp2_session_data_backoff_by_high_pri_frame(void) { - nghttp2_session *session; - nghttp2_session_callbacks callbacks; - my_user_data ud; - nghttp2_data_provider data_prd; - nghttp2_stream *stream; - - memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); - callbacks.send_callback = block_count_send_callback; - callbacks.on_frame_send_callback = on_frame_send_callback; - data_prd.read_callback = fixed_length_data_source_read_callback; - - ud.frame_send_cb_called = 0; - ud.data_source_length = NGHTTP2_DATA_PAYLOADLEN * 4; - - nghttp2_session_client_new(&session, &callbacks, &ud); - nghttp2_submit_request(session, NULL, NULL, 0, &data_prd, NULL); - - session->remote_window_size = 1 << 20; - - ud.block_count = 2; - /* Sends request HEADERS + DATA[0] */ - CU_ASSERT(0 == nghttp2_session_send(session)); - - stream = nghttp2_session_get_stream(session, 1); - stream->remote_window_size = 1 << 20; - - CU_ASSERT(NGHTTP2_DATA == ud.sent_frame_type); - /* data for DATA[1] is read from data_prd but it is not sent */ - CU_ASSERT(ud.data_source_length == NGHTTP2_DATA_PAYLOADLEN * 2); - - nghttp2_submit_ping(session, NGHTTP2_FLAG_NONE, NULL); - ud.block_count = 2; - /* Sends DATA[1] + PING, PING is interleaved in DATA sequence */ - CU_ASSERT(0 == nghttp2_session_send(session)); - CU_ASSERT(NGHTTP2_PING == ud.sent_frame_type); - /* data for DATA[2] is read from data_prd but it is not sent */ - CU_ASSERT(ud.data_source_length == NGHTTP2_DATA_PAYLOADLEN); - - ud.block_count = 2; - /* Sends DATA[2..3] */ - CU_ASSERT(0 == nghttp2_session_send(session)); - - CU_ASSERT(stream->shut_flags & NGHTTP2_SHUT_WR); - - nghttp2_session_del(session); -} - -static void check_session_recv_data_with_padding(nghttp2_bufs *bufs, - size_t datalen, - nghttp2_mem *mem) { - nghttp2_session *session; - my_user_data ud; - nghttp2_session_callbacks callbacks; - uint8_t *in; - size_t inlen; - - memset(&callbacks, 0, sizeof(callbacks)); - callbacks.on_frame_recv_callback = on_frame_recv_callback; - callbacks.on_data_chunk_recv_callback = on_data_chunk_recv_callback; - nghttp2_session_server_new(&session, &callbacks, &ud); - - nghttp2_session_open_stream(session, 1, NGHTTP2_STREAM_FLAG_NONE, - &pri_spec_default, NGHTTP2_STREAM_OPENING, NULL); - - inlen = nghttp2_bufs_remove(bufs, &in); - - ud.frame_recv_cb_called = 0; - ud.data_chunk_len = 0; - - CU_ASSERT((ssize_t)inlen == nghttp2_session_mem_recv(session, in, inlen)); - - CU_ASSERT(1 == ud.frame_recv_cb_called); - CU_ASSERT(datalen == ud.data_chunk_len); - - mem->free(in, NULL); - nghttp2_session_del(session); -} - -void test_nghttp2_session_pack_data_with_padding(void) { - nghttp2_session *session; - my_user_data ud; - nghttp2_session_callbacks callbacks; - nghttp2_data_provider data_prd; - nghttp2_frame *frame; - size_t datalen = 55; - nghttp2_mem *mem; - - mem = nghttp2_mem_default(); - - memset(&callbacks, 0, sizeof(callbacks)); - callbacks.send_callback = block_count_send_callback; - callbacks.on_frame_send_callback = on_frame_send_callback; - callbacks.select_padding_callback = select_padding_callback; - - data_prd.read_callback = fixed_length_data_source_read_callback; - - nghttp2_session_client_new(&session, &callbacks, &ud); - - ud.padlen = 63; - - nghttp2_submit_request(session, NULL, NULL, 0, &data_prd, NULL); - ud.block_count = 1; - ud.data_source_length = datalen; - /* Sends HEADERS */ - CU_ASSERT(0 == nghttp2_session_send(session)); - CU_ASSERT(NGHTTP2_HEADERS == ud.sent_frame_type); - - frame = &session->aob.item->frame; - - CU_ASSERT(ud.padlen == frame->data.padlen); - CU_ASSERT(frame->hd.flags & NGHTTP2_FLAG_PADDED); - - /* Check reception of this DATA frame */ - check_session_recv_data_with_padding(&session->aob.framebufs, datalen, mem); - - nghttp2_session_del(session); -} - -void test_nghttp2_session_pack_headers_with_padding(void) { - nghttp2_session *session, *sv_session; - accumulator acc; - my_user_data ud; - nghttp2_session_callbacks callbacks; - - memset(&callbacks, 0, sizeof(callbacks)); - callbacks.send_callback = accumulator_send_callback; - callbacks.on_frame_send_callback = on_frame_send_callback; - callbacks.select_padding_callback = select_padding_callback; - callbacks.on_frame_recv_callback = on_frame_recv_callback; - - acc.length = 0; - ud.acc = &acc; - - nghttp2_session_client_new(&session, &callbacks, &ud); - nghttp2_session_server_new(&sv_session, &callbacks, &ud); - - ud.padlen = 163; - - CU_ASSERT(1 == nghttp2_submit_request(session, NULL, reqnv, ARRLEN(reqnv), - NULL, NULL)); - CU_ASSERT(0 == nghttp2_session_send(session)); - - CU_ASSERT(acc.length < NGHTTP2_MAX_PAYLOADLEN); - ud.frame_recv_cb_called = 0; - CU_ASSERT((ssize_t)acc.length == - nghttp2_session_mem_recv(sv_session, acc.buf, acc.length)); - CU_ASSERT(1 == ud.frame_recv_cb_called); - CU_ASSERT(NULL == nghttp2_session_get_next_ob_item(sv_session)); - - nghttp2_session_del(sv_session); - nghttp2_session_del(session); -} - -void test_nghttp2_pack_settings_payload(void) { - nghttp2_settings_entry iv[2]; - uint8_t buf[64]; - ssize_t len; - nghttp2_settings_entry *resiv; - size_t resniv; - nghttp2_mem *mem; - - mem = nghttp2_mem_default(); - - iv[0].settings_id = NGHTTP2_SETTINGS_HEADER_TABLE_SIZE; - iv[0].value = 1023; - iv[1].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE; - iv[1].value = 4095; - - len = nghttp2_pack_settings_payload(buf, sizeof(buf), iv, 2); - CU_ASSERT(2 * NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH == len); - CU_ASSERT(0 == nghttp2_frame_unpack_settings_payload2(&resiv, &resniv, buf, - len, mem)); - CU_ASSERT(2 == resniv); - CU_ASSERT(NGHTTP2_SETTINGS_HEADER_TABLE_SIZE == resiv[0].settings_id); - CU_ASSERT(1023 == resiv[0].value); - CU_ASSERT(NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE == resiv[1].settings_id); - CU_ASSERT(4095 == resiv[1].value); - - mem->free(resiv, NULL); - - len = nghttp2_pack_settings_payload(buf, 9 /* too small */, iv, 2); - CU_ASSERT(NGHTTP2_ERR_INSUFF_BUFSIZE == len); -} - -#define check_stream_dep_sib(STREAM, DEP_PREV, DEP_NEXT, SIB_PREV, SIB_NEXT) \ - do { \ - CU_ASSERT(DEP_PREV == STREAM->dep_prev); \ - CU_ASSERT(DEP_NEXT == STREAM->dep_next); \ - CU_ASSERT(SIB_PREV == STREAM->sib_prev); \ - CU_ASSERT(SIB_NEXT == STREAM->sib_next); \ - } while (0) - -/* nghttp2_stream_dep_add() and its families functions should be - tested in nghttp2_stream_test.c, but it is easier to use - nghttp2_session_open_stream(). Therefore, we test them here. */ -void test_nghttp2_session_stream_dep_add(void) { - nghttp2_session *session; - nghttp2_session_callbacks callbacks; - nghttp2_stream *a, *b, *c, *d, *e; - - memset(&callbacks, 0, sizeof(callbacks)); - - nghttp2_session_server_new(&session, &callbacks, NULL); - - a = open_stream(session, 1); - - c = open_stream_with_dep(session, 5, a); - b = open_stream_with_dep(session, 3, a); - d = open_stream_with_dep(session, 7, c); - - /* a - * | - * b--c - * | - * d - */ - - CU_ASSERT(4 == a->num_substreams); - CU_ASSERT(1 == b->num_substreams); - CU_ASSERT(2 == c->num_substreams); - CU_ASSERT(1 == d->num_substreams); - - CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT * 2 == a->sum_dep_weight); - CU_ASSERT(0 == b->sum_dep_weight); - CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT == c->sum_dep_weight); - CU_ASSERT(0 == d->sum_dep_weight); - - check_stream_dep_sib(a, NULL, b, NULL, NULL); - check_stream_dep_sib(b, a, NULL, NULL, c); - check_stream_dep_sib(c, NULL, d, b, NULL); - check_stream_dep_sib(d, c, NULL, NULL, NULL); - - CU_ASSERT(4 == session->roots.num_streams); - CU_ASSERT(a == session->roots.head); - CU_ASSERT(NULL == a->root_next); - - e = open_stream_with_dep_excl(session, 9, a); - - /* a - * | - * e - * | - * b--c - * | - * d - */ - - CU_ASSERT(5 == a->num_substreams); - CU_ASSERT(4 == e->num_substreams); - CU_ASSERT(1 == b->num_substreams); - CU_ASSERT(2 == c->num_substreams); - CU_ASSERT(1 == d->num_substreams); - - CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT == a->sum_dep_weight); - CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT * 2 == e->sum_dep_weight); - CU_ASSERT(0 == b->sum_dep_weight); - CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT == c->sum_dep_weight); - CU_ASSERT(0 == d->sum_dep_weight); - - check_stream_dep_sib(a, NULL, e, NULL, NULL); - check_stream_dep_sib(e, a, b, NULL, NULL); - check_stream_dep_sib(b, e, NULL, NULL, c); - check_stream_dep_sib(c, NULL, d, b, NULL); - check_stream_dep_sib(d, c, NULL, NULL, NULL); - - CU_ASSERT(5 == session->roots.num_streams); - CU_ASSERT(a == session->roots.head); - CU_ASSERT(NULL == a->root_next); - - nghttp2_session_del(session); -} - -void test_nghttp2_session_stream_dep_remove(void) { - nghttp2_session *session; - nghttp2_session_callbacks callbacks; - nghttp2_stream *a, *b, *c, *d, *e, *f; - - memset(&callbacks, 0, sizeof(callbacks)); - - /* Remove root */ - nghttp2_session_server_new(&session, &callbacks, NULL); - - a = open_stream(session, 1); - b = open_stream_with_dep(session, 3, a); - c = open_stream_with_dep(session, 5, a); - d = open_stream_with_dep(session, 7, c); - - /* a - * | - * c--b - * | - * d - */ - - nghttp2_stream_dep_remove(a); - - /* becomes: - * b c - * | - * d - */ - - CU_ASSERT(1 == a->num_substreams); - CU_ASSERT(1 == b->num_substreams); - CU_ASSERT(2 == c->num_substreams); - CU_ASSERT(1 == d->num_substreams); - - CU_ASSERT(0 == a->sum_dep_weight); - CU_ASSERT(0 == b->sum_dep_weight); - CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT == c->sum_dep_weight); - CU_ASSERT(0 == d->sum_dep_weight); - - check_stream_dep_sib(a, NULL, NULL, NULL, NULL); - check_stream_dep_sib(b, NULL, NULL, NULL, NULL); - check_stream_dep_sib(c, NULL, d, NULL, NULL); - check_stream_dep_sib(d, c, NULL, NULL, NULL); - - CU_ASSERT(3 == session->roots.num_streams); - CU_ASSERT(b == session->roots.head); - CU_ASSERT(c == b->root_next); - CU_ASSERT(NULL == c->root_next); - - nghttp2_session_del(session); - - /* Remove left most stream */ - nghttp2_session_server_new(&session, &callbacks, NULL); - - a = open_stream(session, 1); - b = open_stream_with_dep(session, 3, a); - c = open_stream_with_dep(session, 5, a); - d = open_stream_with_dep(session, 7, c); - - /* a - * | - * c--b - * | - * d - */ - - nghttp2_stream_dep_remove(b); - - /* becomes: - * a - * | - * c - * | - * d - */ - - CU_ASSERT(3 == a->num_substreams); - CU_ASSERT(1 == b->num_substreams); - CU_ASSERT(2 == c->num_substreams); - CU_ASSERT(1 == d->num_substreams); - - CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT == a->sum_dep_weight); - CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT == c->sum_dep_weight); - CU_ASSERT(0 == d->sum_dep_weight); - CU_ASSERT(0 == b->sum_dep_weight); - - check_stream_dep_sib(a, NULL, c, NULL, NULL); - check_stream_dep_sib(b, NULL, NULL, NULL, NULL); - check_stream_dep_sib(c, a, d, NULL, NULL); - check_stream_dep_sib(d, c, NULL, NULL, NULL); - - CU_ASSERT(3 == session->roots.num_streams); - CU_ASSERT(a == session->roots.head); - CU_ASSERT(NULL == a->root_next); - - nghttp2_session_del(session); - - /* Remove right most stream */ - nghttp2_session_server_new(&session, &callbacks, NULL); - - a = open_stream(session, 1); - b = open_stream_with_dep(session, 3, a); - c = open_stream_with_dep(session, 5, a); - d = open_stream_with_dep(session, 7, c); - - /* a - * | - * c--b - * | - * d - */ - - nghttp2_stream_dep_remove(c); - - /* becomes: - * a - * | - * d--b - */ - - CU_ASSERT(3 == a->num_substreams); - CU_ASSERT(1 == b->num_substreams); - CU_ASSERT(1 == c->num_substreams); - CU_ASSERT(1 == d->num_substreams); - - CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT * 2 == a->sum_dep_weight); - CU_ASSERT(0 == b->sum_dep_weight); - CU_ASSERT(0 == d->sum_dep_weight); - CU_ASSERT(0 == c->sum_dep_weight); - - check_stream_dep_sib(a, NULL, d, NULL, NULL); - check_stream_dep_sib(b, NULL, NULL, d, NULL); - check_stream_dep_sib(c, NULL, NULL, NULL, NULL); - check_stream_dep_sib(d, a, NULL, NULL, b); - - nghttp2_session_del(session); - - /* Remove middle stream */ - nghttp2_session_server_new(&session, &callbacks, NULL); - - a = open_stream(session, 1); - b = open_stream_with_dep(session, 3, a); - c = open_stream_with_dep(session, 5, a); - d = open_stream_with_dep(session, 7, a); - e = open_stream_with_dep(session, 9, c); - f = open_stream_with_dep(session, 11, c); - - /* a - * | - * d--c--b - * | - * f--e - */ - - CU_ASSERT(6 == a->num_substreams); - CU_ASSERT(1 == b->num_substreams); - CU_ASSERT(3 == c->num_substreams); - CU_ASSERT(1 == d->num_substreams); - CU_ASSERT(1 == e->num_substreams); - CU_ASSERT(1 == f->num_substreams); - - CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT * 3 == a->sum_dep_weight); - CU_ASSERT(0 == b->sum_dep_weight); - CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT * 2 == c->sum_dep_weight); - CU_ASSERT(0 == d->sum_dep_weight); - CU_ASSERT(0 == e->sum_dep_weight); - CU_ASSERT(0 == f->sum_dep_weight); - - nghttp2_stream_dep_remove(c); - - /* becomes: - * a - * | - * d--f--e--b - */ - - CU_ASSERT(5 == a->num_substreams); - CU_ASSERT(1 == b->num_substreams); - CU_ASSERT(1 == c->num_substreams); - CU_ASSERT(1 == d->num_substreams); - CU_ASSERT(1 == e->num_substreams); - CU_ASSERT(1 == f->num_substreams); - - /* c's weight 16 is distributed evenly to e and f. Each weight of e - and f becomes 8. */ - CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT * 2 + 8 * 2 == a->sum_dep_weight); - CU_ASSERT(0 == b->sum_dep_weight); - CU_ASSERT(0 == c->sum_dep_weight); - CU_ASSERT(0 == d->sum_dep_weight); - CU_ASSERT(0 == e->sum_dep_weight); - CU_ASSERT(0 == f->sum_dep_weight); - - check_stream_dep_sib(a, NULL, d, NULL, NULL); - check_stream_dep_sib(b, NULL, NULL, e, NULL); - check_stream_dep_sib(c, NULL, NULL, NULL, NULL); - check_stream_dep_sib(e, NULL, NULL, f, b); - check_stream_dep_sib(f, NULL, NULL, d, e); - check_stream_dep_sib(d, a, NULL, NULL, f); - - nghttp2_session_del(session); -} - -void test_nghttp2_session_stream_dep_add_subtree(void) { - nghttp2_session *session; - nghttp2_session_callbacks callbacks; - nghttp2_stream *a, *b, *c, *d, *e, *f; - - memset(&callbacks, 0, sizeof(callbacks)); - - /* dep_stream has dep_next */ - nghttp2_session_server_new(&session, &callbacks, NULL); - - a = open_stream(session, 1); - b = open_stream_with_dep(session, 3, a); - c = open_stream_with_dep(session, 5, a); - d = open_stream_with_dep(session, 7, c); - - e = open_stream(session, 9); - f = open_stream_with_dep(session, 11, e); - - /* a e - * | | - * c--b f - * | - * d - */ - - nghttp2_stream_dep_add_subtree(a, e, session); - - /* becomes - * a - * | - * e--c--b - * | | - * f d - */ - - CU_ASSERT(6 == a->num_substreams); - CU_ASSERT(1 == b->num_substreams); - CU_ASSERT(2 == c->num_substreams); - CU_ASSERT(1 == d->num_substreams); - CU_ASSERT(2 == e->num_substreams); - CU_ASSERT(1 == f->num_substreams); - - CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT * 3 == a->sum_dep_weight); - CU_ASSERT(0 == b->sum_dep_weight); - CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT == c->sum_dep_weight); - CU_ASSERT(0 == d->sum_dep_weight); - CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT == e->sum_dep_weight); - CU_ASSERT(0 == f->sum_dep_weight); - - check_stream_dep_sib(a, NULL, e, NULL, NULL); - check_stream_dep_sib(b, NULL, NULL, c, NULL); - check_stream_dep_sib(c, NULL, d, e, b); - check_stream_dep_sib(d, c, NULL, NULL, NULL); - check_stream_dep_sib(e, a, f, NULL, c); - check_stream_dep_sib(f, e, NULL, NULL, NULL); - - nghttp2_session_del(session); - - /* dep_stream has dep_next and now we insert subtree */ - nghttp2_session_server_new(&session, &callbacks, NULL); - - a = open_stream(session, 1); - b = open_stream_with_dep(session, 3, a); - c = open_stream_with_dep(session, 5, a); - d = open_stream_with_dep(session, 7, c); - - e = open_stream(session, 9); - f = open_stream_with_dep(session, 11, e); - - /* a e - * | | - * c--b f - * | - * d - */ - - nghttp2_stream_dep_insert_subtree(a, e, session); - - /* becomes - * a - * | - * e - * | - * f--c--b - * | - * d - */ - - CU_ASSERT(6 == a->num_substreams); - CU_ASSERT(1 == b->num_substreams); - CU_ASSERT(2 == c->num_substreams); - CU_ASSERT(1 == d->num_substreams); - CU_ASSERT(5 == e->num_substreams); - CU_ASSERT(1 == f->num_substreams); - - CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT == a->sum_dep_weight); - CU_ASSERT(0 == b->sum_dep_weight); - CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT == c->sum_dep_weight); - CU_ASSERT(0 == d->sum_dep_weight); - CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT * 3 == e->sum_dep_weight); - CU_ASSERT(0 == f->sum_dep_weight); - - check_stream_dep_sib(a, NULL, e, NULL, NULL); - check_stream_dep_sib(e, a, f, NULL, NULL); - check_stream_dep_sib(f, e, NULL, NULL, c); - check_stream_dep_sib(b, NULL, NULL, c, NULL); - check_stream_dep_sib(c, NULL, d, f, b); - check_stream_dep_sib(d, c, NULL, NULL, NULL); - - nghttp2_session_del(session); -} - -void test_nghttp2_session_stream_dep_remove_subtree(void) { - nghttp2_session *session; - nghttp2_session_callbacks callbacks; - nghttp2_stream *a, *b, *c, *d, *e; - - memset(&callbacks, 0, sizeof(callbacks)); - - /* Remove left most stream */ - nghttp2_session_server_new(&session, &callbacks, NULL); - - a = open_stream(session, 1); - b = open_stream_with_dep(session, 3, a); - c = open_stream_with_dep(session, 5, a); - d = open_stream_with_dep(session, 7, c); - - /* a - * | - * c--b - * | - * d - */ - - nghttp2_stream_dep_remove_subtree(c); - - /* becomes - * a c - * | | - * b d - */ - - CU_ASSERT(2 == a->num_substreams); - CU_ASSERT(1 == b->num_substreams); - CU_ASSERT(2 == c->num_substreams); - CU_ASSERT(1 == d->num_substreams); - - CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT == a->sum_dep_weight); - CU_ASSERT(0 == b->sum_dep_weight); - CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT == c->sum_dep_weight); - CU_ASSERT(0 == d->sum_dep_weight); - - check_stream_dep_sib(a, NULL, b, NULL, NULL); - check_stream_dep_sib(b, a, NULL, NULL, NULL); - check_stream_dep_sib(c, NULL, d, NULL, NULL); - check_stream_dep_sib(d, c, NULL, NULL, NULL); - - nghttp2_session_del(session); - - /* Remove right most stream */ - nghttp2_session_server_new(&session, &callbacks, NULL); - - a = open_stream(session, 1); - b = open_stream_with_dep(session, 3, a); - c = open_stream_with_dep(session, 5, a); - d = open_stream_with_dep(session, 7, c); - - /* a - * | - * c--b - * | - * d - */ - - nghttp2_stream_dep_remove_subtree(b); - - /* becomes - * a b - * | - * c - * | - * d - */ - - CU_ASSERT(3 == a->num_substreams); - CU_ASSERT(1 == b->num_substreams); - CU_ASSERT(2 == c->num_substreams); - CU_ASSERT(1 == d->num_substreams); - - CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT == a->sum_dep_weight); - CU_ASSERT(0 == b->sum_dep_weight); - CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT == c->sum_dep_weight); - CU_ASSERT(0 == d->sum_dep_weight); - - check_stream_dep_sib(a, NULL, c, NULL, NULL); - check_stream_dep_sib(c, a, d, NULL, NULL); - check_stream_dep_sib(d, c, NULL, NULL, NULL); - check_stream_dep_sib(b, NULL, NULL, NULL, NULL); - - nghttp2_session_del(session); - - /* Remove middle stream */ - nghttp2_session_server_new(&session, &callbacks, NULL); - - a = open_stream(session, 1); - e = open_stream_with_dep(session, 9, a); - c = open_stream_with_dep(session, 5, a); - b = open_stream_with_dep(session, 3, a); - d = open_stream_with_dep(session, 7, c); - - /* a - * | - * b--c--e - * | - * d - */ - - nghttp2_stream_dep_remove_subtree(c); - - /* becomes - * a c - * | | - * b--e d - */ - - CU_ASSERT(3 == a->num_substreams); - CU_ASSERT(1 == b->num_substreams); - CU_ASSERT(1 == e->num_substreams); - CU_ASSERT(2 == c->num_substreams); - CU_ASSERT(1 == d->num_substreams); - - CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT * 2 == a->sum_dep_weight); - CU_ASSERT(0 == b->sum_dep_weight); - CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT == c->sum_dep_weight); - CU_ASSERT(0 == d->sum_dep_weight); - CU_ASSERT(0 == e->sum_dep_weight); - - check_stream_dep_sib(a, NULL, b, NULL, NULL); - check_stream_dep_sib(b, a, NULL, NULL, e); - check_stream_dep_sib(e, NULL, NULL, b, NULL); - check_stream_dep_sib(c, NULL, d, NULL, NULL); - check_stream_dep_sib(d, c, NULL, NULL, NULL); - - nghttp2_session_del(session); -} - -void test_nghttp2_session_stream_dep_all_your_stream_are_belong_to_us(void) { - nghttp2_session *session; - nghttp2_session_callbacks callbacks; - nghttp2_stream *a, *b, *c, *d; - - memset(&callbacks, 0, sizeof(callbacks)); - - nghttp2_session_server_new(&session, &callbacks, NULL); - - a = open_stream(session, 1); - b = open_stream_with_dep(session, 3, a); - - c = open_stream(session, 5); - - /* a c - * | - * b - */ - - nghttp2_stream_dep_remove_subtree(c); - CU_ASSERT(0 == - nghttp2_stream_dep_all_your_stream_are_belong_to_us(c, session)); - - /* - * c - * | - * a - * | - * b - */ - - CU_ASSERT(3 == c->num_substreams); - CU_ASSERT(2 == a->num_substreams); - CU_ASSERT(1 == b->num_substreams); - - CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT == c->sum_dep_weight); - CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT == a->sum_dep_weight); - CU_ASSERT(0 == b->sum_dep_weight); - - check_stream_dep_sib(c, NULL, a, NULL, NULL); - check_stream_dep_sib(a, c, b, NULL, NULL); - check_stream_dep_sib(b, a, NULL, NULL, NULL); - - nghttp2_session_del(session); - - nghttp2_session_server_new(&session, &callbacks, NULL); - - a = open_stream(session, 1); - - b = open_stream(session, 3); - - c = open_stream(session, 5); - - /* - * a b c - */ - - nghttp2_stream_dep_remove_subtree(c); - CU_ASSERT(0 == - nghttp2_stream_dep_all_your_stream_are_belong_to_us(c, session)); - - /* - * c - * | - * b--a - */ - - CU_ASSERT(3 == c->num_substreams); - CU_ASSERT(1 == a->num_substreams); - CU_ASSERT(1 == b->num_substreams); - - CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT * 2 == c->sum_dep_weight); - CU_ASSERT(0 == b->sum_dep_weight); - CU_ASSERT(0 == a->sum_dep_weight); - - check_stream_dep_sib(c, NULL, b, NULL, NULL); - check_stream_dep_sib(b, c, NULL, NULL, a); - check_stream_dep_sib(a, NULL, NULL, b, NULL); - - nghttp2_session_del(session); - - nghttp2_session_server_new(&session, &callbacks, NULL); - - a = open_stream(session, 1); - b = open_stream_with_dep(session, 3, a); - - c = open_stream(session, 5); - d = open_stream_with_dep(session, 7, c); - - /* a c - * | | - * b d - */ - - nghttp2_stream_dep_remove_subtree(c); - CU_ASSERT(0 == - nghttp2_stream_dep_all_your_stream_are_belong_to_us(c, session)); - - /* - * c - * | - * a--d - * | - * b - */ - - CU_ASSERT(4 == c->num_substreams); - CU_ASSERT(1 == d->num_substreams); - CU_ASSERT(2 == a->num_substreams); - CU_ASSERT(1 == b->num_substreams); - - CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT * 2 == c->sum_dep_weight); - CU_ASSERT(0 == d->sum_dep_weight); - CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT == a->sum_dep_weight); - CU_ASSERT(0 == b->sum_dep_weight); - - check_stream_dep_sib(c, NULL, a, NULL, NULL); - check_stream_dep_sib(d, NULL, NULL, a, NULL); - check_stream_dep_sib(a, c, b, NULL, d); - check_stream_dep_sib(b, a, NULL, NULL, NULL); - - nghttp2_session_del(session); -} - -void test_nghttp2_session_stream_attach_item(void) { - nghttp2_session *session; - nghttp2_session_callbacks callbacks; - nghttp2_stream *a, *b, *c, *d; - nghttp2_outbound_item *da, *db, *dc, *dd; - nghttp2_mem *mem; - - mem = nghttp2_mem_default(); - - memset(&callbacks, 0, sizeof(callbacks)); - - nghttp2_session_server_new(&session, &callbacks, NULL); - - a = open_stream(session, 1); - b = open_stream_with_dep(session, 3, a); - c = open_stream_with_dep(session, 5, a); - d = open_stream_with_dep(session, 7, c); - - /* a - * | - * c--b - * | - * d - */ - - db = create_data_ob_item(mem); - - nghttp2_stream_attach_item(b, db, session); - - CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == a->dpri); - CU_ASSERT(NGHTTP2_STREAM_DPRI_TOP == b->dpri); - CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == c->dpri); - CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == d->dpri); - - CU_ASSERT(16 == b->effective_weight); - - CU_ASSERT(16 == a->sum_norest_weight); - - CU_ASSERT(1 == db->queued); - - dc = create_data_ob_item(mem); - - nghttp2_stream_attach_item(c, dc, session); - - CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == a->dpri); - CU_ASSERT(NGHTTP2_STREAM_DPRI_TOP == b->dpri); - CU_ASSERT(NGHTTP2_STREAM_DPRI_TOP == c->dpri); - CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == d->dpri); - - CU_ASSERT(16 * 16 / 32 == b->effective_weight); - CU_ASSERT(16 * 16 / 32 == c->effective_weight); - - CU_ASSERT(32 == a->sum_norest_weight); - - CU_ASSERT(1 == dc->queued); - - da = create_data_ob_item(mem); - - nghttp2_stream_attach_item(a, da, session); - - CU_ASSERT(NGHTTP2_STREAM_DPRI_TOP == a->dpri); - CU_ASSERT(NGHTTP2_STREAM_DPRI_REST == b->dpri); - CU_ASSERT(NGHTTP2_STREAM_DPRI_REST == c->dpri); - CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == d->dpri); - - CU_ASSERT(16 == a->effective_weight); - - CU_ASSERT(1 == da->queued); - - nghttp2_stream_detach_item(a, session); - - CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == a->dpri); - CU_ASSERT(NGHTTP2_STREAM_DPRI_TOP == b->dpri); - CU_ASSERT(NGHTTP2_STREAM_DPRI_TOP == c->dpri); - CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == d->dpri); - - CU_ASSERT(16 * 16 / 32 == b->effective_weight); - CU_ASSERT(16 * 16 / 32 == c->effective_weight); - - dd = create_data_ob_item(mem); - - nghttp2_stream_attach_item(d, dd, session); - - CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == a->dpri); - CU_ASSERT(NGHTTP2_STREAM_DPRI_TOP == b->dpri); - CU_ASSERT(NGHTTP2_STREAM_DPRI_TOP == c->dpri); - CU_ASSERT(NGHTTP2_STREAM_DPRI_REST == d->dpri); - - CU_ASSERT(16 * 16 / 32 == b->effective_weight); - CU_ASSERT(16 * 16 / 32 == c->effective_weight); - - CU_ASSERT(0 == dd->queued); - - nghttp2_stream_detach_item(c, session); - - CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == a->dpri); - CU_ASSERT(NGHTTP2_STREAM_DPRI_TOP == b->dpri); - CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == c->dpri); - CU_ASSERT(NGHTTP2_STREAM_DPRI_TOP == d->dpri); - - CU_ASSERT(16 * 16 / 32 == b->effective_weight); - CU_ASSERT(16 * 16 / 32 == d->effective_weight); - - CU_ASSERT(1 == dd->queued); - - nghttp2_stream_detach_item(b, session); - - CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == a->dpri); - CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == b->dpri); - CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == c->dpri); - CU_ASSERT(NGHTTP2_STREAM_DPRI_TOP == d->dpri); - - CU_ASSERT(16 * 16 / 16 == d->effective_weight); - - CU_ASSERT(1 == dd->queued); - - nghttp2_session_del(session); -} - -void test_nghttp2_session_stream_attach_item_subtree(void) { - nghttp2_session *session; - nghttp2_session_callbacks callbacks; - nghttp2_stream *a, *b, *c, *d, *e, *f; - nghttp2_outbound_item *db, *dd, *de; - nghttp2_mem *mem; - - mem = nghttp2_mem_default(); - - memset(&callbacks, 0, sizeof(callbacks)); - - nghttp2_session_server_new(&session, &callbacks, NULL); - - a = open_stream(session, 1); - b = open_stream_with_dep(session, 3, a); - c = open_stream_with_dep(session, 5, a); - d = open_stream_with_dep(session, 7, c); - - e = open_stream(session, 9); - f = open_stream_with_dep(session, 11, e); - /* - * a e - * | | - * c--b f - * | - * d - */ - - de = create_data_ob_item(mem); - - nghttp2_stream_attach_item(e, de, session); - - db = create_data_ob_item(mem); - - nghttp2_stream_attach_item(b, db, session); - - CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == a->dpri); - CU_ASSERT(NGHTTP2_STREAM_DPRI_TOP == b->dpri); - CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == c->dpri); - CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == d->dpri); - CU_ASSERT(NGHTTP2_STREAM_DPRI_TOP == e->dpri); - CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == f->dpri); - - CU_ASSERT(16 == b->effective_weight); - CU_ASSERT(16 == e->effective_weight); - - /* Insert subtree e under a */ - - nghttp2_stream_dep_remove_subtree(e); - nghttp2_stream_dep_insert_subtree(a, e, session); - - /* - * a - * | - * e - * | - * f--c--b - * | - * d - */ - - CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == a->dpri); - CU_ASSERT(NGHTTP2_STREAM_DPRI_REST == b->dpri); - CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == c->dpri); - CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == d->dpri); - CU_ASSERT(NGHTTP2_STREAM_DPRI_TOP == e->dpri); - CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == f->dpri); - - CU_ASSERT(16 == e->effective_weight); - - /* Remove subtree b */ - - nghttp2_stream_dep_remove_subtree(b); - - nghttp2_stream_dep_make_root(b, session); - - /* - * a b - * | - * e - * | - * f--c - * | - * d - */ - - CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == a->dpri); - CU_ASSERT(NGHTTP2_STREAM_DPRI_TOP == b->dpri); - CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == c->dpri); - CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == d->dpri); - CU_ASSERT(NGHTTP2_STREAM_DPRI_TOP == e->dpri); - CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == f->dpri); - - CU_ASSERT(16 == b->effective_weight); - CU_ASSERT(16 == e->effective_weight); - - /* Remove subtree a */ - - nghttp2_stream_dep_remove_subtree(a); - - nghttp2_stream_dep_make_root(a, session); - - CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == a->dpri); - CU_ASSERT(NGHTTP2_STREAM_DPRI_TOP == b->dpri); - CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == c->dpri); - CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == d->dpri); - CU_ASSERT(NGHTTP2_STREAM_DPRI_TOP == e->dpri); - CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == f->dpri); - - /* Remove subtree c */ - - nghttp2_stream_dep_remove_subtree(c); - - nghttp2_stream_dep_make_root(c, session); - - /* - * a b c - * | | - * e d - * | - * f - */ - - CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == a->dpri); - CU_ASSERT(NGHTTP2_STREAM_DPRI_TOP == b->dpri); - CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == c->dpri); - CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == d->dpri); - CU_ASSERT(NGHTTP2_STREAM_DPRI_TOP == e->dpri); - CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == f->dpri); - - dd = create_data_ob_item(mem); - - nghttp2_stream_attach_item(d, dd, session); - - /* Add subtree c to a */ - - nghttp2_stream_dep_remove_subtree(c); - nghttp2_stream_dep_add_subtree(a, c, session); - - /* - * a b - * | - * c--e - * | | - * d f - */ - - CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == a->dpri); - CU_ASSERT(NGHTTP2_STREAM_DPRI_TOP == b->dpri); - CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == c->dpri); - CU_ASSERT(NGHTTP2_STREAM_DPRI_TOP == d->dpri); - CU_ASSERT(NGHTTP2_STREAM_DPRI_TOP == e->dpri); - CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == f->dpri); - - CU_ASSERT(16 == b->effective_weight); - CU_ASSERT(16 * 16 / 32 == e->effective_weight); - CU_ASSERT(16 * 16 / 32 == e->effective_weight); - - CU_ASSERT(32 == a->sum_norest_weight); - CU_ASSERT(16 == c->sum_norest_weight); - - /* Insert b under a */ - - nghttp2_stream_dep_remove_subtree(b); - nghttp2_stream_dep_insert_subtree(a, b, session); - - /* - * a - * | - * b - * | - * e--c - * | | - * f d - */ - - CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == a->dpri); - CU_ASSERT(NGHTTP2_STREAM_DPRI_TOP == b->dpri); - CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == c->dpri); - CU_ASSERT(NGHTTP2_STREAM_DPRI_REST == d->dpri); - CU_ASSERT(NGHTTP2_STREAM_DPRI_REST == e->dpri); - CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == f->dpri); - - CU_ASSERT(16 == b->effective_weight); - - CU_ASSERT(16 == a->sum_norest_weight); - CU_ASSERT(0 == b->sum_norest_weight); - - /* Remove subtree b */ - - nghttp2_stream_dep_remove_subtree(b); - nghttp2_stream_dep_make_root(b, session); - - /* - * b a - * | - * e--c - * | | - * f d - */ - - CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == a->dpri); - CU_ASSERT(NGHTTP2_STREAM_DPRI_TOP == b->dpri); - CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == c->dpri); - CU_ASSERT(NGHTTP2_STREAM_DPRI_REST == d->dpri); - CU_ASSERT(NGHTTP2_STREAM_DPRI_REST == e->dpri); - CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == f->dpri); - - CU_ASSERT(0 == a->sum_norest_weight); - CU_ASSERT(0 == b->sum_norest_weight); - - nghttp2_session_del(session); -} - -void test_nghttp2_session_keep_closed_stream(void) { - nghttp2_session *session; - nghttp2_session_callbacks callbacks; - const size_t max_concurrent_streams = 5; - nghttp2_settings_entry iv = {NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, - max_concurrent_streams}; - size_t i; - - memset(&callbacks, 0, sizeof(callbacks)); - callbacks.send_callback = null_send_callback; - - nghttp2_session_server_new(&session, &callbacks, NULL); - - nghttp2_submit_settings(session, NGHTTP2_FLAG_NONE, &iv, 1); - - for (i = 0; i < max_concurrent_streams; ++i) { - open_stream(session, (int)i * 2 + 1); - } - - CU_ASSERT(0 == session->num_closed_streams); - - nghttp2_session_close_stream(session, 1, NGHTTP2_NO_ERROR); - - CU_ASSERT(1 == session->num_closed_streams); - CU_ASSERT(1 == session->closed_stream_tail->stream_id); - CU_ASSERT(session->closed_stream_tail == session->closed_stream_head); - - nghttp2_session_close_stream(session, 5, NGHTTP2_NO_ERROR); - - CU_ASSERT(2 == session->num_closed_streams); - CU_ASSERT(5 == session->closed_stream_tail->stream_id); - CU_ASSERT(1 == session->closed_stream_head->stream_id); - CU_ASSERT(session->closed_stream_head == - session->closed_stream_tail->closed_prev); - CU_ASSERT(NULL == session->closed_stream_tail->closed_next); - CU_ASSERT(session->closed_stream_tail == - session->closed_stream_head->closed_next); - CU_ASSERT(NULL == session->closed_stream_head->closed_prev); - - open_stream(session, 11); - - CU_ASSERT(1 == session->num_closed_streams); - CU_ASSERT(5 == session->closed_stream_tail->stream_id); - CU_ASSERT(session->closed_stream_tail == session->closed_stream_head); - CU_ASSERT(NULL == session->closed_stream_head->closed_prev); - CU_ASSERT(NULL == session->closed_stream_head->closed_next); - - open_stream(session, 13); - - CU_ASSERT(0 == session->num_closed_streams); - CU_ASSERT(NULL == session->closed_stream_tail); - CU_ASSERT(NULL == session->closed_stream_head); - - nghttp2_session_del(session); -} - -void test_nghttp2_session_keep_idle_stream(void) { - nghttp2_session *session; - nghttp2_session_callbacks callbacks; - const size_t max_concurrent_streams = 1; - nghttp2_settings_entry iv = {NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, - max_concurrent_streams}; - int i; - - memset(&callbacks, 0, sizeof(callbacks)); - callbacks.send_callback = null_send_callback; - - nghttp2_session_server_new(&session, &callbacks, NULL); - - nghttp2_submit_settings(session, NGHTTP2_FLAG_NONE, &iv, 1); - - /* We at least allow 2 idle streams even if max concurrent streams - is very low. */ - for (i = 0; i < 2; ++i) { - nghttp2_session_open_stream(session, i * 2 + 1, NGHTTP2_STREAM_FLAG_NONE, - &pri_spec_default, NGHTTP2_STREAM_IDLE, NULL); - } - - CU_ASSERT(2 == session->num_idle_streams); - - CU_ASSERT(1 == session->idle_stream_head->stream_id); - CU_ASSERT(3 == session->idle_stream_tail->stream_id); - - nghttp2_session_open_stream(session, 5, NGHTTP2_FLAG_NONE, &pri_spec_default, - NGHTTP2_STREAM_IDLE, NULL); - - CU_ASSERT(2 == session->num_idle_streams); - - CU_ASSERT(3 == session->idle_stream_head->stream_id); - CU_ASSERT(5 == session->idle_stream_tail->stream_id); - - nghttp2_session_del(session); -} - -void test_nghttp2_session_detach_idle_stream(void) { - nghttp2_session *session; - nghttp2_session_callbacks callbacks; - int i; - nghttp2_stream *stream; - - memset(&callbacks, 0, sizeof(callbacks)); - callbacks.send_callback = null_send_callback; - - nghttp2_session_server_new(&session, &callbacks, NULL); - - for (i = 1; i <= 3; ++i) { - nghttp2_session_open_stream(session, i, NGHTTP2_STREAM_FLAG_NONE, - &pri_spec_default, NGHTTP2_STREAM_IDLE, NULL); - } - - CU_ASSERT(3 == session->num_idle_streams); - - /* Detach middle stream */ - stream = nghttp2_session_get_stream_raw(session, 2); - - CU_ASSERT(session->idle_stream_head == stream->closed_prev); - CU_ASSERT(session->idle_stream_tail == stream->closed_next); - CU_ASSERT(stream == session->idle_stream_head->closed_next); - CU_ASSERT(stream == session->idle_stream_tail->closed_prev); - - nghttp2_session_detach_idle_stream(session, stream); - - CU_ASSERT(2 == session->num_idle_streams); - - CU_ASSERT(NULL == stream->closed_prev); - CU_ASSERT(NULL == stream->closed_next); - - CU_ASSERT(session->idle_stream_head == - session->idle_stream_tail->closed_prev); - CU_ASSERT(session->idle_stream_tail == - session->idle_stream_head->closed_next); - - /* Detach head stream */ - stream = session->idle_stream_head; - - nghttp2_session_detach_idle_stream(session, stream); - - CU_ASSERT(1 == session->num_idle_streams); - - CU_ASSERT(session->idle_stream_head == session->idle_stream_tail); - CU_ASSERT(NULL == session->idle_stream_head->closed_prev); - CU_ASSERT(NULL == session->idle_stream_head->closed_next); - - /* Detach last stream */ - - stream = session->idle_stream_head; - - nghttp2_session_detach_idle_stream(session, stream); - - CU_ASSERT(0 == session->num_idle_streams); - - CU_ASSERT(NULL == session->idle_stream_head); - CU_ASSERT(NULL == session->idle_stream_tail); - - for (i = 4; i <= 5; ++i) { - nghttp2_session_open_stream(session, i, NGHTTP2_STREAM_FLAG_NONE, - &pri_spec_default, NGHTTP2_STREAM_IDLE, NULL); - } - - CU_ASSERT(2 == session->num_idle_streams); - - /* Detach tail stream */ - - stream = session->idle_stream_tail; - - nghttp2_session_detach_idle_stream(session, stream); - - CU_ASSERT(1 == session->num_idle_streams); - - CU_ASSERT(session->idle_stream_head == session->idle_stream_tail); - CU_ASSERT(NULL == session->idle_stream_head->closed_prev); - CU_ASSERT(NULL == session->idle_stream_head->closed_next); - - nghttp2_session_del(session); -} - -void test_nghttp2_session_large_dep_tree(void) { - nghttp2_session *session; - nghttp2_session_callbacks callbacks; - size_t i; - nghttp2_stream *dep_stream = NULL; - nghttp2_stream *root_stream; - int32_t stream_id; - - memset(&callbacks, 0, sizeof(callbacks)); - callbacks.send_callback = null_send_callback; - - nghttp2_session_server_new(&session, &callbacks, NULL); - - stream_id = 1; - for (i = 0; i < NGHTTP2_MAX_DEP_TREE_LENGTH; ++i) { - dep_stream = open_stream_with_dep(session, stream_id, dep_stream); - stream_id += 2; - } - - root_stream = nghttp2_session_get_stream(session, 1); - - /* Check that last dep_stream must be part of tree */ - CU_ASSERT(nghttp2_stream_dep_subtree_find(root_stream, dep_stream)); - - dep_stream = open_stream_with_dep(session, stream_id, dep_stream); - - /* We exceeded NGHTTP2_MAX_DEP_TREE_LENGTH limit. dep_stream is now - root node and has no descendants. */ - CU_ASSERT(!nghttp2_stream_dep_subtree_find(root_stream, dep_stream)); - CU_ASSERT(nghttp2_stream_in_dep_tree(dep_stream)); - - nghttp2_session_del(session); -} - -void test_nghttp2_session_graceful_shutdown(void) { - nghttp2_session *session; - nghttp2_session_callbacks callbacks; - my_user_data ud; - - memset(&callbacks, 0, sizeof(callbacks)); - callbacks.send_callback = null_send_callback; - callbacks.on_frame_send_callback = on_frame_send_callback; - callbacks.on_stream_close_callback = on_stream_close_callback; - - nghttp2_session_server_new(&session, &callbacks, &ud); - - open_stream(session, 301); - open_stream(session, 302); - open_stream(session, 309); - open_stream(session, 311); - open_stream(session, 319); - - CU_ASSERT(0 == nghttp2_submit_shutdown_notice(session)); - - ud.frame_send_cb_called = 0; - - CU_ASSERT(0 == nghttp2_session_send(session)); - - CU_ASSERT(1 == ud.frame_send_cb_called); - CU_ASSERT((1u << 31) - 1 == session->local_last_stream_id); - - CU_ASSERT(0 == nghttp2_submit_goaway(session, NGHTTP2_FLAG_NONE, 311, - NGHTTP2_NO_ERROR, NULL, 0)); - - ud.frame_send_cb_called = 0; - ud.stream_close_cb_called = 0; - - CU_ASSERT(0 == nghttp2_session_send(session)); - - CU_ASSERT(1 == ud.frame_send_cb_called); - CU_ASSERT(311 == session->local_last_stream_id); - CU_ASSERT(1 == ud.stream_close_cb_called); - - CU_ASSERT(0 == - nghttp2_session_terminate_session2(session, 301, NGHTTP2_NO_ERROR)); - - ud.frame_send_cb_called = 0; - ud.stream_close_cb_called = 0; - - CU_ASSERT(0 == nghttp2_session_send(session)); - - CU_ASSERT(1 == ud.frame_send_cb_called); - CU_ASSERT(301 == session->local_last_stream_id); - CU_ASSERT(2 == ud.stream_close_cb_called); - - CU_ASSERT(NULL != nghttp2_session_get_stream(session, 301)); - CU_ASSERT(NULL != nghttp2_session_get_stream(session, 302)); - CU_ASSERT(NULL == nghttp2_session_get_stream(session, 309)); - CU_ASSERT(NULL == nghttp2_session_get_stream(session, 311)); - CU_ASSERT(NULL == nghttp2_session_get_stream(session, 319)); - - nghttp2_session_del(session); -} - -void test_nghttp2_session_on_header_temporal_failure(void) { - nghttp2_session *session; - nghttp2_session_callbacks callbacks; - my_user_data ud; - nghttp2_bufs bufs; - nghttp2_buf *buf; - nghttp2_hd_deflater deflater; - nghttp2_nv nv[] = {MAKE_NV("alpha", "bravo"), MAKE_NV("charlie", "delta")}; - nghttp2_nv *nva; - size_t hdpos; - ssize_t rv; - nghttp2_frame frame; - nghttp2_frame_hd hd; - nghttp2_outbound_item *item; - nghttp2_mem *mem; - - mem = nghttp2_mem_default(); - memset(&callbacks, 0, sizeof(callbacks)); - callbacks.on_header_callback = temporal_failure_on_header_callback; - - nghttp2_session_server_new(&session, &callbacks, &ud); - - frame_pack_bufs_init(&bufs); - - nghttp2_hd_deflate_init(&deflater, mem); - - nghttp2_nv_array_copy(&nva, reqnv, ARRLEN(reqnv), mem); - - nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_END_STREAM, 1, - NGHTTP2_HCAT_REQUEST, NULL, nva, ARRLEN(reqnv)); - nghttp2_frame_pack_headers(&bufs, &frame.headers, &deflater); - nghttp2_frame_headers_free(&frame.headers, mem); - - /* We are going to create CONTINUATION. First serialize header - block, and then frame header. */ - hdpos = nghttp2_bufs_len(&bufs); - - buf = &bufs.head->buf; - buf->last += NGHTTP2_FRAME_HDLEN; - - nghttp2_hd_deflate_hd_bufs(&deflater, &bufs, &nv[1], 1); - - nghttp2_frame_hd_init(&hd, - nghttp2_bufs_len(&bufs) - hdpos - NGHTTP2_FRAME_HDLEN, - NGHTTP2_CONTINUATION, NGHTTP2_FLAG_END_HEADERS, 1); - - nghttp2_frame_pack_frame_hd(&buf->pos[hdpos], &hd); - - ud.header_cb_called = 0; - rv = nghttp2_session_mem_recv(session, buf->pos, nghttp2_bufs_len(&bufs)); - - CU_ASSERT(rv == nghttp2_bufs_len(&bufs)); - CU_ASSERT(1 == ud.header_cb_called); - - item = nghttp2_session_get_next_ob_item(session); - - CU_ASSERT(NGHTTP2_RST_STREAM == item->frame.hd.type); - CU_ASSERT(1 == item->frame.hd.stream_id); - - /* Make sure no header decompression error occurred */ - CU_ASSERT(NGHTTP2_GOAWAY_NONE == session->goaway_flags); - - nghttp2_hd_deflate_free(&deflater); - nghttp2_session_del(session); - - nghttp2_bufs_reset(&bufs); - - /* Check for PUSH_PROMISE */ - nghttp2_hd_deflate_init(&deflater, mem); - nghttp2_session_client_new(&session, &callbacks, &ud); - - nghttp2_session_open_stream(session, 1, NGHTTP2_STREAM_FLAG_NONE, - &pri_spec_default, NGHTTP2_STREAM_OPENING, NULL); - - rv = pack_push_promise(&bufs, &deflater, 1, NGHTTP2_FLAG_END_HEADERS, 2, - reqnv, ARRLEN(reqnv), mem); - CU_ASSERT(0 == rv); - - ud.header_cb_called = 0; - rv = nghttp2_session_mem_recv(session, bufs.head->buf.pos, - nghttp2_bufs_len(&bufs)); - CU_ASSERT(nghttp2_bufs_len(&bufs) == rv); - CU_ASSERT(1 == ud.header_cb_called); - - item = nghttp2_session_get_next_ob_item(session); - CU_ASSERT(NGHTTP2_RST_STREAM == item->frame.hd.type); - CU_ASSERT(2 == item->frame.hd.stream_id); - CU_ASSERT(NGHTTP2_INTERNAL_ERROR == item->frame.rst_stream.error_code); - - nghttp2_session_del(session); - nghttp2_hd_deflate_free(&deflater); - nghttp2_bufs_free(&bufs); -} - -void test_nghttp2_session_recv_client_magic(void) { - nghttp2_session *session; - nghttp2_session_callbacks callbacks; - ssize_t rv; - nghttp2_frame ping_frame; - uint8_t buf[16]; - - /* enable global nghttp2_enable_strict_preface here */ - nghttp2_enable_strict_preface = 1; - - memset(&callbacks, 0, sizeof(callbacks)); - - /* Check success case */ - nghttp2_session_server_new(&session, &callbacks, NULL); - - rv = nghttp2_session_mem_recv(session, (const uint8_t *)NGHTTP2_CLIENT_MAGIC, - NGHTTP2_CLIENT_MAGIC_LEN); - - CU_ASSERT(rv == NGHTTP2_CLIENT_MAGIC_LEN); - CU_ASSERT(NGHTTP2_IB_READ_FIRST_SETTINGS == session->iframe.state); - - /* Receiving PING is error because we want SETTINGS. */ - nghttp2_frame_ping_init(&ping_frame.ping, NGHTTP2_FLAG_NONE, NULL); - - nghttp2_frame_pack_frame_hd(buf, &ping_frame.ping.hd); - - rv = nghttp2_session_mem_recv(session, buf, NGHTTP2_FRAME_HDLEN); - CU_ASSERT(NGHTTP2_FRAME_HDLEN == rv); - CU_ASSERT(NGHTTP2_IB_IGN_ALL == session->iframe.state); - CU_ASSERT(0 == session->iframe.payloadleft); - - nghttp2_frame_ping_free(&ping_frame.ping); - - nghttp2_session_del(session); - - /* Check bad case */ - nghttp2_session_server_new(&session, &callbacks, NULL); - - /* Feed magic with one byte less */ - rv = nghttp2_session_mem_recv(session, (const uint8_t *)NGHTTP2_CLIENT_MAGIC, - NGHTTP2_CLIENT_MAGIC_LEN - 1); - - CU_ASSERT(rv == NGHTTP2_CLIENT_MAGIC_LEN - 1); - CU_ASSERT(NGHTTP2_IB_READ_CLIENT_MAGIC == session->iframe.state); - CU_ASSERT(1 == session->iframe.payloadleft); - - rv = nghttp2_session_mem_recv(session, (const uint8_t *)"\0", 1); - - CU_ASSERT(NGHTTP2_ERR_BAD_CLIENT_MAGIC == rv); - - nghttp2_session_del(session); - - /* disable global nghttp2_enable_strict_preface here */ - nghttp2_enable_strict_preface = 0; -} - -void test_nghttp2_session_delete_data_item(void) { - nghttp2_session *session; - nghttp2_session_callbacks callbacks; - nghttp2_stream *a; - nghttp2_data_provider prd; - - memset(&callbacks, 0, sizeof(callbacks)); - - nghttp2_session_server_new(&session, &callbacks, NULL); - - a = open_stream(session, 1); - open_stream_with_dep(session, 3, a); - - /* We don't care about these members, since we won't send data */ - prd.source.ptr = NULL; - prd.read_callback = fail_data_source_read_callback; - - /* This data item will be marked as TOP */ - CU_ASSERT(0 == nghttp2_submit_data(session, NGHTTP2_FLAG_NONE, 1, &prd)); - /* This data item will be marked as REST */ - CU_ASSERT(0 == nghttp2_submit_data(session, NGHTTP2_FLAG_NONE, 3, &prd)); - - nghttp2_session_del(session); -} - -void test_nghttp2_session_open_idle_stream(void) { - nghttp2_session *session; - nghttp2_session_callbacks callbacks; - nghttp2_stream *stream; - nghttp2_stream *opened_stream; - nghttp2_priority_spec pri_spec; - nghttp2_frame frame; - - memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); - - nghttp2_session_server_new(&session, &callbacks, NULL); - - nghttp2_priority_spec_init(&pri_spec, 0, 3, 0); - - nghttp2_frame_priority_init(&frame.priority, 1, &pri_spec); - - CU_ASSERT(0 == nghttp2_session_on_priority_received(session, &frame)); - - stream = nghttp2_session_get_stream_raw(session, 1); - - CU_ASSERT(NGHTTP2_STREAM_IDLE == stream->state); - CU_ASSERT(NULL == stream->closed_prev); - CU_ASSERT(NULL == stream->closed_next); - CU_ASSERT(1 == session->num_idle_streams); - CU_ASSERT(session->idle_stream_head == stream); - CU_ASSERT(session->idle_stream_tail == stream); - - opened_stream = nghttp2_session_open_stream( - session, 1, NGHTTP2_STREAM_FLAG_NONE, &pri_spec_default, - NGHTTP2_STREAM_OPENING, NULL); - - CU_ASSERT(stream == opened_stream); - CU_ASSERT(NGHTTP2_STREAM_OPENING == stream->state); - CU_ASSERT(0 == session->num_idle_streams); - CU_ASSERT(NULL == session->idle_stream_head); - CU_ASSERT(NULL == session->idle_stream_tail); - - nghttp2_frame_priority_free(&frame.priority); - - nghttp2_session_del(session); -} - -void test_nghttp2_session_cancel_reserved_remote(void) { - nghttp2_session *session; - nghttp2_session_callbacks callbacks; - nghttp2_stream *stream; - nghttp2_frame frame; - nghttp2_nv *nva; - ssize_t nvlen; - nghttp2_hd_deflater deflater; - nghttp2_mem *mem; - nghttp2_bufs bufs; - ssize_t rv; - - mem = nghttp2_mem_default(); - frame_pack_bufs_init(&bufs); - - memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); - callbacks.send_callback = null_send_callback; - - nghttp2_session_client_new(&session, &callbacks, NULL); - - nghttp2_hd_deflate_init(&deflater, mem); - - stream = nghttp2_session_open_stream(session, 2, NGHTTP2_STREAM_FLAG_NONE, - &pri_spec_default, - NGHTTP2_STREAM_RESERVED, NULL); - - session->last_recv_stream_id = 2; - - nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, 2, NGHTTP2_CANCEL); - - CU_ASSERT(NGHTTP2_STREAM_CLOSING == stream->state); - - CU_ASSERT(0 == nghttp2_session_send(session)); - - nvlen = ARRLEN(resnv); - nghttp2_nv_array_copy(&nva, resnv, nvlen, mem); - - nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_END_HEADERS, 2, - NGHTTP2_HCAT_PUSH_RESPONSE, NULL, nva, nvlen); - rv = nghttp2_frame_pack_headers(&bufs, &frame.headers, &deflater); - - CU_ASSERT(0 == rv); - - rv = nghttp2_session_mem_recv(session, bufs.head->buf.pos, - nghttp2_buf_len(&bufs.head->buf)); - - CU_ASSERT(nghttp2_buf_len(&bufs.head->buf) == rv); - - /* stream is not dangling, so assign NULL */ - stream = NULL; - - /* No RST_STREAM or GOAWAY is generated since stream should be in - NGHTTP2_STREAM_CLOSING and push response should be ignored. */ - CU_ASSERT(0 == nghttp2_outbound_queue_size(&session->ob_reg)); - - /* Check that we can receive push response HEADERS while RST_STREAM - is just queued. */ - nghttp2_session_open_stream(session, 4, NGHTTP2_STREAM_FLAG_NONE, - &pri_spec_default, NGHTTP2_STREAM_RESERVED, NULL); - - session->last_recv_stream_id = 4; - - nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, 2, NGHTTP2_CANCEL); - - nghttp2_bufs_reset(&bufs); - - frame.hd.stream_id = 4; - rv = nghttp2_frame_pack_headers(&bufs, &frame.headers, &deflater); - - CU_ASSERT(0 == rv); - - rv = nghttp2_session_mem_recv(session, bufs.head->buf.pos, - nghttp2_buf_len(&bufs.head->buf)); - - CU_ASSERT(nghttp2_buf_len(&bufs.head->buf) == rv); - - CU_ASSERT(1 == nghttp2_outbound_queue_size(&session->ob_reg)); - - nghttp2_frame_headers_free(&frame.headers, mem); - - nghttp2_hd_deflate_free(&deflater); - - nghttp2_session_del(session); - - nghttp2_bufs_free(&bufs); -} - -void test_nghttp2_session_reset_pending_headers(void) { - nghttp2_session *session; - nghttp2_session_callbacks callbacks; - nghttp2_stream *stream; - int32_t stream_id; - my_user_data ud; - - memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); - callbacks.send_callback = null_send_callback; - callbacks.on_frame_send_callback = on_frame_send_callback; - callbacks.on_frame_not_send_callback = on_frame_not_send_callback; - callbacks.on_stream_close_callback = on_stream_close_callback; - - nghttp2_session_client_new(&session, &callbacks, &ud); - - stream_id = nghttp2_submit_request(session, NULL, NULL, 0, NULL, NULL); - CU_ASSERT(stream_id >= 1); - - nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, stream_id, - NGHTTP2_CANCEL); - - session->remote_settings.max_concurrent_streams = 0; - - /* RST_STREAM cancels pending HEADERS and is not actually sent. */ - ud.frame_send_cb_called = 0; - CU_ASSERT(0 == nghttp2_session_send(session)); - - CU_ASSERT(0 == ud.frame_send_cb_called); - - stream = nghttp2_session_get_stream(session, stream_id); - - CU_ASSERT(NULL == stream); - - /* See HEADERS is not sent. on_stream_close is called just like - transmission failure. */ - session->remote_settings.max_concurrent_streams = 1; - - ud.frame_not_send_cb_called = 0; - ud.stream_close_error_code = 0; - CU_ASSERT(0 == nghttp2_session_send(session)); - - CU_ASSERT(1 == ud.frame_not_send_cb_called); - CU_ASSERT(NGHTTP2_HEADERS == ud.not_sent_frame_type); - CU_ASSERT(NGHTTP2_CANCEL == ud.stream_close_error_code); - - stream = nghttp2_session_get_stream(session, stream_id); - - CU_ASSERT(NULL == stream); - - nghttp2_session_del(session); -} - -void test_nghttp2_session_send_data_callback(void) { - nghttp2_session *session; - nghttp2_session_callbacks callbacks; - nghttp2_data_provider data_prd; - my_user_data ud; - accumulator acc; - nghttp2_frame_hd hd; - - memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); - callbacks.send_callback = accumulator_send_callback; - callbacks.send_data_callback = send_data_callback; - - data_prd.read_callback = no_copy_data_source_read_callback; - - acc.length = 0; - ud.acc = &acc; - - ud.data_source_length = NGHTTP2_DATA_PAYLOADLEN * 2; - - nghttp2_session_client_new(&session, &callbacks, &ud); - - nghttp2_session_open_stream(session, 1, NGHTTP2_STREAM_FLAG_NONE, - &pri_spec_default, NGHTTP2_STREAM_OPENING, NULL); - - nghttp2_submit_data(session, NGHTTP2_FLAG_END_STREAM, 1, &data_prd); - - CU_ASSERT(0 == nghttp2_session_send(session)); - - CU_ASSERT((NGHTTP2_FRAME_HDLEN + NGHTTP2_DATA_PAYLOADLEN) * 2 == acc.length); - - nghttp2_frame_unpack_frame_hd(&hd, acc.buf); - - CU_ASSERT(16384 == hd.length); - CU_ASSERT(NGHTTP2_DATA == hd.type); - CU_ASSERT(NGHTTP2_FLAG_NONE == hd.flags); - - nghttp2_frame_unpack_frame_hd(&hd, acc.buf + NGHTTP2_FRAME_HDLEN + hd.length); - - CU_ASSERT(16384 == hd.length); - CU_ASSERT(NGHTTP2_DATA == hd.type); - CU_ASSERT(NGHTTP2_FLAG_END_STREAM == hd.flags); - - nghttp2_session_del(session); -} - -void test_nghttp2_session_on_begin_headers_temporal_failure(void) { - nghttp2_session *session; - nghttp2_session_callbacks callbacks; - my_user_data ud; - nghttp2_bufs bufs; - nghttp2_mem *mem; - ssize_t rv; - nghttp2_hd_deflater deflater; - nghttp2_outbound_item *item; - - mem = nghttp2_mem_default(); - frame_pack_bufs_init(&bufs); - nghttp2_hd_deflate_init(&deflater, mem); - - memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); - callbacks.on_begin_headers_callback = - temporal_failure_on_begin_headers_callback; - callbacks.on_header_callback = on_header_callback; - callbacks.on_frame_recv_callback = on_frame_recv_callback; - callbacks.send_callback = null_send_callback; - nghttp2_session_server_new(&session, &callbacks, &ud); - - rv = pack_headers(&bufs, &deflater, 1, NGHTTP2_FLAG_END_HEADERS, reqnv, - ARRLEN(reqnv), mem); - CU_ASSERT(0 == rv); - - ud.header_cb_called = 0; - ud.frame_recv_cb_called = 0; - rv = nghttp2_session_mem_recv(session, bufs.head->buf.pos, - nghttp2_bufs_len(&bufs)); - CU_ASSERT(nghttp2_bufs_len(&bufs) == rv); - CU_ASSERT(0 == ud.header_cb_called); - CU_ASSERT(0 == ud.frame_recv_cb_called); - - item = nghttp2_session_get_next_ob_item(session); - CU_ASSERT(NGHTTP2_RST_STREAM == item->frame.hd.type); - CU_ASSERT(1 == item->frame.hd.stream_id); - CU_ASSERT(NGHTTP2_INTERNAL_ERROR == item->frame.rst_stream.error_code); - - nghttp2_session_del(session); - nghttp2_hd_deflate_free(&deflater); - - nghttp2_bufs_reset(&bufs); - /* check for PUSH_PROMISE */ - nghttp2_hd_deflate_init(&deflater, mem); - nghttp2_session_client_new(&session, &callbacks, &ud); - - nghttp2_session_open_stream(session, 1, NGHTTP2_STREAM_FLAG_NONE, - &pri_spec_default, NGHTTP2_STREAM_OPENING, NULL); - - rv = pack_push_promise(&bufs, &deflater, 1, NGHTTP2_FLAG_END_HEADERS, 2, - reqnv, ARRLEN(reqnv), mem); - CU_ASSERT(0 == rv); - - ud.header_cb_called = 0; - ud.frame_recv_cb_called = 0; - rv = nghttp2_session_mem_recv(session, bufs.head->buf.pos, - nghttp2_bufs_len(&bufs)); - CU_ASSERT(nghttp2_bufs_len(&bufs) == rv); - CU_ASSERT(0 == ud.header_cb_called); - CU_ASSERT(0 == ud.frame_recv_cb_called); - - item = nghttp2_session_get_next_ob_item(session); - CU_ASSERT(NGHTTP2_RST_STREAM == item->frame.hd.type); - CU_ASSERT(2 == item->frame.hd.stream_id); - CU_ASSERT(NGHTTP2_INTERNAL_ERROR == item->frame.rst_stream.error_code); - - nghttp2_session_del(session); - nghttp2_hd_deflate_free(&deflater); - nghttp2_bufs_free(&bufs); -} - -static void check_nghttp2_http_recv_headers_fail( - nghttp2_session *session, nghttp2_hd_deflater *deflater, int32_t stream_id, - int stream_state, const nghttp2_nv *nva, size_t nvlen) { - nghttp2_mem *mem; - ssize_t rv; - nghttp2_outbound_item *item; - nghttp2_bufs bufs; - my_user_data *ud; - - mem = nghttp2_mem_default(); - frame_pack_bufs_init(&bufs); - - ud = session->user_data; - - if (stream_state != -1) { - nghttp2_session_open_stream(session, stream_id, NGHTTP2_STREAM_FLAG_NONE, - &pri_spec_default, stream_state, NULL); - } - - rv = pack_headers(&bufs, deflater, stream_id, NGHTTP2_FLAG_END_HEADERS, nva, - nvlen, mem); - CU_ASSERT(0 == rv); - - ud->invalid_frame_recv_cb_called = 0; - - rv = nghttp2_session_mem_recv(session, bufs.head->buf.pos, - nghttp2_buf_len(&bufs.head->buf)); - - CU_ASSERT(nghttp2_buf_len(&bufs.head->buf) == rv); - - item = nghttp2_session_get_next_ob_item(session); - - CU_ASSERT(NGHTTP2_RST_STREAM == item->frame.hd.type); - CU_ASSERT(1 == ud->invalid_frame_recv_cb_called); - - CU_ASSERT(0 == nghttp2_session_send(session)); - - nghttp2_bufs_free(&bufs); -} - -static void check_nghttp2_http_recv_headers_ok( - nghttp2_session *session, nghttp2_hd_deflater *deflater, int32_t stream_id, - int stream_state, const nghttp2_nv *nva, size_t nvlen) { - nghttp2_mem *mem; - ssize_t rv; - nghttp2_bufs bufs; - my_user_data *ud; - - mem = nghttp2_mem_default(); - frame_pack_bufs_init(&bufs); - - ud = session->user_data; - - if (stream_state != -1) { - nghttp2_session_open_stream(session, stream_id, NGHTTP2_STREAM_FLAG_NONE, - &pri_spec_default, stream_state, NULL); - } - - rv = pack_headers(&bufs, deflater, stream_id, NGHTTP2_FLAG_END_HEADERS, nva, - nvlen, mem); - CU_ASSERT(0 == rv); - - ud->frame_recv_cb_called = 0; - - rv = nghttp2_session_mem_recv(session, bufs.head->buf.pos, - nghttp2_buf_len(&bufs.head->buf)); - - CU_ASSERT(nghttp2_buf_len(&bufs.head->buf) == rv); - CU_ASSERT(NULL == nghttp2_session_get_next_ob_item(session)); - CU_ASSERT(1 == ud->frame_recv_cb_called); - - nghttp2_bufs_free(&bufs); -} - -void test_nghttp2_http_mandatory_headers(void) { - nghttp2_session *session; - nghttp2_session_callbacks callbacks; - nghttp2_hd_deflater deflater; - nghttp2_mem *mem; - my_user_data ud; - /* test case for response */ - const nghttp2_nv nostatus_resnv[] = {MAKE_NV("server", "foo")}; - const nghttp2_nv dupstatus_resnv[] = {MAKE_NV(":status", "200"), - MAKE_NV(":status", "200")}; - const nghttp2_nv badpseudo_resnv[] = {MAKE_NV(":status", "200"), - MAKE_NV(":scheme", "https")}; - const nghttp2_nv latepseudo_resnv[] = {MAKE_NV("server", "foo"), - MAKE_NV(":status", "200")}; - const nghttp2_nv badstatus_resnv[] = {MAKE_NV(":status", "2000")}; - const nghttp2_nv badcl_resnv[] = {MAKE_NV(":status", "200"), - MAKE_NV("content-length", "-1")}; - const nghttp2_nv dupcl_resnv[] = {MAKE_NV(":status", "200"), - MAKE_NV("content-length", "0"), - MAKE_NV("content-length", "0")}; - const nghttp2_nv badhd_resnv[] = {MAKE_NV(":status", "200"), - MAKE_NV("connection", "close")}; - - /* test case for request */ - const nghttp2_nv nopath_reqnv[] = {MAKE_NV(":scheme", "https"), - MAKE_NV(":method", "GET"), - MAKE_NV(":authority", "localhost")}; - const nghttp2_nv earlyconnect_reqnv[] = { - MAKE_NV(":method", "CONNECT"), MAKE_NV(":scheme", "https"), - MAKE_NV(":path", "/"), MAKE_NV(":authority", "localhost")}; - const nghttp2_nv lateconnect_reqnv[] = { - MAKE_NV(":scheme", "https"), MAKE_NV(":path", "/"), - MAKE_NV(":method", "CONNECT"), MAKE_NV(":authority", "localhost")}; - const nghttp2_nv duppath_reqnv[] = { - MAKE_NV(":scheme", "https"), MAKE_NV(":method", "GET"), - MAKE_NV(":authority", "localhost"), MAKE_NV(":path", "/"), - MAKE_NV(":path", "/")}; - const nghttp2_nv badcl_reqnv[] = { - MAKE_NV(":scheme", "https"), MAKE_NV(":method", "POST"), - MAKE_NV(":authority", "localhost"), MAKE_NV(":path", "/"), - MAKE_NV("content-length", "-1")}; - const nghttp2_nv dupcl_reqnv[] = { - MAKE_NV(":scheme", "https"), MAKE_NV(":method", "POST"), - MAKE_NV(":authority", "localhost"), MAKE_NV(":path", "/"), - MAKE_NV("content-length", "0"), MAKE_NV("content-length", "0")}; - const nghttp2_nv badhd_reqnv[] = { - MAKE_NV(":scheme", "https"), MAKE_NV(":method", "GET"), - MAKE_NV(":authority", "localhost"), MAKE_NV(":path", "/"), - MAKE_NV("connection", "close")}; - const nghttp2_nv badauthority_reqnv[] = { - MAKE_NV(":scheme", "https"), MAKE_NV(":method", "GET"), - MAKE_NV(":authority", "\x0d\x0alocalhost"), MAKE_NV(":path", "/")}; - const nghttp2_nv badhdbtw_reqnv[] = { - MAKE_NV(":scheme", "https"), MAKE_NV(":method", "GET"), - MAKE_NV("foo", "\x0d\x0a"), MAKE_NV(":authority", "localhost"), - MAKE_NV(":path", "/")}; - const nghttp2_nv asteriskget1_reqnv[] = { - MAKE_NV(":path", "*"), MAKE_NV(":scheme", "https"), - MAKE_NV(":authority", "localhost"), MAKE_NV(":method", "GET")}; - const nghttp2_nv asteriskget2_reqnv[] = { - MAKE_NV(":scheme", "https"), MAKE_NV(":authority", "localhost"), - MAKE_NV(":method", "GET"), MAKE_NV(":path", "*")}; - const nghttp2_nv asteriskoptions1_reqnv[] = { - MAKE_NV(":path", "*"), MAKE_NV(":scheme", "https"), - MAKE_NV(":authority", "localhost"), MAKE_NV(":method", "OPTIONS")}; - const nghttp2_nv asteriskoptions2_reqnv[] = { - MAKE_NV(":scheme", "https"), MAKE_NV(":authority", "localhost"), - MAKE_NV(":method", "OPTIONS"), MAKE_NV(":path", "*")}; - - mem = nghttp2_mem_default(); - - memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); - callbacks.send_callback = null_send_callback; - callbacks.on_frame_recv_callback = on_frame_recv_callback; - callbacks.on_invalid_frame_recv_callback = on_invalid_frame_recv_callback; - - nghttp2_session_client_new(&session, &callbacks, &ud); - - nghttp2_hd_deflate_init(&deflater, mem); - - /* response header lacks :status */ - check_nghttp2_http_recv_headers_fail(session, &deflater, 1, - NGHTTP2_STREAM_OPENING, nostatus_resnv, - ARRLEN(nostatus_resnv)); - - /* response header has 2 :status */ - check_nghttp2_http_recv_headers_fail(session, &deflater, 3, - NGHTTP2_STREAM_OPENING, dupstatus_resnv, - ARRLEN(dupstatus_resnv)); - - /* response header has bad pseudo header :scheme */ - check_nghttp2_http_recv_headers_fail(session, &deflater, 5, - NGHTTP2_STREAM_OPENING, badpseudo_resnv, - ARRLEN(badpseudo_resnv)); - - /* response header has :status after regular header field */ - check_nghttp2_http_recv_headers_fail(session, &deflater, 7, - NGHTTP2_STREAM_OPENING, latepseudo_resnv, - ARRLEN(latepseudo_resnv)); - - /* response header has bad status code */ - check_nghttp2_http_recv_headers_fail(session, &deflater, 9, - NGHTTP2_STREAM_OPENING, badstatus_resnv, - ARRLEN(badstatus_resnv)); - - /* response header has bad content-length */ - check_nghttp2_http_recv_headers_fail(session, &deflater, 11, - NGHTTP2_STREAM_OPENING, badcl_resnv, - ARRLEN(badcl_resnv)); - - /* response header has multiple content-length */ - check_nghttp2_http_recv_headers_fail(session, &deflater, 13, - NGHTTP2_STREAM_OPENING, dupcl_resnv, - ARRLEN(dupcl_resnv)); - - /* response header has disallowed header field */ - check_nghttp2_http_recv_headers_fail(session, &deflater, 15, - NGHTTP2_STREAM_OPENING, badhd_resnv, - ARRLEN(badhd_resnv)); - - nghttp2_hd_deflate_free(&deflater); - - nghttp2_session_del(session); - - /* check server side */ - nghttp2_session_server_new(&session, &callbacks, &ud); - - nghttp2_hd_deflate_init(&deflater, mem); - - /* request header has no :path */ - check_nghttp2_http_recv_headers_fail(session, &deflater, 1, -1, nopath_reqnv, - ARRLEN(nopath_reqnv)); - - /* request header has CONNECT method, but followed by :path */ - check_nghttp2_http_recv_headers_fail(session, &deflater, 3, -1, - earlyconnect_reqnv, - ARRLEN(earlyconnect_reqnv)); - - /* request header has CONNECT method following :path */ - check_nghttp2_http_recv_headers_fail( - session, &deflater, 5, -1, lateconnect_reqnv, ARRLEN(lateconnect_reqnv)); - - /* request header has multiple :path */ - check_nghttp2_http_recv_headers_fail(session, &deflater, 7, -1, duppath_reqnv, - ARRLEN(duppath_reqnv)); - - /* request header has bad content-length */ - check_nghttp2_http_recv_headers_fail(session, &deflater, 9, -1, badcl_reqnv, - ARRLEN(badcl_reqnv)); - - /* request header has multiple content-length */ - check_nghttp2_http_recv_headers_fail(session, &deflater, 11, -1, dupcl_reqnv, - ARRLEN(dupcl_reqnv)); - - /* request header has disallowed header field */ - check_nghttp2_http_recv_headers_fail(session, &deflater, 13, -1, badhd_reqnv, - ARRLEN(badhd_reqnv)); - - /* request header has :authority header field containing illegal - characters */ - check_nghttp2_http_recv_headers_fail(session, &deflater, 15, -1, - badauthority_reqnv, - ARRLEN(badauthority_reqnv)); - - /* request header has regular header field containing illegal - character before all mandatory header fields are seen. */ - check_nghttp2_http_recv_headers_fail(session, &deflater, 17, -1, - badhdbtw_reqnv, ARRLEN(badhdbtw_reqnv)); - - /* request header has "*" in :path header field while method is GET. - :path is received before :method */ - check_nghttp2_http_recv_headers_fail(session, &deflater, 19, -1, - asteriskget1_reqnv, - ARRLEN(asteriskget1_reqnv)); - - /* request header has "*" in :path header field while method is GET. - :method is received before :path */ - check_nghttp2_http_recv_headers_fail(session, &deflater, 21, -1, - asteriskget2_reqnv, - ARRLEN(asteriskget2_reqnv)); - - /* OPTIONS method can include "*" in :path header field. :path is - received before :method. */ - check_nghttp2_http_recv_headers_ok(session, &deflater, 23, -1, - asteriskoptions1_reqnv, - ARRLEN(asteriskoptions1_reqnv)); - - /* OPTIONS method can include "*" in :path header field. :method is - received before :path. */ - check_nghttp2_http_recv_headers_ok(session, &deflater, 25, -1, - asteriskoptions2_reqnv, - ARRLEN(asteriskoptions2_reqnv)); - - nghttp2_hd_deflate_free(&deflater); - - nghttp2_session_del(session); -} - -void test_nghttp2_http_content_length(void) { - nghttp2_session *session; - nghttp2_session_callbacks callbacks; - nghttp2_hd_deflater deflater; - nghttp2_mem *mem; - nghttp2_bufs bufs; - ssize_t rv; - nghttp2_stream *stream; - const nghttp2_nv cl_resnv[] = {MAKE_NV(":status", "200"), - MAKE_NV("te", "trailers"), - MAKE_NV("content-length", "9000000000")}; - const nghttp2_nv cl_reqnv[] = { - MAKE_NV(":path", "/"), MAKE_NV(":method", "PUT"), - MAKE_NV(":scheme", "https"), MAKE_NV("te", "trailers"), - MAKE_NV("host", "localhost"), MAKE_NV("content-length", "9000000000")}; - - mem = nghttp2_mem_default(); - frame_pack_bufs_init(&bufs); - - memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); - callbacks.send_callback = null_send_callback; - - nghttp2_session_client_new(&session, &callbacks, NULL); - - nghttp2_hd_deflate_init(&deflater, mem); - - stream = nghttp2_session_open_stream(session, 1, NGHTTP2_STREAM_FLAG_NONE, - &pri_spec_default, - NGHTTP2_STREAM_OPENING, NULL); - - rv = pack_headers(&bufs, &deflater, 1, NGHTTP2_FLAG_END_HEADERS, cl_resnv, - ARRLEN(cl_resnv), mem); - CU_ASSERT(0 == rv); - - rv = nghttp2_session_mem_recv(session, bufs.head->buf.pos, - nghttp2_buf_len(&bufs.head->buf)); - - CU_ASSERT(nghttp2_buf_len(&bufs.head->buf) == rv); - CU_ASSERT(NULL == nghttp2_session_get_next_ob_item(session)); - CU_ASSERT(9000000000LL == stream->content_length); - CU_ASSERT(200 == stream->status_code); - - nghttp2_hd_deflate_free(&deflater); - - nghttp2_session_del(session); - - nghttp2_bufs_reset(&bufs); - - /* check server side */ - nghttp2_session_server_new(&session, &callbacks, NULL); - - nghttp2_hd_deflate_init(&deflater, mem); - - rv = pack_headers(&bufs, &deflater, 1, NGHTTP2_FLAG_END_HEADERS, cl_reqnv, - ARRLEN(cl_reqnv), mem); - CU_ASSERT(0 == rv); - - rv = nghttp2_session_mem_recv(session, bufs.head->buf.pos, - nghttp2_buf_len(&bufs.head->buf)); - - CU_ASSERT(nghttp2_buf_len(&bufs.head->buf) == rv); - - stream = nghttp2_session_get_stream(session, 1); - - CU_ASSERT(NULL == nghttp2_session_get_next_ob_item(session)); - CU_ASSERT(9000000000LL == stream->content_length); - - nghttp2_hd_deflate_free(&deflater); - - nghttp2_session_del(session); - - nghttp2_bufs_free(&bufs); -} - -void test_nghttp2_http_content_length_mismatch(void) { - nghttp2_session *session; - nghttp2_session_callbacks callbacks; - nghttp2_hd_deflater deflater; - nghttp2_mem *mem; - nghttp2_bufs bufs; - ssize_t rv; - const nghttp2_nv cl_reqnv[] = { - MAKE_NV(":path", "/"), MAKE_NV(":method", "PUT"), - MAKE_NV(":authority", "localhost"), MAKE_NV(":scheme", "https"), - MAKE_NV("content-length", "20")}; - nghttp2_outbound_item *item; - nghttp2_frame_hd hd; - - mem = nghttp2_mem_default(); - frame_pack_bufs_init(&bufs); - - memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); - callbacks.send_callback = null_send_callback; - - nghttp2_session_server_new(&session, &callbacks, NULL); - - nghttp2_hd_deflate_init(&deflater, mem); - - /* header says content-length: 20, but HEADERS has END_STREAM flag set */ - rv = pack_headers(&bufs, &deflater, 1, - NGHTTP2_FLAG_END_HEADERS | NGHTTP2_FLAG_END_STREAM, - cl_reqnv, ARRLEN(cl_reqnv), mem); - CU_ASSERT(0 == rv); - - rv = nghttp2_session_mem_recv(session, bufs.head->buf.pos, - nghttp2_buf_len(&bufs.head->buf)); - - CU_ASSERT(nghttp2_buf_len(&bufs.head->buf) == rv); - - item = nghttp2_session_get_next_ob_item(session); - CU_ASSERT(NGHTTP2_RST_STREAM == item->frame.hd.type); - - CU_ASSERT(0 == nghttp2_session_send(session)); - - nghttp2_bufs_reset(&bufs); - - /* header says content-length: 20, but DATA has 0 byte */ - rv = pack_headers(&bufs, &deflater, 3, NGHTTP2_FLAG_END_HEADERS, cl_reqnv, - ARRLEN(cl_reqnv), mem); - CU_ASSERT(0 == rv); - - nghttp2_frame_hd_init(&hd, 0, NGHTTP2_DATA, NGHTTP2_FLAG_END_STREAM, 3); - nghttp2_frame_pack_frame_hd(bufs.head->buf.last, &hd); - bufs.head->buf.last += NGHTTP2_FRAME_HDLEN; - - rv = nghttp2_session_mem_recv(session, bufs.head->buf.pos, - nghttp2_buf_len(&bufs.head->buf)); - - CU_ASSERT(nghttp2_buf_len(&bufs.head->buf) == rv); - - item = nghttp2_session_get_next_ob_item(session); - CU_ASSERT(NGHTTP2_RST_STREAM == item->frame.hd.type); - - CU_ASSERT(0 == nghttp2_session_send(session)); - - nghttp2_bufs_reset(&bufs); - - /* header says content-length: 20, but DATA has 21 bytes */ - rv = pack_headers(&bufs, &deflater, 5, NGHTTP2_FLAG_END_HEADERS, cl_reqnv, - ARRLEN(cl_reqnv), mem); - CU_ASSERT(0 == rv); - - nghttp2_frame_hd_init(&hd, 21, NGHTTP2_DATA, NGHTTP2_FLAG_END_STREAM, 5); - nghttp2_frame_pack_frame_hd(bufs.head->buf.last, &hd); - bufs.head->buf.last += NGHTTP2_FRAME_HDLEN + 21; - - rv = nghttp2_session_mem_recv(session, bufs.head->buf.pos, - nghttp2_buf_len(&bufs.head->buf)); - - CU_ASSERT(nghttp2_buf_len(&bufs.head->buf) == rv); - - item = nghttp2_session_get_next_ob_item(session); - CU_ASSERT(NGHTTP2_RST_STREAM == item->frame.hd.type); - - CU_ASSERT(0 == nghttp2_session_send(session)); - - nghttp2_bufs_reset(&bufs); - - nghttp2_hd_deflate_free(&deflater); - - nghttp2_session_del(session); - - nghttp2_bufs_free(&bufs); -} - -void test_nghttp2_http_non_final_response(void) { - nghttp2_session *session; - nghttp2_session_callbacks callbacks; - nghttp2_hd_deflater deflater; - nghttp2_mem *mem; - nghttp2_bufs bufs; - ssize_t rv; - const nghttp2_nv nonfinal_resnv[] = { - MAKE_NV(":status", "100"), - }; - nghttp2_outbound_item *item; - nghttp2_frame_hd hd; - - mem = nghttp2_mem_default(); - frame_pack_bufs_init(&bufs); - - memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); - callbacks.send_callback = null_send_callback; - - nghttp2_session_client_new(&session, &callbacks, NULL); - - nghttp2_hd_deflate_init(&deflater, mem); - - /* non-final HEADERS with END_STREAM is illegal */ - nghttp2_session_open_stream(session, 1, NGHTTP2_STREAM_FLAG_NONE, - &pri_spec_default, NGHTTP2_STREAM_OPENING, NULL); - - rv = pack_headers(&bufs, &deflater, 1, - NGHTTP2_FLAG_END_HEADERS | NGHTTP2_FLAG_END_STREAM, - nonfinal_resnv, ARRLEN(nonfinal_resnv), mem); - CU_ASSERT(0 == rv); - - rv = nghttp2_session_mem_recv(session, bufs.head->buf.pos, - nghttp2_buf_len(&bufs.head->buf)); - - CU_ASSERT(nghttp2_buf_len(&bufs.head->buf) == rv); - - item = nghttp2_session_get_next_ob_item(session); - CU_ASSERT(NGHTTP2_RST_STREAM == item->frame.hd.type); - - CU_ASSERT(0 == nghttp2_session_send(session)); - - nghttp2_bufs_reset(&bufs); - - /* non-final HEADERS followed by non-empty DATA is illegal */ - nghttp2_session_open_stream(session, 3, NGHTTP2_STREAM_FLAG_NONE, - &pri_spec_default, NGHTTP2_STREAM_OPENING, NULL); - - rv = pack_headers(&bufs, &deflater, 3, NGHTTP2_FLAG_END_HEADERS, - nonfinal_resnv, ARRLEN(nonfinal_resnv), mem); - CU_ASSERT(0 == rv); - - nghttp2_frame_hd_init(&hd, 10, NGHTTP2_DATA, NGHTTP2_FLAG_END_STREAM, 3); - nghttp2_frame_pack_frame_hd(bufs.head->buf.last, &hd); - bufs.head->buf.last += NGHTTP2_FRAME_HDLEN + 10; - - rv = nghttp2_session_mem_recv(session, bufs.head->buf.pos, - nghttp2_buf_len(&bufs.head->buf)); - - CU_ASSERT(nghttp2_buf_len(&bufs.head->buf) == rv); - - item = nghttp2_session_get_next_ob_item(session); - CU_ASSERT(NGHTTP2_RST_STREAM == item->frame.hd.type); - - CU_ASSERT(0 == nghttp2_session_send(session)); - - nghttp2_bufs_reset(&bufs); - - /* non-final HEADERS followed by empty DATA (without END_STREAM) is - ok */ - nghttp2_session_open_stream(session, 5, NGHTTP2_STREAM_FLAG_NONE, - &pri_spec_default, NGHTTP2_STREAM_OPENING, NULL); - - rv = pack_headers(&bufs, &deflater, 5, NGHTTP2_FLAG_END_HEADERS, - nonfinal_resnv, ARRLEN(nonfinal_resnv), mem); - CU_ASSERT(0 == rv); - - nghttp2_frame_hd_init(&hd, 0, NGHTTP2_DATA, NGHTTP2_FLAG_NONE, 5); - nghttp2_frame_pack_frame_hd(bufs.head->buf.last, &hd); - bufs.head->buf.last += NGHTTP2_FRAME_HDLEN; - - rv = nghttp2_session_mem_recv(session, bufs.head->buf.pos, - nghttp2_buf_len(&bufs.head->buf)); - - CU_ASSERT(nghttp2_buf_len(&bufs.head->buf) == rv); - - CU_ASSERT(NULL == nghttp2_session_get_next_ob_item(session)); - - nghttp2_bufs_reset(&bufs); - - /* non-final HEADERS followed by empty DATA (with END_STREAM) is - illegal */ - nghttp2_session_open_stream(session, 7, NGHTTP2_STREAM_FLAG_NONE, - &pri_spec_default, NGHTTP2_STREAM_OPENING, NULL); - - rv = pack_headers(&bufs, &deflater, 7, NGHTTP2_FLAG_END_HEADERS, - nonfinal_resnv, ARRLEN(nonfinal_resnv), mem); - CU_ASSERT(0 == rv); - - nghttp2_frame_hd_init(&hd, 0, NGHTTP2_DATA, NGHTTP2_FLAG_END_STREAM, 7); - nghttp2_frame_pack_frame_hd(bufs.head->buf.last, &hd); - bufs.head->buf.last += NGHTTP2_FRAME_HDLEN; - - rv = nghttp2_session_mem_recv(session, bufs.head->buf.pos, - nghttp2_buf_len(&bufs.head->buf)); - - CU_ASSERT(nghttp2_buf_len(&bufs.head->buf) == rv); - - item = nghttp2_session_get_next_ob_item(session); - - CU_ASSERT(NGHTTP2_RST_STREAM == item->frame.hd.type); - - CU_ASSERT(0 == nghttp2_session_send(session)); - - nghttp2_bufs_reset(&bufs); - - /* non-final HEADERS followed by final HEADERS is OK */ - nghttp2_session_open_stream(session, 9, NGHTTP2_STREAM_FLAG_NONE, - &pri_spec_default, NGHTTP2_STREAM_OPENING, NULL); - - rv = pack_headers(&bufs, &deflater, 9, NGHTTP2_FLAG_END_HEADERS, - nonfinal_resnv, ARRLEN(nonfinal_resnv), mem); - CU_ASSERT(0 == rv); - - rv = nghttp2_session_mem_recv(session, bufs.head->buf.pos, - nghttp2_buf_len(&bufs.head->buf)); - - CU_ASSERT(nghttp2_buf_len(&bufs.head->buf) == rv); - - nghttp2_bufs_reset(&bufs); - - rv = pack_headers(&bufs, &deflater, 9, NGHTTP2_FLAG_END_HEADERS, resnv, - ARRLEN(resnv), mem); - CU_ASSERT(0 == rv); - - rv = nghttp2_session_mem_recv(session, bufs.head->buf.pos, - nghttp2_buf_len(&bufs.head->buf)); - - CU_ASSERT(nghttp2_buf_len(&bufs.head->buf) == rv); - - CU_ASSERT(NULL == nghttp2_session_get_next_ob_item(session)); - - nghttp2_bufs_reset(&bufs); - - nghttp2_hd_deflate_free(&deflater); - - nghttp2_session_del(session); - - nghttp2_bufs_free(&bufs); -} - -void test_nghttp2_http_trailer_headers(void) { - nghttp2_session *session; - nghttp2_session_callbacks callbacks; - nghttp2_hd_deflater deflater; - nghttp2_mem *mem; - nghttp2_bufs bufs; - ssize_t rv; - const nghttp2_nv trailer_reqnv[] = { - MAKE_NV("foo", "bar"), - }; - nghttp2_outbound_item *item; - - mem = nghttp2_mem_default(); - frame_pack_bufs_init(&bufs); - - memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); - callbacks.send_callback = null_send_callback; - - nghttp2_session_server_new(&session, &callbacks, NULL); - - nghttp2_hd_deflate_init(&deflater, mem); - - /* good trailer header */ - rv = pack_headers(&bufs, &deflater, 1, NGHTTP2_FLAG_END_HEADERS, reqnv, - ARRLEN(reqnv), mem); - CU_ASSERT(0 == rv); - - rv = nghttp2_session_mem_recv(session, bufs.head->buf.pos, - nghttp2_buf_len(&bufs.head->buf)); - - CU_ASSERT(nghttp2_buf_len(&bufs.head->buf) == rv); - - nghttp2_bufs_reset(&bufs); - - rv = pack_headers(&bufs, &deflater, 1, - NGHTTP2_FLAG_END_HEADERS | NGHTTP2_FLAG_END_STREAM, - trailer_reqnv, ARRLEN(trailer_reqnv), mem); - CU_ASSERT(0 == rv); - - rv = nghttp2_session_mem_recv(session, bufs.head->buf.pos, - nghttp2_buf_len(&bufs.head->buf)); - - CU_ASSERT(nghttp2_buf_len(&bufs.head->buf) == rv); - - CU_ASSERT(NULL == nghttp2_session_get_next_ob_item(session)); - - nghttp2_bufs_reset(&bufs); - - /* trailer header without END_STREAM is illegal */ - rv = pack_headers(&bufs, &deflater, 3, NGHTTP2_FLAG_END_HEADERS, reqnv, - ARRLEN(reqnv), mem); - CU_ASSERT(0 == rv); - - rv = nghttp2_session_mem_recv(session, bufs.head->buf.pos, - nghttp2_buf_len(&bufs.head->buf)); - - CU_ASSERT(nghttp2_buf_len(&bufs.head->buf) == rv); - - nghttp2_bufs_reset(&bufs); - - rv = pack_headers(&bufs, &deflater, 3, NGHTTP2_FLAG_END_HEADERS, - trailer_reqnv, ARRLEN(trailer_reqnv), mem); - CU_ASSERT(0 == rv); - - rv = nghttp2_session_mem_recv(session, bufs.head->buf.pos, - nghttp2_buf_len(&bufs.head->buf)); - - CU_ASSERT(nghttp2_buf_len(&bufs.head->buf) == rv); - - item = nghttp2_session_get_next_ob_item(session); - - CU_ASSERT(NGHTTP2_RST_STREAM == item->frame.hd.type); - - CU_ASSERT(0 == nghttp2_session_send(session)); - - nghttp2_bufs_reset(&bufs); - - /* trailer header including pseudo header field is illegal */ - rv = pack_headers(&bufs, &deflater, 5, NGHTTP2_FLAG_END_HEADERS, reqnv, - ARRLEN(reqnv), mem); - CU_ASSERT(0 == rv); - - rv = nghttp2_session_mem_recv(session, bufs.head->buf.pos, - nghttp2_buf_len(&bufs.head->buf)); - - CU_ASSERT(nghttp2_buf_len(&bufs.head->buf) == rv); - - nghttp2_bufs_reset(&bufs); - - rv = pack_headers(&bufs, &deflater, 5, NGHTTP2_FLAG_END_HEADERS, reqnv, - ARRLEN(reqnv), mem); - CU_ASSERT(0 == rv); - - rv = nghttp2_session_mem_recv(session, bufs.head->buf.pos, - nghttp2_buf_len(&bufs.head->buf)); - - CU_ASSERT(nghttp2_buf_len(&bufs.head->buf) == rv); - - item = nghttp2_session_get_next_ob_item(session); - - CU_ASSERT(NGHTTP2_RST_STREAM == item->frame.hd.type); - - CU_ASSERT(0 == nghttp2_session_send(session)); - - nghttp2_bufs_reset(&bufs); - - nghttp2_hd_deflate_free(&deflater); - - nghttp2_session_del(session); - - nghttp2_bufs_free(&bufs); -} - -void test_nghttp2_http_ignore_regular_header(void) { - nghttp2_session *session; - nghttp2_session_callbacks callbacks; - nghttp2_hd_deflater deflater; - nghttp2_mem *mem; - nghttp2_bufs bufs; - ssize_t rv; - my_user_data ud; - const nghttp2_nv bad_reqnv[] = { - MAKE_NV(":authority", "localhost"), MAKE_NV(":scheme", "https"), - MAKE_NV(":path", "/"), MAKE_NV(":method", "GET"), - MAKE_NV("foo", "\x0zzz"), MAKE_NV("bar", "buzz"), - }; - const nghttp2_nv bad_ansnv[] = { - MAKE_NV(":authority", "localhost"), MAKE_NV(":scheme", "https"), - MAKE_NV(":path", "/"), MAKE_NV(":method", "GET"), MAKE_NV("bar", "buzz")}; - ssize_t proclen; - size_t i; - - mem = nghttp2_mem_default(); - frame_pack_bufs_init(&bufs); - - memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); - callbacks.send_callback = null_send_callback; - callbacks.on_header_callback = pause_on_header_callback; - - nghttp2_session_server_new(&session, &callbacks, &ud); - nghttp2_hd_deflate_init(&deflater, mem); - - rv = pack_headers(&bufs, &deflater, 1, - NGHTTP2_FLAG_END_HEADERS | NGHTTP2_FLAG_END_STREAM, - bad_reqnv, ARRLEN(bad_reqnv), mem); - - CU_ASSERT_FATAL(0 == rv); - - proclen = 0; - - for (i = 0; i < 4; ++i) { - rv = nghttp2_session_mem_recv(session, bufs.head->buf.pos + proclen, - nghttp2_buf_len(&bufs.head->buf) - proclen); - CU_ASSERT_FATAL(rv > 0); - proclen += rv; - CU_ASSERT(nghttp2_nv_equal(&bad_ansnv[i], &ud.nv)); - } - - rv = nghttp2_session_mem_recv(session, bufs.head->buf.pos + proclen, - nghttp2_buf_len(&bufs.head->buf) - proclen); - CU_ASSERT_FATAL(rv > 0); - /* header field "foo" must be ignored because it has illegal value. - So we have "bar" header field for 5th header. */ - CU_ASSERT(nghttp2_nv_equal(&bad_ansnv[4], &ud.nv)); - proclen += rv; - - CU_ASSERT(nghttp2_buf_len(&bufs.head->buf) == proclen); - - nghttp2_hd_deflate_free(&deflater); - nghttp2_session_del(session); - nghttp2_bufs_free(&bufs); -} - -void test_nghttp2_http_ignore_content_length(void) { - nghttp2_session *session; - nghttp2_session_callbacks callbacks; - nghttp2_hd_deflater deflater; - nghttp2_mem *mem; - nghttp2_bufs bufs; - ssize_t rv; - const nghttp2_nv cl_resnv[] = {MAKE_NV(":status", "304"), - MAKE_NV("content-length", "20")}; - const nghttp2_nv conn_reqnv[] = {MAKE_NV(":authority", "localhost"), - MAKE_NV(":method", "CONNECT"), - MAKE_NV("content-length", "999999")}; - nghttp2_stream *stream; - - mem = nghttp2_mem_default(); - frame_pack_bufs_init(&bufs); - - memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); - callbacks.send_callback = null_send_callback; - - nghttp2_session_client_new(&session, &callbacks, NULL); - - nghttp2_hd_deflate_init(&deflater, mem); - - /* If status 304, content-length must be ignored */ - nghttp2_session_open_stream(session, 1, NGHTTP2_STREAM_FLAG_NONE, - &pri_spec_default, NGHTTP2_STREAM_OPENING, NULL); - - rv = pack_headers(&bufs, &deflater, 1, - NGHTTP2_FLAG_END_HEADERS | NGHTTP2_FLAG_END_STREAM, - cl_resnv, ARRLEN(cl_resnv), mem); - CU_ASSERT(0 == rv); - - rv = nghttp2_session_mem_recv(session, bufs.head->buf.pos, - nghttp2_buf_len(&bufs.head->buf)); - - CU_ASSERT(nghttp2_buf_len(&bufs.head->buf) == rv); - - CU_ASSERT(NULL == nghttp2_session_get_next_ob_item(session)); - - nghttp2_bufs_reset(&bufs); - - nghttp2_hd_deflate_free(&deflater); - nghttp2_session_del(session); - - /* If request method is CONNECT, content-length must be ignored */ - nghttp2_session_server_new(&session, &callbacks, NULL); - - nghttp2_hd_deflate_init(&deflater, mem); - - rv = pack_headers(&bufs, &deflater, 1, NGHTTP2_FLAG_END_HEADERS, conn_reqnv, - ARRLEN(conn_reqnv), mem); - - CU_ASSERT(0 == rv); - - rv = nghttp2_session_mem_recv(session, bufs.head->buf.pos, - nghttp2_buf_len(&bufs.head->buf)); - - CU_ASSERT(nghttp2_buf_len(&bufs.head->buf) == rv); - - CU_ASSERT(NULL == nghttp2_session_get_next_ob_item(session)); - - stream = nghttp2_session_get_stream(session, 1); - - CU_ASSERT(-1 == stream->content_length); - CU_ASSERT((stream->http_flags & NGHTTP2_HTTP_FLAG_METH_CONNECT) > 0); - - nghttp2_hd_deflate_free(&deflater); - nghttp2_session_del(session); - nghttp2_bufs_free(&bufs); -} - -void test_nghttp2_http_record_request_method(void) { - nghttp2_session *session; - nghttp2_session_callbacks callbacks; - const nghttp2_nv conn_reqnv[] = {MAKE_NV(":method", "CONNECT"), - MAKE_NV(":authority", "localhost")}; - const nghttp2_nv conn_resnv[] = {MAKE_NV(":status", "200"), - MAKE_NV("content-length", "9999")}; - nghttp2_stream *stream; - ssize_t rv; - nghttp2_bufs bufs; - nghttp2_hd_deflater deflater; - nghttp2_mem *mem; - - mem = nghttp2_mem_default(); - frame_pack_bufs_init(&bufs); - - memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); - callbacks.send_callback = null_send_callback; - - nghttp2_session_client_new(&session, &callbacks, NULL); - - nghttp2_hd_deflate_init(&deflater, mem); - - CU_ASSERT(1 == nghttp2_submit_request(session, NULL, conn_reqnv, - ARRLEN(conn_reqnv), NULL, NULL)); - - CU_ASSERT(0 == nghttp2_session_send(session)); - - stream = nghttp2_session_get_stream(session, 1); - - CU_ASSERT(NGHTTP2_HTTP_FLAG_METH_CONNECT == stream->http_flags); - - rv = pack_headers(&bufs, &deflater, 1, NGHTTP2_FLAG_END_HEADERS, conn_resnv, - ARRLEN(conn_resnv), mem); - CU_ASSERT(0 == rv); - - rv = nghttp2_session_mem_recv(session, bufs.head->buf.pos, - nghttp2_buf_len(&bufs.head->buf)); - - CU_ASSERT(nghttp2_buf_len(&bufs.head->buf) == rv); - - CU_ASSERT((NGHTTP2_HTTP_FLAG_METH_CONNECT & stream->http_flags) > 0); - CU_ASSERT(-1 == stream->content_length); - - nghttp2_hd_deflate_free(&deflater); - nghttp2_session_del(session); - nghttp2_bufs_free(&bufs); -} - -void test_nghttp2_http_push_promise(void) { - nghttp2_session *session; - nghttp2_session_callbacks callbacks; - nghttp2_hd_deflater deflater; - nghttp2_mem *mem; - nghttp2_bufs bufs; - ssize_t rv; - nghttp2_stream *stream; - const nghttp2_nv bad_reqnv[] = {MAKE_NV(":method", "GET")}; - nghttp2_outbound_item *item; - - mem = nghttp2_mem_default(); - frame_pack_bufs_init(&bufs); - - memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); - callbacks.send_callback = null_send_callback; - - /* good PUSH_PROMISE case */ - nghttp2_session_client_new(&session, &callbacks, NULL); - - nghttp2_hd_deflate_init(&deflater, mem); - - nghttp2_session_open_stream(session, 1, NGHTTP2_STREAM_FLAG_NONE, - &pri_spec_default, NGHTTP2_STREAM_OPENING, NULL); - - rv = pack_push_promise(&bufs, &deflater, 1, NGHTTP2_FLAG_END_HEADERS, 2, - reqnv, ARRLEN(reqnv), mem); - CU_ASSERT(0 == rv); - - rv = nghttp2_session_mem_recv(session, bufs.head->buf.pos, - nghttp2_buf_len(&bufs.head->buf)); - - CU_ASSERT(nghttp2_buf_len(&bufs.head->buf) == rv); - - CU_ASSERT(NULL == nghttp2_session_get_next_ob_item(session)); - - stream = nghttp2_session_get_stream(session, 2); - CU_ASSERT(NULL != stream); - - nghttp2_bufs_reset(&bufs); - - rv = pack_headers(&bufs, &deflater, 2, NGHTTP2_FLAG_END_HEADERS, resnv, - ARRLEN(resnv), mem); - - CU_ASSERT(0 == rv); - - rv = nghttp2_session_mem_recv(session, bufs.head->buf.pos, - nghttp2_buf_len(&bufs.head->buf)); - - CU_ASSERT(nghttp2_buf_len(&bufs.head->buf) == rv); - - CU_ASSERT(NULL == nghttp2_session_get_next_ob_item(session)); - - CU_ASSERT(200 == stream->status_code); - - nghttp2_bufs_reset(&bufs); - - /* PUSH_PROMISE lacks mandatory header */ - rv = pack_push_promise(&bufs, &deflater, 1, NGHTTP2_FLAG_END_HEADERS, 4, - bad_reqnv, ARRLEN(bad_reqnv), mem); - - CU_ASSERT(0 == rv); - - rv = nghttp2_session_mem_recv(session, bufs.head->buf.pos, - nghttp2_buf_len(&bufs.head->buf)); - - CU_ASSERT(nghttp2_buf_len(&bufs.head->buf) == rv); - - item = nghttp2_session_get_next_ob_item(session); - - CU_ASSERT(NGHTTP2_RST_STREAM == item->frame.hd.type); - CU_ASSERT(4 == item->frame.hd.stream_id); - - nghttp2_bufs_reset(&bufs); - - nghttp2_hd_deflate_free(&deflater); - nghttp2_session_del(session); - nghttp2_bufs_free(&bufs); -} diff --git a/tests/nghttp2_session_test.h b/tests/nghttp2_session_test.h deleted file mode 100644 index e879c78..0000000 --- a/tests/nghttp2_session_test.h +++ /dev/null @@ -1,138 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2012 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#ifndef NGHTTP2_SESSION_TEST_H -#define NGHTTP2_SESSION_TEST_H - -void test_nghttp2_session_recv(void); -void test_nghttp2_session_recv_invalid_stream_id(void); -void test_nghttp2_session_recv_invalid_frame(void); -void test_nghttp2_session_recv_eof(void); -void test_nghttp2_session_recv_data(void); -void test_nghttp2_session_recv_continuation(void); -void test_nghttp2_session_recv_headers_with_priority(void); -void test_nghttp2_session_recv_premature_headers(void); -void test_nghttp2_session_recv_unknown_frame(void); -void test_nghttp2_session_recv_unexpected_continuation(void); -void test_nghttp2_session_recv_settings_header_table_size(void); -void test_nghttp2_session_recv_too_large_frame_length(void); -void test_nghttp2_session_continue(void); -void test_nghttp2_session_add_frame(void); -void test_nghttp2_session_on_request_headers_received(void); -void test_nghttp2_session_on_response_headers_received(void); -void test_nghttp2_session_on_headers_received(void); -void test_nghttp2_session_on_push_response_headers_received(void); -void test_nghttp2_session_on_priority_received(void); -void test_nghttp2_session_on_rst_stream_received(void); -void test_nghttp2_session_on_settings_received(void); -void test_nghttp2_session_on_push_promise_received(void); -void test_nghttp2_session_on_ping_received(void); -void test_nghttp2_session_on_goaway_received(void); -void test_nghttp2_session_on_window_update_received(void); -void test_nghttp2_session_on_data_received(void); -void test_nghttp2_session_send_headers_start_stream(void); -void test_nghttp2_session_send_headers_reply(void); -void test_nghttp2_session_send_headers_frame_size_error(void); -void test_nghttp2_session_send_headers_push_reply(void); -void test_nghttp2_session_send_rst_stream(void); -void test_nghttp2_session_send_push_promise(void); -void test_nghttp2_session_is_my_stream_id(void); -void test_nghttp2_session_upgrade(void); -void test_nghttp2_session_reprioritize_stream(void); -void test_nghttp2_session_reprioritize_stream_with_idle_stream_dep(void); -void test_nghttp2_submit_data(void); -void test_nghttp2_submit_data_read_length_too_large(void); -void test_nghttp2_submit_data_read_length_smallest(void); -void test_nghttp2_submit_data_twice(void); -void test_nghttp2_submit_request_with_data(void); -void test_nghttp2_submit_request_without_data(void); -void test_nghttp2_submit_response_with_data(void); -void test_nghttp2_submit_response_without_data(void); -void test_nghttp2_submit_trailer(void); -void test_nghttp2_submit_headers_start_stream(void); -void test_nghttp2_submit_headers_reply(void); -void test_nghttp2_submit_headers_push_reply(void); -void test_nghttp2_submit_headers(void); -void test_nghttp2_submit_headers_continuation(void); -void test_nghttp2_submit_priority(void); -void test_nghttp2_submit_settings(void); -void test_nghttp2_submit_settings_update_local_window_size(void); -void test_nghttp2_submit_push_promise(void); -void test_nghttp2_submit_window_update(void); -void test_nghttp2_submit_window_update_local_window_size(void); -void test_nghttp2_submit_shutdown_notice(void); -void test_nghttp2_submit_invalid_nv(void); -void test_nghttp2_session_open_stream(void); -void test_nghttp2_session_open_stream_with_idle_stream_dep(void); -void test_nghttp2_session_get_next_ob_item(void); -void test_nghttp2_session_pop_next_ob_item(void); -void test_nghttp2_session_reply_fail(void); -void test_nghttp2_session_max_concurrent_streams(void); -void test_nghttp2_session_stream_close_on_headers_push(void); -void test_nghttp2_session_stop_data_with_rst_stream(void); -void test_nghttp2_session_defer_data(void); -void test_nghttp2_session_flow_control(void); -void test_nghttp2_session_flow_control_data_recv(void); -void test_nghttp2_session_flow_control_data_with_padding_recv(void); -void test_nghttp2_session_data_read_temporal_failure(void); -void test_nghttp2_session_on_stream_close(void); -void test_nghttp2_session_on_ctrl_not_send(void); -void test_nghttp2_session_get_outbound_queue_size(void); -void test_nghttp2_session_get_effective_local_window_size(void); -void test_nghttp2_session_set_option(void); -void test_nghttp2_session_data_backoff_by_high_pri_frame(void); -void test_nghttp2_session_pack_data_with_padding(void); -void test_nghttp2_session_pack_headers_with_padding(void); -void test_nghttp2_pack_settings_payload(void); -void test_nghttp2_session_stream_dep_add(void); -void test_nghttp2_session_stream_dep_remove(void); -void test_nghttp2_session_stream_dep_add_subtree(void); -void test_nghttp2_session_stream_dep_remove_subtree(void); -void test_nghttp2_session_stream_dep_all_your_stream_are_belong_to_us(void); -void test_nghttp2_session_stream_attach_item(void); -void test_nghttp2_session_stream_attach_item_subtree(void); -void test_nghttp2_session_keep_closed_stream(void); -void test_nghttp2_session_keep_idle_stream(void); -void test_nghttp2_session_detach_idle_stream(void); -void test_nghttp2_session_large_dep_tree(void); -void test_nghttp2_session_graceful_shutdown(void); -void test_nghttp2_session_on_header_temporal_failure(void); -void test_nghttp2_session_recv_client_magic(void); -void test_nghttp2_session_delete_data_item(void); -void test_nghttp2_session_open_idle_stream(void); -void test_nghttp2_session_cancel_reserved_remote(void); -void test_nghttp2_session_reset_pending_headers(void); -void test_nghttp2_session_send_data_callback(void); -void test_nghttp2_session_on_begin_headers_temporal_failure(void); -void test_nghttp2_http_mandatory_headers(void); -void test_nghttp2_http_content_length(void); -void test_nghttp2_http_content_length_mismatch(void); -void test_nghttp2_http_non_final_response(void); -void test_nghttp2_http_trailer_headers(void); -void test_nghttp2_http_ignore_regular_header(void); -void test_nghttp2_http_ignore_content_length(void); -void test_nghttp2_http_record_request_method(void); -void test_nghttp2_http_push_promise(void); - -#endif /* NGHTTP2_SESSION_TEST_H */ diff --git a/tests/nghttp2_stream_test.c b/tests/nghttp2_stream_test.c deleted file mode 100644 index 9b572d0..0000000 --- a/tests/nghttp2_stream_test.c +++ /dev/null @@ -1,29 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2012 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#include "nghttp2_stream_test.h" - -#include - -#include "nghttp2_stream.h" diff --git a/tests/nghttp2_stream_test.h b/tests/nghttp2_stream_test.h deleted file mode 100644 index 508a8e1..0000000 --- a/tests/nghttp2_stream_test.h +++ /dev/null @@ -1,28 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2012 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#ifndef NGHTTP2_STREAM_TEST_H -#define NGHTTP2_STREAM_TEST_H - -#endif /* NGHTTP2_STREAM_TEST_H */ diff --git a/tests/nghttp2_test_helper.c b/tests/nghttp2_test_helper.c deleted file mode 100644 index a9cb10c..0000000 --- a/tests/nghttp2_test_helper.c +++ /dev/null @@ -1,303 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2012 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#include "nghttp2_test_helper.h" - -#include - -#include - -#include "nghttp2_helper.h" -#include "nghttp2_priority_spec.h" - -int unpack_framebuf(nghttp2_frame *frame, nghttp2_bufs *bufs) { - nghttp2_buf *buf; - - /* Assuming we have required data in first buffer. We don't decode - header block so, we don't mind its space */ - buf = &bufs->head->buf; - return unpack_frame(frame, buf->pos, nghttp2_buf_len(buf)); -} - -int unpack_frame(nghttp2_frame *frame, const uint8_t *in, size_t len) { - int rv = 0; - const uint8_t *payload = in + NGHTTP2_FRAME_HDLEN; - size_t payloadlen = len - NGHTTP2_FRAME_HDLEN; - size_t payloadoff; - nghttp2_mem *mem; - - mem = nghttp2_mem_default(); - - nghttp2_frame_unpack_frame_hd(&frame->hd, in); - switch (frame->hd.type) { - case NGHTTP2_HEADERS: - payloadoff = ((frame->hd.flags & NGHTTP2_FLAG_PADDED) > 0); - rv = nghttp2_frame_unpack_headers_payload( - &frame->headers, payload + payloadoff, payloadlen - payloadoff); - break; - case NGHTTP2_PRIORITY: - nghttp2_frame_unpack_priority_payload(&frame->priority, payload, - payloadlen); - break; - case NGHTTP2_RST_STREAM: - nghttp2_frame_unpack_rst_stream_payload(&frame->rst_stream, payload, - payloadlen); - break; - case NGHTTP2_SETTINGS: - rv = nghttp2_frame_unpack_settings_payload2( - &frame->settings.iv, &frame->settings.niv, payload, payloadlen, mem); - break; - case NGHTTP2_PUSH_PROMISE: - rv = nghttp2_frame_unpack_push_promise_payload(&frame->push_promise, - payload, payloadlen); - break; - case NGHTTP2_PING: - nghttp2_frame_unpack_ping_payload(&frame->ping, payload, payloadlen); - break; - case NGHTTP2_GOAWAY: - nghttp2_frame_unpack_goaway_payload2(&frame->goaway, payload, payloadlen, - mem); - break; - case NGHTTP2_WINDOW_UPDATE: - nghttp2_frame_unpack_window_update_payload(&frame->window_update, payload, - payloadlen); - break; - default: - /* Must not be reachable */ - assert(0); - } - return rv; -} - -int strmemeq(const char *a, const uint8_t *b, size_t bn) { - const uint8_t *c; - if (!a || !b) { - return 0; - } - c = b + bn; - for (; *a && b != c && *a == *b; ++a, ++b) - ; - return !*a && b == c; -} - -int nvnameeq(const char *a, nghttp2_nv *nv) { - return strmemeq(a, nv->name, nv->namelen); -} - -int nvvalueeq(const char *a, nghttp2_nv *nv) { - return strmemeq(a, nv->value, nv->valuelen); -} - -void nva_out_init(nva_out *out) { - memset(out->nva, 0, sizeof(out->nva)); - out->nvlen = 0; -} - -void nva_out_reset(nva_out *out, nghttp2_mem *mem) { - size_t i; - for (i = 0; i < out->nvlen; ++i) { - mem->free(out->nva[i].name, NULL); - mem->free(out->nva[i].value, NULL); - } - memset(out->nva, 0, sizeof(out->nva)); - out->nvlen = 0; -} - -void add_out(nva_out *out, nghttp2_nv *nv, nghttp2_mem *mem) { - nghttp2_nv *onv = &out->nva[out->nvlen]; - if (nv->namelen) { - onv->name = mem->malloc(nv->namelen, NULL); - memcpy(onv->name, nv->name, nv->namelen); - } else { - onv->name = NULL; - } - if (nv->valuelen) { - onv->value = mem->malloc(nv->valuelen, NULL); - memcpy(onv->value, nv->value, nv->valuelen); - } else { - onv->value = NULL; - } - onv->namelen = nv->namelen; - onv->valuelen = nv->valuelen; - - onv->flags = nv->flags; - - ++out->nvlen; -} - -ssize_t inflate_hd(nghttp2_hd_inflater *inflater, nva_out *out, - nghttp2_bufs *bufs, size_t offset, nghttp2_mem *mem) { - ssize_t rv; - nghttp2_nv nv; - int inflate_flags; - nghttp2_buf_chain *ci; - nghttp2_buf *buf; - nghttp2_buf bp; - int final; - size_t processed; - - processed = 0; - - for (ci = bufs->head; ci; ci = ci->next) { - buf = &ci->buf; - final = nghttp2_buf_len(buf) == 0 || ci->next == NULL; - bp = *buf; - - if (offset) { - ssize_t n; - - n = nghttp2_min((ssize_t)offset, nghttp2_buf_len(&bp)); - bp.pos += n; - offset -= n; - } - - for (;;) { - inflate_flags = 0; - rv = nghttp2_hd_inflate_hd(inflater, &nv, &inflate_flags, bp.pos, - nghttp2_buf_len(&bp), final); - - if (rv < 0) { - return rv; - } - - bp.pos += rv; - processed += rv; - - if (inflate_flags & NGHTTP2_HD_INFLATE_EMIT) { - if (out) { - add_out(out, &nv, mem); - } - } - if (inflate_flags & NGHTTP2_HD_INFLATE_FINAL) { - break; - } - } - } - - nghttp2_hd_inflate_end_headers(inflater); - - return processed; -} - -int pack_headers(nghttp2_bufs *bufs, nghttp2_hd_deflater *deflater, - int32_t stream_id, int flags, const nghttp2_nv *nva, - size_t nvlen, nghttp2_mem *mem) { - nghttp2_nv *dnva; - nghttp2_frame frame; - int rv; - - nghttp2_nv_array_copy(&dnva, nva, nvlen, mem); - - nghttp2_frame_headers_init(&frame.headers, flags, stream_id, - NGHTTP2_HCAT_HEADERS, NULL, dnva, nvlen); - rv = nghttp2_frame_pack_headers(bufs, &frame.headers, deflater); - - nghttp2_frame_headers_free(&frame.headers, mem); - - return rv; -} - -int pack_push_promise(nghttp2_bufs *bufs, nghttp2_hd_deflater *deflater, - int32_t stream_id, int flags, int32_t promised_stream_id, - const nghttp2_nv *nva, size_t nvlen, nghttp2_mem *mem) { - nghttp2_nv *dnva; - nghttp2_frame frame; - int rv; - - nghttp2_nv_array_copy(&dnva, nva, nvlen, mem); - - nghttp2_frame_push_promise_init(&frame.push_promise, flags, stream_id, - promised_stream_id, dnva, nvlen); - rv = nghttp2_frame_pack_push_promise(bufs, &frame.push_promise, deflater); - - nghttp2_frame_push_promise_free(&frame.push_promise, mem); - - return rv; -} - -int frame_pack_bufs_init(nghttp2_bufs *bufs) { - /* 1 for Pad Length */ - return nghttp2_bufs_init2(bufs, 4096, 16, NGHTTP2_FRAME_HDLEN + 1, - nghttp2_mem_default()); -} - -void bufs_large_init(nghttp2_bufs *bufs, size_t chunk_size) { - /* 1 for Pad Length */ - nghttp2_bufs_init2(bufs, chunk_size, 16, NGHTTP2_FRAME_HDLEN + 1, - nghttp2_mem_default()); -} - -static nghttp2_stream *open_stream_with_all(nghttp2_session *session, - int32_t stream_id, int32_t weight, - uint8_t exclusive, - nghttp2_stream *dep_stream) { - nghttp2_priority_spec pri_spec; - int32_t dep_stream_id; - - if (dep_stream) { - dep_stream_id = dep_stream->stream_id; - } else { - dep_stream_id = 0; - } - - nghttp2_priority_spec_init(&pri_spec, dep_stream_id, weight, exclusive); - - return nghttp2_session_open_stream(session, stream_id, - NGHTTP2_STREAM_FLAG_NONE, &pri_spec, - NGHTTP2_STREAM_OPENED, NULL); -} - -nghttp2_stream *open_stream(nghttp2_session *session, int32_t stream_id) { - return open_stream_with_all(session, stream_id, NGHTTP2_DEFAULT_WEIGHT, 0, - NULL); -} - -nghttp2_stream *open_stream_with_dep(nghttp2_session *session, - int32_t stream_id, - nghttp2_stream *dep_stream) { - return open_stream_with_all(session, stream_id, NGHTTP2_DEFAULT_WEIGHT, 0, - dep_stream); -} - -nghttp2_stream *open_stream_with_dep_weight(nghttp2_session *session, - int32_t stream_id, int32_t weight, - nghttp2_stream *dep_stream) { - return open_stream_with_all(session, stream_id, weight, 0, dep_stream); -} - -nghttp2_stream *open_stream_with_dep_excl(nghttp2_session *session, - int32_t stream_id, - nghttp2_stream *dep_stream) { - return open_stream_with_all(session, stream_id, NGHTTP2_DEFAULT_WEIGHT, 1, - dep_stream); -} - -nghttp2_outbound_item *create_data_ob_item(nghttp2_mem *mem) { - nghttp2_outbound_item *item; - - item = mem->malloc(sizeof(nghttp2_outbound_item), NULL); - memset(item, 0, sizeof(nghttp2_outbound_item)); - - return item; -} diff --git a/tests/nghttp2_test_helper.h b/tests/nghttp2_test_helper.h deleted file mode 100644 index 295c102..0000000 --- a/tests/nghttp2_test_helper.h +++ /dev/null @@ -1,112 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2012 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#ifndef NGHTTP2_TEST_HELPER_H -#define NGHTTP2_TEST_HELPER_H - -#ifdef HAVE_CONFIG_H -#include -#endif /* HAVE_CONFIG_H */ - -#include "nghttp2_frame.h" -#include "nghttp2_hd.h" -#include "nghttp2_session.h" - -#define MAKE_NV(NAME, VALUE) \ - { \ - (uint8_t *)(NAME), (uint8_t *)(VALUE), sizeof((NAME)) - 1, \ - sizeof((VALUE)) - 1, NGHTTP2_NV_FLAG_NONE \ - } -#define ARRLEN(ARR) (sizeof(ARR) / sizeof(ARR[0])) - -#define assert_nv_equal(A, B, len, mem) \ - do { \ - size_t alloclen = sizeof(nghttp2_nv) * len; \ - const nghttp2_nv *sa = A, *sb = B; \ - nghttp2_nv *a = mem->malloc(alloclen, NULL); \ - nghttp2_nv *b = mem->malloc(alloclen, NULL); \ - ssize_t i_; \ - memcpy(a, sa, alloclen); \ - memcpy(b, sb, alloclen); \ - nghttp2_nv_array_sort(a, len); \ - nghttp2_nv_array_sort(b, len); \ - for (i_ = 0; i_ < (ssize_t)len; ++i_) { \ - CU_ASSERT(nghttp2_nv_equal(&a[i_], &b[i_])); \ - } \ - mem->free(b, NULL); \ - mem->free(a, NULL); \ - } while (0); - -int unpack_framebuf(nghttp2_frame *frame, nghttp2_bufs *bufs); - -int unpack_frame(nghttp2_frame *frame, const uint8_t *in, size_t len); - -int strmemeq(const char *a, const uint8_t *b, size_t bn); - -int nvnameeq(const char *a, nghttp2_nv *nv); - -int nvvalueeq(const char *a, nghttp2_nv *nv); - -typedef struct { - nghttp2_nv nva[256]; - size_t nvlen; -} nva_out; - -void nva_out_init(nva_out *out); -void nva_out_reset(nva_out *out, nghttp2_mem *mem); - -void add_out(nva_out *out, nghttp2_nv *nv, nghttp2_mem *mem); - -ssize_t inflate_hd(nghttp2_hd_inflater *inflater, nva_out *out, - nghttp2_bufs *bufs, size_t offset, nghttp2_mem *mem); - -int pack_headers(nghttp2_bufs *bufs, nghttp2_hd_deflater *deflater, - int32_t stream_id, int flags, const nghttp2_nv *nva, - size_t nvlen, nghttp2_mem *mem); - -int pack_push_promise(nghttp2_bufs *bufs, nghttp2_hd_deflater *deflater, - int32_t stream_id, int flags, int32_t promised_stream_id, - const nghttp2_nv *nva, size_t nvlen, nghttp2_mem *mem); - -int frame_pack_bufs_init(nghttp2_bufs *bufs); - -void bufs_large_init(nghttp2_bufs *bufs, size_t chunk_size); - -nghttp2_stream *open_stream(nghttp2_session *session, int32_t stream_id); - -nghttp2_stream *open_stream_with_dep(nghttp2_session *session, - int32_t stream_id, - nghttp2_stream *dep_stream); - -nghttp2_stream *open_stream_with_dep_weight(nghttp2_session *session, - int32_t stream_id, int32_t weight, - nghttp2_stream *dep_stream); - -nghttp2_stream *open_stream_with_dep_excl(nghttp2_session *session, - int32_t stream_id, - nghttp2_stream *dep_stream); - -nghttp2_outbound_item *create_data_ob_item(nghttp2_mem *mem); - -#endif /* NGHTTP2_TEST_HELPER_H */ diff --git a/tests/testdata/Makefile.am b/tests/testdata/Makefile.am deleted file mode 100644 index ee38113..0000000 --- a/tests/testdata/Makefile.am +++ /dev/null @@ -1,23 +0,0 @@ -# nghttp2 - HTTP/2 C Library - -# Copyright (c) 2012 Tatsuhiro Tsujikawa - -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: - -# The above copyright notice and this permission notice shall be -# included in all copies or substantial portions of the Software. - -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -EXTRA_DIST = cacert.pem index.html privkey.pem diff --git a/tests/testdata/Makefile.in b/tests/testdata/Makefile.in deleted file mode 100644 index a40481f..0000000 --- a/tests/testdata/Makefile.in +++ /dev/null @@ -1,512 +0,0 @@ -# Makefile.in generated by automake 1.14.1 from Makefile.am. -# @configure_input@ - -# Copyright (C) 1994-2013 Free Software Foundation, Inc. - -# This Makefile.in is free software; the Free Software Foundation -# gives unlimited permission to copy and/or distribute it, -# with or without modifications, as long as this notice is preserved. - -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY, to the extent permitted by law; without -# even the implied warranty of MERCHANTABILITY or FITNESS FOR A -# PARTICULAR PURPOSE. - -@SET_MAKE@ - -# nghttp2 - HTTP/2 C Library - -# Copyright (c) 2012 Tatsuhiro Tsujikawa - -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: - -# The above copyright notice and this permission notice shall be -# included in all copies or substantial portions of the Software. -VPATH = @srcdir@ -am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)' -am__make_running_with_option = \ - case $${target_option-} in \ - ?) ;; \ - *) echo "am__make_running_with_option: internal error: invalid" \ - "target option '$${target_option-}' specified" >&2; \ - exit 1;; \ - esac; \ - has_opt=no; \ - sane_makeflags=$$MAKEFLAGS; \ - if $(am__is_gnu_make); then \ - sane_makeflags=$$MFLAGS; \ - else \ - case $$MAKEFLAGS in \ - *\\[\ \ ]*) \ - bs=\\; \ - sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ - | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ - esac; \ - fi; \ - skip_next=no; \ - strip_trailopt () \ - { \ - flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ - }; \ - for flg in $$sane_makeflags; do \ - test $$skip_next = yes && { skip_next=no; continue; }; \ - case $$flg in \ - *=*|--*) continue;; \ - -*I) strip_trailopt 'I'; skip_next=yes;; \ - -*I?*) strip_trailopt 'I';; \ - -*O) strip_trailopt 'O'; skip_next=yes;; \ - -*O?*) strip_trailopt 'O';; \ - -*l) strip_trailopt 'l'; skip_next=yes;; \ - -*l?*) strip_trailopt 'l';; \ - -[dEDm]) skip_next=yes;; \ - -[JT]) skip_next=yes;; \ - esac; \ - case $$flg in \ - *$$target_option*) has_opt=yes; break;; \ - esac; \ - done; \ - test $$has_opt = yes -am__make_dryrun = (target_option=n; $(am__make_running_with_option)) -am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) -pkgdatadir = $(datadir)/@PACKAGE@ -pkgincludedir = $(includedir)/@PACKAGE@ -pkglibdir = $(libdir)/@PACKAGE@ -pkglibexecdir = $(libexecdir)/@PACKAGE@ -am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd -install_sh_DATA = $(install_sh) -c -m 644 -install_sh_PROGRAM = $(install_sh) -c -install_sh_SCRIPT = $(install_sh) -c -INSTALL_HEADER = $(INSTALL_DATA) -transform = $(program_transform_name) -NORMAL_INSTALL = : -PRE_INSTALL = : -POST_INSTALL = : -NORMAL_UNINSTALL = : -PRE_UNINSTALL = : -POST_UNINSTALL = : -build_triplet = @build@ -host_triplet = @host@ -target_triplet = @target@ -subdir = tests/testdata -DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am -ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 -am__aclocal_m4_deps = $(top_srcdir)/m4/ax_boost_asio.m4 \ - $(top_srcdir)/m4/ax_boost_base.m4 \ - $(top_srcdir)/m4/ax_boost_system.m4 \ - $(top_srcdir)/m4/ax_boost_thread.m4 \ - $(top_srcdir)/m4/ax_check_compile_flag.m4 \ - $(top_srcdir)/m4/ax_cxx_compile_stdcxx_11.m4 \ - $(top_srcdir)/m4/ax_have_epoll.m4 \ - $(top_srcdir)/m4/ax_python_devel.m4 \ - $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/libxml2.m4 \ - $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ - $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ - $(top_srcdir)/configure.ac -am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ - $(ACLOCAL_M4) -mkinstalldirs = $(install_sh) -d -CONFIG_HEADER = $(top_builddir)/config.h -CONFIG_CLEAN_FILES = -CONFIG_CLEAN_VPATH_FILES = -AM_V_P = $(am__v_P_@AM_V@) -am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) -am__v_P_0 = false -am__v_P_1 = : -AM_V_GEN = $(am__v_GEN_@AM_V@) -am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) -am__v_GEN_0 = @echo " GEN " $@; -am__v_GEN_1 = -AM_V_at = $(am__v_at_@AM_V@) -am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) -am__v_at_0 = @ -am__v_at_1 = -SOURCES = -DIST_SOURCES = -am__can_run_installinfo = \ - case $$AM_UPDATE_INFO_DIR in \ - n|no|NO) false;; \ - *) (install-info --version) >/dev/null 2>&1;; \ - esac -am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) -DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) -ACLOCAL = @ACLOCAL@ -AMTAR = @AMTAR@ -AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ -APPLDFLAGS = @APPLDFLAGS@ -AR = @AR@ -AUTOCONF = @AUTOCONF@ -AUTOHEADER = @AUTOHEADER@ -AUTOMAKE = @AUTOMAKE@ -AWK = @AWK@ -BOOST_ASIO_LIB = @BOOST_ASIO_LIB@ -BOOST_CPPFLAGS = @BOOST_CPPFLAGS@ -BOOST_LDFLAGS = @BOOST_LDFLAGS@ -BOOST_SYSTEM_LIB = @BOOST_SYSTEM_LIB@ -BOOST_THREAD_LIB = @BOOST_THREAD_LIB@ -CC = @CC@ -CCDEPMODE = @CCDEPMODE@ -CFLAGS = @CFLAGS@ -CPP = @CPP@ -CPPFLAGS = @CPPFLAGS@ -CUNIT_CFLAGS = @CUNIT_CFLAGS@ -CUNIT_LIBS = @CUNIT_LIBS@ -CXX = @CXX@ -CXXCPP = @CXXCPP@ -CXXDEPMODE = @CXXDEPMODE@ -CXXFLAGS = @CXXFLAGS@ -CYGPATH_W = @CYGPATH_W@ -CYTHON = @CYTHON@ -DEFS = @DEFS@ -DEPDIR = @DEPDIR@ -DLLTOOL = @DLLTOOL@ -DSYMUTIL = @DSYMUTIL@ -DUMPBIN = @DUMPBIN@ -ECHO_C = @ECHO_C@ -ECHO_N = @ECHO_N@ -ECHO_T = @ECHO_T@ -EGREP = @EGREP@ -EXEEXT = @EXEEXT@ -FGREP = @FGREP@ -GREP = @GREP@ -HAVE_CXX11 = @HAVE_CXX11@ -INSTALL = @INSTALL@ -INSTALL_DATA = @INSTALL_DATA@ -INSTALL_PROGRAM = @INSTALL_PROGRAM@ -INSTALL_SCRIPT = @INSTALL_SCRIPT@ -INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ -JANSSON_CFLAGS = @JANSSON_CFLAGS@ -JANSSON_LIBS = @JANSSON_LIBS@ -JEMALLOC_LIBS = @JEMALLOC_LIBS@ -LD = @LD@ -LDFLAGS = @LDFLAGS@ -LIBEVENT_OPENSSL_CFLAGS = @LIBEVENT_OPENSSL_CFLAGS@ -LIBEVENT_OPENSSL_LIBS = @LIBEVENT_OPENSSL_LIBS@ -LIBEV_CFLAGS = @LIBEV_CFLAGS@ -LIBEV_LIBS = @LIBEV_LIBS@ -LIBOBJS = @LIBOBJS@ -LIBS = @LIBS@ -LIBSPDYLAY_CFLAGS = @LIBSPDYLAY_CFLAGS@ -LIBSPDYLAY_LIBS = @LIBSPDYLAY_LIBS@ -LIBTOOL = @LIBTOOL@ -LIPO = @LIPO@ -LN_S = @LN_S@ -LTLIBOBJS = @LTLIBOBJS@ -LT_AGE = @LT_AGE@ -LT_CURRENT = @LT_CURRENT@ -LT_REVISION = @LT_REVISION@ -MAKEINFO = @MAKEINFO@ -MANIFEST_TOOL = @MANIFEST_TOOL@ -MKDIR_P = @MKDIR_P@ -NM = @NM@ -NMEDIT = @NMEDIT@ -OBJDUMP = @OBJDUMP@ -OBJEXT = @OBJEXT@ -OPENSSL_CFLAGS = @OPENSSL_CFLAGS@ -OPENSSL_LIBS = @OPENSSL_LIBS@ -OTOOL = @OTOOL@ -OTOOL64 = @OTOOL64@ -PACKAGE = @PACKAGE@ -PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ -PACKAGE_NAME = @PACKAGE_NAME@ -PACKAGE_STRING = @PACKAGE_STRING@ -PACKAGE_TARNAME = @PACKAGE_TARNAME@ -PACKAGE_URL = @PACKAGE_URL@ -PACKAGE_VERSION = @PACKAGE_VERSION@ -PACKAGE_VERSION_NUM = @PACKAGE_VERSION_NUM@ -PATH_SEPARATOR = @PATH_SEPARATOR@ -PKG_CONFIG = @PKG_CONFIG@ -PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ -PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ -PYTHON = @PYTHON@ -PYTHON_CPPFLAGS = @PYTHON_CPPFLAGS@ -PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@ -PYTHON_EXTRA_LDFLAGS = @PYTHON_EXTRA_LDFLAGS@ -PYTHON_EXTRA_LIBS = @PYTHON_EXTRA_LIBS@ -PYTHON_LDFLAGS = @PYTHON_LDFLAGS@ -PYTHON_PLATFORM = @PYTHON_PLATFORM@ -PYTHON_PREFIX = @PYTHON_PREFIX@ -PYTHON_SITE_PKG = @PYTHON_SITE_PKG@ -PYTHON_VERSION = @PYTHON_VERSION@ -RANLIB = @RANLIB@ -SED = @SED@ -SET_MAKE = @SET_MAKE@ -SHELL = @SHELL@ -STRIP = @STRIP@ -TESTLDADD = @TESTLDADD@ -VERSION = @VERSION@ -WARNCFLAGS = @WARNCFLAGS@ -XML2_CONFIG = @XML2_CONFIG@ -XML_CPPFLAGS = @XML_CPPFLAGS@ -XML_LIBS = @XML_LIBS@ -ZLIB_CFLAGS = @ZLIB_CFLAGS@ -ZLIB_LIBS = @ZLIB_LIBS@ -abs_builddir = @abs_builddir@ -abs_srcdir = @abs_srcdir@ -abs_top_builddir = @abs_top_builddir@ -abs_top_srcdir = @abs_top_srcdir@ -ac_ct_AR = @ac_ct_AR@ -ac_ct_CC = @ac_ct_CC@ -ac_ct_CXX = @ac_ct_CXX@ -ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ -am__include = @am__include@ -am__leading_dot = @am__leading_dot@ -am__quote = @am__quote@ -am__tar = @am__tar@ -am__untar = @am__untar@ -bindir = @bindir@ -build = @build@ -build_alias = @build_alias@ -build_cpu = @build_cpu@ -build_os = @build_os@ -build_vendor = @build_vendor@ -builddir = @builddir@ -datadir = @datadir@ -datarootdir = @datarootdir@ -docdir = @docdir@ -dvidir = @dvidir@ -exec_prefix = @exec_prefix@ -host = @host@ -host_alias = @host_alias@ -host_cpu = @host_cpu@ -host_os = @host_os@ -host_vendor = @host_vendor@ -htmldir = @htmldir@ -includedir = @includedir@ -infodir = @infodir@ -install_sh = @install_sh@ -libdir = @libdir@ -libexecdir = @libexecdir@ -localedir = @localedir@ -localstatedir = @localstatedir@ -mandir = @mandir@ -mkdir_p = @mkdir_p@ -oldincludedir = @oldincludedir@ -pdfdir = @pdfdir@ -pkgpyexecdir = @pkgpyexecdir@ -pkgpythondir = @pkgpythondir@ -prefix = @prefix@ -program_transform_name = @program_transform_name@ -psdir = @psdir@ -pyexecdir = @pyexecdir@ -pythondir = @pythondir@ -sbindir = @sbindir@ -sharedstatedir = @sharedstatedir@ -srcdir = @srcdir@ -sysconfdir = @sysconfdir@ -target = @target@ -target_alias = @target_alias@ -target_cpu = @target_cpu@ -target_os = @target_os@ -target_vendor = @target_vendor@ -top_build_prefix = @top_build_prefix@ -top_builddir = @top_builddir@ -top_srcdir = @top_srcdir@ - -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -EXTRA_DIST = cacert.pem index.html privkey.pem -all: all-am - -.SUFFIXES: -$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) - @for dep in $?; do \ - case '$(am__configure_deps)' in \ - *$$dep*) \ - ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ - && { if test -f $@; then exit 0; else break; fi; }; \ - exit 1;; \ - esac; \ - done; \ - echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu tests/testdata/Makefile'; \ - $(am__cd) $(top_srcdir) && \ - $(AUTOMAKE) --gnu tests/testdata/Makefile -.PRECIOUS: Makefile -Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status - @case '$?' in \ - *config.status*) \ - cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ - *) \ - echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ - cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ - esac; - -$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) - cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh - -$(top_srcdir)/configure: $(am__configure_deps) - cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh -$(ACLOCAL_M4): $(am__aclocal_m4_deps) - cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh -$(am__aclocal_m4_deps): - -mostlyclean-libtool: - -rm -f *.lo - -clean-libtool: - -rm -rf .libs _libs -tags TAGS: - -ctags CTAGS: - -cscope cscopelist: - - -distdir: $(DISTFILES) - @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ - topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ - list='$(DISTFILES)'; \ - dist_files=`for file in $$list; do echo $$file; done | \ - sed -e "s|^$$srcdirstrip/||;t" \ - -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ - case $$dist_files in \ - */*) $(MKDIR_P) `echo "$$dist_files" | \ - sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ - sort -u` ;; \ - esac; \ - for file in $$dist_files; do \ - if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ - if test -d $$d/$$file; then \ - dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ - if test -d "$(distdir)/$$file"; then \ - find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ - fi; \ - if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ - cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ - find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ - fi; \ - cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ - else \ - test -f "$(distdir)/$$file" \ - || cp -p $$d/$$file "$(distdir)/$$file" \ - || exit 1; \ - fi; \ - done -check-am: all-am -check: check-am -all-am: Makefile -installdirs: -install: install-am -install-exec: install-exec-am -install-data: install-data-am -uninstall: uninstall-am - -install-am: all-am - @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am - -installcheck: installcheck-am -install-strip: - if test -z '$(STRIP)'; then \ - $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ - install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ - install; \ - else \ - $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ - install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ - "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ - fi -mostlyclean-generic: - -clean-generic: - -distclean-generic: - -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) - -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) - -maintainer-clean-generic: - @echo "This command is intended for maintainers to use" - @echo "it deletes files that may require special tools to rebuild." -clean: clean-am - -clean-am: clean-generic clean-libtool mostlyclean-am - -distclean: distclean-am - -rm -f Makefile -distclean-am: clean-am distclean-generic - -dvi: dvi-am - -dvi-am: - -html: html-am - -html-am: - -info: info-am - -info-am: - -install-data-am: - -install-dvi: install-dvi-am - -install-dvi-am: - -install-exec-am: - -install-html: install-html-am - -install-html-am: - -install-info: install-info-am - -install-info-am: - -install-man: - -install-pdf: install-pdf-am - -install-pdf-am: - -install-ps: install-ps-am - -install-ps-am: - -installcheck-am: - -maintainer-clean: maintainer-clean-am - -rm -f Makefile -maintainer-clean-am: distclean-am maintainer-clean-generic - -mostlyclean: mostlyclean-am - -mostlyclean-am: mostlyclean-generic mostlyclean-libtool - -pdf: pdf-am - -pdf-am: - -ps: ps-am - -ps-am: - -uninstall-am: - -.MAKE: install-am install-strip - -.PHONY: all all-am check check-am clean clean-generic clean-libtool \ - cscopelist-am ctags-am distclean distclean-generic \ - distclean-libtool distdir dvi dvi-am html html-am info info-am \ - install install-am install-data install-data-am install-dvi \ - install-dvi-am install-exec install-exec-am install-html \ - install-html-am install-info install-info-am install-man \ - install-pdf install-pdf-am install-ps install-ps-am \ - install-strip installcheck installcheck-am installdirs \ - maintainer-clean maintainer-clean-generic mostlyclean \ - mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ - tags-am uninstall uninstall-am - - -# Tell versions [3.59,3.63) of GNU make to not export all variables. -# Otherwise a system limit (for SysV at least) may be exceeded. -.NOEXPORT: diff --git a/tests/testdata/cacert.pem b/tests/testdata/cacert.pem deleted file mode 100644 index e462790..0000000 --- a/tests/testdata/cacert.pem +++ /dev/null @@ -1,14 +0,0 @@ ------BEGIN CERTIFICATE----- -MIICKTCCAdOgAwIBAgIJAIsolheWrwMZMA0GCSqGSIb3DQEBBQUAMHAxCzAJBgNV -BAYTAlVTMQswCQYDVQQIDAJDQTENMAsGA1UEBwwEQ2l0eTESMBAGA1UECgwJU3Bk -eSBUZXN0MRIwEAYDVQQDDAlsb2NhbGhvc3QxHTAbBgkqhkiG9w0BCQEWDnNwZHlA -bG9jYWxob3N0MB4XDTEyMDMwMTE5MTI0NVoXDTIzMDUxOTE5MTI0NVowcDELMAkG -A1UEBhMCVVMxCzAJBgNVBAgMAkNBMQ0wCwYDVQQHDARDaXR5MRIwEAYDVQQKDAlT -cGR5IFRlc3QxEjAQBgNVBAMMCWxvY2FsaG9zdDEdMBsGCSqGSIb3DQEJARYOc3Bk -eUBsb2NhbGhvc3QwXDANBgkqhkiG9w0BAQEFAANLADBIAkEAw/2MgzAdlJDm29qH -ZlAibgs9mH+8keOtsRrb4B1PiCcZoHvN9eCVZ4WnzT+0zhHF+nO3YfwVFVC3w7TF -7fLB3QIDAQABo1AwTjAdBgNVHQ4EFgQUVP2Jw9RX6BB76aV5x2qk5qsrAIQwHwYD -VR0jBBgwFoAUVP2Jw9RX6BB76aV5x2qk5qsrAIQwDAYDVR0TBAUwAwEB/zANBgkq -hkiG9w0BAQUFAANBAKd9M5FzQLEZW1KPe9/XNZlgxZ2g3EC5Krxo5I4Ul3MnIYS9 -u4K8t/iprhgOzjFH6+8LVk9v0Za+gU+K43CpUo4= ------END CERTIFICATE----- diff --git a/tests/testdata/index.html b/tests/testdata/index.html deleted file mode 100644 index cdd75fc..0000000 --- a/tests/testdata/index.html +++ /dev/null @@ -1 +0,0 @@ -small diff --git a/tests/testdata/privkey.pem b/tests/testdata/privkey.pem deleted file mode 100644 index 0ab825b..0000000 --- a/tests/testdata/privkey.pem +++ /dev/null @@ -1,9 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -MIIBOwIBAAJBAMP9jIMwHZSQ5tvah2ZQIm4LPZh/vJHjrbEa2+AdT4gnGaB7zfXg -lWeFp80/tM4Rxfpzt2H8FRVQt8O0xe3ywd0CAwEAAQJBAIQ8PGP/QNYOdlT8OsLj -aneJCgQsm1Rro7ONBbFO1WxslvA6+uJsx4Rs8zLiS8cyqmJ/lmGa7zhwYSOvFQPa -XgECIQDgIcgM/2C67peTm1diKKIoGVVKFCfdRi+Dje6mTl2TQQIhAN/bcFWbG73j -cUVlIsr9Wk1dJzjPPWKeyirF1qd/WbOdAiEApTsCOeLCssxV3jF02B5QfPNAFx6I -zO2C9Z7awque/IECIGCHW3VOoTPMs7dc2Rf3D810cclJdArmtf6juOAZRjDxAiBS -AC+H685IBJ99N5nCbF9NWYIVSkuiKVQ8POYVZX+0Jg== ------END RSA PRIVATE KEY----- diff --git a/third-party/Makefile.am b/third-party/Makefile.am deleted file mode 100644 index aa8832d..0000000 --- a/third-party/Makefile.am +++ /dev/null @@ -1,32 +0,0 @@ -# nghttp2 - HTTP/2 C Library - -# Copyright (c) 2014 Tatsuhiro Tsujikawa - -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: - -# The above copyright notice and this permission notice shall be -# included in all copies or substantial portions of the Software. - -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -AM_CPPFLAGS = -Wall @DEFS@ - -noinst_LTLIBRARIES = libhttp-parser.la -libhttp_parser_la_SOURCES = \ - http-parser/http_parser.c \ - http-parser/http_parser.h - - -dist_pkgdata_SCRIPTS = h2o/fetch-ocsp-response diff --git a/third-party/Makefile.in b/third-party/Makefile.in deleted file mode 100644 index 352a959..0000000 --- a/third-party/Makefile.in +++ /dev/null @@ -1,751 +0,0 @@ -# Makefile.in generated by automake 1.14.1 from Makefile.am. -# @configure_input@ - -# Copyright (C) 1994-2013 Free Software Foundation, Inc. - -# This Makefile.in is free software; the Free Software Foundation -# gives unlimited permission to copy and/or distribute it, -# with or without modifications, as long as this notice is preserved. - -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY, to the extent permitted by law; without -# even the implied warranty of MERCHANTABILITY or FITNESS FOR A -# PARTICULAR PURPOSE. - -@SET_MAKE@ - -# nghttp2 - HTTP/2 C Library - -# Copyright (c) 2014 Tatsuhiro Tsujikawa - -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: - -# The above copyright notice and this permission notice shall be -# included in all copies or substantial portions of the Software. - -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - - -VPATH = @srcdir@ -am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)' -am__make_running_with_option = \ - case $${target_option-} in \ - ?) ;; \ - *) echo "am__make_running_with_option: internal error: invalid" \ - "target option '$${target_option-}' specified" >&2; \ - exit 1;; \ - esac; \ - has_opt=no; \ - sane_makeflags=$$MAKEFLAGS; \ - if $(am__is_gnu_make); then \ - sane_makeflags=$$MFLAGS; \ - else \ - case $$MAKEFLAGS in \ - *\\[\ \ ]*) \ - bs=\\; \ - sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ - | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ - esac; \ - fi; \ - skip_next=no; \ - strip_trailopt () \ - { \ - flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ - }; \ - for flg in $$sane_makeflags; do \ - test $$skip_next = yes && { skip_next=no; continue; }; \ - case $$flg in \ - *=*|--*) continue;; \ - -*I) strip_trailopt 'I'; skip_next=yes;; \ - -*I?*) strip_trailopt 'I';; \ - -*O) strip_trailopt 'O'; skip_next=yes;; \ - -*O?*) strip_trailopt 'O';; \ - -*l) strip_trailopt 'l'; skip_next=yes;; \ - -*l?*) strip_trailopt 'l';; \ - -[dEDm]) skip_next=yes;; \ - -[JT]) skip_next=yes;; \ - esac; \ - case $$flg in \ - *$$target_option*) has_opt=yes; break;; \ - esac; \ - done; \ - test $$has_opt = yes -am__make_dryrun = (target_option=n; $(am__make_running_with_option)) -am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) -pkgdatadir = $(datadir)/@PACKAGE@ -pkgincludedir = $(includedir)/@PACKAGE@ -pkglibdir = $(libdir)/@PACKAGE@ -pkglibexecdir = $(libexecdir)/@PACKAGE@ -am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd -install_sh_DATA = $(install_sh) -c -m 644 -install_sh_PROGRAM = $(install_sh) -c -install_sh_SCRIPT = $(install_sh) -c -INSTALL_HEADER = $(INSTALL_DATA) -transform = $(program_transform_name) -NORMAL_INSTALL = : -PRE_INSTALL = : -POST_INSTALL = : -NORMAL_UNINSTALL = : -PRE_UNINSTALL = : -POST_UNINSTALL = : -build_triplet = @build@ -host_triplet = @host@ -target_triplet = @target@ -subdir = third-party -DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \ - $(dist_pkgdata_SCRIPTS) $(top_srcdir)/depcomp -ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 -am__aclocal_m4_deps = $(top_srcdir)/m4/ax_boost_asio.m4 \ - $(top_srcdir)/m4/ax_boost_base.m4 \ - $(top_srcdir)/m4/ax_boost_system.m4 \ - $(top_srcdir)/m4/ax_boost_thread.m4 \ - $(top_srcdir)/m4/ax_check_compile_flag.m4 \ - $(top_srcdir)/m4/ax_cxx_compile_stdcxx_11.m4 \ - $(top_srcdir)/m4/ax_have_epoll.m4 \ - $(top_srcdir)/m4/ax_python_devel.m4 \ - $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/libxml2.m4 \ - $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ - $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ - $(top_srcdir)/configure.ac -am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ - $(ACLOCAL_M4) -mkinstalldirs = $(install_sh) -d -CONFIG_HEADER = $(top_builddir)/config.h -CONFIG_CLEAN_FILES = -CONFIG_CLEAN_VPATH_FILES = -LTLIBRARIES = $(noinst_LTLIBRARIES) -libhttp_parser_la_LIBADD = -am__dirstamp = $(am__leading_dot)dirstamp -am_libhttp_parser_la_OBJECTS = http-parser/http_parser.lo -libhttp_parser_la_OBJECTS = $(am_libhttp_parser_la_OBJECTS) -AM_V_lt = $(am__v_lt_@AM_V@) -am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) -am__v_lt_0 = --silent -am__v_lt_1 = -am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; -am__vpath_adj = case $$p in \ - $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ - *) f=$$p;; \ - esac; -am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; -am__install_max = 40 -am__nobase_strip_setup = \ - srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` -am__nobase_strip = \ - for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" -am__nobase_list = $(am__nobase_strip_setup); \ - for p in $$list; do echo "$$p $$p"; done | \ - sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ - $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ - if (++n[$$2] == $(am__install_max)) \ - { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ - END { for (dir in files) print dir, files[dir] }' -am__base_list = \ - sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ - sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' -am__uninstall_files_from_dir = { \ - test -z "$$files" \ - || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ - || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ - $(am__cd) "$$dir" && rm -f $$files; }; \ - } -am__installdirs = "$(DESTDIR)$(pkgdatadir)" -SCRIPTS = $(dist_pkgdata_SCRIPTS) -AM_V_P = $(am__v_P_@AM_V@) -am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) -am__v_P_0 = false -am__v_P_1 = : -AM_V_GEN = $(am__v_GEN_@AM_V@) -am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) -am__v_GEN_0 = @echo " GEN " $@; -am__v_GEN_1 = -AM_V_at = $(am__v_at_@AM_V@) -am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) -am__v_at_0 = @ -am__v_at_1 = -DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) -depcomp = $(SHELL) $(top_srcdir)/depcomp -am__depfiles_maybe = depfiles -am__mv = mv -f -COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ - $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ - $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ - $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ - $(AM_CFLAGS) $(CFLAGS) -AM_V_CC = $(am__v_CC_@AM_V@) -am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) -am__v_CC_0 = @echo " CC " $@; -am__v_CC_1 = -CCLD = $(CC) -LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ - $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ - $(AM_LDFLAGS) $(LDFLAGS) -o $@ -AM_V_CCLD = $(am__v_CCLD_@AM_V@) -am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) -am__v_CCLD_0 = @echo " CCLD " $@; -am__v_CCLD_1 = -SOURCES = $(libhttp_parser_la_SOURCES) -DIST_SOURCES = $(libhttp_parser_la_SOURCES) -am__can_run_installinfo = \ - case $$AM_UPDATE_INFO_DIR in \ - n|no|NO) false;; \ - *) (install-info --version) >/dev/null 2>&1;; \ - esac -am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) -# Read a list of newline-separated strings from the standard input, -# and print each of them once, without duplicates. Input order is -# *not* preserved. -am__uniquify_input = $(AWK) '\ - BEGIN { nonempty = 0; } \ - { items[$$0] = 1; nonempty = 1; } \ - END { if (nonempty) { for (i in items) print i; }; } \ -' -# Make sure the list of sources is unique. This is necessary because, -# e.g., the same source file might be shared among _SOURCES variables -# for different programs/libraries. -am__define_uniq_tagged_files = \ - list='$(am__tagged_files)'; \ - unique=`for i in $$list; do \ - if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ - done | $(am__uniquify_input)` -ETAGS = etags -CTAGS = ctags -DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) -ACLOCAL = @ACLOCAL@ -AMTAR = @AMTAR@ -AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ -APPLDFLAGS = @APPLDFLAGS@ -AR = @AR@ -AUTOCONF = @AUTOCONF@ -AUTOHEADER = @AUTOHEADER@ -AUTOMAKE = @AUTOMAKE@ -AWK = @AWK@ -BOOST_ASIO_LIB = @BOOST_ASIO_LIB@ -BOOST_CPPFLAGS = @BOOST_CPPFLAGS@ -BOOST_LDFLAGS = @BOOST_LDFLAGS@ -BOOST_SYSTEM_LIB = @BOOST_SYSTEM_LIB@ -BOOST_THREAD_LIB = @BOOST_THREAD_LIB@ -CC = @CC@ -CCDEPMODE = @CCDEPMODE@ -CFLAGS = @CFLAGS@ -CPP = @CPP@ -CPPFLAGS = @CPPFLAGS@ -CUNIT_CFLAGS = @CUNIT_CFLAGS@ -CUNIT_LIBS = @CUNIT_LIBS@ -CXX = @CXX@ -CXXCPP = @CXXCPP@ -CXXDEPMODE = @CXXDEPMODE@ -CXXFLAGS = @CXXFLAGS@ -CYGPATH_W = @CYGPATH_W@ -CYTHON = @CYTHON@ -DEFS = @DEFS@ -DEPDIR = @DEPDIR@ -DLLTOOL = @DLLTOOL@ -DSYMUTIL = @DSYMUTIL@ -DUMPBIN = @DUMPBIN@ -ECHO_C = @ECHO_C@ -ECHO_N = @ECHO_N@ -ECHO_T = @ECHO_T@ -EGREP = @EGREP@ -EXEEXT = @EXEEXT@ -FGREP = @FGREP@ -GREP = @GREP@ -HAVE_CXX11 = @HAVE_CXX11@ -INSTALL = @INSTALL@ -INSTALL_DATA = @INSTALL_DATA@ -INSTALL_PROGRAM = @INSTALL_PROGRAM@ -INSTALL_SCRIPT = @INSTALL_SCRIPT@ -INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ -JANSSON_CFLAGS = @JANSSON_CFLAGS@ -JANSSON_LIBS = @JANSSON_LIBS@ -JEMALLOC_LIBS = @JEMALLOC_LIBS@ -LD = @LD@ -LDFLAGS = @LDFLAGS@ -LIBEVENT_OPENSSL_CFLAGS = @LIBEVENT_OPENSSL_CFLAGS@ -LIBEVENT_OPENSSL_LIBS = @LIBEVENT_OPENSSL_LIBS@ -LIBEV_CFLAGS = @LIBEV_CFLAGS@ -LIBEV_LIBS = @LIBEV_LIBS@ -LIBOBJS = @LIBOBJS@ -LIBS = @LIBS@ -LIBSPDYLAY_CFLAGS = @LIBSPDYLAY_CFLAGS@ -LIBSPDYLAY_LIBS = @LIBSPDYLAY_LIBS@ -LIBTOOL = @LIBTOOL@ -LIPO = @LIPO@ -LN_S = @LN_S@ -LTLIBOBJS = @LTLIBOBJS@ -LT_AGE = @LT_AGE@ -LT_CURRENT = @LT_CURRENT@ -LT_REVISION = @LT_REVISION@ -MAKEINFO = @MAKEINFO@ -MANIFEST_TOOL = @MANIFEST_TOOL@ -MKDIR_P = @MKDIR_P@ -NM = @NM@ -NMEDIT = @NMEDIT@ -OBJDUMP = @OBJDUMP@ -OBJEXT = @OBJEXT@ -OPENSSL_CFLAGS = @OPENSSL_CFLAGS@ -OPENSSL_LIBS = @OPENSSL_LIBS@ -OTOOL = @OTOOL@ -OTOOL64 = @OTOOL64@ -PACKAGE = @PACKAGE@ -PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ -PACKAGE_NAME = @PACKAGE_NAME@ -PACKAGE_STRING = @PACKAGE_STRING@ -PACKAGE_TARNAME = @PACKAGE_TARNAME@ -PACKAGE_URL = @PACKAGE_URL@ -PACKAGE_VERSION = @PACKAGE_VERSION@ -PACKAGE_VERSION_NUM = @PACKAGE_VERSION_NUM@ -PATH_SEPARATOR = @PATH_SEPARATOR@ -PKG_CONFIG = @PKG_CONFIG@ -PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ -PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ -PYTHON = @PYTHON@ -PYTHON_CPPFLAGS = @PYTHON_CPPFLAGS@ -PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@ -PYTHON_EXTRA_LDFLAGS = @PYTHON_EXTRA_LDFLAGS@ -PYTHON_EXTRA_LIBS = @PYTHON_EXTRA_LIBS@ -PYTHON_LDFLAGS = @PYTHON_LDFLAGS@ -PYTHON_PLATFORM = @PYTHON_PLATFORM@ -PYTHON_PREFIX = @PYTHON_PREFIX@ -PYTHON_SITE_PKG = @PYTHON_SITE_PKG@ -PYTHON_VERSION = @PYTHON_VERSION@ -RANLIB = @RANLIB@ -SED = @SED@ -SET_MAKE = @SET_MAKE@ -SHELL = @SHELL@ -STRIP = @STRIP@ -TESTLDADD = @TESTLDADD@ -VERSION = @VERSION@ -WARNCFLAGS = @WARNCFLAGS@ -XML2_CONFIG = @XML2_CONFIG@ -XML_CPPFLAGS = @XML_CPPFLAGS@ -XML_LIBS = @XML_LIBS@ -ZLIB_CFLAGS = @ZLIB_CFLAGS@ -ZLIB_LIBS = @ZLIB_LIBS@ -abs_builddir = @abs_builddir@ -abs_srcdir = @abs_srcdir@ -abs_top_builddir = @abs_top_builddir@ -abs_top_srcdir = @abs_top_srcdir@ -ac_ct_AR = @ac_ct_AR@ -ac_ct_CC = @ac_ct_CC@ -ac_ct_CXX = @ac_ct_CXX@ -ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ -am__include = @am__include@ -am__leading_dot = @am__leading_dot@ -am__quote = @am__quote@ -am__tar = @am__tar@ -am__untar = @am__untar@ -bindir = @bindir@ -build = @build@ -build_alias = @build_alias@ -build_cpu = @build_cpu@ -build_os = @build_os@ -build_vendor = @build_vendor@ -builddir = @builddir@ -datadir = @datadir@ -datarootdir = @datarootdir@ -docdir = @docdir@ -dvidir = @dvidir@ -exec_prefix = @exec_prefix@ -host = @host@ -host_alias = @host_alias@ -host_cpu = @host_cpu@ -host_os = @host_os@ -host_vendor = @host_vendor@ -htmldir = @htmldir@ -includedir = @includedir@ -infodir = @infodir@ -install_sh = @install_sh@ -libdir = @libdir@ -libexecdir = @libexecdir@ -localedir = @localedir@ -localstatedir = @localstatedir@ -mandir = @mandir@ -mkdir_p = @mkdir_p@ -oldincludedir = @oldincludedir@ -pdfdir = @pdfdir@ -pkgpyexecdir = @pkgpyexecdir@ -pkgpythondir = @pkgpythondir@ -prefix = @prefix@ -program_transform_name = @program_transform_name@ -psdir = @psdir@ -pyexecdir = @pyexecdir@ -pythondir = @pythondir@ -sbindir = @sbindir@ -sharedstatedir = @sharedstatedir@ -srcdir = @srcdir@ -sysconfdir = @sysconfdir@ -target = @target@ -target_alias = @target_alias@ -target_cpu = @target_cpu@ -target_os = @target_os@ -target_vendor = @target_vendor@ -top_build_prefix = @top_build_prefix@ -top_builddir = @top_builddir@ -top_srcdir = @top_srcdir@ -AM_CPPFLAGS = -Wall @DEFS@ -noinst_LTLIBRARIES = libhttp-parser.la -libhttp_parser_la_SOURCES = \ - http-parser/http_parser.c \ - http-parser/http_parser.h - -dist_pkgdata_SCRIPTS = h2o/fetch-ocsp-response -all: all-am - -.SUFFIXES: -.SUFFIXES: .c .lo .o .obj -$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) - @for dep in $?; do \ - case '$(am__configure_deps)' in \ - *$$dep*) \ - ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ - && { if test -f $@; then exit 0; else break; fi; }; \ - exit 1;; \ - esac; \ - done; \ - echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu third-party/Makefile'; \ - $(am__cd) $(top_srcdir) && \ - $(AUTOMAKE) --gnu third-party/Makefile -.PRECIOUS: Makefile -Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status - @case '$?' in \ - *config.status*) \ - cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ - *) \ - echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ - cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ - esac; - -$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) - cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh - -$(top_srcdir)/configure: $(am__configure_deps) - cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh -$(ACLOCAL_M4): $(am__aclocal_m4_deps) - cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh -$(am__aclocal_m4_deps): - -clean-noinstLTLIBRARIES: - -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES) - @list='$(noinst_LTLIBRARIES)'; \ - locs=`for p in $$list; do echo $$p; done | \ - sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ - sort -u`; \ - test -z "$$locs" || { \ - echo rm -f $${locs}; \ - rm -f $${locs}; \ - } -http-parser/$(am__dirstamp): - @$(MKDIR_P) http-parser - @: > http-parser/$(am__dirstamp) -http-parser/$(DEPDIR)/$(am__dirstamp): - @$(MKDIR_P) http-parser/$(DEPDIR) - @: > http-parser/$(DEPDIR)/$(am__dirstamp) -http-parser/http_parser.lo: http-parser/$(am__dirstamp) \ - http-parser/$(DEPDIR)/$(am__dirstamp) - -libhttp-parser.la: $(libhttp_parser_la_OBJECTS) $(libhttp_parser_la_DEPENDENCIES) $(EXTRA_libhttp_parser_la_DEPENDENCIES) - $(AM_V_CCLD)$(LINK) $(libhttp_parser_la_OBJECTS) $(libhttp_parser_la_LIBADD) $(LIBS) -install-dist_pkgdataSCRIPTS: $(dist_pkgdata_SCRIPTS) - @$(NORMAL_INSTALL) - @list='$(dist_pkgdata_SCRIPTS)'; test -n "$(pkgdatadir)" || list=; \ - if test -n "$$list"; then \ - echo " $(MKDIR_P) '$(DESTDIR)$(pkgdatadir)'"; \ - $(MKDIR_P) "$(DESTDIR)$(pkgdatadir)" || exit 1; \ - fi; \ - for p in $$list; do \ - if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ - if test -f "$$d$$p"; then echo "$$d$$p"; echo "$$p"; else :; fi; \ - done | \ - sed -e 'p;s,.*/,,;n' \ - -e 'h;s|.*|.|' \ - -e 'p;x;s,.*/,,;$(transform)' | sed 'N;N;N;s,\n, ,g' | \ - $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1; } \ - { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ - if ($$2 == $$4) { files[d] = files[d] " " $$1; \ - if (++n[d] == $(am__install_max)) { \ - print "f", d, files[d]; n[d] = 0; files[d] = "" } } \ - else { print "f", d "/" $$4, $$1 } } \ - END { for (d in files) print "f", d, files[d] }' | \ - while read type dir files; do \ - if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ - test -z "$$files" || { \ - echo " $(INSTALL_SCRIPT) $$files '$(DESTDIR)$(pkgdatadir)$$dir'"; \ - $(INSTALL_SCRIPT) $$files "$(DESTDIR)$(pkgdatadir)$$dir" || exit $$?; \ - } \ - ; done - -uninstall-dist_pkgdataSCRIPTS: - @$(NORMAL_UNINSTALL) - @list='$(dist_pkgdata_SCRIPTS)'; test -n "$(pkgdatadir)" || exit 0; \ - files=`for p in $$list; do echo "$$p"; done | \ - sed -e 's,.*/,,;$(transform)'`; \ - dir='$(DESTDIR)$(pkgdatadir)'; $(am__uninstall_files_from_dir) - -mostlyclean-compile: - -rm -f *.$(OBJEXT) - -rm -f http-parser/*.$(OBJEXT) - -rm -f http-parser/*.lo - -distclean-compile: - -rm -f *.tab.c - -@AMDEP_TRUE@@am__include@ @am__quote@http-parser/$(DEPDIR)/http_parser.Plo@am__quote@ - -.c.o: -@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\ -@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ -@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< - -.c.obj: -@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\ -@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\ -@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` - -.c.lo: -@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\ -@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ -@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< - -mostlyclean-libtool: - -rm -f *.lo - -clean-libtool: - -rm -rf .libs _libs - -rm -rf http-parser/.libs http-parser/_libs - -ID: $(am__tagged_files) - $(am__define_uniq_tagged_files); mkid -fID $$unique -tags: tags-am -TAGS: tags - -tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) - set x; \ - here=`pwd`; \ - $(am__define_uniq_tagged_files); \ - shift; \ - if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ - test -n "$$unique" || unique=$$empty_fix; \ - if test $$# -gt 0; then \ - $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ - "$$@" $$unique; \ - else \ - $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ - $$unique; \ - fi; \ - fi -ctags: ctags-am - -CTAGS: ctags -ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) - $(am__define_uniq_tagged_files); \ - test -z "$(CTAGS_ARGS)$$unique" \ - || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ - $$unique - -GTAGS: - here=`$(am__cd) $(top_builddir) && pwd` \ - && $(am__cd) $(top_srcdir) \ - && gtags -i $(GTAGS_ARGS) "$$here" -cscopelist: cscopelist-am - -cscopelist-am: $(am__tagged_files) - list='$(am__tagged_files)'; \ - case "$(srcdir)" in \ - [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ - *) sdir=$(subdir)/$(srcdir) ;; \ - esac; \ - for i in $$list; do \ - if test -f "$$i"; then \ - echo "$(subdir)/$$i"; \ - else \ - echo "$$sdir/$$i"; \ - fi; \ - done >> $(top_builddir)/cscope.files - -distclean-tags: - -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags - -distdir: $(DISTFILES) - @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ - topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ - list='$(DISTFILES)'; \ - dist_files=`for file in $$list; do echo $$file; done | \ - sed -e "s|^$$srcdirstrip/||;t" \ - -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ - case $$dist_files in \ - */*) $(MKDIR_P) `echo "$$dist_files" | \ - sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ - sort -u` ;; \ - esac; \ - for file in $$dist_files; do \ - if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ - if test -d $$d/$$file; then \ - dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ - if test -d "$(distdir)/$$file"; then \ - find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ - fi; \ - if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ - cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ - find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ - fi; \ - cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ - else \ - test -f "$(distdir)/$$file" \ - || cp -p $$d/$$file "$(distdir)/$$file" \ - || exit 1; \ - fi; \ - done -check-am: all-am -check: check-am -all-am: Makefile $(LTLIBRARIES) $(SCRIPTS) -installdirs: - for dir in "$(DESTDIR)$(pkgdatadir)"; do \ - test -z "$$dir" || $(MKDIR_P) "$$dir"; \ - done -install: install-am -install-exec: install-exec-am -install-data: install-data-am -uninstall: uninstall-am - -install-am: all-am - @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am - -installcheck: installcheck-am -install-strip: - if test -z '$(STRIP)'; then \ - $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ - install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ - install; \ - else \ - $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ - install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ - "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ - fi -mostlyclean-generic: - -clean-generic: - -distclean-generic: - -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) - -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) - -rm -f http-parser/$(DEPDIR)/$(am__dirstamp) - -rm -f http-parser/$(am__dirstamp) - -maintainer-clean-generic: - @echo "This command is intended for maintainers to use" - @echo "it deletes files that may require special tools to rebuild." -clean: clean-am - -clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \ - mostlyclean-am - -distclean: distclean-am - -rm -rf http-parser/$(DEPDIR) - -rm -f Makefile -distclean-am: clean-am distclean-compile distclean-generic \ - distclean-tags - -dvi: dvi-am - -dvi-am: - -html: html-am - -html-am: - -info: info-am - -info-am: - -install-data-am: install-dist_pkgdataSCRIPTS - -install-dvi: install-dvi-am - -install-dvi-am: - -install-exec-am: - -install-html: install-html-am - -install-html-am: - -install-info: install-info-am - -install-info-am: - -install-man: - -install-pdf: install-pdf-am - -install-pdf-am: - -install-ps: install-ps-am - -install-ps-am: - -installcheck-am: - -maintainer-clean: maintainer-clean-am - -rm -rf http-parser/$(DEPDIR) - -rm -f Makefile -maintainer-clean-am: distclean-am maintainer-clean-generic - -mostlyclean: mostlyclean-am - -mostlyclean-am: mostlyclean-compile mostlyclean-generic \ - mostlyclean-libtool - -pdf: pdf-am - -pdf-am: - -ps: ps-am - -ps-am: - -uninstall-am: uninstall-dist_pkgdataSCRIPTS - -.MAKE: install-am install-strip - -.PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ - clean-libtool clean-noinstLTLIBRARIES cscopelist-am ctags \ - ctags-am distclean distclean-compile distclean-generic \ - distclean-libtool distclean-tags distdir dvi dvi-am html \ - html-am info info-am install install-am install-data \ - install-data-am install-dist_pkgdataSCRIPTS install-dvi \ - install-dvi-am install-exec install-exec-am install-html \ - install-html-am install-info install-info-am install-man \ - install-pdf install-pdf-am install-ps install-ps-am \ - install-strip installcheck installcheck-am installdirs \ - maintainer-clean maintainer-clean-generic mostlyclean \ - mostlyclean-compile mostlyclean-generic mostlyclean-libtool \ - pdf pdf-am ps ps-am tags tags-am uninstall uninstall-am \ - uninstall-dist_pkgdataSCRIPTS - - -# Tell versions [3.59,3.63) of GNU make to not export all variables. -# Otherwise a system limit (for SysV at least) may be exceeded. -.NOEXPORT: diff --git a/third-party/h2o/fetch-ocsp-response b/third-party/h2o/fetch-ocsp-response deleted file mode 100755 index 9272dae..0000000 --- a/third-party/h2o/fetch-ocsp-response +++ /dev/null @@ -1,150 +0,0 @@ -#! /bin/sh -exec perl -x $0 "$@" -#! perl - -# Copyright (c) 2015 DeNA Co., Ltd. -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to -# deal in the Software without restriction, including without limitation the -# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -# sell copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -# IN THE SOFTWARE. - -use strict; -use warnings; -use File::Temp qw(tempdir); -use Getopt::Long; - -# from sysexits.h -use constant EX_TEMPFAIL => 75; - -my ($issuer_fn, $opt_help); -my $openssl_cmd = 'openssl'; - -GetOptions( - "issuer=s" => \$issuer_fn, - "openssl=s", => \$openssl_cmd, - help => \$opt_help, -) or exit(1); -if ($opt_help) { - print << "EOT"; -Usage: $0 [] - -Options: - --issuer issuer certificate (if omitted, is extracted from the - certificate chain) - --openssl openssl command to use (default: "openssl") - --help prints this help - -The command issues an OCSP request for given server certificate, verifies the -response and prints the resulting DER. - -The command exits 0 if successful, or 75 (EX_TEMPFAIL) on temporary error. -Other exit codes may be returned in case of hard errors. - -EOT - exit(0); -} - -die "no certificate file\n" - if @ARGV == 0; -my $cert_fn = shift @ARGV; - -my $tempdir = tempdir(CLEANUP => 1); - -my $openssl_version = run_openssl("version"); -chomp $openssl_version; -print STDERR "fetch-ocsp-response (using $openssl_version)\n"; - -# obtain ocsp uri -my $ocsp_uri = run_openssl("x509 -in $cert_fn -noout -ocsp_uri"); -chomp $ocsp_uri; -die "failed to extract ocsp URI from $cert_fn\n" - if $ocsp_uri !~ m{^https?://}; -my($ocsp_host) = $ocsp_uri =~ m{^https?://([^/:]+)}; - -# save issuer certificate -if (! defined $issuer_fn) { - my $chain = read_file($cert_fn); - $chain =~ m{-----END CERTIFICATE-----.*?(-----BEGIN CERTIFICATE-----.*?-----END CERTIFICATE-----)}s - or die "--issuer option was not used, and failed to extract issuer certificate from the certificate\n"; - $issuer_fn = "$tempdir/issuer.crt"; - write_file($issuer_fn, "$1\n"); -} - -# obtain response (without verification) -print STDERR "sending OCSP request to $ocsp_uri\n"; -my $resp = run_openssl( - "ocsp -issuer $issuer_fn -cert $cert_fn -url $ocsp_uri" - . ($openssl_version =~ /^OpenSSL 1\./is ? " -header Host $ocsp_host" : "") - . " -noverify -respout $tempdir/resp.der " . join(' ', @ARGV), - 1, -); -print STDERR $resp; - -# verify the response -print STDERR "verifying the response signature\n"; -my $success; -for my $args ( - # try from exotic options - "-VAfile $issuer_fn", # for comodo - "-partial_chain -trusted_first -CAfile $issuer_fn", # these options are only available in OpenSSL >= 1.0.2 - "-CAfile $issuer_fn", # for OpenSSL <= 1.0.1 -) { - if (system("$openssl_cmd ocsp -respin $tempdir/resp.der $args > $tempdir/verify.out 2>&1") == 0) { - print STDERR "verify OK (used: $args)\n"; - $success = 1; - last; - } -} -if (! $success) { - print STDERR read_file("$tempdir/verify.out"); - tempfail("failed to verify the response\n"); -} - -# success -print read_file("$tempdir/resp.der"); -exit 0; - -sub run_openssl { - my ($args, $tempfail) = @_; - open my $fh, "-|", "$openssl_cmd $args" - or die "failed to invoke $openssl_cmd:$!"; - my $resp = do { local $/; <$fh> }; - close $fh - or ($tempfail ? \&tempfail : \&die)->("OpenSSL exitted abnormally: $openssl_cmd $args:$!"); - $resp; -} - -sub read_file { - my $fn = shift; - open my $fh, "<", $fn - or die "failed to open file:$fn:$!"; - local $/; - <$fh>; -} - -sub write_file { - my ($fn, $data) = @_; - open my $fh, ">", $fn - or die "failed to open file:$fn:$!"; - print $fh $data; - close $fh; -} - -sub tempfail { - print STDERR @_; - exit EX_TEMPFAIL; -} diff --git a/third-party/http-parser/http_parser.c b/third-party/http-parser/http_parser.c deleted file mode 100644 index aa6310f..0000000 --- a/third-party/http-parser/http_parser.c +++ /dev/null @@ -1,2418 +0,0 @@ -/* Based on src/http/ngx_http_parse.c from NGINX copyright Igor Sysoev - * - * Additional changes are licensed under the same terms as NGINX and - * copyright Joyent, Inc. and other Node contributors. All rights reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS - * IN THE SOFTWARE. - */ -#include "http_parser.h" -#include -#include -#include -#include -#include -#include - -#ifndef ULLONG_MAX -# define ULLONG_MAX ((uint64_t) -1) /* 2^64-1 */ -#endif - -#ifndef MIN -# define MIN(a,b) ((a) < (b) ? (a) : (b)) -#endif - -#ifndef ARRAY_SIZE -# define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) -#endif - -#ifndef BIT_AT -# define BIT_AT(a, i) \ - (!!((unsigned int) (a)[(unsigned int) (i) >> 3] & \ - (1 << ((unsigned int) (i) & 7)))) -#endif - -#ifndef ELEM_AT -# define ELEM_AT(a, i, v) ((unsigned int) (i) < ARRAY_SIZE(a) ? (a)[(i)] : (v)) -#endif - -#define SET_ERRNO(e) \ -do { \ - parser->http_errno = (e); \ -} while(0) - -#define CURRENT_STATE() p_state -#define UPDATE_STATE(V) p_state = (enum state) (V); -#define RETURN(V) \ -do { \ - parser->state = CURRENT_STATE(); \ - return (V); \ -} while (0); -#define REEXECUTE() \ - goto reexecute; \ - - -#ifdef __GNUC__ -# define LIKELY(X) __builtin_expect(!!(X), 1) -# define UNLIKELY(X) __builtin_expect(!!(X), 0) -#else -# define LIKELY(X) (X) -# define UNLIKELY(X) (X) -#endif - - -/* Run the notify callback FOR, returning ER if it fails */ -#define CALLBACK_NOTIFY_(FOR, ER) \ -do { \ - assert(HTTP_PARSER_ERRNO(parser) == HPE_OK); \ - \ - if (LIKELY(settings->on_##FOR)) { \ - parser->state = CURRENT_STATE(); \ - if (UNLIKELY(0 != settings->on_##FOR(parser))) { \ - SET_ERRNO(HPE_CB_##FOR); \ - } \ - UPDATE_STATE(parser->state); \ - \ - /* We either errored above or got paused; get out */ \ - if (UNLIKELY(HTTP_PARSER_ERRNO(parser) != HPE_OK)) { \ - return (ER); \ - } \ - } \ -} while (0) - -/* Run the notify callback FOR and consume the current byte */ -#define CALLBACK_NOTIFY(FOR) CALLBACK_NOTIFY_(FOR, p - data + 1) - -/* Run the notify callback FOR and don't consume the current byte */ -#define CALLBACK_NOTIFY_NOADVANCE(FOR) CALLBACK_NOTIFY_(FOR, p - data) - -/* Run data callback FOR with LEN bytes, returning ER if it fails */ -#define CALLBACK_DATA_(FOR, LEN, ER) \ -do { \ - assert(HTTP_PARSER_ERRNO(parser) == HPE_OK); \ - \ - if (FOR##_mark) { \ - if (LIKELY(settings->on_##FOR)) { \ - parser->state = CURRENT_STATE(); \ - if (UNLIKELY(0 != \ - settings->on_##FOR(parser, FOR##_mark, (LEN)))) { \ - SET_ERRNO(HPE_CB_##FOR); \ - } \ - UPDATE_STATE(parser->state); \ - \ - /* We either errored above or got paused; get out */ \ - if (UNLIKELY(HTTP_PARSER_ERRNO(parser) != HPE_OK)) { \ - return (ER); \ - } \ - } \ - FOR##_mark = NULL; \ - } \ -} while (0) - -/* Run the data callback FOR and consume the current byte */ -#define CALLBACK_DATA(FOR) \ - CALLBACK_DATA_(FOR, p - FOR##_mark, p - data + 1) - -/* Run the data callback FOR and don't consume the current byte */ -#define CALLBACK_DATA_NOADVANCE(FOR) \ - CALLBACK_DATA_(FOR, p - FOR##_mark, p - data) - -/* Set the mark FOR; non-destructive if mark is already set */ -#define MARK(FOR) \ -do { \ - if (!FOR##_mark) { \ - FOR##_mark = p; \ - } \ -} while (0) - -/* Don't allow the total size of the HTTP headers (including the status - * line) to exceed HTTP_MAX_HEADER_SIZE. This check is here to protect - * embedders against denial-of-service attacks where the attacker feeds - * us a never-ending header that the embedder keeps buffering. - * - * This check is arguably the responsibility of embedders but we're doing - * it on the embedder's behalf because most won't bother and this way we - * make the web a little safer. HTTP_MAX_HEADER_SIZE is still far bigger - * than any reasonable request or response so this should never affect - * day-to-day operation. - */ -#define COUNT_HEADER_SIZE(V) \ -do { \ - parser->nread += (V); \ - if (UNLIKELY(parser->nread > (HTTP_MAX_HEADER_SIZE))) { \ - SET_ERRNO(HPE_HEADER_OVERFLOW); \ - goto error; \ - } \ -} while (0) - - -#define PROXY_CONNECTION "proxy-connection" -#define CONNECTION "connection" -#define CONTENT_LENGTH "content-length" -#define TRANSFER_ENCODING "transfer-encoding" -#define UPGRADE "upgrade" -#define CHUNKED "chunked" -#define KEEP_ALIVE "keep-alive" -#define CLOSE "close" - - -static const char *method_strings[] = - { -#define XX(num, name, string) #string, - HTTP_METHOD_MAP(XX) -#undef XX - }; - - -/* Tokens as defined by rfc 2616. Also lowercases them. - * token = 1* - * separators = "(" | ")" | "<" | ">" | "@" - * | "," | ";" | ":" | "\" | <"> - * | "/" | "[" | "]" | "?" | "=" - * | "{" | "}" | SP | HT - */ -static const char tokens[256] = { -/* 0 nul 1 soh 2 stx 3 etx 4 eot 5 enq 6 ack 7 bel */ - 0, 0, 0, 0, 0, 0, 0, 0, -/* 8 bs 9 ht 10 nl 11 vt 12 np 13 cr 14 so 15 si */ - 0, 0, 0, 0, 0, 0, 0, 0, -/* 16 dle 17 dc1 18 dc2 19 dc3 20 dc4 21 nak 22 syn 23 etb */ - 0, 0, 0, 0, 0, 0, 0, 0, -/* 24 can 25 em 26 sub 27 esc 28 fs 29 gs 30 rs 31 us */ - 0, 0, 0, 0, 0, 0, 0, 0, -/* 32 sp 33 ! 34 " 35 # 36 $ 37 % 38 & 39 ' */ - 0, '!', 0, '#', '$', '%', '&', '\'', -/* 40 ( 41 ) 42 * 43 + 44 , 45 - 46 . 47 / */ - 0, 0, '*', '+', 0, '-', '.', 0, -/* 48 0 49 1 50 2 51 3 52 4 53 5 54 6 55 7 */ - '0', '1', '2', '3', '4', '5', '6', '7', -/* 56 8 57 9 58 : 59 ; 60 < 61 = 62 > 63 ? */ - '8', '9', 0, 0, 0, 0, 0, 0, -/* 64 @ 65 A 66 B 67 C 68 D 69 E 70 F 71 G */ - 0, 'a', 'b', 'c', 'd', 'e', 'f', 'g', -/* 72 H 73 I 74 J 75 K 76 L 77 M 78 N 79 O */ - 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', -/* 80 P 81 Q 82 R 83 S 84 T 85 U 86 V 87 W */ - 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', -/* 88 X 89 Y 90 Z 91 [ 92 \ 93 ] 94 ^ 95 _ */ - 'x', 'y', 'z', 0, 0, 0, '^', '_', -/* 96 ` 97 a 98 b 99 c 100 d 101 e 102 f 103 g */ - '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g', -/* 104 h 105 i 106 j 107 k 108 l 109 m 110 n 111 o */ - 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', -/* 112 p 113 q 114 r 115 s 116 t 117 u 118 v 119 w */ - 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', -/* 120 x 121 y 122 z 123 { 124 | 125 } 126 ~ 127 del */ - 'x', 'y', 'z', 0, '|', 0, '~', 0 }; - - -static const int8_t unhex[256] = - {-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 - , 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,-1,-1,-1,-1,-1,-1 - ,-1,10,11,12,13,14,15,-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,10,11,12,13,14,15,-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 - }; - - -#if HTTP_PARSER_STRICT -# define T(v) 0 -#else -# define T(v) v -#endif - - -static const uint8_t normal_url_char[32] = { -/* 0 nul 1 soh 2 stx 3 etx 4 eot 5 enq 6 ack 7 bel */ - 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0, -/* 8 bs 9 ht 10 nl 11 vt 12 np 13 cr 14 so 15 si */ - 0 | T(2) | 0 | 0 | T(16) | 0 | 0 | 0, -/* 16 dle 17 dc1 18 dc2 19 dc3 20 dc4 21 nak 22 syn 23 etb */ - 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0, -/* 24 can 25 em 26 sub 27 esc 28 fs 29 gs 30 rs 31 us */ - 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0, -/* 32 sp 33 ! 34 " 35 # 36 $ 37 % 38 & 39 ' */ - 0 | 2 | 4 | 0 | 16 | 32 | 64 | 128, -/* 40 ( 41 ) 42 * 43 + 44 , 45 - 46 . 47 / */ - 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, -/* 48 0 49 1 50 2 51 3 52 4 53 5 54 6 55 7 */ - 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, -/* 56 8 57 9 58 : 59 ; 60 < 61 = 62 > 63 ? */ - 1 | 2 | 4 | 8 | 16 | 32 | 64 | 0, -/* 64 @ 65 A 66 B 67 C 68 D 69 E 70 F 71 G */ - 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, -/* 72 H 73 I 74 J 75 K 76 L 77 M 78 N 79 O */ - 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, -/* 80 P 81 Q 82 R 83 S 84 T 85 U 86 V 87 W */ - 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, -/* 88 X 89 Y 90 Z 91 [ 92 \ 93 ] 94 ^ 95 _ */ - 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, -/* 96 ` 97 a 98 b 99 c 100 d 101 e 102 f 103 g */ - 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, -/* 104 h 105 i 106 j 107 k 108 l 109 m 110 n 111 o */ - 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, -/* 112 p 113 q 114 r 115 s 116 t 117 u 118 v 119 w */ - 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, -/* 120 x 121 y 122 z 123 { 124 | 125 } 126 ~ 127 del */ - 1 | 2 | 4 | 8 | 16 | 32 | 64 | 0, }; - -#undef T - -enum state - { s_dead = 1 /* important that this is > 0 */ - - , s_start_req_or_res - , s_res_or_resp_H - , s_start_res - , s_res_H - , s_res_HT - , s_res_HTT - , s_res_HTTP - , s_res_first_http_major - , s_res_http_major - , s_res_first_http_minor - , s_res_http_minor - , s_res_first_status_code - , s_res_status_code - , s_res_status_start - , s_res_status - , s_res_line_almost_done - - , s_start_req - - , s_req_method - , s_req_spaces_before_url - , s_req_schema - , s_req_schema_slash - , s_req_schema_slash_slash - , s_req_server_start - , s_req_server - , s_req_server_with_at - , s_req_path - , s_req_query_string_start - , s_req_query_string - , s_req_fragment_start - , s_req_fragment - , s_req_http_start - , s_req_http_H - , s_req_http_HT - , s_req_http_HTT - , s_req_http_HTTP - , s_req_first_http_major - , s_req_http_major - , s_req_first_http_minor - , s_req_http_minor - , s_req_line_almost_done - - , s_header_field_start - , s_header_field - , s_header_value_discard_ws - , s_header_value_discard_ws_almost_done - , s_header_value_discard_lws - , s_header_value_start - , s_header_value - , s_header_value_lws - - , s_header_almost_done - - , s_chunk_size_start - , s_chunk_size - , s_chunk_parameters - , s_chunk_size_almost_done - - , s_headers_almost_done - , s_headers_done - - /* Important: 's_headers_done' must be the last 'header' state. All - * states beyond this must be 'body' states. It is used for overflow - * checking. See the PARSING_HEADER() macro. - */ - - , s_chunk_data - , s_chunk_data_almost_done - , s_chunk_data_done - - , s_body_identity - , s_body_identity_eof - - , s_message_done - }; - - -#define PARSING_HEADER(state) (state <= s_headers_done) - - -enum header_states - { h_general = 0 - , h_C - , h_CO - , h_CON - - , h_matching_connection - , h_matching_proxy_connection - , h_matching_content_length - , h_matching_transfer_encoding - , h_matching_upgrade - - , h_connection - , h_content_length - , h_transfer_encoding - , h_upgrade - - , h_matching_transfer_encoding_chunked - , h_matching_connection_token_start - , h_matching_connection_keep_alive - , h_matching_connection_close - , h_matching_connection_upgrade - , h_matching_connection_token - - , h_transfer_encoding_chunked - , h_connection_keep_alive - , h_connection_close - , h_connection_upgrade - }; - -enum http_host_state - { - s_http_host_dead = 1 - , s_http_userinfo_start - , s_http_userinfo - , s_http_host_start - , s_http_host_v6_start - , s_http_host - , s_http_host_v6 - , s_http_host_v6_end - , s_http_host_port_start - , s_http_host_port -}; - -/* Macros for character classes; depends on strict-mode */ -#define CR '\r' -#define LF '\n' -#define LOWER(c) (unsigned char)(c | 0x20) -#define IS_ALPHA(c) (LOWER(c) >= 'a' && LOWER(c) <= 'z') -#define IS_NUM(c) ((c) >= '0' && (c) <= '9') -#define IS_ALPHANUM(c) (IS_ALPHA(c) || IS_NUM(c)) -#define IS_HEX(c) (IS_NUM(c) || (LOWER(c) >= 'a' && LOWER(c) <= 'f')) -#define IS_MARK(c) ((c) == '-' || (c) == '_' || (c) == '.' || \ - (c) == '!' || (c) == '~' || (c) == '*' || (c) == '\'' || (c) == '(' || \ - (c) == ')') -#define IS_USERINFO_CHAR(c) (IS_ALPHANUM(c) || IS_MARK(c) || (c) == '%' || \ - (c) == ';' || (c) == ':' || (c) == '&' || (c) == '=' || (c) == '+' || \ - (c) == '$' || (c) == ',') - -#define STRICT_TOKEN(c) (tokens[(unsigned char)c]) - -#if HTTP_PARSER_STRICT -#define TOKEN(c) (tokens[(unsigned char)c]) -#define IS_URL_CHAR(c) (BIT_AT(normal_url_char, (unsigned char)c)) -#define IS_HOST_CHAR(c) (IS_ALPHANUM(c) || (c) == '.' || (c) == '-') -#else -#define TOKEN(c) ((c == ' ') ? ' ' : tokens[(unsigned char)c]) -#define IS_URL_CHAR(c) \ - (BIT_AT(normal_url_char, (unsigned char)c) || ((c) & 0x80)) -#define IS_HOST_CHAR(c) \ - (IS_ALPHANUM(c) || (c) == '.' || (c) == '-' || (c) == '_') -#endif - - -#define start_state (parser->type == HTTP_REQUEST ? s_start_req : s_start_res) - - -#if HTTP_PARSER_STRICT -# define STRICT_CHECK(cond) \ -do { \ - if (cond) { \ - SET_ERRNO(HPE_STRICT); \ - goto error; \ - } \ -} while (0) -# define NEW_MESSAGE() (http_should_keep_alive(parser) ? start_state : s_dead) -#else -# define STRICT_CHECK(cond) -# define NEW_MESSAGE() start_state -#endif - - -/* Map errno values to strings for human-readable output */ -#define HTTP_STRERROR_GEN(n, s) { "HPE_" #n, s }, -static struct { - const char *name; - const char *description; -} http_strerror_tab[] = { - HTTP_ERRNO_MAP(HTTP_STRERROR_GEN) -}; -#undef HTTP_STRERROR_GEN - -int http_message_needs_eof(const http_parser *parser); - -/* Our URL parser. - * - * This is designed to be shared by http_parser_execute() for URL validation, - * hence it has a state transition + byte-for-byte interface. In addition, it - * is meant to be embedded in http_parser_parse_url(), which does the dirty - * work of turning state transitions URL components for its API. - * - * This function should only be invoked with non-space characters. It is - * assumed that the caller cares about (and can detect) the transition between - * URL and non-URL states by looking for these. - */ -static enum state -parse_url_char(enum state s, const char ch) -{ - if (ch == ' ' || ch == '\r' || ch == '\n') { - return s_dead; - } - -#if HTTP_PARSER_STRICT - if (ch == '\t' || ch == '\f') { - return s_dead; - } -#endif - - switch (s) { - case s_req_spaces_before_url: - /* Proxied requests are followed by scheme of an absolute URI (alpha). - * All methods except CONNECT are followed by '/' or '*'. - */ - - if (ch == '/' || ch == '*') { - return s_req_path; - } - - if (IS_ALPHA(ch)) { - return s_req_schema; - } - - break; - - case s_req_schema: - if (IS_ALPHA(ch)) { - return s; - } - - if (ch == ':') { - return s_req_schema_slash; - } - - break; - - case s_req_schema_slash: - if (ch == '/') { - return s_req_schema_slash_slash; - } - - break; - - case s_req_schema_slash_slash: - if (ch == '/') { - return s_req_server_start; - } - - break; - - case s_req_server_with_at: - if (ch == '@') { - return s_dead; - } - - /* FALLTHROUGH */ - case s_req_server_start: - case s_req_server: - if (ch == '/') { - return s_req_path; - } - - if (ch == '?') { - return s_req_query_string_start; - } - - if (ch == '@') { - return s_req_server_with_at; - } - - if (IS_USERINFO_CHAR(ch) || ch == '[' || ch == ']') { - return s_req_server; - } - - break; - - case s_req_path: - if (IS_URL_CHAR(ch)) { - return s; - } - - switch (ch) { - case '?': - return s_req_query_string_start; - - case '#': - return s_req_fragment_start; - } - - break; - - case s_req_query_string_start: - case s_req_query_string: - if (IS_URL_CHAR(ch)) { - return s_req_query_string; - } - - switch (ch) { - case '?': - /* allow extra '?' in query string */ - return s_req_query_string; - - case '#': - return s_req_fragment_start; - } - - break; - - case s_req_fragment_start: - if (IS_URL_CHAR(ch)) { - return s_req_fragment; - } - - switch (ch) { - case '?': - return s_req_fragment; - - case '#': - return s; - } - - break; - - case s_req_fragment: - if (IS_URL_CHAR(ch)) { - return s; - } - - switch (ch) { - case '?': - case '#': - return s; - } - - break; - - default: - break; - } - - /* We should never fall out of the switch above unless there's an error */ - return s_dead; -} - -size_t http_parser_execute (http_parser *parser, - const http_parser_settings *settings, - const char *data, - size_t len) -{ - char c, ch; - int8_t unhex_val; - const char *p = data; - const char *header_field_mark = 0; - const char *header_value_mark = 0; - const char *url_mark = 0; - const char *body_mark = 0; - const char *status_mark = 0; - enum state p_state = (enum state) parser->state; - - /* We're in an error state. Don't bother doing anything. */ - if (HTTP_PARSER_ERRNO(parser) != HPE_OK) { - return 0; - } - - if (len == 0) { - switch (CURRENT_STATE()) { - case s_body_identity_eof: - /* Use of CALLBACK_NOTIFY() here would erroneously return 1 byte read if - * we got paused. - */ - CALLBACK_NOTIFY_NOADVANCE(message_complete); - return 0; - - case s_dead: - case s_start_req_or_res: - case s_start_res: - case s_start_req: - return 0; - - default: - SET_ERRNO(HPE_INVALID_EOF_STATE); - return 1; - } - } - - - if (CURRENT_STATE() == s_header_field) - header_field_mark = data; - if (CURRENT_STATE() == s_header_value) - header_value_mark = data; - switch (CURRENT_STATE()) { - case s_req_path: - case s_req_schema: - case s_req_schema_slash: - case s_req_schema_slash_slash: - case s_req_server_start: - case s_req_server: - case s_req_server_with_at: - case s_req_query_string_start: - case s_req_query_string: - case s_req_fragment_start: - case s_req_fragment: - url_mark = data; - break; - case s_res_status: - status_mark = data; - break; - default: - break; - } - - for (p=data; p != data + len; p++) { - ch = *p; - - if (PARSING_HEADER(CURRENT_STATE())) - COUNT_HEADER_SIZE(1); - -reexecute: - switch (CURRENT_STATE()) { - - case s_dead: - /* this state is used after a 'Connection: close' message - * the parser will error out if it reads another message - */ - if (LIKELY(ch == CR || ch == LF)) - break; - - SET_ERRNO(HPE_CLOSED_CONNECTION); - goto error; - - case s_start_req_or_res: - { - if (ch == CR || ch == LF) - break; - parser->flags = 0; - parser->content_length = ULLONG_MAX; - - if (ch == 'H') { - UPDATE_STATE(s_res_or_resp_H); - - CALLBACK_NOTIFY(message_begin); - } else { - parser->type = HTTP_REQUEST; - UPDATE_STATE(s_start_req); - REEXECUTE(); - } - - break; - } - - case s_res_or_resp_H: - if (ch == 'T') { - parser->type = HTTP_RESPONSE; - UPDATE_STATE(s_res_HT); - } else { - if (UNLIKELY(ch != 'E')) { - SET_ERRNO(HPE_INVALID_CONSTANT); - goto error; - } - - parser->type = HTTP_REQUEST; - parser->method = HTTP_HEAD; - parser->index = 2; - UPDATE_STATE(s_req_method); - } - break; - - case s_start_res: - { - parser->flags = 0; - parser->content_length = ULLONG_MAX; - - switch (ch) { - case 'H': - UPDATE_STATE(s_res_H); - break; - - case CR: - case LF: - break; - - default: - SET_ERRNO(HPE_INVALID_CONSTANT); - goto error; - } - - CALLBACK_NOTIFY(message_begin); - break; - } - - case s_res_H: - STRICT_CHECK(ch != 'T'); - UPDATE_STATE(s_res_HT); - break; - - case s_res_HT: - STRICT_CHECK(ch != 'T'); - UPDATE_STATE(s_res_HTT); - break; - - case s_res_HTT: - STRICT_CHECK(ch != 'P'); - UPDATE_STATE(s_res_HTTP); - break; - - case s_res_HTTP: - STRICT_CHECK(ch != '/'); - UPDATE_STATE(s_res_first_http_major); - break; - - case s_res_first_http_major: - if (UNLIKELY(ch < '0' || ch > '9')) { - SET_ERRNO(HPE_INVALID_VERSION); - goto error; - } - - parser->http_major = ch - '0'; - UPDATE_STATE(s_res_http_major); - break; - - /* major HTTP version or dot */ - case s_res_http_major: - { - if (ch == '.') { - UPDATE_STATE(s_res_first_http_minor); - break; - } - - if (!IS_NUM(ch)) { - SET_ERRNO(HPE_INVALID_VERSION); - goto error; - } - - parser->http_major *= 10; - parser->http_major += ch - '0'; - - if (UNLIKELY(parser->http_major > 999)) { - SET_ERRNO(HPE_INVALID_VERSION); - goto error; - } - - break; - } - - /* first digit of minor HTTP version */ - case s_res_first_http_minor: - if (UNLIKELY(!IS_NUM(ch))) { - SET_ERRNO(HPE_INVALID_VERSION); - goto error; - } - - parser->http_minor = ch - '0'; - UPDATE_STATE(s_res_http_minor); - break; - - /* minor HTTP version or end of request line */ - case s_res_http_minor: - { - if (ch == ' ') { - UPDATE_STATE(s_res_first_status_code); - break; - } - - if (UNLIKELY(!IS_NUM(ch))) { - SET_ERRNO(HPE_INVALID_VERSION); - goto error; - } - - parser->http_minor *= 10; - parser->http_minor += ch - '0'; - - if (UNLIKELY(parser->http_minor > 999)) { - SET_ERRNO(HPE_INVALID_VERSION); - goto error; - } - - break; - } - - case s_res_first_status_code: - { - if (!IS_NUM(ch)) { - if (ch == ' ') { - break; - } - - SET_ERRNO(HPE_INVALID_STATUS); - goto error; - } - parser->status_code = ch - '0'; - UPDATE_STATE(s_res_status_code); - break; - } - - case s_res_status_code: - { - if (!IS_NUM(ch)) { - switch (ch) { - case ' ': - UPDATE_STATE(s_res_status_start); - break; - case CR: - UPDATE_STATE(s_res_line_almost_done); - break; - case LF: - UPDATE_STATE(s_header_field_start); - break; - default: - SET_ERRNO(HPE_INVALID_STATUS); - goto error; - } - break; - } - - parser->status_code *= 10; - parser->status_code += ch - '0'; - - if (UNLIKELY(parser->status_code > 999)) { - SET_ERRNO(HPE_INVALID_STATUS); - goto error; - } - - break; - } - - case s_res_status_start: - { - if (ch == CR) { - UPDATE_STATE(s_res_line_almost_done); - break; - } - - if (ch == LF) { - UPDATE_STATE(s_header_field_start); - break; - } - - MARK(status); - UPDATE_STATE(s_res_status); - parser->index = 0; - break; - } - - case s_res_status: - if (ch == CR) { - UPDATE_STATE(s_res_line_almost_done); - CALLBACK_DATA(status); - break; - } - - if (ch == LF) { - UPDATE_STATE(s_header_field_start); - CALLBACK_DATA(status); - break; - } - - break; - - case s_res_line_almost_done: - STRICT_CHECK(ch != LF); - UPDATE_STATE(s_header_field_start); - break; - - case s_start_req: - { - if (ch == CR || ch == LF) - break; - parser->flags = 0; - parser->content_length = ULLONG_MAX; - - if (UNLIKELY(!IS_ALPHA(ch))) { - SET_ERRNO(HPE_INVALID_METHOD); - goto error; - } - - parser->method = (enum http_method) 0; - parser->index = 1; - switch (ch) { - case 'C': parser->method = HTTP_CONNECT; /* or COPY, CHECKOUT */ break; - case 'D': parser->method = HTTP_DELETE; break; - case 'G': parser->method = HTTP_GET; break; - case 'H': parser->method = HTTP_HEAD; break; - case 'L': parser->method = HTTP_LOCK; break; - case 'M': parser->method = HTTP_MKCOL; /* or MOVE, MKACTIVITY, MERGE, M-SEARCH, MKCALENDAR */ break; - case 'N': parser->method = HTTP_NOTIFY; break; - case 'O': parser->method = HTTP_OPTIONS; break; - case 'P': parser->method = HTTP_POST; - /* or PROPFIND|PROPPATCH|PUT|PATCH|PURGE */ - break; - case 'R': parser->method = HTTP_REPORT; break; - case 'S': parser->method = HTTP_SUBSCRIBE; /* or SEARCH */ break; - case 'T': parser->method = HTTP_TRACE; break; - case 'U': parser->method = HTTP_UNLOCK; /* or UNSUBSCRIBE */ break; - default: - SET_ERRNO(HPE_INVALID_METHOD); - goto error; - } - UPDATE_STATE(s_req_method); - - CALLBACK_NOTIFY(message_begin); - - break; - } - - case s_req_method: - { - const char *matcher; - if (UNLIKELY(ch == '\0')) { - SET_ERRNO(HPE_INVALID_METHOD); - goto error; - } - - matcher = method_strings[parser->method]; - if (ch == ' ' && matcher[parser->index] == '\0') { - UPDATE_STATE(s_req_spaces_before_url); - } else if (ch == matcher[parser->index]) { - ; /* nada */ - } else if (parser->method == HTTP_CONNECT) { - if (parser->index == 1 && ch == 'H') { - parser->method = HTTP_CHECKOUT; - } else if (parser->index == 2 && ch == 'P') { - parser->method = HTTP_COPY; - } else { - SET_ERRNO(HPE_INVALID_METHOD); - goto error; - } - } else if (parser->method == HTTP_MKCOL) { - if (parser->index == 1 && ch == 'O') { - parser->method = HTTP_MOVE; - } else if (parser->index == 1 && ch == 'E') { - parser->method = HTTP_MERGE; - } else if (parser->index == 1 && ch == '-') { - parser->method = HTTP_MSEARCH; - } else if (parser->index == 2 && ch == 'A') { - parser->method = HTTP_MKACTIVITY; - } else if (parser->index == 3 && ch == 'A') { - parser->method = HTTP_MKCALENDAR; - } else { - SET_ERRNO(HPE_INVALID_METHOD); - goto error; - } - } else if (parser->method == HTTP_SUBSCRIBE) { - if (parser->index == 1 && ch == 'E') { - parser->method = HTTP_SEARCH; - } else { - SET_ERRNO(HPE_INVALID_METHOD); - goto error; - } - } else if (parser->index == 1 && parser->method == HTTP_POST) { - if (ch == 'R') { - parser->method = HTTP_PROPFIND; /* or HTTP_PROPPATCH */ - } else if (ch == 'U') { - parser->method = HTTP_PUT; /* or HTTP_PURGE */ - } else if (ch == 'A') { - parser->method = HTTP_PATCH; - } else { - SET_ERRNO(HPE_INVALID_METHOD); - goto error; - } - } else if (parser->index == 2) { - if (parser->method == HTTP_PUT) { - if (ch == 'R') { - parser->method = HTTP_PURGE; - } else { - SET_ERRNO(HPE_INVALID_METHOD); - goto error; - } - } else if (parser->method == HTTP_UNLOCK) { - if (ch == 'S') { - parser->method = HTTP_UNSUBSCRIBE; - } else { - SET_ERRNO(HPE_INVALID_METHOD); - goto error; - } - } else { - SET_ERRNO(HPE_INVALID_METHOD); - goto error; - } - } else if (parser->index == 4 && parser->method == HTTP_PROPFIND && ch == 'P') { - parser->method = HTTP_PROPPATCH; - } else { - SET_ERRNO(HPE_INVALID_METHOD); - goto error; - } - - ++parser->index; - break; - } - - case s_req_spaces_before_url: - { - if (ch == ' ') break; - - MARK(url); - if (parser->method == HTTP_CONNECT) { - UPDATE_STATE(s_req_server_start); - } - - UPDATE_STATE(parse_url_char(CURRENT_STATE(), ch)); - if (UNLIKELY(CURRENT_STATE() == s_dead)) { - SET_ERRNO(HPE_INVALID_URL); - goto error; - } - - break; - } - - case s_req_schema: - case s_req_schema_slash: - case s_req_schema_slash_slash: - case s_req_server_start: - { - switch (ch) { - /* No whitespace allowed here */ - case ' ': - case CR: - case LF: - SET_ERRNO(HPE_INVALID_URL); - goto error; - default: - UPDATE_STATE(parse_url_char(CURRENT_STATE(), ch)); - if (UNLIKELY(CURRENT_STATE() == s_dead)) { - SET_ERRNO(HPE_INVALID_URL); - goto error; - } - } - - break; - } - - case s_req_server: - case s_req_server_with_at: - case s_req_path: - case s_req_query_string_start: - case s_req_query_string: - case s_req_fragment_start: - case s_req_fragment: - { - switch (ch) { - case ' ': - UPDATE_STATE(s_req_http_start); - CALLBACK_DATA(url); - break; - case CR: - case LF: - parser->http_major = 0; - parser->http_minor = 9; - UPDATE_STATE((ch == CR) ? - s_req_line_almost_done : - s_header_field_start); - CALLBACK_DATA(url); - break; - default: - UPDATE_STATE(parse_url_char(CURRENT_STATE(), ch)); - if (UNLIKELY(CURRENT_STATE() == s_dead)) { - SET_ERRNO(HPE_INVALID_URL); - goto error; - } - } - break; - } - - case s_req_http_start: - switch (ch) { - case 'H': - UPDATE_STATE(s_req_http_H); - break; - case ' ': - break; - default: - SET_ERRNO(HPE_INVALID_CONSTANT); - goto error; - } - break; - - case s_req_http_H: - STRICT_CHECK(ch != 'T'); - UPDATE_STATE(s_req_http_HT); - break; - - case s_req_http_HT: - STRICT_CHECK(ch != 'T'); - UPDATE_STATE(s_req_http_HTT); - break; - - case s_req_http_HTT: - STRICT_CHECK(ch != 'P'); - UPDATE_STATE(s_req_http_HTTP); - break; - - case s_req_http_HTTP: - STRICT_CHECK(ch != '/'); - UPDATE_STATE(s_req_first_http_major); - break; - - /* first digit of major HTTP version */ - case s_req_first_http_major: - if (UNLIKELY(ch < '1' || ch > '9')) { - SET_ERRNO(HPE_INVALID_VERSION); - goto error; - } - - parser->http_major = ch - '0'; - UPDATE_STATE(s_req_http_major); - break; - - /* major HTTP version or dot */ - case s_req_http_major: - { - if (ch == '.') { - UPDATE_STATE(s_req_first_http_minor); - break; - } - - if (UNLIKELY(!IS_NUM(ch))) { - SET_ERRNO(HPE_INVALID_VERSION); - goto error; - } - - parser->http_major *= 10; - parser->http_major += ch - '0'; - - if (UNLIKELY(parser->http_major > 999)) { - SET_ERRNO(HPE_INVALID_VERSION); - goto error; - } - - break; - } - - /* first digit of minor HTTP version */ - case s_req_first_http_minor: - if (UNLIKELY(!IS_NUM(ch))) { - SET_ERRNO(HPE_INVALID_VERSION); - goto error; - } - - parser->http_minor = ch - '0'; - UPDATE_STATE(s_req_http_minor); - break; - - /* minor HTTP version or end of request line */ - case s_req_http_minor: - { - if (ch == CR) { - UPDATE_STATE(s_req_line_almost_done); - break; - } - - if (ch == LF) { - UPDATE_STATE(s_header_field_start); - break; - } - - /* XXX allow spaces after digit? */ - - if (UNLIKELY(!IS_NUM(ch))) { - SET_ERRNO(HPE_INVALID_VERSION); - goto error; - } - - parser->http_minor *= 10; - parser->http_minor += ch - '0'; - - if (UNLIKELY(parser->http_minor > 999)) { - SET_ERRNO(HPE_INVALID_VERSION); - goto error; - } - - break; - } - - /* end of request line */ - case s_req_line_almost_done: - { - if (UNLIKELY(ch != LF)) { - SET_ERRNO(HPE_LF_EXPECTED); - goto error; - } - - UPDATE_STATE(s_header_field_start); - break; - } - - case s_header_field_start: - { - if (ch == CR) { - UPDATE_STATE(s_headers_almost_done); - break; - } - - if (ch == LF) { - /* they might be just sending \n instead of \r\n so this would be - * the second \n to denote the end of headers*/ - UPDATE_STATE(s_headers_almost_done); - REEXECUTE(); - } - - c = TOKEN(ch); - - if (UNLIKELY(!c)) { - SET_ERRNO(HPE_INVALID_HEADER_TOKEN); - goto error; - } - - MARK(header_field); - - parser->index = 0; - UPDATE_STATE(s_header_field); - - switch (c) { - case 'c': - parser->header_state = h_C; - break; - - case 'p': - parser->header_state = h_matching_proxy_connection; - break; - - case 't': - parser->header_state = h_matching_transfer_encoding; - break; - - case 'u': - parser->header_state = h_matching_upgrade; - break; - - default: - parser->header_state = h_general; - break; - } - break; - } - - case s_header_field: - { - const char* start = p; - for (; p != data + len; p++) { - ch = *p; - c = TOKEN(ch); - - if (!c) - break; - - switch (parser->header_state) { - case h_general: - break; - - case h_C: - parser->index++; - parser->header_state = (c == 'o' ? h_CO : h_general); - break; - - case h_CO: - parser->index++; - parser->header_state = (c == 'n' ? h_CON : h_general); - break; - - case h_CON: - parser->index++; - switch (c) { - case 'n': - parser->header_state = h_matching_connection; - break; - case 't': - parser->header_state = h_matching_content_length; - break; - default: - parser->header_state = h_general; - break; - } - break; - - /* connection */ - - case h_matching_connection: - parser->index++; - if (parser->index > sizeof(CONNECTION)-1 - || c != CONNECTION[parser->index]) { - parser->header_state = h_general; - } else if (parser->index == sizeof(CONNECTION)-2) { - parser->header_state = h_connection; - } - break; - - /* proxy-connection */ - - case h_matching_proxy_connection: - parser->index++; - if (parser->index > sizeof(PROXY_CONNECTION)-1 - || c != PROXY_CONNECTION[parser->index]) { - parser->header_state = h_general; - } else if (parser->index == sizeof(PROXY_CONNECTION)-2) { - parser->header_state = h_connection; - } - break; - - /* content-length */ - - case h_matching_content_length: - parser->index++; - if (parser->index > sizeof(CONTENT_LENGTH)-1 - || c != CONTENT_LENGTH[parser->index]) { - parser->header_state = h_general; - } else if (parser->index == sizeof(CONTENT_LENGTH)-2) { - parser->header_state = h_content_length; - } - break; - - /* transfer-encoding */ - - case h_matching_transfer_encoding: - parser->index++; - if (parser->index > sizeof(TRANSFER_ENCODING)-1 - || c != TRANSFER_ENCODING[parser->index]) { - parser->header_state = h_general; - } else if (parser->index == sizeof(TRANSFER_ENCODING)-2) { - parser->header_state = h_transfer_encoding; - } - break; - - /* upgrade */ - - case h_matching_upgrade: - parser->index++; - if (parser->index > sizeof(UPGRADE)-1 - || c != UPGRADE[parser->index]) { - parser->header_state = h_general; - } else if (parser->index == sizeof(UPGRADE)-2) { - parser->header_state = h_upgrade; - } - break; - - case h_connection: - case h_content_length: - case h_transfer_encoding: - case h_upgrade: - if (ch != ' ') parser->header_state = h_general; - break; - - default: - assert(0 && "Unknown header_state"); - break; - } - } - - COUNT_HEADER_SIZE(p - start); - - if (p == data + len) { - --p; - break; - } - - if (ch == ':') { - UPDATE_STATE(s_header_value_discard_ws); - CALLBACK_DATA(header_field); - break; - } - - SET_ERRNO(HPE_INVALID_HEADER_TOKEN); - goto error; - } - - case s_header_value_discard_ws: - if (ch == ' ' || ch == '\t') break; - - if (ch == CR) { - UPDATE_STATE(s_header_value_discard_ws_almost_done); - break; - } - - if (ch == LF) { - UPDATE_STATE(s_header_value_discard_lws); - break; - } - - /* FALLTHROUGH */ - - case s_header_value_start: - { - MARK(header_value); - - UPDATE_STATE(s_header_value); - parser->index = 0; - - c = LOWER(ch); - - switch (parser->header_state) { - case h_upgrade: - parser->flags |= F_UPGRADE; - parser->header_state = h_general; - break; - - case h_transfer_encoding: - /* looking for 'Transfer-Encoding: chunked' */ - if ('c' == c) { - parser->header_state = h_matching_transfer_encoding_chunked; - } else { - parser->header_state = h_general; - } - break; - - case h_content_length: - if (UNLIKELY(!IS_NUM(ch))) { - SET_ERRNO(HPE_INVALID_CONTENT_LENGTH); - goto error; - } - - parser->content_length = ch - '0'; - break; - - case h_connection: - /* looking for 'Connection: keep-alive' */ - if (c == 'k') { - parser->header_state = h_matching_connection_keep_alive; - /* looking for 'Connection: close' */ - } else if (c == 'c') { - parser->header_state = h_matching_connection_close; - } else if (c == 'u') { - parser->header_state = h_matching_connection_upgrade; - } else { - parser->header_state = h_matching_connection_token; - } - break; - - /* Multi-value `Connection` header */ - case h_matching_connection_token_start: - break; - - default: - parser->header_state = h_general; - break; - } - break; - } - - case s_header_value: - { - const char* start = p; - enum header_states h_state = (enum header_states) parser->header_state; - for (; p != data + len; p++) { - ch = *p; - if (ch == CR) { - UPDATE_STATE(s_header_almost_done); - parser->header_state = h_state; - CALLBACK_DATA(header_value); - break; - } - - if (ch == LF) { - UPDATE_STATE(s_header_almost_done); - COUNT_HEADER_SIZE(p - start); - parser->header_state = h_state; - CALLBACK_DATA_NOADVANCE(header_value); - REEXECUTE(); - } - - c = LOWER(ch); - - switch (h_state) { - case h_general: - { - const char* p_cr; - const char* p_lf; - size_t limit = data + len - p; - - limit = MIN(limit, HTTP_MAX_HEADER_SIZE); - - p_cr = (const char*) memchr(p, CR, limit); - p_lf = (const char*) memchr(p, LF, limit); - if (p_cr != NULL) { - if (p_lf != NULL && p_cr >= p_lf) - p = p_lf; - else - p = p_cr; - } else if (UNLIKELY(p_lf != NULL)) { - p = p_lf; - } else { - p = data + len; - } - --p; - - break; - } - - case h_connection: - case h_transfer_encoding: - assert(0 && "Shouldn't get here."); - break; - - case h_content_length: - { - uint64_t t; - - if (ch == ' ') break; - - if (UNLIKELY(!IS_NUM(ch))) { - SET_ERRNO(HPE_INVALID_CONTENT_LENGTH); - parser->header_state = h_state; - goto error; - } - - t = parser->content_length; - t *= 10; - t += ch - '0'; - - /* Overflow? Test against a conservative limit for simplicity. */ - if (UNLIKELY((ULLONG_MAX - 10) / 10 < parser->content_length)) { - SET_ERRNO(HPE_INVALID_CONTENT_LENGTH); - parser->header_state = h_state; - goto error; - } - - parser->content_length = t; - break; - } - - /* Transfer-Encoding: chunked */ - case h_matching_transfer_encoding_chunked: - parser->index++; - if (parser->index > sizeof(CHUNKED)-1 - || c != CHUNKED[parser->index]) { - h_state = h_general; - } else if (parser->index == sizeof(CHUNKED)-2) { - h_state = h_transfer_encoding_chunked; - } - break; - - case h_matching_connection_token_start: - /* looking for 'Connection: keep-alive' */ - if (c == 'k') { - h_state = h_matching_connection_keep_alive; - /* looking for 'Connection: close' */ - } else if (c == 'c') { - h_state = h_matching_connection_close; - } else if (c == 'u') { - h_state = h_matching_connection_upgrade; - } else if (STRICT_TOKEN(c)) { - h_state = h_matching_connection_token; - } else if (c == ' ' || c == '\t') { - /* Skip lws */ - } else { - h_state = h_general; - } - break; - - /* looking for 'Connection: keep-alive' */ - case h_matching_connection_keep_alive: - parser->index++; - if (parser->index > sizeof(KEEP_ALIVE)-1 - || c != KEEP_ALIVE[parser->index]) { - h_state = h_matching_connection_token; - } else if (parser->index == sizeof(KEEP_ALIVE)-2) { - h_state = h_connection_keep_alive; - } - break; - - /* looking for 'Connection: close' */ - case h_matching_connection_close: - parser->index++; - if (parser->index > sizeof(CLOSE)-1 || c != CLOSE[parser->index]) { - h_state = h_matching_connection_token; - } else if (parser->index == sizeof(CLOSE)-2) { - h_state = h_connection_close; - } - break; - - /* looking for 'Connection: upgrade' */ - case h_matching_connection_upgrade: - parser->index++; - if (parser->index > sizeof(UPGRADE) - 1 || - c != UPGRADE[parser->index]) { - h_state = h_matching_connection_token; - } else if (parser->index == sizeof(UPGRADE)-2) { - h_state = h_connection_upgrade; - } - break; - - case h_matching_connection_token: - if (ch == ',') { - h_state = h_matching_connection_token_start; - parser->index = 0; - } - break; - - case h_transfer_encoding_chunked: - if (ch != ' ') h_state = h_general; - break; - - case h_connection_keep_alive: - case h_connection_close: - case h_connection_upgrade: - if (ch == ',') { - if (h_state == h_connection_keep_alive) { - parser->flags |= F_CONNECTION_KEEP_ALIVE; - } else if (h_state == h_connection_close) { - parser->flags |= F_CONNECTION_CLOSE; - } else if (h_state == h_connection_upgrade) { - parser->flags |= F_CONNECTION_UPGRADE; - } - h_state = h_matching_connection_token_start; - parser->index = 0; - } else if (ch != ' ') { - h_state = h_matching_connection_token; - } - break; - - default: - UPDATE_STATE(s_header_value); - h_state = h_general; - break; - } - } - parser->header_state = h_state; - - COUNT_HEADER_SIZE(p - start); - - if (p == data + len) - --p; - break; - } - - case s_header_almost_done: - { - STRICT_CHECK(ch != LF); - - UPDATE_STATE(s_header_value_lws); - break; - } - - case s_header_value_lws: - { - if (ch == ' ' || ch == '\t') { - UPDATE_STATE(s_header_value_start); - REEXECUTE(); - } - - /* finished the header */ - switch (parser->header_state) { - case h_connection_keep_alive: - parser->flags |= F_CONNECTION_KEEP_ALIVE; - break; - case h_connection_close: - parser->flags |= F_CONNECTION_CLOSE; - break; - case h_transfer_encoding_chunked: - parser->flags |= F_CHUNKED; - break; - case h_connection_upgrade: - parser->flags |= F_CONNECTION_UPGRADE; - break; - default: - break; - } - - UPDATE_STATE(s_header_field_start); - REEXECUTE(); - } - - case s_header_value_discard_ws_almost_done: - { - STRICT_CHECK(ch != LF); - UPDATE_STATE(s_header_value_discard_lws); - break; - } - - case s_header_value_discard_lws: - { - if (ch == ' ' || ch == '\t') { - UPDATE_STATE(s_header_value_discard_ws); - break; - } else { - switch (parser->header_state) { - case h_connection_keep_alive: - parser->flags |= F_CONNECTION_KEEP_ALIVE; - break; - case h_connection_close: - parser->flags |= F_CONNECTION_CLOSE; - break; - case h_connection_upgrade: - parser->flags |= F_CONNECTION_UPGRADE; - break; - case h_transfer_encoding_chunked: - parser->flags |= F_CHUNKED; - break; - default: - break; - } - - /* header value was empty */ - MARK(header_value); - UPDATE_STATE(s_header_field_start); - CALLBACK_DATA_NOADVANCE(header_value); - REEXECUTE(); - } - } - - case s_headers_almost_done: - { - STRICT_CHECK(ch != LF); - - if (parser->flags & F_TRAILING) { - /* End of a chunked request */ - UPDATE_STATE(NEW_MESSAGE()); - CALLBACK_NOTIFY(message_complete); - break; - } - - UPDATE_STATE(s_headers_done); - - /* Set this here so that on_headers_complete() callbacks can see it */ - parser->upgrade = - ((parser->flags & (F_UPGRADE | F_CONNECTION_UPGRADE)) == - (F_UPGRADE | F_CONNECTION_UPGRADE) || - parser->method == HTTP_CONNECT); - - /* Here we call the headers_complete callback. This is somewhat - * different than other callbacks because if the user returns 1, we - * will interpret that as saying that this message has no body. This - * is needed for the annoying case of recieving a response to a HEAD - * request. - * - * We'd like to use CALLBACK_NOTIFY_NOADVANCE() here but we cannot, so - * we have to simulate it by handling a change in errno below. - */ - if (settings->on_headers_complete) { - switch (settings->on_headers_complete(parser)) { - case 0: - break; - - case 1: - parser->flags |= F_SKIPBODY; - break; - - default: - SET_ERRNO(HPE_CB_headers_complete); - RETURN(p - data); /* Error */ - } - } - - if (HTTP_PARSER_ERRNO(parser) != HPE_OK) { - RETURN(p - data); - } - - REEXECUTE(); - } - - case s_headers_done: - { - STRICT_CHECK(ch != LF); - - parser->nread = 0; - - /* Exit, the rest of the connect is in a different protocol. */ - if (parser->upgrade) { - UPDATE_STATE(NEW_MESSAGE()); - CALLBACK_NOTIFY(message_complete); - RETURN((p - data) + 1); - } - - if (parser->flags & F_SKIPBODY) { - UPDATE_STATE(NEW_MESSAGE()); - CALLBACK_NOTIFY(message_complete); - } else if (parser->flags & F_CHUNKED) { - /* chunked encoding - ignore Content-Length header */ - UPDATE_STATE(s_chunk_size_start); - } else { - if (parser->content_length == 0) { - /* Content-Length header given but zero: Content-Length: 0\r\n */ - UPDATE_STATE(NEW_MESSAGE()); - CALLBACK_NOTIFY(message_complete); - } else if (parser->content_length != ULLONG_MAX) { - /* Content-Length header given and non-zero */ - UPDATE_STATE(s_body_identity); - } else { - if (parser->type == HTTP_REQUEST || - !http_message_needs_eof(parser)) { - /* Assume content-length 0 - read the next */ - UPDATE_STATE(NEW_MESSAGE()); - CALLBACK_NOTIFY(message_complete); - } else { - /* Read body until EOF */ - UPDATE_STATE(s_body_identity_eof); - } - } - } - - break; - } - - case s_body_identity: - { - uint64_t to_read = MIN(parser->content_length, - (uint64_t) ((data + len) - p)); - - assert(parser->content_length != 0 - && parser->content_length != ULLONG_MAX); - - /* The difference between advancing content_length and p is because - * the latter will automaticaly advance on the next loop iteration. - * Further, if content_length ends up at 0, we want to see the last - * byte again for our message complete callback. - */ - MARK(body); - parser->content_length -= to_read; - p += to_read - 1; - - if (parser->content_length == 0) { - UPDATE_STATE(s_message_done); - - /* Mimic CALLBACK_DATA_NOADVANCE() but with one extra byte. - * - * The alternative to doing this is to wait for the next byte to - * trigger the data callback, just as in every other case. The - * problem with this is that this makes it difficult for the test - * harness to distinguish between complete-on-EOF and - * complete-on-length. It's not clear that this distinction is - * important for applications, but let's keep it for now. - */ - CALLBACK_DATA_(body, p - body_mark + 1, p - data); - REEXECUTE(); - } - - break; - } - - /* read until EOF */ - case s_body_identity_eof: - MARK(body); - p = data + len - 1; - - break; - - case s_message_done: - UPDATE_STATE(NEW_MESSAGE()); - CALLBACK_NOTIFY(message_complete); - break; - - case s_chunk_size_start: - { - assert(parser->nread == 1); - assert(parser->flags & F_CHUNKED); - - unhex_val = unhex[(unsigned char)ch]; - if (UNLIKELY(unhex_val == -1)) { - SET_ERRNO(HPE_INVALID_CHUNK_SIZE); - goto error; - } - - parser->content_length = unhex_val; - UPDATE_STATE(s_chunk_size); - break; - } - - case s_chunk_size: - { - uint64_t t; - - assert(parser->flags & F_CHUNKED); - - if (ch == CR) { - UPDATE_STATE(s_chunk_size_almost_done); - break; - } - - unhex_val = unhex[(unsigned char)ch]; - - if (unhex_val == -1) { - if (ch == ';' || ch == ' ') { - UPDATE_STATE(s_chunk_parameters); - break; - } - - SET_ERRNO(HPE_INVALID_CHUNK_SIZE); - goto error; - } - - t = parser->content_length; - t *= 16; - t += unhex_val; - - /* Overflow? Test against a conservative limit for simplicity. */ - if (UNLIKELY((ULLONG_MAX - 16) / 16 < parser->content_length)) { - SET_ERRNO(HPE_INVALID_CONTENT_LENGTH); - goto error; - } - - parser->content_length = t; - break; - } - - case s_chunk_parameters: - { - assert(parser->flags & F_CHUNKED); - /* just ignore this shit. TODO check for overflow */ - if (ch == CR) { - UPDATE_STATE(s_chunk_size_almost_done); - break; - } - break; - } - - case s_chunk_size_almost_done: - { - assert(parser->flags & F_CHUNKED); - STRICT_CHECK(ch != LF); - - parser->nread = 0; - - if (parser->content_length == 0) { - parser->flags |= F_TRAILING; - UPDATE_STATE(s_header_field_start); - } else { - UPDATE_STATE(s_chunk_data); - } - break; - } - - case s_chunk_data: - { - uint64_t to_read = MIN(parser->content_length, - (uint64_t) ((data + len) - p)); - - assert(parser->flags & F_CHUNKED); - assert(parser->content_length != 0 - && parser->content_length != ULLONG_MAX); - - /* See the explanation in s_body_identity for why the content - * length and data pointers are managed this way. - */ - MARK(body); - parser->content_length -= to_read; - p += to_read - 1; - - if (parser->content_length == 0) { - UPDATE_STATE(s_chunk_data_almost_done); - } - - break; - } - - case s_chunk_data_almost_done: - assert(parser->flags & F_CHUNKED); - assert(parser->content_length == 0); - STRICT_CHECK(ch != CR); - UPDATE_STATE(s_chunk_data_done); - CALLBACK_DATA(body); - break; - - case s_chunk_data_done: - assert(parser->flags & F_CHUNKED); - STRICT_CHECK(ch != LF); - parser->nread = 0; - UPDATE_STATE(s_chunk_size_start); - break; - - default: - assert(0 && "unhandled state"); - SET_ERRNO(HPE_INVALID_INTERNAL_STATE); - goto error; - } - } - - /* Run callbacks for any marks that we have leftover after we ran our of - * bytes. There should be at most one of these set, so it's OK to invoke - * them in series (unset marks will not result in callbacks). - * - * We use the NOADVANCE() variety of callbacks here because 'p' has already - * overflowed 'data' and this allows us to correct for the off-by-one that - * we'd otherwise have (since CALLBACK_DATA() is meant to be run with a 'p' - * value that's in-bounds). - */ - - assert(((header_field_mark ? 1 : 0) + - (header_value_mark ? 1 : 0) + - (url_mark ? 1 : 0) + - (body_mark ? 1 : 0) + - (status_mark ? 1 : 0)) <= 1); - - CALLBACK_DATA_NOADVANCE(header_field); - CALLBACK_DATA_NOADVANCE(header_value); - CALLBACK_DATA_NOADVANCE(url); - CALLBACK_DATA_NOADVANCE(body); - CALLBACK_DATA_NOADVANCE(status); - - RETURN(len); - -error: - if (HTTP_PARSER_ERRNO(parser) == HPE_OK) { - SET_ERRNO(HPE_UNKNOWN); - } - - RETURN(p - data); -} - - -/* Does the parser need to see an EOF to find the end of the message? */ -int -http_message_needs_eof (const http_parser *parser) -{ - if (parser->type == HTTP_REQUEST) { - return 0; - } - - /* See RFC 2616 section 4.4 */ - if (parser->status_code / 100 == 1 || /* 1xx e.g. Continue */ - parser->status_code == 204 || /* No Content */ - parser->status_code == 304 || /* Not Modified */ - parser->flags & F_SKIPBODY) { /* response to a HEAD request */ - return 0; - } - - if ((parser->flags & F_CHUNKED) || parser->content_length != ULLONG_MAX) { - return 0; - } - - return 1; -} - - -int -http_should_keep_alive (const http_parser *parser) -{ - if (parser->http_major > 0 && parser->http_minor > 0) { - /* HTTP/1.1 */ - if (parser->flags & F_CONNECTION_CLOSE) { - return 0; - } - } else { - /* HTTP/1.0 or earlier */ - if (!(parser->flags & F_CONNECTION_KEEP_ALIVE)) { - return 0; - } - } - - return !http_message_needs_eof(parser); -} - - -const char * -http_method_str (enum http_method m) -{ - return ELEM_AT(method_strings, m, ""); -} - - -void -http_parser_init (http_parser *parser, enum http_parser_type t) -{ - void *data = parser->data; /* preserve application data */ - memset(parser, 0, sizeof(*parser)); - parser->data = data; - parser->type = t; - parser->state = (t == HTTP_REQUEST ? s_start_req : (t == HTTP_RESPONSE ? s_start_res : s_start_req_or_res)); - parser->http_errno = HPE_OK; -} - -void -http_parser_settings_init(http_parser_settings *settings) -{ - memset(settings, 0, sizeof(*settings)); -} - -const char * -http_errno_name(enum http_errno err) { - assert(err < (sizeof(http_strerror_tab)/sizeof(http_strerror_tab[0]))); - return http_strerror_tab[err].name; -} - -const char * -http_errno_description(enum http_errno err) { - assert(err < (sizeof(http_strerror_tab)/sizeof(http_strerror_tab[0]))); - return http_strerror_tab[err].description; -} - -static enum http_host_state -http_parse_host_char(enum http_host_state s, const char ch) { - switch(s) { - case s_http_userinfo: - case s_http_userinfo_start: - if (ch == '@') { - return s_http_host_start; - } - - if (IS_USERINFO_CHAR(ch)) { - return s_http_userinfo; - } - break; - - case s_http_host_start: - if (ch == '[') { - return s_http_host_v6_start; - } - - if (IS_HOST_CHAR(ch)) { - return s_http_host; - } - - break; - - case s_http_host: - if (IS_HOST_CHAR(ch)) { - return s_http_host; - } - - /* FALLTHROUGH */ - case s_http_host_v6_end: - if (ch == ':') { - return s_http_host_port_start; - } - - break; - - case s_http_host_v6: - if (ch == ']') { - return s_http_host_v6_end; - } - - /* FALLTHROUGH */ - case s_http_host_v6_start: - if (IS_HEX(ch) || ch == ':' || ch == '.') { - return s_http_host_v6; - } - - break; - - case s_http_host_port: - case s_http_host_port_start: - if (IS_NUM(ch)) { - return s_http_host_port; - } - - break; - - default: - break; - } - return s_http_host_dead; -} - -static int -http_parse_host(const char * buf, struct http_parser_url *u, int found_at) { - enum http_host_state s; - - const char *p; - size_t buflen = u->field_data[UF_HOST].off + u->field_data[UF_HOST].len; - - u->field_data[UF_HOST].len = 0; - - s = found_at ? s_http_userinfo_start : s_http_host_start; - - for (p = buf + u->field_data[UF_HOST].off; p < buf + buflen; p++) { - enum http_host_state new_s = http_parse_host_char(s, *p); - - if (new_s == s_http_host_dead) { - return 1; - } - - switch(new_s) { - case s_http_host: - if (s != s_http_host) { - u->field_data[UF_HOST].off = p - buf; - } - u->field_data[UF_HOST].len++; - break; - - case s_http_host_v6: - if (s != s_http_host_v6) { - u->field_data[UF_HOST].off = p - buf; - } - u->field_data[UF_HOST].len++; - break; - - case s_http_host_port: - if (s != s_http_host_port) { - u->field_data[UF_PORT].off = p - buf; - u->field_data[UF_PORT].len = 0; - u->field_set |= (1 << UF_PORT); - } - u->field_data[UF_PORT].len++; - break; - - case s_http_userinfo: - if (s != s_http_userinfo) { - u->field_data[UF_USERINFO].off = p - buf ; - u->field_data[UF_USERINFO].len = 0; - u->field_set |= (1 << UF_USERINFO); - } - u->field_data[UF_USERINFO].len++; - break; - - default: - break; - } - s = new_s; - } - - /* Make sure we don't end somewhere unexpected */ - switch (s) { - case s_http_host_start: - case s_http_host_v6_start: - case s_http_host_v6: - case s_http_host_port_start: - case s_http_userinfo: - case s_http_userinfo_start: - return 1; - default: - break; - } - - return 0; -} - -int -http_parser_parse_url(const char *buf, size_t buflen, int is_connect, - struct http_parser_url *u) -{ - enum state s; - const char *p; - enum http_parser_url_fields uf, old_uf; - int found_at = 0; - - u->port = u->field_set = 0; - s = is_connect ? s_req_server_start : s_req_spaces_before_url; - old_uf = UF_MAX; - - for (p = buf; p < buf + buflen; p++) { - s = parse_url_char(s, *p); - - /* Figure out the next field that we're operating on */ - switch (s) { - case s_dead: - return 1; - - /* Skip delimeters */ - case s_req_schema_slash: - case s_req_schema_slash_slash: - case s_req_server_start: - case s_req_query_string_start: - case s_req_fragment_start: - continue; - - case s_req_schema: - uf = UF_SCHEMA; - break; - - case s_req_server_with_at: - found_at = 1; - - /* FALLTROUGH */ - case s_req_server: - uf = UF_HOST; - break; - - case s_req_path: - uf = UF_PATH; - break; - - case s_req_query_string: - uf = UF_QUERY; - break; - - case s_req_fragment: - uf = UF_FRAGMENT; - break; - - default: - assert(!"Unexpected state"); - return 1; - } - - /* Nothing's changed; soldier on */ - if (uf == old_uf) { - u->field_data[uf].len++; - continue; - } - - u->field_data[uf].off = p - buf; - u->field_data[uf].len = 1; - - u->field_set |= (1 << uf); - old_uf = uf; - } - - /* host must be present if there is a schema */ - /* parsing http:///toto will fail */ - if ((u->field_set & ((1 << UF_SCHEMA) | (1 << UF_HOST))) != 0) { - if (http_parse_host(buf, u, found_at) != 0) { - return 1; - } - } - - /* CONNECT requests can only contain "hostname:port" */ - if (is_connect && u->field_set != ((1 << UF_HOST)|(1 << UF_PORT))) { - return 1; - } - - if (u->field_set & (1 << UF_PORT)) { - /* Don't bother with endp; we've already validated the string */ - unsigned long v = strtoul(buf + u->field_data[UF_PORT].off, NULL, 10); - - /* Ports have a max value of 2^16 */ - if (v > 0xffff) { - return 1; - } - - u->port = (uint16_t) v; - } - - return 0; -} - -void -http_parser_pause(http_parser *parser, int paused) { - /* Users should only be pausing/unpausing a parser that is not in an error - * state. In non-debug builds, there's not much that we can do about this - * other than ignore it. - */ - if (HTTP_PARSER_ERRNO(parser) == HPE_OK || - HTTP_PARSER_ERRNO(parser) == HPE_PAUSED) { - SET_ERRNO((paused) ? HPE_PAUSED : HPE_OK); - } else { - assert(0 && "Attempting to pause parser in error state"); - } -} - -int -http_body_is_final(const struct http_parser *parser) { - return parser->state == s_message_done; -} - -unsigned long -http_parser_version(void) { - return HTTP_PARSER_VERSION_MAJOR * 0x10000 | - HTTP_PARSER_VERSION_MINOR * 0x00100 | - HTTP_PARSER_VERSION_PATCH * 0x00001; -} diff --git a/third-party/http-parser/http_parser.h b/third-party/http-parser/http_parser.h deleted file mode 100644 index 99c533a..0000000 --- a/third-party/http-parser/http_parser.h +++ /dev/null @@ -1,335 +0,0 @@ -/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS - * IN THE SOFTWARE. - */ -#ifndef http_parser_h -#define http_parser_h -#ifdef __cplusplus -extern "C" { -#endif - -/* Also update SONAME in the Makefile whenever you change these. */ -#define HTTP_PARSER_VERSION_MAJOR 2 -#define HTTP_PARSER_VERSION_MINOR 4 -#define HTTP_PARSER_VERSION_PATCH 2 - -#include -#if defined(_WIN32) && !defined(__MINGW32__) && (!defined(_MSC_VER) || _MSC_VER<1600) -#include -#include -typedef __int8 int8_t; -typedef unsigned __int8 uint8_t; -typedef __int16 int16_t; -typedef unsigned __int16 uint16_t; -typedef __int32 int32_t; -typedef unsigned __int32 uint32_t; -typedef __int64 int64_t; -typedef unsigned __int64 uint64_t; -#else -#include -#endif - -/* Compile with -DHTTP_PARSER_STRICT=0 to make less checks, but run - * faster - */ -#ifndef HTTP_PARSER_STRICT -# define HTTP_PARSER_STRICT 1 -#endif - -/* Maximium header size allowed. If the macro is not defined - * before including this header then the default is used. To - * change the maximum header size, define the macro in the build - * environment (e.g. -DHTTP_MAX_HEADER_SIZE=). To remove - * the effective limit on the size of the header, define the macro - * to a very large number (e.g. -DHTTP_MAX_HEADER_SIZE=0x7fffffff) - */ -#ifndef HTTP_MAX_HEADER_SIZE -# define HTTP_MAX_HEADER_SIZE (80*1024) -#endif - -typedef struct http_parser http_parser; -typedef struct http_parser_settings http_parser_settings; - - -/* Callbacks should return non-zero to indicate an error. The parser will - * then halt execution. - * - * The one exception is on_headers_complete. In a HTTP_RESPONSE parser - * returning '1' from on_headers_complete will tell the parser that it - * should not expect a body. This is used when receiving a response to a - * HEAD request which may contain 'Content-Length' or 'Transfer-Encoding: - * chunked' headers that indicate the presence of a body. - * - * http_data_cb does not return data chunks. It will be called arbitrarily - * many times for each string. E.G. you might get 10 callbacks for "on_url" - * each providing just a few characters more data. - */ -typedef int (*http_data_cb) (http_parser*, const char *at, size_t length); -typedef int (*http_cb) (http_parser*); - - -/* Request Methods */ -#define HTTP_METHOD_MAP(XX) \ - XX(0, DELETE, DELETE) \ - XX(1, GET, GET) \ - XX(2, HEAD, HEAD) \ - XX(3, POST, POST) \ - XX(4, PUT, PUT) \ - /* pathological */ \ - XX(5, CONNECT, CONNECT) \ - XX(6, OPTIONS, OPTIONS) \ - XX(7, TRACE, TRACE) \ - /* webdav */ \ - XX(8, COPY, COPY) \ - XX(9, LOCK, LOCK) \ - XX(10, MKCOL, MKCOL) \ - XX(11, MOVE, MOVE) \ - XX(12, PROPFIND, PROPFIND) \ - XX(13, PROPPATCH, PROPPATCH) \ - XX(14, SEARCH, SEARCH) \ - XX(15, UNLOCK, UNLOCK) \ - /* subversion */ \ - XX(16, REPORT, REPORT) \ - XX(17, MKACTIVITY, MKACTIVITY) \ - XX(18, CHECKOUT, CHECKOUT) \ - XX(19, MERGE, MERGE) \ - /* upnp */ \ - XX(20, MSEARCH, M-SEARCH) \ - XX(21, NOTIFY, NOTIFY) \ - XX(22, SUBSCRIBE, SUBSCRIBE) \ - XX(23, UNSUBSCRIBE, UNSUBSCRIBE) \ - /* RFC-5789 */ \ - XX(24, PATCH, PATCH) \ - XX(25, PURGE, PURGE) \ - /* CalDAV */ \ - XX(26, MKCALENDAR, MKCALENDAR) \ - -enum http_method - { -#define XX(num, name, string) HTTP_##name = num, - HTTP_METHOD_MAP(XX) -#undef XX - }; - - -enum http_parser_type { HTTP_REQUEST, HTTP_RESPONSE, HTTP_BOTH }; - - -/* Flag values for http_parser.flags field */ -enum flags - { F_CHUNKED = 1 << 0 - , F_CONNECTION_KEEP_ALIVE = 1 << 1 - , F_CONNECTION_CLOSE = 1 << 2 - , F_CONNECTION_UPGRADE = 1 << 3 - , F_TRAILING = 1 << 4 - , F_UPGRADE = 1 << 5 - , F_SKIPBODY = 1 << 6 - }; - - -/* Map for errno-related constants - * - * The provided argument should be a macro that takes 2 arguments. - */ -#define HTTP_ERRNO_MAP(XX) \ - /* No error */ \ - XX(OK, "success") \ - \ - /* Callback-related errors */ \ - XX(CB_message_begin, "the on_message_begin callback failed") \ - XX(CB_url, "the on_url callback failed") \ - XX(CB_header_field, "the on_header_field callback failed") \ - XX(CB_header_value, "the on_header_value callback failed") \ - XX(CB_headers_complete, "the on_headers_complete callback failed") \ - XX(CB_body, "the on_body callback failed") \ - XX(CB_message_complete, "the on_message_complete callback failed") \ - XX(CB_status, "the on_status callback failed") \ - \ - /* Parsing-related errors */ \ - XX(INVALID_EOF_STATE, "stream ended at an unexpected time") \ - XX(HEADER_OVERFLOW, \ - "too many header bytes seen; overflow detected") \ - XX(CLOSED_CONNECTION, \ - "data received after completed connection: close message") \ - XX(INVALID_VERSION, "invalid HTTP version") \ - XX(INVALID_STATUS, "invalid HTTP status code") \ - XX(INVALID_METHOD, "invalid HTTP method") \ - XX(INVALID_URL, "invalid URL") \ - XX(INVALID_HOST, "invalid host") \ - XX(INVALID_PORT, "invalid port") \ - XX(INVALID_PATH, "invalid path") \ - XX(INVALID_QUERY_STRING, "invalid query string") \ - XX(INVALID_FRAGMENT, "invalid fragment") \ - XX(LF_EXPECTED, "LF character expected") \ - XX(INVALID_HEADER_TOKEN, "invalid character in header") \ - XX(INVALID_CONTENT_LENGTH, \ - "invalid character in content-length header") \ - XX(INVALID_CHUNK_SIZE, \ - "invalid character in chunk size header") \ - XX(INVALID_CONSTANT, "invalid constant string") \ - XX(INVALID_INTERNAL_STATE, "encountered unexpected internal state")\ - XX(STRICT, "strict mode assertion failed") \ - XX(PAUSED, "parser is paused") \ - XX(UNKNOWN, "an unknown error occurred") - - -/* Define HPE_* values for each errno value above */ -#define HTTP_ERRNO_GEN(n, s) HPE_##n, -enum http_errno { - HTTP_ERRNO_MAP(HTTP_ERRNO_GEN) -}; -#undef HTTP_ERRNO_GEN - - -/* Get an http_errno value from an http_parser */ -#define HTTP_PARSER_ERRNO(p) ((enum http_errno) (p)->http_errno) - - -struct http_parser { - /** PRIVATE **/ - unsigned int type : 2; /* enum http_parser_type */ - unsigned int flags : 7; /* F_* values from 'flags' enum; semi-public */ - unsigned int state : 7; /* enum state from http_parser.c */ - unsigned int header_state : 8; /* enum header_state from http_parser.c */ - unsigned int index : 8; /* index into current matcher */ - - uint32_t nread; /* # bytes read in various scenarios */ - uint64_t content_length; /* # bytes in body (0 if no Content-Length header) */ - - /** READ-ONLY **/ - unsigned short http_major; - unsigned short http_minor; - unsigned int status_code : 16; /* responses only */ - unsigned int method : 8; /* requests only */ - unsigned int http_errno : 7; - - /* 1 = Upgrade header was present and the parser has exited because of that. - * 0 = No upgrade header present. - * Should be checked when http_parser_execute() returns in addition to - * error checking. - */ - unsigned int upgrade : 1; - - /** PUBLIC **/ - void *data; /* A pointer to get hook to the "connection" or "socket" object */ -}; - - -struct http_parser_settings { - http_cb on_message_begin; - http_data_cb on_url; - http_data_cb on_status; - http_data_cb on_header_field; - http_data_cb on_header_value; - http_cb on_headers_complete; - http_data_cb on_body; - http_cb on_message_complete; -}; - - -enum http_parser_url_fields - { UF_SCHEMA = 0 - , UF_HOST = 1 - , UF_PORT = 2 - , UF_PATH = 3 - , UF_QUERY = 4 - , UF_FRAGMENT = 5 - , UF_USERINFO = 6 - , UF_MAX = 7 - }; - - -/* Result structure for http_parser_parse_url(). - * - * Callers should index into field_data[] with UF_* values iff field_set - * has the relevant (1 << UF_*) bit set. As a courtesy to clients (and - * because we probably have padding left over), we convert any port to - * a uint16_t. - */ -struct http_parser_url { - uint16_t field_set; /* Bitmask of (1 << UF_*) values */ - uint16_t port; /* Converted UF_PORT string */ - - struct { - uint16_t off; /* Offset into buffer in which field starts */ - uint16_t len; /* Length of run in buffer */ - } field_data[UF_MAX]; -}; - - -/* Returns the library version. Bits 16-23 contain the major version number, - * bits 8-15 the minor version number and bits 0-7 the patch level. - * Usage example: - * - * unsigned long version = http_parser_version(); - * unsigned major = (version >> 16) & 255; - * unsigned minor = (version >> 8) & 255; - * unsigned patch = version & 255; - * printf("http_parser v%u.%u.%u\n", major, minor, patch); - */ -unsigned long http_parser_version(void); - -void http_parser_init(http_parser *parser, enum http_parser_type type); - - -/* Initialize http_parser_settings members to 0 - */ -void http_parser_settings_init(http_parser_settings *settings); - - -/* Executes the parser. Returns number of parsed bytes. Sets - * `parser->http_errno` on error. */ -size_t http_parser_execute(http_parser *parser, - const http_parser_settings *settings, - const char *data, - size_t len); - - -/* If http_should_keep_alive() in the on_headers_complete or - * on_message_complete callback returns 0, then this should be - * the last message on the connection. - * If you are the server, respond with the "Connection: close" header. - * If you are the client, close the connection. - */ -int http_should_keep_alive(const http_parser *parser); - -/* Returns a string version of the HTTP method. */ -const char *http_method_str(enum http_method m); - -/* Return a string name of the given error */ -const char *http_errno_name(enum http_errno err); - -/* Return a string description of the given error */ -const char *http_errno_description(enum http_errno err); - -/* Parse a URL; return nonzero on failure */ -int http_parser_parse_url(const char *buf, size_t buflen, - int is_connect, - struct http_parser_url *u); - -/* Pause or un-pause the parser; a nonzero value pauses */ -void http_parser_pause(http_parser *parser, int paused); - -/* Checks if this is the final chunk of the body. */ -int http_body_is_final(const http_parser *parser); - -#ifdef __cplusplus -} -#endif -#endif -- 2.7.4 From 3040884754c55bc1a872943192c45ba52c03b953 Mon Sep 17 00:00:00 2001 From: "chleun.moon" Date: Tue, 23 Aug 2016 18:16:32 +0900 Subject: [PATCH 4/4] Imported Upstream version 1.0.0 Change-Id: I57bd2e1d1b47075c9eead2020986dbca09fb8416 Signed-off-by: cheoleun --- AUTHORS | 1 + COPYING | 22 + ChangeLog | 259 + Dockerfile.android | 109 + INSTALL | 370 + Makefile.am | 43 + Makefile.in | 964 + NEWS | 0 README | 1 + README.rst | 1427 ++ aclocal.m4 | 1613 ++ android-config | 47 + android-make | 33 + compile | 347 + config.guess | 1420 ++ config.h.in | 379 + config.sub | 1799 ++ configure | 25483 +++++++++++++++++++ configure.ac | 758 + contrib/Makefile.am | 44 + contrib/Makefile.in | 531 + contrib/nghttpx-init.in | 173 + contrib/nghttpx-logrotate | 11 + contrib/nghttpx-upstart.conf.in | 8 + contrib/nghttpx.service.in | 10 + depcomp | 791 + doc/Makefile.am | 320 + doc/Makefile.in | 939 + doc/README.rst | 160 + doc/_themes/sphinx_rtd_theme/__init__.py | 17 + doc/_themes/sphinx_rtd_theme/breadcrumbs.html | 23 + doc/_themes/sphinx_rtd_theme/footer.html | 36 + doc/_themes/sphinx_rtd_theme/layout.html | 181 + doc/_themes/sphinx_rtd_theme/layout_old.html | 205 + doc/_themes/sphinx_rtd_theme/search.html | 50 + doc/_themes/sphinx_rtd_theme/searchbox.html | 9 + .../sphinx_rtd_theme/static/css/badge_only.css | 2 + doc/_themes/sphinx_rtd_theme/static/css/theme.css | 5 + .../sphinx_rtd_theme/static/fonts/FontAwesome.otf | Bin 0 -> 62856 bytes .../static/fonts/fontawesome-webfont.eot | Bin 0 -> 38205 bytes .../static/fonts/fontawesome-webfont.svg | 414 + .../static/fonts/fontawesome-webfont.ttf | Bin 0 -> 80652 bytes .../static/fonts/fontawesome-webfont.woff | Bin 0 -> 44432 bytes doc/_themes/sphinx_rtd_theme/static/js/theme.js | 113 + doc/_themes/sphinx_rtd_theme/theme.conf | 9 + doc/_themes/sphinx_rtd_theme/versions.html | 37 + doc/apiref.rst | 104 + doc/asio_http2.h.rst.in | 5 + doc/asio_http2_client.h.rst.in | 5 + doc/asio_http2_server.h.rst.in | 5 + doc/bash_completion/h2load | 19 + doc/bash_completion/nghttp | 19 + doc/bash_completion/nghttpd | 19 + doc/bash_completion/nghttpx | 19 + doc/building-android-binary.rst.in | 1 + doc/conf.py.in | 253 + doc/contribute.rst.in | 1 + doc/enums.rst | 446 + doc/h2load-howto.rst.in | 1 + doc/h2load.1 | 273 + doc/h2load.1.rst | 204 + doc/index.rst.in | 1 + doc/libnghttp2_asio.rst.in | 1 + doc/macros.rst | 86 + doc/mkapiref.py | 277 + doc/nghttp.1 | 289 + doc/nghttp.1.rst | 233 + doc/nghttp2.h.rst.in | 4 + doc/nghttp2_check_header_name.rst | 17 + doc/nghttp2_check_header_value.rst | 15 + doc/nghttp2_hd_deflate_bound.rst | 14 + doc/nghttp2_hd_deflate_change_table_size.rst | 31 + doc/nghttp2_hd_deflate_del.rst | 13 + doc/nghttp2_hd_deflate_hd.rst | 35 + doc/nghttp2_hd_deflate_new.rst | 24 + doc/nghttp2_hd_deflate_new2.rst | 23 + doc/nghttp2_hd_inflate_change_table_size.rst | 23 + doc/nghttp2_hd_inflate_del.rst | 13 + doc/nghttp2_hd_inflate_end_headers.rst | 16 + doc/nghttp2_hd_inflate_hd.rst | 85 + doc/nghttp2_hd_inflate_new.rst | 21 + doc/nghttp2_hd_inflate_new2.rst | 23 + doc/nghttp2_is_fatal.rst | 14 + doc/nghttp2_nv_compare_name.rst | 17 + doc/nghttp2_option_del.rst | 14 + doc/nghttp2_option_new.rst | 22 + doc/nghttp2_option_set_no_auto_window_update.rst | 18 + doc/nghttp2_option_set_no_http_messaging.rst | 18 + doc/nghttp2_option_set_no_recv_client_magic.rst | 26 + ...ttp2_option_set_peer_max_concurrent_streams.rst | 23 + doc/nghttp2_pack_settings_payload.rst | 29 + doc/nghttp2_priority_spec_check_default.rst | 13 + doc/nghttp2_priority_spec_default_init.rst | 15 + doc/nghttp2_priority_spec_init.rst | 18 + doc/nghttp2_select_next_protocol.rst | 66 + doc/nghttp2_session_callbacks_del.rst | 14 + doc/nghttp2_session_callbacks_new.rst | 25 + ...on_callbacks_set_before_frame_send_callback.rst | 13 + ...lbacks_set_data_source_read_length_callback.rst | 14 + ...ssion_callbacks_set_on_begin_frame_callback.rst | 13 + ...ion_callbacks_set_on_begin_headers_callback.rst | 14 + ...n_callbacks_set_on_data_chunk_recv_callback.rst | 14 + ...on_callbacks_set_on_frame_not_send_callback.rst | 14 + ...ession_callbacks_set_on_frame_recv_callback.rst | 14 + ...ession_callbacks_set_on_frame_send_callback.rst | 13 + ...p2_session_callbacks_set_on_header_callback.rst | 14 + ...allbacks_set_on_invalid_frame_recv_callback.rst | 15 + ...sion_callbacks_set_on_stream_close_callback.rst | 13 + ...nghttp2_session_callbacks_set_recv_callback.rst | 16 + ...ssion_callbacks_set_select_padding_callback.rst | 15 + ...nghttp2_session_callbacks_set_send_callback.rst | 16 + ...p2_session_callbacks_set_send_data_callback.rst | 15 + doc/nghttp2_session_client_new.rst | 29 + doc/nghttp2_session_client_new2.rst | 29 + doc/nghttp2_session_client_new3.rst | 29 + doc/nghttp2_session_consume.rst | 31 + doc/nghttp2_session_consume_connection.rst | 24 + doc/nghttp2_session_consume_stream.rst | 26 + doc/nghttp2_session_del.rst | 14 + ...tp2_session_get_effective_local_window_size.rst | 18 + ...ttp2_session_get_effective_recv_data_length.rst | 22 + doc/nghttp2_session_get_last_proc_stream_id.rst | 19 + doc/nghttp2_session_get_next_stream_id.rst | 15 + doc/nghttp2_session_get_outbound_queue_size.rst | 14 + doc/nghttp2_session_get_remote_settings.rst | 15 + doc/nghttp2_session_get_remote_window_size.rst | 15 + ...sion_get_stream_effective_local_window_size.rst | 18 + ...ssion_get_stream_effective_recv_data_length.rst | 22 + doc/nghttp2_session_get_stream_local_close.rst | 14 + doc/nghttp2_session_get_stream_remote_close.rst | 14 + ...http2_session_get_stream_remote_window_size.rst | 22 + doc/nghttp2_session_get_stream_user_data.rst | 20 + doc/nghttp2_session_mem_recv.rst | 42 + doc/nghttp2_session_mem_send.rst | 38 + doc/nghttp2_session_recv.rst | 72 + doc/nghttp2_session_resume_data.rst | 22 + doc/nghttp2_session_send.rst | 55 + doc/nghttp2_session_server_new.rst | 29 + doc/nghttp2_session_server_new2.rst | 29 + doc/nghttp2_session_server_new3.rst | 29 + doc/nghttp2_session_set_next_stream_id.rst | 24 + doc/nghttp2_session_set_stream_user_data.rst | 26 + doc/nghttp2_session_terminate_session.rst | 34 + doc/nghttp2_session_terminate_session2.rst | 34 + doc/nghttp2_session_upgrade.rst | 44 + doc/nghttp2_session_want_read.rst | 18 + doc/nghttp2_session_want_write.rst | 18 + doc/nghttp2_strerror.rst | 14 + doc/nghttp2_submit_data.rst | 42 + doc/nghttp2_submit_goaway.rst | 53 + doc/nghttp2_submit_headers.rst | 80 + doc/nghttp2_submit_ping.rst | 29 + doc/nghttp2_submit_priority.rst | 37 + doc/nghttp2_submit_push_promise.rst | 65 + doc/nghttp2_submit_request.rst | 68 + doc/nghttp2_submit_response.rst | 57 + doc/nghttp2_submit_rst_stream.rst | 27 + doc/nghttp2_submit_settings.rst | 41 + doc/nghttp2_submit_shutdown_notice.rst | 43 + doc/nghttp2_submit_trailer.rst | 52 + doc/nghttp2_submit_window_update.rst | 43 + doc/nghttp2_version.rst | 17 + doc/nghttp2ver.h.rst.in | 4 + doc/nghttpd.1 | 194 + doc/nghttpd.1.rst | 151 + doc/nghttpx-howto.rst.in | 1 + doc/nghttpx.1 | 899 + doc/nghttpx.1.rst | 811 + doc/package_README.rst.in | 1 + doc/programmers-guide.rst | 105 + doc/python-apiref.rst.in | 1 + doc/sources/building-android-binary.rst | 135 + doc/sources/contribute.rst | 57 + doc/sources/h2load-howto.rst | 91 + doc/sources/index.rst | 53 + doc/sources/libnghttp2_asio.rst | 433 + doc/sources/nghttpx-howto.rst | 303 + doc/sources/python-apiref.rst | 437 + doc/sources/tutorial-client.rst | 439 + doc/sources/tutorial-hpack.rst | 118 + doc/sources/tutorial-server.rst | 550 + doc/tutorial-client.rst.in | 6 + doc/tutorial-hpack.rst.in | 6 + doc/tutorial-server.rst.in | 6 + doc/types.rst | 914 + examples/Makefile.am | 95 + examples/Makefile.in | 926 + examples/asio-cl.cc | 96 + examples/asio-cl2.cc | 134 + examples/asio-sv.cc | 149 + examples/asio-sv2.cc | 125 + examples/client.c | 703 + examples/deflate.c | 206 + examples/libevent-client.c | 561 + examples/libevent-server.c | 734 + examples/tiny-nghttpd.c | 1342 + install-sh | 527 + integration-tests/Makefile.am | 43 + integration-tests/Makefile.in | 537 + integration-tests/alt-server.crt | 21 + integration-tests/alt-server.key | 28 + integration-tests/config.go.in | 5 + integration-tests/nghttpx_http1_test.go | 506 + integration-tests/nghttpx_http2_test.go | 813 + integration-tests/nghttpx_spdy_test.go | 232 + integration-tests/server.crt | 21 + integration-tests/server.key | 28 + integration-tests/server_tester.go | 683 + integration-tests/setenv | 6 + integration-tests/setenv.in | 6 + lib/Makefile.am | 67 + lib/Makefile.in | 930 + lib/Makefile.msvc | 249 + lib/includes/Makefile.am | 23 + lib/includes/Makefile.in | 636 + lib/includes/nghttp2/nghttp2.h | 3860 +++ lib/includes/nghttp2/nghttp2ver.h | 42 + lib/includes/nghttp2/nghttp2ver.h.in | 42 + lib/libnghttp2.pc.in | 33 + lib/nghttp2_buf.c | 494 + lib/nghttp2_buf.h | 385 + lib/nghttp2_callbacks.c | 129 + lib/nghttp2_callbacks.h | 112 + lib/nghttp2_frame.c | 888 + lib/nghttp2_frame.h | 527 + lib/nghttp2_hd.c | 2343 ++ lib/nghttp2_hd.h | 421 + lib/nghttp2_hd_huffman.c | 227 + lib/nghttp2_hd_huffman.h | 74 + lib/nghttp2_hd_huffman_data.c | 5152 ++++ lib/nghttp2_helper.c | 463 + lib/nghttp2_helper.h | 114 + lib/nghttp2_http.c | 459 + lib/nghttp2_http.h | 98 + lib/nghttp2_int.h | 58 + lib/nghttp2_map.c | 190 + lib/nghttp2_map.h | 144 + lib/nghttp2_mem.c | 61 + lib/nghttp2_mem.h | 44 + lib/nghttp2_net.h | 91 + lib/nghttp2_npn.c | 57 + lib/nghttp2_npn.h | 34 + lib/nghttp2_option.c | 58 + lib/nghttp2_option.h | 91 + lib/nghttp2_outbound_item.c | 105 + lib/nghttp2_outbound_item.h | 157 + lib/nghttp2_pq.c | 146 + lib/nghttp2_pq.h | 121 + lib/nghttp2_priority_spec.c | 44 + lib/nghttp2_priority_spec.h | 34 + lib/nghttp2_queue.c | 85 + lib/nghttp2_queue.h | 49 + lib/nghttp2_session.c | 6612 +++++ lib/nghttp2_session.h | 762 + lib/nghttp2_stream.c | 1010 + lib/nghttp2_stream.h | 486 + lib/nghttp2_submit.c | 479 + lib/nghttp2_submit.h | 34 + lib/nghttp2_version.c | 38 + ltmain.sh | 9661 +++++++ m4/ax_boost_asio.m4 | 110 + m4/ax_boost_base.m4 | 275 + m4/ax_boost_system.m4 | 120 + m4/ax_boost_thread.m4 | 149 + m4/ax_check_compile_flag.m4 | 74 + m4/ax_cxx_compile_stdcxx_11.m4 | 133 + m4/ax_have_epoll.m4 | 104 + m4/ax_python_devel.m4 | 344 + m4/libtool.m4 | 7997 ++++++ m4/libxml2.m4 | 188 + m4/ltoptions.m4 | 384 + m4/ltsugar.m4 | 123 + m4/ltversion.m4 | 23 + m4/lt~obsolete.m4 | 98 + missing | 215 + nghttpx.conf.sample | 29 + proxy.pac.sample | 6 + python/Makefile.am | 48 + python/Makefile.in | 541 + python/cnghttp2.pxd | 345 + python/nghttp2.pyx | 1591 ++ python/setup.py.in | 49 + src/HtmlParser.cc | 187 + src/HtmlParser.h | 94 + src/HttpServer.cc | 1889 ++ src/HttpServer.h | 202 + src/Makefile.am | 216 + src/Makefile.in | 2137 ++ src/app_helper.cc | 505 + src/app_helper.h | 95 + src/asio_client_request.cc | 67 + src/asio_client_request_impl.cc | 110 + src/asio_client_request_impl.h | 93 + src/asio_client_response.cc | 53 + src/asio_client_response_impl.cc | 57 + src/asio_client_response_impl.h | 69 + src/asio_client_session.cc | 98 + src/asio_client_session_impl.cc | 625 + src/asio_client_session_impl.h | 123 + src/asio_client_session_tcp_impl.cc | 69 + src/asio_client_session_tcp_impl.h | 60 + src/asio_client_session_tls_impl.cc | 85 + src/asio_client_session_tls_impl.h | 63 + src/asio_client_stream.cc | 59 + src/asio_client_stream.h | 68 + src/asio_client_tls_context.cc | 64 + src/asio_client_tls_context.h | 32 + src/asio_common.cc | 148 + src/asio_common.h | 65 + src/asio_io_service_pool.cc | 97 + src/asio_io_service_pool.h | 91 + src/asio_server.cc | 165 + src/asio_server.h | 105 + src/asio_server_connection.h | 169 + src/asio_server_http2.cc | 84 + src/asio_server_http2_handler.cc | 462 + src/asio_server_http2_handler.h | 167 + src/asio_server_http2_impl.cc | 66 + src/asio_server_http2_impl.h | 67 + src/asio_server_request.cc | 55 + src/asio_server_request_handler.cc | 84 + src/asio_server_request_handler.h | 32 + src/asio_server_request_impl.cc | 59 + src/asio_server_request_impl.h | 69 + src/asio_server_response.cc | 75 + src/asio_server_response_impl.cc | 163 + src/asio_server_response_impl.h | 92 + src/asio_server_serve_mux.cc | 138 + src/asio_server_serve_mux.h | 64 + src/asio_server_stream.cc | 55 + src/asio_server_stream.h | 59 + src/asio_server_tls_context.cc | 85 + src/asio_server_tls_context.h | 32 + src/base64.h | 170 + src/buffer.h | 68 + src/buffer_test.cc | 78 + src/buffer_test.h | 38 + src/comp_helper.c | 91 + src/comp_helper.h | 47 + src/deflatehd.cc | 450 + src/h2load.cc | 1528 ++ src/h2load.h | 249 + src/h2load_http2_session.cc | 260 + src/h2load_http2_session.h | 53 + src/h2load_session.h | 57 + src/h2load_spdy_session.cc | 270 + src/h2load_spdy_session.h | 57 + src/http2.cc | 1118 + src/http2.h | 296 + src/http2_test.cc | 826 + src/http2_test.h | 51 + src/includes/Makefile.am | 27 + src/includes/Makefile.in | 640 + src/includes/nghttp2/asio_http2.h | 139 + src/includes/nghttp2/asio_http2_client.h | 195 + src/includes/nghttp2/asio_http2_server.h | 229 + src/inflatehd.cc | 277 + src/libnghttp2_asio.pc.in | 33 + src/memchunk.h | 260 + src/memchunk_test.cc | 199 + src/memchunk_test.h | 42 + src/nghttp.cc | 2652 ++ src/nghttp.h | 272 + src/nghttp2_config.h | 38 + src/nghttp2_gzip.c | 92 + src/nghttp2_gzip.h | 122 + src/nghttp2_gzip_test.c | 115 + src/nghttp2_gzip_test.h | 42 + src/nghttpd.cc | 381 + src/shrpx-unittest.cc | 178 + src/shrpx.cc | 2165 ++ src/shrpx.h | 49 + src/shrpx_accept_handler.cc | 114 + src/shrpx_accept_handler.h | 53 + src/shrpx_client_handler.cc | 793 + src/shrpx_client_handler.h | 154 + src/shrpx_config.cc | 1406 + src/shrpx_config.h | 409 + src/shrpx_config_test.cc | 175 + src/shrpx_config_test.h | 41 + src/shrpx_connect_blocker.cc | 65 + src/shrpx_connect_blocker.h | 56 + src/shrpx_connection.cc | 333 + src/shrpx_connection.h | 109 + src/shrpx_connection_handler.cc | 539 + src/shrpx_connection_handler.h | 138 + src/shrpx_downstream.cc | 1218 + src/shrpx_downstream.h | 456 + src/shrpx_downstream_connection.cc | 52 + src/shrpx_downstream_connection.h | 77 + src/shrpx_downstream_connection_pool.cc | 60 + src/shrpx_downstream_connection_pool.h | 52 + src/shrpx_downstream_queue.cc | 167 + src/shrpx_downstream_queue.h | 105 + src/shrpx_downstream_test.cc | 160 + src/shrpx_downstream_test.h | 44 + src/shrpx_error.h | 43 + src/shrpx_http.cc | 103 + src/shrpx_http.h | 53 + src/shrpx_http2_downstream_connection.cc | 569 + src/shrpx_http2_downstream_connection.h | 86 + src/shrpx_http2_session.cc | 1747 ++ src/shrpx_http2_session.h | 219 + src/shrpx_http2_upstream.cc | 1661 ++ src/shrpx_http2_upstream.h | 121 + src/shrpx_http_downstream_connection.cc | 844 + src/shrpx_http_downstream_connection.h | 78 + src/shrpx_https_upstream.cc | 983 + src/shrpx_https_upstream.h | 91 + src/shrpx_io_control.cc | 66 + src/shrpx_io_control.h | 57 + src/shrpx_log.cc | 343 + src/shrpx_log.h | 151 + src/shrpx_log_config.cc | 69 + src/shrpx_log_config.h | 53 + src/shrpx_rate_limit.cc | 109 + src/shrpx_rate_limit.h | 65 + src/shrpx_spdy_upstream.cc | 1103 + src/shrpx_spdy_upstream.h | 94 + src/shrpx_ssl.cc | 1028 + src/shrpx_ssl.h | 177 + src/shrpx_ssl_test.cc | 119 + src/shrpx_ssl_test.h | 39 + src/shrpx_upstream.h | 69 + src/shrpx_worker.cc | 242 + src/shrpx_worker.h | 142 + src/ssl.cc | 83 + src/ssl.h | 45 + src/template.h | 150 + src/timegm.c | 70 + src/timegm.h | 50 + src/util.cc | 1161 + src/util.h | 626 + src/util_test.cc | 370 + src/util_test.h | 59 + test-driver | 139 + tests/Makefile.am | 82 + tests/Makefile.in | 1253 + tests/failmalloc.c | 77 + tests/failmalloc_test.c | 509 + tests/failmalloc_test.h | 33 + tests/main.c | 368 + tests/malloc_wrapper.c | 75 + tests/malloc_wrapper.h | 61 + tests/nghttp2_buf_test.c | 342 + tests/nghttp2_buf_test.h | 38 + tests/nghttp2_frame_test.c | 561 + tests/nghttp2_frame_test.h | 40 + tests/nghttp2_hd_test.c | 1257 + tests/nghttp2_hd_test.h | 47 + tests/nghttp2_helper_test.c | 169 + tests/nghttp2_helper_test.h | 32 + tests/nghttp2_map_test.c | 176 + tests/nghttp2_map_test.h | 32 + tests/nghttp2_npn_test.c | 72 + tests/nghttp2_npn_test.h | 30 + tests/nghttp2_pq_test.c | 123 + tests/nghttp2_pq_test.h | 31 + tests/nghttp2_queue_test.c | 48 + tests/nghttp2_queue_test.h | 30 + tests/nghttp2_session_test.c | 8125 ++++++ tests/nghttp2_session_test.h | 138 + tests/nghttp2_stream_test.c | 29 + tests/nghttp2_stream_test.h | 28 + tests/nghttp2_test_helper.c | 303 + tests/nghttp2_test_helper.h | 112 + tests/testdata/Makefile.am | 23 + tests/testdata/Makefile.in | 512 + tests/testdata/cacert.pem | 14 + tests/testdata/index.html | 1 + tests/testdata/privkey.pem | 9 + third-party/Makefile.am | 32 + third-party/Makefile.in | 751 + third-party/h2o/fetch-ocsp-response | 150 + third-party/http-parser/http_parser.c | 2418 ++ third-party/http-parser/http_parser.h | 335 + 476 files changed, 171766 insertions(+) create mode 100644 AUTHORS create mode 100644 COPYING create mode 100644 ChangeLog create mode 100644 Dockerfile.android create mode 100644 INSTALL create mode 100644 Makefile.am create mode 100644 Makefile.in create mode 100644 NEWS create mode 100644 README create mode 100644 README.rst create mode 100644 aclocal.m4 create mode 100755 android-config create mode 100755 android-make create mode 100755 compile create mode 100755 config.guess create mode 100644 config.h.in create mode 100755 config.sub create mode 100755 configure create mode 100644 configure.ac create mode 100644 contrib/Makefile.am create mode 100644 contrib/Makefile.in create mode 100644 contrib/nghttpx-init.in create mode 100644 contrib/nghttpx-logrotate create mode 100644 contrib/nghttpx-upstart.conf.in create mode 100644 contrib/nghttpx.service.in create mode 100755 depcomp create mode 100644 doc/Makefile.am create mode 100644 doc/Makefile.in create mode 100644 doc/README.rst create mode 100644 doc/_themes/sphinx_rtd_theme/__init__.py create mode 100644 doc/_themes/sphinx_rtd_theme/breadcrumbs.html create mode 100644 doc/_themes/sphinx_rtd_theme/footer.html create mode 100644 doc/_themes/sphinx_rtd_theme/layout.html create mode 100644 doc/_themes/sphinx_rtd_theme/layout_old.html create mode 100644 doc/_themes/sphinx_rtd_theme/search.html create mode 100644 doc/_themes/sphinx_rtd_theme/searchbox.html create mode 100644 doc/_themes/sphinx_rtd_theme/static/css/badge_only.css create mode 100644 doc/_themes/sphinx_rtd_theme/static/css/theme.css create mode 100644 doc/_themes/sphinx_rtd_theme/static/fonts/FontAwesome.otf create mode 100644 doc/_themes/sphinx_rtd_theme/static/fonts/fontawesome-webfont.eot create mode 100644 doc/_themes/sphinx_rtd_theme/static/fonts/fontawesome-webfont.svg create mode 100644 doc/_themes/sphinx_rtd_theme/static/fonts/fontawesome-webfont.ttf create mode 100644 doc/_themes/sphinx_rtd_theme/static/fonts/fontawesome-webfont.woff create mode 100644 doc/_themes/sphinx_rtd_theme/static/js/theme.js create mode 100644 doc/_themes/sphinx_rtd_theme/theme.conf create mode 100644 doc/_themes/sphinx_rtd_theme/versions.html create mode 100644 doc/apiref.rst create mode 100644 doc/asio_http2.h.rst.in create mode 100644 doc/asio_http2_client.h.rst.in create mode 100644 doc/asio_http2_server.h.rst.in create mode 100644 doc/bash_completion/h2load create mode 100644 doc/bash_completion/nghttp create mode 100644 doc/bash_completion/nghttpd create mode 100644 doc/bash_completion/nghttpx create mode 100644 doc/building-android-binary.rst.in create mode 100644 doc/conf.py.in create mode 100644 doc/contribute.rst.in create mode 100644 doc/enums.rst create mode 100644 doc/h2load-howto.rst.in create mode 100644 doc/h2load.1 create mode 100644 doc/h2load.1.rst create mode 100644 doc/index.rst.in create mode 100644 doc/libnghttp2_asio.rst.in create mode 100644 doc/macros.rst create mode 100755 doc/mkapiref.py create mode 100644 doc/nghttp.1 create mode 100644 doc/nghttp.1.rst create mode 100644 doc/nghttp2.h.rst.in create mode 100644 doc/nghttp2_check_header_name.rst create mode 100644 doc/nghttp2_check_header_value.rst create mode 100644 doc/nghttp2_hd_deflate_bound.rst create mode 100644 doc/nghttp2_hd_deflate_change_table_size.rst create mode 100644 doc/nghttp2_hd_deflate_del.rst create mode 100644 doc/nghttp2_hd_deflate_hd.rst create mode 100644 doc/nghttp2_hd_deflate_new.rst create mode 100644 doc/nghttp2_hd_deflate_new2.rst create mode 100644 doc/nghttp2_hd_inflate_change_table_size.rst create mode 100644 doc/nghttp2_hd_inflate_del.rst create mode 100644 doc/nghttp2_hd_inflate_end_headers.rst create mode 100644 doc/nghttp2_hd_inflate_hd.rst create mode 100644 doc/nghttp2_hd_inflate_new.rst create mode 100644 doc/nghttp2_hd_inflate_new2.rst create mode 100644 doc/nghttp2_is_fatal.rst create mode 100644 doc/nghttp2_nv_compare_name.rst create mode 100644 doc/nghttp2_option_del.rst create mode 100644 doc/nghttp2_option_new.rst create mode 100644 doc/nghttp2_option_set_no_auto_window_update.rst create mode 100644 doc/nghttp2_option_set_no_http_messaging.rst create mode 100644 doc/nghttp2_option_set_no_recv_client_magic.rst create mode 100644 doc/nghttp2_option_set_peer_max_concurrent_streams.rst create mode 100644 doc/nghttp2_pack_settings_payload.rst create mode 100644 doc/nghttp2_priority_spec_check_default.rst create mode 100644 doc/nghttp2_priority_spec_default_init.rst create mode 100644 doc/nghttp2_priority_spec_init.rst create mode 100644 doc/nghttp2_select_next_protocol.rst create mode 100644 doc/nghttp2_session_callbacks_del.rst create mode 100644 doc/nghttp2_session_callbacks_new.rst create mode 100644 doc/nghttp2_session_callbacks_set_before_frame_send_callback.rst create mode 100644 doc/nghttp2_session_callbacks_set_data_source_read_length_callback.rst create mode 100644 doc/nghttp2_session_callbacks_set_on_begin_frame_callback.rst create mode 100644 doc/nghttp2_session_callbacks_set_on_begin_headers_callback.rst create mode 100644 doc/nghttp2_session_callbacks_set_on_data_chunk_recv_callback.rst create mode 100644 doc/nghttp2_session_callbacks_set_on_frame_not_send_callback.rst create mode 100644 doc/nghttp2_session_callbacks_set_on_frame_recv_callback.rst create mode 100644 doc/nghttp2_session_callbacks_set_on_frame_send_callback.rst create mode 100644 doc/nghttp2_session_callbacks_set_on_header_callback.rst create mode 100644 doc/nghttp2_session_callbacks_set_on_invalid_frame_recv_callback.rst create mode 100644 doc/nghttp2_session_callbacks_set_on_stream_close_callback.rst create mode 100644 doc/nghttp2_session_callbacks_set_recv_callback.rst create mode 100644 doc/nghttp2_session_callbacks_set_select_padding_callback.rst create mode 100644 doc/nghttp2_session_callbacks_set_send_callback.rst create mode 100644 doc/nghttp2_session_callbacks_set_send_data_callback.rst create mode 100644 doc/nghttp2_session_client_new.rst create mode 100644 doc/nghttp2_session_client_new2.rst create mode 100644 doc/nghttp2_session_client_new3.rst create mode 100644 doc/nghttp2_session_consume.rst create mode 100644 doc/nghttp2_session_consume_connection.rst create mode 100644 doc/nghttp2_session_consume_stream.rst create mode 100644 doc/nghttp2_session_del.rst create mode 100644 doc/nghttp2_session_get_effective_local_window_size.rst create mode 100644 doc/nghttp2_session_get_effective_recv_data_length.rst create mode 100644 doc/nghttp2_session_get_last_proc_stream_id.rst create mode 100644 doc/nghttp2_session_get_next_stream_id.rst create mode 100644 doc/nghttp2_session_get_outbound_queue_size.rst create mode 100644 doc/nghttp2_session_get_remote_settings.rst create mode 100644 doc/nghttp2_session_get_remote_window_size.rst create mode 100644 doc/nghttp2_session_get_stream_effective_local_window_size.rst create mode 100644 doc/nghttp2_session_get_stream_effective_recv_data_length.rst create mode 100644 doc/nghttp2_session_get_stream_local_close.rst create mode 100644 doc/nghttp2_session_get_stream_remote_close.rst create mode 100644 doc/nghttp2_session_get_stream_remote_window_size.rst create mode 100644 doc/nghttp2_session_get_stream_user_data.rst create mode 100644 doc/nghttp2_session_mem_recv.rst create mode 100644 doc/nghttp2_session_mem_send.rst create mode 100644 doc/nghttp2_session_recv.rst create mode 100644 doc/nghttp2_session_resume_data.rst create mode 100644 doc/nghttp2_session_send.rst create mode 100644 doc/nghttp2_session_server_new.rst create mode 100644 doc/nghttp2_session_server_new2.rst create mode 100644 doc/nghttp2_session_server_new3.rst create mode 100644 doc/nghttp2_session_set_next_stream_id.rst create mode 100644 doc/nghttp2_session_set_stream_user_data.rst create mode 100644 doc/nghttp2_session_terminate_session.rst create mode 100644 doc/nghttp2_session_terminate_session2.rst create mode 100644 doc/nghttp2_session_upgrade.rst create mode 100644 doc/nghttp2_session_want_read.rst create mode 100644 doc/nghttp2_session_want_write.rst create mode 100644 doc/nghttp2_strerror.rst create mode 100644 doc/nghttp2_submit_data.rst create mode 100644 doc/nghttp2_submit_goaway.rst create mode 100644 doc/nghttp2_submit_headers.rst create mode 100644 doc/nghttp2_submit_ping.rst create mode 100644 doc/nghttp2_submit_priority.rst create mode 100644 doc/nghttp2_submit_push_promise.rst create mode 100644 doc/nghttp2_submit_request.rst create mode 100644 doc/nghttp2_submit_response.rst create mode 100644 doc/nghttp2_submit_rst_stream.rst create mode 100644 doc/nghttp2_submit_settings.rst create mode 100644 doc/nghttp2_submit_shutdown_notice.rst create mode 100644 doc/nghttp2_submit_trailer.rst create mode 100644 doc/nghttp2_submit_window_update.rst create mode 100644 doc/nghttp2_version.rst create mode 100644 doc/nghttp2ver.h.rst.in create mode 100644 doc/nghttpd.1 create mode 100644 doc/nghttpd.1.rst create mode 100644 doc/nghttpx-howto.rst.in create mode 100644 doc/nghttpx.1 create mode 100644 doc/nghttpx.1.rst create mode 100644 doc/package_README.rst.in create mode 100644 doc/programmers-guide.rst create mode 100644 doc/python-apiref.rst.in create mode 100644 doc/sources/building-android-binary.rst create mode 100644 doc/sources/contribute.rst create mode 100644 doc/sources/h2load-howto.rst create mode 100644 doc/sources/index.rst create mode 100644 doc/sources/libnghttp2_asio.rst create mode 100644 doc/sources/nghttpx-howto.rst create mode 100644 doc/sources/python-apiref.rst create mode 100644 doc/sources/tutorial-client.rst create mode 100644 doc/sources/tutorial-hpack.rst create mode 100644 doc/sources/tutorial-server.rst create mode 100644 doc/tutorial-client.rst.in create mode 100644 doc/tutorial-hpack.rst.in create mode 100644 doc/tutorial-server.rst.in create mode 100644 doc/types.rst create mode 100644 examples/Makefile.am create mode 100644 examples/Makefile.in create mode 100644 examples/asio-cl.cc create mode 100644 examples/asio-cl2.cc create mode 100644 examples/asio-sv.cc create mode 100644 examples/asio-sv2.cc create mode 100644 examples/client.c create mode 100644 examples/deflate.c create mode 100644 examples/libevent-client.c create mode 100644 examples/libevent-server.c create mode 100644 examples/tiny-nghttpd.c create mode 100755 install-sh create mode 100644 integration-tests/Makefile.am create mode 100644 integration-tests/Makefile.in create mode 100644 integration-tests/alt-server.crt create mode 100644 integration-tests/alt-server.key create mode 100644 integration-tests/config.go.in create mode 100644 integration-tests/nghttpx_http1_test.go create mode 100644 integration-tests/nghttpx_http2_test.go create mode 100644 integration-tests/nghttpx_spdy_test.go create mode 100644 integration-tests/server.crt create mode 100644 integration-tests/server.key create mode 100644 integration-tests/server_tester.go create mode 100644 integration-tests/setenv create mode 100644 integration-tests/setenv.in create mode 100644 lib/Makefile.am create mode 100644 lib/Makefile.in create mode 100644 lib/Makefile.msvc create mode 100644 lib/includes/Makefile.am create mode 100644 lib/includes/Makefile.in create mode 100644 lib/includes/nghttp2/nghttp2.h create mode 100644 lib/includes/nghttp2/nghttp2ver.h create mode 100644 lib/includes/nghttp2/nghttp2ver.h.in create mode 100644 lib/libnghttp2.pc.in create mode 100644 lib/nghttp2_buf.c create mode 100644 lib/nghttp2_buf.h create mode 100644 lib/nghttp2_callbacks.c create mode 100644 lib/nghttp2_callbacks.h create mode 100644 lib/nghttp2_frame.c create mode 100644 lib/nghttp2_frame.h create mode 100644 lib/nghttp2_hd.c create mode 100644 lib/nghttp2_hd.h create mode 100644 lib/nghttp2_hd_huffman.c create mode 100644 lib/nghttp2_hd_huffman.h create mode 100644 lib/nghttp2_hd_huffman_data.c create mode 100644 lib/nghttp2_helper.c create mode 100644 lib/nghttp2_helper.h create mode 100644 lib/nghttp2_http.c create mode 100644 lib/nghttp2_http.h create mode 100644 lib/nghttp2_int.h create mode 100644 lib/nghttp2_map.c create mode 100644 lib/nghttp2_map.h create mode 100644 lib/nghttp2_mem.c create mode 100644 lib/nghttp2_mem.h create mode 100644 lib/nghttp2_net.h create mode 100644 lib/nghttp2_npn.c create mode 100644 lib/nghttp2_npn.h create mode 100644 lib/nghttp2_option.c create mode 100644 lib/nghttp2_option.h create mode 100644 lib/nghttp2_outbound_item.c create mode 100644 lib/nghttp2_outbound_item.h create mode 100644 lib/nghttp2_pq.c create mode 100644 lib/nghttp2_pq.h create mode 100644 lib/nghttp2_priority_spec.c create mode 100644 lib/nghttp2_priority_spec.h create mode 100644 lib/nghttp2_queue.c create mode 100644 lib/nghttp2_queue.h create mode 100644 lib/nghttp2_session.c create mode 100644 lib/nghttp2_session.h create mode 100644 lib/nghttp2_stream.c create mode 100644 lib/nghttp2_stream.h create mode 100644 lib/nghttp2_submit.c create mode 100644 lib/nghttp2_submit.h create mode 100644 lib/nghttp2_version.c create mode 100644 ltmain.sh create mode 100644 m4/ax_boost_asio.m4 create mode 100644 m4/ax_boost_base.m4 create mode 100644 m4/ax_boost_system.m4 create mode 100644 m4/ax_boost_thread.m4 create mode 100644 m4/ax_check_compile_flag.m4 create mode 100644 m4/ax_cxx_compile_stdcxx_11.m4 create mode 100644 m4/ax_have_epoll.m4 create mode 100644 m4/ax_python_devel.m4 create mode 100644 m4/libtool.m4 create mode 100644 m4/libxml2.m4 create mode 100644 m4/ltoptions.m4 create mode 100644 m4/ltsugar.m4 create mode 100644 m4/ltversion.m4 create mode 100644 m4/lt~obsolete.m4 create mode 100755 missing create mode 100644 nghttpx.conf.sample create mode 100644 proxy.pac.sample create mode 100644 python/Makefile.am create mode 100644 python/Makefile.in create mode 100644 python/cnghttp2.pxd create mode 100644 python/nghttp2.pyx create mode 100644 python/setup.py.in create mode 100644 src/HtmlParser.cc create mode 100644 src/HtmlParser.h create mode 100644 src/HttpServer.cc create mode 100644 src/HttpServer.h create mode 100644 src/Makefile.am create mode 100644 src/Makefile.in create mode 100644 src/app_helper.cc create mode 100644 src/app_helper.h create mode 100644 src/asio_client_request.cc create mode 100644 src/asio_client_request_impl.cc create mode 100644 src/asio_client_request_impl.h create mode 100644 src/asio_client_response.cc create mode 100644 src/asio_client_response_impl.cc create mode 100644 src/asio_client_response_impl.h create mode 100644 src/asio_client_session.cc create mode 100644 src/asio_client_session_impl.cc create mode 100644 src/asio_client_session_impl.h create mode 100644 src/asio_client_session_tcp_impl.cc create mode 100644 src/asio_client_session_tcp_impl.h create mode 100644 src/asio_client_session_tls_impl.cc create mode 100644 src/asio_client_session_tls_impl.h create mode 100644 src/asio_client_stream.cc create mode 100644 src/asio_client_stream.h create mode 100644 src/asio_client_tls_context.cc create mode 100644 src/asio_client_tls_context.h create mode 100644 src/asio_common.cc create mode 100644 src/asio_common.h create mode 100644 src/asio_io_service_pool.cc create mode 100644 src/asio_io_service_pool.h create mode 100644 src/asio_server.cc create mode 100644 src/asio_server.h create mode 100644 src/asio_server_connection.h create mode 100644 src/asio_server_http2.cc create mode 100644 src/asio_server_http2_handler.cc create mode 100644 src/asio_server_http2_handler.h create mode 100644 src/asio_server_http2_impl.cc create mode 100644 src/asio_server_http2_impl.h create mode 100644 src/asio_server_request.cc create mode 100644 src/asio_server_request_handler.cc create mode 100644 src/asio_server_request_handler.h create mode 100644 src/asio_server_request_impl.cc create mode 100644 src/asio_server_request_impl.h create mode 100644 src/asio_server_response.cc create mode 100644 src/asio_server_response_impl.cc create mode 100644 src/asio_server_response_impl.h create mode 100644 src/asio_server_serve_mux.cc create mode 100644 src/asio_server_serve_mux.h create mode 100644 src/asio_server_stream.cc create mode 100644 src/asio_server_stream.h create mode 100644 src/asio_server_tls_context.cc create mode 100644 src/asio_server_tls_context.h create mode 100644 src/base64.h create mode 100644 src/buffer.h create mode 100644 src/buffer_test.cc create mode 100644 src/buffer_test.h create mode 100644 src/comp_helper.c create mode 100644 src/comp_helper.h create mode 100644 src/deflatehd.cc create mode 100644 src/h2load.cc create mode 100644 src/h2load.h create mode 100644 src/h2load_http2_session.cc create mode 100644 src/h2load_http2_session.h create mode 100644 src/h2load_session.h create mode 100644 src/h2load_spdy_session.cc create mode 100644 src/h2load_spdy_session.h create mode 100644 src/http2.cc create mode 100644 src/http2.h create mode 100644 src/http2_test.cc create mode 100644 src/http2_test.h create mode 100644 src/includes/Makefile.am create mode 100644 src/includes/Makefile.in create mode 100644 src/includes/nghttp2/asio_http2.h create mode 100644 src/includes/nghttp2/asio_http2_client.h create mode 100644 src/includes/nghttp2/asio_http2_server.h create mode 100644 src/inflatehd.cc create mode 100644 src/libnghttp2_asio.pc.in create mode 100644 src/memchunk.h create mode 100644 src/memchunk_test.cc create mode 100644 src/memchunk_test.h create mode 100644 src/nghttp.cc create mode 100644 src/nghttp.h create mode 100644 src/nghttp2_config.h create mode 100644 src/nghttp2_gzip.c create mode 100644 src/nghttp2_gzip.h create mode 100644 src/nghttp2_gzip_test.c create mode 100644 src/nghttp2_gzip_test.h create mode 100644 src/nghttpd.cc create mode 100644 src/shrpx-unittest.cc create mode 100644 src/shrpx.cc create mode 100644 src/shrpx.h create mode 100644 src/shrpx_accept_handler.cc create mode 100644 src/shrpx_accept_handler.h create mode 100644 src/shrpx_client_handler.cc create mode 100644 src/shrpx_client_handler.h create mode 100644 src/shrpx_config.cc create mode 100644 src/shrpx_config.h create mode 100644 src/shrpx_config_test.cc create mode 100644 src/shrpx_config_test.h create mode 100644 src/shrpx_connect_blocker.cc create mode 100644 src/shrpx_connect_blocker.h create mode 100644 src/shrpx_connection.cc create mode 100644 src/shrpx_connection.h create mode 100644 src/shrpx_connection_handler.cc create mode 100644 src/shrpx_connection_handler.h create mode 100644 src/shrpx_downstream.cc create mode 100644 src/shrpx_downstream.h create mode 100644 src/shrpx_downstream_connection.cc create mode 100644 src/shrpx_downstream_connection.h create mode 100644 src/shrpx_downstream_connection_pool.cc create mode 100644 src/shrpx_downstream_connection_pool.h create mode 100644 src/shrpx_downstream_queue.cc create mode 100644 src/shrpx_downstream_queue.h create mode 100644 src/shrpx_downstream_test.cc create mode 100644 src/shrpx_downstream_test.h create mode 100644 src/shrpx_error.h create mode 100644 src/shrpx_http.cc create mode 100644 src/shrpx_http.h create mode 100644 src/shrpx_http2_downstream_connection.cc create mode 100644 src/shrpx_http2_downstream_connection.h create mode 100644 src/shrpx_http2_session.cc create mode 100644 src/shrpx_http2_session.h create mode 100644 src/shrpx_http2_upstream.cc create mode 100644 src/shrpx_http2_upstream.h create mode 100644 src/shrpx_http_downstream_connection.cc create mode 100644 src/shrpx_http_downstream_connection.h create mode 100644 src/shrpx_https_upstream.cc create mode 100644 src/shrpx_https_upstream.h create mode 100644 src/shrpx_io_control.cc create mode 100644 src/shrpx_io_control.h create mode 100644 src/shrpx_log.cc create mode 100644 src/shrpx_log.h create mode 100644 src/shrpx_log_config.cc create mode 100644 src/shrpx_log_config.h create mode 100644 src/shrpx_rate_limit.cc create mode 100644 src/shrpx_rate_limit.h create mode 100644 src/shrpx_spdy_upstream.cc create mode 100644 src/shrpx_spdy_upstream.h create mode 100644 src/shrpx_ssl.cc create mode 100644 src/shrpx_ssl.h create mode 100644 src/shrpx_ssl_test.cc create mode 100644 src/shrpx_ssl_test.h create mode 100644 src/shrpx_upstream.h create mode 100644 src/shrpx_worker.cc create mode 100644 src/shrpx_worker.h create mode 100644 src/ssl.cc create mode 100644 src/ssl.h create mode 100644 src/template.h create mode 100644 src/timegm.c create mode 100644 src/timegm.h create mode 100644 src/util.cc create mode 100644 src/util.h create mode 100644 src/util_test.cc create mode 100644 src/util_test.h create mode 100755 test-driver create mode 100644 tests/Makefile.am create mode 100644 tests/Makefile.in create mode 100644 tests/failmalloc.c create mode 100644 tests/failmalloc_test.c create mode 100644 tests/failmalloc_test.h create mode 100644 tests/main.c create mode 100644 tests/malloc_wrapper.c create mode 100644 tests/malloc_wrapper.h create mode 100644 tests/nghttp2_buf_test.c create mode 100644 tests/nghttp2_buf_test.h create mode 100644 tests/nghttp2_frame_test.c create mode 100644 tests/nghttp2_frame_test.h create mode 100644 tests/nghttp2_hd_test.c create mode 100644 tests/nghttp2_hd_test.h create mode 100644 tests/nghttp2_helper_test.c create mode 100644 tests/nghttp2_helper_test.h create mode 100644 tests/nghttp2_map_test.c create mode 100644 tests/nghttp2_map_test.h create mode 100644 tests/nghttp2_npn_test.c create mode 100644 tests/nghttp2_npn_test.h create mode 100644 tests/nghttp2_pq_test.c create mode 100644 tests/nghttp2_pq_test.h create mode 100644 tests/nghttp2_queue_test.c create mode 100644 tests/nghttp2_queue_test.h create mode 100644 tests/nghttp2_session_test.c create mode 100644 tests/nghttp2_session_test.h create mode 100644 tests/nghttp2_stream_test.c create mode 100644 tests/nghttp2_stream_test.h create mode 100644 tests/nghttp2_test_helper.c create mode 100644 tests/nghttp2_test_helper.h create mode 100644 tests/testdata/Makefile.am create mode 100644 tests/testdata/Makefile.in create mode 100644 tests/testdata/cacert.pem create mode 100644 tests/testdata/index.html create mode 100644 tests/testdata/privkey.pem create mode 100644 third-party/Makefile.am create mode 100644 third-party/Makefile.in create mode 100755 third-party/h2o/fetch-ocsp-response create mode 100644 third-party/http-parser/http_parser.c create mode 100644 third-party/http-parser/http_parser.h diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 0000000..95bc954 --- /dev/null +++ b/AUTHORS @@ -0,0 +1 @@ +Tatsuhiro Tsujikawa diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..a43e837 --- /dev/null +++ b/COPYING @@ -0,0 +1,22 @@ +The MIT License + +Copyright (c) 2012, 2014, 2015 Tatsuhiro Tsujikawa + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 0000000..9711d5d --- /dev/null +++ b/ChangeLog @@ -0,0 +1,259 @@ +commit 553d741f036b3b5f47b0f296e4663fe1de8af6ec +Author: Tatsuhiro Tsujikawa +AuthorDate: 2015-05-16 +Commit: Tatsuhiro Tsujikawa +CommitDate: 2015-05-16 + + Fix migration version number + +commit 6bd728b3c2a6faccad3e4f69c75ca792f3a39e85 +Author: Tatsuhiro Tsujikawa +AuthorDate: 2015-05-16 +Commit: Tatsuhiro Tsujikawa +CommitDate: 2015-05-16 + + Update man pages + +commit a99085891a9378165f68211225d94e3008d742f5 +Author: Tatsuhiro Tsujikawa +AuthorDate: 2015-05-16 +Commit: Tatsuhiro Tsujikawa +CommitDate: 2015-05-16 + + Bump up version number to 1.0.0, LT revision to 14:0:0 + +commit 68d3724fad57db135015de05bdd169706c9e6983 +Author: Tatsuhiro Tsujikawa +AuthorDate: 2015-05-15 +Commit: Tatsuhiro Tsujikawa +CommitDate: 2015-05-15 + + Update README.rst + +commit fe39ec869733920f3f79e9ebdeb7850fbc9ffd2b +Author: Tatsuhiro Tsujikawa +AuthorDate: 2015-05-15 +Commit: Tatsuhiro Tsujikawa +CommitDate: 2015-05-15 + + doc: Update Resources + +commit c896118747e298aaed4ddc86da1169c02bdfc734 +Author: Tatsuhiro Tsujikawa +AuthorDate: 2015-05-15 +Commit: Tatsuhiro Tsujikawa +CommitDate: 2015-05-15 + + Fix required spdylay version + +commit b89140c311e7a3438d29637b44dea09bf20c8e79 +Merge: 92a20c7 a869c39 +Author: Tatsuhiro Tsujikawa +AuthorDate: 2015-05-15 +Commit: Tatsuhiro Tsujikawa +CommitDate: 2015-05-15 + + Merge branch 'v1.0.0' + +commit a869c39a2c2c7ec0db4e8db477b2dfed5f3eaa0f +Author: Tatsuhiro Tsujikawa +AuthorDate: 2015-05-15 +Commit: Tatsuhiro Tsujikawa +CommitDate: 2015-05-15 + + Bump up version number to 1.0.0-DEV + +commit 0b27f005e0c23931a82e612b024f3ace66546924 +Merge: 64b1aae 92a20c7 +Author: Tatsuhiro Tsujikawa +AuthorDate: 2015-05-15 +Commit: Tatsuhiro Tsujikawa +CommitDate: 2015-05-15 + + Merge branch 'master' into v1.0.0 + + Conflicts: + src/HttpServer.cc + +commit 64b1aae56748c7b6816409aee649817ee56aa1e8 +Author: Tatsuhiro Tsujikawa +AuthorDate: 2015-05-08 +Commit: Tatsuhiro Tsujikawa +CommitDate: 2015-05-08 + + integration: Fix TestH2H1Upgrade test failure + +commit e63d6e490aa9fab1cf8240bfd5c3133df241ced0 +Merge: de47350 7f60de0 +Author: Tatsuhiro Tsujikawa +AuthorDate: 2015-05-08 +Commit: Tatsuhiro Tsujikawa +CommitDate: 2015-05-08 + + Merge branch 'master' into v1.0.0 + + Conflicts: + lib/nghttp2_option.h + lib/nghttp2_session.h + src/HttpServer.cc + +commit de4735092a2970f9f5d93de617dc0faf2db9952d +Author: Tatsuhiro Tsujikawa +AuthorDate: 2015-04-28 +Commit: Tatsuhiro Tsujikawa +CommitDate: 2015-04-28 + + Fix doc formatting + +commit 1c4df1832b179802052b9785001381855e03981c +Author: Tatsuhiro Tsujikawa +AuthorDate: 2015-04-28 +Commit: Tatsuhiro Tsujikawa +CommitDate: 2015-04-28 + + Update doc, mainly for RFC numbers + +commit 1ad1fe600574e6fb5760b5ea66ac69472bf5e4d5 +Merge: db4a684 f05a483 +Author: Tatsuhiro Tsujikawa +AuthorDate: 2015-04-28 +Commit: Tatsuhiro Tsujikawa +CommitDate: 2015-04-28 + + Merge branch 'master' into v1.0.0 + +commit db4a68454a07d304ed92cfed9df8401b6b89196f +Merge: 5937b4b 6b0b8ea +Author: Tatsuhiro Tsujikawa +AuthorDate: 2015-04-24 +Commit: Tatsuhiro Tsujikawa +CommitDate: 2015-04-24 + + Merge branch 'master' into v1.0.0 + + Conflicts: + lib/includes/nghttp2/nghttp2.h + +commit 5937b4b6f73380e8a0c22932c118e265de2c30c3 +Merge: 90bfea7 787d401 +Author: Tatsuhiro Tsujikawa +AuthorDate: 2015-04-19 +Commit: Tatsuhiro Tsujikawa +CommitDate: 2015-04-19 + + Merge branch 'master' into v1.0.0 + +commit 90bfea77e098d54429445ee8cf8eee6854ccc767 +Author: Tatsuhiro Tsujikawa +AuthorDate: 2015-04-08 +Commit: Tatsuhiro Tsujikawa +CommitDate: 2015-04-08 + + doc: Remove nghttp2_submit_altsvc.rst + +commit cf0576253f93a096ea79610f5ef6349fa3e6ac9f +Merge: 59e3783 4aca2f0 +Author: Tatsuhiro Tsujikawa +AuthorDate: 2015-04-08 +Commit: Tatsuhiro Tsujikawa +CommitDate: 2015-04-08 + + Merge branch 'master' into v1.0.0 + +commit 59e3783f3f34cf8663b51fd98fdae14993aa1cb8 +Author: Tatsuhiro Tsujikawa +AuthorDate: 2015-04-07 +Commit: Tatsuhiro Tsujikawa +CommitDate: 2015-04-07 + + Update manual entry + +commit 084e4487edee1a0cd9c503a074519888ab347af4 +Author: Tatsuhiro Tsujikawa +AuthorDate: 2015-04-05 +Commit: Tatsuhiro Tsujikawa +CommitDate: 2015-04-05 + + Add migration section + +commit 24897aa50decdde2bf360f5805bc2cfb722f4cc8 +Author: Tatsuhiro Tsujikawa +AuthorDate: 2015-04-05 +Commit: Tatsuhiro Tsujikawa +CommitDate: 2015-04-05 + + Update README.rst + +commit 3e50ef439d914dbf6c611fc4afc8925c9e79d6c7 +Author: Tatsuhiro Tsujikawa +AuthorDate: 2015-04-05 +Commit: Tatsuhiro Tsujikawa +CommitDate: 2015-04-05 + + Announce h2, final HTTP/2 ALPN identifier + +commit 87602e5d7296b37ed3f2864db1965d139657aba6 +Author: Tatsuhiro Tsujikawa +AuthorDate: 2015-04-05 +Commit: Tatsuhiro Tsujikawa +CommitDate: 2015-04-05 + + Use NGHTTP2_PROTOCOL_ERROR for NGHTTP2_ERR_HTTP_{HEADER,MESSAGING} + +commit d0c27d52296a0adb6f5614cb58408c8617f39aab +Author: Tatsuhiro Tsujikawa +AuthorDate: 2015-04-05 +Commit: Tatsuhiro Tsujikawa +CommitDate: 2015-04-05 + + Send 24 bytes client magic byte string by library + + Previously nghttp2_session_send() and nghttp2_session_mem_send() did + not send 24 bytes client magic byte string (MAGIC). We made + nghttp2_session_recv() and nghttp2_session_mem_recv() process MAGIC by + default, so it is natural to make library send MAGIC as well. This + commit makes nghttp2_session_send() and nghttp2_session_mem_send() + send MAGIC. This commit also replace "connection preface" with + "client magic", since we call MAGIC as "connection preface" but it is + just a part of connection preface. NGHTTP2_CLIENT_CONNECTION_PREFACE + macro was replaced with NGHTTP2_CLIENT_MAGIC. The already deprecated + NGHTTP2_CLIENT_CONNECTION_HEADER macro was removed permanently. + nghttp2_option_set_no_recv_client_preface() was renamed as + nghttp2_option_set_no_recv_client_magic(). NGHTTP2_ERR_BAD_PREFACE + was renamed as NGHTTP2_ERR_BAD_CLIENT_MAGIC. + +commit ebf214c8fc45ea96f60a5edb7737b96f1d59e3ee +Author: Tatsuhiro Tsujikawa +AuthorDate: 2015-03-23 +Commit: Tatsuhiro Tsujikawa +CommitDate: 2015-04-05 + + nghttp2_on_invalid_frame_recv_callback should have lib_error_code as param + + nghttp2_error_code is HTTP/2 standard error code and is too coarse to + know what's going on. + +commit 250ea53e4b968bfc2a3dd0e301a1878fdfa41efb +Author: Tatsuhiro Tsujikawa +AuthorDate: 2015-03-23 +Commit: Tatsuhiro Tsujikawa +CommitDate: 2015-04-05 + + Deal with 24 bytes client connection preface by default + + Since HTTP/2 spec requires for client to send connection preface, it + is reasonable to make this option enabled by default. It is still a + use case to disable this, so replace this option with + nghttp2_option_set_no_recv_client_preface(). + +commit 01af6ea70c466b8ace7f22c25737418babddb8b5 +Author: Tatsuhiro Tsujikawa +AuthorDate: 2015-03-22 +Commit: Tatsuhiro Tsujikawa +CommitDate: 2015-04-05 + + Remove ALTSVC related code + + HTTP/2 and HPACK are going to be published as RFC, but ALTSVC is still + in draft state. To make our API stable, it would be better to remove + ALTSVC API for 1.0.0 release. diff --git a/Dockerfile.android b/Dockerfile.android new file mode 100644 index 0000000..87990f0 --- /dev/null +++ b/Dockerfile.android @@ -0,0 +1,109 @@ +# vim: ft=dockerfile: +# Dockerfile to build nghttp2 android binary +# +# $ sudo docker build -t nghttp2-android - < Dockerfile.android +# +# After successful build, android binaries are located under +# /root/build/nghttp2. You can copy the binary using docker cp. For +# example, to copy nghttpx binary to host file system location +# /path/to/dest, do this: +# +# $ sudo docker run -v /path/to/dest:/out nghttp2-android cp /root/build/nghttp2/src/nghttpx /out + +FROM ubuntu:trusty + +MAINTAINER Tatsuhiro Tsujikawa + +ENV ANDROID_HOME /root/android +ENV PREFIX $ANDROID_HOME/usr/local +ENV TOOLCHAIN $ANDROID_HOME/toolchain +ENV PATH $TOOLCHAIN/bin:$PATH + +# It would be better to use nearest ubuntu archive mirror for faster +# downloads. +# RUN sed -ie 's/archive\.ubuntu/jp.archive.ubuntu/g' /etc/apt/sources.list + +RUN apt-get update +# genisoimage, libc6-i386 and lib32stdc++6 are required to decompress ndk. +RUN apt-get install -y make binutils autoconf automake autotools-dev libtool \ + pkg-config git curl dpkg-dev libxml2-dev \ + genisoimage libc6-i386 lib32stdc++6 + +WORKDIR /root/build +RUN curl -L -O http://dl.google.com/android/ndk/android-ndk-r10c-linux-x86_64.bin && \ + chmod a+x android-ndk-r10c-linux-x86_64.bin && \ + ./android-ndk-r10c-linux-x86_64.bin && \ + rm android-ndk-r10c-linux-x86_64.bin + +WORKDIR /root/build/android-ndk-r10c +RUN /bin/bash build/tools/make-standalone-toolchain.sh \ + --install-dir=$ANDROID_HOME/toolchain \ + --toolchain=arm-linux-androideabi-4.9 --llvm-version=3.5 \ + --system=linux-x86_64 + +WORKDIR /root/build +RUN git clone https://github.com/tatsuhiro-t/spdylay +WORKDIR /root/build/spdylay +RUN autoreconf -i && \ + ./configure \ + --disable-shared \ + --host=arm-linux-androideabi \ + --build=`dpkg-architecture -qDEB_BUILD_GNU_TYPE` \ + --prefix=$PREFIX \ + --without-libxml2 \ + --disable-src \ + --disable-examples \ + CPPFLAGS="-I$PREFIX/include" \ + PKG_CONFIG_LIBDIR="$PREFIX/lib/pkgconfig" \ + LDFLAGS="-L$PREFIX/lib" && \ + make install + +WORKDIR /root/build +RUN curl -L -O https://www.openssl.org/source/openssl-1.0.2a.tar.gz && \ + tar xf openssl-1.0.2a.tar.gz && \ + rm openssl-1.0.2a.tar.gz + +WORKDIR /root/build/openssl-1.0.2a +RUN export CROSS_COMPILE=$TOOLCHAIN/bin/arm-linux-androideabi- && \ + ./Configure --prefix=$PREFIX android && \ + make && make install_sw + +WORKDIR /root/build +RUN curl -L -O http://dist.schmorp.de/libev/libev-4.19.tar.gz && \ + curl -L -O https://gist.github.com/tatsuhiro-t/48c45f08950f587180ed/raw/80a8f003b5d1091eae497c5995bbaa68096e739b/libev-4.19-android.patch && \ + tar xf libev-4.19.tar.gz && \ + rm libev-4.19.tar.gz + +WORKDIR /root/build/libev-4.19 +RUN patch -p1 < ../libev-4.19-android.patch && \ + ./configure \ + --host=arm-linux-androideabi \ + --build=`dpkg-architecture -qDEB_BUILD_GNU_TYPE` \ + --prefix=$PREFIX \ + --disable-shared \ + --enable-static \ + CPPFLAGS=-I$PREFIX/include \ + LDFLAGS=-L$PREFIX/lib && \ + make install + +WORKDIR /root/build +RUN git clone https://github.com/tatsuhiro-t/nghttp2 +WORKDIR /root/build/nghttp2 +RUN autoreconf -i && \ + ./configure \ + --disable-shared \ + --host=arm-linux-androideabi \ + --build=`dpkg-architecture -qDEB_BUILD_GNU_TYPE` \ + --with-xml-prefix="$PREFIX" \ + --without-libxml2 \ + --disable-python-bindings \ + --disable-examples \ + --disable-threads \ + LIBSPDYLAY_CFLAGS=-I$PREFIX/usr/local/include \ + LIBSPDYLAY_LIBS="-L$PREFIX/usr/local/lib -lspdylay" \ + CPPFLAGS="-fPIE -I$PREFIX/include" \ + CXXFLAGS="-fno-strict-aliasing" \ + PKG_CONFIG_LIBDIR="$PREFIX/lib/pkgconfig" \ + LDFLAGS="-fPIE -pie -L$PREFIX/lib" && \ + make && \ + arm-linux-androideabi-strip src/nghttpx src/nghttpd src/nghttp diff --git a/INSTALL b/INSTALL new file mode 100644 index 0000000..2099840 --- /dev/null +++ b/INSTALL @@ -0,0 +1,370 @@ +Installation Instructions +************************* + +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 `' 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/Makefile.am b/Makefile.am new file mode 100644 index 0000000..1058e02 --- /dev/null +++ b/Makefile.am @@ -0,0 +1,43 @@ +# nghttp2 - HTTP/2 C Library + +# Copyright (c) 2012 Tatsuhiro Tsujikawa + +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: + +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. + +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +SUBDIRS = lib third-party src examples python tests integration-tests \ + doc contrib + +ACLOCAL_AMFLAGS = -I m4 + +dist_doc_DATA = README.rst + +EXTRA_DIST = nghttpx.conf.sample proxy.pac.sample android-config android-make \ + Dockerfile.android + +.PHONY: clang-format + +# Format source files using clang-format. Don't format source files +# under third-party directory since we are not responsible for thier +# coding style. +clang-format: + CLANGFORMAT=`git config --get clangformat.binary`; \ + test -z $${CLANGFORMAT} && CLANGFORMAT="clang-format"; \ + $${CLANGFORMAT} -i lib/*.{c,h} lib/includes/nghttp2/*.h \ + src/*.{c,cc,h} src/includes/nghttp2/*.h examples/*.{c,cc} \ + tests/*.{c,h} diff --git a/Makefile.in b/Makefile.in new file mode 100644 index 0000000..0b92de8 --- /dev/null +++ b/Makefile.in @@ -0,0 +1,964 @@ +# Makefile.in generated by automake 1.14.1 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2013 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +# nghttp2 - HTTP/2 C Library + +# Copyright (c) 2012 Tatsuhiro Tsujikawa + +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: + +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. + +VPATH = @srcdir@ +am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)' +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +target_triplet = @target@ +subdir = . +DIST_COMMON = INSTALL NEWS README AUTHORS ChangeLog \ + $(srcdir)/Makefile.in $(srcdir)/Makefile.am \ + $(top_srcdir)/configure $(am__configure_deps) \ + $(srcdir)/config.h.in \ + $(top_srcdir)/lib/includes/nghttp2/nghttp2ver.h.in \ + $(dist_doc_DATA) COPYING compile config.guess config.sub \ + install-sh missing ltmain.sh +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/ax_boost_asio.m4 \ + $(top_srcdir)/m4/ax_boost_base.m4 \ + $(top_srcdir)/m4/ax_boost_system.m4 \ + $(top_srcdir)/m4/ax_boost_thread.m4 \ + $(top_srcdir)/m4/ax_check_compile_flag.m4 \ + $(top_srcdir)/m4/ax_cxx_compile_stdcxx_11.m4 \ + $(top_srcdir)/m4/ax_have_epoll.m4 \ + $(top_srcdir)/m4/ax_python_devel.m4 \ + $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/libxml2.m4 \ + $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ + $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +am__CONFIG_DISTCLEAN_FILES = config.status config.cache config.log \ + configure.lineno config.status.lineno +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = config.h +CONFIG_CLEAN_FILES = lib/includes/nghttp2/nghttp2ver.h +CONFIG_CLEAN_VPATH_FILES = +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +SOURCES = +DIST_SOURCES = +RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \ + ctags-recursive dvi-recursive html-recursive info-recursive \ + install-data-recursive install-dvi-recursive \ + install-exec-recursive install-html-recursive \ + install-info-recursive install-pdf-recursive \ + install-ps-recursive install-recursive installcheck-recursive \ + installdirs-recursive pdf-recursive ps-recursive \ + tags-recursive uninstall-recursive +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +am__uninstall_files_from_dir = { \ + test -z "$$files" \ + || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ + || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ + $(am__cd) "$$dir" && rm -f $$files; }; \ + } +am__installdirs = "$(DESTDIR)$(docdir)" +DATA = $(dist_doc_DATA) +RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \ + distclean-recursive maintainer-clean-recursive +am__recursive_targets = \ + $(RECURSIVE_TARGETS) \ + $(RECURSIVE_CLEAN_TARGETS) \ + $(am__extra_recursive_targets) +AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \ + cscope distdir dist dist-all distcheck +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) \ + $(LISP)config.h.in +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +ETAGS = etags +CTAGS = ctags +CSCOPE = cscope +DIST_SUBDIRS = $(SUBDIRS) +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +distdir = $(PACKAGE)-$(VERSION) +top_distdir = $(distdir) +am__remove_distdir = \ + if test -d "$(distdir)"; then \ + find "$(distdir)" -type d ! -perm -200 -exec chmod u+w {} ';' \ + && rm -rf "$(distdir)" \ + || { sleep 5 && rm -rf "$(distdir)"; }; \ + else :; fi +am__post_remove_distdir = $(am__remove_distdir) +am__relativize = \ + dir0=`pwd`; \ + sed_first='s,^\([^/]*\)/.*$$,\1,'; \ + sed_rest='s,^[^/]*/*,,'; \ + sed_last='s,^.*/\([^/]*\)$$,\1,'; \ + sed_butlast='s,/*[^/]*$$,,'; \ + while test -n "$$dir1"; do \ + first=`echo "$$dir1" | sed -e "$$sed_first"`; \ + if test "$$first" != "."; then \ + if test "$$first" = ".."; then \ + dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \ + dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \ + else \ + first2=`echo "$$dir2" | sed -e "$$sed_first"`; \ + if test "$$first2" = "$$first"; then \ + dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \ + else \ + dir2="../$$dir2"; \ + fi; \ + dir0="$$dir0"/"$$first"; \ + fi; \ + fi; \ + dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \ + done; \ + reldir="$$dir2" +DIST_ARCHIVES = $(distdir).tar.gz +GZIP_ENV = --best +DIST_TARGETS = dist-gzip +distuninstallcheck_listfiles = find . -type f -print +am__distuninstallcheck_listfiles = $(distuninstallcheck_listfiles) \ + | sed 's|^\./|$(prefix)/|' | grep -v '$(infodir)/dir$$' +distcleancheck_listfiles = find . -type f -print +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +APPLDFLAGS = @APPLDFLAGS@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +BOOST_ASIO_LIB = @BOOST_ASIO_LIB@ +BOOST_CPPFLAGS = @BOOST_CPPFLAGS@ +BOOST_LDFLAGS = @BOOST_LDFLAGS@ +BOOST_SYSTEM_LIB = @BOOST_SYSTEM_LIB@ +BOOST_THREAD_LIB = @BOOST_THREAD_LIB@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CUNIT_CFLAGS = @CUNIT_CFLAGS@ +CUNIT_LIBS = @CUNIT_LIBS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +CYTHON = @CYTHON@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DLLTOOL = @DLLTOOL@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +GREP = @GREP@ +HAVE_CXX11 = @HAVE_CXX11@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +JANSSON_CFLAGS = @JANSSON_CFLAGS@ +JANSSON_LIBS = @JANSSON_LIBS@ +JEMALLOC_LIBS = @JEMALLOC_LIBS@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBEVENT_OPENSSL_CFLAGS = @LIBEVENT_OPENSSL_CFLAGS@ +LIBEVENT_OPENSSL_LIBS = @LIBEVENT_OPENSSL_LIBS@ +LIBEV_CFLAGS = @LIBEV_CFLAGS@ +LIBEV_LIBS = @LIBEV_LIBS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBSPDYLAY_CFLAGS = @LIBSPDYLAY_CFLAGS@ +LIBSPDYLAY_LIBS = @LIBSPDYLAY_LIBS@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +LT_AGE = @LT_AGE@ +LT_CURRENT = @LT_CURRENT@ +LT_REVISION = @LT_REVISION@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MKDIR_P = @MKDIR_P@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OPENSSL_CFLAGS = @OPENSSL_CFLAGS@ +OPENSSL_LIBS = @OPENSSL_LIBS@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PACKAGE_VERSION_NUM = @PACKAGE_VERSION_NUM@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PKG_CONFIG = @PKG_CONFIG@ +PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ +PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ +PYTHON = @PYTHON@ +PYTHON_CPPFLAGS = @PYTHON_CPPFLAGS@ +PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@ +PYTHON_EXTRA_LDFLAGS = @PYTHON_EXTRA_LDFLAGS@ +PYTHON_EXTRA_LIBS = @PYTHON_EXTRA_LIBS@ +PYTHON_LDFLAGS = @PYTHON_LDFLAGS@ +PYTHON_PLATFORM = @PYTHON_PLATFORM@ +PYTHON_PREFIX = @PYTHON_PREFIX@ +PYTHON_SITE_PKG = @PYTHON_SITE_PKG@ +PYTHON_VERSION = @PYTHON_VERSION@ +RANLIB = @RANLIB@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +TESTLDADD = @TESTLDADD@ +VERSION = @VERSION@ +WARNCFLAGS = @WARNCFLAGS@ +XML2_CONFIG = @XML2_CONFIG@ +XML_CPPFLAGS = @XML_CPPFLAGS@ +XML_LIBS = @XML_LIBS@ +ZLIB_CFLAGS = @ZLIB_CFLAGS@ +ZLIB_LIBS = @ZLIB_LIBS@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +pkgpyexecdir = @pkgpyexecdir@ +pkgpythondir = @pkgpythondir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +pyexecdir = @pyexecdir@ +pythondir = @pythondir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target = @target@ +target_alias = @target_alias@ +target_cpu = @target_cpu@ +target_os = @target_os@ +target_vendor = @target_vendor@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ + +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +SUBDIRS = lib third-party src examples python tests integration-tests \ + doc contrib + +ACLOCAL_AMFLAGS = -I m4 +dist_doc_DATA = README.rst +EXTRA_DIST = nghttpx.conf.sample proxy.pac.sample android-config android-make \ + Dockerfile.android + +all: config.h + $(MAKE) $(AM_MAKEFLAGS) all-recursive + +.SUFFIXES: +am--refresh: Makefile + @: +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + echo ' cd $(srcdir) && $(AUTOMAKE) --gnu'; \ + $(am__cd) $(srcdir) && $(AUTOMAKE) --gnu \ + && exit 0; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + echo ' $(SHELL) ./config.status'; \ + $(SHELL) ./config.status;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + $(SHELL) ./config.status --recheck + +$(top_srcdir)/configure: $(am__configure_deps) + $(am__cd) $(srcdir) && $(AUTOCONF) +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + $(am__cd) $(srcdir) && $(ACLOCAL) $(ACLOCAL_AMFLAGS) +$(am__aclocal_m4_deps): + +config.h: stamp-h1 + @test -f $@ || rm -f stamp-h1 + @test -f $@ || $(MAKE) $(AM_MAKEFLAGS) stamp-h1 + +stamp-h1: $(srcdir)/config.h.in $(top_builddir)/config.status + @rm -f stamp-h1 + cd $(top_builddir) && $(SHELL) ./config.status config.h +$(srcdir)/config.h.in: $(am__configure_deps) + ($(am__cd) $(top_srcdir) && $(AUTOHEADER)) + rm -f stamp-h1 + touch $@ + +distclean-hdr: + -rm -f config.h stamp-h1 +lib/includes/nghttp2/nghttp2ver.h: $(top_builddir)/config.status $(top_srcdir)/lib/includes/nghttp2/nghttp2ver.h.in + cd $(top_builddir) && $(SHELL) ./config.status $@ + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +distclean-libtool: + -rm -f libtool config.lt +install-dist_docDATA: $(dist_doc_DATA) + @$(NORMAL_INSTALL) + @list='$(dist_doc_DATA)'; test -n "$(docdir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(docdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(docdir)" || exit 1; \ + fi; \ + for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + echo "$$d$$p"; \ + done | $(am__base_list) | \ + while read files; do \ + echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(docdir)'"; \ + $(INSTALL_DATA) $$files "$(DESTDIR)$(docdir)" || exit $$?; \ + done + +uninstall-dist_docDATA: + @$(NORMAL_UNINSTALL) + @list='$(dist_doc_DATA)'; test -n "$(docdir)" || list=; \ + files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ + dir='$(DESTDIR)$(docdir)'; $(am__uninstall_files_from_dir) + +# This directory's subdirectories are mostly independent; you can cd +# into them and run 'make' without going through this Makefile. +# To change the values of 'make' variables: instead of editing Makefiles, +# (1) if the variable is set in 'config.status', edit 'config.status' +# (which will cause the Makefiles to be regenerated when you run 'make'); +# (2) otherwise, pass the desired values on the 'make' command line. +$(am__recursive_targets): + @fail=; \ + if $(am__make_keepgoing); then \ + failcom='fail=yes'; \ + else \ + failcom='exit 1'; \ + fi; \ + dot_seen=no; \ + target=`echo $@ | sed s/-recursive//`; \ + case "$@" in \ + distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ + *) list='$(SUBDIRS)' ;; \ + esac; \ + for subdir in $$list; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + dot_seen=yes; \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || eval $$failcom; \ + done; \ + if test "$$dot_seen" = "no"; then \ + $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ + fi; test -z "$$fail" + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-recursive +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \ + include_option=--etags-include; \ + empty_fix=.; \ + else \ + include_option=--include; \ + empty_fix=; \ + fi; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + test ! -f $$subdir/TAGS || \ + set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \ + fi; \ + done; \ + $(am__define_uniq_tagged_files); \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: ctags-recursive + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" +cscope: cscope.files + test ! -s cscope.files \ + || $(CSCOPE) -b -q $(AM_CSCOPEFLAGS) $(CSCOPEFLAGS) -i cscope.files $(CSCOPE_ARGS) +clean-cscope: + -rm -f cscope.files +cscope.files: clean-cscope cscopelist +cscopelist: cscopelist-recursive + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + -rm -f cscope.out cscope.in.out cscope.po.out cscope.files + +distdir: $(DISTFILES) + $(am__remove_distdir) + test -d "$(distdir)" || mkdir "$(distdir)" + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done + @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + $(am__make_dryrun) \ + || test -d "$(distdir)/$$subdir" \ + || $(MKDIR_P) "$(distdir)/$$subdir" \ + || exit 1; \ + dir1=$$subdir; dir2="$(distdir)/$$subdir"; \ + $(am__relativize); \ + new_distdir=$$reldir; \ + dir1=$$subdir; dir2="$(top_distdir)"; \ + $(am__relativize); \ + new_top_distdir=$$reldir; \ + echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \ + echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \ + ($(am__cd) $$subdir && \ + $(MAKE) $(AM_MAKEFLAGS) \ + top_distdir="$$new_top_distdir" \ + distdir="$$new_distdir" \ + am__remove_distdir=: \ + am__skip_length_check=: \ + am__skip_mode_fix=: \ + distdir) \ + || exit 1; \ + fi; \ + done + -test -n "$(am__skip_mode_fix)" \ + || find "$(distdir)" -type d ! -perm -755 \ + -exec chmod u+rwx,go+rx {} \; -o \ + ! -type d ! -perm -444 -links 1 -exec chmod a+r {} \; -o \ + ! -type d ! -perm -400 -exec chmod a+r {} \; -o \ + ! -type d ! -perm -444 -exec $(install_sh) -c -m a+r {} {} \; \ + || chmod -R a+r "$(distdir)" +dist-gzip: distdir + tardir=$(distdir) && $(am__tar) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).tar.gz + $(am__post_remove_distdir) + +dist-bzip2: distdir + tardir=$(distdir) && $(am__tar) | BZIP2=$${BZIP2--9} bzip2 -c >$(distdir).tar.bz2 + $(am__post_remove_distdir) + +dist-lzip: distdir + tardir=$(distdir) && $(am__tar) | lzip -c $${LZIP_OPT--9} >$(distdir).tar.lz + $(am__post_remove_distdir) + +dist-xz: distdir + tardir=$(distdir) && $(am__tar) | XZ_OPT=$${XZ_OPT--e} xz -c >$(distdir).tar.xz + $(am__post_remove_distdir) + +dist-tarZ: distdir + @echo WARNING: "Support for shar distribution archives is" \ + "deprecated." >&2 + @echo WARNING: "It will be removed altogether in Automake 2.0" >&2 + tardir=$(distdir) && $(am__tar) | compress -c >$(distdir).tar.Z + $(am__post_remove_distdir) + +dist-shar: distdir + @echo WARNING: "Support for distribution archives compressed with" \ + "legacy program 'compress' is deprecated." >&2 + @echo WARNING: "It will be removed altogether in Automake 2.0" >&2 + shar $(distdir) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).shar.gz + $(am__post_remove_distdir) + +dist-zip: distdir + -rm -f $(distdir).zip + zip -rq $(distdir).zip $(distdir) + $(am__post_remove_distdir) + +dist dist-all: + $(MAKE) $(AM_MAKEFLAGS) $(DIST_TARGETS) am__post_remove_distdir='@:' + $(am__post_remove_distdir) + +# This target untars the dist file and tries a VPATH configuration. Then +# it guarantees that the distribution is self-contained by making another +# tarfile. +distcheck: dist + case '$(DIST_ARCHIVES)' in \ + *.tar.gz*) \ + GZIP=$(GZIP_ENV) gzip -dc $(distdir).tar.gz | $(am__untar) ;;\ + *.tar.bz2*) \ + bzip2 -dc $(distdir).tar.bz2 | $(am__untar) ;;\ + *.tar.lz*) \ + lzip -dc $(distdir).tar.lz | $(am__untar) ;;\ + *.tar.xz*) \ + xz -dc $(distdir).tar.xz | $(am__untar) ;;\ + *.tar.Z*) \ + uncompress -c $(distdir).tar.Z | $(am__untar) ;;\ + *.shar.gz*) \ + GZIP=$(GZIP_ENV) gzip -dc $(distdir).shar.gz | unshar ;;\ + *.zip*) \ + unzip $(distdir).zip ;;\ + esac + chmod -R a-w $(distdir) + chmod u+w $(distdir) + mkdir $(distdir)/_build $(distdir)/_inst + chmod a-w $(distdir) + test -d $(distdir)/_build || exit 0; \ + dc_install_base=`$(am__cd) $(distdir)/_inst && pwd | sed -e 's,^[^:\\/]:[\\/],/,'` \ + && dc_destdir="$${TMPDIR-/tmp}/am-dc-$$$$/" \ + && am__cwd=`pwd` \ + && $(am__cd) $(distdir)/_build \ + && ../configure \ + $(AM_DISTCHECK_CONFIGURE_FLAGS) \ + $(DISTCHECK_CONFIGURE_FLAGS) \ + --srcdir=.. --prefix="$$dc_install_base" \ + && $(MAKE) $(AM_MAKEFLAGS) \ + && $(MAKE) $(AM_MAKEFLAGS) dvi \ + && $(MAKE) $(AM_MAKEFLAGS) check \ + && $(MAKE) $(AM_MAKEFLAGS) install \ + && $(MAKE) $(AM_MAKEFLAGS) installcheck \ + && $(MAKE) $(AM_MAKEFLAGS) uninstall \ + && $(MAKE) $(AM_MAKEFLAGS) distuninstallcheck_dir="$$dc_install_base" \ + distuninstallcheck \ + && chmod -R a-w "$$dc_install_base" \ + && ({ \ + (cd ../.. && umask 077 && mkdir "$$dc_destdir") \ + && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" install \ + && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" uninstall \ + && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" \ + distuninstallcheck_dir="$$dc_destdir" distuninstallcheck; \ + } || { rm -rf "$$dc_destdir"; exit 1; }) \ + && rm -rf "$$dc_destdir" \ + && $(MAKE) $(AM_MAKEFLAGS) dist \ + && rm -rf $(DIST_ARCHIVES) \ + && $(MAKE) $(AM_MAKEFLAGS) distcleancheck \ + && cd "$$am__cwd" \ + || exit 1 + $(am__post_remove_distdir) + @(echo "$(distdir) archives ready for distribution: "; \ + list='$(DIST_ARCHIVES)'; for i in $$list; do echo $$i; done) | \ + sed -e 1h -e 1s/./=/g -e 1p -e 1x -e '$$p' -e '$$x' +distuninstallcheck: + @test -n '$(distuninstallcheck_dir)' || { \ + echo 'ERROR: trying to run $@ with an empty' \ + '$$(distuninstallcheck_dir)' >&2; \ + exit 1; \ + }; \ + $(am__cd) '$(distuninstallcheck_dir)' || { \ + echo 'ERROR: cannot chdir into $(distuninstallcheck_dir)' >&2; \ + exit 1; \ + }; \ + test `$(am__distuninstallcheck_listfiles) | wc -l` -eq 0 \ + || { echo "ERROR: files left after uninstall:" ; \ + if test -n "$(DESTDIR)"; then \ + echo " (check DESTDIR support)"; \ + fi ; \ + $(distuninstallcheck_listfiles) ; \ + exit 1; } >&2 +distcleancheck: distclean + @if test '$(srcdir)' = . ; then \ + echo "ERROR: distcleancheck can only run from a VPATH build" ; \ + exit 1 ; \ + fi + @test `$(distcleancheck_listfiles) | wc -l` -eq 0 \ + || { echo "ERROR: files left in build directory after distclean:" ; \ + $(distcleancheck_listfiles) ; \ + exit 1; } >&2 +check-am: all-am +check: check-recursive +all-am: Makefile $(DATA) config.h +installdirs: installdirs-recursive +installdirs-am: + for dir in "$(DESTDIR)$(docdir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: install-recursive +install-exec: install-exec-recursive +install-data: install-data-recursive +uninstall: uninstall-recursive + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-recursive +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-recursive + +clean-am: clean-generic clean-libtool mostlyclean-am + +distclean: distclean-recursive + -rm -f $(am__CONFIG_DISTCLEAN_FILES) + -rm -f Makefile +distclean-am: clean-am distclean-generic distclean-hdr \ + distclean-libtool distclean-tags + +dvi: dvi-recursive + +dvi-am: + +html: html-recursive + +html-am: + +info: info-recursive + +info-am: + +install-data-am: install-dist_docDATA + +install-dvi: install-dvi-recursive + +install-dvi-am: + +install-exec-am: + +install-html: install-html-recursive + +install-html-am: + +install-info: install-info-recursive + +install-info-am: + +install-man: + +install-pdf: install-pdf-recursive + +install-pdf-am: + +install-ps: install-ps-recursive + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-recursive + -rm -f $(am__CONFIG_DISTCLEAN_FILES) + -rm -rf $(top_srcdir)/autom4te.cache + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-recursive + +mostlyclean-am: mostlyclean-generic mostlyclean-libtool + +pdf: pdf-recursive + +pdf-am: + +ps: ps-recursive + +ps-am: + +uninstall-am: uninstall-dist_docDATA + +.MAKE: $(am__recursive_targets) all install-am install-strip + +.PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am \ + am--refresh check check-am clean clean-cscope clean-generic \ + clean-libtool cscope cscopelist-am ctags ctags-am dist \ + dist-all dist-bzip2 dist-gzip dist-lzip dist-shar dist-tarZ \ + dist-xz dist-zip distcheck distclean distclean-generic \ + distclean-hdr distclean-libtool distclean-tags distcleancheck \ + distdir distuninstallcheck dvi dvi-am html html-am info \ + info-am install install-am install-data install-data-am \ + install-dist_docDATA install-dvi install-dvi-am install-exec \ + install-exec-am install-html install-html-am install-info \ + install-info-am install-man install-pdf install-pdf-am \ + install-ps install-ps-am install-strip installcheck \ + installcheck-am installdirs installdirs-am maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-generic \ + mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \ + uninstall-am uninstall-dist_docDATA + + +.PHONY: clang-format + +# Format source files using clang-format. Don't format source files +# under third-party directory since we are not responsible for thier +# coding style. +clang-format: + CLANGFORMAT=`git config --get clangformat.binary`; \ + test -z $${CLANGFORMAT} && CLANGFORMAT="clang-format"; \ + $${CLANGFORMAT} -i lib/*.{c,h} lib/includes/nghttp2/*.h \ + src/*.{c,cc,h} src/includes/nghttp2/*.h examples/*.{c,cc} \ + tests/*.{c,h} + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/NEWS b/NEWS new file mode 100644 index 0000000..e69de29 diff --git a/README b/README new file mode 100644 index 0000000..5ccc0ea --- /dev/null +++ b/README @@ -0,0 +1 @@ +See README.rst diff --git a/README.rst b/README.rst new file mode 100644 index 0000000..82e8ecc --- /dev/null +++ b/README.rst @@ -0,0 +1,1427 @@ +nghttp2 - HTTP/2 C Library +========================== + +This is an implementation of the Hypertext Transfer Protocol version 2 +in C. + +The framing layer of HTTP/2 is implemented as a reusable C +library. On top of that, we have implemented an HTTP/2 client, server +and proxy. We have also developed load test and benchmarking tools for +HTTP/2 and SPDY. + +An HPACK encoder and decoder are available as a public API. + +An experimental high level C++ library is also available. + +We have Python bindings of this library, but we do not have full +code coverage yet. + +Development Status +------------------ + +We have implemented `RFC 7540 `_ +HTTP/2 and `RFC 7541 `_ HPACK - +Header Compression for HTTP/2 + +The nghttp2 code base was forked from the spdylay +(https://github.com/tatsuhiro-t/spdylay) project. + +Public Test Server +------------------ + +The following endpoints are available to try out our nghttp2 +implementation. + +* https://nghttp2.org/ (TLS + ALPN/NPN) + + This endpoint supports ``h2``, ``h2-16``, ``h2-14``, ``spdy/3.1`` + and ``http/1.1`` via ALPN/NPN and requires TLSv1.2 for HTTP/2 + connection. + +* http://nghttp2.org/ (HTTP Upgrade and HTTP/2 Direct) + + ``h2c`` and ``http/1.1``. + +Requirements +------------ + +The following package is required to build the libnghttp2 library: + +* pkg-config >= 0.20 + +To build and run the unit test programs, the following package is +required: + +* cunit >= 2.1 + +To build the documentation, you need to install: + +* sphinx (http://sphinx-doc.org/) + +To build and run the application programs (``nghttp``, ``nghttpd`` and +``nghttpx``) in the ``src`` directory, the following packages are +required: + +* OpenSSL >= 1.0.1 +* libev >= 4.15 +* zlib >= 1.2.3 + +ALPN support requires OpenSSL >= 1.0.2 (released 22 January 2015). + +To enable the SPDY protocol in the application program ``nghttpx`` and +``h2load``, the following package is required: + +* spdylay >= 1.3.2 + +To enable ``-a`` option (getting linked assets from the downloaded +resource) in ``nghttp``, the following package is required: + +* libxml2 >= 2.7.7 + +The HPACK tools require the following package: + +* jansson >= 2.5 + +To build sources under the examples directory, libevent is required: + +* libevent-openssl >= 2.0.8 + +To mitigate heap fragmentation in long running server programs +(``nghttpd`` and ``nghttpx``), jemalloc is recommended: + +* jemalloc + +libnghttp2_asio C++ library requires the following packages: + +* libboost-dev >= 1.54.0 +* libboost-thread-dev >= 1.54.0 + +The Python bindings require the following packages: + +* cython >= 0.19 +* python >= 2.7 + +If you are using Ubuntu 14.04 LTS (trusty), run the following to install the needed packages:: + + sudo apt-get install make binutils autoconf automake autotools-dev libtool pkg-config \ + zlib1g-dev libcunit1-dev libssl-dev libxml2-dev libev-dev libevent-dev libjansson-dev \ + libjemalloc-dev cython python3.4-dev + +spdylay is not packaged in Ubuntu, so you need to build it yourself: +http://tatsuhiro-t.github.io/spdylay/ + +Building 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 + +To compile the source code, gcc >= 4.8.3 or clang >= 3.4 is required. + +.. note:: + + Mac OS X users may need the ``--disable-threads`` configure option to + disable multi-threading in nghttpd, nghttpx and h2load to prevent + them from crashing. A patch is welcome to make multi threading work + on Mac OS X platform. + +Notes for building on Windows (Mingw/Cygwin) +-------------------------------------------- + +Under Mingw environment, you can only compile the library, it's +``libnghttp2-X.dll`` and ``libnghttp2.a``. + +If you want to compile the applications(``h2load``, ``nghttp``, +``nghttpx``, ``nghttpd``), you need to use the Cygwin environment. + +Under Cygwin environment, to compile the applications you need to +compile and install the libev first. + +Secondly, you need to undefine the macro ``__STRICT_ANSI__``, if you +not, the functions ``fdopen``, ``fileno`` and ``strptime`` will not +available. + +the sample command like this:: + + $ export CFLAGS="-U__STRICT_ANSI__ -I$libev_PREFIX/include -L$libev_PREFIX/lib" + $ export CXXFLAGS=$CFLAGS + $ ./configure + $ make + +If you want to compile the applications under ``examples/``, you need +to remove or rename the ``event.h`` from libev's installation, because +it conflicts with libevent's installation. + +Building the documentation +-------------------------- + +.. note:: + + Documentation is still incomplete. + +To build the documentation, run:: + + $ make html + +The documents will be generated under ``doc/manual/html/``. + +The generated documents will not be installed with ``make install``. + +The online documentation is available at +https://nghttp2.org/documentation/ + +Unit tests +---------- + +Unit tests are done by simply running ``make check``. + +Integration tests +----------------- + +We have the integration tests for the nghttpx proxy server. The tests are +written in the `Go programming language `_ and uses +its testing framework. We depend on the following libraries: + +* https://github.com/bradfitz/http2 +* https://github.com/tatsuhiro-t/go-nghttp2 +* https://golang.org/x/net/spdy + +To download the above packages, after settings ``GOPATH``, run the +following command under ``integration-tests`` directory:: + + $ make itprep + +To run the tests, run the following command under +``integration-tests`` directory:: + + $ make it + +Inside the tests, we use port 3009 to run the test subject server. + +Migration from v0.7.15 or earlier +--------------------------------- + +nghttp2 v1.0.0 introduced several backward incompatible changes. In +this section, we describe these changes and how to migrate to v1.0.0. + +ALPN protocol ID is now ``h2`` and ``h2c`` +++++++++++++++++++++++++++++++++++++++++++ + +Previously we announced ``h2-14`` and ``h2c-14``. v1.0.0 implements +final protocol version, and we changed ALPN ID to ``h2`` and ``h2c``. +The macros ``NGHTTP2_PROTO_VERSION_ID``, +``NGHTTP2_PROTO_VERSION_ID_LEN``, +``NGHTTP2_CLEARTEXT_PROTO_VERSION_ID``, and +``NGHTTP2_CLEARTEXT_PROTO_VERSION_ID_LEN`` have been updated to +reflect this change. + +Basically, existing applications do not have to do anything, just +recompiling is enough for this change. + +Use word "client magic" where we use "client connection preface" +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +We use "client connection preface" to mean first 24 bytes of client +connection preface. This is technically not correct, since client +connection preface is composed of 24 bytes client magic byte string +followed by SETTINGS frame. For clarification, we call "client magic" +for this 24 bytes byte string and updated API. + +* ``NGHTTP2_CLIENT_CONNECTION_PREFACE`` was replaced with + ``NGHTTP2_CLIENT_MAGIC``. +* ``NGHTTP2_CLIENT_CONNECTION_PREFACE_LEN`` was replaced with + ``NGHTTP2_CLIENT_MAGIC_LEN``. +* ``NGHTTP2_BAD_PREFACE`` was renamed as ``NGHTTP2_BAD_CLIENT_MAGIC`` + +The alreay deprecated ``NGHTTP2_CLIENT_CONNECTION_HEADER`` and +``NGHTTP2_CLIENT_CONNECTION_HEADER_LEN`` were removed. + +If application uses these macros, just replace old ones with new ones. +Since v1.0.0, client magic is sent by library (see next subsection), +so client application may just remove these macro use. + +Client magic is sent by library ++++++++++++++++++++++++++++++++ + +Previously nghttp2 library did not send client magic, which is first +24 bytes byte string of client connection preface, and client +applications have to send it by themselves. Since v1.0.0, client +magic is sent by library via first call of ``nghttp2_session_send()`` +or ``nghttp2_session_mem_send()``. + +The client applications which send client magic must remove the +relevant code. + +Remove HTTP Alternative Services (Alt-Svc) related code ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +Alt-Svc specification is not finalized yet. To make our API stable, +we have decided to remove all Alt-Svc related API from nghttp2. + +* ``NGHTTP2_EXT_ALTSVC`` was removed. +* ``nghttp2_ext_altsvc`` was removed. + +We have already removed the functionality of Alt-Svc in v0.7 series +and they have been essentially noop. The application using these +macro and struct, remove those lines. + +Use nghttp2_error in nghttp2_on_invalid_frame_recv_callback ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +Previously ``nghttp2_on_invalid_frame_recv_cb_called`` took the +``error_code``, defined in ``nghttp2_error_code``, as parameter. But +they are not detailed enough to debug. Therefore, we decided to use +more detailed ``nghttp2_error`` values instead. + +The application using this callback should update the callback +signature. If it treats ``error_code`` as HTTP/2 error code, update +the code so that it is treated as ``nghttp2_error``. + +Receive client magic by default ++++++++++++++++++++++++++++++++ + +Previously nghttp2 did not process client magic (24 bytes byte +string). To make it deal with it, we had to use +``nghttp2_option_set_recv_client_preface()``. Since v1.0.0, nghttp2 +processes client magic by default and +``nghttp2_option_set_recv_client_preface()`` was removed. + +Some application may want to disable this behaviour, so we added +``nghttp2_option_set_no_recv_client_magic()`` to achieve this. + +The application using ``nghttp2_option_set_recv_client_preface()`` +with nonzero value, just remove it. + +The application using ``nghttp2_option_set_recv_client_preface()`` +with zero value or not using it must use +``nghttp2_option_set_no_recv_client_magic()`` with nonzero value. + +Client, Server and Proxy programs +--------------------------------- + +The ``src`` directory contains the HTTP/2 client, server and proxy programs. + +nghttp - client ++++++++++++++++ + +``nghttp`` is a HTTP/2 client. It can connect to the HTTP/2 server +with prior knowledge, HTTP Upgrade and NPN/ALPN TLS extension. + +It has verbose output mode for framing information. Here is sample +output from ``nghttp`` client:: + + $ nghttp -nv https://nghttp2.org + [ 0.067] Connected + The negotiated protocol: h2 + [ 0.135] send SETTINGS frame + (niv=2) + [SETTINGS_MAX_CONCURRENT_STREAMS(0x03):100] + [SETTINGS_INITIAL_WINDOW_SIZE(0x04):65535] + [ 0.135] send PRIORITY frame + (dep_stream_id=0, weight=201, exclusive=0) + [ 0.135] send PRIORITY frame + (dep_stream_id=0, weight=101, exclusive=0) + [ 0.135] send PRIORITY frame + (dep_stream_id=0, weight=1, exclusive=0) + [ 0.135] send PRIORITY frame + (dep_stream_id=7, weight=1, exclusive=0) + [ 0.135] send PRIORITY frame + (dep_stream_id=3, weight=1, exclusive=0) + [ 0.135] send HEADERS frame + ; END_STREAM | END_HEADERS | PRIORITY + (padlen=0, dep_stream_id=11, weight=16, exclusive=0) + ; Open new stream + :method: GET + :path: / + :scheme: https + :authority: nghttp2.org + accept: */* + accept-encoding: gzip, deflate + user-agent: nghttp2/1.0.0-DEV + [ 0.135] recv SETTINGS frame + (niv=2) + [SETTINGS_MAX_CONCURRENT_STREAMS(0x03):100] + [SETTINGS_INITIAL_WINDOW_SIZE(0x04):65535] + [ 0.135] send SETTINGS frame + ; ACK + (niv=0) + [ 0.165] recv SETTINGS frame + ; ACK + (niv=0) + [ 0.166] recv (stream_id=13) :status: 200 + [ 0.166] recv (stream_id=13) date: Fri, 15 May 2015 14:45:22 GMT + [ 0.166] recv (stream_id=13) content-type: text/html + [ 0.166] recv (stream_id=13) last-modified: Fri, 15 May 2015 14:20:46 GMT + [ 0.166] recv (stream_id=13) etag: W/"555600be-1a7f" + [ 0.166] recv (stream_id=13) link: ; rel=preload; as=stylesheet + [ 0.166] recv (stream_id=13) content-encoding: gzip + [ 0.166] recv (stream_id=13) server: nghttpx nghttp2/1.0.0-DEV + [ 0.166] recv (stream_id=13) via: 1.1 nghttpx + [ 0.166] recv (stream_id=13) strict-transport-security: max-age=31536000 + [ 0.166] recv HEADERS frame + ; END_HEADERS + (padlen=0) + ; First response header + [ 0.166] recv (stream_id=13) :method: GET + [ 0.166] recv (stream_id=13) :scheme: https + [ 0.166] recv (stream_id=13) :path: /stylesheets/screen.css + [ 0.166] recv (stream_id=13) :authority: nghttp2.org + [ 0.166] recv (stream_id=13) accept-encoding: gzip, deflate + [ 0.166] recv (stream_id=13) user-agent: nghttp2/1.0.0-DEV + [ 0.166] recv PUSH_PROMISE frame + ; END_HEADERS + (padlen=0, promised_stream_id=2) + [ 0.166] recv DATA frame + ; END_STREAM + [ 0.167] recv (stream_id=2) :status: 200 + [ 0.167] recv (stream_id=2) date: Fri, 15 May 2015 14:45:22 GMT + [ 0.167] recv (stream_id=2) content-type: text/css + [ 0.167] recv (stream_id=2) last-modified: Fri, 15 May 2015 14:20:46 GMT + [ 0.167] recv (stream_id=2) etag: W/"555600be-9845" + [ 0.167] recv (stream_id=2) content-encoding: gzip + [ 0.167] recv (stream_id=2) server: nghttpx nghttp2/1.0.0-DEV + [ 0.167] recv (stream_id=2) via: 1.1 nghttpx + [ 0.167] recv (stream_id=2) strict-transport-security: max-age=31536000 + [ 0.167] recv HEADERS frame + ; END_HEADERS + (padlen=0) + ; First push response header + [ 0.196] recv DATA frame + ; END_STREAM + [ 0.196] send GOAWAY frame + (last_stream_id=2, error_code=NO_ERROR(0x00), opaque_data(0)=[]) + +The HTTP Upgrade is performed like so:: + + $ nghttp -nvu http://nghttp2.org + [ 0.137] Connected + [ 0.137] HTTP Upgrade request + GET / HTTP/1.1 + Host: nghttp2.org + Connection: Upgrade, HTTP2-Settings + Upgrade: h2c + HTTP2-Settings: AAMAAABkAAQAAP__ + Accept: */* + User-Agent: nghttp2/1.0.0-DEV + + + [ 0.156] HTTP Upgrade response + HTTP/1.1 101 Switching Protocols + Connection: Upgrade + Upgrade: h2c + + + [ 0.156] HTTP Upgrade success + [ 0.157] send SETTINGS frame + (niv=2) + [SETTINGS_MAX_CONCURRENT_STREAMS(0x03):100] + [SETTINGS_INITIAL_WINDOW_SIZE(0x04):65535] + [ 0.157] send PRIORITY frame + (dep_stream_id=0, weight=201, exclusive=0) + [ 0.157] send PRIORITY frame + (dep_stream_id=0, weight=101, exclusive=0) + [ 0.157] send PRIORITY frame + (dep_stream_id=0, weight=1, exclusive=0) + [ 0.157] send PRIORITY frame + (dep_stream_id=7, weight=1, exclusive=0) + [ 0.157] send PRIORITY frame + (dep_stream_id=3, weight=1, exclusive=0) + [ 0.157] send PRIORITY frame + (dep_stream_id=11, weight=16, exclusive=0) + [ 0.157] recv SETTINGS frame + (niv=2) + [SETTINGS_MAX_CONCURRENT_STREAMS(0x03):100] + [SETTINGS_INITIAL_WINDOW_SIZE(0x04):65535] + [ 0.157] recv (stream_id=1) :status: 200 + [ 0.157] recv (stream_id=1) date: Fri, 15 May 2015 14:46:08 GMT + [ 0.157] recv (stream_id=1) content-type: text/html + [ 0.157] recv (stream_id=1) content-length: 6783 + [ 0.157] recv (stream_id=1) last-modified: Fri, 15 May 2015 14:20:46 GMT + [ 0.157] recv (stream_id=1) etag: "555600be-1a7f" + [ 0.157] recv (stream_id=1) link: ; rel=preload; as=stylesheet + [ 0.157] recv (stream_id=1) accept-ranges: bytes + [ 0.157] recv (stream_id=1) server: nghttpx nghttp2/1.0.0-DEV + [ 0.157] recv (stream_id=1) via: 1.1 nghttpx + [ 0.157] recv HEADERS frame + ; END_HEADERS + (padlen=0) + ; First response header + [ 0.157] recv (stream_id=1) :method: GET + [ 0.157] recv (stream_id=1) :scheme: http + [ 0.157] recv (stream_id=1) :path: /stylesheets/screen.css + [ 0.157] recv (stream_id=1) host: nghttp2.org + [ 0.157] recv (stream_id=1) user-agent: nghttp2/1.0.0-DEV + [ 0.157] recv PUSH_PROMISE frame + ; END_HEADERS + (padlen=0, promised_stream_id=2) + [ 0.157] send SETTINGS frame + ; ACK + (niv=0) + [ 0.161] recv DATA frame + ; END_STREAM + [ 0.162] recv (stream_id=2) :status: 200 + [ 0.162] recv (stream_id=2) date: Fri, 15 May 2015 14:46:08 GMT + [ 0.162] recv (stream_id=2) content-type: text/css + [ 0.162] recv (stream_id=2) content-length: 38981 + [ 0.162] recv (stream_id=2) last-modified: Fri, 15 May 2015 14:20:46 GMT + [ 0.162] recv (stream_id=2) etag: "555600be-9845" + [ 0.162] recv (stream_id=2) accept-ranges: bytes + [ 0.162] recv (stream_id=2) server: nghttpx nghttp2/1.0.0-DEV + [ 0.162] recv (stream_id=2) via: 1.1 nghttpx + [ 0.162] recv HEADERS frame + ; END_HEADERS + (padlen=0) + ; First push response header + [ 0.191] recv DATA frame + [ 0.215] recv DATA frame + [ 0.215] send WINDOW_UPDATE frame + (window_size_increment=33322) + [ 0.238] send WINDOW_UPDATE frame + (window_size_increment=33549) + [ 0.238] recv DATA frame + ; END_STREAM + [ 0.238] recv SETTINGS frame + ; ACK + (niv=0) + [ 0.238] send GOAWAY frame + (last_stream_id=2, error_code=NO_ERROR(0x00), opaque_data(0)=[]) + +Using the ``-s`` option, ``nghttp`` prints out some timing information for +requests, sorted by completion time:: + + $ nghttp -nas https://nghttp2.org/ + ***** Statistics ***** + + Request timing: + responseEnd: the time when last byte of response was received + relative to connectEnd + requestStart: the time just before first byte of request was sent + relative to connectEnd. If '*' is shown, this was + pushed by server. + process: responseEnd - requestStart + code: HTTP status code + size: number of bytes received as response body without + inflation. + URI: request URI + + see http://www.w3.org/TR/resource-timing/#processing-model + + sorted by 'complete' + + id responseEnd requestStart process code size request path + 13 +37.19ms +280us 36.91ms 200 2K / + 2 +72.65ms * +36.38ms 36.26ms 200 8K /stylesheets/screen.css + 17 +77.43ms +38.67ms 38.75ms 200 3K /javascripts/octopress.js + 15 +78.12ms +38.66ms 39.46ms 200 3K /javascripts/modernizr-2.0.js + +Using the ``-r`` option, ``nghttp`` writes more detailed timing data to +the given file in HAR format. + +nghttpd - server +++++++++++++++++ + +``nghttpd`` is a multi-threaded static web server. + +By default, it uses SSL/TLS connection. Use ``--no-tls`` option to +disable it. + +``nghttpd`` only accepts HTTP/2 connections via NPN/ALPN or direct +HTTP/2 connections. No HTTP Upgrade is supported. + +The ``-p`` option allows users to configure server push. + +Just like ``nghttp``, it has a verbose output mode for framing +information. Here is sample output from ``nghttpd``:: + + $ nghttpd --no-tls -v 8080 + IPv4: listen 0.0.0.0:8080 + IPv6: listen :::8080 + [id=1] [ 1.521] send SETTINGS frame + (niv=1) + [SETTINGS_MAX_CONCURRENT_STREAMS(0x03):100] + [id=1] [ 1.521] recv SETTINGS frame + (niv=2) + [SETTINGS_MAX_CONCURRENT_STREAMS(0x03):100] + [SETTINGS_INITIAL_WINDOW_SIZE(0x04):65535] + [id=1] [ 1.521] recv SETTINGS frame + ; ACK + (niv=0) + [id=1] [ 1.521] recv PRIORITY frame + (dep_stream_id=0, weight=201, exclusive=0) + [id=1] [ 1.521] recv PRIORITY frame + (dep_stream_id=0, weight=101, exclusive=0) + [id=1] [ 1.521] recv PRIORITY frame + (dep_stream_id=0, weight=1, exclusive=0) + [id=1] [ 1.521] recv PRIORITY frame + (dep_stream_id=7, weight=1, exclusive=0) + [id=1] [ 1.521] recv PRIORITY frame + (dep_stream_id=3, weight=1, exclusive=0) + [id=1] [ 1.521] recv (stream_id=13) :method: GET + [id=1] [ 1.521] recv (stream_id=13) :path: / + [id=1] [ 1.521] recv (stream_id=13) :scheme: http + [id=1] [ 1.521] recv (stream_id=13) :authority: localhost:8080 + [id=1] [ 1.521] recv (stream_id=13) accept: */* + [id=1] [ 1.521] recv (stream_id=13) accept-encoding: gzip, deflate + [id=1] [ 1.521] recv (stream_id=13) user-agent: nghttp2/1.0.0-DEV + [id=1] [ 1.521] recv HEADERS frame + ; END_STREAM | END_HEADERS | PRIORITY + (padlen=0, dep_stream_id=11, weight=16, exclusive=0) + ; Open new stream + [id=1] [ 1.521] send SETTINGS frame + ; ACK + (niv=0) + [id=1] [ 1.521] send HEADERS frame + ; END_HEADERS + (padlen=0) + ; First response header + :status: 200 + server: nghttpd nghttp2/1.0.0-DEV + content-length: 10 + cache-control: max-age=3600 + date: Fri, 15 May 2015 14:49:04 GMT + last-modified: Tue, 30 Sep 2014 12:40:52 GMT + [id=1] [ 1.522] send DATA frame + ; END_STREAM + [id=1] [ 1.522] stream_id=13 closed + [id=1] [ 1.522] recv GOAWAY frame + (last_stream_id=0, error_code=NO_ERROR(0x00), opaque_data(0)=[]) + [id=1] [ 1.522] closed + +nghttpx - proxy ++++++++++++++++ + +``nghttpx`` is a multi-threaded reverse proxy for HTTP/2, SPDY and +HTTP/1.1, and powers http://nghttp2.org and supports HTTP/2 server +push. + +``nghttpx`` implements `important performance-oriented features +`_ in TLS, such as +session IDs, session tickets (with automatic key rotation), OCSP +stapling, dynamic record sizing, ALPN/NPN, forward secrecy and SPDY & +HTTP/2. + +``nghttpx`` has several operational modes: + +================== ============================ ============== ============= +Mode option Frontend Backend Note +================== ============================ ============== ============= +default mode HTTP/2, SPDY, HTTP/1.1 (TLS) HTTP/1.1 Reverse proxy +``--http2-proxy`` HTTP/2, SPDY, HTTP/1.1 (TLS) HTTP/1.1 SPDY proxy +``--http2-bridge`` HTTP/2, SPDY, HTTP/1.1 (TLS) HTTP/2 (TLS) +``--client`` HTTP/2, HTTP/1.1 HTTP/2 (TLS) +``--client-proxy`` HTTP/2, HTTP/1.1 HTTP/2 (TLS) Forward proxy +================== ============================ ============== ============= + +The interesting mode at the moment is the default mode. It works like +a reverse proxy and listens for HTTP/2, SPDY and HTTP/1.1 and can be +deployed as a SSL/TLS terminator for existing web server. + +The default mode, ``--http2-proxy`` and ``--http2-bridge`` modes use +SSL/TLS in the frontend connection by default. To disable SSL/TLS, +use the ``--frontend-no-tls`` option. If that option is used, SPDY is +disabled in the frontend and incoming HTTP/1.1 connections can be +upgraded to HTTP/2 through HTTP Upgrade. + +The ``--http2-bridge``, ``--client`` and ``--client-proxy`` modes use +SSL/TLS in the backend connection by default. To disable SSL/TLS, use +the ``--backend-no-tls`` option. + +``nghttpx`` supports a configuration file. See the ``--conf`` option and +sample configuration file ``nghttpx.conf.sample``. + +In the default mode, (without any of ``--http2-proxy``, +``--http2-bridge``, ``--client-proxy`` and ``--client`` options), +``nghttpx`` works as reverse proxy to the backend server:: + + Client <-- (HTTP/2, SPDY, HTTP/1.1) --> nghttpx <-- (HTTP/1.1) --> Web Server + [reverse proxy] + +With the ``--http2-proxy`` option, it works as a so called secure proxy (aka +SPDY proxy):: + + Client <-- (HTTP/2, SPDY, HTTP/1.1) --> nghttpx <-- (HTTP/1.1) --> Proxy + [secure proxy] (e.g., Squid, ATS) + +The ``Client`` in the above example needs to be configured to use +``nghttpx`` as secure proxy. + +At the time of this writing, Chrome is the only browser which supports +secure proxy. One way to configure Chrome to use a secure proxy is +to create a proxy.pac script like this: + +.. code-block:: javascript + + function FindProxyForURL(url, host) { + return "HTTPS SERVERADDR:PORT"; + } + +``SERVERADDR`` and ``PORT`` is the hostname/address and port of the +machine nghttpx is running on. Please note that Chrome requires a valid +certificate for secure proxy. + +Then run Chrome with the following arguments:: + + $ google-chrome --proxy-pac-url=file:///path/to/proxy.pac --use-npn + +With ``--http2-bridge``, it accepts HTTP/2, SPDY and HTTP/1.1 +connections and communicates with the backend in HTTP/2:: + + Client <-- (HTTP/2, SPDY, HTTP/1.1) --> nghttpx <-- (HTTP/2) --> Web or HTTP/2 Proxy etc + (e.g., nghttpx -s) + +With ``--client-proxy``, it works as a forward proxy and expects +that the backend is an HTTP/2 proxy:: + + Client <-- (HTTP/2, HTTP/1.1) --> nghttpx <-- (HTTP/2) --> HTTP/2 Proxy + [forward proxy] (e.g., nghttpx -s) + +The ``Client`` needs to be configured to use nghttpx as a forward +proxy. The frontend HTTP/1.1 connection can be upgraded to HTTP/2 +through HTTP Upgrade. With the above configuration, one can use +HTTP/1.1 client to access and test their HTTP/2 servers. + +With ``--client``, it works as a reverse proxy and expects that +the backend is an HTTP/2 Web server:: + + Client <-- (HTTP/2, HTTP/1.1) --> nghttpx <-- (HTTP/2) --> Web Server + [reverse proxy] + +The frontend HTTP/1.1 connection can be upgraded to HTTP/2 +through HTTP Upgrade. + +For the operation modes which talk to the backend in HTTP/2 over +SSL/TLS, the backend connections can be tunneled through an HTTP proxy. +The proxy is specified using ``--backend-http-proxy-uri``. The +following figure illustrates the example of the ``--http2-bridge`` and +``--backend-http-proxy-uri`` options to talk to the outside HTTP/2 +proxy through an HTTP proxy:: + + Client <-- (HTTP/2, SPDY, HTTP/1.1) --> nghttpx <-- (HTTP/2) -- + + --===================---> HTTP/2 Proxy + (HTTP proxy tunnel) (e.g., nghttpx -s) + +Benchmarking tool +----------------- + +The ``h2load`` program is a benchmarking tool for HTTP/2 and SPDY. +The SPDY support is enabled if the program was built with the spdylay +library. The UI of ``h2load`` is heavily inspired by ``weighttp`` +(https://github.com/lighttpd/weighttp). The typical usage is as +follows:: + + $ h2load -n100000 -c100 -m100 https://localhost:8443/ + starting benchmark... + spawning thread #0: 100 concurrent clients, 100000 total requests + Protocol: TLSv1.2 + Cipher: ECDHE-RSA-AES128-GCM-SHA256 + Server Temp Key: ECDH P-256 256 bits + progress: 10% done + progress: 20% done + progress: 30% done + progress: 40% done + progress: 50% done + progress: 60% done + progress: 70% done + progress: 80% done + progress: 90% done + progress: 100% done + + finished in 771.26ms, 129658 req/s, 4.71MB/s + requests: 100000 total, 100000 started, 100000 done, 100000 succeeded, 0 failed, 0 errored + status codes: 100000 2xx, 0 3xx, 0 4xx, 0 5xx + traffic: 3812300 bytes total, 1009900 bytes headers, 1000000 bytes data + min max mean sd +/- sd + time for request: 25.12ms 124.55ms 51.07ms 15.36ms 84.87% + time for connect: 208.94ms 254.67ms 241.38ms 7.95ms 63.00% + time to 1st byte: 209.11ms 254.80ms 241.51ms 7.94ms 63.00% + +The above example issued total 100,000 requests, using 100 concurrent +clients (in other words, 100 HTTP/2 sessions), and a maximum of 100 streams +per client. With the ``-t`` option, ``h2load`` will use multiple native +threads to avoid saturating a single core on client side. + +.. warning:: + + **Don't use this tool against publicly available servers.** That is + considered a DOS attack. Please only use it against your private + servers. + +HPACK tools +----------- + +The ``src`` directory contains the HPACK tools. The ``deflatehd`` program is a +command-line header compression tool. The ``inflatehd`` program is a +command-line header decompression tool. Both tools read input from +stdin and write output to stdout. Errors are written to stderr. +They take JSON as input and output. We (mostly) use the same JSON data +format described at https://github.com/http2jp/hpack-test-case. + +deflatehd - header compressor ++++++++++++++++++++++++++++++ + +The ``deflatehd`` program reads JSON data or HTTP/1-style header fields from +stdin and outputs compressed header block in JSON. + +For the JSON input, the root JSON object must include a ``cases`` key. +Its value has to include the sequence of input header set. They share +the same compression context and are processed in the order they +appear. Each item in the sequence is a JSON object and it must +include a ``headers`` key. Its value is an array of JSON objects, +which includes exactly one name/value pair. + +Example: + +.. code-block:: json + + { + "cases": + [ + { + "headers": [ + { ":method": "GET" }, + { ":path": "/" } + ] + }, + { + "headers": [ + { ":method": "POST" }, + { ":path": "/" } + ] + } + ] + } + + +With the ``-t`` option, the program can accept more familiar HTTP/1 style +header field blocks. Each header set is delimited by an empty line: + +Example:: + + :method: GET + :scheme: https + :path: / + + :method: POST + user-agent: nghttp2 + +The output is in JSON object. It should include a ``cases`` key and its +value is an array of JSON objects, which has at least the following keys: + +seq + The index of header set in the input. + +input_length + The sum of the length of the name/value pairs in the input. + +output_length + The length of the compressed header block. + +percentage_of_original_size + ``input_length`` / ``output_length`` * 100 + +wire + The compressed header block as a hex string. + +headers + The input header set. + +header_table_size + The header table size adjusted before deflating the header set. + +Examples: + +.. code-block:: json + + { + "cases": + [ + { + "seq": 0, + "input_length": 66, + "output_length": 20, + "percentage_of_original_size": 30.303030303030305, + "wire": "01881f3468e5891afcbf83868a3d856659c62e3f", + "headers": [ + { + ":authority": "example.org" + }, + { + ":method": "GET" + }, + { + ":path": "/" + }, + { + ":scheme": "https" + }, + { + "user-agent": "nghttp2" + } + ], + "header_table_size": 4096 + } + , + { + "seq": 1, + "input_length": 74, + "output_length": 10, + "percentage_of_original_size": 13.513513513513514, + "wire": "88448504252dd5918485", + "headers": [ + { + ":authority": "example.org" + }, + { + ":method": "POST" + }, + { + ":path": "/account" + }, + { + ":scheme": "https" + }, + { + "user-agent": "nghttp2" + } + ], + "header_table_size": 4096 + } + ] + } + + +The output can be used as the input for ``inflatehd`` and +``deflatehd``. + +With the ``-d`` option, the extra ``header_table`` key is added and its +associated value includes the state of dynamic header table after the +corresponding header set was processed. The value includes at least +the following keys: + +entries + The entry in the header table. If ``referenced`` is ``true``, it + is in the reference set. The ``size`` includes the overhead (32 + bytes). The ``index`` corresponds to the index of header table. + The ``name`` is the header field name and the ``value`` is the + header field value. + +size + The sum of the spaces entries occupied, this includes the + entry overhead. + +max_size + The maximum header table size. + +deflate_size + The sum of the spaces entries occupied within + ``max_deflate_size``. + +max_deflate_size + The maximum header table size the encoder uses. This can be smaller + than ``max_size``. In this case, the encoder only uses up to first + ``max_deflate_size`` buffer. Since the header table size is still + ``max_size``, the encoder has to keep track of entries outside the + ``max_deflate_size`` but inside the ``max_size`` and make sure + that they are no longer referenced. + +Example: + +.. code-block:: json + + { + "cases": + [ + { + "seq": 0, + "input_length": 66, + "output_length": 20, + "percentage_of_original_size": 30.303030303030305, + "wire": "01881f3468e5891afcbf83868a3d856659c62e3f", + "headers": [ + { + ":authority": "example.org" + }, + { + ":method": "GET" + }, + { + ":path": "/" + }, + { + ":scheme": "https" + }, + { + "user-agent": "nghttp2" + } + ], + "header_table_size": 4096, + "header_table": { + "entries": [ + { + "index": 1, + "name": "user-agent", + "value": "nghttp2", + "referenced": true, + "size": 49 + }, + { + "index": 2, + "name": ":scheme", + "value": "https", + "referenced": true, + "size": 44 + }, + { + "index": 3, + "name": ":path", + "value": "/", + "referenced": true, + "size": 38 + }, + { + "index": 4, + "name": ":method", + "value": "GET", + "referenced": true, + "size": 42 + }, + { + "index": 5, + "name": ":authority", + "value": "example.org", + "referenced": true, + "size": 53 + } + ], + "size": 226, + "max_size": 4096, + "deflate_size": 226, + "max_deflate_size": 4096 + } + } + , + { + "seq": 1, + "input_length": 74, + "output_length": 10, + "percentage_of_original_size": 13.513513513513514, + "wire": "88448504252dd5918485", + "headers": [ + { + ":authority": "example.org" + }, + { + ":method": "POST" + }, + { + ":path": "/account" + }, + { + ":scheme": "https" + }, + { + "user-agent": "nghttp2" + } + ], + "header_table_size": 4096, + "header_table": { + "entries": [ + { + "index": 1, + "name": ":method", + "value": "POST", + "referenced": true, + "size": 43 + }, + { + "index": 2, + "name": "user-agent", + "value": "nghttp2", + "referenced": true, + "size": 49 + }, + { + "index": 3, + "name": ":scheme", + "value": "https", + "referenced": true, + "size": 44 + }, + { + "index": 4, + "name": ":path", + "value": "/", + "referenced": false, + "size": 38 + }, + { + "index": 5, + "name": ":method", + "value": "GET", + "referenced": false, + "size": 42 + }, + { + "index": 6, + "name": ":authority", + "value": "example.org", + "referenced": true, + "size": 53 + } + ], + "size": 269, + "max_size": 4096, + "deflate_size": 269, + "max_deflate_size": 4096 + } + } + ] + } + +inflatehd - header decompressor ++++++++++++++++++++++++++++++++ + +The ``inflatehd`` program reads JSON data from stdin and outputs decompressed +name/value pairs in JSON. + +The root JSON object must include the ``cases`` key. Its value has to +include the sequence of compressed header blocks. They share the same +compression context and are processed in the order they appear. Each +item in the sequence is a JSON object and it must have at least a +``wire`` key. Its value is a compressed header block as a hex string. + +Example: + +.. code-block:: json + + { + "cases": + [ + { "wire": "8285" }, + { "wire": "8583" } + ] + } + +The output is a JSON object. It should include a ``cases`` key and its +value is an array of JSON objects, which has at least following keys: + +seq + The index of the header set in the input. + +headers + A JSON array that includes decompressed name/value pairs. + +wire + The compressed header block as a hex string. + +header_table_size + The header table size adjusted before inflating compressed header + block. + +Example: + +.. code-block:: json + + { + "cases": + [ + { + "seq": 0, + "wire": "01881f3468e5891afcbf83868a3d856659c62e3f", + "headers": [ + { + ":authority": "example.org" + }, + { + ":method": "GET" + }, + { + ":path": "/" + }, + { + ":scheme": "https" + }, + { + "user-agent": "nghttp2" + } + ], + "header_table_size": 4096 + } + , + { + "seq": 1, + "wire": "88448504252dd5918485", + "headers": [ + { + ":method": "POST" + }, + { + ":path": "/account" + }, + { + "user-agent": "nghttp2" + }, + { + ":scheme": "https" + }, + { + ":authority": "example.org" + } + ], + "header_table_size": 4096 + } + ] + } + +The output can be used as the input for ``deflatehd`` and +``inflatehd``. + +With the ``-d`` option, the extra ``header_table`` key is added and its +associated value includes the state of the dynamic header table after the +corresponding header set was processed. The format is the same as +``deflatehd``. + +libnghttp2_asio: High level HTTP/2 C++ library +---------------------------------------------- + +libnghttp2_asio is C++ library built on top of libnghttp2 and provides +high level abstraction API to build HTTP/2 applications. It depends +on the Boost::ASIO library and OpenSSL. Currently libnghttp2_asio +provides both client and server APIs. + +libnghttp2_asio is not built by default. Use the ``--enable-asio-lib`` +configure flag to build libnghttp2_asio. The required Boost libraries +are: + +* Boost::Asio +* Boost::System +* Boost::Thread + +The server API is designed to build an HTTP/2 server very easily to utilize +C++11 anonymous functions and closures. The bare minimum example of +an HTTP/2 server looks like this: + +.. code-block:: cpp + + #include + + using namespace nghttp2::asio_http2; + using namespace nghttp2::asio_http2::server; + + int main(int argc, char *argv[]) { + boost::system::error_code ec; + http2 server; + + server.handle("/", [](const request &req, const response &res) { + res.write_head(200); + res.end("hello, world\n"); + }); + + if (server.listen_and_serve(ec, "localhost", "3000")) { + std::cerr << "error: " << ec.message() << std::endl; + } + } + +Here is sample code to use the client API: + +.. code-block:: cpp + + #include + + #include + + using boost::asio::ip::tcp; + + using namespace nghttp2::asio_http2; + using namespace nghttp2::asio_http2::client; + + int main(int argc, char *argv[]) { + boost::system::error_code ec; + boost::asio::io_service io_service; + + // connect to localhost:3000 + session sess(io_service, "localhost", "3000"); + + sess.on_connect([&sess](tcp::resolver::iterator endpoint_it) { + boost::system::error_code ec; + + auto req = sess.submit(ec, "GET", "http://localhost:3000/"); + + req->on_response([](const response &res) { + // print status code and response header fields. + std::cerr << "HTTP/2 " << res.status_code() << std::endl; + for (auto &kv : res.header()) { + std::cerr << kv.first << ": " << kv.second.value << "\n"; + } + std::cerr << std::endl; + + res.on_data([](const uint8_t *data, std::size_t len) { + std::cerr.write(reinterpret_cast(data), len); + std::cerr << std::endl; + }); + }); + + req->on_close([&sess](uint32_t error_code) { + // shutdown session after first request was done. + sess.shutdown(); + }); + }); + + sess.on_error([](const boost::system::error_code &ec) { + std::cerr << "error: " << ec.message() << std::endl; + }); + + io_service.run(); + } + +For more details, see the documentation of libnghttp2_asio. + +Python bindings +--------------- + +The ``python`` directory contains nghttp2 Python bindings. The +bindings currently provide HPACK compressor and decompressor classes +and an HTTP/2 server. + +The extension module is called ``nghttp2``. + +``make`` will build the bindings and target Python version is +determined by the ``configure`` script. If the detected Python version is not +what you expect, specify a path to Python executable in a ``PYTHON`` +variable as an argument to configure script (e.g., ``./configure +PYTHON=/usr/bin/python3.4``). + +The following example code illustrates basic usage of the HPACK compressor +and decompressor in Python: + +.. code-block:: python + + import binascii + import nghttp2 + + deflater = nghttp2.HDDeflater() + inflater = nghttp2.HDInflater() + + data = deflater.deflate([(b'foo', b'bar'), + (b'baz', b'buz')]) + print(binascii.b2a_hex(data)) + + hdrs = inflater.inflate(data) + print(hdrs) + +The ``nghttp2.HTTP2Server`` class builds on top of the asyncio event +loop. On construction, *RequestHandlerClass* must be given, which +must be a subclass of ``nghttp2.BaseRequestHandler`` class. + +The ``BaseRequestHandler`` class is used to handle the HTTP/2 stream. +By default, it does nothing. It must be subclassed to handle each +event callback method. + +The first callback method invoked is ``on_headers()``. It is called +when HEADERS frame, which includes the request header fields, has arrived. + +If the request has a request body, ``on_data(data)`` is invoked for each +chunk of received data. + +Once the entire request is received, ``on_request_done()`` is invoked. + +When the stream is closed, ``on_close(error_code)`` is called. + +The application can send a response using ``send_response()`` method. +It can be used in ``on_headers()``, ``on_data()`` or +``on_request_done()``. + +The application can push resources using the ``push()`` method. It must be +used before the ``send_response()`` call. + +The following instance variables are available: + +client_address + Contains a tuple of the form (host, port) referring to the + client's address. + +stream_id + Stream ID of this stream. + +scheme + Scheme of the request URI. This is a value of :scheme header + field. + +method + Method of this stream. This is a value of :method header field. + +host + This is a value of :authority or host header field. + +path + This is a value of :path header field. + +The following example illustrates the HTTP2Server and +BaseRequestHandler usage: + +.. code-block:: python + + #!/usr/bin/env python + + import io, ssl + import nghttp2 + + class Handler(nghttp2.BaseRequestHandler): + + def on_headers(self): + self.push(path='/css/bootstrap.css', + request_headers = [('content-length', '3')], + status=200, + body='foo') + + self.push(path='/js/bootstrap.js', + method='GET', + request_headers = [('content-length', '10')], + status=200, + body='foobarbuzz') + + self.send_response(status=200, + headers = [('content-type', 'text/plain')], + body=io.BytesIO(b'nghttp2-python FTW')) + + ctx = ssl.SSLContext(ssl.PROTOCOL_SSLv23) + ctx.options = ssl.OP_ALL | ssl.OP_NO_SSLv2 + ctx.load_cert_chain('server.crt', 'server.key') + + # give None to ssl to make the server non-SSL/TLS + server = nghttp2.HTTP2Server(('127.0.0.1', 8443), Handler, ssl=ctx) + server.serve_forever() + +Contribution +------------ + +[This text was composed based on 1.2. License section of curl/libcurl +project.] + +When contributing with code, you agree to put your changes and new +code under the same license nghttp2 is already using unless stated and +agreed otherwise. + +When changing existing source code, do not alter the copyright of +the original file(s). The copyright will still be owned by the +original creator(s) or those who have been assigned copyright by the +original author(s). + +By submitting a patch to the nghttp2 project, you (or your employer, as +the case may be) agree to assign the copyright of your submission to us. +.. the above really needs to be reworded to pass legal muster. +We will credit you for your +changes as far as possible, to give credit but also to keep a trace +back to who made what changes. Please always provide us with your +full real name when contributing! + +See `Contribution Guidelines +`_ for more +details. diff --git a/aclocal.m4 b/aclocal.m4 new file mode 100644 index 0000000..44bdb74 --- /dev/null +++ b/aclocal.m4 @@ -0,0 +1,1613 @@ +# generated automatically by aclocal 1.14.1 -*- Autoconf -*- + +# Copyright (C) 1996-2013 Free Software Foundation, Inc. + +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +m4_ifndef([AC_CONFIG_MACRO_DIRS], [m4_defun([_AM_CONFIG_MACRO_DIRS], [])m4_defun([AC_CONFIG_MACRO_DIRS], [_AM_CONFIG_MACRO_DIRS($@)])]) +m4_ifndef([AC_AUTOCONF_VERSION], + [m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl +m4_if(m4_defn([AC_AUTOCONF_VERSION]), [2.69],, +[m4_warning([this file was generated for autoconf 2.69. +You have another version of autoconf. It may work, but is not guaranteed to. +If you have problems, you may need to regenerate the build system entirely. +To do so, use the procedure documented by the package, typically 'autoreconf'.])]) + +# pkg.m4 - Macros to locate and utilise pkg-config. -*- Autoconf -*- +# serial 1 (pkg-config-0.24) +# +# Copyright © 2004 Scott James Remnant . +# +# 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 2 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, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +# PKG_PROG_PKG_CONFIG([MIN-VERSION]) +# ---------------------------------- +AC_DEFUN([PKG_PROG_PKG_CONFIG], +[m4_pattern_forbid([^_?PKG_[A-Z_]+$]) +m4_pattern_allow([^PKG_CONFIG(_(PATH|LIBDIR|SYSROOT_DIR|ALLOW_SYSTEM_(CFLAGS|LIBS)))?$]) +m4_pattern_allow([^PKG_CONFIG_(DISABLE_UNINSTALLED|TOP_BUILD_DIR|DEBUG_SPEW)$]) +AC_ARG_VAR([PKG_CONFIG], [path to pkg-config utility]) +AC_ARG_VAR([PKG_CONFIG_PATH], [directories to add to pkg-config's search path]) +AC_ARG_VAR([PKG_CONFIG_LIBDIR], [path overriding pkg-config's built-in search path]) + +if test "x$ac_cv_env_PKG_CONFIG_set" != "xset"; then + AC_PATH_TOOL([PKG_CONFIG], [pkg-config]) +fi +if test -n "$PKG_CONFIG"; then + _pkg_min_version=m4_default([$1], [0.9.0]) + AC_MSG_CHECKING([pkg-config is at least version $_pkg_min_version]) + if $PKG_CONFIG --atleast-pkgconfig-version $_pkg_min_version; then + AC_MSG_RESULT([yes]) + else + AC_MSG_RESULT([no]) + PKG_CONFIG="" + fi +fi[]dnl +])# PKG_PROG_PKG_CONFIG + +# PKG_CHECK_EXISTS(MODULES, [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) +# +# Check to see whether a particular set of modules exists. Similar +# to PKG_CHECK_MODULES(), but does not set variables or print errors. +# +# Please remember that m4 expands AC_REQUIRE([PKG_PROG_PKG_CONFIG]) +# only at the first occurence in configure.ac, so if the first place +# it's called might be skipped (such as if it is within an "if", you +# have to call PKG_CHECK_EXISTS manually +# -------------------------------------------------------------- +AC_DEFUN([PKG_CHECK_EXISTS], +[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl +if test -n "$PKG_CONFIG" && \ + AC_RUN_LOG([$PKG_CONFIG --exists --print-errors "$1"]); then + m4_default([$2], [:]) +m4_ifvaln([$3], [else + $3])dnl +fi]) + +# _PKG_CONFIG([VARIABLE], [COMMAND], [MODULES]) +# --------------------------------------------- +m4_define([_PKG_CONFIG], +[if test -n "$$1"; then + pkg_cv_[]$1="$$1" + elif test -n "$PKG_CONFIG"; then + PKG_CHECK_EXISTS([$3], + [pkg_cv_[]$1=`$PKG_CONFIG --[]$2 "$3" 2>/dev/null` + test "x$?" != "x0" && pkg_failed=yes ], + [pkg_failed=yes]) + else + pkg_failed=untried +fi[]dnl +])# _PKG_CONFIG + +# _PKG_SHORT_ERRORS_SUPPORTED +# ----------------------------- +AC_DEFUN([_PKG_SHORT_ERRORS_SUPPORTED], +[AC_REQUIRE([PKG_PROG_PKG_CONFIG]) +if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then + _pkg_short_errors_supported=yes +else + _pkg_short_errors_supported=no +fi[]dnl +])# _PKG_SHORT_ERRORS_SUPPORTED + + +# PKG_CHECK_MODULES(VARIABLE-PREFIX, MODULES, [ACTION-IF-FOUND], +# [ACTION-IF-NOT-FOUND]) +# +# +# Note that if there is a possibility the first call to +# PKG_CHECK_MODULES might not happen, you should be sure to include an +# explicit call to PKG_PROG_PKG_CONFIG in your configure.ac +# +# +# -------------------------------------------------------------- +AC_DEFUN([PKG_CHECK_MODULES], +[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl +AC_ARG_VAR([$1][_CFLAGS], [C compiler flags for $1, overriding pkg-config])dnl +AC_ARG_VAR([$1][_LIBS], [linker flags for $1, overriding pkg-config])dnl + +pkg_failed=no +AC_MSG_CHECKING([for $1]) + +_PKG_CONFIG([$1][_CFLAGS], [cflags], [$2]) +_PKG_CONFIG([$1][_LIBS], [libs], [$2]) + +m4_define([_PKG_TEXT], [Alternatively, you may set the environment variables $1[]_CFLAGS +and $1[]_LIBS to avoid the need to call pkg-config. +See the pkg-config man page for more details.]) + +if test $pkg_failed = yes; then + AC_MSG_RESULT([no]) + _PKG_SHORT_ERRORS_SUPPORTED + if test $_pkg_short_errors_supported = yes; then + $1[]_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "$2" 2>&1` + else + $1[]_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "$2" 2>&1` + fi + # Put the nasty error message in config.log where it belongs + echo "$$1[]_PKG_ERRORS" >&AS_MESSAGE_LOG_FD + + m4_default([$4], [AC_MSG_ERROR( +[Package requirements ($2) were not met: + +$$1_PKG_ERRORS + +Consider adjusting the PKG_CONFIG_PATH environment variable if you +installed software in a non-standard prefix. + +_PKG_TEXT])[]dnl + ]) +elif test $pkg_failed = untried; then + AC_MSG_RESULT([no]) + m4_default([$4], [AC_MSG_FAILURE( +[The pkg-config script could not be found or is too old. Make sure it +is in your PATH or set the PKG_CONFIG environment variable to the full +path to pkg-config. + +_PKG_TEXT + +To get pkg-config, see .])[]dnl + ]) +else + $1[]_CFLAGS=$pkg_cv_[]$1[]_CFLAGS + $1[]_LIBS=$pkg_cv_[]$1[]_LIBS + AC_MSG_RESULT([yes]) + $3 +fi[]dnl +])# PKG_CHECK_MODULES + + +# PKG_INSTALLDIR(DIRECTORY) +# ------------------------- +# Substitutes the variable pkgconfigdir as the location where a module +# should install pkg-config .pc files. By default the directory is +# $libdir/pkgconfig, but the default can be changed by passing +# DIRECTORY. The user can override through the --with-pkgconfigdir +# parameter. +AC_DEFUN([PKG_INSTALLDIR], +[m4_pushdef([pkg_default], [m4_default([$1], ['${libdir}/pkgconfig'])]) +m4_pushdef([pkg_description], + [pkg-config installation directory @<:@]pkg_default[@:>@]) +AC_ARG_WITH([pkgconfigdir], + [AS_HELP_STRING([--with-pkgconfigdir], pkg_description)],, + [with_pkgconfigdir=]pkg_default) +AC_SUBST([pkgconfigdir], [$with_pkgconfigdir]) +m4_popdef([pkg_default]) +m4_popdef([pkg_description]) +]) dnl PKG_INSTALLDIR + + +# PKG_NOARCH_INSTALLDIR(DIRECTORY) +# ------------------------- +# Substitutes the variable noarch_pkgconfigdir as the location where a +# module should install arch-independent pkg-config .pc files. By +# default the directory is $datadir/pkgconfig, but the default can be +# changed by passing DIRECTORY. The user can override through the +# --with-noarch-pkgconfigdir parameter. +AC_DEFUN([PKG_NOARCH_INSTALLDIR], +[m4_pushdef([pkg_default], [m4_default([$1], ['${datadir}/pkgconfig'])]) +m4_pushdef([pkg_description], + [pkg-config arch-independent installation directory @<:@]pkg_default[@:>@]) +AC_ARG_WITH([noarch-pkgconfigdir], + [AS_HELP_STRING([--with-noarch-pkgconfigdir], pkg_description)],, + [with_noarch_pkgconfigdir=]pkg_default) +AC_SUBST([noarch_pkgconfigdir], [$with_noarch_pkgconfigdir]) +m4_popdef([pkg_default]) +m4_popdef([pkg_description]) +]) dnl PKG_NOARCH_INSTALLDIR + + +# PKG_CHECK_VAR(VARIABLE, MODULE, CONFIG-VARIABLE, +# [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) +# ------------------------------------------- +# Retrieves the value of the pkg-config variable for the given module. +AC_DEFUN([PKG_CHECK_VAR], +[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl +AC_ARG_VAR([$1], [value of $3 for $2, overriding pkg-config])dnl + +_PKG_CONFIG([$1], [variable="][$3]["], [$2]) +AS_VAR_COPY([$1], [pkg_cv_][$1]) + +AS_VAR_IF([$1], [""], [$5], [$4])dnl +])# PKG_CHECK_VAR + +# Copyright (C) 2002-2013 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# AM_AUTOMAKE_VERSION(VERSION) +# ---------------------------- +# Automake X.Y traces this macro to ensure aclocal.m4 has been +# generated from the m4 files accompanying Automake X.Y. +# (This private macro should not be called outside this file.) +AC_DEFUN([AM_AUTOMAKE_VERSION], +[am__api_version='1.14' +dnl Some users find AM_AUTOMAKE_VERSION and mistake it for a way to +dnl require some minimum version. Point them to the right macro. +m4_if([$1], [1.14.1], [], + [AC_FATAL([Do not call $0, use AM_INIT_AUTOMAKE([$1]).])])dnl +]) + +# _AM_AUTOCONF_VERSION(VERSION) +# ----------------------------- +# aclocal traces this macro to find the Autoconf version. +# This is a private macro too. Using m4_define simplifies +# the logic in aclocal, which can simply ignore this definition. +m4_define([_AM_AUTOCONF_VERSION], []) + +# AM_SET_CURRENT_AUTOMAKE_VERSION +# ------------------------------- +# Call AM_AUTOMAKE_VERSION and AM_AUTOMAKE_VERSION so they can be traced. +# This function is AC_REQUIREd by AM_INIT_AUTOMAKE. +AC_DEFUN([AM_SET_CURRENT_AUTOMAKE_VERSION], +[AM_AUTOMAKE_VERSION([1.14.1])dnl +m4_ifndef([AC_AUTOCONF_VERSION], + [m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl +_AM_AUTOCONF_VERSION(m4_defn([AC_AUTOCONF_VERSION]))]) + +# AM_AUX_DIR_EXPAND -*- Autoconf -*- + +# Copyright (C) 2001-2013 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# For projects using AC_CONFIG_AUX_DIR([foo]), Autoconf sets +# $ac_aux_dir to '$srcdir/foo'. In other projects, it is set to +# '$srcdir', '$srcdir/..', or '$srcdir/../..'. +# +# Of course, Automake must honor this variable whenever it calls a +# tool from the auxiliary directory. The problem is that $srcdir (and +# therefore $ac_aux_dir as well) can be either absolute or relative, +# depending on how configure is run. This is pretty annoying, since +# it makes $ac_aux_dir quite unusable in subdirectories: in the top +# source directory, any form will work fine, but in subdirectories a +# relative path needs to be adjusted first. +# +# $ac_aux_dir/missing +# fails when called from a subdirectory if $ac_aux_dir is relative +# $top_srcdir/$ac_aux_dir/missing +# fails if $ac_aux_dir is absolute, +# fails when called from a subdirectory in a VPATH build with +# a relative $ac_aux_dir +# +# The reason of the latter failure is that $top_srcdir and $ac_aux_dir +# are both prefixed by $srcdir. In an in-source build this is usually +# harmless because $srcdir is '.', but things will broke when you +# start a VPATH build or use an absolute $srcdir. +# +# So we could use something similar to $top_srcdir/$ac_aux_dir/missing, +# iff we strip the leading $srcdir from $ac_aux_dir. That would be: +# am_aux_dir='\$(top_srcdir)/'`expr "$ac_aux_dir" : "$srcdir//*\(.*\)"` +# and then we would define $MISSING as +# MISSING="\${SHELL} $am_aux_dir/missing" +# This will work as long as MISSING is not called from configure, because +# unfortunately $(top_srcdir) has no meaning in configure. +# However there are other variables, like CC, which are often used in +# configure, and could therefore not use this "fixed" $ac_aux_dir. +# +# Another solution, used here, is to always expand $ac_aux_dir to an +# absolute PATH. The drawback is that using absolute paths prevent a +# configured tree to be moved without reconfiguration. + +AC_DEFUN([AM_AUX_DIR_EXPAND], +[AC_REQUIRE([AC_CONFIG_AUX_DIR_DEFAULT])dnl +# Expand $ac_aux_dir to an absolute path. +am_aux_dir=`cd "$ac_aux_dir" && pwd` +]) + +# AM_CONDITIONAL -*- Autoconf -*- + +# Copyright (C) 1997-2013 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# AM_CONDITIONAL(NAME, SHELL-CONDITION) +# ------------------------------------- +# Define a conditional. +AC_DEFUN([AM_CONDITIONAL], +[AC_PREREQ([2.52])dnl + m4_if([$1], [TRUE], [AC_FATAL([$0: invalid condition: $1])], + [$1], [FALSE], [AC_FATAL([$0: invalid condition: $1])])dnl +AC_SUBST([$1_TRUE])dnl +AC_SUBST([$1_FALSE])dnl +_AM_SUBST_NOTMAKE([$1_TRUE])dnl +_AM_SUBST_NOTMAKE([$1_FALSE])dnl +m4_define([_AM_COND_VALUE_$1], [$2])dnl +if $2; then + $1_TRUE= + $1_FALSE='#' +else + $1_TRUE='#' + $1_FALSE= +fi +AC_CONFIG_COMMANDS_PRE( +[if test -z "${$1_TRUE}" && test -z "${$1_FALSE}"; then + AC_MSG_ERROR([[conditional "$1" was never defined. +Usually this means the macro was only invoked conditionally.]]) +fi])]) + +# Copyright (C) 1999-2013 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + + +# There are a few dirty hacks below to avoid letting 'AC_PROG_CC' be +# written in clear, in which case automake, when reading aclocal.m4, +# will think it sees a *use*, and therefore will trigger all it's +# C support machinery. Also note that it means that autoscan, seeing +# CC etc. in the Makefile, will ask for an AC_PROG_CC use... + + +# _AM_DEPENDENCIES(NAME) +# ---------------------- +# See how the compiler implements dependency checking. +# NAME is "CC", "CXX", "OBJC", "OBJCXX", "UPC", or "GJC". +# We try a few techniques and use that to set a single cache variable. +# +# We don't AC_REQUIRE the corresponding AC_PROG_CC since the latter was +# modified to invoke _AM_DEPENDENCIES(CC); we would have a circular +# dependency, and given that the user is not expected to run this macro, +# just rely on AC_PROG_CC. +AC_DEFUN([_AM_DEPENDENCIES], +[AC_REQUIRE([AM_SET_DEPDIR])dnl +AC_REQUIRE([AM_OUTPUT_DEPENDENCY_COMMANDS])dnl +AC_REQUIRE([AM_MAKE_INCLUDE])dnl +AC_REQUIRE([AM_DEP_TRACK])dnl + +m4_if([$1], [CC], [depcc="$CC" am_compiler_list=], + [$1], [CXX], [depcc="$CXX" am_compiler_list=], + [$1], [OBJC], [depcc="$OBJC" am_compiler_list='gcc3 gcc'], + [$1], [OBJCXX], [depcc="$OBJCXX" am_compiler_list='gcc3 gcc'], + [$1], [UPC], [depcc="$UPC" am_compiler_list=], + [$1], [GCJ], [depcc="$GCJ" am_compiler_list='gcc3 gcc'], + [depcc="$$1" am_compiler_list=]) + +AC_CACHE_CHECK([dependency style of $depcc], + [am_cv_$1_dependencies_compiler_type], +[if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then + # We make a subdir and do the tests there. Otherwise we can end up + # making bogus files that we don't know about and never remove. For + # instance it was reported that on HP-UX the gcc test will end up + # making a dummy file named 'D' -- because '-MD' means "put the output + # in D". + rm -rf conftest.dir + mkdir conftest.dir + # Copy depcomp to subdir because otherwise we won't find it if we're + # using a relative directory. + cp "$am_depcomp" conftest.dir + cd conftest.dir + # We will build objects and dependencies in a subdirectory because + # it helps to detect inapplicable dependency modes. For instance + # both Tru64's cc and ICC support -MD to output dependencies as a + # side effect of compilation, but ICC will put the dependencies in + # the current directory while Tru64 will put them in the object + # directory. + mkdir sub + + am_cv_$1_dependencies_compiler_type=none + if test "$am_compiler_list" = ""; then + am_compiler_list=`sed -n ['s/^#*\([a-zA-Z0-9]*\))$/\1/p'] < ./depcomp` + fi + am__universal=false + m4_case([$1], [CC], + [case " $depcc " in #( + *\ -arch\ *\ -arch\ *) am__universal=true ;; + esac], + [CXX], + [case " $depcc " in #( + *\ -arch\ *\ -arch\ *) am__universal=true ;; + esac]) + + for depmode in $am_compiler_list; do + # Setup a source with many dependencies, because some compilers + # like to wrap large dependency lists on column 80 (with \), and + # we should not choose a depcomp mode which is confused by this. + # + # We need to recreate these files for each test, as the compiler may + # overwrite some of them when testing with obscure command lines. + # This happens at least with the AIX C compiler. + : > sub/conftest.c + for i in 1 2 3 4 5 6; do + echo '#include "conftst'$i'.h"' >> sub/conftest.c + # Using ": > sub/conftst$i.h" creates only sub/conftst1.h with + # Solaris 10 /bin/sh. + echo '/* dummy */' > sub/conftst$i.h + done + echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf + + # We check with '-c' and '-o' for the sake of the "dashmstdout" + # mode. It turns out that the SunPro C++ compiler does not properly + # handle '-M -o', and we need to detect this. Also, some Intel + # versions had trouble with output in subdirs. + am__obj=sub/conftest.${OBJEXT-o} + am__minus_obj="-o $am__obj" + case $depmode in + gcc) + # This depmode causes a compiler race in universal mode. + test "$am__universal" = false || continue + ;; + nosideeffect) + # After this tag, mechanisms are not by side-effect, so they'll + # only be used when explicitly requested. + if test "x$enable_dependency_tracking" = xyes; then + continue + else + break + fi + ;; + msvc7 | msvc7msys | msvisualcpp | msvcmsys) + # This compiler won't grok '-c -o', but also, the minuso test has + # not run yet. These depmodes are late enough in the game, and + # so weak that their functioning should not be impacted. + am__obj=conftest.${OBJEXT-o} + am__minus_obj= + ;; + none) break ;; + esac + if depmode=$depmode \ + source=sub/conftest.c object=$am__obj \ + depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \ + $SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \ + >/dev/null 2>conftest.err && + grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 && + grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 && + grep $am__obj sub/conftest.Po > /dev/null 2>&1 && + ${MAKE-make} -s -f confmf > /dev/null 2>&1; then + # icc doesn't choke on unknown options, it will just issue warnings + # or remarks (even with -Werror). So we grep stderr for any message + # that says an option was ignored or not supported. + # When given -MP, icc 7.0 and 7.1 complain thusly: + # icc: Command line warning: ignoring option '-M'; no argument required + # The diagnosis changed in icc 8.0: + # icc: Command line remark: option '-MP' not supported + if (grep 'ignoring option' conftest.err || + grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else + am_cv_$1_dependencies_compiler_type=$depmode + break + fi + fi + done + + cd .. + rm -rf conftest.dir +else + am_cv_$1_dependencies_compiler_type=none +fi +]) +AC_SUBST([$1DEPMODE], [depmode=$am_cv_$1_dependencies_compiler_type]) +AM_CONDITIONAL([am__fastdep$1], [ + test "x$enable_dependency_tracking" != xno \ + && test "$am_cv_$1_dependencies_compiler_type" = gcc3]) +]) + + +# AM_SET_DEPDIR +# ------------- +# Choose a directory name for dependency files. +# This macro is AC_REQUIREd in _AM_DEPENDENCIES. +AC_DEFUN([AM_SET_DEPDIR], +[AC_REQUIRE([AM_SET_LEADING_DOT])dnl +AC_SUBST([DEPDIR], ["${am__leading_dot}deps"])dnl +]) + + +# AM_DEP_TRACK +# ------------ +AC_DEFUN([AM_DEP_TRACK], +[AC_ARG_ENABLE([dependency-tracking], [dnl +AS_HELP_STRING( + [--enable-dependency-tracking], + [do not reject slow dependency extractors]) +AS_HELP_STRING( + [--disable-dependency-tracking], + [speeds up one-time build])]) +if test "x$enable_dependency_tracking" != xno; then + am_depcomp="$ac_aux_dir/depcomp" + AMDEPBACKSLASH='\' + am__nodep='_no' +fi +AM_CONDITIONAL([AMDEP], [test "x$enable_dependency_tracking" != xno]) +AC_SUBST([AMDEPBACKSLASH])dnl +_AM_SUBST_NOTMAKE([AMDEPBACKSLASH])dnl +AC_SUBST([am__nodep])dnl +_AM_SUBST_NOTMAKE([am__nodep])dnl +]) + +# Generate code to set up dependency tracking. -*- Autoconf -*- + +# Copyright (C) 1999-2013 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + + +# _AM_OUTPUT_DEPENDENCY_COMMANDS +# ------------------------------ +AC_DEFUN([_AM_OUTPUT_DEPENDENCY_COMMANDS], +[{ + # Older Autoconf quotes --file arguments for eval, but not when files + # are listed without --file. Let's play safe and only enable the eval + # if we detect the quoting. + case $CONFIG_FILES in + *\'*) eval set x "$CONFIG_FILES" ;; + *) set x $CONFIG_FILES ;; + esac + shift + for mf + do + # Strip MF so we end up with the name of the file. + mf=`echo "$mf" | sed -e 's/:.*$//'` + # Check whether this is an Automake generated Makefile or not. + # We used to match only the files named 'Makefile.in', but + # some people rename them; so instead we look at the file content. + # Grep'ing the first line is not enough: some people post-process + # each Makefile.in and add a new line on top of each file to say so. + # Grep'ing the whole file is not good either: AIX grep has a line + # limit of 2048, but all sed's we know have understand at least 4000. + if sed -n 's,^#.*generated by automake.*,X,p' "$mf" | grep X >/dev/null 2>&1; then + dirpart=`AS_DIRNAME("$mf")` + else + continue + fi + # Extract the definition of DEPDIR, am__include, and am__quote + # from the Makefile without running 'make'. + DEPDIR=`sed -n 's/^DEPDIR = //p' < "$mf"` + test -z "$DEPDIR" && continue + am__include=`sed -n 's/^am__include = //p' < "$mf"` + test -z "$am__include" && continue + am__quote=`sed -n 's/^am__quote = //p' < "$mf"` + # Find all dependency output files, they are included files with + # $(DEPDIR) in their names. We invoke sed twice because it is the + # simplest approach to changing $(DEPDIR) to its actual value in the + # expansion. + for file in `sed -n " + s/^$am__include $am__quote\(.*(DEPDIR).*\)$am__quote"'$/\1/p' <"$mf" | \ + sed -e 's/\$(DEPDIR)/'"$DEPDIR"'/g'`; do + # Make sure the directory exists. + test -f "$dirpart/$file" && continue + fdir=`AS_DIRNAME(["$file"])` + AS_MKDIR_P([$dirpart/$fdir]) + # echo "creating $dirpart/$file" + echo '# dummy' > "$dirpart/$file" + done + done +} +])# _AM_OUTPUT_DEPENDENCY_COMMANDS + + +# AM_OUTPUT_DEPENDENCY_COMMANDS +# ----------------------------- +# This macro should only be invoked once -- use via AC_REQUIRE. +# +# This code is only required when automatic dependency tracking +# is enabled. FIXME. This creates each '.P' file that we will +# need in order to bootstrap the dependency handling code. +AC_DEFUN([AM_OUTPUT_DEPENDENCY_COMMANDS], +[AC_CONFIG_COMMANDS([depfiles], + [test x"$AMDEP_TRUE" != x"" || _AM_OUTPUT_DEPENDENCY_COMMANDS], + [AMDEP_TRUE="$AMDEP_TRUE" ac_aux_dir="$ac_aux_dir"]) +]) + +# Do all the work for Automake. -*- Autoconf -*- + +# Copyright (C) 1996-2013 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This macro actually does too much. Some checks are only needed if +# your package does certain things. But this isn't really a big deal. + +dnl Redefine AC_PROG_CC to automatically invoke _AM_PROG_CC_C_O. +m4_define([AC_PROG_CC], +m4_defn([AC_PROG_CC]) +[_AM_PROG_CC_C_O +]) + +# AM_INIT_AUTOMAKE(PACKAGE, VERSION, [NO-DEFINE]) +# AM_INIT_AUTOMAKE([OPTIONS]) +# ----------------------------------------------- +# The call with PACKAGE and VERSION arguments is the old style +# call (pre autoconf-2.50), which is being phased out. PACKAGE +# and VERSION should now be passed to AC_INIT and removed from +# the call to AM_INIT_AUTOMAKE. +# We support both call styles for the transition. After +# the next Automake release, Autoconf can make the AC_INIT +# arguments mandatory, and then we can depend on a new Autoconf +# release and drop the old call support. +AC_DEFUN([AM_INIT_AUTOMAKE], +[AC_PREREQ([2.65])dnl +dnl Autoconf wants to disallow AM_ names. We explicitly allow +dnl the ones we care about. +m4_pattern_allow([^AM_[A-Z]+FLAGS$])dnl +AC_REQUIRE([AM_SET_CURRENT_AUTOMAKE_VERSION])dnl +AC_REQUIRE([AC_PROG_INSTALL])dnl +if test "`cd $srcdir && pwd`" != "`pwd`"; then + # Use -I$(srcdir) only when $(srcdir) != ., so that make's output + # is not polluted with repeated "-I." + AC_SUBST([am__isrc], [' -I$(srcdir)'])_AM_SUBST_NOTMAKE([am__isrc])dnl + # test to see if srcdir already configured + if test -f $srcdir/config.status; then + AC_MSG_ERROR([source directory already configured; run "make distclean" there first]) + fi +fi + +# test whether we have cygpath +if test -z "$CYGPATH_W"; then + if (cygpath --version) >/dev/null 2>/dev/null; then + CYGPATH_W='cygpath -w' + else + CYGPATH_W=echo + fi +fi +AC_SUBST([CYGPATH_W]) + +# Define the identity of the package. +dnl Distinguish between old-style and new-style calls. +m4_ifval([$2], +[AC_DIAGNOSE([obsolete], + [$0: two- and three-arguments forms are deprecated.]) +m4_ifval([$3], [_AM_SET_OPTION([no-define])])dnl + AC_SUBST([PACKAGE], [$1])dnl + AC_SUBST([VERSION], [$2])], +[_AM_SET_OPTIONS([$1])dnl +dnl Diagnose old-style AC_INIT with new-style AM_AUTOMAKE_INIT. +m4_if( + m4_ifdef([AC_PACKAGE_NAME], [ok]):m4_ifdef([AC_PACKAGE_VERSION], [ok]), + [ok:ok],, + [m4_fatal([AC_INIT should be called with package and version arguments])])dnl + AC_SUBST([PACKAGE], ['AC_PACKAGE_TARNAME'])dnl + AC_SUBST([VERSION], ['AC_PACKAGE_VERSION'])])dnl + +_AM_IF_OPTION([no-define],, +[AC_DEFINE_UNQUOTED([PACKAGE], ["$PACKAGE"], [Name of package]) + AC_DEFINE_UNQUOTED([VERSION], ["$VERSION"], [Version number of package])])dnl + +# Some tools Automake needs. +AC_REQUIRE([AM_SANITY_CHECK])dnl +AC_REQUIRE([AC_ARG_PROGRAM])dnl +AM_MISSING_PROG([ACLOCAL], [aclocal-${am__api_version}]) +AM_MISSING_PROG([AUTOCONF], [autoconf]) +AM_MISSING_PROG([AUTOMAKE], [automake-${am__api_version}]) +AM_MISSING_PROG([AUTOHEADER], [autoheader]) +AM_MISSING_PROG([MAKEINFO], [makeinfo]) +AC_REQUIRE([AM_PROG_INSTALL_SH])dnl +AC_REQUIRE([AM_PROG_INSTALL_STRIP])dnl +AC_REQUIRE([AC_PROG_MKDIR_P])dnl +# For better backward compatibility. To be removed once Automake 1.9.x +# dies out for good. For more background, see: +# +# +AC_SUBST([mkdir_p], ['$(MKDIR_P)']) +# We need awk for the "check" target. The system "awk" is bad on +# some platforms. +AC_REQUIRE([AC_PROG_AWK])dnl +AC_REQUIRE([AC_PROG_MAKE_SET])dnl +AC_REQUIRE([AM_SET_LEADING_DOT])dnl +_AM_IF_OPTION([tar-ustar], [_AM_PROG_TAR([ustar])], + [_AM_IF_OPTION([tar-pax], [_AM_PROG_TAR([pax])], + [_AM_PROG_TAR([v7])])]) +_AM_IF_OPTION([no-dependencies],, +[AC_PROVIDE_IFELSE([AC_PROG_CC], + [_AM_DEPENDENCIES([CC])], + [m4_define([AC_PROG_CC], + m4_defn([AC_PROG_CC])[_AM_DEPENDENCIES([CC])])])dnl +AC_PROVIDE_IFELSE([AC_PROG_CXX], + [_AM_DEPENDENCIES([CXX])], + [m4_define([AC_PROG_CXX], + m4_defn([AC_PROG_CXX])[_AM_DEPENDENCIES([CXX])])])dnl +AC_PROVIDE_IFELSE([AC_PROG_OBJC], + [_AM_DEPENDENCIES([OBJC])], + [m4_define([AC_PROG_OBJC], + m4_defn([AC_PROG_OBJC])[_AM_DEPENDENCIES([OBJC])])])dnl +AC_PROVIDE_IFELSE([AC_PROG_OBJCXX], + [_AM_DEPENDENCIES([OBJCXX])], + [m4_define([AC_PROG_OBJCXX], + m4_defn([AC_PROG_OBJCXX])[_AM_DEPENDENCIES([OBJCXX])])])dnl +]) +AC_REQUIRE([AM_SILENT_RULES])dnl +dnl The testsuite driver may need to know about EXEEXT, so add the +dnl 'am__EXEEXT' conditional if _AM_COMPILER_EXEEXT was seen. This +dnl macro is hooked onto _AC_COMPILER_EXEEXT early, see below. +AC_CONFIG_COMMANDS_PRE(dnl +[m4_provide_if([_AM_COMPILER_EXEEXT], + [AM_CONDITIONAL([am__EXEEXT], [test -n "$EXEEXT"])])])dnl + +# POSIX will say in a future version that running "rm -f" with no argument +# is OK; and we want to be able to make that assumption in our Makefile +# recipes. So use an aggressive probe to check that the usage we want is +# actually supported "in the wild" to an acceptable degree. +# See automake bug#10828. +# To make any issue more visible, cause the running configure to be aborted +# by default if the 'rm' program in use doesn't match our expectations; the +# user can still override this though. +if rm -f && rm -fr && rm -rf; then : OK; else + cat >&2 <<'END' +Oops! + +Your 'rm' program seems unable to run without file operands specified +on the command line, even when the '-f' option is present. This is contrary +to the behaviour of most rm programs out there, and not conforming with +the upcoming POSIX standard: + +Please tell bug-automake@gnu.org about your system, including the value +of your $PATH and any error possibly output before this message. This +can help us improve future automake versions. + +END + if test x"$ACCEPT_INFERIOR_RM_PROGRAM" = x"yes"; then + echo 'Configuration will proceed anyway, since you have set the' >&2 + echo 'ACCEPT_INFERIOR_RM_PROGRAM variable to "yes"' >&2 + echo >&2 + else + cat >&2 <<'END' +Aborting the configuration process, to ensure you take notice of the issue. + +You can download and install GNU coreutils to get an 'rm' implementation +that behaves properly: . + +If you want to complete the configuration process using your problematic +'rm' anyway, export the environment variable ACCEPT_INFERIOR_RM_PROGRAM +to "yes", and re-run configure. + +END + AC_MSG_ERROR([Your 'rm' program is bad, sorry.]) + fi +fi +]) + +dnl Hook into '_AC_COMPILER_EXEEXT' early to learn its expansion. Do not +dnl add the conditional right here, as _AC_COMPILER_EXEEXT may be further +dnl mangled by Autoconf and run in a shell conditional statement. +m4_define([_AC_COMPILER_EXEEXT], +m4_defn([_AC_COMPILER_EXEEXT])[m4_provide([_AM_COMPILER_EXEEXT])]) + +# When config.status generates a header, we must update the stamp-h file. +# This file resides in the same directory as the config header +# that is generated. The stamp files are numbered to have different names. + +# Autoconf calls _AC_AM_CONFIG_HEADER_HOOK (when defined) in the +# loop where config.status creates the headers, so we can generate +# our stamp files there. +AC_DEFUN([_AC_AM_CONFIG_HEADER_HOOK], +[# Compute $1's index in $config_headers. +_am_arg=$1 +_am_stamp_count=1 +for _am_header in $config_headers :; do + case $_am_header in + $_am_arg | $_am_arg:* ) + break ;; + * ) + _am_stamp_count=`expr $_am_stamp_count + 1` ;; + esac +done +echo "timestamp for $_am_arg" >`AS_DIRNAME(["$_am_arg"])`/stamp-h[]$_am_stamp_count]) + +# Copyright (C) 2001-2013 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# AM_PROG_INSTALL_SH +# ------------------ +# Define $install_sh. +AC_DEFUN([AM_PROG_INSTALL_SH], +[AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl +if test x"${install_sh}" != xset; then + case $am_aux_dir in + *\ * | *\ *) + install_sh="\${SHELL} '$am_aux_dir/install-sh'" ;; + *) + install_sh="\${SHELL} $am_aux_dir/install-sh" + esac +fi +AC_SUBST([install_sh])]) + +# Copyright (C) 2003-2013 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# Check whether the underlying file-system supports filenames +# with a leading dot. For instance MS-DOS doesn't. +AC_DEFUN([AM_SET_LEADING_DOT], +[rm -rf .tst 2>/dev/null +mkdir .tst 2>/dev/null +if test -d .tst; then + am__leading_dot=. +else + am__leading_dot=_ +fi +rmdir .tst 2>/dev/null +AC_SUBST([am__leading_dot])]) + +# Check to see how 'make' treats includes. -*- Autoconf -*- + +# Copyright (C) 2001-2013 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# AM_MAKE_INCLUDE() +# ----------------- +# Check to see how make treats includes. +AC_DEFUN([AM_MAKE_INCLUDE], +[am_make=${MAKE-make} +cat > confinc << 'END' +am__doit: + @echo this is the am__doit target +.PHONY: am__doit +END +# If we don't find an include directive, just comment out the code. +AC_MSG_CHECKING([for style of include used by $am_make]) +am__include="#" +am__quote= +_am_result=none +# First try GNU make style include. +echo "include confinc" > confmf +# Ignore all kinds of additional output from 'make'. +case `$am_make -s -f confmf 2> /dev/null` in #( +*the\ am__doit\ target*) + am__include=include + am__quote= + _am_result=GNU + ;; +esac +# Now try BSD make style include. +if test "$am__include" = "#"; then + echo '.include "confinc"' > confmf + case `$am_make -s -f confmf 2> /dev/null` in #( + *the\ am__doit\ target*) + am__include=.include + am__quote="\"" + _am_result=BSD + ;; + esac +fi +AC_SUBST([am__include]) +AC_SUBST([am__quote]) +AC_MSG_RESULT([$_am_result]) +rm -f confinc confmf +]) + +# Fake the existence of programs that GNU maintainers use. -*- Autoconf -*- + +# Copyright (C) 1997-2013 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# AM_MISSING_PROG(NAME, PROGRAM) +# ------------------------------ +AC_DEFUN([AM_MISSING_PROG], +[AC_REQUIRE([AM_MISSING_HAS_RUN]) +$1=${$1-"${am_missing_run}$2"} +AC_SUBST($1)]) + +# AM_MISSING_HAS_RUN +# ------------------ +# Define MISSING if not defined so far and test if it is modern enough. +# If it is, set am_missing_run to use it, otherwise, to nothing. +AC_DEFUN([AM_MISSING_HAS_RUN], +[AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl +AC_REQUIRE_AUX_FILE([missing])dnl +if test x"${MISSING+set}" != xset; then + case $am_aux_dir in + *\ * | *\ *) + MISSING="\${SHELL} \"$am_aux_dir/missing\"" ;; + *) + MISSING="\${SHELL} $am_aux_dir/missing" ;; + esac +fi +# Use eval to expand $SHELL +if eval "$MISSING --is-lightweight"; then + am_missing_run="$MISSING " +else + am_missing_run= + AC_MSG_WARN(['missing' script is too old or missing]) +fi +]) + +# Helper functions for option handling. -*- Autoconf -*- + +# Copyright (C) 2001-2013 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# _AM_MANGLE_OPTION(NAME) +# ----------------------- +AC_DEFUN([_AM_MANGLE_OPTION], +[[_AM_OPTION_]m4_bpatsubst($1, [[^a-zA-Z0-9_]], [_])]) + +# _AM_SET_OPTION(NAME) +# -------------------- +# Set option NAME. Presently that only means defining a flag for this option. +AC_DEFUN([_AM_SET_OPTION], +[m4_define(_AM_MANGLE_OPTION([$1]), [1])]) + +# _AM_SET_OPTIONS(OPTIONS) +# ------------------------ +# OPTIONS is a space-separated list of Automake options. +AC_DEFUN([_AM_SET_OPTIONS], +[m4_foreach_w([_AM_Option], [$1], [_AM_SET_OPTION(_AM_Option)])]) + +# _AM_IF_OPTION(OPTION, IF-SET, [IF-NOT-SET]) +# ------------------------------------------- +# Execute IF-SET if OPTION is set, IF-NOT-SET otherwise. +AC_DEFUN([_AM_IF_OPTION], +[m4_ifset(_AM_MANGLE_OPTION([$1]), [$2], [$3])]) + +# Copyright (C) 1999-2013 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# _AM_PROG_CC_C_O +# --------------- +# Like AC_PROG_CC_C_O, but changed for automake. We rewrite AC_PROG_CC +# to automatically call this. +AC_DEFUN([_AM_PROG_CC_C_O], +[AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl +AC_REQUIRE_AUX_FILE([compile])dnl +AC_LANG_PUSH([C])dnl +AC_CACHE_CHECK( + [whether $CC understands -c and -o together], + [am_cv_prog_cc_c_o], + [AC_LANG_CONFTEST([AC_LANG_PROGRAM([])]) + # Make sure it works both with $CC and with simple cc. + # Following AC_PROG_CC_C_O, we do the test twice because some + # compilers refuse to overwrite an existing .o file with -o, + # though they will create one. + am_cv_prog_cc_c_o=yes + for am_i in 1 2; do + if AM_RUN_LOG([$CC -c conftest.$ac_ext -o conftest2.$ac_objext]) \ + && test -f conftest2.$ac_objext; then + : OK + else + am_cv_prog_cc_c_o=no + break + fi + done + rm -f core conftest* + unset am_i]) +if test "$am_cv_prog_cc_c_o" != yes; then + # Losing compiler, so override with the script. + # FIXME: It is wrong to rewrite CC. + # But if we don't then we get into trouble of one sort or another. + # A longer-term fix would be to have automake use am__CC in this case, + # and then we could set am__CC="\$(top_srcdir)/compile \$(CC)" + CC="$am_aux_dir/compile $CC" +fi +AC_LANG_POP([C])]) + +# For backward compatibility. +AC_DEFUN_ONCE([AM_PROG_CC_C_O], [AC_REQUIRE([AC_PROG_CC])]) + +# Copyright (C) 1999-2013 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + + +# AM_PATH_PYTHON([MINIMUM-VERSION], [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) +# --------------------------------------------------------------------------- +# Adds support for distributing Python modules and packages. To +# install modules, copy them to $(pythondir), using the python_PYTHON +# automake variable. To install a package with the same name as the +# automake package, install to $(pkgpythondir), or use the +# pkgpython_PYTHON automake variable. +# +# The variables $(pyexecdir) and $(pkgpyexecdir) are provided as +# locations to install python extension modules (shared libraries). +# Another macro is required to find the appropriate flags to compile +# extension modules. +# +# If your package is configured with a different prefix to python, +# users will have to add the install directory to the PYTHONPATH +# environment variable, or create a .pth file (see the python +# documentation for details). +# +# If the MINIMUM-VERSION argument is passed, AM_PATH_PYTHON will +# cause an error if the version of python installed on the system +# doesn't meet the requirement. MINIMUM-VERSION should consist of +# numbers and dots only. +AC_DEFUN([AM_PATH_PYTHON], + [ + dnl Find a Python interpreter. Python versions prior to 2.0 are not + dnl supported. (2.0 was released on October 16, 2000). + m4_define_default([_AM_PYTHON_INTERPRETER_LIST], +[python python2 python3 python3.3 python3.2 python3.1 python3.0 python2.7 dnl + python2.6 python2.5 python2.4 python2.3 python2.2 python2.1 python2.0]) + + AC_ARG_VAR([PYTHON], [the Python interpreter]) + + m4_if([$1],[],[ + dnl No version check is needed. + # Find any Python interpreter. + if test -z "$PYTHON"; then + AC_PATH_PROGS([PYTHON], _AM_PYTHON_INTERPRETER_LIST, :) + fi + am_display_PYTHON=python + ], [ + dnl A version check is needed. + if test -n "$PYTHON"; then + # If the user set $PYTHON, use it and don't search something else. + AC_MSG_CHECKING([whether $PYTHON version is >= $1]) + AM_PYTHON_CHECK_VERSION([$PYTHON], [$1], + [AC_MSG_RESULT([yes])], + [AC_MSG_RESULT([no]) + AC_MSG_ERROR([Python interpreter is too old])]) + am_display_PYTHON=$PYTHON + else + # Otherwise, try each interpreter until we find one that satisfies + # VERSION. + AC_CACHE_CHECK([for a Python interpreter with version >= $1], + [am_cv_pathless_PYTHON],[ + for am_cv_pathless_PYTHON in _AM_PYTHON_INTERPRETER_LIST none; do + test "$am_cv_pathless_PYTHON" = none && break + AM_PYTHON_CHECK_VERSION([$am_cv_pathless_PYTHON], [$1], [break]) + done]) + # Set $PYTHON to the absolute path of $am_cv_pathless_PYTHON. + if test "$am_cv_pathless_PYTHON" = none; then + PYTHON=: + else + AC_PATH_PROG([PYTHON], [$am_cv_pathless_PYTHON]) + fi + am_display_PYTHON=$am_cv_pathless_PYTHON + fi + ]) + + if test "$PYTHON" = :; then + dnl Run any user-specified action, or abort. + m4_default([$3], [AC_MSG_ERROR([no suitable Python interpreter found])]) + else + + dnl Query Python for its version number. Getting [:3] seems to be + dnl the best way to do this; it's what "site.py" does in the standard + dnl library. + + AC_CACHE_CHECK([for $am_display_PYTHON version], [am_cv_python_version], + [am_cv_python_version=`$PYTHON -c "import sys; sys.stdout.write(sys.version[[:3]])"`]) + AC_SUBST([PYTHON_VERSION], [$am_cv_python_version]) + + dnl Use the values of $prefix and $exec_prefix for the corresponding + dnl values of PYTHON_PREFIX and PYTHON_EXEC_PREFIX. These are made + dnl distinct variables so they can be overridden if need be. However, + dnl general consensus is that you shouldn't need this ability. + + AC_SUBST([PYTHON_PREFIX], ['${prefix}']) + AC_SUBST([PYTHON_EXEC_PREFIX], ['${exec_prefix}']) + + dnl At times (like when building shared libraries) you may want + dnl to know which OS platform Python thinks this is. + + AC_CACHE_CHECK([for $am_display_PYTHON platform], [am_cv_python_platform], + [am_cv_python_platform=`$PYTHON -c "import sys; sys.stdout.write(sys.platform)"`]) + AC_SUBST([PYTHON_PLATFORM], [$am_cv_python_platform]) + + # Just factor out some code duplication. + am_python_setup_sysconfig="\ +import sys +# Prefer sysconfig over distutils.sysconfig, for better compatibility +# with python 3.x. See automake bug#10227. +try: + import sysconfig +except ImportError: + can_use_sysconfig = 0 +else: + can_use_sysconfig = 1 +# Can't use sysconfig in CPython 2.7, since it's broken in virtualenvs: +# +try: + from platform import python_implementation + if python_implementation() == 'CPython' and sys.version[[:3]] == '2.7': + can_use_sysconfig = 0 +except ImportError: + pass" + + dnl Set up 4 directories: + + dnl pythondir -- where to install python scripts. This is the + dnl site-packages directory, not the python standard library + dnl directory like in previous automake betas. This behavior + dnl is more consistent with lispdir.m4 for example. + dnl Query distutils for this directory. + AC_CACHE_CHECK([for $am_display_PYTHON script directory], + [am_cv_python_pythondir], + [if test "x$prefix" = xNONE + then + am_py_prefix=$ac_default_prefix + else + am_py_prefix=$prefix + fi + am_cv_python_pythondir=`$PYTHON -c " +$am_python_setup_sysconfig +if can_use_sysconfig: + sitedir = sysconfig.get_path('purelib', vars={'base':'$am_py_prefix'}) +else: + from distutils import sysconfig + sitedir = sysconfig.get_python_lib(0, 0, prefix='$am_py_prefix') +sys.stdout.write(sitedir)"` + case $am_cv_python_pythondir in + $am_py_prefix*) + am__strip_prefix=`echo "$am_py_prefix" | sed 's|.|.|g'` + am_cv_python_pythondir=`echo "$am_cv_python_pythondir" | sed "s,^$am__strip_prefix,$PYTHON_PREFIX,"` + ;; + *) + case $am_py_prefix in + /usr|/System*) ;; + *) + am_cv_python_pythondir=$PYTHON_PREFIX/lib/python$PYTHON_VERSION/site-packages + ;; + esac + ;; + esac + ]) + AC_SUBST([pythondir], [$am_cv_python_pythondir]) + + dnl pkgpythondir -- $PACKAGE directory under pythondir. Was + dnl PYTHON_SITE_PACKAGE in previous betas, but this naming is + dnl more consistent with the rest of automake. + + AC_SUBST([pkgpythondir], [\${pythondir}/$PACKAGE]) + + dnl pyexecdir -- directory for installing python extension modules + dnl (shared libraries) + dnl Query distutils for this directory. + AC_CACHE_CHECK([for $am_display_PYTHON extension module directory], + [am_cv_python_pyexecdir], + [if test "x$exec_prefix" = xNONE + then + am_py_exec_prefix=$am_py_prefix + else + am_py_exec_prefix=$exec_prefix + fi + am_cv_python_pyexecdir=`$PYTHON -c " +$am_python_setup_sysconfig +if can_use_sysconfig: + sitedir = sysconfig.get_path('platlib', vars={'platbase':'$am_py_prefix'}) +else: + from distutils import sysconfig + sitedir = sysconfig.get_python_lib(1, 0, prefix='$am_py_prefix') +sys.stdout.write(sitedir)"` + case $am_cv_python_pyexecdir in + $am_py_exec_prefix*) + am__strip_prefix=`echo "$am_py_exec_prefix" | sed 's|.|.|g'` + am_cv_python_pyexecdir=`echo "$am_cv_python_pyexecdir" | sed "s,^$am__strip_prefix,$PYTHON_EXEC_PREFIX,"` + ;; + *) + case $am_py_exec_prefix in + /usr|/System*) ;; + *) + am_cv_python_pyexecdir=$PYTHON_EXEC_PREFIX/lib/python$PYTHON_VERSION/site-packages + ;; + esac + ;; + esac + ]) + AC_SUBST([pyexecdir], [$am_cv_python_pyexecdir]) + + dnl pkgpyexecdir -- $(pyexecdir)/$(PACKAGE) + + AC_SUBST([pkgpyexecdir], [\${pyexecdir}/$PACKAGE]) + + dnl Run any user-specified action. + $2 + fi + +]) + + +# AM_PYTHON_CHECK_VERSION(PROG, VERSION, [ACTION-IF-TRUE], [ACTION-IF-FALSE]) +# --------------------------------------------------------------------------- +# Run ACTION-IF-TRUE if the Python interpreter PROG has version >= VERSION. +# Run ACTION-IF-FALSE otherwise. +# This test uses sys.hexversion instead of the string equivalent (first +# word of sys.version), in order to cope with versions such as 2.2c1. +# This supports Python 2.0 or higher. (2.0 was released on October 16, 2000). +AC_DEFUN([AM_PYTHON_CHECK_VERSION], + [prog="import sys +# split strings by '.' and convert to numeric. Append some zeros +# because we need at least 4 digits for the hex conversion. +# map returns an iterator in Python 3.0 and a list in 2.x +minver = list(map(int, '$2'.split('.'))) + [[0, 0, 0]] +minverhex = 0 +# xrange is not present in Python 3.0 and range returns an iterator +for i in list(range(0, 4)): minverhex = (minverhex << 8) + minver[[i]] +sys.exit(sys.hexversion < minverhex)" + AS_IF([AM_RUN_LOG([$1 -c "$prog"])], [$3], [$4])]) + +# Copyright (C) 2001-2013 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# AM_RUN_LOG(COMMAND) +# ------------------- +# Run COMMAND, save the exit status in ac_status, and log it. +# (This has been adapted from Autoconf's _AC_RUN_LOG macro.) +AC_DEFUN([AM_RUN_LOG], +[{ echo "$as_me:$LINENO: $1" >&AS_MESSAGE_LOG_FD + ($1) >&AS_MESSAGE_LOG_FD 2>&AS_MESSAGE_LOG_FD + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&AS_MESSAGE_LOG_FD + (exit $ac_status); }]) + +# Check to make sure that the build environment is sane. -*- Autoconf -*- + +# Copyright (C) 1996-2013 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# AM_SANITY_CHECK +# --------------- +AC_DEFUN([AM_SANITY_CHECK], +[AC_MSG_CHECKING([whether build environment is sane]) +# Reject unsafe characters in $srcdir or the absolute working directory +# name. Accept space and tab only in the latter. +am_lf=' +' +case `pwd` in + *[[\\\"\#\$\&\'\`$am_lf]]*) + AC_MSG_ERROR([unsafe absolute working directory name]);; +esac +case $srcdir in + *[[\\\"\#\$\&\'\`$am_lf\ \ ]]*) + AC_MSG_ERROR([unsafe srcdir value: '$srcdir']);; +esac + +# Do 'set' in a subshell so we don't clobber the current shell's +# arguments. Must try -L first in case configure is actually a +# symlink; some systems play weird games with the mod time of symlinks +# (eg FreeBSD returns the mod time of the symlink's containing +# directory). +if ( + am_has_slept=no + for am_try in 1 2; do + echo "timestamp, slept: $am_has_slept" > conftest.file + set X `ls -Lt "$srcdir/configure" conftest.file 2> /dev/null` + if test "$[*]" = "X"; then + # -L didn't work. + set X `ls -t "$srcdir/configure" conftest.file` + fi + if test "$[*]" != "X $srcdir/configure conftest.file" \ + && test "$[*]" != "X conftest.file $srcdir/configure"; then + + # If neither matched, then we have a broken ls. This can happen + # if, for instance, CONFIG_SHELL is bash and it inherits a + # broken ls alias from the environment. This has actually + # happened. Such a system could not be considered "sane". + AC_MSG_ERROR([ls -t appears to fail. Make sure there is not a broken + alias in your environment]) + fi + if test "$[2]" = conftest.file || test $am_try -eq 2; then + break + fi + # Just in case. + sleep 1 + am_has_slept=yes + done + test "$[2]" = conftest.file + ) +then + # Ok. + : +else + AC_MSG_ERROR([newly created file is older than distributed files! +Check your system clock]) +fi +AC_MSG_RESULT([yes]) +# If we didn't sleep, we still need to ensure time stamps of config.status and +# generated files are strictly newer. +am_sleep_pid= +if grep 'slept: no' conftest.file >/dev/null 2>&1; then + ( sleep 1 ) & + am_sleep_pid=$! +fi +AC_CONFIG_COMMANDS_PRE( + [AC_MSG_CHECKING([that generated files are newer than configure]) + if test -n "$am_sleep_pid"; then + # Hide warnings about reused PIDs. + wait $am_sleep_pid 2>/dev/null + fi + AC_MSG_RESULT([done])]) +rm -f conftest.file +]) + +# Copyright (C) 2009-2013 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# AM_SILENT_RULES([DEFAULT]) +# -------------------------- +# Enable less verbose build rules; with the default set to DEFAULT +# ("yes" being less verbose, "no" or empty being verbose). +AC_DEFUN([AM_SILENT_RULES], +[AC_ARG_ENABLE([silent-rules], [dnl +AS_HELP_STRING( + [--enable-silent-rules], + [less verbose build output (undo: "make V=1")]) +AS_HELP_STRING( + [--disable-silent-rules], + [verbose build output (undo: "make V=0")])dnl +]) +case $enable_silent_rules in @%:@ ((( + yes) AM_DEFAULT_VERBOSITY=0;; + no) AM_DEFAULT_VERBOSITY=1;; + *) AM_DEFAULT_VERBOSITY=m4_if([$1], [yes], [0], [1]);; +esac +dnl +dnl A few 'make' implementations (e.g., NonStop OS and NextStep) +dnl do not support nested variable expansions. +dnl See automake bug#9928 and bug#10237. +am_make=${MAKE-make} +AC_CACHE_CHECK([whether $am_make supports nested variables], + [am_cv_make_support_nested_variables], + [if AS_ECHO([['TRUE=$(BAR$(V)) +BAR0=false +BAR1=true +V=1 +am__doit: + @$(TRUE) +.PHONY: am__doit']]) | $am_make -f - >/dev/null 2>&1; then + am_cv_make_support_nested_variables=yes +else + am_cv_make_support_nested_variables=no +fi]) +if test $am_cv_make_support_nested_variables = yes; then + dnl Using '$V' instead of '$(V)' breaks IRIX make. + AM_V='$(V)' + AM_DEFAULT_V='$(AM_DEFAULT_VERBOSITY)' +else + AM_V=$AM_DEFAULT_VERBOSITY + AM_DEFAULT_V=$AM_DEFAULT_VERBOSITY +fi +AC_SUBST([AM_V])dnl +AM_SUBST_NOTMAKE([AM_V])dnl +AC_SUBST([AM_DEFAULT_V])dnl +AM_SUBST_NOTMAKE([AM_DEFAULT_V])dnl +AC_SUBST([AM_DEFAULT_VERBOSITY])dnl +AM_BACKSLASH='\' +AC_SUBST([AM_BACKSLASH])dnl +_AM_SUBST_NOTMAKE([AM_BACKSLASH])dnl +]) + +# Copyright (C) 2001-2013 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# AM_PROG_INSTALL_STRIP +# --------------------- +# One issue with vendor 'install' (even GNU) is that you can't +# specify the program used to strip binaries. This is especially +# annoying in cross-compiling environments, where the build's strip +# is unlikely to handle the host's binaries. +# Fortunately install-sh will honor a STRIPPROG variable, so we +# always use install-sh in "make install-strip", and initialize +# STRIPPROG with the value of the STRIP variable (set by the user). +AC_DEFUN([AM_PROG_INSTALL_STRIP], +[AC_REQUIRE([AM_PROG_INSTALL_SH])dnl +# Installed binaries are usually stripped using 'strip' when the user +# run "make install-strip". However 'strip' might not be the right +# tool to use in cross-compilation environments, therefore Automake +# will honor the 'STRIP' environment variable to overrule this program. +dnl Don't test for $cross_compiling = yes, because it might be 'maybe'. +if test "$cross_compiling" != no; then + AC_CHECK_TOOL([STRIP], [strip], :) +fi +INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s" +AC_SUBST([INSTALL_STRIP_PROGRAM])]) + +# Copyright (C) 2006-2013 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# _AM_SUBST_NOTMAKE(VARIABLE) +# --------------------------- +# Prevent Automake from outputting VARIABLE = @VARIABLE@ in Makefile.in. +# This macro is traced by Automake. +AC_DEFUN([_AM_SUBST_NOTMAKE]) + +# AM_SUBST_NOTMAKE(VARIABLE) +# -------------------------- +# Public sister of _AM_SUBST_NOTMAKE. +AC_DEFUN([AM_SUBST_NOTMAKE], [_AM_SUBST_NOTMAKE($@)]) + +# Check how to create a tarball. -*- Autoconf -*- + +# Copyright (C) 2004-2013 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# _AM_PROG_TAR(FORMAT) +# -------------------- +# Check how to create a tarball in format FORMAT. +# FORMAT should be one of 'v7', 'ustar', or 'pax'. +# +# Substitute a variable $(am__tar) that is a command +# writing to stdout a FORMAT-tarball containing the directory +# $tardir. +# tardir=directory && $(am__tar) > result.tar +# +# Substitute a variable $(am__untar) that extract such +# a tarball read from stdin. +# $(am__untar) < result.tar +# +AC_DEFUN([_AM_PROG_TAR], +[# Always define AMTAR for backward compatibility. Yes, it's still used +# in the wild :-( We should find a proper way to deprecate it ... +AC_SUBST([AMTAR], ['$${TAR-tar}']) + +# We'll loop over all known methods to create a tar archive until one works. +_am_tools='gnutar m4_if([$1], [ustar], [plaintar]) pax cpio none' + +m4_if([$1], [v7], + [am__tar='$${TAR-tar} chof - "$$tardir"' am__untar='$${TAR-tar} xf -'], + + [m4_case([$1], + [ustar], + [# The POSIX 1988 'ustar' format is defined with fixed-size fields. + # There is notably a 21 bits limit for the UID and the GID. In fact, + # the 'pax' utility can hang on bigger UID/GID (see automake bug#8343 + # and bug#13588). + am_max_uid=2097151 # 2^21 - 1 + am_max_gid=$am_max_uid + # The $UID and $GID variables are not portable, so we need to resort + # to the POSIX-mandated id(1) utility. Errors in the 'id' calls + # below are definitely unexpected, so allow the users to see them + # (that is, avoid stderr redirection). + am_uid=`id -u || echo unknown` + am_gid=`id -g || echo unknown` + AC_MSG_CHECKING([whether UID '$am_uid' is supported by ustar format]) + if test $am_uid -le $am_max_uid; then + AC_MSG_RESULT([yes]) + else + AC_MSG_RESULT([no]) + _am_tools=none + fi + AC_MSG_CHECKING([whether GID '$am_gid' is supported by ustar format]) + if test $am_gid -le $am_max_gid; then + AC_MSG_RESULT([yes]) + else + AC_MSG_RESULT([no]) + _am_tools=none + fi], + + [pax], + [], + + [m4_fatal([Unknown tar format])]) + + AC_MSG_CHECKING([how to create a $1 tar archive]) + + # Go ahead even if we have the value already cached. We do so because we + # need to set the values for the 'am__tar' and 'am__untar' variables. + _am_tools=${am_cv_prog_tar_$1-$_am_tools} + + for _am_tool in $_am_tools; do + case $_am_tool in + gnutar) + for _am_tar in tar gnutar gtar; do + AM_RUN_LOG([$_am_tar --version]) && break + done + am__tar="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$$tardir"' + am__tar_="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$tardir"' + am__untar="$_am_tar -xf -" + ;; + plaintar) + # Must skip GNU tar: if it does not support --format= it doesn't create + # ustar tarball either. + (tar --version) >/dev/null 2>&1 && continue + am__tar='tar chf - "$$tardir"' + am__tar_='tar chf - "$tardir"' + am__untar='tar xf -' + ;; + pax) + am__tar='pax -L -x $1 -w "$$tardir"' + am__tar_='pax -L -x $1 -w "$tardir"' + am__untar='pax -r' + ;; + cpio) + am__tar='find "$$tardir" -print | cpio -o -H $1 -L' + am__tar_='find "$tardir" -print | cpio -o -H $1 -L' + am__untar='cpio -i -H $1 -d' + ;; + none) + am__tar=false + am__tar_=false + am__untar=false + ;; + esac + + # If the value was cached, stop now. We just wanted to have am__tar + # and am__untar set. + test -n "${am_cv_prog_tar_$1}" && break + + # tar/untar a dummy directory, and stop if the command works. + rm -rf conftest.dir + mkdir conftest.dir + echo GrepMe > conftest.dir/file + AM_RUN_LOG([tardir=conftest.dir && eval $am__tar_ >conftest.tar]) + rm -rf conftest.dir + if test -s conftest.tar; then + AM_RUN_LOG([$am__untar /dev/null 2>&1 && break + fi + done + rm -rf conftest.dir + + AC_CACHE_VAL([am_cv_prog_tar_$1], [am_cv_prog_tar_$1=$_am_tool]) + AC_MSG_RESULT([$am_cv_prog_tar_$1])]) + +AC_SUBST([am__tar]) +AC_SUBST([am__untar]) +]) # _AM_PROG_TAR + +m4_include([m4/ax_boost_asio.m4]) +m4_include([m4/ax_boost_base.m4]) +m4_include([m4/ax_boost_system.m4]) +m4_include([m4/ax_boost_thread.m4]) +m4_include([m4/ax_check_compile_flag.m4]) +m4_include([m4/ax_cxx_compile_stdcxx_11.m4]) +m4_include([m4/ax_have_epoll.m4]) +m4_include([m4/ax_python_devel.m4]) +m4_include([m4/libtool.m4]) +m4_include([m4/libxml2.m4]) +m4_include([m4/ltoptions.m4]) +m4_include([m4/ltsugar.m4]) +m4_include([m4/ltversion.m4]) +m4_include([m4/lt~obsolete.m4]) diff --git a/android-config b/android-config new file mode 100755 index 0000000..9828822 --- /dev/null +++ b/android-config @@ -0,0 +1,47 @@ +#!/bin/sh +# +# nghttp2 - HTTP/2 C Library +# +# Copyright (c) 2013 Tatsuhiro Tsujikawa +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +if [ -z "$ANDROID_HOME" ]; then + echo 'No $ANDROID_HOME specified.' + exit 1 +fi +PREFIX=$ANDROID_HOME/usr/local +TOOLCHAIN=$ANDROID_HOME/toolchain +PATH=$TOOLCHAIN/bin:$PATH + +./configure \ + --disable-shared \ + --host=arm-linux-androideabi \ + --build=`dpkg-architecture -qDEB_BUILD_GNU_TYPE` \ + --with-xml-prefix="$PREFIX" \ + --without-libxml2 \ + --disable-python-bindings \ + --disable-examples \ + --enable-werror \ + CC=clang \ + CXX=clang++ \ + CPPFLAGS="-fPIE -I$PREFIX/include" \ + PKG_CONFIG_LIBDIR="$PREFIX/lib/pkgconfig" \ + LDFLAGS="-fPIE -pie -L$PREFIX/lib" diff --git a/android-make b/android-make new file mode 100755 index 0000000..375045a --- /dev/null +++ b/android-make @@ -0,0 +1,33 @@ +#!/bin/sh +# +# nghttp2 - HTTP/2 C Library +# +# Copyright (c) 2013 Tatsuhiro Tsujikawa +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +if [ -z "$ANDROID_HOME" ]; then + echo 'No $ANDROID_HOME specified.' + exit 1 +fi +TOOLCHAIN=$ANDROID_HOME/toolchain +PATH=$TOOLCHAIN/bin:$PATH + +make "$@" diff --git a/compile b/compile new file mode 100755 index 0000000..531136b --- /dev/null +++ b/compile @@ -0,0 +1,347 @@ +#! /bin/sh +# Wrapper for compilers which do not understand '-c -o'. + +scriptversion=2012-10-14.11; # UTC + +# Copyright (C) 1999-2013 Free Software Foundation, Inc. +# Written by Tom Tromey . +# +# 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 2, 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 . + +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +# This file is maintained in Automake, please report +# bugs to or send patches to +# . + +nl=' +' + +# We need space, tab and new line, in precisely that order. Quoting is +# there to prevent tools from complaining about whitespace usage. +IFS=" "" $nl" + +file_conv= + +# func_file_conv build_file lazy +# Convert a $build file to $host form and store it in $file +# Currently only supports Windows hosts. If the determined conversion +# type is listed in (the comma separated) LAZY, no conversion will +# take place. +func_file_conv () +{ + file=$1 + case $file in + / | /[!/]*) # absolute file, and not a UNC file + if test -z "$file_conv"; then + # lazily determine how to convert abs files + case `uname -s` in + MINGW*) + file_conv=mingw + ;; + CYGWIN*) + file_conv=cygwin + ;; + *) + file_conv=wine + ;; + esac + fi + case $file_conv/,$2, in + *,$file_conv,*) + ;; + mingw/*) + file=`cmd //C echo "$file " | sed -e 's/"\(.*\) " *$/\1/'` + ;; + cygwin/*) + file=`cygpath -m "$file" || echo "$file"` + ;; + wine/*) + file=`winepath -w "$file" || echo "$file"` + ;; + esac + ;; + esac +} + +# func_cl_dashL linkdir +# Make cl look for libraries in LINKDIR +func_cl_dashL () +{ + func_file_conv "$1" + if test -z "$lib_path"; then + lib_path=$file + else + lib_path="$lib_path;$file" + fi + linker_opts="$linker_opts -LIBPATH:$file" +} + +# func_cl_dashl library +# Do a library search-path lookup for cl +func_cl_dashl () +{ + lib=$1 + found=no + save_IFS=$IFS + IFS=';' + for dir in $lib_path $LIB + do + IFS=$save_IFS + if $shared && test -f "$dir/$lib.dll.lib"; then + found=yes + lib=$dir/$lib.dll.lib + break + fi + if test -f "$dir/$lib.lib"; then + found=yes + lib=$dir/$lib.lib + break + fi + if test -f "$dir/lib$lib.a"; then + found=yes + lib=$dir/lib$lib.a + break + fi + done + IFS=$save_IFS + + if test "$found" != yes; then + lib=$lib.lib + fi +} + +# func_cl_wrapper cl arg... +# Adjust compile command to suit cl +func_cl_wrapper () +{ + # Assume a capable shell + lib_path= + shared=: + linker_opts= + for arg + do + if test -n "$eat"; then + eat= + else + case $1 in + -o) + # configure might choose to run compile as 'compile cc -o foo foo.c'. + eat=1 + case $2 in + *.o | *.[oO][bB][jJ]) + func_file_conv "$2" + set x "$@" -Fo"$file" + shift + ;; + *) + func_file_conv "$2" + set x "$@" -Fe"$file" + shift + ;; + esac + ;; + -I) + eat=1 + func_file_conv "$2" mingw + set x "$@" -I"$file" + shift + ;; + -I*) + func_file_conv "${1#-I}" mingw + set x "$@" -I"$file" + shift + ;; + -l) + eat=1 + func_cl_dashl "$2" + set x "$@" "$lib" + shift + ;; + -l*) + func_cl_dashl "${1#-l}" + set x "$@" "$lib" + shift + ;; + -L) + eat=1 + func_cl_dashL "$2" + ;; + -L*) + func_cl_dashL "${1#-L}" + ;; + -static) + shared=false + ;; + -Wl,*) + arg=${1#-Wl,} + save_ifs="$IFS"; IFS=',' + for flag in $arg; do + IFS="$save_ifs" + linker_opts="$linker_opts $flag" + done + IFS="$save_ifs" + ;; + -Xlinker) + eat=1 + linker_opts="$linker_opts $2" + ;; + -*) + set x "$@" "$1" + shift + ;; + *.cc | *.CC | *.cxx | *.CXX | *.[cC]++) + func_file_conv "$1" + set x "$@" -Tp"$file" + shift + ;; + *.c | *.cpp | *.CPP | *.lib | *.LIB | *.Lib | *.OBJ | *.obj | *.[oO]) + func_file_conv "$1" mingw + set x "$@" "$file" + shift + ;; + *) + set x "$@" "$1" + shift + ;; + esac + fi + shift + done + if test -n "$linker_opts"; then + linker_opts="-link$linker_opts" + fi + exec "$@" $linker_opts + exit 1 +} + +eat= + +case $1 in + '') + echo "$0: No command. Try '$0 --help' for more information." 1>&2 + exit 1; + ;; + -h | --h*) + cat <<\EOF +Usage: compile [--help] [--version] PROGRAM [ARGS] + +Wrapper for compilers which do not understand '-c -o'. +Remove '-o dest.o' from ARGS, run PROGRAM with the remaining +arguments, and rename the output as expected. + +If you are trying to build a whole package this is not the +right script to run: please start by reading the file 'INSTALL'. + +Report bugs to . +EOF + exit $? + ;; + -v | --v*) + echo "compile $scriptversion" + exit $? + ;; + cl | *[/\\]cl | cl.exe | *[/\\]cl.exe ) + func_cl_wrapper "$@" # Doesn't return... + ;; +esac + +ofile= +cfile= + +for arg +do + if test -n "$eat"; then + eat= + else + case $1 in + -o) + # configure might choose to run compile as 'compile cc -o foo foo.c'. + # So we strip '-o arg' only if arg is an object. + eat=1 + case $2 in + *.o | *.obj) + ofile=$2 + ;; + *) + set x "$@" -o "$2" + shift + ;; + esac + ;; + *.c) + cfile=$1 + set x "$@" "$1" + shift + ;; + *) + set x "$@" "$1" + shift + ;; + esac + fi + shift +done + +if test -z "$ofile" || test -z "$cfile"; then + # If no '-o' option was seen then we might have been invoked from a + # pattern rule where we don't need one. That is ok -- this is a + # normal compilation that the losing compiler can handle. If no + # '.c' file was seen then we are probably linking. That is also + # ok. + exec "$@" +fi + +# Name of file we expect compiler to create. +cofile=`echo "$cfile" | sed 's|^.*[\\/]||; s|^[a-zA-Z]:||; s/\.c$/.o/'` + +# Create the lock directory. +# Note: use '[/\\:.-]' here to ensure that we don't use the same name +# that we are using for the .o file. Also, base the name on the expected +# object file name, since that is what matters with a parallel build. +lockdir=`echo "$cofile" | sed -e 's|[/\\:.-]|_|g'`.d +while true; do + if mkdir "$lockdir" >/dev/null 2>&1; then + break + fi + sleep 1 +done +# FIXME: race condition here if user kills between mkdir and trap. +trap "rmdir '$lockdir'; exit 1" 1 2 15 + +# Run the compile. +"$@" +ret=$? + +if test -f "$cofile"; then + test "$cofile" = "$ofile" || mv "$cofile" "$ofile" +elif test -f "${cofile}bj"; then + test "${cofile}bj" = "$ofile" || mv "${cofile}bj" "$ofile" +fi + +rmdir "$lockdir" +exit $ret + +# Local Variables: +# mode: shell-script +# sh-indentation: 2 +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "scriptversion=" +# time-stamp-format: "%:y-%02m-%02d.%02H" +# time-stamp-time-zone: "UTC" +# time-stamp-end: "; # UTC" +# End: diff --git a/config.guess b/config.guess new file mode 100755 index 0000000..1f5c50c --- /dev/null +++ b/config.guess @@ -0,0 +1,1420 @@ +#! /bin/sh +# Attempt to guess a canonical system name. +# Copyright 1992-2014 Free Software Foundation, Inc. + +timestamp='2014-03-23' + +# This file 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 . +# +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that +# program. This Exception is an additional permission under section 7 +# of the GNU General Public License, version 3 ("GPLv3"). +# +# Originally written by Per Bothner. +# +# You can get the latest version of this script from: +# http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess;hb=HEAD +# +# Please send patches with a ChangeLog entry to config-patches@gnu.org. + + +me=`echo "$0" | sed -e 's,.*/,,'` + +usage="\ +Usage: $0 [OPTION] + +Output the configuration name of the system \`$me' is run on. + +Operation modes: + -h, --help print this help, then exit + -t, --time-stamp print date of last modification, then exit + -v, --version print version number, then exit + +Report bugs and patches to ." + +version="\ +GNU config.guess ($timestamp) + +Originally written by Per Bothner. +Copyright 1992-2014 Free Software Foundation, Inc. + +This is free software; see the source for copying conditions. There is NO +warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." + +help=" +Try \`$me --help' for more information." + +# Parse command line +while test $# -gt 0 ; do + case $1 in + --time-stamp | --time* | -t ) + echo "$timestamp" ; exit ;; + --version | -v ) + echo "$version" ; exit ;; + --help | --h* | -h ) + echo "$usage"; exit ;; + -- ) # Stop option processing + shift; break ;; + - ) # Use stdin as input. + break ;; + -* ) + echo "$me: invalid option $1$help" >&2 + exit 1 ;; + * ) + break ;; + esac +done + +if test $# != 0; then + echo "$me: too many arguments$help" >&2 + exit 1 +fi + +trap 'exit 1' 1 2 15 + +# CC_FOR_BUILD -- compiler used by this script. Note that the use of a +# compiler to aid in system detection is discouraged as it requires +# temporary files to be created and, as you can see below, it is a +# headache to deal with in a portable fashion. + +# Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still +# use `HOST_CC' if defined, but it is deprecated. + +# Portable tmp directory creation inspired by the Autoconf team. + +set_cc_for_build=' +trap "exitcode=\$?; (rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null) && exit \$exitcode" 0 ; +trap "rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null; exit 1" 1 2 13 15 ; +: ${TMPDIR=/tmp} ; + { tmp=`(umask 077 && mktemp -d "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" ; } || + { test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir $tmp) ; } || + { tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir $tmp) && echo "Warning: creating insecure temp directory" >&2 ; } || + { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; } ; +dummy=$tmp/dummy ; +tmpfiles="$dummy.c $dummy.o $dummy.rel $dummy" ; +case $CC_FOR_BUILD,$HOST_CC,$CC in + ,,) echo "int x;" > $dummy.c ; + for c in cc gcc c89 c99 ; do + if ($c -c -o $dummy.o $dummy.c) >/dev/null 2>&1 ; then + CC_FOR_BUILD="$c"; break ; + fi ; + done ; + if test x"$CC_FOR_BUILD" = x ; then + CC_FOR_BUILD=no_compiler_found ; + fi + ;; + ,,*) CC_FOR_BUILD=$CC ;; + ,*,*) CC_FOR_BUILD=$HOST_CC ;; +esac ; set_cc_for_build= ;' + +# This is needed to find uname on a Pyramid OSx when run in the BSD universe. +# (ghazi@noc.rutgers.edu 1994-08-24) +if (test -f /.attbin/uname) >/dev/null 2>&1 ; then + PATH=$PATH:/.attbin ; export PATH +fi + +UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown +UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown +UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown +UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown + +case "${UNAME_SYSTEM}" in +Linux|GNU|GNU/*) + # If the system lacks a compiler, then just pick glibc. + # We could probably try harder. + LIBC=gnu + + eval $set_cc_for_build + cat <<-EOF > $dummy.c + #include + #if defined(__UCLIBC__) + LIBC=uclibc + #elif defined(__dietlibc__) + LIBC=dietlibc + #else + LIBC=gnu + #endif + EOF + eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^LIBC' | sed 's, ,,g'` + ;; +esac + +# Note: order is significant - the case branches are not exclusive. + +case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in + *:NetBSD:*:*) + # NetBSD (nbsd) targets should (where applicable) match one or + # more of the tuples: *-*-netbsdelf*, *-*-netbsdaout*, + # *-*-netbsdecoff* and *-*-netbsd*. For targets that recently + # switched to ELF, *-*-netbsd* would select the old + # object file format. This provides both forward + # compatibility and a consistent mechanism for selecting the + # object file format. + # + # Note: NetBSD doesn't particularly care about the vendor + # portion of the name. We always set it to "unknown". + sysctl="sysctl -n hw.machine_arch" + UNAME_MACHINE_ARCH=`(/sbin/$sysctl 2>/dev/null || \ + /usr/sbin/$sysctl 2>/dev/null || echo unknown)` + case "${UNAME_MACHINE_ARCH}" in + armeb) machine=armeb-unknown ;; + arm*) machine=arm-unknown ;; + sh3el) machine=shl-unknown ;; + sh3eb) machine=sh-unknown ;; + sh5el) machine=sh5le-unknown ;; + *) machine=${UNAME_MACHINE_ARCH}-unknown ;; + esac + # The Operating System including object format, if it has switched + # to ELF recently, or will in the future. + case "${UNAME_MACHINE_ARCH}" in + arm*|i386|m68k|ns32k|sh3*|sparc|vax) + eval $set_cc_for_build + if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \ + | grep -q __ELF__ + then + # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout). + # Return netbsd for either. FIX? + os=netbsd + else + os=netbsdelf + fi + ;; + *) + os=netbsd + ;; + esac + # The OS release + # Debian GNU/NetBSD machines have a different userland, and + # thus, need a distinct triplet. However, they do not need + # kernel version information, so it can be replaced with a + # suitable tag, in the style of linux-gnu. + case "${UNAME_VERSION}" in + Debian*) + release='-gnu' + ;; + *) + release=`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'` + ;; + esac + # Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM: + # contains redundant information, the shorter form: + # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used. + echo "${machine}-${os}${release}" + exit ;; + *:Bitrig:*:*) + UNAME_MACHINE_ARCH=`arch | sed 's/Bitrig.//'` + echo ${UNAME_MACHINE_ARCH}-unknown-bitrig${UNAME_RELEASE} + exit ;; + *:OpenBSD:*:*) + UNAME_MACHINE_ARCH=`arch | sed 's/OpenBSD.//'` + echo ${UNAME_MACHINE_ARCH}-unknown-openbsd${UNAME_RELEASE} + exit ;; + *:ekkoBSD:*:*) + echo ${UNAME_MACHINE}-unknown-ekkobsd${UNAME_RELEASE} + exit ;; + *:SolidBSD:*:*) + echo ${UNAME_MACHINE}-unknown-solidbsd${UNAME_RELEASE} + exit ;; + macppc:MirBSD:*:*) + echo powerpc-unknown-mirbsd${UNAME_RELEASE} + exit ;; + *:MirBSD:*:*) + echo ${UNAME_MACHINE}-unknown-mirbsd${UNAME_RELEASE} + exit ;; + alpha:OSF1:*:*) + case $UNAME_RELEASE in + *4.0) + UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'` + ;; + *5.*) + UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $4}'` + ;; + esac + # According to Compaq, /usr/sbin/psrinfo has been available on + # OSF/1 and Tru64 systems produced since 1995. I hope that + # covers most systems running today. This code pipes the CPU + # types through head -n 1, so we only detect the type of CPU 0. + ALPHA_CPU_TYPE=`/usr/sbin/psrinfo -v | sed -n -e 's/^ The alpha \(.*\) processor.*$/\1/p' | head -n 1` + case "$ALPHA_CPU_TYPE" in + "EV4 (21064)") + UNAME_MACHINE="alpha" ;; + "EV4.5 (21064)") + UNAME_MACHINE="alpha" ;; + "LCA4 (21066/21068)") + UNAME_MACHINE="alpha" ;; + "EV5 (21164)") + UNAME_MACHINE="alphaev5" ;; + "EV5.6 (21164A)") + UNAME_MACHINE="alphaev56" ;; + "EV5.6 (21164PC)") + UNAME_MACHINE="alphapca56" ;; + "EV5.7 (21164PC)") + UNAME_MACHINE="alphapca57" ;; + "EV6 (21264)") + UNAME_MACHINE="alphaev6" ;; + "EV6.7 (21264A)") + UNAME_MACHINE="alphaev67" ;; + "EV6.8CB (21264C)") + UNAME_MACHINE="alphaev68" ;; + "EV6.8AL (21264B)") + UNAME_MACHINE="alphaev68" ;; + "EV6.8CX (21264D)") + UNAME_MACHINE="alphaev68" ;; + "EV6.9A (21264/EV69A)") + UNAME_MACHINE="alphaev69" ;; + "EV7 (21364)") + UNAME_MACHINE="alphaev7" ;; + "EV7.9 (21364A)") + UNAME_MACHINE="alphaev79" ;; + esac + # A Pn.n version is a patched version. + # A Vn.n version is a released version. + # A Tn.n version is a released field test version. + # A Xn.n version is an unreleased experimental baselevel. + # 1.2 uses "1.2" for uname -r. + echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[PVTX]//' | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` + # Reset EXIT trap before exiting to avoid spurious non-zero exit code. + exitcode=$? + trap '' 0 + exit $exitcode ;; + Alpha\ *:Windows_NT*:*) + # How do we know it's Interix rather than the generic POSIX subsystem? + # Should we change UNAME_MACHINE based on the output of uname instead + # of the specific Alpha model? + echo alpha-pc-interix + exit ;; + 21064:Windows_NT:50:3) + echo alpha-dec-winnt3.5 + exit ;; + Amiga*:UNIX_System_V:4.0:*) + echo m68k-unknown-sysv4 + exit ;; + *:[Aa]miga[Oo][Ss]:*:*) + echo ${UNAME_MACHINE}-unknown-amigaos + exit ;; + *:[Mm]orph[Oo][Ss]:*:*) + echo ${UNAME_MACHINE}-unknown-morphos + exit ;; + *:OS/390:*:*) + echo i370-ibm-openedition + exit ;; + *:z/VM:*:*) + echo s390-ibm-zvmoe + exit ;; + *:OS400:*:*) + echo powerpc-ibm-os400 + exit ;; + arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*) + echo arm-acorn-riscix${UNAME_RELEASE} + exit ;; + arm*:riscos:*:*|arm*:RISCOS:*:*) + echo arm-unknown-riscos + exit ;; + SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*) + echo hppa1.1-hitachi-hiuxmpp + exit ;; + Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*) + # akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE. + if test "`(/bin/universe) 2>/dev/null`" = att ; then + echo pyramid-pyramid-sysv3 + else + echo pyramid-pyramid-bsd + fi + exit ;; + NILE*:*:*:dcosx) + echo pyramid-pyramid-svr4 + exit ;; + DRS?6000:unix:4.0:6*) + echo sparc-icl-nx6 + exit ;; + DRS?6000:UNIX_SV:4.2*:7* | DRS?6000:isis:4.2*:7*) + case `/usr/bin/uname -p` in + sparc) echo sparc-icl-nx7; exit ;; + esac ;; + s390x:SunOS:*:*) + echo ${UNAME_MACHINE}-ibm-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit ;; + sun4H:SunOS:5.*:*) + echo sparc-hal-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit ;; + sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*) + echo sparc-sun-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit ;; + i86pc:AuroraUX:5.*:* | i86xen:AuroraUX:5.*:*) + echo i386-pc-auroraux${UNAME_RELEASE} + exit ;; + i86pc:SunOS:5.*:* | i86xen:SunOS:5.*:*) + eval $set_cc_for_build + SUN_ARCH="i386" + # If there is a compiler, see if it is configured for 64-bit objects. + # Note that the Sun cc does not turn __LP64__ into 1 like gcc does. + # This test works for both compilers. + if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then + if (echo '#ifdef __amd64'; echo IS_64BIT_ARCH; echo '#endif') | \ + (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \ + grep IS_64BIT_ARCH >/dev/null + then + SUN_ARCH="x86_64" + fi + fi + echo ${SUN_ARCH}-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit ;; + sun4*:SunOS:6*:*) + # According to config.sub, this is the proper way to canonicalize + # SunOS6. Hard to guess exactly what SunOS6 will be like, but + # it's likely to be more like Solaris than SunOS4. + echo sparc-sun-solaris3`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit ;; + sun4*:SunOS:*:*) + case "`/usr/bin/arch -k`" in + Series*|S4*) + UNAME_RELEASE=`uname -v` + ;; + esac + # Japanese Language versions have a version number like `4.1.3-JL'. + echo sparc-sun-sunos`echo ${UNAME_RELEASE}|sed -e 's/-/_/'` + exit ;; + sun3*:SunOS:*:*) + echo m68k-sun-sunos${UNAME_RELEASE} + exit ;; + sun*:*:4.2BSD:*) + UNAME_RELEASE=`(sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null` + test "x${UNAME_RELEASE}" = "x" && UNAME_RELEASE=3 + case "`/bin/arch`" in + sun3) + echo m68k-sun-sunos${UNAME_RELEASE} + ;; + sun4) + echo sparc-sun-sunos${UNAME_RELEASE} + ;; + esac + exit ;; + aushp:SunOS:*:*) + echo sparc-auspex-sunos${UNAME_RELEASE} + exit ;; + # The situation for MiNT is a little confusing. The machine name + # can be virtually everything (everything which is not + # "atarist" or "atariste" at least should have a processor + # > m68000). The system name ranges from "MiNT" over "FreeMiNT" + # to the lowercase version "mint" (or "freemint"). Finally + # the system name "TOS" denotes a system which is actually not + # MiNT. But MiNT is downward compatible to TOS, so this should + # be no problem. + atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*) + echo m68k-atari-mint${UNAME_RELEASE} + exit ;; + atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*) + echo m68k-atari-mint${UNAME_RELEASE} + exit ;; + *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*) + echo m68k-atari-mint${UNAME_RELEASE} + exit ;; + milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*) + echo m68k-milan-mint${UNAME_RELEASE} + exit ;; + hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*) + echo m68k-hades-mint${UNAME_RELEASE} + exit ;; + *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*) + echo m68k-unknown-mint${UNAME_RELEASE} + exit ;; + m68k:machten:*:*) + echo m68k-apple-machten${UNAME_RELEASE} + exit ;; + powerpc:machten:*:*) + echo powerpc-apple-machten${UNAME_RELEASE} + exit ;; + RISC*:Mach:*:*) + echo mips-dec-mach_bsd4.3 + exit ;; + RISC*:ULTRIX:*:*) + echo mips-dec-ultrix${UNAME_RELEASE} + exit ;; + VAX*:ULTRIX*:*:*) + echo vax-dec-ultrix${UNAME_RELEASE} + exit ;; + 2020:CLIX:*:* | 2430:CLIX:*:*) + echo clipper-intergraph-clix${UNAME_RELEASE} + exit ;; + mips:*:*:UMIPS | mips:*:*:RISCos) + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c +#ifdef __cplusplus +#include /* for printf() prototype */ + int main (int argc, char *argv[]) { +#else + int main (argc, argv) int argc; char *argv[]; { +#endif + #if defined (host_mips) && defined (MIPSEB) + #if defined (SYSTYPE_SYSV) + printf ("mips-mips-riscos%ssysv\n", argv[1]); exit (0); + #endif + #if defined (SYSTYPE_SVR4) + printf ("mips-mips-riscos%ssvr4\n", argv[1]); exit (0); + #endif + #if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD) + printf ("mips-mips-riscos%sbsd\n", argv[1]); exit (0); + #endif + #endif + exit (-1); + } +EOF + $CC_FOR_BUILD -o $dummy $dummy.c && + dummyarg=`echo "${UNAME_RELEASE}" | sed -n 's/\([0-9]*\).*/\1/p'` && + SYSTEM_NAME=`$dummy $dummyarg` && + { echo "$SYSTEM_NAME"; exit; } + echo mips-mips-riscos${UNAME_RELEASE} + exit ;; + Motorola:PowerMAX_OS:*:*) + echo powerpc-motorola-powermax + exit ;; + Motorola:*:4.3:PL8-*) + echo powerpc-harris-powermax + exit ;; + Night_Hawk:*:*:PowerMAX_OS | Synergy:PowerMAX_OS:*:*) + echo powerpc-harris-powermax + exit ;; + Night_Hawk:Power_UNIX:*:*) + echo powerpc-harris-powerunix + exit ;; + m88k:CX/UX:7*:*) + echo m88k-harris-cxux7 + exit ;; + m88k:*:4*:R4*) + echo m88k-motorola-sysv4 + exit ;; + m88k:*:3*:R3*) + echo m88k-motorola-sysv3 + exit ;; + AViiON:dgux:*:*) + # DG/UX returns AViiON for all architectures + UNAME_PROCESSOR=`/usr/bin/uname -p` + if [ $UNAME_PROCESSOR = mc88100 ] || [ $UNAME_PROCESSOR = mc88110 ] + then + if [ ${TARGET_BINARY_INTERFACE}x = m88kdguxelfx ] || \ + [ ${TARGET_BINARY_INTERFACE}x = x ] + then + echo m88k-dg-dgux${UNAME_RELEASE} + else + echo m88k-dg-dguxbcs${UNAME_RELEASE} + fi + else + echo i586-dg-dgux${UNAME_RELEASE} + fi + exit ;; + M88*:DolphinOS:*:*) # DolphinOS (SVR3) + echo m88k-dolphin-sysv3 + exit ;; + M88*:*:R3*:*) + # Delta 88k system running SVR3 + echo m88k-motorola-sysv3 + exit ;; + XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3) + echo m88k-tektronix-sysv3 + exit ;; + Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD) + echo m68k-tektronix-bsd + exit ;; + *:IRIX*:*:*) + echo mips-sgi-irix`echo ${UNAME_RELEASE}|sed -e 's/-/_/g'` + exit ;; + ????????:AIX?:[12].1:2) # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX. + echo romp-ibm-aix # uname -m gives an 8 hex-code CPU id + exit ;; # Note that: echo "'`uname -s`'" gives 'AIX ' + i*86:AIX:*:*) + echo i386-ibm-aix + exit ;; + ia64:AIX:*:*) + if [ -x /usr/bin/oslevel ] ; then + IBM_REV=`/usr/bin/oslevel` + else + IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE} + fi + echo ${UNAME_MACHINE}-ibm-aix${IBM_REV} + exit ;; + *:AIX:2:3) + if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + #include + + main() + { + if (!__power_pc()) + exit(1); + puts("powerpc-ibm-aix3.2.5"); + exit(0); + } +EOF + if $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy` + then + echo "$SYSTEM_NAME" + else + echo rs6000-ibm-aix3.2.5 + fi + elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then + echo rs6000-ibm-aix3.2.4 + else + echo rs6000-ibm-aix3.2 + fi + exit ;; + *:AIX:*:[4567]) + IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }'` + if /usr/sbin/lsattr -El ${IBM_CPU_ID} | grep ' POWER' >/dev/null 2>&1; then + IBM_ARCH=rs6000 + else + IBM_ARCH=powerpc + fi + if [ -x /usr/bin/oslevel ] ; then + IBM_REV=`/usr/bin/oslevel` + else + IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE} + fi + echo ${IBM_ARCH}-ibm-aix${IBM_REV} + exit ;; + *:AIX:*:*) + echo rs6000-ibm-aix + exit ;; + ibmrt:4.4BSD:*|romp-ibm:BSD:*) + echo romp-ibm-bsd4.4 + exit ;; + ibmrt:*BSD:*|romp-ibm:BSD:*) # covers RT/PC BSD and + echo romp-ibm-bsd${UNAME_RELEASE} # 4.3 with uname added to + exit ;; # report: romp-ibm BSD 4.3 + *:BOSX:*:*) + echo rs6000-bull-bosx + exit ;; + DPX/2?00:B.O.S.:*:*) + echo m68k-bull-sysv3 + exit ;; + 9000/[34]??:4.3bsd:1.*:*) + echo m68k-hp-bsd + exit ;; + hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*) + echo m68k-hp-bsd4.4 + exit ;; + 9000/[34678]??:HP-UX:*:*) + HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'` + case "${UNAME_MACHINE}" in + 9000/31? ) HP_ARCH=m68000 ;; + 9000/[34]?? ) HP_ARCH=m68k ;; + 9000/[678][0-9][0-9]) + if [ -x /usr/bin/getconf ]; then + sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null` + sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null` + case "${sc_cpu_version}" in + 523) HP_ARCH="hppa1.0" ;; # CPU_PA_RISC1_0 + 528) HP_ARCH="hppa1.1" ;; # CPU_PA_RISC1_1 + 532) # CPU_PA_RISC2_0 + case "${sc_kernel_bits}" in + 32) HP_ARCH="hppa2.0n" ;; + 64) HP_ARCH="hppa2.0w" ;; + '') HP_ARCH="hppa2.0" ;; # HP-UX 10.20 + esac ;; + esac + fi + if [ "${HP_ARCH}" = "" ]; then + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + + #define _HPUX_SOURCE + #include + #include + + int main () + { + #if defined(_SC_KERNEL_BITS) + long bits = sysconf(_SC_KERNEL_BITS); + #endif + long cpu = sysconf (_SC_CPU_VERSION); + + switch (cpu) + { + case CPU_PA_RISC1_0: puts ("hppa1.0"); break; + case CPU_PA_RISC1_1: puts ("hppa1.1"); break; + case CPU_PA_RISC2_0: + #if defined(_SC_KERNEL_BITS) + switch (bits) + { + case 64: puts ("hppa2.0w"); break; + case 32: puts ("hppa2.0n"); break; + default: puts ("hppa2.0"); break; + } break; + #else /* !defined(_SC_KERNEL_BITS) */ + puts ("hppa2.0"); break; + #endif + default: puts ("hppa1.0"); break; + } + exit (0); + } +EOF + (CCOPTS= $CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null) && HP_ARCH=`$dummy` + test -z "$HP_ARCH" && HP_ARCH=hppa + fi ;; + esac + if [ ${HP_ARCH} = "hppa2.0w" ] + then + eval $set_cc_for_build + + # hppa2.0w-hp-hpux* has a 64-bit kernel and a compiler generating + # 32-bit code. hppa64-hp-hpux* has the same kernel and a compiler + # generating 64-bit code. GNU and HP use different nomenclature: + # + # $ CC_FOR_BUILD=cc ./config.guess + # => hppa2.0w-hp-hpux11.23 + # $ CC_FOR_BUILD="cc +DA2.0w" ./config.guess + # => hppa64-hp-hpux11.23 + + if echo __LP64__ | (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | + grep -q __LP64__ + then + HP_ARCH="hppa2.0w" + else + HP_ARCH="hppa64" + fi + fi + echo ${HP_ARCH}-hp-hpux${HPUX_REV} + exit ;; + ia64:HP-UX:*:*) + HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'` + echo ia64-hp-hpux${HPUX_REV} + exit ;; + 3050*:HI-UX:*:*) + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + #include + int + main () + { + long cpu = sysconf (_SC_CPU_VERSION); + /* The order matters, because CPU_IS_HP_MC68K erroneously returns + true for CPU_PA_RISC1_0. CPU_IS_PA_RISC returns correct + results, however. */ + if (CPU_IS_PA_RISC (cpu)) + { + switch (cpu) + { + case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break; + case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break; + case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break; + default: puts ("hppa-hitachi-hiuxwe2"); break; + } + } + else if (CPU_IS_HP_MC68K (cpu)) + puts ("m68k-hitachi-hiuxwe2"); + else puts ("unknown-hitachi-hiuxwe2"); + exit (0); + } +EOF + $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy` && + { echo "$SYSTEM_NAME"; exit; } + echo unknown-hitachi-hiuxwe2 + exit ;; + 9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:* ) + echo hppa1.1-hp-bsd + exit ;; + 9000/8??:4.3bsd:*:*) + echo hppa1.0-hp-bsd + exit ;; + *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*) + echo hppa1.0-hp-mpeix + exit ;; + hp7??:OSF1:*:* | hp8?[79]:OSF1:*:* ) + echo hppa1.1-hp-osf + exit ;; + hp8??:OSF1:*:*) + echo hppa1.0-hp-osf + exit ;; + i*86:OSF1:*:*) + if [ -x /usr/sbin/sysversion ] ; then + echo ${UNAME_MACHINE}-unknown-osf1mk + else + echo ${UNAME_MACHINE}-unknown-osf1 + fi + exit ;; + parisc*:Lites*:*:*) + echo hppa1.1-hp-lites + exit ;; + C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*) + echo c1-convex-bsd + exit ;; + C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*) + if getsysinfo -f scalar_acc + then echo c32-convex-bsd + else echo c2-convex-bsd + fi + exit ;; + C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*) + echo c34-convex-bsd + exit ;; + C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*) + echo c38-convex-bsd + exit ;; + C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*) + echo c4-convex-bsd + exit ;; + CRAY*Y-MP:*:*:*) + echo ymp-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit ;; + CRAY*[A-Z]90:*:*:*) + echo ${UNAME_MACHINE}-cray-unicos${UNAME_RELEASE} \ + | sed -e 's/CRAY.*\([A-Z]90\)/\1/' \ + -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \ + -e 's/\.[^.]*$/.X/' + exit ;; + CRAY*TS:*:*:*) + echo t90-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit ;; + CRAY*T3E:*:*:*) + echo alphaev5-cray-unicosmk${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit ;; + CRAY*SV1:*:*:*) + echo sv1-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit ;; + *:UNICOS/mp:*:*) + echo craynv-cray-unicosmp${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit ;; + F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*) + FUJITSU_PROC=`uname -m | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` + FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'` + FUJITSU_REL=`echo ${UNAME_RELEASE} | sed -e 's/ /_/'` + echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" + exit ;; + 5000:UNIX_System_V:4.*:*) + FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'` + FUJITSU_REL=`echo ${UNAME_RELEASE} | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/ /_/'` + echo "sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" + exit ;; + i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*) + echo ${UNAME_MACHINE}-pc-bsdi${UNAME_RELEASE} + exit ;; + sparc*:BSD/OS:*:*) + echo sparc-unknown-bsdi${UNAME_RELEASE} + exit ;; + *:BSD/OS:*:*) + echo ${UNAME_MACHINE}-unknown-bsdi${UNAME_RELEASE} + exit ;; + *:FreeBSD:*:*) + UNAME_PROCESSOR=`/usr/bin/uname -p` + case ${UNAME_PROCESSOR} in + amd64) + echo x86_64-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;; + *) + echo ${UNAME_PROCESSOR}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;; + esac + exit ;; + i*:CYGWIN*:*) + echo ${UNAME_MACHINE}-pc-cygwin + exit ;; + *:MINGW64*:*) + echo ${UNAME_MACHINE}-pc-mingw64 + exit ;; + *:MINGW*:*) + echo ${UNAME_MACHINE}-pc-mingw32 + exit ;; + *:MSYS*:*) + echo ${UNAME_MACHINE}-pc-msys + exit ;; + i*:windows32*:*) + # uname -m includes "-pc" on this system. + echo ${UNAME_MACHINE}-mingw32 + exit ;; + i*:PW*:*) + echo ${UNAME_MACHINE}-pc-pw32 + exit ;; + *:Interix*:*) + case ${UNAME_MACHINE} in + x86) + echo i586-pc-interix${UNAME_RELEASE} + exit ;; + authenticamd | genuineintel | EM64T) + echo x86_64-unknown-interix${UNAME_RELEASE} + exit ;; + IA64) + echo ia64-unknown-interix${UNAME_RELEASE} + exit ;; + esac ;; + [345]86:Windows_95:* | [345]86:Windows_98:* | [345]86:Windows_NT:*) + echo i${UNAME_MACHINE}-pc-mks + exit ;; + 8664:Windows_NT:*) + echo x86_64-pc-mks + exit ;; + i*:Windows_NT*:* | Pentium*:Windows_NT*:*) + # How do we know it's Interix rather than the generic POSIX subsystem? + # It also conflicts with pre-2.0 versions of AT&T UWIN. Should we + # UNAME_MACHINE based on the output of uname instead of i386? + echo i586-pc-interix + exit ;; + i*:UWIN*:*) + echo ${UNAME_MACHINE}-pc-uwin + exit ;; + amd64:CYGWIN*:*:* | x86_64:CYGWIN*:*:*) + echo x86_64-unknown-cygwin + exit ;; + p*:CYGWIN*:*) + echo powerpcle-unknown-cygwin + exit ;; + prep*:SunOS:5.*:*) + echo powerpcle-unknown-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit ;; + *:GNU:*:*) + # the GNU system + echo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-unknown-${LIBC}`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'` + exit ;; + *:GNU/*:*:*) + # other systems with GNU libc and userland + echo ${UNAME_MACHINE}-unknown-`echo ${UNAME_SYSTEM} | sed 's,^[^/]*/,,' | tr '[A-Z]' '[a-z]'``echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`-${LIBC} + exit ;; + i*86:Minix:*:*) + echo ${UNAME_MACHINE}-pc-minix + exit ;; + aarch64:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + aarch64_be:Linux:*:*) + UNAME_MACHINE=aarch64_be + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + alpha:Linux:*:*) + case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in + EV5) UNAME_MACHINE=alphaev5 ;; + EV56) UNAME_MACHINE=alphaev56 ;; + PCA56) UNAME_MACHINE=alphapca56 ;; + PCA57) UNAME_MACHINE=alphapca56 ;; + EV6) UNAME_MACHINE=alphaev6 ;; + EV67) UNAME_MACHINE=alphaev67 ;; + EV68*) UNAME_MACHINE=alphaev68 ;; + esac + objdump --private-headers /bin/sh | grep -q ld.so.1 + if test "$?" = 0 ; then LIBC="gnulibc1" ; fi + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + arc:Linux:*:* | arceb:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + arm*:Linux:*:*) + eval $set_cc_for_build + if echo __ARM_EABI__ | $CC_FOR_BUILD -E - 2>/dev/null \ + | grep -q __ARM_EABI__ + then + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + else + if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \ + | grep -q __ARM_PCS_VFP + then + echo ${UNAME_MACHINE}-unknown-linux-${LIBC}eabi + else + echo ${UNAME_MACHINE}-unknown-linux-${LIBC}eabihf + fi + fi + exit ;; + avr32*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + cris:Linux:*:*) + echo ${UNAME_MACHINE}-axis-linux-${LIBC} + exit ;; + crisv32:Linux:*:*) + echo ${UNAME_MACHINE}-axis-linux-${LIBC} + exit ;; + frv:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + hexagon:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + i*86:Linux:*:*) + echo ${UNAME_MACHINE}-pc-linux-${LIBC} + exit ;; + ia64:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + m32r*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + m68*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + mips:Linux:*:* | mips64:Linux:*:*) + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + #undef CPU + #undef ${UNAME_MACHINE} + #undef ${UNAME_MACHINE}el + #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL) + CPU=${UNAME_MACHINE}el + #else + #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB) + CPU=${UNAME_MACHINE} + #else + CPU= + #endif + #endif +EOF + eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^CPU'` + test x"${CPU}" != x && { echo "${CPU}-unknown-linux-${LIBC}"; exit; } + ;; + openrisc*:Linux:*:*) + echo or1k-unknown-linux-${LIBC} + exit ;; + or32:Linux:*:* | or1k*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + padre:Linux:*:*) + echo sparc-unknown-linux-${LIBC} + exit ;; + parisc64:Linux:*:* | hppa64:Linux:*:*) + echo hppa64-unknown-linux-${LIBC} + exit ;; + parisc:Linux:*:* | hppa:Linux:*:*) + # Look for CPU level + case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in + PA7*) echo hppa1.1-unknown-linux-${LIBC} ;; + PA8*) echo hppa2.0-unknown-linux-${LIBC} ;; + *) echo hppa-unknown-linux-${LIBC} ;; + esac + exit ;; + ppc64:Linux:*:*) + echo powerpc64-unknown-linux-${LIBC} + exit ;; + ppc:Linux:*:*) + echo powerpc-unknown-linux-${LIBC} + exit ;; + ppc64le:Linux:*:*) + echo powerpc64le-unknown-linux-${LIBC} + exit ;; + ppcle:Linux:*:*) + echo powerpcle-unknown-linux-${LIBC} + exit ;; + s390:Linux:*:* | s390x:Linux:*:*) + echo ${UNAME_MACHINE}-ibm-linux-${LIBC} + exit ;; + sh64*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + sh*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + sparc:Linux:*:* | sparc64:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + tile*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + vax:Linux:*:*) + echo ${UNAME_MACHINE}-dec-linux-${LIBC} + exit ;; + x86_64:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + xtensa*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + i*86:DYNIX/ptx:4*:*) + # ptx 4.0 does uname -s correctly, with DYNIX/ptx in there. + # earlier versions are messed up and put the nodename in both + # sysname and nodename. + echo i386-sequent-sysv4 + exit ;; + i*86:UNIX_SV:4.2MP:2.*) + # Unixware is an offshoot of SVR4, but it has its own version + # number series starting with 2... + # I am not positive that other SVR4 systems won't match this, + # I just have to hope. -- rms. + # Use sysv4.2uw... so that sysv4* matches it. + echo ${UNAME_MACHINE}-pc-sysv4.2uw${UNAME_VERSION} + exit ;; + i*86:OS/2:*:*) + # If we were able to find `uname', then EMX Unix compatibility + # is probably installed. + echo ${UNAME_MACHINE}-pc-os2-emx + exit ;; + i*86:XTS-300:*:STOP) + echo ${UNAME_MACHINE}-unknown-stop + exit ;; + i*86:atheos:*:*) + echo ${UNAME_MACHINE}-unknown-atheos + exit ;; + i*86:syllable:*:*) + echo ${UNAME_MACHINE}-pc-syllable + exit ;; + i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.[02]*:*) + echo i386-unknown-lynxos${UNAME_RELEASE} + exit ;; + i*86:*DOS:*:*) + echo ${UNAME_MACHINE}-pc-msdosdjgpp + exit ;; + i*86:*:4.*:* | i*86:SYSTEM_V:4.*:*) + UNAME_REL=`echo ${UNAME_RELEASE} | sed 's/\/MP$//'` + if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then + echo ${UNAME_MACHINE}-univel-sysv${UNAME_REL} + else + echo ${UNAME_MACHINE}-pc-sysv${UNAME_REL} + fi + exit ;; + i*86:*:5:[678]*) + # UnixWare 7.x, OpenUNIX and OpenServer 6. + case `/bin/uname -X | grep "^Machine"` in + *486*) UNAME_MACHINE=i486 ;; + *Pentium) UNAME_MACHINE=i586 ;; + *Pent*|*Celeron) UNAME_MACHINE=i686 ;; + esac + echo ${UNAME_MACHINE}-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION} + exit ;; + i*86:*:3.2:*) + if test -f /usr/options/cb.name; then + UNAME_REL=`sed -n 's/.*Version //p' /dev/null >/dev/null ; then + UNAME_REL=`(/bin/uname -X|grep Release|sed -e 's/.*= //')` + (/bin/uname -X|grep i80486 >/dev/null) && UNAME_MACHINE=i486 + (/bin/uname -X|grep '^Machine.*Pentium' >/dev/null) \ + && UNAME_MACHINE=i586 + (/bin/uname -X|grep '^Machine.*Pent *II' >/dev/null) \ + && UNAME_MACHINE=i686 + (/bin/uname -X|grep '^Machine.*Pentium Pro' >/dev/null) \ + && UNAME_MACHINE=i686 + echo ${UNAME_MACHINE}-pc-sco$UNAME_REL + else + echo ${UNAME_MACHINE}-pc-sysv32 + fi + exit ;; + pc:*:*:*) + # Left here for compatibility: + # uname -m prints for DJGPP always 'pc', but it prints nothing about + # the processor, so we play safe by assuming i586. + # Note: whatever this is, it MUST be the same as what config.sub + # prints for the "djgpp" host, or else GDB configury will decide that + # this is a cross-build. + echo i586-pc-msdosdjgpp + exit ;; + Intel:Mach:3*:*) + echo i386-pc-mach3 + exit ;; + paragon:*:*:*) + echo i860-intel-osf1 + exit ;; + i860:*:4.*:*) # i860-SVR4 + if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then + echo i860-stardent-sysv${UNAME_RELEASE} # Stardent Vistra i860-SVR4 + else # Add other i860-SVR4 vendors below as they are discovered. + echo i860-unknown-sysv${UNAME_RELEASE} # Unknown i860-SVR4 + fi + exit ;; + mini*:CTIX:SYS*5:*) + # "miniframe" + echo m68010-convergent-sysv + exit ;; + mc68k:UNIX:SYSTEM5:3.51m) + echo m68k-convergent-sysv + exit ;; + M680?0:D-NIX:5.3:*) + echo m68k-diab-dnix + exit ;; + M68*:*:R3V[5678]*:*) + test -r /sysV68 && { echo 'm68k-motorola-sysv'; exit; } ;; + 3[345]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 3[34]??/*:*:4.0:3.0 | 4400:*:4.0:3.0 | 4850:*:4.0:3.0 | SKA40:*:4.0:3.0 | SDS2:*:4.0:3.0 | SHG2:*:4.0:3.0 | S7501*:*:4.0:3.0) + OS_REL='' + test -r /etc/.relid \ + && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` + /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ + && { echo i486-ncr-sysv4.3${OS_REL}; exit; } + /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ + && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;; + 3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*) + /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ + && { echo i486-ncr-sysv4; exit; } ;; + NCR*:*:4.2:* | MPRAS*:*:4.2:*) + OS_REL='.3' + test -r /etc/.relid \ + && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` + /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ + && { echo i486-ncr-sysv4.3${OS_REL}; exit; } + /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ + && { echo i586-ncr-sysv4.3${OS_REL}; exit; } + /bin/uname -p 2>/dev/null | /bin/grep pteron >/dev/null \ + && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;; + m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*) + echo m68k-unknown-lynxos${UNAME_RELEASE} + exit ;; + mc68030:UNIX_System_V:4.*:*) + echo m68k-atari-sysv4 + exit ;; + TSUNAMI:LynxOS:2.*:*) + echo sparc-unknown-lynxos${UNAME_RELEASE} + exit ;; + rs6000:LynxOS:2.*:*) + echo rs6000-unknown-lynxos${UNAME_RELEASE} + exit ;; + PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.[02]*:*) + echo powerpc-unknown-lynxos${UNAME_RELEASE} + exit ;; + SM[BE]S:UNIX_SV:*:*) + echo mips-dde-sysv${UNAME_RELEASE} + exit ;; + RM*:ReliantUNIX-*:*:*) + echo mips-sni-sysv4 + exit ;; + RM*:SINIX-*:*:*) + echo mips-sni-sysv4 + exit ;; + *:SINIX-*:*:*) + if uname -p 2>/dev/null >/dev/null ; then + UNAME_MACHINE=`(uname -p) 2>/dev/null` + echo ${UNAME_MACHINE}-sni-sysv4 + else + echo ns32k-sni-sysv + fi + exit ;; + PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort + # says + echo i586-unisys-sysv4 + exit ;; + *:UNIX_System_V:4*:FTX*) + # From Gerald Hewes . + # How about differentiating between stratus architectures? -djm + echo hppa1.1-stratus-sysv4 + exit ;; + *:*:*:FTX*) + # From seanf@swdc.stratus.com. + echo i860-stratus-sysv4 + exit ;; + i*86:VOS:*:*) + # From Paul.Green@stratus.com. + echo ${UNAME_MACHINE}-stratus-vos + exit ;; + *:VOS:*:*) + # From Paul.Green@stratus.com. + echo hppa1.1-stratus-vos + exit ;; + mc68*:A/UX:*:*) + echo m68k-apple-aux${UNAME_RELEASE} + exit ;; + news*:NEWS-OS:6*:*) + echo mips-sony-newsos6 + exit ;; + R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*) + if [ -d /usr/nec ]; then + echo mips-nec-sysv${UNAME_RELEASE} + else + echo mips-unknown-sysv${UNAME_RELEASE} + fi + exit ;; + BeBox:BeOS:*:*) # BeOS running on hardware made by Be, PPC only. + echo powerpc-be-beos + exit ;; + BeMac:BeOS:*:*) # BeOS running on Mac or Mac clone, PPC only. + echo powerpc-apple-beos + exit ;; + BePC:BeOS:*:*) # BeOS running on Intel PC compatible. + echo i586-pc-beos + exit ;; + BePC:Haiku:*:*) # Haiku running on Intel PC compatible. + echo i586-pc-haiku + exit ;; + x86_64:Haiku:*:*) + echo x86_64-unknown-haiku + exit ;; + SX-4:SUPER-UX:*:*) + echo sx4-nec-superux${UNAME_RELEASE} + exit ;; + SX-5:SUPER-UX:*:*) + echo sx5-nec-superux${UNAME_RELEASE} + exit ;; + SX-6:SUPER-UX:*:*) + echo sx6-nec-superux${UNAME_RELEASE} + exit ;; + SX-7:SUPER-UX:*:*) + echo sx7-nec-superux${UNAME_RELEASE} + exit ;; + SX-8:SUPER-UX:*:*) + echo sx8-nec-superux${UNAME_RELEASE} + exit ;; + SX-8R:SUPER-UX:*:*) + echo sx8r-nec-superux${UNAME_RELEASE} + exit ;; + Power*:Rhapsody:*:*) + echo powerpc-apple-rhapsody${UNAME_RELEASE} + exit ;; + *:Rhapsody:*:*) + echo ${UNAME_MACHINE}-apple-rhapsody${UNAME_RELEASE} + exit ;; + *:Darwin:*:*) + UNAME_PROCESSOR=`uname -p` || UNAME_PROCESSOR=unknown + eval $set_cc_for_build + if test "$UNAME_PROCESSOR" = unknown ; then + UNAME_PROCESSOR=powerpc + fi + if test `echo "$UNAME_RELEASE" | sed -e 's/\..*//'` -le 10 ; then + if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then + if (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \ + (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \ + grep IS_64BIT_ARCH >/dev/null + then + case $UNAME_PROCESSOR in + i386) UNAME_PROCESSOR=x86_64 ;; + powerpc) UNAME_PROCESSOR=powerpc64 ;; + esac + fi + fi + elif test "$UNAME_PROCESSOR" = i386 ; then + # Avoid executing cc on OS X 10.9, as it ships with a stub + # that puts up a graphical alert prompting to install + # developer tools. Any system running Mac OS X 10.7 or + # later (Darwin 11 and later) is required to have a 64-bit + # processor. This is not true of the ARM version of Darwin + # that Apple uses in portable devices. + UNAME_PROCESSOR=x86_64 + fi + echo ${UNAME_PROCESSOR}-apple-darwin${UNAME_RELEASE} + exit ;; + *:procnto*:*:* | *:QNX:[0123456789]*:*) + UNAME_PROCESSOR=`uname -p` + if test "$UNAME_PROCESSOR" = "x86"; then + UNAME_PROCESSOR=i386 + UNAME_MACHINE=pc + fi + echo ${UNAME_PROCESSOR}-${UNAME_MACHINE}-nto-qnx${UNAME_RELEASE} + exit ;; + *:QNX:*:4*) + echo i386-pc-qnx + exit ;; + NEO-?:NONSTOP_KERNEL:*:*) + echo neo-tandem-nsk${UNAME_RELEASE} + exit ;; + NSE-*:NONSTOP_KERNEL:*:*) + echo nse-tandem-nsk${UNAME_RELEASE} + exit ;; + NSR-?:NONSTOP_KERNEL:*:*) + echo nsr-tandem-nsk${UNAME_RELEASE} + exit ;; + *:NonStop-UX:*:*) + echo mips-compaq-nonstopux + exit ;; + BS2000:POSIX*:*:*) + echo bs2000-siemens-sysv + exit ;; + DS/*:UNIX_System_V:*:*) + echo ${UNAME_MACHINE}-${UNAME_SYSTEM}-${UNAME_RELEASE} + exit ;; + *:Plan9:*:*) + # "uname -m" is not consistent, so use $cputype instead. 386 + # is converted to i386 for consistency with other x86 + # operating systems. + if test "$cputype" = "386"; then + UNAME_MACHINE=i386 + else + UNAME_MACHINE="$cputype" + fi + echo ${UNAME_MACHINE}-unknown-plan9 + exit ;; + *:TOPS-10:*:*) + echo pdp10-unknown-tops10 + exit ;; + *:TENEX:*:*) + echo pdp10-unknown-tenex + exit ;; + KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*) + echo pdp10-dec-tops20 + exit ;; + XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*) + echo pdp10-xkl-tops20 + exit ;; + *:TOPS-20:*:*) + echo pdp10-unknown-tops20 + exit ;; + *:ITS:*:*) + echo pdp10-unknown-its + exit ;; + SEI:*:*:SEIUX) + echo mips-sei-seiux${UNAME_RELEASE} + exit ;; + *:DragonFly:*:*) + echo ${UNAME_MACHINE}-unknown-dragonfly`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` + exit ;; + *:*VMS:*:*) + UNAME_MACHINE=`(uname -p) 2>/dev/null` + case "${UNAME_MACHINE}" in + A*) echo alpha-dec-vms ; exit ;; + I*) echo ia64-dec-vms ; exit ;; + V*) echo vax-dec-vms ; exit ;; + esac ;; + *:XENIX:*:SysV) + echo i386-pc-xenix + exit ;; + i*86:skyos:*:*) + echo ${UNAME_MACHINE}-pc-skyos`echo ${UNAME_RELEASE}` | sed -e 's/ .*$//' + exit ;; + i*86:rdos:*:*) + echo ${UNAME_MACHINE}-pc-rdos + exit ;; + i*86:AROS:*:*) + echo ${UNAME_MACHINE}-pc-aros + exit ;; + x86_64:VMkernel:*:*) + echo ${UNAME_MACHINE}-unknown-esx + exit ;; +esac + +cat >&2 < in order to provide the needed +information to handle your system. + +config.guess timestamp = $timestamp + +uname -m = `(uname -m) 2>/dev/null || echo unknown` +uname -r = `(uname -r) 2>/dev/null || echo unknown` +uname -s = `(uname -s) 2>/dev/null || echo unknown` +uname -v = `(uname -v) 2>/dev/null || echo unknown` + +/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null` +/bin/uname -X = `(/bin/uname -X) 2>/dev/null` + +hostinfo = `(hostinfo) 2>/dev/null` +/bin/universe = `(/bin/universe) 2>/dev/null` +/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null` +/bin/arch = `(/bin/arch) 2>/dev/null` +/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null` +/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null` + +UNAME_MACHINE = ${UNAME_MACHINE} +UNAME_RELEASE = ${UNAME_RELEASE} +UNAME_SYSTEM = ${UNAME_SYSTEM} +UNAME_VERSION = ${UNAME_VERSION} +EOF + +exit 1 + +# Local variables: +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "timestamp='" +# time-stamp-format: "%:y-%02m-%02d" +# time-stamp-end: "'" +# End: diff --git a/config.h.in b/config.h.in new file mode 100644 index 0000000..f88e213 --- /dev/null +++ b/config.h.in @@ -0,0 +1,379 @@ +/* config.h.in. Generated from configure.ac by autoheader. */ + +/* Define if building universal (internal helper macro) */ +#undef AC_APPLE_UNIVERSAL_BUILD + +/* Define to 1 to enable debug output. */ +#undef DEBUGBUILD + +/* Define to 1 if you have the `accept4' function. */ +#undef HAVE_ACCEPT4 + +/* Define to 1 if you have the header file. */ +#undef HAVE_ARPA_INET_H + +/* define if the Boost library is available */ +#undef HAVE_BOOST + +/* define if the Boost::ASIO library is available */ +#undef HAVE_BOOST_ASIO + +/* define if the Boost::System library is available */ +#undef HAVE_BOOST_SYSTEM + +/* define if the Boost::Thread library is available */ +#undef HAVE_BOOST_THREAD + +/* Define to 1 if your system has a working `chown' function. */ +#undef HAVE_CHOWN + +/* Define to 1 if you have the `clock_gettime`. */ +#undef HAVE_CLOCK_GETTIME + +/* define if the compiler supports basic C++11 syntax */ +#undef HAVE_CXX11 + +/* Define to 1 if you have the declaration of `strerror_r', and to 0 if you + don't. */ +#undef HAVE_DECL_STRERROR_R + +/* Define to 1 if you have the header file. */ +#undef HAVE_DLFCN_H + +/* Define to 1 if you have the `dup2' function. */ +#undef HAVE_DUP2 + +/* Define to 1 if you have the header file. */ +#undef HAVE_FCNTL_H + +/* Define to 1 if you have the `fork' function. */ +#undef HAVE_FORK + +/* Define to 1 if you have the `getcwd' function. */ +#undef HAVE_GETCWD + +/* Define to 1 if you have the `getpwnam' function. */ +#undef HAVE_GETPWNAM + +/* Define to 1 if you have the header file. */ +#undef HAVE_INTTYPES_H + +/* Define to 1 if you have `libjansson` library. */ +#undef HAVE_JANSSON + +/* Define to 1 if you have `libxml2` library. */ +#undef HAVE_LIBXML2 + +/* Define to 1 if you have the header file. */ +#undef HAVE_LIMITS_H + +/* Define to 1 if you have the `localtime_r' function. */ +#undef HAVE_LOCALTIME_R + +/* Define to 1 if your system has a GNU libc compatible `malloc' function, and + to 0 otherwise. */ +#undef HAVE_MALLOC + +/* Define to 1 if you have the `memchr' function. */ +#undef HAVE_MEMCHR + +/* Define to 1 if you have the `memmove' function. */ +#undef HAVE_MEMMOVE + +/* Define to 1 if you have the header file. */ +#undef HAVE_MEMORY_H + +/* Define to 1 if you have the `memset' function. */ +#undef HAVE_MEMSET + +/* Define to 1 if you have the header file. */ +#undef HAVE_NETDB_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_NETINET_IN_H + +/* Define to 1 if the system has the type `ptrdiff_t'. */ +#undef HAVE_PTRDIFF_T + +/* Define to 1 if you have the header file. */ +#undef HAVE_PWD_H + +/* If available, contains the Python version number currently in use. */ +#undef HAVE_PYTHON + +/* Define to 1 if you have the `socket' function. */ +#undef HAVE_SOCKET + +/* Define to 1 if you have `spdylay` library. */ +#undef HAVE_SPDYLAY + +/* Define to 1 if you have the `sqrt' function. */ +#undef HAVE_SQRT + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDDEF_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDINT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDLIB_H + +/* Define to 1 if you have the `std::future`. */ +#undef HAVE_STD_FUTURE + +/* Define to 1 if you have the `std::map::emplace`. */ +#undef HAVE_STD_MAP_EMPLACE + +/* Define to 1 if you have the `std::chrono::steady_clock`. */ +#undef HAVE_STEADY_CLOCK + +/* Define to 1 if you have the `strchr' function. */ +#undef HAVE_STRCHR + +/* Define to 1 if you have the `strdup' function. */ +#undef HAVE_STRDUP + +/* Define to 1 if you have the `strerror' function. */ +#undef HAVE_STRERROR + +/* Define to 1 if you have the `strerror_r' function. */ +#undef HAVE_STRERROR_R + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRINGS_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRING_H + +/* Define to 1 if you have the `strndup' function. */ +#undef HAVE_STRNDUP + +/* Define to 1 if you have the `strstr' function. */ +#undef HAVE_STRSTR + +/* Define to 1 if you have the `strtol' function. */ +#undef HAVE_STRTOL + +/* Define to 1 if you have the `strtoul' function. */ +#undef HAVE_STRTOUL + +/* Define to 1 if you have `struct tm.tm_gmtoff` member. */ +#undef HAVE_STRUCT_TM_TM_GMTOFF + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYSLOG_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_SOCKET_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_STAT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_TIME_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_TYPES_H + +/* Define to 1 if you have the `timegm' function. */ +#undef HAVE_TIMEGM + +/* Define to 1 if you have the header file. */ +#undef HAVE_TIME_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_UNISTD_H + +/* Define to 1 if you have the `vfork' function. */ +#undef HAVE_VFORK + +/* Define to 1 if you have the header file. */ +#undef HAVE_VFORK_H + +/* Define to 1 if `fork' works. */ +#undef HAVE_WORKING_FORK + +/* Define to 1 if `vfork' works. */ +#undef HAVE_WORKING_VFORK + +/* Define to 1 if you have the `_Exit' function. */ +#undef HAVE__EXIT + +/* Define to the sub-directory in which libtool stores uninstalled libraries. + */ +#undef LT_OBJDIR + +/* Define to 1 if assertions should be disabled. */ +#undef NDEBUG + +/* Define to 1 if you want to disable threads. */ +#undef NOTHREADS + +/* Name of package */ +#undef PACKAGE + +/* Define to the address where bug reports for this package should be sent. */ +#undef PACKAGE_BUGREPORT + +/* Define to the full name of this package. */ +#undef PACKAGE_NAME + +/* Define to the full name and version of this package. */ +#undef PACKAGE_STRING + +/* Define to the one symbol short name of this package. */ +#undef PACKAGE_TARNAME + +/* Define to the home page for this package. */ +#undef PACKAGE_URL + +/* Define to the version of this package. */ +#undef PACKAGE_VERSION + +/* The size of `int *', as computed by sizeof. */ +#undef SIZEOF_INT_P + +/* Define to 1 if you have the ANSI C header files. */ +#undef STDC_HEADERS + +/* Define to 1 if strerror_r returns char *. */ +#undef STRERROR_R_CHAR_P + +/* Enable extensions on AIX 3, Interix. */ +#ifndef _ALL_SOURCE +# undef _ALL_SOURCE +#endif +/* Enable GNU extensions on systems that have them. */ +#ifndef _GNU_SOURCE +# undef _GNU_SOURCE +#endif +/* Enable threading extensions on Solaris. */ +#ifndef _POSIX_PTHREAD_SEMANTICS +# undef _POSIX_PTHREAD_SEMANTICS +#endif +/* Enable extensions on HP NonStop. */ +#ifndef _TANDEM_SOURCE +# undef _TANDEM_SOURCE +#endif +/* Enable general extensions on Solaris. */ +#ifndef __EXTENSIONS__ +# undef __EXTENSIONS__ +#endif + + +/* Version number of package */ +#undef VERSION + +/* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most + significant byte first (like Motorola and SPARC, unlike Intel). */ +#if defined AC_APPLE_UNIVERSAL_BUILD +# if defined __BIG_ENDIAN__ +# define WORDS_BIGENDIAN 1 +# endif +#else +# ifndef WORDS_BIGENDIAN +# undef WORDS_BIGENDIAN +# endif +#endif + +/* Enable large inode numbers on Mac OS X 10.5. */ +#ifndef _DARWIN_USE_64_BIT_INODE +# define _DARWIN_USE_64_BIT_INODE 1 +#endif + +/* Number of bits in a file offset, on hosts where this is settable. */ +#undef _FILE_OFFSET_BITS + +/* Define for large files, on AIX-style hosts. */ +#undef _LARGE_FILES + +/* Define to 1 if on MINIX. */ +#undef _MINIX + +/* Define to 2 if the system does not provide POSIX.1 features except with + this defined. */ +#undef _POSIX_1_SOURCE + +/* Define to 1 if you need to in order for `stat' and other things to work. */ +#undef _POSIX_SOURCE + +/* Define for Solaris 2.5.1 so the uint32_t typedef from , + , or is not used. If the typedef were allowed, the + #define below would cause a syntax error. */ +#undef _UINT32_T + +/* Define for Solaris 2.5.1 so the uint64_t typedef from , + , or is not used. If the typedef were allowed, the + #define below would cause a syntax error. */ +#undef _UINT64_T + +/* Define for Solaris 2.5.1 so the uint8_t typedef from , + , or is not used. If the typedef were allowed, the + #define below would cause a syntax error. */ +#undef _UINT8_T + +/* Hint to the compiler that a function parameters is not used */ +#undef _U_ + +/* Define to `int' if doesn't define. */ +#undef gid_t + +/* Define to `__inline__' or `__inline' if that's what the C compiler + calls it, or to nothing if 'inline' is not supported under any name. */ +#ifndef __cplusplus +#undef inline +#endif + +/* Define to the type of a signed integer type of width exactly 16 bits if + such a type exists and the standard includes do not define it. */ +#undef int16_t + +/* Define to the type of a signed integer type of width exactly 32 bits if + such a type exists and the standard includes do not define it. */ +#undef int32_t + +/* Define to the type of a signed integer type of width exactly 64 bits if + such a type exists and the standard includes do not define it. */ +#undef int64_t + +/* Define to the type of a signed integer type of width exactly 8 bits if such + a type exists and the standard includes do not define it. */ +#undef int8_t + +/* Define to rpl_malloc if the replacement function should be used. */ +#undef malloc + +/* Define to `long int' if does not define. */ +#undef off_t + +/* Define to `int' if does not define. */ +#undef pid_t + +/* Define to `unsigned int' if does not define. */ +#undef size_t + +/* Define to `int' if does not define. */ +#undef ssize_t + +/* Define to `int' if doesn't define. */ +#undef uid_t + +/* Define to the type of an unsigned integer type of width exactly 16 bits if + such a type exists and the standard includes do not define it. */ +#undef uint16_t + +/* Define to the type of an unsigned integer type of width exactly 32 bits if + such a type exists and the standard includes do not define it. */ +#undef uint32_t + +/* Define to the type of an unsigned integer type of width exactly 64 bits if + such a type exists and the standard includes do not define it. */ +#undef uint64_t + +/* Define to the type of an unsigned integer type of width exactly 8 bits if + such a type exists and the standard includes do not define it. */ +#undef uint8_t + +/* Define as `fork' if `vfork' does not work. */ +#undef vfork diff --git a/config.sub b/config.sub new file mode 100755 index 0000000..bba4efb --- /dev/null +++ b/config.sub @@ -0,0 +1,1799 @@ +#! /bin/sh +# Configuration validation subroutine script. +# Copyright 1992-2014 Free Software Foundation, Inc. + +timestamp='2014-09-11' + +# This file 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 . +# +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that +# program. This Exception is an additional permission under section 7 +# of the GNU General Public License, version 3 ("GPLv3"). + + +# Please send patches with a ChangeLog entry to config-patches@gnu.org. +# +# Configuration subroutine to validate and canonicalize a configuration type. +# Supply the specified configuration type as an argument. +# If it is invalid, we print an error message on stderr and exit with code 1. +# Otherwise, we print the canonical config type on stdout and succeed. + +# You can get the latest version of this script from: +# http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub;hb=HEAD + +# This file is supposed to be the same for all GNU packages +# and recognize all the CPU types, system types and aliases +# that are meaningful with *any* GNU software. +# Each package is responsible for reporting which valid configurations +# it does not support. The user should be able to distinguish +# a failure to support a valid configuration from a meaningless +# configuration. + +# The goal of this file is to map all the various variations of a given +# machine specification into a single specification in the form: +# CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM +# or in some cases, the newer four-part form: +# CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM +# It is wrong to echo any other type of specification. + +me=`echo "$0" | sed -e 's,.*/,,'` + +usage="\ +Usage: $0 [OPTION] CPU-MFR-OPSYS + $0 [OPTION] ALIAS + +Canonicalize a configuration name. + +Operation modes: + -h, --help print this help, then exit + -t, --time-stamp print date of last modification, then exit + -v, --version print version number, then exit + +Report bugs and patches to ." + +version="\ +GNU config.sub ($timestamp) + +Copyright 1992-2014 Free Software Foundation, Inc. + +This is free software; see the source for copying conditions. There is NO +warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." + +help=" +Try \`$me --help' for more information." + +# Parse command line +while test $# -gt 0 ; do + case $1 in + --time-stamp | --time* | -t ) + echo "$timestamp" ; exit ;; + --version | -v ) + echo "$version" ; exit ;; + --help | --h* | -h ) + echo "$usage"; exit ;; + -- ) # Stop option processing + shift; break ;; + - ) # Use stdin as input. + break ;; + -* ) + echo "$me: invalid option $1$help" + exit 1 ;; + + *local*) + # First pass through any local machine types. + echo $1 + exit ;; + + * ) + break ;; + esac +done + +case $# in + 0) echo "$me: missing argument$help" >&2 + exit 1;; + 1) ;; + *) echo "$me: too many arguments$help" >&2 + exit 1;; +esac + +# Separate what the user gave into CPU-COMPANY and OS or KERNEL-OS (if any). +# Here we must recognize all the valid KERNEL-OS combinations. +maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'` +case $maybe_os in + nto-qnx* | linux-gnu* | linux-android* | linux-dietlibc | linux-newlib* | \ + linux-musl* | linux-uclibc* | uclinux-uclibc* | uclinux-gnu* | kfreebsd*-gnu* | \ + knetbsd*-gnu* | netbsd*-gnu* | \ + kopensolaris*-gnu* | \ + storm-chaos* | os2-emx* | rtmk-nova*) + os=-$maybe_os + basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'` + ;; + android-linux) + os=-linux-android + basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'`-unknown + ;; + *) + basic_machine=`echo $1 | sed 's/-[^-]*$//'` + if [ $basic_machine != $1 ] + then os=`echo $1 | sed 's/.*-/-/'` + else os=; fi + ;; +esac + +### Let's recognize common machines as not being operating systems so +### that things like config.sub decstation-3100 work. We also +### recognize some manufacturers as not being operating systems, so we +### can provide default operating systems below. +case $os in + -sun*os*) + # Prevent following clause from handling this invalid input. + ;; + -dec* | -mips* | -sequent* | -encore* | -pc532* | -sgi* | -sony* | \ + -att* | -7300* | -3300* | -delta* | -motorola* | -sun[234]* | \ + -unicom* | -ibm* | -next | -hp | -isi* | -apollo | -altos* | \ + -convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\ + -c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \ + -harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp | \ + -apple | -axis | -knuth | -cray | -microblaze*) + os= + basic_machine=$1 + ;; + -bluegene*) + os=-cnk + ;; + -sim | -cisco | -oki | -wec | -winbond) + os= + basic_machine=$1 + ;; + -scout) + ;; + -wrs) + os=-vxworks + basic_machine=$1 + ;; + -chorusos*) + os=-chorusos + basic_machine=$1 + ;; + -chorusrdb) + os=-chorusrdb + basic_machine=$1 + ;; + -hiux*) + os=-hiuxwe2 + ;; + -sco6) + os=-sco5v6 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco5) + os=-sco3.2v5 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco4) + os=-sco3.2v4 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco3.2.[4-9]*) + os=`echo $os | sed -e 's/sco3.2./sco3.2v/'` + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco3.2v[4-9]*) + # Don't forget version if it is 3.2v4 or newer. + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco5v6*) + # Don't forget version if it is 3.2v4 or newer. + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco*) + os=-sco3.2v2 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -udk*) + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -isc) + os=-isc2.2 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -clix*) + basic_machine=clipper-intergraph + ;; + -isc*) + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -lynx*178) + os=-lynxos178 + ;; + -lynx*5) + os=-lynxos5 + ;; + -lynx*) + os=-lynxos + ;; + -ptx*) + basic_machine=`echo $1 | sed -e 's/86-.*/86-sequent/'` + ;; + -windowsnt*) + os=`echo $os | sed -e 's/windowsnt/winnt/'` + ;; + -psos*) + os=-psos + ;; + -mint | -mint[0-9]*) + basic_machine=m68k-atari + os=-mint + ;; +esac + +# Decode aliases for certain CPU-COMPANY combinations. +case $basic_machine in + # Recognize the basic CPU types without company name. + # Some are omitted here because they have special meanings below. + 1750a | 580 \ + | a29k \ + | aarch64 | aarch64_be \ + | alpha | alphaev[4-8] | alphaev56 | alphaev6[78] | alphapca5[67] \ + | alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] | alpha64pca5[67] \ + | am33_2.0 \ + | arc | arceb \ + | arm | arm[bl]e | arme[lb] | armv[2-8] | armv[3-8][lb] | armv7[arm] \ + | avr | avr32 \ + | be32 | be64 \ + | bfin \ + | c4x | c8051 | clipper \ + | d10v | d30v | dlx | dsp16xx \ + | epiphany \ + | fido | fr30 | frv \ + | h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \ + | hexagon \ + | i370 | i860 | i960 | ia64 \ + | ip2k | iq2000 \ + | k1om \ + | le32 | le64 \ + | lm32 \ + | m32c | m32r | m32rle | m68000 | m68k | m88k \ + | maxq | mb | microblaze | microblazeel | mcore | mep | metag \ + | mips | mipsbe | mipseb | mipsel | mipsle \ + | mips16 \ + | mips64 | mips64el \ + | mips64octeon | mips64octeonel \ + | mips64orion | mips64orionel \ + | mips64r5900 | mips64r5900el \ + | mips64vr | mips64vrel \ + | mips64vr4100 | mips64vr4100el \ + | mips64vr4300 | mips64vr4300el \ + | mips64vr5000 | mips64vr5000el \ + | mips64vr5900 | mips64vr5900el \ + | mipsisa32 | mipsisa32el \ + | mipsisa32r2 | mipsisa32r2el \ + | mipsisa32r6 | mipsisa32r6el \ + | mipsisa64 | mipsisa64el \ + | mipsisa64r2 | mipsisa64r2el \ + | mipsisa64r6 | mipsisa64r6el \ + | mipsisa64sb1 | mipsisa64sb1el \ + | mipsisa64sr71k | mipsisa64sr71kel \ + | mipsr5900 | mipsr5900el \ + | mipstx39 | mipstx39el \ + | mn10200 | mn10300 \ + | moxie \ + | mt \ + | msp430 \ + | nds32 | nds32le | nds32be \ + | nios | nios2 | nios2eb | nios2el \ + | ns16k | ns32k \ + | open8 | or1k | or1knd | or32 \ + | pdp10 | pdp11 | pj | pjl \ + | powerpc | powerpc64 | powerpc64le | powerpcle \ + | pyramid \ + | riscv32 | riscv64 \ + | rl78 | rx \ + | score \ + | sh | sh[1234] | sh[24]a | sh[24]aeb | sh[23]e | sh[34]eb | sheb | shbe | shle | sh[1234]le | sh3ele \ + | sh64 | sh64le \ + | sparc | sparc64 | sparc64b | sparc64v | sparc86x | sparclet | sparclite \ + | sparcv8 | sparcv9 | sparcv9b | sparcv9v \ + | spu \ + | tahoe | tic4x | tic54x | tic55x | tic6x | tic80 | tron \ + | ubicom32 \ + | v850 | v850e | v850e1 | v850e2 | v850es | v850e2v3 \ + | we32k \ + | x86 | xc16x | xstormy16 | xtensa \ + | z8k | z80) + basic_machine=$basic_machine-unknown + ;; + c54x) + basic_machine=tic54x-unknown + ;; + c55x) + basic_machine=tic55x-unknown + ;; + c6x) + basic_machine=tic6x-unknown + ;; + m6811 | m68hc11 | m6812 | m68hc12 | m68hcs12x | nvptx | picochip) + basic_machine=$basic_machine-unknown + os=-none + ;; + m88110 | m680[12346]0 | m683?2 | m68360 | m5200 | v70 | w65 | z8k) + ;; + ms1) + basic_machine=mt-unknown + ;; + + strongarm | thumb | xscale) + basic_machine=arm-unknown + ;; + xgate) + basic_machine=$basic_machine-unknown + os=-none + ;; + xscaleeb) + basic_machine=armeb-unknown + ;; + + xscaleel) + basic_machine=armel-unknown + ;; + + # We use `pc' rather than `unknown' + # because (1) that's what they normally are, and + # (2) the word "unknown" tends to confuse beginning users. + i*86 | x86_64) + basic_machine=$basic_machine-pc + ;; + # Object if more than one company name word. + *-*-*) + echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2 + exit 1 + ;; + # Recognize the basic CPU types with company name. + 580-* \ + | a29k-* \ + | aarch64-* | aarch64_be-* \ + | alpha-* | alphaev[4-8]-* | alphaev56-* | alphaev6[78]-* \ + | alpha64-* | alpha64ev[4-8]-* | alpha64ev56-* | alpha64ev6[78]-* \ + | alphapca5[67]-* | alpha64pca5[67]-* | arc-* | arceb-* \ + | arm-* | armbe-* | armle-* | armeb-* | armv*-* \ + | avr-* | avr32-* \ + | be32-* | be64-* \ + | bfin-* | bs2000-* \ + | c[123]* | c30-* | [cjt]90-* | c4x-* \ + | c8051-* | clipper-* | craynv-* | cydra-* \ + | d10v-* | d30v-* | dlx-* \ + | elxsi-* \ + | f30[01]-* | f700-* | fido-* | fr30-* | frv-* | fx80-* \ + | h8300-* | h8500-* \ + | hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \ + | hexagon-* \ + | i*86-* | i860-* | i960-* | ia64-* \ + | ip2k-* | iq2000-* \ + | k1om-* \ + | le32-* | le64-* \ + | lm32-* \ + | m32c-* | m32r-* | m32rle-* \ + | m68000-* | m680[012346]0-* | m68360-* | m683?2-* | m68k-* \ + | m88110-* | m88k-* | maxq-* | mcore-* | metag-* \ + | microblaze-* | microblazeel-* \ + | mips-* | mipsbe-* | mipseb-* | mipsel-* | mipsle-* \ + | mips16-* \ + | mips64-* | mips64el-* \ + | mips64octeon-* | mips64octeonel-* \ + | mips64orion-* | mips64orionel-* \ + | mips64r5900-* | mips64r5900el-* \ + | mips64vr-* | mips64vrel-* \ + | mips64vr4100-* | mips64vr4100el-* \ + | mips64vr4300-* | mips64vr4300el-* \ + | mips64vr5000-* | mips64vr5000el-* \ + | mips64vr5900-* | mips64vr5900el-* \ + | mipsisa32-* | mipsisa32el-* \ + | mipsisa32r2-* | mipsisa32r2el-* \ + | mipsisa32r6-* | mipsisa32r6el-* \ + | mipsisa64-* | mipsisa64el-* \ + | mipsisa64r2-* | mipsisa64r2el-* \ + | mipsisa64r6-* | mipsisa64r6el-* \ + | mipsisa64sb1-* | mipsisa64sb1el-* \ + | mipsisa64sr71k-* | mipsisa64sr71kel-* \ + | mipsr5900-* | mipsr5900el-* \ + | mipstx39-* | mipstx39el-* \ + | mmix-* \ + | mt-* \ + | msp430-* \ + | nds32-* | nds32le-* | nds32be-* \ + | nios-* | nios2-* | nios2eb-* | nios2el-* \ + | none-* | np1-* | ns16k-* | ns32k-* \ + | open8-* \ + | or1k*-* \ + | orion-* \ + | pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \ + | powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* \ + | pyramid-* \ + | rl78-* | romp-* | rs6000-* | rx-* \ + | sh-* | sh[1234]-* | sh[24]a-* | sh[24]aeb-* | sh[23]e-* | sh[34]eb-* | sheb-* | shbe-* \ + | shle-* | sh[1234]le-* | sh3ele-* | sh64-* | sh64le-* \ + | sparc-* | sparc64-* | sparc64b-* | sparc64v-* | sparc86x-* | sparclet-* \ + | sparclite-* \ + | sparcv8-* | sparcv9-* | sparcv9b-* | sparcv9v-* | sv1-* | sx?-* \ + | tahoe-* \ + | tic30-* | tic4x-* | tic54x-* | tic55x-* | tic6x-* | tic80-* \ + | tile*-* \ + | tron-* \ + | ubicom32-* \ + | v850-* | v850e-* | v850e1-* | v850es-* | v850e2-* | v850e2v3-* \ + | vax-* \ + | we32k-* \ + | x86-* | x86_64-* | xc16x-* | xps100-* \ + | xstormy16-* | xtensa*-* \ + | ymp-* \ + | z8k-* | z80-*) + ;; + # Recognize the basic CPU types without company name, with glob match. + xtensa*) + basic_machine=$basic_machine-unknown + ;; + # Recognize the various machine names and aliases which stand + # for a CPU type and a company and sometimes even an OS. + 386bsd) + basic_machine=i386-unknown + os=-bsd + ;; + 3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc) + basic_machine=m68000-att + ;; + 3b*) + basic_machine=we32k-att + ;; + a29khif) + basic_machine=a29k-amd + os=-udi + ;; + abacus) + basic_machine=abacus-unknown + ;; + adobe68k) + basic_machine=m68010-adobe + os=-scout + ;; + alliant | fx80) + basic_machine=fx80-alliant + ;; + altos | altos3068) + basic_machine=m68k-altos + ;; + am29k) + basic_machine=a29k-none + os=-bsd + ;; + amd64) + basic_machine=x86_64-pc + ;; + amd64-*) + basic_machine=x86_64-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + amdahl) + basic_machine=580-amdahl + os=-sysv + ;; + amiga | amiga-*) + basic_machine=m68k-unknown + ;; + amigaos | amigados) + basic_machine=m68k-unknown + os=-amigaos + ;; + amigaunix | amix) + basic_machine=m68k-unknown + os=-sysv4 + ;; + apollo68) + basic_machine=m68k-apollo + os=-sysv + ;; + apollo68bsd) + basic_machine=m68k-apollo + os=-bsd + ;; + aros) + basic_machine=i386-pc + os=-aros + ;; + aux) + basic_machine=m68k-apple + os=-aux + ;; + balance) + basic_machine=ns32k-sequent + os=-dynix + ;; + blackfin) + basic_machine=bfin-unknown + os=-linux + ;; + blackfin-*) + basic_machine=bfin-`echo $basic_machine | sed 's/^[^-]*-//'` + os=-linux + ;; + bluegene*) + basic_machine=powerpc-ibm + os=-cnk + ;; + c54x-*) + basic_machine=tic54x-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + c55x-*) + basic_machine=tic55x-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + c6x-*) + basic_machine=tic6x-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + c90) + basic_machine=c90-cray + os=-unicos + ;; + cegcc) + basic_machine=arm-unknown + os=-cegcc + ;; + convex-c1) + basic_machine=c1-convex + os=-bsd + ;; + convex-c2) + basic_machine=c2-convex + os=-bsd + ;; + convex-c32) + basic_machine=c32-convex + os=-bsd + ;; + convex-c34) + basic_machine=c34-convex + os=-bsd + ;; + convex-c38) + basic_machine=c38-convex + os=-bsd + ;; + cray | j90) + basic_machine=j90-cray + os=-unicos + ;; + craynv) + basic_machine=craynv-cray + os=-unicosmp + ;; + cr16 | cr16-*) + basic_machine=cr16-unknown + os=-elf + ;; + crds | unos) + basic_machine=m68k-crds + ;; + crisv32 | crisv32-* | etraxfs*) + basic_machine=crisv32-axis + ;; + cris | cris-* | etrax*) + basic_machine=cris-axis + ;; + crx) + basic_machine=crx-unknown + os=-elf + ;; + da30 | da30-*) + basic_machine=m68k-da30 + ;; + decstation | decstation-3100 | pmax | pmax-* | pmin | dec3100 | decstatn) + basic_machine=mips-dec + ;; + decsystem10* | dec10*) + basic_machine=pdp10-dec + os=-tops10 + ;; + decsystem20* | dec20*) + basic_machine=pdp10-dec + os=-tops20 + ;; + delta | 3300 | motorola-3300 | motorola-delta \ + | 3300-motorola | delta-motorola) + basic_machine=m68k-motorola + ;; + delta88) + basic_machine=m88k-motorola + os=-sysv3 + ;; + dicos) + basic_machine=i686-pc + os=-dicos + ;; + djgpp) + basic_machine=i586-pc + os=-msdosdjgpp + ;; + dpx20 | dpx20-*) + basic_machine=rs6000-bull + os=-bosx + ;; + dpx2* | dpx2*-bull) + basic_machine=m68k-bull + os=-sysv3 + ;; + ebmon29k) + basic_machine=a29k-amd + os=-ebmon + ;; + elxsi) + basic_machine=elxsi-elxsi + os=-bsd + ;; + encore | umax | mmax) + basic_machine=ns32k-encore + ;; + es1800 | OSE68k | ose68k | ose | OSE) + basic_machine=m68k-ericsson + os=-ose + ;; + fx2800) + basic_machine=i860-alliant + ;; + genix) + basic_machine=ns32k-ns + ;; + gmicro) + basic_machine=tron-gmicro + os=-sysv + ;; + go32) + basic_machine=i386-pc + os=-go32 + ;; + h3050r* | hiux*) + basic_machine=hppa1.1-hitachi + os=-hiuxwe2 + ;; + h8300hms) + basic_machine=h8300-hitachi + os=-hms + ;; + h8300xray) + basic_machine=h8300-hitachi + os=-xray + ;; + h8500hms) + basic_machine=h8500-hitachi + os=-hms + ;; + harris) + basic_machine=m88k-harris + os=-sysv3 + ;; + hp300-*) + basic_machine=m68k-hp + ;; + hp300bsd) + basic_machine=m68k-hp + os=-bsd + ;; + hp300hpux) + basic_machine=m68k-hp + os=-hpux + ;; + hp3k9[0-9][0-9] | hp9[0-9][0-9]) + basic_machine=hppa1.0-hp + ;; + hp9k2[0-9][0-9] | hp9k31[0-9]) + basic_machine=m68000-hp + ;; + hp9k3[2-9][0-9]) + basic_machine=m68k-hp + ;; + hp9k6[0-9][0-9] | hp6[0-9][0-9]) + basic_machine=hppa1.0-hp + ;; + hp9k7[0-79][0-9] | hp7[0-79][0-9]) + basic_machine=hppa1.1-hp + ;; + hp9k78[0-9] | hp78[0-9]) + # FIXME: really hppa2.0-hp + basic_machine=hppa1.1-hp + ;; + hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893) + # FIXME: really hppa2.0-hp + basic_machine=hppa1.1-hp + ;; + hp9k8[0-9][13679] | hp8[0-9][13679]) + basic_machine=hppa1.1-hp + ;; + hp9k8[0-9][0-9] | hp8[0-9][0-9]) + basic_machine=hppa1.0-hp + ;; + hppa-next) + os=-nextstep3 + ;; + hppaosf) + basic_machine=hppa1.1-hp + os=-osf + ;; + hppro) + basic_machine=hppa1.1-hp + os=-proelf + ;; + i370-ibm* | ibm*) + basic_machine=i370-ibm + ;; + i*86v32) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-sysv32 + ;; + i*86v4*) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-sysv4 + ;; + i*86v) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-sysv + ;; + i*86sol2) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-solaris2 + ;; + i386mach) + basic_machine=i386-mach + os=-mach + ;; + i386-vsta | vsta) + basic_machine=i386-unknown + os=-vsta + ;; + iris | iris4d) + basic_machine=mips-sgi + case $os in + -irix*) + ;; + *) + os=-irix4 + ;; + esac + ;; + isi68 | isi) + basic_machine=m68k-isi + os=-sysv + ;; + m68knommu) + basic_machine=m68k-unknown + os=-linux + ;; + m68knommu-*) + basic_machine=m68k-`echo $basic_machine | sed 's/^[^-]*-//'` + os=-linux + ;; + m88k-omron*) + basic_machine=m88k-omron + ;; + magnum | m3230) + basic_machine=mips-mips + os=-sysv + ;; + merlin) + basic_machine=ns32k-utek + os=-sysv + ;; + microblaze*) + basic_machine=microblaze-xilinx + ;; + mingw64) + basic_machine=x86_64-pc + os=-mingw64 + ;; + mingw32) + basic_machine=i686-pc + os=-mingw32 + ;; + mingw32ce) + basic_machine=arm-unknown + os=-mingw32ce + ;; + miniframe) + basic_machine=m68000-convergent + ;; + *mint | -mint[0-9]* | *MiNT | *MiNT[0-9]*) + basic_machine=m68k-atari + os=-mint + ;; + mips3*-*) + basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'` + ;; + mips3*) + basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`-unknown + ;; + monitor) + basic_machine=m68k-rom68k + os=-coff + ;; + morphos) + basic_machine=powerpc-unknown + os=-morphos + ;; + moxiebox) + basic_machine=moxie-unknown + os=-moxiebox + ;; + msdos) + basic_machine=i386-pc + os=-msdos + ;; + ms1-*) + basic_machine=`echo $basic_machine | sed -e 's/ms1-/mt-/'` + ;; + msys) + basic_machine=i686-pc + os=-msys + ;; + mvs) + basic_machine=i370-ibm + os=-mvs + ;; + nacl) + basic_machine=le32-unknown + os=-nacl + ;; + ncr3000) + basic_machine=i486-ncr + os=-sysv4 + ;; + netbsd386) + basic_machine=i386-unknown + os=-netbsd + ;; + netwinder) + basic_machine=armv4l-rebel + os=-linux + ;; + news | news700 | news800 | news900) + basic_machine=m68k-sony + os=-newsos + ;; + news1000) + basic_machine=m68030-sony + os=-newsos + ;; + news-3600 | risc-news) + basic_machine=mips-sony + os=-newsos + ;; + necv70) + basic_machine=v70-nec + os=-sysv + ;; + next | m*-next ) + basic_machine=m68k-next + case $os in + -nextstep* ) + ;; + -ns2*) + os=-nextstep2 + ;; + *) + os=-nextstep3 + ;; + esac + ;; + nh3000) + basic_machine=m68k-harris + os=-cxux + ;; + nh[45]000) + basic_machine=m88k-harris + os=-cxux + ;; + nindy960) + basic_machine=i960-intel + os=-nindy + ;; + mon960) + basic_machine=i960-intel + os=-mon960 + ;; + nonstopux) + basic_machine=mips-compaq + os=-nonstopux + ;; + np1) + basic_machine=np1-gould + ;; + neo-tandem) + basic_machine=neo-tandem + ;; + nse-tandem) + basic_machine=nse-tandem + ;; + nsr-tandem) + basic_machine=nsr-tandem + ;; + op50n-* | op60c-*) + basic_machine=hppa1.1-oki + os=-proelf + ;; + openrisc | openrisc-*) + basic_machine=or32-unknown + ;; + os400) + basic_machine=powerpc-ibm + os=-os400 + ;; + OSE68000 | ose68000) + basic_machine=m68000-ericsson + os=-ose + ;; + os68k) + basic_machine=m68k-none + os=-os68k + ;; + pa-hitachi) + basic_machine=hppa1.1-hitachi + os=-hiuxwe2 + ;; + paragon) + basic_machine=i860-intel + os=-osf + ;; + parisc) + basic_machine=hppa-unknown + os=-linux + ;; + parisc-*) + basic_machine=hppa-`echo $basic_machine | sed 's/^[^-]*-//'` + os=-linux + ;; + pbd) + basic_machine=sparc-tti + ;; + pbb) + basic_machine=m68k-tti + ;; + pc532 | pc532-*) + basic_machine=ns32k-pc532 + ;; + pc98) + basic_machine=i386-pc + ;; + pc98-*) + basic_machine=i386-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pentium | p5 | k5 | k6 | nexgen | viac3) + basic_machine=i586-pc + ;; + pentiumpro | p6 | 6x86 | athlon | athlon_*) + basic_machine=i686-pc + ;; + pentiumii | pentium2 | pentiumiii | pentium3) + basic_machine=i686-pc + ;; + pentium4) + basic_machine=i786-pc + ;; + pentium-* | p5-* | k5-* | k6-* | nexgen-* | viac3-*) + basic_machine=i586-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pentiumpro-* | p6-* | 6x86-* | athlon-*) + basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pentiumii-* | pentium2-* | pentiumiii-* | pentium3-*) + basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pentium4-*) + basic_machine=i786-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pn) + basic_machine=pn-gould + ;; + power) basic_machine=power-ibm + ;; + ppc | ppcbe) basic_machine=powerpc-unknown + ;; + ppc-* | ppcbe-*) + basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + ppcle | powerpclittle | ppc-le | powerpc-little) + basic_machine=powerpcle-unknown + ;; + ppcle-* | powerpclittle-*) + basic_machine=powerpcle-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + ppc64) basic_machine=powerpc64-unknown + ;; + ppc64-*) basic_machine=powerpc64-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + ppc64le | powerpc64little | ppc64-le | powerpc64-little) + basic_machine=powerpc64le-unknown + ;; + ppc64le-* | powerpc64little-*) + basic_machine=powerpc64le-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + ps2) + basic_machine=i386-ibm + ;; + pw32) + basic_machine=i586-unknown + os=-pw32 + ;; + rdos | rdos64) + basic_machine=x86_64-pc + os=-rdos + ;; + rdos32) + basic_machine=i386-pc + os=-rdos + ;; + rom68k) + basic_machine=m68k-rom68k + os=-coff + ;; + rm[46]00) + basic_machine=mips-siemens + ;; + rtpc | rtpc-*) + basic_machine=romp-ibm + ;; + s390 | s390-*) + basic_machine=s390-ibm + ;; + s390x | s390x-*) + basic_machine=s390x-ibm + ;; + sa29200) + basic_machine=a29k-amd + os=-udi + ;; + sb1) + basic_machine=mipsisa64sb1-unknown + ;; + sb1el) + basic_machine=mipsisa64sb1el-unknown + ;; + sde) + basic_machine=mipsisa32-sde + os=-elf + ;; + sei) + basic_machine=mips-sei + os=-seiux + ;; + sequent) + basic_machine=i386-sequent + ;; + sh) + basic_machine=sh-hitachi + os=-hms + ;; + sh5el) + basic_machine=sh5le-unknown + ;; + sh64) + basic_machine=sh64-unknown + ;; + sparclite-wrs | simso-wrs) + basic_machine=sparclite-wrs + os=-vxworks + ;; + sps7) + basic_machine=m68k-bull + os=-sysv2 + ;; + spur) + basic_machine=spur-unknown + ;; + st2000) + basic_machine=m68k-tandem + ;; + stratus) + basic_machine=i860-stratus + os=-sysv4 + ;; + strongarm-* | thumb-*) + basic_machine=arm-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + sun2) + basic_machine=m68000-sun + ;; + sun2os3) + basic_machine=m68000-sun + os=-sunos3 + ;; + sun2os4) + basic_machine=m68000-sun + os=-sunos4 + ;; + sun3os3) + basic_machine=m68k-sun + os=-sunos3 + ;; + sun3os4) + basic_machine=m68k-sun + os=-sunos4 + ;; + sun4os3) + basic_machine=sparc-sun + os=-sunos3 + ;; + sun4os4) + basic_machine=sparc-sun + os=-sunos4 + ;; + sun4sol2) + basic_machine=sparc-sun + os=-solaris2 + ;; + sun3 | sun3-*) + basic_machine=m68k-sun + ;; + sun4) + basic_machine=sparc-sun + ;; + sun386 | sun386i | roadrunner) + basic_machine=i386-sun + ;; + sv1) + basic_machine=sv1-cray + os=-unicos + ;; + symmetry) + basic_machine=i386-sequent + os=-dynix + ;; + t3e) + basic_machine=alphaev5-cray + os=-unicos + ;; + t90) + basic_machine=t90-cray + os=-unicos + ;; + tile*) + basic_machine=$basic_machine-unknown + os=-linux-gnu + ;; + tx39) + basic_machine=mipstx39-unknown + ;; + tx39el) + basic_machine=mipstx39el-unknown + ;; + toad1) + basic_machine=pdp10-xkl + os=-tops20 + ;; + tower | tower-32) + basic_machine=m68k-ncr + ;; + tpf) + basic_machine=s390x-ibm + os=-tpf + ;; + udi29k) + basic_machine=a29k-amd + os=-udi + ;; + ultra3) + basic_machine=a29k-nyu + os=-sym1 + ;; + v810 | necv810) + basic_machine=v810-nec + os=-none + ;; + vaxv) + basic_machine=vax-dec + os=-sysv + ;; + vms) + basic_machine=vax-dec + os=-vms + ;; + vpp*|vx|vx-*) + basic_machine=f301-fujitsu + ;; + vxworks960) + basic_machine=i960-wrs + os=-vxworks + ;; + vxworks68) + basic_machine=m68k-wrs + os=-vxworks + ;; + vxworks29k) + basic_machine=a29k-wrs + os=-vxworks + ;; + w65*) + basic_machine=w65-wdc + os=-none + ;; + w89k-*) + basic_machine=hppa1.1-winbond + os=-proelf + ;; + xbox) + basic_machine=i686-pc + os=-mingw32 + ;; + xps | xps100) + basic_machine=xps100-honeywell + ;; + xscale-* | xscalee[bl]-*) + basic_machine=`echo $basic_machine | sed 's/^xscale/arm/'` + ;; + ymp) + basic_machine=ymp-cray + os=-unicos + ;; + z8k-*-coff) + basic_machine=z8k-unknown + os=-sim + ;; + z80-*-coff) + basic_machine=z80-unknown + os=-sim + ;; + none) + basic_machine=none-none + os=-none + ;; + +# Here we handle the default manufacturer of certain CPU types. It is in +# some cases the only manufacturer, in others, it is the most popular. + w89k) + basic_machine=hppa1.1-winbond + ;; + op50n) + basic_machine=hppa1.1-oki + ;; + op60c) + basic_machine=hppa1.1-oki + ;; + romp) + basic_machine=romp-ibm + ;; + mmix) + basic_machine=mmix-knuth + ;; + rs6000) + basic_machine=rs6000-ibm + ;; + vax) + basic_machine=vax-dec + ;; + pdp10) + # there are many clones, so DEC is not a safe bet + basic_machine=pdp10-unknown + ;; + pdp11) + basic_machine=pdp11-dec + ;; + we32k) + basic_machine=we32k-att + ;; + sh[1234] | sh[24]a | sh[24]aeb | sh[34]eb | sh[1234]le | sh[23]ele) + basic_machine=sh-unknown + ;; + sparc | sparcv8 | sparcv9 | sparcv9b | sparcv9v) + basic_machine=sparc-sun + ;; + cydra) + basic_machine=cydra-cydrome + ;; + orion) + basic_machine=orion-highlevel + ;; + orion105) + basic_machine=clipper-highlevel + ;; + mac | mpw | mac-mpw) + basic_machine=m68k-apple + ;; + pmac | pmac-mpw) + basic_machine=powerpc-apple + ;; + *-unknown) + # Make sure to match an already-canonicalized machine name. + ;; + *) + echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2 + exit 1 + ;; +esac + +# Here we canonicalize certain aliases for manufacturers. +case $basic_machine in + *-digital*) + basic_machine=`echo $basic_machine | sed 's/digital.*/dec/'` + ;; + *-commodore*) + basic_machine=`echo $basic_machine | sed 's/commodore.*/cbm/'` + ;; + *) + ;; +esac + +# Decode manufacturer-specific aliases for certain operating systems. + +if [ x"$os" != x"" ] +then +case $os in + # First match some system type aliases + # that might get confused with valid system types. + # -solaris* is a basic system type, with this one exception. + -auroraux) + os=-auroraux + ;; + -solaris1 | -solaris1.*) + os=`echo $os | sed -e 's|solaris1|sunos4|'` + ;; + -solaris) + os=-solaris2 + ;; + -svr4*) + os=-sysv4 + ;; + -unixware*) + os=-sysv4.2uw + ;; + -gnu/linux*) + os=`echo $os | sed -e 's|gnu/linux|linux-gnu|'` + ;; + # First accept the basic system types. + # The portable systems comes first. + # Each alternative MUST END IN A *, to match a version number. + # -sysv* is not here because it comes later, after sysvr4. + -gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \ + | -*vms* | -sco* | -esix* | -isc* | -aix* | -cnk* | -sunos | -sunos[34]*\ + | -hpux* | -unos* | -osf* | -luna* | -dgux* | -auroraux* | -solaris* \ + | -sym* | -kopensolaris* | -plan9* \ + | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \ + | -aos* | -aros* \ + | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \ + | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \ + | -hiux* | -386bsd* | -knetbsd* | -mirbsd* | -netbsd* \ + | -bitrig* | -openbsd* | -solidbsd* \ + | -ekkobsd* | -kfreebsd* | -freebsd* | -riscix* | -lynxos* \ + | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \ + | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \ + | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \ + | -chorusos* | -chorusrdb* | -cegcc* \ + | -cygwin* | -msys* | -pe* | -psos* | -moss* | -proelf* | -rtems* \ + | -mingw32* | -mingw64* | -linux-gnu* | -linux-android* \ + | -linux-newlib* | -linux-musl* | -linux-uclibc* \ + | -uxpv* | -beos* | -mpeix* | -udk* | -moxiebox* \ + | -interix* | -uwin* | -mks* | -rhapsody* | -darwin* | -opened* \ + | -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \ + | -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* \ + | -os2* | -vos* | -palmos* | -uclinux* | -nucleus* \ + | -morphos* | -superux* | -rtmk* | -rtmk-nova* | -windiss* \ + | -powermax* | -dnix* | -nx6 | -nx7 | -sei* | -dragonfly* \ + | -skyos* | -haiku* | -rdos* | -toppers* | -drops* | -es* | -tirtos*) + # Remember, each alternative MUST END IN *, to match a version number. + ;; + -qnx*) + case $basic_machine in + x86-* | i*86-*) + ;; + *) + os=-nto$os + ;; + esac + ;; + -nto-qnx*) + ;; + -nto*) + os=`echo $os | sed -e 's|nto|nto-qnx|'` + ;; + -sim | -es1800* | -hms* | -xray | -os68k* | -none* | -v88r* \ + | -windows* | -osx | -abug | -netware* | -os9* | -beos* | -haiku* \ + | -macos* | -mpw* | -magic* | -mmixware* | -mon960* | -lnews*) + ;; + -mac*) + os=`echo $os | sed -e 's|mac|macos|'` + ;; + -linux-dietlibc) + os=-linux-dietlibc + ;; + -linux*) + os=`echo $os | sed -e 's|linux|linux-gnu|'` + ;; + -sunos5*) + os=`echo $os | sed -e 's|sunos5|solaris2|'` + ;; + -sunos6*) + os=`echo $os | sed -e 's|sunos6|solaris3|'` + ;; + -opened*) + os=-openedition + ;; + -os400*) + os=-os400 + ;; + -wince*) + os=-wince + ;; + -osfrose*) + os=-osfrose + ;; + -osf*) + os=-osf + ;; + -utek*) + os=-bsd + ;; + -dynix*) + os=-bsd + ;; + -acis*) + os=-aos + ;; + -atheos*) + os=-atheos + ;; + -syllable*) + os=-syllable + ;; + -386bsd) + os=-bsd + ;; + -ctix* | -uts*) + os=-sysv + ;; + -nova*) + os=-rtmk-nova + ;; + -ns2 ) + os=-nextstep2 + ;; + -nsk*) + os=-nsk + ;; + # Preserve the version number of sinix5. + -sinix5.*) + os=`echo $os | sed -e 's|sinix|sysv|'` + ;; + -sinix*) + os=-sysv4 + ;; + -tpf*) + os=-tpf + ;; + -triton*) + os=-sysv3 + ;; + -oss*) + os=-sysv3 + ;; + -svr4) + os=-sysv4 + ;; + -svr3) + os=-sysv3 + ;; + -sysvr4) + os=-sysv4 + ;; + # This must come after -sysvr4. + -sysv*) + ;; + -ose*) + os=-ose + ;; + -es1800*) + os=-ose + ;; + -xenix) + os=-xenix + ;; + -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*) + os=-mint + ;; + -aros*) + os=-aros + ;; + -zvmoe) + os=-zvmoe + ;; + -dicos*) + os=-dicos + ;; + -nacl*) + ;; + -none) + ;; + *) + # Get rid of the `-' at the beginning of $os. + os=`echo $os | sed 's/[^-]*-//'` + echo Invalid configuration \`$1\': system \`$os\' not recognized 1>&2 + exit 1 + ;; +esac +else + +# Here we handle the default operating systems that come with various machines. +# The value should be what the vendor currently ships out the door with their +# machine or put another way, the most popular os provided with the machine. + +# Note that if you're going to try to match "-MANUFACTURER" here (say, +# "-sun"), then you have to tell the case statement up towards the top +# that MANUFACTURER isn't an operating system. Otherwise, code above +# will signal an error saying that MANUFACTURER isn't an operating +# system, and we'll never get to this point. + +case $basic_machine in + score-*) + os=-elf + ;; + spu-*) + os=-elf + ;; + *-acorn) + os=-riscix1.2 + ;; + arm*-rebel) + os=-linux + ;; + arm*-semi) + os=-aout + ;; + c4x-* | tic4x-*) + os=-coff + ;; + c8051-*) + os=-elf + ;; + hexagon-*) + os=-elf + ;; + tic54x-*) + os=-coff + ;; + tic55x-*) + os=-coff + ;; + tic6x-*) + os=-coff + ;; + # This must come before the *-dec entry. + pdp10-*) + os=-tops20 + ;; + pdp11-*) + os=-none + ;; + *-dec | vax-*) + os=-ultrix4.2 + ;; + m68*-apollo) + os=-domain + ;; + i386-sun) + os=-sunos4.0.2 + ;; + m68000-sun) + os=-sunos3 + ;; + m68*-cisco) + os=-aout + ;; + mep-*) + os=-elf + ;; + mips*-cisco) + os=-elf + ;; + mips*-*) + os=-elf + ;; + or32-*) + os=-coff + ;; + *-tti) # must be before sparc entry or we get the wrong os. + os=-sysv3 + ;; + sparc-* | *-sun) + os=-sunos4.1.1 + ;; + *-be) + os=-beos + ;; + *-haiku) + os=-haiku + ;; + *-ibm) + os=-aix + ;; + *-knuth) + os=-mmixware + ;; + *-wec) + os=-proelf + ;; + *-winbond) + os=-proelf + ;; + *-oki) + os=-proelf + ;; + *-hp) + os=-hpux + ;; + *-hitachi) + os=-hiux + ;; + i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent) + os=-sysv + ;; + *-cbm) + os=-amigaos + ;; + *-dg) + os=-dgux + ;; + *-dolphin) + os=-sysv3 + ;; + m68k-ccur) + os=-rtu + ;; + m88k-omron*) + os=-luna + ;; + *-next ) + os=-nextstep + ;; + *-sequent) + os=-ptx + ;; + *-crds) + os=-unos + ;; + *-ns) + os=-genix + ;; + i370-*) + os=-mvs + ;; + *-next) + os=-nextstep3 + ;; + *-gould) + os=-sysv + ;; + *-highlevel) + os=-bsd + ;; + *-encore) + os=-bsd + ;; + *-sgi) + os=-irix + ;; + *-siemens) + os=-sysv4 + ;; + *-masscomp) + os=-rtu + ;; + f30[01]-fujitsu | f700-fujitsu) + os=-uxpv + ;; + *-rom68k) + os=-coff + ;; + *-*bug) + os=-coff + ;; + *-apple) + os=-macos + ;; + *-atari*) + os=-mint + ;; + *) + os=-none + ;; +esac +fi + +# Here we handle the case where we know the os, and the CPU type, but not the +# manufacturer. We pick the logical manufacturer. +vendor=unknown +case $basic_machine in + *-unknown) + case $os in + -riscix*) + vendor=acorn + ;; + -sunos*) + vendor=sun + ;; + -cnk*|-aix*) + vendor=ibm + ;; + -beos*) + vendor=be + ;; + -hpux*) + vendor=hp + ;; + -mpeix*) + vendor=hp + ;; + -hiux*) + vendor=hitachi + ;; + -unos*) + vendor=crds + ;; + -dgux*) + vendor=dg + ;; + -luna*) + vendor=omron + ;; + -genix*) + vendor=ns + ;; + -mvs* | -opened*) + vendor=ibm + ;; + -os400*) + vendor=ibm + ;; + -ptx*) + vendor=sequent + ;; + -tpf*) + vendor=ibm + ;; + -vxsim* | -vxworks* | -windiss*) + vendor=wrs + ;; + -aux*) + vendor=apple + ;; + -hms*) + vendor=hitachi + ;; + -mpw* | -macos*) + vendor=apple + ;; + -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*) + vendor=atari + ;; + -vos*) + vendor=stratus + ;; + esac + basic_machine=`echo $basic_machine | sed "s/unknown/$vendor/"` + ;; +esac + +echo $basic_machine$os +exit + +# Local variables: +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "timestamp='" +# time-stamp-format: "%:y-%02m-%02d" +# time-stamp-end: "'" +# End: diff --git a/configure b/configure new file mode 100755 index 0000000..2d46036 --- /dev/null +++ b/configure @@ -0,0 +1,25483 @@ +#! /bin/sh +# Guess values for system-dependent variables and create Makefiles. +# Generated by GNU Autoconf 2.69 for nghttp2 1.0.0. +# +# Report bugs to . +# +# +# Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc. +# +# +# This configure script is free software; the Free Software Foundation +# gives unlimited permission to copy, distribute and modify it. +## -------------------- ## +## M4sh Initialization. ## +## -------------------- ## + +# Be more Bourne compatible +DUALCASE=1; export DUALCASE # for MKS sh +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : + emulate sh + NULLCMD=: + # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' + setopt NO_GLOB_SUBST +else + case `(set -o) 2>/dev/null` in #( + *posix*) : + set -o posix ;; #( + *) : + ;; +esac +fi + + +as_nl=' +' +export as_nl +# Printing a long string crashes Solaris 7 /usr/bin/printf. +as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' +as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo +as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo +# Prefer a ksh shell builtin over an external printf program on Solaris, +# but without wasting forks for bash or zsh. +if test -z "$BASH_VERSION$ZSH_VERSION" \ + && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then + as_echo='print -r --' + as_echo_n='print -rn --' +elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then + as_echo='printf %s\n' + as_echo_n='printf %s' +else + if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then + as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' + as_echo_n='/usr/ucb/echo -n' + else + as_echo_body='eval expr "X$1" : "X\\(.*\\)"' + as_echo_n_body='eval + arg=$1; + case $arg in #( + *"$as_nl"*) + expr "X$arg" : "X\\(.*\\)$as_nl"; + arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; + esac; + expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" + ' + export as_echo_n_body + as_echo_n='sh -c $as_echo_n_body as_echo' + fi + export as_echo_body + as_echo='sh -c $as_echo_body as_echo' +fi + +# The user is always right. +if test "${PATH_SEPARATOR+set}" != set; then + PATH_SEPARATOR=: + (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { + (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || + PATH_SEPARATOR=';' + } +fi + + +# IFS +# We need space, tab and new line, in precisely that order. Quoting is +# there to prevent editors from complaining about space-tab. +# (If _AS_PATH_WALK were called with IFS unset, it would disable word +# splitting by setting IFS to empty value.) +IFS=" "" $as_nl" + +# Find who we are. Look in the path if we contain no directory separator. +as_myself= +case $0 in #(( + *[\\/]* ) as_myself=$0 ;; + *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break + done +IFS=$as_save_IFS + + ;; +esac +# We did not find ourselves, most probably we were run as `sh COMMAND' +# in which case we are not to be found in the path. +if test "x$as_myself" = x; then + as_myself=$0 +fi +if test ! -f "$as_myself"; then + $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 + exit 1 +fi + +# Unset variables that we do not need and which cause bugs (e.g. in +# pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1" +# suppresses any "Segmentation fault" message there. '((' could +# trigger a bug in pdksh 5.2.14. +for as_var in BASH_ENV ENV MAIL MAILPATH +do eval test x\${$as_var+set} = xset \ + && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : +done +PS1='$ ' +PS2='> ' +PS4='+ ' + +# NLS nuisances. +LC_ALL=C +export LC_ALL +LANGUAGE=C +export LANGUAGE + +# CDPATH. +(unset CDPATH) >/dev/null 2>&1 && unset CDPATH + +# Use a proper internal environment variable to ensure we don't fall + # into an infinite loop, continuously re-executing ourselves. + if test x"${_as_can_reexec}" != xno && test "x$CONFIG_SHELL" != x; then + _as_can_reexec=no; export _as_can_reexec; + # We cannot yet assume a decent shell, so we have to provide a +# neutralization value for shells without unset; and this also +# works around shells that cannot unset nonexistent variables. +# Preserve -v and -x to the replacement shell. +BASH_ENV=/dev/null +ENV=/dev/null +(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV +case $- in # (((( + *v*x* | *x*v* ) as_opts=-vx ;; + *v* ) as_opts=-v ;; + *x* ) as_opts=-x ;; + * ) as_opts= ;; +esac +exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} +# Admittedly, this is quite paranoid, since all the known shells bail +# out after a failed `exec'. +$as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2 +as_fn_exit 255 + fi + # We don't want this to propagate to other subprocesses. + { _as_can_reexec=; unset _as_can_reexec;} +if test "x$CONFIG_SHELL" = x; then + as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then : + emulate sh + NULLCMD=: + # Pre-4.2 versions of Zsh do word splitting on \${1+\"\$@\"}, which + # is contrary to our usage. Disable this feature. + alias -g '\${1+\"\$@\"}'='\"\$@\"' + setopt NO_GLOB_SUBST +else + case \`(set -o) 2>/dev/null\` in #( + *posix*) : + set -o posix ;; #( + *) : + ;; +esac +fi +" + as_required="as_fn_return () { (exit \$1); } +as_fn_success () { as_fn_return 0; } +as_fn_failure () { as_fn_return 1; } +as_fn_ret_success () { return 0; } +as_fn_ret_failure () { return 1; } + +exitcode=0 +as_fn_success || { exitcode=1; echo as_fn_success failed.; } +as_fn_failure && { exitcode=1; echo as_fn_failure succeeded.; } +as_fn_ret_success || { exitcode=1; echo as_fn_ret_success failed.; } +as_fn_ret_failure && { exitcode=1; echo as_fn_ret_failure succeeded.; } +if ( set x; as_fn_ret_success y && test x = \"\$1\" ); then : + +else + exitcode=1; echo positional parameters were not saved. +fi +test x\$exitcode = x0 || exit 1 +test -x / || exit 1" + as_suggested=" as_lineno_1=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_1a=\$LINENO + as_lineno_2=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_2a=\$LINENO + eval 'test \"x\$as_lineno_1'\$as_run'\" != \"x\$as_lineno_2'\$as_run'\" && + test \"x\`expr \$as_lineno_1'\$as_run' + 1\`\" = \"x\$as_lineno_2'\$as_run'\"' || exit 1 +test \$(( 1 + 1 )) = 2 || exit 1 + + test -n \"\${ZSH_VERSION+set}\${BASH_VERSION+set}\" || ( + ECHO='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' + ECHO=\$ECHO\$ECHO\$ECHO\$ECHO\$ECHO + ECHO=\$ECHO\$ECHO\$ECHO\$ECHO\$ECHO\$ECHO + PATH=/empty FPATH=/empty; export PATH FPATH + test \"X\`printf %s \$ECHO\`\" = \"X\$ECHO\" \\ + || test \"X\`print -r -- \$ECHO\`\" = \"X\$ECHO\" ) || exit 1" + if (eval "$as_required") 2>/dev/null; then : + as_have_required=yes +else + as_have_required=no +fi + if test x$as_have_required = xyes && (eval "$as_suggested") 2>/dev/null; then : + +else + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +as_found=false +for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + as_found=: + case $as_dir in #( + /*) + for as_base in sh bash ksh sh5; do + # Try only shells that exist, to save several forks. + as_shell=$as_dir/$as_base + if { test -f "$as_shell" || test -f "$as_shell.exe"; } && + { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$as_shell"; } 2>/dev/null; then : + CONFIG_SHELL=$as_shell as_have_required=yes + if { $as_echo "$as_bourne_compatible""$as_suggested" | as_run=a "$as_shell"; } 2>/dev/null; then : + break 2 +fi +fi + done;; + esac + as_found=false +done +$as_found || { if { test -f "$SHELL" || test -f "$SHELL.exe"; } && + { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$SHELL"; } 2>/dev/null; then : + CONFIG_SHELL=$SHELL as_have_required=yes +fi; } +IFS=$as_save_IFS + + + if test "x$CONFIG_SHELL" != x; then : + export CONFIG_SHELL + # We cannot yet assume a decent shell, so we have to provide a +# neutralization value for shells without unset; and this also +# works around shells that cannot unset nonexistent variables. +# Preserve -v and -x to the replacement shell. +BASH_ENV=/dev/null +ENV=/dev/null +(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV +case $- in # (((( + *v*x* | *x*v* ) as_opts=-vx ;; + *v* ) as_opts=-v ;; + *x* ) as_opts=-x ;; + * ) as_opts= ;; +esac +exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} +# Admittedly, this is quite paranoid, since all the known shells bail +# out after a failed `exec'. +$as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2 +exit 255 +fi + + if test x$as_have_required = xno; then : + $as_echo "$0: This script requires a shell more modern than all" + $as_echo "$0: the shells that I found on your system." + if test x${ZSH_VERSION+set} = xset ; then + $as_echo "$0: In particular, zsh $ZSH_VERSION has bugs and should" + $as_echo "$0: be upgraded to zsh 4.3.4 or later." + else + $as_echo "$0: Please tell bug-autoconf@gnu.org and +$0: t-tujikawa@users.sourceforge.net about your system, +$0: including any error possibly output before this +$0: message. Then install a modern shell, or manually run +$0: the script under such a shell if you do have one." + fi + exit 1 +fi +fi +fi +SHELL=${CONFIG_SHELL-/bin/sh} +export SHELL +# Unset more variables known to interfere with behavior of common tools. +CLICOLOR_FORCE= GREP_OPTIONS= +unset CLICOLOR_FORCE GREP_OPTIONS + +## --------------------- ## +## M4sh Shell Functions. ## +## --------------------- ## +# as_fn_unset VAR +# --------------- +# Portably unset VAR. +as_fn_unset () +{ + { eval $1=; unset $1;} +} +as_unset=as_fn_unset + +# as_fn_set_status STATUS +# ----------------------- +# Set $? to STATUS, without forking. +as_fn_set_status () +{ + return $1 +} # as_fn_set_status + +# as_fn_exit STATUS +# ----------------- +# Exit the shell with STATUS, even in a "trap 0" or "set -e" context. +as_fn_exit () +{ + set +e + as_fn_set_status $1 + exit $1 +} # as_fn_exit + +# as_fn_mkdir_p +# ------------- +# Create "$as_dir" as a directory, including parents if necessary. +as_fn_mkdir_p () +{ + + case $as_dir in #( + -*) as_dir=./$as_dir;; + esac + test -d "$as_dir" || eval $as_mkdir_p || { + as_dirs= + while :; do + case $as_dir in #( + *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( + *) as_qdir=$as_dir;; + esac + as_dirs="'$as_qdir' $as_dirs" + as_dir=`$as_dirname -- "$as_dir" || +$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_dir" : 'X\(//\)[^/]' \| \ + X"$as_dir" : 'X\(//\)$' \| \ + X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$as_dir" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + test -d "$as_dir" && break + done + test -z "$as_dirs" || eval "mkdir $as_dirs" + } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" + + +} # as_fn_mkdir_p + +# as_fn_executable_p FILE +# ----------------------- +# Test if FILE is an executable regular file. +as_fn_executable_p () +{ + test -f "$1" && test -x "$1" +} # as_fn_executable_p +# as_fn_append VAR VALUE +# ---------------------- +# Append the text in VALUE to the end of the definition contained in VAR. Take +# advantage of any shell optimizations that allow amortized linear growth over +# repeated appends, instead of the typical quadratic growth present in naive +# implementations. +if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then : + eval 'as_fn_append () + { + eval $1+=\$2 + }' +else + as_fn_append () + { + eval $1=\$$1\$2 + } +fi # as_fn_append + +# as_fn_arith ARG... +# ------------------ +# Perform arithmetic evaluation on the ARGs, and store the result in the +# global $as_val. Take advantage of shells that can avoid forks. The arguments +# must be portable across $(()) and expr. +if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then : + eval 'as_fn_arith () + { + as_val=$(( $* )) + }' +else + as_fn_arith () + { + as_val=`expr "$@" || test $? -eq 1` + } +fi # as_fn_arith + + +# as_fn_error STATUS ERROR [LINENO LOG_FD] +# ---------------------------------------- +# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are +# provided, also output the error to LOG_FD, referencing LINENO. Then exit the +# script with STATUS, using 1 if that was 0. +as_fn_error () +{ + as_status=$1; test $as_status -eq 0 && as_status=1 + if test "$4"; then + as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 + fi + $as_echo "$as_me: error: $2" >&2 + as_fn_exit $as_status +} # as_fn_error + +if expr a : '\(a\)' >/dev/null 2>&1 && + test "X`expr 00001 : '.*\(...\)'`" = X001; then + as_expr=expr +else + as_expr=false +fi + +if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then + as_basename=basename +else + as_basename=false +fi + +if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then + as_dirname=dirname +else + as_dirname=false +fi + +as_me=`$as_basename -- "$0" || +$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X/"$0" | + sed '/^.*\/\([^/][^/]*\)\/*$/{ + s//\1/ + q + } + /^X\/\(\/\/\)$/{ + s//\1/ + q + } + /^X\/\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + +# Avoid depending upon Character Ranges. +as_cr_letters='abcdefghijklmnopqrstuvwxyz' +as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' +as_cr_Letters=$as_cr_letters$as_cr_LETTERS +as_cr_digits='0123456789' +as_cr_alnum=$as_cr_Letters$as_cr_digits + + + as_lineno_1=$LINENO as_lineno_1a=$LINENO + as_lineno_2=$LINENO as_lineno_2a=$LINENO + eval 'test "x$as_lineno_1'$as_run'" != "x$as_lineno_2'$as_run'" && + test "x`expr $as_lineno_1'$as_run' + 1`" = "x$as_lineno_2'$as_run'"' || { + # Blame Lee E. McMahon (1931-1989) for sed's syntax. :-) + sed -n ' + p + /[$]LINENO/= + ' <$as_myself | + sed ' + s/[$]LINENO.*/&-/ + t lineno + b + :lineno + N + :loop + s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/ + t loop + s/-\n.*// + ' >$as_me.lineno && + chmod +x "$as_me.lineno" || + { $as_echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2; as_fn_exit 1; } + + # If we had to re-execute with $CONFIG_SHELL, we're ensured to have + # already done that, so ensure we don't try to do so again and fall + # in an infinite loop. This has already happened in practice. + _as_can_reexec=no; export _as_can_reexec + # Don't try to exec as it changes $[0], causing all sort of problems + # (the dirname of $[0] is not the place where we might find the + # original and so on. Autoconf is especially sensitive to this). + . "./$as_me.lineno" + # Exit status is that of the last command. + exit +} + +ECHO_C= ECHO_N= ECHO_T= +case `echo -n x` in #((((( +-n*) + case `echo 'xy\c'` in + *c*) ECHO_T=' ';; # ECHO_T is single tab character. + xy) ECHO_C='\c';; + *) echo `echo ksh88 bug on AIX 6.1` > /dev/null + ECHO_T=' ';; + esac;; +*) + ECHO_N='-n';; +esac + +rm -f conf$$ conf$$.exe conf$$.file +if test -d conf$$.dir; then + rm -f conf$$.dir/conf$$.file +else + rm -f conf$$.dir + mkdir conf$$.dir 2>/dev/null +fi +if (echo >conf$$.file) 2>/dev/null; then + if ln -s conf$$.file conf$$ 2>/dev/null; then + as_ln_s='ln -s' + # ... but there are two gotchas: + # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. + # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. + # In both cases, we have to default to `cp -pR'. + ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || + as_ln_s='cp -pR' + elif ln conf$$.file conf$$ 2>/dev/null; then + as_ln_s=ln + else + as_ln_s='cp -pR' + fi +else + as_ln_s='cp -pR' +fi +rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file +rmdir conf$$.dir 2>/dev/null + +if mkdir -p . 2>/dev/null; then + as_mkdir_p='mkdir -p "$as_dir"' +else + test -d ./-p && rmdir ./-p + as_mkdir_p=false +fi + +as_test_x='test -x' +as_executable_p=as_fn_executable_p + +# Sed expression to map a string onto a valid CPP name. +as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" + +# Sed expression to map a string onto a valid variable name. +as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" + +SHELL=${CONFIG_SHELL-/bin/sh} + + +test -n "$DJDIR" || exec 7<&0 &1 + +# Name of the host. +# hostname on some systems (SVR3.2, old GNU/Linux) returns a bogus exit status, +# so uname gets run too. +ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q` + +# +# Initializations. +# +ac_default_prefix=/usr/local +ac_clean_files= +ac_config_libobj_dir=. +LIBOBJS= +cross_compiling=no +subdirs= +MFLAGS= +MAKEFLAGS= + +# Identity of this package. +PACKAGE_NAME='nghttp2' +PACKAGE_TARNAME='nghttp2' +PACKAGE_VERSION='1.0.0' +PACKAGE_STRING='nghttp2 1.0.0' +PACKAGE_BUGREPORT='t-tujikawa@users.sourceforge.net' +PACKAGE_URL='' + +# Factoring default headers for most tests. +ac_includes_default="\ +#include +#ifdef HAVE_SYS_TYPES_H +# include +#endif +#ifdef HAVE_SYS_STAT_H +# include +#endif +#ifdef STDC_HEADERS +# include +# include +#else +# ifdef HAVE_STDLIB_H +# include +# endif +#endif +#ifdef HAVE_STRING_H +# if !defined STDC_HEADERS && defined HAVE_MEMORY_H +# include +# endif +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif +#ifdef HAVE_INTTYPES_H +# include +#endif +#ifdef HAVE_STDINT_H +# include +#endif +#ifdef HAVE_UNISTD_H +# include +#endif" + +ac_subst_vars='am__EXEEXT_FALSE +am__EXEEXT_TRUE +LTLIBOBJS +APPLDFLAGS +TESTLDADD +WARNCFLAGS +ENABLE_TINY_NGHTTPD_FALSE +ENABLE_TINY_NGHTTPD_TRUE +LIBOBJS +ENABLE_FAILMALLOC_FALSE +ENABLE_FAILMALLOC_TRUE +HAVE_CYTHON_FALSE +HAVE_CYTHON_TRUE +ENABLE_PYTHON_BINDINGS_FALSE +ENABLE_PYTHON_BINDINGS_TRUE +ENABLE_EXAMPLES_FALSE +ENABLE_EXAMPLES_TRUE +ENABLE_ASIO_LIB_FALSE +ENABLE_ASIO_LIB_TRUE +ENABLE_HPACK_TOOLS_FALSE +ENABLE_HPACK_TOOLS_TRUE +ENABLE_APP_FALSE +ENABLE_APP_TRUE +BOOST_THREAD_LIB +BOOST_SYSTEM_LIB +BOOST_ASIO_LIB +BOOST_LDFLAGS +BOOST_CPPFLAGS +HAVE_SPDYLAY_FALSE +HAVE_SPDYLAY_TRUE +LIBSPDYLAY_LIBS +LIBSPDYLAY_CFLAGS +JEMALLOC_LIBS +HAVE_LIBXML2_FALSE +HAVE_LIBXML2_TRUE +XML_LIBS +XML_CPPFLAGS +XML2_CONFIG +JANSSON_LIBS +JANSSON_CFLAGS +LIBEVENT_OPENSSL_LIBS +LIBEVENT_OPENSSL_CFLAGS +OPENSSL_LIBS +OPENSSL_CFLAGS +LIBEV_CFLAGS +LIBEV_LIBS +HAVE_CUNIT_FALSE +HAVE_CUNIT_TRUE +CUNIT_LIBS +CUNIT_CFLAGS +ZLIB_LIBS +ZLIB_CFLAGS +HAVE_CXX11 +PYTHON_EXTRA_LDFLAGS +PYTHON_EXTRA_LIBS +PYTHON_SITE_PKG +PYTHON_LDFLAGS +PYTHON_CPPFLAGS +pkgpyexecdir +pyexecdir +pkgpythondir +pythondir +PYTHON_PLATFORM +PYTHON_EXEC_PREFIX +PYTHON_PREFIX +PYTHON_VERSION +PYTHON +PKG_CONFIG_LIBDIR +PKG_CONFIG_PATH +PKG_CONFIG +am__fastdepCXX_FALSE +am__fastdepCXX_TRUE +CXXDEPMODE +CXXCPP +ac_ct_CXX +CXXFLAGS +CXX +CYTHON +PACKAGE_VERSION_NUM +LT_AGE +LT_REVISION +LT_CURRENT +AM_BACKSLASH +AM_DEFAULT_VERBOSITY +AM_DEFAULT_V +AM_V +am__fastdepCC_FALSE +am__fastdepCC_TRUE +CCDEPMODE +am__nodep +AMDEPBACKSLASH +AMDEP_FALSE +AMDEP_TRUE +am__quote +am__include +DEPDIR +am__untar +am__tar +AMTAR +am__leading_dot +SET_MAKE +mkdir_p +MKDIR_P +INSTALL_STRIP_PROGRAM +install_sh +MAKEINFO +AUTOHEADER +AUTOMAKE +AUTOCONF +ACLOCAL +VERSION +PACKAGE +CYGPATH_W +am__isrc +INSTALL_DATA +INSTALL_SCRIPT +INSTALL_PROGRAM +target_os +target_vendor +target_cpu +target +OTOOL64 +OTOOL +LIPO +NMEDIT +DSYMUTIL +MANIFEST_TOOL +AWK +RANLIB +STRIP +ac_ct_AR +AR +DLLTOOL +OBJDUMP +LN_S +NM +ac_ct_DUMPBIN +DUMPBIN +LD +FGREP +SED +host_os +host_vendor +host_cpu +host +build_os +build_vendor +build_cpu +build +LIBTOOL +EGREP +GREP +CPP +OBJEXT +EXEEXT +ac_ct_CC +CPPFLAGS +LDFLAGS +CFLAGS +CC +target_alias +host_alias +build_alias +LIBS +ECHO_T +ECHO_N +ECHO_C +DEFS +mandir +localedir +libdir +psdir +pdfdir +dvidir +htmldir +infodir +docdir +oldincludedir +includedir +localstatedir +sharedstatedir +sysconfdir +datadir +datarootdir +libexecdir +sbindir +bindir +program_transform_name +prefix +exec_prefix +PACKAGE_URL +PACKAGE_BUGREPORT +PACKAGE_STRING +PACKAGE_VERSION +PACKAGE_TARNAME +PACKAGE_NAME +PATH_SEPARATOR +SHELL' +ac_subst_files='' +ac_user_opts=' +enable_option_checking +enable_shared +enable_static +with_pic +enable_fast_install +with_gnu_ld +with_sysroot +enable_libtool_lock +enable_dependency_tracking +enable_silent_rules +enable_werror +enable_debug +enable_threads +enable_app +enable_hpack_tools +enable_asio_lib +enable_examples +enable_python_bindings +enable_failmalloc +with_libxml2 +with_jemalloc +with_spdylay +with_cython +with_xml_prefix +with_xml_exec_prefix +enable_xmltest +with_boost +with_boost_libdir +with_boost_asio +with_boost_system +with_boost_thread +enable_assert +enable_largefile +' + ac_precious_vars='build_alias +host_alias +target_alias +CC +CFLAGS +LDFLAGS +LIBS +CPPFLAGS +CPP +CYTHON +CXX +CXXFLAGS +CCC +CXXCPP +PKG_CONFIG +PKG_CONFIG_PATH +PKG_CONFIG_LIBDIR +PYTHON +PYTHON_VERSION +ZLIB_CFLAGS +ZLIB_LIBS +CUNIT_CFLAGS +CUNIT_LIBS +OPENSSL_CFLAGS +OPENSSL_LIBS +LIBEVENT_OPENSSL_CFLAGS +LIBEVENT_OPENSSL_LIBS +JANSSON_CFLAGS +JANSSON_LIBS +LIBSPDYLAY_CFLAGS +LIBSPDYLAY_LIBS' + + +# Initialize some variables set by options. +ac_init_help= +ac_init_version=false +ac_unrecognized_opts= +ac_unrecognized_sep= +# The variables have the same names as the options, with +# dashes changed to underlines. +cache_file=/dev/null +exec_prefix=NONE +no_create= +no_recursion= +prefix=NONE +program_prefix=NONE +program_suffix=NONE +program_transform_name=s,x,x, +silent= +site= +srcdir= +verbose= +x_includes=NONE +x_libraries=NONE + +# Installation directory options. +# These are left unexpanded so users can "make install exec_prefix=/foo" +# and all the variables that are supposed to be based on exec_prefix +# by default will actually change. +# Use braces instead of parens because sh, perl, etc. also accept them. +# (The list follows the same order as the GNU Coding Standards.) +bindir='${exec_prefix}/bin' +sbindir='${exec_prefix}/sbin' +libexecdir='${exec_prefix}/libexec' +datarootdir='${prefix}/share' +datadir='${datarootdir}' +sysconfdir='${prefix}/etc' +sharedstatedir='${prefix}/com' +localstatedir='${prefix}/var' +includedir='${prefix}/include' +oldincludedir='/usr/include' +docdir='${datarootdir}/doc/${PACKAGE_TARNAME}' +infodir='${datarootdir}/info' +htmldir='${docdir}' +dvidir='${docdir}' +pdfdir='${docdir}' +psdir='${docdir}' +libdir='${exec_prefix}/lib' +localedir='${datarootdir}/locale' +mandir='${datarootdir}/man' + +ac_prev= +ac_dashdash= +for ac_option +do + # If the previous option needs an argument, assign it. + if test -n "$ac_prev"; then + eval $ac_prev=\$ac_option + ac_prev= + continue + fi + + case $ac_option in + *=?*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;; + *=) ac_optarg= ;; + *) ac_optarg=yes ;; + esac + + # Accept the important Cygnus configure options, so we can diagnose typos. + + case $ac_dashdash$ac_option in + --) + ac_dashdash=yes ;; + + -bindir | --bindir | --bindi | --bind | --bin | --bi) + ac_prev=bindir ;; + -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*) + bindir=$ac_optarg ;; + + -build | --build | --buil | --bui | --bu) + ac_prev=build_alias ;; + -build=* | --build=* | --buil=* | --bui=* | --bu=*) + build_alias=$ac_optarg ;; + + -cache-file | --cache-file | --cache-fil | --cache-fi \ + | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) + ac_prev=cache_file ;; + -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \ + | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*) + cache_file=$ac_optarg ;; + + --config-cache | -C) + cache_file=config.cache ;; + + -datadir | --datadir | --datadi | --datad) + ac_prev=datadir ;; + -datadir=* | --datadir=* | --datadi=* | --datad=*) + datadir=$ac_optarg ;; + + -datarootdir | --datarootdir | --datarootdi | --datarootd | --dataroot \ + | --dataroo | --dataro | --datar) + ac_prev=datarootdir ;; + -datarootdir=* | --datarootdir=* | --datarootdi=* | --datarootd=* \ + | --dataroot=* | --dataroo=* | --dataro=* | --datar=*) + datarootdir=$ac_optarg ;; + + -disable-* | --disable-*) + ac_useropt=`expr "x$ac_option" : 'x-*disable-\(.*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + as_fn_error $? "invalid feature name: $ac_useropt" + ac_useropt_orig=$ac_useropt + ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"enable_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--disable-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval enable_$ac_useropt=no ;; + + -docdir | --docdir | --docdi | --doc | --do) + ac_prev=docdir ;; + -docdir=* | --docdir=* | --docdi=* | --doc=* | --do=*) + docdir=$ac_optarg ;; + + -dvidir | --dvidir | --dvidi | --dvid | --dvi | --dv) + ac_prev=dvidir ;; + -dvidir=* | --dvidir=* | --dvidi=* | --dvid=* | --dvi=* | --dv=*) + dvidir=$ac_optarg ;; + + -enable-* | --enable-*) + ac_useropt=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + as_fn_error $? "invalid feature name: $ac_useropt" + ac_useropt_orig=$ac_useropt + ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"enable_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--enable-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval enable_$ac_useropt=\$ac_optarg ;; + + -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \ + | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \ + | --exec | --exe | --ex) + ac_prev=exec_prefix ;; + -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \ + | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \ + | --exec=* | --exe=* | --ex=*) + exec_prefix=$ac_optarg ;; + + -gas | --gas | --ga | --g) + # Obsolete; use --with-gas. + with_gas=yes ;; + + -help | --help | --hel | --he | -h) + ac_init_help=long ;; + -help=r* | --help=r* | --hel=r* | --he=r* | -hr*) + ac_init_help=recursive ;; + -help=s* | --help=s* | --hel=s* | --he=s* | -hs*) + ac_init_help=short ;; + + -host | --host | --hos | --ho) + ac_prev=host_alias ;; + -host=* | --host=* | --hos=* | --ho=*) + host_alias=$ac_optarg ;; + + -htmldir | --htmldir | --htmldi | --htmld | --html | --htm | --ht) + ac_prev=htmldir ;; + -htmldir=* | --htmldir=* | --htmldi=* | --htmld=* | --html=* | --htm=* \ + | --ht=*) + htmldir=$ac_optarg ;; + + -includedir | --includedir | --includedi | --included | --include \ + | --includ | --inclu | --incl | --inc) + ac_prev=includedir ;; + -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \ + | --includ=* | --inclu=* | --incl=* | --inc=*) + includedir=$ac_optarg ;; + + -infodir | --infodir | --infodi | --infod | --info | --inf) + ac_prev=infodir ;; + -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*) + infodir=$ac_optarg ;; + + -libdir | --libdir | --libdi | --libd) + ac_prev=libdir ;; + -libdir=* | --libdir=* | --libdi=* | --libd=*) + libdir=$ac_optarg ;; + + -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \ + | --libexe | --libex | --libe) + ac_prev=libexecdir ;; + -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \ + | --libexe=* | --libex=* | --libe=*) + libexecdir=$ac_optarg ;; + + -localedir | --localedir | --localedi | --localed | --locale) + ac_prev=localedir ;; + -localedir=* | --localedir=* | --localedi=* | --localed=* | --locale=*) + localedir=$ac_optarg ;; + + -localstatedir | --localstatedir | --localstatedi | --localstated \ + | --localstate | --localstat | --localsta | --localst | --locals) + ac_prev=localstatedir ;; + -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \ + | --localstate=* | --localstat=* | --localsta=* | --localst=* | --locals=*) + localstatedir=$ac_optarg ;; + + -mandir | --mandir | --mandi | --mand | --man | --ma | --m) + ac_prev=mandir ;; + -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*) + mandir=$ac_optarg ;; + + -nfp | --nfp | --nf) + # Obsolete; use --without-fp. + with_fp=no ;; + + -no-create | --no-create | --no-creat | --no-crea | --no-cre \ + | --no-cr | --no-c | -n) + no_create=yes ;; + + -no-recursion | --no-recursion | --no-recursio | --no-recursi \ + | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) + no_recursion=yes ;; + + -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \ + | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \ + | --oldin | --oldi | --old | --ol | --o) + ac_prev=oldincludedir ;; + -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \ + | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \ + | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*) + oldincludedir=$ac_optarg ;; + + -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) + ac_prev=prefix ;; + -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) + prefix=$ac_optarg ;; + + -program-prefix | --program-prefix | --program-prefi | --program-pref \ + | --program-pre | --program-pr | --program-p) + ac_prev=program_prefix ;; + -program-prefix=* | --program-prefix=* | --program-prefi=* \ + | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*) + program_prefix=$ac_optarg ;; + + -program-suffix | --program-suffix | --program-suffi | --program-suff \ + | --program-suf | --program-su | --program-s) + ac_prev=program_suffix ;; + -program-suffix=* | --program-suffix=* | --program-suffi=* \ + | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*) + program_suffix=$ac_optarg ;; + + -program-transform-name | --program-transform-name \ + | --program-transform-nam | --program-transform-na \ + | --program-transform-n | --program-transform- \ + | --program-transform | --program-transfor \ + | --program-transfo | --program-transf \ + | --program-trans | --program-tran \ + | --progr-tra | --program-tr | --program-t) + ac_prev=program_transform_name ;; + -program-transform-name=* | --program-transform-name=* \ + | --program-transform-nam=* | --program-transform-na=* \ + | --program-transform-n=* | --program-transform-=* \ + | --program-transform=* | --program-transfor=* \ + | --program-transfo=* | --program-transf=* \ + | --program-trans=* | --program-tran=* \ + | --progr-tra=* | --program-tr=* | --program-t=*) + program_transform_name=$ac_optarg ;; + + -pdfdir | --pdfdir | --pdfdi | --pdfd | --pdf | --pd) + ac_prev=pdfdir ;; + -pdfdir=* | --pdfdir=* | --pdfdi=* | --pdfd=* | --pdf=* | --pd=*) + pdfdir=$ac_optarg ;; + + -psdir | --psdir | --psdi | --psd | --ps) + ac_prev=psdir ;; + -psdir=* | --psdir=* | --psdi=* | --psd=* | --ps=*) + psdir=$ac_optarg ;; + + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil) + silent=yes ;; + + -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) + ac_prev=sbindir ;; + -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ + | --sbi=* | --sb=*) + sbindir=$ac_optarg ;; + + -sharedstatedir | --sharedstatedir | --sharedstatedi \ + | --sharedstated | --sharedstate | --sharedstat | --sharedsta \ + | --sharedst | --shareds | --shared | --share | --shar \ + | --sha | --sh) + ac_prev=sharedstatedir ;; + -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \ + | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \ + | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \ + | --sha=* | --sh=*) + sharedstatedir=$ac_optarg ;; + + -site | --site | --sit) + ac_prev=site ;; + -site=* | --site=* | --sit=*) + site=$ac_optarg ;; + + -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) + ac_prev=srcdir ;; + -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) + srcdir=$ac_optarg ;; + + -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \ + | --syscon | --sysco | --sysc | --sys | --sy) + ac_prev=sysconfdir ;; + -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \ + | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*) + sysconfdir=$ac_optarg ;; + + -target | --target | --targe | --targ | --tar | --ta | --t) + ac_prev=target_alias ;; + -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*) + target_alias=$ac_optarg ;; + + -v | -verbose | --verbose | --verbos | --verbo | --verb) + verbose=yes ;; + + -version | --version | --versio | --versi | --vers | -V) + ac_init_version=: ;; + + -with-* | --with-*) + ac_useropt=`expr "x$ac_option" : 'x-*with-\([^=]*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + as_fn_error $? "invalid package name: $ac_useropt" + ac_useropt_orig=$ac_useropt + ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"with_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--with-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval with_$ac_useropt=\$ac_optarg ;; + + -without-* | --without-*) + ac_useropt=`expr "x$ac_option" : 'x-*without-\(.*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + as_fn_error $? "invalid package name: $ac_useropt" + ac_useropt_orig=$ac_useropt + ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"with_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--without-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval with_$ac_useropt=no ;; + + --x) + # Obsolete; use --with-x. + with_x=yes ;; + + -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \ + | --x-incl | --x-inc | --x-in | --x-i) + ac_prev=x_includes ;; + -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \ + | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*) + x_includes=$ac_optarg ;; + + -x-libraries | --x-libraries | --x-librarie | --x-librari \ + | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l) + ac_prev=x_libraries ;; + -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \ + | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) + x_libraries=$ac_optarg ;; + + -*) as_fn_error $? "unrecognized option: \`$ac_option' +Try \`$0 --help' for more information" + ;; + + *=*) + ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='` + # Reject names that are not valid shell variable names. + case $ac_envvar in #( + '' | [0-9]* | *[!_$as_cr_alnum]* ) + as_fn_error $? "invalid variable name: \`$ac_envvar'" ;; + esac + eval $ac_envvar=\$ac_optarg + export $ac_envvar ;; + + *) + # FIXME: should be removed in autoconf 3.0. + $as_echo "$as_me: WARNING: you should use --build, --host, --target" >&2 + expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null && + $as_echo "$as_me: WARNING: invalid host type: $ac_option" >&2 + : "${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}" + ;; + + esac +done + +if test -n "$ac_prev"; then + ac_option=--`echo $ac_prev | sed 's/_/-/g'` + as_fn_error $? "missing argument to $ac_option" +fi + +if test -n "$ac_unrecognized_opts"; then + case $enable_option_checking in + no) ;; + fatal) as_fn_error $? "unrecognized options: $ac_unrecognized_opts" ;; + *) $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2 ;; + esac +fi + +# Check all directory arguments for consistency. +for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \ + datadir sysconfdir sharedstatedir localstatedir includedir \ + oldincludedir docdir infodir htmldir dvidir pdfdir psdir \ + libdir localedir mandir +do + eval ac_val=\$$ac_var + # Remove trailing slashes. + case $ac_val in + */ ) + ac_val=`expr "X$ac_val" : 'X\(.*[^/]\)' \| "X$ac_val" : 'X\(.*\)'` + eval $ac_var=\$ac_val;; + esac + # Be sure to have absolute directory names. + case $ac_val in + [\\/$]* | ?:[\\/]* ) continue;; + NONE | '' ) case $ac_var in *prefix ) continue;; esac;; + esac + as_fn_error $? "expected an absolute directory name for --$ac_var: $ac_val" +done + +# There might be people who depend on the old broken behavior: `$host' +# used to hold the argument of --host etc. +# FIXME: To remove some day. +build=$build_alias +host=$host_alias +target=$target_alias + +# FIXME: To remove some day. +if test "x$host_alias" != x; then + if test "x$build_alias" = x; then + cross_compiling=maybe + elif test "x$build_alias" != "x$host_alias"; then + cross_compiling=yes + fi +fi + +ac_tool_prefix= +test -n "$host_alias" && ac_tool_prefix=$host_alias- + +test "$silent" = yes && exec 6>/dev/null + + +ac_pwd=`pwd` && test -n "$ac_pwd" && +ac_ls_di=`ls -di .` && +ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` || + as_fn_error $? "working directory cannot be determined" +test "X$ac_ls_di" = "X$ac_pwd_ls_di" || + as_fn_error $? "pwd does not report name of working directory" + + +# Find the source files, if location was not specified. +if test -z "$srcdir"; then + ac_srcdir_defaulted=yes + # Try the directory containing this script, then the parent directory. + ac_confdir=`$as_dirname -- "$as_myself" || +$as_expr X"$as_myself" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_myself" : 'X\(//\)[^/]' \| \ + X"$as_myself" : 'X\(//\)$' \| \ + X"$as_myself" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$as_myself" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + srcdir=$ac_confdir + if test ! -r "$srcdir/$ac_unique_file"; then + srcdir=.. + fi +else + ac_srcdir_defaulted=no +fi +if test ! -r "$srcdir/$ac_unique_file"; then + test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .." + as_fn_error $? "cannot find sources ($ac_unique_file) in $srcdir" +fi +ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work" +ac_abs_confdir=`( + cd "$srcdir" && test -r "./$ac_unique_file" || as_fn_error $? "$ac_msg" + pwd)` +# When building in place, set srcdir=. +if test "$ac_abs_confdir" = "$ac_pwd"; then + srcdir=. +fi +# Remove unnecessary trailing slashes from srcdir. +# Double slashes in file names in object file debugging info +# mess up M-x gdb in Emacs. +case $srcdir in +*/) srcdir=`expr "X$srcdir" : 'X\(.*[^/]\)' \| "X$srcdir" : 'X\(.*\)'`;; +esac +for ac_var in $ac_precious_vars; do + eval ac_env_${ac_var}_set=\${${ac_var}+set} + eval ac_env_${ac_var}_value=\$${ac_var} + eval ac_cv_env_${ac_var}_set=\${${ac_var}+set} + eval ac_cv_env_${ac_var}_value=\$${ac_var} +done + +# +# Report the --help message. +# +if test "$ac_init_help" = "long"; then + # Omit some internal or obsolete options to make the list less imposing. + # This message is too long to be a string in the A/UX 3.1 sh. + cat <<_ACEOF +\`configure' configures nghttp2 1.0.0 to adapt to many kinds of systems. + +Usage: $0 [OPTION]... [VAR=VALUE]... + +To assign environment variables (e.g., CC, CFLAGS...), specify them as +VAR=VALUE. See below for descriptions of some of the useful variables. + +Defaults for the options are specified in brackets. + +Configuration: + -h, --help display this help and exit + --help=short display options specific to this package + --help=recursive display the short help of all the included packages + -V, --version display version information and exit + -q, --quiet, --silent do not print \`checking ...' messages + --cache-file=FILE cache test results in FILE [disabled] + -C, --config-cache alias for \`--cache-file=config.cache' + -n, --no-create do not create output files + --srcdir=DIR find the sources in DIR [configure dir or \`..'] + +Installation directories: + --prefix=PREFIX install architecture-independent files in PREFIX + [$ac_default_prefix] + --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX + [PREFIX] + +By default, \`make install' will install all the files in +\`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc. You can specify +an installation prefix other than \`$ac_default_prefix' using \`--prefix', +for instance \`--prefix=\$HOME'. + +For better control, use the options below. + +Fine tuning of the installation directories: + --bindir=DIR user executables [EPREFIX/bin] + --sbindir=DIR system admin executables [EPREFIX/sbin] + --libexecdir=DIR program executables [EPREFIX/libexec] + --sysconfdir=DIR read-only single-machine data [PREFIX/etc] + --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] + --localstatedir=DIR modifiable single-machine data [PREFIX/var] + --libdir=DIR object code libraries [EPREFIX/lib] + --includedir=DIR C header files [PREFIX/include] + --oldincludedir=DIR C header files for non-gcc [/usr/include] + --datarootdir=DIR read-only arch.-independent data root [PREFIX/share] + --datadir=DIR read-only architecture-independent data [DATAROOTDIR] + --infodir=DIR info documentation [DATAROOTDIR/info] + --localedir=DIR locale-dependent data [DATAROOTDIR/locale] + --mandir=DIR man documentation [DATAROOTDIR/man] + --docdir=DIR documentation root [DATAROOTDIR/doc/nghttp2] + --htmldir=DIR html documentation [DOCDIR] + --dvidir=DIR dvi documentation [DOCDIR] + --pdfdir=DIR pdf documentation [DOCDIR] + --psdir=DIR ps documentation [DOCDIR] +_ACEOF + + cat <<\_ACEOF + +Program names: + --program-prefix=PREFIX prepend PREFIX to installed program names + --program-suffix=SUFFIX append SUFFIX to installed program names + --program-transform-name=PROGRAM run sed PROGRAM on installed program names + +System types: + --build=BUILD configure for building on BUILD [guessed] + --host=HOST cross-compile to build programs to run on HOST [BUILD] + --target=TARGET configure for building compilers for TARGET [HOST] +_ACEOF +fi + +if test -n "$ac_init_help"; then + case $ac_init_help in + short | recursive ) echo "Configuration of nghttp2 1.0.0:";; + esac + cat <<\_ACEOF + +Optional Features: + --disable-option-checking ignore unrecognized --enable/--with options + --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no) + --enable-FEATURE[=ARG] include FEATURE [ARG=yes] + --enable-shared[=PKGS] build shared libraries [default=yes] + --enable-static[=PKGS] build static libraries [default=yes] + --enable-fast-install[=PKGS] + optimize for fast installation [default=yes] + --disable-libtool-lock avoid locking (might break parallel builds) + --enable-dependency-tracking + do not reject slow dependency extractors + --disable-dependency-tracking + speeds up one-time build + --enable-silent-rules less verbose build output (undo: "make V=1") + --disable-silent-rules verbose build output (undo: "make V=0") + --enable-werror Turn on compile time warnings + --enable-debug Turn on debug output + --disable-threads Turn off threading in apps + --enable-app Build applications (nghttp, nghttpd and nghttpx) + [default=check] + --enable-hpack-tools Build HPACK tools [default=check] + --enable-asio-lib Build C++ libnghttp2_asio library [default=no] + --enable-examples Build examples [default=check] + --enable-python-bindings + Build Python bindings [default=check] + --disable-failmalloc Do not build failmalloc test program + --disable-xmltest Do not try to compile and run a test LIBXML program + --disable-assert turn off assertions + --disable-largefile omit support for large files + +Optional Packages: + --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] + --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no) + --with-pic[=PKGS] try to use only PIC/non-PIC objects [default=use + both] + --with-gnu-ld assume the C compiler uses GNU ld [default=no] + --with-sysroot=DIR Search for dependent libraries within DIR + (or the compiler's sysroot if not specified). + --with-libxml2 Use libxml2 [default=check] + --with-jemalloc Use jemalloc [default=check] + --with-spdylay Use spdylay [default=check] + --with-cython=PATH Use cython in given PATH + --with-xml-prefix=PFX Prefix where libxml is installed (optional) + --with-xml-exec-prefix=PFX Exec prefix where libxml is installed (optional) + --with-boost[=ARG] use Boost library from a standard location + (ARG=yes), from the specified location (ARG=), + or disable it (ARG=no) [ARG=yes] + --with-boost-libdir=LIB_DIR + Force given directory for boost libraries. Note that + this will override library path detection, so use + this parameter only if default library detection + fails and you know exactly where your boost + libraries are located. + --with-boost-asio[=special-lib] + use the ASIO library from boost - it is possible to + specify a certain library for the linker e.g. + --with-boost-asio=boost_system-gcc41-mt-1_34 + --with-boost-system[=special-lib] + use the System library from boost - it is possible + to specify a certain library for the linker e.g. + --with-boost-system=boost_system-gcc-mt + --with-boost-thread[=special-lib] + use the Thread library from boost - it is possible + to specify a certain library for the linker e.g. + --with-boost-thread=boost_thread-gcc-mt + +Some influential environment variables: + CC C compiler command + CFLAGS C compiler flags + LDFLAGS linker flags, e.g. -L if you have libraries in a + nonstandard directory + LIBS libraries to pass to the linker, e.g. -l + CPPFLAGS (Objective) C/C++ preprocessor flags, e.g. -I if + you have headers in a nonstandard directory + CPP C preprocessor + CYTHON the Cython executable + CXX C++ compiler command + CXXFLAGS C++ compiler flags + CXXCPP C++ preprocessor + PKG_CONFIG path to pkg-config utility + PKG_CONFIG_PATH + directories to add to pkg-config's search path + PKG_CONFIG_LIBDIR + path overriding pkg-config's built-in search path + PYTHON the Python interpreter + PYTHON_VERSION + The installed Python version to use, for example '2.3'. This + string will be appended to the Python interpreter canonical + name. + ZLIB_CFLAGS C compiler flags for ZLIB, overriding pkg-config + ZLIB_LIBS linker flags for ZLIB, overriding pkg-config + CUNIT_CFLAGS + C compiler flags for CUNIT, overriding pkg-config + CUNIT_LIBS linker flags for CUNIT, overriding pkg-config + OPENSSL_CFLAGS + C compiler flags for OPENSSL, overriding pkg-config + OPENSSL_LIBS + linker flags for OPENSSL, overriding pkg-config + LIBEVENT_OPENSSL_CFLAGS + C compiler flags for LIBEVENT_OPENSSL, overriding pkg-config + LIBEVENT_OPENSSL_LIBS + linker flags for LIBEVENT_OPENSSL, overriding pkg-config + JANSSON_CFLAGS + C compiler flags for JANSSON, overriding pkg-config + JANSSON_LIBS + linker flags for JANSSON, overriding pkg-config + LIBSPDYLAY_CFLAGS + C compiler flags for LIBSPDYLAY, overriding pkg-config + LIBSPDYLAY_LIBS + linker flags for LIBSPDYLAY, overriding pkg-config + +Use these variables to override the choices made by `configure' or to help +it to find libraries and programs with nonstandard names/locations. + +Report bugs to . +_ACEOF +ac_status=$? +fi + +if test "$ac_init_help" = "recursive"; then + # If there are subdirs, report their specific --help. + for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue + test -d "$ac_dir" || + { cd "$srcdir" && ac_pwd=`pwd` && srcdir=. && test -d "$ac_dir"; } || + continue + ac_builddir=. + +case "$ac_dir" in +.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; +*) + ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` + # A ".." for each directory in $ac_dir_suffix. + ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` + case $ac_top_builddir_sub in + "") ac_top_builddir_sub=. ac_top_build_prefix= ;; + *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; + esac ;; +esac +ac_abs_top_builddir=$ac_pwd +ac_abs_builddir=$ac_pwd$ac_dir_suffix +# for backward compatibility: +ac_top_builddir=$ac_top_build_prefix + +case $srcdir in + .) # We are building in place. + ac_srcdir=. + ac_top_srcdir=$ac_top_builddir_sub + ac_abs_top_srcdir=$ac_pwd ;; + [\\/]* | ?:[\\/]* ) # Absolute name. + ac_srcdir=$srcdir$ac_dir_suffix; + ac_top_srcdir=$srcdir + ac_abs_top_srcdir=$srcdir ;; + *) # Relative name. + ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix + ac_top_srcdir=$ac_top_build_prefix$srcdir + ac_abs_top_srcdir=$ac_pwd/$srcdir ;; +esac +ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix + + cd "$ac_dir" || { ac_status=$?; continue; } + # Check for guested configure. + if test -f "$ac_srcdir/configure.gnu"; then + echo && + $SHELL "$ac_srcdir/configure.gnu" --help=recursive + elif test -f "$ac_srcdir/configure"; then + echo && + $SHELL "$ac_srcdir/configure" --help=recursive + else + $as_echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2 + fi || ac_status=$? + cd "$ac_pwd" || { ac_status=$?; break; } + done +fi + +test -n "$ac_init_help" && exit $ac_status +if $ac_init_version; then + cat <<\_ACEOF +nghttp2 configure 1.0.0 +generated by GNU Autoconf 2.69 + +Copyright (C) 2012 Free Software Foundation, Inc. +This configure script is free software; the Free Software Foundation +gives unlimited permission to copy, distribute and modify it. +_ACEOF + exit +fi + +## ------------------------ ## +## Autoconf initialization. ## +## ------------------------ ## + +# ac_fn_c_try_compile LINENO +# -------------------------- +# Try to compile conftest.$ac_ext, and return whether this succeeded. +ac_fn_c_try_compile () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + rm -f conftest.$ac_objext + if { { ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_compile") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + grep -v '^ *+' conftest.err >conftest.er1 + cat conftest.er1 >&5 + mv -f conftest.er1 conftest.err + fi + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then : + ac_retval=0 +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=1 +fi + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval + +} # ac_fn_c_try_compile + +# ac_fn_c_try_cpp LINENO +# ---------------------- +# Try to preprocess conftest.$ac_ext, and return whether this succeeded. +ac_fn_c_try_cpp () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + if { { ac_try="$ac_cpp conftest.$ac_ext" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_cpp conftest.$ac_ext") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + grep -v '^ *+' conftest.err >conftest.er1 + cat conftest.er1 >&5 + mv -f conftest.er1 conftest.err + fi + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } > conftest.i && { + test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || + test ! -s conftest.err + }; then : + ac_retval=0 +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=1 +fi + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval + +} # ac_fn_c_try_cpp + +# ac_fn_c_check_header_mongrel LINENO HEADER VAR INCLUDES +# ------------------------------------------------------- +# Tests whether HEADER exists, giving a warning if it cannot be compiled using +# the include files in INCLUDES and setting the cache variable VAR +# accordingly. +ac_fn_c_check_header_mongrel () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + if eval \${$3+:} false; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 +$as_echo_n "checking for $2... " >&6; } +if eval \${$3+:} false; then : + $as_echo_n "(cached) " >&6 +fi +eval ac_res=\$$3 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } +else + # Is the header compilable? +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 usability" >&5 +$as_echo_n "checking $2 usability... " >&6; } +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +#include <$2> +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_header_compiler=yes +else + ac_header_compiler=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_compiler" >&5 +$as_echo "$ac_header_compiler" >&6; } + +# Is the header present? +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 presence" >&5 +$as_echo_n "checking $2 presence... " >&6; } +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include <$2> +_ACEOF +if ac_fn_c_try_cpp "$LINENO"; then : + ac_header_preproc=yes +else + ac_header_preproc=no +fi +rm -f conftest.err conftest.i conftest.$ac_ext +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_preproc" >&5 +$as_echo "$ac_header_preproc" >&6; } + +# So? What about this header? +case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in #(( + yes:no: ) + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&5 +$as_echo "$as_me: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5 +$as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;} + ;; + no:yes:* ) + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: present but cannot be compiled" >&5 +$as_echo "$as_me: WARNING: $2: present but cannot be compiled" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: check for missing prerequisite headers?" >&5 +$as_echo "$as_me: WARNING: $2: check for missing prerequisite headers?" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: see the Autoconf documentation" >&5 +$as_echo "$as_me: WARNING: $2: see the Autoconf documentation" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&5 +$as_echo "$as_me: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5 +$as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;} +( $as_echo "## ----------------------------------------------- ## +## Report this to t-tujikawa@users.sourceforge.net ## +## ----------------------------------------------- ##" + ) | sed "s/^/$as_me: WARNING: /" >&2 + ;; +esac + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 +$as_echo_n "checking for $2... " >&6; } +if eval \${$3+:} false; then : + $as_echo_n "(cached) " >&6 +else + eval "$3=\$ac_header_compiler" +fi +eval ac_res=\$$3 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } +fi + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + +} # ac_fn_c_check_header_mongrel + +# ac_fn_c_try_run LINENO +# ---------------------- +# Try to link conftest.$ac_ext, and return whether this succeeded. Assumes +# that executables *can* be run. +ac_fn_c_try_run () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + if { { ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && { ac_try='./conftest$ac_exeext' + { { case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_try") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; }; then : + ac_retval=0 +else + $as_echo "$as_me: program exited with status $ac_status" >&5 + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=$ac_status +fi + rm -rf conftest.dSYM conftest_ipa8_conftest.oo + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval + +} # ac_fn_c_try_run + +# ac_fn_c_check_header_compile LINENO HEADER VAR INCLUDES +# ------------------------------------------------------- +# Tests whether HEADER exists and can be compiled using the include files in +# INCLUDES, setting the cache variable VAR accordingly. +ac_fn_c_check_header_compile () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 +$as_echo_n "checking for $2... " >&6; } +if eval \${$3+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +#include <$2> +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + eval "$3=yes" +else + eval "$3=no" +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +eval ac_res=\$$3 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + +} # ac_fn_c_check_header_compile + +# ac_fn_c_try_link LINENO +# ----------------------- +# Try to link conftest.$ac_ext, and return whether this succeeded. +ac_fn_c_try_link () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + rm -f conftest.$ac_objext conftest$ac_exeext + if { { ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + grep -v '^ *+' conftest.err >conftest.er1 + cat conftest.er1 >&5 + mv -f conftest.er1 conftest.err + fi + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest$ac_exeext && { + test "$cross_compiling" = yes || + test -x conftest$ac_exeext + }; then : + ac_retval=0 +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=1 +fi + # Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information + # created by the PGI compiler (conftest_ipa8_conftest.oo), as it would + # interfere with the next link command; also delete a directory that is + # left behind by Apple's compiler. We do this before executing the actions. + rm -rf conftest.dSYM conftest_ipa8_conftest.oo + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval + +} # ac_fn_c_try_link + +# ac_fn_c_check_func LINENO FUNC VAR +# ---------------------------------- +# Tests whether FUNC exists, setting the cache variable VAR accordingly +ac_fn_c_check_func () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 +$as_echo_n "checking for $2... " >&6; } +if eval \${$3+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +/* Define $2 to an innocuous variant, in case declares $2. + For example, HP-UX 11i declares gettimeofday. */ +#define $2 innocuous_$2 + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $2 (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#endif + +#undef $2 + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char $2 (); +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined __stub_$2 || defined __stub___$2 +choke me +#endif + +int +main () +{ +return $2 (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + eval "$3=yes" +else + eval "$3=no" +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +eval ac_res=\$$3 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + +} # ac_fn_c_check_func + +# ac_fn_cxx_try_compile LINENO +# ---------------------------- +# Try to compile conftest.$ac_ext, and return whether this succeeded. +ac_fn_cxx_try_compile () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + rm -f conftest.$ac_objext + if { { ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_compile") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + grep -v '^ *+' conftest.err >conftest.er1 + cat conftest.er1 >&5 + mv -f conftest.er1 conftest.err + fi + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && { + test -z "$ac_cxx_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then : + ac_retval=0 +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=1 +fi + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval + +} # ac_fn_cxx_try_compile + +# ac_fn_cxx_try_cpp LINENO +# ------------------------ +# Try to preprocess conftest.$ac_ext, and return whether this succeeded. +ac_fn_cxx_try_cpp () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + if { { ac_try="$ac_cpp conftest.$ac_ext" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_cpp conftest.$ac_ext") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + grep -v '^ *+' conftest.err >conftest.er1 + cat conftest.er1 >&5 + mv -f conftest.er1 conftest.err + fi + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } > conftest.i && { + test -z "$ac_cxx_preproc_warn_flag$ac_cxx_werror_flag" || + test ! -s conftest.err + }; then : + ac_retval=0 +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=1 +fi + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval + +} # ac_fn_cxx_try_cpp + +# ac_fn_cxx_try_link LINENO +# ------------------------- +# Try to link conftest.$ac_ext, and return whether this succeeded. +ac_fn_cxx_try_link () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + rm -f conftest.$ac_objext conftest$ac_exeext + if { { ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + grep -v '^ *+' conftest.err >conftest.er1 + cat conftest.er1 >&5 + mv -f conftest.er1 conftest.err + fi + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && { + test -z "$ac_cxx_werror_flag" || + test ! -s conftest.err + } && test -s conftest$ac_exeext && { + test "$cross_compiling" = yes || + test -x conftest$ac_exeext + }; then : + ac_retval=0 +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=1 +fi + # Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information + # created by the PGI compiler (conftest_ipa8_conftest.oo), as it would + # interfere with the next link command; also delete a directory that is + # left behind by Apple's compiler. We do this before executing the actions. + rm -rf conftest.dSYM conftest_ipa8_conftest.oo + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval + +} # ac_fn_cxx_try_link + +# ac_fn_c_check_type LINENO TYPE VAR INCLUDES +# ------------------------------------------- +# Tests whether TYPE exists after having included INCLUDES, setting cache +# variable VAR accordingly. +ac_fn_c_check_type () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 +$as_echo_n "checking for $2... " >&6; } +if eval \${$3+:} false; then : + $as_echo_n "(cached) " >&6 +else + eval "$3=no" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +int +main () +{ +if (sizeof ($2)) + return 0; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +int +main () +{ +if (sizeof (($2))) + return 0; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + +else + eval "$3=yes" +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +eval ac_res=\$$3 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + +} # ac_fn_c_check_type + +# ac_fn_c_find_uintX_t LINENO BITS VAR +# ------------------------------------ +# Finds an unsigned integer type with width BITS, setting cache variable VAR +# accordingly. +ac_fn_c_find_uintX_t () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for uint$2_t" >&5 +$as_echo_n "checking for uint$2_t... " >&6; } +if eval \${$3+:} false; then : + $as_echo_n "(cached) " >&6 +else + eval "$3=no" + # Order is important - never check a type that is potentially smaller + # than half of the expected target width. + for ac_type in uint$2_t 'unsigned int' 'unsigned long int' \ + 'unsigned long long int' 'unsigned short int' 'unsigned char'; do + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +static int test_array [1 - 2 * !((($ac_type) -1 >> ($2 / 2 - 1)) >> ($2 / 2 - 1) == 3)]; +test_array [0] = 0; +return test_array [0]; + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + case $ac_type in #( + uint$2_t) : + eval "$3=yes" ;; #( + *) : + eval "$3=\$ac_type" ;; +esac +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + if eval test \"x\$"$3"\" = x"no"; then : + +else + break +fi + done +fi +eval ac_res=\$$3 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + +} # ac_fn_c_find_uintX_t + +# ac_fn_c_find_intX_t LINENO BITS VAR +# ----------------------------------- +# Finds a signed integer type with width BITS, setting cache variable VAR +# accordingly. +ac_fn_c_find_intX_t () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for int$2_t" >&5 +$as_echo_n "checking for int$2_t... " >&6; } +if eval \${$3+:} false; then : + $as_echo_n "(cached) " >&6 +else + eval "$3=no" + # Order is important - never check a type that is potentially smaller + # than half of the expected target width. + for ac_type in int$2_t 'int' 'long int' \ + 'long long int' 'short int' 'signed char'; do + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$ac_includes_default + enum { N = $2 / 2 - 1 }; +int +main () +{ +static int test_array [1 - 2 * !(0 < ($ac_type) ((((($ac_type) 1 << N) << N) - 1) * 2 + 1))]; +test_array [0] = 0; +return test_array [0]; + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$ac_includes_default + enum { N = $2 / 2 - 1 }; +int +main () +{ +static int test_array [1 - 2 * !(($ac_type) ((((($ac_type) 1 << N) << N) - 1) * 2 + 1) + < ($ac_type) ((((($ac_type) 1 << N) << N) - 1) * 2 + 2))]; +test_array [0] = 0; +return test_array [0]; + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + +else + case $ac_type in #( + int$2_t) : + eval "$3=yes" ;; #( + *) : + eval "$3=\$ac_type" ;; +esac +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + if eval test \"x\$"$3"\" = x"no"; then : + +else + break +fi + done +fi +eval ac_res=\$$3 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + +} # ac_fn_c_find_intX_t + +# ac_fn_c_check_member LINENO AGGR MEMBER VAR INCLUDES +# ---------------------------------------------------- +# Tries to find if the field MEMBER exists in type AGGR, after including +# INCLUDES, setting cache variable VAR accordingly. +ac_fn_c_check_member () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2.$3" >&5 +$as_echo_n "checking for $2.$3... " >&6; } +if eval \${$4+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$5 +int +main () +{ +static $2 ac_aggr; +if (ac_aggr.$3) +return 0; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + eval "$4=yes" +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$5 +int +main () +{ +static $2 ac_aggr; +if (sizeof ac_aggr.$3) +return 0; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + eval "$4=yes" +else + eval "$4=no" +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +eval ac_res=\$$4 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + +} # ac_fn_c_check_member + +# ac_fn_c_compute_int LINENO EXPR VAR INCLUDES +# -------------------------------------------- +# Tries to find the compile-time value of EXPR in a program that includes +# INCLUDES, setting VAR accordingly. Returns whether the value could be +# computed +ac_fn_c_compute_int () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + if test "$cross_compiling" = yes; then + # Depending upon the size, compute the lo and hi bounds. +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +int +main () +{ +static int test_array [1 - 2 * !(($2) >= 0)]; +test_array [0] = 0; +return test_array [0]; + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_lo=0 ac_mid=0 + while :; do + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +int +main () +{ +static int test_array [1 - 2 * !(($2) <= $ac_mid)]; +test_array [0] = 0; +return test_array [0]; + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_hi=$ac_mid; break +else + as_fn_arith $ac_mid + 1 && ac_lo=$as_val + if test $ac_lo -le $ac_mid; then + ac_lo= ac_hi= + break + fi + as_fn_arith 2 '*' $ac_mid + 1 && ac_mid=$as_val +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + done +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +int +main () +{ +static int test_array [1 - 2 * !(($2) < 0)]; +test_array [0] = 0; +return test_array [0]; + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_hi=-1 ac_mid=-1 + while :; do + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +int +main () +{ +static int test_array [1 - 2 * !(($2) >= $ac_mid)]; +test_array [0] = 0; +return test_array [0]; + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_lo=$ac_mid; break +else + as_fn_arith '(' $ac_mid ')' - 1 && ac_hi=$as_val + if test $ac_mid -le $ac_hi; then + ac_lo= ac_hi= + break + fi + as_fn_arith 2 '*' $ac_mid && ac_mid=$as_val +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + done +else + ac_lo= ac_hi= +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +# Binary search between lo and hi bounds. +while test "x$ac_lo" != "x$ac_hi"; do + as_fn_arith '(' $ac_hi - $ac_lo ')' / 2 + $ac_lo && ac_mid=$as_val + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +int +main () +{ +static int test_array [1 - 2 * !(($2) <= $ac_mid)]; +test_array [0] = 0; +return test_array [0]; + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_hi=$ac_mid +else + as_fn_arith '(' $ac_mid ')' + 1 && ac_lo=$as_val +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +done +case $ac_lo in #(( +?*) eval "$3=\$ac_lo"; ac_retval=0 ;; +'') ac_retval=1 ;; +esac + else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +static long int longval () { return $2; } +static unsigned long int ulongval () { return $2; } +#include +#include +int +main () +{ + + FILE *f = fopen ("conftest.val", "w"); + if (! f) + return 1; + if (($2) < 0) + { + long int i = longval (); + if (i != ($2)) + return 1; + fprintf (f, "%ld", i); + } + else + { + unsigned long int i = ulongval (); + if (i != ($2)) + return 1; + fprintf (f, "%lu", i); + } + /* Do not output a trailing newline, as this causes \r\n confusion + on some platforms. */ + return ferror (f) || fclose (f) != 0; + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + echo >>conftest.val; read $3 &5 +$as_echo_n "checking whether $as_decl_name is declared... " >&6; } +if eval \${$3+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +int +main () +{ +#ifndef $as_decl_name +#ifdef __cplusplus + (void) $as_decl_use; +#else + (void) $as_decl_name; +#endif +#endif + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + eval "$3=yes" +else + eval "$3=no" +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +eval ac_res=\$$3 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + +} # ac_fn_c_check_decl +cat >config.log <<_ACEOF +This file contains any messages produced by compilers while +running configure, to aid debugging if configure makes a mistake. + +It was created by nghttp2 $as_me 1.0.0, which was +generated by GNU Autoconf 2.69. Invocation command line was + + $ $0 $@ + +_ACEOF +exec 5>>config.log +{ +cat <<_ASUNAME +## --------- ## +## Platform. ## +## --------- ## + +hostname = `(hostname || uname -n) 2>/dev/null | sed 1q` +uname -m = `(uname -m) 2>/dev/null || echo unknown` +uname -r = `(uname -r) 2>/dev/null || echo unknown` +uname -s = `(uname -s) 2>/dev/null || echo unknown` +uname -v = `(uname -v) 2>/dev/null || echo unknown` + +/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown` +/bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown` + +/bin/arch = `(/bin/arch) 2>/dev/null || echo unknown` +/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown` +/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown` +/usr/bin/hostinfo = `(/usr/bin/hostinfo) 2>/dev/null || echo unknown` +/bin/machine = `(/bin/machine) 2>/dev/null || echo unknown` +/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown` +/bin/universe = `(/bin/universe) 2>/dev/null || echo unknown` + +_ASUNAME + +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + $as_echo "PATH: $as_dir" + done +IFS=$as_save_IFS + +} >&5 + +cat >&5 <<_ACEOF + + +## ----------- ## +## Core tests. ## +## ----------- ## + +_ACEOF + + +# Keep a trace of the command line. +# Strip out --no-create and --no-recursion so they do not pile up. +# Strip out --silent because we don't want to record it for future runs. +# Also quote any args containing shell meta-characters. +# Make two passes to allow for proper duplicate-argument suppression. +ac_configure_args= +ac_configure_args0= +ac_configure_args1= +ac_must_keep_next=false +for ac_pass in 1 2 +do + for ac_arg + do + case $ac_arg in + -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;; + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil) + continue ;; + *\'*) + ac_arg=`$as_echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; + esac + case $ac_pass in + 1) as_fn_append ac_configure_args0 " '$ac_arg'" ;; + 2) + as_fn_append ac_configure_args1 " '$ac_arg'" + if test $ac_must_keep_next = true; then + ac_must_keep_next=false # Got value, back to normal. + else + case $ac_arg in + *=* | --config-cache | -C | -disable-* | --disable-* \ + | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \ + | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \ + | -with-* | --with-* | -without-* | --without-* | --x) + case "$ac_configure_args0 " in + "$ac_configure_args1"*" '$ac_arg' "* ) continue ;; + esac + ;; + -* ) ac_must_keep_next=true ;; + esac + fi + as_fn_append ac_configure_args " '$ac_arg'" + ;; + esac + done +done +{ ac_configure_args0=; unset ac_configure_args0;} +{ ac_configure_args1=; unset ac_configure_args1;} + +# When interrupted or exit'd, cleanup temporary files, and complete +# config.log. We remove comments because anyway the quotes in there +# would cause problems or look ugly. +# WARNING: Use '\'' to represent an apostrophe within the trap. +# WARNING: Do not start the trap code with a newline, due to a FreeBSD 4.0 bug. +trap 'exit_status=$? + # Save into config.log some information that might help in debugging. + { + echo + + $as_echo "## ---------------- ## +## Cache variables. ## +## ---------------- ##" + echo + # The following way of writing the cache mishandles newlines in values, +( + for ac_var in `(set) 2>&1 | sed -n '\''s/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'\''`; do + eval ac_val=\$$ac_var + case $ac_val in #( + *${as_nl}*) + case $ac_var in #( + *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 +$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; + esac + case $ac_var in #( + _ | IFS | as_nl) ;; #( + BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( + *) { eval $ac_var=; unset $ac_var;} ;; + esac ;; + esac + done + (set) 2>&1 | + case $as_nl`(ac_space='\'' '\''; set) 2>&1` in #( + *${as_nl}ac_space=\ *) + sed -n \ + "s/'\''/'\''\\\\'\'''\''/g; + s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\''\\2'\''/p" + ;; #( + *) + sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" + ;; + esac | + sort +) + echo + + $as_echo "## ----------------- ## +## Output variables. ## +## ----------------- ##" + echo + for ac_var in $ac_subst_vars + do + eval ac_val=\$$ac_var + case $ac_val in + *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; + esac + $as_echo "$ac_var='\''$ac_val'\''" + done | sort + echo + + if test -n "$ac_subst_files"; then + $as_echo "## ------------------- ## +## File substitutions. ## +## ------------------- ##" + echo + for ac_var in $ac_subst_files + do + eval ac_val=\$$ac_var + case $ac_val in + *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; + esac + $as_echo "$ac_var='\''$ac_val'\''" + done | sort + echo + fi + + if test -s confdefs.h; then + $as_echo "## ----------- ## +## confdefs.h. ## +## ----------- ##" + echo + cat confdefs.h + echo + fi + test "$ac_signal" != 0 && + $as_echo "$as_me: caught signal $ac_signal" + $as_echo "$as_me: exit $exit_status" + } >&5 + rm -f core *.core core.conftest.* && + rm -f -r conftest* confdefs* conf$$* $ac_clean_files && + exit $exit_status +' 0 +for ac_signal in 1 2 13 15; do + trap 'ac_signal='$ac_signal'; as_fn_exit 1' $ac_signal +done +ac_signal=0 + +# confdefs.h avoids OS command line length limits that DEFS can exceed. +rm -f -r conftest* confdefs.h + +$as_echo "/* confdefs.h */" > confdefs.h + +# Predefined preprocessor variables. + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_NAME "$PACKAGE_NAME" +_ACEOF + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_TARNAME "$PACKAGE_TARNAME" +_ACEOF + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_VERSION "$PACKAGE_VERSION" +_ACEOF + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_STRING "$PACKAGE_STRING" +_ACEOF + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT" +_ACEOF + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_URL "$PACKAGE_URL" +_ACEOF + + +# Let the site file select an alternate cache file if it wants to. +# Prefer an explicitly selected file to automatically selected ones. +ac_site_file1=NONE +ac_site_file2=NONE +if test -n "$CONFIG_SITE"; then + # We do not want a PATH search for config.site. + case $CONFIG_SITE in #(( + -*) ac_site_file1=./$CONFIG_SITE;; + */*) ac_site_file1=$CONFIG_SITE;; + *) ac_site_file1=./$CONFIG_SITE;; + esac +elif test "x$prefix" != xNONE; then + ac_site_file1=$prefix/share/config.site + ac_site_file2=$prefix/etc/config.site +else + ac_site_file1=$ac_default_prefix/share/config.site + ac_site_file2=$ac_default_prefix/etc/config.site +fi +for ac_site_file in "$ac_site_file1" "$ac_site_file2" +do + test "x$ac_site_file" = xNONE && continue + if test /dev/null != "$ac_site_file" && test -r "$ac_site_file"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: loading site script $ac_site_file" >&5 +$as_echo "$as_me: loading site script $ac_site_file" >&6;} + sed 's/^/| /' "$ac_site_file" >&5 + . "$ac_site_file" \ + || { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "failed to load site script $ac_site_file +See \`config.log' for more details" "$LINENO" 5; } + fi +done + +if test -r "$cache_file"; then + # Some versions of bash will fail to source /dev/null (special files + # actually), so we avoid doing that. DJGPP emulates it as a regular file. + if test /dev/null != "$cache_file" && test -f "$cache_file"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: loading cache $cache_file" >&5 +$as_echo "$as_me: loading cache $cache_file" >&6;} + case $cache_file in + [\\/]* | ?:[\\/]* ) . "$cache_file";; + *) . "./$cache_file";; + esac + fi +else + { $as_echo "$as_me:${as_lineno-$LINENO}: creating cache $cache_file" >&5 +$as_echo "$as_me: creating cache $cache_file" >&6;} + >$cache_file +fi + +# Check that the precious variables saved in the cache have kept the same +# value. +ac_cache_corrupted=false +for ac_var in $ac_precious_vars; do + eval ac_old_set=\$ac_cv_env_${ac_var}_set + eval ac_new_set=\$ac_env_${ac_var}_set + eval ac_old_val=\$ac_cv_env_${ac_var}_value + eval ac_new_val=\$ac_env_${ac_var}_value + case $ac_old_set,$ac_new_set in + set,) + { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5 +$as_echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;} + ac_cache_corrupted=: ;; + ,set) + { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was not set in the previous run" >&5 +$as_echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;} + ac_cache_corrupted=: ;; + ,);; + *) + if test "x$ac_old_val" != "x$ac_new_val"; then + # differences in whitespace do not lead to failure. + ac_old_val_w=`echo x $ac_old_val` + ac_new_val_w=`echo x $ac_new_val` + if test "$ac_old_val_w" != "$ac_new_val_w"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' has changed since the previous run:" >&5 +$as_echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;} + ac_cache_corrupted=: + else + { $as_echo "$as_me:${as_lineno-$LINENO}: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&5 +$as_echo "$as_me: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&2;} + eval $ac_var=\$ac_old_val + fi + { $as_echo "$as_me:${as_lineno-$LINENO}: former value: \`$ac_old_val'" >&5 +$as_echo "$as_me: former value: \`$ac_old_val'" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: current value: \`$ac_new_val'" >&5 +$as_echo "$as_me: current value: \`$ac_new_val'" >&2;} + fi;; + esac + # Pass precious variables to config.status. + if test "$ac_new_set" = set; then + case $ac_new_val in + *\'*) ac_arg=$ac_var=`$as_echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;; + *) ac_arg=$ac_var=$ac_new_val ;; + esac + case " $ac_configure_args " in + *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy. + *) as_fn_append ac_configure_args " '$ac_arg'" ;; + esac + fi +done +if $ac_cache_corrupted; then + { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: error: changes in the environment can compromise the build" >&5 +$as_echo "$as_me: error: changes in the environment can compromise the build" >&2;} + as_fn_error $? "run \`make distclean' and/or \`rm $cache_file' and start over" "$LINENO" 5 +fi +## -------------------- ## +## Main body of script. ## +## -------------------- ## + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + +ac_aux_dir= +for ac_dir in "$srcdir" "$srcdir/.." "$srcdir/../.."; do + if test -f "$ac_dir/install-sh"; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/install-sh -c" + break + elif test -f "$ac_dir/install.sh"; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/install.sh -c" + break + elif test -f "$ac_dir/shtool"; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/shtool install -c" + break + fi +done +if test -z "$ac_aux_dir"; then + as_fn_error $? "cannot find install-sh, install.sh, or shtool in \"$srcdir\" \"$srcdir/..\" \"$srcdir/../..\"" "$LINENO" 5 +fi + +# These three variables are undocumented and unsupported, +# and are intended to be withdrawn in a future Autoconf release. +# They can cause serious problems if a builder's source tree is in a directory +# whose full name contains unusual characters. +ac_config_guess="$SHELL $ac_aux_dir/config.guess" # Please don't use this var. +ac_config_sub="$SHELL $ac_aux_dir/config.sub" # Please don't use this var. +ac_configure="$SHELL $ac_aux_dir/configure" # Please don't use this var. + + +# Expand $ac_aux_dir to an absolute path. +am_aux_dir=`cd "$ac_aux_dir" && pwd` + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args. +set dummy ${ac_tool_prefix}gcc; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="${ac_tool_prefix}gcc" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_CC"; then + ac_ct_CC=$CC + # Extract the first word of "gcc", so it can be a program name with args. +set dummy gcc; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_CC="gcc" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 +$as_echo "$ac_ct_CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_CC" = x; then + CC="" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + CC=$ac_ct_CC + fi +else + CC="$ac_cv_prog_CC" +fi + +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args. +set dummy ${ac_tool_prefix}cc; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="${ac_tool_prefix}cc" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + fi +fi +if test -z "$CC"; then + # Extract the first word of "cc", so it can be a program name with args. +set dummy cc; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else + ac_prog_rejected=no +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then + ac_prog_rejected=yes + continue + fi + ac_cv_prog_CC="cc" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +if test $ac_prog_rejected = yes; then + # We found a bogon in the path, so make sure we never use it. + set dummy $ac_cv_prog_CC + shift + if test $# != 0; then + # We chose a different compiler from the bogus one. + # However, it has the same basename, so the bogon will be chosen + # first if we set CC to just the basename; use the full file name. + shift + ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@" + fi +fi +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + for ac_prog in cl.exe + do + # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. +set dummy $ac_tool_prefix$ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="$ac_tool_prefix$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$CC" && break + done +fi +if test -z "$CC"; then + ac_ct_CC=$CC + for ac_prog in cl.exe +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_CC="$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 +$as_echo "$ac_ct_CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$ac_ct_CC" && break +done + + if test "x$ac_ct_CC" = x; then + CC="" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + CC=$ac_ct_CC + fi +fi + +fi + + +test -z "$CC" && { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "no acceptable C compiler found in \$PATH +See \`config.log' for more details" "$LINENO" 5; } + +# Provide some information about the compiler. +$as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5 +set X $ac_compile +ac_compiler=$2 +for ac_option in --version -v -V -qversion; do + { { ac_try="$ac_compiler $ac_option >&5" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_compiler $ac_option >&5") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + sed '10a\ +... rest of stderr output deleted ... + 10q' conftest.err >conftest.er1 + cat conftest.er1 >&5 + fi + rm -f conftest.er1 conftest.err + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } +done + +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +ac_clean_files_save=$ac_clean_files +ac_clean_files="$ac_clean_files a.out a.out.dSYM a.exe b.out" +# Try to create an executable without -o first, disregard a.out. +# It will help us diagnose broken compilers, and finding out an intuition +# of exeext. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C compiler works" >&5 +$as_echo_n "checking whether the C compiler works... " >&6; } +ac_link_default=`$as_echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'` + +# The possible output files: +ac_files="a.out conftest.exe conftest a.exe a_out.exe b.out conftest.*" + +ac_rmfiles= +for ac_file in $ac_files +do + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; + * ) ac_rmfiles="$ac_rmfiles $ac_file";; + esac +done +rm -f $ac_rmfiles + +if { { ac_try="$ac_link_default" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link_default") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then : + # Autoconf-2.13 could set the ac_cv_exeext variable to `no'. +# So ignore a value of `no', otherwise this would lead to `EXEEXT = no' +# in a Makefile. We should not override ac_cv_exeext if it was cached, +# so that the user can short-circuit this test for compilers unknown to +# Autoconf. +for ac_file in $ac_files '' +do + test -f "$ac_file" || continue + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) + ;; + [ab].out ) + # We found the default executable, but exeext='' is most + # certainly right. + break;; + *.* ) + if test "${ac_cv_exeext+set}" = set && test "$ac_cv_exeext" != no; + then :; else + ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` + fi + # We set ac_cv_exeext here because the later test for it is not + # safe: cross compilers may not add the suffix if given an `-o' + # argument, so we may need to know it at that point already. + # Even if this section looks crufty: it has the advantage of + # actually working. + break;; + * ) + break;; + esac +done +test "$ac_cv_exeext" = no && ac_cv_exeext= + +else + ac_file='' +fi +if test -z "$ac_file"; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +$as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error 77 "C compiler cannot create executables +See \`config.log' for more details" "$LINENO" 5; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler default output file name" >&5 +$as_echo_n "checking for C compiler default output file name... " >&6; } +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_file" >&5 +$as_echo "$ac_file" >&6; } +ac_exeext=$ac_cv_exeext + +rm -f -r a.out a.out.dSYM a.exe conftest$ac_cv_exeext b.out +ac_clean_files=$ac_clean_files_save +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of executables" >&5 +$as_echo_n "checking for suffix of executables... " >&6; } +if { { ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then : + # If both `conftest.exe' and `conftest' are `present' (well, observable) +# catch `conftest.exe'. For instance with Cygwin, `ls conftest' will +# work properly (i.e., refer to `conftest.exe'), while it won't with +# `rm'. +for ac_file in conftest.exe conftest conftest.*; do + test -f "$ac_file" || continue + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; + *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` + break;; + * ) break;; + esac +done +else + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "cannot compute suffix of executables: cannot compile and link +See \`config.log' for more details" "$LINENO" 5; } +fi +rm -f conftest conftest$ac_cv_exeext +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_exeext" >&5 +$as_echo "$ac_cv_exeext" >&6; } + +rm -f conftest.$ac_ext +EXEEXT=$ac_cv_exeext +ac_exeext=$EXEEXT +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +int +main () +{ +FILE *f = fopen ("conftest.out", "w"); + return ferror (f) || fclose (f) != 0; + + ; + return 0; +} +_ACEOF +ac_clean_files="$ac_clean_files conftest.out" +# Check that the compiler produces executables we can run. If not, either +# the compiler is broken, or we cross compile. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are cross compiling" >&5 +$as_echo_n "checking whether we are cross compiling... " >&6; } +if test "$cross_compiling" != yes; then + { { ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } + if { ac_try='./conftest$ac_cv_exeext' + { { case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_try") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; }; then + cross_compiling=no + else + if test "$cross_compiling" = maybe; then + cross_compiling=yes + else + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "cannot run C compiled programs. +If you meant to cross compile, use \`--host'. +See \`config.log' for more details" "$LINENO" 5; } + fi + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $cross_compiling" >&5 +$as_echo "$cross_compiling" >&6; } + +rm -f conftest.$ac_ext conftest$ac_cv_exeext conftest.out +ac_clean_files=$ac_clean_files_save +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of object files" >&5 +$as_echo_n "checking for suffix of object files... " >&6; } +if ${ac_cv_objext+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.o conftest.obj +if { { ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_compile") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then : + for ac_file in conftest.o conftest.obj conftest.*; do + test -f "$ac_file" || continue; + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM ) ;; + *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'` + break;; + esac +done +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "cannot compute suffix of object files: cannot compile +See \`config.log' for more details" "$LINENO" 5; } +fi +rm -f conftest.$ac_cv_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_objext" >&5 +$as_echo "$ac_cv_objext" >&6; } +OBJEXT=$ac_cv_objext +ac_objext=$OBJEXT +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C compiler" >&5 +$as_echo_n "checking whether we are using the GNU C compiler... " >&6; } +if ${ac_cv_c_compiler_gnu+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ +#ifndef __GNUC__ + choke me +#endif + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_compiler_gnu=yes +else + ac_compiler_gnu=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +ac_cv_c_compiler_gnu=$ac_compiler_gnu + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5 +$as_echo "$ac_cv_c_compiler_gnu" >&6; } +if test $ac_compiler_gnu = yes; then + GCC=yes +else + GCC= +fi +ac_test_CFLAGS=${CFLAGS+set} +ac_save_CFLAGS=$CFLAGS +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5 +$as_echo_n "checking whether $CC accepts -g... " >&6; } +if ${ac_cv_prog_cc_g+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_save_c_werror_flag=$ac_c_werror_flag + ac_c_werror_flag=yes + ac_cv_prog_cc_g=no + CFLAGS="-g" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_prog_cc_g=yes +else + CFLAGS="" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + +else + ac_c_werror_flag=$ac_save_c_werror_flag + CFLAGS="-g" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_prog_cc_g=yes +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + ac_c_werror_flag=$ac_save_c_werror_flag +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5 +$as_echo "$ac_cv_prog_cc_g" >&6; } +if test "$ac_test_CFLAGS" = set; then + CFLAGS=$ac_save_CFLAGS +elif test $ac_cv_prog_cc_g = yes; then + if test "$GCC" = yes; then + CFLAGS="-g -O2" + else + CFLAGS="-g" + fi +else + if test "$GCC" = yes; then + CFLAGS="-O2" + else + CFLAGS= + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C89" >&5 +$as_echo_n "checking for $CC option to accept ISO C89... " >&6; } +if ${ac_cv_prog_cc_c89+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_cv_prog_cc_c89=no +ac_save_CC=$CC +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +#include +struct stat; +/* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */ +struct buf { int x; }; +FILE * (*rcsopen) (struct buf *, struct stat *, int); +static char *e (p, i) + char **p; + int i; +{ + return p[i]; +} +static char *f (char * (*g) (char **, int), char **p, ...) +{ + char *s; + va_list v; + va_start (v,p); + s = g (p, va_arg (v,int)); + va_end (v); + return s; +} + +/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has + function prototypes and stuff, but not '\xHH' hex character constants. + These don't provoke an error unfortunately, instead are silently treated + as 'x'. The following induces an error, until -std is added to get + proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an + array size at least. It's necessary to write '\x00'==0 to get something + that's true only with -std. */ +int osf4_cc_array ['\x00' == 0 ? 1 : -1]; + +/* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters + inside strings and character constants. */ +#define FOO(x) 'x' +int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1]; + +int test (int i, double x); +struct s1 {int (*f) (int a);}; +struct s2 {int (*f) (double a);}; +int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int); +int argc; +char **argv; +int +main () +{ +return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]; + ; + return 0; +} +_ACEOF +for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \ + -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" +do + CC="$ac_save_CC $ac_arg" + if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_prog_cc_c89=$ac_arg +fi +rm -f core conftest.err conftest.$ac_objext + test "x$ac_cv_prog_cc_c89" != "xno" && break +done +rm -f conftest.$ac_ext +CC=$ac_save_CC + +fi +# AC_CACHE_VAL +case "x$ac_cv_prog_cc_c89" in + x) + { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 +$as_echo "none needed" >&6; } ;; + xno) + { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 +$as_echo "unsupported" >&6; } ;; + *) + CC="$CC $ac_cv_prog_cc_c89" + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5 +$as_echo "$ac_cv_prog_cc_c89" >&6; } ;; +esac +if test "x$ac_cv_prog_cc_c89" != xno; then : + +fi + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC understands -c and -o together" >&5 +$as_echo_n "checking whether $CC understands -c and -o together... " >&6; } +if ${am_cv_prog_cc_c_o+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF + # Make sure it works both with $CC and with simple cc. + # Following AC_PROG_CC_C_O, we do the test twice because some + # compilers refuse to overwrite an existing .o file with -o, + # though they will create one. + am_cv_prog_cc_c_o=yes + for am_i in 1 2; do + if { echo "$as_me:$LINENO: $CC -c conftest.$ac_ext -o conftest2.$ac_objext" >&5 + ($CC -c conftest.$ac_ext -o conftest2.$ac_objext) >&5 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } \ + && test -f conftest2.$ac_objext; then + : OK + else + am_cv_prog_cc_c_o=no + break + fi + done + rm -f core conftest* + unset am_i +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_prog_cc_c_o" >&5 +$as_echo "$am_cv_prog_cc_c_o" >&6; } +if test "$am_cv_prog_cc_c_o" != yes; then + # Losing compiler, so override with the script. + # FIXME: It is wrong to rewrite CC. + # But if we don't then we get into trouble of one sort or another. + # A longer-term fix would be to have automake use am__CC in this case, + # and then we could set am__CC="\$(top_srcdir)/compile \$(CC)" + CC="$am_aux_dir/compile $CC" +fi +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to run the C preprocessor" >&5 +$as_echo_n "checking how to run the C preprocessor... " >&6; } +# On Suns, sometimes $CPP names a directory. +if test -n "$CPP" && test -d "$CPP"; then + CPP= +fi +if test -z "$CPP"; then + if ${ac_cv_prog_CPP+:} false; then : + $as_echo_n "(cached) " >&6 +else + # Double quotes because CPP needs to be expanded + for CPP in "$CC -E" "$CC -E -traditional-cpp" "/lib/cpp" + do + ac_preproc_ok=false +for ac_c_preproc_warn_flag in '' yes +do + # Use a header file that comes with gcc, so configuring glibc + # with a fresh cross-compiler works. + # Prefer to if __STDC__ is defined, since + # exists even on freestanding compilers. + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. "Syntax error" is here to catch this case. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#ifdef __STDC__ +# include +#else +# include +#endif + Syntax error +_ACEOF +if ac_fn_c_try_cpp "$LINENO"; then : + +else + # Broken: fails on valid input. +continue +fi +rm -f conftest.err conftest.i conftest.$ac_ext + + # OK, works on sane cases. Now check whether nonexistent headers + # can be detected and how. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +_ACEOF +if ac_fn_c_try_cpp "$LINENO"; then : + # Broken: success on invalid input. +continue +else + # Passes both tests. +ac_preproc_ok=: +break +fi +rm -f conftest.err conftest.i conftest.$ac_ext + +done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f conftest.i conftest.err conftest.$ac_ext +if $ac_preproc_ok; then : + break +fi + + done + ac_cv_prog_CPP=$CPP + +fi + CPP=$ac_cv_prog_CPP +else + ac_cv_prog_CPP=$CPP +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $CPP" >&5 +$as_echo "$CPP" >&6; } +ac_preproc_ok=false +for ac_c_preproc_warn_flag in '' yes +do + # Use a header file that comes with gcc, so configuring glibc + # with a fresh cross-compiler works. + # Prefer to if __STDC__ is defined, since + # exists even on freestanding compilers. + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. "Syntax error" is here to catch this case. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#ifdef __STDC__ +# include +#else +# include +#endif + Syntax error +_ACEOF +if ac_fn_c_try_cpp "$LINENO"; then : + +else + # Broken: fails on valid input. +continue +fi +rm -f conftest.err conftest.i conftest.$ac_ext + + # OK, works on sane cases. Now check whether nonexistent headers + # can be detected and how. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +_ACEOF +if ac_fn_c_try_cpp "$LINENO"; then : + # Broken: success on invalid input. +continue +else + # Passes both tests. +ac_preproc_ok=: +break +fi +rm -f conftest.err conftest.i conftest.$ac_ext + +done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f conftest.i conftest.err conftest.$ac_ext +if $ac_preproc_ok; then : + +else + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "C preprocessor \"$CPP\" fails sanity check +See \`config.log' for more details" "$LINENO" 5; } +fi + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for grep that handles long lines and -e" >&5 +$as_echo_n "checking for grep that handles long lines and -e... " >&6; } +if ${ac_cv_path_GREP+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -z "$GREP"; then + ac_path_GREP_found=false + # Loop through the user's path and test for each of PROGNAME-LIST + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_prog in grep ggrep; do + for ac_exec_ext in '' $ac_executable_extensions; do + ac_path_GREP="$as_dir/$ac_prog$ac_exec_ext" + as_fn_executable_p "$ac_path_GREP" || continue +# Check for GNU ac_path_GREP and select it if it is found. + # Check for GNU $ac_path_GREP +case `"$ac_path_GREP" --version 2>&1` in +*GNU*) + ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_found=:;; +*) + ac_count=0 + $as_echo_n 0123456789 >"conftest.in" + while : + do + cat "conftest.in" "conftest.in" >"conftest.tmp" + mv "conftest.tmp" "conftest.in" + cp "conftest.in" "conftest.nl" + $as_echo 'GREP' >> "conftest.nl" + "$ac_path_GREP" -e 'GREP$' -e '-(cannot match)-' < "conftest.nl" >"conftest.out" 2>/dev/null || break + diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break + as_fn_arith $ac_count + 1 && ac_count=$as_val + if test $ac_count -gt ${ac_path_GREP_max-0}; then + # Best one so far, save it but keep looking for a better one + ac_cv_path_GREP="$ac_path_GREP" + ac_path_GREP_max=$ac_count + fi + # 10*(2^10) chars as input seems more than enough + test $ac_count -gt 10 && break + done + rm -f conftest.in conftest.tmp conftest.nl conftest.out;; +esac + + $ac_path_GREP_found && break 3 + done + done + done +IFS=$as_save_IFS + if test -z "$ac_cv_path_GREP"; then + as_fn_error $? "no acceptable grep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 + fi +else + ac_cv_path_GREP=$GREP +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_GREP" >&5 +$as_echo "$ac_cv_path_GREP" >&6; } + GREP="$ac_cv_path_GREP" + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for egrep" >&5 +$as_echo_n "checking for egrep... " >&6; } +if ${ac_cv_path_EGREP+:} false; then : + $as_echo_n "(cached) " >&6 +else + if echo a | $GREP -E '(a|b)' >/dev/null 2>&1 + then ac_cv_path_EGREP="$GREP -E" + else + if test -z "$EGREP"; then + ac_path_EGREP_found=false + # Loop through the user's path and test for each of PROGNAME-LIST + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_prog in egrep; do + for ac_exec_ext in '' $ac_executable_extensions; do + ac_path_EGREP="$as_dir/$ac_prog$ac_exec_ext" + as_fn_executable_p "$ac_path_EGREP" || continue +# Check for GNU ac_path_EGREP and select it if it is found. + # Check for GNU $ac_path_EGREP +case `"$ac_path_EGREP" --version 2>&1` in +*GNU*) + ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_found=:;; +*) + ac_count=0 + $as_echo_n 0123456789 >"conftest.in" + while : + do + cat "conftest.in" "conftest.in" >"conftest.tmp" + mv "conftest.tmp" "conftest.in" + cp "conftest.in" "conftest.nl" + $as_echo 'EGREP' >> "conftest.nl" + "$ac_path_EGREP" 'EGREP$' < "conftest.nl" >"conftest.out" 2>/dev/null || break + diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break + as_fn_arith $ac_count + 1 && ac_count=$as_val + if test $ac_count -gt ${ac_path_EGREP_max-0}; then + # Best one so far, save it but keep looking for a better one + ac_cv_path_EGREP="$ac_path_EGREP" + ac_path_EGREP_max=$ac_count + fi + # 10*(2^10) chars as input seems more than enough + test $ac_count -gt 10 && break + done + rm -f conftest.in conftest.tmp conftest.nl conftest.out;; +esac + + $ac_path_EGREP_found && break 3 + done + done + done +IFS=$as_save_IFS + if test -z "$ac_cv_path_EGREP"; then + as_fn_error $? "no acceptable egrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 + fi +else + ac_cv_path_EGREP=$EGREP +fi + + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_EGREP" >&5 +$as_echo "$ac_cv_path_EGREP" >&6; } + EGREP="$ac_cv_path_EGREP" + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for ANSI C header files" >&5 +$as_echo_n "checking for ANSI C header files... " >&6; } +if ${ac_cv_header_stdc+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +#include +#include +#include + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_header_stdc=yes +else + ac_cv_header_stdc=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + +if test $ac_cv_header_stdc = yes; then + # SunOS 4.x string.h does not declare mem*, contrary to ANSI. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "memchr" >/dev/null 2>&1; then : + +else + ac_cv_header_stdc=no +fi +rm -f conftest* + +fi + +if test $ac_cv_header_stdc = yes; then + # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "free" >/dev/null 2>&1; then : + +else + ac_cv_header_stdc=no +fi +rm -f conftest* + +fi + +if test $ac_cv_header_stdc = yes; then + # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi. + if test "$cross_compiling" = yes; then : + : +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +#include +#if ((' ' & 0x0FF) == 0x020) +# define ISLOWER(c) ('a' <= (c) && (c) <= 'z') +# define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c)) +#else +# define ISLOWER(c) \ + (('a' <= (c) && (c) <= 'i') \ + || ('j' <= (c) && (c) <= 'r') \ + || ('s' <= (c) && (c) <= 'z')) +# define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c)) +#endif + +#define XOR(e, f) (((e) && !(f)) || (!(e) && (f))) +int +main () +{ + int i; + for (i = 0; i < 256; i++) + if (XOR (islower (i), ISLOWER (i)) + || toupper (i) != TOUPPER (i)) + return 2; + return 0; +} +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + +else + ac_cv_header_stdc=no +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +fi + +fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_stdc" >&5 +$as_echo "$ac_cv_header_stdc" >&6; } +if test $ac_cv_header_stdc = yes; then + +$as_echo "#define STDC_HEADERS 1" >>confdefs.h + +fi + +# On IRIX 5.3, sys/types and inttypes.h are conflicting. +for ac_header in sys/types.h sys/stat.h stdlib.h string.h memory.h strings.h \ + inttypes.h stdint.h unistd.h +do : + as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` +ac_fn_c_check_header_compile "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default +" +if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : + cat >>confdefs.h <<_ACEOF +#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + + + + ac_fn_c_check_header_mongrel "$LINENO" "minix/config.h" "ac_cv_header_minix_config_h" "$ac_includes_default" +if test "x$ac_cv_header_minix_config_h" = xyes; then : + MINIX=yes +else + MINIX= +fi + + + if test "$MINIX" = yes; then + +$as_echo "#define _POSIX_SOURCE 1" >>confdefs.h + + +$as_echo "#define _POSIX_1_SOURCE 2" >>confdefs.h + + +$as_echo "#define _MINIX 1" >>confdefs.h + + fi + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether it is safe to define __EXTENSIONS__" >&5 +$as_echo_n "checking whether it is safe to define __EXTENSIONS__... " >&6; } +if ${ac_cv_safe_to_define___extensions__+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +# define __EXTENSIONS__ 1 + $ac_includes_default +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_safe_to_define___extensions__=yes +else + ac_cv_safe_to_define___extensions__=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_safe_to_define___extensions__" >&5 +$as_echo "$ac_cv_safe_to_define___extensions__" >&6; } + test $ac_cv_safe_to_define___extensions__ = yes && + $as_echo "#define __EXTENSIONS__ 1" >>confdefs.h + + $as_echo "#define _ALL_SOURCE 1" >>confdefs.h + + $as_echo "#define _GNU_SOURCE 1" >>confdefs.h + + $as_echo "#define _POSIX_PTHREAD_SEMANTICS 1" >>confdefs.h + + $as_echo "#define _TANDEM_SOURCE 1" >>confdefs.h + + + + +case `pwd` in + *\ * | *\ *) + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Libtool does not cope well with whitespace in \`pwd\`" >&5 +$as_echo "$as_me: WARNING: Libtool does not cope well with whitespace in \`pwd\`" >&2;} ;; +esac + + + +macro_version='2.4.2' +macro_revision='1.3337' + + + + + + + + + + + + + +ltmain="$ac_aux_dir/ltmain.sh" + +# Make sure we can run config.sub. +$SHELL "$ac_aux_dir/config.sub" sun4 >/dev/null 2>&1 || + as_fn_error $? "cannot run $SHELL $ac_aux_dir/config.sub" "$LINENO" 5 + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking build system type" >&5 +$as_echo_n "checking build system type... " >&6; } +if ${ac_cv_build+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_build_alias=$build_alias +test "x$ac_build_alias" = x && + ac_build_alias=`$SHELL "$ac_aux_dir/config.guess"` +test "x$ac_build_alias" = x && + as_fn_error $? "cannot guess build type; you must specify one" "$LINENO" 5 +ac_cv_build=`$SHELL "$ac_aux_dir/config.sub" $ac_build_alias` || + as_fn_error $? "$SHELL $ac_aux_dir/config.sub $ac_build_alias failed" "$LINENO" 5 + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_build" >&5 +$as_echo "$ac_cv_build" >&6; } +case $ac_cv_build in +*-*-*) ;; +*) as_fn_error $? "invalid value of canonical build" "$LINENO" 5;; +esac +build=$ac_cv_build +ac_save_IFS=$IFS; IFS='-' +set x $ac_cv_build +shift +build_cpu=$1 +build_vendor=$2 +shift; shift +# Remember, the first character of IFS is used to create $*, +# except with old shells: +build_os=$* +IFS=$ac_save_IFS +case $build_os in *\ *) build_os=`echo "$build_os" | sed 's/ /-/g'`;; esac + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking host system type" >&5 +$as_echo_n "checking host system type... " >&6; } +if ${ac_cv_host+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test "x$host_alias" = x; then + ac_cv_host=$ac_cv_build +else + ac_cv_host=`$SHELL "$ac_aux_dir/config.sub" $host_alias` || + as_fn_error $? "$SHELL $ac_aux_dir/config.sub $host_alias failed" "$LINENO" 5 +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_host" >&5 +$as_echo "$ac_cv_host" >&6; } +case $ac_cv_host in +*-*-*) ;; +*) as_fn_error $? "invalid value of canonical host" "$LINENO" 5;; +esac +host=$ac_cv_host +ac_save_IFS=$IFS; IFS='-' +set x $ac_cv_host +shift +host_cpu=$1 +host_vendor=$2 +shift; shift +# Remember, the first character of IFS is used to create $*, +# except with old shells: +host_os=$* +IFS=$ac_save_IFS +case $host_os in *\ *) host_os=`echo "$host_os" | sed 's/ /-/g'`;; esac + + +# Backslashify metacharacters that are still active within +# double-quoted strings. +sed_quote_subst='s/\(["`$\\]\)/\\\1/g' + +# Same as above, but do not quote variable references. +double_quote_subst='s/\(["`\\]\)/\\\1/g' + +# Sed substitution to delay expansion of an escaped shell variable in a +# double_quote_subst'ed string. +delay_variable_subst='s/\\\\\\\\\\\$/\\\\\\$/g' + +# Sed substitution to delay expansion of an escaped single quote. +delay_single_quote_subst='s/'\''/'\'\\\\\\\'\''/g' + +# Sed substitution to avoid accidental globbing in evaled expressions +no_glob_subst='s/\*/\\\*/g' + +ECHO='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' +ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO +ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO$ECHO + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to print strings" >&5 +$as_echo_n "checking how to print strings... " >&6; } +# Test print first, because it will be a builtin if present. +if test "X`( print -r -- -n ) 2>/dev/null`" = X-n && \ + test "X`print -r -- $ECHO 2>/dev/null`" = "X$ECHO"; then + ECHO='print -r --' +elif test "X`printf %s $ECHO 2>/dev/null`" = "X$ECHO"; then + ECHO='printf %s\n' +else + # Use this function as a fallback that always works. + func_fallback_echo () + { + eval 'cat <<_LTECHO_EOF +$1 +_LTECHO_EOF' + } + ECHO='func_fallback_echo' +fi + +# func_echo_all arg... +# Invoke $ECHO with all args, space-separated. +func_echo_all () +{ + $ECHO "" +} + +case "$ECHO" in + printf*) { $as_echo "$as_me:${as_lineno-$LINENO}: result: printf" >&5 +$as_echo "printf" >&6; } ;; + print*) { $as_echo "$as_me:${as_lineno-$LINENO}: result: print -r" >&5 +$as_echo "print -r" >&6; } ;; + *) { $as_echo "$as_me:${as_lineno-$LINENO}: result: cat" >&5 +$as_echo "cat" >&6; } ;; +esac + + + + + + + + + + + + + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for a sed that does not truncate output" >&5 +$as_echo_n "checking for a sed that does not truncate output... " >&6; } +if ${ac_cv_path_SED+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_script=s/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb/ + for ac_i in 1 2 3 4 5 6 7; do + ac_script="$ac_script$as_nl$ac_script" + done + echo "$ac_script" 2>/dev/null | sed 99q >conftest.sed + { ac_script=; unset ac_script;} + if test -z "$SED"; then + ac_path_SED_found=false + # Loop through the user's path and test for each of PROGNAME-LIST + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_prog in sed gsed; do + for ac_exec_ext in '' $ac_executable_extensions; do + ac_path_SED="$as_dir/$ac_prog$ac_exec_ext" + as_fn_executable_p "$ac_path_SED" || continue +# Check for GNU ac_path_SED and select it if it is found. + # Check for GNU $ac_path_SED +case `"$ac_path_SED" --version 2>&1` in +*GNU*) + ac_cv_path_SED="$ac_path_SED" ac_path_SED_found=:;; +*) + ac_count=0 + $as_echo_n 0123456789 >"conftest.in" + while : + do + cat "conftest.in" "conftest.in" >"conftest.tmp" + mv "conftest.tmp" "conftest.in" + cp "conftest.in" "conftest.nl" + $as_echo '' >> "conftest.nl" + "$ac_path_SED" -f conftest.sed < "conftest.nl" >"conftest.out" 2>/dev/null || break + diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break + as_fn_arith $ac_count + 1 && ac_count=$as_val + if test $ac_count -gt ${ac_path_SED_max-0}; then + # Best one so far, save it but keep looking for a better one + ac_cv_path_SED="$ac_path_SED" + ac_path_SED_max=$ac_count + fi + # 10*(2^10) chars as input seems more than enough + test $ac_count -gt 10 && break + done + rm -f conftest.in conftest.tmp conftest.nl conftest.out;; +esac + + $ac_path_SED_found && break 3 + done + done + done +IFS=$as_save_IFS + if test -z "$ac_cv_path_SED"; then + as_fn_error $? "no acceptable sed could be found in \$PATH" "$LINENO" 5 + fi +else + ac_cv_path_SED=$SED +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_SED" >&5 +$as_echo "$ac_cv_path_SED" >&6; } + SED="$ac_cv_path_SED" + rm -f conftest.sed + +test -z "$SED" && SED=sed +Xsed="$SED -e 1s/^X//" + + + + + + + + + + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for fgrep" >&5 +$as_echo_n "checking for fgrep... " >&6; } +if ${ac_cv_path_FGREP+:} false; then : + $as_echo_n "(cached) " >&6 +else + if echo 'ab*c' | $GREP -F 'ab*c' >/dev/null 2>&1 + then ac_cv_path_FGREP="$GREP -F" + else + if test -z "$FGREP"; then + ac_path_FGREP_found=false + # Loop through the user's path and test for each of PROGNAME-LIST + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_prog in fgrep; do + for ac_exec_ext in '' $ac_executable_extensions; do + ac_path_FGREP="$as_dir/$ac_prog$ac_exec_ext" + as_fn_executable_p "$ac_path_FGREP" || continue +# Check for GNU ac_path_FGREP and select it if it is found. + # Check for GNU $ac_path_FGREP +case `"$ac_path_FGREP" --version 2>&1` in +*GNU*) + ac_cv_path_FGREP="$ac_path_FGREP" ac_path_FGREP_found=:;; +*) + ac_count=0 + $as_echo_n 0123456789 >"conftest.in" + while : + do + cat "conftest.in" "conftest.in" >"conftest.tmp" + mv "conftest.tmp" "conftest.in" + cp "conftest.in" "conftest.nl" + $as_echo 'FGREP' >> "conftest.nl" + "$ac_path_FGREP" FGREP < "conftest.nl" >"conftest.out" 2>/dev/null || break + diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break + as_fn_arith $ac_count + 1 && ac_count=$as_val + if test $ac_count -gt ${ac_path_FGREP_max-0}; then + # Best one so far, save it but keep looking for a better one + ac_cv_path_FGREP="$ac_path_FGREP" + ac_path_FGREP_max=$ac_count + fi + # 10*(2^10) chars as input seems more than enough + test $ac_count -gt 10 && break + done + rm -f conftest.in conftest.tmp conftest.nl conftest.out;; +esac + + $ac_path_FGREP_found && break 3 + done + done + done +IFS=$as_save_IFS + if test -z "$ac_cv_path_FGREP"; then + as_fn_error $? "no acceptable fgrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 + fi +else + ac_cv_path_FGREP=$FGREP +fi + + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_FGREP" >&5 +$as_echo "$ac_cv_path_FGREP" >&6; } + FGREP="$ac_cv_path_FGREP" + + +test -z "$GREP" && GREP=grep + + + + + + + + + + + + + + + + + + + +# Check whether --with-gnu-ld was given. +if test "${with_gnu_ld+set}" = set; then : + withval=$with_gnu_ld; test "$withval" = no || with_gnu_ld=yes +else + with_gnu_ld=no +fi + +ac_prog=ld +if test "$GCC" = yes; then + # Check if gcc -print-prog-name=ld gives a path. + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ld used by $CC" >&5 +$as_echo_n "checking for ld used by $CC... " >&6; } + case $host in + *-*-mingw*) + # gcc leaves a trailing carriage return which upsets mingw + ac_prog=`($CC -print-prog-name=ld) 2>&5 | tr -d '\015'` ;; + *) + ac_prog=`($CC -print-prog-name=ld) 2>&5` ;; + esac + case $ac_prog in + # Accept absolute paths. + [\\/]* | ?:[\\/]*) + re_direlt='/[^/][^/]*/\.\./' + # Canonicalize the pathname of ld + ac_prog=`$ECHO "$ac_prog"| $SED 's%\\\\%/%g'` + while $ECHO "$ac_prog" | $GREP "$re_direlt" > /dev/null 2>&1; do + ac_prog=`$ECHO $ac_prog| $SED "s%$re_direlt%/%"` + done + test -z "$LD" && LD="$ac_prog" + ;; + "") + # If it fails, then pretend we aren't using GCC. + ac_prog=ld + ;; + *) + # If it is relative, then search for the first ld in PATH. + with_gnu_ld=unknown + ;; + esac +elif test "$with_gnu_ld" = yes; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for GNU ld" >&5 +$as_echo_n "checking for GNU ld... " >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for non-GNU ld" >&5 +$as_echo_n "checking for non-GNU ld... " >&6; } +fi +if ${lt_cv_path_LD+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -z "$LD"; then + lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR + for ac_dir in $PATH; do + IFS="$lt_save_ifs" + test -z "$ac_dir" && ac_dir=. + if test -f "$ac_dir/$ac_prog" || test -f "$ac_dir/$ac_prog$ac_exeext"; then + lt_cv_path_LD="$ac_dir/$ac_prog" + # Check to see if the program is GNU ld. I'd rather use --version, + # but apparently some variants of GNU ld only accept -v. + # Break only if it was the GNU/non-GNU ld that we prefer. + case `"$lt_cv_path_LD" -v 2>&1 &5 +$as_echo "$LD" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi +test -z "$LD" && as_fn_error $? "no acceptable ld found in \$PATH" "$LINENO" 5 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if the linker ($LD) is GNU ld" >&5 +$as_echo_n "checking if the linker ($LD) is GNU ld... " >&6; } +if ${lt_cv_prog_gnu_ld+:} false; then : + $as_echo_n "(cached) " >&6 +else + # I'd rather use --version here, but apparently some GNU lds only accept -v. +case `$LD -v 2>&1 &5 +$as_echo "$lt_cv_prog_gnu_ld" >&6; } +with_gnu_ld=$lt_cv_prog_gnu_ld + + + + + + + + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for BSD- or MS-compatible name lister (nm)" >&5 +$as_echo_n "checking for BSD- or MS-compatible name lister (nm)... " >&6; } +if ${lt_cv_path_NM+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$NM"; then + # Let the user override the test. + lt_cv_path_NM="$NM" +else + lt_nm_to_check="${ac_tool_prefix}nm" + if test -n "$ac_tool_prefix" && test "$build" = "$host"; then + lt_nm_to_check="$lt_nm_to_check nm" + fi + for lt_tmp_nm in $lt_nm_to_check; do + lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR + for ac_dir in $PATH /usr/ccs/bin/elf /usr/ccs/bin /usr/ucb /bin; do + IFS="$lt_save_ifs" + test -z "$ac_dir" && ac_dir=. + tmp_nm="$ac_dir/$lt_tmp_nm" + if test -f "$tmp_nm" || test -f "$tmp_nm$ac_exeext" ; then + # Check to see if the nm accepts a BSD-compat flag. + # Adding the `sed 1q' prevents false positives on HP-UX, which says: + # nm: unknown option "B" ignored + # Tru64's nm complains that /dev/null is an invalid object file + case `"$tmp_nm" -B /dev/null 2>&1 | sed '1q'` in + */dev/null* | *'Invalid file or object type'*) + lt_cv_path_NM="$tmp_nm -B" + break + ;; + *) + case `"$tmp_nm" -p /dev/null 2>&1 | sed '1q'` in + */dev/null*) + lt_cv_path_NM="$tmp_nm -p" + break + ;; + *) + lt_cv_path_NM=${lt_cv_path_NM="$tmp_nm"} # keep the first match, but + continue # so that we can try to find one that supports BSD flags + ;; + esac + ;; + esac + fi + done + IFS="$lt_save_ifs" + done + : ${lt_cv_path_NM=no} +fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_path_NM" >&5 +$as_echo "$lt_cv_path_NM" >&6; } +if test "$lt_cv_path_NM" != "no"; then + NM="$lt_cv_path_NM" +else + # Didn't find any BSD compatible name lister, look for dumpbin. + if test -n "$DUMPBIN"; then : + # Let the user override the test. + else + if test -n "$ac_tool_prefix"; then + for ac_prog in dumpbin "link -dump" + do + # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. +set dummy $ac_tool_prefix$ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_DUMPBIN+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$DUMPBIN"; then + ac_cv_prog_DUMPBIN="$DUMPBIN" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_DUMPBIN="$ac_tool_prefix$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +DUMPBIN=$ac_cv_prog_DUMPBIN +if test -n "$DUMPBIN"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $DUMPBIN" >&5 +$as_echo "$DUMPBIN" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$DUMPBIN" && break + done +fi +if test -z "$DUMPBIN"; then + ac_ct_DUMPBIN=$DUMPBIN + for ac_prog in dumpbin "link -dump" +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_DUMPBIN+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_DUMPBIN"; then + ac_cv_prog_ac_ct_DUMPBIN="$ac_ct_DUMPBIN" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_DUMPBIN="$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_DUMPBIN=$ac_cv_prog_ac_ct_DUMPBIN +if test -n "$ac_ct_DUMPBIN"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_DUMPBIN" >&5 +$as_echo "$ac_ct_DUMPBIN" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$ac_ct_DUMPBIN" && break +done + + if test "x$ac_ct_DUMPBIN" = x; then + DUMPBIN=":" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + DUMPBIN=$ac_ct_DUMPBIN + fi +fi + + case `$DUMPBIN -symbols /dev/null 2>&1 | sed '1q'` in + *COFF*) + DUMPBIN="$DUMPBIN -symbols" + ;; + *) + DUMPBIN=: + ;; + esac + fi + + if test "$DUMPBIN" != ":"; then + NM="$DUMPBIN" + fi +fi +test -z "$NM" && NM=nm + + + + + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking the name lister ($NM) interface" >&5 +$as_echo_n "checking the name lister ($NM) interface... " >&6; } +if ${lt_cv_nm_interface+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_nm_interface="BSD nm" + echo "int some_variable = 0;" > conftest.$ac_ext + (eval echo "\"\$as_me:$LINENO: $ac_compile\"" >&5) + (eval "$ac_compile" 2>conftest.err) + cat conftest.err >&5 + (eval echo "\"\$as_me:$LINENO: $NM \\\"conftest.$ac_objext\\\"\"" >&5) + (eval "$NM \"conftest.$ac_objext\"" 2>conftest.err > conftest.out) + cat conftest.err >&5 + (eval echo "\"\$as_me:$LINENO: output\"" >&5) + cat conftest.out >&5 + if $GREP 'External.*some_variable' conftest.out > /dev/null; then + lt_cv_nm_interface="MS dumpbin" + fi + rm -f conftest* +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_nm_interface" >&5 +$as_echo "$lt_cv_nm_interface" >&6; } + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ln -s works" >&5 +$as_echo_n "checking whether ln -s works... " >&6; } +LN_S=$as_ln_s +if test "$LN_S" = "ln -s"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no, using $LN_S" >&5 +$as_echo "no, using $LN_S" >&6; } +fi + +# find the maximum length of command line arguments +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking the maximum length of command line arguments" >&5 +$as_echo_n "checking the maximum length of command line arguments... " >&6; } +if ${lt_cv_sys_max_cmd_len+:} false; then : + $as_echo_n "(cached) " >&6 +else + i=0 + teststring="ABCD" + + case $build_os in + msdosdjgpp*) + # On DJGPP, this test can blow up pretty badly due to problems in libc + # (any single argument exceeding 2000 bytes causes a buffer overrun + # during glob expansion). Even if it were fixed, the result of this + # check would be larger than it should be. + lt_cv_sys_max_cmd_len=12288; # 12K is about right + ;; + + gnu*) + # Under GNU Hurd, this test is not required because there is + # no limit to the length of command line arguments. + # Libtool will interpret -1 as no limit whatsoever + lt_cv_sys_max_cmd_len=-1; + ;; + + cygwin* | mingw* | cegcc*) + # On Win9x/ME, this test blows up -- it succeeds, but takes + # about 5 minutes as the teststring grows exponentially. + # Worse, since 9x/ME are not pre-emptively multitasking, + # you end up with a "frozen" computer, even though with patience + # the test eventually succeeds (with a max line length of 256k). + # Instead, let's just punt: use the minimum linelength reported by + # all of the supported platforms: 8192 (on NT/2K/XP). + lt_cv_sys_max_cmd_len=8192; + ;; + + mint*) + # On MiNT this can take a long time and run out of memory. + lt_cv_sys_max_cmd_len=8192; + ;; + + amigaos*) + # On AmigaOS with pdksh, this test takes hours, literally. + # So we just punt and use a minimum line length of 8192. + lt_cv_sys_max_cmd_len=8192; + ;; + + netbsd* | freebsd* | openbsd* | darwin* | dragonfly*) + # This has been around since 386BSD, at least. Likely further. + if test -x /sbin/sysctl; then + lt_cv_sys_max_cmd_len=`/sbin/sysctl -n kern.argmax` + elif test -x /usr/sbin/sysctl; then + lt_cv_sys_max_cmd_len=`/usr/sbin/sysctl -n kern.argmax` + else + lt_cv_sys_max_cmd_len=65536 # usable default for all BSDs + fi + # And add a safety zone + lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4` + lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3` + ;; + + interix*) + # We know the value 262144 and hardcode it with a safety zone (like BSD) + lt_cv_sys_max_cmd_len=196608 + ;; + + os2*) + # The test takes a long time on OS/2. + lt_cv_sys_max_cmd_len=8192 + ;; + + osf*) + # Dr. Hans Ekkehard Plesser reports seeing a kernel panic running configure + # due to this test when exec_disable_arg_limit is 1 on Tru64. It is not + # nice to cause kernel panics so lets avoid the loop below. + # First set a reasonable default. + lt_cv_sys_max_cmd_len=16384 + # + if test -x /sbin/sysconfig; then + case `/sbin/sysconfig -q proc exec_disable_arg_limit` in + *1*) lt_cv_sys_max_cmd_len=-1 ;; + esac + fi + ;; + sco3.2v5*) + lt_cv_sys_max_cmd_len=102400 + ;; + sysv5* | sco5v6* | sysv4.2uw2*) + kargmax=`grep ARG_MAX /etc/conf/cf.d/stune 2>/dev/null` + if test -n "$kargmax"; then + lt_cv_sys_max_cmd_len=`echo $kargmax | sed 's/.*[ ]//'` + else + lt_cv_sys_max_cmd_len=32768 + fi + ;; + *) + lt_cv_sys_max_cmd_len=`(getconf ARG_MAX) 2> /dev/null` + if test -n "$lt_cv_sys_max_cmd_len" && \ + test undefined != "$lt_cv_sys_max_cmd_len"; then + lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4` + lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3` + else + # Make teststring a little bigger before we do anything with it. + # a 1K string should be a reasonable start. + for i in 1 2 3 4 5 6 7 8 ; do + teststring=$teststring$teststring + done + SHELL=${SHELL-${CONFIG_SHELL-/bin/sh}} + # If test is not a shell built-in, we'll probably end up computing a + # maximum length that is only half of the actual maximum length, but + # we can't tell. + while { test "X"`env echo "$teststring$teststring" 2>/dev/null` \ + = "X$teststring$teststring"; } >/dev/null 2>&1 && + test $i != 17 # 1/2 MB should be enough + do + i=`expr $i + 1` + teststring=$teststring$teststring + done + # Only check the string length outside the loop. + lt_cv_sys_max_cmd_len=`expr "X$teststring" : ".*" 2>&1` + teststring= + # Add a significant safety factor because C++ compilers can tack on + # massive amounts of additional arguments before passing them to the + # linker. It appears as though 1/2 is a usable value. + lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 2` + fi + ;; + esac + +fi + +if test -n $lt_cv_sys_max_cmd_len ; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_sys_max_cmd_len" >&5 +$as_echo "$lt_cv_sys_max_cmd_len" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: none" >&5 +$as_echo "none" >&6; } +fi +max_cmd_len=$lt_cv_sys_max_cmd_len + + + + + + +: ${CP="cp -f"} +: ${MV="mv -f"} +: ${RM="rm -f"} + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the shell understands some XSI constructs" >&5 +$as_echo_n "checking whether the shell understands some XSI constructs... " >&6; } +# Try some XSI features +xsi_shell=no +( _lt_dummy="a/b/c" + test "${_lt_dummy##*/},${_lt_dummy%/*},${_lt_dummy#??}"${_lt_dummy%"$_lt_dummy"}, \ + = c,a/b,b/c, \ + && eval 'test $(( 1 + 1 )) -eq 2 \ + && test "${#_lt_dummy}" -eq 5' ) >/dev/null 2>&1 \ + && xsi_shell=yes +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $xsi_shell" >&5 +$as_echo "$xsi_shell" >&6; } + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the shell understands \"+=\"" >&5 +$as_echo_n "checking whether the shell understands \"+=\"... " >&6; } +lt_shell_append=no +( foo=bar; set foo baz; eval "$1+=\$2" && test "$foo" = barbaz ) \ + >/dev/null 2>&1 \ + && lt_shell_append=yes +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_shell_append" >&5 +$as_echo "$lt_shell_append" >&6; } + + +if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then + lt_unset=unset +else + lt_unset=false +fi + + + + + +# test EBCDIC or ASCII +case `echo X|tr X '\101'` in + A) # ASCII based system + # \n is not interpreted correctly by Solaris 8 /usr/ucb/tr + lt_SP2NL='tr \040 \012' + lt_NL2SP='tr \015\012 \040\040' + ;; + *) # EBCDIC based system + lt_SP2NL='tr \100 \n' + lt_NL2SP='tr \r\n \100\100' + ;; +esac + + + + + + + + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to convert $build file names to $host format" >&5 +$as_echo_n "checking how to convert $build file names to $host format... " >&6; } +if ${lt_cv_to_host_file_cmd+:} false; then : + $as_echo_n "(cached) " >&6 +else + case $host in + *-*-mingw* ) + case $build in + *-*-mingw* ) # actually msys + lt_cv_to_host_file_cmd=func_convert_file_msys_to_w32 + ;; + *-*-cygwin* ) + lt_cv_to_host_file_cmd=func_convert_file_cygwin_to_w32 + ;; + * ) # otherwise, assume *nix + lt_cv_to_host_file_cmd=func_convert_file_nix_to_w32 + ;; + esac + ;; + *-*-cygwin* ) + case $build in + *-*-mingw* ) # actually msys + lt_cv_to_host_file_cmd=func_convert_file_msys_to_cygwin + ;; + *-*-cygwin* ) + lt_cv_to_host_file_cmd=func_convert_file_noop + ;; + * ) # otherwise, assume *nix + lt_cv_to_host_file_cmd=func_convert_file_nix_to_cygwin + ;; + esac + ;; + * ) # unhandled hosts (and "normal" native builds) + lt_cv_to_host_file_cmd=func_convert_file_noop + ;; +esac + +fi + +to_host_file_cmd=$lt_cv_to_host_file_cmd +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_to_host_file_cmd" >&5 +$as_echo "$lt_cv_to_host_file_cmd" >&6; } + + + + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to convert $build file names to toolchain format" >&5 +$as_echo_n "checking how to convert $build file names to toolchain format... " >&6; } +if ${lt_cv_to_tool_file_cmd+:} false; then : + $as_echo_n "(cached) " >&6 +else + #assume ordinary cross tools, or native build. +lt_cv_to_tool_file_cmd=func_convert_file_noop +case $host in + *-*-mingw* ) + case $build in + *-*-mingw* ) # actually msys + lt_cv_to_tool_file_cmd=func_convert_file_msys_to_w32 + ;; + esac + ;; +esac + +fi + +to_tool_file_cmd=$lt_cv_to_tool_file_cmd +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_to_tool_file_cmd" >&5 +$as_echo "$lt_cv_to_tool_file_cmd" >&6; } + + + + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $LD option to reload object files" >&5 +$as_echo_n "checking for $LD option to reload object files... " >&6; } +if ${lt_cv_ld_reload_flag+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_ld_reload_flag='-r' +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_ld_reload_flag" >&5 +$as_echo "$lt_cv_ld_reload_flag" >&6; } +reload_flag=$lt_cv_ld_reload_flag +case $reload_flag in +"" | " "*) ;; +*) reload_flag=" $reload_flag" ;; +esac +reload_cmds='$LD$reload_flag -o $output$reload_objs' +case $host_os in + cygwin* | mingw* | pw32* | cegcc*) + if test "$GCC" != yes; then + reload_cmds=false + fi + ;; + darwin*) + if test "$GCC" = yes; then + reload_cmds='$LTCC $LTCFLAGS -nostdlib ${wl}-r -o $output$reload_objs' + else + reload_cmds='$LD$reload_flag -o $output$reload_objs' + fi + ;; +esac + + + + + + + + + +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}objdump", so it can be a program name with args. +set dummy ${ac_tool_prefix}objdump; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_OBJDUMP+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$OBJDUMP"; then + ac_cv_prog_OBJDUMP="$OBJDUMP" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_OBJDUMP="${ac_tool_prefix}objdump" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +OBJDUMP=$ac_cv_prog_OBJDUMP +if test -n "$OBJDUMP"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $OBJDUMP" >&5 +$as_echo "$OBJDUMP" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_OBJDUMP"; then + ac_ct_OBJDUMP=$OBJDUMP + # Extract the first word of "objdump", so it can be a program name with args. +set dummy objdump; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_OBJDUMP+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_OBJDUMP"; then + ac_cv_prog_ac_ct_OBJDUMP="$ac_ct_OBJDUMP" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_OBJDUMP="objdump" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_OBJDUMP=$ac_cv_prog_ac_ct_OBJDUMP +if test -n "$ac_ct_OBJDUMP"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_OBJDUMP" >&5 +$as_echo "$ac_ct_OBJDUMP" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_OBJDUMP" = x; then + OBJDUMP="false" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + OBJDUMP=$ac_ct_OBJDUMP + fi +else + OBJDUMP="$ac_cv_prog_OBJDUMP" +fi + +test -z "$OBJDUMP" && OBJDUMP=objdump + + + + + + + + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to recognize dependent libraries" >&5 +$as_echo_n "checking how to recognize dependent libraries... " >&6; } +if ${lt_cv_deplibs_check_method+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_file_magic_cmd='$MAGIC_CMD' +lt_cv_file_magic_test_file= +lt_cv_deplibs_check_method='unknown' +# Need to set the preceding variable on all platforms that support +# interlibrary dependencies. +# 'none' -- dependencies not supported. +# `unknown' -- same as none, but documents that we really don't know. +# 'pass_all' -- all dependencies passed with no checks. +# 'test_compile' -- check by making test program. +# 'file_magic [[regex]]' -- check by looking for files in library path +# which responds to the $file_magic_cmd with a given extended regex. +# If you have `file' or equivalent on your system and you're not sure +# whether `pass_all' will *always* work, you probably want this one. + +case $host_os in +aix[4-9]*) + lt_cv_deplibs_check_method=pass_all + ;; + +beos*) + lt_cv_deplibs_check_method=pass_all + ;; + +bsdi[45]*) + lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [ML]SB (shared object|dynamic lib)' + lt_cv_file_magic_cmd='/usr/bin/file -L' + lt_cv_file_magic_test_file=/shlib/libc.so + ;; + +cygwin*) + # func_win32_libid is a shell function defined in ltmain.sh + lt_cv_deplibs_check_method='file_magic ^x86 archive import|^x86 DLL' + lt_cv_file_magic_cmd='func_win32_libid' + ;; + +mingw* | pw32*) + # Base MSYS/MinGW do not provide the 'file' command needed by + # func_win32_libid shell function, so use a weaker test based on 'objdump', + # unless we find 'file', for example because we are cross-compiling. + # func_win32_libid assumes BSD nm, so disallow it if using MS dumpbin. + if ( test "$lt_cv_nm_interface" = "BSD nm" && file / ) >/dev/null 2>&1; then + lt_cv_deplibs_check_method='file_magic ^x86 archive import|^x86 DLL' + lt_cv_file_magic_cmd='func_win32_libid' + else + # Keep this pattern in sync with the one in func_win32_libid. + lt_cv_deplibs_check_method='file_magic file format (pei*-i386(.*architecture: i386)?|pe-arm-wince|pe-x86-64)' + lt_cv_file_magic_cmd='$OBJDUMP -f' + fi + ;; + +cegcc*) + # use the weaker test based on 'objdump'. See mingw*. + lt_cv_deplibs_check_method='file_magic file format pe-arm-.*little(.*architecture: arm)?' + lt_cv_file_magic_cmd='$OBJDUMP -f' + ;; + +darwin* | rhapsody*) + lt_cv_deplibs_check_method=pass_all + ;; + +freebsd* | dragonfly*) + if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then + case $host_cpu in + i*86 ) + # Not sure whether the presence of OpenBSD here was a mistake. + # Let's accept both of them until this is cleared up. + lt_cv_deplibs_check_method='file_magic (FreeBSD|OpenBSD|DragonFly)/i[3-9]86 (compact )?demand paged shared library' + lt_cv_file_magic_cmd=/usr/bin/file + lt_cv_file_magic_test_file=`echo /usr/lib/libc.so.*` + ;; + esac + else + lt_cv_deplibs_check_method=pass_all + fi + ;; + +haiku*) + lt_cv_deplibs_check_method=pass_all + ;; + +hpux10.20* | hpux11*) + lt_cv_file_magic_cmd=/usr/bin/file + case $host_cpu in + ia64*) + lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|ELF-[0-9][0-9]) shared object file - IA64' + lt_cv_file_magic_test_file=/usr/lib/hpux32/libc.so + ;; + hppa*64*) + lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|ELF[ -][0-9][0-9])(-bit)?( [LM]SB)? shared object( file)?[, -]* PA-RISC [0-9]\.[0-9]' + lt_cv_file_magic_test_file=/usr/lib/pa20_64/libc.sl + ;; + *) + lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|PA-RISC[0-9]\.[0-9]) shared library' + lt_cv_file_magic_test_file=/usr/lib/libc.sl + ;; + esac + ;; + +interix[3-9]*) + # PIC code is broken on Interix 3.x, that's why |\.a not |_pic\.a here + lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so|\.a)$' + ;; + +irix5* | irix6* | nonstopux*) + case $LD in + *-32|*"-32 ") libmagic=32-bit;; + *-n32|*"-n32 ") libmagic=N32;; + *-64|*"-64 ") libmagic=64-bit;; + *) libmagic=never-match;; + esac + lt_cv_deplibs_check_method=pass_all + ;; + +# This must be glibc/ELF. +linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) + lt_cv_deplibs_check_method=pass_all + ;; + +netbsd* | netbsdelf*-gnu) + if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then + lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so\.[0-9]+\.[0-9]+|_pic\.a)$' + else + lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so|_pic\.a)$' + fi + ;; + +newos6*) + lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [ML]SB (executable|dynamic lib)' + lt_cv_file_magic_cmd=/usr/bin/file + lt_cv_file_magic_test_file=/usr/lib/libnls.so + ;; + +*nto* | *qnx*) + lt_cv_deplibs_check_method=pass_all + ;; + +openbsd*) + if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then + lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so\.[0-9]+\.[0-9]+|\.so|_pic\.a)$' + else + lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so\.[0-9]+\.[0-9]+|_pic\.a)$' + fi + ;; + +osf3* | osf4* | osf5*) + lt_cv_deplibs_check_method=pass_all + ;; + +rdos*) + lt_cv_deplibs_check_method=pass_all + ;; + +solaris*) + lt_cv_deplibs_check_method=pass_all + ;; + +sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*) + lt_cv_deplibs_check_method=pass_all + ;; + +sysv4 | sysv4.3*) + case $host_vendor in + motorola) + lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [ML]SB (shared object|dynamic lib) M[0-9][0-9]* Version [0-9]' + lt_cv_file_magic_test_file=`echo /usr/lib/libc.so*` + ;; + ncr) + lt_cv_deplibs_check_method=pass_all + ;; + sequent) + lt_cv_file_magic_cmd='/bin/file' + lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [LM]SB (shared object|dynamic lib )' + ;; + sni) + lt_cv_file_magic_cmd='/bin/file' + lt_cv_deplibs_check_method="file_magic ELF [0-9][0-9]*-bit [LM]SB dynamic lib" + lt_cv_file_magic_test_file=/lib/libc.so + ;; + siemens) + lt_cv_deplibs_check_method=pass_all + ;; + pc) + lt_cv_deplibs_check_method=pass_all + ;; + esac + ;; + +tpf*) + lt_cv_deplibs_check_method=pass_all + ;; +esac + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_deplibs_check_method" >&5 +$as_echo "$lt_cv_deplibs_check_method" >&6; } + +file_magic_glob= +want_nocaseglob=no +if test "$build" = "$host"; then + case $host_os in + mingw* | pw32*) + if ( shopt | grep nocaseglob ) >/dev/null 2>&1; then + want_nocaseglob=yes + else + file_magic_glob=`echo aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ | $SED -e "s/\(..\)/s\/[\1]\/[\1]\/g;/g"` + fi + ;; + esac +fi + +file_magic_cmd=$lt_cv_file_magic_cmd +deplibs_check_method=$lt_cv_deplibs_check_method +test -z "$deplibs_check_method" && deplibs_check_method=unknown + + + + + + + + + + + + + + + + + + + + + + +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}dlltool", so it can be a program name with args. +set dummy ${ac_tool_prefix}dlltool; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_DLLTOOL+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$DLLTOOL"; then + ac_cv_prog_DLLTOOL="$DLLTOOL" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_DLLTOOL="${ac_tool_prefix}dlltool" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +DLLTOOL=$ac_cv_prog_DLLTOOL +if test -n "$DLLTOOL"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $DLLTOOL" >&5 +$as_echo "$DLLTOOL" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_DLLTOOL"; then + ac_ct_DLLTOOL=$DLLTOOL + # Extract the first word of "dlltool", so it can be a program name with args. +set dummy dlltool; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_DLLTOOL+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_DLLTOOL"; then + ac_cv_prog_ac_ct_DLLTOOL="$ac_ct_DLLTOOL" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_DLLTOOL="dlltool" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_DLLTOOL=$ac_cv_prog_ac_ct_DLLTOOL +if test -n "$ac_ct_DLLTOOL"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_DLLTOOL" >&5 +$as_echo "$ac_ct_DLLTOOL" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_DLLTOOL" = x; then + DLLTOOL="false" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + DLLTOOL=$ac_ct_DLLTOOL + fi +else + DLLTOOL="$ac_cv_prog_DLLTOOL" +fi + +test -z "$DLLTOOL" && DLLTOOL=dlltool + + + + + + + + + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to associate runtime and link libraries" >&5 +$as_echo_n "checking how to associate runtime and link libraries... " >&6; } +if ${lt_cv_sharedlib_from_linklib_cmd+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_sharedlib_from_linklib_cmd='unknown' + +case $host_os in +cygwin* | mingw* | pw32* | cegcc*) + # two different shell functions defined in ltmain.sh + # decide which to use based on capabilities of $DLLTOOL + case `$DLLTOOL --help 2>&1` in + *--identify-strict*) + lt_cv_sharedlib_from_linklib_cmd=func_cygming_dll_for_implib + ;; + *) + lt_cv_sharedlib_from_linklib_cmd=func_cygming_dll_for_implib_fallback + ;; + esac + ;; +*) + # fallback: assume linklib IS sharedlib + lt_cv_sharedlib_from_linklib_cmd="$ECHO" + ;; +esac + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_sharedlib_from_linklib_cmd" >&5 +$as_echo "$lt_cv_sharedlib_from_linklib_cmd" >&6; } +sharedlib_from_linklib_cmd=$lt_cv_sharedlib_from_linklib_cmd +test -z "$sharedlib_from_linklib_cmd" && sharedlib_from_linklib_cmd=$ECHO + + + + + + + +if test -n "$ac_tool_prefix"; then + for ac_prog in ar + do + # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. +set dummy $ac_tool_prefix$ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_AR+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$AR"; then + ac_cv_prog_AR="$AR" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_AR="$ac_tool_prefix$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +AR=$ac_cv_prog_AR +if test -n "$AR"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $AR" >&5 +$as_echo "$AR" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$AR" && break + done +fi +if test -z "$AR"; then + ac_ct_AR=$AR + for ac_prog in ar +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_AR+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_AR"; then + ac_cv_prog_ac_ct_AR="$ac_ct_AR" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_AR="$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_AR=$ac_cv_prog_ac_ct_AR +if test -n "$ac_ct_AR"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_AR" >&5 +$as_echo "$ac_ct_AR" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$ac_ct_AR" && break +done + + if test "x$ac_ct_AR" = x; then + AR="false" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + AR=$ac_ct_AR + fi +fi + +: ${AR=ar} +: ${AR_FLAGS=cru} + + + + + + + + + + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for archiver @FILE support" >&5 +$as_echo_n "checking for archiver @FILE support... " >&6; } +if ${lt_cv_ar_at_file+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_ar_at_file=no + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + echo conftest.$ac_objext > conftest.lst + lt_ar_try='$AR $AR_FLAGS libconftest.a @conftest.lst >&5' + { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$lt_ar_try\""; } >&5 + (eval $lt_ar_try) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } + if test "$ac_status" -eq 0; then + # Ensure the archiver fails upon bogus file names. + rm -f conftest.$ac_objext libconftest.a + { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$lt_ar_try\""; } >&5 + (eval $lt_ar_try) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } + if test "$ac_status" -ne 0; then + lt_cv_ar_at_file=@ + fi + fi + rm -f conftest.* libconftest.a + +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_ar_at_file" >&5 +$as_echo "$lt_cv_ar_at_file" >&6; } + +if test "x$lt_cv_ar_at_file" = xno; then + archiver_list_spec= +else + archiver_list_spec=$lt_cv_ar_at_file +fi + + + + + + + +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}strip", so it can be a program name with args. +set dummy ${ac_tool_prefix}strip; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_STRIP+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$STRIP"; then + ac_cv_prog_STRIP="$STRIP" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_STRIP="${ac_tool_prefix}strip" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +STRIP=$ac_cv_prog_STRIP +if test -n "$STRIP"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $STRIP" >&5 +$as_echo "$STRIP" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_STRIP"; then + ac_ct_STRIP=$STRIP + # Extract the first word of "strip", so it can be a program name with args. +set dummy strip; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_STRIP+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_STRIP"; then + ac_cv_prog_ac_ct_STRIP="$ac_ct_STRIP" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_STRIP="strip" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_STRIP=$ac_cv_prog_ac_ct_STRIP +if test -n "$ac_ct_STRIP"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_STRIP" >&5 +$as_echo "$ac_ct_STRIP" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_STRIP" = x; then + STRIP=":" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + STRIP=$ac_ct_STRIP + fi +else + STRIP="$ac_cv_prog_STRIP" +fi + +test -z "$STRIP" && STRIP=: + + + + + + +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}ranlib", so it can be a program name with args. +set dummy ${ac_tool_prefix}ranlib; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_RANLIB+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$RANLIB"; then + ac_cv_prog_RANLIB="$RANLIB" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_RANLIB="${ac_tool_prefix}ranlib" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +RANLIB=$ac_cv_prog_RANLIB +if test -n "$RANLIB"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $RANLIB" >&5 +$as_echo "$RANLIB" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_RANLIB"; then + ac_ct_RANLIB=$RANLIB + # Extract the first word of "ranlib", so it can be a program name with args. +set dummy ranlib; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_RANLIB+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_RANLIB"; then + ac_cv_prog_ac_ct_RANLIB="$ac_ct_RANLIB" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_RANLIB="ranlib" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_RANLIB=$ac_cv_prog_ac_ct_RANLIB +if test -n "$ac_ct_RANLIB"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_RANLIB" >&5 +$as_echo "$ac_ct_RANLIB" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_RANLIB" = x; then + RANLIB=":" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + RANLIB=$ac_ct_RANLIB + fi +else + RANLIB="$ac_cv_prog_RANLIB" +fi + +test -z "$RANLIB" && RANLIB=: + + + + + + +# Determine commands to create old-style static archives. +old_archive_cmds='$AR $AR_FLAGS $oldlib$oldobjs' +old_postinstall_cmds='chmod 644 $oldlib' +old_postuninstall_cmds= + +if test -n "$RANLIB"; then + case $host_os in + openbsd*) + old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB -t \$tool_oldlib" + ;; + *) + old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB \$tool_oldlib" + ;; + esac + old_archive_cmds="$old_archive_cmds~\$RANLIB \$tool_oldlib" +fi + +case $host_os in + darwin*) + lock_old_archive_extraction=yes ;; + *) + lock_old_archive_extraction=no ;; +esac + + + + + + + + + + + + + + + + + + + + + +for ac_prog in gawk mawk nawk awk +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_AWK+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$AWK"; then + ac_cv_prog_AWK="$AWK" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_AWK="$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +AWK=$ac_cv_prog_AWK +if test -n "$AWK"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $AWK" >&5 +$as_echo "$AWK" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$AWK" && break +done + + + + + + + + + + + + + + + + + + + +# If no C compiler was specified, use CC. +LTCC=${LTCC-"$CC"} + +# If no C compiler flags were specified, use CFLAGS. +LTCFLAGS=${LTCFLAGS-"$CFLAGS"} + +# Allow CC to be a program name with arguments. +compiler=$CC + + +# Check for command to grab the raw symbol name followed by C symbol from nm. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking command to parse $NM output from $compiler object" >&5 +$as_echo_n "checking command to parse $NM output from $compiler object... " >&6; } +if ${lt_cv_sys_global_symbol_pipe+:} false; then : + $as_echo_n "(cached) " >&6 +else + +# These are sane defaults that work on at least a few old systems. +# [They come from Ultrix. What could be older than Ultrix?!! ;)] + +# Character class describing NM global symbol codes. +symcode='[BCDEGRST]' + +# Regexp to match symbols that can be accessed directly from C. +sympat='\([_A-Za-z][_A-Za-z0-9]*\)' + +# Define system-specific variables. +case $host_os in +aix*) + symcode='[BCDT]' + ;; +cygwin* | mingw* | pw32* | cegcc*) + symcode='[ABCDGISTW]' + ;; +hpux*) + if test "$host_cpu" = ia64; then + symcode='[ABCDEGRST]' + fi + ;; +irix* | nonstopux*) + symcode='[BCDEGRST]' + ;; +osf*) + symcode='[BCDEGQRST]' + ;; +solaris*) + symcode='[BDRT]' + ;; +sco3.2v5*) + symcode='[DT]' + ;; +sysv4.2uw2*) + symcode='[DT]' + ;; +sysv5* | sco5v6* | unixware* | OpenUNIX*) + symcode='[ABDT]' + ;; +sysv4) + symcode='[DFNSTU]' + ;; +esac + +# If we're using GNU nm, then use its standard symbol codes. +case `$NM -V 2>&1` in +*GNU* | *'with BFD'*) + symcode='[ABCDGIRSTW]' ;; +esac + +# Transform an extracted symbol line into a proper C declaration. +# Some systems (esp. on ia64) link data and code symbols differently, +# so use this general approach. +lt_cv_sys_global_symbol_to_cdecl="sed -n -e 's/^T .* \(.*\)$/extern int \1();/p' -e 's/^$symcode* .* \(.*\)$/extern char \1;/p'" + +# Transform an extracted symbol line into symbol name and symbol address +lt_cv_sys_global_symbol_to_c_name_address="sed -n -e 's/^: \([^ ]*\)[ ]*$/ {\\\"\1\\\", (void *) 0},/p' -e 's/^$symcode* \([^ ]*\) \([^ ]*\)$/ {\"\2\", (void *) \&\2},/p'" +lt_cv_sys_global_symbol_to_c_name_address_lib_prefix="sed -n -e 's/^: \([^ ]*\)[ ]*$/ {\\\"\1\\\", (void *) 0},/p' -e 's/^$symcode* \([^ ]*\) \(lib[^ ]*\)$/ {\"\2\", (void *) \&\2},/p' -e 's/^$symcode* \([^ ]*\) \([^ ]*\)$/ {\"lib\2\", (void *) \&\2},/p'" + +# Handle CRLF in mingw tool chain +opt_cr= +case $build_os in +mingw*) + opt_cr=`$ECHO 'x\{0,1\}' | tr x '\015'` # option cr in regexp + ;; +esac + +# Try without a prefix underscore, then with it. +for ac_symprfx in "" "_"; do + + # Transform symcode, sympat, and symprfx into a raw symbol and a C symbol. + symxfrm="\\1 $ac_symprfx\\2 \\2" + + # Write the raw and C identifiers. + if test "$lt_cv_nm_interface" = "MS dumpbin"; then + # Fake it for dumpbin and say T for any non-static function + # and D for any global variable. + # Also find C++ and __fastcall symbols from MSVC++, + # which start with @ or ?. + lt_cv_sys_global_symbol_pipe="$AWK '"\ +" {last_section=section; section=\$ 3};"\ +" /^COFF SYMBOL TABLE/{for(i in hide) delete hide[i]};"\ +" /Section length .*#relocs.*(pick any)/{hide[last_section]=1};"\ +" \$ 0!~/External *\|/{next};"\ +" / 0+ UNDEF /{next}; / UNDEF \([^|]\)*()/{next};"\ +" {if(hide[section]) next};"\ +" {f=0}; \$ 0~/\(\).*\|/{f=1}; {printf f ? \"T \" : \"D \"};"\ +" {split(\$ 0, a, /\||\r/); split(a[2], s)};"\ +" s[1]~/^[@?]/{print s[1], s[1]; next};"\ +" s[1]~prfx {split(s[1],t,\"@\"); print t[1], substr(t[1],length(prfx))}"\ +" ' prfx=^$ac_symprfx" + else + lt_cv_sys_global_symbol_pipe="sed -n -e 's/^.*[ ]\($symcode$symcode*\)[ ][ ]*$ac_symprfx$sympat$opt_cr$/$symxfrm/p'" + fi + lt_cv_sys_global_symbol_pipe="$lt_cv_sys_global_symbol_pipe | sed '/ __gnu_lto/d'" + + # Check to see that the pipe works correctly. + pipe_works=no + + rm -f conftest* + cat > conftest.$ac_ext <<_LT_EOF +#ifdef __cplusplus +extern "C" { +#endif +char nm_test_var; +void nm_test_func(void); +void nm_test_func(void){} +#ifdef __cplusplus +} +#endif +int main(){nm_test_var='a';nm_test_func();return(0);} +_LT_EOF + + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then + # Now try to grab the symbols. + nlist=conftest.nm + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$NM conftest.$ac_objext \| "$lt_cv_sys_global_symbol_pipe" \> $nlist\""; } >&5 + (eval $NM conftest.$ac_objext \| "$lt_cv_sys_global_symbol_pipe" \> $nlist) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && test -s "$nlist"; then + # Try sorting and uniquifying the output. + if sort "$nlist" | uniq > "$nlist"T; then + mv -f "$nlist"T "$nlist" + else + rm -f "$nlist"T + fi + + # Make sure that we snagged all the symbols we need. + if $GREP ' nm_test_var$' "$nlist" >/dev/null; then + if $GREP ' nm_test_func$' "$nlist" >/dev/null; then + cat <<_LT_EOF > conftest.$ac_ext +/* Keep this code in sync between libtool.m4, ltmain, lt_system.h, and tests. */ +#if defined(_WIN32) || defined(__CYGWIN__) || defined(_WIN32_WCE) +/* DATA imports from DLLs on WIN32 con't be const, because runtime + relocations are performed -- see ld's documentation on pseudo-relocs. */ +# define LT_DLSYM_CONST +#elif defined(__osf__) +/* This system does not cope well with relocations in const data. */ +# define LT_DLSYM_CONST +#else +# define LT_DLSYM_CONST const +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +_LT_EOF + # Now generate the symbol file. + eval "$lt_cv_sys_global_symbol_to_cdecl"' < "$nlist" | $GREP -v main >> conftest.$ac_ext' + + cat <<_LT_EOF >> conftest.$ac_ext + +/* The mapping between symbol names and symbols. */ +LT_DLSYM_CONST struct { + const char *name; + void *address; +} +lt__PROGRAM__LTX_preloaded_symbols[] = +{ + { "@PROGRAM@", (void *) 0 }, +_LT_EOF + $SED "s/^$symcode$symcode* \(.*\) \(.*\)$/ {\"\2\", (void *) \&\2},/" < "$nlist" | $GREP -v main >> conftest.$ac_ext + cat <<\_LT_EOF >> conftest.$ac_ext + {0, (void *) 0} +}; + +/* This works around a problem in FreeBSD linker */ +#ifdef FREEBSD_WORKAROUND +static const void *lt_preloaded_setup() { + return lt__PROGRAM__LTX_preloaded_symbols; +} +#endif + +#ifdef __cplusplus +} +#endif +_LT_EOF + # Now try linking the two files. + mv conftest.$ac_objext conftstm.$ac_objext + lt_globsym_save_LIBS=$LIBS + lt_globsym_save_CFLAGS=$CFLAGS + LIBS="conftstm.$ac_objext" + CFLAGS="$CFLAGS$lt_prog_compiler_no_builtin_flag" + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_link\""; } >&5 + (eval $ac_link) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && test -s conftest${ac_exeext}; then + pipe_works=yes + fi + LIBS=$lt_globsym_save_LIBS + CFLAGS=$lt_globsym_save_CFLAGS + else + echo "cannot find nm_test_func in $nlist" >&5 + fi + else + echo "cannot find nm_test_var in $nlist" >&5 + fi + else + echo "cannot run $lt_cv_sys_global_symbol_pipe" >&5 + fi + else + echo "$progname: failed program was:" >&5 + cat conftest.$ac_ext >&5 + fi + rm -rf conftest* conftst* + + # Do not use the global_symbol_pipe unless it works. + if test "$pipe_works" = yes; then + break + else + lt_cv_sys_global_symbol_pipe= + fi +done + +fi + +if test -z "$lt_cv_sys_global_symbol_pipe"; then + lt_cv_sys_global_symbol_to_cdecl= +fi +if test -z "$lt_cv_sys_global_symbol_pipe$lt_cv_sys_global_symbol_to_cdecl"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: failed" >&5 +$as_echo "failed" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: ok" >&5 +$as_echo "ok" >&6; } +fi + +# Response file support. +if test "$lt_cv_nm_interface" = "MS dumpbin"; then + nm_file_list_spec='@' +elif $NM --help 2>/dev/null | grep '[@]FILE' >/dev/null; then + nm_file_list_spec='@' +fi + + + + + + + + + + + + + + + + + + + + + + + + + + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for sysroot" >&5 +$as_echo_n "checking for sysroot... " >&6; } + +# Check whether --with-sysroot was given. +if test "${with_sysroot+set}" = set; then : + withval=$with_sysroot; +else + with_sysroot=no +fi + + +lt_sysroot= +case ${with_sysroot} in #( + yes) + if test "$GCC" = yes; then + lt_sysroot=`$CC --print-sysroot 2>/dev/null` + fi + ;; #( + /*) + lt_sysroot=`echo "$with_sysroot" | sed -e "$sed_quote_subst"` + ;; #( + no|'') + ;; #( + *) + { $as_echo "$as_me:${as_lineno-$LINENO}: result: ${with_sysroot}" >&5 +$as_echo "${with_sysroot}" >&6; } + as_fn_error $? "The sysroot must be an absolute path." "$LINENO" 5 + ;; +esac + + { $as_echo "$as_me:${as_lineno-$LINENO}: result: ${lt_sysroot:-no}" >&5 +$as_echo "${lt_sysroot:-no}" >&6; } + + + + + +# Check whether --enable-libtool-lock was given. +if test "${enable_libtool_lock+set}" = set; then : + enableval=$enable_libtool_lock; +fi + +test "x$enable_libtool_lock" != xno && enable_libtool_lock=yes + +# Some flags need to be propagated to the compiler or linker for good +# libtool support. +case $host in +ia64-*-hpux*) + # Find out which ABI we are using. + echo 'int i;' > conftest.$ac_ext + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then + case `/usr/bin/file conftest.$ac_objext` in + *ELF-32*) + HPUX_IA64_MODE="32" + ;; + *ELF-64*) + HPUX_IA64_MODE="64" + ;; + esac + fi + rm -rf conftest* + ;; +*-*-irix6*) + # Find out which ABI we are using. + echo '#line '$LINENO' "configure"' > conftest.$ac_ext + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then + if test "$lt_cv_prog_gnu_ld" = yes; then + case `/usr/bin/file conftest.$ac_objext` in + *32-bit*) + LD="${LD-ld} -melf32bsmip" + ;; + *N32*) + LD="${LD-ld} -melf32bmipn32" + ;; + *64-bit*) + LD="${LD-ld} -melf64bmip" + ;; + esac + else + case `/usr/bin/file conftest.$ac_objext` in + *32-bit*) + LD="${LD-ld} -32" + ;; + *N32*) + LD="${LD-ld} -n32" + ;; + *64-bit*) + LD="${LD-ld} -64" + ;; + esac + fi + fi + rm -rf conftest* + ;; + +x86_64-*kfreebsd*-gnu|x86_64-*linux*|powerpc*-*linux*| \ +s390*-*linux*|s390*-*tpf*|sparc*-*linux*) + # Find out which ABI we are using. + echo 'int i;' > conftest.$ac_ext + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then + case `/usr/bin/file conftest.o` in + *32-bit*) + case $host in + x86_64-*kfreebsd*-gnu) + LD="${LD-ld} -m elf_i386_fbsd" + ;; + x86_64-*linux*) + case `/usr/bin/file conftest.o` in + *x86-64*) + LD="${LD-ld} -m elf32_x86_64" + ;; + *) + LD="${LD-ld} -m elf_i386" + ;; + esac + ;; + powerpc64le-*) + LD="${LD-ld} -m elf32lppclinux" + ;; + powerpc64-*) + LD="${LD-ld} -m elf32ppclinux" + ;; + s390x-*linux*) + LD="${LD-ld} -m elf_s390" + ;; + sparc64-*linux*) + LD="${LD-ld} -m elf32_sparc" + ;; + esac + ;; + *64-bit*) + case $host in + x86_64-*kfreebsd*-gnu) + LD="${LD-ld} -m elf_x86_64_fbsd" + ;; + x86_64-*linux*) + LD="${LD-ld} -m elf_x86_64" + ;; + powerpcle-*) + LD="${LD-ld} -m elf64lppc" + ;; + powerpc-*) + LD="${LD-ld} -m elf64ppc" + ;; + s390*-*linux*|s390*-*tpf*) + LD="${LD-ld} -m elf64_s390" + ;; + sparc*-*linux*) + LD="${LD-ld} -m elf64_sparc" + ;; + esac + ;; + esac + fi + rm -rf conftest* + ;; + +*-*-sco3.2v5*) + # On SCO OpenServer 5, we need -belf to get full-featured binaries. + SAVE_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS -belf" + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C compiler needs -belf" >&5 +$as_echo_n "checking whether the C compiler needs -belf... " >&6; } +if ${lt_cv_cc_needs_belf+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + lt_cv_cc_needs_belf=yes +else + lt_cv_cc_needs_belf=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_cc_needs_belf" >&5 +$as_echo "$lt_cv_cc_needs_belf" >&6; } + if test x"$lt_cv_cc_needs_belf" != x"yes"; then + # this is probably gcc 2.8.0, egcs 1.0 or newer; no need for -belf + CFLAGS="$SAVE_CFLAGS" + fi + ;; +*-*solaris*) + # Find out which ABI we are using. + echo 'int i;' > conftest.$ac_ext + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then + case `/usr/bin/file conftest.o` in + *64-bit*) + case $lt_cv_prog_gnu_ld in + yes*) + case $host in + i?86-*-solaris*) + LD="${LD-ld} -m elf_x86_64" + ;; + sparc*-*-solaris*) + LD="${LD-ld} -m elf64_sparc" + ;; + esac + # GNU ld 2.21 introduced _sol2 emulations. Use them if available. + if ${LD-ld} -V | grep _sol2 >/dev/null 2>&1; then + LD="${LD-ld}_sol2" + fi + ;; + *) + if ${LD-ld} -64 -r -o conftest2.o conftest.o >/dev/null 2>&1; then + LD="${LD-ld} -64" + fi + ;; + esac + ;; + esac + fi + rm -rf conftest* + ;; +esac + +need_locks="$enable_libtool_lock" + +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}mt", so it can be a program name with args. +set dummy ${ac_tool_prefix}mt; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_MANIFEST_TOOL+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$MANIFEST_TOOL"; then + ac_cv_prog_MANIFEST_TOOL="$MANIFEST_TOOL" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_MANIFEST_TOOL="${ac_tool_prefix}mt" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +MANIFEST_TOOL=$ac_cv_prog_MANIFEST_TOOL +if test -n "$MANIFEST_TOOL"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $MANIFEST_TOOL" >&5 +$as_echo "$MANIFEST_TOOL" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_MANIFEST_TOOL"; then + ac_ct_MANIFEST_TOOL=$MANIFEST_TOOL + # Extract the first word of "mt", so it can be a program name with args. +set dummy mt; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_MANIFEST_TOOL+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_MANIFEST_TOOL"; then + ac_cv_prog_ac_ct_MANIFEST_TOOL="$ac_ct_MANIFEST_TOOL" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_MANIFEST_TOOL="mt" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_MANIFEST_TOOL=$ac_cv_prog_ac_ct_MANIFEST_TOOL +if test -n "$ac_ct_MANIFEST_TOOL"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_MANIFEST_TOOL" >&5 +$as_echo "$ac_ct_MANIFEST_TOOL" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_MANIFEST_TOOL" = x; then + MANIFEST_TOOL=":" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + MANIFEST_TOOL=$ac_ct_MANIFEST_TOOL + fi +else + MANIFEST_TOOL="$ac_cv_prog_MANIFEST_TOOL" +fi + +test -z "$MANIFEST_TOOL" && MANIFEST_TOOL=mt +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if $MANIFEST_TOOL is a manifest tool" >&5 +$as_echo_n "checking if $MANIFEST_TOOL is a manifest tool... " >&6; } +if ${lt_cv_path_mainfest_tool+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_path_mainfest_tool=no + echo "$as_me:$LINENO: $MANIFEST_TOOL '-?'" >&5 + $MANIFEST_TOOL '-?' 2>conftest.err > conftest.out + cat conftest.err >&5 + if $GREP 'Manifest Tool' conftest.out > /dev/null; then + lt_cv_path_mainfest_tool=yes + fi + rm -f conftest* +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_path_mainfest_tool" >&5 +$as_echo "$lt_cv_path_mainfest_tool" >&6; } +if test "x$lt_cv_path_mainfest_tool" != xyes; then + MANIFEST_TOOL=: +fi + + + + + + + case $host_os in + rhapsody* | darwin*) + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}dsymutil", so it can be a program name with args. +set dummy ${ac_tool_prefix}dsymutil; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_DSYMUTIL+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$DSYMUTIL"; then + ac_cv_prog_DSYMUTIL="$DSYMUTIL" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_DSYMUTIL="${ac_tool_prefix}dsymutil" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +DSYMUTIL=$ac_cv_prog_DSYMUTIL +if test -n "$DSYMUTIL"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $DSYMUTIL" >&5 +$as_echo "$DSYMUTIL" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_DSYMUTIL"; then + ac_ct_DSYMUTIL=$DSYMUTIL + # Extract the first word of "dsymutil", so it can be a program name with args. +set dummy dsymutil; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_DSYMUTIL+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_DSYMUTIL"; then + ac_cv_prog_ac_ct_DSYMUTIL="$ac_ct_DSYMUTIL" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_DSYMUTIL="dsymutil" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_DSYMUTIL=$ac_cv_prog_ac_ct_DSYMUTIL +if test -n "$ac_ct_DSYMUTIL"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_DSYMUTIL" >&5 +$as_echo "$ac_ct_DSYMUTIL" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_DSYMUTIL" = x; then + DSYMUTIL=":" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + DSYMUTIL=$ac_ct_DSYMUTIL + fi +else + DSYMUTIL="$ac_cv_prog_DSYMUTIL" +fi + + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}nmedit", so it can be a program name with args. +set dummy ${ac_tool_prefix}nmedit; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_NMEDIT+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$NMEDIT"; then + ac_cv_prog_NMEDIT="$NMEDIT" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_NMEDIT="${ac_tool_prefix}nmedit" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +NMEDIT=$ac_cv_prog_NMEDIT +if test -n "$NMEDIT"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $NMEDIT" >&5 +$as_echo "$NMEDIT" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_NMEDIT"; then + ac_ct_NMEDIT=$NMEDIT + # Extract the first word of "nmedit", so it can be a program name with args. +set dummy nmedit; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_NMEDIT+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_NMEDIT"; then + ac_cv_prog_ac_ct_NMEDIT="$ac_ct_NMEDIT" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_NMEDIT="nmedit" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_NMEDIT=$ac_cv_prog_ac_ct_NMEDIT +if test -n "$ac_ct_NMEDIT"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_NMEDIT" >&5 +$as_echo "$ac_ct_NMEDIT" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_NMEDIT" = x; then + NMEDIT=":" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + NMEDIT=$ac_ct_NMEDIT + fi +else + NMEDIT="$ac_cv_prog_NMEDIT" +fi + + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}lipo", so it can be a program name with args. +set dummy ${ac_tool_prefix}lipo; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_LIPO+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$LIPO"; then + ac_cv_prog_LIPO="$LIPO" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_LIPO="${ac_tool_prefix}lipo" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +LIPO=$ac_cv_prog_LIPO +if test -n "$LIPO"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $LIPO" >&5 +$as_echo "$LIPO" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_LIPO"; then + ac_ct_LIPO=$LIPO + # Extract the first word of "lipo", so it can be a program name with args. +set dummy lipo; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_LIPO+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_LIPO"; then + ac_cv_prog_ac_ct_LIPO="$ac_ct_LIPO" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_LIPO="lipo" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_LIPO=$ac_cv_prog_ac_ct_LIPO +if test -n "$ac_ct_LIPO"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_LIPO" >&5 +$as_echo "$ac_ct_LIPO" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_LIPO" = x; then + LIPO=":" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + LIPO=$ac_ct_LIPO + fi +else + LIPO="$ac_cv_prog_LIPO" +fi + + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}otool", so it can be a program name with args. +set dummy ${ac_tool_prefix}otool; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_OTOOL+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$OTOOL"; then + ac_cv_prog_OTOOL="$OTOOL" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_OTOOL="${ac_tool_prefix}otool" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +OTOOL=$ac_cv_prog_OTOOL +if test -n "$OTOOL"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $OTOOL" >&5 +$as_echo "$OTOOL" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_OTOOL"; then + ac_ct_OTOOL=$OTOOL + # Extract the first word of "otool", so it can be a program name with args. +set dummy otool; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_OTOOL+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_OTOOL"; then + ac_cv_prog_ac_ct_OTOOL="$ac_ct_OTOOL" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_OTOOL="otool" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_OTOOL=$ac_cv_prog_ac_ct_OTOOL +if test -n "$ac_ct_OTOOL"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_OTOOL" >&5 +$as_echo "$ac_ct_OTOOL" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_OTOOL" = x; then + OTOOL=":" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + OTOOL=$ac_ct_OTOOL + fi +else + OTOOL="$ac_cv_prog_OTOOL" +fi + + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}otool64", so it can be a program name with args. +set dummy ${ac_tool_prefix}otool64; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_OTOOL64+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$OTOOL64"; then + ac_cv_prog_OTOOL64="$OTOOL64" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_OTOOL64="${ac_tool_prefix}otool64" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +OTOOL64=$ac_cv_prog_OTOOL64 +if test -n "$OTOOL64"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $OTOOL64" >&5 +$as_echo "$OTOOL64" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_OTOOL64"; then + ac_ct_OTOOL64=$OTOOL64 + # Extract the first word of "otool64", so it can be a program name with args. +set dummy otool64; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_OTOOL64+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_OTOOL64"; then + ac_cv_prog_ac_ct_OTOOL64="$ac_ct_OTOOL64" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_OTOOL64="otool64" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_OTOOL64=$ac_cv_prog_ac_ct_OTOOL64 +if test -n "$ac_ct_OTOOL64"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_OTOOL64" >&5 +$as_echo "$ac_ct_OTOOL64" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_OTOOL64" = x; then + OTOOL64=":" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + OTOOL64=$ac_ct_OTOOL64 + fi +else + OTOOL64="$ac_cv_prog_OTOOL64" +fi + + + + + + + + + + + + + + + + + + + + + + + + + + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for -single_module linker flag" >&5 +$as_echo_n "checking for -single_module linker flag... " >&6; } +if ${lt_cv_apple_cc_single_mod+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_apple_cc_single_mod=no + if test -z "${LT_MULTI_MODULE}"; then + # By default we will add the -single_module flag. You can override + # by either setting the environment variable LT_MULTI_MODULE + # non-empty at configure time, or by adding -multi_module to the + # link flags. + rm -rf libconftest.dylib* + echo "int foo(void){return 1;}" > conftest.c + echo "$LTCC $LTCFLAGS $LDFLAGS -o libconftest.dylib \ +-dynamiclib -Wl,-single_module conftest.c" >&5 + $LTCC $LTCFLAGS $LDFLAGS -o libconftest.dylib \ + -dynamiclib -Wl,-single_module conftest.c 2>conftest.err + _lt_result=$? + # If there is a non-empty error log, and "single_module" + # appears in it, assume the flag caused a linker warning + if test -s conftest.err && $GREP single_module conftest.err; then + cat conftest.err >&5 + # Otherwise, if the output was created with a 0 exit code from + # the compiler, it worked. + elif test -f libconftest.dylib && test $_lt_result -eq 0; then + lt_cv_apple_cc_single_mod=yes + else + cat conftest.err >&5 + fi + rm -rf libconftest.dylib* + rm -f conftest.* + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_apple_cc_single_mod" >&5 +$as_echo "$lt_cv_apple_cc_single_mod" >&6; } + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for -exported_symbols_list linker flag" >&5 +$as_echo_n "checking for -exported_symbols_list linker flag... " >&6; } +if ${lt_cv_ld_exported_symbols_list+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_ld_exported_symbols_list=no + save_LDFLAGS=$LDFLAGS + echo "_main" > conftest.sym + LDFLAGS="$LDFLAGS -Wl,-exported_symbols_list,conftest.sym" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + lt_cv_ld_exported_symbols_list=yes +else + lt_cv_ld_exported_symbols_list=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + LDFLAGS="$save_LDFLAGS" + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_ld_exported_symbols_list" >&5 +$as_echo "$lt_cv_ld_exported_symbols_list" >&6; } + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for -force_load linker flag" >&5 +$as_echo_n "checking for -force_load linker flag... " >&6; } +if ${lt_cv_ld_force_load+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_ld_force_load=no + cat > conftest.c << _LT_EOF +int forced_loaded() { return 2;} +_LT_EOF + echo "$LTCC $LTCFLAGS -c -o conftest.o conftest.c" >&5 + $LTCC $LTCFLAGS -c -o conftest.o conftest.c 2>&5 + echo "$AR cru libconftest.a conftest.o" >&5 + $AR cru libconftest.a conftest.o 2>&5 + echo "$RANLIB libconftest.a" >&5 + $RANLIB libconftest.a 2>&5 + cat > conftest.c << _LT_EOF +int main() { return 0;} +_LT_EOF + echo "$LTCC $LTCFLAGS $LDFLAGS -o conftest conftest.c -Wl,-force_load,./libconftest.a" >&5 + $LTCC $LTCFLAGS $LDFLAGS -o conftest conftest.c -Wl,-force_load,./libconftest.a 2>conftest.err + _lt_result=$? + if test -s conftest.err && $GREP force_load conftest.err; then + cat conftest.err >&5 + elif test -f conftest && test $_lt_result -eq 0 && $GREP forced_load conftest >/dev/null 2>&1 ; then + lt_cv_ld_force_load=yes + else + cat conftest.err >&5 + fi + rm -f conftest.err libconftest.a conftest conftest.c + rm -rf conftest.dSYM + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_ld_force_load" >&5 +$as_echo "$lt_cv_ld_force_load" >&6; } + case $host_os in + rhapsody* | darwin1.[012]) + _lt_dar_allow_undefined='${wl}-undefined ${wl}suppress' ;; + darwin1.*) + _lt_dar_allow_undefined='${wl}-flat_namespace ${wl}-undefined ${wl}suppress' ;; + darwin*) # darwin 5.x on + # if running on 10.5 or later, the deployment target defaults + # to the OS version, if on x86, and 10.4, the deployment + # target defaults to 10.4. Don't you love it? + case ${MACOSX_DEPLOYMENT_TARGET-10.0},$host in + 10.0,*86*-darwin8*|10.0,*-darwin[91]*) + _lt_dar_allow_undefined='${wl}-undefined ${wl}dynamic_lookup' ;; + 10.[012]*) + _lt_dar_allow_undefined='${wl}-flat_namespace ${wl}-undefined ${wl}suppress' ;; + 10.*) + _lt_dar_allow_undefined='${wl}-undefined ${wl}dynamic_lookup' ;; + esac + ;; + esac + if test "$lt_cv_apple_cc_single_mod" = "yes"; then + _lt_dar_single_mod='$single_module' + fi + if test "$lt_cv_ld_exported_symbols_list" = "yes"; then + _lt_dar_export_syms=' ${wl}-exported_symbols_list,$output_objdir/${libname}-symbols.expsym' + else + _lt_dar_export_syms='~$NMEDIT -s $output_objdir/${libname}-symbols.expsym ${lib}' + fi + if test "$DSYMUTIL" != ":" && test "$lt_cv_ld_force_load" = "no"; then + _lt_dsymutil='~$DSYMUTIL $lib || :' + else + _lt_dsymutil= + fi + ;; + esac + +for ac_header in dlfcn.h +do : + ac_fn_c_check_header_compile "$LINENO" "dlfcn.h" "ac_cv_header_dlfcn_h" "$ac_includes_default +" +if test "x$ac_cv_header_dlfcn_h" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_DLFCN_H 1 +_ACEOF + +fi + +done + + + + + +# Set options + + + + enable_dlopen=no + + + enable_win32_dll=no + + + # Check whether --enable-shared was given. +if test "${enable_shared+set}" = set; then : + enableval=$enable_shared; p=${PACKAGE-default} + case $enableval in + yes) enable_shared=yes ;; + no) enable_shared=no ;; + *) + enable_shared=no + # Look at the argument we got. We use all the common list separators. + lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR," + for pkg in $enableval; do + IFS="$lt_save_ifs" + if test "X$pkg" = "X$p"; then + enable_shared=yes + fi + done + IFS="$lt_save_ifs" + ;; + esac +else + enable_shared=yes +fi + + + + + + + + + + # Check whether --enable-static was given. +if test "${enable_static+set}" = set; then : + enableval=$enable_static; p=${PACKAGE-default} + case $enableval in + yes) enable_static=yes ;; + no) enable_static=no ;; + *) + enable_static=no + # Look at the argument we got. We use all the common list separators. + lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR," + for pkg in $enableval; do + IFS="$lt_save_ifs" + if test "X$pkg" = "X$p"; then + enable_static=yes + fi + done + IFS="$lt_save_ifs" + ;; + esac +else + enable_static=yes +fi + + + + + + + + + + +# Check whether --with-pic was given. +if test "${with_pic+set}" = set; then : + withval=$with_pic; lt_p=${PACKAGE-default} + case $withval in + yes|no) pic_mode=$withval ;; + *) + pic_mode=default + # Look at the argument we got. We use all the common list separators. + lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR," + for lt_pkg in $withval; do + IFS="$lt_save_ifs" + if test "X$lt_pkg" = "X$lt_p"; then + pic_mode=yes + fi + done + IFS="$lt_save_ifs" + ;; + esac +else + pic_mode=default +fi + + +test -z "$pic_mode" && pic_mode=default + + + + + + + + # Check whether --enable-fast-install was given. +if test "${enable_fast_install+set}" = set; then : + enableval=$enable_fast_install; p=${PACKAGE-default} + case $enableval in + yes) enable_fast_install=yes ;; + no) enable_fast_install=no ;; + *) + enable_fast_install=no + # Look at the argument we got. We use all the common list separators. + lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR," + for pkg in $enableval; do + IFS="$lt_save_ifs" + if test "X$pkg" = "X$p"; then + enable_fast_install=yes + fi + done + IFS="$lt_save_ifs" + ;; + esac +else + enable_fast_install=yes +fi + + + + + + + + + + + +# This can be used to rebuild libtool when needed +LIBTOOL_DEPS="$ltmain" + +# Always use our own libtool. +LIBTOOL='$(SHELL) $(top_builddir)/libtool' + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +test -z "$LN_S" && LN_S="ln -s" + + + + + + + + + + + + + + +if test -n "${ZSH_VERSION+set}" ; then + setopt NO_GLOB_SUBST +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for objdir" >&5 +$as_echo_n "checking for objdir... " >&6; } +if ${lt_cv_objdir+:} false; then : + $as_echo_n "(cached) " >&6 +else + rm -f .libs 2>/dev/null +mkdir .libs 2>/dev/null +if test -d .libs; then + lt_cv_objdir=.libs +else + # MS-DOS does not allow filenames that begin with a dot. + lt_cv_objdir=_libs +fi +rmdir .libs 2>/dev/null +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_objdir" >&5 +$as_echo "$lt_cv_objdir" >&6; } +objdir=$lt_cv_objdir + + + + + +cat >>confdefs.h <<_ACEOF +#define LT_OBJDIR "$lt_cv_objdir/" +_ACEOF + + + + +case $host_os in +aix3*) + # AIX sometimes has problems with the GCC collect2 program. For some + # reason, if we set the COLLECT_NAMES environment variable, the problems + # vanish in a puff of smoke. + if test "X${COLLECT_NAMES+set}" != Xset; then + COLLECT_NAMES= + export COLLECT_NAMES + fi + ;; +esac + +# Global variables: +ofile=libtool +can_build_shared=yes + +# All known linkers require a `.a' archive for static linking (except MSVC, +# which needs '.lib'). +libext=a + +with_gnu_ld="$lt_cv_prog_gnu_ld" + +old_CC="$CC" +old_CFLAGS="$CFLAGS" + +# Set sane defaults for various variables +test -z "$CC" && CC=cc +test -z "$LTCC" && LTCC=$CC +test -z "$LTCFLAGS" && LTCFLAGS=$CFLAGS +test -z "$LD" && LD=ld +test -z "$ac_objext" && ac_objext=o + +for cc_temp in $compiler""; do + case $cc_temp in + compile | *[\\/]compile | ccache | *[\\/]ccache ) ;; + distcc | *[\\/]distcc | purify | *[\\/]purify ) ;; + \-*) ;; + *) break;; + esac +done +cc_basename=`$ECHO "$cc_temp" | $SED "s%.*/%%; s%^$host_alias-%%"` + + +# Only perform the check for file, if the check method requires it +test -z "$MAGIC_CMD" && MAGIC_CMD=file +case $deplibs_check_method in +file_magic*) + if test "$file_magic_cmd" = '$MAGIC_CMD'; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ${ac_tool_prefix}file" >&5 +$as_echo_n "checking for ${ac_tool_prefix}file... " >&6; } +if ${lt_cv_path_MAGIC_CMD+:} false; then : + $as_echo_n "(cached) " >&6 +else + case $MAGIC_CMD in +[\\/*] | ?:[\\/]*) + lt_cv_path_MAGIC_CMD="$MAGIC_CMD" # Let the user override the test with a path. + ;; +*) + lt_save_MAGIC_CMD="$MAGIC_CMD" + lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR + ac_dummy="/usr/bin$PATH_SEPARATOR$PATH" + for ac_dir in $ac_dummy; do + IFS="$lt_save_ifs" + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/${ac_tool_prefix}file; then + lt_cv_path_MAGIC_CMD="$ac_dir/${ac_tool_prefix}file" + if test -n "$file_magic_test_file"; then + case $deplibs_check_method in + "file_magic "*) + file_magic_regex=`expr "$deplibs_check_method" : "file_magic \(.*\)"` + MAGIC_CMD="$lt_cv_path_MAGIC_CMD" + if eval $file_magic_cmd \$file_magic_test_file 2> /dev/null | + $EGREP "$file_magic_regex" > /dev/null; then + : + else + cat <<_LT_EOF 1>&2 + +*** Warning: the command libtool uses to detect shared libraries, +*** $file_magic_cmd, produces output that libtool cannot recognize. +*** The result is that libtool may fail to recognize shared libraries +*** as such. This will affect the creation of libtool libraries that +*** depend on shared libraries, but programs linked with such libtool +*** libraries will work regardless of this problem. Nevertheless, you +*** may want to report the problem to your system manager and/or to +*** bug-libtool@gnu.org + +_LT_EOF + fi ;; + esac + fi + break + fi + done + IFS="$lt_save_ifs" + MAGIC_CMD="$lt_save_MAGIC_CMD" + ;; +esac +fi + +MAGIC_CMD="$lt_cv_path_MAGIC_CMD" +if test -n "$MAGIC_CMD"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $MAGIC_CMD" >&5 +$as_echo "$MAGIC_CMD" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + + + +if test -z "$lt_cv_path_MAGIC_CMD"; then + if test -n "$ac_tool_prefix"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for file" >&5 +$as_echo_n "checking for file... " >&6; } +if ${lt_cv_path_MAGIC_CMD+:} false; then : + $as_echo_n "(cached) " >&6 +else + case $MAGIC_CMD in +[\\/*] | ?:[\\/]*) + lt_cv_path_MAGIC_CMD="$MAGIC_CMD" # Let the user override the test with a path. + ;; +*) + lt_save_MAGIC_CMD="$MAGIC_CMD" + lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR + ac_dummy="/usr/bin$PATH_SEPARATOR$PATH" + for ac_dir in $ac_dummy; do + IFS="$lt_save_ifs" + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/file; then + lt_cv_path_MAGIC_CMD="$ac_dir/file" + if test -n "$file_magic_test_file"; then + case $deplibs_check_method in + "file_magic "*) + file_magic_regex=`expr "$deplibs_check_method" : "file_magic \(.*\)"` + MAGIC_CMD="$lt_cv_path_MAGIC_CMD" + if eval $file_magic_cmd \$file_magic_test_file 2> /dev/null | + $EGREP "$file_magic_regex" > /dev/null; then + : + else + cat <<_LT_EOF 1>&2 + +*** Warning: the command libtool uses to detect shared libraries, +*** $file_magic_cmd, produces output that libtool cannot recognize. +*** The result is that libtool may fail to recognize shared libraries +*** as such. This will affect the creation of libtool libraries that +*** depend on shared libraries, but programs linked with such libtool +*** libraries will work regardless of this problem. Nevertheless, you +*** may want to report the problem to your system manager and/or to +*** bug-libtool@gnu.org + +_LT_EOF + fi ;; + esac + fi + break + fi + done + IFS="$lt_save_ifs" + MAGIC_CMD="$lt_save_MAGIC_CMD" + ;; +esac +fi + +MAGIC_CMD="$lt_cv_path_MAGIC_CMD" +if test -n "$MAGIC_CMD"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $MAGIC_CMD" >&5 +$as_echo "$MAGIC_CMD" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + else + MAGIC_CMD=: + fi +fi + + fi + ;; +esac + +# Use C for the default configuration in the libtool script + +lt_save_CC="$CC" +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + +# Source file extension for C test sources. +ac_ext=c + +# Object file extension for compiled C test sources. +objext=o +objext=$objext + +# Code to be used in simple compile tests +lt_simple_compile_test_code="int some_variable = 0;" + +# Code to be used in simple link tests +lt_simple_link_test_code='int main(){return(0);}' + + + + + + + +# If no C compiler was specified, use CC. +LTCC=${LTCC-"$CC"} + +# If no C compiler flags were specified, use CFLAGS. +LTCFLAGS=${LTCFLAGS-"$CFLAGS"} + +# Allow CC to be a program name with arguments. +compiler=$CC + +# Save the default compiler, since it gets overwritten when the other +# tags are being tested, and _LT_TAGVAR(compiler, []) is a NOP. +compiler_DEFAULT=$CC + +# save warnings/boilerplate of simple test code +ac_outfile=conftest.$ac_objext +echo "$lt_simple_compile_test_code" >conftest.$ac_ext +eval "$ac_compile" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err +_lt_compiler_boilerplate=`cat conftest.err` +$RM conftest* + +ac_outfile=conftest.$ac_objext +echo "$lt_simple_link_test_code" >conftest.$ac_ext +eval "$ac_link" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err +_lt_linker_boilerplate=`cat conftest.err` +$RM -r conftest* + + +## CAVEAT EMPTOR: +## There is no encapsulation within the following macros, do not change +## the running order or otherwise move them around unless you know exactly +## what you are doing... +if test -n "$compiler"; then + +lt_prog_compiler_no_builtin_flag= + +if test "$GCC" = yes; then + case $cc_basename in + nvcc*) + lt_prog_compiler_no_builtin_flag=' -Xcompiler -fno-builtin' ;; + *) + lt_prog_compiler_no_builtin_flag=' -fno-builtin' ;; + esac + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler supports -fno-rtti -fno-exceptions" >&5 +$as_echo_n "checking if $compiler supports -fno-rtti -fno-exceptions... " >&6; } +if ${lt_cv_prog_compiler_rtti_exceptions+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_prog_compiler_rtti_exceptions=no + ac_outfile=conftest.$ac_objext + echo "$lt_simple_compile_test_code" > conftest.$ac_ext + lt_compiler_flag="-fno-rtti -fno-exceptions" + # Insert the option either (1) after the last *FLAGS variable, or + # (2) before a word containing "conftest.", or (3) at the end. + # Note that $ac_compile itself does not contain backslashes and begins + # with a dollar sign (not a hyphen), so the echo should work correctly. + # The option is referenced via a variable to avoid confusing sed. + lt_compile=`echo "$ac_compile" | $SED \ + -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ + -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ + -e 's:$: $lt_compiler_flag:'` + (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5) + (eval "$lt_compile" 2>conftest.err) + ac_status=$? + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + if (exit $ac_status) && test -s "$ac_outfile"; then + # The compiler can only warn and ignore the option if not recognized + # So say no if there are warnings other than the usual output. + $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' >conftest.exp + $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 + if test ! -s conftest.er2 || diff conftest.exp conftest.er2 >/dev/null; then + lt_cv_prog_compiler_rtti_exceptions=yes + fi + fi + $RM conftest* + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_rtti_exceptions" >&5 +$as_echo "$lt_cv_prog_compiler_rtti_exceptions" >&6; } + +if test x"$lt_cv_prog_compiler_rtti_exceptions" = xyes; then + lt_prog_compiler_no_builtin_flag="$lt_prog_compiler_no_builtin_flag -fno-rtti -fno-exceptions" +else + : +fi + +fi + + + + + + + lt_prog_compiler_wl= +lt_prog_compiler_pic= +lt_prog_compiler_static= + + + if test "$GCC" = yes; then + lt_prog_compiler_wl='-Wl,' + lt_prog_compiler_static='-static' + + case $host_os in + aix*) + # All AIX code is PIC. + if test "$host_cpu" = ia64; then + # AIX 5 now supports IA64 processor + lt_prog_compiler_static='-Bstatic' + fi + ;; + + amigaos*) + case $host_cpu in + powerpc) + # see comment about AmigaOS4 .so support + lt_prog_compiler_pic='-fPIC' + ;; + m68k) + # FIXME: we need at least 68020 code to build shared libraries, but + # adding the `-m68020' flag to GCC prevents building anything better, + # like `-m68040'. + lt_prog_compiler_pic='-m68020 -resident32 -malways-restore-a4' + ;; + esac + ;; + + beos* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*) + # PIC is the default for these OSes. + ;; + + mingw* | cygwin* | pw32* | os2* | cegcc*) + # This hack is so that the source file can tell whether it is being + # built for inclusion in a dll (and should export symbols for example). + # Although the cygwin gcc ignores -fPIC, still need this for old-style + # (--disable-auto-import) libraries + lt_prog_compiler_pic='-DDLL_EXPORT' + ;; + + darwin* | rhapsody*) + # PIC is the default on this platform + # Common symbols not allowed in MH_DYLIB files + lt_prog_compiler_pic='-fno-common' + ;; + + haiku*) + # PIC is the default for Haiku. + # The "-static" flag exists, but is broken. + lt_prog_compiler_static= + ;; + + hpux*) + # PIC is the default for 64-bit PA HP-UX, but not for 32-bit + # PA HP-UX. On IA64 HP-UX, PIC is the default but the pic flag + # sets the default TLS model and affects inlining. + case $host_cpu in + hppa*64*) + # +Z the default + ;; + *) + lt_prog_compiler_pic='-fPIC' + ;; + esac + ;; + + interix[3-9]*) + # Interix 3.x gcc -fpic/-fPIC options generate broken code. + # Instead, we relocate shared libraries at runtime. + ;; + + msdosdjgpp*) + # Just because we use GCC doesn't mean we suddenly get shared libraries + # on systems that don't support them. + lt_prog_compiler_can_build_shared=no + enable_shared=no + ;; + + *nto* | *qnx*) + # QNX uses GNU C++, but need to define -shared option too, otherwise + # it will coredump. + lt_prog_compiler_pic='-fPIC -shared' + ;; + + sysv4*MP*) + if test -d /usr/nec; then + lt_prog_compiler_pic=-Kconform_pic + fi + ;; + + *) + lt_prog_compiler_pic='-fPIC' + ;; + esac + + case $cc_basename in + nvcc*) # Cuda Compiler Driver 2.2 + lt_prog_compiler_wl='-Xlinker ' + if test -n "$lt_prog_compiler_pic"; then + lt_prog_compiler_pic="-Xcompiler $lt_prog_compiler_pic" + fi + ;; + esac + else + # PORTME Check for flag to pass linker flags through the system compiler. + case $host_os in + aix*) + lt_prog_compiler_wl='-Wl,' + if test "$host_cpu" = ia64; then + # AIX 5 now supports IA64 processor + lt_prog_compiler_static='-Bstatic' + else + lt_prog_compiler_static='-bnso -bI:/lib/syscalls.exp' + fi + ;; + + mingw* | cygwin* | pw32* | os2* | cegcc*) + # This hack is so that the source file can tell whether it is being + # built for inclusion in a dll (and should export symbols for example). + lt_prog_compiler_pic='-DDLL_EXPORT' + ;; + + hpux9* | hpux10* | hpux11*) + lt_prog_compiler_wl='-Wl,' + # PIC is the default for IA64 HP-UX and 64-bit HP-UX, but + # not for PA HP-UX. + case $host_cpu in + hppa*64*|ia64*) + # +Z the default + ;; + *) + lt_prog_compiler_pic='+Z' + ;; + esac + # Is there a better lt_prog_compiler_static that works with the bundled CC? + lt_prog_compiler_static='${wl}-a ${wl}archive' + ;; + + irix5* | irix6* | nonstopux*) + lt_prog_compiler_wl='-Wl,' + # PIC (with -KPIC) is the default. + lt_prog_compiler_static='-non_shared' + ;; + + linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) + case $cc_basename in + # old Intel for x86_64 which still supported -KPIC. + ecc*) + lt_prog_compiler_wl='-Wl,' + lt_prog_compiler_pic='-KPIC' + lt_prog_compiler_static='-static' + ;; + # icc used to be incompatible with GCC. + # ICC 10 doesn't accept -KPIC any more. + icc* | ifort*) + lt_prog_compiler_wl='-Wl,' + lt_prog_compiler_pic='-fPIC' + lt_prog_compiler_static='-static' + ;; + # Lahey Fortran 8.1. + lf95*) + lt_prog_compiler_wl='-Wl,' + lt_prog_compiler_pic='--shared' + lt_prog_compiler_static='--static' + ;; + nagfor*) + # NAG Fortran compiler + lt_prog_compiler_wl='-Wl,-Wl,,' + lt_prog_compiler_pic='-PIC' + lt_prog_compiler_static='-Bstatic' + ;; + pgcc* | pgf77* | pgf90* | pgf95* | pgfortran*) + # Portland Group compilers (*not* the Pentium gcc compiler, + # which looks to be a dead project) + lt_prog_compiler_wl='-Wl,' + lt_prog_compiler_pic='-fpic' + lt_prog_compiler_static='-Bstatic' + ;; + ccc*) + lt_prog_compiler_wl='-Wl,' + # All Alpha code is PIC. + lt_prog_compiler_static='-non_shared' + ;; + xl* | bgxl* | bgf* | mpixl*) + # IBM XL C 8.0/Fortran 10.1, 11.1 on PPC and BlueGene + lt_prog_compiler_wl='-Wl,' + lt_prog_compiler_pic='-qpic' + lt_prog_compiler_static='-qstaticlink' + ;; + *) + case `$CC -V 2>&1 | sed 5q` in + *Sun\ Ceres\ Fortran* | *Sun*Fortran*\ [1-7].* | *Sun*Fortran*\ 8.[0-3]*) + # Sun Fortran 8.3 passes all unrecognized flags to the linker + lt_prog_compiler_pic='-KPIC' + lt_prog_compiler_static='-Bstatic' + lt_prog_compiler_wl='' + ;; + *Sun\ F* | *Sun*Fortran*) + lt_prog_compiler_pic='-KPIC' + lt_prog_compiler_static='-Bstatic' + lt_prog_compiler_wl='-Qoption ld ' + ;; + *Sun\ C*) + # Sun C 5.9 + lt_prog_compiler_pic='-KPIC' + lt_prog_compiler_static='-Bstatic' + lt_prog_compiler_wl='-Wl,' + ;; + *Intel*\ [CF]*Compiler*) + lt_prog_compiler_wl='-Wl,' + lt_prog_compiler_pic='-fPIC' + lt_prog_compiler_static='-static' + ;; + *Portland\ Group*) + lt_prog_compiler_wl='-Wl,' + lt_prog_compiler_pic='-fpic' + lt_prog_compiler_static='-Bstatic' + ;; + esac + ;; + esac + ;; + + newsos6) + lt_prog_compiler_pic='-KPIC' + lt_prog_compiler_static='-Bstatic' + ;; + + *nto* | *qnx*) + # QNX uses GNU C++, but need to define -shared option too, otherwise + # it will coredump. + lt_prog_compiler_pic='-fPIC -shared' + ;; + + osf3* | osf4* | osf5*) + lt_prog_compiler_wl='-Wl,' + # All OSF/1 code is PIC. + lt_prog_compiler_static='-non_shared' + ;; + + rdos*) + lt_prog_compiler_static='-non_shared' + ;; + + solaris*) + lt_prog_compiler_pic='-KPIC' + lt_prog_compiler_static='-Bstatic' + case $cc_basename in + f77* | f90* | f95* | sunf77* | sunf90* | sunf95*) + lt_prog_compiler_wl='-Qoption ld ';; + *) + lt_prog_compiler_wl='-Wl,';; + esac + ;; + + sunos4*) + lt_prog_compiler_wl='-Qoption ld ' + lt_prog_compiler_pic='-PIC' + lt_prog_compiler_static='-Bstatic' + ;; + + sysv4 | sysv4.2uw2* | sysv4.3*) + lt_prog_compiler_wl='-Wl,' + lt_prog_compiler_pic='-KPIC' + lt_prog_compiler_static='-Bstatic' + ;; + + sysv4*MP*) + if test -d /usr/nec ;then + lt_prog_compiler_pic='-Kconform_pic' + lt_prog_compiler_static='-Bstatic' + fi + ;; + + sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*) + lt_prog_compiler_wl='-Wl,' + lt_prog_compiler_pic='-KPIC' + lt_prog_compiler_static='-Bstatic' + ;; + + unicos*) + lt_prog_compiler_wl='-Wl,' + lt_prog_compiler_can_build_shared=no + ;; + + uts4*) + lt_prog_compiler_pic='-pic' + lt_prog_compiler_static='-Bstatic' + ;; + + *) + lt_prog_compiler_can_build_shared=no + ;; + esac + fi + +case $host_os in + # For platforms which do not support PIC, -DPIC is meaningless: + *djgpp*) + lt_prog_compiler_pic= + ;; + *) + lt_prog_compiler_pic="$lt_prog_compiler_pic -DPIC" + ;; +esac + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $compiler option to produce PIC" >&5 +$as_echo_n "checking for $compiler option to produce PIC... " >&6; } +if ${lt_cv_prog_compiler_pic+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_prog_compiler_pic=$lt_prog_compiler_pic +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_pic" >&5 +$as_echo "$lt_cv_prog_compiler_pic" >&6; } +lt_prog_compiler_pic=$lt_cv_prog_compiler_pic + +# +# Check to make sure the PIC flag actually works. +# +if test -n "$lt_prog_compiler_pic"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler PIC flag $lt_prog_compiler_pic works" >&5 +$as_echo_n "checking if $compiler PIC flag $lt_prog_compiler_pic works... " >&6; } +if ${lt_cv_prog_compiler_pic_works+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_prog_compiler_pic_works=no + ac_outfile=conftest.$ac_objext + echo "$lt_simple_compile_test_code" > conftest.$ac_ext + lt_compiler_flag="$lt_prog_compiler_pic -DPIC" + # Insert the option either (1) after the last *FLAGS variable, or + # (2) before a word containing "conftest.", or (3) at the end. + # Note that $ac_compile itself does not contain backslashes and begins + # with a dollar sign (not a hyphen), so the echo should work correctly. + # The option is referenced via a variable to avoid confusing sed. + lt_compile=`echo "$ac_compile" | $SED \ + -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ + -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ + -e 's:$: $lt_compiler_flag:'` + (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5) + (eval "$lt_compile" 2>conftest.err) + ac_status=$? + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + if (exit $ac_status) && test -s "$ac_outfile"; then + # The compiler can only warn and ignore the option if not recognized + # So say no if there are warnings other than the usual output. + $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' >conftest.exp + $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 + if test ! -s conftest.er2 || diff conftest.exp conftest.er2 >/dev/null; then + lt_cv_prog_compiler_pic_works=yes + fi + fi + $RM conftest* + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_pic_works" >&5 +$as_echo "$lt_cv_prog_compiler_pic_works" >&6; } + +if test x"$lt_cv_prog_compiler_pic_works" = xyes; then + case $lt_prog_compiler_pic in + "" | " "*) ;; + *) lt_prog_compiler_pic=" $lt_prog_compiler_pic" ;; + esac +else + lt_prog_compiler_pic= + lt_prog_compiler_can_build_shared=no +fi + +fi + + + + + + + + + + + +# +# Check to make sure the static flag actually works. +# +wl=$lt_prog_compiler_wl eval lt_tmp_static_flag=\"$lt_prog_compiler_static\" +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler static flag $lt_tmp_static_flag works" >&5 +$as_echo_n "checking if $compiler static flag $lt_tmp_static_flag works... " >&6; } +if ${lt_cv_prog_compiler_static_works+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_prog_compiler_static_works=no + save_LDFLAGS="$LDFLAGS" + LDFLAGS="$LDFLAGS $lt_tmp_static_flag" + echo "$lt_simple_link_test_code" > conftest.$ac_ext + if (eval $ac_link 2>conftest.err) && test -s conftest$ac_exeext; then + # The linker can only warn and ignore the option if not recognized + # So say no if there are warnings + if test -s conftest.err; then + # Append any errors to the config.log. + cat conftest.err 1>&5 + $ECHO "$_lt_linker_boilerplate" | $SED '/^$/d' > conftest.exp + $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 + if diff conftest.exp conftest.er2 >/dev/null; then + lt_cv_prog_compiler_static_works=yes + fi + else + lt_cv_prog_compiler_static_works=yes + fi + fi + $RM -r conftest* + LDFLAGS="$save_LDFLAGS" + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_static_works" >&5 +$as_echo "$lt_cv_prog_compiler_static_works" >&6; } + +if test x"$lt_cv_prog_compiler_static_works" = xyes; then + : +else + lt_prog_compiler_static= +fi + + + + + + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler supports -c -o file.$ac_objext" >&5 +$as_echo_n "checking if $compiler supports -c -o file.$ac_objext... " >&6; } +if ${lt_cv_prog_compiler_c_o+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_prog_compiler_c_o=no + $RM -r conftest 2>/dev/null + mkdir conftest + cd conftest + mkdir out + echo "$lt_simple_compile_test_code" > conftest.$ac_ext + + lt_compiler_flag="-o out/conftest2.$ac_objext" + # Insert the option either (1) after the last *FLAGS variable, or + # (2) before a word containing "conftest.", or (3) at the end. + # Note that $ac_compile itself does not contain backslashes and begins + # with a dollar sign (not a hyphen), so the echo should work correctly. + lt_compile=`echo "$ac_compile" | $SED \ + -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ + -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ + -e 's:$: $lt_compiler_flag:'` + (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5) + (eval "$lt_compile" 2>out/conftest.err) + ac_status=$? + cat out/conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + if (exit $ac_status) && test -s out/conftest2.$ac_objext + then + # The compiler can only warn and ignore the option if not recognized + # So say no if there are warnings + $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' > out/conftest.exp + $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2 + if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then + lt_cv_prog_compiler_c_o=yes + fi + fi + chmod u+w . 2>&5 + $RM conftest* + # SGI C++ compiler will create directory out/ii_files/ for + # template instantiation + test -d out/ii_files && $RM out/ii_files/* && rmdir out/ii_files + $RM out/* && rmdir out + cd .. + $RM -r conftest + $RM conftest* + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_c_o" >&5 +$as_echo "$lt_cv_prog_compiler_c_o" >&6; } + + + + + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler supports -c -o file.$ac_objext" >&5 +$as_echo_n "checking if $compiler supports -c -o file.$ac_objext... " >&6; } +if ${lt_cv_prog_compiler_c_o+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_prog_compiler_c_o=no + $RM -r conftest 2>/dev/null + mkdir conftest + cd conftest + mkdir out + echo "$lt_simple_compile_test_code" > conftest.$ac_ext + + lt_compiler_flag="-o out/conftest2.$ac_objext" + # Insert the option either (1) after the last *FLAGS variable, or + # (2) before a word containing "conftest.", or (3) at the end. + # Note that $ac_compile itself does not contain backslashes and begins + # with a dollar sign (not a hyphen), so the echo should work correctly. + lt_compile=`echo "$ac_compile" | $SED \ + -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ + -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ + -e 's:$: $lt_compiler_flag:'` + (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5) + (eval "$lt_compile" 2>out/conftest.err) + ac_status=$? + cat out/conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + if (exit $ac_status) && test -s out/conftest2.$ac_objext + then + # The compiler can only warn and ignore the option if not recognized + # So say no if there are warnings + $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' > out/conftest.exp + $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2 + if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then + lt_cv_prog_compiler_c_o=yes + fi + fi + chmod u+w . 2>&5 + $RM conftest* + # SGI C++ compiler will create directory out/ii_files/ for + # template instantiation + test -d out/ii_files && $RM out/ii_files/* && rmdir out/ii_files + $RM out/* && rmdir out + cd .. + $RM -r conftest + $RM conftest* + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_c_o" >&5 +$as_echo "$lt_cv_prog_compiler_c_o" >&6; } + + + + +hard_links="nottested" +if test "$lt_cv_prog_compiler_c_o" = no && test "$need_locks" != no; then + # do not overwrite the value of need_locks provided by the user + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if we can lock with hard links" >&5 +$as_echo_n "checking if we can lock with hard links... " >&6; } + hard_links=yes + $RM conftest* + ln conftest.a conftest.b 2>/dev/null && hard_links=no + touch conftest.a + ln conftest.a conftest.b 2>&5 || hard_links=no + ln conftest.a conftest.b 2>/dev/null && hard_links=no + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $hard_links" >&5 +$as_echo "$hard_links" >&6; } + if test "$hard_links" = no; then + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: \`$CC' does not support \`-c -o', so \`make -j' may be unsafe" >&5 +$as_echo "$as_me: WARNING: \`$CC' does not support \`-c -o', so \`make -j' may be unsafe" >&2;} + need_locks=warn + fi +else + need_locks=no +fi + + + + + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the $compiler linker ($LD) supports shared libraries" >&5 +$as_echo_n "checking whether the $compiler linker ($LD) supports shared libraries... " >&6; } + + runpath_var= + allow_undefined_flag= + always_export_symbols=no + archive_cmds= + archive_expsym_cmds= + compiler_needs_object=no + enable_shared_with_static_runtimes=no + export_dynamic_flag_spec= + export_symbols_cmds='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' + hardcode_automatic=no + hardcode_direct=no + hardcode_direct_absolute=no + hardcode_libdir_flag_spec= + hardcode_libdir_separator= + hardcode_minus_L=no + hardcode_shlibpath_var=unsupported + inherit_rpath=no + link_all_deplibs=unknown + module_cmds= + module_expsym_cmds= + old_archive_from_new_cmds= + old_archive_from_expsyms_cmds= + thread_safe_flag_spec= + whole_archive_flag_spec= + # include_expsyms should be a list of space-separated symbols to be *always* + # included in the symbol list + include_expsyms= + # exclude_expsyms can be an extended regexp of symbols to exclude + # it will be wrapped by ` (' and `)$', so one must not match beginning or + # end of line. Example: `a|bc|.*d.*' will exclude the symbols `a' and `bc', + # as well as any symbol that contains `d'. + exclude_expsyms='_GLOBAL_OFFSET_TABLE_|_GLOBAL__F[ID]_.*' + # Although _GLOBAL_OFFSET_TABLE_ is a valid symbol C name, most a.out + # platforms (ab)use it in PIC code, but their linkers get confused if + # the symbol is explicitly referenced. Since portable code cannot + # rely on this symbol name, it's probably fine to never include it in + # preloaded symbol tables. + # Exclude shared library initialization/finalization symbols. + extract_expsyms_cmds= + + case $host_os in + cygwin* | mingw* | pw32* | cegcc*) + # FIXME: the MSVC++ port hasn't been tested in a loooong time + # When not using gcc, we currently assume that we are using + # Microsoft Visual C++. + if test "$GCC" != yes; then + with_gnu_ld=no + fi + ;; + interix*) + # we just hope/assume this is gcc and not c89 (= MSVC++) + with_gnu_ld=yes + ;; + openbsd*) + with_gnu_ld=no + ;; + linux* | k*bsd*-gnu | gnu*) + link_all_deplibs=no + ;; + esac + + ld_shlibs=yes + + # On some targets, GNU ld is compatible enough with the native linker + # that we're better off using the native interface for both. + lt_use_gnu_ld_interface=no + if test "$with_gnu_ld" = yes; then + case $host_os in + aix*) + # The AIX port of GNU ld has always aspired to compatibility + # with the native linker. However, as the warning in the GNU ld + # block says, versions before 2.19.5* couldn't really create working + # shared libraries, regardless of the interface used. + case `$LD -v 2>&1` in + *\ \(GNU\ Binutils\)\ 2.19.5*) ;; + *\ \(GNU\ Binutils\)\ 2.[2-9]*) ;; + *\ \(GNU\ Binutils\)\ [3-9]*) ;; + *) + lt_use_gnu_ld_interface=yes + ;; + esac + ;; + *) + lt_use_gnu_ld_interface=yes + ;; + esac + fi + + if test "$lt_use_gnu_ld_interface" = yes; then + # If archive_cmds runs LD, not CC, wlarc should be empty + wlarc='${wl}' + + # Set some defaults for GNU ld with shared library support. These + # are reset later if shared libraries are not supported. Putting them + # here allows them to be overridden if necessary. + runpath_var=LD_RUN_PATH + hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' + export_dynamic_flag_spec='${wl}--export-dynamic' + # ancient GNU ld didn't support --whole-archive et. al. + if $LD --help 2>&1 | $GREP 'no-whole-archive' > /dev/null; then + whole_archive_flag_spec="$wlarc"'--whole-archive$convenience '"$wlarc"'--no-whole-archive' + else + whole_archive_flag_spec= + fi + supports_anon_versioning=no + case `$LD -v 2>&1` in + *GNU\ gold*) supports_anon_versioning=yes ;; + *\ [01].* | *\ 2.[0-9].* | *\ 2.10.*) ;; # catch versions < 2.11 + *\ 2.11.93.0.2\ *) supports_anon_versioning=yes ;; # RH7.3 ... + *\ 2.11.92.0.12\ *) supports_anon_versioning=yes ;; # Mandrake 8.2 ... + *\ 2.11.*) ;; # other 2.11 versions + *) supports_anon_versioning=yes ;; + esac + + # See if GNU ld supports shared libraries. + case $host_os in + aix[3-9]*) + # On AIX/PPC, the GNU linker is very broken + if test "$host_cpu" != ia64; then + ld_shlibs=no + cat <<_LT_EOF 1>&2 + +*** Warning: the GNU linker, at least up to release 2.19, is reported +*** to be unable to reliably create shared libraries on AIX. +*** Therefore, libtool is disabling shared libraries support. If you +*** really care for shared libraries, you may want to install binutils +*** 2.20 or above, or modify your PATH so that a non-GNU linker is found. +*** You will then need to restart the configuration process. + +_LT_EOF + fi + ;; + + amigaos*) + case $host_cpu in + powerpc) + # see comment about AmigaOS4 .so support + archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + archive_expsym_cmds='' + ;; + m68k) + archive_cmds='$RM $output_objdir/a2ixlibrary.data~$ECHO "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$ECHO "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$ECHO "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$ECHO "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)' + hardcode_libdir_flag_spec='-L$libdir' + hardcode_minus_L=yes + ;; + esac + ;; + + beos*) + if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then + allow_undefined_flag=unsupported + # Joseph Beckenbach says some releases of gcc + # support --undefined. This deserves some investigation. FIXME + archive_cmds='$CC -nostart $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + else + ld_shlibs=no + fi + ;; + + cygwin* | mingw* | pw32* | cegcc*) + # _LT_TAGVAR(hardcode_libdir_flag_spec, ) is actually meaningless, + # as there is no search path for DLLs. + hardcode_libdir_flag_spec='-L$libdir' + export_dynamic_flag_spec='${wl}--export-all-symbols' + allow_undefined_flag=unsupported + always_export_symbols=no + enable_shared_with_static_runtimes=yes + export_symbols_cmds='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[BCDGRS][ ]/s/.*[ ]\([^ ]*\)/\1 DATA/;s/^.*[ ]__nm__\([^ ]*\)[ ][^ ]*/\1 DATA/;/^I[ ]/d;/^[AITW][ ]/s/.* //'\'' | sort | uniq > $export_symbols' + exclude_expsyms='[_]+GLOBAL_OFFSET_TABLE_|[_]+GLOBAL__[FID]_.*|[_]+head_[A-Za-z0-9_]+_dll|[A-Za-z0-9_]+_dll_iname' + + if $LD --help 2>&1 | $GREP 'auto-import' > /dev/null; then + archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' + # If the export-symbols file already is a .def file (1st line + # is EXPORTS), use it as is; otherwise, prepend... + archive_expsym_cmds='if test "x`$SED 1q $export_symbols`" = xEXPORTS; then + cp $export_symbols $output_objdir/$soname.def; + else + echo EXPORTS > $output_objdir/$soname.def; + cat $export_symbols >> $output_objdir/$soname.def; + fi~ + $CC -shared $output_objdir/$soname.def $libobjs $deplibs $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' + else + ld_shlibs=no + fi + ;; + + haiku*) + archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + link_all_deplibs=yes + ;; + + interix[3-9]*) + hardcode_direct=no + hardcode_shlibpath_var=no + hardcode_libdir_flag_spec='${wl}-rpath,$libdir' + export_dynamic_flag_spec='${wl}-E' + # Hack: On Interix 3.x, we cannot compile PIC because of a broken gcc. + # Instead, shared libraries are loaded at an image base (0x10000000 by + # default) and relocated if they conflict, which is a slow very memory + # consuming and fragmenting process. To avoid this, we pick a random, + # 256 KiB-aligned image base between 0x50000000 and 0x6FFC0000 at link + # time. Moving up from 0x10000000 also allows more sbrk(2) space. + archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' + archive_expsym_cmds='sed "s,^,_," $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--retain-symbols-file,$output_objdir/$soname.expsym ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' + ;; + + gnu* | linux* | tpf* | k*bsd*-gnu | kopensolaris*-gnu) + tmp_diet=no + if test "$host_os" = linux-dietlibc; then + case $cc_basename in + diet\ *) tmp_diet=yes;; # linux-dietlibc with static linking (!diet-dyn) + esac + fi + if $LD --help 2>&1 | $EGREP ': supported targets:.* elf' > /dev/null \ + && test "$tmp_diet" = no + then + tmp_addflag=' $pic_flag' + tmp_sharedflag='-shared' + case $cc_basename,$host_cpu in + pgcc*) # Portland Group C compiler + whole_archive_flag_spec='${wl}--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` ${wl}--no-whole-archive' + tmp_addflag=' $pic_flag' + ;; + pgf77* | pgf90* | pgf95* | pgfortran*) + # Portland Group f77 and f90 compilers + whole_archive_flag_spec='${wl}--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` ${wl}--no-whole-archive' + tmp_addflag=' $pic_flag -Mnomain' ;; + ecc*,ia64* | icc*,ia64*) # Intel C compiler on ia64 + tmp_addflag=' -i_dynamic' ;; + efc*,ia64* | ifort*,ia64*) # Intel Fortran compiler on ia64 + tmp_addflag=' -i_dynamic -nofor_main' ;; + ifc* | ifort*) # Intel Fortran compiler + tmp_addflag=' -nofor_main' ;; + lf95*) # Lahey Fortran 8.1 + whole_archive_flag_spec= + tmp_sharedflag='--shared' ;; + xl[cC]* | bgxl[cC]* | mpixl[cC]*) # IBM XL C 8.0 on PPC (deal with xlf below) + tmp_sharedflag='-qmkshrobj' + tmp_addflag= ;; + nvcc*) # Cuda Compiler Driver 2.2 + whole_archive_flag_spec='${wl}--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` ${wl}--no-whole-archive' + compiler_needs_object=yes + ;; + esac + case `$CC -V 2>&1 | sed 5q` in + *Sun\ C*) # Sun C 5.9 + whole_archive_flag_spec='${wl}--whole-archive`new_convenience=; for conv in $convenience\"\"; do test -z \"$conv\" || new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` ${wl}--no-whole-archive' + compiler_needs_object=yes + tmp_sharedflag='-G' ;; + *Sun\ F*) # Sun Fortran 8.3 + tmp_sharedflag='-G' ;; + esac + archive_cmds='$CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + + if test "x$supports_anon_versioning" = xyes; then + archive_expsym_cmds='echo "{ global:" > $output_objdir/$libname.ver~ + cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ + echo "local: *; };" >> $output_objdir/$libname.ver~ + $CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-version-script ${wl}$output_objdir/$libname.ver -o $lib' + fi + + case $cc_basename in + xlf* | bgf* | bgxlf* | mpixlf*) + # IBM XL Fortran 10.1 on PPC cannot create shared libs itself + whole_archive_flag_spec='--whole-archive$convenience --no-whole-archive' + hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' + archive_cmds='$LD -shared $libobjs $deplibs $linker_flags -soname $soname -o $lib' + if test "x$supports_anon_versioning" = xyes; then + archive_expsym_cmds='echo "{ global:" > $output_objdir/$libname.ver~ + cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ + echo "local: *; };" >> $output_objdir/$libname.ver~ + $LD -shared $libobjs $deplibs $linker_flags -soname $soname -version-script $output_objdir/$libname.ver -o $lib' + fi + ;; + esac + else + ld_shlibs=no + fi + ;; + + netbsd* | netbsdelf*-gnu) + if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then + archive_cmds='$LD -Bshareable $libobjs $deplibs $linker_flags -o $lib' + wlarc= + else + archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + archive_expsym_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' + fi + ;; + + solaris*) + if $LD -v 2>&1 | $GREP 'BFD 2\.8' > /dev/null; then + ld_shlibs=no + cat <<_LT_EOF 1>&2 + +*** Warning: The releases 2.8.* of the GNU linker cannot reliably +*** create shared libraries on Solaris systems. Therefore, libtool +*** is disabling shared libraries support. We urge you to upgrade GNU +*** binutils to release 2.9.1 or newer. Another option is to modify +*** your PATH or compiler configuration so that the native linker is +*** used, and then restart. + +_LT_EOF + elif $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then + archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + archive_expsym_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' + else + ld_shlibs=no + fi + ;; + + sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX*) + case `$LD -v 2>&1` in + *\ [01].* | *\ 2.[0-9].* | *\ 2.1[0-5].*) + ld_shlibs=no + cat <<_LT_EOF 1>&2 + +*** Warning: Releases of the GNU linker prior to 2.16.91.0.3 can not +*** reliably create shared libraries on SCO systems. Therefore, libtool +*** is disabling shared libraries support. We urge you to upgrade GNU +*** binutils to release 2.16.91.0.3 or newer. Another option is to modify +*** your PATH or compiler configuration so that the native linker is +*** used, and then restart. + +_LT_EOF + ;; + *) + # For security reasons, it is highly recommended that you always + # use absolute paths for naming shared libraries, and exclude the + # DT_RUNPATH tag from executables and libraries. But doing so + # requires that you compile everything twice, which is a pain. + if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then + hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' + archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + archive_expsym_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' + else + ld_shlibs=no + fi + ;; + esac + ;; + + sunos4*) + archive_cmds='$LD -assert pure-text -Bshareable -o $lib $libobjs $deplibs $linker_flags' + wlarc= + hardcode_direct=yes + hardcode_shlibpath_var=no + ;; + + *) + if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then + archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + archive_expsym_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' + else + ld_shlibs=no + fi + ;; + esac + + if test "$ld_shlibs" = no; then + runpath_var= + hardcode_libdir_flag_spec= + export_dynamic_flag_spec= + whole_archive_flag_spec= + fi + else + # PORTME fill in a description of your system's linker (not GNU ld) + case $host_os in + aix3*) + allow_undefined_flag=unsupported + always_export_symbols=yes + archive_expsym_cmds='$LD -o $output_objdir/$soname $libobjs $deplibs $linker_flags -bE:$export_symbols -T512 -H512 -bM:SRE~$AR $AR_FLAGS $lib $output_objdir/$soname' + # Note: this linker hardcodes the directories in LIBPATH if there + # are no directories specified by -L. + hardcode_minus_L=yes + if test "$GCC" = yes && test -z "$lt_prog_compiler_static"; then + # Neither direct hardcoding nor static linking is supported with a + # broken collect2. + hardcode_direct=unsupported + fi + ;; + + aix[4-9]*) + if test "$host_cpu" = ia64; then + # On IA64, the linker does run time linking by default, so we don't + # have to do anything special. + aix_use_runtimelinking=no + exp_sym_flag='-Bexport' + no_entry_flag="" + else + # If we're using GNU nm, then we don't want the "-C" option. + # -C means demangle to AIX nm, but means don't demangle with GNU nm + # Also, AIX nm treats weak defined symbols like other global + # defined symbols, whereas GNU nm marks them as "W". + if $NM -V 2>&1 | $GREP 'GNU' > /dev/null; then + export_symbols_cmds='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W")) && (substr(\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols' + else + export_symbols_cmds='$NM -BCpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B")) && (substr(\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols' + fi + aix_use_runtimelinking=no + + # Test if we are trying to use run time linking or normal + # AIX style linking. If -brtl is somewhere in LDFLAGS, we + # need to do runtime linking. + case $host_os in aix4.[23]|aix4.[23].*|aix[5-9]*) + for ld_flag in $LDFLAGS; do + if (test $ld_flag = "-brtl" || test $ld_flag = "-Wl,-brtl"); then + aix_use_runtimelinking=yes + break + fi + done + ;; + esac + + exp_sym_flag='-bexport' + no_entry_flag='-bnoentry' + fi + + # When large executables or shared objects are built, AIX ld can + # have problems creating the table of contents. If linking a library + # or program results in "error TOC overflow" add -mminimal-toc to + # CXXFLAGS/CFLAGS for g++/gcc. In the cases where that is not + # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS. + + archive_cmds='' + hardcode_direct=yes + hardcode_direct_absolute=yes + hardcode_libdir_separator=':' + link_all_deplibs=yes + file_list_spec='${wl}-f,' + + if test "$GCC" = yes; then + case $host_os in aix4.[012]|aix4.[012].*) + # We only want to do this on AIX 4.2 and lower, the check + # below for broken collect2 doesn't work under 4.3+ + collect2name=`${CC} -print-prog-name=collect2` + if test -f "$collect2name" && + strings "$collect2name" | $GREP resolve_lib_name >/dev/null + then + # We have reworked collect2 + : + else + # We have old collect2 + hardcode_direct=unsupported + # It fails to find uninstalled libraries when the uninstalled + # path is not listed in the libpath. Setting hardcode_minus_L + # to unsupported forces relinking + hardcode_minus_L=yes + hardcode_libdir_flag_spec='-L$libdir' + hardcode_libdir_separator= + fi + ;; + esac + shared_flag='-shared' + if test "$aix_use_runtimelinking" = yes; then + shared_flag="$shared_flag "'${wl}-G' + fi + link_all_deplibs=no + else + # not using gcc + if test "$host_cpu" = ia64; then + # VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release + # chokes on -Wl,-G. The following line is correct: + shared_flag='-G' + else + if test "$aix_use_runtimelinking" = yes; then + shared_flag='${wl}-G' + else + shared_flag='${wl}-bM:SRE' + fi + fi + fi + + export_dynamic_flag_spec='${wl}-bexpall' + # It seems that -bexpall does not export symbols beginning with + # underscore (_), so it is better to generate a list of symbols to export. + always_export_symbols=yes + if test "$aix_use_runtimelinking" = yes; then + # Warning - without using the other runtime loading flags (-brtl), + # -berok will link without error, but may produce a broken library. + allow_undefined_flag='-berok' + # Determine the default libpath from the value encoded in an + # empty executable. + if test "${lt_cv_aix_libpath+set}" = set; then + aix_libpath=$lt_cv_aix_libpath +else + if ${lt_cv_aix_libpath_+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + + lt_aix_libpath_sed=' + /Import File Strings/,/^$/ { + /^0/ { + s/^0 *\([^ ]*\) *$/\1/ + p + } + }' + lt_cv_aix_libpath_=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` + # Check for a 64-bit object if we didn't find anything. + if test -z "$lt_cv_aix_libpath_"; then + lt_cv_aix_libpath_=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` + fi +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + if test -z "$lt_cv_aix_libpath_"; then + lt_cv_aix_libpath_="/usr/lib:/lib" + fi + +fi + + aix_libpath=$lt_cv_aix_libpath_ +fi + + hardcode_libdir_flag_spec='${wl}-blibpath:$libdir:'"$aix_libpath" + archive_expsym_cmds='$CC -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags `if test "x${allow_undefined_flag}" != "x"; then func_echo_all "${wl}${allow_undefined_flag}"; else :; fi` '"\${wl}$exp_sym_flag:\$export_symbols $shared_flag" + else + if test "$host_cpu" = ia64; then + hardcode_libdir_flag_spec='${wl}-R $libdir:/usr/lib:/lib' + allow_undefined_flag="-z nodefs" + archive_expsym_cmds="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags ${wl}${allow_undefined_flag} '"\${wl}$exp_sym_flag:\$export_symbols" + else + # Determine the default libpath from the value encoded in an + # empty executable. + if test "${lt_cv_aix_libpath+set}" = set; then + aix_libpath=$lt_cv_aix_libpath +else + if ${lt_cv_aix_libpath_+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + + lt_aix_libpath_sed=' + /Import File Strings/,/^$/ { + /^0/ { + s/^0 *\([^ ]*\) *$/\1/ + p + } + }' + lt_cv_aix_libpath_=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` + # Check for a 64-bit object if we didn't find anything. + if test -z "$lt_cv_aix_libpath_"; then + lt_cv_aix_libpath_=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` + fi +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + if test -z "$lt_cv_aix_libpath_"; then + lt_cv_aix_libpath_="/usr/lib:/lib" + fi + +fi + + aix_libpath=$lt_cv_aix_libpath_ +fi + + hardcode_libdir_flag_spec='${wl}-blibpath:$libdir:'"$aix_libpath" + # Warning - without using the other run time loading flags, + # -berok will link without error, but may produce a broken library. + no_undefined_flag=' ${wl}-bernotok' + allow_undefined_flag=' ${wl}-berok' + if test "$with_gnu_ld" = yes; then + # We only use this code for GNU lds that support --whole-archive. + whole_archive_flag_spec='${wl}--whole-archive$convenience ${wl}--no-whole-archive' + else + # Exported symbols can be pulled into shared objects from archives + whole_archive_flag_spec='$convenience' + fi + archive_cmds_need_lc=yes + # This is similar to how AIX traditionally builds its shared libraries. + archive_expsym_cmds="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs ${wl}-bnoentry $compiler_flags ${wl}-bE:$export_symbols${allow_undefined_flag}~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$soname' + fi + fi + ;; + + amigaos*) + case $host_cpu in + powerpc) + # see comment about AmigaOS4 .so support + archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + archive_expsym_cmds='' + ;; + m68k) + archive_cmds='$RM $output_objdir/a2ixlibrary.data~$ECHO "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$ECHO "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$ECHO "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$ECHO "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)' + hardcode_libdir_flag_spec='-L$libdir' + hardcode_minus_L=yes + ;; + esac + ;; + + bsdi[45]*) + export_dynamic_flag_spec=-rdynamic + ;; + + cygwin* | mingw* | pw32* | cegcc*) + # When not using gcc, we currently assume that we are using + # Microsoft Visual C++. + # hardcode_libdir_flag_spec is actually meaningless, as there is + # no search path for DLLs. + case $cc_basename in + cl*) + # Native MSVC + hardcode_libdir_flag_spec=' ' + allow_undefined_flag=unsupported + always_export_symbols=yes + file_list_spec='@' + # Tell ltmain to make .lib files, not .a files. + libext=lib + # Tell ltmain to make .dll files, not .so files. + shrext_cmds=".dll" + # FIXME: Setting linknames here is a bad hack. + archive_cmds='$CC -o $output_objdir/$soname $libobjs $compiler_flags $deplibs -Wl,-dll~linknames=' + archive_expsym_cmds='if test "x`$SED 1q $export_symbols`" = xEXPORTS; then + sed -n -e 's/\\\\\\\(.*\\\\\\\)/-link\\\ -EXPORT:\\\\\\\1/' -e '1\\\!p' < $export_symbols > $output_objdir/$soname.exp; + else + sed -e 's/\\\\\\\(.*\\\\\\\)/-link\\\ -EXPORT:\\\\\\\1/' < $export_symbols > $output_objdir/$soname.exp; + fi~ + $CC -o $tool_output_objdir$soname $libobjs $compiler_flags $deplibs "@$tool_output_objdir$soname.exp" -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~ + linknames=' + # The linker will not automatically build a static lib if we build a DLL. + # _LT_TAGVAR(old_archive_from_new_cmds, )='true' + enable_shared_with_static_runtimes=yes + exclude_expsyms='_NULL_IMPORT_DESCRIPTOR|_IMPORT_DESCRIPTOR_.*' + export_symbols_cmds='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[BCDGRS][ ]/s/.*[ ]\([^ ]*\)/\1,DATA/'\'' | $SED -e '\''/^[AITW][ ]/s/.*[ ]//'\'' | sort | uniq > $export_symbols' + # Don't use ranlib + old_postinstall_cmds='chmod 644 $oldlib' + postlink_cmds='lt_outputfile="@OUTPUT@"~ + lt_tool_outputfile="@TOOL_OUTPUT@"~ + case $lt_outputfile in + *.exe|*.EXE) ;; + *) + lt_outputfile="$lt_outputfile.exe" + lt_tool_outputfile="$lt_tool_outputfile.exe" + ;; + esac~ + if test "$MANIFEST_TOOL" != ":" && test -f "$lt_outputfile.manifest"; then + $MANIFEST_TOOL -manifest "$lt_tool_outputfile.manifest" -outputresource:"$lt_tool_outputfile" || exit 1; + $RM "$lt_outputfile.manifest"; + fi' + ;; + *) + # Assume MSVC wrapper + hardcode_libdir_flag_spec=' ' + allow_undefined_flag=unsupported + # Tell ltmain to make .lib files, not .a files. + libext=lib + # Tell ltmain to make .dll files, not .so files. + shrext_cmds=".dll" + # FIXME: Setting linknames here is a bad hack. + archive_cmds='$CC -o $lib $libobjs $compiler_flags `func_echo_all "$deplibs" | $SED '\''s/ -lc$//'\''` -link -dll~linknames=' + # The linker will automatically build a .lib file if we build a DLL. + old_archive_from_new_cmds='true' + # FIXME: Should let the user specify the lib program. + old_archive_cmds='lib -OUT:$oldlib$oldobjs$old_deplibs' + enable_shared_with_static_runtimes=yes + ;; + esac + ;; + + darwin* | rhapsody*) + + + archive_cmds_need_lc=no + hardcode_direct=no + hardcode_automatic=yes + hardcode_shlibpath_var=unsupported + if test "$lt_cv_ld_force_load" = "yes"; then + whole_archive_flag_spec='`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience ${wl}-force_load,$conv\"; done; func_echo_all \"$new_convenience\"`' + + else + whole_archive_flag_spec='' + fi + link_all_deplibs=yes + allow_undefined_flag="$_lt_dar_allow_undefined" + case $cc_basename in + ifort*) _lt_dar_can_shared=yes ;; + *) _lt_dar_can_shared=$GCC ;; + esac + if test "$_lt_dar_can_shared" = "yes"; then + output_verbose_link_cmd=func_echo_all + archive_cmds="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod${_lt_dsymutil}" + module_cmds="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dsymutil}" + archive_expsym_cmds="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring ${_lt_dar_single_mod}${_lt_dar_export_syms}${_lt_dsymutil}" + module_expsym_cmds="sed -e 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dar_export_syms}${_lt_dsymutil}" + + else + ld_shlibs=no + fi + + ;; + + dgux*) + archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + hardcode_libdir_flag_spec='-L$libdir' + hardcode_shlibpath_var=no + ;; + + # FreeBSD 2.2.[012] allows us to include c++rt0.o to get C++ constructor + # support. Future versions do this automatically, but an explicit c++rt0.o + # does not break anything, and helps significantly (at the cost of a little + # extra space). + freebsd2.2*) + archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags /usr/lib/c++rt0.o' + hardcode_libdir_flag_spec='-R$libdir' + hardcode_direct=yes + hardcode_shlibpath_var=no + ;; + + # Unfortunately, older versions of FreeBSD 2 do not have this feature. + freebsd2.*) + archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' + hardcode_direct=yes + hardcode_minus_L=yes + hardcode_shlibpath_var=no + ;; + + # FreeBSD 3 and greater uses gcc -shared to do shared libraries. + freebsd* | dragonfly*) + archive_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' + hardcode_libdir_flag_spec='-R$libdir' + hardcode_direct=yes + hardcode_shlibpath_var=no + ;; + + hpux9*) + if test "$GCC" = yes; then + archive_cmds='$RM $output_objdir/$soname~$CC -shared $pic_flag ${wl}+b ${wl}$install_libdir -o $output_objdir/$soname $libobjs $deplibs $compiler_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib' + else + archive_cmds='$RM $output_objdir/$soname~$LD -b +b $install_libdir -o $output_objdir/$soname $libobjs $deplibs $linker_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib' + fi + hardcode_libdir_flag_spec='${wl}+b ${wl}$libdir' + hardcode_libdir_separator=: + hardcode_direct=yes + + # hardcode_minus_L: Not really in the search PATH, + # but as the default location of the library. + hardcode_minus_L=yes + export_dynamic_flag_spec='${wl}-E' + ;; + + hpux10*) + if test "$GCC" = yes && test "$with_gnu_ld" = no; then + archive_cmds='$CC -shared $pic_flag ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags' + else + archive_cmds='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags' + fi + if test "$with_gnu_ld" = no; then + hardcode_libdir_flag_spec='${wl}+b ${wl}$libdir' + hardcode_libdir_separator=: + hardcode_direct=yes + hardcode_direct_absolute=yes + export_dynamic_flag_spec='${wl}-E' + # hardcode_minus_L: Not really in the search PATH, + # but as the default location of the library. + hardcode_minus_L=yes + fi + ;; + + hpux11*) + if test "$GCC" = yes && test "$with_gnu_ld" = no; then + case $host_cpu in + hppa*64*) + archive_cmds='$CC -shared ${wl}+h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags' + ;; + ia64*) + archive_cmds='$CC -shared $pic_flag ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags' + ;; + *) + archive_cmds='$CC -shared $pic_flag ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags' + ;; + esac + else + case $host_cpu in + hppa*64*) + archive_cmds='$CC -b ${wl}+h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags' + ;; + ia64*) + archive_cmds='$CC -b ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags' + ;; + *) + + # Older versions of the 11.00 compiler do not understand -b yet + # (HP92453-01 A.11.01.20 doesn't, HP92453-01 B.11.X.35175-35176.GP does) + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $CC understands -b" >&5 +$as_echo_n "checking if $CC understands -b... " >&6; } +if ${lt_cv_prog_compiler__b+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_prog_compiler__b=no + save_LDFLAGS="$LDFLAGS" + LDFLAGS="$LDFLAGS -b" + echo "$lt_simple_link_test_code" > conftest.$ac_ext + if (eval $ac_link 2>conftest.err) && test -s conftest$ac_exeext; then + # The linker can only warn and ignore the option if not recognized + # So say no if there are warnings + if test -s conftest.err; then + # Append any errors to the config.log. + cat conftest.err 1>&5 + $ECHO "$_lt_linker_boilerplate" | $SED '/^$/d' > conftest.exp + $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 + if diff conftest.exp conftest.er2 >/dev/null; then + lt_cv_prog_compiler__b=yes + fi + else + lt_cv_prog_compiler__b=yes + fi + fi + $RM -r conftest* + LDFLAGS="$save_LDFLAGS" + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler__b" >&5 +$as_echo "$lt_cv_prog_compiler__b" >&6; } + +if test x"$lt_cv_prog_compiler__b" = xyes; then + archive_cmds='$CC -b ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags' +else + archive_cmds='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags' +fi + + ;; + esac + fi + if test "$with_gnu_ld" = no; then + hardcode_libdir_flag_spec='${wl}+b ${wl}$libdir' + hardcode_libdir_separator=: + + case $host_cpu in + hppa*64*|ia64*) + hardcode_direct=no + hardcode_shlibpath_var=no + ;; + *) + hardcode_direct=yes + hardcode_direct_absolute=yes + export_dynamic_flag_spec='${wl}-E' + + # hardcode_minus_L: Not really in the search PATH, + # but as the default location of the library. + hardcode_minus_L=yes + ;; + esac + fi + ;; + + irix5* | irix6* | nonstopux*) + if test "$GCC" = yes; then + archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' + # Try to use the -exported_symbol ld option, if it does not + # work, assume that -exports_file does not work either and + # implicitly export all symbols. + # This should be the same for all languages, so no per-tag cache variable. + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the $host_os linker accepts -exported_symbol" >&5 +$as_echo_n "checking whether the $host_os linker accepts -exported_symbol... " >&6; } +if ${lt_cv_irix_exported_symbol+:} false; then : + $as_echo_n "(cached) " >&6 +else + save_LDFLAGS="$LDFLAGS" + LDFLAGS="$LDFLAGS -shared ${wl}-exported_symbol ${wl}foo ${wl}-update_registry ${wl}/dev/null" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +int foo (void) { return 0; } +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + lt_cv_irix_exported_symbol=yes +else + lt_cv_irix_exported_symbol=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + LDFLAGS="$save_LDFLAGS" +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_irix_exported_symbol" >&5 +$as_echo "$lt_cv_irix_exported_symbol" >&6; } + if test "$lt_cv_irix_exported_symbol" = yes; then + archive_expsym_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations ${wl}-exports_file ${wl}$export_symbols -o $lib' + fi + else + archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib' + archive_expsym_cmds='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -exports_file $export_symbols -o $lib' + fi + archive_cmds_need_lc='no' + hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' + hardcode_libdir_separator=: + inherit_rpath=yes + link_all_deplibs=yes + ;; + + netbsd* | netbsdelf*-gnu) + if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then + archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' # a.out + else + archive_cmds='$LD -shared -o $lib $libobjs $deplibs $linker_flags' # ELF + fi + hardcode_libdir_flag_spec='-R$libdir' + hardcode_direct=yes + hardcode_shlibpath_var=no + ;; + + newsos6) + archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + hardcode_direct=yes + hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' + hardcode_libdir_separator=: + hardcode_shlibpath_var=no + ;; + + *nto* | *qnx*) + ;; + + openbsd*) + if test -f /usr/libexec/ld.so; then + hardcode_direct=yes + hardcode_shlibpath_var=no + hardcode_direct_absolute=yes + if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then + archive_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' + archive_expsym_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags ${wl}-retain-symbols-file,$export_symbols' + hardcode_libdir_flag_spec='${wl}-rpath,$libdir' + export_dynamic_flag_spec='${wl}-E' + else + case $host_os in + openbsd[01].* | openbsd2.[0-7] | openbsd2.[0-7].*) + archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' + hardcode_libdir_flag_spec='-R$libdir' + ;; + *) + archive_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' + hardcode_libdir_flag_spec='${wl}-rpath,$libdir' + ;; + esac + fi + else + ld_shlibs=no + fi + ;; + + os2*) + hardcode_libdir_flag_spec='-L$libdir' + hardcode_minus_L=yes + allow_undefined_flag=unsupported + archive_cmds='$ECHO "LIBRARY $libname INITINSTANCE" > $output_objdir/$libname.def~$ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~echo DATA >> $output_objdir/$libname.def~echo " SINGLE NONSHARED" >> $output_objdir/$libname.def~echo EXPORTS >> $output_objdir/$libname.def~emxexp $libobjs >> $output_objdir/$libname.def~$CC -Zdll -Zcrtdll -o $lib $libobjs $deplibs $compiler_flags $output_objdir/$libname.def' + old_archive_from_new_cmds='emximp -o $output_objdir/$libname.a $output_objdir/$libname.def' + ;; + + osf3*) + if test "$GCC" = yes; then + allow_undefined_flag=' ${wl}-expect_unresolved ${wl}\*' + archive_cmds='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' + else + allow_undefined_flag=' -expect_unresolved \*' + archive_cmds='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib' + fi + archive_cmds_need_lc='no' + hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' + hardcode_libdir_separator=: + ;; + + osf4* | osf5*) # as osf3* with the addition of -msym flag + if test "$GCC" = yes; then + allow_undefined_flag=' ${wl}-expect_unresolved ${wl}\*' + archive_cmds='$CC -shared${allow_undefined_flag} $pic_flag $libobjs $deplibs $compiler_flags ${wl}-msym ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' + hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' + else + allow_undefined_flag=' -expect_unresolved \*' + archive_cmds='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags -msym -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib' + archive_expsym_cmds='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done; printf "%s\\n" "-hidden">> $lib.exp~ + $CC -shared${allow_undefined_flag} ${wl}-input ${wl}$lib.exp $compiler_flags $libobjs $deplibs -soname $soname `test -n "$verstring" && $ECHO "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib~$RM $lib.exp' + + # Both c and cxx compiler support -rpath directly + hardcode_libdir_flag_spec='-rpath $libdir' + fi + archive_cmds_need_lc='no' + hardcode_libdir_separator=: + ;; + + solaris*) + no_undefined_flag=' -z defs' + if test "$GCC" = yes; then + wlarc='${wl}' + archive_cmds='$CC -shared $pic_flag ${wl}-z ${wl}text ${wl}-h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags' + archive_expsym_cmds='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ + $CC -shared $pic_flag ${wl}-z ${wl}text ${wl}-M ${wl}$lib.exp ${wl}-h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp' + else + case `$CC -V 2>&1` in + *"Compilers 5.0"*) + wlarc='' + archive_cmds='$LD -G${allow_undefined_flag} -h $soname -o $lib $libobjs $deplibs $linker_flags' + archive_expsym_cmds='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ + $LD -G${allow_undefined_flag} -M $lib.exp -h $soname -o $lib $libobjs $deplibs $linker_flags~$RM $lib.exp' + ;; + *) + wlarc='${wl}' + archive_cmds='$CC -G${allow_undefined_flag} -h $soname -o $lib $libobjs $deplibs $compiler_flags' + archive_expsym_cmds='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ + $CC -G${allow_undefined_flag} -M $lib.exp -h $soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp' + ;; + esac + fi + hardcode_libdir_flag_spec='-R$libdir' + hardcode_shlibpath_var=no + case $host_os in + solaris2.[0-5] | solaris2.[0-5].*) ;; + *) + # The compiler driver will combine and reorder linker options, + # but understands `-z linker_flag'. GCC discards it without `$wl', + # but is careful enough not to reorder. + # Supported since Solaris 2.6 (maybe 2.5.1?) + if test "$GCC" = yes; then + whole_archive_flag_spec='${wl}-z ${wl}allextract$convenience ${wl}-z ${wl}defaultextract' + else + whole_archive_flag_spec='-z allextract$convenience -z defaultextract' + fi + ;; + esac + link_all_deplibs=yes + ;; + + sunos4*) + if test "x$host_vendor" = xsequent; then + # Use $CC to link under sequent, because it throws in some extra .o + # files that make .init and .fini sections work. + archive_cmds='$CC -G ${wl}-h $soname -o $lib $libobjs $deplibs $compiler_flags' + else + archive_cmds='$LD -assert pure-text -Bstatic -o $lib $libobjs $deplibs $linker_flags' + fi + hardcode_libdir_flag_spec='-L$libdir' + hardcode_direct=yes + hardcode_minus_L=yes + hardcode_shlibpath_var=no + ;; + + sysv4) + case $host_vendor in + sni) + archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + hardcode_direct=yes # is this really true??? + ;; + siemens) + ## LD is ld it makes a PLAMLIB + ## CC just makes a GrossModule. + archive_cmds='$LD -G -o $lib $libobjs $deplibs $linker_flags' + reload_cmds='$CC -r -o $output$reload_objs' + hardcode_direct=no + ;; + motorola) + archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + hardcode_direct=no #Motorola manual says yes, but my tests say they lie + ;; + esac + runpath_var='LD_RUN_PATH' + hardcode_shlibpath_var=no + ;; + + sysv4.3*) + archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + hardcode_shlibpath_var=no + export_dynamic_flag_spec='-Bexport' + ;; + + sysv4*MP*) + if test -d /usr/nec; then + archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + hardcode_shlibpath_var=no + runpath_var=LD_RUN_PATH + hardcode_runpath_var=yes + ld_shlibs=yes + fi + ;; + + sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[01].[10]* | unixware7* | sco3.2v5.0.[024]*) + no_undefined_flag='${wl}-z,text' + archive_cmds_need_lc=no + hardcode_shlibpath_var=no + runpath_var='LD_RUN_PATH' + + if test "$GCC" = yes; then + archive_cmds='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + archive_expsym_cmds='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + else + archive_cmds='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + archive_expsym_cmds='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + fi + ;; + + sysv5* | sco3.2v5* | sco5v6*) + # Note: We can NOT use -z defs as we might desire, because we do not + # link with -lc, and that would cause any symbols used from libc to + # always be unresolved, which means just about no library would + # ever link correctly. If we're not using GNU ld we use -z text + # though, which does catch some bad symbols but isn't as heavy-handed + # as -z defs. + no_undefined_flag='${wl}-z,text' + allow_undefined_flag='${wl}-z,nodefs' + archive_cmds_need_lc=no + hardcode_shlibpath_var=no + hardcode_libdir_flag_spec='${wl}-R,$libdir' + hardcode_libdir_separator=':' + link_all_deplibs=yes + export_dynamic_flag_spec='${wl}-Bexport' + runpath_var='LD_RUN_PATH' + + if test "$GCC" = yes; then + archive_cmds='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + archive_expsym_cmds='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + else + archive_cmds='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + archive_expsym_cmds='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + fi + ;; + + uts4*) + archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + hardcode_libdir_flag_spec='-L$libdir' + hardcode_shlibpath_var=no + ;; + + *) + ld_shlibs=no + ;; + esac + + if test x$host_vendor = xsni; then + case $host in + sysv4 | sysv4.2uw2* | sysv4.3* | sysv5*) + export_dynamic_flag_spec='${wl}-Blargedynsym' + ;; + esac + fi + fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ld_shlibs" >&5 +$as_echo "$ld_shlibs" >&6; } +test "$ld_shlibs" = no && can_build_shared=no + +with_gnu_ld=$with_gnu_ld + + + + + + + + + + + + + + + +# +# Do we need to explicitly link libc? +# +case "x$archive_cmds_need_lc" in +x|xyes) + # Assume -lc should be added + archive_cmds_need_lc=yes + + if test "$enable_shared" = yes && test "$GCC" = yes; then + case $archive_cmds in + *'~'*) + # FIXME: we may have to deal with multi-command sequences. + ;; + '$CC '*) + # Test whether the compiler implicitly links with -lc since on some + # systems, -lgcc has to come before -lc. If gcc already passes -lc + # to ld, don't add -lc before -lgcc. + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether -lc should be explicitly linked in" >&5 +$as_echo_n "checking whether -lc should be explicitly linked in... " >&6; } +if ${lt_cv_archive_cmds_need_lc+:} false; then : + $as_echo_n "(cached) " >&6 +else + $RM conftest* + echo "$lt_simple_compile_test_code" > conftest.$ac_ext + + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } 2>conftest.err; then + soname=conftest + lib=conftest + libobjs=conftest.$ac_objext + deplibs= + wl=$lt_prog_compiler_wl + pic_flag=$lt_prog_compiler_pic + compiler_flags=-v + linker_flags=-v + verstring= + output_objdir=. + libname=conftest + lt_save_allow_undefined_flag=$allow_undefined_flag + allow_undefined_flag= + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$archive_cmds 2\>\&1 \| $GREP \" -lc \" \>/dev/null 2\>\&1\""; } >&5 + (eval $archive_cmds 2\>\&1 \| $GREP \" -lc \" \>/dev/null 2\>\&1) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } + then + lt_cv_archive_cmds_need_lc=no + else + lt_cv_archive_cmds_need_lc=yes + fi + allow_undefined_flag=$lt_save_allow_undefined_flag + else + cat conftest.err 1>&5 + fi + $RM conftest* + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_archive_cmds_need_lc" >&5 +$as_echo "$lt_cv_archive_cmds_need_lc" >&6; } + archive_cmds_need_lc=$lt_cv_archive_cmds_need_lc + ;; + esac + fi + ;; +esac + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking dynamic linker characteristics" >&5 +$as_echo_n "checking dynamic linker characteristics... " >&6; } + +if test "$GCC" = yes; then + case $host_os in + darwin*) lt_awk_arg="/^libraries:/,/LR/" ;; + *) lt_awk_arg="/^libraries:/" ;; + esac + case $host_os in + mingw* | cegcc*) lt_sed_strip_eq="s,=\([A-Za-z]:\),\1,g" ;; + *) lt_sed_strip_eq="s,=/,/,g" ;; + esac + lt_search_path_spec=`$CC -print-search-dirs | awk $lt_awk_arg | $SED -e "s/^libraries://" -e $lt_sed_strip_eq` + case $lt_search_path_spec in + *\;*) + # if the path contains ";" then we assume it to be the separator + # otherwise default to the standard path separator (i.e. ":") - it is + # assumed that no part of a normal pathname contains ";" but that should + # okay in the real world where ";" in dirpaths is itself problematic. + lt_search_path_spec=`$ECHO "$lt_search_path_spec" | $SED 's/;/ /g'` + ;; + *) + lt_search_path_spec=`$ECHO "$lt_search_path_spec" | $SED "s/$PATH_SEPARATOR/ /g"` + ;; + esac + # Ok, now we have the path, separated by spaces, we can step through it + # and add multilib dir if necessary. + lt_tmp_lt_search_path_spec= + lt_multi_os_dir=`$CC $CPPFLAGS $CFLAGS $LDFLAGS -print-multi-os-directory 2>/dev/null` + for lt_sys_path in $lt_search_path_spec; do + if test -d "$lt_sys_path/$lt_multi_os_dir"; then + lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec $lt_sys_path/$lt_multi_os_dir" + else + test -d "$lt_sys_path" && \ + lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec $lt_sys_path" + fi + done + lt_search_path_spec=`$ECHO "$lt_tmp_lt_search_path_spec" | awk ' +BEGIN {RS=" "; FS="/|\n";} { + lt_foo=""; + lt_count=0; + for (lt_i = NF; lt_i > 0; lt_i--) { + if ($lt_i != "" && $lt_i != ".") { + if ($lt_i == "..") { + lt_count++; + } else { + if (lt_count == 0) { + lt_foo="/" $lt_i lt_foo; + } else { + lt_count--; + } + } + } + } + if (lt_foo != "") { lt_freq[lt_foo]++; } + if (lt_freq[lt_foo] == 1) { print lt_foo; } +}'` + # AWK program above erroneously prepends '/' to C:/dos/paths + # for these hosts. + case $host_os in + mingw* | cegcc*) lt_search_path_spec=`$ECHO "$lt_search_path_spec" |\ + $SED 's,/\([A-Za-z]:\),\1,g'` ;; + esac + sys_lib_search_path_spec=`$ECHO "$lt_search_path_spec" | $lt_NL2SP` +else + sys_lib_search_path_spec="/lib /usr/lib /usr/local/lib" +fi +library_names_spec= +libname_spec='lib$name' +soname_spec= +shrext_cmds=".so" +postinstall_cmds= +postuninstall_cmds= +finish_cmds= +finish_eval= +shlibpath_var= +shlibpath_overrides_runpath=unknown +version_type=none +dynamic_linker="$host_os ld.so" +sys_lib_dlsearch_path_spec="/lib /usr/lib" +need_lib_prefix=unknown +hardcode_into_libs=no + +# when you set need_version to no, make sure it does not cause -set_version +# flags to be left without arguments +need_version=unknown + +case $host_os in +aix3*) + version_type=linux # correct to gnu/linux during the next big refactor + library_names_spec='${libname}${release}${shared_ext}$versuffix $libname.a' + shlibpath_var=LIBPATH + + # AIX 3 has no versioning support, so we append a major version to the name. + soname_spec='${libname}${release}${shared_ext}$major' + ;; + +aix[4-9]*) + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + hardcode_into_libs=yes + if test "$host_cpu" = ia64; then + # AIX 5 supports IA64 + library_names_spec='${libname}${release}${shared_ext}$major ${libname}${release}${shared_ext}$versuffix $libname${shared_ext}' + shlibpath_var=LD_LIBRARY_PATH + else + # With GCC up to 2.95.x, collect2 would create an import file + # for dependence libraries. The import file would start with + # the line `#! .'. This would cause the generated library to + # depend on `.', always an invalid library. This was fixed in + # development snapshots of GCC prior to 3.0. + case $host_os in + aix4 | aix4.[01] | aix4.[01].*) + if { echo '#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 97)' + echo ' yes ' + echo '#endif'; } | ${CC} -E - | $GREP yes > /dev/null; then + : + else + can_build_shared=no + fi + ;; + esac + # AIX (on Power*) has no versioning support, so currently we can not hardcode correct + # soname into executable. Probably we can add versioning support to + # collect2, so additional links can be useful in future. + if test "$aix_use_runtimelinking" = yes; then + # If using run time linking (on AIX 4.2 or later) use lib.so + # instead of lib.a to let people know that these are not + # typical AIX shared libraries. + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + else + # We preserve .a as extension for shared libraries through AIX4.2 + # and later when we are not doing run time linking. + library_names_spec='${libname}${release}.a $libname.a' + soname_spec='${libname}${release}${shared_ext}$major' + fi + shlibpath_var=LIBPATH + fi + ;; + +amigaos*) + case $host_cpu in + powerpc) + # Since July 2007 AmigaOS4 officially supports .so libraries. + # When compiling the executable, add -use-dynld -Lsobjs: to the compileline. + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + ;; + m68k) + library_names_spec='$libname.ixlibrary $libname.a' + # Create ${libname}_ixlibrary.a entries in /sys/libs. + finish_eval='for lib in `ls $libdir/*.ixlibrary 2>/dev/null`; do libname=`func_echo_all "$lib" | $SED '\''s%^.*/\([^/]*\)\.ixlibrary$%\1%'\''`; test $RM /sys/libs/${libname}_ixlibrary.a; $show "cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a"; cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a || exit 1; done' + ;; + esac + ;; + +beos*) + library_names_spec='${libname}${shared_ext}' + dynamic_linker="$host_os ld.so" + shlibpath_var=LIBRARY_PATH + ;; + +bsdi[45]*) + version_type=linux # correct to gnu/linux during the next big refactor + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + finish_cmds='PATH="\$PATH:/sbin" ldconfig $libdir' + shlibpath_var=LD_LIBRARY_PATH + sys_lib_search_path_spec="/shlib /usr/lib /usr/X11/lib /usr/contrib/lib /lib /usr/local/lib" + sys_lib_dlsearch_path_spec="/shlib /usr/lib /usr/local/lib" + # the default ld.so.conf also contains /usr/contrib/lib and + # /usr/X11R6/lib (/usr/X11 is a link to /usr/X11R6), but let us allow + # libtool to hard-code these into programs + ;; + +cygwin* | mingw* | pw32* | cegcc*) + version_type=windows + shrext_cmds=".dll" + need_version=no + need_lib_prefix=no + + case $GCC,$cc_basename in + yes,*) + # gcc + library_names_spec='$libname.dll.a' + # DLL is installed to $(libdir)/../bin by postinstall_cmds + postinstall_cmds='base_file=`basename \${file}`~ + dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\${base_file}'\''i; echo \$dlname'\''`~ + dldir=$destdir/`dirname \$dlpath`~ + test -d \$dldir || mkdir -p \$dldir~ + $install_prog $dir/$dlname \$dldir/$dlname~ + chmod a+x \$dldir/$dlname~ + if test -n '\''$stripme'\'' && test -n '\''$striplib'\''; then + eval '\''$striplib \$dldir/$dlname'\'' || exit \$?; + fi' + postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~ + dlpath=$dir/\$dldll~ + $RM \$dlpath' + shlibpath_overrides_runpath=yes + + case $host_os in + cygwin*) + # Cygwin DLLs use 'cyg' prefix rather than 'lib' + soname_spec='`echo ${libname} | sed -e 's/^lib/cyg/'``echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext}' + + sys_lib_search_path_spec="$sys_lib_search_path_spec /usr/lib/w32api" + ;; + mingw* | cegcc*) + # MinGW DLLs use traditional 'lib' prefix + soname_spec='${libname}`echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext}' + ;; + pw32*) + # pw32 DLLs use 'pw' prefix rather than 'lib' + library_names_spec='`echo ${libname} | sed -e 's/^lib/pw/'``echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext}' + ;; + esac + dynamic_linker='Win32 ld.exe' + ;; + + *,cl*) + # Native MSVC + libname_spec='$name' + soname_spec='${libname}`echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext}' + library_names_spec='${libname}.dll.lib' + + case $build_os in + mingw*) + sys_lib_search_path_spec= + lt_save_ifs=$IFS + IFS=';' + for lt_path in $LIB + do + IFS=$lt_save_ifs + # Let DOS variable expansion print the short 8.3 style file name. + lt_path=`cd "$lt_path" 2>/dev/null && cmd //C "for %i in (".") do @echo %~si"` + sys_lib_search_path_spec="$sys_lib_search_path_spec $lt_path" + done + IFS=$lt_save_ifs + # Convert to MSYS style. + sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | sed -e 's|\\\\|/|g' -e 's| \\([a-zA-Z]\\):| /\\1|g' -e 's|^ ||'` + ;; + cygwin*) + # Convert to unix form, then to dos form, then back to unix form + # but this time dos style (no spaces!) so that the unix form looks + # like /cygdrive/c/PROGRA~1:/cygdr... + sys_lib_search_path_spec=`cygpath --path --unix "$LIB"` + sys_lib_search_path_spec=`cygpath --path --dos "$sys_lib_search_path_spec" 2>/dev/null` + sys_lib_search_path_spec=`cygpath --path --unix "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"` + ;; + *) + sys_lib_search_path_spec="$LIB" + if $ECHO "$sys_lib_search_path_spec" | $GREP ';[c-zC-Z]:/' >/dev/null; then + # It is most probably a Windows format PATH. + sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e 's/;/ /g'` + else + sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"` + fi + # FIXME: find the short name or the path components, as spaces are + # common. (e.g. "Program Files" -> "PROGRA~1") + ;; + esac + + # DLL is installed to $(libdir)/../bin by postinstall_cmds + postinstall_cmds='base_file=`basename \${file}`~ + dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\${base_file}'\''i; echo \$dlname'\''`~ + dldir=$destdir/`dirname \$dlpath`~ + test -d \$dldir || mkdir -p \$dldir~ + $install_prog $dir/$dlname \$dldir/$dlname' + postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~ + dlpath=$dir/\$dldll~ + $RM \$dlpath' + shlibpath_overrides_runpath=yes + dynamic_linker='Win32 link.exe' + ;; + + *) + # Assume MSVC wrapper + library_names_spec='${libname}`echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext} $libname.lib' + dynamic_linker='Win32 ld.exe' + ;; + esac + # FIXME: first we should search . and the directory the executable is in + shlibpath_var=PATH + ;; + +darwin* | rhapsody*) + dynamic_linker="$host_os dyld" + version_type=darwin + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${major}$shared_ext ${libname}$shared_ext' + soname_spec='${libname}${release}${major}$shared_ext' + shlibpath_overrides_runpath=yes + shlibpath_var=DYLD_LIBRARY_PATH + shrext_cmds='`test .$module = .yes && echo .so || echo .dylib`' + + sys_lib_search_path_spec="$sys_lib_search_path_spec /usr/local/lib" + sys_lib_dlsearch_path_spec='/usr/local/lib /lib /usr/lib' + ;; + +dgux*) + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname$shared_ext' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + ;; + +freebsd* | dragonfly*) + # DragonFly does not have aout. When/if they implement a new + # versioning mechanism, adjust this. + if test -x /usr/bin/objformat; then + objformat=`/usr/bin/objformat` + else + case $host_os in + freebsd[23].*) objformat=aout ;; + *) objformat=elf ;; + esac + fi + version_type=freebsd-$objformat + case $version_type in + freebsd-elf*) + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext} $libname${shared_ext}' + need_version=no + need_lib_prefix=no + ;; + freebsd-*) + library_names_spec='${libname}${release}${shared_ext}$versuffix $libname${shared_ext}$versuffix' + need_version=yes + ;; + esac + shlibpath_var=LD_LIBRARY_PATH + case $host_os in + freebsd2.*) + shlibpath_overrides_runpath=yes + ;; + freebsd3.[01]* | freebsdelf3.[01]*) + shlibpath_overrides_runpath=yes + hardcode_into_libs=yes + ;; + freebsd3.[2-9]* | freebsdelf3.[2-9]* | \ + freebsd4.[0-5] | freebsdelf4.[0-5] | freebsd4.1.1 | freebsdelf4.1.1) + shlibpath_overrides_runpath=no + hardcode_into_libs=yes + ;; + *) # from 4.6 on, and DragonFly + shlibpath_overrides_runpath=yes + hardcode_into_libs=yes + ;; + esac + ;; + +haiku*) + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + dynamic_linker="$host_os runtime_loader" + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}${major} ${libname}${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LIBRARY_PATH + shlibpath_overrides_runpath=yes + sys_lib_dlsearch_path_spec='/boot/home/config/lib /boot/common/lib /boot/system/lib' + hardcode_into_libs=yes + ;; + +hpux9* | hpux10* | hpux11*) + # Give a soname corresponding to the major version so that dld.sl refuses to + # link against other versions. + version_type=sunos + need_lib_prefix=no + need_version=no + case $host_cpu in + ia64*) + shrext_cmds='.so' + hardcode_into_libs=yes + dynamic_linker="$host_os dld.so" + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes # Unless +noenvvar is specified. + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + if test "X$HPUX_IA64_MODE" = X32; then + sys_lib_search_path_spec="/usr/lib/hpux32 /usr/local/lib/hpux32 /usr/local/lib" + else + sys_lib_search_path_spec="/usr/lib/hpux64 /usr/local/lib/hpux64" + fi + sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec + ;; + hppa*64*) + shrext_cmds='.sl' + hardcode_into_libs=yes + dynamic_linker="$host_os dld.sl" + shlibpath_var=LD_LIBRARY_PATH # How should we handle SHLIB_PATH + shlibpath_overrides_runpath=yes # Unless +noenvvar is specified. + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + sys_lib_search_path_spec="/usr/lib/pa20_64 /usr/ccs/lib/pa20_64" + sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec + ;; + *) + shrext_cmds='.sl' + dynamic_linker="$host_os dld.sl" + shlibpath_var=SHLIB_PATH + shlibpath_overrides_runpath=no # +s is required to enable SHLIB_PATH + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + ;; + esac + # HP-UX runs *really* slowly unless shared libraries are mode 555, ... + postinstall_cmds='chmod 555 $lib' + # or fails outright, so override atomically: + install_override_mode=555 + ;; + +interix[3-9]*) + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + dynamic_linker='Interix 3.x ld.so.1 (PE, like ELF)' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=no + hardcode_into_libs=yes + ;; + +irix5* | irix6* | nonstopux*) + case $host_os in + nonstopux*) version_type=nonstopux ;; + *) + if test "$lt_cv_prog_gnu_ld" = yes; then + version_type=linux # correct to gnu/linux during the next big refactor + else + version_type=irix + fi ;; + esac + need_lib_prefix=no + need_version=no + soname_spec='${libname}${release}${shared_ext}$major' + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${release}${shared_ext} $libname${shared_ext}' + case $host_os in + irix5* | nonstopux*) + libsuff= shlibsuff= + ;; + *) + case $LD in # libtool.m4 will add one of these switches to LD + *-32|*"-32 "|*-melf32bsmip|*"-melf32bsmip ") + libsuff= shlibsuff= libmagic=32-bit;; + *-n32|*"-n32 "|*-melf32bmipn32|*"-melf32bmipn32 ") + libsuff=32 shlibsuff=N32 libmagic=N32;; + *-64|*"-64 "|*-melf64bmip|*"-melf64bmip ") + libsuff=64 shlibsuff=64 libmagic=64-bit;; + *) libsuff= shlibsuff= libmagic=never-match;; + esac + ;; + esac + shlibpath_var=LD_LIBRARY${shlibsuff}_PATH + shlibpath_overrides_runpath=no + sys_lib_search_path_spec="/usr/lib${libsuff} /lib${libsuff} /usr/local/lib${libsuff}" + sys_lib_dlsearch_path_spec="/usr/lib${libsuff} /lib${libsuff}" + hardcode_into_libs=yes + ;; + +# No shared lib support for Linux oldld, aout, or coff. +linux*oldld* | linux*aout* | linux*coff*) + dynamic_linker=no + ;; + +# This must be glibc/ELF. +linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + finish_cmds='PATH="\$PATH:/sbin" ldconfig -n $libdir' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=no + + # Some binutils ld are patched to set DT_RUNPATH + if ${lt_cv_shlibpath_overrides_runpath+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_shlibpath_overrides_runpath=no + save_LDFLAGS=$LDFLAGS + save_libdir=$libdir + eval "libdir=/foo; wl=\"$lt_prog_compiler_wl\"; \ + LDFLAGS=\"\$LDFLAGS $hardcode_libdir_flag_spec\"" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + if ($OBJDUMP -p conftest$ac_exeext) 2>/dev/null | grep "RUNPATH.*$libdir" >/dev/null; then : + lt_cv_shlibpath_overrides_runpath=yes +fi +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + LDFLAGS=$save_LDFLAGS + libdir=$save_libdir + +fi + + shlibpath_overrides_runpath=$lt_cv_shlibpath_overrides_runpath + + # This implies no fast_install, which is unacceptable. + # Some rework will be needed to allow for fast_install + # before this can be enabled. + hardcode_into_libs=yes + + # Append ld.so.conf contents to the search path + if test -f /etc/ld.so.conf; then + lt_ld_extra=`awk '/^include / { system(sprintf("cd /etc; cat %s 2>/dev/null", \$2)); skip = 1; } { if (!skip) print \$0; skip = 0; }' < /etc/ld.so.conf | $SED -e 's/#.*//;/^[ ]*hwcap[ ]/d;s/[:, ]/ /g;s/=[^=]*$//;s/=[^= ]* / /g;s/"//g;/^$/d' | tr '\n' ' '` + sys_lib_dlsearch_path_spec="/lib /usr/lib $lt_ld_extra" + fi + + # We used to test for /lib/ld.so.1 and disable shared libraries on + # powerpc, because MkLinux only supported shared libraries with the + # GNU dynamic linker. Since this was broken with cross compilers, + # most powerpc-linux boxes support dynamic linking these days and + # people can always --disable-shared, the test was removed, and we + # assume the GNU/Linux dynamic linker is in use. + dynamic_linker='GNU/Linux ld.so' + ;; + +netbsdelf*-gnu) + version_type=linux + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=no + hardcode_into_libs=yes + dynamic_linker='NetBSD ld.elf_so' + ;; + +netbsd*) + version_type=sunos + need_lib_prefix=no + need_version=no + if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix' + finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' + dynamic_linker='NetBSD (a.out) ld.so' + else + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + dynamic_linker='NetBSD ld.elf_so' + fi + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + hardcode_into_libs=yes + ;; + +newsos6) + version_type=linux # correct to gnu/linux during the next big refactor + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + ;; + +*nto* | *qnx*) + version_type=qnx + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=no + hardcode_into_libs=yes + dynamic_linker='ldqnx.so' + ;; + +openbsd*) + version_type=sunos + sys_lib_dlsearch_path_spec="/usr/lib" + need_lib_prefix=no + # Some older versions of OpenBSD (3.3 at least) *do* need versioned libs. + case $host_os in + openbsd3.3 | openbsd3.3.*) need_version=yes ;; + *) need_version=no ;; + esac + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix' + finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' + shlibpath_var=LD_LIBRARY_PATH + if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then + case $host_os in + openbsd2.[89] | openbsd2.[89].*) + shlibpath_overrides_runpath=no + ;; + *) + shlibpath_overrides_runpath=yes + ;; + esac + else + shlibpath_overrides_runpath=yes + fi + ;; + +os2*) + libname_spec='$name' + shrext_cmds=".dll" + need_lib_prefix=no + library_names_spec='$libname${shared_ext} $libname.a' + dynamic_linker='OS/2 ld.exe' + shlibpath_var=LIBPATH + ;; + +osf3* | osf4* | osf5*) + version_type=osf + need_lib_prefix=no + need_version=no + soname_spec='${libname}${release}${shared_ext}$major' + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + shlibpath_var=LD_LIBRARY_PATH + sys_lib_search_path_spec="/usr/shlib /usr/ccs/lib /usr/lib/cmplrs/cc /usr/lib /usr/local/lib /var/shlib" + sys_lib_dlsearch_path_spec="$sys_lib_search_path_spec" + ;; + +rdos*) + dynamic_linker=no + ;; + +solaris*) + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + hardcode_into_libs=yes + # ldd complains unless libraries are executable + postinstall_cmds='chmod +x $lib' + ;; + +sunos4*) + version_type=sunos + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix' + finish_cmds='PATH="\$PATH:/usr/etc" ldconfig $libdir' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + if test "$with_gnu_ld" = yes; then + need_lib_prefix=no + fi + need_version=yes + ;; + +sysv4 | sysv4.3*) + version_type=linux # correct to gnu/linux during the next big refactor + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + case $host_vendor in + sni) + shlibpath_overrides_runpath=no + need_lib_prefix=no + runpath_var=LD_RUN_PATH + ;; + siemens) + need_lib_prefix=no + ;; + motorola) + need_lib_prefix=no + need_version=no + shlibpath_overrides_runpath=no + sys_lib_search_path_spec='/lib /usr/lib /usr/ccs/lib' + ;; + esac + ;; + +sysv4*MP*) + if test -d /usr/nec ;then + version_type=linux # correct to gnu/linux during the next big refactor + library_names_spec='$libname${shared_ext}.$versuffix $libname${shared_ext}.$major $libname${shared_ext}' + soname_spec='$libname${shared_ext}.$major' + shlibpath_var=LD_LIBRARY_PATH + fi + ;; + +sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*) + version_type=freebsd-elf + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext} $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + hardcode_into_libs=yes + if test "$with_gnu_ld" = yes; then + sys_lib_search_path_spec='/usr/local/lib /usr/gnu/lib /usr/ccs/lib /usr/lib /lib' + else + sys_lib_search_path_spec='/usr/ccs/lib /usr/lib' + case $host_os in + sco3.2v5*) + sys_lib_search_path_spec="$sys_lib_search_path_spec /lib" + ;; + esac + fi + sys_lib_dlsearch_path_spec='/usr/lib' + ;; + +tpf*) + # TPF is a cross-target only. Preferred cross-host = GNU/Linux. + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=no + hardcode_into_libs=yes + ;; + +uts4*) + version_type=linux # correct to gnu/linux during the next big refactor + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + ;; + +*) + dynamic_linker=no + ;; +esac +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $dynamic_linker" >&5 +$as_echo "$dynamic_linker" >&6; } +test "$dynamic_linker" = no && can_build_shared=no + +variables_saved_for_relink="PATH $shlibpath_var $runpath_var" +if test "$GCC" = yes; then + variables_saved_for_relink="$variables_saved_for_relink GCC_EXEC_PREFIX COMPILER_PATH LIBRARY_PATH" +fi + +if test "${lt_cv_sys_lib_search_path_spec+set}" = set; then + sys_lib_search_path_spec="$lt_cv_sys_lib_search_path_spec" +fi +if test "${lt_cv_sys_lib_dlsearch_path_spec+set}" = set; then + sys_lib_dlsearch_path_spec="$lt_cv_sys_lib_dlsearch_path_spec" +fi + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to hardcode library paths into programs" >&5 +$as_echo_n "checking how to hardcode library paths into programs... " >&6; } +hardcode_action= +if test -n "$hardcode_libdir_flag_spec" || + test -n "$runpath_var" || + test "X$hardcode_automatic" = "Xyes" ; then + + # We can hardcode non-existent directories. + if test "$hardcode_direct" != no && + # If the only mechanism to avoid hardcoding is shlibpath_var, we + # have to relink, otherwise we might link with an installed library + # when we should be linking with a yet-to-be-installed one + ## test "$_LT_TAGVAR(hardcode_shlibpath_var, )" != no && + test "$hardcode_minus_L" != no; then + # Linking always hardcodes the temporary library directory. + hardcode_action=relink + else + # We can link without hardcoding, and we can hardcode nonexisting dirs. + hardcode_action=immediate + fi +else + # We cannot hardcode anything, or else we can only hardcode existing + # directories. + hardcode_action=unsupported +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $hardcode_action" >&5 +$as_echo "$hardcode_action" >&6; } + +if test "$hardcode_action" = relink || + test "$inherit_rpath" = yes; then + # Fast installation is not supported + enable_fast_install=no +elif test "$shlibpath_overrides_runpath" = yes || + test "$enable_shared" = no; then + # Fast installation is not necessary + enable_fast_install=needless +fi + + + + + + + if test "x$enable_dlopen" != xyes; then + enable_dlopen=unknown + enable_dlopen_self=unknown + enable_dlopen_self_static=unknown +else + lt_cv_dlopen=no + lt_cv_dlopen_libs= + + case $host_os in + beos*) + lt_cv_dlopen="load_add_on" + lt_cv_dlopen_libs= + lt_cv_dlopen_self=yes + ;; + + mingw* | pw32* | cegcc*) + lt_cv_dlopen="LoadLibrary" + lt_cv_dlopen_libs= + ;; + + cygwin*) + lt_cv_dlopen="dlopen" + lt_cv_dlopen_libs= + ;; + + darwin*) + # if libdl is installed we need to link against it + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for dlopen in -ldl" >&5 +$as_echo_n "checking for dlopen in -ldl... " >&6; } +if ${ac_cv_lib_dl_dlopen+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-ldl $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char dlopen (); +int +main () +{ +return dlopen (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_dl_dlopen=yes +else + ac_cv_lib_dl_dlopen=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dl_dlopen" >&5 +$as_echo "$ac_cv_lib_dl_dlopen" >&6; } +if test "x$ac_cv_lib_dl_dlopen" = xyes; then : + lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-ldl" +else + + lt_cv_dlopen="dyld" + lt_cv_dlopen_libs= + lt_cv_dlopen_self=yes + +fi + + ;; + + *) + ac_fn_c_check_func "$LINENO" "shl_load" "ac_cv_func_shl_load" +if test "x$ac_cv_func_shl_load" = xyes; then : + lt_cv_dlopen="shl_load" +else + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for shl_load in -ldld" >&5 +$as_echo_n "checking for shl_load in -ldld... " >&6; } +if ${ac_cv_lib_dld_shl_load+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-ldld $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char shl_load (); +int +main () +{ +return shl_load (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_dld_shl_load=yes +else + ac_cv_lib_dld_shl_load=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dld_shl_load" >&5 +$as_echo "$ac_cv_lib_dld_shl_load" >&6; } +if test "x$ac_cv_lib_dld_shl_load" = xyes; then : + lt_cv_dlopen="shl_load" lt_cv_dlopen_libs="-ldld" +else + ac_fn_c_check_func "$LINENO" "dlopen" "ac_cv_func_dlopen" +if test "x$ac_cv_func_dlopen" = xyes; then : + lt_cv_dlopen="dlopen" +else + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for dlopen in -ldl" >&5 +$as_echo_n "checking for dlopen in -ldl... " >&6; } +if ${ac_cv_lib_dl_dlopen+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-ldl $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char dlopen (); +int +main () +{ +return dlopen (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_dl_dlopen=yes +else + ac_cv_lib_dl_dlopen=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dl_dlopen" >&5 +$as_echo "$ac_cv_lib_dl_dlopen" >&6; } +if test "x$ac_cv_lib_dl_dlopen" = xyes; then : + lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-ldl" +else + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for dlopen in -lsvld" >&5 +$as_echo_n "checking for dlopen in -lsvld... " >&6; } +if ${ac_cv_lib_svld_dlopen+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lsvld $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char dlopen (); +int +main () +{ +return dlopen (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_svld_dlopen=yes +else + ac_cv_lib_svld_dlopen=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_svld_dlopen" >&5 +$as_echo "$ac_cv_lib_svld_dlopen" >&6; } +if test "x$ac_cv_lib_svld_dlopen" = xyes; then : + lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-lsvld" +else + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for dld_link in -ldld" >&5 +$as_echo_n "checking for dld_link in -ldld... " >&6; } +if ${ac_cv_lib_dld_dld_link+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-ldld $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char dld_link (); +int +main () +{ +return dld_link (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_dld_dld_link=yes +else + ac_cv_lib_dld_dld_link=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dld_dld_link" >&5 +$as_echo "$ac_cv_lib_dld_dld_link" >&6; } +if test "x$ac_cv_lib_dld_dld_link" = xyes; then : + lt_cv_dlopen="dld_link" lt_cv_dlopen_libs="-ldld" +fi + + +fi + + +fi + + +fi + + +fi + + +fi + + ;; + esac + + if test "x$lt_cv_dlopen" != xno; then + enable_dlopen=yes + else + enable_dlopen=no + fi + + case $lt_cv_dlopen in + dlopen) + save_CPPFLAGS="$CPPFLAGS" + test "x$ac_cv_header_dlfcn_h" = xyes && CPPFLAGS="$CPPFLAGS -DHAVE_DLFCN_H" + + save_LDFLAGS="$LDFLAGS" + wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $export_dynamic_flag_spec\" + + save_LIBS="$LIBS" + LIBS="$lt_cv_dlopen_libs $LIBS" + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether a program can dlopen itself" >&5 +$as_echo_n "checking whether a program can dlopen itself... " >&6; } +if ${lt_cv_dlopen_self+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test "$cross_compiling" = yes; then : + lt_cv_dlopen_self=cross +else + lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 + lt_status=$lt_dlunknown + cat > conftest.$ac_ext <<_LT_EOF +#line $LINENO "configure" +#include "confdefs.h" + +#if HAVE_DLFCN_H +#include +#endif + +#include + +#ifdef RTLD_GLOBAL +# define LT_DLGLOBAL RTLD_GLOBAL +#else +# ifdef DL_GLOBAL +# define LT_DLGLOBAL DL_GLOBAL +# else +# define LT_DLGLOBAL 0 +# endif +#endif + +/* We may have to define LT_DLLAZY_OR_NOW in the command line if we + find out it does not work in some platform. */ +#ifndef LT_DLLAZY_OR_NOW +# ifdef RTLD_LAZY +# define LT_DLLAZY_OR_NOW RTLD_LAZY +# else +# ifdef DL_LAZY +# define LT_DLLAZY_OR_NOW DL_LAZY +# else +# ifdef RTLD_NOW +# define LT_DLLAZY_OR_NOW RTLD_NOW +# else +# ifdef DL_NOW +# define LT_DLLAZY_OR_NOW DL_NOW +# else +# define LT_DLLAZY_OR_NOW 0 +# endif +# endif +# endif +# endif +#endif + +/* When -fvisbility=hidden is used, assume the code has been annotated + correspondingly for the symbols needed. */ +#if defined(__GNUC__) && (((__GNUC__ == 3) && (__GNUC_MINOR__ >= 3)) || (__GNUC__ > 3)) +int fnord () __attribute__((visibility("default"))); +#endif + +int fnord () { return 42; } +int main () +{ + void *self = dlopen (0, LT_DLGLOBAL|LT_DLLAZY_OR_NOW); + int status = $lt_dlunknown; + + if (self) + { + if (dlsym (self,"fnord")) status = $lt_dlno_uscore; + else + { + if (dlsym( self,"_fnord")) status = $lt_dlneed_uscore; + else puts (dlerror ()); + } + /* dlclose (self); */ + } + else + puts (dlerror ()); + + return status; +} +_LT_EOF + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_link\""; } >&5 + (eval $ac_link) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && test -s conftest${ac_exeext} 2>/dev/null; then + (./conftest; exit; ) >&5 2>/dev/null + lt_status=$? + case x$lt_status in + x$lt_dlno_uscore) lt_cv_dlopen_self=yes ;; + x$lt_dlneed_uscore) lt_cv_dlopen_self=yes ;; + x$lt_dlunknown|x*) lt_cv_dlopen_self=no ;; + esac + else : + # compilation failed + lt_cv_dlopen_self=no + fi +fi +rm -fr conftest* + + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_dlopen_self" >&5 +$as_echo "$lt_cv_dlopen_self" >&6; } + + if test "x$lt_cv_dlopen_self" = xyes; then + wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $lt_prog_compiler_static\" + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether a statically linked program can dlopen itself" >&5 +$as_echo_n "checking whether a statically linked program can dlopen itself... " >&6; } +if ${lt_cv_dlopen_self_static+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test "$cross_compiling" = yes; then : + lt_cv_dlopen_self_static=cross +else + lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 + lt_status=$lt_dlunknown + cat > conftest.$ac_ext <<_LT_EOF +#line $LINENO "configure" +#include "confdefs.h" + +#if HAVE_DLFCN_H +#include +#endif + +#include + +#ifdef RTLD_GLOBAL +# define LT_DLGLOBAL RTLD_GLOBAL +#else +# ifdef DL_GLOBAL +# define LT_DLGLOBAL DL_GLOBAL +# else +# define LT_DLGLOBAL 0 +# endif +#endif + +/* We may have to define LT_DLLAZY_OR_NOW in the command line if we + find out it does not work in some platform. */ +#ifndef LT_DLLAZY_OR_NOW +# ifdef RTLD_LAZY +# define LT_DLLAZY_OR_NOW RTLD_LAZY +# else +# ifdef DL_LAZY +# define LT_DLLAZY_OR_NOW DL_LAZY +# else +# ifdef RTLD_NOW +# define LT_DLLAZY_OR_NOW RTLD_NOW +# else +# ifdef DL_NOW +# define LT_DLLAZY_OR_NOW DL_NOW +# else +# define LT_DLLAZY_OR_NOW 0 +# endif +# endif +# endif +# endif +#endif + +/* When -fvisbility=hidden is used, assume the code has been annotated + correspondingly for the symbols needed. */ +#if defined(__GNUC__) && (((__GNUC__ == 3) && (__GNUC_MINOR__ >= 3)) || (__GNUC__ > 3)) +int fnord () __attribute__((visibility("default"))); +#endif + +int fnord () { return 42; } +int main () +{ + void *self = dlopen (0, LT_DLGLOBAL|LT_DLLAZY_OR_NOW); + int status = $lt_dlunknown; + + if (self) + { + if (dlsym (self,"fnord")) status = $lt_dlno_uscore; + else + { + if (dlsym( self,"_fnord")) status = $lt_dlneed_uscore; + else puts (dlerror ()); + } + /* dlclose (self); */ + } + else + puts (dlerror ()); + + return status; +} +_LT_EOF + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_link\""; } >&5 + (eval $ac_link) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && test -s conftest${ac_exeext} 2>/dev/null; then + (./conftest; exit; ) >&5 2>/dev/null + lt_status=$? + case x$lt_status in + x$lt_dlno_uscore) lt_cv_dlopen_self_static=yes ;; + x$lt_dlneed_uscore) lt_cv_dlopen_self_static=yes ;; + x$lt_dlunknown|x*) lt_cv_dlopen_self_static=no ;; + esac + else : + # compilation failed + lt_cv_dlopen_self_static=no + fi +fi +rm -fr conftest* + + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_dlopen_self_static" >&5 +$as_echo "$lt_cv_dlopen_self_static" >&6; } + fi + + CPPFLAGS="$save_CPPFLAGS" + LDFLAGS="$save_LDFLAGS" + LIBS="$save_LIBS" + ;; + esac + + case $lt_cv_dlopen_self in + yes|no) enable_dlopen_self=$lt_cv_dlopen_self ;; + *) enable_dlopen_self=unknown ;; + esac + + case $lt_cv_dlopen_self_static in + yes|no) enable_dlopen_self_static=$lt_cv_dlopen_self_static ;; + *) enable_dlopen_self_static=unknown ;; + esac +fi + + + + + + + + + + + + + + + + + +striplib= +old_striplib= +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether stripping libraries is possible" >&5 +$as_echo_n "checking whether stripping libraries is possible... " >&6; } +if test -n "$STRIP" && $STRIP -V 2>&1 | $GREP "GNU strip" >/dev/null; then + test -z "$old_striplib" && old_striplib="$STRIP --strip-debug" + test -z "$striplib" && striplib="$STRIP --strip-unneeded" + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } +else +# FIXME - insert some real tests, host_os isn't really good enough + case $host_os in + darwin*) + if test -n "$STRIP" ; then + striplib="$STRIP -x" + old_striplib="$STRIP -S" + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + fi + ;; + *) + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + ;; + esac +fi + + + + + + + + + + + + + # Report which library types will actually be built + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if libtool supports shared libraries" >&5 +$as_echo_n "checking if libtool supports shared libraries... " >&6; } + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $can_build_shared" >&5 +$as_echo "$can_build_shared" >&6; } + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to build shared libraries" >&5 +$as_echo_n "checking whether to build shared libraries... " >&6; } + test "$can_build_shared" = "no" && enable_shared=no + + # On AIX, shared libraries and static libraries use the same namespace, and + # are all built from PIC. + case $host_os in + aix3*) + test "$enable_shared" = yes && enable_static=no + if test -n "$RANLIB"; then + archive_cmds="$archive_cmds~\$RANLIB \$lib" + postinstall_cmds='$RANLIB $lib' + fi + ;; + + aix[4-9]*) + if test "$host_cpu" != ia64 && test "$aix_use_runtimelinking" = no ; then + test "$enable_shared" = yes && enable_static=no + fi + ;; + esac + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $enable_shared" >&5 +$as_echo "$enable_shared" >&6; } + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to build static libraries" >&5 +$as_echo_n "checking whether to build static libraries... " >&6; } + # Make sure either enable_shared or enable_static is yes. + test "$enable_shared" = yes || enable_static=yes + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $enable_static" >&5 +$as_echo "$enable_static" >&6; } + + + + +fi +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + +CC="$lt_save_CC" + + + + + + + + + + + + + + + + ac_config_commands="$ac_config_commands libtool" + + + + +# Only expand once: + + + + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking target system type" >&5 +$as_echo_n "checking target system type... " >&6; } +if ${ac_cv_target+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test "x$target_alias" = x; then + ac_cv_target=$ac_cv_host +else + ac_cv_target=`$SHELL "$ac_aux_dir/config.sub" $target_alias` || + as_fn_error $? "$SHELL $ac_aux_dir/config.sub $target_alias failed" "$LINENO" 5 +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_target" >&5 +$as_echo "$ac_cv_target" >&6; } +case $ac_cv_target in +*-*-*) ;; +*) as_fn_error $? "invalid value of canonical target" "$LINENO" 5;; +esac +target=$ac_cv_target +ac_save_IFS=$IFS; IFS='-' +set x $ac_cv_target +shift +target_cpu=$1 +target_vendor=$2 +shift; shift +# Remember, the first character of IFS is used to create $*, +# except with old shells: +target_os=$* +IFS=$ac_save_IFS +case $target_os in *\ *) target_os=`echo "$target_os" | sed 's/ /-/g'`;; esac + + +# The aliases save the names the user supplied, while $host etc. +# will get canonicalized. +test -n "$target_alias" && + test "$program_prefix$program_suffix$program_transform_name" = \ + NONENONEs,x,x, && + program_prefix=${target_alias}- + +am__api_version='1.14' + +# Find a good install program. We prefer a C program (faster), +# so one script is as good as another. But avoid the broken or +# incompatible versions: +# SysV /etc/install, /usr/sbin/install +# SunOS /usr/etc/install +# IRIX /sbin/install +# AIX /bin/install +# AmigaOS /C/install, which installs bootblocks on floppy discs +# AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag +# AFS /usr/afsws/bin/install, which mishandles nonexistent args +# SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff" +# OS/2's system install, which has a completely different semantic +# ./install, which can be erroneously created by make from ./install.sh. +# Reject install programs that cannot install multiple files. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for a BSD-compatible install" >&5 +$as_echo_n "checking for a BSD-compatible install... " >&6; } +if test -z "$INSTALL"; then +if ${ac_cv_path_install+:} false; then : + $as_echo_n "(cached) " >&6 +else + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + # Account for people who put trailing slashes in PATH elements. +case $as_dir/ in #(( + ./ | .// | /[cC]/* | \ + /etc/* | /usr/sbin/* | /usr/etc/* | /sbin/* | /usr/afsws/bin/* | \ + ?:[\\/]os2[\\/]install[\\/]* | ?:[\\/]OS2[\\/]INSTALL[\\/]* | \ + /usr/ucb/* ) ;; + *) + # OSF1 and SCO ODT 3.0 have their own names for install. + # Don't use installbsd from OSF since it installs stuff as root + # by default. + for ac_prog in ginstall scoinst install; do + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_prog$ac_exec_ext"; then + if test $ac_prog = install && + grep dspmsg "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then + # AIX install. It has an incompatible calling convention. + : + elif test $ac_prog = install && + grep pwplus "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then + # program-specific install script used by HP pwplus--don't use. + : + else + rm -rf conftest.one conftest.two conftest.dir + echo one > conftest.one + echo two > conftest.two + mkdir conftest.dir + if "$as_dir/$ac_prog$ac_exec_ext" -c conftest.one conftest.two "`pwd`/conftest.dir" && + test -s conftest.one && test -s conftest.two && + test -s conftest.dir/conftest.one && + test -s conftest.dir/conftest.two + then + ac_cv_path_install="$as_dir/$ac_prog$ac_exec_ext -c" + break 3 + fi + fi + fi + done + done + ;; +esac + + done +IFS=$as_save_IFS + +rm -rf conftest.one conftest.two conftest.dir + +fi + if test "${ac_cv_path_install+set}" = set; then + INSTALL=$ac_cv_path_install + else + # As a last resort, use the slow shell script. Don't cache a + # value for INSTALL within a source directory, because that will + # break other packages using the cache if that directory is + # removed, or if the value is a relative name. + INSTALL=$ac_install_sh + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $INSTALL" >&5 +$as_echo "$INSTALL" >&6; } + +# Use test -z because SunOS4 sh mishandles braces in ${var-val}. +# It thinks the first close brace ends the variable substitution. +test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}' + +test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL}' + +test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644' + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether build environment is sane" >&5 +$as_echo_n "checking whether build environment is sane... " >&6; } +# Reject unsafe characters in $srcdir or the absolute working directory +# name. Accept space and tab only in the latter. +am_lf=' +' +case `pwd` in + *[\\\"\#\$\&\'\`$am_lf]*) + as_fn_error $? "unsafe absolute working directory name" "$LINENO" 5;; +esac +case $srcdir in + *[\\\"\#\$\&\'\`$am_lf\ \ ]*) + as_fn_error $? "unsafe srcdir value: '$srcdir'" "$LINENO" 5;; +esac + +# Do 'set' in a subshell so we don't clobber the current shell's +# arguments. Must try -L first in case configure is actually a +# symlink; some systems play weird games with the mod time of symlinks +# (eg FreeBSD returns the mod time of the symlink's containing +# directory). +if ( + am_has_slept=no + for am_try in 1 2; do + echo "timestamp, slept: $am_has_slept" > conftest.file + set X `ls -Lt "$srcdir/configure" conftest.file 2> /dev/null` + if test "$*" = "X"; then + # -L didn't work. + set X `ls -t "$srcdir/configure" conftest.file` + fi + if test "$*" != "X $srcdir/configure conftest.file" \ + && test "$*" != "X conftest.file $srcdir/configure"; then + + # If neither matched, then we have a broken ls. This can happen + # if, for instance, CONFIG_SHELL is bash and it inherits a + # broken ls alias from the environment. This has actually + # happened. Such a system could not be considered "sane". + as_fn_error $? "ls -t appears to fail. Make sure there is not a broken + alias in your environment" "$LINENO" 5 + fi + if test "$2" = conftest.file || test $am_try -eq 2; then + break + fi + # Just in case. + sleep 1 + am_has_slept=yes + done + test "$2" = conftest.file + ) +then + # Ok. + : +else + as_fn_error $? "newly created file is older than distributed files! +Check your system clock" "$LINENO" 5 +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } +# If we didn't sleep, we still need to ensure time stamps of config.status and +# generated files are strictly newer. +am_sleep_pid= +if grep 'slept: no' conftest.file >/dev/null 2>&1; then + ( sleep 1 ) & + am_sleep_pid=$! +fi + +rm -f conftest.file + +test "$program_prefix" != NONE && + program_transform_name="s&^&$program_prefix&;$program_transform_name" +# Use a double $ so make ignores it. +test "$program_suffix" != NONE && + program_transform_name="s&\$&$program_suffix&;$program_transform_name" +# Double any \ or $. +# By default was `s,x,x', remove it if useless. +ac_script='s/[\\$]/&&/g;s/;s,x,x,$//' +program_transform_name=`$as_echo "$program_transform_name" | sed "$ac_script"` + +if test x"${MISSING+set}" != xset; then + case $am_aux_dir in + *\ * | *\ *) + MISSING="\${SHELL} \"$am_aux_dir/missing\"" ;; + *) + MISSING="\${SHELL} $am_aux_dir/missing" ;; + esac +fi +# Use eval to expand $SHELL +if eval "$MISSING --is-lightweight"; then + am_missing_run="$MISSING " +else + am_missing_run= + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: 'missing' script is too old or missing" >&5 +$as_echo "$as_me: WARNING: 'missing' script is too old or missing" >&2;} +fi + +if test x"${install_sh}" != xset; then + case $am_aux_dir in + *\ * | *\ *) + install_sh="\${SHELL} '$am_aux_dir/install-sh'" ;; + *) + install_sh="\${SHELL} $am_aux_dir/install-sh" + esac +fi + +# Installed binaries are usually stripped using 'strip' when the user +# run "make install-strip". However 'strip' might not be the right +# tool to use in cross-compilation environments, therefore Automake +# will honor the 'STRIP' environment variable to overrule this program. +if test "$cross_compiling" != no; then + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}strip", so it can be a program name with args. +set dummy ${ac_tool_prefix}strip; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_STRIP+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$STRIP"; then + ac_cv_prog_STRIP="$STRIP" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_STRIP="${ac_tool_prefix}strip" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +STRIP=$ac_cv_prog_STRIP +if test -n "$STRIP"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $STRIP" >&5 +$as_echo "$STRIP" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_STRIP"; then + ac_ct_STRIP=$STRIP + # Extract the first word of "strip", so it can be a program name with args. +set dummy strip; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_STRIP+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_STRIP"; then + ac_cv_prog_ac_ct_STRIP="$ac_ct_STRIP" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_STRIP="strip" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_STRIP=$ac_cv_prog_ac_ct_STRIP +if test -n "$ac_ct_STRIP"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_STRIP" >&5 +$as_echo "$ac_ct_STRIP" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_STRIP" = x; then + STRIP=":" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + STRIP=$ac_ct_STRIP + fi +else + STRIP="$ac_cv_prog_STRIP" +fi + +fi +INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s" + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for a thread-safe mkdir -p" >&5 +$as_echo_n "checking for a thread-safe mkdir -p... " >&6; } +if test -z "$MKDIR_P"; then + if ${ac_cv_path_mkdir+:} false; then : + $as_echo_n "(cached) " >&6 +else + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH$PATH_SEPARATOR/opt/sfw/bin +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_prog in mkdir gmkdir; do + for ac_exec_ext in '' $ac_executable_extensions; do + as_fn_executable_p "$as_dir/$ac_prog$ac_exec_ext" || continue + case `"$as_dir/$ac_prog$ac_exec_ext" --version 2>&1` in #( + 'mkdir (GNU coreutils) '* | \ + 'mkdir (coreutils) '* | \ + 'mkdir (fileutils) '4.1*) + ac_cv_path_mkdir=$as_dir/$ac_prog$ac_exec_ext + break 3;; + esac + done + done + done +IFS=$as_save_IFS + +fi + + test -d ./--version && rmdir ./--version + if test "${ac_cv_path_mkdir+set}" = set; then + MKDIR_P="$ac_cv_path_mkdir -p" + else + # As a last resort, use the slow shell script. Don't cache a + # value for MKDIR_P within a source directory, because that will + # break other packages using the cache if that directory is + # removed, or if the value is a relative name. + MKDIR_P="$ac_install_sh -d" + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $MKDIR_P" >&5 +$as_echo "$MKDIR_P" >&6; } + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ${MAKE-make} sets \$(MAKE)" >&5 +$as_echo_n "checking whether ${MAKE-make} sets \$(MAKE)... " >&6; } +set x ${MAKE-make} +ac_make=`$as_echo "$2" | sed 's/+/p/g; s/[^a-zA-Z0-9_]/_/g'` +if eval \${ac_cv_prog_make_${ac_make}_set+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat >conftest.make <<\_ACEOF +SHELL = /bin/sh +all: + @echo '@@@%%%=$(MAKE)=@@@%%%' +_ACEOF +# GNU make sometimes prints "make[1]: Entering ...", which would confuse us. +case `${MAKE-make} -f conftest.make 2>/dev/null` in + *@@@%%%=?*=@@@%%%*) + eval ac_cv_prog_make_${ac_make}_set=yes;; + *) + eval ac_cv_prog_make_${ac_make}_set=no;; +esac +rm -f conftest.make +fi +if eval test \$ac_cv_prog_make_${ac_make}_set = yes; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + SET_MAKE= +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + SET_MAKE="MAKE=${MAKE-make}" +fi + +rm -rf .tst 2>/dev/null +mkdir .tst 2>/dev/null +if test -d .tst; then + am__leading_dot=. +else + am__leading_dot=_ +fi +rmdir .tst 2>/dev/null + +DEPDIR="${am__leading_dot}deps" + +ac_config_commands="$ac_config_commands depfiles" + + +am_make=${MAKE-make} +cat > confinc << 'END' +am__doit: + @echo this is the am__doit target +.PHONY: am__doit +END +# If we don't find an include directive, just comment out the code. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for style of include used by $am_make" >&5 +$as_echo_n "checking for style of include used by $am_make... " >&6; } +am__include="#" +am__quote= +_am_result=none +# First try GNU make style include. +echo "include confinc" > confmf +# Ignore all kinds of additional output from 'make'. +case `$am_make -s -f confmf 2> /dev/null` in #( +*the\ am__doit\ target*) + am__include=include + am__quote= + _am_result=GNU + ;; +esac +# Now try BSD make style include. +if test "$am__include" = "#"; then + echo '.include "confinc"' > confmf + case `$am_make -s -f confmf 2> /dev/null` in #( + *the\ am__doit\ target*) + am__include=.include + am__quote="\"" + _am_result=BSD + ;; + esac +fi + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $_am_result" >&5 +$as_echo "$_am_result" >&6; } +rm -f confinc confmf + +# Check whether --enable-dependency-tracking was given. +if test "${enable_dependency_tracking+set}" = set; then : + enableval=$enable_dependency_tracking; +fi + +if test "x$enable_dependency_tracking" != xno; then + am_depcomp="$ac_aux_dir/depcomp" + AMDEPBACKSLASH='\' + am__nodep='_no' +fi + if test "x$enable_dependency_tracking" != xno; then + AMDEP_TRUE= + AMDEP_FALSE='#' +else + AMDEP_TRUE='#' + AMDEP_FALSE= +fi + + +# Check whether --enable-silent-rules was given. +if test "${enable_silent_rules+set}" = set; then : + enableval=$enable_silent_rules; +fi + +case $enable_silent_rules in # ((( + yes) AM_DEFAULT_VERBOSITY=0;; + no) AM_DEFAULT_VERBOSITY=1;; + *) AM_DEFAULT_VERBOSITY=1;; +esac +am_make=${MAKE-make} +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $am_make supports nested variables" >&5 +$as_echo_n "checking whether $am_make supports nested variables... " >&6; } +if ${am_cv_make_support_nested_variables+:} false; then : + $as_echo_n "(cached) " >&6 +else + if $as_echo 'TRUE=$(BAR$(V)) +BAR0=false +BAR1=true +V=1 +am__doit: + @$(TRUE) +.PHONY: am__doit' | $am_make -f - >/dev/null 2>&1; then + am_cv_make_support_nested_variables=yes +else + am_cv_make_support_nested_variables=no +fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_make_support_nested_variables" >&5 +$as_echo "$am_cv_make_support_nested_variables" >&6; } +if test $am_cv_make_support_nested_variables = yes; then + AM_V='$(V)' + AM_DEFAULT_V='$(AM_DEFAULT_VERBOSITY)' +else + AM_V=$AM_DEFAULT_VERBOSITY + AM_DEFAULT_V=$AM_DEFAULT_VERBOSITY +fi +AM_BACKSLASH='\' + +if test "`cd $srcdir && pwd`" != "`pwd`"; then + # Use -I$(srcdir) only when $(srcdir) != ., so that make's output + # is not polluted with repeated "-I." + am__isrc=' -I$(srcdir)' + # test to see if srcdir already configured + if test -f $srcdir/config.status; then + as_fn_error $? "source directory already configured; run \"make distclean\" there first" "$LINENO" 5 + fi +fi + +# test whether we have cygpath +if test -z "$CYGPATH_W"; then + if (cygpath --version) >/dev/null 2>/dev/null; then + CYGPATH_W='cygpath -w' + else + CYGPATH_W=echo + fi +fi + + +# Define the identity of the package. + PACKAGE='nghttp2' + VERSION='1.0.0' + + +cat >>confdefs.h <<_ACEOF +#define PACKAGE "$PACKAGE" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define VERSION "$VERSION" +_ACEOF + +# Some tools Automake needs. + +ACLOCAL=${ACLOCAL-"${am_missing_run}aclocal-${am__api_version}"} + + +AUTOCONF=${AUTOCONF-"${am_missing_run}autoconf"} + + +AUTOMAKE=${AUTOMAKE-"${am_missing_run}automake-${am__api_version}"} + + +AUTOHEADER=${AUTOHEADER-"${am_missing_run}autoheader"} + + +MAKEINFO=${MAKEINFO-"${am_missing_run}makeinfo"} + +# For better backward compatibility. To be removed once Automake 1.9.x +# dies out for good. For more background, see: +# +# +mkdir_p='$(MKDIR_P)' + +# We need awk for the "check" target. The system "awk" is bad on +# some platforms. +# Always define AMTAR for backward compatibility. Yes, it's still used +# in the wild :-( We should find a proper way to deprecate it ... +AMTAR='$${TAR-tar}' + + +# We'll loop over all known methods to create a tar archive until one works. +_am_tools='gnutar pax cpio none' + +am__tar='$${TAR-tar} chof - "$$tardir"' am__untar='$${TAR-tar} xf -' + + + + + +depcc="$CC" am_compiler_list= + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking dependency style of $depcc" >&5 +$as_echo_n "checking dependency style of $depcc... " >&6; } +if ${am_cv_CC_dependencies_compiler_type+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then + # We make a subdir and do the tests there. Otherwise we can end up + # making bogus files that we don't know about and never remove. For + # instance it was reported that on HP-UX the gcc test will end up + # making a dummy file named 'D' -- because '-MD' means "put the output + # in D". + rm -rf conftest.dir + mkdir conftest.dir + # Copy depcomp to subdir because otherwise we won't find it if we're + # using a relative directory. + cp "$am_depcomp" conftest.dir + cd conftest.dir + # We will build objects and dependencies in a subdirectory because + # it helps to detect inapplicable dependency modes. For instance + # both Tru64's cc and ICC support -MD to output dependencies as a + # side effect of compilation, but ICC will put the dependencies in + # the current directory while Tru64 will put them in the object + # directory. + mkdir sub + + am_cv_CC_dependencies_compiler_type=none + if test "$am_compiler_list" = ""; then + am_compiler_list=`sed -n 's/^#*\([a-zA-Z0-9]*\))$/\1/p' < ./depcomp` + fi + am__universal=false + case " $depcc " in #( + *\ -arch\ *\ -arch\ *) am__universal=true ;; + esac + + for depmode in $am_compiler_list; do + # Setup a source with many dependencies, because some compilers + # like to wrap large dependency lists on column 80 (with \), and + # we should not choose a depcomp mode which is confused by this. + # + # We need to recreate these files for each test, as the compiler may + # overwrite some of them when testing with obscure command lines. + # This happens at least with the AIX C compiler. + : > sub/conftest.c + for i in 1 2 3 4 5 6; do + echo '#include "conftst'$i'.h"' >> sub/conftest.c + # Using ": > sub/conftst$i.h" creates only sub/conftst1.h with + # Solaris 10 /bin/sh. + echo '/* dummy */' > sub/conftst$i.h + done + echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf + + # We check with '-c' and '-o' for the sake of the "dashmstdout" + # mode. It turns out that the SunPro C++ compiler does not properly + # handle '-M -o', and we need to detect this. Also, some Intel + # versions had trouble with output in subdirs. + am__obj=sub/conftest.${OBJEXT-o} + am__minus_obj="-o $am__obj" + case $depmode in + gcc) + # This depmode causes a compiler race in universal mode. + test "$am__universal" = false || continue + ;; + nosideeffect) + # After this tag, mechanisms are not by side-effect, so they'll + # only be used when explicitly requested. + if test "x$enable_dependency_tracking" = xyes; then + continue + else + break + fi + ;; + msvc7 | msvc7msys | msvisualcpp | msvcmsys) + # This compiler won't grok '-c -o', but also, the minuso test has + # not run yet. These depmodes are late enough in the game, and + # so weak that their functioning should not be impacted. + am__obj=conftest.${OBJEXT-o} + am__minus_obj= + ;; + none) break ;; + esac + if depmode=$depmode \ + source=sub/conftest.c object=$am__obj \ + depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \ + $SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \ + >/dev/null 2>conftest.err && + grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 && + grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 && + grep $am__obj sub/conftest.Po > /dev/null 2>&1 && + ${MAKE-make} -s -f confmf > /dev/null 2>&1; then + # icc doesn't choke on unknown options, it will just issue warnings + # or remarks (even with -Werror). So we grep stderr for any message + # that says an option was ignored or not supported. + # When given -MP, icc 7.0 and 7.1 complain thusly: + # icc: Command line warning: ignoring option '-M'; no argument required + # The diagnosis changed in icc 8.0: + # icc: Command line remark: option '-MP' not supported + if (grep 'ignoring option' conftest.err || + grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else + am_cv_CC_dependencies_compiler_type=$depmode + break + fi + fi + done + + cd .. + rm -rf conftest.dir +else + am_cv_CC_dependencies_compiler_type=none +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_CC_dependencies_compiler_type" >&5 +$as_echo "$am_cv_CC_dependencies_compiler_type" >&6; } +CCDEPMODE=depmode=$am_cv_CC_dependencies_compiler_type + + if + test "x$enable_dependency_tracking" != xno \ + && test "$am_cv_CC_dependencies_compiler_type" = gcc3; then + am__fastdepCC_TRUE= + am__fastdepCC_FALSE='#' +else + am__fastdepCC_TRUE='#' + am__fastdepCC_FALSE= +fi + + + +# POSIX will say in a future version that running "rm -f" with no argument +# is OK; and we want to be able to make that assumption in our Makefile +# recipes. So use an aggressive probe to check that the usage we want is +# actually supported "in the wild" to an acceptable degree. +# See automake bug#10828. +# To make any issue more visible, cause the running configure to be aborted +# by default if the 'rm' program in use doesn't match our expectations; the +# user can still override this though. +if rm -f && rm -fr && rm -rf; then : OK; else + cat >&2 <<'END' +Oops! + +Your 'rm' program seems unable to run without file operands specified +on the command line, even when the '-f' option is present. This is contrary +to the behaviour of most rm programs out there, and not conforming with +the upcoming POSIX standard: + +Please tell bug-automake@gnu.org about your system, including the value +of your $PATH and any error possibly output before this message. This +can help us improve future automake versions. + +END + if test x"$ACCEPT_INFERIOR_RM_PROGRAM" = x"yes"; then + echo 'Configuration will proceed anyway, since you have set the' >&2 + echo 'ACCEPT_INFERIOR_RM_PROGRAM variable to "yes"' >&2 + echo >&2 + else + cat >&2 <<'END' +Aborting the configuration process, to ensure you take notice of the issue. + +You can download and install GNU coreutils to get an 'rm' implementation +that behaves properly: . + +If you want to complete the configuration process using your problematic +'rm' anyway, export the environment variable ACCEPT_INFERIOR_RM_PROGRAM +to "yes", and re-run configure. + +END + as_fn_error $? "Your 'rm' program is bad, sorry." "$LINENO" 5 + fi +fi + +# comment out for now since this requires automake 1.13 or higher and +# travis has older one. +# AM_EXTRA_RECURSIVE_TARGETS([it]) + +# Check whether --enable-silent-rules was given. +if test "${enable_silent_rules+set}" = set; then : + enableval=$enable_silent_rules; +fi + +case $enable_silent_rules in # ((( + yes) AM_DEFAULT_VERBOSITY=0;; + no) AM_DEFAULT_VERBOSITY=1;; + *) AM_DEFAULT_VERBOSITY=0;; +esac +am_make=${MAKE-make} +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $am_make supports nested variables" >&5 +$as_echo_n "checking whether $am_make supports nested variables... " >&6; } +if ${am_cv_make_support_nested_variables+:} false; then : + $as_echo_n "(cached) " >&6 +else + if $as_echo 'TRUE=$(BAR$(V)) +BAR0=false +BAR1=true +V=1 +am__doit: + @$(TRUE) +.PHONY: am__doit' | $am_make -f - >/dev/null 2>&1; then + am_cv_make_support_nested_variables=yes +else + am_cv_make_support_nested_variables=no +fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_make_support_nested_variables" >&5 +$as_echo "$am_cv_make_support_nested_variables" >&6; } +if test $am_cv_make_support_nested_variables = yes; then + AM_V='$(V)' + AM_DEFAULT_V='$(AM_DEFAULT_VERBOSITY)' +else + AM_V=$AM_DEFAULT_VERBOSITY + AM_DEFAULT_V=$AM_DEFAULT_VERBOSITY +fi +AM_BACKSLASH='\' + + + +ac_config_headers="$ac_config_headers config.h" + + +LT_CURRENT=14 + +LT_REVISION=0 + +LT_AGE=0 + + +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"` + + + +# Check whether --enable-werror was given. +if test "${enable_werror+set}" = set; then : + enableval=$enable_werror; werror=$enableval +else + werror=no +fi + + +# Check whether --enable-debug was given. +if test "${enable_debug+set}" = set; then : + enableval=$enable_debug; debug=$enableval +else + debug=no +fi + + +# Check whether --enable-threads was given. +if test "${enable_threads+set}" = set; then : + enableval=$enable_threads; threads=$enableval +else + threads=yes +fi + + +# Check whether --enable-app was given. +if test "${enable_app+set}" = set; then : + enableval=$enable_app; request_app=$enableval +else + request_app=check +fi + + +# Check whether --enable-hpack-tools was given. +if test "${enable_hpack_tools+set}" = set; then : + enableval=$enable_hpack_tools; request_hpack_tools=$enableval +else + request_hpack_tools=check +fi + + +# Check whether --enable-asio-lib was given. +if test "${enable_asio_lib+set}" = set; then : + enableval=$enable_asio_lib; request_asio_lib=$enableval +else + request_asio_lib=no +fi + + +# Check whether --enable-examples was given. +if test "${enable_examples+set}" = set; then : + enableval=$enable_examples; request_examples=$enableval +else + request_examples=check +fi + + +# Check whether --enable-python-bindings was given. +if test "${enable_python_bindings+set}" = set; then : + enableval=$enable_python_bindings; request_python_bindings=$enableval +else + request_python_bindings=check +fi + + +# Check whether --enable-failmalloc was given. +if test "${enable_failmalloc+set}" = set; then : + enableval=$enable_failmalloc; request_failmalloc=$enableval +else + request_failmalloc=yes +fi + + + +# Check whether --with-libxml2 was given. +if test "${with_libxml2+set}" = set; then : + withval=$with_libxml2; request_libxml2=$withval +else + request_libxml2=check +fi + + + +# Check whether --with-jemalloc was given. +if test "${with_jemalloc+set}" = set; then : + withval=$with_jemalloc; request_jemalloc=$withval +else + request_jemalloc=check +fi + + + +# Check whether --with-spdylay was given. +if test "${with_spdylay+set}" = set; then : + withval=$with_spdylay; request_spdylay=$withval +else + request_spdylay=check +fi + + + +# Check whether --with-cython was given. +if test "${with_cython+set}" = set; then : + withval=$with_cython; cython_path=$withval +fi + + + + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args. +set dummy ${ac_tool_prefix}gcc; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="${ac_tool_prefix}gcc" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_CC"; then + ac_ct_CC=$CC + # Extract the first word of "gcc", so it can be a program name with args. +set dummy gcc; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_CC="gcc" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 +$as_echo "$ac_ct_CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_CC" = x; then + CC="" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + CC=$ac_ct_CC + fi +else + CC="$ac_cv_prog_CC" +fi + +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args. +set dummy ${ac_tool_prefix}cc; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="${ac_tool_prefix}cc" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + fi +fi +if test -z "$CC"; then + # Extract the first word of "cc", so it can be a program name with args. +set dummy cc; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else + ac_prog_rejected=no +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then + ac_prog_rejected=yes + continue + fi + ac_cv_prog_CC="cc" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +if test $ac_prog_rejected = yes; then + # We found a bogon in the path, so make sure we never use it. + set dummy $ac_cv_prog_CC + shift + if test $# != 0; then + # We chose a different compiler from the bogus one. + # However, it has the same basename, so the bogon will be chosen + # first if we set CC to just the basename; use the full file name. + shift + ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@" + fi +fi +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + for ac_prog in cl.exe + do + # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. +set dummy $ac_tool_prefix$ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="$ac_tool_prefix$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$CC" && break + done +fi +if test -z "$CC"; then + ac_ct_CC=$CC + for ac_prog in cl.exe +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_CC="$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 +$as_echo "$ac_ct_CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$ac_ct_CC" && break +done + + if test "x$ac_ct_CC" = x; then + CC="" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + CC=$ac_ct_CC + fi +fi + +fi + + +test -z "$CC" && { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "no acceptable C compiler found in \$PATH +See \`config.log' for more details" "$LINENO" 5; } + +# Provide some information about the compiler. +$as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5 +set X $ac_compile +ac_compiler=$2 +for ac_option in --version -v -V -qversion; do + { { ac_try="$ac_compiler $ac_option >&5" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_compiler $ac_option >&5") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + sed '10a\ +... rest of stderr output deleted ... + 10q' conftest.err >conftest.er1 + cat conftest.er1 >&5 + fi + rm -f conftest.er1 conftest.err + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } +done + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C compiler" >&5 +$as_echo_n "checking whether we are using the GNU C compiler... " >&6; } +if ${ac_cv_c_compiler_gnu+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ +#ifndef __GNUC__ + choke me +#endif + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_compiler_gnu=yes +else + ac_compiler_gnu=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +ac_cv_c_compiler_gnu=$ac_compiler_gnu + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5 +$as_echo "$ac_cv_c_compiler_gnu" >&6; } +if test $ac_compiler_gnu = yes; then + GCC=yes +else + GCC= +fi +ac_test_CFLAGS=${CFLAGS+set} +ac_save_CFLAGS=$CFLAGS +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5 +$as_echo_n "checking whether $CC accepts -g... " >&6; } +if ${ac_cv_prog_cc_g+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_save_c_werror_flag=$ac_c_werror_flag + ac_c_werror_flag=yes + ac_cv_prog_cc_g=no + CFLAGS="-g" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_prog_cc_g=yes +else + CFLAGS="" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + +else + ac_c_werror_flag=$ac_save_c_werror_flag + CFLAGS="-g" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_prog_cc_g=yes +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + ac_c_werror_flag=$ac_save_c_werror_flag +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5 +$as_echo "$ac_cv_prog_cc_g" >&6; } +if test "$ac_test_CFLAGS" = set; then + CFLAGS=$ac_save_CFLAGS +elif test $ac_cv_prog_cc_g = yes; then + if test "$GCC" = yes; then + CFLAGS="-g -O2" + else + CFLAGS="-g" + fi +else + if test "$GCC" = yes; then + CFLAGS="-O2" + else + CFLAGS= + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C89" >&5 +$as_echo_n "checking for $CC option to accept ISO C89... " >&6; } +if ${ac_cv_prog_cc_c89+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_cv_prog_cc_c89=no +ac_save_CC=$CC +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +#include +struct stat; +/* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */ +struct buf { int x; }; +FILE * (*rcsopen) (struct buf *, struct stat *, int); +static char *e (p, i) + char **p; + int i; +{ + return p[i]; +} +static char *f (char * (*g) (char **, int), char **p, ...) +{ + char *s; + va_list v; + va_start (v,p); + s = g (p, va_arg (v,int)); + va_end (v); + return s; +} + +/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has + function prototypes and stuff, but not '\xHH' hex character constants. + These don't provoke an error unfortunately, instead are silently treated + as 'x'. The following induces an error, until -std is added to get + proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an + array size at least. It's necessary to write '\x00'==0 to get something + that's true only with -std. */ +int osf4_cc_array ['\x00' == 0 ? 1 : -1]; + +/* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters + inside strings and character constants. */ +#define FOO(x) 'x' +int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1]; + +int test (int i, double x); +struct s1 {int (*f) (int a);}; +struct s2 {int (*f) (double a);}; +int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int); +int argc; +char **argv; +int +main () +{ +return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]; + ; + return 0; +} +_ACEOF +for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \ + -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" +do + CC="$ac_save_CC $ac_arg" + if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_prog_cc_c89=$ac_arg +fi +rm -f core conftest.err conftest.$ac_objext + test "x$ac_cv_prog_cc_c89" != "xno" && break +done +rm -f conftest.$ac_ext +CC=$ac_save_CC + +fi +# AC_CACHE_VAL +case "x$ac_cv_prog_cc_c89" in + x) + { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 +$as_echo "none needed" >&6; } ;; + xno) + { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 +$as_echo "unsupported" >&6; } ;; + *) + CC="$CC $ac_cv_prog_cc_c89" + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5 +$as_echo "$ac_cv_prog_cc_c89" >&6; } ;; +esac +if test "x$ac_cv_prog_cc_c89" != xno; then : + +fi + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC understands -c and -o together" >&5 +$as_echo_n "checking whether $CC understands -c and -o together... " >&6; } +if ${am_cv_prog_cc_c_o+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF + # Make sure it works both with $CC and with simple cc. + # Following AC_PROG_CC_C_O, we do the test twice because some + # compilers refuse to overwrite an existing .o file with -o, + # though they will create one. + am_cv_prog_cc_c_o=yes + for am_i in 1 2; do + if { echo "$as_me:$LINENO: $CC -c conftest.$ac_ext -o conftest2.$ac_objext" >&5 + ($CC -c conftest.$ac_ext -o conftest2.$ac_objext) >&5 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } \ + && test -f conftest2.$ac_objext; then + : OK + else + am_cv_prog_cc_c_o=no + break + fi + done + rm -f core conftest* + unset am_i +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_prog_cc_c_o" >&5 +$as_echo "$am_cv_prog_cc_c_o" >&6; } +if test "$am_cv_prog_cc_c_o" != yes; then + # Losing compiler, so override with the script. + # FIXME: It is wrong to rewrite CC. + # But if we don't then we get into trouble of one sort or another. + # A longer-term fix would be to have automake use am__CC in this case, + # and then we could set am__CC="\$(top_srcdir)/compile \$(CC)" + CC="$am_aux_dir/compile $CC" +fi +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + +ac_ext=cpp +ac_cpp='$CXXCPP $CPPFLAGS' +ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_cxx_compiler_gnu +if test -z "$CXX"; then + if test -n "$CCC"; then + CXX=$CCC + else + if test -n "$ac_tool_prefix"; then + for ac_prog in g++ c++ gpp aCC CC cxx cc++ cl.exe FCC KCC RCC xlC_r xlC + do + # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. +set dummy $ac_tool_prefix$ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_CXX+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$CXX"; then + ac_cv_prog_CXX="$CXX" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CXX="$ac_tool_prefix$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +CXX=$ac_cv_prog_CXX +if test -n "$CXX"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CXX" >&5 +$as_echo "$CXX" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$CXX" && break + done +fi +if test -z "$CXX"; then + ac_ct_CXX=$CXX + for ac_prog in g++ c++ gpp aCC CC cxx cc++ cl.exe FCC KCC RCC xlC_r xlC +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_CXX+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_CXX"; then + ac_cv_prog_ac_ct_CXX="$ac_ct_CXX" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_CXX="$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_CXX=$ac_cv_prog_ac_ct_CXX +if test -n "$ac_ct_CXX"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CXX" >&5 +$as_echo "$ac_ct_CXX" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$ac_ct_CXX" && break +done + + if test "x$ac_ct_CXX" = x; then + CXX="g++" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + CXX=$ac_ct_CXX + fi +fi + + fi +fi +# Provide some information about the compiler. +$as_echo "$as_me:${as_lineno-$LINENO}: checking for C++ compiler version" >&5 +set X $ac_compile +ac_compiler=$2 +for ac_option in --version -v -V -qversion; do + { { ac_try="$ac_compiler $ac_option >&5" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_compiler $ac_option >&5") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + sed '10a\ +... rest of stderr output deleted ... + 10q' conftest.err >conftest.er1 + cat conftest.er1 >&5 + fi + rm -f conftest.er1 conftest.err + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } +done + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C++ compiler" >&5 +$as_echo_n "checking whether we are using the GNU C++ compiler... " >&6; } +if ${ac_cv_cxx_compiler_gnu+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ +#ifndef __GNUC__ + choke me +#endif + + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_compile "$LINENO"; then : + ac_compiler_gnu=yes +else + ac_compiler_gnu=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +ac_cv_cxx_compiler_gnu=$ac_compiler_gnu + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cxx_compiler_gnu" >&5 +$as_echo "$ac_cv_cxx_compiler_gnu" >&6; } +if test $ac_compiler_gnu = yes; then + GXX=yes +else + GXX= +fi +ac_test_CXXFLAGS=${CXXFLAGS+set} +ac_save_CXXFLAGS=$CXXFLAGS +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CXX accepts -g" >&5 +$as_echo_n "checking whether $CXX accepts -g... " >&6; } +if ${ac_cv_prog_cxx_g+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_save_cxx_werror_flag=$ac_cxx_werror_flag + ac_cxx_werror_flag=yes + ac_cv_prog_cxx_g=no + CXXFLAGS="-g" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_compile "$LINENO"; then : + ac_cv_prog_cxx_g=yes +else + CXXFLAGS="" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_compile "$LINENO"; then : + +else + ac_cxx_werror_flag=$ac_save_cxx_werror_flag + CXXFLAGS="-g" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_compile "$LINENO"; then : + ac_cv_prog_cxx_g=yes +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + ac_cxx_werror_flag=$ac_save_cxx_werror_flag +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cxx_g" >&5 +$as_echo "$ac_cv_prog_cxx_g" >&6; } +if test "$ac_test_CXXFLAGS" = set; then + CXXFLAGS=$ac_save_CXXFLAGS +elif test $ac_cv_prog_cxx_g = yes; then + if test "$GXX" = yes; then + CXXFLAGS="-g -O2" + else + CXXFLAGS="-g" + fi +else + if test "$GXX" = yes; then + CXXFLAGS="-O2" + else + CXXFLAGS= + fi +fi +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + + +func_stripname_cnf () +{ + case ${2} in + .*) func_stripname_result=`$ECHO "${3}" | $SED "s%^${1}%%; s%\\\\${2}\$%%"`;; + *) func_stripname_result=`$ECHO "${3}" | $SED "s%^${1}%%; s%${2}\$%%"`;; + esac +} # func_stripname_cnf + + if test -n "$CXX" && ( test "X$CXX" != "Xno" && + ( (test "X$CXX" = "Xg++" && `g++ -v >/dev/null 2>&1` ) || + (test "X$CXX" != "Xg++"))) ; then + ac_ext=cpp +ac_cpp='$CXXCPP $CPPFLAGS' +ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_cxx_compiler_gnu +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to run the C++ preprocessor" >&5 +$as_echo_n "checking how to run the C++ preprocessor... " >&6; } +if test -z "$CXXCPP"; then + if ${ac_cv_prog_CXXCPP+:} false; then : + $as_echo_n "(cached) " >&6 +else + # Double quotes because CXXCPP needs to be expanded + for CXXCPP in "$CXX -E" "/lib/cpp" + do + ac_preproc_ok=false +for ac_cxx_preproc_warn_flag in '' yes +do + # Use a header file that comes with gcc, so configuring glibc + # with a fresh cross-compiler works. + # Prefer to if __STDC__ is defined, since + # exists even on freestanding compilers. + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. "Syntax error" is here to catch this case. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#ifdef __STDC__ +# include +#else +# include +#endif + Syntax error +_ACEOF +if ac_fn_cxx_try_cpp "$LINENO"; then : + +else + # Broken: fails on valid input. +continue +fi +rm -f conftest.err conftest.i conftest.$ac_ext + + # OK, works on sane cases. Now check whether nonexistent headers + # can be detected and how. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +_ACEOF +if ac_fn_cxx_try_cpp "$LINENO"; then : + # Broken: success on invalid input. +continue +else + # Passes both tests. +ac_preproc_ok=: +break +fi +rm -f conftest.err conftest.i conftest.$ac_ext + +done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f conftest.i conftest.err conftest.$ac_ext +if $ac_preproc_ok; then : + break +fi + + done + ac_cv_prog_CXXCPP=$CXXCPP + +fi + CXXCPP=$ac_cv_prog_CXXCPP +else + ac_cv_prog_CXXCPP=$CXXCPP +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $CXXCPP" >&5 +$as_echo "$CXXCPP" >&6; } +ac_preproc_ok=false +for ac_cxx_preproc_warn_flag in '' yes +do + # Use a header file that comes with gcc, so configuring glibc + # with a fresh cross-compiler works. + # Prefer to if __STDC__ is defined, since + # exists even on freestanding compilers. + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. "Syntax error" is here to catch this case. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#ifdef __STDC__ +# include +#else +# include +#endif + Syntax error +_ACEOF +if ac_fn_cxx_try_cpp "$LINENO"; then : + +else + # Broken: fails on valid input. +continue +fi +rm -f conftest.err conftest.i conftest.$ac_ext + + # OK, works on sane cases. Now check whether nonexistent headers + # can be detected and how. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +_ACEOF +if ac_fn_cxx_try_cpp "$LINENO"; then : + # Broken: success on invalid input. +continue +else + # Passes both tests. +ac_preproc_ok=: +break +fi +rm -f conftest.err conftest.i conftest.$ac_ext + +done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f conftest.i conftest.err conftest.$ac_ext +if $ac_preproc_ok; then : + +else + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "C++ preprocessor \"$CXXCPP\" fails sanity check +See \`config.log' for more details" "$LINENO" 5; } +fi + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + +else + _lt_caught_CXX_error=yes +fi + +ac_ext=cpp +ac_cpp='$CXXCPP $CPPFLAGS' +ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_cxx_compiler_gnu + +archive_cmds_need_lc_CXX=no +allow_undefined_flag_CXX= +always_export_symbols_CXX=no +archive_expsym_cmds_CXX= +compiler_needs_object_CXX=no +export_dynamic_flag_spec_CXX= +hardcode_direct_CXX=no +hardcode_direct_absolute_CXX=no +hardcode_libdir_flag_spec_CXX= +hardcode_libdir_separator_CXX= +hardcode_minus_L_CXX=no +hardcode_shlibpath_var_CXX=unsupported +hardcode_automatic_CXX=no +inherit_rpath_CXX=no +module_cmds_CXX= +module_expsym_cmds_CXX= +link_all_deplibs_CXX=unknown +old_archive_cmds_CXX=$old_archive_cmds +reload_flag_CXX=$reload_flag +reload_cmds_CXX=$reload_cmds +no_undefined_flag_CXX= +whole_archive_flag_spec_CXX= +enable_shared_with_static_runtimes_CXX=no + +# Source file extension for C++ test sources. +ac_ext=cpp + +# Object file extension for compiled C++ test sources. +objext=o +objext_CXX=$objext + +# No sense in running all these tests if we already determined that +# the CXX compiler isn't working. Some variables (like enable_shared) +# are currently assumed to apply to all compilers on this platform, +# and will be corrupted by setting them based on a non-working compiler. +if test "$_lt_caught_CXX_error" != yes; then + # Code to be used in simple compile tests + lt_simple_compile_test_code="int some_variable = 0;" + + # Code to be used in simple link tests + lt_simple_link_test_code='int main(int, char *[]) { return(0); }' + + # ltmain only uses $CC for tagged configurations so make sure $CC is set. + + + + + + +# If no C compiler was specified, use CC. +LTCC=${LTCC-"$CC"} + +# If no C compiler flags were specified, use CFLAGS. +LTCFLAGS=${LTCFLAGS-"$CFLAGS"} + +# Allow CC to be a program name with arguments. +compiler=$CC + + + # save warnings/boilerplate of simple test code + ac_outfile=conftest.$ac_objext +echo "$lt_simple_compile_test_code" >conftest.$ac_ext +eval "$ac_compile" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err +_lt_compiler_boilerplate=`cat conftest.err` +$RM conftest* + + ac_outfile=conftest.$ac_objext +echo "$lt_simple_link_test_code" >conftest.$ac_ext +eval "$ac_link" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err +_lt_linker_boilerplate=`cat conftest.err` +$RM -r conftest* + + + # Allow CC to be a program name with arguments. + lt_save_CC=$CC + lt_save_CFLAGS=$CFLAGS + lt_save_LD=$LD + lt_save_GCC=$GCC + GCC=$GXX + lt_save_with_gnu_ld=$with_gnu_ld + lt_save_path_LD=$lt_cv_path_LD + if test -n "${lt_cv_prog_gnu_ldcxx+set}"; then + lt_cv_prog_gnu_ld=$lt_cv_prog_gnu_ldcxx + else + $as_unset lt_cv_prog_gnu_ld + fi + if test -n "${lt_cv_path_LDCXX+set}"; then + lt_cv_path_LD=$lt_cv_path_LDCXX + else + $as_unset lt_cv_path_LD + fi + test -z "${LDCXX+set}" || LD=$LDCXX + CC=${CXX-"c++"} + CFLAGS=$CXXFLAGS + compiler=$CC + compiler_CXX=$CC + for cc_temp in $compiler""; do + case $cc_temp in + compile | *[\\/]compile | ccache | *[\\/]ccache ) ;; + distcc | *[\\/]distcc | purify | *[\\/]purify ) ;; + \-*) ;; + *) break;; + esac +done +cc_basename=`$ECHO "$cc_temp" | $SED "s%.*/%%; s%^$host_alias-%%"` + + + if test -n "$compiler"; then + # We don't want -fno-exception when compiling C++ code, so set the + # no_builtin_flag separately + if test "$GXX" = yes; then + lt_prog_compiler_no_builtin_flag_CXX=' -fno-builtin' + else + lt_prog_compiler_no_builtin_flag_CXX= + fi + + if test "$GXX" = yes; then + # Set up default GNU C++ configuration + + + +# Check whether --with-gnu-ld was given. +if test "${with_gnu_ld+set}" = set; then : + withval=$with_gnu_ld; test "$withval" = no || with_gnu_ld=yes +else + with_gnu_ld=no +fi + +ac_prog=ld +if test "$GCC" = yes; then + # Check if gcc -print-prog-name=ld gives a path. + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ld used by $CC" >&5 +$as_echo_n "checking for ld used by $CC... " >&6; } + case $host in + *-*-mingw*) + # gcc leaves a trailing carriage return which upsets mingw + ac_prog=`($CC -print-prog-name=ld) 2>&5 | tr -d '\015'` ;; + *) + ac_prog=`($CC -print-prog-name=ld) 2>&5` ;; + esac + case $ac_prog in + # Accept absolute paths. + [\\/]* | ?:[\\/]*) + re_direlt='/[^/][^/]*/\.\./' + # Canonicalize the pathname of ld + ac_prog=`$ECHO "$ac_prog"| $SED 's%\\\\%/%g'` + while $ECHO "$ac_prog" | $GREP "$re_direlt" > /dev/null 2>&1; do + ac_prog=`$ECHO $ac_prog| $SED "s%$re_direlt%/%"` + done + test -z "$LD" && LD="$ac_prog" + ;; + "") + # If it fails, then pretend we aren't using GCC. + ac_prog=ld + ;; + *) + # If it is relative, then search for the first ld in PATH. + with_gnu_ld=unknown + ;; + esac +elif test "$with_gnu_ld" = yes; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for GNU ld" >&5 +$as_echo_n "checking for GNU ld... " >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for non-GNU ld" >&5 +$as_echo_n "checking for non-GNU ld... " >&6; } +fi +if ${lt_cv_path_LD+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -z "$LD"; then + lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR + for ac_dir in $PATH; do + IFS="$lt_save_ifs" + test -z "$ac_dir" && ac_dir=. + if test -f "$ac_dir/$ac_prog" || test -f "$ac_dir/$ac_prog$ac_exeext"; then + lt_cv_path_LD="$ac_dir/$ac_prog" + # Check to see if the program is GNU ld. I'd rather use --version, + # but apparently some variants of GNU ld only accept -v. + # Break only if it was the GNU/non-GNU ld that we prefer. + case `"$lt_cv_path_LD" -v 2>&1 &5 +$as_echo "$LD" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi +test -z "$LD" && as_fn_error $? "no acceptable ld found in \$PATH" "$LINENO" 5 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if the linker ($LD) is GNU ld" >&5 +$as_echo_n "checking if the linker ($LD) is GNU ld... " >&6; } +if ${lt_cv_prog_gnu_ld+:} false; then : + $as_echo_n "(cached) " >&6 +else + # I'd rather use --version here, but apparently some GNU lds only accept -v. +case `$LD -v 2>&1 &5 +$as_echo "$lt_cv_prog_gnu_ld" >&6; } +with_gnu_ld=$lt_cv_prog_gnu_ld + + + + + + + + # Check if GNU C++ uses GNU ld as the underlying linker, since the + # archiving commands below assume that GNU ld is being used. + if test "$with_gnu_ld" = yes; then + archive_cmds_CXX='$CC $pic_flag -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib' + archive_expsym_cmds_CXX='$CC $pic_flag -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' + + hardcode_libdir_flag_spec_CXX='${wl}-rpath ${wl}$libdir' + export_dynamic_flag_spec_CXX='${wl}--export-dynamic' + + # If archive_cmds runs LD, not CC, wlarc should be empty + # XXX I think wlarc can be eliminated in ltcf-cxx, but I need to + # investigate it a little bit more. (MM) + wlarc='${wl}' + + # ancient GNU ld didn't support --whole-archive et. al. + if eval "`$CC -print-prog-name=ld` --help 2>&1" | + $GREP 'no-whole-archive' > /dev/null; then + whole_archive_flag_spec_CXX="$wlarc"'--whole-archive$convenience '"$wlarc"'--no-whole-archive' + else + whole_archive_flag_spec_CXX= + fi + else + with_gnu_ld=no + wlarc= + + # A generic and very simple default shared library creation + # command for GNU C++ for the case where it uses the native + # linker, instead of GNU ld. If possible, this setting should + # overridden to take advantage of the native linker features on + # the platform it is being used on. + archive_cmds_CXX='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $lib' + fi + + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"' + + else + GXX=no + with_gnu_ld=no + wlarc= + fi + + # PORTME: fill in a description of your system's C++ link characteristics + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the $compiler linker ($LD) supports shared libraries" >&5 +$as_echo_n "checking whether the $compiler linker ($LD) supports shared libraries... " >&6; } + ld_shlibs_CXX=yes + case $host_os in + aix3*) + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + aix[4-9]*) + if test "$host_cpu" = ia64; then + # On IA64, the linker does run time linking by default, so we don't + # have to do anything special. + aix_use_runtimelinking=no + exp_sym_flag='-Bexport' + no_entry_flag="" + else + aix_use_runtimelinking=no + + # Test if we are trying to use run time linking or normal + # AIX style linking. If -brtl is somewhere in LDFLAGS, we + # need to do runtime linking. + case $host_os in aix4.[23]|aix4.[23].*|aix[5-9]*) + for ld_flag in $LDFLAGS; do + case $ld_flag in + *-brtl*) + aix_use_runtimelinking=yes + break + ;; + esac + done + ;; + esac + + exp_sym_flag='-bexport' + no_entry_flag='-bnoentry' + fi + + # When large executables or shared objects are built, AIX ld can + # have problems creating the table of contents. If linking a library + # or program results in "error TOC overflow" add -mminimal-toc to + # CXXFLAGS/CFLAGS for g++/gcc. In the cases where that is not + # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS. + + archive_cmds_CXX='' + hardcode_direct_CXX=yes + hardcode_direct_absolute_CXX=yes + hardcode_libdir_separator_CXX=':' + link_all_deplibs_CXX=yes + file_list_spec_CXX='${wl}-f,' + + if test "$GXX" = yes; then + case $host_os in aix4.[012]|aix4.[012].*) + # We only want to do this on AIX 4.2 and lower, the check + # below for broken collect2 doesn't work under 4.3+ + collect2name=`${CC} -print-prog-name=collect2` + if test -f "$collect2name" && + strings "$collect2name" | $GREP resolve_lib_name >/dev/null + then + # We have reworked collect2 + : + else + # We have old collect2 + hardcode_direct_CXX=unsupported + # It fails to find uninstalled libraries when the uninstalled + # path is not listed in the libpath. Setting hardcode_minus_L + # to unsupported forces relinking + hardcode_minus_L_CXX=yes + hardcode_libdir_flag_spec_CXX='-L$libdir' + hardcode_libdir_separator_CXX= + fi + esac + shared_flag='-shared' + if test "$aix_use_runtimelinking" = yes; then + shared_flag="$shared_flag "'${wl}-G' + fi + else + # not using gcc + if test "$host_cpu" = ia64; then + # VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release + # chokes on -Wl,-G. The following line is correct: + shared_flag='-G' + else + if test "$aix_use_runtimelinking" = yes; then + shared_flag='${wl}-G' + else + shared_flag='${wl}-bM:SRE' + fi + fi + fi + + export_dynamic_flag_spec_CXX='${wl}-bexpall' + # It seems that -bexpall does not export symbols beginning with + # underscore (_), so it is better to generate a list of symbols to + # export. + always_export_symbols_CXX=yes + if test "$aix_use_runtimelinking" = yes; then + # Warning - without using the other runtime loading flags (-brtl), + # -berok will link without error, but may produce a broken library. + allow_undefined_flag_CXX='-berok' + # Determine the default libpath from the value encoded in an empty + # executable. + if test "${lt_cv_aix_libpath+set}" = set; then + aix_libpath=$lt_cv_aix_libpath +else + if ${lt_cv_aix_libpath__CXX+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_link "$LINENO"; then : + + lt_aix_libpath_sed=' + /Import File Strings/,/^$/ { + /^0/ { + s/^0 *\([^ ]*\) *$/\1/ + p + } + }' + lt_cv_aix_libpath__CXX=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` + # Check for a 64-bit object if we didn't find anything. + if test -z "$lt_cv_aix_libpath__CXX"; then + lt_cv_aix_libpath__CXX=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` + fi +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + if test -z "$lt_cv_aix_libpath__CXX"; then + lt_cv_aix_libpath__CXX="/usr/lib:/lib" + fi + +fi + + aix_libpath=$lt_cv_aix_libpath__CXX +fi + + hardcode_libdir_flag_spec_CXX='${wl}-blibpath:$libdir:'"$aix_libpath" + + archive_expsym_cmds_CXX='$CC -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags `if test "x${allow_undefined_flag}" != "x"; then func_echo_all "${wl}${allow_undefined_flag}"; else :; fi` '"\${wl}$exp_sym_flag:\$export_symbols $shared_flag" + else + if test "$host_cpu" = ia64; then + hardcode_libdir_flag_spec_CXX='${wl}-R $libdir:/usr/lib:/lib' + allow_undefined_flag_CXX="-z nodefs" + archive_expsym_cmds_CXX="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags ${wl}${allow_undefined_flag} '"\${wl}$exp_sym_flag:\$export_symbols" + else + # Determine the default libpath from the value encoded in an + # empty executable. + if test "${lt_cv_aix_libpath+set}" = set; then + aix_libpath=$lt_cv_aix_libpath +else + if ${lt_cv_aix_libpath__CXX+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_link "$LINENO"; then : + + lt_aix_libpath_sed=' + /Import File Strings/,/^$/ { + /^0/ { + s/^0 *\([^ ]*\) *$/\1/ + p + } + }' + lt_cv_aix_libpath__CXX=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` + # Check for a 64-bit object if we didn't find anything. + if test -z "$lt_cv_aix_libpath__CXX"; then + lt_cv_aix_libpath__CXX=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` + fi +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + if test -z "$lt_cv_aix_libpath__CXX"; then + lt_cv_aix_libpath__CXX="/usr/lib:/lib" + fi + +fi + + aix_libpath=$lt_cv_aix_libpath__CXX +fi + + hardcode_libdir_flag_spec_CXX='${wl}-blibpath:$libdir:'"$aix_libpath" + # Warning - without using the other run time loading flags, + # -berok will link without error, but may produce a broken library. + no_undefined_flag_CXX=' ${wl}-bernotok' + allow_undefined_flag_CXX=' ${wl}-berok' + if test "$with_gnu_ld" = yes; then + # We only use this code for GNU lds that support --whole-archive. + whole_archive_flag_spec_CXX='${wl}--whole-archive$convenience ${wl}--no-whole-archive' + else + # Exported symbols can be pulled into shared objects from archives + whole_archive_flag_spec_CXX='$convenience' + fi + archive_cmds_need_lc_CXX=yes + # This is similar to how AIX traditionally builds its shared + # libraries. + archive_expsym_cmds_CXX="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs ${wl}-bnoentry $compiler_flags ${wl}-bE:$export_symbols${allow_undefined_flag}~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$soname' + fi + fi + ;; + + beos*) + if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then + allow_undefined_flag_CXX=unsupported + # Joseph Beckenbach says some releases of gcc + # support --undefined. This deserves some investigation. FIXME + archive_cmds_CXX='$CC -nostart $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + else + ld_shlibs_CXX=no + fi + ;; + + chorus*) + case $cc_basename in + *) + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + esac + ;; + + cygwin* | mingw* | pw32* | cegcc*) + case $GXX,$cc_basename in + ,cl* | no,cl*) + # Native MSVC + # hardcode_libdir_flag_spec is actually meaningless, as there is + # no search path for DLLs. + hardcode_libdir_flag_spec_CXX=' ' + allow_undefined_flag_CXX=unsupported + always_export_symbols_CXX=yes + file_list_spec_CXX='@' + # Tell ltmain to make .lib files, not .a files. + libext=lib + # Tell ltmain to make .dll files, not .so files. + shrext_cmds=".dll" + # FIXME: Setting linknames here is a bad hack. + archive_cmds_CXX='$CC -o $output_objdir/$soname $libobjs $compiler_flags $deplibs -Wl,-dll~linknames=' + archive_expsym_cmds_CXX='if test "x`$SED 1q $export_symbols`" = xEXPORTS; then + $SED -n -e 's/\\\\\\\(.*\\\\\\\)/-link\\\ -EXPORT:\\\\\\\1/' -e '1\\\!p' < $export_symbols > $output_objdir/$soname.exp; + else + $SED -e 's/\\\\\\\(.*\\\\\\\)/-link\\\ -EXPORT:\\\\\\\1/' < $export_symbols > $output_objdir/$soname.exp; + fi~ + $CC -o $tool_output_objdir$soname $libobjs $compiler_flags $deplibs "@$tool_output_objdir$soname.exp" -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~ + linknames=' + # The linker will not automatically build a static lib if we build a DLL. + # _LT_TAGVAR(old_archive_from_new_cmds, CXX)='true' + enable_shared_with_static_runtimes_CXX=yes + # Don't use ranlib + old_postinstall_cmds_CXX='chmod 644 $oldlib' + postlink_cmds_CXX='lt_outputfile="@OUTPUT@"~ + lt_tool_outputfile="@TOOL_OUTPUT@"~ + case $lt_outputfile in + *.exe|*.EXE) ;; + *) + lt_outputfile="$lt_outputfile.exe" + lt_tool_outputfile="$lt_tool_outputfile.exe" + ;; + esac~ + func_to_tool_file "$lt_outputfile"~ + if test "$MANIFEST_TOOL" != ":" && test -f "$lt_outputfile.manifest"; then + $MANIFEST_TOOL -manifest "$lt_tool_outputfile.manifest" -outputresource:"$lt_tool_outputfile" || exit 1; + $RM "$lt_outputfile.manifest"; + fi' + ;; + *) + # g++ + # _LT_TAGVAR(hardcode_libdir_flag_spec, CXX) is actually meaningless, + # as there is no search path for DLLs. + hardcode_libdir_flag_spec_CXX='-L$libdir' + export_dynamic_flag_spec_CXX='${wl}--export-all-symbols' + allow_undefined_flag_CXX=unsupported + always_export_symbols_CXX=no + enable_shared_with_static_runtimes_CXX=yes + + if $LD --help 2>&1 | $GREP 'auto-import' > /dev/null; then + archive_cmds_CXX='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' + # If the export-symbols file already is a .def file (1st line + # is EXPORTS), use it as is; otherwise, prepend... + archive_expsym_cmds_CXX='if test "x`$SED 1q $export_symbols`" = xEXPORTS; then + cp $export_symbols $output_objdir/$soname.def; + else + echo EXPORTS > $output_objdir/$soname.def; + cat $export_symbols >> $output_objdir/$soname.def; + fi~ + $CC -shared -nostdlib $output_objdir/$soname.def $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' + else + ld_shlibs_CXX=no + fi + ;; + esac + ;; + darwin* | rhapsody*) + + + archive_cmds_need_lc_CXX=no + hardcode_direct_CXX=no + hardcode_automatic_CXX=yes + hardcode_shlibpath_var_CXX=unsupported + if test "$lt_cv_ld_force_load" = "yes"; then + whole_archive_flag_spec_CXX='`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience ${wl}-force_load,$conv\"; done; func_echo_all \"$new_convenience\"`' + + else + whole_archive_flag_spec_CXX='' + fi + link_all_deplibs_CXX=yes + allow_undefined_flag_CXX="$_lt_dar_allow_undefined" + case $cc_basename in + ifort*) _lt_dar_can_shared=yes ;; + *) _lt_dar_can_shared=$GCC ;; + esac + if test "$_lt_dar_can_shared" = "yes"; then + output_verbose_link_cmd=func_echo_all + archive_cmds_CXX="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod${_lt_dsymutil}" + module_cmds_CXX="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dsymutil}" + archive_expsym_cmds_CXX="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring ${_lt_dar_single_mod}${_lt_dar_export_syms}${_lt_dsymutil}" + module_expsym_cmds_CXX="sed -e 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dar_export_syms}${_lt_dsymutil}" + if test "$lt_cv_apple_cc_single_mod" != "yes"; then + archive_cmds_CXX="\$CC -r -keep_private_externs -nostdlib -o \${lib}-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \${lib}-master.o \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring${_lt_dsymutil}" + archive_expsym_cmds_CXX="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -r -keep_private_externs -nostdlib -o \${lib}-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \${lib}-master.o \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring${_lt_dar_export_syms}${_lt_dsymutil}" + fi + + else + ld_shlibs_CXX=no + fi + + ;; + + dgux*) + case $cc_basename in + ec++*) + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + ghcx*) + # Green Hills C++ Compiler + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + *) + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + esac + ;; + + freebsd2.*) + # C++ shared libraries reported to be fairly broken before + # switch to ELF + ld_shlibs_CXX=no + ;; + + freebsd-elf*) + archive_cmds_need_lc_CXX=no + ;; + + freebsd* | dragonfly*) + # FreeBSD 3 and later use GNU C++ and GNU ld with standard ELF + # conventions + ld_shlibs_CXX=yes + ;; + + haiku*) + archive_cmds_CXX='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + link_all_deplibs_CXX=yes + ;; + + hpux9*) + hardcode_libdir_flag_spec_CXX='${wl}+b ${wl}$libdir' + hardcode_libdir_separator_CXX=: + export_dynamic_flag_spec_CXX='${wl}-E' + hardcode_direct_CXX=yes + hardcode_minus_L_CXX=yes # Not in the search PATH, + # but as the default + # location of the library. + + case $cc_basename in + CC*) + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + aCC*) + archive_cmds_CXX='$RM $output_objdir/$soname~$CC -b ${wl}+b ${wl}$install_libdir -o $output_objdir/$soname $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib' + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + # + # There doesn't appear to be a way to prevent this compiler from + # explicitly linking system object files so we need to strip them + # from the output so that they don't get included in the library + # dependencies. + output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | $EGREP "\-L"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"' + ;; + *) + if test "$GXX" = yes; then + archive_cmds_CXX='$RM $output_objdir/$soname~$CC -shared -nostdlib $pic_flag ${wl}+b ${wl}$install_libdir -o $output_objdir/$soname $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib' + else + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + fi + ;; + esac + ;; + + hpux10*|hpux11*) + if test $with_gnu_ld = no; then + hardcode_libdir_flag_spec_CXX='${wl}+b ${wl}$libdir' + hardcode_libdir_separator_CXX=: + + case $host_cpu in + hppa*64*|ia64*) + ;; + *) + export_dynamic_flag_spec_CXX='${wl}-E' + ;; + esac + fi + case $host_cpu in + hppa*64*|ia64*) + hardcode_direct_CXX=no + hardcode_shlibpath_var_CXX=no + ;; + *) + hardcode_direct_CXX=yes + hardcode_direct_absolute_CXX=yes + hardcode_minus_L_CXX=yes # Not in the search PATH, + # but as the default + # location of the library. + ;; + esac + + case $cc_basename in + CC*) + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + aCC*) + case $host_cpu in + hppa*64*) + archive_cmds_CXX='$CC -b ${wl}+h ${wl}$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + ;; + ia64*) + archive_cmds_CXX='$CC -b ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + ;; + *) + archive_cmds_CXX='$CC -b ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + ;; + esac + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + # + # There doesn't appear to be a way to prevent this compiler from + # explicitly linking system object files so we need to strip them + # from the output so that they don't get included in the library + # dependencies. + output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | $GREP "\-L"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"' + ;; + *) + if test "$GXX" = yes; then + if test $with_gnu_ld = no; then + case $host_cpu in + hppa*64*) + archive_cmds_CXX='$CC -shared -nostdlib -fPIC ${wl}+h ${wl}$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + ;; + ia64*) + archive_cmds_CXX='$CC -shared -nostdlib $pic_flag ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + ;; + *) + archive_cmds_CXX='$CC -shared -nostdlib $pic_flag ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + ;; + esac + fi + else + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + fi + ;; + esac + ;; + + interix[3-9]*) + hardcode_direct_CXX=no + hardcode_shlibpath_var_CXX=no + hardcode_libdir_flag_spec_CXX='${wl}-rpath,$libdir' + export_dynamic_flag_spec_CXX='${wl}-E' + # Hack: On Interix 3.x, we cannot compile PIC because of a broken gcc. + # Instead, shared libraries are loaded at an image base (0x10000000 by + # default) and relocated if they conflict, which is a slow very memory + # consuming and fragmenting process. To avoid this, we pick a random, + # 256 KiB-aligned image base between 0x50000000 and 0x6FFC0000 at link + # time. Moving up from 0x10000000 also allows more sbrk(2) space. + archive_cmds_CXX='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' + archive_expsym_cmds_CXX='sed "s,^,_," $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--retain-symbols-file,$output_objdir/$soname.expsym ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' + ;; + irix5* | irix6*) + case $cc_basename in + CC*) + # SGI C++ + archive_cmds_CXX='$CC -shared -all -multigot $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib' + + # Archives containing C++ object files must be created using + # "CC -ar", where "CC" is the IRIX C++ compiler. This is + # necessary to make sure instantiated templates are included + # in the archive. + old_archive_cmds_CXX='$CC -ar -WR,-u -o $oldlib $oldobjs' + ;; + *) + if test "$GXX" = yes; then + if test "$with_gnu_ld" = no; then + archive_cmds_CXX='$CC -shared $pic_flag -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' + else + archive_cmds_CXX='$CC -shared $pic_flag -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` -o $lib' + fi + fi + link_all_deplibs_CXX=yes + ;; + esac + hardcode_libdir_flag_spec_CXX='${wl}-rpath ${wl}$libdir' + hardcode_libdir_separator_CXX=: + inherit_rpath_CXX=yes + ;; + + linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) + case $cc_basename in + KCC*) + # Kuck and Associates, Inc. (KAI) C++ Compiler + + # KCC will only create a shared library if the output file + # ends with ".so" (or ".sl" for HP-UX), so rename the library + # to its proper name (with version) after linking. + archive_cmds_CXX='tempext=`echo $shared_ext | $SED -e '\''s/\([^()0-9A-Za-z{}]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\${tempext}\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib; mv \$templib $lib' + archive_expsym_cmds_CXX='tempext=`echo $shared_ext | $SED -e '\''s/\([^()0-9A-Za-z{}]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\${tempext}\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib ${wl}-retain-symbols-file,$export_symbols; mv \$templib $lib' + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + # + # There doesn't appear to be a way to prevent this compiler from + # explicitly linking system object files so we need to strip them + # from the output so that they don't get included in the library + # dependencies. + output_verbose_link_cmd='templist=`$CC $CFLAGS -v conftest.$objext -o libconftest$shared_ext 2>&1 | $GREP "ld"`; rm -f libconftest$shared_ext; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"' + + hardcode_libdir_flag_spec_CXX='${wl}-rpath,$libdir' + export_dynamic_flag_spec_CXX='${wl}--export-dynamic' + + # Archives containing C++ object files must be created using + # "CC -Bstatic", where "CC" is the KAI C++ compiler. + old_archive_cmds_CXX='$CC -Bstatic -o $oldlib $oldobjs' + ;; + icpc* | ecpc* ) + # Intel C++ + with_gnu_ld=yes + # version 8.0 and above of icpc choke on multiply defined symbols + # if we add $predep_objects and $postdep_objects, however 7.1 and + # earlier do not add the objects themselves. + case `$CC -V 2>&1` in + *"Version 7."*) + archive_cmds_CXX='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib' + archive_expsym_cmds_CXX='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' + ;; + *) # Version 8.0 or newer + tmp_idyn= + case $host_cpu in + ia64*) tmp_idyn=' -i_dynamic';; + esac + archive_cmds_CXX='$CC -shared'"$tmp_idyn"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + archive_expsym_cmds_CXX='$CC -shared'"$tmp_idyn"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' + ;; + esac + archive_cmds_need_lc_CXX=no + hardcode_libdir_flag_spec_CXX='${wl}-rpath,$libdir' + export_dynamic_flag_spec_CXX='${wl}--export-dynamic' + whole_archive_flag_spec_CXX='${wl}--whole-archive$convenience ${wl}--no-whole-archive' + ;; + pgCC* | pgcpp*) + # Portland Group C++ compiler + case `$CC -V` in + *pgCC\ [1-5].* | *pgcpp\ [1-5].*) + prelink_cmds_CXX='tpldir=Template.dir~ + rm -rf $tpldir~ + $CC --prelink_objects --instantiation_dir $tpldir $objs $libobjs $compile_deplibs~ + compile_command="$compile_command `find $tpldir -name \*.o | sort | $NL2SP`"' + old_archive_cmds_CXX='tpldir=Template.dir~ + rm -rf $tpldir~ + $CC --prelink_objects --instantiation_dir $tpldir $oldobjs$old_deplibs~ + $AR $AR_FLAGS $oldlib$oldobjs$old_deplibs `find $tpldir -name \*.o | sort | $NL2SP`~ + $RANLIB $oldlib' + archive_cmds_CXX='tpldir=Template.dir~ + rm -rf $tpldir~ + $CC --prelink_objects --instantiation_dir $tpldir $predep_objects $libobjs $deplibs $convenience $postdep_objects~ + $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | sort | $NL2SP` $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname -o $lib' + archive_expsym_cmds_CXX='tpldir=Template.dir~ + rm -rf $tpldir~ + $CC --prelink_objects --instantiation_dir $tpldir $predep_objects $libobjs $deplibs $convenience $postdep_objects~ + $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | sort | $NL2SP` $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname ${wl}-retain-symbols-file ${wl}$export_symbols -o $lib' + ;; + *) # Version 6 and above use weak symbols + archive_cmds_CXX='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname -o $lib' + archive_expsym_cmds_CXX='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname ${wl}-retain-symbols-file ${wl}$export_symbols -o $lib' + ;; + esac + + hardcode_libdir_flag_spec_CXX='${wl}--rpath ${wl}$libdir' + export_dynamic_flag_spec_CXX='${wl}--export-dynamic' + whole_archive_flag_spec_CXX='${wl}--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` ${wl}--no-whole-archive' + ;; + cxx*) + # Compaq C++ + archive_cmds_CXX='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib' + archive_expsym_cmds_CXX='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib ${wl}-retain-symbols-file $wl$export_symbols' + + runpath_var=LD_RUN_PATH + hardcode_libdir_flag_spec_CXX='-rpath $libdir' + hardcode_libdir_separator_CXX=: + + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + # + # There doesn't appear to be a way to prevent this compiler from + # explicitly linking system object files so we need to strip them + # from the output so that they don't get included in the library + # dependencies. + output_verbose_link_cmd='templist=`$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP "ld"`; templist=`func_echo_all "$templist" | $SED "s/\(^.*ld.*\)\( .*ld .*$\)/\1/"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "X$list" | $Xsed' + ;; + xl* | mpixl* | bgxl*) + # IBM XL 8.0 on PPC, with GNU ld + hardcode_libdir_flag_spec_CXX='${wl}-rpath ${wl}$libdir' + export_dynamic_flag_spec_CXX='${wl}--export-dynamic' + archive_cmds_CXX='$CC -qmkshrobj $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + if test "x$supports_anon_versioning" = xyes; then + archive_expsym_cmds_CXX='echo "{ global:" > $output_objdir/$libname.ver~ + cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ + echo "local: *; };" >> $output_objdir/$libname.ver~ + $CC -qmkshrobj $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-version-script ${wl}$output_objdir/$libname.ver -o $lib' + fi + ;; + *) + case `$CC -V 2>&1 | sed 5q` in + *Sun\ C*) + # Sun C++ 5.9 + no_undefined_flag_CXX=' -zdefs' + archive_cmds_CXX='$CC -G${allow_undefined_flag} -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + archive_expsym_cmds_CXX='$CC -G${allow_undefined_flag} -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-retain-symbols-file ${wl}$export_symbols' + hardcode_libdir_flag_spec_CXX='-R$libdir' + whole_archive_flag_spec_CXX='${wl}--whole-archive`new_convenience=; for conv in $convenience\"\"; do test -z \"$conv\" || new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` ${wl}--no-whole-archive' + compiler_needs_object_CXX=yes + + # Not sure whether something based on + # $CC $CFLAGS -v conftest.$objext -o libconftest$shared_ext 2>&1 + # would be better. + output_verbose_link_cmd='func_echo_all' + + # Archives containing C++ object files must be created using + # "CC -xar", where "CC" is the Sun C++ compiler. This is + # necessary to make sure instantiated templates are included + # in the archive. + old_archive_cmds_CXX='$CC -xar -o $oldlib $oldobjs' + ;; + esac + ;; + esac + ;; + + lynxos*) + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + + m88k*) + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + + mvs*) + case $cc_basename in + cxx*) + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + *) + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + esac + ;; + + netbsd*) + if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then + archive_cmds_CXX='$LD -Bshareable -o $lib $predep_objects $libobjs $deplibs $postdep_objects $linker_flags' + wlarc= + hardcode_libdir_flag_spec_CXX='-R$libdir' + hardcode_direct_CXX=yes + hardcode_shlibpath_var_CXX=no + fi + # Workaround some broken pre-1.5 toolchains + output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP conftest.$objext | $SED -e "s:-lgcc -lc -lgcc::"' + ;; + + *nto* | *qnx*) + ld_shlibs_CXX=yes + ;; + + openbsd2*) + # C++ shared libraries are fairly broken + ld_shlibs_CXX=no + ;; + + openbsd*) + if test -f /usr/libexec/ld.so; then + hardcode_direct_CXX=yes + hardcode_shlibpath_var_CXX=no + hardcode_direct_absolute_CXX=yes + archive_cmds_CXX='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $lib' + hardcode_libdir_flag_spec_CXX='${wl}-rpath,$libdir' + if test -z "`echo __ELF__ | $CC -E - | grep __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then + archive_expsym_cmds_CXX='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-retain-symbols-file,$export_symbols -o $lib' + export_dynamic_flag_spec_CXX='${wl}-E' + whole_archive_flag_spec_CXX="$wlarc"'--whole-archive$convenience '"$wlarc"'--no-whole-archive' + fi + output_verbose_link_cmd=func_echo_all + else + ld_shlibs_CXX=no + fi + ;; + + osf3* | osf4* | osf5*) + case $cc_basename in + KCC*) + # Kuck and Associates, Inc. (KAI) C++ Compiler + + # KCC will only create a shared library if the output file + # ends with ".so" (or ".sl" for HP-UX), so rename the library + # to its proper name (with version) after linking. + archive_cmds_CXX='tempext=`echo $shared_ext | $SED -e '\''s/\([^()0-9A-Za-z{}]\)/\\\\\1/g'\''`; templib=`echo "$lib" | $SED -e "s/\${tempext}\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib; mv \$templib $lib' + + hardcode_libdir_flag_spec_CXX='${wl}-rpath,$libdir' + hardcode_libdir_separator_CXX=: + + # Archives containing C++ object files must be created using + # the KAI C++ compiler. + case $host in + osf3*) old_archive_cmds_CXX='$CC -Bstatic -o $oldlib $oldobjs' ;; + *) old_archive_cmds_CXX='$CC -o $oldlib $oldobjs' ;; + esac + ;; + RCC*) + # Rational C++ 2.4.1 + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + cxx*) + case $host in + osf3*) + allow_undefined_flag_CXX=' ${wl}-expect_unresolved ${wl}\*' + archive_cmds_CXX='$CC -shared${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $soname `test -n "$verstring" && func_echo_all "${wl}-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib' + hardcode_libdir_flag_spec_CXX='${wl}-rpath ${wl}$libdir' + ;; + *) + allow_undefined_flag_CXX=' -expect_unresolved \*' + archive_cmds_CXX='$CC -shared${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -msym -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib' + archive_expsym_cmds_CXX='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done~ + echo "-hidden">> $lib.exp~ + $CC -shared$allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -msym -soname $soname ${wl}-input ${wl}$lib.exp `test -n "$verstring" && $ECHO "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib~ + $RM $lib.exp' + hardcode_libdir_flag_spec_CXX='-rpath $libdir' + ;; + esac + + hardcode_libdir_separator_CXX=: + + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + # + # There doesn't appear to be a way to prevent this compiler from + # explicitly linking system object files so we need to strip them + # from the output so that they don't get included in the library + # dependencies. + output_verbose_link_cmd='templist=`$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP "ld" | $GREP -v "ld:"`; templist=`func_echo_all "$templist" | $SED "s/\(^.*ld.*\)\( .*ld.*$\)/\1/"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"' + ;; + *) + if test "$GXX" = yes && test "$with_gnu_ld" = no; then + allow_undefined_flag_CXX=' ${wl}-expect_unresolved ${wl}\*' + case $host in + osf3*) + archive_cmds_CXX='$CC -shared -nostdlib ${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' + ;; + *) + archive_cmds_CXX='$CC -shared $pic_flag -nostdlib ${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-msym ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' + ;; + esac + + hardcode_libdir_flag_spec_CXX='${wl}-rpath ${wl}$libdir' + hardcode_libdir_separator_CXX=: + + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"' + + else + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + fi + ;; + esac + ;; + + psos*) + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + + sunos4*) + case $cc_basename in + CC*) + # Sun C++ 4.x + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + lcc*) + # Lucid + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + *) + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + esac + ;; + + solaris*) + case $cc_basename in + CC* | sunCC*) + # Sun C++ 4.2, 5.x and Centerline C++ + archive_cmds_need_lc_CXX=yes + no_undefined_flag_CXX=' -zdefs' + archive_cmds_CXX='$CC -G${allow_undefined_flag} -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + archive_expsym_cmds_CXX='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ + $CC -G${allow_undefined_flag} ${wl}-M ${wl}$lib.exp -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp' + + hardcode_libdir_flag_spec_CXX='-R$libdir' + hardcode_shlibpath_var_CXX=no + case $host_os in + solaris2.[0-5] | solaris2.[0-5].*) ;; + *) + # The compiler driver will combine and reorder linker options, + # but understands `-z linker_flag'. + # Supported since Solaris 2.6 (maybe 2.5.1?) + whole_archive_flag_spec_CXX='-z allextract$convenience -z defaultextract' + ;; + esac + link_all_deplibs_CXX=yes + + output_verbose_link_cmd='func_echo_all' + + # Archives containing C++ object files must be created using + # "CC -xar", where "CC" is the Sun C++ compiler. This is + # necessary to make sure instantiated templates are included + # in the archive. + old_archive_cmds_CXX='$CC -xar -o $oldlib $oldobjs' + ;; + gcx*) + # Green Hills C++ Compiler + archive_cmds_CXX='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-h $wl$soname -o $lib' + + # The C++ compiler must be used to create the archive. + old_archive_cmds_CXX='$CC $LDFLAGS -archive -o $oldlib $oldobjs' + ;; + *) + # GNU C++ compiler with Solaris linker + if test "$GXX" = yes && test "$with_gnu_ld" = no; then + no_undefined_flag_CXX=' ${wl}-z ${wl}defs' + if $CC --version | $GREP -v '^2\.7' > /dev/null; then + archive_cmds_CXX='$CC -shared $pic_flag -nostdlib $LDFLAGS $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-h $wl$soname -o $lib' + archive_expsym_cmds_CXX='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ + $CC -shared $pic_flag -nostdlib ${wl}-M $wl$lib.exp -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp' + + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"' + else + # g++ 2.7 appears to require `-G' NOT `-shared' on this + # platform. + archive_cmds_CXX='$CC -G -nostdlib $LDFLAGS $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-h $wl$soname -o $lib' + archive_expsym_cmds_CXX='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ + $CC -G -nostdlib ${wl}-M $wl$lib.exp -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp' + + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + output_verbose_link_cmd='$CC -G $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"' + fi + + hardcode_libdir_flag_spec_CXX='${wl}-R $wl$libdir' + case $host_os in + solaris2.[0-5] | solaris2.[0-5].*) ;; + *) + whole_archive_flag_spec_CXX='${wl}-z ${wl}allextract$convenience ${wl}-z ${wl}defaultextract' + ;; + esac + fi + ;; + esac + ;; + + sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[01].[10]* | unixware7* | sco3.2v5.0.[024]*) + no_undefined_flag_CXX='${wl}-z,text' + archive_cmds_need_lc_CXX=no + hardcode_shlibpath_var_CXX=no + runpath_var='LD_RUN_PATH' + + case $cc_basename in + CC*) + archive_cmds_CXX='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + archive_expsym_cmds_CXX='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + ;; + *) + archive_cmds_CXX='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + archive_expsym_cmds_CXX='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + ;; + esac + ;; + + sysv5* | sco3.2v5* | sco5v6*) + # Note: We can NOT use -z defs as we might desire, because we do not + # link with -lc, and that would cause any symbols used from libc to + # always be unresolved, which means just about no library would + # ever link correctly. If we're not using GNU ld we use -z text + # though, which does catch some bad symbols but isn't as heavy-handed + # as -z defs. + no_undefined_flag_CXX='${wl}-z,text' + allow_undefined_flag_CXX='${wl}-z,nodefs' + archive_cmds_need_lc_CXX=no + hardcode_shlibpath_var_CXX=no + hardcode_libdir_flag_spec_CXX='${wl}-R,$libdir' + hardcode_libdir_separator_CXX=':' + link_all_deplibs_CXX=yes + export_dynamic_flag_spec_CXX='${wl}-Bexport' + runpath_var='LD_RUN_PATH' + + case $cc_basename in + CC*) + archive_cmds_CXX='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + archive_expsym_cmds_CXX='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + old_archive_cmds_CXX='$CC -Tprelink_objects $oldobjs~ + '"$old_archive_cmds_CXX" + reload_cmds_CXX='$CC -Tprelink_objects $reload_objs~ + '"$reload_cmds_CXX" + ;; + *) + archive_cmds_CXX='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + archive_expsym_cmds_CXX='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + ;; + esac + ;; + + tandem*) + case $cc_basename in + NCC*) + # NonStop-UX NCC 3.20 + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + *) + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + esac + ;; + + vxworks*) + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + + *) + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + esac + + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ld_shlibs_CXX" >&5 +$as_echo "$ld_shlibs_CXX" >&6; } + test "$ld_shlibs_CXX" = no && can_build_shared=no + + GCC_CXX="$GXX" + LD_CXX="$LD" + + ## CAVEAT EMPTOR: + ## There is no encapsulation within the following macros, do not change + ## the running order or otherwise move them around unless you know exactly + ## what you are doing... + # Dependencies to place before and after the object being linked: +predep_objects_CXX= +postdep_objects_CXX= +predeps_CXX= +postdeps_CXX= +compiler_lib_search_path_CXX= + +cat > conftest.$ac_ext <<_LT_EOF +class Foo +{ +public: + Foo (void) { a = 0; } +private: + int a; +}; +_LT_EOF + + +_lt_libdeps_save_CFLAGS=$CFLAGS +case "$CC $CFLAGS " in #( +*\ -flto*\ *) CFLAGS="$CFLAGS -fno-lto" ;; +*\ -fwhopr*\ *) CFLAGS="$CFLAGS -fno-whopr" ;; +*\ -fuse-linker-plugin*\ *) CFLAGS="$CFLAGS -fno-use-linker-plugin" ;; +esac + +if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then + # Parse the compiler output and extract the necessary + # objects, libraries and library flags. + + # Sentinel used to keep track of whether or not we are before + # the conftest object file. + pre_test_object_deps_done=no + + for p in `eval "$output_verbose_link_cmd"`; do + case ${prev}${p} in + + -L* | -R* | -l*) + # Some compilers place space between "-{L,R}" and the path. + # Remove the space. + if test $p = "-L" || + test $p = "-R"; then + prev=$p + continue + fi + + # Expand the sysroot to ease extracting the directories later. + if test -z "$prev"; then + case $p in + -L*) func_stripname_cnf '-L' '' "$p"; prev=-L; p=$func_stripname_result ;; + -R*) func_stripname_cnf '-R' '' "$p"; prev=-R; p=$func_stripname_result ;; + -l*) func_stripname_cnf '-l' '' "$p"; prev=-l; p=$func_stripname_result ;; + esac + fi + case $p in + =*) func_stripname_cnf '=' '' "$p"; p=$lt_sysroot$func_stripname_result ;; + esac + if test "$pre_test_object_deps_done" = no; then + case ${prev} in + -L | -R) + # Internal compiler library paths should come after those + # provided the user. The postdeps already come after the + # user supplied libs so there is no need to process them. + if test -z "$compiler_lib_search_path_CXX"; then + compiler_lib_search_path_CXX="${prev}${p}" + else + compiler_lib_search_path_CXX="${compiler_lib_search_path_CXX} ${prev}${p}" + fi + ;; + # The "-l" case would never come before the object being + # linked, so don't bother handling this case. + esac + else + if test -z "$postdeps_CXX"; then + postdeps_CXX="${prev}${p}" + else + postdeps_CXX="${postdeps_CXX} ${prev}${p}" + fi + fi + prev= + ;; + + *.lto.$objext) ;; # Ignore GCC LTO objects + *.$objext) + # This assumes that the test object file only shows up + # once in the compiler output. + if test "$p" = "conftest.$objext"; then + pre_test_object_deps_done=yes + continue + fi + + if test "$pre_test_object_deps_done" = no; then + if test -z "$predep_objects_CXX"; then + predep_objects_CXX="$p" + else + predep_objects_CXX="$predep_objects_CXX $p" + fi + else + if test -z "$postdep_objects_CXX"; then + postdep_objects_CXX="$p" + else + postdep_objects_CXX="$postdep_objects_CXX $p" + fi + fi + ;; + + *) ;; # Ignore the rest. + + esac + done + + # Clean up. + rm -f a.out a.exe +else + echo "libtool.m4: error: problem compiling CXX test program" +fi + +$RM -f confest.$objext +CFLAGS=$_lt_libdeps_save_CFLAGS + +# PORTME: override above test on systems where it is broken +case $host_os in +interix[3-9]*) + # Interix 3.5 installs completely hosed .la files for C++, so rather than + # hack all around it, let's just trust "g++" to DTRT. + predep_objects_CXX= + postdep_objects_CXX= + postdeps_CXX= + ;; + +linux*) + case `$CC -V 2>&1 | sed 5q` in + *Sun\ C*) + # Sun C++ 5.9 + + # The more standards-conforming stlport4 library is + # incompatible with the Cstd library. Avoid specifying + # it if it's in CXXFLAGS. Ignore libCrun as + # -library=stlport4 depends on it. + case " $CXX $CXXFLAGS " in + *" -library=stlport4 "*) + solaris_use_stlport4=yes + ;; + esac + + if test "$solaris_use_stlport4" != yes; then + postdeps_CXX='-library=Cstd -library=Crun' + fi + ;; + esac + ;; + +solaris*) + case $cc_basename in + CC* | sunCC*) + # The more standards-conforming stlport4 library is + # incompatible with the Cstd library. Avoid specifying + # it if it's in CXXFLAGS. Ignore libCrun as + # -library=stlport4 depends on it. + case " $CXX $CXXFLAGS " in + *" -library=stlport4 "*) + solaris_use_stlport4=yes + ;; + esac + + # Adding this requires a known-good setup of shared libraries for + # Sun compiler versions before 5.6, else PIC objects from an old + # archive will be linked into the output, leading to subtle bugs. + if test "$solaris_use_stlport4" != yes; then + postdeps_CXX='-library=Cstd -library=Crun' + fi + ;; + esac + ;; +esac + + +case " $postdeps_CXX " in +*" -lc "*) archive_cmds_need_lc_CXX=no ;; +esac + compiler_lib_search_dirs_CXX= +if test -n "${compiler_lib_search_path_CXX}"; then + compiler_lib_search_dirs_CXX=`echo " ${compiler_lib_search_path_CXX}" | ${SED} -e 's! -L! !g' -e 's!^ !!'` +fi + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + lt_prog_compiler_wl_CXX= +lt_prog_compiler_pic_CXX= +lt_prog_compiler_static_CXX= + + + # C++ specific cases for pic, static, wl, etc. + if test "$GXX" = yes; then + lt_prog_compiler_wl_CXX='-Wl,' + lt_prog_compiler_static_CXX='-static' + + case $host_os in + aix*) + # All AIX code is PIC. + if test "$host_cpu" = ia64; then + # AIX 5 now supports IA64 processor + lt_prog_compiler_static_CXX='-Bstatic' + fi + ;; + + amigaos*) + case $host_cpu in + powerpc) + # see comment about AmigaOS4 .so support + lt_prog_compiler_pic_CXX='-fPIC' + ;; + m68k) + # FIXME: we need at least 68020 code to build shared libraries, but + # adding the `-m68020' flag to GCC prevents building anything better, + # like `-m68040'. + lt_prog_compiler_pic_CXX='-m68020 -resident32 -malways-restore-a4' + ;; + esac + ;; + + beos* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*) + # PIC is the default for these OSes. + ;; + mingw* | cygwin* | os2* | pw32* | cegcc*) + # This hack is so that the source file can tell whether it is being + # built for inclusion in a dll (and should export symbols for example). + # Although the cygwin gcc ignores -fPIC, still need this for old-style + # (--disable-auto-import) libraries + lt_prog_compiler_pic_CXX='-DDLL_EXPORT' + ;; + darwin* | rhapsody*) + # PIC is the default on this platform + # Common symbols not allowed in MH_DYLIB files + lt_prog_compiler_pic_CXX='-fno-common' + ;; + *djgpp*) + # DJGPP does not support shared libraries at all + lt_prog_compiler_pic_CXX= + ;; + haiku*) + # PIC is the default for Haiku. + # The "-static" flag exists, but is broken. + lt_prog_compiler_static_CXX= + ;; + interix[3-9]*) + # Interix 3.x gcc -fpic/-fPIC options generate broken code. + # Instead, we relocate shared libraries at runtime. + ;; + sysv4*MP*) + if test -d /usr/nec; then + lt_prog_compiler_pic_CXX=-Kconform_pic + fi + ;; + hpux*) + # PIC is the default for 64-bit PA HP-UX, but not for 32-bit + # PA HP-UX. On IA64 HP-UX, PIC is the default but the pic flag + # sets the default TLS model and affects inlining. + case $host_cpu in + hppa*64*) + ;; + *) + lt_prog_compiler_pic_CXX='-fPIC' + ;; + esac + ;; + *qnx* | *nto*) + # QNX uses GNU C++, but need to define -shared option too, otherwise + # it will coredump. + lt_prog_compiler_pic_CXX='-fPIC -shared' + ;; + *) + lt_prog_compiler_pic_CXX='-fPIC' + ;; + esac + else + case $host_os in + aix[4-9]*) + # All AIX code is PIC. + if test "$host_cpu" = ia64; then + # AIX 5 now supports IA64 processor + lt_prog_compiler_static_CXX='-Bstatic' + else + lt_prog_compiler_static_CXX='-bnso -bI:/lib/syscalls.exp' + fi + ;; + chorus*) + case $cc_basename in + cxch68*) + # Green Hills C++ Compiler + # _LT_TAGVAR(lt_prog_compiler_static, CXX)="--no_auto_instantiation -u __main -u __premain -u _abort -r $COOL_DIR/lib/libOrb.a $MVME_DIR/lib/CC/libC.a $MVME_DIR/lib/classix/libcx.s.a" + ;; + esac + ;; + mingw* | cygwin* | os2* | pw32* | cegcc*) + # This hack is so that the source file can tell whether it is being + # built for inclusion in a dll (and should export symbols for example). + lt_prog_compiler_pic_CXX='-DDLL_EXPORT' + ;; + dgux*) + case $cc_basename in + ec++*) + lt_prog_compiler_pic_CXX='-KPIC' + ;; + ghcx*) + # Green Hills C++ Compiler + lt_prog_compiler_pic_CXX='-pic' + ;; + *) + ;; + esac + ;; + freebsd* | dragonfly*) + # FreeBSD uses GNU C++ + ;; + hpux9* | hpux10* | hpux11*) + case $cc_basename in + CC*) + lt_prog_compiler_wl_CXX='-Wl,' + lt_prog_compiler_static_CXX='${wl}-a ${wl}archive' + if test "$host_cpu" != ia64; then + lt_prog_compiler_pic_CXX='+Z' + fi + ;; + aCC*) + lt_prog_compiler_wl_CXX='-Wl,' + lt_prog_compiler_static_CXX='${wl}-a ${wl}archive' + case $host_cpu in + hppa*64*|ia64*) + # +Z the default + ;; + *) + lt_prog_compiler_pic_CXX='+Z' + ;; + esac + ;; + *) + ;; + esac + ;; + interix*) + # This is c89, which is MS Visual C++ (no shared libs) + # Anyone wants to do a port? + ;; + irix5* | irix6* | nonstopux*) + case $cc_basename in + CC*) + lt_prog_compiler_wl_CXX='-Wl,' + lt_prog_compiler_static_CXX='-non_shared' + # CC pic flag -KPIC is the default. + ;; + *) + ;; + esac + ;; + linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) + case $cc_basename in + KCC*) + # KAI C++ Compiler + lt_prog_compiler_wl_CXX='--backend -Wl,' + lt_prog_compiler_pic_CXX='-fPIC' + ;; + ecpc* ) + # old Intel C++ for x86_64 which still supported -KPIC. + lt_prog_compiler_wl_CXX='-Wl,' + lt_prog_compiler_pic_CXX='-KPIC' + lt_prog_compiler_static_CXX='-static' + ;; + icpc* ) + # Intel C++, used to be incompatible with GCC. + # ICC 10 doesn't accept -KPIC any more. + lt_prog_compiler_wl_CXX='-Wl,' + lt_prog_compiler_pic_CXX='-fPIC' + lt_prog_compiler_static_CXX='-static' + ;; + pgCC* | pgcpp*) + # Portland Group C++ compiler + lt_prog_compiler_wl_CXX='-Wl,' + lt_prog_compiler_pic_CXX='-fpic' + lt_prog_compiler_static_CXX='-Bstatic' + ;; + cxx*) + # Compaq C++ + # Make sure the PIC flag is empty. It appears that all Alpha + # Linux and Compaq Tru64 Unix objects are PIC. + lt_prog_compiler_pic_CXX= + lt_prog_compiler_static_CXX='-non_shared' + ;; + xlc* | xlC* | bgxl[cC]* | mpixl[cC]*) + # IBM XL 8.0, 9.0 on PPC and BlueGene + lt_prog_compiler_wl_CXX='-Wl,' + lt_prog_compiler_pic_CXX='-qpic' + lt_prog_compiler_static_CXX='-qstaticlink' + ;; + *) + case `$CC -V 2>&1 | sed 5q` in + *Sun\ C*) + # Sun C++ 5.9 + lt_prog_compiler_pic_CXX='-KPIC' + lt_prog_compiler_static_CXX='-Bstatic' + lt_prog_compiler_wl_CXX='-Qoption ld ' + ;; + esac + ;; + esac + ;; + lynxos*) + ;; + m88k*) + ;; + mvs*) + case $cc_basename in + cxx*) + lt_prog_compiler_pic_CXX='-W c,exportall' + ;; + *) + ;; + esac + ;; + netbsd* | netbsdelf*-gnu) + ;; + *qnx* | *nto*) + # QNX uses GNU C++, but need to define -shared option too, otherwise + # it will coredump. + lt_prog_compiler_pic_CXX='-fPIC -shared' + ;; + osf3* | osf4* | osf5*) + case $cc_basename in + KCC*) + lt_prog_compiler_wl_CXX='--backend -Wl,' + ;; + RCC*) + # Rational C++ 2.4.1 + lt_prog_compiler_pic_CXX='-pic' + ;; + cxx*) + # Digital/Compaq C++ + lt_prog_compiler_wl_CXX='-Wl,' + # Make sure the PIC flag is empty. It appears that all Alpha + # Linux and Compaq Tru64 Unix objects are PIC. + lt_prog_compiler_pic_CXX= + lt_prog_compiler_static_CXX='-non_shared' + ;; + *) + ;; + esac + ;; + psos*) + ;; + solaris*) + case $cc_basename in + CC* | sunCC*) + # Sun C++ 4.2, 5.x and Centerline C++ + lt_prog_compiler_pic_CXX='-KPIC' + lt_prog_compiler_static_CXX='-Bstatic' + lt_prog_compiler_wl_CXX='-Qoption ld ' + ;; + gcx*) + # Green Hills C++ Compiler + lt_prog_compiler_pic_CXX='-PIC' + ;; + *) + ;; + esac + ;; + sunos4*) + case $cc_basename in + CC*) + # Sun C++ 4.x + lt_prog_compiler_pic_CXX='-pic' + lt_prog_compiler_static_CXX='-Bstatic' + ;; + lcc*) + # Lucid + lt_prog_compiler_pic_CXX='-pic' + ;; + *) + ;; + esac + ;; + sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*) + case $cc_basename in + CC*) + lt_prog_compiler_wl_CXX='-Wl,' + lt_prog_compiler_pic_CXX='-KPIC' + lt_prog_compiler_static_CXX='-Bstatic' + ;; + esac + ;; + tandem*) + case $cc_basename in + NCC*) + # NonStop-UX NCC 3.20 + lt_prog_compiler_pic_CXX='-KPIC' + ;; + *) + ;; + esac + ;; + vxworks*) + ;; + *) + lt_prog_compiler_can_build_shared_CXX=no + ;; + esac + fi + +case $host_os in + # For platforms which do not support PIC, -DPIC is meaningless: + *djgpp*) + lt_prog_compiler_pic_CXX= + ;; + *) + lt_prog_compiler_pic_CXX="$lt_prog_compiler_pic_CXX -DPIC" + ;; +esac + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $compiler option to produce PIC" >&5 +$as_echo_n "checking for $compiler option to produce PIC... " >&6; } +if ${lt_cv_prog_compiler_pic_CXX+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_prog_compiler_pic_CXX=$lt_prog_compiler_pic_CXX +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_pic_CXX" >&5 +$as_echo "$lt_cv_prog_compiler_pic_CXX" >&6; } +lt_prog_compiler_pic_CXX=$lt_cv_prog_compiler_pic_CXX + +# +# Check to make sure the PIC flag actually works. +# +if test -n "$lt_prog_compiler_pic_CXX"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler PIC flag $lt_prog_compiler_pic_CXX works" >&5 +$as_echo_n "checking if $compiler PIC flag $lt_prog_compiler_pic_CXX works... " >&6; } +if ${lt_cv_prog_compiler_pic_works_CXX+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_prog_compiler_pic_works_CXX=no + ac_outfile=conftest.$ac_objext + echo "$lt_simple_compile_test_code" > conftest.$ac_ext + lt_compiler_flag="$lt_prog_compiler_pic_CXX -DPIC" + # Insert the option either (1) after the last *FLAGS variable, or + # (2) before a word containing "conftest.", or (3) at the end. + # Note that $ac_compile itself does not contain backslashes and begins + # with a dollar sign (not a hyphen), so the echo should work correctly. + # The option is referenced via a variable to avoid confusing sed. + lt_compile=`echo "$ac_compile" | $SED \ + -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ + -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ + -e 's:$: $lt_compiler_flag:'` + (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5) + (eval "$lt_compile" 2>conftest.err) + ac_status=$? + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + if (exit $ac_status) && test -s "$ac_outfile"; then + # The compiler can only warn and ignore the option if not recognized + # So say no if there are warnings other than the usual output. + $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' >conftest.exp + $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 + if test ! -s conftest.er2 || diff conftest.exp conftest.er2 >/dev/null; then + lt_cv_prog_compiler_pic_works_CXX=yes + fi + fi + $RM conftest* + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_pic_works_CXX" >&5 +$as_echo "$lt_cv_prog_compiler_pic_works_CXX" >&6; } + +if test x"$lt_cv_prog_compiler_pic_works_CXX" = xyes; then + case $lt_prog_compiler_pic_CXX in + "" | " "*) ;; + *) lt_prog_compiler_pic_CXX=" $lt_prog_compiler_pic_CXX" ;; + esac +else + lt_prog_compiler_pic_CXX= + lt_prog_compiler_can_build_shared_CXX=no +fi + +fi + + + + + +# +# Check to make sure the static flag actually works. +# +wl=$lt_prog_compiler_wl_CXX eval lt_tmp_static_flag=\"$lt_prog_compiler_static_CXX\" +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler static flag $lt_tmp_static_flag works" >&5 +$as_echo_n "checking if $compiler static flag $lt_tmp_static_flag works... " >&6; } +if ${lt_cv_prog_compiler_static_works_CXX+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_prog_compiler_static_works_CXX=no + save_LDFLAGS="$LDFLAGS" + LDFLAGS="$LDFLAGS $lt_tmp_static_flag" + echo "$lt_simple_link_test_code" > conftest.$ac_ext + if (eval $ac_link 2>conftest.err) && test -s conftest$ac_exeext; then + # The linker can only warn and ignore the option if not recognized + # So say no if there are warnings + if test -s conftest.err; then + # Append any errors to the config.log. + cat conftest.err 1>&5 + $ECHO "$_lt_linker_boilerplate" | $SED '/^$/d' > conftest.exp + $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 + if diff conftest.exp conftest.er2 >/dev/null; then + lt_cv_prog_compiler_static_works_CXX=yes + fi + else + lt_cv_prog_compiler_static_works_CXX=yes + fi + fi + $RM -r conftest* + LDFLAGS="$save_LDFLAGS" + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_static_works_CXX" >&5 +$as_echo "$lt_cv_prog_compiler_static_works_CXX" >&6; } + +if test x"$lt_cv_prog_compiler_static_works_CXX" = xyes; then + : +else + lt_prog_compiler_static_CXX= +fi + + + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler supports -c -o file.$ac_objext" >&5 +$as_echo_n "checking if $compiler supports -c -o file.$ac_objext... " >&6; } +if ${lt_cv_prog_compiler_c_o_CXX+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_prog_compiler_c_o_CXX=no + $RM -r conftest 2>/dev/null + mkdir conftest + cd conftest + mkdir out + echo "$lt_simple_compile_test_code" > conftest.$ac_ext + + lt_compiler_flag="-o out/conftest2.$ac_objext" + # Insert the option either (1) after the last *FLAGS variable, or + # (2) before a word containing "conftest.", or (3) at the end. + # Note that $ac_compile itself does not contain backslashes and begins + # with a dollar sign (not a hyphen), so the echo should work correctly. + lt_compile=`echo "$ac_compile" | $SED \ + -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ + -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ + -e 's:$: $lt_compiler_flag:'` + (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5) + (eval "$lt_compile" 2>out/conftest.err) + ac_status=$? + cat out/conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + if (exit $ac_status) && test -s out/conftest2.$ac_objext + then + # The compiler can only warn and ignore the option if not recognized + # So say no if there are warnings + $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' > out/conftest.exp + $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2 + if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then + lt_cv_prog_compiler_c_o_CXX=yes + fi + fi + chmod u+w . 2>&5 + $RM conftest* + # SGI C++ compiler will create directory out/ii_files/ for + # template instantiation + test -d out/ii_files && $RM out/ii_files/* && rmdir out/ii_files + $RM out/* && rmdir out + cd .. + $RM -r conftest + $RM conftest* + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_c_o_CXX" >&5 +$as_echo "$lt_cv_prog_compiler_c_o_CXX" >&6; } + + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler supports -c -o file.$ac_objext" >&5 +$as_echo_n "checking if $compiler supports -c -o file.$ac_objext... " >&6; } +if ${lt_cv_prog_compiler_c_o_CXX+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_prog_compiler_c_o_CXX=no + $RM -r conftest 2>/dev/null + mkdir conftest + cd conftest + mkdir out + echo "$lt_simple_compile_test_code" > conftest.$ac_ext + + lt_compiler_flag="-o out/conftest2.$ac_objext" + # Insert the option either (1) after the last *FLAGS variable, or + # (2) before a word containing "conftest.", or (3) at the end. + # Note that $ac_compile itself does not contain backslashes and begins + # with a dollar sign (not a hyphen), so the echo should work correctly. + lt_compile=`echo "$ac_compile" | $SED \ + -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ + -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ + -e 's:$: $lt_compiler_flag:'` + (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5) + (eval "$lt_compile" 2>out/conftest.err) + ac_status=$? + cat out/conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + if (exit $ac_status) && test -s out/conftest2.$ac_objext + then + # The compiler can only warn and ignore the option if not recognized + # So say no if there are warnings + $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' > out/conftest.exp + $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2 + if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then + lt_cv_prog_compiler_c_o_CXX=yes + fi + fi + chmod u+w . 2>&5 + $RM conftest* + # SGI C++ compiler will create directory out/ii_files/ for + # template instantiation + test -d out/ii_files && $RM out/ii_files/* && rmdir out/ii_files + $RM out/* && rmdir out + cd .. + $RM -r conftest + $RM conftest* + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_c_o_CXX" >&5 +$as_echo "$lt_cv_prog_compiler_c_o_CXX" >&6; } + + + + +hard_links="nottested" +if test "$lt_cv_prog_compiler_c_o_CXX" = no && test "$need_locks" != no; then + # do not overwrite the value of need_locks provided by the user + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if we can lock with hard links" >&5 +$as_echo_n "checking if we can lock with hard links... " >&6; } + hard_links=yes + $RM conftest* + ln conftest.a conftest.b 2>/dev/null && hard_links=no + touch conftest.a + ln conftest.a conftest.b 2>&5 || hard_links=no + ln conftest.a conftest.b 2>/dev/null && hard_links=no + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $hard_links" >&5 +$as_echo "$hard_links" >&6; } + if test "$hard_links" = no; then + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: \`$CC' does not support \`-c -o', so \`make -j' may be unsafe" >&5 +$as_echo "$as_me: WARNING: \`$CC' does not support \`-c -o', so \`make -j' may be unsafe" >&2;} + need_locks=warn + fi +else + need_locks=no +fi + + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the $compiler linker ($LD) supports shared libraries" >&5 +$as_echo_n "checking whether the $compiler linker ($LD) supports shared libraries... " >&6; } + + export_symbols_cmds_CXX='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' + exclude_expsyms_CXX='_GLOBAL_OFFSET_TABLE_|_GLOBAL__F[ID]_.*' + case $host_os in + aix[4-9]*) + # If we're using GNU nm, then we don't want the "-C" option. + # -C means demangle to AIX nm, but means don't demangle with GNU nm + # Also, AIX nm treats weak defined symbols like other global defined + # symbols, whereas GNU nm marks them as "W". + if $NM -V 2>&1 | $GREP 'GNU' > /dev/null; then + export_symbols_cmds_CXX='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W")) && (substr(\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols' + else + export_symbols_cmds_CXX='$NM -BCpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B")) && (substr(\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols' + fi + ;; + pw32*) + export_symbols_cmds_CXX="$ltdll_cmds" + ;; + cygwin* | mingw* | cegcc*) + case $cc_basename in + cl*) + exclude_expsyms_CXX='_NULL_IMPORT_DESCRIPTOR|_IMPORT_DESCRIPTOR_.*' + ;; + *) + export_symbols_cmds_CXX='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[BCDGRS][ ]/s/.*[ ]\([^ ]*\)/\1 DATA/;s/^.*[ ]__nm__\([^ ]*\)[ ][^ ]*/\1 DATA/;/^I[ ]/d;/^[AITW][ ]/s/.* //'\'' | sort | uniq > $export_symbols' + exclude_expsyms_CXX='[_]+GLOBAL_OFFSET_TABLE_|[_]+GLOBAL__[FID]_.*|[_]+head_[A-Za-z0-9_]+_dll|[A-Za-z0-9_]+_dll_iname' + ;; + esac + ;; + linux* | k*bsd*-gnu | gnu*) + link_all_deplibs_CXX=no + ;; + *) + export_symbols_cmds_CXX='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' + ;; + esac + +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ld_shlibs_CXX" >&5 +$as_echo "$ld_shlibs_CXX" >&6; } +test "$ld_shlibs_CXX" = no && can_build_shared=no + +with_gnu_ld_CXX=$with_gnu_ld + + + + + + +# +# Do we need to explicitly link libc? +# +case "x$archive_cmds_need_lc_CXX" in +x|xyes) + # Assume -lc should be added + archive_cmds_need_lc_CXX=yes + + if test "$enable_shared" = yes && test "$GCC" = yes; then + case $archive_cmds_CXX in + *'~'*) + # FIXME: we may have to deal with multi-command sequences. + ;; + '$CC '*) + # Test whether the compiler implicitly links with -lc since on some + # systems, -lgcc has to come before -lc. If gcc already passes -lc + # to ld, don't add -lc before -lgcc. + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether -lc should be explicitly linked in" >&5 +$as_echo_n "checking whether -lc should be explicitly linked in... " >&6; } +if ${lt_cv_archive_cmds_need_lc_CXX+:} false; then : + $as_echo_n "(cached) " >&6 +else + $RM conftest* + echo "$lt_simple_compile_test_code" > conftest.$ac_ext + + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } 2>conftest.err; then + soname=conftest + lib=conftest + libobjs=conftest.$ac_objext + deplibs= + wl=$lt_prog_compiler_wl_CXX + pic_flag=$lt_prog_compiler_pic_CXX + compiler_flags=-v + linker_flags=-v + verstring= + output_objdir=. + libname=conftest + lt_save_allow_undefined_flag=$allow_undefined_flag_CXX + allow_undefined_flag_CXX= + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$archive_cmds_CXX 2\>\&1 \| $GREP \" -lc \" \>/dev/null 2\>\&1\""; } >&5 + (eval $archive_cmds_CXX 2\>\&1 \| $GREP \" -lc \" \>/dev/null 2\>\&1) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } + then + lt_cv_archive_cmds_need_lc_CXX=no + else + lt_cv_archive_cmds_need_lc_CXX=yes + fi + allow_undefined_flag_CXX=$lt_save_allow_undefined_flag + else + cat conftest.err 1>&5 + fi + $RM conftest* + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_archive_cmds_need_lc_CXX" >&5 +$as_echo "$lt_cv_archive_cmds_need_lc_CXX" >&6; } + archive_cmds_need_lc_CXX=$lt_cv_archive_cmds_need_lc_CXX + ;; + esac + fi + ;; +esac + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking dynamic linker characteristics" >&5 +$as_echo_n "checking dynamic linker characteristics... " >&6; } + +library_names_spec= +libname_spec='lib$name' +soname_spec= +shrext_cmds=".so" +postinstall_cmds= +postuninstall_cmds= +finish_cmds= +finish_eval= +shlibpath_var= +shlibpath_overrides_runpath=unknown +version_type=none +dynamic_linker="$host_os ld.so" +sys_lib_dlsearch_path_spec="/lib /usr/lib" +need_lib_prefix=unknown +hardcode_into_libs=no + +# when you set need_version to no, make sure it does not cause -set_version +# flags to be left without arguments +need_version=unknown + +case $host_os in +aix3*) + version_type=linux # correct to gnu/linux during the next big refactor + library_names_spec='${libname}${release}${shared_ext}$versuffix $libname.a' + shlibpath_var=LIBPATH + + # AIX 3 has no versioning support, so we append a major version to the name. + soname_spec='${libname}${release}${shared_ext}$major' + ;; + +aix[4-9]*) + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + hardcode_into_libs=yes + if test "$host_cpu" = ia64; then + # AIX 5 supports IA64 + library_names_spec='${libname}${release}${shared_ext}$major ${libname}${release}${shared_ext}$versuffix $libname${shared_ext}' + shlibpath_var=LD_LIBRARY_PATH + else + # With GCC up to 2.95.x, collect2 would create an import file + # for dependence libraries. The import file would start with + # the line `#! .'. This would cause the generated library to + # depend on `.', always an invalid library. This was fixed in + # development snapshots of GCC prior to 3.0. + case $host_os in + aix4 | aix4.[01] | aix4.[01].*) + if { echo '#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 97)' + echo ' yes ' + echo '#endif'; } | ${CC} -E - | $GREP yes > /dev/null; then + : + else + can_build_shared=no + fi + ;; + esac + # AIX (on Power*) has no versioning support, so currently we can not hardcode correct + # soname into executable. Probably we can add versioning support to + # collect2, so additional links can be useful in future. + if test "$aix_use_runtimelinking" = yes; then + # If using run time linking (on AIX 4.2 or later) use lib.so + # instead of lib.a to let people know that these are not + # typical AIX shared libraries. + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + else + # We preserve .a as extension for shared libraries through AIX4.2 + # and later when we are not doing run time linking. + library_names_spec='${libname}${release}.a $libname.a' + soname_spec='${libname}${release}${shared_ext}$major' + fi + shlibpath_var=LIBPATH + fi + ;; + +amigaos*) + case $host_cpu in + powerpc) + # Since July 2007 AmigaOS4 officially supports .so libraries. + # When compiling the executable, add -use-dynld -Lsobjs: to the compileline. + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + ;; + m68k) + library_names_spec='$libname.ixlibrary $libname.a' + # Create ${libname}_ixlibrary.a entries in /sys/libs. + finish_eval='for lib in `ls $libdir/*.ixlibrary 2>/dev/null`; do libname=`func_echo_all "$lib" | $SED '\''s%^.*/\([^/]*\)\.ixlibrary$%\1%'\''`; test $RM /sys/libs/${libname}_ixlibrary.a; $show "cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a"; cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a || exit 1; done' + ;; + esac + ;; + +beos*) + library_names_spec='${libname}${shared_ext}' + dynamic_linker="$host_os ld.so" + shlibpath_var=LIBRARY_PATH + ;; + +bsdi[45]*) + version_type=linux # correct to gnu/linux during the next big refactor + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + finish_cmds='PATH="\$PATH:/sbin" ldconfig $libdir' + shlibpath_var=LD_LIBRARY_PATH + sys_lib_search_path_spec="/shlib /usr/lib /usr/X11/lib /usr/contrib/lib /lib /usr/local/lib" + sys_lib_dlsearch_path_spec="/shlib /usr/lib /usr/local/lib" + # the default ld.so.conf also contains /usr/contrib/lib and + # /usr/X11R6/lib (/usr/X11 is a link to /usr/X11R6), but let us allow + # libtool to hard-code these into programs + ;; + +cygwin* | mingw* | pw32* | cegcc*) + version_type=windows + shrext_cmds=".dll" + need_version=no + need_lib_prefix=no + + case $GCC,$cc_basename in + yes,*) + # gcc + library_names_spec='$libname.dll.a' + # DLL is installed to $(libdir)/../bin by postinstall_cmds + postinstall_cmds='base_file=`basename \${file}`~ + dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\${base_file}'\''i; echo \$dlname'\''`~ + dldir=$destdir/`dirname \$dlpath`~ + test -d \$dldir || mkdir -p \$dldir~ + $install_prog $dir/$dlname \$dldir/$dlname~ + chmod a+x \$dldir/$dlname~ + if test -n '\''$stripme'\'' && test -n '\''$striplib'\''; then + eval '\''$striplib \$dldir/$dlname'\'' || exit \$?; + fi' + postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~ + dlpath=$dir/\$dldll~ + $RM \$dlpath' + shlibpath_overrides_runpath=yes + + case $host_os in + cygwin*) + # Cygwin DLLs use 'cyg' prefix rather than 'lib' + soname_spec='`echo ${libname} | sed -e 's/^lib/cyg/'``echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext}' + + ;; + mingw* | cegcc*) + # MinGW DLLs use traditional 'lib' prefix + soname_spec='${libname}`echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext}' + ;; + pw32*) + # pw32 DLLs use 'pw' prefix rather than 'lib' + library_names_spec='`echo ${libname} | sed -e 's/^lib/pw/'``echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext}' + ;; + esac + dynamic_linker='Win32 ld.exe' + ;; + + *,cl*) + # Native MSVC + libname_spec='$name' + soname_spec='${libname}`echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext}' + library_names_spec='${libname}.dll.lib' + + case $build_os in + mingw*) + sys_lib_search_path_spec= + lt_save_ifs=$IFS + IFS=';' + for lt_path in $LIB + do + IFS=$lt_save_ifs + # Let DOS variable expansion print the short 8.3 style file name. + lt_path=`cd "$lt_path" 2>/dev/null && cmd //C "for %i in (".") do @echo %~si"` + sys_lib_search_path_spec="$sys_lib_search_path_spec $lt_path" + done + IFS=$lt_save_ifs + # Convert to MSYS style. + sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | sed -e 's|\\\\|/|g' -e 's| \\([a-zA-Z]\\):| /\\1|g' -e 's|^ ||'` + ;; + cygwin*) + # Convert to unix form, then to dos form, then back to unix form + # but this time dos style (no spaces!) so that the unix form looks + # like /cygdrive/c/PROGRA~1:/cygdr... + sys_lib_search_path_spec=`cygpath --path --unix "$LIB"` + sys_lib_search_path_spec=`cygpath --path --dos "$sys_lib_search_path_spec" 2>/dev/null` + sys_lib_search_path_spec=`cygpath --path --unix "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"` + ;; + *) + sys_lib_search_path_spec="$LIB" + if $ECHO "$sys_lib_search_path_spec" | $GREP ';[c-zC-Z]:/' >/dev/null; then + # It is most probably a Windows format PATH. + sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e 's/;/ /g'` + else + sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"` + fi + # FIXME: find the short name or the path components, as spaces are + # common. (e.g. "Program Files" -> "PROGRA~1") + ;; + esac + + # DLL is installed to $(libdir)/../bin by postinstall_cmds + postinstall_cmds='base_file=`basename \${file}`~ + dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\${base_file}'\''i; echo \$dlname'\''`~ + dldir=$destdir/`dirname \$dlpath`~ + test -d \$dldir || mkdir -p \$dldir~ + $install_prog $dir/$dlname \$dldir/$dlname' + postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~ + dlpath=$dir/\$dldll~ + $RM \$dlpath' + shlibpath_overrides_runpath=yes + dynamic_linker='Win32 link.exe' + ;; + + *) + # Assume MSVC wrapper + library_names_spec='${libname}`echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext} $libname.lib' + dynamic_linker='Win32 ld.exe' + ;; + esac + # FIXME: first we should search . and the directory the executable is in + shlibpath_var=PATH + ;; + +darwin* | rhapsody*) + dynamic_linker="$host_os dyld" + version_type=darwin + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${major}$shared_ext ${libname}$shared_ext' + soname_spec='${libname}${release}${major}$shared_ext' + shlibpath_overrides_runpath=yes + shlibpath_var=DYLD_LIBRARY_PATH + shrext_cmds='`test .$module = .yes && echo .so || echo .dylib`' + + sys_lib_dlsearch_path_spec='/usr/local/lib /lib /usr/lib' + ;; + +dgux*) + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname$shared_ext' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + ;; + +freebsd* | dragonfly*) + # DragonFly does not have aout. When/if they implement a new + # versioning mechanism, adjust this. + if test -x /usr/bin/objformat; then + objformat=`/usr/bin/objformat` + else + case $host_os in + freebsd[23].*) objformat=aout ;; + *) objformat=elf ;; + esac + fi + version_type=freebsd-$objformat + case $version_type in + freebsd-elf*) + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext} $libname${shared_ext}' + need_version=no + need_lib_prefix=no + ;; + freebsd-*) + library_names_spec='${libname}${release}${shared_ext}$versuffix $libname${shared_ext}$versuffix' + need_version=yes + ;; + esac + shlibpath_var=LD_LIBRARY_PATH + case $host_os in + freebsd2.*) + shlibpath_overrides_runpath=yes + ;; + freebsd3.[01]* | freebsdelf3.[01]*) + shlibpath_overrides_runpath=yes + hardcode_into_libs=yes + ;; + freebsd3.[2-9]* | freebsdelf3.[2-9]* | \ + freebsd4.[0-5] | freebsdelf4.[0-5] | freebsd4.1.1 | freebsdelf4.1.1) + shlibpath_overrides_runpath=no + hardcode_into_libs=yes + ;; + *) # from 4.6 on, and DragonFly + shlibpath_overrides_runpath=yes + hardcode_into_libs=yes + ;; + esac + ;; + +haiku*) + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + dynamic_linker="$host_os runtime_loader" + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}${major} ${libname}${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LIBRARY_PATH + shlibpath_overrides_runpath=yes + sys_lib_dlsearch_path_spec='/boot/home/config/lib /boot/common/lib /boot/system/lib' + hardcode_into_libs=yes + ;; + +hpux9* | hpux10* | hpux11*) + # Give a soname corresponding to the major version so that dld.sl refuses to + # link against other versions. + version_type=sunos + need_lib_prefix=no + need_version=no + case $host_cpu in + ia64*) + shrext_cmds='.so' + hardcode_into_libs=yes + dynamic_linker="$host_os dld.so" + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes # Unless +noenvvar is specified. + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + if test "X$HPUX_IA64_MODE" = X32; then + sys_lib_search_path_spec="/usr/lib/hpux32 /usr/local/lib/hpux32 /usr/local/lib" + else + sys_lib_search_path_spec="/usr/lib/hpux64 /usr/local/lib/hpux64" + fi + sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec + ;; + hppa*64*) + shrext_cmds='.sl' + hardcode_into_libs=yes + dynamic_linker="$host_os dld.sl" + shlibpath_var=LD_LIBRARY_PATH # How should we handle SHLIB_PATH + shlibpath_overrides_runpath=yes # Unless +noenvvar is specified. + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + sys_lib_search_path_spec="/usr/lib/pa20_64 /usr/ccs/lib/pa20_64" + sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec + ;; + *) + shrext_cmds='.sl' + dynamic_linker="$host_os dld.sl" + shlibpath_var=SHLIB_PATH + shlibpath_overrides_runpath=no # +s is required to enable SHLIB_PATH + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + ;; + esac + # HP-UX runs *really* slowly unless shared libraries are mode 555, ... + postinstall_cmds='chmod 555 $lib' + # or fails outright, so override atomically: + install_override_mode=555 + ;; + +interix[3-9]*) + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + dynamic_linker='Interix 3.x ld.so.1 (PE, like ELF)' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=no + hardcode_into_libs=yes + ;; + +irix5* | irix6* | nonstopux*) + case $host_os in + nonstopux*) version_type=nonstopux ;; + *) + if test "$lt_cv_prog_gnu_ld" = yes; then + version_type=linux # correct to gnu/linux during the next big refactor + else + version_type=irix + fi ;; + esac + need_lib_prefix=no + need_version=no + soname_spec='${libname}${release}${shared_ext}$major' + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${release}${shared_ext} $libname${shared_ext}' + case $host_os in + irix5* | nonstopux*) + libsuff= shlibsuff= + ;; + *) + case $LD in # libtool.m4 will add one of these switches to LD + *-32|*"-32 "|*-melf32bsmip|*"-melf32bsmip ") + libsuff= shlibsuff= libmagic=32-bit;; + *-n32|*"-n32 "|*-melf32bmipn32|*"-melf32bmipn32 ") + libsuff=32 shlibsuff=N32 libmagic=N32;; + *-64|*"-64 "|*-melf64bmip|*"-melf64bmip ") + libsuff=64 shlibsuff=64 libmagic=64-bit;; + *) libsuff= shlibsuff= libmagic=never-match;; + esac + ;; + esac + shlibpath_var=LD_LIBRARY${shlibsuff}_PATH + shlibpath_overrides_runpath=no + sys_lib_search_path_spec="/usr/lib${libsuff} /lib${libsuff} /usr/local/lib${libsuff}" + sys_lib_dlsearch_path_spec="/usr/lib${libsuff} /lib${libsuff}" + hardcode_into_libs=yes + ;; + +# No shared lib support for Linux oldld, aout, or coff. +linux*oldld* | linux*aout* | linux*coff*) + dynamic_linker=no + ;; + +# This must be glibc/ELF. +linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + finish_cmds='PATH="\$PATH:/sbin" ldconfig -n $libdir' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=no + + # Some binutils ld are patched to set DT_RUNPATH + if ${lt_cv_shlibpath_overrides_runpath+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_shlibpath_overrides_runpath=no + save_LDFLAGS=$LDFLAGS + save_libdir=$libdir + eval "libdir=/foo; wl=\"$lt_prog_compiler_wl_CXX\"; \ + LDFLAGS=\"\$LDFLAGS $hardcode_libdir_flag_spec_CXX\"" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_link "$LINENO"; then : + if ($OBJDUMP -p conftest$ac_exeext) 2>/dev/null | grep "RUNPATH.*$libdir" >/dev/null; then : + lt_cv_shlibpath_overrides_runpath=yes +fi +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + LDFLAGS=$save_LDFLAGS + libdir=$save_libdir + +fi + + shlibpath_overrides_runpath=$lt_cv_shlibpath_overrides_runpath + + # This implies no fast_install, which is unacceptable. + # Some rework will be needed to allow for fast_install + # before this can be enabled. + hardcode_into_libs=yes + + # Append ld.so.conf contents to the search path + if test -f /etc/ld.so.conf; then + lt_ld_extra=`awk '/^include / { system(sprintf("cd /etc; cat %s 2>/dev/null", \$2)); skip = 1; } { if (!skip) print \$0; skip = 0; }' < /etc/ld.so.conf | $SED -e 's/#.*//;/^[ ]*hwcap[ ]/d;s/[:, ]/ /g;s/=[^=]*$//;s/=[^= ]* / /g;s/"//g;/^$/d' | tr '\n' ' '` + sys_lib_dlsearch_path_spec="/lib /usr/lib $lt_ld_extra" + fi + + # We used to test for /lib/ld.so.1 and disable shared libraries on + # powerpc, because MkLinux only supported shared libraries with the + # GNU dynamic linker. Since this was broken with cross compilers, + # most powerpc-linux boxes support dynamic linking these days and + # people can always --disable-shared, the test was removed, and we + # assume the GNU/Linux dynamic linker is in use. + dynamic_linker='GNU/Linux ld.so' + ;; + +netbsdelf*-gnu) + version_type=linux + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=no + hardcode_into_libs=yes + dynamic_linker='NetBSD ld.elf_so' + ;; + +netbsd*) + version_type=sunos + need_lib_prefix=no + need_version=no + if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix' + finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' + dynamic_linker='NetBSD (a.out) ld.so' + else + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + dynamic_linker='NetBSD ld.elf_so' + fi + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + hardcode_into_libs=yes + ;; + +newsos6) + version_type=linux # correct to gnu/linux during the next big refactor + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + ;; + +*nto* | *qnx*) + version_type=qnx + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=no + hardcode_into_libs=yes + dynamic_linker='ldqnx.so' + ;; + +openbsd*) + version_type=sunos + sys_lib_dlsearch_path_spec="/usr/lib" + need_lib_prefix=no + # Some older versions of OpenBSD (3.3 at least) *do* need versioned libs. + case $host_os in + openbsd3.3 | openbsd3.3.*) need_version=yes ;; + *) need_version=no ;; + esac + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix' + finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' + shlibpath_var=LD_LIBRARY_PATH + if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then + case $host_os in + openbsd2.[89] | openbsd2.[89].*) + shlibpath_overrides_runpath=no + ;; + *) + shlibpath_overrides_runpath=yes + ;; + esac + else + shlibpath_overrides_runpath=yes + fi + ;; + +os2*) + libname_spec='$name' + shrext_cmds=".dll" + need_lib_prefix=no + library_names_spec='$libname${shared_ext} $libname.a' + dynamic_linker='OS/2 ld.exe' + shlibpath_var=LIBPATH + ;; + +osf3* | osf4* | osf5*) + version_type=osf + need_lib_prefix=no + need_version=no + soname_spec='${libname}${release}${shared_ext}$major' + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + shlibpath_var=LD_LIBRARY_PATH + sys_lib_search_path_spec="/usr/shlib /usr/ccs/lib /usr/lib/cmplrs/cc /usr/lib /usr/local/lib /var/shlib" + sys_lib_dlsearch_path_spec="$sys_lib_search_path_spec" + ;; + +rdos*) + dynamic_linker=no + ;; + +solaris*) + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + hardcode_into_libs=yes + # ldd complains unless libraries are executable + postinstall_cmds='chmod +x $lib' + ;; + +sunos4*) + version_type=sunos + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix' + finish_cmds='PATH="\$PATH:/usr/etc" ldconfig $libdir' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + if test "$with_gnu_ld" = yes; then + need_lib_prefix=no + fi + need_version=yes + ;; + +sysv4 | sysv4.3*) + version_type=linux # correct to gnu/linux during the next big refactor + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + case $host_vendor in + sni) + shlibpath_overrides_runpath=no + need_lib_prefix=no + runpath_var=LD_RUN_PATH + ;; + siemens) + need_lib_prefix=no + ;; + motorola) + need_lib_prefix=no + need_version=no + shlibpath_overrides_runpath=no + sys_lib_search_path_spec='/lib /usr/lib /usr/ccs/lib' + ;; + esac + ;; + +sysv4*MP*) + if test -d /usr/nec ;then + version_type=linux # correct to gnu/linux during the next big refactor + library_names_spec='$libname${shared_ext}.$versuffix $libname${shared_ext}.$major $libname${shared_ext}' + soname_spec='$libname${shared_ext}.$major' + shlibpath_var=LD_LIBRARY_PATH + fi + ;; + +sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*) + version_type=freebsd-elf + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext} $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + hardcode_into_libs=yes + if test "$with_gnu_ld" = yes; then + sys_lib_search_path_spec='/usr/local/lib /usr/gnu/lib /usr/ccs/lib /usr/lib /lib' + else + sys_lib_search_path_spec='/usr/ccs/lib /usr/lib' + case $host_os in + sco3.2v5*) + sys_lib_search_path_spec="$sys_lib_search_path_spec /lib" + ;; + esac + fi + sys_lib_dlsearch_path_spec='/usr/lib' + ;; + +tpf*) + # TPF is a cross-target only. Preferred cross-host = GNU/Linux. + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=no + hardcode_into_libs=yes + ;; + +uts4*) + version_type=linux # correct to gnu/linux during the next big refactor + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + ;; + +*) + dynamic_linker=no + ;; +esac +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $dynamic_linker" >&5 +$as_echo "$dynamic_linker" >&6; } +test "$dynamic_linker" = no && can_build_shared=no + +variables_saved_for_relink="PATH $shlibpath_var $runpath_var" +if test "$GCC" = yes; then + variables_saved_for_relink="$variables_saved_for_relink GCC_EXEC_PREFIX COMPILER_PATH LIBRARY_PATH" +fi + +if test "${lt_cv_sys_lib_search_path_spec+set}" = set; then + sys_lib_search_path_spec="$lt_cv_sys_lib_search_path_spec" +fi +if test "${lt_cv_sys_lib_dlsearch_path_spec+set}" = set; then + sys_lib_dlsearch_path_spec="$lt_cv_sys_lib_dlsearch_path_spec" +fi + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to hardcode library paths into programs" >&5 +$as_echo_n "checking how to hardcode library paths into programs... " >&6; } +hardcode_action_CXX= +if test -n "$hardcode_libdir_flag_spec_CXX" || + test -n "$runpath_var_CXX" || + test "X$hardcode_automatic_CXX" = "Xyes" ; then + + # We can hardcode non-existent directories. + if test "$hardcode_direct_CXX" != no && + # If the only mechanism to avoid hardcoding is shlibpath_var, we + # have to relink, otherwise we might link with an installed library + # when we should be linking with a yet-to-be-installed one + ## test "$_LT_TAGVAR(hardcode_shlibpath_var, CXX)" != no && + test "$hardcode_minus_L_CXX" != no; then + # Linking always hardcodes the temporary library directory. + hardcode_action_CXX=relink + else + # We can link without hardcoding, and we can hardcode nonexisting dirs. + hardcode_action_CXX=immediate + fi +else + # We cannot hardcode anything, or else we can only hardcode existing + # directories. + hardcode_action_CXX=unsupported +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $hardcode_action_CXX" >&5 +$as_echo "$hardcode_action_CXX" >&6; } + +if test "$hardcode_action_CXX" = relink || + test "$inherit_rpath_CXX" = yes; then + # Fast installation is not supported + enable_fast_install=no +elif test "$shlibpath_overrides_runpath" = yes || + test "$enable_shared" = no; then + # Fast installation is not necessary + enable_fast_install=needless +fi + + + + + + + + fi # test -n "$compiler" + + CC=$lt_save_CC + CFLAGS=$lt_save_CFLAGS + LDCXX=$LD + LD=$lt_save_LD + GCC=$lt_save_GCC + with_gnu_ld=$lt_save_with_gnu_ld + lt_cv_path_LDCXX=$lt_cv_path_LD + lt_cv_path_LD=$lt_save_path_LD + lt_cv_prog_gnu_ldcxx=$lt_cv_prog_gnu_ld + lt_cv_prog_gnu_ld=$lt_save_with_gnu_ld +fi # test "$_lt_caught_CXX_error" != yes + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + +depcc="$CXX" am_compiler_list= + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking dependency style of $depcc" >&5 +$as_echo_n "checking dependency style of $depcc... " >&6; } +if ${am_cv_CXX_dependencies_compiler_type+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then + # We make a subdir and do the tests there. Otherwise we can end up + # making bogus files that we don't know about and never remove. For + # instance it was reported that on HP-UX the gcc test will end up + # making a dummy file named 'D' -- because '-MD' means "put the output + # in D". + rm -rf conftest.dir + mkdir conftest.dir + # Copy depcomp to subdir because otherwise we won't find it if we're + # using a relative directory. + cp "$am_depcomp" conftest.dir + cd conftest.dir + # We will build objects and dependencies in a subdirectory because + # it helps to detect inapplicable dependency modes. For instance + # both Tru64's cc and ICC support -MD to output dependencies as a + # side effect of compilation, but ICC will put the dependencies in + # the current directory while Tru64 will put them in the object + # directory. + mkdir sub + + am_cv_CXX_dependencies_compiler_type=none + if test "$am_compiler_list" = ""; then + am_compiler_list=`sed -n 's/^#*\([a-zA-Z0-9]*\))$/\1/p' < ./depcomp` + fi + am__universal=false + case " $depcc " in #( + *\ -arch\ *\ -arch\ *) am__universal=true ;; + esac + + for depmode in $am_compiler_list; do + # Setup a source with many dependencies, because some compilers + # like to wrap large dependency lists on column 80 (with \), and + # we should not choose a depcomp mode which is confused by this. + # + # We need to recreate these files for each test, as the compiler may + # overwrite some of them when testing with obscure command lines. + # This happens at least with the AIX C compiler. + : > sub/conftest.c + for i in 1 2 3 4 5 6; do + echo '#include "conftst'$i'.h"' >> sub/conftest.c + # Using ": > sub/conftst$i.h" creates only sub/conftst1.h with + # Solaris 10 /bin/sh. + echo '/* dummy */' > sub/conftst$i.h + done + echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf + + # We check with '-c' and '-o' for the sake of the "dashmstdout" + # mode. It turns out that the SunPro C++ compiler does not properly + # handle '-M -o', and we need to detect this. Also, some Intel + # versions had trouble with output in subdirs. + am__obj=sub/conftest.${OBJEXT-o} + am__minus_obj="-o $am__obj" + case $depmode in + gcc) + # This depmode causes a compiler race in universal mode. + test "$am__universal" = false || continue + ;; + nosideeffect) + # After this tag, mechanisms are not by side-effect, so they'll + # only be used when explicitly requested. + if test "x$enable_dependency_tracking" = xyes; then + continue + else + break + fi + ;; + msvc7 | msvc7msys | msvisualcpp | msvcmsys) + # This compiler won't grok '-c -o', but also, the minuso test has + # not run yet. These depmodes are late enough in the game, and + # so weak that their functioning should not be impacted. + am__obj=conftest.${OBJEXT-o} + am__minus_obj= + ;; + none) break ;; + esac + if depmode=$depmode \ + source=sub/conftest.c object=$am__obj \ + depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \ + $SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \ + >/dev/null 2>conftest.err && + grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 && + grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 && + grep $am__obj sub/conftest.Po > /dev/null 2>&1 && + ${MAKE-make} -s -f confmf > /dev/null 2>&1; then + # icc doesn't choke on unknown options, it will just issue warnings + # or remarks (even with -Werror). So we grep stderr for any message + # that says an option was ignored or not supported. + # When given -MP, icc 7.0 and 7.1 complain thusly: + # icc: Command line warning: ignoring option '-M'; no argument required + # The diagnosis changed in icc 8.0: + # icc: Command line remark: option '-MP' not supported + if (grep 'ignoring option' conftest.err || + grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else + am_cv_CXX_dependencies_compiler_type=$depmode + break + fi + fi + done + + cd .. + rm -rf conftest.dir +else + am_cv_CXX_dependencies_compiler_type=none +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_CXX_dependencies_compiler_type" >&5 +$as_echo "$am_cv_CXX_dependencies_compiler_type" >&6; } +CXXDEPMODE=depmode=$am_cv_CXX_dependencies_compiler_type + + if + test "x$enable_dependency_tracking" != xno \ + && test "$am_cv_CXX_dependencies_compiler_type" = gcc3; then + am__fastdepCXX_TRUE= + am__fastdepCXX_FALSE='#' +else + am__fastdepCXX_TRUE='#' + am__fastdepCXX_FALSE= +fi + + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to run the C preprocessor" >&5 +$as_echo_n "checking how to run the C preprocessor... " >&6; } +# On Suns, sometimes $CPP names a directory. +if test -n "$CPP" && test -d "$CPP"; then + CPP= +fi +if test -z "$CPP"; then + if ${ac_cv_prog_CPP+:} false; then : + $as_echo_n "(cached) " >&6 +else + # Double quotes because CPP needs to be expanded + for CPP in "$CC -E" "$CC -E -traditional-cpp" "/lib/cpp" + do + ac_preproc_ok=false +for ac_c_preproc_warn_flag in '' yes +do + # Use a header file that comes with gcc, so configuring glibc + # with a fresh cross-compiler works. + # Prefer to if __STDC__ is defined, since + # exists even on freestanding compilers. + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. "Syntax error" is here to catch this case. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#ifdef __STDC__ +# include +#else +# include +#endif + Syntax error +_ACEOF +if ac_fn_c_try_cpp "$LINENO"; then : + +else + # Broken: fails on valid input. +continue +fi +rm -f conftest.err conftest.i conftest.$ac_ext + + # OK, works on sane cases. Now check whether nonexistent headers + # can be detected and how. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +_ACEOF +if ac_fn_c_try_cpp "$LINENO"; then : + # Broken: success on invalid input. +continue +else + # Passes both tests. +ac_preproc_ok=: +break +fi +rm -f conftest.err conftest.i conftest.$ac_ext + +done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f conftest.i conftest.err conftest.$ac_ext +if $ac_preproc_ok; then : + break +fi + + done + ac_cv_prog_CPP=$CPP + +fi + CPP=$ac_cv_prog_CPP +else + ac_cv_prog_CPP=$CPP +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $CPP" >&5 +$as_echo "$CPP" >&6; } +ac_preproc_ok=false +for ac_c_preproc_warn_flag in '' yes +do + # Use a header file that comes with gcc, so configuring glibc + # with a fresh cross-compiler works. + # Prefer to if __STDC__ is defined, since + # exists even on freestanding compilers. + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. "Syntax error" is here to catch this case. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#ifdef __STDC__ +# include +#else +# include +#endif + Syntax error +_ACEOF +if ac_fn_c_try_cpp "$LINENO"; then : + +else + # Broken: fails on valid input. +continue +fi +rm -f conftest.err conftest.i conftest.$ac_ext + + # OK, works on sane cases. Now check whether nonexistent headers + # can be detected and how. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +_ACEOF +if ac_fn_c_try_cpp "$LINENO"; then : + # Broken: success on invalid input. +continue +else + # Passes both tests. +ac_preproc_ok=: +break +fi +rm -f conftest.err conftest.i conftest.$ac_ext + +done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f conftest.i conftest.err conftest.$ac_ext +if $ac_preproc_ok; then : + +else + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "C preprocessor \"$CPP\" fails sanity check +See \`config.log' for more details" "$LINENO" 5; } +fi + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ln -s works" >&5 +$as_echo_n "checking whether ln -s works... " >&6; } +LN_S=$as_ln_s +if test "$LN_S" = "ln -s"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no, using $LN_S" >&5 +$as_echo "no, using $LN_S" >&6; } +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ${MAKE-make} sets \$(MAKE)" >&5 +$as_echo_n "checking whether ${MAKE-make} sets \$(MAKE)... " >&6; } +set x ${MAKE-make} +ac_make=`$as_echo "$2" | sed 's/+/p/g; s/[^a-zA-Z0-9_]/_/g'` +if eval \${ac_cv_prog_make_${ac_make}_set+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat >conftest.make <<\_ACEOF +SHELL = /bin/sh +all: + @echo '@@@%%%=$(MAKE)=@@@%%%' +_ACEOF +# GNU make sometimes prints "make[1]: Entering ...", which would confuse us. +case `${MAKE-make} -f conftest.make 2>/dev/null` in + *@@@%%%=?*=@@@%%%*) + eval ac_cv_prog_make_${ac_make}_set=yes;; + *) + eval ac_cv_prog_make_${ac_make}_set=no;; +esac +rm -f conftest.make +fi +if eval test \$ac_cv_prog_make_${ac_make}_set = yes; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + SET_MAKE= +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + SET_MAKE="MAKE=${MAKE-make}" +fi + + + + + + + + + + +if test "x$ac_cv_env_PKG_CONFIG_set" != "xset"; then + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}pkg-config", so it can be a program name with args. +set dummy ${ac_tool_prefix}pkg-config; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_path_PKG_CONFIG+:} false; then : + $as_echo_n "(cached) " >&6 +else + case $PKG_CONFIG in + [\\/]* | ?:[\\/]*) + ac_cv_path_PKG_CONFIG="$PKG_CONFIG" # Let the user override the test with a path. + ;; + *) + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_path_PKG_CONFIG="$as_dir/$ac_word$ac_exec_ext" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + + ;; +esac +fi +PKG_CONFIG=$ac_cv_path_PKG_CONFIG +if test -n "$PKG_CONFIG"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PKG_CONFIG" >&5 +$as_echo "$PKG_CONFIG" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_path_PKG_CONFIG"; then + ac_pt_PKG_CONFIG=$PKG_CONFIG + # Extract the first word of "pkg-config", so it can be a program name with args. +set dummy pkg-config; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_path_ac_pt_PKG_CONFIG+:} false; then : + $as_echo_n "(cached) " >&6 +else + case $ac_pt_PKG_CONFIG in + [\\/]* | ?:[\\/]*) + ac_cv_path_ac_pt_PKG_CONFIG="$ac_pt_PKG_CONFIG" # Let the user override the test with a path. + ;; + *) + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_path_ac_pt_PKG_CONFIG="$as_dir/$ac_word$ac_exec_ext" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + + ;; +esac +fi +ac_pt_PKG_CONFIG=$ac_cv_path_ac_pt_PKG_CONFIG +if test -n "$ac_pt_PKG_CONFIG"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_pt_PKG_CONFIG" >&5 +$as_echo "$ac_pt_PKG_CONFIG" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_pt_PKG_CONFIG" = x; then + PKG_CONFIG="" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + PKG_CONFIG=$ac_pt_PKG_CONFIG + fi +else + PKG_CONFIG="$ac_cv_path_PKG_CONFIG" +fi + +fi +if test -n "$PKG_CONFIG"; then + _pkg_min_version=0.20 + { $as_echo "$as_me:${as_lineno-$LINENO}: checking pkg-config is at least version $_pkg_min_version" >&5 +$as_echo_n "checking pkg-config is at least version $_pkg_min_version... " >&6; } + if $PKG_CONFIG --atleast-pkgconfig-version $_pkg_min_version; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + PKG_CONFIG="" + fi +fi + +if test "x$request_python_bindings" != "xno"; then + + + + + + + if test -n "$PYTHON"; then + # If the user set $PYTHON, use it and don't search something else. + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $PYTHON version is >= 2.7" >&5 +$as_echo_n "checking whether $PYTHON version is >= 2.7... " >&6; } + prog="import sys +# split strings by '.' and convert to numeric. Append some zeros +# because we need at least 4 digits for the hex conversion. +# map returns an iterator in Python 3.0 and a list in 2.x +minver = list(map(int, '2.7'.split('.'))) + [0, 0, 0] +minverhex = 0 +# xrange is not present in Python 3.0 and range returns an iterator +for i in list(range(0, 4)): minverhex = (minverhex << 8) + minver[i] +sys.exit(sys.hexversion < minverhex)" + if { echo "$as_me:$LINENO: $PYTHON -c "$prog"" >&5 + ($PYTHON -c "$prog") >&5 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + as_fn_error $? "Python interpreter is too old" "$LINENO" 5 +fi + am_display_PYTHON=$PYTHON + else + # Otherwise, try each interpreter until we find one that satisfies + # VERSION. + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for a Python interpreter with version >= 2.7" >&5 +$as_echo_n "checking for a Python interpreter with version >= 2.7... " >&6; } +if ${am_cv_pathless_PYTHON+:} false; then : + $as_echo_n "(cached) " >&6 +else + + for am_cv_pathless_PYTHON in python python2 python3 python3.3 python3.2 python3.1 python3.0 python2.7 python2.6 python2.5 python2.4 python2.3 python2.2 python2.1 python2.0 none; do + test "$am_cv_pathless_PYTHON" = none && break + prog="import sys +# split strings by '.' and convert to numeric. Append some zeros +# because we need at least 4 digits for the hex conversion. +# map returns an iterator in Python 3.0 and a list in 2.x +minver = list(map(int, '2.7'.split('.'))) + [0, 0, 0] +minverhex = 0 +# xrange is not present in Python 3.0 and range returns an iterator +for i in list(range(0, 4)): minverhex = (minverhex << 8) + minver[i] +sys.exit(sys.hexversion < minverhex)" + if { echo "$as_me:$LINENO: $am_cv_pathless_PYTHON -c "$prog"" >&5 + ($am_cv_pathless_PYTHON -c "$prog") >&5 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; then : + break +fi + done +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_pathless_PYTHON" >&5 +$as_echo "$am_cv_pathless_PYTHON" >&6; } + # Set $PYTHON to the absolute path of $am_cv_pathless_PYTHON. + if test "$am_cv_pathless_PYTHON" = none; then + PYTHON=: + else + # Extract the first word of "$am_cv_pathless_PYTHON", so it can be a program name with args. +set dummy $am_cv_pathless_PYTHON; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_path_PYTHON+:} false; then : + $as_echo_n "(cached) " >&6 +else + case $PYTHON in + [\\/]* | ?:[\\/]*) + ac_cv_path_PYTHON="$PYTHON" # Let the user override the test with a path. + ;; + *) + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_path_PYTHON="$as_dir/$ac_word$ac_exec_ext" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + + ;; +esac +fi +PYTHON=$ac_cv_path_PYTHON +if test -n "$PYTHON"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PYTHON" >&5 +$as_echo "$PYTHON" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + fi + am_display_PYTHON=$am_cv_pathless_PYTHON + fi + + + if test "$PYTHON" = :; then + : + else + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $am_display_PYTHON version" >&5 +$as_echo_n "checking for $am_display_PYTHON version... " >&6; } +if ${am_cv_python_version+:} false; then : + $as_echo_n "(cached) " >&6 +else + am_cv_python_version=`$PYTHON -c "import sys; sys.stdout.write(sys.version[:3])"` +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_python_version" >&5 +$as_echo "$am_cv_python_version" >&6; } + PYTHON_VERSION=$am_cv_python_version + + + + PYTHON_PREFIX='${prefix}' + + PYTHON_EXEC_PREFIX='${exec_prefix}' + + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $am_display_PYTHON platform" >&5 +$as_echo_n "checking for $am_display_PYTHON platform... " >&6; } +if ${am_cv_python_platform+:} false; then : + $as_echo_n "(cached) " >&6 +else + am_cv_python_platform=`$PYTHON -c "import sys; sys.stdout.write(sys.platform)"` +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_python_platform" >&5 +$as_echo "$am_cv_python_platform" >&6; } + PYTHON_PLATFORM=$am_cv_python_platform + + + # Just factor out some code duplication. + am_python_setup_sysconfig="\ +import sys +# Prefer sysconfig over distutils.sysconfig, for better compatibility +# with python 3.x. See automake bug#10227. +try: + import sysconfig +except ImportError: + can_use_sysconfig = 0 +else: + can_use_sysconfig = 1 +# Can't use sysconfig in CPython 2.7, since it's broken in virtualenvs: +# +try: + from platform import python_implementation + if python_implementation() == 'CPython' and sys.version[:3] == '2.7': + can_use_sysconfig = 0 +except ImportError: + pass" + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $am_display_PYTHON script directory" >&5 +$as_echo_n "checking for $am_display_PYTHON script directory... " >&6; } +if ${am_cv_python_pythondir+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test "x$prefix" = xNONE + then + am_py_prefix=$ac_default_prefix + else + am_py_prefix=$prefix + fi + am_cv_python_pythondir=`$PYTHON -c " +$am_python_setup_sysconfig +if can_use_sysconfig: + sitedir = sysconfig.get_path('purelib', vars={'base':'$am_py_prefix'}) +else: + from distutils import sysconfig + sitedir = sysconfig.get_python_lib(0, 0, prefix='$am_py_prefix') +sys.stdout.write(sitedir)"` + case $am_cv_python_pythondir in + $am_py_prefix*) + am__strip_prefix=`echo "$am_py_prefix" | sed 's|.|.|g'` + am_cv_python_pythondir=`echo "$am_cv_python_pythondir" | sed "s,^$am__strip_prefix,$PYTHON_PREFIX,"` + ;; + *) + case $am_py_prefix in + /usr|/System*) ;; + *) + am_cv_python_pythondir=$PYTHON_PREFIX/lib/python$PYTHON_VERSION/site-packages + ;; + esac + ;; + esac + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_python_pythondir" >&5 +$as_echo "$am_cv_python_pythondir" >&6; } + pythondir=$am_cv_python_pythondir + + + + pkgpythondir=\${pythondir}/$PACKAGE + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $am_display_PYTHON extension module directory" >&5 +$as_echo_n "checking for $am_display_PYTHON extension module directory... " >&6; } +if ${am_cv_python_pyexecdir+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test "x$exec_prefix" = xNONE + then + am_py_exec_prefix=$am_py_prefix + else + am_py_exec_prefix=$exec_prefix + fi + am_cv_python_pyexecdir=`$PYTHON -c " +$am_python_setup_sysconfig +if can_use_sysconfig: + sitedir = sysconfig.get_path('platlib', vars={'platbase':'$am_py_prefix'}) +else: + from distutils import sysconfig + sitedir = sysconfig.get_python_lib(1, 0, prefix='$am_py_prefix') +sys.stdout.write(sitedir)"` + case $am_cv_python_pyexecdir in + $am_py_exec_prefix*) + am__strip_prefix=`echo "$am_py_exec_prefix" | sed 's|.|.|g'` + am_cv_python_pyexecdir=`echo "$am_cv_python_pyexecdir" | sed "s,^$am__strip_prefix,$PYTHON_EXEC_PREFIX,"` + ;; + *) + case $am_py_exec_prefix in + /usr|/System*) ;; + *) + am_cv_python_pyexecdir=$PYTHON_EXEC_PREFIX/lib/python$PYTHON_VERSION/site-packages + ;; + esac + ;; + esac + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_python_pyexecdir" >&5 +$as_echo "$am_cv_python_pyexecdir" >&6; } + pyexecdir=$am_cv_python_pyexecdir + + + + pkgpyexecdir=\${pyexecdir}/$PACKAGE + + + + fi + + + + # + # Allow the use of a (user set) custom python version + # + + + # Extract the first word of "python[$PYTHON_VERSION]", so it can be a program name with args. +set dummy python$PYTHON_VERSION; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_path_PYTHON+:} false; then : + $as_echo_n "(cached) " >&6 +else + case $PYTHON in + [\\/]* | ?:[\\/]*) + ac_cv_path_PYTHON="$PYTHON" # Let the user override the test with a path. + ;; + *) + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_path_PYTHON="$as_dir/$ac_word$ac_exec_ext" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + + ;; +esac +fi +PYTHON=$ac_cv_path_PYTHON +if test -n "$PYTHON"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PYTHON" >&5 +$as_echo "$PYTHON" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + if test -z "$PYTHON"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Cannot find python$PYTHON_VERSION in your system path" >&5 +$as_echo "$as_me: WARNING: Cannot find python$PYTHON_VERSION in your system path" >&2;} + PYTHON_VERSION="" + no_python_devel=yes + fi + +if test -z "$no_python_devel"; then : + + # + # Check for a version of Python >= 2.1.0 + # + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for a version of Python >= '2.1.0'" >&5 +$as_echo_n "checking for a version of Python >= '2.1.0'... " >&6; } + ac_supports_python_ver=`$PYTHON -c "import sys; \ + ver = sys.version.split ()[0]; \ + print (ver >= '2.1.0')"` + if test "$ac_supports_python_ver" != "True"; then + if test -z "$PYTHON_NOVERSIONCHECK"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: +This version of the AC_PYTHON_DEVEL macro +doesn't work properly with versions of Python before +2.1.0. You may need to re-run configure, setting the +variables PYTHON_CPPFLAGS, PYTHON_LDFLAGS, PYTHON_SITE_PKG, +PYTHON_EXTRA_LIBS and PYTHON_EXTRA_LDFLAGS by hand. +Moreover, to disable this check, set PYTHON_NOVERSIONCHECK +to something else than an empty string. +" >&5 +$as_echo "$as_me: WARNING: +This version of the AC_PYTHON_DEVEL macro +doesn't work properly with versions of Python before +2.1.0. You may need to re-run configure, setting the +variables PYTHON_CPPFLAGS, PYTHON_LDFLAGS, PYTHON_SITE_PKG, +PYTHON_EXTRA_LIBS and PYTHON_EXTRA_LDFLAGS by hand. +Moreover, to disable this check, set PYTHON_NOVERSIONCHECK +to something else than an empty string. +" >&2;} + no_python_devel=yes + else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: skip at user request" >&5 +$as_echo "skip at user request" >&6; } + fi + else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + fi + +fi # AS_IF + +if test -z "$no_python_devel"; then : + + # + # if the macro parameter ``version'' is set, honour it + # + if test -n ">= '2.7'"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for a version of Python >= '2.7'" >&5 +$as_echo_n "checking for a version of Python >= '2.7'... " >&6; } + ac_supports_python_ver=`$PYTHON -c "import sys; \ + ver = sys.version.split ()[0]; \ + print (ver >= '2.7')"` + if test "$ac_supports_python_ver" = "True"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: this package requires Python >= '2.7'. +If you have it installed, but it isn't the default Python +interpreter in your system path, please pass the PYTHON_VERSION +variable to configure. See \`\`configure --help'' for reference. +" >&5 +$as_echo "$as_me: WARNING: this package requires Python >= '2.7'. +If you have it installed, but it isn't the default Python +interpreter in your system path, please pass the PYTHON_VERSION +variable to configure. See \`\`configure --help'' for reference. +" >&2;} + PYTHON_VERSION="" + no_python_devel=yes + fi + fi + +fi # AS_IF + +if test -z "$no_python_devel"; then : + + # + # Check if you have distutils, else fail + # + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for the distutils Python package" >&5 +$as_echo_n "checking for the distutils Python package... " >&6; } + ac_distutils_result=`$PYTHON -c "import distutils" 2>&1` + if test -z "$ac_distutils_result"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cannot import Python module \"distutils\". +Please check your Python installation. The error was: +$ac_distutils_result" >&5 +$as_echo "$as_me: WARNING: cannot import Python module \"distutils\". +Please check your Python installation. The error was: +$ac_distutils_result" >&2;} + PYTHON_VERSION="" + no_python_devel=yes + fi + +fi # AS_IF + +if test -z "$no_python_devel"; then : + + # + # Check for Python include path + # + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for Python include path" >&5 +$as_echo_n "checking for Python include path... " >&6; } + if test -z "$PYTHON_CPPFLAGS"; then + python_path=`$PYTHON -c "import distutils.sysconfig; \ + print (distutils.sysconfig.get_python_inc ());"` + plat_python_path=`$PYTHON -c "import distutils.sysconfig; \ + print (distutils.sysconfig.get_python_inc (plat_specific=1));"` + if test -n "${python_path}"; then + if test "${plat_python_path}" != "${python_path}"; then + python_path="-I$python_path -I$plat_python_path" + else + python_path="-I$python_path" + fi + fi + PYTHON_CPPFLAGS=$python_path + fi + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PYTHON_CPPFLAGS" >&5 +$as_echo "$PYTHON_CPPFLAGS" >&6; } + + + # + # Check for Python library path + # + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for Python library path" >&5 +$as_echo_n "checking for Python library path... " >&6; } + if test -z "$PYTHON_LDFLAGS"; then + # (makes two attempts to ensure we've got a version number + # from the interpreter) + ac_python_version=`cat<>confdefs.h <<_ACEOF +#define HAVE_PYTHON "$ac_python_version" +_ACEOF + + + # First, the library directory: + ac_python_libdir=`cat<&5 +$as_echo "$as_me: WARNING: + Cannot determine location of your Python DSO. Please check it was installed with + dynamic libraries enabled, or try setting PYTHON_LDFLAGS by hand. + " >&2;} + no_python_devel=yes + fi + fi + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PYTHON_LDFLAGS" >&5 +$as_echo "$PYTHON_LDFLAGS" >&6; } + + +fi # AS_IF + +if test -z "$no_python_devel"; then : + + # + # Check for site packages + # + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for Python site-packages path" >&5 +$as_echo_n "checking for Python site-packages path... " >&6; } + if test -z "$PYTHON_SITE_PKG"; then + PYTHON_SITE_PKG=`$PYTHON -c "import distutils.sysconfig; \ + print (distutils.sysconfig.get_python_lib(0,0));"` + fi + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PYTHON_SITE_PKG" >&5 +$as_echo "$PYTHON_SITE_PKG" >&6; } + + + # + # libraries which must be linked in when embedding + # + { $as_echo "$as_me:${as_lineno-$LINENO}: checking python extra libraries" >&5 +$as_echo_n "checking python extra libraries... " >&6; } + if test -z "$PYTHON_EXTRA_LIBS"; then + PYTHON_EXTRA_LIBS=`$PYTHON -c "import distutils.sysconfig; \ + conf = distutils.sysconfig.get_config_var; \ + print (conf('LIBS'))"` + fi + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PYTHON_EXTRA_LIBS" >&5 +$as_echo "$PYTHON_EXTRA_LIBS" >&6; } + + + # + # linking flags needed when embedding + # + { $as_echo "$as_me:${as_lineno-$LINENO}: checking python extra linking flags" >&5 +$as_echo_n "checking python extra linking flags... " >&6; } + if test -z "$PYTHON_EXTRA_LDFLAGS"; then + PYTHON_EXTRA_LDFLAGS=`$PYTHON -c "import distutils.sysconfig; \ + conf = distutils.sysconfig.get_config_var; \ + print (conf('LINKFORSHARED'))"` + fi + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PYTHON_EXTRA_LDFLAGS" >&5 +$as_echo "$PYTHON_EXTRA_LDFLAGS" >&6; } + + + # + # final check to see if everything compiles alright + # + { $as_echo "$as_me:${as_lineno-$LINENO}: checking consistency of all components of python development environment" >&5 +$as_echo_n "checking consistency of all components of python development environment... " >&6; } + # save current global flags + ac_save_LIBS="$LIBS" + ac_save_CPPFLAGS="$CPPFLAGS" + LIBS="$ac_save_LIBS $PYTHON_LDFLAGS $PYTHON_EXTRA_LDFLAGS $PYTHON_EXTRA_LIBS" + CPPFLAGS="$ac_save_CPPFLAGS $PYTHON_CPPFLAGS" + ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + + #include +int +main () +{ +Py_Initialize(); + ; + return 0; +} + +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + pythonexists=yes +else + pythonexists=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + # turn back to default flags + CPPFLAGS="$ac_save_CPPFLAGS" + LIBS="$ac_save_LIBS" + + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $pythonexists" >&5 +$as_echo "$pythonexists" >&6; } + + if test ! "x$pythonexists" = "xyes"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: + Could not link test program to Python. Maybe the main Python library has been + installed in some non-standard library path. If so, pass it to configure, + via the LDFLAGS environment variable. + Example: ./configure LDFLAGS=\"-L/usr/non-standard-path/python/lib\" + ============================================================================ + ERROR! + You probably have to install the development version of the Python package + for your distribution. The exact name of this package varies among them. + ============================================================================ + " >&5 +$as_echo "$as_me: WARNING: + Could not link test program to Python. Maybe the main Python library has been + installed in some non-standard library path. If so, pass it to configure, + via the LDFLAGS environment variable. + Example: ./configure LDFLAGS=\"-L/usr/non-standard-path/python/lib\" + ============================================================================ + ERROR! + You probably have to install the development version of the Python package + for your distribution. The exact name of this package varies among them. + ============================================================================ + " >&2;} + PYTHON_VERSION="" + no_python_devel=yes + fi + + # + # all done! + # + +fi # AS_IF + +if test -z "$no_python_devel"; then : + have_python_dev=yes +else + have_python_dev=no +fi + + +fi + +if test "x${cython_path}" = "x"; then + for ac_prog in cython.py cython +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_CYTHON+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$CYTHON"; then + ac_cv_prog_CYTHON="$CYTHON" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CYTHON="$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +CYTHON=$ac_cv_prog_CYTHON +if test -n "$CYTHON"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CYTHON" >&5 +$as_echo "$CYTHON" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$CYTHON" && break +done + +else + CYTHON=${cython_path} + +fi + +# +# If we're running GCC or clang define _U_ to be "__attribute__((unused))" +# so we can use _U_ to flag unused function parameters and not get warnings +# about them. Otherwise, define _U_ to be an empty string so that _U_ used +# to flag an unused function parameters will compile with other compilers. +# +# XXX - similar hints for other compilers? +# +if test "x$GCC" = "xyes" -o "x$CC" = "xclang" ; then + +$as_echo "#define _U_ __attribute__((unused))" >>confdefs.h + +else + +$as_echo "#define _U_ /**/" >>confdefs.h + +fi + + ax_cxx_compile_cxx11_required=falsednl + ac_ext=cpp +ac_cpp='$CXXCPP $CPPFLAGS' +ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_cxx_compiler_gnu + ac_success=no + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CXX supports C++11 features by default" >&5 +$as_echo_n "checking whether $CXX supports C++11 features by default... " >&6; } +if ${ax_cv_cxx_compile_cxx11+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + + template + struct check + { + static_assert(sizeof(int) <= sizeof(T), "not big enough"); + }; + + typedef check> right_angle_brackets; + + int a; + decltype(a) b; + + typedef check check_type; + check_type c; + check_type&& cr = static_cast(c); + + auto d = a; + +_ACEOF +if ac_fn_cxx_try_compile "$LINENO"; then : + ax_cv_cxx_compile_cxx11=yes +else + ax_cv_cxx_compile_cxx11=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_cxx_compile_cxx11" >&5 +$as_echo "$ax_cv_cxx_compile_cxx11" >&6; } + if test x$ax_cv_cxx_compile_cxx11 = xyes; then + ac_success=yes + fi + + + + if test x$ac_success = xno; then + for switch in -std=c++11 -std=c++0x; do + cachevar=`$as_echo "ax_cv_cxx_compile_cxx11_$switch" | $as_tr_sh` + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CXX supports C++11 features with $switch" >&5 +$as_echo_n "checking whether $CXX supports C++11 features with $switch... " >&6; } +if eval \${$cachevar+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_save_CXXFLAGS="$CXXFLAGS" + CXXFLAGS="$CXXFLAGS $switch" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + + template + struct check + { + static_assert(sizeof(int) <= sizeof(T), "not big enough"); + }; + + typedef check> right_angle_brackets; + + int a; + decltype(a) b; + + typedef check check_type; + check_type c; + check_type&& cr = static_cast(c); + + auto d = a; + +_ACEOF +if ac_fn_cxx_try_compile "$LINENO"; then : + eval $cachevar=yes +else + eval $cachevar=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + CXXFLAGS="$ac_save_CXXFLAGS" +fi +eval ac_res=\$$cachevar + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } + if eval test x\$$cachevar = xyes; then + CXXFLAGS="$CXXFLAGS $switch" + ac_success=yes + break + fi + done + fi + ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + if test x$ax_cxx_compile_cxx11_required = xtrue; then + if test x$ac_success = xno; then + as_fn_error $? "*** A compiler with support for C++11 language features is required." "$LINENO" 5 + fi + else + if test x$ac_success = xno; then + HAVE_CXX11=0 + { $as_echo "$as_me:${as_lineno-$LINENO}: No compiler with C++11 support was found" >&5 +$as_echo "$as_me: No compiler with C++11 support was found" >&6;} + else + HAVE_CXX11=1 + +$as_echo "#define HAVE_CXX11 1" >>confdefs.h + + fi + + + fi + + +ac_ext=cpp +ac_cpp='$CXXCPP $CPPFLAGS' +ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_cxx_compiler_gnu + + +# Check that std::chrono::steady_clock is available. In particular, +# gcc 4.6 does not have one, but has monotonic_clock which is the old +# name existed in the pre-standard draft. If steady_clock is not +# available, don't define HAVE_STEADY_CLOCK and replace steady_clock +# with monotonic_clock. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether std::chrono::steady_clock is available" >&5 +$as_echo_n "checking whether std::chrono::steady_clock is available... " >&6; } +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#include + +int +main () +{ + +auto tp = std::chrono::steady_clock::now(); + + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_compile "$LINENO"; then : + +$as_echo "#define HAVE_STEADY_CLOCK 1" >>confdefs.h + + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + +# Check that std::future is available. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether std::future is available" >&5 +$as_echo_n "checking whether std::future is available... " >&6; } +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#include +#include + +int +main () +{ + +std::vector> v; + + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_compile "$LINENO"; then : + +$as_echo "#define HAVE_STD_FUTURE 1" >>confdefs.h + + have_std_future=yes + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } +else + have_std_future=no + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + +# Check that std::map::emplace is available for g++-4.7. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether std::map::emplace is available" >&5 +$as_echo_n "checking whether std::map::emplace is available... " >&6; } +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#include + +int +main () +{ + +std::map().emplace(1, 2); + + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_compile "$LINENO"; then : + +$as_echo "#define HAVE_STD_MAP_EMPLACE 1" >>confdefs.h + + have_std_map_emplace=yes + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } +else + have_std_map_emplace=no + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + +# Checks for libraries. + +# Additional libraries required for tests. +TESTLDADD= + +# Additional libraries required for programs under src directory. +APPLDFLAGS= + +LIBS_OLD=$LIBS +# Search for dlsym function, which is used in tests. Linux needs -ldl, +# but netbsd does not need it. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing dlsym" >&5 +$as_echo_n "checking for library containing dlsym... " >&6; } +if ${ac_cv_search_dlsym+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_func_search_save_LIBS=$LIBS +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char dlsym (); +int +main () +{ +return dlsym (); + ; + return 0; +} +_ACEOF +for ac_lib in '' dl; do + if test -z "$ac_lib"; then + ac_res="none required" + else + ac_res=-l$ac_lib + LIBS="-l$ac_lib $ac_func_search_save_LIBS" + fi + if ac_fn_c_try_link "$LINENO"; then : + ac_cv_search_dlsym=$ac_res +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext + if ${ac_cv_search_dlsym+:} false; then : + break +fi +done +if ${ac_cv_search_dlsym+:} false; then : + +else + ac_cv_search_dlsym=no +fi +rm conftest.$ac_ext +LIBS=$ac_func_search_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_dlsym" >&5 +$as_echo "$ac_cv_search_dlsym" >&6; } +ac_res=$ac_cv_search_dlsym +if test "$ac_res" != no; then : + test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" + +fi + +TESTLDADD="$LIBS $TESTLDADD" +LIBS=$LIBS_OLD + +LIBS_OLD=$LIBS +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing clock_gettime" >&5 +$as_echo_n "checking for library containing clock_gettime... " >&6; } +if ${ac_cv_search_clock_gettime+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_func_search_save_LIBS=$LIBS +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char clock_gettime (); +int +main () +{ +return clock_gettime (); + ; + return 0; +} +_ACEOF +for ac_lib in '' rt; do + if test -z "$ac_lib"; then + ac_res="none required" + else + ac_res=-l$ac_lib + LIBS="-l$ac_lib $ac_func_search_save_LIBS" + fi + if ac_fn_c_try_link "$LINENO"; then : + ac_cv_search_clock_gettime=$ac_res +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext + if ${ac_cv_search_clock_gettime+:} false; then : + break +fi +done +if ${ac_cv_search_clock_gettime+:} false; then : + +else + ac_cv_search_clock_gettime=no +fi +rm conftest.$ac_ext +LIBS=$ac_func_search_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_clock_gettime" >&5 +$as_echo "$ac_cv_search_clock_gettime" >&6; } +ac_res=$ac_cv_search_clock_gettime +if test "$ac_res" != no; then : + test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" + +$as_echo "#define HAVE_CLOCK_GETTIME 1" >>confdefs.h + +fi + +APPLDFLAGS="$LIBS $APPLDFLAGS" +LIBS=$LIBS_OLD + +case "$host" in + *android*) + android_build=yes + # android does not need -pthread, but needs followng 3 libs for C++ + APPLDFLAGS="$APPLDFLAGS -lstdc++ -latomic -lsupc++" + ;; + *) + PTHREAD_LDFLAGS="-pthread" + APPLDFLAGS="$APPLDFLAGS $PTHREAD_LDFLAGS" + ;; +esac + +# zlib +if test "x$android_build" = "xyes"; then + # Use zlib provided by NDK + APPLDFLAGS="-lz $APPLDFLAGS" + have_zlib=yes +else + +pkg_failed=no +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for ZLIB" >&5 +$as_echo_n "checking for ZLIB... " >&6; } + +if test -n "$ZLIB_CFLAGS"; then + pkg_cv_ZLIB_CFLAGS="$ZLIB_CFLAGS" + elif test -n "$PKG_CONFIG"; then + if test -n "$PKG_CONFIG" && \ + { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"zlib >= 1.2.3\""; } >&5 + ($PKG_CONFIG --exists --print-errors "zlib >= 1.2.3") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then + pkg_cv_ZLIB_CFLAGS=`$PKG_CONFIG --cflags "zlib >= 1.2.3" 2>/dev/null` + test "x$?" != "x0" && pkg_failed=yes +else + pkg_failed=yes +fi + else + pkg_failed=untried +fi +if test -n "$ZLIB_LIBS"; then + pkg_cv_ZLIB_LIBS="$ZLIB_LIBS" + elif test -n "$PKG_CONFIG"; then + if test -n "$PKG_CONFIG" && \ + { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"zlib >= 1.2.3\""; } >&5 + ($PKG_CONFIG --exists --print-errors "zlib >= 1.2.3") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then + pkg_cv_ZLIB_LIBS=`$PKG_CONFIG --libs "zlib >= 1.2.3" 2>/dev/null` + test "x$?" != "x0" && pkg_failed=yes +else + pkg_failed=yes +fi + else + pkg_failed=untried +fi + + + +if test $pkg_failed = yes; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + +if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then + _pkg_short_errors_supported=yes +else + _pkg_short_errors_supported=no +fi + if test $_pkg_short_errors_supported = yes; then + ZLIB_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "zlib >= 1.2.3" 2>&1` + else + ZLIB_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "zlib >= 1.2.3" 2>&1` + fi + # Put the nasty error message in config.log where it belongs + echo "$ZLIB_PKG_ERRORS" >&5 + + have_zlib=no +elif test $pkg_failed = untried; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + have_zlib=no +else + ZLIB_CFLAGS=$pkg_cv_ZLIB_CFLAGS + ZLIB_LIBS=$pkg_cv_ZLIB_LIBS + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + have_zlib=yes +fi + + if test "x${have_zlib}" = "xno"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: $ZLIB_PKG_ERRORS" >&5 +$as_echo "$as_me: $ZLIB_PKG_ERRORS" >&6;} + fi +fi + +# cunit + +pkg_failed=no +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for CUNIT" >&5 +$as_echo_n "checking for CUNIT... " >&6; } + +if test -n "$CUNIT_CFLAGS"; then + pkg_cv_CUNIT_CFLAGS="$CUNIT_CFLAGS" + elif test -n "$PKG_CONFIG"; then + if test -n "$PKG_CONFIG" && \ + { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"cunit >= 2.1\""; } >&5 + ($PKG_CONFIG --exists --print-errors "cunit >= 2.1") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then + pkg_cv_CUNIT_CFLAGS=`$PKG_CONFIG --cflags "cunit >= 2.1" 2>/dev/null` + test "x$?" != "x0" && pkg_failed=yes +else + pkg_failed=yes +fi + else + pkg_failed=untried +fi +if test -n "$CUNIT_LIBS"; then + pkg_cv_CUNIT_LIBS="$CUNIT_LIBS" + elif test -n "$PKG_CONFIG"; then + if test -n "$PKG_CONFIG" && \ + { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"cunit >= 2.1\""; } >&5 + ($PKG_CONFIG --exists --print-errors "cunit >= 2.1") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then + pkg_cv_CUNIT_LIBS=`$PKG_CONFIG --libs "cunit >= 2.1" 2>/dev/null` + test "x$?" != "x0" && pkg_failed=yes +else + pkg_failed=yes +fi + else + pkg_failed=untried +fi + + + +if test $pkg_failed = yes; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + +if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then + _pkg_short_errors_supported=yes +else + _pkg_short_errors_supported=no +fi + if test $_pkg_short_errors_supported = yes; then + CUNIT_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "cunit >= 2.1" 2>&1` + else + CUNIT_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "cunit >= 2.1" 2>&1` + fi + # Put the nasty error message in config.log where it belongs + echo "$CUNIT_PKG_ERRORS" >&5 + + have_cunit=no +elif test $pkg_failed = untried; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + have_cunit=no +else + CUNIT_CFLAGS=$pkg_cv_CUNIT_CFLAGS + CUNIT_LIBS=$pkg_cv_CUNIT_LIBS + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + have_cunit=yes +fi +# If pkg-config does not find cunit, check it using AC_CHECK_LIB. We +# do this because Debian (Ubuntu) lacks pkg-config file for cunit. +if test "x${have_cunit}" = "xno"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: ${CUNIT_PKG_ERRORS}" >&5 +$as_echo "$as_me: WARNING: ${CUNIT_PKG_ERRORS}" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for CU_initialize_registry in -lcunit" >&5 +$as_echo_n "checking for CU_initialize_registry in -lcunit... " >&6; } +if ${ac_cv_lib_cunit_CU_initialize_registry+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lcunit $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char CU_initialize_registry (); +int +main () +{ +return CU_initialize_registry (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_cunit_CU_initialize_registry=yes +else + ac_cv_lib_cunit_CU_initialize_registry=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_cunit_CU_initialize_registry" >&5 +$as_echo "$ac_cv_lib_cunit_CU_initialize_registry" >&6; } +if test "x$ac_cv_lib_cunit_CU_initialize_registry" = xyes; then : + have_cunit=yes +else + have_cunit=no +fi + + if test "x${have_cunit}" = "xyes"; then + CUNIT_LIBS="-lcunit" + CUNIT_CFLAGS="" + + + fi +fi +if test "x${have_cunit}" = "xyes"; then + # cunit in Mac OS X requires ncurses. Note that in Mac OS X, test + # program can be built without -lncurses, but it emits runtime + # error. + case "${build}" in + *-apple-darwin*) + CUNIT_LIBS="$CUNIT_LIBS -lncurses" + + ;; + esac +fi + + if test "x${have_cunit}" = "xyes" ; then + HAVE_CUNIT_TRUE= + HAVE_CUNIT_FALSE='#' +else + HAVE_CUNIT_TRUE='#' + HAVE_CUNIT_FALSE= +fi + + +# libev (for src) +# libev does not have pkg-config file. Check it in an old way. +LIBS_OLD=$LIBS +# android requires -lm for floor +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for ev_time in -lev" >&5 +$as_echo_n "checking for ev_time in -lev... " >&6; } +if ${ac_cv_lib_ev_ev_time+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lev -lm $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char ev_time (); +int +main () +{ +return ev_time (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_ev_ev_time=yes +else + ac_cv_lib_ev_ev_time=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_ev_ev_time" >&5 +$as_echo "$ac_cv_lib_ev_ev_time" >&6; } +if test "x$ac_cv_lib_ev_ev_time" = xyes; then : + have_libev=yes +else + have_libev=no +fi + +if test "x${have_libev}" = "xyes"; then + ac_fn_c_check_header_mongrel "$LINENO" "ev.h" "ac_cv_header_ev_h" "$ac_includes_default" +if test "x$ac_cv_header_ev_h" = xyes; then : + have_libev=yes +else + have_libev=no +fi + + + if test "x${have_libev}" = "xyes"; then + LIBEV_LIBS=-lev + LIBEV_CFLAGS= + + + fi +fi +LIBS=$LIBS_OLD + +# openssl (for src) + +pkg_failed=no +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for OPENSSL" >&5 +$as_echo_n "checking for OPENSSL... " >&6; } + +if test -n "$OPENSSL_CFLAGS"; then + pkg_cv_OPENSSL_CFLAGS="$OPENSSL_CFLAGS" + elif test -n "$PKG_CONFIG"; then + if test -n "$PKG_CONFIG" && \ + { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"openssl >= 1.0.1\""; } >&5 + ($PKG_CONFIG --exists --print-errors "openssl >= 1.0.1") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then + pkg_cv_OPENSSL_CFLAGS=`$PKG_CONFIG --cflags "openssl >= 1.0.1" 2>/dev/null` + test "x$?" != "x0" && pkg_failed=yes +else + pkg_failed=yes +fi + else + pkg_failed=untried +fi +if test -n "$OPENSSL_LIBS"; then + pkg_cv_OPENSSL_LIBS="$OPENSSL_LIBS" + elif test -n "$PKG_CONFIG"; then + if test -n "$PKG_CONFIG" && \ + { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"openssl >= 1.0.1\""; } >&5 + ($PKG_CONFIG --exists --print-errors "openssl >= 1.0.1") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then + pkg_cv_OPENSSL_LIBS=`$PKG_CONFIG --libs "openssl >= 1.0.1" 2>/dev/null` + test "x$?" != "x0" && pkg_failed=yes +else + pkg_failed=yes +fi + else + pkg_failed=untried +fi + + + +if test $pkg_failed = yes; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + +if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then + _pkg_short_errors_supported=yes +else + _pkg_short_errors_supported=no +fi + if test $_pkg_short_errors_supported = yes; then + OPENSSL_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "openssl >= 1.0.1" 2>&1` + else + OPENSSL_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "openssl >= 1.0.1" 2>&1` + fi + # Put the nasty error message in config.log where it belongs + echo "$OPENSSL_PKG_ERRORS" >&5 + + have_openssl=no +elif test $pkg_failed = untried; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + have_openssl=no +else + OPENSSL_CFLAGS=$pkg_cv_OPENSSL_CFLAGS + OPENSSL_LIBS=$pkg_cv_OPENSSL_LIBS + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + have_openssl=yes +fi +if test "x${have_openssl}" = "xno"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: $OPENSSL_PKG_ERRORS" >&5 +$as_echo "$as_me: $OPENSSL_PKG_ERRORS" >&6;} +fi + +# libevent_openssl (for examples) +# 2.0.8 is required because we use evconnlistener_set_error_cb() + +pkg_failed=no +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for LIBEVENT_OPENSSL" >&5 +$as_echo_n "checking for LIBEVENT_OPENSSL... " >&6; } + +if test -n "$LIBEVENT_OPENSSL_CFLAGS"; then + pkg_cv_LIBEVENT_OPENSSL_CFLAGS="$LIBEVENT_OPENSSL_CFLAGS" + elif test -n "$PKG_CONFIG"; then + if test -n "$PKG_CONFIG" && \ + { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libevent_openssl >= 2.0.8\""; } >&5 + ($PKG_CONFIG --exists --print-errors "libevent_openssl >= 2.0.8") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then + pkg_cv_LIBEVENT_OPENSSL_CFLAGS=`$PKG_CONFIG --cflags "libevent_openssl >= 2.0.8" 2>/dev/null` + test "x$?" != "x0" && pkg_failed=yes +else + pkg_failed=yes +fi + else + pkg_failed=untried +fi +if test -n "$LIBEVENT_OPENSSL_LIBS"; then + pkg_cv_LIBEVENT_OPENSSL_LIBS="$LIBEVENT_OPENSSL_LIBS" + elif test -n "$PKG_CONFIG"; then + if test -n "$PKG_CONFIG" && \ + { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libevent_openssl >= 2.0.8\""; } >&5 + ($PKG_CONFIG --exists --print-errors "libevent_openssl >= 2.0.8") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then + pkg_cv_LIBEVENT_OPENSSL_LIBS=`$PKG_CONFIG --libs "libevent_openssl >= 2.0.8" 2>/dev/null` + test "x$?" != "x0" && pkg_failed=yes +else + pkg_failed=yes +fi + else + pkg_failed=untried +fi + + + +if test $pkg_failed = yes; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + +if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then + _pkg_short_errors_supported=yes +else + _pkg_short_errors_supported=no +fi + if test $_pkg_short_errors_supported = yes; then + LIBEVENT_OPENSSL_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "libevent_openssl >= 2.0.8" 2>&1` + else + LIBEVENT_OPENSSL_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "libevent_openssl >= 2.0.8" 2>&1` + fi + # Put the nasty error message in config.log where it belongs + echo "$LIBEVENT_OPENSSL_PKG_ERRORS" >&5 + + have_libevent_openssl=no +elif test $pkg_failed = untried; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + have_libevent_openssl=no +else + LIBEVENT_OPENSSL_CFLAGS=$pkg_cv_LIBEVENT_OPENSSL_CFLAGS + LIBEVENT_OPENSSL_LIBS=$pkg_cv_LIBEVENT_OPENSSL_LIBS + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + have_libevent_openssl=yes +fi +if test "x${have_libevent_openssl}" = "xno"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: $LIBEVENT_OPENSSL_PKG_ERRORS" >&5 +$as_echo "$as_me: $LIBEVENT_OPENSSL_PKG_ERRORS" >&6;} +fi + +# jansson (for src/nghttp, src/deflatehd and src/inflatehd) + +pkg_failed=no +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for JANSSON" >&5 +$as_echo_n "checking for JANSSON... " >&6; } + +if test -n "$JANSSON_CFLAGS"; then + pkg_cv_JANSSON_CFLAGS="$JANSSON_CFLAGS" + elif test -n "$PKG_CONFIG"; then + if test -n "$PKG_CONFIG" && \ + { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"jansson >= 2.5\""; } >&5 + ($PKG_CONFIG --exists --print-errors "jansson >= 2.5") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then + pkg_cv_JANSSON_CFLAGS=`$PKG_CONFIG --cflags "jansson >= 2.5" 2>/dev/null` + test "x$?" != "x0" && pkg_failed=yes +else + pkg_failed=yes +fi + else + pkg_failed=untried +fi +if test -n "$JANSSON_LIBS"; then + pkg_cv_JANSSON_LIBS="$JANSSON_LIBS" + elif test -n "$PKG_CONFIG"; then + if test -n "$PKG_CONFIG" && \ + { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"jansson >= 2.5\""; } >&5 + ($PKG_CONFIG --exists --print-errors "jansson >= 2.5") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then + pkg_cv_JANSSON_LIBS=`$PKG_CONFIG --libs "jansson >= 2.5" 2>/dev/null` + test "x$?" != "x0" && pkg_failed=yes +else + pkg_failed=yes +fi + else + pkg_failed=untried +fi + + + +if test $pkg_failed = yes; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + +if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then + _pkg_short_errors_supported=yes +else + _pkg_short_errors_supported=no +fi + if test $_pkg_short_errors_supported = yes; then + JANSSON_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "jansson >= 2.5" 2>&1` + else + JANSSON_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "jansson >= 2.5" 2>&1` + fi + # Put the nasty error message in config.log where it belongs + echo "$JANSSON_PKG_ERRORS" >&5 + + have_jansson=no +elif test $pkg_failed = untried; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + have_jansson=no +else + JANSSON_CFLAGS=$pkg_cv_JANSSON_CFLAGS + JANSSON_LIBS=$pkg_cv_JANSSON_LIBS + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + have_jansson=yes +fi +if test "x${have_jansson}" == "xyes"; then + +$as_echo "#define HAVE_JANSSON 1" >>confdefs.h + +else + { $as_echo "$as_me:${as_lineno-$LINENO}: $JANSSON_PKG_ERRORS" >&5 +$as_echo "$as_me: $JANSSON_PKG_ERRORS" >&6;} +fi + +# libxml2 (for src/nghttp) +have_libxml2=no +if test "x${request_libxml2}" != "xno"; then + + +# Check whether --with-xml-prefix was given. +if test "${with_xml_prefix+set}" = set; then : + withval=$with_xml_prefix; xml_config_prefix="$withval" +else + xml_config_prefix="" +fi + + +# Check whether --with-xml-exec-prefix was given. +if test "${with_xml_exec_prefix+set}" = set; then : + withval=$with_xml_exec_prefix; xml_config_exec_prefix="$withval" +else + xml_config_exec_prefix="" +fi + +# Check whether --enable-xmltest was given. +if test "${enable_xmltest+set}" = set; then : + enableval=$enable_xmltest; +else + enable_xmltest=yes +fi + + + if test x$xml_config_exec_prefix != x ; then + xml_config_args="$xml_config_args" + if test x${XML2_CONFIG+set} != xset ; then + XML2_CONFIG=$xml_config_exec_prefix/bin/xml2-config + fi + fi + if test x$xml_config_prefix != x ; then + xml_config_args="$xml_config_args --prefix=$xml_config_prefix" + if test x${XML2_CONFIG+set} != xset ; then + XML2_CONFIG=$xml_config_prefix/bin/xml2-config + fi + fi + + # Extract the first word of "xml2-config", so it can be a program name with args. +set dummy xml2-config; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_path_XML2_CONFIG+:} false; then : + $as_echo_n "(cached) " >&6 +else + case $XML2_CONFIG in + [\\/]* | ?:[\\/]*) + ac_cv_path_XML2_CONFIG="$XML2_CONFIG" # Let the user override the test with a path. + ;; + *) + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_path_XML2_CONFIG="$as_dir/$ac_word$ac_exec_ext" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + + test -z "$ac_cv_path_XML2_CONFIG" && ac_cv_path_XML2_CONFIG="no" + ;; +esac +fi +XML2_CONFIG=$ac_cv_path_XML2_CONFIG +if test -n "$XML2_CONFIG"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $XML2_CONFIG" >&5 +$as_echo "$XML2_CONFIG" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + min_xml_version=2.7.7 + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for libxml - version >= $min_xml_version" >&5 +$as_echo_n "checking for libxml - version >= $min_xml_version... " >&6; } + no_xml="" + if test "$XML2_CONFIG" = "no" ; then + no_xml=yes + else + XML_CPPFLAGS=`$XML2_CONFIG $xml_config_args --cflags` + XML_LIBS=`$XML2_CONFIG $xml_config_args --libs` + xml_config_major_version=`$XML2_CONFIG $xml_config_args --version | \ + sed 's/\([0-9]*\).\([0-9]*\).\([0-9]*\)/\1/'` + xml_config_minor_version=`$XML2_CONFIG $xml_config_args --version | \ + sed 's/\([0-9]*\).\([0-9]*\).\([0-9]*\)/\2/'` + xml_config_micro_version=`$XML2_CONFIG $xml_config_args --version | \ + sed 's/\([0-9]*\).\([0-9]*\).\([0-9]*\)/\3/'` + if test "x$enable_xmltest" = "xyes" ; then + ac_save_CPPFLAGS="$CPPFLAGS" + ac_save_LIBS="$LIBS" + CPPFLAGS="$CPPFLAGS $XML_CPPFLAGS" + LIBS="$XML_LIBS $LIBS" + rm -f conf.xmltest + if test "$cross_compiling" = yes; then : + echo $ac_n "cross compiling; assumed OK... $ac_c" +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#include +#include +#include +#include + +int +main() +{ + int xml_major_version, xml_minor_version, xml_micro_version; + int major, minor, micro; + char *tmp_version; + + system("touch conf.xmltest"); + + /* Capture xml2-config output via autoconf/configure variables */ + /* HP/UX 9 (%@#!) writes to sscanf strings */ + tmp_version = (char *)strdup("$min_xml_version"); + if (sscanf(tmp_version, "%d.%d.%d", &major, &minor, µ) != 3) { + printf("%s, bad version string from xml2-config\n", "$min_xml_version"); + exit(1); + } + free(tmp_version); + + /* Capture the version information from the header files */ + tmp_version = (char *)strdup(LIBXML_DOTTED_VERSION); + if (sscanf(tmp_version, "%d.%d.%d", &xml_major_version, &xml_minor_version, &xml_micro_version) != 3) { + printf("%s, bad version string from libxml includes\n", "LIBXML_DOTTED_VERSION"); + exit(1); + } + free(tmp_version); + + /* Compare xml2-config output to the libxml headers */ + if ((xml_major_version != $xml_config_major_version) || + (xml_minor_version != $xml_config_minor_version) || + (xml_micro_version != $xml_config_micro_version)) + { + printf("*** libxml header files (version %d.%d.%d) do not match\n", + xml_major_version, xml_minor_version, xml_micro_version); + printf("*** xml2-config (version %d.%d.%d)\n", + $xml_config_major_version, $xml_config_minor_version, $xml_config_micro_version); + return 1; + } +/* Compare the headers to the library to make sure we match */ + /* Less than ideal -- doesn't provide us with return value feedback, + * only exits if there's a serious mismatch between header and library. + */ + LIBXML_TEST_VERSION; + + /* Test that the library is greater than our minimum version */ + if ((xml_major_version > major) || + ((xml_major_version == major) && (xml_minor_version > minor)) || + ((xml_major_version == major) && (xml_minor_version == minor) && + (xml_micro_version >= micro))) + { + return 0; + } + else + { + printf("\n*** An old version of libxml (%d.%d.%d) was found.\n", + xml_major_version, xml_minor_version, xml_micro_version); + printf("*** You need a version of libxml newer than %d.%d.%d. The latest version of\n", + major, minor, micro); + printf("*** libxml is always available from ftp://ftp.xmlsoft.org.\n"); + printf("***\n"); + printf("*** If you have already installed a sufficiently new version, this error\n"); + printf("*** probably means that the wrong copy of the xml2-config shell script is\n"); + printf("*** being found. The easiest way to fix this is to remove the old version\n"); + printf("*** of LIBXML, but you can also set the XML2_CONFIG environment to point to the\n"); + printf("*** correct copy of xml2-config. (In this case, you will have to\n"); + printf("*** modify your LD_LIBRARY_PATH enviroment variable, or edit /etc/ld.so.conf\n"); + printf("*** so that the correct libraries are found at run-time))\n"); + } + return 1; +} + +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + +else + no_xml=yes +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +fi + + CPPFLAGS="$ac_save_CPPFLAGS" + LIBS="$ac_save_LIBS" + fi + fi + + if test "x$no_xml" = x ; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes (version $xml_config_major_version.$xml_config_minor_version.$xml_config_micro_version)" >&5 +$as_echo "yes (version $xml_config_major_version.$xml_config_minor_version.$xml_config_micro_version)" >&6; } + have_libxml2=yes + else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + if test "$XML2_CONFIG" = "no" ; then + echo "*** The xml2-config script installed by LIBXML could not be found" + echo "*** If libxml was installed in PREFIX, make sure PREFIX/bin is in" + echo "*** your path, or set the XML2_CONFIG environment variable to the" + echo "*** full path to xml2-config." + else + if test -f conf.xmltest ; then + : + else + echo "*** Could not run libxml test program, checking why..." + CPPFLAGS="$CPPFLAGS $XML_CPPFLAGS" + LIBS="$LIBS $XML_LIBS" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#include +#include + +int +main () +{ + LIBXML_TEST_VERSION; return 0; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + echo "*** The test program compiled, but did not run. This usually means" + echo "*** that the run-time linker is not finding LIBXML or finding the wrong" + echo "*** version of LIBXML. If it is not finding LIBXML, you'll need to set your" + echo "*** LD_LIBRARY_PATH environment variable, or edit /etc/ld.so.conf to point" + echo "*** to the installed location Also, make sure you have run ldconfig if that" + echo "*** is required on your system" + echo "***" + echo "*** If you have an old version installed, it is best to remove it, although" + echo "*** you may also be able to get things to work by modifying LD_LIBRARY_PATH" +else + echo "*** The test program failed to compile or link. See the file config.log for the" + echo "*** exact error that occured. This usually means LIBXML was incorrectly installed" + echo "*** or that you have moved LIBXML since it was installed. In the latter case, you" + echo "*** may want to edit the xml2-config script: $XML2_CONFIG" +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + CPPFLAGS="$ac_save_CPPFLAGS" + LIBS="$ac_save_LIBS" + fi + fi + + XML_CPPFLAGS="" + XML_LIBS="" + have_libxml2=no + fi + + + rm -f conf.xmltest + + if test "x${have_libxml2}" = "xyes"; then + +$as_echo "#define HAVE_LIBXML2 1" >>confdefs.h + + fi +fi + +if test "x${request_libxml2}" = "xyes" && + test "x${have_libxml2}" != "xyes"; then + as_fn_error $? "libxml2 was requested (--with-libxml2) but not found" "$LINENO" 5 +fi + + if test "x${have_libxml2}" = "xyes" ; then + HAVE_LIBXML2_TRUE= + HAVE_LIBXML2_FALSE='#' +else + HAVE_LIBXML2_TRUE='#' + HAVE_LIBXML2_FALSE= +fi + + +# jemalloc +have_jemalloc=no +if test "x${request_jemalloc}" != "xno"; then + LIBS_OLD=$LIBS + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing malloc_stats_print" >&5 +$as_echo_n "checking for library containing malloc_stats_print... " >&6; } +if ${ac_cv_search_malloc_stats_print+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_func_search_save_LIBS=$LIBS +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char malloc_stats_print (); +int +main () +{ +return malloc_stats_print (); + ; + return 0; +} +_ACEOF +for ac_lib in '' jemalloc; do + if test -z "$ac_lib"; then + ac_res="none required" + else + ac_res=-l$ac_lib + LIBS="-l$ac_lib $PTHREAD_LDFLAGS $ac_func_search_save_LIBS" + fi + if ac_fn_c_try_link "$LINENO"; then : + ac_cv_search_malloc_stats_print=$ac_res +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext + if ${ac_cv_search_malloc_stats_print+:} false; then : + break +fi +done +if ${ac_cv_search_malloc_stats_print+:} false; then : + +else + ac_cv_search_malloc_stats_print=no +fi +rm conftest.$ac_ext +LIBS=$ac_func_search_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_malloc_stats_print" >&5 +$as_echo "$ac_cv_search_malloc_stats_print" >&6; } +ac_res=$ac_cv_search_malloc_stats_print +if test "$ac_res" != no; then : + test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" + have_jemalloc=yes +fi + + LIBS=$LIBS_OLD + + if test "x${have_jemalloc}" = "xyes"; then + jemalloc_libs=${ac_cv_search_malloc_stats_print} + else + # On Darwin, malloc_stats_print is je_malloc_stats_print + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing je_malloc_stats_print" >&5 +$as_echo_n "checking for library containing je_malloc_stats_print... " >&6; } +if ${ac_cv_search_je_malloc_stats_print+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_func_search_save_LIBS=$LIBS +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char je_malloc_stats_print (); +int +main () +{ +return je_malloc_stats_print (); + ; + return 0; +} +_ACEOF +for ac_lib in '' jemalloc; do + if test -z "$ac_lib"; then + ac_res="none required" + else + ac_res=-l$ac_lib + LIBS="-l$ac_lib $PTHREAD_LDFLAGS $ac_func_search_save_LIBS" + fi + if ac_fn_c_try_link "$LINENO"; then : + ac_cv_search_je_malloc_stats_print=$ac_res +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext + if ${ac_cv_search_je_malloc_stats_print+:} false; then : + break +fi +done +if ${ac_cv_search_je_malloc_stats_print+:} false; then : + +else + ac_cv_search_je_malloc_stats_print=no +fi +rm conftest.$ac_ext +LIBS=$ac_func_search_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_je_malloc_stats_print" >&5 +$as_echo "$ac_cv_search_je_malloc_stats_print" >&6; } +ac_res=$ac_cv_search_je_malloc_stats_print +if test "$ac_res" != no; then : + test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" + have_jemalloc=yes +fi + + LIBS=$LIBS_OLD + + if test "x${have_jemalloc}" = "xyes"; then + jemalloc_libs=${ac_cv_search_je_malloc_stats_print} + fi + fi + + if test "x${have_jemalloc}" = "xyes" && + test "x${jemalloc_libs}" != "xnone required"; then + JEMALLOC_LIBS=${jemalloc_libs} + + fi +fi + +if test "x${request_jemalloc}" = "xyes" && + test "x${have_jemalloc}" != "xyes"; then + as_fn_error $? "jemalloc was requested (--with-jemalloc) but not found" "$LINENO" 5 +fi + +# spdylay (for src/nghttpx and src/h2load) +have_spdylay=no +if test "x${request_spdylay}" != "xno"; then + +pkg_failed=no +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for LIBSPDYLAY" >&5 +$as_echo_n "checking for LIBSPDYLAY... " >&6; } + +if test -n "$LIBSPDYLAY_CFLAGS"; then + pkg_cv_LIBSPDYLAY_CFLAGS="$LIBSPDYLAY_CFLAGS" + elif test -n "$PKG_CONFIG"; then + if test -n "$PKG_CONFIG" && \ + { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libspdylay >= 1.3.2\""; } >&5 + ($PKG_CONFIG --exists --print-errors "libspdylay >= 1.3.2") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then + pkg_cv_LIBSPDYLAY_CFLAGS=`$PKG_CONFIG --cflags "libspdylay >= 1.3.2" 2>/dev/null` + test "x$?" != "x0" && pkg_failed=yes +else + pkg_failed=yes +fi + else + pkg_failed=untried +fi +if test -n "$LIBSPDYLAY_LIBS"; then + pkg_cv_LIBSPDYLAY_LIBS="$LIBSPDYLAY_LIBS" + elif test -n "$PKG_CONFIG"; then + if test -n "$PKG_CONFIG" && \ + { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libspdylay >= 1.3.2\""; } >&5 + ($PKG_CONFIG --exists --print-errors "libspdylay >= 1.3.2") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then + pkg_cv_LIBSPDYLAY_LIBS=`$PKG_CONFIG --libs "libspdylay >= 1.3.2" 2>/dev/null` + test "x$?" != "x0" && pkg_failed=yes +else + pkg_failed=yes +fi + else + pkg_failed=untried +fi + + + +if test $pkg_failed = yes; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + +if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then + _pkg_short_errors_supported=yes +else + _pkg_short_errors_supported=no +fi + if test $_pkg_short_errors_supported = yes; then + LIBSPDYLAY_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "libspdylay >= 1.3.2" 2>&1` + else + LIBSPDYLAY_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "libspdylay >= 1.3.2" 2>&1` + fi + # Put the nasty error message in config.log where it belongs + echo "$LIBSPDYLAY_PKG_ERRORS" >&5 + + have_spdylay=no +elif test $pkg_failed = untried; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + have_spdylay=no +else + LIBSPDYLAY_CFLAGS=$pkg_cv_LIBSPDYLAY_CFLAGS + LIBSPDYLAY_LIBS=$pkg_cv_LIBSPDYLAY_LIBS + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + have_spdylay=yes +fi + if test "x${have_spdylay}" = "xyes"; then + +$as_echo "#define HAVE_SPDYLAY 1" >>confdefs.h + + else + { $as_echo "$as_me:${as_lineno-$LINENO}: $LIBSPDYLAY_PKG_ERRORS" >&5 +$as_echo "$as_me: $LIBSPDYLAY_PKG_ERRORS" >&6;} + { $as_echo "$as_me:${as_lineno-$LINENO}: The SPDY support in nghttpx and h2load will be disabled." >&5 +$as_echo "$as_me: The SPDY support in nghttpx and h2load will be disabled." >&6;} + fi +fi + +if test "x${request_spdylay}" = "xyes" && + test "x${have_spdylay}" != "xyes"; then + as_fn_error $? "spdylay was requested (--with-spdylay) but not found" "$LINENO" 5 +fi + + if test "x${have_spdylay}" = "xyes" ; then + HAVE_SPDYLAY_TRUE= + HAVE_SPDYLAY_FALSE='#' +else + HAVE_SPDYLAY_TRUE='#' + HAVE_SPDYLAY_FALSE= +fi + + +# Check Boost Asio library +have_asio_lib=no + +if test "x${request_asio_lib}" = "xyes"; then + + +# Check whether --with-boost was given. +if test "${with_boost+set}" = set; then : + withval=$with_boost; + if test "$withval" = "no"; then + want_boost="no" + elif test "$withval" = "yes"; then + want_boost="yes" + ac_boost_path="" + else + want_boost="yes" + ac_boost_path="$withval" + fi + +else + want_boost="yes" +fi + + + + +# Check whether --with-boost-libdir was given. +if test "${with_boost_libdir+set}" = set; then : + withval=$with_boost_libdir; + if test -d "$withval" + then + ac_boost_lib_path="$withval" + else + as_fn_error $? "--with-boost-libdir expected directory name" "$LINENO" 5 + fi + +else + ac_boost_lib_path="" + +fi + + +if test "x$want_boost" = "xyes"; then + boost_lib_version_req=1.54.0 + boost_lib_version_req_shorten=`expr $boost_lib_version_req : '\([0-9]*\.[0-9]*\)'` + boost_lib_version_req_major=`expr $boost_lib_version_req : '\([0-9]*\)'` + boost_lib_version_req_minor=`expr $boost_lib_version_req : '[0-9]*\.\([0-9]*\)'` + boost_lib_version_req_sub_minor=`expr $boost_lib_version_req : '[0-9]*\.[0-9]*\.\([0-9]*\)'` + if test "x$boost_lib_version_req_sub_minor" = "x" ; then + boost_lib_version_req_sub_minor="0" + fi + WANT_BOOST_VERSION=`expr $boost_lib_version_req_major \* 100000 \+ $boost_lib_version_req_minor \* 100 \+ $boost_lib_version_req_sub_minor` + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for boostlib >= $boost_lib_version_req" >&5 +$as_echo_n "checking for boostlib >= $boost_lib_version_req... " >&6; } + succeeded=no + + libsubdirs="lib" + ax_arch=`uname -m` + case $ax_arch in + x86_64) + libsubdirs="lib64 libx32 lib lib64" + ;; + ppc64|s390x|sparc64|aarch64|ppc64le) + libsubdirs="lib64 lib lib64 ppc64le" + ;; + esac + + + libsubdirs="lib/${host_cpu}-${host_os} $libsubdirs" + + case ${host_cpu} in + i?86) + libsubdirs="lib/i386-${host_os} $libsubdirs" + ;; + esac + + if test "$ac_boost_path" != ""; then + BOOST_CPPFLAGS="-I$ac_boost_path/include" + for ac_boost_path_tmp in $libsubdirs; do + if test -d "$ac_boost_path"/"$ac_boost_path_tmp" ; then + BOOST_LDFLAGS="-L$ac_boost_path/$ac_boost_path_tmp" + break + fi + done + elif test "$cross_compiling" != yes; then + for ac_boost_path_tmp in /usr /usr/local /opt /opt/local ; do + if test -d "$ac_boost_path_tmp/include/boost" && test -r "$ac_boost_path_tmp/include/boost"; then + for libsubdir in $libsubdirs ; do + if ls "$ac_boost_path_tmp/$libsubdir/libboost_"* >/dev/null 2>&1 ; then break; fi + done + BOOST_LDFLAGS="-L$ac_boost_path_tmp/$libsubdir" + BOOST_CPPFLAGS="-I$ac_boost_path_tmp/include" + break; + fi + done + fi + + if test "$ac_boost_lib_path" != ""; then + BOOST_LDFLAGS="-L$ac_boost_lib_path" + fi + + CPPFLAGS_SAVED="$CPPFLAGS" + CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS" + export CPPFLAGS + + LDFLAGS_SAVED="$LDFLAGS" + LDFLAGS="$LDFLAGS $BOOST_LDFLAGS" + export LDFLAGS + + + ac_ext=cpp +ac_cpp='$CXXCPP $CPPFLAGS' +ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_cxx_compiler_gnu + + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + + #include + +int +main () +{ + + #if BOOST_VERSION >= $WANT_BOOST_VERSION + // Everything is okay + #else + # error Boost version is too old + #endif + + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_compile "$LINENO"; then : + + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + succeeded=yes + found_system=yes + +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + + + + if test "x$succeeded" != "xyes"; then + _version=0 + if test "$ac_boost_path" != ""; then + if test -d "$ac_boost_path" && test -r "$ac_boost_path"; then + for i in `ls -d $ac_boost_path/include/boost-* 2>/dev/null`; do + _version_tmp=`echo $i | sed "s#$ac_boost_path##" | sed 's/\/include\/boost-//' | sed 's/_/./'` + V_CHECK=`expr $_version_tmp \> $_version` + if test "$V_CHECK" = "1" ; then + _version=$_version_tmp + fi + VERSION_UNDERSCORE=`echo $_version | sed 's/\./_/'` + BOOST_CPPFLAGS="-I$ac_boost_path/include/boost-$VERSION_UNDERSCORE" + done + fi + else + if test "$cross_compiling" != yes; then + for ac_boost_path in /usr /usr/local /opt /opt/local ; do + if test -d "$ac_boost_path" && test -r "$ac_boost_path"; then + for i in `ls -d $ac_boost_path/include/boost-* 2>/dev/null`; do + _version_tmp=`echo $i | sed "s#$ac_boost_path##" | sed 's/\/include\/boost-//' | sed 's/_/./'` + V_CHECK=`expr $_version_tmp \> $_version` + if test "$V_CHECK" = "1" ; then + _version=$_version_tmp + best_path=$ac_boost_path + fi + done + fi + done + + VERSION_UNDERSCORE=`echo $_version | sed 's/\./_/'` + BOOST_CPPFLAGS="-I$best_path/include/boost-$VERSION_UNDERSCORE" + if test "$ac_boost_lib_path" = ""; then + for libsubdir in $libsubdirs ; do + if ls "$best_path/$libsubdir/libboost_"* >/dev/null 2>&1 ; then break; fi + done + BOOST_LDFLAGS="-L$best_path/$libsubdir" + fi + fi + + if test "x$BOOST_ROOT" != "x"; then + for libsubdir in $libsubdirs ; do + if ls "$BOOST_ROOT/stage/$libsubdir/libboost_"* >/dev/null 2>&1 ; then break; fi + done + if test -d "$BOOST_ROOT" && test -r "$BOOST_ROOT" && test -d "$BOOST_ROOT/stage/$libsubdir" && test -r "$BOOST_ROOT/stage/$libsubdir"; then + version_dir=`expr //$BOOST_ROOT : '.*/\(.*\)'` + stage_version=`echo $version_dir | sed 's/boost_//' | sed 's/_/./g'` + stage_version_shorten=`expr $stage_version : '\([0-9]*\.[0-9]*\)'` + V_CHECK=`expr $stage_version_shorten \>\= $_version` + if test "$V_CHECK" = "1" -a "$ac_boost_lib_path" = "" ; then + { $as_echo "$as_me:${as_lineno-$LINENO}: We will use a staged boost library from $BOOST_ROOT" >&5 +$as_echo "$as_me: We will use a staged boost library from $BOOST_ROOT" >&6;} + BOOST_CPPFLAGS="-I$BOOST_ROOT" + BOOST_LDFLAGS="-L$BOOST_ROOT/stage/$libsubdir" + fi + fi + fi + fi + + CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS" + export CPPFLAGS + LDFLAGS="$LDFLAGS $BOOST_LDFLAGS" + export LDFLAGS + + ac_ext=cpp +ac_cpp='$CXXCPP $CPPFLAGS' +ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_cxx_compiler_gnu + + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + + #include + +int +main () +{ + + #if BOOST_VERSION >= $WANT_BOOST_VERSION + // Everything is okay + #else + # error Boost version is too old + #endif + + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_compile "$LINENO"; then : + + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + succeeded=yes + found_system=yes + +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + fi + + if test "$succeeded" != "yes" ; then + if test "$_version" = "0" ; then + { $as_echo "$as_me:${as_lineno-$LINENO}: We could not detect the boost libraries (version $boost_lib_version_req_shorten or higher). If you have a staged boost library (still not installed) please specify \$BOOST_ROOT in your environment and do not give a PATH to --with-boost option. If you are sure you have boost installed, then check your version number looking in . See http://randspringer.de/boost for more documentation." >&5 +$as_echo "$as_me: We could not detect the boost libraries (version $boost_lib_version_req_shorten or higher). If you have a staged boost library (still not installed) please specify \$BOOST_ROOT in your environment and do not give a PATH to --with-boost option. If you are sure you have boost installed, then check your version number looking in . See http://randspringer.de/boost for more documentation." >&6;} + else + { $as_echo "$as_me:${as_lineno-$LINENO}: Your boost libraries seems to old (version $_version)." >&5 +$as_echo "$as_me: Your boost libraries seems to old (version $_version)." >&6;} + fi + # execute ACTION-IF-NOT-FOUND (if present): + have_boost_base=no + else + + + +$as_echo "#define HAVE_BOOST /**/" >>confdefs.h + + # execute ACTION-IF-FOUND (if present): + have_boost_base=yes + fi + + CPPFLAGS="$CPPFLAGS_SAVED" + LDFLAGS="$LDFLAGS_SAVED" +fi + + + + if test "x${have_boost_base}" = "xyes"; then + + +# Check whether --with-boost-asio was given. +if test "${with_boost_asio+set}" = set; then : + withval=$with_boost_asio; + if test "$withval" = "no"; then + want_boost="no" + elif test "$withval" = "yes"; then + want_boost="yes" + ax_boost_user_asio_lib="" + else + want_boost="yes" + ax_boost_user_asio_lib="$withval" + fi + +else + want_boost="yes" + +fi + + + if test "x$want_boost" = "xyes"; then + + CPPFLAGS_SAVED="$CPPFLAGS" + CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS" + export CPPFLAGS + + LDFLAGS_SAVED="$LDFLAGS" + LDFLAGS="$LDFLAGS $BOOST_LDFLAGS" + export LDFLAGS + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the Boost::ASIO library is available" >&5 +$as_echo_n "checking whether the Boost::ASIO library is available... " >&6; } +if ${ax_cv_boost_asio+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_ext=cpp +ac_cpp='$CXXCPP $CPPFLAGS' +ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_cxx_compiler_gnu + + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + #include + +int +main () +{ + + + boost::asio::io_service io; + boost::system::error_code timer_result; + boost::asio::deadline_timer t(io); + t.cancel(); + io.run_one(); + return 0; + + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_compile "$LINENO"; then : + ax_cv_boost_asio=yes +else + ax_cv_boost_asio=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_boost_asio" >&5 +$as_echo "$ax_cv_boost_asio" >&6; } + if test "x$ax_cv_boost_asio" = "xyes"; then + +$as_echo "#define HAVE_BOOST_ASIO /**/" >>confdefs.h + + BN=boost_system + BOOSTLIBDIR=`echo $BOOST_LDFLAGS | sed -e 's/[^\/]*//'` + if test "x$ax_boost_user_asio_lib" = "x"; then + for ax_lib in `ls $BOOSTLIBDIR/libboost_system*.so* $BOOSTLIBDIR/libboost_system*.dylib* $BOOSTLIBDIR/libboost_system*.a* 2>/dev/null | sed 's,.*/,,' | sed -e 's;^lib\(boost_system.*\)\.so.*$;\1;' -e 's;^lib\(boost_system.*\)\.dylib.*$;\1;' -e 's;^lib\(boost_system.*\)\.a.*$;\1;' ` ; do + as_ac_Lib=`$as_echo "ac_cv_lib_$ax_lib''_main" | $as_tr_sh` +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for main in -l$ax_lib" >&5 +$as_echo_n "checking for main in -l$ax_lib... " >&6; } +if eval \${$as_ac_Lib+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-l$ax_lib $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + + +int +main () +{ +return main (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + eval "$as_ac_Lib=yes" +else + eval "$as_ac_Lib=no" +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +eval ac_res=\$$as_ac_Lib + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } +if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : + BOOST_ASIO_LIB="-l$ax_lib" link_thread="yes" break +else + link_thread="no" +fi + + done + else + for ax_lib in $ax_boost_user_asio_lib $BN-$ax_boost_user_asio_lib; do + as_ac_Lib=`$as_echo "ac_cv_lib_$ax_lib''_main" | $as_tr_sh` +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for main in -l$ax_lib" >&5 +$as_echo_n "checking for main in -l$ax_lib... " >&6; } +if eval \${$as_ac_Lib+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-l$ax_lib $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + + +int +main () +{ +return main (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + eval "$as_ac_Lib=yes" +else + eval "$as_ac_Lib=no" +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +eval ac_res=\$$as_ac_Lib + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } +if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : + BOOST_ASIO_LIB="-l$ax_lib" link_asio="yes" break +else + link_asio="no" +fi + + done + + fi + if test "x$ax_lib" = "x"; then + as_fn_error $? "Could not find a version of the library!" "$LINENO" 5 + fi + if test "x$link_asio" = "xno"; then + as_fn_error $? "Could not link against $ax_lib !" "$LINENO" 5 + fi + fi + + CPPFLAGS="$CPPFLAGS_SAVED" + LDFLAGS="$LDFLAGS_SAVED" + fi + + + +# Check whether --with-boost-system was given. +if test "${with_boost_system+set}" = set; then : + withval=$with_boost_system; + if test "$withval" = "no"; then + want_boost="no" + elif test "$withval" = "yes"; then + want_boost="yes" + ax_boost_user_system_lib="" + else + want_boost="yes" + ax_boost_user_system_lib="$withval" + fi + +else + want_boost="yes" + +fi + + + if test "x$want_boost" = "xyes"; then + + + CPPFLAGS_SAVED="$CPPFLAGS" + CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS" + export CPPFLAGS + + LDFLAGS_SAVED="$LDFLAGS" + LDFLAGS="$LDFLAGS $BOOST_LDFLAGS" + export LDFLAGS + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the Boost::System library is available" >&5 +$as_echo_n "checking whether the Boost::System library is available... " >&6; } +if ${ax_cv_boost_system+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_ext=cpp +ac_cpp='$CXXCPP $CPPFLAGS' +ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_cxx_compiler_gnu + + CXXFLAGS_SAVE=$CXXFLAGS + + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +int +main () +{ +boost::system::system_category + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_compile "$LINENO"; then : + ax_cv_boost_system=yes +else + ax_cv_boost_system=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + CXXFLAGS=$CXXFLAGS_SAVE + ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_boost_system" >&5 +$as_echo "$ax_cv_boost_system" >&6; } + if test "x$ax_cv_boost_system" = "xyes"; then + + + +$as_echo "#define HAVE_BOOST_SYSTEM /**/" >>confdefs.h + + BOOSTLIBDIR=`echo $BOOST_LDFLAGS | sed -e 's/[^\/]*//'` + + LDFLAGS_SAVE=$LDFLAGS + if test "x$ax_boost_user_system_lib" = "x"; then + for libextension in `ls -r $BOOSTLIBDIR/libboost_system* 2>/dev/null | sed 's,.*/lib,,' | sed 's,\..*,,'` ; do + ax_lib=${libextension} + as_ac_Lib=`$as_echo "ac_cv_lib_$ax_lib''_exit" | $as_tr_sh` +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for exit in -l$ax_lib" >&5 +$as_echo_n "checking for exit in -l$ax_lib... " >&6; } +if eval \${$as_ac_Lib+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-l$ax_lib $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char exit (); +int +main () +{ +return exit (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + eval "$as_ac_Lib=yes" +else + eval "$as_ac_Lib=no" +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +eval ac_res=\$$as_ac_Lib + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } +if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : + BOOST_SYSTEM_LIB="-l$ax_lib"; link_system="yes"; break +else + link_system="no" +fi + + done + if test "x$link_system" != "xyes"; then + for libextension in `ls -r $BOOSTLIBDIR/boost_system* 2>/dev/null | sed 's,.*/,,' | sed -e 's,\..*,,'` ; do + ax_lib=${libextension} + as_ac_Lib=`$as_echo "ac_cv_lib_$ax_lib''_exit" | $as_tr_sh` +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for exit in -l$ax_lib" >&5 +$as_echo_n "checking for exit in -l$ax_lib... " >&6; } +if eval \${$as_ac_Lib+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-l$ax_lib $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char exit (); +int +main () +{ +return exit (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + eval "$as_ac_Lib=yes" +else + eval "$as_ac_Lib=no" +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +eval ac_res=\$$as_ac_Lib + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } +if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : + BOOST_SYSTEM_LIB="-l$ax_lib"; link_system="yes"; break +else + link_system="no" +fi + + done + fi + + else + for ax_lib in $ax_boost_user_system_lib boost_system-$ax_boost_user_system_lib; do + as_ac_Lib=`$as_echo "ac_cv_lib_$ax_lib''_exit" | $as_tr_sh` +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for exit in -l$ax_lib" >&5 +$as_echo_n "checking for exit in -l$ax_lib... " >&6; } +if eval \${$as_ac_Lib+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-l$ax_lib $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char exit (); +int +main () +{ +return exit (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + eval "$as_ac_Lib=yes" +else + eval "$as_ac_Lib=no" +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +eval ac_res=\$$as_ac_Lib + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } +if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : + BOOST_SYSTEM_LIB="-l$ax_lib"; link_system="yes"; break +else + link_system="no" +fi + + done + + fi + if test "x$ax_lib" = "x"; then + as_fn_error $? "Could not find a version of the library!" "$LINENO" 5 + fi + if test "x$link_system" = "xno"; then + as_fn_error $? "Could not link against $ax_lib !" "$LINENO" 5 + fi + fi + + CPPFLAGS="$CPPFLAGS_SAVED" + LDFLAGS="$LDFLAGS_SAVED" + fi + + + +# Check whether --with-boost-thread was given. +if test "${with_boost_thread+set}" = set; then : + withval=$with_boost_thread; + if test "$withval" = "no"; then + want_boost="no" + elif test "$withval" = "yes"; then + want_boost="yes" + ax_boost_user_thread_lib="" + else + want_boost="yes" + ax_boost_user_thread_lib="$withval" + fi + +else + want_boost="yes" + +fi + + + if test "x$want_boost" = "xyes"; then + + + CPPFLAGS_SAVED="$CPPFLAGS" + CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS" + export CPPFLAGS + + LDFLAGS_SAVED="$LDFLAGS" + LDFLAGS="$LDFLAGS $BOOST_LDFLAGS" + export LDFLAGS + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the Boost::Thread library is available" >&5 +$as_echo_n "checking whether the Boost::Thread library is available... " >&6; } +if ${ax_cv_boost_thread+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_ext=cpp +ac_cpp='$CXXCPP $CPPFLAGS' +ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_cxx_compiler_gnu + + CXXFLAGS_SAVE=$CXXFLAGS + + if test "x$host_os" = "xsolaris" ; then + CXXFLAGS="-pthreads $CXXFLAGS" + elif test "x$host_os" = "xmingw32" ; then + CXXFLAGS="-mthreads $CXXFLAGS" + else + CXXFLAGS="-pthread $CXXFLAGS" + fi + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +int +main () +{ +boost::thread_group thrds; + return 0; + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_compile "$LINENO"; then : + ax_cv_boost_thread=yes +else + ax_cv_boost_thread=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + CXXFLAGS=$CXXFLAGS_SAVE + ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_boost_thread" >&5 +$as_echo "$ax_cv_boost_thread" >&6; } + if test "x$ax_cv_boost_thread" = "xyes"; then + if test "x$host_os" = "xsolaris" ; then + BOOST_CPPFLAGS="-pthreads $BOOST_CPPFLAGS" + elif test "x$host_os" = "xmingw32" ; then + BOOST_CPPFLAGS="-mthreads $BOOST_CPPFLAGS" + else + BOOST_CPPFLAGS="-pthread $BOOST_CPPFLAGS" + fi + + + + +$as_echo "#define HAVE_BOOST_THREAD /**/" >>confdefs.h + + BOOSTLIBDIR=`echo $BOOST_LDFLAGS | sed -e 's/[^\/]*//'` + + LDFLAGS_SAVE=$LDFLAGS + case "x$host_os" in + *bsd* ) + LDFLAGS="-pthread $LDFLAGS" + break; + ;; + esac + if test "x$ax_boost_user_thread_lib" = "x"; then + for libextension in `ls -r $BOOSTLIBDIR/libboost_thread* 2>/dev/null | sed 's,.*/lib,,' | sed 's,\..*,,'`; do + ax_lib=${libextension} + as_ac_Lib=`$as_echo "ac_cv_lib_$ax_lib''_exit" | $as_tr_sh` +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for exit in -l$ax_lib" >&5 +$as_echo_n "checking for exit in -l$ax_lib... " >&6; } +if eval \${$as_ac_Lib+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-l$ax_lib $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char exit (); +int +main () +{ +return exit (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + eval "$as_ac_Lib=yes" +else + eval "$as_ac_Lib=no" +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +eval ac_res=\$$as_ac_Lib + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } +if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : + BOOST_THREAD_LIB="-l$ax_lib"; link_thread="yes"; break +else + link_thread="no" +fi + + done + if test "x$link_thread" != "xyes"; then + for libextension in `ls -r $BOOSTLIBDIR/boost_thread* 2>/dev/null | sed 's,.*/,,' | sed 's,\..*,,'`; do + ax_lib=${libextension} + as_ac_Lib=`$as_echo "ac_cv_lib_$ax_lib''_exit" | $as_tr_sh` +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for exit in -l$ax_lib" >&5 +$as_echo_n "checking for exit in -l$ax_lib... " >&6; } +if eval \${$as_ac_Lib+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-l$ax_lib $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char exit (); +int +main () +{ +return exit (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + eval "$as_ac_Lib=yes" +else + eval "$as_ac_Lib=no" +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +eval ac_res=\$$as_ac_Lib + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } +if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : + BOOST_THREAD_LIB="-l$ax_lib"; link_thread="yes"; break +else + link_thread="no" +fi + + done + fi + + else + for ax_lib in $ax_boost_user_thread_lib boost_thread-$ax_boost_user_thread_lib; do + as_ac_Lib=`$as_echo "ac_cv_lib_$ax_lib''_exit" | $as_tr_sh` +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for exit in -l$ax_lib" >&5 +$as_echo_n "checking for exit in -l$ax_lib... " >&6; } +if eval \${$as_ac_Lib+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-l$ax_lib $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char exit (); +int +main () +{ +return exit (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + eval "$as_ac_Lib=yes" +else + eval "$as_ac_Lib=no" +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +eval ac_res=\$$as_ac_Lib + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } +if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : + BOOST_THREAD_LIB="-l$ax_lib"; link_thread="yes"; break +else + link_thread="no" +fi + + done + + fi + if test "x$ax_lib" = "x"; then + as_fn_error $? "Could not find a version of the library!" "$LINENO" 5 + fi + if test "x$link_thread" = "xno"; then + as_fn_error $? "Could not link against $ax_lib !" "$LINENO" 5 + else + case "x$host_os" in + *bsd* ) + BOOST_LDFLAGS="-pthread $BOOST_LDFLAGS" + break; + ;; + esac + + fi + fi + + CPPFLAGS="$CPPFLAGS_SAVED" + LDFLAGS="$LDFLAGS_SAVED" + fi + + + if test "x${ax_cv_boost_asio}" = "xyes" && + test "x${ax_cv_boost_system}" = "xyes" && + test "x${ax_cv_boost_thread}" = "xyes"; then + have_asio_lib=yes + fi + fi +fi + +# The nghttp, nghttpd and nghttpx under src depend on zlib, OpenSSL +# and libev +enable_app=no +if test "x${request_app}" != "xno" && + test "x${have_zlib}" = "xyes" && + test "x${have_openssl}" = "xyes" && + test "x${have_libev}" = "xyes"; then + enable_app=yes +fi + +if test "x${request_app}" = "xyes" && + test "x${enable_app}" != "xyes"; then + as_fn_error $? "applications were requested (--enable-app) but dependencies are not met." "$LINENO" 5 +fi + + if test "x${enable_app}" = "xyes" ; then + ENABLE_APP_TRUE= + ENABLE_APP_FALSE='#' +else + ENABLE_APP_TRUE='#' + ENABLE_APP_FALSE= +fi + + +enable_hpack_tools=no +# HPACK tools requires jansson +if test "x${request_hpack_tools}" != "xno" && + test "x${have_jansson}" = "xyes"; then + enable_hpack_tools=yes +fi + +if test "x${request_hpack_tools}" = "xyes" && + test "x${enable_hpack_tools}" != "xyes"; then + as_fn_error $? "HPACK tools were requested (--enable-hpack-tools) but dependencies are not met." "$LINENO" 5 +fi + + if test "x${enable_hpack_tools}" = "xyes" ; then + ENABLE_HPACK_TOOLS_TRUE= + ENABLE_HPACK_TOOLS_FALSE='#' +else + ENABLE_HPACK_TOOLS_TRUE='#' + ENABLE_HPACK_TOOLS_FALSE= +fi + + +# C++ library libnghttp2_asio + +enable_asio_lib=no +if test "x${request_asio_lib}" != "xno" && + test "x${have_asio_lib}" = "xyes"; then + enable_asio_lib=yes +fi + + if test "x${enable_asio_lib}" = "xyes" ; then + ENABLE_ASIO_LIB_TRUE= + ENABLE_ASIO_LIB_FALSE='#' +else + ENABLE_ASIO_LIB_TRUE='#' + ENABLE_ASIO_LIB_FALSE= +fi + + +# The example programs depend on OpenSSL and libevent_openssl +enable_examples=no +if test "x${request_examples}" != "xno" && + test "x${have_openssl}" = "xyes" && + test "x${have_libevent_openssl}" = "xyes"; then + enable_examples=yes +fi + +if test "x${request_examples}" = "xyes" && + test "x${enable_examples}" != "xyes"; then + as_fn_error $? "examples were requested (--enable-examples) but dependencies are not met." "$LINENO" 5 +fi + + if test "x${enable_examples}" = "xyes" ; then + ENABLE_EXAMPLES_TRUE= + ENABLE_EXAMPLES_FALSE='#' +else + ENABLE_EXAMPLES_TRUE='#' + ENABLE_EXAMPLES_FALSE= +fi + + +# Python bindings +enable_python_bindings=no +if test "x${request_python_bindings}" != "xno" && + test "x${CYTHON}" != "x" && + test "x${PYTHON}" != "x:" && + test "x${have_python_dev}" = "xyes"; then + enable_python_bindings=yes +fi + +if test "x${request_python_bindings}" = "xyes" && + test "x${enable_python_bindings}" != "xyes"; then + as_fn_error $? "python bindings were requested (--enable-python-bindings) but dependencies are not met." "$LINENO" 5 +fi + + if test "x${enable_python_bindings}" = "xyes"; then + ENABLE_PYTHON_BINDINGS_TRUE= + ENABLE_PYTHON_BINDINGS_FALSE='#' +else + ENABLE_PYTHON_BINDINGS_TRUE='#' + ENABLE_PYTHON_BINDINGS_FALSE= +fi + + +# Produce cython conditional, so that we can distribute generated C +# source + if test "x${CYTHON}" != "x"; then + HAVE_CYTHON_TRUE= + HAVE_CYTHON_FALSE='#' +else + HAVE_CYTHON_TRUE='#' + HAVE_CYTHON_FALSE= +fi + + +# failmalloc tests +enable_failmalloc=no +if test "x${request_failmalloc}" = "xyes"; then + enable_failmalloc=yes +fi + + if test "x${enable_failmalloc}" = "xyes" ; then + ENABLE_FAILMALLOC_TRUE= + ENABLE_FAILMALLOC_FALSE='#' +else + ENABLE_FAILMALLOC_TRUE='#' + ENABLE_FAILMALLOC_FALSE= +fi + + +# Checks for header files. + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to enable assertions" >&5 +$as_echo_n "checking whether to enable assertions... " >&6; } + # Check whether --enable-assert was given. +if test "${enable_assert+set}" = set; then : + enableval=$enable_assert; ac_enable_assert=$enableval + if test "x$enableval" = xno; then : + +$as_echo "#define NDEBUG 1" >>confdefs.h + +elif test "x$enableval" != xyes; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: invalid argument supplied to --enable-assert" >&5 +$as_echo "$as_me: WARNING: invalid argument supplied to --enable-assert" >&2;} + ac_enable_assert=yes +fi +else + ac_enable_assert=yes +fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_enable_assert" >&5 +$as_echo "$ac_enable_assert" >&6; } + +for ac_header in \ + arpa/inet.h \ + fcntl.h \ + inttypes.h \ + limits.h \ + netdb.h \ + netinet/in.h \ + pwd.h \ + stddef.h \ + stdint.h \ + stdlib.h \ + string.h \ + sys/socket.h \ + sys/time.h \ + syslog.h \ + time.h \ + unistd.h \ + +do : + as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` +ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default" +if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : + cat >>confdefs.h <<_ACEOF +#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + + +# Checks for typedefs, structures, and compiler characteristics. +ac_fn_c_check_type "$LINENO" "size_t" "ac_cv_type_size_t" "$ac_includes_default" +if test "x$ac_cv_type_size_t" = xyes; then : + +else + +cat >>confdefs.h <<_ACEOF +#define size_t unsigned int +_ACEOF + +fi + +ac_fn_c_check_type "$LINENO" "ssize_t" "ac_cv_type_ssize_t" "$ac_includes_default" +if test "x$ac_cv_type_ssize_t" = xyes; then : + +else + +cat >>confdefs.h <<_ACEOF +#define ssize_t int +_ACEOF + +fi + +ac_fn_c_find_uintX_t "$LINENO" "8" "ac_cv_c_uint8_t" +case $ac_cv_c_uint8_t in #( + no|yes) ;; #( + *) + +$as_echo "#define _UINT8_T 1" >>confdefs.h + + +cat >>confdefs.h <<_ACEOF +#define uint8_t $ac_cv_c_uint8_t +_ACEOF +;; + esac + +ac_fn_c_find_uintX_t "$LINENO" "16" "ac_cv_c_uint16_t" +case $ac_cv_c_uint16_t in #( + no|yes) ;; #( + *) + + +cat >>confdefs.h <<_ACEOF +#define uint16_t $ac_cv_c_uint16_t +_ACEOF +;; + esac + +ac_fn_c_find_uintX_t "$LINENO" "32" "ac_cv_c_uint32_t" +case $ac_cv_c_uint32_t in #( + no|yes) ;; #( + *) + +$as_echo "#define _UINT32_T 1" >>confdefs.h + + +cat >>confdefs.h <<_ACEOF +#define uint32_t $ac_cv_c_uint32_t +_ACEOF +;; + esac + +ac_fn_c_find_uintX_t "$LINENO" "64" "ac_cv_c_uint64_t" +case $ac_cv_c_uint64_t in #( + no|yes) ;; #( + *) + +$as_echo "#define _UINT64_T 1" >>confdefs.h + + +cat >>confdefs.h <<_ACEOF +#define uint64_t $ac_cv_c_uint64_t +_ACEOF +;; + esac + +ac_fn_c_find_intX_t "$LINENO" "8" "ac_cv_c_int8_t" +case $ac_cv_c_int8_t in #( + no|yes) ;; #( + *) + +cat >>confdefs.h <<_ACEOF +#define int8_t $ac_cv_c_int8_t +_ACEOF +;; +esac + +ac_fn_c_find_intX_t "$LINENO" "16" "ac_cv_c_int16_t" +case $ac_cv_c_int16_t in #( + no|yes) ;; #( + *) + +cat >>confdefs.h <<_ACEOF +#define int16_t $ac_cv_c_int16_t +_ACEOF +;; +esac + +ac_fn_c_find_intX_t "$LINENO" "32" "ac_cv_c_int32_t" +case $ac_cv_c_int32_t in #( + no|yes) ;; #( + *) + +cat >>confdefs.h <<_ACEOF +#define int32_t $ac_cv_c_int32_t +_ACEOF +;; +esac + +ac_fn_c_find_intX_t "$LINENO" "64" "ac_cv_c_int64_t" +case $ac_cv_c_int64_t in #( + no|yes) ;; #( + *) + +cat >>confdefs.h <<_ACEOF +#define int64_t $ac_cv_c_int64_t +_ACEOF +;; +esac + +ac_fn_c_check_type "$LINENO" "off_t" "ac_cv_type_off_t" "$ac_includes_default" +if test "x$ac_cv_type_off_t" = xyes; then : + +else + +cat >>confdefs.h <<_ACEOF +#define off_t long int +_ACEOF + +fi + +ac_fn_c_check_type "$LINENO" "pid_t" "ac_cv_type_pid_t" "$ac_includes_default" +if test "x$ac_cv_type_pid_t" = xyes; then : + +else + +cat >>confdefs.h <<_ACEOF +#define pid_t int +_ACEOF + +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for uid_t in sys/types.h" >&5 +$as_echo_n "checking for uid_t in sys/types.h... " >&6; } +if ${ac_cv_type_uid_t+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "uid_t" >/dev/null 2>&1; then : + ac_cv_type_uid_t=yes +else + ac_cv_type_uid_t=no +fi +rm -f conftest* + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_type_uid_t" >&5 +$as_echo "$ac_cv_type_uid_t" >&6; } +if test $ac_cv_type_uid_t = no; then + +$as_echo "#define uid_t int" >>confdefs.h + + +$as_echo "#define gid_t int" >>confdefs.h + +fi + +ac_fn_c_check_type "$LINENO" "ptrdiff_t" "ac_cv_type_ptrdiff_t" "$ac_includes_default" +if test "x$ac_cv_type_ptrdiff_t" = xyes; then : + +cat >>confdefs.h <<_ACEOF +#define HAVE_PTRDIFF_T 1 +_ACEOF + + +fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether byte ordering is bigendian" >&5 +$as_echo_n "checking whether byte ordering is bigendian... " >&6; } +if ${ac_cv_c_bigendian+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_cv_c_bigendian=unknown + # See if we're dealing with a universal compiler. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#ifndef __APPLE_CC__ + not a universal capable compiler + #endif + typedef int dummy; + +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + + # Check for potential -arch flags. It is not universal unless + # there are at least two -arch flags with different values. + ac_arch= + ac_prev= + for ac_word in $CC $CFLAGS $CPPFLAGS $LDFLAGS; do + if test -n "$ac_prev"; then + case $ac_word in + i?86 | x86_64 | ppc | ppc64) + if test -z "$ac_arch" || test "$ac_arch" = "$ac_word"; then + ac_arch=$ac_word + else + ac_cv_c_bigendian=universal + break + fi + ;; + esac + ac_prev= + elif test "x$ac_word" = "x-arch"; then + ac_prev=arch + fi + done +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + if test $ac_cv_c_bigendian = unknown; then + # See if sys/param.h defines the BYTE_ORDER macro. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include + #include + +int +main () +{ +#if ! (defined BYTE_ORDER && defined BIG_ENDIAN \ + && defined LITTLE_ENDIAN && BYTE_ORDER && BIG_ENDIAN \ + && LITTLE_ENDIAN) + bogus endian macros + #endif + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + # It does; now see whether it defined to BIG_ENDIAN or not. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include + #include + +int +main () +{ +#if BYTE_ORDER != BIG_ENDIAN + not big endian + #endif + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_c_bigendian=yes +else + ac_cv_c_bigendian=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + fi + if test $ac_cv_c_bigendian = unknown; then + # See if defines _LITTLE_ENDIAN or _BIG_ENDIAN (e.g., Solaris). + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include + +int +main () +{ +#if ! (defined _LITTLE_ENDIAN || defined _BIG_ENDIAN) + bogus endian macros + #endif + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + # It does; now see whether it defined to _BIG_ENDIAN or not. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include + +int +main () +{ +#ifndef _BIG_ENDIAN + not big endian + #endif + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_c_bigendian=yes +else + ac_cv_c_bigendian=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + fi + if test $ac_cv_c_bigendian = unknown; then + # Compile a test program. + if test "$cross_compiling" = yes; then : + # Try to guess by grepping values from an object file. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +short int ascii_mm[] = + { 0x4249, 0x4765, 0x6E44, 0x6961, 0x6E53, 0x7953, 0 }; + short int ascii_ii[] = + { 0x694C, 0x5454, 0x656C, 0x6E45, 0x6944, 0x6E61, 0 }; + int use_ascii (int i) { + return ascii_mm[i] + ascii_ii[i]; + } + short int ebcdic_ii[] = + { 0x89D3, 0xE3E3, 0x8593, 0x95C5, 0x89C4, 0x9581, 0 }; + short int ebcdic_mm[] = + { 0xC2C9, 0xC785, 0x95C4, 0x8981, 0x95E2, 0xA8E2, 0 }; + int use_ebcdic (int i) { + return ebcdic_mm[i] + ebcdic_ii[i]; + } + extern int foo; + +int +main () +{ +return use_ascii (foo) == use_ebcdic (foo); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + if grep BIGenDianSyS conftest.$ac_objext >/dev/null; then + ac_cv_c_bigendian=yes + fi + if grep LiTTleEnDian conftest.$ac_objext >/dev/null ; then + if test "$ac_cv_c_bigendian" = unknown; then + ac_cv_c_bigendian=no + else + # finding both strings is unlikely to happen, but who knows? + ac_cv_c_bigendian=unknown + fi + fi +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ + + /* Are we little or big endian? From Harbison&Steele. */ + union + { + long int l; + char c[sizeof (long int)]; + } u; + u.l = 1; + return u.c[sizeof (long int) - 1] == 1; + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + ac_cv_c_bigendian=no +else + ac_cv_c_bigendian=yes +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +fi + + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_bigendian" >&5 +$as_echo "$ac_cv_c_bigendian" >&6; } + case $ac_cv_c_bigendian in #( + yes) + $as_echo "#define WORDS_BIGENDIAN 1" >>confdefs.h +;; #( + no) + ;; #( + universal) + +$as_echo "#define AC_APPLE_UNIVERSAL_BUILD 1" >>confdefs.h + + ;; #( + *) + as_fn_error $? "unknown endianness + presetting ac_cv_c_bigendian=no (or yes) will help" "$LINENO" 5 ;; + esac + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for inline" >&5 +$as_echo_n "checking for inline... " >&6; } +if ${ac_cv_c_inline+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_cv_c_inline=no +for ac_kw in inline __inline__ __inline; do + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#ifndef __cplusplus +typedef int foo_t; +static $ac_kw foo_t static_foo () {return 0; } +$ac_kw foo_t foo () {return 0; } +#endif + +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_c_inline=$ac_kw +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + test "$ac_cv_c_inline" != no && break +done + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_inline" >&5 +$as_echo "$ac_cv_c_inline" >&6; } + +case $ac_cv_c_inline in + inline | yes) ;; + *) + case $ac_cv_c_inline in + no) ac_val=;; + *) ac_val=$ac_cv_c_inline;; + esac + cat >>confdefs.h <<_ACEOF +#ifndef __cplusplus +#define inline $ac_val +#endif +_ACEOF + ;; +esac + +# Check whether --enable-largefile was given. +if test "${enable_largefile+set}" = set; then : + enableval=$enable_largefile; +fi + +if test "$enable_largefile" != no; then + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for special C compiler options needed for large files" >&5 +$as_echo_n "checking for special C compiler options needed for large files... " >&6; } +if ${ac_cv_sys_largefile_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_cv_sys_largefile_CC=no + if test "$GCC" != yes; then + ac_save_CC=$CC + while :; do + # IRIX 6.2 and later do not support large files by default, + # so use the C compiler's -n32 option if that helps. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include + /* Check that off_t can represent 2**63 - 1 correctly. + We can't simply define LARGE_OFF_T to be 9223372036854775807, + since some C++ compilers masquerading as C compilers + incorrectly reject 9223372036854775807. */ +#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31)) + int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 + && LARGE_OFF_T % 2147483647 == 1) + ? 1 : -1]; +int +main () +{ + + ; + return 0; +} +_ACEOF + if ac_fn_c_try_compile "$LINENO"; then : + break +fi +rm -f core conftest.err conftest.$ac_objext + CC="$CC -n32" + if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_sys_largefile_CC=' -n32'; break +fi +rm -f core conftest.err conftest.$ac_objext + break + done + CC=$ac_save_CC + rm -f conftest.$ac_ext + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sys_largefile_CC" >&5 +$as_echo "$ac_cv_sys_largefile_CC" >&6; } + if test "$ac_cv_sys_largefile_CC" != no; then + CC=$CC$ac_cv_sys_largefile_CC + fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for _FILE_OFFSET_BITS value needed for large files" >&5 +$as_echo_n "checking for _FILE_OFFSET_BITS value needed for large files... " >&6; } +if ${ac_cv_sys_file_offset_bits+:} false; then : + $as_echo_n "(cached) " >&6 +else + while :; do + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include + /* Check that off_t can represent 2**63 - 1 correctly. + We can't simply define LARGE_OFF_T to be 9223372036854775807, + since some C++ compilers masquerading as C compilers + incorrectly reject 9223372036854775807. */ +#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31)) + int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 + && LARGE_OFF_T % 2147483647 == 1) + ? 1 : -1]; +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_sys_file_offset_bits=no; break +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#define _FILE_OFFSET_BITS 64 +#include + /* Check that off_t can represent 2**63 - 1 correctly. + We can't simply define LARGE_OFF_T to be 9223372036854775807, + since some C++ compilers masquerading as C compilers + incorrectly reject 9223372036854775807. */ +#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31)) + int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 + && LARGE_OFF_T % 2147483647 == 1) + ? 1 : -1]; +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_sys_file_offset_bits=64; break +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + ac_cv_sys_file_offset_bits=unknown + break +done +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sys_file_offset_bits" >&5 +$as_echo "$ac_cv_sys_file_offset_bits" >&6; } +case $ac_cv_sys_file_offset_bits in #( + no | unknown) ;; + *) +cat >>confdefs.h <<_ACEOF +#define _FILE_OFFSET_BITS $ac_cv_sys_file_offset_bits +_ACEOF +;; +esac +rm -rf conftest* + if test $ac_cv_sys_file_offset_bits = unknown; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for _LARGE_FILES value needed for large files" >&5 +$as_echo_n "checking for _LARGE_FILES value needed for large files... " >&6; } +if ${ac_cv_sys_large_files+:} false; then : + $as_echo_n "(cached) " >&6 +else + while :; do + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include + /* Check that off_t can represent 2**63 - 1 correctly. + We can't simply define LARGE_OFF_T to be 9223372036854775807, + since some C++ compilers masquerading as C compilers + incorrectly reject 9223372036854775807. */ +#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31)) + int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 + && LARGE_OFF_T % 2147483647 == 1) + ? 1 : -1]; +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_sys_large_files=no; break +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#define _LARGE_FILES 1 +#include + /* Check that off_t can represent 2**63 - 1 correctly. + We can't simply define LARGE_OFF_T to be 9223372036854775807, + since some C++ compilers masquerading as C compilers + incorrectly reject 9223372036854775807. */ +#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31)) + int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 + && LARGE_OFF_T % 2147483647 == 1) + ? 1 : -1]; +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_sys_large_files=1; break +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + ac_cv_sys_large_files=unknown + break +done +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sys_large_files" >&5 +$as_echo "$ac_cv_sys_large_files" >&6; } +case $ac_cv_sys_large_files in #( + no | unknown) ;; + *) +cat >>confdefs.h <<_ACEOF +#define _LARGE_FILES $ac_cv_sys_large_files +_ACEOF +;; +esac +rm -rf conftest* + fi + + +fi + + +ac_fn_c_check_member "$LINENO" "struct tm" "tm_gmtoff" "ac_cv_member_struct_tm_tm_gmtoff" "#include +" +if test "x$ac_cv_member_struct_tm_tm_gmtoff" = xyes; then : + have_struct_tm_tm_gmtoff=yes +else + have_struct_tm_tm_gmtoff=no +fi + + +if test "x$have_struct_tm_tm_gmtoff" = "xyes"; then + +$as_echo "#define HAVE_STRUCT_TM_TM_GMTOFF 1" >>confdefs.h + +fi + +# Check size of pointer to decide we need 8 bytes alignment +# adjustment. +# The cast to long int works around a bug in the HP C Compiler +# version HP92453-01 B.11.11.23709.GP, which incorrectly rejects +# declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'. +# This bug is HP SR number 8606223364. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking size of int *" >&5 +$as_echo_n "checking size of int *... " >&6; } +if ${ac_cv_sizeof_int_p+:} false; then : + $as_echo_n "(cached) " >&6 +else + if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (int *))" "ac_cv_sizeof_int_p" "$ac_includes_default"; then : + +else + if test "$ac_cv_type_int_p" = yes; then + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error 77 "cannot compute sizeof (int *) +See \`config.log' for more details" "$LINENO" 5; } + else + ac_cv_sizeof_int_p=0 + fi +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_int_p" >&5 +$as_echo "$ac_cv_sizeof_int_p" >&6; } + + + +cat >>confdefs.h <<_ACEOF +#define SIZEOF_INT_P $ac_cv_sizeof_int_p +_ACEOF + + + +# Checks for library functions. +if test "x$cross_compiling" != "xyes"; then + for ac_header in stdlib.h +do : + ac_fn_c_check_header_mongrel "$LINENO" "stdlib.h" "ac_cv_header_stdlib_h" "$ac_includes_default" +if test "x$ac_cv_header_stdlib_h" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_STDLIB_H 1 +_ACEOF + +fi + +done + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for GNU libc compatible malloc" >&5 +$as_echo_n "checking for GNU libc compatible malloc... " >&6; } +if ${ac_cv_func_malloc_0_nonnull+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test "$cross_compiling" = yes; then : + ac_cv_func_malloc_0_nonnull=no +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#if defined STDC_HEADERS || defined HAVE_STDLIB_H +# include +#else +char *malloc (); +#endif + +int +main () +{ +return ! malloc (0); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + ac_cv_func_malloc_0_nonnull=yes +else + ac_cv_func_malloc_0_nonnull=no +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_malloc_0_nonnull" >&5 +$as_echo "$ac_cv_func_malloc_0_nonnull" >&6; } +if test $ac_cv_func_malloc_0_nonnull = yes; then : + +$as_echo "#define HAVE_MALLOC 1" >>confdefs.h + +else + $as_echo "#define HAVE_MALLOC 0" >>confdefs.h + + case " $LIBOBJS " in + *" malloc.$ac_objext "* ) ;; + *) LIBOBJS="$LIBOBJS malloc.$ac_objext" + ;; +esac + + +$as_echo "#define malloc rpl_malloc" >>confdefs.h + +fi + + +fi + +for ac_header in unistd.h +do : + ac_fn_c_check_header_mongrel "$LINENO" "unistd.h" "ac_cv_header_unistd_h" "$ac_includes_default" +if test "x$ac_cv_header_unistd_h" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_UNISTD_H 1 +_ACEOF + +fi + +done + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for working chown" >&5 +$as_echo_n "checking for working chown... " >&6; } +if ${ac_cv_func_chown_works+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test "$cross_compiling" = yes; then : + ac_cv_func_chown_works=no +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$ac_includes_default +#include + +int +main () +{ + char *f = "conftest.chown"; + struct stat before, after; + + if (creat (f, 0600) < 0) + return 1; + if (stat (f, &before) < 0) + return 1; + if (chown (f, (uid_t) -1, (gid_t) -1) == -1) + return 1; + if (stat (f, &after) < 0) + return 1; + return ! (before.st_uid == after.st_uid && before.st_gid == after.st_gid); + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + ac_cv_func_chown_works=yes +else + ac_cv_func_chown_works=no +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +fi + +rm -f conftest.chown + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_chown_works" >&5 +$as_echo "$ac_cv_func_chown_works" >&6; } +if test $ac_cv_func_chown_works = yes; then + +$as_echo "#define HAVE_CHOWN 1" >>confdefs.h + +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for error_at_line" >&5 +$as_echo_n "checking for error_at_line... " >&6; } +if ${ac_cv_lib_error_at_line+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +int +main () +{ +error_at_line (0, 0, "", 0, "an error occurred"); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_error_at_line=yes +else + ac_cv_lib_error_at_line=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_error_at_line" >&5 +$as_echo "$ac_cv_lib_error_at_line" >&6; } +if test $ac_cv_lib_error_at_line = no; then + case " $LIBOBJS " in + *" error.$ac_objext "* ) ;; + *) LIBOBJS="$LIBOBJS error.$ac_objext" + ;; +esac + +fi + +for ac_header in vfork.h +do : + ac_fn_c_check_header_mongrel "$LINENO" "vfork.h" "ac_cv_header_vfork_h" "$ac_includes_default" +if test "x$ac_cv_header_vfork_h" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_VFORK_H 1 +_ACEOF + +fi + +done + +for ac_func in fork vfork +do : + as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` +ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" +if eval test \"x\$"$as_ac_var"\" = x"yes"; then : + cat >>confdefs.h <<_ACEOF +#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + +fi +done + +if test "x$ac_cv_func_fork" = xyes; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for working fork" >&5 +$as_echo_n "checking for working fork... " >&6; } +if ${ac_cv_func_fork_works+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test "$cross_compiling" = yes; then : + ac_cv_func_fork_works=cross +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ + + /* By Ruediger Kuhlmann. */ + return fork () < 0; + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + ac_cv_func_fork_works=yes +else + ac_cv_func_fork_works=no +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_fork_works" >&5 +$as_echo "$ac_cv_func_fork_works" >&6; } + +else + ac_cv_func_fork_works=$ac_cv_func_fork +fi +if test "x$ac_cv_func_fork_works" = xcross; then + case $host in + *-*-amigaos* | *-*-msdosdjgpp*) + # Override, as these systems have only a dummy fork() stub + ac_cv_func_fork_works=no + ;; + *) + ac_cv_func_fork_works=yes + ;; + esac + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: result $ac_cv_func_fork_works guessed because of cross compilation" >&5 +$as_echo "$as_me: WARNING: result $ac_cv_func_fork_works guessed because of cross compilation" >&2;} +fi +ac_cv_func_vfork_works=$ac_cv_func_vfork +if test "x$ac_cv_func_vfork" = xyes; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for working vfork" >&5 +$as_echo_n "checking for working vfork... " >&6; } +if ${ac_cv_func_vfork_works+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test "$cross_compiling" = yes; then : + ac_cv_func_vfork_works=cross +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +/* Thanks to Paul Eggert for this test. */ +$ac_includes_default +#include +#ifdef HAVE_VFORK_H +# include +#endif +/* On some sparc systems, changes by the child to local and incoming + argument registers are propagated back to the parent. The compiler + is told about this with #include , but some compilers + (e.g. gcc -O) don't grok . Test for this by using a + static variable whose address is put into a register that is + clobbered by the vfork. */ +static void +#ifdef __cplusplus +sparc_address_test (int arg) +# else +sparc_address_test (arg) int arg; +#endif +{ + static pid_t child; + if (!child) { + child = vfork (); + if (child < 0) { + perror ("vfork"); + _exit(2); + } + if (!child) { + arg = getpid(); + write(-1, "", 0); + _exit (arg); + } + } +} + +int +main () +{ + pid_t parent = getpid (); + pid_t child; + + sparc_address_test (0); + + child = vfork (); + + if (child == 0) { + /* Here is another test for sparc vfork register problems. This + test uses lots of local variables, at least as many local + variables as main has allocated so far including compiler + temporaries. 4 locals are enough for gcc 1.40.3 on a Solaris + 4.1.3 sparc, but we use 8 to be safe. A buggy compiler should + reuse the register of parent for one of the local variables, + since it will think that parent can't possibly be used any more + in this routine. Assigning to the local variable will thus + munge parent in the parent process. */ + pid_t + p = getpid(), p1 = getpid(), p2 = getpid(), p3 = getpid(), + p4 = getpid(), p5 = getpid(), p6 = getpid(), p7 = getpid(); + /* Convince the compiler that p..p7 are live; otherwise, it might + use the same hardware register for all 8 local variables. */ + if (p != p1 || p != p2 || p != p3 || p != p4 + || p != p5 || p != p6 || p != p7) + _exit(1); + + /* On some systems (e.g. IRIX 3.3), vfork doesn't separate parent + from child file descriptors. If the child closes a descriptor + before it execs or exits, this munges the parent's descriptor + as well. Test for this by closing stdout in the child. */ + _exit(close(fileno(stdout)) != 0); + } else { + int status; + struct stat st; + + while (wait(&status) != child) + ; + return ( + /* Was there some problem with vforking? */ + child < 0 + + /* Did the child fail? (This shouldn't happen.) */ + || status + + /* Did the vfork/compiler bug occur? */ + || parent != getpid() + + /* Did the file descriptor bug occur? */ + || fstat(fileno(stdout), &st) != 0 + ); + } +} +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + ac_cv_func_vfork_works=yes +else + ac_cv_func_vfork_works=no +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_vfork_works" >&5 +$as_echo "$ac_cv_func_vfork_works" >&6; } + +fi; +if test "x$ac_cv_func_fork_works" = xcross; then + ac_cv_func_vfork_works=$ac_cv_func_vfork + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: result $ac_cv_func_vfork_works guessed because of cross compilation" >&5 +$as_echo "$as_me: WARNING: result $ac_cv_func_vfork_works guessed because of cross compilation" >&2;} +fi + +if test "x$ac_cv_func_vfork_works" = xyes; then + +$as_echo "#define HAVE_WORKING_VFORK 1" >>confdefs.h + +else + +$as_echo "#define vfork fork" >>confdefs.h + +fi +if test "x$ac_cv_func_fork_works" = xyes; then + +$as_echo "#define HAVE_WORKING_FORK 1" >>confdefs.h + +fi + +# Don't check realloc, since LeakSanitizer detects memory leak during check +# AC_FUNC_REALLOC +ac_fn_c_check_decl "$LINENO" "strerror_r" "ac_cv_have_decl_strerror_r" "$ac_includes_default" +if test "x$ac_cv_have_decl_strerror_r" = xyes; then : + ac_have_decl=1 +else + ac_have_decl=0 +fi + +cat >>confdefs.h <<_ACEOF +#define HAVE_DECL_STRERROR_R $ac_have_decl +_ACEOF + +for ac_func in strerror_r +do : + ac_fn_c_check_func "$LINENO" "strerror_r" "ac_cv_func_strerror_r" +if test "x$ac_cv_func_strerror_r" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_STRERROR_R 1 +_ACEOF + +fi +done + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether strerror_r returns char *" >&5 +$as_echo_n "checking whether strerror_r returns char *... " >&6; } +if ${ac_cv_func_strerror_r_char_p+:} false; then : + $as_echo_n "(cached) " >&6 +else + + ac_cv_func_strerror_r_char_p=no + if test $ac_cv_have_decl_strerror_r = yes; then + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ + + char buf[100]; + char x = *strerror_r (0, buf, sizeof buf); + char *p = strerror_r (0, buf, sizeof buf); + return !p || x; + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_func_strerror_r_char_p=yes +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + else + # strerror_r is not declared. Choose between + # systems that have relatively inaccessible declarations for the + # function. BeOS and DEC UNIX 4.0 fall in this category, but the + # former has a strerror_r that returns char*, while the latter + # has a strerror_r that returns `int'. + # This test should segfault on the DEC system. + if test "$cross_compiling" = yes; then : + : +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$ac_includes_default + extern char *strerror_r (); +int +main () +{ +char buf[100]; + char x = *strerror_r (0, buf, sizeof buf); + return ! isalpha (x); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + ac_cv_func_strerror_r_char_p=yes +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +fi + + fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_strerror_r_char_p" >&5 +$as_echo "$ac_cv_func_strerror_r_char_p" >&6; } +if test $ac_cv_func_strerror_r_char_p = yes; then + +$as_echo "#define STRERROR_R_CHAR_P 1" >>confdefs.h + +fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for working strnlen" >&5 +$as_echo_n "checking for working strnlen... " >&6; } +if ${ac_cv_func_strnlen_working+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test "$cross_compiling" = yes; then : + # Guess no on AIX systems, yes otherwise. + case "$host_os" in + aix*) ac_cv_func_strnlen_working=no;; + *) ac_cv_func_strnlen_working=yes;; + esac +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ + +#define S "foobar" +#define S_LEN (sizeof S - 1) + + /* At least one implementation is buggy: that of AIX 4.3 would + give strnlen (S, 1) == 3. */ + + int i; + for (i = 0; i < S_LEN + 1; ++i) + { + int expected = i <= S_LEN ? i : S_LEN; + if (strnlen (S, i) != expected) + return 1; + } + return 0; + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + ac_cv_func_strnlen_working=yes +else + ac_cv_func_strnlen_working=no +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_strnlen_working" >&5 +$as_echo "$ac_cv_func_strnlen_working" >&6; } +test $ac_cv_func_strnlen_working = no && case " $LIBOBJS " in + *" strnlen.$ac_objext "* ) ;; + *) LIBOBJS="$LIBOBJS strnlen.$ac_objext" + ;; +esac + + + +for ac_func in \ + _Exit \ + accept4 \ + dup2 \ + getcwd \ + getpwnam \ + localtime_r \ + memchr \ + memmove \ + memset \ + socket \ + sqrt \ + strchr \ + strdup \ + strerror \ + strndup \ + strstr \ + strtol \ + strtoul \ + timegm \ + +do : + as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` +ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" +if eval test \"x\$"$as_ac_var"\" = x"yes"; then : + cat >>confdefs.h <<_ACEOF +#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + +fi +done + + +# timerfd_create was added in linux kernel 2.6.25 + +ac_fn_c_check_func "$LINENO" "timerfd_create" "ac_cv_func_timerfd_create" +if test "x$ac_cv_func_timerfd_create" = xyes; then : + have_timerfd_create=yes +else + have_timerfd_create=no +fi + + + +# Checks for epoll availability, primarily for examples/tiny-nghttpd + ax_have_epoll_cppflags="${CPPFLAGS}" + ac_fn_c_check_header_mongrel "$LINENO" "linux/version.h" "ac_cv_header_linux_version_h" "$ac_includes_default" +if test "x$ac_cv_header_linux_version_h" = xyes; then : + CPPFLAGS="${CPPFLAGS} -DHAVE_LINUX_VERSION_H" +fi + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for Linux epoll(7) interface" >&5 +$as_echo_n "checking for Linux epoll(7) interface... " >&6; } + if ${ax_cv_have_epoll+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + #include +#ifdef HAVE_LINUX_VERSION_H +# include +# if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,45) +# error linux kernel version is too old to have epoll +# endif +#endif + +int +main () +{ +int fd, rc; +struct epoll_event ev; +fd = epoll_create(128); +rc = epoll_wait(fd, &ev, 1, 0); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ax_cv_have_epoll=yes +else + ax_cv_have_epoll=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi + + CPPFLAGS="${ax_have_epoll_cppflags}" + if test "${ax_cv_have_epoll}" = "yes"; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } +have_epoll=yes +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +have_epoll=no +fi + + + if test "x${have_epoll}" = "xyes" && + test "x${have_timerfd_create}" = "xyes"; then + ENABLE_TINY_NGHTTPD_TRUE= + ENABLE_TINY_NGHTTPD_FALSE='#' +else + ENABLE_TINY_NGHTTPD_TRUE='#' + ENABLE_TINY_NGHTTPD_FALSE= +fi + + +ac_save_CFLAGS=$CFLAGS +CFLAGS= + +if test "x$werror" != "xno"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -Wall" >&5 +$as_echo_n "checking whether C compiler accepts -Wall... " >&6; } +if ${ax_cv_check_cflags___Wall+:} false; then : + $as_echo_n "(cached) " >&6 +else + + ax_check_save_flags=$CFLAGS + CFLAGS="$CFLAGS -Wall" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ax_cv_check_cflags___Wall=yes +else + ax_cv_check_cflags___Wall=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + CFLAGS=$ax_check_save_flags +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags___Wall" >&5 +$as_echo "$ax_cv_check_cflags___Wall" >&6; } +if test x"$ax_cv_check_cflags___Wall" = xyes; then : + CFLAGS="$CFLAGS -Wall" +else + : +fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -Wextra" >&5 +$as_echo_n "checking whether C compiler accepts -Wextra... " >&6; } +if ${ax_cv_check_cflags___Wextra+:} false; then : + $as_echo_n "(cached) " >&6 +else + + ax_check_save_flags=$CFLAGS + CFLAGS="$CFLAGS -Wextra" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ax_cv_check_cflags___Wextra=yes +else + ax_cv_check_cflags___Wextra=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + CFLAGS=$ax_check_save_flags +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags___Wextra" >&5 +$as_echo "$ax_cv_check_cflags___Wextra" >&6; } +if test x"$ax_cv_check_cflags___Wextra" = xyes; then : + CFLAGS="$CFLAGS -Wextra" +else + : +fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -Werror" >&5 +$as_echo_n "checking whether C compiler accepts -Werror... " >&6; } +if ${ax_cv_check_cflags___Werror+:} false; then : + $as_echo_n "(cached) " >&6 +else + + ax_check_save_flags=$CFLAGS + CFLAGS="$CFLAGS -Werror" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ax_cv_check_cflags___Werror=yes +else + ax_cv_check_cflags___Werror=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + CFLAGS=$ax_check_save_flags +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags___Werror" >&5 +$as_echo "$ax_cv_check_cflags___Werror" >&6; } +if test x"$ax_cv_check_cflags___Werror" = xyes; then : + CFLAGS="$CFLAGS -Werror" +else + : +fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -Wmissing-prototypes" >&5 +$as_echo_n "checking whether C compiler accepts -Wmissing-prototypes... " >&6; } +if ${ax_cv_check_cflags___Wmissing_prototypes+:} false; then : + $as_echo_n "(cached) " >&6 +else + + ax_check_save_flags=$CFLAGS + CFLAGS="$CFLAGS -Wmissing-prototypes" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ax_cv_check_cflags___Wmissing_prototypes=yes +else + ax_cv_check_cflags___Wmissing_prototypes=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + CFLAGS=$ax_check_save_flags +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags___Wmissing_prototypes" >&5 +$as_echo "$ax_cv_check_cflags___Wmissing_prototypes" >&6; } +if test x"$ax_cv_check_cflags___Wmissing_prototypes" = xyes; then : + CFLAGS="$CFLAGS -Wmissing-prototypes" +else + : +fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -Wstrict-prototypes" >&5 +$as_echo_n "checking whether C compiler accepts -Wstrict-prototypes... " >&6; } +if ${ax_cv_check_cflags___Wstrict_prototypes+:} false; then : + $as_echo_n "(cached) " >&6 +else + + ax_check_save_flags=$CFLAGS + CFLAGS="$CFLAGS -Wstrict-prototypes" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ax_cv_check_cflags___Wstrict_prototypes=yes +else + ax_cv_check_cflags___Wstrict_prototypes=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + CFLAGS=$ax_check_save_flags +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags___Wstrict_prototypes" >&5 +$as_echo "$ax_cv_check_cflags___Wstrict_prototypes" >&6; } +if test x"$ax_cv_check_cflags___Wstrict_prototypes" = xyes; then : + CFLAGS="$CFLAGS -Wstrict-prototypes" +else + : +fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -Wmissing-declarations" >&5 +$as_echo_n "checking whether C compiler accepts -Wmissing-declarations... " >&6; } +if ${ax_cv_check_cflags___Wmissing_declarations+:} false; then : + $as_echo_n "(cached) " >&6 +else + + ax_check_save_flags=$CFLAGS + CFLAGS="$CFLAGS -Wmissing-declarations" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ax_cv_check_cflags___Wmissing_declarations=yes +else + ax_cv_check_cflags___Wmissing_declarations=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + CFLAGS=$ax_check_save_flags +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags___Wmissing_declarations" >&5 +$as_echo "$ax_cv_check_cflags___Wmissing_declarations" >&6; } +if test x"$ax_cv_check_cflags___Wmissing_declarations" = xyes; then : + CFLAGS="$CFLAGS -Wmissing-declarations" +else + : +fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -Wpointer-arith" >&5 +$as_echo_n "checking whether C compiler accepts -Wpointer-arith... " >&6; } +if ${ax_cv_check_cflags___Wpointer_arith+:} false; then : + $as_echo_n "(cached) " >&6 +else + + ax_check_save_flags=$CFLAGS + CFLAGS="$CFLAGS -Wpointer-arith" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ax_cv_check_cflags___Wpointer_arith=yes +else + ax_cv_check_cflags___Wpointer_arith=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + CFLAGS=$ax_check_save_flags +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags___Wpointer_arith" >&5 +$as_echo "$ax_cv_check_cflags___Wpointer_arith" >&6; } +if test x"$ax_cv_check_cflags___Wpointer_arith" = xyes; then : + CFLAGS="$CFLAGS -Wpointer-arith" +else + : +fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -Wdeclaration-after-statement" >&5 +$as_echo_n "checking whether C compiler accepts -Wdeclaration-after-statement... " >&6; } +if ${ax_cv_check_cflags___Wdeclaration_after_statement+:} false; then : + $as_echo_n "(cached) " >&6 +else + + ax_check_save_flags=$CFLAGS + CFLAGS="$CFLAGS -Wdeclaration-after-statement" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ax_cv_check_cflags___Wdeclaration_after_statement=yes +else + ax_cv_check_cflags___Wdeclaration_after_statement=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + CFLAGS=$ax_check_save_flags +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags___Wdeclaration_after_statement" >&5 +$as_echo "$ax_cv_check_cflags___Wdeclaration_after_statement" >&6; } +if test x"$ax_cv_check_cflags___Wdeclaration_after_statement" = xyes; then : + CFLAGS="$CFLAGS -Wdeclaration-after-statement" +else + : +fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -Wformat-security" >&5 +$as_echo_n "checking whether C compiler accepts -Wformat-security... " >&6; } +if ${ax_cv_check_cflags___Wformat_security+:} false; then : + $as_echo_n "(cached) " >&6 +else + + ax_check_save_flags=$CFLAGS + CFLAGS="$CFLAGS -Wformat-security" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ax_cv_check_cflags___Wformat_security=yes +else + ax_cv_check_cflags___Wformat_security=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + CFLAGS=$ax_check_save_flags +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags___Wformat_security" >&5 +$as_echo "$ax_cv_check_cflags___Wformat_security" >&6; } +if test x"$ax_cv_check_cflags___Wformat_security" = xyes; then : + CFLAGS="$CFLAGS -Wformat-security" +else + : +fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -Wwrite-strings" >&5 +$as_echo_n "checking whether C compiler accepts -Wwrite-strings... " >&6; } +if ${ax_cv_check_cflags___Wwrite_strings+:} false; then : + $as_echo_n "(cached) " >&6 +else + + ax_check_save_flags=$CFLAGS + CFLAGS="$CFLAGS -Wwrite-strings" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ax_cv_check_cflags___Wwrite_strings=yes +else + ax_cv_check_cflags___Wwrite_strings=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + CFLAGS=$ax_check_save_flags +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags___Wwrite_strings" >&5 +$as_echo "$ax_cv_check_cflags___Wwrite_strings" >&6; } +if test x"$ax_cv_check_cflags___Wwrite_strings" = xyes; then : + CFLAGS="$CFLAGS -Wwrite-strings" +else + : +fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -Wshadow" >&5 +$as_echo_n "checking whether C compiler accepts -Wshadow... " >&6; } +if ${ax_cv_check_cflags___Wshadow+:} false; then : + $as_echo_n "(cached) " >&6 +else + + ax_check_save_flags=$CFLAGS + CFLAGS="$CFLAGS -Wshadow" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ax_cv_check_cflags___Wshadow=yes +else + ax_cv_check_cflags___Wshadow=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + CFLAGS=$ax_check_save_flags +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags___Wshadow" >&5 +$as_echo "$ax_cv_check_cflags___Wshadow" >&6; } +if test x"$ax_cv_check_cflags___Wshadow" = xyes; then : + CFLAGS="$CFLAGS -Wshadow" +else + : +fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -Winline" >&5 +$as_echo_n "checking whether C compiler accepts -Winline... " >&6; } +if ${ax_cv_check_cflags___Winline+:} false; then : + $as_echo_n "(cached) " >&6 +else + + ax_check_save_flags=$CFLAGS + CFLAGS="$CFLAGS -Winline" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ax_cv_check_cflags___Winline=yes +else + ax_cv_check_cflags___Winline=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + CFLAGS=$ax_check_save_flags +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags___Winline" >&5 +$as_echo "$ax_cv_check_cflags___Winline" >&6; } +if test x"$ax_cv_check_cflags___Winline" = xyes; then : + CFLAGS="$CFLAGS -Winline" +else + : +fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -Wnested-externs" >&5 +$as_echo_n "checking whether C compiler accepts -Wnested-externs... " >&6; } +if ${ax_cv_check_cflags___Wnested_externs+:} false; then : + $as_echo_n "(cached) " >&6 +else + + ax_check_save_flags=$CFLAGS + CFLAGS="$CFLAGS -Wnested-externs" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ax_cv_check_cflags___Wnested_externs=yes +else + ax_cv_check_cflags___Wnested_externs=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + CFLAGS=$ax_check_save_flags +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags___Wnested_externs" >&5 +$as_echo "$ax_cv_check_cflags___Wnested_externs" >&6; } +if test x"$ax_cv_check_cflags___Wnested_externs" = xyes; then : + CFLAGS="$CFLAGS -Wnested-externs" +else + : +fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -Wfloat-equal" >&5 +$as_echo_n "checking whether C compiler accepts -Wfloat-equal... " >&6; } +if ${ax_cv_check_cflags___Wfloat_equal+:} false; then : + $as_echo_n "(cached) " >&6 +else + + ax_check_save_flags=$CFLAGS + CFLAGS="$CFLAGS -Wfloat-equal" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ax_cv_check_cflags___Wfloat_equal=yes +else + ax_cv_check_cflags___Wfloat_equal=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + CFLAGS=$ax_check_save_flags +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags___Wfloat_equal" >&5 +$as_echo "$ax_cv_check_cflags___Wfloat_equal" >&6; } +if test x"$ax_cv_check_cflags___Wfloat_equal" = xyes; then : + CFLAGS="$CFLAGS -Wfloat-equal" +else + : +fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -Wundef" >&5 +$as_echo_n "checking whether C compiler accepts -Wundef... " >&6; } +if ${ax_cv_check_cflags___Wundef+:} false; then : + $as_echo_n "(cached) " >&6 +else + + ax_check_save_flags=$CFLAGS + CFLAGS="$CFLAGS -Wundef" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ax_cv_check_cflags___Wundef=yes +else + ax_cv_check_cflags___Wundef=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + CFLAGS=$ax_check_save_flags +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags___Wundef" >&5 +$as_echo "$ax_cv_check_cflags___Wundef" >&6; } +if test x"$ax_cv_check_cflags___Wundef" = xyes; then : + CFLAGS="$CFLAGS -Wundef" +else + : +fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -Wendif-labels" >&5 +$as_echo_n "checking whether C compiler accepts -Wendif-labels... " >&6; } +if ${ax_cv_check_cflags___Wendif_labels+:} false; then : + $as_echo_n "(cached) " >&6 +else + + ax_check_save_flags=$CFLAGS + CFLAGS="$CFLAGS -Wendif-labels" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ax_cv_check_cflags___Wendif_labels=yes +else + ax_cv_check_cflags___Wendif_labels=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + CFLAGS=$ax_check_save_flags +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags___Wendif_labels" >&5 +$as_echo "$ax_cv_check_cflags___Wendif_labels" >&6; } +if test x"$ax_cv_check_cflags___Wendif_labels" = xyes; then : + CFLAGS="$CFLAGS -Wendif-labels" +else + : +fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -Wempty-body" >&5 +$as_echo_n "checking whether C compiler accepts -Wempty-body... " >&6; } +if ${ax_cv_check_cflags___Wempty_body+:} false; then : + $as_echo_n "(cached) " >&6 +else + + ax_check_save_flags=$CFLAGS + CFLAGS="$CFLAGS -Wempty-body" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ax_cv_check_cflags___Wempty_body=yes +else + ax_cv_check_cflags___Wempty_body=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + CFLAGS=$ax_check_save_flags +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags___Wempty_body" >&5 +$as_echo "$ax_cv_check_cflags___Wempty_body" >&6; } +if test x"$ax_cv_check_cflags___Wempty_body" = xyes; then : + CFLAGS="$CFLAGS -Wempty-body" +else + : +fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -Wcast-align" >&5 +$as_echo_n "checking whether C compiler accepts -Wcast-align... " >&6; } +if ${ax_cv_check_cflags___Wcast_align+:} false; then : + $as_echo_n "(cached) " >&6 +else + + ax_check_save_flags=$CFLAGS + CFLAGS="$CFLAGS -Wcast-align" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ax_cv_check_cflags___Wcast_align=yes +else + ax_cv_check_cflags___Wcast_align=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + CFLAGS=$ax_check_save_flags +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags___Wcast_align" >&5 +$as_echo "$ax_cv_check_cflags___Wcast_align" >&6; } +if test x"$ax_cv_check_cflags___Wcast_align" = xyes; then : + CFLAGS="$CFLAGS -Wcast-align" +else + : +fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -Wclobbered" >&5 +$as_echo_n "checking whether C compiler accepts -Wclobbered... " >&6; } +if ${ax_cv_check_cflags___Wclobbered+:} false; then : + $as_echo_n "(cached) " >&6 +else + + ax_check_save_flags=$CFLAGS + CFLAGS="$CFLAGS -Wclobbered" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ax_cv_check_cflags___Wclobbered=yes +else + ax_cv_check_cflags___Wclobbered=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + CFLAGS=$ax_check_save_flags +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags___Wclobbered" >&5 +$as_echo "$ax_cv_check_cflags___Wclobbered" >&6; } +if test x"$ax_cv_check_cflags___Wclobbered" = xyes; then : + CFLAGS="$CFLAGS -Wclobbered" +else + : +fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -Wvla" >&5 +$as_echo_n "checking whether C compiler accepts -Wvla... " >&6; } +if ${ax_cv_check_cflags___Wvla+:} false; then : + $as_echo_n "(cached) " >&6 +else + + ax_check_save_flags=$CFLAGS + CFLAGS="$CFLAGS -Wvla" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ax_cv_check_cflags___Wvla=yes +else + ax_cv_check_cflags___Wvla=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + CFLAGS=$ax_check_save_flags +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags___Wvla" >&5 +$as_echo "$ax_cv_check_cflags___Wvla" >&6; } +if test x"$ax_cv_check_cflags___Wvla" = xyes; then : + CFLAGS="$CFLAGS -Wvla" +else + : +fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -Wpragmas" >&5 +$as_echo_n "checking whether C compiler accepts -Wpragmas... " >&6; } +if ${ax_cv_check_cflags___Wpragmas+:} false; then : + $as_echo_n "(cached) " >&6 +else + + ax_check_save_flags=$CFLAGS + CFLAGS="$CFLAGS -Wpragmas" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ax_cv_check_cflags___Wpragmas=yes +else + ax_cv_check_cflags___Wpragmas=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + CFLAGS=$ax_check_save_flags +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags___Wpragmas" >&5 +$as_echo "$ax_cv_check_cflags___Wpragmas" >&6; } +if test x"$ax_cv_check_cflags___Wpragmas" = xyes; then : + CFLAGS="$CFLAGS -Wpragmas" +else + : +fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -Wunreachable-code" >&5 +$as_echo_n "checking whether C compiler accepts -Wunreachable-code... " >&6; } +if ${ax_cv_check_cflags___Wunreachable_code+:} false; then : + $as_echo_n "(cached) " >&6 +else + + ax_check_save_flags=$CFLAGS + CFLAGS="$CFLAGS -Wunreachable-code" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ax_cv_check_cflags___Wunreachable_code=yes +else + ax_cv_check_cflags___Wunreachable_code=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + CFLAGS=$ax_check_save_flags +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags___Wunreachable_code" >&5 +$as_echo "$ax_cv_check_cflags___Wunreachable_code" >&6; } +if test x"$ax_cv_check_cflags___Wunreachable_code" = xyes; then : + CFLAGS="$CFLAGS -Wunreachable-code" +else + : +fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -Waddress" >&5 +$as_echo_n "checking whether C compiler accepts -Waddress... " >&6; } +if ${ax_cv_check_cflags___Waddress+:} false; then : + $as_echo_n "(cached) " >&6 +else + + ax_check_save_flags=$CFLAGS + CFLAGS="$CFLAGS -Waddress" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ax_cv_check_cflags___Waddress=yes +else + ax_cv_check_cflags___Waddress=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + CFLAGS=$ax_check_save_flags +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags___Waddress" >&5 +$as_echo "$ax_cv_check_cflags___Waddress" >&6; } +if test x"$ax_cv_check_cflags___Waddress" = xyes; then : + CFLAGS="$CFLAGS -Waddress" +else + : +fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -Wattributes" >&5 +$as_echo_n "checking whether C compiler accepts -Wattributes... " >&6; } +if ${ax_cv_check_cflags___Wattributes+:} false; then : + $as_echo_n "(cached) " >&6 +else + + ax_check_save_flags=$CFLAGS + CFLAGS="$CFLAGS -Wattributes" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ax_cv_check_cflags___Wattributes=yes +else + ax_cv_check_cflags___Wattributes=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + CFLAGS=$ax_check_save_flags +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags___Wattributes" >&5 +$as_echo "$ax_cv_check_cflags___Wattributes" >&6; } +if test x"$ax_cv_check_cflags___Wattributes" = xyes; then : + CFLAGS="$CFLAGS -Wattributes" +else + : +fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -Wdiv-by-zero" >&5 +$as_echo_n "checking whether C compiler accepts -Wdiv-by-zero... " >&6; } +if ${ax_cv_check_cflags___Wdiv_by_zero+:} false; then : + $as_echo_n "(cached) " >&6 +else + + ax_check_save_flags=$CFLAGS + CFLAGS="$CFLAGS -Wdiv-by-zero" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ax_cv_check_cflags___Wdiv_by_zero=yes +else + ax_cv_check_cflags___Wdiv_by_zero=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + CFLAGS=$ax_check_save_flags +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags___Wdiv_by_zero" >&5 +$as_echo "$ax_cv_check_cflags___Wdiv_by_zero" >&6; } +if test x"$ax_cv_check_cflags___Wdiv_by_zero" = xyes; then : + CFLAGS="$CFLAGS -Wdiv-by-zero" +else + : +fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -Wshorten-64-to-32" >&5 +$as_echo_n "checking whether C compiler accepts -Wshorten-64-to-32... " >&6; } +if ${ax_cv_check_cflags___Wshorten_64_to_32+:} false; then : + $as_echo_n "(cached) " >&6 +else + + ax_check_save_flags=$CFLAGS + CFLAGS="$CFLAGS -Wshorten-64-to-32" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ax_cv_check_cflags___Wshorten_64_to_32=yes +else + ax_cv_check_cflags___Wshorten_64_to_32=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + CFLAGS=$ax_check_save_flags +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags___Wshorten_64_to_32" >&5 +$as_echo "$ax_cv_check_cflags___Wshorten_64_to_32" >&6; } +if test x"$ax_cv_check_cflags___Wshorten_64_to_32" = xyes; then : + CFLAGS="$CFLAGS -Wshorten-64-to-32" +else + : +fi + + + # Only work with Clang for the moment + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -Wheader-guard" >&5 +$as_echo_n "checking whether C compiler accepts -Wheader-guard... " >&6; } +if ${ax_cv_check_cflags___Wheader_guard+:} false; then : + $as_echo_n "(cached) " >&6 +else + + ax_check_save_flags=$CFLAGS + CFLAGS="$CFLAGS -Wheader-guard" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ax_cv_check_cflags___Wheader_guard=yes +else + ax_cv_check_cflags___Wheader_guard=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + CFLAGS=$ax_check_save_flags +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags___Wheader_guard" >&5 +$as_echo "$ax_cv_check_cflags___Wheader_guard" >&6; } +if test x"$ax_cv_check_cflags___Wheader_guard" = xyes; then : + CFLAGS="$CFLAGS -Wheader-guard" +else + : +fi + +fi + +WARNCFLAGS=$CFLAGS +CFLAGS=$ac_save_CFLAGS + + + +if test "x$debug" != "xno"; then + +$as_echo "#define DEBUGBUILD 1" >>confdefs.h + +fi + +enable_threads=yes +# Some platform does not have working std::future. We disable +# threading for those platforms. +if test "x$threads" != "xyes" || + test "x$have_std_future" != "xyes"; then + enable_threads=no + +$as_echo "#define NOTHREADS 1" >>confdefs.h + +fi + + + + +ac_config_files="$ac_config_files Makefile lib/Makefile lib/libnghttp2.pc lib/includes/Makefile lib/includes/nghttp2/nghttp2ver.h tests/Makefile tests/testdata/Makefile third-party/Makefile src/Makefile src/includes/Makefile src/libnghttp2_asio.pc examples/Makefile python/Makefile python/setup.py integration-tests/Makefile integration-tests/config.go integration-tests/setenv doc/Makefile doc/conf.py doc/index.rst doc/package_README.rst doc/tutorial-client.rst doc/tutorial-server.rst doc/tutorial-hpack.rst doc/nghttpx-howto.rst doc/h2load-howto.rst doc/libnghttp2_asio.rst doc/python-apiref.rst doc/building-android-binary.rst doc/nghttp2.h.rst doc/nghttp2ver.h.rst doc/asio_http2.h.rst doc/asio_http2_server.h.rst doc/asio_http2_client.h.rst doc/contribute.rst contrib/Makefile" + +cat >confcache <<\_ACEOF +# This file is a shell script that caches the results of configure +# tests run on this system so they can be shared between configure +# scripts and configure runs, see configure's option --config-cache. +# It is not useful on other systems. If it contains results you don't +# want to keep, you may remove or edit it. +# +# config.status only pays attention to the cache file if you give it +# the --recheck option to rerun configure. +# +# `ac_cv_env_foo' variables (set or unset) will be overridden when +# loading this file, other *unset* `ac_cv_foo' will be assigned the +# following values. + +_ACEOF + +# The following way of writing the cache mishandles newlines in values, +# but we know of no workaround that is simple, portable, and efficient. +# So, we kill variables containing newlines. +# Ultrix sh set writes to stderr and can't be redirected directly, +# and sets the high bit in the cache file unless we assign to the vars. +( + for ac_var in `(set) 2>&1 | sed -n 's/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'`; do + eval ac_val=\$$ac_var + case $ac_val in #( + *${as_nl}*) + case $ac_var in #( + *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 +$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; + esac + case $ac_var in #( + _ | IFS | as_nl) ;; #( + BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( + *) { eval $ac_var=; unset $ac_var;} ;; + esac ;; + esac + done + + (set) 2>&1 | + case $as_nl`(ac_space=' '; set) 2>&1` in #( + *${as_nl}ac_space=\ *) + # `set' does not quote correctly, so add quotes: double-quote + # substitution turns \\\\ into \\, and sed turns \\ into \. + sed -n \ + "s/'/'\\\\''/g; + s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p" + ;; #( + *) + # `set' quotes correctly as required by POSIX, so do not add quotes. + sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" + ;; + esac | + sort +) | + sed ' + /^ac_cv_env_/b end + t clear + :clear + s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/ + t end + s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/ + :end' >>confcache +if diff "$cache_file" confcache >/dev/null 2>&1; then :; else + if test -w "$cache_file"; then + if test "x$cache_file" != "x/dev/null"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: updating cache $cache_file" >&5 +$as_echo "$as_me: updating cache $cache_file" >&6;} + if test ! -f "$cache_file" || test -h "$cache_file"; then + cat confcache >"$cache_file" + else + case $cache_file in #( + */* | ?:*) + mv -f confcache "$cache_file"$$ && + mv -f "$cache_file"$$ "$cache_file" ;; #( + *) + mv -f confcache "$cache_file" ;; + esac + fi + fi + else + { $as_echo "$as_me:${as_lineno-$LINENO}: not updating unwritable cache $cache_file" >&5 +$as_echo "$as_me: not updating unwritable cache $cache_file" >&6;} + fi +fi +rm -f confcache + +test "x$prefix" = xNONE && prefix=$ac_default_prefix +# Let make expand exec_prefix. +test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' + +DEFS=-DHAVE_CONFIG_H + +ac_libobjs= +ac_ltlibobjs= +U= +for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue + # 1. Remove the extension, and $U if already installed. + ac_script='s/\$U\././;s/\.o$//;s/\.obj$//' + ac_i=`$as_echo "$ac_i" | sed "$ac_script"` + # 2. Prepend LIBOBJDIR. When used with automake>=1.10 LIBOBJDIR + # will be set to the directory where LIBOBJS objects are built. + as_fn_append ac_libobjs " \${LIBOBJDIR}$ac_i\$U.$ac_objext" + as_fn_append ac_ltlibobjs " \${LIBOBJDIR}$ac_i"'$U.lo' +done +LIBOBJS=$ac_libobjs + +LTLIBOBJS=$ac_ltlibobjs + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking that generated files are newer than configure" >&5 +$as_echo_n "checking that generated files are newer than configure... " >&6; } + if test -n "$am_sleep_pid"; then + # Hide warnings about reused PIDs. + wait $am_sleep_pid 2>/dev/null + fi + { $as_echo "$as_me:${as_lineno-$LINENO}: result: done" >&5 +$as_echo "done" >&6; } +if test -z "${AMDEP_TRUE}" && test -z "${AMDEP_FALSE}"; then + as_fn_error $? "conditional \"AMDEP\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${am__fastdepCC_TRUE}" && test -z "${am__fastdepCC_FALSE}"; then + as_fn_error $? "conditional \"am__fastdepCC\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi + if test -n "$EXEEXT"; then + am__EXEEXT_TRUE= + am__EXEEXT_FALSE='#' +else + am__EXEEXT_TRUE='#' + am__EXEEXT_FALSE= +fi + +if test -z "${am__fastdepCXX_TRUE}" && test -z "${am__fastdepCXX_FALSE}"; then + as_fn_error $? "conditional \"am__fastdepCXX\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${HAVE_CUNIT_TRUE}" && test -z "${HAVE_CUNIT_FALSE}"; then + as_fn_error $? "conditional \"HAVE_CUNIT\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${HAVE_LIBXML2_TRUE}" && test -z "${HAVE_LIBXML2_FALSE}"; then + as_fn_error $? "conditional \"HAVE_LIBXML2\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${HAVE_SPDYLAY_TRUE}" && test -z "${HAVE_SPDYLAY_FALSE}"; then + as_fn_error $? "conditional \"HAVE_SPDYLAY\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${ENABLE_APP_TRUE}" && test -z "${ENABLE_APP_FALSE}"; then + as_fn_error $? "conditional \"ENABLE_APP\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${ENABLE_HPACK_TOOLS_TRUE}" && test -z "${ENABLE_HPACK_TOOLS_FALSE}"; then + as_fn_error $? "conditional \"ENABLE_HPACK_TOOLS\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${ENABLE_ASIO_LIB_TRUE}" && test -z "${ENABLE_ASIO_LIB_FALSE}"; then + as_fn_error $? "conditional \"ENABLE_ASIO_LIB\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${ENABLE_EXAMPLES_TRUE}" && test -z "${ENABLE_EXAMPLES_FALSE}"; then + as_fn_error $? "conditional \"ENABLE_EXAMPLES\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${ENABLE_PYTHON_BINDINGS_TRUE}" && test -z "${ENABLE_PYTHON_BINDINGS_FALSE}"; then + as_fn_error $? "conditional \"ENABLE_PYTHON_BINDINGS\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${HAVE_CYTHON_TRUE}" && test -z "${HAVE_CYTHON_FALSE}"; then + as_fn_error $? "conditional \"HAVE_CYTHON\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${ENABLE_FAILMALLOC_TRUE}" && test -z "${ENABLE_FAILMALLOC_FALSE}"; then + as_fn_error $? "conditional \"ENABLE_FAILMALLOC\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi + +if test -z "${ENABLE_TINY_NGHTTPD_TRUE}" && test -z "${ENABLE_TINY_NGHTTPD_FALSE}"; then + as_fn_error $? "conditional \"ENABLE_TINY_NGHTTPD\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi + +: "${CONFIG_STATUS=./config.status}" +ac_write_fail=0 +ac_clean_files_save=$ac_clean_files +ac_clean_files="$ac_clean_files $CONFIG_STATUS" +{ $as_echo "$as_me:${as_lineno-$LINENO}: creating $CONFIG_STATUS" >&5 +$as_echo "$as_me: creating $CONFIG_STATUS" >&6;} +as_write_fail=0 +cat >$CONFIG_STATUS <<_ASEOF || as_write_fail=1 +#! $SHELL +# Generated by $as_me. +# Run this file to recreate the current configuration. +# Compiler output produced by configure, useful for debugging +# configure, is in config.log if it exists. + +debug=false +ac_cs_recheck=false +ac_cs_silent=false + +SHELL=\${CONFIG_SHELL-$SHELL} +export SHELL +_ASEOF +cat >>$CONFIG_STATUS <<\_ASEOF || as_write_fail=1 +## -------------------- ## +## M4sh Initialization. ## +## -------------------- ## + +# Be more Bourne compatible +DUALCASE=1; export DUALCASE # for MKS sh +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : + emulate sh + NULLCMD=: + # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' + setopt NO_GLOB_SUBST +else + case `(set -o) 2>/dev/null` in #( + *posix*) : + set -o posix ;; #( + *) : + ;; +esac +fi + + +as_nl=' +' +export as_nl +# Printing a long string crashes Solaris 7 /usr/bin/printf. +as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' +as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo +as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo +# Prefer a ksh shell builtin over an external printf program on Solaris, +# but without wasting forks for bash or zsh. +if test -z "$BASH_VERSION$ZSH_VERSION" \ + && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then + as_echo='print -r --' + as_echo_n='print -rn --' +elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then + as_echo='printf %s\n' + as_echo_n='printf %s' +else + if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then + as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' + as_echo_n='/usr/ucb/echo -n' + else + as_echo_body='eval expr "X$1" : "X\\(.*\\)"' + as_echo_n_body='eval + arg=$1; + case $arg in #( + *"$as_nl"*) + expr "X$arg" : "X\\(.*\\)$as_nl"; + arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; + esac; + expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" + ' + export as_echo_n_body + as_echo_n='sh -c $as_echo_n_body as_echo' + fi + export as_echo_body + as_echo='sh -c $as_echo_body as_echo' +fi + +# The user is always right. +if test "${PATH_SEPARATOR+set}" != set; then + PATH_SEPARATOR=: + (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { + (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || + PATH_SEPARATOR=';' + } +fi + + +# IFS +# We need space, tab and new line, in precisely that order. Quoting is +# there to prevent editors from complaining about space-tab. +# (If _AS_PATH_WALK were called with IFS unset, it would disable word +# splitting by setting IFS to empty value.) +IFS=" "" $as_nl" + +# Find who we are. Look in the path if we contain no directory separator. +as_myself= +case $0 in #(( + *[\\/]* ) as_myself=$0 ;; + *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break + done +IFS=$as_save_IFS + + ;; +esac +# We did not find ourselves, most probably we were run as `sh COMMAND' +# in which case we are not to be found in the path. +if test "x$as_myself" = x; then + as_myself=$0 +fi +if test ! -f "$as_myself"; then + $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 + exit 1 +fi + +# Unset variables that we do not need and which cause bugs (e.g. in +# pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1" +# suppresses any "Segmentation fault" message there. '((' could +# trigger a bug in pdksh 5.2.14. +for as_var in BASH_ENV ENV MAIL MAILPATH +do eval test x\${$as_var+set} = xset \ + && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : +done +PS1='$ ' +PS2='> ' +PS4='+ ' + +# NLS nuisances. +LC_ALL=C +export LC_ALL +LANGUAGE=C +export LANGUAGE + +# CDPATH. +(unset CDPATH) >/dev/null 2>&1 && unset CDPATH + + +# as_fn_error STATUS ERROR [LINENO LOG_FD] +# ---------------------------------------- +# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are +# provided, also output the error to LOG_FD, referencing LINENO. Then exit the +# script with STATUS, using 1 if that was 0. +as_fn_error () +{ + as_status=$1; test $as_status -eq 0 && as_status=1 + if test "$4"; then + as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 + fi + $as_echo "$as_me: error: $2" >&2 + as_fn_exit $as_status +} # as_fn_error + + +# as_fn_set_status STATUS +# ----------------------- +# Set $? to STATUS, without forking. +as_fn_set_status () +{ + return $1 +} # as_fn_set_status + +# as_fn_exit STATUS +# ----------------- +# Exit the shell with STATUS, even in a "trap 0" or "set -e" context. +as_fn_exit () +{ + set +e + as_fn_set_status $1 + exit $1 +} # as_fn_exit + +# as_fn_unset VAR +# --------------- +# Portably unset VAR. +as_fn_unset () +{ + { eval $1=; unset $1;} +} +as_unset=as_fn_unset +# as_fn_append VAR VALUE +# ---------------------- +# Append the text in VALUE to the end of the definition contained in VAR. Take +# advantage of any shell optimizations that allow amortized linear growth over +# repeated appends, instead of the typical quadratic growth present in naive +# implementations. +if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then : + eval 'as_fn_append () + { + eval $1+=\$2 + }' +else + as_fn_append () + { + eval $1=\$$1\$2 + } +fi # as_fn_append + +# as_fn_arith ARG... +# ------------------ +# Perform arithmetic evaluation on the ARGs, and store the result in the +# global $as_val. Take advantage of shells that can avoid forks. The arguments +# must be portable across $(()) and expr. +if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then : + eval 'as_fn_arith () + { + as_val=$(( $* )) + }' +else + as_fn_arith () + { + as_val=`expr "$@" || test $? -eq 1` + } +fi # as_fn_arith + + +if expr a : '\(a\)' >/dev/null 2>&1 && + test "X`expr 00001 : '.*\(...\)'`" = X001; then + as_expr=expr +else + as_expr=false +fi + +if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then + as_basename=basename +else + as_basename=false +fi + +if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then + as_dirname=dirname +else + as_dirname=false +fi + +as_me=`$as_basename -- "$0" || +$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X/"$0" | + sed '/^.*\/\([^/][^/]*\)\/*$/{ + s//\1/ + q + } + /^X\/\(\/\/\)$/{ + s//\1/ + q + } + /^X\/\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + +# Avoid depending upon Character Ranges. +as_cr_letters='abcdefghijklmnopqrstuvwxyz' +as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' +as_cr_Letters=$as_cr_letters$as_cr_LETTERS +as_cr_digits='0123456789' +as_cr_alnum=$as_cr_Letters$as_cr_digits + +ECHO_C= ECHO_N= ECHO_T= +case `echo -n x` in #((((( +-n*) + case `echo 'xy\c'` in + *c*) ECHO_T=' ';; # ECHO_T is single tab character. + xy) ECHO_C='\c';; + *) echo `echo ksh88 bug on AIX 6.1` > /dev/null + ECHO_T=' ';; + esac;; +*) + ECHO_N='-n';; +esac + +rm -f conf$$ conf$$.exe conf$$.file +if test -d conf$$.dir; then + rm -f conf$$.dir/conf$$.file +else + rm -f conf$$.dir + mkdir conf$$.dir 2>/dev/null +fi +if (echo >conf$$.file) 2>/dev/null; then + if ln -s conf$$.file conf$$ 2>/dev/null; then + as_ln_s='ln -s' + # ... but there are two gotchas: + # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. + # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. + # In both cases, we have to default to `cp -pR'. + ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || + as_ln_s='cp -pR' + elif ln conf$$.file conf$$ 2>/dev/null; then + as_ln_s=ln + else + as_ln_s='cp -pR' + fi +else + as_ln_s='cp -pR' +fi +rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file +rmdir conf$$.dir 2>/dev/null + + +# as_fn_mkdir_p +# ------------- +# Create "$as_dir" as a directory, including parents if necessary. +as_fn_mkdir_p () +{ + + case $as_dir in #( + -*) as_dir=./$as_dir;; + esac + test -d "$as_dir" || eval $as_mkdir_p || { + as_dirs= + while :; do + case $as_dir in #( + *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( + *) as_qdir=$as_dir;; + esac + as_dirs="'$as_qdir' $as_dirs" + as_dir=`$as_dirname -- "$as_dir" || +$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_dir" : 'X\(//\)[^/]' \| \ + X"$as_dir" : 'X\(//\)$' \| \ + X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$as_dir" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + test -d "$as_dir" && break + done + test -z "$as_dirs" || eval "mkdir $as_dirs" + } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" + + +} # as_fn_mkdir_p +if mkdir -p . 2>/dev/null; then + as_mkdir_p='mkdir -p "$as_dir"' +else + test -d ./-p && rmdir ./-p + as_mkdir_p=false +fi + + +# as_fn_executable_p FILE +# ----------------------- +# Test if FILE is an executable regular file. +as_fn_executable_p () +{ + test -f "$1" && test -x "$1" +} # as_fn_executable_p +as_test_x='test -x' +as_executable_p=as_fn_executable_p + +# Sed expression to map a string onto a valid CPP name. +as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" + +# Sed expression to map a string onto a valid variable name. +as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" + + +exec 6>&1 +## ----------------------------------- ## +## Main body of $CONFIG_STATUS script. ## +## ----------------------------------- ## +_ASEOF +test $as_write_fail = 0 && chmod +x $CONFIG_STATUS || ac_write_fail=1 + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +# Save the log message, to keep $0 and so on meaningful, and to +# report actual input values of CONFIG_FILES etc. instead of their +# values after options handling. +ac_log=" +This file was extended by nghttp2 $as_me 1.0.0, which was +generated by GNU Autoconf 2.69. Invocation command line was + + CONFIG_FILES = $CONFIG_FILES + CONFIG_HEADERS = $CONFIG_HEADERS + CONFIG_LINKS = $CONFIG_LINKS + CONFIG_COMMANDS = $CONFIG_COMMANDS + $ $0 $@ + +on `(hostname || uname -n) 2>/dev/null | sed 1q` +" + +_ACEOF + +case $ac_config_files in *" +"*) set x $ac_config_files; shift; ac_config_files=$*;; +esac + +case $ac_config_headers in *" +"*) set x $ac_config_headers; shift; ac_config_headers=$*;; +esac + + +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +# Files that config.status was made for. +config_files="$ac_config_files" +config_headers="$ac_config_headers" +config_commands="$ac_config_commands" + +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +ac_cs_usage="\ +\`$as_me' instantiates files and other configuration actions +from templates according to the current configuration. Unless the files +and actions are specified as TAGs, all are instantiated by default. + +Usage: $0 [OPTION]... [TAG]... + + -h, --help print this help, then exit + -V, --version print version number and configuration settings, then exit + --config print configuration, then exit + -q, --quiet, --silent + do not print progress messages + -d, --debug don't remove temporary files + --recheck update $as_me by reconfiguring in the same conditions + --file=FILE[:TEMPLATE] + instantiate the configuration file FILE + --header=FILE[:TEMPLATE] + instantiate the configuration header FILE + +Configuration files: +$config_files + +Configuration headers: +$config_headers + +Configuration commands: +$config_commands + +Report bugs to ." + +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" +ac_cs_version="\\ +nghttp2 config.status 1.0.0 +configured by $0, generated by GNU Autoconf 2.69, + with options \\"\$ac_cs_config\\" + +Copyright (C) 2012 Free Software Foundation, Inc. +This config.status script is free software; the Free Software Foundation +gives unlimited permission to copy, distribute and modify it." + +ac_pwd='$ac_pwd' +srcdir='$srcdir' +INSTALL='$INSTALL' +MKDIR_P='$MKDIR_P' +AWK='$AWK' +test -n "\$AWK" || AWK=awk +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +# The default lists apply if the user does not specify any file. +ac_need_defaults=: +while test $# != 0 +do + case $1 in + --*=?*) + ac_option=`expr "X$1" : 'X\([^=]*\)='` + ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'` + ac_shift=: + ;; + --*=) + ac_option=`expr "X$1" : 'X\([^=]*\)='` + ac_optarg= + ac_shift=: + ;; + *) + ac_option=$1 + ac_optarg=$2 + ac_shift=shift + ;; + esac + + case $ac_option in + # Handling of the options. + -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) + ac_cs_recheck=: ;; + --version | --versio | --versi | --vers | --ver | --ve | --v | -V ) + $as_echo "$ac_cs_version"; exit ;; + --config | --confi | --conf | --con | --co | --c ) + $as_echo "$ac_cs_config"; exit ;; + --debug | --debu | --deb | --de | --d | -d ) + debug=: ;; + --file | --fil | --fi | --f ) + $ac_shift + case $ac_optarg in + *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; + '') as_fn_error $? "missing file argument" ;; + esac + as_fn_append CONFIG_FILES " '$ac_optarg'" + ac_need_defaults=false;; + --header | --heade | --head | --hea ) + $ac_shift + case $ac_optarg in + *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; + esac + as_fn_append CONFIG_HEADERS " '$ac_optarg'" + ac_need_defaults=false;; + --he | --h) + # Conflict between --help and --header + as_fn_error $? "ambiguous option: \`$1' +Try \`$0 --help' for more information.";; + --help | --hel | -h ) + $as_echo "$ac_cs_usage"; exit ;; + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil | --si | --s) + ac_cs_silent=: ;; + + # This is an error. + -*) as_fn_error $? "unrecognized option: \`$1' +Try \`$0 --help' for more information." ;; + + *) as_fn_append ac_config_targets " $1" + ac_need_defaults=false ;; + + esac + shift +done + +ac_configure_extra_args= + +if $ac_cs_silent; then + exec 6>/dev/null + ac_configure_extra_args="$ac_configure_extra_args --silent" +fi + +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +if \$ac_cs_recheck; then + set X $SHELL '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion + shift + \$as_echo "running CONFIG_SHELL=$SHELL \$*" >&6 + CONFIG_SHELL='$SHELL' + export CONFIG_SHELL + exec "\$@" +fi + +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +exec 5>>config.log +{ + echo + sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX +## Running $as_me. ## +_ASBOX + $as_echo "$ac_log" +} >&5 + +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +# +# INIT-COMMANDS +# + + +# The HP-UX ksh and POSIX shell print the target directory to stdout +# if CDPATH is set. +(unset CDPATH) >/dev/null 2>&1 && unset CDPATH + +sed_quote_subst='$sed_quote_subst' +double_quote_subst='$double_quote_subst' +delay_variable_subst='$delay_variable_subst' +macro_version='`$ECHO "$macro_version" | $SED "$delay_single_quote_subst"`' +macro_revision='`$ECHO "$macro_revision" | $SED "$delay_single_quote_subst"`' +enable_shared='`$ECHO "$enable_shared" | $SED "$delay_single_quote_subst"`' +enable_static='`$ECHO "$enable_static" | $SED "$delay_single_quote_subst"`' +pic_mode='`$ECHO "$pic_mode" | $SED "$delay_single_quote_subst"`' +enable_fast_install='`$ECHO "$enable_fast_install" | $SED "$delay_single_quote_subst"`' +SHELL='`$ECHO "$SHELL" | $SED "$delay_single_quote_subst"`' +ECHO='`$ECHO "$ECHO" | $SED "$delay_single_quote_subst"`' +PATH_SEPARATOR='`$ECHO "$PATH_SEPARATOR" | $SED "$delay_single_quote_subst"`' +host_alias='`$ECHO "$host_alias" | $SED "$delay_single_quote_subst"`' +host='`$ECHO "$host" | $SED "$delay_single_quote_subst"`' +host_os='`$ECHO "$host_os" | $SED "$delay_single_quote_subst"`' +build_alias='`$ECHO "$build_alias" | $SED "$delay_single_quote_subst"`' +build='`$ECHO "$build" | $SED "$delay_single_quote_subst"`' +build_os='`$ECHO "$build_os" | $SED "$delay_single_quote_subst"`' +SED='`$ECHO "$SED" | $SED "$delay_single_quote_subst"`' +Xsed='`$ECHO "$Xsed" | $SED "$delay_single_quote_subst"`' +GREP='`$ECHO "$GREP" | $SED "$delay_single_quote_subst"`' +EGREP='`$ECHO "$EGREP" | $SED "$delay_single_quote_subst"`' +FGREP='`$ECHO "$FGREP" | $SED "$delay_single_quote_subst"`' +LD='`$ECHO "$LD" | $SED "$delay_single_quote_subst"`' +NM='`$ECHO "$NM" | $SED "$delay_single_quote_subst"`' +LN_S='`$ECHO "$LN_S" | $SED "$delay_single_quote_subst"`' +max_cmd_len='`$ECHO "$max_cmd_len" | $SED "$delay_single_quote_subst"`' +ac_objext='`$ECHO "$ac_objext" | $SED "$delay_single_quote_subst"`' +exeext='`$ECHO "$exeext" | $SED "$delay_single_quote_subst"`' +lt_unset='`$ECHO "$lt_unset" | $SED "$delay_single_quote_subst"`' +lt_SP2NL='`$ECHO "$lt_SP2NL" | $SED "$delay_single_quote_subst"`' +lt_NL2SP='`$ECHO "$lt_NL2SP" | $SED "$delay_single_quote_subst"`' +lt_cv_to_host_file_cmd='`$ECHO "$lt_cv_to_host_file_cmd" | $SED "$delay_single_quote_subst"`' +lt_cv_to_tool_file_cmd='`$ECHO "$lt_cv_to_tool_file_cmd" | $SED "$delay_single_quote_subst"`' +reload_flag='`$ECHO "$reload_flag" | $SED "$delay_single_quote_subst"`' +reload_cmds='`$ECHO "$reload_cmds" | $SED "$delay_single_quote_subst"`' +OBJDUMP='`$ECHO "$OBJDUMP" | $SED "$delay_single_quote_subst"`' +deplibs_check_method='`$ECHO "$deplibs_check_method" | $SED "$delay_single_quote_subst"`' +file_magic_cmd='`$ECHO "$file_magic_cmd" | $SED "$delay_single_quote_subst"`' +file_magic_glob='`$ECHO "$file_magic_glob" | $SED "$delay_single_quote_subst"`' +want_nocaseglob='`$ECHO "$want_nocaseglob" | $SED "$delay_single_quote_subst"`' +DLLTOOL='`$ECHO "$DLLTOOL" | $SED "$delay_single_quote_subst"`' +sharedlib_from_linklib_cmd='`$ECHO "$sharedlib_from_linklib_cmd" | $SED "$delay_single_quote_subst"`' +AR='`$ECHO "$AR" | $SED "$delay_single_quote_subst"`' +AR_FLAGS='`$ECHO "$AR_FLAGS" | $SED "$delay_single_quote_subst"`' +archiver_list_spec='`$ECHO "$archiver_list_spec" | $SED "$delay_single_quote_subst"`' +STRIP='`$ECHO "$STRIP" | $SED "$delay_single_quote_subst"`' +RANLIB='`$ECHO "$RANLIB" | $SED "$delay_single_quote_subst"`' +old_postinstall_cmds='`$ECHO "$old_postinstall_cmds" | $SED "$delay_single_quote_subst"`' +old_postuninstall_cmds='`$ECHO "$old_postuninstall_cmds" | $SED "$delay_single_quote_subst"`' +old_archive_cmds='`$ECHO "$old_archive_cmds" | $SED "$delay_single_quote_subst"`' +lock_old_archive_extraction='`$ECHO "$lock_old_archive_extraction" | $SED "$delay_single_quote_subst"`' +CC='`$ECHO "$CC" | $SED "$delay_single_quote_subst"`' +CFLAGS='`$ECHO "$CFLAGS" | $SED "$delay_single_quote_subst"`' +compiler='`$ECHO "$compiler" | $SED "$delay_single_quote_subst"`' +GCC='`$ECHO "$GCC" | $SED "$delay_single_quote_subst"`' +lt_cv_sys_global_symbol_pipe='`$ECHO "$lt_cv_sys_global_symbol_pipe" | $SED "$delay_single_quote_subst"`' +lt_cv_sys_global_symbol_to_cdecl='`$ECHO "$lt_cv_sys_global_symbol_to_cdecl" | $SED "$delay_single_quote_subst"`' +lt_cv_sys_global_symbol_to_c_name_address='`$ECHO "$lt_cv_sys_global_symbol_to_c_name_address" | $SED "$delay_single_quote_subst"`' +lt_cv_sys_global_symbol_to_c_name_address_lib_prefix='`$ECHO "$lt_cv_sys_global_symbol_to_c_name_address_lib_prefix" | $SED "$delay_single_quote_subst"`' +nm_file_list_spec='`$ECHO "$nm_file_list_spec" | $SED "$delay_single_quote_subst"`' +lt_sysroot='`$ECHO "$lt_sysroot" | $SED "$delay_single_quote_subst"`' +objdir='`$ECHO "$objdir" | $SED "$delay_single_quote_subst"`' +MAGIC_CMD='`$ECHO "$MAGIC_CMD" | $SED "$delay_single_quote_subst"`' +lt_prog_compiler_no_builtin_flag='`$ECHO "$lt_prog_compiler_no_builtin_flag" | $SED "$delay_single_quote_subst"`' +lt_prog_compiler_pic='`$ECHO "$lt_prog_compiler_pic" | $SED "$delay_single_quote_subst"`' +lt_prog_compiler_wl='`$ECHO "$lt_prog_compiler_wl" | $SED "$delay_single_quote_subst"`' +lt_prog_compiler_static='`$ECHO "$lt_prog_compiler_static" | $SED "$delay_single_quote_subst"`' +lt_cv_prog_compiler_c_o='`$ECHO "$lt_cv_prog_compiler_c_o" | $SED "$delay_single_quote_subst"`' +need_locks='`$ECHO "$need_locks" | $SED "$delay_single_quote_subst"`' +MANIFEST_TOOL='`$ECHO "$MANIFEST_TOOL" | $SED "$delay_single_quote_subst"`' +DSYMUTIL='`$ECHO "$DSYMUTIL" | $SED "$delay_single_quote_subst"`' +NMEDIT='`$ECHO "$NMEDIT" | $SED "$delay_single_quote_subst"`' +LIPO='`$ECHO "$LIPO" | $SED "$delay_single_quote_subst"`' +OTOOL='`$ECHO "$OTOOL" | $SED "$delay_single_quote_subst"`' +OTOOL64='`$ECHO "$OTOOL64" | $SED "$delay_single_quote_subst"`' +libext='`$ECHO "$libext" | $SED "$delay_single_quote_subst"`' +shrext_cmds='`$ECHO "$shrext_cmds" | $SED "$delay_single_quote_subst"`' +extract_expsyms_cmds='`$ECHO "$extract_expsyms_cmds" | $SED "$delay_single_quote_subst"`' +archive_cmds_need_lc='`$ECHO "$archive_cmds_need_lc" | $SED "$delay_single_quote_subst"`' +enable_shared_with_static_runtimes='`$ECHO "$enable_shared_with_static_runtimes" | $SED "$delay_single_quote_subst"`' +export_dynamic_flag_spec='`$ECHO "$export_dynamic_flag_spec" | $SED "$delay_single_quote_subst"`' +whole_archive_flag_spec='`$ECHO "$whole_archive_flag_spec" | $SED "$delay_single_quote_subst"`' +compiler_needs_object='`$ECHO "$compiler_needs_object" | $SED "$delay_single_quote_subst"`' +old_archive_from_new_cmds='`$ECHO "$old_archive_from_new_cmds" | $SED "$delay_single_quote_subst"`' +old_archive_from_expsyms_cmds='`$ECHO "$old_archive_from_expsyms_cmds" | $SED "$delay_single_quote_subst"`' +archive_cmds='`$ECHO "$archive_cmds" | $SED "$delay_single_quote_subst"`' +archive_expsym_cmds='`$ECHO "$archive_expsym_cmds" | $SED "$delay_single_quote_subst"`' +module_cmds='`$ECHO "$module_cmds" | $SED "$delay_single_quote_subst"`' +module_expsym_cmds='`$ECHO "$module_expsym_cmds" | $SED "$delay_single_quote_subst"`' +with_gnu_ld='`$ECHO "$with_gnu_ld" | $SED "$delay_single_quote_subst"`' +allow_undefined_flag='`$ECHO "$allow_undefined_flag" | $SED "$delay_single_quote_subst"`' +no_undefined_flag='`$ECHO "$no_undefined_flag" | $SED "$delay_single_quote_subst"`' +hardcode_libdir_flag_spec='`$ECHO "$hardcode_libdir_flag_spec" | $SED "$delay_single_quote_subst"`' +hardcode_libdir_separator='`$ECHO "$hardcode_libdir_separator" | $SED "$delay_single_quote_subst"`' +hardcode_direct='`$ECHO "$hardcode_direct" | $SED "$delay_single_quote_subst"`' +hardcode_direct_absolute='`$ECHO "$hardcode_direct_absolute" | $SED "$delay_single_quote_subst"`' +hardcode_minus_L='`$ECHO "$hardcode_minus_L" | $SED "$delay_single_quote_subst"`' +hardcode_shlibpath_var='`$ECHO "$hardcode_shlibpath_var" | $SED "$delay_single_quote_subst"`' +hardcode_automatic='`$ECHO "$hardcode_automatic" | $SED "$delay_single_quote_subst"`' +inherit_rpath='`$ECHO "$inherit_rpath" | $SED "$delay_single_quote_subst"`' +link_all_deplibs='`$ECHO "$link_all_deplibs" | $SED "$delay_single_quote_subst"`' +always_export_symbols='`$ECHO "$always_export_symbols" | $SED "$delay_single_quote_subst"`' +export_symbols_cmds='`$ECHO "$export_symbols_cmds" | $SED "$delay_single_quote_subst"`' +exclude_expsyms='`$ECHO "$exclude_expsyms" | $SED "$delay_single_quote_subst"`' +include_expsyms='`$ECHO "$include_expsyms" | $SED "$delay_single_quote_subst"`' +prelink_cmds='`$ECHO "$prelink_cmds" | $SED "$delay_single_quote_subst"`' +postlink_cmds='`$ECHO "$postlink_cmds" | $SED "$delay_single_quote_subst"`' +file_list_spec='`$ECHO "$file_list_spec" | $SED "$delay_single_quote_subst"`' +variables_saved_for_relink='`$ECHO "$variables_saved_for_relink" | $SED "$delay_single_quote_subst"`' +need_lib_prefix='`$ECHO "$need_lib_prefix" | $SED "$delay_single_quote_subst"`' +need_version='`$ECHO "$need_version" | $SED "$delay_single_quote_subst"`' +version_type='`$ECHO "$version_type" | $SED "$delay_single_quote_subst"`' +runpath_var='`$ECHO "$runpath_var" | $SED "$delay_single_quote_subst"`' +shlibpath_var='`$ECHO "$shlibpath_var" | $SED "$delay_single_quote_subst"`' +shlibpath_overrides_runpath='`$ECHO "$shlibpath_overrides_runpath" | $SED "$delay_single_quote_subst"`' +libname_spec='`$ECHO "$libname_spec" | $SED "$delay_single_quote_subst"`' +library_names_spec='`$ECHO "$library_names_spec" | $SED "$delay_single_quote_subst"`' +soname_spec='`$ECHO "$soname_spec" | $SED "$delay_single_quote_subst"`' +install_override_mode='`$ECHO "$install_override_mode" | $SED "$delay_single_quote_subst"`' +postinstall_cmds='`$ECHO "$postinstall_cmds" | $SED "$delay_single_quote_subst"`' +postuninstall_cmds='`$ECHO "$postuninstall_cmds" | $SED "$delay_single_quote_subst"`' +finish_cmds='`$ECHO "$finish_cmds" | $SED "$delay_single_quote_subst"`' +finish_eval='`$ECHO "$finish_eval" | $SED "$delay_single_quote_subst"`' +hardcode_into_libs='`$ECHO "$hardcode_into_libs" | $SED "$delay_single_quote_subst"`' +sys_lib_search_path_spec='`$ECHO "$sys_lib_search_path_spec" | $SED "$delay_single_quote_subst"`' +sys_lib_dlsearch_path_spec='`$ECHO "$sys_lib_dlsearch_path_spec" | $SED "$delay_single_quote_subst"`' +hardcode_action='`$ECHO "$hardcode_action" | $SED "$delay_single_quote_subst"`' +enable_dlopen='`$ECHO "$enable_dlopen" | $SED "$delay_single_quote_subst"`' +enable_dlopen_self='`$ECHO "$enable_dlopen_self" | $SED "$delay_single_quote_subst"`' +enable_dlopen_self_static='`$ECHO "$enable_dlopen_self_static" | $SED "$delay_single_quote_subst"`' +old_striplib='`$ECHO "$old_striplib" | $SED "$delay_single_quote_subst"`' +striplib='`$ECHO "$striplib" | $SED "$delay_single_quote_subst"`' +compiler_lib_search_dirs='`$ECHO "$compiler_lib_search_dirs" | $SED "$delay_single_quote_subst"`' +predep_objects='`$ECHO "$predep_objects" | $SED "$delay_single_quote_subst"`' +postdep_objects='`$ECHO "$postdep_objects" | $SED "$delay_single_quote_subst"`' +predeps='`$ECHO "$predeps" | $SED "$delay_single_quote_subst"`' +postdeps='`$ECHO "$postdeps" | $SED "$delay_single_quote_subst"`' +compiler_lib_search_path='`$ECHO "$compiler_lib_search_path" | $SED "$delay_single_quote_subst"`' +LD_CXX='`$ECHO "$LD_CXX" | $SED "$delay_single_quote_subst"`' +reload_flag_CXX='`$ECHO "$reload_flag_CXX" | $SED "$delay_single_quote_subst"`' +reload_cmds_CXX='`$ECHO "$reload_cmds_CXX" | $SED "$delay_single_quote_subst"`' +old_archive_cmds_CXX='`$ECHO "$old_archive_cmds_CXX" | $SED "$delay_single_quote_subst"`' +compiler_CXX='`$ECHO "$compiler_CXX" | $SED "$delay_single_quote_subst"`' +GCC_CXX='`$ECHO "$GCC_CXX" | $SED "$delay_single_quote_subst"`' +lt_prog_compiler_no_builtin_flag_CXX='`$ECHO "$lt_prog_compiler_no_builtin_flag_CXX" | $SED "$delay_single_quote_subst"`' +lt_prog_compiler_pic_CXX='`$ECHO "$lt_prog_compiler_pic_CXX" | $SED "$delay_single_quote_subst"`' +lt_prog_compiler_wl_CXX='`$ECHO "$lt_prog_compiler_wl_CXX" | $SED "$delay_single_quote_subst"`' +lt_prog_compiler_static_CXX='`$ECHO "$lt_prog_compiler_static_CXX" | $SED "$delay_single_quote_subst"`' +lt_cv_prog_compiler_c_o_CXX='`$ECHO "$lt_cv_prog_compiler_c_o_CXX" | $SED "$delay_single_quote_subst"`' +archive_cmds_need_lc_CXX='`$ECHO "$archive_cmds_need_lc_CXX" | $SED "$delay_single_quote_subst"`' +enable_shared_with_static_runtimes_CXX='`$ECHO "$enable_shared_with_static_runtimes_CXX" | $SED "$delay_single_quote_subst"`' +export_dynamic_flag_spec_CXX='`$ECHO "$export_dynamic_flag_spec_CXX" | $SED "$delay_single_quote_subst"`' +whole_archive_flag_spec_CXX='`$ECHO "$whole_archive_flag_spec_CXX" | $SED "$delay_single_quote_subst"`' +compiler_needs_object_CXX='`$ECHO "$compiler_needs_object_CXX" | $SED "$delay_single_quote_subst"`' +old_archive_from_new_cmds_CXX='`$ECHO "$old_archive_from_new_cmds_CXX" | $SED "$delay_single_quote_subst"`' +old_archive_from_expsyms_cmds_CXX='`$ECHO "$old_archive_from_expsyms_cmds_CXX" | $SED "$delay_single_quote_subst"`' +archive_cmds_CXX='`$ECHO "$archive_cmds_CXX" | $SED "$delay_single_quote_subst"`' +archive_expsym_cmds_CXX='`$ECHO "$archive_expsym_cmds_CXX" | $SED "$delay_single_quote_subst"`' +module_cmds_CXX='`$ECHO "$module_cmds_CXX" | $SED "$delay_single_quote_subst"`' +module_expsym_cmds_CXX='`$ECHO "$module_expsym_cmds_CXX" | $SED "$delay_single_quote_subst"`' +with_gnu_ld_CXX='`$ECHO "$with_gnu_ld_CXX" | $SED "$delay_single_quote_subst"`' +allow_undefined_flag_CXX='`$ECHO "$allow_undefined_flag_CXX" | $SED "$delay_single_quote_subst"`' +no_undefined_flag_CXX='`$ECHO "$no_undefined_flag_CXX" | $SED "$delay_single_quote_subst"`' +hardcode_libdir_flag_spec_CXX='`$ECHO "$hardcode_libdir_flag_spec_CXX" | $SED "$delay_single_quote_subst"`' +hardcode_libdir_separator_CXX='`$ECHO "$hardcode_libdir_separator_CXX" | $SED "$delay_single_quote_subst"`' +hardcode_direct_CXX='`$ECHO "$hardcode_direct_CXX" | $SED "$delay_single_quote_subst"`' +hardcode_direct_absolute_CXX='`$ECHO "$hardcode_direct_absolute_CXX" | $SED "$delay_single_quote_subst"`' +hardcode_minus_L_CXX='`$ECHO "$hardcode_minus_L_CXX" | $SED "$delay_single_quote_subst"`' +hardcode_shlibpath_var_CXX='`$ECHO "$hardcode_shlibpath_var_CXX" | $SED "$delay_single_quote_subst"`' +hardcode_automatic_CXX='`$ECHO "$hardcode_automatic_CXX" | $SED "$delay_single_quote_subst"`' +inherit_rpath_CXX='`$ECHO "$inherit_rpath_CXX" | $SED "$delay_single_quote_subst"`' +link_all_deplibs_CXX='`$ECHO "$link_all_deplibs_CXX" | $SED "$delay_single_quote_subst"`' +always_export_symbols_CXX='`$ECHO "$always_export_symbols_CXX" | $SED "$delay_single_quote_subst"`' +export_symbols_cmds_CXX='`$ECHO "$export_symbols_cmds_CXX" | $SED "$delay_single_quote_subst"`' +exclude_expsyms_CXX='`$ECHO "$exclude_expsyms_CXX" | $SED "$delay_single_quote_subst"`' +include_expsyms_CXX='`$ECHO "$include_expsyms_CXX" | $SED "$delay_single_quote_subst"`' +prelink_cmds_CXX='`$ECHO "$prelink_cmds_CXX" | $SED "$delay_single_quote_subst"`' +postlink_cmds_CXX='`$ECHO "$postlink_cmds_CXX" | $SED "$delay_single_quote_subst"`' +file_list_spec_CXX='`$ECHO "$file_list_spec_CXX" | $SED "$delay_single_quote_subst"`' +hardcode_action_CXX='`$ECHO "$hardcode_action_CXX" | $SED "$delay_single_quote_subst"`' +compiler_lib_search_dirs_CXX='`$ECHO "$compiler_lib_search_dirs_CXX" | $SED "$delay_single_quote_subst"`' +predep_objects_CXX='`$ECHO "$predep_objects_CXX" | $SED "$delay_single_quote_subst"`' +postdep_objects_CXX='`$ECHO "$postdep_objects_CXX" | $SED "$delay_single_quote_subst"`' +predeps_CXX='`$ECHO "$predeps_CXX" | $SED "$delay_single_quote_subst"`' +postdeps_CXX='`$ECHO "$postdeps_CXX" | $SED "$delay_single_quote_subst"`' +compiler_lib_search_path_CXX='`$ECHO "$compiler_lib_search_path_CXX" | $SED "$delay_single_quote_subst"`' + +LTCC='$LTCC' +LTCFLAGS='$LTCFLAGS' +compiler='$compiler_DEFAULT' + +# A function that is used when there is no print builtin or printf. +func_fallback_echo () +{ + eval 'cat <<_LTECHO_EOF +\$1 +_LTECHO_EOF' +} + +# Quote evaled strings. +for var in SHELL \ +ECHO \ +PATH_SEPARATOR \ +SED \ +GREP \ +EGREP \ +FGREP \ +LD \ +NM \ +LN_S \ +lt_SP2NL \ +lt_NL2SP \ +reload_flag \ +OBJDUMP \ +deplibs_check_method \ +file_magic_cmd \ +file_magic_glob \ +want_nocaseglob \ +DLLTOOL \ +sharedlib_from_linklib_cmd \ +AR \ +AR_FLAGS \ +archiver_list_spec \ +STRIP \ +RANLIB \ +CC \ +CFLAGS \ +compiler \ +lt_cv_sys_global_symbol_pipe \ +lt_cv_sys_global_symbol_to_cdecl \ +lt_cv_sys_global_symbol_to_c_name_address \ +lt_cv_sys_global_symbol_to_c_name_address_lib_prefix \ +nm_file_list_spec \ +lt_prog_compiler_no_builtin_flag \ +lt_prog_compiler_pic \ +lt_prog_compiler_wl \ +lt_prog_compiler_static \ +lt_cv_prog_compiler_c_o \ +need_locks \ +MANIFEST_TOOL \ +DSYMUTIL \ +NMEDIT \ +LIPO \ +OTOOL \ +OTOOL64 \ +shrext_cmds \ +export_dynamic_flag_spec \ +whole_archive_flag_spec \ +compiler_needs_object \ +with_gnu_ld \ +allow_undefined_flag \ +no_undefined_flag \ +hardcode_libdir_flag_spec \ +hardcode_libdir_separator \ +exclude_expsyms \ +include_expsyms \ +file_list_spec \ +variables_saved_for_relink \ +libname_spec \ +library_names_spec \ +soname_spec \ +install_override_mode \ +finish_eval \ +old_striplib \ +striplib \ +compiler_lib_search_dirs \ +predep_objects \ +postdep_objects \ +predeps \ +postdeps \ +compiler_lib_search_path \ +LD_CXX \ +reload_flag_CXX \ +compiler_CXX \ +lt_prog_compiler_no_builtin_flag_CXX \ +lt_prog_compiler_pic_CXX \ +lt_prog_compiler_wl_CXX \ +lt_prog_compiler_static_CXX \ +lt_cv_prog_compiler_c_o_CXX \ +export_dynamic_flag_spec_CXX \ +whole_archive_flag_spec_CXX \ +compiler_needs_object_CXX \ +with_gnu_ld_CXX \ +allow_undefined_flag_CXX \ +no_undefined_flag_CXX \ +hardcode_libdir_flag_spec_CXX \ +hardcode_libdir_separator_CXX \ +exclude_expsyms_CXX \ +include_expsyms_CXX \ +file_list_spec_CXX \ +compiler_lib_search_dirs_CXX \ +predep_objects_CXX \ +postdep_objects_CXX \ +predeps_CXX \ +postdeps_CXX \ +compiler_lib_search_path_CXX; do + case \`eval \\\\\$ECHO \\\\""\\\\\$\$var"\\\\"\` in + *[\\\\\\\`\\"\\\$]*) + eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"\\\$\$var\\" | \\\$SED \\"\\\$sed_quote_subst\\"\\\`\\\\\\"" + ;; + *) + eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\"" + ;; + esac +done + +# Double-quote double-evaled strings. +for var in reload_cmds \ +old_postinstall_cmds \ +old_postuninstall_cmds \ +old_archive_cmds \ +extract_expsyms_cmds \ +old_archive_from_new_cmds \ +old_archive_from_expsyms_cmds \ +archive_cmds \ +archive_expsym_cmds \ +module_cmds \ +module_expsym_cmds \ +export_symbols_cmds \ +prelink_cmds \ +postlink_cmds \ +postinstall_cmds \ +postuninstall_cmds \ +finish_cmds \ +sys_lib_search_path_spec \ +sys_lib_dlsearch_path_spec \ +reload_cmds_CXX \ +old_archive_cmds_CXX \ +old_archive_from_new_cmds_CXX \ +old_archive_from_expsyms_cmds_CXX \ +archive_cmds_CXX \ +archive_expsym_cmds_CXX \ +module_cmds_CXX \ +module_expsym_cmds_CXX \ +export_symbols_cmds_CXX \ +prelink_cmds_CXX \ +postlink_cmds_CXX; do + case \`eval \\\\\$ECHO \\\\""\\\\\$\$var"\\\\"\` in + *[\\\\\\\`\\"\\\$]*) + eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"\\\$\$var\\" | \\\$SED -e \\"\\\$double_quote_subst\\" -e \\"\\\$sed_quote_subst\\" -e \\"\\\$delay_variable_subst\\"\\\`\\\\\\"" + ;; + *) + eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\"" + ;; + esac +done + +ac_aux_dir='$ac_aux_dir' +xsi_shell='$xsi_shell' +lt_shell_append='$lt_shell_append' + +# See if we are running on zsh, and set the options which allow our +# commands through without removal of \ escapes INIT. +if test -n "\${ZSH_VERSION+set}" ; then + setopt NO_GLOB_SUBST +fi + + + PACKAGE='$PACKAGE' + VERSION='$VERSION' + TIMESTAMP='$TIMESTAMP' + RM='$RM' + ofile='$ofile' + + + + + +AMDEP_TRUE="$AMDEP_TRUE" ac_aux_dir="$ac_aux_dir" + +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 + +# Handling of arguments. +for ac_config_target in $ac_config_targets +do + case $ac_config_target in + "libtool") CONFIG_COMMANDS="$CONFIG_COMMANDS libtool" ;; + "depfiles") CONFIG_COMMANDS="$CONFIG_COMMANDS depfiles" ;; + "config.h") CONFIG_HEADERS="$CONFIG_HEADERS config.h" ;; + "Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;; + "lib/Makefile") CONFIG_FILES="$CONFIG_FILES lib/Makefile" ;; + "lib/libnghttp2.pc") CONFIG_FILES="$CONFIG_FILES lib/libnghttp2.pc" ;; + "lib/includes/Makefile") CONFIG_FILES="$CONFIG_FILES lib/includes/Makefile" ;; + "lib/includes/nghttp2/nghttp2ver.h") CONFIG_FILES="$CONFIG_FILES lib/includes/nghttp2/nghttp2ver.h" ;; + "tests/Makefile") CONFIG_FILES="$CONFIG_FILES tests/Makefile" ;; + "tests/testdata/Makefile") CONFIG_FILES="$CONFIG_FILES tests/testdata/Makefile" ;; + "third-party/Makefile") CONFIG_FILES="$CONFIG_FILES third-party/Makefile" ;; + "src/Makefile") CONFIG_FILES="$CONFIG_FILES src/Makefile" ;; + "src/includes/Makefile") CONFIG_FILES="$CONFIG_FILES src/includes/Makefile" ;; + "src/libnghttp2_asio.pc") CONFIG_FILES="$CONFIG_FILES src/libnghttp2_asio.pc" ;; + "examples/Makefile") CONFIG_FILES="$CONFIG_FILES examples/Makefile" ;; + "python/Makefile") CONFIG_FILES="$CONFIG_FILES python/Makefile" ;; + "python/setup.py") CONFIG_FILES="$CONFIG_FILES python/setup.py" ;; + "integration-tests/Makefile") CONFIG_FILES="$CONFIG_FILES integration-tests/Makefile" ;; + "integration-tests/config.go") CONFIG_FILES="$CONFIG_FILES integration-tests/config.go" ;; + "integration-tests/setenv") CONFIG_FILES="$CONFIG_FILES integration-tests/setenv" ;; + "doc/Makefile") CONFIG_FILES="$CONFIG_FILES doc/Makefile" ;; + "doc/conf.py") CONFIG_FILES="$CONFIG_FILES doc/conf.py" ;; + "doc/index.rst") CONFIG_FILES="$CONFIG_FILES doc/index.rst" ;; + "doc/package_README.rst") CONFIG_FILES="$CONFIG_FILES doc/package_README.rst" ;; + "doc/tutorial-client.rst") CONFIG_FILES="$CONFIG_FILES doc/tutorial-client.rst" ;; + "doc/tutorial-server.rst") CONFIG_FILES="$CONFIG_FILES doc/tutorial-server.rst" ;; + "doc/tutorial-hpack.rst") CONFIG_FILES="$CONFIG_FILES doc/tutorial-hpack.rst" ;; + "doc/nghttpx-howto.rst") CONFIG_FILES="$CONFIG_FILES doc/nghttpx-howto.rst" ;; + "doc/h2load-howto.rst") CONFIG_FILES="$CONFIG_FILES doc/h2load-howto.rst" ;; + "doc/libnghttp2_asio.rst") CONFIG_FILES="$CONFIG_FILES doc/libnghttp2_asio.rst" ;; + "doc/python-apiref.rst") CONFIG_FILES="$CONFIG_FILES doc/python-apiref.rst" ;; + "doc/building-android-binary.rst") CONFIG_FILES="$CONFIG_FILES doc/building-android-binary.rst" ;; + "doc/nghttp2.h.rst") CONFIG_FILES="$CONFIG_FILES doc/nghttp2.h.rst" ;; + "doc/nghttp2ver.h.rst") CONFIG_FILES="$CONFIG_FILES doc/nghttp2ver.h.rst" ;; + "doc/asio_http2.h.rst") CONFIG_FILES="$CONFIG_FILES doc/asio_http2.h.rst" ;; + "doc/asio_http2_server.h.rst") CONFIG_FILES="$CONFIG_FILES doc/asio_http2_server.h.rst" ;; + "doc/asio_http2_client.h.rst") CONFIG_FILES="$CONFIG_FILES doc/asio_http2_client.h.rst" ;; + "doc/contribute.rst") CONFIG_FILES="$CONFIG_FILES doc/contribute.rst" ;; + "contrib/Makefile") CONFIG_FILES="$CONFIG_FILES contrib/Makefile" ;; + + *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;; + esac +done + + +# If the user did not use the arguments to specify the items to instantiate, +# then the envvar interface is used. Set only those that are not. +# We use the long form for the default assignment because of an extremely +# bizarre bug on SunOS 4.1.3. +if $ac_need_defaults; then + test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files + test "${CONFIG_HEADERS+set}" = set || CONFIG_HEADERS=$config_headers + test "${CONFIG_COMMANDS+set}" = set || CONFIG_COMMANDS=$config_commands +fi + +# Have a temporary directory for convenience. Make it in the build tree +# simply because there is no reason against having it here, and in addition, +# creating and moving files from /tmp can sometimes cause problems. +# Hook for its removal unless debugging. +# Note that there is a small window in which the directory will not be cleaned: +# after its creation but before its name has been assigned to `$tmp'. +$debug || +{ + tmp= ac_tmp= + trap 'exit_status=$? + : "${ac_tmp:=$tmp}" + { test ! -d "$ac_tmp" || rm -fr "$ac_tmp"; } && exit $exit_status +' 0 + trap 'as_fn_exit 1' 1 2 13 15 +} +# Create a (secure) tmp directory for tmp files. + +{ + tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` && + test -d "$tmp" +} || +{ + tmp=./conf$$-$RANDOM + (umask 077 && mkdir "$tmp") +} || as_fn_error $? "cannot create a temporary directory in ." "$LINENO" 5 +ac_tmp=$tmp + +# Set up the scripts for CONFIG_FILES section. +# No need to generate them if there are no CONFIG_FILES. +# This happens for instance with `./config.status config.h'. +if test -n "$CONFIG_FILES"; then + + +ac_cr=`echo X | tr X '\015'` +# On cygwin, bash can eat \r inside `` if the user requested igncr. +# But we know of no other shell where ac_cr would be empty at this +# point, so we can use a bashism as a fallback. +if test "x$ac_cr" = x; then + eval ac_cr=\$\'\\r\' +fi +ac_cs_awk_cr=`$AWK 'BEGIN { print "a\rb" }' /dev/null` +if test "$ac_cs_awk_cr" = "a${ac_cr}b"; then + ac_cs_awk_cr='\\r' +else + ac_cs_awk_cr=$ac_cr +fi + +echo 'BEGIN {' >"$ac_tmp/subs1.awk" && +_ACEOF + + +{ + echo "cat >conf$$subs.awk <<_ACEOF" && + echo "$ac_subst_vars" | sed 's/.*/&!$&$ac_delim/' && + echo "_ACEOF" +} >conf$$subs.sh || + as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 +ac_delim_num=`echo "$ac_subst_vars" | grep -c '^'` +ac_delim='%!_!# ' +for ac_last_try in false false false false false :; do + . ./conf$$subs.sh || + as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 + + ac_delim_n=`sed -n "s/.*$ac_delim\$/X/p" conf$$subs.awk | grep -c X` + if test $ac_delim_n = $ac_delim_num; then + break + elif $ac_last_try; then + as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 + else + ac_delim="$ac_delim!$ac_delim _$ac_delim!! " + fi +done +rm -f conf$$subs.sh + +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +cat >>"\$ac_tmp/subs1.awk" <<\\_ACAWK && +_ACEOF +sed -n ' +h +s/^/S["/; s/!.*/"]=/ +p +g +s/^[^!]*!// +:repl +t repl +s/'"$ac_delim"'$// +t delim +:nl +h +s/\(.\{148\}\)..*/\1/ +t more1 +s/["\\]/\\&/g; s/^/"/; s/$/\\n"\\/ +p +n +b repl +:more1 +s/["\\]/\\&/g; s/^/"/; s/$/"\\/ +p +g +s/.\{148\}// +t nl +:delim +h +s/\(.\{148\}\)..*/\1/ +t more2 +s/["\\]/\\&/g; s/^/"/; s/$/"/ +p +b +:more2 +s/["\\]/\\&/g; s/^/"/; s/$/"\\/ +p +g +s/.\{148\}// +t delim +' >$CONFIG_STATUS || ac_write_fail=1 +rm -f conf$$subs.awk +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +_ACAWK +cat >>"\$ac_tmp/subs1.awk" <<_ACAWK && + for (key in S) S_is_set[key] = 1 + FS = "" + +} +{ + line = $ 0 + nfields = split(line, field, "@") + substed = 0 + len = length(field[1]) + for (i = 2; i < nfields; i++) { + key = field[i] + keylen = length(key) + if (S_is_set[key]) { + value = S[key] + line = substr(line, 1, len) "" value "" substr(line, len + keylen + 3) + len += length(value) + length(field[++i]) + substed = 1 + } else + len += 1 + keylen + } + + print line +} + +_ACAWK +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +if sed "s/$ac_cr//" < /dev/null > /dev/null 2>&1; then + sed "s/$ac_cr\$//; s/$ac_cr/$ac_cs_awk_cr/g" +else + cat +fi < "$ac_tmp/subs1.awk" > "$ac_tmp/subs.awk" \ + || as_fn_error $? "could not setup config files machinery" "$LINENO" 5 +_ACEOF + +# VPATH may cause trouble with some makes, so we remove sole $(srcdir), +# ${srcdir} and @srcdir@ entries from VPATH if srcdir is ".", strip leading and +# trailing colons and then remove the whole line if VPATH becomes empty +# (actually we leave an empty line to preserve line numbers). +if test "x$srcdir" = x.; then + ac_vpsub='/^[ ]*VPATH[ ]*=[ ]*/{ +h +s/// +s/^/:/ +s/[ ]*$/:/ +s/:\$(srcdir):/:/g +s/:\${srcdir}:/:/g +s/:@srcdir@:/:/g +s/^:*// +s/:*$// +x +s/\(=[ ]*\).*/\1/ +G +s/\n// +s/^[^=]*=[ ]*$// +}' +fi + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +fi # test -n "$CONFIG_FILES" + +# Set up the scripts for CONFIG_HEADERS section. +# No need to generate them if there are no CONFIG_HEADERS. +# This happens for instance with `./config.status Makefile'. +if test -n "$CONFIG_HEADERS"; then +cat >"$ac_tmp/defines.awk" <<\_ACAWK || +BEGIN { +_ACEOF + +# Transform confdefs.h into an awk script `defines.awk', embedded as +# here-document in config.status, that substitutes the proper values into +# config.h.in to produce config.h. + +# Create a delimiter string that does not exist in confdefs.h, to ease +# handling of long lines. +ac_delim='%!_!# ' +for ac_last_try in false false :; do + ac_tt=`sed -n "/$ac_delim/p" confdefs.h` + if test -z "$ac_tt"; then + break + elif $ac_last_try; then + as_fn_error $? "could not make $CONFIG_HEADERS" "$LINENO" 5 + else + ac_delim="$ac_delim!$ac_delim _$ac_delim!! " + fi +done + +# For the awk script, D is an array of macro values keyed by name, +# likewise P contains macro parameters if any. Preserve backslash +# newline sequences. + +ac_word_re=[_$as_cr_Letters][_$as_cr_alnum]* +sed -n ' +s/.\{148\}/&'"$ac_delim"'/g +t rset +:rset +s/^[ ]*#[ ]*define[ ][ ]*/ / +t def +d +:def +s/\\$// +t bsnl +s/["\\]/\\&/g +s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\ +D["\1"]=" \3"/p +s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2"/p +d +:bsnl +s/["\\]/\\&/g +s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\ +D["\1"]=" \3\\\\\\n"\\/p +t cont +s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2\\\\\\n"\\/p +t cont +d +:cont +n +s/.\{148\}/&'"$ac_delim"'/g +t clear +:clear +s/\\$// +t bsnlc +s/["\\]/\\&/g; s/^/"/; s/$/"/p +d +:bsnlc +s/["\\]/\\&/g; s/^/"/; s/$/\\\\\\n"\\/p +b cont +' >$CONFIG_STATUS || ac_write_fail=1 + +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 + for (key in D) D_is_set[key] = 1 + FS = "" +} +/^[\t ]*#[\t ]*(define|undef)[\t ]+$ac_word_re([\t (]|\$)/ { + line = \$ 0 + split(line, arg, " ") + if (arg[1] == "#") { + defundef = arg[2] + mac1 = arg[3] + } else { + defundef = substr(arg[1], 2) + mac1 = arg[2] + } + split(mac1, mac2, "(") #) + macro = mac2[1] + prefix = substr(line, 1, index(line, defundef) - 1) + if (D_is_set[macro]) { + # Preserve the white space surrounding the "#". + print prefix "define", macro P[macro] D[macro] + next + } else { + # Replace #undef with comments. This is necessary, for example, + # in the case of _POSIX_SOURCE, which is predefined and required + # on some systems where configure will not decide to define it. + if (defundef == "undef") { + print "/*", prefix defundef, macro, "*/" + next + } + } +} +{ print } +_ACAWK +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 + as_fn_error $? "could not setup config headers machinery" "$LINENO" 5 +fi # test -n "$CONFIG_HEADERS" + + +eval set X " :F $CONFIG_FILES :H $CONFIG_HEADERS :C $CONFIG_COMMANDS" +shift +for ac_tag +do + case $ac_tag in + :[FHLC]) ac_mode=$ac_tag; continue;; + esac + case $ac_mode$ac_tag in + :[FHL]*:*);; + :L* | :C*:*) as_fn_error $? "invalid tag \`$ac_tag'" "$LINENO" 5;; + :[FH]-) ac_tag=-:-;; + :[FH]*) ac_tag=$ac_tag:$ac_tag.in;; + esac + ac_save_IFS=$IFS + IFS=: + set x $ac_tag + IFS=$ac_save_IFS + shift + ac_file=$1 + shift + + case $ac_mode in + :L) ac_source=$1;; + :[FH]) + ac_file_inputs= + for ac_f + do + case $ac_f in + -) ac_f="$ac_tmp/stdin";; + *) # Look for the file first in the build tree, then in the source tree + # (if the path is not absolute). The absolute path cannot be DOS-style, + # because $ac_f cannot contain `:'. + test -f "$ac_f" || + case $ac_f in + [\\/$]*) false;; + *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";; + esac || + as_fn_error 1 "cannot find input file: \`$ac_f'" "$LINENO" 5;; + esac + case $ac_f in *\'*) ac_f=`$as_echo "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac + as_fn_append ac_file_inputs " '$ac_f'" + done + + # Let's still pretend it is `configure' which instantiates (i.e., don't + # use $as_me), people would be surprised to read: + # /* config.h. Generated by config.status. */ + configure_input='Generated from '` + $as_echo "$*" | sed 's|^[^:]*/||;s|:[^:]*/|, |g' + `' by configure.' + if test x"$ac_file" != x-; then + configure_input="$ac_file. $configure_input" + { $as_echo "$as_me:${as_lineno-$LINENO}: creating $ac_file" >&5 +$as_echo "$as_me: creating $ac_file" >&6;} + fi + # Neutralize special characters interpreted by sed in replacement strings. + case $configure_input in #( + *\&* | *\|* | *\\* ) + ac_sed_conf_input=`$as_echo "$configure_input" | + sed 's/[\\\\&|]/\\\\&/g'`;; #( + *) ac_sed_conf_input=$configure_input;; + esac + + case $ac_tag in + *:-:* | *:-) cat >"$ac_tmp/stdin" \ + || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;; + esac + ;; + esac + + ac_dir=`$as_dirname -- "$ac_file" || +$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$ac_file" : 'X\(//\)[^/]' \| \ + X"$ac_file" : 'X\(//\)$' \| \ + X"$ac_file" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$ac_file" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + as_dir="$ac_dir"; as_fn_mkdir_p + ac_builddir=. + +case "$ac_dir" in +.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; +*) + ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` + # A ".." for each directory in $ac_dir_suffix. + ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` + case $ac_top_builddir_sub in + "") ac_top_builddir_sub=. ac_top_build_prefix= ;; + *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; + esac ;; +esac +ac_abs_top_builddir=$ac_pwd +ac_abs_builddir=$ac_pwd$ac_dir_suffix +# for backward compatibility: +ac_top_builddir=$ac_top_build_prefix + +case $srcdir in + .) # We are building in place. + ac_srcdir=. + ac_top_srcdir=$ac_top_builddir_sub + ac_abs_top_srcdir=$ac_pwd ;; + [\\/]* | ?:[\\/]* ) # Absolute name. + ac_srcdir=$srcdir$ac_dir_suffix; + ac_top_srcdir=$srcdir + ac_abs_top_srcdir=$srcdir ;; + *) # Relative name. + ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix + ac_top_srcdir=$ac_top_build_prefix$srcdir + ac_abs_top_srcdir=$ac_pwd/$srcdir ;; +esac +ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix + + + case $ac_mode in + :F) + # + # CONFIG_FILE + # + + case $INSTALL in + [\\/$]* | ?:[\\/]* ) ac_INSTALL=$INSTALL ;; + *) ac_INSTALL=$ac_top_build_prefix$INSTALL ;; + esac + ac_MKDIR_P=$MKDIR_P + case $MKDIR_P in + [\\/$]* | ?:[\\/]* ) ;; + */*) ac_MKDIR_P=$ac_top_build_prefix$MKDIR_P ;; + esac +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +# If the template does not know about datarootdir, expand it. +# FIXME: This hack should be removed a few years after 2.60. +ac_datarootdir_hack=; ac_datarootdir_seen= +ac_sed_dataroot=' +/datarootdir/ { + p + q +} +/@datadir@/p +/@docdir@/p +/@infodir@/p +/@localedir@/p +/@mandir@/p' +case `eval "sed -n \"\$ac_sed_dataroot\" $ac_file_inputs"` in +*datarootdir*) ac_datarootdir_seen=yes;; +*@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*) + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5 +$as_echo "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;} +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 + ac_datarootdir_hack=' + s&@datadir@&$datadir&g + s&@docdir@&$docdir&g + s&@infodir@&$infodir&g + s&@localedir@&$localedir&g + s&@mandir@&$mandir&g + s&\\\${datarootdir}&$datarootdir&g' ;; +esac +_ACEOF + +# Neutralize VPATH when `$srcdir' = `.'. +# Shell code in configure.ac might set extrasub. +# FIXME: do we really want to maintain this feature? +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +ac_sed_extra="$ac_vpsub +$extrasub +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +:t +/@[a-zA-Z_][a-zA-Z_0-9]*@/!b +s|@configure_input@|$ac_sed_conf_input|;t t +s&@top_builddir@&$ac_top_builddir_sub&;t t +s&@top_build_prefix@&$ac_top_build_prefix&;t t +s&@srcdir@&$ac_srcdir&;t t +s&@abs_srcdir@&$ac_abs_srcdir&;t t +s&@top_srcdir@&$ac_top_srcdir&;t t +s&@abs_top_srcdir@&$ac_abs_top_srcdir&;t t +s&@builddir@&$ac_builddir&;t t +s&@abs_builddir@&$ac_abs_builddir&;t t +s&@abs_top_builddir@&$ac_abs_top_builddir&;t t +s&@INSTALL@&$ac_INSTALL&;t t +s&@MKDIR_P@&$ac_MKDIR_P&;t t +$ac_datarootdir_hack +" +eval sed \"\$ac_sed_extra\" "$ac_file_inputs" | $AWK -f "$ac_tmp/subs.awk" \ + >$ac_tmp/out || as_fn_error $? "could not create $ac_file" "$LINENO" 5 + +test -z "$ac_datarootdir_hack$ac_datarootdir_seen" && + { ac_out=`sed -n '/\${datarootdir}/p' "$ac_tmp/out"`; test -n "$ac_out"; } && + { ac_out=`sed -n '/^[ ]*datarootdir[ ]*:*=/p' \ + "$ac_tmp/out"`; test -z "$ac_out"; } && + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file contains a reference to the variable \`datarootdir' +which seems to be undefined. Please make sure it is defined" >&5 +$as_echo "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir' +which seems to be undefined. Please make sure it is defined" >&2;} + + rm -f "$ac_tmp/stdin" + case $ac_file in + -) cat "$ac_tmp/out" && rm -f "$ac_tmp/out";; + *) rm -f "$ac_file" && mv "$ac_tmp/out" "$ac_file";; + esac \ + || as_fn_error $? "could not create $ac_file" "$LINENO" 5 + ;; + :H) + # + # CONFIG_HEADER + # + if test x"$ac_file" != x-; then + { + $as_echo "/* $configure_input */" \ + && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" + } >"$ac_tmp/config.h" \ + || as_fn_error $? "could not create $ac_file" "$LINENO" 5 + if diff "$ac_file" "$ac_tmp/config.h" >/dev/null 2>&1; then + { $as_echo "$as_me:${as_lineno-$LINENO}: $ac_file is unchanged" >&5 +$as_echo "$as_me: $ac_file is unchanged" >&6;} + else + rm -f "$ac_file" + mv "$ac_tmp/config.h" "$ac_file" \ + || as_fn_error $? "could not create $ac_file" "$LINENO" 5 + fi + else + $as_echo "/* $configure_input */" \ + && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" \ + || as_fn_error $? "could not create -" "$LINENO" 5 + fi +# Compute "$ac_file"'s index in $config_headers. +_am_arg="$ac_file" +_am_stamp_count=1 +for _am_header in $config_headers :; do + case $_am_header in + $_am_arg | $_am_arg:* ) + break ;; + * ) + _am_stamp_count=`expr $_am_stamp_count + 1` ;; + esac +done +echo "timestamp for $_am_arg" >`$as_dirname -- "$_am_arg" || +$as_expr X"$_am_arg" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$_am_arg" : 'X\(//\)[^/]' \| \ + X"$_am_arg" : 'X\(//\)$' \| \ + X"$_am_arg" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$_am_arg" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'`/stamp-h$_am_stamp_count + ;; + + :C) { $as_echo "$as_me:${as_lineno-$LINENO}: executing $ac_file commands" >&5 +$as_echo "$as_me: executing $ac_file commands" >&6;} + ;; + esac + + + case $ac_file$ac_mode in + "libtool":C) + + # See if we are running on zsh, and set the options which allow our + # commands through without removal of \ escapes. + if test -n "${ZSH_VERSION+set}" ; then + setopt NO_GLOB_SUBST + fi + + cfgfile="${ofile}T" + trap "$RM \"$cfgfile\"; exit 1" 1 2 15 + $RM "$cfgfile" + + cat <<_LT_EOF >> "$cfgfile" +#! $SHELL + +# `$ECHO "$ofile" | sed 's%^.*/%%'` - Provide generalized library-building support services. +# Generated automatically by $as_me ($PACKAGE$TIMESTAMP) $VERSION +# Libtool was configured on host `(hostname || uname -n) 2>/dev/null | sed 1q`: +# NOTE: Changes made to this file will be lost: look at ltmain.sh. +# +# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2003, 2004, 2005, +# 2006, 2007, 2008, 2009, 2010, 2011 Free Software +# Foundation, Inc. +# Written by Gordon Matzigkeit, 1996 +# +# This file is part of GNU Libtool. +# +# GNU Libtool 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 2 of +# the License, or (at your option) any later version. +# +# As a special exception to the GNU General Public License, +# if you distribute this file as part of a program or library that +# is built using GNU Libtool, you may include this file under the +# same distribution terms that you use for the rest of that program. +# +# GNU Libtool 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 GNU Libtool; see the file COPYING. If not, a copy +# can be downloaded from http://www.gnu.org/licenses/gpl.html, or +# obtained by writing to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + +# The names of the tagged configurations supported by this script. +available_tags="CXX " + +# ### BEGIN LIBTOOL CONFIG + +# Which release of libtool.m4 was used? +macro_version=$macro_version +macro_revision=$macro_revision + +# Whether or not to build shared libraries. +build_libtool_libs=$enable_shared + +# Whether or not to build static libraries. +build_old_libs=$enable_static + +# What type of objects to build. +pic_mode=$pic_mode + +# Whether or not to optimize for fast installation. +fast_install=$enable_fast_install + +# Shell to use when invoking shell scripts. +SHELL=$lt_SHELL + +# An echo program that protects backslashes. +ECHO=$lt_ECHO + +# The PATH separator for the build system. +PATH_SEPARATOR=$lt_PATH_SEPARATOR + +# The host system. +host_alias=$host_alias +host=$host +host_os=$host_os + +# The build system. +build_alias=$build_alias +build=$build +build_os=$build_os + +# A sed program that does not truncate output. +SED=$lt_SED + +# Sed that helps us avoid accidentally triggering echo(1) options like -n. +Xsed="\$SED -e 1s/^X//" + +# A grep program that handles long lines. +GREP=$lt_GREP + +# An ERE matcher. +EGREP=$lt_EGREP + +# A literal string matcher. +FGREP=$lt_FGREP + +# A BSD- or MS-compatible name lister. +NM=$lt_NM + +# Whether we need soft or hard links. +LN_S=$lt_LN_S + +# What is the maximum length of a command? +max_cmd_len=$max_cmd_len + +# Object file suffix (normally "o"). +objext=$ac_objext + +# Executable file suffix (normally ""). +exeext=$exeext + +# whether the shell understands "unset". +lt_unset=$lt_unset + +# turn spaces into newlines. +SP2NL=$lt_lt_SP2NL + +# turn newlines into spaces. +NL2SP=$lt_lt_NL2SP + +# convert \$build file names to \$host format. +to_host_file_cmd=$lt_cv_to_host_file_cmd + +# convert \$build files to toolchain format. +to_tool_file_cmd=$lt_cv_to_tool_file_cmd + +# An object symbol dumper. +OBJDUMP=$lt_OBJDUMP + +# Method to check whether dependent libraries are shared objects. +deplibs_check_method=$lt_deplibs_check_method + +# Command to use when deplibs_check_method = "file_magic". +file_magic_cmd=$lt_file_magic_cmd + +# How to find potential files when deplibs_check_method = "file_magic". +file_magic_glob=$lt_file_magic_glob + +# Find potential files using nocaseglob when deplibs_check_method = "file_magic". +want_nocaseglob=$lt_want_nocaseglob + +# DLL creation program. +DLLTOOL=$lt_DLLTOOL + +# Command to associate shared and link libraries. +sharedlib_from_linklib_cmd=$lt_sharedlib_from_linklib_cmd + +# The archiver. +AR=$lt_AR + +# Flags to create an archive. +AR_FLAGS=$lt_AR_FLAGS + +# How to feed a file listing to the archiver. +archiver_list_spec=$lt_archiver_list_spec + +# A symbol stripping program. +STRIP=$lt_STRIP + +# Commands used to install an old-style archive. +RANLIB=$lt_RANLIB +old_postinstall_cmds=$lt_old_postinstall_cmds +old_postuninstall_cmds=$lt_old_postuninstall_cmds + +# Whether to use a lock for old archive extraction. +lock_old_archive_extraction=$lock_old_archive_extraction + +# A C compiler. +LTCC=$lt_CC + +# LTCC compiler flags. +LTCFLAGS=$lt_CFLAGS + +# Take the output of nm and produce a listing of raw symbols and C names. +global_symbol_pipe=$lt_lt_cv_sys_global_symbol_pipe + +# Transform the output of nm in a proper C declaration. +global_symbol_to_cdecl=$lt_lt_cv_sys_global_symbol_to_cdecl + +# Transform the output of nm in a C name address pair. +global_symbol_to_c_name_address=$lt_lt_cv_sys_global_symbol_to_c_name_address + +# Transform the output of nm in a C name address pair when lib prefix is needed. +global_symbol_to_c_name_address_lib_prefix=$lt_lt_cv_sys_global_symbol_to_c_name_address_lib_prefix + +# Specify filename containing input files for \$NM. +nm_file_list_spec=$lt_nm_file_list_spec + +# The root where to search for dependent libraries,and in which our libraries should be installed. +lt_sysroot=$lt_sysroot + +# The name of the directory that contains temporary libtool files. +objdir=$objdir + +# Used to examine libraries when file_magic_cmd begins with "file". +MAGIC_CMD=$MAGIC_CMD + +# Must we lock files when doing compilation? +need_locks=$lt_need_locks + +# Manifest tool. +MANIFEST_TOOL=$lt_MANIFEST_TOOL + +# Tool to manipulate archived DWARF debug symbol files on Mac OS X. +DSYMUTIL=$lt_DSYMUTIL + +# Tool to change global to local symbols on Mac OS X. +NMEDIT=$lt_NMEDIT + +# Tool to manipulate fat objects and archives on Mac OS X. +LIPO=$lt_LIPO + +# ldd/readelf like tool for Mach-O binaries on Mac OS X. +OTOOL=$lt_OTOOL + +# ldd/readelf like tool for 64 bit Mach-O binaries on Mac OS X 10.4. +OTOOL64=$lt_OTOOL64 + +# Old archive suffix (normally "a"). +libext=$libext + +# Shared library suffix (normally ".so"). +shrext_cmds=$lt_shrext_cmds + +# The commands to extract the exported symbol list from a shared archive. +extract_expsyms_cmds=$lt_extract_expsyms_cmds + +# Variables whose values should be saved in libtool wrapper scripts and +# restored at link time. +variables_saved_for_relink=$lt_variables_saved_for_relink + +# Do we need the "lib" prefix for modules? +need_lib_prefix=$need_lib_prefix + +# Do we need a version for libraries? +need_version=$need_version + +# Library versioning type. +version_type=$version_type + +# Shared library runtime path variable. +runpath_var=$runpath_var + +# Shared library path variable. +shlibpath_var=$shlibpath_var + +# Is shlibpath searched before the hard-coded library search path? +shlibpath_overrides_runpath=$shlibpath_overrides_runpath + +# Format of library name prefix. +libname_spec=$lt_libname_spec + +# List of archive names. First name is the real one, the rest are links. +# The last name is the one that the linker finds with -lNAME +library_names_spec=$lt_library_names_spec + +# The coded name of the library, if different from the real name. +soname_spec=$lt_soname_spec + +# Permission mode override for installation of shared libraries. +install_override_mode=$lt_install_override_mode + +# Command to use after installation of a shared archive. +postinstall_cmds=$lt_postinstall_cmds + +# Command to use after uninstallation of a shared archive. +postuninstall_cmds=$lt_postuninstall_cmds + +# Commands used to finish a libtool library installation in a directory. +finish_cmds=$lt_finish_cmds + +# As "finish_cmds", except a single script fragment to be evaled but +# not shown. +finish_eval=$lt_finish_eval + +# Whether we should hardcode library paths into libraries. +hardcode_into_libs=$hardcode_into_libs + +# Compile-time system search path for libraries. +sys_lib_search_path_spec=$lt_sys_lib_search_path_spec + +# Run-time system search path for libraries. +sys_lib_dlsearch_path_spec=$lt_sys_lib_dlsearch_path_spec + +# Whether dlopen is supported. +dlopen_support=$enable_dlopen + +# Whether dlopen of programs is supported. +dlopen_self=$enable_dlopen_self + +# Whether dlopen of statically linked programs is supported. +dlopen_self_static=$enable_dlopen_self_static + +# Commands to strip libraries. +old_striplib=$lt_old_striplib +striplib=$lt_striplib + + +# The linker used to build libraries. +LD=$lt_LD + +# How to create reloadable object files. +reload_flag=$lt_reload_flag +reload_cmds=$lt_reload_cmds + +# Commands used to build an old-style archive. +old_archive_cmds=$lt_old_archive_cmds + +# A language specific compiler. +CC=$lt_compiler + +# Is the compiler the GNU compiler? +with_gcc=$GCC + +# Compiler flag to turn off builtin functions. +no_builtin_flag=$lt_lt_prog_compiler_no_builtin_flag + +# Additional compiler flags for building library objects. +pic_flag=$lt_lt_prog_compiler_pic + +# How to pass a linker flag through the compiler. +wl=$lt_lt_prog_compiler_wl + +# Compiler flag to prevent dynamic linking. +link_static_flag=$lt_lt_prog_compiler_static + +# Does compiler simultaneously support -c and -o options? +compiler_c_o=$lt_lt_cv_prog_compiler_c_o + +# Whether or not to add -lc for building shared libraries. +build_libtool_need_lc=$archive_cmds_need_lc + +# Whether or not to disallow shared libs when runtime libs are static. +allow_libtool_libs_with_static_runtimes=$enable_shared_with_static_runtimes + +# Compiler flag to allow reflexive dlopens. +export_dynamic_flag_spec=$lt_export_dynamic_flag_spec + +# Compiler flag to generate shared objects directly from archives. +whole_archive_flag_spec=$lt_whole_archive_flag_spec + +# Whether the compiler copes with passing no objects directly. +compiler_needs_object=$lt_compiler_needs_object + +# Create an old-style archive from a shared archive. +old_archive_from_new_cmds=$lt_old_archive_from_new_cmds + +# Create a temporary old-style archive to link instead of a shared archive. +old_archive_from_expsyms_cmds=$lt_old_archive_from_expsyms_cmds + +# Commands used to build a shared archive. +archive_cmds=$lt_archive_cmds +archive_expsym_cmds=$lt_archive_expsym_cmds + +# Commands used to build a loadable module if different from building +# a shared archive. +module_cmds=$lt_module_cmds +module_expsym_cmds=$lt_module_expsym_cmds + +# Whether we are building with GNU ld or not. +with_gnu_ld=$lt_with_gnu_ld + +# Flag that allows shared libraries with undefined symbols to be built. +allow_undefined_flag=$lt_allow_undefined_flag + +# Flag that enforces no undefined symbols. +no_undefined_flag=$lt_no_undefined_flag + +# Flag to hardcode \$libdir into a binary during linking. +# This must work even if \$libdir does not exist +hardcode_libdir_flag_spec=$lt_hardcode_libdir_flag_spec + +# Whether we need a single "-rpath" flag with a separated argument. +hardcode_libdir_separator=$lt_hardcode_libdir_separator + +# Set to "yes" if using DIR/libNAME\${shared_ext} during linking hardcodes +# DIR into the resulting binary. +hardcode_direct=$hardcode_direct + +# Set to "yes" if using DIR/libNAME\${shared_ext} during linking hardcodes +# DIR into the resulting binary and the resulting library dependency is +# "absolute",i.e impossible to change by setting \${shlibpath_var} if the +# library is relocated. +hardcode_direct_absolute=$hardcode_direct_absolute + +# Set to "yes" if using the -LDIR flag during linking hardcodes DIR +# into the resulting binary. +hardcode_minus_L=$hardcode_minus_L + +# Set to "yes" if using SHLIBPATH_VAR=DIR during linking hardcodes DIR +# into the resulting binary. +hardcode_shlibpath_var=$hardcode_shlibpath_var + +# Set to "yes" if building a shared library automatically hardcodes DIR +# into the library and all subsequent libraries and executables linked +# against it. +hardcode_automatic=$hardcode_automatic + +# Set to yes if linker adds runtime paths of dependent libraries +# to runtime path list. +inherit_rpath=$inherit_rpath + +# Whether libtool must link a program against all its dependency libraries. +link_all_deplibs=$link_all_deplibs + +# Set to "yes" if exported symbols are required. +always_export_symbols=$always_export_symbols + +# The commands to list exported symbols. +export_symbols_cmds=$lt_export_symbols_cmds + +# Symbols that should not be listed in the preloaded symbols. +exclude_expsyms=$lt_exclude_expsyms + +# Symbols that must always be exported. +include_expsyms=$lt_include_expsyms + +# Commands necessary for linking programs (against libraries) with templates. +prelink_cmds=$lt_prelink_cmds + +# Commands necessary for finishing linking programs. +postlink_cmds=$lt_postlink_cmds + +# Specify filename containing input files. +file_list_spec=$lt_file_list_spec + +# How to hardcode a shared library path into an executable. +hardcode_action=$hardcode_action + +# The directories searched by this compiler when creating a shared library. +compiler_lib_search_dirs=$lt_compiler_lib_search_dirs + +# Dependencies to place before and after the objects being linked to +# create a shared library. +predep_objects=$lt_predep_objects +postdep_objects=$lt_postdep_objects +predeps=$lt_predeps +postdeps=$lt_postdeps + +# The library search path used internally by the compiler when linking +# a shared library. +compiler_lib_search_path=$lt_compiler_lib_search_path + +# ### END LIBTOOL CONFIG + +_LT_EOF + + case $host_os in + aix3*) + cat <<\_LT_EOF >> "$cfgfile" +# AIX sometimes has problems with the GCC collect2 program. For some +# reason, if we set the COLLECT_NAMES environment variable, the problems +# vanish in a puff of smoke. +if test "X${COLLECT_NAMES+set}" != Xset; then + COLLECT_NAMES= + export COLLECT_NAMES +fi +_LT_EOF + ;; + esac + + +ltmain="$ac_aux_dir/ltmain.sh" + + + # We use sed instead of cat because bash on DJGPP gets confused if + # if finds mixed CR/LF and LF-only lines. Since sed operates in + # text mode, it properly converts lines to CR/LF. This bash problem + # is reportedly fixed, but why not run on old versions too? + sed '$q' "$ltmain" >> "$cfgfile" \ + || (rm -f "$cfgfile"; exit 1) + + if test x"$xsi_shell" = xyes; then + sed -e '/^func_dirname ()$/,/^} # func_dirname /c\ +func_dirname ()\ +{\ +\ case ${1} in\ +\ */*) func_dirname_result="${1%/*}${2}" ;;\ +\ * ) func_dirname_result="${3}" ;;\ +\ esac\ +} # Extended-shell func_dirname implementation' "$cfgfile" > $cfgfile.tmp \ + && mv -f "$cfgfile.tmp" "$cfgfile" \ + || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") +test 0 -eq $? || _lt_function_replace_fail=: + + + sed -e '/^func_basename ()$/,/^} # func_basename /c\ +func_basename ()\ +{\ +\ func_basename_result="${1##*/}"\ +} # Extended-shell func_basename implementation' "$cfgfile" > $cfgfile.tmp \ + && mv -f "$cfgfile.tmp" "$cfgfile" \ + || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") +test 0 -eq $? || _lt_function_replace_fail=: + + + sed -e '/^func_dirname_and_basename ()$/,/^} # func_dirname_and_basename /c\ +func_dirname_and_basename ()\ +{\ +\ case ${1} in\ +\ */*) func_dirname_result="${1%/*}${2}" ;;\ +\ * ) func_dirname_result="${3}" ;;\ +\ esac\ +\ func_basename_result="${1##*/}"\ +} # Extended-shell func_dirname_and_basename implementation' "$cfgfile" > $cfgfile.tmp \ + && mv -f "$cfgfile.tmp" "$cfgfile" \ + || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") +test 0 -eq $? || _lt_function_replace_fail=: + + + sed -e '/^func_stripname ()$/,/^} # func_stripname /c\ +func_stripname ()\ +{\ +\ # pdksh 5.2.14 does not do ${X%$Y} correctly if both X and Y are\ +\ # positional parameters, so assign one to ordinary parameter first.\ +\ func_stripname_result=${3}\ +\ func_stripname_result=${func_stripname_result#"${1}"}\ +\ func_stripname_result=${func_stripname_result%"${2}"}\ +} # Extended-shell func_stripname implementation' "$cfgfile" > $cfgfile.tmp \ + && mv -f "$cfgfile.tmp" "$cfgfile" \ + || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") +test 0 -eq $? || _lt_function_replace_fail=: + + + sed -e '/^func_split_long_opt ()$/,/^} # func_split_long_opt /c\ +func_split_long_opt ()\ +{\ +\ func_split_long_opt_name=${1%%=*}\ +\ func_split_long_opt_arg=${1#*=}\ +} # Extended-shell func_split_long_opt implementation' "$cfgfile" > $cfgfile.tmp \ + && mv -f "$cfgfile.tmp" "$cfgfile" \ + || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") +test 0 -eq $? || _lt_function_replace_fail=: + + + sed -e '/^func_split_short_opt ()$/,/^} # func_split_short_opt /c\ +func_split_short_opt ()\ +{\ +\ func_split_short_opt_arg=${1#??}\ +\ func_split_short_opt_name=${1%"$func_split_short_opt_arg"}\ +} # Extended-shell func_split_short_opt implementation' "$cfgfile" > $cfgfile.tmp \ + && mv -f "$cfgfile.tmp" "$cfgfile" \ + || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") +test 0 -eq $? || _lt_function_replace_fail=: + + + sed -e '/^func_lo2o ()$/,/^} # func_lo2o /c\ +func_lo2o ()\ +{\ +\ case ${1} in\ +\ *.lo) func_lo2o_result=${1%.lo}.${objext} ;;\ +\ *) func_lo2o_result=${1} ;;\ +\ esac\ +} # Extended-shell func_lo2o implementation' "$cfgfile" > $cfgfile.tmp \ + && mv -f "$cfgfile.tmp" "$cfgfile" \ + || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") +test 0 -eq $? || _lt_function_replace_fail=: + + + sed -e '/^func_xform ()$/,/^} # func_xform /c\ +func_xform ()\ +{\ + func_xform_result=${1%.*}.lo\ +} # Extended-shell func_xform implementation' "$cfgfile" > $cfgfile.tmp \ + && mv -f "$cfgfile.tmp" "$cfgfile" \ + || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") +test 0 -eq $? || _lt_function_replace_fail=: + + + sed -e '/^func_arith ()$/,/^} # func_arith /c\ +func_arith ()\ +{\ + func_arith_result=$(( $* ))\ +} # Extended-shell func_arith implementation' "$cfgfile" > $cfgfile.tmp \ + && mv -f "$cfgfile.tmp" "$cfgfile" \ + || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") +test 0 -eq $? || _lt_function_replace_fail=: + + + sed -e '/^func_len ()$/,/^} # func_len /c\ +func_len ()\ +{\ + func_len_result=${#1}\ +} # Extended-shell func_len implementation' "$cfgfile" > $cfgfile.tmp \ + && mv -f "$cfgfile.tmp" "$cfgfile" \ + || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") +test 0 -eq $? || _lt_function_replace_fail=: + +fi + +if test x"$lt_shell_append" = xyes; then + sed -e '/^func_append ()$/,/^} # func_append /c\ +func_append ()\ +{\ + eval "${1}+=\\${2}"\ +} # Extended-shell func_append implementation' "$cfgfile" > $cfgfile.tmp \ + && mv -f "$cfgfile.tmp" "$cfgfile" \ + || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") +test 0 -eq $? || _lt_function_replace_fail=: + + + sed -e '/^func_append_quoted ()$/,/^} # func_append_quoted /c\ +func_append_quoted ()\ +{\ +\ func_quote_for_eval "${2}"\ +\ eval "${1}+=\\\\ \\$func_quote_for_eval_result"\ +} # Extended-shell func_append_quoted implementation' "$cfgfile" > $cfgfile.tmp \ + && mv -f "$cfgfile.tmp" "$cfgfile" \ + || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") +test 0 -eq $? || _lt_function_replace_fail=: + + + # Save a `func_append' function call where possible by direct use of '+=' + sed -e 's%func_append \([a-zA-Z_]\{1,\}\) "%\1+="%g' $cfgfile > $cfgfile.tmp \ + && mv -f "$cfgfile.tmp" "$cfgfile" \ + || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") + test 0 -eq $? || _lt_function_replace_fail=: +else + # Save a `func_append' function call even when '+=' is not available + sed -e 's%func_append \([a-zA-Z_]\{1,\}\) "%\1="$\1%g' $cfgfile > $cfgfile.tmp \ + && mv -f "$cfgfile.tmp" "$cfgfile" \ + || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") + test 0 -eq $? || _lt_function_replace_fail=: +fi + +if test x"$_lt_function_replace_fail" = x":"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Unable to substitute extended shell functions in $ofile" >&5 +$as_echo "$as_me: WARNING: Unable to substitute extended shell functions in $ofile" >&2;} +fi + + + mv -f "$cfgfile" "$ofile" || + (rm -f "$ofile" && cp "$cfgfile" "$ofile" && rm -f "$cfgfile") + chmod +x "$ofile" + + + cat <<_LT_EOF >> "$ofile" + +# ### BEGIN LIBTOOL TAG CONFIG: CXX + +# The linker used to build libraries. +LD=$lt_LD_CXX + +# How to create reloadable object files. +reload_flag=$lt_reload_flag_CXX +reload_cmds=$lt_reload_cmds_CXX + +# Commands used to build an old-style archive. +old_archive_cmds=$lt_old_archive_cmds_CXX + +# A language specific compiler. +CC=$lt_compiler_CXX + +# Is the compiler the GNU compiler? +with_gcc=$GCC_CXX + +# Compiler flag to turn off builtin functions. +no_builtin_flag=$lt_lt_prog_compiler_no_builtin_flag_CXX + +# Additional compiler flags for building library objects. +pic_flag=$lt_lt_prog_compiler_pic_CXX + +# How to pass a linker flag through the compiler. +wl=$lt_lt_prog_compiler_wl_CXX + +# Compiler flag to prevent dynamic linking. +link_static_flag=$lt_lt_prog_compiler_static_CXX + +# Does compiler simultaneously support -c and -o options? +compiler_c_o=$lt_lt_cv_prog_compiler_c_o_CXX + +# Whether or not to add -lc for building shared libraries. +build_libtool_need_lc=$archive_cmds_need_lc_CXX + +# Whether or not to disallow shared libs when runtime libs are static. +allow_libtool_libs_with_static_runtimes=$enable_shared_with_static_runtimes_CXX + +# Compiler flag to allow reflexive dlopens. +export_dynamic_flag_spec=$lt_export_dynamic_flag_spec_CXX + +# Compiler flag to generate shared objects directly from archives. +whole_archive_flag_spec=$lt_whole_archive_flag_spec_CXX + +# Whether the compiler copes with passing no objects directly. +compiler_needs_object=$lt_compiler_needs_object_CXX + +# Create an old-style archive from a shared archive. +old_archive_from_new_cmds=$lt_old_archive_from_new_cmds_CXX + +# Create a temporary old-style archive to link instead of a shared archive. +old_archive_from_expsyms_cmds=$lt_old_archive_from_expsyms_cmds_CXX + +# Commands used to build a shared archive. +archive_cmds=$lt_archive_cmds_CXX +archive_expsym_cmds=$lt_archive_expsym_cmds_CXX + +# Commands used to build a loadable module if different from building +# a shared archive. +module_cmds=$lt_module_cmds_CXX +module_expsym_cmds=$lt_module_expsym_cmds_CXX + +# Whether we are building with GNU ld or not. +with_gnu_ld=$lt_with_gnu_ld_CXX + +# Flag that allows shared libraries with undefined symbols to be built. +allow_undefined_flag=$lt_allow_undefined_flag_CXX + +# Flag that enforces no undefined symbols. +no_undefined_flag=$lt_no_undefined_flag_CXX + +# Flag to hardcode \$libdir into a binary during linking. +# This must work even if \$libdir does not exist +hardcode_libdir_flag_spec=$lt_hardcode_libdir_flag_spec_CXX + +# Whether we need a single "-rpath" flag with a separated argument. +hardcode_libdir_separator=$lt_hardcode_libdir_separator_CXX + +# Set to "yes" if using DIR/libNAME\${shared_ext} during linking hardcodes +# DIR into the resulting binary. +hardcode_direct=$hardcode_direct_CXX + +# Set to "yes" if using DIR/libNAME\${shared_ext} during linking hardcodes +# DIR into the resulting binary and the resulting library dependency is +# "absolute",i.e impossible to change by setting \${shlibpath_var} if the +# library is relocated. +hardcode_direct_absolute=$hardcode_direct_absolute_CXX + +# Set to "yes" if using the -LDIR flag during linking hardcodes DIR +# into the resulting binary. +hardcode_minus_L=$hardcode_minus_L_CXX + +# Set to "yes" if using SHLIBPATH_VAR=DIR during linking hardcodes DIR +# into the resulting binary. +hardcode_shlibpath_var=$hardcode_shlibpath_var_CXX + +# Set to "yes" if building a shared library automatically hardcodes DIR +# into the library and all subsequent libraries and executables linked +# against it. +hardcode_automatic=$hardcode_automatic_CXX + +# Set to yes if linker adds runtime paths of dependent libraries +# to runtime path list. +inherit_rpath=$inherit_rpath_CXX + +# Whether libtool must link a program against all its dependency libraries. +link_all_deplibs=$link_all_deplibs_CXX + +# Set to "yes" if exported symbols are required. +always_export_symbols=$always_export_symbols_CXX + +# The commands to list exported symbols. +export_symbols_cmds=$lt_export_symbols_cmds_CXX + +# Symbols that should not be listed in the preloaded symbols. +exclude_expsyms=$lt_exclude_expsyms_CXX + +# Symbols that must always be exported. +include_expsyms=$lt_include_expsyms_CXX + +# Commands necessary for linking programs (against libraries) with templates. +prelink_cmds=$lt_prelink_cmds_CXX + +# Commands necessary for finishing linking programs. +postlink_cmds=$lt_postlink_cmds_CXX + +# Specify filename containing input files. +file_list_spec=$lt_file_list_spec_CXX + +# How to hardcode a shared library path into an executable. +hardcode_action=$hardcode_action_CXX + +# The directories searched by this compiler when creating a shared library. +compiler_lib_search_dirs=$lt_compiler_lib_search_dirs_CXX + +# Dependencies to place before and after the objects being linked to +# create a shared library. +predep_objects=$lt_predep_objects_CXX +postdep_objects=$lt_postdep_objects_CXX +predeps=$lt_predeps_CXX +postdeps=$lt_postdeps_CXX + +# The library search path used internally by the compiler when linking +# a shared library. +compiler_lib_search_path=$lt_compiler_lib_search_path_CXX + +# ### END LIBTOOL TAG CONFIG: CXX +_LT_EOF + + ;; + "depfiles":C) test x"$AMDEP_TRUE" != x"" || { + # Older Autoconf quotes --file arguments for eval, but not when files + # are listed without --file. Let's play safe and only enable the eval + # if we detect the quoting. + case $CONFIG_FILES in + *\'*) eval set x "$CONFIG_FILES" ;; + *) set x $CONFIG_FILES ;; + esac + shift + for mf + do + # Strip MF so we end up with the name of the file. + mf=`echo "$mf" | sed -e 's/:.*$//'` + # Check whether this is an Automake generated Makefile or not. + # We used to match only the files named 'Makefile.in', but + # some people rename them; so instead we look at the file content. + # Grep'ing the first line is not enough: some people post-process + # each Makefile.in and add a new line on top of each file to say so. + # Grep'ing the whole file is not good either: AIX grep has a line + # limit of 2048, but all sed's we know have understand at least 4000. + if sed -n 's,^#.*generated by automake.*,X,p' "$mf" | grep X >/dev/null 2>&1; then + dirpart=`$as_dirname -- "$mf" || +$as_expr X"$mf" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$mf" : 'X\(//\)[^/]' \| \ + X"$mf" : 'X\(//\)$' \| \ + X"$mf" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$mf" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + else + continue + fi + # Extract the definition of DEPDIR, am__include, and am__quote + # from the Makefile without running 'make'. + DEPDIR=`sed -n 's/^DEPDIR = //p' < "$mf"` + test -z "$DEPDIR" && continue + am__include=`sed -n 's/^am__include = //p' < "$mf"` + test -z "$am__include" && continue + am__quote=`sed -n 's/^am__quote = //p' < "$mf"` + # Find all dependency output files, they are included files with + # $(DEPDIR) in their names. We invoke sed twice because it is the + # simplest approach to changing $(DEPDIR) to its actual value in the + # expansion. + for file in `sed -n " + s/^$am__include $am__quote\(.*(DEPDIR).*\)$am__quote"'$/\1/p' <"$mf" | \ + sed -e 's/\$(DEPDIR)/'"$DEPDIR"'/g'`; do + # Make sure the directory exists. + test -f "$dirpart/$file" && continue + fdir=`$as_dirname -- "$file" || +$as_expr X"$file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$file" : 'X\(//\)[^/]' \| \ + X"$file" : 'X\(//\)$' \| \ + X"$file" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$file" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + as_dir=$dirpart/$fdir; as_fn_mkdir_p + # echo "creating $dirpart/$file" + echo '# dummy' > "$dirpart/$file" + done + done +} + ;; + + esac +done # for ac_tag + + +as_fn_exit 0 +_ACEOF +ac_clean_files=$ac_clean_files_save + +test $ac_write_fail = 0 || + as_fn_error $? "write failure creating $CONFIG_STATUS" "$LINENO" 5 + + +# configure is writing to config.log, and then calls config.status. +# config.status does its own redirection, appending to config.log. +# Unfortunately, on DOS this fails, as config.log is still kept open +# by configure, so config.status won't be able to write to it; its +# output is simply discarded. So we exec the FD to /dev/null, +# effectively closing config.log, so it can be properly (re)opened and +# appended to by config.status. When coming back to configure, we +# need to make the FD available again. +if test "$no_create" != yes; then + ac_cs_success=: + ac_config_status_args= + test "$silent" = yes && + ac_config_status_args="$ac_config_status_args --quiet" + exec 5>/dev/null + $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false + exec 5>>config.log + # Use ||, not &&, to avoid exiting from the if with $? = 1, which + # would make configure fail if this is the last instruction. + $ac_cs_success || as_fn_exit 1 +fi +if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: unrecognized options: $ac_unrecognized_opts" >&5 +$as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;} +fi + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: summary of build options: + + Version: ${VERSION} shared $LT_CURRENT:$LT_REVISION:$LT_AGE + Host type: ${host} + Install prefix: ${prefix} + C compiler: ${CC} + CFLAGS: ${CFLAGS} + WARNCFLAGS: ${WARNCFLAGS} + LDFLAGS: ${LDFLAGS} + LIBS: ${LIBS} + CPPFLAGS: ${CPPFLAGS} + C preprocessor: ${CPP} + C++ compiler: ${CXX} + CXXFLAGS: ${CXXFLAGS} + CXXCPP: ${CXXCPP} + Library types: Shared=${enable_shared}, Static=${enable_static} + Python: + Python: ${PYTHON} + PYTHON_VERSION: ${PYTHON_VERSION} + pyexecdir: ${pyexecdir} + Python-dev: ${have_python_dev} + PYTHON_CPPFLAGS:${PYTHON_CPPFLAGS} + PYTHON_LDFLAGS: ${PYTHON_LDFLAGS} + Cython: ${CYTHON} + Test: + CUnit: ${have_cunit} + Failmalloc: ${enable_failmalloc} + Libs: + OpenSSL: ${have_openssl} + Libxml2: ${have_libxml2} + Libev: ${have_libev} + Libevent(SSL): ${have_libevent_openssl} + Spdylay: ${have_spdylay} + Jansson: ${have_jansson} + Jemalloc: ${have_jemalloc} + Boost CPPFLAGS: ${BOOST_CPPFLAGS} + Boost LDFLAGS: ${BOOST_LDFLAGS} + Boost::ASIO: ${BOOST_ASIO_LIB} + Boost::System: ${BOOST_SYSTEM_LIB} + Boost::Thread: ${BOOST_THREAD_LIB} + Features: + Applications: ${enable_app} + HPACK tools: ${enable_hpack_tools} + Libnghttp2_asio:${enable_asio_lib} + Examples: ${enable_examples} + Python bindings:${enable_python_bindings} + Threading: ${enable_threads} +" >&5 +$as_echo "$as_me: summary of build options: + + Version: ${VERSION} shared $LT_CURRENT:$LT_REVISION:$LT_AGE + Host type: ${host} + Install prefix: ${prefix} + C compiler: ${CC} + CFLAGS: ${CFLAGS} + WARNCFLAGS: ${WARNCFLAGS} + LDFLAGS: ${LDFLAGS} + LIBS: ${LIBS} + CPPFLAGS: ${CPPFLAGS} + C preprocessor: ${CPP} + C++ compiler: ${CXX} + CXXFLAGS: ${CXXFLAGS} + CXXCPP: ${CXXCPP} + Library types: Shared=${enable_shared}, Static=${enable_static} + Python: + Python: ${PYTHON} + PYTHON_VERSION: ${PYTHON_VERSION} + pyexecdir: ${pyexecdir} + Python-dev: ${have_python_dev} + PYTHON_CPPFLAGS:${PYTHON_CPPFLAGS} + PYTHON_LDFLAGS: ${PYTHON_LDFLAGS} + Cython: ${CYTHON} + Test: + CUnit: ${have_cunit} + Failmalloc: ${enable_failmalloc} + Libs: + OpenSSL: ${have_openssl} + Libxml2: ${have_libxml2} + Libev: ${have_libev} + Libevent(SSL): ${have_libevent_openssl} + Spdylay: ${have_spdylay} + Jansson: ${have_jansson} + Jemalloc: ${have_jemalloc} + Boost CPPFLAGS: ${BOOST_CPPFLAGS} + Boost LDFLAGS: ${BOOST_LDFLAGS} + Boost::ASIO: ${BOOST_ASIO_LIB} + Boost::System: ${BOOST_SYSTEM_LIB} + Boost::Thread: ${BOOST_THREAD_LIB} + Features: + Applications: ${enable_app} + HPACK tools: ${enable_hpack_tools} + Libnghttp2_asio:${enable_asio_lib} + Examples: ${enable_examples} + Python bindings:${enable_python_bindings} + Threading: ${enable_threads} +" >&6;} diff --git a/configure.ac b/configure.ac new file mode 100644 index 0000000..94d099d --- /dev/null +++ b/configure.ac @@ -0,0 +1,758 @@ +dnl nghttp2 - HTTP/2 C Library + +dnl Copyright (c) 2012, 2013, 2014, 2015 Tatsuhiro Tsujikawa + +dnl Permission is hereby granted, free of charge, to any person obtaining +dnl a copy of this software and associated documentation files (the +dnl "Software"), to deal in the Software without restriction, including +dnl without limitation the rights to use, copy, modify, merge, publish, +dnl distribute, sublicense, and/or sell copies of the Software, and to +dnl permit persons to whom the Software is furnished to do so, subject to +dnl the following conditions: + +dnl The above copyright notice and this permission notice shall be +dnl included in all copies or substantial portions of the Software. + +dnl THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +dnl EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +dnl MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +dnl NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +dnl LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +dnl OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +dnl WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +dnl Do not change user variables! +dnl http://www.gnu.org/software/automake/manual/html_node/Flag-Variables-Ordering.html + +AC_PREREQ(2.61) +AC_INIT([nghttp2], [1.0.0], [t-tujikawa@users.sourceforge.net]) +AC_USE_SYSTEM_EXTENSIONS + +LT_PREREQ([2.2.6]) +LT_INIT() + +AC_CANONICAL_BUILD +AC_CANONICAL_HOST +AC_CANONICAL_TARGET + +AM_INIT_AUTOMAKE([subdir-objects]) +# comment out for now since this requires automake 1.13 or higher and +# travis has older one. +# AM_EXTRA_RECURSIVE_TARGETS([it]) + +m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) + +AC_CONFIG_MACRO_DIR([m4]) +AC_CONFIG_HEADERS([config.h]) + +dnl See versioning rule: +dnl http://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html +AC_SUBST(LT_CURRENT, 14) +AC_SUBST(LT_REVISION, 0) +AC_SUBST(LT_AGE, 0) + +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) + +dnl Checks for command-line options +AC_ARG_ENABLE([werror], + [AS_HELP_STRING([--enable-werror], + [Turn on compile time warnings])], + [werror=$enableval], [werror=no]) + +AC_ARG_ENABLE([debug], + [AS_HELP_STRING([--enable-debug], + [Turn on debug output])], + [debug=$enableval], [debug=no]) + +AC_ARG_ENABLE([threads], + [AS_HELP_STRING([--disable-threads], + [Turn off threading in apps])], + [threads=$enableval], [threads=yes]) + +AC_ARG_ENABLE([app], + [AS_HELP_STRING([--enable-app], + [Build applications (nghttp, nghttpd and nghttpx) [default=check]])], + [request_app=$enableval], [request_app=check]) + +AC_ARG_ENABLE([hpack-tools], + [AS_HELP_STRING([--enable-hpack-tools], + [Build HPACK tools [default=check]])], + [request_hpack_tools=$enableval], [request_hpack_tools=check]) + +AC_ARG_ENABLE([asio-lib], + [AS_HELP_STRING([--enable-asio-lib], + [Build C++ libnghttp2_asio library [default=no]])], + [request_asio_lib=$enableval], [request_asio_lib=no]) + +AC_ARG_ENABLE([examples], + [AS_HELP_STRING([--enable-examples], + [Build examples [default=check]])], + [request_examples=$enableval], [request_examples=check]) + +AC_ARG_ENABLE([python-bindings], + [AS_HELP_STRING([--enable-python-bindings], + [Build Python bindings [default=check]])], + [request_python_bindings=$enableval], [request_python_bindings=check]) + +AC_ARG_ENABLE([failmalloc], + [AS_HELP_STRING([--disable-failmalloc], + [Do not build failmalloc test program])], + [request_failmalloc=$enableval], [request_failmalloc=yes]) + +AC_ARG_WITH([libxml2], + [AS_HELP_STRING([--with-libxml2], + [Use libxml2 [default=check]])], + [request_libxml2=$withval], [request_libxml2=check]) + +AC_ARG_WITH([jemalloc], + [AS_HELP_STRING([--with-jemalloc], + [Use jemalloc [default=check]])], + [request_jemalloc=$withval], [request_jemalloc=check]) + +AC_ARG_WITH([spdylay], + [AS_HELP_STRING([--with-spdylay], + [Use spdylay [default=check]])], + [request_spdylay=$withval], [request_spdylay=check]) + +AC_ARG_WITH([cython], + [AS_HELP_STRING([--with-cython=PATH], + [Use cython in given PATH])], + [cython_path=$withval], []) + +dnl Define variables +AC_ARG_VAR([CYTHON], [the Cython executable]) + +dnl Checks for programs +AC_PROG_CC +AC_PROG_CXX +AC_PROG_CPP +AC_PROG_INSTALL +AC_PROG_LN_S +AC_PROG_MAKE_SET +AC_PROG_MKDIR_P + +PKG_PROG_PKG_CONFIG([0.20]) + +if [test "x$request_python_bindings" != "xno"]; then + AM_PATH_PYTHON([2.7],, [:]) + AX_PYTHON_DEVEL([>= '2.7']) +fi + +if test "x${cython_path}" = "x"; then + AC_CHECK_PROGS([CYTHON], [cython.py cython]) +else + CYTHON=${cython_path} + AC_SUBST([CYTHON]) +fi + +# +# If we're running GCC or clang define _U_ to be "__attribute__((unused))" +# so we can use _U_ to flag unused function parameters and not get warnings +# about them. Otherwise, define _U_ to be an empty string so that _U_ used +# to flag an unused function parameters will compile with other compilers. +# +# XXX - similar hints for other compilers? +# +if test "x$GCC" = "xyes" -o "x$CC" = "xclang" ; then + AC_DEFINE([_U_], [__attribute__((unused))], [Hint to the compiler that a function parameters is not used]) +else + AC_DEFINE([_U_], , [Hint to the compiler that a function parameters is not used]) +fi + +AX_CXX_COMPILE_STDCXX_11([noext], [optional]) + +AC_LANG_PUSH(C++) + +# Check that std::chrono::steady_clock is available. In particular, +# gcc 4.6 does not have one, but has monotonic_clock which is the old +# name existed in the pre-standard draft. If steady_clock is not +# available, don't define HAVE_STEADY_CLOCK and replace steady_clock +# with monotonic_clock. +AC_MSG_CHECKING([whether std::chrono::steady_clock is available]) +AC_COMPILE_IFELSE([AC_LANG_PROGRAM( +[[ +#include +]], +[[ +auto tp = std::chrono::steady_clock::now(); +]])], + [AC_DEFINE([HAVE_STEADY_CLOCK], [1], + [Define to 1 if you have the `std::chrono::steady_clock`.]) + AC_MSG_RESULT([yes])], + [AC_MSG_RESULT([no])]) + +# Check that std::future is available. +AC_MSG_CHECKING([whether std::future is available]) +AC_COMPILE_IFELSE([AC_LANG_PROGRAM( +[[ +#include +#include +]], +[[ +std::vector> v; +]])], + [AC_DEFINE([HAVE_STD_FUTURE], [1], + [Define to 1 if you have the `std::future`.]) + have_std_future=yes + AC_MSG_RESULT([yes])], + [have_std_future=no + AC_MSG_RESULT([no])]) + +# Check that std::map::emplace is available for g++-4.7. +AC_MSG_CHECKING([whether std::map::emplace is available]) +AC_COMPILE_IFELSE([AC_LANG_PROGRAM( +[[ +#include +]], +[[ +std::map().emplace(1, 2); +]])], + [AC_DEFINE([HAVE_STD_MAP_EMPLACE], [1], + [Define to 1 if you have the `std::map::emplace`.]) + have_std_map_emplace=yes + AC_MSG_RESULT([yes])], + [have_std_map_emplace=no + AC_MSG_RESULT([no])]) + +AC_LANG_POP() + +# Checks for libraries. + +# Additional libraries required for tests. +TESTLDADD= + +# Additional libraries required for programs under src directory. +APPLDFLAGS= + +LIBS_OLD=$LIBS +# Search for dlsym function, which is used in tests. Linux needs -ldl, +# but netbsd does not need it. +AC_SEARCH_LIBS([dlsym], [dl]) +TESTLDADD="$LIBS $TESTLDADD" +LIBS=$LIBS_OLD + +LIBS_OLD=$LIBS +AC_SEARCH_LIBS([clock_gettime], [rt], + [AC_DEFINE([HAVE_CLOCK_GETTIME], [1], + [Define to 1 if you have the `clock_gettime`.])]) +APPLDFLAGS="$LIBS $APPLDFLAGS" +LIBS=$LIBS_OLD + +case "$host" in + *android*) + android_build=yes + # android does not need -pthread, but needs followng 3 libs for C++ + APPLDFLAGS="$APPLDFLAGS -lstdc++ -latomic -lsupc++" + ;; + *) + PTHREAD_LDFLAGS="-pthread" + APPLDFLAGS="$APPLDFLAGS $PTHREAD_LDFLAGS" + ;; +esac + +# zlib +if test "x$android_build" = "xyes"; then + # Use zlib provided by NDK + APPLDFLAGS="-lz $APPLDFLAGS" + have_zlib=yes +else + PKG_CHECK_MODULES([ZLIB], [zlib >= 1.2.3], [have_zlib=yes], [have_zlib=no]) + + if test "x${have_zlib}" = "xno"; then + AC_MSG_NOTICE($ZLIB_PKG_ERRORS) + fi +fi + +# cunit +PKG_CHECK_MODULES([CUNIT], [cunit >= 2.1], [have_cunit=yes], [have_cunit=no]) +# If pkg-config does not find cunit, check it using AC_CHECK_LIB. We +# do this because Debian (Ubuntu) lacks pkg-config file for cunit. +if test "x${have_cunit}" = "xno"; then + AC_MSG_WARN([${CUNIT_PKG_ERRORS}]) + AC_CHECK_LIB([cunit], [CU_initialize_registry], + [have_cunit=yes], [have_cunit=no]) + if test "x${have_cunit}" = "xyes"; then + CUNIT_LIBS="-lcunit" + CUNIT_CFLAGS="" + AC_SUBST([CUNIT_LIBS]) + AC_SUBST([CUNIT_CFLAGS]) + fi +fi +if test "x${have_cunit}" = "xyes"; then + # cunit in Mac OS X requires ncurses. Note that in Mac OS X, test + # program can be built without -lncurses, but it emits runtime + # error. + case "${build}" in + *-apple-darwin*) + CUNIT_LIBS="$CUNIT_LIBS -lncurses" + AC_SUBST([CUNIT_LIBS]) + ;; + esac +fi + +AM_CONDITIONAL([HAVE_CUNIT], [ test "x${have_cunit}" = "xyes" ]) + +# libev (for src) +# libev does not have pkg-config file. Check it in an old way. +LIBS_OLD=$LIBS +# android requires -lm for floor +AC_CHECK_LIB([ev], [ev_time], [have_libev=yes], [have_libev=no], [-lm]) +if test "x${have_libev}" = "xyes"; then + AC_CHECK_HEADER([ev.h], [have_libev=yes], [have_libev=no]) + if test "x${have_libev}" = "xyes"; then + LIBEV_LIBS=-lev + LIBEV_CFLAGS= + AC_SUBST([LIBEV_LIBS]) + AC_SUBST([LIBEV_CFLAGS]) + fi +fi +LIBS=$LIBS_OLD + +# openssl (for src) +PKG_CHECK_MODULES([OPENSSL], [openssl >= 1.0.1], + [have_openssl=yes], [have_openssl=no]) +if test "x${have_openssl}" = "xno"; then + AC_MSG_NOTICE($OPENSSL_PKG_ERRORS) +fi + +# libevent_openssl (for examples) +# 2.0.8 is required because we use evconnlistener_set_error_cb() +PKG_CHECK_MODULES([LIBEVENT_OPENSSL], [libevent_openssl >= 2.0.8], + [have_libevent_openssl=yes], [have_libevent_openssl=no]) +if test "x${have_libevent_openssl}" = "xno"; then + AC_MSG_NOTICE($LIBEVENT_OPENSSL_PKG_ERRORS) +fi + +# jansson (for src/nghttp, src/deflatehd and src/inflatehd) +PKG_CHECK_MODULES([JANSSON], [jansson >= 2.5], + [have_jansson=yes], [have_jansson=no]) +if test "x${have_jansson}" == "xyes"; then + AC_DEFINE([HAVE_JANSSON], [1], + [Define to 1 if you have `libjansson` library.]) +else + AC_MSG_NOTICE($JANSSON_PKG_ERRORS) +fi + +# libxml2 (for src/nghttp) +have_libxml2=no +if test "x${request_libxml2}" != "xno"; then + AM_PATH_XML2(2.7.7, [have_libxml2=yes], [have_libxml2=no]) + if test "x${have_libxml2}" = "xyes"; then + AC_DEFINE([HAVE_LIBXML2], [1], [Define to 1 if you have `libxml2` library.]) + fi +fi + +if test "x${request_libxml2}" = "xyes" && + test "x${have_libxml2}" != "xyes"; then + AC_MSG_ERROR([libxml2 was requested (--with-libxml2) but not found]) +fi + +AM_CONDITIONAL([HAVE_LIBXML2], [ test "x${have_libxml2}" = "xyes" ]) + +# jemalloc +have_jemalloc=no +if test "x${request_jemalloc}" != "xno"; then + LIBS_OLD=$LIBS + AC_SEARCH_LIBS([malloc_stats_print], [jemalloc], [have_jemalloc=yes], [], + [$PTHREAD_LDFLAGS]) + LIBS=$LIBS_OLD + + if test "x${have_jemalloc}" = "xyes"; then + jemalloc_libs=${ac_cv_search_malloc_stats_print} + else + # On Darwin, malloc_stats_print is je_malloc_stats_print + AC_SEARCH_LIBS([je_malloc_stats_print], [jemalloc], [have_jemalloc=yes], [], + [$PTHREAD_LDFLAGS]) + LIBS=$LIBS_OLD + + if test "x${have_jemalloc}" = "xyes"; then + jemalloc_libs=${ac_cv_search_je_malloc_stats_print} + fi + fi + + if test "x${have_jemalloc}" = "xyes" && + test "x${jemalloc_libs}" != "xnone required"; then + JEMALLOC_LIBS=${jemalloc_libs} + AC_SUBST([JEMALLOC_LIBS]) + fi +fi + +if test "x${request_jemalloc}" = "xyes" && + test "x${have_jemalloc}" != "xyes"; then + AC_MSG_ERROR([jemalloc was requested (--with-jemalloc) but not found]) +fi + +# spdylay (for src/nghttpx and src/h2load) +have_spdylay=no +if test "x${request_spdylay}" != "xno"; then + PKG_CHECK_MODULES([LIBSPDYLAY], [libspdylay >= 1.3.2], + [have_spdylay=yes], [have_spdylay=no]) + if test "x${have_spdylay}" = "xyes"; then + AC_DEFINE([HAVE_SPDYLAY], [1], [Define to 1 if you have `spdylay` library.]) + else + AC_MSG_NOTICE($LIBSPDYLAY_PKG_ERRORS) + AC_MSG_NOTICE([The SPDY support in nghttpx and h2load will be disabled.]) + fi +fi + +if test "x${request_spdylay}" = "xyes" && + test "x${have_spdylay}" != "xyes"; then + AC_MSG_ERROR([spdylay was requested (--with-spdylay) but not found]) +fi + +AM_CONDITIONAL([HAVE_SPDYLAY], [ test "x${have_spdylay}" = "xyes" ]) + +# Check Boost Asio library +have_asio_lib=no + +if test "x${request_asio_lib}" = "xyes"; then + AX_BOOST_BASE([1.54.0], [have_boost_base=yes], [have_boost_base=no]) + + if test "x${have_boost_base}" = "xyes"; then + AX_BOOST_ASIO() + AX_BOOST_SYSTEM() + AX_BOOST_THREAD() + + if test "x${ax_cv_boost_asio}" = "xyes" && + test "x${ax_cv_boost_system}" = "xyes" && + test "x${ax_cv_boost_thread}" = "xyes"; then + have_asio_lib=yes + fi + fi +fi + +# The nghttp, nghttpd and nghttpx under src depend on zlib, OpenSSL +# and libev +enable_app=no +if test "x${request_app}" != "xno" && + test "x${have_zlib}" = "xyes" && + test "x${have_openssl}" = "xyes" && + test "x${have_libev}" = "xyes"; then + enable_app=yes +fi + +if test "x${request_app}" = "xyes" && + test "x${enable_app}" != "xyes"; then + AC_MSG_ERROR([applications were requested (--enable-app) but dependencies are not met.]) +fi + +AM_CONDITIONAL([ENABLE_APP], [ test "x${enable_app}" = "xyes" ]) + +enable_hpack_tools=no +# HPACK tools requires jansson +if test "x${request_hpack_tools}" != "xno" && + test "x${have_jansson}" = "xyes"; then + enable_hpack_tools=yes +fi + +if test "x${request_hpack_tools}" = "xyes" && + test "x${enable_hpack_tools}" != "xyes"; then + AC_MSG_ERROR([HPACK tools were requested (--enable-hpack-tools) but dependencies are not met.]) +fi + +AM_CONDITIONAL([ENABLE_HPACK_TOOLS], [ test "x${enable_hpack_tools}" = "xyes" ]) + +# C++ library libnghttp2_asio + +enable_asio_lib=no +if test "x${request_asio_lib}" != "xno" && + test "x${have_asio_lib}" = "xyes"; then + enable_asio_lib=yes +fi + +AM_CONDITIONAL([ENABLE_ASIO_LIB], [ test "x${enable_asio_lib}" = "xyes" ]) + +# The example programs depend on OpenSSL and libevent_openssl +enable_examples=no +if test "x${request_examples}" != "xno" && + test "x${have_openssl}" = "xyes" && + test "x${have_libevent_openssl}" = "xyes"; then + enable_examples=yes +fi + +if test "x${request_examples}" = "xyes" && + test "x${enable_examples}" != "xyes"; then + AC_MSG_ERROR([examples were requested (--enable-examples) but dependencies are not met.]) +fi + +AM_CONDITIONAL([ENABLE_EXAMPLES], [ test "x${enable_examples}" = "xyes" ]) + +# Python bindings +enable_python_bindings=no +if test "x${request_python_bindings}" != "xno" && + test "x${CYTHON}" != "x" && + test "x${PYTHON}" != "x:" && + test "x${have_python_dev}" = "xyes"; then + enable_python_bindings=yes +fi + +if test "x${request_python_bindings}" = "xyes" && + test "x${enable_python_bindings}" != "xyes"; then + AC_MSG_ERROR([python bindings were requested (--enable-python-bindings) but dependencies are not met.]) +fi + +AM_CONDITIONAL([ENABLE_PYTHON_BINDINGS], + [test "x${enable_python_bindings}" = "xyes"]) + +# Produce cython conditional, so that we can distribute generated C +# source +AM_CONDITIONAL([HAVE_CYTHON], [test "x${CYTHON}" != "x"]) + +# failmalloc tests +enable_failmalloc=no +if test "x${request_failmalloc}" = "xyes"; then + enable_failmalloc=yes +fi + +AM_CONDITIONAL([ENABLE_FAILMALLOC], [ test "x${enable_failmalloc}" = "xyes" ]) + +# Checks for header files. +AC_HEADER_ASSERT +AC_CHECK_HEADERS([ \ + arpa/inet.h \ + fcntl.h \ + inttypes.h \ + limits.h \ + netdb.h \ + netinet/in.h \ + pwd.h \ + stddef.h \ + stdint.h \ + stdlib.h \ + string.h \ + sys/socket.h \ + sys/time.h \ + syslog.h \ + time.h \ + unistd.h \ +]) + +# Checks for typedefs, structures, and compiler characteristics. +AC_TYPE_SIZE_T +AC_TYPE_SSIZE_T +AC_TYPE_UINT8_T +AC_TYPE_UINT16_T +AC_TYPE_UINT32_T +AC_TYPE_UINT64_T +AC_TYPE_INT8_T +AC_TYPE_INT16_T +AC_TYPE_INT32_T +AC_TYPE_INT64_T +AC_TYPE_OFF_T +AC_TYPE_PID_T +AC_TYPE_UID_T +AC_CHECK_TYPES([ptrdiff_t]) +AC_C_BIGENDIAN +AC_C_INLINE +AC_SYS_LARGEFILE + +AC_CHECK_MEMBER([struct tm.tm_gmtoff], [have_struct_tm_tm_gmtoff=yes], + [have_struct_tm_tm_gmtoff=no], [[#include ]]) + +if test "x$have_struct_tm_tm_gmtoff" = "xyes"; then + AC_DEFINE([HAVE_STRUCT_TM_TM_GMTOFF], [1], + [Define to 1 if you have `struct tm.tm_gmtoff` member.]) +fi + +# Check size of pointer to decide we need 8 bytes alignment +# adjustment. +AC_CHECK_SIZEOF([int *]) + +# Checks for library functions. +if test "x$cross_compiling" != "xyes"; then + AC_FUNC_MALLOC +fi + +AC_FUNC_CHOWN +AC_FUNC_ERROR_AT_LINE +AC_FUNC_FORK +# Don't check realloc, since LeakSanitizer detects memory leak during check +# AC_FUNC_REALLOC +AC_FUNC_STRERROR_R +AC_FUNC_STRNLEN + +AC_CHECK_FUNCS([ \ + _Exit \ + accept4 \ + dup2 \ + getcwd \ + getpwnam \ + localtime_r \ + memchr \ + memmove \ + memset \ + socket \ + sqrt \ + strchr \ + strdup \ + strerror \ + strndup \ + strstr \ + strtol \ + strtoul \ + timegm \ +]) + +# timerfd_create was added in linux kernel 2.6.25 + +AC_CHECK_FUNC([timerfd_create], + [have_timerfd_create=yes], [have_timerfd_create=no]) + + +# Checks for epoll availability, primarily for examples/tiny-nghttpd +AX_HAVE_EPOLL([have_epoll=yes], [have_epoll=no]) + +AM_CONDITIONAL([ENABLE_TINY_NGHTTPD], + [ test "x${have_epoll}" = "xyes" && + test "x${have_timerfd_create}" = "xyes"]) + +ac_save_CFLAGS=$CFLAGS +CFLAGS= + +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"]) + 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"]) + 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"]) + + # Only work with Clang for the moment + AX_CHECK_COMPILE_FLAG([-Wheader-guard], [CFLAGS="$CFLAGS -Wheader-guard"]) +fi + +WARNCFLAGS=$CFLAGS +CFLAGS=$ac_save_CFLAGS + +AC_SUBST([WARNCFLAGS]) + +if test "x$debug" != "xno"; then + AC_DEFINE([DEBUGBUILD], [1], [Define to 1 to enable debug output.]) +fi + +enable_threads=yes +# Some platform does not have working std::future. We disable +# threading for those platforms. +if test "x$threads" != "xyes" || + test "x$have_std_future" != "xyes"; then + enable_threads=no + AC_DEFINE([NOTHREADS], [1], [Define to 1 if you want to disable threads.]) +fi + +AC_SUBST([TESTLDADD]) +AC_SUBST([APPLDFLAGS]) + +AC_CONFIG_FILES([ + Makefile + lib/Makefile + lib/libnghttp2.pc + lib/includes/Makefile + lib/includes/nghttp2/nghttp2ver.h + tests/Makefile + tests/testdata/Makefile + third-party/Makefile + src/Makefile + src/includes/Makefile + src/libnghttp2_asio.pc + examples/Makefile + python/Makefile + python/setup.py + integration-tests/Makefile + integration-tests/config.go + integration-tests/setenv + doc/Makefile + doc/conf.py + doc/index.rst + doc/package_README.rst + doc/tutorial-client.rst + doc/tutorial-server.rst + doc/tutorial-hpack.rst + doc/nghttpx-howto.rst + doc/h2load-howto.rst + doc/libnghttp2_asio.rst + doc/python-apiref.rst + doc/building-android-binary.rst + doc/nghttp2.h.rst + doc/nghttp2ver.h.rst + doc/asio_http2.h.rst + doc/asio_http2_server.h.rst + doc/asio_http2_client.h.rst + doc/contribute.rst + contrib/Makefile +]) +AC_OUTPUT + +AC_MSG_NOTICE([summary of build options: + + Version: ${VERSION} shared $LT_CURRENT:$LT_REVISION:$LT_AGE + Host type: ${host} + Install prefix: ${prefix} + C compiler: ${CC} + CFLAGS: ${CFLAGS} + WARNCFLAGS: ${WARNCFLAGS} + LDFLAGS: ${LDFLAGS} + LIBS: ${LIBS} + CPPFLAGS: ${CPPFLAGS} + C preprocessor: ${CPP} + C++ compiler: ${CXX} + CXXFLAGS: ${CXXFLAGS} + CXXCPP: ${CXXCPP} + Library types: Shared=${enable_shared}, Static=${enable_static} + Python: + Python: ${PYTHON} + PYTHON_VERSION: ${PYTHON_VERSION} + pyexecdir: ${pyexecdir} + Python-dev: ${have_python_dev} + PYTHON_CPPFLAGS:${PYTHON_CPPFLAGS} + PYTHON_LDFLAGS: ${PYTHON_LDFLAGS} + Cython: ${CYTHON} + Test: + CUnit: ${have_cunit} + Failmalloc: ${enable_failmalloc} + Libs: + OpenSSL: ${have_openssl} + Libxml2: ${have_libxml2} + Libev: ${have_libev} + Libevent(SSL): ${have_libevent_openssl} + Spdylay: ${have_spdylay} + Jansson: ${have_jansson} + Jemalloc: ${have_jemalloc} + Boost CPPFLAGS: ${BOOST_CPPFLAGS} + Boost LDFLAGS: ${BOOST_LDFLAGS} + Boost::ASIO: ${BOOST_ASIO_LIB} + Boost::System: ${BOOST_SYSTEM_LIB} + Boost::Thread: ${BOOST_THREAD_LIB} + Features: + Applications: ${enable_app} + HPACK tools: ${enable_hpack_tools} + Libnghttp2_asio:${enable_asio_lib} + Examples: ${enable_examples} + Python bindings:${enable_python_bindings} + Threading: ${enable_threads} +]) diff --git a/contrib/Makefile.am b/contrib/Makefile.am new file mode 100644 index 0000000..07a3bf8 --- /dev/null +++ b/contrib/Makefile.am @@ -0,0 +1,44 @@ +# nghttp2 - HTTP/2 C Library + +# Copyright (c) 2014 Tatsuhiro Tsujikawa + +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: + +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. + +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +configfiles = nghttpx-init nghttpx.service nghttpx-upstart.conf + +EXTRA_DIST = $(configfiles:%=%.in) nghttpx-logrotate + +edit = sed -e 's|@bindir[@]|$(bindir)|g' + +nghttpx-init: %: $(srcdir)/%.in + rm -f $@ $@.tmp + $(edit) $< > $@.tmp + chmod +x $@.tmp + mv $@.tmp $@ + +nghttpx.service nghttpx-upstart.conf: %: $(srcdir)/%.in + $(edit) $< > $@ + +$(configfiles): Makefile + +all-local: $(configfiles) + +clean-local: + -rm -f nghttpx-init.tmp $(configfiles) diff --git a/contrib/Makefile.in b/contrib/Makefile.in new file mode 100644 index 0000000..357b6e3 --- /dev/null +++ b/contrib/Makefile.in @@ -0,0 +1,531 @@ +# Makefile.in generated by automake 1.14.1 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2013 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +# nghttp2 - HTTP/2 C Library + +# Copyright (c) 2014 Tatsuhiro Tsujikawa + +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: + +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. + +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +VPATH = @srcdir@ +am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)' +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +target_triplet = @target@ +subdir = contrib +DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/ax_boost_asio.m4 \ + $(top_srcdir)/m4/ax_boost_base.m4 \ + $(top_srcdir)/m4/ax_boost_system.m4 \ + $(top_srcdir)/m4/ax_boost_thread.m4 \ + $(top_srcdir)/m4/ax_check_compile_flag.m4 \ + $(top_srcdir)/m4/ax_cxx_compile_stdcxx_11.m4 \ + $(top_srcdir)/m4/ax_have_epoll.m4 \ + $(top_srcdir)/m4/ax_python_devel.m4 \ + $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/libxml2.m4 \ + $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ + $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +SOURCES = +DIST_SOURCES = +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +APPLDFLAGS = @APPLDFLAGS@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +BOOST_ASIO_LIB = @BOOST_ASIO_LIB@ +BOOST_CPPFLAGS = @BOOST_CPPFLAGS@ +BOOST_LDFLAGS = @BOOST_LDFLAGS@ +BOOST_SYSTEM_LIB = @BOOST_SYSTEM_LIB@ +BOOST_THREAD_LIB = @BOOST_THREAD_LIB@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CUNIT_CFLAGS = @CUNIT_CFLAGS@ +CUNIT_LIBS = @CUNIT_LIBS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +CYTHON = @CYTHON@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DLLTOOL = @DLLTOOL@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +GREP = @GREP@ +HAVE_CXX11 = @HAVE_CXX11@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +JANSSON_CFLAGS = @JANSSON_CFLAGS@ +JANSSON_LIBS = @JANSSON_LIBS@ +JEMALLOC_LIBS = @JEMALLOC_LIBS@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBEVENT_OPENSSL_CFLAGS = @LIBEVENT_OPENSSL_CFLAGS@ +LIBEVENT_OPENSSL_LIBS = @LIBEVENT_OPENSSL_LIBS@ +LIBEV_CFLAGS = @LIBEV_CFLAGS@ +LIBEV_LIBS = @LIBEV_LIBS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBSPDYLAY_CFLAGS = @LIBSPDYLAY_CFLAGS@ +LIBSPDYLAY_LIBS = @LIBSPDYLAY_LIBS@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +LT_AGE = @LT_AGE@ +LT_CURRENT = @LT_CURRENT@ +LT_REVISION = @LT_REVISION@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MKDIR_P = @MKDIR_P@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OPENSSL_CFLAGS = @OPENSSL_CFLAGS@ +OPENSSL_LIBS = @OPENSSL_LIBS@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PACKAGE_VERSION_NUM = @PACKAGE_VERSION_NUM@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PKG_CONFIG = @PKG_CONFIG@ +PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ +PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ +PYTHON = @PYTHON@ +PYTHON_CPPFLAGS = @PYTHON_CPPFLAGS@ +PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@ +PYTHON_EXTRA_LDFLAGS = @PYTHON_EXTRA_LDFLAGS@ +PYTHON_EXTRA_LIBS = @PYTHON_EXTRA_LIBS@ +PYTHON_LDFLAGS = @PYTHON_LDFLAGS@ +PYTHON_PLATFORM = @PYTHON_PLATFORM@ +PYTHON_PREFIX = @PYTHON_PREFIX@ +PYTHON_SITE_PKG = @PYTHON_SITE_PKG@ +PYTHON_VERSION = @PYTHON_VERSION@ +RANLIB = @RANLIB@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +TESTLDADD = @TESTLDADD@ +VERSION = @VERSION@ +WARNCFLAGS = @WARNCFLAGS@ +XML2_CONFIG = @XML2_CONFIG@ +XML_CPPFLAGS = @XML_CPPFLAGS@ +XML_LIBS = @XML_LIBS@ +ZLIB_CFLAGS = @ZLIB_CFLAGS@ +ZLIB_LIBS = @ZLIB_LIBS@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +pkgpyexecdir = @pkgpyexecdir@ +pkgpythondir = @pkgpythondir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +pyexecdir = @pyexecdir@ +pythondir = @pythondir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target = @target@ +target_alias = @target_alias@ +target_cpu = @target_cpu@ +target_os = @target_os@ +target_vendor = @target_vendor@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +configfiles = nghttpx-init nghttpx.service nghttpx-upstart.conf +EXTRA_DIST = $(configfiles:%=%.in) nghttpx-logrotate +edit = sed -e 's|@bindir[@]|$(bindir)|g' +all: all-am + +.SUFFIXES: +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu contrib/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu contrib/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs +tags TAGS: + +ctags CTAGS: + +cscope cscopelist: + + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile all-local +installdirs: +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic clean-libtool clean-local mostlyclean-am + +distclean: distclean-am + -rm -f Makefile +distclean-am: clean-am distclean-generic + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-generic mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: + +.MAKE: install-am install-strip + +.PHONY: all all-am all-local check check-am clean clean-generic \ + clean-libtool clean-local cscopelist-am ctags-am distclean \ + distclean-generic distclean-libtool distdir dvi dvi-am html \ + html-am info info-am install install-am install-data \ + install-data-am install-dvi install-dvi-am install-exec \ + install-exec-am install-html install-html-am install-info \ + install-info-am install-man install-pdf install-pdf-am \ + install-ps install-ps-am install-strip installcheck \ + installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-generic \ + mostlyclean-libtool pdf pdf-am ps ps-am tags-am uninstall \ + uninstall-am + + +nghttpx-init: %: $(srcdir)/%.in + rm -f $@ $@.tmp + $(edit) $< > $@.tmp + chmod +x $@.tmp + mv $@.tmp $@ + +nghttpx.service nghttpx-upstart.conf: %: $(srcdir)/%.in + $(edit) $< > $@ + +$(configfiles): Makefile + +all-local: $(configfiles) + +clean-local: + -rm -f nghttpx-init.tmp $(configfiles) + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/contrib/nghttpx-init.in b/contrib/nghttpx-init.in new file mode 100644 index 0000000..a1620a9 --- /dev/null +++ b/contrib/nghttpx-init.in @@ -0,0 +1,173 @@ +#! /bin/sh +### BEGIN INIT INFO +# Provides: nghttpx +# Required-Start: $remote_fs $syslog +# Required-Stop: $remote_fs $syslog +# Default-Start: 2 3 4 5 +# Default-Stop: 0 1 6 +# Short-Description: nghttpx initscript +# Description: nghttpx initscript +### END INIT INFO + +# Author: Tatsuhiro Tsujikawa +# +# Do NOT "set -e" + +# PATH should only include /usr/* if it runs after the mountnfs.sh script +PATH=/sbin:/usr/sbin:/bin:/usr/bin:/usr/local/bin +DESC="HTTP/2 reverse proxy" +NAME=nghttpx +# Depending on the configuration, binary may be located under @sbindir@ +DAEMON=@bindir@/$NAME +PIDFILE=/var/run/$NAME.pid +DAEMON_ARGS="--conf /etc/nghttpx/nghttpx.conf --pid-file=$PIDFILE" +SCRIPTNAME=/etc/init.d/$NAME + +# Exit if the package is not installed +[ -x "$DAEMON" ] || exit 0 + +# Read configuration variable file if it is present +[ -r /etc/default/$NAME ] && . /etc/default/$NAME + +# Load the VERBOSE setting and other rcS variables +. /lib/init/vars.sh + +# Define LSB log_* functions. +# Depend on lsb-base (>= 3.2-14) to ensure that this file is present +# and status_of_proc is working. +. /lib/lsb/init-functions + +# +# Function that starts the daemon/service +# +do_start() +{ + # Return + # 0 if daemon has been started + # 1 if daemon was already running + # 2 if daemon could not be started + start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON --test > /dev/null \ + || return 1 + start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON -- \ + $DAEMON_ARGS \ + || return 2 + # Add code here, if necessary, that waits for the process to be ready + # to handle requests from services started subsequently which depend + # on this one. As a last resort, sleep for some time. +} + +# +# Function that stops the daemon/service +# +do_stop() +{ + # Return + # 0 if daemon has been stopped + # 1 if daemon was already stopped + # 2 if daemon could not be stopped + # other if a failure occurred + start-stop-daemon --stop --quiet --retry=TERM/30/KILL/5 --pidfile $PIDFILE + RETVAL="$?" + [ "$RETVAL" = 2 ] && return 2 + + # Wait for children to finish too if this is a daemon that forks + # and if the daemon is only ever run from this initscript. + # If the above conditions are not satisfied then add some other code + # that waits for the process to drop all resources that could be + # needed by services started subsequently. A last resort is to + # sleep for some time. + #start-stop-daemon --stop --quiet --oknodo --retry=0/30/KILL/5 --exec $DAEMON + #[ "$?" = 2 ] && return 2 + # Many daemons don't delete their pidfiles when they exit. + rm -f $PIDFILE + return "$RETVAL" +} + +# +# Function that sends a SIGHUP to the daemon/service +# +do_reload() { + # + # If the daemon can reload its configuration without + # restarting (for example, when it is sent a SIGHUP), + # then implement that here. + # + start-stop-daemon --stop --signal 1 --quiet --pidfile $PIDFILE --name $NAME + return 0 +} + +case "$1" in + start) + [ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC" "$NAME" + do_start + case "$?" in + 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;; + 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;; + esac + ;; + stop) + [ "$VERBOSE" != no ] && log_daemon_msg "Stopping $DESC" "$NAME" + do_stop + case "$?" in + 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;; + 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;; + esac + ;; + status) + status_of_proc "$DAEMON" "$NAME" && exit 0 || exit $? + ;; + upgrade) + log_daemon_msg "Upgrade $DESC" "$NAME" + pid=`pidofproc -p $PIDFILE $NAME` + case "$?" in + 0) echo "Sending USR2 signal to $pid" + kill -USR2 $pid + echo "Waiting for new binary..." + sleep 5 + echo "Sending QUIT signal to $pid" + kill -QUIT $pid + log_end_msg 0 + ;; + *) echo "pidofproc() failed" + log_end_msg 1 + ;; + esac + ;; + #reload|force-reload) + # + # If do_reload() is not implemented then leave this commented out + # and leave 'force-reload' as an alias for 'restart'. + # + #log_daemon_msg "Reloading $DESC" "$NAME" + #do_reload + #log_end_msg $? + #;; + restart|force-reload) + # + # If the "reload" option is implemented then remove the + # 'force-reload' alias + # + log_daemon_msg "Restarting $DESC" "$NAME" + do_stop + case "$?" in + 0|1) + do_start + case "$?" in + 0) log_end_msg 0 ;; + 1) log_end_msg 1 ;; # Old process is still running + *) log_end_msg 1 ;; # Failed to start + esac + ;; + *) + # Failed to stop + log_end_msg 1 + ;; + esac + ;; + *) + echo "Usage: $SCRIPTNAME {start|stop|status|restart|force-reload|upgrade}" >&2 + exit 3 + ;; +esac + +: diff --git a/contrib/nghttpx-logrotate b/contrib/nghttpx-logrotate new file mode 100644 index 0000000..f1002bd --- /dev/null +++ b/contrib/nghttpx-logrotate @@ -0,0 +1,11 @@ +/var/log/nghttpx/*.log { + weekly + rotate 52 + missingok + compress + delaycompress + notifempty + postrotate + killall -USR1 nghttpx 2> /dev/null || true + endscript +} diff --git a/contrib/nghttpx-upstart.conf.in b/contrib/nghttpx-upstart.conf.in new file mode 100644 index 0000000..0b79916 --- /dev/null +++ b/contrib/nghttpx-upstart.conf.in @@ -0,0 +1,8 @@ +# vim: ft=upstart: + +description "HTTP/2 reverse proxy" + +start on runlevel [2] +stop on runlevel [016] + +exec @bindir@/nghttpx diff --git a/contrib/nghttpx.service.in b/contrib/nghttpx.service.in new file mode 100644 index 0000000..9c7f851 --- /dev/null +++ b/contrib/nghttpx.service.in @@ -0,0 +1,10 @@ +[Unit] +Description=HTTP/2 experimental proxy +After=network.target + +[Service] +Type=simple +ExecStart=@bindir@/nghttpx --errorlog-syslog + +[Install] +WantedBy=multi-user.target diff --git a/depcomp b/depcomp new file mode 100755 index 0000000..4ebd5b3 --- /dev/null +++ b/depcomp @@ -0,0 +1,791 @@ +#! /bin/sh +# depcomp - compile a program generating dependencies as side-effects + +scriptversion=2013-05-30.07; # UTC + +# Copyright (C) 1999-2013 Free Software Foundation, Inc. + +# 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 2, 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 . + +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +# Originally written by Alexandre Oliva . + +case $1 in + '') + echo "$0: No command. Try '$0 --help' for more information." 1>&2 + exit 1; + ;; + -h | --h*) + cat <<\EOF +Usage: depcomp [--help] [--version] PROGRAM [ARGS] + +Run PROGRAMS ARGS to compile a file, generating dependencies +as side-effects. + +Environment variables: + depmode Dependency tracking mode. + source Source file read by 'PROGRAMS ARGS'. + object Object file output by 'PROGRAMS ARGS'. + DEPDIR directory where to store dependencies. + depfile Dependency file to output. + tmpdepfile Temporary file to use when outputting dependencies. + libtool Whether libtool is used (yes/no). + +Report bugs to . +EOF + exit $? + ;; + -v | --v*) + echo "depcomp $scriptversion" + exit $? + ;; +esac + +# Get the directory component of the given path, and save it in the +# global variables '$dir'. Note that this directory component will +# be either empty or ending with a '/' character. This is deliberate. +set_dir_from () +{ + case $1 in + */*) dir=`echo "$1" | sed -e 's|/[^/]*$|/|'`;; + *) dir=;; + esac +} + +# Get the suffix-stripped basename of the given path, and save it the +# global variable '$base'. +set_base_from () +{ + base=`echo "$1" | sed -e 's|^.*/||' -e 's/\.[^.]*$//'` +} + +# If no dependency file was actually created by the compiler invocation, +# we still have to create a dummy depfile, to avoid errors with the +# Makefile "include basename.Plo" scheme. +make_dummy_depfile () +{ + echo "#dummy" > "$depfile" +} + +# Factor out some common post-processing of the generated depfile. +# Requires the auxiliary global variable '$tmpdepfile' to be set. +aix_post_process_depfile () +{ + # If the compiler actually managed to produce a dependency file, + # post-process it. + if test -f "$tmpdepfile"; then + # Each line is of the form 'foo.o: dependency.h'. + # Do two passes, one to just change these to + # $object: dependency.h + # and one to simply output + # dependency.h: + # which is needed to avoid the deleted-header problem. + { sed -e "s,^.*\.[$lower]*:,$object:," < "$tmpdepfile" + sed -e "s,^.*\.[$lower]*:[$tab ]*,," -e 's,$,:,' < "$tmpdepfile" + } > "$depfile" + rm -f "$tmpdepfile" + else + make_dummy_depfile + fi +} + +# A tabulation character. +tab=' ' +# A newline character. +nl=' +' +# Character ranges might be problematic outside the C locale. +# These definitions help. +upper=ABCDEFGHIJKLMNOPQRSTUVWXYZ +lower=abcdefghijklmnopqrstuvwxyz +digits=0123456789 +alpha=${upper}${lower} + +if test -z "$depmode" || test -z "$source" || test -z "$object"; then + echo "depcomp: Variables source, object and depmode must be set" 1>&2 + exit 1 +fi + +# Dependencies for sub/bar.o or sub/bar.obj go into sub/.deps/bar.Po. +depfile=${depfile-`echo "$object" | + sed 's|[^\\/]*$|'${DEPDIR-.deps}'/&|;s|\.\([^.]*\)$|.P\1|;s|Pobj$|Po|'`} +tmpdepfile=${tmpdepfile-`echo "$depfile" | sed 's/\.\([^.]*\)$/.T\1/'`} + +rm -f "$tmpdepfile" + +# Avoid interferences from the environment. +gccflag= dashmflag= + +# Some modes work just like other modes, but use different flags. We +# parameterize here, but still list the modes in the big case below, +# to make depend.m4 easier to write. Note that we *cannot* use a case +# here, because this file can only contain one case statement. +if test "$depmode" = hp; then + # HP compiler uses -M and no extra arg. + gccflag=-M + depmode=gcc +fi + +if test "$depmode" = dashXmstdout; then + # This is just like dashmstdout with a different argument. + dashmflag=-xM + depmode=dashmstdout +fi + +cygpath_u="cygpath -u -f -" +if test "$depmode" = msvcmsys; then + # This is just like msvisualcpp but w/o cygpath translation. + # Just convert the backslash-escaped backslashes to single forward + # slashes to satisfy depend.m4 + cygpath_u='sed s,\\\\,/,g' + depmode=msvisualcpp +fi + +if test "$depmode" = msvc7msys; then + # This is just like msvc7 but w/o cygpath translation. + # Just convert the backslash-escaped backslashes to single forward + # slashes to satisfy depend.m4 + cygpath_u='sed s,\\\\,/,g' + depmode=msvc7 +fi + +if test "$depmode" = xlc; then + # IBM C/C++ Compilers xlc/xlC can output gcc-like dependency information. + gccflag=-qmakedep=gcc,-MF + depmode=gcc +fi + +case "$depmode" in +gcc3) +## gcc 3 implements dependency tracking that does exactly what +## we want. Yay! Note: for some reason libtool 1.4 doesn't like +## it if -MD -MP comes after the -MF stuff. Hmm. +## Unfortunately, FreeBSD c89 acceptance of flags depends upon +## the command line argument order; so add the flags where they +## appear in depend2.am. Note that the slowdown incurred here +## affects only configure: in makefiles, %FASTDEP% shortcuts this. + for arg + do + case $arg in + -c) set fnord "$@" -MT "$object" -MD -MP -MF "$tmpdepfile" "$arg" ;; + *) set fnord "$@" "$arg" ;; + esac + shift # fnord + shift # $arg + done + "$@" + stat=$? + if test $stat -ne 0; then + rm -f "$tmpdepfile" + exit $stat + fi + mv "$tmpdepfile" "$depfile" + ;; + +gcc) +## Note that this doesn't just cater to obsosete pre-3.x GCC compilers. +## but also to in-use compilers like IMB xlc/xlC and the HP C compiler. +## (see the conditional assignment to $gccflag above). +## There are various ways to get dependency output from gcc. Here's +## why we pick this rather obscure method: +## - Don't want to use -MD because we'd like the dependencies to end +## up in a subdir. Having to rename by hand is ugly. +## (We might end up doing this anyway to support other compilers.) +## - The DEPENDENCIES_OUTPUT environment variable makes gcc act like +## -MM, not -M (despite what the docs say). Also, it might not be +## supported by the other compilers which use the 'gcc' depmode. +## - Using -M directly means running the compiler twice (even worse +## than renaming). + if test -z "$gccflag"; then + gccflag=-MD, + fi + "$@" -Wp,"$gccflag$tmpdepfile" + stat=$? + if test $stat -ne 0; then + rm -f "$tmpdepfile" + exit $stat + fi + rm -f "$depfile" + echo "$object : \\" > "$depfile" + # The second -e expression handles DOS-style file names with drive + # letters. + sed -e 's/^[^:]*: / /' \ + -e 's/^['$alpha']:\/[^:]*: / /' < "$tmpdepfile" >> "$depfile" +## This next piece of magic avoids the "deleted header file" problem. +## The problem is that when a header file which appears in a .P file +## is deleted, the dependency causes make to die (because there is +## typically no way to rebuild the header). We avoid this by adding +## dummy dependencies for each header file. Too bad gcc doesn't do +## this for us directly. +## Some versions of gcc put a space before the ':'. On the theory +## that the space means something, we add a space to the output as +## well. hp depmode also adds that space, but also prefixes the VPATH +## to the object. Take care to not repeat it in the output. +## Some versions of the HPUX 10.20 sed can't process this invocation +## correctly. Breaking it into two sed invocations is a workaround. + tr ' ' "$nl" < "$tmpdepfile" \ + | sed -e 's/^\\$//' -e '/^$/d' -e "s|.*$object$||" -e '/:$/d' \ + | sed -e 's/$/ :/' >> "$depfile" + rm -f "$tmpdepfile" + ;; + +hp) + # This case exists only to let depend.m4 do its work. It works by + # looking at the text of this script. This case will never be run, + # since it is checked for above. + exit 1 + ;; + +sgi) + if test "$libtool" = yes; then + "$@" "-Wp,-MDupdate,$tmpdepfile" + else + "$@" -MDupdate "$tmpdepfile" + fi + stat=$? + if test $stat -ne 0; then + rm -f "$tmpdepfile" + exit $stat + fi + rm -f "$depfile" + + if test -f "$tmpdepfile"; then # yes, the sourcefile depend on other files + echo "$object : \\" > "$depfile" + # Clip off the initial element (the dependent). Don't try to be + # clever and replace this with sed code, as IRIX sed won't handle + # lines with more than a fixed number of characters (4096 in + # IRIX 6.2 sed, 8192 in IRIX 6.5). We also remove comment lines; + # the IRIX cc adds comments like '#:fec' to the end of the + # dependency line. + tr ' ' "$nl" < "$tmpdepfile" \ + | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' \ + | tr "$nl" ' ' >> "$depfile" + echo >> "$depfile" + # The second pass generates a dummy entry for each header file. + tr ' ' "$nl" < "$tmpdepfile" \ + | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' -e 's/$/:/' \ + >> "$depfile" + else + make_dummy_depfile + fi + rm -f "$tmpdepfile" + ;; + +xlc) + # This case exists only to let depend.m4 do its work. It works by + # looking at the text of this script. This case will never be run, + # since it is checked for above. + exit 1 + ;; + +aix) + # The C for AIX Compiler uses -M and outputs the dependencies + # in a .u file. In older versions, this file always lives in the + # current directory. Also, the AIX compiler puts '$object:' at the + # start of each line; $object doesn't have directory information. + # Version 6 uses the directory in both cases. + set_dir_from "$object" + set_base_from "$object" + if test "$libtool" = yes; then + tmpdepfile1=$dir$base.u + tmpdepfile2=$base.u + tmpdepfile3=$dir.libs/$base.u + "$@" -Wc,-M + else + tmpdepfile1=$dir$base.u + tmpdepfile2=$dir$base.u + tmpdepfile3=$dir$base.u + "$@" -M + fi + stat=$? + if test $stat -ne 0; then + rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" + exit $stat + fi + + for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" + do + test -f "$tmpdepfile" && break + done + aix_post_process_depfile + ;; + +tcc) + # tcc (Tiny C Compiler) understand '-MD -MF file' since version 0.9.26 + # FIXME: That version still under development at the moment of writing. + # Make that this statement remains true also for stable, released + # versions. + # It will wrap lines (doesn't matter whether long or short) with a + # trailing '\', as in: + # + # foo.o : \ + # foo.c \ + # foo.h \ + # + # It will put a trailing '\' even on the last line, and will use leading + # spaces rather than leading tabs (at least since its commit 0394caf7 + # "Emit spaces for -MD"). + "$@" -MD -MF "$tmpdepfile" + stat=$? + if test $stat -ne 0; then + rm -f "$tmpdepfile" + exit $stat + fi + rm -f "$depfile" + # Each non-empty line is of the form 'foo.o : \' or ' dep.h \'. + # We have to change lines of the first kind to '$object: \'. + sed -e "s|.*:|$object :|" < "$tmpdepfile" > "$depfile" + # And for each line of the second kind, we have to emit a 'dep.h:' + # dummy dependency, to avoid the deleted-header problem. + sed -n -e 's|^ *\(.*\) *\\$|\1:|p' < "$tmpdepfile" >> "$depfile" + rm -f "$tmpdepfile" + ;; + +## The order of this option in the case statement is important, since the +## shell code in configure will try each of these formats in the order +## listed in this file. A plain '-MD' option would be understood by many +## compilers, so we must ensure this comes after the gcc and icc options. +pgcc) + # Portland's C compiler understands '-MD'. + # Will always output deps to 'file.d' where file is the root name of the + # source file under compilation, even if file resides in a subdirectory. + # The object file name does not affect the name of the '.d' file. + # pgcc 10.2 will output + # foo.o: sub/foo.c sub/foo.h + # and will wrap long lines using '\' : + # foo.o: sub/foo.c ... \ + # sub/foo.h ... \ + # ... + set_dir_from "$object" + # Use the source, not the object, to determine the base name, since + # that's sadly what pgcc will do too. + set_base_from "$source" + tmpdepfile=$base.d + + # For projects that build the same source file twice into different object + # files, the pgcc approach of using the *source* file root name can cause + # problems in parallel builds. Use a locking strategy to avoid stomping on + # the same $tmpdepfile. + lockdir=$base.d-lock + trap " + echo '$0: caught signal, cleaning up...' >&2 + rmdir '$lockdir' + exit 1 + " 1 2 13 15 + numtries=100 + i=$numtries + while test $i -gt 0; do + # mkdir is a portable test-and-set. + if mkdir "$lockdir" 2>/dev/null; then + # This process acquired the lock. + "$@" -MD + stat=$? + # Release the lock. + rmdir "$lockdir" + break + else + # If the lock is being held by a different process, wait + # until the winning process is done or we timeout. + while test -d "$lockdir" && test $i -gt 0; do + sleep 1 + i=`expr $i - 1` + done + fi + i=`expr $i - 1` + done + trap - 1 2 13 15 + if test $i -le 0; then + echo "$0: failed to acquire lock after $numtries attempts" >&2 + echo "$0: check lockdir '$lockdir'" >&2 + exit 1 + fi + + if test $stat -ne 0; then + rm -f "$tmpdepfile" + exit $stat + fi + rm -f "$depfile" + # Each line is of the form `foo.o: dependent.h', + # or `foo.o: dep1.h dep2.h \', or ` dep3.h dep4.h \'. + # Do two passes, one to just change these to + # `$object: dependent.h' and one to simply `dependent.h:'. + sed "s,^[^:]*:,$object :," < "$tmpdepfile" > "$depfile" + # Some versions of the HPUX 10.20 sed can't process this invocation + # correctly. Breaking it into two sed invocations is a workaround. + sed 's,^[^:]*: \(.*\)$,\1,;s/^\\$//;/^$/d;/:$/d' < "$tmpdepfile" \ + | sed -e 's/$/ :/' >> "$depfile" + rm -f "$tmpdepfile" + ;; + +hp2) + # The "hp" stanza above does not work with aCC (C++) and HP's ia64 + # compilers, which have integrated preprocessors. The correct option + # to use with these is +Maked; it writes dependencies to a file named + # 'foo.d', which lands next to the object file, wherever that + # happens to be. + # Much of this is similar to the tru64 case; see comments there. + set_dir_from "$object" + set_base_from "$object" + if test "$libtool" = yes; then + tmpdepfile1=$dir$base.d + tmpdepfile2=$dir.libs/$base.d + "$@" -Wc,+Maked + else + tmpdepfile1=$dir$base.d + tmpdepfile2=$dir$base.d + "$@" +Maked + fi + stat=$? + if test $stat -ne 0; then + rm -f "$tmpdepfile1" "$tmpdepfile2" + exit $stat + fi + + for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" + do + test -f "$tmpdepfile" && break + done + if test -f "$tmpdepfile"; then + sed -e "s,^.*\.[$lower]*:,$object:," "$tmpdepfile" > "$depfile" + # Add 'dependent.h:' lines. + sed -ne '2,${ + s/^ *// + s/ \\*$// + s/$/:/ + p + }' "$tmpdepfile" >> "$depfile" + else + make_dummy_depfile + fi + rm -f "$tmpdepfile" "$tmpdepfile2" + ;; + +tru64) + # The Tru64 compiler uses -MD to generate dependencies as a side + # effect. 'cc -MD -o foo.o ...' puts the dependencies into 'foo.o.d'. + # At least on Alpha/Redhat 6.1, Compaq CCC V6.2-504 seems to put + # dependencies in 'foo.d' instead, so we check for that too. + # Subdirectories are respected. + set_dir_from "$object" + set_base_from "$object" + + if test "$libtool" = yes; then + # Libtool generates 2 separate objects for the 2 libraries. These + # two compilations output dependencies in $dir.libs/$base.o.d and + # in $dir$base.o.d. We have to check for both files, because + # one of the two compilations can be disabled. We should prefer + # $dir$base.o.d over $dir.libs/$base.o.d because the latter is + # automatically cleaned when .libs/ is deleted, while ignoring + # the former would cause a distcleancheck panic. + tmpdepfile1=$dir$base.o.d # libtool 1.5 + tmpdepfile2=$dir.libs/$base.o.d # Likewise. + tmpdepfile3=$dir.libs/$base.d # Compaq CCC V6.2-504 + "$@" -Wc,-MD + else + tmpdepfile1=$dir$base.d + tmpdepfile2=$dir$base.d + tmpdepfile3=$dir$base.d + "$@" -MD + fi + + stat=$? + if test $stat -ne 0; then + rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" + exit $stat + fi + + for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" + do + test -f "$tmpdepfile" && break + done + # Same post-processing that is required for AIX mode. + aix_post_process_depfile + ;; + +msvc7) + if test "$libtool" = yes; then + showIncludes=-Wc,-showIncludes + else + showIncludes=-showIncludes + fi + "$@" $showIncludes > "$tmpdepfile" + stat=$? + grep -v '^Note: including file: ' "$tmpdepfile" + if test $stat -ne 0; then + rm -f "$tmpdepfile" + exit $stat + fi + rm -f "$depfile" + echo "$object : \\" > "$depfile" + # The first sed program below extracts the file names and escapes + # backslashes for cygpath. The second sed program outputs the file + # name when reading, but also accumulates all include files in the + # hold buffer in order to output them again at the end. This only + # works with sed implementations that can handle large buffers. + sed < "$tmpdepfile" -n ' +/^Note: including file: *\(.*\)/ { + s//\1/ + s/\\/\\\\/g + p +}' | $cygpath_u | sort -u | sed -n ' +s/ /\\ /g +s/\(.*\)/'"$tab"'\1 \\/p +s/.\(.*\) \\/\1:/ +H +$ { + s/.*/'"$tab"'/ + G + p +}' >> "$depfile" + echo >> "$depfile" # make sure the fragment doesn't end with a backslash + rm -f "$tmpdepfile" + ;; + +msvc7msys) + # This case exists only to let depend.m4 do its work. It works by + # looking at the text of this script. This case will never be run, + # since it is checked for above. + exit 1 + ;; + +#nosideeffect) + # This comment above is used by automake to tell side-effect + # dependency tracking mechanisms from slower ones. + +dashmstdout) + # Important note: in order to support this mode, a compiler *must* + # always write the preprocessed file to stdout, regardless of -o. + "$@" || exit $? + + # Remove the call to Libtool. + if test "$libtool" = yes; then + while test "X$1" != 'X--mode=compile'; do + shift + done + shift + fi + + # Remove '-o $object'. + IFS=" " + for arg + do + case $arg in + -o) + shift + ;; + $object) + shift + ;; + *) + set fnord "$@" "$arg" + shift # fnord + shift # $arg + ;; + esac + done + + test -z "$dashmflag" && dashmflag=-M + # Require at least two characters before searching for ':' + # in the target name. This is to cope with DOS-style filenames: + # a dependency such as 'c:/foo/bar' could be seen as target 'c' otherwise. + "$@" $dashmflag | + sed "s|^[$tab ]*[^:$tab ][^:][^:]*:[$tab ]*|$object: |" > "$tmpdepfile" + rm -f "$depfile" + cat < "$tmpdepfile" > "$depfile" + # Some versions of the HPUX 10.20 sed can't process this sed invocation + # correctly. Breaking it into two sed invocations is a workaround. + tr ' ' "$nl" < "$tmpdepfile" \ + | sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' \ + | sed -e 's/$/ :/' >> "$depfile" + rm -f "$tmpdepfile" + ;; + +dashXmstdout) + # This case only exists to satisfy depend.m4. It is never actually + # run, as this mode is specially recognized in the preamble. + exit 1 + ;; + +makedepend) + "$@" || exit $? + # Remove any Libtool call + if test "$libtool" = yes; then + while test "X$1" != 'X--mode=compile'; do + shift + done + shift + fi + # X makedepend + shift + cleared=no eat=no + for arg + do + case $cleared in + no) + set ""; shift + cleared=yes ;; + esac + if test $eat = yes; then + eat=no + continue + fi + case "$arg" in + -D*|-I*) + set fnord "$@" "$arg"; shift ;; + # Strip any option that makedepend may not understand. Remove + # the object too, otherwise makedepend will parse it as a source file. + -arch) + eat=yes ;; + -*|$object) + ;; + *) + set fnord "$@" "$arg"; shift ;; + esac + done + obj_suffix=`echo "$object" | sed 's/^.*\././'` + touch "$tmpdepfile" + ${MAKEDEPEND-makedepend} -o"$obj_suffix" -f"$tmpdepfile" "$@" + rm -f "$depfile" + # makedepend may prepend the VPATH from the source file name to the object. + # No need to regex-escape $object, excess matching of '.' is harmless. + sed "s|^.*\($object *:\)|\1|" "$tmpdepfile" > "$depfile" + # Some versions of the HPUX 10.20 sed can't process the last invocation + # correctly. Breaking it into two sed invocations is a workaround. + sed '1,2d' "$tmpdepfile" \ + | tr ' ' "$nl" \ + | sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' \ + | sed -e 's/$/ :/' >> "$depfile" + rm -f "$tmpdepfile" "$tmpdepfile".bak + ;; + +cpp) + # Important note: in order to support this mode, a compiler *must* + # always write the preprocessed file to stdout. + "$@" || exit $? + + # Remove the call to Libtool. + if test "$libtool" = yes; then + while test "X$1" != 'X--mode=compile'; do + shift + done + shift + fi + + # Remove '-o $object'. + IFS=" " + for arg + do + case $arg in + -o) + shift + ;; + $object) + shift + ;; + *) + set fnord "$@" "$arg" + shift # fnord + shift # $arg + ;; + esac + done + + "$@" -E \ + | sed -n -e '/^# [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' \ + -e '/^#line [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' \ + | sed '$ s: \\$::' > "$tmpdepfile" + rm -f "$depfile" + echo "$object : \\" > "$depfile" + cat < "$tmpdepfile" >> "$depfile" + sed < "$tmpdepfile" '/^$/d;s/^ //;s/ \\$//;s/$/ :/' >> "$depfile" + rm -f "$tmpdepfile" + ;; + +msvisualcpp) + # Important note: in order to support this mode, a compiler *must* + # always write the preprocessed file to stdout. + "$@" || exit $? + + # Remove the call to Libtool. + if test "$libtool" = yes; then + while test "X$1" != 'X--mode=compile'; do + shift + done + shift + fi + + IFS=" " + for arg + do + case "$arg" in + -o) + shift + ;; + $object) + shift + ;; + "-Gm"|"/Gm"|"-Gi"|"/Gi"|"-ZI"|"/ZI") + set fnord "$@" + shift + shift + ;; + *) + set fnord "$@" "$arg" + shift + shift + ;; + esac + done + "$@" -E 2>/dev/null | + sed -n '/^#line [0-9][0-9]* "\([^"]*\)"/ s::\1:p' | $cygpath_u | sort -u > "$tmpdepfile" + rm -f "$depfile" + echo "$object : \\" > "$depfile" + sed < "$tmpdepfile" -n -e 's% %\\ %g' -e '/^\(.*\)$/ s::'"$tab"'\1 \\:p' >> "$depfile" + echo "$tab" >> "$depfile" + sed < "$tmpdepfile" -n -e 's% %\\ %g' -e '/^\(.*\)$/ s::\1\::p' >> "$depfile" + rm -f "$tmpdepfile" + ;; + +msvcmsys) + # This case exists only to let depend.m4 do its work. It works by + # looking at the text of this script. This case will never be run, + # since it is checked for above. + exit 1 + ;; + +none) + exec "$@" + ;; + +*) + echo "Unknown depmode $depmode" 1>&2 + exit 1 + ;; +esac + +exit 0 + +# Local Variables: +# mode: shell-script +# sh-indentation: 2 +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "scriptversion=" +# time-stamp-format: "%:y-%02m-%02d.%02H" +# time-stamp-time-zone: "UTC" +# time-stamp-end: "; # UTC" +# End: diff --git a/doc/Makefile.am b/doc/Makefile.am new file mode 100644 index 0000000..0eefa44 --- /dev/null +++ b/doc/Makefile.am @@ -0,0 +1,320 @@ +# nghttp2 - HTTP/2 C Library + +# Copyright (c) 2012 Tatsuhiro Tsujikawa + +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: + +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. + +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +man_MANS = nghttp.1 nghttpd.1 nghttpx.1 h2load.1 + +APIDOCS= \ + apiref.rst \ + macros.rst \ + enums.rst \ + types.rst \ + nghttp2_check_header_name.rst \ + nghttp2_check_header_value.rst \ + nghttp2_hd_deflate_bound.rst \ + nghttp2_hd_deflate_change_table_size.rst \ + nghttp2_hd_deflate_del.rst \ + nghttp2_hd_deflate_hd.rst \ + nghttp2_hd_deflate_new.rst \ + nghttp2_hd_deflate_new2.rst \ + nghttp2_hd_inflate_change_table_size.rst \ + nghttp2_hd_inflate_del.rst \ + nghttp2_hd_inflate_end_headers.rst \ + nghttp2_hd_inflate_hd.rst \ + nghttp2_hd_inflate_new.rst \ + nghttp2_hd_inflate_new2.rst \ + nghttp2_is_fatal.rst \ + nghttp2_nv_compare_name.rst \ + nghttp2_option_del.rst \ + nghttp2_option_new.rst \ + nghttp2_option_set_no_auto_window_update.rst \ + nghttp2_option_set_no_http_messaging.rst \ + nghttp2_option_set_peer_max_concurrent_streams.rst \ + nghttp2_option_set_no_recv_client_magic.rst \ + nghttp2_pack_settings_payload.rst \ + nghttp2_priority_spec_check_default.rst \ + nghttp2_priority_spec_default_init.rst \ + nghttp2_priority_spec_init.rst \ + nghttp2_select_next_protocol.rst \ + nghttp2_session_callbacks_del.rst \ + nghttp2_session_callbacks_new.rst \ + nghttp2_session_callbacks_set_before_frame_send_callback.rst \ + nghttp2_session_callbacks_set_data_source_read_length_callback.rst \ + nghttp2_session_callbacks_set_on_begin_frame_callback.rst \ + nghttp2_session_callbacks_set_on_begin_headers_callback.rst \ + nghttp2_session_callbacks_set_on_data_chunk_recv_callback.rst \ + nghttp2_session_callbacks_set_on_frame_not_send_callback.rst \ + nghttp2_session_callbacks_set_on_frame_recv_callback.rst \ + nghttp2_session_callbacks_set_on_frame_send_callback.rst \ + nghttp2_session_callbacks_set_on_header_callback.rst \ + nghttp2_session_callbacks_set_on_invalid_frame_recv_callback.rst \ + nghttp2_session_callbacks_set_on_stream_close_callback.rst \ + nghttp2_session_callbacks_set_recv_callback.rst \ + nghttp2_session_callbacks_set_select_padding_callback.rst \ + nghttp2_session_callbacks_set_send_callback.rst \ + nghttp2_session_callbacks_set_send_data_callback.rst \ + nghttp2_session_client_new.rst \ + nghttp2_session_client_new2.rst \ + nghttp2_session_client_new3.rst \ + nghttp2_session_consume.rst \ + nghttp2_session_consume_connection.rst \ + nghttp2_session_consume_stream.rst \ + nghttp2_session_del.rst \ + nghttp2_session_get_effective_local_window_size.rst \ + nghttp2_session_get_effective_recv_data_length.rst \ + nghttp2_session_get_last_proc_stream_id.rst \ + nghttp2_session_get_next_stream_id.rst \ + nghttp2_session_get_outbound_queue_size.rst \ + nghttp2_session_get_remote_settings.rst \ + nghttp2_session_get_remote_window_size.rst \ + nghttp2_session_get_stream_effective_local_window_size.rst \ + nghttp2_session_get_stream_effective_recv_data_length.rst \ + nghttp2_session_get_stream_local_close.rst \ + nghttp2_session_get_stream_remote_close.rst \ + nghttp2_session_get_stream_remote_window_size.rst \ + nghttp2_session_get_stream_user_data.rst \ + nghttp2_session_mem_recv.rst \ + nghttp2_session_mem_send.rst \ + nghttp2_session_recv.rst \ + nghttp2_session_resume_data.rst \ + nghttp2_session_send.rst \ + nghttp2_session_server_new.rst \ + nghttp2_session_server_new2.rst \ + nghttp2_session_server_new3.rst \ + nghttp2_session_set_next_stream_id.rst \ + nghttp2_session_set_stream_user_data.rst \ + nghttp2_session_terminate_session.rst \ + nghttp2_session_terminate_session2.rst \ + nghttp2_session_upgrade.rst \ + nghttp2_session_want_read.rst \ + nghttp2_session_want_write.rst \ + nghttp2_strerror.rst \ + nghttp2_submit_data.rst \ + nghttp2_submit_goaway.rst \ + nghttp2_submit_headers.rst \ + nghttp2_submit_ping.rst \ + nghttp2_submit_priority.rst \ + nghttp2_submit_push_promise.rst \ + nghttp2_submit_request.rst \ + nghttp2_submit_response.rst \ + nghttp2_submit_rst_stream.rst \ + nghttp2_submit_settings.rst \ + nghttp2_submit_shutdown_notice.rst \ + nghttp2_submit_trailer.rst \ + nghttp2_submit_window_update.rst \ + nghttp2_version.rst + +EXTRA_DIST = \ + mkapiref.py \ + README.rst \ + programmers-guide.rst \ + $(APIDOCS) \ + nghttp.1.rst \ + nghttpd.1.rst \ + nghttpx.1.rst \ + h2load.1.rst \ + sources/index.rst \ + sources/tutorial-client.rst \ + sources/tutorial-server.rst \ + sources/tutorial-hpack.rst \ + sources/nghttpx-howto.rst \ + sources/h2load-howto.rst \ + sources/libnghttp2_asio.rst \ + sources/python-apiref.rst \ + sources/building-android-binary.rst \ + sources/contribute.rst \ + _themes/sphinx_rtd_theme/__init__.py \ + _themes/sphinx_rtd_theme/breadcrumbs.html \ + _themes/sphinx_rtd_theme/footer.html \ + _themes/sphinx_rtd_theme/layout.html \ + _themes/sphinx_rtd_theme/layout_old.html \ + _themes/sphinx_rtd_theme/search.html \ + _themes/sphinx_rtd_theme/searchbox.html \ + _themes/sphinx_rtd_theme/static/css/badge_only.css \ + _themes/sphinx_rtd_theme/static/css/theme.css \ + _themes/sphinx_rtd_theme/static/fonts/FontAwesome.otf \ + _themes/sphinx_rtd_theme/static/fonts/fontawesome-webfont.eot \ + _themes/sphinx_rtd_theme/static/fonts/fontawesome-webfont.svg \ + _themes/sphinx_rtd_theme/static/fonts/fontawesome-webfont.ttf \ + _themes/sphinx_rtd_theme/static/fonts/fontawesome-webfont.woff \ + _themes/sphinx_rtd_theme/static/js/theme.js \ + _themes/sphinx_rtd_theme/theme.conf \ + _themes/sphinx_rtd_theme/versions.html \ + $(man_MANS) \ + bash_completion/nghttp \ + bash_completion/nghttpd \ + bash_completion/nghttpx \ + bash_completion/h2load + +# Makefile for Sphinx documentation +# + +# You can set these variables from the command line. +SPHINXOPTS = +SPHINXBUILD = sphinx-build +PAPER = +BUILDDIR = manual + +# Internal variables. +PAPEROPT_a4 = -D latex_paper_size=a4 +PAPEROPT_letter = -D latex_paper_size=letter +ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . + +.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest + +help: + @echo "Please use \`make ' where is one of" + @echo " html to make standalone HTML files" + @echo " dirhtml to make HTML files named index.html in directories" + @echo " singlehtml to make a single large HTML file" + @echo " pickle to make pickle files" + @echo " json to make JSON files" + @echo " htmlhelp to make HTML files and a HTML help project" + @echo " qthelp to make HTML files and a qthelp project" + @echo " devhelp to make HTML files and a Devhelp project" + @echo " epub to make an epub" + @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" + @echo " latexpdf to make LaTeX files and run them through pdflatex" + @echo " text to make text files" + @echo " man to make manual pages" + @echo " changes to make an overview of all changed/added/deprecated items" + @echo " linkcheck to check all external links for integrity" + @echo " doctest to run all doctests embedded in the documentation (if enabled)" + +apiref.rst macros.rst enums.rst types.rst: \ + $(top_builddir)/lib/includes/nghttp2/nghttp2ver.h \ + $(top_builddir)/lib/includes/nghttp2/nghttp2.h + $(PYTHON) $(top_srcdir)/doc/mkapiref.py \ + $@ macros.rst enums.rst types.rst . $^ + +# Inspired by +# http://www.gnu.org/savannah-checkouts/gnu/automake/manual/html_node/Multiple-Outputs.html +apidoc.stamp: $(top_builddir)/lib/includes/nghttp2/nghttp2ver.h \ + $(top_builddir)/lib/includes/nghttp2/nghttp2.h + @rm -f apidoc.tmp + @touch apidoc.tmp + $(PYTHON) $(top_srcdir)/doc/mkapiref.py \ + $@ macros.rst enums.rst types.rst . $^ + @mv -f apidoc.tmp $@ +$(APIDOC): apidoc.stamp +## Recover from the removal of $@ + @if test -f $@; then :; else \ + rm -f apidoc.stamp; \ + $(MAKE) $(AM_MAKEFLAGS) apidoc.stamp; \ + fi + +clean-local: + -rm $(APIDOCS) + -rm -rf $(BUILDDIR)/* + +html-local: apiref.rst + $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." + +dirhtml: + $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." + +singlehtml: + $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml + @echo + @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." + +pickle: + $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle + @echo + @echo "Build finished; now you can process the pickle files." + +json: + $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json + @echo + @echo "Build finished; now you can process the JSON files." + +htmlhelp: + $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp + @echo + @echo "Build finished; now you can run HTML Help Workshop with the" \ + ".hhp project file in $(BUILDDIR)/htmlhelp." + +qthelp: + $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp + @echo + @echo "Build finished; now you can run "qcollectiongenerator" with the" \ + ".qhcp project file in $(BUILDDIR)/qthelp, like this:" + @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/nghttp2.qhcp" + @echo "To view the help file:" + @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/nghttp2.qhc" + +devhelp: + $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp + @echo + @echo "Build finished." + @echo "To view the help file:" + @echo "# mkdir -p $$HOME/.local/share/devhelp/nghttp2" + @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/nghttp2" + @echo "# devhelp" + +epub: + $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub + @echo + @echo "Build finished. The epub file is in $(BUILDDIR)/epub." + +latex: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo + @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." + @echo "Run \`make' in that directory to run these through (pdf)latex" \ + "(use \`make latexpdf' here to do that automatically)." + +latexpdf: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo "Running LaTeX files through pdflatex..." + $(MAKE) -C $(BUILDDIR)/latex all-pdf + @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." + +text: + $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text + @echo + @echo "Build finished. The text files are in $(BUILDDIR)/text." + +man: apiref.rst + $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man + @echo + @echo "Build finished. The manual pages are in $(BUILDDIR)/man." + +changes: + $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes + @echo + @echo "The overview file is in $(BUILDDIR)/changes." + +linkcheck: + $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck + @echo + @echo "Link check complete; look for any errors in the above output " \ + "or in $(BUILDDIR)/linkcheck/output.txt." + +doctest: + $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest + @echo "Testing of doctests in the sources finished, look at the " \ + "results in $(BUILDDIR)/doctest/output.txt." diff --git a/doc/Makefile.in b/doc/Makefile.in new file mode 100644 index 0000000..0f53eb8 --- /dev/null +++ b/doc/Makefile.in @@ -0,0 +1,939 @@ +# Makefile.in generated by automake 1.14.1 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2013 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +# nghttp2 - HTTP/2 C Library + +# Copyright (c) 2012 Tatsuhiro Tsujikawa + +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: + +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. + +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +VPATH = @srcdir@ +am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)' +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +target_triplet = @target@ +subdir = doc +DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \ + $(srcdir)/conf.py.in $(srcdir)/index.rst.in \ + $(srcdir)/package_README.rst.in \ + $(srcdir)/tutorial-client.rst.in \ + $(srcdir)/tutorial-server.rst.in \ + $(srcdir)/tutorial-hpack.rst.in $(srcdir)/nghttpx-howto.rst.in \ + $(srcdir)/h2load-howto.rst.in $(srcdir)/libnghttp2_asio.rst.in \ + $(srcdir)/python-apiref.rst.in \ + $(srcdir)/building-android-binary.rst.in \ + $(srcdir)/nghttp2.h.rst.in $(srcdir)/nghttp2ver.h.rst.in \ + $(srcdir)/asio_http2.h.rst.in \ + $(srcdir)/asio_http2_server.h.rst.in \ + $(srcdir)/asio_http2_client.h.rst.in \ + $(srcdir)/contribute.rst.in +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/ax_boost_asio.m4 \ + $(top_srcdir)/m4/ax_boost_base.m4 \ + $(top_srcdir)/m4/ax_boost_system.m4 \ + $(top_srcdir)/m4/ax_boost_thread.m4 \ + $(top_srcdir)/m4/ax_check_compile_flag.m4 \ + $(top_srcdir)/m4/ax_cxx_compile_stdcxx_11.m4 \ + $(top_srcdir)/m4/ax_have_epoll.m4 \ + $(top_srcdir)/m4/ax_python_devel.m4 \ + $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/libxml2.m4 \ + $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ + $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = conf.py index.rst package_README.rst \ + tutorial-client.rst tutorial-server.rst tutorial-hpack.rst \ + nghttpx-howto.rst h2load-howto.rst libnghttp2_asio.rst \ + python-apiref.rst building-android-binary.rst nghttp2.h.rst \ + nghttp2ver.h.rst asio_http2.h.rst asio_http2_server.h.rst \ + asio_http2_client.h.rst contribute.rst +CONFIG_CLEAN_VPATH_FILES = +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +SOURCES = +DIST_SOURCES = +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +am__uninstall_files_from_dir = { \ + test -z "$$files" \ + || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ + || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ + $(am__cd) "$$dir" && rm -f $$files; }; \ + } +man1dir = $(mandir)/man1 +am__installdirs = "$(DESTDIR)$(man1dir)" +NROFF = nroff +MANS = $(man_MANS) +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +APPLDFLAGS = @APPLDFLAGS@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +BOOST_ASIO_LIB = @BOOST_ASIO_LIB@ +BOOST_CPPFLAGS = @BOOST_CPPFLAGS@ +BOOST_LDFLAGS = @BOOST_LDFLAGS@ +BOOST_SYSTEM_LIB = @BOOST_SYSTEM_LIB@ +BOOST_THREAD_LIB = @BOOST_THREAD_LIB@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CUNIT_CFLAGS = @CUNIT_CFLAGS@ +CUNIT_LIBS = @CUNIT_LIBS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +CYTHON = @CYTHON@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DLLTOOL = @DLLTOOL@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +GREP = @GREP@ +HAVE_CXX11 = @HAVE_CXX11@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +JANSSON_CFLAGS = @JANSSON_CFLAGS@ +JANSSON_LIBS = @JANSSON_LIBS@ +JEMALLOC_LIBS = @JEMALLOC_LIBS@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBEVENT_OPENSSL_CFLAGS = @LIBEVENT_OPENSSL_CFLAGS@ +LIBEVENT_OPENSSL_LIBS = @LIBEVENT_OPENSSL_LIBS@ +LIBEV_CFLAGS = @LIBEV_CFLAGS@ +LIBEV_LIBS = @LIBEV_LIBS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBSPDYLAY_CFLAGS = @LIBSPDYLAY_CFLAGS@ +LIBSPDYLAY_LIBS = @LIBSPDYLAY_LIBS@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +LT_AGE = @LT_AGE@ +LT_CURRENT = @LT_CURRENT@ +LT_REVISION = @LT_REVISION@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MKDIR_P = @MKDIR_P@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OPENSSL_CFLAGS = @OPENSSL_CFLAGS@ +OPENSSL_LIBS = @OPENSSL_LIBS@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PACKAGE_VERSION_NUM = @PACKAGE_VERSION_NUM@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PKG_CONFIG = @PKG_CONFIG@ +PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ +PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ +PYTHON = @PYTHON@ +PYTHON_CPPFLAGS = @PYTHON_CPPFLAGS@ +PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@ +PYTHON_EXTRA_LDFLAGS = @PYTHON_EXTRA_LDFLAGS@ +PYTHON_EXTRA_LIBS = @PYTHON_EXTRA_LIBS@ +PYTHON_LDFLAGS = @PYTHON_LDFLAGS@ +PYTHON_PLATFORM = @PYTHON_PLATFORM@ +PYTHON_PREFIX = @PYTHON_PREFIX@ +PYTHON_SITE_PKG = @PYTHON_SITE_PKG@ +PYTHON_VERSION = @PYTHON_VERSION@ +RANLIB = @RANLIB@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +TESTLDADD = @TESTLDADD@ +VERSION = @VERSION@ +WARNCFLAGS = @WARNCFLAGS@ +XML2_CONFIG = @XML2_CONFIG@ +XML_CPPFLAGS = @XML_CPPFLAGS@ +XML_LIBS = @XML_LIBS@ +ZLIB_CFLAGS = @ZLIB_CFLAGS@ +ZLIB_LIBS = @ZLIB_LIBS@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +pkgpyexecdir = @pkgpyexecdir@ +pkgpythondir = @pkgpythondir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +pyexecdir = @pyexecdir@ +pythondir = @pythondir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target = @target@ +target_alias = @target_alias@ +target_cpu = @target_cpu@ +target_os = @target_os@ +target_vendor = @target_vendor@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +man_MANS = nghttp.1 nghttpd.1 nghttpx.1 h2load.1 +APIDOCS = \ + apiref.rst \ + macros.rst \ + enums.rst \ + types.rst \ + nghttp2_check_header_name.rst \ + nghttp2_check_header_value.rst \ + nghttp2_hd_deflate_bound.rst \ + nghttp2_hd_deflate_change_table_size.rst \ + nghttp2_hd_deflate_del.rst \ + nghttp2_hd_deflate_hd.rst \ + nghttp2_hd_deflate_new.rst \ + nghttp2_hd_deflate_new2.rst \ + nghttp2_hd_inflate_change_table_size.rst \ + nghttp2_hd_inflate_del.rst \ + nghttp2_hd_inflate_end_headers.rst \ + nghttp2_hd_inflate_hd.rst \ + nghttp2_hd_inflate_new.rst \ + nghttp2_hd_inflate_new2.rst \ + nghttp2_is_fatal.rst \ + nghttp2_nv_compare_name.rst \ + nghttp2_option_del.rst \ + nghttp2_option_new.rst \ + nghttp2_option_set_no_auto_window_update.rst \ + nghttp2_option_set_no_http_messaging.rst \ + nghttp2_option_set_peer_max_concurrent_streams.rst \ + nghttp2_option_set_no_recv_client_magic.rst \ + nghttp2_pack_settings_payload.rst \ + nghttp2_priority_spec_check_default.rst \ + nghttp2_priority_spec_default_init.rst \ + nghttp2_priority_spec_init.rst \ + nghttp2_select_next_protocol.rst \ + nghttp2_session_callbacks_del.rst \ + nghttp2_session_callbacks_new.rst \ + nghttp2_session_callbacks_set_before_frame_send_callback.rst \ + nghttp2_session_callbacks_set_data_source_read_length_callback.rst \ + nghttp2_session_callbacks_set_on_begin_frame_callback.rst \ + nghttp2_session_callbacks_set_on_begin_headers_callback.rst \ + nghttp2_session_callbacks_set_on_data_chunk_recv_callback.rst \ + nghttp2_session_callbacks_set_on_frame_not_send_callback.rst \ + nghttp2_session_callbacks_set_on_frame_recv_callback.rst \ + nghttp2_session_callbacks_set_on_frame_send_callback.rst \ + nghttp2_session_callbacks_set_on_header_callback.rst \ + nghttp2_session_callbacks_set_on_invalid_frame_recv_callback.rst \ + nghttp2_session_callbacks_set_on_stream_close_callback.rst \ + nghttp2_session_callbacks_set_recv_callback.rst \ + nghttp2_session_callbacks_set_select_padding_callback.rst \ + nghttp2_session_callbacks_set_send_callback.rst \ + nghttp2_session_callbacks_set_send_data_callback.rst \ + nghttp2_session_client_new.rst \ + nghttp2_session_client_new2.rst \ + nghttp2_session_client_new3.rst \ + nghttp2_session_consume.rst \ + nghttp2_session_consume_connection.rst \ + nghttp2_session_consume_stream.rst \ + nghttp2_session_del.rst \ + nghttp2_session_get_effective_local_window_size.rst \ + nghttp2_session_get_effective_recv_data_length.rst \ + nghttp2_session_get_last_proc_stream_id.rst \ + nghttp2_session_get_next_stream_id.rst \ + nghttp2_session_get_outbound_queue_size.rst \ + nghttp2_session_get_remote_settings.rst \ + nghttp2_session_get_remote_window_size.rst \ + nghttp2_session_get_stream_effective_local_window_size.rst \ + nghttp2_session_get_stream_effective_recv_data_length.rst \ + nghttp2_session_get_stream_local_close.rst \ + nghttp2_session_get_stream_remote_close.rst \ + nghttp2_session_get_stream_remote_window_size.rst \ + nghttp2_session_get_stream_user_data.rst \ + nghttp2_session_mem_recv.rst \ + nghttp2_session_mem_send.rst \ + nghttp2_session_recv.rst \ + nghttp2_session_resume_data.rst \ + nghttp2_session_send.rst \ + nghttp2_session_server_new.rst \ + nghttp2_session_server_new2.rst \ + nghttp2_session_server_new3.rst \ + nghttp2_session_set_next_stream_id.rst \ + nghttp2_session_set_stream_user_data.rst \ + nghttp2_session_terminate_session.rst \ + nghttp2_session_terminate_session2.rst \ + nghttp2_session_upgrade.rst \ + nghttp2_session_want_read.rst \ + nghttp2_session_want_write.rst \ + nghttp2_strerror.rst \ + nghttp2_submit_data.rst \ + nghttp2_submit_goaway.rst \ + nghttp2_submit_headers.rst \ + nghttp2_submit_ping.rst \ + nghttp2_submit_priority.rst \ + nghttp2_submit_push_promise.rst \ + nghttp2_submit_request.rst \ + nghttp2_submit_response.rst \ + nghttp2_submit_rst_stream.rst \ + nghttp2_submit_settings.rst \ + nghttp2_submit_shutdown_notice.rst \ + nghttp2_submit_trailer.rst \ + nghttp2_submit_window_update.rst \ + nghttp2_version.rst + +EXTRA_DIST = \ + mkapiref.py \ + README.rst \ + programmers-guide.rst \ + $(APIDOCS) \ + nghttp.1.rst \ + nghttpd.1.rst \ + nghttpx.1.rst \ + h2load.1.rst \ + sources/index.rst \ + sources/tutorial-client.rst \ + sources/tutorial-server.rst \ + sources/tutorial-hpack.rst \ + sources/nghttpx-howto.rst \ + sources/h2load-howto.rst \ + sources/libnghttp2_asio.rst \ + sources/python-apiref.rst \ + sources/building-android-binary.rst \ + sources/contribute.rst \ + _themes/sphinx_rtd_theme/__init__.py \ + _themes/sphinx_rtd_theme/breadcrumbs.html \ + _themes/sphinx_rtd_theme/footer.html \ + _themes/sphinx_rtd_theme/layout.html \ + _themes/sphinx_rtd_theme/layout_old.html \ + _themes/sphinx_rtd_theme/search.html \ + _themes/sphinx_rtd_theme/searchbox.html \ + _themes/sphinx_rtd_theme/static/css/badge_only.css \ + _themes/sphinx_rtd_theme/static/css/theme.css \ + _themes/sphinx_rtd_theme/static/fonts/FontAwesome.otf \ + _themes/sphinx_rtd_theme/static/fonts/fontawesome-webfont.eot \ + _themes/sphinx_rtd_theme/static/fonts/fontawesome-webfont.svg \ + _themes/sphinx_rtd_theme/static/fonts/fontawesome-webfont.ttf \ + _themes/sphinx_rtd_theme/static/fonts/fontawesome-webfont.woff \ + _themes/sphinx_rtd_theme/static/js/theme.js \ + _themes/sphinx_rtd_theme/theme.conf \ + _themes/sphinx_rtd_theme/versions.html \ + $(man_MANS) \ + bash_completion/nghttp \ + bash_completion/nghttpd \ + bash_completion/nghttpx \ + bash_completion/h2load + + +# Makefile for Sphinx documentation +# + +# You can set these variables from the command line. +SPHINXOPTS = +SPHINXBUILD = sphinx-build +PAPER = +BUILDDIR = manual + +# Internal variables. +PAPEROPT_a4 = -D latex_paper_size=a4 +PAPEROPT_letter = -D latex_paper_size=letter +ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . +all: all-am + +.SUFFIXES: +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu doc/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu doc/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): +conf.py: $(top_builddir)/config.status $(srcdir)/conf.py.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +index.rst: $(top_builddir)/config.status $(srcdir)/index.rst.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +package_README.rst: $(top_builddir)/config.status $(srcdir)/package_README.rst.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +tutorial-client.rst: $(top_builddir)/config.status $(srcdir)/tutorial-client.rst.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +tutorial-server.rst: $(top_builddir)/config.status $(srcdir)/tutorial-server.rst.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +tutorial-hpack.rst: $(top_builddir)/config.status $(srcdir)/tutorial-hpack.rst.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +nghttpx-howto.rst: $(top_builddir)/config.status $(srcdir)/nghttpx-howto.rst.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +h2load-howto.rst: $(top_builddir)/config.status $(srcdir)/h2load-howto.rst.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +libnghttp2_asio.rst: $(top_builddir)/config.status $(srcdir)/libnghttp2_asio.rst.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +python-apiref.rst: $(top_builddir)/config.status $(srcdir)/python-apiref.rst.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +building-android-binary.rst: $(top_builddir)/config.status $(srcdir)/building-android-binary.rst.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +nghttp2.h.rst: $(top_builddir)/config.status $(srcdir)/nghttp2.h.rst.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +nghttp2ver.h.rst: $(top_builddir)/config.status $(srcdir)/nghttp2ver.h.rst.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +asio_http2.h.rst: $(top_builddir)/config.status $(srcdir)/asio_http2.h.rst.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +asio_http2_server.h.rst: $(top_builddir)/config.status $(srcdir)/asio_http2_server.h.rst.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +asio_http2_client.h.rst: $(top_builddir)/config.status $(srcdir)/asio_http2_client.h.rst.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +contribute.rst: $(top_builddir)/config.status $(srcdir)/contribute.rst.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs +install-man1: $(man_MANS) + @$(NORMAL_INSTALL) + @list1=''; \ + list2='$(man_MANS)'; \ + test -n "$(man1dir)" \ + && test -n "`echo $$list1$$list2`" \ + || exit 0; \ + echo " $(MKDIR_P) '$(DESTDIR)$(man1dir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(man1dir)" || exit 1; \ + { for i in $$list1; do echo "$$i"; done; \ + if test -n "$$list2"; then \ + for i in $$list2; do echo "$$i"; done \ + | sed -n '/\.1[a-z]*$$/p'; \ + fi; \ + } | while read p; do \ + if test -f $$p; then d=; else d="$(srcdir)/"; fi; \ + echo "$$d$$p"; echo "$$p"; \ + done | \ + sed -e 'n;s,.*/,,;p;h;s,.*\.,,;s,^[^1][0-9a-z]*$$,1,;x' \ + -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,' | \ + sed 'N;N;s,\n, ,g' | { \ + list=; while read file base inst; do \ + if test "$$base" = "$$inst"; then list="$$list $$file"; else \ + echo " $(INSTALL_DATA) '$$file' '$(DESTDIR)$(man1dir)/$$inst'"; \ + $(INSTALL_DATA) "$$file" "$(DESTDIR)$(man1dir)/$$inst" || exit $$?; \ + fi; \ + done; \ + for i in $$list; do echo "$$i"; done | $(am__base_list) | \ + while read files; do \ + test -z "$$files" || { \ + echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(man1dir)'"; \ + $(INSTALL_DATA) $$files "$(DESTDIR)$(man1dir)" || exit $$?; }; \ + done; } + +uninstall-man1: + @$(NORMAL_UNINSTALL) + @list=''; test -n "$(man1dir)" || exit 0; \ + files=`{ for i in $$list; do echo "$$i"; done; \ + l2='$(man_MANS)'; for i in $$l2; do echo "$$i"; done | \ + sed -n '/\.1[a-z]*$$/p'; \ + } | sed -e 's,.*/,,;h;s,.*\.,,;s,^[^1][0-9a-z]*$$,1,;x' \ + -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,'`; \ + dir='$(DESTDIR)$(man1dir)'; $(am__uninstall_files_from_dir) +tags TAGS: + +ctags CTAGS: + +cscope cscopelist: + + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(MANS) +installdirs: + for dir in "$(DESTDIR)$(man1dir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic clean-libtool clean-local mostlyclean-am + +distclean: distclean-am + -rm -f Makefile +distclean-am: clean-am distclean-generic + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: html-local + +info: info-am + +info-am: + +install-data-am: install-man + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: install-man1 + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-generic mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-man + +uninstall-man: uninstall-man1 + +.MAKE: install-am install-strip + +.PHONY: all all-am check check-am clean clean-generic clean-libtool \ + clean-local cscopelist-am ctags-am distclean distclean-generic \ + distclean-libtool distdir dvi dvi-am html html-am html-local \ + info info-am install install-am install-data install-data-am \ + install-dvi install-dvi-am install-exec install-exec-am \ + install-html install-html-am install-info install-info-am \ + install-man install-man1 install-pdf install-pdf-am install-ps \ + install-ps-am install-strip installcheck installcheck-am \ + installdirs maintainer-clean maintainer-clean-generic \ + mostlyclean mostlyclean-generic mostlyclean-libtool pdf pdf-am \ + ps ps-am tags-am uninstall uninstall-am uninstall-man \ + uninstall-man1 + + +.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest + +help: + @echo "Please use \`make ' where is one of" + @echo " html to make standalone HTML files" + @echo " dirhtml to make HTML files named index.html in directories" + @echo " singlehtml to make a single large HTML file" + @echo " pickle to make pickle files" + @echo " json to make JSON files" + @echo " htmlhelp to make HTML files and a HTML help project" + @echo " qthelp to make HTML files and a qthelp project" + @echo " devhelp to make HTML files and a Devhelp project" + @echo " epub to make an epub" + @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" + @echo " latexpdf to make LaTeX files and run them through pdflatex" + @echo " text to make text files" + @echo " man to make manual pages" + @echo " changes to make an overview of all changed/added/deprecated items" + @echo " linkcheck to check all external links for integrity" + @echo " doctest to run all doctests embedded in the documentation (if enabled)" + +apiref.rst macros.rst enums.rst types.rst: \ + $(top_builddir)/lib/includes/nghttp2/nghttp2ver.h \ + $(top_builddir)/lib/includes/nghttp2/nghttp2.h + $(PYTHON) $(top_srcdir)/doc/mkapiref.py \ + $@ macros.rst enums.rst types.rst . $^ + +# Inspired by +# http://www.gnu.org/savannah-checkouts/gnu/automake/manual/html_node/Multiple-Outputs.html +apidoc.stamp: $(top_builddir)/lib/includes/nghttp2/nghttp2ver.h \ + $(top_builddir)/lib/includes/nghttp2/nghttp2.h + @rm -f apidoc.tmp + @touch apidoc.tmp + $(PYTHON) $(top_srcdir)/doc/mkapiref.py \ + $@ macros.rst enums.rst types.rst . $^ + @mv -f apidoc.tmp $@ +$(APIDOC): apidoc.stamp + @if test -f $@; then :; else \ + rm -f apidoc.stamp; \ + $(MAKE) $(AM_MAKEFLAGS) apidoc.stamp; \ + fi + +clean-local: + -rm $(APIDOCS) + -rm -rf $(BUILDDIR)/* + +html-local: apiref.rst + $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." + +dirhtml: + $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." + +singlehtml: + $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml + @echo + @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." + +pickle: + $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle + @echo + @echo "Build finished; now you can process the pickle files." + +json: + $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json + @echo + @echo "Build finished; now you can process the JSON files." + +htmlhelp: + $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp + @echo + @echo "Build finished; now you can run HTML Help Workshop with the" \ + ".hhp project file in $(BUILDDIR)/htmlhelp." + +qthelp: + $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp + @echo + @echo "Build finished; now you can run "qcollectiongenerator" with the" \ + ".qhcp project file in $(BUILDDIR)/qthelp, like this:" + @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/nghttp2.qhcp" + @echo "To view the help file:" + @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/nghttp2.qhc" + +devhelp: + $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp + @echo + @echo "Build finished." + @echo "To view the help file:" + @echo "# mkdir -p $$HOME/.local/share/devhelp/nghttp2" + @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/nghttp2" + @echo "# devhelp" + +epub: + $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub + @echo + @echo "Build finished. The epub file is in $(BUILDDIR)/epub." + +latex: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo + @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." + @echo "Run \`make' in that directory to run these through (pdf)latex" \ + "(use \`make latexpdf' here to do that automatically)." + +latexpdf: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo "Running LaTeX files through pdflatex..." + $(MAKE) -C $(BUILDDIR)/latex all-pdf + @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." + +text: + $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text + @echo + @echo "Build finished. The text files are in $(BUILDDIR)/text." + +man: apiref.rst + $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man + @echo + @echo "Build finished. The manual pages are in $(BUILDDIR)/man." + +changes: + $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes + @echo + @echo "The overview file is in $(BUILDDIR)/changes." + +linkcheck: + $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck + @echo + @echo "Link check complete; look for any errors in the above output " \ + "or in $(BUILDDIR)/linkcheck/output.txt." + +doctest: + $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest + @echo "Testing of doctests in the sources finished, look at the " \ + "results in $(BUILDDIR)/doctest/output.txt." + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/doc/README.rst b/doc/README.rst new file mode 100644 index 0000000..35bfe7e --- /dev/null +++ b/doc/README.rst @@ -0,0 +1,160 @@ +nghttp2 Documentation +===================== + +The documentation of nghttp2 is generated using Sphinx. This +directory contains the source files to be processed by Sphinx. The +source file for API reference is generated using a script called +``mkapiref.py`` from the nghttp2 C source code. + +Generating API reference +------------------------ + +As described earlier, we use ``mkapiref.py`` to generate rst formatted +text of API reference from C source code. The ``mkapiref.py`` is not +so flexible and it requires that C source code is formatted in rather +strict rules. + +To generate API reference, just run ``make html``. It runs +``mkapiref.py`` and then run Sphinx to build the entire document. + +The ``mkapiref.py`` reads C source code and searches the comment block +starts with ``/**``. In other words, it only processes the comment +block starting ``/**``. The comment block must end with ``*/``. The +``mkapiref.py`` requires that which type of the object this comment +block refers to. To specify the type of the object, the next line +must contain the so-caled action keyword. Currently, the following +action keywords are supported: ``@function``, ``@functypedef``, +``@enum``, ``@struct`` and ``@union``. The following sections +describes each action keyword. + +@function +######### + +``@function`` is used to refer to the function. The comment block is +used for the document for the function. After the script sees the end +of the comment block, it consumes the lines as the function +declaration until the line which ends with ``;`` is encountered. + +In Sphinx doc, usually the function argument is formatted like +``*this*``. But in C, ``*`` is used for dereferencing a pointer and +we must escape ``*`` with a back slash. To avoid this, we format the +argument like ``|this|``. The ``mkapiref.py`` translates it with +``*this*``, as escaping ``*`` inside ``|`` and ``|`` as necessary. +Note that this shadows the substitution feature of Sphinx. + +The example follows:: + + /** + * @function + * + * Submits PING frame to the |session|. + */ + int nghttp2_submit_ping(nghttp2_session *session); + + +@functypedef +############ + +``@functypedef`` is used to refer to the typedef of the function +pointer. The formatting rule is pretty much the same with +``@function``, but this outputs ``type`` domain, rather than +``function`` domain. + +The example follows:: + + /** + * @functypedef + * + * Callback function invoked when |session| wants to send data to + * remote peer. + */ + typedef ssize_t (*nghttp2_send_callback) + (nghttp2_session *session, + const uint8_t *data, size_t length, int flags, void *user_data); + +@enum +##### + +``@enum`` is used to refer to the enum. Currently, only enum typedefs +are supported. The comment block is used for the document for the +enum type itself. To document each values, put comment block starting +with the line ``/**`` and ending with the ``*/`` just before the enum +value. When the line starts with ``}`` is encountered, the +``mkapiref.py`` extracts strings next to ``}`` as the name of enum. + +At the time of this writing, Sphinx does not support enum type. So we +use ``type`` domain for enum it self and ``macro`` domain for each +value. To refer to the enum value, use ``:enum:`` pseudo role. The +``mkapiref.py`` replaces it with ``:macro:``. By doing this, when +Sphinx will support enum officially, we can replace ``:enum:`` with +the official role easily. + +The example follows:: + + /** + * @enum + * Error codes used in the nghttp2 library. + */ + typedef enum { + /** + * Invalid argument passed. + */ + NGHTTP2_ERR_INVALID_ARGUMENT = -501, + /** + * Zlib error. + */ + NGHTTP2_ERR_ZLIB = -502, + } nghttp2_error; + +@struct +####### + +``@struct`` is used to refer to the struct. Currently, only struct +typedefs are supported. The comment block is used for the document for +the struct type itself.To document each member, put comment block +starting with the line ``/**`` and ending with the ``*/`` just before +the member. When the line starts with ``}`` is encountered, the +``mkapiref.py`` extracts strings next to ``}`` as the name of struct. +The block-less typedef is also supported. In this case, typedef +declaration must be all in one line and the ``mkapiref.py`` uses last +word as the name of struct. + +Some examples follow:: + + /** + * @struct + * The control frame header. + */ + typedef struct { + /** + * SPDY protocol version. + */ + uint16_t version; + /** + * The type of this control frame. + */ + uint16_t type; + /** + * The control frame flags. + */ + uint8_t flags; + /** + * The length field of this control frame. + */ + int32_t length; + } nghttp2_ctrl_hd; + + /** + * @struct + * + * The primary structure to hold the resources needed for a SPDY + * session. The details of this structure is hidden from the public + * API. + */ + typedef struct nghttp2_session nghttp2_session; + +@union +###### + +``@union`` is used to refer to the union. Currently, ``@union`` is an +alias of ``@struct``. diff --git a/doc/_themes/sphinx_rtd_theme/__init__.py b/doc/_themes/sphinx_rtd_theme/__init__.py new file mode 100644 index 0000000..95ddc52 --- /dev/null +++ b/doc/_themes/sphinx_rtd_theme/__init__.py @@ -0,0 +1,17 @@ +"""Sphinx ReadTheDocs theme. + +From https://github.com/ryan-roemer/sphinx-bootstrap-theme. + +""" +import os + +VERSION = (0, 1, 8) + +__version__ = ".".join(str(v) for v in VERSION) +__version_full__ = __version__ + + +def get_html_theme_path(): + """Return list of HTML theme paths.""" + cur_dir = os.path.abspath(os.path.dirname(os.path.dirname(__file__))) + return cur_dir diff --git a/doc/_themes/sphinx_rtd_theme/breadcrumbs.html b/doc/_themes/sphinx_rtd_theme/breadcrumbs.html new file mode 100644 index 0000000..0028421 --- /dev/null +++ b/doc/_themes/sphinx_rtd_theme/breadcrumbs.html @@ -0,0 +1,23 @@ +
      + +
      +
      diff --git a/doc/_themes/sphinx_rtd_theme/footer.html b/doc/_themes/sphinx_rtd_theme/footer.html new file mode 100644 index 0000000..6347a44 --- /dev/null +++ b/doc/_themes/sphinx_rtd_theme/footer.html @@ -0,0 +1,36 @@ +
      + {% if next or prev %} + + {% endif %} + +
      + +
      +

      + {%- if show_copyright %} + {%- if hasdoc('copyright') %} + {% trans path=pathto('copyright'), copyright=copyright|e %}© Copyright {{ copyright }}.{% endtrans %} + {%- else %} + {% trans copyright=copyright|e %}© Copyright {{ copyright }}.{% endtrans %} + {%- endif %} + {%- endif %} + + {%- if last_updated %} + {% trans last_updated=last_updated|e %}Last updated on {{ last_updated }}.{% endtrans %} + {%- endif %} +

      +
      + + {%- if show_sphinx %} + {% trans %}Built with Sphinx using a theme provided by Read the Docs{% endtrans %}. + {%- endif %} + +
      + diff --git a/doc/_themes/sphinx_rtd_theme/layout.html b/doc/_themes/sphinx_rtd_theme/layout.html new file mode 100644 index 0000000..9481d8b --- /dev/null +++ b/doc/_themes/sphinx_rtd_theme/layout.html @@ -0,0 +1,181 @@ +{# TEMPLATE VAR SETTINGS #} +{%- set url_root = pathto('', 1) %} +{%- if url_root == '#' %}{% set url_root = '' %}{% endif %} +{%- if not embedded and docstitle %} + {%- set titlesuffix = " — "|safe + docstitle|e %} +{%- else %} + {%- set titlesuffix = "" %} +{%- endif %} + + + + + + + {{ metatags }} + + {% block htmltitle %} + {{ title|striptags|e }}{{ titlesuffix }} + {% endblock %} + + {# FAVICON #} + {% if favicon %} + + {% endif %} + + {# CSS #} + + {# OPENSEARCH #} + {% if not embedded %} + {% if use_opensearch %} + + {% endif %} + + {% endif %} + + {# RTD hosts this file, so just load on non RTD builds #} + {% if not READTHEDOCS %} + + {% endif %} + + {% for cssfile in css_files %} + + {% endfor %} + + {% for cssfile in extra_css_files %} + + {% endfor %} + + {%- block linktags %} + {%- if hasdoc('about') %} + + {%- endif %} + {%- if hasdoc('genindex') %} + + {%- endif %} + {%- if hasdoc('search') %} + + {%- endif %} + {%- if hasdoc('copyright') %} + + {%- endif %} + + {%- if parents %} + + {%- endif %} + {%- if next %} + + {%- endif %} + {%- if prev %} + + {%- endif %} + {%- endblock %} + {%- block extrahead %} {% endblock %} + + {# Keep modernizr in head - http://modernizr.com/docs/#installing #} + + + + + + +
      + + {# SIDE NAV, TOGGLES ON MOBILE #} + + +
      + + {# MOBILE NAV, TRIGGLES SIDE NAV ON TOGGLE #} + + + + {# PAGE CONTENT #} +
      +
      + {% include "breadcrumbs.html" %} +
      + {% block body %}{% endblock %} +
      + {% include "footer.html" %} +
      +
      + +
      + +
      + {% include "versions.html" %} + + {% if not embedded %} + + + {%- for scriptfile in script_files %} + + {%- endfor %} + + {% endif %} + + {# RTD hosts this file, so just load on non RTD builds #} + {% if not READTHEDOCS %} + + {% endif %} + + {# STICKY NAVIGATION #} + {% if theme_sticky_navigation %} + + {% endif %} + + {%- block footer %} {% endblock %} + + + diff --git a/doc/_themes/sphinx_rtd_theme/layout_old.html b/doc/_themes/sphinx_rtd_theme/layout_old.html new file mode 100644 index 0000000..deb8df2 --- /dev/null +++ b/doc/_themes/sphinx_rtd_theme/layout_old.html @@ -0,0 +1,205 @@ +{# + basic/layout.html + ~~~~~~~~~~~~~~~~~ + + Master layout template for Sphinx themes. + + :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS. + :license: BSD, see LICENSE for details. +#} +{%- block doctype -%} + +{%- endblock %} +{%- set reldelim1 = reldelim1 is not defined and ' »' or reldelim1 %} +{%- set reldelim2 = reldelim2 is not defined and ' |' or reldelim2 %} +{%- set render_sidebar = (not embedded) and (not theme_nosidebar|tobool) and + (sidebars != []) %} +{%- set url_root = pathto('', 1) %} +{# XXX necessary? #} +{%- if url_root == '#' %}{% set url_root = '' %}{% endif %} +{%- if not embedded and docstitle %} + {%- set titlesuffix = " — "|safe + docstitle|e %} +{%- else %} + {%- set titlesuffix = "" %} +{%- endif %} + +{%- macro relbar() %} + +{%- endmacro %} + +{%- macro sidebar() %} + {%- if render_sidebar %} +
      +
      + {%- block sidebarlogo %} + {%- if logo %} + + {%- endif %} + {%- endblock %} + {%- if sidebars != None %} + {#- new style sidebar: explicitly include/exclude templates #} + {%- for sidebartemplate in sidebars %} + {%- include sidebartemplate %} + {%- endfor %} + {%- else %} + {#- old style sidebars: using blocks -- should be deprecated #} + {%- block sidebartoc %} + {%- include "localtoc.html" %} + {%- endblock %} + {%- block sidebarrel %} + {%- include "relations.html" %} + {%- endblock %} + {%- block sidebarsourcelink %} + {%- include "sourcelink.html" %} + {%- endblock %} + {%- if customsidebar %} + {%- include customsidebar %} + {%- endif %} + {%- block sidebarsearch %} + {%- include "searchbox.html" %} + {%- endblock %} + {%- endif %} +
      +
      + {%- endif %} +{%- endmacro %} + +{%- macro script() %} + + {%- for scriptfile in script_files %} + + {%- endfor %} +{%- endmacro %} + +{%- macro css() %} + + + {%- for cssfile in css_files %} + + {%- endfor %} +{%- endmacro %} + + + + + {{ metatags }} + {%- block htmltitle %} + {{ title|striptags|e }}{{ titlesuffix }} + {%- endblock %} + {{ css() }} + {%- if not embedded %} + {{ script() }} + {%- if use_opensearch %} + + {%- endif %} + {%- if favicon %} + + {%- endif %} + {%- endif %} +{%- block linktags %} + {%- if hasdoc('about') %} + + {%- endif %} + {%- if hasdoc('genindex') %} + + {%- endif %} + {%- if hasdoc('search') %} + + {%- endif %} + {%- if hasdoc('copyright') %} + + {%- endif %} + + {%- if parents %} + + {%- endif %} + {%- if next %} + + {%- endif %} + {%- if prev %} + + {%- endif %} +{%- endblock %} +{%- block extrahead %} {% endblock %} + + +{%- block header %}{% endblock %} + +{%- block relbar1 %}{{ relbar() }}{% endblock %} + +{%- block content %} + {%- block sidebar1 %} {# possible location for sidebar #} {% endblock %} + +
      + {%- block document %} +
      + {%- if render_sidebar %} +
      + {%- endif %} +
      + {% block body %} {% endblock %} +
      + {%- if render_sidebar %} +
      + {%- endif %} +
      + {%- endblock %} + + {%- block sidebar2 %}{{ sidebar() }}{% endblock %} +
      +
      +{%- endblock %} + +{%- block relbar2 %}{{ relbar() }}{% endblock %} + +{%- block footer %} + +

      asdf asdf asdf asdf 22

      +{%- endblock %} + + + diff --git a/doc/_themes/sphinx_rtd_theme/search.html b/doc/_themes/sphinx_rtd_theme/search.html new file mode 100644 index 0000000..e3aa9b5 --- /dev/null +++ b/doc/_themes/sphinx_rtd_theme/search.html @@ -0,0 +1,50 @@ +{# + basic/search.html + ~~~~~~~~~~~~~~~~~ + + Template for the search page. + + :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS. + :license: BSD, see LICENSE for details. +#} +{%- extends "layout.html" %} +{% set title = _('Search') %} +{% set script_files = script_files + ['_static/searchtools.js'] %} +{% block footer %} + + {# this is used when loading the search index using $.ajax fails, + such as on Chrome for documents on localhost #} + + {{ super() }} +{% endblock %} +{% block body %} + + + {% if search_performed %} +

      {{ _('Search Results') }}

      + {% if not search_results %} +

      {{ _('Your search did not match any documents. Please make sure that all words are spelled correctly and that you\'ve selected enough categories.') }}

      + {% endif %} + {% endif %} +
      + {% if search_results %} +
        + {% for href, caption, context in search_results %} +
      • + {{ caption }} +

        {{ context|e }}

        +
      • + {% endfor %} +
      + {% endif %} +
      +{% endblock %} diff --git a/doc/_themes/sphinx_rtd_theme/searchbox.html b/doc/_themes/sphinx_rtd_theme/searchbox.html new file mode 100644 index 0000000..35ad52c --- /dev/null +++ b/doc/_themes/sphinx_rtd_theme/searchbox.html @@ -0,0 +1,9 @@ +{%- if builder != 'singlehtml' %} +
      +
      + + + +
      +
      +{%- endif %} diff --git a/doc/_themes/sphinx_rtd_theme/static/css/badge_only.css b/doc/_themes/sphinx_rtd_theme/static/css/badge_only.css new file mode 100644 index 0000000..7e17fb1 --- /dev/null +++ b/doc/_themes/sphinx_rtd_theme/static/css/badge_only.css @@ -0,0 +1,2 @@ +.fa:before{-webkit-font-smoothing:antialiased}.clearfix{*zoom:1}.clearfix:before,.clearfix:after{display:table;content:""}.clearfix:after{clear:both}@font-face{font-family:FontAwesome;font-weight:normal;font-style:normal;src:url("../font/fontawesome_webfont.eot");src:url("../font/fontawesome_webfont.eot?#iefix") format("embedded-opentype"),url("../font/fontawesome_webfont.woff") format("woff"),url("../font/fontawesome_webfont.ttf") format("truetype"),url("../font/fontawesome_webfont.svg#FontAwesome") format("svg")}.fa:before{display:inline-block;font-family:FontAwesome;font-style:normal;font-weight:normal;line-height:1;text-decoration:inherit}a .fa{display:inline-block;text-decoration:inherit}li .fa{display:inline-block}li .fa-large:before,li .fa-large:before{width:1.875em}ul.fas{list-style-type:none;margin-left:2em;text-indent:-0.8em}ul.fas li .fa{width:0.8em}ul.fas li .fa-large:before,ul.fas li .fa-large:before{vertical-align:baseline}.fa-book:before{content:""}.icon-book:before{content:""}.fa-caret-down:before{content:""}.icon-caret-down:before{content:""}.fa-caret-up:before{content:""}.icon-caret-up:before{content:""}.fa-caret-left:before{content:""}.icon-caret-left:before{content:""}.fa-caret-right:before{content:""}.icon-caret-right:before{content:""}.rst-versions{position:fixed;bottom:0;left:0;width:300px;color:#fcfcfc;background:#1f1d1d;border-top:solid 10px #343131;font-family:"Lato","proxima-nova","Helvetica Neue",Arial,sans-serif;z-index:400}.rst-versions a{color:#2980B9;text-decoration:none}.rst-versions .rst-badge-small{display:none}.rst-versions .rst-current-version{padding:12px;background-color:#272525;display:block;text-align:right;font-size:90%;cursor:pointer;color:#27AE60;*zoom:1}.rst-versions .rst-current-version:before,.rst-versions .rst-current-version:after{display:table;content:""}.rst-versions .rst-current-version:after{clear:both}.rst-versions .rst-current-version .fa{color:#fcfcfc}.rst-versions .rst-current-version .fa-book{float:left}.rst-versions .rst-current-version .icon-book{float:left}.rst-versions .rst-current-version.rst-out-of-date{background-color:#E74C3C;color:#fff}.rst-versions .rst-current-version.rst-active-old-version{background-color:#F1C40F;color:#000}.rst-versions.shift-up .rst-other-versions{display:block}.rst-versions .rst-other-versions{font-size:90%;padding:12px;color:gray;display:none}.rst-versions .rst-other-versions hr{display:block;height:1px;border:0;margin:20px 0;padding:0;border-top:solid 1px #413d3d}.rst-versions .rst-other-versions dd{display:inline-block;margin:0}.rst-versions .rst-other-versions dd a{display:inline-block;padding:6px;color:#fcfcfc}.rst-versions.rst-badge{width:auto;bottom:20px;right:20px;left:auto;border:none;max-width:300px}.rst-versions.rst-badge .icon-book{float:none}.rst-versions.rst-badge .fa-book{float:none}.rst-versions.rst-badge.shift-up .rst-current-version{text-align:right}.rst-versions.rst-badge.shift-up .rst-current-version .fa-book{float:left}.rst-versions.rst-badge.shift-up .rst-current-version .icon-book{float:left}.rst-versions.rst-badge .rst-current-version{width:auto;height:30px;line-height:30px;padding:0 6px;display:block;text-align:center}@media screen and (max-width: 768px){.rst-versions{width:85%;display:none}.rst-versions.shift{display:block}img{width:100%;height:auto}} +/*# sourceMappingURL=badge_only.css.map */ diff --git a/doc/_themes/sphinx_rtd_theme/static/css/theme.css b/doc/_themes/sphinx_rtd_theme/static/css/theme.css new file mode 100644 index 0000000..57b98fe --- /dev/null +++ b/doc/_themes/sphinx_rtd_theme/static/css/theme.css @@ -0,0 +1,5 @@ +*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}article,aside,details,figcaption,figure,footer,header,hgroup,nav,section{display:block}audio,canvas,video{display:inline-block;*display:inline;*zoom:1}audio:not([controls]){display:none}[hidden]{display:none}*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}html{font-size:100%;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%}body{margin:0}a:hover,a:active{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:bold}blockquote{margin:0}dfn{font-style:italic}ins{background:#ff9;color:#000;text-decoration:none}mark{background:#ff0;color:#000;font-style:italic;font-weight:bold}pre,code,.rst-content tt,.rst-content code,kbd,samp{font-family:monospace,serif;_font-family:"courier new",monospace;font-size:1em}pre{white-space:pre}q{quotes:none}q:before,q:after{content:"";content:none}small{font-size:85%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-0.5em}sub{bottom:-0.25em}ul,ol,dl{margin:0;padding:0;list-style:none;list-style-image:none}li{list-style:none}dd{margin:0}img{border:0;-ms-interpolation-mode:bicubic;vertical-align:middle;max-width:100%}svg:not(:root){overflow:hidden}figure{margin:0}form{margin:0}fieldset{border:0;margin:0;padding:0}label{cursor:pointer}legend{border:0;*margin-left:-7px;padding:0;white-space:normal}button,input,select,textarea{font-size:100%;margin:0;vertical-align:baseline;*vertical-align:middle}button,input{line-height:normal}button,input[type="button"],input[type="reset"],input[type="submit"]{cursor:pointer;-webkit-appearance:button;*overflow:visible}button[disabled],input[disabled]{cursor:default}input[type="checkbox"],input[type="radio"]{box-sizing:border-box;padding:0;*width:13px;*height:13px}input[type="search"]{-webkit-appearance:textfield;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box}input[type="search"]::-webkit-search-decoration,input[type="search"]::-webkit-search-cancel-button{-webkit-appearance:none}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}textarea{overflow:auto;vertical-align:top;resize:vertical}table{border-collapse:collapse;border-spacing:0}td{vertical-align:top}.chromeframe{margin:0.2em 0;background:#ccc;color:#000;padding:0.2em 0}.ir{display:block;border:0;text-indent:-999em;overflow:hidden;background-color:transparent;background-repeat:no-repeat;text-align:left;direction:ltr;*line-height:0}.ir br{display:none}.hidden{display:none !important;visibility:hidden}.visuallyhidden{border:0;clip:rect(0 0 0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.visuallyhidden.focusable:active,.visuallyhidden.focusable:focus{clip:auto;height:auto;margin:0;overflow:visible;position:static;width:auto}.invisible{visibility:hidden}.relative{position:relative}big,small{font-size:100%}@media print{html,body,section{background:none !important}*{box-shadow:none !important;text-shadow:none !important;filter:none !important;-ms-filter:none !important}a,a:visited{text-decoration:underline}.ir a:after,a[href^="javascript:"]:after,a[href^="#"]:after{content:""}pre,blockquote{page-break-inside:avoid}thead{display:table-header-group}tr,img{page-break-inside:avoid}img{max-width:100% !important}@page{margin:0.5cm}p,h2,.rst-content p.caption,h3{orphans:3;widows:3}h2,.rst-content p.caption,h3{page-break-after:avoid}}.fa:before,.wy-menu-vertical li span.toctree-expand:before,.wy-menu-vertical li.on a span.toctree-expand:before,.wy-menu-vertical li.current>a span.toctree-expand:before,.rst-content .admonition-title:before,.rst-content h1 .headerlink:before,.rst-content h2 .headerlink:before,.rst-content p.caption .headerlink:before,.rst-content h3 .headerlink:before,.rst-content h4 .headerlink:before,.rst-content h5 .headerlink:before,.rst-content h6 .headerlink:before,.rst-content dl dt .headerlink:before,.rst-content tt.download span:first-child:before,.rst-content code.download span:first-child:before,.icon:before,.wy-dropdown .caret:before,.wy-inline-validate.wy-inline-validate-success .wy-input-context:before,.wy-inline-validate.wy-inline-validate-danger .wy-input-context:before,.wy-inline-validate.wy-inline-validate-warning .wy-input-context:before,.wy-inline-validate.wy-inline-validate-info .wy-input-context:before,.wy-alert,.rst-content .note,.rst-content .attention,.rst-content .caution,.rst-content .danger,.rst-content .error,.rst-content .hint,.rst-content .important,.rst-content .tip,.rst-content .warning,.rst-content .seealso,.rst-content .admonition-todo,.btn,input[type="text"],input[type="password"],input[type="email"],input[type="url"],input[type="date"],input[type="month"],input[type="time"],input[type="datetime"],input[type="datetime-local"],input[type="week"],input[type="number"],input[type="search"],input[type="tel"],input[type="color"],select,textarea,.wy-menu-vertical li.on a,.wy-menu-vertical li.current>a,.wy-side-nav-search>a,.wy-side-nav-search .wy-dropdown>a,.wy-nav-top a{-webkit-font-smoothing:antialiased}.clearfix{*zoom:1}.clearfix:before,.clearfix:after{display:table;content:""}.clearfix:after{clear:both}/*! + * Font Awesome 4.2.0 by @davegandy - http://fontawesome.io - @fontawesome + * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) + */@font-face{font-family:'FontAwesome';src:url("../fonts/fontawesome-webfont.eot?v=4.2.0");src:url("../fonts/fontawesome-webfont.eot?#iefix&v=4.2.0") format("embedded-opentype"),url("../fonts/fontawesome-webfont.woff?v=4.2.0") format("woff"),url("../fonts/fontawesome-webfont.ttf?v=4.2.0") format("truetype"),url("../fonts/fontawesome-webfont.svg?v=4.2.0#fontawesomeregular") format("svg");font-weight:normal;font-style:normal}.fa,.wy-menu-vertical li span.toctree-expand,.wy-menu-vertical li.on a span.toctree-expand,.wy-menu-vertical li.current>a span.toctree-expand,.rst-content .admonition-title,.rst-content h1 .headerlink,.rst-content h2 .headerlink,.rst-content p.caption .headerlink,.rst-content h3 .headerlink,.rst-content h4 .headerlink,.rst-content h5 .headerlink,.rst-content h6 .headerlink,.rst-content dl dt .headerlink,.rst-content tt.download span:first-child,.rst-content code.download span:first-child,.icon{display:inline-block;font:normal normal normal 14px/1 FontAwesome;font-size:inherit;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.fa-lg{font-size:1.33333em;line-height:0.75em;vertical-align:-15%}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-fw{width:1.28571em;text-align:center}.fa-ul{padding-left:0;margin-left:2.14286em;list-style-type:none}.fa-ul>li{position:relative}.fa-li{position:absolute;left:-2.14286em;width:2.14286em;top:0.14286em;text-align:center}.fa-li.fa-lg{left:-1.85714em}.fa-border{padding:.2em .25em .15em;border:solid 0.08em #eee;border-radius:.1em}.pull-right{float:right}.pull-left{float:left}.fa.pull-left,.wy-menu-vertical li span.pull-left.toctree-expand,.wy-menu-vertical li.on a span.pull-left.toctree-expand,.wy-menu-vertical li.current>a span.pull-left.toctree-expand,.rst-content .pull-left.admonition-title,.rst-content h1 .pull-left.headerlink,.rst-content h2 .pull-left.headerlink,.rst-content p.caption .pull-left.headerlink,.rst-content h3 .pull-left.headerlink,.rst-content h4 .pull-left.headerlink,.rst-content h5 .pull-left.headerlink,.rst-content h6 .pull-left.headerlink,.rst-content dl dt .pull-left.headerlink,.rst-content tt.download span.pull-left:first-child,.rst-content code.download span.pull-left:first-child,.pull-left.icon{margin-right:.3em}.fa.pull-right,.wy-menu-vertical li span.pull-right.toctree-expand,.wy-menu-vertical li.on a span.pull-right.toctree-expand,.wy-menu-vertical li.current>a span.pull-right.toctree-expand,.rst-content .pull-right.admonition-title,.rst-content h1 .pull-right.headerlink,.rst-content h2 .pull-right.headerlink,.rst-content p.caption .pull-right.headerlink,.rst-content h3 .pull-right.headerlink,.rst-content h4 .pull-right.headerlink,.rst-content h5 .pull-right.headerlink,.rst-content h6 .pull-right.headerlink,.rst-content dl dt .pull-right.headerlink,.rst-content tt.download span.pull-right:first-child,.rst-content code.download span.pull-right:first-child,.pull-right.icon{margin-left:.3em}.fa-spin{-webkit-animation:fa-spin 2s infinite linear;animation:fa-spin 2s infinite linear}@-webkit-keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.fa-rotate-90{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=1);-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=2);-webkit-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=3);-webkit-transform:rotate(270deg);-ms-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=0);-webkit-transform:scale(-1, 1);-ms-transform:scale(-1, 1);transform:scale(-1, 1)}.fa-flip-vertical{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=2);-webkit-transform:scale(1, -1);-ms-transform:scale(1, -1);transform:scale(1, -1)}:root .fa-rotate-90,:root .fa-rotate-180,:root .fa-rotate-270,:root .fa-flip-horizontal,:root .fa-flip-vertical{filter:none}.fa-stack{position:relative;display:inline-block;width:2em;height:2em;line-height:2em;vertical-align:middle}.fa-stack-1x,.fa-stack-2x{position:absolute;left:0;width:100%;text-align:center}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-glass:before{content:""}.fa-music:before{content:""}.fa-search:before,.icon-search:before{content:""}.fa-envelope-o:before{content:""}.fa-heart:before{content:""}.fa-star:before{content:""}.fa-star-o:before{content:""}.fa-user:before{content:""}.fa-film:before{content:""}.fa-th-large:before{content:""}.fa-th:before{content:""}.fa-th-list:before{content:""}.fa-check:before{content:""}.fa-remove:before,.fa-close:before,.fa-times:before{content:""}.fa-search-plus:before{content:""}.fa-search-minus:before{content:""}.fa-power-off:before{content:""}.fa-signal:before{content:""}.fa-gear:before,.fa-cog:before{content:""}.fa-trash-o:before{content:""}.fa-home:before,.icon-home:before{content:""}.fa-file-o:before{content:""}.fa-clock-o:before{content:""}.fa-road:before{content:""}.fa-download:before,.rst-content tt.download span:first-child:before,.rst-content code.download span:first-child:before{content:""}.fa-arrow-circle-o-down:before{content:""}.fa-arrow-circle-o-up:before{content:""}.fa-inbox:before{content:""}.fa-play-circle-o:before{content:""}.fa-rotate-right:before,.fa-repeat:before{content:""}.fa-refresh:before{content:""}.fa-list-alt:before{content:""}.fa-lock:before{content:""}.fa-flag:before{content:""}.fa-headphones:before{content:""}.fa-volume-off:before{content:""}.fa-volume-down:before{content:""}.fa-volume-up:before{content:""}.fa-qrcode:before{content:""}.fa-barcode:before{content:""}.fa-tag:before{content:""}.fa-tags:before{content:""}.fa-book:before,.icon-book:before{content:""}.fa-bookmark:before{content:""}.fa-print:before{content:""}.fa-camera:before{content:""}.fa-font:before{content:""}.fa-bold:before{content:""}.fa-italic:before{content:""}.fa-text-height:before{content:""}.fa-text-width:before{content:""}.fa-align-left:before{content:""}.fa-align-center:before{content:""}.fa-align-right:before{content:""}.fa-align-justify:before{content:""}.fa-list:before{content:""}.fa-dedent:before,.fa-outdent:before{content:""}.fa-indent:before{content:""}.fa-video-camera:before{content:""}.fa-photo:before,.fa-image:before,.fa-picture-o:before{content:""}.fa-pencil:before{content:""}.fa-map-marker:before{content:""}.fa-adjust:before{content:""}.fa-tint:before{content:""}.fa-edit:before,.fa-pencil-square-o:before{content:""}.fa-share-square-o:before{content:""}.fa-check-square-o:before{content:""}.fa-arrows:before{content:""}.fa-step-backward:before{content:""}.fa-fast-backward:before{content:""}.fa-backward:before{content:""}.fa-play:before{content:""}.fa-pause:before{content:""}.fa-stop:before{content:""}.fa-forward:before{content:""}.fa-fast-forward:before{content:""}.fa-step-forward:before{content:""}.fa-eject:before{content:""}.fa-chevron-left:before{content:""}.fa-chevron-right:before{content:""}.fa-plus-circle:before{content:""}.fa-minus-circle:before{content:""}.fa-times-circle:before,.wy-inline-validate.wy-inline-validate-danger .wy-input-context:before{content:""}.fa-check-circle:before,.wy-inline-validate.wy-inline-validate-success .wy-input-context:before{content:""}.fa-question-circle:before{content:""}.fa-info-circle:before{content:""}.fa-crosshairs:before{content:""}.fa-times-circle-o:before{content:""}.fa-check-circle-o:before{content:""}.fa-ban:before{content:""}.fa-arrow-left:before{content:""}.fa-arrow-right:before{content:""}.fa-arrow-up:before{content:""}.fa-arrow-down:before{content:""}.fa-mail-forward:before,.fa-share:before{content:""}.fa-expand:before{content:""}.fa-compress:before{content:""}.fa-plus:before{content:""}.fa-minus:before{content:""}.fa-asterisk:before{content:""}.fa-exclamation-circle:before,.wy-inline-validate.wy-inline-validate-warning .wy-input-context:before,.wy-inline-validate.wy-inline-validate-info .wy-input-context:before,.rst-content .admonition-title:before{content:""}.fa-gift:before{content:""}.fa-leaf:before{content:""}.fa-fire:before,.icon-fire:before{content:""}.fa-eye:before{content:""}.fa-eye-slash:before{content:""}.fa-warning:before,.fa-exclamation-triangle:before{content:""}.fa-plane:before{content:""}.fa-calendar:before{content:""}.fa-random:before{content:""}.fa-comment:before{content:""}.fa-magnet:before{content:""}.fa-chevron-up:before{content:""}.fa-chevron-down:before{content:""}.fa-retweet:before{content:""}.fa-shopping-cart:before{content:""}.fa-folder:before{content:""}.fa-folder-open:before{content:""}.fa-arrows-v:before{content:""}.fa-arrows-h:before{content:""}.fa-bar-chart-o:before,.fa-bar-chart:before{content:""}.fa-twitter-square:before{content:""}.fa-facebook-square:before{content:""}.fa-camera-retro:before{content:""}.fa-key:before{content:""}.fa-gears:before,.fa-cogs:before{content:""}.fa-comments:before{content:""}.fa-thumbs-o-up:before{content:""}.fa-thumbs-o-down:before{content:""}.fa-star-half:before{content:""}.fa-heart-o:before{content:""}.fa-sign-out:before{content:""}.fa-linkedin-square:before{content:""}.fa-thumb-tack:before{content:""}.fa-external-link:before{content:""}.fa-sign-in:before{content:""}.fa-trophy:before{content:""}.fa-github-square:before{content:""}.fa-upload:before{content:""}.fa-lemon-o:before{content:""}.fa-phone:before{content:""}.fa-square-o:before{content:""}.fa-bookmark-o:before{content:""}.fa-phone-square:before{content:""}.fa-twitter:before{content:""}.fa-facebook:before{content:""}.fa-github:before,.icon-github:before{content:""}.fa-unlock:before{content:""}.fa-credit-card:before{content:""}.fa-rss:before{content:""}.fa-hdd-o:before{content:""}.fa-bullhorn:before{content:""}.fa-bell:before{content:""}.fa-certificate:before{content:""}.fa-hand-o-right:before{content:""}.fa-hand-o-left:before{content:""}.fa-hand-o-up:before{content:""}.fa-hand-o-down:before{content:""}.fa-arrow-circle-left:before,.icon-circle-arrow-left:before{content:""}.fa-arrow-circle-right:before,.icon-circle-arrow-right:before{content:""}.fa-arrow-circle-up:before{content:""}.fa-arrow-circle-down:before{content:""}.fa-globe:before{content:""}.fa-wrench:before{content:""}.fa-tasks:before{content:""}.fa-filter:before{content:""}.fa-briefcase:before{content:""}.fa-arrows-alt:before{content:""}.fa-group:before,.fa-users:before{content:""}.fa-chain:before,.fa-link:before,.icon-link:before{content:""}.fa-cloud:before{content:""}.fa-flask:before{content:""}.fa-cut:before,.fa-scissors:before{content:""}.fa-copy:before,.fa-files-o:before{content:""}.fa-paperclip:before{content:""}.fa-save:before,.fa-floppy-o:before{content:""}.fa-square:before{content:""}.fa-navicon:before,.fa-reorder:before,.fa-bars:before{content:""}.fa-list-ul:before{content:""}.fa-list-ol:before{content:""}.fa-strikethrough:before{content:""}.fa-underline:before{content:""}.fa-table:before{content:""}.fa-magic:before{content:""}.fa-truck:before{content:""}.fa-pinterest:before{content:""}.fa-pinterest-square:before{content:""}.fa-google-plus-square:before{content:""}.fa-google-plus:before{content:""}.fa-money:before{content:""}.fa-caret-down:before,.wy-dropdown .caret:before,.icon-caret-down:before{content:""}.fa-caret-up:before{content:""}.fa-caret-left:before{content:""}.fa-caret-right:before{content:""}.fa-columns:before{content:""}.fa-unsorted:before,.fa-sort:before{content:""}.fa-sort-down:before,.fa-sort-desc:before{content:""}.fa-sort-up:before,.fa-sort-asc:before{content:""}.fa-envelope:before{content:""}.fa-linkedin:before{content:""}.fa-rotate-left:before,.fa-undo:before{content:""}.fa-legal:before,.fa-gavel:before{content:""}.fa-dashboard:before,.fa-tachometer:before{content:""}.fa-comment-o:before{content:""}.fa-comments-o:before{content:""}.fa-flash:before,.fa-bolt:before{content:""}.fa-sitemap:before{content:""}.fa-umbrella:before{content:""}.fa-paste:before,.fa-clipboard:before{content:""}.fa-lightbulb-o:before{content:""}.fa-exchange:before{content:""}.fa-cloud-download:before{content:""}.fa-cloud-upload:before{content:""}.fa-user-md:before{content:""}.fa-stethoscope:before{content:""}.fa-suitcase:before{content:""}.fa-bell-o:before{content:""}.fa-coffee:before{content:""}.fa-cutlery:before{content:""}.fa-file-text-o:before{content:""}.fa-building-o:before{content:""}.fa-hospital-o:before{content:""}.fa-ambulance:before{content:""}.fa-medkit:before{content:""}.fa-fighter-jet:before{content:""}.fa-beer:before{content:""}.fa-h-square:before{content:""}.fa-plus-square:before{content:""}.fa-angle-double-left:before{content:""}.fa-angle-double-right:before{content:""}.fa-angle-double-up:before{content:""}.fa-angle-double-down:before{content:""}.fa-angle-left:before{content:""}.fa-angle-right:before{content:""}.fa-angle-up:before{content:""}.fa-angle-down:before{content:""}.fa-desktop:before{content:""}.fa-laptop:before{content:""}.fa-tablet:before{content:""}.fa-mobile-phone:before,.fa-mobile:before{content:""}.fa-circle-o:before{content:""}.fa-quote-left:before{content:""}.fa-quote-right:before{content:""}.fa-spinner:before{content:""}.fa-circle:before{content:""}.fa-mail-reply:before,.fa-reply:before{content:""}.fa-github-alt:before{content:""}.fa-folder-o:before{content:""}.fa-folder-open-o:before{content:""}.fa-smile-o:before{content:""}.fa-frown-o:before{content:""}.fa-meh-o:before{content:""}.fa-gamepad:before{content:""}.fa-keyboard-o:before{content:""}.fa-flag-o:before{content:""}.fa-flag-checkered:before{content:""}.fa-terminal:before{content:""}.fa-code:before{content:""}.fa-mail-reply-all:before,.fa-reply-all:before{content:""}.fa-star-half-empty:before,.fa-star-half-full:before,.fa-star-half-o:before{content:""}.fa-location-arrow:before{content:""}.fa-crop:before{content:""}.fa-code-fork:before{content:""}.fa-unlink:before,.fa-chain-broken:before{content:""}.fa-question:before{content:""}.fa-info:before{content:""}.fa-exclamation:before{content:""}.fa-superscript:before{content:""}.fa-subscript:before{content:""}.fa-eraser:before{content:""}.fa-puzzle-piece:before{content:""}.fa-microphone:before{content:""}.fa-microphone-slash:before{content:""}.fa-shield:before{content:""}.fa-calendar-o:before{content:""}.fa-fire-extinguisher:before{content:""}.fa-rocket:before{content:""}.fa-maxcdn:before{content:""}.fa-chevron-circle-left:before{content:""}.fa-chevron-circle-right:before{content:""}.fa-chevron-circle-up:before{content:""}.fa-chevron-circle-down:before{content:""}.fa-html5:before{content:""}.fa-css3:before{content:""}.fa-anchor:before{content:""}.fa-unlock-alt:before{content:""}.fa-bullseye:before{content:""}.fa-ellipsis-h:before{content:""}.fa-ellipsis-v:before{content:""}.fa-rss-square:before{content:""}.fa-play-circle:before{content:""}.fa-ticket:before{content:""}.fa-minus-square:before{content:""}.fa-minus-square-o:before,.wy-menu-vertical li.on a span.toctree-expand:before,.wy-menu-vertical li.current>a span.toctree-expand:before{content:""}.fa-level-up:before{content:""}.fa-level-down:before{content:""}.fa-check-square:before{content:""}.fa-pencil-square:before{content:""}.fa-external-link-square:before{content:""}.fa-share-square:before{content:""}.fa-compass:before{content:""}.fa-toggle-down:before,.fa-caret-square-o-down:before{content:""}.fa-toggle-up:before,.fa-caret-square-o-up:before{content:""}.fa-toggle-right:before,.fa-caret-square-o-right:before{content:""}.fa-euro:before,.fa-eur:before{content:""}.fa-gbp:before{content:""}.fa-dollar:before,.fa-usd:before{content:""}.fa-rupee:before,.fa-inr:before{content:""}.fa-cny:before,.fa-rmb:before,.fa-yen:before,.fa-jpy:before{content:""}.fa-ruble:before,.fa-rouble:before,.fa-rub:before{content:""}.fa-won:before,.fa-krw:before{content:""}.fa-bitcoin:before,.fa-btc:before{content:""}.fa-file:before{content:""}.fa-file-text:before{content:""}.fa-sort-alpha-asc:before{content:""}.fa-sort-alpha-desc:before{content:""}.fa-sort-amount-asc:before{content:""}.fa-sort-amount-desc:before{content:""}.fa-sort-numeric-asc:before{content:""}.fa-sort-numeric-desc:before{content:""}.fa-thumbs-up:before{content:""}.fa-thumbs-down:before{content:""}.fa-youtube-square:before{content:""}.fa-youtube:before{content:""}.fa-xing:before{content:""}.fa-xing-square:before{content:""}.fa-youtube-play:before{content:""}.fa-dropbox:before{content:""}.fa-stack-overflow:before{content:""}.fa-instagram:before{content:""}.fa-flickr:before{content:""}.fa-adn:before{content:""}.fa-bitbucket:before,.icon-bitbucket:before{content:""}.fa-bitbucket-square:before{content:""}.fa-tumblr:before{content:""}.fa-tumblr-square:before{content:""}.fa-long-arrow-down:before{content:""}.fa-long-arrow-up:before{content:""}.fa-long-arrow-left:before{content:""}.fa-long-arrow-right:before{content:""}.fa-apple:before{content:""}.fa-windows:before{content:""}.fa-android:before{content:""}.fa-linux:before{content:""}.fa-dribbble:before{content:""}.fa-skype:before{content:""}.fa-foursquare:before{content:""}.fa-trello:before{content:""}.fa-female:before{content:""}.fa-male:before{content:""}.fa-gittip:before{content:""}.fa-sun-o:before{content:""}.fa-moon-o:before{content:""}.fa-archive:before{content:""}.fa-bug:before{content:""}.fa-vk:before{content:""}.fa-weibo:before{content:""}.fa-renren:before{content:""}.fa-pagelines:before{content:""}.fa-stack-exchange:before{content:""}.fa-arrow-circle-o-right:before{content:""}.fa-arrow-circle-o-left:before{content:""}.fa-toggle-left:before,.fa-caret-square-o-left:before{content:""}.fa-dot-circle-o:before{content:""}.fa-wheelchair:before{content:""}.fa-vimeo-square:before{content:""}.fa-turkish-lira:before,.fa-try:before{content:""}.fa-plus-square-o:before,.wy-menu-vertical li span.toctree-expand:before{content:""}.fa-space-shuttle:before{content:""}.fa-slack:before{content:""}.fa-envelope-square:before{content:""}.fa-wordpress:before{content:""}.fa-openid:before{content:""}.fa-institution:before,.fa-bank:before,.fa-university:before{content:""}.fa-mortar-board:before,.fa-graduation-cap:before{content:""}.fa-yahoo:before{content:""}.fa-google:before{content:""}.fa-reddit:before{content:""}.fa-reddit-square:before{content:""}.fa-stumbleupon-circle:before{content:""}.fa-stumbleupon:before{content:""}.fa-delicious:before{content:""}.fa-digg:before{content:""}.fa-pied-piper:before{content:""}.fa-pied-piper-alt:before{content:""}.fa-drupal:before{content:""}.fa-joomla:before{content:""}.fa-language:before{content:""}.fa-fax:before{content:""}.fa-building:before{content:""}.fa-child:before{content:""}.fa-paw:before{content:""}.fa-spoon:before{content:""}.fa-cube:before{content:""}.fa-cubes:before{content:""}.fa-behance:before{content:""}.fa-behance-square:before{content:""}.fa-steam:before{content:""}.fa-steam-square:before{content:""}.fa-recycle:before{content:""}.fa-automobile:before,.fa-car:before{content:""}.fa-cab:before,.fa-taxi:before{content:""}.fa-tree:before{content:""}.fa-spotify:before{content:""}.fa-deviantart:before{content:""}.fa-soundcloud:before{content:""}.fa-database:before{content:""}.fa-file-pdf-o:before{content:""}.fa-file-word-o:before{content:""}.fa-file-excel-o:before{content:""}.fa-file-powerpoint-o:before{content:""}.fa-file-photo-o:before,.fa-file-picture-o:before,.fa-file-image-o:before{content:""}.fa-file-zip-o:before,.fa-file-archive-o:before{content:""}.fa-file-sound-o:before,.fa-file-audio-o:before{content:""}.fa-file-movie-o:before,.fa-file-video-o:before{content:""}.fa-file-code-o:before{content:""}.fa-vine:before{content:""}.fa-codepen:before{content:""}.fa-jsfiddle:before{content:""}.fa-life-bouy:before,.fa-life-buoy:before,.fa-life-saver:before,.fa-support:before,.fa-life-ring:before{content:""}.fa-circle-o-notch:before{content:""}.fa-ra:before,.fa-rebel:before{content:""}.fa-ge:before,.fa-empire:before{content:""}.fa-git-square:before{content:""}.fa-git:before{content:""}.fa-hacker-news:before{content:""}.fa-tencent-weibo:before{content:""}.fa-qq:before{content:""}.fa-wechat:before,.fa-weixin:before{content:""}.fa-send:before,.fa-paper-plane:before{content:""}.fa-send-o:before,.fa-paper-plane-o:before{content:""}.fa-history:before{content:""}.fa-circle-thin:before{content:""}.fa-header:before{content:""}.fa-paragraph:before{content:""}.fa-sliders:before{content:""}.fa-share-alt:before{content:""}.fa-share-alt-square:before{content:""}.fa-bomb:before{content:""}.fa-soccer-ball-o:before,.fa-futbol-o:before{content:""}.fa-tty:before{content:""}.fa-binoculars:before{content:""}.fa-plug:before{content:""}.fa-slideshare:before{content:""}.fa-twitch:before{content:""}.fa-yelp:before{content:""}.fa-newspaper-o:before{content:""}.fa-wifi:before{content:""}.fa-calculator:before{content:""}.fa-paypal:before{content:""}.fa-google-wallet:before{content:""}.fa-cc-visa:before{content:""}.fa-cc-mastercard:before{content:""}.fa-cc-discover:before{content:""}.fa-cc-amex:before{content:""}.fa-cc-paypal:before{content:""}.fa-cc-stripe:before{content:""}.fa-bell-slash:before{content:""}.fa-bell-slash-o:before{content:""}.fa-trash:before{content:""}.fa-copyright:before{content:""}.fa-at:before{content:""}.fa-eyedropper:before{content:""}.fa-paint-brush:before{content:""}.fa-birthday-cake:before{content:""}.fa-area-chart:before{content:""}.fa-pie-chart:before{content:""}.fa-line-chart:before{content:""}.fa-lastfm:before{content:""}.fa-lastfm-square:before{content:""}.fa-toggle-off:before{content:""}.fa-toggle-on:before{content:""}.fa-bicycle:before{content:""}.fa-bus:before{content:""}.fa-ioxhost:before{content:""}.fa-angellist:before{content:""}.fa-cc:before{content:""}.fa-shekel:before,.fa-sheqel:before,.fa-ils:before{content:""}.fa-meanpath:before{content:""}.fa,.wy-menu-vertical li span.toctree-expand,.wy-menu-vertical li.on a span.toctree-expand,.wy-menu-vertical li.current>a span.toctree-expand,.rst-content .admonition-title,.rst-content h1 .headerlink,.rst-content h2 .headerlink,.rst-content p.caption .headerlink,.rst-content h3 .headerlink,.rst-content h4 .headerlink,.rst-content h5 .headerlink,.rst-content h6 .headerlink,.rst-content dl dt .headerlink,.rst-content tt.download span:first-child,.rst-content code.download span:first-child,.icon,.wy-dropdown .caret,.wy-inline-validate.wy-inline-validate-success .wy-input-context,.wy-inline-validate.wy-inline-validate-danger .wy-input-context,.wy-inline-validate.wy-inline-validate-warning .wy-input-context,.wy-inline-validate.wy-inline-validate-info .wy-input-context{font-family:inherit}.fa:before,.wy-menu-vertical li span.toctree-expand:before,.wy-menu-vertical li.on a span.toctree-expand:before,.wy-menu-vertical li.current>a span.toctree-expand:before,.rst-content .admonition-title:before,.rst-content h1 .headerlink:before,.rst-content h2 .headerlink:before,.rst-content p.caption .headerlink:before,.rst-content h3 .headerlink:before,.rst-content h4 .headerlink:before,.rst-content h5 .headerlink:before,.rst-content h6 .headerlink:before,.rst-content dl dt .headerlink:before,.rst-content tt.download span:first-child:before,.rst-content code.download span:first-child:before,.icon:before,.wy-dropdown .caret:before,.wy-inline-validate.wy-inline-validate-success .wy-input-context:before,.wy-inline-validate.wy-inline-validate-danger .wy-input-context:before,.wy-inline-validate.wy-inline-validate-warning .wy-input-context:before,.wy-inline-validate.wy-inline-validate-info .wy-input-context:before{font-family:"FontAwesome";display:inline-block;font-style:normal;font-weight:normal;line-height:1;text-decoration:inherit}a .fa,a .wy-menu-vertical li span.toctree-expand,.wy-menu-vertical li a span.toctree-expand,.wy-menu-vertical li.on a span.toctree-expand,.wy-menu-vertical li.current>a span.toctree-expand,a .rst-content .admonition-title,.rst-content a .admonition-title,a .rst-content h1 .headerlink,.rst-content h1 a .headerlink,a .rst-content h2 .headerlink,.rst-content h2 a .headerlink,a .rst-content p.caption .headerlink,.rst-content p.caption a .headerlink,a .rst-content h3 .headerlink,.rst-content h3 a .headerlink,a .rst-content h4 .headerlink,.rst-content h4 a .headerlink,a .rst-content h5 .headerlink,.rst-content h5 a .headerlink,a .rst-content h6 .headerlink,.rst-content h6 a .headerlink,a .rst-content dl dt .headerlink,.rst-content dl dt a .headerlink,a .rst-content tt.download span:first-child,.rst-content tt.download a span:first-child,a .rst-content code.download span:first-child,.rst-content code.download a span:first-child,a .icon{display:inline-block;text-decoration:inherit}.btn .fa,.btn .wy-menu-vertical li span.toctree-expand,.wy-menu-vertical li .btn span.toctree-expand,.btn .wy-menu-vertical li.on a span.toctree-expand,.wy-menu-vertical li.on a .btn span.toctree-expand,.btn .wy-menu-vertical li.current>a span.toctree-expand,.wy-menu-vertical li.current>a .btn span.toctree-expand,.btn .rst-content .admonition-title,.rst-content .btn .admonition-title,.btn .rst-content h1 .headerlink,.rst-content h1 .btn .headerlink,.btn .rst-content h2 .headerlink,.rst-content h2 .btn .headerlink,.btn .rst-content p.caption .headerlink,.rst-content p.caption .btn .headerlink,.btn .rst-content h3 .headerlink,.rst-content h3 .btn .headerlink,.btn .rst-content h4 .headerlink,.rst-content h4 .btn .headerlink,.btn .rst-content h5 .headerlink,.rst-content h5 .btn .headerlink,.btn .rst-content h6 .headerlink,.rst-content h6 .btn .headerlink,.btn .rst-content dl dt .headerlink,.rst-content dl dt .btn .headerlink,.btn .rst-content tt.download span:first-child,.rst-content tt.download .btn span:first-child,.btn .rst-content code.download span:first-child,.rst-content code.download .btn span:first-child,.btn .icon,.nav .fa,.nav .wy-menu-vertical li span.toctree-expand,.wy-menu-vertical li .nav span.toctree-expand,.nav .wy-menu-vertical li.on a span.toctree-expand,.wy-menu-vertical li.on a .nav span.toctree-expand,.nav .wy-menu-vertical li.current>a span.toctree-expand,.wy-menu-vertical li.current>a .nav span.toctree-expand,.nav .rst-content .admonition-title,.rst-content .nav .admonition-title,.nav .rst-content h1 .headerlink,.rst-content h1 .nav .headerlink,.nav .rst-content h2 .headerlink,.rst-content h2 .nav .headerlink,.nav .rst-content p.caption .headerlink,.rst-content p.caption .nav .headerlink,.nav .rst-content h3 .headerlink,.rst-content h3 .nav .headerlink,.nav .rst-content h4 .headerlink,.rst-content h4 .nav .headerlink,.nav .rst-content h5 .headerlink,.rst-content h5 .nav .headerlink,.nav .rst-content h6 .headerlink,.rst-content h6 .nav .headerlink,.nav .rst-content dl dt .headerlink,.rst-content dl dt .nav .headerlink,.nav .rst-content tt.download span:first-child,.rst-content tt.download .nav span:first-child,.nav .rst-content code.download span:first-child,.rst-content code.download .nav span:first-child,.nav .icon{display:inline}.btn .fa.fa-large,.btn .wy-menu-vertical li span.fa-large.toctree-expand,.wy-menu-vertical li .btn span.fa-large.toctree-expand,.btn .rst-content .fa-large.admonition-title,.rst-content .btn .fa-large.admonition-title,.btn .rst-content h1 .fa-large.headerlink,.rst-content h1 .btn .fa-large.headerlink,.btn .rst-content h2 .fa-large.headerlink,.rst-content h2 .btn .fa-large.headerlink,.btn .rst-content p.caption .fa-large.headerlink,.rst-content p.caption .btn .fa-large.headerlink,.btn .rst-content h3 .fa-large.headerlink,.rst-content h3 .btn .fa-large.headerlink,.btn .rst-content h4 .fa-large.headerlink,.rst-content h4 .btn .fa-large.headerlink,.btn .rst-content h5 .fa-large.headerlink,.rst-content h5 .btn .fa-large.headerlink,.btn .rst-content h6 .fa-large.headerlink,.rst-content h6 .btn .fa-large.headerlink,.btn .rst-content dl dt .fa-large.headerlink,.rst-content dl dt .btn .fa-large.headerlink,.btn .rst-content tt.download span.fa-large:first-child,.rst-content tt.download .btn span.fa-large:first-child,.btn .rst-content code.download span.fa-large:first-child,.rst-content code.download .btn span.fa-large:first-child,.btn .fa-large.icon,.nav .fa.fa-large,.nav .wy-menu-vertical li span.fa-large.toctree-expand,.wy-menu-vertical li .nav span.fa-large.toctree-expand,.nav .rst-content .fa-large.admonition-title,.rst-content .nav .fa-large.admonition-title,.nav .rst-content h1 .fa-large.headerlink,.rst-content h1 .nav .fa-large.headerlink,.nav .rst-content h2 .fa-large.headerlink,.rst-content h2 .nav .fa-large.headerlink,.nav .rst-content p.caption .fa-large.headerlink,.rst-content p.caption .nav .fa-large.headerlink,.nav .rst-content h3 .fa-large.headerlink,.rst-content h3 .nav .fa-large.headerlink,.nav .rst-content h4 .fa-large.headerlink,.rst-content h4 .nav .fa-large.headerlink,.nav .rst-content h5 .fa-large.headerlink,.rst-content h5 .nav .fa-large.headerlink,.nav .rst-content h6 .fa-large.headerlink,.rst-content h6 .nav .fa-large.headerlink,.nav .rst-content dl dt .fa-large.headerlink,.rst-content dl dt .nav .fa-large.headerlink,.nav .rst-content tt.download span.fa-large:first-child,.rst-content tt.download .nav span.fa-large:first-child,.nav .rst-content code.download span.fa-large:first-child,.rst-content code.download .nav span.fa-large:first-child,.nav .fa-large.icon{line-height:0.9em}.btn .fa.fa-spin,.btn .wy-menu-vertical li span.fa-spin.toctree-expand,.wy-menu-vertical li .btn span.fa-spin.toctree-expand,.btn .rst-content .fa-spin.admonition-title,.rst-content .btn .fa-spin.admonition-title,.btn .rst-content h1 .fa-spin.headerlink,.rst-content h1 .btn .fa-spin.headerlink,.btn .rst-content h2 .fa-spin.headerlink,.rst-content h2 .btn .fa-spin.headerlink,.btn .rst-content p.caption .fa-spin.headerlink,.rst-content p.caption .btn .fa-spin.headerlink,.btn .rst-content h3 .fa-spin.headerlink,.rst-content h3 .btn .fa-spin.headerlink,.btn .rst-content h4 .fa-spin.headerlink,.rst-content h4 .btn .fa-spin.headerlink,.btn .rst-content h5 .fa-spin.headerlink,.rst-content h5 .btn .fa-spin.headerlink,.btn .rst-content h6 .fa-spin.headerlink,.rst-content h6 .btn .fa-spin.headerlink,.btn .rst-content dl dt .fa-spin.headerlink,.rst-content dl dt .btn .fa-spin.headerlink,.btn .rst-content tt.download span.fa-spin:first-child,.rst-content tt.download .btn span.fa-spin:first-child,.btn .rst-content code.download span.fa-spin:first-child,.rst-content code.download .btn span.fa-spin:first-child,.btn .fa-spin.icon,.nav .fa.fa-spin,.nav .wy-menu-vertical li span.fa-spin.toctree-expand,.wy-menu-vertical li .nav span.fa-spin.toctree-expand,.nav .rst-content .fa-spin.admonition-title,.rst-content .nav .fa-spin.admonition-title,.nav .rst-content h1 .fa-spin.headerlink,.rst-content h1 .nav .fa-spin.headerlink,.nav .rst-content h2 .fa-spin.headerlink,.rst-content h2 .nav .fa-spin.headerlink,.nav .rst-content p.caption .fa-spin.headerlink,.rst-content p.caption .nav .fa-spin.headerlink,.nav .rst-content h3 .fa-spin.headerlink,.rst-content h3 .nav .fa-spin.headerlink,.nav .rst-content h4 .fa-spin.headerlink,.rst-content h4 .nav .fa-spin.headerlink,.nav .rst-content h5 .fa-spin.headerlink,.rst-content h5 .nav .fa-spin.headerlink,.nav .rst-content h6 .fa-spin.headerlink,.rst-content h6 .nav .fa-spin.headerlink,.nav .rst-content dl dt .fa-spin.headerlink,.rst-content dl dt .nav .fa-spin.headerlink,.nav .rst-content tt.download span.fa-spin:first-child,.rst-content tt.download .nav span.fa-spin:first-child,.nav .rst-content code.download span.fa-spin:first-child,.rst-content code.download .nav span.fa-spin:first-child,.nav .fa-spin.icon{display:inline-block}.btn.fa:before,.wy-menu-vertical li span.btn.toctree-expand:before,.rst-content .btn.admonition-title:before,.rst-content h1 .btn.headerlink:before,.rst-content h2 .btn.headerlink:before,.rst-content p.caption .btn.headerlink:before,.rst-content h3 .btn.headerlink:before,.rst-content h4 .btn.headerlink:before,.rst-content h5 .btn.headerlink:before,.rst-content h6 .btn.headerlink:before,.rst-content dl dt .btn.headerlink:before,.rst-content tt.download span.btn:first-child:before,.rst-content code.download span.btn:first-child:before,.btn.icon:before{opacity:0.5;-webkit-transition:opacity 0.05s ease-in;-moz-transition:opacity 0.05s ease-in;transition:opacity 0.05s ease-in}.btn.fa:hover:before,.wy-menu-vertical li span.btn.toctree-expand:hover:before,.rst-content .btn.admonition-title:hover:before,.rst-content h1 .btn.headerlink:hover:before,.rst-content h2 .btn.headerlink:hover:before,.rst-content p.caption .btn.headerlink:hover:before,.rst-content h3 .btn.headerlink:hover:before,.rst-content h4 .btn.headerlink:hover:before,.rst-content h5 .btn.headerlink:hover:before,.rst-content h6 .btn.headerlink:hover:before,.rst-content dl dt .btn.headerlink:hover:before,.rst-content tt.download span.btn:first-child:hover:before,.rst-content code.download span.btn:first-child:hover:before,.btn.icon:hover:before{opacity:1}.btn-mini .fa:before,.btn-mini .wy-menu-vertical li span.toctree-expand:before,.wy-menu-vertical li .btn-mini span.toctree-expand:before,.btn-mini .rst-content .admonition-title:before,.rst-content .btn-mini .admonition-title:before,.btn-mini .rst-content h1 .headerlink:before,.rst-content h1 .btn-mini .headerlink:before,.btn-mini .rst-content h2 .headerlink:before,.rst-content h2 .btn-mini .headerlink:before,.btn-mini .rst-content p.caption .headerlink:before,.rst-content p.caption .btn-mini .headerlink:before,.btn-mini .rst-content h3 .headerlink:before,.rst-content h3 .btn-mini .headerlink:before,.btn-mini .rst-content h4 .headerlink:before,.rst-content h4 .btn-mini .headerlink:before,.btn-mini .rst-content h5 .headerlink:before,.rst-content h5 .btn-mini .headerlink:before,.btn-mini .rst-content h6 .headerlink:before,.rst-content h6 .btn-mini .headerlink:before,.btn-mini .rst-content dl dt .headerlink:before,.rst-content dl dt .btn-mini .headerlink:before,.btn-mini .rst-content tt.download span:first-child:before,.rst-content tt.download .btn-mini span:first-child:before,.btn-mini .rst-content code.download span:first-child:before,.rst-content code.download .btn-mini span:first-child:before,.btn-mini .icon:before{font-size:14px;vertical-align:-15%}.wy-alert,.rst-content .note,.rst-content .attention,.rst-content .caution,.rst-content .danger,.rst-content .error,.rst-content .hint,.rst-content .important,.rst-content .tip,.rst-content .warning,.rst-content .seealso,.rst-content .admonition-todo{padding:12px;line-height:24px;margin-bottom:24px;background:#e7f2fa}.wy-alert-title,.rst-content .admonition-title{color:#fff;font-weight:bold;display:block;color:#fff;background:#6ab0de;margin:-12px;padding:6px 12px;margin-bottom:12px}.wy-alert.wy-alert-danger,.rst-content .wy-alert-danger.note,.rst-content .wy-alert-danger.attention,.rst-content .wy-alert-danger.caution,.rst-content .danger,.rst-content .error,.rst-content .wy-alert-danger.hint,.rst-content .wy-alert-danger.important,.rst-content .wy-alert-danger.tip,.rst-content .wy-alert-danger.warning,.rst-content .wy-alert-danger.seealso,.rst-content .wy-alert-danger.admonition-todo{background:#fdf3f2}.wy-alert.wy-alert-danger .wy-alert-title,.rst-content .wy-alert-danger.note .wy-alert-title,.rst-content .wy-alert-danger.attention .wy-alert-title,.rst-content .wy-alert-danger.caution .wy-alert-title,.rst-content .danger .wy-alert-title,.rst-content .error .wy-alert-title,.rst-content .wy-alert-danger.hint .wy-alert-title,.rst-content .wy-alert-danger.important .wy-alert-title,.rst-content .wy-alert-danger.tip .wy-alert-title,.rst-content .wy-alert-danger.warning .wy-alert-title,.rst-content .wy-alert-danger.seealso .wy-alert-title,.rst-content .wy-alert-danger.admonition-todo .wy-alert-title,.wy-alert.wy-alert-danger .rst-content .admonition-title,.rst-content .wy-alert.wy-alert-danger .admonition-title,.rst-content .wy-alert-danger.note .admonition-title,.rst-content .wy-alert-danger.attention .admonition-title,.rst-content .wy-alert-danger.caution .admonition-title,.rst-content .danger .admonition-title,.rst-content .error .admonition-title,.rst-content .wy-alert-danger.hint .admonition-title,.rst-content .wy-alert-danger.important .admonition-title,.rst-content .wy-alert-danger.tip .admonition-title,.rst-content .wy-alert-danger.warning .admonition-title,.rst-content .wy-alert-danger.seealso .admonition-title,.rst-content .wy-alert-danger.admonition-todo .admonition-title{background:#f29f97}.wy-alert.wy-alert-warning,.rst-content .wy-alert-warning.note,.rst-content .attention,.rst-content .caution,.rst-content .wy-alert-warning.danger,.rst-content .wy-alert-warning.error,.rst-content .wy-alert-warning.hint,.rst-content .wy-alert-warning.important,.rst-content .wy-alert-warning.tip,.rst-content .warning,.rst-content .wy-alert-warning.seealso,.rst-content .admonition-todo{background:#ffedcc}.wy-alert.wy-alert-warning .wy-alert-title,.rst-content .wy-alert-warning.note .wy-alert-title,.rst-content .attention .wy-alert-title,.rst-content .caution .wy-alert-title,.rst-content .wy-alert-warning.danger .wy-alert-title,.rst-content .wy-alert-warning.error .wy-alert-title,.rst-content .wy-alert-warning.hint .wy-alert-title,.rst-content .wy-alert-warning.important .wy-alert-title,.rst-content .wy-alert-warning.tip .wy-alert-title,.rst-content .warning .wy-alert-title,.rst-content .wy-alert-warning.seealso .wy-alert-title,.rst-content .admonition-todo .wy-alert-title,.wy-alert.wy-alert-warning .rst-content .admonition-title,.rst-content .wy-alert.wy-alert-warning .admonition-title,.rst-content .wy-alert-warning.note .admonition-title,.rst-content .attention .admonition-title,.rst-content .caution .admonition-title,.rst-content .wy-alert-warning.danger .admonition-title,.rst-content .wy-alert-warning.error .admonition-title,.rst-content .wy-alert-warning.hint .admonition-title,.rst-content .wy-alert-warning.important .admonition-title,.rst-content .wy-alert-warning.tip .admonition-title,.rst-content .warning .admonition-title,.rst-content .wy-alert-warning.seealso .admonition-title,.rst-content .admonition-todo .admonition-title{background:#f0b37e}.wy-alert.wy-alert-info,.rst-content .note,.rst-content .wy-alert-info.attention,.rst-content .wy-alert-info.caution,.rst-content .wy-alert-info.danger,.rst-content .wy-alert-info.error,.rst-content .wy-alert-info.hint,.rst-content .wy-alert-info.important,.rst-content .wy-alert-info.tip,.rst-content .wy-alert-info.warning,.rst-content .seealso,.rst-content .wy-alert-info.admonition-todo{background:#e7f2fa}.wy-alert.wy-alert-info .wy-alert-title,.rst-content .note .wy-alert-title,.rst-content .wy-alert-info.attention .wy-alert-title,.rst-content .wy-alert-info.caution .wy-alert-title,.rst-content .wy-alert-info.danger .wy-alert-title,.rst-content .wy-alert-info.error .wy-alert-title,.rst-content .wy-alert-info.hint .wy-alert-title,.rst-content .wy-alert-info.important .wy-alert-title,.rst-content .wy-alert-info.tip .wy-alert-title,.rst-content .wy-alert-info.warning .wy-alert-title,.rst-content .seealso .wy-alert-title,.rst-content .wy-alert-info.admonition-todo .wy-alert-title,.wy-alert.wy-alert-info .rst-content .admonition-title,.rst-content .wy-alert.wy-alert-info .admonition-title,.rst-content .note .admonition-title,.rst-content .wy-alert-info.attention .admonition-title,.rst-content .wy-alert-info.caution .admonition-title,.rst-content .wy-alert-info.danger .admonition-title,.rst-content .wy-alert-info.error .admonition-title,.rst-content .wy-alert-info.hint .admonition-title,.rst-content .wy-alert-info.important .admonition-title,.rst-content .wy-alert-info.tip .admonition-title,.rst-content .wy-alert-info.warning .admonition-title,.rst-content .seealso .admonition-title,.rst-content .wy-alert-info.admonition-todo .admonition-title{background:#6ab0de}.wy-alert.wy-alert-success,.rst-content .wy-alert-success.note,.rst-content .wy-alert-success.attention,.rst-content .wy-alert-success.caution,.rst-content .wy-alert-success.danger,.rst-content .wy-alert-success.error,.rst-content .hint,.rst-content .important,.rst-content .tip,.rst-content .wy-alert-success.warning,.rst-content .wy-alert-success.seealso,.rst-content .wy-alert-success.admonition-todo{background:#dbfaf4}.wy-alert.wy-alert-success .wy-alert-title,.rst-content .wy-alert-success.note .wy-alert-title,.rst-content .wy-alert-success.attention .wy-alert-title,.rst-content .wy-alert-success.caution .wy-alert-title,.rst-content .wy-alert-success.danger .wy-alert-title,.rst-content .wy-alert-success.error .wy-alert-title,.rst-content .hint .wy-alert-title,.rst-content .important .wy-alert-title,.rst-content .tip .wy-alert-title,.rst-content .wy-alert-success.warning .wy-alert-title,.rst-content .wy-alert-success.seealso .wy-alert-title,.rst-content .wy-alert-success.admonition-todo .wy-alert-title,.wy-alert.wy-alert-success .rst-content .admonition-title,.rst-content .wy-alert.wy-alert-success .admonition-title,.rst-content .wy-alert-success.note .admonition-title,.rst-content .wy-alert-success.attention .admonition-title,.rst-content .wy-alert-success.caution .admonition-title,.rst-content .wy-alert-success.danger .admonition-title,.rst-content .wy-alert-success.error .admonition-title,.rst-content .hint .admonition-title,.rst-content .important .admonition-title,.rst-content .tip .admonition-title,.rst-content .wy-alert-success.warning .admonition-title,.rst-content .wy-alert-success.seealso .admonition-title,.rst-content .wy-alert-success.admonition-todo .admonition-title{background:#1abc9c}.wy-alert.wy-alert-neutral,.rst-content .wy-alert-neutral.note,.rst-content .wy-alert-neutral.attention,.rst-content .wy-alert-neutral.caution,.rst-content .wy-alert-neutral.danger,.rst-content .wy-alert-neutral.error,.rst-content .wy-alert-neutral.hint,.rst-content .wy-alert-neutral.important,.rst-content .wy-alert-neutral.tip,.rst-content .wy-alert-neutral.warning,.rst-content .wy-alert-neutral.seealso,.rst-content .wy-alert-neutral.admonition-todo{background:#f3f6f6}.wy-alert.wy-alert-neutral .wy-alert-title,.rst-content .wy-alert-neutral.note .wy-alert-title,.rst-content .wy-alert-neutral.attention .wy-alert-title,.rst-content .wy-alert-neutral.caution .wy-alert-title,.rst-content .wy-alert-neutral.danger .wy-alert-title,.rst-content .wy-alert-neutral.error .wy-alert-title,.rst-content .wy-alert-neutral.hint .wy-alert-title,.rst-content .wy-alert-neutral.important .wy-alert-title,.rst-content .wy-alert-neutral.tip .wy-alert-title,.rst-content .wy-alert-neutral.warning .wy-alert-title,.rst-content .wy-alert-neutral.seealso .wy-alert-title,.rst-content .wy-alert-neutral.admonition-todo .wy-alert-title,.wy-alert.wy-alert-neutral .rst-content .admonition-title,.rst-content .wy-alert.wy-alert-neutral .admonition-title,.rst-content .wy-alert-neutral.note .admonition-title,.rst-content .wy-alert-neutral.attention .admonition-title,.rst-content .wy-alert-neutral.caution .admonition-title,.rst-content .wy-alert-neutral.danger .admonition-title,.rst-content .wy-alert-neutral.error .admonition-title,.rst-content .wy-alert-neutral.hint .admonition-title,.rst-content .wy-alert-neutral.important .admonition-title,.rst-content .wy-alert-neutral.tip .admonition-title,.rst-content .wy-alert-neutral.warning .admonition-title,.rst-content .wy-alert-neutral.seealso .admonition-title,.rst-content .wy-alert-neutral.admonition-todo .admonition-title{color:#404040;background:#e1e4e5}.wy-alert.wy-alert-neutral a,.rst-content .wy-alert-neutral.note a,.rst-content .wy-alert-neutral.attention a,.rst-content .wy-alert-neutral.caution a,.rst-content .wy-alert-neutral.danger a,.rst-content .wy-alert-neutral.error a,.rst-content .wy-alert-neutral.hint a,.rst-content .wy-alert-neutral.important a,.rst-content .wy-alert-neutral.tip a,.rst-content .wy-alert-neutral.warning a,.rst-content .wy-alert-neutral.seealso a,.rst-content .wy-alert-neutral.admonition-todo a{color:#2980B9}.wy-alert p:last-child,.rst-content .note p:last-child,.rst-content .attention p:last-child,.rst-content .caution p:last-child,.rst-content .danger p:last-child,.rst-content .error p:last-child,.rst-content .hint p:last-child,.rst-content .important p:last-child,.rst-content .tip p:last-child,.rst-content .warning p:last-child,.rst-content .seealso p:last-child,.rst-content .admonition-todo p:last-child{margin-bottom:0}.wy-tray-container{position:fixed;bottom:0px;left:0;z-index:600}.wy-tray-container li{display:block;width:300px;background:transparent;color:#fff;text-align:center;box-shadow:0 5px 5px 0 rgba(0,0,0,0.1);padding:0 24px;min-width:20%;opacity:0;height:0;line-height:56px;overflow:hidden;-webkit-transition:all 0.3s ease-in;-moz-transition:all 0.3s ease-in;transition:all 0.3s ease-in}.wy-tray-container li.wy-tray-item-success{background:#27AE60}.wy-tray-container li.wy-tray-item-info{background:#2980B9}.wy-tray-container li.wy-tray-item-warning{background:#E67E22}.wy-tray-container li.wy-tray-item-danger{background:#E74C3C}.wy-tray-container li.on{opacity:1;height:56px}@media screen and (max-width: 768px){.wy-tray-container{bottom:auto;top:0;width:100%}.wy-tray-container li{width:100%}}button{font-size:100%;margin:0;vertical-align:baseline;*vertical-align:middle;cursor:pointer;line-height:normal;-webkit-appearance:button;*overflow:visible}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}button[disabled]{cursor:default}.btn{display:inline-block;border-radius:2px;line-height:normal;white-space:nowrap;text-align:center;cursor:pointer;font-size:100%;padding:6px 12px 8px 12px;color:#fff;border:1px solid rgba(0,0,0,0.1);background-color:#27AE60;text-decoration:none;font-weight:normal;font-family:"Lato","proxima-nova","Helvetica Neue",Arial,sans-serif;box-shadow:0px 1px 2px -1px rgba(255,255,255,0.5) inset,0px -2px 0px 0px rgba(0,0,0,0.1) inset;outline-none:false;vertical-align:middle;*display:inline;zoom:1;-webkit-user-drag:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-webkit-transition:all 0.1s linear;-moz-transition:all 0.1s linear;transition:all 0.1s linear}.btn-hover{background:#2e8ece;color:#fff}.btn:hover{background:#2cc36b;color:#fff}.btn:focus{background:#2cc36b;outline:0}.btn:active{box-shadow:0px -1px 0px 0px rgba(0,0,0,0.05) inset,0px 2px 0px 0px rgba(0,0,0,0.1) inset;padding:8px 12px 6px 12px}.btn:visited{color:#fff}.btn:disabled{background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);filter:alpha(opacity=40);opacity:0.4;cursor:not-allowed;box-shadow:none}.btn-disabled{background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);filter:alpha(opacity=40);opacity:0.4;cursor:not-allowed;box-shadow:none}.btn-disabled:hover,.btn-disabled:focus,.btn-disabled:active{background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);filter:alpha(opacity=40);opacity:0.4;cursor:not-allowed;box-shadow:none}.btn::-moz-focus-inner{padding:0;border:0}.btn-small{font-size:80%}.btn-info{background-color:#2980B9 !important}.btn-info:hover{background-color:#2e8ece !important}.btn-neutral{background-color:#f3f6f6 !important;color:#404040 !important}.btn-neutral:hover{background-color:#e5ebeb !important;color:#404040}.btn-neutral:visited{color:#404040 !important}.btn-success{background-color:#27AE60 !important}.btn-success:hover{background-color:#295 !important}.btn-danger{background-color:#E74C3C !important}.btn-danger:hover{background-color:#ea6153 !important}.btn-warning{background-color:#E67E22 !important}.btn-warning:hover{background-color:#e98b39 !important}.btn-invert{background-color:#222}.btn-invert:hover{background-color:#2f2f2f !important}.btn-link{background-color:transparent !important;color:#2980B9;box-shadow:none;border-color:transparent !important}.btn-link:hover{background-color:transparent !important;color:#409ad5 !important;box-shadow:none}.btn-link:active{background-color:transparent !important;color:#409ad5 !important;box-shadow:none}.btn-link:visited{color:#9B59B6}.wy-btn-group .btn,.wy-control .btn{vertical-align:middle}.wy-btn-group{margin-bottom:24px;*zoom:1}.wy-btn-group:before,.wy-btn-group:after{display:table;content:""}.wy-btn-group:after{clear:both}.wy-dropdown{position:relative;display:inline-block}.wy-dropdown-active .wy-dropdown-menu{display:block}.wy-dropdown-menu{position:absolute;left:0;display:none;float:left;top:100%;min-width:100%;background:#fcfcfc;z-index:100;border:solid 1px #cfd7dd;box-shadow:0 2px 2px 0 rgba(0,0,0,0.1);padding:12px}.wy-dropdown-menu>dd>a{display:block;clear:both;color:#404040;white-space:nowrap;font-size:90%;padding:0 12px;cursor:pointer}.wy-dropdown-menu>dd>a:hover{background:#2980B9;color:#fff}.wy-dropdown-menu>dd.divider{border-top:solid 1px #cfd7dd;margin:6px 0}.wy-dropdown-menu>dd.search{padding-bottom:12px}.wy-dropdown-menu>dd.search input[type="search"]{width:100%}.wy-dropdown-menu>dd.call-to-action{background:#e3e3e3;text-transform:uppercase;font-weight:500;font-size:80%}.wy-dropdown-menu>dd.call-to-action:hover{background:#e3e3e3}.wy-dropdown-menu>dd.call-to-action .btn{color:#fff}.wy-dropdown.wy-dropdown-up .wy-dropdown-menu{bottom:100%;top:auto;left:auto;right:0}.wy-dropdown.wy-dropdown-bubble .wy-dropdown-menu{background:#fcfcfc;margin-top:2px}.wy-dropdown.wy-dropdown-bubble .wy-dropdown-menu a{padding:6px 12px}.wy-dropdown.wy-dropdown-bubble .wy-dropdown-menu a:hover{background:#2980B9;color:#fff}.wy-dropdown.wy-dropdown-left .wy-dropdown-menu{right:0;left:auto;text-align:right}.wy-dropdown-arrow:before{content:" ";border-bottom:5px solid #f5f5f5;border-left:5px solid transparent;border-right:5px solid transparent;position:absolute;display:block;top:-4px;left:50%;margin-left:-3px}.wy-dropdown-arrow.wy-dropdown-arrow-left:before{left:11px}.wy-form-stacked select{display:block}.wy-form-aligned input,.wy-form-aligned textarea,.wy-form-aligned select,.wy-form-aligned .wy-help-inline,.wy-form-aligned label{display:inline-block;*display:inline;*zoom:1;vertical-align:middle}.wy-form-aligned .wy-control-group>label{display:inline-block;vertical-align:middle;width:10em;margin:6px 12px 0 0;float:left}.wy-form-aligned .wy-control{float:left}.wy-form-aligned .wy-control label{display:block}.wy-form-aligned .wy-control select{margin-top:6px}fieldset{border:0;margin:0;padding:0}legend{display:block;width:100%;border:0;padding:0;white-space:normal;margin-bottom:24px;font-size:150%;*margin-left:-7px}label{display:block;margin:0 0 0.3125em 0;color:#333;font-size:90%}input,select,textarea{font-size:100%;margin:0;vertical-align:baseline;*vertical-align:middle}.wy-control-group{margin-bottom:24px;*zoom:1;max-width:68em;margin-left:auto;margin-right:auto;*zoom:1}.wy-control-group:before,.wy-control-group:after{display:table;content:""}.wy-control-group:after{clear:both}.wy-control-group:before,.wy-control-group:after{display:table;content:""}.wy-control-group:after{clear:both}.wy-control-group.wy-control-group-required>label:after{content:" *";color:#E74C3C}.wy-control-group .wy-form-full,.wy-control-group .wy-form-halves,.wy-control-group .wy-form-thirds{padding-bottom:12px}.wy-control-group .wy-form-full select,.wy-control-group .wy-form-halves select,.wy-control-group .wy-form-thirds select{width:100%}.wy-control-group .wy-form-full input[type="text"],.wy-control-group .wy-form-full input[type="password"],.wy-control-group .wy-form-full input[type="email"],.wy-control-group .wy-form-full input[type="url"],.wy-control-group .wy-form-full input[type="date"],.wy-control-group .wy-form-full input[type="month"],.wy-control-group .wy-form-full input[type="time"],.wy-control-group .wy-form-full input[type="datetime"],.wy-control-group .wy-form-full input[type="datetime-local"],.wy-control-group .wy-form-full input[type="week"],.wy-control-group .wy-form-full input[type="number"],.wy-control-group .wy-form-full input[type="search"],.wy-control-group .wy-form-full input[type="tel"],.wy-control-group .wy-form-full input[type="color"],.wy-control-group .wy-form-halves input[type="text"],.wy-control-group .wy-form-halves input[type="password"],.wy-control-group .wy-form-halves input[type="email"],.wy-control-group .wy-form-halves input[type="url"],.wy-control-group .wy-form-halves input[type="date"],.wy-control-group .wy-form-halves input[type="month"],.wy-control-group .wy-form-halves input[type="time"],.wy-control-group .wy-form-halves input[type="datetime"],.wy-control-group .wy-form-halves input[type="datetime-local"],.wy-control-group .wy-form-halves input[type="week"],.wy-control-group .wy-form-halves input[type="number"],.wy-control-group .wy-form-halves input[type="search"],.wy-control-group .wy-form-halves input[type="tel"],.wy-control-group .wy-form-halves input[type="color"],.wy-control-group .wy-form-thirds input[type="text"],.wy-control-group .wy-form-thirds input[type="password"],.wy-control-group .wy-form-thirds input[type="email"],.wy-control-group .wy-form-thirds input[type="url"],.wy-control-group .wy-form-thirds input[type="date"],.wy-control-group .wy-form-thirds input[type="month"],.wy-control-group .wy-form-thirds input[type="time"],.wy-control-group .wy-form-thirds input[type="datetime"],.wy-control-group .wy-form-thirds input[type="datetime-local"],.wy-control-group .wy-form-thirds input[type="week"],.wy-control-group .wy-form-thirds input[type="number"],.wy-control-group .wy-form-thirds input[type="search"],.wy-control-group .wy-form-thirds input[type="tel"],.wy-control-group .wy-form-thirds input[type="color"]{width:100%}.wy-control-group .wy-form-full{float:left;display:block;margin-right:2.35765%;width:100%;margin-right:0}.wy-control-group .wy-form-full:last-child{margin-right:0}.wy-control-group .wy-form-halves{float:left;display:block;margin-right:2.35765%;width:48.82117%}.wy-control-group .wy-form-halves:last-child{margin-right:0}.wy-control-group .wy-form-halves:nth-of-type(2n){margin-right:0}.wy-control-group .wy-form-halves:nth-of-type(2n+1){clear:left}.wy-control-group .wy-form-thirds{float:left;display:block;margin-right:2.35765%;width:31.76157%}.wy-control-group .wy-form-thirds:last-child{margin-right:0}.wy-control-group .wy-form-thirds:nth-of-type(3n){margin-right:0}.wy-control-group .wy-form-thirds:nth-of-type(3n+1){clear:left}.wy-control-group.wy-control-group-no-input .wy-control{margin:6px 0 0 0;font-size:90%}.wy-control-no-input{display:inline-block;margin:6px 0 0 0;font-size:90%}.wy-control-group.fluid-input input[type="text"],.wy-control-group.fluid-input input[type="password"],.wy-control-group.fluid-input input[type="email"],.wy-control-group.fluid-input input[type="url"],.wy-control-group.fluid-input input[type="date"],.wy-control-group.fluid-input input[type="month"],.wy-control-group.fluid-input input[type="time"],.wy-control-group.fluid-input input[type="datetime"],.wy-control-group.fluid-input input[type="datetime-local"],.wy-control-group.fluid-input input[type="week"],.wy-control-group.fluid-input input[type="number"],.wy-control-group.fluid-input input[type="search"],.wy-control-group.fluid-input input[type="tel"],.wy-control-group.fluid-input input[type="color"]{width:100%}.wy-form-message-inline{display:inline-block;padding-left:0.3em;color:#666;vertical-align:middle;font-size:90%}.wy-form-message{display:block;color:#999;font-size:70%;margin-top:0.3125em;font-style:italic}.wy-form-message p{font-size:inherit;font-style:italic;margin-bottom:6px}.wy-form-message p:last-child{margin-bottom:0}input{line-height:normal}input[type="button"],input[type="reset"],input[type="submit"]{-webkit-appearance:button;cursor:pointer;font-family:"Lato","proxima-nova","Helvetica Neue",Arial,sans-serif;*overflow:visible}input[type="text"],input[type="password"],input[type="email"],input[type="url"],input[type="date"],input[type="month"],input[type="time"],input[type="datetime"],input[type="datetime-local"],input[type="week"],input[type="number"],input[type="search"],input[type="tel"],input[type="color"]{-webkit-appearance:none;padding:6px;display:inline-block;border:1px solid #ccc;font-size:80%;font-family:"Lato","proxima-nova","Helvetica Neue",Arial,sans-serif;box-shadow:inset 0 1px 3px #ddd;border-radius:0;-webkit-transition:border 0.3s linear;-moz-transition:border 0.3s linear;transition:border 0.3s linear}input[type="datetime-local"]{padding:0.34375em 0.625em}input[disabled]{cursor:default}input[type="checkbox"],input[type="radio"]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;padding:0;margin-right:0.3125em;*height:13px;*width:13px}input[type="search"]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}input[type="search"]::-webkit-search-cancel-button,input[type="search"]::-webkit-search-decoration{-webkit-appearance:none}input[type="text"]:focus,input[type="password"]:focus,input[type="email"]:focus,input[type="url"]:focus,input[type="date"]:focus,input[type="month"]:focus,input[type="time"]:focus,input[type="datetime"]:focus,input[type="datetime-local"]:focus,input[type="week"]:focus,input[type="number"]:focus,input[type="search"]:focus,input[type="tel"]:focus,input[type="color"]:focus{outline:0;outline:thin dotted \9;border-color:#333}input.no-focus:focus{border-color:#ccc !important}input[type="file"]:focus,input[type="radio"]:focus,input[type="checkbox"]:focus{outline:thin dotted #333;outline:1px auto #129FEA}input[type="text"][disabled],input[type="password"][disabled],input[type="email"][disabled],input[type="url"][disabled],input[type="date"][disabled],input[type="month"][disabled],input[type="time"][disabled],input[type="datetime"][disabled],input[type="datetime-local"][disabled],input[type="week"][disabled],input[type="number"][disabled],input[type="search"][disabled],input[type="tel"][disabled],input[type="color"][disabled]{cursor:not-allowed;background-color:#fafafa}input:focus:invalid,textarea:focus:invalid,select:focus:invalid{color:#E74C3C;border:1px solid #E74C3C}input:focus:invalid:focus,textarea:focus:invalid:focus,select:focus:invalid:focus{border-color:#E74C3C}input[type="file"]:focus:invalid:focus,input[type="radio"]:focus:invalid:focus,input[type="checkbox"]:focus:invalid:focus{outline-color:#E74C3C}input.wy-input-large{padding:12px;font-size:100%}textarea{overflow:auto;vertical-align:top;width:100%;font-family:"Lato","proxima-nova","Helvetica Neue",Arial,sans-serif}select,textarea{padding:0.5em 0.625em;display:inline-block;border:1px solid #ccc;font-size:80%;box-shadow:inset 0 1px 3px #ddd;-webkit-transition:border 0.3s linear;-moz-transition:border 0.3s linear;transition:border 0.3s linear}select{border:1px solid #ccc;background-color:#fff}select[multiple]{height:auto}select:focus,textarea:focus{outline:0}select[disabled],textarea[disabled],input[readonly],select[readonly],textarea[readonly]{cursor:not-allowed;background-color:#fafafa}input[type="radio"][disabled],input[type="checkbox"][disabled]{cursor:not-allowed}.wy-checkbox,.wy-radio{margin:6px 0;color:#404040;display:block}.wy-checkbox input,.wy-radio input{vertical-align:baseline}.wy-form-message-inline{display:inline-block;*display:inline;*zoom:1;vertical-align:middle}.wy-input-prefix,.wy-input-suffix{white-space:nowrap;padding:6px}.wy-input-prefix .wy-input-context,.wy-input-suffix .wy-input-context{line-height:27px;padding:0 8px;display:inline-block;font-size:80%;background-color:#f3f6f6;border:solid 1px #ccc;color:#999}.wy-input-suffix .wy-input-context{border-left:0}.wy-input-prefix .wy-input-context{border-right:0}.wy-switch{width:36px;height:12px;margin:12px 0;position:relative;border-radius:4px;background:#ccc;cursor:pointer;-webkit-transition:all 0.2s ease-in-out;-moz-transition:all 0.2s ease-in-out;transition:all 0.2s ease-in-out}.wy-switch:before{position:absolute;content:"";display:block;width:18px;height:18px;border-radius:4px;background:#999;left:-3px;top:-3px;-webkit-transition:all 0.2s ease-in-out;-moz-transition:all 0.2s ease-in-out;transition:all 0.2s ease-in-out}.wy-switch:after{content:"false";position:absolute;left:48px;display:block;font-size:12px;color:#ccc}.wy-switch.active{background:#1e8449}.wy-switch.active:before{left:24px;background:#27AE60}.wy-switch.active:after{content:"true"}.wy-switch.disabled,.wy-switch.active.disabled{cursor:not-allowed}.wy-control-group.wy-control-group-error .wy-form-message,.wy-control-group.wy-control-group-error>label{color:#E74C3C}.wy-control-group.wy-control-group-error input[type="text"],.wy-control-group.wy-control-group-error input[type="password"],.wy-control-group.wy-control-group-error input[type="email"],.wy-control-group.wy-control-group-error input[type="url"],.wy-control-group.wy-control-group-error input[type="date"],.wy-control-group.wy-control-group-error input[type="month"],.wy-control-group.wy-control-group-error input[type="time"],.wy-control-group.wy-control-group-error input[type="datetime"],.wy-control-group.wy-control-group-error input[type="datetime-local"],.wy-control-group.wy-control-group-error input[type="week"],.wy-control-group.wy-control-group-error input[type="number"],.wy-control-group.wy-control-group-error input[type="search"],.wy-control-group.wy-control-group-error input[type="tel"],.wy-control-group.wy-control-group-error input[type="color"]{border:solid 1px #E74C3C}.wy-control-group.wy-control-group-error textarea{border:solid 1px #E74C3C}.wy-inline-validate{white-space:nowrap}.wy-inline-validate .wy-input-context{padding:0.5em 0.625em;display:inline-block;font-size:80%}.wy-inline-validate.wy-inline-validate-success .wy-input-context{color:#27AE60}.wy-inline-validate.wy-inline-validate-danger .wy-input-context{color:#E74C3C}.wy-inline-validate.wy-inline-validate-warning .wy-input-context{color:#E67E22}.wy-inline-validate.wy-inline-validate-info .wy-input-context{color:#2980B9}.rotate-90{-webkit-transform:rotate(90deg);-moz-transform:rotate(90deg);-ms-transform:rotate(90deg);-o-transform:rotate(90deg);transform:rotate(90deg)}.rotate-180{-webkit-transform:rotate(180deg);-moz-transform:rotate(180deg);-ms-transform:rotate(180deg);-o-transform:rotate(180deg);transform:rotate(180deg)}.rotate-270{-webkit-transform:rotate(270deg);-moz-transform:rotate(270deg);-ms-transform:rotate(270deg);-o-transform:rotate(270deg);transform:rotate(270deg)}.mirror{-webkit-transform:scaleX(-1);-moz-transform:scaleX(-1);-ms-transform:scaleX(-1);-o-transform:scaleX(-1);transform:scaleX(-1)}.mirror.rotate-90{-webkit-transform:scaleX(-1) rotate(90deg);-moz-transform:scaleX(-1) rotate(90deg);-ms-transform:scaleX(-1) rotate(90deg);-o-transform:scaleX(-1) rotate(90deg);transform:scaleX(-1) rotate(90deg)}.mirror.rotate-180{-webkit-transform:scaleX(-1) rotate(180deg);-moz-transform:scaleX(-1) rotate(180deg);-ms-transform:scaleX(-1) rotate(180deg);-o-transform:scaleX(-1) rotate(180deg);transform:scaleX(-1) rotate(180deg)}.mirror.rotate-270{-webkit-transform:scaleX(-1) rotate(270deg);-moz-transform:scaleX(-1) rotate(270deg);-ms-transform:scaleX(-1) rotate(270deg);-o-transform:scaleX(-1) rotate(270deg);transform:scaleX(-1) rotate(270deg)}@media only screen and (max-width: 480px){.wy-form button[type="submit"]{margin:0.7em 0 0}.wy-form input[type="text"],.wy-form input[type="password"],.wy-form input[type="email"],.wy-form input[type="url"],.wy-form input[type="date"],.wy-form input[type="month"],.wy-form input[type="time"],.wy-form input[type="datetime"],.wy-form input[type="datetime-local"],.wy-form input[type="week"],.wy-form input[type="number"],.wy-form input[type="search"],.wy-form input[type="tel"],.wy-form input[type="color"]{margin-bottom:0.3em;display:block}.wy-form label{margin-bottom:0.3em;display:block}.wy-form input[type="password"],.wy-form input[type="email"],.wy-form input[type="url"],.wy-form input[type="date"],.wy-form input[type="month"],.wy-form input[type="time"],.wy-form input[type="datetime"],.wy-form input[type="datetime-local"],.wy-form input[type="week"],.wy-form input[type="number"],.wy-form input[type="search"],.wy-form input[type="tel"],.wy-form input[type="color"]{margin-bottom:0}.wy-form-aligned .wy-control-group label{margin-bottom:0.3em;text-align:left;display:block;width:100%}.wy-form-aligned .wy-control{margin:1.5em 0 0 0}.wy-form .wy-help-inline,.wy-form-message-inline,.wy-form-message{display:block;font-size:80%;padding:6px 0}}@media screen and (max-width: 768px){.tablet-hide{display:none}}@media screen and (max-width: 480px){.mobile-hide{display:none}}.float-left{float:left}.float-right{float:right}.full-width{width:100%}.wy-table,.rst-content table.docutils,.rst-content table.field-list{border-collapse:collapse;border-spacing:0;empty-cells:show;margin-bottom:24px}.wy-table caption,.rst-content table.docutils caption,.rst-content table.field-list caption{color:#000;font:italic 85%/1 arial,sans-serif;padding:1em 0;text-align:center}.wy-table td,.rst-content table.docutils td,.rst-content table.field-list td,.wy-table th,.rst-content table.docutils th,.rst-content table.field-list th{font-size:90%;margin:0;overflow:visible;padding:8px 16px}.wy-table td:first-child,.rst-content table.docutils td:first-child,.rst-content table.field-list td:first-child,.wy-table th:first-child,.rst-content table.docutils th:first-child,.rst-content table.field-list th:first-child{border-left-width:0}.wy-table thead,.rst-content table.docutils thead,.rst-content table.field-list thead{color:#000;text-align:left;vertical-align:bottom;white-space:nowrap}.wy-table thead th,.rst-content table.docutils thead th,.rst-content table.field-list thead th{font-weight:bold;border-bottom:solid 2px #e1e4e5}.wy-table td,.rst-content table.docutils td,.rst-content table.field-list td{background-color:transparent;vertical-align:middle}.wy-table td p,.rst-content table.docutils td p,.rst-content table.field-list td p{line-height:18px}.wy-table td p:last-child,.rst-content table.docutils td p:last-child,.rst-content table.field-list td p:last-child{margin-bottom:0}.wy-table .wy-table-cell-min,.rst-content table.docutils .wy-table-cell-min,.rst-content table.field-list .wy-table-cell-min{width:1%;padding-right:0}.wy-table .wy-table-cell-min input[type=checkbox],.rst-content table.docutils .wy-table-cell-min input[type=checkbox],.rst-content table.field-list .wy-table-cell-min input[type=checkbox],.wy-table .wy-table-cell-min input[type=checkbox],.rst-content table.docutils .wy-table-cell-min input[type=checkbox],.rst-content table.field-list .wy-table-cell-min input[type=checkbox]{margin:0}.wy-table-secondary{color:gray;font-size:90%}.wy-table-tertiary{color:gray;font-size:80%}.wy-table-odd td,.wy-table-striped tr:nth-child(2n-1) td,.rst-content table.docutils:not(.field-list) tr:nth-child(2n-1) td{background-color:#f3f6f6}.wy-table-backed{background-color:#f3f6f6}.wy-table-bordered-all,.rst-content table.docutils{border:1px solid #e1e4e5}.wy-table-bordered-all td,.rst-content table.docutils td{border-bottom:1px solid #e1e4e5;border-left:1px solid #e1e4e5}.wy-table-bordered-all tbody>tr:last-child td,.rst-content table.docutils tbody>tr:last-child td{border-bottom-width:0}.wy-table-bordered{border:1px solid #e1e4e5}.wy-table-bordered-rows td{border-bottom:1px solid #e1e4e5}.wy-table-bordered-rows tbody>tr:last-child td{border-bottom-width:0}.wy-table-horizontal tbody>tr:last-child td{border-bottom-width:0}.wy-table-horizontal td,.wy-table-horizontal th{border-width:0 0 1px 0;border-bottom:1px solid #e1e4e5}.wy-table-horizontal tbody>tr:last-child td{border-bottom-width:0}.wy-table-responsive{margin-bottom:24px;max-width:100%;overflow:auto}.wy-table-responsive table{margin-bottom:0 !important}.wy-table-responsive table td,.wy-table-responsive table th{white-space:nowrap}a{color:#2980B9;text-decoration:none;cursor:pointer}a:hover{color:#3091d1}a:visited{color:#9B59B6}html{height:100%;overflow-x:hidden}body{font-family:"Lato","proxima-nova","Helvetica Neue",Arial,sans-serif;font-weight:normal;color:#404040;min-height:100%;overflow-x:hidden;background:#edf0f2}.wy-text-left{text-align:left}.wy-text-center{text-align:center}.wy-text-right{text-align:right}.wy-text-large{font-size:120%}.wy-text-normal{font-size:100%}.wy-text-small,small{font-size:80%}.wy-text-strike{text-decoration:line-through}.wy-text-warning{color:#E67E22 !important}a.wy-text-warning:hover{color:#eb9950 !important}.wy-text-info{color:#2980B9 !important}a.wy-text-info:hover{color:#409ad5 !important}.wy-text-success{color:#27AE60 !important}a.wy-text-success:hover{color:#36d278 !important}.wy-text-danger{color:#E74C3C !important}a.wy-text-danger:hover{color:#ed7669 !important}.wy-text-neutral{color:#404040 !important}a.wy-text-neutral:hover{color:#595959 !important}h1,h2,.rst-content p.caption,h3,h4,h5,h6,legend{margin-top:0;font-weight:700;font-family:"Roboto Slab","ff-tisa-web-pro","Georgia",Arial,sans-serif}p{line-height:24px;margin:0;font-size:16px;margin-bottom:24px}h1{font-size:175%}h2,.rst-content p.caption{font-size:150%}h3{font-size:125%}h4{font-size:115%}h5{font-size:110%}h6{font-size:100%}hr{display:block;height:1px;border:0;border-top:1px solid #e1e4e5;margin:24px 0;padding:0}code,.rst-content tt,.rst-content code{white-space:nowrap;max-width:100%;background:#fff;border:solid 1px #e1e4e5;font-size:75%;padding:0 5px;font-family:Consolas,"Andale Mono WT","Andale Mono","Lucida Console","Lucida Sans Typewriter","DejaVu Sans Mono","Bitstream Vera Sans Mono","Liberation Mono","Nimbus Mono L",Monaco,"Courier New",Courier,monospace;color:#E74C3C;overflow-x:auto}code.code-large,.rst-content tt.code-large{font-size:90%}.wy-plain-list-disc,.rst-content .section ul,.rst-content .toctree-wrapper ul,article ul{list-style:disc;line-height:24px;margin-bottom:24px}.wy-plain-list-disc li,.rst-content .section ul li,.rst-content .toctree-wrapper ul li,article ul li{list-style:disc;margin-left:24px}.wy-plain-list-disc li p:last-child,.rst-content .section ul li p:last-child,.rst-content .toctree-wrapper ul li p:last-child,article ul li p:last-child{margin-bottom:0}.wy-plain-list-disc li ul,.rst-content .section ul li ul,.rst-content .toctree-wrapper ul li ul,article ul li ul{margin-bottom:0}.wy-plain-list-disc li li,.rst-content .section ul li li,.rst-content .toctree-wrapper ul li li,article ul li li{list-style:circle}.wy-plain-list-disc li li li,.rst-content .section ul li li li,.rst-content .toctree-wrapper ul li li li,article ul li li li{list-style:square}.wy-plain-list-disc li ol li,.rst-content .section ul li ol li,.rst-content .toctree-wrapper ul li ol li,article ul li ol li{list-style:decimal}.wy-plain-list-decimal,.rst-content .section ol,.rst-content ol.arabic,article ol{list-style:decimal;line-height:24px;margin-bottom:24px}.wy-plain-list-decimal li,.rst-content .section ol li,.rst-content ol.arabic li,article ol li{list-style:decimal;margin-left:24px}.wy-plain-list-decimal li p:last-child,.rst-content .section ol li p:last-child,.rst-content ol.arabic li p:last-child,article ol li p:last-child{margin-bottom:0}.wy-plain-list-decimal li ul,.rst-content .section ol li ul,.rst-content ol.arabic li ul,article ol li ul{margin-bottom:0}.wy-plain-list-decimal li ul li,.rst-content .section ol li ul li,.rst-content ol.arabic li ul li,article ol li ul li{list-style:disc}.codeblock-example{border:1px solid #e1e4e5;border-bottom:none;padding:24px;padding-top:48px;font-weight:500;background:#fff;position:relative}.codeblock-example:after{content:"Example";position:absolute;top:0px;left:0px;background:#9B59B6;color:#fff;padding:6px 12px}.codeblock-example.prettyprint-example-only{border:1px solid #e1e4e5;margin-bottom:24px}.codeblock,pre.literal-block,.rst-content .literal-block,.rst-content pre.literal-block,div[class^='highlight']{border:1px solid #e1e4e5;padding:0px;overflow-x:auto;background:#fff;margin:1px 0 24px 0}.codeblock div[class^='highlight'],pre.literal-block div[class^='highlight'],.rst-content .literal-block div[class^='highlight'],div[class^='highlight'] div[class^='highlight']{border:none;background:none;margin:0}div[class^='highlight'] td.code{width:100%}.linenodiv pre{border-right:solid 1px #e6e9ea;margin:0;padding:12px 12px;font-family:Consolas,"Andale Mono WT","Andale Mono","Lucida Console","Lucida Sans Typewriter","DejaVu Sans Mono","Bitstream Vera Sans Mono","Liberation Mono","Nimbus Mono L",Monaco,"Courier New",Courier,monospace;font-size:12px;line-height:1.5;color:#d9d9d9}div[class^='highlight'] pre{white-space:pre;margin:0;padding:12px 12px;font-family:Consolas,"Andale Mono WT","Andale Mono","Lucida Console","Lucida Sans Typewriter","DejaVu Sans Mono","Bitstream Vera Sans Mono","Liberation Mono","Nimbus Mono L",Monaco,"Courier New",Courier,monospace;font-size:12px;line-height:1.5;display:block;overflow:auto;color:#404040}@media print{.codeblock,pre.literal-block,.rst-content .literal-block,.rst-content pre.literal-block,div[class^='highlight'],div[class^='highlight'] pre{white-space:pre-wrap}}.hll{background-color:#ffc;margin:0 -12px;padding:0 12px;display:block}.c{color:#998;font-style:italic}.err{color:#a61717;background-color:#e3d2d2}.k{font-weight:bold}.o{font-weight:bold}.cm{color:#998;font-style:italic}.cp{color:#999;font-weight:bold}.c1{color:#998;font-style:italic}.cs{color:#999;font-weight:bold;font-style:italic}.gd{color:#000;background-color:#fdd}.gd .x{color:#000;background-color:#faa}.ge{font-style:italic}.gr{color:#a00}.gh{color:#999}.gi{color:#000;background-color:#dfd}.gi .x{color:#000;background-color:#afa}.go{color:#888}.gp{color:#555}.gs{font-weight:bold}.gu{color:purple;font-weight:bold}.gt{color:#a00}.kc{font-weight:bold}.kd{font-weight:bold}.kn{font-weight:bold}.kp{font-weight:bold}.kr{font-weight:bold}.kt{color:#458;font-weight:bold}.m{color:#099}.s{color:#d14}.n{color:#333}.na{color:teal}.nb{color:#0086b3}.nc{color:#458;font-weight:bold}.no{color:teal}.ni{color:purple}.ne{color:#900;font-weight:bold}.nf{color:#900;font-weight:bold}.nn{color:#555}.nt{color:navy}.nv{color:teal}.ow{font-weight:bold}.w{color:#bbb}.mf{color:#099}.mh{color:#099}.mi{color:#099}.mo{color:#099}.sb{color:#d14}.sc{color:#d14}.sd{color:#d14}.s2{color:#d14}.se{color:#d14}.sh{color:#d14}.si{color:#d14}.sx{color:#d14}.sr{color:#009926}.s1{color:#d14}.ss{color:#990073}.bp{color:#999}.vc{color:teal}.vg{color:teal}.vi{color:teal}.il{color:#099}.gc{color:#999;background-color:#EAF2F5}.wy-breadcrumbs li{display:inline-block}.wy-breadcrumbs li.wy-breadcrumbs-aside{float:right}.wy-breadcrumbs li a{display:inline-block;padding:5px}.wy-breadcrumbs li a:first-child{padding-left:0}.wy-breadcrumbs-extra{margin-bottom:0;color:#b3b3b3;font-size:80%;display:inline-block}@media screen and (max-width: 480px){.wy-breadcrumbs-extra{display:none}.wy-breadcrumbs li.wy-breadcrumbs-aside{display:none}}@media print{.wy-breadcrumbs li.wy-breadcrumbs-aside{display:none}}.wy-affix{position:fixed;top:1.618em}.wy-menu a:hover{text-decoration:none}.wy-menu-horiz{*zoom:1}.wy-menu-horiz:before,.wy-menu-horiz:after{display:table;content:""}.wy-menu-horiz:after{clear:both}.wy-menu-horiz ul,.wy-menu-horiz li{display:inline-block}.wy-menu-horiz li:hover{background:rgba(255,255,255,0.1)}.wy-menu-horiz li.divide-left{border-left:solid 1px #404040}.wy-menu-horiz li.divide-right{border-right:solid 1px #404040}.wy-menu-horiz a{height:32px;display:inline-block;line-height:32px;padding:0 16px}.wy-menu-vertical header,.wy-menu-vertical p.caption{height:32px;display:inline-block;line-height:32px;padding:0 1.618em;margin-bottom:0;display:block;font-weight:bold;text-transform:uppercase;font-size:80%;color:#555;white-space:nowrap}.wy-menu-vertical ul{margin-bottom:0}.wy-menu-vertical li.divide-top{border-top:solid 1px #404040}.wy-menu-vertical li.divide-bottom{border-bottom:solid 1px #404040}.wy-menu-vertical li.current{background:#e3e3e3}.wy-menu-vertical li.current a{color:gray;border-right:solid 1px #c9c9c9;padding:0.4045em 2.427em}.wy-menu-vertical li.current a:hover{background:#d6d6d6}.wy-menu-vertical li code,.wy-menu-vertical li .rst-content tt,.rst-content .wy-menu-vertical li tt{border:none;background:inherit;color:inherit;padding-left:0;padding-right:0}.wy-menu-vertical li span.toctree-expand{display:block;float:left;margin-left:-1.2em;font-size:0.8em;line-height:1.6em;color:#4d4d4d}.wy-menu-vertical li.on a,.wy-menu-vertical li.current>a{color:#404040;padding:0.4045em 1.618em;font-weight:bold;position:relative;background:#fcfcfc;border:none;border-bottom:solid 1px #c9c9c9;border-top:solid 1px #c9c9c9;padding-left:1.618em -4px}.wy-menu-vertical li.on a:hover,.wy-menu-vertical li.current>a:hover{background:#fcfcfc}.wy-menu-vertical li.on a:hover span.toctree-expand,.wy-menu-vertical li.current>a:hover span.toctree-expand{color:gray}.wy-menu-vertical li.on a span.toctree-expand,.wy-menu-vertical li.current>a span.toctree-expand{display:block;font-size:0.8em;line-height:1.6em;color:#333}.wy-menu-vertical li.toctree-l1.current li.toctree-l2>ul,.wy-menu-vertical li.toctree-l2.current li.toctree-l3>ul{display:none}.wy-menu-vertical li.toctree-l1.current li.toctree-l2.current>ul,.wy-menu-vertical li.toctree-l2.current li.toctree-l3.current>ul{display:block}.wy-menu-vertical li.toctree-l2.current>a{background:#c9c9c9;padding:0.4045em 2.427em}.wy-menu-vertical li.toctree-l2.current li.toctree-l3>a{display:block;background:#c9c9c9;padding:0.4045em 4.045em}.wy-menu-vertical li.toctree-l2 a:hover span.toctree-expand{color:gray}.wy-menu-vertical li.toctree-l2 span.toctree-expand{color:#a3a3a3}.wy-menu-vertical li.toctree-l3{font-size:0.9em}.wy-menu-vertical li.toctree-l3.current>a{background:#bdbdbd;padding:0.4045em 4.045em}.wy-menu-vertical li.toctree-l3.current li.toctree-l4>a{display:block;background:#bdbdbd;padding:0.4045em 5.663em;border-top:none;border-bottom:none}.wy-menu-vertical li.toctree-l3 a:hover span.toctree-expand{color:gray}.wy-menu-vertical li.toctree-l3 span.toctree-expand{color:#969696}.wy-menu-vertical li.toctree-l4{font-size:0.9em}.wy-menu-vertical li.current ul{display:block}.wy-menu-vertical li ul{margin-bottom:0;display:none}.wy-menu-vertical .local-toc li ul{display:block}.wy-menu-vertical li ul li a{margin-bottom:0;color:#b3b3b3;font-weight:normal}.wy-menu-vertical a{display:inline-block;line-height:18px;padding:0.4045em 1.618em;display:block;position:relative;font-size:90%;color:#b3b3b3}.wy-menu-vertical a:hover{background-color:#4e4a4a;cursor:pointer}.wy-menu-vertical a:hover span.toctree-expand{color:#b3b3b3}.wy-menu-vertical a:active{background-color:#2980B9;cursor:pointer;color:#fff}.wy-menu-vertical a:active span.toctree-expand{color:#fff}.wy-side-nav-search{z-index:200;background-color:#2980B9;text-align:center;padding:0.809em;display:block;color:#fcfcfc;margin-bottom:0.809em}.wy-side-nav-search input[type=text]{width:100%;border-radius:50px;padding:6px 12px;border-color:#2472a4}.wy-side-nav-search img{display:block;margin:auto auto 0.809em auto;height:45px;width:45px;background-color:#2980B9;padding:5px;border-radius:100%}.wy-side-nav-search>a,.wy-side-nav-search .wy-dropdown>a{color:#fcfcfc;font-size:100%;font-weight:bold;display:inline-block;padding:4px 6px;margin-bottom:0.809em}.wy-side-nav-search>a:hover,.wy-side-nav-search .wy-dropdown>a:hover{background:rgba(255,255,255,0.1)}.wy-side-nav-search>a img.logo,.wy-side-nav-search .wy-dropdown>a img.logo{display:block;margin:0 auto;height:auto;width:auto;border-radius:0;max-width:100%;background:transparent}.wy-side-nav-search>a.icon img.logo,.wy-side-nav-search .wy-dropdown>a.icon img.logo{margin-top:0.85em}.wy-nav .wy-menu-vertical header{color:#2980B9}.wy-nav .wy-menu-vertical a{color:#b3b3b3}.wy-nav .wy-menu-vertical a:hover{background-color:#2980B9;color:#fff}[data-menu-wrap]{-webkit-transition:all 0.2s ease-in;-moz-transition:all 0.2s ease-in;transition:all 0.2s ease-in;position:absolute;opacity:1;width:100%;opacity:0}[data-menu-wrap].move-center{left:0;right:auto;opacity:1}[data-menu-wrap].move-left{right:auto;left:-100%;opacity:0}[data-menu-wrap].move-right{right:-100%;left:auto;opacity:0}.wy-body-for-nav{background:left repeat-y #fcfcfc;background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyRpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoTWFjaW50b3NoKSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDoxOERBMTRGRDBFMUUxMUUzODUwMkJCOThDMEVFNURFMCIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDoxOERBMTRGRTBFMUUxMUUzODUwMkJCOThDMEVFNURFMCI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOjE4REExNEZCMEUxRTExRTM4NTAyQkI5OEMwRUU1REUwIiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOjE4REExNEZDMEUxRTExRTM4NTAyQkI5OEMwRUU1REUwIi8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+EwrlwAAAAA5JREFUeNpiMDU0BAgwAAE2AJgB9BnaAAAAAElFTkSuQmCC);background-size:300px 1px}.wy-grid-for-nav{position:absolute;width:100%;height:100%}.wy-nav-side{position:fixed;top:0;bottom:0;left:0;padding-bottom:2em;width:300px;overflow-x:hidden;overflow-y:scroll;min-height:100%;background:#343131;z-index:200}.wy-nav-top{display:none;background:#2980B9;color:#fff;padding:0.4045em 0.809em;position:relative;line-height:50px;text-align:center;font-size:100%;*zoom:1}.wy-nav-top:before,.wy-nav-top:after{display:table;content:""}.wy-nav-top:after{clear:both}.wy-nav-top a{color:#fff;font-weight:bold}.wy-nav-top img{margin-right:12px;height:45px;width:45px;background-color:#2980B9;padding:5px;border-radius:100%}.wy-nav-top i{font-size:30px;float:left;cursor:pointer}.wy-nav-content-wrap{margin-left:300px;background:#fcfcfc;min-height:100%}.wy-nav-content{padding:1.618em 3.236em;height:100%;max-width:800px;margin:auto}.wy-body-mask{position:fixed;width:100%;height:100%;background:rgba(0,0,0,0.2);display:none;z-index:499}.wy-body-mask.on{display:block}footer{color:#999}footer p{margin-bottom:12px}.rst-footer-buttons{*zoom:1}.rst-footer-buttons:before,.rst-footer-buttons:after{display:table;content:""}.rst-footer-buttons:after{clear:both}#search-results .search li{margin-bottom:24px;border-bottom:solid 1px #e1e4e5;padding-bottom:24px}#search-results .search li:first-child{border-top:solid 1px #e1e4e5;padding-top:24px}#search-results .search li a{font-size:120%;margin-bottom:12px;display:inline-block}#search-results .context{color:gray;font-size:90%}@media screen and (max-width: 768px){.wy-body-for-nav{background:#fcfcfc}.wy-nav-top{display:block}.wy-nav-side{left:-300px}.wy-nav-side.shift{width:85%;left:0}.wy-nav-content-wrap{margin-left:0}.wy-nav-content-wrap .wy-nav-content{padding:1.618em}.wy-nav-content-wrap.shift{position:fixed;min-width:100%;left:85%;top:0;height:100%;overflow:hidden}}@media screen and (min-width: 1400px){.wy-nav-content-wrap{background:rgba(0,0,0,0.05)}.wy-nav-content{margin:0;background:#fcfcfc}}@media print{.rst-versions,footer,.wy-nav-side{display:none}.wy-nav-content-wrap{margin-left:0}}.rst-versions{position:fixed;bottom:0;left:0;width:300px;color:#fcfcfc;background:#1f1d1d;border-top:solid 10px #343131;font-family:"Lato","proxima-nova","Helvetica Neue",Arial,sans-serif;z-index:400}.rst-versions a{color:#2980B9;text-decoration:none}.rst-versions .rst-badge-small{display:none}.rst-versions .rst-current-version{padding:12px;background-color:#272525;display:block;text-align:right;font-size:90%;cursor:pointer;color:#27AE60;*zoom:1}.rst-versions .rst-current-version:before,.rst-versions .rst-current-version:after{display:table;content:""}.rst-versions .rst-current-version:after{clear:both}.rst-versions .rst-current-version .fa,.rst-versions .rst-current-version .wy-menu-vertical li span.toctree-expand,.wy-menu-vertical li .rst-versions .rst-current-version span.toctree-expand,.rst-versions .rst-current-version .rst-content .admonition-title,.rst-content .rst-versions .rst-current-version .admonition-title,.rst-versions .rst-current-version .rst-content h1 .headerlink,.rst-content h1 .rst-versions .rst-current-version .headerlink,.rst-versions .rst-current-version .rst-content h2 .headerlink,.rst-content h2 .rst-versions .rst-current-version .headerlink,.rst-versions .rst-current-version .rst-content p.caption .headerlink,.rst-content p.caption .rst-versions .rst-current-version .headerlink,.rst-versions .rst-current-version .rst-content h3 .headerlink,.rst-content h3 .rst-versions .rst-current-version .headerlink,.rst-versions .rst-current-version .rst-content h4 .headerlink,.rst-content h4 .rst-versions .rst-current-version .headerlink,.rst-versions .rst-current-version .rst-content h5 .headerlink,.rst-content h5 .rst-versions .rst-current-version .headerlink,.rst-versions .rst-current-version .rst-content h6 .headerlink,.rst-content h6 .rst-versions .rst-current-version .headerlink,.rst-versions .rst-current-version .rst-content dl dt .headerlink,.rst-content dl dt .rst-versions .rst-current-version .headerlink,.rst-versions .rst-current-version .rst-content tt.download span:first-child,.rst-content tt.download .rst-versions .rst-current-version span:first-child,.rst-versions .rst-current-version .rst-content code.download span:first-child,.rst-content code.download .rst-versions .rst-current-version span:first-child,.rst-versions .rst-current-version .icon{color:#fcfcfc}.rst-versions .rst-current-version .fa-book,.rst-versions .rst-current-version .icon-book{float:left}.rst-versions .rst-current-version .icon-book{float:left}.rst-versions .rst-current-version.rst-out-of-date{background-color:#E74C3C;color:#fff}.rst-versions .rst-current-version.rst-active-old-version{background-color:#F1C40F;color:#000}.rst-versions.shift-up .rst-other-versions{display:block}.rst-versions .rst-other-versions{font-size:90%;padding:12px;color:gray;display:none}.rst-versions .rst-other-versions hr{display:block;height:1px;border:0;margin:20px 0;padding:0;border-top:solid 1px #413d3d}.rst-versions .rst-other-versions dd{display:inline-block;margin:0}.rst-versions .rst-other-versions dd a{display:inline-block;padding:6px;color:#fcfcfc}.rst-versions.rst-badge{width:auto;bottom:20px;right:20px;left:auto;border:none;max-width:300px}.rst-versions.rst-badge .icon-book{float:none}.rst-versions.rst-badge .fa-book,.rst-versions.rst-badge .icon-book{float:none}.rst-versions.rst-badge.shift-up .rst-current-version{text-align:right}.rst-versions.rst-badge.shift-up .rst-current-version .fa-book,.rst-versions.rst-badge.shift-up .rst-current-version .icon-book{float:left}.rst-versions.rst-badge.shift-up .rst-current-version .icon-book{float:left}.rst-versions.rst-badge .rst-current-version{width:auto;height:30px;line-height:30px;padding:0 6px;display:block;text-align:center}@media screen and (max-width: 768px){.rst-versions{width:85%;display:none}.rst-versions.shift{display:block}img{width:100%;height:auto}}.rst-content img{max-width:100%;height:auto !important}.rst-content div.figure{margin-bottom:24px}.rst-content div.figure.align-center{text-align:center}.rst-content .section>img,.rst-content .section>a>img{margin-bottom:24px}.rst-content blockquote{margin-left:24px;line-height:24px;margin-bottom:24px}.rst-content .note .last,.rst-content .attention .last,.rst-content .caution .last,.rst-content .danger .last,.rst-content .error .last,.rst-content .hint .last,.rst-content .important .last,.rst-content .tip .last,.rst-content .warning .last,.rst-content .seealso .last,.rst-content .admonition-todo .last{margin-bottom:0}.rst-content .admonition-title:before{margin-right:4px}.rst-content .admonition table{border-color:rgba(0,0,0,0.1)}.rst-content .admonition table td,.rst-content .admonition table th{background:transparent !important;border-color:rgba(0,0,0,0.1) !important}.rst-content .section ol.loweralpha,.rst-content .section ol.loweralpha li{list-style:lower-alpha}.rst-content .section ol.upperalpha,.rst-content .section ol.upperalpha li{list-style:upper-alpha}.rst-content .section ol p,.rst-content .section ul p{margin-bottom:12px}.rst-content .line-block{margin-left:24px}.rst-content .topic-title{font-weight:bold;margin-bottom:12px}.rst-content .toc-backref{color:#404040}.rst-content .align-right{float:right;margin:0px 0px 24px 24px}.rst-content .align-left{float:left;margin:0px 24px 24px 0px}.rst-content .align-center{margin:auto;display:block}.rst-content h1 .headerlink,.rst-content h2 .headerlink,.rst-content p.caption .headerlink,.rst-content h3 .headerlink,.rst-content h4 .headerlink,.rst-content h5 .headerlink,.rst-content h6 .headerlink,.rst-content dl dt .headerlink,.rst-content p.caption .headerlink{display:none;visibility:hidden;font-size:14px}.rst-content h1 .headerlink:after,.rst-content h2 .headerlink:after,.rst-content p.caption .headerlink:after,.rst-content h3 .headerlink:after,.rst-content h4 .headerlink:after,.rst-content h5 .headerlink:after,.rst-content h6 .headerlink:after,.rst-content dl dt .headerlink:after,.rst-content p.caption .headerlink:after{visibility:visible;content:"";font-family:FontAwesome;display:inline-block}.rst-content h1:hover .headerlink,.rst-content h2:hover .headerlink,.rst-content p.caption:hover .headerlink,.rst-content h3:hover .headerlink,.rst-content h4:hover .headerlink,.rst-content h5:hover .headerlink,.rst-content h6:hover .headerlink,.rst-content dl dt:hover .headerlink,.rst-content p.caption:hover .headerlink{display:inline-block}.rst-content .sidebar{float:right;width:40%;display:block;margin:0 0 24px 24px;padding:24px;background:#f3f6f6;border:solid 1px #e1e4e5}.rst-content .sidebar p,.rst-content .sidebar ul,.rst-content .sidebar dl{font-size:90%}.rst-content .sidebar .last{margin-bottom:0}.rst-content .sidebar .sidebar-title{display:block;font-family:"Roboto Slab","ff-tisa-web-pro","Georgia",Arial,sans-serif;font-weight:bold;background:#e1e4e5;padding:6px 12px;margin:-24px;margin-bottom:24px;font-size:100%}.rst-content .highlighted{background:#F1C40F;display:inline-block;font-weight:bold;padding:0 6px}.rst-content .footnote-reference,.rst-content .citation-reference{vertical-align:super;font-size:90%}.rst-content table.docutils.citation,.rst-content table.docutils.footnote{background:none;border:none;color:#999}.rst-content table.docutils.citation td,.rst-content table.docutils.citation tr,.rst-content table.docutils.footnote td,.rst-content table.docutils.footnote tr{border:none;background-color:transparent !important;white-space:normal}.rst-content table.docutils.citation td.label,.rst-content table.docutils.footnote td.label{padding-left:0;padding-right:0;vertical-align:top}.rst-content table.field-list{border:none}.rst-content table.field-list td{border:none;padding-top:5px}.rst-content table.field-list td>strong{display:inline-block;margin-top:3px}.rst-content table.field-list .field-name{padding-right:10px;text-align:left;white-space:nowrap}.rst-content table.field-list .field-body{text-align:left;padding-left:0}.rst-content tt,.rst-content tt,.rst-content code{color:#000}.rst-content tt big,.rst-content tt em,.rst-content tt big,.rst-content code big,.rst-content tt em,.rst-content code em{font-size:100% !important;line-height:normal}.rst-content tt .xref,a .rst-content tt,.rst-content tt .xref,.rst-content code .xref,a .rst-content tt,a .rst-content code{font-weight:bold}.rst-content a tt,.rst-content a tt,.rst-content a code{color:#2980B9}.rst-content dl{margin-bottom:24px}.rst-content dl dt{font-weight:bold}.rst-content dl p,.rst-content dl table,.rst-content dl ul,.rst-content dl ol{margin-bottom:12px !important}.rst-content dl dd{margin:0 0 12px 24px}.rst-content dl:not(.docutils){margin-bottom:24px}.rst-content dl:not(.docutils) dt{display:inline-block;margin:6px 0;font-size:90%;line-height:normal;background:#e7f2fa;color:#2980B9;border-top:solid 3px #6ab0de;padding:6px;position:relative}.rst-content dl:not(.docutils) dt:before{color:#6ab0de}.rst-content dl:not(.docutils) dt .headerlink{color:#404040;font-size:100% !important}.rst-content dl:not(.docutils) dl dt{margin-bottom:6px;border:none;border-left:solid 3px #ccc;background:#f0f0f0;color:gray}.rst-content dl:not(.docutils) dl dt .headerlink{color:#404040;font-size:100% !important}.rst-content dl:not(.docutils) dt:first-child{margin-top:0}.rst-content dl:not(.docutils) tt,.rst-content dl:not(.docutils) tt,.rst-content dl:not(.docutils) code{font-weight:bold}.rst-content dl:not(.docutils) tt.descname,.rst-content dl:not(.docutils) tt.descclassname,.rst-content dl:not(.docutils) tt.descname,.rst-content dl:not(.docutils) code.descname,.rst-content dl:not(.docutils) tt.descclassname,.rst-content dl:not(.docutils) code.descclassname{background-color:transparent;border:none;padding:0;font-size:100% !important}.rst-content dl:not(.docutils) tt.descname,.rst-content dl:not(.docutils) tt.descname,.rst-content dl:not(.docutils) code.descname{font-weight:bold}.rst-content dl:not(.docutils) .optional{display:inline-block;padding:0 4px;color:#000;font-weight:bold}.rst-content dl:not(.docutils) .property{display:inline-block;padding-right:8px}.rst-content .viewcode-link,.rst-content .viewcode-back{display:inline-block;color:#27AE60;font-size:80%;padding-left:24px}.rst-content .viewcode-back{display:block;float:right}.rst-content p.rubric{margin-bottom:12px;font-weight:bold}.rst-content tt.download,.rst-content code.download{background:inherit;padding:inherit;font-family:inherit;font-size:inherit;color:inherit;border:inherit;white-space:inherit}.rst-content tt.download span:first-child:before,.rst-content code.download span:first-child:before{margin-right:4px}@media screen and (max-width: 480px){.rst-content .sidebar{width:100%}}span[id*='MathJax-Span']{color:#404040}.math{text-align:center}@font-face{font-family:"Inconsolata";font-style:normal;font-weight:400;src:local("Inconsolata"),url(../fonts/Inconsolata.ttf) format("truetype")}@font-face{font-family:"Inconsolata";font-style:normal;font-weight:700;src:local("Inconsolata Bold"),local("Inconsolata-Bold"),url(../fonts/Inconsolata-Bold.ttf) format("truetype")}@font-face{font-family:"Lato";font-style:normal;font-weight:400;src:local("Lato Regular"),local("Lato-Regular"),url(../fonts/Lato-Regular.ttf) format("truetype")}@font-face{font-family:"Lato";font-style:normal;font-weight:700;src:local("Lato Bold"),local("Lato-Bold"),url(../fonts/Lato-Bold.ttf) format("truetype")}@font-face{font-family:"Roboto Slab";font-style:normal;font-weight:400;src:local("Roboto Slab Regular"),local("RobotoSlab-Regular"),url(../fonts/RobotoSlab-Regular.ttf) format("truetype")}@font-face{font-family:"Roboto Slab";font-style:normal;font-weight:700;src:local("Roboto Slab Bold"),local("RobotoSlab-Bold"),url(../fonts/RobotoSlab-Bold.ttf) format("truetype")} +/*# sourceMappingURL=theme.css.map */ diff --git a/doc/_themes/sphinx_rtd_theme/static/fonts/FontAwesome.otf b/doc/_themes/sphinx_rtd_theme/static/fonts/FontAwesome.otf new file mode 100644 index 0000000000000000000000000000000000000000..8b0f54e47e1d356dcf1496942a50e228e0f1ee14 GIT binary patch literal 62856 zcmcfp2Y3_5)&LBzEbU6(wGF`%u_do$I-wUs=poc3^xzP>t859|l91%ydy%{4ZewH9 zLNU#OK%5)jlp7M#adH#VlN(Y~MSVYG)7F`Dsts8mQIv>+ztD)dFw+9OVG%`1 zdML`ns?&x=Qnp|IfM+dm&(}ePcdqmf37+Ghm#p%f+FVKQ2*chjkzF#ZB~9w-bef!xGBr6D7h{6UGOP@t%*!8rhr zqTX&D_txFJckW8F88SgJDOYWQiq1}9HpST zU`<34PZ)C!_3}_&M2)6kC53tq%16Wv<;B!kk^fL$a$g&o8ZTNrRL|U3FQqy}Aw%^t z%FjbIl=r0M9>Z`rYKq77t>{++@-k0@oM~*1+}p2(7`Q4V*n=HYq=vsI?g5v}-nP z3|{}}ibb1(*R0;YdDD}@+q7nj-e?F6nlWp}oWMD=X3yOms||yGW^I(#9B4HL0`>*2 zG{Pq6qjlCmi#Eba+D94TAv}p9V_D5%k=nR0b4*~E)oRv<#|upiMk~z0GGmR=Yz-V5 ze^pq5HgIj2Au?HKwVD>qoJsnJx#u=RZ=|+Tk5lVmJ2z1#N=q3aw}vu8YK7c-N>4=y zwHEjdq-Iky;2wVdD3u7c7HAy@>636rQ}I+R6-Jq%%_eFi6$}s_rB+ajpcD*stEugP zo136*FtrWZo1wQ}7%h+r0@$R$MYWppE&yKBVk^ODoieQIXI-PMCWPv3^jr9p7*cDDu9q6%xx{?3;;b@n3omixrmwx*YNmZf9p3xm@i;8 zp?TpJjUB@J0D^@;Vq@WEgcj}}s2gf=U*-SLs=qz||El20$!O-RlsfnS_J9)6lK^rf z@F|+|fem;DctSVzuQ6lCs>g=*`}C{(m-TP#-`gM6ukSbXXY`l%AL#GuKiB_u|L6U` z^xwJVb4z_|(yht2X53nKYvZlGw+y#3Zk69U@CS95u-8E9*x%q${UiIw^e^w<+#lK> z-M_Ej)SuN~+27uOroXrU-Tp88`)^UVM&1epcn{s0b!+*p&9_2tnQmp>swD94ennAt zcir7`_tDR9d~W}I%Sf-0+(^%nvXRn}u#+RjBRxinMp7g0j<_@8_K4p{{5Im&i2f13 zj`+pr(-A+9_-Vw=5kHRjVZ`?%z8i6aJ1^|@`u}w?=l`!y{JYkcahKF7zYy(4XAHaLAh7>kswf;WDJ8 zodnW*&mk}LA4ATyzs;HS z&jMIk)X1SUY8WQ8mk8qz!5gX{ac?|#KNXah-`{R{t;jx;+arrw4mTM?C=b`)g9B|K zKbe$=Z!xqbc>xxr!#G3cIJ_43-sk>0XiMsaXE3e+56S@N-W&nebhy1GS=0t{!`!CB zeXl$`20SDCO)=z#yl@A)%foXM<_FJ&aY(!S?qN9ajLc&>wDpF%>BD`=97%ujZX|^{ zkUJb;(Bvllh3Ak$Tkm1o9O@S+z@h#=rtsbrEayd0}DguL&kx00m+ja=Bpt$)C)Jj(+GE#@N5{qN_YooPx`~Xe7HP3 z{%{$_+eqqQIN>I3Ngv^P)=&zdhx-v8M)G7X!|w&{r;s|*7v>g7Gy(!cXqP3lRov@8 zR1fWh=MwT9Zqok0{>Y@@?`{gwSN{7?L`gvE7m2*?lX6LUm1893w2Pdz9?n{^!(W2e zdWpaFl9b@u0BLprBcj#q)KgjW@7iqlGG5Yvz*k2E1b+8G7f(?i1&vA9XxDLyUk5nmBs6~80?xA;He-^DJ8RN^C1NybWMO6ExxOV&s>OP-SKlxQUu zNxCEtRJdwMgQQb(MDmQ}tmIiqujCEMHOY0!HkBMipnS7>{u``WKCv$?i#JtM9$^4u7g87d5nYqQ>kup*r>4Q>U zI$1hRI!8KRx>mYFs*@&5bEW0dI%&J~sPvTdy!1usRp|%PFQwl}f0q6xb;-PBD%k|t zY}tI-V%aj;YS{+aQ?dwIjLaxYk`>BoWsR~9*)iEk*+tn)va7OpWS_{smHjSrdP+V0 zJk_4#J?D9@_1xwe?HTK7@=Wl|@+|Uf_B`o%#`BWri=J_T=4`v|*&UBhl-L)Zv5p0%+J>@(~s_AL7X`wDx7eUJT&{SSMK z9pETV%t<)~r{X4Z^SBk<7A}m7;^H_fm&|2x`CJ88%QbUt++pq*cal5LUErSMUf^El zUgJLCKIVSme)FQdBwi!E`Us0Q z%p9T98WOazMw1pS4`!>y8fGSUh&Ik-O^&x{%~AT;IIAusHq0EYwdzPtZ?PI<%-T3( zf;Poyj0@2lgv1zcHAY2Q^wEZ}*a%}ZXpR=04ir-WpbZI&wOaLYTC*`MGSZl6h=r8Y z4d>%cq(*NDHzt{4!;(WH^yY|Ityyc*hFL*fHES(8GA!v5YmA7AiVce8e_;!6kC&7Z?Hyy8O0n%G}drq zY^2^A7ORi2YLl!XIxW$Sg>0fe(yD_8(T0#%Z4_w&Inczd&{N0@YP37MFWzF+MkX06M(8q>71~9GMQF*2ge2%AwMG*R7f)W-5CO{_W(pxQ1Gtd{5P-01VNw=dm{|+^ z6%j+0-eT37Lc+r$ViLp5kx^l=IKzeEl&qvF4E7NA%LH2ey@o@10m4vTyAQN~fSq7A zx?gWNFHF`H8*d3AI~%7r4CUPWFH{<1gk*m_30u(tfF`iWB#nqQTC}hv2E8F#m?SuDFTQn3UEkkc8@TWC!-F{GC^ww z>q*$~q;*EKK82V{VgW}(B4CfL)4q56 z4)D)xH0hF~^)O1fFcUYy3iJruY7hufKutIFVd8R^gr`Ecp*I_TDL24)U$r5ORbRg-pCjNXR?8@hRjlg!)^B z(D!dOu%iM74)q`)qGOHW+C($Zqs|&;iLn3^gGC89>$Oo4U_&EF=f-R>g=zQ41JxU% z^ai~(IaX`22o=$0BPn|0z*CK8 zK%DqkW2^;?Z85-a0Z6ni9$1JOKmq#-j|FR7G;j-Zd_)ZF6-)}K?p{V%Lg*B4TBUeba0p4h(`{lkhnUa;!S@mlEwb3uRAAna%X|R34lqnNUbFX_%$pF{0bXxjWdRmGt^CFZcG*MWq&*% zpD-JDPJjsSWiSA$4WFQ~!(L z(g@%$q;&`!M=`(;0H;FcJiPEeUTy)bGXu%#O;$^MxH}UvXTe-kd`b#g8@(3xP*30x znc%M+5eqCjy*4&-n6xnX2oC%!5s^Uj?t@SuO@S=#uW(bx z{WX6b2|^FDjXG;w?7RqzWiB8Wa4|QJBTGftngtFZz*C@qy(Q$Y1K?iO@DUL*ch+1% z9wK1j&>$1McLEb&Zk8+5#cF{jf&aTxfx3yPAYib-S%s<1oju2WfRYkWB~Tuak9)I+ z(-1(skh!xT*2bHo!{JN-dNJ<8yjM5m zG60rH7zk-~uZGNixK`kLe=CruA#>*j!96b-j;Z)?t?(j4`6Spia^GJE{4Ojx680Zt zNWe8%t069;H$XAk92OS^LR}2VREDV856=$Q!%mO|6<}C_6UCa{zd}W<5upDiblg`Y z4Cvl7f*bc0-6U;-JxByu&zNWdaxxqBk$}(fNs-__0UlzBNj3priZ@%}*dQl4?7A@u zxFO-}z(C>X2fTOs4u7+;J0*%HiJsMQxqoBiu59bC{I)* zIwpEv)GK;ZbY1kl=qJ%1q5%)ugY$R_l;6D`VIDej?~k_t(Uq#ab(*CcOB-jjSFxlRYtLG(g8nl{qO zbOHT5{ZCLqIVOM^&rD@zGV_^TOav3dn3%)Nr_5K(_smbsZ;XR+Nxh{3(y`L%(je&q z=^E)esaBdKO_%0LE2WLn1JX|EJJNqkKa+kfy&=6R{Z;m$EI>A1Hd!`RHd8iFwn+Af zOe@pN;$&u7o$Qe8lVqKiD_fkJ-=Jui1W386V`Pb1S)E zZZ{Xs={O@7&!utMTpf3Udy%`wead~q-Q@bYKfGjKDz6z{L0&7o9`}0EYlm03m(I)J zmEe`?mG4#O)#laVb=0fN>w?#dUN3vS=Jl4>2VS3feeLyw*Uw(Rc{#l9deh#V_egJz z_ayH*-iy4Kd2jIE?ESR2*4ylzxhxHlZ~0u+4bSNe2Avwqk&^$DHRv=KS#CD3;S~8SQm|;x zN%uXOg<%H!6sOWpT07MECb~&~iaal%Kr~kA@W=0ly z{t+$Uxdi~XHN7!e%}J9R(_7UXGlAu{@LgPTdU`T9mC4D=%h61g=2Yj|)i)V?b+ui? zE#uW(1@DS-MfI`{o?I@T&abi;)~M_?7x@=n*uipt?Z;r>c-GlBp66Pcnp(J_b~W~k zJU4;W8IE;z9Xr-_5FpZ3`8gH2s@$By{Co|!66RIRN3*C1^>ST?V>+@U!LTF2up`?- zL$|?lw4^nqr~{nKnUu7&6b%lRrZlCsr~{Z@h76@~^htykcl!R`V4$yrCB3Hbq$wn746_@NOa-3Klzp2l^gn2VQjbAuo0?#JQLL z$Mz}bSE*b<%<3&$R%={A(pBfD{9}jO88R43TRRf@j!umu(~;H5a&uR%M853YmDj$} zIQyjET)Xy-no~>!4446Ue9XYDW$(ym^9NXsBiI!j&bBmH*VjYd5uCtsQXS7>`8HO> zDbN}`0?ouLy46Rz8=vn%p8Uqm@ezB}D0m6pght^=)w6thX?kgz2G3qG5zoOZl-P#$ z;62Eu9_V9|U>i5{jy^LBsJUYYou6NrldH_F$f?R#6Z}L^@PMpQjwrgSs={8Q zoOChE&E(fDVqJZ+_^S(9K%?|z4Qv@&$Gd6owP0l%>_y%&IxVx)7#jOLcGPC4#d!g42=Yrv!#JYwQRKph}ax;`_tIz`20);H(1 zsJH++i<8d1wvyoE7px2R-tQK>V~5{WU|KHT4=~~?>;J-zTfD!37u?D8Q>s%Z8#$yy z%h5wD_x>xdywB+ughWP$WMyPzRwT*3=TpiXGn-0FZKbMbDvnhisqR1g!-dcPCCh&K zU-?&5z+T@$$>=nPF5$IkC4LdF#0#)`=@RwFOYj1u#w%4&w-#zI;XGu*dusADPKoOm z8YZ0Itm0}4+W;2`1!=edNfwuq23(9Y^AiBwidZ$*g5O$1LZ$6+E(!Uc|#A>nDKry|{>zcC#+K%kF13+aeB` z9VD9p6UpVd$^V7B9CH{zE9`mIIchS3J(9JvNG|5m;2dy7E#^4~49g)Y8pA2@Lg!dK zg2BOf!)Nnef3=~Zrna)izq+0-OJ%Z4GBT8|Rd_LG9C|4SxZ~=3jfW$p9$pYw$y_dg z$>JhlV>uJMiW^X%#R@E9a470Q>roqx9zaWQErSDbk~yp(uQ0DT&%cNvuP5iE^LQ+u z26PNWna=x2;dpDwYtF2PX<;eXb5R_ zZZpZ*jjdH0&h{xRQ82^3_v)+fai0dznTkb#fpNA>TZj!$wMBp(y(a5G+OcF=O-IX7 zI1yn7^P5|gEmh6+^=fi-zRxzcYPfTi=c-TFqDL>HS)ZW?kxW)_xu>W{<;ZnRKUuRK|0& z{yIfL1XJ`OLv>qeQ+d6Ac^h59pu}O!d{)1 zv*gVuu9H;FWrMuddxQ0v#UA3Pz#$I+SM%g3Mhc$GgAw6?7&+-zJQ9zbG>QEFIth(L zBY*uBja2)zlewX3ESktVZS|5(mkM&oHz$Xv$b>E&ZkH^c3ZkKeyP{@`J>81Zl|K725KKL~og7cTUw&+r2C zUk9>oB)d(Z#5JNP*mUmDq4TywX6_8%+DKj@yYsN}P;F;x zs~Sy06X}*#uDQ7i4t1y4@e^&gBNN(#@|4_eym;lN^{dj7Q_?EUGMmj-qU3N8NR(vr zL5@U0AW!DyaDfW~n7L>qoU7ycb%~=uC}_($bO;~RAg|+gl_}Tm%SPM9pFM`C+p(U`f$Ogj39`p#D49F9Oe2B)Y(1=eW zw)bneg>cL|gV(T-@p*5{tE=Jcu_#{Qxp*GXIvt3kkYHpQ3rMZzl>31_u>s6-4t1k$ z+%4rq9}T342VUdi$!t^dQ!_JRmu7%?geCz#$k7y78#|!3og3_v;<;Rny}YW5!%{qk zYr=}g#4>emYj$g9vy8LVs?h8`L_|TiBLNz~6T}mIn`7Q#x%%eXmYM^ywlbt>Y*KQW ztPgGNM5|#@Lho##(bo(L9oRr~qe#cANDc%f=kjIw`MHHTDlBJG(mA{ekB4g&=UR+@ z#y>k2b08anAWukZCeRZa(ch0ofCOX(Es0wN+K`%qt+#QuZ7_-y0m}#2?n`dsD*wD% zU9TxGD=jNm!ZzETgs?z(%&2dH6S29assTs?*$2o*DW}7G$(=zkCn=n0K=g91j%PTP zO^O&KdH%vD8V)3XPz7L>;2B8w07~qv;%G|;IoyGV`0yOvTG|Z!pBsQ#a448*<@V{7 zdf2gEhBIedl9SbV5}wF0Z(rH8R)gfF3J%|GPxzE<#INuQA;=Fuj>54gr^1)E;a_nA zo)4mW8(@oc8NVA2@UCNk;D%})%w{#z2H@ok=K_g?v+@cKVge`%egi3pAfR$7s)V8% zDeAC@I!=iS?|Kv_iSmi9WFEB;;){P5Rf%dKM4(>OC~6j+5}g+P=`qz~g~xw9Zi~l? z6U67mcO<+dT5?YEC%uhsrC(z|gAE zO*vJ0Soy8esY(oZgqQLER6n4etX{4*s1K;GsNYi~jhAMuW{;*_b1QI4;QGKH$2>CT zA7i<(=f?Sr+dQskyn1}e_?r{PPpF*GHsRt#zlr~zR50n=$@LGNnX+igA5%|F+cqs@ z+S}6~n7(}aZ!^p@%4hsObLz||W*(ijYF6oN$QX$5KDr7zAHmywn^DlpJ_O|_m=Lh-A{Et-MyoGSNERokiok) zBnhB3NFqWKByj{Ii5OXtL=iv-I)VcRzH|jku>?yL&Y*4VU{JsS#rOmaeBcup%p(vg z?BW3W4M&OsA3!q@+*i8Vuj{V(uR|WXD@)op>iqEmJe@|bq0uaUO$x21Z|quaWJ_xUXAmZ_~hhx4bGFsw0wse^@d)0B zL-DjAP%gua%Yc&7*ptG~HMb>n%yYV^Ir+quNu8Y~X zOsAO}fxX6IZ{=QTe4}1~-O+ORpvERWcIMrGol^hUixhq6Nu^Kwy$j!Uz@hXT4-9Ss z-^eat$rCh}7lHN*%g%HL&}$Su8|+c)fPpL~YD3OWLx-U)QRDO)^r8pth-2Z11unc6 zgng%-ae6tu=(e_wW5-~S1W_f(E39}MY+<0HH}t}`?3|LK9Q9xyw$l+A#;7pmon0@m z&K*)1ESq+ndV%!`g!5xSUcduLyEub)22bZfY4K@?Qx%R1r~Nu#$Db%*0|u7If<;f- zZs~|Wl!(S*4>TT2kOs?S>p%Q{+3%`Sh&B5C`;XrEP=ho`23o%ajYA%X+By!lcghCs z(t*>G`3tf5iS25v9E+7>u>TlY=(eddSF1{x5@z+(?=Ec9VE;d`68_zm&3^yMUl5~Q z0Git}{%n4T8P1e5L>?Gep2ptkLk#cJzMcm|(|{by6<_nIywA5V(E)G8Gcom+3bm`G z563%p(Fbx;4q8>~c*j#Xi_WWWENE06tM5GgA^R;KAldIYrnu%>=<-IpTt0YLpJO5Z z7ka_5=ykNkF$!&QjdCo4<9+{Y{}-4YM?Pfn-Sr?2iLE?(P=OM*pd0w2DX66fl@N?-1iD^%I(}!F>Y{#DE3uA#DGd2hEe5<#MzbG*8eJ9rAVS*a7>X z{S`8p!61R*K0CV=3?EN|rl+Y>-AblM$u#nWsCFL|0B zfQG|)pZ4~I6JVA_-Cz?4mQ3W`hJitlTLhF*gLObK6@qDS+lA0x(4E2J0agpr&cu^; zCO{MD_+OBcSu~yntMX9y*I=$xBgAa|S3PuJ@wbLP?TrDFLn7oI!1w?W6b|fFfXJWR zs>T5*;3zvdesBW5jGjNr;s6}*4v+5OI|y>`@(7+gbxs`u84}+uPY@vw00iu76xufo z;xcky3)%Z&;>+Yhm+!$8%J?!scS9CB;mhtZ2z){+m9XdqJo!a-xeFw$i9EJ~O~`HB z##U^V3ifpbIY!5;!OjkR*D9R>68VYgd@_*MUtkE$$-fkUxcc07c}E{~7;XvDpX)Cb|1|XFuvZq>JsB#)PveQe{;jxBiN^8{5K0jUrRqVzDg~18#Ciz@>FQUv zymy! z&*Od810Fl&u{>a&NYRqnoKmjF>yBohOh1`&!vECeGZ#-?l2ulhSKE~}#We+0>ac&U zetlbytST=DEOI$HMPT2?V*?FMarLpa{zkN(ZYfS}NLFDp%px@Hdbg?*+HWKXULd8 zkEK16c|6zUdZ=x9l%!V#N--vs)1Y?7`7@ zUn0ko6}wEv0^s#bf$8Y;nt{g#G6c;O9Rxkp~37xp$cQT7Cj!TNVhT`^& zI&4Hw_&KKS_Q{rzgsVT3nbUxjS!=s=ByFFeTQM)>Kqhz5aopk1G=ntHm(bZMG8dQ$BhNn1}_Fh1}7Nti)0c zsT@ogRyZ#PtP12$h;{@IwrJG15JZTZim@zu2-s#H3a(^DF9b*f!~-`SXB4TWX_;v% zT*RcM)i;-FDx{sz1Pp>3(E_#;_tAw?r_B|uIG=Ss?X=o8Z{QexDBE<7`o%{7?Ua9oUL)qyK{_Ai_VIOP#S7N&Z?ckpe>SiZNU9u zm_q=i4bJZ5(sVGj!PB!f7mo=XL{82L5inMgk&7V{T*SK~8Nwgw=%`(Z+g00lwVjUA zU=<3WUD{k?Dq6tekKu^y$hJ1`S7AGt=)v}92iHh2woB0rmiQX{&w_)RM|6e?WpRxG1qwgX1Z!msyPF7Ub7d7P6Vlc}3fyKQX z{8za}`FR?A4PT@4^9plwl!99goGkcu9*=ILU}-~rO?{;X|K@0ah;2_8fQ@>SAE*Hu zm0Ehb1*Q3A1^#G9oZ@s=Z~7@U&T;h6C(|Pi z>r_B2x`_Sz(lt28)kCN2v$jPmT?xPQJ9rqtDh3Y{nDII?+Y{^5u5Q$qRByH=X89*( zW+qsbz#re{>&mNY!JH4q<+i%|_71QcjvmY20Be`s_Y9ba=Ca)^9*q@#$RFGQTd(6C zD%WBR767mVjOD@V9ovsqp^2K>2HSzmI?N+AtVd2c@Vk*_I(IXT8ZbX?y>VB zUjx`hNA3vvLF4-_R%7+suyd>U8$5c5_dOFpf9J3&TGE@)C^juSC%r(E5|OF3M9T2A z8F=ALyha5M-v?g!X1a!$w-VTSu>AxDq`vRwfu|HHXh4~0-SQeQgF!}1ZYz~VPn9c zflBaRv=`n3Qn*Usc#Ek45eF0^LSR7lb6Mh?HnDpSg`cyk1F(JR%Ob?7Vgyf{qpy_(zgvuS>Vj=cLo{pa z>7>`QufDBBFQFGv3;F@B7jX-I>9Oo}NgLE_GwF{*7W7V4osfp`C!~n`D{ zw)N2Ge`)&ziIhHfGEX#uH_&MpKf(LB?vesIuAl_mzgzL^#-FF3QCH;Vl;)~*24l45 z5hQEJ5XpdL?T;vL1Qt`RP}9%>a6BA^|X!|NjdB_-jxI_CZ_l=Idxa zYiv&H$kZH3Ka|;-Ec<2Ut6=@}QDUDhSUP#7+LCO}G^NX|nW;%eh5%56KxP0ZU4iv*KA7w1xTwa7;q_g#*D8$PI$hF$~8E;@fbZi2er?M%mste&UVe zXw>l^U;pv=3AlcEd7Zho235`~JX|gRb zKMD8VG5SSkg(gI)?#yI@*VMn7sL4H8YOkr6)!UoP8&pmwgM1I4LNhLF(2)Uk4S`SY@Fxs`Oc(;0h69>rvKnWwBS-<;xgEr(x6DibxmxA2GpmIW%yoQloTB&TirQB-&)3iy;JKCM^{C2fZQ!-8vmGcos@_>` zs?06jUahZ9ZjxoybQv>rMOIl>wlW*yIdawc z1=gI%9Q>fsugF}o-=uuC4DGI?OOHNR`nu}nH;VJ$(-gdSwdhq6NdZ#d`u?6~~Z{9B`t z1-wD7iVv{1TrJ$)^S%f-D(W5jPFReasvb;xyJU+{ge@XLF!sW1Y>t#pxHf&n1 zT#>nH|1Pz8XL!_BlgzYrRr(xN=QBka^;w~<(os*A)DqVV3{f`x~wu*<2rlCTY(;`{I>jL zIg(cYQuReK+EM8DP0?Fb7i+$1ey6Rcv#0a&>5I>wJl%P&@mbk{muvs|59Qaf*EhbW z_U+#I{v1%Pj(mLjABWnTWxgjboH*Xqepc3gw(i1Z<%PWN^t0;pv+-Sq_cH?QCUG% zdPQ{U<|=F`!^+a9%Ut<>^NXIy4^bDT=A~pM$7FvlUt%w-s(;S!0?Is#=3GHno8CWo>lpI)FKe$jT79zST+OkX zwj*_?YR}i6x1XsyQCHPo(E_mQ%IeFS(o1y3!G*H?$*YP&RM{3=S)>NP*O)ZkUffX9 zT;l&u;qy61(`3n|nI*aE+#T^)mAc-5XO|S1md4@P{+a8x;&v0(YMUovWmkUrJ&Pu zXoQi+mlzyVO8Y8*2502splvA@57<9pE;b(RGHHC@z@yN7Q&))11UB+fcs{K&H5xCf zKDlFG%!H&Hbw@N1lr{f|?xO7oSi+$#0O~rDel$eo146*S?V*`hq6(0H%NP%`pACJIXr6*_&%wUIKAOx$>g;p&(WnhH6fYKMq71sza*elGHFyzT zNPIVF5n6Pb9n8$&3wSgMoXv3B$C6Mh1fewGk~#e>zp;A#;b65xG}uIkv|TbiuX_H{ zk&Epb2jy&{55H9X#uX)4CZOX@#Zq2#rw<$&plbvIOi;aXCP=0bJUn3c-RxUQ+%1X* z{>fL~SNpafs_Cq6Q#Z8rzSI7;tgaj)tW-6%1zF{q_Q!hHHYCdG6KgDHrSE2tnfv2@ z*#3!n`zLrG>Rg06WEV2S+hbHQ5ecCgnnkz+d`6wy7t4G@cPx&bJ`uY72A&*2kiR() z6bXoV6U+i~@qib)t=M{V>dOo`ML-S4(`fXOqhDdqDM`!8!N1|({Bm;AN^(==Jist4j@u&|VHkfH@Du$@Qy2AQ$ zyS=B!4Apu-Qm z??=AR!Q1>cw5nx=g{6hW@|2gSS+|amKUv#qsXH{+_oKfB=iXcIlJfGBa)=elxEVFOi~iUHd&I=pcASXucdT%& zI1%%L?ZgRx=S$9)Xz&P5Vg--jbHH8UD3D7bnD#I%oeT0z8Q3~q@{90U0|W>Iq7TOh z1NXBNgAP&M96-(t7<7ax5CV`lsF`;0Kr{)mF%V-31dg>2)dn!v5Y0Px-e3)^bLR_u zAk-tD0EPi=Wb4oq5)tMOdh~ZfmOf-|vv(;;YY^!I0+^8?SJRo`dC@ukP#kZu9gS@X z7R zCS-&8Ac`H_`5nyExf3wSe-KjId?+zTryShb!;;qltDAkOl@Z$Z084;cCoF^bIV@Ee zi3{;N-Umb2864mq;zq|m6=t(Nu}cM>#x8r?A+v@+MLw**Gn*WdKniw(tq8euTdsi8Zq0W~rrMOat z%m0Qa9T0xxB&|C-8&94BV}cy@fj6lSv`8TpH^P5~fbH1MJPwr1O5YI>fq5L>0N%zO zpw)L380LDgt&xsGhe10dgc}3xt5^u(a<_ofE8Q_ik&>4J5mvKj)0vr&g(IvQf*&EM z=Wz@dRD$rSN=YG=v%iJN&b$_g?5u8v$WA1*LC~f?kA!H=1=V$Z2@4m*i z!)jf11|vI|n8CTKI0gr=6lqxSh(fRxsD;zUZFwYAz1w8iX;p%+pFb`A>8H=%KcT*I z^vK~Cl@~X6uZ!LX%cM?9PfXsuNtT-rdYCFNudJd#gZ+NZs4Z-@H~OP-Um>6O(8DSS zoDRl3UI$DI2g5tT@K!iGt*{MN6a;gygZes?bp@Y!A_yRcap%RV1Aj6_&7Kx;2d?wJhEtaB~olpbt#z|334}xAjCm}zo^*y)xKLutVI8W?{JDyFB1Q@ zZ_8I|ht9Q2;aCbEKK)ESZ-CDnes(Q&ErZV-ejfVF;b+G(wNC)OE>Uz9__G-Nz3=RO zZ6z2L7<36;qB{jz2UcO}R4@MkgsPa&d5c9es2Nn#RuU84VO2XdgMo>XE1Z^x!2y&xJLkH-3zbN3m%kH8KljihAJNb-ug>0nsnuBd*6X?d6;)zd+r*T zW2CS(mmnq)+H`6@{E%?I6J&tp0rb`DATh%L%b^w|O)E&6u#ND-5T68qh?oB|I~X|p z2@cFJ@H7ifZHSfthPe--wSjaqP6Yd#K)hyrfmUFjYbnTCJU^_5+x3N53hR# z%hh$(x|pT}S$1`GUZbk5zWG3NVQWdVrl`BPyIbklk4}H?SP7qr0PoF%gUtaaGMsqM zLWgx1?>y+dy%z!%qyh8|Q3L#d1ncPA3r`1b?*eB7@SU5^Ai{UTK*kTiV-(5hX({SM zd~#Y-s|GzOZEb1-=Sncs(wLU4DMm9C=_P4d;9uOpB&F3gYEqmc8a&F?73#_=d%0bO zOpM)LR8XaQxY8$jL6_Ykc&_$lHY{ri9Qr?lgOz-=rM)PkfMXZbcU8L&C61U zPD*?Y2U(X+x>f4h?fglZc;v8 z4XQz@C<#qQf2!cj1MkmH#g|cl&Gf^j-P?oJ;GFSuJ$4<3t(D<3({U9}#P2J0<+>`p zx+3xLwwx_^=b~}Sgz9{Iih9qH1F>&>{Td2=L3RG-`qbw&u{VB6y{SUe(A4wqAe9D; z`f9Wr?Y)Yw${Ma#zj>8d_#v(fJp@s(pg{&fWG{s1xT8FPC^iG04cu0s8#oI-dO3!C z)ukmxrS$QQT{BkW8dtF1<*URuP!?W^j$vPQNohq19dkwZ{d=g!5q!$w3*la{n*$Ow zUgQWyI(rdKs&+03P}IdMxon^wJ+EegJG^7B0Xxyc%CLKZ^bQ;6Uhr6Dl5U z*PMIqT+i`;$Qlk-w;v`8L*z602~b(lJVNvDvqSXW2=x9Z55$h2lomT!MMg4@`|!bbNtJ)t8(lGj!JyO57)!Bt(Pt>F0vKDH>o6MXX+Gi=;uJYQV7SX zDF7jBiywIBDywp93TsRJOKtE~7}!oUH*Z3GK79S*zYT3e^>CeVRgw<&V*iqIh%Zr9 zSC>^(g0^$Bwx+V7sNNq3IoG3kXx`16S5eTqtNx(10=0Et1*sM6Fn;`rt0#cl1;ImD zSRpS5K1Zw^3dHeOM zu@muwpA$d5brnd044QhC_)A~aod2Qw`&c>N|F)9h5%!0F8W~ zOX7qE><;<;HLE}y1wH9Hs3Sy80@-H}q@3Y{UXUS<^Hw5*49O3md?gc|=`UFU{A{4D zfsjB9Qhx~vM5zLGEd^u)kVD*p1(97&Lo5)Q4r>Qeb258EQC(D1Sf$265MffCpAA7} zu0Bx7gPCP)Q$bU99Yk<~t)Ve9xh6@Kl$@ImT2Y@%PG@Hoq@^K<+=iYnHXFSjIS=0spgd563i}N>f zk6XpVsBFQsxjg;O?JtUpi3k7a-Q)VbjFxT zvu)6pLrfF{lxH+gg0LQH5P-V>h`o9|_GVmVuA$1Ut2S;}6C%w{$x2C4(R#2LTireA zGXTz?AH*3;N=>Ee2jA~L^BMn|dECX&Z;-VqG#0AMi!9bMen9!STMt!W*k*AJ@r}uQ zOwxJ#0$W;D`|_L0>bXB)X}$J3c{4?dR8nb)ib(I>Bhm|}!`AHMjyMjLHP^%~-Mo6` zw)brZ^7oZWu@o)zM-Yj0asEV>kgepk&VHgHWG&VNHI`!fX8XTrvGZR*G;ak; z_W2{SfrA;dl|CgNoxWurPdk&P60(Nu^~V4|r@17&e~&0W^3bDNU~(%E9)-op%uY-c z!!*o*9Hxl@^o{X&85^7#&^;#N47#r>34Hv6m?MO%%Dp&A&K~$gK==z0Z!KOreIzYJ zA#wr=C8jcPn25upDggj}Cvm6@vF=Xfc`&lY418P3?p#c^TJ*y6+{M}Iawy-Ig>1DK zY~u>H*|&zM-k0?pe*4j*+qWO>+>w@4$0gOJ?bxYe?;qVB-jj3QZPzMy(gsqpp^5YA zFX&!-O}Fjd=*mbQYb6XH(N}FJ(GedN384c>e;Q10bUcFbZU6}(KwzBws*Q6FYaiCZ zZ#>h|a>fHt=4mJiy?OObZ6j8`8bz?L28{2 zw?jE)-rUJk=AOM;r}^|8;JYqI*Z+LN$?fbzkl5X$ltsyf3BcYCtWMdHv^{aV?~eVu z_U_y-&9MQ@s@g$iq|>$<&YF(d2q6oj0kB)y(C~t={B60uI#4%?j0yP(YC21tkd&N| z!6z;?Xbnq3Q^JzN5~<{SpB&GQAwU;D7aGMQZ2-R`&61Xr&NZyxwPDBF#4vqW>NfgX zxDR65@rf!rQ<9LESY+hLz;MUbg3zK+-;i~|8$#AgK|X~5LkN-i*M)PyeIgfQ&ov|Y zKxE(5B-QHcQhlqzLP;5J54mbj=OuLx1%qt?^bw&`B{My_)@>-2gp*gR(Pz9{PZ%WcbGeJfMYUJa}R{xq( z!4Wm+0@+>hv3$}5nLGtwdB2d)!dJ|$Z2BieX4oF0#rORpS2BDwoUT1t*y&<5l|L z6PbO#Ve63PCayBPXnBxIzSa7(#u8(Wjs~D}bToL~v?1%ZN$GZW z!(kqL9+nsmT)E>$aPm%m1+I3V)#N2Ly7HrVueeoKd$91>F;#VDO?nmAaHRC?IaN1U zZ&vTC^W|P??H8 zt(!nK+>8$!$*cVzZrvGPA673t_b$aqj8zAT<+D#>a3p8$?kzvX?;}qU@g5?BC5kU9 zNte%;U|{64t-UaPaW-@T5p?cToA-<*J~B<&ohWw)w!cW5@;|KTS&P zdM@^C&=Jm7WvQuF;Sk3XkA)rN%thJ7MXHv_mUYKCt3-bAB$=I!*|QU!uBKhZbP#=E z{Sx{zpByqec&nOX;AWqEGK|~B`?q~EWY@agEBCD0xAy$>Ep+Iw{iNP-%OAfs{d|!=I z%ex;^FJ#^vx*H}$k2uZ0HJ)?}>4_CsabMZA&Jc#Ys@R)F(Rw9Lnly(JKiTo73>MNq zq;8P#^nSs+0)*yGh>sxm?VNs(q>+3~)5-AR<@jg7zvM1>+fC`5PU709ONw3o%D0y+ z7|mswByTJ^_0cCMPF%l!bkVeIUby+#Unxi=_cmXCea8A#Yhts;gSNn2s#9Pz3USvXoF>* z1qz5+X8?tr|2n`1gQ*WEI3#r%uqSZ+d-PuzdxCevO7{WvelUFa4`d{OX2>D4?1)DchD@fD zkx%dkAp|kmQ5vKI{Ml#3kIgO2u;~m?lEMpM-UP%pX}gRT#qSnQ+qz-D6$q_np!we% z#v?kG2bBWvH=AG#w*FfNQ__W`u+YjV21KEFU3k~oQ%RRJQ(xlui|RfS2y{pT?e^Yl zoa-{#q3lO}fkjxdhI{XB1CWzLfSViu(}yU&meJ<>;tZL)HC{G=GR2dFGCGgM(hcOp zc<#XBrr@#!>B(h9OJ=BM1i{H1Fk=7*NWK%0{1(am0WAXt1hurZ6dgNxgexm*+I8T# zlzdnWQp*O$sKYg~>3mgubySt5{$3Fhd@G5fmb|miIhNGRb505zc}JO(V|1k3puUlv zVK8KvQ|##wWHRMgrSb{-)fbf+_Ed`@!;qN;Vuv*?H#5f~&5~GivT_Y}>8uM%b55o; z-2&{m$(U)(uo!Ha)=Zn(Y?0OnDswC*yTN9#rXh)#k(r%lO}85C#+)1}!T?>BW?Q-) z$N&gO7?C!&r8$gJd2c<)gch?+dfA|~r&?1?TuPcDJ&%jV_J>m7EhjX#&CG}$0P zV@ffmr)Q^Sg970&18-w9*`%(;t~pG_3l3q!?yMtxnd!T?G&{m;R=oLg7VQ$ITGp7= z0HX<~kKqLViyF`ZX25vy#L&qLUWauretq((&qI0l`2SD>mMinB4LhRCn7V~eVN$Fu zP8}EPK`3b5+K*vxxV7R}@zhr)XmR%Is!M9}cy4h%WV1ykvRAQnh@pe{fv& z4*p=(dxuqWYvqlw>o-&+{ZrCN-X*Vc=MP?M_+-0u_wDcZ{HT^2{IRNumXT-n?|1B1 z=UB5$IlSCH!4a1o75#4VyDL-+@C;qngg&E|n?r_%!H$Fxa>!;Y#Q zJ9
        g6hQci^?554dATb{-)j(lvyL)qjwGIrcmNyA&2j9QlLX#>zGk0YGw8Y0t7} z+PSpKrBzXR^BU&X&u^5LYzx}8W!6yo_5yY2rrM%#o=*P_5TfpV$aHB!P1v68r^wsi zT~yTvH^kL(o6l@H7j!ncBI0PIU5a>aR+@U_l(_iK{L;vv`C;!$gXTofeoHlI-^ltA zT-B`Yb9QUn=r{!HR+Diroen%7dND$}<<__Be^h^bp}gTdf2j6ML*-FvabwA+ds(pZ zfy~tgkh^zYV6#uF7?F{H%UG1<8ZSdFz){i9u6Ud{1>I7Ua+C0nKW(N#L#O8VmTb*iYcu)G-VbL#WM zVB#}Tnp{>JQ?dU;^5Q{tb#;WkoZk^g`b@ONNX>?@cw$|lV z&JBAfW_sGk2aaE^xi)jdl+Z~D(#vy3?jNKE2l!>$n@$b0gjsPmDvM|;F6?1sv2^RQ zIPGi|?RvKFzvprb%}a_`)ksZQMw5yTAzf$>(l?k(3k}H#QAb9ZEm3?k?uKUuk(V;1 z0kjJRW^{l$G%VY)jeiZi*l`QV47KnB`AX0W7+4Y>~o`MOdo|%T7~g ztikuX2)V9J2nk6(w;zD`)Jvp^Mu}>^E~ZbSS; z*Zo|tkcpTS>s^~L9X82BTR}R4cv3St*PGj)R#a0_X1e$m*diS>$m?OMsKW65c8;8T z2qltca@XV1dl(1Eoof*~XJi8x{H;z{FSP9exv)nilVk%B2LX|SCB|DoZk;N_`j5Ha zfm4p+ZCKVh;WeoWp z!RedSOtNVSZX+jr6)3EAuWfXHB@Hz1 z*tT1Z%x77N9dMLF)@rHLlYr?8v#Bd{f!E2LX(Zsj_iYzfEdpHoG0XPApRP0j%oYmH zH372)r{QV58!G6OWQY(cDz%mumZ_c9;s(E!38L{r&g!da&(FCyXaHh zTSq6V+pEPB-a39%*a-$kimsk%@VZH>T5DAQEB)a1F&9uXUySp`T0k{@LV^lE`2 z)43IDw=N!0st66~CZ0kgZqupf=+wI-NWS?J>DKd`AvZoHk~h9?2HX3Y1LW5basVP9 zQ)yo**yCs^M#IQ5Nb|UVQ_>=`oZ5(p+IL7vwS?Gr5E~-s_*B}>pE|w<1xf*0YgcA) zb+^h|zWy3{CmmLekB({(b8c4RO;#JZO1@Pg9MStcc@vM`bLbNKZ5zFcKtUEbn>}!p zZGeE@CEuw?1bqojhSYJ^d`n@WYLZO8n}rw>Es0jd(eU;o`W^ijy-SPeHf|?YHBcUY z)exx$>suGuI|zWULPQ5 zbC$6U(!zYx@m+ZgR#f1G@P}<;3-h&yRYcXMlR3+L7SdU1o=tqqqPM5j+R3bwK1b*r zTUdEiU7Bxg`gVI+Ir1)?57IN7D50=CwOnnpXJ^~^T6;x>t@a3+<3naGME9|wFZ*d} zwF}8CA2R1it*xTMUh8Y~{4{B|)9fZ5g4hilQ#msrtNTrC5pzoQab;fOx*LftZPakKsXgDT($l>er~IP`$3R?+c;=JLVI z1J`U^Bi$S_ZTK?gH^FH_7yfoXFF)82agksD$D=KztGZQI*;IJI@}88uA%@nc6z-8f z&wl1HB8TrijVRaR_cE(h9`ZU)Kc*b{p2ZNI8;4W}8t*dcC_(EXhsv|dEoI#5YTenx zsv28OK_w^O`g&kP^nnjl4MiVR*0AxII_LbAPcB~g7-E`YdF1Pt2Yg5rs{7X(Zf!qC zMY;m6Kv$qEifCN8Z$7x-8rmP{Gw&kZa0ST8=C{0gFle| zICm8pPgQEhS_q(TthBExUc+O2aIMH-yl~)+Nh$kX_>Gp;g=;G}NYP;~* zEaC8zOa>91Zz8H*jAQmxTSL=B{HoWhEVq`3j^3St>Nh80zDn|K)IayU%^FdLA`hx?}fepwKVnEe6z~QsH)z!SEtlSJ~ z$L9`@rw}qxSe0ZZ?E;f?u94fn1iwd}5N|Rj@NzO|L*?4S)fSvu3Gv4ONTGAbVL)UE zVz_0J;x()6E7kOk0N60YsEUkV_2XRrgJ6v5MkzYe7;<~sG8Ju>u%5nx=sX((KqW6X zJ*c|K?fawt5$WoQPW;bH1;di#y$@)YrIV1;kJTEJ}_u) z^m6s)mBkg?JU@AF6T54s&A#|ChY@*a`T(j>4+y$;YdaAgt1jTH3#tpMicU7-E@_sw zwtRo}k*Yx=|D?&OK*%B|6xm<}E=lxPfoPLg3Koi|I5P6v=niqTW1OA}YTNLTi@3Pq z!DSVGiT8Rc*ojLFcL;vzvf1T9JAemRW@W%KrRN}jqujjEH*af_w`GD! zLeWhkmhC`eN@d85;c?QJO>>Spt9L=(xV;sbuabP_HIL-T` zC2wooCJCsBb3KFN>7F(FNn0GrJWYBNxzRy1Ao~`Vm6sMD#;yUR^Pr-vx<5;^t9Fw< zI15L}l*a2fQ>s4LQRg^Pk$WPtf=C_mo3HHFuhz)F#S_`?E>q^)kyOga&vaxYrby+# z;A4ov=A;=x&dA6}sf!Pci8V`eO=0obsuV*~R$5A`K0i7>Cp}STPfo~Biip)0Cudmo z$>}+e)=SGUXBQ+}Oj3g}Bg3G!Ch8MXQj=44shP%@*rc$AG--C$W>YqAPO@%_EKIhh z@5s#0EHGuI79_?S^YwPAr+a!^9Ng!4z21^pnvt5DWXd!o13qs{%-b3pZT6xJ;U2$c+|=1hQhFf@a#}&RNS@GeU3Vl8w=o zIr*lH%*;$6$AWqWc~JfQB5#5|kBoKt4C zLEIt9o(T-WI!k%AJ-0R^*MN2g9M|Wk7wF@Y?WV>QL!#7Xu{v_q4wE@D$50ejb1cUg zW8V#AlRYy(JdqtZV~;*RIXfZ>Qpa)SiShVk+HQSHat1K=2?^2Jv1Yp|LTAii+5*N@ zW3pLqNG`QHwxpRVEu~o%Y2Fr!43)Ura%|<9He*40cA`a}6JHosnrksvK?)Sxytqf7 zYELQ4&CAU%w^)myV;YoMs>&<0m_~T{??CX!>wb7{u-r6zd;(%Q zb;&X5_$@|Tjy)&G?l725`BgR(epg~ndQM7yW=@LK4so*Tbi1)U-xM#+$uV29RoMx) zxKcB;Aft_$TzX2pImM7^3Xim8CKg9##o}rMjWaDZBNaa{Gs6&LFy)!8`MIpaxQXe= z$DNfXt0^yAWhyDnHx=V%Vq~n+;(~(wf_zJLW|5&Lt2U!1JH6D51T;>z)sAG49XyXb zTV-`YLS9l>Vxc}KH=`gox1=mTs>D!gu%#F3Gjb~I=4@$sPOiQ%xhT0R%@~zuv}Hmi zJ|iCyu-E$2ZqukHoZ0wEe&V3cm44zt&~92LX`DX7>q`3KiI>_Ikr&(FXn(_pW$+&% zPp8p1$2rG|oZW2*U~mEk`G&}0v*+il3ep|PcCLBWz^X~= zbeR{?1gV0#WITwLQ!n%R4F%1OK-O4fojrUR7aT~IEJWV$u>)yb7AEy171>LcO(cr; zR%N)%>FC<=2O$xv&}nW!#3s(K>sKAJ8E{a=Oe!PUo$TX|m6S8NaajjR#~CXTl7-~I zr8AHgvNAm`rpg7Em>HJ}Kde{7a4Z1_cPiRJs1AU-Cp4{F8vxyH4{+Hu*oC<7W#?0xT2I0<9ZouT}fIhTo|C$-CFTB zU0irFpRBWPg-e02eSp})1OGvj+tbBr-x`k+NQeFdNE9_7QP{mC3Ol4p*_On!7xu*K ziyHE(jJ@z-&3L{+!%TgGMFyda%v3IM9OOSc^v;;7m92wuD|`>1YSFcj?|)ELnX4>S zT>Pq)sVk_u*R4o3m0M`-Xxio8vR`?k5`X;ly+eOkq^>jVFFaAw3Pcp0r_1qpp74QC z()zPM3GfJM1^mf$v>rq7y?r8L=59q0g4Z-cdBZ|#0iBENHG-VwcZcs z)1hR(d{QTQN+&;26TEgZUL%T)2}=o6gGo>ZtkxQ`mMOm0)~a?DR99ATn;UnmJFb31 zCV!#R@pU^kH*%E~)%iQ2Xqy~U#*=k)ov17(FMOM-eZF&nGB`;W8O1ej-nxIWnt82@ z_it_7%tuD)l0!P$$Fb=;vhKD9NzT6;Swq*dMxdJOlD98Vei`za_B6+~5}jHwao2eD z*oi^&wfwLNH=?g>*KQ_%`$LuPx>02)`435k8r&|i!pVE%qzRGfK4EGlRqgevv-)QHB|hY+pxxPGe?c%I{Mj z(5J3QPmSoe>s9rT@u7?6^Ya#kjJLnx=zXOx={!Zc;MRlSd+IaC^D7SWHdaw0ophVz zBTwx_yG=?-PfJTr@vT_7IDfwS)xNy3IsRFGx zr7EUS>PMG5`zXV=tw~y;me+KeHKk(zES`4yWc_a!&q!UM=*KW(r&8@5RxxPFhRTPz!2)P|SfE{$Sk_HUeR+pNao|~HMn`t&? z8!aihJ_w?Th=_3j;U3Ls*ST9oLYo`J$m`^5D-?k&Ilg2H;e=B6Kuk>3u?F)oPAi*| zVID(ErQ?m~wfsSopSUtn16rkc-I7?{I-cBsr#c7IZ-98=#4Q^(@a}TX#EKZz2_XS^t=*Mfh+Lt0|b$SfxsYJDFlGY6(B(i zPQ~LkCDS_qEKE)Yd%u#fHRyRFclCf&h=n}gIS0KqVHGPNa$NE8WPtL{hFkAk;*huf zN_1e|g6jEd`qc2@^eJt%_P{z`7~~!V8Y`5v)Rkw?R^mC`#=8dzgGBKq$(2>A{X2K; ztEx(gFG1+i{S_n>Y8Po$Bi?yu#Dayj`_^;qrOq%y?$5UhrJ|XaZmqwg2KDe6 zJO=YXLO{X>CqO`|kw5{0-Nfv{)E@*mw~#YIS{Z{hN!E^K&mBM&?0$D+yaf*+TvD+= zE}@7gyXkIGVPff;Xw_qd#O-h)a7wk_xGBPjPh*u0Qg+BhG?K;+nFvhnBE~_3{3hd= zx!U|SSq|Af$eSY`s#R*SSJ#d|z*#$FEl~~VFN-yIMFk=B254^bHbmEpWULknV70Ec zUH{7$PHosfw__I{>5OU7(eD?cc(9W=%JEk5pnJoka`Mb3K(L=C@|WA>)Ahm&Bb8TH zo_MQ-`-wbSIyvo0!(cGXmNmi}fym;e^y7@lMmX^%$HFRytD^W5I(XkHvnXWE#+fK)l}dg;M^M9u|=N`R9ecJtfHd z%CC+uFRduf$5fFd9&H*uTIDa6D<BsB~lLv|aP6mKD*Lng_kV z@{n}pp@_prRp+XX9@@|CKXkF;3-#AmgJ+%RcW>M?ZFip{qtCbL1s0K|#0>Do`-Y1t z*SWM4X$R8kCf3X;S(z&>n5ea{SJR2~#nmH*@{Fl69;N5<3YZ$7pc zo#amz9;-eE!QZ{xYpNR?t9KVSNq1Z+y!x4{(O3`UIWh;C6bxe5v3o;)9Db)eN*f$< zMv|_h{*;^L3y%1SdMa-kk0zApr1^2S$+WwQ-j=*<9h| z{ik^Hl=|me`BklaYt@BaN1Kl9+t*xouyj{ZbKY@09va91soatvbW1JEQkiOv6@{vD zTcN|jS*_cxAJ}(h??43)DLjZghst3r&8X#K%`m%~#4J-HZ^6B>pdhn2tIQs#UZW_8VjT<+r(+%4s}GyoysBgnvww{23nm_@wD$26ukXAae*n|i z?wYOi|C6!2{`41-K|P@3o>aimrDQ3BNO3ksw`BPyKbH&tBMg;}P!-bj1xXxPN|!Rr zKOIy`8*Fwz5$;zph?F*PE&W`F$-Lt-fbM;iv&rJwOo)~}U!aRGki}&21(7q%J>s~m zJ<>V!xQ7m`0X(hy_Z@SyoWQ!eF9Y(@q1+|Ou@ze^99cvbi7b|4TaKCx70Z7G3?1sS zj{BI*8IJfdD7_vg_r_&WVPOc)BH6!Gq}Aq)ovea(@x-t4j`1yGZ>~k*eLnV8^5-5j zL5p(;83RNq1O1p`FZLr=#9ZePYZqiMKS5-xn$*x|IOD184~x!8vx+Z$O9U?LXjUtr zJmQaT-TZX-!gr>;`;x9dH!AwV+h40mpI^vqvJHs?F{nywXaW+uljy>?Dwfx8;EQ6- z>4vC`gw(){L_-wFt9GgX!6m>=G0Y}7EX6`65YZOUK#+n?)3G#yX1)H#q2t@Qcj=Ur zz${hVoXvAWR!Ad1{Y?Lb+7sLR(%FxUB0V5!&=-$v>^;jvyJR^~;5KH6(@&@TS#_6n z{2S87g&)oO3?1+K;kP%gG%lJsb!9Kz0B$roeqBvo{ux02tz-;bk>?>z9Sgr|Jk`Ec zv0@iG9%oL2v8=)@7u%~X44i$K{Gr_Ze(D!^kV3b{%$a5Pj}W>TLSREi+|z+V9Zm`XGsJRsdT*M=Y9`QpK> zGvpy0%tpYX>9{W*C<9C$!EYJTYomDNxjK=7O=OH(cw0=>GoV^1E(|Wrsf?ChnbAl) z4+a-1JOaH|k`s$*qe`2&aNAOFFaeOEj=Mtj1rmFKATL9vT!#%fb36t-f-K!nW=@Bx zQv&>z6dH;^;I3tzR*ez9o%Z9k*h+ipG=bF}Rldk|7Nbh=fDuZhe0GM;K&{ z^yG2ahCW1BLCSD7Eg{eKy@c;8kmuO+mM}JcOz5qBRmaeR5iX}l?y=!TCcPi# zIi#V5W<0gYuAXIISed#89JTv+(`=N)g~jW`BgcL1gFa|PMC{fA+|E#52%k)c$U!2m zw+&D;x?U z3M~MeY_bNN{Z^s%E+8oLG)%j|!QNmFoh5tx7Yp2UZV>=zRJdB9M(NhNwU`mpFe4%u z!z4_Bg6r5U3!4e8uqh6(a!{}j!N>&035-k#uX*r&_~nSmyr2O}DWFG^#?|Ho?NSd{ z0-ERUHt3-%9=G9Vf>FT4$1#7yj_H`d+mkSlN8Lq>^Vl>$3rYhsSU=f&blUr+lXV(a zj!x5nU*`N+8N3-KSHoZ)i!iB(L0*(eXO8SOo_6-=pwrI1zPL1!rz6QTbSyIFqlsuk zZQ#z}Mrr#V1cqF#UGGf#EC9&%31a_+Bl`{hjf$==<52;w6B&YkkbacD`yqMiwHqEi z_8a7>yN5o+*Dx}N;C2~II!W(b{N^{7&~lC-g>(#gxqCVJ#`%EUl!uasu3k#|&Es(L zjkwZJ^ny~}^s{No=Tw9{dE&(W1Fw!pki?uNCX&y-_{qfkb+xnyE6G_%2)#suIe93Z z`bOVrt9W^n8R4dz;;fuO8IOB#S>&d0OtQ571FM0^$+x-cD{xy8WPm zRS&UL`4zC81!$v!96bh^{rO{oD(uMtSEIZLm_fKnAu;N|6|cbuV6n+Foe$s- z;41f_<_8AcUtkw89`yPxaiO6+yL-T%?2aNm)`CJ+p`jqf!3FQC+Im=BSDjZ@&hOoQ zWbY}JS6kdYP#B0f3@R6?7i?U%F_4dmPDW9r6+0q!1#^xRD7mN;lME>+J@^~_O_YL6 zN}?*!n&e2~b_GZ5SfSpggYX`|F>u+&1s&y&1m9u`p9CDp`meG)~ldk&6wMNxjX$$d;XJj0_!;fat`|IxL^gvNVqzJ zcBD+0;Eqs!`0nmek)uOdn{Y^;zv(cewU+ z`PJ?BeFBb&=)_-M0UWBIiqs=YlPCmm%nVWf%}nF6Bp!0we)=cKY5W~cgtaWL0(?%h zdKXh=V#^BbGub^%b6Ol5OF=2B^dJ<6bz?I9aM5C`V+p@7Z{?P#gvi9mB;P&X_CF({ ziq9uLB2THX4wM45@*!fsT>N#R|9R(SKe|=<1o1x`l_~zBj(jNlyX0M5Pea%q zSAi{2osnTOW$;e zA38W$(7_S<|3;UzA2mc4MpmWynygk+j=HQQuQ-<%n*6$^+lw*4y!Mmodsj~Z2%hU~7(MqZv0H7{yh2A3EY|j?h2UECq zK)~g+9M-#BGeI)8EKKc`%B4Nvu3^Z)~t&kkHb_ySnqx|fM@3xdHpDF=o83~iTjuUeH@myN#+!^;#!S^Fjl+(_1b6D(seRw5 zf4WH|vO;wcQORzc|4IGR4ZJN<7vk+ry#40X`UU6sbh{lix%n6KIbiTRv05rYxKMba4FSlTw?mw!(f}m(7FkOITv{(| zZ3g5(+5=!W9*Bq+ z04Z+6qX5@=?aRA|UK!8HU025c;GgR+4T+5j+N=t9=t^R_xY!h3xN380@QxTRHNg-Y zr;`6L{rHx1+}yfz>o2P>pWAn?jz4$2{zD{$Qj7QXh0NOs(lKyVf8K8_! zh=4S+w$AE+ z*!Xa;>f|WN;lWs7X4BY;R z)!Ub;Jw=|YtL*vZyt~g&GNF$|UtX0~t@a`Xm#q$67r~?XYyTEJEHKdNz_1?2GmfhJ^ib)KLJIiLyuCzkL( zNJ1tz%g!(R$I_4<46OoeLv98Vp<>1+C<7d33X+eB}u=hC$Vq&FDtl4!uQ5EAy})F6=!V^wt0GqI6g8gRupETL01|9su9kc>Vt>5EXVy`rPy zlCwhc#r6}eH&jf|89ZbMQX=52G-E#<7J;4Y672$jH&vWR-#sN2Tn++KO1pN2hA~ng z!2X)%?>CPX?q((GEuc^A($1B2wlHl)qWfF9-O=K$1n#XnJ;Pg6dIn>smvW3TkGmVY zwhqIj3lqXqdiwvm(f`lauV9u$W2kQR6=J%Hm?%2Iy8y_T(VLlj;e>k;1NVaU_Pp$S zhET$!PZU3Sfq!Jde|H=NY3bxaAlkP#f93HOf)IPwzAlrei5iH5xe0E@%JC5T?*qFC zuriYZ0ARO63Sa>IsRWr^2KV}DnLJ~P;Ap^rLvKJV53NV009CDMGom8!j5>LH1^_kO z5zicfD2!JXf-Oy$jO5NrL}Nz&9gWGh0o!V2(HI~3pC_$3`8l?1DH)2>$?PClWC~}1 zQT7ocuJE3kmDn2^X6$;RtstXsTIz|;{CUz7o(T(!TDnPv%VuZD9xM`K+7q-Q1pDz2 z+fbI>6R7dNCMYxjwF;-hyI^7j9q=4$Fg*m^XMM!nAmF(2KlLBU@UDuzf}yDExE=A) zV?~dk2bu;kMh=;9+}{7VB?H(k*(xDz?3N6|n+6YkJgWhdr6b7mKhZXHX9CXhM*IO- zGApZrHn(uJt%2%VL^B{tgjxOynWh;4(!F>_Pz$m)@*8+bwL~WxAPx$GJZ3`>QKU+! zHe7TNHgLEol`4XQs$>m8B6;I|F%G5^L2Wt!dt+V{-$!dxnFLdt2=8?*q^&^&p^2=9 zEDuN?7fp8!D=&bsi2}Z6{Kl+t>dDZXLO3Ic zDnxD_dul-hqm@l^s8~xjaruv+h7On|idw)tm2~rvD6~qbxwX0-*zj$cO96ZsZAEYr z?=3B-APkOqRl4mh}C`aJ4t|L63P4s+* zm2)^+>pEQ4?eSlpV+z-COqWiHy7yCL|2#;?28Gzb)BgXhAUW1_R-~Mj@=528E!n^X z`AC&;o%Ns%Jz#H7dEPpkad21%I!%XWs!b*|16I%I1v6ml{rAX@UvBS*x^CMLvgM968Z7RT?Z(? z)39>CJbpwLj@8206k{}9aN|$H&=Taf+R>0p3meqiIx2W0Afi>?dGoVjsQu%OFFRYy zG>?a5>+stE`N)wIf1@FWfstEn5Zk}Fx(6dp*0Yfsh|k- z*3LrWi_LEAn<7~td_Jc(5K4?ID`m^DY^UM2t3{ICi7`c&bhuvw0J@OJ3iw9(_4Jmp zV`j`4Gp1$6*PJ}_`iCuF^TK4R^?;@Sma~`)eUbP6ZiKhhzalmy6TB!HCQ^34Ra4XM{ht}1@Se6s2py`KSES^ zm&9_PItlXCdtY~NTVq_4xrR5zWyHj(q6^|GitP40J6Bu@`Rr;bqH&+1W`sZH8mjmS zc8(7ARd;}eP@o2**{b{!gWBUu$m92*=V{||n#s|zVhGeVegGQvt3M)8I`X5Iq?8Z& z)DtH%PpVIzu;iZL9UomT_z2(ph+rxz!RW|jCF!%4@B@g5D?8;ldscNV_FCX4939-} ztwHn|zH0EmyjRt|dg;Ua@b~DmeXh`<>cDBS6DFwUIp&sWxdF86T7a(msA!jb`poe@ z9D?;4L8&99YEnr4s)HJ^4}a`oK9NBf&r1}Bc?t6Zw-f3WV(wrj6|^Fu1%cbarTq%` z6za~cTFB%6!D6QU-*iPVzv3dqCB^31Ht*7D^bn682@jR=DTyh14pMM`iB<x=hnsaCE0*CbGEzC%fAM6_0vSa8o>|uwn#20$?zrMD|Mo80PKz^b0<1{ z39k<<-?UrbsNY+jzgzleu4u!Z3>9yOpzY`Jh_o|Evk*YESoYzOoy3BF$k~ccye6aCT8%s!73dX^rqou+ zbTauNqF9RG{60J^#ZnE1N(=AmAhP!}V4XNHamu4Tvdl3WPJZa>*?E(B7Ny3gf2%;_ z>!GOYtUh9s1 zC4bxi?2*vbtO;NiUz=G&b*QY3`F4PWA#30gqPRASY-63qmjN0q+5u*byl1CQ?QQ?H zp|j1qVSC4h-W?8Wcb27p`Zfe@iI|@v_zzf7yijdyni(L zBmt7pEkWGdxl1X3*IWLGlP4~(TeB~MRY3C86q0|#Y9Jkf`zMpX`?E~`O*HCbMX=gN z^2Cod1*}3A>5Sf7#8;L1MO8H{3gGGN3#SW(!9-z40t4OMi%Y3dNuN)qFR!4|1yV8- zg|E+&SB{cy`O+$xFrq7c-aubkL}jz2WUhofb&>QvPrBQr6!lD7-D{ux(!gL_ekf1o zND^}rt%)}2SqQN`e~J!BPX}X`gh|Y$CD|ovGT`2VxkSPjrWYCtGo*0miE0fQ_VEvg zr1Tw$Fuv>H#dO#>s@f+dizVr`b;j)&4S9DumyHK`>{)n1W&b@CY#`**kI3Z77>u7~ zPX?l6806F0K)iQR)-eoBo*FWc;_xm4g5;4JSBrbaRM}(rSuXIg6!$BV>>x9x;np_rZomuJ=XN^fV z#JZpMb3O7wEti;5!=+fC5<^*@wN!Z8PxOqBvv)fm=>cNE7GbN4pJ+N3G~keyD&0MW zp7m(Er|^>KiV3qq1AwM6WCJLcuW_I$LlmHu?kty*Vv~mCK+-jqaEosZ{Ec?qP2UQk zb*6YnLa{*#$?PnPx**?{Z{_WU$V8kc>r|-M>esbe_(HjKdBNKkfG@pD#?Gl1xfV$v z{e5lM?2nR(ut-D}6(|qBpYYyn2P(SycuKl%PlzpwQD;eFViH0Vc^ctf<~B{5oszKn z{Z+m~C;I1bccy4%TFJJ0b$(G!ZZR(`AbNq7e@!h0y+K`HQg<+oA1-8)zsR4We_(uL z{JPdC3u_I#qROR(o}7DfvJt2~cp>eIZHWoN_7L9?du`M%Cd<_-4z38>nZ~i`t5sc7 zRalkJI{{E)+Uc))%^%?urZ`x#cSY{Il6J)*&ufWrsyzTj7j@3NVvC}9;O1>!H*>P8=k4Jhd8DiBF3oG? z>Lfp(s3F6Sp;j+`^Vb&AF7@v3!P08yL<#{d0({`_uyDYlBj5e~P9CQhW{@(wjJ&bt zbIip;Glr&B45f{t1RyJ*10mPz{kr~!{(l+#*#h8Mza!tpmPQvw75K)0n7y6u=m5?F zfxB_zjO>kjeQ6y&PK_yuDvU0T^~Dj$zv-P0VCt8jJwc_OKDFz!FIDb#=O(56*-l9n ziRH1S^xx!;j~5C%?#(ASSnYz~H^-^Q?RxVRaIoLe?@D9K6DyKf%Vi{uZYSGsYijc9 z)O9r;EN>k?Ni7pOpBwo$)#iQ$JBB7NcRH3IJUllabj3ll>QA4#dbvbH`UY_ElfmF8I@XvbXNs#Oio% z+8VMco8Qsy5N*od6#{j0hj`DfoqO<+(;)(yXp9g{x^IM#%YAT!{6zC{*8wFVKP#^- z(#X%=0YK|ZWFR$?M49si=f9P-`xqK8E&_M`Rs~5@5#K(yXzvlTf;Qil?JnD=KKa3> zMZEkhc~cf`PT(w|A|YSg4RM|BShL3_mxhJCzLq)PQvMv&s z_Zi)V2r@$+iZyh)vTg3qRKiiYw*OT1rY%)9IzFU6{os45oB1~jZ*b;3`*}-_)GU!V zr6Z*)-bN+r$rE?n1l*Q%fh3BGbRK@bchCN)I)^rX)=pJzir5ma<3hHqOkb@YH7dVw zG@opq1C3s(JQSXli6ug~LStEGIsW-3-ngm1sebREZD&1SQ(aZR=Su(6M6M!|pU<`Z zetQn>%+YSNOAviZHR|)NSO55}!rZ)d2crH#O;e z{`T+8!DN*`tavCwk>+ki6mhLal8y?H9$8q}Y=|U6ujME_u}sn&#O32M1P%zv0}ud^ zO6}>%-s1%@|Hy^m8IQ>vW>i?ZKESH}%G!RN)ChN!DSOlR?S}-1r^)ffZ*G5^`|UT8 z>w)k9OWLTLJ`WL~8-)LTT4Xmz`8?DRJF)wGy6WqYTPf0f7La6JNtaEWQr<9&gECsu z?xwVT>c5YPkd*|Wmv)i+dE%oa-QK0L?)ot+_yjN)TOutht&S`mYFwIX~0 zERce}=s%Jh^UkQ{i$kTX9Jm(IQmDc?SiF!$UL6wmDB(6Ouhnx1ix?dMDCa)=a&5kF zo0JQq;Km?-gxIK$CwwUU!}{z3%!)$ka_BTTosZ$|!a|+_!?<}VAZ8lc417V4wNF0r z0LNA%hI$VT-S1AC?<1s!DPGTv`EK?@$)(#LQWa<;+ zRrIvjQDKELqu1{Z$_ptD>ho-q#+8EmaGXG7e5E7_#R zH6f-w*1n2MsF$j}*;|SM5h_3lp2GUxXBYPniZAi`iA9;fRtyk5(PD*Mjl3z>mgC4{ zj;RjJh|Uf815|P)U>O}t4;HLuWm#NN46@zx$51o1aP#KQd3*L`_rIcil1<4-&oHS0 zpR^=%T%NvVhL5-84(x?&3r}|5V&L8pbZ4gCl9Zd`ix3%dLXd&80n&{cGzy|~*lc;( zdA=3Gzph^R==`~}zL1AXxeLtKEf|?l8=gtNMzm1;HN8%*%WwIKKXv9PcMzWt;ydOS z=`UmHzs`Uf;s+5f@+$qBa2m2-%>KS1-n%O)vXn22v<9VaqEp*jeaOGXz$m=#%z@1S zc`78WEKug}Nr1c5xR(k`ed=Wbd-_)Mu(wZ(hF+i-d{8~|LW{;%s1ka5sH=bP=3MRB z4LbDoOa$(N55*rCS`Qz7i>;Tsm$IEYAHqKGXuSIXB4|b2L4OA`_1n-^_~3@d_1HCD z**-#CjDibJAMp}*Go^h+rVI&v{A&cM7m+u`h2WbnUPzXltRm4Ow;*0Fzn_-k4_WM z?RY);qK97_)hYQh#nJ9rh;=8t#BSfD52a>G@P{u&mZ0=b4U9Mdc@~Y9T3SD zJ?SgI=+a{81l6qdF|)VY#ED6%Ne14KWJz=+|N4s05J>7y97dOhN}XyrrUN{6542>Y z_=|%lZvF&1N|bEiiBVsyVka&*Y7N{80pk@DQ?xK1VL8$t3_-o&#BJ2>&Ah z`kss0TjWOmQ-L)XC=<-jm65pl|5>=!)r{m&yRJ!dLh~w84CA2Ghcc5rlj4)XmS82TfOjq4jZxk4LPgYsVjm*t^2Xd+3IPJ$FIO5AOaSuPU=s zGE&lszoxL%#K%LGXcQSmR~JiTvlEHG%;v~(n8@W=RN*z1(#ui-YI@m7-KJrOBDRAt z3}Wa%xQDSF60n2aZpkwVrLn>&_oz}gG)v!e&G(1$@M?6py+w)36$#{IeWo7V8;doW zk19yQ{OD9jstYPB3b=~=T2x#{LcZ0fLSF!Si7qKJO3y0Yuk;h=(f7!E-A}Puamh7f=X>x0-E*QbBg;7l=8i{cg* zbsds+tw`FzkVY6mp`3-62sbm`w^k4C?lQg~$q)%RTP!-;#bt4gQs!4>Y>z8PYC+)> zzH>=dcnE}O6+Us%nW1?R&~~UwsKqVQu7HsVhHV-W>j6}onrs4$$yaYJNGm|0@=#Lyn%RprcsWuT0BL zFrre|L3$9Cx{L{+@}?G<9S(Ak97Lrqb5W`tvX|{sm9!aoJ)v2^6Kcn`w0J(ad$+0S zQdZLjUsn06X+ze`4S0Eo9P-HP?s3I>Fy@|ToJ~L%w#Dgm;9#OI7Aq2GD}ePa6y~eFW21sytS`L845#YH6+aO=)N(P(OTc8Kk z=PYS_cwQV3WDuXGvwH?loyAWY6;1o^qUq*@)PzKX)Rbc(G2H+L;({!^HyqpS2~Q(v4)cM<^+X6w ztyLm-WK|;e=@8w){xni2SO=8nsg)_PX)V&MEkRHS20c_`fo_Jhp&y!+(n| z+GdW_`$p&!Bf?d%AHxeHs`Ol?zRp};gte*Fr?eoiyix@fa2<@m$Ee}s(k_+ZpXRZa zrR>mEcKb!c9H$n~2Sh%)E5FZ*F=@4mQ~& zCjCApJ%1o$uYMAntu8f`=H-;WPloxJb4`v6y8%)Gsb*<*#_+0MYOvQFbQWzK%J+jR zrFgLBW3h2l*81!q>DwUmP?5yL==n)ZKlm1??m6T`HF@^O2H@0+t&Wn65~*i)*-ST+ z5ENBdBq&K70!OHCIg~`o<6Tyv7nbJ{V);=ln{T^^O62j_?A$jp@?x2co+ClxhhKa` zM8DmhX3FMl1{7q>c4RXY*zZK{lUHaePs*2C(*g1ZzDZ5(C{HnpM)Nd$Ao-VuzBpL( zlUv@Ob+bQ2%;zAchS&)MPkch`56H4MV(a4C0Ps3Vr|WLecdl~urPH+A2ai-g+_?-~ zR)6xGKMtFlj=?kMW#`(gjvJ)U|LN;Hpqse1u4Qb^3>uphdx$MrBUB-BLeP!Oi$MD|wul29* zUjj>-raLot&OP^>v-kEaD#-!udsYF0^8M)MI*!aoQ&p&JNCNbC5leS&N4@@7`i7Dg z5bZ>=Xg+wP-Xe;PW0X`rc+DutK@1{FV~!}1M1t!vH#I9WeHb{OQd5lamXyK_OdbZ2 z?2KJo7b$pf4osB-R zx054D(-nV!IrJuOnb(s$L|z2((f2!jIy8=nGZZf(!}%&hokD28<#aw057I?)XP=f| ztw449NVC zmpBpSm5<5HyJVIVu(dj8`)>m)$|R`F*W~Eeia&9&j@~6lrz`$qD{%JZ-0d2(7#6E=vv?r zw7AM1eV_fLUz&;AFNhd`s4yq*#}I^IG2IQ>TVMJLOXPW&Ju5$~-nG}Hp+^8}GUS>-Q*OvqIfk<_*(pI= zREE49D$f&x=u)}+QnHab)Sla}qQ$Jc0Szc*a^LPW99Gc+`~togGsId-7JXDlvMR}% zm%gLJ+c@{P?{&TZMKbZ?=w8R$0$oKvuN^9q2kc+ubFiOk=G(&r;0_zAr-XK{oo}!jAQr;d4`CK>{uiu3 zKhi;-Iiu)toKQcm7^+5b+*gY3JK(yWrpQUvB<0BSSgZB6f+VtCiu*l}AE^Nb@wpA0 z8~vZ%agFz2Z!H$DOcG~P0f%rLD_)%EReH%(L?*bPgh`Y zyeS=^dx{+gc(S?l6m|RIaD7Ml@3)(M2Y1Gy2xdT1n*(F+D@f#B*ss1rq<*qR5!}7C z2&DyB+cN~4-G?*q&0R!w^nF|Gps7XbectlMEmC2Egg=ItghTlWyFx;D?+R^hZ)^LVy_WM|DeoA_LaHrMh+DR% z`0AFYtk5mnu_GubaLX?L%`3)GJ|LUhlN}nmN7*Z|yZ412%oW>mFGhbD#RVXxtJ+A0 zsw$YVV~t^@!n!4h+a;@8q21O0)LqTE&BhYtEgP zLQpgNYLB3717AXD4{1jGLwD_N4rxaNbC(I1LE5K(Ws6@O`G*OpU@8z&pNtRzF6>QyG5p+l)^V*r(D-iTTj zy*rl+%nc5O>ZZW%X$}RU=ArCIls~qj-T&a0{XvI!SeKQour4q0J-U^PgpI_tx${-< z`SABNx>~&@t(7DDn7_We_m@#~I{JKI2ZDyEIV6KF5$^2Wi>Iy;kB{vcKVeoMLZ*EB z{gq7*NLQ3Prh^nUKHr2sqTT`W`7%WzK zWt_3dSX!%etm*z#IH;?Pj?%{kqE>?qw8YoeSSt>S_I-{sNTq+eT!m}z42iVa&< zrgMoB9>ze`FyeSGqiW5{q76rr&vP-~7#`e(l;yX^2UTB-whJeYo;Pu2kcR_)M-4_v zyeATG&AE&dTS}L6Rj(K(OvTo{S=}0e`oBi}+4T0r_ad()9*;ksc%1u;IZfA`0#5W6 zLpC_vgdOR@K+HzOh9~0$!)*<5nxv}q76gO`vWJUWN^$O$jkbfT1C7ZMRhrV+q7a<> zKo(-3uEG&EI4mMDLKU58u1wctmE=@l;&S|B+Q7Q^<75ejH26_EBOF7Ot<+LerXlSg zI~dl!h@8Vj$PA3@s~2t&=GLu;hOszRbm8qzeGW!ZIYO1tX5 zL&ioMbjEBkDX$2V<;tqk=4y?7zCxgYT}13|)!v}WL&2I2le)*; zXWg06G8)Xbx9qPxplWM~4X|p8V)FL*E0O;u4=h56AtonP%!x^h(UVr$slDx*AHg{AthzA?nDvqnV+TsHnHI)(OovW3@KyJ4unx?Z;m#&DN#YIq;T*R0;^cu<<=rfI=2d$j-(TY21Tr?ihHvz#^ z0fPCap$2kscZx5culk&8ATCCbIkC#e@!l>DVIeJ_Ps-(knHt~PH)?%b$5$^fLr%2* zH&V|MH~UaIsiEHrr&ABd;v6G(SNN+o?T!zO(8NZh?pUpaGriipqbghsY-o$`QXOxr zIM|@6YA_$cmAOa07bZBKV?ttLlb|M-UR;_ZS%8unrQLagLu7a5M;0cE5$2kd7S(}+ z)o-_J{8)FntmXl7Tu7sMGm!YRKkV)n47o-?_d3Lyl(_m`Dw+n3luY=i>3U;QQ8K*g zR?l3J{^zQw$>EotY)m%kz4Rt4WF$!%(^i4`CtMf%QcHzF+5HY=ZY&wP!Xy>VV0I-& zX_GY$>*HbZ!3HIcKz`_T5~HnEk?qp1rPe}Ak;Y^(l&0J0eLMBcH5iR5dqdBRA{&-j zyij};hfxj@fyka)Boc9w?h?U}o=pAd4`O_3Qf!zcA*o9%EJj?WIM-sb;K}*b6Kyq! zh*Je+T5_$0m|zx~3rbYv4W_v?E&){?&(m;2F52p1&kzdJ4EjvHV_fepPqYt=yf#Oe zNsnb|UTK-BS#as!U_z3r%7J__fU&iRFR(p9J-60G9Oy^{SHrRl4a}rL&?0 z#cm!*h8oD&ARvsQewlq^oRw>!5j4s`flk)qJ%UDP#_8tFiyFo4r5Xb!Z9~E4jQ9Oi zBi4@kY~Dj17eOLO6zU>Wm^nll8c2lZq4l#HHNSAJM1y0Kp~y5yeL&%K*{XK75AVJv z&uxZG?z6Rjk$6o zYfqNcPj7j<+!q|uAs)~=dn!36x2Mu`0x)&w$s^ifPa-$uj-+mID@)(73TCOUubRP3 zc))(f;8wf!Od+mNSRyK+cTKLGj$ymk8091bH;cMD9zUL9e@xwawMGW_t4;KF3Bo6% zp-qVu-9i!_-Tl@Q8yPL{eb)Y*u!9coew8jg3_d4Eg}p_XLkHUbMICp@Ksn9pUI^{O zsrI3cFUhlaQz-ZoR%_RAXPZWC4K6i!kAz4>8DB(Xv+&`<{)0mf2W77a60K zq@NHN78WQzKEnitH67G+dy~Oz^0xF%o0Kr(d+2r`vMb0QvYnW_(z}v7F(o!Iz1}Q6 zWZx%X#xGJO0P=G{S*ipCe>%o1CCJlX1&OedP8UI^?htkc1??2+TxMs`{tgY9&UWnI z-+{qxE$hx>x&y0lfQRSl=#(13@MF#BoE0(O=O@ggt;je$4OCX-j zzi?!6&s#!aTk+w@{i{Eo);hb6hF+!##WXri?kTud?_5atUq?F$0L{+DDi z`jw6R_63>x1^J!WoV)LLj~9xU&E2?W|B8CU59gY=6D`+vtWKdRV@{bR28`?eO+4U_TyVVO23dsWXZ%S z_n*=WMIW1vb#ZU^CJWK?OUC+arNVqVF^vvs^s!B@-*!Fj6W#TcYlS7AB_774EhwFwb)au}T$ikzo_llP!W|Gk`>93ir=I_Vs|ykaIz~& zs5Aa7RqJQPEeT%}zBX|4mVhn0)`TvL;b<_K<7j6W6ungzAeII+?e5sqvG;iR8PM6B z`5^V0>Vxwp8`x+{F4SJx&yh@a?VLFgvsIgSSZV?_5oK}JsSTXIG3(rYrCkI=MutOX z_XJCo2LVcf_#q=oh`X>}yD5HqDwn!_OQyeS^~NIGcFlH>v4%8+*2gsInmAo^28Lbx zNKn8{W4p=@*R(brXl^`E)lq%e_HNMy4iCsNRPijPP4on_s9;M`tXLFlORUmy35_l3 z2UO?JR~mkvJEMD$;Em? zkWfI5S;{tyRGW(nOeT^1Y4<3$3g(W$*Gz%rjI!Fp{snYhTVA#wM z>7NddG<}Yg?MNxKrrR(s;D=D1CD{NiYqJ(3N`?x@5f~7_Vgzw%DGwuUqGfDpR$ZY8 z5O|J0)!{+^@szL(smdSKPtXi@5BjGi&6ZPA=v7i!WVI=AXqUT^@Ue6>?UpYx<{!D#D z>htTbQ~p#PIA*OotEoM6!g@s2c}gF3K@)xPxbC3p?za%__*QfNyCdH;e9k#sy#0)q? zQl9LdV{Z}+y>lFA*zP&wqKBo!Fz1 z_|dCU&nkUPm zHNB_l8^TI||5X~tTz2Jg|8wWMj-M0lbJ_R(kFOGYx?+XLqkG3QZ@#K;RoFi?ct6@;hcZh z%2ocGR*Fwr`J@2|ki5IO^PQTQN95ZI`^k@wRTH*4uR5tLecy?i#LDN3Pzwp{)v$*@ z-#4GwyWi3o*zwV~P468nZ#&;!3ky6gwTwJh<6gDogP*&{^mGe*^K!HnBWF#o%&XQI z*zb}AOM$*RBpJ*Bm4(JwOFl>ca=a=OgA6eYmvZg{WtU`Gs}lUuRs|dLYs~vO_kOZxW#%T^ z0b{FiUv_0$L3*JsH6c9E@3qL+(-x*KEeh<=*<#{zva>TwQ>`(ayKDj@D-SK(yfeo5 z`(D$Y56}en{@jpHE*F`v2DL;sQ1Or5N8&5B=G2;~6N#TRy$i25D=UucYe&?Ot5eI4 zS@-GBn2zC4K67Q3+nuIDYO*sx3!kERkdN8Y|iOGgDIyKm#(wE$+_e zOV^6ajrE0=_QoH!6X)%>w8x@aQY^>AE=(z1%2mExvMX#NSDtE-QkwPowLE{G`-`l)RXNjVEgAICsuTCc|yw z`pINaw~whxDc6@46~uD%brL9K>$CEdIb~_3$XVe~d08eKrm!_Bxslu%1c)+q+WgF% z*z}CJ)FORxnYqAiVd}oDBc>+nnU?aFwUv8JJ1=K*?#o?d zvfr|*e{U_U$*;YX@Jrm^zGV8WZ#Z|IOy;uq+O|vK-$i|za=qXa)4lcNnc3&px6i45 zJ(t@NkdYXwq1+n@6Z3}Ujmf9|tV5GGES#`q)ryrN)OqLVn6-N%vlr*a8aGswUVZN_ z^+bi%CY((Dj_*JuGd6l{`t?Jn`mKWyyC>o9Uhj~a51Y3^kQ`=1MWH{v@>O?7kA?aSv{(C2kBpPPrs z><{TPxBL7x7yG?G5)iDdBrXW-xp;#v!o~f|9&@{}XV%o%36iMAi|2l%jK%=TwoDO~ zqfK_`%^8$N5TC1lpy?fSqh$q0eeHhkKbC%LP9bje6~J9Laos-j zh7e4b4yBXmh>_`scayiKqMU5^0kU*OX%^ReygN?7?9HG789PMF?cdQCg`Dj1bO<%P zg#6hy5Oq$|+qjaG?-iX^xg#@2#`?YpfB}hg#0hCe8u>1b4&mI_W?HjKGObCiiLHtI zNy)$dCS&vRexNRA>Cim-5=UIpF#%Xg(tBo0nbJ`}G5e5@x;w~ws9$rj*n!$>AmXQ*yee|_igU@g<1~Lo%E^$uWcD&TS4sX&gN1v+U#|N|w45-VI;FIG zfqw0(!)xu@4E+Z2wvD2G@7Z@yxOBpr65BeIhsxTU8bwTO-Q4$tk zNwzh^qM-)+OLF4b#Uk|bP##vfFQFA&)s89MooA#eMPF+qia2fGKh)2fyKj;i3K6v$ zN5RuDh4odOK6>=DNdCV3co++OrG3X`#}4U3&#=p=g?qZ1c6R@L1|?eEr6gIPf7pY= z4(%oU?;m@8_x@K~j;`b4%A2CzQ@z(*TUo9-dh)BI->&*&(O$}j1#tF>i||a;0NT&| z8zGS!&y(06lGQ)BAM%!;Mm~mKhp@dBfAJ0l`|Ei9_gz{pk`}s8K)o0epL7v1dLj{P zG?|T-Y>QX61&sdrwCj;4xxiX7!SgRdf+0_zMZ3m%N*kw?hZ<56yyzloq+Jj71^S%S z46n2dbR8wVz|yWUQk7b^-YZ*ggn9###768!jTvdVx_rG?zP>o!oK3pMcw%E@T#GYA za|X(A3rN>PG=mx?rT0t=XqO9%K^lVJBVFSxS(ZGr$qVHM7K;+iB3+NBOktqLuS&~brtUyYxo%28vd(`5XI67K&m4fLT}bPf7?ZFy)e=a`g8 zKcn9}CMyZJ3{R0Jl!?}p=TI?+{^8db`a)rhBwGvy0!g-b403ZjQJ4r|1BheCqS|FN z_;E)nE_=&$sITq;AA=+sw;FT|01X#POn*|k^QKfq?1O2}7W+-08?@kFGyHZ1!E3yp zQFxI73M5Wn^X$FLP-)Qsg;zv`VS7Uak(MqtjG!Kv1O1JZ6GOSWNo|m)+C*ctVbHyZ z^wQbNGRstw%p2mYOF_|YAf6aQ7mLWDN%;9WpzL!sXuzns4ji0n{2utzcX}SV-t>?> zhq)M#kXlrZlLg@I8;U9pyyLY102_%zuQs~J(2`d4yf+(K=KhH{o77_z3`s|(0D;<> zBag~YNJYpqJ~b^$+(_M)4K+Z*hlZ?4i7w^V@3;K~hUML@_r}(VK}Fia8OCA8DY@&x ziW;%2ET<~_Xlh*$XK2_~Fj2J9ytP3F<&NluZ6nAw&amQ-O^Cjy)g)MP^tjUS0uelC zO*!(diLnMlVnXR24XBhP?$|=CCy_LXn933MV%avxD`8Q2W$pnnhm5~bXHZ_N{hq%1 zXfbEFx$dl0B<2D+Q5lyK7lSg>y!R7~Fhe;oszDC8CX%eiBc>n|-+7eS$qlHP`Uldz zC;{6JJsOFJ?lsav)X&=o{Y=(cKP4;e0YvdHBD#~i-P1^+5aRC}<6pC>Ch2rbSM#xp z)m^dg#FVWL_2(WEH$fk#O^YjvE6%L4R9Y%(iz_oK(@in@IF8I6BwQrQ*D$FW(Lo2d z)5!~b>9fAn{UYcphWf)tMBVQOGWXMq#2Z4fNS0U8HHh*qrYD0r_d*|fG6bg2^B8{feKug`Fv+3+na6?{F(v$XQ=^{I4Pat=IOpOv>>vj=VUVgcL zc08Krvo}2^sA#WgZ);2|1a8P(1KUyDnbI8898|nDWheYNeNe@o{rdg-0~MD24Yw1% zTlpbEXh0-GhUo?R+PidWAT|m}i`-QlP#kb5@=!5h>d7;^zZw6{OSRN}7j?#J-LC4L z?J4ak9n~f6+>h#$WI_;R`4nWJFWkQPYE;5p%sLqyABEwddY~=7?66J%}M`j&OOj zr7o!+!)Tv+0fv&kyhC!&Hu5E6J03m%Ci`%|9`w8*B)|SLu+|f4z@mvWro6Z;KE`k^W?%EV(n(oV`O$)#v(FOQzOxM{>fS9l&RK|TP1&flv#^A(+&EEu(fn;r z=bDxP|)vi%~c?1(jy`9cr@oihO(rpybjAhveZ+VeFm+#p!lWi6Ba<0{>fK$93>1hPBJ&ybFv|_7iAMo7Vu9gpxkCu;@zbaoKUm{;AUoYP*-!9)RkC7+Jb@HR~ z6Y}%&JMu^JKjoC^$1sd5^DHxlna0d!<}(|aEzC}44->+aGF41H)52V2t}r*5JIpW4 z-^{;RXSP2(fOThIVAbr4?09w#yM$fKZeq8wfov!n&1SMKY%6=3z0Tfe|6m_;E?j@k zjT^_!_1ppO2zQnHn!CsS*1v!MXZtJrPwxLl|K9$igk*OiZDg0B41%uR4M8eEsEoc z4;5c2epe91W4<5XpI7li_>uf%{uSPbU%{{D*YSaTI3L4j@g}~Uujlvi2l-C^1b>#l z#9!ra@}KkH@b~%Ok!56DJzR&nj&+^n`m*aB*VkSBT-Uj7a^3E_+cn%Z#x>DZ@0#gq za5cM@x;D8UaXsdG%JrP<2d-CKue;uH{m%7Q*MD3eyScdacN^g5?k2hobsOn6-fgCcW? z4*U|uFI`ly;}rZHg@%7gA@V@v@{`pX9?(?*KQo!1hlNjBh#=MuYs`aMt6Mh$)G)e{ z_!Zddc=3NFF?(6TV@4$MHemV55+M0O6NvCXND3Pi5{f@S!M}=@55|F>(ql;67$VQi z#!1Xl^UQb!Uzt>TU4d$2UKp##Dt&#%d<%t1>tQZx{BLkVj9+r!N$ftC#*&Md1z0@SVTqPo zBWx;O2v@`?`l#@DiAzW1rOU!IixfN(7wb;WR7=bS;QYnk>FSdw*Wr9$QuD%8HIGVg zzX@u7ySi1)6Z1AKl$w<4iKpeJb=x<6lFTWd3hn4 z>BzdySVtDCE?qEL-q(D+{Na}0!<*7uGWTVb=aktBD&bTWR3(?_Y&(u^J{;1(fQ=oL>;jKrJbIpPgPlAuom4vF+5@{#;d zz-#+g1NZ=j&>m2(4j{vjnvw85e2rlV(mqn}>Ot?4W>35j^f&|+t%-@*8A+PW@uAx` zf#jdCo0_*Bix)FW%d*Q<`&&wC>NIv=`z*V!W0pL+u0^+BRaTZ~D%BjTXzuJ%ddvH> zU7?+uD@0RLVt%4(04^K}+%L345^1Zcy+GQLi~sU8R6r80y~d(z_ECh^H~r)#NT-SeByTpYRC$hc-aOlNgM6$v_1C zMhpfMHh7+Yq70;+FVyU77W0^)vA!dyt12YiR1~ApL5DB3U%}-a`1MC6lh<(#wSQuWY2FSV7-l;U-+ucq*b%7`6Ib9V%j16n zY5$>qi$%+r6gAJzQ}gH4-80IiyI-#8o}pLp=M=p&%5f6q>fRZt3SQ#9;z3tGT1^M@ zE)x__?9h{m^%GmvCsD!UQhV@_geZa}1WyNp+E2ifH3j$2TQ1|*t7{ef1jS=NW}|xP z>wu>$hmnE89A70}BVXD8I__hjJ_cL3l>wJ{zNOq;ZYuH0FM?QL5tq;8rRS%c((AXP zbe$H;iIL&ADPV1%FkZnq;o1E3f(%ne_xj%To(&Pz<~AkIW*23dGEBYeyVrY0G{mH$ zKYg-}2xM#R?ugS*Ljf<2-n8R>liY z_&A=@abPFUlw?CpNiz2YqFG#=FLAvhASTCMEW;0&e@ByGk9Y_Tn9)BbsX+rmBw7aI z$&k)gv@DyvtK{Po(o<6SM73o1cyE`}*y;a$@e(WmXN+?8!qo~XD9N)hiSb3y`QQ;1 z7o{6%=AkgczpgM)KVDu}S9;(eu3ScjX5G4pI2$yLc;nZpStp&L6lg*01%LihKV>D4 zh|tGj;8<{e#Wg&Jn-zRSgW*U+1Mg%oDV%w5k{F0Tk1$TJxfM<%Q7HhS=X11Ku^CAb zi3{Tw$L-LCCnQC2sjM+vo?%Qi>O^auk*`+_cuMC>eP%qB`5~_<`0a{bK%Sth(hbCu z#Fxw;+tm*KIdIUm@9N{A6M5;;kp?k|W{Yo3sMA`+NK0sDTx{Q?qUHxrojMq&&hjM| zV_vbw1mQAOHmgg`%!H(DiWiUzjq-{1FIjYAQ1hS*z&k}Bme*--sI`%r*L>uXykxhA z@2G9;dymaO?^9D1S};U;^KbX8`sUQm9@$?LT%b1vU0qK#)oQpPu~1 z5|RI~k7R)Vk29nh(3~XK9zUIOL4~x(n6D0A)&LciUy#S~li2FGlJGF4gu$0CZBt2O zb)|Tp<%O{dJ{kSlmdqbaI-sq`Bc{RA)W>?v$HrxbTC`q~mb!#S6(6GB1M2{jQCzr4UfpzM)b|+6>&*Cd1m?Nq*mP8KhpWgynDUB>cpQJ1IYRZU^PR|GtCpH>Dv$ z%T7=LWV%r;aRl&Ar@fhh2n;Ueg}G>lN1>WG6%`blVN2i@*+8U|Y~zJhemj>7wHweybP}6B8X5@%KxfR$ED_&C1m@@Wa#tpo&|RQIu{=FH0?@8WQVC zTX=%e;B}(;gs9JwK`4%x+xMj6p5dl?@N8R*IU+*D!_s5MyqE_eGrk~6!?Wlp!@x5& zAFpBHOyvb>)LF3N{Iy2)weu?$i2VCl>wSsNR6!>rnm)I4sfMR)Zfv%E=N7|y)ol7) zeqI^|hcOZ*ecz83!uH+39kYQOD4G8+3EES}GhBSFlffum38#9=2qCl5XH$5xSkb~e zcTrY2r{En(-OxDNPGA0uzDQmkhYgn~c%ynid>SMYP|b2v0!n9O!}{Xcf%%0X_k;)h za-TDrl6Z5RHJ;NI6=(3iC)a%-B@Qq&RrGOX-(-2^frK3@0;=11^m`Z>X( zY&S$i@C`655t3m+StJj!Bv5Sgabg3;UJz9>u$}N#se$_QtTb>x;gy6moZ={ko&aAu z(1$8idxA~2aB0Zwd(DJR!(AvgS&KB*3Ug&i1$XNVFX0-x5o_inB%hC$cdB`b)}#>T zY#tySuseKd1;|HOiK^Qqn)n_dq0iDd}CdCJ>1^9%;wli9$KqfXst#{TC2RU9ge!hU{l5u$GDu^pCUj~|^P&2$(s({I(A>l|zdeA5g^DojrwX{*0oX5K$ oMn#JeYGU{pbzL1U=O}QIp<2eKYy*8l(j literal 0 HcmV?d00001 diff --git a/doc/_themes/sphinx_rtd_theme/static/fonts/fontawesome-webfont.eot b/doc/_themes/sphinx_rtd_theme/static/fonts/fontawesome-webfont.eot new file mode 100644 index 0000000000000000000000000000000000000000..7c79c6a6bc9a128a2a8eaffbe49a4338625fdbc2 GIT binary patch literal 38205 zcmZ^IWlSYp%;vqo1upLH?(XjH?(XhB4DRmk?(Q(SyX)W#I)m#B?7N%&@gNzPg3A9y|F{1i{C~vS%_!vmy8pvq0i*!V z04IP4KosB&umrgOcXRyD0su$=wg0R&z!TsAFa@~%hfn~t{zKgUi?RJbIV1oM026@a zKV<`u{HH7cRsj2daa8}Gnk4^EMF2odUHbodF(eRY6Og71NK*#{I$+FQ#4RkN>Xu5t zDV|CZ0erHH%7mJ7f9C(hMgfc`(&`gnuuiqhEZtN@Gm6qm9jtBTu`bUstuVt`VE1U^ zQeRP-GNx@G1O+8HnNjpn78T|1$sHu=pO{n+?Hbd%?rXh*b{x)ZZ9Ey*heliTM$ph9 zeSOvxJI7sn2z_VOStQwpj}H7Y+@M&VY|#ngtbu=`HY)^$pT2Bh?F%Qz)A!hd^bxco z(ph?3k$*g}cpvrc9fcXhjj;5WPot~Co6>e-hv7*v=?ht4ZzfafOKSl*nvanjGNp%5 zqVHEAb0A25 ztDEMbuMI$uR5*rQ;Ex2f;9~>x3rZo2m^kwR6UQRPZz@Czx8NQJM6qF(2xu!inpqCE zp&p-KF}@yM;D2@511uFKw|p7`rR5E%Q=P-zPeXA1Ktriy6is`S1oMudP6;lGGo*>+ z8#MeQ*S6fE;37Z&V&V2oyeT_l1gp@&a)ah*E|M@ELRv^E70jhArQEOCVR(XrnfK5q zp=6hd;d{^XAPeI<#-L-CBvNu5_(Jtd*&!2*tS%|-yzds5)A{0f(w};Y^KBe@AdynU zQL37Co!%Eq%0_)~bcR`#k94J}qgc4SSR@Ul!8_*tW{Z3Z>U6}ivNUHWn8P$)EbfkT z@k>R%?c7o_o;AP3>Pi=p)K`@mYLKBdm&H(%0ai{ls$|XAptE5F3tx6U{?(i@T>GA3 z^_!F+A*NF}bxUB`5ssZLyE(_w@^Dbsgs-6_CGq92Gx|oi!cA-HhDACy{4K)xs|&hF z>LTWj1(w}4LTGz@)0q87y$|wm>pEPvgpR{F10WY$v~2DYt@t>2Z4;zPN_He3aPb@z ziE0^tt>sf2&yu8qR?@PaDB@HEgBHaU>ZnpXEB^D(;d~K@`H3P(?)J@Vn z@CfT^4qS#V(v@+Tim_UUz_Xd-$p=1fq8#h)@{UE|bVYBR`b>ehNCJ;D5bU7L26}ay zF9bjM0OWm1Ao>6*BK&HtwoOBWueI2fo{G7Y(GD|!_MzfV9ur=<&-+oRNRfybM70FE ziI3L556BV<%TDstB!_UPon6HAw*b{&kueNsC+=#&J+)243^;t8PopRU4eb)@)UjTC z%|J@gDtLqz=z5jdArpDBF8$;L=m(uEBXxr?n&v3{9kTU@&#yiW%YPB)RIU}%aSn`6 z$@EM;F;6}0Oe=&L&gfL&?rfC)Kx@IRPdd3jy;|W(cPJI&mJ)b22%#Jh)6+MBXi}{R zv^IAae*Q9Ff|}Y>L3KPUWC=0h^@i;U8!M>_cS{w^1mL3n#)V zzLDJBVg}IArNIql9*}a_j5k%x5~ySF{kx7~rG&ilzkAtDE&P%=41?qbzUVW>mJ;wI zG5?8dPhnkm~3cU8v`qiyh&L1E1^VPh=!%X+Uo>1c96Q;$2#!T1Ajyyr?xG>dq*93%MpnA#<7B$B#7=HPXzf=n$eqoJt`+9|FBhvLb+Wa z4m8GHx>=pcMvH?ROyEX%6zNvTMAD1qZ;AsG_0HNgMRs*xMPr|7Ah1x>6n>WIU!Rbx zAYDQVirff^+o%FmVd0B_;=cS=Pb5fBM{XhmuA5{$CX^gd>K>tNd;Lue-*M39)i8u$ zvloM|Alu~~`DW*t3*x9MP(pP*a$yx_Za4IsuM$&kOP znIjBTyD&_q?33=(F8vwuz4}#@VC5b=BR^1qta#WB)w-2XWN|LD`9AlpS}&US6%rj_ zR)6|i3w@-sbdLY*wIZzMyd+h(eZ#``O&@Bi9YU38yi!ozx7p}(2j2!@LD^z z=Hq^=#||B`(#WvR3+)d*sr80BN|Ky6Jt`#Qjwg11 zG(HT7qi~b5*RMzyF*&HHxNqS2WkJBe>I_J0^)kQLmlNmelxf#>?%GJIl_lQcfQhMcCHR zpjs9>tRLYo;~E98pm1*t7SyL+0x}cVhI- z>CT#lG-N@6SO=jawi;8;(_?PT(9ie_1fvY;Jk2=I_w!E z!Y^R`3t#8*m?I|Ud>4es$FXWl2HUO$%~7*kxDsbkG4Q&Gd8^ez857WVF=K{GnKur# zV9TxY3P)fpjfiFra;dkVwPR>95jhb+kD|;*iA+l2Oqxik?B99KpfozgmzxwxSylWb zg)%DWt{5oQP7NgLljJDmH3}IPvoJ+PtxxycCnYT&69cDw>&}In&F09a^uTC0WeDa( zEL8Nxmcz5q4LfwxV%sU0hvQRh+z2C;vEp+E2B3SEF-f|#6-mSx*mK)c0$fDM7kPz8 z?`_-7=l0}C#Zht53SIt`Y4vfg!7WuL-bBA!&v`K(@{u2PXiuNAgvs0jjDCI?mYq<; z@mZQ{ZtFKytujvz#Oopf6!|7kA*r+I0ob}^W8~7^gRdfY+9S_F(zSHB!HwR(Y{(zI z-ibb7)VpopINsALOXkwt^<)cm?aV--LZ?;j*$ezC^n=3iBOB=!JGQ8>rYy~O6p6Wf zY~=*?XKaLp<&Qo6W*RX!e1xBb&9_ct3YV5z_iE#2JViml)_rvMZsp2wS_7iXxJvew%gf;mkQY%&1+`Gi*e*2*B>O@GO()_#LH6z(C{)jcjQ~2H z)FMk)q>Sp8;Wk^A>(}J1pqse|RN~jF+6{lt1bbson9)wiI+YmW7Np-sVNxH|T&AA! zBI7Xjs!)N);7)_r(h`BeuV_SgPbsHm*uRBUVktIpforWVBjVz-avd%1F&mvltBvF? zfNt|pMlEQ@*r7Zr@j1anSI{yWHPQ$!*)ikAEYb7Vw$0#qFN1VR2OI)KFA*m1z+qk`Qy*pW{`d{N@Nn-0){$edMYF#Lln)aUBU%x zpbeNn0tProp-?4C-fLh&EA7jUs3uXR>mE(WMi;sRvb?M`LI&#S!`abZ>*?LAUzBEv z;)Sf?7eJk&T&RX^Zw74e7XPe{@Ple&hu)^v@rLAWVA)heayJ-&0YhI9ste5a#M@pF z()}*Gekga)6xf{ah%_;p~T z+j{vjFu{}Ns1UWUeQeT)f!3d>d;a(X|5DX!wu&XZ9eRYc!uzZQ6r{8oI2ArhVA%G? zHyb=YT19dD63$YpPa%n8ND7_Z+Jr5NQ>dEfM3VIVW%dBxo*UEF9g+=Z` z3D|>we0$`qMMT%+#&?bKsMuGo8^3qSNM2?u$wL0_nc8UkL68&{gP*hNYcXSBRb%cB?pVTSk*kfIOciI=QQrZ1JZwiYyN9#?{qgO7Q!32 zgX+p(BAS0u%GTgED?@bG%^)gzHm;AuU5;tPf-`#gsCDOP-I(3&c+iFWwqT)~_?WRs z0IY9YJeXjU!Nm%OqKuR|k8Mk;_D%MBlM=Kp?lshdEZwvMKMFR{C5D4la_j_TyeaQ~ zdSvtTk@H$=sJHwFks8_|tO%{fojwPmtKj`Q1zQ>HauCfT53_ze)l zTG-M87<=xxy| zDdO)&IMC;(lZM18FVB?v=R|Rw@)!k9^%zF2N_oFCDrd~Y_ws}mz~dKX%-kV41cU}} zQ~qUWCv|=_P_%uplL?G&6J|d>Wk_c3gKFN@F)jA%#ii3cI4UcpfE7lu4V5L?>N`$! zk)h#WZ(15(Finwk1ceGKs3lJx3!EAjUatNdO{TJTR0f@n1S1an1=2=8TU1Ml9{F^EsNZr(g5=z%U97>sgM zril2uR`W@#-Wt5t4Bn5Yz{|T;kcFdy!DE^@u598ty3OaS54s~Hb)tkY7zz6}Z_G@k z&5BO9g?I?$$5+Ud9=`SC0y?M!A2=yUZ(a`GKLJ%Ec-W*#J(z zal~$;zmv0W6y8{yxu3p}rN~roYmS7RdYm}J=#D391J6{cb%T#4)$PQp>Q8-uV-c7&nmY~uoMX$~7PY5dy=uY?@pM1GFC@wI|v|Qrw-=$Sf4{wk5&4_=sF>gnp z*P({nvArrS(l#^E8wXB^60 zjj8eIprA~2PY#gR{Q)B%m?ITG#X@32;je#;)B6g}9@Lo{@=*J&tl^#@&d70hV zqvdqNZSrNvD`pj@qo;n?u+SB3dYiht9J6DcMtae}KQt|F%fb$wYUmT-k7u?}UG8yl z)Fn}2q?zp*uBGX@u7bNWI76Nt7RMm)!sbX2Hz;8bW%E3gv$UWV_F%`6i4Cp7qpcfJ zDggycgt){-@q3Xf(|fbVc=5I>92_~)!?urM`!cFbfKnO~Et7=kL&!+Ci3&hjX#21i zKFjJr(e$x^2(e2@eFplc?uR%6Bo=N#WU7i-P3r}$20vvC5=maef9!lE`8^MhF~c2C zpe=9m1d%QT;koR$`WI=uIaOv;*&wjp4F`WIs*eFc#p^<+tI9=knDS`Y5Hk`w5F|r_ z4?}k75;f>g@CXGS58Xp^u#Y!M9~*|c8HAWY>=({SS*)Ox9&@4z<~uD-@;AQcA~6`) znp0N7D_`!W=)@bxJMyWUz#U*pQ{cN0!i%$t+J2M;9RU6#E3;dfkcw9t9*NT*lcI1S zbVTz`ZG|Ev(sHZt5`F5KoNfAh|<`q^eO8loN$OjJIl2#PXtQA)~wGv&f^-Al_TjJ58Pa+M5kmz-NhD0 z>XD-aM~}AOprfr!hqfUw;f(eLw$1NUyo!L*Yc&h>8ZR3PcRsr zpYsNmhGRf-y508v%`$L8SaCUt#Le-|`Pk(FB`->6b$q*QiU>;5;ZO^-`(W`&3^SQ( zkqH=nN4>YBjf+!y{$c`$oM{CvIf05nmqxq36o*w@|2|2@sQgRAPEnrIYoiG6NcTuA zi20@ezU2fusTA{G1B8BuLkp+2=rSrPB@K@xP~VI_i<*3sk11&W&=Hk2t3r5-zDpV6 z#dQ?z6_e_cU_h5fCw*a;JR+eAljWPV_Vci#Oh=B8idNeaXLW~$1j{iF5rJu`*b1F% zh*c0OefvNb3TPm=QtqJnS&kg0IhUac=EH`4_JOdO2>dyQq`rdoW9z5}NrSU|aEVe@ z!0U9?EzH~X@v58!f-M3vXUndSwO;G6qI#e7_sY;FZ`~pD{4qHs6Dq@w0jvTvuB-~N z8+2+lf)Uo1oXzp{W-SR*n2#9tSW9am$`FVl_l@Qnkpcu$B>@qN%5&yQ1Sw+BnKemL zRfpwW%f=D?SAe7)%1{97X=s}IQA|YiL6S9K$N>{4hvtXo3ypJsGLwUJwmpXvvPb`i zPkFFE0I#G&1qC%RlILTgZcE(q9+YC<%6We|>5Vf%t>CBZCH(2j~p;r3-+a*1_ko zbDXT3(;;8uXXy6+1Dk)LQsHjW_wQy>RZ=1Ndb*^$3dPZD;?iXgYVT4mXTRmuV@H@d z+u^8>gmn-Ztx&?PG9OW)by86jFo4ZHASsxOGZ=Hk?0FLtV$3cds2baN$3E4A#Cl31p{Ux18pUuLY!{ z4`cJ3-aWj(HRT`W2eeMg9XCNOM0LZ3*_F@?(ptb*MXl6wMq(2O8`(E*p^_64!N@mh zN}T6Iy|eL?DEPiQ3hfe{h(y80^dA*EwBR9&WeP}~^-1)Q!~NsxR;~NduFokawu-+X zBk?;o@e$fU1Ti{AzikyOdXzd22eX9kBS`pQkdEjn{K^EqmgG`{$d@+XqZ9O6SY_gu zVF`tjkVmDrsCq}^dc~hYd`tGM!y0j&M8QMw%5XSu{5J^=s>#z|3VD@{Gx!}uptysk zT-+YXFP4p2TEnMWl(`?Zi-2;tKPjKmJ|@->q=`h8(^8lcI;rt9Vh4rL1X0bU&<>to zQ6;sD%}9Rgx_URn9|V~;>{Y$#W1I~`l^ZP`I}3}K2ERDD$UwHe2|PEk(Z?gSX5)<+ zdUVERMQ8fU8wU?*Omoc^6-f@ZzMlOCCI4JZ6pFU7w%(&U3w2ffD{wNRM)kBsFp1D~ z$hptcdV!tgO9it8id@_=mRh|S1`n@*{P87e8yPYawPY3Ej4zfgPmjpJt2xkQ)}yWE z8!BwmbeSH$?$nPCXocC}BuHU>8G_#JzpON-o8dHDrRT}GC=zG4n-7RYj5gxvKZ=Te zSOn$?;)Y`Oh+*oP4+?!cN|V?jhT*7k+1UwXf3vmw_`8RK38Xw0v`a;iv1{x~`@aLM%hM*qtStGVzXCYf`q* z_(Exk=MfFjEUpAv%V>G@&>gR|FJndsyiouJU(}m+h$7w~k3( zW%y9pi}!Z98ob(Mvpx~OfountwA-jxjjOYhbyE7{fri?p4n@6qdH^jr7&38fVczz`O5|rS zdy!`@=)KgM`o`*xTGX6Xu3ZvA3j2C&@tIF-vj3*NrQ~{bnX;X!<-Ae3z#`X$V(A?- zR>Eba34!GF`jUademjbn#TO6DETFmI1 zzS4Ag!l8Mt{T_^WuF)6(;xNHm4}e?OJGCJrNUFcL`Kh&jmc&pBdHbLT;X{(%Yck+$ z9rjdgp4HO5J=y1e6o0fXPkuh0x`e&vK^jbN zLp|T>34R?^3!C<1=U?}@-t=y2v*M`L27Wk8BFOxfx|1;Xni@||$FAh)b)?sBW> zzw>aD<;V80(-5HXqbXyvg-F(qA6|AbNFJ@SK>r2 z1KK76v~3*m5M?RO@~rZr4@<>T$Pxjuw=^e(_#E?V8&W8b5hz8G9Og?S%wxe24~VR& z0*ZpRTVmJdRbj=qb<5uLm(abvLXYTU9@-jw)?ms&mfc8AE!QY0D)J>g-lmy@O#5rY z6WLsH{weaGczE8jONV{}7m$23_L)sEBHTLA?Zbb6s1(3*q~4x|K72BGM_9-U=s9sU39y!~V5p@k##Z1v$ zRm8R`n7%GrkuQ9-DMesZFZqp1B@nB$^Rq%jm}XzRNYPx9EK!;LbE>VkX}0H7VYmtx zJjuxDl_{Gm<0co4N93{5g1C}PR|$ebo?XxyrGGPoPNS1T35K!QkOYXJjNv~{hQ<}) zj=PwUzrPmNOe$M3S>%bIQ{zQ?gB@@uBh3V44xG940Al0GE|aM6Jr(w5h1=03lZIFbBq;fVp3GD+(ARJ!+=|3t4d~)LXIZ2?0`BfXcHj8 zbFHKWn9noh6O;9%f2%6a{o=6@ySg)Fj7Dl80r{ry(Q=;~OrOv@ysCr@xCg4Q?h) z0>WslwOatjzulyT&7q=aiqW`VEU)869Tu$`L`7jXD3k3&LeBAPXqa?S`Pd|7 z2qFA79}#)cd|QZvZPO?h+Y&M#*`{8bO5oYngy#14(vLt|k0Chlj3L@1ZEP_ANPmHY|$QXQ!wD`4GueT7t zb9DaP`^6}`7+hfI+Lt3byh=*|2RmW|5RYL%|k;X#f~6nsc z*CEiAl#o!);6?bZ&&7Cuw=)?`YsI9rCORFy;ceZau=(}DK+fzi?8WFD6_MBMG$ml= zMsh-4ss&nJ$hgT~NSX41@Jwctel6t^3f!aS7D~w?`X92Uy{}4vADR1Y?ObuRR)4U} z2pv1}O4qjvl5YamQNHtoGN&HSZttO^zz9Oa6hS-=n2);DK{SzE6Q+vde1;^FCjSC9$*dy_*- zJ%hTbBmFU~CdErX%Nyeb$#OsI&ESCeA;@k@I4(q&7^1U1`s(G-VP}*LfJS{r7`{#t z3XBp#j3T)A zE{aoA15z}9lo-8(YRQ(SblP(l(>v_To=WdGwoOA(@uxpNPV2il0IpNJ2f3e-`Bpo!hL?RGM5E3eh8=8p>5^l_lXR9EPYY1}o z(k*0k1kU9Jyl--}Xw&XwA1P8^Q?cdv!cZY&l&Kq>B9GCGmdj4wHT^9dwMXYPap)$` zHcW`T%JL;fA%H>*c_mB?l#JLN?qHDW%PHjlUn{q>GpoUxp}-?hslNMUVKQVajYo`7 z>$&QaAbR9@gn)v*X_q1S^FTc3n^;^>(C45_gJ;x8ksNA!J8?Eww{X(y5t1#x)f`Qv z$afQ#`DUDiAP+HE#XzFQfSdoe-ssF`yXbms&A6+g4ZQu2BGnb5t5;(%?va?q$&kRJ6O8P9QtkTz$f0HLozGu3sL1T)XQ$jv*TKZZcy0*t| zK_TQs!%2>%4P>HGk!Wh`(xKdSBv*e;=wIYw7-Vd3f_575 z(1=MApsGiLJ4hjLR@)szko>7!=Mo)iqa96vMJ&dRf?a3#D;$evQ z{_YY+Q+@rn5PCc^9*jnFAMTfUSH-g22#!1STP2Pao1A(Ln%MXc8bY?jv~j`xipY2wT{IOb13X&AJk-5nTR+wl5td2i1=+j94+tN z#ltppQ4jMkmI!9MfaNY_6h(w`qsE!^;@090RmQ!EZH8N8Qs0vKiosb!dcr~y0z;3Y zc?m2$yi;?v#SgG}?w`?N$lDPxJUGnrqzyF6ECSA6iHE zMmXjfI#M|SwM2gyozz_z3C})%JT?s!dVF)l`84z(f|d!j{UQ}Ap@rBDEw3W{Itg{I zNJZsRdQPFi!zloCuI^&>(+Blj{~CtNs_W>xFkZX125*_wJ98t$i=ehjc`5@(yd(2u zT?>W>QqvI(U(%#Yz#1J9RBWcyAngI(;j%jXs@elcsgk zjas-ld1lL{O~fH~9q|_tC9}!DV`;gM=*! z8ip;mpc5sz9uI7RwZ8;>dJ+ele$aWeoXuWdAdG)CWRFuFEcP@LxmdwxSkc?z&}UJ_ z08WXvLj!wjn}~#TCX9NPIc`2z*W@bg%&xvOIewG`y0STb1mq~gp%uS^6(Q2#as80L z|18VSW315517}JcsqYkA`{6di;aW;2wkA=R*}KLiI|h=(ZGMB;EvE)S-hI2->&k0% z9XqG;&yK?V5qPfiI~0EURzMh8%w+%yGtpQbwTJUzWxcJ04&k#-5q-L>x4-B58gbL6 z2xm7dvGamFUVE4Zr@ae^f-=YsOjlm-GtAO}f{z+x7G{VW%aDvWBS9C{t6kOzj6H0^ z8YEmZmqmb$bHtEg+s8(GP#b=%AwIf3^lBpJg*Iv)ludv@gk@!u2{OHFA6|f=Fq7aj zD+OB~lm_FIcUcWY;}m@2*m(lKDEH|8!o1JKb|~q19`#wLQ_GD~ON#)q2!G}Hvt*)$ zd9t^xsn0=5lknsVSWEoU0229mEB7LcH>W7Vgsl%_@8?~uWwUD} z`XxhMRw~@(gYFi7+syt*GUAJxp0gKYG=_J&X?gwDFQyc*lF^iqR$g!<7wKhv-j6q& zzvr-n4l-w3hE0T=>}pxf__W3O`L&E&t$3^wrU9$^^ zTq~O8NYqYbldSWw*?>enK`TBbRn4&WcxtJ4QS?lHx}AtuYG_I?@`rj4X*rCV_~hukuD?XojV7i&{J2ZIr-*=BAMJ&k0JU9NIq# zkz0mMp78F9fe^?!Lg>!&0Zv9yf1mgsQlc6Q2-;;B1cw%=UqR+R=4DvR@&Cl2mBVKp z^$`k`%+4)*RPDpZ+$`m!LPH4&7pOZJ^plAKLhYLIT;iCK$q`45h2sKPP+o4cvJ{4+ zpZ%hK0QCWZEa(A+(-JPhPI>g+A@NBZ4C1@Z-ovz)*y?$kP0pSY@G|23zIIL@AFT2F zs-71oJ&Y}5MHOWGq@sArAoRIn$v&m}RBSsfUX8-fT)OITeMh~nx83g&vx-Oqcgs|* z0bOZp(4vsA!q{KcO(H5w3TQmzrO>)0VYDJ+$~Uf)iS6H$2*$^fsf}xz&Yd&Y5X0HZ zjHgQtaD};It7$bx3Z?b+Fq}>o!)(VO$Jw!?$W@^;heX|Rh=zOW3}!StFr>yb+lI=g zJcd3Yp$`6a*px@(a0;3x=(&u1`w?jX71o9Wt9FhHFEp(_D{=3x62uA}6M*ayf6r`9 z{auu7q^{SrEDhaj2Rnth^rvap#Bh}zQhGPu7Cg6vIMx20KW7#nSo9ih-fDL||8rD| z?F30se51-f=q|`|T*15_ITLh-woarjY*hr4YRGl)Q{BK8@AEZqf4Nti}!Cu+IxrT8t+nm2+GO*-^Y=+7-}W$WHpXp&=F_>|8~SXJ;k>(5GYwS}>~9;4YWl$R5|{36(|VO1 zwA-mm_p+urSKUi)o32KYVnVxTZ^R6m7W2CBzih2-%sCYD18CZgOx?(EU;#>TVzC z00(zo?At;%HQ60Bfd^w)H!PbA>p26=*O9x30bYiwULWM8Z1)w>k0~~hV*-x2hl`^5 zwvGQLmgWW69OCf}RVH|!GS^Kqj3uFc*8R z>e>_(uv`W0+l#JF-(pIhARC;Vf_Ng2GxaJ;u7u6$exj3mrNpQ&j8R5-_%w#@_dyFn zvfSFh;%61eB05sSi z`Yhwg!&_DQtF z@0MJfCj_nYMS;n0llhGVkt;VYD^)vdca2fi&Jxmb>Q(!TcrtN+d|{4d!pqNB58zvq zN6-gHE(cK#CVr}E+uMbADdD5Fx1CzLaF1G$h-i^8M~qM+U23HtrBU;fPGThCE3r#% zopji+n%!Bnw33WI6yuFBU6F8W<0iVBzZHiZWi_U8T>yt@>h4K-BC1D$QCEsYhW~%%K(pj127tbyQhk7Ay!gYzjdO6Jt%k64wTo!kNfR0(2(dmneO zNT(;B$nIq^p)NRYG&JB=)I$JLR%< zzmjY5$0?7q491IWEL@6lbW(tFH3cm-iZR96WL+7riuoI&%Wvc%f~Rk&UVc2OqyLh0 zt)zq%Ry*TI#p1L$g8ypa{k};(6X(P$bCI95$H>}a^Py)5qYzY!9`U4vuN1P2rcC?$ zlVNL5_VeCzjsC-y)gptp;v=bE95bAGZY=oqD|OdI`#wjEs&x1K_?Vh-aSb&0BW~pF zs_jI6Q42NGbW9u1-kcK!^Cb(GHYHzs2!5ZWm;*f(d>Rf96ldZ=5^gw|n50nHT?n#+ zm;B|@@%4;pV=36ej{7<&-t{k{6hYExI-_M{D1Igphg@gvS5->f7_GdMA|ZD`{{(7& znEZjFK$xuM77w{$+D~*8T*P3WT1s#b5Q4u3&1k}6%e}2$Kk#&_wV}x|e-b-#^-6Fz zYTo-I_g zT!2Be5zcJp=#oOI`tRcwDTDphmGbYOy+Sz4xg5n@({V^nWI{v3uHv~MNTwqAD3yoo zXuN)7AcX>t?kRET5$a=B0h5q9xBQG;s!LDHZ2bYy^Icm_ej+o+SP5`$Jv1f%z~3yf zP$(J&Gv_JQaf`vy|1lauI~cJY`u7{0h;ONdWBoh;0Zu|S9*(5HDdOq;z-DAQ83$ua z$3$3P{qZ%b;Tr8TR6eMpX;~)9WQyE7>E&uHhlxf)j?>=2#ILCvT8Y37Yr(th(MYRWZ!h1J(B(s@fbpan5 zN!;*SXL=%wfQf*u8edjrRe}VIxd)(`@`S8pv<^cB3GPr~O5j%vV+_XR*J?o$HB+kn z4Y9}N78Xe-Kgh_5F}hK3)kB?}_`hl5D_2M)#Dg!nVO|fcgZS;a%r)26Q2> z5s+VrrE-t79bfCeEzP8gG@&>rv>9OLf`*wCd+8eHPnwf^d1b6*BBP#@uy{NcJURbR zn?^PGElmeWUbqANIGDFOsRx{weXt5hSaGCZ5!UuYo_#03-SBZvVyOHi@C7fKc={u! zy4obhWSV$($=o?lSk|VBEosrdiomxzXx0$?t32;oPxD`smBja5{XM|GkytzG7HB+i zI+_xONpRW*Wd-t^I!(3t7vo7RQW9G!Ly6#|(XcAj8qJ;fwg=fURXgNm3T~Jf)b?{AxFghlwu)YxhxEJiZS)NI7FL&!Il2W z_|u~DS1!2t%?WR4WaN05$M-KE7P>R_b}bE5?Q~_J7SKG$*`2s}@rt`P6VF%tDnv(# zFb5Oy28(nbPf?AV@MPu!z;Cr6lx{K#EY5&jGQ`6&(#r#JWGyDOXM1CKL7XH!)0WSWHc&>o0D5 zS0bJEzjr@awn>pb_vpmH0}$;w3^y;zi#CF!#oTN1wYo5-P zBKPi8elw+db`nlW#MhUR`Gybz1|~kx)*uH6Wzad z+4w^?sTHI3FOWV(vrBcNKzGJ*RG`C3rwb)b3H zG2>8)%R{9^uPtgBJe49tAcmer5+`{{ckMtKLJJ}L`+>$>9w!FziW(a1tEOp!jk`8- ziUe|c5+g``wWAGqkR+FCJMleG!nIX)1Exf!WgJwMv=+^n(5_Xq)Sv@`bj(;%W)Gzc z@2ZB@YYM(l#Z<}C#p@me^!LN74(|KfT%uUcU|}+(B_v$!tp1Ij*ivQ!BtjAZ7^_ZW zOr<@(=633BJO%nWl+>z3PW^{!OSd>f(E@ozDI;uR>SxQS=K;IGAvIp9NAeyXR&TQA zszK87!&H|)M~H~41*VL%r0>+ZHg4H8u5s|WOK6Tf0x0}ee<|?ixzaq?qNg0;gBD_S zA(=kCH%5uabf_=}GKd!2$Hm|v=pM*BBGu$WN8UeUKFk(Gu)XRKFBbyA5bdb9su7m6 z&HoE9K+nHtmRW0-n>^F2HS2=1!7d-&=XPeK!D&joa2^FQ1^fOmsnrrI8pg#BK6(W`PW8j-?^%>Y%1# zJ?EQ-4xVGt)JO^*IJ8ZpC%76145J*l%rM_c)PW==CPc^UnFSlp1Zig~W&`_FpnF1Xi-ZmVYk(M)eBG z?*xE7f!3hW&5p7p?Q*68}WEeih55*V?c8|1V$59nxh+M6$Er*@mi zJXApP#GbfKPF`P$tQWePqVvkuTI#?in8t{3n!IC%v?}j4r2w!9kASC#R=ij+*9OHG z#-mmxq*0CxB=RJDD0w~`DJD0d)6Y1526{m8RLF~s$q&f?Eg3~%@3_}Mp{;>m*~d5x zoZNOGoqVK!^*FDEN9}TgK*FJ@=_DSdb4rO|99j7}i zg2nv#36Zvh+*I&0=IS9z8w?l?ItCn>+5A{|YTrTa@BDjBwGKeFmbB{yd@O+>t25QCl;N0D7+GD{+rcr@YAL>3O#8Ao8#IgKqSs++?_8G5&SD8{oeu=_d^ zPQH8nD;}21YI&})RXV>w;%I=wYD<|FyXHY^?LKFo-x=#7y?7wKIv3- z^qm1Qe@X)2nhgT%=@9hxADhYWm^{Tc@-FZ!qeoY1fk_A4>jqT()5WL8QpDkH*#t3V z^q6CIQ=9(-bT*R}(w0_YQ)=so&l84Kl+Z5n_IM4D?fNXDU3A8N-eIYMzQd4^ov#`b z=OMNrM+ovoct55A6Xn^vCn>bwjWsr@k4zjGJVJ*ReuHoK9v2Q2k`mb`A}H-Rl?HqUD-6VE}d{ zKiY)If#boCCP?xG(~-F)BEZ^#M6w8VRAdwTF}}APoU|_`X>tS2)FX#}h+&5MjMjD_ zNb#H_>vxTmnK@S6zz3gUX{Kpb!u(?ki2ZQLB(z3*C~FZY%k+?>R6`9}a17CzKq3IY z6og`t1{o-1@G2?dYR}K$O(bYXbAjQ}KI5~Pqd(1cX102Xv!a@YQ0^N~#8EJ8PR60Z&V|tu8sG~O zUg01sgSE;DQ>mer!Ua2@c@G^BO&6vD@JGmi z&U46(LZ0n^Cm*K{l&cM()za{B2i_ zza!H;u&@;2AN1^9oaU4d1gFo9wWGCeFu5eYJeffpbny^_WC#XJ0Az(?c(*5u!ww*2 z>4*TRoV`h4lCeIr_;@H>rQhFv7}IeGP#9+H$ufm90V#rx)8afQ7Sk}Jj=ZAuQdNny zrWg}qxG6*Hz%)puO@?vnTI;SMggHx7pQ*lXs2EJt0_EYo7q10Uj)2(Y7Mn$zM0 z2;K!2GTt_#I{tVG*R7UlY{@JXLCXhHjyR5jquHnq%~}aRseT#fK(n8n7gEsrC|t9Y zeQwgw{od@g)ecMG4f=c`u!$W98mz;RR17*_1`sMe6pt1vuof<`Rq6V{GN8pd>>HUc#MOtPD5%F% zRl!K!W7Fk2A||J}`DHS*>7KUI?Vov+c2P`yJ4_5MQ4$6eKwPqOdmn zV5adY8IlxSSb6$&EFypH8%8qJNf`X8ODmSwVUgNf07D@1u`==`G1{lR)nCn*?Uaze z8ERJpU?O{DDgeEP3u+nP(dnk&8#Nh(@(X06EOCgvgMvge;pb%p$82x+-$;n}lc5hp zpG$z+hc#3mp?-|6fOKsTDN`FHP^?NB*PUqO*%1{BycWECs%9*x09AB^as8SPBrK=W2-Zg zeLhUvw{SegHUv^P*pRj|RI9YJEHbq?Ik3&E3*mcMp;4|kJ_Bkh?XXo*kz9jEw%|O> zAdP*cBGgJ0uz2SQmQ0E}jenNSVxtW1dv@lN9q4kNGh`W~&}NT9s@F#3veFQcWS1y` zA_lDmAZ+3-4aow?Kq??1S3;p;E5vHNBm@9?+>D8%mIOHPL?$WL5dLlAqP=Q83Q;yu zS{b-J7yI6|9OiA4X@erlLErB|?E4i*3?#}l>`N$&p8gV=Pvqr?ED=fjrWz>1E z6FUJJmx8-a{V8)|W_~tK!M1E{FWA%5M5f8uw@Dd8EY07aYO(d)}rCQOWY65heABPXqQErYW-2fDnrkO ztE2rPTq!g!0x0Atth5e&kuT<(yv#_BF(!)`^SNmJ#{k`<*_prG*ZZNUVx-d-uMkDp zqEKQI!9SFjt0+Qtg)D(CiD&TKLOfrp4g}VXzzU~20OcdVBM3yKcE_5dW@g&?l+>7{ zIv^^qF0z7I(G0j-EA8yVXg&h}`xcAvUJz~!1AmeAS2x5(3a!zyC&<5RnWQK-hqOd_ zc&(bTi8g`G!B9S3vE>@j!HHKS)Cp5?@`OBIP{t;Eh`m;7d7&DDdR06-zI@Q&Zv-Q6 z{oV+P!PH+yFCt{2@6g%lc(b9)+5om{bif=Jxh)rOjZS!2`BEG>Gcw_ZNM5K%vaD(tF!1aj%Rtq_uY^j?pqW2L}L|!!!mNkhB4gzT$Kjv@yA= zJwzG=JTL{22aiBJS5s73{;d*vfJdsGM)K*(8akWp3Y}5?>v&b&zt{&0_g|ruU3^hPfd@fw*3_UfnMaL&{H+@!#6amQ70ET-< zu|Ypz1`Fs?6q8c@vmF*bieE)i2%3jEB6eIxnYLdXs1Ypzl<5;IWn&Y#J>jBb*0aw# zs58CR#-X+&j1K(EE-YHLf{8VZe`mqWH?1F!a9p_HrTLM<2Dz}*rq39~1`Q$QRL-C%0vP5VD zRJBqG!^prX8%vOQ8Rl>)Y*PKEMEU0X1_6a1L<0{AEQ-YAIDy89oQcuUb}=VR@rBu8 zxS^a4jNSU>db0Cx46A4zlb0|pv~5w4(c?Y5GGSaDXCX!{au9dzE*%e(k-{o;TUrAT z?EJxOx1|o@G_ipNNf%>syK^T4yFdxqVnuN^N4mazcURzTMGoA%!Qlgre8$qF+&32E zmkbg_VtL~+4@!v(%fsYHoQpl|MfFJc(u-m!lnD4mQvMeM{-EE5VUY#LUo|A1)_fqy z4e46XLQ%odYP%q#{E9P%MIfveEH?7bM{63%dxtUDP6Pti6c6&Ic?%n#Vdik-WhiVY zI1v_rMF!~t6aU1NDHo8)**-``MT3o*Cj=*f;-8UE;caqdzezL2pO{6hFHn3kOji;( z4EIkc;b@F){zhYjuyu&-O=+d7{`fV5Vs^gS}r zSlnz8Ufy^}Z1`vtnigWm!4?Xime#mJM~<5aKp>h-1zL~HA9X?et-KMkR!ZBBSEup} z<0}P0xUD5UK^yKajIh)6%pnU3$6^cnUjs^(WJkRmGGqQn|94Rz9JC3vPHbpaH}2+m z;UNGc>@|wGTc zn*CC)q?r!38f)2vsgP0}p({#+tte3(dAODUxSkY_Xp6WM(ycQlk>? zi90?Q2y`8f__Bj69I2m_C6sx+$`Ci73zahi4QQ#f7PvCCC--9`@nmIR8rm3^al&0+?ciPZVSfYtY_kBWwX) zp6!T*Elqhf2}~d$8UgO(P0b9H5-m$5i?4DAMEqWaKU51A8=pheK>-U2!brk25D-jZ zlt!DGCN4@pZHe4wRFY$vCjp@%m`2U*lR~5YgMq$kDT+Gx%+D)Pl*Kww`z8%2&`4$& z;gM`8E+{mJ79N7i?emDeL75VTddW}~l79wxVj=@)O1g*oiONH*B7l$$y;QYF{U(f> zbN(Gh22oA$&m}bHx+8Rjz-V4F>1U-sch#wX4$9!Kzf5y?qR6C`%nZ>}i}kNDb=8MW z&@a*la2TgL*_*dnu}`!`tjs3A4frq7=1b0>#>CJTQ;TuLj;|$=Zs#f^#Eso-jzS$n z_#5!N4U<;jYQLfw*}|AGJSzorKs?F-nS@Mo2Cgtjfd;|)WyyXl#t9AVro(Ji)cy#C zI*Tm3cyJh71DShm3fl-!FhCYgK3#Ij0GMny<3MrthIShbB%$A#=jA#HrY>sg)ScIG z>%2(!sh#7(gR&Kv>OZ1q8Sy~2k{-pOw?&-2w*&!cc>&HmLJI@LA&hvKQ3rw;t$`5v zDM*QOIQTChL~kTeu@e*oe=}fE4M$fJA?WR$j+b2PnAyXL(~Vfi`fRoplMeQJ8|Z48UpB~H_8y!d!9pe^6HHD1aUz1_pVYE?jJ+3wcV#7-iw5}o<8 z&AS4Hqy}IF1q{@n(RIvtR6r~&ga8N*@PIlq++i^l|0TDP=;Hq{UyzJ1OVA?6n0 z4QlwkniuXNq0ABZ=3(Ppe^{zWhR61~>Ga27j`Gh254B8-5?STtj!x0X&@q<+fDe)I zaFC3whx5$L`U8{1!ImV2V7Ukv0HLU&fWmrCtO=I2{4MEXZUW% z>9&DLp7LW-HLm7|q{-=nhk~AF6Uzu9Nc$}fQ7bZ)bmUmWU$Hcst&8(uYZeln08gBQ zNRYG0F+E}(L%f@lr$~e7laWe?ngZ6Ds&l|Oe4)ol>_v$V8oJi=6}sJ`EHD946S7pG zs{9ZZr*dt~6UahCj`Op3_JBwW-Q3Bx z|2mRHEuG2CBLVydoBRbJs&_OEv%Wc{5qVaKF18Lc)8n72VHMq4pd}P_Ao+qtQk-mH7em4XOK1+uveEcxLlJ9YyE+iI{!6(Zpc#W~ z%a(LBj{H92-)(`>k@G)^M(jDoLS`@#rbmtnbE)AMo)UTE9rs6T`Fo>R8Tt4bvx`{1(3U}|7q1)xk?AJ;`EsNSj zoot2O!X5_KVP^7>_5!!0H|+N7rH!CY!%5`+ELrOV^?*o~@zJcQuwG06Z&tI-HhTsc z{HWxvNl%VcCoL?if#}y70(3J$`vO8uHU5v75-j7>4w`m>&<7C{nO$X@v(ftV+O*RF)vL#5k^C_^Q%7jjvhR_`)>;Vm+FN|}p z)gymTb9zD5+%icdKC_YHs{l#h9$}Xif)Na9*4p^K@+qRX%9X%h#k+0}fpO6S!m_)2 zx#?$Kec=qO+g5YPdDNb+U4OQ6C0grZf2?JpM}Vk?5ugl9v4p9TqU(R zwehj_SZigl-5|e(BU4I7ot2wHR*M82NJvq#Hemw_Xa!TNSl3#@p-SQx!!Bh?;U2=7 z@7dSC57Ir9kjC3}RhAS{@d#5;1lAS-%N7?X#!ObJ0Q*{#tTKA}X@K(n=oZ40Z8w8j z-H`WFqR5_0%?P&?uV7fD7Ec!bHO2o|x_Vq&66q%du~yNeGg0!a>Cm6Um`808R+Vy0 zFcc69fue?5SA_LF0IxD)W+9-i;G^-Xx(;_@LU#@?kqaCzaFYoyp+cfr&4F^A(ku%? z6b?(lBjCjpw!f^kq;XMRRB{s&WiuQZ@C8d=aq;rB*j0$LOJL}5oV3T`iqZx-PFA*P zxGk`xy)Z(el4?S)0Ki~l*Ubb&k>#cW)6$Ia&5IF?khaEE(;Y?*!LU^}UtLKUw4t{* zc+q~-)bHIzLx@az>jYuL!j~kJaFKFvUR#Ptw#H8#MwEttL32Z4mJ-=K$}Y6L{*L7k zErl;};dP94!}>%8k|o{K%71cf!xyuL{1}bwW}&^qar3-BZKY%;;+f`ci;jQ$4CR^l z)Ya4}O@PFoWsHJW0C{#(t!RP_t`>p?-61{8QJO*~IGFe&CZ%I2zxRnz7+UWuaody- ze6`-on7{<}gW(jCawHQDlYK0-p<`#B58DL+Yl5)ZFcFHK=g5%Ihx58Q$b(o&9%6mCUc^N6v-aAsc ze7TH23DIau58oINcMYJz$zY9a#lDJxq(}hYYA@{%ZE*XTH3u+jmi# z*(?MSVWH2l(OGhB7(Znaj)rjuOi=dh)PIZ^c9TOu0Qv^LFaWl;!T@^PSg={7;ipP- zuK66IeGU`|=NLR{fJD)xb|)=a$8Q!APZ)r&Pl{eK&4c3FoiAJ}IC^goa(@a&XJ$y* zBU3yIMiVK^+^WzU*d{~CS!Q>^d|;i%U>&AFX#fjR(mdSox5_4DWD2m!X!?IkdWbo5U6=| zVPgD^i0w!^S(2L$NHLC>Y%%^q&e@Fk)Muh17!6Urj6@{4C=bT4U_BON11L58s4?PX zF>gdjJ+lvaLS<2FIbxZE+8HVvQCQu*xjBXz&tUJk*c!DIxB28dyFa)SVJTL3D*E5qWqDE7Z`i`Zd*P#PzBqVkyZ z5q%lpV%R|9YCX->J21*3l(8x(<>|n|+n(5AL8=bd1Ry}5wzdQOPW?S;wSfddz=AO+ z!7U^Bjn3$aR_-W+pLpTYsJ*&TzW2{|A>&*in$F9@WI@OArgp_)KHSg33^s( z5~`f2W7b3(+uN`9F+<@5e(Z;3i8qzYNWT|_tjG`ta71e>%F+7AVNV<6Y1}AA&v=Qvs%_gNXx=;*d6MyF0m?T?Un#o31OYwfPZID zZzNh_l4ob41SEtA6oCx7@U6ZIRZ^n0mlJ+8srg`Hxk>aaN5?3Sa|R2;Fj)4moM}UZ zEINtcya{S%&jwoJHO-jj#smn)wjD|WBYNOQlC58nohb2jW;kgbrh(W-)7%G?UyuRK zq#$@)8N|iVL4v!PW4=H@SyOn2@C5{mEGbK_y07%OMkOEMw_}S1z9K~+0eY|#i8L&r z`O$RIAgy_)#!?I{oEbyMwk#>y%Ly`D_c7-lEIxv6s@cGjum~#fakjfVOI#U6$FnS# z9LblHni{IC@p|&viO{*&-8yhv3?c^*I5y;d!(m?ftBs~fM6gn*^zmpW!m?BIcZ98y zTqmBGxINDRj1|tUYb{rhbEx^-$3jOeD1p&73z1b@8nXhKR@@6Nk?lHQ;uBp!ZM%lR zX)|>lLL}?SKA$WH=y@juIcC&!NIHkhOSXnQF*6fAANb7#OM0K-N#muPPZKP~#BHNVp!*5$Nou5LQxB$Zth)w9_gP8MVrYqkOc0 zkHJ$*X%k9xA2m3onQgoigKInz1YaP>Q0Z%VmU+=VfXd_X^0KA0ut4QcWJ^5hJ`6ua zuCpX!n_L+Hpv)nsrl<;kD+}s7la&>tnX#9|>Eg-?JD66St-s=I(J>+j%4L(%SpzF; zS>fk{L`;%*6VFrQ3Ob9LtAU*f7iP)Dxg*8$LpW0nngO&4DGN6Ga zz4D*cG5Y9&*aaW$)`_wl00W@7hzU=vjJ^jKrN|OdB_=|R$)IErcOzU3PXGzP91Hvi z1Hl^^bMsoP8b8*4*}h*`t?5K5o9(L2m_g(;hR6-;>4-nw1Y$essv5)r@mv=#!+mVN zy369O0e5E`5Do^y)Vq4weGDxy==KBE3$&*InScmzgD^d?bg~3>CN7J|hGT#TVq6_H>LXckc$bjRTuVCLUusB6cyzAmf)Ai!_ z#NL7-QejN*Es8S0`o8uSvn&U&yki0>-hGK8%rLOTKyd0wIP}F1=VeljySB4p zAC4tj&8X^{G3FU9TSGOf;e}0Tv1%pb3~bca5GaMH!j^hyKwv2Kkoa#D z;0KmE9^Cr~I>STVp^-DAxC0TX-;T}}5|Tj*&`S6NN=L#tauE?ESk}Y5B?#=6kBD_1 z?hI+lp^#}^Q@oV0SQ}71VqQ0ZWKiZx2cPjU$b?FL&64ep_D%dLZb(=#sQzpHc3_4q zOhFO*A~K*YaSpn7Q^k2$pduQ{R0s?AbcoR~WCYX27hsSq3kKuCmN9KIkwi;E^UrCo z6naP;$%&f&33H(+k6xX;W_o;%+j1sjpg`HqnUg@1&UA@RUDky%TBv-aSXR#SThC9Z zqE0FlL_fE&{ra&uWBs~jX6h&ozJOS-)u3kQ#;1c@bDs8CKdCQ!N)GOMNgPylAM5tB^Tg+x(7axuJy z94GC-zN&g^t1IzBVrkMB9GRjbPOmR0msE+i@AmGVDVox*h+UJysK8Q6=M6dl39=$S zs98&3*h(IP@Y3j|uAJ-d52&RW5E-^N#YWVn{i{27&cWY1_5isF1~i1p&!Ps62gUYd zyxX*Z73$wL|Fz8)_&gFPC#22_m*i9$rLK1YI6@mD*C{G-FlpZYw;i0twe}~AGSfQw z!C0U7L)gp|46XKQ2ep-=RAnwz&dX%Kk=HGRLSn&OW)TMJsy_rj{=1K*&{WXgo*Gc2 zn_nd;t5X*425l}ot30tixWqiA1b!O>c$yy8v)-dFG&L_|65kx4v;YrKVbDI5MHG^R z3el>MOrP7Pj_VrxAhHnyw9!6MCYp9Y1WKWQNh1Zq!Na3sjangyjt@GKro}*W!(I9< zGoj<@=PAKtkg`gB0Ul92Sa+2KJcXg)VL`sCP+QUac}1(GXjdOh0|Rh6EcQPvaEBBi z96an|jEZcYCz24@lz{N2E9Mw#5P;LjI&F=`q~&C7<<)zftjMP@-ieh?ELQcxyhY}# znQ;OSr;t7=q*m{7x~Y88brlsasSa|N%ZuqZnvZIfWvI|-gru{fY0`zn1&Uy9_%Flv zaahF3-!VeC_alhq|Hd7K$NqU#`$(ja5uK6goYrYc9T*cpY^LA_d#(g-s}_hO33!{W zu<;{BC^|VSP^6c|Mx%YvyHsRkzATp8cR(dvA_PUU;>Z~!pgDpzIf!)KvnNFQg2ht9 zM5x*Ffz4G3I?7qoSRr`TivVfRJHd zoJFkEZXfR_Xa$IP;eqzNtvG}ta$SJG&5q4E9gjFE`b*4zE`c%F9HiNZg=JB9(&1{0 zWyr5e$4?g5fi3p+E_BhcYfTh#xGL@-T5T6GH2&F@G&x9)s}12;tzbIaBnvJ$ICaP& ze^nu_1xDfs08>W02FLy635_!IVp;=mhx=QG(k_I zyz44f$^wBYtxB;?Q+L5tvdZh$lFC%@zB?seOIsPAd)7I%!%cw$0D5N!$csEp_%82T z7%1q7K9@w$*S3fTfD8*O_c9H!4uLR$?~8yH_N?EHi{OZ9Y6u7tNkB8xFye@Hy(f;E zy1z0c!an5ClOL9O*+xdH(g?FVCq4%2v4P>XWh({1DkWn~aTXvyP$$oZ`H1u^3@5_j z^`+Zb)|k^Jk!jyz6cunPNEhJ+e^=0dy~U?z$w;8q^|o69JE4ZgJ?kzX4v3@%!{UG6 zu8jx)Li+`<$4Jr70=lW!pVL;v42Vv@+hYx8p4PZTGK!^yK|7RV37)0~2@DJZdm(_Y zWJlV3VBKqk^aw#!Y6ZVl`Rw8zfFUKIMW*0MAmsXzCsH;$_L7IkIfemz5C8}r{r$5D zd{=>IW55BM`8323BGh@z_Wg;tF$51pm=?>I1e?->(hQ|5Q~@HSp6wiM@!z_77*y4n>&`>+j z06xsW@8mRfTozfzz zZ2VlioyxFOLUDBtNoW9stu=ZI4!wsq5=5lHqz<%jQa%WSQ`Dh2B7$2V*<%y{Bqxpr zSK58v zG`SZEQ=|FhA?yJWAsF#gP|xxo3%&nV;a#u9ktlmGOm__!Pz{@VFc|zlsp0ySPu9M? zeaA(C1_wjnsTOhtF-JbpXI+W;8kXGymUz#ppCbUharZ^hLiJ|XU6AwdX=E@`DCkYi z3=}IaC6LkaY~Mqf;N}WLQnyNY<~v!EXk*v|JTf7ph3gU?8Z$A`?Ib|sGDwT&^;jYf z@DX@RLt?)HeKs6-^j?MdWop25`Z*SF_ySTGf+sOT6k#+1Cdoz0C2SltLr1lF;7$^= z?_{OrkFfcWGFgmd(*g@hxl6Gk{Q-XpIj0_6N=__4;69cAsXC+(FRCEY!m+F99IQ-h z1HkwQFlgL2WujwMNFk-Q3r2G;=5^fQHnrRd1G`-$qwpTjGsy}kBbxZ1Dr*#^Ql3RQ ztw$2#r?j~|sOZDDgb;a??gQuu9g9|#=*5hMt?@;l<|9ZCj1 zEcQqS#+J4WAnm_GsU-apwifKKT0X_oO;%S{=_oixDKMnfR#Oy=sa^o1lAjj6pe#zD z(w>71(70IF1Ps95E?yfF;RSSxE~(cug}_ChZD73;>RsK;YhLDP99uish%65nL|wUk z?wifwh;p@{U>OP2NYG0V_h`krC&UzFK53YewW4tCLz~K}yAe7vj9t&o30)KecRGszp2)O(re$IL+ zTFc*{gB=R3l0c!5`xArP0!JG*7)Xp)xg(CFiId6ztZ9+lf*m;#X?Sd+9!5^XepPlm z*BBRwM;+;Lnu&1cW$STl2=-bVP+bvO?VH`;75SKt@9gK zP=cW+lc`mCkoPcV_vszRmD@ex;T!wypI}$sw zSGkxS?#QQ--pnkXWY5NRFV5JZXxqG^`-*(f^#8A^j*cg=Q%EwvQ`n(iguOCU;vEN- zU@zIu0Stu`e?$pkytDqWx9in z*8g$Cq2g$-73Ta+OPoY!HRt5%7`zn?w&ua|(q`eHe*@sk&k`J?f3S72vLk}OA5cI5 zg*}x#yD71X0Gc@0j*;{@`>Ay{JS;HKi`ejso$^(&<{_@iN#8Q2QNO{J1{d~yo_1Pt>@V3Of?LefzId^#%f zyI?dh=n-Xd$mZBb8^9jWI4Ic0Yprv6TnmL0!a^CP#1Dv;TJIV0?1yu8+3rAtP#o?tr>?)Kz|DPY8472R0<|)qKOh0N-uY? zS&<-XyFRE!FFIs42kXNOVLG+K5iKBhV;cT%dqH%71kDgp)& zsgH%$$>utLqrN0_%%VK`;T9?hB)#ddsz`*2dmc9sm|w;-jCV@k;dgQ5m`sG9am$^N zZD7LSP||v>+9wG9AU6Z}%(dV<5jE4cLHkZ%)wx3X&AUmByS}`;)eFW@-42@?xiAs$ zUD#%yNQ&~RHEfPg1B)$?mBQw74TAIh`(0_S0jCS01)VNl+_IwgHLH@%qQh~!1 z0m1J#M%#181prie;{Iw`tcURn`FnB)u=|+MfosUgz+FYVBR`nS(3$e`9#cn0$fCW-{J- zKV70+l`gtvv@?pyCR?*Lt6sBYMFG-59y7P=SB=e znfRUiJj{hf^3dX+Nh}7xaD@Sn6Ca&T(u;o*fYu$urJ>lL!}}XwE0sQaf0?B>Lyt2} zVy#S4W}<1IVC(V+brX(#pBBmxQVOkZ=N~UORTS^?L5OVy4q>5yH34u8o5L4QqBNrX z!^UL!N5JFLNH!*Ei|~J=ECL)M_I!Sm2%9@WW|fvo&?u1v;jBW>IiM{R?6#etr_OVI zIQU&g6E1zW?kwuekEum?T%FjO7V1Q*h_LxLugHDNzqf$Q$Ae5xLa)JzWGHe{CZCQR zy1M;5&tk?0$|yGqfA>VKQl`K!O_QSX`$k4-0vCsQb9_!QwD9RjUu6!ie^~`!zxDX+ zf`K`#*U1MwJ(tgaiC~Ts6ug;b&hl+0412lNDn~fqdp!GdQ=2xB48v0l#V=e z-Zzy}H!z6qYkF0QIkQl*QW0Hwl;>%)y%oUdn#@N04uw9;0I2{h>Kksto%Gz=xnhgB z(YeZSjkYBO3BdYSv<0h};;DWjja)bq&Nr`_1N|zs3hw- zBNC#^WvvX>*R>2&{Jngq>f=lOCRO2GkFp!K7B#3-DVb;Dqk;iwzE<{dn~!|EcjC445>}()P{b< zz^8$<1M&7iz-aM5WDn6INCyA~X0J`n1P*oSK4CzvaFP42tD@&CoV$h|wupoLVU1mn zM$rgRiW7j@v+q{ib}?Hy6%sR)N!DCD2d>M=Vw8qZwpj7u_l8XhK(`7YN%?hUOcx5z3~@%eZ%$4vBxE_@q%u#}-1&pb$uV$*w=4)7;V|ZE5$An? z{9I;)2{=%L3P7i6YKN9$XLEdik#MMHU1S`PDU>vzxV1ANl`#~+Z7z948>~;zO@QH~ zQz`Ok=3%}-%mDYofnd6^5xE}vgClw1%oVuSe(y4S6ro{UJSJtz&cq9*;l328SEN0J ziREB3u>~nC3&n$^XmHnHao*#Xk3C>C6drl7{t7X8TVMt$0>gh7W2y;UfzHci5^E{A zAjoDwhU<$3Nf$+sDx)#@<{^$4RrO=IWjOsz6tKiD`|7ptclbNuMTurBxGQk;8EI=7 zP{QGVgCKjDSi>VyS%65N60zB!ZF-~Khd}XW<;qT)1{FR!9p&*4P%4py_sRs4A)>S^ zE@m-VKUc z!OHht{0<^eb_VU1#JXr9c77(D7hEdo+{6e*O$7S@*M{{GUMNIvWD$AqQ z&=#rOB=m@f09RTZ$vHXq+2f3{Tg&lO6GQca64!0=Aw5UE$l1pJSEU4%g$TpG9kKHIqV!5 zgeI`@2h{R>Z3Njj-G~4Lv*!?(VmAOFbH2j73`2+{U>f<1lxjT|;a-gfDPi=*#Pf9ldF&jevss!IsT^wf9EB1|385PE*HNG`qdf@G z1_m(bjwjzQW&azHfE|co3j-|^%=7{`4EHyFl}=C>HYA&4^3g?+i*I=b%s}}^8mB;l zh_!__{Zdy3=!|9@UW4(FrDYKrMZC?tZl~{q+CodO8-*y(hRh4hOK$GguBQ!f+tM?Z z`M3v{_ok4+;-Zr=Dzi1bPOQ39yGDpO^@@jVf$N6EX1)nkqCTNH#!vSt^@eyqAre-M z#C&S)u>XXeEKi}tDL~`T#6OgH#$g>>YhBZsNLr<9Zb0yh+-2C&Ar_5e3SJ_h#+$_= zmV4BVq4~PWPuncYsg;H|!n}|+cpyoIM774v zO^--5^f&-+{-;gsBT{H`)h7P&H7s@2!yT4Rk%lk|bb(1`V2F2t#L9DrR)aF&m)D{6 z*h~Y;W8X>Q8#;~v^rqD_q#p-Jx8Jb1!bs+VfewgnX`Rp0clH>+LJJEFLX&Z(9s?%% zQRO$<@Xc-+H6Ui1JKUym+-IFW&|OG!B#+gRl#z+)cx(k3OdM@aCyS$}OF$98TO?6_ z#;Mk^JQGrumPEUJ6Voflg1Q%H&UF7YFA3A78q?qTf2xXD*gn#OI_j0tEiU?!{O$}O zWj`g-VXyO9eZ8}k^C`V$c2(JQ={2~wt0nNC44eFvtO}(PCTm!q6}7$mWRE} zw!{JyaK*sQQc$>zr+Mk(A*dC%a}1f|g@+12-H$_gG3_80Sk-6uWY=;5|z`tFl0=f;#mvlGQ?zli^lD$F? z4C6mPY;}ZO!ghjx((8e3Wq!ob4Yvh2R}FF`%K4=VT-FoBtPwG{hl2|uJp#RTG!5kW z+dn9haS~>!qX0{xE@(jLur?H9`H5?dL0zIZT95I@J1-Z}>(q$Z-$R zgTrU<6Z)YW0)Efkr~;NL?7bK7rD#f~3iaa2oGV2|W;?|ByTi?Q;H6Cd((zGs?*{Q$ zqusfyzr098LnDxsBq(-oE~!X4oI|J+S_lteX$SyxV)05`L(MJShk!f)Sei_c$fz4y z{0hOQ7YeMa{Jn~oa2_EA+plYBfq@8;)`abAB-7HW7eP?IAoLL(fuVIJCMeTG?!4r$ zget<&RS@b5FuU`@EB3j}r(n-kLq%22p>bUgVaz?qKk9fOVu{EP-u}7yzJftMZiGg= zPDo7C9UVkE+XcDe_-clr*6u6RVmP3E0t<~wRJf#q-DHzwFhIG)Wx8ni@k30GP*DM|iyK_C#|&%$4$fe|X^3MP=RDL7}@U9SPeHP^N^^sb+1 zp9V2PcFt(@!BR_4!3Eksgk+W$yxv`LRVFeUHfV$v|Gz$m8G+0Y;KMtL7$C8sD&6A^ z8tt3^oyl$j9a`u{^a%e3wlpLpx}o~xJo6k3IAsLJ;0rFHy+=p7$G=cTy<>2ZLJ%Vw zh&s^MSO%6!AovQlBxTyI1!)bagEXAh#COP3Ga5GgI0E|EQKd9qYk8pG@EJMB5F#Ii z(?Zz7?-n5H1*R4AMOltZkSDu<`T+(YBfTzV(scN>_RL@AQ2z|k%$yh<9O^O%+V8H$p^x5B!&fqwM6W5HnQtZ%KgZtYJ;%-J0K`*@RNKb6 za)5XeBeyWXQX7bMpeB$(j!NVcJUvC$v^lklNjy;sn*rn15LkysA=j$g(w$pEBSLVkBB%Y88T_Bl_`FrHJ77>&`7rX90BsbvmY4IU3Ik@&d# z%V0^5Ss$(ec@&20WsU~UsdY+9r8`n&L4}b7D_!|ZNIF?#uzG?vZ&9QH2taFUa;U!) zpOopLPK<+Q2gz_+$(3+r(Is<7@|e>CBxI;{!w8eo0cxTh{@wKG1UN$!2ns5)0UiL` zS^ZJ)5peyp?GBBBF*FkE7F|35xS~-n6BFO}dnnw4UWgx2sQ|l$#kyW0O)N#s;Uh*| zBq}TXPIUZqvNQ-;&gm}{CS;h{G9Rz~#K^@VmI~y?PW@S+Bsvi^Q1QsarV|4NkOenG z+EwQX+zdIWNy2FjLjxNE0_x~>##mpRZP38KfcC8+Dk+IlBLT!>3HlPDT^PRuv#vR5 z;W~d@MG}Ja(g*~_Y`}dqie{ADK#J>}C)kdxy%WoW_3lEWpJ9`UK1P&|j*Pj2GCp zWO8?>j97(h8LiI1Fdak=rg+nF*6O7Q*-Lrtn}jy=mm??!+jXvgS}lbgqg!qHo(L5q zGnw$|r3yz`YrF|Ad6pj8!nvd{nc@)iIy2xJ3fg)d z;X;~y_gH9gr0i!OO-bO5xJUadI~D@^(*)GM85dI6=x`j^3T)idi0ST+0ZHy8e!Uew zAAn&6zXu95(GS12jO_}Eh>tLc_}5U3-GD4k6Y``J#UQCk{HX;)60)9Z53kunrzrXk z#FWflWssd;p@KC%(t9ig7xte~4F-jBIEQ>Q%xYxLyW(aav*v!r)YQuY6DY8U#_N@j z!q^OtWE{nwF}tm>Bko_+iRyxQ#u>ftBx#bmPU@1G*XHG4((<1qwqs3)v|2=Z93W^B>lK@N%1DWH4 zh-s>K6QbdX`{5=`X|U0dH8iO2L!8lTwZ5@G8LRCq07R^VY0X_96LH$gDf*#fC7 z*>*NZ#d$6hNI@Vnr~2GoDt(H}Td9 z#W+(W!}0*A3t{vR__%C4|h><<(a9k0mV89;2~y0GLbaWqfqb&Wdz+2 z3KG|Q9N3(hLI)18PI36QP$0m+oB}7zoK=gipwZ35Mh;wUPl5W9?igb(VyT3ff#^g0x^$1zxXFf!HQkK zS{puhkV&Ig{Nc*%cR(7`rnp9-8`s!kd}3fgASbXLHq zzATe?n}agP1VU6Md0b$;cBXcE9cL zVR4aVL`QsTXbZup5SGk+Wr>#~gv45ic1M~gy+@flV56X0T5vuO>3d#i*x44r;fBGWnXCgZ3w))l+TvRFz}E-@;kRK zoigNz#0I2Hp_bTx1F_l5jZz64O~lS1P(WMWYSqKy^>86z9$jj&NP;0v^krWlV2lDa zP)$LNhM)yw-Z@FZ&jhPn_K}kk7NtaQTMLI*fkKFk*aH0la&yH3TI*q9T~3T_;;Z1Y z+t*=2kKrg5fZVHPu=(nkezaBSUU)z>3|Fc`_?=El@VefO=oo!#-O*%@N=lG=0J@+x zqR5msA@8Z}2t#rRsTFu+X>W@II`HJr3KsRvHSa8Cte4vW%zrVOWb$(gIya=L&F$o8 zC!W)pomoa``&sOPNNy)jWAuZ?Rn%oh!j=Lkb>4hg*+KkM6IiJPh%is>)uF2#S2@}I zC)f9Fwm<%b41e=g!jkwC>*Hj*LPdKyL|oQ*K~DOA6erODf?pG%!i`9Ev{G_4KG-z55hx3fZ+5}ux zFll&T+^*}r;D#@5E_TJGY{}FywEI5_<gk-VGiT)19+e5*NrCbeBIB}VH$^_t0a~>~ zjTLN?6QB}6UB2u@JG%2%H!9(dsA_mf^+gn0)Jdgh;*=@P?aGNXsLTneKH&8AIwx8} zPiEIK;(Xd9%UyTw%bNqwQp9dR@lAY=E=_w>b_JZYYy?BicG)gTXLb^MH(wyr(xVwiY5GrR^@E#4%k`@6b9;KCHZZ z%L?u_GUh+{HCeE#LOvoSNMb+~aAnpUfvf!mZfG}eWeau!ARQ1TjWEb8dkAp39Vj~U zv@iG5SJew&N^U1T(A+vFra=^5vu2PrEM!F6TUH}CoL6JJZcM2#mC?`?XOy`@g)wL5 zKteUGP|MIw*v4}(AQ()W033j#<$fR)qHJ+JC5vlZwg>X zD_$6PGfZir)_HHmiaBCg4}{=Z6jOaWzLqhEi4eguCgSCnrqG0wgwkGg8&Y13uzZDN z#*>x?-GL|;`zd%;0YvDoArwX`WKaa#Rx8dVrbIP~RV6UPt-Cnt>|lp53j8Tr@fshj z@l7;VkOrIjJ`Gw^xsa&sS_)x;0c)Qi5k%+ds3yD$Bf#3c>MM?6fiA+19}qV*hiFgG zt0D4Fz=E)~Kg6+=(-{WUX(TkALind7oaCB#Yea=&TcAKDj@j5}@WE42@&fFrUg&=Y zymO9hZh!_3`Jm&_bFz{+Ym%+~jJE}KoP&fWh9{OYUVA&h0L%n|X^!?3kRZeNcv|ZN z?lr6BvY@e{w^7Zst)uFD>Kop?J#{8%t0xUE8)5DgL{V`|a-epGv(n-Pq*F|(>>0NK z>f%sQQiXmM7F7W&B(Rd8P8lYmaS23{uO+NYkda|K6kBPt}dP~TV`5-bc z2sk3(hh$&~q!HdAbcAFdkXRhNJgjhlc~JNf)FY_IE*O|*V9OD?15Jj2400KoH0WjV zp9Z28gk1q~1j!ICB)~&(kO2Y$H3-uWTpXk`NMvC7Ln4MJ40Ippe!-$cfQ2v#LKDm= z&`_YDK@);zg4PDO3WOC1Ens|rssL&N><9P?;5C3LK(zsD0=@?T2pj$Xj{m!S>;D7& z|L{IieNpqEupdodiF~W@|1tRQ@muAWsJ?#vX!z*%yTG4P{5E=f;iJZ7(0Ajn@T#4z4zC7QD2%3Ff)Ocg-i0?QXz&0ASR~&F~(D z4+FO)zwl+Ru{)gF&e(R9ye*gahqMOOdS_{`p&TZbN3} zO4>MqZ5rdExMe&rj;N5jxiq|QdR&K4@n$r5YVhF7^ggha6Y%&gcSaJzeSVDx4g+gLDYO6l@O(c_MRFWi2fFL0*d2lr) z8n#&-XQxbsNQp1-1>ZE|25lV(ItxN336wT|AOUA~<$G#-Lm;EUflWQ2PaKt!V0)2@ zjJ^F|+4&{1156y1XVhq>2He_=DqEeIy1hpzgCD+R&0^9)0J$9*>C2In3%|&ElmRjaUw6#F0}I9dQeSkV z^RzLX`Af@FJ2@Woj(}VlLHkjbhA`x+CcA>^#@fP__w;dyboTg56DwFGCb^;j5X8cR zLI{`Gb#h_5wKMp3fnJO4ppzx@>y2a(Io#{*0K_;QW;p`_@ys!fAt{OENE;VuFUsbC z40h0pe4(G)dKLkoLJvYaa^3p$CM(sf4-6kw&$s8>k>#d3MdQwty-GY+EW*B82yv!H z8Fn=-o&)#nl90Ts0VOSU&X&>=kMHhvbI0fY{(po}wG&vZJ1Jm_MJ znZg=Dkqpd@MdosKGVTZb?tb%;6?47t(q~qaF@Efi<-zN6t1FL;l|p`+*eXW$PP8xU zwWe{O_Xtuc+^SR3q|qm4G$l~R@qD`i7bMI(4}Xz8p=K+^y_=BS%Lg9Q6@x9R42G{_ z3ujo$F#cfmIf!D-V!92kt)M)q0D%-tAve2&X~N~C(5xJOS!o9sX5A#7=E-d828}6u zEb|K&T5zgCoJb4p$9EH%f$C+G{LUH~tv){r`^C=p-iX<)ZyiuM4Ejlj;Qv_AJ(c<1^(u_O? z!9h&{iHbJXecG1W(?@=BXRrQfFq_r>Ns)O5dSc{+eKeE=LOWeoQOS>{1I3Ae^qV~& zMVyz(&kg>Lss1J>_F3JQ!_(JMF8oZMFC>f!8((o%fP?>WM~N{K#TOxx2Vhi)P6SnG z)VYfB8mattOu)u&z%DmUTfB(}1hry-W*%Yg>w+FF)KGK#rMv?{gx4!L8ZvRY&?8aA z;?n6XbgqHq_MOB=vo=uJ@dBJizk1;t-NhFZbHOU^dIl=QTGU~9L~Nxz!`v4c?YE}^ z4+HBd(|2gGF>P2X@V2WdAP`hl5OzNW-tpn--;vOvJ>heyF11A#Oo;gW?0Uow;-T@b z87P-Fkc% z~9spB&5E0V2-wEC_4B>(&?nod9X8@&nMmf`& zo$*$@gQu^K+>qXKi|&%C5CBQn7X`%)XlLO0#_N}~Ut#AR2aZTmd*lP))3~cX>ZY-5 z)zaJ>3=Mgmg{PR(r*IL{;-cKyzQcsI%^R(R*z=GO28L`>2+IhR4ekE+4 zM+Gjxzqe4kWU~R-5>VMZT-3ZM(po&(PI(v(&1dv(86XaN;BvHm}^fU38+P=hf%-Z4PrXG}u{ z^{g=)0^+lVS>{0*NjXNV8&_q+Y)FC5rw3J)qxWAWsHWI1Q7czoL5fLjuNaLok>pJ0 zQivnSZfgD;R3V$T#E<_`Og=^fL87?6@mL~$cPHC8+zk`RkkHzqC2ee!6OOT25}?Au z8lo5|NxX-eBv?+_Jl(h9D~;e6g@3JwzU4b}rUS0FtbaUHZZ$m{NtvL!ESZJHISL z#$q3276qW>>e0K9BC6Lm!PDcC*mJ>96;}jV-`)zxB`?jOs*Xw=t0)s{mG?QRw~8qt zfu=rKWTTDPq=!y;1b*tE3H@nBXu_aSH~}ouMp}xlRsiQy|?8 z+=eFuOFpAznJa$ z9HP}Oq&hZZjUr$CB~(eAM!iJ*;=b?Yrx6h>^|H)MP==A9VPv1#j0hS{CaVQ1a0U*_ zOPt|Q3|tBH4>cTq2$K@~xI!3~L_nbiL8%UpJy?`vZOB>f8|q^o(U}ch?lcb}gFn9* z1|~O!l8`0`5O(Y2Oh~*GnI51ZmY26LDazLJ5qc&Ez{Mb8VGH2izKeuw*Z=?k00000 E0QL`y%>V!Z literal 0 HcmV?d00001 diff --git a/doc/_themes/sphinx_rtd_theme/static/fonts/fontawesome-webfont.svg b/doc/_themes/sphinx_rtd_theme/static/fonts/fontawesome-webfont.svg new file mode 100644 index 0000000..45fdf33 --- /dev/null +++ b/doc/_themes/sphinx_rtd_theme/static/fonts/fontawesome-webfont.svg @@ -0,0 +1,414 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/_themes/sphinx_rtd_theme/static/fonts/fontawesome-webfont.ttf b/doc/_themes/sphinx_rtd_theme/static/fonts/fontawesome-webfont.ttf new file mode 100644 index 0000000000000000000000000000000000000000..e89738de5eaf8fca33a2f2cdc5cb4929caa62b71 GIT binary patch literal 80652 zcmd4434B!5y$62Jx!dgfl1wJaOp=*N2qchXlCUL1*hxS(1pzUj2!bdoh~hR1qKGRh zwYF;1y3o}w_SLrdruJ!H7kRd|tG>S2R@?Wq7TP{rA#?eEf9K95lK|TG|33fEKg+%6 z+hTSaAdmL)uWh^R%I%Bq{=#vIHGE2vyyxxQ zu>PXwf4+35#HOMTl7@fkt@MNGkN*dqzrXxudarck;ms?=9TzfXbVcIGGxh+E^d!f> ztp1kWBdO@h9ZDcN>E)O$)*L%OUQ<(5(?2L3bseob+I4i% z(X~e}J$l2@yN*6`^z%o*bo9v4Umbn#sBz47tm;_Pv94o_j;%d*>9HG*-F57d|CLTs zlc>gL3N=cjYLt$8j>eB>jxIjhe{|c??9qFU4jg^^^s&K$J;*W3T~FTeWV|2+Pm&&ML33QxpS<_UX3 zo}ee-@q2t8ugBw&J>0`QlKZ6FaOd4a?i23g?ho95bN|)-zJuoA|NMsm7K+s}nqB%Y z{lQI|ivK_S=vvsKmRk#edAb%6i2hSQfN{*f8@=C#{(3MdvZPB=N8B5iy>ag#%Ndz% zd|;azJHAbmj*E8`hfQQA(J-EOQqrDKvr;880iAi{Eunx`8?Q;WwYSE-ESYZWVy*F( zDyBWrn7@r>BFSWAC`(6{$=}vkS07fh;rcptPAzWdrDR(Yf3n1{ZmbPgSS%G{s_+g8 z?`TBE8*uTOCf?S?TU)|jb#%6^y@R#4wuCfk)~1cCHg1}Q(}asx@ZVV6;lsib{$)h;3&X! zv#^nE>r1k8t{W+F*LfUs0DkxY35 zA&hmqcN%Y!F$Y>O5DtZ_l&QR>OYUgz=wcmSb8^yNnjQ>PHkL5{@qN#TZq2kl zV*Di$^E=g?)6Z1RVL6_0`tSSJtJ;*Bj-~)(fu@d{DcY;wYCkW#w&!@JXYJY^HP^E? zCQEfyNA@&MoHS`-XZ2cas^9s{_6MI-Cq)uIUm`L|ee%J^d;3q| zxwSnC)nU#t^(_m0Cn*@xCMAs)wp8(Omy8LeF_j-`^X2cc)%HzmHU_(Hx@>V>-Qvq` z>KZiO%HNyy@l}?(^Dn$><{N)&oS&(y%gk^5+Z+G+R{j~Y?$2TF2BjKgP>~{l@+5#xb#STNuZ8r?=WCN#*;G43z#WbeP}pXPs)z27Nc6N(s* z7!KVTtaQBluA?%jx!7OW`ifw}I-h-~p~09u-%4wQ;KqEnm7v$k5_U|!oKTDHICC?U z%UO%D>hNJ>6>FK#cCl;NcSO4y&fF{>U=3aD2IJ-~<7dX|?|etL6`R@eA+4k~0kR8WvKfSYMJobh>0d z!tvr{#Gs=xQsl%)QZ6lGj9fo`gtklOnC+PFB5q~+|H?r@3FXkQznBmY53W~ekX>W(B9tH3|SwvWJ~1XLheJ)N0I z(>o?V_Wu8Me(d|W)LC!j>N`8@S%!`yX`U_3UsHzz6Au-Z2`g~&4=#RcvTJE15t5HKCG3gq~ zrQNE0NeW>%!QQ27HO-7A+qxMxD=QAwOuIFjAAehPar8FhU^GezmgM(PUjEZ!aVvTo z+f4ar)c6Iz7iCcIr6=E0eaZm|+(=!(&9s`76^CY2-C-SFe<+|^nd%cY8^1JuY1YJ& zNEP13l7-rTiL2s0XS!=XLA99lj7d|~VsD&Yr5kF;8J`tNS3NtP z3km=mX{w2Vehi0vgtJWyPIUIJBgSuye>Z-6WY=Q{8ZWMnxyP;FvgG!|uO7aA$(Hrw z+_CD-;|@HQ&-QKV!ynInl1lD6!lIx2D(l%Ab2W~;IJV%Y*K9&@JhkbXpDu`9Jg(6d z+iJYP7vu#V=X4}m3WTqqe@p2FDIs8{2q`V01X>50LF_ODG-LDB`qKNS2O{^EnaD-4lj8PxQryhw9Ovnz(^f)Ef8uU z2*Uc*F(U!YNG;Z=rsJ1-f#sUgX(1$2M8Sf-$E7Al%LWLdqj6bc7WX_~h3j9O9*_O&uJZbsHf!YGkkdK3@Lg87({WRsC>(L4Fb~li4zjJka)fxa zJ<+n#5wRuivR)E)-_{cKI=|)#Zn4_0Xty~X_TcLBmPr*n=oDp}nkFxCIBd?kyKP%a z3)^)xWl9 z2=r7xK?qCFaWA6%eUW<(OS^n>tOSf)XGrI(tU^jX@g7V5_k36_LmfzD;9cZ2Bt60U(mW+|v56fMdYE1^I$# zYn;WCDXavVH)nd^#bB7oM%}kFw5ay^Kq2z{plQ z*kp&z*ff+Sx=PK|ch*OZe~qcIBxv>_<;k*S^aT##S!CCW3BP%kt1v!dz`J42aRDEB3Q^9 zD21}(34VTQ(IZF1Jhn)Zz6j{i3uu>ET5e**HtBLu3lZPM0<{ndq;MH6#$^pcf*PO; zMvz-W$VC(*%z=WTFr*hN%2>epb!UK;F`wfv4j+HNDW7rrSOAxeqqrVmK4(7D6k(59 z>H=&TuDEgKDHL&|2wN7Yv#`e^JgPA4Vt%KQQyd--xMIJPNp#^Pj`Q2Qlz>0#cjjo8 zb50~ryxS#YuAmFBly%H=0lx0*)XAQmQFc zVkB8gwmsEZe;gBw3IE}(Q$9K6HufsO;~U;;BjaoL8JTLYcN~)dnc$I_H0~)Ok20lF zEH*-E-`3fATPOE6R2mt-pXDkWQY&S}~TyokXyw@6buLX;*ub6eMzw9v-7(QKA+|L8-TdVjzepa!yjpUdH3-BzoS z^RN#-q^Xcm5ON2MJ89*!I0RmDT*l@V565YbFRc3xzln{*{*Zi$V6!2au+0Bx*H7*XCt+j>rd*JFSa16?@c(S!c!QKzj4ghXs#(BNfx8MKW zBJs8JwfVZoW#4CImaWG3K089H-N*b}ZU%&_l97od>r+*??<+P0u+n#%g zsAHWhdSusS8*aiP8m2FSuj{0_Xk|d>QoN=P1j~p30GtQ5SzQ}+72XTOe%Vit(OY{CQQmf*S4a-!rCL=&B z(CJbN?hlE3G6w2QX%r&SuPF&0CF^DV!xjJeG^zaQE{7S&Sbe7~`Fyx7${c(L58e zQHg&n=5!keg~5Y?YTC|+Ni!3LPbVIMqgMshgqEEacs{gm38lO<&kG^fB@*scroW@{W9O-ROG z?Ki$`92a<4V+*lVm4Oqq!r4Ns(=2x7h2|P0c!?=lQP+gi*9Iv8O(X`OOKxkDF*?Ne zobDYgd-fcgJCZD`sVSrXWW;TobD9?$z6W_|Am$cJq`G6!Mus~mfQn}2SD_BIBt{9=O676JNwgjI2{$qRA*qp zvSkYbovCER>AZt|+W4^(V4Bja^`^ROZ@>N8x+WyW%^&~$qtIa-G4fN@WF!@+bhkh8 zwI|x$m4OtXf9h9_Hsi+CxKkHaoJx6QHS@3*=2;ynM>brCBC90_4WiIPkRH+w+RqOe zN(FF1EwlrzVyy;i(|-KN@y|g0(=VMF60C3?yj!}~TkDMnThnx%epwbjau%!?u^sde zS&;zAY~an5J+Sao@ENtSReJH*(HOgzJIJ)h-SLtH00GoIooB1?3c{;3Nd zItcmYsr^Vn(q;B#D)b#vYpu7{|Nr8@8$Yqw+Un|u@z>RLLv?kx_zn@U-bhFpUq!UIUk>Ec_WYcV*tuLL-w-b>i$yiSh=vxZ!f`sbB z-=>;v02>IL2n8amC4Bu+tzcQvxVok)_R|ElFqg}#JPB|&a9k?c0rhlyvZITWpoS78Q5&7WEiJ5reQ7B^2Lk}GYoL%= zdn%+7>()ZDog}I(uyQ4NZDW1N_=Eq-8ABTu-W@FqX$*TJcLcTYc#EuZIVuOoDNI+C zI>q0tFbn6dkY@2Z{egH2Qe!9oV8P;$@m}5B^M*cAVYl1Lu9iPh*=}Lub)G!&2gTvy z{mybFh(vw>iA|?mQEDd78@ej9V#}hL)08Hcr9!g@Ds0IuNn5?eUZd4*tFbnz&RR9H zBWbC%S^^P^BN0!PhnOZ?w=EdDYUgaXr(#ZZM1DO~>#m~xQcw#9Q43}gLkhU~n2-ZN zSIk-+8nHbWxKEwL8t%nvp~o20mvgBjMit)x|{(&v217kK;Gm%Ge*DDkEd}3 zEcC!xm-842CmxLU*PoOw7i%S}X9dq3hdfu3$P5EU7$6d8bf|e|%Z9~Ok|{^`$n)Pj zbm+Z9@*t5+$Fp=CZ1rzQb1A*S-a;nkyjT2|&-h^`Q0)lX6-|y- zd2IoUi~3Kv3m6l4zz+$=258kmIHE^D78r%v8a=4{12SEsE6Br81A-H=yVLljW!mAz zZ!?>~I$A&okdQ`<6<~_!8j=WO#3+Sdi03dcjeVKjpH3tjrYu|h^nwZ|^TwVpeCh1v zpJ`hJI}?`wEuRox*yL5LTveEj*?p~5%N0oAuA89xRMrq!uySK#dh&$v<1*cm>%O>Z zO=Ym9XTkiNmu`P)`A_5S*wT4(F1w;K@(28nZKh;Nq5U>8jB7UBSrvR=yRd(vYP`*;+HPhnDTHj9A0I9 zUwx&cqSImVx$JtSCuC{Z7`6G?^i)mH{qZ@BE4tRvo=G?yR%Lu>da}{Mn7+e%c4ZViB0LPC|dWSDQ?y(zK%Ro0605Cgn)Hvx}3u07gM+AOX_w zkpve4C?F}UF31K#B34<&_qDw-vEY2y_hr!QjHD)jLV?bWz1 za6@1U{(bSqi%T==jTI_t<;-KTFcx_@ec_at-z_(uUAC~DyA{sWb*Tr9uNWV{uPIfo z+dPWJHbKSg*(@$4q(rQ7Ptp;r%^hQ(?YewTNKu(qVYg1aDDIC`cv-_aCwLp zzmL_AXI7`3hCXU58T#XYKJA3l> zv2a47oQfj}bB~LhhNHNbrF#mFIgz3RyXYg5{~xv6G>w$e7}0LgC>2Lx6(n*T$N%eg zkF|yPsQl>hE*4my+5|EWAjXcl7&dJ%nBi$iu?x{ z2ftGj%|0QHinvmm9w{RalF0@=9;Ji-BYRfTUkOT$Q~OxZF_@NeWa$HlDaDXu`|weD z)=wQ25=a-Cs2=)9yU343sRq+51u4TSMuiR~ojH9{&~~Dal923rLE_K^7Wz~a8B{Ww z&TvSVQjk&kjID=u<}*7F9oorrI}fq@d=(C7iiA<)ysDqw_f+xDp`A~%1AY}62U7+I zJ_z)c4!@QvsR`EvAJpCg_ASjYkl>ra5eYsTFHVL_xFce_d3M{twrvB-w&Pir8Q|b# zJ`f$%GU(}jrPh{;hYD`X!%RLWin5sBd4h^L6+99f}e!kWQ(MMn=A)U zAjLaUdayOf+CarI@Hn7s!Q!KRUdVeHI03TS2(c}z-&vjISA}eP{?|H=yh?9p14B8Z zUwtR>l+piGU3)tDP6DO2WaWVnm9mAX)c1`3p&T3FgXzRmY~aac@_!&z5qz1Tv31DS zMoCm$z(-h9LclJY#vtrq+_>M>s!2{I zYjl@PtYN67JwZBoGJlc58$jk$C5K^&5nz>}sIJr~dK83K0HP*H>|Qfg8m}$UE|H?nvgB=pa{W}siM-Fvh3iT%GguL@o^=lx>; z6V@Be^{V|1{nP+slcg?c9$ID2rj*27hB}ykG-wld0`d&8Fzg@i{<-` zL1oPvV{i>@@g9t_epJ)h&vV1|NQK~+4u zhQ-!IQ42X9(Y%r_0IOI3=q_E|S>6$+z zRy|qvcj=_bArOavE}&+MU6f8b{gH*8Hf>w6cfM%E;}8D9$coiJU>v@3=L9)yQ9L$V zX!5vPJy<(+(Pg(kw|M|4BjRUSKd&|N#eVvo6>6kLDfaTGew(w*W3jR~j4bfQxZLi2 z#5K?ckHqy#+;;WeUAdxtjswo~89U-m~%dGnMrGy#Pjk^B_V zmR$w8Wcg{@LX#uvigl>K^jWfHYOmA7YJe zI{s=n9uKP%!+c%7${C2Lxk$i?R2{*T*jEHkO?G!Cg*J>MOpPj0FU6f+*dItV&g76V z1b)pJ&Z!wP(E#rzjwNY&55X=l5!R#o)VENrBjrccGxDs4XEAo+;jV=ttEC~7{vmN(Hc`<9+{#fpHLj)Nd9eTcO~l4NgU1bOrQL!VpqQp zib+yUYF})TFh>{Clp6kaemgWrcOVVJ5D~Q z^rB8sKjecYq+-~LVDp})?U-e;_|57^a!dOlcUVjWQBca@2J(2{ZyU8X`l3 z!ZKqBCZ5TXguooG(a*5PF(lMTyU2d2(5_-@PHjVp@6l=BYJ$lrZz=76qtMm1H8T=; zL)Zn0K6KS|1i=Ogr#OaMVYNs06d3hV8d164|J-wa|0;h)gc6YoBu~A$=ZzS1s)}zl0NU8}YaCa@jC(V+kyrbM#+k?(iPn;jyOUHEk1n>nCMH%%UO0z z>j#QY`}pTq9$fm9GT()oV^&#NTRhnmitd5??kC*r}T6#G;# zT{4>ua-y&#TH0ZnA=XK;L!+!AC74DR4QTuOh2bC?SJFX#O5+DyJ}yy7B#fLm`Q*Eh zF_YgK+uo5i(hMI&X~g#gMiv-qQ}zODLySC{h&;4W71rlt+aHv#vZ#wET>Bzi;ca&u1rSmPQ3G&xc}HYiM#26F&DUrAx`u3aCK}v z5XBiDFVsi4Yh=C%cTL3z2uCAvAX#O!28fAe3N0efEC^aMGBB5Io|*; znm#!N-*Pp!BJbKaaM^bcoHJC;|9tC{V5ij>OsjqaADrKikrhxvC#!sg?|y7=-hJ+h z1KA#I_y(psW-K8JT^i~i=~ohErf-5MqY3uB9yQZHd2 zvjZa~Xp3ZD8@!%alE$wWbO-JULWg8MMCtqzV+|Kq%teyO5p!I#pgnWsn^55C(m=2- zc&&s31%G#_6ye;};fuGT2`1lW5MwsD{u3X+e0^7~s(RfXhwgC8H>Mxw-yH;Z#wB>& z`%#L>5l40V**gX{bj;Fft?q!=8o^Fk`P6szvipbKFk7%?rwBtNM2*2;N z&8GHYeSp@@0(J;^#d;j(7lv2JFaTl1RM?0Z{hjqWI5G4KuZ97UVXzgE$y@i7tD=12 zT^#R{O_6XaY>I zy0Q0#)#3Ig+TkVzzd}|0UQ?E8H^PXK&+) zOL6<-#w)_ZyY=IEnDis^28kc{4fX92q8$_?LW8qXYst__)tzbG_lR*${^0d6!=uONX5J;|nf-!1;nR z;Aa={tq#p%(H!~vY;JI`5@f>Qp(NlYC%k*B$?74I_QJLiviuMzi+0vZL^FH<;r2qr zb8Cy~r-q?6ndySL5uA8v{a|qk(va@Lkaobx)kSmBI-~R3H$)mSllep!x+h^|kYM?>=wK^lWze7D}H+0pF!brYsPI zmJ3$apq9uww+rYAb{>=fIg39EKmqTa$Y+f=ezOaUzARX=Hn5NBUybl&pvidW^`8#j zf4loY*wftDRarGI;N=!s?pn|l<<=D+dtqzGSHAqE2U50Fpe9w8>W+D2*iv0^=+?;y6u&ad)|$TZN008T^SNbfDq%}` z!`3x>whKNF>jv^OH>^@6@(ZNtFn2F#qXGiyrouwdsRDzCQ&kG-ltwgcC#6Ye_4l7O zX{N$f-LY>~hnee<&D?;{A<#kbFWPh7vU&4XxAtclYgoShrq8Y~URir{;R+2o=rOw`ynAzQsbu|GY)=^OFN;>mcZ!a(H*m zl+Fg^cfe||twYm&W80aacA6VEAOpqB7ROtJ7c0s7{osYbwWA#Qx&XvrY1RQkn>Q|6 zu^xSSn(rIw1-q49Y^>Ql$>wwH@{GUx*vdfQzRXUduRN7Uv*#g zJIv!<=W)Q7hue&a``>C|?@!n>rzW%HvoGxNz4y&8U%4&wC9oPacOKx=qXM4d1X0-a zKLRJoFe@FlDg}-OMVWU@qh6w3BEioP=-Z6|I)(Xwx=JWE z8X376kOPuHLlCBjbXbK#M(rP;>3eKI^=5U4BD*!?zm0rab@p3b+-*HPWarF=w8md# zvZ1(OFP3$A_{RtOa%z8DuJ5t@Jin`7W3rPC8Tl8zu6`@G4;|J$PRBYcOT#KDY=IYY z)~P-^(3c^pAjN6ISe|NoO%~*2b$ym}CFFl`({em9<_syfuqYSThlMu3e8!`ERRiZnEi zMP$Jc5#>1f%D2H?2YMl9o^VB!WU&lY2fq~-8LZDFXYwY7KrAnja($5jo!gQVAv zZSGvv*4NV0Hl<=}p$K_k7u^e~$VqA9qG{vGVoj9|GpDaO@9J4*9b+yQpHiyVJU5|Z zUPGl2lMK0_{?0-DonuVaUE!Lh>8bO+BJN{DguAA^vsj>NT6a^|)}B>YFFvO=E*>6r z#Vn3-!@43p4A3EwrXWbbnrJF;STdDPwkK&1R68gfLl?uQsp!&C3!KaK52%x zLXlNwgU_NqG1yR6Wqc3<> zX3R4ldkN$@#175VmNt!RS~{)S%u>K3auYXm6bxx3$8*{58ZSKe9P9b6C;_NVh7=`4 zj1ZpS7mXAxeT)VU;<$pz<`P{_!7K{Odzd(O@dmU)eAILyQ)mUZN;_K`=7elaJYN3f@5 z0o&xm4S7;s!3skuoXKlZSF7N+rh`~5z!4z5Lq^vHGgzgBaffH2xbNL8e_x!wA1goc zF4NUA`9XrCAt{m!CHNPAAb?8pl)LSU&Xg}kl4;>vBA)4$bB0uwkay{oWj4=5GN+HY zT4yP82a---bts`HX)S^l&tfe=*Dw~&q57mqd3)BJ$gJ73XAQ%V53JcE59CE&&e7Ev zOi7D#x&rn1rEw!o^AX@&xu@3x|%IUO3Bou zjYC7ZwMV8KUr<@$#WB2mUUjXpy>)J+s=Ailfis&jaQ-}FyQX-RlE#p1N8&l`h0w^s z3I;#~@E~+6q+!6!1ZE`S0hI9^1dUi~rRrPC7Sy%MFWV?!S&23m>sRP;@c@1>ek`L) za?X4gy@N11KzEb|8DMM59fZF4v=xqMgG*iy(!bC+ybB$I|0c~HOntCJ_XS1*?35_xct%NR#)2>jcL0W$O{82u=(lp6e? zog*^kiBbmb({!kWb>iqClK~k^rzE7yuv-UW0liA65afU0gi`Hefe?YFX3Q#|F?;%& z71yda{rarR)y?S(=U0ZDk>HkD+wYB(-T(P*|8~cQN#ME1!JIDRZfYw5gVIxFYBJ6sl}dnsEbubsQ|6Ni@jtP>a?dFs%p_WOl2qN7$|owN|! z*9Kd~SdZQT)Qa%S)t#4q;lVw-cQcLMU)m79`Sq=nQm@~0=kC|@xA1G(`=xKw#hgl* zQ;M5Zf%m1LH|Rnuh=VNQTG|Wv1D4Zq$&-v}o=}X^avb2Mmxclm0wsCC=jvJOi~2h2 zU4MeN@WI!H4pJ;rC0mG7IP@m@0cJI6=-)E=>$Gfd`nUw+AIL=0z5Gj2-`XCcGwM4n zB6Q8ri&H}FSVPY}CB5Ejv zaXMM@)1;GB5-8n=Z5~%(3RHAety1I+Ow9ZZ;}(;t8J*>CulHJ0HH~ur8_`AM>ZAE} z&mMl_l^0mcz!R_RW*79!O*OIgUZ+i4y!_nB^0P2eTRg78kB7zCki6?-HBIzz{kTO@ z{^;&ko)};)FTC=^;b)D9`{hOid-1NfX$zOG>Ou3xT61Hq9R(iuVqR{P4ofEr{i4`J zX8+JLki&&(BB>SFgMxPoupc%l5H({176Bmw+e1|JcZVy&$P|MW;T@=v#)?KR1tdf7 z5iyX!d4OI4)kqsC#jXs6fpg$82Xh>hhanckEC2k%a#lc*d=TNRu)UZ^BkQt$!XB*Y z)b;RAzuk6aqTcS%!(X@iSh%L)D&1+f-J{#OJYmO!HrH^`(A8A5rm?iB#X&_K)7)V@ zit_9O4qvOXi(C3!fk433XW_e)R-fa62b|tkMd|7++-Pmkl&h6iuk(R_w0t2X(@8Z|;YOPb5vwvXF_=jxVQDy%lwqR{wc8S~nQ zi`uOYOVw5SDxd3;rcp&beW8gpVeZWj-r;dqlwV%1$aB{QIS;O#D=WxWxIMU08KxWX zXFm_O<~Hy-bT3@#mXH23PZ9hI94u(;gpfyhC>TbHz>(l4i5RCOXd=-A#qPzz)IoMs zX#{D)i$kl8(Tc4DtYYm_xT9|x-}u*aR$cc{U5jk@b1(y3m0<``=cx?ZuDk1-Y&N@r z&F0hYy3Q7?^whyIg8VK~EZ}IVd+54V=NQMnJEiI|R=@rFz2Tb<%KMG~d3T>@WxW*~ zE$kUJMVGO8CWDFkvUxw+x&PgL`||s){^7i``b03PG2B!%O_yCBrd#V*diE%*majRw zcVX|`pAOUW*dBHGD{dW$nuAqZ8*c;hN!AW?SRe(^QxY?xUtO@Nq}xbzV2RK&p??j5 zg)vAYBtAJAfh_^uOD<@n426vX=&3g4sYNZuK!2t`QkG~4btuX5@pTO;#658)Dx1R- z)gSM^CZ|@_`qBY+tT8*ungo^m**ojb>;J~J+e5}6AzbFG+c0HPSvc94YF)l}&ctUo zJ@^z=o#ffpg;Tyib^Y4NRkt*TXQ?f*bZwn4pVf4?#mnbE9jWrnUl41VT|V8**3_N5 zAYQj{W-zp2;r_=aG}iZ~c{bf!w!1f7e$Ae7i5a)=IPZc70T)D{0=WTC>ySVp{=h!qkX`Q5q$w(Sf?HcBtUOu}ewqU-eDsuMH z`P^%9>smhRtE)}NTGUzL##^q6tX)6#`%@OSY<%#7^RAjTdqyI@e%U#}mW8|FM@ger zKYsip`_zRSLcy5}>*5QD#yj~rIinJv4{Ga_;K_1kY_Mc?@c2uo21hPkmlW@LGHOF` z2EqNqc^3&8lo8k~z@ng4Nsvk~SBM3zWgBPqui13h z!x;FPdMQJ^S_oq6k(tH>n->Zuuv2)IETkU9EDskmwQfAind(MFEHdGw=vaj;NmW=3 zD9EeX6nVg(A0(5?j9_hYq>796E3sh2X_~{s#+)*1d-4$Vz>U$)TVRehNQ$wT$zZb> z$oKqU!6sh7x(w$GARxE3WmM!9;#~glyWhRf z=4_uocQTtgkI(+IP>PqVuodSu6j zp8OqbPtsRA>0y3lDeXr%T2hFfx0Ag-^rJ*dz)XrFmqEaQC{I{~DVfF*aNsTQhr~2` zfq@1=-QkaeS2dQka<79`sC~vIk>tY{&|W6ON48z?Fdtx$yugekgQM|zFte2oZv}fR z8M*c)E}8Ku4e2FJHrhid6nHd6F&f4a;$;7UsUJ3WF4~t;IgmQ0+@VCLIbz++MFVKU zOv`OE7F-r{`)q!@soUgtJc}tLqe$LwLWm4XUKA`^F_X&0CoeTnMm#4}ob(*2I7Qnr z*AQ?@8FWLepi^MbI^3r=h?y|8?dSyX{5XV-2Wk_SLdxktkX?CbCpqH_m}R0TkQACQ zTe!CK5V3Hl14Y(K?i|CA%X22=T1>DOI5{hLa19!<`51X1SuCtXIv&umGX)X(9~(E> zMPN%7b~v;Ig>*`wWFX(Bg0PAJ1rRGZYxcbbC#A#6w@*q7?mV1bcIPXXk4q;jr_b!& z;d2dPN_OYwze-=J)5S%m6^SIL3``Mnud1utnK&A&DMAJ3+X7-q!c3xG7xi*aY4gZg|#;U zlD0d6KQu&xfPH)lCh# zMKzmM$Nw(Hja|bt4Ik<7PT?^HU+Q@I(9S`RH)Ly@yn5Y?hO-hAqMK96^IksBlfI&I zeB!Kz%(~T+>#f0wJu|}osewSyqd9av)M&FgyXMWLU>u>)ps-vA^81?AVYlEv?a;M| zsy9O`tgEuxpxf*a>e_cWG&uRH9+>CbxooqP$z1*-p$%>cdjGg?f>zdk*6y>fIeYcx z*7~xtNW>nSV7+`bF5JAhy-ceE)!Nt)t5;;J%cZKe&Tu%{?1X!A@@6>{mf=i+7J$hW zemQ`-92UIWT<^sggT?b`xj_}laN0Xajsq+(EC7vz`6yV%LtjaB3nSX4G}_>2f)`9@ z()0_0>@yt+tR8S^w1lvy;s{*t>p<*Z z!AhBB#e+b$MC%EavRM|72^a$ze51?muvu(2#p+)anD+arjT>in?wiqnTowzoCL#VuNe)gP2552f++V7_L`vOZA*tmjV1RfuM zdHnv0s_2ABcy%b@W7dh`vQYb^`TzaLo9YJ|!YjsChN|l({EP+mKWTj9M928b%FE`L ztqj*c)^OQRj(l~-)ai>R+BPf?uL|3|URy}3f0)Ju^h&{&0-9*xDD)l!VNz*Od!~r2 zAc7WKok`b`G?K;#ga)KBRru}%@sE_`lbE?Kb|$QR<5%9 z^w!Rn@)Z>>-B)W*#@uqHYx2y=Ha*Dt{%s$xaaCA-oh{P>uF7#r`Q$nNIhxGsD^`@Z zbhhd~dzD-}@hs-eE?jS2T%BpHShIFR&>nzSm4D9Ua%EhlD=@94(`T)4)$o1)*2jXn z4RyOJWp^xTuk}H0V&Z&ZGh*7_kKUV3ad1=mNBm6I{;KGCL)(lh755nOD;g+z9nnG| z_%dUzXhIeQQCmlt`9C!H3Pfb=>2uFzPdm;Sg+)4%WCzba+t{qG`tW!x0=@+RG)q;Tx{ps|lRu?R^fi>%c_!Z%1ou-)@~{~s`kaj@M*sd*~ zc|Pm=#7~VMebzYkW^Ln}&tCjgbv)WQZrgpc7WFI|e+^sxvgPpJJNmcwCoVou*|dJP zD|)k$fA3$m-mBcsuV1Iy!(ZH?B<1mUEnC_9z?W^wy1j=l3QoSV+h(qdpO0e5|xWW4_Sit>MUpNdrc-gvzbj`s-9o-i(3 zh-e@`{^xg{i)3G!x{%#_;)kXw5uql5p9H;=K*rqNX>$hkD*_yn^TY^`A^bA6Y!YTt zNr<3?1&;Yq0#LRh_Kut@`VCMFpIm2sN%X_#DKrn>31BM7&fU;zk(9L&?>4`XqHj#mxYMseX72QVfMY+CvMj4YY(63d$K}C6r~iZm zr{R7CjPhschv>WlUZ!s;A-eCdhc2igB2X}mSkFR=Hx+grh&itg-{Df-$UO(F4}8pY z*yY=}-&c8Sc^wZK-*~GWR#XvnfYn`o#jV`Q1HS0pkpy#m35K%Q|E#<=;ETwRPyg4~ zzwuM%5njB;OVL0uUj7!F9pZK6w^sVR&Regz+<4>hia?;Y{AX-8tNfCaCCcvxv*G;d zH@+-1e=*DZ{cgxJw56C<1GTW?}m&l3+@XpkAMc^tne=-T)-_ZhV9Pd^bBb)df zd&OYjRSl!{xwbx9WPNRqv0pIl$rl4YKM`tvU*N?jjpK&U@4~YYG?}4ZFL)WawS!ov zV>8iVphW0QVb$qK7WU?`1EOkT4#=3#JceO3Nz4L0jpx<=+pBDj`fsKk)s+ojpJ;1v z=+%K+Z;g&?uuc4WLuIui{mpuZt?KqMr5Y-4y|uDobQzu<^B51&WA=uT%Ev`VSKVN9 zRPWzkWw(tgBjzP5U`U62VbfUIqcH3v7Z&r^l%|31DwRDJG^e6Fgl>fE_-b#>Oyn_D$|ZY(zMg_o8bE=U|%FQD#Y7avmMLh5+S z;ZIF1h#X_KFf0mPWqd}hv%aReJ9+&RA$C=%;4v^cy{vKO^!?+5nI%igC+D-7OsT-J zFMaWYU6V~|%WGV}4&KXqkI1Ml7FeS%h$my{05mS+`>O%P+7^CfCxNHU_7D z>V+HcdX};2a$Grd@y8zA#I6cGaecD8xu)J(JA;?GDuQKU8;hlTvpieYGA=I58eftL zfx?a_!_#LrE=x}iEQCGouqd)DcJ|Ut#^h}%US_&?>g-S4q4r%A3Qq2N@ZyaRPMfuB zZ*8V)X|Q8~j6wAJtuTxz$ZCaLTfml590>}Y04bIZ=0?*A(Gs4;sEVNs{lz}7)I zUKmgCNKn-Y{fN*@f*3&#Fx4f~+S7`5KNv>hhBBGFn0Bjrx=C-EY>J<0&LQFw9C2Z; z+h@>Rw=cNn)-iJ}#LiP^^9&$yUIB0|${E16mgMKkI(fPn+WagNRIBt42h{>#W7x#L zXUb=)1rF(eH4fq_Bn~G()R$7UO+pjUDyUV_C}0S(R&R}qCWhdj z*iq{Fr>dfEvoVHE$dBJIG?i^$&75PKwgE-a`a)wOBMn7qV~nHR2p?8xR|=aI+9euB zgEj2kDn80Es$I&dJs*Amb+9Bwc25bkTT6!G6 zI{i~=sIyQluMMH@j&=yJLWm?QN@(Gv3(PW0)lik~NTC`Mc2MjgRUPKNFc{hpe2KMGTN4M0Mq{Zl7$q%OlR~e$WNHmHn(mOr zq`1mLAp1Z?gwU>zwq!@BL%bYVkJ{Mzrw-0@KS02|i9RWBIV8)@#wQkj^SZ#jQC0iX7Hsm&?_{R*=3X9F*Rozj&&d*i5&ee#Df(Wo$?NepMIka+wHwLXAQe{NflsU6% z+zxRIBNcg#jyPUWzB?3zI>jf3WSQxWnp;;nj0ekA89h^N+-}hkc@jTv9e!mluM)%; zbs2`+3Td=zg=AW-mUV>h3~{e4`e~y7{DULJWhZV z$Ix5LWYw+$yj2?_apDWI9Lg3Aky~NUU`60ftD;%`vgT5CuhW7!nL&*!G)8L3U9MWJ zPN!96_~?`tripbs6t`N2v9ytsgAXsTVuZqgyK?5XxR?W>H&xw=DACNOFwCnGP}Fk8 zDl>)a77Qqc+Z{m@tjwjW9;+g2nnROa7|F$VAi$DUmD3=fPeSJa>)<86A-6XIG$z-Fn_bf<X~j}>pSeswiai#x7;04^a=|o zHdzXu3~D!k_twGB!iup-<%>wx!n(HuDjeATlAIHvY9Un}`;FJJc|{`9 z-^eP`5K?4)M{evN9gQ)Ivh+8UDT=wU1GBf!lmQtmso=k_g?xr&l!&KZ3_Az9*8E0P zi+U}-`{WnV=3tR(`03+Msx(gd1-|R#&qqX{Imr*3ZT1Iz{{}+=eG!d^m^rdjB)d}@ zhv6|Gg(Yc-5b`RBcykb*k*rxTX9aa6^#76}DUg)W_p?cD%^=e2hYDQ!00MXh&pi5I z3G44!t4i6tWW-GI$p8@?0~mrqGDd}bo&*j9YpI__JtHg*t=Pz5=w`NuBnsrA174Bj zAoLZJYFr@J5w>!s6rAJ=Rv~d9ei09fyQ*wF%r3YGod%I3J`{A1@v!mmJv2b1fr9qw z9(DmP_#+NSJ-UFHS>9?~!b9Q7|;*yG03lx9S&g z2w#aT#@!2P_+)8@v`ku!t_wS^w1>1bU}!)Hfrk-&9rN|-g4Jm8E7m9lmnE|A5eBz- zmKRF!C6901yL8)iTJP0UXZEPd=+9l-dKT}!ZSUe9Tj6upLuQ;j`J93^sT|+7bnnK; zm#956r(WHwU1u5#azNpdMQq);#&Du?f8KS5Ph+bs!p797E_@+7|LCG6*Qz`AS0=)Z zCdBjmI$D>Co8tS9>Me{SF zN22wq%KM_xS1TIEmXdEg`@UsYU$gAUvXv{(*>&~uSC@~;;}eIdJtkK>BIWM-PTg-u z8g{M!Q4u*1<-bQFT5%wnLZOQ4(S`DF9$j`|+1dZG?CNXJS-BE5kIvG%z*@}$cU54F z1YAHpAOwLxqYCxS6bI_rHy=Hb1G>CxJ4eL7M;Mzrr+@RohMS&Y*+<`mW8IA#nxI7`cA~EsZ zB0@lmq&3oJ>1t`ObO&yc#1>XDDv%tR-ePrQje|G`4N4jDr3v(wtYAU4(j_8a+ex)6 zsBQWJXkpTUEL70BNfOp!r)h1GK}%E41v~=NWkfweB~&y1@Dzf0!i*WUAl*T4m7fy) zIJ<bgFWYnPZRf1A>+6^9Ik0S&)wyez(>iO}fjvvt>uN*e z+57I@vuwSNl9o&Pmt0jd^0O{|Znre2adYkAvU3nxxuN)Ov@(KDXfy1?z@_Owo|qeFgb>z;9S;=l){ z*y{q8=7{V8S;YQ3#xogX$>sePsI@&x#K>jXgSX4rG_VN)f6=~Cji?X_Sb^Y+5+p(& z**FA(#%DgDj~0lyy%jMx5F64@n+QR#*h_{pn!x|00m={3mmnB@3WB`;XHCl*KVgm7 zVsZR8HqFSA$3K_q<)52L1s6=$eikcya{>>e4&!U}KQVs7KV$sF_!PdKH$ZOQ_!5p( z-#_#>C2QsYZA?;5?oqE(uOod2c`X6lOu?h+tR(WL2##0X*y-ktwOq^2@i&K`mRHNMSxQTG)~ zS5D`%FZ|e!M=q2tSAO!*UtOMm+~)91xAF5A9^8C!-_T#XmuHrC^Vwy|%2C;m4gEiK{lgY8LcUti zW04jM6b(hIrcKn;^qA49KP*2w?p`q@oth;ycU&APof9cKu(wZ_q{VSE2U;^DnfkO8 z^gEzvik@S>!VV3&_^8$uHEv_CkBx|2&=Zm$#kK+UXsKrHxT!)MeX+E_t3pS}?h&W_ z01V*Fxs-o1_6i$`bd702pWL+W)xW~}Yns#ttbK`e9ngVTHA48BZqrkcKBOTT5g)LE zddeS+3!y6sBx`UNLVvzaYCzjYcn4rdyRuUK-&WPDEpeB(v#Dz{oYp|NY~{7mn{3C&AtI6|43)`Tu!rgp-*)z4*b^gHU3 zi?5yLs{l{=KY(m8KR9{7|DU06X@Cnq#sM0b@sRo831Zd6+f((G}2m25mpZIv36j}4j( z;C=Nq(4g@E8s1cNzlZRAGc8BzL@rXqqENp@K`qic>gu|&5uIobG}rDcTrg*AenUPJ zniI{)VZ~5_UGPkp^bfra@_w(r&L)I^kP0?6IokinDX1=M@ z)?IMu{%zZvTRb*fKcvzFhupsB+hh9Y2r0a}cxS?e<~qsHpj78{-N{vTg3y<&XhxL~NFa@zFmU3ak= z$8(BK?8)>E+}_FeMa6wK6k17W0?SmC_w#zy5m3%ib+?Z?AKfvaV(w zp81BXm$8}InMH{X2Tt9Q#)WV~9tcB^Q9}r~F;>KVq)G502hIW(@e-wgk>D(Q>Dw%_ z4rpg3juR(fH+a$EP-|#^;^pPb^Yih?c0T`nb2I+L->0vnzL`D{zssL}tB#(g=riiT;) zg!eRU!GI}(9~hZd_ybdHN?I);B)R*${0d8c)2#ooUah#pv*|jgC1i?;C2XscFoAw0Y5=wuX+8! zTOPc6UCUI9E`nIW)&)5$?9!`pCL8-~ZqW&zJE`zHv2j;_dU*3oyBm9UUD?t5&7di$ z9SgmF%Q?6F=H9&zeY~(Gylrtob^GS|Q>x_diR+fIoqyr}UfFd6V#W~PpQ)V#l_OV1 zrE+u?HiR#!92sSaF_i|0kxP}%_v*{sYnqS!dE%u{ukAgy>zvYAGt6$upw`%{e{uiK z_wQfZOqKJ*t6Jv!miz3_&|^F<0i56^iwYl$HL%zp=iRkq%DA3OuV`O&XHadhl-a$` z)w|VpmA%|qWY00^<==gH%j$=MQTN{#o>#LpG1j~K-1fDtLGcZQDU`*^I%af~ zRkV+F*a2@ zlYQqRbxTeMJGyd5?cCnp%ANyrc3+vF3T}UJ%DnbXQzle5cvfJL|~-hkLbp`M02S`iMdZr((3Y9evH-jHK2a+cexH1<$k@5Xs`leX+m zG_C8dzc|#guKnCq-m!_LHRmnd%Z}~eKWSz~dwWGFo=C()*WN1sSJRG5yPG4y{zv;s7K452_o-6#ymjR42ds~zQd zO>VwvMv0kpt|c>eAKpEqMA-=?YY(4H5>1klhd+e+88j^F*J8_(J*@xgu82z>c>mgi zJ7><^c~IHOCCE382V}k#6DO1O2<0{c@dE8)2}va;5xD{%KqYQX!La}`lbnF%ADgHj ziJioA_^}h-`?W;&__G)&BH_T{SuWh9Q5gs%We{KBH)F%N9|@h|b;`2|RZ>Vw{JSLg zku1(1266@hi||q9LsBC9Jv@Oj%8X|d%Ckd}LL8w%NboYlX#-DFI8UbVKzU54@E_;D zhhlYryANDzXem4qY@z)g-4lKA|3u1#3jm$a12@oYUO-Bo>;rm_)N?ZF90{R7ylX!& z%&A?V!5i7CkOoO49cm|D-r-`7YPR2IwZs|PkbeiC`^vs!*)O7YKpTqaJ6^`G=sWbg z(w>>Vf;Usag$L2NAdyk>e?;``4su8rH1jPEdaM?-ny33@rEVxLxrsu&Yhv|AHPg& z9DJYHG0|TY{nv_;%Brf$l1qOdV+&>-tdUP9w3T^94o6X5r8e=AujIzInZ4b-&mV`s z>v|kn!9StI2m_!bf}9+|C66>zplpx|-1d;e2Dce^nAQOgJ6C?1En}3b&Xm=6RnxwxbjUsJ z2bM)xiPIW1M52SAL6mWNSXXFpUn^o4xZVuCizi=&29j$k6^K|rDwVoTENq9-OW^`q`_Mk ziAUB05TC4ur3~M)z+{5=*$h#<+vw5jNd;MK##fC2d>^)0$t~bB_}1ySqEu(Nb@wS% zDe4j<4i|g{pBtnLqKvj=^?@^BhQZD3nX|3}JO*M!$rlD|Vl-nx&D@dk7GyR)24Ycr zt%HL7$#a|o1Tmws`}}-Opt?ePesj0Y)ph#;m#s`#&VNZM;6pz7adJ}>Vb zrg@rPa^0u$Q#7uLE}#KG7d*87!CQ#rbArv+Vr-M_UQ}m`5<)u04FQIM9T`wLpyHiR6ePH9uQ>%NH z%x+sB)#$GI8*}{aC&S=kZu=Rq#U5p`haXO_54;X8(6*J?wHT^HZIpW9OAr~@mt!%2 z?-v&%aq-5_CtLEI=&@j*C zEHGGlpLpeo53c^(SHL!${Nk$-8!o;0b@SXo)qOB5y&dB4_GD;iiR`>|T3&1A5NQAqrVQ@)sSb{in6v}%w; z7jq-#7E3Tdc9XZhb}Q_4Ggr>c1@9?d204?MTNm>RtwKC`&C^x{^@`qys=ymmJ?G-b`H=HsMU4Q76d3-LJjVW zIxTdX;t7_f^hki`aCW~UYB!&WDv{fN;CX;xo>YSL-vV^A7`~;j7@@Z_hA7}gqo3SX zS_{CKqI>#Skl#<6)CIVIehPgI*9FCdL1rhj73)C{h=jsd^1L-RAT2CK-*M#yaTOfm z7|o9*o#M+}+;Zuyf$tu9PhuGrhLKB1CBWmLsoP0v;(zeg!y$zlA)|AGA*CUhFc7?S4q%t`D!ldH>{nx)E|oN{wpg{!N(%T>{4F3-uSl$x8$S1-Qd zneRVy!(tJQ;51iM<88s|wUc+wDleb4bMpDKjAh2#Zn)t#>}H*R$EK?3TdH&GB7s1p zHqYy;s4lCmEvv5ZdGl)NT3v4Smg!ZS?pX2grt#x9JH+b;BuyGJuxc)&V^oP%f#DKti~TMtPKgC4pFD#B*e+D0d zmYLq<_W3<;*XNsIpMUfq?DNxG3&=h{s*GqlCCwrrZ-#u7A#G!PfiXN=8R;`8C;4U+A(-|$01{+vA5IHI1%=+ zN#k<%v5EU~)*cQb=qU)*9p6uAf}YQy>x3=CDEFsbTmS?JGPP^Rfde}_cOTxe#9G_= zvTJ1v@X5MbR=QqpE$HnnXiXemyEw0eW_d~8VnX2ZR{Y|=k^ z_gx^Wp)H8-Nv7KZy3Gv#29O=C-30*a7T9LF+N;{jO=9S|LL_qSR6kl;(qkM235Qb{pzL8ZmeAT*`^r`AXlt}529YAF z+Ld9%`5ev-@VGz>B;pL{SZRIgn4#VwAks^a!|@{42vGxvcA#B|L*5FHCR~1;J)KgV*D`=XsnQpsTdad4%C3J0>d`> z_^5LzOVcZRh_bly94Bdsmyao0#U;?(RDw(|86=v_@nBL?kAO70kMp8vgmqkN&rAl+W~;;gX%WkpM{t z6oxFz4Vtu(UovN&QTz^AeF@tnnmanF#=BSQkLTEFh-I|W)NgR;SNlpclrJ6YvX4#}ro z8JjEt>IgbYUf%ypWArOV)ZmR$GDsvicrwYymDsPikM;C$2D+cN{J4C0`Vig~sy0CD zPa=&Gq1c(5VYeEJOF$on$;VWiVb7er`_g@g-c%evnlMf>y$L3pFTDz{!M6&xhQ(H~ zL#LhW(pcZ}%dkURbU#MKj|wc+w6!mT`{wQf1GHWZ9U=nU-=DEfCy5OBoi92Q{yxPj z!ylbSCTT(YW0N6ulHJS5ogqcwV z&qu;1`#M$sT3jBNhR#q$*h`4}OLERe>Oa}vH_ZJ7agmWH#Tjbz@s~1%;Jz6CRNADJ zP4aed&_&*k}kB9L;+<$O24wD4k!dQ)04Ok9slF9GNeFF*k zcN3`jd-@WIzW$zIFxlUq3AZ)2nZP260oKFR2pdWS@jv7$i$2Ku27>)ToiFLr zVL!n7g18D^H`s_QCE(!_XQmYc+LH;6!ad}E?8W~W<%dZ;YgV}w z70pnQU>H}Te$!+Ug;OTh=yJ*ZO4;Ze_?A*Ce12rfgapc>lxp+?LgUDS3E-h;i2syo zfQ>(fBvefQAu}V-4X9_*nJx-j4Ap=&lq(Qh_XZBC4F-8TyP6$1VgutLrd|1(oA#XiXWc#waFCwugwTx5zJby1j0Wl}zOHNL>V#oj=<&U9Ir zp;UpYg2Gc)OR5OHfND1SGL>tF>KjsxGlizwGwt9yo45YUs5uCq*sF1eJyU4{vp=pSg<}f+wRamPUl?Nd;5Db!1!ygR>Qv+l)*1+a01Vzq) z4H7pY&LDTY$m|v~5gki&SF{`HD{w0+rGg%s>kBDg8leV&=0dE?2r4`R0t|wO%7%-) zti%HH!hso7SJ#3lyJ}b;eVV_u{bV0dMEU1W;`8dBJ_VAhPuys;^&!3%c5wj(QqXb5 zo?(Txb8v1C@i{$MrKng~W>CN+)&eaed0=?VSPyAcIK9<|i=B=sVc$lw6>0%9wFVp; zhOzZlajnsSq9Gon!iqm1;grbR1sH0i6Y(mZ_hZrx7FAIx zKogz))C7HOER;5|r;v@McKR|73-u}K?9=*taYis09OO4hv?aQgS$~Wuk4hD^Fk3zg zBKb8pHU^7;(+G>5c$55V%4^HB+n$!aSL(}3l>5EYz!30_^qNkwYgp5V*40*lgnaVh zrX`q`Iyxs+OnQMk^9`bEW0#!l+DImQEOLmbT6?&mc%W;e2<_1se-ILMd1IH*Po{pp zJRV*P=2yA>4A-g1r5tX5LKs@cw-ks!NlZQevtZ8iP0sd z2R3${aX4Vy1VyD7q%~LZ(o`cRv%iu`jAi$73#)5;ULc-c`F~UgBQ=6ckw*=&zvI{ z+UcS0)T{JRySSJhTHV9rDh5B`Str@$eDqR%Sk@TjKBAdX$^AUDhnuMQZDv6HUQIs> z9-imOWiAm0BT^ef=^7_DM8bGSLu6JRm^5pGaB){%CR&jb*Jib=)#29Vn{K;f`2aaq zsgTQEMagr8pWYK^eczVS11fQ40 zyr+3q1-(BgKde<143rp|{IZU{WcVUS5$vGq&lfQ#T16*}U9kOENMz39mMul^O=@w9 zXMnCUr)6GC4sC?nh7O-QaM76CCp|Lh*3yd(B$gk#a?S&Dt~|6nG0+m-f8!4iFP)jZ z|G-siL#NwdyluQbeTz}m;9;v_a zP4NleYHgHnj!%HLpFbPix3sUSB1rAZcvf<6z56qP^efdl)#xu zoB=3Q*(!vfMX==yp!7p&amjz=!pP6$pG9;&e@>+?Xa58Hb97^?eX@a1bpc{I{;_GR z9{xxk{OI9T*fZ&)huwU5K9H@_2e-@Q|G@?H=VC~Y`RvJIewpx>MGa&_v%)YQ)$aoOQ);M zK~)9)|FmvKcqxN=E%D$aIJ-PWt8Of3GHrQI8$_Zxuex*I}nb zQ_y<;H8dg_f2@oGsmP{+9WM-0Oz;+=YB2#th{KY!IH23eIusJ=A(!6CZ@$@o=|9SX3zi2DzN8bFE_?N%l>~g9b%+<~ce_6Q9z zLB2-vnp(|fiEUF3gm0X&0#{Rw6ctli@bZ+6Z}R!by{X$BH;XYP?Q0 z%9mVyV^igp&4zbTtS5!2uPW{QN^f3fAkdhHbUlQCoDaZ|L!At>0wBtv-kXyx<{ zDq#o_#J^JL6;tm>CGEv(gC~&c_k;}&ms(}E1sqnb^sSSsu%HfmghZgM7*1DOrv-{# z@Wqrn8+@?EO@np+h9kbjmR*lnZlV zx|o|fDkU=po58*jmI`t1zc5Pm`p*a8*QLU(zr|lq|L{Fx4;Jst>F0Vq?*7-{QJO4V ze&RlYd_JJ){$I}-8h`}XJ zz7?KTMAq6eVW4w=a&B2IB-z@s^sa7Y{rKr6F*`r?@u#F``ED}b_S7!Uk>9;6T3XyX z!Jo6ZmIQTN5^IN#Wvd@pV3CsMS?P-zc^y^&l?72DQQ#b%3xuC-;6#Wf(Ns|s$R3xM zgjKF@sP+JIdx&9FlVXxjwHP6XL6b<{`}LH31qfeJB}^1^PfKnh1m;461t{xTui$cU z`qgUENDh6JJ#$KBFq@3BR}DGf5Pm6IRO9z$saqyZq_v~ zb;~F6Cuy)C=D;=i@iZO~o9Py=%X&@fAIhuQEvHmQ-_Qq{{*;Q31q7O6NYrEnGY{}I zP<wD4m;$J15AMqV$M(8_|yWS+rb=ZI3fAtPu(cef{XYA@^{>8lr&PRtXJMQ z;$sR;=)pu8#Jsce*fc&jGLr%NIHG9et4B&KK1CpxkSGZuo@g5<-VS7I7KDBuI2s?{ zu;zl;q_WtUdYoC^duBFOpW8CNG(6etFq!W)t98)jb=|XP4)bLm@ClRax|^B<9`C#y zdqKomKKI6Ops}(fk(YChO}ERCZ)S$p-dj*$E^iAor}HVd7Wuf)NKqzlW*UQCC2a@X znX`VTi%@cMy)U$CT(?F^y>Wo6!>DWhT;{-r;W9r?^+%;u{UnLdhRU!Un|zdk^uMQh zGC2{uL1l`GQDs?GWxqZ@m&NF7F_z0BWQ~om-~hdwHj*Z#qGOS^oNB3nx4uqQNVp*p zcbL!%!UTx~kPN37j)yp)Lrq2u1*^(nB$b%4i0}UP{2)5HJ7Yhz~e| zdV}>2Sx&z2+||fGBe-!z)a6{u*sf<^5k5@GqEtKcoSC&vV`?fao;Ci++%*?oRW)tV z^m_4w`|lqt(VN^Z---KKnAsk9Pl^J2(^T@_1M+9`uZ8XQXy|TgENu>TDdSB|c?!insMEx+Qz!M=>m+{7I{hsrOXA2nb*;bfstGGrPL;l* zO22tEP|i-TQTv*X#?Ba32tYQFw=To{5ka|C5kfffkm`kx04$>*M;Lfwl63+3?s3g$ zR%6a!GTN9@McZsR7I7@%I7x6hQoL|l?x3n{Od<9X_OvdlPQA_j9eZ(t!OqdZ;ftVk z1HuX{K6%s*1&Z_ZgG!eh>l%1!R*qCLauNHpj)fdN*kd2|I)$%kYyX zxp>x?DdnA!3xmvKEWE6@qGeuqOnCk5c^BnJ@+%@;%MR-!dNYtRg@TB9cv)AZ0@p8^ z-?bih&1*?~P{{!P>I;{Zd&X6DmCjkho}NuV?Tpy86sa*x@#9eyQ3S4jR|V6@ zvYP~j)AFuBmainBzWc#9Gp@em%lhpKC@yX`HuXYZyzq=-##Ck z^iGl>)~i=^C{8Ux0@-M; zZ=3q8_;^aS;K98+=S=Zy0e9=4GH2)B2Nx)W5Z@ynNi~Fb5hi-*h4eFc<)tvcr|6r0Qou5{qQ8d=5+2 z@ywIl45h}lhm3YT$`&Rm&-_J zT2LYdxsv!JgqV4XqJmVRc!P`IHUZC8loLkFDbl*Mk>ieS^mNi8nPUTiaa?IyLe zVf>ng9GEC9tiobs{UU&jO=@L$_sIP=y_WR|4&y5C<68y?Xrzn5wGZZRsBD@V(uK9A zYM&uEZTtjBNg35GRA6)nJpc`+x)q%Ya(-J23;0mo0BHz48-Jm~#US556Kl@rwLM+TJD&p8uVu<`Us#N-ZWDf}z1l;&b%JCe5BQ zYaTHHwY@tcKTjZ!L){yshpc9JyyjL^_O`4)3xF6Rw~IxHvm&wV02;G=mt1L zA7q*z-ZM%=j4FdzepWH+~Hh68Nu+sCw^XA7qY^}srSEqJb|56j*sRE-RI73=B-s^mpI1f&srlt6cX;4&{f_^EL{KTQGabEI<2!#br0& z{{N{}bDL1%2W+yLx$vNa8Q;F$ zYce2TDR=_#yd$PR<2u#_Hl2-gp8jo_iajks@JL_83|Lpa$LS%-EQ zURM=apCoJ8))mjyGyAJ5PO;=Ddj=0xMWry(BbASBzHTV7M5k*MzQT8ll#-PA85(+U zKO>yBk{Bhxh6277kgFX-VN5+7Ha)NTh%z zJsvoJ(^Mut7~fFQXmf)1;`$n}3#3!8CvqI(ykcFDT)g^=ivn^#UJ6HJJ3a}Oma)&Q z2e6ydGI;mYpp5sjWI;3{B#r$R7nr@_ek1z>#~A#&dS8{69IH z<77A!S7pz%k8qE|is2sR=G&d(mD#gtnC@#p-Q9{O9P?_)@ti{<@b*L64dRl(5Q90% zmQzSyz;3#=wxNf;VX@2a*v%F@Fnr~cLQoz^4T#C5xw*IIcI7S=`mzhg9=Wx)r-A*4 znI5s2>5)`I2r|q~c|hn{iYIQ(&0X4)UDE7!${}B9ihD*^Yc)W>PIGP?pyPC!MIPgF zkb~r>K2#b)@EmjmOy=0AVc)|BfSo@k?;!5uEryNHUOp3{E;jFSTzNV1_Yn5p4& z0`ZS~7mi4)MZp>rSR<>%V3r%|3tGc9MB zRe2<3@d2ew8VnrgC`vK9m82aGuiWo!cgp=v!4q&yh_e+?~~wsDa#{`WsnE(@%)6X15aq-BXGG z1P{{#iUb?H75Qf1B@!F5K1DP6NSjz4ApJ?Zi+jjKs)oOumau=x7!uNWl|xcA=MyfJ z1k&vFh_8i3lTj_1oxT7%!1VyWmcOOn-<6DY9k zeyN(hY111-pE@A>knZJWD>wunbO7?Mu`gfdC@RQxBVCNyZ2I#Nlbh1cAe9pG=rHv= zPV*+SbKF>mWwXWc22*+Qee)4A$s)ZHGRY)20y$u_KhkM3SvMN3+pb2+7&Tsifmf5E=#u-pSB!S(VDbmw6V`^%i>y%xtG9{&90 zBNO!M+@kL3zj9dinw|0$$M7JE%2c($ws`|G({h}^)HcL&lIJ3N0GUe0QlD{*ctD#~ z=uo=)Azc&Df2jMY8t`@`_ea2@X~Z{va>QZTZ+5m{+SQq(wp&+gZC1UoX-_0F`_lYK zS8ZLad}d|)n2H?x^LIJT`z?-f>pGep8oOz>&T27>-ul*sCCe_hmqeyjRK^>6>L99Pm zDGZg^G!EAxEAm%~j&PoLL8reg76>B^thX}SI(|{Q&-S3tTG0l)0f08+p+pVfzGL8m zl@5exCSZHWvQ=~+X7XqWW$6M?)J#@ zsc+a_POCG_X7@)xfU?0B!rThb(&fxfw)9@>2#4twt1D*Q^c7t9g|KwME%>AAfDtlCg zO?6mSo1OC=mR_?{Xt&vH4tZg8p>L6$-Rrbj?5XcL&Ak@Ke5ZLeFgKnyJBgPeVG?x! z3=s}#iAJy#5C+1b;gSsv#vy7#ct+{z#2q{&=N?F=FlVq0sh8wO*uSZrWUbSDf5t35 zKvxD3P9JzlT>a8cIl=ChcmLN#qn+1q;bxS5o5ev21X3ZOY&sxZ+Tf9$r@9a$!x?tM zqzed3M6`u!Vqv-fpj+jFA|r}?#E4Dc0sQe>_iBAdeA;inen0j`yU_O<)%CH^ zb+o%+G4hbvuJ)_XVXM#6`gZ%Y%h?6zs{L2n3`hn+()V%^pE? zUJ9Z#vQnsFzhFm`$sk5)>Q@`SZj^ntux;|dxuB*W&Uj*c; z1jKy+hgP?0=mbjxPFgk6^^TjjZ8d9aW^TP~&h1?#w>u^~Un*#N^Y{a}QrL zY5l}Xk96uJ8wA3^Gd1iGV+Eb}GB)_R@Y$fYpy|BST}2H=IVO!DKgvY4$>xV6#}}cR zkQZ418PsSDDCpjT3WZPSW81F8L=LNDAZox&6$#nN)DQoS40uBjA)|S+IH#I5REw&? z0a7jyHUp&%NwSo+T7Ico;nnziNv5izdGnQ6=2_~X5#K&L%mh1gsropzq756u!FR9= z&r(#BwGg(AU6@J+$SUosIha2+kPG5rEfyK1N=y4caIr`+TySX#rqMV<#4)8>z+A#W z3Aq`V3OC&tN798jCZ4v2_RboobpLlIn9FN96S&_mhSV0$e}$O%*#+&$3O( z^@rqcCdUUC3-$8#8mrNwcYpDQJTR^DpOw?(cPGAo&-+sEZ!2w*ixrwq=4SwzpkY(@ z&_p@W=eXi8=LmL(9yrrZ!AqwXtkWGDMmso+J{Jbg+|^PrTVsF`kV;bD3E1L9PS6SK z=O?FB`~=&cGu3(+j6Ro8o8bz` z!85mp&^M~iBU)ovvl1Mt;N~+m1=~FI`&k=+k9qa0>ABuP-n|iW)_{5oT;titd<2d- zq12QRqv-h8?Aeum_jj@CK-m;Rw`?bOZF>lU1;&h@R^FPKwh z(`h$pCG)n0-rVcYUvubtLgnVo>~XD6Z8Mo2jSHSjZ62EMLv^p`p3TE`|8hDvs(Q{Z zYmTo`_t&!P_v0^V2q|6plMkJ#_JgCVsjfL=d(iq$a(e>nJLy+}1E}=6;)pRCT^hpx z=}3_8jB=i7w1ksPdCp*OK_^260(ihys6vn#keR(_b;AGGv7} zsMCQ|rV?|{+}uwu!8?V(P%s8AENCkWPH$;w85h|&VY*Nd@B>33;ukK@i3q~x#KMrH zIZ_fUYj!!^1=YpP`M&7%vOp<oB$@JDx<&+A))0Jz~>h*p{ zsI#iqms1q=hcBJ6@XmJo^r9;gjry3?Zm$rDVPj+*8g6=!5aBbr96hWnUc}0@ zU}UUB?v-m*-&8%J`VmG+8~|rpH)ec2z|;!e@Bu>(fp8o+Yw@&kt|qOPw__l1gB@-m zwve<3bVV`ZK@Q*!tpGGZP*`<+ZCx$pUZUWRYF10m%F$4eBZWe}1``Gl`DmPhZP&&q z!!_PjgTheU9=B&G3ONGN;IRo1tB_@kU(5*d83z#YmOMKQ19{K3x2Im{nu;_89kEDA zuW3iZ9G8c+X-#9op^lDV(HN8Vq#&9C@!CAMD{oc6eMO;9!{o~o3Bm0&w3l9m)Pf&f zRW{z>asdYXY9V?xAi!NI^EuOM;xlzYZP+-Kh1_{nH37FfP*auXKGxB}p`|-CM!cPU zo~{1-%U#uo_IS9krsji*@?v)X#NF}@#pSuSC@Ylz;S;O{%(vlCt-EAQ5&P)w;u81M z`aFxrQ5+34UEUOkMspjdkFW7FliMgZ+*wm|XKhOS&fKylwbiO_DqDE;@p+}qblhAz z4-t;VKmM_Isdsh#PcPonm=}%aHS%4cnQfN;TwoJ?4C!nm4mg_Wvb9Bgb^tHw&sZyl z$Hx+2*X&YVt-3??7?;1XCQwL-8q8m9b)<%{ZS6IoGjvO)^WqpCaT-r`k$9L77=)ys z*0Jb$3^xc^)jU(LRukky1ksr^DuR53uo@AaPI;1QoSCslj0#aDFM#t;AEDyQF|Wtt zjj=iBoHN+CPJU_4N)}waI3LN2*EgxZW9#6nJ!c8XTE&xrSVw0p zH!n6}G6WDI)wf`Q@C(0XQRA~I|FeyY&3+s=JtMr&j|cs$cC55iMsn9qVo&ErCUit| zbE6#-BDrkVl6ZB6S+|6VjzB&u`p*szEBAC(RCFHh?oR!LeJo#D;ueE!y}YB!7isB! zVT!+@?l-A5W9#b!bImn|q6rIE&x+L4L}neuE*=Qz#UH&fVZs{|Qwu-b+SH|SyER=+ z8$YIFt;?mwv1Eb4`|r#;^}ykVr-bJ2e(wx*gtKmvYJUy9Qw9K7Rwy-)z7lrwT&jZm<+%7|kvAf~R?ER$J zFaFGEOnu6_j0S_}lM-F&BfKE!BO@L2~kRm+3yHr?;CCn&h(cM6Rr`>&b&ZHvWR zB+fR4Q!zmfg&{bzx0&#twyQ=?7e!A3T?F|u!>XuKEC?C1CGsNCItkQqK9(ux1_fEB zM>C=eRQa;1pfD7&SrO_EMZ93O+SX3`{owB3Pg-ZQScUYtxF>zSWU8GdTncvfBk*qr>xZF1t-VNG9xeqd> z31h`^tC8gy?uao;78$YwNh#t~;}0%gNDLlvA}f4fszrQ?oxCZ`c8Gn0zlMb_)iy_X zIF_3KGvT}$sUz$dyKbkvNoe13^N#(uuv^%YR7V))8Au%#)-D=r@(a&FCd{mfiroyFVNeqCU>qrZxaLwe8j*-c2 zvKWvIYsh&NJw|=*kwufdU4*PdBuG5=+@aM56s@W zb+&ZT?5!6HSG9HSerqSQ_II|WF7}7R?8z@4d+dwHgd6Y69Wy5PK0Nf%@aUNR zBPar~gR&sOs~JlGRNP<&Drg>I4Z!qqf)guJgZm^$V{l}@TqfZ zI5q)N7(!7Fy*TBCs4qec5rDWWb=%^xyxeHfl==;p7niq96QvuMF1h4A*W|J)`5pPA z(u#y5e`$U5dvCYJmoCs*&1FRke(}QUib-=4uAHF8@du%Pz^$ z>vfe?T0@~fH>}s@nzSUUah%Bs_?rJ3=KW(eiaVpvfS$_>tQrI=Yr`FZ;kZ&H& z?nDcseFe&#SqDznS&N*-AXHX{8Tm)o@C-NUqOL1mKA4@P2u*^3Xf}z1KC*GFElOfs9NMI zn8O;~evR4%%~g)e>C?h+rPk)8L~SfbTDw+by1ij`pkjq{{955BaZi1yEnq6Ny2j>r zUi-5mb*-z=*yYMyVs=H{@K>uIo(1qqK*OnK!ta~bB+w~jw}tYXcuvlBy3>3vH4=Ey zI0h-RHYmWQ#`sqq!o)6)I{>& zvV#bodyRQ{Rbx9ZgVDLPrFCXU>p1pdc9ULqtifx~&0oP{$5{BBapOvgz2B18&nzt| zinv@Bv!p()O~g|PA%&ra=mS+c-@<5>neds-EZ<`=TMY7DW}V(OphTiUNV3UE#6~7< zPNy_L%A1oxyoG!-R614X(fEZd8m0(n%gaK$(28O?}+`?G7v zra%2o(xH*{X-GQ+-3a(4O+OW3RH=l$XbM0wW>*0Xgm?1(R&PRkMtQ_wdRURv6D|}H zLZNWC#6NQh3%^5#2a~Lf1R8cAkS>pUQ*7Sl$*Ls_#<$F#U32TrH*VVa$mBJ>h2_gv zP1@dFTRST}{($^$UVd9$U8F;tHuZ6aq=Ibxu3gUugP}s4sQ>Zap@aGPg@xmb5*;<& zn|8h^UD7gbT3emNsJVIlx-p^+ZrekC@t6}L)^sD*a#&I$a7m!(d1Ws=lv+T4n&jX% za*+}oscqeeX#78^3xs%T`{2jBgqy_+2j3U&Lj8$mVTP%9<84;>|I`EfZ3(VdlQ)*e zC8hUjWpz{7JcRCpQAKx>o)Y3ES}GbRBTn2-L5k$14rhS60`eIGb;BT~6 z(CZC)*zusp6Z8(AENO09(A+G|N|aA)UeJ7?xwNF2O|3`>kFHA&u1Kz*q&1nflb5}@ zY_isD(z3(!dvi%?vy|th_bC5<(Oe?WDQ#{pWsjCLJ5#GF5`UtzKPlTpg>XB&x&DQ1 z+g_;OYu0K^`$|gonKW8+>gLQ-rAbur|yq$=ZoR~y3#^aB=%C-|g?SZg@QjkuR%X<@ z9cDAL6y|s&$z_aLn>0F&Cnu6?Fgn0%*mFF#bq=N+v z8wwe`O_{;6z@G1O$AdM6db2|?!RwblTkl7!l>*!cL`qHz;|PgS_0ez6rSh|v%T)D=1c4!uS2L>)Gl)6j5EaZ}5b_*i2s z7z&9NX0iHh0qK0^WExb3Sw*8+BhO(vz+CAJ0<#&A!3*6j$hSLu)|`MX&rql>Rgb;U zzw=|k9&NfPDDn=>RKkY=Qt5#o>1o(yY-@Ow^c7n+Hp`{ zjVrL06$qkH&+?p}d{$Br71LGX4bUt@MTW&65WyYUx3QFGndTT|oXl<&h z@OA2JIzg@1*4nI-qdHARPKP&-IkyJgYZm(*k)Tm5vHJzMurRCZM>?dC77ef>3buNQ zIR=b&9X$JBuMUXnzX=+hU}a{rMl!3RY%qyTI`NVz$LsOHbJ!s{rv_|Vhd$4PVT?}7 z4dyV`Y{sxQ*^S3#%p-3qoN8jjnT=^3)N_ zy!wf|#!pg*s=_&_R*um)b&{!|CO=@rBA3B|OCqj32n|IAkV0BvQCJRnF)D`1a2|t} zON_>(5UtQ&B}FhO3CKiH9fhK}l|h|Rrv^!)6UiBk(Nmo60DB3(Id#ZLmVslFR3*y= z!B%(E?yJJqXFuH6;tt9`l@GH;UDY=pxHKA(9IG$hd7wYYD#W+n_{qXC8*Uo>I~H_d z)^lG>pS5?(gi9thTi+88F}ekhSkfwhUH8PiovV7G5{Q zcv!fxs`Xs0W#_w#7vIs{X)!bPFW5ig#LlYM~ue%Ondf@LQPFGVK5yDu$0Q2 zb7znQxJ7j64927rNwNc}vF(>s#NQ9nmR%<#>4e)$Ma%F_Q8X{-rJ?jv55WHd2r%5r z12-SHlLiy_Dj$+6Fo2wKcmi>grV=xaX3xaRkn=}P-k-`p*CR@(y`rz89kv+#=jDIO zt0`^(IO>$uEV+6LaGd0xz5lUy?|(3Of|RoP`{eVj4uD#JN~wVX`ssIA*&X}jhf5oZ z^L#A1Zk?R;i9PhdUZt#%EeDXvhP-OQp;FsG+jPb~%&us&O!*`gViywtd*pvO2IwY$ zEad@S8ZkkcNPwB&Gq{nLAy?!>u?K z0@x^zw^GjNJq3PnD88}C>V!dgSW-4>K^%3cxh?6zc8D>=+?lEi&gii zt#;EFUzlz9l~pUhnoP>C@~imOX8z&}6Yuk+`um7;aA1V0B1FrGlxaBCLsrTN&%nwv zuh$iE)|j9$$l(?zz{UBvuHk9ZjUS+v=-p0JI?9vEh#uUu_#g>~+ z9I9~?Sc);H6@9T{GcKjxfaf1qdWNb;YZ*q{kflTx>V&W=dj{i|6Dpd{8f=Ac^VmA3 z8cfh7Zsla(9)`ofOcqqZQ+=8q=mXl}o2J63FNMHMl#qr2kUKF=083Dr9;AS1f$I{% z{UM42@jEmeLKqZjFdYVYFzC_r0P&*ZH5i)f951R}iT34VlQrj0X|hQ;ul4_`q6(R&HjxqyI1yQva2L&u&tVUoq#0+?C@u`5(4><-(Yfw69 zM)MgY7ZOL19zyU&Ah&3Dd5`+W%rw~x>1rsWDOzjI#D7EHj)J{%2hL6 zQDg6v;&!vCP%n6#M!&#JYI{Mbv37CP*jiXwpcf>6>5|so9R@4RJNPH4t$K1FRh@cB z^SOE&^vy)|DiM*o23BxYWJnH%w1eu-W1?9RFJA=tjV2?)$l)YI92>=@ zI&extAX4bUF`K-3Efl>9FbVRiuWbGgJjqzpE~ph`F9q5A7h99z#=R<_23WXl>EN@ zUvKTXCix&+Jav4zq_J2vnrnVpQC=>nEe6xLrJY;nB_F(UYT^cq3By2WYH8bIwg6<#(YQuf)_rLM zzK$}q^_cN>-x#%dR!?e6!0)II%z3JFLfoM#XsFcq0bns~ci0TAh!Z}(DhlC`L2#$6 z^$75%B*aC?NDN|WN2H^4!NV^+|L}ny7lwZ<-;sLd7+k!i__0?~PqL!>3%k1)esS>N z7wQ%{Fesn5;#bV~T{hvDsS^2vU#(zA2HBtUe<@>%LT5<2s7s)KK_nith{U35R8WUt z^#wh)2v8^h0aozV(XpD2)lf3UE7XwoB@09wkf>IyK^B_I8ah;85?s{XyP|tmv(3Iq zKJuCqDOQfM(p5#1yB95AFgLXMrTv@Ra^iliXHw^~ISUfynu(V!U(iw$@~8ol5SY|Z zYl+rOxuCg7t#QGo3AxBpS+{7}<()#TW#;^O)0^yeZ?(oZt!w+%>)3a?wzdRCOMZ^Q z@Sgl{=8xvEw~kvJI&<07-E%8l;hEFR_VzJR5bb#lQ@2dawL8Z&wY61QZI?{ZxF$^9 zxak|6Ia9jMSu}TI9efFv__f})cw>R!oq5@umV5{1k9gx%T5nTDRH%a8%nkqHzryxO zUf3=ko5Z;+3Z#Qt4r(|%{YBs^rZ6wkU$@L2Cl97RnY~5&<;jxF-RMMf>bHYgs8rClzow^(gBx zJF|h|PmAb+)*4}pNHNOVC=;lXfmA;ArKJ^z>_wS4P_8E(F6L++el!mtsiJotLDZL&koA%;!_`kmrnBt0xYObF z6~0_^F8Fe{st#1Z%ULpTX^wiV13>-COsED**bl=NE-u?zfMH z#mLsxp;cFw=9ZOu^Ylg$+P=!bxQTW572BL9cSn`o2x?(3Dsq>!l+G*MyS?}7kybl# z@BGT~F40+1Kfg*_F}-%lOn0!tH+%eQ=;k8-x3a5&v!lA|bME`x_p!T4^PK=oNJ9uA zY<82)hZHtp2}wvoNMlGs!ppq(?t5?Y=FLpzW50l~4IiaIDMri>u|-5gtcW!#(we3b z5h)_piY?-=h_PaeNU^rH@{7U$xihob1*|{c?wxz?x#ymH?z!ilduQg(On(+DsR!m| zvI_(*9-cGxqLsy^pFPrBnNyfPeaj>F;3XXkPmkZ5#$7r1XxxMtOO0s*NK6yS@RUxS zuD~B)p|oNm9PZ*i2d4-8^hPE%JqD)q@h59>`+i1p?5k&vf9;X>sozedb8W?$-;d*| z?Lg8{$DEn?c1jo>r=-G)lV3Y?{Hxf%TvU>w@P&;TzoVqy6Tx>raPIfPeTpAie~;mO8eXHHKb*@F z(Eji_kp2JX6WSl5SDb#<6Wd`wVDH4?8{K-TQQ@m+ zLS?IRY3i}F;_uj2pl75 zClU7|W+4OzMtv1JxRn2tGcyuK8(vLzQ~JZVj6V8c>NRG_K`5?Sq3f>$4Yj_BPe;0 z7vV-#dm`G2`Dwg^E;**HKnOnArk|1SS9vH0UMo}`A@3sBqv{&dc`Lmiz_>;X>^O){3BW5ywLa2(5ma&wXHpGX($ zhi!m^7}NR@xDJ($@#B0z19%aqP&F}J*hn4L0^o=C*TC|3luLdKOu1YfiG}g5-{g6jv|=T$m@&o zs6WABB9D)PS28mWAbI81ze`xF2P@cxGT8if&BNPG@*h z0G`uH#9Rl{f5dMF_LKd8|IXF6X-BkIXdOB96!v9amROKDoZOInIr(1dvee_L)9D@Q z=Q6d->Fkc|k?b378`_>|JA=0s-k*Cdza;-qVW2Qvc(K@5+*^FCeW3k`ju{=BJ09=c z)p>X4sVR%6d~xc))Tci-JZ;sq2d2F{ebe;EW^A2ta%RuW+RS4!e==*qtZlO%oZUJ5 zzS%#WvwzP0bG|hf`u16c)=+=7{@ty;pq$a zUwH3@#}_SLba>I@i{8Fy{zbbkdUA1L@w&y2U);XLTJl}omYlY9&C(-F-@UZ|(z`Bw zvwNWX$z_L@o$4`r-sqj$yS?|N<#U!_zWn&|pR8E5;`4o4-_E`#SI%E~3|FDwSbg*A z7uU>KQ(p6>Pn@{C{c`j2qnE#N#r7*+?Kk@$>VIYJv30Z74X-xZv@ zZdd27y}O>+^`qVWyASMsVE2jL-`mr@=g^+xHzaT9yWz+U@9f>V*WdfhzP^3K`%dxS zjoWTKQJPmew15Bp*Y(5tv*pF*d&{p?u$ijzeD!Gc9oa3b^5t4ztyX)t-d{gff2*;z zaoi{vYm8CjE5_*qmmM$<9BCGs1I@>qZ<$NXhs~%;)OyWcVq5kz zj&L?RuN+)*@F_R#Hr%JZJ>Iu`;qUTa3AP3=4{jZNX=u~XH->kNR7dxYK012(rp-4U zx#{(r*W7H~{Kzc>x4eC5;i17pj~sgO(2s6C_twE%A0At9_=mS0xqaI0qqjeI$DBKE zyyM|Jr`=h-^NCMS{q(DMeetgEerEJDU%ESe_ujjoxckj}`tN!A-dXpKe)tcghwy(? z%*NR~|AfK-r}ZO*zoPaihB_s25e@f0dDt^d7-KyVEO38xLj)(Z`M5(G(%@848;;-< zo;rOvg3~DbYy@Y({nZH0YO`oGg4?udbR>fDjRtx=f?v?^{k91Hy4Fo^;=3ao@s`Uj z?OLoLC7uiK($;G>Vjs|ET;r=KtcPP4t|Kf(i1XLtYb8?iK;1&T9ifi5hMSs>uR*K_ zzpdI1a9E2g(rb{~0o+yi?$kEG+f^#8Wipqp5AfLut}f~@luTXt#?Vr&Tir?Sg8sT8 zP4E9A&o)RRAxkK^3%I6ub)jW8+Tv>sq`Pn~VWZ_EsKtQ%4b^TgQvnp$S_6$cp$w-( z4f(+9cpgYX2i)!^sC1NMyn#F2!2~WAN-yyeYRq|eslI3xVu+O@&LySvwp-*h^?!q6xN^co7xCY1NIQAkw zt5ddQ{N5kc_Jq*nBOOH=uh7?UeOS9syGOfQ`>e({SCV+pK8;;iS>B$5{h{yyfvuHNWp}Ba?Hoq$WJnEwJX+GXsy@0RL(uK5$E~3SB zG2VrD2`>F!O5NDm)r0ff<@^)_zDTi(R?`~1$n7%v1a87zLH)EAbI_GEKv&Uv>;cJLv$;R(WmGz-A1?59dsvs zn(iWeewOZ`d+D=uAAOGQr(eMH1HVWQ&@a(Z?7V-FewiMkU!l*_7wBR7ReFSejUJ_6 zr^o0w@RG>i#8-oUi@r#|O;6JA&{Oog^d7VIM`WN~heV^W9s0liEAPCumoz$YSp zOh2Ljq@U7%(R+mV4A6hm8G0Y{KXz*2T6R*TL|SA7UI!_1c(F-A6a}vMicaiznkqgf zritldhM1|%7qi4{F-Oc5^TauLrsF)(CC(S~#RX!4__$aoE)d1fAg&VY#nobi*eEuMYs6-; zMQjz<~XMc8cr8F0ote5jTjvVxPECl*E3ai?a4jQ4v)kMNQO2L*T7+ z*c@Prmav2^9C1*%!V|s-#Gn`w!(v2?ikrmE;udj8+$zSzr^I1#o48%vp*@fZETg-7 zZ8yg~-Q97#EK2u8ac>kakKz?k+!w_wqj*&mua4riVcfGmj8~}mD%6vzo4V(vT7hR& z(w@}aN+T<+L225KOf``9lb)};IX;wR%kf8&fhXN$%`jV8zfm%Ew=RX>$S`bpzOb8V zSGMdynHjb1R>`okDz*bZVb^MD&!}6vnW)(Hl<(?ZBiXQ9G7E09q?>-yH(E03+IqE6 zwTCPd0Hd>UA{{u4OBq(#9?mVuWpr0S@R1aSdo@5-F%pE znYrwJJPBcX0D|>C6-mX zX}!t}p<&1=tA?NQ8oDb}m4<|dxWkH`FP&0ZuQZ2rw_2>}P+^?P#z2ylo^o^;0Sv=- zGBw*}@`56d6N*!mNXY}T;ulcQplgRMFUASggf_Emu4Pyem=BFep)+<<#l?ex zgi64KiQ5dTW{1VRiYuk%HEh2a6$`DR4Fy9eSJtf<)LqveQku+%ppqgR!hw?u0c8)H_@==0C=!gU#l&)`}#wk&{VY|jC%vU$tVDY62?7}bjLxvB#3>D8t z#%8Zlh0x+lsNA&^O*xXpX!f#^$X?NJ1g)}H3LI8kN0ef5Io+llNkcbldF5R~pOWDY zg^MVfhSh{|hCQ5d0e3%3CeV>OivF|0HycN!!4x`7(Xp&f+YfvZWG@Ih8e zjrY7V@vx%yc<_eFoFY(#Gf{)Haa+?N=X3x!RB7g6Vi+{6;A+D4yhNi~&6Z&eP@a`6 zOVi9(SgkcE)|a^ky0H{mw*q;*XA~4TZ7ODkObLy%bk-uLPQoY#9g|RjGr176fe*LK zGCkyC%r{cL?lrwMJSue7R(1_ptLUE0vE_#2Bvp6qz=2z_nkg7$P)(Pm4iAy21U|ab z8Ob@iqwL3UlAb;&bKEsCdk zTe8|T{Ctf?LM;a*M3< zf~sIPgxRAi{!E&wO0S7&BW>yqN6JwALd!05yVPhbME0)iEq5@m{ZO=g2!{QP)>;-C z6Vj$I`#$>j8{~9O4m&(V0it)&fsUsZAStf}K~go$5LTik8<{$0 zcSo;g;pUWGWO*&Y#o861Tnp^FnuU%rd+8=dP*t`mfk0+&}oBi3yY$@+znO zEXWI;wAV1CS#6Ienoyc4JVlk@USUIl;WeO97tT)d#4}u}!a+r|w(gT%B;25!Xu3m*vR~n4vTPe4vz^Khl}8|= z)6mNpk)__A)l4}z6F?W*k<4x#5}-16yR1L8T@442@X)z@CNu^v#TACdA`t||;-DUMaCk_l9+ qx{Kk=rVu5YQ9XR<GPS>b$X_& zr@E%wRZdI{1Qg`ERKc?6xc~A0WB<2^i7Cl^2Z(%A-2Y_45ThzCA}aRH^uB$9 zZxMnHfc%hCWMKYgf4_bHZ|OyVd7v9w>)U;^-fxkDfPgv7S$2Y(>N|cju!HXysQ(p` zsg=9QH@g46Jsf$-2G#R*$WrR zL!siQ#}&N%w0_klvWRwyOkEG73-*c8@-muo+C7K=Bo3EnwJa2(a7H43$lf1EY>~q! z3mwbDz*EeaKAD%~!kO0Da<=BcLYl9Y|AkDJC@+d9(`X+~b8i5nitUFHth3Kob^|K4b^+um zCzkfUZBhJvn6ir5@{`bg_*ZV3kqLJlv+x=L&aJNfHpm5oTk-ekfPQ^}Ai4oNyP&<4 z4wo2xW*l46c-}VDn{&eVe+u%qqksC#~wFzVQ80u_cqNWek zbBc>7*?S&wJP1z?ZJE|9HFP$>!(E>9#}Ap1>aQYQ5{}2y3E|wz7&jtHxVVwn=%hQY z;qjf|^^)n)ldPiv0xXz?KE!&$l;lHOUw3+jrV$bPMc!^m7S$1Rb@bVn8fpmcJZb(dkg+ z@wt!x9qkVViWH;cz*ZTCEDchhtu|2t*sFa#t3yk{U5eg*0j@NXFmdy2gmq4a;U4d| zw+Ti^aFMFVRuw{sgP`21@$TBW+f}ke)6b9Z<4V}1tn9->HAsph=1duR5}waeP+aCN z1b`;+bQy!4; zWAS1tVL8em;&*91yvo~$NY~6YK5>+OOFn+brPzsWhB3F&7ys+#>6ZD2yZHTs%Ji0= zjCppcIO<-@cdXvbX^m{?~DK#d`OOh>+l3d&lcz&JI$C>^4TZZGWx^seZ;RM^z0S&l$GBd=)kwB*_S zSXrWfaCYlS=$YSNz+arKAJVqi*_9oqUFIN|rWr%9cE`qOEaNL{q%rE%+s zn2dxp#y2Aq;f!?q{U%gOA|zcRnZLcxrJ*5oaG}C#G4(h2+({}3sph5Z2uOp-=!o*B zvEA_9ALloGI)X^c)m(a2E5LtrP?2Evl#}0E5>wYM+8hc2bEEL!HNWYx0kza0h|D9(I|EO;H%cx zz&r5VY7r(XD=R9tV1|ifO!Y1NrEH(yW88w{M_K~^&I-Dz{p6S&w#WDnvMCUSFP)>nOjbYLi|+d@eZ-Z0-%(Fmv3*onRo_phiTs z*<<^mNoMQ!%PQ@?Uhq?_e$0(YE&Eh_s4zh9olq|UZWT^@hGr3?9#o~~Zhw0Bgzl_y z%H`~0d!wFfltQ z$ewvMz({&pSbm{NXgKFsWu{mPKwAiCyhT80(2RL^sx&hTQo!9G_w7YIwv87L z&EL*@oRfq;GY+a+UUK-Waj8`cl^LSY%|AanbldO`&1_#UL?&Gbxjnim(w8aUAjIVq zu|-rOsAxqMq2V8p-K$xe5QHuvgte({1?@P|@VYDdm^F`yM)nTT>aVON_|Km*Ei~*E zr@%m~S~`bi^{S;B==r(ZDUmxOG?I6IGIODeHC|I zJ&$?qS=jo=;M8<93Vp@EsFe-9Yj<>r(oDS@Oi%cI4b899W&FS2lSCq36kv`XNT#5( zpf0w(hgHuqXm0Enj+ok?MKGml&6~4ty}XBn1~e9Zt0uln;j9wIc@smE2+wNneD<2`b!F@FG2KIL~R0*pnjCX3Y1jQ$Li(HUa|jkS+am1C+1#x zVak2~*An~Ocr8A&@`1ozi)qJ~=ZadctMC>cv$s5bg<#t0V8Hnxwhu4orpP2nrw00Uc zlYMcu%$^icmD1$$?a0GpmcTTGc8mkzC2wJS)DQ{I^2LK?l9dLSJjWY_aZ77^Zz*tt zc4P(+XwBGLj^^Qs$q4Kwi9Fe1^twrXJU4_y z#19xYv^)I`6b6c2=B4QPH|!#FW)RF#+X?IEmFkxV6yY9Jo)t254Ib5j-xd|M@^K>p zxg_qYevP4}x&G$P+7BmmPUzK>x*Y8cT$IJ)0OZEv6lcKx7ITe;!eNi8Ee2>Mm(bCd zf|k4xm{7R)G^I9h_679;JFu?6N{Uh~ANmG@OJP+ELg9t+M@ZSF!DzJQ!Fex8d_Y&n z3ekTwY)0P~TY!#Z*Jkz}?@7n(D14NQZgbF`@P4|;rA5b5qL}R)XmJ=&7IoFWtBg!F zt}M*`RwZyV3Lp8!`&(U(8?F^E4?+HzS}?N<|JsUoIF|MKRHlKS@7%=gXW#x$@qlDU zlT3~3zFji_>C|5oU9G!)Dn87QfE}zYS4WCZWO2o=WJP7lMGmsu-jiZ2^vXp$`C#x? z>dW%K;p=gOm-#PUPkl-6N+NdDF?csf5y-%Tda7O1YRB@LcON{EcN#?Tz}) zWAI#6CM@^ZQ5t;+1YQz~&;iilU}`7hA%AE{pOIohR7Y{bqXdOjmRt>M&UWQ~Vcy(G z)t#ez39hKek_g*xGi{VwY|GE{^B@1Fxn7LNt+~0WHlZ+4a1()LoIberY?m~&=G4-B zcXnOET5IJVC(3i<*C3XWkJ}7sC|D>MR4Rd1{B+;i4%%ocroOwg=sGW%aBgmY92bTR23baR4$iRyZ*1Y=A z|M>#^7&ln6VZ&qe-zB~j*ToWEx&n1xhlkoFE;;nN9TwS11}8(aolu8i+A=6re%zE% z6ry<61v-u$o!cWT@3Y9;5NSdL!Uh$D)<#;-Nx1JYt;-9_j>GZ{wJY>Fw)c$%sjc5u zexe>U(gArOn|f?IbY$jE`;$uW)t(<3p1$1u%6|6EQlPZpgns>a6?`}J`lDx zZ~k4=6Cni(G}dT)Z9SChi0~HSpJ+M_6h%9BQP<30U^z^H^7Rr2`~=ilT4eg?>r457 zLZULx-&4J#p8j_|`%#_bfr2ST@uS!S3QJ&|mzRWv+|@AOa8j77Z{MwpQHkp6I-xb( z_v_|_bY`QVkzciuol;93a`vQ zs^MiHr->$DQ-p`P6~Q3&^mI)f-sHTTwV<$ofW6QE&t%rJs>fj2s)=g}mtnhsk-I*p zc~%VR)-`5C{`@usmN<*JbqT4Z!Vmu#eX$bGP=W;MLOHBA@t=0Jtvf;`-hddU4t}=k zSK%YgWd*P%yD|r}+iO>C0|=gN+t&UV^9u$*$X1`T@$b2dMTn*aVkCBEr=R{#J>v@E zbRlOsdb8t{)^VkO2TK8aqnVj?e``bll#StP?Job(v`beo8&wSH*ys%dKLUMqC}4PC zU%kpgcOkmYTg_iktGxflzP(=`NtiO7tF%TChCz^MW;~tW-8_>&E-`JYM8n;sXeX-? zVKk@vSKZ4V+pZn_$B;L>aUUtV<@A8(he74E_I0&&)`~{Nb$hDX$S=&N4%^*KI-^VV zN$WRG>wc0ZwDBwR*e#R6^+C?U8ziJGm-yTt?qoyaSIC*4ZR@m0?QZ!CO-6^~WYyCm z8>V#|fSd&%8$m{yQFsT-`*Ka2HfmtFEXK=S3_pzeC0P}xX5<@6wTI@>oGpKP-BJe% z)JH>4UQy%uvZ3@Mjas0_wnwcn&k<%9tcihE2Pp7k|Ne&!TjFH`M@mZsUn~&437G!W%z(AAI(q~1`EakbK07<{iGOlA)ML4}J-oG5fWt9w)YWD1x%#l@ z{Iwi29pO{FP0>B{c=Ae(FA7Z}1Y;2S{O=bi$H-?@{~^;PiK-l2|VRp-*vxy!A<(dM`QNPyViJ12&Wy%n%&V|>03~VFw9YCiaPALOch&Q z_Sf+HlkGG4DYzM>{*71uF7m2BFdpH}--V8$WO8LN+A}QFO48--nJf4Z?XsFaIqKv2 zV8e&LktQ{1Imj~E5$%6-cWnTvClrBbk^uoHQi(CLQ&Uo<+zn|B@~SmT6ZfQOznPqq zTS}9bnnHgsIb#8&k|#Xh_CT4?{H$Muv2j8RnX5Z2L?YsKoI5#eV_Q$2zC_We3g#X= zC|BHD-;*lnLrczI9~f4dLqYcL*b5Gw+xho%vhGj*GB}FuMz_)Zzs)=A$94#K{!eAO zL5$K|I*q)&#cM|aqU5Xaya5~#*VEqONEoj(J-_27yNne)DN-Q|Yfll)Qo6|IQ=b;q zNgTSYUBfRpR}DD9=gMYwk&k@jkKunh*(vv3qmit>m?Lbb8PNN0f#bQU&WUQv+`$-B z1T$o{h0h!X_aLr0^6&5q9T-G4sQKl_A|u*jv}e%^NHIhMQNo`CpTisGJbw#3Wli_( zx4we*8a7aDxTEM|-irl=W4U zo@ZTrZh6F`I~@ZF@+cSTc)g=Zm!{17i#RIA_FfF%jeJg^WTY?%fZXHrx6hsK!~H=l zHvHKk;kW}>wrSBhahlN$gCvqdYjH?p%vu5!{Z_w-r+BV<*2zfFQK8qNx_n1X6s$>u zQ6~zqxWRHMLdQ^EhK?}=c+IL1U5X-_Z1&QegVztgU>EO8WEirqWhd{+EYf)~a@=TeOSqCgDZeKe;1KeHv;S1$F3%t3$6ssViVjB>yc&f9=GcMRY z!>x#FTAOw}*Y0dGo1Cx0e*%I9n4oo&IBSXBA<9$=avYwP3#!EvBjM)A@7y0m7f3UNp(@Q9L-?jk@MC*ca za)TGEoDh_~W0540;KZk2>x9wZ3(T?WZ*6Lw=F8*8a4U{H1sPIFX336^8PJI#5P5;@E1hu7-Q@pkx!tLSdB2wSzf zyBFmixHW$o47%2X`R=H`T!$6RrYEZd(U;(m=BFpk;-E*~+A?FOJ24Vlm2->Ne>WUE zSK9l?a3p=Rf20haZOOpi%OhCL6rf~@bY-0{ zxcKfP9A-1jZo4ZF;@1!LaT5oohBZp*JEsxN$-o)o0?=5aJv7TqG3Bnupkka9El=*! za+>50^vO2!iG?T|x7?@V=vHy!123AsIi)3!7>nk0Y!lfCU*C+!0m$ui`VOmj%H~d`w$yZxFsI;3Z8v9|2&wx3J1jhEa$ts1jZdApJKqFL^;fH4 z*M%w)tma4khE+iV8R?njIXpXfo!Vg#M@yhEOdc=VU8ESwMI(e3v8}TFL?Eb&|m{K!{Ucg{@(mQf;V3>w2T4#* zAEt+k)eRJ}gfqF}n>*2x>ha&=r4h-=r%=Q%129#WsN~1uk4T2Ppmo(W@Y_Vk*iQ+^ z9f?)c1Q}3cXNmih-lp|p-CAPk5LTOE&2%s~43FZ}fV-Z>M*DIuwcD`MrbDh+5usH$ zr}rU^G|<}zg_VkseUd0|i}<{jP(xu~5bP4aIfH!RYt{1L&(&>;EW5K^r_U?SE$EJ+ zx9g3=39XGM&;+SCDHPU`G_;7()Yk81^HD;p0`70Bod!noMTae_%&!<=RfO2T7ln>A zIojV4Oaw0kW-a@MuOlrT9*q?vuiN;iUli8-O>c(HFT!sAsJ3NzB{y;a4gw6{@^0`F z4J;VGA>saK!$}h2c<;yzY7^=wi6YikE9T>qZ5mnq`Ps3CI-akDVWnf&g}1~+`b*d^ znbBNa#R_>GCTt?JMhzw84}w~JsY3+vn13 zj^9Tp7>-$r9Veq#1~yM|Bps6aPspt!>ZZ-4lq}_IMCEof`-iC{9RvXZP5g57Pm~U~Pt5$1zovU{%mi^zw!`_V;rZ~V3ioY? z7?+xP1upW+&=6%FNUY5oK?aOS@jP*Z2_iI}uMYh!A)95{Uh$NAI%8*xE#0GT48P0`L;pO2L*9U*c z*=IzuX@##EkH^~8Y3B;zD*6yh0~c`zNkfW`!-S${i2cM(S!+TDjs zIi|HnX6Bv3up*wc^6j^nlw#a-8)GqaSca$^#UWzJYJsTF%HkR^O?gE}rfxxUj@|P; z?0R`mn|CGZLgplF*`j`&9rQ^}a9x9+7LACEG<1c91CC%Rl+(u>^IQXJ8i_K>7)pAy zv{Ge>a_a3|EL*DTxPQllq`|3X`~$cUFUbL>0@v_L}9+ z^~Svk=y*7LSu1;imj@*3ztdAAunHDWT#g#OLuUvzQEI)GSmRhVihHUlGPe+zF=(|k;PwrEOd zBvUSPFVblcER<6&Y6=UMv>cejqse}Fu(;*6Cs>+hB<_>y7+O9_He~P=CaPJzA~VGV z$4HT*eb&No5^b}uk7%BU7P$I@PEn3$PX-TOY|WTn^BC5~R9=z}7M`NtqBSGgB(YCf zY=0Pem~>xvr_z2z_wdK0E9v0W>0}hv>BLU&O5&bEvw}e0Y6m=U( zdM^gqaBpy)UkOFrbR&_`y`hx_gQR7sdFa)UX$sPIc(#sC%w~yTvf!n${aMB7%=n7? zHgPt_*ki&$-CFv5Tq38-gCp=0E4hP>9VwzOBb@;QCsYS(NJD}siSnvn;q(Eq6WVsx z)t5I~e}4s}tLC7TU7qw{RylYhI<}f45su60Fs~6@F5G@z2mfZc zPpC~{a?CyV&}glU`lU#rW4wy14PLojJYiWQ-&>PBPMCIOq5sN4(fZfVEo-It5kO>( z-0cP+c5NZy;sk=hGun25?MzXw?2Nl7RTBt5yf?w6X(yOadjZaX;{9 z&eGWy=Dx4J5J{naM2Z=u+ZCTy&ik=?;4n39C#Y1&XrfTYliB&nzt5`j?2v2EUqi?4 zXW5A8Tkl*)@)mmw#GaOhN?fO-Z6VB1Me6m92vF z!H!j>Qb&j6K2qbyI7;y6T&?&-93O)4q?XwY(%nACKdVU3*6fp+*ZnD%JGN)aVkx~T zzYjA=%u@?RcO_F8`;m-TXF$(pDjSa0s9N{wMvXUunti~`5a=1=5N>GPo;@huZ7Blw-Kq0(b4S{JP+f3PgUE{qHl{~6mn+njuxTv9vj zrM}(Cn_6U}Y*#zKYEaaeV(zsk!L&ilA3I(GAe0@cA-Iipk`{NOtO+sT?is4X$I5j? zE;$*+x>C=*(aAq8eQ#DC6rNO`ceN#h_V;!Uj*n*EES8tDFj^?#Z!=Vs6G6jc?@(u7 ze?Fg&i6w|8Y!cQiVJ^AG-pb6P5RGI{88{h8sQh5OCGAV7|}0x%8|ZtpsoZ0Vr^u3RfP?`l_m(qr|C`chpN*<7A4R#7tAsY)7P ze(o8b(g^jk@{#LK8u^+7q^}KsD%{3T<{l1S?rjfE+&{`JMVA4m4lc;eN6{|H+az&> zuF@LU(BH80t5MZ8V$k)fDq~?lCXc8v09z02tRoo~76 z*!*;*C-|lZErNu~3hNchWdjtr!!6(;dV?W#4Wwse6P=XvPTc^Hduzw&G?!7vrH^T( z5qmKj=U!afFIB)dxcR0h%^7iDZ5qmx#e!dRn0^Z3^IIVtOwR_9pM{Uaikq@NC<6?` z&u`ZZBfsL!1A5fL%J>l}tC+JSqqrw{K1H&8b!5oQK=w+@@r8i*bRC_C2{qhw5D^nW zh!pnJ;SX#T`J7tIw(83E#P|;HH8UE@DTnG2zk}{ZMNP)^Vkd_@(K4#MMuINK?J=eU zlhBOH+>fVSq zO<(JrTlS@q^juk4-D=-yk?@AOC02tM87gk`I$m$Fv^XE%ZLXKXcAGor#SEF4h#&S!P5*RR`0exopuGp@Ue$7luUpBn5xa#G?)#Bl@1h7*%(#8 z`>}yaCVLD4wxk;R=Z;JXMMaghD8BB;ocenKfKo)np*y$hF@&$R(_+IJM;r3jXK>7* zb`?;w=F{O|OVbLn>#;dG`}J4DgdiO6c0=KaT%;xc?S<%Cjqhc}6Io&)O=hX&J>b%d z7hT|ZROSj>%aILdsiNht({eHLWm^Qj6>7=>zyV*kOD~Dm!HALNH~JCP*uAlUrPbYP_9W6wc%2qIF+rB7sE#5OZ%Z0|Rs22~}tK1kE1ui5v{9OA)(+fv0bZ)7tE$ z@uwq%n(Mlsv-;-B$a(i}cw=WS{if^DxM;*OMaVx8nF<%3uOOMj*eH%fA*t3Mc&>iq zjUlP}*=}I2-dPOvWB5N@*fF^WG9}?1oiO}yZQR%3y1NuUZ*Vr-b5);kLTm#&cF|iq zo)fp7r&ivhKKUxN--D{x8%1vU=zWeJ`<7wy!n1#NXCBM>Bw$JMJXR4F3Rbjb9!Cr?&_bN`Q^gC5O!ott+R%cPpCO zVs46N7O{2py?O%}>IZ2}+%r9m%EXl#V!A*j9z$VRHwE#ATM-Oo>-l=8De{X6)Pr6% zh8^(2N@_6gtl1dFemr>#EDWl3>d#7O&#YMNJv8NWxcHz>xs!0`$sHUN7ItYhD*L*2Pt zWDaQST>!q7(`_rr+42rMbLH55cUhy|%=fg^aNpLj|9MXzP=XXxx=Qs#iqGpHT8?&7 z6!OQ}G@>JZ=stZ+0hmO~iy6jc5)xy-yB4h$c#NwJ+m1gRCD}9&c@aR6VVoe@Y@t46 zu$#l1e0^Dk7;;|LYA4L9!JR;l#!%=H-0Hpli_WnNRZI`}1|!!3padFbEi5*>se_!- z$;nE`adT69GCE=6*CGl0nhQ6dV>W6;$+$f!4g2eF6UGbKNv`H@Fs^xdkT3uaVNa=y z<<{CN(S#t`tEs0%!+%_h@H5Q(zSOEEb%tFC+wBJX!bNe5n4gt5wt!*{`lEW!Xzjdy z@xgq<826Y?GJ1r(GY_b%zm@p7U+%O9ZC?kiK~3hspk&<9n-G%A4kjGC00X=c;rOY4 z#q0eK7k+LNc$0dDP+S%WPD96u0sZ2)$W+Xfv%Q*fz7F*YD}3(}z?Dpw60k#=j0o`& zl}8FCNN)T)3NO+pjx6sdjB;PVNSYrya*ptQy1s-jLgERQ*32H10+YH8GRaxf>;CS9;>dp6+duUCX~A^mJqr&MvJ39p$&%X_BjC zgVm1gi9G(*d17rKP+5dSL03~s4)W1vON_ACdjP`KEu!-vOZT!TyDGBYVjw;k%tlNm z?H8dtp{pThq&; zQKo;LPJ(;9^zV*G7TzU`xh`CoDoefMcRx{gcs!oR$6TbUKktA8K;p~YV`rJT=4$k+ zsVbUwpc4a|Tj6Q)w$yO!uvcO1SKi}=qMYD1qBDk}1>qI)4@9y+%ADuUy27QkaW4a# zltqU72AoTjDAUYeKxImvoFf`kXKrVhj%EdN`pB06y@+N@;5!{RzE)DBCouxJ*Q z1lz_Frhk_*Zi*!v&zZ7Iahel}8Pf%_N>|E#GG4-ej$AzK>s{Wq z2x3@14@^cA#%E|&chd@$?Gb)r zu!%HgjRkf868>Q`z%hx6tK3pwJ6?|6_x9JKUo>%4d3$0GEp$)B>$2|NZB1;_2Y+Q55ay(j^PTTI%pHkj? z=n<&$@z#9Z7<#~unCY_Kn(pvsd-5@Vd$L*Q1vkGsBIyuM+d$J@^$zr{U0&tHYPr{L zD%MGI&EA}IH|JQ4|I}6qnC$>tzQw`3`do}tmfd$EG;E8GwCovgMP7qicb<>5Ca|Yi z!;&*I%6bY4o{s48a@*eOBJAs0f+y0{?J^VFTk5dcezUk0b3pIZ)y~i|UJu!`R8p)? zI;WD4RbKp6Ogn`x6~gJsOS#4;cy=TVW#iC91+w`UcfM39bZ~9W%sXa`H3~n!SvtsT zOm_F=T&V%EgX^_R>(+v5JBNR`=-$kP2B8)m9eg5?)cv<2w%;@B-of` z(1h*SaZCdov3EU_Ch6wD$#xLg3pMvtWTfdhKEBi!^Wk3L1s&6olVndKi$=Xu8eK&Y z;0J$;w_68rvD3=)bjsH?VIUQ%i5S%UKayDHyqwf_w&gdMH6K3GX^gg zUIv=E-B5e?zwZN{8lIS@qkeY|c&>>&I%FKhPl%pJrLE-`=xqXndUGQjs!GO{P^pvh zk^q71UYX$Kf%=iMR%CPm17mq*YlbT>wQe1-=JDI@vB~3~XtyDNX1JZTe1WFUrDv)H zo(-yrt<7@DHriz~=83Hm8QGiQ4Ehv0@l+o5OhnjvSXNZ)(wTMMZIFlDQ)%| z=!E!pZxd66Rbe=Am6Qo%JjPf)p?UM}YyJolDk#3JqEMp*QY|7e_QQnmH@G!B!z}qa`UmNVmA?Z@k`~PA z@O~4A&a&r0Rr~QkNZw0*275Gdn}+o>3)e-M_x>mwp$#0&e_$TxRxXjHPxDYH@Y!MV zuo?$y1ZqyGA8Q16Rmc=YCr?JN=2smrxRD^Qjmi zXwdWMIHIM4O~0q`yfrS{xqmwu4{n=q4$&UA3xO z&oAYXNy}Zs#_}2RFGSEEp zE`VO_(PKBHgWnTM8=rLf2K5Umfp|(us$Qrf?)V9-+qM#GTN&5pEDD_vMqQRT$t#3M z0(S>~DBWvtRFUv@Hwxq6kHf!M7|3K-BGqJJSWB%22>!0@o?55>^tw)hU_!Dl)^67O z?Gwxtt#*ZJ6O+w#KdH>a2ZY)b==-_JYbh4Ru@x^-4eZJN7^4euUgsgr!OeWwU&~;B zrSGX5;*q<6DkhOPWnvg(4+x<3>Bp>P&_TIK)m^{*3qQw_9GD;AxS2f_(8AB#Ra7S+ z^Y8RCz3bx?Nb|%ta z9y79_M3F+Qe5f5QS)`z-pR@q!7ks5x-@%-pv}*wk)G{|ECA85<*nV@Y+gw*6X!sHE zD5B`3VXZalk#4}ok1L0Drj{A2SK5SRq^5&62d`*K`;ASdfR)bmwJ`>l{zETY_%RE%KV!$b;9cUhOO$ zUfZu!Z+r=-!wEiW<`q6laNnNpk?&mR3d%D3gq^6-*|3m9n11l&{cH=6^gQ3INb!A4 z+nXr7T+b;Q&d*9ni^EUwgWuzym#}Y3oiHR@atrQ2`_s>E8V91=7F0pHV7n=i{nxC) zOd2dvV}#nB>I!Nxzg1Y_hmRUv^dBN|69zn(dun=4(jS}r5%l-f8mXp+x^a6Y{#L|z zROt|?kiT89{X-cs#mCzx+xfsO}H^+UK`i=@#P!c|kTtFDOfRT2Uy{wvGV9PaN`{`EqZ~eI=^PA6nF7A|(5?HQ zkgnEOG+ThTz3I_N$Wh~^R)YN!mJSAT>Ka6D>Rr9oAJ!nYMMsk;yaoBplHy_fg(3yu zuDQsAS2r<)RpnLEC?P-320<@{bl?3PsgFn$k9mIu`-Md?u3G?8VpFR)c+PgBTCdBG zp-a|F7F&;LSaCPSQ4`h}t5>YiRB4cvXeDJ`QaH)4eyf3pw}o4=u-u9TY2?seE!Loo zS<98TW0C%xhcPD7O|GTgnTVA7M^oBMIx%8{Vb1R{#AQM;@q5<^28&hYH8GqdS#drv zG%y`nl=p!!hVds`G)lHVcHnYaf>}FJ_>cGGiQejWF}u9fWVsW%F}#3=gFg?o*VB)d zgU5oGq?Vr60xrCo>+JQO33I$5sMHinfoq90ar8qKk^9v?|^E-ahz(2~neOa1OT#p4KDp|p?ZTL$#XuHFw(=Bw6 ze94Q3l@ng|gxJD18tHFR@AQ1%;m#MXp-WSDUR=-q?Eb{H+3TFMA3Vbn5HO`=mmp=G zy;DlWPRYq4OUXJ|!pOPWW+rb+@za8qVMJ_D47R-d5G?6ViPx`|J%A@AyF|&ID~nnk zGnax5oie{7q&1BbN?Yi@K6P`PyMaC*hirbKKJt~VlHR(sWXK9`7zw_6+Jcz|Ac`D$ zrl7i#W7?7_&~n$CnRjlo=wZRjX1X%%<$a`htos$Q`LZr1;QSC{^4X0#fMNT%D292g z%Fy-I#;5I@UWCw^%pf01h!wUesgvqrsog8Ed8~aM#?`laRds7*Li;J;+tqE~I@V#L z(N#jk{h_+k{=jsZw!dcn@Q^}Vt$uFp)p{DQ+j$?w)zFdBOp~GNzT%D^B77?mg&3Jq zl*=73X#iH#@iTdNu1kpWr=~%(9dbwRh6FeNBJ>tWO~z}!tPmUDVCTfaR;RtNHuFmD zWUD!2&BsIIBNPE6*P)TA_+>hG#YJT5o*<5{Z5EenF>#0fjwhtVs)nhPi;GiR<-?TF z zk;~TA673(NkVaj(KBc!w@05^onf3r){p@)dSXW+z5Lp53b?WLjJ5O4}&eE6r=G3#l zy9na&jq-~fNu=eZP^F3@M#1VeV%Q;f01*?feWPUTUCiQz{OtlxQ)i&@(#7sf8_RFn z_zl(qN&8!`sG8}DRNz9@oyZ(9k0j>gd*tGkRe2Q9bZcMCsT=#ykBxk8cCY4Gdpwh0 zy*~CL>-Yx0fm$;?pN@TKAG7GRipAf5#Ct~Cv$1(>jow@A%?Hzd978^HCH=@W`nU%) z=`da;>@~y%Ys6noaF$BJ1F^cNy>H*x^%%cTvmR3HCGw~F(nf>cj$+TE&m+X8ZH>5w zj_*JJ5geh<&LG^&-3>MYy%*rG^(k7ws@ z*_b@N#vePW%*V5wbBnJ{$8pss)61p$TJkZ175bmw=WhhQp5(Ib+)Sf5pivxQ6zlO6_a z7r&o1Wltfm8fboXwM*@ zalz;j)vkuSndmtIF_CJE`<2E-gZiOYt@q>xMD!(Jvbu1Sx=WwA z+IJPe(23K1LI1ChdzPLb+7YUrTh|UD7TbSc@KLI|%C=5xH=IrpE}O*9w5la8YxEcv zeV4%MfIM-lweSDZN}B#iA|}#o+Oyfopn2|)Z#cSB_!yEau@Ar{XjGwJSbJMrd(RH* zAS%aCl37VG!#y5G2!6MZW&nf_F#W~qK{Oc_V4Mvrb7rR zaD`}!x$m4bqEVR%Kr?fL zq~QKRCFhO|PIXCZy;8|fbQPb;0^ECu@y=7uu3o+kH$<#({Lu|yC37Xi_2_&M#UP_vB*vzllRG-w1(FRoe6UqPn$t=7S42cMJGFvl+IRP=vyce0b_H5T?##eWt=$YhyyWe?nneKNYaUvqieyUY8aa+3$I)Ln>|D*~Jl z<4Ewq^?;t%9c#%ZRkJOfdR#GGrmDn)lZPgl@3BQD-x5QuuO@^qO-Ns^AG7mEQ3$gEkR)fL~Y3alDY;Pl&n}w-3HeGCb3d2QZUKx?qr>rf; z#Mg1qkMigkZBD4a+RR%=l<)8--dW2Ay=cvslI70vs?8_vtv%oGOZ za4iqRHSUYxDXJ{^+AIq+nny0%+*4Va-JLEbOgR(EEVz*Kn7CJIWsW$3PvO~GMqkz{ZqoU~wYPiMoO9t$Le-2q60_uwD`;<&V<9s)7P^2IFSOJ!r$Yj5Ci>kRS? zPk+I@I?EQ?J*F!&@WN_3l@|$AMNNKAHmq#klK$c#K#A762^-MdahNGs8T4H5k4hfJ zRWPh_TyaB(Dt@~o)m@mw-E$A4opDDRKp5)UbktNSHf;wal=;EX)RVithHKI5U~dv5 zEML6jw9DXf&g^HeIX?T}A-YbjHweU^tM5+J@7g2bmDlz3R~UO)12l!)NlQ-yRiGMp zl-KgM(YRCBbT&Tc8~|79hF07`a5K_oQXg^~Jc#OAq%MpdrgVS?BsR+;jG5TP5jf3Ffl+ zOXvV|59xBeeytPE*WLESN^7lfpZl;gQiB5O_KeD~>}Xn}3brqixTGo$F-0t~XP>gN zT4z2ra&~LS;HK_HtZg-6rY82HZlf}7Xl+%L`{MrxHbBY0^g>0um3@>UI$m$`q@GtQ z1M9?AoyS`1oT4wqQ?;v&4Oc}-Q&;G8d4V-+oJ|s{&pAoYoorN2Zr8bEvpfk5a3?-Y zAI${6CN&fE53C?}^pxyAdgGKG(F;;M;gVBvDN!bDDU};%#^hwAisVc@kz`Ra(m-wx zJt1h6gu9)UP&0G%Op)o2rtX0>y|#;ZnEX8+yPizK!%|4zxD{v(VOnH{7RazY4>epT zd1OjsQbH@v*pgIaMb-=PWg=C<7$xkuwZKq3!ZyaZ8cC_?Ak{6+n+1 zmLiOwlFjG_tUCf&5sQsb!!4BSLZ5VJqMxA3>T#5y^<*ZZxi;_VGUc$qbH}N*RA{lvE1e=RDr0^|+ z#V_zaUX*15k|^*dRgjHdNsQKpBuO^&gg1g&<|8)IA{Z4_wDLx?QRK}wg8~k_0gR%- z!21=oPOg(gFew&dm54>b8b#5-%Rxn`afpHdykO;9+a*b~ldwUwN-}mxCW6gsuuBKe zkVS#;icx|VmGBm@124I|FmJqhwX%+;tfp`IU;A?pxf<$~aij@!p=HeBri%52Z z(IbfxAr`ZX7wZg)*&*8ea#SUvNhYFC#Dp$`wZSR!ga}3=0U)mL5qS%a69J<{OlDOE zdPN?VEh@cyHw%O|9)}U+7Re@yM6BU!MIL)5D#T=v4M6|dWJLk1LvTy7065%6SrkR1 zS(d~GUM9TYAr78*S`<5PHu4T)^Ei&abT_Z^P6=eAohOQ5l4Lqn1l%^!Y&1zC!Nnx< zHltOr5S%-r5`mZ1IwIKZaFU{s_B=R1F@tQ7B!fykfMDSPy9Ggt;Lsauc+n&xc#Dcc z0B~Fhh>`$;T@s82A{qtBsPd9klpPj>T`;&MBG54sJ+@lWV6<3_B3Ny_{0WR%2+B>9cFnbADN)m$rx zZh^K{V75zTOrBBf^dB6bv=IksuT! z1R$;iU*co2wurxSoZ5~0cGcYX$_X)RjEu)*_yl>)+xFJ&x>C-p>!#W5+N<9Y z@4d=sbCm8C{)owA7cyDrBbz<}wg#xCq>Bz`7e*HohSN$zcUDmP=PuJN< zy@b*sDF06J4cCc&fupFumKV5D`cW=wLjNOKW@P61@ozL&W^++96mL%Dq4c+i^!HUF z$9R+;xng#XD*m!>M0JQ)IT|#TS(`h-shUbZ{v>kE!f%@DHMQtthUPfc2XDe(>YEZ{ zb}8A+Q8~pn_MMWdF$lTKHlQNz5c~eX#Op{xzZ}2`rEjXxYis&Z^q~`2_6OX?J{Zzj zb}-bpQRMPPP7CVnlVRGmVH^Ug0Fv+9s2c;{SZxz$A;%dBWfi!`z6fMwCs3Kul%dKw za{1#$x(zEE1|{_Ipcz@L$ZHS4Id@^F%O485OM5_j;4V5qrH=sJ1?OOZ>NA@g>3tMS z1Lt5S_64niFU~A-@qd^+Um!6d7d6O5bI}y6ZkB@9EvmX4BFF5TJGdF#Ol}Uhl3UNX z;*>zK>)eDaB0@0v*Q-n1xbj!5nF$9b-@^oMF)t~lAj=;)fB%Z@S4;g@%%0mP3gbU_ zt@JJ1fAjujeM;$b*Q2_fJbraanv@T1U$OuEN0y6yb7x=CFI}w*3lfCFN|;-$6h5Gdlcr2mJ|5RM#**QStS6R~}q>`hTvx z;;Pka*J8=zy(OEIl+Rqp?*9-jxU|j)Pylo zE%X=&K_cylINahtJLhjbp5HpZ6aJYio4Shoa@yP4yW|JjyRQ7&Gp@Vt489ibED3S# zn5V6TFE+&BPHjg_-*%uR%P4b8xeeS_?h0-{ciWh)e-Rjuk?nB|Ik%RUI>XtMOpuky zG=|x?W7yR$!?vkVZE4aegE6CH`|iGZ^*WQhX~n*SE9V(4d-hn2^Hv_*w_=kl zHnp67;O>1ZH_4dNa54F+)nT{f10wG~zM-{a`G#|sB=lG7@{ZQTl5;ocFR%`Utf%>S ztB82guZGA7?wG^WyuDTM@k9CIzrI3DL_Z{b+NG{&#GXTxZ*QLfGuj7lPp?|K>Z*Y| z(yJOQ#>I<`mWEa7I|gQ7m^f`!>W;zo86fn*UW1&oN20D=hWRfz3j1W@kAyWD@XDU?i4Dj{SYjDa{@DC8QM1+f1&+?d|vy7_8I7+x;*r26~HwPjs8o>>psTU7EbIF zuNJRnR+(L8ttj1sMoFN(q~!pmFC2{d-4oJ_S3kJxrgKOCx#P8m9=wd4sdU>dO7W4? z&f9u$fH(B6$gS!vKI045$7|t!rN?eowDWo|U9q;C%s=-NyB<83H(d7Vhkm!C_=sY* zcPr$q!9!aw7#RI$@2cF2UNXNXULUN}&cnDK1@7-&yW&zTY|}V-II1f>U;nlTlYwL3 zjTzIgcO=U!uZg;#;w0Z11^OW%j?d>^iuNa^-KO8b<#D)q9BwUNrJ;*q$Jp&0&xXIo z-^e~nl()`MpjL5}73`05y2S>VM+9 z)i-O$@{JBlctA1ya=wX+^l$o1MpKKUBluo87wkgSpY|?ScLAd6k za)Hk-`!)q@yFCn>yqR!;1RLeAP zZQZQd$(bt`cC2j8)^=&%(Z|f{RQb!#Ij8B7MzbR}aGiFcc1!npEP`a)^?eHEA> z5E#>yNiw>TR;s;W1FC$&4z|kW03WLQf(pZam;wmJo6}ic>c?BMxke?aB&IO@0h9cL z@A|#%`)>rHV^`lLipeUPS6MsKYxi6_Z*E`TFXnHV6?+>#B{zB7V~dt8UUt=`%Ws=$ zGf=wmJX^pfMy9v)%wC-9ADrH{JWTRq-`vYZrk}n3sr+@SIT~MfRhP34Y0CRL*Uz4{ zcJbV~J+4-N%?U1%zGQQDMx?df>Gn3-%?7LG!uCKsHjRXr#0@iJQMaeg*VR35)#Cap zzUVph)=7=G>4s@ppE|O#*DdJ-;&GS0#-sOE?{TX>WHvz1@_MpkpPQlSJ*sDHcLaLYENxz%vX zxmL33#epl3)}NkOEZKO2RdU;W@g@D+E;{(cuH9YT9=oGfTjOz^}1 zuzzBGC+j?x?dUNn;wty}7>%1c?xUxyc2jbf$sUMQw5(!V5bmfrwJ|4eoh(PQ3u7U^g09FvhQlnW z*h8Qj5hd-ZN)9s?#8Z7){Su<|^-CS4q~FdC00Yso9XCTU3-p0cu6Z;@m$XM zw81kMhQE@SdEnhcm;T_|Swq+CpS$J3pgAbFOI}y^x=;M(GkZVx&YJGXt}`0`Z*%Vf zA4hTbjql91>t*+v?xfT8Q$1Na-JQBl#g^qNcN-g7*v6I%xMPFcVH=E1GX{)lu^Bd2)ZIb^@v#%vMgOaynb(GPq9+38qe!&#@{i%qyEt z{B6RvCs*~K*l}L@^r>1iqhdK@&8zp_eBZuRO}KKFNOkiZ+Y+1cDSR2pOF)v~W%E6c z1nWTXzh>WgX?K0!wkz6~-{E3ax(cIJY?*)ft-CM3|C4!5p3U=$tJ~JknpiC@S$3N& zJyQ9(C03-@gsBx+w&5`@4NlduI+cLqiLV)zT$GIy>0BN;Qx{J%3}HgWvHQVr3`a&~ zjb((z(~X31_#>6Hck!(b+j$rF$6Q9P+E^+2j0GyC^rw$+S@EDNVE$y@1>r^Uan=>* zx36k((QiDkMXCr^bWH822(`C`BGsHhsb=@>lO`W{Ys%d_ap_M}IO&^8)Cb(_7gn}; zbdd3AJVsA}&m9Dl_-WwBm$1zR9pLz~OKWHK_gD2Dn7Q*xXUetZf$rJu>$}I-G&+6p z#tEAa-4NnbtWFi5x_IZq4{Yhf5kln789oYmz9^(B(Hy)M%@MUB1r|f_+r~uQEs(BF zhb-Wb<0$Rsy*Ry&9B1*2>n5#+=?&zV>~x5BEQ+K*+(Z%FMD!Y^s=(+ID~;8h(H-qy zH#^$3ac8`7b#H8|yLol{`OB^2;)}u;%-aJ_?AzBhE!5r~a!2Cvi2Ir&(tkHzx~;d# z?@HW#)08;FsbGoo=C^)&buY6f(@I_Dpxak~nn&Ydpw3s<+tj(b*;x?jrSELow{zx! zzN-HIS+$qK*6EdZ&!4n$LSw7XUK6Tm?pj(uaM>PH)%c4#nkU82ueQQj?Ha4Wp6&+oO_}@SR?FH~F>ZtgwO9qwk_nwFZ;j%lB_9%lJt2r%p$6$&MtO9@X+UOo?Woxf zbG#-t+%&aJi*2rDQ+FQTIkik)z_L|`PbKh}#3T-X9I$^&tT8+WJx=t20|x1Sls1!fLogOlF&Ije;uujhE)rrV`aH5O zf}~iR!6ip3HATneYi0g(Ihg>1qzn-pge1m6NCFZ^BFcgP^0jd)0WpS%Hp@1ghFic^ zkKBWpc>aCF499c=#+ke_%V39A0OO?0^0RO{Pp0sJ^mB*j>J(8_*iGU@{g@+jwA?WO z`%(#!y(pD{eKMVRRu*6qrv|j5i|IR+7y+SxW!EGl5Wb|V{y{LYzI;iybk!nNTX}QTibR)ab9tL;q4c1q z<>FaW*<{;dx?$)866tTR4*Y9rSygp)RoS*b2f^Iw2gA~-IA2xd69ivT6(9f9R(50S zwEkZ5&L2f%{Th--Se{1Qu*hM{IJS~_J4h@R#yb}bRlsfbl9WwwzVswm3|7pBGncLS z(K68TlWTj!Y7(o;w!0^QJ5*0rMb*lYClLvH#npr(7tlI}?tTrl)*>IEpQ+%i7w z45!`(*Ml#{jXUTXS6BSk;amWTm%Spr zf5$`8Z!hA3V!ujn;Je@4(*Nv%88Z$%+rQ+A3H$TB7Q0si@y0tq;VX2Z^n&#ME0^7{ zS5=@mpoFT${pj@9&{bXS2lBicmtVN{vR6s4{XUsMCQ(W1R|)jB)BtK$T+)-fDluzsBze*lSo0(6e;V z#G#W6ssOq`ZBZ(T6;X?BrFNj3D$vc%5IqJxYxJq8RAZdF^E6eC>Jp@~cp!3YHDAXT+0O7|gHi8*xS^S`Zj`*(YYKmBEw+AY%&wwY>QHLe5bW;xBCK zHJEyCJ76+Yz$N5JN(LW->GQ6>R`h;%rB}QbBW{5;V9FQQ0U2osrYWP3f}QqCox?8e zW~VkyJy6m!wP}M+KI28Q*esuylurG*sOVk5J&A8}-51gmnQ=kJ1+(D!k3vE$k_$0x zJ|C44^L&G|01eU)3I+&4%BgX1& zqkzP|0C#{7!5vKE>QDBsdvQ`t-@+NKYXY3&>Q8|1$**(ZVrJtQ*kTWZ;IU&l`wSWr z(b%>uzZTg#)CTZdI13^JI6D>t5{>Bv(ks%x?p)P(f!9-55t%mmR-n4`&eRVu2E)m7 zAT_WJ-wUDPIwsNo*z%c2>gr~j#A21M|FM@I`*8m!=YVZE_072v8@6qI9gPp*G(~Sm zW0+g^QOnMmn8?bGn{;9T8YO5y`sC@&f;#oSwun&~jm-1XDn=n_1@X8fcJ>&! zM!|^mZ%wvS+X^6CXrN0j1ZusFuGa|#MukeMUIO!ZO6Cl=6(fbvZ4Qqlj2?3zacX;q z6Md8;aWsu|$WwJCa_VBAL=kKCm|Ih7p}b8J983BjMi(rp%TIeuCNpP`u~j=InYkA4 zO-`vz*5zcAB+~S!Qw!2^Q6~H!qwpA`HL?X3tCU>EO@<@wz=%yUnaMZ@Q3}r**j)z9 z0S`}ZM<A*)YFa zqt=R`k~$6M{PY^29lX~KQdC(*84innE_Jg1$dP_5!qiNgRs%cL0j;PCg(fwre4Nq9 z`BY7l^4CKlm8fOmQ^0st&y9aQ0O1=;AY6ilQYPzjQcyM|LB)`6=9c|T?ooy$cQz-y zc{qU!@odmYvc*0LDS??JQ^e8>lc)|9D3{)XRL&7qSHhq*vmVa{3GC(o1HhHVvrS!u z&YzPa?|eXZVPLnDR*&X`zN}nHcxwz)3AKp$ZAqHC>{rFfm}pAJ`DG^JxwM9(#1;@U z;po3C&IZ<+Nun5ebD2LJYab!11B8R3U0hR(%T=><^1%4D`wr||JHAs@s!C|z*Cx=i zGqIwwv5BcFD5%u7hD<%ZJ*H5rwz8n0ifL-BT(RJWr+)g>4GU;ul@8UQySb*+PTW4d zvU2+Ni5E^+SEz5j;f7n$V)})*udkl6v8FKUcR2jDMOIs=rlPjCq9$as7S-Z?(ZZUI zQ>xeBzVz7owzl=h$oMbg{if`s|q06`+|laVe#AF2iVuR`ZxcE~tJu@s>@187Oi?pfH%3~nLeQHqdU zTv1q`(U3= z0DZ&ux?;oSAD@= zFkx@Os>80jo;uf*{wZWRz7YUMrReN$@T;X{I>hCV#J#`c(gO!B?c8~I<3fFH=ZmIg z%{}YZ^)xRtz1ULR-(TDkKfG!|Q5pWY%Ze6Y{EggJ=N6But+=*K)Gyq4cqje)bg)Y{ zhh1)qsX0k6hSVRUiE;TbsY;p-mAJ&n7lGcTD=OzH5PO;Y_HatFSw2D}iJELmM_0WJ zaedD_0XwHMHhFPMfV=o4P@F7w<8^P7QN`H<@7#lT)pw!Rq2+*#c*_#AwE5_J?;YK1 z`u#xy(c$zVDNc|sCYH@Z0^0C7A?7kW_c}IM~;r4Gd1p9>2R_<7*EUd9`bfc1%X@c=%|yHkKlvl66<>6@t$wL z;Hkr_PEo54^YQnN#`iA5sGHdEa+Dr7uue*(lIYQl67?e&ZX-B|*~4-e?Uhu!ECKM@ z3|qMyk#1s<@mq$kv)MDf`Mj`Q^@Nb1zAGQ10cZ74WIq}jPVU8_hio#HK%c_USGeQT zYV>hH8Md~M1SbxRT>qAEc|bH`)2_WI19FZoo8i(cp{ml@yu%#1k&%ww?9A@QEUrN? zMtlM$Qc4lOOa_T2vp$68Tr$7oh|H}jjr40x5uVjg$r;269HUTISOWU8uCOn&YpFvt zg{OHbQKSL&8kN*Pl*o%uc!5mpraa92(SEZ>sGm`PGtG)!IgD^Bw|+Wroj$|<)BhLGhiBM7 zyv!hRDuL@pfU~H4=J~;FP5(K%;(7a0{~TlIKmQM&DE;%SCHwA13`jaC3uJkr&)A}P zmT%@M>QB^H|M$O=|4A>+4pn*mwE$!|4!n`!kyXtgY#xoNA9iOolK&&U`}_93(^#`b zBb$sD3^IrE%9BXnFVi}+5KnYe z_Csf2 zV}<-LHLBEc84TPt>OOcChOj#)~X?ZxcahJn+Xc+XZU}Fz!PCkY1%zy1>AoE9p|$5;g@|4uS!f5^HvGSA&U0700
        V$fDV|Iw z-#ZH8@kAo&8X6qN(~8+vauls2VmxK&6M~O83OR_xEJ{?4GZ$vqTJvKqld>-g({5yZ zQg}d+aKr=sA0y&0N0jUP@W+l-E-5LOEh#@sE>(PF$z%fAxLms77r=&*IN+7kRQjJx z7)f!ZSVPr=oSQMt$IFbh6K+)1sO%~!q*8%5&`OO;C2axw!GSS%A17;M5BiZ$*&=OG zjlEmuazo|%&rG?fTpW)wL%EL1HO5Xj3qM@G?|$?Ia#QdID%V)M;Z(V-WNSazpDuAo zHTG^?uBp_uOqiK9ti6udyQbH z7slF&%5}!-jR)gpd5^eM8FuGfZ$cd@efF?^Lw`DUW0CO< z^$j>Hd(ZFP3C{Gk$vvk6Efc0^$@ly>ULd&WOz#BWvl88NW3HUvv+?Q5Gc;$~uPn=r zRWhFHXdVQUGplXawtz_97=lfQ!*~!=X3>XZ6lF>zFbX>YGXRsEBW)b6aADX4IvG0s5>sZmuo|SX_=VFgY zV_N(u-2z%#Zmb-B-g06b7?drNJw-C{joCo5W2p0LD$Jl_=S=P&;L@j0r`WK(^o0Q(Z3C5IKRtzxnfznlS04*>PKd z>}{z%K={em^tQxucw7^D?Ay>{)pXE~wjeP=5t?Q8z zJ?pT`p3G+PRfp?J27A`gi8CC4alCt74@_cLKbiUtuR_AFeEJyssWHo~gL!HWlJ&?u zollK)_7iAoRKeEufCMi084fVXRD5KK0V(kr_EUKnv`I=y8L5J-C%uhWn$t$pYh7_C+bU;?Rl}hhR*GXFEt3B#)5( zI<$56?5(qlZAhas}%!{evS#;{97qv0-Eui-TYy^&?TElbwldixSgj4M$h z))~UC;YHID_Z_%umAmCCM|jOW zt8cvfroAigSsiv<1^RntcXrMm{<-ADmk&V zWm(&{*FHTubN;5~(`S2KGp8-zG;hYh@bAcq-$Htv!(Yi+M_ZYJ38~(xc+P!{iD^fX zG7Um4Gl;XlK&=eOhgz6``+}(79T{0Lq^PnvHmCe@5s$ak z!hIDvl`L6km;NY3n0U#e0uT^RU5#y{G7cjyG@vRDvh^Y959NnCP9?MDMw(nQdY(lO z&-a!WOE=pL-il(d+VaFet}4esV`TgfTN;+Ydf_?YzD^QH9u}La9 z7DndQ0+W{?`&1hG^w@H=1k9($J{U>n{_>?a-E=9s0lH1k(xp9io1qH4nn%u+lJI5A zbGJdm^N8{8(0tBLH?11J8i!l&grw2-qYI=-Jp zgc%W^kp~N ziT?%F2@MCR93o!O(W+_qW?c5UGb{)RpTQsdsj(kgSKrtF9SVzwIBJVf# z#i(7<7#ryYkQeFy(f~QnfOBgx1=|pL5RHFj5jvi>%~_~2YA%+}GO<0pk>nZ>+ygMe z1(^2qWitP8peU0?#)y%y)l4=V8r%~P?4Q}X?Ec>4AAEH(cEQqEtgxbf>#2*pMZ^hK z-GKuht5K;_cj<$>2QZ-zBD#qr}X9&8x&Y(lUL_<7S3-_Dnvj0z-uy>HwRi` z;yMj$5KK6)DN}bA_24q9hMGWaz~3Rqo1-H6MeD%`8Y-2jIn1O|Rx_#>I*96Ow*3EU z7CL_7#g`v{=*_q3kN$qMNo4D^HDbtK;jOS(?c(wit3^{;_15DL?5}j+bn2o1QCmS< z(s1E3ec;jO6_-4_R;qh?Q{^D1qzgG4FLG*zq5s?vQF14Zkbice;<+;L+5fB|u`LP7 zCB$Cf!+Bw&>;)FnNEa;Z9?O8BVk!mQ5b=)Ec+@H#+iD_J=4BP)K3sYFMt&CaDS3W9 zl8pFK<}`~*iDq<6n1(?DF!c49#e^%zvaYG%c&Oq)?3(P@AR0f*a-ILVBjfJ9k> z&LfN4MWsP$qbPD(PkE$}Q zgaZjPAVo0&5|Y40)(M!q0g&!!cOGp7ElnEmm2~r5)?zhUrB z#C+q}A(=C#2oQspoH&&k=gfHQLt-%-N$&tIqNU3J;nT9pT3Z1JJNG4KRn#Jtw6-F> zh%Sq@O(_c+$)=55!aPkD6UlF1?Sca7ypWzI=0>EC_5EEdiwd)N@_EbMAC0LZECcbta4B*30Mi_35;wu$smZ4!_cUJqxWN& zdGJRPn1N=yj zna!UAqhqGy#==7BGr?;HJ+o7{d@g;S1`7fL+9y4l#sdP=%<#Ir+oZmfZw+oaO{s0! z2Lk13iu46Q7U8^P<3V!%z*Y}PcMt(q3aj>f*SQtx0QP*Y6Xq<9xbaF0ONY@-aQl8G8fq3#At70 zlfz=2U0^Ksi*yHgGSUuv9X@EGNz+Ik6W~OVE!q%TF@mAtEj7 z)ImCs&QZ_5y|WMm@n#Sd0zdY~`hjZ@AH+Wlmm(+91n>=yS`;g>t0@o04e^`37`?!Y zA(7mXut<9&ZUX2Kj?Q%hOy&&*WwslVYZH#pmw$8Arl4u1N`Jc~C7yp~ zKQLVl&1es;D7XfI9Z$amKTb(BQ#EZ#XL>iP(}eF+C-%&BqQ7UIK1oRoJ-kjmYc9TO{L*EUm~&L=53e{X!RQ*b zuk2{(4EB)v0Hkm2VrBe1%8%pDE!gxzdO(28UD!IB06i&6dX)Q0uPzu$1R7FQpw)oZ zX|ztGb%GnnL_CuVhp38D4_Y#4DcktoA>(JijQK^-z%f3q*~9CgjAot9r6%;_^4wVk zJV8&yh%rB~aElYNGYQy)G6@sNn6bqWV~5DZKu9TAFuk<9veSRD3s}^iUHzfv+1^s` zni;b%ar&Jhf6wB>O21MIAcVz!`taf&e+ccrWKPc-bk^+V_=i=1Wr59GQE92K?kS(S z5Ii{pAKD%~5@eC6p^DV|J1e_Or!QDIv%IIe-cniNwLu0#02pe-rRkE?N1P*`mX^hs z1mUv_lkbn>%~{fQ5;Pv5@YhJJ>y#_Kj%NWEnFU-HCL#Ud4+K^*ZDRn`AEZBElK}yZ zL@TGMlhQXQam*|oPrNHVW7{hSNA9(Ou6N}jLdK&cs6WdkYVXODdm;YC5wS>?*+^nk zJMe6dZkR2O63CJ7JZkj3LXN6Hkk7|(u$cTn26YGe3vpTnvr@X{s_m3i=t?`j z1zw^%;2K_%jcu0slRR=P1NtsSqe;gS(#tHiIun=TTYCSV>{z;g)6R%NQ>ZaSc5d3g zv_lSRfpM5Pb$#okr|Cyi)Z7R5Y@gX}=Q)nIchB6u=YhHMK$y!rPvc#9@px!;8{Pg9 z5e}obM`Zb=g}dw;YEd+qe1|^29Aphm<<>D_$9IHrG11$OS@h%u+JhvvBybT>5F*p% ztxr2e+)yme{vqsn^6wPVZZwf|2a&8dB^ML!Ps3FDLpVK2=Ag=yI~KvY_36(V=aOZE zn%(H2pTOThIU1b)kw&3mXeqANou<~_AWwEXmbx0(bv2t9V~Ig)HELL~u5D#qLGRvP z9SG^vAW1XmDpr2yeNxh(MkGS&MRpCBKNj_22h#u%PJ!)~$7XCW zL7kM~l^S(i%g&Mhm-GqE>6CG!W>94S+xmJ=g4ux8nHX701&ME^n;-A#lddqR1{o!O zX(muG2PosB2_$sTv|+|it`oETM6b&_2B6(yG>AG2TDs96?Iw8L-0Sy9k3FU>bksfY zlJwY1(tqLKTbZE?f85wq22Z6}I$q~;4|UPc;6Kncqr3ZO!((0WfJ6CX(ORTcWw7@- zl0lO1-l4BuE{f92AS{Z@u@=`Lir`mbExdAsCG%Q*6ok=vwIaTvK|UG2eMY=^`T6M4 z!8E|WRhb5}&woCA89h$E9l9+DOD~gx&=W>JAD0RjO)lok=sbMIxtO z8^lSzhmrKK80uLVV#h18;fP;!2Z5Vr{md%E&^1+XndSNCw2xT8Dh8~mNp06lb!;M$ z`f2JH^sz@$AHN@oTqAwF3@nAN6X31ymfU?e>A#xOaqhpfe$)QO>AJE37ndUhPM}`uYejXyYa5Oz${SuvvgY-c$tG_PTsdF zk3&^}L#-4Xg{$iX);v`?Pw6y=GoEZ?3y5XFcj=@&DlIoD7_I93Ez)|aR$9O1e5H<2 zn9zvXXHh8h%R0WgSr)DvCLDhA@Pr0=^PJOM{MPT1`EA=#0-)U;#aGJ|Lmk1&Qnl zI)e{3N<(DN6)&BrD69u#`x036I!_L$)Sx&&`cclp_k0K@YJmwI7l8Vm+q6cL z_BK%b(T|t2K&2vk`PZd;UeXFGCH?Zqn8=*p&M|_~gAC<_Y>4O*qgWpv!(mj#ZkNko zFzQD!0i%VyvxYFj>-k${Qy z%W5$pMWHG6ob()630I*38FQ(m4x@2nDj|CO!)o9AYrjc2^X2mkQ|JjLE+veX6!ZTa6wFkXmk?^G3vr0Uda-lLrS8X zN=dsBJyJ^Q)B{?jlBGo5&|Q;U61p!)6bJk;p-$>d;&55OmnRE=U``eo^%)+A%hR)a z<$tEd0W1?O&wq=b!sTgM0G%VBe49vLng2d><35K*c60ijT6r9JP9PCT`zdK7NRu<^ zN5{e4bfmVf54@o>O79xAIwSBJrBl!)4W|2DcI8s=+sP9bQeF2W4O~+R9Tycg0DF$Q%!kCfSE&_L-`dDrV zXgMf2G}_>ZZr=xx5)mvd!sn5eL+6RC5tikbBv%eU&Tm#`2Av|{(Xq0LA{GroOl~Z1 zjVurSDdzmM5D38z_8|e9G#Cwfk(gXTzmi`jB7f5VL}ltjBa+p^>4A>-dZ=Jlqz=Tgt5J%u zcq5^kxJX$H+#w6$sGyuxUd4uHf(ym8Vh1DrnwQq7Sw<_`9OwmzA4_+)F2)Vi4(SeD zs3jfXg2CmB)Jl#nr!88B(VGe!#k!p@)POe)N)>Hm9g>Zv!Haq%A=sdxmUfJLahKpL zE;Jh$R;$(g?Wo3#X=gZ=Wf=(AcSY@btyn)!&~4BOZve`Qp07QMU9x~?Xc{KgX*9YG zc7LZvqhF`iZ{ANc=t2Nlo=@xJ^bl%~)?DQ5a7(_7%z~YNI7JKdhmjB*cLp5Un6c#0 zL#W9+b%Ln9U@@-g;;(=9%weP=tWavTDz>bza!x;}Cdp#2f*%OFyU~lhUb+FFc^GxE zU7~i6PWa2QKkrZ!sCKCVRI-J>-YIVjx;9x-RPaQWMpt1;4NvU;~*8x z1_;Np0!$zyhlkx6Ezx4d-kIHk?tbf=58elSI+eowOM_B+1>*s z4Y+7D`TjntG9E+PVA*n=aPSG!W72H~LC}D;FDbRVwBp>Ef({*6FKVyA=c3i-Spoqf zM4|@aS*P6IG%-OMS|r=uWRar=BSs_jRV3?ZTn%TsnK{?tOdMSJ5b6{p4-vTJH`rMy^M_!_;fJuUGg;ty+==!xHY&RGTf;2BM z&o;!d`k?Lyr{h|ehz z_>>fs21z>wXtcc;^$gJ~T1?j3s2Fow-Ql1Y??6hByhGLzY0_h8FD)}+)7jGI#zQ*u zUfklarG=-n1_vJd=i!W_lK}vmywW=^aM#t|3E=3oyJw(1Yu(b@1dsf!dwAPX8~>x% z??X$q5e~eD>+^{FI=r}O0jp9O_S@O>z={ia+fEz51YC4JYu|5Bsn~^U@hLZW9!F!w z98iwbX9hEtJ(Nf!Qb?7S-a;E_*YQNcg?ee~h|LE3(XUPg`-!YATb99my;ftBj(~of z{HxLGrTfz-VEwl4G{t;~+A&N`Bsf79Oyr_tc(XU+37Wk|5BiK^ND4BB170HzO0?F* zB4KkhjDDOnT^nLN1UR&&g~J&>l-(vw6kjM_Tca>= zD(#fDZ^qrX%`CZX`epsiuRANcn&#I`S11|+oz-ojYNyy$;A^VsE^p)6Mo)W1W56fS zi6^HN9=^J3&4elobNUn*qE3US!r%}9#hv#6F!VM2YKSjxydZU_ug+JX;h^*|pjnN< z?g@c!++nv>#Q`9_jHU;L&RQJG^CKALoXBAr(r9w_yD?%D5;wEp4VdGjNTO%ffVvu* z8XC-CGhno)1W4&?q!(&rSuKk>QH{Twb7GmF>Dgz7nE+##Y9Om-0bOqO;xiN#mDO{a z;&yNtjonAJQ!`OJgfWGYmq(KfkTH=mYLPsd5N(OYgj~^9fTN@x`7mCJVUfA-#}hS}vX4o9p^|=%qaLIrwy-5hTnY|h=}bKh)@ziQ+)X2VxE02v z>p8tzr!;@_hBP?2>Yr7UrS~R$aQ6pH{~xOij0t!&r<@r;CWB~V`*2;q8xXGe=sai? zlu8=V8~?T-^_fCYLkPFfm#i7e|-~(vx$AJ`>H-&AV-&oty-B~js^@B51`ZIf7&*t$h zA)64?8~lOU7aE{>M#ZWt4_>tG9;Z}(AAr0RSd4?PR3Hf#Wo@;26>(FzT7pGj??M%6t=BAat{Kl?a0qI%-ln&W%a z{k8o1{qigg!K5pH>cO#UKQywMYZJ) z{myNza7}5hYp(aN8$SgWJM85E`0eoW0zZTs;`7`>lfNuj(PR?M#Wf{OPFr9~g@?15 zbQ`EFzk8hIi#gJmh}oAnQZx5k%tXtDRvg?ypoK9>F_h_+(@lcgqmjm3Z{&|Rov9&K z#=!b%(%%_{jur$HQ0m=P-66YZDpd1IrCo4$R`=Tqd;z<6+thh?v>T`Ru821%gLsJ`V zocWO;i2g-b^p|$dh0|tvBb$!>L8oA`5L*w-rVN`68W2f9YZ368P3Y{}Xf5Vm!U-2O zpq9|*xm^S)Gz~=QBK-`B?R?NnfGN#kOvp-Nu#m(g8{{yEhA~|ZZ@L_#40E>>84U(w z(bMhispoqpO#?sf2>RVht{niK$pTt=O{v%2(c$uyYWP!-);J=yMP^gca)mhWtE5k)Pp_(IQ<+Svw(|Wju)iFwr?lry4o9XbT)bC33AoKg)nSL(>V|1KZj| zwdS%?ANcgHk}~s?$|9XbC@s|Y=AakkpAQs9F;&Z z+%}884m4i=4ULz%{;`l+O6{QbQ@2x(5d9k?2BLS(BB7_Y#vjJmw#Kk~jMtKRc@fk* zBIM=yBVN*Bnn8Hfi;ZC>9uL~AAxynI=OSGM!*`=z;UYZ*glTkl3}hS@Gks6)XSnbA z$LOK-i$SZ!Vhw_s=bbmyuv&UyO<31zI~=Z+r@VK-P!s%P(D~tMV7F z>H<#|`p0(!3JU`rR}`@R@XFnVEKh zHPWTkHh**P^WFBk=pRxm$HiifS=zA5H-6rV>HcuoKm9mbL>vw!{fjrokAGuAYTn12 z8hbdind@m>_ZeR2O(q_#GdgL#^beq)bYR77>Dvj9%s^KMdLHS)H<>AEV=aDL7#xsp za6?Nu*dfP8Vt(I$Q6kRV2b`=K$HbaoMiIu=UUSCS0-^x#gmYA1I|84ZO{x?CcWKm0 z>*pnQ`nPIz>I=}LR;etXm)WG_0t5xYe^}@X1!+>qgE<7yE7a>N!7_t+=sb|R)nwFH z!i!z>b(J|j1Uxp0gtrbOj$%6w_6(S5&WfX}Vu0)c7C^S5L4d??>nNwnPIK|of`V7< zcuuKQ7@jE>=@@VPiBps=L~69j^|Zh%l+qBmRq>}`#%CJ5>rrcrzX#HfbULk%o}uxk zf>3gMk>U*A0q{Q!SB=J-p=6wKf)havcUuCVNhbM}`!eR-0J+|b!BL$ORqS!Q4SJIf zQqT$Ydc&%&KM(EvbJuEvP7l-D^zQWb!bwIDHwi)@l?Vt56^I{BuDQ3Zdzqr3K(Va5 z?cO!RHz^s1ic7Kwh~E>lEf=Ftn=u1(kdGjJ9{rD*l^Uc>e^8LdRP+ZX6aSwub@?We~t7f!u{@F(+3JMGn@22^Ly#9 z(rZ8`eJTAz`Z*|~cS=8(z69e49zDhGB=L0mY-zkWBA1N-BX4#GFL1k*Dc_R5SeqICYa3TuKiN{T?Q@sn(hBSTHr`xA20gsiWWoxNf_&9=2b4^QHT4 z0k?pKsSYnH&tU2>Ts6P#a2t5zsY6eJ&!r=~K|gpo_0$|V@uO6i9X^xiV=<>O;wUtd z;Gk7Z7mmgsZ(1&(vXWyiJyVYPi;a|~X6`d3-r4=U^r7imubrtZ@Ja8VNbEXsVpjsZ zUQ+aMQ3?5Zc+-qi2WD*AG=sTh#-@wmRjr*n-`WoJ$<E!4^`mQNHl>%(kp}T@zm4-P(4-- zZx4Gp`$HtB;|#4h_`zR1> z1xSo=0#4)zHh~}QX7CZr3la0NI97tLQf!U{iwXn2?$}!0ua>k0Rm5@=#oGE{Zk1|4wUU(OiXITj87g>hmi?T{GjR0v9Lz1;z%=oZ*Ch4qH*~9+GbR z=8)d3WqGLdn(a!u$W!NY?l=jyfzsQX3;^ESI>lw2InyX;8jY(rR1{u1eqlnPI07$o zc$JE(YF_2B7kZU^QK3TN9TMypc66J@RnbO;$rJJRJ!eqfbQ9;Pqo2M{vN>xDjXML5 zb(*45N3F8vg>4T_v{yQvdUZ(f&kId4wGjSK`CTcFgqI zA1u{kp&m)PVr?`KL<5x`5Dr7!uu;qzz;e9Y)=nDjXRr<+j1stdX8OuOd2se5#r(ai zXc()UaQ%~}j$p;@4^#v?%-WF0`KveFzM48UtG`R?zgxrF^;LI%`?$xc-={Q|ulv39 zkG;Kt@-U;Y_&A{81ntVl0e!+&T+ECECBwX5x0Q!1rj>#<+T4DzW>H7=d{gmE&|tQ6 ztjWaj1t!tPBY~ae3sN*6EMQix;xxC_&2WU4ifyaluOpV2yVarb=uP9Co!9)<$JUxW z>K;?!Laixa25L|nj^7FsDlJo*;?X>ewb2_PoMYh1KcVUTCY?4|)3JHu z@+njMR?e8#)L^zexG)|M2HAwP{U6dLSNZ(b;wfK_Gm4Ians79_8an>qjK-!;8w114 zA4xwYLRhN2GGC-QY&7MlHAndpm(HIX_7|ztK#)GWM_p7@J+5uP-aH{!m&ot-Q?VH<@%=h8@)=^yxTEp{|AzZY*P~(C{mR zR=QiI)v2UAwF;#vjje~2B!iStsX)RYiVU&+pUT8$P%yMo-yJN~GNO2j1VS@|0RuocmlB3FuM?noicXPxW)R>r`0rL3c!H;J2}TqO4i10D z5*?{QnrDjUlIeTO{@vlo@t9F2iHk6zRB#V!iXZ3{`Bgv-l#Od&kJ>XpG6vJ#3Jb?x z4-F$}=@!3dqG8G0p&-M#Dih#YO%`^2aQ5Yi>VE5;j(tAbD)@anKF>GXKoeDRKO@A~b( zVlHc*Jh?S0sJWZhtS+SuG^5GqW24cWu9n%7{YJuMlwQIIQ*-ejml)cNL!_XP+T05( z;r~iq1S6>}L!a${H`5mneE{zyypjZ?mEB2V77LN&Hx=m|6jc)?^A?j{vhwUEcXAo_ zkt8EFWA&0K^FiWk!%2!bN*zap7UOULoMg?DFC_he)L6i~F00jL0ViD+i_1E6s;sGT zZc`I8JzhDvX>QYjrt-2TFewy=53f!PElsTH;x$@+;^H?KPvo^49vsHUo65?Ym?A5_ zkNp4DrZQ<}c~et4c(|-dOf3(^|BAQ%D*whq@HTLB?D@@`pO5X)@|`8nwl@gl|Gmc>oVgzz3>97x5A!kUEZbb5@f#gt{>%tmiQQ4<5yMl1OB& zv2Y~ulT5udo)c(1RREda1I-=*d8Re zka~h1X~8$Bi2^6Yg#iTAgeI^*yp9ga4T0~En}7)75mG>OHz&=T@I7$>v6YM1z5@6l zv3j9e$K+WvOkiO6^tl%N5SrW;wGeL9^o`T)>}26BY9+&p>>@_5vMFfkc7|bTn&&yj z$N&fdr02vKB;F!1R|!;;yf*hdw>ns?2Wq8R&}xCsQ($2jlRBtx)8$^!yC(Q&3Bg-mO5ExXn0>5r3 z-6q)d1r9@z%EOnl<1RLtTJPRe0-4IoLcykDK?7Q5I(-&%n@2%A0jQ}3bbEoQ=b1R` zEHNu-#ZJAFX88Jc0P2hN6~&NND?yQHae^`*qt|JyKxbzaR=pZPBhV;~N*#wvLUYB8 z$RMedVf0o2GzL+xWR#F)8IIP{i^XWt3XC|(Vc-R2 zkp*>Q^pXl)1pqW@QMc9@)z*1x!#KZBsbN%t$J6aLv9wlS#@RF$wZ2nlRB{Ch&ZVQd zirTiI@u#(uJW89vQiK`4mq$BI*VnH5)p^^>&7jCpcC>Txmh~$eUz=CmRRW>Mj~ZPe zYKmCDZgyo@bFO<&+TY~5d%Sd6&XufK#h~JMu$b=mo0(N z5WQ*VRbKtmAMb58yQJSphr#@wni~&n3-}pf#n$Zyk}eRU-+ANL^Ges=H1rQNp~LCV zd^2VGo{i%#>uS=!PagtGQ^({T;|oNnqcq-nzH#%UeEgD*pU~$$z6S0^o*w#0THBkB>H)CC`VC0Zl=? zzPm6|##vGKqLIeH!WYKEEljsx3)PEtk`P@5Fmr9VhLE}DJ=$sZ=R6dW_%Vc zP$ry0e?Cmm7L(2Q7`2VD2pF@CxjEP{e`eoHg*O^$`5tuZ$ z>Ckx=S5I4bMs-7}h=u*z3Ee z_V1QAq*Hh!+Xf7g?VDtblng?NRf(sv477ly7=%e6tO?D##7$L=m4GxxNije_?2D-r zwYNl4Cn6CzIdV7xl+uQiW%Z4vTg%G8VW*!fYzo5FFtU5APL~Q8O$-z?(n_7~Qf-B9 z2)5|UAeFrq{Y0d%rS&JvN-r&GY$(HwhfFD4O-ByH=B@fNeJY>_Py>$W%XC}y`XSh= zA7+0b@y7m95sv4;|HOV@A|r#rv_~|%H4w0WM_e8(`b{##pE^Vlf^tYarNm!K>vAUr zvb=vR#SRjLM%l{~q`hX*LgIghk&@KL#E6$pGn0{=Y1HhQTp1kv5ia^`<=4u9J=q=_ z2(>5e0p-_~e=Q1^)ENNPy#gdwbOXvD_3inOJ$wEG43^ZDgE@Pp3-y9MAbo+Ufq@}l z7xduvz0$Grx{@LrNUUBhC2VvbzF?1BRtA^VPa;^;!malVOS#RmSY}jRPhGryQ9JoV z>+5=8qGz2nNJ>M;C7BbhZ)hDU$!pR$yrd6G1P>1k^sHM4Ue1*xWB+pFxb+rnBFHef zK_o_5tiF6h4-0w?#-gf{xy?3TQ=`w;JhwDdWHd1IM+_<-gFjd%^%dKZgi=yc=mGZP zzDbtr#uyhWkUsGydm8nlZfrv(;077MG2^fQhq#^;h~I!GLf~ScJP>ZJFbeLu3lDvF()I- zf_LFMJ;3#`NvfTiNHW;Uk;02dLfj2>40cI+La-`BGuR5!gb0nm7{uR4F+tNwgXsV_ zPQd5-0`|d<*F;f>3cq4a@%AO-65$KG8+H1pOocX4q>aCAkYO>7i-B74I6dXKSQ`+J z589;(sl-o!>L>8L+Q6|buZy*!C_c{`N?mpgq~-_)wYpc$1|eel>xKbbv4DJ`d>iSH zkhC+V8cQ9Sll_b`VlXW+1xELY{03zj%)TuH4%acFNf!fR9Eet_jASxE_D@czq5#$tXtpnJuhjbAngFvev=`H*Y>v3D@G>x&? z7{_wLwKYf)QIrKvQ?|Its0Td52;Pldhu5EPD^PjY^k3V=(Tu(f2pS8^ z8Wg5ly`d;tUQ(!qoS;;(P{(rxOAnO4~YYHdV=W z1Ax2MU|~5C$(RhSHrK2!ENYrxUC083uc5!Yq+P4=D4|7E+ab`f#$tCv?Sg>1#Zy(R zgp9p>VN3s|Dm_gD^dGW%rOb`{Aon#pnNpEauZo&Ot)zCLFEXnKV;)?xij+=k1|JhO zt3L#MNPoj0V=U_PBV8Abj5seS3<6Qlt)qe!Qe6-htYM|K6V zLMyA~@Q2vFI?ZemI%jNBD7CsG-ssdhPgMTb+SN0vs$O5Ub}`Zn2c*-7{v!QJryKy_ z&|iQb1STE)xs;MVkpBCv-B%|b01GCyRWh7T&v94(E>u|wS)EE#zo>K5>;h3yZbbz% z&2P1pF|6Iz1m?^O2bDEZyQ0w7((=%}!f~47!fjs;c_!#}cDHA|%W=Eb!Ln*?v5r;u zF7NYso>_eUB1h4QroNjd=&YX}k{8!?UcaZmrDMxeYc>KV@xYan;y36ts2jk>=GKi` zof`G1hLvz}@3uPhbX11cJ}r8>t(4VH?@MiT*o7L$%qKd>M+C08u8Oly&i4mypp=w| z`OyiVE7GqqYrP5bn1t8|3_KbvjTS~=E;{!7bH@(+(&PQ5bbIQh6ZZih6FKox>T%$^ z&(qsG@0)`MzhRpt$B=Zv(zk)_Ct&>VQf1PIZ!ZN$hrr*QzmtBF#zv;t%Q%W!jqNQo z7Ew8hCkPp6Jk~+%N&x8disE$^ud~G<8VRvT+h=r0wLwD^wuk8Or_AA1_A=M}-u|V% z)0+&&_0rMTM7v!)4$7DNCic!>GIy4H!wdU1v=&6{yrrvi@yxmLN^ZigC3Bm@ZVSt3 z6ppUCT3sOAeNmH-wT81z?%A^GI`HG3P0cP^ z=PXdE-j}`w_CNu6>!eOlXe%b|oKk&{Z=6vt4W&Mxv61=Rsj|%9#u@aq85@D4ea;r? zpFq21PCJ-znmP?8qMvIzI%aR#k|%2xAZe*Oom(>|ZKvf7iBU`{?21(OO_hu$4-}ZIQwWm`KWNlvSN--T)-UlC}!>)IBQ`C(?tZWmW%rI&hs8UO&zEcs`QL%~TX;Q4*01OJp%Co?WRh7EG;VG@@nDtr#KG z#NGwbZFb{KDUm+Cyg_>HCwE9+-~Rf8#>)-?{+XR`ZHA79)0EawV*FexvH9sfsL;)g zw)ggT`oVqDN(1;j z+C$-`c8%FQb>M0c27zH7D3Ilw=)@WxWMq{t8w}J6BKhl?R460@6(JdtHD^|gQ7V0q zNjxi^{Mmp`c$?-_O0D&y%u>*yonVXJZk4vA7bgKj_QK@Pq?6AII=HkQa4JK>s^~gD zyY?N{P)}@PO?d0l^D`?_ffks4ilcIK`Pbew>a#hW>LXVsJE&znYTq*_8;=@sOq@#; z={`9Rr0<*=+M~`VcRE|fHue7jDoYD$004N}V_;-pU|?ZjXo@RJkLS1f%D~Oe00QUc zW`)D(|Ns9pus5)QxEu^jAPN9Cg$rB&004N}V_;-pU}N}qmw|!3;Xe?tH!uK2kO5;K z0I6LEeE@jcg;cRl12GKsT`m_1IMIcLE)`;6XcwS}@qPfdj!1|PKuCyzP7zn5ugFYzITwTLGqsUul~03g?(GI z$Nvn^x|r_)-_XCSO{+dM*h6>eWewk3wb=*uYlgFXwsW!`?@s5i?!;@H#-=g%hhvaf z8cNdU8*<&++t|&1TT_KNm%!Jd-1eZCbC!&d^qr3*cWcXy&v~Etq88bC(d033+1s4k zf(LUyxoCJuH5v1^Qe*XLf9@+Jl5a~kl_C@U{B0r(8#HJ~G2{_N;1iZoDGhkn}5)14*olpEb$m@Oe z7GBPD_ElHqefpq!-0K*}=F8OX-u*y2YP`-7(W58n*+^Fm=(lJU<~;+Z+=HgCdLMW5 zkb9ry4R#FSQ|DRjPTOLhym^OUKNrb$n1#66*f$ln7kg%9oK@|$^7{vZ16004N} zV_;wqBLm7Y1TaiuxWeefSircBiGj(6S%tZY#e?M>%P&?N)@7`J*h1Kju&1&A;RxZF z#PNXBgL4JvKdvCI30$|hb+~8oxbRf)oZ>a(jp1Fw=fbywUyR>}f0;mpK$pNHK`p^m zLM}qvgeycWM5c&*5cLvWBIYM{K-@??O?;F1HwhJq0Eror0+M}_Kco_*CP-bAW|LNu z4wEjCULyTUMoPv@_Xd}DVQnbDXdUeY%)rH9jbWYPBcmLn2gX9iLB?lHq)hBg_LzJ# zwJ@Dy#$Xm^w#Hn^e3M0h#RJP4%TrcjR!LSHZ1>sm+2z6FPkDM8tU7XjsM7g|ko#s~LcE#PreUpcr$2w0p&qbaGJnwn_@sjfL@oMmz=e5UM z#5=}&osXB#312PWeZD{ZGW_27yZN68kO;^M*ca#$xGC^mkWo-p(1~E9kTYQ%VUxms zh5Lk8gdd3zh=_?;5%DF`Au=m+O60!C7f}XLby0hwS)$FNCq=)D35zL-*%50NTM_#R z1mgnY_QlJ@*Ciw*+)HdqJd~uB)RS~8nI$tRB z7FGSJ_Nks!eXqum8x&?Ko>b}&=)tA-JYfx$W)I6z0q@}9mNUKz9 zTshx$_qHC1o+?ZT0KC^I-vD^pV_;-p zV4TJz$soc20!%>62!sp_4q!e502Y`53;=lAb&$_a!axwlzZLvLjGhef*cju%1Gd!@ zH$+hr1cC&;7NpWBf6`VIAHxUm;K2v+q&JT~fzRRB=~lpKHoNnincZ(@2fzxRk%CHR z0NC6yD`e@#Jcm^rYffPUP0eX+;a>ARHu0o+fp1?mFH-$e^Agt8gXRp@)T8EQY^xW| zZ^)_-&F?VP7tU~kG7MBPL57)Yn*%w!k}1*~V$6)kx?TBq^rlTps=BoP)EoC_LLuW0E*b4fzt@a8jE17u;y)%T zecDh@G~gdfq8h2pc78yGk<>XN^{GCVzC!ky#|~Fg-MaGnVFenLC;7x zl3FKNGE=}D$8ngMnVFd!W@d1h6Q{bRS$N65-R`PVLv{79U%e$N>7U1!OIMZt&kr6^ zO^HfnQ0e~CJ*B%#_mv(*85LAfLmdq?(Lx&?bTNX_(!HgJN)KQRa)K7RTXuoPZOt1t;NToPtwv8cxRDFxN~h83bOxPCXVKYo4xLNq(fM=%T}T(v z#dHZ>N|({)bOl{WSJBmU4P8sukwMp!Nml7mvdJMqJ?fK79&M!o`4mt{k|NqhF(s5z zM)R~li?l?`bOYT;H_^>>3*Ab$(d~2x-AQ+q9pDX&!MZYEQCr``!Y2Ba7`&9eBnIzR9OFX-l2s5_bh6v|{FC$TPSx+lT zYQ`IwO9mlUeuSR3=A)9=w4=NS@wFh z#OsHqU$$kxn#N}0R$Li~2CpUz(@!g@7l=wMO{e3?h0td~nHxi;mPM+odZ8s3+mUZB z8MYVOzTiD0VW#z1^kR{?4dsen(3ke0((}!Jix1;Ot_(%enwNeS2!s7;7oysrS;$#b z+ZNl>5p~PdeK|Gz75+;qmXw2rY63GJRHN7n)0%AtA~q{M8K(T*cWPd0`kviR#bRo> z!t1+fOUnzMle#Vb)(;I|^wLf)+9FIv+|HF)4e#di)+|ZA-cm)KrR{|dkIUy3vK~9q zGi{-wX3TqzkoCy3(<~OXNQAcMw*oUVl&>PLnT}eJBg}pZ$4je;YsR8#yMiO6F07lR zA~Gz~9xRx#)9slY!lBj}3KbRfYGg797#K3D_hhW>9X))g=#>hkDz*wc?eISHvCL22 z9V+?=&B)IZLjj`|cwr&7a}a5{E(f~rZp#FRgy$)(>4iO+PfP4rh%j+w+AXH#sA%%U zTxwZnI26q|mJ8aCb}ni!8o8WB#dnPe9U_Gzb|>+ch0)7=zf;IbVEX=;ShRgJFjw5F z^t~R#PMAH;kytdu5(ABIqp1Yjmx<_bR6;N8>)}<7XDAxB>5I@Y<63NnjtuIy34FexmyaGrYDt?Dw$o!2ia6h_T`0yuq8tvOEw=70%|QQMjCRQ#T8&gnd8A`jYfvao2xB7Am6MwaASDZTE22E3l)d78Dg9? zD!@)TPLi_ga8fWDICx>j629NIRako**i^J!zQzLGT2yGOYblFziwekij!0t_ksH=o z^a7*nOj)#kl3Ip2Tw0>G5OdDE)znM|NsSqm57V?_PxNdv5iNz>JWs0qSY}a0#j?s6 z$())cOlF9(ouz!05l6+0G=99Ol9=_`BR2jUU%`~6cgC<`i`@`uwvLflQkM*VO^J!K%puNUW?E=nf zWM>F%T~V0hQ^sp5m|Gi+?U?W0WJYApYx&9vgJEGcm>2k-`(i|g*ceu@POj!it*cUM z1Wudhrmjpl_@a?yUaD@ap+Kc}tl3rWx?= zW@w9AAe@1hwtLDY-es#`*9F%BH>auIL{E%6GP4wvLKSh1zjc-zf9p()zjeAgS8H{C zd(Fhga7Jr&Xx$OXfXhbBHzU<)proBZTIyUn8#@KQHQrj=GMN@j=VE@(eA+PN!{lSD zT>br}RzU?En6b4KsA*^o4Jy4Q79*8~`R(!rM)|mE60jrH9;a4V4uo6pGuK6?(_os@ zxM--igc>=b1x+oCW~ae1=IUko74>3hYKM53Kf1zq1pzUchg>qS_?GN6UtFmV%(xniN5;)ipu6Y2Z&+ z>?E10F*cbpTRE#1AZBLb>bM=_-HQ@0SyPb4S8T(gRWYU}rkeWcr`E5rk^LQ6eL3iI zom0LxHhjTJuV9!98nO9z{fyAGu2aI8+Bn(DOTMlMoc5g7s ul li.current').removeClass('current'); + parent_li.toggleClass('current'); +} + +$(document).ready(function() { + // Shift nav in mobile when clicking the menu. + $(document).on('click', "[data-toggle='wy-nav-top']", function() { + $("[data-toggle='wy-nav-shift']").toggleClass("shift"); + $("[data-toggle='rst-versions']").toggleClass("shift"); + }); + // Nav menu link click operations + $(document).on('click', ".wy-menu-vertical .current ul li a", function() { + var target = $(this); + // Close menu when you click a link. + $("[data-toggle='wy-nav-shift']").removeClass("shift"); + $("[data-toggle='rst-versions']").toggleClass("shift"); + // Handle dynamic display of l3 and l4 nav lists + toggleCurrent(target); + if (typeof(window.SphinxRtdTheme) != 'undefined') { + window.SphinxRtdTheme.StickyNav.hashChange(); + } + }); + $(document).on('click', "[data-toggle='rst-current-version']", function() { + $("[data-toggle='rst-versions']").toggleClass("shift-up"); + }); + // Make tables responsive + $("table.docutils:not(.field-list)").wrap("
        "); + + // Add expand links to all parents of nested ul + $('.wy-menu-vertical ul').siblings('a').each(function () { + var link = $(this); + expand = $(''); + expand.on('click', function (ev) { + toggleCurrent(link); + ev.stopPropagation(); + return false; + }); + link.prepend(expand); + }); +}); + +// Sphinx theme state +window.SphinxRtdTheme = (function (jquery) { + var stickyNav = (function () { + var navBar, + win, + winScroll = false, + linkScroll = false, + winPosition = 0, + enable = function () { + init(); + reset(); + win.on('hashchange', reset); + + // Set scrolling + win.on('scroll', function () { + if (!linkScroll) { + winScroll = true; + } + }); + setInterval(function () { + if (winScroll) { + winScroll = false; + var newWinPosition = win.scrollTop(), + navPosition = navBar.scrollTop(), + newNavPosition = navPosition + (newWinPosition - winPosition); + navBar.scrollTop(newNavPosition); + winPosition = newWinPosition; + } + }, 25); + }, + init = function () { + navBar = jquery('nav.wy-nav-side:first'); + win = jquery(window); + }, + reset = function () { + // Get anchor from URL and open up nested nav + var anchor = encodeURI(window.location.hash); + if (anchor) { + try { + var link = $('.wy-menu-vertical') + .find('[href="' + anchor + '"]'); + $('.wy-menu-vertical li.toctree-l1 li.current') + .removeClass('current'); + link.closest('li.toctree-l2').addClass('current'); + link.closest('li.toctree-l3').addClass('current'); + link.closest('li.toctree-l4').addClass('current'); + } + catch (err) { + console.log("Error expanding nav for anchor", err); + } + } + }, + hashChange = function () { + linkScroll = true; + win.one('hashchange', function () { + linkScroll = false; + }); + }; + jquery(init); + return { + enable: enable, + hashChange: hashChange + }; + }()); + return { + StickyNav: stickyNav + }; +}($)); diff --git a/doc/_themes/sphinx_rtd_theme/theme.conf b/doc/_themes/sphinx_rtd_theme/theme.conf new file mode 100644 index 0000000..b71548b --- /dev/null +++ b/doc/_themes/sphinx_rtd_theme/theme.conf @@ -0,0 +1,9 @@ +[theme] +inherit = basic +stylesheet = css/theme.css + +[options] +typekit_id = hiw1hhg +analytics_id = +sticky_navigation = False +logo_only = diff --git a/doc/_themes/sphinx_rtd_theme/versions.html b/doc/_themes/sphinx_rtd_theme/versions.html new file mode 100644 index 0000000..8b3eb79 --- /dev/null +++ b/doc/_themes/sphinx_rtd_theme/versions.html @@ -0,0 +1,37 @@ +{% if READTHEDOCS %} +{# Add rst-badge after rst-versions for small badge style. #} +
        + + Read the Docs + v: {{ current_version }} + + +
        +
        +
        Versions
        + {% for slug, url in versions %} +
        {{ slug }}
        + {% endfor %} +
        +
        +
        Downloads
        + {% for type, url in downloads %} +
        {{ type }}
        + {% endfor %} +
        +
        +
        On Read the Docs
        +
        + Project Home +
        +
        + Builds +
        +
        +
        + Free document hosting provided by Read the Docs. + +
        +
        +{% endif %} + diff --git a/doc/apiref.rst b/doc/apiref.rst new file mode 100644 index 0000000..ab66564 --- /dev/null +++ b/doc/apiref.rst @@ -0,0 +1,104 @@ + +API Reference +============= + +.. toctree:: + :maxdepth: 1 + + macros + enums + types + nghttp2_check_header_name + nghttp2_check_header_value + nghttp2_hd_deflate_bound + nghttp2_hd_deflate_change_table_size + nghttp2_hd_deflate_del + nghttp2_hd_deflate_hd + nghttp2_hd_deflate_new + nghttp2_hd_deflate_new2 + nghttp2_hd_inflate_change_table_size + nghttp2_hd_inflate_del + nghttp2_hd_inflate_end_headers + nghttp2_hd_inflate_hd + nghttp2_hd_inflate_new + nghttp2_hd_inflate_new2 + nghttp2_is_fatal + nghttp2_nv_compare_name + nghttp2_option_del + nghttp2_option_new + nghttp2_option_set_no_auto_window_update + nghttp2_option_set_no_http_messaging + nghttp2_option_set_no_recv_client_magic + nghttp2_option_set_peer_max_concurrent_streams + nghttp2_pack_settings_payload + nghttp2_priority_spec_check_default + nghttp2_priority_spec_default_init + nghttp2_priority_spec_init + nghttp2_select_next_protocol + nghttp2_session_callbacks_del + nghttp2_session_callbacks_new + nghttp2_session_callbacks_set_before_frame_send_callback + nghttp2_session_callbacks_set_data_source_read_length_callback + nghttp2_session_callbacks_set_on_begin_frame_callback + nghttp2_session_callbacks_set_on_begin_headers_callback + nghttp2_session_callbacks_set_on_data_chunk_recv_callback + nghttp2_session_callbacks_set_on_frame_not_send_callback + nghttp2_session_callbacks_set_on_frame_recv_callback + nghttp2_session_callbacks_set_on_frame_send_callback + nghttp2_session_callbacks_set_on_header_callback + nghttp2_session_callbacks_set_on_invalid_frame_recv_callback + nghttp2_session_callbacks_set_on_stream_close_callback + nghttp2_session_callbacks_set_recv_callback + nghttp2_session_callbacks_set_select_padding_callback + nghttp2_session_callbacks_set_send_callback + nghttp2_session_callbacks_set_send_data_callback + nghttp2_session_client_new + nghttp2_session_client_new2 + nghttp2_session_client_new3 + nghttp2_session_consume + nghttp2_session_consume_connection + nghttp2_session_consume_stream + nghttp2_session_del + nghttp2_session_get_effective_local_window_size + nghttp2_session_get_effective_recv_data_length + nghttp2_session_get_last_proc_stream_id + nghttp2_session_get_next_stream_id + nghttp2_session_get_outbound_queue_size + nghttp2_session_get_remote_settings + nghttp2_session_get_remote_window_size + nghttp2_session_get_stream_effective_local_window_size + nghttp2_session_get_stream_effective_recv_data_length + nghttp2_session_get_stream_local_close + nghttp2_session_get_stream_remote_close + nghttp2_session_get_stream_remote_window_size + nghttp2_session_get_stream_user_data + nghttp2_session_mem_recv + nghttp2_session_mem_send + nghttp2_session_recv + nghttp2_session_resume_data + nghttp2_session_send + nghttp2_session_server_new + nghttp2_session_server_new2 + nghttp2_session_server_new3 + nghttp2_session_set_next_stream_id + nghttp2_session_set_stream_user_data + nghttp2_session_terminate_session + nghttp2_session_terminate_session2 + nghttp2_session_upgrade + nghttp2_session_want_read + nghttp2_session_want_write + nghttp2_strerror + nghttp2_submit_data + nghttp2_submit_goaway + nghttp2_submit_headers + nghttp2_submit_ping + nghttp2_submit_priority + nghttp2_submit_push_promise + nghttp2_submit_request + nghttp2_submit_response + nghttp2_submit_rst_stream + nghttp2_submit_settings + nghttp2_submit_shutdown_notice + nghttp2_submit_trailer + nghttp2_submit_window_update + nghttp2_version diff --git a/doc/asio_http2.h.rst.in b/doc/asio_http2.h.rst.in new file mode 100644 index 0000000..645ed48 --- /dev/null +++ b/doc/asio_http2.h.rst.in @@ -0,0 +1,5 @@ +asio_http2.h +============ + +.. literalinclude:: @top_srcdir@/src/includes/nghttp2/asio_http2.h + :language: cpp diff --git a/doc/asio_http2_client.h.rst.in b/doc/asio_http2_client.h.rst.in new file mode 100644 index 0000000..756f00f --- /dev/null +++ b/doc/asio_http2_client.h.rst.in @@ -0,0 +1,5 @@ +asio_http2_client.h +=================== + +.. literalinclude:: @top_srcdir@/src/includes/nghttp2/asio_http2_client.h + :language: cpp diff --git a/doc/asio_http2_server.h.rst.in b/doc/asio_http2_server.h.rst.in new file mode 100644 index 0000000..e7c1434 --- /dev/null +++ b/doc/asio_http2_server.h.rst.in @@ -0,0 +1,5 @@ +asio_http2_server.h +=================== + +.. literalinclude:: @top_srcdir@/src/includes/nghttp2/asio_http2_server.h + :language: cpp diff --git a/doc/bash_completion/h2load b/doc/bash_completion/h2load new file mode 100644 index 0000000..2a4bf98 --- /dev/null +++ b/doc/bash_completion/h2load @@ -0,0 +1,19 @@ +_h2load() +{ + local cur prev split=false + COMPREPLY=() + COMP_WORDBREAKS=${COMP_WORDBREAKS//=} + + cmd=${COMP_WORDS[0]} + _get_comp_words_by_ref cur prev + case $cur in + -*) + COMPREPLY=( $( compgen -W '--threads --connection-window-bits --input-file --help --requests --data --verbose --version --window-bits --clients --no-tls-proto --header --max-concurrent-streams ' -- "$cur" ) ) + ;; + *) + _filedir + return 0 + esac + return 0 +} +complete -F _h2load h2load diff --git a/doc/bash_completion/nghttp b/doc/bash_completion/nghttp new file mode 100644 index 0000000..6bb7a3e --- /dev/null +++ b/doc/bash_completion/nghttp @@ -0,0 +1,19 @@ +_nghttp() +{ + local cur prev split=false + COMPREPLY=() + COMP_WORDBREAKS=${COMP_WORDBREAKS//=} + + cmd=${COMP_WORDS[0]} + _get_comp_words_by_ref cur prev + case $cur in + -*) + COMPREPLY=( $( compgen -W '--no-push --verbose --no-dep --get-assets --har --header-table-size --multiply --padding --hexdump --continuation --connection-window-bits --peer-max-concurrent-streams --timeout --data --no-content-length --version --color --cert --upgrade --remote-name --trailer --weight --help --key --null-out --window-bits --stat --header ' -- "$cur" ) ) + ;; + *) + _filedir + return 0 + esac + return 0 +} +complete -F _nghttp nghttp diff --git a/doc/bash_completion/nghttpd b/doc/bash_completion/nghttpd new file mode 100644 index 0000000..6de3d26 --- /dev/null +++ b/doc/bash_completion/nghttpd @@ -0,0 +1,19 @@ +_nghttpd() +{ + local cur prev split=false + COMPREPLY=() + COMP_WORDBREAKS=${COMP_WORDBREAKS//=} + + cmd=${COMP_WORDS[0]} + _get_comp_words_by_ref cur prev + case $cur in + -*) + COMPREPLY=( $( compgen -W '--error-gzip --push --header-table-size --trailer --htdocs --address --padding --verbose --version --help --hexdump --dh-param-file --daemon --verify-client --echo-upload --workers --no-tls --color --early-response --max-concurrent-streams ' -- "$cur" ) ) + ;; + *) + _filedir + return 0 + esac + return 0 +} +complete -F _nghttpd nghttpd diff --git a/doc/bash_completion/nghttpx b/doc/bash_completion/nghttpx new file mode 100644 index 0000000..7bbb77c --- /dev/null +++ b/doc/bash_completion/nghttpx @@ -0,0 +1,19 @@ +_nghttpx() +{ + local cur prev split=false + COMPREPLY=() + COMP_WORDBREAKS=${COMP_WORDBREAKS//=} + + cmd=${COMP_WORDS[0]} + _get_comp_words_by_ref cur prev + case $cur in + -*) + COMPREPLY=( $( compgen -W '--worker-read-rate --frontend-no-tls --frontend-http2-dump-response-header --backend-http1-connections-per-frontend --tls-ticket-key-file --verify-client-cacert --backend-request-buffer --backend-http2-connection-window-bits --conf --worker-write-burst --npn-list --fetch-ocsp-response-file --stream-read-timeout --accesslog-syslog --frontend-http2-read-timeout --listener-disable-timeout --frontend-http2-connection-window-bits --ciphers --strip-incoming-x-forwarded-for --daemon --backend-keep-alive-timeout --backend-http-proxy-uri --backend-http1-connections-per-host --rlimit-nofile --no-via --ocsp-update-interval --backend-write-timeout --client --http2-no-cookie-crumbling --worker-read-burst --client-proxy --http2-bridge --accesslog-format --errorlog-syslog --errorlog-file --http2-max-concurrent-streams --frontend-write-timeout --read-burst --backend-ipv4 --backend-ipv6 --backend --insecure --log-level --tls-proto-list --backend-http2-connections-per-worker --dh-param-file --worker-frontend-connections --header-field-buffer --no-server-push --no-location-rewrite --no-ocsp --backend-response-buffer --workers --frontend-http2-window-bits --no-host-rewrite --worker-write-rate --backend-tls-sni-field --subcert --help --frontend-frame-debug --pid-file --frontend-http2-dump-request-header --private-key-passwd-file --write-rate --altsvc --user --add-x-forwarded-for --syslog-facility --frontend-read-timeout --backlog --write-burst --backend-http2-window-bits --padding --stream-write-timeout --cacert --version --verify-client --backend-read-timeout --frontend --accesslog-file --http2-proxy --max-header-fields --backend-no-tls --client-private-key-file --client-cert-file --add-response-header --read-rate ' -- "$cur" ) ) + ;; + *) + _filedir + return 0 + esac + return 0 +} +complete -F _nghttpx nghttpx diff --git a/doc/building-android-binary.rst.in b/doc/building-android-binary.rst.in new file mode 100644 index 0000000..2c77c98 --- /dev/null +++ b/doc/building-android-binary.rst.in @@ -0,0 +1 @@ +.. include:: @top_srcdir@/doc/sources/building-android-binary.rst diff --git a/doc/conf.py.in b/doc/conf.py.in new file mode 100644 index 0000000..0e572cf --- /dev/null +++ b/doc/conf.py.in @@ -0,0 +1,253 @@ +# -*- coding: utf-8 -*- +# nghttp2 - HTTP/2 C Library + +# Copyright (c) 2012 Tatsuhiro Tsujikawa + +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: + +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. + +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +# +# nghttp2 documentation build configuration file, created by +# sphinx-quickstart on Sun Mar 11 22:57:49 2012. +# +# This file is execfile()d with the current directory set to its containing dir. +# +# Note that not all possible configuration values are present in this +# autogenerated file. +# +# All configuration values have a default; values that are commented out +# serve to show the default. + +import sys, os + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +#sys.path.insert(0, os.path.abspath('.')) + +# -- General configuration ----------------------------------------------------- + +# If your documentation needs a minimal Sphinx version, state it here. +#needs_sphinx = '1.0' + +# Add any Sphinx extension module names here, as strings. They can be extensions +# coming with Sphinx (named 'sphinx.ext.*') or your custom ones. +extensions = [] + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['@top_srcdir@/_templates'] + +# The suffix of source filenames. +source_suffix = '.rst' + +# The encoding of source files. +#source_encoding = 'utf-8-sig' + +# The master toctree document. +master_doc = 'index' + +# General information about the project. +project = u'nghttp2' +copyright = u'2012, 2015, Tatsuhiro Tsujikawa' + +# The version info for the project you're documenting, acts as replacement for +# |version| and |release|, also used in various other places throughout the +# built documents. +# +# The short X.Y version. +version = '@PACKAGE_VERSION@' +# The full version, including alpha/beta/rc tags. +release = '@PACKAGE_VERSION@' + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +#language = None + +# There are two options for replacing |today|: either, you set today to some +# non-false value, then it is used: +#today = '' +# Else, today_fmt is used as the format for a strftime call. +#today_fmt = '%B %d, %Y' + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +exclude_patterns = ['manual', 'README.rst', '*-header.rst', 'sources'] + +# The reST default role (used for this markup: `text`) to use for all documents. +default_role = 'c:func' +primary_domain = 'c' + +# If true, '()' will be appended to :func: etc. cross-reference text. +#add_function_parentheses = True + +# If true, the current module name will be prepended to all description +# unit titles (such as .. function::). +#add_module_names = True + +# If true, sectionauthor and moduleauthor directives will be shown in the +# output. They are ignored by default. +#show_authors = False + +# The default language to highlight source code in. The default is 'python'. +highlight_language = 'c' + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = 'sphinx' + +# A list of ignored prefixes for module index sorting. +#modindex_common_prefix = [] + + +# -- Options for HTML output --------------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +html_theme = 'sphinx_rtd_theme' + +# Theme options are theme-specific and customize the look and feel of a theme +# further. For a list of options available for each theme, see the +# documentation. +#html_theme_options = {} + +# Add any paths that contain custom themes here, relative to this directory. +html_theme_path = ['@top_srcdir@/doc/_themes'] + +# The name for this set of Sphinx documents. If None, it defaults to +# " v documentation". +#html_title = None + +# A shorter title for the navigation bar. Default is the same as html_title. +#html_short_title = None + +# The name of an image file (relative to this directory) to place at the top +# of the sidebar. +#html_logo = None + +# The name of an image file (within the static path) to use as favicon of the +# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 +# pixels large. +#html_favicon = None + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +#html_static_path = [] + +# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, +# using the given strftime format. +#html_last_updated_fmt = '%b %d, %Y' + +# If true, SmartyPants will be used to convert quotes and dashes to +# typographically correct entities. +html_use_smartypants = False + +# Custom sidebar templates, maps document names to template names. +html_sidebars = { + '**': ['menu.html', 'localtoc.html', 'relations.html', 'sourcelink.html', + 'searchbox.html'] + } + +# Additional templates that should be rendered to pages, maps page names to +# template names. +#html_additional_pages = {} + +# If false, no module index is generated. +#html_domain_indices = True + +# If false, no index is generated. +#html_use_index = True + +# If true, the index is split into individual pages for each letter. +#html_split_index = False + +# If true, links to the reST sources are added to the pages. +html_show_sourcelink = False +html_copy_source = False + +# If true, "Created using Sphinx" is shown in the HTML footer. Default is True. +#html_show_sphinx = True + +# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. +#html_show_copyright = True + +# If true, an OpenSearch description file will be output, and all pages will +# contain a tag referring to it. The value of this option must be the +# base URL from which the finished HTML is served. +#html_use_opensearch = '' + +# This is the file name suffix for HTML files (e.g. ".xhtml"). +#html_file_suffix = None + +# Output file base name for HTML help builder. +htmlhelp_basename = 'nghttp2doc' + + +# -- Options for LaTeX output -------------------------------------------------- + +# The paper size ('letter' or 'a4'). +#latex_paper_size = 'letter' + +# The font size ('10pt', '11pt' or '12pt'). +#latex_font_size = '10pt' + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, author, documentclass [howto/manual]). +latex_documents = [ + ('index', 'nghttp2.tex', u'nghttp2 Documentation', + u'Tatsuhiro Tsujikawa', 'manual'), +] + +# The name of an image file (relative to this directory) to place at the top of +# the title page. +#latex_logo = None + +# For "manual" documents, if this is true, then toplevel headings are parts, +# not chapters. +#latex_use_parts = False + +# If true, show page references after internal links. +#latex_show_pagerefs = False + +# If true, show URL addresses after external links. +#latex_show_urls = False + +# Additional stuff for the LaTeX preamble. +#latex_preamble = '' + +# Documents to append as an appendix to all manuals. +#latex_appendices = [] + +# If false, no module index is generated. +#latex_domain_indices = True + + +# -- Options for manual page output -------------------------------------------- + +# One entry per manual page. List of tuples +# (source start file, name, description, authors, manual section). +man_pages = [ + ('nghttp.1', 'nghttp', u'HTTP/2 experimental client', + [u'Tatsuhiro Tsujikawa'], 1), + ('nghttpd.1', 'nghttpd', u'HTTP/2 experimental server', + [u'Tatsuhiro Tsujikawa'], 1), + ('nghttpx.1', 'nghttpx', u'HTTP/2 experimental proxy', + [u'Tatsuhiro Tsujikawa'], 1), + ('h2load.1', 'h2load', u'HTTP/2 benchmarking tool', + [u'Tatsuhiro Tsujikawa'], 1) +] diff --git a/doc/contribute.rst.in b/doc/contribute.rst.in new file mode 100644 index 0000000..6a229d4 --- /dev/null +++ b/doc/contribute.rst.in @@ -0,0 +1 @@ +.. include:: @top_srcdir@/doc/sources/contribute.rst diff --git a/doc/enums.rst b/doc/enums.rst new file mode 100644 index 0000000..27041c9 --- /dev/null +++ b/doc/enums.rst @@ -0,0 +1,446 @@ + +Enums +===== +.. type:: nghttp2_error + + + Error codes used in this library. The code range is [-999, -500], + inclusive. The following values are defined: + + .. macro:: NGHTTP2_ERR_INVALID_ARGUMENT + + (``-501``) + Invalid argument passed. + .. macro:: NGHTTP2_ERR_BUFFER_ERROR + + (``-502``) + Out of buffer space. + .. macro:: NGHTTP2_ERR_UNSUPPORTED_VERSION + + (``-503``) + The specified protocol version is not supported. + .. macro:: NGHTTP2_ERR_WOULDBLOCK + + (``-504``) + Used as a return value from :type:`nghttp2_send_callback`, + :type:`nghttp2_recv_callback` and + :type:`nghttp2_send_data_callback` to indicate that the operation + would block. + .. macro:: NGHTTP2_ERR_PROTO + + (``-505``) + General protocol error + .. macro:: NGHTTP2_ERR_INVALID_FRAME + + (``-506``) + The frame is invalid. + .. macro:: NGHTTP2_ERR_EOF + + (``-507``) + The peer performed a shutdown on the connection. + .. macro:: NGHTTP2_ERR_DEFERRED + + (``-508``) + Used as a return value from + :func:`nghttp2_data_source_read_callback` to indicate that data + transfer is postponed. See + :func:`nghttp2_data_source_read_callback` for details. + .. macro:: NGHTTP2_ERR_STREAM_ID_NOT_AVAILABLE + + (``-509``) + Stream ID has reached the maximum value. Therefore no stream ID + is available. + .. macro:: NGHTTP2_ERR_STREAM_CLOSED + + (``-510``) + The stream is already closed; or the stream ID is invalid. + .. macro:: NGHTTP2_ERR_STREAM_CLOSING + + (``-511``) + RST_STREAM has been added to the outbound queue. The stream is + in closing state. + .. macro:: NGHTTP2_ERR_STREAM_SHUT_WR + + (``-512``) + The transmission is not allowed for this stream (e.g., a frame + with END_STREAM flag set has already sent). + .. macro:: NGHTTP2_ERR_INVALID_STREAM_ID + + (``-513``) + The stream ID is invalid. + .. macro:: NGHTTP2_ERR_INVALID_STREAM_STATE + + (``-514``) + The state of the stream is not valid (e.g., DATA cannot be sent + to the stream if response HEADERS has not been sent). + .. macro:: NGHTTP2_ERR_DEFERRED_DATA_EXIST + + (``-515``) + Another DATA frame has already been deferred. + .. macro:: NGHTTP2_ERR_START_STREAM_NOT_ALLOWED + + (``-516``) + Starting new stream is not allowed (e.g., GOAWAY has been sent + and/or received). + .. macro:: NGHTTP2_ERR_GOAWAY_ALREADY_SENT + + (``-517``) + GOAWAY has already been sent. + .. macro:: NGHTTP2_ERR_INVALID_HEADER_BLOCK + + (``-518``) + The received frame contains the invalid header block (e.g., There + are duplicate header names; or the header names are not encoded + in US-ASCII character set and not lower cased; or the header name + is zero-length string; or the header value contains multiple + in-sequence NUL bytes). + .. macro:: NGHTTP2_ERR_INVALID_STATE + + (``-519``) + Indicates that the context is not suitable to perform the + requested operation. + .. macro:: NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE + + (``-521``) + The user callback function failed due to the temporal error. + .. macro:: NGHTTP2_ERR_FRAME_SIZE_ERROR + + (``-522``) + The length of the frame is invalid, either too large or too small. + .. macro:: NGHTTP2_ERR_HEADER_COMP + + (``-523``) + Header block inflate/deflate error. + .. macro:: NGHTTP2_ERR_FLOW_CONTROL + + (``-524``) + Flow control error + .. macro:: NGHTTP2_ERR_INSUFF_BUFSIZE + + (``-525``) + Insufficient buffer size given to function. + .. macro:: NGHTTP2_ERR_PAUSE + + (``-526``) + Callback was paused by the application + .. macro:: NGHTTP2_ERR_TOO_MANY_INFLIGHT_SETTINGS + + (``-527``) + There are too many in-flight SETTING frame and no more + transmission of SETTINGS is allowed. + .. macro:: NGHTTP2_ERR_PUSH_DISABLED + + (``-528``) + The server push is disabled. + .. macro:: NGHTTP2_ERR_DATA_EXIST + + (``-529``) + DATA frame for a given stream has been already submitted and has + not been fully processed yet. + .. macro:: NGHTTP2_ERR_SESSION_CLOSING + + (``-530``) + The current session is closing due to a connection error or + `nghttp2_session_terminate_session()` is called. + .. macro:: NGHTTP2_ERR_HTTP_HEADER + + (``-531``) + Invalid HTTP header field was received and stream is going to be + closed. + .. macro:: NGHTTP2_ERR_HTTP_MESSAGING + + (``-532``) + Violation in HTTP messaging rule. + .. macro:: NGHTTP2_ERR_REFUSED_STREAM + + (``-533``) + Stream was refused. + .. macro:: NGHTTP2_ERR_INTERNAL + + (``-534``) + Unexpected internal error, but recovered. + .. macro:: NGHTTP2_ERR_FATAL + + (``-900``) + The errors < :macro:`NGHTTP2_ERR_FATAL` mean that the library is + under unexpected condition and processing was terminated (e.g., + out of memory). If application receives this error code, it must + stop using that :type:`nghttp2_session` object and only allowed + operation for that object is deallocate it using + `nghttp2_session_del()`. + .. macro:: NGHTTP2_ERR_NOMEM + + (``-901``) + Out of memory. This is a fatal error. + .. macro:: NGHTTP2_ERR_CALLBACK_FAILURE + + (``-902``) + The user callback function failed. This is a fatal error. + .. macro:: NGHTTP2_ERR_BAD_CLIENT_MAGIC + + (``-903``) + Invalid client magic (see :macro:`NGHTTP2_CLIENT_MAGIC`) was + received and further processing is not possible. + +.. type:: nghttp2_nv_flag + + + The flags for header field name/value pair. + + .. macro:: NGHTTP2_NV_FLAG_NONE + + (``0``) + No flag set. + .. macro:: NGHTTP2_NV_FLAG_NO_INDEX + + (``0x01``) + Indicates that this name/value pair must not be indexed ("Literal + Header Field never Indexed" representation must be used in HPACK + encoding). Other implementation calls this bit as "sensitive". + +.. type:: nghttp2_frame_type + + + The frame types in HTTP/2 specification. + + .. macro:: NGHTTP2_DATA + + (``0``) + The DATA frame. + .. macro:: NGHTTP2_HEADERS + + (``0x01``) + The HEADERS frame. + .. macro:: NGHTTP2_PRIORITY + + (``0x02``) + The PRIORITY frame. + .. macro:: NGHTTP2_RST_STREAM + + (``0x03``) + The RST_STREAM frame. + .. macro:: NGHTTP2_SETTINGS + + (``0x04``) + The SETTINGS frame. + .. macro:: NGHTTP2_PUSH_PROMISE + + (``0x05``) + The PUSH_PROMISE frame. + .. macro:: NGHTTP2_PING + + (``0x06``) + The PING frame. + .. macro:: NGHTTP2_GOAWAY + + (``0x07``) + The GOAWAY frame. + .. macro:: NGHTTP2_WINDOW_UPDATE + + (``0x08``) + The WINDOW_UPDATE frame. + .. macro:: NGHTTP2_CONTINUATION + + (``0x09``) + The CONTINUATION frame. This frame type won't be passed to any + callbacks because the library processes this frame type and its + preceding HEADERS/PUSH_PROMISE as a single frame. + +.. type:: nghttp2_flag + + + The flags for HTTP/2 frames. This enum defines all flags for all + frames. + + .. macro:: NGHTTP2_FLAG_NONE + + (``0``) + No flag set. + .. macro:: NGHTTP2_FLAG_END_STREAM + + (``0x01``) + The END_STREAM flag. + .. macro:: NGHTTP2_FLAG_END_HEADERS + + (``0x04``) + The END_HEADERS flag. + .. macro:: NGHTTP2_FLAG_ACK + + (``0x01``) + The ACK flag. + .. macro:: NGHTTP2_FLAG_PADDED + + (``0x08``) + The PADDED flag. + .. macro:: NGHTTP2_FLAG_PRIORITY + + (``0x20``) + The PRIORITY flag. + +.. type:: nghttp2_settings_id + + The SETTINGS ID. + + .. macro:: NGHTTP2_SETTINGS_HEADER_TABLE_SIZE + + (``0x01``) + SETTINGS_HEADER_TABLE_SIZE + .. macro:: NGHTTP2_SETTINGS_ENABLE_PUSH + + (``0x02``) + SETTINGS_ENABLE_PUSH + .. macro:: NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS + + (``0x03``) + SETTINGS_MAX_CONCURRENT_STREAMS + .. macro:: NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE + + (``0x04``) + SETTINGS_INITIAL_WINDOW_SIZE + .. macro:: NGHTTP2_SETTINGS_MAX_FRAME_SIZE + + (``0x05``) + SETTINGS_MAX_FRAME_SIZE + .. macro:: NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE + + (``0x06``) + SETTINGS_MAX_HEADER_LIST_SIZE + +.. type:: nghttp2_error_code + + The status codes for the RST_STREAM and GOAWAY frames. + + .. macro:: NGHTTP2_NO_ERROR + + (``0x00``) + No errors. + .. macro:: NGHTTP2_PROTOCOL_ERROR + + (``0x01``) + PROTOCOL_ERROR + .. macro:: NGHTTP2_INTERNAL_ERROR + + (``0x02``) + INTERNAL_ERROR + .. macro:: NGHTTP2_FLOW_CONTROL_ERROR + + (``0x03``) + FLOW_CONTROL_ERROR + .. macro:: NGHTTP2_SETTINGS_TIMEOUT + + (``0x04``) + SETTINGS_TIMEOUT + .. macro:: NGHTTP2_STREAM_CLOSED + + (``0x05``) + STREAM_CLOSED + .. macro:: NGHTTP2_FRAME_SIZE_ERROR + + (``0x06``) + FRAME_SIZE_ERROR + .. macro:: NGHTTP2_REFUSED_STREAM + + (``0x07``) + REFUSED_STREAM + .. macro:: NGHTTP2_CANCEL + + (``0x08``) + CANCEL + .. macro:: NGHTTP2_COMPRESSION_ERROR + + (``0x09``) + COMPRESSION_ERROR + .. macro:: NGHTTP2_CONNECT_ERROR + + (``0x0a``) + CONNECT_ERROR + .. macro:: NGHTTP2_ENHANCE_YOUR_CALM + + (``0x0b``) + ENHANCE_YOUR_CALM + .. macro:: NGHTTP2_INADEQUATE_SECURITY + + (``0x0c``) + INADEQUATE_SECURITY + .. macro:: NGHTTP2_HTTP_1_1_REQUIRED + + (``0x0d``) + HTTP_1_1_REQUIRED + +.. type:: nghttp2_data_flag + + + The flags used to set in *data_flags* output parameter in + :type:`nghttp2_data_source_read_callback`. + + .. macro:: NGHTTP2_DATA_FLAG_NONE + + (``0``) + No flag set. + .. macro:: NGHTTP2_DATA_FLAG_EOF + + (``0x01``) + Indicates EOF was sensed. + .. macro:: NGHTTP2_DATA_FLAG_NO_END_STREAM + + (``0x02``) + Indicates that END_STREAM flag must not be set even if + NGHTTP2_DATA_FLAG_EOF is set. Usually this flag is used to send + trailer header fields with `nghttp2_submit_request()` or + `nghttp2_submit_response()`. + .. macro:: NGHTTP2_DATA_FLAG_NO_COPY + + (``0x04``) + Indicates that application will send complete DATA frame in + :type:`nghttp2_send_data_callback`. + +.. type:: nghttp2_headers_category + + + The category of HEADERS, which indicates the role of the frame. In + HTTP/2 spec, request, response, push response and other arbitrary + headers (e.g., trailers) are all called just HEADERS. To give the + application the role of incoming HEADERS frame, we define several + categories. + + .. macro:: NGHTTP2_HCAT_REQUEST + + (``0``) + The HEADERS frame is opening new stream, which is analogous to + SYN_STREAM in SPDY. + .. macro:: NGHTTP2_HCAT_RESPONSE + + (``1``) + The HEADERS frame is the first response headers, which is + analogous to SYN_REPLY in SPDY. + .. macro:: NGHTTP2_HCAT_PUSH_RESPONSE + + (``2``) + The HEADERS frame is the first headers sent against reserved + stream. + .. macro:: NGHTTP2_HCAT_HEADERS + + (``3``) + The HEADERS frame which does not apply for the above categories, + which is analogous to HEADERS in SPDY. If non-final response + (e.g., status 1xx) is used, final response HEADERS frame will be + categorized here. + +.. type:: nghttp2_hd_inflate_flag + + + The flags for header inflation. + + .. macro:: NGHTTP2_HD_INFLATE_NONE + + (``0``) + No flag set. + .. macro:: NGHTTP2_HD_INFLATE_FINAL + + (``0x01``) + Indicates all headers were inflated. + .. macro:: NGHTTP2_HD_INFLATE_EMIT + + (``0x02``) + Indicates a header was emitted. + diff --git a/doc/h2load-howto.rst.in b/doc/h2load-howto.rst.in new file mode 100644 index 0000000..252069d --- /dev/null +++ b/doc/h2load-howto.rst.in @@ -0,0 +1 @@ +.. include:: @top_srcdir@/doc/sources/h2load-howto.rst diff --git a/doc/h2load.1 b/doc/h2load.1 new file mode 100644 index 0000000..beeb863 --- /dev/null +++ b/doc/h2load.1 @@ -0,0 +1,273 @@ +.\" Man page generated from reStructuredText. +. +.TH "H2LOAD" "1" "May 16, 2015" "1.0.0" "nghttp2" +.SH NAME +h2load \- HTTP/2 benchmarking tool +. +.nr rst2man-indent-level 0 +. +.de1 rstReportMargin +\\$1 \\n[an-margin] +level \\n[rst2man-indent-level] +level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] +- +\\n[rst2man-indent0] +\\n[rst2man-indent1] +\\n[rst2man-indent2] +.. +.de1 INDENT +.\" .rstReportMargin pre: +. RS \\$1 +. nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] +. nr rst2man-indent-level +1 +.\" .rstReportMargin post: +.. +.de UNINDENT +. RE +.\" indent \\n[an-margin] +.\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] +.nr rst2man-indent-level -1 +.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] +.in \\n[rst2man-indent\\n[rst2man-indent-level]]u +.. +.SH SYNOPSIS +.sp +\fBh2load\fP [OPTIONS]... [URI]... +.SH DESCRIPTION +.sp +benchmarking tool for HTTP/2 and SPDY server +.INDENT 0.0 +.TP +.B +Specify URI to access. Multiple URIs can be specified. +URIs are used in this order for each client. All URIs +are used, then first URI is used and then 2nd URI, and +so on. The scheme, host and port in the subsequent +URIs, if present, are ignored. Those in the first URI +are used solely. +.UNINDENT +.SH OPTIONS +.INDENT 0.0 +.TP +.B \-n, \-\-requests= +Number of requests. +.sp +Default: \fB1\fP +.UNINDENT +.INDENT 0.0 +.TP +.B \-c, \-\-clients= +Number of concurrent clients. +.sp +Default: \fB1\fP +.UNINDENT +.INDENT 0.0 +.TP +.B \-t, \-\-threads= +Number of native threads. +.sp +Default: \fB1\fP +.UNINDENT +.INDENT 0.0 +.TP +.B \-i, \-\-input\-file= +Path of a file with multiple URIs are separated by EOLs. +This option will disable URIs getting from command\-line. +If \(aq\-\(aq is given as , URIs will be read from stdin. +URIs are used in this order for each client. All URIs +are used, then first URI is used and then 2nd URI, and +so on. The scheme, host and port in the subsequent +URIs, if present, are ignored. Those in the first URI +are used solely. +.UNINDENT +.INDENT 0.0 +.TP +.B \-m, \-\-max\-concurrent\-streams=(auto|) +Max concurrent streams to issue per session. If "auto" +is given, the number of given URIs is used. +.sp +Default: \fBauto\fP +.UNINDENT +.INDENT 0.0 +.TP +.B \-w, \-\-window\-bits= +Sets the stream level initial window size to (2**)\-1. +For SPDY, 2** is used instead. +.sp +Default: \fB30\fP +.UNINDENT +.INDENT 0.0 +.TP +.B \-W, \-\-connection\-window\-bits= +Sets the connection level initial window size to +(2**)\-1. For SPDY, if is strictly less than 16, +this option is ignored. Otherwise 2** is used for +SPDY. +.sp +Default: \fB30\fP +.UNINDENT +.INDENT 0.0 +.TP +.B \-H, \-\-header=
        +Add/Override a header to the requests. +.UNINDENT +.INDENT 0.0 +.TP +.B \-p, \-\-no\-tls\-proto= +Specify ALPN identifier of the protocol to be used when +accessing http URI without SSL/TLS. +Available protocols: spdy/2, spdy/3, spdy/3.1 and h2c +.sp +Default: \fBh2c\fP +.UNINDENT +.INDENT 0.0 +.TP +.B \-d, \-\-data= +Post FILE to server. The request method is changed to +POST. +.UNINDENT +.INDENT 0.0 +.TP +.B \-v, \-\-verbose +Output debug information. +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-version +Display version information and exit. +.UNINDENT +.INDENT 0.0 +.TP +.B \-h, \-\-help +Display this help and exit. +.UNINDENT +.SH OUTPUT +.INDENT 0.0 +.TP +.B requests +.INDENT 7.0 +.TP +.B total +The number of requests h2load was instructed to make. +.TP +.B started +The number of requests h2load has started. +.TP +.B done +The number of requests completed. +.TP +.B succeeded +The number of requests completed successfully. Only HTTP status +code 2xx or3xx are considered as success. +.TP +.B failed +The number of requests failed, including HTTP level failures +(non\-successful HTTP status code). +.TP +.B errored +The number of requests failed, except for HTTP level failures. +status code. This is the subset of the number reported in +\fBfailed\fP and most likely the network level failures or stream +was reset by RST_STREAM. +.UNINDENT +.TP +.B status codes +The number of status code h2load received. +.TP +.B traffic +.INDENT 7.0 +.TP +.B total +The number of bytes received from the server "on the wire". If +requests were made via TLS, this value is the number of decrpyted +bytes. +.TP +.B headers +The number of response header bytes from the server without +decompression. For HTTP/2, this is the sum of the payload of +HEADERS frame. For SPDY, this is the sum of the payload of +SYN_REPLY frame. +.TP +.B data +The number of response body bytes received from the server. +.UNINDENT +.TP +.B time for request +.INDENT 7.0 +.TP +.B min +The minimum time taken for request and response. +.TP +.B max +The maximum time taken for request and response. +.TP +.B mean +The mean time taken for request and response. +.TP +.B sd +The standard deviation of the time taken for request and response. +.TP +.B +/\- sd +The fraction of the number of requests within standard deviation +range (mean +/\- sd) against total number of successful requests. +.UNINDENT +.TP +.B time for connect +.INDENT 7.0 +.TP +.B min +The minimum time taken to connect to a server. +.TP +.B max +The maximum time taken to connect to a server. +.TP +.B mean +The mean time taken to connect to a server. +.TP +.B sd +The standard deviation of the time taken to connect to a server. +.TP +.B +/\- sd +The fraction of the number of connections within standard +deviation range (mean +/\- sd) against total number of successful +connections. +.UNINDENT +.TP +.B time for 1st byte (of (decrypted in case of TLS) application data) +.INDENT 7.0 +.TP +.B min +The minimum time taken to get 1st byte from a server. +.TP +.B max +The maximum time taken to get 1st byte from a server. +.TP +.B mean +The mean time taken to get 1st byte from a server. +.TP +.B sd +The standard deviation of the time taken to get 1st byte from a +server. +.TP +.B +/\- sd +The fraction of the number of connections within standard +deviation range (mean +/\- sd) against total number of successful +connections. +.UNINDENT +.UNINDENT +.SH FLOW CONTROL +.sp +h2load sets large flow control window by default, and effectively +disables flow control to avoid under utilization of server +performance. To set smaller flow control window, use \fI\%\-w\fP and +\fI\%\-W\fP options. For example, use \fB\-w16 \-W16\fP to set default +window size described in HTTP/2 and SPDY protocol specification. +.SH SEE ALSO +.sp +\fInghttp(1)\fP, \fInghttpd(1)\fP, \fInghttpx(1)\fP +.SH AUTHOR +Tatsuhiro Tsujikawa +.SH COPYRIGHT +2012, 2015, Tatsuhiro Tsujikawa +.\" Generated by docutils manpage writer. +. diff --git a/doc/h2load.1.rst b/doc/h2load.1.rst new file mode 100644 index 0000000..21e5a42 --- /dev/null +++ b/doc/h2load.1.rst @@ -0,0 +1,204 @@ + +.. GENERATED by help2rst.py. DO NOT EDIT DIRECTLY. + +.. program:: h2load + +h2load(1) +========= + +SYNOPSIS +-------- + +**h2load** [OPTIONS]... [URI]... + +DESCRIPTION +----------- + +benchmarking tool for HTTP/2 and SPDY server + +.. describe:: + + Specify URI to access. Multiple URIs can be specified. + URIs are used in this order for each client. All URIs + are used, then first URI is used and then 2nd URI, and + so on. The scheme, host and port in the subsequent + URIs, if present, are ignored. Those in the first URI + are used solely. + +OPTIONS +------- + +.. option:: -n, --requests= + + Number of requests. + + Default: ``1`` + +.. option:: -c, --clients= + + Number of concurrent clients. + + Default: ``1`` + +.. option:: -t, --threads= + + Number of native threads. + + Default: ``1`` + +.. option:: -i, --input-file= + + Path of a file with multiple URIs are separated by EOLs. + This option will disable URIs getting from command-line. + If '-' is given as , URIs will be read from stdin. + URIs are used in this order for each client. All URIs + are used, then first URI is used and then 2nd URI, and + so on. The scheme, host and port in the subsequent + URIs, if present, are ignored. Those in the first URI + are used solely. + +.. option:: -m, --max-concurrent-streams=(auto|) + + Max concurrent streams to issue per session. If "auto" + is given, the number of given URIs is used. + + Default: ``auto`` + +.. option:: -w, --window-bits= + + Sets the stream level initial window size to (2\*\*)-1. + For SPDY, 2** is used instead. + + Default: ``30`` + +.. option:: -W, --connection-window-bits= + + Sets the connection level initial window size to + (2**)-1. For SPDY, if is strictly less than 16, + this option is ignored. Otherwise 2\*\* is used for + SPDY. + + Default: ``30`` + +.. option:: -H, --header=
        + + Add/Override a header to the requests. + +.. option:: -p, --no-tls-proto= + + Specify ALPN identifier of the protocol to be used when + accessing http URI without SSL/TLS. + Available protocols: spdy/2, spdy/3, spdy/3.1 and h2c + + Default: ``h2c`` + +.. option:: -d, --data= + + Post FILE to server. The request method is changed to + POST. + +.. option:: -v, --verbose + + Output debug information. + +.. option:: --version + + Display version information and exit. + +.. option:: -h, --help + + Display this help and exit. + +OUTPUT +------ + +requests + total + The number of requests h2load was instructed to make. + started + The number of requests h2load has started. + done + The number of requests completed. + succeeded + The number of requests completed successfully. Only HTTP status + code 2xx or3xx are considered as success. + failed + The number of requests failed, including HTTP level failures + (non-successful HTTP status code). + errored + The number of requests failed, except for HTTP level failures. + status code. This is the subset of the number reported in + ``failed`` and most likely the network level failures or stream + was reset by RST_STREAM. + +status codes + The number of status code h2load received. + +traffic + total + The number of bytes received from the server "on the wire". If + requests were made via TLS, this value is the number of decrpyted + bytes. + headers + The number of response header bytes from the server without + decompression. For HTTP/2, this is the sum of the payload of + HEADERS frame. For SPDY, this is the sum of the payload of + SYN_REPLY frame. + data + The number of response body bytes received from the server. + +time for request + min + The minimum time taken for request and response. + max + The maximum time taken for request and response. + mean + The mean time taken for request and response. + sd + The standard deviation of the time taken for request and response. + +/- sd + The fraction of the number of requests within standard deviation + range (mean +/- sd) against total number of successful requests. + +time for connect + min + The minimum time taken to connect to a server. + max + The maximum time taken to connect to a server. + mean + The mean time taken to connect to a server. + sd + The standard deviation of the time taken to connect to a server. + +/- sd + The fraction of the number of connections within standard + deviation range (mean +/- sd) against total number of successful + connections. + +time for 1st byte (of (decrypted in case of TLS) application data) + min + The minimum time taken to get 1st byte from a server. + max + The maximum time taken to get 1st byte from a server. + mean + The mean time taken to get 1st byte from a server. + sd + The standard deviation of the time taken to get 1st byte from a + server. + +/- sd + The fraction of the number of connections within standard + deviation range (mean +/- sd) against total number of successful + connections. + +FLOW CONTROL +------------ + +h2load sets large flow control window by default, and effectively +disables flow control to avoid under utilization of server +performance. To set smaller flow control window, use :option:`-w` and +:option:`-W` options. For example, use ``-w16 -W16`` to set default +window size described in HTTP/2 and SPDY protocol specification. + +SEE ALSO +-------- + +:manpage:`nghttp(1)`, :manpage:`nghttpd(1)`, :manpage:`nghttpx(1)` diff --git a/doc/index.rst.in b/doc/index.rst.in new file mode 100644 index 0000000..2a49369 --- /dev/null +++ b/doc/index.rst.in @@ -0,0 +1 @@ +.. include:: @top_srcdir@/doc/sources/index.rst diff --git a/doc/libnghttp2_asio.rst.in b/doc/libnghttp2_asio.rst.in new file mode 100644 index 0000000..38254e1 --- /dev/null +++ b/doc/libnghttp2_asio.rst.in @@ -0,0 +1 @@ +.. include:: @top_srcdir@/doc/sources/libnghttp2_asio.rst diff --git a/doc/macros.rst b/doc/macros.rst new file mode 100644 index 0000000..ee9ec33 --- /dev/null +++ b/doc/macros.rst @@ -0,0 +1,86 @@ + +Macros +====== +.. macro:: NGHTTP2_VERSION + + Version number of the nghttp2 library release +.. macro:: NGHTTP2_VERSION_NUM + + Numerical representation of the version number of the nghttp2 library + 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. +.. macro:: NGHTTP2_PROTO_VERSION_ID + + + The protocol version identification string of this library + supports. This identifier is used if HTTP/2 is used over TLS. +.. macro:: NGHTTP2_PROTO_VERSION_ID_LEN + + + The length of :macro:`NGHTTP2_PROTO_VERSION_ID`. +.. macro:: NGHTTP2_PROTO_ALPN + + + The seriazlied form of ALPN protocol identifier this library + supports. Notice that first byte is the length of following + protocol identifier. This is the same wire format of `TLS ALPN + extension `_. This is useful + to process incoming ALPN tokens in wire format. +.. macro:: NGHTTP2_PROTO_ALPN_LEN + + + The length of :macro:`NGHTTP2_PROTO_ALPN`. +.. macro:: NGHTTP2_CLEARTEXT_PROTO_VERSION_ID + + + The protocol version identification string of this library + supports. This identifier is used if HTTP/2 is used over cleartext + TCP. +.. macro:: NGHTTP2_CLEARTEXT_PROTO_VERSION_ID_LEN + + + The length of :macro:`NGHTTP2_CLEARTEXT_PROTO_VERSION_ID`. +.. macro:: NGHTTP2_VERSION_AGE + + + The age of :type:`nghttp2_info` +.. macro:: NGHTTP2_DEFAULT_WEIGHT + + + The default weight of stream dependency. +.. macro:: NGHTTP2_MAX_WEIGHT + + + The maximum weight of stream dependency. +.. macro:: NGHTTP2_MIN_WEIGHT + + + The minimum weight of stream dependency. +.. macro:: NGHTTP2_MAX_WINDOW_SIZE + + + The maximum window size +.. macro:: NGHTTP2_INITIAL_WINDOW_SIZE + + + The initial window size for stream level flow control. +.. macro:: NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE + + + The initial window size for connection level flow control. +.. macro:: NGHTTP2_DEFAULT_HEADER_TABLE_SIZE + + + The default header table size. +.. macro:: NGHTTP2_CLIENT_MAGIC + + + The client magic string, which is the first 24 bytes byte string of + client connection preface. +.. macro:: NGHTTP2_CLIENT_MAGIC_LEN + + + The length of :macro:`NGHTTP2_CLIENT_MAGIC`. +.. macro:: NGHTTP2_INITIAL_MAX_CONCURRENT_STREAMS + + Default maximum concurrent streams. diff --git a/doc/mkapiref.py b/doc/mkapiref.py new file mode 100755 index 0000000..23ab9cc --- /dev/null +++ b/doc/mkapiref.py @@ -0,0 +1,277 @@ +#!/usr/bin/env python +# nghttp2 - HTTP/2 C Library + +# Copyright (c) 2012 Tatsuhiro Tsujikawa + +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: + +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. + +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +# Generates API reference from C source code. +from __future__ import print_function # At least python 2.6 is required +import re, sys, argparse, os.path + +class FunctionDoc: + def __init__(self, name, content, domain): + self.name = name + self.content = content + self.domain = domain + if self.domain == 'function': + self.funcname = re.search(r'(nghttp2_[^ )]+)\(', self.name).group(1) + + def write(self, out): + out.write('.. {}:: {}\n'.format(self.domain, self.name)) + out.write('\n') + for line in self.content: + out.write(' {}\n'.format(line)) + +class StructDoc: + def __init__(self, name, content, members, member_domain): + self.name = name + self.content = content + self.members = members + self.member_domain = member_domain + + def write(self, out): + if self.name: + out.write('.. type:: {}\n'.format(self.name)) + out.write('\n') + for line in self.content: + out.write(' {}\n'.format(line)) + out.write('\n') + for name, content in self.members: + out.write(' .. {}:: {}\n'.format(self.member_domain, name)) + out.write('\n') + for line in content: + out.write(' {}\n'.format(line)) + out.write('\n') + +class MacroDoc: + def __init__(self, name, content): + self.name = name + self.content = content + + def write(self, out): + out.write('''.. macro:: {}\n'''.format(self.name)) + out.write('\n') + for line in self.content: + out.write(' {}\n'.format(line)) + +def make_api_ref(infiles): + macros = [] + enums = [] + types = [] + functions = [] + for infile in infiles: + while True: + line = infile.readline() + if not line: + break + elif line == '/**\n': + line = infile.readline() + doctype = line.split()[1] + if doctype == '@function': + functions.append(process_function('function', infile)) + elif doctype == '@functypedef': + types.append(process_function('type', infile)) + elif doctype == '@struct' or doctype == '@union': + types.append(process_struct(infile)) + elif doctype == '@enum': + enums.append(process_enum(infile)) + elif doctype == '@macro': + macros.append(process_macro(infile)) + return macros, enums, types, functions + + alldocs = [('Macros', macros), + ('Enums', enums), + ('Types (structs, unions and typedefs)', types), + ('Functions', functions)] + +def output( + indexfile, macrosfile, enumsfile, typesfile, funcsdir, + macros, enums, types, functions): + indexfile.write(''' +API Reference +============= + +.. toctree:: + :maxdepth: 1 + + macros + enums + types +''') + + for doc in functions: + indexfile.write(' {}\n'.format(doc.funcname)) + + macrosfile.write(''' +Macros +====== +''') + for doc in macros: + doc.write(macrosfile) + + enumsfile.write(''' +Enums +===== +''') + for doc in enums: + doc.write(enumsfile) + + typesfile.write(''' +Types (structs, unions and typedefs) +==================================== +''') + for doc in types: + doc.write(typesfile) + + for doc in functions: + with open(os.path.join(funcsdir, doc.funcname + '.rst'), 'w') as f: + f.write(''' +{funcname} +{secul} + +Synopsis +-------- + +*#include * + +'''.format(funcname=doc.funcname, secul='='*len(doc.funcname))) + doc.write(f) + +def process_macro(infile): + content = read_content(infile) + line = infile.readline() + macro_name = line.split()[1] + return MacroDoc(macro_name, content) + +def process_enum(infile): + members = [] + enum_name = None + content = read_content(infile) + while True: + line = infile.readline() + if not line: + break + elif re.match(r'\s*/\*\*\n', line): + member_content = read_content(infile) + line = infile.readline() + items = line.split() + member_name = items[0] + if len(items) >= 3: + member_content.insert(0, '(``{}``) '\ + .format(' '.join(items[2:]).rstrip(','))) + members.append((member_name, member_content)) + elif line.startswith('}'): + enum_name = line.rstrip().split()[1] + enum_name = re.sub(r';$', '', enum_name) + break + return StructDoc(enum_name, content, members, 'macro') + +def process_struct(infile): + members = [] + struct_name = None + content = read_content(infile) + while True: + line = infile.readline() + if not line: + break + elif re.match(r'\s*/\*\*\n', line): + member_content = read_content(infile) + line = infile.readline() + member_name = line.rstrip().rstrip(';') + members.append((member_name, member_content)) + elif line.startswith('}') or\ + (line.startswith('typedef ') and line.endswith(';\n')): + if line.startswith('}'): + index = 1 + else: + index = 3 + struct_name = line.rstrip().split()[index] + struct_name = re.sub(r';$', '', struct_name) + break + return StructDoc(struct_name, content, members, 'member') + +def process_function(domain, infile): + content = read_content(infile) + func_proto = [] + while True: + line = infile.readline() + if not line: + break + elif line == '\n': + break + else: + func_proto.append(line) + func_proto = ''.join(func_proto) + func_proto = re.sub(r';\n$', '', func_proto) + func_proto = re.sub(r'\s+', ' ', func_proto) + func_proto = re.sub(r'NGHTTP2_EXTERN ', '', func_proto) + return FunctionDoc(func_proto, content, domain) + +def read_content(infile): + content = [] + while True: + line = infile.readline() + if not line: + break + if re.match(r'\s*\*/\n', line): + break + else: + content.append(transform_content(line.rstrip())) + return content + +def arg_repl(matchobj): + return '*{}*'.format(matchobj.group(1).replace('*', '\\*')) + +def transform_content(content): + content = re.sub(r'^\s+\* ?', '', content) + content = re.sub(r'\|([^\s|]+)\|', arg_repl, content) + content = re.sub(r':enum:', ':macro:', content) + return content + +if __name__ == '__main__': + parser = argparse.ArgumentParser(description="Generate API reference") + parser.add_argument('index', type=argparse.FileType('w'), + help='index output file') + parser.add_argument('macros', type=argparse.FileType('w'), + help='macros section output file. The filename should be macros.rst') + parser.add_argument('enums', type=argparse.FileType('w'), + help='enums section output file. The filename should be enums.rst') + parser.add_argument('types', type=argparse.FileType('w'), + help='types section output file. The filename should be types.rst') + parser.add_argument('funcsdir', + help='functions doc output dir') + parser.add_argument('files', nargs='+', type=argparse.FileType('r'), + help='source file') + args = parser.parse_args() + macros = [] + enums = [] + types = [] + funcs = [] + for infile in args.files: + m, e, t, f = make_api_ref(args.files) + macros.extend(m) + enums.extend(e) + types.extend(t) + funcs.extend(f) + funcs.sort(key=lambda x: x.funcname) + output( + args.index, args.macros, args.enums, args.types, args.funcsdir, + macros, enums, types, funcs) diff --git a/doc/nghttp.1 b/doc/nghttp.1 new file mode 100644 index 0000000..c29360f --- /dev/null +++ b/doc/nghttp.1 @@ -0,0 +1,289 @@ +.\" Man page generated from reStructuredText. +. +.TH "NGHTTP" "1" "May 16, 2015" "1.0.0" "nghttp2" +.SH NAME +nghttp \- HTTP/2 experimental client +. +.nr rst2man-indent-level 0 +. +.de1 rstReportMargin +\\$1 \\n[an-margin] +level \\n[rst2man-indent-level] +level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] +- +\\n[rst2man-indent0] +\\n[rst2man-indent1] +\\n[rst2man-indent2] +.. +.de1 INDENT +.\" .rstReportMargin pre: +. RS \\$1 +. nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] +. nr rst2man-indent-level +1 +.\" .rstReportMargin post: +.. +.de UNINDENT +. RE +.\" indent \\n[an-margin] +.\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] +.nr rst2man-indent-level -1 +.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] +.in \\n[rst2man-indent\\n[rst2man-indent-level]]u +.. +.SH SYNOPSIS +.sp +\fBnghttp\fP [OPTIONS]... ... +.SH DESCRIPTION +.sp +HTTP/2 experimental client +.INDENT 0.0 +.TP +.B +Specify URI to access. +.UNINDENT +.SH OPTIONS +.INDENT 0.0 +.TP +.B \-v, \-\-verbose +Print debug information such as reception and +transmission of frames and name/value pairs. Specifying +this option multiple times increases verbosity. +.UNINDENT +.INDENT 0.0 +.TP +.B \-n, \-\-null\-out +Discard downloaded data. +.UNINDENT +.INDENT 0.0 +.TP +.B \-O, \-\-remote\-name +Save download data in the current directory. The +filename is dereived from URI. If URI ends with \(aq\fI/\fP\(aq, +\(aqindex.html\(aq is used as a filename. Not implemented +yet. +.UNINDENT +.INDENT 0.0 +.TP +.B \-t, \-\-timeout= +Timeout each request after . Set 0 to disable +timeout. +.UNINDENT +.INDENT 0.0 +.TP +.B \-w, \-\-window\-bits= +Sets the stream level initial window size to 2**\-1. +.UNINDENT +.INDENT 0.0 +.TP +.B \-W, \-\-connection\-window\-bits= +Sets the connection level initial window size to +2**\-1. +.UNINDENT +.INDENT 0.0 +.TP +.B \-a, \-\-get\-assets +Download assets such as stylesheets, images and script +files linked from the downloaded resource. Only links +whose origins are the same with the linking resource +will be downloaded. nghttp prioritizes resources using +HTTP/2 dependency based priority. The priority order, +from highest to lowest, is html itself, css, javascript +and images. +.UNINDENT +.INDENT 0.0 +.TP +.B \-s, \-\-stat +Print statistics. +.UNINDENT +.INDENT 0.0 +.TP +.B \-H, \-\-header=
        +Add a header to the requests. Example: \fI\%\-H\fP\(aq:method: PUT\(aq +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-trailer=
        +Add a trailer header to the requests.
        must not +include pseudo header field (header field name starting +with \(aq:\(aq). To send trailer, one must use \fI\%\-d\fP option to +send request body. Example: \fI\%\-\-trailer\fP \(aqfoo: bar\(aq. +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-cert= +Use the specified client certificate file. The file +must be in PEM format. +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-key= +Use the client private key file. The file must be in +PEM format. +.UNINDENT +.INDENT 0.0 +.TP +.B \-d, \-\-data= +Post FILE to server. If \(aq\-\(aq is given, data will be read +from stdin. +.UNINDENT +.INDENT 0.0 +.TP +.B \-m, \-\-multiply= +Request each URI times. By default, same URI is not +requested twice. This option disables it too. +.UNINDENT +.INDENT 0.0 +.TP +.B \-u, \-\-upgrade +Perform HTTP Upgrade for HTTP/2. This option is ignored +if the request URI has https scheme. If \fI\%\-d\fP is used, the +HTTP upgrade request is performed with OPTIONS method. +.UNINDENT +.INDENT 0.0 +.TP +.B \-p, \-\-weight= +Sets priority group weight. The valid value range is +[1, 256], inclusive. +.sp +Default: \fB16\fP +.UNINDENT +.INDENT 0.0 +.TP +.B \-M, \-\-peer\-max\-concurrent\-streams= +Use as SETTINGS_MAX_CONCURRENT_STREAMS value of +remote endpoint as if it is received in SETTINGS frame. +The default is large enough as it is seen as unlimited. +.UNINDENT +.INDENT 0.0 +.TP +.B \-c, \-\-header\-table\-size= +Specify decoder header table size. +.UNINDENT +.INDENT 0.0 +.TP +.B \-b, \-\-padding= +Add at most bytes to a frame payload as padding. +Specify 0 to disable padding. +.UNINDENT +.INDENT 0.0 +.TP +.B \-r, \-\-har= +Output HTTP transactions in HAR format. If \(aq\-\(aq +is given, data is written to stdout. +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-color +Force colored log output. +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-continuation +Send large header to test CONTINUATION. +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-no\-content\-length +Don\(aqt send content\-length header field. +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-no\-dep +Don\(aqt send dependency based priority hint to server. +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-hexdump +Display the incoming traffic in hexadecimal (Canonical +hex+ASCII display). If SSL/TLS is used, decrypted data +are used. +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-no\-push +Disable server push. +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-version +Display version information and exit. +.UNINDENT +.INDENT 0.0 +.TP +.B \-h, \-\-help +Display this help and exit. +.UNINDENT +.sp +The argument is an integer and an optional unit (e.g., 10K is +10 * 1024). Units are K, M and G (powers of 1024). +.sp +The argument is an integer and an optional unit (e.g., 1s +is 1 second and 500ms is 500 milliseconds). Units are h, m, s or ms +(hours, minutes, seconds and milliseconds, respectively). If a unit +is omitted, a second is used as unit. +.SH DEPENDENCY BASED PRIORITY +.sp +nghttp sends priority hints to server by default unless +\fI\%\-\-no\-dep\fP is used. nghttp mimics the way Firefox employs to +manages dependency using idle streams. We follows the behaviour of +Firefox Nightly as of April, 2015, and nghttp\(aqs behaviour is very +static and could be different from Firefox in detail. But reproducing +the same behaviour of Firefox is not our goal. The goal is provide +the easy way to test out the dependency priority in server +implementation. +.sp +When connection is established, nghttp sends 5 PRIORITY frames to idle +streams 3, 5, 7, 9 and 11 to create "anchor" nodes in dependency +tree: +.INDENT 0.0 +.INDENT 3.5 +.sp +.nf +.ft C + +\-\-\-\-\-+ + |id=0 | + +\-\-\-\-\-+ + ^ ^ ^ + w=201 / | \e w=1 + / | \e + / w=101| \e + +\-\-\-\-\-+ +\-\-\-\-\-+ +\-\-\-\-\-+ + |id=3 | |id=5 | |id=7 | + +\-\-\-\-\-+ +\-\-\-\-\-+ +\-\-\-\-\-+ + ^ ^ +w=1 | w=1 | + | | + +\-\-\-\-\-+ +\-\-\-\-\-+ + |id=11| |id=9 | + +\-\-\-\-\-+ +\-\-\-\-\-+ +.ft P +.fi +.UNINDENT +.UNINDENT +.sp +In the above figure, \fBid\fP means stream ID, and \fBw\fP means weight. +The stream 0 is non\-existence stream, and forms the root of the tree. +The stream 7 and 9 are not used for now. +.sp +The URIs given in the command\-line depend on stream 11 with the weight +given in \fI\%\-p\fP option, which defaults to 16. +.sp +If \fI\%\-a\fP option is used, nghttp parses the resource pointed by +URI given in command\-line as html, and extracts resource links from +it. When requesting those resources, nghttp uses dependency according +to its resource type. +.sp +For CSS, and Javascript files inside "head" element, they depend on +stream 3 with the weight 2. The Javascript files outside "head" +element depend on stream 5 with the weight 2. The mages depend on +stream 11 with the weight 12. The other resources (e.g., icon) depend +on stream 11 with the weight 2. +.SH SEE ALSO +.sp +\fInghttpd(1)\fP, \fInghttpx(1)\fP, \fIh2load(1)\fP +.SH AUTHOR +Tatsuhiro Tsujikawa +.SH COPYRIGHT +2012, 2015, Tatsuhiro Tsujikawa +.\" Generated by docutils manpage writer. +. diff --git a/doc/nghttp.1.rst b/doc/nghttp.1.rst new file mode 100644 index 0000000..7f2449b --- /dev/null +++ b/doc/nghttp.1.rst @@ -0,0 +1,233 @@ + +.. GENERATED by help2rst.py. DO NOT EDIT DIRECTLY. + +.. program:: nghttp + +nghttp(1) +========= + +SYNOPSIS +-------- + +**nghttp** [OPTIONS]... ... + +DESCRIPTION +----------- + +HTTP/2 experimental client + +.. describe:: + + Specify URI to access. + +OPTIONS +------- + +.. option:: -v, --verbose + + Print debug information such as reception and + transmission of frames and name/value pairs. Specifying + this option multiple times increases verbosity. + +.. option:: -n, --null-out + + Discard downloaded data. + +.. option:: -O, --remote-name + + Save download data in the current directory. The + filename is dereived from URI. If URI ends with '*/*', + 'index.html' is used as a filename. Not implemented + yet. + +.. option:: -t, --timeout= + + Timeout each request after . Set 0 to disable + timeout. + +.. option:: -w, --window-bits= + + Sets the stream level initial window size to 2\*\*-1. + +.. option:: -W, --connection-window-bits= + + Sets the connection level initial window size to + 2\*\*-1. + +.. option:: -a, --get-assets + + Download assets such as stylesheets, images and script + files linked from the downloaded resource. Only links + whose origins are the same with the linking resource + will be downloaded. nghttp prioritizes resources using + HTTP/2 dependency based priority. The priority order, + from highest to lowest, is html itself, css, javascript + and images. + +.. option:: -s, --stat + + Print statistics. + +.. option:: -H, --header=
        + + Add a header to the requests. Example: :option:`-H`\':method: PUT' + +.. option:: --trailer=
        + + Add a trailer header to the requests.
        must not + include pseudo header field (header field name starting + with ':'). To send trailer, one must use :option:`-d` option to + send request body. Example: :option:`--trailer` 'foo: bar'. + +.. option:: --cert= + + Use the specified client certificate file. The file + must be in PEM format. + +.. option:: --key= + + Use the client private key file. The file must be in + PEM format. + +.. option:: -d, --data= + + Post FILE to server. If '-' is given, data will be read + from stdin. + +.. option:: -m, --multiply= + + Request each URI times. By default, same URI is not + requested twice. This option disables it too. + +.. option:: -u, --upgrade + + Perform HTTP Upgrade for HTTP/2. This option is ignored + if the request URI has https scheme. If :option:`-d` is used, the + HTTP upgrade request is performed with OPTIONS method. + +.. option:: -p, --weight= + + Sets priority group weight. The valid value range is + [1, 256], inclusive. + + Default: ``16`` + +.. option:: -M, --peer-max-concurrent-streams= + + Use as SETTINGS_MAX_CONCURRENT_STREAMS value of + remote endpoint as if it is received in SETTINGS frame. + The default is large enough as it is seen as unlimited. + +.. option:: -c, --header-table-size= + + Specify decoder header table size. + +.. option:: -b, --padding= + + Add at most bytes to a frame payload as padding. + Specify 0 to disable padding. + +.. option:: -r, --har= + + Output HTTP transactions in HAR format. If '-' + is given, data is written to stdout. + +.. option:: --color + + Force colored log output. + +.. option:: --continuation + + Send large header to test CONTINUATION. + +.. option:: --no-content-length + + Don't send content-length header field. + +.. option:: --no-dep + + Don't send dependency based priority hint to server. + +.. option:: --hexdump + + Display the incoming traffic in hexadecimal (Canonical + hex+ASCII display). If SSL/TLS is used, decrypted data + are used. + +.. option:: --no-push + + Disable server push. + +.. option:: --version + + Display version information and exit. + +.. option:: -h, --help + + Display this help and exit. + + + +The argument is an integer and an optional unit (e.g., 10K is +10 * 1024). Units are K, M and G (powers of 1024). + +The argument is an integer and an optional unit (e.g., 1s +is 1 second and 500ms is 500 milliseconds). Units are h, m, s or ms +(hours, minutes, seconds and milliseconds, respectively). If a unit +is omitted, a second is used as unit. + +DEPENDENCY BASED PRIORITY +------------------------- + +nghttp sends priority hints to server by default unless +:option:`--no-dep` is used. nghttp mimics the way Firefox employs to +manages dependency using idle streams. We follows the behaviour of +Firefox Nightly as of April, 2015, and nghttp's behaviour is very +static and could be different from Firefox in detail. But reproducing +the same behaviour of Firefox is not our goal. The goal is provide +the easy way to test out the dependency priority in server +implementation. + +When connection is established, nghttp sends 5 PRIORITY frames to idle +streams 3, 5, 7, 9 and 11 to create "anchor" nodes in dependency +tree:: + + +-----+ + |id=0 | + +-----+ + ^ ^ ^ + w=201 / | \ w=1 + / | \ + / w=101| \ + +-----+ +-----+ +-----+ + |id=3 | |id=5 | |id=7 | + +-----+ +-----+ +-----+ + ^ ^ + w=1 | w=1 | + | | + +-----+ +-----+ + |id=11| |id=9 | + +-----+ +-----+ + +In the above figure, ``id`` means stream ID, and ``w`` means weight. +The stream 0 is non-existence stream, and forms the root of the tree. +The stream 7 and 9 are not used for now. + +The URIs given in the command-line depend on stream 11 with the weight +given in :option:`-p` option, which defaults to 16. + +If :option:`-a` option is used, nghttp parses the resource pointed by +URI given in command-line as html, and extracts resource links from +it. When requesting those resources, nghttp uses dependency according +to its resource type. + +For CSS, and Javascript files inside "head" element, they depend on +stream 3 with the weight 2. The Javascript files outside "head" +element depend on stream 5 with the weight 2. The mages depend on +stream 11 with the weight 12. The other resources (e.g., icon) depend +on stream 11 with the weight 2. + +SEE ALSO +-------- + +:manpage:`nghttpd(1)`, :manpage:`nghttpx(1)`, :manpage:`h2load(1)` diff --git a/doc/nghttp2.h.rst.in b/doc/nghttp2.h.rst.in new file mode 100644 index 0000000..29e641d --- /dev/null +++ b/doc/nghttp2.h.rst.in @@ -0,0 +1,4 @@ +nghttp2.h +========= + +.. literalinclude:: @top_srcdir@/lib/includes/nghttp2/nghttp2.h diff --git a/doc/nghttp2_check_header_name.rst b/doc/nghttp2_check_header_name.rst new file mode 100644 index 0000000..2fba2c5 --- /dev/null +++ b/doc/nghttp2_check_header_name.rst @@ -0,0 +1,17 @@ + +nghttp2_check_header_name +========================= + +Synopsis +-------- + +*#include * + +.. function:: int nghttp2_check_header_name(const uint8_t *name, size_t len) + + + Returns nonzero if HTTP header field name *name* of length *len* is + valid according to http://tools.ietf.org/html/rfc7230#section-3.2 + + Because this is a header field name in HTTP2, the upper cased alphabet + is treated as error. diff --git a/doc/nghttp2_check_header_value.rst b/doc/nghttp2_check_header_value.rst new file mode 100644 index 0000000..e6f792e --- /dev/null +++ b/doc/nghttp2_check_header_value.rst @@ -0,0 +1,15 @@ + +nghttp2_check_header_value +========================== + +Synopsis +-------- + +*#include * + +.. function:: int nghttp2_check_header_value(const uint8_t *value, size_t len) + + + Returns nonzero if HTTP header field value *value* of length *len* + is valid according to + http://tools.ietf.org/html/rfc7230#section-3.2 diff --git a/doc/nghttp2_hd_deflate_bound.rst b/doc/nghttp2_hd_deflate_bound.rst new file mode 100644 index 0000000..27eaed3 --- /dev/null +++ b/doc/nghttp2_hd_deflate_bound.rst @@ -0,0 +1,14 @@ + +nghttp2_hd_deflate_bound +======================== + +Synopsis +-------- + +*#include * + +.. function:: size_t nghttp2_hd_deflate_bound(nghttp2_hd_deflater *deflater, const nghttp2_nv *nva, size_t nvlen) + + + Returns an upper bound on the compressed size after deflation of + *nva* of length *nvlen*. diff --git a/doc/nghttp2_hd_deflate_change_table_size.rst b/doc/nghttp2_hd_deflate_change_table_size.rst new file mode 100644 index 0000000..d0bec79 --- /dev/null +++ b/doc/nghttp2_hd_deflate_change_table_size.rst @@ -0,0 +1,31 @@ + +nghttp2_hd_deflate_change_table_size +==================================== + +Synopsis +-------- + +*#include * + +.. function:: int nghttp2_hd_deflate_change_table_size(nghttp2_hd_deflater *deflater, size_t settings_hd_table_bufsize_max) + + + Changes header table size of the *deflater* to + *settings_hd_table_bufsize_max* bytes. This may trigger eviction + in the dynamic table. + + The *settings_hd_table_bufsize_max* should be the value received in + SETTINGS_HEADER_TABLE_SIZE. + + The deflater never uses more memory than + ``deflate_hd_table_bufsize_max`` bytes specified in + `nghttp2_hd_deflate_new()`. Therefore, if + *settings_hd_table_bufsize_max* > ``deflate_hd_table_bufsize_max``, + resulting maximum table size becomes + ``deflate_hd_table_bufsize_max``. + + This function returns 0 if it succeeds, or one of the following + negative error codes: + + :macro:`NGHTTP2_ERR_NOMEM` + Out of memory. diff --git a/doc/nghttp2_hd_deflate_del.rst b/doc/nghttp2_hd_deflate_del.rst new file mode 100644 index 0000000..ea81a35 --- /dev/null +++ b/doc/nghttp2_hd_deflate_del.rst @@ -0,0 +1,13 @@ + +nghttp2_hd_deflate_del +====================== + +Synopsis +-------- + +*#include * + +.. function:: void nghttp2_hd_deflate_del(nghttp2_hd_deflater *deflater) + + + Deallocates any resources allocated for *deflater*. diff --git a/doc/nghttp2_hd_deflate_hd.rst b/doc/nghttp2_hd_deflate_hd.rst new file mode 100644 index 0000000..4c80bfc --- /dev/null +++ b/doc/nghttp2_hd_deflate_hd.rst @@ -0,0 +1,35 @@ + +nghttp2_hd_deflate_hd +===================== + +Synopsis +-------- + +*#include * + +.. function:: ssize_t nghttp2_hd_deflate_hd(nghttp2_hd_deflater *deflater, uint8_t *buf, size_t buflen, const nghttp2_nv *nva, size_t nvlen) + + + Deflates the *nva*, which has the *nvlen* name/value pairs, into + the *buf* of length *buflen*. + + If *buf* is not large enough to store the deflated header block, + this function fails with :macro:`NGHTTP2_ERR_INSUFF_BUFSIZE`. The + caller should use `nghttp2_hd_deflate_bound()` to know the upper + bound of buffer size required to deflate given header name/value + pairs. + + Once this function fails, subsequent call of this function always + returns :macro:`NGHTTP2_ERR_HEADER_COMP`. + + After this function returns, it is safe to delete the *nva*. + + This function returns 0 if it succeeds, or one of the following + negative error codes: + + :macro:`NGHTTP2_ERR_NOMEM` + Out of memory. + :macro:`NGHTTP2_ERR_HEADER_COMP` + Deflation process has failed. + :macro:`NGHTTP2_ERR_INSUFF_BUFSIZE` + The provided *buflen* size is too small to hold the output. diff --git a/doc/nghttp2_hd_deflate_new.rst b/doc/nghttp2_hd_deflate_new.rst new file mode 100644 index 0000000..146113f --- /dev/null +++ b/doc/nghttp2_hd_deflate_new.rst @@ -0,0 +1,24 @@ + +nghttp2_hd_deflate_new +====================== + +Synopsis +-------- + +*#include * + +.. function:: int nghttp2_hd_deflate_new(nghttp2_hd_deflater **deflater_ptr, size_t deflate_hd_table_bufsize_max) + + + Initializes *\*deflater_ptr* for deflating name/values pairs. + + The *deflate_hd_table_bufsize_max* is the upper bound of header + table size the deflater will use. + + If this function fails, *\*deflater_ptr* is left untouched. + + This function returns 0 if it succeeds, or one of the following + negative error codes: + + :macro:`NGHTTP2_ERR_NOMEM` + Out of memory. diff --git a/doc/nghttp2_hd_deflate_new2.rst b/doc/nghttp2_hd_deflate_new2.rst new file mode 100644 index 0000000..f128f3e --- /dev/null +++ b/doc/nghttp2_hd_deflate_new2.rst @@ -0,0 +1,23 @@ + +nghttp2_hd_deflate_new2 +======================= + +Synopsis +-------- + +*#include * + +.. function:: int nghttp2_hd_deflate_new2(nghttp2_hd_deflater **deflater_ptr, size_t deflate_hd_table_bufsize_max, nghttp2_mem *mem) + + + Like `nghttp2_hd_deflate_new()`, but with additional custom memory + allocator specified in the *mem*. + + The *mem* can be ``NULL`` and the call is equivalent to + `nghttp2_hd_deflate_new()`. + + This function does not take ownership *mem*. The application is + responsible for freeing *mem*. + + The library code does not refer to *mem* pointer after this + function returns, so the application can safely free it. diff --git a/doc/nghttp2_hd_inflate_change_table_size.rst b/doc/nghttp2_hd_inflate_change_table_size.rst new file mode 100644 index 0000000..e859207 --- /dev/null +++ b/doc/nghttp2_hd_inflate_change_table_size.rst @@ -0,0 +1,23 @@ + +nghttp2_hd_inflate_change_table_size +==================================== + +Synopsis +-------- + +*#include * + +.. function:: int nghttp2_hd_inflate_change_table_size(nghttp2_hd_inflater *inflater, size_t settings_hd_table_bufsize_max) + + + Changes header table size in the *inflater*. This may trigger + eviction in the dynamic table. + + The *settings_hd_table_bufsize_max* should be the value transmitted + in SETTINGS_HEADER_TABLE_SIZE. + + This function returns 0 if it succeeds, or one of the following + negative error codes: + + :macro:`NGHTTP2_ERR_NOMEM` + Out of memory. diff --git a/doc/nghttp2_hd_inflate_del.rst b/doc/nghttp2_hd_inflate_del.rst new file mode 100644 index 0000000..bdf51bb --- /dev/null +++ b/doc/nghttp2_hd_inflate_del.rst @@ -0,0 +1,13 @@ + +nghttp2_hd_inflate_del +====================== + +Synopsis +-------- + +*#include * + +.. function:: void nghttp2_hd_inflate_del(nghttp2_hd_inflater *inflater) + + + Deallocates any resources allocated for *inflater*. diff --git a/doc/nghttp2_hd_inflate_end_headers.rst b/doc/nghttp2_hd_inflate_end_headers.rst new file mode 100644 index 0000000..63ea728 --- /dev/null +++ b/doc/nghttp2_hd_inflate_end_headers.rst @@ -0,0 +1,16 @@ + +nghttp2_hd_inflate_end_headers +============================== + +Synopsis +-------- + +*#include * + +.. function:: int nghttp2_hd_inflate_end_headers(nghttp2_hd_inflater *inflater) + + + Signals the end of decompression for one header block. + + This function returns 0 if it succeeds. Currently this function + always succeeds. diff --git a/doc/nghttp2_hd_inflate_hd.rst b/doc/nghttp2_hd_inflate_hd.rst new file mode 100644 index 0000000..216cdfe --- /dev/null +++ b/doc/nghttp2_hd_inflate_hd.rst @@ -0,0 +1,85 @@ + +nghttp2_hd_inflate_hd +===================== + +Synopsis +-------- + +*#include * + +.. function:: ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_inflater *inflater, nghttp2_nv *nv_out, int *inflate_flags, uint8_t *in, size_t inlen, int in_final) + + + Inflates name/value block stored in *in* with length *inlen*. This + function performs decompression. For each successful emission of + header name/value pair, :macro:`NGHTTP2_HD_INFLATE_EMIT` is set in + *\*inflate_flags* and name/value pair is assigned to the *nv_out* + and the function returns. The caller must not free the members of + *nv_out*. + + The *nv_out* may include pointers to the memory region in the *in*. + The caller must retain the *in* while the *nv_out* is used. + + The application should call this function repeatedly until the + ``(*inflate_flags) & NGHTTP2_HD_INFLATE_FINAL`` is nonzero and + return value is non-negative. This means the all input values are + processed successfully. Then the application must call + `nghttp2_hd_inflate_end_headers()` to prepare for the next header + block input. + + The caller can feed complete compressed header block. It also can + feed it in several chunks. The caller must set *in_final* to + nonzero if the given input is the last block of the compressed + header. + + This function returns the number of bytes processed if it succeeds, + or one of the following negative error codes: + + :macro:`NGHTTP2_ERR_NOMEM` + Out of memory. + :macro:`NGHTTP2_ERR_HEADER_COMP` + Inflation process has failed. + :macro:`NGHTTP2_ERR_BUFFER_ERROR` + The heder field name or value is too large. + + Example follows:: + + int inflate_header_block(nghttp2_hd_inflater *hd_inflater, + uint8_t *in, size_t inlen, int final) + { + ssize_t rv; + + for(;;) { + nghttp2_nv nv; + int inflate_flags = 0; + + rv = nghttp2_hd_inflate_hd(hd_inflater, &nv, &inflate_flags, + in, inlen, final); + + if(rv < 0) { + fprintf(stderr, "inflate failed with error code %zd", rv); + return -1; + } + + in += rv; + inlen -= rv; + + if(inflate_flags & NGHTTP2_HD_INFLATE_EMIT) { + fwrite(nv.name, nv.namelen, 1, stderr); + fprintf(stderr, ": "); + fwrite(nv.value, nv.valuelen, 1, stderr); + fprintf(stderr, "\n"); + } + if(inflate_flags & NGHTTP2_HD_INFLATE_FINAL) { + nghttp2_hd_inflate_end_headers(hd_inflater); + break; + } + if((inflate_flags & NGHTTP2_HD_INFLATE_EMIT) == 0 && + inlen == 0) { + break; + } + } + + return 0; + } + diff --git a/doc/nghttp2_hd_inflate_new.rst b/doc/nghttp2_hd_inflate_new.rst new file mode 100644 index 0000000..cfd2212 --- /dev/null +++ b/doc/nghttp2_hd_inflate_new.rst @@ -0,0 +1,21 @@ + +nghttp2_hd_inflate_new +====================== + +Synopsis +-------- + +*#include * + +.. function:: int nghttp2_hd_inflate_new(nghttp2_hd_inflater **inflater_ptr) + + + Initializes *\*inflater_ptr* for inflating name/values pairs. + + If this function fails, *\*inflater_ptr* is left untouched. + + This function returns 0 if it succeeds, or one of the following + negative error codes: + + :macro:`NGHTTP2_ERR_NOMEM` + Out of memory. diff --git a/doc/nghttp2_hd_inflate_new2.rst b/doc/nghttp2_hd_inflate_new2.rst new file mode 100644 index 0000000..f052019 --- /dev/null +++ b/doc/nghttp2_hd_inflate_new2.rst @@ -0,0 +1,23 @@ + +nghttp2_hd_inflate_new2 +======================= + +Synopsis +-------- + +*#include * + +.. function:: int nghttp2_hd_inflate_new2(nghttp2_hd_inflater **inflater_ptr, nghttp2_mem *mem) + + + Like `nghttp2_hd_inflate_new()`, but with additional custom memory + allocator specified in the *mem*. + + The *mem* can be ``NULL`` and the call is equivalent to + `nghttp2_hd_inflate_new()`. + + This function does not take ownership *mem*. The application is + responsible for freeing *mem*. + + The library code does not refer to *mem* pointer after this + function returns, so the application can safely free it. diff --git a/doc/nghttp2_is_fatal.rst b/doc/nghttp2_is_fatal.rst new file mode 100644 index 0000000..b548219 --- /dev/null +++ b/doc/nghttp2_is_fatal.rst @@ -0,0 +1,14 @@ + +nghttp2_is_fatal +================ + +Synopsis +-------- + +*#include * + +.. function:: int nghttp2_is_fatal(int lib_error_code) + + + Returns nonzero if the :type:`nghttp2_error` library error code + *lib_error* is fatal. diff --git a/doc/nghttp2_nv_compare_name.rst b/doc/nghttp2_nv_compare_name.rst new file mode 100644 index 0000000..9fa4c1b --- /dev/null +++ b/doc/nghttp2_nv_compare_name.rst @@ -0,0 +1,17 @@ + +nghttp2_nv_compare_name +======================= + +Synopsis +-------- + +*#include * + +.. function:: int nghttp2_nv_compare_name(const nghttp2_nv *lhs, const nghttp2_nv *rhs) + + + Compares ``lhs->name`` of length ``lhs->namelen`` bytes and + ``rhs->name`` of length ``rhs->namelen`` bytes. Returns negative + integer if ``lhs->name`` is found to be less than ``rhs->name``; or + returns positive integer if ``lhs->name`` is found to be greater + than ``rhs->name``; or returns 0 otherwise. diff --git a/doc/nghttp2_option_del.rst b/doc/nghttp2_option_del.rst new file mode 100644 index 0000000..b816b71 --- /dev/null +++ b/doc/nghttp2_option_del.rst @@ -0,0 +1,14 @@ + +nghttp2_option_del +================== + +Synopsis +-------- + +*#include * + +.. function:: void nghttp2_option_del(nghttp2_option *option) + + + Frees any resources allocated for *option*. If *option* is + ``NULL``, this function does nothing. diff --git a/doc/nghttp2_option_new.rst b/doc/nghttp2_option_new.rst new file mode 100644 index 0000000..4dcfb64 --- /dev/null +++ b/doc/nghttp2_option_new.rst @@ -0,0 +1,22 @@ + +nghttp2_option_new +================== + +Synopsis +-------- + +*#include * + +.. function:: int nghttp2_option_new(nghttp2_option **option_ptr) + + + Initializes *\*option_ptr* with default values. + + When the application finished using this object, it can use + `nghttp2_option_del()` to free its memory. + + This function returns 0 if it succeeds, or one of the following + negative error codes: + + :macro:`NGHTTP2_ERR_NOMEM` + Out of memory. diff --git a/doc/nghttp2_option_set_no_auto_window_update.rst b/doc/nghttp2_option_set_no_auto_window_update.rst new file mode 100644 index 0000000..737b6ce --- /dev/null +++ b/doc/nghttp2_option_set_no_auto_window_update.rst @@ -0,0 +1,18 @@ + +nghttp2_option_set_no_auto_window_update +======================================== + +Synopsis +-------- + +*#include * + +.. function:: void nghttp2_option_set_no_auto_window_update(nghttp2_option *option, int val) + + + This option prevents the library from sending WINDOW_UPDATE for a + connection automatically. If this option is set to nonzero, the + library won't send WINDOW_UPDATE for DATA until application calls + `nghttp2_session_consume()` to indicate the consumed amount of + data. Don't use `nghttp2_submit_window_update()` for this purpose. + By default, this option is set to zero. diff --git a/doc/nghttp2_option_set_no_http_messaging.rst b/doc/nghttp2_option_set_no_http_messaging.rst new file mode 100644 index 0000000..602fe3b --- /dev/null +++ b/doc/nghttp2_option_set_no_http_messaging.rst @@ -0,0 +1,18 @@ + +nghttp2_option_set_no_http_messaging +==================================== + +Synopsis +-------- + +*#include * + +.. function:: void nghttp2_option_set_no_http_messaging(nghttp2_option *option, int val) + + + By default, nghttp2 library enforces subset of HTTP Messaging rules + described in `HTTP/2 specification, section 8 + `_. See + :ref:`http-messaging` section for details. For those applications + who use nghttp2 library as non-HTTP use, give nonzero to *val* to + disable this enforcement. diff --git a/doc/nghttp2_option_set_no_recv_client_magic.rst b/doc/nghttp2_option_set_no_recv_client_magic.rst new file mode 100644 index 0000000..e45fe54 --- /dev/null +++ b/doc/nghttp2_option_set_no_recv_client_magic.rst @@ -0,0 +1,26 @@ + +nghttp2_option_set_no_recv_client_magic +======================================= + +Synopsis +-------- + +*#include * + +.. function:: void nghttp2_option_set_no_recv_client_magic(nghttp2_option *option, int val) + + + By default, nghttp2 library, if configured as server, requires + first 24 bytes of client magic byte string (MAGIC). In most cases, + this will simplify the implementation of server. But sometimes + server may want to detect the application protocol based on first + few bytes on clear text communication. + + If this option is used with nonzero *val*, nghttp2 library does not + handle MAGIC. It still checks following SETTINGS frame. This + means that applications should deal with MAGIC by themselves. + + If this option is not used or used with zero value, if MAGIC does + not match :macro:`NGHTTP2_CLIENT_MAGIC`, `nghttp2_session_recv()` + and `nghttp2_session_mem_recv()` will return error + :macro:`NGHTTP2_ERR_BAD_CLIENT_MAGIC`, which is fatal error. diff --git a/doc/nghttp2_option_set_peer_max_concurrent_streams.rst b/doc/nghttp2_option_set_peer_max_concurrent_streams.rst new file mode 100644 index 0000000..b15fae3 --- /dev/null +++ b/doc/nghttp2_option_set_peer_max_concurrent_streams.rst @@ -0,0 +1,23 @@ + +nghttp2_option_set_peer_max_concurrent_streams +============================================== + +Synopsis +-------- + +*#include * + +.. function:: void nghttp2_option_set_peer_max_concurrent_streams(nghttp2_option *option, uint32_t val) + + + This option sets the SETTINGS_MAX_CONCURRENT_STREAMS value of + remote endpoint as if it is received in SETTINGS frame. Without + specifying this option, before the local endpoint receives + SETTINGS_MAX_CONCURRENT_STREAMS in SETTINGS frame from remote + endpoint, SETTINGS_MAX_CONCURRENT_STREAMS is unlimited. This may + cause problem if local endpoint submits lots of requests initially + and sending them at once to the remote peer may lead to the + rejection of some requests. Specifying this option to the sensible + value, say 100, may avoid this kind of issue. This value will be + overwritten if the local endpoint receives + SETTINGS_MAX_CONCURRENT_STREAMS from the remote endpoint. diff --git a/doc/nghttp2_pack_settings_payload.rst b/doc/nghttp2_pack_settings_payload.rst new file mode 100644 index 0000000..a4ce9fd --- /dev/null +++ b/doc/nghttp2_pack_settings_payload.rst @@ -0,0 +1,29 @@ + +nghttp2_pack_settings_payload +============================= + +Synopsis +-------- + +*#include * + +.. function:: ssize_t nghttp2_pack_settings_payload(uint8_t *buf, size_t buflen, const nghttp2_settings_entry *iv, size_t niv) + + + Serializes the SETTINGS values *iv* in the *buf*. The size of the + *buf* is specified by *buflen*. The number of entries in the *iv* + array is given by *niv*. The required space in *buf* for the *niv* + entries is ``8*niv`` bytes and if the given buffer is too small, an + error is returned. This function is used mainly for creating a + SETTINGS payload to be sent with the ``HTTP2-Settings`` header + field in an HTTP Upgrade request. The data written in *buf* is NOT + base64url encoded and the application is responsible for encoding. + + This function returns the number of bytes written in *buf*, or one + of the following negative error codes: + + :macro:`NGHTTP2_ERR_INVALID_ARGUMENT` + The *iv* contains duplicate settings ID or invalid value. + + :macro:`NGHTTP2_ERR_INSUFF_BUFSIZE` + The provided *buflen* size is too small to hold the output. diff --git a/doc/nghttp2_priority_spec_check_default.rst b/doc/nghttp2_priority_spec_check_default.rst new file mode 100644 index 0000000..12a2765 --- /dev/null +++ b/doc/nghttp2_priority_spec_check_default.rst @@ -0,0 +1,13 @@ + +nghttp2_priority_spec_check_default +=================================== + +Synopsis +-------- + +*#include * + +.. function:: int nghttp2_priority_spec_check_default(const nghttp2_priority_spec *pri_spec) + + + Returns nonzero if the *pri_spec* is filled with default values. diff --git a/doc/nghttp2_priority_spec_default_init.rst b/doc/nghttp2_priority_spec_default_init.rst new file mode 100644 index 0000000..66f03a3 --- /dev/null +++ b/doc/nghttp2_priority_spec_default_init.rst @@ -0,0 +1,15 @@ + +nghttp2_priority_spec_default_init +================================== + +Synopsis +-------- + +*#include * + +.. function:: void nghttp2_priority_spec_default_init(nghttp2_priority_spec *pri_spec) + + + Initializes *pri_spec* with the default values. The default values + are: stream_id = 0, weight = :macro:`NGHTTP2_DEFAULT_WEIGHT` and + exclusive = 0. diff --git a/doc/nghttp2_priority_spec_init.rst b/doc/nghttp2_priority_spec_init.rst new file mode 100644 index 0000000..e6d3757 --- /dev/null +++ b/doc/nghttp2_priority_spec_init.rst @@ -0,0 +1,18 @@ + +nghttp2_priority_spec_init +========================== + +Synopsis +-------- + +*#include * + +.. function:: void nghttp2_priority_spec_init(nghttp2_priority_spec *pri_spec, int32_t stream_id, int32_t weight, int exclusive) + + + Initializes *pri_spec* with the *stream_id* of the stream to depend + on with *weight* and its exclusive flag. If *exclusive* is + nonzero, exclusive flag is set. + + The *weight* must be in [:macro:`NGHTTP2_MIN_WEIGHT`, + :macro:`NGHTTP2_MAX_WEIGHT`], inclusive. diff --git a/doc/nghttp2_select_next_protocol.rst b/doc/nghttp2_select_next_protocol.rst new file mode 100644 index 0000000..5ace810 --- /dev/null +++ b/doc/nghttp2_select_next_protocol.rst @@ -0,0 +1,66 @@ + +nghttp2_select_next_protocol +============================ + +Synopsis +-------- + +*#include * + +.. function:: int nghttp2_select_next_protocol(unsigned char **out, unsigned char *outlen, const unsigned char *in, unsigned int inlen) + + + A helper function for dealing with NPN in client side or ALPN in + server side. The *in* contains peer's protocol list in preferable + order. The format of *in* is length-prefixed and not + null-terminated. For example, ``h2`` and + ``http/1.1`` stored in *in* like this:: + + in[0] = 2 + in[1..2] = "h2" + in[3] = 8 + in[4..11] = "http/1.1" + inlen = 12 + + The selection algorithm is as follows: + + 1. If peer's list contains HTTP/2 protocol the library supports, + it is selected and returns 1. The following step is not taken. + + 2. If peer's list contains ``http/1.1``, this function selects + ``http/1.1`` and returns 0. The following step is not taken. + + 3. This function selects nothing and returns -1 (So called + non-overlap case). In this case, *out* and *outlen* are left + untouched. + + Selecting ``h2`` means that ``h2`` is written into *\*out* and its + length (which is 2) is assigned to *\*outlen*. + + For ALPN, refer to https://tools.ietf.org/html/rfc7301 + + See http://technotes.googlecode.com/git/nextprotoneg.html for more + details about NPN. + + For NPN, to use this method you should do something like:: + + static int select_next_proto_cb(SSL* ssl, + unsigned char **out, + unsigned char *outlen, + const unsigned char *in, + unsigned int inlen, + void *arg) + { + int rv; + rv = nghttp2_select_next_protocol(out, outlen, in, inlen); + if (rv == -1) { + return SSL_TLSEXT_ERR_NOACK; + } + if (rv == 1) { + ((MyType*)arg)->http2_selected = 1; + } + return SSL_TLSEXT_ERR_OK; + } + ... + SSL_CTX_set_next_proto_select_cb(ssl_ctx, select_next_proto_cb, my_obj); + diff --git a/doc/nghttp2_session_callbacks_del.rst b/doc/nghttp2_session_callbacks_del.rst new file mode 100644 index 0000000..2372355 --- /dev/null +++ b/doc/nghttp2_session_callbacks_del.rst @@ -0,0 +1,14 @@ + +nghttp2_session_callbacks_del +============================= + +Synopsis +-------- + +*#include * + +.. function:: void nghttp2_session_callbacks_del(nghttp2_session_callbacks *callbacks) + + + Frees any resources allocated for *callbacks*. If *callbacks* is + ``NULL``, this function does nothing. diff --git a/doc/nghttp2_session_callbacks_new.rst b/doc/nghttp2_session_callbacks_new.rst new file mode 100644 index 0000000..809db59 --- /dev/null +++ b/doc/nghttp2_session_callbacks_new.rst @@ -0,0 +1,25 @@ + +nghttp2_session_callbacks_new +============================= + +Synopsis +-------- + +*#include * + +.. function:: int nghttp2_session_callbacks_new(nghttp2_session_callbacks **callbacks_ptr) + + + Initializes *\*callbacks_ptr* with NULL values. + + The initialized object can be used when initializing multiple + :type:`nghttp2_session` objects. + + When the application finished using this object, it can use + `nghttp2_session_callbacks_del()` to free its memory. + + This function returns 0 if it succeeds, or one of the following + negative error codes: + + :macro:`NGHTTP2_ERR_NOMEM` + Out of memory. diff --git a/doc/nghttp2_session_callbacks_set_before_frame_send_callback.rst b/doc/nghttp2_session_callbacks_set_before_frame_send_callback.rst new file mode 100644 index 0000000..0f52c10 --- /dev/null +++ b/doc/nghttp2_session_callbacks_set_before_frame_send_callback.rst @@ -0,0 +1,13 @@ + +nghttp2_session_callbacks_set_before_frame_send_callback +======================================================== + +Synopsis +-------- + +*#include * + +.. function:: void nghttp2_session_callbacks_set_before_frame_send_callback( nghttp2_session_callbacks *cbs, nghttp2_before_frame_send_callback before_frame_send_callback) + + + Sets callback function invoked before a non-DATA frame is sent. diff --git a/doc/nghttp2_session_callbacks_set_data_source_read_length_callback.rst b/doc/nghttp2_session_callbacks_set_data_source_read_length_callback.rst new file mode 100644 index 0000000..e2819fa --- /dev/null +++ b/doc/nghttp2_session_callbacks_set_data_source_read_length_callback.rst @@ -0,0 +1,14 @@ + +nghttp2_session_callbacks_set_data_source_read_length_callback +============================================================== + +Synopsis +-------- + +*#include * + +.. function:: void nghttp2_session_callbacks_set_data_source_read_length_callback( nghttp2_session_callbacks *cbs, nghttp2_data_source_read_length_callback data_source_read_length_callback) + + + Sets callback function determine the length allowed in + :type:`nghttp2_data_source_read_callback`. diff --git a/doc/nghttp2_session_callbacks_set_on_begin_frame_callback.rst b/doc/nghttp2_session_callbacks_set_on_begin_frame_callback.rst new file mode 100644 index 0000000..95f0b72 --- /dev/null +++ b/doc/nghttp2_session_callbacks_set_on_begin_frame_callback.rst @@ -0,0 +1,13 @@ + +nghttp2_session_callbacks_set_on_begin_frame_callback +===================================================== + +Synopsis +-------- + +*#include * + +.. function:: void nghttp2_session_callbacks_set_on_begin_frame_callback( nghttp2_session_callbacks *cbs, nghttp2_on_begin_frame_callback on_begin_frame_callback) + + + Sets callback function invoked when a frame header is received. diff --git a/doc/nghttp2_session_callbacks_set_on_begin_headers_callback.rst b/doc/nghttp2_session_callbacks_set_on_begin_headers_callback.rst new file mode 100644 index 0000000..970b1fd --- /dev/null +++ b/doc/nghttp2_session_callbacks_set_on_begin_headers_callback.rst @@ -0,0 +1,14 @@ + +nghttp2_session_callbacks_set_on_begin_headers_callback +======================================================= + +Synopsis +-------- + +*#include * + +.. function:: void nghttp2_session_callbacks_set_on_begin_headers_callback( nghttp2_session_callbacks *cbs, nghttp2_on_begin_headers_callback on_begin_headers_callback) + + + Sets callback function invoked when the reception of header block + in HEADERS or PUSH_PROMISE is started. diff --git a/doc/nghttp2_session_callbacks_set_on_data_chunk_recv_callback.rst b/doc/nghttp2_session_callbacks_set_on_data_chunk_recv_callback.rst new file mode 100644 index 0000000..04fa3f6 --- /dev/null +++ b/doc/nghttp2_session_callbacks_set_on_data_chunk_recv_callback.rst @@ -0,0 +1,14 @@ + +nghttp2_session_callbacks_set_on_data_chunk_recv_callback +========================================================= + +Synopsis +-------- + +*#include * + +.. function:: void nghttp2_session_callbacks_set_on_data_chunk_recv_callback( nghttp2_session_callbacks *cbs, nghttp2_on_data_chunk_recv_callback on_data_chunk_recv_callback) + + + Sets callback function invoked when a chunk of data in DATA frame + is received. diff --git a/doc/nghttp2_session_callbacks_set_on_frame_not_send_callback.rst b/doc/nghttp2_session_callbacks_set_on_frame_not_send_callback.rst new file mode 100644 index 0000000..73e9fb1 --- /dev/null +++ b/doc/nghttp2_session_callbacks_set_on_frame_not_send_callback.rst @@ -0,0 +1,14 @@ + +nghttp2_session_callbacks_set_on_frame_not_send_callback +======================================================== + +Synopsis +-------- + +*#include * + +.. function:: void nghttp2_session_callbacks_set_on_frame_not_send_callback( nghttp2_session_callbacks *cbs, nghttp2_on_frame_not_send_callback on_frame_not_send_callback) + + + Sets callback function invoked when a non-DATA frame is not sent + because of an error. diff --git a/doc/nghttp2_session_callbacks_set_on_frame_recv_callback.rst b/doc/nghttp2_session_callbacks_set_on_frame_recv_callback.rst new file mode 100644 index 0000000..736ae53 --- /dev/null +++ b/doc/nghttp2_session_callbacks_set_on_frame_recv_callback.rst @@ -0,0 +1,14 @@ + +nghttp2_session_callbacks_set_on_frame_recv_callback +==================================================== + +Synopsis +-------- + +*#include * + +.. function:: void nghttp2_session_callbacks_set_on_frame_recv_callback( nghttp2_session_callbacks *cbs, nghttp2_on_frame_recv_callback on_frame_recv_callback) + + + Sets callback function invoked by `nghttp2_session_recv()` and + `nghttp2_session_mem_recv()` when a frame is received. diff --git a/doc/nghttp2_session_callbacks_set_on_frame_send_callback.rst b/doc/nghttp2_session_callbacks_set_on_frame_send_callback.rst new file mode 100644 index 0000000..7a02ee5 --- /dev/null +++ b/doc/nghttp2_session_callbacks_set_on_frame_send_callback.rst @@ -0,0 +1,13 @@ + +nghttp2_session_callbacks_set_on_frame_send_callback +==================================================== + +Synopsis +-------- + +*#include * + +.. function:: void nghttp2_session_callbacks_set_on_frame_send_callback( nghttp2_session_callbacks *cbs, nghttp2_on_frame_send_callback on_frame_send_callback) + + + Sets callback function invoked after a frame is sent. diff --git a/doc/nghttp2_session_callbacks_set_on_header_callback.rst b/doc/nghttp2_session_callbacks_set_on_header_callback.rst new file mode 100644 index 0000000..c843aeb --- /dev/null +++ b/doc/nghttp2_session_callbacks_set_on_header_callback.rst @@ -0,0 +1,14 @@ + +nghttp2_session_callbacks_set_on_header_callback +================================================ + +Synopsis +-------- + +*#include * + +.. function:: void nghttp2_session_callbacks_set_on_header_callback( nghttp2_session_callbacks *cbs, nghttp2_on_header_callback on_header_callback) + + + Sets callback function invoked when a header name/value pair is + received. diff --git a/doc/nghttp2_session_callbacks_set_on_invalid_frame_recv_callback.rst b/doc/nghttp2_session_callbacks_set_on_invalid_frame_recv_callback.rst new file mode 100644 index 0000000..97e4851 --- /dev/null +++ b/doc/nghttp2_session_callbacks_set_on_invalid_frame_recv_callback.rst @@ -0,0 +1,15 @@ + +nghttp2_session_callbacks_set_on_invalid_frame_recv_callback +============================================================ + +Synopsis +-------- + +*#include * + +.. function:: void nghttp2_session_callbacks_set_on_invalid_frame_recv_callback( nghttp2_session_callbacks *cbs, nghttp2_on_invalid_frame_recv_callback on_invalid_frame_recv_callback) + + + Sets callback function invoked by `nghttp2_session_recv()` and + `nghttp2_session_mem_recv()` when an invalid non-DATA frame is + received. diff --git a/doc/nghttp2_session_callbacks_set_on_stream_close_callback.rst b/doc/nghttp2_session_callbacks_set_on_stream_close_callback.rst new file mode 100644 index 0000000..9349a1c --- /dev/null +++ b/doc/nghttp2_session_callbacks_set_on_stream_close_callback.rst @@ -0,0 +1,13 @@ + +nghttp2_session_callbacks_set_on_stream_close_callback +====================================================== + +Synopsis +-------- + +*#include * + +.. function:: void nghttp2_session_callbacks_set_on_stream_close_callback( nghttp2_session_callbacks *cbs, nghttp2_on_stream_close_callback on_stream_close_callback) + + + Sets callback function invoked when the stream is closed. diff --git a/doc/nghttp2_session_callbacks_set_recv_callback.rst b/doc/nghttp2_session_callbacks_set_recv_callback.rst new file mode 100644 index 0000000..89b6788 --- /dev/null +++ b/doc/nghttp2_session_callbacks_set_recv_callback.rst @@ -0,0 +1,16 @@ + +nghttp2_session_callbacks_set_recv_callback +=========================================== + +Synopsis +-------- + +*#include * + +.. function:: void nghttp2_session_callbacks_set_recv_callback( nghttp2_session_callbacks *cbs, nghttp2_recv_callback recv_callback) + + + Sets callback function invoked when the a session wants to receive + data from the remote peer. This callback is not necessary if the + application uses solely `nghttp2_session_mem_recv()` to process + received data. diff --git a/doc/nghttp2_session_callbacks_set_select_padding_callback.rst b/doc/nghttp2_session_callbacks_set_select_padding_callback.rst new file mode 100644 index 0000000..17837c2 --- /dev/null +++ b/doc/nghttp2_session_callbacks_set_select_padding_callback.rst @@ -0,0 +1,15 @@ + +nghttp2_session_callbacks_set_select_padding_callback +===================================================== + +Synopsis +-------- + +*#include * + +.. function:: void nghttp2_session_callbacks_set_select_padding_callback( nghttp2_session_callbacks *cbs, nghttp2_select_padding_callback select_padding_callback) + + + Sets callback function invoked when the library asks application + how many padding bytes are required for the transmission of the + given frame. diff --git a/doc/nghttp2_session_callbacks_set_send_callback.rst b/doc/nghttp2_session_callbacks_set_send_callback.rst new file mode 100644 index 0000000..bd7d2b9 --- /dev/null +++ b/doc/nghttp2_session_callbacks_set_send_callback.rst @@ -0,0 +1,16 @@ + +nghttp2_session_callbacks_set_send_callback +=========================================== + +Synopsis +-------- + +*#include * + +.. function:: void nghttp2_session_callbacks_set_send_callback( nghttp2_session_callbacks *cbs, nghttp2_send_callback send_callback) + + + Sets callback function invoked when a session wants to send data to + the remote peer. This callback is not necessary if the application + uses solely `nghttp2_session_mem_send()` to serialize data to + transmit. diff --git a/doc/nghttp2_session_callbacks_set_send_data_callback.rst b/doc/nghttp2_session_callbacks_set_send_data_callback.rst new file mode 100644 index 0000000..5678cfc --- /dev/null +++ b/doc/nghttp2_session_callbacks_set_send_data_callback.rst @@ -0,0 +1,15 @@ + +nghttp2_session_callbacks_set_send_data_callback +================================================ + +Synopsis +-------- + +*#include * + +.. function:: void nghttp2_session_callbacks_set_send_data_callback( nghttp2_session_callbacks *cbs, nghttp2_send_data_callback send_data_callback) + + + Sets callback function invoked when + :macro:`NGHTTP2_DATA_FLAG_NO_COPY` is used in + :type:`nghttp2_data_source_read_callback` to avoid data copy. diff --git a/doc/nghttp2_session_client_new.rst b/doc/nghttp2_session_client_new.rst new file mode 100644 index 0000000..6f0c261 --- /dev/null +++ b/doc/nghttp2_session_client_new.rst @@ -0,0 +1,29 @@ + +nghttp2_session_client_new +========================== + +Synopsis +-------- + +*#include * + +.. function:: int nghttp2_session_client_new(nghttp2_session **session_ptr, const nghttp2_session_callbacks *callbacks, void *user_data) + + + Initializes *\*session_ptr* for client use. The all members of + *callbacks* are copied to *\*session_ptr*. Therefore *\*session_ptr* + does not store *callbacks*. The *user_data* is an arbitrary user + supplied data, which will be passed to the callback functions. + + The :type:`nghttp2_send_callback` must be specified. If the + application code uses `nghttp2_session_recv()`, the + :type:`nghttp2_recv_callback` must be specified. The other members + of *callbacks* can be ``NULL``. + + If this function fails, *\*session_ptr* is left untouched. + + This function returns 0 if it succeeds, or one of the following + negative error codes: + + :macro:`NGHTTP2_ERR_NOMEM` + Out of memory. diff --git a/doc/nghttp2_session_client_new2.rst b/doc/nghttp2_session_client_new2.rst new file mode 100644 index 0000000..863d429 --- /dev/null +++ b/doc/nghttp2_session_client_new2.rst @@ -0,0 +1,29 @@ + +nghttp2_session_client_new2 +=========================== + +Synopsis +-------- + +*#include * + +.. function:: int nghttp2_session_client_new2(nghttp2_session **session_ptr, const nghttp2_session_callbacks *callbacks, void *user_data, const nghttp2_option *option) + + + Like `nghttp2_session_client_new()`, but with additional options + specified in the *option*. + + The *option* can be ``NULL`` and the call is equivalent to + `nghttp2_session_client_new()`. + + This function does not take ownership *option*. The application is + responsible for freeing *option* if it finishes using the object. + + The library code does not refer to *option* after this function + returns. + + This function returns 0 if it succeeds, or one of the following + negative error codes: + + :macro:`NGHTTP2_ERR_NOMEM` + Out of memory. diff --git a/doc/nghttp2_session_client_new3.rst b/doc/nghttp2_session_client_new3.rst new file mode 100644 index 0000000..22bef5a --- /dev/null +++ b/doc/nghttp2_session_client_new3.rst @@ -0,0 +1,29 @@ + +nghttp2_session_client_new3 +=========================== + +Synopsis +-------- + +*#include * + +.. function:: int nghttp2_session_client_new3( nghttp2_session **session_ptr, const nghttp2_session_callbacks *callbacks, void *user_data, const nghttp2_option *option, nghttp2_mem *mem) + + + Like `nghttp2_session_client_new2()`, but with additional custom + memory allocator specified in the *mem*. + + The *mem* can be ``NULL`` and the call is equivalent to + `nghttp2_session_client_new2()`. + + This function does not take ownership *mem*. The application is + responsible for freeing *mem*. + + The library code does not refer to *mem* pointer after this + function returns, so the application can safely free it. + + This function returns 0 if it succeeds, or one of the following + negative error codes: + + :macro:`NGHTTP2_ERR_NOMEM` + Out of memory. diff --git a/doc/nghttp2_session_consume.rst b/doc/nghttp2_session_consume.rst new file mode 100644 index 0000000..c4d08f2 --- /dev/null +++ b/doc/nghttp2_session_consume.rst @@ -0,0 +1,31 @@ + +nghttp2_session_consume +======================= + +Synopsis +-------- + +*#include * + +.. function:: int nghttp2_session_consume(nghttp2_session *session, int32_t stream_id, size_t size) + + + Tells the *session* that *size* bytes for a stream denoted by + *stream_id* were consumed by application and are ready to + WINDOW_UPDATE. The consumed bytes are counted towards both + connection and stream level WINDOW_UPDATE (see + `nghttp2_session_consume_connection()` and + `nghttp2_session_consume_stream()` to update consumption + independently). This function is intended to be used without + automatic window update (see + `nghttp2_option_set_no_auto_window_update()`). + + This function returns 0 if it succeeds, or one of the following + negative error codes: + + :macro:`NGHTTP2_ERR_NOMEM` + Out of memory. + :macro:`NGHTTP2_ERR_INVALID_ARGUMENT` + The *stream_id* is 0. + :macro:`NGHTTP2_ERR_INVALID_STATE` + Automatic WINDOW_UPDATE is not disabled. diff --git a/doc/nghttp2_session_consume_connection.rst b/doc/nghttp2_session_consume_connection.rst new file mode 100644 index 0000000..c263cd6 --- /dev/null +++ b/doc/nghttp2_session_consume_connection.rst @@ -0,0 +1,24 @@ + +nghttp2_session_consume_connection +================================== + +Synopsis +-------- + +*#include * + +.. function:: int nghttp2_session_consume_connection(nghttp2_session *session, size_t size) + + + Like `nghttp2_session_consume()`, but this only tells library that + *size* bytes were consumed only for connection level. Note that + HTTP/2 maintains connection and stream level flow control windows + independently. + + This function returns 0 if it succeeds, or one of the following + negative error codes: + + :macro:`NGHTTP2_ERR_NOMEM` + Out of memory. + :macro:`NGHTTP2_ERR_INVALID_STATE` + Automatic WINDOW_UPDATE is not disabled. diff --git a/doc/nghttp2_session_consume_stream.rst b/doc/nghttp2_session_consume_stream.rst new file mode 100644 index 0000000..694ee50 --- /dev/null +++ b/doc/nghttp2_session_consume_stream.rst @@ -0,0 +1,26 @@ + +nghttp2_session_consume_stream +============================== + +Synopsis +-------- + +*#include * + +.. function:: int nghttp2_session_consume_stream(nghttp2_session *session, int32_t stream_id, size_t size) + + + Like `nghttp2_session_consume()`, but this only tells library that + *size* bytes were consumed only for stream denoted by *stream_id*. + Note that HTTP/2 maintains connection and stream level flow control + windows independently. + + This function returns 0 if it succeeds, or one of the following + negative error codes: + + :macro:`NGHTTP2_ERR_NOMEM` + Out of memory. + :macro:`NGHTTP2_ERR_INVALID_ARGUMENT` + The *stream_id* is 0. + :macro:`NGHTTP2_ERR_INVALID_STATE` + Automatic WINDOW_UPDATE is not disabled. diff --git a/doc/nghttp2_session_del.rst b/doc/nghttp2_session_del.rst new file mode 100644 index 0000000..1c00d7c --- /dev/null +++ b/doc/nghttp2_session_del.rst @@ -0,0 +1,14 @@ + +nghttp2_session_del +=================== + +Synopsis +-------- + +*#include * + +.. function:: void nghttp2_session_del(nghttp2_session *session) + + + Frees any resources allocated for *session*. If *session* is + ``NULL``, this function does nothing. diff --git a/doc/nghttp2_session_get_effective_local_window_size.rst b/doc/nghttp2_session_get_effective_local_window_size.rst new file mode 100644 index 0000000..60efcba --- /dev/null +++ b/doc/nghttp2_session_get_effective_local_window_size.rst @@ -0,0 +1,18 @@ + +nghttp2_session_get_effective_local_window_size +=============================================== + +Synopsis +-------- + +*#include * + +.. function:: int32_t nghttp2_session_get_effective_local_window_size(nghttp2_session *session) + + + Returns the local (receive) window size for a connection. The + local window size can be adjusted by + `nghttp2_submit_window_update()`. This function takes into account + that and returns effective window size. + + This function returns -1 if it fails. diff --git a/doc/nghttp2_session_get_effective_recv_data_length.rst b/doc/nghttp2_session_get_effective_recv_data_length.rst new file mode 100644 index 0000000..be4e97a --- /dev/null +++ b/doc/nghttp2_session_get_effective_recv_data_length.rst @@ -0,0 +1,22 @@ + +nghttp2_session_get_effective_recv_data_length +============================================== + +Synopsis +-------- + +*#include * + +.. function:: int32_t nghttp2_session_get_effective_recv_data_length(nghttp2_session *session) + + + Returns the number of DATA payload in bytes received without + WINDOW_UPDATE transmission for a connection. The local (receive) + window size can be adjusted by `nghttp2_submit_window_update()`. + This function takes into account that and returns effective data + length. In particular, if the local window size is reduced by + submitting negative window_size_increment with + `nghttp2_submit_window_update()`, this function returns the number + of bytes less than actually received. + + This function returns -1 if it fails. diff --git a/doc/nghttp2_session_get_last_proc_stream_id.rst b/doc/nghttp2_session_get_last_proc_stream_id.rst new file mode 100644 index 0000000..726fffd --- /dev/null +++ b/doc/nghttp2_session_get_last_proc_stream_id.rst @@ -0,0 +1,19 @@ + +nghttp2_session_get_last_proc_stream_id +======================================= + +Synopsis +-------- + +*#include * + +.. function:: int32_t nghttp2_session_get_last_proc_stream_id(nghttp2_session *session) + + + Returns the last stream ID of a stream for which + :type:`nghttp2_on_frame_recv_callback` was invoked most recently. + The returned value can be used as last_stream_id parameter for + `nghttp2_submit_goaway()` and + `nghttp2_session_terminate_session2()`. + + This function always succeeds. diff --git a/doc/nghttp2_session_get_next_stream_id.rst b/doc/nghttp2_session_get_next_stream_id.rst new file mode 100644 index 0000000..a68401c --- /dev/null +++ b/doc/nghttp2_session_get_next_stream_id.rst @@ -0,0 +1,15 @@ + +nghttp2_session_get_next_stream_id +================================== + +Synopsis +-------- + +*#include * + +.. function:: uint32_t nghttp2_session_get_next_stream_id(nghttp2_session *session) + + + Returns the next outgoing stream ID. Notice that return type is + uint32_t. If we run out of stream ID for this session, this + function returns 1 << 31. diff --git a/doc/nghttp2_session_get_outbound_queue_size.rst b/doc/nghttp2_session_get_outbound_queue_size.rst new file mode 100644 index 0000000..8816ee7 --- /dev/null +++ b/doc/nghttp2_session_get_outbound_queue_size.rst @@ -0,0 +1,14 @@ + +nghttp2_session_get_outbound_queue_size +======================================= + +Synopsis +-------- + +*#include * + +.. function:: size_t nghttp2_session_get_outbound_queue_size(nghttp2_session *session) + + + Returns the number of frames in the outbound queue. This does not + include the deferred DATA frames. diff --git a/doc/nghttp2_session_get_remote_settings.rst b/doc/nghttp2_session_get_remote_settings.rst new file mode 100644 index 0000000..6e9ad91 --- /dev/null +++ b/doc/nghttp2_session_get_remote_settings.rst @@ -0,0 +1,15 @@ + +nghttp2_session_get_remote_settings +=================================== + +Synopsis +-------- + +*#include * + +.. function:: uint32_t nghttp2_session_get_remote_settings(nghttp2_session *session, nghttp2_settings_id id) + + + Returns the value of SETTINGS *id* notified by a remote endpoint. + The *id* must be one of values defined in + :macro:`nghttp2_settings_id`. diff --git a/doc/nghttp2_session_get_remote_window_size.rst b/doc/nghttp2_session_get_remote_window_size.rst new file mode 100644 index 0000000..cd810c2 --- /dev/null +++ b/doc/nghttp2_session_get_remote_window_size.rst @@ -0,0 +1,15 @@ + +nghttp2_session_get_remote_window_size +====================================== + +Synopsis +-------- + +*#include * + +.. function:: int32_t nghttp2_session_get_remote_window_size(nghttp2_session *session) + + + Returns the remote window size for a connection. + + This function always succeeds. diff --git a/doc/nghttp2_session_get_stream_effective_local_window_size.rst b/doc/nghttp2_session_get_stream_effective_local_window_size.rst new file mode 100644 index 0000000..387f7a6 --- /dev/null +++ b/doc/nghttp2_session_get_stream_effective_local_window_size.rst @@ -0,0 +1,18 @@ + +nghttp2_session_get_stream_effective_local_window_size +====================================================== + +Synopsis +-------- + +*#include * + +.. function:: int32_t nghttp2_session_get_stream_effective_local_window_size( nghttp2_session *session, int32_t stream_id) + + + Returns the local (receive) window size for the stream *stream_id*. + The local window size can be adjusted by + `nghttp2_submit_window_update()`. This function takes into account + that and returns effective window size. + + This function returns -1 if it fails. diff --git a/doc/nghttp2_session_get_stream_effective_recv_data_length.rst b/doc/nghttp2_session_get_stream_effective_recv_data_length.rst new file mode 100644 index 0000000..8e01b2f --- /dev/null +++ b/doc/nghttp2_session_get_stream_effective_recv_data_length.rst @@ -0,0 +1,22 @@ + +nghttp2_session_get_stream_effective_recv_data_length +===================================================== + +Synopsis +-------- + +*#include * + +.. function:: int32_t nghttp2_session_get_stream_effective_recv_data_length( nghttp2_session *session, int32_t stream_id) + + + Returns the number of DATA payload in bytes received without + WINDOW_UPDATE transmission for the stream *stream_id*. The local + (receive) window size can be adjusted by + `nghttp2_submit_window_update()`. This function takes into account + that and returns effective data length. In particular, if the + local window size is reduced by submitting negative + window_size_increment with `nghttp2_submit_window_update()`, this + function returns the number of bytes less than actually received. + + This function returns -1 if it fails. diff --git a/doc/nghttp2_session_get_stream_local_close.rst b/doc/nghttp2_session_get_stream_local_close.rst new file mode 100644 index 0000000..cc01d8d --- /dev/null +++ b/doc/nghttp2_session_get_stream_local_close.rst @@ -0,0 +1,14 @@ + +nghttp2_session_get_stream_local_close +====================================== + +Synopsis +-------- + +*#include * + +.. function:: int nghttp2_session_get_stream_local_close(nghttp2_session *session, int32_t stream_id) + + + Returns 1 if local peer half closed the given stream *stream_id*. + Returns 0 if it did not. Returns -1 if no such stream exists. diff --git a/doc/nghttp2_session_get_stream_remote_close.rst b/doc/nghttp2_session_get_stream_remote_close.rst new file mode 100644 index 0000000..a0a540c --- /dev/null +++ b/doc/nghttp2_session_get_stream_remote_close.rst @@ -0,0 +1,14 @@ + +nghttp2_session_get_stream_remote_close +======================================= + +Synopsis +-------- + +*#include * + +.. function:: int nghttp2_session_get_stream_remote_close(nghttp2_session *session, int32_t stream_id) + + + Returns 1 if remote peer half closed the given stream *stream_id*. + Returns 0 if it did not. Returns -1 if no such stream exists. diff --git a/doc/nghttp2_session_get_stream_remote_window_size.rst b/doc/nghttp2_session_get_stream_remote_window_size.rst new file mode 100644 index 0000000..5404162 --- /dev/null +++ b/doc/nghttp2_session_get_stream_remote_window_size.rst @@ -0,0 +1,22 @@ + +nghttp2_session_get_stream_remote_window_size +============================================= + +Synopsis +-------- + +*#include * + +.. function:: int32_t nghttp2_session_get_stream_remote_window_size(nghttp2_session *session, int32_t stream_id) + + + Returns the remote window size for a given stream *stream_id*. + + This is the amount of flow-controlled payload (e.g., DATA) that the + local endpoint can send without stream level WINDOW_UPDATE. There + is also connection level flow control, so the effective size of + payload that the local endpoint can actually send is + min(`nghttp2_session_get_stream_remote_window_size()`, + `nghttp2_session_get_remote_window_size()`). + + This function returns -1 if it fails. diff --git a/doc/nghttp2_session_get_stream_user_data.rst b/doc/nghttp2_session_get_stream_user_data.rst new file mode 100644 index 0000000..94f18c1 --- /dev/null +++ b/doc/nghttp2_session_get_stream_user_data.rst @@ -0,0 +1,20 @@ + +nghttp2_session_get_stream_user_data +==================================== + +Synopsis +-------- + +*#include * + +.. function:: void * nghttp2_session_get_stream_user_data(nghttp2_session *session, int32_t stream_id) + + + Returns stream_user_data for the stream *stream_id*. The + stream_user_data is provided by `nghttp2_submit_request()`, + `nghttp2_submit_headers()` or + `nghttp2_session_set_stream_user_data()`. Unless it is set using + `nghttp2_session_set_stream_user_data()`, if the stream is + initiated by the remote endpoint, stream_user_data is always + ``NULL``. If the stream does not exist, this function returns + ``NULL``. diff --git a/doc/nghttp2_session_mem_recv.rst b/doc/nghttp2_session_mem_recv.rst new file mode 100644 index 0000000..994db42 --- /dev/null +++ b/doc/nghttp2_session_mem_recv.rst @@ -0,0 +1,42 @@ + +nghttp2_session_mem_recv +======================== + +Synopsis +-------- + +*#include * + +.. function:: ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in, size_t inlen) + + + Processes data *in* as an input from the remote endpoint. The + *inlen* indicates the number of bytes in the *in*. + + This function behaves like `nghttp2_session_recv()` except that it + does not use :type:`nghttp2_recv_callback` to receive data; the + *in* is the only data for the invocation of this function. If all + bytes are processed, this function returns. The other callbacks + are called in the same way as they are in `nghttp2_session_recv()`. + + In the current implementation, this function always tries to + processes all input data unless either an error occurs or + :macro:`NGHTTP2_ERR_PAUSE` is returned from + :type:`nghttp2_on_header_callback` or + :type:`nghttp2_on_data_chunk_recv_callback`. If + :macro:`NGHTTP2_ERR_PAUSE` is used, the return value includes the + number of bytes which was used to produce the data or frame for the + callback. + + This function returns the number of processed bytes, or one of the + following negative error codes: + + :macro:`NGHTTP2_ERR_NOMEM` + Out of memory. + :macro:`NGHTTP2_ERR_CALLBACK_FAILURE` + The callback function failed. + :macro:`NGHTTP2_ERR_BAD_CLIENT_MAGIC` + Invalid client magic was detected. This error only returns + when *session* was configured as server and + `nghttp2_option_set_no_recv_client_magic()` is not used with + nonzero value. diff --git a/doc/nghttp2_session_mem_send.rst b/doc/nghttp2_session_mem_send.rst new file mode 100644 index 0000000..979b637 --- /dev/null +++ b/doc/nghttp2_session_mem_send.rst @@ -0,0 +1,38 @@ + +nghttp2_session_mem_send +======================== + +Synopsis +-------- + +*#include * + +.. function:: ssize_t nghttp2_session_mem_send(nghttp2_session *session, const uint8_t **data_ptr) + + + Returns the serialized data to send. + + This function behaves like `nghttp2_session_send()` except that it + does not use :type:`nghttp2_send_callback` to transmit data. + Instead, it assigns the pointer to the serialized data to the + *\*data_ptr* and returns its length. The other callbacks are called + in the same way as they are in `nghttp2_session_send()`. + + If no data is available to send, this function returns 0. + + This function may not return all serialized data in one invocation. + To get all data, call this function repeatedly until it returns 0 + or one of negative error codes. + + The assigned *\*data_ptr* is valid until the next call of + `nghttp2_session_mem_send()` or `nghttp2_session_send()`. + + The caller must send all data before sending the next chunk of + data. + + This function returns the length of the data pointed by the + *\*data_ptr* if it succeeds, or one of the following negative error + codes: + + :macro:`NGHTTP2_ERR_NOMEM` + Out of memory. diff --git a/doc/nghttp2_session_recv.rst b/doc/nghttp2_session_recv.rst new file mode 100644 index 0000000..61da799 --- /dev/null +++ b/doc/nghttp2_session_recv.rst @@ -0,0 +1,72 @@ + +nghttp2_session_recv +==================== + +Synopsis +-------- + +*#include * + +.. function:: int nghttp2_session_recv(nghttp2_session *session) + + + Receives frames from the remote peer. + + This function receives as many frames as possible until the user + callback :type:`nghttp2_recv_callback` returns + :macro:`NGHTTP2_ERR_WOULDBLOCK`. This function calls several + callback functions which are passed when initializing the + *session*. Here is the simple time chart which tells when each + callback is invoked: + + 1. :type:`nghttp2_recv_callback` is invoked one or more times to + receive frame header. + + 2. When frame header is received, + :type:`nghttp2_on_begin_frame_callback` is invoked. + + 3. If the frame is DATA frame: + + 1. :type:`nghttp2_recv_callback` is invoked to receive DATA + payload. For each chunk of data, + :type:`nghttp2_on_data_chunk_recv_callback` is invoked. + + 2. If one DATA frame is completely received, + :type:`nghttp2_on_frame_recv_callback` is invoked. If the + reception of the frame triggers the closure of the stream, + :type:`nghttp2_on_stream_close_callback` is invoked. + + 4. If the frame is the control frame: + + 1. :type:`nghttp2_recv_callback` is invoked one or more times to + receive whole frame. + + 2. If the received frame is valid, then following actions are + taken. If the frame is either HEADERS or PUSH_PROMISE, + :type:`nghttp2_on_begin_headers_callback` is invoked. Then + :type:`nghttp2_on_header_callback` is invoked for each header + name/value pair. After all name/value pairs are emitted + successfully, :type:`nghttp2_on_frame_recv_callback` is + invoked. For other frames, + :type:`nghttp2_on_frame_recv_callback` is invoked. If the + reception of the frame triggers the closure of the stream, + :type:`nghttp2_on_stream_close_callback` is invoked. + + 3. If the received frame is unpacked but is interpreted as + invalid, :type:`nghttp2_on_invalid_frame_recv_callback` is + invoked. + + This function returns 0 if it succeeds, or one of the following + negative error codes: + + :macro:`NGHTTP2_ERR_EOF` + The remote peer did shutdown on the connection. + :macro:`NGHTTP2_ERR_NOMEM` + Out of memory. + :macro:`NGHTTP2_ERR_CALLBACK_FAILURE` + The callback function failed. + :macro:`NGHTTP2_ERR_BAD_CLIENT_MAGIC` + Invalid client magic was detected. This error only returns + when *session* was configured as server and + `nghttp2_option_set_no_recv_client_magic()` is not used with + nonzero value. diff --git a/doc/nghttp2_session_resume_data.rst b/doc/nghttp2_session_resume_data.rst new file mode 100644 index 0000000..daa9b8f --- /dev/null +++ b/doc/nghttp2_session_resume_data.rst @@ -0,0 +1,22 @@ + +nghttp2_session_resume_data +=========================== + +Synopsis +-------- + +*#include * + +.. function:: int nghttp2_session_resume_data(nghttp2_session *session, int32_t stream_id) + + + Puts back previously deferred DATA frame in the stream *stream_id* + to the outbound queue. + + This function returns 0 if it succeeds, or one of the following + negative error codes: + + :macro:`NGHTTP2_ERR_INVALID_ARGUMENT` + The stream does not exist; or no deferred data exist. + :macro:`NGHTTP2_ERR_NOMEM` + Out of memory. diff --git a/doc/nghttp2_session_send.rst b/doc/nghttp2_session_send.rst new file mode 100644 index 0000000..1f40bab --- /dev/null +++ b/doc/nghttp2_session_send.rst @@ -0,0 +1,55 @@ + +nghttp2_session_send +==================== + +Synopsis +-------- + +*#include * + +.. function:: int nghttp2_session_send(nghttp2_session *session) + + + Sends pending frames to the remote peer. + + This function retrieves the highest prioritized frame from the + outbound queue and sends it to the remote peer. It does this as + many as possible until the user callback + :type:`nghttp2_send_callback` returns + :macro:`NGHTTP2_ERR_WOULDBLOCK` or the outbound queue becomes empty. + This function calls several callback functions which are passed + when initializing the *session*. Here is the simple time chart + which tells when each callback is invoked: + + 1. Get the next frame to send from outbound queue. + + 2. Prepare transmission of the frame. + + 3. If the control frame cannot be sent because some preconditions + are not met (e.g., request HEADERS cannot be sent after GOAWAY), + :type:`nghttp2_on_frame_not_send_callback` is invoked. Abort + the following steps. + + 4. If the frame is HEADERS, PUSH_PROMISE or DATA, + :type:`nghttp2_select_padding_callback` is invoked. + + 5. If the frame is request HEADERS, the stream is opened here. + + 6. :type:`nghttp2_before_frame_send_callback` is invoked. + + 7. :type:`nghttp2_send_callback` is invoked one or more times to + send the frame. + + 8. :type:`nghttp2_on_frame_send_callback` is invoked. + + 9. If the transmission of the frame triggers closure of the stream, + the stream is closed and + :type:`nghttp2_on_stream_close_callback` is invoked. + + This function returns 0 if it succeeds, or one of the following + negative error codes: + + :macro:`NGHTTP2_ERR_NOMEM` + Out of memory. + :macro:`NGHTTP2_ERR_CALLBACK_FAILURE` + The callback function failed. diff --git a/doc/nghttp2_session_server_new.rst b/doc/nghttp2_session_server_new.rst new file mode 100644 index 0000000..7350180 --- /dev/null +++ b/doc/nghttp2_session_server_new.rst @@ -0,0 +1,29 @@ + +nghttp2_session_server_new +========================== + +Synopsis +-------- + +*#include * + +.. function:: int nghttp2_session_server_new(nghttp2_session **session_ptr, const nghttp2_session_callbacks *callbacks, void *user_data) + + + Initializes *\*session_ptr* for server use. The all members of + *callbacks* are copied to *\*session_ptr*. Therefore *\*session_ptr* + does not store *callbacks*. The *user_data* is an arbitrary user + supplied data, which will be passed to the callback functions. + + The :type:`nghttp2_send_callback` must be specified. If the + application code uses `nghttp2_session_recv()`, the + :type:`nghttp2_recv_callback` must be specified. The other members + of *callbacks* can be ``NULL``. + + If this function fails, *\*session_ptr* is left untouched. + + This function returns 0 if it succeeds, or one of the following + negative error codes: + + :macro:`NGHTTP2_ERR_NOMEM` + Out of memory. diff --git a/doc/nghttp2_session_server_new2.rst b/doc/nghttp2_session_server_new2.rst new file mode 100644 index 0000000..482e651 --- /dev/null +++ b/doc/nghttp2_session_server_new2.rst @@ -0,0 +1,29 @@ + +nghttp2_session_server_new2 +=========================== + +Synopsis +-------- + +*#include * + +.. function:: int nghttp2_session_server_new2(nghttp2_session **session_ptr, const nghttp2_session_callbacks *callbacks, void *user_data, const nghttp2_option *option) + + + Like `nghttp2_session_server_new()`, but with additional options + specified in the *option*. + + The *option* can be ``NULL`` and the call is equivalent to + `nghttp2_session_server_new()`. + + This function does not take ownership *option*. The application is + responsible for freeing *option* if it finishes using the object. + + The library code does not refer to *option* after this function + returns. + + This function returns 0 if it succeeds, or one of the following + negative error codes: + + :macro:`NGHTTP2_ERR_NOMEM` + Out of memory. diff --git a/doc/nghttp2_session_server_new3.rst b/doc/nghttp2_session_server_new3.rst new file mode 100644 index 0000000..35d1e39 --- /dev/null +++ b/doc/nghttp2_session_server_new3.rst @@ -0,0 +1,29 @@ + +nghttp2_session_server_new3 +=========================== + +Synopsis +-------- + +*#include * + +.. function:: int nghttp2_session_server_new3( nghttp2_session **session_ptr, const nghttp2_session_callbacks *callbacks, void *user_data, const nghttp2_option *option, nghttp2_mem *mem) + + + Like `nghttp2_session_server_new2()`, but with additional custom + memory allocator specified in the *mem*. + + The *mem* can be ``NULL`` and the call is equivalent to + `nghttp2_session_server_new2()`. + + This function does not take ownership *mem*. The application is + responsible for freeing *mem*. + + The library code does not refer to *mem* pointer after this + function returns, so the application can safely free it. + + This function returns 0 if it succeeds, or one of the following + negative error codes: + + :macro:`NGHTTP2_ERR_NOMEM` + Out of memory. diff --git a/doc/nghttp2_session_set_next_stream_id.rst b/doc/nghttp2_session_set_next_stream_id.rst new file mode 100644 index 0000000..5f2efa9 --- /dev/null +++ b/doc/nghttp2_session_set_next_stream_id.rst @@ -0,0 +1,24 @@ + +nghttp2_session_set_next_stream_id +================================== + +Synopsis +-------- + +*#include * + +.. function:: int nghttp2_session_set_next_stream_id(nghttp2_session *session, int32_t next_stream_id) + + + Tells the *session* that next stream ID is *next_stream_id*. The + *next_stream_id* must be equal or greater than the value returned + by `nghttp2_session_get_next_stream_id()`. + + This function returns 0 if it succeeds, or one of the following + negative error codes: + + :macro:`NGHTTP2_ERR_INVALID_ARGUMENT` + The *next_stream_id* is strictly less than the value + `nghttp2_session_get_next_stream_id()` returns; or + *next_stream_id* is invalid (e.g., even integer for client, or + odd integer for server). diff --git a/doc/nghttp2_session_set_stream_user_data.rst b/doc/nghttp2_session_set_stream_user_data.rst new file mode 100644 index 0000000..6700650 --- /dev/null +++ b/doc/nghttp2_session_set_stream_user_data.rst @@ -0,0 +1,26 @@ + +nghttp2_session_set_stream_user_data +==================================== + +Synopsis +-------- + +*#include * + +.. function:: int nghttp2_session_set_stream_user_data(nghttp2_session *session, int32_t stream_id, void *stream_user_data) + + + Sets the *stream_user_data* to the stream denoted by the + *stream_id*. If a stream user data is already set to the stream, + it is replaced with the *stream_user_data*. It is valid to specify + ``NULL`` in the *stream_user_data*, which nullifies the associated + data pointer. + + It is valid to set the *stream_user_data* to the stream reserved by + PUSH_PROMISE frame. + + This function returns 0 if it succeeds, or one of following + negative error codes: + + :macro:`NGHTTP2_ERR_INVALID_ARGUMENT` + The stream does not exist diff --git a/doc/nghttp2_session_terminate_session.rst b/doc/nghttp2_session_terminate_session.rst new file mode 100644 index 0000000..2b85319 --- /dev/null +++ b/doc/nghttp2_session_terminate_session.rst @@ -0,0 +1,34 @@ + +nghttp2_session_terminate_session +================================= + +Synopsis +-------- + +*#include * + +.. function:: int nghttp2_session_terminate_session(nghttp2_session *session, uint32_t error_code) + + + Signals the session so that the connection should be terminated. + + The last stream ID is the minimum value between the stream ID of a + stream for which :type:`nghttp2_on_frame_recv_callback` was called + most recently and the last stream ID we have sent to the peer + previously. + + The *error_code* is the error code of this GOAWAY frame. The + pre-defined error code is one of :macro:`nghttp2_error_code`. + + After the transmission, both `nghttp2_session_want_read()` and + `nghttp2_session_want_write()` return 0. + + This function should be called when the connection should be + terminated after sending GOAWAY. If the remaining streams should + be processed after GOAWAY, use `nghttp2_submit_goaway()` instead. + + This function returns 0 if it succeeds, or one of the following + negative error codes: + + :macro:`NGHTTP2_ERR_NOMEM` + Out of memory. diff --git a/doc/nghttp2_session_terminate_session2.rst b/doc/nghttp2_session_terminate_session2.rst new file mode 100644 index 0000000..fd31508 --- /dev/null +++ b/doc/nghttp2_session_terminate_session2.rst @@ -0,0 +1,34 @@ + +nghttp2_session_terminate_session2 +================================== + +Synopsis +-------- + +*#include * + +.. function:: int nghttp2_session_terminate_session2(nghttp2_session *session, int32_t last_stream_id, uint32_t error_code) + + + Signals the session so that the connection should be terminated. + + This function behaves like `nghttp2_session_terminate_session()`, + but the last stream ID can be specified by the application for fine + grained control of stream. The HTTP/2 specification does not allow + last_stream_id to be increased. So the actual value sent as + last_stream_id is the minimum value between the given + *last_stream_id* and the last_stream_id we have previously sent to + the peer. + + The *last_stream_id* is peer's stream ID or 0. So if *session* is + initialized as client, *last_stream_id* must be even or 0. If + *session* is initialized as server, *last_stream_id* must be odd or + 0. + + This function returns 0 if it succeeds, or one of the following + negative error codes: + + :macro:`NGHTTP2_ERR_NOMEM` + Out of memory. + :macro:`NGHTTP2_ERR_INVALID_ARGUMENT` + The *last_stream_id* is invalid. diff --git a/doc/nghttp2_session_upgrade.rst b/doc/nghttp2_session_upgrade.rst new file mode 100644 index 0000000..f750e7c --- /dev/null +++ b/doc/nghttp2_session_upgrade.rst @@ -0,0 +1,44 @@ + +nghttp2_session_upgrade +======================= + +Synopsis +-------- + +*#include * + +.. function:: int nghttp2_session_upgrade(nghttp2_session *session, const uint8_t *settings_payload, size_t settings_payloadlen, void *stream_user_data) + + + Performs post-process of HTTP Upgrade request. This function can + be called from both client and server, but the behavior is very + different in each other. + + If called from client side, the *settings_payload* must be the + value sent in ``HTTP2-Settings`` header field and must be decoded + by base64url decoder. The *settings_payloadlen* is the length of + *settings_payload*. The *settings_payload* is unpacked and its + setting values will be submitted using `nghttp2_submit_settings()`. + This means that the client application code does not need to submit + SETTINGS by itself. The stream with stream ID=1 is opened and the + *stream_user_data* is used for its stream_user_data. The opened + stream becomes half-closed (local) state. + + If called from server side, the *settings_payload* must be the + value received in ``HTTP2-Settings`` header field and must be + decoded by base64url decoder. The *settings_payloadlen* is the + length of *settings_payload*. It is treated as if the SETTINGS + frame with that payload is received. Thus, callback functions for + the reception of SETTINGS frame will be invoked. The stream with + stream ID=1 is opened. The *stream_user_data* is ignored. The + opened stream becomes half-closed (remote). + + This function returns 0 if it succeeds, or one of the following + negative error codes: + + :macro:`NGHTTP2_ERR_NOMEM` + Out of memory. + :macro:`NGHTTP2_ERR_INVALID_ARGUMENT` + The *settings_payload* is badly formed. + :macro:`NGHTTP2_ERR_PROTO` + The stream ID 1 is already used or closed; or is not available. diff --git a/doc/nghttp2_session_want_read.rst b/doc/nghttp2_session_want_read.rst new file mode 100644 index 0000000..4a2af49 --- /dev/null +++ b/doc/nghttp2_session_want_read.rst @@ -0,0 +1,18 @@ + +nghttp2_session_want_read +========================= + +Synopsis +-------- + +*#include * + +.. function:: int nghttp2_session_want_read(nghttp2_session *session) + + + Returns nonzero value if *session* wants to receive data from the + remote peer. + + If both `nghttp2_session_want_read()` and + `nghttp2_session_want_write()` return 0, the application should + drop the connection. diff --git a/doc/nghttp2_session_want_write.rst b/doc/nghttp2_session_want_write.rst new file mode 100644 index 0000000..96f27fc --- /dev/null +++ b/doc/nghttp2_session_want_write.rst @@ -0,0 +1,18 @@ + +nghttp2_session_want_write +========================== + +Synopsis +-------- + +*#include * + +.. function:: int nghttp2_session_want_write(nghttp2_session *session) + + + Returns nonzero value if *session* wants to send data to the remote + peer. + + If both `nghttp2_session_want_read()` and + `nghttp2_session_want_write()` return 0, the application should + drop the connection. diff --git a/doc/nghttp2_strerror.rst b/doc/nghttp2_strerror.rst new file mode 100644 index 0000000..479593e --- /dev/null +++ b/doc/nghttp2_strerror.rst @@ -0,0 +1,14 @@ + +nghttp2_strerror +================ + +Synopsis +-------- + +*#include * + +.. function:: const char *nghttp2_strerror(int lib_error_code) + + + Returns string describing the *lib_error_code*. The + *lib_error_code* must be one of the :macro:`nghttp2_error`. diff --git a/doc/nghttp2_submit_data.rst b/doc/nghttp2_submit_data.rst new file mode 100644 index 0000000..d0c5579 --- /dev/null +++ b/doc/nghttp2_submit_data.rst @@ -0,0 +1,42 @@ + +nghttp2_submit_data +=================== + +Synopsis +-------- + +*#include * + +.. function:: int nghttp2_submit_data(nghttp2_session *session, uint8_t flags, int32_t stream_id, const nghttp2_data_provider *data_prd) + + + Submits one or more DATA frames to the stream *stream_id*. The + data to be sent are provided by *data_prd*. If *flags* contains + :macro:`NGHTTP2_FLAG_END_STREAM`, the last DATA frame has END_STREAM + flag set. + + This function does not take ownership of the *data_prd*. The + function copies the members of the *data_prd*. + + This function returns 0 if it succeeds, or one of the following + negative error codes: + + :macro:`NGHTTP2_ERR_NOMEM` + Out of memory. + :macro:`NGHTTP2_ERR_DATA_EXIST` + DATA has been already submitted and not fully processed yet. + :macro:`NGHTTP2_ERR_INVALID_ARGUMENT` + The *stream_id* is 0. + :macro:`NGHTTP2_ERR_STREAM_CLOSED` + The stream was alreay closed; or the *stream_id* is invalid. + + .. note:: + + Currently, only one data is allowed for a stream at a time. + Submitting data more than once before first data is finished + results in :macro:`NGHTTP2_ERR_DATA_EXIST` error code. The + earliest callback which tells that previous data is done is + :type:`nghttp2_on_frame_send_callback`. In side that callback, + new data can be submitted using `nghttp2_submit_data()`. Of + course, all data except for last one must not have + :macro:`NGHTTP2_FLAG_END_STREAM` flag set in *flags*. diff --git a/doc/nghttp2_submit_goaway.rst b/doc/nghttp2_submit_goaway.rst new file mode 100644 index 0000000..93a99f1 --- /dev/null +++ b/doc/nghttp2_submit_goaway.rst @@ -0,0 +1,53 @@ + +nghttp2_submit_goaway +===================== + +Synopsis +-------- + +*#include * + +.. function:: int nghttp2_submit_goaway(nghttp2_session *session, uint8_t flags, int32_t last_stream_id, uint32_t error_code, const uint8_t *opaque_data, size_t opaque_data_len) + + + Submits GOAWAY frame with the last stream ID *last_stream_id* and + the error code *error_code*. + + The pre-defined error code is one of :macro:`nghttp2_error_code`. + + The *flags* is currently ignored and should be + :macro:`NGHTTP2_FLAG_NONE`. + + The *last_stream_id* is peer's stream ID or 0. So if *session* is + initialized as client, *last_stream_id* must be even or 0. If + *session* is initialized as server, *last_stream_id* must be odd or + 0. + + The HTTP/2 specification says last_stream_id must not be increased + from the value previously sent. So the actual value sent as + last_stream_id is the minimum value between the given + *last_stream_id* and the last_stream_id previously sent to the + peer. + + If the *opaque_data* is not ``NULL`` and *opaque_data_len* is not + zero, those data will be sent as additional debug data. The + library makes a copy of the memory region pointed by *opaque_data* + with the length *opaque_data_len*, so the caller does not need to + keep this memory after the return of this function. If the + *opaque_data_len* is 0, the *opaque_data* could be ``NULL``. + + After successful transmission of GOAWAY, following things happen. + All incoming streams having strictly more than *last_stream_id* are + closed. All incoming HEADERS which starts new stream are simply + ignored. After all active streams are handled, both + `nghttp2_session_want_read()` and `nghttp2_session_want_write()` + return 0 and the application can close session. + + This function returns 0 if it succeeds, or one of the following + negative error codes: + + :macro:`NGHTTP2_ERR_NOMEM` + Out of memory. + :macro:`NGHTTP2_ERR_INVALID_ARGUMENT` + The *opaque_data_len* is too large; the *last_stream_id* is + invalid. diff --git a/doc/nghttp2_submit_headers.rst b/doc/nghttp2_submit_headers.rst new file mode 100644 index 0000000..f328bad --- /dev/null +++ b/doc/nghttp2_submit_headers.rst @@ -0,0 +1,80 @@ + +nghttp2_submit_headers +====================== + +Synopsis +-------- + +*#include * + +.. function:: int32_t nghttp2_submit_headers(nghttp2_session *session, uint8_t flags, int32_t stream_id, const nghttp2_priority_spec *pri_spec, const nghttp2_nv *nva, size_t nvlen, void *stream_user_data) + + + Submits HEADERS frame. The *flags* is bitwise OR of the + following values: + + * :macro:`NGHTTP2_FLAG_END_STREAM` + + If *flags* includes :macro:`NGHTTP2_FLAG_END_STREAM`, this frame has + END_STREAM flag set. + + The library handles the CONTINUATION frame internally and it + correctly sets END_HEADERS to the last sequence of the PUSH_PROMISE + or CONTINUATION frame. + + If the *stream_id* is -1, this frame is assumed as request (i.e., + request HEADERS frame which opens new stream). In this case, the + assigned stream ID will be returned. Otherwise, specify stream ID + in *stream_id*. + + The *pri_spec* is priority specification of this request. ``NULL`` + means the default priority (see + `nghttp2_priority_spec_default_init()`). To specify the priority, + use `nghttp2_priority_spec_init()`. If *pri_spec* is not ``NULL``, + this function will copy its data members. + + The ``pri_spec->weight`` must be in [:macro:`NGHTTP2_MIN_WEIGHT`, + :macro:`NGHTTP2_MAX_WEIGHT`], inclusive. If ``pri_spec->weight`` is + strictly less than :macro:`NGHTTP2_MIN_WEIGHT`, it becomes + :macro:`NGHTTP2_MIN_WEIGHT`. If it is strictly greater than + :macro:`NGHTTP2_MAX_WEIGHT`, it becomes :macro:`NGHTTP2_MAX_WEIGHT`. + + The *nva* is an array of name/value pair :type:`nghttp2_nv` with + *nvlen* elements. The application is responsible to include + required pseudo-header fields (header field whose name starts with + ":") in *nva* and must place pseudo-headers before regular header + fields. + + This function creates copies of all name/value pairs in *nva*. It + also lower-cases all names in *nva*. The order of elements in + *nva* is preserved. + + The *stream_user_data* is a pointer to an arbitrary data which is + associated to the stream this frame will open. Therefore it is + only used if this frame opens streams, in other words, it changes + stream state from idle or reserved to open. + + This function is low-level in a sense that the application code can + specify flags directly. For usual HTTP request, + `nghttp2_submit_request()` is useful. + + This function returns newly assigned stream ID if it succeeds and + *stream_id* is -1. Otherwise, this function returns 0 if it + succeeds, or one of the following negative error codes: + + :macro:`NGHTTP2_ERR_NOMEM` + Out of memory. + :macro:`NGHTTP2_ERR_STREAM_ID_NOT_AVAILABLE` + No stream ID is available because maximum stream ID was + reached. + :macro:`NGHTTP2_ERR_INVALID_ARGUMENT` + The *stream_id* is 0. + + .. warning:: + + This function returns assigned stream ID if it succeeds and + *stream_id* is -1. But that stream is not opened yet. The + application must not submit frame to that stream ID before + :type:`nghttp2_before_frame_send_callback` is called for this + frame. + diff --git a/doc/nghttp2_submit_ping.rst b/doc/nghttp2_submit_ping.rst new file mode 100644 index 0000000..7c6ec8f --- /dev/null +++ b/doc/nghttp2_submit_ping.rst @@ -0,0 +1,29 @@ + +nghttp2_submit_ping +=================== + +Synopsis +-------- + +*#include * + +.. function:: int nghttp2_submit_ping(nghttp2_session *session, uint8_t flags, const uint8_t *opaque_data) + + + Submits PING frame. You don't have to send PING back when you + received PING frame. The library automatically submits PING frame + in this case. + + The *flags* is currently ignored and should be + :macro:`NGHTTP2_FLAG_NONE`. + + If the *opaque_data* is non ``NULL``, then it should point to the 8 + bytes array of memory to specify opaque data to send with PING + frame. If the *opaque_data* is ``NULL``, zero-cleared 8 bytes will + be sent as opaque data. + + This function returns 0 if it succeeds, or one of the following + negative error codes: + + :macro:`NGHTTP2_ERR_NOMEM` + Out of memory. diff --git a/doc/nghttp2_submit_priority.rst b/doc/nghttp2_submit_priority.rst new file mode 100644 index 0000000..2fda48a --- /dev/null +++ b/doc/nghttp2_submit_priority.rst @@ -0,0 +1,37 @@ + +nghttp2_submit_priority +======================= + +Synopsis +-------- + +*#include * + +.. function:: int nghttp2_submit_priority(nghttp2_session *session, uint8_t flags, int32_t stream_id, const nghttp2_priority_spec *pri_spec) + + + Submits PRIORITY frame to change the priority of stream *stream_id* + to the priority specification *pri_spec*. + + The *flags* is currently ignored and should be + :macro:`NGHTTP2_FLAG_NONE`. + + The *pri_spec* is priority specification of this request. ``NULL`` + is not allowed for this function. To specify the priority, use + `nghttp2_priority_spec_init()`. This function will copy its data + members. + + The ``pri_spec->weight`` must be in [:macro:`NGHTTP2_MIN_WEIGHT`, + :macro:`NGHTTP2_MAX_WEIGHT`], inclusive. If ``pri_spec->weight`` is + strictly less than :macro:`NGHTTP2_MIN_WEIGHT`, it becomes + :macro:`NGHTTP2_MIN_WEIGHT`. If it is strictly greater than + :macro:`NGHTTP2_MAX_WEIGHT`, it becomes :macro:`NGHTTP2_MAX_WEIGHT`. + + This function returns 0 if it succeeds, or one of the following + negative error codes: + + :macro:`NGHTTP2_ERR_NOMEM` + Out of memory. + :macro:`NGHTTP2_ERR_INVALID_ARGUMENT` + The *stream_id* is 0; or the *pri_spec* is NULL; or trying to + depend on itself. diff --git a/doc/nghttp2_submit_push_promise.rst b/doc/nghttp2_submit_push_promise.rst new file mode 100644 index 0000000..8d66933 --- /dev/null +++ b/doc/nghttp2_submit_push_promise.rst @@ -0,0 +1,65 @@ + +nghttp2_submit_push_promise +=========================== + +Synopsis +-------- + +*#include * + +.. function:: int32_t nghttp2_submit_push_promise(nghttp2_session *session, uint8_t flags, int32_t stream_id, const nghttp2_nv *nva, size_t nvlen, void *promised_stream_user_data) + + + Submits PUSH_PROMISE frame. + + The *flags* is currently ignored. The library handles the + CONTINUATION frame internally and it correctly sets END_HEADERS to + the last sequence of the PUSH_PROMISE or CONTINUATION frame. + + The *stream_id* must be client initiated stream ID. + + The *nva* is an array of name/value pair :type:`nghttp2_nv` with + *nvlen* elements. The application is responsible to include + required pseudo-header fields (header field whose name starts with + ":") in *nva* and must place pseudo-headers before regular header + fields. + + This function creates copies of all name/value pairs in *nva*. It + also lower-cases all names in *nva*. The order of elements in + *nva* is preserved. + + The *promised_stream_user_data* is a pointer to an arbitrary data + which is associated to the promised stream this frame will open and + make it in reserved state. It is available using + `nghttp2_session_get_stream_user_data()`. The application can + access it in :type:`nghttp2_before_frame_send_callback` and + :type:`nghttp2_on_frame_send_callback` of this frame. + + The client side is not allowed to use this function. + + To submit response headers and data, use + `nghttp2_submit_response()`. + + This function returns assigned promised stream ID if it succeeds, + or one of the following negative error codes: + + :macro:`NGHTTP2_ERR_NOMEM` + Out of memory. + :macro:`NGHTTP2_ERR_PROTO` + This function was invoked when *session* is initialized as + client. + :macro:`NGHTTP2_ERR_STREAM_ID_NOT_AVAILABLE` + No stream ID is available because maximum stream ID was + reached. + :macro:`NGHTTP2_ERR_INVALID_ARGUMENT` + The *stream_id* is 0; The *stream_id* does not designate stream + that peer initiated. + + .. warning:: + + This function returns assigned promised stream ID if it succeeds. + But that stream is not opened yet. The application must not + submit frame to that stream ID before + :type:`nghttp2_before_frame_send_callback` is called for this + frame. + diff --git a/doc/nghttp2_submit_request.rst b/doc/nghttp2_submit_request.rst new file mode 100644 index 0000000..a4b36dd --- /dev/null +++ b/doc/nghttp2_submit_request.rst @@ -0,0 +1,68 @@ + +nghttp2_submit_request +====================== + +Synopsis +-------- + +*#include * + +.. function:: int32_t nghttp2_submit_request(nghttp2_session *session, const nghttp2_priority_spec *pri_spec, const nghttp2_nv *nva, size_t nvlen, const nghttp2_data_provider *data_prd, void *stream_user_data) + + + Submits HEADERS frame and optionally one or more DATA frames. + + The *pri_spec* is priority specification of this request. ``NULL`` + means the default priority (see + `nghttp2_priority_spec_default_init()`). To specify the priority, + use `nghttp2_priority_spec_init()`. If *pri_spec* is not ``NULL``, + this function will copy its data members. + + The ``pri_spec->weight`` must be in [:macro:`NGHTTP2_MIN_WEIGHT`, + :macro:`NGHTTP2_MAX_WEIGHT`], inclusive. If ``pri_spec->weight`` is + strictly less than :macro:`NGHTTP2_MIN_WEIGHT`, it becomes + :macro:`NGHTTP2_MIN_WEIGHT`. If it is strictly greater than + :macro:`NGHTTP2_MAX_WEIGHT`, it becomes :macro:`NGHTTP2_MAX_WEIGHT`. + + The *nva* is an array of name/value pair :type:`nghttp2_nv` with + *nvlen* elements. The application is responsible to include + required pseudo-header fields (header field whose name starts with + ":") in *nva* and must place pseudo-headers before regular header + fields. + + This function creates copies of all name/value pairs in *nva*. It + also lower-cases all names in *nva*. The order of elements in + *nva* is preserved. + + HTTP/2 specification has requirement about header fields in the + request HEADERS. See the specification for more details. + + If *data_prd* is not ``NULL``, it provides data which will be sent + in subsequent DATA frames. In this case, a method that allows + request message bodies + (https://tools.ietf.org/html/rfc7231#section-4) must be specified + with ``:method`` key in *nva* (e.g. ``POST``). This function does + not take ownership of the *data_prd*. The function copies the + members of the *data_prd*. If *data_prd* is ``NULL``, HEADERS have + END_STREAM set. The *stream_user_data* is data associated to the + stream opened by this request and can be an arbitrary pointer, + which can be retrieved later by + `nghttp2_session_get_stream_user_data()`. + + This function returns assigned stream ID if it succeeds, or one of + the following negative error codes: + + :macro:`NGHTTP2_ERR_NOMEM` + Out of memory. + :macro:`NGHTTP2_ERR_STREAM_ID_NOT_AVAILABLE` + No stream ID is available because maximum stream ID was + reached. + + .. warning:: + + This function returns assigned stream ID if it succeeds. But + that stream is not opened yet. The application must not submit + frame to that stream ID before + :type:`nghttp2_before_frame_send_callback` is called for this + frame. + diff --git a/doc/nghttp2_submit_response.rst b/doc/nghttp2_submit_response.rst new file mode 100644 index 0000000..f01d892 --- /dev/null +++ b/doc/nghttp2_submit_response.rst @@ -0,0 +1,57 @@ + +nghttp2_submit_response +======================= + +Synopsis +-------- + +*#include * + +.. function:: int nghttp2_submit_response(nghttp2_session *session, int32_t stream_id, const nghttp2_nv *nva, size_t nvlen, const nghttp2_data_provider *data_prd) + + + Submits response HEADERS frame and optionally one or more DATA + frames against the stream *stream_id*. + + The *nva* is an array of name/value pair :type:`nghttp2_nv` with + *nvlen* elements. The application is responsible to include + required pseudo-header fields (header field whose name starts with + ":") in *nva* and must place pseudo-headers before regular header + fields. + + This function creates copies of all name/value pairs in *nva*. It + also lower-cases all names in *nva*. The order of elements in + *nva* is preserved. + + HTTP/2 specification has requirement about header fields in the + response HEADERS. See the specification for more details. + + If *data_prd* is not ``NULL``, it provides data which will be sent + in subsequent DATA frames. This function does not take ownership + of the *data_prd*. The function copies the members of the + *data_prd*. If *data_prd* is ``NULL``, HEADERS will have + END_STREAM flag set. + + This method can be used as normal HTTP response and push response. + When pushing a resource using this function, the *session* must be + configured using `nghttp2_session_server_new()` or its variants and + the target stream denoted by the *stream_id* must be reserved using + `nghttp2_submit_push_promise()`. + + To send non-final response headers (e.g., HTTP status 101), don't + use this function because this function half-closes the outbound + stream. Instead, use `nghttp2_submit_headers()` for this purpose. + + This function returns 0 if it succeeds, or one of the following + negative error codes: + + :macro:`NGHTTP2_ERR_NOMEM` + Out of memory. + :macro:`NGHTTP2_ERR_INVALID_ARGUMENT` + The *stream_id* is 0. + + .. warning:: + + Calling this function twice for the same stream ID may lead to + program crash. It is generally considered to a programming error + to commit response twice. diff --git a/doc/nghttp2_submit_rst_stream.rst b/doc/nghttp2_submit_rst_stream.rst new file mode 100644 index 0000000..a179424 --- /dev/null +++ b/doc/nghttp2_submit_rst_stream.rst @@ -0,0 +1,27 @@ + +nghttp2_submit_rst_stream +========================= + +Synopsis +-------- + +*#include * + +.. function:: int nghttp2_submit_rst_stream(nghttp2_session *session, uint8_t flags, int32_t stream_id, uint32_t error_code) + + + Submits RST_STREAM frame to cancel/reject the stream *stream_id* + with the error code *error_code*. + + The pre-defined error code is one of :macro:`nghttp2_error_code`. + + The *flags* is currently ignored and should be + :macro:`NGHTTP2_FLAG_NONE`. + + This function returns 0 if it succeeds, or one of the following + negative error codes: + + :macro:`NGHTTP2_ERR_NOMEM` + Out of memory. + :macro:`NGHTTP2_ERR_INVALID_ARGUMENT` + The *stream_id* is 0. diff --git a/doc/nghttp2_submit_settings.rst b/doc/nghttp2_submit_settings.rst new file mode 100644 index 0000000..dc4df55 --- /dev/null +++ b/doc/nghttp2_submit_settings.rst @@ -0,0 +1,41 @@ + +nghttp2_submit_settings +======================= + +Synopsis +-------- + +*#include * + +.. function:: int nghttp2_submit_settings(nghttp2_session *session, uint8_t flags, const nghttp2_settings_entry *iv, size_t niv) + + + Stores local settings and submits SETTINGS frame. The *iv* is the + pointer to the array of :type:`nghttp2_settings_entry`. The *niv* + indicates the number of :type:`nghttp2_settings_entry`. + + The *flags* is currently ignored and should be + :macro:`NGHTTP2_FLAG_NONE`. + + This function does not take ownership of the *iv*. This function + copies all the elements in the *iv*. + + While updating individual stream's local window size, if the window + size becomes strictly larger than NGHTTP2_MAX_WINDOW_SIZE, + RST_STREAM is issued against such a stream. + + SETTINGS with :macro:`NGHTTP2_FLAG_ACK` is automatically submitted + by the library and application could not send it at its will. + + This function returns 0 if it succeeds, or one of the following + negative error codes: + + :macro:`NGHTTP2_ERR_INVALID_ARGUMENT` + The *iv* contains invalid value (e.g., initial window size + strictly greater than (1 << 31) - 1. + :macro:`NGHTTP2_ERR_TOO_MANY_INFLIGHT_SETTINGS` + There is already another in-flight SETTINGS. Note that the + current implementation only allows 1 in-flight SETTINGS frame + without ACK flag set. + :macro:`NGHTTP2_ERR_NOMEM` + Out of memory. diff --git a/doc/nghttp2_submit_shutdown_notice.rst b/doc/nghttp2_submit_shutdown_notice.rst new file mode 100644 index 0000000..d6c28b8 --- /dev/null +++ b/doc/nghttp2_submit_shutdown_notice.rst @@ -0,0 +1,43 @@ + +nghttp2_submit_shutdown_notice +============================== + +Synopsis +-------- + +*#include * + +.. function:: int nghttp2_submit_shutdown_notice(nghttp2_session *session) + + + Signals to the client that the server started graceful shutdown + procedure. + + This function is only usable for server. If this function is + called with client side session, this function returns + :macro:`NGHTTP2_ERR_INVALID_STATE`. + + To gracefully shutdown HTTP/2 session, server should call this + function to send GOAWAY with last_stream_id (1u << 31) - 1. And + after some delay (e.g., 1 RTT), send another GOAWAY with the stream + ID that the server has some processing using + `nghttp2_submit_goaway()`. See also + `nghttp2_session_get_last_proc_stream_id()`. + + Unlike `nghttp2_submit_goaway()`, this function just sends GOAWAY + and does nothing more. This is a mere indication to the client + that session shutdown is imminent. The application should call + `nghttp2_submit_goaway()` with appropriate last_stream_id after + this call. + + If one or more GOAWAY frame have been already sent by either + `nghttp2_submit_goaway()` or `nghttp2_session_terminate_session()`, + this function has no effect. + + This function returns 0 if it succeeds, or one of the following + negative error codes: + + :macro:`NGHTTP2_ERR_NOMEM` + Out of memory. + :macro:`NGHTTP2_ERR_INVALID_STATE` + The *session* is initialized as client. diff --git a/doc/nghttp2_submit_trailer.rst b/doc/nghttp2_submit_trailer.rst new file mode 100644 index 0000000..420c6b9 --- /dev/null +++ b/doc/nghttp2_submit_trailer.rst @@ -0,0 +1,52 @@ + +nghttp2_submit_trailer +====================== + +Synopsis +-------- + +*#include * + +.. function:: int nghttp2_submit_trailer(nghttp2_session *session, int32_t stream_id, const nghttp2_nv *nva, size_t nvlen) + + + Submits trailer HEADERS against the stream *stream_id*. + + The *nva* is an array of name/value pair :type:`nghttp2_nv` with + *nvlen* elements. The application is responsible not to include + required pseudo-header fields (header field whose name starts with + ":") in *nva*. + + This function creates copies of all name/value pairs in *nva*. It + also lower-cases all names in *nva*. The order of elements in + *nva* is preserved. + + For server, trailer must be followed by response HEADERS or + response DATA. The library does not check that response HEADERS + has already sent and if `nghttp2_submit_trailer()` is called before + any response HEADERS submission (usually by + `nghttp2_submit_response()`), the content of *nva* will be sent as + reponse headers, which will result in error. + + This function has the same effect with `nghttp2_submit_headers()`, + with flags = :macro:`NGHTTP2_FLAG_END_HEADERS` and both pri_spec and + stream_user_data to NULL. + + To submit trailer after `nghttp2_submit_response()` is called, the + application has to specify :type:`nghttp2_data_provider` to + `nghttp2_submit_response()`. In side + :type:`nghttp2_data_source_read_callback`, when setting + :macro:`NGHTTP2_DATA_FLAG_EOF`, also set + :macro:`NGHTTP2_DATA_FLAG_NO_END_STREAM`. After that, the + application can send trailer using `nghttp2_submit_trailer()`. + `nghttp2_submit_trailer()` can be used inside + :type:`nghttp2_data_source_read_callback`. + + This function returns 0 if it succeeds and *stream_id* is -1. + Otherwise, this function returns 0 if it succeeds, or one of the + following negative error codes: + + :macro:`NGHTTP2_ERR_NOMEM` + Out of memory. + :macro:`NGHTTP2_ERR_INVALID_ARGUMENT` + The *stream_id* is 0. diff --git a/doc/nghttp2_submit_window_update.rst b/doc/nghttp2_submit_window_update.rst new file mode 100644 index 0000000..4410444 --- /dev/null +++ b/doc/nghttp2_submit_window_update.rst @@ -0,0 +1,43 @@ + +nghttp2_submit_window_update +============================ + +Synopsis +-------- + +*#include * + +.. function:: int nghttp2_submit_window_update(nghttp2_session *session, uint8_t flags, int32_t stream_id, int32_t window_size_increment) + + + Submits WINDOW_UPDATE frame. + + The *flags* is currently ignored and should be + :macro:`NGHTTP2_FLAG_NONE`. + + The *stream_id* is the stream ID to send this WINDOW_UPDATE. To + send connection level WINDOW_UPDATE, specify 0 to *stream_id*. + + If the *window_size_increment* is positive, the WINDOW_UPDATE with + that value as window_size_increment is queued. If the + *window_size_increment* is larger than the received bytes from the + remote endpoint, the local window size is increased by that + difference. + + If the *window_size_increment* is negative, the local window size + is decreased by -*window_size_increment*. If automatic + WINDOW_UPDATE is enabled + (`nghttp2_option_set_no_auto_window_update()`), and the library + decided that the WINDOW_UPDATE should be submitted, then + WINDOW_UPDATE is queued with the current received bytes count. + + If the *window_size_increment* is 0, the function does nothing and + returns 0. + + This function returns 0 if it succeeds, or one of the following + negative error codes: + + :macro:`NGHTTP2_ERR_FLOW_CONTROL` + The local window size overflow or gets negative. + :macro:`NGHTTP2_ERR_NOMEM` + Out of memory. diff --git a/doc/nghttp2_version.rst b/doc/nghttp2_version.rst new file mode 100644 index 0000000..3f16c54 --- /dev/null +++ b/doc/nghttp2_version.rst @@ -0,0 +1,17 @@ + +nghttp2_version +=============== + +Synopsis +-------- + +*#include * + +.. function:: nghttp2_info *nghttp2_version(int least_version) + + + Returns a pointer to a nghttp2_info struct with version information + about the run-time library in use. The *least_version* argument + can be set to a 24 bit numerical value for the least accepted + version number and if the condition is not met, this function will + return a ``NULL``. Pass in 0 to skip the version checking. diff --git a/doc/nghttp2ver.h.rst.in b/doc/nghttp2ver.h.rst.in new file mode 100644 index 0000000..c6aa779 --- /dev/null +++ b/doc/nghttp2ver.h.rst.in @@ -0,0 +1,4 @@ +nghttp2ver.h +============ + +.. literalinclude:: @top_builddir@/lib/includes/nghttp2/nghttp2ver.h diff --git a/doc/nghttpd.1 b/doc/nghttpd.1 new file mode 100644 index 0000000..24e7bb9 --- /dev/null +++ b/doc/nghttpd.1 @@ -0,0 +1,194 @@ +.\" Man page generated from reStructuredText. +. +.TH "NGHTTPD" "1" "May 16, 2015" "1.0.0" "nghttp2" +.SH NAME +nghttpd \- HTTP/2 experimental server +. +.nr rst2man-indent-level 0 +. +.de1 rstReportMargin +\\$1 \\n[an-margin] +level \\n[rst2man-indent-level] +level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] +- +\\n[rst2man-indent0] +\\n[rst2man-indent1] +\\n[rst2man-indent2] +.. +.de1 INDENT +.\" .rstReportMargin pre: +. RS \\$1 +. nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] +. nr rst2man-indent-level +1 +.\" .rstReportMargin post: +.. +.de UNINDENT +. RE +.\" indent \\n[an-margin] +.\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] +.nr rst2man-indent-level -1 +.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] +.in \\n[rst2man-indent\\n[rst2man-indent-level]]u +.. +.SH SYNOPSIS +.sp +\fBnghttpd\fP [OPTION]... [ ] +.SH DESCRIPTION +.sp +HTTP/2 experimental server +.INDENT 0.0 +.TP +.B +Specify listening port number. +.UNINDENT +.INDENT 0.0 +.TP +.B +Set path to server\(aqs private key. Required unless +\fI\%\-\-no\-tls\fP is specified. +.UNINDENT +.INDENT 0.0 +.TP +.B +Set path to server\(aqs certificate. Required unless +\fI\%\-\-no\-tls\fP is specified. +.UNINDENT +.SH OPTIONS +.INDENT 0.0 +.TP +.B \-a, \-\-address= +The address to bind to. If not specified the default IP +address determined by getaddrinfo is used. +.UNINDENT +.INDENT 0.0 +.TP +.B \-D, \-\-daemon +Run in a background. If \fI\%\-D\fP is used, the current working +directory is changed to \(aq\fI/\fP\(aq. Therefore if this option +is used, \fI\%\-d\fP option must be specified. +.UNINDENT +.INDENT 0.0 +.TP +.B \-V, \-\-verify\-client +The server sends a client certificate request. If the +client did not return a certificate, the handshake is +terminated. Currently, this option just requests a +client certificate and does not verify it. +.UNINDENT +.INDENT 0.0 +.TP +.B \-d, \-\-htdocs= +Specify document root. If this option is not specified, +the document root is the current working directory. +.UNINDENT +.INDENT 0.0 +.TP +.B \-v, \-\-verbose +Print debug information such as reception/ transmission +of frames and name/value pairs. +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-no\-tls +Disable SSL/TLS. +.UNINDENT +.INDENT 0.0 +.TP +.B \-c, \-\-header\-table\-size= +Specify decoder header table size. +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-color +Force colored log output. +.UNINDENT +.INDENT 0.0 +.TP +.B \-p, \-\-push== +Push resources s when is requested. +This option can be used repeatedly to specify multiple +push configurations. and s are +relative to document root. See \fI\%\-\-htdocs\fP option. +Example: \fI\%\-p\fP/=/foo.png \fI\%\-p\fP/doc=/bar.css +.UNINDENT +.INDENT 0.0 +.TP +.B \-b, \-\-padding= +Add at most bytes to a frame payload as padding. +Specify 0 to disable padding. +.UNINDENT +.INDENT 0.0 +.TP +.B \-m, \-\-max\-concurrent\-streams= +Set the maximum number of the concurrent streams in one +HTTP/2 session. +.sp +Default: \fB100\fP +.UNINDENT +.INDENT 0.0 +.TP +.B \-n, \-\-workers= +Set the number of worker threads. +.sp +Default: \fB1\fP +.UNINDENT +.INDENT 0.0 +.TP +.B \-e, \-\-error\-gzip +Make error response gzipped. +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-dh\-param\-file= +Path to file that contains DH parameters in PEM format. +Without this option, DHE cipher suites are not +available. +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-early\-response +Start sending response when request HEADERS is received, +rather than complete request is received. +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-trailer=
        +Add a trailer header to a response.
        must not +include pseudo header field (header field name starting +with \(aq:\(aq). The trailer is sent only if a response has +body part. Example: \fI\%\-\-trailer\fP \(aqfoo: bar\(aq. +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-hexdump +Display the incoming traffic in hexadecimal (Canonical +hex+ASCII display). If SSL/TLS is used, decrypted data +are used. +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-echo\-upload +Send back uploaded content if method is POST or PUT. +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-version +Display version information and exit. +.UNINDENT +.INDENT 0.0 +.TP +.B \-h, \-\-help +Display this help and exit. +.UNINDENT +.sp +The argument is an integer and an optional unit (e.g., 10K is +10 * 1024). Units are K, M and G (powers of 1024). +.SH SEE ALSO +.sp +\fInghttp(1)\fP, \fInghttpx(1)\fP, \fIh2load(1)\fP +.SH AUTHOR +Tatsuhiro Tsujikawa +.SH COPYRIGHT +2012, 2015, Tatsuhiro Tsujikawa +.\" Generated by docutils manpage writer. +. diff --git a/doc/nghttpd.1.rst b/doc/nghttpd.1.rst new file mode 100644 index 0000000..e9ac30d --- /dev/null +++ b/doc/nghttpd.1.rst @@ -0,0 +1,151 @@ + +.. GENERATED by help2rst.py. DO NOT EDIT DIRECTLY. + +.. program:: nghttpd + +nghttpd(1) +========== + +SYNOPSIS +-------- + +**nghttpd** [OPTION]... [ ] + +DESCRIPTION +----------- + +HTTP/2 experimental server + +.. describe:: + + Specify listening port number. + +.. describe:: + + + Set path to server's private key. Required unless + :option:`--no-tls` is specified. + +.. describe:: + + Set path to server's certificate. Required unless + :option:`--no-tls` is specified. + +OPTIONS +------- + +.. option:: -a, --address= + + The address to bind to. If not specified the default IP + address determined by getaddrinfo is used. + +.. option:: -D, --daemon + + Run in a background. If :option:`-D` is used, the current working + directory is changed to '*/*'. Therefore if this option + is used, :option:`-d` option must be specified. + +.. option:: -V, --verify-client + + The server sends a client certificate request. If the + client did not return a certificate, the handshake is + terminated. Currently, this option just requests a + client certificate and does not verify it. + +.. option:: -d, --htdocs= + + Specify document root. If this option is not specified, + the document root is the current working directory. + +.. option:: -v, --verbose + + Print debug information such as reception/ transmission + of frames and name/value pairs. + +.. option:: --no-tls + + Disable SSL/TLS. + +.. option:: -c, --header-table-size= + + Specify decoder header table size. + +.. option:: --color + + Force colored log output. + +.. option:: -p, --push== + + Push resources s when is requested. + This option can be used repeatedly to specify multiple + push configurations. and s are + relative to document root. See :option:`--htdocs` option. + Example: :option:`-p`\/=/foo.png :option:`-p`\/doc=/bar.css + +.. option:: -b, --padding= + + Add at most bytes to a frame payload as padding. + Specify 0 to disable padding. + +.. option:: -m, --max-concurrent-streams= + + Set the maximum number of the concurrent streams in one + HTTP/2 session. + + Default: ``100`` + +.. option:: -n, --workers= + + Set the number of worker threads. + + Default: ``1`` + +.. option:: -e, --error-gzip + + Make error response gzipped. + +.. option:: --dh-param-file= + + Path to file that contains DH parameters in PEM format. + Without this option, DHE cipher suites are not + available. + +.. option:: --early-response + + Start sending response when request HEADERS is received, + rather than complete request is received. + +.. option:: --trailer=
        + + Add a trailer header to a response.
        must not + include pseudo header field (header field name starting + with ':'). The trailer is sent only if a response has + body part. Example: :option:`--trailer` 'foo: bar'. + +.. option:: --hexdump + + Display the incoming traffic in hexadecimal (Canonical + hex+ASCII display). If SSL/TLS is used, decrypted data + are used. + +.. option:: --echo-upload + + Send back uploaded content if method is POST or PUT. + +.. option:: --version + + Display version information and exit. + +.. option:: -h, --help + + Display this help and exit. + + + +The argument is an integer and an optional unit (e.g., 10K is +10 * 1024). Units are K, M and G (powers of 1024). + +SEE ALSO +-------- + +:manpage:`nghttp(1)`, :manpage:`nghttpx(1)`, :manpage:`h2load(1)` diff --git a/doc/nghttpx-howto.rst.in b/doc/nghttpx-howto.rst.in new file mode 100644 index 0000000..082ce51 --- /dev/null +++ b/doc/nghttpx-howto.rst.in @@ -0,0 +1 @@ +.. include:: @top_srcdir@/doc/sources/nghttpx-howto.rst diff --git a/doc/nghttpx.1 b/doc/nghttpx.1 new file mode 100644 index 0000000..e42ec76 --- /dev/null +++ b/doc/nghttpx.1 @@ -0,0 +1,899 @@ +.\" Man page generated from reStructuredText. +. +.TH "NGHTTPX" "1" "May 16, 2015" "1.0.0" "nghttp2" +.SH NAME +nghttpx \- HTTP/2 experimental proxy +. +.nr rst2man-indent-level 0 +. +.de1 rstReportMargin +\\$1 \\n[an-margin] +level \\n[rst2man-indent-level] +level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] +- +\\n[rst2man-indent0] +\\n[rst2man-indent1] +\\n[rst2man-indent2] +.. +.de1 INDENT +.\" .rstReportMargin pre: +. RS \\$1 +. nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] +. nr rst2man-indent-level +1 +.\" .rstReportMargin post: +.. +.de UNINDENT +. RE +.\" indent \\n[an-margin] +.\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] +.nr rst2man-indent-level -1 +.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] +.in \\n[rst2man-indent\\n[rst2man-indent-level]]u +.. +.SH SYNOPSIS +.sp +\fBnghttpx\fP [OPTIONS]... [ ] +.SH DESCRIPTION +.sp +A reverse proxy for HTTP/2, HTTP/1 and SPDY. +.INDENT 0.0 +.TP +.B +Set path to server\(aqs private key. Required unless \fI\%\-p\fP, +\fI\%\-\-client\fP or \fI\%\-\-frontend\-no\-tls\fP are given. +.UNINDENT +.INDENT 0.0 +.TP +.B +Set path to server\(aqs certificate. Required unless \fI\%\-p\fP, +\fI\%\-\-client\fP or \fI\%\-\-frontend\-no\-tls\fP are given. To make OCSP +stapling work, this must be absolute path. +.UNINDENT +.SH OPTIONS +.sp +The options are categorized into several groups. +.SS Connections +.INDENT 0.0 +.TP +.B \-b, \-\-backend= +Set backend host and port. The multiple backend +addresses are accepted by repeating this option. UNIX +domain socket can be specified by prefixing path name +with "unix:" (e.g., unix:/var/run/backend.sock) +.sp +Default: \fB127.0.0.1,80\fP +.UNINDENT +.INDENT 0.0 +.TP +.B \-f, \-\-frontend= +Set frontend host and port. If is \(aq*\(aq, it +assumes all addresses including both IPv4 and IPv6. +UNIX domain socket can be specified by prefixing path +name with "unix:" (e.g., unix:/var/run/nghttpx.sock) +.sp +Default: \fB*,3000\fP +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-backlog= +Set listen backlog size. +.sp +Default: \fB512\fP +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-backend\-ipv4 +Resolve backend hostname to IPv4 address only. +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-backend\-ipv6 +Resolve backend hostname to IPv6 address only. +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-backend\-http\-proxy\-uri= +Specify proxy URI in the form +\fI\%http:/\fP/[:@]:. If a proxy +requires authentication, specify and . +Note that they must be properly percent\-encoded. This +proxy is used when the backend connection is HTTP/2. +First, make a CONNECT request to the proxy and it +connects to the backend on behalf of nghttpx. This +forms tunnel. After that, nghttpx performs SSL/TLS +handshake with the downstream through the tunnel. The +timeouts when connecting and making CONNECT request can +be specified by \fI\%\-\-backend\-read\-timeout\fP and +\fI\%\-\-backend\-write\-timeout\fP options. +.UNINDENT +.SS Performance +.INDENT 0.0 +.TP +.B \-n, \-\-workers= +Set the number of worker threads. +.sp +Default: \fB1\fP +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-read\-rate= +Set maximum average read rate on frontend connection. +Setting 0 to this option means read rate is unlimited. +.sp +Default: \fB0\fP +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-read\-burst= +Set maximum read burst size on frontend connection. +Setting 0 to this option means read burst size is +unlimited. +.sp +Default: \fB0\fP +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-write\-rate= +Set maximum average write rate on frontend connection. +Setting 0 to this option means write rate is unlimited. +.sp +Default: \fB0\fP +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-write\-burst= +Set maximum write burst size on frontend connection. +Setting 0 to this option means write burst size is +unlimited. +.sp +Default: \fB0\fP +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-worker\-read\-rate= +Set maximum average read rate on frontend connection per +worker. Setting 0 to this option means read rate is +unlimited. Not implemented yet. +.sp +Default: \fB0\fP +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-worker\-read\-burst= +Set maximum read burst size on frontend connection per +worker. Setting 0 to this option means read burst size +is unlimited. Not implemented yet. +.sp +Default: \fB0\fP +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-worker\-write\-rate= +Set maximum average write rate on frontend connection +per worker. Setting 0 to this option means write rate +is unlimited. Not implemented yet. +.sp +Default: \fB0\fP +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-worker\-write\-burst= +Set maximum write burst size on frontend connection per +worker. Setting 0 to this option means write burst size +is unlimited. Not implemented yet. +.sp +Default: \fB0\fP +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-worker\-frontend\-connections= +Set maximum number of simultaneous connections frontend +accepts. Setting 0 means unlimited. +.sp +Default: \fB0\fP +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-backend\-http2\-connections\-per\-worker= +Set maximum number of HTTP/2 connections per worker. +The default value is 0, which means the number of +backend addresses specified by \fI\%\-b\fP option. +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-backend\-http1\-connections\-per\-host= +Set maximum number of backend concurrent HTTP/1 +connections per host. This option is meaningful when \fI\%\-s\fP +option is used. To limit the number of connections per +frontend for default mode, use +\fI\%\-\-backend\-http1\-connections\-per\-frontend\fP\&. +.sp +Default: \fB8\fP +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-backend\-http1\-connections\-per\-frontend= +Set maximum number of backend concurrent HTTP/1 +connections per frontend. This option is only used for +default mode. 0 means unlimited. To limit the number +of connections per host for HTTP/2 or SPDY proxy mode +(\-s option), use \fI\%\-\-backend\-http1\-connections\-per\-host\fP\&. +.sp +Default: \fB0\fP +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-rlimit\-nofile= +Set maximum number of open files (RLIMIT_NOFILE) to . +If 0 is given, nghttpx does not set the limit. +.sp +Default: \fB0\fP +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-backend\-request\-buffer= +Set buffer size used to store backend request. +.sp +Default: \fB16K\fP +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-backend\-response\-buffer= +Set buffer size used to store backend response. +.sp +Default: \fB16K\fP +.UNINDENT +.SS Timeout +.INDENT 0.0 +.TP +.B \-\-frontend\-http2\-read\-timeout= +Specify read timeout for HTTP/2 and SPDY frontend +connection. +.sp +Default: \fB3m\fP +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-frontend\-read\-timeout= +Specify read timeout for HTTP/1.1 frontend connection. +.sp +Default: \fB3m\fP +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-frontend\-write\-timeout= +Specify write timeout for all frontend connections. +.sp +Default: \fB30s\fP +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-stream\-read\-timeout= +Specify read timeout for HTTP/2 and SPDY streams. 0 +means no timeout. +.sp +Default: \fB0\fP +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-stream\-write\-timeout= +Specify write timeout for HTTP/2 and SPDY streams. 0 +means no timeout. +.sp +Default: \fB0\fP +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-backend\-read\-timeout= +Specify read timeout for backend connection. +.sp +Default: \fB3m\fP +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-backend\-write\-timeout= +Specify write timeout for backend connection. +.sp +Default: \fB30s\fP +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-backend\-keep\-alive\-timeout= +Specify keep\-alive timeout for backend connection. +.sp +Default: \fB2s\fP +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-listener\-disable\-timeout= +After accepting connection failed, connection listener +is disabled for a given amount of time. Specifying 0 +disables this feature. +.sp +Default: \fB0\fP +.UNINDENT +.SS SSL/TLS +.INDENT 0.0 +.TP +.B \-\-ciphers= +Set allowed cipher list. The format of the string is +described in OpenSSL ciphers(1). +.UNINDENT +.INDENT 0.0 +.TP +.B \-k, \-\-insecure +Don\(aqt verify backend server\(aqs certificate if \fI\%\-p\fP, +\fI\%\-\-client\fP or \fI\%\-\-http2\-bridge\fP are given and +\fI\%\-\-backend\-no\-tls\fP is not given. +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-cacert= +Set path to trusted CA certificate file if \fI\%\-p\fP, \fI\%\-\-client\fP +or \fI\%\-\-http2\-bridge\fP are given and \fI\%\-\-backend\-no\-tls\fP is not +given. The file must be in PEM format. It can contain +multiple certificates. If the linked OpenSSL is +configured to load system wide certificates, they are +loaded at startup regardless of this option. +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-private\-key\-passwd\-file= +Path to file that contains password for the server\(aqs +private key. If none is given and the private key is +password protected it\(aqll be requested interactively. +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-subcert=: +Specify additional certificate and private key file. +nghttpx will choose certificates based on the hostname +indicated by client using TLS SNI extension. This +option can be used multiple times. To make OCSP +stapling work, must be absolute path. +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-backend\-tls\-sni\-field= +Explicitly set the content of the TLS SNI extension. +This will default to the backend HOST name. +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-dh\-param\-file= +Path to file that contains DH parameters in PEM format. +Without this option, DHE cipher suites are not +available. +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-npn\-list= +Comma delimited list of ALPN protocol identifier sorted +in the order of preference. That means most desirable +protocol comes first. This is used in both ALPN and +NPN. The parameter must be delimited by a single comma +only and any white spaces are treated as a part of +protocol string. +.sp +Default: \fBh2,h2\-16,h2\-14,spdy/3.1,http/1.1\fP +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-verify\-client +Require and verify client certificate. +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-verify\-client\-cacert= +Path to file that contains CA certificates to verify +client certificate. The file must be in PEM format. It +can contain multiple certificates. +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-client\-private\-key\-file= +Path to file that contains client private key used in +backend client authentication. +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-client\-cert\-file= +Path to file that contains client certificate used in +backend client authentication. +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-tls\-proto\-list= +Comma delimited list of SSL/TLS protocol to be enabled. +The following protocols are available: TLSv1.2, TLSv1.1 +and TLSv1.0. The name matching is done in +case\-insensitive manner. The parameter must be +delimited by a single comma only and any white spaces +are treated as a part of protocol string. +.sp +Default: \fBTLSv1.2,TLSv1.1\fP +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-tls\-ticket\-key\-file= +Path to file that contains 48 bytes random data to +construct TLS session ticket parameters. This options +can be used repeatedly to specify multiple ticket +parameters. If several files are given, only the first +key is used to encrypt TLS session tickets. Other keys +are accepted but server will issue new session ticket +with first key. This allows session key rotation. +Please note that key rotation does not occur +automatically. User should rearrange files or change +options values and restart nghttpx gracefully. If +opening or reading given file fails, all loaded keys are +discarded and it is treated as if none of this option is +given. If this option is not given or an error occurred +while opening or reading a file, key is generated +automatically and renewed every 12hrs. At most 2 keys +are stored in memory. +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-fetch\-ocsp\-response\-file= +Path to fetch\-ocsp\-response script file. It should be +absolute path. +.sp +Default: \fB/usr/local/share/nghttp2/fetch\-ocsp\-response\fP +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-ocsp\-update\-interval= +Set interval to update OCSP response cache. +.sp +Default: \fB4h\fP +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-no\-ocsp +Disable OCSP stapling. +.UNINDENT +.SS HTTP/2 and SPDY +.INDENT 0.0 +.TP +.B \-c, \-\-http2\-max\-concurrent\-streams= +Set the maximum number of the concurrent streams in one +HTTP/2 and SPDY session. +.sp +Default: \fB100\fP +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-frontend\-http2\-window\-bits= +Sets the per\-stream initial window size of HTTP/2 SPDY +frontend connection. For HTTP/2, the size is 2**\-1. +For SPDY, the size is 2**. +.sp +Default: \fB16\fP +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-frontend\-http2\-connection\-window\-bits= +Sets the per\-connection window size of HTTP/2 and SPDY +frontend connection. For HTTP/2, the size is +2**\-1. For SPDY, the size is 2**. +.sp +Default: \fB16\fP +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-frontend\-no\-tls +Disable SSL/TLS on frontend connections. +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-backend\-http2\-window\-bits= +Sets the initial window size of HTTP/2 backend +connection to 2**\-1. +.sp +Default: \fB16\fP +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-backend\-http2\-connection\-window\-bits= +Sets the per\-connection window size of HTTP/2 backend +connection to 2**\-1. +.sp +Default: \fB16\fP +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-backend\-no\-tls +Disable SSL/TLS on backend connections. +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-http2\-no\-cookie\-crumbling +Don\(aqt crumble cookie header field. +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-padding= +Add at most bytes to a HTTP/2 frame payload as +padding. Specify 0 to disable padding. This option is +meant for debugging purpose and not intended to enhance +protocol security. +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-no\-server\-push +Disable HTTP/2 server push. Server push is only +supported by default mode and HTTP/2 frontend. SPDY +frontend does not support server push. +.UNINDENT +.SS Mode +.INDENT 0.0 +.TP +.B (default mode) +Accept HTTP/2, SPDY and HTTP/1.1 over SSL/TLS. If +\fI\%\-\-frontend\-no\-tls\fP is used, accept HTTP/2 and HTTP/1.1. +The incoming HTTP/1.1 connection can be upgraded to +HTTP/2 through HTTP Upgrade. The protocol to the +backend is HTTP/1.1. +.UNINDENT +.INDENT 0.0 +.TP +.B \-s, \-\-http2\-proxy +Like default mode, but enable secure proxy mode. +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-http2\-bridge +Like default mode, but communicate with the backend in +HTTP/2 over SSL/TLS. Thus the incoming all connections +are converted to HTTP/2 connection and relayed to the +backend. See \fI\%\-\-backend\-http\-proxy\-uri\fP option if you are +behind the proxy and want to connect to the outside +HTTP/2 proxy. +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-client +Accept HTTP/2 and HTTP/1.1 without SSL/TLS. The +incoming HTTP/1.1 connection can be upgraded to HTTP/2 +connection through HTTP Upgrade. The protocol to the +backend is HTTP/2. To use nghttpx as a forward proxy, +use \fI\%\-p\fP option instead. +.UNINDENT +.INDENT 0.0 +.TP +.B \-p, \-\-client\-proxy +Like \fI\%\-\-client\fP option, but it also requires the request +path from frontend must be an absolute URI, suitable for +use as a forward proxy. +.UNINDENT +.SS Logging +.INDENT 0.0 +.TP +.B \-L, \-\-log\-level= +Set the severity level of log output. must be +one of INFO, NOTICE, WARN, ERROR and FATAL. +.sp +Default: \fBNOTICE\fP +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-accesslog\-file= +Set path to write access log. To reopen file, send USR1 +signal to nghttpx. +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-accesslog\-syslog +Send access log to syslog. If this option is used, +\fI\%\-\-accesslog\-file\fP option is ignored. +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-accesslog\-format= +Specify format string for access log. The default +format is combined format. The following variables are +available: +.INDENT 7.0 +.IP \(bu 2 +$remote_addr: client IP address. +.IP \(bu 2 +$time_local: local time in Common Log format. +.IP \(bu 2 +$time_iso8601: local time in ISO 8601 format. +.IP \(bu 2 +$request: HTTP request line. +.IP \(bu 2 +$status: HTTP response status code. +.IP \(bu 2 +$body_bytes_sent: the number of bytes sent to client +as response body. +.IP \(bu 2 +$http_: value of HTTP request header where +\(aq_\(aq in is replaced with \(aq\-\(aq. +.IP \(bu 2 +$remote_port: client port. +.IP \(bu 2 +$server_port: server port. +.IP \(bu 2 +$request_time: request processing time in seconds with +milliseconds resolution. +.IP \(bu 2 +$pid: PID of the running process. +.IP \(bu 2 +$alpn: ALPN identifier of the protocol which generates +the response. For HTTP/1, ALPN is always http/1.1, +regardless of minor version. +.UNINDENT +.sp +Default: \fB$remote_addr \- \- [$time_local] "$request" $status $body_bytes_sent "$http_referer" "$http_user_agent"\fP +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-errorlog\-file= +Set path to write error log. To reopen file, send USR1 +signal to nghttpx. stderr will be redirected to the +error log file unless \fI\%\-\-errorlog\-syslog\fP is used. +.sp +Default: \fB/dev/stderr\fP +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-errorlog\-syslog +Send error log to syslog. If this option is used, +\fI\%\-\-errorlog\-file\fP option is ignored. +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-syslog\-facility= +Set syslog facility to . +.sp +Default: \fBdaemon\fP +.UNINDENT +.SS HTTP +.INDENT 0.0 +.TP +.B \-\-add\-x\-forwarded\-for +Append X\-Forwarded\-For header field to the downstream +request. +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-strip\-incoming\-x\-forwarded\-for +Strip X\-Forwarded\-For header field from inbound client +requests. +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-no\-via +Don\(aqt append to Via header field. If Via header field +is received, it is left unaltered. +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-no\-location\-rewrite +Don\(aqt rewrite location header field on \fI\%\-\-http2\-bridge\fP, +\fI\%\-\-client\fP and default mode. For \fI\%\-\-http2\-proxy\fP and +\fI\%\-\-client\-proxy\fP mode, location header field will not be +altered regardless of this option. +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-no\-host\-rewrite +Don\(aqt rewrite host and :authority header fields on +\fI\%\-\-http2\-bridge\fP, \fI\%\-\-client\fP and default mode. For +\fI\%\-\-http2\-proxy\fP and \fI\%\-\-client\-proxy\fP mode, these headers +will not be altered regardless of this option. +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-altsvc= +Specify protocol ID, port, host and origin of +alternative service. and are optional. +They are advertised in alt\-svc header field only in +HTTP/1.1 frontend. This option can be used multiple +times to specify multiple alternative services. +Example: \fI\%\-\-altsvc\fP=h2,443 +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-add\-response\-header=
        +Specify additional header field to add to response +header set. This option just appends header field and +won\(aqt replace anything already set. This option can be +used several times to specify multiple header fields. +Example: \fI\%\-\-add\-response\-header\fP="foo: bar" +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-header\-field\-buffer= +Set maximum buffer size for incoming HTTP header field +list. This is the sum of header name and value in +bytes. +.sp +Default: \fB64K\fP +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-max\-header\-fields= +Set maximum number of incoming HTTP header fields, which +appear in one request or response header field list. +.sp +Default: \fB100\fP +.UNINDENT +.SS Debug +.INDENT 0.0 +.TP +.B \-\-frontend\-http2\-dump\-request\-header= +Dumps request headers received by HTTP/2 frontend to the +file denoted in . The output is done in HTTP/1 +header field format and each header block is followed by +an empty line. This option is not thread safe and MUST +NOT be used with option \fI\%\-n\fP, where >= 2. +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-frontend\-http2\-dump\-response\-header= +Dumps response headers sent from HTTP/2 frontend to the +file denoted in . The output is done in HTTP/1 +header field format and each header block is followed by +an empty line. This option is not thread safe and MUST +NOT be used with option \fI\%\-n\fP, where >= 2. +.UNINDENT +.INDENT 0.0 +.TP +.B \-o, \-\-frontend\-frame\-debug +Print HTTP/2 frames in frontend to stderr. This option +is not thread safe and MUST NOT be used with option +\fI\%\-n\fP=N, where N >= 2. +.UNINDENT +.SS Process +.INDENT 0.0 +.TP +.B \-D, \-\-daemon +Run in a background. If \fI\%\-D\fP is used, the current working +directory is changed to \(aq\fI/\fP\(aq. +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-pid\-file= +Set path to save PID of this program. +.UNINDENT +.INDENT 0.0 +.TP +.B \-\-user= +Run this program as . This option is intended to +be used to drop root privileges. +.UNINDENT +.SS Misc +.INDENT 0.0 +.TP +.B \-\-conf= +Load configuration from . +.sp +Default: \fB/etc/nghttpx/nghttpx.conf\fP +.UNINDENT +.INDENT 0.0 +.TP +.B \-v, \-\-version +Print version and exit. +.UNINDENT +.INDENT 0.0 +.TP +.B \-h, \-\-help +Print this help and exit. +.UNINDENT +.sp +The argument is an integer and an optional unit (e.g., 10K is +10 * 1024). Units are K, M and G (powers of 1024). +.sp +The argument is an integer and an optional unit (e.g., 1s +is 1 second and 500ms is 500 milliseconds). Units are h, m, s or ms +(hours, minutes, seconds and milliseconds, respectively). If a unit +is omitted, a second is used as unit. +.SH FILES +.INDENT 0.0 +.TP +.B \fI/etc/nghttpx/nghttpx.conf\fP +The default configuration file path nghttpx searches at startup. +The configuration file path can be changed using \fI\%\-\-conf\fP +option. +.sp +Those lines which are staring \fB#\fP are treated as comment. +.sp +The option name in the configuration file is the long command\-line +option name with leading \fB\-\-\fP stripped (e.g., \fBfrontend\fP). Put +\fB=\fP between option name and value. Don\(aqt put extra leading or +trailing spaces. +.sp +The options which do not take argument in the command\-line \fItake\fP +argument in the configuration file. Specify \fByes\fP as an argument +(e.g., \fBhttp2\-proxy=yes\fP). If other string is given, it is +ignored. +.sp +To specify private key and certificate file which are given as +positional arguments in command\-line, use \fBprivate\-key\-file\fP and +\fBcertificate\-file\fP\&. +.sp +\fI\%\-\-conf\fP option cannot be used in the configuration file and +will be ignored if specified. +.UNINDENT +.SH SIGNALS +.INDENT 0.0 +.TP +.B SIGQUIT +Shutdown gracefully. First accept pending connections and stop +accepting connection. After all connections are handled, nghttpx +exits. +.TP +.B SIGUSR1 +Reopen log files. +.TP +.B SIGUSR2 +Fork and execute nghttpx. It will execute the binary in the same +path with same command\-line arguments and environment variables. +After new process comes up, sending SIGQUIT to the original process +to perform hot swapping. +.UNINDENT +.SH SERVER PUSH +.sp +nghttpx supports HTTP/2 server push in default mode. nghttpx looks +for Link header field (\fI\%RFC 5988\fP) in response headers from +backend server and extracts URI\-reference with parameter +\fBrel=preload\fP (see \fI\%preload\fP) +and pushes those URIs to the frontend client. Here is a sample Link +header field to initiate server push: +.INDENT 0.0 +.INDENT 3.5 +.sp +.nf +.ft C +Link: ; rel=preload +Link: ; rel=preload +.ft P +.fi +.UNINDENT +.UNINDENT +.sp +Currently, the following restrictions are applied for server push: +.INDENT 0.0 +.IP 1. 3 +URI\-reference must not contain authority. If it exists, it is not +pushed. \fB/fonts/font.woff\fP and \fBcss/theme.css\fP are eligible to +be pushed. \fBhttps://example.org/fonts/font.woff\fP and +\fB//example.org/css/theme.css\fP are not. +.IP 2. 3 +The associated stream must have method "GET" or "POST". The +associated stream\(aqs status code must be 200. +.UNINDENT +.sp +These limitations may be loosened in the future release. +.SH UNIX DOMAIN SOCKET +.sp +nghttpx supports UNIX domain socket with a filename for both frontend +and backend connections. +.sp +Please note that current nghttpx implementation does not delete a +socket with a filename. And on start up, if nghttpx detects that the +specified socket already exists in the file system, nghttpx first +deletes it. However, if SIGUSR2 is used to execute new binary and +both old and new configurations use same filename, new binary does not +delete the socket and continues to use it. +.SH OCSP STAPLING +.sp +OCSP query is done using external perl script \fBfetch\-ocsp\-response\fP, +which has been developed as part of h2o project +(\fI\%https://github.com/h2o/h2o\fP). +.sp +The script file is usually installed under +\fB$(prefix)/share/nghttp2/\fP directory. The actual path to script can +be customized using \fI\%\-\-fetch\-ocsp\-response\-file\fP option. +.SH SEE ALSO +.sp +\fInghttp(1)\fP, \fInghttpd(1)\fP, \fIh2load(1)\fP +.SH AUTHOR +Tatsuhiro Tsujikawa +.SH COPYRIGHT +2012, 2015, Tatsuhiro Tsujikawa +.\" Generated by docutils manpage writer. +. diff --git a/doc/nghttpx.1.rst b/doc/nghttpx.1.rst new file mode 100644 index 0000000..ad3f915 --- /dev/null +++ b/doc/nghttpx.1.rst @@ -0,0 +1,811 @@ + +.. GENERATED by help2rst.py. DO NOT EDIT DIRECTLY. + +.. program:: nghttpx + +nghttpx(1) +========== + +SYNOPSIS +-------- + +**nghttpx** [OPTIONS]... [ ] + +DESCRIPTION +----------- + +A reverse proxy for HTTP/2, HTTP/1 and SPDY. + +.. describe:: + + + Set path to server's private key. Required unless :option:`-p`\, + :option:`--client` or :option:`\--frontend-no-tls` are given. + +.. describe:: + + Set path to server's certificate. Required unless :option:`-p`\, + :option:`--client` or :option:`\--frontend-no-tls` are given. To make OCSP + stapling work, this must be absolute path. + + +OPTIONS +------- + +The options are categorized into several groups. + +Connections +~~~~~~~~~~~ + +.. option:: -b, --backend= + + Set backend host and port. The multiple backend + addresses are accepted by repeating this option. UNIX + domain socket can be specified by prefixing path name + with "unix:" (e.g., unix:/var/run/backend.sock) + + Default: ``127.0.0.1,80`` + +.. option:: -f, --frontend= + + Set frontend host and port. If is '\*', it + assumes all addresses including both IPv4 and IPv6. + UNIX domain socket can be specified by prefixing path + name with "unix:" (e.g., unix:/var/run/nghttpx.sock) + + Default: ``*,3000`` + +.. option:: --backlog= + + Set listen backlog size. + + Default: ``512`` + +.. option:: --backend-ipv4 + + Resolve backend hostname to IPv4 address only. + +.. option:: --backend-ipv6 + + Resolve backend hostname to IPv6 address only. + +.. option:: --backend-http-proxy-uri= + + Specify proxy URI in the form + http://[:@]:. If a proxy + requires authentication, specify and . + Note that they must be properly percent-encoded. This + proxy is used when the backend connection is HTTP/2. + First, make a CONNECT request to the proxy and it + connects to the backend on behalf of nghttpx. This + forms tunnel. After that, nghttpx performs SSL/TLS + handshake with the downstream through the tunnel. The + timeouts when connecting and making CONNECT request can + be specified by :option:`--backend-read-timeout` and + :option:`--backend-write-timeout` options. + + +Performance +~~~~~~~~~~~ + +.. option:: -n, --workers= + + Set the number of worker threads. + + Default: ``1`` + +.. option:: --read-rate= + + Set maximum average read rate on frontend connection. + Setting 0 to this option means read rate is unlimited. + + Default: ``0`` + +.. option:: --read-burst= + + Set maximum read burst size on frontend connection. + Setting 0 to this option means read burst size is + unlimited. + + Default: ``0`` + +.. option:: --write-rate= + + Set maximum average write rate on frontend connection. + Setting 0 to this option means write rate is unlimited. + + Default: ``0`` + +.. option:: --write-burst= + + Set maximum write burst size on frontend connection. + Setting 0 to this option means write burst size is + unlimited. + + Default: ``0`` + +.. option:: --worker-read-rate= + + Set maximum average read rate on frontend connection per + worker. Setting 0 to this option means read rate is + unlimited. Not implemented yet. + + Default: ``0`` + +.. option:: --worker-read-burst= + + Set maximum read burst size on frontend connection per + worker. Setting 0 to this option means read burst size + is unlimited. Not implemented yet. + + Default: ``0`` + +.. option:: --worker-write-rate= + + Set maximum average write rate on frontend connection + per worker. Setting 0 to this option means write rate + is unlimited. Not implemented yet. + + Default: ``0`` + +.. option:: --worker-write-burst= + + Set maximum write burst size on frontend connection per + worker. Setting 0 to this option means write burst size + is unlimited. Not implemented yet. + + Default: ``0`` + +.. option:: --worker-frontend-connections= + + Set maximum number of simultaneous connections frontend + accepts. Setting 0 means unlimited. + + Default: ``0`` + +.. option:: --backend-http2-connections-per-worker= + + Set maximum number of HTTP/2 connections per worker. + The default value is 0, which means the number of + backend addresses specified by :option:`-b` option. + +.. option:: --backend-http1-connections-per-host= + + Set maximum number of backend concurrent HTTP/1 + connections per host. This option is meaningful when :option:`-s` + option is used. To limit the number of connections per + frontend for default mode, use + :option:`--backend-http1-connections-per-frontend`\. + + Default: ``8`` + +.. option:: --backend-http1-connections-per-frontend= + + Set maximum number of backend concurrent HTTP/1 + connections per frontend. This option is only used for + default mode. 0 means unlimited. To limit the number + of connections per host for HTTP/2 or SPDY proxy mode + (-s option), use :option:`--backend-http1-connections-per-host`\. + + Default: ``0`` + +.. option:: --rlimit-nofile= + + Set maximum number of open files (RLIMIT_NOFILE) to . + If 0 is given, nghttpx does not set the limit. + + Default: ``0`` + +.. option:: --backend-request-buffer= + + Set buffer size used to store backend request. + + Default: ``16K`` + +.. option:: --backend-response-buffer= + + Set buffer size used to store backend response. + + Default: ``16K`` + + +Timeout +~~~~~~~ + +.. option:: --frontend-http2-read-timeout= + + Specify read timeout for HTTP/2 and SPDY frontend + connection. + + Default: ``3m`` + +.. option:: --frontend-read-timeout= + + Specify read timeout for HTTP/1.1 frontend connection. + + Default: ``3m`` + +.. option:: --frontend-write-timeout= + + Specify write timeout for all frontend connections. + + Default: ``30s`` + +.. option:: --stream-read-timeout= + + Specify read timeout for HTTP/2 and SPDY streams. 0 + means no timeout. + + Default: ``0`` + +.. option:: --stream-write-timeout= + + Specify write timeout for HTTP/2 and SPDY streams. 0 + means no timeout. + + Default: ``0`` + +.. option:: --backend-read-timeout= + + Specify read timeout for backend connection. + + Default: ``3m`` + +.. option:: --backend-write-timeout= + + Specify write timeout for backend connection. + + Default: ``30s`` + +.. option:: --backend-keep-alive-timeout= + + Specify keep-alive timeout for backend connection. + + Default: ``2s`` + +.. option:: --listener-disable-timeout= + + After accepting connection failed, connection listener + is disabled for a given amount of time. Specifying 0 + disables this feature. + + Default: ``0`` + + +SSL/TLS +~~~~~~~ + +.. option:: --ciphers= + + Set allowed cipher list. The format of the string is + described in OpenSSL ciphers(1). + +.. option:: -k, --insecure + + Don't verify backend server's certificate if :option:`-p`\, + :option:`--client` or :option:`\--http2-bridge` are given and + :option:`--backend-no-tls` is not given. + +.. option:: --cacert= + + Set path to trusted CA certificate file if :option:`-p`\, :option:`--client` + or :option:`--http2-bridge` are given and :option:`\--backend-no-tls` is not + given. The file must be in PEM format. It can contain + multiple certificates. If the linked OpenSSL is + configured to load system wide certificates, they are + loaded at startup regardless of this option. + +.. option:: --private-key-passwd-file= + + Path to file that contains password for the server's + private key. If none is given and the private key is + password protected it'll be requested interactively. + +.. option:: --subcert=: + + Specify additional certificate and private key file. + nghttpx will choose certificates based on the hostname + indicated by client using TLS SNI extension. This + option can be used multiple times. To make OCSP + stapling work, must be absolute path. + +.. option:: --backend-tls-sni-field= + + Explicitly set the content of the TLS SNI extension. + This will default to the backend HOST name. + +.. option:: --dh-param-file= + + Path to file that contains DH parameters in PEM format. + Without this option, DHE cipher suites are not + available. + +.. option:: --npn-list= + + Comma delimited list of ALPN protocol identifier sorted + in the order of preference. That means most desirable + protocol comes first. This is used in both ALPN and + NPN. The parameter must be delimited by a single comma + only and any white spaces are treated as a part of + protocol string. + + Default: ``h2,h2-16,h2-14,spdy/3.1,http/1.1`` + +.. option:: --verify-client + + Require and verify client certificate. + +.. option:: --verify-client-cacert= + + Path to file that contains CA certificates to verify + client certificate. The file must be in PEM format. It + can contain multiple certificates. + +.. option:: --client-private-key-file= + + Path to file that contains client private key used in + backend client authentication. + +.. option:: --client-cert-file= + + Path to file that contains client certificate used in + backend client authentication. + +.. option:: --tls-proto-list= + + Comma delimited list of SSL/TLS protocol to be enabled. + The following protocols are available: TLSv1.2, TLSv1.1 + and TLSv1.0. The name matching is done in + case-insensitive manner. The parameter must be + delimited by a single comma only and any white spaces + are treated as a part of protocol string. + + Default: ``TLSv1.2,TLSv1.1`` + +.. option:: --tls-ticket-key-file= + + Path to file that contains 48 bytes random data to + construct TLS session ticket parameters. This options + can be used repeatedly to specify multiple ticket + parameters. If several files are given, only the first + key is used to encrypt TLS session tickets. Other keys + are accepted but server will issue new session ticket + with first key. This allows session key rotation. + Please note that key rotation does not occur + automatically. User should rearrange files or change + options values and restart nghttpx gracefully. If + opening or reading given file fails, all loaded keys are + discarded and it is treated as if none of this option is + given. If this option is not given or an error occurred + while opening or reading a file, key is generated + automatically and renewed every 12hrs. At most 2 keys + are stored in memory. + +.. option:: --fetch-ocsp-response-file= + + Path to fetch-ocsp-response script file. It should be + absolute path. + + Default: ``/usr/local/share/nghttp2/fetch-ocsp-response`` + +.. option:: --ocsp-update-interval= + + Set interval to update OCSP response cache. + + Default: ``4h`` + +.. option:: --no-ocsp + + Disable OCSP stapling. + + +HTTP/2 and SPDY +~~~~~~~~~~~~~~~ + +.. option:: -c, --http2-max-concurrent-streams= + + Set the maximum number of the concurrent streams in one + HTTP/2 and SPDY session. + + Default: ``100`` + +.. option:: --frontend-http2-window-bits= + + Sets the per-stream initial window size of HTTP/2 SPDY + frontend connection. For HTTP/2, the size is 2\*\*-1. + For SPDY, the size is 2\*\*. + + Default: ``16`` + +.. option:: --frontend-http2-connection-window-bits= + + Sets the per-connection window size of HTTP/2 and SPDY + frontend connection. For HTTP/2, the size is + 2**-1. For SPDY, the size is 2\*\*. + + Default: ``16`` + +.. option:: --frontend-no-tls + + Disable SSL/TLS on frontend connections. + +.. option:: --backend-http2-window-bits= + + Sets the initial window size of HTTP/2 backend + connection to 2\*\*-1. + + Default: ``16`` + +.. option:: --backend-http2-connection-window-bits= + + Sets the per-connection window size of HTTP/2 backend + connection to 2\*\*-1. + + Default: ``16`` + +.. option:: --backend-no-tls + + Disable SSL/TLS on backend connections. + +.. option:: --http2-no-cookie-crumbling + + Don't crumble cookie header field. + +.. option:: --padding= + + Add at most bytes to a HTTP/2 frame payload as + padding. Specify 0 to disable padding. This option is + meant for debugging purpose and not intended to enhance + protocol security. + +.. option:: --no-server-push + + Disable HTTP/2 server push. Server push is only + supported by default mode and HTTP/2 frontend. SPDY + frontend does not support server push. + + +Mode +~~~~ + +.. describe:: (default mode) + + + Accept HTTP/2, SPDY and HTTP/1.1 over SSL/TLS. If + :option:`--frontend-no-tls` is used, accept HTTP/2 and HTTP/1.1. + The incoming HTTP/1.1 connection can be upgraded to + HTTP/2 through HTTP Upgrade. The protocol to the + backend is HTTP/1.1. + +.. option:: -s, --http2-proxy + + Like default mode, but enable secure proxy mode. + +.. option:: --http2-bridge + + Like default mode, but communicate with the backend in + HTTP/2 over SSL/TLS. Thus the incoming all connections + are converted to HTTP/2 connection and relayed to the + backend. See :option:`--backend-http-proxy-uri` option if you are + behind the proxy and want to connect to the outside + HTTP/2 proxy. + +.. option:: --client + + Accept HTTP/2 and HTTP/1.1 without SSL/TLS. The + incoming HTTP/1.1 connection can be upgraded to HTTP/2 + connection through HTTP Upgrade. The protocol to the + backend is HTTP/2. To use nghttpx as a forward proxy, + use :option:`-p` option instead. + +.. option:: -p, --client-proxy + + Like :option:`--client` option, but it also requires the request + path from frontend must be an absolute URI, suitable for + use as a forward proxy. + + +Logging +~~~~~~~ + +.. option:: -L, --log-level= + + Set the severity level of log output. must be + one of INFO, NOTICE, WARN, ERROR and FATAL. + + Default: ``NOTICE`` + +.. option:: --accesslog-file= + + Set path to write access log. To reopen file, send USR1 + signal to nghttpx. + +.. option:: --accesslog-syslog + + Send access log to syslog. If this option is used, + :option:`--accesslog-file` option is ignored. + +.. option:: --accesslog-format= + + Specify format string for access log. The default + format is combined format. The following variables are + available: + + * $remote_addr: client IP address. + * $time_local: local time in Common Log format. + * $time_iso8601: local time in ISO 8601 format. + * $request: HTTP request line. + * $status: HTTP response status code. + * $body_bytes_sent: the number of bytes sent to client + as response body. + * $http_: value of HTTP request header where + '_' in is replaced with '-'. + * $remote_port: client port. + * $server_port: server port. + * $request_time: request processing time in seconds with + milliseconds resolution. + * $pid: PID of the running process. + * $alpn: ALPN identifier of the protocol which generates + the response. For HTTP/1, ALPN is always http/1.1, + regardless of minor version. + + + Default: ``$remote_addr - - [$time_local] "$request" $status $body_bytes_sent "$http_referer" "$http_user_agent"`` + +.. option:: --errorlog-file= + + Set path to write error log. To reopen file, send USR1 + signal to nghttpx. stderr will be redirected to the + error log file unless :option:`--errorlog-syslog` is used. + + Default: ``/dev/stderr`` + +.. option:: --errorlog-syslog + + Send error log to syslog. If this option is used, + :option:`--errorlog-file` option is ignored. + +.. option:: --syslog-facility= + + Set syslog facility to . + + Default: ``daemon`` + + +HTTP +~~~~ + +.. option:: --add-x-forwarded-for + + Append X-Forwarded-For header field to the downstream + request. + +.. option:: --strip-incoming-x-forwarded-for + + Strip X-Forwarded-For header field from inbound client + requests. + +.. option:: --no-via + + Don't append to Via header field. If Via header field + is received, it is left unaltered. + +.. option:: --no-location-rewrite + + Don't rewrite location header field on :option:`--http2-bridge`\, + :option:`--client` and default mode. For :option:`\--http2-proxy` and + :option:`--client-proxy` mode, location header field will not be + altered regardless of this option. + +.. option:: --no-host-rewrite + + Don't rewrite host and :authority header fields on + :option:`--http2-bridge`\, :option:`--client` and default mode. For + :option:`--http2-proxy` and :option:`\--client-proxy` mode, these headers + will not be altered regardless of this option. + +.. option:: --altsvc= + + Specify protocol ID, port, host and origin of + alternative service. and are optional. + They are advertised in alt-svc header field only in + HTTP/1.1 frontend. This option can be used multiple + times to specify multiple alternative services. + Example: :option:`--altsvc`\=h2,443 + +.. option:: --add-response-header=
        + + Specify additional header field to add to response + header set. This option just appends header field and + won't replace anything already set. This option can be + used several times to specify multiple header fields. + Example: :option:`--add-response-header`\="foo: bar" + +.. option:: --header-field-buffer= + + Set maximum buffer size for incoming HTTP header field + list. This is the sum of header name and value in + bytes. + + Default: ``64K`` + +.. option:: --max-header-fields= + + Set maximum number of incoming HTTP header fields, which + appear in one request or response header field list. + + Default: ``100`` + + +Debug +~~~~~ + +.. option:: --frontend-http2-dump-request-header= + + Dumps request headers received by HTTP/2 frontend to the + file denoted in . The output is done in HTTP/1 + header field format and each header block is followed by + an empty line. This option is not thread safe and MUST + NOT be used with option :option:`-n`\, where >= 2. + +.. option:: --frontend-http2-dump-response-header= + + Dumps response headers sent from HTTP/2 frontend to the + file denoted in . The output is done in HTTP/1 + header field format and each header block is followed by + an empty line. This option is not thread safe and MUST + NOT be used with option :option:`-n`\, where >= 2. + +.. option:: -o, --frontend-frame-debug + + Print HTTP/2 frames in frontend to stderr. This option + is not thread safe and MUST NOT be used with option + :option:`-n`\=N, where N >= 2. + + +Process +~~~~~~~ + +.. option:: -D, --daemon + + Run in a background. If :option:`-D` is used, the current working + directory is changed to '*/*'. + +.. option:: --pid-file= + + Set path to save PID of this program. + +.. option:: --user= + + Run this program as . This option is intended to + be used to drop root privileges. + + +Misc +~~~~ + +.. option:: --conf= + + Load configuration from . + + Default: ``/etc/nghttpx/nghttpx.conf`` + +.. option:: -v, --version + + Print version and exit. + +.. option:: -h, --help + + Print this help and exit. + + + +The argument is an integer and an optional unit (e.g., 10K is +10 * 1024). Units are K, M and G (powers of 1024). + +The argument is an integer and an optional unit (e.g., 1s +is 1 second and 500ms is 500 milliseconds). Units are h, m, s or ms +(hours, minutes, seconds and milliseconds, respectively). If a unit +is omitted, a second is used as unit. + +FILES +----- + +*/etc/nghttpx/nghttpx.conf* + The default configuration file path nghttpx searches at startup. + The configuration file path can be changed using :option:`--conf` + option. + + Those lines which are staring ``#`` are treated as comment. + + The option name in the configuration file is the long command-line + option name with leading ``--`` stripped (e.g., ``frontend``). Put + ``=`` between option name and value. Don't put extra leading or + trailing spaces. + + The options which do not take argument in the command-line *take* + argument in the configuration file. Specify ``yes`` as an argument + (e.g., ``http2-proxy=yes``). If other string is given, it is + ignored. + + To specify private key and certificate file which are given as + positional arguments in command-line, use ``private-key-file`` and + ``certificate-file``. + + :option:`--conf` option cannot be used in the configuration file and + will be ignored if specified. + +SIGNALS +------- + +SIGQUIT + Shutdown gracefully. First accept pending connections and stop + accepting connection. After all connections are handled, nghttpx + exits. + +SIGUSR1 + Reopen log files. + +SIGUSR2 + Fork and execute nghttpx. It will execute the binary in the same + path with same command-line arguments and environment variables. + After new process comes up, sending SIGQUIT to the original process + to perform hot swapping. + +SERVER PUSH +----------- + +nghttpx supports HTTP/2 server push in default mode. nghttpx looks +for Link header field (`RFC 5988 +`_) in response headers from +backend server and extracts URI-reference with parameter +``rel=preload`` (see `preload +`_) +and pushes those URIs to the frontend client. Here is a sample Link +header field to initiate server push: + +.. code-block:: http + + Link: ; rel=preload + Link: ; rel=preload + +Currently, the following restrictions are applied for server push: + +1. URI-reference must not contain authority. If it exists, it is not + pushed. ``/fonts/font.woff`` and ``css/theme.css`` are eligible to + be pushed. ``https://example.org/fonts/font.woff`` and + ``//example.org/css/theme.css`` are not. + +2. The associated stream must have method "GET" or "POST". The + associated stream's status code must be 200. + +These limitations may be loosened in the future release. + +UNIX DOMAIN SOCKET +------------------ + +nghttpx supports UNIX domain socket with a filename for both frontend +and backend connections. + +Please note that current nghttpx implementation does not delete a +socket with a filename. And on start up, if nghttpx detects that the +specified socket already exists in the file system, nghttpx first +deletes it. However, if SIGUSR2 is used to execute new binary and +both old and new configurations use same filename, new binary does not +delete the socket and continues to use it. + +OCSP STAPLING +------------- + +OCSP query is done using external perl script ``fetch-ocsp-response``, +which has been developed as part of h2o project +(https://github.com/h2o/h2o). + +The script file is usually installed under +``$(prefix)/share/nghttp2/`` directory. The actual path to script can +be customized using :option:`--fetch-ocsp-response-file` option. + +SEE ALSO +-------- + +:manpage:`nghttp(1)`, :manpage:`nghttpd(1)`, :manpage:`h2load(1)` diff --git a/doc/package_README.rst.in b/doc/package_README.rst.in new file mode 100644 index 0000000..dfa6b2d --- /dev/null +++ b/doc/package_README.rst.in @@ -0,0 +1 @@ +.. include:: @top_srcdir@/README.rst diff --git a/doc/programmers-guide.rst b/doc/programmers-guide.rst new file mode 100644 index 0000000..94c72e9 --- /dev/null +++ b/doc/programmers-guide.rst @@ -0,0 +1,105 @@ +Programmers' Guide +================== + +Includes +-------- + +To use the public APIs, include ``nghttp2/nghttp2.h``:: + + #include + +The header files are also available online: :doc:`nghttp2.h` and +:doc:`nghttp2ver.h`. + +Remarks +------- + +Do not call `nghttp2_session_send()`, `nghttp2_session_mem_send()`, +`nghttp2_session_recv()` or `nghttp2_session_mem_recv()` from the +nghttp2 callback functions directly or indirectly. It will lead to the +crash. You can submit requests or frames in the callbacks then call +these functions outside the callbacks. + +`nghttp2_session_send()` and `nghttp2_session_mem_send()` send first +24 bytes of client magic string (MAGIC) +(:macro:`NGHTTP2_CLIENT_MAGIC`) on client configuration. The +applications are responsible to send SETTINGS frame as part of +connection preface using `nghttp2_submit_settings()`. Similarly, +`nghttp2_session_recv()` and `nghttp2_session_mem_recv()` consume +MAGIC on server configuration unless +`nghttp2_option_set_no_recv_client_magic()` is used with nonzero +option value. + +.. _http-messaging: + +HTTP Messaging +-------------- + +By default, nghttp2 library checks HTTP messaging rules described in +`HTTP/2 specification, section 8 +`_. +Everything described in that section is not validated however. We +briefly describe what the library does in this area. In the following +description, without loss of generality we omit CONTINUATION frame +since they must follow HEADERS frame and are processed atomically. In +other words, they are just one big HEADERS frame. To disable these +validations, use `nghttp2_option_set_no_http_messaging()`. + +For HTTP request, including those carried by PUSH_PROMISE, HTTP +message starts with one HEADERS frame containing request headers. It +is followed by zero or more DATA frames containing request body, which +is followed by zero or one HEADERS containing trailer headers. The +request headers must include ":scheme", ":method" and ":path" pseudo +header fields unless ":method" is not "CONNECT". ":authority" is +optional, but nghttp2 requires either ":authority" or "Host" header +field must be present. If ":method" is "CONNECT", the request headers +must include ":method" and ":authority" and must omit ":scheme" and +":path". + +For HTTP response, HTTP message starts with zero or more HEADERS +frames containing non-final response (status code 1xx). They are +followed by one HEADERS frame containing final response headers +(non-1xx). It is followed by zero or more DATA frames containing +response body, which is followed by zero or one HEADERS containing +trailer headers. The non-final and final response headers must +contain ":status" pseudo header field containing 3 digits only. + +All request and response headers must include exactly one valid value +for each pseudo header field. Additionally nghttp2 requires all +request headers must not include more than one "Host" header field. + +HTTP/2 prohibits connection-specific header fields. The following +header fields must not appear: "Connection", "Keep-Alive", +"Proxy-Connection", "Transfer-Encoding" and "Upgrade". Additionally, +"TE" header field must not include any value other than "trailers". + +Each header field name and value must obey the field-name and +field-value production rules described in `RFC 7230, section +3.2. `_. +Additionally, all field name must be lower cased. While the pseudo +header fields must satisfy these rules, we just ignore illegal regular +headers (this means that these header fields are not passed to +application callback). This is because these illegal header fields +are floating around in existing internet and resetting stream just +because of this may break many web sites. This is especially true if +we forward to or translate from HTTP/1 traffic. + +For "http" or "https" URIs, ":path" pseudo header fields must start +with "/". The only exception is OPTIONS request, in that case, "*" is +allowed in ":path" pseudo header field to represent system-wide +OPTIONS request. + +With the above validations, nghttp2 library guarantees that header +field name passed to `nghttp2_on_header_callback()` is not empty. +Also required pseudo headers are all present and not empty. + +nghttp2 enforces "Content-Length" validation as well. All request or +response headers must not contain more than one "Content-Length" +header field. If "Content-Length" header field is present, it must be +parsed as 64 bit signed integer. The sum of data length in the +following DATA frames must match with the number in "Content-Length" +header field if it is present (this does not include padding bytes). + +Any deviation results in stream error of type PROTOCOL_ERROR. If +error is found in PUSH_PROMISE frame, stream error is raised against +promised stream. diff --git a/doc/python-apiref.rst.in b/doc/python-apiref.rst.in new file mode 100644 index 0000000..5fd40de --- /dev/null +++ b/doc/python-apiref.rst.in @@ -0,0 +1 @@ +.. include:: @top_srcdir@/doc/sources/python-apiref.rst diff --git a/doc/sources/building-android-binary.rst b/doc/sources/building-android-binary.rst new file mode 100644 index 0000000..99aec39 --- /dev/null +++ b/doc/sources/building-android-binary.rst @@ -0,0 +1,135 @@ +Building Android binary +======================= + +In this article, we briefly describe how to build Android binary using +`Android NDK `_ +cross-compiler on Debian Linux. + +The easiest way to build android binary is use Dockerfile.android. +See Dockerfile.android for more details. If you cannot use +Dockerfile.android for whatever reason, continue to read the rest of +this article. + +We offer ``android-config`` and ``android-make`` scripts to make the +build easier. To make these script work, NDK toolchain must be +installed in the following way. First, let us introduce +``ANDROID_HOME`` environment variable. We need to install toolchain +under ``$ANDROID_HOME/toolchain``. An user can freely choose the path +for ``ANDROID_HOME``. For example, to install toolchain under +``$ANDROID_HOME/toolchain``, do this in the the directory where NDK is +unpacked:: + + $ build/tools/make-standalone-toolchain.sh \ + --install-dir=$ANDROID_HOME/toolchain \ + --toolchain=arm-linux-androideabi-4.9 \ + --llvm-version=3.5 \ + --platform=android-16 + +The additional flag ``--system=linux-x86_64`` may be required if you +are using x86_64 system. + +The platform level is not important here because we don't use Android +specific C/C++ API. + +The dependent libraries, such as OpenSSL and libev should be built +with the toolchain and installed under ``$ANDROID_HOME/usr/local``. +We recommend to build these libraries as static library to make the +deployment easier. libxml2 support is currently disabled. + +We use zlib which comes with Android NDK, so we don't have to build it +by ourselves. + +If SPDY support is required for nghttpx and h2load, build and install +spdylay as well. + +Before running ``android-config`` and ``android-make``, +``ANDROID_HOME`` environment variable must be set to point to the +correct path. Also add ``$ANDROID_HOME/toolchain/bin`` to ``PATH``:: + + $ export PATH=$PATH:$ANDROID_HOME/toolchain/bin + +To configure OpenSSL, use the following script: + +.. code-block:: sh + + #!/bin/sh + + if [ -z "$ANDROID_HOME" ]; then + echo 'No $ANDROID_HOME specified.' + exit 1 + fi + PREFIX=$ANDROID_HOME/usr/local + TOOLCHAIN=$ANDROID_HOME/toolchain + PATH=$TOOLCHAIN/bin:$PATH + + export CROSS_COMPILE=$TOOLCHAIN/bin/arm-linux-androideabi- + ./Configure --prefix=$PREFIX android + +And run ``make install`` to build and install. + +We cannot compile libev without modification. Apply `this patch +`_ before +configuring libev. This patch is for libev-4.19. After applying the +patch, to configure libev, use the following script: + +.. code-block:: sh + + #!/bin/sh + + if [ -z "$ANDROID_HOME" ]; then + echo 'No $ANDROID_HOME specified.' + exit 1 + fi + PREFIX=$ANDROID_HOME/usr/local + TOOLCHAIN=$ANDROID_HOME/toolchain + PATH=$TOOLCHAIN/bin:$PATH + + ./configure \ + --host=arm-linux-androideabi \ + --build=`dpkg-architecture -qDEB_BUILD_GNU_TYPE` \ + --prefix=$PREFIX \ + --disable-shared \ + --enable-static \ + CPPFLAGS=-I$PREFIX/include \ + LDFLAGS=-L$PREFIX/lib + +And run ``make install`` to build and install. + +To configure spdylay, use the following script: + +.. code-block:: sh + + if [ -z "$ANDROID_HOME" ]; then + echo 'No $ANDROID_HOME specified.' + exit 1 + fi + PREFIX=$ANDROID_HOME/usr/local + TOOLCHAIN=$ANDROID_HOME/toolchain + PATH=$TOOLCHAIN/bin:$PATH + + ./configure \ + --disable-shared \ + --host=arm-linux-androideabi \ + --build=`dpkg-architecture -qDEB_BUILD_GNU_TYPE` \ + --prefix=$PREFIX \ + --without-libxml2 \ + --disable-src \ + --disable-examples \ + CPPFLAGS="-I$PREFIX/include" \ + PKG_CONFIG_LIBDIR="$PREFIX/lib/pkgconfig" \ + LDFLAGS="-L$PREFIX/lib" + +And run ``make install`` to build and install. After spdylay +installation, edit $ANDROID_HOME/usr/local/lib/pkgconfig/libspdylay.pc +and remove the following line:: + + Requires.private: zlib + +After prerequisite libraries are prepared, run ``android-config`` and +then ``android-make`` to compile nghttp2 source files. + +If all went well, application binaries, such as nghttpx, are created +under src directory. Strip debugging information from the binary +using the following command:: + + $ arm-linux-androideabi-strip src/nghttpx diff --git a/doc/sources/contribute.rst b/doc/sources/contribute.rst new file mode 100644 index 0000000..14a11e2 --- /dev/null +++ b/doc/sources/contribute.rst @@ -0,0 +1,57 @@ +Contribution Guidelines +======================= + +[This text was composed based on 1.2. License section of curl/libcurl +project.] + +When contributing with code, you agree to put your changes and new +code under the same license nghttp2 is already using unless stated and +agreed otherwise. + +When changing existing source code, you do not alter the copyright of +the original file(s). The copyright will still be owned by the +original creator(s) or those who have been assigned copyright by the +original author(s). + +By submitting a patch to the nghttp2 project, you are assumed to have +the right to the code and to be allowed by your employer or whatever +to hand over that patch/code to us. We will credit you for your +changes as far as possible, to give credit but also to keep a trace +back to who made what changes. Please always provide us with your +full real name when contributing! + +Coding style +------------ + +We use clang-format to format source code consistently. The +clang-format configuration file .clang-format is located at the root +directory. Since clang-format produces slightly different results +between versions, we currently use clang-format which comes with +clang-3.5. + +To detect any violation to the coding style, we recommend to setup git +pre-commit hook to check coding style of the changes you introduced. +The pre-commit file is located at the root directory. Copy it under +.git/hooks and make sure that it is executable. The pre-commit script +uses clang-format-diff.py to detect any style errors. If it is not in +your PATH or it exists under different name (e.g., +clang-format-diff-3.5 in debian), either add it to PATH variable or +add git option ``clangformatdiff.binary`` to point to the script. + +For emacs users, integrating clang-format to emacs is very easy. +clang-format.el should come with clang distribution. If it is not +found, download it from `here +`_. +And add these lines to your .emacs file: + +.. code-block:: lisp + + ;; From + ;; https://code.google.com/p/chromium/wiki/Emacs#Use_Google's_C++_style! + (load "//clang-format.el") + (add-hook 'c-mode-common-hook + (function (lambda () (local-set-key (kbd "TAB") + 'clang-format-region)))) + +You can find other editor integration in +http://clang.llvm.org/docs/ClangFormat.html. diff --git a/doc/sources/h2load-howto.rst b/doc/sources/h2load-howto.rst new file mode 100644 index 0000000..7c07194 --- /dev/null +++ b/doc/sources/h2load-howto.rst @@ -0,0 +1,91 @@ +h2load - HTTP/2 benchmarking tool - HOW-TO +========================================== + +h2load is benchmarking tool for HTTP/2. If built with +spdylay (http://tatsuhiro-t.github.io/spdylay/) library, it also +supports SPDY protocol. It supports SSL/TLS and clear text for both +HTTP/2 and SPDY. + +Basic Usage +----------- + +In order to set benchmark settings, specify following 3 options. + +``-n`` + The number of total requests. Default: 1 + +``-c`` + The number of concurrent clients. Default: 1 + +``-m`` + The max concurrent streams to issue per client. + If ``auto`` is given, the number of given URIs is used. + Default: ``auto`` + +Here is a command-line to perform benchmark to URI \https://localhost +using total 100000 requests, 100 concurrent clients and 10 max +concurrent streams:: + + $ h2load -n100000 -c100 -m10 https://localhost + +The benchmarking result looks like this:: + + finished in 0 sec, 385 millisec and 851 microsec, 2591 req/s, 1689 kbytes/s + requests: 1000 total, 1000 started, 1000 done, 1000 succeeded, 0 failed, 0 errored + status codes: 1000 2xx, 0 3xx, 0 4xx, 0 5xx + traffic: 667500 bytes total, 28700 bytes headers, 612000 bytes data + +The number of ``failed`` is the number of requests returned with non +2xx status. The number of ``error`` is the number of ``failed`` plus +the number of requests which failed with connection error. + +The number of ``total`` in ``traffic`` is the received application +data. If SSL/TLS is used, this number is calculated after decryption. +The number of ``headers`` is the sum of payload size of response +HEADERS (or SYN_REPLY for SPDY). This number comes before +decompressing header block. The number of ``data`` is the sum of +response body. + +Flow Control +------------ + +HTTP/2 and SPDY/3 or later employ flow control and it may affect +benchmarking results. By default, h2load uses large enough flow +control window, which effectively disables flow control. To adjust +receiver flow control window size, there are following options: + +``-w`` + Sets the stream level initial window size to + (2**)-1. For SPDY, 2** is used instead. + +``-W`` + Sets the connection level initial window size to + (2**)-1. For SPDY, if is strictly less + than 16, this option is ignored. Otherwise + 2** is used for SPDY. + +Multi-Threading +--------------- + +Sometimes benchmarking client itself becomes a bottleneck. To remedy +this situation, use ``-t`` option to specify the number of native +thread to use. + +``-t`` + The number of native threads. Default: 1 + +Selecting protocol for clear text +--------------------------------- + +By default, if \http:// URI is given, HTTP/2 protocol is used. To +change the protocol to use for clear text, use ``-p`` option. + +Multiple URIs +------------- + +If multiple URIs are specified, they are used in round robin manner. + +.. note:: + + Please note that h2load uses scheme, host and port in the first URI + and ignores those parts in the rest of the URIs. diff --git a/doc/sources/index.rst b/doc/sources/index.rst new file mode 100644 index 0000000..14a469f --- /dev/null +++ b/doc/sources/index.rst @@ -0,0 +1,53 @@ +.. nghttp2 documentation master file, created by + sphinx-quickstart on Sun Mar 11 22:57:49 2012. + You can adapt this file completely to your liking, but it should at least + contain the root `toctree` directive. + +nghttp2 - HTTP/2 C Library +============================ + +This is an experimental implementation of Hypertext Transfer Protocol +version 2. + +The project is hosted at `github.com/tatsuhiro-t/nghttp2 `_. + +Contents: + +.. toctree:: + :maxdepth: 2 + + package_README + contribute + building-android-binary + tutorial-client + tutorial-server + tutorial-hpack + nghttp.1 + nghttpd.1 + nghttpx.1 + h2load.1 + nghttpx-howto + h2load-howto + programmers-guide + apiref + libnghttp2_asio + python-apiref + nghttp2.h + nghttp2ver.h + asio_http2_server.h + asio_http2_client.h + asio_http2.h + Source + Issues + nghttp2.org + +Released Versions +================= + +https://github.com/tatsuhiro-t/nghttp2/releases + +Resources +--------- + +* HTTP/2 https://tools.ietf.org/html/rfc7540 +* HPACK https://tools.ietf.org/html/rfc7541 diff --git a/doc/sources/libnghttp2_asio.rst b/doc/sources/libnghttp2_asio.rst new file mode 100644 index 0000000..76999f9 --- /dev/null +++ b/doc/sources/libnghttp2_asio.rst @@ -0,0 +1,433 @@ +libnghttp2_asio: High level HTTP/2 C++ library +============================================== + +libnghttp2_asio is C++ library built on top of libnghttp2 and provides +high level abstraction API to build HTTP/2 applications. It depends +on Boost::ASIO library and OpenSSL. Currently libnghttp2_asio +provides server and client side API. + +libnghttp2_asio is not built by default. Use ``--enable-asio-lib`` +configure flag to build libnghttp2_asio. The required Boost libraries +are: + +* Boost::Asio +* Boost::System +* Boost::Thread + +We have 3 header files for this library: + +* :doc:`asio_http2_server.h` +* :doc:`asio_http2_client.h` +* :doc:`asio_http2.h` + +asio_http2.h is included from the other two files. + +To build a program with libnghttp2_asio, link to the following +libraries:: + + -lnghttp2_asio -lboost_system + +If ``boost::asio::ssl`` is used in application code, OpenSSL is also +required in link line:: + + -lnghttp2_asio -lboost_system -lssl -lcrypto + +Server API +---------- + +To use server API, first include following header file: + +.. code-block:: cpp + + #include + +Also take a look at that header file :doc:`asio_http2_server.h`. + +Server API is designed to build HTTP/2 server very easily to utilize +C++11 anonymous function and closure. The bare minimum example of +HTTP/2 server looks like this: + +.. code-block:: cpp + + using namespace nghttp2::asio_http2; + using namespace nghttp2::asio_http2::server; + + int main(int argc, char *argv[]) { + boost::system::error_code ec; + http2 server; + + server.handle("/", [](const request &req, const response &res) { + res.write_head(200); + res.end("hello, world\n"); + }); + + if (server.listen_and_serve(ec, "localhost", "3000")) { + std::cerr << "error: " << ec.message() << std::endl; + } + } + +First we instantiate ``nghttp2::asio_http2::server::http2`` object. +``nghttp2::asio_http2::server::http2::handle`` function registers +pattern and its handler function. In this example, we register "/" as +pattern, which matches all requests. Then call +``nghttp2::asio_http2::server::http2::listen_and_serve`` function with +address and port to listen to. + +The ``req`` and ``res`` represent HTTP request and response +respectively. ``nghttp2::asio_http2_::server::response::write_head`` +constructs HTTP response header fields. The first argument is HTTP +status code, in the above example, which is 200. The second argument, +which is omitted in the above example, is additional header fields to +send. + +``nghttp2::asio_http2::server::response::end`` sends response body. +In the above example, we send string "hello, world". + +The life time of req and res object ends after the callback set by +``nghttp2::asio_http2::server::response::on_close`` function. +Application must not use those objects after this call. + +Serving static files and enabling SSL/TLS ++++++++++++++++++++++++++++++++++++++++++ + +In this example, we serve a couple of static files and also enable +SSL/TLS. + +.. code-block:: cpp + + #include + + using namespace nghttp2::asio_http2; + using namespace nghttp2::asio_http2::server; + + int main(int argc, char *argv[]) { + boost::system::error_code ec; + boost::asio::ssl::context tls(boost::asio::ssl::context::sslv23); + + tls.use_private_key_file("server.key", boost::asio::ssl::context::pem); + tls.use_certificate_chain_file("server.crt"); + + configure_tls_context_easy(ec, tls); + + http2 server; + + server.handle("/index.html", [](const request &req, const response &res) { + res.write_head(200); + res.end(file_generator("index.html")); + }); + + if (server.listen_and_serve(ec, tls, "localhost", "3000")) { + std::cerr << "error: " << ec.message() << std::endl; + } + } + +We first create ``boost::asio::ssl::context`` object and set path to +private key file and certificate file. +``nghttp2::asio_http2::server::configure_tls_context_easy`` function +configures SSL/TLS context object for HTTP/2 server use, including NPN +callbacks. + +In the above example, if request path is "/index.html", we serve +index.html file in the current working directory. +``nghttp2::asio_http2::server::response::end`` has overload to take +function of type ``nghttp2::asio_http2::generator_cb`` and application +pass its implementation to generate response body. For the +convenience, libnghttp2_asio library provides +``nghttp2::asio_http2::file_generator`` function to generate function +to server static file. If other resource is requested, server +automatically responds with 404 status code. + +Server push ++++++++++++ + +Server push is also supported. + +.. code-block:: cpp + + #include + + using namespace nghttp2::asio_http2; + using namespace nghttp2::asio_http2::server; + + int main(int argc, char *argv[]) { + boost::system::error_code ec; + boost::asio::ssl::context tls(boost::asio::ssl::context::sslv23); + + tls.use_private_key_file("server.key", boost::asio::ssl::context::pem); + tls.use_certificate_chain_file("server.crt"); + + configure_tls_context_easy(ec, tls); + + http2 server; + + std::string style_css = "h1 { color: green; }"; + + server.handle("/", [&style_css](const request &req, const response &res) { + boost::system::error_code ec; + auto push = res.push(ec, "GET", "/style.css"); + push->write_head(200); + push->end(style_css); + + res.write_head(200); + res.end(R"( + + HTTP/2 FTW + +

        This should be green

        + + )"); + }); + + server.handle("/style.css", + [&style_css](const request &req, const response &res) { + res.write_head(200); + res.end(style_css); + }); + + if (server.listen_and_serve(ec, tls, "localhost", "3000")) { + std::cerr << "error: " << ec.message() << std::endl; + } + } + +When client requested any resource other than "/style.css", we push +"/style.css". To push resource, call +``nghttp2::asio_http2::server::response::push`` function with desired +method and path. It returns another response object and use its +functions to send push response. + +Enable multi-threading +++++++++++++++++++++++ + +Enabling multi-threading is very easy. Just call +``nghttp2::asio_http2::server::http2::num_threads`` function with the +desired number of threads: + +.. code-block:: cpp + + http2 server; + + // Use 4 native threads + server.num_threads(4); + +Client API +---------- + +To use client API, first include following header file: + +.. code-block:: cpp + + #include + +Also take a look at that header file :doc:`asio_http2_client.h`. + +Here is the sample client code to access HTTP/2 server and print out +response header fields and response body to the console screen: + +.. code-block:: cpp + + #include + + #include + + using boost::asio::ip::tcp; + + using namespace nghttp2::asio_http2; + using namespace nghttp2::asio_http2::client; + + int main(int argc, char *argv[]) { + boost::system::error_code ec; + boost::asio::io_service io_service; + + // connect to localhost:3000 + session sess(io_service, "localhost", "3000"); + + sess.on_connect([&sess](tcp::resolver::iterator endpoint_it) { + boost::system::error_code ec; + + auto req = sess.submit(ec, "GET", "http://localhost:3000/"); + + req->on_response([](const response &res) { + // print status code and response header fields. + std::cerr << "HTTP/2 " << res.status_code() << std::endl; + for (auto &kv : res.header()) { + std::cerr << kv.first << ": " << kv.second.value << "\n"; + } + std::cerr << std::endl; + + res.on_data([](const uint8_t *data, std::size_t len) { + std::cerr.write(reinterpret_cast(data), len); + std::cerr << std::endl; + }); + }); + + req->on_close([&sess](uint32_t error_code) { + // shutdown session after first request was done. + sess.shutdown(); + }); + }); + + sess.on_error([](const boost::system::error_code &ec) { + std::cerr << "error: " << ec.message() << std::endl; + }); + + io_service.run(); + } + +``nghttp2::asio_http2::client::session`` object takes +``boost::asio::io_service`` object and remote server address. When +connection is made, the callback function passed to +``nghttp2::asio_http2::client::on_connect`` is invoked with connected +address as its parameter. After this callback call, use +``nghttp2::asio_http2::session::submit`` to send request to the +server. You can submit multiple requests at once without waiting for +the completion of previous request. + +The life time of req and res object ends after the callback set by +``nghttp2::asio_http2::server::request::on_close`` function. +Application must not use those objects after this call. + +Normally, client does not stop even after all requests are done unless +connection is lost. To stop client, call +``nghttp2::asio_http2::server::session::shutdown()``. + +Recieve server push and enable SSL/TLS +++++++++++++++++++++++++++++++++++++++ + +.. code-block:: cpp + + #include + + #include + + using boost::asio::ip::tcp; + + using namespace nghttp2::asio_http2; + using namespace nghttp2::asio_http2::client; + + int main(int argc, char *argv[]) { + boost::system::error_code ec; + boost::asio::io_service io_service; + + boost::asio::ssl::context tls(boost::asio::ssl::context::sslv23); + tls.set_default_verify_paths(); + // disabled to make development easier... + // tls_ctx.set_verify_mode(boost::asio::ssl::verify_peer); + configure_tls_context(ec, tls); + + // connect to localhost:3000 + session sess(io_service, tls, "localhost", "3000"); + + sess.on_connect([&sess](tcp::resolver::iterator endpoint_it) { + boost::system::error_code ec; + + auto req = sess.submit(ec, "GET", "http://localhost:3000/"); + + req->on_response([&sess](const response &res) { + std::cerr << "response received!" << std::endl; + res.on_data([&sess](const uint8_t *data, std::size_t len) { + std::cerr.write(reinterpret_cast(data), len); + std::cerr << std::endl; + }); + }); + + req->on_push([](const request &push) { + std::cerr << "push request received!" << std::endl; + push.on_response([](const response &res) { + std::cerr << "push response received!" << std::endl; + res.on_data([](const uint8_t *data, std::size_t len) { + std::cerr.write(reinterpret_cast(data), len); + std::cerr << std::endl; + }); + }); + }); + }); + + sess.on_error([](const boost::system::error_code &ec) { + std::cerr << "error: " << ec.message() << std::endl; + }); + + io_service.run(); + } + +The above sample code demonstrates how to enable SSL/TLS and receive +server push. Currently, +``nghttp2::asio_http2::client::configure_tls_context`` function setups +NPN callbacks for SSL/TLS context for HTTP/2 use. + +To receive server push, use +``nghttp2::asio_http2::client::request::on_push`` function to set +callback function which is invoked when server push request is +arrived. The callback function takes +``nghttp2::asio_http2::client::request`` object, which contains the +pushed request. To get server push response, set callback using +``nghttp2::asio_http2::client::request::on_response``. + +As stated in the previous section, client does not stop automatically +as long as HTTP/2 session is fine and connection is alive. We don't +call ``nghttp2::asio_http2::client::session::shutdown`` in this +example, so the program does not terminate after all responses are +received. Hit Ctrl-C to terminate the program. + +Multiple concurrent requests +++++++++++++++++++++++++++++ + +.. code-block:: cpp + + #include + + #include + + using boost::asio::ip::tcp; + + using namespace nghttp2::asio_http2; + using namespace nghttp2::asio_http2::client; + + int main(int argc, char *argv[]) { + boost::system::error_code ec; + boost::asio::io_service io_service; + + // connect to localhost:3000 + session sess(io_service, "localhost", "3000"); + + sess.on_connect([&sess](tcp::resolver::iterator endpoint_it) { + boost::system::error_code ec; + + auto printer = [](const response &res) { + res.on_data([](const uint8_t *data, std::size_t len) { + std::cerr.write(reinterpret_cast(data), len); + std::cerr << std::endl; + }); + }; + + std::size_t num = 3; + auto count = std::make_shared(num); + + for (std::size_t i = 0; i < num; ++i) { + auto req = sess.submit(ec, "GET", + "http://localhost:3000/" + std::to_string(i + 1)); + + req->on_response(printer); + req->on_close([&sess, count](uint32_t error_code) { + if (--*count == 0) { + // shutdown session after |num| requests were done. + sess.shutdown(); + } + }); + } + }); + + sess.on_error([](const boost::system::error_code &ec) { + std::cerr << "error: " << ec.message() << std::endl; + }); + + io_service.run(); + } + +Here is the sample to send 3 requests at once. Depending on the +server settings, these requests are processed out-of-order. In this +example, we have a trick to shutdown session after all requests were +done. We made ``count`` object which is shared pointer to int and is +initialized to 3. On each request closure (the invocation of the +callback set by ``nghttp2::asio_http2::client::request::on_close``), +we decrement the count. If count becomes 0, we are sure that all +requests have been done and initiate shutdown. diff --git a/doc/sources/nghttpx-howto.rst b/doc/sources/nghttpx-howto.rst new file mode 100644 index 0000000..1f7d87e --- /dev/null +++ b/doc/sources/nghttpx-howto.rst @@ -0,0 +1,303 @@ +nghttpx - HTTP/2 proxy - HOW-TO +=============================== + +nghttpx is a proxy translating protocols between HTTP/2 and other +protocols (e.g., HTTP/1, SPDY). It operates in several modes and each +mode may require additional programs to work with. This article +describes each operation mode and explains the intended use-cases. It +also covers some useful options later. + +Default mode +------------ + +If nghttpx is invoked without any ``-s``, ``-p`` and ``--client``, it +operates in default mode. In this mode, nghttpx frontend listens for +HTTP/2 requests and translates them to HTTP/1 requests. Thus it works +as reverse proxy (gateway) for HTTP/2 clients to HTTP/1 web server. +HTTP/1 requests are also supported in frontend as a fallback. If +nghttpx is linked with spdylay library and frontend connection is +SSL/TLS, the frontend also supports SPDY protocol. + +By default, this mode's frontend connection is encrypted using +SSL/TLS. So server's private key and certificate must be supplied to +the command line (or through configuration file). In this case, the +frontend protocol selection will is done via ALPN or NPN. + +With ``--frontend-no-tls`` option, user can turn off SSL/TLS in +frontend connection. In this case, SPDY protocol is not available +even if spdylay library is liked to nghttpx. HTTP/2 and HTTP/1 are +available on the frontend and a HTTP/1 connection can be upgraded to +HTTP/2 using HTTP Upgrade. Starting HTTP/2 connection by sending +HTTP/2 connection preface is also supported. + +The backend is supposed to be HTTP/1 Web server. For example, to make +nghttpx listen to encrypted HTTP/2 requests at port 8443, and a +backend HTTP/1 web server is configured to listen to HTTP/1 request at +port 8080 in the same host, run nghttpx command-line like this:: + + $ nghttpx -f0.0.0.0,8443 -b127.0.0.1,8080 /path/to/server.key /path/to/server.crt + +Then HTTP/2 enabled client can access to the nghttpx in HTTP/2. For +example, you can send GET request to the server using nghttp:: + + $ nghttp -nv https://localhost:8443/ + +HTTP/2 proxy mode +----------------- + +If nghttpx is invoked with ``-s`` option, it operates in HTTP/2 proxy +mode. The supported protocols in frontend and backend connections are +the same in `default mode`_. The difference is that this mode acts +like forward proxy and assumes the backend is HTTP/1 proxy server +(e.g., squid, traffic server). So HTTP/1 request must include +absolute URI in request line. + +By default, frontend connection is encrypted. So this mode is also +called secure proxy. If nghttpx is linked with spdylay, it supports +SPDY protocols and it works as so called SPDY proxy. + +With ``--frontend-no-tls`` option, SSL/TLS is turned off in frontend +connection, so the connection gets insecure. + +The backend must be HTTP/1 proxy server. nghttpx supports multiple +backend server addresses. It translates incoming requests to HTTP/1 +request to backend server. The backend server performs real proxy +work for each request, for example, dispatching requests to the origin +server and caching contents. + +For example, to make nghttpx listen to encrypted HTTP/2 requests at +port 8443, and a backend HTTP/1 proxy server is configured to listen +to HTTP/1 request at port 8080 in the same host, run nghttpx +command-line like this:: + + $ nghttpx -s -f'*,8443' -b127.0.0.1,8080 /path/to/server.key /path/to/server.crt + +At the time of this writing, Firefox nightly supports HTTP/2 proxy. +Chromium can use nghttpx as secure (SPDY) proxy and will support +HTTP/2 proxy in the near future. + +To make Firefox nightly or Chromium use nghttpx as HTTP/2 or SPDY +proxy, user has to create proxy.pac script file like this: + +.. code-block:: javascript + + function FindProxyForURL(url, host) { + return "HTTPS SERVERADDR:PORT"; + } + +``SERVERADDR`` and ``PORT`` is the hostname/address and port of the +machine nghttpx is running. Please note that both Firefox nightly and +Chromium require valid certificate for secure proxy. + +For Firefox nightly, open Preference window and select Advanced then +click Network tab. Clicking Connection Settings button will show the +dialog. Select "Automatic proxy configuration URL" and enter the path +to proxy.pac file, something like this:: + + file:///path/to/proxy.pac + +For Chromium, use following command-line:: + + $ google-chrome --proxy-pac-url=file:///path/to/proxy.pac --use-npn + +As HTTP/1 proxy server, Squid may work as out-of-box. Traffic server +requires to be configured as forward proxy. Here is the minimum +configuration items to edit:: + + CONFIG proxy.config.reverse_proxy.enabled INT 0 + CONFIG proxy.config.url_remap.remap_required INT 0 + +Consult Traffic server `documentation +`_ +to know how to configure traffic server as forward proxy and its +security implications. + +Client mode +----------- + +If nghttpx is invoked with ``--client`` option, it operates in client +mode. In this mode, nghttpx listens for plain, unencrypted HTTP/2 and +HTTP/1 requests and translates them to encrypted HTTP/2 requests to +the backend. User cannot enable SSL/TLS in frontend connection. + +HTTP/1 frontend connection can be upgraded to HTTP/2 using HTTP +Upgrade. To disable SSL/TLS in backend connection, use +``--backend-no-tls`` option. + +By default, the number of backend HTTP/2 connections per worker +(thread) is determined by number of ``-b`` option. To adjust this +value, use ``--backend-http2-connections-per-worker`` option. + +The backend server is supporsed to be a HTTP/2 web server (e.g., +nghttpd). The one use-case of this mode is utilize existing HTTP/1 +clients to test HTTP/2 deployment. Suppose that HTTP/2 web server +listens to port 80 without encryption. Then run nghttpx as client +mode to access to that web server:: + + $ nghttpx --client -f127.0.0.1,8080 -b127.0.0.1,80 --backend-no-tls + +.. note:: + + You may need ``-k`` option if HTTP/2 server enables SSL/TLS and + its certificate is self-signed. But please note that it is + insecure. + +Then you can use curl to access HTTP/2 server via nghttpx:: + + $ curl http://localhost:8080/ + +Client proxy mode +----------------- + +If nghttpx is invoked with ``-p`` option, it operates in client proxy +mode. This mode behaves like `client mode`_, but it works like +forward proxy. So HTTP/1 request must include absolute URI in request +line. + +HTTP/1 frontend connection can be upgraded to HTTP/2 using HTTP +Upgrade. To disable SSL/TLS in backend connection, use +``--backend-no-tls`` option. + +By default, the number of backend HTTP/2 connections per worker +(thread) is determined by number of ``-b`` option. To adjust this +value, use ``--backend-http2-connections-per-worker`` option. + +The backend server must be a HTTP/2 proxy. You can use nghttpx in +`HTTP/2 proxy mode`_ as backend server. The one use-case of this mode +is utilize existing HTTP/1 clients to test HTTP/2 connections between +2 proxies. The another use-case is use this mode to aggregate local +HTTP/1 connections to one HTTP/2 backend encrypted connection. This +makes HTTP/1 clients which does not support secure proxy can use +secure HTTP/2 proxy via nghttpx client mode. + +Suppose that HTTP/2 proxy listens to port 8443, just like we saw in +`HTTP/2 proxy mode`_. To run nghttpx in client proxy mode to access +that server, invoke nghttpx like this:: + + $ nghttpx -p -f127.0.0.1,8080 -b127.0.0.1,8443 + +.. note:: + + You may need ``-k`` option if HTTP/2 server's certificate is + self-signed. But please note that it is insecure. + +Then you can use curl to issue HTTP request via HTTP/2 proxy:: + + $ curl --http-proxy=http://localhost:8080 http://www.google.com/ + +You can configure web browser to use localhost:8080 as forward +proxy. + +HTTP/2 bridge mode +------------------ + +If nghttpx is invoked with ``--http2-bridge`` option, it operates in +HTTP/2 bridge mode. The supported protocols in frontend connections +are the same in `default mode`_. The protocol in backend is HTTP/2 +only. + +With ``--frontend-no-tls`` option, SSL/TLS is turned off in frontend +connection, so the connection gets insecure. To disable SSL/TLS in +backend connection, use ``--backend-no-tls`` option. + +By default, the number of backend HTTP/2 connections per worker +(thread) is determined by number of ``-b`` option. To adjust this +value, use ``--backend-http2-connections-per-worker`` option. + +The backend server is supporsed to be a HTTP/2 web server or HTTP/2 +proxy. If backend server is HTTP/2 proxy, use +``--no-location-rewrite`` and ``--no-host-rewrite`` options to disable +rewriting location, host and :authority header field. + +The use-case of this mode is aggregate the incoming connections to one +HTTP/2 connection. One backend HTTP/2 connection is created per +worker (thread). + +Disable SSL/TLS +--------------- + +In `default mode`_, `HTTP/2 proxy mode`_ and `HTTP/2 bridge mode`_, +frontend connections are encrypted with SSL/TLS by default. To turn +off SSL/TLS, use ``--frontend-no-tls`` option. If this option is +used, the private key and certificate are not required to run nghttpx. + +In `client mode`_, `client proxy mode`_ and `HTTP/2 bridge mode`_, +backend connections are encrypted with SSL/TLS by default. To turn +off SSL/TLS, use ``--backend-no-tls`` option. + +Specifying additional CA certificate +------------------------------------ + +By default, nghttpx tries to read CA certificate from system. But +depending on the system you use, this may fail or is not supported. +To specify CA certificate manually, use ``--cacert`` option. The +specified file must be PEM format and can contain multiple +certificates. + +By default, nghttpx validates server's certificate. If you want to +turn off this validation, knowing this is really insecure and what you +are doing, you can use ``-k`` option to disable certificate +validation. + +Read/write rate limit +--------------------- + +nghttpx supports transfer rate limiting on frontend connections. You +can do rate limit per frontend connection for reading and writing +individually. + +To perform rate limit for reading, use ``--read-rate`` and +``--read-burst`` options. For writing, use ``--write-rate`` and +``--write-burst``. + +Please note that rate limit is performed on top of TCP and nothing to +do with HTTP/2 flow control. + +Rewriting location header field +------------------------------- + +nghttpx automatically rewrites location response header field if the +following all conditions satisfy: + +* URI in location header field is not absolute URI or is not https URI. +* URI in location header field includes non empty host component. +* host (without port) in URI in location header field must match the + host appearing in :authority or host header field. + +When rewrite happens, URI scheme and port are replaced with the ones +used in frontend, and host is replaced with which appears in +:authority or host request header field. :authority header field has +precedence. If the above conditions are not met with the host value +in :authority header field, rewrite is retried with the value in host +header field. + +Hot swapping +------------ + +nghttpx supports hot swapping using signals. The hot swapping in +nghttpx is multi step process. First send USR2 signal to nghttpx +process. It will do fork and execute new executable, using same +command-line arguments and environment variables. At this point, both +current and new processes can accept requests. To gracefully shutdown +current process, send QUIT signal to current nghttpx process. When +all existing frontend connections are done, the current process will +exit. At this point, only new nghttpx process exists and serves +incoming requests. + +Re-opening log files +-------------------- + +When rotating log files, it is desirable to re-open log files after +log rotation daemon renamed existing log files. To tell nghttpx to +re-open log files, send USR1 signal to nghttpx process. It will +re-open files specified by ``--accesslog-file`` and +``--errorlog-file`` options. + +Multiple backend addresses +-------------------------- + +nghttpx supports multiple backend addresses. To specify them, just +use ``-b`` option repeatedly. For example, to use backend1:8080 and +backend2:8080, use command-line like this: ``-bbackend1,8080 +-bbackend2,8080``. For HTTP/2 backend, see also +``--backend-http2-connections-per-worker`` option. diff --git a/doc/sources/python-apiref.rst b/doc/sources/python-apiref.rst new file mode 100644 index 0000000..d9d8494 --- /dev/null +++ b/doc/sources/python-apiref.rst @@ -0,0 +1,437 @@ +Python API Reference +==================== + +.. py:module:: nghttp2 + +nghttp2 offers some high level Python API to C library. The bindings +currently provide HPACK compressor and decompressor classes and HTTP/2 +server class. + +The extension module is called ``nghttp2``. + +``make`` will build the bindings. The target Python version is +determined by configure script. If the detected Python version is not +what you expect, specify a path to Python executable in ``PYTHON`` +variable as an argument to configure script (e.g., ``./configure +PYTHON=/usr/bin/python3.4``). + +HPACK API +--------- + +.. py:class:: HDDeflater(hd_table_bufsize_max=DEFLATE_MAX_HEADER_TABLE_SIZE) + + This class is used to perform header compression. The + *hd_table_bufsize_max* limits the usage of header table in the + given amount of bytes. The default value is + :py:data:`DEFLATE_MAX_HEADER_TABLE_SIZE`. This is necessary + because the deflater and inflater share the same amount of header + table and the inflater decides that number. The deflater may not + want to use all header table size because of limited memory + availability. In that case, *hd_table_bufsize_max* can be used to + cap the upper limit of table size whatever the header table size is + chosen by the inflater. + + .. py:method:: deflate(headers) + + Deflates the *headers*. The *headers* must be sequence of tuple + of name/value pair, which are byte strings (not unicode string). + + This method returns the deflated header block in byte string. + Raises the exception if any error occurs. + + .. py:method:: set_no_refset(no_refset) + + Tells the deflater not to use reference set if *no_refset* is + evaluated to ``True``. If that happens, on each subsequent + invocation of :py:meth:`deflate()`, deflater will clear up + refersent set. + + .. py:method:: change_table_size(hd_table_bufsize_max) + + Changes header table size to *hd_table_bufsize_max* byte. if + *hd_table_bufsize_max* is strictly larger than + ``hd_table_bufsize_max`` given in constructor, + ``hd_table_bufsize_max`` is used as header table size instead. + + Raises the exception if any error occurs. + + .. py:method:: get_hd_table() + + Returns copy of current dynamic header table. + +The following example shows how to deflate header name/value pairs: + +.. code-block:: python + + import binascii, nghttp2 + + deflater = nghttp2.HDDeflater() + + res = deflater.deflate([(b'foo', b'bar'), + (b'baz', b'buz')]) + + print(binascii.b2a_hex(res)) + + +.. py:class:: HDInflater() + + This class is used to perform header decompression. + + .. py:method:: inflate(data) + + Inflates the deflated header block *data*. The *data* must be + byte string. + + Raises the exception if any error occurs. + + .. py:method:: change_table_size(hd_table_bufsize_max) + + Changes header table size to *hd_table_bufsize_max* byte. + + Raises the exception if any error occurs. + + .. py:method:: get_hd_table() + + Returns copy of current dynamic header table. + +The following example shows how to inflate deflated header block: + +.. code-block:: python + + deflater = nghttp2.HDDeflater() + + data = deflater.deflate([(b'foo', b'bar'), + (b'baz', b'buz')]) + + inflater = nghttp2.HDInflater() + + hdrs = inflater.inflate(data) + + print(hdrs) + + +.. py:function:: print_hd_table(hdtable) + + Convenient function to print *hdtable* to the standard output. The + *hdtable* is the one retrieved by + :py:meth:`HDDeflater.get_hd_table()` or + :py:meth:`HDInflater.get_hd_table()`. This function does not work + if header name/value cannot be decoded using UTF-8 encoding. + + In output, ``s=N`` means the entry occupies ``N`` bytes in header + table. If ``r=y``, then the entry is in the reference set. + +.. py:data:: DEFAULT_HEADER_TABLE_SIZE + + The default header table size, which is 4096 as per HTTP/2 + specification. + +.. py:data:: DEFLATE_MAX_HEADER_TABLE_SIZE + + The default header table size for deflater. The initial value + is 4096. + +HTTP/2 servers +-------------- + +.. note:: + + We use :py:mod:`asyncio` for HTTP/2 server classes. Therefore, + Python 3.4 or later is required to use these objects. To + explicitly configure nghttp2 build to use Python 3.4, specify the + ``PYTHON`` variable to the path to Python 3.4 executable when + invoking configure script like this:: + + $ ./configure PYTHON=/usr/bin/python3.4 + +.. py:class:: HTTP2Server(address, RequestHandlerClass, ssl=None) + + This class builds on top of the :py:mod:`asyncio` event loop. On + construction, *RequestHandlerClass* must be given, which must be a + subclass of :py:class:`BaseRequestHandler` class. + + The *address* must be a tuple of hostname/IP address and port to + bind. If hostname/IP address is ``None``, all interfaces are + assumed. + + To enable SSL/TLS, specify instance of :py:class:`ssl.SSLContext` + in *ssl*. Before passing *ssl* to + :py:func:`BaseEventLoop.create_server`, ALPN protocol identifiers + are set using :py:meth:`ssl.SSLContext.set_npn_protocols`. + + To disable SSL/TLS, omit *ssl* or specify ``None``. + + .. py:method:: serve_forever() + + Runs server and processes incoming requests forever. + +.. py:class:: BaseRequestHandler(http2, stream_id) + + The class is used to handle the single HTTP/2 stream. By default, + it does not nothing. It must be subclassed to handle each event + callback method. + + The first callback method invoked is :py:meth:`on_headers()`. It is + called when HEADERS frame, which includes request header fields, is + arrived. + + If request has request body, :py:meth:`on_data()` is invoked for + each chunk of received data chunk. + + When whole request is received, :py:meth:`on_request_done()` is + invoked. + + When stream is closed, :py:meth:`on_close()` is called. + + The application can send response using :py:meth:`send_response()` + method. It can be used in :py:meth:`on_headers()`, + :py:meth:`on_data()` or :py:meth:`on_request_done()`. + + The application can push resource using :py:meth:`push()` method. + It must be used before :py:meth:`send_response()` call. + + A :py:class:`BaseRequestHandler` has the following instance + variables: + + .. py:attribute:: client_address + + Contains a tuple of the form ``(host, port)`` referring to the + client's address. + + .. py:attribute:: stream_id + + Stream ID of this stream + + .. py:attribute:: scheme + + Scheme of the request URI. This is a value of ``:scheme`` + header field. + + .. py:attribute:: method + + Method of this stream. This is a value of ``:method`` header + field. + + .. py:attribute:: host + + This is a value of ``:authority`` or ``host`` header field. + + .. py:attribute:: path + + This is a value of ``:path`` header field. + + .. py:attribute:: headers + + Request header fields. + + A :py:class:`BaseRequestHandler` has the following methods: + + .. py:method:: on_headers() + + Called when request HEADERS is arrived. By default, this method + does nothing. + + .. py:method:: on_data(data) + + Called when a chunk of request body *data* is arrived. This + method will be called multiple times until all data are + received. By default, this method does nothing. + + .. py:method:: on_request_done() + + Called when whole request was received. By default, this method + does nothing. + + .. py:method:: on_close(error_code) + + Called when stream is about to close. The *error_code* + indicates the reason of closure. If it is ``0``, the stream is + going to close without error. + + .. py:method:: send_response(status=200, headers=None, body=None) + + Send response. The *status* is HTTP status code. The *headers* + is additional response headers. The *:status* header field will + be appended by the library. The *body* is the response body. + It could be ``None`` if response body is empty. Or it must be + instance of either ``str``, ``bytes``, :py:class:`io.IOBase` or + callable, called body generator, which takes one parameter, + size. The body generator generates response body. It can pause + generation of response so that it can wait for slow backend data + generation. When invoked, it should return tuple, byte string + at most size length and flag. The flag is either + :py:data:`DATA_OK`, :py:data:`DATA_EOF` or + :py:data:`DATA_DEFERRED`. For non-empty byte string and it is + not the last chunk of response, :py:data:`DATA_OK` must be + returned as flag. If this is the last chunk of the response + (byte string could be ``None``), :py:data:`DATA_EOF` must be + returned as flag. If there is no data available right now, but + additional data are anticipated, return tuple (``None``, + :py:data:`DATA_DEFERRED`). When data arrived, call + :py:meth:`resume()` and restart response body transmission. + + Only the body generator can pause response body generation; + instance of :py:class:`io.IOBase` must not block. + + If instance of ``str`` is specified as *body*, it will be + encoded using UTF-8. + + The *headers* is a list of tuple of the form ``(name, + value)``. The ``name`` and ``value`` can be either byte string + or unicode string. In the latter case, they will be encoded + using UTF-8. + + Raises the exception if any error occurs. + + .. py:method:: push(path, method='GET', request_headers=None, status=200, headers=None, body=None) + + Push a specified resource. The *path* is a path portion of + request URI for this resource. The *method* is a method to + access this resource. The *request_headers* is additional + request headers to access this resource. The ``:scheme``, + ``:method``, ``:authority`` and ``:path`` are appended by the + library. The ``:scheme`` and ``:authority`` are inherited from + request header fields of the associated stream. + + The *status* is HTTP status code. The *headers* is additional + response headers. The ``:status`` header field is appended by + the library. The *body* is the response body. It has the same + semantics of *body* parameter of :py:meth:`send_response()`. + + The headers and request_headers are a list of tuple of the form + ``(name, value)``. The ``name`` and ``value`` can be either byte + string or unicode string. In the latter case, they will be + encoded using UTF-8. + + Returns an instance of ``RequestHandlerClass`` specified in + :py:class:`HTTP2Server` constructor for the pushed resource. + + Raises the exception if any error occurs. + + .. py:method:: resume() + + Signals the restarting of response body transmission paused by + ``DATA_DEFERRED`` from the body generator (see + :py:meth:`send_response()` about the body generator). It is not + an error calling this method while response body transmission is + not paused. + +.. py:data:: DATA_OK + + ``DATA_OK`` indicates non empty data is generated from body generator. + +.. py:data:: DATA_EOF + + ``DATA_EOF`` indicates the end of response body. + +.. py:data:: DATA_DEFERRED + + ``DATA_DEFERRED`` indicates that data are not available right now + and response should be paused. + +The following example illustrates :py:class:`HTTP2Server` and +:py:class:`BaseRequestHandler` usage: + +.. code-block:: python + + #!/usr/bin/env python + + import io, ssl + + import nghttp2 + + class Handler(nghttp2.BaseRequestHandler): + + def on_headers(self): + self.push(path='/css/style.css', + request_headers = [('content-type', 'text/css')], + status=200, + body='body{margin:0;}') + + self.send_response(status=200, + headers = [('content-type', 'text/plain')], + body=io.BytesIO(b'nghttp2-python FTW')) + + ctx = ssl.SSLContext(ssl.PROTOCOL_SSLv23) + ctx.options = ssl.OP_ALL | ssl.OP_NO_SSLv2 | ssl.OP_NO_SSLv3 + ctx.load_cert_chain('server.crt', 'server.key') + + # give None to ssl to make the server non-SSL/TLS + server = nghttp2.HTTP2Server(('127.0.0.1', 8443), Handler, ssl=ctx) + server.serve_forever() + +The following example illustrates HTTP/2 server using asynchronous +response body generation. This is simplified reverse proxy: + +.. code-block:: python + + #!/usr/bin/env python + + import ssl + import os + import urllib + import asyncio + import io + + import nghttp2 + + @asyncio.coroutine + def get_http_header(handler, url): + url = urllib.parse.urlsplit(url) + ssl = url.scheme == 'https' + if url.port == None: + if url.scheme == 'https': + port = 443 + else: + port = 80 + else: + port = url.port + + connect = asyncio.open_connection(url.hostname, port, ssl=ssl) + reader, writer = yield from connect + req = 'GET {path} HTTP/1.0\r\n\r\n'.format(path=url.path or '/') + writer.write(req.encode('utf-8')) + # skip response header fields + while True: + line = yield from reader.readline() + line = line.rstrip() + if not line: + break + # read body + while True: + b = yield from reader.read(4096) + if not b: + break + handler.buf.write(b) + writer.close() + handler.buf.seek(0) + handler.eof = True + handler.resume() + + class Body: + def __init__(self, handler): + self.handler = handler + self.handler.eof = False + self.handler.buf = io.BytesIO() + + def generate(self, n): + buf = self.handler.buf + data = buf.read1(n) + if not data and not self.handler.eof: + return None, nghttp2.DATA_DEFERRED + return data, nghttp2.DATA_EOF if self.handler.eof else nghttp2.DATA_OK + + class Handler(nghttp2.BaseRequestHandler): + + def on_headers(self): + body = Body(self) + asyncio.async(get_http_header( + self, 'http://localhost' + self.path.decode('utf-8'))) + self.send_response(status=200, body=body.generate) + + ctx = ssl.SSLContext(ssl.PROTOCOL_SSLv23) + ctx.options = ssl.OP_ALL | ssl.OP_NO_SSLv2 | ssl.OP_NO_SSLv3 + ctx.load_cert_chain('server.crt', 'server.key') + + server = nghttp2.HTTP2Server(('127.0.0.1', 8443), Handler, ssl=ctx) + server.serve_forever() diff --git a/doc/sources/tutorial-client.rst b/doc/sources/tutorial-client.rst new file mode 100644 index 0000000..6ce1238 --- /dev/null +++ b/doc/sources/tutorial-client.rst @@ -0,0 +1,439 @@ +Tutorial: HTTP/2 client +========================= + +In this tutorial, we are going to write very primitive HTTP/2 +client. The complete source code, `libevent-client.c`_, is attached at +the end of this page. It also resides in examples directory in the +archive or repository. + +This simple client takes 1 argument, HTTPS URI, and retrieves the +resource denoted by the URI. Its synopsis is like this:: + + $ libevent-client HTTPS_URI + +We use libevent in this tutorial to handle networking I/O. Please +note that nghttp2 itself does not depend on libevent. + +First we do some setup routine for libevent and OpenSSL library in +function ``main()`` and ``run()``, which is not so relevant to nghttp2 +library use. The one thing you should look at is setup NPN callback. +The NPN callback is used for the client to select the next application +protocol over the SSL/TLS transport. In this tutorial, we use +`nghttp2_select_next_protocol()` function to select the HTTP/2 +protocol the library supports:: + + static int select_next_proto_cb(SSL *ssl _U_, unsigned char **out, + unsigned char *outlen, const unsigned char *in, + unsigned int inlen, void *arg _U_) { + if (nghttp2_select_next_protocol(out, outlen, in, inlen) <= 0) { + errx(1, "Server did not advertise " NGHTTP2_PROTO_VERSION_ID); + } + return SSL_TLSEXT_ERR_OK; + } + +The callback is set to the SSL_CTX object using +``SSL_CTX_set_next_proto_select_cb()`` function:: + + static SSL_CTX *create_ssl_ctx(void) { + SSL_CTX *ssl_ctx; + ssl_ctx = SSL_CTX_new(SSLv23_client_method()); + if (!ssl_ctx) { + errx(1, "Could not create SSL/TLS context: %s", + ERR_error_string(ERR_get_error(), NULL)); + } + SSL_CTX_set_options(ssl_ctx, + SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | + SSL_OP_NO_COMPRESSION | + SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION); + SSL_CTX_set_next_proto_select_cb(ssl_ctx, select_next_proto_cb, NULL); + return ssl_ctx; + } + +We use ``http2_session_data`` structure to store the data related to +the HTTP/2 session:: + + typedef struct { + nghttp2_session *session; + struct evdns_base *dnsbase; + struct bufferevent *bev; + http2_stream_data *stream_data; + } http2_session_data; + +Since this program only handles 1 URI, it uses only 1 stream. We store +its stream specific data in ``http2_stream_data`` structure and the +``stream_data`` points to it. The ``struct http2_stream_data`` is +defined as follows:: + + typedef struct { + /* The NULL-terminated URI string to retrieve. */ + const char *uri; + /* Parsed result of the |uri| */ + struct http_parser_url *u; + /* The authority portion of the |uri|, not NULL-terminated */ + char *authority; + /* The path portion of the |uri|, including query, not + NULL-terminated */ + char *path; + /* The length of the |authority| */ + size_t authoritylen; + /* The length of the |path| */ + size_t pathlen; + /* The stream ID of this stream */ + int32_t stream_id; + } http2_stream_data; + +We creates and initializes these structures in +``create_http2_session_data()`` and ``create_http2_stream_data()`` +respectively. + +Then we call function ``initiate_connection()`` to start connecting to +the remote server:: + + static void initiate_connection(struct event_base *evbase, SSL_CTX *ssl_ctx, + const char *host, uint16_t port, + http2_session_data *session_data) { + int rv; + struct bufferevent *bev; + SSL *ssl; + + ssl = create_ssl(ssl_ctx); + bev = bufferevent_openssl_socket_new( + evbase, -1, ssl, BUFFEREVENT_SSL_CONNECTING, + BEV_OPT_DEFER_CALLBACKS | BEV_OPT_CLOSE_ON_FREE); + bufferevent_setcb(bev, readcb, writecb, eventcb, session_data); + rv = bufferevent_socket_connect_hostname(bev, session_data->dnsbase, + AF_UNSPEC, host, port); + + if (rv != 0) { + errx(1, "Could not connect to the remote host %s", host); + } + session_data->bev = bev; + } + +We set 3 callbacks for the bufferevent: ``reacb``, ``writecb`` and +``eventcb``. + +The ``eventcb()`` is invoked by libevent event loop when an event +(e.g., connection has been established, timeout, etc) happens on the +underlying network socket:: + + static void eventcb(struct bufferevent *bev, short events, void *ptr) { + http2_session_data *session_data = (http2_session_data *)ptr; + if (events & BEV_EVENT_CONNECTED) { + int fd = bufferevent_getfd(bev); + int val = 1; + fprintf(stderr, "Connected\n"); + setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char *)&val, sizeof(val)); + initialize_nghttp2_session(session_data); + send_client_connection_header(session_data); + submit_request(session_data); + if (session_send(session_data) != 0) { + delete_http2_session_data(session_data); + } + return; + } + if (events & BEV_EVENT_EOF) { + warnx("Disconnected from the remote host"); + } else if (events & BEV_EVENT_ERROR) { + warnx("Network error"); + } else if (events & BEV_EVENT_TIMEOUT) { + warnx("Timeout"); + } + delete_http2_session_data(session_data); + } + +For ``BEV_EVENT_EOF``, ``BEV_EVENT_ERROR`` and ``BEV_EVENT_TIMEOUT`` +event, we just simply tear down the connection. The +``BEV_EVENT_CONNECTED`` event is invoked when SSL/TLS handshake is +finished successfully. We first initialize nghttp2 session object in +``initialize_nghttp2_session()`` function:: + + static void initialize_nghttp2_session(http2_session_data *session_data) { + nghttp2_session_callbacks *callbacks; + + nghttp2_session_callbacks_new(&callbacks); + + nghttp2_session_callbacks_set_send_callback(callbacks, send_callback); + + nghttp2_session_callbacks_set_on_frame_recv_callback(callbacks, + on_frame_recv_callback); + + nghttp2_session_callbacks_set_on_data_chunk_recv_callback( + callbacks, on_data_chunk_recv_callback); + + nghttp2_session_callbacks_set_on_stream_close_callback( + callbacks, on_stream_close_callback); + + nghttp2_session_callbacks_set_on_header_callback(callbacks, + on_header_callback); + + nghttp2_session_callbacks_set_on_begin_headers_callback( + callbacks, on_begin_headers_callback); + + nghttp2_session_client_new(&session_data->session, callbacks, session_data); + + nghttp2_session_callbacks_del(callbacks); + } + +Since we are creating client, we use `nghttp2_session_client_new()` to +initialize nghttp2 session object. We setup 7 callbacks for the +nghttp2 session. We'll explain these callbacks later. + +The `delete_http2_session_data()` destroys ``session_data`` and frees +its bufferevent, so it closes underlying connection as well. It also +calls `nghttp2_session_del()` to delete nghttp2 session object. + +We begin HTTP/2 communication by sending client connection preface, +which is 24 bytes magic byte string (:macro:`NGHTTP2_CLIENT_MAGIC`) +followed by SETTINGS frame. First 24 bytes magic string is +automatically sent by nghttp2 library. We send SETTINGS frame in +``send_client_connection_header()``:: + + static void send_client_connection_header(http2_session_data *session_data) { + nghttp2_settings_entry iv[1] = { + {NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100}}; + int rv; + + /* client 24 bytes magic string will be sent by nghttp2 library */ + rv = nghttp2_submit_settings(session_data->session, NGHTTP2_FLAG_NONE, iv, + ARRLEN(iv)); + if (rv != 0) { + errx(1, "Could not submit SETTINGS: %s", nghttp2_strerror(rv)); + } + } + +Here we specify SETTINGS_MAX_CONCURRENT_STREAMS to 100, which is +really not needed for this tiny example program, but we are +demonstrating the use of SETTINGS frame. To queue the SETTINGS frame +for the transmission, we use `nghttp2_submit_settings()`. Note that +`nghttp2_submit_settings()` function only queues the frame and not +actually send it. All ``nghttp2_submit_*()`` family functions have +this property. To actually send the frame, `nghttp2_session_send()` is +used, which is described about later. + +After the transmission of client connection header, we enqueue HTTP +request in ``submit_request()`` function:: + + static void submit_request(http2_session_data *session_data) { + int32_t stream_id; + http2_stream_data *stream_data = session_data->stream_data; + const char *uri = stream_data->uri; + const struct http_parser_url *u = stream_data->u; + nghttp2_nv hdrs[] = { + MAKE_NV2(":method", "GET"), + MAKE_NV(":scheme", &uri[u->field_data[UF_SCHEMA].off], + u->field_data[UF_SCHEMA].len), + MAKE_NV(":authority", stream_data->authority, stream_data->authoritylen), + MAKE_NV(":path", stream_data->path, stream_data->pathlen)}; + fprintf(stderr, "Request headers:\n"); + print_headers(stderr, hdrs, ARRLEN(hdrs)); + stream_id = nghttp2_submit_request(session_data->session, NULL, hdrs, + ARRLEN(hdrs), NULL, stream_data); + if (stream_id < 0) { + errx(1, "Could not submit HTTP request: %s", nghttp2_strerror(stream_id)); + } + + stream_data->stream_id = stream_id; + } + +We build HTTP request header fields in ``hdrs`` which is an array of +:type:`nghttp2_nv`. There are 4 header fields to be sent: ``:method``, +``:scheme``, ``:authority`` and ``:path``. To queue this HTTP request, +we use `nghttp2_submit_request()` function. The `stream_data` is +passed in *stream_user_data* parameter. It is used in nghttp2 +callbacks which we'll describe about later. +`nghttp2_submit_request()` returns the newly assigned stream ID for +this request. + +The next bufferevent callback is ``readcb()``, which is invoked when +data is available to read in the bufferevent input buffer:: + + static void readcb(struct bufferevent *bev, void *ptr) { + http2_session_data *session_data = (http2_session_data *)ptr; + ssize_t readlen; + struct evbuffer *input = bufferevent_get_input(bev); + size_t datalen = evbuffer_get_length(input); + unsigned char *data = evbuffer_pullup(input, -1); + + readlen = nghttp2_session_mem_recv(session_data->session, data, datalen); + if (readlen < 0) { + warnx("Fatal error: %s", nghttp2_strerror((int)readlen)); + delete_http2_session_data(session_data); + return; + } + if (evbuffer_drain(input, readlen) != 0) { + warnx("Fatal error: evbuffer_drain failed"); + delete_http2_session_data(session_data); + return; + } + if (session_send(session_data) != 0) { + delete_http2_session_data(session_data); + return; + } + } + +In this function, we feed all unprocessed, received data to nghttp2 +session object using `nghttp2_session_mem_recv()` function. The +`nghttp2_session_mem_recv()` processes the received data and may +invoke nghttp2 callbacks and also queue frames. Since there may be +pending frames, we call ``session_send()`` function to send those +frames. The ``session_send()`` function is defined as follows:: + + static int session_send(http2_session_data *session_data) { + int rv; + + rv = nghttp2_session_send(session_data->session); + if (rv != 0) { + warnx("Fatal error: %s", nghttp2_strerror(rv)); + return -1; + } + return 0; + } + +The `nghttp2_session_send()` function serializes the frame into wire +format and call ``send_callback()`` function of type +:type:`nghttp2_send_callback`. The ``send_callback()`` is defined as +follows:: + + static ssize_t send_callback(nghttp2_session *session _U_, const uint8_t *data, + size_t length, int flags _U_, void *user_data) { + http2_session_data *session_data = (http2_session_data *)user_data; + struct bufferevent *bev = session_data->bev; + bufferevent_write(bev, data, length); + return length; + } + +Since we use bufferevent to abstract network I/O, we just write the +data to the bufferevent object. Note that `nghttp2_session_send()` +continues to write all frames queued so far. If we were writing the +data to the non-blocking socket directly using ``write()`` system call +in the ``send_callback()``, we will surely get ``EAGAIN`` or +``EWOULDBLOCK`` since the socket has limited send buffer. If that +happens, we can return :macro:`NGHTTP2_ERR_WOULDBLOCK` to signal the +nghttp2 library to stop sending further data. But writing to the +bufferevent, we have to regulate the amount data to be buffered by +ourselves to avoid possible huge memory consumption. In this example +client, we do not limit anything. To see how to regulate the amount of +buffered data, see the ``send_callback()`` in the server tutorial. + +The third bufferevent callback is ``writecb()``, which is invoked when +all data written in the bufferevent output buffer have been sent:: + + static void writecb(struct bufferevent *bev _U_, void *ptr) { + http2_session_data *session_data = (http2_session_data *)ptr; + if (nghttp2_session_want_read(session_data->session) == 0 && + nghttp2_session_want_write(session_data->session) == 0 && + evbuffer_get_length(bufferevent_get_output(session_data->bev)) == 0) { + delete_http2_session_data(session_data); + } + } + +As described earlier, we just write off all data in `send_callback()`, +we have no data to write in this function. All we have to do is check +we have to drop connection or not. The nghttp2 session object keeps +track of reception and transmission of GOAWAY frame and other error +conditions as well. Using these information, nghttp2 session object +will tell whether the connection should be dropped or not. More +specifically, both `nghttp2_session_want_read()` and +`nghttp2_session_want_write()` return 0, we have no business in the +connection. But since we are using bufferevent and its deferred +callback option, the bufferevent output buffer may contain the pending +data when the ``writecb()`` is called. To handle this situation, we +also check whether the output buffer is empty or not. If these +conditions are met, we drop connection. + +We have already described about nghttp2 callback ``send_callback()``. +Let's describe remaining nghttp2 callbacks we setup in +``initialize_nghttp2_setup()`` function. + +Each request header name/value pair is emitted via +``on_header_callback`` function:: + + static int on_header_callback(nghttp2_session *session _U_, + const nghttp2_frame *frame, const uint8_t *name, + size_t namelen, const uint8_t *value, + size_t valuelen, uint8_t flags _U_, + void *user_data) { + http2_session_data *session_data = (http2_session_data *)user_data; + switch (frame->hd.type) { + case NGHTTP2_HEADERS: + if (frame->headers.cat == NGHTTP2_HCAT_RESPONSE && + session_data->stream_data->stream_id == frame->hd.stream_id) { + /* Print response headers for the initiated request. */ + print_header(stderr, name, namelen, value, valuelen); + break; + } + } + return 0; + } + +In this tutorial, we just print the name/value pair. + +After all name/value pairs are emitted for a frame, +``on_frame_recv_callback`` function is called:: + + static int on_frame_recv_callback(nghttp2_session *session _U_, + const nghttp2_frame *frame, void *user_data) { + http2_session_data *session_data = (http2_session_data *)user_data; + switch (frame->hd.type) { + case NGHTTP2_HEADERS: + if (frame->headers.cat == NGHTTP2_HCAT_RESPONSE && + session_data->stream_data->stream_id == frame->hd.stream_id) { + fprintf(stderr, "All headers received\n"); + } + break; + } + return 0; + } + +In this tutorial, we are just interested in the HTTP response +HEADERS. We check the frame type and its category (it should be +:macro:`NGHTTP2_HCAT_RESPONSE` for HTTP response HEADERS). Also check +its stream ID. + +The ``on_data_chunk_recv_callback()`` function is invoked when a chunk +of data is received from the remote peer:: + + static int on_data_chunk_recv_callback(nghttp2_session *session _U_, + uint8_t flags _U_, int32_t stream_id, + const uint8_t *data, size_t len, + void *user_data) { + http2_session_data *session_data = (http2_session_data *)user_data; + if (session_data->stream_data->stream_id == stream_id) { + fwrite(data, len, 1, stdout); + } + return 0; + } + +In our case, a chunk of data is response body. After checking stream +ID, we just write the received data to the stdout. Note that the +output in the terminal may be corrupted if the response body contains +some binary data. + +The ``on_stream_close_callback()`` function is invoked when the stream +is about to close:: + + static int on_stream_close_callback(nghttp2_session *session, int32_t stream_id, + nghttp2_error_code error_code, + void *user_data) { + http2_session_data *session_data = (http2_session_data *)user_data; + int rv; + + if (session_data->stream_data->stream_id == stream_id) { + fprintf(stderr, "Stream %d closed with error_code=%d\n", stream_id, + error_code); + rv = nghttp2_session_terminate_session(session, NGHTTP2_NO_ERROR); + if (rv != 0) { + return NGHTTP2_ERR_CALLBACK_FAILURE; + } + } + return 0; + } + +If the stream ID matches the one we initiated, it means that its +stream is going to be closed. Since we have finished to get the +resource we want (or the stream was reset by RST_STREAM from the +remote peer), we call `nghttp2_session_terminate_session()` to +commencing the closure of the HTTP/2 session gracefully. If you have +some data associated for the stream to be closed, you may delete it +here. diff --git a/doc/sources/tutorial-hpack.rst b/doc/sources/tutorial-hpack.rst new file mode 100644 index 0000000..0b4d59f --- /dev/null +++ b/doc/sources/tutorial-hpack.rst @@ -0,0 +1,118 @@ +Tutorial: HPACK API +=================== + +In this tutorial, we describe basic use of HPACK API in nghttp2 +library. We briefly describe APIs for deflating and inflating header +fields. The example of using these APIs are presented as complete +source code `deflate.c`_. + +Deflating (encoding) headers +---------------------------- + +First we need to initialize :type:`nghttp2_hd_deflater` object using +`nghttp2_hd_deflate_new()` function:: + + int nghttp2_hd_deflate_new(nghttp2_hd_deflater **deflater_ptr, + size_t deflate_hd_table_bufsize_max); + +This function allocates :type:`nghttp2_hd_deflater` object and +initializes it and assigns its pointer to ``*deflater_ptr`` passed by +parameter. The *deflate_hd_table_bufsize_max* is the upper bound of +header table size the deflater will use. This will limit the memory +usage in deflater object for dynamic header table. If you doubt, just +specify 4096 here, which is the default upper bound of dynamic header +table buffer size. + +To encode header fields, `nghttp2_hd_deflate_hd()` function:: + + ssize_t nghttp2_hd_deflate_hd(nghttp2_hd_deflater *deflater, + uint8_t *buf, size_t buflen, + const nghttp2_nv *nva, size_t nvlen); + +The *deflater* is the deflater object initialized by +`nghttp2_hd_deflate_new()` function described above. The *buf* is a +pointer to buffer to store encoded byte string. The *buflen* is +capacity of *buf*. The *nva* is a pointer to :type:`nghttp2_nv`, +which is an array of header fields to deflate. The *nvlen* is the +number of header fields which *nva* contains. + +It is important to initialize and assign all members of +:type:`nghttp2_nv`. If a header field should not be inserted in +dynamic header table for a security reason, set +:macro:`NGHTTP2_NV_FLAG_NO_INDEX` flag in :member:`nghttp2_nv.flags`. + +`nghttp2_hd_deflate_hd()` processes all headers given in *nva*. The +*nva* must include all request or response header fields to be sent in +one HEADERS (or optionally following (multiple) CONTINUATION +frame(s)). The *buf* must have enough space to store the encoded +result. Otherwise, the function will fail. To estimate the upper +bound of encoded result, use `nghttp2_hd_deflate_bound()` function:: + + size_t nghttp2_hd_deflate_bound(nghttp2_hd_deflater *deflater, + const nghttp2_nv *nva, size_t nvlen); + +Pass this function with the same parameters *deflater*, *nva* and +*nvlen* which will be passed to `nghttp2_hd_deflate_hd()`. + +The subsequent call of `nghttp2_hd_deflate_hd()` will use current +encoder state and perform differential encoding which is the +fundamental compression gain for HPACK. + +Once `nghttp2_hd_deflate_hd()` fails, it cannot be undone and its +further call with the same deflater object shall fail. So it is very +important to use `nghttp2_hd_deflate_bound()` to know the required +size of buffer. + +To delete :type:`nghttp2_hd_deflater` object, use `nghttp2_hd_deflate_del()` +function. + +Inflating (decoding) headers +---------------------------- + +We use :type:`nghttp2_hd_inflater` object to inflate compressed header +data. To initialize the object, use `nghttp2_hd_inflate_new()`:: + + int nghttp2_hd_inflate_new(nghttp2_hd_inflater **inflater_ptr); + +To inflate header data, use `nghttp2_hd_inflate_hd()` function:: + + ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_inflater *inflater, + nghttp2_nv *nv_out, int *inflate_flags, + uint8_t *in, size_t inlen, int in_final); + +The *inflater* is the inflater object initialized above. The *nv_out* +is a pointer to :type:`nghttp2_nv` to store the result. The *in* is a +pointer to input data and *inlen* is its length. The caller is not +required to specify whole deflated header data to *in* at once. It +can call this function multiple times for portion of the data in +streaming way. If *in_final* is nonzero, it tells the function that +the passed data is the final sequence of deflated header data. The +*inflate_flags* is output parameter and successful call of this +function stores a set of flags in it. It will be described later. + +This function returns when each header field is inflated. When this +happens, the function sets :macro:`NGHTTP2_HD_INFLATE_EMIT` flag to +*inflate_flag* parameter and header field is stored in *nv_out*. The +return value indicates the number of data read from *in* to processed +so far. It may be less than *inlen*. The caller should call the +function repeatedly until all data are processed by adjusting *in* and +*inlen* with the processed bytes. + +If *in_final* is nonzero and all given data was processed, the +function sets :macro:`NGHTTP2_HD_INFLATE_FINAL` flag to +*inflate_flag*. If the caller sees this flag set, call +`nghttp2_hd_inflate_end_headers()` function. + +If *in_final* is zero and :macro:`NGHTTP2_HD_INFLATE_EMIT` flag is not +set, it indicates that all given data was processed. The caller is +required to pass subsequent data. + +It is important to note that the function may produce one or more +header fields even if *inlen* is 0 when *in_final* is nonzero, due to +differential encoding. + +The example use of `nghttp2_hd_inflate_hd()` is shown in +`inflate_header_block()` function in `deflate.c`_. + +To delete :type:`nghttp2_hd_inflater` object, use `nghttp2_hd_inflate_del()` +function. diff --git a/doc/sources/tutorial-server.rst b/doc/sources/tutorial-server.rst new file mode 100644 index 0000000..8f5b583 --- /dev/null +++ b/doc/sources/tutorial-server.rst @@ -0,0 +1,550 @@ +Tutorial: HTTP/2 server +========================= + +In this tutorial, we are going to write single-threaded, event-based +HTTP/2 web server, which supports HTTPS only. It can handle +concurrent multiple requests, but only the GET method is supported. The +complete source code, `libevent-server.c`_, is attached at the end of +this page. It also resides in examples directory in the archive or +repository. + +This simple server takes 3 arguments, a port number to listen to, a path to +your SSL/TLS private key file and a path to your certificate file. Its +synopsis is like this:: + + $ libevent-server PORT /path/to/server.key /path/to/server.crt + +We use libevent in this tutorial to handle networking I/O. Please +note that nghttp2 itself does not depend on libevent. + +First we create a setup routine for libevent and OpenSSL in the functions +``main()`` and ``run()``. One thing in there you should look at, is the setup +of the NPN callback. The NPN callback is used for the server to advertise +which application protocols the server supports to a client. In this example +program, when creating ``SSL_CTX`` object, we store the application protocol +name in the wire format of NPN in a statically allocated buffer. This is safe +because we only create one ``SSL_CTX`` object in the program's entire life +time:: + + static unsigned char next_proto_list[256]; + static size_t next_proto_list_len; + + static int next_proto_cb(SSL *s _U_, const unsigned char **data, + unsigned int *len, void *arg _U_) { + *data = next_proto_list; + *len = (unsigned int)next_proto_list_len; + return SSL_TLSEXT_ERR_OK; + } + + static SSL_CTX *create_ssl_ctx(const char *key_file, const char *cert_file) { + SSL_CTX *ssl_ctx; + EC_KEY *ecdh; + + ssl_ctx = SSL_CTX_new(SSLv23_server_method()); + + ... + + next_proto_list[0] = NGHTTP2_PROTO_VERSION_ID_LEN; + memcpy(&next_proto_list[1], NGHTTP2_PROTO_VERSION_ID, + NGHTTP2_PROTO_VERSION_ID_LEN); + next_proto_list_len = 1 + NGHTTP2_PROTO_VERSION_ID_LEN; + + SSL_CTX_set_next_protos_advertised_cb(ssl_ctx, next_proto_cb, NULL); + return ssl_ctx; + } + +The wire format of NPN is a sequence of length prefixed string. Exactly one +byte is used to specify the length of each protocol identifier. In this +tutorial, we advertise the specific HTTP/2 protocol version the current +nghttp2 library supports. The nghttp2 library exports its identifier in +:macro:`NGHTTP2_PROTO_VERSION_ID`. The ``next_proto_cb()`` function is the +server-side NPN callback. In the OpenSSL implementation, we just assign the +pointer to the NPN buffers we filled in earlier. The NPN callback function is +set to the ``SSL_CTX`` object using +``SSL_CTX_set_next_protos_advertised_cb()``. + +We use the ``app_content`` structure to store application-wide data:: + + struct app_context { + SSL_CTX *ssl_ctx; + struct event_base *evbase; + }; + +We use the ``http2_session_data`` structure to store session-level +(which corresponds to one HTTP/2 connection) data:: + + typedef struct http2_session_data { + struct http2_stream_data root; + struct bufferevent *bev; + app_context *app_ctx; + nghttp2_session *session; + char *client_addr; + } http2_session_data; + +We use the ``http2_stream_data`` structure to store stream-level data:: + + typedef struct http2_stream_data { + struct http2_stream_data *prev, *next; + char *request_path; + int32_t stream_id; + int fd; + } http2_stream_data; + +A single HTTP/2 session can have multiple streams. We manage these +multiple streams with a doubly linked list. The first element of this +list is pointed to by the ``root->next`` in ``http2_session_data``. +Initially, ``root->next`` is ``NULL``. We use libevent's bufferevent +structure to perform network I/O. Note that the bufferevent object is +kept in ``http2_session_data`` and not in ``http2_stream_data``. This +is because ``http2_stream_data`` is just a logical stream multiplexed +over the single connection managed by bufferevent in +``http2_session_data``. + +We first create a listener object to accept incoming connections. We use +libevent's ``struct evconnlistener`` for this purpose:: + + static void start_listen(struct event_base *evbase, const char *service, + app_context *app_ctx) { + int rv; + struct addrinfo hints; + struct addrinfo *res, *rp; + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_PASSIVE; + #ifdef AI_ADDRCONFIG + hints.ai_flags |= AI_ADDRCONFIG; + #endif /* AI_ADDRCONFIG */ + + rv = getaddrinfo(NULL, service, &hints, &res); + if (rv != 0) { + errx(1, NULL); + } + for (rp = res; rp; rp = rp->ai_next) { + struct evconnlistener *listener; + listener = evconnlistener_new_bind( + evbase, acceptcb, app_ctx, LEV_OPT_CLOSE_ON_FREE | LEV_OPT_REUSEABLE, + 16, rp->ai_addr, rp->ai_addrlen); + if (listener) { + freeaddrinfo(res); + + return; + } + } + errx(1, "Could not start listener"); + } + +We specify the ``acceptcb`` callback which is called when a new connection is +accepted:: + + static void acceptcb(struct evconnlistener *listener _U_, int fd, + struct sockaddr *addr, int addrlen, void *arg) { + app_context *app_ctx = (app_context *)arg; + http2_session_data *session_data; + + session_data = create_http2_session_data(app_ctx, fd, addr, addrlen); + + bufferevent_setcb(session_data->bev, readcb, writecb, eventcb, session_data); + } + +Here we create the ``http2_session_data`` object. The bufferevent for +this connection is also initialized at this time. We specify three +callbacks for the bufferevent: ``readcb``, ``writecb`` and +``eventcb``. + +The ``eventcb()`` callback is invoked by the libevent event loop when an event +(e.g., connection has been established, timeout, etc) happens on the +underlying network socket:: + + static void eventcb(struct bufferevent *bev _U_, short events, void *ptr) { + http2_session_data *session_data = (http2_session_data *)ptr; + if (events & BEV_EVENT_CONNECTED) { + fprintf(stderr, "%s connected\n", session_data->client_addr); + + initialize_nghttp2_session(session_data); + + if (send_server_connection_header(session_data) != 0) { + delete_http2_session_data(session_data); + return; + } + + return; + } + if (events & BEV_EVENT_EOF) { + fprintf(stderr, "%s EOF\n", session_data->client_addr); + } else if (events & BEV_EVENT_ERROR) { + fprintf(stderr, "%s network error\n", session_data->client_addr); + } else if (events & BEV_EVENT_TIMEOUT) { + fprintf(stderr, "%s timeout\n", session_data->client_addr); + } + delete_http2_session_data(session_data); + } + +For the ``BEV_EVENT_EOF``, ``BEV_EVENT_ERROR`` and +``BEV_EVENT_TIMEOUT`` events, we just simply tear down the connection. +The ``delete_http2_session_data()`` function destroys the +``http2_session_data`` object and thus also its bufferevent member. +As a result, the underlying connection is closed. The +``BEV_EVENT_CONNECTED`` event is invoked when SSL/TLS handshake is +finished successfully. Now we are ready to start the HTTP/2 +communication. + +We initialize a nghttp2 session object which is done in +``initialize_nghttp2_session()``:: + + static void initialize_nghttp2_session(http2_session_data *session_data) { + nghttp2_session_callbacks *callbacks; + + nghttp2_session_callbacks_new(&callbacks); + + nghttp2_session_callbacks_set_send_callback(callbacks, send_callback); + + nghttp2_session_callbacks_set_on_frame_recv_callback(callbacks, + on_frame_recv_callback); + + nghttp2_session_callbacks_set_on_stream_close_callback( + callbacks, on_stream_close_callback); + + nghttp2_session_callbacks_set_on_header_callback(callbacks, + on_header_callback); + + nghttp2_session_callbacks_set_on_begin_headers_callback( + callbacks, on_begin_headers_callback); + + nghttp2_session_server_new(&session_data->session, callbacks, session_data); + + nghttp2_session_callbacks_del(callbacks); + } + +Since we are creating a server and uses options, the nghttp2 session +object is created using `nghttp2_session_server_new2()` function. We +registers five callbacks for nghttp2 session object. We'll talk about +these callbacks later. + +After initialization of the nghttp2 session object, we are going to send +a server connection header in ``send_server_connection_header()``:: + + static int send_server_connection_header(http2_session_data *session_data) { + nghttp2_settings_entry iv[1] = { + {NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100}}; + int rv; + + rv = nghttp2_submit_settings(session_data->session, NGHTTP2_FLAG_NONE, iv, + ARRLEN(iv)); + if (rv != 0) { + warnx("Fatal error: %s", nghttp2_strerror(rv)); + return -1; + } + return 0; + } + +The server connection header is a SETTINGS frame. We specify +SETTINGS_MAX_CONCURRENT_STREAMS to 100 in the SETTINGS frame. To queue +the SETTINGS frame for the transmission, we use +`nghttp2_submit_settings()`. Note that `nghttp2_submit_settings()` +function only queues the frame and it does not actually send it. All +functions in the ``nghttp2_submit_*()`` family have this property. To +actually send the frame, `nghttp2_session_send()` should be used, as +described later. + +Since bufferevent may buffer more than the first 24 bytes from the client, we +have to process them here since libevent won't invoke callback functions for +this pending data. To process the received data, we call the +``session_recv()`` function:: + + static int session_recv(http2_session_data *session_data) { + ssize_t readlen; + struct evbuffer *input = bufferevent_get_input(session_data->bev); + size_t datalen = evbuffer_get_length(input); + unsigned char *data = evbuffer_pullup(input, -1); + + readlen = nghttp2_session_mem_recv(session_data->session, data, datalen); + if (readlen < 0) { + warnx("Fatal error: %s", nghttp2_strerror((int)readlen)); + return -1; + } + if (evbuffer_drain(input, readlen) != 0) { + warnx("Fatal error: evbuffer_drain failed"); + return -1; + } + if (session_send(session_data) != 0) { + return -1; + } + return 0; + } + +In this function, we feed all unprocessed but already received data to the +nghttp2 session object using the `nghttp2_session_mem_recv()` function. The +`nghttp2_session_mem_recv()` function processes the data and may invoke the +nghttp2 callbacks and also queue outgoing frames. Since there may be pending +outgoing frames, we call ``session_send()`` function to send off those +frames. The ``session_send()`` function is defined as follows:: + + static int session_send(http2_session_data *session_data) { + int rv; + rv = nghttp2_session_send(session_data->session); + if (rv != 0) { + warnx("Fatal error: %s", nghttp2_strerror(rv)); + return -1; + } + return 0; + } + +The `nghttp2_session_send()` function serializes the frame into wire +format and calls ``send_callback()`` of type +:type:`nghttp2_send_callback`. The ``send_callback()`` is defined as +follows:: + + static ssize_t send_callback(nghttp2_session *session _U_, const uint8_t *data, + size_t length, int flags _U_, void *user_data) { + http2_session_data *session_data = (http2_session_data *)user_data; + struct bufferevent *bev = session_data->bev; + /* Avoid excessive buffering in server side. */ + if (evbuffer_get_length(bufferevent_get_output(session_data->bev)) >= + OUTPUT_WOULDBLOCK_THRESHOLD) { + return NGHTTP2_ERR_WOULDBLOCK; + } + bufferevent_write(bev, data, length); + return length; + } + +Since we use bufferevent to abstract network I/O, we just write the +data to the bufferevent object. Note that `nghttp2_session_send()` +continues to write all frames queued so far. If we were writing the +data to a non-blocking socket directly using ``write()`` system call +in the ``send_callback()``, we would surely get ``EAGAIN`` or +``EWOULDBLOCK`` back since the socket has limited send buffer. If that +happens, we can return :macro:`NGHTTP2_ERR_WOULDBLOCK` to signal the +nghttp2 library to stop sending further data. But when writing to the +bufferevent, we have to regulate the amount data to get buffered +ourselves to avoid using huge amounts of memory. To achieve this, we +check the size of the output buffer and if it reaches more than or +equal to ``OUTPUT_WOULDBLOCK_THRESHOLD`` bytes, we stop writing data +and return :macro:`NGHTTP2_ERR_WOULDBLOCK` to tell the library to stop +calling send_callback. + +The next bufferevent callback is ``readcb()``, which is invoked when +data is available to read in the bufferevent input buffer:: + + static void readcb(struct bufferevent *bev _U_, void *ptr) { + http2_session_data *session_data = (http2_session_data *)ptr; + if (session_recv(session_data) != 0) { + delete_http2_session_data(session_data); + return; + } + } + +In this function, we just call ``session_recv()`` to process incoming +data. + +The third bufferevent callback is ``writecb()``, which is invoked when all +data in the bufferevent output buffer has been sent:: + + static void writecb(struct bufferevent *bev, void *ptr) { + http2_session_data *session_data = (http2_session_data *)ptr; + if (evbuffer_get_length(bufferevent_get_output(bev)) > 0) { + return; + } + if (nghttp2_session_want_read(session_data->session) == 0 && + nghttp2_session_want_write(session_data->session) == 0) { + delete_http2_session_data(session_data); + return; + } + if (session_send(session_data) != 0) { + delete_http2_session_data(session_data); + return; + } + } + +First we check whether we should drop the connection or not. The nghttp2 +session object keeps track of reception and transmission of GOAWAY frames and +other error conditions as well. Using this information, the nghttp2 session +object will tell whether the connection should be dropped or not. More +specifically, if both `nghttp2_session_want_read()` and +`nghttp2_session_want_write()` return 0, we have no business left in the +connection. But since we are using bufferevent and its deferred callback +option, the bufferevent output buffer may contain pending data when the +``writecb()`` is called. To handle this, we check whether the output buffer is +empty or not. If all these conditions are met, we drop connection. + +Otherwise, we call ``session_send()`` to process the pending output +data. Remember that in ``send_callback()``, we must not write all data to +bufferevent to avoid excessive buffering. We continue processing pending data +when the output buffer becomes empty. + +We have already described the nghttp2 callback ``send_callback()``. Let's +learn about the remaining nghttp2 callbacks we setup in +``initialize_nghttp2_setup()`` function. + +The ``on_begin_headers_callback()`` function is invoked when the reception of +a header block in HEADERS or PUSH_PROMISE frame is started:: + + static int on_begin_headers_callback(nghttp2_session *session, + const nghttp2_frame *frame, + void *user_data) { + http2_session_data *session_data = (http2_session_data *)user_data; + http2_stream_data *stream_data; + + if (frame->hd.type != NGHTTP2_HEADERS || + frame->headers.cat != NGHTTP2_HCAT_REQUEST) { + return 0; + } + stream_data = create_http2_stream_data(session_data, frame->hd.stream_id); + nghttp2_session_set_stream_user_data(session, frame->hd.stream_id, + stream_data); + return 0; + } + +We are only interested in the HEADERS frame in this function. Since the +HEADERS frame has several roles in the HTTP/2 protocol, we check that it is a +request HEADERS, which opens new stream. If the frame is a request HEADERS, we +create a ``http2_stream_data`` object to store the stream related data. We +associate the created ``http2_stream_data`` object with the stream in the +nghttp2 session object using `nghttp2_set_stream_user_data()` to get the +object without searching through the doubly linked list. + +In this example server, we want to serve files relative to the current working +directory in which the program was invoked. Each header name/value pair is +emitted via ``on_header_callback`` function, which is called after +``on_begin_headers_callback()``:: + + static int on_header_callback(nghttp2_session *session, + const nghttp2_frame *frame, const uint8_t *name, + size_t namelen, const uint8_t *value, + size_t valuelen, uint8_t flags _U_, + void *user_data _U_) { + http2_stream_data *stream_data; + const char PATH[] = ":path"; + switch (frame->hd.type) { + case NGHTTP2_HEADERS: + if (frame->headers.cat != NGHTTP2_HCAT_REQUEST) { + break; + } + stream_data = + nghttp2_session_get_stream_user_data(session, frame->hd.stream_id); + if (!stream_data || stream_data->request_path) { + break; + } + if (namelen == sizeof(PATH) - 1 && memcmp(PATH, name, namelen) == 0) { + size_t j; + for (j = 0; j < valuelen && value[j] != '?'; ++j) + ; + stream_data->request_path = percent_decode(value, j); + } + break; + } + return 0; + } + +We search for the ``:path`` header field among the request headers and store +the requested path in the ``http2_stream_data`` object. In this example +program, we ignore ``:method`` header field and always treat the request as a +GET request. + +The ``on_frame_recv_callback()`` function is invoked when a frame is +fully received:: + + static int on_frame_recv_callback(nghttp2_session *session, + const nghttp2_frame *frame, void *user_data) { + http2_session_data *session_data = (http2_session_data *)user_data; + http2_stream_data *stream_data; + switch (frame->hd.type) { + case NGHTTP2_DATA: + case NGHTTP2_HEADERS: + /* Check that the client request has finished */ + if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) { + stream_data = + nghttp2_session_get_stream_user_data(session, frame->hd.stream_id); + /* For DATA and HEADERS frame, this callback may be called after + on_stream_close_callback. Check that stream still alive. */ + if (!stream_data) { + return 0; + } + return on_request_recv(session, session_data, stream_data); + } + break; + default: + break; + } + return 0; + } + +First we retrieve the ``http2_stream_data`` object associated with the stream +in ``on_begin_headers_callback()``. It is done using +`nghttp2_session_get_stream_user_data()`. If the requested path cannot be +served for some reason (e.g., file is not found), we send a 404 response, +which is done in ``error_reply()``. Otherwise, we open the requested file and +send its content. We send the header field ``:status`` as a single response +header. + +Sending the content of the file is done in ``send_response()`` function:: + + static int send_response(nghttp2_session *session, int32_t stream_id, + nghttp2_nv *nva, size_t nvlen, int fd) { + int rv; + nghttp2_data_provider data_prd; + data_prd.source.fd = fd; + data_prd.read_callback = file_read_callback; + + rv = nghttp2_submit_response(session, stream_id, nva, nvlen, &data_prd); + if (rv != 0) { + warnx("Fatal error: %s", nghttp2_strerror(rv)); + return -1; + } + return 0; + } + +The nghttp2 library uses the :type:`nghttp2_data_provider` structure to +send entity body to the remote peer. The ``source`` member of this +structure is a union and it can be either void pointer or int which is +intended to be used as file descriptor. In this example server, we use +the file descriptor. We also set the ``file_read_callback()`` callback +function to read the contents of the file:: + + static ssize_t file_read_callback(nghttp2_session *session _U_, + int32_t stream_id _U_, uint8_t *buf, + size_t length, uint32_t *data_flags, + nghttp2_data_source *source, + void *user_data _U_) { + int fd = source->fd; + ssize_t r; + while ((r = read(fd, buf, length)) == -1 && errno == EINTR) + ; + if (r == -1) { + return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; + } + if (r == 0) { + *data_flags |= NGHTTP2_DATA_FLAG_EOF; + } + return r; + } + +If an error happens while reading the file, we return +:macro:`NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE`. This tells the +library to send RST_STREAM to the stream. When all data has been read, set +the :macro:`NGHTTP2_DATA_FLAG_EOF` flag to ``*data_flags`` to tell the +nghttp2 library that we have finished reading the file. + +The `nghttp2_submit_response()` function is used to send the response to the +remote peer. + +The ``on_stream_close_callback()`` function is invoked when the stream +is about to close:: + + static int on_stream_close_callback(nghttp2_session *session, int32_t stream_id, + uint32_t error_code _U_, void *user_data) { + http2_session_data *session_data = (http2_session_data *)user_data; + http2_stream_data *stream_data; + + stream_data = nghttp2_session_get_stream_user_data(session, stream_id); + if (!stream_data) { + return 0; + } + remove_stream(session_data, stream_data); + delete_http2_stream_data(stream_data); + return 0; + } + +We destroy the ``http2_stream_data`` object in this function since the stream +is about to close and we no longer use that object. diff --git a/doc/tutorial-client.rst.in b/doc/tutorial-client.rst.in new file mode 100644 index 0000000..4f7fcfc --- /dev/null +++ b/doc/tutorial-client.rst.in @@ -0,0 +1,6 @@ +.. include:: @top_srcdir@/doc/sources/tutorial-client.rst + +libevent-client.c +----------------- + +.. literalinclude:: @top_srcdir@/examples/libevent-client.c diff --git a/doc/tutorial-hpack.rst.in b/doc/tutorial-hpack.rst.in new file mode 100644 index 0000000..832dedf --- /dev/null +++ b/doc/tutorial-hpack.rst.in @@ -0,0 +1,6 @@ +.. include:: @top_srcdir@/doc/sources/tutorial-hpack.rst + +deflate.c +--------- + +.. literalinclude:: @top_srcdir@/examples/deflate.c diff --git a/doc/tutorial-server.rst.in b/doc/tutorial-server.rst.in new file mode 100644 index 0000000..1972266 --- /dev/null +++ b/doc/tutorial-server.rst.in @@ -0,0 +1,6 @@ +.. include:: @top_srcdir@/doc/sources/tutorial-server.rst + +libevent-server.c +----------------- + +.. literalinclude:: @top_srcdir@/examples/libevent-server.c diff --git a/doc/types.rst b/doc/types.rst new file mode 100644 index 0000000..acb2ef1 --- /dev/null +++ b/doc/types.rst @@ -0,0 +1,914 @@ + +Types (structs, unions and typedefs) +==================================== +.. type:: nghttp2_session + + + The primary structure to hold the resources needed for a HTTP/2 + session. The details of this structure are intentionally hidden + from the public API. + + +.. type:: nghttp2_info + + + This struct is what `nghttp2_version()` returns. It holds + information about the particular nghttp2 version. + + .. member:: int age + + Age of this struct. This instance of nghttp2 sets it to + :macro:`NGHTTP2_VERSION_AGE` but a future version may bump it and + add more struct fields at the bottom + .. member:: int version_num + + the :macro:`NGHTTP2_VERSION_NUM` number (since age ==1) + .. member:: const char *version_str + + points to the :macro:`NGHTTP2_VERSION` string (since age ==1) + .. member:: const char *proto_str + + points to the :macro:`NGHTTP2_PROTO_VERSION_ID` string this + instance implements (since age ==1) + +.. type:: nghttp2_nv + + + The name/value pair, which mainly used to represent header fields. + + .. member:: uint8_t *name + + The *name* byte string. If this struct is presented from library + (e.g., :type:`nghttp2_on_frame_recv_callback`), *name* is + guaranteed to be NULL-terminated. When application is + constructing this struct, *name* is not required to be + NULL-terminated. + .. member:: uint8_t *value + + The *value* byte string. If this struct is presented from + library (e.g., :type:`nghttp2_on_frame_recv_callback`), *value* + is guaranteed to be NULL-terminated. When application is + constructing this struct, *value* is not required to be + NULL-terminated. + .. member:: size_t namelen + + The length of the *name*, excluding terminating NULL. + .. member:: size_t valuelen + + The length of the *value*, excluding terminating NULL. + .. member:: uint8_t flags + + Bitwise OR of one or more of :type:`nghttp2_nv_flag`. + +.. type:: nghttp2_frame_hd + + The frame header. + + .. member:: size_t length + + The length field of this frame, excluding frame header. + .. member:: int32_t stream_id + + The stream identifier (aka, stream ID) + .. member:: uint8_t type + + The type of this frame. See `nghttp2_frame_type`. + .. member:: uint8_t flags + + The flags. + .. member:: uint8_t reserved + + Reserved bit in frame header. Currently, this is always set to 0 + and application should not expect something useful in here. + +.. type:: nghttp2_data_source + + + This union represents the some kind of data source passed to + :type:`nghttp2_data_source_read_callback`. + + .. member:: int fd + + The integer field, suitable for a file descriptor. + .. member:: void *ptr + + The pointer to an arbitrary object. + +.. type:: typedef ssize_t (*nghttp2_data_source_read_callback)( nghttp2_session *session, int32_t stream_id, uint8_t *buf, size_t length, uint32_t *data_flags, nghttp2_data_source *source, void *user_data) + + + Callback function invoked when the library wants to read data from + the *source*. The read data is sent in the stream *stream_id*. + The implementation of this function must read at most *length* + bytes of data from *source* (or possibly other places) and store + them in *buf* and return number of data stored in *buf*. If EOF is + reached, set :macro:`NGHTTP2_DATA_FLAG_EOF` flag in *\*data_flags*. + + Sometime it is desirable to avoid copying data into *buf* and let + application to send data directly. To achieve this, set + :macro:`NGHTTP2_DATA_FLAG_NO_COPY` to *\*data_flags* (and possibly + other flags, just like when we do copy), and return the number of + bytes to send without copying data into *buf*. The library, seeing + :macro:`NGHTTP2_DATA_FLAG_NO_COPY`, will invoke + :type:`nghttp2_send_data_callback`. The application must send + complete DATA frame in that callback. + + If this callback is set by `nghttp2_submit_request()`, + `nghttp2_submit_response()` or `nghttp2_submit_headers()` and + `nghttp2_submit_data()` with flag parameter + :macro:`NGHTTP2_FLAG_END_STREAM` set, and + :macro:`NGHTTP2_DATA_FLAG_EOF` flag is set to *\*data_flags*, DATA + frame will have END_STREAM flag set. Usually, this is expected + behaviour and all are fine. One exception is send trailer header + fields. You cannot send trailers after sending frame with + END_STREAM set. To avoid this problem, one can set + :macro:`NGHTTP2_DATA_FLAG_NO_END_STREAM` along with + :macro:`NGHTTP2_DATA_FLAG_EOF` to signal the library not to set + END_STREAM in DATA frame. Then application can use + `nghttp2_submit_trailer()` to send trailers. + `nghttp2_submit_trailer()` can be called inside this callback. + + If the application wants to postpone DATA frames (e.g., + asynchronous I/O, or reading data blocks for long time), it is + achieved by returning :macro:`NGHTTP2_ERR_DEFERRED` without reading + any data in this invocation. The library removes DATA frame from + the outgoing queue temporarily. To move back deferred DATA frame + to outgoing queue, call `nghttp2_session_resume_data()`. In case + of error, there are 2 choices. Returning + :macro:`NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE` will close the stream + by issuing RST_STREAM with :macro:`NGHTTP2_INTERNAL_ERROR`. If a + different error code is desirable, use + `nghttp2_submit_rst_stream()` with a desired error code and then + return :macro:`NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE`. Returning + :macro:`NGHTTP2_ERR_CALLBACK_FAILURE` will signal the entire session + failure. +.. type:: nghttp2_data_provider + + + This struct represents the data source and the way to read a chunk + of data from it. + + .. member:: nghttp2_data_source source + + The data source. + .. member:: nghttp2_data_source_read_callback read_callback + + The callback function to read a chunk of data from the *source*. + +.. type:: nghttp2_data + + + The DATA frame. The received data is delivered via + :type:`nghttp2_on_data_chunk_recv_callback`. + + .. member:: size_t padlen + + The length of the padding in this frame. This includes PAD_HIGH + and PAD_LOW. + +.. type:: nghttp2_priority_spec + + + The structure to specify stream dependency. + + .. member:: int32_t stream_id + + The stream ID of the stream to depend on. Specifying 0 makes + stream not depend any other stream. + .. member:: int32_t weight + + The weight of this dependency. + .. member:: uint8_t exclusive + + nonzero means exclusive dependency + +.. type:: nghttp2_headers + + + The HEADERS frame. It has the following members: + + .. member:: nghttp2_frame_hd hd + + The frame header. + .. member:: size_t padlen + + The length of the padding in this frame. This includes PAD_HIGH + and PAD_LOW. + .. member:: nghttp2_priority_spec pri_spec + + The priority specification + .. member:: nghttp2_nv *nva + + The name/value pairs. + .. member:: size_t nvlen + + The number of name/value pairs in *nva*. + .. member:: nghttp2_headers_category cat + + The category of this HEADERS frame. + +.. type:: nghttp2_priority + + + The PRIORITY frame. It has the following members: + + .. member:: nghttp2_frame_hd hd + + The frame header. + .. member:: nghttp2_priority_spec pri_spec + + The priority specification. + +.. type:: nghttp2_rst_stream + + + The RST_STREAM frame. It has the following members: + + .. member:: nghttp2_frame_hd hd + + The frame header. + .. member:: uint32_t error_code + + The error code. See :type:`nghttp2_error_code`. + +.. type:: nghttp2_settings_entry + + + The SETTINGS ID/Value pair. It has the following members: + + .. member:: int32_t settings_id + + The SETTINGS ID. See :type:`nghttp2_settings_id`. + .. member:: uint32_t value + + The value of this entry. + +.. type:: nghttp2_settings + + + The SETTINGS frame. It has the following members: + + .. member:: nghttp2_frame_hd hd + + The frame header. + .. member:: size_t niv + + The number of SETTINGS ID/Value pairs in *iv*. + .. member:: nghttp2_settings_entry *iv + + The pointer to the array of SETTINGS ID/Value pair. + +.. type:: nghttp2_push_promise + + + The PUSH_PROMISE frame. It has the following members: + + .. member:: nghttp2_frame_hd hd + + The frame header. + .. member:: size_t padlen + + The length of the padding in this frame. This includes PAD_HIGH + and PAD_LOW. + .. member:: nghttp2_nv *nva + + The name/value pairs. + .. member:: size_t nvlen + + The number of name/value pairs in *nva*. + .. member:: int32_t promised_stream_id + + The promised stream ID + .. member:: uint8_t reserved + + Reserved bit. Currently this is always set to 0 and application + should not expect something useful in here. + +.. type:: nghttp2_ping + + + The PING frame. It has the following members: + + .. member:: nghttp2_frame_hd hd + + The frame header. + .. member:: uint8_t opaque_data[8] + + The opaque data + +.. type:: nghttp2_goaway + + + The GOAWAY frame. It has the following members: + + .. member:: nghttp2_frame_hd hd + + The frame header. + .. member:: int32_t last_stream_id + + The last stream stream ID. + .. member:: uint32_t error_code + + The error code. See :type:`nghttp2_error_code`. + .. member:: uint8_t *opaque_data + + The additional debug data + .. member:: size_t opaque_data_len + + The length of *opaque_data* member. + .. member:: uint8_t reserved + + Reserved bit. Currently this is always set to 0 and application + should not expect something useful in here. + +.. type:: nghttp2_window_update + + + The WINDOW_UPDATE frame. It has the following members: + + .. member:: nghttp2_frame_hd hd + + The frame header. + .. member:: int32_t window_size_increment + + The window size increment. + .. member:: uint8_t reserved + + Reserved bit. Currently this is always set to 0 and application + should not expect something useful in here. + +.. type:: nghttp2_extension + + + The extension frame. It has following members: + + .. member:: nghttp2_frame_hd hd + + The frame header. + .. member:: void *payload + + The pointer to extension payload. The exact pointer type is + determined by hd.type. + + Currently, no extension is supported. This is a place holder for + the future extensions. + +.. type:: nghttp2_frame + + + This union includes all frames to pass them to various function + calls as nghttp2_frame type. The CONTINUATION frame is omitted + from here because the library deals with it internally. + + .. member:: nghttp2_frame_hd hd + + The frame header, which is convenient to inspect frame header. + .. member:: nghttp2_data data + + The DATA frame. + .. member:: nghttp2_headers headers + + The HEADERS frame. + .. member:: nghttp2_priority priority + + The PRIORITY frame. + .. member:: nghttp2_rst_stream rst_stream + + The RST_STREAM frame. + .. member:: nghttp2_settings settings + + The SETTINGS frame. + .. member:: nghttp2_push_promise push_promise + + The PUSH_PROMISE frame. + .. member:: nghttp2_ping ping + + The PING frame. + .. member:: nghttp2_goaway goaway + + The GOAWAY frame. + .. member:: nghttp2_window_update window_update + + The WINDOW_UPDATE frame. + .. member:: nghttp2_extension ext + + The extension frame. + +.. type:: typedef ssize_t (*nghttp2_send_callback)(nghttp2_session *session, const uint8_t *data, size_t length, int flags, void *user_data) + + + Callback function invoked when *session* wants to send data to the + remote peer. The implementation of this function must send at most + *length* bytes of data stored in *data*. The *flags* is currently + not used and always 0. It must return the number of bytes sent if + it succeeds. If it cannot send any single byte without blocking, + it must return :macro:`NGHTTP2_ERR_WOULDBLOCK`. For other errors, + it must return :macro:`NGHTTP2_ERR_CALLBACK_FAILURE`. The + *user_data* pointer is the third argument passed in to the call to + `nghttp2_session_client_new()` or `nghttp2_session_server_new()`. + + This callback is required if the application uses + `nghttp2_session_send()` to send data to the remote endpoint. If + the application uses solely `nghttp2_session_mem_send()` instead, + this callback function is unnecessary. + + To set this callback to :type:`nghttp2_session_callbacks`, use + `nghttp2_session_callbacks_set_send_callback()`. +.. type:: typedef int (*nghttp2_send_data_callback)(nghttp2_session *session, nghttp2_frame *frame, const uint8_t *framehd, size_t length, nghttp2_data_source *source, void *user_data) + + + Callback function invoked when :macro:`NGHTTP2_DATA_FLAG_NO_COPY` is + used in :type:`nghttp2_data_source_read_callback` to send complete + DATA frame. + + The *frame* is a DATA frame to send. The *framehd* is the + serialized frame header (9 bytes). The *length* is the length of + application data to send (this does not include padding). The + *source* is the same pointer passed to + :type:`nghttp2_data_source_read_callback`. + + The application first must send frame header *framehd* of length 9 + bytes. If ``frame->padlen > 0``, send 1 byte of value + ``frame->padlen - 1``. Then send exactly *length* bytes of + application data. Finally, if ``frame->padlen > 0``, send + ``frame->padlen - 1`` bytes of zero (they are padding). + + The application has to send complete DATA frame in this callback. + If all data were written successfully, return 0. + + If it cannot send it all, just return + :macro:`NGHTTP2_ERR_WOULDBLOCK`; the library will call this callback + with the same parameters later (It is recommended to send complete + DATA frame at once in this function to deal with error; if partial + frame data has already sent, it is impossible to send another data + in that state, and all we can do is tear down connection). If + application decided to reset this stream, return + :macro:`NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE`, then the library + will send RST_STREAM with INTERNAL_ERROR as error code. The + application can also return :macro:`NGHTTP2_ERR_CALLBACK_FAILURE`, + which will result in connection closure. Returning any other value + is treated as :macro:`NGHTTP2_ERR_CALLBACK_FAILURE` is returned. +.. type:: typedef ssize_t (*nghttp2_recv_callback)(nghttp2_session *session, uint8_t *buf, size_t length, int flags, void *user_data) + + + Callback function invoked when *session* wants to receive data from + the remote peer. The implementation of this function must read at + most *length* bytes of data and store it in *buf*. The *flags* is + currently not used and always 0. It must return the number of + bytes written in *buf* if it succeeds. If it cannot read any + single byte without blocking, it must return + :macro:`NGHTTP2_ERR_WOULDBLOCK`. If it gets EOF before it reads any + single byte, it must return :macro:`NGHTTP2_ERR_EOF`. For other + errors, it must return :macro:`NGHTTP2_ERR_CALLBACK_FAILURE`. + Returning 0 is treated as :macro:`NGHTTP2_ERR_WOULDBLOCK`. The + *user_data* pointer is the third argument passed in to the call to + `nghttp2_session_client_new()` or `nghttp2_session_server_new()`. + + This callback is required if the application uses + `nghttp2_session_recv()` to receive data from the remote endpoint. + If the application uses solely `nghttp2_session_mem_recv()` + instead, this callback function is unnecessary. + + To set this callback to :type:`nghttp2_session_callbacks`, use + `nghttp2_session_callbacks_set_recv_callback()`. +.. type:: typedef int (*nghttp2_on_frame_recv_callback)(nghttp2_session *session, const nghttp2_frame *frame, void *user_data) + + + Callback function invoked by `nghttp2_session_recv()` and + `nghttp2_session_mem_recv()` when a frame is received. The + *user_data* pointer is the third argument passed in to the call to + `nghttp2_session_client_new()` or `nghttp2_session_server_new()`. + + If frame is HEADERS or PUSH_PROMISE, the ``nva`` and ``nvlen`` + member of their data structure are always ``NULL`` and 0 + respectively. The header name/value pairs are emitted via + :type:`nghttp2_on_header_callback`. + + For HEADERS, PUSH_PROMISE and DATA frames, this callback may be + called after stream is closed (see + :type:`nghttp2_on_stream_close_callback`). The application should + check that stream is still alive using its own stream management or + :func:`nghttp2_session_get_stream_user_data()`. + + Only HEADERS and DATA frame can signal the end of incoming data. + If ``frame->hd.flags & NGHTTP2_FLAG_END_STREAM`` is nonzero, the + *frame* is the last frame from the remote peer in this stream. + + This callback won't be called for CONTINUATION frames. + HEADERS/PUSH_PROMISE + CONTINUATIONs are treated as single frame. + + The implementation of this function must return 0 if it succeeds. + If nonzero value is returned, it is treated as fatal error and + `nghttp2_session_recv()` and `nghttp2_session_mem_recv()` functions + immediately return :macro:`NGHTTP2_ERR_CALLBACK_FAILURE`. + + To set this callback to :type:`nghttp2_session_callbacks`, use + `nghttp2_session_callbacks_set_on_frame_recv_callback()`. +.. type:: typedef int (*nghttp2_on_invalid_frame_recv_callback)( nghttp2_session *session, const nghttp2_frame *frame, int lib_error_code, void *user_data) + + + Callback function invoked by `nghttp2_session_recv()` and + `nghttp2_session_mem_recv()` when an invalid non-DATA frame is + received. The error is indicated by the *lib_error_code*, which is + one of the values defined in :type:`nghttp2_error`. When this + callback function is invoked, the library automatically submits + either RST_STREAM or GOAWAY frame. The *user_data* pointer is the + third argument passed in to the call to + `nghttp2_session_client_new()` or `nghttp2_session_server_new()`. + + If frame is HEADERS or PUSH_PROMISE, the ``nva`` and ``nvlen`` + member of their data structure are always ``NULL`` and 0 + respectively. + + The implementation of this function must return 0 if it succeeds. + If nonzero is returned, it is treated as fatal error and + `nghttp2_session_recv()` and `nghttp2_session_mem_recv()` functions + immediately return :macro:`NGHTTP2_ERR_CALLBACK_FAILURE`. + + To set this callback to :type:`nghttp2_session_callbacks`, use + `nghttp2_session_callbacks_set_on_invalid_frame_recv_callback()`. +.. type:: typedef int (*nghttp2_on_data_chunk_recv_callback)(nghttp2_session *session, uint8_t flags, int32_t stream_id, const uint8_t *data, size_t len, void *user_data) + + + Callback function invoked when a chunk of data in DATA frame is + received. The *stream_id* is the stream ID this DATA frame belongs + to. The *flags* is the flags of DATA frame which this data chunk + is contained. ``(flags & NGHTTP2_FLAG_END_STREAM) != 0`` does not + necessarily mean this chunk of data is the last one in the stream. + You should use :type:`nghttp2_on_frame_recv_callback` to know all + data frames are received. The *user_data* pointer is the third + argument passed in to the call to `nghttp2_session_client_new()` or + `nghttp2_session_server_new()`. + + If the application uses `nghttp2_session_mem_recv()`, it can return + :macro:`NGHTTP2_ERR_PAUSE` to make `nghttp2_session_mem_recv()` + return without processing further input bytes. The memory by + pointed by the *data* is retained until + `nghttp2_session_mem_recv()` or `nghttp2_session_recv()` is called. + The application must retain the input bytes which was used to + produce the *data* parameter, because it may refer to the memory + region included in the input bytes. + + The implementation of this function must return 0 if it succeeds. + If nonzero is returned, it is treated as fatal error, and + `nghttp2_session_recv()` and `nghttp2_session_mem_recv()` functions + immediately return :macro:`NGHTTP2_ERR_CALLBACK_FAILURE`. + + To set this callback to :type:`nghttp2_session_callbacks`, use + `nghttp2_session_callbacks_set_on_data_chunk_recv_callback()`. +.. type:: typedef int (*nghttp2_before_frame_send_callback)(nghttp2_session *session, const nghttp2_frame *frame, void *user_data) + + + Callback function invoked just before the non-DATA frame *frame* is + sent. The *user_data* pointer is the third argument passed in to + the call to `nghttp2_session_client_new()` or + `nghttp2_session_server_new()`. + + The implementation of this function must return 0 if it succeeds. + If nonzero is returned, it is treated as fatal error and + `nghttp2_session_send()` and `nghttp2_session_mem_send()` functions + immediately return :macro:`NGHTTP2_ERR_CALLBACK_FAILURE`. + + To set this callback to :type:`nghttp2_session_callbacks`, use + `nghttp2_session_callbacks_set_before_frame_send_callback()`. +.. type:: typedef int (*nghttp2_on_frame_send_callback)(nghttp2_session *session, const nghttp2_frame *frame, void *user_data) + + + Callback function invoked after the frame *frame* is sent. The + *user_data* pointer is the third argument passed in to the call to + `nghttp2_session_client_new()` or `nghttp2_session_server_new()`. + + The implementation of this function must return 0 if it succeeds. + If nonzero is returned, it is treated as fatal error and + `nghttp2_session_send()` and `nghttp2_session_mem_send()` functions + immediately return :macro:`NGHTTP2_ERR_CALLBACK_FAILURE`. + + To set this callback to :type:`nghttp2_session_callbacks`, use + `nghttp2_session_callbacks_set_on_frame_send_callback()`. +.. type:: typedef int (*nghttp2_on_frame_not_send_callback)(nghttp2_session *session, const nghttp2_frame *frame, int lib_error_code, void *user_data) + + + Callback function invoked after the non-DATA frame *frame* is not + sent because of the error. The error is indicated by the + *lib_error_code*, which is one of the values defined in + :type:`nghttp2_error`. The *user_data* pointer is the third + argument passed in to the call to `nghttp2_session_client_new()` or + `nghttp2_session_server_new()`. + + The implementation of this function must return 0 if it succeeds. + If nonzero is returned, it is treated as fatal error and + `nghttp2_session_send()` and `nghttp2_session_mem_send()` functions + immediately return :macro:`NGHTTP2_ERR_CALLBACK_FAILURE`. + + `nghttp2_session_get_stream_user_data()` can be used to get + associated data. + + To set this callback to :type:`nghttp2_session_callbacks`, use + `nghttp2_session_callbacks_set_on_frame_not_send_callback()`. +.. type:: typedef int (*nghttp2_on_stream_close_callback)(nghttp2_session *session, int32_t stream_id, uint32_t error_code, void *user_data) + + + Callback function invoked when the stream *stream_id* is closed. + The reason of closure is indicated by the *error_code*. The + *error_code* is usually one of :macro:`nghttp2_error_code`, but that + is not guaranteed. The stream_user_data, which was specified in + `nghttp2_submit_request()` or `nghttp2_submit_headers()`, is still + available in this function. The *user_data* pointer is the third + argument passed in to the call to `nghttp2_session_client_new()` or + `nghttp2_session_server_new()`. + + This function is also called for a stream in reserved state. + + The implementation of this function must return 0 if it succeeds. + If nonzero is returned, it is treated as fatal error and + `nghttp2_session_recv()`, `nghttp2_session_mem_recv()`, + `nghttp2_session_send()`, and `nghttp2_session_mem_send()` + functions immediately return :macro:`NGHTTP2_ERR_CALLBACK_FAILURE`. + + To set this callback to :type:`nghttp2_session_callbacks`, use + `nghttp2_session_callbacks_set_on_stream_close_callback()`. +.. type:: typedef int (*nghttp2_on_begin_headers_callback)(nghttp2_session *session, const nghttp2_frame *frame, void *user_data) + + + Callback function invoked when the reception of header block in + HEADERS or PUSH_PROMISE is started. Each header name/value pair + will be emitted by :type:`nghttp2_on_header_callback`. + + The ``frame->hd.flags`` may not have + :macro:`NGHTTP2_FLAG_END_HEADERS` flag set, which indicates that one + or more CONTINUATION frames are involved. But the application does + not need to care about that because the header name/value pairs are + emitted transparently regardless of CONTINUATION frames. + + The server applications probably create an object to store + information about new stream if ``frame->hd.type == + NGHTTP2_HEADERS`` and ``frame->headers.cat == + NGHTTP2_HCAT_REQUEST``. If *session* is configured as server side, + ``frame->headers.cat`` is either ``NGHTTP2_HCAT_REQUEST`` + containing request headers or ``NGHTTP2_HCAT_HEADERS`` containing + trailer headers and never get PUSH_PROMISE in this callback. + + For the client applications, ``frame->hd.type`` is either + ``NGHTTP2_HEADERS`` or ``NGHTTP2_PUSH_PROMISE``. In case of + ``NGHTTP2_HEADERS``, ``frame->headers.cat == + NGHTTP2_HCAT_RESPONSE`` means that it is the first response + headers, but it may be non-final response which is indicated by 1xx + status code. In this case, there may be zero or more HEADERS frame + with ``frame->headers.cat == NGHTTP2_HCAT_HEADERS`` which has + non-final response code and finally client gets exactly one HEADERS + frame with ``frame->headers.cat == NGHTTP2_HCAT_HEADERS`` + containing final response headers (non-1xx status code). The + trailer headers also has ``frame->headers.cat == + NGHTTP2_HCAT_HEADERS`` which does not contain any status code. + + Returning :macro:`NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE` will close + the stream (promised stream if frame is PUSH_PROMISE) by issuing + RST_STREAM with :macro:`NGHTTP2_INTERNAL_ERROR`. In this case, + :type:`nghttp2_on_header_callback` and + :type:`nghttp2_on_frame_recv_callback` will not be invoked. If a + different error code is desirable, use + `nghttp2_submit_rst_stream()` with a desired error code and then + return :macro:`NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE`. Again, use + ``frame->push_promise.promised_stream_id`` as stream_id parameter + in `nghttp2_submit_rst_stream()` if frame is PUSH_PROMISE. + + The implementation of this function must return 0 if it succeeds. + It can return :macro:`NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE` to + reset the stream (promised stream if frame is PUSH_PROMISE). For + critical errors, it must return + :macro:`NGHTTP2_ERR_CALLBACK_FAILURE`. If the other value is + returned, it is treated as if :macro:`NGHTTP2_ERR_CALLBACK_FAILURE` + is returned. If :macro:`NGHTTP2_ERR_CALLBACK_FAILURE` is returned, + `nghttp2_session_mem_recv()` function will immediately return + :macro:`NGHTTP2_ERR_CALLBACK_FAILURE`. + + To set this callback to :type:`nghttp2_session_callbacks`, use + `nghttp2_session_callbacks_set_on_begin_headers_callback()`. +.. type:: typedef int (*nghttp2_on_header_callback)(nghttp2_session *session, const nghttp2_frame *frame, const uint8_t *name, size_t namelen, const uint8_t *value, size_t valuelen, uint8_t flags, void *user_data) + + + Callback function invoked when a header name/value pair is received + for the *frame*. The *name* of length *namelen* is header name. + The *value* of length *valuelen* is header value. The *flags* is + bitwise OR of one or more of :type:`nghttp2_nv_flag`. + + If :macro:`NGHTTP2_NV_FLAG_NO_INDEX` is set in *flags*, the receiver + must not index this name/value pair when forwarding it to the next + hop. More specifically, "Literal Header Field never Indexed" + representation must be used in HPACK encoding. + + When this callback is invoked, ``frame->hd.type`` is either + :macro:`NGHTTP2_HEADERS` or :macro:`NGHTTP2_PUSH_PROMISE`. After all + header name/value pairs are processed with this callback, and no + error has been detected, :type:`nghttp2_on_frame_recv_callback` + will be invoked. If there is an error in decompression, + :type:`nghttp2_on_frame_recv_callback` for the *frame* will not be + invoked. + + Both *name* and *value* are guaranteed to be NULL-terminated. The + *namelen* and *valuelen* do not include terminal NULL. If + `nghttp2_option_set_no_http_messaging()` is used with nonzero + value, NULL character may be included in *name* or *value* before + terminating NULL. + + Please note that unless `nghttp2_option_set_no_http_messaging()` is + used, nghttp2 library does perform validation against the *name* + and the *value* using `nghttp2_check_header_name()` and + `nghttp2_check_header_value()`. In addition to this, nghttp2 + performs vaidation based on HTTP Messaging rule, which is briefly + explained in :ref:`http-messaging` section. + + If the application uses `nghttp2_session_mem_recv()`, it can return + :macro:`NGHTTP2_ERR_PAUSE` to make `nghttp2_session_mem_recv()` + return without processing further input bytes. The memory pointed + by *frame*, *name* and *value* parameters are retained until + `nghttp2_session_mem_recv()` or `nghttp2_session_recv()` is called. + The application must retain the input bytes which was used to + produce these parameters, because it may refer to the memory region + included in the input bytes. + + Returning :macro:`NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE` will close + the stream (promised stream if frame is PUSH_PROMISE) by issuing + RST_STREAM with :macro:`NGHTTP2_INTERNAL_ERROR`. In this case, + :type:`nghttp2_on_header_callback` and + :type:`nghttp2_on_frame_recv_callback` will not be invoked. If a + different error code is desirable, use + `nghttp2_submit_rst_stream()` with a desired error code and then + return :macro:`NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE`. Again, use + ``frame->push_promise.promised_stream_id`` as stream_id parameter + in `nghttp2_submit_rst_stream()` if frame is PUSH_PROMISE. + + The implementation of this function must return 0 if it succeeds. + It may return :macro:`NGHTTP2_ERR_PAUSE` or + :macro:`NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE`. For other critical + failures, it must return :macro:`NGHTTP2_ERR_CALLBACK_FAILURE`. If + the other nonzero value is returned, it is treated as + :macro:`NGHTTP2_ERR_CALLBACK_FAILURE`. If + :macro:`NGHTTP2_ERR_CALLBACK_FAILURE` is returned, + `nghttp2_session_recv()` and `nghttp2_session_mem_recv()` functions + immediately return :macro:`NGHTTP2_ERR_CALLBACK_FAILURE`. + + To set this callback to :type:`nghttp2_session_callbacks`, use + `nghttp2_session_callbacks_set_on_header_callback()`. +.. type:: typedef ssize_t (*nghttp2_select_padding_callback)(nghttp2_session *session, const nghttp2_frame *frame, size_t max_payloadlen, void *user_data) + + + Callback function invoked when the library asks application how + many padding bytes are required for the transmission of the + *frame*. The application must choose the total length of payload + including padded bytes in range [frame->hd.length, max_payloadlen], + inclusive. Choosing number not in this range will be treated as + :macro:`NGHTTP2_ERR_CALLBACK_FAILURE`. Returning + ``frame->hd.length`` means no padding is added. Returning + :macro:`NGHTTP2_ERR_CALLBACK_FAILURE` will make + `nghttp2_session_send()` and `nghttp2_session_mem_send()` functions + immediately return :macro:`NGHTTP2_ERR_CALLBACK_FAILURE`. + + To set this callback to :type:`nghttp2_session_callbacks`, use + `nghttp2_session_callbacks_set_select_padding_callback()`. +.. type:: typedef ssize_t (*nghttp2_data_source_read_length_callback)( nghttp2_session *session, uint8_t frame_type, int32_t stream_id, int32_t session_remote_window_size, int32_t stream_remote_window_size, uint32_t remote_max_frame_size, void *user_data) + + + Callback function invoked when library wants to get max length of + data to send data to the remote peer. The implementation of this + function should return a value in the following range. [1, + min(*session_remote_window_size*, *stream_remote_window_size*, + *remote_max_frame_size*)]. If a value greater than this range is + returned than the max allow value will be used. Returning a value + smaller than this range is treated as + :macro:`NGHTTP2_ERR_CALLBACK_FAILURE`. The *frame_type* is provided + for future extensibility and identifies the type of frame (see + :type:`nghttp2_frame_type`) for which to get the length for. + Currently supported frame types are: :macro:`NGHTTP2_DATA`. + + This callback can be used to control the length in bytes for which + :type:`nghttp2_data_source_read_callback` is allowed to send to the + remote endpoint. This callback is optional. Returning + :macro:`NGHTTP2_ERR_CALLBACK_FAILURE` will signal the entire session + failure. + + To set this callback to :type:`nghttp2_session_callbacks`, use + `nghttp2_session_callbacks_set_data_source_read_length_callback()`. +.. type:: typedef int (*nghttp2_on_begin_frame_callback)(nghttp2_session *session, const nghttp2_frame_hd *hd, void *user_data) + + + Callback function invoked when a frame header is received. The + *hd* points to received frame header. + + Unlike :type:`nghttp2_on_frame_recv_callback`, this callback will + also be called when frame header of CONTINUATION frame is received. + + If both :type:`nghttp2_on_begin_frame_callback` and + :type:`nghttp2_on_begin_headers_callback` are set and HEADERS or + PUSH_PROMISE is received, :type:`nghttp2_on_begin_frame_callback` + will be called first. + + The implementation of this function must return 0 if it succeeds. + If nonzero value is returned, it is treated as fatal error and + `nghttp2_session_recv()` and `nghttp2_session_mem_recv()` functions + immediately return :macro:`NGHTTP2_ERR_CALLBACK_FAILURE`. + + To set this callback to :type:`nghttp2_session_callbacks`, use + `nghttp2_session_callbacks_set_on_begin_frame_callback()`. +.. type:: nghttp2_session_callbacks + + + Callback functions for :type:`nghttp2_session`. The details of + this structure are intentionally hidden from the public API. + + +.. type:: typedef void *(*nghttp2_malloc)(size_t size, void *mem_user_data) + + + Custom memory allocator to replace malloc(). The *mem_user_data* + is the mem_user_data member of :type:`nghttp2_mem` structure. +.. type:: typedef void (*nghttp2_free)(void *ptr, void *mem_user_data) + + + Custom memory allocator to replace free(). The *mem_user_data* is + the mem_user_data member of :type:`nghttp2_mem` structure. +.. type:: typedef void *(*nghttp2_calloc)(size_t nmemb, size_t size, void *mem_user_data) + + + Custom memory allocator to replace calloc(). The *mem_user_data* + is the mem_user_data member of :type:`nghttp2_mem` structure. +.. type:: typedef void *(*nghttp2_realloc)(void *ptr, size_t size, void *mem_user_data) + + + Custom memory allocator to replace realloc(). The *mem_user_data* + is the mem_user_data member of :type:`nghttp2_mem` structure. +.. type:: nghttp2_mem + + + Custom memory allocator functions and user defined pointer. The + *mem_user_data* member is passed to each allocator function. This + can be used, for example, to achieve per-session memory pool. + + In the following example code, ``my_malloc``, ``my_free``, + ``my_calloc`` and ``my_realloc`` are the replacement of the + standard allocators ``malloc``, ``free``, ``calloc`` and + ``realloc`` respectively:: + + void *my_malloc_cb(size_t size, void *mem_user_data) { + return my_malloc(size); + } + + void my_free_cb(void *ptr, void *mem_user_data) { my_free(ptr); } + + void *my_calloc_cb(size_t nmemb, size_t size, void *mem_user_data) { + return my_calloc(nmemb, size); + } + + void *my_realloc_cb(void *ptr, size_t size, void *mem_user_data) { + return my_realloc(ptr, size); + } + + void session_new() { + nghttp2_session *session; + nghttp2_session_callbacks *callbacks; + nghttp2_mem mem = {NULL, my_malloc_cb, my_free_cb, my_calloc_cb, + my_realloc_cb}; + + ... + + nghttp2_session_client_new3(&session, callbacks, NULL, NULL, &mem); + + ... + } + + .. member:: void *mem_user_data + + An arbitrary user supplied data. This is passed to each + allocator function. + .. member:: nghttp2_malloc malloc + + Custom allocator function to replace malloc(). + .. member:: nghttp2_free free + + Custom allocator function to replace free(). + .. member:: nghttp2_calloc calloc + + Custom allocator function to replace calloc(). + .. member:: nghttp2_realloc realloc + + Custom allocator function to replace realloc(). + +.. type:: nghttp2_option + + + Configuration options for :type:`nghttp2_session`. The details of + this structure are intentionally hidden from the public API. + + +.. type:: nghttp2_hd_deflater + + + HPACK deflater object. + + +.. type:: nghttp2_hd_inflater + + + HPACK inflater object. + + diff --git a/examples/Makefile.am b/examples/Makefile.am new file mode 100644 index 0000000..5bcf2d1 --- /dev/null +++ b/examples/Makefile.am @@ -0,0 +1,95 @@ +# nghttp2 - HTTP/2 C Library + +# Copyright (c) 2012 Tatsuhiro Tsujikawa + +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: + +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. + +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +if ENABLE_EXAMPLES + +AM_CFLAGS = $(WARNCFLAGS) +AM_CPPFLAGS = \ + -Wall \ + -I$(top_srcdir)/lib/includes \ + -I$(top_builddir)/lib/includes \ + -I$(top_srcdir)/src/includes \ + -I$(top_srcdir)/third-party \ + @LIBEVENT_OPENSSL_CFLAGS@ \ + @OPENSSL_CFLAGS@ \ + @DEFS@ +LDADD = $(top_builddir)/lib/libnghttp2.la \ + $(top_builddir)/third-party/libhttp-parser.la \ + @LIBEVENT_OPENSSL_LIBS@ \ + @OPENSSL_LIBS@ + +noinst_PROGRAMS = client libevent-client libevent-server deflate + +client_SOURCES = client.c + +libevent_client_SOURCES = libevent-client.c + +libevent_server_SOURCES = libevent-server.c + +deflate_SOURCES = deflate.c + +if ENABLE_TINY_NGHTTPD + +noinst_PROGRAMS += tiny-nghttpd + +tiny_nghttpd_SOURCES = tiny-nghttpd.c + +endif # ENABLE_TINY_NGHTTPD + +if ENABLE_ASIO_LIB + +noinst_PROGRAMS += asio-sv asio-sv2 asio-cl asio-cl2 + +# AM_CPPFLAGS must be placed first, so that header file (e.g., +# nghttp2/nghttp2.h) in this package is used rather than installed +# one. +ASIOCPPFLAGS = ${AM_CPPFLAGS} ${BOOST_CPPFLAGS} +ASIOLDADD = $(top_builddir)/lib/libnghttp2.la \ + $(top_builddir)/src/libnghttp2_asio.la @JEMALLOC_LIBS@ \ + $(top_builddir)/third-party/libhttp-parser.la \ + ${BOOST_LDFLAGS} \ + ${BOOST_ASIO_LIB} \ + ${BOOST_THREAD_LIB} \ + ${BOOST_SYSTEM_LIB} \ + @OPENSSL_LIBS@ \ + @APPLDFLAGS@ + +asio_sv_SOURCES = asio-sv.cc +asio_sv_CPPFLAGS = ${ASIOCPPFLAGS} +asio_sv_LDADD = ${ASIOLDADD} + +asio_sv2_SOURCES = asio-sv2.cc +asio_sv2_CPPFLAGS = ${ASIOCPPFLAGS} +asio_sv2_LDADD = ${ASIOLDADD} + +asio_cl_SOURCES = asio-cl.cc +asio_cl_CPPFLAGS = ${ASIOCPPFLAGS} +asio_cl_LDADD = ${ASIOLDADD} + +asio_cl2_SOURCES = asio-cl2.cc +asio_cl2_CPPFLAGS = ${ASIOCPPFLAGS} +asio_cl2_LDADD = ${ASIOLDADD} + +endif # ENABLE_ASIO_LIB + +endif # ENABLE_EXAMPLES diff --git a/examples/Makefile.in b/examples/Makefile.in new file mode 100644 index 0000000..9b55d0a --- /dev/null +++ b/examples/Makefile.in @@ -0,0 +1,926 @@ +# Makefile.in generated by automake 1.14.1 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2013 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +# nghttp2 - HTTP/2 C Library + +# Copyright (c) 2012 Tatsuhiro Tsujikawa + +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: + +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. + +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +VPATH = @srcdir@ +am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)' +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +target_triplet = @target@ +@ENABLE_EXAMPLES_TRUE@noinst_PROGRAMS = client$(EXEEXT) \ +@ENABLE_EXAMPLES_TRUE@ libevent-client$(EXEEXT) \ +@ENABLE_EXAMPLES_TRUE@ libevent-server$(EXEEXT) \ +@ENABLE_EXAMPLES_TRUE@ deflate$(EXEEXT) $(am__EXEEXT_1) \ +@ENABLE_EXAMPLES_TRUE@ $(am__EXEEXT_2) +@ENABLE_EXAMPLES_TRUE@@ENABLE_TINY_NGHTTPD_TRUE@am__append_1 = tiny-nghttpd +@ENABLE_ASIO_LIB_TRUE@@ENABLE_EXAMPLES_TRUE@am__append_2 = asio-sv asio-sv2 asio-cl asio-cl2 +subdir = examples +DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \ + $(top_srcdir)/depcomp +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/ax_boost_asio.m4 \ + $(top_srcdir)/m4/ax_boost_base.m4 \ + $(top_srcdir)/m4/ax_boost_system.m4 \ + $(top_srcdir)/m4/ax_boost_thread.m4 \ + $(top_srcdir)/m4/ax_check_compile_flag.m4 \ + $(top_srcdir)/m4/ax_cxx_compile_stdcxx_11.m4 \ + $(top_srcdir)/m4/ax_have_epoll.m4 \ + $(top_srcdir)/m4/ax_python_devel.m4 \ + $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/libxml2.m4 \ + $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ + $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +@ENABLE_EXAMPLES_TRUE@@ENABLE_TINY_NGHTTPD_TRUE@am__EXEEXT_1 = tiny-nghttpd$(EXEEXT) +@ENABLE_ASIO_LIB_TRUE@@ENABLE_EXAMPLES_TRUE@am__EXEEXT_2 = \ +@ENABLE_ASIO_LIB_TRUE@@ENABLE_EXAMPLES_TRUE@ asio-sv$(EXEEXT) \ +@ENABLE_ASIO_LIB_TRUE@@ENABLE_EXAMPLES_TRUE@ asio-sv2$(EXEEXT) \ +@ENABLE_ASIO_LIB_TRUE@@ENABLE_EXAMPLES_TRUE@ asio-cl$(EXEEXT) \ +@ENABLE_ASIO_LIB_TRUE@@ENABLE_EXAMPLES_TRUE@ asio-cl2$(EXEEXT) +PROGRAMS = $(noinst_PROGRAMS) +am__asio_cl_SOURCES_DIST = asio-cl.cc +@ENABLE_ASIO_LIB_TRUE@@ENABLE_EXAMPLES_TRUE@am_asio_cl_OBJECTS = asio_cl-asio-cl.$(OBJEXT) +asio_cl_OBJECTS = $(am_asio_cl_OBJECTS) +am__DEPENDENCIES_1 = +@ENABLE_ASIO_LIB_TRUE@@ENABLE_EXAMPLES_TRUE@am__DEPENDENCIES_2 = $(top_builddir)/lib/libnghttp2.la \ +@ENABLE_ASIO_LIB_TRUE@@ENABLE_EXAMPLES_TRUE@ $(top_builddir)/src/libnghttp2_asio.la \ +@ENABLE_ASIO_LIB_TRUE@@ENABLE_EXAMPLES_TRUE@ $(top_builddir)/third-party/libhttp-parser.la \ +@ENABLE_ASIO_LIB_TRUE@@ENABLE_EXAMPLES_TRUE@ $(am__DEPENDENCIES_1) \ +@ENABLE_ASIO_LIB_TRUE@@ENABLE_EXAMPLES_TRUE@ $(am__DEPENDENCIES_1) \ +@ENABLE_ASIO_LIB_TRUE@@ENABLE_EXAMPLES_TRUE@ $(am__DEPENDENCIES_1) \ +@ENABLE_ASIO_LIB_TRUE@@ENABLE_EXAMPLES_TRUE@ $(am__DEPENDENCIES_1) +@ENABLE_ASIO_LIB_TRUE@@ENABLE_EXAMPLES_TRUE@asio_cl_DEPENDENCIES = $(am__DEPENDENCIES_2) +AM_V_lt = $(am__v_lt_@AM_V@) +am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) +am__v_lt_0 = --silent +am__v_lt_1 = +am__asio_cl2_SOURCES_DIST = asio-cl2.cc +@ENABLE_ASIO_LIB_TRUE@@ENABLE_EXAMPLES_TRUE@am_asio_cl2_OBJECTS = asio_cl2-asio-cl2.$(OBJEXT) +asio_cl2_OBJECTS = $(am_asio_cl2_OBJECTS) +@ENABLE_ASIO_LIB_TRUE@@ENABLE_EXAMPLES_TRUE@asio_cl2_DEPENDENCIES = $(am__DEPENDENCIES_2) +am__asio_sv_SOURCES_DIST = asio-sv.cc +@ENABLE_ASIO_LIB_TRUE@@ENABLE_EXAMPLES_TRUE@am_asio_sv_OBJECTS = asio_sv-asio-sv.$(OBJEXT) +asio_sv_OBJECTS = $(am_asio_sv_OBJECTS) +@ENABLE_ASIO_LIB_TRUE@@ENABLE_EXAMPLES_TRUE@asio_sv_DEPENDENCIES = $(am__DEPENDENCIES_2) +am__asio_sv2_SOURCES_DIST = asio-sv2.cc +@ENABLE_ASIO_LIB_TRUE@@ENABLE_EXAMPLES_TRUE@am_asio_sv2_OBJECTS = asio_sv2-asio-sv2.$(OBJEXT) +asio_sv2_OBJECTS = $(am_asio_sv2_OBJECTS) +@ENABLE_ASIO_LIB_TRUE@@ENABLE_EXAMPLES_TRUE@asio_sv2_DEPENDENCIES = $(am__DEPENDENCIES_2) +am__client_SOURCES_DIST = client.c +@ENABLE_EXAMPLES_TRUE@am_client_OBJECTS = client.$(OBJEXT) +client_OBJECTS = $(am_client_OBJECTS) +client_LDADD = $(LDADD) +@ENABLE_EXAMPLES_TRUE@client_DEPENDENCIES = \ +@ENABLE_EXAMPLES_TRUE@ $(top_builddir)/lib/libnghttp2.la \ +@ENABLE_EXAMPLES_TRUE@ $(top_builddir)/third-party/libhttp-parser.la +am__deflate_SOURCES_DIST = deflate.c +@ENABLE_EXAMPLES_TRUE@am_deflate_OBJECTS = deflate.$(OBJEXT) +deflate_OBJECTS = $(am_deflate_OBJECTS) +deflate_LDADD = $(LDADD) +@ENABLE_EXAMPLES_TRUE@deflate_DEPENDENCIES = \ +@ENABLE_EXAMPLES_TRUE@ $(top_builddir)/lib/libnghttp2.la \ +@ENABLE_EXAMPLES_TRUE@ $(top_builddir)/third-party/libhttp-parser.la +am__libevent_client_SOURCES_DIST = libevent-client.c +@ENABLE_EXAMPLES_TRUE@am_libevent_client_OBJECTS = \ +@ENABLE_EXAMPLES_TRUE@ libevent-client.$(OBJEXT) +libevent_client_OBJECTS = $(am_libevent_client_OBJECTS) +libevent_client_LDADD = $(LDADD) +@ENABLE_EXAMPLES_TRUE@libevent_client_DEPENDENCIES = \ +@ENABLE_EXAMPLES_TRUE@ $(top_builddir)/lib/libnghttp2.la \ +@ENABLE_EXAMPLES_TRUE@ $(top_builddir)/third-party/libhttp-parser.la +am__libevent_server_SOURCES_DIST = libevent-server.c +@ENABLE_EXAMPLES_TRUE@am_libevent_server_OBJECTS = \ +@ENABLE_EXAMPLES_TRUE@ libevent-server.$(OBJEXT) +libevent_server_OBJECTS = $(am_libevent_server_OBJECTS) +libevent_server_LDADD = $(LDADD) +@ENABLE_EXAMPLES_TRUE@libevent_server_DEPENDENCIES = \ +@ENABLE_EXAMPLES_TRUE@ $(top_builddir)/lib/libnghttp2.la \ +@ENABLE_EXAMPLES_TRUE@ $(top_builddir)/third-party/libhttp-parser.la +am__tiny_nghttpd_SOURCES_DIST = tiny-nghttpd.c +@ENABLE_EXAMPLES_TRUE@@ENABLE_TINY_NGHTTPD_TRUE@am_tiny_nghttpd_OBJECTS = tiny-nghttpd.$(OBJEXT) +tiny_nghttpd_OBJECTS = $(am_tiny_nghttpd_OBJECTS) +tiny_nghttpd_LDADD = $(LDADD) +@ENABLE_EXAMPLES_TRUE@tiny_nghttpd_DEPENDENCIES = \ +@ENABLE_EXAMPLES_TRUE@ $(top_builddir)/lib/libnghttp2.la \ +@ENABLE_EXAMPLES_TRUE@ $(top_builddir)/third-party/libhttp-parser.la +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__depfiles_maybe = depfiles +am__mv = mv -f +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CFLAGS) $(CFLAGS) +AM_V_CC = $(am__v_CC_@AM_V@) +am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) +am__v_CC_0 = @echo " CC " $@; +am__v_CC_1 = +CCLD = $(CC) +LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CCLD = $(am__v_CCLD_@AM_V@) +am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) +am__v_CCLD_0 = @echo " CCLD " $@; +am__v_CCLD_1 = +CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) +LTCXXCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CXXFLAGS) $(CXXFLAGS) +AM_V_CXX = $(am__v_CXX_@AM_V@) +am__v_CXX_ = $(am__v_CXX_@AM_DEFAULT_V@) +am__v_CXX_0 = @echo " CXX " $@; +am__v_CXX_1 = +CXXLD = $(CXX) +CXXLINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CXXLD) $(AM_CXXFLAGS) \ + $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CXXLD = $(am__v_CXXLD_@AM_V@) +am__v_CXXLD_ = $(am__v_CXXLD_@AM_DEFAULT_V@) +am__v_CXXLD_0 = @echo " CXXLD " $@; +am__v_CXXLD_1 = +SOURCES = $(asio_cl_SOURCES) $(asio_cl2_SOURCES) $(asio_sv_SOURCES) \ + $(asio_sv2_SOURCES) $(client_SOURCES) $(deflate_SOURCES) \ + $(libevent_client_SOURCES) $(libevent_server_SOURCES) \ + $(tiny_nghttpd_SOURCES) +DIST_SOURCES = $(am__asio_cl_SOURCES_DIST) \ + $(am__asio_cl2_SOURCES_DIST) $(am__asio_sv_SOURCES_DIST) \ + $(am__asio_sv2_SOURCES_DIST) $(am__client_SOURCES_DIST) \ + $(am__deflate_SOURCES_DIST) \ + $(am__libevent_client_SOURCES_DIST) \ + $(am__libevent_server_SOURCES_DIST) \ + $(am__tiny_nghttpd_SOURCES_DIST) +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +ETAGS = etags +CTAGS = ctags +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +APPLDFLAGS = @APPLDFLAGS@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +BOOST_ASIO_LIB = @BOOST_ASIO_LIB@ +BOOST_CPPFLAGS = @BOOST_CPPFLAGS@ +BOOST_LDFLAGS = @BOOST_LDFLAGS@ +BOOST_SYSTEM_LIB = @BOOST_SYSTEM_LIB@ +BOOST_THREAD_LIB = @BOOST_THREAD_LIB@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CUNIT_CFLAGS = @CUNIT_CFLAGS@ +CUNIT_LIBS = @CUNIT_LIBS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +CYTHON = @CYTHON@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DLLTOOL = @DLLTOOL@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +GREP = @GREP@ +HAVE_CXX11 = @HAVE_CXX11@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +JANSSON_CFLAGS = @JANSSON_CFLAGS@ +JANSSON_LIBS = @JANSSON_LIBS@ +JEMALLOC_LIBS = @JEMALLOC_LIBS@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBEVENT_OPENSSL_CFLAGS = @LIBEVENT_OPENSSL_CFLAGS@ +LIBEVENT_OPENSSL_LIBS = @LIBEVENT_OPENSSL_LIBS@ +LIBEV_CFLAGS = @LIBEV_CFLAGS@ +LIBEV_LIBS = @LIBEV_LIBS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBSPDYLAY_CFLAGS = @LIBSPDYLAY_CFLAGS@ +LIBSPDYLAY_LIBS = @LIBSPDYLAY_LIBS@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +LT_AGE = @LT_AGE@ +LT_CURRENT = @LT_CURRENT@ +LT_REVISION = @LT_REVISION@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MKDIR_P = @MKDIR_P@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OPENSSL_CFLAGS = @OPENSSL_CFLAGS@ +OPENSSL_LIBS = @OPENSSL_LIBS@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PACKAGE_VERSION_NUM = @PACKAGE_VERSION_NUM@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PKG_CONFIG = @PKG_CONFIG@ +PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ +PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ +PYTHON = @PYTHON@ +PYTHON_CPPFLAGS = @PYTHON_CPPFLAGS@ +PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@ +PYTHON_EXTRA_LDFLAGS = @PYTHON_EXTRA_LDFLAGS@ +PYTHON_EXTRA_LIBS = @PYTHON_EXTRA_LIBS@ +PYTHON_LDFLAGS = @PYTHON_LDFLAGS@ +PYTHON_PLATFORM = @PYTHON_PLATFORM@ +PYTHON_PREFIX = @PYTHON_PREFIX@ +PYTHON_SITE_PKG = @PYTHON_SITE_PKG@ +PYTHON_VERSION = @PYTHON_VERSION@ +RANLIB = @RANLIB@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +TESTLDADD = @TESTLDADD@ +VERSION = @VERSION@ +WARNCFLAGS = @WARNCFLAGS@ +XML2_CONFIG = @XML2_CONFIG@ +XML_CPPFLAGS = @XML_CPPFLAGS@ +XML_LIBS = @XML_LIBS@ +ZLIB_CFLAGS = @ZLIB_CFLAGS@ +ZLIB_LIBS = @ZLIB_LIBS@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +pkgpyexecdir = @pkgpyexecdir@ +pkgpythondir = @pkgpythondir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +pyexecdir = @pyexecdir@ +pythondir = @pythondir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target = @target@ +target_alias = @target_alias@ +target_cpu = @target_cpu@ +target_os = @target_os@ +target_vendor = @target_vendor@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +@ENABLE_EXAMPLES_TRUE@AM_CFLAGS = $(WARNCFLAGS) +@ENABLE_EXAMPLES_TRUE@AM_CPPFLAGS = \ +@ENABLE_EXAMPLES_TRUE@ -Wall \ +@ENABLE_EXAMPLES_TRUE@ -I$(top_srcdir)/lib/includes \ +@ENABLE_EXAMPLES_TRUE@ -I$(top_builddir)/lib/includes \ +@ENABLE_EXAMPLES_TRUE@ -I$(top_srcdir)/src/includes \ +@ENABLE_EXAMPLES_TRUE@ -I$(top_srcdir)/third-party \ +@ENABLE_EXAMPLES_TRUE@ @LIBEVENT_OPENSSL_CFLAGS@ \ +@ENABLE_EXAMPLES_TRUE@ @OPENSSL_CFLAGS@ \ +@ENABLE_EXAMPLES_TRUE@ @DEFS@ + +@ENABLE_EXAMPLES_TRUE@LDADD = $(top_builddir)/lib/libnghttp2.la \ +@ENABLE_EXAMPLES_TRUE@ $(top_builddir)/third-party/libhttp-parser.la \ +@ENABLE_EXAMPLES_TRUE@ @LIBEVENT_OPENSSL_LIBS@ \ +@ENABLE_EXAMPLES_TRUE@ @OPENSSL_LIBS@ + +@ENABLE_EXAMPLES_TRUE@client_SOURCES = client.c +@ENABLE_EXAMPLES_TRUE@libevent_client_SOURCES = libevent-client.c +@ENABLE_EXAMPLES_TRUE@libevent_server_SOURCES = libevent-server.c +@ENABLE_EXAMPLES_TRUE@deflate_SOURCES = deflate.c +@ENABLE_EXAMPLES_TRUE@@ENABLE_TINY_NGHTTPD_TRUE@tiny_nghttpd_SOURCES = tiny-nghttpd.c + +# AM_CPPFLAGS must be placed first, so that header file (e.g., +# nghttp2/nghttp2.h) in this package is used rather than installed +# one. +@ENABLE_ASIO_LIB_TRUE@@ENABLE_EXAMPLES_TRUE@ASIOCPPFLAGS = ${AM_CPPFLAGS} ${BOOST_CPPFLAGS} +@ENABLE_ASIO_LIB_TRUE@@ENABLE_EXAMPLES_TRUE@ASIOLDADD = $(top_builddir)/lib/libnghttp2.la \ +@ENABLE_ASIO_LIB_TRUE@@ENABLE_EXAMPLES_TRUE@ $(top_builddir)/src/libnghttp2_asio.la @JEMALLOC_LIBS@ \ +@ENABLE_ASIO_LIB_TRUE@@ENABLE_EXAMPLES_TRUE@ $(top_builddir)/third-party/libhttp-parser.la \ +@ENABLE_ASIO_LIB_TRUE@@ENABLE_EXAMPLES_TRUE@ ${BOOST_LDFLAGS} \ +@ENABLE_ASIO_LIB_TRUE@@ENABLE_EXAMPLES_TRUE@ ${BOOST_ASIO_LIB} \ +@ENABLE_ASIO_LIB_TRUE@@ENABLE_EXAMPLES_TRUE@ ${BOOST_THREAD_LIB} \ +@ENABLE_ASIO_LIB_TRUE@@ENABLE_EXAMPLES_TRUE@ ${BOOST_SYSTEM_LIB} \ +@ENABLE_ASIO_LIB_TRUE@@ENABLE_EXAMPLES_TRUE@ @OPENSSL_LIBS@ \ +@ENABLE_ASIO_LIB_TRUE@@ENABLE_EXAMPLES_TRUE@ @APPLDFLAGS@ + +@ENABLE_ASIO_LIB_TRUE@@ENABLE_EXAMPLES_TRUE@asio_sv_SOURCES = asio-sv.cc +@ENABLE_ASIO_LIB_TRUE@@ENABLE_EXAMPLES_TRUE@asio_sv_CPPFLAGS = ${ASIOCPPFLAGS} +@ENABLE_ASIO_LIB_TRUE@@ENABLE_EXAMPLES_TRUE@asio_sv_LDADD = ${ASIOLDADD} +@ENABLE_ASIO_LIB_TRUE@@ENABLE_EXAMPLES_TRUE@asio_sv2_SOURCES = asio-sv2.cc +@ENABLE_ASIO_LIB_TRUE@@ENABLE_EXAMPLES_TRUE@asio_sv2_CPPFLAGS = ${ASIOCPPFLAGS} +@ENABLE_ASIO_LIB_TRUE@@ENABLE_EXAMPLES_TRUE@asio_sv2_LDADD = ${ASIOLDADD} +@ENABLE_ASIO_LIB_TRUE@@ENABLE_EXAMPLES_TRUE@asio_cl_SOURCES = asio-cl.cc +@ENABLE_ASIO_LIB_TRUE@@ENABLE_EXAMPLES_TRUE@asio_cl_CPPFLAGS = ${ASIOCPPFLAGS} +@ENABLE_ASIO_LIB_TRUE@@ENABLE_EXAMPLES_TRUE@asio_cl_LDADD = ${ASIOLDADD} +@ENABLE_ASIO_LIB_TRUE@@ENABLE_EXAMPLES_TRUE@asio_cl2_SOURCES = asio-cl2.cc +@ENABLE_ASIO_LIB_TRUE@@ENABLE_EXAMPLES_TRUE@asio_cl2_CPPFLAGS = ${ASIOCPPFLAGS} +@ENABLE_ASIO_LIB_TRUE@@ENABLE_EXAMPLES_TRUE@asio_cl2_LDADD = ${ASIOLDADD} +all: all-am + +.SUFFIXES: +.SUFFIXES: .c .cc .lo .o .obj +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu examples/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu examples/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): + +clean-noinstPROGRAMS: + @list='$(noinst_PROGRAMS)'; test -n "$$list" || exit 0; \ + echo " rm -f" $$list; \ + rm -f $$list || exit $$?; \ + test -n "$(EXEEXT)" || exit 0; \ + list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ + echo " rm -f" $$list; \ + rm -f $$list + +asio-cl$(EXEEXT): $(asio_cl_OBJECTS) $(asio_cl_DEPENDENCIES) $(EXTRA_asio_cl_DEPENDENCIES) + @rm -f asio-cl$(EXEEXT) + $(AM_V_CXXLD)$(CXXLINK) $(asio_cl_OBJECTS) $(asio_cl_LDADD) $(LIBS) + +asio-cl2$(EXEEXT): $(asio_cl2_OBJECTS) $(asio_cl2_DEPENDENCIES) $(EXTRA_asio_cl2_DEPENDENCIES) + @rm -f asio-cl2$(EXEEXT) + $(AM_V_CXXLD)$(CXXLINK) $(asio_cl2_OBJECTS) $(asio_cl2_LDADD) $(LIBS) + +asio-sv$(EXEEXT): $(asio_sv_OBJECTS) $(asio_sv_DEPENDENCIES) $(EXTRA_asio_sv_DEPENDENCIES) + @rm -f asio-sv$(EXEEXT) + $(AM_V_CXXLD)$(CXXLINK) $(asio_sv_OBJECTS) $(asio_sv_LDADD) $(LIBS) + +asio-sv2$(EXEEXT): $(asio_sv2_OBJECTS) $(asio_sv2_DEPENDENCIES) $(EXTRA_asio_sv2_DEPENDENCIES) + @rm -f asio-sv2$(EXEEXT) + $(AM_V_CXXLD)$(CXXLINK) $(asio_sv2_OBJECTS) $(asio_sv2_LDADD) $(LIBS) + +client$(EXEEXT): $(client_OBJECTS) $(client_DEPENDENCIES) $(EXTRA_client_DEPENDENCIES) + @rm -f client$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(client_OBJECTS) $(client_LDADD) $(LIBS) + +deflate$(EXEEXT): $(deflate_OBJECTS) $(deflate_DEPENDENCIES) $(EXTRA_deflate_DEPENDENCIES) + @rm -f deflate$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(deflate_OBJECTS) $(deflate_LDADD) $(LIBS) + +libevent-client$(EXEEXT): $(libevent_client_OBJECTS) $(libevent_client_DEPENDENCIES) $(EXTRA_libevent_client_DEPENDENCIES) + @rm -f libevent-client$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(libevent_client_OBJECTS) $(libevent_client_LDADD) $(LIBS) + +libevent-server$(EXEEXT): $(libevent_server_OBJECTS) $(libevent_server_DEPENDENCIES) $(EXTRA_libevent_server_DEPENDENCIES) + @rm -f libevent-server$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(libevent_server_OBJECTS) $(libevent_server_LDADD) $(LIBS) + +tiny-nghttpd$(EXEEXT): $(tiny_nghttpd_OBJECTS) $(tiny_nghttpd_DEPENDENCIES) $(EXTRA_tiny_nghttpd_DEPENDENCIES) + @rm -f tiny-nghttpd$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(tiny_nghttpd_OBJECTS) $(tiny_nghttpd_LDADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asio_cl-asio-cl.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asio_cl2-asio-cl2.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asio_sv-asio-sv.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asio_sv2-asio-sv2.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/client.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/deflate.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libevent-client.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libevent-server.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tiny-nghttpd.Po@am__quote@ + +.c.o: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\ +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< + +.c.obj: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\ +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.c.lo: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\ +@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< + +.cc.o: +@am__fastdepCXX_TRUE@ $(AM_V_CXX)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\ +@am__fastdepCXX_TRUE@ $(CXXCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCXX_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ $< + +.cc.obj: +@am__fastdepCXX_TRUE@ $(AM_V_CXX)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\ +@am__fastdepCXX_TRUE@ $(CXXCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\ +@am__fastdepCXX_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.cc.lo: +@am__fastdepCXX_TRUE@ $(AM_V_CXX)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\ +@am__fastdepCXX_TRUE@ $(LTCXXCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCXX_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LTCXXCOMPILE) -c -o $@ $< + +asio_cl-asio-cl.o: asio-cl.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(asio_cl_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT asio_cl-asio-cl.o -MD -MP -MF $(DEPDIR)/asio_cl-asio-cl.Tpo -c -o asio_cl-asio-cl.o `test -f 'asio-cl.cc' || echo '$(srcdir)/'`asio-cl.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/asio_cl-asio-cl.Tpo $(DEPDIR)/asio_cl-asio-cl.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='asio-cl.cc' object='asio_cl-asio-cl.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(asio_cl_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o asio_cl-asio-cl.o `test -f 'asio-cl.cc' || echo '$(srcdir)/'`asio-cl.cc + +asio_cl-asio-cl.obj: asio-cl.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(asio_cl_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT asio_cl-asio-cl.obj -MD -MP -MF $(DEPDIR)/asio_cl-asio-cl.Tpo -c -o asio_cl-asio-cl.obj `if test -f 'asio-cl.cc'; then $(CYGPATH_W) 'asio-cl.cc'; else $(CYGPATH_W) '$(srcdir)/asio-cl.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/asio_cl-asio-cl.Tpo $(DEPDIR)/asio_cl-asio-cl.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='asio-cl.cc' object='asio_cl-asio-cl.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(asio_cl_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o asio_cl-asio-cl.obj `if test -f 'asio-cl.cc'; then $(CYGPATH_W) 'asio-cl.cc'; else $(CYGPATH_W) '$(srcdir)/asio-cl.cc'; fi` + +asio_cl2-asio-cl2.o: asio-cl2.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(asio_cl2_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT asio_cl2-asio-cl2.o -MD -MP -MF $(DEPDIR)/asio_cl2-asio-cl2.Tpo -c -o asio_cl2-asio-cl2.o `test -f 'asio-cl2.cc' || echo '$(srcdir)/'`asio-cl2.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/asio_cl2-asio-cl2.Tpo $(DEPDIR)/asio_cl2-asio-cl2.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='asio-cl2.cc' object='asio_cl2-asio-cl2.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(asio_cl2_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o asio_cl2-asio-cl2.o `test -f 'asio-cl2.cc' || echo '$(srcdir)/'`asio-cl2.cc + +asio_cl2-asio-cl2.obj: asio-cl2.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(asio_cl2_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT asio_cl2-asio-cl2.obj -MD -MP -MF $(DEPDIR)/asio_cl2-asio-cl2.Tpo -c -o asio_cl2-asio-cl2.obj `if test -f 'asio-cl2.cc'; then $(CYGPATH_W) 'asio-cl2.cc'; else $(CYGPATH_W) '$(srcdir)/asio-cl2.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/asio_cl2-asio-cl2.Tpo $(DEPDIR)/asio_cl2-asio-cl2.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='asio-cl2.cc' object='asio_cl2-asio-cl2.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(asio_cl2_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o asio_cl2-asio-cl2.obj `if test -f 'asio-cl2.cc'; then $(CYGPATH_W) 'asio-cl2.cc'; else $(CYGPATH_W) '$(srcdir)/asio-cl2.cc'; fi` + +asio_sv-asio-sv.o: asio-sv.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(asio_sv_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT asio_sv-asio-sv.o -MD -MP -MF $(DEPDIR)/asio_sv-asio-sv.Tpo -c -o asio_sv-asio-sv.o `test -f 'asio-sv.cc' || echo '$(srcdir)/'`asio-sv.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/asio_sv-asio-sv.Tpo $(DEPDIR)/asio_sv-asio-sv.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='asio-sv.cc' object='asio_sv-asio-sv.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(asio_sv_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o asio_sv-asio-sv.o `test -f 'asio-sv.cc' || echo '$(srcdir)/'`asio-sv.cc + +asio_sv-asio-sv.obj: asio-sv.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(asio_sv_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT asio_sv-asio-sv.obj -MD -MP -MF $(DEPDIR)/asio_sv-asio-sv.Tpo -c -o asio_sv-asio-sv.obj `if test -f 'asio-sv.cc'; then $(CYGPATH_W) 'asio-sv.cc'; else $(CYGPATH_W) '$(srcdir)/asio-sv.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/asio_sv-asio-sv.Tpo $(DEPDIR)/asio_sv-asio-sv.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='asio-sv.cc' object='asio_sv-asio-sv.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(asio_sv_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o asio_sv-asio-sv.obj `if test -f 'asio-sv.cc'; then $(CYGPATH_W) 'asio-sv.cc'; else $(CYGPATH_W) '$(srcdir)/asio-sv.cc'; fi` + +asio_sv2-asio-sv2.o: asio-sv2.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(asio_sv2_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT asio_sv2-asio-sv2.o -MD -MP -MF $(DEPDIR)/asio_sv2-asio-sv2.Tpo -c -o asio_sv2-asio-sv2.o `test -f 'asio-sv2.cc' || echo '$(srcdir)/'`asio-sv2.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/asio_sv2-asio-sv2.Tpo $(DEPDIR)/asio_sv2-asio-sv2.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='asio-sv2.cc' object='asio_sv2-asio-sv2.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(asio_sv2_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o asio_sv2-asio-sv2.o `test -f 'asio-sv2.cc' || echo '$(srcdir)/'`asio-sv2.cc + +asio_sv2-asio-sv2.obj: asio-sv2.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(asio_sv2_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT asio_sv2-asio-sv2.obj -MD -MP -MF $(DEPDIR)/asio_sv2-asio-sv2.Tpo -c -o asio_sv2-asio-sv2.obj `if test -f 'asio-sv2.cc'; then $(CYGPATH_W) 'asio-sv2.cc'; else $(CYGPATH_W) '$(srcdir)/asio-sv2.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/asio_sv2-asio-sv2.Tpo $(DEPDIR)/asio_sv2-asio-sv2.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='asio-sv2.cc' object='asio_sv2-asio-sv2.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(asio_sv2_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o asio_sv2-asio-sv2.obj `if test -f 'asio-sv2.cc'; then $(CYGPATH_W) 'asio-sv2.cc'; else $(CYGPATH_W) '$(srcdir)/asio-sv2.cc'; fi` + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-am +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + $(am__define_uniq_tagged_files); \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: ctags-am + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" +cscopelist: cscopelist-am + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(PROGRAMS) +installdirs: +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic clean-libtool clean-noinstPROGRAMS \ + mostlyclean-am + +distclean: distclean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: + +.MAKE: install-am install-strip + +.PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ + clean-libtool clean-noinstPROGRAMS cscopelist-am ctags \ + ctags-am distclean distclean-compile distclean-generic \ + distclean-libtool distclean-tags distdir dvi dvi-am html \ + html-am info info-am install install-am install-data \ + install-data-am install-dvi install-dvi-am install-exec \ + install-exec-am install-html install-html-am install-info \ + install-info-am install-man install-pdf install-pdf-am \ + install-ps install-ps-am install-strip installcheck \ + installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-compile \ + mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ + tags tags-am uninstall uninstall-am + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/examples/asio-cl.cc b/examples/asio-cl.cc new file mode 100644 index 0000000..c7d7bb1 --- /dev/null +++ b/examples/asio-cl.cc @@ -0,0 +1,96 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2015 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include + +#include + +using boost::asio::ip::tcp; + +using namespace nghttp2::asio_http2; +using namespace nghttp2::asio_http2::client; + +int main(int argc, char *argv[]) { + try { + if (argc < 2) { + std::cerr << "Usage: asio-cl URI" << std::endl; + return 1; + } + boost::system::error_code ec; + boost::asio::io_service io_service; + + std::string uri = argv[1]; + std::string scheme, host, service; + + if (host_service_from_uri(ec, scheme, host, service, uri)) { + std::cerr << "error: bad URI: " << ec.message() << std::endl; + return 1; + } + + boost::asio::ssl::context tls_ctx(boost::asio::ssl::context::sslv23); + tls_ctx.set_default_verify_paths(); + // disabled to make development easier... + // tls_ctx.set_verify_mode(boost::asio::ssl::verify_peer); + configure_tls_context(ec, tls_ctx); + + auto sess = scheme == "https" ? session(io_service, tls_ctx, host, service) + : session(io_service, host, service); + + sess.on_connect([&sess, &uri](tcp::resolver::iterator endpoint_it) { + boost::system::error_code ec; + auto req = sess.submit(ec, "GET", uri); + + if (ec) { + std::cerr << "error: " << ec.message() << std::endl; + return; + } + + req->on_response([&sess](const response &res) { + std::cerr << "HTTP/2 " << res.status_code() << std::endl; + for (auto &kv : res.header()) { + std::cerr << kv.first << ": " << kv.second.value << "\n"; + } + std::cerr << std::endl; + + res.on_data([&sess](const uint8_t *data, std::size_t len) { + std::cerr.write(reinterpret_cast(data), len); + std::cerr << std::endl; + }); + }); + + req->on_close([&sess](uint32_t error_code) { sess.shutdown(); }); + }); + + sess.on_error([](const boost::system::error_code &ec) { + std::cerr << "error: " << ec.message() << std::endl; + }); + + io_service.run(); + } catch (std::exception &e) { + std::cerr << "exception: " << e.what() << "\n"; + } + + return 0; +} diff --git a/examples/asio-cl2.cc b/examples/asio-cl2.cc new file mode 100644 index 0000000..2a8ed71 --- /dev/null +++ b/examples/asio-cl2.cc @@ -0,0 +1,134 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2015 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include +#include + +#include + +using boost::asio::ip::tcp; + +using namespace nghttp2::asio_http2; +using namespace nghttp2::asio_http2::client; + +void print_header(const header_map &h) { + for (auto &kv : h) { + std::cerr << kv.first << ": " << kv.second.value << "\n"; + } + std::cerr << std::endl; +} + +void print_header(const response &res) { + std::cerr << "HTTP/2 " << res.status_code() << "\n"; + print_header(res.header()); +} + +void print_header(const request &req) { + auto &uri = req.uri(); + std::cerr << req.method() << " " << uri.scheme << "://" << uri.host + << uri.path; + if (!uri.raw_query.empty()) { + std::cerr << "?" << uri.raw_query; + } + std::cerr << " HTTP/2\n"; + print_header(req.header()); +} + +int main(int argc, char *argv[]) { + try { + if (argc < 2) { + std::cerr << "Usage: asio-cl URI" << std::endl; + return 1; + } + boost::system::error_code ec; + boost::asio::io_service io_service; + + std::string uri = argv[1]; + std::string scheme, host, service; + + if (host_service_from_uri(ec, scheme, host, service, uri)) { + std::cerr << "error: bad URI: " << ec.message() << std::endl; + return 1; + } + + boost::asio::ssl::context tls_ctx(boost::asio::ssl::context::sslv23); + tls_ctx.set_default_verify_paths(); + // disabled to make development easier... + // tls_ctx.set_verify_mode(boost::asio::ssl::verify_peer); + configure_tls_context(ec, tls_ctx); + + auto sess = scheme == "https" ? session(io_service, tls_ctx, host, service) + : session(io_service, host, service); + + sess.on_connect([&sess, &uri](tcp::resolver::iterator endpoint_it) { + std::cerr << "connected to " << (*endpoint_it).endpoint() << std::endl; + boost::system::error_code ec; + auto req = sess.submit(ec, "GET", uri, {{"cookie", {"foo=bar", true}}}); + if (ec) { + std::cerr << "error: " << ec.message() << std::endl; + return; + } + + req->on_response([&sess, req](const response &res) { + std::cerr << "response header was received" << std::endl; + print_header(res); + + res.on_data([&sess](const uint8_t *data, std::size_t len) { + std::cerr.write(reinterpret_cast(data), len); + std::cerr << std::endl; + }); + }); + + req->on_close([&sess](uint32_t error_code) { + std::cerr << "request done with error_code=" << error_code << std::endl; + }); + + req->on_push([](const request &push_req) { + std::cerr << "push request was received" << std::endl; + + print_header(push_req); + + push_req.on_response([](const response &res) { + std::cerr << "push response header was received" << std::endl; + + res.on_data([](const uint8_t *data, std::size_t len) { + std::cerr.write(reinterpret_cast(data), len); + std::cerr << std::endl; + }); + }); + }); + }); + + sess.on_error([](const boost::system::error_code &ec) { + std::cerr << "error: " << ec.message() << std::endl; + }); + + io_service.run(); + } catch (std::exception &e) { + std::cerr << "exception: " << e.what() << "\n"; + } + + return 0; +} diff --git a/examples/asio-sv.cc b/examples/asio-sv.cc new file mode 100644 index 0000000..47a1d84 --- /dev/null +++ b/examples/asio-sv.cc @@ -0,0 +1,149 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2014 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +// We wrote this code based on the original code which has the +// following license: +// +// main.cpp +// ~~~~~~~~ +// +// Copyright (c) 2003-2013 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#include +#include + +#include + +using namespace nghttp2::asio_http2; +using namespace nghttp2::asio_http2::server; + +int main(int argc, char *argv[]) { + try { + // Check command line arguments. + if (argc < 4) { + std::cerr + << "Usage: asio-sv
        [ " + << "]\n"; + return 1; + } + + boost::system::error_code ec; + + std::string addr = argv[1]; + std::string port = argv[2]; + std::size_t num_threads = std::stoi(argv[3]); + + http2 server; + + server.num_threads(num_threads); + + server.handle("/", [](const request &req, const response &res) { + res.write_head(200, {{"foo", {"bar"}}}); + res.end("hello, world\n"); + }); + server.handle("/secret/", [](const request &req, const response &res) { + res.write_head(200); + res.end("under construction!\n"); + }); + server.handle("/push", [](const request &req, const response &res) { + boost::system::error_code ec; + auto push = res.push(ec, "GET", "/push/1"); + if (!ec) { + push->write_head(200); + push->end("server push FTW!\n"); + } + + res.write_head(200); + res.end("you'll receive server push!\n"); + }); + server.handle("/delay", [](const request &req, const response &res) { + res.write_head(200); + + auto timer = std::make_shared( + res.io_service(), boost::posix_time::seconds(3)); + auto closed = std::make_shared(); + + res.on_close([timer, closed](uint32_t error_code) { + timer->cancel(); + *closed = true; + }); + + timer->async_wait([&res, closed](const boost::system::error_code &ec) { + if (ec || *closed) { + return; + } + + res.end("finally!\n"); + }); + }); + server.handle("/trailer", [](const request &req, const response &res) { + // send trailer part. + res.write_head(200, {{"trailers", {"digest"}}}); + + std::string body = "nghttp2 FTW!\n"; + auto left = std::make_shared(body.size()); + + res.end([&res, body, left](uint8_t *dst, std::size_t len, + uint32_t *data_flags) { + auto n = std::min(len, *left); + std::copy_n(body.c_str() + (body.size() - *left), n, dst); + *left -= n; + if (*left == 0) { + *data_flags |= + NGHTTP2_DATA_FLAG_EOF | NGHTTP2_DATA_FLAG_NO_END_STREAM; + // RFC 3230 Instance Digests in HTTP. The digest value is + // SHA-256 message digest of body. + res.write_trailer( + {{"digest", + {"SHA-256=qqXqskW7F3ueBSvmZRCiSwl2ym4HRO0M/pvQCBlSDis="}}}); + } + return n; + }); + }); + + if (argc >= 6) { + boost::asio::ssl::context tls(boost::asio::ssl::context::sslv23); + tls.use_private_key_file(argv[4], boost::asio::ssl::context::pem); + tls.use_certificate_chain_file(argv[5]); + + configure_tls_context_easy(ec, tls); + + if (server.listen_and_serve(ec, tls, addr, port)) { + std::cerr << "error: " << ec.message() << std::endl; + } + } else { + if (server.listen_and_serve(ec, addr, port)) { + std::cerr << "error: " << ec.message() << std::endl; + } + } + } catch (std::exception &e) { + std::cerr << "exception: " << e.what() << "\n"; + } + + return 0; +} diff --git a/examples/asio-sv2.cc b/examples/asio-sv2.cc new file mode 100644 index 0000000..8d2580e --- /dev/null +++ b/examples/asio-sv2.cc @@ -0,0 +1,125 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2014 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +// We wrote this code based on the original code which has the +// following license: +// +// main.cpp +// ~~~~~~~~ +// +// Copyright (c) 2003-2013 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif // HAVE_UNISTD_H +#ifdef HAVE_FCNTL_H +#include +#endif // HAVE_FCNTL_H +#include +#include + +#include + +using namespace nghttp2::asio_http2; +using namespace nghttp2::asio_http2::server; + +int main(int argc, char *argv[]) { + try { + // Check command line arguments. + if (argc < 5) { + std::cerr << "Usage: asio-sv2
        " + << "[ ]\n"; + return 1; + } + + boost::system::error_code ec; + + std::string addr = argv[1]; + std::string port = argv[2]; + std::size_t num_threads = std::stoi(argv[3]); + std::string docroot = argv[4]; + + http2 server; + + server.num_threads(num_threads); + + server.handle("/", [&docroot](const request &req, const response &res) { + auto path = percent_decode(req.uri().path); + if (!check_path(path)) { + res.write_head(404); + res.end(); + return; + } + + if (path == "/") { + path = "/index.html"; + } + + path = docroot + path; + auto fd = open(path.c_str(), O_RDONLY); + if (fd == -1) { + res.write_head(404); + res.end(); + return; + } + + auto header = header_map(); + + struct stat stbuf; + if (stat(path.c_str(), &stbuf) == 0) { + header.emplace("content-length", + header_value{std::to_string(stbuf.st_size)}); + header.emplace("last-modified", + header_value{http_date(stbuf.st_mtime)}); + } + res.write_head(200, std::move(header)); + res.end(file_generator_from_fd(fd)); + }); + + if (argc >= 7) { + boost::asio::ssl::context tls(boost::asio::ssl::context::sslv23); + tls.use_private_key_file(argv[5], boost::asio::ssl::context::pem); + tls.use_certificate_chain_file(argv[6]); + + configure_tls_context_easy(ec, tls); + + if (server.listen_and_serve(ec, tls, addr, port)) { + std::cerr << "error: " << ec.message() << std::endl; + } + } else { + if (server.listen_and_serve(ec, addr, port)) { + std::cerr << "error: " << ec.message() << std::endl; + } + } + } catch (std::exception &e) { + std::cerr << "exception: " << e.what() << "\n"; + } + + return 0; +} diff --git a/examples/client.c b/examples/client.c new file mode 100644 index 0000000..4b4deee --- /dev/null +++ b/examples/client.c @@ -0,0 +1,703 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2013 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +/* + * This program is written to show how to use nghttp2 API in C and + * intentionally made simple. + */ +#ifdef HAVE_CONFIG_H +#include +#endif /* HAVE_CONFIG_H */ + +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif /* HAVE_UNISTD_H */ +#ifdef HAVE_FCNTL_H +#include +#endif /* HAVE_FCNTL_H */ +#include +#ifdef HAVE_SYS_SOCKET_H +#include +#endif /* HAVE_SYS_SOCKET_H */ +#ifdef HAVE_NETDB_H +#include +#endif /* HAVE_NETDB_H */ +#ifdef HAVE_NETINET_IN_H +#include +#endif /* HAVE_NETINET_IN_H */ +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +enum { IO_NONE, WANT_READ, WANT_WRITE }; + +#define MAKE_NV(NAME, VALUE) \ + { \ + (uint8_t *) NAME, (uint8_t *)VALUE, sizeof(NAME) - 1, sizeof(VALUE) - 1, \ + NGHTTP2_NV_FLAG_NONE \ + } + +#define MAKE_NV_CS(NAME, VALUE) \ + { \ + (uint8_t *) NAME, (uint8_t *)VALUE, sizeof(NAME) - 1, strlen(VALUE), \ + NGHTTP2_NV_FLAG_NONE \ + } + +struct Connection { + SSL *ssl; + nghttp2_session *session; + /* WANT_READ if SSL/TLS connection needs more input; or WANT_WRITE + if it needs more output; or IO_NONE. This is necessary because + SSL/TLS re-negotiation is possible at any time. nghttp2 API + offers similar functions like nghttp2_session_want_read() and + nghttp2_session_want_write() but they do not take into account + SSL/TSL connection. */ + int want_io; +}; + +struct Request { + char *host; + /* In this program, path contains query component as well. */ + char *path; + /* This is the concatenation of host and port with ":" in + between. */ + char *hostport; + /* Stream ID for this request. */ + int32_t stream_id; + uint16_t port; +}; + +struct URI { + const char *host; + /* In this program, path contains query component as well. */ + const char *path; + size_t pathlen; + const char *hostport; + size_t hostlen; + size_t hostportlen; + uint16_t port; +}; + +/* + * Returns copy of string |s| with the length |len|. The returned + * string is NULL-terminated. + */ +static char *strcopy(const char *s, size_t len) { + char *dst; + dst = malloc(len + 1); + memcpy(dst, s, len); + dst[len] = '\0'; + return dst; +} + +/* + * Prints error message |msg| and exit. + */ +static void die(const char *msg) { + fprintf(stderr, "FATAL: %s\n", msg); + exit(EXIT_FAILURE); +} + +/* + * Prints error containing the function name |func| and message |msg| + * and exit. + */ +static void dief(const char *func, const char *msg) { + fprintf(stderr, "FATAL: %s: %s\n", func, msg); + exit(EXIT_FAILURE); +} + +/* + * Prints error containing the function name |func| and error code + * |error_code| and exit. + */ +static void diec(const char *func, int error_code) { + fprintf(stderr, "FATAL: %s: error_code=%d, msg=%s\n", func, error_code, + nghttp2_strerror(error_code)); + exit(EXIT_FAILURE); +} + +/* + * The implementation of nghttp2_send_callback type. Here we write + * |data| with size |length| to the network and return the number of + * bytes actually written. See the documentation of + * nghttp2_send_callback for the details. + */ +static ssize_t send_callback(nghttp2_session *session _U_, const uint8_t *data, + size_t length, int flags _U_, void *user_data) { + struct Connection *connection; + int rv; + connection = (struct Connection *)user_data; + connection->want_io = IO_NONE; + ERR_clear_error(); + rv = SSL_write(connection->ssl, data, (int)length); + if (rv <= 0) { + int err = SSL_get_error(connection->ssl, rv); + if (err == SSL_ERROR_WANT_WRITE || err == SSL_ERROR_WANT_READ) { + connection->want_io = + (err == SSL_ERROR_WANT_READ ? WANT_READ : WANT_WRITE); + rv = NGHTTP2_ERR_WOULDBLOCK; + } else { + rv = NGHTTP2_ERR_CALLBACK_FAILURE; + } + } + return rv; +} + +/* + * The implementation of nghttp2_recv_callback type. Here we read data + * from the network and write them in |buf|. The capacity of |buf| is + * |length| bytes. Returns the number of bytes stored in |buf|. See + * the documentation of nghttp2_recv_callback for the details. + */ +static ssize_t recv_callback(nghttp2_session *session _U_, uint8_t *buf, + size_t length, int flags _U_, void *user_data) { + struct Connection *connection; + int rv; + connection = (struct Connection *)user_data; + connection->want_io = IO_NONE; + ERR_clear_error(); + rv = SSL_read(connection->ssl, buf, (int)length); + if (rv < 0) { + int err = SSL_get_error(connection->ssl, rv); + if (err == SSL_ERROR_WANT_WRITE || err == SSL_ERROR_WANT_READ) { + connection->want_io = + (err == SSL_ERROR_WANT_READ ? WANT_READ : WANT_WRITE); + rv = NGHTTP2_ERR_WOULDBLOCK; + } else { + rv = NGHTTP2_ERR_CALLBACK_FAILURE; + } + } else if (rv == 0) { + rv = NGHTTP2_ERR_EOF; + } + return rv; +} + +static int on_frame_send_callback(nghttp2_session *session, + const nghttp2_frame *frame, + void *user_data _U_) { + size_t i; + switch (frame->hd.type) { + case NGHTTP2_HEADERS: + if (nghttp2_session_get_stream_user_data(session, frame->hd.stream_id)) { + const nghttp2_nv *nva = frame->headers.nva; + printf("[INFO] C ----------------------------> S (HEADERS)\n"); + for (i = 0; i < frame->headers.nvlen; ++i) { + fwrite(nva[i].name, nva[i].namelen, 1, stdout); + printf(": "); + fwrite(nva[i].value, nva[i].valuelen, 1, stdout); + printf("\n"); + } + } + break; + case NGHTTP2_RST_STREAM: + printf("[INFO] C ----------------------------> S (RST_STREAM)\n"); + break; + case NGHTTP2_GOAWAY: + printf("[INFO] C ----------------------------> S (GOAWAY)\n"); + break; + } + return 0; +} + +static int on_frame_recv_callback(nghttp2_session *session, + const nghttp2_frame *frame, + void *user_data _U_) { + size_t i; + switch (frame->hd.type) { + case NGHTTP2_HEADERS: + if (frame->headers.cat == NGHTTP2_HCAT_RESPONSE) { + const nghttp2_nv *nva = frame->headers.nva; + struct Request *req; + req = nghttp2_session_get_stream_user_data(session, frame->hd.stream_id); + if (req) { + printf("[INFO] C <---------------------------- S (HEADERS)\n"); + for (i = 0; i < frame->headers.nvlen; ++i) { + fwrite(nva[i].name, nva[i].namelen, 1, stdout); + printf(": "); + fwrite(nva[i].value, nva[i].valuelen, 1, stdout); + printf("\n"); + } + } + } + break; + case NGHTTP2_RST_STREAM: + printf("[INFO] C <---------------------------- S (RST_STREAM)\n"); + break; + case NGHTTP2_GOAWAY: + printf("[INFO] C <---------------------------- S (GOAWAY)\n"); + break; + } + return 0; +} + +/* + * The implementation of nghttp2_on_stream_close_callback type. We use + * this function to know the response is fully received. Since we just + * fetch 1 resource in this program, after reception of the response, + * we submit GOAWAY and close the session. + */ +static int on_stream_close_callback(nghttp2_session *session, int32_t stream_id, + uint32_t error_code _U_, + void *user_data _U_) { + struct Request *req; + req = nghttp2_session_get_stream_user_data(session, stream_id); + if (req) { + int rv; + rv = nghttp2_session_terminate_session(session, NGHTTP2_NO_ERROR); + + if (rv != 0) { + diec("nghttp2_session_terminate_session", rv); + } + } + return 0; +} + +#define MAX_OUTLEN 4096 + +/* + * The implementation of nghttp2_on_data_chunk_recv_callback type. We + * use this function to print the received response body. + */ +static int on_data_chunk_recv_callback(nghttp2_session *session, + uint8_t flags _U_, int32_t stream_id, + const uint8_t *data, size_t len, + void *user_data _U_) { + struct Request *req; + req = nghttp2_session_get_stream_user_data(session, stream_id); + if (req) { + printf("[INFO] C <---------------------------- S (DATA chunk)\n" + "%lu bytes\n", + (unsigned long int)len); + fwrite(data, 1, len, stdout); + printf("\n"); + } + return 0; +} + +/* + * Setup callback functions. nghttp2 API offers many callback + * functions, but most of them are optional. The send_callback is + * always required. Since we use nghttp2_session_recv(), the + * recv_callback is also required. + */ +static void setup_nghttp2_callbacks(nghttp2_session_callbacks *callbacks) { + nghttp2_session_callbacks_set_send_callback(callbacks, send_callback); + + nghttp2_session_callbacks_set_recv_callback(callbacks, recv_callback); + + nghttp2_session_callbacks_set_on_frame_send_callback(callbacks, + on_frame_send_callback); + + nghttp2_session_callbacks_set_on_frame_recv_callback(callbacks, + on_frame_recv_callback); + + nghttp2_session_callbacks_set_on_stream_close_callback( + callbacks, on_stream_close_callback); + + nghttp2_session_callbacks_set_on_data_chunk_recv_callback( + callbacks, on_data_chunk_recv_callback); +} + +/* + * Callback function for TLS NPN. Since this program only supports + * HTTP/2 protocol, if server does not offer HTTP/2 the nghttp2 + * library supports, we terminate program. + */ +static int select_next_proto_cb(SSL *ssl _U_, unsigned char **out, + unsigned char *outlen, const unsigned char *in, + unsigned int inlen, void *arg _U_) { + int rv; + /* nghttp2_select_next_protocol() selects HTTP/2 protocol the + nghttp2 library supports. */ + rv = nghttp2_select_next_protocol(out, outlen, in, inlen); + if (rv <= 0) { + die("Server did not advertise HTTP/2 protocol"); + } + return SSL_TLSEXT_ERR_OK; +} + +/* + * Setup SSL/TLS context. + */ +static void init_ssl_ctx(SSL_CTX *ssl_ctx) { + /* Disable SSLv2 and enable all workarounds for buggy servers */ + SSL_CTX_set_options(ssl_ctx, SSL_OP_ALL | SSL_OP_NO_SSLv2); + SSL_CTX_set_mode(ssl_ctx, SSL_MODE_AUTO_RETRY); + SSL_CTX_set_mode(ssl_ctx, SSL_MODE_RELEASE_BUFFERS); + /* Set NPN callback */ + SSL_CTX_set_next_proto_select_cb(ssl_ctx, select_next_proto_cb, NULL); +} + +static void ssl_handshake(SSL *ssl, int fd) { + int rv; + if (SSL_set_fd(ssl, fd) == 0) { + dief("SSL_set_fd", ERR_error_string(ERR_get_error(), NULL)); + } + ERR_clear_error(); + rv = SSL_connect(ssl); + if (rv <= 0) { + dief("SSL_connect", ERR_error_string(ERR_get_error(), NULL)); + } +} + +/* + * Connects to the host |host| and port |port|. This function returns + * the file descriptor of the client socket. + */ +static int connect_to(const char *host, uint16_t port) { + struct addrinfo hints; + int fd = -1; + int rv; + char service[NI_MAXSERV]; + struct addrinfo *res, *rp; + snprintf(service, sizeof(service), "%u", port); + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + rv = getaddrinfo(host, service, &hints, &res); + if (rv != 0) { + dief("getaddrinfo", gai_strerror(rv)); + } + for (rp = res; rp; rp = rp->ai_next) { + fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); + if (fd == -1) { + continue; + } + while ((rv = connect(fd, rp->ai_addr, rp->ai_addrlen)) == -1 && + errno == EINTR) + ; + if (rv == 0) { + break; + } + close(fd); + fd = -1; + } + freeaddrinfo(res); + return fd; +} + +static void make_non_block(int fd) { + int flags, rv; + while ((flags = fcntl(fd, F_GETFL, 0)) == -1 && errno == EINTR) + ; + if (flags == -1) { + dief("fcntl", strerror(errno)); + } + while ((rv = fcntl(fd, F_SETFL, flags | O_NONBLOCK)) == -1 && errno == EINTR) + ; + if (rv == -1) { + dief("fcntl", strerror(errno)); + } +} + +static void set_tcp_nodelay(int fd) { + int val = 1; + int rv; + rv = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &val, (socklen_t)sizeof(val)); + if (rv == -1) { + dief("setsockopt", strerror(errno)); + } +} + +/* + * Update |pollfd| based on the state of |connection|. + */ +static void ctl_poll(struct pollfd *pollfd, struct Connection *connection) { + pollfd->events = 0; + if (nghttp2_session_want_read(connection->session) || + connection->want_io == WANT_READ) { + pollfd->events |= POLLIN; + } + if (nghttp2_session_want_write(connection->session) || + connection->want_io == WANT_WRITE) { + pollfd->events |= POLLOUT; + } +} + +/* + * Submits the request |req| to the connection |connection|. This + * function does not send packets; just append the request to the + * internal queue in |connection->session|. + */ +static void submit_request(struct Connection *connection, struct Request *req) { + int32_t stream_id; + /* Make sure that the last item is NULL */ + const nghttp2_nv nva[] = {MAKE_NV(":method", "GET"), + MAKE_NV_CS(":path", req->path), + MAKE_NV(":scheme", "https"), + MAKE_NV_CS(":authority", req->hostport), + MAKE_NV("accept", "*/*"), + MAKE_NV("user-agent", "nghttp2/" NGHTTP2_VERSION)}; + + stream_id = nghttp2_submit_request(connection->session, NULL, nva, + sizeof(nva) / sizeof(nva[0]), NULL, req); + + if (stream_id < 0) { + diec("nghttp2_submit_request", stream_id); + } + + req->stream_id = stream_id; + printf("[INFO] Stream ID = %d\n", stream_id); +} + +/* + * Performs the network I/O. + */ +static void exec_io(struct Connection *connection) { + int rv; + rv = nghttp2_session_recv(connection->session); + if (rv != 0) { + diec("nghttp2_session_recv", rv); + } + rv = nghttp2_session_send(connection->session); + if (rv != 0) { + diec("nghttp2_session_send", rv); + } +} + +static void request_init(struct Request *req, const struct URI *uri) { + req->host = strcopy(uri->host, uri->hostlen); + req->port = uri->port; + req->path = strcopy(uri->path, uri->pathlen); + req->hostport = strcopy(uri->hostport, uri->hostportlen); + req->stream_id = -1; +} + +static void request_free(struct Request *req) { + free(req->host); + free(req->path); + free(req->hostport); +} + +/* + * Fetches the resource denoted by |uri|. + */ +static void fetch_uri(const struct URI *uri) { + nghttp2_session_callbacks *callbacks; + int fd; + SSL_CTX *ssl_ctx; + SSL *ssl; + struct Request req; + struct Connection connection; + int rv; + nfds_t npollfds = 1; + struct pollfd pollfds[1]; + + request_init(&req, uri); + + /* Establish connection and setup SSL */ + fd = connect_to(req.host, req.port); + if (fd == -1) { + die("Could not open file descriptor"); + } + ssl_ctx = SSL_CTX_new(SSLv23_client_method()); + if (ssl_ctx == NULL) { + dief("SSL_CTX_new", ERR_error_string(ERR_get_error(), NULL)); + } + init_ssl_ctx(ssl_ctx); + ssl = SSL_new(ssl_ctx); + if (ssl == NULL) { + dief("SSL_new", ERR_error_string(ERR_get_error(), NULL)); + } + /* To simplify the program, we perform SSL/TLS handshake in blocking + I/O. */ + ssl_handshake(ssl, fd); + + connection.ssl = ssl; + connection.want_io = IO_NONE; + + /* Here make file descriptor non-block */ + make_non_block(fd); + set_tcp_nodelay(fd); + + printf("[INFO] SSL/TLS handshake completed\n"); + + rv = nghttp2_session_callbacks_new(&callbacks); + + if (rv != 0) { + diec("nghttp2_session_callbacks_new", rv); + } + + setup_nghttp2_callbacks(callbacks); + + rv = nghttp2_session_client_new(&connection.session, callbacks, &connection); + + nghttp2_session_callbacks_del(callbacks); + + if (rv != 0) { + diec("nghttp2_session_client_new", rv); + } + + nghttp2_submit_settings(connection.session, NGHTTP2_FLAG_NONE, NULL, 0); + + /* Submit the HTTP request to the outbound queue. */ + submit_request(&connection, &req); + + pollfds[0].fd = fd; + ctl_poll(pollfds, &connection); + + /* Event loop */ + while (nghttp2_session_want_read(connection.session) || + nghttp2_session_want_write(connection.session)) { + int nfds = poll(pollfds, npollfds, -1); + if (nfds == -1) { + dief("poll", strerror(errno)); + } + if (pollfds[0].revents & (POLLIN | POLLOUT)) { + exec_io(&connection); + } + if ((pollfds[0].revents & POLLHUP) || (pollfds[0].revents & POLLERR)) { + die("Connection error"); + } + ctl_poll(pollfds, &connection); + } + + /* Resource cleanup */ + nghttp2_session_del(connection.session); + SSL_shutdown(ssl); + SSL_free(ssl); + SSL_CTX_free(ssl_ctx); + shutdown(fd, SHUT_WR); + close(fd); + request_free(&req); +} + +static int parse_uri(struct URI *res, const char *uri) { + /* We only interested in https */ + size_t len, i, offset; + int ipv6addr = 0; + memset(res, 0, sizeof(struct URI)); + len = strlen(uri); + if (len < 9 || memcmp("https://", uri, 8) != 0) { + return -1; + } + offset = 8; + res->host = res->hostport = &uri[offset]; + res->hostlen = 0; + if (uri[offset] == '[') { + /* IPv6 literal address */ + ++offset; + ++res->host; + ipv6addr = 1; + for (i = offset; i < len; ++i) { + if (uri[i] == ']') { + res->hostlen = i - offset; + offset = i + 1; + break; + } + } + } else { + const char delims[] = ":/?#"; + for (i = offset; i < len; ++i) { + if (strchr(delims, uri[i]) != NULL) { + break; + } + } + res->hostlen = i - offset; + offset = i; + } + if (res->hostlen == 0) { + return -1; + } + /* Assuming https */ + res->port = 443; + if (offset < len) { + if (uri[offset] == ':') { + /* port */ + const char delims[] = "/?#"; + int port = 0; + ++offset; + for (i = offset; i < len; ++i) { + if (strchr(delims, uri[i]) != NULL) { + break; + } + if ('0' <= uri[i] && uri[i] <= '9') { + port *= 10; + port += uri[i] - '0'; + if (port > 65535) { + return -1; + } + } else { + return -1; + } + } + if (port == 0) { + return -1; + } + offset = i; + res->port = port; + } + } + res->hostportlen = uri + offset + ipv6addr - res->host; + for (i = offset; i < len; ++i) { + if (uri[i] == '#') { + break; + } + } + if (i - offset == 0) { + res->path = "/"; + res->pathlen = 1; + } else { + res->path = &uri[offset]; + res->pathlen = i - offset; + } + return 0; +} + +int main(int argc, char **argv) { + struct URI uri; + struct sigaction act; + int rv; + + if (argc < 2) { + die("Specify a https URI"); + } + + memset(&act, 0, sizeof(struct sigaction)); + act.sa_handler = SIG_IGN; + sigaction(SIGPIPE, &act, 0); + + OPENSSL_config(NULL); + OpenSSL_add_all_algorithms(); + SSL_load_error_strings(); + SSL_library_init(); + + rv = parse_uri(&uri, argv[1]); + if (rv != 0) { + die("parse_uri failed"); + } + fetch_uri(&uri); + return EXIT_SUCCESS; +} diff --git a/examples/deflate.c b/examples/deflate.c new file mode 100644 index 0000000..d0e7d3f --- /dev/null +++ b/examples/deflate.c @@ -0,0 +1,206 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2014 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifdef HAVE_CONFIG_H +#include +#endif /* !HAVE_CONFIG_H */ + +#include +#include + +#include + +#define MAKE_NV(K, V) \ + { \ + (uint8_t *) K, (uint8_t *)V, sizeof(K) - 1, sizeof(V) - 1, \ + NGHTTP2_NV_FLAG_NONE \ + } + +static void deflate(nghttp2_hd_deflater *deflater, + nghttp2_hd_inflater *inflater, const nghttp2_nv *const nva, + size_t nvlen); + +static int inflate_header_block(nghttp2_hd_inflater *inflater, uint8_t *in, + size_t inlen, int final); + +int main(int argc _U_, char **argv _U_) { + int rv; + nghttp2_hd_deflater *deflater; + nghttp2_hd_inflater *inflater; + /* Define 1st header set. This is looks like a HTTP request. */ + nghttp2_nv nva1[] = { + MAKE_NV(":scheme", "https"), MAKE_NV(":authority", "example.org"), + MAKE_NV(":path", "/"), MAKE_NV("user-agent", "libnghttp2"), + MAKE_NV("accept-encoding", "gzip, deflate")}; + /* Define 2nd header set */ + nghttp2_nv nva2[] = {MAKE_NV(":scheme", "https"), + MAKE_NV(":authority", "example.org"), + MAKE_NV(":path", "/stylesheet/style.css"), + MAKE_NV("user-agent", "libnghttp2"), + MAKE_NV("accept-encoding", "gzip, deflate"), + MAKE_NV("referer", "https://example.org")}; + + rv = nghttp2_hd_deflate_new(&deflater, 4096); + + if (rv != 0) { + fprintf(stderr, "nghttp2_hd_deflate_init failed with error: %s\n", + nghttp2_strerror(rv)); + exit(EXIT_FAILURE); + } + + rv = nghttp2_hd_inflate_new(&inflater); + + if (rv != 0) { + fprintf(stderr, "nghttp2_hd_inflate_init failed with error: %s\n", + nghttp2_strerror(rv)); + exit(EXIT_FAILURE); + } + + /* Encode and decode 1st header set */ + deflate(deflater, inflater, nva1, sizeof(nva1) / sizeof(nva1[0])); + + /* Encode and decode 2nd header set, using differential encoding + using state after encoding 1st header set. */ + deflate(deflater, inflater, nva2, sizeof(nva2) / sizeof(nva2[0])); + + nghttp2_hd_inflate_del(inflater); + nghttp2_hd_deflate_del(deflater); + + return 0; +} + +static void deflate(nghttp2_hd_deflater *deflater, + nghttp2_hd_inflater *inflater, const nghttp2_nv *const nva, + size_t nvlen) { + ssize_t rv; + uint8_t *buf; + size_t buflen; + size_t outlen; + size_t i; + size_t sum; + + sum = 0; + + for (i = 0; i < nvlen; ++i) { + sum += nva[i].namelen + nva[i].valuelen; + } + + printf("Input (%zu byte(s)):\n\n", sum); + + for (i = 0; i < nvlen; ++i) { + fwrite(nva[i].name, nva[i].namelen, 1, stdout); + printf(": "); + fwrite(nva[i].value, nva[i].valuelen, 1, stdout); + printf("\n"); + } + + buflen = nghttp2_hd_deflate_bound(deflater, nva, nvlen); + buf = malloc(buflen); + + rv = nghttp2_hd_deflate_hd(deflater, buf, buflen, nva, nvlen); + + if (rv < 0) { + fprintf(stderr, "nghttp2_hd_deflate_hd() failed with error: %s\n", + nghttp2_strerror((int)rv)); + + free(buf); + + exit(EXIT_FAILURE); + } + + outlen = rv; + + printf("\nDeflate (%zu byte(s), ratio %.02f):\n\n", outlen, + sum == 0 ? 0 : (double)outlen / sum); + + for (i = 0; i < outlen; ++i) { + if ((i & 0x0fu) == 0) { + printf("%08zX: ", i); + } + + printf("%02X ", buf[i]); + + if (((i + 1) & 0x0fu) == 0) { + printf("\n"); + } + } + + printf("\n\nInflate:\n\n"); + + /* We pass 1 to final parameter, because buf contains whole deflated + header data. */ + rv = inflate_header_block(inflater, buf, outlen, 1); + + if (rv != 0) { + free(buf); + + exit(EXIT_FAILURE); + } + + printf("\n-----------------------------------------------------------" + "--------------------\n"); + + free(buf); +} + +int inflate_header_block(nghttp2_hd_inflater *inflater, uint8_t *in, + size_t inlen, int final) { + ssize_t rv; + + for (;;) { + nghttp2_nv nv; + int inflate_flags = 0; + size_t proclen; + + rv = nghttp2_hd_inflate_hd(inflater, &nv, &inflate_flags, in, inlen, final); + + if (rv < 0) { + fprintf(stderr, "inflate failed with error code %zd", rv); + return -1; + } + + proclen = rv; + + in += proclen; + inlen -= proclen; + + if (inflate_flags & NGHTTP2_HD_INFLATE_EMIT) { + fwrite(nv.name, nv.namelen, 1, stderr); + fprintf(stderr, ": "); + fwrite(nv.value, nv.valuelen, 1, stderr); + fprintf(stderr, "\n"); + } + + if (inflate_flags & NGHTTP2_HD_INFLATE_FINAL) { + nghttp2_hd_inflate_end_headers(inflater); + break; + } + + if ((inflate_flags & NGHTTP2_HD_INFLATE_EMIT) == 0 && inlen == 0) { + break; + } + } + + return 0; +} diff --git a/examples/libevent-client.c b/examples/libevent-client.c new file mode 100644 index 0000000..17ed94b --- /dev/null +++ b/examples/libevent-client.c @@ -0,0 +1,561 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2013 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifdef HAVE_CONFIG_H +#include +#endif /* HAVE_CONFIG_H */ + +#include +#ifdef HAVE_UNISTD_H +#include +#endif /* HAVE_UNISTD_H */ +#ifdef HAVE_SYS_SOCKET_H +#include +#endif /* HAVE_SYS_SOCKET_H */ +#ifdef HAVE_NETINET_IN_H +#include +#endif /* HAVE_NETINET_IN_H */ +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include "http-parser/http_parser.h" + +#define ARRLEN(x) (sizeof(x) / sizeof(x[0])) + +typedef struct { + /* The NULL-terminated URI string to retrieve. */ + const char *uri; + /* Parsed result of the |uri| */ + struct http_parser_url *u; + /* The authority portion of the |uri|, not NULL-terminated */ + char *authority; + /* The path portion of the |uri|, including query, not + NULL-terminated */ + char *path; + /* The length of the |authority| */ + size_t authoritylen; + /* The length of the |path| */ + size_t pathlen; + /* The stream ID of this stream */ + int32_t stream_id; +} http2_stream_data; + +typedef struct { + nghttp2_session *session; + struct evdns_base *dnsbase; + struct bufferevent *bev; + http2_stream_data *stream_data; +} http2_session_data; + +static http2_stream_data *create_http2_stream_data(const char *uri, + struct http_parser_url *u) { + /* MAX 5 digits (max 65535) + 1 ':' + 1 NULL (because of snprintf) */ + size_t extra = 7; + http2_stream_data *stream_data = malloc(sizeof(http2_stream_data)); + + stream_data->uri = uri; + stream_data->u = u; + stream_data->stream_id = -1; + + stream_data->authoritylen = u->field_data[UF_HOST].len; + stream_data->authority = malloc(stream_data->authoritylen + extra); + memcpy(stream_data->authority, &uri[u->field_data[UF_HOST].off], + u->field_data[UF_HOST].len); + if (u->field_set & (1 << UF_PORT)) { + stream_data->authoritylen += + snprintf(stream_data->authority + u->field_data[UF_HOST].len, extra, + ":%u", u->port); + } + + stream_data->pathlen = 0; + if (u->field_set & (1 << UF_PATH)) { + stream_data->pathlen = u->field_data[UF_PATH].len; + } + if (u->field_set & (1 << UF_QUERY)) { + /* +1 for '?' character */ + stream_data->pathlen += u->field_data[UF_QUERY].len + 1; + } + if (stream_data->pathlen > 0) { + stream_data->path = malloc(stream_data->pathlen); + if (u->field_set & (1 << UF_PATH)) { + memcpy(stream_data->path, &uri[u->field_data[UF_PATH].off], + u->field_data[UF_PATH].len); + } + if (u->field_set & (1 << UF_QUERY)) { + memcpy(stream_data->path + u->field_data[UF_PATH].len + 1, + &uri[u->field_data[UF_QUERY].off], u->field_data[UF_QUERY].len); + } + } else { + stream_data->path = NULL; + } + return stream_data; +} + +static void delete_http2_stream_data(http2_stream_data *stream_data) { + free(stream_data->path); + free(stream_data->authority); + free(stream_data); +} + +/* Initializes |session_data| */ +static http2_session_data * +create_http2_session_data(struct event_base *evbase) { + http2_session_data *session_data = malloc(sizeof(http2_session_data)); + + memset(session_data, 0, sizeof(http2_session_data)); + session_data->dnsbase = evdns_base_new(evbase, 1); + return session_data; +} + +static void delete_http2_session_data(http2_session_data *session_data) { + SSL *ssl = bufferevent_openssl_get_ssl(session_data->bev); + + if (ssl) { + SSL_shutdown(ssl); + } + bufferevent_free(session_data->bev); + session_data->bev = NULL; + evdns_base_free(session_data->dnsbase, 1); + session_data->dnsbase = NULL; + nghttp2_session_del(session_data->session); + session_data->session = NULL; + if (session_data->stream_data) { + delete_http2_stream_data(session_data->stream_data); + session_data->stream_data = NULL; + } + free(session_data); +} + +static void print_header(FILE *f, const uint8_t *name, size_t namelen, + const uint8_t *value, size_t valuelen) { + fwrite(name, namelen, 1, f); + fprintf(f, ": "); + fwrite(value, valuelen, 1, f); + fprintf(f, "\n"); +} + +/* Print HTTP headers to |f|. Please note that this function does not + take into account that header name and value are sequence of + octets, therefore they may contain non-printable characters. */ +static void print_headers(FILE *f, nghttp2_nv *nva, size_t nvlen) { + size_t i; + for (i = 0; i < nvlen; ++i) { + print_header(f, nva[i].name, nva[i].namelen, nva[i].value, nva[i].valuelen); + } + fprintf(f, "\n"); +} + +/* nghttp2_send_callback. Here we transmit the |data|, |length| bytes, + to the network. Because we are using libevent bufferevent, we just + write those bytes into bufferevent buffer. */ +static ssize_t send_callback(nghttp2_session *session _U_, const uint8_t *data, + size_t length, int flags _U_, void *user_data) { + http2_session_data *session_data = (http2_session_data *)user_data; + struct bufferevent *bev = session_data->bev; + bufferevent_write(bev, data, length); + return length; +} + +/* nghttp2_on_header_callback: Called when nghttp2 library emits + single header name/value pair. */ +static int on_header_callback(nghttp2_session *session _U_, + const nghttp2_frame *frame, const uint8_t *name, + size_t namelen, const uint8_t *value, + size_t valuelen, uint8_t flags _U_, + void *user_data) { + http2_session_data *session_data = (http2_session_data *)user_data; + switch (frame->hd.type) { + case NGHTTP2_HEADERS: + if (frame->headers.cat == NGHTTP2_HCAT_RESPONSE && + session_data->stream_data->stream_id == frame->hd.stream_id) { + /* Print response headers for the initiated request. */ + print_header(stderr, name, namelen, value, valuelen); + break; + } + } + return 0; +} + +/* nghttp2_on_begin_headers_callback: Called when nghttp2 library gets + started to receive header block. */ +static int on_begin_headers_callback(nghttp2_session *session _U_, + const nghttp2_frame *frame, + void *user_data) { + http2_session_data *session_data = (http2_session_data *)user_data; + switch (frame->hd.type) { + case NGHTTP2_HEADERS: + if (frame->headers.cat == NGHTTP2_HCAT_RESPONSE && + session_data->stream_data->stream_id == frame->hd.stream_id) { + fprintf(stderr, "Response headers for stream ID=%d:\n", + frame->hd.stream_id); + } + break; + } + return 0; +} + +/* nghttp2_on_frame_recv_callback: Called when nghttp2 library + received a complete frame from the remote peer. */ +static int on_frame_recv_callback(nghttp2_session *session _U_, + const nghttp2_frame *frame, void *user_data) { + http2_session_data *session_data = (http2_session_data *)user_data; + switch (frame->hd.type) { + case NGHTTP2_HEADERS: + if (frame->headers.cat == NGHTTP2_HCAT_RESPONSE && + session_data->stream_data->stream_id == frame->hd.stream_id) { + fprintf(stderr, "All headers received\n"); + } + break; + } + return 0; +} + +/* nghttp2_on_data_chunk_recv_callback: Called when DATA frame is + received from the remote peer. In this implementation, if the frame + is meant to the stream we initiated, print the received data in + stdout, so that the user can redirect its output to the file + easily. */ +static int on_data_chunk_recv_callback(nghttp2_session *session _U_, + uint8_t flags _U_, int32_t stream_id, + const uint8_t *data, size_t len, + void *user_data) { + http2_session_data *session_data = (http2_session_data *)user_data; + if (session_data->stream_data->stream_id == stream_id) { + fwrite(data, len, 1, stdout); + } + return 0; +} + +/* nghttp2_on_stream_close_callback: Called when a stream is about to + closed. This example program only deals with 1 HTTP request (1 + stream), if it is closed, we send GOAWAY and tear down the + session */ +static int on_stream_close_callback(nghttp2_session *session, int32_t stream_id, + uint32_t error_code, void *user_data) { + http2_session_data *session_data = (http2_session_data *)user_data; + int rv; + + if (session_data->stream_data->stream_id == stream_id) { + fprintf(stderr, "Stream %d closed with error_code=%d\n", stream_id, + error_code); + rv = nghttp2_session_terminate_session(session, NGHTTP2_NO_ERROR); + if (rv != 0) { + return NGHTTP2_ERR_CALLBACK_FAILURE; + } + } + return 0; +} + +/* NPN TLS extension client callback. We check that server advertised + the HTTP/2 protocol the nghttp2 library supports. If not, exit + the program. */ +static int select_next_proto_cb(SSL *ssl _U_, unsigned char **out, + unsigned char *outlen, const unsigned char *in, + unsigned int inlen, void *arg _U_) { + if (nghttp2_select_next_protocol(out, outlen, in, inlen) <= 0) { + errx(1, "Server did not advertise " NGHTTP2_PROTO_VERSION_ID); + } + return SSL_TLSEXT_ERR_OK; +} + +/* Create SSL_CTX. */ +static SSL_CTX *create_ssl_ctx(void) { + SSL_CTX *ssl_ctx; + ssl_ctx = SSL_CTX_new(SSLv23_client_method()); + if (!ssl_ctx) { + errx(1, "Could not create SSL/TLS context: %s", + ERR_error_string(ERR_get_error(), NULL)); + } + SSL_CTX_set_options(ssl_ctx, + SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | + SSL_OP_NO_COMPRESSION | + SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION); + SSL_CTX_set_next_proto_select_cb(ssl_ctx, select_next_proto_cb, NULL); + return ssl_ctx; +} + +/* Create SSL object */ +static SSL *create_ssl(SSL_CTX *ssl_ctx) { + SSL *ssl; + ssl = SSL_new(ssl_ctx); + if (!ssl) { + errx(1, "Could not create SSL/TLS session object: %s", + ERR_error_string(ERR_get_error(), NULL)); + } + return ssl; +} + +static void initialize_nghttp2_session(http2_session_data *session_data) { + nghttp2_session_callbacks *callbacks; + + nghttp2_session_callbacks_new(&callbacks); + + nghttp2_session_callbacks_set_send_callback(callbacks, send_callback); + + nghttp2_session_callbacks_set_on_frame_recv_callback(callbacks, + on_frame_recv_callback); + + nghttp2_session_callbacks_set_on_data_chunk_recv_callback( + callbacks, on_data_chunk_recv_callback); + + nghttp2_session_callbacks_set_on_stream_close_callback( + callbacks, on_stream_close_callback); + + nghttp2_session_callbacks_set_on_header_callback(callbacks, + on_header_callback); + + nghttp2_session_callbacks_set_on_begin_headers_callback( + callbacks, on_begin_headers_callback); + + nghttp2_session_client_new(&session_data->session, callbacks, session_data); + + nghttp2_session_callbacks_del(callbacks); +} + +static void send_client_connection_header(http2_session_data *session_data) { + nghttp2_settings_entry iv[1] = { + {NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100}}; + int rv; + + /* client 24 bytes magic string will be sent by nghttp2 library */ + rv = nghttp2_submit_settings(session_data->session, NGHTTP2_FLAG_NONE, iv, + ARRLEN(iv)); + if (rv != 0) { + errx(1, "Could not submit SETTINGS: %s", nghttp2_strerror(rv)); + } +} + +#define MAKE_NV(NAME, VALUE, VALUELEN) \ + { \ + (uint8_t *) NAME, (uint8_t *)VALUE, sizeof(NAME) - 1, VALUELEN, \ + NGHTTP2_NV_FLAG_NONE \ + } + +#define MAKE_NV2(NAME, VALUE) \ + { \ + (uint8_t *) NAME, (uint8_t *)VALUE, sizeof(NAME) - 1, sizeof(VALUE) - 1, \ + NGHTTP2_NV_FLAG_NONE \ + } + +/* Send HTTP request to the remote peer */ +static void submit_request(http2_session_data *session_data) { + int32_t stream_id; + http2_stream_data *stream_data = session_data->stream_data; + const char *uri = stream_data->uri; + const struct http_parser_url *u = stream_data->u; + nghttp2_nv hdrs[] = { + MAKE_NV2(":method", "GET"), + MAKE_NV(":scheme", &uri[u->field_data[UF_SCHEMA].off], + u->field_data[UF_SCHEMA].len), + MAKE_NV(":authority", stream_data->authority, stream_data->authoritylen), + MAKE_NV(":path", stream_data->path, stream_data->pathlen)}; + fprintf(stderr, "Request headers:\n"); + print_headers(stderr, hdrs, ARRLEN(hdrs)); + stream_id = nghttp2_submit_request(session_data->session, NULL, hdrs, + ARRLEN(hdrs), NULL, stream_data); + if (stream_id < 0) { + errx(1, "Could not submit HTTP request: %s", nghttp2_strerror(stream_id)); + } + + stream_data->stream_id = stream_id; +} + +/* Serialize the frame and send (or buffer) the data to + bufferevent. */ +static int session_send(http2_session_data *session_data) { + int rv; + + rv = nghttp2_session_send(session_data->session); + if (rv != 0) { + warnx("Fatal error: %s", nghttp2_strerror(rv)); + return -1; + } + return 0; +} + +/* readcb for bufferevent. Here we get the data from the input buffer + of bufferevent and feed them to nghttp2 library. This may invoke + nghttp2 callbacks. It may also queues the frame in nghttp2 session + context. To send them, we call session_send() in the end. */ +static void readcb(struct bufferevent *bev, void *ptr) { + http2_session_data *session_data = (http2_session_data *)ptr; + ssize_t readlen; + struct evbuffer *input = bufferevent_get_input(bev); + size_t datalen = evbuffer_get_length(input); + unsigned char *data = evbuffer_pullup(input, -1); + + readlen = nghttp2_session_mem_recv(session_data->session, data, datalen); + if (readlen < 0) { + warnx("Fatal error: %s", nghttp2_strerror((int)readlen)); + delete_http2_session_data(session_data); + return; + } + if (evbuffer_drain(input, readlen) != 0) { + warnx("Fatal error: evbuffer_drain failed"); + delete_http2_session_data(session_data); + return; + } + if (session_send(session_data) != 0) { + delete_http2_session_data(session_data); + return; + } +} + +/* writecb for bufferevent. To greaceful shutdown after sending or + receiving GOAWAY, we check the some conditions on the nghttp2 + library and output buffer of bufferevent. If it indicates we have + no business to this session, tear down the connection. */ +static void writecb(struct bufferevent *bev _U_, void *ptr) { + http2_session_data *session_data = (http2_session_data *)ptr; + if (nghttp2_session_want_read(session_data->session) == 0 && + nghttp2_session_want_write(session_data->session) == 0 && + evbuffer_get_length(bufferevent_get_output(session_data->bev)) == 0) { + delete_http2_session_data(session_data); + } +} + +/* eventcb for bufferevent. For the purpose of simplicity and + readability of the example program, we omitted the certificate and + peer verification. After SSL/TLS handshake is over, initialize + nghttp2 library session, and send client connection header. Then + send HTTP request. */ +static void eventcb(struct bufferevent *bev, short events, void *ptr) { + http2_session_data *session_data = (http2_session_data *)ptr; + if (events & BEV_EVENT_CONNECTED) { + int fd = bufferevent_getfd(bev); + int val = 1; + fprintf(stderr, "Connected\n"); + setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char *)&val, sizeof(val)); + initialize_nghttp2_session(session_data); + send_client_connection_header(session_data); + submit_request(session_data); + if (session_send(session_data) != 0) { + delete_http2_session_data(session_data); + } + return; + } + if (events & BEV_EVENT_EOF) { + warnx("Disconnected from the remote host"); + } else if (events & BEV_EVENT_ERROR) { + warnx("Network error"); + } else if (events & BEV_EVENT_TIMEOUT) { + warnx("Timeout"); + } + delete_http2_session_data(session_data); +} + +/* Start connecting to the remote peer |host:port| */ +static void initiate_connection(struct event_base *evbase, SSL_CTX *ssl_ctx, + const char *host, uint16_t port, + http2_session_data *session_data) { + int rv; + struct bufferevent *bev; + SSL *ssl; + + ssl = create_ssl(ssl_ctx); + bev = bufferevent_openssl_socket_new( + evbase, -1, ssl, BUFFEREVENT_SSL_CONNECTING, + BEV_OPT_DEFER_CALLBACKS | BEV_OPT_CLOSE_ON_FREE); + bufferevent_setcb(bev, readcb, writecb, eventcb, session_data); + rv = bufferevent_socket_connect_hostname(bev, session_data->dnsbase, + AF_UNSPEC, host, port); + + if (rv != 0) { + errx(1, "Could not connect to the remote host %s", host); + } + session_data->bev = bev; +} + +/* Get resource denoted by the |uri|. The debug and error messages are + printed in stderr, while the response body is printed in stdout. */ +static void run(const char *uri) { + struct http_parser_url u; + char *host; + uint16_t port; + int rv; + SSL_CTX *ssl_ctx; + struct event_base *evbase; + http2_session_data *session_data; + + /* Parse the |uri| and stores its components in |u| */ + rv = http_parser_parse_url(uri, strlen(uri), 0, &u); + if (rv != 0) { + errx(1, "Could not parse URI %s", uri); + } + host = strndup(&uri[u.field_data[UF_HOST].off], u.field_data[UF_HOST].len); + if (!(u.field_set & (1 << UF_PORT))) { + port = 443; + } else { + port = u.port; + } + + ssl_ctx = create_ssl_ctx(); + + evbase = event_base_new(); + + session_data = create_http2_session_data(evbase); + session_data->stream_data = create_http2_stream_data(uri, &u); + + initiate_connection(evbase, ssl_ctx, host, port, session_data); + free(host); + host = NULL; + + event_base_loop(evbase, 0); + + event_base_free(evbase); + SSL_CTX_free(ssl_ctx); +} + +int main(int argc, char **argv) { + struct sigaction act; + + if (argc < 2) { + fprintf(stderr, "Usage: libevent-client HTTPS_URI\n"); + exit(EXIT_FAILURE); + } + + memset(&act, 0, sizeof(struct sigaction)); + act.sa_handler = SIG_IGN; + sigaction(SIGPIPE, &act, NULL); + + OPENSSL_config(NULL); + OpenSSL_add_all_algorithms(); + SSL_load_error_strings(); + SSL_library_init(); + + run(argv[1]); + return 0; +} diff --git a/examples/libevent-server.c b/examples/libevent-server.c new file mode 100644 index 0000000..412c6d0 --- /dev/null +++ b/examples/libevent-server.c @@ -0,0 +1,734 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2013 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifdef HAVE_CONFIG_H +#include +#endif /* HAVE_CONFIG_H */ + +#include +#ifdef HAVE_SYS_SOCKET_H +#include +#endif /* HAVE_SYS_SOCKET_H */ +#ifdef HAVE_NETDB_H +#include +#endif /* HAVE_NETDB_H */ +#include +#ifdef HAVE_UNISTD_H +#include +#endif /* HAVE_UNISTD_H */ +#include +#ifdef HAVE_FCNTL_H +#include +#endif /* HAVE_FCNTL_H */ +#include +#ifdef HAVE_NETINET_IN_H +#include +#endif /* HAVE_NETINET_IN_H */ +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include + +#define OUTPUT_WOULDBLOCK_THRESHOLD (1 << 16) + +#define ARRLEN(x) (sizeof(x) / sizeof(x[0])) + +#define MAKE_NV(NAME, VALUE) \ + { \ + (uint8_t *) NAME, (uint8_t *)VALUE, sizeof(NAME) - 1, sizeof(VALUE) - 1, \ + NGHTTP2_NV_FLAG_NONE \ + } + +struct app_context; +typedef struct app_context app_context; + +typedef struct http2_stream_data { + struct http2_stream_data *prev, *next; + char *request_path; + int32_t stream_id; + int fd; +} http2_stream_data; + +typedef struct http2_session_data { + struct http2_stream_data root; + struct bufferevent *bev; + app_context *app_ctx; + nghttp2_session *session; + char *client_addr; +} http2_session_data; + +struct app_context { + SSL_CTX *ssl_ctx; + struct event_base *evbase; +}; + +static unsigned char next_proto_list[256]; +static size_t next_proto_list_len; + +static int next_proto_cb(SSL *s _U_, const unsigned char **data, + unsigned int *len, void *arg _U_) { + *data = next_proto_list; + *len = (unsigned int)next_proto_list_len; + return SSL_TLSEXT_ERR_OK; +} + +/* Create SSL_CTX. */ +static SSL_CTX *create_ssl_ctx(const char *key_file, const char *cert_file) { + SSL_CTX *ssl_ctx; + EC_KEY *ecdh; + + ssl_ctx = SSL_CTX_new(SSLv23_server_method()); + if (!ssl_ctx) { + errx(1, "Could not create SSL/TLS context: %s", + ERR_error_string(ERR_get_error(), NULL)); + } + SSL_CTX_set_options(ssl_ctx, + SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | + SSL_OP_NO_COMPRESSION | + SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION); + + ecdh = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1); + if (!ecdh) { + errx(1, "EC_KEY_new_by_curv_name failed: %s", + ERR_error_string(ERR_get_error(), NULL)); + } + SSL_CTX_set_tmp_ecdh(ssl_ctx, ecdh); + EC_KEY_free(ecdh); + + if (SSL_CTX_use_PrivateKey_file(ssl_ctx, key_file, SSL_FILETYPE_PEM) != 1) { + errx(1, "Could not read private key file %s", key_file); + } + if (SSL_CTX_use_certificate_chain_file(ssl_ctx, cert_file) != 1) { + errx(1, "Could not read certificate file %s", cert_file); + } + + next_proto_list[0] = NGHTTP2_PROTO_VERSION_ID_LEN; + memcpy(&next_proto_list[1], NGHTTP2_PROTO_VERSION_ID, + NGHTTP2_PROTO_VERSION_ID_LEN); + next_proto_list_len = 1 + NGHTTP2_PROTO_VERSION_ID_LEN; + + SSL_CTX_set_next_protos_advertised_cb(ssl_ctx, next_proto_cb, NULL); + return ssl_ctx; +} + +/* Create SSL object */ +static SSL *create_ssl(SSL_CTX *ssl_ctx) { + SSL *ssl; + ssl = SSL_new(ssl_ctx); + if (!ssl) { + errx(1, "Could not create SSL/TLS session object: %s", + ERR_error_string(ERR_get_error(), NULL)); + } + return ssl; +} + +static void add_stream(http2_session_data *session_data, + http2_stream_data *stream_data) { + stream_data->next = session_data->root.next; + session_data->root.next = stream_data; + stream_data->prev = &session_data->root; + if (stream_data->next) { + stream_data->next->prev = stream_data; + } +} + +static void remove_stream(http2_session_data *session_data _U_, + http2_stream_data *stream_data) { + stream_data->prev->next = stream_data->next; + if (stream_data->next) { + stream_data->next->prev = stream_data->prev; + } +} + +static http2_stream_data * +create_http2_stream_data(http2_session_data *session_data, int32_t stream_id) { + http2_stream_data *stream_data; + stream_data = malloc(sizeof(http2_stream_data)); + memset(stream_data, 0, sizeof(http2_stream_data)); + stream_data->stream_id = stream_id; + stream_data->fd = -1; + + add_stream(session_data, stream_data); + return stream_data; +} + +static void delete_http2_stream_data(http2_stream_data *stream_data) { + if (stream_data->fd != -1) { + close(stream_data->fd); + } + free(stream_data->request_path); + free(stream_data); +} + +static http2_session_data *create_http2_session_data(app_context *app_ctx, + int fd, + struct sockaddr *addr, + int addrlen) { + int rv; + http2_session_data *session_data; + SSL *ssl; + char host[NI_MAXHOST]; + int val = 1; + + ssl = create_ssl(app_ctx->ssl_ctx); + session_data = malloc(sizeof(http2_session_data)); + memset(session_data, 0, sizeof(http2_session_data)); + session_data->app_ctx = app_ctx; + setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char *)&val, sizeof(val)); + session_data->bev = bufferevent_openssl_socket_new( + app_ctx->evbase, fd, ssl, BUFFEREVENT_SSL_ACCEPTING, + BEV_OPT_CLOSE_ON_FREE | BEV_OPT_DEFER_CALLBACKS); + rv = getnameinfo(addr, addrlen, host, sizeof(host), NULL, 0, NI_NUMERICHOST); + if (rv != 0) { + session_data->client_addr = strdup("(unknown)"); + } else { + session_data->client_addr = strdup(host); + } + + return session_data; +} + +static void delete_http2_session_data(http2_session_data *session_data) { + http2_stream_data *stream_data; + SSL *ssl = bufferevent_openssl_get_ssl(session_data->bev); + fprintf(stderr, "%s disconnected\n", session_data->client_addr); + if (ssl) { + SSL_shutdown(ssl); + } + bufferevent_free(session_data->bev); + nghttp2_session_del(session_data->session); + for (stream_data = session_data->root.next; stream_data;) { + http2_stream_data *next = stream_data->next; + delete_http2_stream_data(stream_data); + stream_data = next; + } + free(session_data->client_addr); + free(session_data); +} + +/* Serialize the frame and send (or buffer) the data to + bufferevent. */ +static int session_send(http2_session_data *session_data) { + int rv; + rv = nghttp2_session_send(session_data->session); + if (rv != 0) { + warnx("Fatal error: %s", nghttp2_strerror(rv)); + return -1; + } + return 0; +} + +/* Read the data in the bufferevent and feed them into nghttp2 library + function. Invocation of nghttp2_session_mem_recv() may make + additional pending frames, so call session_send() at the end of the + function. */ +static int session_recv(http2_session_data *session_data) { + ssize_t readlen; + struct evbuffer *input = bufferevent_get_input(session_data->bev); + size_t datalen = evbuffer_get_length(input); + unsigned char *data = evbuffer_pullup(input, -1); + + readlen = nghttp2_session_mem_recv(session_data->session, data, datalen); + if (readlen < 0) { + warnx("Fatal error: %s", nghttp2_strerror((int)readlen)); + return -1; + } + if (evbuffer_drain(input, readlen) != 0) { + warnx("Fatal error: evbuffer_drain failed"); + return -1; + } + if (session_send(session_data) != 0) { + return -1; + } + return 0; +} + +static ssize_t send_callback(nghttp2_session *session _U_, const uint8_t *data, + size_t length, int flags _U_, void *user_data) { + http2_session_data *session_data = (http2_session_data *)user_data; + struct bufferevent *bev = session_data->bev; + /* Avoid excessive buffering in server side. */ + if (evbuffer_get_length(bufferevent_get_output(session_data->bev)) >= + OUTPUT_WOULDBLOCK_THRESHOLD) { + return NGHTTP2_ERR_WOULDBLOCK; + } + bufferevent_write(bev, data, length); + return length; +} + +/* Returns nonzero if the string |s| ends with the substring |sub| */ +static int ends_with(const char *s, const char *sub) { + size_t slen = strlen(s); + size_t sublen = strlen(sub); + if (slen < sublen) { + return 0; + } + return memcmp(s + slen - sublen, sub, sublen) == 0; +} + +/* Returns int value of hex string character |c| */ +static uint8_t hex_to_uint(uint8_t c) { + if ('0' <= c && c <= '9') { + return c - '0'; + } + if ('A' <= c && c <= 'F') { + return c - 'A' + 10; + } + if ('a' <= c && c <= 'f') { + return c - 'a' + 10; + } + return 0; +} + +/* Decodes percent-encoded byte string |value| with length |valuelen| + and returns the decoded byte string in allocated buffer. The return + value is NULL terminated. The caller must free the returned + string. */ +static char *percent_decode(const uint8_t *value, size_t valuelen) { + char *res; + + res = malloc(valuelen + 1); + if (valuelen > 3) { + size_t i, j; + for (i = 0, j = 0; i < valuelen - 2;) { + if (value[i] != '%' || !isxdigit(value[i + 1]) || + !isxdigit(value[i + 2])) { + res[j++] = value[i++]; + continue; + } + res[j++] = (hex_to_uint(value[i + 1]) << 4) + hex_to_uint(value[i + 2]); + i += 3; + } + memcpy(&res[j], &value[i], 2); + res[j + 2] = '\0'; + } else { + memcpy(res, value, valuelen); + res[valuelen] = '\0'; + } + return res; +} + +static ssize_t file_read_callback(nghttp2_session *session _U_, + int32_t stream_id _U_, uint8_t *buf, + size_t length, uint32_t *data_flags, + nghttp2_data_source *source, + void *user_data _U_) { + int fd = source->fd; + ssize_t r; + while ((r = read(fd, buf, length)) == -1 && errno == EINTR) + ; + if (r == -1) { + return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; + } + if (r == 0) { + *data_flags |= NGHTTP2_DATA_FLAG_EOF; + } + return r; +} + +static int send_response(nghttp2_session *session, int32_t stream_id, + nghttp2_nv *nva, size_t nvlen, int fd) { + int rv; + nghttp2_data_provider data_prd; + data_prd.source.fd = fd; + data_prd.read_callback = file_read_callback; + + rv = nghttp2_submit_response(session, stream_id, nva, nvlen, &data_prd); + if (rv != 0) { + warnx("Fatal error: %s", nghttp2_strerror(rv)); + return -1; + } + return 0; +} + +const char ERROR_HTML[] = "404" + "

        404 Not Found

        "; + +static int error_reply(nghttp2_session *session, + http2_stream_data *stream_data) { + int rv; + ssize_t writelen; + int pipefd[2]; + nghttp2_nv hdrs[] = {MAKE_NV(":status", "404")}; + + rv = pipe(pipefd); + if (rv != 0) { + warn("Could not create pipe"); + rv = nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, + stream_data->stream_id, + NGHTTP2_INTERNAL_ERROR); + if (rv != 0) { + warnx("Fatal error: %s", nghttp2_strerror(rv)); + return -1; + } + return 0; + } + + writelen = write(pipefd[1], ERROR_HTML, sizeof(ERROR_HTML) - 1); + close(pipefd[1]); + + if (writelen != sizeof(ERROR_HTML) - 1) { + close(pipefd[0]); + return -1; + } + + stream_data->fd = pipefd[0]; + + if (send_response(session, stream_data->stream_id, hdrs, ARRLEN(hdrs), + pipefd[0]) != 0) { + close(pipefd[0]); + return -1; + } + return 0; +} + +/* nghttp2_on_header_callback: Called when nghttp2 library emits + single header name/value pair. */ +static int on_header_callback(nghttp2_session *session, + const nghttp2_frame *frame, const uint8_t *name, + size_t namelen, const uint8_t *value, + size_t valuelen, uint8_t flags _U_, + void *user_data _U_) { + http2_stream_data *stream_data; + const char PATH[] = ":path"; + switch (frame->hd.type) { + case NGHTTP2_HEADERS: + if (frame->headers.cat != NGHTTP2_HCAT_REQUEST) { + break; + } + stream_data = + nghttp2_session_get_stream_user_data(session, frame->hd.stream_id); + if (!stream_data || stream_data->request_path) { + break; + } + if (namelen == sizeof(PATH) - 1 && memcmp(PATH, name, namelen) == 0) { + size_t j; + for (j = 0; j < valuelen && value[j] != '?'; ++j) + ; + stream_data->request_path = percent_decode(value, j); + } + break; + } + return 0; +} + +static int on_begin_headers_callback(nghttp2_session *session, + const nghttp2_frame *frame, + void *user_data) { + http2_session_data *session_data = (http2_session_data *)user_data; + http2_stream_data *stream_data; + + if (frame->hd.type != NGHTTP2_HEADERS || + frame->headers.cat != NGHTTP2_HCAT_REQUEST) { + return 0; + } + stream_data = create_http2_stream_data(session_data, frame->hd.stream_id); + nghttp2_session_set_stream_user_data(session, frame->hd.stream_id, + stream_data); + return 0; +} + +/* Minimum check for directory traversal. Returns nonzero if it is + safe. */ +static int check_path(const char *path) { + /* We don't like '\' in url. */ + return path[0] && path[0] == '/' && strchr(path, '\\') == NULL && + strstr(path, "/../") == NULL && strstr(path, "/./") == NULL && + !ends_with(path, "/..") && !ends_with(path, "/."); +} + +static int on_request_recv(nghttp2_session *session, + http2_session_data *session_data, + http2_stream_data *stream_data) { + int fd; + nghttp2_nv hdrs[] = {MAKE_NV(":status", "200")}; + char *rel_path; + + if (!stream_data->request_path) { + if (error_reply(session, stream_data) != 0) { + return NGHTTP2_ERR_CALLBACK_FAILURE; + } + return 0; + } + fprintf(stderr, "%s GET %s\n", session_data->client_addr, + stream_data->request_path); + if (!check_path(stream_data->request_path)) { + if (error_reply(session, stream_data) != 0) { + return NGHTTP2_ERR_CALLBACK_FAILURE; + } + return 0; + } + for (rel_path = stream_data->request_path; *rel_path == '/'; ++rel_path) + ; + fd = open(rel_path, O_RDONLY); + if (fd == -1) { + if (error_reply(session, stream_data) != 0) { + return NGHTTP2_ERR_CALLBACK_FAILURE; + } + return 0; + } + stream_data->fd = fd; + + if (send_response(session, stream_data->stream_id, hdrs, ARRLEN(hdrs), fd) != + 0) { + close(fd); + return NGHTTP2_ERR_CALLBACK_FAILURE; + } + return 0; +} + +static int on_frame_recv_callback(nghttp2_session *session, + const nghttp2_frame *frame, void *user_data) { + http2_session_data *session_data = (http2_session_data *)user_data; + http2_stream_data *stream_data; + switch (frame->hd.type) { + case NGHTTP2_DATA: + case NGHTTP2_HEADERS: + /* Check that the client request has finished */ + if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) { + stream_data = + nghttp2_session_get_stream_user_data(session, frame->hd.stream_id); + /* For DATA and HEADERS frame, this callback may be called after + on_stream_close_callback. Check that stream still alive. */ + if (!stream_data) { + return 0; + } + return on_request_recv(session, session_data, stream_data); + } + break; + default: + break; + } + return 0; +} + +static int on_stream_close_callback(nghttp2_session *session, int32_t stream_id, + uint32_t error_code _U_, void *user_data) { + http2_session_data *session_data = (http2_session_data *)user_data; + http2_stream_data *stream_data; + + stream_data = nghttp2_session_get_stream_user_data(session, stream_id); + if (!stream_data) { + return 0; + } + remove_stream(session_data, stream_data); + delete_http2_stream_data(stream_data); + return 0; +} + +static void initialize_nghttp2_session(http2_session_data *session_data) { + nghttp2_session_callbacks *callbacks; + + nghttp2_session_callbacks_new(&callbacks); + + nghttp2_session_callbacks_set_send_callback(callbacks, send_callback); + + nghttp2_session_callbacks_set_on_frame_recv_callback(callbacks, + on_frame_recv_callback); + + nghttp2_session_callbacks_set_on_stream_close_callback( + callbacks, on_stream_close_callback); + + nghttp2_session_callbacks_set_on_header_callback(callbacks, + on_header_callback); + + nghttp2_session_callbacks_set_on_begin_headers_callback( + callbacks, on_begin_headers_callback); + + nghttp2_session_server_new(&session_data->session, callbacks, session_data); + + nghttp2_session_callbacks_del(callbacks); +} + +/* Send HTTP/2 client connection header, which includes 24 bytes + magic octets and SETTINGS frame */ +static int send_server_connection_header(http2_session_data *session_data) { + nghttp2_settings_entry iv[1] = { + {NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100}}; + int rv; + + rv = nghttp2_submit_settings(session_data->session, NGHTTP2_FLAG_NONE, iv, + ARRLEN(iv)); + if (rv != 0) { + warnx("Fatal error: %s", nghttp2_strerror(rv)); + return -1; + } + return 0; +} + +/* readcb for bufferevent after client connection header was + checked. */ +static void readcb(struct bufferevent *bev _U_, void *ptr) { + http2_session_data *session_data = (http2_session_data *)ptr; + if (session_recv(session_data) != 0) { + delete_http2_session_data(session_data); + return; + } +} + +/* writecb for bufferevent. To greaceful shutdown after sending or + receiving GOAWAY, we check the some conditions on the nghttp2 + library and output buffer of bufferevent. If it indicates we have + no business to this session, tear down the connection. If the + connection is not going to shutdown, we call session_send() to + process pending data in the output buffer. This is necessary + because we have a threshold on the buffer size to avoid too much + buffering. See send_callback(). */ +static void writecb(struct bufferevent *bev, void *ptr) { + http2_session_data *session_data = (http2_session_data *)ptr; + if (evbuffer_get_length(bufferevent_get_output(bev)) > 0) { + return; + } + if (nghttp2_session_want_read(session_data->session) == 0 && + nghttp2_session_want_write(session_data->session) == 0) { + delete_http2_session_data(session_data); + return; + } + if (session_send(session_data) != 0) { + delete_http2_session_data(session_data); + return; + } +} + +/* eventcb for bufferevent */ +static void eventcb(struct bufferevent *bev _U_, short events, void *ptr) { + http2_session_data *session_data = (http2_session_data *)ptr; + if (events & BEV_EVENT_CONNECTED) { + fprintf(stderr, "%s connected\n", session_data->client_addr); + + initialize_nghttp2_session(session_data); + + if (send_server_connection_header(session_data) != 0) { + delete_http2_session_data(session_data); + return; + } + + return; + } + if (events & BEV_EVENT_EOF) { + fprintf(stderr, "%s EOF\n", session_data->client_addr); + } else if (events & BEV_EVENT_ERROR) { + fprintf(stderr, "%s network error\n", session_data->client_addr); + } else if (events & BEV_EVENT_TIMEOUT) { + fprintf(stderr, "%s timeout\n", session_data->client_addr); + } + delete_http2_session_data(session_data); +} + +/* callback for evconnlistener */ +static void acceptcb(struct evconnlistener *listener _U_, int fd, + struct sockaddr *addr, int addrlen, void *arg) { + app_context *app_ctx = (app_context *)arg; + http2_session_data *session_data; + + session_data = create_http2_session_data(app_ctx, fd, addr, addrlen); + + bufferevent_setcb(session_data->bev, readcb, writecb, eventcb, session_data); +} + +static void start_listen(struct event_base *evbase, const char *service, + app_context *app_ctx) { + int rv; + struct addrinfo hints; + struct addrinfo *res, *rp; + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_PASSIVE; +#ifdef AI_ADDRCONFIG + hints.ai_flags |= AI_ADDRCONFIG; +#endif /* AI_ADDRCONFIG */ + + rv = getaddrinfo(NULL, service, &hints, &res); + if (rv != 0) { + errx(1, NULL); + } + for (rp = res; rp; rp = rp->ai_next) { + struct evconnlistener *listener; + listener = evconnlistener_new_bind( + evbase, acceptcb, app_ctx, LEV_OPT_CLOSE_ON_FREE | LEV_OPT_REUSEABLE, + 16, rp->ai_addr, rp->ai_addrlen); + if (listener) { + freeaddrinfo(res); + + return; + } + } + errx(1, "Could not start listener"); +} + +static void initialize_app_context(app_context *app_ctx, SSL_CTX *ssl_ctx, + struct event_base *evbase) { + memset(app_ctx, 0, sizeof(app_context)); + app_ctx->ssl_ctx = ssl_ctx; + app_ctx->evbase = evbase; +} + +static void run(const char *service, const char *key_file, + const char *cert_file) { + SSL_CTX *ssl_ctx; + app_context app_ctx; + struct event_base *evbase; + + ssl_ctx = create_ssl_ctx(key_file, cert_file); + evbase = event_base_new(); + initialize_app_context(&app_ctx, ssl_ctx, evbase); + start_listen(evbase, service, &app_ctx); + + event_base_loop(evbase, 0); + + event_base_free(evbase); + SSL_CTX_free(ssl_ctx); +} + +int main(int argc, char **argv) { + struct sigaction act; + + if (argc < 4) { + fprintf(stderr, "Usage: libevent-server PORT KEY_FILE CERT_FILE\n"); + exit(EXIT_FAILURE); + } + + memset(&act, 0, sizeof(struct sigaction)); + act.sa_handler = SIG_IGN; + sigaction(SIGPIPE, &act, NULL); + + OPENSSL_config(NULL); + OpenSSL_add_all_algorithms(); + SSL_load_error_strings(); + SSL_library_init(); + + run(argv[1], argv[2], argv[3]); + return 0; +} diff --git a/examples/tiny-nghttpd.c b/examples/tiny-nghttpd.c new file mode 100644 index 0000000..3410e9d --- /dev/null +++ b/examples/tiny-nghttpd.c @@ -0,0 +1,1342 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2014 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +/* + * This program is intended to measure library performance, avoiding + * overhead of underlying I/O library (e.g., libevent, Boost ASIO). + */ +#define _GNU_SOURCE + +#ifdef HAVE_CONFIG_H +#include +#endif /* HAVE_CONFIG_H */ + +#include +#ifdef HAVE_SYS_SOCKET_H +#include +#endif /* HAVE_SYS_SOCKET_H */ +#include +#ifdef HAVE_FCNTL_H +#include +#endif /* HAVE_FCNTL_H */ +#ifdef HAVE_NETDB_H +#include +#endif /* HAVE_NETDB_H */ +#ifdef HAVE_NETINET_IN_H +#include +#endif /* HAVE_NETINET_IN_H */ +#include +#ifdef HAVE_UNISTD_H +#include +#endif /* HAVE_UNISTD_H */ +#include +#ifdef HAVE_TIME_H +#include +#endif /* HAVE_TIME_H */ +#include +#include +#include +#include +#include +#include +#include + +#include + +#define SERVER_NAME "tiny-nghttpd nghttp2/" NGHTTP2_VERSION + +#define MAKE_NV(name, value) \ + { \ + (uint8_t *)(name), (uint8_t *)(value), sizeof((name)) - 1, \ + sizeof((value)) - 1, NGHTTP2_NV_FLAG_NONE \ + } + +#define MAKE_NV2(name, value, valuelen) \ + { \ + (uint8_t *)(name), (uint8_t *)(value), sizeof((name)) - 1, (valuelen), \ + NGHTTP2_NV_FLAG_NONE \ + } + +#define array_size(a) (sizeof((a)) / sizeof((a)[0])) + +/* Returns the length of remaning data in buffer */ +#define io_buf_len(iobuf) ((iobuf)->last - (iobuf)->pos) +/* Returns the space buffer can still accept */ +#define io_buf_left(iobuf) ((iobuf)->end - (iobuf)->last) + +typedef struct { + /* beginning of buffer */ + uint8_t *begin; + /* one byte beyond the end of buffer */ + uint8_t *end; + /* next read/write position of buffer */ + uint8_t *pos; + /* one byte beyond last data of buffer */ + uint8_t *last; +} io_buf; + +typedef struct { + /* epoll fd */ + int epfd; +} io_loop; + +typedef struct stream { + struct stream *prev, *next; + /* mandatory header fields */ + char *method; + char *scheme; + char *authority; + char *path; + char *host; + /* region of response body in rawscrbuf */ + uint8_t *res_begin, *res_end; + /* io_buf wrapping rawscrbuf */ + io_buf scrbuf; + int64_t fileleft; + /* length of mandatory header fields */ + size_t methodlen; + size_t schemelen; + size_t authoritylen; + size_t pathlen; + size_t hostlen; + /* stream ID of this stream */ + int32_t stream_id; + /* fd for reading file */ + int filefd; + /* scratch buffer for this stream */ + uint8_t rawscrbuf[4096]; +} stream; + +typedef struct { int (*handler)(io_loop *, uint32_t, void *); } evhandle; + +typedef struct { + evhandle evhn; + nghttp2_session *session; + /* list of stream */ + stream strm_head; + /* pending library output */ + const uint8_t *cache; + /* io_buf wrapping rawoutbuf */ + io_buf buf; + /* length of cache */ + size_t cachelen; + /* client fd */ + int fd; + /* output buffer */ + uint8_t rawoutbuf[65536]; +} connection; + +typedef struct { + evhandle evhn; + /* listening fd */ + int fd; +} server; + +typedef struct { + evhandle evhn; + /* timerfd */ + int fd; +} timer; + +/* document root */ +const char *docroot; +/* length of docroot */ +size_t docrootlen; + +nghttp2_session_callbacks *shared_callbacks; + +static int handle_accept(io_loop *loop, uint32_t events, void *ptr); +static int handle_connection(io_loop *loop, uint32_t events, void *ptr); +static int handle_timer(io_loop *loop, uint32_t events, void *ptr); + +static void io_buf_init(io_buf *buf, uint8_t *underlying, size_t len) { + buf->begin = buf->pos = buf->last = underlying; + buf->end = underlying + len; +} + +static void io_buf_add(io_buf *buf, const void *src, size_t len) { + memcpy(buf->last, src, len); + buf->last += len; +} + +static char *io_buf_add_str(io_buf *buf, const void *src, size_t len) { + uint8_t *start = buf->last; + + memcpy(buf->last, src, len); + buf->last += len; + *buf->last++ = '\0'; + + return (char *)start; +} + +static int memeq(const void *a, const void *b, size_t n) { + return memcmp(a, b, n) == 0; +} + +#define streq(A, B, N) ((sizeof((A)) - 1) == (N) && memeq((A), (B), (N))) + +typedef enum { + NGHTTP2_TOKEN__AUTHORITY, + NGHTTP2_TOKEN__METHOD, + NGHTTP2_TOKEN__PATH, + NGHTTP2_TOKEN__SCHEME, + NGHTTP2_TOKEN_HOST +} nghttp2_token; + +/* Inspired by h2o header lookup. https://github.com/h2o/h2o */ +static int lookup_token(const uint8_t *name, size_t namelen) { + switch (namelen) { + case 5: + switch (name[namelen - 1]) { + case 'h': + if (streq(":pat", name, 4)) { + return NGHTTP2_TOKEN__PATH; + } + break; + } + break; + case 7: + switch (name[namelen - 1]) { + case 'd': + if (streq(":metho", name, 6)) { + return NGHTTP2_TOKEN__METHOD; + } + break; + case 'e': + if (streq(":schem", name, 6)) { + return NGHTTP2_TOKEN__SCHEME; + } + break; + } + break; + case 10: + switch (name[namelen - 1]) { + case 'y': + if (streq(":authorit", name, 9)) { + return NGHTTP2_TOKEN__AUTHORITY; + } + break; + } + break; + } + return -1; +} + +static char *cpydig(char *buf, int n, size_t len) { + char *p; + + p = buf + len - 1; + do { + *p-- = (n % 10) + '0'; + n /= 10; + } while (p >= buf); + + return buf + len; +} + +static const char *MONTH[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; +static const char *DAY_OF_WEEK[] = {"Sun", "Mon", "Tue", "Wed", + "Thu", "Fri", "Sat"}; + +static size_t http_date(char *buf, time_t t) { + struct tm tms; + char *p = buf; + + if (gmtime_r(&t, &tms) == NULL) { + return 0; + } + + /* Sat, 27 Sep 2014 06:31:15 GMT */ + + memcpy(p, DAY_OF_WEEK[tms.tm_wday], 3); + p += 3; + *p++ = ','; + *p++ = ' '; + p = cpydig(p, tms.tm_mday, 2); + *p++ = ' '; + memcpy(p, MONTH[tms.tm_mon], 3); + p += 3; + *p++ = ' '; + p = cpydig(p, tms.tm_year + 1900, 4); + *p++ = ' '; + p = cpydig(p, tms.tm_hour, 2); + *p++ = ':'; + p = cpydig(p, tms.tm_min, 2); + *p++ = ':'; + p = cpydig(p, tms.tm_sec, 2); + memcpy(p, " GMT", 4); + p += 4; + + return p - buf; +} + +static char date[29]; +static char datelen; + +static void update_date() { datelen = http_date(date, time(NULL)); } + +static size_t utos(char *buf, size_t len, uint64_t n) { + size_t nwrite = 0; + uint64_t t = n; + + if (len == 0) { + return 0; + } + + if (n == 0) { + buf[0] = '0'; + return 1; + } + + for (; t; t /= 10, ++nwrite) + ; + + if (nwrite > len) { + return 0; + } + + buf += nwrite - 1; + do { + *buf-- = (n % 10) + '0'; + n /= 10; + } while (n); + + return nwrite; +} + +static void print_errno(const char *prefix, int errnum) { + char buf[1024]; + char *errmsg; + + errmsg = strerror_r(errnum, buf, sizeof(buf)); + + fprintf(stderr, "%s: %s\n", prefix, errmsg); +} + +#define list_insert(head, elem) \ + do { \ + (elem)->prev = (head); \ + (elem)->next = (head)->next; \ + \ + if ((head)->next) { \ + (head)->next->prev = (elem); \ + } \ + (head)->next = (elem); \ + } while (0) + +#define list_remove(elem) \ + do { \ + (elem)->prev->next = (elem)->next; \ + if ((elem)->next) { \ + (elem)->next->prev = (elem)->prev; \ + } \ + } while (0) + +static stream *stream_new(int32_t stream_id, connection *conn) { + stream *strm; + + strm = malloc(sizeof(stream)); + + strm->prev = strm->next = NULL; + strm->method = NULL; + strm->scheme = NULL; + strm->authority = NULL; + strm->path = NULL; + strm->host = NULL; + strm->res_begin = NULL; + strm->res_end = NULL; + strm->methodlen = 0; + strm->schemelen = 0; + strm->authoritylen = 0; + strm->pathlen = 0; + strm->hostlen = 0; + strm->stream_id = stream_id; + strm->filefd = -1; + strm->fileleft = 0; + + list_insert(&conn->strm_head, strm); + + io_buf_init(&strm->scrbuf, strm->rawscrbuf, sizeof(strm->rawscrbuf)); + + return strm; +} + +static void stream_del(stream *strm) { + list_remove(strm); + + if (strm->filefd != -1) { + close(strm->filefd); + } + + free(strm); +} + +static connection *connection_new(int fd) { + connection *conn; + int rv; + + conn = malloc(sizeof(connection)); + + rv = nghttp2_session_server_new(&conn->session, shared_callbacks, conn); + + if (rv != 0) { + goto cleanup; + } + + conn->fd = fd; + conn->cache = NULL; + conn->cachelen = 0; + io_buf_init(&conn->buf, conn->rawoutbuf, sizeof(conn->rawoutbuf)); + conn->evhn.handler = handle_connection; + conn->strm_head.next = NULL; + + return conn; + +cleanup: + free(conn); + return NULL; +} + +static void connection_del(connection *conn) { + stream *strm; + + nghttp2_session_del(conn->session); + shutdown(conn->fd, SHUT_WR); + close(conn->fd); + + strm = conn->strm_head.next; + while (strm) { + stream *next_strm = strm->next; + + stream_del(strm); + strm = next_strm; + } + + free(conn); +} + +static int connection_start(connection *conn) { + nghttp2_settings_entry iv = {NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100}; + int rv; + + rv = nghttp2_submit_settings(conn->session, NGHTTP2_FLAG_NONE, &iv, 1); + + if (rv != 0) { + return -1; + } + + return 0; +} + +static int server_init(server *serv, const char *node, const char *service) { + int rv; + struct addrinfo hints; + struct addrinfo *res, *rp; + int fd; + int on = 1; + socklen_t optlen = sizeof(on); + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = 0; + hints.ai_flags = AI_PASSIVE | AI_ADDRCONFIG; + + rv = getaddrinfo(node, service, &hints, &res); + + if (rv != 0) { + fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv)); + return -1; + } + + for (rp = res; rp; rp = rp->ai_next) { + fd = + socket(rp->ai_family, rp->ai_socktype | SOCK_NONBLOCK, rp->ai_protocol); + + if (fd == -1) { + continue; + } + + rv = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, optlen); + + if (rv == -1) { + print_errno("setsockopt", errno); + } + + if (bind(fd, rp->ai_addr, rp->ai_addrlen) != 0) { + close(fd); + continue; + } + + if (listen(fd, 65536) != 0) { + close(fd); + continue; + } + + break; + } + + freeaddrinfo(res); + + if (!rp) { + fprintf(stderr, "No address to bind\n"); + return -1; + } + + serv->fd = fd; + serv->evhn.handler = handle_accept; + + return 0; +} + +static int timer_init(timer *tmr) { + int fd; + struct itimerspec timerval = {{1, 0}, {1, 0}}; + + fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK); + if (fd == -1) { + print_errno("timerfd_create", errno); + return -1; + } + + if (timerfd_settime(fd, 0, &timerval, NULL) != 0) { + print_errno("timerfd_settime", errno); + return -1; + } + + tmr->fd = fd; + tmr->evhn.handler = handle_timer; + + return 0; +} + +static int io_loop_init(io_loop *loop) { + int epfd; + + epfd = epoll_create1(0); + + if (epfd == -1) { + print_errno("epoll_create", errno); + return -1; + } + + loop->epfd = epfd; + + return 0; +} + +static int io_loop_ctl(io_loop *loop, int op, int fd, uint32_t events, + void *ptr) { + int rv; + struct epoll_event ev; + + ev.events = events; + ev.data.ptr = ptr; + + rv = epoll_ctl(loop->epfd, op, fd, &ev); + + if (rv != 0) { + print_errno("epoll_ctl", errno); + return -1; + } + + return 0; +} + +static int io_loop_add(io_loop *loop, int fd, uint32_t events, void *ptr) { + return io_loop_ctl(loop, EPOLL_CTL_ADD, fd, events, ptr); +} + +static int io_loop_mod(io_loop *loop, int fd, uint32_t events, void *ptr) { + return io_loop_ctl(loop, EPOLL_CTL_MOD, fd, events, ptr); +} + +static int io_loop_run(io_loop *loop, server *serv _U_) { +#define NUM_EVENTS 1024 + struct epoll_event events[NUM_EVENTS]; + + for (;;) { + int nev; + evhandle *evhn; + struct epoll_event *ev, *end; + + while ((nev = epoll_wait(loop->epfd, events, NUM_EVENTS, -1)) == -1 && + errno == EINTR) + ; + + if (nev == -1) { + print_errno("epoll_wait", errno); + return -1; + } + + for (ev = events, end = events + nev; ev != end; ++ev) { + evhn = ev->data.ptr; + evhn->handler(loop, ev->events, ev->data.ptr); + } + } +} + +static int handle_timer(io_loop *loop _U_, uint32_t events _U_, void *ptr) { + timer *tmr = ptr; + int64_t buf; + ssize_t nread; + + while ((nread = read(tmr->fd, &buf, sizeof(buf))) == -1 && errno == EINTR) + ; + + assert(nread == sizeof(buf)); + + update_date(); + + return 0; +} + +static int handle_accept(io_loop *loop, uint32_t events _U_, void *ptr) { + int acfd; + server *serv = ptr; + int on = 1; + socklen_t optlen = sizeof(on); + int rv; + + for (;;) { + connection *conn; + + while ((acfd = accept4(serv->fd, NULL, NULL, SOCK_NONBLOCK)) == -1 && + errno == EINTR) + ; + + if (acfd == -1) { + switch (errno) { + case ENETDOWN: + case EPROTO: + case ENOPROTOOPT: + case EHOSTDOWN: + case ENONET: + case EHOSTUNREACH: + case EOPNOTSUPP: + case ENETUNREACH: + continue; + } + return 0; + } + + rv = setsockopt(acfd, IPPROTO_TCP, TCP_NODELAY, &on, optlen); + + if (rv == -1) { + print_errno("setsockopt", errno); + } + + conn = connection_new(acfd); + + if (conn == NULL) { + close(acfd); + continue; + } + + if (connection_start(conn) != 0 || + io_loop_add(loop, acfd, EPOLLIN | EPOLLOUT, conn) != 0) { + connection_del(conn); + } + } +} + +static void stream_error(connection *conn, int32_t stream_id, + uint32_t error_code) { + nghttp2_submit_rst_stream(conn->session, NGHTTP2_FLAG_NONE, stream_id, + error_code); +} + +static int send_data_callback(nghttp2_session *session _U_, + nghttp2_frame *frame, const uint8_t *framehd, + size_t length, nghttp2_data_source *source, + void *user_data) { + connection *conn = user_data; + uint8_t *p = conn->buf.last; + stream *strm = source->ptr; + + /* We never use padding in this program */ + assert(frame->data.padlen == 0); + + if ((size_t)io_buf_left(&conn->buf) < 9 + frame->hd.length) { + return NGHTTP2_ERR_WOULDBLOCK; + } + + memcpy(p, framehd, 9); + p += 9; + + while (length) { + ssize_t nread; + while ((nread = read(strm->filefd, p, length)) == -1 && errno == EINTR) + ; + if (nread == -1) { + return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; + } + + length -= nread; + p += nread; + } + + conn->buf.last = p; + + return 0; +} + +static ssize_t fd_read_callback(nghttp2_session *session _U_, + int32_t stream_id _U_, uint8_t *buf _U_, + size_t length, uint32_t *data_flags, + nghttp2_data_source *source, + void *user_data _U_) { + stream *strm = source->ptr; + ssize_t nread = + (int64_t)length < strm->fileleft ? (int64_t)length : strm->fileleft; + + *data_flags |= NGHTTP2_DATA_FLAG_NO_COPY; + + strm->fileleft -= nread; + if (nread == 0 || strm->fileleft == 0) { + if (strm->fileleft != 0) { + return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; + } + *data_flags |= NGHTTP2_DATA_FLAG_EOF; + } + return nread; +} + +static ssize_t resbuf_read_callback(nghttp2_session *session _U_, + int32_t stream_id _U_, uint8_t *buf, + size_t length, uint32_t *data_flags, + nghttp2_data_source *source, + void *user_data _U_) { + stream *strm = source->ptr; + size_t left = strm->res_end - strm->res_begin; + size_t nwrite = length < left ? length : left; + + memcpy(buf, strm->res_begin, nwrite); + strm->res_begin += nwrite; + + if (strm->res_begin == strm->res_end) { + *data_flags |= NGHTTP2_DATA_FLAG_EOF; + } + + return nwrite; +} + +static int hex_digit(char c) { + return ('0' <= c && c <= '9') || ('A' <= c && c <= 'F') || + ('a' <= c && c <= 'f'); +} + +static unsigned int hex_to_uint(char c) { + if (c <= '9') { + return c - '0'; + } + + if (c <= 'F') { + return c - 'A' + 10; + } + + return c - 'a' + 10; +} + +static void percent_decode(io_buf *buf, const char *s) { + for (; *s; ++s) { + if (*s == '?' || *s == '#') { + break; + } + + if (*s == '%' && hex_digit(*(s + 1)) && hex_digit(*(s + 2))) { + *buf->last++ = (hex_to_uint(*(s + 1)) << 4) + hex_to_uint(*(s + 2)); + s += 2; + continue; + } + + *buf->last++ = *s; + } +} + +static int check_path(const char *path, size_t len) { + return path[0] == '/' && strchr(path, '\\') == NULL && + strstr(path, "/../") == NULL && strstr(path, "/./") == NULL && + (len < 3 || memcmp(path + len - 3, "/..", 3) != 0) && + (len < 2 || memcmp(path + len - 2, "/.", 2) != 0); +} + +static int make_path(io_buf *pathbuf, const char *req, size_t reqlen _U_) { + uint8_t *p; + + if (req[0] != '/') { + return -1; + } + + if (docrootlen + strlen(req) + sizeof("index.html") > + (size_t)io_buf_left(pathbuf)) { + return -1; + } + + io_buf_add(pathbuf, docroot, docrootlen); + + p = pathbuf->last; + + percent_decode(pathbuf, req); + + if (*(pathbuf->last - 1) == '/') { + io_buf_add(pathbuf, "index.html", sizeof("index.html") - 1); + } + + *pathbuf->last++ = '\0'; + + if (!check_path((const char *)p, pathbuf->last - 1 - p)) { + + return -1; + } + + return 0; +} + +static int status_response(stream *strm, connection *conn, + const char *status_code) { + int rv; + size_t status_codelen = strlen(status_code); + char contentlength[19]; + size_t contentlengthlen; + size_t reslen; + nghttp2_data_provider prd, *prdptr; + nghttp2_nv nva[5] = { + MAKE_NV(":status", ""), MAKE_NV("server", SERVER_NAME), + MAKE_NV2("date", date, datelen), MAKE_NV("content-length", ""), + }; + size_t nvlen = 3; + + nva[0].value = (uint8_t *)status_code; + nva[0].valuelen = strlen(status_code); + +#define BODY1 "" +#define BODY2 "

        " +#define BODY3 "

        " + + reslen = sizeof(BODY1) - 1 + sizeof(BODY2) - 1 + sizeof(BODY3) - 1 + + status_codelen * 2; + + if ((size_t)io_buf_left(&strm->scrbuf) < reslen) { + contentlength[0] = '0'; + contentlengthlen = 1; + prdptr = NULL; + } else { + contentlengthlen = utos(contentlength, sizeof(contentlength), reslen); + + strm->res_begin = strm->scrbuf.last; + + io_buf_add(&strm->scrbuf, BODY1, sizeof(BODY1) - 1); + io_buf_add(&strm->scrbuf, status_code, strlen(status_code)); + io_buf_add(&strm->scrbuf, BODY2, sizeof(BODY2) - 1); + io_buf_add(&strm->scrbuf, status_code, strlen(status_code)); + io_buf_add(&strm->scrbuf, BODY3, sizeof(BODY3) - 1); + + strm->res_end = strm->scrbuf.last; + prdptr = &prd; + } + + nva[nvlen].value = (uint8_t *)contentlength; + nva[nvlen].valuelen = contentlengthlen; + + ++nvlen; + + prd.source.ptr = strm; + prd.read_callback = resbuf_read_callback; + + rv = nghttp2_submit_response(conn->session, strm->stream_id, nva, nvlen, + prdptr); + if (rv != 0) { + return -1; + } + + return 0; +} + +static int redirect_response(stream *strm, connection *conn) { + int rv; + size_t locationlen; + nghttp2_nv nva[5] = { + MAKE_NV(":status", "301"), MAKE_NV("server", SERVER_NAME), + MAKE_NV2("date", date, datelen), MAKE_NV("content-length", "0"), + MAKE_NV("location", ""), + }; + + /* + 1 for trailing '/' */ + locationlen = strm->schemelen + 3 + strm->hostlen + strm->pathlen + 1; + if ((size_t)io_buf_left(&strm->scrbuf) < locationlen) { + return -1; + } + + nva[4].value = strm->scrbuf.last; + nva[4].valuelen = locationlen; + + io_buf_add(&strm->scrbuf, strm->scheme, strm->schemelen); + io_buf_add(&strm->scrbuf, "://", 3); + io_buf_add(&strm->scrbuf, strm->host, strm->hostlen); + io_buf_add(&strm->scrbuf, strm->path, strm->pathlen); + *strm->scrbuf.last++ = '/'; + + rv = nghttp2_submit_response(conn->session, strm->stream_id, nva, + array_size(nva), NULL); + + if (rv != 0) { + return -1; + } + + return 0; +} + +static int process_request(stream *strm, connection *conn) { + int fd; + struct stat stbuf; + int rv; + nghttp2_data_provider prd; + char lastmod[32]; + size_t lastmodlen; + char contentlength[19]; + size_t contentlengthlen; + char path[1024]; + io_buf pathbuf; + nghttp2_nv nva[5] = { + MAKE_NV(":status", "200"), MAKE_NV("server", SERVER_NAME), + MAKE_NV2("date", date, datelen), MAKE_NV("content-length", ""), + }; + size_t nvlen = 3; + + io_buf_init(&pathbuf, (uint8_t *)path, sizeof(path)); + + rv = make_path(&pathbuf, strm->path, strm->pathlen); + + if (rv != 0) { + return status_response(strm, conn, "400"); + } + + fd = open(path, O_RDONLY); + + if (fd == -1) { + return status_response(strm, conn, "404"); + } + + strm->filefd = fd; + + rv = fstat(fd, &stbuf); + + if (rv == -1) { + return status_response(strm, conn, "404"); + } + + if (stbuf.st_mode & S_IFDIR) { + return redirect_response(strm, conn); + } + + prd.source.ptr = strm; + prd.read_callback = fd_read_callback; + + strm->fileleft = stbuf.st_size; + + lastmodlen = http_date(lastmod, stbuf.st_mtim.tv_sec); + contentlengthlen = utos(contentlength, sizeof(contentlength), stbuf.st_size); + + nva[nvlen].value = (uint8_t *)contentlength; + nva[nvlen].valuelen = contentlengthlen; + + ++nvlen; + + if (lastmodlen) { + nva[nvlen].name = (uint8_t *)"last-modified"; + nva[nvlen].namelen = sizeof("last-modified") - 1; + nva[nvlen].value = (uint8_t *)lastmod; + nva[nvlen].valuelen = lastmodlen; + nva[nvlen].flags = NGHTTP2_NV_FLAG_NONE; + + ++nvlen; + } + + rv = + nghttp2_submit_response(conn->session, strm->stream_id, nva, nvlen, &prd); + if (rv != 0) { + return -1; + } + + return 0; +} + +static int on_begin_headers_callback(nghttp2_session *session, + const nghttp2_frame *frame, + void *user_data) { + connection *conn = user_data; + stream *strm; + + if (frame->hd.type != NGHTTP2_HEADERS || + frame->headers.cat != NGHTTP2_HCAT_REQUEST) { + return 0; + } + + strm = stream_new(frame->hd.stream_id, conn); + + if (!strm) { + nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, frame->hd.stream_id, + NGHTTP2_INTERNAL_ERROR); + return 0; + } + + nghttp2_session_set_stream_user_data(session, frame->hd.stream_id, strm); + + return 0; +} + +static int on_header_callback(nghttp2_session *session, + const nghttp2_frame *frame, const uint8_t *name, + size_t namelen, const uint8_t *value, + size_t valuelen, uint8_t flags _U_, + void *user_data _U_) { + stream *strm; + + if (frame->hd.type != NGHTTP2_HEADERS || + frame->headers.cat != NGHTTP2_HCAT_REQUEST) { + return 0; + } + + strm = nghttp2_session_get_stream_user_data(session, frame->hd.stream_id); + + if (!strm) { + return 0; + } + + switch (lookup_token(name, namelen)) { + case NGHTTP2_TOKEN__METHOD: + strm->method = io_buf_add_str(&strm->scrbuf, value, valuelen); + if (!strm->method) { + return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; + } + strm->methodlen = valuelen; + break; + case NGHTTP2_TOKEN__SCHEME: + strm->scheme = io_buf_add_str(&strm->scrbuf, value, valuelen); + if (!strm->scheme) { + return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; + } + strm->schemelen = valuelen; + break; + case NGHTTP2_TOKEN__AUTHORITY: + strm->authority = io_buf_add_str(&strm->scrbuf, value, valuelen); + if (!strm->authority) { + return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; + } + strm->authoritylen = valuelen; + break; + case NGHTTP2_TOKEN__PATH: + strm->path = io_buf_add_str(&strm->scrbuf, value, valuelen); + if (!strm->path) { + return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; + } + strm->pathlen = valuelen; + break; + case NGHTTP2_TOKEN_HOST: + strm->host = io_buf_add_str(&strm->scrbuf, value, valuelen); + if (!strm->host) { + return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; + } + strm->hostlen = valuelen; + break; + } + + return 0; +} + +static int on_frame_recv_callback(nghttp2_session *session, + const nghttp2_frame *frame, void *user_data) { + connection *conn = user_data; + stream *strm; + + if (frame->hd.type != NGHTTP2_HEADERS || + frame->headers.cat != NGHTTP2_HCAT_REQUEST) { + return 0; + } + + strm = nghttp2_session_get_stream_user_data(session, frame->hd.stream_id); + + if (!strm) { + return 0; + } + + if (!strm->host) { + strm->host = strm->authority; + strm->hostlen = strm->authoritylen; + } + + if (process_request(strm, conn) != 0) { + stream_error(conn, strm->stream_id, NGHTTP2_INTERNAL_ERROR); + return 0; + } + + return 0; +} + +static int on_stream_close_callback(nghttp2_session *session, int32_t stream_id, + uint32_t error_code _U_, + void *user_data _U_) { + stream *strm; + + strm = nghttp2_session_get_stream_user_data(session, stream_id); + + if (!strm) { + return 0; + } + + stream_del(strm); + + return 0; +} + +static int on_frame_not_send_callback(nghttp2_session *session _U_, + const nghttp2_frame *frame, + int lib_error_code _U_, void *user_data) { + connection *conn = user_data; + + if (frame->hd.type != NGHTTP2_HEADERS) { + return 0; + } + + /* Issue RST_STREAM so that stream does not hang around. */ + nghttp2_submit_rst_stream(conn->session, NGHTTP2_FLAG_NONE, + frame->hd.stream_id, NGHTTP2_INTERNAL_ERROR); + + return 0; +} + +static int do_read(connection *conn) { + uint8_t buf[32768]; + + for (;;) { + ssize_t nread; + ssize_t nproc; + + while ((nread = read(conn->fd, buf, sizeof(buf))) == -1 && errno == EINTR) + ; + if (nread == -1) { + if (errno == EAGAIN || errno == EWOULDBLOCK) { + return 0; + } + + return -1; + } + + if (nread == 0) { + return -1; + } + + nproc = nghttp2_session_mem_recv(conn->session, buf, nread); + + if (nproc < 0) { + return -1; + } + } +} + +static int do_write(connection *conn) { + for (;;) { + if (io_buf_len(&conn->buf)) { + ssize_t nwrite; + while ((nwrite = write(conn->fd, conn->buf.pos, + io_buf_len(&conn->buf))) == -1 && + errno == EINTR) + ; + if (nwrite == -1) { + if (errno == EAGAIN || errno == EWOULDBLOCK) { + return 0; + } + return -1; + } + + conn->buf.pos += nwrite; + + if (io_buf_len(&conn->buf)) { + return 0; + } + + io_buf_init(&conn->buf, conn->rawoutbuf, sizeof(conn->rawoutbuf)); + } + + if (conn->cache) { + io_buf_add(&conn->buf, conn->cache, conn->cachelen); + conn->cache = NULL; + conn->cachelen = 0; + } + + for (;;) { + ssize_t n; + const uint8_t *b; + + n = nghttp2_session_mem_send(conn->session, &b); + + if (n < 0) { + return -1; + } + + if (n == 0) { + if (io_buf_len(&conn->buf) == 0) { + return 0; + } + break; + } + + if (io_buf_left(&conn->buf) < n) { + conn->cache = b; + conn->cachelen = n; + break; + } + + io_buf_add(&conn->buf, b, n); + } + } +} + +static int handle_connection(io_loop *loop, uint32_t events, void *ptr) { + connection *conn = ptr; + int rv; + uint32_t nextev = 0; + + if (events & (EPOLLHUP | EPOLLERR)) { + goto cleanup; + } + + if (events & EPOLLIN) { + rv = do_read(conn); + + if (rv != 0) { + goto cleanup; + } + } + + rv = do_write(conn); + + if (rv != 0) { + goto cleanup; + } + + if (nghttp2_session_want_read(conn->session)) { + nextev |= EPOLLIN; + } + + if (io_buf_len(&conn->buf) || nghttp2_session_want_write(conn->session)) { + nextev |= EPOLLOUT; + } + + if (!nextev) { + goto cleanup; + } + + io_loop_mod(loop, conn->fd, nextev, conn); + + return 0; + +cleanup: + connection_del(conn); + + return 0; +} + +int main(int argc, char **argv) { + int rv; + server serv; + timer tmr; + io_loop loop; + struct sigaction act; + const char *address; + const char *service; + + if (argc < 4) { + fprintf(stderr, "Usage: tiny-nghttpd
        \n"); + exit(EXIT_FAILURE); + } + + address = argv[1]; + service = argv[2]; + docroot = argv[3]; + docrootlen = strlen(docroot); + + memset(&act, 0, sizeof(act)); + act.sa_handler = SIG_IGN; + sigaction(SIGPIPE, &act, NULL); + + rv = server_init(&serv, address, service); + + if (rv != 0) { + exit(EXIT_FAILURE); + } + + rv = timer_init(&tmr); + + if (rv != 0) { + exit(EXIT_FAILURE); + } + + rv = io_loop_init(&loop); + + if (rv != 0) { + exit(EXIT_FAILURE); + } + + rv = nghttp2_session_callbacks_new(&shared_callbacks); + if (rv != 0) { + fprintf(stderr, "nghttp2_session_callbacks_new: %s", nghttp2_strerror(rv)); + exit(EXIT_FAILURE); + } + + nghttp2_session_callbacks_set_on_begin_headers_callback( + shared_callbacks, on_begin_headers_callback); + nghttp2_session_callbacks_set_on_header_callback(shared_callbacks, + on_header_callback); + nghttp2_session_callbacks_set_on_frame_recv_callback(shared_callbacks, + on_frame_recv_callback); + nghttp2_session_callbacks_set_on_stream_close_callback( + shared_callbacks, on_stream_close_callback); + nghttp2_session_callbacks_set_on_frame_not_send_callback( + shared_callbacks, on_frame_not_send_callback); + nghttp2_session_callbacks_set_send_data_callback(shared_callbacks, + send_data_callback); + + rv = io_loop_add(&loop, serv.fd, EPOLLIN, &serv); + + if (rv != 0) { + exit(EXIT_FAILURE); + } + + rv = io_loop_add(&loop, tmr.fd, EPOLLIN, &tmr); + + if (rv != 0) { + exit(EXIT_FAILURE); + } + + update_date(); + + io_loop_run(&loop, &serv); + + nghttp2_session_callbacks_del(shared_callbacks); + + return 0; +} diff --git a/install-sh b/install-sh new file mode 100755 index 0000000..377bb86 --- /dev/null +++ b/install-sh @@ -0,0 +1,527 @@ +#!/bin/sh +# install - install a program, script, or datafile + +scriptversion=2011-11-20.07; # UTC + +# This originates from X11R5 (mit/util/scripts/install.sh), which was +# later released in X11R6 (xc/config/util/install.sh) with the +# following copyright and license. +# +# Copyright (C) 1994 X Consortium +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +# AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC- +# TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# +# Except as contained in this notice, the name of the X Consortium shall not +# be used in advertising or otherwise to promote the sale, use or other deal- +# ings in this Software without prior written authorization from the X Consor- +# tium. +# +# +# FSF changes to this file are in the public domain. +# +# Calling this script install-sh is preferred over install.sh, to prevent +# 'make' implicit rules from creating a file called install from it +# when there is no Makefile. +# +# This script is compatible with the BSD install script, but was written +# from scratch. + +nl=' +' +IFS=" "" $nl" + +# set DOITPROG to echo to test this script + +# Don't use :- since 4.3BSD and earlier shells don't like it. +doit=${DOITPROG-} +if test -z "$doit"; then + doit_exec=exec +else + doit_exec=$doit +fi + +# Put in absolute file names if you don't have them in your path; +# or use environment vars. + +chgrpprog=${CHGRPPROG-chgrp} +chmodprog=${CHMODPROG-chmod} +chownprog=${CHOWNPROG-chown} +cmpprog=${CMPPROG-cmp} +cpprog=${CPPROG-cp} +mkdirprog=${MKDIRPROG-mkdir} +mvprog=${MVPROG-mv} +rmprog=${RMPROG-rm} +stripprog=${STRIPPROG-strip} + +posix_glob='?' +initialize_posix_glob=' + test "$posix_glob" != "?" || { + if (set -f) 2>/dev/null; then + posix_glob= + else + posix_glob=: + fi + } +' + +posix_mkdir= + +# Desired mode of installed file. +mode=0755 + +chgrpcmd= +chmodcmd=$chmodprog +chowncmd= +mvcmd=$mvprog +rmcmd="$rmprog -f" +stripcmd= + +src= +dst= +dir_arg= +dst_arg= + +copy_on_change=false +no_target_directory= + +usage="\ +Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE + or: $0 [OPTION]... SRCFILES... DIRECTORY + or: $0 [OPTION]... -t DIRECTORY SRCFILES... + or: $0 [OPTION]... -d DIRECTORIES... + +In the 1st form, copy SRCFILE to DSTFILE. +In the 2nd and 3rd, copy all SRCFILES to DIRECTORY. +In the 4th, create DIRECTORIES. + +Options: + --help display this help and exit. + --version display version info and exit. + + -c (ignored) + -C install only if different (preserve the last data modification time) + -d create directories instead of installing files. + -g GROUP $chgrpprog installed files to GROUP. + -m MODE $chmodprog installed files to MODE. + -o USER $chownprog installed files to USER. + -s $stripprog installed files. + -t DIRECTORY install into DIRECTORY. + -T report an error if DSTFILE is a directory. + +Environment variables override the default commands: + CHGRPPROG CHMODPROG CHOWNPROG CMPPROG CPPROG MKDIRPROG MVPROG + RMPROG STRIPPROG +" + +while test $# -ne 0; do + case $1 in + -c) ;; + + -C) copy_on_change=true;; + + -d) dir_arg=true;; + + -g) chgrpcmd="$chgrpprog $2" + shift;; + + --help) echo "$usage"; exit $?;; + + -m) mode=$2 + case $mode in + *' '* | *' '* | *' +'* | *'*'* | *'?'* | *'['*) + echo "$0: invalid mode: $mode" >&2 + exit 1;; + esac + shift;; + + -o) chowncmd="$chownprog $2" + shift;; + + -s) stripcmd=$stripprog;; + + -t) dst_arg=$2 + # Protect names problematic for 'test' and other utilities. + case $dst_arg in + -* | [=\(\)!]) dst_arg=./$dst_arg;; + esac + shift;; + + -T) no_target_directory=true;; + + --version) echo "$0 $scriptversion"; exit $?;; + + --) shift + break;; + + -*) echo "$0: invalid option: $1" >&2 + exit 1;; + + *) break;; + esac + shift +done + +if test $# -ne 0 && test -z "$dir_arg$dst_arg"; then + # When -d is used, all remaining arguments are directories to create. + # When -t is used, the destination is already specified. + # Otherwise, the last argument is the destination. Remove it from $@. + for arg + do + if test -n "$dst_arg"; then + # $@ is not empty: it contains at least $arg. + set fnord "$@" "$dst_arg" + shift # fnord + fi + shift # arg + dst_arg=$arg + # Protect names problematic for 'test' and other utilities. + case $dst_arg in + -* | [=\(\)!]) dst_arg=./$dst_arg;; + esac + done +fi + +if test $# -eq 0; then + if test -z "$dir_arg"; then + echo "$0: no input file specified." >&2 + exit 1 + fi + # It's OK to call 'install-sh -d' without argument. + # This can happen when creating conditional directories. + exit 0 +fi + +if test -z "$dir_arg"; then + do_exit='(exit $ret); exit $ret' + trap "ret=129; $do_exit" 1 + trap "ret=130; $do_exit" 2 + trap "ret=141; $do_exit" 13 + trap "ret=143; $do_exit" 15 + + # Set umask so as not to create temps with too-generous modes. + # However, 'strip' requires both read and write access to temps. + case $mode in + # Optimize common cases. + *644) cp_umask=133;; + *755) cp_umask=22;; + + *[0-7]) + if test -z "$stripcmd"; then + u_plus_rw= + else + u_plus_rw='% 200' + fi + cp_umask=`expr '(' 777 - $mode % 1000 ')' $u_plus_rw`;; + *) + if test -z "$stripcmd"; then + u_plus_rw= + else + u_plus_rw=,u+rw + fi + cp_umask=$mode$u_plus_rw;; + esac +fi + +for src +do + # Protect names problematic for 'test' and other utilities. + case $src in + -* | [=\(\)!]) src=./$src;; + esac + + if test -n "$dir_arg"; then + dst=$src + dstdir=$dst + test -d "$dstdir" + dstdir_status=$? + else + + # Waiting for this to be detected by the "$cpprog $src $dsttmp" command + # might cause directories to be created, which would be especially bad + # if $src (and thus $dsttmp) contains '*'. + if test ! -f "$src" && test ! -d "$src"; then + echo "$0: $src does not exist." >&2 + exit 1 + fi + + if test -z "$dst_arg"; then + echo "$0: no destination specified." >&2 + exit 1 + fi + dst=$dst_arg + + # If destination is a directory, append the input filename; won't work + # if double slashes aren't ignored. + if test -d "$dst"; then + if test -n "$no_target_directory"; then + echo "$0: $dst_arg: Is a directory" >&2 + exit 1 + fi + dstdir=$dst + dst=$dstdir/`basename "$src"` + dstdir_status=0 + else + # Prefer dirname, but fall back on a substitute if dirname fails. + dstdir=` + (dirname "$dst") 2>/dev/null || + expr X"$dst" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$dst" : 'X\(//\)[^/]' \| \ + X"$dst" : 'X\(//\)$' \| \ + X"$dst" : 'X\(/\)' \| . 2>/dev/null || + echo X"$dst" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q' + ` + + test -d "$dstdir" + dstdir_status=$? + fi + fi + + obsolete_mkdir_used=false + + if test $dstdir_status != 0; then + case $posix_mkdir in + '') + # Create intermediate dirs using mode 755 as modified by the umask. + # This is like FreeBSD 'install' as of 1997-10-28. + umask=`umask` + case $stripcmd.$umask in + # Optimize common cases. + *[2367][2367]) mkdir_umask=$umask;; + .*0[02][02] | .[02][02] | .[02]) mkdir_umask=22;; + + *[0-7]) + mkdir_umask=`expr $umask + 22 \ + - $umask % 100 % 40 + $umask % 20 \ + - $umask % 10 % 4 + $umask % 2 + `;; + *) mkdir_umask=$umask,go-w;; + esac + + # With -d, create the new directory with the user-specified mode. + # Otherwise, rely on $mkdir_umask. + if test -n "$dir_arg"; then + mkdir_mode=-m$mode + else + mkdir_mode= + fi + + posix_mkdir=false + case $umask in + *[123567][0-7][0-7]) + # POSIX mkdir -p sets u+wx bits regardless of umask, which + # is incompatible with FreeBSD 'install' when (umask & 300) != 0. + ;; + *) + tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$ + trap 'ret=$?; rmdir "$tmpdir/d" "$tmpdir" 2>/dev/null; exit $ret' 0 + + if (umask $mkdir_umask && + exec $mkdirprog $mkdir_mode -p -- "$tmpdir/d") >/dev/null 2>&1 + then + if test -z "$dir_arg" || { + # Check for POSIX incompatibilities with -m. + # HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or + # other-writable bit of parent directory when it shouldn't. + # FreeBSD 6.1 mkdir -m -p sets mode of existing directory. + ls_ld_tmpdir=`ls -ld "$tmpdir"` + case $ls_ld_tmpdir in + d????-?r-*) different_mode=700;; + d????-?--*) different_mode=755;; + *) false;; + esac && + $mkdirprog -m$different_mode -p -- "$tmpdir" && { + ls_ld_tmpdir_1=`ls -ld "$tmpdir"` + test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1" + } + } + then posix_mkdir=: + fi + rmdir "$tmpdir/d" "$tmpdir" + else + # Remove any dirs left behind by ancient mkdir implementations. + rmdir ./$mkdir_mode ./-p ./-- 2>/dev/null + fi + trap '' 0;; + esac;; + esac + + if + $posix_mkdir && ( + umask $mkdir_umask && + $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir" + ) + then : + else + + # The umask is ridiculous, or mkdir does not conform to POSIX, + # or it failed possibly due to a race condition. Create the + # directory the slow way, step by step, checking for races as we go. + + case $dstdir in + /*) prefix='/';; + [-=\(\)!]*) prefix='./';; + *) prefix='';; + esac + + eval "$initialize_posix_glob" + + oIFS=$IFS + IFS=/ + $posix_glob set -f + set fnord $dstdir + shift + $posix_glob set +f + IFS=$oIFS + + prefixes= + + for d + do + test X"$d" = X && continue + + prefix=$prefix$d + if test -d "$prefix"; then + prefixes= + else + if $posix_mkdir; then + (umask=$mkdir_umask && + $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir") && break + # Don't fail if two instances are running concurrently. + test -d "$prefix" || exit 1 + else + case $prefix in + *\'*) qprefix=`echo "$prefix" | sed "s/'/'\\\\\\\\''/g"`;; + *) qprefix=$prefix;; + esac + prefixes="$prefixes '$qprefix'" + fi + fi + prefix=$prefix/ + done + + if test -n "$prefixes"; then + # Don't fail if two instances are running concurrently. + (umask $mkdir_umask && + eval "\$doit_exec \$mkdirprog $prefixes") || + test -d "$dstdir" || exit 1 + obsolete_mkdir_used=true + fi + fi + fi + + if test -n "$dir_arg"; then + { test -z "$chowncmd" || $doit $chowncmd "$dst"; } && + { test -z "$chgrpcmd" || $doit $chgrpcmd "$dst"; } && + { test "$obsolete_mkdir_used$chowncmd$chgrpcmd" = false || + test -z "$chmodcmd" || $doit $chmodcmd $mode "$dst"; } || exit 1 + else + + # Make a couple of temp file names in the proper directory. + dsttmp=$dstdir/_inst.$$_ + rmtmp=$dstdir/_rm.$$_ + + # Trap to clean up those temp files at exit. + trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0 + + # Copy the file name to the temp name. + (umask $cp_umask && $doit_exec $cpprog "$src" "$dsttmp") && + + # and set any options; do chmod last to preserve setuid bits. + # + # If any of these fail, we abort the whole thing. If we want to + # ignore errors from any of these, just make sure not to ignore + # errors from the above "$doit $cpprog $src $dsttmp" command. + # + { test -z "$chowncmd" || $doit $chowncmd "$dsttmp"; } && + { test -z "$chgrpcmd" || $doit $chgrpcmd "$dsttmp"; } && + { test -z "$stripcmd" || $doit $stripcmd "$dsttmp"; } && + { test -z "$chmodcmd" || $doit $chmodcmd $mode "$dsttmp"; } && + + # If -C, don't bother to copy if it wouldn't change the file. + if $copy_on_change && + old=`LC_ALL=C ls -dlL "$dst" 2>/dev/null` && + new=`LC_ALL=C ls -dlL "$dsttmp" 2>/dev/null` && + + eval "$initialize_posix_glob" && + $posix_glob set -f && + set X $old && old=:$2:$4:$5:$6 && + set X $new && new=:$2:$4:$5:$6 && + $posix_glob set +f && + + test "$old" = "$new" && + $cmpprog "$dst" "$dsttmp" >/dev/null 2>&1 + then + rm -f "$dsttmp" + else + # Rename the file to the real destination. + $doit $mvcmd -f "$dsttmp" "$dst" 2>/dev/null || + + # The rename failed, perhaps because mv can't rename something else + # to itself, or perhaps because mv is so ancient that it does not + # support -f. + { + # Now remove or move aside any old file at destination location. + # We try this two ways since rm can't unlink itself on some + # systems and the destination file might be busy for other + # reasons. In this case, the final cleanup might fail but the new + # file should still install successfully. + { + test ! -f "$dst" || + $doit $rmcmd -f "$dst" 2>/dev/null || + { $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null && + { $doit $rmcmd -f "$rmtmp" 2>/dev/null; :; } + } || + { echo "$0: cannot unlink or rename $dst" >&2 + (exit 1); exit 1 + } + } && + + # Now rename the file to the real destination. + $doit $mvcmd "$dsttmp" "$dst" + } + fi || exit 1 + + trap '' 0 + fi +done + +# Local variables: +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "scriptversion=" +# time-stamp-format: "%:y-%02m-%02d.%02H" +# time-stamp-time-zone: "UTC" +# time-stamp-end: "; # UTC" +# End: diff --git a/integration-tests/Makefile.am b/integration-tests/Makefile.am new file mode 100644 index 0000000..f03cd78 --- /dev/null +++ b/integration-tests/Makefile.am @@ -0,0 +1,43 @@ +# nghttp2 - HTTP/2 C Library + +# Copyright (c) 2015 Tatsuhiro Tsujikawa + +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: + +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. + +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +EXTRA_DIST = \ + nghttpx_http1_test.go \ + nghttpx_http2_test.go \ + nghttpx_spdy_test.go \ + server_tester.go \ + server.key \ + server.crt \ + alt-server.key \ + alt-server.crt \ + setenv + +.PHONY: itprep it + +itprep: + go get -d -v github.com/bradfitz/http2 + go get -d -v github.com/tatsuhiro-t/go-nghttp2 + go get -d -v golang.org/x/net/spdy + +it: + sh setenv go test -v diff --git a/integration-tests/Makefile.in b/integration-tests/Makefile.in new file mode 100644 index 0000000..1110f9a --- /dev/null +++ b/integration-tests/Makefile.in @@ -0,0 +1,537 @@ +# Makefile.in generated by automake 1.14.1 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2013 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +# nghttp2 - HTTP/2 C Library + +# Copyright (c) 2015 Tatsuhiro Tsujikawa + +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: + +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. + +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +VPATH = @srcdir@ +am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)' +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +target_triplet = @target@ +subdir = integration-tests +DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \ + $(srcdir)/config.go.in $(srcdir)/setenv.in +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/ax_boost_asio.m4 \ + $(top_srcdir)/m4/ax_boost_base.m4 \ + $(top_srcdir)/m4/ax_boost_system.m4 \ + $(top_srcdir)/m4/ax_boost_thread.m4 \ + $(top_srcdir)/m4/ax_check_compile_flag.m4 \ + $(top_srcdir)/m4/ax_cxx_compile_stdcxx_11.m4 \ + $(top_srcdir)/m4/ax_have_epoll.m4 \ + $(top_srcdir)/m4/ax_python_devel.m4 \ + $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/libxml2.m4 \ + $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ + $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = config.go setenv +CONFIG_CLEAN_VPATH_FILES = +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +SOURCES = +DIST_SOURCES = +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +APPLDFLAGS = @APPLDFLAGS@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +BOOST_ASIO_LIB = @BOOST_ASIO_LIB@ +BOOST_CPPFLAGS = @BOOST_CPPFLAGS@ +BOOST_LDFLAGS = @BOOST_LDFLAGS@ +BOOST_SYSTEM_LIB = @BOOST_SYSTEM_LIB@ +BOOST_THREAD_LIB = @BOOST_THREAD_LIB@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CUNIT_CFLAGS = @CUNIT_CFLAGS@ +CUNIT_LIBS = @CUNIT_LIBS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +CYTHON = @CYTHON@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DLLTOOL = @DLLTOOL@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +GREP = @GREP@ +HAVE_CXX11 = @HAVE_CXX11@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +JANSSON_CFLAGS = @JANSSON_CFLAGS@ +JANSSON_LIBS = @JANSSON_LIBS@ +JEMALLOC_LIBS = @JEMALLOC_LIBS@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBEVENT_OPENSSL_CFLAGS = @LIBEVENT_OPENSSL_CFLAGS@ +LIBEVENT_OPENSSL_LIBS = @LIBEVENT_OPENSSL_LIBS@ +LIBEV_CFLAGS = @LIBEV_CFLAGS@ +LIBEV_LIBS = @LIBEV_LIBS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBSPDYLAY_CFLAGS = @LIBSPDYLAY_CFLAGS@ +LIBSPDYLAY_LIBS = @LIBSPDYLAY_LIBS@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +LT_AGE = @LT_AGE@ +LT_CURRENT = @LT_CURRENT@ +LT_REVISION = @LT_REVISION@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MKDIR_P = @MKDIR_P@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OPENSSL_CFLAGS = @OPENSSL_CFLAGS@ +OPENSSL_LIBS = @OPENSSL_LIBS@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PACKAGE_VERSION_NUM = @PACKAGE_VERSION_NUM@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PKG_CONFIG = @PKG_CONFIG@ +PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ +PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ +PYTHON = @PYTHON@ +PYTHON_CPPFLAGS = @PYTHON_CPPFLAGS@ +PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@ +PYTHON_EXTRA_LDFLAGS = @PYTHON_EXTRA_LDFLAGS@ +PYTHON_EXTRA_LIBS = @PYTHON_EXTRA_LIBS@ +PYTHON_LDFLAGS = @PYTHON_LDFLAGS@ +PYTHON_PLATFORM = @PYTHON_PLATFORM@ +PYTHON_PREFIX = @PYTHON_PREFIX@ +PYTHON_SITE_PKG = @PYTHON_SITE_PKG@ +PYTHON_VERSION = @PYTHON_VERSION@ +RANLIB = @RANLIB@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +TESTLDADD = @TESTLDADD@ +VERSION = @VERSION@ +WARNCFLAGS = @WARNCFLAGS@ +XML2_CONFIG = @XML2_CONFIG@ +XML_CPPFLAGS = @XML_CPPFLAGS@ +XML_LIBS = @XML_LIBS@ +ZLIB_CFLAGS = @ZLIB_CFLAGS@ +ZLIB_LIBS = @ZLIB_LIBS@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +pkgpyexecdir = @pkgpyexecdir@ +pkgpythondir = @pkgpythondir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +pyexecdir = @pyexecdir@ +pythondir = @pythondir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target = @target@ +target_alias = @target_alias@ +target_cpu = @target_cpu@ +target_os = @target_os@ +target_vendor = @target_vendor@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +EXTRA_DIST = \ + nghttpx_http1_test.go \ + nghttpx_http2_test.go \ + nghttpx_spdy_test.go \ + server_tester.go \ + server.key \ + server.crt \ + alt-server.key \ + alt-server.crt \ + setenv + +all: all-am + +.SUFFIXES: +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu integration-tests/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu integration-tests/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): +config.go: $(top_builddir)/config.status $(srcdir)/config.go.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +setenv: $(top_builddir)/config.status $(srcdir)/setenv.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs +tags TAGS: + +ctags CTAGS: + +cscope cscopelist: + + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile +installdirs: +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic clean-libtool mostlyclean-am + +distclean: distclean-am + -rm -f Makefile +distclean-am: clean-am distclean-generic + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-generic mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: + +.MAKE: install-am install-strip + +.PHONY: all all-am check check-am clean clean-generic clean-libtool \ + cscopelist-am ctags-am distclean distclean-generic \ + distclean-libtool distdir dvi dvi-am html html-am info info-am \ + install install-am install-data install-data-am install-dvi \ + install-dvi-am install-exec install-exec-am install-html \ + install-html-am install-info install-info-am install-man \ + install-pdf install-pdf-am install-ps install-ps-am \ + install-strip installcheck installcheck-am installdirs \ + maintainer-clean maintainer-clean-generic mostlyclean \ + mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ + tags-am uninstall uninstall-am + + +.PHONY: itprep it + +itprep: + go get -d -v github.com/bradfitz/http2 + go get -d -v github.com/tatsuhiro-t/go-nghttp2 + go get -d -v golang.org/x/net/spdy + +it: + sh setenv go test -v + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/integration-tests/alt-server.crt b/integration-tests/alt-server.crt new file mode 100644 index 0000000..f003eb1 --- /dev/null +++ b/integration-tests/alt-server.crt @@ -0,0 +1,21 @@ +-----BEGIN CERTIFICATE----- +MIIDhzCCAm+gAwIBAgIJANfuEldiquMNMA0GCSqGSIb3DQEBCwUAMFoxCzAJBgNV +BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX +aWRnaXRzIFB0eSBMdGQxEzARBgNVBAMMCmFsdC1kb21haW4wHhcNMTUwMTI1MDYy +NTQxWhcNMjUwMTIyMDYyNTQxWjBaMQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29t +ZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMRMwEQYD +VQQDDAphbHQtZG9tYWluMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA +0IwhDOGDipGrJQ9IoRSzPdkU/Ii4aJgGKHlXminym42X0VI3IW61RLvOHRlHVmVH +JQjFuDo2x+y81t9NlDg3HGUbSpzOzpm6StiutB7c4hreT5G4r0YKya1ugiemN0+p +qjIPJWm2jVnf448eZvUKRKEQ9W0MLZjiNjVGKrKlwo7fIlXg4N3+YixLYffAT1NV +d1T6V5jzlbruj15gK2nGjMQ9D1h1t9vTbTxY+mtk72aX0Y64IE6pPBWLFSSH8ozU +idDoL3AZwz2Jker+ALKK8CM4uho/RPpyW1C06HH+HLdH2MqEjDOROde/Nzxm668O +gK/JWGIEyUqYiUXx0yhFxwIDAQABo1AwTjAdBgNVHQ4EFgQU/Y0GDN2uPjbyePcu +95ZvYEK/gHIwHwYDVR0jBBgwFoAU/Y0GDN2uPjbyePcu95ZvYEK/gHIwDAYDVR0T +BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAodD6LVCzL3wfsZ6TxTzf9TfgIdbj +ilL3SEMT/xnfTXT3SLYScTRqQIAI29Y7dOLMq89p4hY2wmeUEhBUAz+y9G2JVr8o +6EbxXrQpWgNJogELqoNnMdrDxB5RsmDDKEJ/rLjDfSkjWbK7B2PZsqVTDgjekCFw +u6FqTIjn/O1O/L5tjwxwxjHmQod/maFCvXoDOVBuwdHnkp298tqlvsHfHO8m++Wj ++XYB8plMIjpeTh9v4w9Jc4QZ59lK/3Tt4qaENeQrMEubKSY/Zen7L2bzhk+cChWT +GSGz9uNXieoZaH79D0wnyZaSZ5Ds4ActMevnGg3iYXuzuFqx8Pungn74Vg== +-----END CERTIFICATE----- diff --git a/integration-tests/alt-server.key b/integration-tests/alt-server.key new file mode 100644 index 0000000..a977663 --- /dev/null +++ b/integration-tests/alt-server.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEwAIBADANBgkqhkiG9w0BAQEFAASCBKowggSmAgEAAoIBAQDQjCEM4YOKkasl +D0ihFLM92RT8iLhomAYoeVeaKfKbjZfRUjchbrVEu84dGUdWZUclCMW4OjbH7LzW +302UODccZRtKnM7OmbpK2K60HtziGt5PkbivRgrJrW6CJ6Y3T6mqMg8labaNWd/j +jx5m9QpEoRD1bQwtmOI2NUYqsqXCjt8iVeDg3f5iLEth98BPU1V3VPpXmPOVuu6P +XmAracaMxD0PWHW329NtPFj6a2TvZpfRjrggTqk8FYsVJIfyjNSJ0OgvcBnDPYmR +6v4AsorwIzi6Gj9E+nJbULTocf4ct0fYyoSMM5E51783PGbrrw6Ar8lYYgTJSpiJ +RfHTKEXHAgMBAAECggEBALTrjFSXY72YB+h7rN+JjMIwDIPUvF6I3HbKZhQpJf6K +xNVkRM2tNHavku0tm/S4ohLf3F+pqRKiL2Udjjjy1+S7VgTRqpwTQ0lhV5aNW8SP +2KMg4R61XfB+k+s4KHu9kYxEJ12mqydPe+r3o0FgfYryTDsOYk1AX6b1aqzqFOGF +7GaqLALSbKU59tcJJ1SZNBbpIKFUrAT9nZt9dW02/foqP5bzUk43Yjw48xmLwegc +bMXXcpZhNZSktltvwRw7Q4Foc9kuRlMdTAnAD9PnMCcZwicS/YeVVF6Rz4fGviKv +7/kPHQ7g4YpFktVDzuZ5xw6GDVFeJ6uGMVUX8+EePvkCgYEA+/nrcn82nFHCxm8Q +0iiUhi/AoXjZg+O5Ytaje9O/YNoX+c4ywe13h0+TXKH79O0KfTwXeJyDgPZbAIFV +9oURellRYUzKDafnBHis2f+Ywn6GqHL5e2X30ZxIp1GK46pcvne1YuvJhgGmiVay +vd7sRx09OKU124dG22rIFCis6asCgYEA0+CsA6LrEwQ/aPJYASY3VHNO/WoAOnPg +Cwsg+02XWsPEwP//lNmpanz8TUm2URS063ZK8bx7t3ejvDgBdsRwwjiMlDp7XTUU +3Zk+mhCV2qkMi02aKemvz29bDhmh5JoH7W3IwsXtJYO0yZDYrDR3ioiKRccioPoE +b/Nq781sEFUCgYEA4xqx9xRpaCLY5nicNI6WrwrDF8YQZisNn+PMnYKP7v8itOgA +H4GkRbSXINpueKZc2dsbXH3UmJtyEdaAYBw3UIrIKmZHhl9afFE3mZQhXssjGxfl +fC6/WZD+eq+n+uJFjPXf6jSSAdHjA828dB1D4CSeVTuyexZF6uUnR+QRVNkCgYEA +i+pb7XLSpZYygY03zFp+Q0h6KyKqz+7hTqmkuA8/GfMZpRHop1UtaWLsAeXhfZ2c +87kEOKptUHSzLYIWhWWnyLorK1+LQ7vf8Y5XJso5C1KDNCKk4XSuYt94U9FddWa6 +QXI0F1s5BYL6Cfma++0R2+va08Vy+rbf40XtojoXWJkCgYEA0hMQSCvok7is27nQ +G80KXfmghU2eEB7zif3T00/fwJycxEbmnNeof+SKmhdY4ZgqTscfOxlQPflV/eqB +xs4GnFDDeM0F8KH0BimOXxr7sJPFCg22PCCQQcRtM/KoU+ip/kNmTfwrsC0xMFPU +HD8M1JCZF2eLMekXXP3cB0U4sUs= +-----END PRIVATE KEY----- diff --git a/integration-tests/config.go.in b/integration-tests/config.go.in new file mode 100644 index 0000000..0a6fd6b --- /dev/null +++ b/integration-tests/config.go.in @@ -0,0 +1,5 @@ +package nghttp2 + +const ( + buildDir = "@top_builddir@" +) diff --git a/integration-tests/nghttpx_http1_test.go b/integration-tests/nghttpx_http1_test.go new file mode 100644 index 0000000..c8d5870 --- /dev/null +++ b/integration-tests/nghttpx_http1_test.go @@ -0,0 +1,506 @@ +package nghttp2 + +import ( + "bufio" + "fmt" + "github.com/bradfitz/http2/hpack" + "io" + "net/http" + "syscall" + "testing" +) + +// TestH1H1PlainGET tests whether simple HTTP/1 GET request works. +func TestH1H1PlainGET(t *testing.T) { + st := newServerTester(nil, t, noopHandler) + defer st.Close() + + res, err := st.http1(requestParam{ + name: "TestH1H1PlainGET", + }) + if err != nil { + t.Fatalf("Error st.http1() = %v", err) + } + + want := 200 + if got := res.status; got != want { + t.Errorf("status = %v; want %v", got, want) + } +} + +// TestH1H1PlainGETClose tests whether simple HTTP/1 GET request with +// Connetion: close request header field works. +func TestH1H1PlainGETClose(t *testing.T) { + st := newServerTester(nil, t, noopHandler) + defer st.Close() + + res, err := st.http1(requestParam{ + name: "TestH1H1PlainGETClose", + header: []hpack.HeaderField{ + pair("Connection", "close"), + }, + }) + if err != nil { + t.Fatalf("Error st.http1() = %v", err) + } + + want := 200 + if got := res.status; got != want { + t.Errorf("status = %v; want %v", got, want) + } +} + +// TestH1H1MultipleRequestCL tests that server rejects request which +// contains multiple Content-Length header fields. +func TestH1H1MultipleRequestCL(t *testing.T) { + st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) { + t.Errorf("server should not forward bad request") + }) + defer st.Close() + + if _, err := io.WriteString(st.conn, fmt.Sprintf(`GET / HTTP/1.1 +Host: %v +Test-Case: TestH1H1MultipleRequestCL +Content-Length: 0 +Content-Length: 0 + +`, st.authority)); err != nil { + t.Fatalf("Error io.WriteString() = %v", err) + } + + resp, err := http.ReadResponse(bufio.NewReader(st.conn), nil) + if err != nil { + t.Fatalf("Error http.ReadResponse() = %v", err) + } + + want := 400 + if got := resp.StatusCode; got != want { + t.Errorf("status: %v; want %v", got, want) + } +} + +// TestH1H1ConnectFailure tests that server handles the situation that +// connection attempt to HTTP/1 backend failed. +func TestH1H1ConnectFailure(t *testing.T) { + st := newServerTester(nil, t, noopHandler) + defer st.Close() + + // shutdown backend server to simulate backend connect failure + st.ts.Close() + + res, err := st.http1(requestParam{ + name: "TestH1H1ConnectFailure", + }) + if err != nil { + t.Fatalf("Error st.http1() = %v", err) + } + want := 503 + if got := res.status; got != want { + t.Errorf("status: %v; want %v", got, want) + } +} + +// TestH1H1GracefulShutdown tests graceful shutdown. +func TestH1H1GracefulShutdown(t *testing.T) { + st := newServerTester(nil, t, noopHandler) + defer st.Close() + + res, err := st.http1(requestParam{ + name: "TestH1H1GracefulShutdown-1", + }) + if err != nil { + t.Fatalf("Error st.http1() = %v", err) + } + + if got, want := res.status, 200; got != want { + t.Errorf("status: %v; want %v", got, want) + } + + st.cmd.Process.Signal(syscall.SIGQUIT) + + res, err = st.http1(requestParam{ + name: "TestH1H1GracefulShutdown-2", + }) + if err != nil { + t.Fatalf("Error st.http1() = %v", err) + } + + if got, want := res.status, 200; got != want { + t.Errorf("status: %v; want %v", got, want) + } + + if got, want := res.connClose, true; got != want { + t.Errorf("res.connClose: %v; want %v", got, want) + } + + want := io.EOF + if _, err := st.conn.Read(nil); err == nil || err != want { + t.Errorf("st.conn.Read(): %v; want %v", err, want) + } +} + +// TestH1H1HostRewrite tests that server rewrites Host header field +func TestH1H1HostRewrite(t *testing.T) { + st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) { + w.Header().Add("request-host", r.Host) + }) + defer st.Close() + + res, err := st.http1(requestParam{ + name: "TestH1H1HostRewrite", + }) + if err != nil { + t.Fatalf("Error st.http1() = %v", err) + } + if got, want := res.status, 200; got != want { + t.Errorf("status: %v; want %v", got, want) + } + if got, want := res.header.Get("request-host"), st.backendHost; got != want { + t.Errorf("request-host: %v; want %v", got, want) + } +} + +// TestH1H1HTTP10 tests that server can accept HTTP/1.0 request +// without Host header field +func TestH1H1HTTP10(t *testing.T) { + st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) { + w.Header().Add("request-host", r.Host) + }) + defer st.Close() + + if _, err := io.WriteString(st.conn, "GET / HTTP/1.0\r\nTest-Case: TestH1H1HTTP10\r\n\r\n"); err != nil { + t.Fatalf("Error io.WriteString() = %v", err) + } + + resp, err := http.ReadResponse(bufio.NewReader(st.conn), nil) + if err != nil { + t.Fatalf("Error http.ReadResponse() = %v", err) + } + + if got, want := resp.StatusCode, 200; got != want { + t.Errorf("status: %v; want %v", got, want) + } + if got, want := resp.Header.Get("request-host"), st.backendHost; got != want { + t.Errorf("request-host: %v; want %v", got, want) + } +} + +// TestH1H1HTTP10NoHostRewrite tests that server generates host header +// field using actual backend server even if --no-http-rewrite is +// used. +func TestH1H1HTTP10NoHostRewrite(t *testing.T) { + st := newServerTester([]string{"--no-host-rewrite"}, t, func(w http.ResponseWriter, r *http.Request) { + w.Header().Add("request-host", r.Host) + }) + defer st.Close() + + if _, err := io.WriteString(st.conn, "GET / HTTP/1.0\r\nTest-Case: TestH1H1HTTP10NoHostRewrite\r\n\r\n"); err != nil { + t.Fatalf("Error io.WriteString() = %v", err) + } + + resp, err := http.ReadResponse(bufio.NewReader(st.conn), nil) + if err != nil { + t.Fatalf("Error http.ReadResponse() = %v", err) + } + + if got, want := resp.StatusCode, 200; got != want { + t.Errorf("status: %v; want %v", got, want) + } + if got, want := resp.Header.Get("request-host"), st.backendHost; got != want { + t.Errorf("request-host: %v; want %v", got, want) + } +} + +// TestH1H1RequestTrailer tests request trailer part is forwarded to +// backend. +func TestH1H1RequestTrailer(t *testing.T) { + st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) { + buf := make([]byte, 4096) + for { + _, err := r.Body.Read(buf) + if err == io.EOF { + break + } + if err != nil { + t.Fatalf("r.Body.Read() = %v", err) + } + } + if got, want := r.Trailer.Get("foo"), "bar"; got != want { + t.Errorf("r.Trailer.Get(foo): %v; want %v", got, want) + } + }) + defer st.Close() + + res, err := st.http1(requestParam{ + name: "TestH1H1RequestTrailer", + body: []byte("1"), + trailer: []hpack.HeaderField{ + pair("foo", "bar"), + }, + }) + if err != nil { + t.Fatalf("Error st.http1() = %v", err) + } + if got, want := res.status, 200; got != want { + t.Errorf("res.status: %v; want %v", got, want) + } +} + +// TestH1H1HeaderFieldBufferPath tests that request with request path +// larger than configured buffer size is rejected. +func TestH1H1HeaderFieldBufferPath(t *testing.T) { + // The value 100 is chosen so that sum of header fields bytes + // does not exceed it. We use > 100 bytes URI to exceed this + // limit. + st := newServerTester([]string{"--header-field-buffer=100"}, t, func(w http.ResponseWriter, r *http.Request) { + t.Fatal("execution path should not be here") + }) + defer st.Close() + + res, err := st.http1(requestParam{ + name: "TestH1H1HeaderFieldBufferPath", + path: "/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", + }) + if err != nil { + t.Fatalf("Error st.http1() = %v", err) + } + if got, want := res.status, 431; got != want { + t.Errorf("status: %v; want %v", got, want) + } +} + +// TestH1H1HeaderFieldBuffer tests that request with header fields +// larger than configured buffer size is rejected. +func TestH1H1HeaderFieldBuffer(t *testing.T) { + st := newServerTester([]string{"--header-field-buffer=10"}, t, func(w http.ResponseWriter, r *http.Request) { + t.Fatal("execution path should not be here") + }) + defer st.Close() + + res, err := st.http1(requestParam{ + name: "TestH1H1HeaderFieldBuffer", + }) + if err != nil { + t.Fatalf("Error st.http1() = %v", err) + } + if got, want := res.status, 431; got != want { + t.Errorf("status: %v; want %v", got, want) + } +} + +// TestH1H1HeaderFields tests that request with header fields more +// than configured number is rejected. +func TestH1H1HeaderFields(t *testing.T) { + st := newServerTester([]string{"--max-header-fields=1"}, t, func(w http.ResponseWriter, r *http.Request) { + t.Fatal("execution path should not be here") + }) + defer st.Close() + + res, err := st.http1(requestParam{ + name: "TestH1H1HeaderFields", + header: []hpack.HeaderField{ + // Add extra header field to ensure that + // header field limit exceeds + pair("Connection", "close"), + }, + }) + if err != nil { + t.Fatalf("Error st.http1() = %v", err) + } + if got, want := res.status, 431; got != want { + t.Errorf("status: %v; want %v", got, want) + } +} + +// TestH1H2ConnectFailure tests that server handles the situation that +// connection attempt to HTTP/2 backend failed. +func TestH1H2ConnectFailure(t *testing.T) { + st := newServerTester([]string{"--http2-bridge"}, t, noopHandler) + defer st.Close() + + // simulate backend connect attempt failure + st.ts.Close() + + res, err := st.http1(requestParam{ + name: "TestH1H2ConnectFailure", + }) + if err != nil { + t.Fatalf("Error st.http1() = %v", err) + } + want := 503 + if got := res.status; got != want { + t.Errorf("status: %v; want %v", got, want) + } +} + +// TestH1H2NoHost tests that server rejects request without Host +// header field for HTTP/2 backend. +func TestH1H2NoHost(t *testing.T) { + st := newServerTester([]string{"--http2-bridge"}, t, func(w http.ResponseWriter, r *http.Request) { + t.Errorf("server should not forward bad request") + }) + defer st.Close() + + // without Host header field, we expect 400 response + if _, err := io.WriteString(st.conn, "GET / HTTP/1.1\r\nTest-Case: TestH1H2NoHost\r\n\r\n"); err != nil { + t.Fatalf("Error io.WriteString() = %v", err) + } + + resp, err := http.ReadResponse(bufio.NewReader(st.conn), nil) + if err != nil { + t.Fatalf("Error http.ReadResponse() = %v", err) + } + + want := 400 + if got := resp.StatusCode; got != want { + t.Errorf("status: %v; want %v", got, want) + } +} + +// TestH1H2HTTP10 tests that server can accept HTTP/1.0 request +// without Host header field +func TestH1H2HTTP10(t *testing.T) { + st := newServerTester([]string{"--http2-bridge"}, t, func(w http.ResponseWriter, r *http.Request) { + w.Header().Add("request-host", r.Host) + }) + defer st.Close() + + if _, err := io.WriteString(st.conn, "GET / HTTP/1.0\r\nTest-Case: TestH1H2HTTP10\r\n\r\n"); err != nil { + t.Fatalf("Error io.WriteString() = %v", err) + } + + resp, err := http.ReadResponse(bufio.NewReader(st.conn), nil) + if err != nil { + t.Fatalf("Error http.ReadResponse() = %v", err) + } + + if got, want := resp.StatusCode, 200; got != want { + t.Errorf("status: %v; want %v", got, want) + } + if got, want := resp.Header.Get("request-host"), st.backendHost; got != want { + t.Errorf("request-host: %v; want %v", got, want) + } +} + +// TestH1H2HTTP10NoHostRewrite tests that server generates host header +// field using actual backend server even if --no-http-rewrite is +// used. +func TestH1H2HTTP10NoHostRewrite(t *testing.T) { + st := newServerTester([]string{"--http2-bridge", "--no-host-rewrite"}, t, func(w http.ResponseWriter, r *http.Request) { + w.Header().Add("request-host", r.Host) + }) + defer st.Close() + + if _, err := io.WriteString(st.conn, "GET / HTTP/1.0\r\nTest-Case: TestH1H2HTTP10NoHostRewrite\r\n\r\n"); err != nil { + t.Fatalf("Error io.WriteString() = %v", err) + } + + resp, err := http.ReadResponse(bufio.NewReader(st.conn), nil) + if err != nil { + t.Fatalf("Error http.ReadResponse() = %v", err) + } + + if got, want := resp.StatusCode, 200; got != want { + t.Errorf("status: %v; want %v", got, want) + } + if got, want := resp.Header.Get("request-host"), st.backendHost; got != want { + t.Errorf("request-host: %v; want %v", got, want) + } +} + +// TestH1H2CrumbleCookie tests that Cookies are crumbled and assembled +// when forwarding to HTTP/2 backend link. go-nghttp2 server +// concatenates crumbled Cookies automatically, so this test is not +// much effective now. +func TestH1H2CrumbleCookie(t *testing.T) { + st := newServerTester([]string{"--http2-bridge"}, t, func(w http.ResponseWriter, r *http.Request) { + if got, want := r.Header.Get("Cookie"), "alpha; bravo; charlie"; got != want { + t.Errorf("Cookie: %v; want %v", got, want) + } + }) + defer st.Close() + + res, err := st.http1(requestParam{ + name: "TestH1H2CrumbleCookie", + header: []hpack.HeaderField{ + pair("Cookie", "alpha; bravo; charlie"), + }, + }) + if err != nil { + t.Fatalf("Error st.http1() = %v", err) + } + if got, want := res.status, 200; got != want { + t.Errorf("status: %v; want %v", got, want) + } +} + +// TestH1H2GenerateVia tests that server generates Via header field to and +// from backend server. +func TestH1H2GenerateVia(t *testing.T) { + st := newServerTester([]string{"--http2-bridge"}, t, func(w http.ResponseWriter, r *http.Request) { + if got, want := r.Header.Get("Via"), "1.1 nghttpx"; got != want { + t.Errorf("Via: %v; want %v", got, want) + } + }) + defer st.Close() + + res, err := st.http1(requestParam{ + name: "TestH1H2GenerateVia", + }) + if err != nil { + t.Fatalf("Error st.http1() = %v", err) + } + if got, want := res.header.Get("Via"), "2 nghttpx"; got != want { + t.Errorf("Via: %v; want %v", got, want) + } +} + +// TestH1H2AppendVia tests that server adds value to existing Via +// header field to and from backend server. +func TestH1H2AppendVia(t *testing.T) { + st := newServerTester([]string{"--http2-bridge"}, t, func(w http.ResponseWriter, r *http.Request) { + if got, want := r.Header.Get("Via"), "foo, 1.1 nghttpx"; got != want { + t.Errorf("Via: %v; want %v", got, want) + } + w.Header().Add("Via", "bar") + }) + defer st.Close() + + res, err := st.http1(requestParam{ + name: "TestH1H2AppendVia", + header: []hpack.HeaderField{ + pair("via", "foo"), + }, + }) + if err != nil { + t.Fatalf("Error st.http1() = %v", err) + } + if got, want := res.header.Get("Via"), "bar, 2 nghttpx"; got != want { + t.Errorf("Via: %v; want %v", got, want) + } +} + +// TestH1H2NoVia tests that server does not add value to existing Via +// header field to and from backend server. +func TestH1H2NoVia(t *testing.T) { + st := newServerTester([]string{"--http2-bridge", "--no-via"}, t, func(w http.ResponseWriter, r *http.Request) { + if got, want := r.Header.Get("Via"), "foo"; got != want { + t.Errorf("Via: %v; want %v", got, want) + } + w.Header().Add("Via", "bar") + }) + defer st.Close() + + res, err := st.http1(requestParam{ + name: "TestH1H2NoVia", + header: []hpack.HeaderField{ + pair("via", "foo"), + }, + }) + if err != nil { + t.Fatalf("Error st.http1() = %v", err) + } + if got, want := res.header.Get("Via"), "bar"; got != want { + t.Errorf("Via: %v; want %v", got, want) + } +} diff --git a/integration-tests/nghttpx_http2_test.go b/integration-tests/nghttpx_http2_test.go new file mode 100644 index 0000000..953e9d4 --- /dev/null +++ b/integration-tests/nghttpx_http2_test.go @@ -0,0 +1,813 @@ +package nghttp2 + +import ( + "crypto/tls" + "fmt" + "github.com/bradfitz/http2" + "github.com/bradfitz/http2/hpack" + "io" + "io/ioutil" + "net/http" + "strings" + "syscall" + "testing" +) + +// TestH2H1PlainGET tests whether simple HTTP/2 GET request works. +func TestH2H1PlainGET(t *testing.T) { + st := newServerTester(nil, t, noopHandler) + defer st.Close() + + res, err := st.http2(requestParam{ + name: "TestH2H1PlainGET", + }) + if err != nil { + t.Fatalf("Error st.http2() = %v", err) + } + + want := 200 + if res.status != want { + t.Errorf("status = %v; want %v", res.status, want) + } +} + +// TestH2H1AddXff tests that server generates X-Forwarded-For header +// field when forwarding request to backend. +func TestH2H1AddXff(t *testing.T) { + st := newServerTester([]string{"--add-x-forwarded-for"}, t, func(w http.ResponseWriter, r *http.Request) { + xff := r.Header.Get("X-Forwarded-For") + want := "127.0.0.1" + if xff != want { + t.Errorf("X-Forwarded-For = %v; want %v", xff, want) + } + }) + defer st.Close() + + _, err := st.http2(requestParam{ + name: "TestH2H1AddXff", + }) + if err != nil { + t.Fatalf("Error st.http2() = %v", err) + } +} + +// TestH2H1AddXff2 tests that server appends X-Forwarded-For header +// field to existing one when forwarding request to backend. +func TestH2H1AddXff2(t *testing.T) { + st := newServerTester([]string{"--add-x-forwarded-for"}, t, func(w http.ResponseWriter, r *http.Request) { + xff := r.Header.Get("X-Forwarded-For") + want := "host, 127.0.0.1" + if xff != want { + t.Errorf("X-Forwarded-For = %v; want %v", xff, want) + } + }) + defer st.Close() + + _, err := st.http2(requestParam{ + name: "TestH2H1AddXff2", + header: []hpack.HeaderField{ + pair("x-forwarded-for", "host"), + }, + }) + if err != nil { + t.Fatalf("Error st.http2() = %v", err) + } +} + +// TestH2H1StripXff tests that --strip-incoming-x-forwarded-for +// option. +func TestH2H1StripXff(t *testing.T) { + st := newServerTester([]string{"--strip-incoming-x-forwarded-for"}, t, func(w http.ResponseWriter, r *http.Request) { + if xff, found := r.Header["X-Forwarded-For"]; found { + t.Errorf("X-Forwarded-For = %v; want nothing", xff) + } + }) + defer st.Close() + + _, err := st.http2(requestParam{ + name: "TestH2H1StripXff1", + header: []hpack.HeaderField{ + pair("x-forwarded-for", "host"), + }, + }) + if err != nil { + t.Fatalf("Error st.http2() = %v", err) + } +} + +// TestH2H1StripAddXff tests that --strip-incoming-x-forwarded-for and +// --add-x-forwarded-for options. +func TestH2H1StripAddXff(t *testing.T) { + args := []string{ + "--strip-incoming-x-forwarded-for", + "--add-x-forwarded-for", + } + st := newServerTester(args, t, func(w http.ResponseWriter, r *http.Request) { + xff := r.Header.Get("X-Forwarded-For") + want := "127.0.0.1" + if xff != want { + t.Errorf("X-Forwarded-For = %v; want %v", xff, want) + } + }) + defer st.Close() + + _, err := st.http2(requestParam{ + name: "TestH2H1StripAddXff", + header: []hpack.HeaderField{ + pair("x-forwarded-for", "host"), + }, + }) + if err != nil { + t.Fatalf("Error st.http2() = %v", err) + } +} + +// TestH2H1GenerateVia tests that server generates Via header field to and +// from backend server. +func TestH2H1GenerateVia(t *testing.T) { + st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) { + if got, want := r.Header.Get("Via"), "2 nghttpx"; got != want { + t.Errorf("Via: %v; want %v", got, want) + } + }) + defer st.Close() + + res, err := st.http2(requestParam{ + name: "TestH2H1GenerateVia", + }) + if err != nil { + t.Fatalf("Error st.http2() = %v", err) + } + if got, want := res.header.Get("Via"), "1.1 nghttpx"; got != want { + t.Errorf("Via: %v; want %v", got, want) + } +} + +// TestH2H1AppendVia tests that server adds value to existing Via +// header field to and from backend server. +func TestH2H1AppendVia(t *testing.T) { + st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) { + if got, want := r.Header.Get("Via"), "foo, 2 nghttpx"; got != want { + t.Errorf("Via: %v; want %v", got, want) + } + w.Header().Add("Via", "bar") + }) + defer st.Close() + + res, err := st.http2(requestParam{ + name: "TestH2H1AppendVia", + header: []hpack.HeaderField{ + pair("via", "foo"), + }, + }) + if err != nil { + t.Fatalf("Error st.http2() = %v", err) + } + if got, want := res.header.Get("Via"), "bar, 1.1 nghttpx"; got != want { + t.Errorf("Via: %v; want %v", got, want) + } +} + +// TestH2H1NoVia tests that server does not add value to existing Via +// header field to and from backend server. +func TestH2H1NoVia(t *testing.T) { + st := newServerTester([]string{"--no-via"}, t, func(w http.ResponseWriter, r *http.Request) { + if got, want := r.Header.Get("Via"), "foo"; got != want { + t.Errorf("Via: %v; want %v", got, want) + } + w.Header().Add("Via", "bar") + }) + defer st.Close() + + res, err := st.http2(requestParam{ + name: "TestH2H1NoVia", + header: []hpack.HeaderField{ + pair("via", "foo"), + }, + }) + if err != nil { + t.Fatalf("Error st.http2() = %v", err) + } + if got, want := res.header.Get("Via"), "bar"; got != want { + t.Errorf("Via: %v; want %v", got, want) + } +} + +// TestH2H1HostRewrite tests that server rewrites host header field +func TestH2H1HostRewrite(t *testing.T) { + st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) { + w.Header().Add("request-host", r.Host) + }) + defer st.Close() + + res, err := st.http2(requestParam{ + name: "TestH2H1HostRewrite", + }) + if err != nil { + t.Fatalf("Error st.http2() = %v", err) + } + if got, want := res.status, 200; got != want { + t.Errorf("status: %v; want %v", got, want) + } + if got, want := res.header.Get("request-host"), st.backendHost; got != want { + t.Errorf("request-host: %v; want %v", got, want) + } +} + +// TestH2H1NoHostRewrite tests that server does not rewrite host +// header field +func TestH2H1NoHostRewrite(t *testing.T) { + st := newServerTester([]string{"--no-host-rewrite"}, t, func(w http.ResponseWriter, r *http.Request) { + w.Header().Add("request-host", r.Host) + }) + defer st.Close() + + res, err := st.http2(requestParam{ + name: "TestH2H1NoHostRewrite", + }) + if err != nil { + t.Fatalf("Error st.http2() = %v", err) + } + if got, want := res.status, 200; got != want { + t.Errorf("status: %v; want %v", got, want) + } + if got, want := res.header.Get("request-host"), st.frontendHost; got != want { + t.Errorf("request-host: %v; want %v", got, want) + } +} + +// TestH2H1BadRequestCL tests that server rejects request whose +// content-length header field value does not match its request body +// size. +func TestH2H1BadRequestCL(t *testing.T) { + st := newServerTester(nil, t, noopHandler) + defer st.Close() + + // we set content-length: 1024, but the actual request body is + // 3 bytes. + res, err := st.http2(requestParam{ + name: "TestH2H1BadRequestCL", + method: "POST", + header: []hpack.HeaderField{ + pair("content-length", "1024"), + }, + body: []byte("foo"), + }) + if err != nil { + t.Fatalf("Error st.http2() = %v", err) + } + + want := http2.ErrCodeProtocol + if res.errCode != want { + t.Errorf("res.errCode = %v; want %v", res.errCode, want) + } +} + +// TestH2H1BadResponseCL tests that server returns error when +// content-length response header field value does not match its +// response body size. +func TestH2H1BadResponseCL(t *testing.T) { + st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) { + // we set content-length: 1024, but only send 3 bytes. + w.Header().Add("Content-Length", "1024") + w.Write([]byte("foo")) + }) + defer st.Close() + + res, err := st.http2(requestParam{ + name: "TestH2H1BadResponseCL", + }) + if err != nil { + t.Fatalf("Error st.http2() = %v", err) + } + + want := http2.ErrCodeProtocol + if res.errCode != want { + t.Errorf("res.errCode = %v; want %v", res.errCode, want) + } +} + +// TestH2H1LocationRewrite tests location header field rewriting +// works. +func TestH2H1LocationRewrite(t *testing.T) { + st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) { + // TODO we cannot get st.ts's port number here.. 8443 + // is just a place holder. We ignore it on rewrite. + w.Header().Add("Location", "http://127.0.0.1:8443/p/q?a=b#fragment") + }) + defer st.Close() + + res, err := st.http2(requestParam{ + name: "TestH2H1LocationRewrite", + }) + if err != nil { + t.Fatalf("Error st.http2() = %v", err) + } + + want := fmt.Sprintf("http://127.0.0.1:%v/p/q?a=b#fragment", serverPort) + if got := res.header.Get("Location"); got != want { + t.Errorf("Location: %v; want %v", got, want) + } +} + +// TestH2H1ChunkedRequestBody tests that chunked request body works. +func TestH2H1ChunkedRequestBody(t *testing.T) { + st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) { + want := "[chunked]" + if got := fmt.Sprint(r.TransferEncoding); got != want { + t.Errorf("Transfer-Encoding: %v; want %v", got, want) + } + body, err := ioutil.ReadAll(r.Body) + if err != nil { + t.Fatalf("Error reading r.body: %v", err) + } + want = "foo" + if got := string(body); got != want { + t.Errorf("body: %v; want %v", got, want) + } + }) + defer st.Close() + + _, err := st.http2(requestParam{ + name: "TestH2H1ChunkedRequestBody", + method: "POST", + body: []byte("foo"), + }) + if err != nil { + t.Fatalf("Error st.http2() = %v", err) + } +} + +// TestH2H1MultipleRequestCL tests that server rejects request with +// multiple Content-Length request header fields. +func TestH2H1MultipleRequestCL(t *testing.T) { + st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) { + t.Errorf("server should not forward bad request") + }) + defer st.Close() + + res, err := st.http2(requestParam{ + name: "TestH2H1MultipleRequestCL", + header: []hpack.HeaderField{ + pair("content-length", "1"), + pair("content-length", "1"), + }, + }) + if err != nil { + t.Fatalf("Error st.http2() = %v", err) + } + if got, want := res.errCode, http2.ErrCodeProtocol; got != want { + t.Errorf("res.errCode: %v; want %v", got, want) + } +} + +// TestH2H1InvalidRequestCL tests that server rejects request with +// Content-Length which cannot be parsed as a number. +func TestH2H1InvalidRequestCL(t *testing.T) { + st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) { + t.Errorf("server should not forward bad request") + }) + defer st.Close() + + res, err := st.http2(requestParam{ + name: "TestH2H1InvalidRequestCL", + header: []hpack.HeaderField{ + pair("content-length", ""), + }, + }) + if err != nil { + t.Fatalf("Error st.http2() = %v", err) + } + if got, want := res.errCode, http2.ErrCodeProtocol; got != want { + t.Errorf("res.errCode: %v; want %v", got, want) + } +} + +// TestH2H1ConnectFailure tests that server handles the situation that +// connection attempt to HTTP/1 backend failed. +func TestH2H1ConnectFailure(t *testing.T) { + st := newServerTester(nil, t, noopHandler) + defer st.Close() + + // shutdown backend server to simulate backend connect failure + st.ts.Close() + + res, err := st.http2(requestParam{ + name: "TestH2H1ConnectFailure", + }) + if err != nil { + t.Fatalf("Error st.http2() = %v", err) + } + want := 503 + if got := res.status; got != want { + t.Errorf("status: %v; want %v", got, want) + } +} + +// TestH2H1AssembleCookies tests that crumbled cookies in HTTP/2 +// request is assembled into 1 when forwarding to HTTP/1 backend link. +func TestH2H1AssembleCookies(t *testing.T) { + st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) { + if got, want := r.Header.Get("Cookie"), "alpha; bravo; charlie"; got != want { + t.Errorf("Cookie: %v; want %v", got, want) + } + }) + defer st.Close() + + res, err := st.http2(requestParam{ + name: "TestH2H1AssembleCookies", + header: []hpack.HeaderField{ + pair("cookie", "alpha"), + pair("cookie", "bravo"), + pair("cookie", "charlie"), + }, + }) + if err != nil { + t.Fatalf("Error st.http2() = %v", err) + } + if got, want := res.status, 200; got != want { + t.Errorf("status: %v; want %v", got, want) + } +} + +// TestH2H1TETrailers tests that server accepts TE request header +// field if it has trailers only. +func TestH2H1TETrailers(t *testing.T) { + st := newServerTester(nil, t, noopHandler) + defer st.Close() + + res, err := st.http2(requestParam{ + name: "TestH2H1TETrailers", + header: []hpack.HeaderField{ + pair("te", "trailers"), + }, + }) + if err != nil { + t.Fatalf("Error st.http2() = %v", err) + } + if got, want := res.status, 200; got != want { + t.Errorf("status: %v; want %v", got, want) + } +} + +// TestH2H1TEGzip tests that server resets stream if TE request header +// field contains gzip. +func TestH2H1TEGzip(t *testing.T) { + st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) { + t.Error("server should not forward bad request") + }) + defer st.Close() + + res, err := st.http2(requestParam{ + name: "TestH2H1TEGzip", + header: []hpack.HeaderField{ + pair("te", "gzip"), + }, + }) + if err != nil { + t.Fatalf("Error st.http2() = %v", err) + } + if got, want := res.errCode, http2.ErrCodeProtocol; got != want { + t.Errorf("res.errCode = %v; want %v", res.errCode, want) + } +} + +// TestH2H1SNI tests server's TLS SNI extension feature. It must +// choose appropriate certificate depending on the indicated +// server_name from client. +func TestH2H1SNI(t *testing.T) { + st := newServerTesterTLSConfig([]string{"--subcert=" + testDir + "/alt-server.key:" + testDir + "/alt-server.crt"}, t, noopHandler, &tls.Config{ + ServerName: "alt-domain", + }) + defer st.Close() + + tlsConn := st.conn.(*tls.Conn) + connState := tlsConn.ConnectionState() + cert := connState.PeerCertificates[0] + + if got, want := cert.Subject.CommonName, "alt-domain"; got != want { + t.Errorf("CommonName: %v; want %v", got, want) + } +} + +// TestH2H1ServerPush tests server push using Link header field from +// backend server. +func TestH2H1ServerPush(t *testing.T) { + st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) { + // only resources marked as rel=preload are pushed + if !strings.HasPrefix(r.URL.Path, "/css/") { + w.Header().Add("Link", "; rel=preload, , ; rel=preload") + } + }) + defer st.Close() + + res, err := st.http2(requestParam{ + name: "TestH2H1ServerPush", + }) + if err != nil { + t.Fatalf("Error st.http2() = %v", err) + } + if got, want := res.status, 200; got != want { + t.Errorf("res.status: %v; want %v", got, want) + } + if got, want := len(res.pushResponse), 2; got != want { + t.Fatalf("len(res.pushResponse): %v; want %v", got, want) + } + mainCSS := res.pushResponse[0] + if got, want := mainCSS.status, 200; got != want { + t.Errorf("mainCSS.status: %v; want %v", got, want) + } + themeCSS := res.pushResponse[1] + if got, want := themeCSS.status, 200; got != want { + t.Errorf("themeCSS.status: %v; want %v", got, want) + } +} + +// TestH2H1RequestTrailer tests request trailer part is forwarded to +// backend. +func TestH2H1RequestTrailer(t *testing.T) { + st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) { + buf := make([]byte, 4096) + for { + _, err := r.Body.Read(buf) + if err == io.EOF { + break + } + if err != nil { + t.Fatalf("r.Body.Read() = %v", err) + } + } + if got, want := r.Trailer.Get("foo"), "bar"; got != want { + t.Errorf("r.Trailer.Get(foo): %v; want %v", got, want) + } + }) + defer st.Close() + + res, err := st.http2(requestParam{ + name: "TestH2H1RequestTrailer", + body: []byte("1"), + trailer: []hpack.HeaderField{ + pair("foo", "bar"), + }, + }) + if err != nil { + t.Fatalf("Error st.http2() = %v", err) + } + if got, want := res.status, 200; got != want { + t.Errorf("res.status: %v; want %v", got, want) + } +} + +// TestH2H1HeaderFieldBuffer tests that request with header fields +// larger than configured buffer size is rejected. +func TestH2H1HeaderFieldBuffer(t *testing.T) { + st := newServerTester([]string{"--header-field-buffer=10"}, t, func(w http.ResponseWriter, r *http.Request) { + t.Fatal("execution path should not be here") + }) + defer st.Close() + + res, err := st.http2(requestParam{ + name: "TestH2H1HeaderFieldBuffer", + }) + if err != nil { + t.Fatalf("Error st.http2() = %v", err) + } + if got, want := res.status, 431; got != want { + t.Errorf("status: %v; want %v", got, want) + } +} + +// TestH2H1HeaderFields tests that request with header fields more +// than configured number is rejected. +func TestH2H1HeaderFields(t *testing.T) { + st := newServerTester([]string{"--max-header-fields=1"}, t, func(w http.ResponseWriter, r *http.Request) { + t.Fatal("execution path should not be here") + }) + defer st.Close() + + res, err := st.http2(requestParam{ + name: "TestH2H1HeaderFields", + // we have at least 4 pseudo-header fields sent, and + // that ensures that buffer limit exceeds. + }) + if err != nil { + t.Fatalf("Error st.http2() = %v", err) + } + if got, want := res.status, 431; got != want { + t.Errorf("status: %v; want %v", got, want) + } +} + +// TestH2H1Upgrade tests HTTP Upgrade to HTTP/2 +func TestH2H1Upgrade(t *testing.T) { + st := newServerTester(nil, t, func(w http.ResponseWriter, r *http.Request) {}) + defer st.Close() + + res, err := st.http1(requestParam{ + name: "TestH2H1Upgrade", + header: []hpack.HeaderField{ + pair("Connection", "Upgrade, HTTP2-Settings"), + pair("Upgrade", "h2c"), + pair("HTTP2-Settings", "AAMAAABkAAQAAP__"), + }, + }) + + if err != nil { + t.Fatalf("Error st.http1() = %v", err) + } + + if got, want := res.status, 101; got != want { + t.Errorf("res.status: %v; want %v", got, want) + } + + res, err = st.http2(requestParam{ + httpUpgrade: true, + }) + if err != nil { + t.Fatalf("Error st.http2() = %v", err) + } + if got, want := res.status, 200; got != want { + t.Errorf("res.status: %v; want %v", got, want) + } +} + +// TestH2H1GracefulShutdown tests graceful shutdown. +func TestH2H1GracefulShutdown(t *testing.T) { + st := newServerTester(nil, t, noopHandler) + defer st.Close() + + fmt.Fprint(st.conn, "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n") + if err := st.fr.WriteSettings(); err != nil { + t.Fatalf("st.fr.WriteSettings(): %v", err) + } + + header := []hpack.HeaderField{ + pair(":method", "GET"), + pair(":scheme", "http"), + pair(":authority", st.authority), + pair(":path", "/"), + } + + for _, h := range header { + _ = st.enc.WriteField(h) + } + + if err := st.fr.WriteHeaders(http2.HeadersFrameParam{ + StreamID: 1, + EndStream: false, + EndHeaders: true, + BlockFragment: st.headerBlkBuf.Bytes(), + }); err != nil { + t.Fatalf("st.fr.WriteHeaders(): %v", err) + } + + // send SIGQUIT signal to nghttpx to perform graceful shutdown + st.cmd.Process.Signal(syscall.SIGQUIT) + + // after signal, finish request body + if err := st.fr.WriteData(1, true, nil); err != nil { + t.Fatalf("st.fr.WriteData(): %v", err) + } + + numGoAway := 0 + + for { + fr, err := st.readFrame() + if err != nil { + if err == io.EOF { + want := 2 + if got := numGoAway; got != want { + t.Fatalf("numGoAway: %v; want %v", got, want) + } + return + } + t.Fatalf("st.readFrame(): %v", err) + } + switch f := fr.(type) { + case *http2.GoAwayFrame: + numGoAway += 1 + want := http2.ErrCodeNo + if got := f.ErrCode; got != want { + t.Fatalf("f.ErrCode(%v): %v; want %v", numGoAway, got, want) + } + switch numGoAway { + case 1: + want := (uint32(1) << 31) - 1 + if got := f.LastStreamID; got != want { + t.Fatalf("f.LastStreamID(%v): %v; want %v", numGoAway, got, want) + } + case 2: + want := uint32(1) + if got := f.LastStreamID; got != want { + t.Fatalf("f.LastStreamID(%v): %v; want %v", numGoAway, got, want) + } + case 3: + t.Fatalf("too many GOAWAYs received") + } + } + } +} + +// TestH2H2MultipleResponseCL tests that server returns error if +// multiple Content-Length response header fields are received. +func TestH2H2MultipleResponseCL(t *testing.T) { + st := newServerTester([]string{"--http2-bridge"}, t, func(w http.ResponseWriter, r *http.Request) { + w.Header().Add("content-length", "1") + w.Header().Add("content-length", "1") + }) + defer st.Close() + + res, err := st.http2(requestParam{ + name: "TestH2H2MultipleResponseCL", + }) + if err != nil { + t.Fatalf("Error st.http2() = %v", err) + } + if got, want := res.errCode, http2.ErrCodeInternal; got != want { + t.Errorf("res.errCode: %v; want %v", got, want) + } +} + +// TestH2H2InvalidResponseCL tests that server returns error if +// Content-Length response header field value cannot be parsed as a +// number. +func TestH2H2InvalidResponseCL(t *testing.T) { + st := newServerTester([]string{"--http2-bridge"}, t, func(w http.ResponseWriter, r *http.Request) { + w.Header().Add("content-length", "") + }) + defer st.Close() + + res, err := st.http2(requestParam{ + name: "TestH2H2InvalidResponseCL", + }) + if err != nil { + t.Fatalf("Error st.http2() = %v", err) + } + if got, want := res.errCode, http2.ErrCodeInternal; got != want { + t.Errorf("res.errCode: %v; want %v", got, want) + } +} + +// TestH2H2ConnectFailure tests that server handles the situation that +// connection attempt to HTTP/2 backend failed. +func TestH2H2ConnectFailure(t *testing.T) { + st := newServerTester([]string{"--http2-bridge"}, t, noopHandler) + defer st.Close() + + // simulate backend connect attempt failure + st.ts.Close() + + res, err := st.http2(requestParam{ + name: "TestH2H2ConnectFailure", + }) + if err != nil { + t.Fatalf("Error st.http2() = %v", err) + } + want := 503 + if got := res.status; got != want { + t.Errorf("status: %v; want %v", got, want) + } +} + +// TestH2H2HostRewrite tests that server rewrites host header field +func TestH2H2HostRewrite(t *testing.T) { + st := newServerTester([]string{"--http2-bridge"}, t, func(w http.ResponseWriter, r *http.Request) { + w.Header().Add("request-host", r.Host) + }) + defer st.Close() + + res, err := st.http2(requestParam{ + name: "TestH2H2HostRewrite", + }) + if err != nil { + t.Fatalf("Error st.http2() = %v", err) + } + if got, want := res.status, 200; got != want { + t.Errorf("status: %v; want %v", got, want) + } + if got, want := res.header.Get("request-host"), st.backendHost; got != want { + t.Errorf("request-host: %v; want %v", got, want) + } +} + +// TestH2H2NoHostRewrite tests that server does not rewrite host +// header field +func TestH2H2NoHostRewrite(t *testing.T) { + st := newServerTester([]string{"--http2-bridge", "--no-host-rewrite"}, t, func(w http.ResponseWriter, r *http.Request) { + w.Header().Add("request-host", r.Host) + }) + defer st.Close() + + res, err := st.http2(requestParam{ + name: "TestH2H2NoHostRewrite", + }) + if err != nil { + t.Fatalf("Error st.http2() = %v", err) + } + if got, want := res.status, 200; got != want { + t.Errorf("status: %v; want %v", got, want) + } + if got, want := res.header.Get("request-host"), st.frontendHost; got != want { + t.Errorf("request-host: %v; want %v", got, want) + } +} diff --git a/integration-tests/nghttpx_spdy_test.go b/integration-tests/nghttpx_spdy_test.go new file mode 100644 index 0000000..232e1da --- /dev/null +++ b/integration-tests/nghttpx_spdy_test.go @@ -0,0 +1,232 @@ +package nghttp2 + +import ( + "github.com/bradfitz/http2/hpack" + "golang.org/x/net/spdy" + "net/http" + "testing" +) + +// TestS3H1PlainGET tests whether simple SPDY GET request works. +func TestS3H1PlainGET(t *testing.T) { + st := newServerTesterTLS([]string{"--npn-list=spdy/3.1"}, t, noopHandler) + defer st.Close() + + res, err := st.spdy(requestParam{ + name: "TestS3H1PlainGET", + }) + if err != nil { + t.Fatalf("Error st.spdy() = %v", err) + } + + want := 200 + if got := res.status; got != want { + t.Errorf("status = %v; want %v", got, want) + } +} + +// TestS3H1BadRequestCL tests that server rejects request whose +// content-length header field value does not match its request body +// size. +func TestS3H1BadRequestCL(t *testing.T) { + st := newServerTesterTLS([]string{"--npn-list=spdy/3.1"}, t, noopHandler) + defer st.Close() + + // we set content-length: 1024, but the actual request body is + // 3 bytes. + res, err := st.spdy(requestParam{ + name: "TestS3H1BadRequestCL", + method: "POST", + header: []hpack.HeaderField{ + pair("content-length", "1024"), + }, + body: []byte("foo"), + }) + if err != nil { + t.Fatalf("Error st.spdy() = %v", err) + } + + want := spdy.ProtocolError + if got := res.spdyRstErrCode; got != want { + t.Errorf("res.spdyRstErrCode = %v; want %v", got, want) + } +} + +// TestS3H1MultipleRequestCL tests that server rejects request with +// multiple Content-Length request header fields. +func TestS3H1MultipleRequestCL(t *testing.T) { + st := newServerTesterTLS([]string{"--npn-list=spdy/3.1"}, t, func(w http.ResponseWriter, r *http.Request) { + t.Errorf("server should not forward bad request") + }) + defer st.Close() + + res, err := st.spdy(requestParam{ + name: "TestS3H1MultipleRequestCL", + header: []hpack.HeaderField{ + pair("content-length", "1"), + pair("content-length", "1"), + }, + }) + if err != nil { + t.Fatalf("Error st.spdy() = %v", err) + } + want := 400 + if got := res.status; got != want { + t.Errorf("status: %v; want %v", got, want) + } +} + +// TestS3H1InvalidRequestCL tests that server rejects request with +// Content-Length which cannot be parsed as a number. +func TestS3H1InvalidRequestCL(t *testing.T) { + st := newServerTesterTLS([]string{"--npn-list=spdy/3.1"}, t, func(w http.ResponseWriter, r *http.Request) { + t.Errorf("server should not forward bad request") + }) + defer st.Close() + + res, err := st.spdy(requestParam{ + name: "TestS3H1InvalidRequestCL", + header: []hpack.HeaderField{ + pair("content-length", ""), + }, + }) + if err != nil { + t.Fatalf("Error st.spdy() = %v", err) + } + want := 400 + if got := res.status; got != want { + t.Errorf("status: %v; want %v", got, want) + } +} + +// TestS3H1GenerateVia tests that server generates Via header field to and +// from backend server. +func TestS3H1GenerateVia(t *testing.T) { + st := newServerTesterTLS([]string{"--npn-list=spdy/3.1"}, t, func(w http.ResponseWriter, r *http.Request) { + if got, want := r.Header.Get("Via"), "1.1 nghttpx"; got != want { + t.Errorf("Via: %v; want %v", got, want) + } + }) + defer st.Close() + + res, err := st.spdy(requestParam{ + name: "TestS3H1GenerateVia", + }) + if err != nil { + t.Fatalf("Error st.spdy() = %v", err) + } + if got, want := res.header.Get("Via"), "1.1 nghttpx"; got != want { + t.Errorf("Via: %v; want %v", got, want) + } +} + +// TestS3H1AppendVia tests that server adds value to existing Via +// header field to and from backend server. +func TestS3H1AppendVia(t *testing.T) { + st := newServerTesterTLS([]string{"--npn-list=spdy/3.1"}, t, func(w http.ResponseWriter, r *http.Request) { + if got, want := r.Header.Get("Via"), "foo, 1.1 nghttpx"; got != want { + t.Errorf("Via: %v; want %v", got, want) + } + w.Header().Add("Via", "bar") + }) + defer st.Close() + + res, err := st.spdy(requestParam{ + name: "TestS3H1AppendVia", + header: []hpack.HeaderField{ + pair("via", "foo"), + }, + }) + if err != nil { + t.Fatalf("Error st.spdy() = %v", err) + } + if got, want := res.header.Get("Via"), "bar, 1.1 nghttpx"; got != want { + t.Errorf("Via: %v; want %v", got, want) + } +} + +// TestS3H1NoVia tests that server does not add value to existing Via +// header field to and from backend server. +func TestS3H1NoVia(t *testing.T) { + st := newServerTesterTLS([]string{"--npn-list=spdy/3.1", "--no-via"}, t, func(w http.ResponseWriter, r *http.Request) { + if got, want := r.Header.Get("Via"), "foo"; got != want { + t.Errorf("Via: %v; want %v", got, want) + } + w.Header().Add("Via", "bar") + }) + defer st.Close() + + res, err := st.spdy(requestParam{ + name: "TestS3H1NoVia", + header: []hpack.HeaderField{ + pair("via", "foo"), + }, + }) + if err != nil { + t.Fatalf("Error st.spdy() = %v", err) + } + if got, want := res.header.Get("Via"), "bar"; got != want { + t.Errorf("Via: %v; want %v", got, want) + } +} + +// TestS3H1HeaderFieldBuffer tests that request with header fields +// larger than configured buffer size is rejected. +func TestS3H1HeaderFieldBuffer(t *testing.T) { + st := newServerTesterTLS([]string{"--npn-list=spdy/3.1", "--header-field-buffer=10"}, t, func(w http.ResponseWriter, r *http.Request) { + t.Fatal("execution path should not be here") + }) + defer st.Close() + + res, err := st.spdy(requestParam{ + name: "TestS3H1HeaderFieldBuffer", + }) + if err != nil { + t.Fatalf("Error st.spdy() = %v", err) + } + if got, want := res.spdyRstErrCode, spdy.InternalError; got != want { + t.Errorf("res.spdyRstErrCode: %v; want %v", got, want) + } +} + +// TestS3H1HeaderFields tests that request with header fields more +// than configured number is rejected. +func TestS3H1HeaderFields(t *testing.T) { + st := newServerTesterTLS([]string{"--npn-list=spdy/3.1", "--max-header-fields=1"}, t, func(w http.ResponseWriter, r *http.Request) { + t.Fatal("execution path should not be here") + }) + defer st.Close() + + res, err := st.spdy(requestParam{ + name: "TestS3H1HeaderFields", + // we have at least 5 pseudo-header fields sent, and + // that ensures that buffer limit exceeds. + }) + if err != nil { + t.Fatalf("Error st.spdy() = %v", err) + } + if got, want := res.spdyRstErrCode, spdy.InternalError; got != want { + t.Errorf("res.spdyRstErrCode: %v; want %v", got, want) + } +} + +// TestS3H2ConnectFailure tests that server handles the situation that +// connection attempt to HTTP/2 backend failed. +func TestS3H2ConnectFailure(t *testing.T) { + st := newServerTesterTLS([]string{"--npn-list=spdy/3.1", "--http2-bridge"}, t, noopHandler) + defer st.Close() + + // simulate backend connect attempt failure + st.ts.Close() + + res, err := st.spdy(requestParam{ + name: "TestS3H2ConnectFailure", + }) + if err != nil { + t.Fatalf("Error st.spdy() = %v", err) + } + want := 503 + if got := res.status; got != want { + t.Errorf("status: %v; want %v", got, want) + } +} diff --git a/integration-tests/server.crt b/integration-tests/server.crt new file mode 100644 index 0000000..c50fdaa --- /dev/null +++ b/integration-tests/server.crt @@ -0,0 +1,21 @@ +-----BEGIN CERTIFICATE----- +MIIDhTCCAm2gAwIBAgIJAOvIx8xIxgyOMA0GCSqGSIb3DQEBCwUAMFkxCzAJBgNV +BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX +aWRnaXRzIFB0eSBMdGQxEjAQBgNVBAMMCTEyNy4wLjAuMTAeFw0xNTAxMjMxMjI0 +MjdaFw0yNTAxMjAxMjI0MjdaMFkxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21l +LVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQxEjAQBgNV +BAMMCTEyNy4wLjAuMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMuI +QZRI/iBaxPTjTWGemt8tCEfzZWxuIW3hY/gIhwJDfH2SbourBh1s9vqcqhBq5vmo +kdfVQXAnNLjIG1uhWmcHuNnKrE5hU82N6i9RsmuM5TQRvhsamHri4G+EXJMu9GqF +Mso8g7MWpRSGKf+8gfjAVNwfCHFiu8oBcMmy3l54MFHgRLSveAMhiPB0e3Xlnpr5 +2bS/oGTx5ynwPgBpEn2FrpT4Z/aLCLzJ/ysgNH8BXEh7n/v7xM3vd5grqB039rd5 +JoxlWvp+4XpzKp5upaqmOcVUq4pDSFUQ3w6C+v33Z3OK6Qaon7GMxLv3Us3b7PZ3 +1CLoWJR2o3OSnUfO/gUCAwEAAaNQME4wHQYDVR0OBBYEFLc5JWPUUVx4GJesogMV +w2Rz0L3yMB8GA1UdIwQYMBaAFLc5JWPUUVx4GJesogMVw2Rz0L3yMAwGA1UdEwQF +MAMBAf8wDQYJKoZIhvcNAQELBQADggEBAAP/cJWpM+GEjmVYHFacKTdbXBMox2Xn +QY2NLm00WPOGvKnO7czMFfX/pEmiq71kD45rLLfbaJP205QpxqiAIvhFhuq50Co7 +sTDtwcDTPLX9H7Ugjt4sTMPiwC14uVXFfoT/J46zMjXwP00qKyfszc2tkIgHfrTl +h4M1hkdfmMximir/Ii7TdYYJ3oGS8tdcYb6D4DZwAljKmxF6iUOwFCUgpTmqDBT5 +irXY8D27DzuNN5Pg07rwAlwXLCzrJE10UtO4MmRVXwpzmoaRQD4/tna6bZzdetvs +gPdGP6W1o0q85gullieMJWeKyQA/wasoE7fypn4pHAdTZm/vH+v7GHg= +-----END CERTIFICATE----- diff --git a/integration-tests/server.key b/integration-tests/server.key new file mode 100644 index 0000000..0fc02bb --- /dev/null +++ b/integration-tests/server.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDLiEGUSP4gWsT0 +401hnprfLQhH82VsbiFt4WP4CIcCQ3x9km6LqwYdbPb6nKoQaub5qJHX1UFwJzS4 +yBtboVpnB7jZyqxOYVPNjeovUbJrjOU0Eb4bGph64uBvhFyTLvRqhTLKPIOzFqUU +hin/vIH4wFTcHwhxYrvKAXDJst5eeDBR4ES0r3gDIYjwdHt15Z6a+dm0v6Bk8ecp +8D4AaRJ9ha6U+Gf2iwi8yf8rIDR/AVxIe5/7+8TN73eYK6gdN/a3eSaMZVr6fuF6 +cyqebqWqpjnFVKuKQ0hVEN8Ogvr992dziukGqJ+xjMS791LN2+z2d9Qi6FiUdqNz +kp1Hzv4FAgMBAAECggEACG26GYP0Ui6wHVwUZkiFLVzWDPS9bIIbDEvbMfhYbvWQ +gDrCLTKF7E4I5FP8jvV+XzRl5cRFE3nsKwLObzr9XWrqcsp73DsXl1mbKx58/ws0 +qrVZZBHz4pLmrHeUxduZ75dYhRuAcLgtWe48awTJdR2x5fO7C8cE89afbxrjLpJE +tVyiw6vVB0GfWTZodxtAFMTX1KVm4bTngXfg0NF1FBNHAX3Cm6t4YCE41hKSc0IQ +Jr3C4e9uj8poze1B17k79bGB8HNMbbc8Ws0sdbxi5xnY+HUA/mYQrmGXo8sdqiYC +EYCMqPm3iJrCmmpHukGf2Vt9k1aLlJ+lxOclSwFO+QKBgQDoRmoprfdmU20LyxYH +eVeVqggqmhNohwnuhIvOAyrWGUkbDsssqx2Vv82z0WHAAkwEvQ984UzaYWCCL3m3 ++JzpF2dz6aKhXIaYnXBlk3STMGUCDT5ysPvsin9z/unzkffh3vrbDBARGFYWG18x +eUyTDOVVeTZNHUJXGjRyiftCkwKBgQDgUkR6dHU4ciSt7Y0UkyAgtZ7POR41T05L +bcxbjJeqm6qlj+oP9WUk7JxeSEFUbrMiROABLPPqTwmGo4xrDRx/e7WrqN6QBKC+ +Y8CfalrKRb0np60x7Mxx0kbmHp5cwv9QDKznKViOYSgKxFrOFZyMAEXQdZ3FvjXF +OQWrw86kBwKBgQDXuxa9MWO3uUJtkqkaNfw/+FVvY/0kt09lJdxHci+l/IQmyl2w +Vhm7TRK7sXvtfvSl7gblgMgFiC2/nGKbmR/7ag5e3R98aVhlhMywuvyp/GfEORLI +KVNChfwMezVFUUx+j8BEFHcTuZuzGqcWZ0fUyER0V4k0pDlKdv9BZqBkWwKBgCdP +o3qGQCilMDJex/OMGPxCd9M+4kFbZZAobMC6cbXPU+dxwgYL7i67XGfVZ8WBJNlj +kpICK7irIzM6JBh6krzwlBTCIkbA2N6kopQNUl3SPOTfKKXwJp/nxs77HKuK7K09 +m2tjPoatFhRU9sjY1rdeMN3oTr7hp5CpfonsZaEvAoGAEPsZcDd4N9ap5bgaeDy9 +NOfLsIyaxT5k6moRIiy83QPihvCuECP16+r6M5tiSfgt/PtCimdjhRiqXzIHNRhh +Nfsv13vUtZgt8cYXuTdI4a8feKI7Q4876ME8Qp3WM5/UNZWq6/sWCuZFqbXUhqM0 +mwNEi5Zddzf8VsSL2gCraQg= +-----END PRIVATE KEY----- diff --git a/integration-tests/server_tester.go b/integration-tests/server_tester.go new file mode 100644 index 0000000..87e593b --- /dev/null +++ b/integration-tests/server_tester.go @@ -0,0 +1,683 @@ +package nghttp2 + +import ( + "bufio" + "bytes" + "crypto/tls" + "errors" + "fmt" + "github.com/bradfitz/http2" + "github.com/bradfitz/http2/hpack" + "github.com/tatsuhiro-t/go-nghttp2" + "golang.org/x/net/spdy" + "io" + "io/ioutil" + "net" + "net/http" + "net/http/httptest" + "net/url" + "os/exec" + "sort" + "strconv" + "strings" + "testing" + "time" +) + +const ( + serverBin = buildDir + "/src/nghttpx" + serverPort = 3009 + testDir = buildDir + "/integration-tests" +) + +func pair(name, value string) hpack.HeaderField { + return hpack.HeaderField{ + Name: name, + Value: value, + } +} + +type serverTester struct { + args []string // command-line arguments + cmd *exec.Cmd // test frontend server process, which is test subject + url string // test frontend server URL + t *testing.T + ts *httptest.Server // backend server + frontendHost string // frontend server host + backendHost string // backend server host + conn net.Conn // connection to frontend server + h2PrefaceSent bool // HTTP/2 preface was sent in conn + nextStreamID uint32 // next stream ID + fr *http2.Framer // HTTP/2 framer + spdyFr *spdy.Framer // SPDY/3.1 framer + headerBlkBuf bytes.Buffer // buffer to store encoded header block + enc *hpack.Encoder // HTTP/2 HPACK encoder + header http.Header // received header fields + dec *hpack.Decoder // HTTP/2 HPACK decoder + authority string // server's host:port + frCh chan http2.Frame // used for incoming HTTP/2 frame + spdyFrCh chan spdy.Frame // used for incoming SPDY frame + errCh chan error +} + +// newServerTester creates test context for plain TCP frontend +// connection. +func newServerTester(args []string, t *testing.T, handler http.HandlerFunc) *serverTester { + return newServerTesterInternal(args, t, handler, false, nil) +} + +// newServerTester creates test context for TLS frontend connection. +func newServerTesterTLS(args []string, t *testing.T, handler http.HandlerFunc) *serverTester { + return newServerTesterInternal(args, t, handler, true, nil) +} + +// newServerTester creates test context for TLS frontend connection +// with given clientConfig +func newServerTesterTLSConfig(args []string, t *testing.T, handler http.HandlerFunc, clientConfig *tls.Config) *serverTester { + return newServerTesterInternal(args, t, handler, true, clientConfig) +} + +// newServerTesterInternal creates test context. If frontendTLS is +// true, set up TLS frontend connection. +func newServerTesterInternal(args []string, t *testing.T, handler http.HandlerFunc, frontendTLS bool, clientConfig *tls.Config) *serverTester { + ts := httptest.NewUnstartedServer(handler) + + backendTLS := false + for _, k := range args { + switch k { + case "--http2-bridge": + backendTLS = true + } + } + if backendTLS { + nghttp2.ConfigureServer(ts.Config, &nghttp2.Server{}) + // According to httptest/server.go, we have to set + // NextProtos separately for ts.TLS. NextProtos set + // in nghttp2.ConfigureServer is effectively ignored. + ts.TLS = new(tls.Config) + ts.TLS.NextProtos = append(ts.TLS.NextProtos, "h2-14") + ts.StartTLS() + args = append(args, "-k") + } else { + ts.Start() + } + scheme := "http" + if frontendTLS { + scheme = "https" + args = append(args, testDir+"/server.key", testDir+"/server.crt") + } else { + args = append(args, "--frontend-no-tls") + } + + backendURL, err := url.Parse(ts.URL) + if err != nil { + t.Fatalf("Error parsing URL from httptest.Server: %v", err) + } + + // URL.Host looks like "127.0.0.1:8080", but we want + // "127.0.0.1,8080" + b := "-b" + strings.Replace(backendURL.Host, ":", ",", -1) + args = append(args, fmt.Sprintf("-f127.0.0.1,%v", serverPort), b, + "--errorlog-file="+testDir+"/log.txt", "-LINFO") + + authority := fmt.Sprintf("127.0.0.1:%v", serverPort) + + st := &serverTester{ + cmd: exec.Command(serverBin, args...), + t: t, + ts: ts, + url: fmt.Sprintf("%v://%v", scheme, authority), + frontendHost: fmt.Sprintf("127.0.0.1:%v", serverPort), + backendHost: backendURL.Host, + nextStreamID: 1, + authority: authority, + frCh: make(chan http2.Frame), + spdyFrCh: make(chan spdy.Frame), + errCh: make(chan error), + } + + if err := st.cmd.Start(); err != nil { + st.t.Fatalf("Error starting %v: %v", serverBin, err) + } + + retry := 0 + for { + var conn net.Conn + var err error + if frontendTLS { + var tlsConfig *tls.Config + if clientConfig == nil { + tlsConfig = new(tls.Config) + } else { + tlsConfig = clientConfig + } + tlsConfig.InsecureSkipVerify = true + tlsConfig.NextProtos = []string{"h2-14", "spdy/3.1"} + conn, err = tls.Dial("tcp", authority, tlsConfig) + } else { + conn, err = net.Dial("tcp", authority) + } + if err != nil { + retry += 1 + if retry >= 100 { + st.Close() + st.t.Fatalf("Error server is not responding too long; server command-line arguments may be invalid") + } + time.Sleep(150 * time.Millisecond) + continue + } + if frontendTLS { + tlsConn := conn.(*tls.Conn) + cs := tlsConn.ConnectionState() + if !cs.NegotiatedProtocolIsMutual { + st.Close() + st.t.Fatalf("Error negotiated next protocol is not mutual") + } + } + st.conn = conn + break + } + + st.fr = http2.NewFramer(st.conn, st.conn) + spdyFr, err := spdy.NewFramer(st.conn, st.conn) + if err != nil { + st.Close() + st.t.Fatalf("Error spdy.NewFramer: %v", err) + } + st.spdyFr = spdyFr + st.enc = hpack.NewEncoder(&st.headerBlkBuf) + st.dec = hpack.NewDecoder(4096, func(f hpack.HeaderField) { + st.header.Add(f.Name, f.Value) + }) + + return st +} + +func (st *serverTester) Close() { + if st.conn != nil { + st.conn.Close() + } + if st.cmd != nil { + st.cmd.Process.Kill() + st.cmd.Wait() + } + if st.ts != nil { + st.ts.Close() + } +} + +func (st *serverTester) readFrame() (http2.Frame, error) { + go func() { + f, err := st.fr.ReadFrame() + if err != nil { + st.errCh <- err + return + } + st.frCh <- f + }() + + select { + case f := <-st.frCh: + return f, nil + case err := <-st.errCh: + return nil, err + case <-time.After(5 * time.Second): + return nil, errors.New("timeout waiting for frame") + } +} + +func (st *serverTester) readSpdyFrame() (spdy.Frame, error) { + go func() { + f, err := st.spdyFr.ReadFrame() + if err != nil { + st.errCh <- err + return + } + st.spdyFrCh <- f + }() + + select { + case f := <-st.spdyFrCh: + return f, nil + case err := <-st.errCh: + return nil, err + case <-time.After(2 * time.Second): + return nil, errors.New("timeout waiting for frame") + } +} + +type requestParam struct { + name string // name for this request to identify the request in log easily + streamID uint32 // stream ID, automatically assigned if 0 + method string // method, defaults to GET + scheme string // scheme, defaults to http + authority string // authority, defaults to backend server address + path string // path, defaults to / + header []hpack.HeaderField // additional request header fields + body []byte // request body + trailer []hpack.HeaderField // trailer part + httpUpgrade bool // true if upgraded to HTTP/2 through HTTP Upgrade +} + +// wrapper for request body to set trailer part +type chunkedBodyReader struct { + trailer []hpack.HeaderField + trailerWritten bool + body io.Reader + req *http.Request +} + +func (cbr *chunkedBodyReader) Read(p []byte) (n int, err error) { + // document says that we have to set http.Request.Trailer + // after request was sent and before body returns EOF. + if !cbr.trailerWritten { + cbr.trailerWritten = true + for _, h := range cbr.trailer { + cbr.req.Trailer.Set(h.Name, h.Value) + } + } + return cbr.body.Read(p) +} + +func (st *serverTester) http1(rp requestParam) (*serverResponse, error) { + method := "GET" + if rp.method != "" { + method = rp.method + } + + var body io.Reader + var cbr *chunkedBodyReader + if rp.body != nil { + body = bytes.NewBuffer(rp.body) + if len(rp.trailer) != 0 { + cbr = &chunkedBodyReader{ + trailer: rp.trailer, + body: body, + } + body = cbr + } + } + + reqURL := st.url + + if rp.path != "" { + u, err := url.Parse(st.url) + if err != nil { + st.t.Fatalf("Error parsing URL from st.url %v: %v", st.url, err) + } + u.Path = rp.path + reqURL = u.String() + } + + req, err := http.NewRequest(method, reqURL, body) + if err != nil { + return nil, err + } + for _, h := range rp.header { + req.Header.Add(h.Name, h.Value) + } + req.Header.Add("Test-Case", rp.name) + if cbr != nil { + cbr.req = req + // this makes request use chunked encoding + req.ContentLength = -1 + req.Trailer = make(http.Header) + for _, h := range cbr.trailer { + req.Trailer.Set(h.Name, "") + } + } + if err := req.Write(st.conn); err != nil { + return nil, err + } + resp, err := http.ReadResponse(bufio.NewReader(st.conn), req) + if err != nil { + return nil, err + } + respBody, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, err + } + resp.Body.Close() + + res := &serverResponse{ + status: resp.StatusCode, + header: resp.Header, + body: respBody, + connClose: resp.Close, + } + + return res, nil +} + +func (st *serverTester) spdy(rp requestParam) (*serverResponse, error) { + res := &serverResponse{} + + var id spdy.StreamId + if rp.streamID != 0 { + id = spdy.StreamId(rp.streamID) + if id >= spdy.StreamId(st.nextStreamID) && id%2 == 1 { + st.nextStreamID = uint32(id) + 2 + } + } else { + id = spdy.StreamId(st.nextStreamID) + st.nextStreamID += 2 + } + + method := "GET" + if rp.method != "" { + method = rp.method + } + + scheme := "http" + if rp.scheme != "" { + scheme = rp.scheme + } + + host := st.authority + if rp.authority != "" { + host = rp.authority + } + + path := "/" + if rp.path != "" { + path = rp.path + } + + header := make(http.Header) + header.Add(":method", method) + header.Add(":scheme", scheme) + header.Add(":host", host) + header.Add(":path", path) + header.Add(":version", "HTTP/1.1") + header.Add("test-case", rp.name) + for _, h := range rp.header { + header.Add(h.Name, h.Value) + } + + var synStreamFlags spdy.ControlFlags + if len(rp.body) == 0 { + synStreamFlags = spdy.ControlFlagFin + } + if err := st.spdyFr.WriteFrame(&spdy.SynStreamFrame{ + CFHeader: spdy.ControlFrameHeader{ + Flags: synStreamFlags, + }, + StreamId: id, + Headers: header, + }); err != nil { + return nil, err + } + + if len(rp.body) != 0 { + if err := st.spdyFr.WriteFrame(&spdy.DataFrame{ + StreamId: id, + Flags: spdy.DataFlagFin, + Data: rp.body, + }); err != nil { + return nil, err + } + } + +loop: + for { + fr, err := st.readSpdyFrame() + if err != nil { + return res, err + } + switch f := fr.(type) { + case *spdy.SynReplyFrame: + if f.StreamId != id { + break + } + res.header = cloneHeader(f.Headers) + if _, err := fmt.Sscan(res.header.Get(":status"), &res.status); err != nil { + return res, fmt.Errorf("Error parsing status code: %v", err) + } + if f.CFHeader.Flags&spdy.ControlFlagFin != 0 { + break loop + } + case *spdy.DataFrame: + if f.StreamId != id { + break + } + res.body = append(res.body, f.Data...) + if f.Flags&spdy.DataFlagFin != 0 { + break loop + } + case *spdy.RstStreamFrame: + if f.StreamId != id { + break + } + res.spdyRstErrCode = f.Status + break loop + case *spdy.GoAwayFrame: + if f.Status == spdy.GoAwayOK { + break + } + res.spdyGoAwayErrCode = f.Status + break loop + } + } + return res, nil +} + +func (st *serverTester) http2(rp requestParam) (*serverResponse, error) { + st.headerBlkBuf.Reset() + st.header = make(http.Header) + + var id uint32 + if rp.streamID != 0 { + id = rp.streamID + if id >= st.nextStreamID && id%2 == 1 { + st.nextStreamID = id + 2 + } + } else { + id = st.nextStreamID + st.nextStreamID += 2 + } + + if !st.h2PrefaceSent { + st.h2PrefaceSent = true + fmt.Fprint(st.conn, "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n") + if err := st.fr.WriteSettings(); err != nil { + return nil, err + } + } + + res := &serverResponse{ + streamID: id, + } + + streams := make(map[uint32]*serverResponse) + streams[id] = res + + if !rp.httpUpgrade { + method := "GET" + if rp.method != "" { + method = rp.method + } + _ = st.enc.WriteField(pair(":method", method)) + + scheme := "http" + if rp.scheme != "" { + scheme = rp.scheme + } + _ = st.enc.WriteField(pair(":scheme", scheme)) + + authority := st.authority + if rp.authority != "" { + authority = rp.authority + } + _ = st.enc.WriteField(pair(":authority", authority)) + + path := "/" + if rp.path != "" { + path = rp.path + } + _ = st.enc.WriteField(pair(":path", path)) + + _ = st.enc.WriteField(pair("test-case", rp.name)) + + for _, h := range rp.header { + _ = st.enc.WriteField(h) + } + + err := st.fr.WriteHeaders(http2.HeadersFrameParam{ + StreamID: id, + EndStream: len(rp.body) == 0 && len(rp.trailer) == 0, + EndHeaders: true, + BlockFragment: st.headerBlkBuf.Bytes(), + }) + if err != nil { + return nil, err + } + + if len(rp.body) != 0 { + // TODO we assume rp.body fits in 1 frame + if err := st.fr.WriteData(id, len(rp.trailer) == 0, rp.body); err != nil { + return nil, err + } + } + + if len(rp.trailer) != 0 { + st.headerBlkBuf.Reset() + for _, h := range rp.trailer { + _ = st.enc.WriteField(h) + } + err := st.fr.WriteHeaders(http2.HeadersFrameParam{ + StreamID: id, + EndStream: true, + EndHeaders: true, + BlockFragment: st.headerBlkBuf.Bytes(), + }) + if err != nil { + return nil, err + } + } + } +loop: + for { + fr, err := st.readFrame() + if err != nil { + return res, err + } + switch f := fr.(type) { + case *http2.HeadersFrame: + _, err := st.dec.Write(f.HeaderBlockFragment()) + if err != nil { + return res, err + } + sr, ok := streams[f.FrameHeader.StreamID] + if !ok { + st.header = make(http.Header) + break + } + sr.header = cloneHeader(st.header) + var status int + status, err = strconv.Atoi(sr.header.Get(":status")) + if err != nil { + return res, fmt.Errorf("Error parsing status code: %v", err) + } + sr.status = status + if f.StreamEnded() { + if streamEnded(res, streams, sr) { + break loop + } + } + case *http2.PushPromiseFrame: + _, err := st.dec.Write(f.HeaderBlockFragment()) + if err != nil { + return res, err + } + sr := &serverResponse{ + streamID: f.PromiseID, + reqHeader: cloneHeader(st.header), + } + streams[sr.streamID] = sr + case *http2.DataFrame: + sr, ok := streams[f.FrameHeader.StreamID] + if !ok { + break + } + sr.body = append(sr.body, f.Data()...) + if f.StreamEnded() { + if streamEnded(res, streams, sr) { + break loop + } + } + case *http2.RSTStreamFrame: + sr, ok := streams[f.FrameHeader.StreamID] + if !ok { + break + } + sr.errCode = f.ErrCode + if streamEnded(res, streams, sr) { + break loop + } + case *http2.GoAwayFrame: + if f.ErrCode == http2.ErrCodeNo { + break + } + res.errCode = f.ErrCode + res.connErr = true + break loop + case *http2.SettingsFrame: + if f.IsAck() { + break + } + if err := st.fr.WriteSettingsAck(); err != nil { + return res, err + } + } + } + sort.Sort(ByStreamID(res.pushResponse)) + return res, nil +} + +func streamEnded(mainSr *serverResponse, streams map[uint32]*serverResponse, sr *serverResponse) bool { + delete(streams, sr.streamID) + if mainSr.streamID != sr.streamID { + mainSr.pushResponse = append(mainSr.pushResponse, sr) + } + return len(streams) == 0 +} + +type serverResponse struct { + status int // HTTP status code + header http.Header // response header fields + body []byte // response body + streamID uint32 // stream ID in HTTP/2 + errCode http2.ErrCode // error code received in HTTP/2 RST_STREAM or GOAWAY + connErr bool // true if HTTP/2 connection error + spdyGoAwayErrCode spdy.GoAwayStatus // status code received in SPDY RST_STREAM + spdyRstErrCode spdy.RstStreamStatus // status code received in SPDY GOAWAY + connClose bool // Conection: close is included in response header in HTTP/1 test + reqHeader http.Header // http request header, currently only sotres pushed request header + pushResponse []*serverResponse // pushed response +} + +type ByStreamID []*serverResponse + +func (b ByStreamID) Len() int { + return len(b) +} + +func (b ByStreamID) Swap(i, j int) { + b[i], b[j] = b[j], b[i] +} + +func (b ByStreamID) Less(i, j int) bool { + return b[i].streamID < b[j].streamID +} + +func cloneHeader(h http.Header) http.Header { + h2 := make(http.Header, len(h)) + for k, vv := range h { + vv2 := make([]string, len(vv)) + copy(vv2, vv) + h2[k] = vv2 + } + return h2 +} + +func noopHandler(w http.ResponseWriter, r *http.Request) {} diff --git a/integration-tests/setenv b/integration-tests/setenv new file mode 100644 index 0000000..56879b9 --- /dev/null +++ b/integration-tests/setenv @@ -0,0 +1,6 @@ +#!/bin/sh -e + +export CGO_CFLAGS="-I/mnt/nghttp2/lib/includes -I/mnt/nghttp2/lib/includes" +export CGO_LDFLAGS="-L/mnt/nghttp2/lib/.libs" +export LD_LIBRARY_PATH="/mnt/nghttp2/lib/.libs" +"$@" diff --git a/integration-tests/setenv.in b/integration-tests/setenv.in new file mode 100644 index 0000000..2c2d3c9 --- /dev/null +++ b/integration-tests/setenv.in @@ -0,0 +1,6 @@ +#!/bin/sh -e + +export CGO_CFLAGS="-I@abs_top_srcdir@/lib/includes -I@abs_top_builddir@/lib/includes" +export CGO_LDFLAGS="-L@abs_top_builddir@/lib/.libs" +export LD_LIBRARY_PATH="@abs_top_builddir@/lib/.libs" +"$@" diff --git a/lib/Makefile.am b/lib/Makefile.am new file mode 100644 index 0000000..6d2393e --- /dev/null +++ b/lib/Makefile.am @@ -0,0 +1,67 @@ +# nghttp2 - HTTP/2 C Library + +# Copyright (c) 2012, 2013 Tatsuhiro Tsujikawa + +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: + +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. + +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +SUBDIRS = includes + +EXTRA_DIST = Makefile.msvc + +AM_CFLAGS = $(WARNCFLAGS) +AM_CPPFLAGS = -I$(srcdir)/includes -I$(builddir)/includes @DEFS@ + +pkgconfigdir = $(libdir)/pkgconfig +pkgconfig_DATA = libnghttp2.pc +DISTCLEANFILES = $(pkgconfig_DATA) + +lib_LTLIBRARIES = libnghttp2.la + +OBJECTS = nghttp2_pq.c nghttp2_map.c nghttp2_queue.c \ + nghttp2_frame.c \ + nghttp2_buf.c \ + nghttp2_stream.c nghttp2_outbound_item.c \ + nghttp2_session.c nghttp2_submit.c \ + nghttp2_helper.c \ + nghttp2_npn.c \ + nghttp2_hd.c nghttp2_hd_huffman.c nghttp2_hd_huffman_data.c \ + nghttp2_version.c \ + nghttp2_priority_spec.c \ + nghttp2_option.c \ + nghttp2_callbacks.c \ + nghttp2_mem.c \ + nghttp2_http.c + +HFILES = nghttp2_pq.h nghttp2_int.h nghttp2_map.h nghttp2_queue.h \ + nghttp2_frame.h \ + nghttp2_buf.h \ + nghttp2_session.h nghttp2_helper.h nghttp2_stream.h nghttp2_int.h \ + nghttp2_npn.h \ + nghttp2_submit.h nghttp2_outbound_item.h \ + nghttp2_net.h \ + nghttp2_hd.h nghttp2_hd_huffman.h \ + nghttp2_priority_spec.h \ + nghttp2_option.h \ + nghttp2_callbacks.h \ + nghttp2_mem.h \ + nghttp2_http.h + +libnghttp2_la_SOURCES = $(HFILES) $(OBJECTS) +libnghttp2_la_LDFLAGS = -no-undefined \ + -version-info $(LT_CURRENT):$(LT_REVISION):$(LT_AGE) diff --git a/lib/Makefile.in b/lib/Makefile.in new file mode 100644 index 0000000..dd1cc37 --- /dev/null +++ b/lib/Makefile.in @@ -0,0 +1,930 @@ +# Makefile.in generated by automake 1.14.1 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2013 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +# nghttp2 - HTTP/2 C Library + +# Copyright (c) 2012, 2013 Tatsuhiro Tsujikawa + +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: + +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. + + +VPATH = @srcdir@ +am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)' +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +target_triplet = @target@ +subdir = lib +DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \ + $(srcdir)/libnghttp2.pc.in $(top_srcdir)/depcomp +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/ax_boost_asio.m4 \ + $(top_srcdir)/m4/ax_boost_base.m4 \ + $(top_srcdir)/m4/ax_boost_system.m4 \ + $(top_srcdir)/m4/ax_boost_thread.m4 \ + $(top_srcdir)/m4/ax_check_compile_flag.m4 \ + $(top_srcdir)/m4/ax_cxx_compile_stdcxx_11.m4 \ + $(top_srcdir)/m4/ax_have_epoll.m4 \ + $(top_srcdir)/m4/ax_python_devel.m4 \ + $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/libxml2.m4 \ + $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ + $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = libnghttp2.pc +CONFIG_CLEAN_VPATH_FILES = +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +am__uninstall_files_from_dir = { \ + test -z "$$files" \ + || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ + || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ + $(am__cd) "$$dir" && rm -f $$files; }; \ + } +am__installdirs = "$(DESTDIR)$(libdir)" "$(DESTDIR)$(pkgconfigdir)" +LTLIBRARIES = $(lib_LTLIBRARIES) +libnghttp2_la_LIBADD = +am__objects_1 = +am__objects_2 = nghttp2_pq.lo nghttp2_map.lo nghttp2_queue.lo \ + nghttp2_frame.lo nghttp2_buf.lo nghttp2_stream.lo \ + nghttp2_outbound_item.lo nghttp2_session.lo nghttp2_submit.lo \ + nghttp2_helper.lo nghttp2_npn.lo nghttp2_hd.lo \ + nghttp2_hd_huffman.lo nghttp2_hd_huffman_data.lo \ + nghttp2_version.lo nghttp2_priority_spec.lo nghttp2_option.lo \ + nghttp2_callbacks.lo nghttp2_mem.lo nghttp2_http.lo +am_libnghttp2_la_OBJECTS = $(am__objects_1) $(am__objects_2) +libnghttp2_la_OBJECTS = $(am_libnghttp2_la_OBJECTS) +AM_V_lt = $(am__v_lt_@AM_V@) +am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) +am__v_lt_0 = --silent +am__v_lt_1 = +libnghttp2_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(libnghttp2_la_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__depfiles_maybe = depfiles +am__mv = mv -f +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CFLAGS) $(CFLAGS) +AM_V_CC = $(am__v_CC_@AM_V@) +am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) +am__v_CC_0 = @echo " CC " $@; +am__v_CC_1 = +CCLD = $(CC) +LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CCLD = $(am__v_CCLD_@AM_V@) +am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) +am__v_CCLD_0 = @echo " CCLD " $@; +am__v_CCLD_1 = +SOURCES = $(libnghttp2_la_SOURCES) +DIST_SOURCES = $(libnghttp2_la_SOURCES) +RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \ + ctags-recursive dvi-recursive html-recursive info-recursive \ + install-data-recursive install-dvi-recursive \ + install-exec-recursive install-html-recursive \ + install-info-recursive install-pdf-recursive \ + install-ps-recursive install-recursive installcheck-recursive \ + installdirs-recursive pdf-recursive ps-recursive \ + tags-recursive uninstall-recursive +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +DATA = $(pkgconfig_DATA) +RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \ + distclean-recursive maintainer-clean-recursive +am__recursive_targets = \ + $(RECURSIVE_TARGETS) \ + $(RECURSIVE_CLEAN_TARGETS) \ + $(am__extra_recursive_targets) +AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \ + distdir +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +ETAGS = etags +CTAGS = ctags +DIST_SUBDIRS = $(SUBDIRS) +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +am__relativize = \ + dir0=`pwd`; \ + sed_first='s,^\([^/]*\)/.*$$,\1,'; \ + sed_rest='s,^[^/]*/*,,'; \ + sed_last='s,^.*/\([^/]*\)$$,\1,'; \ + sed_butlast='s,/*[^/]*$$,,'; \ + while test -n "$$dir1"; do \ + first=`echo "$$dir1" | sed -e "$$sed_first"`; \ + if test "$$first" != "."; then \ + if test "$$first" = ".."; then \ + dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \ + dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \ + else \ + first2=`echo "$$dir2" | sed -e "$$sed_first"`; \ + if test "$$first2" = "$$first"; then \ + dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \ + else \ + dir2="../$$dir2"; \ + fi; \ + dir0="$$dir0"/"$$first"; \ + fi; \ + fi; \ + dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \ + done; \ + reldir="$$dir2" +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +APPLDFLAGS = @APPLDFLAGS@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +BOOST_ASIO_LIB = @BOOST_ASIO_LIB@ +BOOST_CPPFLAGS = @BOOST_CPPFLAGS@ +BOOST_LDFLAGS = @BOOST_LDFLAGS@ +BOOST_SYSTEM_LIB = @BOOST_SYSTEM_LIB@ +BOOST_THREAD_LIB = @BOOST_THREAD_LIB@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CUNIT_CFLAGS = @CUNIT_CFLAGS@ +CUNIT_LIBS = @CUNIT_LIBS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +CYTHON = @CYTHON@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DLLTOOL = @DLLTOOL@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +GREP = @GREP@ +HAVE_CXX11 = @HAVE_CXX11@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +JANSSON_CFLAGS = @JANSSON_CFLAGS@ +JANSSON_LIBS = @JANSSON_LIBS@ +JEMALLOC_LIBS = @JEMALLOC_LIBS@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBEVENT_OPENSSL_CFLAGS = @LIBEVENT_OPENSSL_CFLAGS@ +LIBEVENT_OPENSSL_LIBS = @LIBEVENT_OPENSSL_LIBS@ +LIBEV_CFLAGS = @LIBEV_CFLAGS@ +LIBEV_LIBS = @LIBEV_LIBS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBSPDYLAY_CFLAGS = @LIBSPDYLAY_CFLAGS@ +LIBSPDYLAY_LIBS = @LIBSPDYLAY_LIBS@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +LT_AGE = @LT_AGE@ +LT_CURRENT = @LT_CURRENT@ +LT_REVISION = @LT_REVISION@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MKDIR_P = @MKDIR_P@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OPENSSL_CFLAGS = @OPENSSL_CFLAGS@ +OPENSSL_LIBS = @OPENSSL_LIBS@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PACKAGE_VERSION_NUM = @PACKAGE_VERSION_NUM@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PKG_CONFIG = @PKG_CONFIG@ +PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ +PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ +PYTHON = @PYTHON@ +PYTHON_CPPFLAGS = @PYTHON_CPPFLAGS@ +PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@ +PYTHON_EXTRA_LDFLAGS = @PYTHON_EXTRA_LDFLAGS@ +PYTHON_EXTRA_LIBS = @PYTHON_EXTRA_LIBS@ +PYTHON_LDFLAGS = @PYTHON_LDFLAGS@ +PYTHON_PLATFORM = @PYTHON_PLATFORM@ +PYTHON_PREFIX = @PYTHON_PREFIX@ +PYTHON_SITE_PKG = @PYTHON_SITE_PKG@ +PYTHON_VERSION = @PYTHON_VERSION@ +RANLIB = @RANLIB@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +TESTLDADD = @TESTLDADD@ +VERSION = @VERSION@ +WARNCFLAGS = @WARNCFLAGS@ +XML2_CONFIG = @XML2_CONFIG@ +XML_CPPFLAGS = @XML_CPPFLAGS@ +XML_LIBS = @XML_LIBS@ +ZLIB_CFLAGS = @ZLIB_CFLAGS@ +ZLIB_LIBS = @ZLIB_LIBS@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +pkgpyexecdir = @pkgpyexecdir@ +pkgpythondir = @pkgpythondir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +pyexecdir = @pyexecdir@ +pythondir = @pythondir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target = @target@ +target_alias = @target_alias@ +target_cpu = @target_cpu@ +target_os = @target_os@ +target_vendor = @target_vendor@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ + +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +SUBDIRS = includes +EXTRA_DIST = Makefile.msvc +AM_CFLAGS = $(WARNCFLAGS) +AM_CPPFLAGS = -I$(srcdir)/includes -I$(builddir)/includes @DEFS@ +pkgconfigdir = $(libdir)/pkgconfig +pkgconfig_DATA = libnghttp2.pc +DISTCLEANFILES = $(pkgconfig_DATA) +lib_LTLIBRARIES = libnghttp2.la +OBJECTS = nghttp2_pq.c nghttp2_map.c nghttp2_queue.c \ + nghttp2_frame.c \ + nghttp2_buf.c \ + nghttp2_stream.c nghttp2_outbound_item.c \ + nghttp2_session.c nghttp2_submit.c \ + nghttp2_helper.c \ + nghttp2_npn.c \ + nghttp2_hd.c nghttp2_hd_huffman.c nghttp2_hd_huffman_data.c \ + nghttp2_version.c \ + nghttp2_priority_spec.c \ + nghttp2_option.c \ + nghttp2_callbacks.c \ + nghttp2_mem.c \ + nghttp2_http.c + +HFILES = nghttp2_pq.h nghttp2_int.h nghttp2_map.h nghttp2_queue.h \ + nghttp2_frame.h \ + nghttp2_buf.h \ + nghttp2_session.h nghttp2_helper.h nghttp2_stream.h nghttp2_int.h \ + nghttp2_npn.h \ + nghttp2_submit.h nghttp2_outbound_item.h \ + nghttp2_net.h \ + nghttp2_hd.h nghttp2_hd_huffman.h \ + nghttp2_priority_spec.h \ + nghttp2_option.h \ + nghttp2_callbacks.h \ + nghttp2_mem.h \ + nghttp2_http.h + +libnghttp2_la_SOURCES = $(HFILES) $(OBJECTS) +libnghttp2_la_LDFLAGS = -no-undefined \ + -version-info $(LT_CURRENT):$(LT_REVISION):$(LT_AGE) + +all: all-recursive + +.SUFFIXES: +.SUFFIXES: .c .lo .o .obj +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu lib/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu lib/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): +libnghttp2.pc: $(top_builddir)/config.status $(srcdir)/libnghttp2.pc.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ + +install-libLTLIBRARIES: $(lib_LTLIBRARIES) + @$(NORMAL_INSTALL) + @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \ + list2=; for p in $$list; do \ + if test -f $$p; then \ + list2="$$list2 $$p"; \ + else :; fi; \ + done; \ + test -z "$$list2" || { \ + echo " $(MKDIR_P) '$(DESTDIR)$(libdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(libdir)" || exit 1; \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(libdir)'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(libdir)"; \ + } + +uninstall-libLTLIBRARIES: + @$(NORMAL_UNINSTALL) + @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \ + for p in $$list; do \ + $(am__strip_dir) \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$f'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$f"; \ + done + +clean-libLTLIBRARIES: + -test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES) + @list='$(lib_LTLIBRARIES)'; \ + locs=`for p in $$list; do echo $$p; done | \ + sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ + sort -u`; \ + test -z "$$locs" || { \ + echo rm -f $${locs}; \ + rm -f $${locs}; \ + } + +libnghttp2.la: $(libnghttp2_la_OBJECTS) $(libnghttp2_la_DEPENDENCIES) $(EXTRA_libnghttp2_la_DEPENDENCIES) + $(AM_V_CCLD)$(libnghttp2_la_LINK) -rpath $(libdir) $(libnghttp2_la_OBJECTS) $(libnghttp2_la_LIBADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_buf.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_callbacks.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_frame.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_hd.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_hd_huffman.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_hd_huffman_data.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_helper.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_http.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_map.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_mem.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_npn.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_option.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_outbound_item.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_pq.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_priority_spec.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_queue.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_session.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_stream.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_submit.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_version.Plo@am__quote@ + +.c.o: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\ +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< + +.c.obj: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\ +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.c.lo: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\ +@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs +install-pkgconfigDATA: $(pkgconfig_DATA) + @$(NORMAL_INSTALL) + @list='$(pkgconfig_DATA)'; test -n "$(pkgconfigdir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(pkgconfigdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(pkgconfigdir)" || exit 1; \ + fi; \ + for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + echo "$$d$$p"; \ + done | $(am__base_list) | \ + while read files; do \ + echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(pkgconfigdir)'"; \ + $(INSTALL_DATA) $$files "$(DESTDIR)$(pkgconfigdir)" || exit $$?; \ + done + +uninstall-pkgconfigDATA: + @$(NORMAL_UNINSTALL) + @list='$(pkgconfig_DATA)'; test -n "$(pkgconfigdir)" || list=; \ + files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ + dir='$(DESTDIR)$(pkgconfigdir)'; $(am__uninstall_files_from_dir) + +# This directory's subdirectories are mostly independent; you can cd +# into them and run 'make' without going through this Makefile. +# To change the values of 'make' variables: instead of editing Makefiles, +# (1) if the variable is set in 'config.status', edit 'config.status' +# (which will cause the Makefiles to be regenerated when you run 'make'); +# (2) otherwise, pass the desired values on the 'make' command line. +$(am__recursive_targets): + @fail=; \ + if $(am__make_keepgoing); then \ + failcom='fail=yes'; \ + else \ + failcom='exit 1'; \ + fi; \ + dot_seen=no; \ + target=`echo $@ | sed s/-recursive//`; \ + case "$@" in \ + distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ + *) list='$(SUBDIRS)' ;; \ + esac; \ + for subdir in $$list; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + dot_seen=yes; \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || eval $$failcom; \ + done; \ + if test "$$dot_seen" = "no"; then \ + $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ + fi; test -z "$$fail" + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-recursive +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \ + include_option=--etags-include; \ + empty_fix=.; \ + else \ + include_option=--include; \ + empty_fix=; \ + fi; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + test ! -f $$subdir/TAGS || \ + set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \ + fi; \ + done; \ + $(am__define_uniq_tagged_files); \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: ctags-recursive + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" +cscopelist: cscopelist-recursive + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done + @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + $(am__make_dryrun) \ + || test -d "$(distdir)/$$subdir" \ + || $(MKDIR_P) "$(distdir)/$$subdir" \ + || exit 1; \ + dir1=$$subdir; dir2="$(distdir)/$$subdir"; \ + $(am__relativize); \ + new_distdir=$$reldir; \ + dir1=$$subdir; dir2="$(top_distdir)"; \ + $(am__relativize); \ + new_top_distdir=$$reldir; \ + echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \ + echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \ + ($(am__cd) $$subdir && \ + $(MAKE) $(AM_MAKEFLAGS) \ + top_distdir="$$new_top_distdir" \ + distdir="$$new_distdir" \ + am__remove_distdir=: \ + am__skip_length_check=: \ + am__skip_mode_fix=: \ + distdir) \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-recursive +all-am: Makefile $(LTLIBRARIES) $(DATA) +installdirs: installdirs-recursive +installdirs-am: + for dir in "$(DESTDIR)$(libdir)" "$(DESTDIR)$(pkgconfigdir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: install-recursive +install-exec: install-exec-recursive +install-data: install-data-recursive +uninstall: uninstall-recursive + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-recursive +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + -test -z "$(DISTCLEANFILES)" || rm -f $(DISTCLEANFILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-recursive + +clean-am: clean-generic clean-libLTLIBRARIES clean-libtool \ + mostlyclean-am + +distclean: distclean-recursive + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-recursive + +dvi-am: + +html: html-recursive + +html-am: + +info: info-recursive + +info-am: + +install-data-am: install-pkgconfigDATA + +install-dvi: install-dvi-recursive + +install-dvi-am: + +install-exec-am: install-libLTLIBRARIES + +install-html: install-html-recursive + +install-html-am: + +install-info: install-info-recursive + +install-info-am: + +install-man: + +install-pdf: install-pdf-recursive + +install-pdf-am: + +install-ps: install-ps-recursive + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-recursive + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-recursive + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-recursive + +pdf-am: + +ps: ps-recursive + +ps-am: + +uninstall-am: uninstall-libLTLIBRARIES uninstall-pkgconfigDATA + +.MAKE: $(am__recursive_targets) install-am install-strip + +.PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am check \ + check-am clean clean-generic clean-libLTLIBRARIES \ + clean-libtool cscopelist-am ctags ctags-am distclean \ + distclean-compile distclean-generic distclean-libtool \ + distclean-tags distdir dvi dvi-am html html-am info info-am \ + install install-am install-data install-data-am install-dvi \ + install-dvi-am install-exec install-exec-am install-html \ + install-html-am install-info install-info-am \ + install-libLTLIBRARIES install-man install-pdf install-pdf-am \ + install-pkgconfigDATA install-ps install-ps-am install-strip \ + installcheck installcheck-am installdirs installdirs-am \ + maintainer-clean maintainer-clean-generic mostlyclean \ + mostlyclean-compile mostlyclean-generic mostlyclean-libtool \ + pdf pdf-am ps ps-am tags tags-am uninstall uninstall-am \ + uninstall-libLTLIBRARIES uninstall-pkgconfigDATA + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/lib/Makefile.msvc b/lib/Makefile.msvc new file mode 100644 index 0000000..edc6d74 --- /dev/null +++ b/lib/Makefile.msvc @@ -0,0 +1,249 @@ +# +# GNU Makefile for nghttp2 / MSVC. +# +# By G. Vanem 2013 +# Updated 3/2015 by Remo Eichenberger @remoe +# The MIT License apply. +# + +# +# Choose your weapons: +# Set 'USE_CYTHON=1' to build and install the 'nghttp2.pyd' Python extension. +# +USE_CYTHON = 1 + +_VERSION := $(shell grep AC_INIT ../configure.ac | cut -d'[' -f3 | sed -e 's/-DEV], //g') +_VERSION := $(subst ., ,$(_VERSION)) +VER_MAJOR = $(word 1,$(_VERSION)) +VER_MINOR = $(word 2,$(_VERSION)) +VER_MICRO = $(word 3,$(_VERSION)) +VERSION = $(VER_MAJOR).$(VER_MINOR).$(VER_MICRO) +VERSION_NUM = ($(VER_MAJOR) << 16) + ($(VER_MINOR) << 8) + $(VER_MICRO) + +GENERATED = 'Generated by $(realpath Makefile.MSVC)' + +# +# Where to copy nghttp2.dll + lib + headers to. +# Note: 'make install' is not in default targets. Do it explicitly. +# +VC_ROOT = $(realpath $(VCINSTALLDIR)) +INSTALL_BIN = $(VC_ROOT)/bin +INSTALL_LIB = $(VC_ROOT)/lib +INSTALL_HDR = $(VC_ROOT)/include + +# +# Build for DEBUG-model and RELEASE at the same time. +# +TARGETS = nghttp2.lib nghttp2.dll nghttp2_imp.lib \ + nghttp2d.lib nghttp2d.dll nghttp2d_imp.lib + +EXT_LIBS = + +OBJ_DIR = MSVC_obj + +NGHTTP2_PDB_R = $(OBJ_DIR)/nghttp2.pdb +NGHTTP2_PDB_D = $(OBJ_DIR)/nghttp2d.pdb + +CC = cl +CFLAGS = -I./includes -Dssize_t=long -D_U_="" + +CFLAGS_R = -nologo -MD -W3 -Zi -Fd./$(NGHTTP2_PDB_R) +CFLAGS_D = -nologo -MDd -W3 -Zi -Fd./$(NGHTTP2_PDB_D) \ + -Ot -D_DEBUG -GF -RTCs -RTCu # -RTCc -GS + +LDFLAGS = -nologo -machine:x64 -map -debug -incremental:no # -verbose + +NGHTTP2_SRC = nghttp2_pq.c \ + nghttp2_map.c \ + nghttp2_queue.c \ + nghttp2_frame.c \ + nghttp2_buf.c \ + nghttp2_stream.c \ + nghttp2_outbound_item.c \ + nghttp2_session.c \ + nghttp2_submit.c \ + nghttp2_helper.c \ + nghttp2_npn.c \ + nghttp2_hd.c \ + nghttp2_hd_huffman.c \ + nghttp2_hd_huffman_data.c \ + nghttp2_version.c \ + nghttp2_priority_spec.c \ + nghttp2_option.c \ + nghttp2_callbacks.c \ + nghttp2_mem.c \ + nghttp2_http.c + +NGHTTP2_OBJ_R = $(addprefix $(OBJ_DIR)/r_, $(notdir $(NGHTTP2_SRC:.c=.obj))) +NGHTTP2_OBJ_D = $(addprefix $(OBJ_DIR)/d_, $(notdir $(NGHTTP2_SRC:.c=.obj))) + +all: intro $(OBJ_DIR) $(TARGETS) + @echo 'Welcome to NgHTTP2 (release + debug).' + @echo 'Do a "make -f Makefile.MSVC install" at own risk!' + +intro: + @echo 'Building NgHTTP (MSVC) ver. "$(VERSION)".' + +test_ver: + @echo '$$(VERSION): "$(VERSION)".' + @echo '$$(_VERSION): "$(_VERSION)".' + @echo '$$(VER_MAJOR): "$(VER_MAJOR)".' + @echo '$$(VER_MINOR): "$(VER_MINOR)".' + @echo '$$(VER_MICRO): "$(VER_MICRO)".' + +$(OBJ_DIR): + - mkdir $(OBJ_DIR) + +install: includes/nghttp2/nghttp2.h includes/nghttp2/nghttp2ver.h \ + nghttp2.dll nghttp2.lib nghttp2_imp.lib \ + nghttp2d.dll nghttp2d.lib nghttp2d_imp.lib \ + copy_headers_and_libs install_nghttp2_pyd_$(USE_CYTHON) + +# +# This MUST be done before using the 'install_nghttp2_pyd_1' rule. +# +copy_headers_and_libs: + - mkdir $(INSTALL_HDR)/nghttp2 + cp --update $(addprefix includes/nghttp2/, nghttp2.h nghttp2ver.h) $(INSTALL_HDR)/nghttp2 + cp --update nghttp2.dll nghttp2d.dll $(NGHTTP2_PDB_R) $(NGHTTP2_PDB_D) $(INSTALL_BIN) + cp --update nghttp2.lib nghttp2d.lib nghttp2_imp.lib nghttp2d_imp.lib $(INSTALL_LIB) + @echo + +nghttp2.lib: $(NGHTTP2_OBJ_R) + lib -nologo -out:$@ $^ + @echo + +nghttp2d.lib: $(NGHTTP2_OBJ_D) + lib -nologo -out:$@ $^ + @echo + +nghttp2.dll nghttp2_imp.lib: $(NGHTTP2_OBJ_R) $(OBJ_DIR)/r_nghttp2.res + link $(LDFLAGS) -dll -out:nghttp2.dll -implib:nghttp2_imp.lib \ + $(NGHTTP2_OBJ_R) $(OBJ_DIR)/r_nghttp2.res $(EXT_LIBS) + @echo + +nghttp2d.dll nghttp2d_imp.lib: $(NGHTTP2_OBJ_D) $(OBJ_DIR)/d_nghttp2.res + link $(LDFLAGS) -dll -out:nghttp2d.dll -implib:nghttp2d_imp.lib \ + $(NGHTTP2_OBJ_D) $(OBJ_DIR)/d_nghttp2.res $(EXT_LIBS) + @echo + +install_nghttp2_pyd_0: ; + +install_nghttp2_pyd_1: $(addprefix ../python/, setup.py.in nghttp2.pyx) + cd ../python ; \ + echo '# $(GENERATED). DO NOT EDIT.' > setup.py ; \ + sed -e 's/@top_srcdir@/../' \ + -e 's/@top_builddir@/../' \ + -e 's/@PACKAGE_VERSION@/$(VERSION)/' setup.py.in >> setup.py ; \ + cython -v nghttp2.pyx ; \ + python setup.py install + +clean_nghttp2_pyd_0: ; + +clean_nghttp2_pyd_1: + cd ../python ; \ + rm -f setup.py nghttp2.c ; \ + rm -fR build/* + +$(OBJ_DIR)/r_%.obj: %.c + $(CC) $(CFLAGS_R) $(CFLAGS) -Fo$@ -c $< + @echo + +$(OBJ_DIR)/d_%.obj: %.c + $(CC) $(CFLAGS_D) $(CFLAGS) -Fo$@ -c $< + @echo + +$(OBJ_DIR)/r_nghttp2.res: nghttp2.rc + rc -nologo -D_RELEASE -Fo $@ $< + @echo + +$(OBJ_DIR)/d_nghttp2.res: nghttp2.rc + rc -nologo -D_DEBUG -Fo $@ $< + @echo + +includes/nghttp2/nghttp2ver.h: includes/nghttp2/nghttp2ver.h.in + sed < includes/nghttp2/nghttp2ver.h.in \ + -e 's/@PACKAGE_VERSION@/$(VERSION)/g' \ + -e 's/@PACKAGE_VERSION_NUM@/($(VERSION_NUM))/g' > $@ + touch --reference=includes/nghttp2/nghttp2ver.h.in $@ + + +define RES_FILE + #include + + VS_VERSION_INFO VERSIONINFO + FILEVERSION $(VER_MAJOR), $(VER_MINOR), $(VER_MICRO), 0 + PRODUCTVERSION $(VER_MAJOR), $(VER_MINOR), $(VER_MICRO), 0 + FILEFLAGSMASK 0x3fL + FILEOS 0x40004L + FILETYPE 0x2L + FILESUBTYPE 0x0L + #ifdef _DEBUG + #define VER_STR "$(VERSION).0 (MSVC debug)" + #define DBG "d" + FILEFLAGS 0x1L + #else + #define VER_STR "$(VERSION).0 (MSVC release)" + #define DBG "" + FILEFLAGS 0x0L + #endif + BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "CompanyName", "http://tatsuhiro-t.github.io/nghttp2/" + VALUE "FileDescription", "nghttp2; HTTP/2 C library" + VALUE "FileVersion", VER_STR + VALUE "InternalName", "nghttp2" DBG + VALUE "LegalCopyright", "The MIT License" + VALUE "LegalTrademarks", "" + VALUE "OriginalFilename", "nghttp2" DBG ".dll" + VALUE "ProductName", "NGHTTP2." + VALUE "ProductVersion", VER_STR + VALUE "PrivateBuild", "The privat build of ." + VALUE "SpecialBuild", "" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END + END +endef + +export RES_FILE + +nghttp2.rc: Makefile.MSVC + @echo 'Generating $@...' + @echo ' /* $(GENERATED). DO NOT EDIT.' > $@ + @echo ' */' >> $@ + @echo "$$RES_FILE" >> $@ + +clean: + rm -f $(OBJ_DIR)/* nghttp2_imp.exp nghttp2_imp.exp \ + nghttp2.map nghttp2d.map nghttp2.rc includes/nghttp2/nghttp2ver.h + @echo + +vclean realclean: clean clean_nghttp2_pyd_$(USE_CYTHON) + rm -f $(TARGETS) nghttp2.pdb nghttp2d.pdb nghttp2_imp.exp nghttp2d_imp.exp .depend.MSVC + - rmdir $(OBJ_DIR) + +# +# Use gcc to generated the dependencies. No MSVC specific args please! +# +REPLACE_R = 's/\(.*\)\.o: /\n$$(OBJ_DIR)\/r_\1.obj: /' +REPLACE_D = 's/\(.*\)\.o: /\n$$(OBJ_DIR)\/d_\1.obj: /' + +depend: includes/nghttp2/nghttp2ver.h + @echo '# $(GENERATED). DO NOT EDIT.' > .depend.MSVC + gcc -MM $(CFLAGS) $(NGHTTP2_SRC) >> .depend.tmp + @echo '#' >> .depend.MSVC + @echo '# Release lib objects:' >> .depend.MSVC + sed -e $(REPLACE_R) .depend.tmp >> .depend.MSVC + @echo '#' >> .depend.MSVC + @echo '# Debug lib objects:' >> .depend.MSVC + sed -e $(REPLACE_D) .depend.tmp >> .depend.MSVC + rm -f .depend.tmp + +-include .depend.MSVC diff --git a/lib/includes/Makefile.am b/lib/includes/Makefile.am new file mode 100644 index 0000000..80af63c --- /dev/null +++ b/lib/includes/Makefile.am @@ -0,0 +1,23 @@ +# nghttp2 - HTTP/2 C Library + +# Copyright (c) 2012 Tatsuhiro Tsujikawa + +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: + +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. + +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +nobase_include_HEADERS = nghttp2/nghttp2.h nghttp2/nghttp2ver.h diff --git a/lib/includes/Makefile.in b/lib/includes/Makefile.in new file mode 100644 index 0000000..f2d93d8 --- /dev/null +++ b/lib/includes/Makefile.in @@ -0,0 +1,636 @@ +# Makefile.in generated by automake 1.14.1 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2013 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +# nghttp2 - HTTP/2 C Library + +# Copyright (c) 2012 Tatsuhiro Tsujikawa + +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: + +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. + +VPATH = @srcdir@ +am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)' +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +target_triplet = @target@ +subdir = lib/includes +DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \ + $(nobase_include_HEADERS) +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/ax_boost_asio.m4 \ + $(top_srcdir)/m4/ax_boost_base.m4 \ + $(top_srcdir)/m4/ax_boost_system.m4 \ + $(top_srcdir)/m4/ax_boost_thread.m4 \ + $(top_srcdir)/m4/ax_check_compile_flag.m4 \ + $(top_srcdir)/m4/ax_cxx_compile_stdcxx_11.m4 \ + $(top_srcdir)/m4/ax_have_epoll.m4 \ + $(top_srcdir)/m4/ax_python_devel.m4 \ + $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/libxml2.m4 \ + $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ + $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +SOURCES = +DIST_SOURCES = +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +am__uninstall_files_from_dir = { \ + test -z "$$files" \ + || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ + || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ + $(am__cd) "$$dir" && rm -f $$files; }; \ + } +am__installdirs = "$(DESTDIR)$(includedir)" +HEADERS = $(nobase_include_HEADERS) +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +ETAGS = etags +CTAGS = ctags +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +APPLDFLAGS = @APPLDFLAGS@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +BOOST_ASIO_LIB = @BOOST_ASIO_LIB@ +BOOST_CPPFLAGS = @BOOST_CPPFLAGS@ +BOOST_LDFLAGS = @BOOST_LDFLAGS@ +BOOST_SYSTEM_LIB = @BOOST_SYSTEM_LIB@ +BOOST_THREAD_LIB = @BOOST_THREAD_LIB@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CUNIT_CFLAGS = @CUNIT_CFLAGS@ +CUNIT_LIBS = @CUNIT_LIBS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +CYTHON = @CYTHON@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DLLTOOL = @DLLTOOL@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +GREP = @GREP@ +HAVE_CXX11 = @HAVE_CXX11@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +JANSSON_CFLAGS = @JANSSON_CFLAGS@ +JANSSON_LIBS = @JANSSON_LIBS@ +JEMALLOC_LIBS = @JEMALLOC_LIBS@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBEVENT_OPENSSL_CFLAGS = @LIBEVENT_OPENSSL_CFLAGS@ +LIBEVENT_OPENSSL_LIBS = @LIBEVENT_OPENSSL_LIBS@ +LIBEV_CFLAGS = @LIBEV_CFLAGS@ +LIBEV_LIBS = @LIBEV_LIBS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBSPDYLAY_CFLAGS = @LIBSPDYLAY_CFLAGS@ +LIBSPDYLAY_LIBS = @LIBSPDYLAY_LIBS@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +LT_AGE = @LT_AGE@ +LT_CURRENT = @LT_CURRENT@ +LT_REVISION = @LT_REVISION@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MKDIR_P = @MKDIR_P@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OPENSSL_CFLAGS = @OPENSSL_CFLAGS@ +OPENSSL_LIBS = @OPENSSL_LIBS@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PACKAGE_VERSION_NUM = @PACKAGE_VERSION_NUM@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PKG_CONFIG = @PKG_CONFIG@ +PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ +PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ +PYTHON = @PYTHON@ +PYTHON_CPPFLAGS = @PYTHON_CPPFLAGS@ +PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@ +PYTHON_EXTRA_LDFLAGS = @PYTHON_EXTRA_LDFLAGS@ +PYTHON_EXTRA_LIBS = @PYTHON_EXTRA_LIBS@ +PYTHON_LDFLAGS = @PYTHON_LDFLAGS@ +PYTHON_PLATFORM = @PYTHON_PLATFORM@ +PYTHON_PREFIX = @PYTHON_PREFIX@ +PYTHON_SITE_PKG = @PYTHON_SITE_PKG@ +PYTHON_VERSION = @PYTHON_VERSION@ +RANLIB = @RANLIB@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +TESTLDADD = @TESTLDADD@ +VERSION = @VERSION@ +WARNCFLAGS = @WARNCFLAGS@ +XML2_CONFIG = @XML2_CONFIG@ +XML_CPPFLAGS = @XML_CPPFLAGS@ +XML_LIBS = @XML_LIBS@ +ZLIB_CFLAGS = @ZLIB_CFLAGS@ +ZLIB_LIBS = @ZLIB_LIBS@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +pkgpyexecdir = @pkgpyexecdir@ +pkgpythondir = @pkgpythondir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +pyexecdir = @pyexecdir@ +pythondir = @pythondir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target = @target@ +target_alias = @target_alias@ +target_cpu = @target_cpu@ +target_os = @target_os@ +target_vendor = @target_vendor@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ + +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +nobase_include_HEADERS = nghttp2/nghttp2.h nghttp2/nghttp2ver.h +all: all-am + +.SUFFIXES: +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu lib/includes/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu lib/includes/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs +install-nobase_includeHEADERS: $(nobase_include_HEADERS) + @$(NORMAL_INSTALL) + @list='$(nobase_include_HEADERS)'; test -n "$(includedir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(includedir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(includedir)" || exit 1; \ + fi; \ + $(am__nobase_list) | while read dir files; do \ + xfiles=; for file in $$files; do \ + if test -f "$$file"; then xfiles="$$xfiles $$file"; \ + else xfiles="$$xfiles $(srcdir)/$$file"; fi; done; \ + test -z "$$xfiles" || { \ + test "x$$dir" = x. || { \ + echo " $(MKDIR_P) '$(DESTDIR)$(includedir)/$$dir'"; \ + $(MKDIR_P) "$(DESTDIR)$(includedir)/$$dir"; }; \ + echo " $(INSTALL_HEADER) $$xfiles '$(DESTDIR)$(includedir)/$$dir'"; \ + $(INSTALL_HEADER) $$xfiles "$(DESTDIR)$(includedir)/$$dir" || exit $$?; }; \ + done + +uninstall-nobase_includeHEADERS: + @$(NORMAL_UNINSTALL) + @list='$(nobase_include_HEADERS)'; test -n "$(includedir)" || list=; \ + $(am__nobase_strip_setup); files=`$(am__nobase_strip)`; \ + dir='$(DESTDIR)$(includedir)'; $(am__uninstall_files_from_dir) + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-am +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + $(am__define_uniq_tagged_files); \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: ctags-am + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" +cscopelist: cscopelist-am + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(HEADERS) +installdirs: + for dir in "$(DESTDIR)$(includedir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic clean-libtool mostlyclean-am + +distclean: distclean-am + -rm -f Makefile +distclean-am: clean-am distclean-generic distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: install-nobase_includeHEADERS + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-generic mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-nobase_includeHEADERS + +.MAKE: install-am install-strip + +.PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ + clean-libtool cscopelist-am ctags ctags-am distclean \ + distclean-generic distclean-libtool distclean-tags distdir dvi \ + dvi-am html html-am info info-am install install-am \ + install-data install-data-am install-dvi install-dvi-am \ + install-exec install-exec-am install-html install-html-am \ + install-info install-info-am install-man \ + install-nobase_includeHEADERS install-pdf install-pdf-am \ + install-ps install-ps-am install-strip installcheck \ + installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-generic \ + mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \ + uninstall-am uninstall-nobase_includeHEADERS + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/lib/includes/nghttp2/nghttp2.h b/lib/includes/nghttp2/nghttp2.h new file mode 100644 index 0000000..5f5fdab --- /dev/null +++ b/lib/includes/nghttp2/nghttp2.h @@ -0,0 +1,3860 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2013, 2014 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGHTTP2_H +#define NGHTTP2_H + +/* Define WIN32 when build target is Win32 API (borrowed from + libcurl) */ +#if (defined(_WIN32) || defined(__WIN32__)) && !defined(WIN32) +#define WIN32 +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include + +#include + +#ifdef NGHTTP2_STATICLIB +#define NGHTTP2_EXTERN +#elif defined(WIN32) +#define NGHTTP2_EXTERN __declspec(dllexport) +#else /* !defined(WIN32) */ +#define NGHTTP2_EXTERN +#endif /* !defined(WIN32) */ + +/** + * @macro + * + * The protocol version identification string of this library + * supports. This identifier is used if HTTP/2 is used over TLS. + */ +#define NGHTTP2_PROTO_VERSION_ID "h2" +/** + * @macro + * + * The length of :macro:`NGHTTP2_PROTO_VERSION_ID`. + */ +#define NGHTTP2_PROTO_VERSION_ID_LEN 2 + +/** + * @macro + * + * The seriazlied form of ALPN protocol identifier this library + * supports. Notice that first byte is the length of following + * protocol identifier. This is the same wire format of `TLS ALPN + * extension `_. This is useful + * to process incoming ALPN tokens in wire format. + */ +#define NGHTTP2_PROTO_ALPN "\x2h2" + +/** + * @macro + * + * The length of :macro:`NGHTTP2_PROTO_ALPN`. + */ +#define NGHTTP2_PROTO_ALPN_LEN (sizeof(NGHTTP2_PROTO_ALPN) - 1) + +/** + * @macro + * + * The protocol version identification string of this library + * supports. This identifier is used if HTTP/2 is used over cleartext + * TCP. + */ +#define NGHTTP2_CLEARTEXT_PROTO_VERSION_ID "h2c" + +/** + * @macro + * + * The length of :macro:`NGHTTP2_CLEARTEXT_PROTO_VERSION_ID`. + */ +#define NGHTTP2_CLEARTEXT_PROTO_VERSION_ID_LEN 3 + +struct nghttp2_session; +/** + * @struct + * + * The primary structure to hold the resources needed for a HTTP/2 + * session. The details of this structure are intentionally hidden + * from the public API. + */ +typedef struct nghttp2_session nghttp2_session; + +/** + * @macro + * + * The age of :type:`nghttp2_info` + */ +#define NGHTTP2_VERSION_AGE 1 + +/** + * @struct + * + * This struct is what `nghttp2_version()` returns. It holds + * information about the particular nghttp2 version. + */ +typedef struct { + /** + * Age of this struct. This instance of nghttp2 sets it to + * :macro:`NGHTTP2_VERSION_AGE` but a future version may bump it and + * add more struct fields at the bottom + */ + int age; + /** + * the :macro:`NGHTTP2_VERSION_NUM` number (since age ==1) + */ + int version_num; + /** + * points to the :macro:`NGHTTP2_VERSION` string (since age ==1) + */ + const char *version_str; + /** + * points to the :macro:`NGHTTP2_PROTO_VERSION_ID` string this + * instance implements (since age ==1) + */ + const char *proto_str; + /* -------- the above fields all exist when age == 1 */ +} nghttp2_info; + +/** + * @macro + * + * The default weight of stream dependency. + */ +#define NGHTTP2_DEFAULT_WEIGHT 16 + +/** + * @macro + * + * The maximum weight of stream dependency. + */ +#define NGHTTP2_MAX_WEIGHT 256 + +/** + * @macro + * + * The minimum weight of stream dependency. + */ +#define NGHTTP2_MIN_WEIGHT 1 + +/** + * @macro + * + * The maximum window size + */ +#define NGHTTP2_MAX_WINDOW_SIZE ((int32_t)((1U << 31) - 1)) + +/** + * @macro + * + * The initial window size for stream level flow control. + */ +#define NGHTTP2_INITIAL_WINDOW_SIZE ((1 << 16) - 1) +/** + * @macro + * + * The initial window size for connection level flow control. + */ +#define NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE ((1 << 16) - 1) + +/** + * @macro + * + * The default header table size. + */ +#define NGHTTP2_DEFAULT_HEADER_TABLE_SIZE (1 << 12) + +/** + * @macro + * + * The client magic string, which is the first 24 bytes byte string of + * client connection preface. + */ +#define NGHTTP2_CLIENT_MAGIC "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n" + +/** + * @macro + * + * The length of :macro:`NGHTTP2_CLIENT_MAGIC`. + */ +#define NGHTTP2_CLIENT_MAGIC_LEN 24 + +/** + * @enum + * + * Error codes used in this library. The code range is [-999, -500], + * inclusive. The following values are defined: + */ +typedef enum { + /** + * Invalid argument passed. + */ + NGHTTP2_ERR_INVALID_ARGUMENT = -501, + /** + * Out of buffer space. + */ + NGHTTP2_ERR_BUFFER_ERROR = -502, + /** + * The specified protocol version is not supported. + */ + NGHTTP2_ERR_UNSUPPORTED_VERSION = -503, + /** + * Used as a return value from :type:`nghttp2_send_callback`, + * :type:`nghttp2_recv_callback` and + * :type:`nghttp2_send_data_callback` to indicate that the operation + * would block. + */ + NGHTTP2_ERR_WOULDBLOCK = -504, + /** + * General protocol error + */ + NGHTTP2_ERR_PROTO = -505, + /** + * The frame is invalid. + */ + NGHTTP2_ERR_INVALID_FRAME = -506, + /** + * The peer performed a shutdown on the connection. + */ + NGHTTP2_ERR_EOF = -507, + /** + * Used as a return value from + * :func:`nghttp2_data_source_read_callback` to indicate that data + * transfer is postponed. See + * :func:`nghttp2_data_source_read_callback` for details. + */ + NGHTTP2_ERR_DEFERRED = -508, + /** + * Stream ID has reached the maximum value. Therefore no stream ID + * is available. + */ + NGHTTP2_ERR_STREAM_ID_NOT_AVAILABLE = -509, + /** + * The stream is already closed; or the stream ID is invalid. + */ + NGHTTP2_ERR_STREAM_CLOSED = -510, + /** + * RST_STREAM has been added to the outbound queue. The stream is + * in closing state. + */ + NGHTTP2_ERR_STREAM_CLOSING = -511, + /** + * The transmission is not allowed for this stream (e.g., a frame + * with END_STREAM flag set has already sent). + */ + NGHTTP2_ERR_STREAM_SHUT_WR = -512, + /** + * The stream ID is invalid. + */ + NGHTTP2_ERR_INVALID_STREAM_ID = -513, + /** + * The state of the stream is not valid (e.g., DATA cannot be sent + * to the stream if response HEADERS has not been sent). + */ + NGHTTP2_ERR_INVALID_STREAM_STATE = -514, + /** + * Another DATA frame has already been deferred. + */ + NGHTTP2_ERR_DEFERRED_DATA_EXIST = -515, + /** + * Starting new stream is not allowed (e.g., GOAWAY has been sent + * and/or received). + */ + NGHTTP2_ERR_START_STREAM_NOT_ALLOWED = -516, + /** + * GOAWAY has already been sent. + */ + NGHTTP2_ERR_GOAWAY_ALREADY_SENT = -517, + /** + * The received frame contains the invalid header block (e.g., There + * are duplicate header names; or the header names are not encoded + * in US-ASCII character set and not lower cased; or the header name + * is zero-length string; or the header value contains multiple + * in-sequence NUL bytes). + */ + NGHTTP2_ERR_INVALID_HEADER_BLOCK = -518, + /** + * Indicates that the context is not suitable to perform the + * requested operation. + */ + NGHTTP2_ERR_INVALID_STATE = -519, + /** + * The user callback function failed due to the temporal error. + */ + NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE = -521, + /** + * The length of the frame is invalid, either too large or too small. + */ + NGHTTP2_ERR_FRAME_SIZE_ERROR = -522, + /** + * Header block inflate/deflate error. + */ + NGHTTP2_ERR_HEADER_COMP = -523, + /** + * Flow control error + */ + NGHTTP2_ERR_FLOW_CONTROL = -524, + /** + * Insufficient buffer size given to function. + */ + NGHTTP2_ERR_INSUFF_BUFSIZE = -525, + /** + * Callback was paused by the application + */ + NGHTTP2_ERR_PAUSE = -526, + /** + * There are too many in-flight SETTING frame and no more + * transmission of SETTINGS is allowed. + */ + NGHTTP2_ERR_TOO_MANY_INFLIGHT_SETTINGS = -527, + /** + * The server push is disabled. + */ + NGHTTP2_ERR_PUSH_DISABLED = -528, + /** + * DATA frame for a given stream has been already submitted and has + * not been fully processed yet. + */ + NGHTTP2_ERR_DATA_EXIST = -529, + /** + * The current session is closing due to a connection error or + * `nghttp2_session_terminate_session()` is called. + */ + NGHTTP2_ERR_SESSION_CLOSING = -530, + /** + * Invalid HTTP header field was received and stream is going to be + * closed. + */ + NGHTTP2_ERR_HTTP_HEADER = -531, + /** + * Violation in HTTP messaging rule. + */ + NGHTTP2_ERR_HTTP_MESSAGING = -532, + /** + * Stream was refused. + */ + NGHTTP2_ERR_REFUSED_STREAM = -533, + /** + * Unexpected internal error, but recovered. + */ + NGHTTP2_ERR_INTERNAL = -534, + /** + * The errors < :enum:`NGHTTP2_ERR_FATAL` mean that the library is + * under unexpected condition and processing was terminated (e.g., + * out of memory). If application receives this error code, it must + * stop using that :type:`nghttp2_session` object and only allowed + * operation for that object is deallocate it using + * `nghttp2_session_del()`. + */ + NGHTTP2_ERR_FATAL = -900, + /** + * Out of memory. This is a fatal error. + */ + NGHTTP2_ERR_NOMEM = -901, + /** + * The user callback function failed. This is a fatal error. + */ + NGHTTP2_ERR_CALLBACK_FAILURE = -902, + /** + * Invalid client magic (see :macro:`NGHTTP2_CLIENT_MAGIC`) was + * received and further processing is not possible. + */ + NGHTTP2_ERR_BAD_CLIENT_MAGIC = -903 +} nghttp2_error; + +/** + * @enum + * + * The flags for header field name/value pair. + */ +typedef enum { + /** + * No flag set. + */ + NGHTTP2_NV_FLAG_NONE = 0, + /** + * Indicates that this name/value pair must not be indexed ("Literal + * Header Field never Indexed" representation must be used in HPACK + * encoding). Other implementation calls this bit as "sensitive". + */ + NGHTTP2_NV_FLAG_NO_INDEX = 0x01 +} nghttp2_nv_flag; + +/** + * @struct + * + * The name/value pair, which mainly used to represent header fields. + */ +typedef struct { + /** + * The |name| byte string. If this struct is presented from library + * (e.g., :type:`nghttp2_on_frame_recv_callback`), |name| is + * guaranteed to be NULL-terminated. When application is + * constructing this struct, |name| is not required to be + * NULL-terminated. + */ + uint8_t *name; + /** + * The |value| byte string. If this struct is presented from + * library (e.g., :type:`nghttp2_on_frame_recv_callback`), |value| + * is guaranteed to be NULL-terminated. When application is + * constructing this struct, |value| is not required to be + * NULL-terminated. + */ + uint8_t *value; + /** + * The length of the |name|, excluding terminating NULL. + */ + size_t namelen; + /** + * The length of the |value|, excluding terminating NULL. + */ + size_t valuelen; + /** + * Bitwise OR of one or more of :type:`nghttp2_nv_flag`. + */ + uint8_t flags; +} nghttp2_nv; + +/** + * @enum + * + * The frame types in HTTP/2 specification. + */ +typedef enum { + /** + * The DATA frame. + */ + NGHTTP2_DATA = 0, + /** + * The HEADERS frame. + */ + NGHTTP2_HEADERS = 0x01, + /** + * The PRIORITY frame. + */ + NGHTTP2_PRIORITY = 0x02, + /** + * The RST_STREAM frame. + */ + NGHTTP2_RST_STREAM = 0x03, + /** + * The SETTINGS frame. + */ + NGHTTP2_SETTINGS = 0x04, + /** + * The PUSH_PROMISE frame. + */ + NGHTTP2_PUSH_PROMISE = 0x05, + /** + * The PING frame. + */ + NGHTTP2_PING = 0x06, + /** + * The GOAWAY frame. + */ + NGHTTP2_GOAWAY = 0x07, + /** + * The WINDOW_UPDATE frame. + */ + NGHTTP2_WINDOW_UPDATE = 0x08, + /** + * The CONTINUATION frame. This frame type won't be passed to any + * callbacks because the library processes this frame type and its + * preceding HEADERS/PUSH_PROMISE as a single frame. + */ + NGHTTP2_CONTINUATION = 0x09 +} nghttp2_frame_type; + +/** + * @enum + * + * The flags for HTTP/2 frames. This enum defines all flags for all + * frames. + */ +typedef enum { + /** + * No flag set. + */ + NGHTTP2_FLAG_NONE = 0, + /** + * The END_STREAM flag. + */ + NGHTTP2_FLAG_END_STREAM = 0x01, + /** + * The END_HEADERS flag. + */ + NGHTTP2_FLAG_END_HEADERS = 0x04, + /** + * The ACK flag. + */ + NGHTTP2_FLAG_ACK = 0x01, + /** + * The PADDED flag. + */ + NGHTTP2_FLAG_PADDED = 0x08, + /** + * The PRIORITY flag. + */ + NGHTTP2_FLAG_PRIORITY = 0x20 +} nghttp2_flag; + +/** + * @enum + * The SETTINGS ID. + */ +typedef enum { + /** + * SETTINGS_HEADER_TABLE_SIZE + */ + NGHTTP2_SETTINGS_HEADER_TABLE_SIZE = 0x01, + /** + * SETTINGS_ENABLE_PUSH + */ + NGHTTP2_SETTINGS_ENABLE_PUSH = 0x02, + /** + * SETTINGS_MAX_CONCURRENT_STREAMS + */ + NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS = 0x03, + /** + * SETTINGS_INITIAL_WINDOW_SIZE + */ + NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE = 0x04, + /** + * SETTINGS_MAX_FRAME_SIZE + */ + NGHTTP2_SETTINGS_MAX_FRAME_SIZE = 0x05, + /** + * SETTINGS_MAX_HEADER_LIST_SIZE + */ + NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE = 0x06 +} nghttp2_settings_id; +/* Note: If we add SETTINGS, update the capacity of + NGHTTP2_INBOUND_NUM_IV as well */ + +/** + * @macro + * Default maximum concurrent streams. + */ +#define NGHTTP2_INITIAL_MAX_CONCURRENT_STREAMS ((1U << 31) - 1) + +/** + * @enum + * The status codes for the RST_STREAM and GOAWAY frames. + */ +typedef enum { + /** + * No errors. + */ + NGHTTP2_NO_ERROR = 0x00, + /** + * PROTOCOL_ERROR + */ + NGHTTP2_PROTOCOL_ERROR = 0x01, + /** + * INTERNAL_ERROR + */ + NGHTTP2_INTERNAL_ERROR = 0x02, + /** + * FLOW_CONTROL_ERROR + */ + NGHTTP2_FLOW_CONTROL_ERROR = 0x03, + /** + * SETTINGS_TIMEOUT + */ + NGHTTP2_SETTINGS_TIMEOUT = 0x04, + /** + * STREAM_CLOSED + */ + NGHTTP2_STREAM_CLOSED = 0x05, + /** + * FRAME_SIZE_ERROR + */ + NGHTTP2_FRAME_SIZE_ERROR = 0x06, + /** + * REFUSED_STREAM + */ + NGHTTP2_REFUSED_STREAM = 0x07, + /** + * CANCEL + */ + NGHTTP2_CANCEL = 0x08, + /** + * COMPRESSION_ERROR + */ + NGHTTP2_COMPRESSION_ERROR = 0x09, + /** + * CONNECT_ERROR + */ + NGHTTP2_CONNECT_ERROR = 0x0a, + /** + * ENHANCE_YOUR_CALM + */ + NGHTTP2_ENHANCE_YOUR_CALM = 0x0b, + /** + * INADEQUATE_SECURITY + */ + NGHTTP2_INADEQUATE_SECURITY = 0x0c, + /** + * HTTP_1_1_REQUIRED + */ + NGHTTP2_HTTP_1_1_REQUIRED = 0x0d +} nghttp2_error_code; + +/** + * @struct + * The frame header. + */ +typedef struct { + /** + * The length field of this frame, excluding frame header. + */ + size_t length; + /** + * The stream identifier (aka, stream ID) + */ + int32_t stream_id; + /** + * The type of this frame. See `nghttp2_frame_type`. + */ + uint8_t type; + /** + * The flags. + */ + uint8_t flags; + /** + * Reserved bit in frame header. Currently, this is always set to 0 + * and application should not expect something useful in here. + */ + uint8_t reserved; +} nghttp2_frame_hd; + +/** + * @union + * + * This union represents the some kind of data source passed to + * :type:`nghttp2_data_source_read_callback`. + */ +typedef union { + /** + * The integer field, suitable for a file descriptor. + */ + int fd; + /** + * The pointer to an arbitrary object. + */ + void *ptr; +} nghttp2_data_source; + +/** + * @enum + * + * The flags used to set in |data_flags| output parameter in + * :type:`nghttp2_data_source_read_callback`. + */ +typedef enum { + /** + * No flag set. + */ + NGHTTP2_DATA_FLAG_NONE = 0, + /** + * Indicates EOF was sensed. + */ + NGHTTP2_DATA_FLAG_EOF = 0x01, + /** + * Indicates that END_STREAM flag must not be set even if + * NGHTTP2_DATA_FLAG_EOF is set. Usually this flag is used to send + * trailer header fields with `nghttp2_submit_request()` or + * `nghttp2_submit_response()`. + */ + NGHTTP2_DATA_FLAG_NO_END_STREAM = 0x02, + /** + * Indicates that application will send complete DATA frame in + * :type:`nghttp2_send_data_callback`. + */ + NGHTTP2_DATA_FLAG_NO_COPY = 0x04 +} nghttp2_data_flag; + +/** + * @functypedef + * + * Callback function invoked when the library wants to read data from + * the |source|. The read data is sent in the stream |stream_id|. + * The implementation of this function must read at most |length| + * bytes of data from |source| (or possibly other places) and store + * them in |buf| and return number of data stored in |buf|. If EOF is + * reached, set :enum:`NGHTTP2_DATA_FLAG_EOF` flag in |*data_flags|. + * + * Sometime it is desirable to avoid copying data into |buf| and let + * application to send data directly. To achieve this, set + * :enum:`NGHTTP2_DATA_FLAG_NO_COPY` to |*data_flags| (and possibly + * other flags, just like when we do copy), and return the number of + * bytes to send without copying data into |buf|. The library, seeing + * :enum:`NGHTTP2_DATA_FLAG_NO_COPY`, will invoke + * :type:`nghttp2_send_data_callback`. The application must send + * complete DATA frame in that callback. + * + * If this callback is set by `nghttp2_submit_request()`, + * `nghttp2_submit_response()` or `nghttp2_submit_headers()` and + * `nghttp2_submit_data()` with flag parameter + * :enum:`NGHTTP2_FLAG_END_STREAM` set, and + * :enum:`NGHTTP2_DATA_FLAG_EOF` flag is set to |*data_flags|, DATA + * frame will have END_STREAM flag set. Usually, this is expected + * behaviour and all are fine. One exception is send trailer header + * fields. You cannot send trailers after sending frame with + * END_STREAM set. To avoid this problem, one can set + * :enum:`NGHTTP2_DATA_FLAG_NO_END_STREAM` along with + * :enum:`NGHTTP2_DATA_FLAG_EOF` to signal the library not to set + * END_STREAM in DATA frame. Then application can use + * `nghttp2_submit_trailer()` to send trailers. + * `nghttp2_submit_trailer()` can be called inside this callback. + * + * If the application wants to postpone DATA frames (e.g., + * asynchronous I/O, or reading data blocks for long time), it is + * achieved by returning :enum:`NGHTTP2_ERR_DEFERRED` without reading + * any data in this invocation. The library removes DATA frame from + * the outgoing queue temporarily. To move back deferred DATA frame + * to outgoing queue, call `nghttp2_session_resume_data()`. In case + * of error, there are 2 choices. Returning + * :enum:`NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE` will close the stream + * by issuing RST_STREAM with :enum:`NGHTTP2_INTERNAL_ERROR`. If a + * different error code is desirable, use + * `nghttp2_submit_rst_stream()` with a desired error code and then + * return :enum:`NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE`. Returning + * :enum:`NGHTTP2_ERR_CALLBACK_FAILURE` will signal the entire session + * failure. + */ +typedef ssize_t (*nghttp2_data_source_read_callback)( + nghttp2_session *session, int32_t stream_id, uint8_t *buf, size_t length, + uint32_t *data_flags, nghttp2_data_source *source, void *user_data); + +/** + * @struct + * + * This struct represents the data source and the way to read a chunk + * of data from it. + */ +typedef struct { + /** + * The data source. + */ + nghttp2_data_source source; + /** + * The callback function to read a chunk of data from the |source|. + */ + nghttp2_data_source_read_callback read_callback; +} nghttp2_data_provider; + +/** + * @struct + * + * The DATA frame. The received data is delivered via + * :type:`nghttp2_on_data_chunk_recv_callback`. + */ +typedef struct { + nghttp2_frame_hd hd; + /** + * The length of the padding in this frame. This includes PAD_HIGH + * and PAD_LOW. + */ + size_t padlen; +} nghttp2_data; + +/** + * @enum + * + * The category of HEADERS, which indicates the role of the frame. In + * HTTP/2 spec, request, response, push response and other arbitrary + * headers (e.g., trailers) are all called just HEADERS. To give the + * application the role of incoming HEADERS frame, we define several + * categories. + */ +typedef enum { + /** + * The HEADERS frame is opening new stream, which is analogous to + * SYN_STREAM in SPDY. + */ + NGHTTP2_HCAT_REQUEST = 0, + /** + * The HEADERS frame is the first response headers, which is + * analogous to SYN_REPLY in SPDY. + */ + NGHTTP2_HCAT_RESPONSE = 1, + /** + * The HEADERS frame is the first headers sent against reserved + * stream. + */ + NGHTTP2_HCAT_PUSH_RESPONSE = 2, + /** + * The HEADERS frame which does not apply for the above categories, + * which is analogous to HEADERS in SPDY. If non-final response + * (e.g., status 1xx) is used, final response HEADERS frame will be + * categorized here. + */ + NGHTTP2_HCAT_HEADERS = 3 +} nghttp2_headers_category; + +/** + * @struct + * + * The structure to specify stream dependency. + */ +typedef struct { + /** + * The stream ID of the stream to depend on. Specifying 0 makes + * stream not depend any other stream. + */ + int32_t stream_id; + /** + * The weight of this dependency. + */ + int32_t weight; + /** + * nonzero means exclusive dependency + */ + uint8_t exclusive; +} nghttp2_priority_spec; + +/** + * @struct + * + * The HEADERS frame. It has the following members: + */ +typedef struct { + /** + * The frame header. + */ + nghttp2_frame_hd hd; + /** + * The length of the padding in this frame. This includes PAD_HIGH + * and PAD_LOW. + */ + size_t padlen; + /** + * The priority specification + */ + nghttp2_priority_spec pri_spec; + /** + * The name/value pairs. + */ + nghttp2_nv *nva; + /** + * The number of name/value pairs in |nva|. + */ + size_t nvlen; + /** + * The category of this HEADERS frame. + */ + nghttp2_headers_category cat; +} nghttp2_headers; + +/** + * @struct + * + * The PRIORITY frame. It has the following members: + */ +typedef struct { + /** + * The frame header. + */ + nghttp2_frame_hd hd; + /** + * The priority specification. + */ + nghttp2_priority_spec pri_spec; +} nghttp2_priority; + +/** + * @struct + * + * The RST_STREAM frame. It has the following members: + */ +typedef struct { + /** + * The frame header. + */ + nghttp2_frame_hd hd; + /** + * The error code. See :type:`nghttp2_error_code`. + */ + uint32_t error_code; +} nghttp2_rst_stream; + +/** + * @struct + * + * The SETTINGS ID/Value pair. It has the following members: + */ +typedef struct { + /** + * The SETTINGS ID. See :type:`nghttp2_settings_id`. + */ + int32_t settings_id; + /** + * The value of this entry. + */ + uint32_t value; +} nghttp2_settings_entry; + +/** + * @struct + * + * The SETTINGS frame. It has the following members: + */ +typedef struct { + /** + * The frame header. + */ + nghttp2_frame_hd hd; + /** + * The number of SETTINGS ID/Value pairs in |iv|. + */ + size_t niv; + /** + * The pointer to the array of SETTINGS ID/Value pair. + */ + nghttp2_settings_entry *iv; +} nghttp2_settings; + +/** + * @struct + * + * The PUSH_PROMISE frame. It has the following members: + */ +typedef struct { + /** + * The frame header. + */ + nghttp2_frame_hd hd; + /** + * The length of the padding in this frame. This includes PAD_HIGH + * and PAD_LOW. + */ + size_t padlen; + /** + * The name/value pairs. + */ + nghttp2_nv *nva; + /** + * The number of name/value pairs in |nva|. + */ + size_t nvlen; + /** + * The promised stream ID + */ + int32_t promised_stream_id; + /** + * Reserved bit. Currently this is always set to 0 and application + * should not expect something useful in here. + */ + uint8_t reserved; +} nghttp2_push_promise; + +/** + * @struct + * + * The PING frame. It has the following members: + */ +typedef struct { + /** + * The frame header. + */ + nghttp2_frame_hd hd; + /** + * The opaque data + */ + uint8_t opaque_data[8]; +} nghttp2_ping; + +/** + * @struct + * + * The GOAWAY frame. It has the following members: + */ +typedef struct { + /** + * The frame header. + */ + nghttp2_frame_hd hd; + /** + * The last stream stream ID. + */ + int32_t last_stream_id; + /** + * The error code. See :type:`nghttp2_error_code`. + */ + uint32_t error_code; + /** + * The additional debug data + */ + uint8_t *opaque_data; + /** + * The length of |opaque_data| member. + */ + size_t opaque_data_len; + /** + * Reserved bit. Currently this is always set to 0 and application + * should not expect something useful in here. + */ + uint8_t reserved; +} nghttp2_goaway; + +/** + * @struct + * + * The WINDOW_UPDATE frame. It has the following members: + */ +typedef struct { + /** + * The frame header. + */ + nghttp2_frame_hd hd; + /** + * The window size increment. + */ + int32_t window_size_increment; + /** + * Reserved bit. Currently this is always set to 0 and application + * should not expect something useful in here. + */ + uint8_t reserved; +} nghttp2_window_update; + +/** + * @struct + * + * The extension frame. It has following members: + */ +typedef struct { + /** + * The frame header. + */ + nghttp2_frame_hd hd; + /** + * The pointer to extension payload. The exact pointer type is + * determined by hd.type. + * + * Currently, no extension is supported. This is a place holder for + * the future extensions. + */ + void *payload; +} nghttp2_extension; + +/** + * @union + * + * This union includes all frames to pass them to various function + * calls as nghttp2_frame type. The CONTINUATION frame is omitted + * from here because the library deals with it internally. + */ +typedef union { + /** + * The frame header, which is convenient to inspect frame header. + */ + nghttp2_frame_hd hd; + /** + * The DATA frame. + */ + nghttp2_data data; + /** + * The HEADERS frame. + */ + nghttp2_headers headers; + /** + * The PRIORITY frame. + */ + nghttp2_priority priority; + /** + * The RST_STREAM frame. + */ + nghttp2_rst_stream rst_stream; + /** + * The SETTINGS frame. + */ + nghttp2_settings settings; + /** + * The PUSH_PROMISE frame. + */ + nghttp2_push_promise push_promise; + /** + * The PING frame. + */ + nghttp2_ping ping; + /** + * The GOAWAY frame. + */ + nghttp2_goaway goaway; + /** + * The WINDOW_UPDATE frame. + */ + nghttp2_window_update window_update; + /** + * The extension frame. + */ + nghttp2_extension ext; +} nghttp2_frame; + +/** + * @functypedef + * + * Callback function invoked when |session| wants to send data to the + * remote peer. The implementation of this function must send at most + * |length| bytes of data stored in |data|. The |flags| is currently + * not used and always 0. It must return the number of bytes sent if + * it succeeds. If it cannot send any single byte without blocking, + * it must return :enum:`NGHTTP2_ERR_WOULDBLOCK`. For other errors, + * it must return :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`. The + * |user_data| pointer is the third argument passed in to the call to + * `nghttp2_session_client_new()` or `nghttp2_session_server_new()`. + * + * This callback is required if the application uses + * `nghttp2_session_send()` to send data to the remote endpoint. If + * the application uses solely `nghttp2_session_mem_send()` instead, + * this callback function is unnecessary. + * + * To set this callback to :type:`nghttp2_session_callbacks`, use + * `nghttp2_session_callbacks_set_send_callback()`. + */ +typedef ssize_t (*nghttp2_send_callback)(nghttp2_session *session, + const uint8_t *data, size_t length, + int flags, void *user_data); + +/** + * @functypedef + * + * Callback function invoked when :enum:`NGHTTP2_DATA_FLAG_NO_COPY` is + * used in :type:`nghttp2_data_source_read_callback` to send complete + * DATA frame. + * + * The |frame| is a DATA frame to send. The |framehd| is the + * serialized frame header (9 bytes). The |length| is the length of + * application data to send (this does not include padding). The + * |source| is the same pointer passed to + * :type:`nghttp2_data_source_read_callback`. + * + * The application first must send frame header |framehd| of length 9 + * bytes. If ``frame->padlen > 0``, send 1 byte of value + * ``frame->padlen - 1``. Then send exactly |length| bytes of + * application data. Finally, if ``frame->padlen > 0``, send + * ``frame->padlen - 1`` bytes of zero (they are padding). + * + * The application has to send complete DATA frame in this callback. + * If all data were written successfully, return 0. + * + * If it cannot send it all, just return + * :enum:`NGHTTP2_ERR_WOULDBLOCK`; the library will call this callback + * with the same parameters later (It is recommended to send complete + * DATA frame at once in this function to deal with error; if partial + * frame data has already sent, it is impossible to send another data + * in that state, and all we can do is tear down connection). If + * application decided to reset this stream, return + * :enum:`NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE`, then the library + * will send RST_STREAM with INTERNAL_ERROR as error code. The + * application can also return :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`, + * which will result in connection closure. Returning any other value + * is treated as :enum:`NGHTTP2_ERR_CALLBACK_FAILURE` is returned. + */ +typedef int (*nghttp2_send_data_callback)(nghttp2_session *session, + nghttp2_frame *frame, + const uint8_t *framehd, size_t length, + nghttp2_data_source *source, + void *user_data); + +/** + * @functypedef + * + * Callback function invoked when |session| wants to receive data from + * the remote peer. The implementation of this function must read at + * most |length| bytes of data and store it in |buf|. The |flags| is + * currently not used and always 0. It must return the number of + * bytes written in |buf| if it succeeds. If it cannot read any + * single byte without blocking, it must return + * :enum:`NGHTTP2_ERR_WOULDBLOCK`. If it gets EOF before it reads any + * single byte, it must return :enum:`NGHTTP2_ERR_EOF`. For other + * errors, it must return :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`. + * Returning 0 is treated as :enum:`NGHTTP2_ERR_WOULDBLOCK`. The + * |user_data| pointer is the third argument passed in to the call to + * `nghttp2_session_client_new()` or `nghttp2_session_server_new()`. + * + * This callback is required if the application uses + * `nghttp2_session_recv()` to receive data from the remote endpoint. + * If the application uses solely `nghttp2_session_mem_recv()` + * instead, this callback function is unnecessary. + * + * To set this callback to :type:`nghttp2_session_callbacks`, use + * `nghttp2_session_callbacks_set_recv_callback()`. + */ +typedef ssize_t (*nghttp2_recv_callback)(nghttp2_session *session, uint8_t *buf, + size_t length, int flags, + void *user_data); + +/** + * @functypedef + * + * Callback function invoked by `nghttp2_session_recv()` and + * `nghttp2_session_mem_recv()` when a frame is received. The + * |user_data| pointer is the third argument passed in to the call to + * `nghttp2_session_client_new()` or `nghttp2_session_server_new()`. + * + * If frame is HEADERS or PUSH_PROMISE, the ``nva`` and ``nvlen`` + * member of their data structure are always ``NULL`` and 0 + * respectively. The header name/value pairs are emitted via + * :type:`nghttp2_on_header_callback`. + * + * For HEADERS, PUSH_PROMISE and DATA frames, this callback may be + * called after stream is closed (see + * :type:`nghttp2_on_stream_close_callback`). The application should + * check that stream is still alive using its own stream management or + * :func:`nghttp2_session_get_stream_user_data()`. + * + * Only HEADERS and DATA frame can signal the end of incoming data. + * If ``frame->hd.flags & NGHTTP2_FLAG_END_STREAM`` is nonzero, the + * |frame| is the last frame from the remote peer in this stream. + * + * This callback won't be called for CONTINUATION frames. + * HEADERS/PUSH_PROMISE + CONTINUATIONs are treated as single frame. + * + * The implementation of this function must return 0 if it succeeds. + * If nonzero value is returned, it is treated as fatal error and + * `nghttp2_session_recv()` and `nghttp2_session_mem_recv()` functions + * immediately return :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`. + * + * To set this callback to :type:`nghttp2_session_callbacks`, use + * `nghttp2_session_callbacks_set_on_frame_recv_callback()`. + */ +typedef int (*nghttp2_on_frame_recv_callback)(nghttp2_session *session, + const nghttp2_frame *frame, + void *user_data); + +/** + * @functypedef + * + * Callback function invoked by `nghttp2_session_recv()` and + * `nghttp2_session_mem_recv()` when an invalid non-DATA frame is + * received. The error is indicated by the |lib_error_code|, which is + * one of the values defined in :type:`nghttp2_error`. When this + * callback function is invoked, the library automatically submits + * either RST_STREAM or GOAWAY frame. The |user_data| pointer is the + * third argument passed in to the call to + * `nghttp2_session_client_new()` or `nghttp2_session_server_new()`. + * + * If frame is HEADERS or PUSH_PROMISE, the ``nva`` and ``nvlen`` + * member of their data structure are always ``NULL`` and 0 + * respectively. + * + * The implementation of this function must return 0 if it succeeds. + * If nonzero is returned, it is treated as fatal error and + * `nghttp2_session_recv()` and `nghttp2_session_mem_recv()` functions + * immediately return :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`. + * + * To set this callback to :type:`nghttp2_session_callbacks`, use + * `nghttp2_session_callbacks_set_on_invalid_frame_recv_callback()`. + */ +typedef int (*nghttp2_on_invalid_frame_recv_callback)( + nghttp2_session *session, const nghttp2_frame *frame, int lib_error_code, + void *user_data); + +/** + * @functypedef + * + * Callback function invoked when a chunk of data in DATA frame is + * received. The |stream_id| is the stream ID this DATA frame belongs + * to. The |flags| is the flags of DATA frame which this data chunk + * is contained. ``(flags & NGHTTP2_FLAG_END_STREAM) != 0`` does not + * necessarily mean this chunk of data is the last one in the stream. + * You should use :type:`nghttp2_on_frame_recv_callback` to know all + * data frames are received. The |user_data| pointer is the third + * argument passed in to the call to `nghttp2_session_client_new()` or + * `nghttp2_session_server_new()`. + * + * If the application uses `nghttp2_session_mem_recv()`, it can return + * :enum:`NGHTTP2_ERR_PAUSE` to make `nghttp2_session_mem_recv()` + * return without processing further input bytes. The memory by + * pointed by the |data| is retained until + * `nghttp2_session_mem_recv()` or `nghttp2_session_recv()` is called. + * The application must retain the input bytes which was used to + * produce the |data| parameter, because it may refer to the memory + * region included in the input bytes. + * + * The implementation of this function must return 0 if it succeeds. + * If nonzero is returned, it is treated as fatal error, and + * `nghttp2_session_recv()` and `nghttp2_session_mem_recv()` functions + * immediately return :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`. + * + * To set this callback to :type:`nghttp2_session_callbacks`, use + * `nghttp2_session_callbacks_set_on_data_chunk_recv_callback()`. + */ +typedef int (*nghttp2_on_data_chunk_recv_callback)(nghttp2_session *session, + uint8_t flags, + int32_t stream_id, + const uint8_t *data, + size_t len, void *user_data); + +/** + * @functypedef + * + * Callback function invoked just before the non-DATA frame |frame| is + * sent. The |user_data| pointer is the third argument passed in to + * the call to `nghttp2_session_client_new()` or + * `nghttp2_session_server_new()`. + * + * The implementation of this function must return 0 if it succeeds. + * If nonzero is returned, it is treated as fatal error and + * `nghttp2_session_send()` and `nghttp2_session_mem_send()` functions + * immediately return :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`. + * + * To set this callback to :type:`nghttp2_session_callbacks`, use + * `nghttp2_session_callbacks_set_before_frame_send_callback()`. + */ +typedef int (*nghttp2_before_frame_send_callback)(nghttp2_session *session, + const nghttp2_frame *frame, + void *user_data); + +/** + * @functypedef + * + * Callback function invoked after the frame |frame| is sent. The + * |user_data| pointer is the third argument passed in to the call to + * `nghttp2_session_client_new()` or `nghttp2_session_server_new()`. + * + * The implementation of this function must return 0 if it succeeds. + * If nonzero is returned, it is treated as fatal error and + * `nghttp2_session_send()` and `nghttp2_session_mem_send()` functions + * immediately return :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`. + * + * To set this callback to :type:`nghttp2_session_callbacks`, use + * `nghttp2_session_callbacks_set_on_frame_send_callback()`. + */ +typedef int (*nghttp2_on_frame_send_callback)(nghttp2_session *session, + const nghttp2_frame *frame, + void *user_data); + +/** + * @functypedef + * + * Callback function invoked after the non-DATA frame |frame| is not + * sent because of the error. The error is indicated by the + * |lib_error_code|, which is one of the values defined in + * :type:`nghttp2_error`. The |user_data| pointer is the third + * argument passed in to the call to `nghttp2_session_client_new()` or + * `nghttp2_session_server_new()`. + * + * The implementation of this function must return 0 if it succeeds. + * If nonzero is returned, it is treated as fatal error and + * `nghttp2_session_send()` and `nghttp2_session_mem_send()` functions + * immediately return :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`. + * + * `nghttp2_session_get_stream_user_data()` can be used to get + * associated data. + * + * To set this callback to :type:`nghttp2_session_callbacks`, use + * `nghttp2_session_callbacks_set_on_frame_not_send_callback()`. + */ +typedef int (*nghttp2_on_frame_not_send_callback)(nghttp2_session *session, + const nghttp2_frame *frame, + int lib_error_code, + void *user_data); + +/** + * @functypedef + * + * Callback function invoked when the stream |stream_id| is closed. + * The reason of closure is indicated by the |error_code|. The + * |error_code| is usually one of :enum:`nghttp2_error_code`, but that + * is not guaranteed. The stream_user_data, which was specified in + * `nghttp2_submit_request()` or `nghttp2_submit_headers()`, is still + * available in this function. The |user_data| pointer is the third + * argument passed in to the call to `nghttp2_session_client_new()` or + * `nghttp2_session_server_new()`. + * + * This function is also called for a stream in reserved state. + * + * The implementation of this function must return 0 if it succeeds. + * If nonzero is returned, it is treated as fatal error and + * `nghttp2_session_recv()`, `nghttp2_session_mem_recv()`, + * `nghttp2_session_send()`, and `nghttp2_session_mem_send()` + * functions immediately return :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`. + * + * To set this callback to :type:`nghttp2_session_callbacks`, use + * `nghttp2_session_callbacks_set_on_stream_close_callback()`. + */ +typedef int (*nghttp2_on_stream_close_callback)(nghttp2_session *session, + int32_t stream_id, + uint32_t error_code, + void *user_data); + +/** + * @functypedef + * + * Callback function invoked when the reception of header block in + * HEADERS or PUSH_PROMISE is started. Each header name/value pair + * will be emitted by :type:`nghttp2_on_header_callback`. + * + * The ``frame->hd.flags`` may not have + * :enum:`NGHTTP2_FLAG_END_HEADERS` flag set, which indicates that one + * or more CONTINUATION frames are involved. But the application does + * not need to care about that because the header name/value pairs are + * emitted transparently regardless of CONTINUATION frames. + * + * The server applications probably create an object to store + * information about new stream if ``frame->hd.type == + * NGHTTP2_HEADERS`` and ``frame->headers.cat == + * NGHTTP2_HCAT_REQUEST``. If |session| is configured as server side, + * ``frame->headers.cat`` is either ``NGHTTP2_HCAT_REQUEST`` + * containing request headers or ``NGHTTP2_HCAT_HEADERS`` containing + * trailer headers and never get PUSH_PROMISE in this callback. + * + * For the client applications, ``frame->hd.type`` is either + * ``NGHTTP2_HEADERS`` or ``NGHTTP2_PUSH_PROMISE``. In case of + * ``NGHTTP2_HEADERS``, ``frame->headers.cat == + * NGHTTP2_HCAT_RESPONSE`` means that it is the first response + * headers, but it may be non-final response which is indicated by 1xx + * status code. In this case, there may be zero or more HEADERS frame + * with ``frame->headers.cat == NGHTTP2_HCAT_HEADERS`` which has + * non-final response code and finally client gets exactly one HEADERS + * frame with ``frame->headers.cat == NGHTTP2_HCAT_HEADERS`` + * containing final response headers (non-1xx status code). The + * trailer headers also has ``frame->headers.cat == + * NGHTTP2_HCAT_HEADERS`` which does not contain any status code. + * + * Returning :enum:`NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE` will close + * the stream (promised stream if frame is PUSH_PROMISE) by issuing + * RST_STREAM with :enum:`NGHTTP2_INTERNAL_ERROR`. In this case, + * :type:`nghttp2_on_header_callback` and + * :type:`nghttp2_on_frame_recv_callback` will not be invoked. If a + * different error code is desirable, use + * `nghttp2_submit_rst_stream()` with a desired error code and then + * return :enum:`NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE`. Again, use + * ``frame->push_promise.promised_stream_id`` as stream_id parameter + * in `nghttp2_submit_rst_stream()` if frame is PUSH_PROMISE. + * + * The implementation of this function must return 0 if it succeeds. + * It can return :enum:`NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE` to + * reset the stream (promised stream if frame is PUSH_PROMISE). For + * critical errors, it must return + * :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`. If the other value is + * returned, it is treated as if :enum:`NGHTTP2_ERR_CALLBACK_FAILURE` + * is returned. If :enum:`NGHTTP2_ERR_CALLBACK_FAILURE` is returned, + * `nghttp2_session_mem_recv()` function will immediately return + * :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`. + * + * To set this callback to :type:`nghttp2_session_callbacks`, use + * `nghttp2_session_callbacks_set_on_begin_headers_callback()`. + */ +typedef int (*nghttp2_on_begin_headers_callback)(nghttp2_session *session, + const nghttp2_frame *frame, + void *user_data); + +/** + * @functypedef + * + * Callback function invoked when a header name/value pair is received + * for the |frame|. The |name| of length |namelen| is header name. + * The |value| of length |valuelen| is header value. The |flags| is + * bitwise OR of one or more of :type:`nghttp2_nv_flag`. + * + * If :enum:`NGHTTP2_NV_FLAG_NO_INDEX` is set in |flags|, the receiver + * must not index this name/value pair when forwarding it to the next + * hop. More specifically, "Literal Header Field never Indexed" + * representation must be used in HPACK encoding. + * + * When this callback is invoked, ``frame->hd.type`` is either + * :enum:`NGHTTP2_HEADERS` or :enum:`NGHTTP2_PUSH_PROMISE`. After all + * header name/value pairs are processed with this callback, and no + * error has been detected, :type:`nghttp2_on_frame_recv_callback` + * will be invoked. If there is an error in decompression, + * :type:`nghttp2_on_frame_recv_callback` for the |frame| will not be + * invoked. + * + * Both |name| and |value| are guaranteed to be NULL-terminated. The + * |namelen| and |valuelen| do not include terminal NULL. If + * `nghttp2_option_set_no_http_messaging()` is used with nonzero + * value, NULL character may be included in |name| or |value| before + * terminating NULL. + * + * Please note that unless `nghttp2_option_set_no_http_messaging()` is + * used, nghttp2 library does perform validation against the |name| + * and the |value| using `nghttp2_check_header_name()` and + * `nghttp2_check_header_value()`. In addition to this, nghttp2 + * performs vaidation based on HTTP Messaging rule, which is briefly + * explained in :ref:`http-messaging` section. + * + * If the application uses `nghttp2_session_mem_recv()`, it can return + * :enum:`NGHTTP2_ERR_PAUSE` to make `nghttp2_session_mem_recv()` + * return without processing further input bytes. The memory pointed + * by |frame|, |name| and |value| parameters are retained until + * `nghttp2_session_mem_recv()` or `nghttp2_session_recv()` is called. + * The application must retain the input bytes which was used to + * produce these parameters, because it may refer to the memory region + * included in the input bytes. + * + * Returning :enum:`NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE` will close + * the stream (promised stream if frame is PUSH_PROMISE) by issuing + * RST_STREAM with :enum:`NGHTTP2_INTERNAL_ERROR`. In this case, + * :type:`nghttp2_on_header_callback` and + * :type:`nghttp2_on_frame_recv_callback` will not be invoked. If a + * different error code is desirable, use + * `nghttp2_submit_rst_stream()` with a desired error code and then + * return :enum:`NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE`. Again, use + * ``frame->push_promise.promised_stream_id`` as stream_id parameter + * in `nghttp2_submit_rst_stream()` if frame is PUSH_PROMISE. + * + * The implementation of this function must return 0 if it succeeds. + * It may return :enum:`NGHTTP2_ERR_PAUSE` or + * :enum:`NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE`. For other critical + * failures, it must return :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`. If + * the other nonzero value is returned, it is treated as + * :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`. If + * :enum:`NGHTTP2_ERR_CALLBACK_FAILURE` is returned, + * `nghttp2_session_recv()` and `nghttp2_session_mem_recv()` functions + * immediately return :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`. + * + * To set this callback to :type:`nghttp2_session_callbacks`, use + * `nghttp2_session_callbacks_set_on_header_callback()`. + */ +typedef int (*nghttp2_on_header_callback)(nghttp2_session *session, + const nghttp2_frame *frame, + const uint8_t *name, size_t namelen, + const uint8_t *value, size_t valuelen, + uint8_t flags, void *user_data); + +/** + * @functypedef + * + * Callback function invoked when the library asks application how + * many padding bytes are required for the transmission of the + * |frame|. The application must choose the total length of payload + * including padded bytes in range [frame->hd.length, max_payloadlen], + * inclusive. Choosing number not in this range will be treated as + * :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`. Returning + * ``frame->hd.length`` means no padding is added. Returning + * :enum:`NGHTTP2_ERR_CALLBACK_FAILURE` will make + * `nghttp2_session_send()` and `nghttp2_session_mem_send()` functions + * immediately return :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`. + * + * To set this callback to :type:`nghttp2_session_callbacks`, use + * `nghttp2_session_callbacks_set_select_padding_callback()`. + */ +typedef ssize_t (*nghttp2_select_padding_callback)(nghttp2_session *session, + const nghttp2_frame *frame, + size_t max_payloadlen, + void *user_data); + +/** + * @functypedef + * + * Callback function invoked when library wants to get max length of + * data to send data to the remote peer. The implementation of this + * function should return a value in the following range. [1, + * min(|session_remote_window_size|, |stream_remote_window_size|, + * |remote_max_frame_size|)]. If a value greater than this range is + * returned than the max allow value will be used. Returning a value + * smaller than this range is treated as + * :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`. The |frame_type| is provided + * for future extensibility and identifies the type of frame (see + * :type:`nghttp2_frame_type`) for which to get the length for. + * Currently supported frame types are: :enum:`NGHTTP2_DATA`. + * + * This callback can be used to control the length in bytes for which + * :type:`nghttp2_data_source_read_callback` is allowed to send to the + * remote endpoint. This callback is optional. Returning + * :enum:`NGHTTP2_ERR_CALLBACK_FAILURE` will signal the entire session + * failure. + * + * To set this callback to :type:`nghttp2_session_callbacks`, use + * `nghttp2_session_callbacks_set_data_source_read_length_callback()`. + */ +typedef ssize_t (*nghttp2_data_source_read_length_callback)( + nghttp2_session *session, uint8_t frame_type, int32_t stream_id, + int32_t session_remote_window_size, int32_t stream_remote_window_size, + uint32_t remote_max_frame_size, void *user_data); + +/** + * @functypedef + * + * Callback function invoked when a frame header is received. The + * |hd| points to received frame header. + * + * Unlike :type:`nghttp2_on_frame_recv_callback`, this callback will + * also be called when frame header of CONTINUATION frame is received. + * + * If both :type:`nghttp2_on_begin_frame_callback` and + * :type:`nghttp2_on_begin_headers_callback` are set and HEADERS or + * PUSH_PROMISE is received, :type:`nghttp2_on_begin_frame_callback` + * will be called first. + * + * The implementation of this function must return 0 if it succeeds. + * If nonzero value is returned, it is treated as fatal error and + * `nghttp2_session_recv()` and `nghttp2_session_mem_recv()` functions + * immediately return :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`. + * + * To set this callback to :type:`nghttp2_session_callbacks`, use + * `nghttp2_session_callbacks_set_on_begin_frame_callback()`. + */ +typedef int (*nghttp2_on_begin_frame_callback)(nghttp2_session *session, + const nghttp2_frame_hd *hd, + void *user_data); + +struct nghttp2_session_callbacks; + +/** + * @struct + * + * Callback functions for :type:`nghttp2_session`. The details of + * this structure are intentionally hidden from the public API. + */ +typedef struct nghttp2_session_callbacks nghttp2_session_callbacks; + +/** + * @function + * + * Initializes |*callbacks_ptr| with NULL values. + * + * The initialized object can be used when initializing multiple + * :type:`nghttp2_session` objects. + * + * When the application finished using this object, it can use + * `nghttp2_session_callbacks_del()` to free its memory. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`NGHTTP2_ERR_NOMEM` + * Out of memory. + */ +NGHTTP2_EXTERN int +nghttp2_session_callbacks_new(nghttp2_session_callbacks **callbacks_ptr); + +/** + * @function + * + * Frees any resources allocated for |callbacks|. If |callbacks| is + * ``NULL``, this function does nothing. + */ +NGHTTP2_EXTERN void +nghttp2_session_callbacks_del(nghttp2_session_callbacks *callbacks); + +/** + * @function + * + * Sets callback function invoked when a session wants to send data to + * the remote peer. This callback is not necessary if the application + * uses solely `nghttp2_session_mem_send()` to serialize data to + * transmit. + */ +NGHTTP2_EXTERN void nghttp2_session_callbacks_set_send_callback( + nghttp2_session_callbacks *cbs, nghttp2_send_callback send_callback); + +/** + * @function + * + * Sets callback function invoked when the a session wants to receive + * data from the remote peer. This callback is not necessary if the + * application uses solely `nghttp2_session_mem_recv()` to process + * received data. + */ +NGHTTP2_EXTERN void nghttp2_session_callbacks_set_recv_callback( + nghttp2_session_callbacks *cbs, nghttp2_recv_callback recv_callback); + +/** + * @function + * + * Sets callback function invoked by `nghttp2_session_recv()` and + * `nghttp2_session_mem_recv()` when a frame is received. + */ +NGHTTP2_EXTERN void nghttp2_session_callbacks_set_on_frame_recv_callback( + nghttp2_session_callbacks *cbs, + nghttp2_on_frame_recv_callback on_frame_recv_callback); + +/** + * @function + * + * Sets callback function invoked by `nghttp2_session_recv()` and + * `nghttp2_session_mem_recv()` when an invalid non-DATA frame is + * received. + */ +NGHTTP2_EXTERN void +nghttp2_session_callbacks_set_on_invalid_frame_recv_callback( + nghttp2_session_callbacks *cbs, + nghttp2_on_invalid_frame_recv_callback on_invalid_frame_recv_callback); + +/** + * @function + * + * Sets callback function invoked when a chunk of data in DATA frame + * is received. + */ +NGHTTP2_EXTERN void nghttp2_session_callbacks_set_on_data_chunk_recv_callback( + nghttp2_session_callbacks *cbs, + nghttp2_on_data_chunk_recv_callback on_data_chunk_recv_callback); + +/** + * @function + * + * Sets callback function invoked before a non-DATA frame is sent. + */ +NGHTTP2_EXTERN void nghttp2_session_callbacks_set_before_frame_send_callback( + nghttp2_session_callbacks *cbs, + nghttp2_before_frame_send_callback before_frame_send_callback); + +/** + * @function + * + * Sets callback function invoked after a frame is sent. + */ +NGHTTP2_EXTERN void nghttp2_session_callbacks_set_on_frame_send_callback( + nghttp2_session_callbacks *cbs, + nghttp2_on_frame_send_callback on_frame_send_callback); + +/** + * @function + * + * Sets callback function invoked when a non-DATA frame is not sent + * because of an error. + */ +NGHTTP2_EXTERN void nghttp2_session_callbacks_set_on_frame_not_send_callback( + nghttp2_session_callbacks *cbs, + nghttp2_on_frame_not_send_callback on_frame_not_send_callback); + +/** + * @function + * + * Sets callback function invoked when the stream is closed. + */ +NGHTTP2_EXTERN void nghttp2_session_callbacks_set_on_stream_close_callback( + nghttp2_session_callbacks *cbs, + nghttp2_on_stream_close_callback on_stream_close_callback); + +/** + * @function + * + * Sets callback function invoked when the reception of header block + * in HEADERS or PUSH_PROMISE is started. + */ +NGHTTP2_EXTERN void nghttp2_session_callbacks_set_on_begin_headers_callback( + nghttp2_session_callbacks *cbs, + nghttp2_on_begin_headers_callback on_begin_headers_callback); + +/** + * @function + * + * Sets callback function invoked when a header name/value pair is + * received. + */ +NGHTTP2_EXTERN void nghttp2_session_callbacks_set_on_header_callback( + nghttp2_session_callbacks *cbs, + nghttp2_on_header_callback on_header_callback); + +/** + * @function + * + * Sets callback function invoked when the library asks application + * how many padding bytes are required for the transmission of the + * given frame. + */ +NGHTTP2_EXTERN void nghttp2_session_callbacks_set_select_padding_callback( + nghttp2_session_callbacks *cbs, + nghttp2_select_padding_callback select_padding_callback); + +/** + * @function + * + * Sets callback function determine the length allowed in + * :type:`nghttp2_data_source_read_callback`. + */ +NGHTTP2_EXTERN void +nghttp2_session_callbacks_set_data_source_read_length_callback( + nghttp2_session_callbacks *cbs, + nghttp2_data_source_read_length_callback data_source_read_length_callback); + +/** + * @function + * + * Sets callback function invoked when a frame header is received. + */ +NGHTTP2_EXTERN void nghttp2_session_callbacks_set_on_begin_frame_callback( + nghttp2_session_callbacks *cbs, + nghttp2_on_begin_frame_callback on_begin_frame_callback); + +/** + * @function + * + * Sets callback function invoked when + * :enum:`NGHTTP2_DATA_FLAG_NO_COPY` is used in + * :type:`nghttp2_data_source_read_callback` to avoid data copy. + */ +NGHTTP2_EXTERN void nghttp2_session_callbacks_set_send_data_callback( + nghttp2_session_callbacks *cbs, + nghttp2_send_data_callback send_data_callback); + +/** + * @functypedef + * + * Custom memory allocator to replace malloc(). The |mem_user_data| + * is the mem_user_data member of :type:`nghttp2_mem` structure. + */ +typedef void *(*nghttp2_malloc)(size_t size, void *mem_user_data); + +/** + * @functypedef + * + * Custom memory allocator to replace free(). The |mem_user_data| is + * the mem_user_data member of :type:`nghttp2_mem` structure. + */ +typedef void (*nghttp2_free)(void *ptr, void *mem_user_data); + +/** + * @functypedef + * + * Custom memory allocator to replace calloc(). The |mem_user_data| + * is the mem_user_data member of :type:`nghttp2_mem` structure. + */ +typedef void *(*nghttp2_calloc)(size_t nmemb, size_t size, void *mem_user_data); + +/** + * @functypedef + * + * Custom memory allocator to replace realloc(). The |mem_user_data| + * is the mem_user_data member of :type:`nghttp2_mem` structure. + */ +typedef void *(*nghttp2_realloc)(void *ptr, size_t size, void *mem_user_data); + +/** + * @struct + * + * Custom memory allocator functions and user defined pointer. The + * |mem_user_data| member is passed to each allocator function. This + * can be used, for example, to achieve per-session memory pool. + * + * In the following example code, ``my_malloc``, ``my_free``, + * ``my_calloc`` and ``my_realloc`` are the replacement of the + * standard allocators ``malloc``, ``free``, ``calloc`` and + * ``realloc`` respectively:: + * + * void *my_malloc_cb(size_t size, void *mem_user_data) { + * return my_malloc(size); + * } + * + * void my_free_cb(void *ptr, void *mem_user_data) { my_free(ptr); } + * + * void *my_calloc_cb(size_t nmemb, size_t size, void *mem_user_data) { + * return my_calloc(nmemb, size); + * } + * + * void *my_realloc_cb(void *ptr, size_t size, void *mem_user_data) { + * return my_realloc(ptr, size); + * } + * + * void session_new() { + * nghttp2_session *session; + * nghttp2_session_callbacks *callbacks; + * nghttp2_mem mem = {NULL, my_malloc_cb, my_free_cb, my_calloc_cb, + * my_realloc_cb}; + * + * ... + * + * nghttp2_session_client_new3(&session, callbacks, NULL, NULL, &mem); + * + * ... + * } + */ +typedef struct { + /** + * An arbitrary user supplied data. This is passed to each + * allocator function. + */ + void *mem_user_data; + /** + * Custom allocator function to replace malloc(). + */ + nghttp2_malloc malloc; + /** + * Custom allocator function to replace free(). + */ + nghttp2_free free; + /** + * Custom allocator function to replace calloc(). + */ + nghttp2_calloc calloc; + /** + * Custom allocator function to replace realloc(). + */ + nghttp2_realloc realloc; +} nghttp2_mem; + +struct nghttp2_option; + +/** + * @struct + * + * Configuration options for :type:`nghttp2_session`. The details of + * this structure are intentionally hidden from the public API. + */ +typedef struct nghttp2_option nghttp2_option; + +/** + * @function + * + * Initializes |*option_ptr| with default values. + * + * When the application finished using this object, it can use + * `nghttp2_option_del()` to free its memory. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`NGHTTP2_ERR_NOMEM` + * Out of memory. + */ +NGHTTP2_EXTERN int nghttp2_option_new(nghttp2_option **option_ptr); + +/** + * @function + * + * Frees any resources allocated for |option|. If |option| is + * ``NULL``, this function does nothing. + */ +NGHTTP2_EXTERN void nghttp2_option_del(nghttp2_option *option); + +/** + * @function + * + * This option prevents the library from sending WINDOW_UPDATE for a + * connection automatically. If this option is set to nonzero, the + * library won't send WINDOW_UPDATE for DATA until application calls + * `nghttp2_session_consume()` to indicate the consumed amount of + * data. Don't use `nghttp2_submit_window_update()` for this purpose. + * By default, this option is set to zero. + */ +NGHTTP2_EXTERN void +nghttp2_option_set_no_auto_window_update(nghttp2_option *option, int val); + +/** + * @function + * + * This option sets the SETTINGS_MAX_CONCURRENT_STREAMS value of + * remote endpoint as if it is received in SETTINGS frame. Without + * specifying this option, before the local endpoint receives + * SETTINGS_MAX_CONCURRENT_STREAMS in SETTINGS frame from remote + * endpoint, SETTINGS_MAX_CONCURRENT_STREAMS is unlimited. This may + * cause problem if local endpoint submits lots of requests initially + * and sending them at once to the remote peer may lead to the + * rejection of some requests. Specifying this option to the sensible + * value, say 100, may avoid this kind of issue. This value will be + * overwritten if the local endpoint receives + * SETTINGS_MAX_CONCURRENT_STREAMS from the remote endpoint. + */ +NGHTTP2_EXTERN void +nghttp2_option_set_peer_max_concurrent_streams(nghttp2_option *option, + uint32_t val); + +/** + * @function + * + * By default, nghttp2 library, if configured as server, requires + * first 24 bytes of client magic byte string (MAGIC). In most cases, + * this will simplify the implementation of server. But sometimes + * server may want to detect the application protocol based on first + * few bytes on clear text communication. + * + * If this option is used with nonzero |val|, nghttp2 library does not + * handle MAGIC. It still checks following SETTINGS frame. This + * means that applications should deal with MAGIC by themselves. + * + * If this option is not used or used with zero value, if MAGIC does + * not match :macro:`NGHTTP2_CLIENT_MAGIC`, `nghttp2_session_recv()` + * and `nghttp2_session_mem_recv()` will return error + * :enum:`NGHTTP2_ERR_BAD_CLIENT_MAGIC`, which is fatal error. + */ +NGHTTP2_EXTERN void +nghttp2_option_set_no_recv_client_magic(nghttp2_option *option, int val); + +/** + * @function + * + * By default, nghttp2 library enforces subset of HTTP Messaging rules + * described in `HTTP/2 specification, section 8 + * `_. See + * :ref:`http-messaging` section for details. For those applications + * who use nghttp2 library as non-HTTP use, give nonzero to |val| to + * disable this enforcement. + */ +NGHTTP2_EXTERN void nghttp2_option_set_no_http_messaging(nghttp2_option *option, + int val); + +/** + * @function + * + * Initializes |*session_ptr| for client use. The all members of + * |callbacks| are copied to |*session_ptr|. Therefore |*session_ptr| + * does not store |callbacks|. The |user_data| is an arbitrary user + * supplied data, which will be passed to the callback functions. + * + * The :type:`nghttp2_send_callback` must be specified. If the + * application code uses `nghttp2_session_recv()`, the + * :type:`nghttp2_recv_callback` must be specified. The other members + * of |callbacks| can be ``NULL``. + * + * If this function fails, |*session_ptr| is left untouched. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`NGHTTP2_ERR_NOMEM` + * Out of memory. + */ +NGHTTP2_EXTERN int +nghttp2_session_client_new(nghttp2_session **session_ptr, + const nghttp2_session_callbacks *callbacks, + void *user_data); + +/** + * @function + * + * Initializes |*session_ptr| for server use. The all members of + * |callbacks| are copied to |*session_ptr|. Therefore |*session_ptr| + * does not store |callbacks|. The |user_data| is an arbitrary user + * supplied data, which will be passed to the callback functions. + * + * The :type:`nghttp2_send_callback` must be specified. If the + * application code uses `nghttp2_session_recv()`, the + * :type:`nghttp2_recv_callback` must be specified. The other members + * of |callbacks| can be ``NULL``. + * + * If this function fails, |*session_ptr| is left untouched. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`NGHTTP2_ERR_NOMEM` + * Out of memory. + */ +NGHTTP2_EXTERN int +nghttp2_session_server_new(nghttp2_session **session_ptr, + const nghttp2_session_callbacks *callbacks, + void *user_data); + +/** + * @function + * + * Like `nghttp2_session_client_new()`, but with additional options + * specified in the |option|. + * + * The |option| can be ``NULL`` and the call is equivalent to + * `nghttp2_session_client_new()`. + * + * This function does not take ownership |option|. The application is + * responsible for freeing |option| if it finishes using the object. + * + * The library code does not refer to |option| after this function + * returns. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`NGHTTP2_ERR_NOMEM` + * Out of memory. + */ +NGHTTP2_EXTERN int +nghttp2_session_client_new2(nghttp2_session **session_ptr, + const nghttp2_session_callbacks *callbacks, + void *user_data, const nghttp2_option *option); + +/** + * @function + * + * Like `nghttp2_session_server_new()`, but with additional options + * specified in the |option|. + * + * The |option| can be ``NULL`` and the call is equivalent to + * `nghttp2_session_server_new()`. + * + * This function does not take ownership |option|. The application is + * responsible for freeing |option| if it finishes using the object. + * + * The library code does not refer to |option| after this function + * returns. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`NGHTTP2_ERR_NOMEM` + * Out of memory. + */ +NGHTTP2_EXTERN int +nghttp2_session_server_new2(nghttp2_session **session_ptr, + const nghttp2_session_callbacks *callbacks, + void *user_data, const nghttp2_option *option); + +/** + * @function + * + * Like `nghttp2_session_client_new2()`, but with additional custom + * memory allocator specified in the |mem|. + * + * The |mem| can be ``NULL`` and the call is equivalent to + * `nghttp2_session_client_new2()`. + * + * This function does not take ownership |mem|. The application is + * responsible for freeing |mem|. + * + * The library code does not refer to |mem| pointer after this + * function returns, so the application can safely free it. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`NGHTTP2_ERR_NOMEM` + * Out of memory. + */ +NGHTTP2_EXTERN int nghttp2_session_client_new3( + nghttp2_session **session_ptr, const nghttp2_session_callbacks *callbacks, + void *user_data, const nghttp2_option *option, nghttp2_mem *mem); + +/** + * @function + * + * Like `nghttp2_session_server_new2()`, but with additional custom + * memory allocator specified in the |mem|. + * + * The |mem| can be ``NULL`` and the call is equivalent to + * `nghttp2_session_server_new2()`. + * + * This function does not take ownership |mem|. The application is + * responsible for freeing |mem|. + * + * The library code does not refer to |mem| pointer after this + * function returns, so the application can safely free it. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`NGHTTP2_ERR_NOMEM` + * Out of memory. + */ +NGHTTP2_EXTERN int nghttp2_session_server_new3( + nghttp2_session **session_ptr, const nghttp2_session_callbacks *callbacks, + void *user_data, const nghttp2_option *option, nghttp2_mem *mem); + +/** + * @function + * + * Frees any resources allocated for |session|. If |session| is + * ``NULL``, this function does nothing. + */ +NGHTTP2_EXTERN void nghttp2_session_del(nghttp2_session *session); + +/** + * @function + * + * Sends pending frames to the remote peer. + * + * This function retrieves the highest prioritized frame from the + * outbound queue and sends it to the remote peer. It does this as + * many as possible until the user callback + * :type:`nghttp2_send_callback` returns + * :enum:`NGHTTP2_ERR_WOULDBLOCK` or the outbound queue becomes empty. + * This function calls several callback functions which are passed + * when initializing the |session|. Here is the simple time chart + * which tells when each callback is invoked: + * + * 1. Get the next frame to send from outbound queue. + * + * 2. Prepare transmission of the frame. + * + * 3. If the control frame cannot be sent because some preconditions + * are not met (e.g., request HEADERS cannot be sent after GOAWAY), + * :type:`nghttp2_on_frame_not_send_callback` is invoked. Abort + * the following steps. + * + * 4. If the frame is HEADERS, PUSH_PROMISE or DATA, + * :type:`nghttp2_select_padding_callback` is invoked. + * + * 5. If the frame is request HEADERS, the stream is opened here. + * + * 6. :type:`nghttp2_before_frame_send_callback` is invoked. + * + * 7. :type:`nghttp2_send_callback` is invoked one or more times to + * send the frame. + * + * 8. :type:`nghttp2_on_frame_send_callback` is invoked. + * + * 9. If the transmission of the frame triggers closure of the stream, + * the stream is closed and + * :type:`nghttp2_on_stream_close_callback` is invoked. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`NGHTTP2_ERR_NOMEM` + * Out of memory. + * :enum:`NGHTTP2_ERR_CALLBACK_FAILURE` + * The callback function failed. + */ +NGHTTP2_EXTERN int nghttp2_session_send(nghttp2_session *session); + +/** + * @function + * + * Returns the serialized data to send. + * + * This function behaves like `nghttp2_session_send()` except that it + * does not use :type:`nghttp2_send_callback` to transmit data. + * Instead, it assigns the pointer to the serialized data to the + * |*data_ptr| and returns its length. The other callbacks are called + * in the same way as they are in `nghttp2_session_send()`. + * + * If no data is available to send, this function returns 0. + * + * This function may not return all serialized data in one invocation. + * To get all data, call this function repeatedly until it returns 0 + * or one of negative error codes. + * + * The assigned |*data_ptr| is valid until the next call of + * `nghttp2_session_mem_send()` or `nghttp2_session_send()`. + * + * The caller must send all data before sending the next chunk of + * data. + * + * This function returns the length of the data pointed by the + * |*data_ptr| if it succeeds, or one of the following negative error + * codes: + * + * :enum:`NGHTTP2_ERR_NOMEM` + * Out of memory. + */ +NGHTTP2_EXTERN ssize_t nghttp2_session_mem_send(nghttp2_session *session, + const uint8_t **data_ptr); + +/** + * @function + * + * Receives frames from the remote peer. + * + * This function receives as many frames as possible until the user + * callback :type:`nghttp2_recv_callback` returns + * :enum:`NGHTTP2_ERR_WOULDBLOCK`. This function calls several + * callback functions which are passed when initializing the + * |session|. Here is the simple time chart which tells when each + * callback is invoked: + * + * 1. :type:`nghttp2_recv_callback` is invoked one or more times to + * receive frame header. + * + * 2. When frame header is received, + * :type:`nghttp2_on_begin_frame_callback` is invoked. + * + * 3. If the frame is DATA frame: + * + * 1. :type:`nghttp2_recv_callback` is invoked to receive DATA + * payload. For each chunk of data, + * :type:`nghttp2_on_data_chunk_recv_callback` is invoked. + * + * 2. If one DATA frame is completely received, + * :type:`nghttp2_on_frame_recv_callback` is invoked. If the + * reception of the frame triggers the closure of the stream, + * :type:`nghttp2_on_stream_close_callback` is invoked. + * + * 4. If the frame is the control frame: + * + * 1. :type:`nghttp2_recv_callback` is invoked one or more times to + * receive whole frame. + * + * 2. If the received frame is valid, then following actions are + * taken. If the frame is either HEADERS or PUSH_PROMISE, + * :type:`nghttp2_on_begin_headers_callback` is invoked. Then + * :type:`nghttp2_on_header_callback` is invoked for each header + * name/value pair. After all name/value pairs are emitted + * successfully, :type:`nghttp2_on_frame_recv_callback` is + * invoked. For other frames, + * :type:`nghttp2_on_frame_recv_callback` is invoked. If the + * reception of the frame triggers the closure of the stream, + * :type:`nghttp2_on_stream_close_callback` is invoked. + * + * 3. If the received frame is unpacked but is interpreted as + * invalid, :type:`nghttp2_on_invalid_frame_recv_callback` is + * invoked. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`NGHTTP2_ERR_EOF` + * The remote peer did shutdown on the connection. + * :enum:`NGHTTP2_ERR_NOMEM` + * Out of memory. + * :enum:`NGHTTP2_ERR_CALLBACK_FAILURE` + * The callback function failed. + * :enum:`NGHTTP2_ERR_BAD_CLIENT_MAGIC` + * Invalid client magic was detected. This error only returns + * when |session| was configured as server and + * `nghttp2_option_set_no_recv_client_magic()` is not used with + * nonzero value. + */ +NGHTTP2_EXTERN int nghttp2_session_recv(nghttp2_session *session); + +/** + * @function + * + * Processes data |in| as an input from the remote endpoint. The + * |inlen| indicates the number of bytes in the |in|. + * + * This function behaves like `nghttp2_session_recv()` except that it + * does not use :type:`nghttp2_recv_callback` to receive data; the + * |in| is the only data for the invocation of this function. If all + * bytes are processed, this function returns. The other callbacks + * are called in the same way as they are in `nghttp2_session_recv()`. + * + * In the current implementation, this function always tries to + * processes all input data unless either an error occurs or + * :enum:`NGHTTP2_ERR_PAUSE` is returned from + * :type:`nghttp2_on_header_callback` or + * :type:`nghttp2_on_data_chunk_recv_callback`. If + * :enum:`NGHTTP2_ERR_PAUSE` is used, the return value includes the + * number of bytes which was used to produce the data or frame for the + * callback. + * + * This function returns the number of processed bytes, or one of the + * following negative error codes: + * + * :enum:`NGHTTP2_ERR_NOMEM` + * Out of memory. + * :enum:`NGHTTP2_ERR_CALLBACK_FAILURE` + * The callback function failed. + * :enum:`NGHTTP2_ERR_BAD_CLIENT_MAGIC` + * Invalid client magic was detected. This error only returns + * when |session| was configured as server and + * `nghttp2_option_set_no_recv_client_magic()` is not used with + * nonzero value. + */ +NGHTTP2_EXTERN ssize_t nghttp2_session_mem_recv(nghttp2_session *session, + const uint8_t *in, + size_t inlen); + +/** + * @function + * + * Puts back previously deferred DATA frame in the stream |stream_id| + * to the outbound queue. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`NGHTTP2_ERR_INVALID_ARGUMENT` + * The stream does not exist; or no deferred data exist. + * :enum:`NGHTTP2_ERR_NOMEM` + * Out of memory. + */ +NGHTTP2_EXTERN int nghttp2_session_resume_data(nghttp2_session *session, + int32_t stream_id); + +/** + * @function + * + * Returns nonzero value if |session| wants to receive data from the + * remote peer. + * + * If both `nghttp2_session_want_read()` and + * `nghttp2_session_want_write()` return 0, the application should + * drop the connection. + */ +NGHTTP2_EXTERN int nghttp2_session_want_read(nghttp2_session *session); + +/** + * @function + * + * Returns nonzero value if |session| wants to send data to the remote + * peer. + * + * If both `nghttp2_session_want_read()` and + * `nghttp2_session_want_write()` return 0, the application should + * drop the connection. + */ +NGHTTP2_EXTERN int nghttp2_session_want_write(nghttp2_session *session); + +/** + * @function + * + * Returns stream_user_data for the stream |stream_id|. The + * stream_user_data is provided by `nghttp2_submit_request()`, + * `nghttp2_submit_headers()` or + * `nghttp2_session_set_stream_user_data()`. Unless it is set using + * `nghttp2_session_set_stream_user_data()`, if the stream is + * initiated by the remote endpoint, stream_user_data is always + * ``NULL``. If the stream does not exist, this function returns + * ``NULL``. + */ +NGHTTP2_EXTERN void * +nghttp2_session_get_stream_user_data(nghttp2_session *session, + int32_t stream_id); + +/** + * @function + * + * Sets the |stream_user_data| to the stream denoted by the + * |stream_id|. If a stream user data is already set to the stream, + * it is replaced with the |stream_user_data|. It is valid to specify + * ``NULL`` in the |stream_user_data|, which nullifies the associated + * data pointer. + * + * It is valid to set the |stream_user_data| to the stream reserved by + * PUSH_PROMISE frame. + * + * This function returns 0 if it succeeds, or one of following + * negative error codes: + * + * :enum:`NGHTTP2_ERR_INVALID_ARGUMENT` + * The stream does not exist + */ +NGHTTP2_EXTERN int +nghttp2_session_set_stream_user_data(nghttp2_session *session, + int32_t stream_id, void *stream_user_data); + +/** + * @function + * + * Returns the number of frames in the outbound queue. This does not + * include the deferred DATA frames. + */ +NGHTTP2_EXTERN size_t + nghttp2_session_get_outbound_queue_size(nghttp2_session *session); + +/** + * @function + * + * Returns the number of DATA payload in bytes received without + * WINDOW_UPDATE transmission for the stream |stream_id|. The local + * (receive) window size can be adjusted by + * `nghttp2_submit_window_update()`. This function takes into account + * that and returns effective data length. In particular, if the + * local window size is reduced by submitting negative + * window_size_increment with `nghttp2_submit_window_update()`, this + * function returns the number of bytes less than actually received. + * + * This function returns -1 if it fails. + */ +NGHTTP2_EXTERN int32_t nghttp2_session_get_stream_effective_recv_data_length( + nghttp2_session *session, int32_t stream_id); + +/** + * @function + * + * Returns the local (receive) window size for the stream |stream_id|. + * The local window size can be adjusted by + * `nghttp2_submit_window_update()`. This function takes into account + * that and returns effective window size. + * + * This function returns -1 if it fails. + */ +NGHTTP2_EXTERN int32_t nghttp2_session_get_stream_effective_local_window_size( + nghttp2_session *session, int32_t stream_id); + +/** + * @function + * + * Returns the number of DATA payload in bytes received without + * WINDOW_UPDATE transmission for a connection. The local (receive) + * window size can be adjusted by `nghttp2_submit_window_update()`. + * This function takes into account that and returns effective data + * length. In particular, if the local window size is reduced by + * submitting negative window_size_increment with + * `nghttp2_submit_window_update()`, this function returns the number + * of bytes less than actually received. + * + * This function returns -1 if it fails. + */ +NGHTTP2_EXTERN int32_t + nghttp2_session_get_effective_recv_data_length(nghttp2_session *session); + +/** + * @function + * + * Returns the local (receive) window size for a connection. The + * local window size can be adjusted by + * `nghttp2_submit_window_update()`. This function takes into account + * that and returns effective window size. + * + * This function returns -1 if it fails. + */ +NGHTTP2_EXTERN int32_t + nghttp2_session_get_effective_local_window_size(nghttp2_session *session); + +/** + * @function + * + * Returns the remote window size for a given stream |stream_id|. + * + * This is the amount of flow-controlled payload (e.g., DATA) that the + * local endpoint can send without stream level WINDOW_UPDATE. There + * is also connection level flow control, so the effective size of + * payload that the local endpoint can actually send is + * min(`nghttp2_session_get_stream_remote_window_size()`, + * `nghttp2_session_get_remote_window_size()`). + * + * This function returns -1 if it fails. + */ +NGHTTP2_EXTERN int32_t + nghttp2_session_get_stream_remote_window_size(nghttp2_session *session, + int32_t stream_id); + +/** + * @function + * + * Returns the remote window size for a connection. + * + * This function always succeeds. + */ +NGHTTP2_EXTERN int32_t + nghttp2_session_get_remote_window_size(nghttp2_session *session); + +/** + * @function + * + * Returns 1 if local peer half closed the given stream |stream_id|. + * Returns 0 if it did not. Returns -1 if no such stream exists. + */ +NGHTTP2_EXTERN int +nghttp2_session_get_stream_local_close(nghttp2_session *session, + int32_t stream_id); + +/** + * @function + * + * Returns 1 if remote peer half closed the given stream |stream_id|. + * Returns 0 if it did not. Returns -1 if no such stream exists. + */ +NGHTTP2_EXTERN int +nghttp2_session_get_stream_remote_close(nghttp2_session *session, + int32_t stream_id); + +/** + * @function + * + * Signals the session so that the connection should be terminated. + * + * The last stream ID is the minimum value between the stream ID of a + * stream for which :type:`nghttp2_on_frame_recv_callback` was called + * most recently and the last stream ID we have sent to the peer + * previously. + * + * The |error_code| is the error code of this GOAWAY frame. The + * pre-defined error code is one of :enum:`nghttp2_error_code`. + * + * After the transmission, both `nghttp2_session_want_read()` and + * `nghttp2_session_want_write()` return 0. + * + * This function should be called when the connection should be + * terminated after sending GOAWAY. If the remaining streams should + * be processed after GOAWAY, use `nghttp2_submit_goaway()` instead. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`NGHTTP2_ERR_NOMEM` + * Out of memory. + */ +NGHTTP2_EXTERN int nghttp2_session_terminate_session(nghttp2_session *session, + uint32_t error_code); + +/** + * @function + * + * Signals the session so that the connection should be terminated. + * + * This function behaves like `nghttp2_session_terminate_session()`, + * but the last stream ID can be specified by the application for fine + * grained control of stream. The HTTP/2 specification does not allow + * last_stream_id to be increased. So the actual value sent as + * last_stream_id is the minimum value between the given + * |last_stream_id| and the last_stream_id we have previously sent to + * the peer. + * + * The |last_stream_id| is peer's stream ID or 0. So if |session| is + * initialized as client, |last_stream_id| must be even or 0. If + * |session| is initialized as server, |last_stream_id| must be odd or + * 0. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`NGHTTP2_ERR_NOMEM` + * Out of memory. + * :enum:`NGHTTP2_ERR_INVALID_ARGUMENT` + * The |last_stream_id| is invalid. + */ +NGHTTP2_EXTERN int nghttp2_session_terminate_session2(nghttp2_session *session, + int32_t last_stream_id, + uint32_t error_code); + +/** + * @function + * + * Signals to the client that the server started graceful shutdown + * procedure. + * + * This function is only usable for server. If this function is + * called with client side session, this function returns + * :enum:`NGHTTP2_ERR_INVALID_STATE`. + * + * To gracefully shutdown HTTP/2 session, server should call this + * function to send GOAWAY with last_stream_id (1u << 31) - 1. And + * after some delay (e.g., 1 RTT), send another GOAWAY with the stream + * ID that the server has some processing using + * `nghttp2_submit_goaway()`. See also + * `nghttp2_session_get_last_proc_stream_id()`. + * + * Unlike `nghttp2_submit_goaway()`, this function just sends GOAWAY + * and does nothing more. This is a mere indication to the client + * that session shutdown is imminent. The application should call + * `nghttp2_submit_goaway()` with appropriate last_stream_id after + * this call. + * + * If one or more GOAWAY frame have been already sent by either + * `nghttp2_submit_goaway()` or `nghttp2_session_terminate_session()`, + * this function has no effect. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`NGHTTP2_ERR_NOMEM` + * Out of memory. + * :enum:`NGHTTP2_ERR_INVALID_STATE` + * The |session| is initialized as client. + */ +NGHTTP2_EXTERN int nghttp2_submit_shutdown_notice(nghttp2_session *session); + +/** + * @function + * + * Returns the value of SETTINGS |id| notified by a remote endpoint. + * The |id| must be one of values defined in + * :enum:`nghttp2_settings_id`. + */ +NGHTTP2_EXTERN uint32_t + nghttp2_session_get_remote_settings(nghttp2_session *session, + nghttp2_settings_id id); + +/** + * @function + * + * Tells the |session| that next stream ID is |next_stream_id|. The + * |next_stream_id| must be equal or greater than the value returned + * by `nghttp2_session_get_next_stream_id()`. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`NGHTTP2_ERR_INVALID_ARGUMENT` + * The |next_stream_id| is strictly less than the value + * `nghttp2_session_get_next_stream_id()` returns; or + * |next_stream_id| is invalid (e.g., even integer for client, or + * odd integer for server). + */ +NGHTTP2_EXTERN int nghttp2_session_set_next_stream_id(nghttp2_session *session, + int32_t next_stream_id); + +/** + * @function + * + * Returns the next outgoing stream ID. Notice that return type is + * uint32_t. If we run out of stream ID for this session, this + * function returns 1 << 31. + */ +NGHTTP2_EXTERN uint32_t + nghttp2_session_get_next_stream_id(nghttp2_session *session); + +/** + * @function + * + * Tells the |session| that |size| bytes for a stream denoted by + * |stream_id| were consumed by application and are ready to + * WINDOW_UPDATE. The consumed bytes are counted towards both + * connection and stream level WINDOW_UPDATE (see + * `nghttp2_session_consume_connection()` and + * `nghttp2_session_consume_stream()` to update consumption + * independently). This function is intended to be used without + * automatic window update (see + * `nghttp2_option_set_no_auto_window_update()`). + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`NGHTTP2_ERR_NOMEM` + * Out of memory. + * :enum:`NGHTTP2_ERR_INVALID_ARGUMENT` + * The |stream_id| is 0. + * :enum:`NGHTTP2_ERR_INVALID_STATE` + * Automatic WINDOW_UPDATE is not disabled. + */ +NGHTTP2_EXTERN int nghttp2_session_consume(nghttp2_session *session, + int32_t stream_id, size_t size); + +/** + * @function + * + * Like `nghttp2_session_consume()`, but this only tells library that + * |size| bytes were consumed only for connection level. Note that + * HTTP/2 maintains connection and stream level flow control windows + * independently. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`NGHTTP2_ERR_NOMEM` + * Out of memory. + * :enum:`NGHTTP2_ERR_INVALID_STATE` + * Automatic WINDOW_UPDATE is not disabled. + */ +NGHTTP2_EXTERN int nghttp2_session_consume_connection(nghttp2_session *session, + size_t size); + +/** + * @function + * + * Like `nghttp2_session_consume()`, but this only tells library that + * |size| bytes were consumed only for stream denoted by |stream_id|. + * Note that HTTP/2 maintains connection and stream level flow control + * windows independently. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`NGHTTP2_ERR_NOMEM` + * Out of memory. + * :enum:`NGHTTP2_ERR_INVALID_ARGUMENT` + * The |stream_id| is 0. + * :enum:`NGHTTP2_ERR_INVALID_STATE` + * Automatic WINDOW_UPDATE is not disabled. + */ +NGHTTP2_EXTERN int nghttp2_session_consume_stream(nghttp2_session *session, + int32_t stream_id, + size_t size); + +/** + * @function + * + * Performs post-process of HTTP Upgrade request. This function can + * be called from both client and server, but the behavior is very + * different in each other. + * + * If called from client side, the |settings_payload| must be the + * value sent in ``HTTP2-Settings`` header field and must be decoded + * by base64url decoder. The |settings_payloadlen| is the length of + * |settings_payload|. The |settings_payload| is unpacked and its + * setting values will be submitted using `nghttp2_submit_settings()`. + * This means that the client application code does not need to submit + * SETTINGS by itself. The stream with stream ID=1 is opened and the + * |stream_user_data| is used for its stream_user_data. The opened + * stream becomes half-closed (local) state. + * + * If called from server side, the |settings_payload| must be the + * value received in ``HTTP2-Settings`` header field and must be + * decoded by base64url decoder. The |settings_payloadlen| is the + * length of |settings_payload|. It is treated as if the SETTINGS + * frame with that payload is received. Thus, callback functions for + * the reception of SETTINGS frame will be invoked. The stream with + * stream ID=1 is opened. The |stream_user_data| is ignored. The + * opened stream becomes half-closed (remote). + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`NGHTTP2_ERR_NOMEM` + * Out of memory. + * :enum:`NGHTTP2_ERR_INVALID_ARGUMENT` + * The |settings_payload| is badly formed. + * :enum:`NGHTTP2_ERR_PROTO` + * The stream ID 1 is already used or closed; or is not available. + */ +NGHTTP2_EXTERN int nghttp2_session_upgrade(nghttp2_session *session, + const uint8_t *settings_payload, + size_t settings_payloadlen, + void *stream_user_data); + +/** + * @function + * + * Serializes the SETTINGS values |iv| in the |buf|. The size of the + * |buf| is specified by |buflen|. The number of entries in the |iv| + * array is given by |niv|. The required space in |buf| for the |niv| + * entries is ``8*niv`` bytes and if the given buffer is too small, an + * error is returned. This function is used mainly for creating a + * SETTINGS payload to be sent with the ``HTTP2-Settings`` header + * field in an HTTP Upgrade request. The data written in |buf| is NOT + * base64url encoded and the application is responsible for encoding. + * + * This function returns the number of bytes written in |buf|, or one + * of the following negative error codes: + * + * :enum:`NGHTTP2_ERR_INVALID_ARGUMENT` + * The |iv| contains duplicate settings ID or invalid value. + * + * :enum:`NGHTTP2_ERR_INSUFF_BUFSIZE` + * The provided |buflen| size is too small to hold the output. + */ +NGHTTP2_EXTERN ssize_t + nghttp2_pack_settings_payload(uint8_t *buf, size_t buflen, + const nghttp2_settings_entry *iv, size_t niv); + +/** + * @function + * + * Returns string describing the |lib_error_code|. The + * |lib_error_code| must be one of the :enum:`nghttp2_error`. + */ +NGHTTP2_EXTERN const char *nghttp2_strerror(int lib_error_code); + +/** + * @function + * + * Initializes |pri_spec| with the |stream_id| of the stream to depend + * on with |weight| and its exclusive flag. If |exclusive| is + * nonzero, exclusive flag is set. + * + * The |weight| must be in [:enum:`NGHTTP2_MIN_WEIGHT`, + * :enum:`NGHTTP2_MAX_WEIGHT`], inclusive. + */ +NGHTTP2_EXTERN void nghttp2_priority_spec_init(nghttp2_priority_spec *pri_spec, + int32_t stream_id, + int32_t weight, int exclusive); + +/** + * @function + * + * Initializes |pri_spec| with the default values. The default values + * are: stream_id = 0, weight = :macro:`NGHTTP2_DEFAULT_WEIGHT` and + * exclusive = 0. + */ +NGHTTP2_EXTERN void +nghttp2_priority_spec_default_init(nghttp2_priority_spec *pri_spec); + +/** + * @function + * + * Returns nonzero if the |pri_spec| is filled with default values. + */ +NGHTTP2_EXTERN int +nghttp2_priority_spec_check_default(const nghttp2_priority_spec *pri_spec); + +/** + * @function + * + * Submits HEADERS frame and optionally one or more DATA frames. + * + * The |pri_spec| is priority specification of this request. ``NULL`` + * means the default priority (see + * `nghttp2_priority_spec_default_init()`). To specify the priority, + * use `nghttp2_priority_spec_init()`. If |pri_spec| is not ``NULL``, + * this function will copy its data members. + * + * The ``pri_spec->weight`` must be in [:enum:`NGHTTP2_MIN_WEIGHT`, + * :enum:`NGHTTP2_MAX_WEIGHT`], inclusive. If ``pri_spec->weight`` is + * strictly less than :enum:`NGHTTP2_MIN_WEIGHT`, it becomes + * :enum:`NGHTTP2_MIN_WEIGHT`. If it is strictly greater than + * :enum:`NGHTTP2_MAX_WEIGHT`, it becomes :enum:`NGHTTP2_MAX_WEIGHT`. + * + * The |nva| is an array of name/value pair :type:`nghttp2_nv` with + * |nvlen| elements. The application is responsible to include + * required pseudo-header fields (header field whose name starts with + * ":") in |nva| and must place pseudo-headers before regular header + * fields. + * + * This function creates copies of all name/value pairs in |nva|. It + * also lower-cases all names in |nva|. The order of elements in + * |nva| is preserved. + * + * HTTP/2 specification has requirement about header fields in the + * request HEADERS. See the specification for more details. + * + * If |data_prd| is not ``NULL``, it provides data which will be sent + * in subsequent DATA frames. In this case, a method that allows + * request message bodies + * (https://tools.ietf.org/html/rfc7231#section-4) must be specified + * with ``:method`` key in |nva| (e.g. ``POST``). This function does + * not take ownership of the |data_prd|. The function copies the + * members of the |data_prd|. If |data_prd| is ``NULL``, HEADERS have + * END_STREAM set. The |stream_user_data| is data associated to the + * stream opened by this request and can be an arbitrary pointer, + * which can be retrieved later by + * `nghttp2_session_get_stream_user_data()`. + * + * This function returns assigned stream ID if it succeeds, or one of + * the following negative error codes: + * + * :enum:`NGHTTP2_ERR_NOMEM` + * Out of memory. + * :enum:`NGHTTP2_ERR_STREAM_ID_NOT_AVAILABLE` + * No stream ID is available because maximum stream ID was + * reached. + * + * .. warning:: + * + * This function returns assigned stream ID if it succeeds. But + * that stream is not opened yet. The application must not submit + * frame to that stream ID before + * :type:`nghttp2_before_frame_send_callback` is called for this + * frame. + * + */ +NGHTTP2_EXTERN int32_t + nghttp2_submit_request(nghttp2_session *session, + const nghttp2_priority_spec *pri_spec, + const nghttp2_nv *nva, size_t nvlen, + const nghttp2_data_provider *data_prd, + void *stream_user_data); + +/** + * @function + * + * Submits response HEADERS frame and optionally one or more DATA + * frames against the stream |stream_id|. + * + * The |nva| is an array of name/value pair :type:`nghttp2_nv` with + * |nvlen| elements. The application is responsible to include + * required pseudo-header fields (header field whose name starts with + * ":") in |nva| and must place pseudo-headers before regular header + * fields. + * + * This function creates copies of all name/value pairs in |nva|. It + * also lower-cases all names in |nva|. The order of elements in + * |nva| is preserved. + * + * HTTP/2 specification has requirement about header fields in the + * response HEADERS. See the specification for more details. + * + * If |data_prd| is not ``NULL``, it provides data which will be sent + * in subsequent DATA frames. This function does not take ownership + * of the |data_prd|. The function copies the members of the + * |data_prd|. If |data_prd| is ``NULL``, HEADERS will have + * END_STREAM flag set. + * + * This method can be used as normal HTTP response and push response. + * When pushing a resource using this function, the |session| must be + * configured using `nghttp2_session_server_new()` or its variants and + * the target stream denoted by the |stream_id| must be reserved using + * `nghttp2_submit_push_promise()`. + * + * To send non-final response headers (e.g., HTTP status 101), don't + * use this function because this function half-closes the outbound + * stream. Instead, use `nghttp2_submit_headers()` for this purpose. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`NGHTTP2_ERR_NOMEM` + * Out of memory. + * :enum:`NGHTTP2_ERR_INVALID_ARGUMENT` + * The |stream_id| is 0. + * + * .. warning:: + * + * Calling this function twice for the same stream ID may lead to + * program crash. It is generally considered to a programming error + * to commit response twice. + */ +NGHTTP2_EXTERN int +nghttp2_submit_response(nghttp2_session *session, int32_t stream_id, + const nghttp2_nv *nva, size_t nvlen, + const nghttp2_data_provider *data_prd); + +/** + * @function + * + * Submits trailer HEADERS against the stream |stream_id|. + * + * The |nva| is an array of name/value pair :type:`nghttp2_nv` with + * |nvlen| elements. The application is responsible not to include + * required pseudo-header fields (header field whose name starts with + * ":") in |nva|. + * + * This function creates copies of all name/value pairs in |nva|. It + * also lower-cases all names in |nva|. The order of elements in + * |nva| is preserved. + * + * For server, trailer must be followed by response HEADERS or + * response DATA. The library does not check that response HEADERS + * has already sent and if `nghttp2_submit_trailer()` is called before + * any response HEADERS submission (usually by + * `nghttp2_submit_response()`), the content of |nva| will be sent as + * reponse headers, which will result in error. + * + * This function has the same effect with `nghttp2_submit_headers()`, + * with flags = :enum:`NGHTTP2_FLAG_END_HEADERS` and both pri_spec and + * stream_user_data to NULL. + * + * To submit trailer after `nghttp2_submit_response()` is called, the + * application has to specify :type:`nghttp2_data_provider` to + * `nghttp2_submit_response()`. In side + * :type:`nghttp2_data_source_read_callback`, when setting + * :enum:`NGHTTP2_DATA_FLAG_EOF`, also set + * :enum:`NGHTTP2_DATA_FLAG_NO_END_STREAM`. After that, the + * application can send trailer using `nghttp2_submit_trailer()`. + * `nghttp2_submit_trailer()` can be used inside + * :type:`nghttp2_data_source_read_callback`. + * + * This function returns 0 if it succeeds and |stream_id| is -1. + * Otherwise, this function returns 0 if it succeeds, or one of the + * following negative error codes: + * + * :enum:`NGHTTP2_ERR_NOMEM` + * Out of memory. + * :enum:`NGHTTP2_ERR_INVALID_ARGUMENT` + * The |stream_id| is 0. + */ +NGHTTP2_EXTERN int nghttp2_submit_trailer(nghttp2_session *session, + int32_t stream_id, + const nghttp2_nv *nva, size_t nvlen); + +/** + * @function + * + * Submits HEADERS frame. The |flags| is bitwise OR of the + * following values: + * + * * :enum:`NGHTTP2_FLAG_END_STREAM` + * + * If |flags| includes :enum:`NGHTTP2_FLAG_END_STREAM`, this frame has + * END_STREAM flag set. + * + * The library handles the CONTINUATION frame internally and it + * correctly sets END_HEADERS to the last sequence of the PUSH_PROMISE + * or CONTINUATION frame. + * + * If the |stream_id| is -1, this frame is assumed as request (i.e., + * request HEADERS frame which opens new stream). In this case, the + * assigned stream ID will be returned. Otherwise, specify stream ID + * in |stream_id|. + * + * The |pri_spec| is priority specification of this request. ``NULL`` + * means the default priority (see + * `nghttp2_priority_spec_default_init()`). To specify the priority, + * use `nghttp2_priority_spec_init()`. If |pri_spec| is not ``NULL``, + * this function will copy its data members. + * + * The ``pri_spec->weight`` must be in [:enum:`NGHTTP2_MIN_WEIGHT`, + * :enum:`NGHTTP2_MAX_WEIGHT`], inclusive. If ``pri_spec->weight`` is + * strictly less than :enum:`NGHTTP2_MIN_WEIGHT`, it becomes + * :enum:`NGHTTP2_MIN_WEIGHT`. If it is strictly greater than + * :enum:`NGHTTP2_MAX_WEIGHT`, it becomes :enum:`NGHTTP2_MAX_WEIGHT`. + * + * The |nva| is an array of name/value pair :type:`nghttp2_nv` with + * |nvlen| elements. The application is responsible to include + * required pseudo-header fields (header field whose name starts with + * ":") in |nva| and must place pseudo-headers before regular header + * fields. + * + * This function creates copies of all name/value pairs in |nva|. It + * also lower-cases all names in |nva|. The order of elements in + * |nva| is preserved. + * + * The |stream_user_data| is a pointer to an arbitrary data which is + * associated to the stream this frame will open. Therefore it is + * only used if this frame opens streams, in other words, it changes + * stream state from idle or reserved to open. + * + * This function is low-level in a sense that the application code can + * specify flags directly. For usual HTTP request, + * `nghttp2_submit_request()` is useful. + * + * This function returns newly assigned stream ID if it succeeds and + * |stream_id| is -1. Otherwise, this function returns 0 if it + * succeeds, or one of the following negative error codes: + * + * :enum:`NGHTTP2_ERR_NOMEM` + * Out of memory. + * :enum:`NGHTTP2_ERR_STREAM_ID_NOT_AVAILABLE` + * No stream ID is available because maximum stream ID was + * reached. + * :enum:`NGHTTP2_ERR_INVALID_ARGUMENT` + * The |stream_id| is 0. + * + * .. warning:: + * + * This function returns assigned stream ID if it succeeds and + * |stream_id| is -1. But that stream is not opened yet. The + * application must not submit frame to that stream ID before + * :type:`nghttp2_before_frame_send_callback` is called for this + * frame. + * + */ +NGHTTP2_EXTERN int32_t + nghttp2_submit_headers(nghttp2_session *session, uint8_t flags, + int32_t stream_id, + const nghttp2_priority_spec *pri_spec, + const nghttp2_nv *nva, size_t nvlen, + void *stream_user_data); + +/** + * @function + * + * Submits one or more DATA frames to the stream |stream_id|. The + * data to be sent are provided by |data_prd|. If |flags| contains + * :enum:`NGHTTP2_FLAG_END_STREAM`, the last DATA frame has END_STREAM + * flag set. + * + * This function does not take ownership of the |data_prd|. The + * function copies the members of the |data_prd|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`NGHTTP2_ERR_NOMEM` + * Out of memory. + * :enum:`NGHTTP2_ERR_DATA_EXIST` + * DATA has been already submitted and not fully processed yet. + * :enum:`NGHTTP2_ERR_INVALID_ARGUMENT` + * The |stream_id| is 0. + * :enum:`NGHTTP2_ERR_STREAM_CLOSED` + * The stream was alreay closed; or the |stream_id| is invalid. + * + * .. note:: + * + * Currently, only one data is allowed for a stream at a time. + * Submitting data more than once before first data is finished + * results in :enum:`NGHTTP2_ERR_DATA_EXIST` error code. The + * earliest callback which tells that previous data is done is + * :type:`nghttp2_on_frame_send_callback`. In side that callback, + * new data can be submitted using `nghttp2_submit_data()`. Of + * course, all data except for last one must not have + * :enum:`NGHTTP2_FLAG_END_STREAM` flag set in |flags|. + */ +NGHTTP2_EXTERN int nghttp2_submit_data(nghttp2_session *session, uint8_t flags, + int32_t stream_id, + const nghttp2_data_provider *data_prd); + +/** + * @function + * + * Submits PRIORITY frame to change the priority of stream |stream_id| + * to the priority specification |pri_spec|. + * + * The |flags| is currently ignored and should be + * :enum:`NGHTTP2_FLAG_NONE`. + * + * The |pri_spec| is priority specification of this request. ``NULL`` + * is not allowed for this function. To specify the priority, use + * `nghttp2_priority_spec_init()`. This function will copy its data + * members. + * + * The ``pri_spec->weight`` must be in [:enum:`NGHTTP2_MIN_WEIGHT`, + * :enum:`NGHTTP2_MAX_WEIGHT`], inclusive. If ``pri_spec->weight`` is + * strictly less than :enum:`NGHTTP2_MIN_WEIGHT`, it becomes + * :enum:`NGHTTP2_MIN_WEIGHT`. If it is strictly greater than + * :enum:`NGHTTP2_MAX_WEIGHT`, it becomes :enum:`NGHTTP2_MAX_WEIGHT`. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`NGHTTP2_ERR_NOMEM` + * Out of memory. + * :enum:`NGHTTP2_ERR_INVALID_ARGUMENT` + * The |stream_id| is 0; or the |pri_spec| is NULL; or trying to + * depend on itself. + */ +NGHTTP2_EXTERN int +nghttp2_submit_priority(nghttp2_session *session, uint8_t flags, + int32_t stream_id, + const nghttp2_priority_spec *pri_spec); + +/** + * @function + * + * Submits RST_STREAM frame to cancel/reject the stream |stream_id| + * with the error code |error_code|. + * + * The pre-defined error code is one of :enum:`nghttp2_error_code`. + * + * The |flags| is currently ignored and should be + * :enum:`NGHTTP2_FLAG_NONE`. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`NGHTTP2_ERR_NOMEM` + * Out of memory. + * :enum:`NGHTTP2_ERR_INVALID_ARGUMENT` + * The |stream_id| is 0. + */ +NGHTTP2_EXTERN int nghttp2_submit_rst_stream(nghttp2_session *session, + uint8_t flags, int32_t stream_id, + uint32_t error_code); + +/** + * @function + * + * Stores local settings and submits SETTINGS frame. The |iv| is the + * pointer to the array of :type:`nghttp2_settings_entry`. The |niv| + * indicates the number of :type:`nghttp2_settings_entry`. + * + * The |flags| is currently ignored and should be + * :enum:`NGHTTP2_FLAG_NONE`. + * + * This function does not take ownership of the |iv|. This function + * copies all the elements in the |iv|. + * + * While updating individual stream's local window size, if the window + * size becomes strictly larger than NGHTTP2_MAX_WINDOW_SIZE, + * RST_STREAM is issued against such a stream. + * + * SETTINGS with :enum:`NGHTTP2_FLAG_ACK` is automatically submitted + * by the library and application could not send it at its will. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`NGHTTP2_ERR_INVALID_ARGUMENT` + * The |iv| contains invalid value (e.g., initial window size + * strictly greater than (1 << 31) - 1. + * :enum:`NGHTTP2_ERR_TOO_MANY_INFLIGHT_SETTINGS` + * There is already another in-flight SETTINGS. Note that the + * current implementation only allows 1 in-flight SETTINGS frame + * without ACK flag set. + * :enum:`NGHTTP2_ERR_NOMEM` + * Out of memory. + */ +NGHTTP2_EXTERN int nghttp2_submit_settings(nghttp2_session *session, + uint8_t flags, + const nghttp2_settings_entry *iv, + size_t niv); + +/** + * @function + * + * Submits PUSH_PROMISE frame. + * + * The |flags| is currently ignored. The library handles the + * CONTINUATION frame internally and it correctly sets END_HEADERS to + * the last sequence of the PUSH_PROMISE or CONTINUATION frame. + * + * The |stream_id| must be client initiated stream ID. + * + * The |nva| is an array of name/value pair :type:`nghttp2_nv` with + * |nvlen| elements. The application is responsible to include + * required pseudo-header fields (header field whose name starts with + * ":") in |nva| and must place pseudo-headers before regular header + * fields. + * + * This function creates copies of all name/value pairs in |nva|. It + * also lower-cases all names in |nva|. The order of elements in + * |nva| is preserved. + * + * The |promised_stream_user_data| is a pointer to an arbitrary data + * which is associated to the promised stream this frame will open and + * make it in reserved state. It is available using + * `nghttp2_session_get_stream_user_data()`. The application can + * access it in :type:`nghttp2_before_frame_send_callback` and + * :type:`nghttp2_on_frame_send_callback` of this frame. + * + * The client side is not allowed to use this function. + * + * To submit response headers and data, use + * `nghttp2_submit_response()`. + * + * This function returns assigned promised stream ID if it succeeds, + * or one of the following negative error codes: + * + * :enum:`NGHTTP2_ERR_NOMEM` + * Out of memory. + * :enum:`NGHTTP2_ERR_PROTO` + * This function was invoked when |session| is initialized as + * client. + * :enum:`NGHTTP2_ERR_STREAM_ID_NOT_AVAILABLE` + * No stream ID is available because maximum stream ID was + * reached. + * :enum:`NGHTTP2_ERR_INVALID_ARGUMENT` + * The |stream_id| is 0; The |stream_id| does not designate stream + * that peer initiated. + * + * .. warning:: + * + * This function returns assigned promised stream ID if it succeeds. + * But that stream is not opened yet. The application must not + * submit frame to that stream ID before + * :type:`nghttp2_before_frame_send_callback` is called for this + * frame. + * + */ +NGHTTP2_EXTERN int32_t + nghttp2_submit_push_promise(nghttp2_session *session, uint8_t flags, + int32_t stream_id, const nghttp2_nv *nva, + size_t nvlen, void *promised_stream_user_data); + +/** + * @function + * + * Submits PING frame. You don't have to send PING back when you + * received PING frame. The library automatically submits PING frame + * in this case. + * + * The |flags| is currently ignored and should be + * :enum:`NGHTTP2_FLAG_NONE`. + * + * If the |opaque_data| is non ``NULL``, then it should point to the 8 + * bytes array of memory to specify opaque data to send with PING + * frame. If the |opaque_data| is ``NULL``, zero-cleared 8 bytes will + * be sent as opaque data. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`NGHTTP2_ERR_NOMEM` + * Out of memory. + */ +NGHTTP2_EXTERN int nghttp2_submit_ping(nghttp2_session *session, uint8_t flags, + const uint8_t *opaque_data); + +/** + * @function + * + * Submits GOAWAY frame with the last stream ID |last_stream_id| and + * the error code |error_code|. + * + * The pre-defined error code is one of :enum:`nghttp2_error_code`. + * + * The |flags| is currently ignored and should be + * :enum:`NGHTTP2_FLAG_NONE`. + * + * The |last_stream_id| is peer's stream ID or 0. So if |session| is + * initialized as client, |last_stream_id| must be even or 0. If + * |session| is initialized as server, |last_stream_id| must be odd or + * 0. + * + * The HTTP/2 specification says last_stream_id must not be increased + * from the value previously sent. So the actual value sent as + * last_stream_id is the minimum value between the given + * |last_stream_id| and the last_stream_id previously sent to the + * peer. + * + * If the |opaque_data| is not ``NULL`` and |opaque_data_len| is not + * zero, those data will be sent as additional debug data. The + * library makes a copy of the memory region pointed by |opaque_data| + * with the length |opaque_data_len|, so the caller does not need to + * keep this memory after the return of this function. If the + * |opaque_data_len| is 0, the |opaque_data| could be ``NULL``. + * + * After successful transmission of GOAWAY, following things happen. + * All incoming streams having strictly more than |last_stream_id| are + * closed. All incoming HEADERS which starts new stream are simply + * ignored. After all active streams are handled, both + * `nghttp2_session_want_read()` and `nghttp2_session_want_write()` + * return 0 and the application can close session. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`NGHTTP2_ERR_NOMEM` + * Out of memory. + * :enum:`NGHTTP2_ERR_INVALID_ARGUMENT` + * The |opaque_data_len| is too large; the |last_stream_id| is + * invalid. + */ +NGHTTP2_EXTERN int nghttp2_submit_goaway(nghttp2_session *session, + uint8_t flags, int32_t last_stream_id, + uint32_t error_code, + const uint8_t *opaque_data, + size_t opaque_data_len); + +/** + * @function + * + * Returns the last stream ID of a stream for which + * :type:`nghttp2_on_frame_recv_callback` was invoked most recently. + * The returned value can be used as last_stream_id parameter for + * `nghttp2_submit_goaway()` and + * `nghttp2_session_terminate_session2()`. + * + * This function always succeeds. + */ +NGHTTP2_EXTERN int32_t + nghttp2_session_get_last_proc_stream_id(nghttp2_session *session); + +/** + * @function + * + * Submits WINDOW_UPDATE frame. + * + * The |flags| is currently ignored and should be + * :enum:`NGHTTP2_FLAG_NONE`. + * + * The |stream_id| is the stream ID to send this WINDOW_UPDATE. To + * send connection level WINDOW_UPDATE, specify 0 to |stream_id|. + * + * If the |window_size_increment| is positive, the WINDOW_UPDATE with + * that value as window_size_increment is queued. If the + * |window_size_increment| is larger than the received bytes from the + * remote endpoint, the local window size is increased by that + * difference. + * + * If the |window_size_increment| is negative, the local window size + * is decreased by -|window_size_increment|. If automatic + * WINDOW_UPDATE is enabled + * (`nghttp2_option_set_no_auto_window_update()`), and the library + * decided that the WINDOW_UPDATE should be submitted, then + * WINDOW_UPDATE is queued with the current received bytes count. + * + * If the |window_size_increment| is 0, the function does nothing and + * returns 0. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`NGHTTP2_ERR_FLOW_CONTROL` + * The local window size overflow or gets negative. + * :enum:`NGHTTP2_ERR_NOMEM` + * Out of memory. + */ +NGHTTP2_EXTERN int nghttp2_submit_window_update(nghttp2_session *session, + uint8_t flags, + int32_t stream_id, + int32_t window_size_increment); + +/** + * @function + * + * Compares ``lhs->name`` of length ``lhs->namelen`` bytes and + * ``rhs->name`` of length ``rhs->namelen`` bytes. Returns negative + * integer if ``lhs->name`` is found to be less than ``rhs->name``; or + * returns positive integer if ``lhs->name`` is found to be greater + * than ``rhs->name``; or returns 0 otherwise. + */ +NGHTTP2_EXTERN int nghttp2_nv_compare_name(const nghttp2_nv *lhs, + const nghttp2_nv *rhs); + +/** + * @function + * + * A helper function for dealing with NPN in client side or ALPN in + * server side. The |in| contains peer's protocol list in preferable + * order. The format of |in| is length-prefixed and not + * null-terminated. For example, ``h2`` and + * ``http/1.1`` stored in |in| like this:: + * + * in[0] = 2 + * in[1..2] = "h2" + * in[3] = 8 + * in[4..11] = "http/1.1" + * inlen = 12 + * + * The selection algorithm is as follows: + * + * 1. If peer's list contains HTTP/2 protocol the library supports, + * it is selected and returns 1. The following step is not taken. + * + * 2. If peer's list contains ``http/1.1``, this function selects + * ``http/1.1`` and returns 0. The following step is not taken. + * + * 3. This function selects nothing and returns -1 (So called + * non-overlap case). In this case, |out| and |outlen| are left + * untouched. + * + * Selecting ``h2`` means that ``h2`` is written into |*out| and its + * length (which is 2) is assigned to |*outlen|. + * + * For ALPN, refer to https://tools.ietf.org/html/rfc7301 + * + * See http://technotes.googlecode.com/git/nextprotoneg.html for more + * details about NPN. + * + * For NPN, to use this method you should do something like:: + * + * static int select_next_proto_cb(SSL* ssl, + * unsigned char **out, + * unsigned char *outlen, + * const unsigned char *in, + * unsigned int inlen, + * void *arg) + * { + * int rv; + * rv = nghttp2_select_next_protocol(out, outlen, in, inlen); + * if (rv == -1) { + * return SSL_TLSEXT_ERR_NOACK; + * } + * if (rv == 1) { + * ((MyType*)arg)->http2_selected = 1; + * } + * return SSL_TLSEXT_ERR_OK; + * } + * ... + * SSL_CTX_set_next_proto_select_cb(ssl_ctx, select_next_proto_cb, my_obj); + * + */ +NGHTTP2_EXTERN int nghttp2_select_next_protocol(unsigned char **out, + unsigned char *outlen, + const unsigned char *in, + unsigned int inlen); + +/** + * @function + * + * Returns a pointer to a nghttp2_info struct with version information + * about the run-time library in use. The |least_version| argument + * can be set to a 24 bit numerical value for the least accepted + * version number and if the condition is not met, this function will + * return a ``NULL``. Pass in 0 to skip the version checking. + */ +NGHTTP2_EXTERN nghttp2_info *nghttp2_version(int least_version); + +/** + * @function + * + * Returns nonzero if the :type:`nghttp2_error` library error code + * |lib_error| is fatal. + */ +NGHTTP2_EXTERN int nghttp2_is_fatal(int lib_error_code); + +/** + * @function + * + * Returns nonzero if HTTP header field name |name| of length |len| is + * valid according to http://tools.ietf.org/html/rfc7230#section-3.2 + * + * Because this is a header field name in HTTP2, the upper cased alphabet + * is treated as error. + */ +NGHTTP2_EXTERN int nghttp2_check_header_name(const uint8_t *name, size_t len); + +/** + * @function + * + * Returns nonzero if HTTP header field value |value| of length |len| + * is valid according to + * http://tools.ietf.org/html/rfc7230#section-3.2 + */ +NGHTTP2_EXTERN int nghttp2_check_header_value(const uint8_t *value, size_t len); + +/* HPACK API */ + +struct nghttp2_hd_deflater; + +/** + * @struct + * + * HPACK deflater object. + */ +typedef struct nghttp2_hd_deflater nghttp2_hd_deflater; + +/** + * @function + * + * Initializes |*deflater_ptr| for deflating name/values pairs. + * + * The |deflate_hd_table_bufsize_max| is the upper bound of header + * table size the deflater will use. + * + * If this function fails, |*deflater_ptr| is left untouched. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`NGHTTP2_ERR_NOMEM` + * Out of memory. + */ +NGHTTP2_EXTERN int nghttp2_hd_deflate_new(nghttp2_hd_deflater **deflater_ptr, + size_t deflate_hd_table_bufsize_max); + +/** + * @function + * + * Like `nghttp2_hd_deflate_new()`, but with additional custom memory + * allocator specified in the |mem|. + * + * The |mem| can be ``NULL`` and the call is equivalent to + * `nghttp2_hd_deflate_new()`. + * + * This function does not take ownership |mem|. The application is + * responsible for freeing |mem|. + * + * The library code does not refer to |mem| pointer after this + * function returns, so the application can safely free it. + */ +NGHTTP2_EXTERN int nghttp2_hd_deflate_new2(nghttp2_hd_deflater **deflater_ptr, + size_t deflate_hd_table_bufsize_max, + nghttp2_mem *mem); + +/** + * @function + * + * Deallocates any resources allocated for |deflater|. + */ +NGHTTP2_EXTERN void nghttp2_hd_deflate_del(nghttp2_hd_deflater *deflater); + +/** + * @function + * + * Changes header table size of the |deflater| to + * |settings_hd_table_bufsize_max| bytes. This may trigger eviction + * in the dynamic table. + * + * The |settings_hd_table_bufsize_max| should be the value received in + * SETTINGS_HEADER_TABLE_SIZE. + * + * The deflater never uses more memory than + * ``deflate_hd_table_bufsize_max`` bytes specified in + * `nghttp2_hd_deflate_new()`. Therefore, if + * |settings_hd_table_bufsize_max| > ``deflate_hd_table_bufsize_max``, + * resulting maximum table size becomes + * ``deflate_hd_table_bufsize_max``. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`NGHTTP2_ERR_NOMEM` + * Out of memory. + */ +NGHTTP2_EXTERN int +nghttp2_hd_deflate_change_table_size(nghttp2_hd_deflater *deflater, + size_t settings_hd_table_bufsize_max); + +/** + * @function + * + * Deflates the |nva|, which has the |nvlen| name/value pairs, into + * the |buf| of length |buflen|. + * + * If |buf| is not large enough to store the deflated header block, + * this function fails with :enum:`NGHTTP2_ERR_INSUFF_BUFSIZE`. The + * caller should use `nghttp2_hd_deflate_bound()` to know the upper + * bound of buffer size required to deflate given header name/value + * pairs. + * + * Once this function fails, subsequent call of this function always + * returns :enum:`NGHTTP2_ERR_HEADER_COMP`. + * + * After this function returns, it is safe to delete the |nva|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`NGHTTP2_ERR_NOMEM` + * Out of memory. + * :enum:`NGHTTP2_ERR_HEADER_COMP` + * Deflation process has failed. + * :enum:`NGHTTP2_ERR_INSUFF_BUFSIZE` + * The provided |buflen| size is too small to hold the output. + */ +NGHTTP2_EXTERN ssize_t + nghttp2_hd_deflate_hd(nghttp2_hd_deflater *deflater, uint8_t *buf, + size_t buflen, const nghttp2_nv *nva, size_t nvlen); + +/** + * @function + * + * Returns an upper bound on the compressed size after deflation of + * |nva| of length |nvlen|. + */ +NGHTTP2_EXTERN size_t nghttp2_hd_deflate_bound(nghttp2_hd_deflater *deflater, + const nghttp2_nv *nva, + size_t nvlen); + +struct nghttp2_hd_inflater; + +/** + * @struct + * + * HPACK inflater object. + */ +typedef struct nghttp2_hd_inflater nghttp2_hd_inflater; + +/** + * @function + * + * Initializes |*inflater_ptr| for inflating name/values pairs. + * + * If this function fails, |*inflater_ptr| is left untouched. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`NGHTTP2_ERR_NOMEM` + * Out of memory. + */ +NGHTTP2_EXTERN int nghttp2_hd_inflate_new(nghttp2_hd_inflater **inflater_ptr); + +/** + * @function + * + * Like `nghttp2_hd_inflate_new()`, but with additional custom memory + * allocator specified in the |mem|. + * + * The |mem| can be ``NULL`` and the call is equivalent to + * `nghttp2_hd_inflate_new()`. + * + * This function does not take ownership |mem|. The application is + * responsible for freeing |mem|. + * + * The library code does not refer to |mem| pointer after this + * function returns, so the application can safely free it. + */ +NGHTTP2_EXTERN int nghttp2_hd_inflate_new2(nghttp2_hd_inflater **inflater_ptr, + nghttp2_mem *mem); + +/** + * @function + * + * Deallocates any resources allocated for |inflater|. + */ +NGHTTP2_EXTERN void nghttp2_hd_inflate_del(nghttp2_hd_inflater *inflater); + +/** + * @function + * + * Changes header table size in the |inflater|. This may trigger + * eviction in the dynamic table. + * + * The |settings_hd_table_bufsize_max| should be the value transmitted + * in SETTINGS_HEADER_TABLE_SIZE. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`NGHTTP2_ERR_NOMEM` + * Out of memory. + */ +NGHTTP2_EXTERN int +nghttp2_hd_inflate_change_table_size(nghttp2_hd_inflater *inflater, + size_t settings_hd_table_bufsize_max); + +/** + * @enum + * + * The flags for header inflation. + */ +typedef enum { + /** + * No flag set. + */ + NGHTTP2_HD_INFLATE_NONE = 0, + /** + * Indicates all headers were inflated. + */ + NGHTTP2_HD_INFLATE_FINAL = 0x01, + /** + * Indicates a header was emitted. + */ + NGHTTP2_HD_INFLATE_EMIT = 0x02 +} nghttp2_hd_inflate_flag; + +/** + * @function + * + * Inflates name/value block stored in |in| with length |inlen|. This + * function performs decompression. For each successful emission of + * header name/value pair, :enum:`NGHTTP2_HD_INFLATE_EMIT` is set in + * |*inflate_flags| and name/value pair is assigned to the |nv_out| + * and the function returns. The caller must not free the members of + * |nv_out|. + * + * The |nv_out| may include pointers to the memory region in the |in|. + * The caller must retain the |in| while the |nv_out| is used. + * + * The application should call this function repeatedly until the + * ``(*inflate_flags) & NGHTTP2_HD_INFLATE_FINAL`` is nonzero and + * return value is non-negative. This means the all input values are + * processed successfully. Then the application must call + * `nghttp2_hd_inflate_end_headers()` to prepare for the next header + * block input. + * + * The caller can feed complete compressed header block. It also can + * feed it in several chunks. The caller must set |in_final| to + * nonzero if the given input is the last block of the compressed + * header. + * + * This function returns the number of bytes processed if it succeeds, + * or one of the following negative error codes: + * + * :enum:`NGHTTP2_ERR_NOMEM` + * Out of memory. + * :enum:`NGHTTP2_ERR_HEADER_COMP` + * Inflation process has failed. + * :enum:`NGHTTP2_ERR_BUFFER_ERROR` + * The heder field name or value is too large. + * + * Example follows:: + * + * int inflate_header_block(nghttp2_hd_inflater *hd_inflater, + * uint8_t *in, size_t inlen, int final) + * { + * ssize_t rv; + * + * for(;;) { + * nghttp2_nv nv; + * int inflate_flags = 0; + * + * rv = nghttp2_hd_inflate_hd(hd_inflater, &nv, &inflate_flags, + * in, inlen, final); + * + * if(rv < 0) { + * fprintf(stderr, "inflate failed with error code %zd", rv); + * return -1; + * } + * + * in += rv; + * inlen -= rv; + * + * if(inflate_flags & NGHTTP2_HD_INFLATE_EMIT) { + * fwrite(nv.name, nv.namelen, 1, stderr); + * fprintf(stderr, ": "); + * fwrite(nv.value, nv.valuelen, 1, stderr); + * fprintf(stderr, "\n"); + * } + * if(inflate_flags & NGHTTP2_HD_INFLATE_FINAL) { + * nghttp2_hd_inflate_end_headers(hd_inflater); + * break; + * } + * if((inflate_flags & NGHTTP2_HD_INFLATE_EMIT) == 0 && + * inlen == 0) { + * break; + * } + * } + * + * return 0; + * } + * + */ +NGHTTP2_EXTERN ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_inflater *inflater, + nghttp2_nv *nv_out, + int *inflate_flags, uint8_t *in, + size_t inlen, int in_final); + +/** + * @function + * + * Signals the end of decompression for one header block. + * + * This function returns 0 if it succeeds. Currently this function + * always succeeds. + */ +NGHTTP2_EXTERN int +nghttp2_hd_inflate_end_headers(nghttp2_hd_inflater *inflater); + +#ifdef __cplusplus +} +#endif + +#endif /* NGHTTP2_H */ diff --git a/lib/includes/nghttp2/nghttp2ver.h b/lib/includes/nghttp2/nghttp2ver.h new file mode 100644 index 0000000..09be018 --- /dev/null +++ b/lib/includes/nghttp2/nghttp2ver.h @@ -0,0 +1,42 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012, 2013 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGHTTP2VER_H +#define NGHTTP2VER_H + +/** + * @macro + * Version number of the nghttp2 library release + */ +#define NGHTTP2_VERSION "1.0.0" + +/** + * @macro + * Numerical representation of the version number of the nghttp2 library + * 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 NGHTTP2_VERSION_NUM 0x010000 + +#endif /* NGHTTP2VER_H */ diff --git a/lib/includes/nghttp2/nghttp2ver.h.in b/lib/includes/nghttp2/nghttp2ver.h.in new file mode 100644 index 0000000..7717a64 --- /dev/null +++ b/lib/includes/nghttp2/nghttp2ver.h.in @@ -0,0 +1,42 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012, 2013 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGHTTP2VER_H +#define NGHTTP2VER_H + +/** + * @macro + * Version number of the nghttp2 library release + */ +#define NGHTTP2_VERSION "@PACKAGE_VERSION@" + +/** + * @macro + * Numerical representation of the version number of the nghttp2 library + * 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 NGHTTP2_VERSION_NUM @PACKAGE_VERSION_NUM@ + +#endif /* NGHTTP2VER_H */ diff --git a/lib/libnghttp2.pc.in b/lib/libnghttp2.pc.in new file mode 100644 index 0000000..da6938f --- /dev/null +++ b/lib/libnghttp2.pc.in @@ -0,0 +1,33 @@ +# nghttp2 - HTTP/2 C Library + +# Copyright (c) 2012, 2013 Tatsuhiro Tsujikawa + +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: + +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. + +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: libnghttp2 +Description: HTTP/2 C library +URL: https://github.com/tatsuhiro-t/nghttp2 +Version: @VERSION@ +Libs: -L${libdir} -lnghttp2 +Cflags: -I${includedir} diff --git a/lib/nghttp2_buf.c b/lib/nghttp2_buf.c new file mode 100644 index 0000000..96454a8 --- /dev/null +++ b/lib/nghttp2_buf.c @@ -0,0 +1,494 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2014 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "nghttp2_buf.h" + +#include + +#include "nghttp2_helper.h" + +void nghttp2_buf_init(nghttp2_buf *buf) { + buf->begin = NULL; + buf->end = NULL; + buf->pos = NULL; + buf->last = NULL; + buf->mark = NULL; +} + +int nghttp2_buf_init2(nghttp2_buf *buf, size_t initial, nghttp2_mem *mem) { + nghttp2_buf_init(buf); + return nghttp2_buf_reserve(buf, initial, mem); +} + +void nghttp2_buf_free(nghttp2_buf *buf, nghttp2_mem *mem) { + if (buf == NULL) { + return; + } + + nghttp2_mem_free(mem, buf->begin); + buf->begin = NULL; +} + +int nghttp2_buf_reserve(nghttp2_buf *buf, size_t new_cap, nghttp2_mem *mem) { + uint8_t *ptr; + size_t cap; + + cap = nghttp2_buf_cap(buf); + + if (cap >= new_cap) { + return 0; + } + + new_cap = nghttp2_max(new_cap, cap * 2); + + ptr = nghttp2_mem_realloc(mem, buf->begin, new_cap); + if (ptr == NULL) { + return NGHTTP2_ERR_NOMEM; + } + + buf->pos = ptr + (buf->pos - buf->begin); + buf->last = ptr + (buf->last - buf->begin); + buf->mark = ptr + (buf->mark - buf->begin); + buf->begin = ptr; + buf->end = ptr + new_cap; + + return 0; +} + +void nghttp2_buf_reset(nghttp2_buf *buf) { + buf->pos = buf->last = buf->mark = buf->begin; +} + +void nghttp2_buf_wrap_init(nghttp2_buf *buf, uint8_t *begin, size_t len) { + buf->begin = buf->pos = buf->last = buf->mark = begin; + buf->end = begin + len; +} + +static int buf_chain_new(nghttp2_buf_chain **chain, size_t chunk_length, + nghttp2_mem *mem) { + int rv; + + *chain = nghttp2_mem_malloc(mem, sizeof(nghttp2_buf_chain)); + if (*chain == NULL) { + return NGHTTP2_ERR_NOMEM; + } + + (*chain)->next = NULL; + + rv = nghttp2_buf_init2(&(*chain)->buf, chunk_length, mem); + if (rv != 0) { + nghttp2_mem_free(mem, *chain); + return NGHTTP2_ERR_NOMEM; + } + + return 0; +} + +static void buf_chain_del(nghttp2_buf_chain *chain, nghttp2_mem *mem) { + nghttp2_buf_free(&chain->buf, mem); + nghttp2_mem_free(mem, chain); +} + +int nghttp2_bufs_init(nghttp2_bufs *bufs, size_t chunk_length, size_t max_chunk, + nghttp2_mem *mem) { + return nghttp2_bufs_init2(bufs, chunk_length, max_chunk, 0, mem); +} + +int nghttp2_bufs_init2(nghttp2_bufs *bufs, size_t chunk_length, + size_t max_chunk, size_t offset, nghttp2_mem *mem) { + return nghttp2_bufs_init3(bufs, chunk_length, max_chunk, max_chunk, offset, + mem); +} + +int nghttp2_bufs_init3(nghttp2_bufs *bufs, size_t chunk_length, + size_t max_chunk, size_t chunk_keep, size_t offset, + nghttp2_mem *mem) { + int rv; + nghttp2_buf_chain *chain; + + if (chunk_keep == 0 || max_chunk < chunk_keep || chunk_length < offset) { + return NGHTTP2_ERR_INVALID_ARGUMENT; + } + + rv = buf_chain_new(&chain, chunk_length, mem); + if (rv != 0) { + return rv; + } + + bufs->mem = mem; + bufs->offset = offset; + + bufs->head = chain; + bufs->cur = bufs->head; + + nghttp2_buf_shift_right(&bufs->cur->buf, offset); + + bufs->chunk_length = chunk_length; + bufs->chunk_used = 1; + bufs->max_chunk = max_chunk; + bufs->chunk_keep = chunk_keep; + + return 0; +} + +int nghttp2_bufs_realloc(nghttp2_bufs *bufs, size_t chunk_length) { + int rv; + nghttp2_buf_chain *chain; + + if (chunk_length < bufs->offset) { + return NGHTTP2_ERR_INVALID_ARGUMENT; + } + + rv = buf_chain_new(&chain, chunk_length, bufs->mem); + if (rv != 0) { + return rv; + } + + nghttp2_bufs_free(bufs); + + bufs->head = chain; + bufs->cur = bufs->head; + + nghttp2_buf_shift_right(&bufs->cur->buf, bufs->offset); + + bufs->chunk_length = chunk_length; + bufs->chunk_used = 1; + + return 0; +} + +void nghttp2_bufs_free(nghttp2_bufs *bufs) { + nghttp2_buf_chain *chain, *next_chain; + + if (bufs == NULL) { + return; + } + + for (chain = bufs->head; chain;) { + next_chain = chain->next; + + buf_chain_del(chain, bufs->mem); + + chain = next_chain; + } + + bufs->head = NULL; +} + +int nghttp2_bufs_wrap_init(nghttp2_bufs *bufs, uint8_t *begin, size_t len, + nghttp2_mem *mem) { + nghttp2_buf_chain *chain; + + chain = nghttp2_mem_malloc(mem, sizeof(nghttp2_buf_chain)); + if (chain == NULL) { + return NGHTTP2_ERR_NOMEM; + } + + chain->next = NULL; + + nghttp2_buf_wrap_init(&chain->buf, begin, len); + + bufs->mem = mem; + bufs->offset = 0; + + bufs->head = chain; + bufs->cur = bufs->head; + + bufs->chunk_length = len; + bufs->chunk_used = 1; + bufs->max_chunk = 1; + bufs->chunk_keep = 1; + + return 0; +} + +void nghttp2_bufs_wrap_free(nghttp2_bufs *bufs) { + if (bufs == NULL) { + return; + } + + nghttp2_mem_free(bufs->mem, bufs->head); + bufs->head = NULL; +} + +void nghttp2_bufs_seek_last_present(nghttp2_bufs *bufs) { + nghttp2_buf_chain *ci; + + for (ci = bufs->cur; ci; ci = ci->next) { + if (nghttp2_buf_len(&ci->buf) == 0) { + return; + } else { + bufs->cur = ci; + } + } +} + +ssize_t nghttp2_bufs_len(nghttp2_bufs *bufs) { + nghttp2_buf_chain *ci; + ssize_t len; + + len = 0; + for (ci = bufs->head; ci; ci = ci->next) { + len += nghttp2_buf_len(&ci->buf); + } + + return len; +} + +static ssize_t bufs_avail(nghttp2_bufs *bufs) { + return (ssize_t)(nghttp2_buf_avail(&bufs->cur->buf) + + (bufs->chunk_length - bufs->offset) * + (bufs->max_chunk - bufs->chunk_used)); +} + +static int bufs_alloc_chain(nghttp2_bufs *bufs) { + int rv; + nghttp2_buf_chain *chain; + + if (bufs->cur->next) { + bufs->cur = bufs->cur->next; + + return 0; + } + + if (bufs->max_chunk == bufs->chunk_used) { + return NGHTTP2_ERR_BUFFER_ERROR; + } + + rv = buf_chain_new(&chain, bufs->chunk_length, bufs->mem); + if (rv != 0) { + return rv; + } + + DEBUGF(fprintf(stderr, + "new buffer %zu bytes allocated for bufs %p, used %zu\n", + bufs->chunk_length, bufs, bufs->chunk_used)); + + ++bufs->chunk_used; + + bufs->cur->next = chain; + bufs->cur = chain; + + nghttp2_buf_shift_right(&bufs->cur->buf, bufs->offset); + + return 0; +} + +int nghttp2_bufs_add(nghttp2_bufs *bufs, const void *data, size_t len) { + int rv; + size_t nwrite; + nghttp2_buf *buf; + const uint8_t *p; + + if (bufs_avail(bufs) < (ssize_t)len) { + return NGHTTP2_ERR_BUFFER_ERROR; + } + + p = data; + + while (len) { + buf = &bufs->cur->buf; + + nwrite = nghttp2_min((size_t)nghttp2_buf_avail(buf), len); + if (nwrite == 0) { + rv = bufs_alloc_chain(bufs); + if (rv != 0) { + return rv; + } + continue; + } + + buf->last = nghttp2_cpymem(buf->last, p, nwrite); + p += nwrite; + len -= nwrite; + } + + return 0; +} + +static int bufs_ensure_addb(nghttp2_bufs *bufs) { + int rv; + nghttp2_buf *buf; + + buf = &bufs->cur->buf; + + if (nghttp2_buf_avail(buf) > 0) { + return 0; + } + + rv = bufs_alloc_chain(bufs); + if (rv != 0) { + return rv; + } + + return 0; +} + +int nghttp2_bufs_addb(nghttp2_bufs *bufs, uint8_t b) { + int rv; + + rv = bufs_ensure_addb(bufs); + if (rv != 0) { + return rv; + } + + *bufs->cur->buf.last++ = b; + + return 0; +} + +int nghttp2_bufs_addb_hold(nghttp2_bufs *bufs, uint8_t b) { + int rv; + + rv = bufs_ensure_addb(bufs); + if (rv != 0) { + return rv; + } + + *bufs->cur->buf.last = b; + + return 0; +} + +int nghttp2_bufs_orb(nghttp2_bufs *bufs, uint8_t b) { + int rv; + + rv = bufs_ensure_addb(bufs); + if (rv != 0) { + return rv; + } + + *bufs->cur->buf.last++ |= b; + + return 0; +} + +int nghttp2_bufs_orb_hold(nghttp2_bufs *bufs, uint8_t b) { + int rv; + + rv = bufs_ensure_addb(bufs); + if (rv != 0) { + return rv; + } + + *bufs->cur->buf.last |= b; + + return 0; +} + +ssize_t nghttp2_bufs_remove(nghttp2_bufs *bufs, uint8_t **out) { + size_t len; + nghttp2_buf_chain *chain; + nghttp2_buf *buf; + uint8_t *res; + nghttp2_buf resbuf; + + len = 0; + + for (chain = bufs->head; chain; chain = chain->next) { + len += nghttp2_buf_len(&chain->buf); + } + + if (len == 0) { + res = NULL; + return 0; + } + + res = nghttp2_mem_malloc(bufs->mem, len); + if (res == NULL) { + return NGHTTP2_ERR_NOMEM; + } + + nghttp2_buf_wrap_init(&resbuf, res, len); + + for (chain = bufs->head; chain; chain = chain->next) { + buf = &chain->buf; + resbuf.last = nghttp2_cpymem(resbuf.last, buf->pos, nghttp2_buf_len(buf)); + } + + *out = res; + + return (ssize_t)len; +} + +size_t nghttp2_bufs_remove_copy(nghttp2_bufs *bufs, uint8_t *out) { + size_t len; + nghttp2_buf_chain *chain; + nghttp2_buf *buf; + nghttp2_buf resbuf; + + len = nghttp2_bufs_len(bufs); + + nghttp2_buf_wrap_init(&resbuf, out, len); + + for (chain = bufs->head; chain; chain = chain->next) { + buf = &chain->buf; + resbuf.last = nghttp2_cpymem(resbuf.last, buf->pos, nghttp2_buf_len(buf)); + } + + return len; +} + +void nghttp2_bufs_reset(nghttp2_bufs *bufs) { + nghttp2_buf_chain *chain, *ci; + size_t k; + + k = bufs->chunk_keep; + + for (ci = bufs->head; ci; ci = ci->next) { + nghttp2_buf_reset(&ci->buf); + nghttp2_buf_shift_right(&ci->buf, bufs->offset); + + if (--k == 0) { + break; + } + } + + if (ci) { + chain = ci->next; + ci->next = NULL; + + for (ci = chain; ci;) { + chain = ci->next; + + buf_chain_del(ci, bufs->mem); + + ci = chain; + } + + bufs->chunk_used = bufs->chunk_keep; + } + + bufs->cur = bufs->head; +} + +int nghttp2_bufs_advance(nghttp2_bufs *bufs) { return bufs_alloc_chain(bufs); } + +int nghttp2_bufs_next_present(nghttp2_bufs *bufs) { + nghttp2_buf_chain *chain; + + chain = bufs->cur->next; + + return chain && nghttp2_buf_len(&chain->buf); +} diff --git a/lib/nghttp2_buf.h b/lib/nghttp2_buf.h new file mode 100644 index 0000000..9e986c6 --- /dev/null +++ b/lib/nghttp2_buf.h @@ -0,0 +1,385 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2014 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGHTTP2_BUF_H +#define NGHTTP2_BUF_H + +#ifdef HAVE_CONFIG_H +#include +#endif /* HAVE_CONFIG_H */ + +#include + +#include "nghttp2_int.h" +#include "nghttp2_mem.h" + +typedef struct { + /* This points to the beginning of the buffer. The effective range + of buffer is [begin, end). */ + uint8_t *begin; + /* This points to the memory one byte beyond the end of the + buffer. */ + uint8_t *end; + /* The position indicator for effective start of the buffer. pos <= + last must be hold. */ + uint8_t *pos; + /* The position indicator for effective one beyond of the end of the + buffer. last <= end must be hold. */ + uint8_t *last; + /* Mark arbitrary position in buffer [begin, end) */ + uint8_t *mark; +} nghttp2_buf; + +#define nghttp2_buf_len(BUF) ((ssize_t)((BUF)->last - (BUF)->pos)) +#define nghttp2_buf_avail(BUF) ((ssize_t)((BUF)->end - (BUF)->last)) +#define nghttp2_buf_mark_avail(BUF) ((ssize_t)((BUF)->mark - (BUF)->last)) +#define nghttp2_buf_cap(BUF) ((ssize_t)((BUF)->end - (BUF)->begin)) + +#define nghttp2_buf_pos_offset(BUF) ((ssize_t)((BUF)->pos - (BUF)->begin)) +#define nghttp2_buf_last_offset(BUF) ((ssize_t)((BUF)->last - (BUF)->begin)) + +#define nghttp2_buf_shift_right(BUF, AMT) \ + do { \ + (BUF)->pos += AMT; \ + (BUF)->last += AMT; \ + } while (0) + +#define nghttp2_buf_shift_left(BUF, AMT) \ + do { \ + (BUF)->pos -= AMT; \ + (BUF)->last -= AMT; \ + } while (0) + +/* + * Initializes the |buf|. No memory is allocated in this function. Use + * nghttp2_buf_reserve() or nghttp2_buf_reserve2() to allocate memory. + */ +void nghttp2_buf_init(nghttp2_buf *buf); + +/* + * Initializes the |buf| and allocates at least |initial| bytes of + * memory. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory + */ +int nghttp2_buf_init2(nghttp2_buf *buf, size_t initial, nghttp2_mem *mem); + +/* + * Frees buffer in |buf|. + */ +void nghttp2_buf_free(nghttp2_buf *buf, nghttp2_mem *mem); + +/* + * Extends buffer so that nghttp2_buf_cap() returns at least + * |new_cap|. If extensions took place, buffer pointers in |buf| will + * change. + * + * This function returns 0 if it succeeds, or one of the followings + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory + */ +int nghttp2_buf_reserve(nghttp2_buf *buf, size_t new_cap, nghttp2_mem *mem); + +/* + * Resets pos, last, mark member of |buf| to buf->begin. + */ +void nghttp2_buf_reset(nghttp2_buf *buf); + +/* + * Initializes |buf| using supplied buffer |begin| of length + * |len|. Semantically, the application should not call *_reserve() or + * nghttp2_free() functions for |buf|. + */ +void nghttp2_buf_wrap_init(nghttp2_buf *buf, uint8_t *begin, size_t len); + +struct nghttp2_buf_chain; + +typedef struct nghttp2_buf_chain nghttp2_buf_chain; + +/* Chains 2 buffers */ +struct nghttp2_buf_chain { + /* Points to the subsequent buffer. NULL if there is no such + buffer. */ + nghttp2_buf_chain *next; + nghttp2_buf buf; +}; + +typedef struct { + /* Points to the first buffer */ + nghttp2_buf_chain *head; + /* Buffer pointer where write occurs. */ + nghttp2_buf_chain *cur; + /* Memory allocator */ + nghttp2_mem *mem; + /* The buffer capacity of each buf */ + size_t chunk_length; + /* The maximum number of nghttp2_buf_chain */ + size_t max_chunk; + /* The number of nghttp2_buf_chain allocated */ + size_t chunk_used; + /* The number of nghttp2_buf_chain to keep on reset */ + size_t chunk_keep; + /* pos offset from begin in each buffers. On initialization and + reset, buf->pos and buf->last are positioned at buf->begin + + offset. */ + size_t offset; +} nghttp2_bufs; + +/* + * This is the same as calling nghttp2_bufs_init2 with the given + * arguments and offset = 0. + */ +int nghttp2_bufs_init(nghttp2_bufs *bufs, size_t chunk_length, size_t max_chunk, + nghttp2_mem *mem); + +/* + * This is the same as calling nghttp2_bufs_init3 with the given + * arguments and chunk_keep = max_chunk. + */ +int nghttp2_bufs_init2(nghttp2_bufs *bufs, size_t chunk_length, + size_t max_chunk, size_t offset, nghttp2_mem *mem); + +/* + * Initializes |bufs|. Each buffer size is given in the + * |chunk_length|. The maximum number of buffers is given in the + * |max_chunk|. On reset, first |chunk_keep| buffers are kept and + * remaining buffers are deleted. Each buffer will have bufs->pos and + * bufs->last shifted to left by |offset| bytes on creation and reset. + * + * This function allocates first buffer. bufs->head and bufs->cur + * will point to the first buffer after this call. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + * NGHTTP2_ERR_INVALID_ARGUMENT + * chunk_keep is 0; or max_chunk < chunk_keep; or offset is too + * long. + */ +int nghttp2_bufs_init3(nghttp2_bufs *bufs, size_t chunk_length, + size_t max_chunk, size_t chunk_keep, size_t offset, + nghttp2_mem *mem); + +/* + * Frees any related resources to the |bufs|. + */ +void nghttp2_bufs_free(nghttp2_bufs *bufs); + +/* + * Initializes |bufs| using supplied buffer |begin| of length |len|. + * The first buffer bufs->head uses buffer |begin|. The buffer size + * is fixed and no allocate extra chunk buffer is allocated. In other + * words, max_chunk = chunk_keep = 1. To free the resource allocated + * for |bufs|, use nghttp2_bufs_wrap_free(). + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + */ +int nghttp2_bufs_wrap_init(nghttp2_bufs *bufs, uint8_t *begin, size_t len, + nghttp2_mem *mem); + +/* + * Frees any related resource to the |bufs|. This function does not + * free supplied buffer provided in nghttp2_bufs_wrap_init(). + */ +void nghttp2_bufs_wrap_free(nghttp2_bufs *bufs); + +/* + * Reallocates internal buffer using |chunk_length|. The max_chunk, + * chunk_keep and offset do not change. After successful allocation + * of new buffer, previous buffers are deallocated without copying + * anything into new buffers. chunk_used is reset to 1. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + * NGHTTP2_ERR_INVALID_ARGUMENT + * chunk_length < offset + */ +int nghttp2_bufs_realloc(nghttp2_bufs *bufs, size_t chunk_length); + +/* + * Appends the |data| of length |len| to the |bufs|. The write starts + * at bufs->cur->buf.last. A new buffers will be allocated to store + * all data. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + * NGHTTP2_ERR_BUFFER_ERROR + * Out of buffer space. + */ +int nghttp2_bufs_add(nghttp2_bufs *bufs, const void *data, size_t len); + +/* + * Appends a single byte |b| to the |bufs|. The write starts at + * bufs->cur->buf.last. A new buffers will be allocated to store all + * data. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + * NGHTTP2_ERR_BUFFER_ERROR + * Out of buffer space. + */ +int nghttp2_bufs_addb(nghttp2_bufs *bufs, uint8_t b); + +/* + * Behaves like nghttp2_bufs_addb(), but this does not update + * buf->last pointer. + */ +int nghttp2_bufs_addb_hold(nghttp2_bufs *bufs, uint8_t b); + +#define nghttp2_bufs_fast_addb(BUFS, B) \ + do { \ + *(BUFS)->cur->buf.last++ = B; \ + } while (0) + +#define nghttp2_bufs_fast_addb_hold(BUFS, B) \ + do { \ + *(BUFS)->cur->buf.last = B; \ + } while (0) + +/* + * Performs bitwise-OR of |b| at bufs->cur->buf.last. A new buffers + * will be allocated if necessary. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + * NGHTTP2_ERR_BUFFER_ERROR + * Out of buffer space. + */ +int nghttp2_bufs_orb(nghttp2_bufs *bufs, uint8_t b); + +/* + * Behaves like nghttp2_bufs_orb(), but does not update buf->last + * pointer. + */ +int nghttp2_bufs_orb_hold(nghttp2_bufs *bufs, uint8_t b); + +#define nghttp2_bufs_fast_orb(BUFS, B) \ + do { \ + *(BUFS)->cur->buf.last++ |= B; \ + } while (0) + +#define nghttp2_bufs_fast_orb_hold(BUFS, B) \ + do { \ + *(BUFS)->cur->buf.last |= B; \ + } while (0) + +/* + * Copies all data stored in |bufs| to the contagious buffer. This + * function allocates the contagious memory to store all data in + * |bufs| and assigns it to |*out|. + * + * The contents of |bufs| is left unchanged. + * + * This function returns the length of copied data and assigns the + * pointer to copied data to |*out| if it succeeds, or one of the + * following negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory + */ +ssize_t nghttp2_bufs_remove(nghttp2_bufs *bufs, uint8_t **out); + +/* + * Copies all data stored in |bufs| to |out|. This function assumes + * that the buffer space pointed by |out| has at least + * nghttp2_bufs(bufs) bytes. + * + * The contents of |bufs| is left unchanged. + * + * This function returns the length of copied data. + */ +size_t nghttp2_bufs_remove_copy(nghttp2_bufs *bufs, uint8_t *out); + +/* + * Resets |bufs| and makes the buffers empty. + */ +void nghttp2_bufs_reset(nghttp2_bufs *bufs); + +/* + * Moves bufs->cur to bufs->cur->next. If resulting bufs->cur is + * NULL, this function allocates new buffers and bufs->cur points to + * it. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory + * NGHTTP2_ERR_BUFFER_ERROR + * Out of buffer space. + */ +int nghttp2_bufs_advance(nghttp2_bufs *bufs); + +/* Sets bufs->cur to bufs->head */ +#define nghttp2_bufs_rewind(BUFS) \ + do { \ + (BUFS)->cur = (BUFS)->head; \ + } while (0) + +/* + * Move bufs->cur, from the current position, using next member, to + * the last buf which has nghttp2_buf_len(buf) > 0 without seeing buf + * which satisfies nghttp2_buf_len(buf) == 0. If + * nghttp2_buf_len(&bufs->cur->buf) == 0 or bufs->cur->next is NULL, + * bufs->cur is unchanged. + */ +void nghttp2_bufs_seek_last_present(nghttp2_bufs *bufs); + +/* + * Returns nonzero if bufs->cur->next is not emtpy. + */ +int nghttp2_bufs_next_present(nghttp2_bufs *bufs); + +#define nghttp2_bufs_cur_avail(BUFS) nghttp2_buf_avail(&(BUFS)->cur->buf) + +/* + * Returns the buffer length of |bufs|. + */ +ssize_t nghttp2_bufs_len(nghttp2_bufs *bufs); + +#endif /* NGHTTP2_BUF_H */ diff --git a/lib/nghttp2_callbacks.c b/lib/nghttp2_callbacks.c new file mode 100644 index 0000000..3c4be9a --- /dev/null +++ b/lib/nghttp2_callbacks.c @@ -0,0 +1,129 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2014 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "nghttp2_callbacks.h" + +#include + +int nghttp2_session_callbacks_new(nghttp2_session_callbacks **callbacks_ptr) { + *callbacks_ptr = calloc(1, sizeof(nghttp2_session_callbacks)); + + if (*callbacks_ptr == NULL) { + return NGHTTP2_ERR_NOMEM; + } + + return 0; +} + +void nghttp2_session_callbacks_del(nghttp2_session_callbacks *callbacks) { + free(callbacks); +} + +void nghttp2_session_callbacks_set_send_callback( + nghttp2_session_callbacks *cbs, nghttp2_send_callback send_callback) { + cbs->send_callback = send_callback; +} + +void nghttp2_session_callbacks_set_recv_callback( + nghttp2_session_callbacks *cbs, nghttp2_recv_callback recv_callback) { + cbs->recv_callback = recv_callback; +} + +void nghttp2_session_callbacks_set_on_frame_recv_callback( + nghttp2_session_callbacks *cbs, + nghttp2_on_frame_recv_callback on_frame_recv_callback) { + cbs->on_frame_recv_callback = on_frame_recv_callback; +} + +void nghttp2_session_callbacks_set_on_invalid_frame_recv_callback( + nghttp2_session_callbacks *cbs, + nghttp2_on_invalid_frame_recv_callback on_invalid_frame_recv_callback) { + cbs->on_invalid_frame_recv_callback = on_invalid_frame_recv_callback; +} + +void nghttp2_session_callbacks_set_on_data_chunk_recv_callback( + nghttp2_session_callbacks *cbs, + nghttp2_on_data_chunk_recv_callback on_data_chunk_recv_callback) { + cbs->on_data_chunk_recv_callback = on_data_chunk_recv_callback; +} + +void nghttp2_session_callbacks_set_before_frame_send_callback( + nghttp2_session_callbacks *cbs, + nghttp2_before_frame_send_callback before_frame_send_callback) { + cbs->before_frame_send_callback = before_frame_send_callback; +} + +void nghttp2_session_callbacks_set_on_frame_send_callback( + nghttp2_session_callbacks *cbs, + nghttp2_on_frame_send_callback on_frame_send_callback) { + cbs->on_frame_send_callback = on_frame_send_callback; +} + +void nghttp2_session_callbacks_set_on_frame_not_send_callback( + nghttp2_session_callbacks *cbs, + nghttp2_on_frame_not_send_callback on_frame_not_send_callback) { + cbs->on_frame_not_send_callback = on_frame_not_send_callback; +} + +void nghttp2_session_callbacks_set_on_stream_close_callback( + nghttp2_session_callbacks *cbs, + nghttp2_on_stream_close_callback on_stream_close_callback) { + cbs->on_stream_close_callback = on_stream_close_callback; +} + +void nghttp2_session_callbacks_set_on_begin_headers_callback( + nghttp2_session_callbacks *cbs, + nghttp2_on_begin_headers_callback on_begin_headers_callback) { + cbs->on_begin_headers_callback = on_begin_headers_callback; +} + +void nghttp2_session_callbacks_set_on_header_callback( + nghttp2_session_callbacks *cbs, + nghttp2_on_header_callback on_header_callback) { + cbs->on_header_callback = on_header_callback; +} + +void nghttp2_session_callbacks_set_select_padding_callback( + nghttp2_session_callbacks *cbs, + nghttp2_select_padding_callback select_padding_callback) { + cbs->select_padding_callback = select_padding_callback; +} + +void nghttp2_session_callbacks_set_data_source_read_length_callback( + nghttp2_session_callbacks *cbs, + nghttp2_data_source_read_length_callback data_source_read_length_callback) { + cbs->read_length_callback = data_source_read_length_callback; +} + +void nghttp2_session_callbacks_set_on_begin_frame_callback( + nghttp2_session_callbacks *cbs, + nghttp2_on_begin_frame_callback on_begin_frame_callback) { + cbs->on_begin_frame_callback = on_begin_frame_callback; +} + +void nghttp2_session_callbacks_set_send_data_callback( + nghttp2_session_callbacks *cbs, + nghttp2_send_data_callback send_data_callback) { + cbs->send_data_callback = send_data_callback; +} diff --git a/lib/nghttp2_callbacks.h b/lib/nghttp2_callbacks.h new file mode 100644 index 0000000..37958ea --- /dev/null +++ b/lib/nghttp2_callbacks.h @@ -0,0 +1,112 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2014 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGHTTP2_CALLBACKS_H +#define NGHTTP2_CALLBACKS_H + +#ifdef HAVE_CONFIG_H +#include +#endif /* HAVE_CONFIG_H */ + +#include + +/* + * Callback functions. + */ +struct nghttp2_session_callbacks { + /** + * Callback function invoked when the session wants to send data to + * the remote peer. This callback is not necessary if the + * application uses solely `nghttp2_session_mem_send()` to serialize + * data to transmit. + */ + nghttp2_send_callback send_callback; + /** + * Callback function invoked when the session wants to receive data + * from the remote peer. This callback is not necessary if the + * application uses solely `nghttp2_session_mem_recv()` to process + * received data. + */ + nghttp2_recv_callback recv_callback; + /** + * Callback function invoked by `nghttp2_session_recv()` when a + * frame is received. + */ + nghttp2_on_frame_recv_callback on_frame_recv_callback; + /** + * Callback function invoked by `nghttp2_session_recv()` when an + * invalid non-DATA frame is received. + */ + nghttp2_on_invalid_frame_recv_callback on_invalid_frame_recv_callback; + /** + * Callback function invoked when a chunk of data in DATA frame is + * received. + */ + nghttp2_on_data_chunk_recv_callback on_data_chunk_recv_callback; + /** + * Callback function invoked before a non-DATA frame is sent. + */ + nghttp2_before_frame_send_callback before_frame_send_callback; + /** + * Callback function invoked after a frame is sent. + */ + nghttp2_on_frame_send_callback on_frame_send_callback; + /** + * The callback function invoked when a non-DATA frame is not sent + * because of an error. + */ + nghttp2_on_frame_not_send_callback on_frame_not_send_callback; + /** + * Callback function invoked when the stream is closed. + */ + nghttp2_on_stream_close_callback on_stream_close_callback; + /** + * Callback function invoked when the reception of header block in + * HEADERS or PUSH_PROMISE is started. + */ + nghttp2_on_begin_headers_callback on_begin_headers_callback; + /** + * Callback function invoked when a header name/value pair is + * received. + */ + nghttp2_on_header_callback on_header_callback; + /** + * Callback function invoked when the library asks application how + * many padding bytes are required for the transmission of the given + * frame. + */ + nghttp2_select_padding_callback select_padding_callback; + /** + * The callback function used to determine the length allowed in + * `nghttp2_data_source_read_callback()` + */ + nghttp2_data_source_read_length_callback read_length_callback; + /** + * Sets callback function invoked when a frame header is received. + */ + nghttp2_on_begin_frame_callback on_begin_frame_callback; + nghttp2_send_data_callback send_data_callback; +}; + +#endif /* NGHTTP2_CALLBACKS_H */ diff --git a/lib/nghttp2_frame.c b/lib/nghttp2_frame.c new file mode 100644 index 0000000..07f93a1 --- /dev/null +++ b/lib/nghttp2_frame.c @@ -0,0 +1,888 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2013 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "nghttp2_frame.h" + +#include +#include +#include +#include + +#include "nghttp2_helper.h" +#include "nghttp2_net.h" +#include "nghttp2_priority_spec.h" + +void nghttp2_frame_pack_frame_hd(uint8_t *buf, const nghttp2_frame_hd *hd) { + nghttp2_put_uint32be(&buf[0], (uint32_t)(hd->length << 8)); + buf[3] = hd->type; + buf[4] = hd->flags; + nghttp2_put_uint32be(&buf[5], hd->stream_id); + /* ignore hd->reserved for now */ +} + +void nghttp2_frame_unpack_frame_hd(nghttp2_frame_hd *hd, const uint8_t *buf) { + hd->length = nghttp2_get_uint32(&buf[0]) >> 8; + hd->type = buf[3]; + hd->flags = buf[4]; + hd->stream_id = nghttp2_get_uint32(&buf[5]) & NGHTTP2_STREAM_ID_MASK; + hd->reserved = 0; +} + +void nghttp2_frame_hd_init(nghttp2_frame_hd *hd, size_t length, uint8_t type, + uint8_t flags, int32_t stream_id) { + hd->length = length; + hd->type = type; + hd->flags = flags; + hd->stream_id = stream_id; + hd->reserved = 0; +} + +void nghttp2_frame_headers_init(nghttp2_headers *frame, uint8_t flags, + int32_t stream_id, nghttp2_headers_category cat, + const nghttp2_priority_spec *pri_spec, + nghttp2_nv *nva, size_t nvlen) { + nghttp2_frame_hd_init(&frame->hd, 0, NGHTTP2_HEADERS, flags, stream_id); + frame->padlen = 0; + frame->nva = nva; + frame->nvlen = nvlen; + frame->cat = cat; + + if (pri_spec) { + frame->pri_spec = *pri_spec; + } else { + nghttp2_priority_spec_default_init(&frame->pri_spec); + } +} + +void nghttp2_frame_headers_free(nghttp2_headers *frame, nghttp2_mem *mem) { + nghttp2_nv_array_del(frame->nva, mem); +} + +void nghttp2_frame_priority_init(nghttp2_priority *frame, int32_t stream_id, + const nghttp2_priority_spec *pri_spec) { + nghttp2_frame_hd_init(&frame->hd, NGHTTP2_PRIORITY_SPECLEN, NGHTTP2_PRIORITY, + NGHTTP2_FLAG_NONE, stream_id); + frame->pri_spec = *pri_spec; +} + +void nghttp2_frame_priority_free(nghttp2_priority *frame _U_) {} + +void nghttp2_frame_rst_stream_init(nghttp2_rst_stream *frame, int32_t stream_id, + uint32_t error_code) { + nghttp2_frame_hd_init(&frame->hd, 4, NGHTTP2_RST_STREAM, NGHTTP2_FLAG_NONE, + stream_id); + frame->error_code = error_code; +} + +void nghttp2_frame_rst_stream_free(nghttp2_rst_stream *frame _U_) {} + +void nghttp2_frame_settings_init(nghttp2_settings *frame, uint8_t flags, + nghttp2_settings_entry *iv, size_t niv) { + nghttp2_frame_hd_init(&frame->hd, niv * NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH, + NGHTTP2_SETTINGS, flags, 0); + frame->niv = niv; + frame->iv = iv; +} + +void nghttp2_frame_settings_free(nghttp2_settings *frame, nghttp2_mem *mem) { + nghttp2_mem_free(mem, frame->iv); +} + +void nghttp2_frame_push_promise_init(nghttp2_push_promise *frame, uint8_t flags, + int32_t stream_id, + int32_t promised_stream_id, + nghttp2_nv *nva, size_t nvlen) { + nghttp2_frame_hd_init(&frame->hd, 0, NGHTTP2_PUSH_PROMISE, flags, stream_id); + frame->padlen = 0; + frame->nva = nva; + frame->nvlen = nvlen; + frame->promised_stream_id = promised_stream_id; + frame->reserved = 0; +} + +void nghttp2_frame_push_promise_free(nghttp2_push_promise *frame, + nghttp2_mem *mem) { + nghttp2_nv_array_del(frame->nva, mem); +} + +void nghttp2_frame_ping_init(nghttp2_ping *frame, uint8_t flags, + const uint8_t *opaque_data) { + nghttp2_frame_hd_init(&frame->hd, 8, NGHTTP2_PING, flags, 0); + if (opaque_data) { + memcpy(frame->opaque_data, opaque_data, sizeof(frame->opaque_data)); + } else { + memset(frame->opaque_data, 0, sizeof(frame->opaque_data)); + } +} + +void nghttp2_frame_ping_free(nghttp2_ping *frame _U_) {} + +void nghttp2_frame_goaway_init(nghttp2_goaway *frame, int32_t last_stream_id, + uint32_t error_code, uint8_t *opaque_data, + size_t opaque_data_len) { + nghttp2_frame_hd_init(&frame->hd, 8 + opaque_data_len, NGHTTP2_GOAWAY, + NGHTTP2_FLAG_NONE, 0); + frame->last_stream_id = last_stream_id; + frame->error_code = error_code; + frame->opaque_data = opaque_data; + frame->opaque_data_len = opaque_data_len; + frame->reserved = 0; +} + +void nghttp2_frame_goaway_free(nghttp2_goaway *frame, nghttp2_mem *mem) { + nghttp2_mem_free(mem, frame->opaque_data); +} + +void nghttp2_frame_window_update_init(nghttp2_window_update *frame, + uint8_t flags, int32_t stream_id, + int32_t window_size_increment) { + nghttp2_frame_hd_init(&frame->hd, 4, NGHTTP2_WINDOW_UPDATE, flags, stream_id); + frame->window_size_increment = window_size_increment; + frame->reserved = 0; +} + +void nghttp2_frame_window_update_free(nghttp2_window_update *frame _U_) {} + +size_t nghttp2_frame_trail_padlen(nghttp2_frame *frame, size_t padlen) { + return padlen - ((frame->hd.flags & NGHTTP2_FLAG_PADDED) > 0); +} + +void nghttp2_frame_data_init(nghttp2_data *frame, uint8_t flags, + int32_t stream_id) { + /* At this moment, the length of DATA frame is unknown */ + nghttp2_frame_hd_init(&frame->hd, 0, NGHTTP2_DATA, flags, stream_id); + frame->padlen = 0; +} + +void nghttp2_frame_data_free(nghttp2_data *frame _U_) {} + +size_t nghttp2_frame_priority_len(uint8_t flags) { + if (flags & NGHTTP2_FLAG_PRIORITY) { + return NGHTTP2_PRIORITY_SPECLEN; + } + + return 0; +} + +size_t nghttp2_frame_headers_payload_nv_offset(nghttp2_headers *frame) { + return nghttp2_frame_priority_len(frame->hd.flags); +} + +/* + * Call this function after payload was serialized, but not before + * changing buf->pos and serializing frame header. + * + * This function assumes bufs->cur points to the last buf chain of the + * frame(s). + * + * This function serializes frame header for HEADERS/PUSH_PROMISE and + * handles their successive CONTINUATION frames. + * + * We don't process any padding here. + */ +static int frame_pack_headers_shared(nghttp2_bufs *bufs, + nghttp2_frame_hd *frame_hd) { + nghttp2_buf *buf; + nghttp2_buf_chain *ci, *ce; + nghttp2_frame_hd hd; + + buf = &bufs->head->buf; + + hd = *frame_hd; + hd.length = nghttp2_buf_len(buf); + + DEBUGF(fprintf(stderr, "send: HEADERS/PUSH_PROMISE, payloadlen=%zu\n", + hd.length)); + + /* We have multiple frame buffers, which means one or more + CONTINUATION frame is involved. Remove END_HEADERS flag from the + first frame. */ + if (bufs->head != bufs->cur) { + hd.flags &= ~NGHTTP2_FLAG_END_HEADERS; + } + + buf->pos -= NGHTTP2_FRAME_HDLEN; + nghttp2_frame_pack_frame_hd(buf->pos, &hd); + + if (bufs->head != bufs->cur) { + /* 2nd and later frames are CONTINUATION frames. */ + hd.type = NGHTTP2_CONTINUATION; + /* We don't have no flags except for last CONTINUATION */ + hd.flags = NGHTTP2_FLAG_NONE; + + ce = bufs->cur; + + for (ci = bufs->head->next; ci != ce; ci = ci->next) { + buf = &ci->buf; + + hd.length = nghttp2_buf_len(buf); + + DEBUGF(fprintf(stderr, "send: int CONTINUATION, payloadlen=%zu\n", + hd.length)); + + buf->pos -= NGHTTP2_FRAME_HDLEN; + nghttp2_frame_pack_frame_hd(buf->pos, &hd); + } + + buf = &ci->buf; + hd.length = nghttp2_buf_len(buf); + /* Set END_HEADERS flag for last CONTINUATION */ + hd.flags = NGHTTP2_FLAG_END_HEADERS; + + DEBUGF(fprintf(stderr, "send: last CONTINUATION, payloadlen=%zu\n", + hd.length)); + + buf->pos -= NGHTTP2_FRAME_HDLEN; + nghttp2_frame_pack_frame_hd(buf->pos, &hd); + } + + return 0; +} + +int nghttp2_frame_pack_headers(nghttp2_bufs *bufs, nghttp2_headers *frame, + nghttp2_hd_deflater *deflater) { + size_t nv_offset; + int rv; + nghttp2_buf *buf; + + assert(bufs->head == bufs->cur); + + nv_offset = nghttp2_frame_headers_payload_nv_offset(frame); + + buf = &bufs->cur->buf; + + buf->pos += nv_offset; + buf->last = buf->pos; + + /* This call will adjust buf->last to the correct position */ + rv = nghttp2_hd_deflate_hd_bufs(deflater, bufs, frame->nva, frame->nvlen); + + if (rv == NGHTTP2_ERR_BUFFER_ERROR) { + rv = NGHTTP2_ERR_HEADER_COMP; + } + + buf->pos -= nv_offset; + + if (rv != 0) { + return rv; + } + + if (frame->hd.flags & NGHTTP2_FLAG_PRIORITY) { + nghttp2_frame_pack_priority_spec(buf->pos, &frame->pri_spec); + } + + frame->padlen = 0; + frame->hd.length = nghttp2_bufs_len(bufs); + + return frame_pack_headers_shared(bufs, &frame->hd); +} + +void nghttp2_frame_pack_priority_spec(uint8_t *buf, + const nghttp2_priority_spec *pri_spec) { + nghttp2_put_uint32be(buf, pri_spec->stream_id); + if (pri_spec->exclusive) { + buf[0] |= 0x80; + } + buf[4] = pri_spec->weight - 1; +} + +void nghttp2_frame_unpack_priority_spec(nghttp2_priority_spec *pri_spec, + uint8_t flags _U_, + const uint8_t *payload, + size_t payloadlen _U_) { + int32_t dep_stream_id; + uint8_t exclusive; + int32_t weight; + + dep_stream_id = nghttp2_get_uint32(payload) & NGHTTP2_STREAM_ID_MASK; + exclusive = (payload[0] & 0x80) > 0; + weight = payload[4] + 1; + + nghttp2_priority_spec_init(pri_spec, dep_stream_id, weight, exclusive); +} + +int nghttp2_frame_unpack_headers_payload(nghttp2_headers *frame, + const uint8_t *payload, + size_t payloadlen) { + if (frame->hd.flags & NGHTTP2_FLAG_PRIORITY) { + nghttp2_frame_unpack_priority_spec(&frame->pri_spec, frame->hd.flags, + payload, payloadlen); + } else { + nghttp2_priority_spec_default_init(&frame->pri_spec); + } + + frame->nva = NULL; + frame->nvlen = 0; + + return 0; +} + +int nghttp2_frame_pack_priority(nghttp2_bufs *bufs, nghttp2_priority *frame) { + nghttp2_buf *buf; + + assert(bufs->head == bufs->cur); + + buf = &bufs->head->buf; + + assert(nghttp2_buf_avail(buf) >= NGHTTP2_PRIORITY_SPECLEN); + + buf->pos -= NGHTTP2_FRAME_HDLEN; + + nghttp2_frame_pack_frame_hd(buf->pos, &frame->hd); + + nghttp2_frame_pack_priority_spec(buf->last, &frame->pri_spec); + + buf->last += NGHTTP2_PRIORITY_SPECLEN; + + return 0; +} + +void nghttp2_frame_unpack_priority_payload(nghttp2_priority *frame, + const uint8_t *payload, + size_t payloadlen) { + nghttp2_frame_unpack_priority_spec(&frame->pri_spec, frame->hd.flags, payload, + payloadlen); +} + +int nghttp2_frame_pack_rst_stream(nghttp2_bufs *bufs, + nghttp2_rst_stream *frame) { + nghttp2_buf *buf; + + assert(bufs->head == bufs->cur); + + buf = &bufs->head->buf; + + assert(nghttp2_buf_avail(buf) >= 4); + + buf->pos -= NGHTTP2_FRAME_HDLEN; + + nghttp2_frame_pack_frame_hd(buf->pos, &frame->hd); + + nghttp2_put_uint32be(buf->last, frame->error_code); + buf->last += 4; + + return 0; +} + +void nghttp2_frame_unpack_rst_stream_payload(nghttp2_rst_stream *frame, + const uint8_t *payload, + size_t payloadlen _U_) { + frame->error_code = nghttp2_get_uint32(payload); +} + +int nghttp2_frame_pack_settings(nghttp2_bufs *bufs, nghttp2_settings *frame) { + nghttp2_buf *buf; + + assert(bufs->head == bufs->cur); + + buf = &bufs->head->buf; + + if (nghttp2_buf_avail(buf) < (ssize_t)frame->hd.length) { + return NGHTTP2_ERR_FRAME_SIZE_ERROR; + } + + buf->pos -= NGHTTP2_FRAME_HDLEN; + + nghttp2_frame_pack_frame_hd(buf->pos, &frame->hd); + + buf->last += + nghttp2_frame_pack_settings_payload(buf->last, frame->iv, frame->niv); + + return 0; +} + +size_t nghttp2_frame_pack_settings_payload(uint8_t *buf, + const nghttp2_settings_entry *iv, + size_t niv) { + size_t i; + for (i = 0; i < niv; ++i, buf += NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH) { + nghttp2_put_uint16be(buf, iv[i].settings_id); + nghttp2_put_uint32be(buf + 2, iv[i].value); + } + return NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH * niv; +} + +int nghttp2_frame_unpack_settings_payload(nghttp2_settings *frame, + nghttp2_settings_entry *iv, + size_t niv, nghttp2_mem *mem) { + size_t payloadlen = niv * sizeof(nghttp2_settings_entry); + + if (niv == 0) { + frame->iv = NULL; + } else { + frame->iv = nghttp2_mem_malloc(mem, payloadlen); + + if (frame->iv == NULL) { + return NGHTTP2_ERR_NOMEM; + } + + memcpy(frame->iv, iv, payloadlen); + } + + frame->niv = niv; + return 0; +} + +void nghttp2_frame_unpack_settings_entry(nghttp2_settings_entry *iv, + const uint8_t *payload) { + iv->settings_id = nghttp2_get_uint16(&payload[0]); + iv->value = nghttp2_get_uint32(&payload[2]); +} + +int nghttp2_frame_unpack_settings_payload2(nghttp2_settings_entry **iv_ptr, + size_t *niv_ptr, + const uint8_t *payload, + size_t payloadlen, + nghttp2_mem *mem) { + size_t i; + + *niv_ptr = payloadlen / NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH; + + if (*niv_ptr == 0) { + *iv_ptr = NULL; + + return 0; + } + + *iv_ptr = + nghttp2_mem_malloc(mem, (*niv_ptr) * sizeof(nghttp2_settings_entry)); + + if (*iv_ptr == NULL) { + return NGHTTP2_ERR_NOMEM; + } + + for (i = 0; i < *niv_ptr; ++i) { + size_t off = i * NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH; + nghttp2_frame_unpack_settings_entry(&(*iv_ptr)[i], &payload[off]); + } + + return 0; +} + +int nghttp2_frame_pack_push_promise(nghttp2_bufs *bufs, + nghttp2_push_promise *frame, + nghttp2_hd_deflater *deflater) { + size_t nv_offset = 4; + int rv; + nghttp2_buf *buf; + + assert(bufs->head == bufs->cur); + + buf = &bufs->cur->buf; + + buf->pos += nv_offset; + buf->last = buf->pos; + + /* This call will adjust buf->last to the correct position */ + rv = nghttp2_hd_deflate_hd_bufs(deflater, bufs, frame->nva, frame->nvlen); + + if (rv == NGHTTP2_ERR_BUFFER_ERROR) { + rv = NGHTTP2_ERR_HEADER_COMP; + } + + buf->pos -= nv_offset; + + if (rv != 0) { + return rv; + } + + nghttp2_put_uint32be(buf->pos, frame->promised_stream_id); + + frame->padlen = 0; + frame->hd.length = nghttp2_bufs_len(bufs); + + return frame_pack_headers_shared(bufs, &frame->hd); +} + +int nghttp2_frame_unpack_push_promise_payload(nghttp2_push_promise *frame, + const uint8_t *payload, + size_t payloadlen _U_) { + frame->promised_stream_id = + nghttp2_get_uint32(payload) & NGHTTP2_STREAM_ID_MASK; + frame->nva = NULL; + frame->nvlen = 0; + return 0; +} + +int nghttp2_frame_pack_ping(nghttp2_bufs *bufs, nghttp2_ping *frame) { + nghttp2_buf *buf; + + assert(bufs->head == bufs->cur); + + buf = &bufs->head->buf; + + assert(nghttp2_buf_avail(buf) >= 8); + + buf->pos -= NGHTTP2_FRAME_HDLEN; + + nghttp2_frame_pack_frame_hd(buf->pos, &frame->hd); + + buf->last = + nghttp2_cpymem(buf->last, frame->opaque_data, sizeof(frame->opaque_data)); + + return 0; +} + +void nghttp2_frame_unpack_ping_payload(nghttp2_ping *frame, + const uint8_t *payload, + size_t payloadlen _U_) { + memcpy(frame->opaque_data, payload, sizeof(frame->opaque_data)); +} + +int nghttp2_frame_pack_goaway(nghttp2_bufs *bufs, nghttp2_goaway *frame) { + int rv; + nghttp2_buf *buf; + + assert(bufs->head == bufs->cur); + + buf = &bufs->head->buf; + + buf->pos -= NGHTTP2_FRAME_HDLEN; + + nghttp2_frame_pack_frame_hd(buf->pos, &frame->hd); + + nghttp2_put_uint32be(buf->last, frame->last_stream_id); + buf->last += 4; + + nghttp2_put_uint32be(buf->last, frame->error_code); + buf->last += 4; + + rv = nghttp2_bufs_add(bufs, frame->opaque_data, frame->opaque_data_len); + + if (rv == NGHTTP2_ERR_BUFFER_ERROR) { + return NGHTTP2_ERR_FRAME_SIZE_ERROR; + } + + if (rv != 0) { + return rv; + } + + return 0; +} + +void nghttp2_frame_unpack_goaway_payload(nghttp2_goaway *frame, + const uint8_t *payload, + size_t payloadlen _U_, + uint8_t *var_gift_payload, + size_t var_gift_payloadlen) { + frame->last_stream_id = nghttp2_get_uint32(payload) & NGHTTP2_STREAM_ID_MASK; + frame->error_code = nghttp2_get_uint32(payload + 4); + + frame->opaque_data = var_gift_payload; + frame->opaque_data_len = var_gift_payloadlen; +} + +int nghttp2_frame_unpack_goaway_payload2(nghttp2_goaway *frame, + const uint8_t *payload, + size_t payloadlen, nghttp2_mem *mem) { + uint8_t *var_gift_payload; + size_t var_gift_payloadlen; + + if (payloadlen > 8) { + var_gift_payloadlen = payloadlen - 8; + } else { + var_gift_payloadlen = 0; + } + + payloadlen -= var_gift_payloadlen; + + if (!var_gift_payloadlen) { + var_gift_payload = NULL; + } else { + var_gift_payload = nghttp2_mem_malloc(mem, var_gift_payloadlen); + + if (var_gift_payload == NULL) { + return NGHTTP2_ERR_NOMEM; + } + + memcpy(var_gift_payload, payload + 8, var_gift_payloadlen); + } + + nghttp2_frame_unpack_goaway_payload(frame, payload, payloadlen, + var_gift_payload, var_gift_payloadlen); + + return 0; +} + +int nghttp2_frame_pack_window_update(nghttp2_bufs *bufs, + nghttp2_window_update *frame) { + nghttp2_buf *buf; + + assert(bufs->head == bufs->cur); + + buf = &bufs->head->buf; + + assert(nghttp2_buf_avail(buf) >= 4); + + buf->pos -= NGHTTP2_FRAME_HDLEN; + + nghttp2_frame_pack_frame_hd(buf->pos, &frame->hd); + + nghttp2_put_uint32be(buf->last, frame->window_size_increment); + buf->last += 4; + + return 0; +} + +void nghttp2_frame_unpack_window_update_payload(nghttp2_window_update *frame, + const uint8_t *payload, + size_t payloadlen _U_) { + frame->window_size_increment = + nghttp2_get_uint32(payload) & NGHTTP2_WINDOW_SIZE_INCREMENT_MASK; +} + +nghttp2_settings_entry *nghttp2_frame_iv_copy(const nghttp2_settings_entry *iv, + size_t niv, nghttp2_mem *mem) { + nghttp2_settings_entry *iv_copy; + size_t len = niv * sizeof(nghttp2_settings_entry); + + if (len == 0) { + return NULL; + } + + iv_copy = nghttp2_mem_malloc(mem, len); + + if (iv_copy == NULL) { + return NULL; + } + + memcpy(iv_copy, iv, len); + + return iv_copy; +} + +int nghttp2_nv_equal(const nghttp2_nv *a, const nghttp2_nv *b) { + return a->namelen == b->namelen && a->valuelen == b->valuelen && + memcmp(a->name, b->name, a->namelen) == 0 && + memcmp(a->value, b->value, a->valuelen) == 0; +} + +void nghttp2_nv_array_del(nghttp2_nv *nva, nghttp2_mem *mem) { + nghttp2_mem_free(mem, nva); +} + +static int bytes_compar(const uint8_t *a, size_t alen, const uint8_t *b, + size_t blen) { + int rv; + + if (alen == blen) { + return memcmp(a, b, alen); + } + + if (alen < blen) { + rv = memcmp(a, b, alen); + + if (rv == 0) { + return -1; + } + + return rv; + } + + rv = memcmp(a, b, blen); + + if (rv == 0) { + return 1; + } + + return rv; +} + +int nghttp2_nv_compare_name(const nghttp2_nv *lhs, const nghttp2_nv *rhs) { + return bytes_compar(lhs->name, lhs->namelen, rhs->name, rhs->namelen); +} + +static int nv_compar(const void *lhs, const void *rhs) { + const nghttp2_nv *a = (const nghttp2_nv *)lhs; + const nghttp2_nv *b = (const nghttp2_nv *)rhs; + int rv; + + rv = bytes_compar(a->name, a->namelen, b->name, b->namelen); + + if (rv == 0) { + return bytes_compar(a->value, a->valuelen, b->value, b->valuelen); + } + + return rv; +} + +void nghttp2_nv_array_sort(nghttp2_nv *nva, size_t nvlen) { + qsort(nva, nvlen, sizeof(nghttp2_nv), nv_compar); +} + +int nghttp2_nv_array_copy(nghttp2_nv **nva_ptr, const nghttp2_nv *nva, + size_t nvlen, nghttp2_mem *mem) { + size_t i; + uint8_t *data; + size_t buflen = 0; + nghttp2_nv *p; + + for (i = 0; i < nvlen; ++i) { + /* + 2 for null-termination */ + buflen += nva[i].namelen + nva[i].valuelen + 2; + } + + if (nvlen == 0) { + *nva_ptr = NULL; + + return 0; + } + + buflen += sizeof(nghttp2_nv) * nvlen; + + *nva_ptr = nghttp2_mem_malloc(mem, buflen); + + if (*nva_ptr == NULL) { + return NGHTTP2_ERR_NOMEM; + } + + p = *nva_ptr; + data = (uint8_t *)(*nva_ptr) + sizeof(nghttp2_nv) * nvlen; + + for (i = 0; i < nvlen; ++i) { + p->flags = nva[i].flags; + + memcpy(data, nva[i].name, nva[i].namelen); + p->name = data; + p->namelen = nva[i].namelen; + data[p->namelen] = '\0'; + nghttp2_downcase(p->name, p->namelen); + data += nva[i].namelen + 1; + memcpy(data, nva[i].value, nva[i].valuelen); + p->value = data; + p->valuelen = nva[i].valuelen; + data[p->valuelen] = '\0'; + data += nva[i].valuelen + 1; + ++p; + } + return 0; +} + +int nghttp2_iv_check(const nghttp2_settings_entry *iv, size_t niv) { + size_t i; + for (i = 0; i < niv; ++i) { + switch (iv[i].settings_id) { + case NGHTTP2_SETTINGS_HEADER_TABLE_SIZE: + if (iv[i].value > NGHTTP2_MAX_HEADER_TABLE_SIZE) { + return 0; + } + break; + case NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS: + break; + case NGHTTP2_SETTINGS_ENABLE_PUSH: + if (iv[i].value != 0 && iv[i].value != 1) { + return 0; + } + break; + case NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE: + if (iv[i].value > (uint32_t)NGHTTP2_MAX_WINDOW_SIZE) { + return 0; + } + break; + case NGHTTP2_SETTINGS_MAX_FRAME_SIZE: + if (iv[i].value < NGHTTP2_MAX_FRAME_SIZE_MIN || + iv[i].value > NGHTTP2_MAX_FRAME_SIZE_MAX) { + return 0; + } + break; + case NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE: + break; + } + } + return 1; +} + +static void frame_set_pad(nghttp2_buf *buf, size_t padlen, int framehd_only) { + size_t trail_padlen; + size_t newlen; + + DEBUGF(fprintf(stderr, "send: padlen=%zu, shift left 1 bytes\n", padlen)); + + memmove(buf->pos - 1, buf->pos, NGHTTP2_FRAME_HDLEN); + + --buf->pos; + + buf->pos[4] |= NGHTTP2_FLAG_PADDED; + + newlen = (nghttp2_get_uint32(buf->pos) >> 8) + padlen; + nghttp2_put_uint32be(buf->pos, (uint32_t)((newlen << 8) + buf->pos[3])); + + if (framehd_only) { + return; + } + + trail_padlen = padlen - 1; + buf->pos[NGHTTP2_FRAME_HDLEN] = trail_padlen; + + /* zero out padding */ + memset(buf->last, 0, trail_padlen); + /* extend buffers trail_padlen bytes, since we ate previous padlen - + trail_padlen byte(s) */ + buf->last += trail_padlen; +} + +int nghttp2_frame_add_pad(nghttp2_bufs *bufs, nghttp2_frame_hd *hd, + size_t padlen, int framehd_only) { + nghttp2_buf *buf; + + if (padlen == 0) { + DEBUGF(fprintf(stderr, "send: padlen = 0, nothing to do\n")); + + return 0; + } + + /* + * We have arranged bufs like this: + * + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | |Frame header | Frame payload... : + * +-+-----------------+-------------------------------------------+ + * | |Frame header | Frame payload... : + * +-+-----------------+-------------------------------------------+ + * | |Frame header | Frame payload... : + * +-+-----------------+-------------------------------------------+ + * + * We arranged padding so that it is included in the first frame + * completely. For padded frame, we are going to adjust buf->pos of + * frame which includes padding and serialize (memmove) frame header + * in the correct position. Also extends buf->last to include + * padding. + */ + + buf = &bufs->head->buf; + + assert(nghttp2_buf_avail(buf) >= (ssize_t)padlen - 1); + + frame_set_pad(buf, padlen, framehd_only); + + hd->length += padlen; + hd->flags |= NGHTTP2_FLAG_PADDED; + + DEBUGF(fprintf(stderr, "send: final payloadlen=%zu, padlen=%zu\n", hd->length, + padlen)); + + return 0; +} diff --git a/lib/nghttp2_frame.h b/lib/nghttp2_frame.h new file mode 100644 index 0000000..7fc336c --- /dev/null +++ b/lib/nghttp2_frame.h @@ -0,0 +1,527 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGHTTP2_FRAME_H +#define NGHTTP2_FRAME_H + +#ifdef HAVE_CONFIG_H +#include +#endif /* HAVE_CONFIG_H */ + +#include +#include "nghttp2_hd.h" +#include "nghttp2_buf.h" + +#define NGHTTP2_STREAM_ID_MASK ((1u << 31) - 1) +#define NGHTTP2_PRI_GROUP_ID_MASK ((1u << 31) - 1) +#define NGHTTP2_PRIORITY_MASK ((1u << 31) - 1) +#define NGHTTP2_WINDOW_SIZE_INCREMENT_MASK ((1u << 31) - 1) +#define NGHTTP2_SETTINGS_ID_MASK ((1 << 24) - 1) + +/* The number of bytes of frame header. */ +#define NGHTTP2_FRAME_HDLEN 9 + +#define NGHTTP2_MAX_FRAME_SIZE_MAX ((1 << 24) - 1) +#define NGHTTP2_MAX_FRAME_SIZE_MIN (1 << 14) + +#define NGHTTP2_MAX_PAYLOADLEN 16384 +/* The one frame buffer length for tranmission. We may use several of + them to support CONTINUATION. To account for Pad Length field, we + allocate extra 1 byte, which saves extra large memcopying. */ +#define NGHTTP2_FRAMEBUF_CHUNKLEN \ + (NGHTTP2_FRAME_HDLEN + 1 + NGHTTP2_MAX_PAYLOADLEN) + +/* Number of inbound buffer */ +#define NGHTTP2_FRAMEBUF_MAX_NUM 5 + +/* The default length of DATA frame payload. */ +#define NGHTTP2_DATA_PAYLOADLEN NGHTTP2_MAX_FRAME_SIZE_MIN + +/* Maximum headers payload length, calculated in compressed form. + This applies to transmission only. */ +#define NGHTTP2_MAX_HEADERSLEN 65536 + +/* The number of bytes for each SETTINGS entry */ +#define NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH 6 + +/* The maximum header table size in SETTINGS_HEADER_TABLE_SIZE */ +#define NGHTTP2_MAX_HEADER_TABLE_SIZE ((1u << 31) - 1) + +/* Length of priority related fields in HEADERS/PRIORITY frames */ +#define NGHTTP2_PRIORITY_SPECLEN 5 + +/* Maximum length of padding in bytes. */ +#define NGHTTP2_MAX_PADLEN 256 + +/* Union of extension frame payload */ +typedef union { int dummy; } nghttp2_ext_frame_payload; + +void nghttp2_frame_pack_frame_hd(uint8_t *buf, const nghttp2_frame_hd *hd); + +void nghttp2_frame_unpack_frame_hd(nghttp2_frame_hd *hd, const uint8_t *buf); + +/** + * Initializes frame header |hd| with given parameters. Reserved bit + * is set to 0. + */ +void nghttp2_frame_hd_init(nghttp2_frame_hd *hd, size_t length, uint8_t type, + uint8_t flags, int32_t stream_id); + +/** + * Returns the number of priority field depending on the |flags|. If + * |flags| has neither NGHTTP2_FLAG_PRIORITY_GROUP nor + * NGHTTP2_FLAG_PRIORITY_DEPENDENCY set, return 0. + */ +size_t nghttp2_frame_priority_len(uint8_t flags); + +/** + * Packs the |pri_spec| in |buf|. This function assumes |buf| has + * enough space for serialization. + */ +void nghttp2_frame_pack_priority_spec(uint8_t *buf, + const nghttp2_priority_spec *pri_spec); + +/** + * Unpacks the priority specification from payload |payload| of length + * |payloadlen| to |pri_spec|. The |flags| is used to determine what + * kind of priority specification is in |payload|. This function + * assumes the |payload| contains whole priority specification. + */ +void nghttp2_frame_unpack_priority_spec(nghttp2_priority_spec *pri_spec, + uint8_t flags, const uint8_t *payload, + size_t payloadlen); + +/* + * Returns the offset from the HEADERS frame payload where the + * compressed header block starts. The frame payload does not include + * frame header. + */ +size_t nghttp2_frame_headers_payload_nv_offset(nghttp2_headers *frame); + +/* + * Packs HEADERS frame |frame| in wire format and store it in |bufs|. + * This function expands |bufs| as necessary to store frame. + * + * The caller must make sure that nghttp2_bufs_reset(bufs) is called + * before calling this function. + * + * frame->hd.length is assigned after length is determined during + * packing process. CONTINUATION frames are also serialized in this + * function. This function does not handle padding. + * + * This function returns 0 if it succeeds, or returns one of the + * following negative error codes: + * + * NGHTTP2_ERR_HEADER_COMP + * The deflate operation failed. + * NGHTTP2_ERR_NOMEM + * Out of memory. + */ +int nghttp2_frame_pack_headers(nghttp2_bufs *bufs, nghttp2_headers *frame, + nghttp2_hd_deflater *deflater); + +/* + * Unpacks HEADERS frame byte sequence into |frame|. This function + * only unapcks bytes that come before name/value header block and + * after possible Pad Length field. + * + * This function always succeeds and returns 0. + */ +int nghttp2_frame_unpack_headers_payload(nghttp2_headers *frame, + const uint8_t *payload, + size_t payloadlen); + +/* + * Packs PRIORITY frame |frame| in wire format and store it in + * |bufs|. + * + * The caller must make sure that nghttp2_bufs_reset(bufs) is called + * before calling this function. + * + * This function always succeeds and returns 0. + */ +int nghttp2_frame_pack_priority(nghttp2_bufs *bufs, nghttp2_priority *frame); + +/* + * Unpacks PRIORITY wire format into |frame|. + */ +void nghttp2_frame_unpack_priority_payload(nghttp2_priority *frame, + const uint8_t *payload, + size_t payloadlen); + +/* + * Packs RST_STREAM frame |frame| in wire frame format and store it in + * |bufs|. + * + * The caller must make sure that nghttp2_bufs_reset(bufs) is called + * before calling this function. + * + * This function always succeeds and returns 0. + */ +int nghttp2_frame_pack_rst_stream(nghttp2_bufs *bufs, + nghttp2_rst_stream *frame); + +/* + * Unpacks RST_STREAM frame byte sequence into |frame|. + */ +void nghttp2_frame_unpack_rst_stream_payload(nghttp2_rst_stream *frame, + const uint8_t *payload, + size_t payloadlen); + +/* + * Packs SETTINGS frame |frame| in wire format and store it in + * |bufs|. + * + * The caller must make sure that nghttp2_bufs_reset(bufs) is called + * before calling this function. + * + * This function returns 0 if it succeeds, or returns one of the + * following negative error codes: + * + * NGHTTP2_ERR_FRAME_SIZE_ERROR + * The length of the frame is too large. + */ +int nghttp2_frame_pack_settings(nghttp2_bufs *bufs, nghttp2_settings *frame); + +/* + * Packs the |iv|, which includes |niv| entries, in the |buf|, + * assuming the |buf| has at least 8 * |niv| bytes. + * + * Returns the number of bytes written into the |buf|. + */ +size_t nghttp2_frame_pack_settings_payload(uint8_t *buf, + const nghttp2_settings_entry *iv, + size_t niv); + +void nghttp2_frame_unpack_settings_entry(nghttp2_settings_entry *iv, + const uint8_t *payload); + +/* + * Makes a copy of |iv| in frame->settings.iv. The |niv| is assigned + * to frame->settings.niv. + * + * This function returns 0 if it succeeds or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + */ +int nghttp2_frame_unpack_settings_payload(nghttp2_settings *frame, + nghttp2_settings_entry *iv, + size_t niv, nghttp2_mem *mem); + +/* + * Unpacks SETTINGS payload into |*iv_ptr|. The number of entries are + * assigned to the |*niv_ptr|. This function allocates enough memory + * to store the result in |*iv_ptr|. The caller is responsible to free + * |*iv_ptr| after its use. + * + * This function returns 0 if it succeeds or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + */ +int nghttp2_frame_unpack_settings_payload2(nghttp2_settings_entry **iv_ptr, + size_t *niv_ptr, + const uint8_t *payload, + size_t payloadlen, nghttp2_mem *mem); + +/* + * Packs PUSH_PROMISE frame |frame| in wire format and store it in + * |bufs|. This function expands |bufs| as necessary to store + * frame. + * + * The caller must make sure that nghttp2_bufs_reset(bufs) is called + * before calling this function. + * + * frame->hd.length is assigned after length is determined during + * packing process. CONTINUATION frames are also serialized in this + * function. This function does not handle padding. + * + * This function returns 0 if it succeeds, or returns one of the + * following negative error codes: + * + * NGHTTP2_ERR_HEADER_COMP + * The deflate operation failed. + * NGHTTP2_ERR_NOMEM + * Out of memory. + */ +int nghttp2_frame_pack_push_promise(nghttp2_bufs *bufs, + nghttp2_push_promise *frame, + nghttp2_hd_deflater *deflater); + +/* + * Unpacks PUSH_PROMISE frame byte sequence into |frame|. This + * function only unapcks bytes that come before name/value header + * block and after possible Pad Length field. + * + * This function returns 0 if it succeeds or one of the following + * negative error codes: + * + * NGHTTP2_ERR_PROTO + * TODO END_HEADERS flag is not set + */ +int nghttp2_frame_unpack_push_promise_payload(nghttp2_push_promise *frame, + const uint8_t *payload, + size_t payloadlen); + +/* + * Packs PING frame |frame| in wire format and store it in + * |bufs|. + * + * The caller must make sure that nghttp2_bufs_reset(bufs) is called + * before calling this function. + * + * This function always succeeds and returns 0. + */ +int nghttp2_frame_pack_ping(nghttp2_bufs *bufs, nghttp2_ping *frame); + +/* + * Unpacks PING wire format into |frame|. + */ +void nghttp2_frame_unpack_ping_payload(nghttp2_ping *frame, + const uint8_t *payload, + size_t payloadlen); + +/* + * Packs GOAWAY frame |frame| in wire format and store it in |bufs|. + * This function expands |bufs| as necessary to store frame. + * + * The caller must make sure that nghttp2_bufs_reset(bufs) is called + * before calling this function. + * + * This function returns 0 if it succeeds or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + * NGHTTP2_ERR_FRAME_SIZE_ERROR + * The length of the frame is too large. + */ +int nghttp2_frame_pack_goaway(nghttp2_bufs *bufs, nghttp2_goaway *frame); + +/* + * Unpacks GOAWAY wire format into |frame|. The |payload| of length + * |payloadlen| contains first 8 bytes of payload. The + * |var_gift_payload| of length |var_gift_payloadlen| contains + * remaining payload and its buffer is gifted to the function and then + * |frame|. The |var_gift_payloadlen| must be freed by + * nghttp2_frame_goaway_free(). + */ +void nghttp2_frame_unpack_goaway_payload(nghttp2_goaway *frame, + const uint8_t *payload, + size_t payloadlen, + uint8_t *var_gift_payload, + size_t var_gift_payloadlen); + +/* + * Unpacks GOAWAY wire format into |frame|. This function only exists + * for unit test. After allocating buffer for debug data, this + * function internally calls nghttp2_frame_unpack_goaway_payload(). + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + */ +int nghttp2_frame_unpack_goaway_payload2(nghttp2_goaway *frame, + const uint8_t *payload, + size_t payloadlen, nghttp2_mem *mem); + +/* + * Packs WINDOW_UPDATE frame |frame| in wire frame format and store it + * in |bufs|. + * + * The caller must make sure that nghttp2_bufs_reset(bufs) is called + * before calling this function. + * + * This function always succeeds and returns 0. + */ +int nghttp2_frame_pack_window_update(nghttp2_bufs *bufs, + nghttp2_window_update *frame); + +/* + * Unpacks WINDOW_UPDATE frame byte sequence into |frame|. + */ +void nghttp2_frame_unpack_window_update_payload(nghttp2_window_update *frame, + const uint8_t *payload, + size_t payloadlen); + +/* + * Initializes HEADERS frame |frame| with given values. |frame| takes + * ownership of |nva|, so caller must not free it. If |stream_id| is + * not assigned yet, it must be -1. + */ +void nghttp2_frame_headers_init(nghttp2_headers *frame, uint8_t flags, + int32_t stream_id, nghttp2_headers_category cat, + const nghttp2_priority_spec *pri_spec, + nghttp2_nv *nva, size_t nvlen); + +void nghttp2_frame_headers_free(nghttp2_headers *frame, nghttp2_mem *mem); + +void nghttp2_frame_priority_init(nghttp2_priority *frame, int32_t stream_id, + const nghttp2_priority_spec *pri_spec); + +void nghttp2_frame_priority_free(nghttp2_priority *frame); + +void nghttp2_frame_rst_stream_init(nghttp2_rst_stream *frame, int32_t stream_id, + uint32_t error_code); + +void nghttp2_frame_rst_stream_free(nghttp2_rst_stream *frame); + +/* + * Initializes PUSH_PROMISE frame |frame| with given values. |frame| + * takes ownership of |nva|, so caller must not free it. + */ +void nghttp2_frame_push_promise_init(nghttp2_push_promise *frame, uint8_t flags, + int32_t stream_id, + int32_t promised_stream_id, + nghttp2_nv *nva, size_t nvlen); + +void nghttp2_frame_push_promise_free(nghttp2_push_promise *frame, + nghttp2_mem *mem); + +/* + * Initializes SETTINGS frame |frame| with given values. |frame| takes + * ownership of |iv|, so caller must not free it. The |flags| are + * bitwise-OR of one or more of nghttp2_settings_flag. + */ +void nghttp2_frame_settings_init(nghttp2_settings *frame, uint8_t flags, + nghttp2_settings_entry *iv, size_t niv); + +void nghttp2_frame_settings_free(nghttp2_settings *frame, nghttp2_mem *mem); + +/* + * Initializes PING frame |frame| with given values. If the + * |opqeue_data| is not NULL, it must point to 8 bytes memory region + * of data. The data pointed by |opaque_data| is copied. It can be + * NULL. In this case, 8 bytes NULL is used. + */ +void nghttp2_frame_ping_init(nghttp2_ping *frame, uint8_t flags, + const uint8_t *opque_data); + +void nghttp2_frame_ping_free(nghttp2_ping *frame); + +/* + * Initializes GOAWAY frame |frame| with given values. On success, + * this function takes ownership of |opaque_data|, so caller must not + * free it. If the |opaque_data_len| is 0, opaque_data could be NULL. + */ +void nghttp2_frame_goaway_init(nghttp2_goaway *frame, int32_t last_stream_id, + uint32_t error_code, uint8_t *opaque_data, + size_t opaque_data_len); + +void nghttp2_frame_goaway_free(nghttp2_goaway *frame, nghttp2_mem *mem); + +void nghttp2_frame_window_update_init(nghttp2_window_update *frame, + uint8_t flags, int32_t stream_id, + int32_t window_size_increment); + +void nghttp2_frame_window_update_free(nghttp2_window_update *frame); + +/* + * Returns the number of padding bytes after payload. The total + * padding length is given in the |padlen|. The returned value does + * not include the Pad Length field. + */ +size_t nghttp2_frame_trail_padlen(nghttp2_frame *frame, size_t padlen); + +void nghttp2_frame_data_init(nghttp2_data *frame, uint8_t flags, + int32_t stream_id); + +void nghttp2_frame_data_free(nghttp2_data *frame); + +/* + * Makes copy of |iv| and return the copy. The |niv| is the number of + * entries in |iv|. This function returns the pointer to the copy if + * it succeeds, or NULL. + */ +nghttp2_settings_entry *nghttp2_frame_iv_copy(const nghttp2_settings_entry *iv, + size_t niv, nghttp2_mem *mem); + +/* + * Sorts the |nva| in ascending order of name and value. If names are + * equivalent, sort them by value. + */ +void nghttp2_nv_array_sort(nghttp2_nv *nva, size_t nvlen); + +/* + * Copies name/value pairs from |nva|, which contains |nvlen| pairs, + * to |*nva_ptr|, which is dynamically allocated so that all items can + * be stored. The resultant name and value in nghttp2_nv are + * guaranteed to be NULL-terminated even if the input is not + * null-terminated. + * + * The |*nva_ptr| must be freed using nghttp2_nv_array_del(). + * + * This function returns 0 if it succeeds or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + */ +int nghttp2_nv_array_copy(nghttp2_nv **nva_ptr, const nghttp2_nv *nva, + size_t nvlen, nghttp2_mem *mem); + +/* + * Returns nonzero if the name/value pair |a| equals to |b|. The name + * is compared in case-sensitive, because we ensure that this function + * is called after the name is lower-cased. + */ +int nghttp2_nv_equal(const nghttp2_nv *a, const nghttp2_nv *b); + +/* + * Frees |nva|. + */ +void nghttp2_nv_array_del(nghttp2_nv *nva, nghttp2_mem *mem); + +/* + * Checks that the |iv|, which includes |niv| entries, does not have + * invalid values. + * + * This function returns nonzero if it succeeds, or 0. + */ +int nghttp2_iv_check(const nghttp2_settings_entry *iv, size_t niv); + +/* + * Sets Pad Length field and flags and adjusts frame header position + * of each buffers in |bufs|. The number of padding is given in the + * |padlen| including Pad Length field. The |hd| is the frame header + * for the serialized data. This function fills zeros padding region + * unless framehd_only is nonzero. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + * NGHTTP2_ERR_FRAME_SIZE_ERROR + * The length of the resulting frame is too large. + */ +int nghttp2_frame_add_pad(nghttp2_bufs *bufs, nghttp2_frame_hd *hd, + size_t padlen, int framehd_only); + +#endif /* NGHTTP2_FRAME_H */ diff --git a/lib/nghttp2_hd.c b/lib/nghttp2_hd.c new file mode 100644 index 0000000..f9444e0 --- /dev/null +++ b/lib/nghttp2_hd.c @@ -0,0 +1,2343 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2013 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "nghttp2_hd.h" + +#include +#include +#include + +#include "nghttp2_helper.h" +#include "nghttp2_int.h" + +/* Make scalar initialization form of nghttp2_hd_entry */ +#define MAKE_STATIC_ENT(N, V, T) \ + { \ + { (uint8_t *)(N), (uint8_t *)(V), sizeof((N)) - 1, sizeof((V)) - 1, 0 } \ + , (T), 1, NGHTTP2_HD_FLAG_NONE \ + } + +/* Generated by mkstatictbl.py */ +/* 3rd parameter is nghttp2_token value for header field name. We use + first enum value if same header names are repeated (e.g., + :status). */ +static nghttp2_hd_entry static_table[] = { + MAKE_STATIC_ENT(":authority", "", 0), + MAKE_STATIC_ENT(":method", "GET", 1), + MAKE_STATIC_ENT(":method", "POST", 1), + MAKE_STATIC_ENT(":path", "/", 3), + MAKE_STATIC_ENT(":path", "/index.html", 3), + MAKE_STATIC_ENT(":scheme", "http", 5), + MAKE_STATIC_ENT(":scheme", "https", 5), + MAKE_STATIC_ENT(":status", "200", 7), + MAKE_STATIC_ENT(":status", "204", 7), + MAKE_STATIC_ENT(":status", "206", 7), + MAKE_STATIC_ENT(":status", "304", 7), + MAKE_STATIC_ENT(":status", "400", 7), + MAKE_STATIC_ENT(":status", "404", 7), + MAKE_STATIC_ENT(":status", "500", 7), + MAKE_STATIC_ENT("accept-charset", "", 14), + MAKE_STATIC_ENT("accept-encoding", "gzip, deflate", 15), + MAKE_STATIC_ENT("accept-language", "", 16), + MAKE_STATIC_ENT("accept-ranges", "", 17), + MAKE_STATIC_ENT("accept", "", 18), + MAKE_STATIC_ENT("access-control-allow-origin", "", 19), + MAKE_STATIC_ENT("age", "", 20), + MAKE_STATIC_ENT("allow", "", 21), + MAKE_STATIC_ENT("authorization", "", 22), + MAKE_STATIC_ENT("cache-control", "", 23), + MAKE_STATIC_ENT("content-disposition", "", 24), + MAKE_STATIC_ENT("content-encoding", "", 25), + MAKE_STATIC_ENT("content-language", "", 26), + MAKE_STATIC_ENT("content-length", "", 27), + MAKE_STATIC_ENT("content-location", "", 28), + MAKE_STATIC_ENT("content-range", "", 29), + MAKE_STATIC_ENT("content-type", "", 30), + MAKE_STATIC_ENT("cookie", "", 31), + MAKE_STATIC_ENT("date", "", 32), + MAKE_STATIC_ENT("etag", "", 33), + MAKE_STATIC_ENT("expect", "", 34), + MAKE_STATIC_ENT("expires", "", 35), + MAKE_STATIC_ENT("from", "", 36), + MAKE_STATIC_ENT("host", "", 37), + MAKE_STATIC_ENT("if-match", "", 38), + MAKE_STATIC_ENT("if-modified-since", "", 39), + MAKE_STATIC_ENT("if-none-match", "", 40), + MAKE_STATIC_ENT("if-range", "", 41), + MAKE_STATIC_ENT("if-unmodified-since", "", 42), + MAKE_STATIC_ENT("last-modified", "", 43), + MAKE_STATIC_ENT("link", "", 44), + MAKE_STATIC_ENT("location", "", 45), + MAKE_STATIC_ENT("max-forwards", "", 46), + MAKE_STATIC_ENT("proxy-authenticate", "", 47), + MAKE_STATIC_ENT("proxy-authorization", "", 48), + MAKE_STATIC_ENT("range", "", 49), + MAKE_STATIC_ENT("referer", "", 50), + MAKE_STATIC_ENT("refresh", "", 51), + MAKE_STATIC_ENT("retry-after", "", 52), + MAKE_STATIC_ENT("server", "", 53), + MAKE_STATIC_ENT("set-cookie", "", 54), + MAKE_STATIC_ENT("strict-transport-security", "", 55), + MAKE_STATIC_ENT("transfer-encoding", "", 56), + MAKE_STATIC_ENT("user-agent", "", 57), + MAKE_STATIC_ENT("vary", "", 58), + MAKE_STATIC_ENT("via", "", 59), + MAKE_STATIC_ENT("www-authenticate", "", 60), +}; + +static int memeq(const void *s1, const void *s2, size_t n) { + return memcmp(s1, s2, n) == 0; +} + +/* + * This function was generated by genlibtokenlookup.py. Inspired by + * h2o header lookup. https://github.com/h2o/h2o + */ +static int lookup_token(const uint8_t *name, size_t namelen) { + switch (namelen) { + case 2: + switch (name[1]) { + case 'e': + if (lstreq("t", name, 1)) { + return NGHTTP2_TOKEN_TE; + } + break; + } + break; + case 3: + switch (name[2]) { + case 'a': + if (lstreq("vi", name, 2)) { + return NGHTTP2_TOKEN_VIA; + } + break; + case 'e': + if (lstreq("ag", name, 2)) { + return NGHTTP2_TOKEN_AGE; + } + break; + } + break; + case 4: + switch (name[3]) { + case 'e': + if (lstreq("dat", name, 3)) { + return NGHTTP2_TOKEN_DATE; + } + break; + case 'g': + if (lstreq("eta", name, 3)) { + return NGHTTP2_TOKEN_ETAG; + } + break; + case 'k': + if (lstreq("lin", name, 3)) { + return NGHTTP2_TOKEN_LINK; + } + break; + case 'm': + if (lstreq("fro", name, 3)) { + return NGHTTP2_TOKEN_FROM; + } + break; + case 't': + if (lstreq("hos", name, 3)) { + return NGHTTP2_TOKEN_HOST; + } + break; + case 'y': + if (lstreq("var", name, 3)) { + return NGHTTP2_TOKEN_VARY; + } + break; + } + break; + case 5: + switch (name[4]) { + case 'e': + if (lstreq("rang", name, 4)) { + return NGHTTP2_TOKEN_RANGE; + } + break; + case 'h': + if (lstreq(":pat", name, 4)) { + return NGHTTP2_TOKEN__PATH; + } + if (lstreq(":pat", name, 4)) { + return NGHTTP2_TOKEN__PATH; + } + break; + case 'w': + if (lstreq("allo", name, 4)) { + return NGHTTP2_TOKEN_ALLOW; + } + break; + } + break; + case 6: + switch (name[5]) { + case 'e': + if (lstreq("cooki", name, 5)) { + return NGHTTP2_TOKEN_COOKIE; + } + break; + case 'r': + if (lstreq("serve", name, 5)) { + return NGHTTP2_TOKEN_SERVER; + } + break; + case 't': + if (lstreq("accep", name, 5)) { + return NGHTTP2_TOKEN_ACCEPT; + } + if (lstreq("expec", name, 5)) { + return NGHTTP2_TOKEN_EXPECT; + } + break; + } + break; + case 7: + switch (name[6]) { + case 'd': + if (lstreq(":metho", name, 6)) { + return NGHTTP2_TOKEN__METHOD; + } + if (lstreq(":metho", name, 6)) { + return NGHTTP2_TOKEN__METHOD; + } + break; + case 'e': + if (lstreq(":schem", name, 6)) { + return NGHTTP2_TOKEN__SCHEME; + } + if (lstreq(":schem", name, 6)) { + return NGHTTP2_TOKEN__SCHEME; + } + if (lstreq("upgrad", name, 6)) { + return NGHTTP2_TOKEN_UPGRADE; + } + break; + case 'h': + if (lstreq("refres", name, 6)) { + return NGHTTP2_TOKEN_REFRESH; + } + break; + case 'r': + if (lstreq("refere", name, 6)) { + return NGHTTP2_TOKEN_REFERER; + } + break; + case 's': + if (lstreq(":statu", name, 6)) { + return NGHTTP2_TOKEN__STATUS; + } + if (lstreq(":statu", name, 6)) { + return NGHTTP2_TOKEN__STATUS; + } + if (lstreq(":statu", name, 6)) { + return NGHTTP2_TOKEN__STATUS; + } + if (lstreq(":statu", name, 6)) { + return NGHTTP2_TOKEN__STATUS; + } + if (lstreq(":statu", name, 6)) { + return NGHTTP2_TOKEN__STATUS; + } + if (lstreq(":statu", name, 6)) { + return NGHTTP2_TOKEN__STATUS; + } + if (lstreq(":statu", name, 6)) { + return NGHTTP2_TOKEN__STATUS; + } + if (lstreq("expire", name, 6)) { + return NGHTTP2_TOKEN_EXPIRES; + } + break; + } + break; + case 8: + switch (name[7]) { + case 'e': + if (lstreq("if-rang", name, 7)) { + return NGHTTP2_TOKEN_IF_RANGE; + } + break; + case 'h': + if (lstreq("if-matc", name, 7)) { + return NGHTTP2_TOKEN_IF_MATCH; + } + break; + case 'n': + if (lstreq("locatio", name, 7)) { + return NGHTTP2_TOKEN_LOCATION; + } + break; + } + break; + case 10: + switch (name[9]) { + case 'e': + if (lstreq("keep-aliv", name, 9)) { + return NGHTTP2_TOKEN_KEEP_ALIVE; + } + if (lstreq("set-cooki", name, 9)) { + return NGHTTP2_TOKEN_SET_COOKIE; + } + break; + case 'n': + if (lstreq("connectio", name, 9)) { + return NGHTTP2_TOKEN_CONNECTION; + } + break; + case 't': + if (lstreq("user-agen", name, 9)) { + return NGHTTP2_TOKEN_USER_AGENT; + } + break; + case 'y': + if (lstreq(":authorit", name, 9)) { + return NGHTTP2_TOKEN__AUTHORITY; + } + break; + } + break; + case 11: + switch (name[10]) { + case 'r': + if (lstreq("retry-afte", name, 10)) { + return NGHTTP2_TOKEN_RETRY_AFTER; + } + break; + } + break; + case 12: + switch (name[11]) { + case 'e': + if (lstreq("content-typ", name, 11)) { + return NGHTTP2_TOKEN_CONTENT_TYPE; + } + break; + case 's': + if (lstreq("max-forward", name, 11)) { + return NGHTTP2_TOKEN_MAX_FORWARDS; + } + break; + } + break; + case 13: + switch (name[12]) { + case 'd': + if (lstreq("last-modifie", name, 12)) { + return NGHTTP2_TOKEN_LAST_MODIFIED; + } + break; + case 'e': + if (lstreq("content-rang", name, 12)) { + return NGHTTP2_TOKEN_CONTENT_RANGE; + } + break; + case 'h': + if (lstreq("if-none-matc", name, 12)) { + return NGHTTP2_TOKEN_IF_NONE_MATCH; + } + break; + case 'l': + if (lstreq("cache-contro", name, 12)) { + return NGHTTP2_TOKEN_CACHE_CONTROL; + } + break; + case 'n': + if (lstreq("authorizatio", name, 12)) { + return NGHTTP2_TOKEN_AUTHORIZATION; + } + break; + case 's': + if (lstreq("accept-range", name, 12)) { + return NGHTTP2_TOKEN_ACCEPT_RANGES; + } + break; + } + break; + case 14: + switch (name[13]) { + case 'h': + if (lstreq("content-lengt", name, 13)) { + return NGHTTP2_TOKEN_CONTENT_LENGTH; + } + break; + case 't': + if (lstreq("accept-charse", name, 13)) { + return NGHTTP2_TOKEN_ACCEPT_CHARSET; + } + break; + } + break; + case 15: + switch (name[14]) { + case 'e': + if (lstreq("accept-languag", name, 14)) { + return NGHTTP2_TOKEN_ACCEPT_LANGUAGE; + } + break; + case 'g': + if (lstreq("accept-encodin", name, 14)) { + return NGHTTP2_TOKEN_ACCEPT_ENCODING; + } + break; + } + break; + case 16: + switch (name[15]) { + case 'e': + if (lstreq("content-languag", name, 15)) { + return NGHTTP2_TOKEN_CONTENT_LANGUAGE; + } + if (lstreq("www-authenticat", name, 15)) { + return NGHTTP2_TOKEN_WWW_AUTHENTICATE; + } + break; + case 'g': + if (lstreq("content-encodin", name, 15)) { + return NGHTTP2_TOKEN_CONTENT_ENCODING; + } + break; + case 'n': + if (lstreq("content-locatio", name, 15)) { + return NGHTTP2_TOKEN_CONTENT_LOCATION; + } + if (lstreq("proxy-connectio", name, 15)) { + return NGHTTP2_TOKEN_PROXY_CONNECTION; + } + break; + } + break; + case 17: + switch (name[16]) { + case 'e': + if (lstreq("if-modified-sinc", name, 16)) { + return NGHTTP2_TOKEN_IF_MODIFIED_SINCE; + } + break; + case 'g': + if (lstreq("transfer-encodin", name, 16)) { + return NGHTTP2_TOKEN_TRANSFER_ENCODING; + } + break; + } + break; + case 18: + switch (name[17]) { + case 'e': + if (lstreq("proxy-authenticat", name, 17)) { + return NGHTTP2_TOKEN_PROXY_AUTHENTICATE; + } + break; + } + break; + case 19: + switch (name[18]) { + case 'e': + if (lstreq("if-unmodified-sinc", name, 18)) { + return NGHTTP2_TOKEN_IF_UNMODIFIED_SINCE; + } + break; + case 'n': + if (lstreq("content-dispositio", name, 18)) { + return NGHTTP2_TOKEN_CONTENT_DISPOSITION; + } + if (lstreq("proxy-authorizatio", name, 18)) { + return NGHTTP2_TOKEN_PROXY_AUTHORIZATION; + } + break; + } + break; + case 25: + switch (name[24]) { + case 'y': + if (lstreq("strict-transport-securit", name, 24)) { + return NGHTTP2_TOKEN_STRICT_TRANSPORT_SECURITY; + } + break; + } + break; + case 27: + switch (name[26]) { + case 'n': + if (lstreq("access-control-allow-origi", name, 26)) { + return NGHTTP2_TOKEN_ACCESS_CONTROL_ALLOW_ORIGIN; + } + break; + } + break; + } + return -1; +} + +int nghttp2_hd_entry_init(nghttp2_hd_entry *ent, uint8_t flags, uint8_t *name, + size_t namelen, uint8_t *value, size_t valuelen, + int token, nghttp2_mem *mem) { + int rv = 0; + + /* Since nghttp2_hd_entry is used for indexing, ent->nv.flags always + NGHTTP2_NV_FLAG_NONE */ + ent->nv.flags = NGHTTP2_NV_FLAG_NONE; + + if ((flags & NGHTTP2_HD_FLAG_NAME_ALLOC) && + (flags & NGHTTP2_HD_FLAG_NAME_GIFT) == 0) { + if (namelen == 0) { + flags &= ~NGHTTP2_HD_FLAG_NAME_ALLOC; + ent->nv.name = (uint8_t *)""; + } else { + /* copy including terminating NULL byte */ + ent->nv.name = nghttp2_memdup(name, namelen + 1, mem); + if (ent->nv.name == NULL) { + rv = NGHTTP2_ERR_NOMEM; + goto fail; + } + } + } else { + ent->nv.name = name; + } + if ((flags & NGHTTP2_HD_FLAG_VALUE_ALLOC) && + (flags & NGHTTP2_HD_FLAG_VALUE_GIFT) == 0) { + if (valuelen == 0) { + flags &= ~NGHTTP2_HD_FLAG_VALUE_ALLOC; + ent->nv.value = (uint8_t *)""; + } else { + /* copy including terminating NULL byte */ + ent->nv.value = nghttp2_memdup(value, valuelen + 1, mem); + if (ent->nv.value == NULL) { + rv = NGHTTP2_ERR_NOMEM; + goto fail2; + } + } + } else { + ent->nv.value = value; + } + ent->nv.namelen = namelen; + ent->nv.valuelen = valuelen; + ent->token = token; + ent->ref = 1; + ent->flags = flags; + + return 0; + +fail2: + if (flags & NGHTTP2_HD_FLAG_NAME_ALLOC) { + nghttp2_mem_free(mem, ent->nv.name); + } +fail: + return rv; +} + +void nghttp2_hd_entry_free(nghttp2_hd_entry *ent, nghttp2_mem *mem) { + assert(ent->ref == 0); + if (ent->flags & NGHTTP2_HD_FLAG_NAME_ALLOC) { + nghttp2_mem_free(mem, ent->nv.name); + } + if (ent->flags & NGHTTP2_HD_FLAG_VALUE_ALLOC) { + nghttp2_mem_free(mem, ent->nv.value); + } +} + +static int hd_ringbuf_init(nghttp2_hd_ringbuf *ringbuf, size_t bufsize, + nghttp2_mem *mem) { + size_t size; + for (size = 1; size < bufsize; size <<= 1) + ; + ringbuf->buffer = nghttp2_mem_malloc(mem, sizeof(nghttp2_hd_entry *) * size); + if (ringbuf->buffer == NULL) { + return NGHTTP2_ERR_NOMEM; + } + ringbuf->mask = size - 1; + ringbuf->first = 0; + ringbuf->len = 0; + return 0; +} + +static nghttp2_hd_entry *hd_ringbuf_get(nghttp2_hd_ringbuf *ringbuf, + size_t idx) { + assert(idx < ringbuf->len); + return ringbuf->buffer[(ringbuf->first + idx) & ringbuf->mask]; +} + +static int hd_ringbuf_reserve(nghttp2_hd_ringbuf *ringbuf, size_t bufsize, + nghttp2_mem *mem) { + size_t i; + size_t size; + nghttp2_hd_entry **buffer; + + if (ringbuf->mask + 1 >= bufsize) { + return 0; + } + for (size = 1; size < bufsize; size <<= 1) + ; + buffer = nghttp2_mem_malloc(mem, sizeof(nghttp2_hd_entry *) * size); + if (buffer == NULL) { + return NGHTTP2_ERR_NOMEM; + } + for (i = 0; i < ringbuf->len; ++i) { + buffer[i] = hd_ringbuf_get(ringbuf, i); + } + nghttp2_mem_free(mem, ringbuf->buffer); + ringbuf->buffer = buffer; + ringbuf->mask = size - 1; + ringbuf->first = 0; + return 0; +} + +static void hd_ringbuf_free(nghttp2_hd_ringbuf *ringbuf, nghttp2_mem *mem) { + size_t i; + if (ringbuf == NULL) { + return; + } + for (i = 0; i < ringbuf->len; ++i) { + nghttp2_hd_entry *ent = hd_ringbuf_get(ringbuf, i); + --ent->ref; + nghttp2_hd_entry_free(ent, mem); + nghttp2_mem_free(mem, ent); + } + nghttp2_mem_free(mem, ringbuf->buffer); +} + +static int hd_ringbuf_push_front(nghttp2_hd_ringbuf *ringbuf, + nghttp2_hd_entry *ent, nghttp2_mem *mem) { + int rv; + + rv = hd_ringbuf_reserve(ringbuf, ringbuf->len + 1, mem); + + if (rv != 0) { + return rv; + } + + ringbuf->buffer[--ringbuf->first & ringbuf->mask] = ent; + ++ringbuf->len; + + return 0; +} + +static void hd_ringbuf_pop_back(nghttp2_hd_ringbuf *ringbuf) { + assert(ringbuf->len > 0); + --ringbuf->len; +} + +static int hd_context_init(nghttp2_hd_context *context, nghttp2_mem *mem) { + int rv; + context->mem = mem; + context->bad = 0; + context->hd_table_bufsize_max = NGHTTP2_HD_DEFAULT_MAX_BUFFER_SIZE; + rv = hd_ringbuf_init(&context->hd_table, context->hd_table_bufsize_max / + NGHTTP2_HD_ENTRY_OVERHEAD, + mem); + if (rv != 0) { + return rv; + } + + context->hd_table_bufsize = 0; + return 0; +} + +static void hd_context_free(nghttp2_hd_context *context) { + hd_ringbuf_free(&context->hd_table, context->mem); +} + +int nghttp2_hd_deflate_init(nghttp2_hd_deflater *deflater, nghttp2_mem *mem) { + return nghttp2_hd_deflate_init2( + deflater, NGHTTP2_HD_DEFAULT_MAX_DEFLATE_BUFFER_SIZE, mem); +} + +int nghttp2_hd_deflate_init2(nghttp2_hd_deflater *deflater, + size_t deflate_hd_table_bufsize_max, + nghttp2_mem *mem) { + int rv; + rv = hd_context_init(&deflater->ctx, mem); + if (rv != 0) { + return rv; + } + + if (deflate_hd_table_bufsize_max < NGHTTP2_HD_DEFAULT_MAX_BUFFER_SIZE) { + deflater->notify_table_size_change = 1; + deflater->ctx.hd_table_bufsize_max = deflate_hd_table_bufsize_max; + } else { + deflater->notify_table_size_change = 0; + } + + deflater->deflate_hd_table_bufsize_max = deflate_hd_table_bufsize_max; + deflater->min_hd_table_bufsize_max = UINT32_MAX; + + return 0; +} + +int nghttp2_hd_inflate_init(nghttp2_hd_inflater *inflater, nghttp2_mem *mem) { + int rv; + + rv = hd_context_init(&inflater->ctx, mem); + if (rv != 0) { + goto fail; + } + + inflater->settings_hd_table_bufsize_max = NGHTTP2_HD_DEFAULT_MAX_BUFFER_SIZE; + + inflater->ent_keep = NULL; + inflater->nv_keep = NULL; + + inflater->opcode = NGHTTP2_HD_OPCODE_NONE; + inflater->state = NGHTTP2_HD_STATE_OPCODE; + + rv = nghttp2_bufs_init3(&inflater->nvbufs, NGHTTP2_HD_MAX_NV / 8, 8, 1, 0, + mem); + + if (rv != 0) { + goto nvbufs_fail; + } + + inflater->huffman_encoded = 0; + inflater->index = 0; + inflater->left = 0; + inflater->shift = 0; + inflater->newnamelen = 0; + inflater->index_required = 0; + inflater->no_index = 0; + + return 0; + +nvbufs_fail: + hd_context_free(&inflater->ctx); +fail: + return rv; +} + +static void hd_inflate_keep_free(nghttp2_hd_inflater *inflater) { + nghttp2_mem *mem; + + mem = inflater->ctx.mem; + if (inflater->ent_keep) { + if (inflater->ent_keep->ref == 0) { + nghttp2_hd_entry_free(inflater->ent_keep, mem); + nghttp2_mem_free(mem, inflater->ent_keep); + } + inflater->ent_keep = NULL; + } + + nghttp2_mem_free(mem, inflater->nv_keep); + inflater->nv_keep = NULL; +} + +void nghttp2_hd_deflate_free(nghttp2_hd_deflater *deflater) { + hd_context_free(&deflater->ctx); +} + +void nghttp2_hd_inflate_free(nghttp2_hd_inflater *inflater) { + hd_inflate_keep_free(inflater); + nghttp2_bufs_free(&inflater->nvbufs); + hd_context_free(&inflater->ctx); +} + +static size_t entry_room(size_t namelen, size_t valuelen) { + return NGHTTP2_HD_ENTRY_OVERHEAD + namelen + valuelen; +} + +static int emit_indexed_header(nghttp2_nv *nv_out, int *token_out, + nghttp2_hd_entry *ent) { + DEBUGF(fprintf(stderr, "inflatehd: header emission: %s: %s\n", ent->nv.name, + ent->nv.value)); + /* ent->ref may be 0. This happens if the encoder emits literal + block larger than header table capacity with indexing. */ + *nv_out = ent->nv; + *token_out = ent->token; + return 0; +} + +static int emit_literal_header(nghttp2_nv *nv_out, int *token_out, + nghttp2_nv *nv) { + DEBUGF(fprintf(stderr, "inflatehd: header emission: %s: %s\n", nv->name, + nv->value)); + *nv_out = *nv; + *token_out = lookup_token(nv->name, nv->namelen); + return 0; +} + +static size_t count_encoded_length(size_t n, size_t prefix) { + size_t k = (1 << prefix) - 1; + size_t len = 0; + + if (n < k) { + return 1; + } + + n -= k; + ++len; + + for (; n >= 128; n >>= 7, ++len) + ; + + return len + 1; +} + +static size_t encode_length(uint8_t *buf, size_t n, size_t prefix) { + size_t k = (1 << prefix) - 1; + uint8_t *begin = buf; + + *buf &= ~k; + + if (n < k) { + *buf |= n; + return 1; + } + + *buf++ |= k; + n -= k; + + for (; n >= 128; n >>= 7) { + *buf++ = (1 << 7) | (n & 0x7f); + } + + *buf++ = (uint8_t)n; + + return (size_t)(buf - begin); +} + +/* + * Decodes |prefix| prefixed integer stored from |in|. The |last| + * represents the 1 beyond the last of the valid contiguous memory + * region from |in|. The decoded integer must be less than or equal + * to UINT32_MAX. + * + * If the |initial| is nonzero, it is used as a initial value, this + * function assumes the |in| starts with intermediate data. + * + * An entire integer is decoded successfully, decoded, the |*final| is + * set to nonzero. + * + * This function stores the decoded integer in |*res| if it succeed, + * including partial decoding (in this case, number of shift to make + * in the next call will be stored in |*shift_ptr|) and returns number + * of bytes processed, or returns -1, indicating decoding error. + */ +static ssize_t decode_length(uint32_t *res, size_t *shift_ptr, int *final, + uint32_t initial, size_t shift, uint8_t *in, + uint8_t *last, size_t prefix) { + uint32_t k = (1 << prefix) - 1; + uint32_t n = initial; + uint8_t *start = in; + + *shift_ptr = 0; + *final = 0; + + if (n == 0) { + if ((*in & k) != k) { + *res = (*in) & k; + *final = 1; + return 1; + } + + n = k; + + if (++in == last) { + *res = n; + return (ssize_t)(in - start); + } + } + + for (; in != last; ++in, shift += 7) { + uint32_t add = *in & 0x7f; + + if ((UINT32_MAX >> shift) < add) { + DEBUGF(fprintf(stderr, "inflate: integer overflow on shift\n")); + return -1; + } + + add <<= shift; + + if (UINT32_MAX - add < n) { + DEBUGF(fprintf(stderr, "inflate: integer overflow on addition\n")); + return -1; + } + + n += add; + + if ((*in & (1 << 7)) == 0) { + break; + } + } + + *shift_ptr = shift; + + if (in == last) { + *res = n; + return (ssize_t)(in - start); + } + + *res = n; + *final = 1; + return (ssize_t)(in + 1 - start); +} + +static int emit_table_size(nghttp2_bufs *bufs, size_t table_size) { + int rv; + uint8_t *bufp; + size_t blocklen; + uint8_t sb[16]; + + DEBUGF(fprintf(stderr, "deflatehd: emit table_size=%zu\n", table_size)); + + blocklen = count_encoded_length(table_size, 5); + + if (sizeof(sb) < blocklen) { + return NGHTTP2_ERR_HEADER_COMP; + } + + bufp = sb; + + *bufp = 0x20u; + + encode_length(bufp, table_size, 5); + + rv = nghttp2_bufs_add(bufs, sb, blocklen); + if (rv != 0) { + return rv; + } + + return 0; +} + +static int emit_indexed_block(nghttp2_bufs *bufs, size_t idx) { + int rv; + size_t blocklen; + uint8_t sb[16]; + uint8_t *bufp; + + blocklen = count_encoded_length(idx + 1, 7); + + DEBUGF(fprintf(stderr, "deflatehd: emit indexed index=%zu, %zu bytes\n", idx, + blocklen)); + + if (sizeof(sb) < blocklen) { + return NGHTTP2_ERR_HEADER_COMP; + } + + bufp = sb; + *bufp = 0x80u; + encode_length(bufp, idx + 1, 7); + + rv = nghttp2_bufs_add(bufs, sb, blocklen); + if (rv != 0) { + return rv; + } + + return 0; +} + +static int emit_string(nghttp2_bufs *bufs, const uint8_t *str, size_t len) { + int rv; + uint8_t sb[16]; + uint8_t *bufp; + size_t blocklen; + size_t enclen; + int huffman = 0; + + enclen = nghttp2_hd_huff_encode_count(str, len); + + if (enclen < len) { + huffman = 1; + } else { + enclen = len; + } + + blocklen = count_encoded_length(enclen, 7); + + DEBUGF(fprintf(stderr, "deflatehd: emit string str=")); + DEBUGF(fwrite(str, len, 1, stderr)); + DEBUGF(fprintf(stderr, ", length=%zu, huffman=%d, encoded_length=%zu\n", len, + huffman, enclen)); + + if (sizeof(sb) < blocklen) { + return NGHTTP2_ERR_HEADER_COMP; + } + + bufp = sb; + *bufp = huffman ? 1 << 7 : 0; + encode_length(bufp, enclen, 7); + + rv = nghttp2_bufs_add(bufs, sb, blocklen); + if (rv != 0) { + return rv; + } + + if (huffman) { + rv = nghttp2_hd_huff_encode(bufs, str, len); + } else { + assert(enclen == len); + rv = nghttp2_bufs_add(bufs, str, len); + } + + return rv; +} + +static uint8_t pack_first_byte(int indexing_mode) { + switch (indexing_mode) { + case NGHTTP2_HD_WITH_INDEXING: + return 0x40u; + case NGHTTP2_HD_WITHOUT_INDEXING: + return 0; + case NGHTTP2_HD_NEVER_INDEXING: + return 0x10u; + default: + assert(0); + } + /* This is required to compile with android NDK r10d + + --enable-werror */ + return 0; +} + +static int emit_indname_block(nghttp2_bufs *bufs, size_t idx, + const nghttp2_nv *nv, int indexing_mode) { + int rv; + uint8_t *bufp; + size_t blocklen; + uint8_t sb[16]; + size_t prefixlen; + + if (indexing_mode == NGHTTP2_HD_WITH_INDEXING) { + prefixlen = 6; + } else { + prefixlen = 4; + } + + DEBUGF(fprintf(stderr, "deflatehd: emit indname index=%zu, valuelen=%zu, " + "indexing_mode=%d\n", + idx, nv->valuelen, indexing_mode)); + + blocklen = count_encoded_length(idx + 1, prefixlen); + + if (sizeof(sb) < blocklen) { + return NGHTTP2_ERR_HEADER_COMP; + } + + bufp = sb; + + *bufp = pack_first_byte(indexing_mode); + + encode_length(bufp, idx + 1, prefixlen); + + rv = nghttp2_bufs_add(bufs, sb, blocklen); + if (rv != 0) { + return rv; + } + + rv = emit_string(bufs, nv->value, nv->valuelen); + if (rv != 0) { + return rv; + } + + return 0; +} + +static int emit_newname_block(nghttp2_bufs *bufs, const nghttp2_nv *nv, + int indexing_mode) { + int rv; + + DEBUGF(fprintf(stderr, "deflatehd: emit newname namelen=%zu, valuelen=%zu, " + "indexing_mode=%d\n", + nv->namelen, nv->valuelen, indexing_mode)); + + rv = nghttp2_bufs_addb(bufs, pack_first_byte(indexing_mode)); + if (rv != 0) { + return rv; + } + + rv = emit_string(bufs, nv->name, nv->namelen); + if (rv != 0) { + return rv; + } + + rv = emit_string(bufs, nv->value, nv->valuelen); + if (rv != 0) { + return rv; + } + + return 0; +} + +static nghttp2_hd_entry *add_hd_table_incremental(nghttp2_hd_context *context, + const nghttp2_nv *nv, + int token, + uint8_t entry_flags) { + int rv; + nghttp2_hd_entry *new_ent; + size_t room; + nghttp2_mem *mem; + + mem = context->mem; + room = entry_room(nv->namelen, nv->valuelen); + + while (context->hd_table_bufsize + room > context->hd_table_bufsize_max && + context->hd_table.len > 0) { + + size_t idx = context->hd_table.len - 1; + nghttp2_hd_entry *ent = hd_ringbuf_get(&context->hd_table, idx); + + context->hd_table_bufsize -= entry_room(ent->nv.namelen, ent->nv.valuelen); + + DEBUGF(fprintf(stderr, "hpack: remove item from header table: %s: %s\n", + ent->nv.name, ent->nv.value)); + + hd_ringbuf_pop_back(&context->hd_table); + if (--ent->ref == 0) { + nghttp2_hd_entry_free(ent, mem); + nghttp2_mem_free(mem, ent); + } + } + + new_ent = nghttp2_mem_malloc(mem, sizeof(nghttp2_hd_entry)); + if (new_ent == NULL) { + return NULL; + } + + rv = nghttp2_hd_entry_init(new_ent, entry_flags, nv->name, nv->namelen, + nv->value, nv->valuelen, token, mem); + if (rv != 0) { + nghttp2_mem_free(mem, new_ent); + return NULL; + } + + if (room > context->hd_table_bufsize_max) { + /* The entry taking more than NGHTTP2_HD_MAX_BUFFER_SIZE is + immediately evicted. */ + --new_ent->ref; + } else { + rv = hd_ringbuf_push_front(&context->hd_table, new_ent, mem); + + if (rv != 0) { + --new_ent->ref; + + if ((entry_flags & NGHTTP2_HD_FLAG_NAME_ALLOC) && + (entry_flags & NGHTTP2_HD_FLAG_NAME_GIFT)) { + /* nv->name are managed by caller. */ + new_ent->nv.name = NULL; + new_ent->nv.namelen = 0; + } + if ((entry_flags & NGHTTP2_HD_FLAG_VALUE_ALLOC) && + (entry_flags & NGHTTP2_HD_FLAG_VALUE_GIFT)) { + /* nv->value are managed by caller. */ + new_ent->nv.value = NULL; + new_ent->nv.valuelen = 0; + } + + nghttp2_hd_entry_free(new_ent, mem); + nghttp2_mem_free(mem, new_ent); + + return NULL; + } + + context->hd_table_bufsize += room; + } + return new_ent; +} + +static int name_eq(const nghttp2_nv *a, const nghttp2_nv *b) { + return a->namelen == b->namelen && memeq(a->name, b->name, a->namelen); +} + +static int value_eq(const nghttp2_nv *a, const nghttp2_nv *b) { + return a->valuelen == b->valuelen && memeq(a->value, b->value, a->valuelen); +} + +typedef struct { + ssize_t index; + /* Nonzero if both name and value are matched. */ + uint8_t name_value_match; +} search_result; + +static search_result search_static_table(const nghttp2_nv *nv, int token, + int indexing_mode) { + search_result res = {token, 0}; + int i; + + if (indexing_mode == NGHTTP2_HD_NEVER_INDEXING) { + return res; + } + + for (i = token; + i <= NGHTTP2_TOKEN_WWW_AUTHENTICATE && static_table[i].token == token; + ++i) { + if (value_eq(&static_table[i].nv, nv)) { + res.index = i; + res.name_value_match = 1; + return res; + } + } + return res; +} + +static search_result search_hd_table(nghttp2_hd_context *context, + const nghttp2_nv *nv, int token, + int indexing_mode) { + search_result res = {-1, 0}; + size_t i; + + if (token >= 0 && token <= NGHTTP2_TOKEN_WWW_AUTHENTICATE) { + res = search_static_table(nv, token, indexing_mode); + if (res.name_value_match) { + return res; + } + } + + for (i = 0; i < context->hd_table.len; ++i) { + nghttp2_hd_entry *ent = hd_ringbuf_get(&context->hd_table, i); + if (ent->token != token || (token == -1 && !name_eq(&ent->nv, nv))) { + continue; + } + + if (res.index == -1) { + res.index = (ssize_t)(i + NGHTTP2_STATIC_TABLE_LENGTH); + } + + if (indexing_mode != NGHTTP2_HD_NEVER_INDEXING && value_eq(&ent->nv, nv)) { + res.index = (ssize_t)(i + NGHTTP2_STATIC_TABLE_LENGTH); + res.name_value_match = 1; + return res; + } + } + + return res; +} + +static void hd_context_shrink_table_size(nghttp2_hd_context *context) { + nghttp2_mem *mem; + + mem = context->mem; + + while (context->hd_table_bufsize > context->hd_table_bufsize_max && + context->hd_table.len > 0) { + size_t idx = context->hd_table.len - 1; + nghttp2_hd_entry *ent = hd_ringbuf_get(&context->hd_table, idx); + context->hd_table_bufsize -= entry_room(ent->nv.namelen, ent->nv.valuelen); + hd_ringbuf_pop_back(&context->hd_table); + if (--ent->ref == 0) { + nghttp2_hd_entry_free(ent, mem); + nghttp2_mem_free(mem, ent); + } + } +} + +int nghttp2_hd_deflate_change_table_size(nghttp2_hd_deflater *deflater, + size_t settings_hd_table_bufsize_max) { + size_t next_bufsize = nghttp2_min(settings_hd_table_bufsize_max, + deflater->deflate_hd_table_bufsize_max); + + deflater->ctx.hd_table_bufsize_max = next_bufsize; + + deflater->min_hd_table_bufsize_max = + nghttp2_min(deflater->min_hd_table_bufsize_max, next_bufsize); + + deflater->notify_table_size_change = 1; + + hd_context_shrink_table_size(&deflater->ctx); + return 0; +} + +int nghttp2_hd_inflate_change_table_size(nghttp2_hd_inflater *inflater, + size_t settings_hd_table_bufsize_max) { + inflater->settings_hd_table_bufsize_max = settings_hd_table_bufsize_max; + inflater->ctx.hd_table_bufsize_max = settings_hd_table_bufsize_max; + hd_context_shrink_table_size(&inflater->ctx); + return 0; +} + +#define INDEX_RANGE_VALID(context, idx) \ + ((idx) < (context)->hd_table.len + NGHTTP2_STATIC_TABLE_LENGTH) + +static size_t get_max_index(nghttp2_hd_context *context) { + return context->hd_table.len + NGHTTP2_STATIC_TABLE_LENGTH - 1; +} + +nghttp2_hd_entry *nghttp2_hd_table_get(nghttp2_hd_context *context, + size_t idx) { + assert(INDEX_RANGE_VALID(context, idx)); + if (idx >= NGHTTP2_STATIC_TABLE_LENGTH) { + return hd_ringbuf_get(&context->hd_table, + idx - NGHTTP2_STATIC_TABLE_LENGTH); + } else { + return &static_table[idx]; + } +} + +static int hd_deflate_decide_indexing(nghttp2_hd_deflater *deflater, + const nghttp2_nv *nv, int token) { + if (token == NGHTTP2_TOKEN__PATH || token == NGHTTP2_TOKEN_AGE || + token == NGHTTP2_TOKEN_CONTENT_LENGTH || token == NGHTTP2_TOKEN_ETAG || + token == NGHTTP2_TOKEN_IF_MODIFIED_SINCE || + token == NGHTTP2_TOKEN_IF_NONE_MATCH || token == NGHTTP2_TOKEN_LOCATION || + token == NGHTTP2_TOKEN_SET_COOKIE || + entry_room(nv->namelen, nv->valuelen) > + deflater->ctx.hd_table_bufsize_max * 3 / 4) { + return NGHTTP2_HD_WITHOUT_INDEXING; + } + + return NGHTTP2_HD_WITH_INDEXING; +} + +static int deflate_nv(nghttp2_hd_deflater *deflater, nghttp2_bufs *bufs, + const nghttp2_nv *nv) { + int rv; + search_result res; + ssize_t idx; + int indexing_mode; + int token; + nghttp2_mem *mem; + + DEBUGF(fprintf(stderr, "deflatehd: deflating %s: %s\n", nv->name, nv->value)); + + mem = deflater->ctx.mem; + + token = lookup_token(nv->name, nv->namelen); + + /* Don't index authorization header field since it may contain low + entropy secret data (e.g., id/password). Also cookie header + field with less than 20 bytes value is also never indexed. This + is the same criteria used in Firefox codebase. */ + indexing_mode = + token == NGHTTP2_TOKEN_AUTHORIZATION || + (token == NGHTTP2_TOKEN_COOKIE && nv->valuelen < 20) || + (nv->flags & NGHTTP2_NV_FLAG_NO_INDEX) + ? NGHTTP2_HD_NEVER_INDEXING + : hd_deflate_decide_indexing(deflater, nv, token); + + res = search_hd_table(&deflater->ctx, nv, token, indexing_mode); + + idx = res.index; + + if (res.name_value_match) { + + DEBUGF(fprintf(stderr, "deflatehd: name/value match index=%zd\n", idx)); + + rv = emit_indexed_block(bufs, idx); + if (rv != 0) { + return rv; + } + + return 0; + } + + if (res.index != -1) { + DEBUGF(fprintf(stderr, "deflatehd: name match index=%zd\n", res.index)); + } + + if (indexing_mode == NGHTTP2_HD_WITH_INDEXING) { + nghttp2_hd_entry *new_ent; + if (idx != -1 && idx < (ssize_t)NGHTTP2_STATIC_TABLE_LENGTH) { + nghttp2_nv nv_indname; + nv_indname = *nv; + nv_indname.name = nghttp2_hd_table_get(&deflater->ctx, idx)->nv.name; + new_ent = add_hd_table_incremental(&deflater->ctx, &nv_indname, token, + NGHTTP2_HD_FLAG_VALUE_ALLOC); + } else { + new_ent = add_hd_table_incremental(&deflater->ctx, nv, token, + NGHTTP2_HD_FLAG_NAME_ALLOC | + NGHTTP2_HD_FLAG_VALUE_ALLOC); + } + if (!new_ent) { + return NGHTTP2_ERR_HEADER_COMP; + } + if (new_ent->ref == 0) { + nghttp2_hd_entry_free(new_ent, mem); + nghttp2_mem_free(mem, new_ent); + } + } + if (idx == -1) { + rv = emit_newname_block(bufs, nv, indexing_mode); + } else { + rv = emit_indname_block(bufs, idx, nv, indexing_mode); + } + if (rv != 0) { + return rv; + } + + return 0; +} + +int nghttp2_hd_deflate_hd_bufs(nghttp2_hd_deflater *deflater, + nghttp2_bufs *bufs, const nghttp2_nv *nv, + size_t nvlen) { + size_t i; + int rv = 0; + + if (deflater->ctx.bad) { + return NGHTTP2_ERR_HEADER_COMP; + } + + if (deflater->notify_table_size_change) { + size_t min_hd_table_bufsize_max; + + min_hd_table_bufsize_max = deflater->min_hd_table_bufsize_max; + + deflater->notify_table_size_change = 0; + deflater->min_hd_table_bufsize_max = UINT32_MAX; + + if (deflater->ctx.hd_table_bufsize_max > min_hd_table_bufsize_max) { + + rv = emit_table_size(bufs, min_hd_table_bufsize_max); + + if (rv != 0) { + goto fail; + } + } + + rv = emit_table_size(bufs, deflater->ctx.hd_table_bufsize_max); + + if (rv != 0) { + goto fail; + } + } + + for (i = 0; i < nvlen; ++i) { + rv = deflate_nv(deflater, bufs, &nv[i]); + if (rv != 0) { + goto fail; + } + } + + DEBUGF( + fprintf(stderr, "deflatehd: all input name/value pairs were deflated\n")); + + return 0; +fail: + DEBUGF(fprintf(stderr, "deflatehd: error return %d\n", rv)); + + deflater->ctx.bad = 1; + return rv; +} + +ssize_t nghttp2_hd_deflate_hd(nghttp2_hd_deflater *deflater, uint8_t *buf, + size_t buflen, const nghttp2_nv *nv, + size_t nvlen) { + nghttp2_bufs bufs; + int rv; + nghttp2_mem *mem; + + mem = deflater->ctx.mem; + + rv = nghttp2_bufs_wrap_init(&bufs, buf, buflen, mem); + + if (rv != 0) { + return rv; + } + + rv = nghttp2_hd_deflate_hd_bufs(deflater, &bufs, nv, nvlen); + + buflen = nghttp2_bufs_len(&bufs); + + nghttp2_bufs_wrap_free(&bufs); + + if (rv == NGHTTP2_ERR_BUFFER_ERROR) { + return NGHTTP2_ERR_INSUFF_BUFSIZE; + } + + if (rv != 0) { + return rv; + } + + return (ssize_t)buflen; +} + +size_t nghttp2_hd_deflate_bound(nghttp2_hd_deflater *deflater _U_, + const nghttp2_nv *nva, size_t nvlen) { + size_t n = 0; + size_t i; + + /* Possible Maximum Header Table Size Change. Encoding (1u << 31) - + 1 using 4 bit prefix requires 6 bytes. We may emit this at most + twice. */ + n += 12; + + /* Use Literal Header Field without indexing - New Name, since it is + most space consuming format. Also we choose the less one between + non-huffman and huffman, so using literal byte count is + sufficient for upper bound. + + Encoding (1u << 31) - 1 using 7 bit prefix requires 6 bytes. We + need 2 of this for |nvlen| header fields. */ + n += 6 * 2 * nvlen; + + for (i = 0; i < nvlen; ++i) { + n += nva[i].namelen + nva[i].valuelen; + } + + return n; +} + +int nghttp2_hd_deflate_new(nghttp2_hd_deflater **deflater_ptr, + size_t deflate_hd_table_bufsize_max) { + return nghttp2_hd_deflate_new2(deflater_ptr, deflate_hd_table_bufsize_max, + NULL); +} + +int nghttp2_hd_deflate_new2(nghttp2_hd_deflater **deflater_ptr, + size_t deflate_hd_table_bufsize_max, + nghttp2_mem *mem) { + int rv; + nghttp2_hd_deflater *deflater; + + if (mem == NULL) { + mem = nghttp2_mem_default(); + } + + deflater = nghttp2_mem_malloc(mem, sizeof(nghttp2_hd_deflater)); + + if (deflater == NULL) { + return NGHTTP2_ERR_NOMEM; + } + + rv = nghttp2_hd_deflate_init2(deflater, deflate_hd_table_bufsize_max, mem); + + if (rv != 0) { + nghttp2_mem_free(mem, deflater); + + return rv; + } + + *deflater_ptr = deflater; + + return 0; +} + +void nghttp2_hd_deflate_del(nghttp2_hd_deflater *deflater) { + nghttp2_mem *mem; + + mem = deflater->ctx.mem; + + nghttp2_hd_deflate_free(deflater); + + nghttp2_mem_free(mem, deflater); +} + +static void hd_inflate_set_huffman_encoded(nghttp2_hd_inflater *inflater, + const uint8_t *in) { + inflater->huffman_encoded = (*in & (1 << 7)) != 0; +} + +/* + * Decodes the integer from the range [in, last). The result is + * assigned to |inflater->left|. If the |inflater->left| is 0, then + * it performs variable integer decoding from scratch. Otherwise, it + * uses the |inflater->left| as the initial value and continues to + * decode assuming that [in, last) begins with intermediary sequence. + * + * This function returns the number of bytes read if it succeeds, or + * one of the following negative error codes: + * + * NGHTTP2_ERR_HEADER_COMP + * Integer decoding failed + */ +static ssize_t hd_inflate_read_len(nghttp2_hd_inflater *inflater, int *rfin, + uint8_t *in, uint8_t *last, size_t prefix, + size_t maxlen) { + ssize_t rv; + uint32_t out; + + *rfin = 0; + + rv = decode_length(&out, &inflater->shift, rfin, (uint32_t)inflater->left, + inflater->shift, in, last, prefix); + + if (rv == -1) { + DEBUGF(fprintf(stderr, "inflatehd: integer decoding failed\n")); + return NGHTTP2_ERR_HEADER_COMP; + } + + if (out > maxlen) { + DEBUGF(fprintf( + stderr, "inflatehd: integer exceeded the maximum value %zu\n", maxlen)); + return NGHTTP2_ERR_HEADER_COMP; + } + + inflater->left = out; + + DEBUGF(fprintf(stderr, "inflatehd: decoded integer is %u\n", out)); + + return rv; +} + +/* + * Reads |inflater->left| bytes from the range [in, last) and performs + * huffman decoding against them and pushes the result into the + * |buffer|. + * + * This function returns the number of bytes read if it succeeds, or + * one of the following negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory + * NGHTTP2_ERR_HEADER_COMP + * Huffman decoding failed + * NGHTTP2_ERR_BUFFER_ERROR + * Out of buffer space. + */ +static ssize_t hd_inflate_read_huff(nghttp2_hd_inflater *inflater, + nghttp2_bufs *bufs, uint8_t *in, + uint8_t *last) { + ssize_t readlen; + int final = 0; + if ((size_t)(last - in) >= inflater->left) { + last = in + inflater->left; + final = 1; + } + readlen = nghttp2_hd_huff_decode(&inflater->huff_decode_ctx, bufs, in, + last - in, final); + + if (readlen < 0) { + DEBUGF(fprintf(stderr, "inflatehd: huffman decoding failed\n")); + return readlen; + } + inflater->left -= (size_t)readlen; + return readlen; +} + +/* + * Reads |inflater->left| bytes from the range [in, last) and copies + * them into the |buffer|. + * + * This function returns the number of bytes read if it succeeds, or + * one of the following negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory + * NGHTTP2_ERR_HEADER_COMP + * Header decompression failed + * NGHTTP2_ERR_BUFFER_ERROR + * Out of buffer space. + */ +static ssize_t hd_inflate_read(nghttp2_hd_inflater *inflater, + nghttp2_bufs *bufs, uint8_t *in, uint8_t *last) { + int rv; + size_t len = nghttp2_min((size_t)(last - in), inflater->left); + rv = nghttp2_bufs_add(bufs, in, len); + if (rv != 0) { + return rv; + } + inflater->left -= len; + return (ssize_t)len; +} + +/* + * Finalize indexed header representation reception. If header is + * emitted, |*nv_out| is filled with that value and 0 is returned. If + * no header is emitted, 1 is returned. + * + * This function returns either 0 or 1 if it succeeds, or one of the + * following negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory + */ +static int hd_inflate_commit_indexed(nghttp2_hd_inflater *inflater, + nghttp2_nv *nv_out, int *token_out) { + nghttp2_hd_entry *ent = nghttp2_hd_table_get(&inflater->ctx, inflater->index); + + emit_indexed_header(nv_out, token_out, ent); + + return 0; +} + +static int hd_inflate_remove_bufs(nghttp2_hd_inflater *inflater, nghttp2_nv *nv, + int value_only) { + ssize_t rv; + size_t buflen; + uint8_t *buf; + nghttp2_buf *pbuf; + + if (inflater->index_required || + inflater->nvbufs.head != inflater->nvbufs.cur) { + + rv = nghttp2_bufs_remove(&inflater->nvbufs, &buf); + + if (rv < 0) { + return NGHTTP2_ERR_NOMEM; + } + + nghttp2_bufs_reset(&inflater->nvbufs); + + buflen = rv; + + if (value_only) { + /* we don't use this value, so no need to NULL-terminate */ + nv->name = NULL; + nv->namelen = 0; + + nv->value = buf; + nv->valuelen = buflen - 1; + } else { + nv->name = buf; + nv->namelen = inflater->newnamelen; + + nv->value = buf + nv->namelen + 1; + nv->valuelen = buflen - nv->namelen - 2; + } + + return 0; + } + + /* If we are not going to store header in header table and + name/value are in first chunk, we just refer them from nv, + instead of mallocing another memory. */ + + pbuf = &inflater->nvbufs.head->buf; + + if (value_only) { + /* we don't use this value, so no need to NULL-terminate */ + nv->name = NULL; + nv->namelen = 0; + + nv->value = pbuf->pos; + nv->valuelen = nghttp2_buf_len(pbuf) - 1; + } else { + nv->name = pbuf->pos; + nv->namelen = inflater->newnamelen; + + nv->value = pbuf->pos + nv->namelen + 1; + nv->valuelen = nghttp2_buf_len(pbuf) - nv->namelen - 2; + } + + /* Resetting does not change the content of first buffer */ + nghttp2_bufs_reset(&inflater->nvbufs); + + return 0; +} + +static int hd_inflate_remove_bufs_with_name(nghttp2_hd_inflater *inflater, + nghttp2_nv *nv, + nghttp2_hd_entry *ent_name) { +#ifndef NDEBUG + size_t rv; +#endif + size_t buflen; + uint8_t *buf; + nghttp2_mem *mem; + + mem = inflater->ctx.mem; + + /* Allocate buffer including name in ent_name, plus terminating + NULL. */ + buflen = ent_name->nv.namelen + 1 + nghttp2_bufs_len(&inflater->nvbufs); + + buf = nghttp2_mem_malloc(mem, buflen); + if (buf == NULL) { + return NGHTTP2_ERR_NOMEM; + } + + /* Copy including terminal NULL */ + memcpy(buf, ent_name->nv.name, ent_name->nv.namelen + 1); +#ifndef NDEBUG + rv = +#endif + nghttp2_bufs_remove_copy(&inflater->nvbufs, + buf + ent_name->nv.namelen + 1); + assert(ent_name->nv.namelen + 1 + rv == buflen); + + nghttp2_bufs_reset(&inflater->nvbufs); + + nv->name = buf; + nv->namelen = ent_name->nv.namelen; + + nv->value = buf + nv->namelen + 1; + nv->valuelen = buflen - nv->namelen - 2; + + return 0; +} + +/* + * Finalize literal header representation - new name- reception. If + * header is emitted, |*nv_out| is filled with that value and 0 is + * returned. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory + */ +static int hd_inflate_commit_newname(nghttp2_hd_inflater *inflater, + nghttp2_nv *nv_out, int *token_out) { + int rv; + nghttp2_nv nv; + nghttp2_mem *mem; + + mem = inflater->ctx.mem; + + rv = hd_inflate_remove_bufs(inflater, &nv, 0 /* name and value */); + if (rv != 0) { + return NGHTTP2_ERR_NOMEM; + } + + if (inflater->no_index) { + nv.flags = NGHTTP2_NV_FLAG_NO_INDEX; + } else { + nv.flags = NGHTTP2_NV_FLAG_NONE; + } + + if (inflater->index_required) { + nghttp2_hd_entry *new_ent; + uint8_t ent_flags; + + /* nv->value points to the middle of the buffer pointed by + nv->name. So we just need to keep track of nv->name for memory + management. */ + ent_flags = NGHTTP2_HD_FLAG_NAME_ALLOC | NGHTTP2_HD_FLAG_NAME_GIFT; + + new_ent = add_hd_table_incremental( + &inflater->ctx, &nv, lookup_token(nv.name, nv.namelen), ent_flags); + + if (new_ent) { + emit_indexed_header(nv_out, token_out, new_ent); + inflater->ent_keep = new_ent; + + return 0; + } + + nghttp2_mem_free(mem, nv.name); + + return NGHTTP2_ERR_NOMEM; + } + + emit_literal_header(nv_out, token_out, &nv); + + if (nv.name != inflater->nvbufs.head->buf.pos) { + inflater->nv_keep = nv.name; + } + + return 0; +} + +/* + * Finalize literal header representation - indexed name- + * reception. If header is emitted, |*nv_out| is filled with that + * value and 0 is returned. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory + */ +static int hd_inflate_commit_indname(nghttp2_hd_inflater *inflater, + nghttp2_nv *nv_out, int *token_out) { + int rv; + nghttp2_nv nv; + nghttp2_hd_entry *ent_name; + nghttp2_mem *mem; + + mem = inflater->ctx.mem; + + if (inflater->no_index) { + nv.flags = NGHTTP2_NV_FLAG_NO_INDEX; + } else { + nv.flags = NGHTTP2_NV_FLAG_NONE; + } + + ent_name = nghttp2_hd_table_get(&inflater->ctx, inflater->index); + + if (inflater->index_required) { + nghttp2_hd_entry *new_ent; + uint8_t ent_flags; + + if (inflater->index < NGHTTP2_STATIC_TABLE_LENGTH) { + /* We don't copy name in static table */ + rv = hd_inflate_remove_bufs(inflater, &nv, 1 /* value only */); + if (rv != 0) { + return NGHTTP2_ERR_NOMEM; + } + nv.name = ent_name->nv.name; + nv.namelen = ent_name->nv.namelen; + + ent_flags = NGHTTP2_HD_FLAG_VALUE_ALLOC | NGHTTP2_HD_FLAG_VALUE_GIFT; + } else { + rv = hd_inflate_remove_bufs_with_name(inflater, &nv, ent_name); + if (rv != 0) { + return NGHTTP2_ERR_NOMEM; + } + /* nv->name and nv->value are in the same buffer. */ + ent_flags = NGHTTP2_HD_FLAG_NAME_ALLOC | NGHTTP2_HD_FLAG_NAME_GIFT; + } + + new_ent = add_hd_table_incremental(&inflater->ctx, &nv, ent_name->token, + ent_flags); + + /* At this point, ent_name might be deleted. */ + + if (new_ent) { + emit_indexed_header(nv_out, token_out, new_ent); + + inflater->ent_keep = new_ent; + + return 0; + } + + nghttp2_mem_free(mem, nv.value); + + return NGHTTP2_ERR_NOMEM; + } + + rv = hd_inflate_remove_bufs(inflater, &nv, 1 /* value only */); + if (rv != 0) { + return NGHTTP2_ERR_NOMEM; + } + + nv.name = ent_name->nv.name; + nv.namelen = ent_name->nv.namelen; + + emit_literal_header(nv_out, token_out, &nv); + + if (nv.value != inflater->nvbufs.head->buf.pos) { + inflater->nv_keep = nv.value; + } + + return 0; +} + +ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_inflater *inflater, nghttp2_nv *nv_out, + int *inflate_flags, uint8_t *in, size_t inlen, + int in_final) { + int token; + + return nghttp2_hd_inflate_hd2(inflater, nv_out, inflate_flags, &token, in, + inlen, in_final); +} + +ssize_t nghttp2_hd_inflate_hd2(nghttp2_hd_inflater *inflater, + nghttp2_nv *nv_out, int *inflate_flags, + int *token_out, uint8_t *in, size_t inlen, + int in_final) { + ssize_t rv = 0; + uint8_t *first = in; + uint8_t *last = in + inlen; + int rfin = 0; + int busy = 0; + + if (inflater->ctx.bad) { + return NGHTTP2_ERR_HEADER_COMP; + } + + DEBUGF(fprintf(stderr, "inflatehd: start state=%d\n", inflater->state)); + hd_inflate_keep_free(inflater); + *token_out = -1; + *inflate_flags = NGHTTP2_HD_INFLATE_NONE; + for (; in != last || busy;) { + busy = 0; + switch (inflater->state) { + case NGHTTP2_HD_STATE_OPCODE: + if ((*in & 0xe0u) == 0x20u) { + DEBUGF(fprintf(stderr, "inflatehd: header table size change\n")); + inflater->opcode = NGHTTP2_HD_OPCODE_INDEXED; + inflater->state = NGHTTP2_HD_STATE_READ_TABLE_SIZE; + } else if (*in & 0x80u) { + DEBUGF(fprintf(stderr, "inflatehd: indexed repr\n")); + inflater->opcode = NGHTTP2_HD_OPCODE_INDEXED; + inflater->state = NGHTTP2_HD_STATE_READ_INDEX; + } else { + if (*in == 0x40u || *in == 0 || *in == 0x10u) { + DEBUGF( + fprintf(stderr, "inflatehd: literal header repr - new name\n")); + inflater->opcode = NGHTTP2_HD_OPCODE_NEWNAME; + inflater->state = NGHTTP2_HD_STATE_NEWNAME_CHECK_NAMELEN; + } else { + DEBUGF(fprintf(stderr, + "inflatehd: literal header repr - indexed name\n")); + inflater->opcode = NGHTTP2_HD_OPCODE_INDNAME; + inflater->state = NGHTTP2_HD_STATE_READ_INDEX; + } + inflater->index_required = (*in & 0x40) != 0; + inflater->no_index = (*in & 0xf0u) == 0x10u; + DEBUGF(fprintf(stderr, "inflatehd: indexing required=%d, no_index=%d\n", + inflater->index_required, inflater->no_index)); + if (inflater->opcode == NGHTTP2_HD_OPCODE_NEWNAME) { + ++in; + } + } + inflater->left = 0; + inflater->shift = 0; + break; + case NGHTTP2_HD_STATE_READ_TABLE_SIZE: + rfin = 0; + rv = hd_inflate_read_len(inflater, &rfin, in, last, 5, + inflater->settings_hd_table_bufsize_max); + if (rv < 0) { + goto fail; + } + in += rv; + if (!rfin) { + goto almost_ok; + } + DEBUGF(fprintf(stderr, "inflatehd: table_size=%zu\n", inflater->left)); + inflater->ctx.hd_table_bufsize_max = inflater->left; + hd_context_shrink_table_size(&inflater->ctx); + inflater->state = NGHTTP2_HD_STATE_OPCODE; + break; + case NGHTTP2_HD_STATE_READ_INDEX: { + size_t prefixlen; + + if (inflater->opcode == NGHTTP2_HD_OPCODE_INDEXED) { + prefixlen = 7; + } else if (inflater->index_required) { + prefixlen = 6; + } else { + prefixlen = 4; + } + + rfin = 0; + rv = hd_inflate_read_len(inflater, &rfin, in, last, prefixlen, + get_max_index(&inflater->ctx) + 1); + if (rv < 0) { + goto fail; + } + + in += rv; + + if (!rfin) { + goto almost_ok; + } + + if (inflater->left == 0) { + rv = NGHTTP2_ERR_HEADER_COMP; + goto fail; + } + + DEBUGF(fprintf(stderr, "inflatehd: index=%zu\n", inflater->left)); + if (inflater->opcode == NGHTTP2_HD_OPCODE_INDEXED) { + inflater->index = inflater->left; + --inflater->index; + + rv = hd_inflate_commit_indexed(inflater, nv_out, token_out); + if (rv < 0) { + goto fail; + } + inflater->state = NGHTTP2_HD_STATE_OPCODE; + /* If rv == 1, no header was emitted */ + if (rv == 0) { + *inflate_flags |= NGHTTP2_HD_INFLATE_EMIT; + return (ssize_t)(in - first); + } + } else { + inflater->index = inflater->left; + --inflater->index; + + inflater->state = NGHTTP2_HD_STATE_CHECK_VALUELEN; + } + break; + } + case NGHTTP2_HD_STATE_NEWNAME_CHECK_NAMELEN: + hd_inflate_set_huffman_encoded(inflater, in); + inflater->state = NGHTTP2_HD_STATE_NEWNAME_READ_NAMELEN; + inflater->left = 0; + inflater->shift = 0; + DEBUGF(fprintf(stderr, "inflatehd: huffman encoded=%d\n", + inflater->huffman_encoded != 0)); + /* Fall through */ + case NGHTTP2_HD_STATE_NEWNAME_READ_NAMELEN: + rfin = 0; + rv = hd_inflate_read_len(inflater, &rfin, in, last, 7, NGHTTP2_HD_MAX_NV); + if (rv < 0) { + goto fail; + } + in += rv; + if (!rfin) { + DEBUGF(fprintf(stderr, + "inflatehd: integer not fully decoded. current=%zu\n", + inflater->left)); + + goto almost_ok; + } + + if (inflater->huffman_encoded) { + nghttp2_hd_huff_decode_context_init(&inflater->huff_decode_ctx); + + inflater->state = NGHTTP2_HD_STATE_NEWNAME_READ_NAMEHUFF; + } else { + inflater->state = NGHTTP2_HD_STATE_NEWNAME_READ_NAME; + } + break; + case NGHTTP2_HD_STATE_NEWNAME_READ_NAMEHUFF: + rv = hd_inflate_read_huff(inflater, &inflater->nvbufs, in, last); + if (rv < 0) { + goto fail; + } + + in += rv; + + DEBUGF(fprintf(stderr, "inflatehd: %zd bytes read\n", rv)); + + if (inflater->left) { + DEBUGF(fprintf(stderr, "inflatehd: still %zu bytes to go\n", + inflater->left)); + + goto almost_ok; + } + + inflater->newnamelen = nghttp2_bufs_len(&inflater->nvbufs); + + rv = nghttp2_bufs_addb(&inflater->nvbufs, '\0'); + if (rv != 0) { + goto fail; + } + + inflater->state = NGHTTP2_HD_STATE_CHECK_VALUELEN; + + break; + case NGHTTP2_HD_STATE_NEWNAME_READ_NAME: + rv = hd_inflate_read(inflater, &inflater->nvbufs, in, last); + if (rv < 0) { + goto fail; + } + + in += rv; + + DEBUGF(fprintf(stderr, "inflatehd: %zd bytes read\n", rv)); + if (inflater->left) { + DEBUGF(fprintf(stderr, "inflatehd: still %zu bytes to go\n", + inflater->left)); + + goto almost_ok; + } + + inflater->newnamelen = nghttp2_bufs_len(&inflater->nvbufs); + + rv = nghttp2_bufs_addb(&inflater->nvbufs, '\0'); + if (rv != 0) { + goto fail; + } + + inflater->state = NGHTTP2_HD_STATE_CHECK_VALUELEN; + + break; + case NGHTTP2_HD_STATE_CHECK_VALUELEN: + hd_inflate_set_huffman_encoded(inflater, in); + inflater->state = NGHTTP2_HD_STATE_READ_VALUELEN; + inflater->left = 0; + inflater->shift = 0; + DEBUGF(fprintf(stderr, "inflatehd: huffman encoded=%d\n", + inflater->huffman_encoded != 0)); + /* Fall through */ + case NGHTTP2_HD_STATE_READ_VALUELEN: + rfin = 0; + rv = hd_inflate_read_len(inflater, &rfin, in, last, 7, NGHTTP2_HD_MAX_NV); + if (rv < 0) { + goto fail; + } + + in += rv; + + if (!rfin) { + goto almost_ok; + } + + DEBUGF(fprintf(stderr, "inflatehd: valuelen=%zu\n", inflater->left)); + + if (inflater->huffman_encoded) { + nghttp2_hd_huff_decode_context_init(&inflater->huff_decode_ctx); + + inflater->state = NGHTTP2_HD_STATE_READ_VALUEHUFF; + } else { + inflater->state = NGHTTP2_HD_STATE_READ_VALUE; + } + + busy = 1; + + break; + case NGHTTP2_HD_STATE_READ_VALUEHUFF: + rv = hd_inflate_read_huff(inflater, &inflater->nvbufs, in, last); + if (rv < 0) { + goto fail; + } + + in += rv; + + DEBUGF(fprintf(stderr, "inflatehd: %zd bytes read\n", rv)); + + if (inflater->left) { + DEBUGF(fprintf(stderr, "inflatehd: still %zu bytes to go\n", + inflater->left)); + + goto almost_ok; + } + + rv = nghttp2_bufs_addb(&inflater->nvbufs, '\0'); + if (rv != 0) { + goto fail; + } + + if (inflater->opcode == NGHTTP2_HD_OPCODE_NEWNAME) { + rv = hd_inflate_commit_newname(inflater, nv_out, token_out); + } else { + rv = hd_inflate_commit_indname(inflater, nv_out, token_out); + } + + if (rv != 0) { + goto fail; + } + + inflater->state = NGHTTP2_HD_STATE_OPCODE; + *inflate_flags |= NGHTTP2_HD_INFLATE_EMIT; + + return (ssize_t)(in - first); + case NGHTTP2_HD_STATE_READ_VALUE: + rv = hd_inflate_read(inflater, &inflater->nvbufs, in, last); + if (rv < 0) { + DEBUGF(fprintf(stderr, "inflatehd: value read failure %zd: %s\n", rv, + nghttp2_strerror((int)rv))); + goto fail; + } + + in += rv; + + DEBUGF(fprintf(stderr, "inflatehd: %zd bytes read\n", rv)); + + if (inflater->left) { + DEBUGF(fprintf(stderr, "inflatehd: still %zu bytes to go\n", + inflater->left)); + goto almost_ok; + } + + rv = nghttp2_bufs_addb(&inflater->nvbufs, '\0'); + if (rv != 0) { + goto fail; + } + + if (inflater->opcode == NGHTTP2_HD_OPCODE_NEWNAME) { + rv = hd_inflate_commit_newname(inflater, nv_out, token_out); + } else { + rv = hd_inflate_commit_indname(inflater, nv_out, token_out); + } + + if (rv != 0) { + goto fail; + } + + inflater->state = NGHTTP2_HD_STATE_OPCODE; + *inflate_flags |= NGHTTP2_HD_INFLATE_EMIT; + + return (ssize_t)(in - first); + } + } + + assert(in == last); + + DEBUGF(fprintf(stderr, "inflatehd: all input bytes were processed\n")); + + if (in_final) { + DEBUGF(fprintf(stderr, "inflatehd: in_final set\n")); + + if (inflater->state != NGHTTP2_HD_STATE_OPCODE) { + DEBUGF(fprintf(stderr, "inflatehd: unacceptable state=%d\n", + inflater->state)); + rv = NGHTTP2_ERR_HEADER_COMP; + + goto fail; + } + *inflate_flags |= NGHTTP2_HD_INFLATE_FINAL; + } + return (ssize_t)(in - first); + +almost_ok: + if (in_final && inflater->state != NGHTTP2_HD_STATE_OPCODE) { + DEBUGF(fprintf(stderr, "inflatehd: input ended prematurely\n")); + + rv = NGHTTP2_ERR_HEADER_COMP; + + goto fail; + } + return (ssize_t)(in - first); + +fail: + DEBUGF(fprintf(stderr, "inflatehd: error return %zd\n", rv)); + + inflater->ctx.bad = 1; + return rv; +} + +int nghttp2_hd_inflate_end_headers(nghttp2_hd_inflater *inflater) { + hd_inflate_keep_free(inflater); + return 0; +} + +int nghttp2_hd_inflate_new(nghttp2_hd_inflater **inflater_ptr) { + return nghttp2_hd_inflate_new2(inflater_ptr, NULL); +} + +int nghttp2_hd_inflate_new2(nghttp2_hd_inflater **inflater_ptr, + nghttp2_mem *mem) { + int rv; + nghttp2_hd_inflater *inflater; + + if (mem == NULL) { + mem = nghttp2_mem_default(); + } + + inflater = nghttp2_mem_malloc(mem, sizeof(nghttp2_hd_inflater)); + + if (inflater == NULL) { + return NGHTTP2_ERR_NOMEM; + } + + rv = nghttp2_hd_inflate_init(inflater, mem); + + if (rv != 0) { + nghttp2_mem_free(mem, inflater); + + return rv; + } + + *inflater_ptr = inflater; + + return 0; +} + +void nghttp2_hd_inflate_del(nghttp2_hd_inflater *inflater) { + nghttp2_mem *mem; + + mem = inflater->ctx.mem; + nghttp2_hd_inflate_free(inflater); + + nghttp2_mem_free(mem, inflater); +} + +int nghttp2_hd_emit_indname_block(nghttp2_bufs *bufs, size_t idx, + nghttp2_nv *nv, int indexing_mode) { + + return emit_indname_block(bufs, idx, nv, indexing_mode); +} + +int nghttp2_hd_emit_newname_block(nghttp2_bufs *bufs, nghttp2_nv *nv, + int indexing_mode) { + return emit_newname_block(bufs, nv, indexing_mode); +} + +int nghttp2_hd_emit_table_size(nghttp2_bufs *bufs, size_t table_size) { + return emit_table_size(bufs, table_size); +} + +ssize_t nghttp2_hd_decode_length(uint32_t *res, size_t *shift_ptr, int *final, + uint32_t initial, size_t shift, uint8_t *in, + uint8_t *last, size_t prefix) { + return decode_length(res, shift_ptr, final, initial, shift, in, last, prefix); +} diff --git a/lib/nghttp2_hd.h b/lib/nghttp2_hd.h new file mode 100644 index 0000000..1b9ca7e --- /dev/null +++ b/lib/nghttp2_hd.h @@ -0,0 +1,421 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2013 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGHTTP2_HD_H +#define NGHTTP2_HD_H + +#ifdef HAVE_CONFIG_H +#include +#endif /* HAVE_CONFIG_H */ + +#include + +#include "nghttp2_hd_huffman.h" +#include "nghttp2_buf.h" +#include "nghttp2_mem.h" + +#define NGHTTP2_HD_DEFAULT_MAX_BUFFER_SIZE NGHTTP2_DEFAULT_HEADER_TABLE_SIZE +#define NGHTTP2_HD_ENTRY_OVERHEAD 32 + +/* The maximum length of one name/value pair. This is the sum of the + length of name and value. This is not specified by the spec. We + just chose the arbitrary size */ +#define NGHTTP2_HD_MAX_NV 65536 + +/* Default size of maximum table buffer size for encoder. Even if + remote decoder notifies larger buffer size for its decoding, + encoder only uses the memory up to this value. */ +#define NGHTTP2_HD_DEFAULT_MAX_DEFLATE_BUFFER_SIZE (1 << 12) + +/* Exported for unit test */ +#define NGHTTP2_STATIC_TABLE_LENGTH 61 + +/* Generated by genlibtokenlookup.py */ +typedef enum { + NGHTTP2_TOKEN__AUTHORITY = 0, + NGHTTP2_TOKEN__METHOD = 1, + NGHTTP2_TOKEN__PATH = 3, + NGHTTP2_TOKEN__SCHEME = 5, + NGHTTP2_TOKEN__STATUS = 7, + NGHTTP2_TOKEN_ACCEPT_CHARSET = 14, + NGHTTP2_TOKEN_ACCEPT_ENCODING = 15, + NGHTTP2_TOKEN_ACCEPT_LANGUAGE = 16, + NGHTTP2_TOKEN_ACCEPT_RANGES = 17, + NGHTTP2_TOKEN_ACCEPT = 18, + NGHTTP2_TOKEN_ACCESS_CONTROL_ALLOW_ORIGIN = 19, + NGHTTP2_TOKEN_AGE = 20, + NGHTTP2_TOKEN_ALLOW = 21, + NGHTTP2_TOKEN_AUTHORIZATION = 22, + NGHTTP2_TOKEN_CACHE_CONTROL = 23, + NGHTTP2_TOKEN_CONTENT_DISPOSITION = 24, + NGHTTP2_TOKEN_CONTENT_ENCODING = 25, + NGHTTP2_TOKEN_CONTENT_LANGUAGE = 26, + NGHTTP2_TOKEN_CONTENT_LENGTH = 27, + NGHTTP2_TOKEN_CONTENT_LOCATION = 28, + NGHTTP2_TOKEN_CONTENT_RANGE = 29, + NGHTTP2_TOKEN_CONTENT_TYPE = 30, + NGHTTP2_TOKEN_COOKIE = 31, + NGHTTP2_TOKEN_DATE = 32, + NGHTTP2_TOKEN_ETAG = 33, + NGHTTP2_TOKEN_EXPECT = 34, + NGHTTP2_TOKEN_EXPIRES = 35, + NGHTTP2_TOKEN_FROM = 36, + NGHTTP2_TOKEN_HOST = 37, + NGHTTP2_TOKEN_IF_MATCH = 38, + NGHTTP2_TOKEN_IF_MODIFIED_SINCE = 39, + NGHTTP2_TOKEN_IF_NONE_MATCH = 40, + NGHTTP2_TOKEN_IF_RANGE = 41, + NGHTTP2_TOKEN_IF_UNMODIFIED_SINCE = 42, + NGHTTP2_TOKEN_LAST_MODIFIED = 43, + NGHTTP2_TOKEN_LINK = 44, + NGHTTP2_TOKEN_LOCATION = 45, + NGHTTP2_TOKEN_MAX_FORWARDS = 46, + NGHTTP2_TOKEN_PROXY_AUTHENTICATE = 47, + NGHTTP2_TOKEN_PROXY_AUTHORIZATION = 48, + NGHTTP2_TOKEN_RANGE = 49, + NGHTTP2_TOKEN_REFERER = 50, + NGHTTP2_TOKEN_REFRESH = 51, + NGHTTP2_TOKEN_RETRY_AFTER = 52, + NGHTTP2_TOKEN_SERVER = 53, + NGHTTP2_TOKEN_SET_COOKIE = 54, + NGHTTP2_TOKEN_STRICT_TRANSPORT_SECURITY = 55, + NGHTTP2_TOKEN_TRANSFER_ENCODING = 56, + NGHTTP2_TOKEN_USER_AGENT = 57, + NGHTTP2_TOKEN_VARY = 58, + NGHTTP2_TOKEN_VIA = 59, + NGHTTP2_TOKEN_WWW_AUTHENTICATE = 60, + NGHTTP2_TOKEN_TE, + NGHTTP2_TOKEN_CONNECTION, + NGHTTP2_TOKEN_KEEP_ALIVE, + NGHTTP2_TOKEN_PROXY_CONNECTION, + NGHTTP2_TOKEN_UPGRADE +} nghttp2_token; + +typedef enum { + NGHTTP2_HD_FLAG_NONE = 0, + /* Indicates name was dynamically allocated and must be freed */ + NGHTTP2_HD_FLAG_NAME_ALLOC = 1, + /* Indicates value was dynamically allocated and must be freed */ + NGHTTP2_HD_FLAG_VALUE_ALLOC = 1 << 1, + /* Indicates that the name was gifted to the entry and no copying + necessary. */ + NGHTTP2_HD_FLAG_NAME_GIFT = 1 << 2, + /* Indicates that the value was gifted to the entry and no copying + necessary. */ + NGHTTP2_HD_FLAG_VALUE_GIFT = 1 << 3 +} nghttp2_hd_flags; + +typedef struct { + nghttp2_nv nv; + /* nghttp2_token value for nv.name. It could be -1 if we have no + token for that header field name. */ + int token; + /* Reference count */ + uint8_t ref; + uint8_t flags; +} nghttp2_hd_entry; + +typedef struct { + nghttp2_hd_entry **buffer; + size_t mask; + size_t first; + size_t len; +} nghttp2_hd_ringbuf; + +typedef enum { + NGHTTP2_HD_OPCODE_NONE, + NGHTTP2_HD_OPCODE_INDEXED, + NGHTTP2_HD_OPCODE_NEWNAME, + NGHTTP2_HD_OPCODE_INDNAME +} nghttp2_hd_opcode; + +typedef enum { + NGHTTP2_HD_STATE_OPCODE, + NGHTTP2_HD_STATE_READ_TABLE_SIZE, + NGHTTP2_HD_STATE_READ_INDEX, + NGHTTP2_HD_STATE_NEWNAME_CHECK_NAMELEN, + NGHTTP2_HD_STATE_NEWNAME_READ_NAMELEN, + NGHTTP2_HD_STATE_NEWNAME_READ_NAMEHUFF, + NGHTTP2_HD_STATE_NEWNAME_READ_NAME, + NGHTTP2_HD_STATE_CHECK_VALUELEN, + NGHTTP2_HD_STATE_READ_VALUELEN, + NGHTTP2_HD_STATE_READ_VALUEHUFF, + NGHTTP2_HD_STATE_READ_VALUE +} nghttp2_hd_inflate_state; + +typedef enum { + NGHTTP2_HD_WITH_INDEXING, + NGHTTP2_HD_WITHOUT_INDEXING, + NGHTTP2_HD_NEVER_INDEXING +} nghttp2_hd_indexing_mode; + +typedef struct { + /* dynamic header table */ + nghttp2_hd_ringbuf hd_table; + /* Memory allocator */ + nghttp2_mem *mem; + /* Abstract buffer size of hd_table as described in the spec. This + is the sum of length of name/value in hd_table + + NGHTTP2_HD_ENTRY_OVERHEAD bytes overhead per each entry. */ + size_t hd_table_bufsize; + /* The effective header table size. */ + size_t hd_table_bufsize_max; + /* If inflate/deflate error occurred, this value is set to 1 and + further invocation of inflate/deflate will fail with + NGHTTP2_ERR_HEADER_COMP. */ + uint8_t bad; +} nghttp2_hd_context; + +struct nghttp2_hd_deflater { + nghttp2_hd_context ctx; + /* The upper limit of the header table size the deflater accepts. */ + size_t deflate_hd_table_bufsize_max; + /* Minimum header table size notified in the next context update */ + size_t min_hd_table_bufsize_max; + /* If nonzero, send header table size using encoding context update + in the next deflate process */ + uint8_t notify_table_size_change; +}; + +struct nghttp2_hd_inflater { + nghttp2_hd_context ctx; + /* header buffer */ + nghttp2_bufs nvbufs; + /* Stores current state of huffman decoding */ + nghttp2_hd_huff_decode_context huff_decode_ctx; + /* Pointer to the nghttp2_hd_entry which is used current header + emission. This is required because in some cases the + ent_keep->ref == 0 and we have to keep track of it. */ + nghttp2_hd_entry *ent_keep; + /* Pointer to the name/value pair buffer which is used in the + current header emission. */ + uint8_t *nv_keep; + /* The number of bytes to read */ + size_t left; + /* The index in indexed repr or indexed name */ + size_t index; + /* The length of new name encoded in literal. For huffman encoded + string, this is the length after it is decoded. */ + size_t newnamelen; + /* The maximum header table size the inflater supports. This is the + same value transmitted in SETTINGS_HEADER_TABLE_SIZE */ + size_t settings_hd_table_bufsize_max; + /* The number of next shift to decode integer */ + size_t shift; + nghttp2_hd_opcode opcode; + nghttp2_hd_inflate_state state; + /* nonzero if string is huffman encoded */ + uint8_t huffman_encoded; + /* nonzero if deflater requires that current entry is indexed */ + uint8_t index_required; + /* nonzero if deflater requires that current entry must not be + indexed */ + uint8_t no_index; +}; + +/* + * Initializes the |ent| members. If NGHTTP2_HD_FLAG_NAME_ALLOC bit + * set in the |flags|, the content pointed by the |name| with length + * |namelen| is copied. Likewise, if NGHTTP2_HD_FLAG_VALUE_ALLOC bit + * set in the |flags|, the content pointed by the |value| with length + * |valuelen| is copied. The |token| is enum number looked up by + * |name|. It could be -1 if we don't have that enum value. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + */ +int nghttp2_hd_entry_init(nghttp2_hd_entry *ent, uint8_t flags, uint8_t *name, + size_t namelen, uint8_t *value, size_t valuelen, + int token, nghttp2_mem *mem); + +void nghttp2_hd_entry_free(nghttp2_hd_entry *ent, nghttp2_mem *mem); + +/* + * Initializes |deflater| for deflating name/values pairs. + * + * The encoder only uses up to + * NGHTTP2_HD_DEFAULT_MAX_DEFLATE_BUFFER_SIZE bytes for header table + * even if the larger value is specified later in + * nghttp2_hd_change_table_size(). + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + */ +int nghttp2_hd_deflate_init(nghttp2_hd_deflater *deflater, nghttp2_mem *mem); + +/* + * Initializes |deflater| for deflating name/values pairs. + * + * The encoder only uses up to |deflate_hd_table_bufsize_max| bytes + * for header table even if the larger value is specified later in + * nghttp2_hd_change_table_size(). + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + */ +int nghttp2_hd_deflate_init2(nghttp2_hd_deflater *deflater, + size_t deflate_hd_table_bufsize_max, + nghttp2_mem *mem); + +/* + * Deallocates any resources allocated for |deflater|. + */ +void nghttp2_hd_deflate_free(nghttp2_hd_deflater *deflater); + +/* + * Deflates the |nva|, which has the |nvlen| name/value pairs, into + * the |bufs|. + * + * This function expands |bufs| as necessary to store the result. If + * buffers is full and the process still requires more space, this + * funtion fails and returns NGHTTP2_ERR_HEADER_COMP. + * + * After this function returns, it is safe to delete the |nva|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + * NGHTTP2_ERR_HEADER_COMP + * Deflation process has failed. + * NGHTTP2_ERR_BUFFER_ERROR + * Out of buffer space. + */ +int nghttp2_hd_deflate_hd_bufs(nghttp2_hd_deflater *deflater, + nghttp2_bufs *bufs, const nghttp2_nv *nva, + size_t nvlen); + +/* + * Initializes |inflater| for inflating name/values pairs. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`NGHTTP2_ERR_NOMEM` + * Out of memory. + */ +int nghttp2_hd_inflate_init(nghttp2_hd_inflater *inflater, nghttp2_mem *mem); + +/* + * Deallocates any resources allocated for |inflater|. + */ +void nghttp2_hd_inflate_free(nghttp2_hd_inflater *inflater); + +/* + * Similar to nghttp2_hd_inflate_hd(), but this takes additional + * output parameter |token|. On successful header emission, it + * contains nghttp2_token value for nv_out->name. It could be -1 if + * we don't have enum value for the name. Other than that return + * values and semantics are the same as nghttp2_hd_inflate_hd(). + */ +ssize_t nghttp2_hd_inflate_hd2(nghttp2_hd_inflater *inflater, + nghttp2_nv *nv_out, int *inflate_flags, + int *token, uint8_t *in, size_t inlen, + int in_final); + +/* For unittesting purpose */ +int nghttp2_hd_emit_indname_block(nghttp2_bufs *bufs, size_t index, + nghttp2_nv *nv, int indexing_mode); + +/* For unittesting purpose */ +int nghttp2_hd_emit_newname_block(nghttp2_bufs *bufs, nghttp2_nv *nv, + int indexing_mode); + +/* For unittesting purpose */ +int nghttp2_hd_emit_table_size(nghttp2_bufs *bufs, size_t table_size); + +/* For unittesting purpose */ +nghttp2_hd_entry *nghttp2_hd_table_get(nghttp2_hd_context *context, + size_t index); + +/* For unittesting purpose */ +ssize_t nghttp2_hd_decode_length(uint32_t *res, size_t *shift_ptr, int *final, + uint32_t initial, size_t shift, uint8_t *in, + uint8_t *last, size_t prefix); + +/* Huffman encoding/decoding functions */ + +/* + * Counts the required bytes to encode |src| with length |len|. + * + * This function returns the number of required bytes to encode given + * data, including padding of prefix of terminal symbol code. This + * function always succeeds. + */ +size_t nghttp2_hd_huff_encode_count(const uint8_t *src, size_t len); + +/* + * Encodes the given data |src| with length |srclen| to the |bufs|. + * This function expands extra buffers in |bufs| if necessary. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + * NGHTTP2_ERR_BUFFER_ERROR + * Out of buffer space. + */ +int nghttp2_hd_huff_encode(nghttp2_bufs *bufs, const uint8_t *src, + size_t srclen); + +void nghttp2_hd_huff_decode_context_init(nghttp2_hd_huff_decode_context *ctx); + +/* + * Decodes the given data |src| with length |srclen|. The |ctx| must + * be initialized by nghttp2_hd_huff_decode_context_init(). The result + * will be added to |dest|. This function may expand |dest| as + * needed. The caller is responsible to release the memory of |dest| + * by calling nghttp2_bufs_free(). + * + * The caller must set the |final| to nonzero if the given input is + * the final block. + * + * This function returns the number of read bytes from the |in|. + * + * If this function fails, it returns one of the following negative + * return codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + * NGHTTP2_ERR_BUFFER_ERROR + * Maximum buffer capacity size exceeded. + * NGHTTP2_ERR_HEADER_COMP + * Decoding process has failed. + */ +ssize_t nghttp2_hd_huff_decode(nghttp2_hd_huff_decode_context *ctx, + nghttp2_bufs *bufs, const uint8_t *src, + size_t srclen, int final); + +#endif /* NGHTTP2_HD_H */ diff --git a/lib/nghttp2_hd_huffman.c b/lib/nghttp2_hd_huffman.c new file mode 100644 index 0000000..4df1cd0 --- /dev/null +++ b/lib/nghttp2_hd_huffman.c @@ -0,0 +1,227 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2013 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "nghttp2_hd_huffman.h" + +#include +#include +#include + +#include "nghttp2_hd.h" + +extern const nghttp2_huff_sym huff_sym_table[]; +extern const nghttp2_huff_decode huff_decode_table[][16]; + +/* + * Encodes huffman code |sym| into |*dest_ptr|, whose least |rembits| + * bits are not filled yet. The |rembits| must be in range [1, 8], + * inclusive. At the end of the process, the |*dest_ptr| is updated + * and points where next output should be placed. The number of + * unfilled bits in the pointed location is returned. + */ +static ssize_t huff_encode_sym(nghttp2_bufs *bufs, size_t *avail_ptr, + size_t rembits, const nghttp2_huff_sym *sym) { + int rv; + size_t nbits = sym->nbits; + uint32_t code = sym->code; + + /* We assume that sym->nbits <= 32 */ + if (rembits > nbits) { + nghttp2_bufs_fast_orb_hold(bufs, code << (rembits - nbits)); + return (ssize_t)(rembits - nbits); + } + + if (rembits == nbits) { + nghttp2_bufs_fast_orb(bufs, code); + --*avail_ptr; + return 8; + } + + nghttp2_bufs_fast_orb(bufs, code >> (nbits - rembits)); + --*avail_ptr; + + nbits -= rembits; + if (nbits & 0x7) { + /* align code to MSB byte boundary */ + code <<= 8 - (nbits & 0x7); + } + + /* we lose at most 3 bytes, but it is not critical in practice */ + if (*avail_ptr < (nbits + 7) / 8) { + rv = nghttp2_bufs_advance(bufs); + if (rv != 0) { + return rv; + } + *avail_ptr = nghttp2_bufs_cur_avail(bufs); + /* we assume that we at least 3 buffer space available */ + assert(*avail_ptr >= 3); + } + + /* fast path, since most code is less than 8 */ + if (nbits < 8) { + nghttp2_bufs_fast_addb_hold(bufs, code); + *avail_ptr = nghttp2_bufs_cur_avail(bufs); + return (ssize_t)(8 - nbits); + } + + /* handle longer code path */ + if (nbits > 24) { + nghttp2_bufs_fast_addb(bufs, code >> 24); + nbits -= 8; + } + + if (nbits > 16) { + nghttp2_bufs_fast_addb(bufs, code >> 16); + nbits -= 8; + } + + if (nbits > 8) { + nghttp2_bufs_fast_addb(bufs, code >> 8); + nbits -= 8; + } + + if (nbits == 8) { + nghttp2_bufs_fast_addb(bufs, code); + *avail_ptr = nghttp2_bufs_cur_avail(bufs); + return 8; + } + + nghttp2_bufs_fast_addb_hold(bufs, code); + *avail_ptr = nghttp2_bufs_cur_avail(bufs); + return (ssize_t)(8 - nbits); +} + +size_t nghttp2_hd_huff_encode_count(const uint8_t *src, size_t len) { + size_t i; + size_t nbits = 0; + + for (i = 0; i < len; ++i) { + nbits += huff_sym_table[src[i]].nbits; + } + /* pad the prefix of EOS (256) */ + return (nbits + 7) / 8; +} + +int nghttp2_hd_huff_encode(nghttp2_bufs *bufs, const uint8_t *src, + size_t srclen) { + int rv; + ssize_t rembits = 8; + size_t i; + size_t avail; + + avail = nghttp2_bufs_cur_avail(bufs); + + for (i = 0; i < srclen; ++i) { + const nghttp2_huff_sym *sym = &huff_sym_table[src[i]]; + if (rembits == 8) { + if (avail) { + nghttp2_bufs_fast_addb_hold(bufs, 0); + } else { + rv = nghttp2_bufs_addb_hold(bufs, 0); + if (rv != 0) { + return rv; + } + avail = nghttp2_bufs_cur_avail(bufs); + } + } + rembits = huff_encode_sym(bufs, &avail, rembits, sym); + if (rembits < 0) { + return (int)rembits; + } + } + /* 256 is special terminal symbol, pad with its prefix */ + if (rembits < 8) { + /* if rembits < 8, we should have at least 1 buffer space + available */ + const nghttp2_huff_sym *sym = &huff_sym_table[256]; + assert(avail); + /* Caution we no longer adjust avail here */ + nghttp2_bufs_fast_orb(bufs, sym->code >> (sym->nbits - rembits)); + } + + return 0; +} + +void nghttp2_hd_huff_decode_context_init(nghttp2_hd_huff_decode_context *ctx) { + ctx->state = 0; + ctx->accept = 1; +} + +/* Use macro to make the code simpler..., but error case is tricky. + We spent most of the CPU in decoding, so we are doing this + thing. */ +#define hd_huff_decode_sym_emit(bufs, sym, avail) \ + do { \ + if ((avail)) { \ + nghttp2_bufs_fast_addb((bufs), (sym)); \ + --(avail); \ + } else { \ + rv = nghttp2_bufs_addb((bufs), (sym)); \ + if (rv != 0) { \ + return rv; \ + } \ + (avail) = nghttp2_bufs_cur_avail((bufs)); \ + } \ + } while (0) + +ssize_t nghttp2_hd_huff_decode(nghttp2_hd_huff_decode_context *ctx, + nghttp2_bufs *bufs, const uint8_t *src, + size_t srclen, int final) { + size_t i; + int rv; + size_t avail; + + avail = nghttp2_bufs_cur_avail(bufs); + + /* We use the decoding algorithm described in + http://graphics.ics.uci.edu/pub/Prefix.pdf */ + for (i = 0; i < srclen; ++i) { + const nghttp2_huff_decode *t; + + t = &huff_decode_table[ctx->state][src[i] >> 4]; + if (t->flags & NGHTTP2_HUFF_FAIL) { + return NGHTTP2_ERR_HEADER_COMP; + } + if (t->flags & NGHTTP2_HUFF_SYM) { + /* this is macro, and may return from this function on error */ + hd_huff_decode_sym_emit(bufs, t->sym, avail); + } + + t = &huff_decode_table[t->state][src[i] & 0xf]; + if (t->flags & NGHTTP2_HUFF_FAIL) { + return NGHTTP2_ERR_HEADER_COMP; + } + if (t->flags & NGHTTP2_HUFF_SYM) { + /* this is macro, and may return from this function on error */ + hd_huff_decode_sym_emit(bufs, t->sym, avail); + } + + ctx->state = t->state; + ctx->accept = (t->flags & NGHTTP2_HUFF_ACCEPTED) != 0; + } + if (final && !ctx->accept) { + return NGHTTP2_ERR_HEADER_COMP; + } + return (ssize_t)i; +} diff --git a/lib/nghttp2_hd_huffman.h b/lib/nghttp2_hd_huffman.h new file mode 100644 index 0000000..714b6b6 --- /dev/null +++ b/lib/nghttp2_hd_huffman.h @@ -0,0 +1,74 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2013 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGHTTP2_HD_HUFFMAN_H +#define NGHTTP2_HD_HUFFMAN_H + +#ifdef HAVE_CONFIG_H +#include +#endif /* HAVE_CONFIG_H */ + +#include + +typedef enum { + /* FSA accepts this state as the end of huffman encoding + sequence. */ + NGHTTP2_HUFF_ACCEPTED = 1, + /* This state emits symbol */ + NGHTTP2_HUFF_SYM = (1 << 1), + /* If state machine reaches this state, decoding fails. */ + NGHTTP2_HUFF_FAIL = (1 << 2) +} nghttp2_huff_decode_flag; + +typedef struct { + /* huffman decoding state, which is actually the node ID of internal + huffman tree. We have 257 leaf nodes, but they are identical to + root node other than emitting a symbol, so we have 256 internal + nodes [1..255], inclusive. */ + uint8_t state; + /* bitwise OR of zero or more of the nghttp2_huff_decode_flag */ + uint8_t flags; + /* symbol if NGHTTP2_HUFF_SYM flag set */ + uint8_t sym; +} nghttp2_huff_decode; + +typedef nghttp2_huff_decode huff_decode_table_type[16]; + +typedef struct { + /* Current huffman decoding state. We stripped leaf nodes, so the + value range is [0..255], inclusive. */ + uint8_t state; + /* nonzero if we can say that the decoding process succeeds at this + state */ + uint8_t accept; +} nghttp2_hd_huff_decode_context; + +typedef struct { + /* The number of bits in this code */ + uint32_t nbits; + /* Huffman code aligned to LSB */ + uint32_t code; +} nghttp2_huff_sym; + +#endif /* NGHTTP2_HD_HUFFMAN_H */ diff --git a/lib/nghttp2_hd_huffman_data.c b/lib/nghttp2_hd_huffman_data.c new file mode 100644 index 0000000..4a4251b --- /dev/null +++ b/lib/nghttp2_hd_huffman_data.c @@ -0,0 +1,5152 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2013 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "nghttp2_hd_huffman.h" + +/* Generated by mkhufftbl.py */ + +const nghttp2_huff_sym huff_sym_table[] = {{13, 0x1ff8u}, + {23, 0x7fffd8u}, + {28, 0xfffffe2u}, + {28, 0xfffffe3u}, + {28, 0xfffffe4u}, + {28, 0xfffffe5u}, + {28, 0xfffffe6u}, + {28, 0xfffffe7u}, + {28, 0xfffffe8u}, + {24, 0xffffeau}, + {30, 0x3ffffffcu}, + {28, 0xfffffe9u}, + {28, 0xfffffeau}, + {30, 0x3ffffffdu}, + {28, 0xfffffebu}, + {28, 0xfffffecu}, + {28, 0xfffffedu}, + {28, 0xfffffeeu}, + {28, 0xfffffefu}, + {28, 0xffffff0u}, + {28, 0xffffff1u}, + {28, 0xffffff2u}, + {30, 0x3ffffffeu}, + {28, 0xffffff3u}, + {28, 0xffffff4u}, + {28, 0xffffff5u}, + {28, 0xffffff6u}, + {28, 0xffffff7u}, + {28, 0xffffff8u}, + {28, 0xffffff9u}, + {28, 0xffffffau}, + {28, 0xffffffbu}, + {6, 0x14u}, + {10, 0x3f8u}, + {10, 0x3f9u}, + {12, 0xffau}, + {13, 0x1ff9u}, + {6, 0x15u}, + {8, 0xf8u}, + {11, 0x7fau}, + {10, 0x3fau}, + {10, 0x3fbu}, + {8, 0xf9u}, + {11, 0x7fbu}, + {8, 0xfau}, + {6, 0x16u}, + {6, 0x17u}, + {6, 0x18u}, + {5, 0x0u}, + {5, 0x1u}, + {5, 0x2u}, + {6, 0x19u}, + {6, 0x1au}, + {6, 0x1bu}, + {6, 0x1cu}, + {6, 0x1du}, + {6, 0x1eu}, + {6, 0x1fu}, + {7, 0x5cu}, + {8, 0xfbu}, + {15, 0x7ffcu}, + {6, 0x20u}, + {12, 0xffbu}, + {10, 0x3fcu}, + {13, 0x1ffau}, + {6, 0x21u}, + {7, 0x5du}, + {7, 0x5eu}, + {7, 0x5fu}, + {7, 0x60u}, + {7, 0x61u}, + {7, 0x62u}, + {7, 0x63u}, + {7, 0x64u}, + {7, 0x65u}, + {7, 0x66u}, + {7, 0x67u}, + {7, 0x68u}, + {7, 0x69u}, + {7, 0x6au}, + {7, 0x6bu}, + {7, 0x6cu}, + {7, 0x6du}, + {7, 0x6eu}, + {7, 0x6fu}, + {7, 0x70u}, + {7, 0x71u}, + {7, 0x72u}, + {8, 0xfcu}, + {7, 0x73u}, + {8, 0xfdu}, + {13, 0x1ffbu}, + {19, 0x7fff0u}, + {13, 0x1ffcu}, + {14, 0x3ffcu}, + {6, 0x22u}, + {15, 0x7ffdu}, + {5, 0x3u}, + {6, 0x23u}, + {5, 0x4u}, + {6, 0x24u}, + {5, 0x5u}, + {6, 0x25u}, + {6, 0x26u}, + {6, 0x27u}, + {5, 0x6u}, + {7, 0x74u}, + {7, 0x75u}, + {6, 0x28u}, + {6, 0x29u}, + {6, 0x2au}, + {5, 0x7u}, + {6, 0x2bu}, + {7, 0x76u}, + {6, 0x2cu}, + {5, 0x8u}, + {5, 0x9u}, + {6, 0x2du}, + {7, 0x77u}, + {7, 0x78u}, + {7, 0x79u}, + {7, 0x7au}, + {7, 0x7bu}, + {15, 0x7ffeu}, + {11, 0x7fcu}, + {14, 0x3ffdu}, + {13, 0x1ffdu}, + {28, 0xffffffcu}, + {20, 0xfffe6u}, + {22, 0x3fffd2u}, + {20, 0xfffe7u}, + {20, 0xfffe8u}, + {22, 0x3fffd3u}, + {22, 0x3fffd4u}, + {22, 0x3fffd5u}, + {23, 0x7fffd9u}, + {22, 0x3fffd6u}, + {23, 0x7fffdau}, + {23, 0x7fffdbu}, + {23, 0x7fffdcu}, + {23, 0x7fffddu}, + {23, 0x7fffdeu}, + {24, 0xffffebu}, + {23, 0x7fffdfu}, + {24, 0xffffecu}, + {24, 0xffffedu}, + {22, 0x3fffd7u}, + {23, 0x7fffe0u}, + {24, 0xffffeeu}, + {23, 0x7fffe1u}, + {23, 0x7fffe2u}, + {23, 0x7fffe3u}, + {23, 0x7fffe4u}, + {21, 0x1fffdcu}, + {22, 0x3fffd8u}, + {23, 0x7fffe5u}, + {22, 0x3fffd9u}, + {23, 0x7fffe6u}, + {23, 0x7fffe7u}, + {24, 0xffffefu}, + {22, 0x3fffdau}, + {21, 0x1fffddu}, + {20, 0xfffe9u}, + {22, 0x3fffdbu}, + {22, 0x3fffdcu}, + {23, 0x7fffe8u}, + {23, 0x7fffe9u}, + {21, 0x1fffdeu}, + {23, 0x7fffeau}, + {22, 0x3fffddu}, + {22, 0x3fffdeu}, + {24, 0xfffff0u}, + {21, 0x1fffdfu}, + {22, 0x3fffdfu}, + {23, 0x7fffebu}, + {23, 0x7fffecu}, + {21, 0x1fffe0u}, + {21, 0x1fffe1u}, + {22, 0x3fffe0u}, + {21, 0x1fffe2u}, + {23, 0x7fffedu}, + {22, 0x3fffe1u}, + {23, 0x7fffeeu}, + {23, 0x7fffefu}, + {20, 0xfffeau}, + {22, 0x3fffe2u}, + {22, 0x3fffe3u}, + {22, 0x3fffe4u}, + {23, 0x7ffff0u}, + {22, 0x3fffe5u}, + {22, 0x3fffe6u}, + {23, 0x7ffff1u}, + {26, 0x3ffffe0u}, + {26, 0x3ffffe1u}, + {20, 0xfffebu}, + {19, 0x7fff1u}, + {22, 0x3fffe7u}, + {23, 0x7ffff2u}, + {22, 0x3fffe8u}, + {25, 0x1ffffecu}, + {26, 0x3ffffe2u}, + {26, 0x3ffffe3u}, + {26, 0x3ffffe4u}, + {27, 0x7ffffdeu}, + {27, 0x7ffffdfu}, + {26, 0x3ffffe5u}, + {24, 0xfffff1u}, + {25, 0x1ffffedu}, + {19, 0x7fff2u}, + {21, 0x1fffe3u}, + {26, 0x3ffffe6u}, + {27, 0x7ffffe0u}, + {27, 0x7ffffe1u}, + {26, 0x3ffffe7u}, + {27, 0x7ffffe2u}, + {24, 0xfffff2u}, + {21, 0x1fffe4u}, + {21, 0x1fffe5u}, + {26, 0x3ffffe8u}, + {26, 0x3ffffe9u}, + {28, 0xffffffdu}, + {27, 0x7ffffe3u}, + {27, 0x7ffffe4u}, + {27, 0x7ffffe5u}, + {20, 0xfffecu}, + {24, 0xfffff3u}, + {20, 0xfffedu}, + {21, 0x1fffe6u}, + {22, 0x3fffe9u}, + {21, 0x1fffe7u}, + {21, 0x1fffe8u}, + {23, 0x7ffff3u}, + {22, 0x3fffeau}, + {22, 0x3fffebu}, + {25, 0x1ffffeeu}, + {25, 0x1ffffefu}, + {24, 0xfffff4u}, + {24, 0xfffff5u}, + {26, 0x3ffffeau}, + {23, 0x7ffff4u}, + {26, 0x3ffffebu}, + {27, 0x7ffffe6u}, + {26, 0x3ffffecu}, + {26, 0x3ffffedu}, + {27, 0x7ffffe7u}, + {27, 0x7ffffe8u}, + {27, 0x7ffffe9u}, + {27, 0x7ffffeau}, + {27, 0x7ffffebu}, + {28, 0xffffffeu}, + {27, 0x7ffffecu}, + {27, 0x7ffffedu}, + {27, 0x7ffffeeu}, + {27, 0x7ffffefu}, + {27, 0x7fffff0u}, + {26, 0x3ffffeeu}, + {30, 0x3fffffffu}}; + +const nghttp2_huff_decode huff_decode_table[][16] = { + /* 0 */ + { + {4, 0x00, 0}, + {5, 0x00, 0}, + {7, 0x00, 0}, + {8, 0x00, 0}, + {11, 0x00, 0}, + {12, 0x00, 0}, + {16, 0x00, 0}, + {19, 0x00, 0}, + {25, 0x00, 0}, + {28, 0x00, 0}, + {32, 0x00, 0}, + {35, 0x00, 0}, + {42, 0x00, 0}, + {49, 0x00, 0}, + {57, 0x00, 0}, + {64, 0x01, 0}, + }, + /* 1 */ + { + {0, 0x03, 48}, + {0, 0x03, 49}, + {0, 0x03, 50}, + {0, 0x03, 97}, + {0, 0x03, 99}, + {0, 0x03, 101}, + {0, 0x03, 105}, + {0, 0x03, 111}, + {0, 0x03, 115}, + {0, 0x03, 116}, + {13, 0x00, 0}, + {14, 0x00, 0}, + {17, 0x00, 0}, + {18, 0x00, 0}, + {20, 0x00, 0}, + {21, 0x00, 0}, + }, + /* 2 */ + { + {1, 0x02, 48}, + {22, 0x03, 48}, + {1, 0x02, 49}, + {22, 0x03, 49}, + {1, 0x02, 50}, + {22, 0x03, 50}, + {1, 0x02, 97}, + {22, 0x03, 97}, + {1, 0x02, 99}, + {22, 0x03, 99}, + {1, 0x02, 101}, + {22, 0x03, 101}, + {1, 0x02, 105}, + {22, 0x03, 105}, + {1, 0x02, 111}, + {22, 0x03, 111}, + }, + /* 3 */ + { + {2, 0x02, 48}, + {9, 0x02, 48}, + {23, 0x02, 48}, + {40, 0x03, 48}, + {2, 0x02, 49}, + {9, 0x02, 49}, + {23, 0x02, 49}, + {40, 0x03, 49}, + {2, 0x02, 50}, + {9, 0x02, 50}, + {23, 0x02, 50}, + {40, 0x03, 50}, + {2, 0x02, 97}, + {9, 0x02, 97}, + {23, 0x02, 97}, + {40, 0x03, 97}, + }, + /* 4 */ + { + {3, 0x02, 48}, + {6, 0x02, 48}, + {10, 0x02, 48}, + {15, 0x02, 48}, + {24, 0x02, 48}, + {31, 0x02, 48}, + {41, 0x02, 48}, + {56, 0x03, 48}, + {3, 0x02, 49}, + {6, 0x02, 49}, + {10, 0x02, 49}, + {15, 0x02, 49}, + {24, 0x02, 49}, + {31, 0x02, 49}, + {41, 0x02, 49}, + {56, 0x03, 49}, + }, + /* 5 */ + { + {3, 0x02, 50}, + {6, 0x02, 50}, + {10, 0x02, 50}, + {15, 0x02, 50}, + {24, 0x02, 50}, + {31, 0x02, 50}, + {41, 0x02, 50}, + {56, 0x03, 50}, + {3, 0x02, 97}, + {6, 0x02, 97}, + {10, 0x02, 97}, + {15, 0x02, 97}, + {24, 0x02, 97}, + {31, 0x02, 97}, + {41, 0x02, 97}, + {56, 0x03, 97}, + }, + /* 6 */ + { + {2, 0x02, 99}, + {9, 0x02, 99}, + {23, 0x02, 99}, + {40, 0x03, 99}, + {2, 0x02, 101}, + {9, 0x02, 101}, + {23, 0x02, 101}, + {40, 0x03, 101}, + {2, 0x02, 105}, + {9, 0x02, 105}, + {23, 0x02, 105}, + {40, 0x03, 105}, + {2, 0x02, 111}, + {9, 0x02, 111}, + {23, 0x02, 111}, + {40, 0x03, 111}, + }, + /* 7 */ + { + {3, 0x02, 99}, + {6, 0x02, 99}, + {10, 0x02, 99}, + {15, 0x02, 99}, + {24, 0x02, 99}, + {31, 0x02, 99}, + {41, 0x02, 99}, + {56, 0x03, 99}, + {3, 0x02, 101}, + {6, 0x02, 101}, + {10, 0x02, 101}, + {15, 0x02, 101}, + {24, 0x02, 101}, + {31, 0x02, 101}, + {41, 0x02, 101}, + {56, 0x03, 101}, + }, + /* 8 */ + { + {3, 0x02, 105}, + {6, 0x02, 105}, + {10, 0x02, 105}, + {15, 0x02, 105}, + {24, 0x02, 105}, + {31, 0x02, 105}, + {41, 0x02, 105}, + {56, 0x03, 105}, + {3, 0x02, 111}, + {6, 0x02, 111}, + {10, 0x02, 111}, + {15, 0x02, 111}, + {24, 0x02, 111}, + {31, 0x02, 111}, + {41, 0x02, 111}, + {56, 0x03, 111}, + }, + /* 9 */ + { + {1, 0x02, 115}, + {22, 0x03, 115}, + {1, 0x02, 116}, + {22, 0x03, 116}, + {0, 0x03, 32}, + {0, 0x03, 37}, + {0, 0x03, 45}, + {0, 0x03, 46}, + {0, 0x03, 47}, + {0, 0x03, 51}, + {0, 0x03, 52}, + {0, 0x03, 53}, + {0, 0x03, 54}, + {0, 0x03, 55}, + {0, 0x03, 56}, + {0, 0x03, 57}, + }, + /* 10 */ + { + {2, 0x02, 115}, + {9, 0x02, 115}, + {23, 0x02, 115}, + {40, 0x03, 115}, + {2, 0x02, 116}, + {9, 0x02, 116}, + {23, 0x02, 116}, + {40, 0x03, 116}, + {1, 0x02, 32}, + {22, 0x03, 32}, + {1, 0x02, 37}, + {22, 0x03, 37}, + {1, 0x02, 45}, + {22, 0x03, 45}, + {1, 0x02, 46}, + {22, 0x03, 46}, + }, + /* 11 */ + { + {3, 0x02, 115}, + {6, 0x02, 115}, + {10, 0x02, 115}, + {15, 0x02, 115}, + {24, 0x02, 115}, + {31, 0x02, 115}, + {41, 0x02, 115}, + {56, 0x03, 115}, + {3, 0x02, 116}, + {6, 0x02, 116}, + {10, 0x02, 116}, + {15, 0x02, 116}, + {24, 0x02, 116}, + {31, 0x02, 116}, + {41, 0x02, 116}, + {56, 0x03, 116}, + }, + /* 12 */ + { + {2, 0x02, 32}, + {9, 0x02, 32}, + {23, 0x02, 32}, + {40, 0x03, 32}, + {2, 0x02, 37}, + {9, 0x02, 37}, + {23, 0x02, 37}, + {40, 0x03, 37}, + {2, 0x02, 45}, + {9, 0x02, 45}, + {23, 0x02, 45}, + {40, 0x03, 45}, + {2, 0x02, 46}, + {9, 0x02, 46}, + {23, 0x02, 46}, + {40, 0x03, 46}, + }, + /* 13 */ + { + {3, 0x02, 32}, + {6, 0x02, 32}, + {10, 0x02, 32}, + {15, 0x02, 32}, + {24, 0x02, 32}, + {31, 0x02, 32}, + {41, 0x02, 32}, + {56, 0x03, 32}, + {3, 0x02, 37}, + {6, 0x02, 37}, + {10, 0x02, 37}, + {15, 0x02, 37}, + {24, 0x02, 37}, + {31, 0x02, 37}, + {41, 0x02, 37}, + {56, 0x03, 37}, + }, + /* 14 */ + { + {3, 0x02, 45}, + {6, 0x02, 45}, + {10, 0x02, 45}, + {15, 0x02, 45}, + {24, 0x02, 45}, + {31, 0x02, 45}, + {41, 0x02, 45}, + {56, 0x03, 45}, + {3, 0x02, 46}, + {6, 0x02, 46}, + {10, 0x02, 46}, + {15, 0x02, 46}, + {24, 0x02, 46}, + {31, 0x02, 46}, + {41, 0x02, 46}, + {56, 0x03, 46}, + }, + /* 15 */ + { + {1, 0x02, 47}, + {22, 0x03, 47}, + {1, 0x02, 51}, + {22, 0x03, 51}, + {1, 0x02, 52}, + {22, 0x03, 52}, + {1, 0x02, 53}, + {22, 0x03, 53}, + {1, 0x02, 54}, + {22, 0x03, 54}, + {1, 0x02, 55}, + {22, 0x03, 55}, + {1, 0x02, 56}, + {22, 0x03, 56}, + {1, 0x02, 57}, + {22, 0x03, 57}, + }, + /* 16 */ + { + {2, 0x02, 47}, + {9, 0x02, 47}, + {23, 0x02, 47}, + {40, 0x03, 47}, + {2, 0x02, 51}, + {9, 0x02, 51}, + {23, 0x02, 51}, + {40, 0x03, 51}, + {2, 0x02, 52}, + {9, 0x02, 52}, + {23, 0x02, 52}, + {40, 0x03, 52}, + {2, 0x02, 53}, + {9, 0x02, 53}, + {23, 0x02, 53}, + {40, 0x03, 53}, + }, + /* 17 */ + { + {3, 0x02, 47}, + {6, 0x02, 47}, + {10, 0x02, 47}, + {15, 0x02, 47}, + {24, 0x02, 47}, + {31, 0x02, 47}, + {41, 0x02, 47}, + {56, 0x03, 47}, + {3, 0x02, 51}, + {6, 0x02, 51}, + {10, 0x02, 51}, + {15, 0x02, 51}, + {24, 0x02, 51}, + {31, 0x02, 51}, + {41, 0x02, 51}, + {56, 0x03, 51}, + }, + /* 18 */ + { + {3, 0x02, 52}, + {6, 0x02, 52}, + {10, 0x02, 52}, + {15, 0x02, 52}, + {24, 0x02, 52}, + {31, 0x02, 52}, + {41, 0x02, 52}, + {56, 0x03, 52}, + {3, 0x02, 53}, + {6, 0x02, 53}, + {10, 0x02, 53}, + {15, 0x02, 53}, + {24, 0x02, 53}, + {31, 0x02, 53}, + {41, 0x02, 53}, + {56, 0x03, 53}, + }, + /* 19 */ + { + {2, 0x02, 54}, + {9, 0x02, 54}, + {23, 0x02, 54}, + {40, 0x03, 54}, + {2, 0x02, 55}, + {9, 0x02, 55}, + {23, 0x02, 55}, + {40, 0x03, 55}, + {2, 0x02, 56}, + {9, 0x02, 56}, + {23, 0x02, 56}, + {40, 0x03, 56}, + {2, 0x02, 57}, + {9, 0x02, 57}, + {23, 0x02, 57}, + {40, 0x03, 57}, + }, + /* 20 */ + { + {3, 0x02, 54}, + {6, 0x02, 54}, + {10, 0x02, 54}, + {15, 0x02, 54}, + {24, 0x02, 54}, + {31, 0x02, 54}, + {41, 0x02, 54}, + {56, 0x03, 54}, + {3, 0x02, 55}, + {6, 0x02, 55}, + {10, 0x02, 55}, + {15, 0x02, 55}, + {24, 0x02, 55}, + {31, 0x02, 55}, + {41, 0x02, 55}, + {56, 0x03, 55}, + }, + /* 21 */ + { + {3, 0x02, 56}, + {6, 0x02, 56}, + {10, 0x02, 56}, + {15, 0x02, 56}, + {24, 0x02, 56}, + {31, 0x02, 56}, + {41, 0x02, 56}, + {56, 0x03, 56}, + {3, 0x02, 57}, + {6, 0x02, 57}, + {10, 0x02, 57}, + {15, 0x02, 57}, + {24, 0x02, 57}, + {31, 0x02, 57}, + {41, 0x02, 57}, + {56, 0x03, 57}, + }, + /* 22 */ + { + {26, 0x00, 0}, + {27, 0x00, 0}, + {29, 0x00, 0}, + {30, 0x00, 0}, + {33, 0x00, 0}, + {34, 0x00, 0}, + {36, 0x00, 0}, + {37, 0x00, 0}, + {43, 0x00, 0}, + {46, 0x00, 0}, + {50, 0x00, 0}, + {53, 0x00, 0}, + {58, 0x00, 0}, + {61, 0x00, 0}, + {65, 0x00, 0}, + {68, 0x01, 0}, + }, + /* 23 */ + { + {0, 0x03, 61}, + {0, 0x03, 65}, + {0, 0x03, 95}, + {0, 0x03, 98}, + {0, 0x03, 100}, + {0, 0x03, 102}, + {0, 0x03, 103}, + {0, 0x03, 104}, + {0, 0x03, 108}, + {0, 0x03, 109}, + {0, 0x03, 110}, + {0, 0x03, 112}, + {0, 0x03, 114}, + {0, 0x03, 117}, + {38, 0x00, 0}, + {39, 0x00, 0}, + }, + /* 24 */ + { + {1, 0x02, 61}, + {22, 0x03, 61}, + {1, 0x02, 65}, + {22, 0x03, 65}, + {1, 0x02, 95}, + {22, 0x03, 95}, + {1, 0x02, 98}, + {22, 0x03, 98}, + {1, 0x02, 100}, + {22, 0x03, 100}, + {1, 0x02, 102}, + {22, 0x03, 102}, + {1, 0x02, 103}, + {22, 0x03, 103}, + {1, 0x02, 104}, + {22, 0x03, 104}, + }, + /* 25 */ + { + {2, 0x02, 61}, + {9, 0x02, 61}, + {23, 0x02, 61}, + {40, 0x03, 61}, + {2, 0x02, 65}, + {9, 0x02, 65}, + {23, 0x02, 65}, + {40, 0x03, 65}, + {2, 0x02, 95}, + {9, 0x02, 95}, + {23, 0x02, 95}, + {40, 0x03, 95}, + {2, 0x02, 98}, + {9, 0x02, 98}, + {23, 0x02, 98}, + {40, 0x03, 98}, + }, + /* 26 */ + { + {3, 0x02, 61}, + {6, 0x02, 61}, + {10, 0x02, 61}, + {15, 0x02, 61}, + {24, 0x02, 61}, + {31, 0x02, 61}, + {41, 0x02, 61}, + {56, 0x03, 61}, + {3, 0x02, 65}, + {6, 0x02, 65}, + {10, 0x02, 65}, + {15, 0x02, 65}, + {24, 0x02, 65}, + {31, 0x02, 65}, + {41, 0x02, 65}, + {56, 0x03, 65}, + }, + /* 27 */ + { + {3, 0x02, 95}, + {6, 0x02, 95}, + {10, 0x02, 95}, + {15, 0x02, 95}, + {24, 0x02, 95}, + {31, 0x02, 95}, + {41, 0x02, 95}, + {56, 0x03, 95}, + {3, 0x02, 98}, + {6, 0x02, 98}, + {10, 0x02, 98}, + {15, 0x02, 98}, + {24, 0x02, 98}, + {31, 0x02, 98}, + {41, 0x02, 98}, + {56, 0x03, 98}, + }, + /* 28 */ + { + {2, 0x02, 100}, + {9, 0x02, 100}, + {23, 0x02, 100}, + {40, 0x03, 100}, + {2, 0x02, 102}, + {9, 0x02, 102}, + {23, 0x02, 102}, + {40, 0x03, 102}, + {2, 0x02, 103}, + {9, 0x02, 103}, + {23, 0x02, 103}, + {40, 0x03, 103}, + {2, 0x02, 104}, + {9, 0x02, 104}, + {23, 0x02, 104}, + {40, 0x03, 104}, + }, + /* 29 */ + { + {3, 0x02, 100}, + {6, 0x02, 100}, + {10, 0x02, 100}, + {15, 0x02, 100}, + {24, 0x02, 100}, + {31, 0x02, 100}, + {41, 0x02, 100}, + {56, 0x03, 100}, + {3, 0x02, 102}, + {6, 0x02, 102}, + {10, 0x02, 102}, + {15, 0x02, 102}, + {24, 0x02, 102}, + {31, 0x02, 102}, + {41, 0x02, 102}, + {56, 0x03, 102}, + }, + /* 30 */ + { + {3, 0x02, 103}, + {6, 0x02, 103}, + {10, 0x02, 103}, + {15, 0x02, 103}, + {24, 0x02, 103}, + {31, 0x02, 103}, + {41, 0x02, 103}, + {56, 0x03, 103}, + {3, 0x02, 104}, + {6, 0x02, 104}, + {10, 0x02, 104}, + {15, 0x02, 104}, + {24, 0x02, 104}, + {31, 0x02, 104}, + {41, 0x02, 104}, + {56, 0x03, 104}, + }, + /* 31 */ + { + {1, 0x02, 108}, + {22, 0x03, 108}, + {1, 0x02, 109}, + {22, 0x03, 109}, + {1, 0x02, 110}, + {22, 0x03, 110}, + {1, 0x02, 112}, + {22, 0x03, 112}, + {1, 0x02, 114}, + {22, 0x03, 114}, + {1, 0x02, 117}, + {22, 0x03, 117}, + {0, 0x03, 58}, + {0, 0x03, 66}, + {0, 0x03, 67}, + {0, 0x03, 68}, + }, + /* 32 */ + { + {2, 0x02, 108}, + {9, 0x02, 108}, + {23, 0x02, 108}, + {40, 0x03, 108}, + {2, 0x02, 109}, + {9, 0x02, 109}, + {23, 0x02, 109}, + {40, 0x03, 109}, + {2, 0x02, 110}, + {9, 0x02, 110}, + {23, 0x02, 110}, + {40, 0x03, 110}, + {2, 0x02, 112}, + {9, 0x02, 112}, + {23, 0x02, 112}, + {40, 0x03, 112}, + }, + /* 33 */ + { + {3, 0x02, 108}, + {6, 0x02, 108}, + {10, 0x02, 108}, + {15, 0x02, 108}, + {24, 0x02, 108}, + {31, 0x02, 108}, + {41, 0x02, 108}, + {56, 0x03, 108}, + {3, 0x02, 109}, + {6, 0x02, 109}, + {10, 0x02, 109}, + {15, 0x02, 109}, + {24, 0x02, 109}, + {31, 0x02, 109}, + {41, 0x02, 109}, + {56, 0x03, 109}, + }, + /* 34 */ + { + {3, 0x02, 110}, + {6, 0x02, 110}, + {10, 0x02, 110}, + {15, 0x02, 110}, + {24, 0x02, 110}, + {31, 0x02, 110}, + {41, 0x02, 110}, + {56, 0x03, 110}, + {3, 0x02, 112}, + {6, 0x02, 112}, + {10, 0x02, 112}, + {15, 0x02, 112}, + {24, 0x02, 112}, + {31, 0x02, 112}, + {41, 0x02, 112}, + {56, 0x03, 112}, + }, + /* 35 */ + { + {2, 0x02, 114}, + {9, 0x02, 114}, + {23, 0x02, 114}, + {40, 0x03, 114}, + {2, 0x02, 117}, + {9, 0x02, 117}, + {23, 0x02, 117}, + {40, 0x03, 117}, + {1, 0x02, 58}, + {22, 0x03, 58}, + {1, 0x02, 66}, + {22, 0x03, 66}, + {1, 0x02, 67}, + {22, 0x03, 67}, + {1, 0x02, 68}, + {22, 0x03, 68}, + }, + /* 36 */ + { + {3, 0x02, 114}, + {6, 0x02, 114}, + {10, 0x02, 114}, + {15, 0x02, 114}, + {24, 0x02, 114}, + {31, 0x02, 114}, + {41, 0x02, 114}, + {56, 0x03, 114}, + {3, 0x02, 117}, + {6, 0x02, 117}, + {10, 0x02, 117}, + {15, 0x02, 117}, + {24, 0x02, 117}, + {31, 0x02, 117}, + {41, 0x02, 117}, + {56, 0x03, 117}, + }, + /* 37 */ + { + {2, 0x02, 58}, + {9, 0x02, 58}, + {23, 0x02, 58}, + {40, 0x03, 58}, + {2, 0x02, 66}, + {9, 0x02, 66}, + {23, 0x02, 66}, + {40, 0x03, 66}, + {2, 0x02, 67}, + {9, 0x02, 67}, + {23, 0x02, 67}, + {40, 0x03, 67}, + {2, 0x02, 68}, + {9, 0x02, 68}, + {23, 0x02, 68}, + {40, 0x03, 68}, + }, + /* 38 */ + { + {3, 0x02, 58}, + {6, 0x02, 58}, + {10, 0x02, 58}, + {15, 0x02, 58}, + {24, 0x02, 58}, + {31, 0x02, 58}, + {41, 0x02, 58}, + {56, 0x03, 58}, + {3, 0x02, 66}, + {6, 0x02, 66}, + {10, 0x02, 66}, + {15, 0x02, 66}, + {24, 0x02, 66}, + {31, 0x02, 66}, + {41, 0x02, 66}, + {56, 0x03, 66}, + }, + /* 39 */ + { + {3, 0x02, 67}, + {6, 0x02, 67}, + {10, 0x02, 67}, + {15, 0x02, 67}, + {24, 0x02, 67}, + {31, 0x02, 67}, + {41, 0x02, 67}, + {56, 0x03, 67}, + {3, 0x02, 68}, + {6, 0x02, 68}, + {10, 0x02, 68}, + {15, 0x02, 68}, + {24, 0x02, 68}, + {31, 0x02, 68}, + {41, 0x02, 68}, + {56, 0x03, 68}, + }, + /* 40 */ + { + {44, 0x00, 0}, + {45, 0x00, 0}, + {47, 0x00, 0}, + {48, 0x00, 0}, + {51, 0x00, 0}, + {52, 0x00, 0}, + {54, 0x00, 0}, + {55, 0x00, 0}, + {59, 0x00, 0}, + {60, 0x00, 0}, + {62, 0x00, 0}, + {63, 0x00, 0}, + {66, 0x00, 0}, + {67, 0x00, 0}, + {69, 0x00, 0}, + {72, 0x01, 0}, + }, + /* 41 */ + { + {0, 0x03, 69}, + {0, 0x03, 70}, + {0, 0x03, 71}, + {0, 0x03, 72}, + {0, 0x03, 73}, + {0, 0x03, 74}, + {0, 0x03, 75}, + {0, 0x03, 76}, + {0, 0x03, 77}, + {0, 0x03, 78}, + {0, 0x03, 79}, + {0, 0x03, 80}, + {0, 0x03, 81}, + {0, 0x03, 82}, + {0, 0x03, 83}, + {0, 0x03, 84}, + }, + /* 42 */ + { + {1, 0x02, 69}, + {22, 0x03, 69}, + {1, 0x02, 70}, + {22, 0x03, 70}, + {1, 0x02, 71}, + {22, 0x03, 71}, + {1, 0x02, 72}, + {22, 0x03, 72}, + {1, 0x02, 73}, + {22, 0x03, 73}, + {1, 0x02, 74}, + {22, 0x03, 74}, + {1, 0x02, 75}, + {22, 0x03, 75}, + {1, 0x02, 76}, + {22, 0x03, 76}, + }, + /* 43 */ + { + {2, 0x02, 69}, + {9, 0x02, 69}, + {23, 0x02, 69}, + {40, 0x03, 69}, + {2, 0x02, 70}, + {9, 0x02, 70}, + {23, 0x02, 70}, + {40, 0x03, 70}, + {2, 0x02, 71}, + {9, 0x02, 71}, + {23, 0x02, 71}, + {40, 0x03, 71}, + {2, 0x02, 72}, + {9, 0x02, 72}, + {23, 0x02, 72}, + {40, 0x03, 72}, + }, + /* 44 */ + { + {3, 0x02, 69}, + {6, 0x02, 69}, + {10, 0x02, 69}, + {15, 0x02, 69}, + {24, 0x02, 69}, + {31, 0x02, 69}, + {41, 0x02, 69}, + {56, 0x03, 69}, + {3, 0x02, 70}, + {6, 0x02, 70}, + {10, 0x02, 70}, + {15, 0x02, 70}, + {24, 0x02, 70}, + {31, 0x02, 70}, + {41, 0x02, 70}, + {56, 0x03, 70}, + }, + /* 45 */ + { + {3, 0x02, 71}, + {6, 0x02, 71}, + {10, 0x02, 71}, + {15, 0x02, 71}, + {24, 0x02, 71}, + {31, 0x02, 71}, + {41, 0x02, 71}, + {56, 0x03, 71}, + {3, 0x02, 72}, + {6, 0x02, 72}, + {10, 0x02, 72}, + {15, 0x02, 72}, + {24, 0x02, 72}, + {31, 0x02, 72}, + {41, 0x02, 72}, + {56, 0x03, 72}, + }, + /* 46 */ + { + {2, 0x02, 73}, + {9, 0x02, 73}, + {23, 0x02, 73}, + {40, 0x03, 73}, + {2, 0x02, 74}, + {9, 0x02, 74}, + {23, 0x02, 74}, + {40, 0x03, 74}, + {2, 0x02, 75}, + {9, 0x02, 75}, + {23, 0x02, 75}, + {40, 0x03, 75}, + {2, 0x02, 76}, + {9, 0x02, 76}, + {23, 0x02, 76}, + {40, 0x03, 76}, + }, + /* 47 */ + { + {3, 0x02, 73}, + {6, 0x02, 73}, + {10, 0x02, 73}, + {15, 0x02, 73}, + {24, 0x02, 73}, + {31, 0x02, 73}, + {41, 0x02, 73}, + {56, 0x03, 73}, + {3, 0x02, 74}, + {6, 0x02, 74}, + {10, 0x02, 74}, + {15, 0x02, 74}, + {24, 0x02, 74}, + {31, 0x02, 74}, + {41, 0x02, 74}, + {56, 0x03, 74}, + }, + /* 48 */ + { + {3, 0x02, 75}, + {6, 0x02, 75}, + {10, 0x02, 75}, + {15, 0x02, 75}, + {24, 0x02, 75}, + {31, 0x02, 75}, + {41, 0x02, 75}, + {56, 0x03, 75}, + {3, 0x02, 76}, + {6, 0x02, 76}, + {10, 0x02, 76}, + {15, 0x02, 76}, + {24, 0x02, 76}, + {31, 0x02, 76}, + {41, 0x02, 76}, + {56, 0x03, 76}, + }, + /* 49 */ + { + {1, 0x02, 77}, + {22, 0x03, 77}, + {1, 0x02, 78}, + {22, 0x03, 78}, + {1, 0x02, 79}, + {22, 0x03, 79}, + {1, 0x02, 80}, + {22, 0x03, 80}, + {1, 0x02, 81}, + {22, 0x03, 81}, + {1, 0x02, 82}, + {22, 0x03, 82}, + {1, 0x02, 83}, + {22, 0x03, 83}, + {1, 0x02, 84}, + {22, 0x03, 84}, + }, + /* 50 */ + { + {2, 0x02, 77}, + {9, 0x02, 77}, + {23, 0x02, 77}, + {40, 0x03, 77}, + {2, 0x02, 78}, + {9, 0x02, 78}, + {23, 0x02, 78}, + {40, 0x03, 78}, + {2, 0x02, 79}, + {9, 0x02, 79}, + {23, 0x02, 79}, + {40, 0x03, 79}, + {2, 0x02, 80}, + {9, 0x02, 80}, + {23, 0x02, 80}, + {40, 0x03, 80}, + }, + /* 51 */ + { + {3, 0x02, 77}, + {6, 0x02, 77}, + {10, 0x02, 77}, + {15, 0x02, 77}, + {24, 0x02, 77}, + {31, 0x02, 77}, + {41, 0x02, 77}, + {56, 0x03, 77}, + {3, 0x02, 78}, + {6, 0x02, 78}, + {10, 0x02, 78}, + {15, 0x02, 78}, + {24, 0x02, 78}, + {31, 0x02, 78}, + {41, 0x02, 78}, + {56, 0x03, 78}, + }, + /* 52 */ + { + {3, 0x02, 79}, + {6, 0x02, 79}, + {10, 0x02, 79}, + {15, 0x02, 79}, + {24, 0x02, 79}, + {31, 0x02, 79}, + {41, 0x02, 79}, + {56, 0x03, 79}, + {3, 0x02, 80}, + {6, 0x02, 80}, + {10, 0x02, 80}, + {15, 0x02, 80}, + {24, 0x02, 80}, + {31, 0x02, 80}, + {41, 0x02, 80}, + {56, 0x03, 80}, + }, + /* 53 */ + { + {2, 0x02, 81}, + {9, 0x02, 81}, + {23, 0x02, 81}, + {40, 0x03, 81}, + {2, 0x02, 82}, + {9, 0x02, 82}, + {23, 0x02, 82}, + {40, 0x03, 82}, + {2, 0x02, 83}, + {9, 0x02, 83}, + {23, 0x02, 83}, + {40, 0x03, 83}, + {2, 0x02, 84}, + {9, 0x02, 84}, + {23, 0x02, 84}, + {40, 0x03, 84}, + }, + /* 54 */ + { + {3, 0x02, 81}, + {6, 0x02, 81}, + {10, 0x02, 81}, + {15, 0x02, 81}, + {24, 0x02, 81}, + {31, 0x02, 81}, + {41, 0x02, 81}, + {56, 0x03, 81}, + {3, 0x02, 82}, + {6, 0x02, 82}, + {10, 0x02, 82}, + {15, 0x02, 82}, + {24, 0x02, 82}, + {31, 0x02, 82}, + {41, 0x02, 82}, + {56, 0x03, 82}, + }, + /* 55 */ + { + {3, 0x02, 83}, + {6, 0x02, 83}, + {10, 0x02, 83}, + {15, 0x02, 83}, + {24, 0x02, 83}, + {31, 0x02, 83}, + {41, 0x02, 83}, + {56, 0x03, 83}, + {3, 0x02, 84}, + {6, 0x02, 84}, + {10, 0x02, 84}, + {15, 0x02, 84}, + {24, 0x02, 84}, + {31, 0x02, 84}, + {41, 0x02, 84}, + {56, 0x03, 84}, + }, + /* 56 */ + { + {0, 0x03, 85}, + {0, 0x03, 86}, + {0, 0x03, 87}, + {0, 0x03, 89}, + {0, 0x03, 106}, + {0, 0x03, 107}, + {0, 0x03, 113}, + {0, 0x03, 118}, + {0, 0x03, 119}, + {0, 0x03, 120}, + {0, 0x03, 121}, + {0, 0x03, 122}, + {70, 0x00, 0}, + {71, 0x00, 0}, + {73, 0x00, 0}, + {74, 0x01, 0}, + }, + /* 57 */ + { + {1, 0x02, 85}, + {22, 0x03, 85}, + {1, 0x02, 86}, + {22, 0x03, 86}, + {1, 0x02, 87}, + {22, 0x03, 87}, + {1, 0x02, 89}, + {22, 0x03, 89}, + {1, 0x02, 106}, + {22, 0x03, 106}, + {1, 0x02, 107}, + {22, 0x03, 107}, + {1, 0x02, 113}, + {22, 0x03, 113}, + {1, 0x02, 118}, + {22, 0x03, 118}, + }, + /* 58 */ + { + {2, 0x02, 85}, + {9, 0x02, 85}, + {23, 0x02, 85}, + {40, 0x03, 85}, + {2, 0x02, 86}, + {9, 0x02, 86}, + {23, 0x02, 86}, + {40, 0x03, 86}, + {2, 0x02, 87}, + {9, 0x02, 87}, + {23, 0x02, 87}, + {40, 0x03, 87}, + {2, 0x02, 89}, + {9, 0x02, 89}, + {23, 0x02, 89}, + {40, 0x03, 89}, + }, + /* 59 */ + { + {3, 0x02, 85}, + {6, 0x02, 85}, + {10, 0x02, 85}, + {15, 0x02, 85}, + {24, 0x02, 85}, + {31, 0x02, 85}, + {41, 0x02, 85}, + {56, 0x03, 85}, + {3, 0x02, 86}, + {6, 0x02, 86}, + {10, 0x02, 86}, + {15, 0x02, 86}, + {24, 0x02, 86}, + {31, 0x02, 86}, + {41, 0x02, 86}, + {56, 0x03, 86}, + }, + /* 60 */ + { + {3, 0x02, 87}, + {6, 0x02, 87}, + {10, 0x02, 87}, + {15, 0x02, 87}, + {24, 0x02, 87}, + {31, 0x02, 87}, + {41, 0x02, 87}, + {56, 0x03, 87}, + {3, 0x02, 89}, + {6, 0x02, 89}, + {10, 0x02, 89}, + {15, 0x02, 89}, + {24, 0x02, 89}, + {31, 0x02, 89}, + {41, 0x02, 89}, + {56, 0x03, 89}, + }, + /* 61 */ + { + {2, 0x02, 106}, + {9, 0x02, 106}, + {23, 0x02, 106}, + {40, 0x03, 106}, + {2, 0x02, 107}, + {9, 0x02, 107}, + {23, 0x02, 107}, + {40, 0x03, 107}, + {2, 0x02, 113}, + {9, 0x02, 113}, + {23, 0x02, 113}, + {40, 0x03, 113}, + {2, 0x02, 118}, + {9, 0x02, 118}, + {23, 0x02, 118}, + {40, 0x03, 118}, + }, + /* 62 */ + { + {3, 0x02, 106}, + {6, 0x02, 106}, + {10, 0x02, 106}, + {15, 0x02, 106}, + {24, 0x02, 106}, + {31, 0x02, 106}, + {41, 0x02, 106}, + {56, 0x03, 106}, + {3, 0x02, 107}, + {6, 0x02, 107}, + {10, 0x02, 107}, + {15, 0x02, 107}, + {24, 0x02, 107}, + {31, 0x02, 107}, + {41, 0x02, 107}, + {56, 0x03, 107}, + }, + /* 63 */ + { + {3, 0x02, 113}, + {6, 0x02, 113}, + {10, 0x02, 113}, + {15, 0x02, 113}, + {24, 0x02, 113}, + {31, 0x02, 113}, + {41, 0x02, 113}, + {56, 0x03, 113}, + {3, 0x02, 118}, + {6, 0x02, 118}, + {10, 0x02, 118}, + {15, 0x02, 118}, + {24, 0x02, 118}, + {31, 0x02, 118}, + {41, 0x02, 118}, + {56, 0x03, 118}, + }, + /* 64 */ + { + {1, 0x02, 119}, + {22, 0x03, 119}, + {1, 0x02, 120}, + {22, 0x03, 120}, + {1, 0x02, 121}, + {22, 0x03, 121}, + {1, 0x02, 122}, + {22, 0x03, 122}, + {0, 0x03, 38}, + {0, 0x03, 42}, + {0, 0x03, 44}, + {0, 0x03, 59}, + {0, 0x03, 88}, + {0, 0x03, 90}, + {75, 0x00, 0}, + {78, 0x00, 0}, + }, + /* 65 */ + { + {2, 0x02, 119}, + {9, 0x02, 119}, + {23, 0x02, 119}, + {40, 0x03, 119}, + {2, 0x02, 120}, + {9, 0x02, 120}, + {23, 0x02, 120}, + {40, 0x03, 120}, + {2, 0x02, 121}, + {9, 0x02, 121}, + {23, 0x02, 121}, + {40, 0x03, 121}, + {2, 0x02, 122}, + {9, 0x02, 122}, + {23, 0x02, 122}, + {40, 0x03, 122}, + }, + /* 66 */ + { + {3, 0x02, 119}, + {6, 0x02, 119}, + {10, 0x02, 119}, + {15, 0x02, 119}, + {24, 0x02, 119}, + {31, 0x02, 119}, + {41, 0x02, 119}, + {56, 0x03, 119}, + {3, 0x02, 120}, + {6, 0x02, 120}, + {10, 0x02, 120}, + {15, 0x02, 120}, + {24, 0x02, 120}, + {31, 0x02, 120}, + {41, 0x02, 120}, + {56, 0x03, 120}, + }, + /* 67 */ + { + {3, 0x02, 121}, + {6, 0x02, 121}, + {10, 0x02, 121}, + {15, 0x02, 121}, + {24, 0x02, 121}, + {31, 0x02, 121}, + {41, 0x02, 121}, + {56, 0x03, 121}, + {3, 0x02, 122}, + {6, 0x02, 122}, + {10, 0x02, 122}, + {15, 0x02, 122}, + {24, 0x02, 122}, + {31, 0x02, 122}, + {41, 0x02, 122}, + {56, 0x03, 122}, + }, + /* 68 */ + { + {1, 0x02, 38}, + {22, 0x03, 38}, + {1, 0x02, 42}, + {22, 0x03, 42}, + {1, 0x02, 44}, + {22, 0x03, 44}, + {1, 0x02, 59}, + {22, 0x03, 59}, + {1, 0x02, 88}, + {22, 0x03, 88}, + {1, 0x02, 90}, + {22, 0x03, 90}, + {76, 0x00, 0}, + {77, 0x00, 0}, + {79, 0x00, 0}, + {81, 0x00, 0}, + }, + /* 69 */ + { + {2, 0x02, 38}, + {9, 0x02, 38}, + {23, 0x02, 38}, + {40, 0x03, 38}, + {2, 0x02, 42}, + {9, 0x02, 42}, + {23, 0x02, 42}, + {40, 0x03, 42}, + {2, 0x02, 44}, + {9, 0x02, 44}, + {23, 0x02, 44}, + {40, 0x03, 44}, + {2, 0x02, 59}, + {9, 0x02, 59}, + {23, 0x02, 59}, + {40, 0x03, 59}, + }, + /* 70 */ + { + {3, 0x02, 38}, + {6, 0x02, 38}, + {10, 0x02, 38}, + {15, 0x02, 38}, + {24, 0x02, 38}, + {31, 0x02, 38}, + {41, 0x02, 38}, + {56, 0x03, 38}, + {3, 0x02, 42}, + {6, 0x02, 42}, + {10, 0x02, 42}, + {15, 0x02, 42}, + {24, 0x02, 42}, + {31, 0x02, 42}, + {41, 0x02, 42}, + {56, 0x03, 42}, + }, + /* 71 */ + { + {3, 0x02, 44}, + {6, 0x02, 44}, + {10, 0x02, 44}, + {15, 0x02, 44}, + {24, 0x02, 44}, + {31, 0x02, 44}, + {41, 0x02, 44}, + {56, 0x03, 44}, + {3, 0x02, 59}, + {6, 0x02, 59}, + {10, 0x02, 59}, + {15, 0x02, 59}, + {24, 0x02, 59}, + {31, 0x02, 59}, + {41, 0x02, 59}, + {56, 0x03, 59}, + }, + /* 72 */ + { + {2, 0x02, 88}, + {9, 0x02, 88}, + {23, 0x02, 88}, + {40, 0x03, 88}, + {2, 0x02, 90}, + {9, 0x02, 90}, + {23, 0x02, 90}, + {40, 0x03, 90}, + {0, 0x03, 33}, + {0, 0x03, 34}, + {0, 0x03, 40}, + {0, 0x03, 41}, + {0, 0x03, 63}, + {80, 0x00, 0}, + {82, 0x00, 0}, + {84, 0x00, 0}, + }, + /* 73 */ + { + {3, 0x02, 88}, + {6, 0x02, 88}, + {10, 0x02, 88}, + {15, 0x02, 88}, + {24, 0x02, 88}, + {31, 0x02, 88}, + {41, 0x02, 88}, + {56, 0x03, 88}, + {3, 0x02, 90}, + {6, 0x02, 90}, + {10, 0x02, 90}, + {15, 0x02, 90}, + {24, 0x02, 90}, + {31, 0x02, 90}, + {41, 0x02, 90}, + {56, 0x03, 90}, + }, + /* 74 */ + { + {1, 0x02, 33}, + {22, 0x03, 33}, + {1, 0x02, 34}, + {22, 0x03, 34}, + {1, 0x02, 40}, + {22, 0x03, 40}, + {1, 0x02, 41}, + {22, 0x03, 41}, + {1, 0x02, 63}, + {22, 0x03, 63}, + {0, 0x03, 39}, + {0, 0x03, 43}, + {0, 0x03, 124}, + {83, 0x00, 0}, + {85, 0x00, 0}, + {88, 0x00, 0}, + }, + /* 75 */ + { + {2, 0x02, 33}, + {9, 0x02, 33}, + {23, 0x02, 33}, + {40, 0x03, 33}, + {2, 0x02, 34}, + {9, 0x02, 34}, + {23, 0x02, 34}, + {40, 0x03, 34}, + {2, 0x02, 40}, + {9, 0x02, 40}, + {23, 0x02, 40}, + {40, 0x03, 40}, + {2, 0x02, 41}, + {9, 0x02, 41}, + {23, 0x02, 41}, + {40, 0x03, 41}, + }, + /* 76 */ + { + {3, 0x02, 33}, + {6, 0x02, 33}, + {10, 0x02, 33}, + {15, 0x02, 33}, + {24, 0x02, 33}, + {31, 0x02, 33}, + {41, 0x02, 33}, + {56, 0x03, 33}, + {3, 0x02, 34}, + {6, 0x02, 34}, + {10, 0x02, 34}, + {15, 0x02, 34}, + {24, 0x02, 34}, + {31, 0x02, 34}, + {41, 0x02, 34}, + {56, 0x03, 34}, + }, + /* 77 */ + { + {3, 0x02, 40}, + {6, 0x02, 40}, + {10, 0x02, 40}, + {15, 0x02, 40}, + {24, 0x02, 40}, + {31, 0x02, 40}, + {41, 0x02, 40}, + {56, 0x03, 40}, + {3, 0x02, 41}, + {6, 0x02, 41}, + {10, 0x02, 41}, + {15, 0x02, 41}, + {24, 0x02, 41}, + {31, 0x02, 41}, + {41, 0x02, 41}, + {56, 0x03, 41}, + }, + /* 78 */ + { + {2, 0x02, 63}, + {9, 0x02, 63}, + {23, 0x02, 63}, + {40, 0x03, 63}, + {1, 0x02, 39}, + {22, 0x03, 39}, + {1, 0x02, 43}, + {22, 0x03, 43}, + {1, 0x02, 124}, + {22, 0x03, 124}, + {0, 0x03, 35}, + {0, 0x03, 62}, + {86, 0x00, 0}, + {87, 0x00, 0}, + {89, 0x00, 0}, + {90, 0x00, 0}, + }, + /* 79 */ + { + {3, 0x02, 63}, + {6, 0x02, 63}, + {10, 0x02, 63}, + {15, 0x02, 63}, + {24, 0x02, 63}, + {31, 0x02, 63}, + {41, 0x02, 63}, + {56, 0x03, 63}, + {2, 0x02, 39}, + {9, 0x02, 39}, + {23, 0x02, 39}, + {40, 0x03, 39}, + {2, 0x02, 43}, + {9, 0x02, 43}, + {23, 0x02, 43}, + {40, 0x03, 43}, + }, + /* 80 */ + { + {3, 0x02, 39}, + {6, 0x02, 39}, + {10, 0x02, 39}, + {15, 0x02, 39}, + {24, 0x02, 39}, + {31, 0x02, 39}, + {41, 0x02, 39}, + {56, 0x03, 39}, + {3, 0x02, 43}, + {6, 0x02, 43}, + {10, 0x02, 43}, + {15, 0x02, 43}, + {24, 0x02, 43}, + {31, 0x02, 43}, + {41, 0x02, 43}, + {56, 0x03, 43}, + }, + /* 81 */ + { + {2, 0x02, 124}, + {9, 0x02, 124}, + {23, 0x02, 124}, + {40, 0x03, 124}, + {1, 0x02, 35}, + {22, 0x03, 35}, + {1, 0x02, 62}, + {22, 0x03, 62}, + {0, 0x03, 0}, + {0, 0x03, 36}, + {0, 0x03, 64}, + {0, 0x03, 91}, + {0, 0x03, 93}, + {0, 0x03, 126}, + {91, 0x00, 0}, + {92, 0x00, 0}, + }, + /* 82 */ + { + {3, 0x02, 124}, + {6, 0x02, 124}, + {10, 0x02, 124}, + {15, 0x02, 124}, + {24, 0x02, 124}, + {31, 0x02, 124}, + {41, 0x02, 124}, + {56, 0x03, 124}, + {2, 0x02, 35}, + {9, 0x02, 35}, + {23, 0x02, 35}, + {40, 0x03, 35}, + {2, 0x02, 62}, + {9, 0x02, 62}, + {23, 0x02, 62}, + {40, 0x03, 62}, + }, + /* 83 */ + { + {3, 0x02, 35}, + {6, 0x02, 35}, + {10, 0x02, 35}, + {15, 0x02, 35}, + {24, 0x02, 35}, + {31, 0x02, 35}, + {41, 0x02, 35}, + {56, 0x03, 35}, + {3, 0x02, 62}, + {6, 0x02, 62}, + {10, 0x02, 62}, + {15, 0x02, 62}, + {24, 0x02, 62}, + {31, 0x02, 62}, + {41, 0x02, 62}, + {56, 0x03, 62}, + }, + /* 84 */ + { + {1, 0x02, 0}, + {22, 0x03, 0}, + {1, 0x02, 36}, + {22, 0x03, 36}, + {1, 0x02, 64}, + {22, 0x03, 64}, + {1, 0x02, 91}, + {22, 0x03, 91}, + {1, 0x02, 93}, + {22, 0x03, 93}, + {1, 0x02, 126}, + {22, 0x03, 126}, + {0, 0x03, 94}, + {0, 0x03, 125}, + {93, 0x00, 0}, + {94, 0x00, 0}, + }, + /* 85 */ + { + {2, 0x02, 0}, + {9, 0x02, 0}, + {23, 0x02, 0}, + {40, 0x03, 0}, + {2, 0x02, 36}, + {9, 0x02, 36}, + {23, 0x02, 36}, + {40, 0x03, 36}, + {2, 0x02, 64}, + {9, 0x02, 64}, + {23, 0x02, 64}, + {40, 0x03, 64}, + {2, 0x02, 91}, + {9, 0x02, 91}, + {23, 0x02, 91}, + {40, 0x03, 91}, + }, + /* 86 */ + { + {3, 0x02, 0}, + {6, 0x02, 0}, + {10, 0x02, 0}, + {15, 0x02, 0}, + {24, 0x02, 0}, + {31, 0x02, 0}, + {41, 0x02, 0}, + {56, 0x03, 0}, + {3, 0x02, 36}, + {6, 0x02, 36}, + {10, 0x02, 36}, + {15, 0x02, 36}, + {24, 0x02, 36}, + {31, 0x02, 36}, + {41, 0x02, 36}, + {56, 0x03, 36}, + }, + /* 87 */ + { + {3, 0x02, 64}, + {6, 0x02, 64}, + {10, 0x02, 64}, + {15, 0x02, 64}, + {24, 0x02, 64}, + {31, 0x02, 64}, + {41, 0x02, 64}, + {56, 0x03, 64}, + {3, 0x02, 91}, + {6, 0x02, 91}, + {10, 0x02, 91}, + {15, 0x02, 91}, + {24, 0x02, 91}, + {31, 0x02, 91}, + {41, 0x02, 91}, + {56, 0x03, 91}, + }, + /* 88 */ + { + {2, 0x02, 93}, + {9, 0x02, 93}, + {23, 0x02, 93}, + {40, 0x03, 93}, + {2, 0x02, 126}, + {9, 0x02, 126}, + {23, 0x02, 126}, + {40, 0x03, 126}, + {1, 0x02, 94}, + {22, 0x03, 94}, + {1, 0x02, 125}, + {22, 0x03, 125}, + {0, 0x03, 60}, + {0, 0x03, 96}, + {0, 0x03, 123}, + {95, 0x00, 0}, + }, + /* 89 */ + { + {3, 0x02, 93}, + {6, 0x02, 93}, + {10, 0x02, 93}, + {15, 0x02, 93}, + {24, 0x02, 93}, + {31, 0x02, 93}, + {41, 0x02, 93}, + {56, 0x03, 93}, + {3, 0x02, 126}, + {6, 0x02, 126}, + {10, 0x02, 126}, + {15, 0x02, 126}, + {24, 0x02, 126}, + {31, 0x02, 126}, + {41, 0x02, 126}, + {56, 0x03, 126}, + }, + /* 90 */ + { + {2, 0x02, 94}, + {9, 0x02, 94}, + {23, 0x02, 94}, + {40, 0x03, 94}, + {2, 0x02, 125}, + {9, 0x02, 125}, + {23, 0x02, 125}, + {40, 0x03, 125}, + {1, 0x02, 60}, + {22, 0x03, 60}, + {1, 0x02, 96}, + {22, 0x03, 96}, + {1, 0x02, 123}, + {22, 0x03, 123}, + {96, 0x00, 0}, + {110, 0x00, 0}, + }, + /* 91 */ + { + {3, 0x02, 94}, + {6, 0x02, 94}, + {10, 0x02, 94}, + {15, 0x02, 94}, + {24, 0x02, 94}, + {31, 0x02, 94}, + {41, 0x02, 94}, + {56, 0x03, 94}, + {3, 0x02, 125}, + {6, 0x02, 125}, + {10, 0x02, 125}, + {15, 0x02, 125}, + {24, 0x02, 125}, + {31, 0x02, 125}, + {41, 0x02, 125}, + {56, 0x03, 125}, + }, + /* 92 */ + { + {2, 0x02, 60}, + {9, 0x02, 60}, + {23, 0x02, 60}, + {40, 0x03, 60}, + {2, 0x02, 96}, + {9, 0x02, 96}, + {23, 0x02, 96}, + {40, 0x03, 96}, + {2, 0x02, 123}, + {9, 0x02, 123}, + {23, 0x02, 123}, + {40, 0x03, 123}, + {97, 0x00, 0}, + {101, 0x00, 0}, + {111, 0x00, 0}, + {133, 0x00, 0}, + }, + /* 93 */ + { + {3, 0x02, 60}, + {6, 0x02, 60}, + {10, 0x02, 60}, + {15, 0x02, 60}, + {24, 0x02, 60}, + {31, 0x02, 60}, + {41, 0x02, 60}, + {56, 0x03, 60}, + {3, 0x02, 96}, + {6, 0x02, 96}, + {10, 0x02, 96}, + {15, 0x02, 96}, + {24, 0x02, 96}, + {31, 0x02, 96}, + {41, 0x02, 96}, + {56, 0x03, 96}, + }, + /* 94 */ + { + {3, 0x02, 123}, + {6, 0x02, 123}, + {10, 0x02, 123}, + {15, 0x02, 123}, + {24, 0x02, 123}, + {31, 0x02, 123}, + {41, 0x02, 123}, + {56, 0x03, 123}, + {98, 0x00, 0}, + {99, 0x00, 0}, + {102, 0x00, 0}, + {105, 0x00, 0}, + {112, 0x00, 0}, + {119, 0x00, 0}, + {134, 0x00, 0}, + {153, 0x00, 0}, + }, + /* 95 */ + { + {0, 0x03, 92}, + {0, 0x03, 195}, + {0, 0x03, 208}, + {100, 0x00, 0}, + {103, 0x00, 0}, + {104, 0x00, 0}, + {106, 0x00, 0}, + {107, 0x00, 0}, + {113, 0x00, 0}, + {116, 0x00, 0}, + {120, 0x00, 0}, + {126, 0x00, 0}, + {135, 0x00, 0}, + {142, 0x00, 0}, + {154, 0x00, 0}, + {169, 0x00, 0}, + }, + /* 96 */ + { + {1, 0x02, 92}, + {22, 0x03, 92}, + {1, 0x02, 195}, + {22, 0x03, 195}, + {1, 0x02, 208}, + {22, 0x03, 208}, + {0, 0x03, 128}, + {0, 0x03, 130}, + {0, 0x03, 131}, + {0, 0x03, 162}, + {0, 0x03, 184}, + {0, 0x03, 194}, + {0, 0x03, 224}, + {0, 0x03, 226}, + {108, 0x00, 0}, + {109, 0x00, 0}, + }, + /* 97 */ + { + {2, 0x02, 92}, + {9, 0x02, 92}, + {23, 0x02, 92}, + {40, 0x03, 92}, + {2, 0x02, 195}, + {9, 0x02, 195}, + {23, 0x02, 195}, + {40, 0x03, 195}, + {2, 0x02, 208}, + {9, 0x02, 208}, + {23, 0x02, 208}, + {40, 0x03, 208}, + {1, 0x02, 128}, + {22, 0x03, 128}, + {1, 0x02, 130}, + {22, 0x03, 130}, + }, + /* 98 */ + { + {3, 0x02, 92}, + {6, 0x02, 92}, + {10, 0x02, 92}, + {15, 0x02, 92}, + {24, 0x02, 92}, + {31, 0x02, 92}, + {41, 0x02, 92}, + {56, 0x03, 92}, + {3, 0x02, 195}, + {6, 0x02, 195}, + {10, 0x02, 195}, + {15, 0x02, 195}, + {24, 0x02, 195}, + {31, 0x02, 195}, + {41, 0x02, 195}, + {56, 0x03, 195}, + }, + /* 99 */ + { + {3, 0x02, 208}, + {6, 0x02, 208}, + {10, 0x02, 208}, + {15, 0x02, 208}, + {24, 0x02, 208}, + {31, 0x02, 208}, + {41, 0x02, 208}, + {56, 0x03, 208}, + {2, 0x02, 128}, + {9, 0x02, 128}, + {23, 0x02, 128}, + {40, 0x03, 128}, + {2, 0x02, 130}, + {9, 0x02, 130}, + {23, 0x02, 130}, + {40, 0x03, 130}, + }, + /* 100 */ + { + {3, 0x02, 128}, + {6, 0x02, 128}, + {10, 0x02, 128}, + {15, 0x02, 128}, + {24, 0x02, 128}, + {31, 0x02, 128}, + {41, 0x02, 128}, + {56, 0x03, 128}, + {3, 0x02, 130}, + {6, 0x02, 130}, + {10, 0x02, 130}, + {15, 0x02, 130}, + {24, 0x02, 130}, + {31, 0x02, 130}, + {41, 0x02, 130}, + {56, 0x03, 130}, + }, + /* 101 */ + { + {1, 0x02, 131}, + {22, 0x03, 131}, + {1, 0x02, 162}, + {22, 0x03, 162}, + {1, 0x02, 184}, + {22, 0x03, 184}, + {1, 0x02, 194}, + {22, 0x03, 194}, + {1, 0x02, 224}, + {22, 0x03, 224}, + {1, 0x02, 226}, + {22, 0x03, 226}, + {0, 0x03, 153}, + {0, 0x03, 161}, + {0, 0x03, 167}, + {0, 0x03, 172}, + }, + /* 102 */ + { + {2, 0x02, 131}, + {9, 0x02, 131}, + {23, 0x02, 131}, + {40, 0x03, 131}, + {2, 0x02, 162}, + {9, 0x02, 162}, + {23, 0x02, 162}, + {40, 0x03, 162}, + {2, 0x02, 184}, + {9, 0x02, 184}, + {23, 0x02, 184}, + {40, 0x03, 184}, + {2, 0x02, 194}, + {9, 0x02, 194}, + {23, 0x02, 194}, + {40, 0x03, 194}, + }, + /* 103 */ + { + {3, 0x02, 131}, + {6, 0x02, 131}, + {10, 0x02, 131}, + {15, 0x02, 131}, + {24, 0x02, 131}, + {31, 0x02, 131}, + {41, 0x02, 131}, + {56, 0x03, 131}, + {3, 0x02, 162}, + {6, 0x02, 162}, + {10, 0x02, 162}, + {15, 0x02, 162}, + {24, 0x02, 162}, + {31, 0x02, 162}, + {41, 0x02, 162}, + {56, 0x03, 162}, + }, + /* 104 */ + { + {3, 0x02, 184}, + {6, 0x02, 184}, + {10, 0x02, 184}, + {15, 0x02, 184}, + {24, 0x02, 184}, + {31, 0x02, 184}, + {41, 0x02, 184}, + {56, 0x03, 184}, + {3, 0x02, 194}, + {6, 0x02, 194}, + {10, 0x02, 194}, + {15, 0x02, 194}, + {24, 0x02, 194}, + {31, 0x02, 194}, + {41, 0x02, 194}, + {56, 0x03, 194}, + }, + /* 105 */ + { + {2, 0x02, 224}, + {9, 0x02, 224}, + {23, 0x02, 224}, + {40, 0x03, 224}, + {2, 0x02, 226}, + {9, 0x02, 226}, + {23, 0x02, 226}, + {40, 0x03, 226}, + {1, 0x02, 153}, + {22, 0x03, 153}, + {1, 0x02, 161}, + {22, 0x03, 161}, + {1, 0x02, 167}, + {22, 0x03, 167}, + {1, 0x02, 172}, + {22, 0x03, 172}, + }, + /* 106 */ + { + {3, 0x02, 224}, + {6, 0x02, 224}, + {10, 0x02, 224}, + {15, 0x02, 224}, + {24, 0x02, 224}, + {31, 0x02, 224}, + {41, 0x02, 224}, + {56, 0x03, 224}, + {3, 0x02, 226}, + {6, 0x02, 226}, + {10, 0x02, 226}, + {15, 0x02, 226}, + {24, 0x02, 226}, + {31, 0x02, 226}, + {41, 0x02, 226}, + {56, 0x03, 226}, + }, + /* 107 */ + { + {2, 0x02, 153}, + {9, 0x02, 153}, + {23, 0x02, 153}, + {40, 0x03, 153}, + {2, 0x02, 161}, + {9, 0x02, 161}, + {23, 0x02, 161}, + {40, 0x03, 161}, + {2, 0x02, 167}, + {9, 0x02, 167}, + {23, 0x02, 167}, + {40, 0x03, 167}, + {2, 0x02, 172}, + {9, 0x02, 172}, + {23, 0x02, 172}, + {40, 0x03, 172}, + }, + /* 108 */ + { + {3, 0x02, 153}, + {6, 0x02, 153}, + {10, 0x02, 153}, + {15, 0x02, 153}, + {24, 0x02, 153}, + {31, 0x02, 153}, + {41, 0x02, 153}, + {56, 0x03, 153}, + {3, 0x02, 161}, + {6, 0x02, 161}, + {10, 0x02, 161}, + {15, 0x02, 161}, + {24, 0x02, 161}, + {31, 0x02, 161}, + {41, 0x02, 161}, + {56, 0x03, 161}, + }, + /* 109 */ + { + {3, 0x02, 167}, + {6, 0x02, 167}, + {10, 0x02, 167}, + {15, 0x02, 167}, + {24, 0x02, 167}, + {31, 0x02, 167}, + {41, 0x02, 167}, + {56, 0x03, 167}, + {3, 0x02, 172}, + {6, 0x02, 172}, + {10, 0x02, 172}, + {15, 0x02, 172}, + {24, 0x02, 172}, + {31, 0x02, 172}, + {41, 0x02, 172}, + {56, 0x03, 172}, + }, + /* 110 */ + { + {114, 0x00, 0}, + {115, 0x00, 0}, + {117, 0x00, 0}, + {118, 0x00, 0}, + {121, 0x00, 0}, + {123, 0x00, 0}, + {127, 0x00, 0}, + {130, 0x00, 0}, + {136, 0x00, 0}, + {139, 0x00, 0}, + {143, 0x00, 0}, + {146, 0x00, 0}, + {155, 0x00, 0}, + {162, 0x00, 0}, + {170, 0x00, 0}, + {180, 0x00, 0}, + }, + /* 111 */ + { + {0, 0x03, 176}, + {0, 0x03, 177}, + {0, 0x03, 179}, + {0, 0x03, 209}, + {0, 0x03, 216}, + {0, 0x03, 217}, + {0, 0x03, 227}, + {0, 0x03, 229}, + {0, 0x03, 230}, + {122, 0x00, 0}, + {124, 0x00, 0}, + {125, 0x00, 0}, + {128, 0x00, 0}, + {129, 0x00, 0}, + {131, 0x00, 0}, + {132, 0x00, 0}, + }, + /* 112 */ + { + {1, 0x02, 176}, + {22, 0x03, 176}, + {1, 0x02, 177}, + {22, 0x03, 177}, + {1, 0x02, 179}, + {22, 0x03, 179}, + {1, 0x02, 209}, + {22, 0x03, 209}, + {1, 0x02, 216}, + {22, 0x03, 216}, + {1, 0x02, 217}, + {22, 0x03, 217}, + {1, 0x02, 227}, + {22, 0x03, 227}, + {1, 0x02, 229}, + {22, 0x03, 229}, + }, + /* 113 */ + { + {2, 0x02, 176}, + {9, 0x02, 176}, + {23, 0x02, 176}, + {40, 0x03, 176}, + {2, 0x02, 177}, + {9, 0x02, 177}, + {23, 0x02, 177}, + {40, 0x03, 177}, + {2, 0x02, 179}, + {9, 0x02, 179}, + {23, 0x02, 179}, + {40, 0x03, 179}, + {2, 0x02, 209}, + {9, 0x02, 209}, + {23, 0x02, 209}, + {40, 0x03, 209}, + }, + /* 114 */ + { + {3, 0x02, 176}, + {6, 0x02, 176}, + {10, 0x02, 176}, + {15, 0x02, 176}, + {24, 0x02, 176}, + {31, 0x02, 176}, + {41, 0x02, 176}, + {56, 0x03, 176}, + {3, 0x02, 177}, + {6, 0x02, 177}, + {10, 0x02, 177}, + {15, 0x02, 177}, + {24, 0x02, 177}, + {31, 0x02, 177}, + {41, 0x02, 177}, + {56, 0x03, 177}, + }, + /* 115 */ + { + {3, 0x02, 179}, + {6, 0x02, 179}, + {10, 0x02, 179}, + {15, 0x02, 179}, + {24, 0x02, 179}, + {31, 0x02, 179}, + {41, 0x02, 179}, + {56, 0x03, 179}, + {3, 0x02, 209}, + {6, 0x02, 209}, + {10, 0x02, 209}, + {15, 0x02, 209}, + {24, 0x02, 209}, + {31, 0x02, 209}, + {41, 0x02, 209}, + {56, 0x03, 209}, + }, + /* 116 */ + { + {2, 0x02, 216}, + {9, 0x02, 216}, + {23, 0x02, 216}, + {40, 0x03, 216}, + {2, 0x02, 217}, + {9, 0x02, 217}, + {23, 0x02, 217}, + {40, 0x03, 217}, + {2, 0x02, 227}, + {9, 0x02, 227}, + {23, 0x02, 227}, + {40, 0x03, 227}, + {2, 0x02, 229}, + {9, 0x02, 229}, + {23, 0x02, 229}, + {40, 0x03, 229}, + }, + /* 117 */ + { + {3, 0x02, 216}, + {6, 0x02, 216}, + {10, 0x02, 216}, + {15, 0x02, 216}, + {24, 0x02, 216}, + {31, 0x02, 216}, + {41, 0x02, 216}, + {56, 0x03, 216}, + {3, 0x02, 217}, + {6, 0x02, 217}, + {10, 0x02, 217}, + {15, 0x02, 217}, + {24, 0x02, 217}, + {31, 0x02, 217}, + {41, 0x02, 217}, + {56, 0x03, 217}, + }, + /* 118 */ + { + {3, 0x02, 227}, + {6, 0x02, 227}, + {10, 0x02, 227}, + {15, 0x02, 227}, + {24, 0x02, 227}, + {31, 0x02, 227}, + {41, 0x02, 227}, + {56, 0x03, 227}, + {3, 0x02, 229}, + {6, 0x02, 229}, + {10, 0x02, 229}, + {15, 0x02, 229}, + {24, 0x02, 229}, + {31, 0x02, 229}, + {41, 0x02, 229}, + {56, 0x03, 229}, + }, + /* 119 */ + { + {1, 0x02, 230}, + {22, 0x03, 230}, + {0, 0x03, 129}, + {0, 0x03, 132}, + {0, 0x03, 133}, + {0, 0x03, 134}, + {0, 0x03, 136}, + {0, 0x03, 146}, + {0, 0x03, 154}, + {0, 0x03, 156}, + {0, 0x03, 160}, + {0, 0x03, 163}, + {0, 0x03, 164}, + {0, 0x03, 169}, + {0, 0x03, 170}, + {0, 0x03, 173}, + }, + /* 120 */ + { + {2, 0x02, 230}, + {9, 0x02, 230}, + {23, 0x02, 230}, + {40, 0x03, 230}, + {1, 0x02, 129}, + {22, 0x03, 129}, + {1, 0x02, 132}, + {22, 0x03, 132}, + {1, 0x02, 133}, + {22, 0x03, 133}, + {1, 0x02, 134}, + {22, 0x03, 134}, + {1, 0x02, 136}, + {22, 0x03, 136}, + {1, 0x02, 146}, + {22, 0x03, 146}, + }, + /* 121 */ + { + {3, 0x02, 230}, + {6, 0x02, 230}, + {10, 0x02, 230}, + {15, 0x02, 230}, + {24, 0x02, 230}, + {31, 0x02, 230}, + {41, 0x02, 230}, + {56, 0x03, 230}, + {2, 0x02, 129}, + {9, 0x02, 129}, + {23, 0x02, 129}, + {40, 0x03, 129}, + {2, 0x02, 132}, + {9, 0x02, 132}, + {23, 0x02, 132}, + {40, 0x03, 132}, + }, + /* 122 */ + { + {3, 0x02, 129}, + {6, 0x02, 129}, + {10, 0x02, 129}, + {15, 0x02, 129}, + {24, 0x02, 129}, + {31, 0x02, 129}, + {41, 0x02, 129}, + {56, 0x03, 129}, + {3, 0x02, 132}, + {6, 0x02, 132}, + {10, 0x02, 132}, + {15, 0x02, 132}, + {24, 0x02, 132}, + {31, 0x02, 132}, + {41, 0x02, 132}, + {56, 0x03, 132}, + }, + /* 123 */ + { + {2, 0x02, 133}, + {9, 0x02, 133}, + {23, 0x02, 133}, + {40, 0x03, 133}, + {2, 0x02, 134}, + {9, 0x02, 134}, + {23, 0x02, 134}, + {40, 0x03, 134}, + {2, 0x02, 136}, + {9, 0x02, 136}, + {23, 0x02, 136}, + {40, 0x03, 136}, + {2, 0x02, 146}, + {9, 0x02, 146}, + {23, 0x02, 146}, + {40, 0x03, 146}, + }, + /* 124 */ + { + {3, 0x02, 133}, + {6, 0x02, 133}, + {10, 0x02, 133}, + {15, 0x02, 133}, + {24, 0x02, 133}, + {31, 0x02, 133}, + {41, 0x02, 133}, + {56, 0x03, 133}, + {3, 0x02, 134}, + {6, 0x02, 134}, + {10, 0x02, 134}, + {15, 0x02, 134}, + {24, 0x02, 134}, + {31, 0x02, 134}, + {41, 0x02, 134}, + {56, 0x03, 134}, + }, + /* 125 */ + { + {3, 0x02, 136}, + {6, 0x02, 136}, + {10, 0x02, 136}, + {15, 0x02, 136}, + {24, 0x02, 136}, + {31, 0x02, 136}, + {41, 0x02, 136}, + {56, 0x03, 136}, + {3, 0x02, 146}, + {6, 0x02, 146}, + {10, 0x02, 146}, + {15, 0x02, 146}, + {24, 0x02, 146}, + {31, 0x02, 146}, + {41, 0x02, 146}, + {56, 0x03, 146}, + }, + /* 126 */ + { + {1, 0x02, 154}, + {22, 0x03, 154}, + {1, 0x02, 156}, + {22, 0x03, 156}, + {1, 0x02, 160}, + {22, 0x03, 160}, + {1, 0x02, 163}, + {22, 0x03, 163}, + {1, 0x02, 164}, + {22, 0x03, 164}, + {1, 0x02, 169}, + {22, 0x03, 169}, + {1, 0x02, 170}, + {22, 0x03, 170}, + {1, 0x02, 173}, + {22, 0x03, 173}, + }, + /* 127 */ + { + {2, 0x02, 154}, + {9, 0x02, 154}, + {23, 0x02, 154}, + {40, 0x03, 154}, + {2, 0x02, 156}, + {9, 0x02, 156}, + {23, 0x02, 156}, + {40, 0x03, 156}, + {2, 0x02, 160}, + {9, 0x02, 160}, + {23, 0x02, 160}, + {40, 0x03, 160}, + {2, 0x02, 163}, + {9, 0x02, 163}, + {23, 0x02, 163}, + {40, 0x03, 163}, + }, + /* 128 */ + { + {3, 0x02, 154}, + {6, 0x02, 154}, + {10, 0x02, 154}, + {15, 0x02, 154}, + {24, 0x02, 154}, + {31, 0x02, 154}, + {41, 0x02, 154}, + {56, 0x03, 154}, + {3, 0x02, 156}, + {6, 0x02, 156}, + {10, 0x02, 156}, + {15, 0x02, 156}, + {24, 0x02, 156}, + {31, 0x02, 156}, + {41, 0x02, 156}, + {56, 0x03, 156}, + }, + /* 129 */ + { + {3, 0x02, 160}, + {6, 0x02, 160}, + {10, 0x02, 160}, + {15, 0x02, 160}, + {24, 0x02, 160}, + {31, 0x02, 160}, + {41, 0x02, 160}, + {56, 0x03, 160}, + {3, 0x02, 163}, + {6, 0x02, 163}, + {10, 0x02, 163}, + {15, 0x02, 163}, + {24, 0x02, 163}, + {31, 0x02, 163}, + {41, 0x02, 163}, + {56, 0x03, 163}, + }, + /* 130 */ + { + {2, 0x02, 164}, + {9, 0x02, 164}, + {23, 0x02, 164}, + {40, 0x03, 164}, + {2, 0x02, 169}, + {9, 0x02, 169}, + {23, 0x02, 169}, + {40, 0x03, 169}, + {2, 0x02, 170}, + {9, 0x02, 170}, + {23, 0x02, 170}, + {40, 0x03, 170}, + {2, 0x02, 173}, + {9, 0x02, 173}, + {23, 0x02, 173}, + {40, 0x03, 173}, + }, + /* 131 */ + { + {3, 0x02, 164}, + {6, 0x02, 164}, + {10, 0x02, 164}, + {15, 0x02, 164}, + {24, 0x02, 164}, + {31, 0x02, 164}, + {41, 0x02, 164}, + {56, 0x03, 164}, + {3, 0x02, 169}, + {6, 0x02, 169}, + {10, 0x02, 169}, + {15, 0x02, 169}, + {24, 0x02, 169}, + {31, 0x02, 169}, + {41, 0x02, 169}, + {56, 0x03, 169}, + }, + /* 132 */ + { + {3, 0x02, 170}, + {6, 0x02, 170}, + {10, 0x02, 170}, + {15, 0x02, 170}, + {24, 0x02, 170}, + {31, 0x02, 170}, + {41, 0x02, 170}, + {56, 0x03, 170}, + {3, 0x02, 173}, + {6, 0x02, 173}, + {10, 0x02, 173}, + {15, 0x02, 173}, + {24, 0x02, 173}, + {31, 0x02, 173}, + {41, 0x02, 173}, + {56, 0x03, 173}, + }, + /* 133 */ + { + {137, 0x00, 0}, + {138, 0x00, 0}, + {140, 0x00, 0}, + {141, 0x00, 0}, + {144, 0x00, 0}, + {145, 0x00, 0}, + {147, 0x00, 0}, + {150, 0x00, 0}, + {156, 0x00, 0}, + {159, 0x00, 0}, + {163, 0x00, 0}, + {166, 0x00, 0}, + {171, 0x00, 0}, + {174, 0x00, 0}, + {181, 0x00, 0}, + {190, 0x00, 0}, + }, + /* 134 */ + { + {0, 0x03, 178}, + {0, 0x03, 181}, + {0, 0x03, 185}, + {0, 0x03, 186}, + {0, 0x03, 187}, + {0, 0x03, 189}, + {0, 0x03, 190}, + {0, 0x03, 196}, + {0, 0x03, 198}, + {0, 0x03, 228}, + {0, 0x03, 232}, + {0, 0x03, 233}, + {148, 0x00, 0}, + {149, 0x00, 0}, + {151, 0x00, 0}, + {152, 0x00, 0}, + }, + /* 135 */ + { + {1, 0x02, 178}, + {22, 0x03, 178}, + {1, 0x02, 181}, + {22, 0x03, 181}, + {1, 0x02, 185}, + {22, 0x03, 185}, + {1, 0x02, 186}, + {22, 0x03, 186}, + {1, 0x02, 187}, + {22, 0x03, 187}, + {1, 0x02, 189}, + {22, 0x03, 189}, + {1, 0x02, 190}, + {22, 0x03, 190}, + {1, 0x02, 196}, + {22, 0x03, 196}, + }, + /* 136 */ + { + {2, 0x02, 178}, + {9, 0x02, 178}, + {23, 0x02, 178}, + {40, 0x03, 178}, + {2, 0x02, 181}, + {9, 0x02, 181}, + {23, 0x02, 181}, + {40, 0x03, 181}, + {2, 0x02, 185}, + {9, 0x02, 185}, + {23, 0x02, 185}, + {40, 0x03, 185}, + {2, 0x02, 186}, + {9, 0x02, 186}, + {23, 0x02, 186}, + {40, 0x03, 186}, + }, + /* 137 */ + { + {3, 0x02, 178}, + {6, 0x02, 178}, + {10, 0x02, 178}, + {15, 0x02, 178}, + {24, 0x02, 178}, + {31, 0x02, 178}, + {41, 0x02, 178}, + {56, 0x03, 178}, + {3, 0x02, 181}, + {6, 0x02, 181}, + {10, 0x02, 181}, + {15, 0x02, 181}, + {24, 0x02, 181}, + {31, 0x02, 181}, + {41, 0x02, 181}, + {56, 0x03, 181}, + }, + /* 138 */ + { + {3, 0x02, 185}, + {6, 0x02, 185}, + {10, 0x02, 185}, + {15, 0x02, 185}, + {24, 0x02, 185}, + {31, 0x02, 185}, + {41, 0x02, 185}, + {56, 0x03, 185}, + {3, 0x02, 186}, + {6, 0x02, 186}, + {10, 0x02, 186}, + {15, 0x02, 186}, + {24, 0x02, 186}, + {31, 0x02, 186}, + {41, 0x02, 186}, + {56, 0x03, 186}, + }, + /* 139 */ + { + {2, 0x02, 187}, + {9, 0x02, 187}, + {23, 0x02, 187}, + {40, 0x03, 187}, + {2, 0x02, 189}, + {9, 0x02, 189}, + {23, 0x02, 189}, + {40, 0x03, 189}, + {2, 0x02, 190}, + {9, 0x02, 190}, + {23, 0x02, 190}, + {40, 0x03, 190}, + {2, 0x02, 196}, + {9, 0x02, 196}, + {23, 0x02, 196}, + {40, 0x03, 196}, + }, + /* 140 */ + { + {3, 0x02, 187}, + {6, 0x02, 187}, + {10, 0x02, 187}, + {15, 0x02, 187}, + {24, 0x02, 187}, + {31, 0x02, 187}, + {41, 0x02, 187}, + {56, 0x03, 187}, + {3, 0x02, 189}, + {6, 0x02, 189}, + {10, 0x02, 189}, + {15, 0x02, 189}, + {24, 0x02, 189}, + {31, 0x02, 189}, + {41, 0x02, 189}, + {56, 0x03, 189}, + }, + /* 141 */ + { + {3, 0x02, 190}, + {6, 0x02, 190}, + {10, 0x02, 190}, + {15, 0x02, 190}, + {24, 0x02, 190}, + {31, 0x02, 190}, + {41, 0x02, 190}, + {56, 0x03, 190}, + {3, 0x02, 196}, + {6, 0x02, 196}, + {10, 0x02, 196}, + {15, 0x02, 196}, + {24, 0x02, 196}, + {31, 0x02, 196}, + {41, 0x02, 196}, + {56, 0x03, 196}, + }, + /* 142 */ + { + {1, 0x02, 198}, + {22, 0x03, 198}, + {1, 0x02, 228}, + {22, 0x03, 228}, + {1, 0x02, 232}, + {22, 0x03, 232}, + {1, 0x02, 233}, + {22, 0x03, 233}, + {0, 0x03, 1}, + {0, 0x03, 135}, + {0, 0x03, 137}, + {0, 0x03, 138}, + {0, 0x03, 139}, + {0, 0x03, 140}, + {0, 0x03, 141}, + {0, 0x03, 143}, + }, + /* 143 */ + { + {2, 0x02, 198}, + {9, 0x02, 198}, + {23, 0x02, 198}, + {40, 0x03, 198}, + {2, 0x02, 228}, + {9, 0x02, 228}, + {23, 0x02, 228}, + {40, 0x03, 228}, + {2, 0x02, 232}, + {9, 0x02, 232}, + {23, 0x02, 232}, + {40, 0x03, 232}, + {2, 0x02, 233}, + {9, 0x02, 233}, + {23, 0x02, 233}, + {40, 0x03, 233}, + }, + /* 144 */ + { + {3, 0x02, 198}, + {6, 0x02, 198}, + {10, 0x02, 198}, + {15, 0x02, 198}, + {24, 0x02, 198}, + {31, 0x02, 198}, + {41, 0x02, 198}, + {56, 0x03, 198}, + {3, 0x02, 228}, + {6, 0x02, 228}, + {10, 0x02, 228}, + {15, 0x02, 228}, + {24, 0x02, 228}, + {31, 0x02, 228}, + {41, 0x02, 228}, + {56, 0x03, 228}, + }, + /* 145 */ + { + {3, 0x02, 232}, + {6, 0x02, 232}, + {10, 0x02, 232}, + {15, 0x02, 232}, + {24, 0x02, 232}, + {31, 0x02, 232}, + {41, 0x02, 232}, + {56, 0x03, 232}, + {3, 0x02, 233}, + {6, 0x02, 233}, + {10, 0x02, 233}, + {15, 0x02, 233}, + {24, 0x02, 233}, + {31, 0x02, 233}, + {41, 0x02, 233}, + {56, 0x03, 233}, + }, + /* 146 */ + { + {1, 0x02, 1}, + {22, 0x03, 1}, + {1, 0x02, 135}, + {22, 0x03, 135}, + {1, 0x02, 137}, + {22, 0x03, 137}, + {1, 0x02, 138}, + {22, 0x03, 138}, + {1, 0x02, 139}, + {22, 0x03, 139}, + {1, 0x02, 140}, + {22, 0x03, 140}, + {1, 0x02, 141}, + {22, 0x03, 141}, + {1, 0x02, 143}, + {22, 0x03, 143}, + }, + /* 147 */ + { + {2, 0x02, 1}, + {9, 0x02, 1}, + {23, 0x02, 1}, + {40, 0x03, 1}, + {2, 0x02, 135}, + {9, 0x02, 135}, + {23, 0x02, 135}, + {40, 0x03, 135}, + {2, 0x02, 137}, + {9, 0x02, 137}, + {23, 0x02, 137}, + {40, 0x03, 137}, + {2, 0x02, 138}, + {9, 0x02, 138}, + {23, 0x02, 138}, + {40, 0x03, 138}, + }, + /* 148 */ + { + {3, 0x02, 1}, + {6, 0x02, 1}, + {10, 0x02, 1}, + {15, 0x02, 1}, + {24, 0x02, 1}, + {31, 0x02, 1}, + {41, 0x02, 1}, + {56, 0x03, 1}, + {3, 0x02, 135}, + {6, 0x02, 135}, + {10, 0x02, 135}, + {15, 0x02, 135}, + {24, 0x02, 135}, + {31, 0x02, 135}, + {41, 0x02, 135}, + {56, 0x03, 135}, + }, + /* 149 */ + { + {3, 0x02, 137}, + {6, 0x02, 137}, + {10, 0x02, 137}, + {15, 0x02, 137}, + {24, 0x02, 137}, + {31, 0x02, 137}, + {41, 0x02, 137}, + {56, 0x03, 137}, + {3, 0x02, 138}, + {6, 0x02, 138}, + {10, 0x02, 138}, + {15, 0x02, 138}, + {24, 0x02, 138}, + {31, 0x02, 138}, + {41, 0x02, 138}, + {56, 0x03, 138}, + }, + /* 150 */ + { + {2, 0x02, 139}, + {9, 0x02, 139}, + {23, 0x02, 139}, + {40, 0x03, 139}, + {2, 0x02, 140}, + {9, 0x02, 140}, + {23, 0x02, 140}, + {40, 0x03, 140}, + {2, 0x02, 141}, + {9, 0x02, 141}, + {23, 0x02, 141}, + {40, 0x03, 141}, + {2, 0x02, 143}, + {9, 0x02, 143}, + {23, 0x02, 143}, + {40, 0x03, 143}, + }, + /* 151 */ + { + {3, 0x02, 139}, + {6, 0x02, 139}, + {10, 0x02, 139}, + {15, 0x02, 139}, + {24, 0x02, 139}, + {31, 0x02, 139}, + {41, 0x02, 139}, + {56, 0x03, 139}, + {3, 0x02, 140}, + {6, 0x02, 140}, + {10, 0x02, 140}, + {15, 0x02, 140}, + {24, 0x02, 140}, + {31, 0x02, 140}, + {41, 0x02, 140}, + {56, 0x03, 140}, + }, + /* 152 */ + { + {3, 0x02, 141}, + {6, 0x02, 141}, + {10, 0x02, 141}, + {15, 0x02, 141}, + {24, 0x02, 141}, + {31, 0x02, 141}, + {41, 0x02, 141}, + {56, 0x03, 141}, + {3, 0x02, 143}, + {6, 0x02, 143}, + {10, 0x02, 143}, + {15, 0x02, 143}, + {24, 0x02, 143}, + {31, 0x02, 143}, + {41, 0x02, 143}, + {56, 0x03, 143}, + }, + /* 153 */ + { + {157, 0x00, 0}, + {158, 0x00, 0}, + {160, 0x00, 0}, + {161, 0x00, 0}, + {164, 0x00, 0}, + {165, 0x00, 0}, + {167, 0x00, 0}, + {168, 0x00, 0}, + {172, 0x00, 0}, + {173, 0x00, 0}, + {175, 0x00, 0}, + {177, 0x00, 0}, + {182, 0x00, 0}, + {185, 0x00, 0}, + {191, 0x00, 0}, + {207, 0x00, 0}, + }, + /* 154 */ + { + {0, 0x03, 147}, + {0, 0x03, 149}, + {0, 0x03, 150}, + {0, 0x03, 151}, + {0, 0x03, 152}, + {0, 0x03, 155}, + {0, 0x03, 157}, + {0, 0x03, 158}, + {0, 0x03, 165}, + {0, 0x03, 166}, + {0, 0x03, 168}, + {0, 0x03, 174}, + {0, 0x03, 175}, + {0, 0x03, 180}, + {0, 0x03, 182}, + {0, 0x03, 183}, + }, + /* 155 */ + { + {1, 0x02, 147}, + {22, 0x03, 147}, + {1, 0x02, 149}, + {22, 0x03, 149}, + {1, 0x02, 150}, + {22, 0x03, 150}, + {1, 0x02, 151}, + {22, 0x03, 151}, + {1, 0x02, 152}, + {22, 0x03, 152}, + {1, 0x02, 155}, + {22, 0x03, 155}, + {1, 0x02, 157}, + {22, 0x03, 157}, + {1, 0x02, 158}, + {22, 0x03, 158}, + }, + /* 156 */ + { + {2, 0x02, 147}, + {9, 0x02, 147}, + {23, 0x02, 147}, + {40, 0x03, 147}, + {2, 0x02, 149}, + {9, 0x02, 149}, + {23, 0x02, 149}, + {40, 0x03, 149}, + {2, 0x02, 150}, + {9, 0x02, 150}, + {23, 0x02, 150}, + {40, 0x03, 150}, + {2, 0x02, 151}, + {9, 0x02, 151}, + {23, 0x02, 151}, + {40, 0x03, 151}, + }, + /* 157 */ + { + {3, 0x02, 147}, + {6, 0x02, 147}, + {10, 0x02, 147}, + {15, 0x02, 147}, + {24, 0x02, 147}, + {31, 0x02, 147}, + {41, 0x02, 147}, + {56, 0x03, 147}, + {3, 0x02, 149}, + {6, 0x02, 149}, + {10, 0x02, 149}, + {15, 0x02, 149}, + {24, 0x02, 149}, + {31, 0x02, 149}, + {41, 0x02, 149}, + {56, 0x03, 149}, + }, + /* 158 */ + { + {3, 0x02, 150}, + {6, 0x02, 150}, + {10, 0x02, 150}, + {15, 0x02, 150}, + {24, 0x02, 150}, + {31, 0x02, 150}, + {41, 0x02, 150}, + {56, 0x03, 150}, + {3, 0x02, 151}, + {6, 0x02, 151}, + {10, 0x02, 151}, + {15, 0x02, 151}, + {24, 0x02, 151}, + {31, 0x02, 151}, + {41, 0x02, 151}, + {56, 0x03, 151}, + }, + /* 159 */ + { + {2, 0x02, 152}, + {9, 0x02, 152}, + {23, 0x02, 152}, + {40, 0x03, 152}, + {2, 0x02, 155}, + {9, 0x02, 155}, + {23, 0x02, 155}, + {40, 0x03, 155}, + {2, 0x02, 157}, + {9, 0x02, 157}, + {23, 0x02, 157}, + {40, 0x03, 157}, + {2, 0x02, 158}, + {9, 0x02, 158}, + {23, 0x02, 158}, + {40, 0x03, 158}, + }, + /* 160 */ + { + {3, 0x02, 152}, + {6, 0x02, 152}, + {10, 0x02, 152}, + {15, 0x02, 152}, + {24, 0x02, 152}, + {31, 0x02, 152}, + {41, 0x02, 152}, + {56, 0x03, 152}, + {3, 0x02, 155}, + {6, 0x02, 155}, + {10, 0x02, 155}, + {15, 0x02, 155}, + {24, 0x02, 155}, + {31, 0x02, 155}, + {41, 0x02, 155}, + {56, 0x03, 155}, + }, + /* 161 */ + { + {3, 0x02, 157}, + {6, 0x02, 157}, + {10, 0x02, 157}, + {15, 0x02, 157}, + {24, 0x02, 157}, + {31, 0x02, 157}, + {41, 0x02, 157}, + {56, 0x03, 157}, + {3, 0x02, 158}, + {6, 0x02, 158}, + {10, 0x02, 158}, + {15, 0x02, 158}, + {24, 0x02, 158}, + {31, 0x02, 158}, + {41, 0x02, 158}, + {56, 0x03, 158}, + }, + /* 162 */ + { + {1, 0x02, 165}, + {22, 0x03, 165}, + {1, 0x02, 166}, + {22, 0x03, 166}, + {1, 0x02, 168}, + {22, 0x03, 168}, + {1, 0x02, 174}, + {22, 0x03, 174}, + {1, 0x02, 175}, + {22, 0x03, 175}, + {1, 0x02, 180}, + {22, 0x03, 180}, + {1, 0x02, 182}, + {22, 0x03, 182}, + {1, 0x02, 183}, + {22, 0x03, 183}, + }, + /* 163 */ + { + {2, 0x02, 165}, + {9, 0x02, 165}, + {23, 0x02, 165}, + {40, 0x03, 165}, + {2, 0x02, 166}, + {9, 0x02, 166}, + {23, 0x02, 166}, + {40, 0x03, 166}, + {2, 0x02, 168}, + {9, 0x02, 168}, + {23, 0x02, 168}, + {40, 0x03, 168}, + {2, 0x02, 174}, + {9, 0x02, 174}, + {23, 0x02, 174}, + {40, 0x03, 174}, + }, + /* 164 */ + { + {3, 0x02, 165}, + {6, 0x02, 165}, + {10, 0x02, 165}, + {15, 0x02, 165}, + {24, 0x02, 165}, + {31, 0x02, 165}, + {41, 0x02, 165}, + {56, 0x03, 165}, + {3, 0x02, 166}, + {6, 0x02, 166}, + {10, 0x02, 166}, + {15, 0x02, 166}, + {24, 0x02, 166}, + {31, 0x02, 166}, + {41, 0x02, 166}, + {56, 0x03, 166}, + }, + /* 165 */ + { + {3, 0x02, 168}, + {6, 0x02, 168}, + {10, 0x02, 168}, + {15, 0x02, 168}, + {24, 0x02, 168}, + {31, 0x02, 168}, + {41, 0x02, 168}, + {56, 0x03, 168}, + {3, 0x02, 174}, + {6, 0x02, 174}, + {10, 0x02, 174}, + {15, 0x02, 174}, + {24, 0x02, 174}, + {31, 0x02, 174}, + {41, 0x02, 174}, + {56, 0x03, 174}, + }, + /* 166 */ + { + {2, 0x02, 175}, + {9, 0x02, 175}, + {23, 0x02, 175}, + {40, 0x03, 175}, + {2, 0x02, 180}, + {9, 0x02, 180}, + {23, 0x02, 180}, + {40, 0x03, 180}, + {2, 0x02, 182}, + {9, 0x02, 182}, + {23, 0x02, 182}, + {40, 0x03, 182}, + {2, 0x02, 183}, + {9, 0x02, 183}, + {23, 0x02, 183}, + {40, 0x03, 183}, + }, + /* 167 */ + { + {3, 0x02, 175}, + {6, 0x02, 175}, + {10, 0x02, 175}, + {15, 0x02, 175}, + {24, 0x02, 175}, + {31, 0x02, 175}, + {41, 0x02, 175}, + {56, 0x03, 175}, + {3, 0x02, 180}, + {6, 0x02, 180}, + {10, 0x02, 180}, + {15, 0x02, 180}, + {24, 0x02, 180}, + {31, 0x02, 180}, + {41, 0x02, 180}, + {56, 0x03, 180}, + }, + /* 168 */ + { + {3, 0x02, 182}, + {6, 0x02, 182}, + {10, 0x02, 182}, + {15, 0x02, 182}, + {24, 0x02, 182}, + {31, 0x02, 182}, + {41, 0x02, 182}, + {56, 0x03, 182}, + {3, 0x02, 183}, + {6, 0x02, 183}, + {10, 0x02, 183}, + {15, 0x02, 183}, + {24, 0x02, 183}, + {31, 0x02, 183}, + {41, 0x02, 183}, + {56, 0x03, 183}, + }, + /* 169 */ + { + {0, 0x03, 188}, + {0, 0x03, 191}, + {0, 0x03, 197}, + {0, 0x03, 231}, + {0, 0x03, 239}, + {176, 0x00, 0}, + {178, 0x00, 0}, + {179, 0x00, 0}, + {183, 0x00, 0}, + {184, 0x00, 0}, + {186, 0x00, 0}, + {187, 0x00, 0}, + {192, 0x00, 0}, + {199, 0x00, 0}, + {208, 0x00, 0}, + {223, 0x00, 0}, + }, + /* 170 */ + { + {1, 0x02, 188}, + {22, 0x03, 188}, + {1, 0x02, 191}, + {22, 0x03, 191}, + {1, 0x02, 197}, + {22, 0x03, 197}, + {1, 0x02, 231}, + {22, 0x03, 231}, + {1, 0x02, 239}, + {22, 0x03, 239}, + {0, 0x03, 9}, + {0, 0x03, 142}, + {0, 0x03, 144}, + {0, 0x03, 145}, + {0, 0x03, 148}, + {0, 0x03, 159}, + }, + /* 171 */ + { + {2, 0x02, 188}, + {9, 0x02, 188}, + {23, 0x02, 188}, + {40, 0x03, 188}, + {2, 0x02, 191}, + {9, 0x02, 191}, + {23, 0x02, 191}, + {40, 0x03, 191}, + {2, 0x02, 197}, + {9, 0x02, 197}, + {23, 0x02, 197}, + {40, 0x03, 197}, + {2, 0x02, 231}, + {9, 0x02, 231}, + {23, 0x02, 231}, + {40, 0x03, 231}, + }, + /* 172 */ + { + {3, 0x02, 188}, + {6, 0x02, 188}, + {10, 0x02, 188}, + {15, 0x02, 188}, + {24, 0x02, 188}, + {31, 0x02, 188}, + {41, 0x02, 188}, + {56, 0x03, 188}, + {3, 0x02, 191}, + {6, 0x02, 191}, + {10, 0x02, 191}, + {15, 0x02, 191}, + {24, 0x02, 191}, + {31, 0x02, 191}, + {41, 0x02, 191}, + {56, 0x03, 191}, + }, + /* 173 */ + { + {3, 0x02, 197}, + {6, 0x02, 197}, + {10, 0x02, 197}, + {15, 0x02, 197}, + {24, 0x02, 197}, + {31, 0x02, 197}, + {41, 0x02, 197}, + {56, 0x03, 197}, + {3, 0x02, 231}, + {6, 0x02, 231}, + {10, 0x02, 231}, + {15, 0x02, 231}, + {24, 0x02, 231}, + {31, 0x02, 231}, + {41, 0x02, 231}, + {56, 0x03, 231}, + }, + /* 174 */ + { + {2, 0x02, 239}, + {9, 0x02, 239}, + {23, 0x02, 239}, + {40, 0x03, 239}, + {1, 0x02, 9}, + {22, 0x03, 9}, + {1, 0x02, 142}, + {22, 0x03, 142}, + {1, 0x02, 144}, + {22, 0x03, 144}, + {1, 0x02, 145}, + {22, 0x03, 145}, + {1, 0x02, 148}, + {22, 0x03, 148}, + {1, 0x02, 159}, + {22, 0x03, 159}, + }, + /* 175 */ + { + {3, 0x02, 239}, + {6, 0x02, 239}, + {10, 0x02, 239}, + {15, 0x02, 239}, + {24, 0x02, 239}, + {31, 0x02, 239}, + {41, 0x02, 239}, + {56, 0x03, 239}, + {2, 0x02, 9}, + {9, 0x02, 9}, + {23, 0x02, 9}, + {40, 0x03, 9}, + {2, 0x02, 142}, + {9, 0x02, 142}, + {23, 0x02, 142}, + {40, 0x03, 142}, + }, + /* 176 */ + { + {3, 0x02, 9}, + {6, 0x02, 9}, + {10, 0x02, 9}, + {15, 0x02, 9}, + {24, 0x02, 9}, + {31, 0x02, 9}, + {41, 0x02, 9}, + {56, 0x03, 9}, + {3, 0x02, 142}, + {6, 0x02, 142}, + {10, 0x02, 142}, + {15, 0x02, 142}, + {24, 0x02, 142}, + {31, 0x02, 142}, + {41, 0x02, 142}, + {56, 0x03, 142}, + }, + /* 177 */ + { + {2, 0x02, 144}, + {9, 0x02, 144}, + {23, 0x02, 144}, + {40, 0x03, 144}, + {2, 0x02, 145}, + {9, 0x02, 145}, + {23, 0x02, 145}, + {40, 0x03, 145}, + {2, 0x02, 148}, + {9, 0x02, 148}, + {23, 0x02, 148}, + {40, 0x03, 148}, + {2, 0x02, 159}, + {9, 0x02, 159}, + {23, 0x02, 159}, + {40, 0x03, 159}, + }, + /* 178 */ + { + {3, 0x02, 144}, + {6, 0x02, 144}, + {10, 0x02, 144}, + {15, 0x02, 144}, + {24, 0x02, 144}, + {31, 0x02, 144}, + {41, 0x02, 144}, + {56, 0x03, 144}, + {3, 0x02, 145}, + {6, 0x02, 145}, + {10, 0x02, 145}, + {15, 0x02, 145}, + {24, 0x02, 145}, + {31, 0x02, 145}, + {41, 0x02, 145}, + {56, 0x03, 145}, + }, + /* 179 */ + { + {3, 0x02, 148}, + {6, 0x02, 148}, + {10, 0x02, 148}, + {15, 0x02, 148}, + {24, 0x02, 148}, + {31, 0x02, 148}, + {41, 0x02, 148}, + {56, 0x03, 148}, + {3, 0x02, 159}, + {6, 0x02, 159}, + {10, 0x02, 159}, + {15, 0x02, 159}, + {24, 0x02, 159}, + {31, 0x02, 159}, + {41, 0x02, 159}, + {56, 0x03, 159}, + }, + /* 180 */ + { + {0, 0x03, 171}, + {0, 0x03, 206}, + {0, 0x03, 215}, + {0, 0x03, 225}, + {0, 0x03, 236}, + {0, 0x03, 237}, + {188, 0x00, 0}, + {189, 0x00, 0}, + {193, 0x00, 0}, + {196, 0x00, 0}, + {200, 0x00, 0}, + {203, 0x00, 0}, + {209, 0x00, 0}, + {216, 0x00, 0}, + {224, 0x00, 0}, + {238, 0x00, 0}, + }, + /* 181 */ + { + {1, 0x02, 171}, + {22, 0x03, 171}, + {1, 0x02, 206}, + {22, 0x03, 206}, + {1, 0x02, 215}, + {22, 0x03, 215}, + {1, 0x02, 225}, + {22, 0x03, 225}, + {1, 0x02, 236}, + {22, 0x03, 236}, + {1, 0x02, 237}, + {22, 0x03, 237}, + {0, 0x03, 199}, + {0, 0x03, 207}, + {0, 0x03, 234}, + {0, 0x03, 235}, + }, + /* 182 */ + { + {2, 0x02, 171}, + {9, 0x02, 171}, + {23, 0x02, 171}, + {40, 0x03, 171}, + {2, 0x02, 206}, + {9, 0x02, 206}, + {23, 0x02, 206}, + {40, 0x03, 206}, + {2, 0x02, 215}, + {9, 0x02, 215}, + {23, 0x02, 215}, + {40, 0x03, 215}, + {2, 0x02, 225}, + {9, 0x02, 225}, + {23, 0x02, 225}, + {40, 0x03, 225}, + }, + /* 183 */ + { + {3, 0x02, 171}, + {6, 0x02, 171}, + {10, 0x02, 171}, + {15, 0x02, 171}, + {24, 0x02, 171}, + {31, 0x02, 171}, + {41, 0x02, 171}, + {56, 0x03, 171}, + {3, 0x02, 206}, + {6, 0x02, 206}, + {10, 0x02, 206}, + {15, 0x02, 206}, + {24, 0x02, 206}, + {31, 0x02, 206}, + {41, 0x02, 206}, + {56, 0x03, 206}, + }, + /* 184 */ + { + {3, 0x02, 215}, + {6, 0x02, 215}, + {10, 0x02, 215}, + {15, 0x02, 215}, + {24, 0x02, 215}, + {31, 0x02, 215}, + {41, 0x02, 215}, + {56, 0x03, 215}, + {3, 0x02, 225}, + {6, 0x02, 225}, + {10, 0x02, 225}, + {15, 0x02, 225}, + {24, 0x02, 225}, + {31, 0x02, 225}, + {41, 0x02, 225}, + {56, 0x03, 225}, + }, + /* 185 */ + { + {2, 0x02, 236}, + {9, 0x02, 236}, + {23, 0x02, 236}, + {40, 0x03, 236}, + {2, 0x02, 237}, + {9, 0x02, 237}, + {23, 0x02, 237}, + {40, 0x03, 237}, + {1, 0x02, 199}, + {22, 0x03, 199}, + {1, 0x02, 207}, + {22, 0x03, 207}, + {1, 0x02, 234}, + {22, 0x03, 234}, + {1, 0x02, 235}, + {22, 0x03, 235}, + }, + /* 186 */ + { + {3, 0x02, 236}, + {6, 0x02, 236}, + {10, 0x02, 236}, + {15, 0x02, 236}, + {24, 0x02, 236}, + {31, 0x02, 236}, + {41, 0x02, 236}, + {56, 0x03, 236}, + {3, 0x02, 237}, + {6, 0x02, 237}, + {10, 0x02, 237}, + {15, 0x02, 237}, + {24, 0x02, 237}, + {31, 0x02, 237}, + {41, 0x02, 237}, + {56, 0x03, 237}, + }, + /* 187 */ + { + {2, 0x02, 199}, + {9, 0x02, 199}, + {23, 0x02, 199}, + {40, 0x03, 199}, + {2, 0x02, 207}, + {9, 0x02, 207}, + {23, 0x02, 207}, + {40, 0x03, 207}, + {2, 0x02, 234}, + {9, 0x02, 234}, + {23, 0x02, 234}, + {40, 0x03, 234}, + {2, 0x02, 235}, + {9, 0x02, 235}, + {23, 0x02, 235}, + {40, 0x03, 235}, + }, + /* 188 */ + { + {3, 0x02, 199}, + {6, 0x02, 199}, + {10, 0x02, 199}, + {15, 0x02, 199}, + {24, 0x02, 199}, + {31, 0x02, 199}, + {41, 0x02, 199}, + {56, 0x03, 199}, + {3, 0x02, 207}, + {6, 0x02, 207}, + {10, 0x02, 207}, + {15, 0x02, 207}, + {24, 0x02, 207}, + {31, 0x02, 207}, + {41, 0x02, 207}, + {56, 0x03, 207}, + }, + /* 189 */ + { + {3, 0x02, 234}, + {6, 0x02, 234}, + {10, 0x02, 234}, + {15, 0x02, 234}, + {24, 0x02, 234}, + {31, 0x02, 234}, + {41, 0x02, 234}, + {56, 0x03, 234}, + {3, 0x02, 235}, + {6, 0x02, 235}, + {10, 0x02, 235}, + {15, 0x02, 235}, + {24, 0x02, 235}, + {31, 0x02, 235}, + {41, 0x02, 235}, + {56, 0x03, 235}, + }, + /* 190 */ + { + {194, 0x00, 0}, + {195, 0x00, 0}, + {197, 0x00, 0}, + {198, 0x00, 0}, + {201, 0x00, 0}, + {202, 0x00, 0}, + {204, 0x00, 0}, + {205, 0x00, 0}, + {210, 0x00, 0}, + {213, 0x00, 0}, + {217, 0x00, 0}, + {220, 0x00, 0}, + {225, 0x00, 0}, + {231, 0x00, 0}, + {239, 0x00, 0}, + {246, 0x00, 0}, + }, + /* 191 */ + { + {0, 0x03, 192}, + {0, 0x03, 193}, + {0, 0x03, 200}, + {0, 0x03, 201}, + {0, 0x03, 202}, + {0, 0x03, 205}, + {0, 0x03, 210}, + {0, 0x03, 213}, + {0, 0x03, 218}, + {0, 0x03, 219}, + {0, 0x03, 238}, + {0, 0x03, 240}, + {0, 0x03, 242}, + {0, 0x03, 243}, + {0, 0x03, 255}, + {206, 0x00, 0}, + }, + /* 192 */ + { + {1, 0x02, 192}, + {22, 0x03, 192}, + {1, 0x02, 193}, + {22, 0x03, 193}, + {1, 0x02, 200}, + {22, 0x03, 200}, + {1, 0x02, 201}, + {22, 0x03, 201}, + {1, 0x02, 202}, + {22, 0x03, 202}, + {1, 0x02, 205}, + {22, 0x03, 205}, + {1, 0x02, 210}, + {22, 0x03, 210}, + {1, 0x02, 213}, + {22, 0x03, 213}, + }, + /* 193 */ + { + {2, 0x02, 192}, + {9, 0x02, 192}, + {23, 0x02, 192}, + {40, 0x03, 192}, + {2, 0x02, 193}, + {9, 0x02, 193}, + {23, 0x02, 193}, + {40, 0x03, 193}, + {2, 0x02, 200}, + {9, 0x02, 200}, + {23, 0x02, 200}, + {40, 0x03, 200}, + {2, 0x02, 201}, + {9, 0x02, 201}, + {23, 0x02, 201}, + {40, 0x03, 201}, + }, + /* 194 */ + { + {3, 0x02, 192}, + {6, 0x02, 192}, + {10, 0x02, 192}, + {15, 0x02, 192}, + {24, 0x02, 192}, + {31, 0x02, 192}, + {41, 0x02, 192}, + {56, 0x03, 192}, + {3, 0x02, 193}, + {6, 0x02, 193}, + {10, 0x02, 193}, + {15, 0x02, 193}, + {24, 0x02, 193}, + {31, 0x02, 193}, + {41, 0x02, 193}, + {56, 0x03, 193}, + }, + /* 195 */ + { + {3, 0x02, 200}, + {6, 0x02, 200}, + {10, 0x02, 200}, + {15, 0x02, 200}, + {24, 0x02, 200}, + {31, 0x02, 200}, + {41, 0x02, 200}, + {56, 0x03, 200}, + {3, 0x02, 201}, + {6, 0x02, 201}, + {10, 0x02, 201}, + {15, 0x02, 201}, + {24, 0x02, 201}, + {31, 0x02, 201}, + {41, 0x02, 201}, + {56, 0x03, 201}, + }, + /* 196 */ + { + {2, 0x02, 202}, + {9, 0x02, 202}, + {23, 0x02, 202}, + {40, 0x03, 202}, + {2, 0x02, 205}, + {9, 0x02, 205}, + {23, 0x02, 205}, + {40, 0x03, 205}, + {2, 0x02, 210}, + {9, 0x02, 210}, + {23, 0x02, 210}, + {40, 0x03, 210}, + {2, 0x02, 213}, + {9, 0x02, 213}, + {23, 0x02, 213}, + {40, 0x03, 213}, + }, + /* 197 */ + { + {3, 0x02, 202}, + {6, 0x02, 202}, + {10, 0x02, 202}, + {15, 0x02, 202}, + {24, 0x02, 202}, + {31, 0x02, 202}, + {41, 0x02, 202}, + {56, 0x03, 202}, + {3, 0x02, 205}, + {6, 0x02, 205}, + {10, 0x02, 205}, + {15, 0x02, 205}, + {24, 0x02, 205}, + {31, 0x02, 205}, + {41, 0x02, 205}, + {56, 0x03, 205}, + }, + /* 198 */ + { + {3, 0x02, 210}, + {6, 0x02, 210}, + {10, 0x02, 210}, + {15, 0x02, 210}, + {24, 0x02, 210}, + {31, 0x02, 210}, + {41, 0x02, 210}, + {56, 0x03, 210}, + {3, 0x02, 213}, + {6, 0x02, 213}, + {10, 0x02, 213}, + {15, 0x02, 213}, + {24, 0x02, 213}, + {31, 0x02, 213}, + {41, 0x02, 213}, + {56, 0x03, 213}, + }, + /* 199 */ + { + {1, 0x02, 218}, + {22, 0x03, 218}, + {1, 0x02, 219}, + {22, 0x03, 219}, + {1, 0x02, 238}, + {22, 0x03, 238}, + {1, 0x02, 240}, + {22, 0x03, 240}, + {1, 0x02, 242}, + {22, 0x03, 242}, + {1, 0x02, 243}, + {22, 0x03, 243}, + {1, 0x02, 255}, + {22, 0x03, 255}, + {0, 0x03, 203}, + {0, 0x03, 204}, + }, + /* 200 */ + { + {2, 0x02, 218}, + {9, 0x02, 218}, + {23, 0x02, 218}, + {40, 0x03, 218}, + {2, 0x02, 219}, + {9, 0x02, 219}, + {23, 0x02, 219}, + {40, 0x03, 219}, + {2, 0x02, 238}, + {9, 0x02, 238}, + {23, 0x02, 238}, + {40, 0x03, 238}, + {2, 0x02, 240}, + {9, 0x02, 240}, + {23, 0x02, 240}, + {40, 0x03, 240}, + }, + /* 201 */ + { + {3, 0x02, 218}, + {6, 0x02, 218}, + {10, 0x02, 218}, + {15, 0x02, 218}, + {24, 0x02, 218}, + {31, 0x02, 218}, + {41, 0x02, 218}, + {56, 0x03, 218}, + {3, 0x02, 219}, + {6, 0x02, 219}, + {10, 0x02, 219}, + {15, 0x02, 219}, + {24, 0x02, 219}, + {31, 0x02, 219}, + {41, 0x02, 219}, + {56, 0x03, 219}, + }, + /* 202 */ + { + {3, 0x02, 238}, + {6, 0x02, 238}, + {10, 0x02, 238}, + {15, 0x02, 238}, + {24, 0x02, 238}, + {31, 0x02, 238}, + {41, 0x02, 238}, + {56, 0x03, 238}, + {3, 0x02, 240}, + {6, 0x02, 240}, + {10, 0x02, 240}, + {15, 0x02, 240}, + {24, 0x02, 240}, + {31, 0x02, 240}, + {41, 0x02, 240}, + {56, 0x03, 240}, + }, + /* 203 */ + { + {2, 0x02, 242}, + {9, 0x02, 242}, + {23, 0x02, 242}, + {40, 0x03, 242}, + {2, 0x02, 243}, + {9, 0x02, 243}, + {23, 0x02, 243}, + {40, 0x03, 243}, + {2, 0x02, 255}, + {9, 0x02, 255}, + {23, 0x02, 255}, + {40, 0x03, 255}, + {1, 0x02, 203}, + {22, 0x03, 203}, + {1, 0x02, 204}, + {22, 0x03, 204}, + }, + /* 204 */ + { + {3, 0x02, 242}, + {6, 0x02, 242}, + {10, 0x02, 242}, + {15, 0x02, 242}, + {24, 0x02, 242}, + {31, 0x02, 242}, + {41, 0x02, 242}, + {56, 0x03, 242}, + {3, 0x02, 243}, + {6, 0x02, 243}, + {10, 0x02, 243}, + {15, 0x02, 243}, + {24, 0x02, 243}, + {31, 0x02, 243}, + {41, 0x02, 243}, + {56, 0x03, 243}, + }, + /* 205 */ + { + {3, 0x02, 255}, + {6, 0x02, 255}, + {10, 0x02, 255}, + {15, 0x02, 255}, + {24, 0x02, 255}, + {31, 0x02, 255}, + {41, 0x02, 255}, + {56, 0x03, 255}, + {2, 0x02, 203}, + {9, 0x02, 203}, + {23, 0x02, 203}, + {40, 0x03, 203}, + {2, 0x02, 204}, + {9, 0x02, 204}, + {23, 0x02, 204}, + {40, 0x03, 204}, + }, + /* 206 */ + { + {3, 0x02, 203}, + {6, 0x02, 203}, + {10, 0x02, 203}, + {15, 0x02, 203}, + {24, 0x02, 203}, + {31, 0x02, 203}, + {41, 0x02, 203}, + {56, 0x03, 203}, + {3, 0x02, 204}, + {6, 0x02, 204}, + {10, 0x02, 204}, + {15, 0x02, 204}, + {24, 0x02, 204}, + {31, 0x02, 204}, + {41, 0x02, 204}, + {56, 0x03, 204}, + }, + /* 207 */ + { + {211, 0x00, 0}, + {212, 0x00, 0}, + {214, 0x00, 0}, + {215, 0x00, 0}, + {218, 0x00, 0}, + {219, 0x00, 0}, + {221, 0x00, 0}, + {222, 0x00, 0}, + {226, 0x00, 0}, + {228, 0x00, 0}, + {232, 0x00, 0}, + {235, 0x00, 0}, + {240, 0x00, 0}, + {243, 0x00, 0}, + {247, 0x00, 0}, + {250, 0x00, 0}, + }, + /* 208 */ + { + {0, 0x03, 211}, + {0, 0x03, 212}, + {0, 0x03, 214}, + {0, 0x03, 221}, + {0, 0x03, 222}, + {0, 0x03, 223}, + {0, 0x03, 241}, + {0, 0x03, 244}, + {0, 0x03, 245}, + {0, 0x03, 246}, + {0, 0x03, 247}, + {0, 0x03, 248}, + {0, 0x03, 250}, + {0, 0x03, 251}, + {0, 0x03, 252}, + {0, 0x03, 253}, + }, + /* 209 */ + { + {1, 0x02, 211}, + {22, 0x03, 211}, + {1, 0x02, 212}, + {22, 0x03, 212}, + {1, 0x02, 214}, + {22, 0x03, 214}, + {1, 0x02, 221}, + {22, 0x03, 221}, + {1, 0x02, 222}, + {22, 0x03, 222}, + {1, 0x02, 223}, + {22, 0x03, 223}, + {1, 0x02, 241}, + {22, 0x03, 241}, + {1, 0x02, 244}, + {22, 0x03, 244}, + }, + /* 210 */ + { + {2, 0x02, 211}, + {9, 0x02, 211}, + {23, 0x02, 211}, + {40, 0x03, 211}, + {2, 0x02, 212}, + {9, 0x02, 212}, + {23, 0x02, 212}, + {40, 0x03, 212}, + {2, 0x02, 214}, + {9, 0x02, 214}, + {23, 0x02, 214}, + {40, 0x03, 214}, + {2, 0x02, 221}, + {9, 0x02, 221}, + {23, 0x02, 221}, + {40, 0x03, 221}, + }, + /* 211 */ + { + {3, 0x02, 211}, + {6, 0x02, 211}, + {10, 0x02, 211}, + {15, 0x02, 211}, + {24, 0x02, 211}, + {31, 0x02, 211}, + {41, 0x02, 211}, + {56, 0x03, 211}, + {3, 0x02, 212}, + {6, 0x02, 212}, + {10, 0x02, 212}, + {15, 0x02, 212}, + {24, 0x02, 212}, + {31, 0x02, 212}, + {41, 0x02, 212}, + {56, 0x03, 212}, + }, + /* 212 */ + { + {3, 0x02, 214}, + {6, 0x02, 214}, + {10, 0x02, 214}, + {15, 0x02, 214}, + {24, 0x02, 214}, + {31, 0x02, 214}, + {41, 0x02, 214}, + {56, 0x03, 214}, + {3, 0x02, 221}, + {6, 0x02, 221}, + {10, 0x02, 221}, + {15, 0x02, 221}, + {24, 0x02, 221}, + {31, 0x02, 221}, + {41, 0x02, 221}, + {56, 0x03, 221}, + }, + /* 213 */ + { + {2, 0x02, 222}, + {9, 0x02, 222}, + {23, 0x02, 222}, + {40, 0x03, 222}, + {2, 0x02, 223}, + {9, 0x02, 223}, + {23, 0x02, 223}, + {40, 0x03, 223}, + {2, 0x02, 241}, + {9, 0x02, 241}, + {23, 0x02, 241}, + {40, 0x03, 241}, + {2, 0x02, 244}, + {9, 0x02, 244}, + {23, 0x02, 244}, + {40, 0x03, 244}, + }, + /* 214 */ + { + {3, 0x02, 222}, + {6, 0x02, 222}, + {10, 0x02, 222}, + {15, 0x02, 222}, + {24, 0x02, 222}, + {31, 0x02, 222}, + {41, 0x02, 222}, + {56, 0x03, 222}, + {3, 0x02, 223}, + {6, 0x02, 223}, + {10, 0x02, 223}, + {15, 0x02, 223}, + {24, 0x02, 223}, + {31, 0x02, 223}, + {41, 0x02, 223}, + {56, 0x03, 223}, + }, + /* 215 */ + { + {3, 0x02, 241}, + {6, 0x02, 241}, + {10, 0x02, 241}, + {15, 0x02, 241}, + {24, 0x02, 241}, + {31, 0x02, 241}, + {41, 0x02, 241}, + {56, 0x03, 241}, + {3, 0x02, 244}, + {6, 0x02, 244}, + {10, 0x02, 244}, + {15, 0x02, 244}, + {24, 0x02, 244}, + {31, 0x02, 244}, + {41, 0x02, 244}, + {56, 0x03, 244}, + }, + /* 216 */ + { + {1, 0x02, 245}, + {22, 0x03, 245}, + {1, 0x02, 246}, + {22, 0x03, 246}, + {1, 0x02, 247}, + {22, 0x03, 247}, + {1, 0x02, 248}, + {22, 0x03, 248}, + {1, 0x02, 250}, + {22, 0x03, 250}, + {1, 0x02, 251}, + {22, 0x03, 251}, + {1, 0x02, 252}, + {22, 0x03, 252}, + {1, 0x02, 253}, + {22, 0x03, 253}, + }, + /* 217 */ + { + {2, 0x02, 245}, + {9, 0x02, 245}, + {23, 0x02, 245}, + {40, 0x03, 245}, + {2, 0x02, 246}, + {9, 0x02, 246}, + {23, 0x02, 246}, + {40, 0x03, 246}, + {2, 0x02, 247}, + {9, 0x02, 247}, + {23, 0x02, 247}, + {40, 0x03, 247}, + {2, 0x02, 248}, + {9, 0x02, 248}, + {23, 0x02, 248}, + {40, 0x03, 248}, + }, + /* 218 */ + { + {3, 0x02, 245}, + {6, 0x02, 245}, + {10, 0x02, 245}, + {15, 0x02, 245}, + {24, 0x02, 245}, + {31, 0x02, 245}, + {41, 0x02, 245}, + {56, 0x03, 245}, + {3, 0x02, 246}, + {6, 0x02, 246}, + {10, 0x02, 246}, + {15, 0x02, 246}, + {24, 0x02, 246}, + {31, 0x02, 246}, + {41, 0x02, 246}, + {56, 0x03, 246}, + }, + /* 219 */ + { + {3, 0x02, 247}, + {6, 0x02, 247}, + {10, 0x02, 247}, + {15, 0x02, 247}, + {24, 0x02, 247}, + {31, 0x02, 247}, + {41, 0x02, 247}, + {56, 0x03, 247}, + {3, 0x02, 248}, + {6, 0x02, 248}, + {10, 0x02, 248}, + {15, 0x02, 248}, + {24, 0x02, 248}, + {31, 0x02, 248}, + {41, 0x02, 248}, + {56, 0x03, 248}, + }, + /* 220 */ + { + {2, 0x02, 250}, + {9, 0x02, 250}, + {23, 0x02, 250}, + {40, 0x03, 250}, + {2, 0x02, 251}, + {9, 0x02, 251}, + {23, 0x02, 251}, + {40, 0x03, 251}, + {2, 0x02, 252}, + {9, 0x02, 252}, + {23, 0x02, 252}, + {40, 0x03, 252}, + {2, 0x02, 253}, + {9, 0x02, 253}, + {23, 0x02, 253}, + {40, 0x03, 253}, + }, + /* 221 */ + { + {3, 0x02, 250}, + {6, 0x02, 250}, + {10, 0x02, 250}, + {15, 0x02, 250}, + {24, 0x02, 250}, + {31, 0x02, 250}, + {41, 0x02, 250}, + {56, 0x03, 250}, + {3, 0x02, 251}, + {6, 0x02, 251}, + {10, 0x02, 251}, + {15, 0x02, 251}, + {24, 0x02, 251}, + {31, 0x02, 251}, + {41, 0x02, 251}, + {56, 0x03, 251}, + }, + /* 222 */ + { + {3, 0x02, 252}, + {6, 0x02, 252}, + {10, 0x02, 252}, + {15, 0x02, 252}, + {24, 0x02, 252}, + {31, 0x02, 252}, + {41, 0x02, 252}, + {56, 0x03, 252}, + {3, 0x02, 253}, + {6, 0x02, 253}, + {10, 0x02, 253}, + {15, 0x02, 253}, + {24, 0x02, 253}, + {31, 0x02, 253}, + {41, 0x02, 253}, + {56, 0x03, 253}, + }, + /* 223 */ + { + {0, 0x03, 254}, + {227, 0x00, 0}, + {229, 0x00, 0}, + {230, 0x00, 0}, + {233, 0x00, 0}, + {234, 0x00, 0}, + {236, 0x00, 0}, + {237, 0x00, 0}, + {241, 0x00, 0}, + {242, 0x00, 0}, + {244, 0x00, 0}, + {245, 0x00, 0}, + {248, 0x00, 0}, + {249, 0x00, 0}, + {251, 0x00, 0}, + {252, 0x00, 0}, + }, + /* 224 */ + { + {1, 0x02, 254}, + {22, 0x03, 254}, + {0, 0x03, 2}, + {0, 0x03, 3}, + {0, 0x03, 4}, + {0, 0x03, 5}, + {0, 0x03, 6}, + {0, 0x03, 7}, + {0, 0x03, 8}, + {0, 0x03, 11}, + {0, 0x03, 12}, + {0, 0x03, 14}, + {0, 0x03, 15}, + {0, 0x03, 16}, + {0, 0x03, 17}, + {0, 0x03, 18}, + }, + /* 225 */ + { + {2, 0x02, 254}, + {9, 0x02, 254}, + {23, 0x02, 254}, + {40, 0x03, 254}, + {1, 0x02, 2}, + {22, 0x03, 2}, + {1, 0x02, 3}, + {22, 0x03, 3}, + {1, 0x02, 4}, + {22, 0x03, 4}, + {1, 0x02, 5}, + {22, 0x03, 5}, + {1, 0x02, 6}, + {22, 0x03, 6}, + {1, 0x02, 7}, + {22, 0x03, 7}, + }, + /* 226 */ + { + {3, 0x02, 254}, + {6, 0x02, 254}, + {10, 0x02, 254}, + {15, 0x02, 254}, + {24, 0x02, 254}, + {31, 0x02, 254}, + {41, 0x02, 254}, + {56, 0x03, 254}, + {2, 0x02, 2}, + {9, 0x02, 2}, + {23, 0x02, 2}, + {40, 0x03, 2}, + {2, 0x02, 3}, + {9, 0x02, 3}, + {23, 0x02, 3}, + {40, 0x03, 3}, + }, + /* 227 */ + { + {3, 0x02, 2}, + {6, 0x02, 2}, + {10, 0x02, 2}, + {15, 0x02, 2}, + {24, 0x02, 2}, + {31, 0x02, 2}, + {41, 0x02, 2}, + {56, 0x03, 2}, + {3, 0x02, 3}, + {6, 0x02, 3}, + {10, 0x02, 3}, + {15, 0x02, 3}, + {24, 0x02, 3}, + {31, 0x02, 3}, + {41, 0x02, 3}, + {56, 0x03, 3}, + }, + /* 228 */ + { + {2, 0x02, 4}, + {9, 0x02, 4}, + {23, 0x02, 4}, + {40, 0x03, 4}, + {2, 0x02, 5}, + {9, 0x02, 5}, + {23, 0x02, 5}, + {40, 0x03, 5}, + {2, 0x02, 6}, + {9, 0x02, 6}, + {23, 0x02, 6}, + {40, 0x03, 6}, + {2, 0x02, 7}, + {9, 0x02, 7}, + {23, 0x02, 7}, + {40, 0x03, 7}, + }, + /* 229 */ + { + {3, 0x02, 4}, + {6, 0x02, 4}, + {10, 0x02, 4}, + {15, 0x02, 4}, + {24, 0x02, 4}, + {31, 0x02, 4}, + {41, 0x02, 4}, + {56, 0x03, 4}, + {3, 0x02, 5}, + {6, 0x02, 5}, + {10, 0x02, 5}, + {15, 0x02, 5}, + {24, 0x02, 5}, + {31, 0x02, 5}, + {41, 0x02, 5}, + {56, 0x03, 5}, + }, + /* 230 */ + { + {3, 0x02, 6}, + {6, 0x02, 6}, + {10, 0x02, 6}, + {15, 0x02, 6}, + {24, 0x02, 6}, + {31, 0x02, 6}, + {41, 0x02, 6}, + {56, 0x03, 6}, + {3, 0x02, 7}, + {6, 0x02, 7}, + {10, 0x02, 7}, + {15, 0x02, 7}, + {24, 0x02, 7}, + {31, 0x02, 7}, + {41, 0x02, 7}, + {56, 0x03, 7}, + }, + /* 231 */ + { + {1, 0x02, 8}, + {22, 0x03, 8}, + {1, 0x02, 11}, + {22, 0x03, 11}, + {1, 0x02, 12}, + {22, 0x03, 12}, + {1, 0x02, 14}, + {22, 0x03, 14}, + {1, 0x02, 15}, + {22, 0x03, 15}, + {1, 0x02, 16}, + {22, 0x03, 16}, + {1, 0x02, 17}, + {22, 0x03, 17}, + {1, 0x02, 18}, + {22, 0x03, 18}, + }, + /* 232 */ + { + {2, 0x02, 8}, + {9, 0x02, 8}, + {23, 0x02, 8}, + {40, 0x03, 8}, + {2, 0x02, 11}, + {9, 0x02, 11}, + {23, 0x02, 11}, + {40, 0x03, 11}, + {2, 0x02, 12}, + {9, 0x02, 12}, + {23, 0x02, 12}, + {40, 0x03, 12}, + {2, 0x02, 14}, + {9, 0x02, 14}, + {23, 0x02, 14}, + {40, 0x03, 14}, + }, + /* 233 */ + { + {3, 0x02, 8}, + {6, 0x02, 8}, + {10, 0x02, 8}, + {15, 0x02, 8}, + {24, 0x02, 8}, + {31, 0x02, 8}, + {41, 0x02, 8}, + {56, 0x03, 8}, + {3, 0x02, 11}, + {6, 0x02, 11}, + {10, 0x02, 11}, + {15, 0x02, 11}, + {24, 0x02, 11}, + {31, 0x02, 11}, + {41, 0x02, 11}, + {56, 0x03, 11}, + }, + /* 234 */ + { + {3, 0x02, 12}, + {6, 0x02, 12}, + {10, 0x02, 12}, + {15, 0x02, 12}, + {24, 0x02, 12}, + {31, 0x02, 12}, + {41, 0x02, 12}, + {56, 0x03, 12}, + {3, 0x02, 14}, + {6, 0x02, 14}, + {10, 0x02, 14}, + {15, 0x02, 14}, + {24, 0x02, 14}, + {31, 0x02, 14}, + {41, 0x02, 14}, + {56, 0x03, 14}, + }, + /* 235 */ + { + {2, 0x02, 15}, + {9, 0x02, 15}, + {23, 0x02, 15}, + {40, 0x03, 15}, + {2, 0x02, 16}, + {9, 0x02, 16}, + {23, 0x02, 16}, + {40, 0x03, 16}, + {2, 0x02, 17}, + {9, 0x02, 17}, + {23, 0x02, 17}, + {40, 0x03, 17}, + {2, 0x02, 18}, + {9, 0x02, 18}, + {23, 0x02, 18}, + {40, 0x03, 18}, + }, + /* 236 */ + { + {3, 0x02, 15}, + {6, 0x02, 15}, + {10, 0x02, 15}, + {15, 0x02, 15}, + {24, 0x02, 15}, + {31, 0x02, 15}, + {41, 0x02, 15}, + {56, 0x03, 15}, + {3, 0x02, 16}, + {6, 0x02, 16}, + {10, 0x02, 16}, + {15, 0x02, 16}, + {24, 0x02, 16}, + {31, 0x02, 16}, + {41, 0x02, 16}, + {56, 0x03, 16}, + }, + /* 237 */ + { + {3, 0x02, 17}, + {6, 0x02, 17}, + {10, 0x02, 17}, + {15, 0x02, 17}, + {24, 0x02, 17}, + {31, 0x02, 17}, + {41, 0x02, 17}, + {56, 0x03, 17}, + {3, 0x02, 18}, + {6, 0x02, 18}, + {10, 0x02, 18}, + {15, 0x02, 18}, + {24, 0x02, 18}, + {31, 0x02, 18}, + {41, 0x02, 18}, + {56, 0x03, 18}, + }, + /* 238 */ + { + {0, 0x03, 19}, + {0, 0x03, 20}, + {0, 0x03, 21}, + {0, 0x03, 23}, + {0, 0x03, 24}, + {0, 0x03, 25}, + {0, 0x03, 26}, + {0, 0x03, 27}, + {0, 0x03, 28}, + {0, 0x03, 29}, + {0, 0x03, 30}, + {0, 0x03, 31}, + {0, 0x03, 127}, + {0, 0x03, 220}, + {0, 0x03, 249}, + {253, 0x00, 0}, + }, + /* 239 */ + { + {1, 0x02, 19}, + {22, 0x03, 19}, + {1, 0x02, 20}, + {22, 0x03, 20}, + {1, 0x02, 21}, + {22, 0x03, 21}, + {1, 0x02, 23}, + {22, 0x03, 23}, + {1, 0x02, 24}, + {22, 0x03, 24}, + {1, 0x02, 25}, + {22, 0x03, 25}, + {1, 0x02, 26}, + {22, 0x03, 26}, + {1, 0x02, 27}, + {22, 0x03, 27}, + }, + /* 240 */ + { + {2, 0x02, 19}, + {9, 0x02, 19}, + {23, 0x02, 19}, + {40, 0x03, 19}, + {2, 0x02, 20}, + {9, 0x02, 20}, + {23, 0x02, 20}, + {40, 0x03, 20}, + {2, 0x02, 21}, + {9, 0x02, 21}, + {23, 0x02, 21}, + {40, 0x03, 21}, + {2, 0x02, 23}, + {9, 0x02, 23}, + {23, 0x02, 23}, + {40, 0x03, 23}, + }, + /* 241 */ + { + {3, 0x02, 19}, + {6, 0x02, 19}, + {10, 0x02, 19}, + {15, 0x02, 19}, + {24, 0x02, 19}, + {31, 0x02, 19}, + {41, 0x02, 19}, + {56, 0x03, 19}, + {3, 0x02, 20}, + {6, 0x02, 20}, + {10, 0x02, 20}, + {15, 0x02, 20}, + {24, 0x02, 20}, + {31, 0x02, 20}, + {41, 0x02, 20}, + {56, 0x03, 20}, + }, + /* 242 */ + { + {3, 0x02, 21}, + {6, 0x02, 21}, + {10, 0x02, 21}, + {15, 0x02, 21}, + {24, 0x02, 21}, + {31, 0x02, 21}, + {41, 0x02, 21}, + {56, 0x03, 21}, + {3, 0x02, 23}, + {6, 0x02, 23}, + {10, 0x02, 23}, + {15, 0x02, 23}, + {24, 0x02, 23}, + {31, 0x02, 23}, + {41, 0x02, 23}, + {56, 0x03, 23}, + }, + /* 243 */ + { + {2, 0x02, 24}, + {9, 0x02, 24}, + {23, 0x02, 24}, + {40, 0x03, 24}, + {2, 0x02, 25}, + {9, 0x02, 25}, + {23, 0x02, 25}, + {40, 0x03, 25}, + {2, 0x02, 26}, + {9, 0x02, 26}, + {23, 0x02, 26}, + {40, 0x03, 26}, + {2, 0x02, 27}, + {9, 0x02, 27}, + {23, 0x02, 27}, + {40, 0x03, 27}, + }, + /* 244 */ + { + {3, 0x02, 24}, + {6, 0x02, 24}, + {10, 0x02, 24}, + {15, 0x02, 24}, + {24, 0x02, 24}, + {31, 0x02, 24}, + {41, 0x02, 24}, + {56, 0x03, 24}, + {3, 0x02, 25}, + {6, 0x02, 25}, + {10, 0x02, 25}, + {15, 0x02, 25}, + {24, 0x02, 25}, + {31, 0x02, 25}, + {41, 0x02, 25}, + {56, 0x03, 25}, + }, + /* 245 */ + { + {3, 0x02, 26}, + {6, 0x02, 26}, + {10, 0x02, 26}, + {15, 0x02, 26}, + {24, 0x02, 26}, + {31, 0x02, 26}, + {41, 0x02, 26}, + {56, 0x03, 26}, + {3, 0x02, 27}, + {6, 0x02, 27}, + {10, 0x02, 27}, + {15, 0x02, 27}, + {24, 0x02, 27}, + {31, 0x02, 27}, + {41, 0x02, 27}, + {56, 0x03, 27}, + }, + /* 246 */ + { + {1, 0x02, 28}, + {22, 0x03, 28}, + {1, 0x02, 29}, + {22, 0x03, 29}, + {1, 0x02, 30}, + {22, 0x03, 30}, + {1, 0x02, 31}, + {22, 0x03, 31}, + {1, 0x02, 127}, + {22, 0x03, 127}, + {1, 0x02, 220}, + {22, 0x03, 220}, + {1, 0x02, 249}, + {22, 0x03, 249}, + {254, 0x00, 0}, + {255, 0x00, 0}, + }, + /* 247 */ + { + {2, 0x02, 28}, + {9, 0x02, 28}, + {23, 0x02, 28}, + {40, 0x03, 28}, + {2, 0x02, 29}, + {9, 0x02, 29}, + {23, 0x02, 29}, + {40, 0x03, 29}, + {2, 0x02, 30}, + {9, 0x02, 30}, + {23, 0x02, 30}, + {40, 0x03, 30}, + {2, 0x02, 31}, + {9, 0x02, 31}, + {23, 0x02, 31}, + {40, 0x03, 31}, + }, + /* 248 */ + { + {3, 0x02, 28}, + {6, 0x02, 28}, + {10, 0x02, 28}, + {15, 0x02, 28}, + {24, 0x02, 28}, + {31, 0x02, 28}, + {41, 0x02, 28}, + {56, 0x03, 28}, + {3, 0x02, 29}, + {6, 0x02, 29}, + {10, 0x02, 29}, + {15, 0x02, 29}, + {24, 0x02, 29}, + {31, 0x02, 29}, + {41, 0x02, 29}, + {56, 0x03, 29}, + }, + /* 249 */ + { + {3, 0x02, 30}, + {6, 0x02, 30}, + {10, 0x02, 30}, + {15, 0x02, 30}, + {24, 0x02, 30}, + {31, 0x02, 30}, + {41, 0x02, 30}, + {56, 0x03, 30}, + {3, 0x02, 31}, + {6, 0x02, 31}, + {10, 0x02, 31}, + {15, 0x02, 31}, + {24, 0x02, 31}, + {31, 0x02, 31}, + {41, 0x02, 31}, + {56, 0x03, 31}, + }, + /* 250 */ + { + {2, 0x02, 127}, + {9, 0x02, 127}, + {23, 0x02, 127}, + {40, 0x03, 127}, + {2, 0x02, 220}, + {9, 0x02, 220}, + {23, 0x02, 220}, + {40, 0x03, 220}, + {2, 0x02, 249}, + {9, 0x02, 249}, + {23, 0x02, 249}, + {40, 0x03, 249}, + {0, 0x03, 10}, + {0, 0x03, 13}, + {0, 0x03, 22}, + {0, 0x04, 0}, + }, + /* 251 */ + { + {3, 0x02, 127}, + {6, 0x02, 127}, + {10, 0x02, 127}, + {15, 0x02, 127}, + {24, 0x02, 127}, + {31, 0x02, 127}, + {41, 0x02, 127}, + {56, 0x03, 127}, + {3, 0x02, 220}, + {6, 0x02, 220}, + {10, 0x02, 220}, + {15, 0x02, 220}, + {24, 0x02, 220}, + {31, 0x02, 220}, + {41, 0x02, 220}, + {56, 0x03, 220}, + }, + /* 252 */ + { + {3, 0x02, 249}, + {6, 0x02, 249}, + {10, 0x02, 249}, + {15, 0x02, 249}, + {24, 0x02, 249}, + {31, 0x02, 249}, + {41, 0x02, 249}, + {56, 0x03, 249}, + {1, 0x02, 10}, + {22, 0x03, 10}, + {1, 0x02, 13}, + {22, 0x03, 13}, + {1, 0x02, 22}, + {22, 0x03, 22}, + {0, 0x04, 0}, + {0, 0x04, 0}, + }, + /* 253 */ + { + {2, 0x02, 10}, + {9, 0x02, 10}, + {23, 0x02, 10}, + {40, 0x03, 10}, + {2, 0x02, 13}, + {9, 0x02, 13}, + {23, 0x02, 13}, + {40, 0x03, 13}, + {2, 0x02, 22}, + {9, 0x02, 22}, + {23, 0x02, 22}, + {40, 0x03, 22}, + {0, 0x04, 0}, + {0, 0x04, 0}, + {0, 0x04, 0}, + {0, 0x04, 0}, + }, + /* 254 */ + { + {3, 0x02, 10}, + {6, 0x02, 10}, + {10, 0x02, 10}, + {15, 0x02, 10}, + {24, 0x02, 10}, + {31, 0x02, 10}, + {41, 0x02, 10}, + {56, 0x03, 10}, + {3, 0x02, 13}, + {6, 0x02, 13}, + {10, 0x02, 13}, + {15, 0x02, 13}, + {24, 0x02, 13}, + {31, 0x02, 13}, + {41, 0x02, 13}, + {56, 0x03, 13}, + }, + /* 255 */ + { + {3, 0x02, 22}, + {6, 0x02, 22}, + {10, 0x02, 22}, + {15, 0x02, 22}, + {24, 0x02, 22}, + {31, 0x02, 22}, + {41, 0x02, 22}, + {56, 0x03, 22}, + {0, 0x04, 0}, + {0, 0x04, 0}, + {0, 0x04, 0}, + {0, 0x04, 0}, + {0, 0x04, 0}, + {0, 0x04, 0}, + {0, 0x04, 0}, + {0, 0x04, 0}, + }, +}; diff --git a/lib/nghttp2_helper.c b/lib/nghttp2_helper.c new file mode 100644 index 0000000..15f329c --- /dev/null +++ b/lib/nghttp2_helper.c @@ -0,0 +1,463 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "nghttp2_helper.h" + +#include +#include + +#include "nghttp2_net.h" + +void nghttp2_put_uint16be(uint8_t *buf, uint16_t n) { + uint16_t x = htons(n); + memcpy(buf, &x, sizeof(uint16_t)); +} + +void nghttp2_put_uint32be(uint8_t *buf, uint32_t n) { + uint32_t x = htonl(n); + memcpy(buf, &x, sizeof(uint32_t)); +} + +uint16_t nghttp2_get_uint16(const uint8_t *data) { + uint16_t n; + memcpy(&n, data, sizeof(uint16_t)); + return ntohs(n); +} + +uint32_t nghttp2_get_uint32(const uint8_t *data) { + uint32_t n; + memcpy(&n, data, sizeof(uint32_t)); + return ntohl(n); +} + +void *nghttp2_memdup(const void *src, size_t n, nghttp2_mem *mem) { + void *dest; + + if (n == 0) { + return NULL; + } + + dest = nghttp2_mem_malloc(mem, n); + if (dest == NULL) { + return NULL; + } + memcpy(dest, src, n); + return dest; +} + +/* Generated by gendowncasetbl.py */ +static const int DOWNCASE_TBL[] = { + 0 /* NUL */, 1 /* SOH */, 2 /* STX */, 3 /* ETX */, + 4 /* EOT */, 5 /* ENQ */, 6 /* ACK */, 7 /* BEL */, + 8 /* BS */, 9 /* HT */, 10 /* LF */, 11 /* VT */, + 12 /* FF */, 13 /* CR */, 14 /* SO */, 15 /* SI */, + 16 /* DLE */, 17 /* DC1 */, 18 /* DC2 */, 19 /* DC3 */, + 20 /* DC4 */, 21 /* NAK */, 22 /* SYN */, 23 /* ETB */, + 24 /* CAN */, 25 /* EM */, 26 /* SUB */, 27 /* ESC */, + 28 /* FS */, 29 /* GS */, 30 /* RS */, 31 /* US */, + 32 /* SPC */, 33 /* ! */, 34 /* " */, 35 /* # */, + 36 /* $ */, 37 /* % */, 38 /* & */, 39 /* ' */, + 40 /* ( */, 41 /* ) */, 42 /* * */, 43 /* + */, + 44 /* , */, 45 /* - */, 46 /* . */, 47 /* / */, + 48 /* 0 */, 49 /* 1 */, 50 /* 2 */, 51 /* 3 */, + 52 /* 4 */, 53 /* 5 */, 54 /* 6 */, 55 /* 7 */, + 56 /* 8 */, 57 /* 9 */, 58 /* : */, 59 /* ; */, + 60 /* < */, 61 /* = */, 62 /* > */, 63 /* ? */, + 64 /* @ */, 97 /* A */, 98 /* B */, 99 /* C */, + 100 /* D */, 101 /* E */, 102 /* F */, 103 /* G */, + 104 /* H */, 105 /* I */, 106 /* J */, 107 /* K */, + 108 /* L */, 109 /* M */, 110 /* N */, 111 /* O */, + 112 /* P */, 113 /* Q */, 114 /* R */, 115 /* S */, + 116 /* T */, 117 /* U */, 118 /* V */, 119 /* W */, + 120 /* X */, 121 /* Y */, 122 /* Z */, 91 /* [ */, + 92 /* \ */, 93 /* ] */, 94 /* ^ */, 95 /* _ */, + 96 /* ` */, 97 /* a */, 98 /* b */, 99 /* c */, + 100 /* d */, 101 /* e */, 102 /* f */, 103 /* g */, + 104 /* h */, 105 /* i */, 106 /* j */, 107 /* k */, + 108 /* l */, 109 /* m */, 110 /* n */, 111 /* o */, + 112 /* p */, 113 /* q */, 114 /* r */, 115 /* s */, + 116 /* t */, 117 /* u */, 118 /* v */, 119 /* w */, + 120 /* x */, 121 /* y */, 122 /* z */, 123 /* { */, + 124 /* | */, 125 /* } */, 126 /* ~ */, 127 /* DEL */, + 128 /* 0x80 */, 129 /* 0x81 */, 130 /* 0x82 */, 131 /* 0x83 */, + 132 /* 0x84 */, 133 /* 0x85 */, 134 /* 0x86 */, 135 /* 0x87 */, + 136 /* 0x88 */, 137 /* 0x89 */, 138 /* 0x8a */, 139 /* 0x8b */, + 140 /* 0x8c */, 141 /* 0x8d */, 142 /* 0x8e */, 143 /* 0x8f */, + 144 /* 0x90 */, 145 /* 0x91 */, 146 /* 0x92 */, 147 /* 0x93 */, + 148 /* 0x94 */, 149 /* 0x95 */, 150 /* 0x96 */, 151 /* 0x97 */, + 152 /* 0x98 */, 153 /* 0x99 */, 154 /* 0x9a */, 155 /* 0x9b */, + 156 /* 0x9c */, 157 /* 0x9d */, 158 /* 0x9e */, 159 /* 0x9f */, + 160 /* 0xa0 */, 161 /* 0xa1 */, 162 /* 0xa2 */, 163 /* 0xa3 */, + 164 /* 0xa4 */, 165 /* 0xa5 */, 166 /* 0xa6 */, 167 /* 0xa7 */, + 168 /* 0xa8 */, 169 /* 0xa9 */, 170 /* 0xaa */, 171 /* 0xab */, + 172 /* 0xac */, 173 /* 0xad */, 174 /* 0xae */, 175 /* 0xaf */, + 176 /* 0xb0 */, 177 /* 0xb1 */, 178 /* 0xb2 */, 179 /* 0xb3 */, + 180 /* 0xb4 */, 181 /* 0xb5 */, 182 /* 0xb6 */, 183 /* 0xb7 */, + 184 /* 0xb8 */, 185 /* 0xb9 */, 186 /* 0xba */, 187 /* 0xbb */, + 188 /* 0xbc */, 189 /* 0xbd */, 190 /* 0xbe */, 191 /* 0xbf */, + 192 /* 0xc0 */, 193 /* 0xc1 */, 194 /* 0xc2 */, 195 /* 0xc3 */, + 196 /* 0xc4 */, 197 /* 0xc5 */, 198 /* 0xc6 */, 199 /* 0xc7 */, + 200 /* 0xc8 */, 201 /* 0xc9 */, 202 /* 0xca */, 203 /* 0xcb */, + 204 /* 0xcc */, 205 /* 0xcd */, 206 /* 0xce */, 207 /* 0xcf */, + 208 /* 0xd0 */, 209 /* 0xd1 */, 210 /* 0xd2 */, 211 /* 0xd3 */, + 212 /* 0xd4 */, 213 /* 0xd5 */, 214 /* 0xd6 */, 215 /* 0xd7 */, + 216 /* 0xd8 */, 217 /* 0xd9 */, 218 /* 0xda */, 219 /* 0xdb */, + 220 /* 0xdc */, 221 /* 0xdd */, 222 /* 0xde */, 223 /* 0xdf */, + 224 /* 0xe0 */, 225 /* 0xe1 */, 226 /* 0xe2 */, 227 /* 0xe3 */, + 228 /* 0xe4 */, 229 /* 0xe5 */, 230 /* 0xe6 */, 231 /* 0xe7 */, + 232 /* 0xe8 */, 233 /* 0xe9 */, 234 /* 0xea */, 235 /* 0xeb */, + 236 /* 0xec */, 237 /* 0xed */, 238 /* 0xee */, 239 /* 0xef */, + 240 /* 0xf0 */, 241 /* 0xf1 */, 242 /* 0xf2 */, 243 /* 0xf3 */, + 244 /* 0xf4 */, 245 /* 0xf5 */, 246 /* 0xf6 */, 247 /* 0xf7 */, + 248 /* 0xf8 */, 249 /* 0xf9 */, 250 /* 0xfa */, 251 /* 0xfb */, + 252 /* 0xfc */, 253 /* 0xfd */, 254 /* 0xfe */, 255 /* 0xff */, +}; + +void nghttp2_downcase(uint8_t *s, size_t len) { + size_t i; + for (i = 0; i < len; ++i) { + s[i] = DOWNCASE_TBL[s[i]]; + } +} + +/* + * local_window_size + * ^ * + * | * recv_window_size + * | * * ^ + * | * * | + * 0+++++++++ + * | * * \ + * | * * | This rage is hidden in flow control. But it must be + * v * * / kept in order to restore it when window size is enlarged. + * recv_reduction + * (+ for negative direction) + * + * recv_window_size could be negative if we decrease + * local_window_size more than recv_window_size: + * + * local_window_size + * ^ * + * | * + * | * + * 0++++++++ + * | * ^ recv_window_size (negative) + * | * | + * v * * + * recv_reduction + */ +int nghttp2_adjust_local_window_size(int32_t *local_window_size_ptr, + int32_t *recv_window_size_ptr, + int32_t *recv_reduction_ptr, + int32_t *delta_ptr) { + if (*delta_ptr > 0) { + int32_t recv_reduction_delta; + int32_t delta; + int32_t new_recv_window_size = + nghttp2_max(0, *recv_window_size_ptr) - *delta_ptr; + + if (new_recv_window_size >= 0) { + *recv_window_size_ptr = new_recv_window_size; + return 0; + } + + delta = -new_recv_window_size; + + /* The delta size is strictly more than received bytes. Increase + local_window_size by that difference |delta|. */ + if (*local_window_size_ptr > NGHTTP2_MAX_WINDOW_SIZE - delta) { + return NGHTTP2_ERR_FLOW_CONTROL; + } + *local_window_size_ptr += delta; + /* If there is recv_reduction due to earlier window_size + reduction, we have to adjust it too. */ + recv_reduction_delta = nghttp2_min(*recv_reduction_ptr, delta); + *recv_reduction_ptr -= recv_reduction_delta; + if (*recv_window_size_ptr < 0) { + *recv_window_size_ptr += recv_reduction_delta; + } else { + /* If *recv_window_size_ptr > 0, then those bytes are going to + be returned to the remote peer (by WINDOW_UPDATE with the + adjusted *delta_ptr), so it is effectively 0 now. We set to + *recv_reduction_delta, because caller does not take into + account it in *delta_ptr. */ + *recv_window_size_ptr = recv_reduction_delta; + } + /* recv_reduction_delta must be paied from *delta_ptr, since it + was added in window size reduction (see below). */ + *delta_ptr -= recv_reduction_delta; + + return 0; + } + + if (*local_window_size_ptr + *delta_ptr < 0 || + *recv_window_size_ptr < INT32_MIN - *delta_ptr || + *recv_reduction_ptr > INT32_MAX + *delta_ptr) { + return NGHTTP2_ERR_FLOW_CONTROL; + } + /* Decreasing local window size. Note that we achieve this without + noticing to the remote peer. To do this, we cut + recv_window_size by -delta. This means that we don't send + WINDOW_UPDATE for -delta bytes. */ + *local_window_size_ptr += *delta_ptr; + *recv_window_size_ptr += *delta_ptr; + *recv_reduction_ptr -= *delta_ptr; + *delta_ptr = 0; + + return 0; +} + +int nghttp2_should_send_window_update(int32_t local_window_size, + int32_t recv_window_size) { + return recv_window_size >= local_window_size / 2; +} + +const char *nghttp2_strerror(int error_code) { + switch (error_code) { + case 0: + return "Success"; + case NGHTTP2_ERR_INVALID_ARGUMENT: + return "Invalid argument"; + case NGHTTP2_ERR_BUFFER_ERROR: + return "Out of buffer space"; + case NGHTTP2_ERR_UNSUPPORTED_VERSION: + return "Unsupported SPDY version"; + case NGHTTP2_ERR_WOULDBLOCK: + return "Operation would block"; + case NGHTTP2_ERR_PROTO: + return "Protocol error"; + case NGHTTP2_ERR_INVALID_FRAME: + return "Invalid frame octets"; + case NGHTTP2_ERR_EOF: + return "EOF"; + case NGHTTP2_ERR_DEFERRED: + return "Data transfer deferred"; + case NGHTTP2_ERR_STREAM_ID_NOT_AVAILABLE: + return "No more Stream ID available"; + case NGHTTP2_ERR_STREAM_CLOSED: + return "Stream was already closed or invalid"; + case NGHTTP2_ERR_STREAM_CLOSING: + return "Stream is closing"; + case NGHTTP2_ERR_STREAM_SHUT_WR: + return "The transmission is not allowed for this stream"; + case NGHTTP2_ERR_INVALID_STREAM_ID: + return "Stream ID is invalid"; + case NGHTTP2_ERR_INVALID_STREAM_STATE: + return "Invalid stream state"; + case NGHTTP2_ERR_DEFERRED_DATA_EXIST: + return "Another DATA frame has already been deferred"; + case NGHTTP2_ERR_START_STREAM_NOT_ALLOWED: + return "request HEADERS is not allowed"; + case NGHTTP2_ERR_GOAWAY_ALREADY_SENT: + return "GOAWAY has already been sent"; + case NGHTTP2_ERR_INVALID_HEADER_BLOCK: + return "Invalid header block"; + case NGHTTP2_ERR_INVALID_STATE: + return "Invalid state"; + case NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE: + return "The user callback function failed due to the temporal error"; + case NGHTTP2_ERR_FRAME_SIZE_ERROR: + return "The length of the frame is invalid"; + case NGHTTP2_ERR_HEADER_COMP: + return "Header compression/decompression error"; + case NGHTTP2_ERR_FLOW_CONTROL: + return "Flow control error"; + case NGHTTP2_ERR_INSUFF_BUFSIZE: + return "Insufficient buffer size given to function"; + case NGHTTP2_ERR_PAUSE: + return "Callback was paused by the application"; + case NGHTTP2_ERR_TOO_MANY_INFLIGHT_SETTINGS: + return "Too many inflight SETTINGS"; + case NGHTTP2_ERR_PUSH_DISABLED: + return "Server push is disabled by peer"; + case NGHTTP2_ERR_DATA_EXIST: + return "DATA frame already exists"; + case NGHTTP2_ERR_SESSION_CLOSING: + return "The current session is closing"; + case NGHTTP2_ERR_HTTP_HEADER: + return "Invalid HTTP header field was received"; + case NGHTTP2_ERR_HTTP_MESSAGING: + return "Violation in HTTP messaging rule"; + case NGHTTP2_ERR_REFUSED_STREAM: + return "Stream was refused"; + case NGHTTP2_ERR_INTERNAL: + return "Internal error"; + case NGHTTP2_ERR_NOMEM: + return "Out of memory"; + case NGHTTP2_ERR_CALLBACK_FAILURE: + return "The user callback function failed"; + case NGHTTP2_ERR_BAD_CLIENT_MAGIC: + return "Received bad clinet magic byte string"; + default: + return "Unknown error code"; + } +} + +/* Generated by gennmchartbl.py */ +static int VALID_HD_NAME_CHARS[] = { + 0 /* NUL */, 0 /* SOH */, 0 /* STX */, 0 /* ETX */, 0 /* EOT */, + 0 /* ENQ */, 0 /* ACK */, 0 /* BEL */, 0 /* BS */, 0 /* HT */, + 0 /* LF */, 0 /* VT */, 0 /* FF */, 0 /* CR */, 0 /* SO */, + 0 /* SI */, 0 /* DLE */, 0 /* DC1 */, 0 /* DC2 */, 0 /* DC3 */, + 0 /* DC4 */, 0 /* NAK */, 0 /* SYN */, 0 /* ETB */, 0 /* CAN */, + 0 /* EM */, 0 /* SUB */, 0 /* ESC */, 0 /* FS */, 0 /* GS */, + 0 /* RS */, 0 /* US */, 0 /* SPC */, 1 /* ! */, 0 /* " */, + 1 /* # */, 1 /* $ */, 1 /* % */, 1 /* & */, 1 /* ' */, + 0 /* ( */, 0 /* ) */, 1 /* * */, 1 /* + */, 0 /* , */, + 1 /* - */, 1 /* . */, 0 /* / */, 1 /* 0 */, 1 /* 1 */, + 1 /* 2 */, 1 /* 3 */, 1 /* 4 */, 1 /* 5 */, 1 /* 6 */, + 1 /* 7 */, 1 /* 8 */, 1 /* 9 */, 0 /* : */, 0 /* ; */, + 0 /* < */, 0 /* = */, 0 /* > */, 0 /* ? */, 0 /* @ */, + 0 /* A */, 0 /* B */, 0 /* C */, 0 /* D */, 0 /* E */, + 0 /* F */, 0 /* G */, 0 /* H */, 0 /* I */, 0 /* J */, + 0 /* K */, 0 /* L */, 0 /* M */, 0 /* N */, 0 /* O */, + 0 /* P */, 0 /* Q */, 0 /* R */, 0 /* S */, 0 /* T */, + 0 /* U */, 0 /* V */, 0 /* W */, 0 /* X */, 0 /* Y */, + 0 /* Z */, 0 /* [ */, 0 /* \ */, 0 /* ] */, 1 /* ^ */, + 1 /* _ */, 1 /* ` */, 1 /* a */, 1 /* b */, 1 /* c */, + 1 /* d */, 1 /* e */, 1 /* f */, 1 /* g */, 1 /* h */, + 1 /* i */, 1 /* j */, 1 /* k */, 1 /* l */, 1 /* m */, + 1 /* n */, 1 /* o */, 1 /* p */, 1 /* q */, 1 /* r */, + 1 /* s */, 1 /* t */, 1 /* u */, 1 /* v */, 1 /* w */, + 1 /* x */, 1 /* y */, 1 /* z */, 0 /* { */, 1 /* | */, + 0 /* } */, 1 /* ~ */, 0 /* DEL */, 0 /* 0x80 */, 0 /* 0x81 */, + 0 /* 0x82 */, 0 /* 0x83 */, 0 /* 0x84 */, 0 /* 0x85 */, 0 /* 0x86 */, + 0 /* 0x87 */, 0 /* 0x88 */, 0 /* 0x89 */, 0 /* 0x8a */, 0 /* 0x8b */, + 0 /* 0x8c */, 0 /* 0x8d */, 0 /* 0x8e */, 0 /* 0x8f */, 0 /* 0x90 */, + 0 /* 0x91 */, 0 /* 0x92 */, 0 /* 0x93 */, 0 /* 0x94 */, 0 /* 0x95 */, + 0 /* 0x96 */, 0 /* 0x97 */, 0 /* 0x98 */, 0 /* 0x99 */, 0 /* 0x9a */, + 0 /* 0x9b */, 0 /* 0x9c */, 0 /* 0x9d */, 0 /* 0x9e */, 0 /* 0x9f */, + 0 /* 0xa0 */, 0 /* 0xa1 */, 0 /* 0xa2 */, 0 /* 0xa3 */, 0 /* 0xa4 */, + 0 /* 0xa5 */, 0 /* 0xa6 */, 0 /* 0xa7 */, 0 /* 0xa8 */, 0 /* 0xa9 */, + 0 /* 0xaa */, 0 /* 0xab */, 0 /* 0xac */, 0 /* 0xad */, 0 /* 0xae */, + 0 /* 0xaf */, 0 /* 0xb0 */, 0 /* 0xb1 */, 0 /* 0xb2 */, 0 /* 0xb3 */, + 0 /* 0xb4 */, 0 /* 0xb5 */, 0 /* 0xb6 */, 0 /* 0xb7 */, 0 /* 0xb8 */, + 0 /* 0xb9 */, 0 /* 0xba */, 0 /* 0xbb */, 0 /* 0xbc */, 0 /* 0xbd */, + 0 /* 0xbe */, 0 /* 0xbf */, 0 /* 0xc0 */, 0 /* 0xc1 */, 0 /* 0xc2 */, + 0 /* 0xc3 */, 0 /* 0xc4 */, 0 /* 0xc5 */, 0 /* 0xc6 */, 0 /* 0xc7 */, + 0 /* 0xc8 */, 0 /* 0xc9 */, 0 /* 0xca */, 0 /* 0xcb */, 0 /* 0xcc */, + 0 /* 0xcd */, 0 /* 0xce */, 0 /* 0xcf */, 0 /* 0xd0 */, 0 /* 0xd1 */, + 0 /* 0xd2 */, 0 /* 0xd3 */, 0 /* 0xd4 */, 0 /* 0xd5 */, 0 /* 0xd6 */, + 0 /* 0xd7 */, 0 /* 0xd8 */, 0 /* 0xd9 */, 0 /* 0xda */, 0 /* 0xdb */, + 0 /* 0xdc */, 0 /* 0xdd */, 0 /* 0xde */, 0 /* 0xdf */, 0 /* 0xe0 */, + 0 /* 0xe1 */, 0 /* 0xe2 */, 0 /* 0xe3 */, 0 /* 0xe4 */, 0 /* 0xe5 */, + 0 /* 0xe6 */, 0 /* 0xe7 */, 0 /* 0xe8 */, 0 /* 0xe9 */, 0 /* 0xea */, + 0 /* 0xeb */, 0 /* 0xec */, 0 /* 0xed */, 0 /* 0xee */, 0 /* 0xef */, + 0 /* 0xf0 */, 0 /* 0xf1 */, 0 /* 0xf2 */, 0 /* 0xf3 */, 0 /* 0xf4 */, + 0 /* 0xf5 */, 0 /* 0xf6 */, 0 /* 0xf7 */, 0 /* 0xf8 */, 0 /* 0xf9 */, + 0 /* 0xfa */, 0 /* 0xfb */, 0 /* 0xfc */, 0 /* 0xfd */, 0 /* 0xfe */, + 0 /* 0xff */ +}; + +int nghttp2_check_header_name(const uint8_t *name, size_t len) { + const uint8_t *last; + if (len == 0) { + return 0; + } + if (*name == ':') { + if (len == 1) { + return 0; + } + ++name; + --len; + } + for (last = name + len; name != last; ++name) { + if (!VALID_HD_NAME_CHARS[*name]) { + return 0; + } + } + return 1; +} + +/* Generated by genvchartbl.py */ +static int VALID_HD_VALUE_CHARS[] = { + 0 /* NUL */, 0 /* SOH */, 0 /* STX */, 0 /* ETX */, 0 /* EOT */, + 0 /* ENQ */, 0 /* ACK */, 0 /* BEL */, 0 /* BS */, 1 /* HT */, + 0 /* LF */, 0 /* VT */, 0 /* FF */, 0 /* CR */, 0 /* SO */, + 0 /* SI */, 0 /* DLE */, 0 /* DC1 */, 0 /* DC2 */, 0 /* DC3 */, + 0 /* DC4 */, 0 /* NAK */, 0 /* SYN */, 0 /* ETB */, 0 /* CAN */, + 0 /* EM */, 0 /* SUB */, 0 /* ESC */, 0 /* FS */, 0 /* GS */, + 0 /* RS */, 0 /* US */, 1 /* SPC */, 1 /* ! */, 1 /* " */, + 1 /* # */, 1 /* $ */, 1 /* % */, 1 /* & */, 1 /* ' */, + 1 /* ( */, 1 /* ) */, 1 /* * */, 1 /* + */, 1 /* , */, + 1 /* - */, 1 /* . */, 1 /* / */, 1 /* 0 */, 1 /* 1 */, + 1 /* 2 */, 1 /* 3 */, 1 /* 4 */, 1 /* 5 */, 1 /* 6 */, + 1 /* 7 */, 1 /* 8 */, 1 /* 9 */, 1 /* : */, 1 /* ; */, + 1 /* < */, 1 /* = */, 1 /* > */, 1 /* ? */, 1 /* @ */, + 1 /* A */, 1 /* B */, 1 /* C */, 1 /* D */, 1 /* E */, + 1 /* F */, 1 /* G */, 1 /* H */, 1 /* I */, 1 /* J */, + 1 /* K */, 1 /* L */, 1 /* M */, 1 /* N */, 1 /* O */, + 1 /* P */, 1 /* Q */, 1 /* R */, 1 /* S */, 1 /* T */, + 1 /* U */, 1 /* V */, 1 /* W */, 1 /* X */, 1 /* Y */, + 1 /* Z */, 1 /* [ */, 1 /* \ */, 1 /* ] */, 1 /* ^ */, + 1 /* _ */, 1 /* ` */, 1 /* a */, 1 /* b */, 1 /* c */, + 1 /* d */, 1 /* e */, 1 /* f */, 1 /* g */, 1 /* h */, + 1 /* i */, 1 /* j */, 1 /* k */, 1 /* l */, 1 /* m */, + 1 /* n */, 1 /* o */, 1 /* p */, 1 /* q */, 1 /* r */, + 1 /* s */, 1 /* t */, 1 /* u */, 1 /* v */, 1 /* w */, + 1 /* x */, 1 /* y */, 1 /* z */, 1 /* { */, 1 /* | */, + 1 /* } */, 1 /* ~ */, 0 /* DEL */, 1 /* 0x80 */, 1 /* 0x81 */, + 1 /* 0x82 */, 1 /* 0x83 */, 1 /* 0x84 */, 1 /* 0x85 */, 1 /* 0x86 */, + 1 /* 0x87 */, 1 /* 0x88 */, 1 /* 0x89 */, 1 /* 0x8a */, 1 /* 0x8b */, + 1 /* 0x8c */, 1 /* 0x8d */, 1 /* 0x8e */, 1 /* 0x8f */, 1 /* 0x90 */, + 1 /* 0x91 */, 1 /* 0x92 */, 1 /* 0x93 */, 1 /* 0x94 */, 1 /* 0x95 */, + 1 /* 0x96 */, 1 /* 0x97 */, 1 /* 0x98 */, 1 /* 0x99 */, 1 /* 0x9a */, + 1 /* 0x9b */, 1 /* 0x9c */, 1 /* 0x9d */, 1 /* 0x9e */, 1 /* 0x9f */, + 1 /* 0xa0 */, 1 /* 0xa1 */, 1 /* 0xa2 */, 1 /* 0xa3 */, 1 /* 0xa4 */, + 1 /* 0xa5 */, 1 /* 0xa6 */, 1 /* 0xa7 */, 1 /* 0xa8 */, 1 /* 0xa9 */, + 1 /* 0xaa */, 1 /* 0xab */, 1 /* 0xac */, 1 /* 0xad */, 1 /* 0xae */, + 1 /* 0xaf */, 1 /* 0xb0 */, 1 /* 0xb1 */, 1 /* 0xb2 */, 1 /* 0xb3 */, + 1 /* 0xb4 */, 1 /* 0xb5 */, 1 /* 0xb6 */, 1 /* 0xb7 */, 1 /* 0xb8 */, + 1 /* 0xb9 */, 1 /* 0xba */, 1 /* 0xbb */, 1 /* 0xbc */, 1 /* 0xbd */, + 1 /* 0xbe */, 1 /* 0xbf */, 1 /* 0xc0 */, 1 /* 0xc1 */, 1 /* 0xc2 */, + 1 /* 0xc3 */, 1 /* 0xc4 */, 1 /* 0xc5 */, 1 /* 0xc6 */, 1 /* 0xc7 */, + 1 /* 0xc8 */, 1 /* 0xc9 */, 1 /* 0xca */, 1 /* 0xcb */, 1 /* 0xcc */, + 1 /* 0xcd */, 1 /* 0xce */, 1 /* 0xcf */, 1 /* 0xd0 */, 1 /* 0xd1 */, + 1 /* 0xd2 */, 1 /* 0xd3 */, 1 /* 0xd4 */, 1 /* 0xd5 */, 1 /* 0xd6 */, + 1 /* 0xd7 */, 1 /* 0xd8 */, 1 /* 0xd9 */, 1 /* 0xda */, 1 /* 0xdb */, + 1 /* 0xdc */, 1 /* 0xdd */, 1 /* 0xde */, 1 /* 0xdf */, 1 /* 0xe0 */, + 1 /* 0xe1 */, 1 /* 0xe2 */, 1 /* 0xe3 */, 1 /* 0xe4 */, 1 /* 0xe5 */, + 1 /* 0xe6 */, 1 /* 0xe7 */, 1 /* 0xe8 */, 1 /* 0xe9 */, 1 /* 0xea */, + 1 /* 0xeb */, 1 /* 0xec */, 1 /* 0xed */, 1 /* 0xee */, 1 /* 0xef */, + 1 /* 0xf0 */, 1 /* 0xf1 */, 1 /* 0xf2 */, 1 /* 0xf3 */, 1 /* 0xf4 */, + 1 /* 0xf5 */, 1 /* 0xf6 */, 1 /* 0xf7 */, 1 /* 0xf8 */, 1 /* 0xf9 */, + 1 /* 0xfa */, 1 /* 0xfb */, 1 /* 0xfc */, 1 /* 0xfd */, 1 /* 0xfe */, + 1 /* 0xff */ +}; + +int nghttp2_check_header_value(const uint8_t *value, size_t len) { + const uint8_t *last; + for (last = value + len; value != last; ++value) { + if (!VALID_HD_VALUE_CHARS[*value]) { + return 0; + } + } + return 1; +} + +uint8_t *nghttp2_cpymem(uint8_t *dest, const void *src, size_t len) { + memcpy(dest, src, len); + + return dest + len; +} diff --git a/lib/nghttp2_helper.h b/lib/nghttp2_helper.h new file mode 100644 index 0000000..0a7f8c8 --- /dev/null +++ b/lib/nghttp2_helper.h @@ -0,0 +1,114 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGHTTP2_HELPER_H +#define NGHTTP2_HELPER_H + +#ifdef HAVE_CONFIG_H +#include +#endif /* HAVE_CONFIG_H */ + +#include + +#include +#include "nghttp2_mem.h" + +#define nghttp2_min(A, B) ((A) < (B) ? (A) : (B)) +#define nghttp2_max(A, B) ((A) > (B) ? (A) : (B)) + +#define lstreq(A, B, N) ((sizeof((A)) - 1) == (N) && memcmp((A), (B), (N)) == 0) + +/* + * Copies 2 byte unsigned integer |n| in host byte order to |buf| in + * network byte order. + */ +void nghttp2_put_uint16be(uint8_t *buf, uint16_t n); + +/* + * Copies 4 byte unsigned integer |n| in host byte order to |buf| in + * network byte order. + */ +void nghttp2_put_uint32be(uint8_t *buf, uint32_t n); + +/* + * Retrieves 2 byte unsigned integer stored in |data| in network byte + * order and returns it in host byte order. + */ +uint16_t nghttp2_get_uint16(const uint8_t *data); + +/* + * Retrieves 4 byte unsigned integer stored in |data| in network byte + * order and returns it in host byte order. + */ +uint32_t nghttp2_get_uint32(const uint8_t *data); + +/* + * Allocates |n| bytes of memory and copy the memory region pointed by + * |src| with the length |n| bytes into it. Returns the allocated memory. + * + * This function returns pointer to allocated memory, or one of the + * following negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + */ +void *nghttp2_memdup(const void *src, size_t n, nghttp2_mem *mem); + +void nghttp2_downcase(uint8_t *s, size_t len); + +/* + * Adjusts |*local_window_size_ptr|, |*recv_window_size_ptr|, + * |*recv_reduction_ptr| with |*delta_ptr| which is the + * WINDOW_UPDATE's window_size_increment sent from local side. If + * |delta| is strictly larger than |*recv_window_size_ptr|, + * |*local_window_size_ptr| is increased by delta - + * *recv_window_size_ptr. If |delta| is negative, + * |*local_window_size_ptr| is decreased by delta. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_FLOW_CONTROL + * local_window_size overflow or gets negative. + */ +int nghttp2_adjust_local_window_size(int32_t *local_window_size_ptr, + int32_t *recv_window_size_ptr, + int32_t *recv_reduction_ptr, + int32_t *delta_ptr); + +/* + * Returns non-zero if the function decided that WINDOW_UPDATE should + * be sent. + */ +int nghttp2_should_send_window_update(int32_t local_window_size, + int32_t recv_window_size); + +/* + * Copies the buffer |src| of length |len| to the destination pointed + * by the |dest|, assuming that the |dest| is at lest |len| bytes long + * . Returns dest + len. + */ +uint8_t *nghttp2_cpymem(uint8_t *dest, const void *src, size_t len); + +#endif /* NGHTTP2_HELPER_H */ diff --git a/lib/nghttp2_http.c b/lib/nghttp2_http.c new file mode 100644 index 0000000..695307e --- /dev/null +++ b/lib/nghttp2_http.c @@ -0,0 +1,459 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2015 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "nghttp2_http.h" + +#include +#include +#include + +#include "nghttp2_hd.h" +#include "nghttp2_helper.h" + +static char downcase(char c) { + return 'A' <= c && c <= 'Z' ? (c - 'A' + 'a') : c; +} + +static int memieq(const void *a, const void *b, size_t n) { + size_t i; + const uint8_t *aa = a, *bb = b; + + for (i = 0; i < n; ++i) { + if (downcase(aa[i]) != downcase(bb[i])) { + return 0; + } + } + return 1; +} + +#define lstrieq(A, B, N) ((sizeof((A)) - 1) == (N) && memieq((A), (B), (N))) + +static int64_t parse_uint(const uint8_t *s, size_t len) { + int64_t n = 0; + size_t i; + if (len == 0) { + return -1; + } + for (i = 0; i < len; ++i) { + if ('0' <= s[i] && s[i] <= '9') { + if (n > INT64_MAX / 10) { + return -1; + } + n *= 10; + if (n > INT64_MAX - (s[i] - '0')) { + return -1; + } + n += s[i] - '0'; + continue; + } + return -1; + } + return n; +} + +static int lws(const uint8_t *s, size_t n) { + size_t i; + for (i = 0; i < n; ++i) { + if (s[i] != ' ' && s[i] != '\t') { + return 0; + } + } + return 1; +} + +static int check_pseudo_header(nghttp2_stream *stream, const nghttp2_nv *nv, + int flag) { + if (stream->http_flags & flag) { + return 0; + } + if (lws(nv->value, nv->valuelen)) { + return 0; + } + stream->http_flags |= flag; + return 1; +} + +static int expect_response_body(nghttp2_stream *stream) { + return (stream->http_flags & NGHTTP2_HTTP_FLAG_METH_HEAD) == 0 && + stream->status_code / 100 != 1 && stream->status_code != 304 && + stream->status_code != 204; +} + +/* For "http" or "https" URIs, OPTIONS request may have "*" in :path + header field to represent system-wide OPTIONS request. Otherwise, + :path header field value must start with "/". This function must + be called after ":method" header field was received. This function + returns nonzero if path is valid.*/ +static int check_path(nghttp2_stream *stream) { + return (stream->http_flags & NGHTTP2_HTTP_FLAG_SCHEME_HTTP) == 0 || + ((stream->http_flags & NGHTTP2_HTTP_FLAG_PATH_REGULAR) || + ((stream->http_flags & NGHTTP2_HTTP_FLAG_METH_OPTIONS) && + (stream->http_flags & NGHTTP2_HTTP_FLAG_PATH_ASTERISK))); +} + +static int http_request_on_header(nghttp2_stream *stream, nghttp2_nv *nv, + int token, int trailer) { + if (nv->name[0] == ':') { + if (trailer || + (stream->http_flags & NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED)) { + return NGHTTP2_ERR_HTTP_HEADER; + } + } + + switch (token) { + case NGHTTP2_TOKEN__AUTHORITY: + if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__AUTHORITY)) { + return NGHTTP2_ERR_HTTP_HEADER; + } + break; + case NGHTTP2_TOKEN__METHOD: + if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__METHOD)) { + return NGHTTP2_ERR_HTTP_HEADER; + } + switch (nv->valuelen) { + case 4: + if (lstreq("HEAD", nv->value, nv->valuelen)) { + stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_HEAD; + } + break; + case 7: + switch (nv->value[6]) { + case 'T': + if (lstreq("CONNECT", nv->value, nv->valuelen)) { + if (stream->stream_id % 2 == 0) { + /* we won't allow CONNECT for push */ + return NGHTTP2_ERR_HTTP_HEADER; + } + stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_CONNECT; + if (stream->http_flags & + (NGHTTP2_HTTP_FLAG__PATH | NGHTTP2_HTTP_FLAG__SCHEME)) { + return NGHTTP2_ERR_HTTP_HEADER; + } + } + break; + case 'S': + if (lstreq("OPTIONS", nv->value, nv->valuelen)) { + stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_OPTIONS; + } + break; + } + break; + } + break; + case NGHTTP2_TOKEN__PATH: + if (stream->http_flags & NGHTTP2_HTTP_FLAG_METH_CONNECT) { + return NGHTTP2_ERR_HTTP_HEADER; + } + if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__PATH)) { + return NGHTTP2_ERR_HTTP_HEADER; + } + if (nv->value[0] == '/') { + stream->http_flags |= NGHTTP2_HTTP_FLAG_PATH_REGULAR; + } else if (nv->valuelen == 1 && nv->value[0] == '*') { + stream->http_flags |= NGHTTP2_HTTP_FLAG_PATH_ASTERISK; + } + break; + case NGHTTP2_TOKEN__SCHEME: + if (stream->http_flags & NGHTTP2_HTTP_FLAG_METH_CONNECT) { + return NGHTTP2_ERR_HTTP_HEADER; + } + if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__SCHEME)) { + return NGHTTP2_ERR_HTTP_HEADER; + } + if ((nv->valuelen == 4 && memieq("http", nv->value, 4)) || + (nv->valuelen == 5 && memieq("https", nv->value, 5))) { + stream->http_flags |= NGHTTP2_HTTP_FLAG_SCHEME_HTTP; + } + break; + case NGHTTP2_TOKEN_HOST: + if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG_HOST)) { + return NGHTTP2_ERR_HTTP_HEADER; + } + break; + case NGHTTP2_TOKEN_CONTENT_LENGTH: { + if (stream->content_length != -1) { + return NGHTTP2_ERR_HTTP_HEADER; + } + stream->content_length = parse_uint(nv->value, nv->valuelen); + if (stream->content_length == -1) { + return NGHTTP2_ERR_HTTP_HEADER; + } + break; + } + /* disallowed header fields */ + case NGHTTP2_TOKEN_CONNECTION: + case NGHTTP2_TOKEN_KEEP_ALIVE: + case NGHTTP2_TOKEN_PROXY_CONNECTION: + case NGHTTP2_TOKEN_TRANSFER_ENCODING: + case NGHTTP2_TOKEN_UPGRADE: + return NGHTTP2_ERR_HTTP_HEADER; + case NGHTTP2_TOKEN_TE: + if (!lstrieq("trailers", nv->value, nv->valuelen)) { + return NGHTTP2_ERR_HTTP_HEADER; + } + break; + default: + if (nv->name[0] == ':') { + return NGHTTP2_ERR_HTTP_HEADER; + } + } + + if (nv->name[0] != ':') { + stream->http_flags |= NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED; + } + + return 0; +} + +static int http_response_on_header(nghttp2_stream *stream, nghttp2_nv *nv, + int token, int trailer) { + if (nv->name[0] == ':') { + if (trailer || + (stream->http_flags & NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED)) { + return NGHTTP2_ERR_HTTP_HEADER; + } + } + + switch (token) { + case NGHTTP2_TOKEN__STATUS: { + if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__STATUS)) { + return NGHTTP2_ERR_HTTP_HEADER; + } + if (nv->valuelen != 3) { + return NGHTTP2_ERR_HTTP_HEADER; + } + stream->status_code = parse_uint(nv->value, nv->valuelen); + if (stream->status_code == -1) { + return NGHTTP2_ERR_HTTP_HEADER; + } + break; + } + case NGHTTP2_TOKEN_CONTENT_LENGTH: { + if (stream->content_length != -1) { + return NGHTTP2_ERR_HTTP_HEADER; + } + stream->content_length = parse_uint(nv->value, nv->valuelen); + if (stream->content_length == -1) { + return NGHTTP2_ERR_HTTP_HEADER; + } + break; + } + /* disallowed header fields */ + case NGHTTP2_TOKEN_CONNECTION: + case NGHTTP2_TOKEN_KEEP_ALIVE: + case NGHTTP2_TOKEN_PROXY_CONNECTION: + case NGHTTP2_TOKEN_TRANSFER_ENCODING: + case NGHTTP2_TOKEN_UPGRADE: + return NGHTTP2_ERR_HTTP_HEADER; + case NGHTTP2_TOKEN_TE: + if (!lstrieq("trailers", nv->value, nv->valuelen)) { + return NGHTTP2_ERR_HTTP_HEADER; + } + break; + default: + if (nv->name[0] == ':') { + return NGHTTP2_ERR_HTTP_HEADER; + } + } + + if (nv->name[0] != ':') { + stream->http_flags |= NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED; + } + + return 0; +} + +int nghttp2_http_on_header(nghttp2_session *session, nghttp2_stream *stream, + nghttp2_frame *frame, nghttp2_nv *nv, int token, + int trailer) { + /* We are strict for pseudo header field. One bad character should + lead to fail. OTOH, we should be a bit forgiving for regular + headers, since existing public internet has so much illegal + headers floating around and if we kill the stream because of + this, we may disrupt many web sites and/or libraries. So we + become conservative here, and just ignore those illegal regular + headers. */ + if (!nghttp2_check_header_name(nv->name, nv->namelen)) { + size_t i; + if (nv->namelen > 0 && nv->name[0] == ':') { + return NGHTTP2_ERR_HTTP_HEADER; + } + /* header field name must be lower-cased without exception */ + for (i = 0; i < nv->namelen; ++i) { + char c = nv->name[i]; + if ('A' <= c && c <= 'Z') { + return NGHTTP2_ERR_HTTP_HEADER; + } + } + /* When ignoring regular headers, we set this flag so that we + still enforce header field ordering rule for pseudo header + fields. */ + stream->http_flags |= NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED; + return NGHTTP2_ERR_IGN_HTTP_HEADER; + } + + if (!nghttp2_check_header_value(nv->value, nv->valuelen)) { + assert(nv->namelen > 0); + if (nv->name[0] == ':') { + return NGHTTP2_ERR_HTTP_HEADER; + } + /* When ignoring regular headers, we set this flag so that we + still enforce header field ordering rule for pseudo header + fields. */ + stream->http_flags |= NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED; + return NGHTTP2_ERR_IGN_HTTP_HEADER; + } + + if (session->server || frame->hd.type == NGHTTP2_PUSH_PROMISE) { + return http_request_on_header(stream, nv, token, trailer); + } + + return http_response_on_header(stream, nv, token, trailer); +} + +int nghttp2_http_on_request_headers(nghttp2_stream *stream, + nghttp2_frame *frame) { + if (stream->http_flags & NGHTTP2_HTTP_FLAG_METH_CONNECT) { + if ((stream->http_flags & NGHTTP2_HTTP_FLAG__AUTHORITY) == 0) { + return -1; + } + stream->content_length = -1; + } else { + if ((stream->http_flags & NGHTTP2_HTTP_FLAG_REQ_HEADERS) != + NGHTTP2_HTTP_FLAG_REQ_HEADERS || + (stream->http_flags & + (NGHTTP2_HTTP_FLAG__AUTHORITY | NGHTTP2_HTTP_FLAG_HOST)) == 0) { + return -1; + } + if (!check_path(stream)) { + return -1; + } + } + + if (frame->hd.type == NGHTTP2_PUSH_PROMISE) { + /* we are going to reuse data fields for upcoming response. Clear + them now, except for method flags. */ + stream->http_flags &= NGHTTP2_HTTP_FLAG_METH_ALL; + stream->content_length = -1; + } + + return 0; +} + +int nghttp2_http_on_response_headers(nghttp2_stream *stream) { + if ((stream->http_flags & NGHTTP2_HTTP_FLAG__STATUS) == 0) { + return -1; + } + + if (stream->status_code / 100 == 1) { + /* non-final response */ + stream->http_flags = (stream->http_flags & NGHTTP2_HTTP_FLAG_METH_ALL) | + NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE; + stream->content_length = -1; + stream->status_code = -1; + return 0; + } + + stream->http_flags &= ~NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE; + + if (!expect_response_body(stream)) { + stream->content_length = 0; + } else if (stream->http_flags & NGHTTP2_HTTP_FLAG_METH_CONNECT) { + stream->content_length = -1; + } + + return 0; +} + +int nghttp2_http_on_trailer_headers(nghttp2_stream *stream _U_, + nghttp2_frame *frame) { + if ((frame->hd.flags & NGHTTP2_FLAG_END_STREAM) == 0) { + return -1; + } + + return 0; +} + +int nghttp2_http_on_remote_end_stream(nghttp2_stream *stream) { + if (stream->http_flags & NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE) { + return -1; + } + + if (stream->content_length != -1 && + stream->content_length != stream->recv_content_length) { + return -1; + } + + return 0; +} + +int nghttp2_http_on_data_chunk(nghttp2_stream *stream, size_t n) { + stream->recv_content_length += n; + + if ((stream->http_flags & NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE) || + (stream->content_length != -1 && + stream->recv_content_length > stream->content_length)) { + return -1; + } + + return 0; +} + +void nghttp2_http_record_request_method(nghttp2_stream *stream, + nghttp2_frame *frame) { + const nghttp2_nv *nva; + size_t nvlen; + size_t i; + + switch (frame->hd.type) { + case NGHTTP2_HEADERS: + nva = frame->headers.nva; + nvlen = frame->headers.nvlen; + break; + case NGHTTP2_PUSH_PROMISE: + nva = frame->push_promise.nva; + nvlen = frame->push_promise.nvlen; + break; + default: + return; + } + + /* TODO we should do this strictly. */ + for (i = 0; i < nvlen; ++i) { + const nghttp2_nv *nv = &nva[i]; + if (!(nv->namelen == 7 && nv->name[6] == 'd' && + memcmp(":metho", nv->name, nv->namelen - 1) == 0)) { + continue; + } + if (lstreq("CONNECT", nv->value, nv->valuelen)) { + stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_CONNECT; + return; + } + if (lstreq("HEAD", nv->value, nv->valuelen)) { + stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_HEAD; + return; + } + return; + } +} diff --git a/lib/nghttp2_http.h b/lib/nghttp2_http.h new file mode 100644 index 0000000..f782058 --- /dev/null +++ b/lib/nghttp2_http.h @@ -0,0 +1,98 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2015 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGHTTP2_HTTP_H +#define NGHTTP2_HTTP_H + +#ifdef HAVE_CONFIG_H +#include +#endif /* HAVE_CONFIG_H */ + +#include +#include "nghttp2_session.h" +#include "nghttp2_stream.h" + +/* + * This function is called when HTTP header field |nv| in |frame| is + * received for |stream|. This function will validate |nv| against + * the current state of stream. The |token| is nghttp2_token value + * for nv->name, or -1 if we don't have enum value for the name. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_HTTP_HEADER + * Invalid HTTP header field was received. + * NGHTTP2_ERR_IGN_HTTP_HEADER + * Invalid HTTP header field was received but it can be treated as + * if it was not received because of compatibility reasons. + */ +int nghttp2_http_on_header(nghttp2_session *session, nghttp2_stream *stream, + nghttp2_frame *frame, nghttp2_nv *nv, int token, + int trailer); + +/* + * This function is called when request header is received. This + * function performs validation and returns 0 if it succeeds, or -1. + */ +int nghttp2_http_on_request_headers(nghttp2_stream *stream, + nghttp2_frame *frame); + +/* + * This function is called when response header is received. This + * function performs validation and returns 0 if it succeeds, or -1. + */ +int nghttp2_http_on_response_headers(nghttp2_stream *stream); + +/* + * This function is called trailer header (for both request and + * response) is received. This function performs validation and + * returns 0 if it succeeds, or -1. + */ +int nghttp2_http_on_trailer_headers(nghttp2_stream *stream, + nghttp2_frame *frame); + +/* + * This function is called when END_STREAM flag is seen in incoming + * frame. This function performs validation and returns 0 if it + * succeeds, or -1. + */ +int nghttp2_http_on_remote_end_stream(nghttp2_stream *stream); + +/* + * This function is called when chunk of data is received. This + * function performs validation and returns 0 if it succeeds, or -1. + */ +int nghttp2_http_on_data_chunk(nghttp2_stream *stream, size_t n); + +/* + * This function inspects header field in |frame| and records its + * method in stream->http_flags. If frame->hd.type is neither + * NGHTTP2_HEADERS nor NGHTTP2_PUSH_PROMISE, this function does + * nothing. + */ +void nghttp2_http_record_request_method(nghttp2_stream *stream, + nghttp2_frame *frame); + +#endif /* NGHTTP2_HTTP_H */ diff --git a/lib/nghttp2_int.h b/lib/nghttp2_int.h new file mode 100644 index 0000000..c26c8e9 --- /dev/null +++ b/lib/nghttp2_int.h @@ -0,0 +1,58 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGHTTP2_INT_H +#define NGHTTP2_INT_H + +#ifdef HAVE_CONFIG_H +#include +#endif /* HAVE_CONFIG_H */ + +/* Macros, types and constants for internal use */ + +#ifdef DEBUGBUILD +#define DEBUGF(x) x +#else +#define DEBUGF(x) \ + do { \ + } while (0) +#endif + +/* "less" function, return nonzero if |lhs| is less than |rhs|. */ +typedef int (*nghttp2_less)(const void *lhs, const void *rhs); + +/* Internal error code. They must be in the range [-499, -100], + inclusive. */ +typedef enum { + NGHTTP2_ERR_CREDENTIAL_PENDING = -101, + NGHTTP2_ERR_IGN_HEADER_BLOCK = -103, + NGHTTP2_ERR_IGN_PAYLOAD = -104, + /* + * Invalid HTTP header field was received but it can be treated as + * if it was not received because of compatibility reasons. + */ + NGHTTP2_ERR_IGN_HTTP_HEADER = -105 +} nghttp2_internal_error; + +#endif /* NGHTTP2_INT_H */ diff --git a/lib/nghttp2_map.c b/lib/nghttp2_map.c new file mode 100644 index 0000000..5a38ee2 --- /dev/null +++ b/lib/nghttp2_map.c @@ -0,0 +1,190 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "nghttp2_map.h" + +#include + +#define INITIAL_TABLE_LENGTH 256 + +int nghttp2_map_init(nghttp2_map *map, nghttp2_mem *mem) { + map->mem = mem; + map->tablelen = INITIAL_TABLE_LENGTH; + map->table = + nghttp2_mem_calloc(mem, map->tablelen, sizeof(nghttp2_map_entry *)); + if (map->table == NULL) { + return NGHTTP2_ERR_NOMEM; + } + + map->size = 0; + + return 0; +} + +void nghttp2_map_free(nghttp2_map *map) { + nghttp2_mem_free(map->mem, map->table); +} + +void nghttp2_map_each_free(nghttp2_map *map, + int (*func)(nghttp2_map_entry *entry, void *ptr), + void *ptr) { + size_t i; + for (i = 0; i < map->tablelen; ++i) { + nghttp2_map_entry *entry; + for (entry = map->table[i]; entry;) { + nghttp2_map_entry *next = entry->next; + func(entry, ptr); + entry = next; + } + map->table[i] = NULL; + } +} + +int nghttp2_map_each(nghttp2_map *map, + int (*func)(nghttp2_map_entry *entry, void *ptr), + void *ptr) { + int rv; + size_t i; + for (i = 0; i < map->tablelen; ++i) { + nghttp2_map_entry *entry; + for (entry = map->table[i]; entry; entry = entry->next) { + rv = func(entry, ptr); + if (rv != 0) { + return rv; + } + } + } + return 0; +} + +void nghttp2_map_entry_init(nghttp2_map_entry *entry, key_type key) { + entry->key = key; + entry->next = NULL; +} + +/* Same hash function in android HashMap source code. */ +/* The |mod| must be power of 2 */ +static int32_t hash(int32_t h, size_t mod) { + h ^= (h >> 20) ^ (h >> 12); + h ^= (h >> 7) ^ (h >> 4); + return h & (mod - 1); +} + +static int insert(nghttp2_map_entry **table, size_t tablelen, + nghttp2_map_entry *entry) { + int32_t h = hash(entry->key, tablelen); + if (table[h] == NULL) { + table[h] = entry; + } else { + nghttp2_map_entry *p; + /* We won't allow duplicated key, so check it out. */ + for (p = table[h]; p; p = p->next) { + if (p->key == entry->key) { + return NGHTTP2_ERR_INVALID_ARGUMENT; + } + } + entry->next = table[h]; + table[h] = entry; + } + return 0; +} + +/* new_tablelen must be power of 2 */ +static int resize(nghttp2_map *map, size_t new_tablelen) { + size_t i; + nghttp2_map_entry **new_table; + + new_table = + nghttp2_mem_calloc(map->mem, new_tablelen, sizeof(nghttp2_map_entry *)); + if (new_table == NULL) { + return NGHTTP2_ERR_NOMEM; + } + + for (i = 0; i < map->tablelen; ++i) { + nghttp2_map_entry *entry; + for (entry = map->table[i]; entry;) { + nghttp2_map_entry *next = entry->next; + entry->next = NULL; + /* This function must succeed */ + insert(new_table, new_tablelen, entry); + entry = next; + } + } + nghttp2_mem_free(map->mem, map->table); + map->tablelen = new_tablelen; + map->table = new_table; + + return 0; +} + +int nghttp2_map_insert(nghttp2_map *map, nghttp2_map_entry *new_entry) { + int rv; + /* Load factor is 0.75 */ + if ((map->size + 1) * 4 > map->tablelen * 3) { + rv = resize(map, map->tablelen * 2); + if (rv != 0) { + return rv; + } + } + rv = insert(map->table, map->tablelen, new_entry); + if (rv != 0) { + return rv; + } + ++map->size; + return 0; +} + +nghttp2_map_entry *nghttp2_map_find(nghttp2_map *map, key_type key) { + int32_t h; + nghttp2_map_entry *entry; + h = hash(key, map->tablelen); + for (entry = map->table[h]; entry; entry = entry->next) { + if (entry->key == key) { + return entry; + } + } + return NULL; +} + +int nghttp2_map_remove(nghttp2_map *map, key_type key) { + int32_t h; + nghttp2_map_entry *entry, *prev; + h = hash(key, map->tablelen); + prev = NULL; + for (entry = map->table[h]; entry; entry = entry->next) { + if (entry->key == key) { + if (prev == NULL) { + map->table[h] = entry->next; + } else { + prev->next = entry->next; + } + --map->size; + return 0; + } + prev = entry; + } + return NGHTTP2_ERR_INVALID_ARGUMENT; +} + +size_t nghttp2_map_size(nghttp2_map *map) { return map->size; } diff --git a/lib/nghttp2_map.h b/lib/nghttp2_map.h new file mode 100644 index 0000000..83f425d --- /dev/null +++ b/lib/nghttp2_map.h @@ -0,0 +1,144 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGHTTP2_MAP_H +#define NGHTTP2_MAP_H + +#ifdef HAVE_CONFIG_H +#include +#endif /* HAVE_CONFIG_H */ + +#include +#include "nghttp2_int.h" +#include "nghttp2_mem.h" + +/* Implementation of unordered map */ + +typedef uint32_t key_type; + +typedef struct nghttp2_map_entry { + struct nghttp2_map_entry *next; + key_type key; +#if SIZEOF_INT_P == 4 + /* we requires 8 bytes aligment */ + int64_t pad; +#endif +} nghttp2_map_entry; + +typedef struct { + nghttp2_map_entry **table; + nghttp2_mem *mem; + size_t tablelen; + size_t size; +} nghttp2_map; + +/* + * Initializes the map |map|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory + */ +int nghttp2_map_init(nghttp2_map *map, nghttp2_mem *mem); + +/* + * Deallocates any resources allocated for |map|. The stored entries + * are not freed by this function. Use nghttp2_map_each_free() to free + * each entries. + */ +void nghttp2_map_free(nghttp2_map *map); + +/* + * Deallocates each entries using |func| function and any resources + * allocated for |map|. The |func| function is responsible for freeing + * given the |entry| object. The |ptr| will be passed to the |func| as + * send argument. The return value of the |func| will be ignored. + */ +void nghttp2_map_each_free(nghttp2_map *map, + int (*func)(nghttp2_map_entry *entry, void *ptr), + void *ptr); + +/* + * Initializes the |entry| with the |key|. All entries to be inserted + * to the map must be initialized with this function. + */ +void nghttp2_map_entry_init(nghttp2_map_entry *entry, key_type key); + +/* + * Inserts the new |entry| with the key |entry->key| to the map |map|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_INVALID_ARGUMENT + * The item associated by |key| already exists. + * NGHTTP2_ERR_NOMEM + * Out of memory + */ +int nghttp2_map_insert(nghttp2_map *map, nghttp2_map_entry *entry); + +/* + * Returns the entry associated by the key |key|. If there is no such + * entry, this function returns NULL. + */ +nghttp2_map_entry *nghttp2_map_find(nghttp2_map *map, key_type key); + +/* + * Removes the entry associated by the key |key| from the |map|. The + * removed entry is not freed by this function. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_INVALID_ARGUMENT + * The entry associated by |key| does not exist. + */ +int nghttp2_map_remove(nghttp2_map *map, key_type key); + +/* + * Returns the number of items stored in the map |map|. + */ +size_t nghttp2_map_size(nghttp2_map *map); + +/* + * Applies the function |func| to each entry in the |map| with the + * optional user supplied pointer |ptr|. + * + * If the |func| returns 0, this function calls the |func| with the + * next entry. If the |func| returns nonzero, it will not call the + * |func| for further entries and return the return value of the + * |func| immediately. Thus, this function returns 0 if all the + * invocations of the |func| return 0, or nonzero value which the last + * invocation of |func| returns. + * + * Don't use this function to free each entry. Use + * nghttp2_map_each_free() instead. + */ +int nghttp2_map_each(nghttp2_map *map, + int (*func)(nghttp2_map_entry *entry, void *ptr), + void *ptr); + +#endif /* NGHTTP2_MAP_H */ diff --git a/lib/nghttp2_mem.c b/lib/nghttp2_mem.c new file mode 100644 index 0000000..e7d5aae --- /dev/null +++ b/lib/nghttp2_mem.c @@ -0,0 +1,61 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2014 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "nghttp2_mem.h" + +static void *default_malloc(size_t size, void *mem_user_data _U_) { + return malloc(size); +} + +static void default_free(void *ptr, void *mem_user_data _U_) { free(ptr); } + +static void *default_calloc(size_t nmemb, size_t size, + void *mem_user_data _U_) { + return calloc(nmemb, size); +} + +static void *default_realloc(void *ptr, size_t size, void *mem_user_data _U_) { + return realloc(ptr, size); +} + +static nghttp2_mem mem_default = {NULL, default_malloc, default_free, + default_calloc, default_realloc}; + +nghttp2_mem *nghttp2_mem_default(void) { return &mem_default; } + +void *nghttp2_mem_malloc(nghttp2_mem *mem, size_t size) { + return mem->malloc(size, mem->mem_user_data); +} + +void nghttp2_mem_free(nghttp2_mem *mem, void *ptr) { + mem->free(ptr, mem->mem_user_data); +} + +void *nghttp2_mem_calloc(nghttp2_mem *mem, size_t nmemb, size_t size) { + return mem->calloc(nmemb, size, mem->mem_user_data); +} + +void *nghttp2_mem_realloc(nghttp2_mem *mem, void *ptr, size_t size) { + return mem->realloc(ptr, size, mem->mem_user_data); +} diff --git a/lib/nghttp2_mem.h b/lib/nghttp2_mem.h new file mode 100644 index 0000000..d1fded4 --- /dev/null +++ b/lib/nghttp2_mem.h @@ -0,0 +1,44 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2014 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGHTTP2_MEM_H +#define NGHTTP2_MEM_H + +#ifdef HAVE_CONFIG_H +#include +#endif /* HAVE_CONFIG_H */ + +#include + +/* The default, system standard memory allocator */ +nghttp2_mem *nghttp2_mem_default(void); + +/* Convenient wrapper functions to call allocator function in + |mem|. */ +void *nghttp2_mem_malloc(nghttp2_mem *mem, size_t size); +void nghttp2_mem_free(nghttp2_mem *mem, void *ptr); +void *nghttp2_mem_calloc(nghttp2_mem *mem, size_t nmemb, size_t size); +void *nghttp2_mem_realloc(nghttp2_mem *mem, void *ptr, size_t size); + +#endif /* NGHTTP2_MEM_H */ diff --git a/lib/nghttp2_net.h b/lib/nghttp2_net.h new file mode 100644 index 0000000..587f418 --- /dev/null +++ b/lib/nghttp2_net.h @@ -0,0 +1,91 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGHTTP2_NET_H +#define NGHTTP2_NET_H + +#ifdef HAVE_CONFIG_H +#include +#endif /* HAVE_CONFIG_H */ + +#ifdef HAVE_ARPA_INET_H +#include +#endif /* HAVE_ARPA_INET_H */ + +#ifdef HAVE_NETINET_IN_H +#include +#endif /* HAVE_NETINET_IN_H */ + +#include + +#if defined(WIN32) +/* Windows requires ws2_32 library for ntonl family functions. We + define inline functions for those function so that we don't have + dependeny on that lib. */ + +#ifdef _MSC_VER +#define STIN static __inline +#else +#define STIN static inline +#endif + +STIN uint32_t htonl(uint32_t hostlong) { + uint32_t res; + unsigned char *p = (unsigned char *)&res; + *p++ = hostlong >> 24; + *p++ = (hostlong >> 16) & 0xffu; + *p++ = (hostlong >> 8) & 0xffu; + *p = hostlong & 0xffu; + return res; +} + +STIN uint16_t htons(uint16_t hostshort) { + uint16_t res; + unsigned char *p = (unsigned char *)&res; + *p++ = hostshort >> 8; + *p = hostshort & 0xffu; + return res; +} + +STIN uint32_t ntohl(uint32_t netlong) { + uint32_t res; + unsigned char *p = (unsigned char *)&netlong; + res = *p++ << 24; + res += *p++ << 16; + res += *p++ << 8; + res += *p; + return res; +} + +STIN uint16_t ntohs(uint16_t netshort) { + uint16_t res; + unsigned char *p = (unsigned char *)&netshort; + res = *p++ << 8; + res += *p; + return res; +} + +#endif /* WIN32 */ + +#endif /* NGHTTP2_NET_H */ diff --git a/lib/nghttp2_npn.c b/lib/nghttp2_npn.c new file mode 100644 index 0000000..a8bdb23 --- /dev/null +++ b/lib/nghttp2_npn.c @@ -0,0 +1,57 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "nghttp2_npn.h" + +#include + +static int select_next_protocol(unsigned char **out, unsigned char *outlen, + const unsigned char *in, unsigned int inlen, + const char *key, unsigned int keylen) { + unsigned int i; + for (i = 0; i + keylen <= inlen; i += in [i] + 1) { + if (memcmp(&in[i], key, keylen) == 0) { + *out = (unsigned char *)&in[i + 1]; + *outlen = in[i]; + return 0; + } + } + return -1; +} + +#define NGHTTP2_HTTP_1_1_ALPN "\x8http/1.1" +#define NGHTTP2_HTTP_1_1_ALPN_LEN (sizeof(NGHTTP2_HTTP_1_1_ALPN) - 1) + +int nghttp2_select_next_protocol(unsigned char **out, unsigned char *outlen, + const unsigned char *in, unsigned int inlen) { + if (select_next_protocol(out, outlen, in, inlen, NGHTTP2_PROTO_ALPN, + NGHTTP2_PROTO_ALPN_LEN) == 0) { + return 1; + } + if (select_next_protocol(out, outlen, in, inlen, NGHTTP2_HTTP_1_1_ALPN, + NGHTTP2_HTTP_1_1_ALPN_LEN) == 0) { + return 0; + } + return -1; +} diff --git a/lib/nghttp2_npn.h b/lib/nghttp2_npn.h new file mode 100644 index 0000000..c4bdedb --- /dev/null +++ b/lib/nghttp2_npn.h @@ -0,0 +1,34 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGHTTP2_NPN_H +#define NGHTTP2_NPN_H + +#ifdef HAVE_CONFIG +#include +#endif /* HAVE_CONFIG */ + +#include + +#endif /* NGHTTP2_NPN_H */ diff --git a/lib/nghttp2_option.c b/lib/nghttp2_option.c new file mode 100644 index 0000000..a45b5e0 --- /dev/null +++ b/lib/nghttp2_option.c @@ -0,0 +1,58 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2014 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "nghttp2_option.h" + +int nghttp2_option_new(nghttp2_option **option_ptr) { + *option_ptr = calloc(1, sizeof(nghttp2_option)); + + if (*option_ptr == NULL) { + return NGHTTP2_ERR_NOMEM; + } + + return 0; +} + +void nghttp2_option_del(nghttp2_option *option) { free(option); } + +void nghttp2_option_set_no_auto_window_update(nghttp2_option *option, int val) { + option->opt_set_mask |= NGHTTP2_OPT_NO_AUTO_WINDOW_UPDATE; + option->no_auto_window_update = val; +} + +void nghttp2_option_set_peer_max_concurrent_streams(nghttp2_option *option, + uint32_t val) { + option->opt_set_mask |= NGHTTP2_OPT_PEER_MAX_CONCURRENT_STREAMS; + option->peer_max_concurrent_streams = val; +} + +void nghttp2_option_set_no_recv_client_magic(nghttp2_option *option, int val) { + option->opt_set_mask |= NGHTTP2_OPT_NO_RECV_CLIENT_MAGIC; + option->no_recv_client_magic = val; +} + +void nghttp2_option_set_no_http_messaging(nghttp2_option *option, int val) { + option->opt_set_mask |= NGHTTP2_OPT_NO_HTTP_MESSAGING; + option->no_http_messaging = val; +} diff --git a/lib/nghttp2_option.h b/lib/nghttp2_option.h new file mode 100644 index 0000000..739bd85 --- /dev/null +++ b/lib/nghttp2_option.h @@ -0,0 +1,91 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2014 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGHTTP2_OPTION_H +#define NGHTTP2_OPTION_H + +#ifdef HAVE_CONFIG_H +#include +#endif /* HAVE_CONFIG_H */ + +#include + +/** + * Configuration options + */ +typedef enum { + /** + * This option prevents the library from sending WINDOW_UPDATE for a + * connection automatically. If this option is set to nonzero, the + * library won't send WINDOW_UPDATE for DATA until application calls + * nghttp2_session_consume() to indicate the amount of consumed + * DATA. By default, this option is set to zero. + */ + NGHTTP2_OPT_NO_AUTO_WINDOW_UPDATE = 1, + /** + * This option sets the SETTINGS_MAX_CONCURRENT_STREAMS value of + * remote endpoint as if it is received in SETTINGS frame. Without + * specifying this option, before the local endpoint receives + * SETTINGS_MAX_CONCURRENT_STREAMS in SETTINGS frame from remote + * endpoint, SETTINGS_MAX_CONCURRENT_STREAMS is unlimited. This may + * cause problem if local endpoint submits lots of requests + * initially and sending them at once to the remote peer may lead to + * the rejection of some requests. Specifying this option to the + * sensible value, say 100, may avoid this kind of issue. This value + * will be overwritten if the local endpoint receives + * SETTINGS_MAX_CONCURRENT_STREAMS from the remote endpoint. + */ + NGHTTP2_OPT_PEER_MAX_CONCURRENT_STREAMS = 1 << 1, + NGHTTP2_OPT_NO_RECV_CLIENT_MAGIC = 1 << 2, + NGHTTP2_OPT_NO_HTTP_MESSAGING = 1 << 3 +} nghttp2_option_flag; + +/** + * Struct to store option values for nghttp2_session. + */ +struct nghttp2_option { + /** + * Bitwise OR of nghttp2_option_flag to determine that which fields + * are specified. + */ + uint32_t opt_set_mask; + /** + * NGHTTP2_OPT_PEER_MAX_CONCURRENT_STREAMS + */ + uint32_t peer_max_concurrent_streams; + /** + * NGHTTP2_OPT_NO_AUTO_WINDOW_UPDATE + */ + uint8_t no_auto_window_update; + /** + * NGHTTP2_OPT_NO_RECV_CLIENT_MAGIC + */ + uint8_t no_recv_client_magic; + /** + * NGHTTP2_OPT_NO_HTTP_MESSAGING + */ + uint8_t no_http_messaging; +}; + +#endif /* NGHTTP2_OPTION_H */ diff --git a/lib/nghttp2_outbound_item.c b/lib/nghttp2_outbound_item.c new file mode 100644 index 0000000..c4ecab9 --- /dev/null +++ b/lib/nghttp2_outbound_item.c @@ -0,0 +1,105 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "nghttp2_outbound_item.h" + +#include +#include + +void nghttp2_outbound_item_init(nghttp2_outbound_item *item) { + item->cycle = 0; + item->qnext = NULL; + item->queued = 0; + + memset(&item->aux_data, 0, sizeof(nghttp2_aux_data)); +} + +void nghttp2_outbound_item_free(nghttp2_outbound_item *item, nghttp2_mem *mem) { + nghttp2_frame *frame; + + if (item == NULL) { + return; + } + + frame = &item->frame; + + switch (frame->hd.type) { + case NGHTTP2_DATA: + nghttp2_frame_data_free(&frame->data); + break; + case NGHTTP2_HEADERS: + nghttp2_frame_headers_free(&frame->headers, mem); + break; + case NGHTTP2_PRIORITY: + nghttp2_frame_priority_free(&frame->priority); + break; + case NGHTTP2_RST_STREAM: + nghttp2_frame_rst_stream_free(&frame->rst_stream); + break; + case NGHTTP2_SETTINGS: + nghttp2_frame_settings_free(&frame->settings, mem); + break; + case NGHTTP2_PUSH_PROMISE: + nghttp2_frame_push_promise_free(&frame->push_promise, mem); + break; + case NGHTTP2_PING: + nghttp2_frame_ping_free(&frame->ping); + break; + case NGHTTP2_GOAWAY: + nghttp2_frame_goaway_free(&frame->goaway, mem); + break; + case NGHTTP2_WINDOW_UPDATE: + nghttp2_frame_window_update_free(&frame->window_update); + break; + } +} + +void nghttp2_outbound_queue_init(nghttp2_outbound_queue *q) { + q->head = q->tail = NULL; + q->n = 0; +} + +void nghttp2_outbound_queue_push(nghttp2_outbound_queue *q, + nghttp2_outbound_item *item) { + if (q->tail) { + q->tail = q->tail->qnext = item; + } else { + q->head = q->tail = item; + } + ++q->n; +} + +void nghttp2_outbound_queue_pop(nghttp2_outbound_queue *q) { + nghttp2_outbound_item *item; + if (!q->head) { + return; + } + item = q->head; + q->head = q->head->qnext; + item->qnext = NULL; + if (!q->head) { + q->tail = NULL; + } + --q->n; +} diff --git a/lib/nghttp2_outbound_item.h b/lib/nghttp2_outbound_item.h new file mode 100644 index 0000000..0be5cb7 --- /dev/null +++ b/lib/nghttp2_outbound_item.h @@ -0,0 +1,157 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGHTTP2_OUTBOUND_ITEM_H +#define NGHTTP2_OUTBOUND_ITEM_H + +#ifdef HAVE_CONFIG_H +#include +#endif /* HAVE_CONFIG_H */ + +#include +#include "nghttp2_frame.h" +#include "nghttp2_mem.h" + +/* struct used for HEADERS and PUSH_PROMISE frame */ +typedef struct { + nghttp2_data_provider data_prd; + void *stream_user_data; + /* error code when request HEADERS is canceled by RST_STREAM while + it is in queue. */ + uint32_t error_code; + /* nonzero if request HEADERS is canceled. The error code is stored + in |error_code|. */ + uint8_t canceled; + /* nonzero if this item should be attached to stream object to make + it under priority control */ + uint8_t attach_stream; +} nghttp2_headers_aux_data; + +/* struct used for DATA frame */ +typedef struct { + /** + * The data to be sent for this DATA frame. + */ + nghttp2_data_provider data_prd; + /** + * The flags of DATA frame. We use separate flags here and + * nghttp2_data frame. The latter contains flags actually sent to + * peer. This |flags| may contain NGHTTP2_FLAG_END_STREAM and only + * when |eof| becomes nonzero, flags in nghttp2_data has + * NGHTTP2_FLAG_END_STREAM set. + */ + uint8_t flags; + /** + * The flag to indicate whether EOF was reached or not. Initially + * |eof| is 0. It becomes 1 after all data were read. + */ + uint8_t eof; + /** + * The flag to indicate that NGHTTP2_DATA_FLAG_NO_COPY is used. + */ + uint8_t no_copy; +} nghttp2_data_aux_data; + +typedef enum { + NGHTTP2_GOAWAY_AUX_NONE = 0x0, + /* indicates that session should be terminated after the + transmission of this frame. */ + NGHTTP2_GOAWAY_AUX_TERM_ON_SEND = 0x1, + /* indicates that this GOAWAY is just a notification for graceful + shutdown. No nghttp2_session.goaway_flags should be updated on + the reaction to this frame. */ + NGHTTP2_GOAWAY_AUX_SHUTDOWN_NOTICE = 0x2 +} nghttp2_goaway_aux_flag; + +/* struct used for GOAWAY frame */ +typedef struct { + /* bitwise-OR of one or more of nghttp2_goaway_aux_flag. */ + uint8_t flags; +} nghttp2_goaway_aux_data; + +/* Additional data which cannot be stored in nghttp2_frame struct */ +typedef union { + nghttp2_data_aux_data data; + nghttp2_headers_aux_data headers; + nghttp2_goaway_aux_data goaway; +} nghttp2_aux_data; + +struct nghttp2_outbound_item; +typedef struct nghttp2_outbound_item nghttp2_outbound_item; + +struct nghttp2_outbound_item { + nghttp2_frame frame; + nghttp2_aux_data aux_data; + /* The priority used in priority comparion. Smaller is served + ealier. For PING, SETTINGS and non-DATA frames (excluding + response HEADERS frame) have dedicated cycle value defined above. + For DATA frame, cycle is computed by taking into account of + effective weight and frame payload length previously sent, so + that the amount of transmission is distributed across streams + proportional to effective weight (inside a tree). */ + uint64_t cycle; + nghttp2_outbound_item *qnext; + /* nonzero if this object is queued. */ + uint8_t queued; +}; + +/* + * Initializes |item|. No memory allocation is done in this function. + * Don't call nghttp2_outbound_item_free() until frame member is + * initialized. + */ +void nghttp2_outbound_item_init(nghttp2_outbound_item *item); + +/* + * Deallocates resource for |item|. If |item| is NULL, this function + * does nothing. + */ +void nghttp2_outbound_item_free(nghttp2_outbound_item *item, nghttp2_mem *mem); + +/* + * queue for nghttp2_outbound_item. + */ +typedef struct { + nghttp2_outbound_item *head, *tail; + /* number of items in this queue. */ + size_t n; +} nghttp2_outbound_queue; + +void nghttp2_outbound_queue_init(nghttp2_outbound_queue *q); + +/* Pushes |item| into |q| */ +void nghttp2_outbound_queue_push(nghttp2_outbound_queue *q, + nghttp2_outbound_item *item); + +/* Pops |item| at the top from |q|. If |q| is empty, nothing + happens. */ +void nghttp2_outbound_queue_pop(nghttp2_outbound_queue *q); + +/* Returns the top item. */ +#define nghttp2_outbound_queue_top(Q) ((Q)->head) + +/* Returns the size of the queue */ +#define nghttp2_outbound_queue_size(Q) ((Q)->n) + +#endif /* NGHTTP2_OUTBOUND_ITEM_H */ diff --git a/lib/nghttp2_pq.c b/lib/nghttp2_pq.c new file mode 100644 index 0000000..f04c189 --- /dev/null +++ b/lib/nghttp2_pq.c @@ -0,0 +1,146 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "nghttp2_pq.h" + +int nghttp2_pq_init(nghttp2_pq *pq, nghttp2_less less, nghttp2_mem *mem) { + pq->mem = mem; + pq->capacity = 128; + pq->q = nghttp2_mem_malloc(mem, pq->capacity * sizeof(void *)); + if (pq->q == NULL) { + return NGHTTP2_ERR_NOMEM; + } + pq->length = 0; + pq->less = less; + return 0; +} + +void nghttp2_pq_free(nghttp2_pq *pq) { + nghttp2_mem_free(pq->mem, pq->q); + pq->q = NULL; +} + +static void swap(nghttp2_pq *pq, size_t i, size_t j) { + void *t = pq->q[i]; + pq->q[i] = pq->q[j]; + pq->q[j] = t; +} + +static void bubble_up(nghttp2_pq *pq, size_t index) { + if (index == 0) { + return; + } else { + size_t parent = (index - 1) / 2; + if (pq->less(pq->q[index], pq->q[parent])) { + swap(pq, parent, index); + bubble_up(pq, parent); + } + } +} + +int nghttp2_pq_push(nghttp2_pq *pq, void *item) { + if (pq->capacity <= pq->length) { + void *nq; + nq = nghttp2_mem_realloc(pq->mem, pq->q, + (pq->capacity * 2) * sizeof(void *)); + if (nq == NULL) { + return NGHTTP2_ERR_NOMEM; + } + pq->capacity *= 2; + pq->q = nq; + } + pq->q[pq->length] = item; + ++pq->length; + bubble_up(pq, pq->length - 1); + return 0; +} + +void *nghttp2_pq_top(nghttp2_pq *pq) { + if (pq->length == 0) { + return NULL; + } else { + return pq->q[0]; + } +} + +static void bubble_down(nghttp2_pq *pq, size_t index) { + size_t lchild = index * 2 + 1; + size_t minindex = index; + size_t i, j; + for (i = 0; i < 2; ++i) { + j = lchild + i; + if (j >= pq->length) { + break; + } + if (pq->less(pq->q[j], pq->q[minindex])) { + minindex = j; + } + } + if (minindex != index) { + swap(pq, index, minindex); + bubble_down(pq, minindex); + } +} + +void nghttp2_pq_pop(nghttp2_pq *pq) { + if (pq->length > 0) { + pq->q[0] = pq->q[pq->length - 1]; + --pq->length; + bubble_down(pq, 0); + } +} + +int nghttp2_pq_empty(nghttp2_pq *pq) { return pq->length == 0; } + +size_t nghttp2_pq_size(nghttp2_pq *pq) { return pq->length; } + +void nghttp2_pq_update(nghttp2_pq *pq, nghttp2_pq_item_cb fun, void *arg) { + size_t i; + int rv = 0; + if (pq->length == 0) { + return; + } + for (i = 0; i < pq->length; ++i) { + rv |= (*fun)(pq->q[i], arg); + } + if (rv) { + for (i = pq->length; i > 0; --i) { + bubble_down(pq, i - 1); + } + } +} + +int nghttp2_pq_each(nghttp2_pq *pq, nghttp2_pq_item_cb fun, void *arg) { + size_t i; + + if (pq->length == 0) { + return 0; + } + for (i = 0; i < pq->length; ++i) { + if ((*fun)(pq->q[i], arg)) { + return 1; + } + } + return 0; +} diff --git a/lib/nghttp2_pq.h b/lib/nghttp2_pq.h new file mode 100644 index 0000000..1775d03 --- /dev/null +++ b/lib/nghttp2_pq.h @@ -0,0 +1,121 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGHTTP2_PQ_H +#define NGHTTP2_PQ_H + +#ifdef HAVE_CONFIG_H +#include +#endif /* HAVE_CONFIG_H */ + +#include +#include "nghttp2_int.h" +#include "nghttp2_mem.h" + +/* Implementation of priority queue */ + +typedef struct { + /* The pointer to the pointer to the item stored */ + void **q; + /* Memory allocator */ + nghttp2_mem *mem; + /* The number of items sotred */ + size_t length; + /* The maximum number of items this pq can store. This is + automatically extended when length is reached to this value. */ + size_t capacity; + /* The less function between items */ + nghttp2_less less; +} nghttp2_pq; + +/* + * Initializes priority queue |pq| with compare function |cmp|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + */ +int nghttp2_pq_init(nghttp2_pq *pq, nghttp2_less less, nghttp2_mem *mem); + +/* + * Deallocates any resources allocated for |pq|. The stored items are + * not freed by this function. + */ +void nghttp2_pq_free(nghttp2_pq *pq); + +/* + * Adds |item| to the priority queue |pq|. + * + * This function returns 0 if it succeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + */ +int nghttp2_pq_push(nghttp2_pq *pq, void *item); + +/* + * Returns item at the top of the queue |pq|. If the queue is empty, + * this function returns NULL. + */ +void *nghttp2_pq_top(nghttp2_pq *pq); + +/* + * Pops item at the top of the queue |pq|. The popped item is not + * freed by this function. + */ +void nghttp2_pq_pop(nghttp2_pq *pq); + +/* + * Returns nonzero if the queue |pq| is empty. + */ +int nghttp2_pq_empty(nghttp2_pq *pq); + +/* + * Returns the number of items in the queue |pq|. + */ +size_t nghttp2_pq_size(nghttp2_pq *pq); + +typedef int (*nghttp2_pq_item_cb)(void *item, void *arg); + +/* + * Updates each item in |pq| using function |fun| and re-construct + * priority queue. The |fun| must return non-zero if it modifies the + * item in a way that it affects ordering in the priority queue. The + * |arg| is passed to the 2nd parameter of |fun|. + */ +void nghttp2_pq_update(nghttp2_pq *pq, nghttp2_pq_item_cb fun, void *arg); + +/* + * Applys |fun| to each item in |pq|. The |arg| is passed as arg + * parameter to callback function. This function must not change the + * ordering key. If the return value from callback is nonzero, this + * function returns 1 immediately without iterating remaining items. + * Otherwise this function returns 0. + */ +int nghttp2_pq_each(nghttp2_pq *pq, nghttp2_pq_item_cb fun, void *arg); + +#endif /* NGHTTP2_PQ_H */ diff --git a/lib/nghttp2_priority_spec.c b/lib/nghttp2_priority_spec.c new file mode 100644 index 0000000..cd254b1 --- /dev/null +++ b/lib/nghttp2_priority_spec.c @@ -0,0 +1,44 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2014 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "nghttp2_priority_spec.h" + +void nghttp2_priority_spec_init(nghttp2_priority_spec *pri_spec, + int32_t stream_id, int32_t weight, + int exclusive) { + pri_spec->stream_id = stream_id; + pri_spec->weight = weight; + pri_spec->exclusive = exclusive != 0; +} + +void nghttp2_priority_spec_default_init(nghttp2_priority_spec *pri_spec) { + pri_spec->stream_id = 0; + pri_spec->weight = NGHTTP2_DEFAULT_WEIGHT; + pri_spec->exclusive = 0; +} + +int nghttp2_priority_spec_check_default(const nghttp2_priority_spec *pri_spec) { + return pri_spec->stream_id == 0 && + pri_spec->weight == NGHTTP2_DEFAULT_WEIGHT && pri_spec->exclusive == 0; +} diff --git a/lib/nghttp2_priority_spec.h b/lib/nghttp2_priority_spec.h new file mode 100644 index 0000000..a325a58 --- /dev/null +++ b/lib/nghttp2_priority_spec.h @@ -0,0 +1,34 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2014 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGHTTP2_PRIORITY_SPEC_H +#define NGHTTP2_PRIORITY_SPEC_H + +#ifdef HAVE_CONFIG_H +#include +#endif /* HAVE_CONFIG_H */ + +#include + +#endif /* NGHTTP2_PRIORITY_SPEC_H */ diff --git a/lib/nghttp2_queue.c b/lib/nghttp2_queue.c new file mode 100644 index 0000000..055eb69 --- /dev/null +++ b/lib/nghttp2_queue.c @@ -0,0 +1,85 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "nghttp2_queue.h" + +#include +#include + +void nghttp2_queue_init(nghttp2_queue *queue) { + queue->front = queue->back = NULL; +} + +void nghttp2_queue_free(nghttp2_queue *queue) { + if (!queue) { + return; + } else { + nghttp2_queue_cell *p = queue->front; + while (p) { + nghttp2_queue_cell *next = p->next; + free(p); + p = next; + } + } +} + +int nghttp2_queue_push(nghttp2_queue *queue, void *data) { + nghttp2_queue_cell *new_cell = + (nghttp2_queue_cell *)malloc(sizeof(nghttp2_queue_cell)); + if (!new_cell) { + return NGHTTP2_ERR_NOMEM; + } + new_cell->data = data; + new_cell->next = NULL; + if (queue->back) { + queue->back->next = new_cell; + queue->back = new_cell; + + } else { + queue->front = queue->back = new_cell; + } + return 0; +} + +void nghttp2_queue_pop(nghttp2_queue *queue) { + nghttp2_queue_cell *front = queue->front; + assert(front); + queue->front = front->next; + if (front == queue->back) { + queue->back = NULL; + } + free(front); +} + +void *nghttp2_queue_front(nghttp2_queue *queue) { + assert(queue->front); + return queue->front->data; +} + +void *nghttp2_queue_back(nghttp2_queue *queue) { + assert(queue->back); + return queue->back->data; +} + +int nghttp2_queue_empty(nghttp2_queue *queue) { return queue->front == NULL; } diff --git a/lib/nghttp2_queue.h b/lib/nghttp2_queue.h new file mode 100644 index 0000000..d872b07 --- /dev/null +++ b/lib/nghttp2_queue.h @@ -0,0 +1,49 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGHTTP2_QUEUE_H +#define NGHTTP2_QUEUE_H + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif /* HAVE_CONFIG_H */ + +#include + +typedef struct nghttp2_queue_cell { + void *data; + struct nghttp2_queue_cell *next; +} nghttp2_queue_cell; + +typedef struct { nghttp2_queue_cell *front, *back; } nghttp2_queue; + +void nghttp2_queue_init(nghttp2_queue *queue); +void nghttp2_queue_free(nghttp2_queue *queue); +int nghttp2_queue_push(nghttp2_queue *queue, void *data); +void nghttp2_queue_pop(nghttp2_queue *queue); +void *nghttp2_queue_front(nghttp2_queue *queue); +void *nghttp2_queue_back(nghttp2_queue *queue); +int nghttp2_queue_empty(nghttp2_queue *queue); + +#endif /* NGHTTP2_QUEUE_H */ diff --git a/lib/nghttp2_session.c b/lib/nghttp2_session.c new file mode 100644 index 0000000..7309aa0 --- /dev/null +++ b/lib/nghttp2_session.c @@ -0,0 +1,6612 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "nghttp2_session.h" + +#include +#include +#include +#include + +#include "nghttp2_helper.h" +#include "nghttp2_net.h" +#include "nghttp2_priority_spec.h" +#include "nghttp2_option.h" +#include "nghttp2_http.h" + +/* + * Returns non-zero if the number of outgoing opened streams is larger + * than or equal to + * remote_settings.max_concurrent_streams. + */ +static int +session_is_outgoing_concurrent_streams_max(nghttp2_session *session) { + return session->remote_settings.max_concurrent_streams <= + session->num_outgoing_streams; +} + +/* + * Returns non-zero if the number of incoming opened streams is larger + * than or equal to + * local_settings.max_concurrent_streams. + */ +static int +session_is_incoming_concurrent_streams_max(nghttp2_session *session) { + return session->local_settings.max_concurrent_streams <= + session->num_incoming_streams; +} + +/* + * Returns non-zero if the number of incoming opened streams is larger + * than or equal to + * session->pending_local_max_concurrent_stream. + */ +static int +session_is_incoming_concurrent_streams_pending_max(nghttp2_session *session) { + return session->pending_local_max_concurrent_stream <= + session->num_incoming_streams; +} + +/* + * Returns non-zero if |lib_error| is non-fatal error. + */ +static int is_non_fatal(int lib_error_code) { + return lib_error_code < 0 && lib_error_code > NGHTTP2_ERR_FATAL; +} + +int nghttp2_is_fatal(int lib_error_code) { + return lib_error_code < NGHTTP2_ERR_FATAL; +} + +static int session_enforce_http_messaging(nghttp2_session *session) { + return (session->opt_flags & NGHTTP2_OPTMASK_NO_HTTP_MESSAGING) == 0; +} + +/* + * Returns nonzero if |frame| is trailer headers. + */ +static int session_trailer_headers(nghttp2_session *session, + nghttp2_stream *stream, + nghttp2_frame *frame) { + if (!stream || frame->hd.type != NGHTTP2_HEADERS) { + return 0; + } + if (session->server) { + return frame->headers.cat == NGHTTP2_HCAT_HEADERS; + } + + return frame->headers.cat == NGHTTP2_HCAT_HEADERS && + (stream->http_flags & NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE) == 0; +} + +/* Returns nonzero if the |stream| is in reserved(remote) state */ +static int state_reserved_remote(nghttp2_session *session, + nghttp2_stream *stream) { + return stream->state == NGHTTP2_STREAM_RESERVED && + !nghttp2_session_is_my_stream_id(session, stream->stream_id); +} + +/* Returns nonzero if the |stream| is in reserved(local) state */ +static int state_reserved_local(nghttp2_session *session, + nghttp2_stream *stream) { + return stream->state == NGHTTP2_STREAM_RESERVED && + nghttp2_session_is_my_stream_id(session, stream->stream_id); +} + +/* + * Checks whether received stream_id is valid. This function returns + * 1 if it succeeds, or 0. + */ +static int session_is_new_peer_stream_id(nghttp2_session *session, + int32_t stream_id) { + return stream_id != 0 && + !nghttp2_session_is_my_stream_id(session, stream_id) && + session->last_recv_stream_id < stream_id; +} + +static int session_detect_idle_stream(nghttp2_session *session, + int32_t stream_id) { + /* Assume that stream object with stream_id does not exist */ + if (nghttp2_session_is_my_stream_id(session, stream_id)) { + if (session->next_stream_id <= (uint32_t)stream_id) { + return 1; + } + return 0; + } + if (session_is_new_peer_stream_id(session, stream_id)) { + return 1; + } + return 0; +} + +static int session_terminate_session(nghttp2_session *session, + int32_t last_stream_id, + uint32_t error_code, const char *reason) { + int rv; + const uint8_t *debug_data; + size_t debug_datalen; + + if (session->goaway_flags & NGHTTP2_GOAWAY_TERM_ON_SEND) { + return 0; + } + + if (reason == NULL) { + debug_data = NULL; + debug_datalen = 0; + } else { + debug_data = (const uint8_t *)reason; + debug_datalen = strlen(reason); + } + + rv = nghttp2_session_add_goaway(session, last_stream_id, error_code, + debug_data, debug_datalen, + NGHTTP2_GOAWAY_AUX_TERM_ON_SEND); + + if (rv != 0) { + return rv; + } + + session->goaway_flags |= NGHTTP2_GOAWAY_TERM_ON_SEND; + + return 0; +} + +int nghttp2_session_terminate_session(nghttp2_session *session, + uint32_t error_code) { + return session_terminate_session(session, session->last_proc_stream_id, + error_code, NULL); +} + +int nghttp2_session_terminate_session2(nghttp2_session *session, + int32_t last_stream_id, + uint32_t error_code) { + return session_terminate_session(session, last_stream_id, error_code, NULL); +} + +int nghttp2_session_terminate_session_with_reason(nghttp2_session *session, + uint32_t error_code, + const char *reason) { + return session_terminate_session(session, session->last_proc_stream_id, + error_code, reason); +} + +int nghttp2_session_is_my_stream_id(nghttp2_session *session, + int32_t stream_id) { + int rem; + if (stream_id == 0) { + return 0; + } + rem = stream_id & 0x1; + if (session->server) { + return rem == 0; + } + return rem == 1; +} + +nghttp2_stream *nghttp2_session_get_stream(nghttp2_session *session, + int32_t stream_id) { + nghttp2_stream *stream; + + stream = (nghttp2_stream *)nghttp2_map_find(&session->streams, stream_id); + + if (stream == NULL || (stream->flags & NGHTTP2_STREAM_FLAG_CLOSED) || + stream->state == NGHTTP2_STREAM_IDLE) { + return NULL; + } + + return stream; +} + +nghttp2_stream *nghttp2_session_get_stream_raw(nghttp2_session *session, + int32_t stream_id) { + return (nghttp2_stream *)nghttp2_map_find(&session->streams, stream_id); +} + +static int outbound_item_less(const void *lhsx, const void *rhsx) { + const nghttp2_outbound_item *lhs, *rhs; + + lhs = (const nghttp2_outbound_item *)lhsx; + rhs = (const nghttp2_outbound_item *)rhsx; + + return (lhs->cycle < rhs->cycle) ? 1 : 0; +} + +static void session_inbound_frame_reset(nghttp2_session *session) { + nghttp2_inbound_frame *iframe = &session->iframe; + nghttp2_mem *mem = &session->mem; + /* A bit risky code, since if this function is called from + nghttp2_session_new(), we rely on the fact that + iframe->frame.hd.type is 0, so that no free is performed. */ + switch (iframe->frame.hd.type) { + case NGHTTP2_HEADERS: + nghttp2_frame_headers_free(&iframe->frame.headers, mem); + break; + case NGHTTP2_PRIORITY: + nghttp2_frame_priority_free(&iframe->frame.priority); + break; + case NGHTTP2_RST_STREAM: + nghttp2_frame_rst_stream_free(&iframe->frame.rst_stream); + break; + case NGHTTP2_SETTINGS: + nghttp2_frame_settings_free(&iframe->frame.settings, mem); + break; + case NGHTTP2_PUSH_PROMISE: + nghttp2_frame_push_promise_free(&iframe->frame.push_promise, mem); + break; + case NGHTTP2_PING: + nghttp2_frame_ping_free(&iframe->frame.ping); + break; + case NGHTTP2_GOAWAY: + nghttp2_frame_goaway_free(&iframe->frame.goaway, mem); + break; + case NGHTTP2_WINDOW_UPDATE: + nghttp2_frame_window_update_free(&iframe->frame.window_update); + break; + } + + memset(&iframe->frame, 0, sizeof(nghttp2_frame)); + memset(&iframe->ext_frame_payload, 0, sizeof(nghttp2_ext_frame_payload)); + + iframe->state = NGHTTP2_IB_READ_HEAD; + + nghttp2_buf_wrap_init(&iframe->sbuf, iframe->raw_sbuf, + sizeof(iframe->raw_sbuf)); + iframe->sbuf.mark += NGHTTP2_FRAME_HDLEN; + + nghttp2_buf_free(&iframe->lbuf, mem); + nghttp2_buf_wrap_init(&iframe->lbuf, NULL, 0); + + iframe->niv = 0; + iframe->payloadleft = 0; + iframe->padlen = 0; + iframe->iv[NGHTTP2_INBOUND_NUM_IV - 1].settings_id = + NGHTTP2_SETTINGS_HEADER_TABLE_SIZE; + iframe->iv[NGHTTP2_INBOUND_NUM_IV - 1].value = UINT32_MAX; +} + +static void init_settings(nghttp2_settings_storage *settings) { + settings->header_table_size = NGHTTP2_HD_DEFAULT_MAX_BUFFER_SIZE; + settings->enable_push = 1; + settings->max_concurrent_streams = NGHTTP2_INITIAL_MAX_CONCURRENT_STREAMS; + settings->initial_window_size = NGHTTP2_INITIAL_WINDOW_SIZE; + settings->max_frame_size = NGHTTP2_MAX_FRAME_SIZE_MIN; + settings->max_header_list_size = UINT32_MAX; +} + +static void active_outbound_item_reset(nghttp2_active_outbound_item *aob, + nghttp2_mem *mem) { + DEBUGF(fprintf(stderr, "send: reset nghttp2_active_outbound_item\n")); + DEBUGF(fprintf(stderr, "send: aob->item = %p\n", aob->item)); + nghttp2_outbound_item_free(aob->item, mem); + nghttp2_mem_free(mem, aob->item); + aob->item = NULL; + nghttp2_bufs_reset(&aob->framebufs); + aob->state = NGHTTP2_OB_POP_ITEM; +} + +/* The global variable for tests where we want to disable strict + preface handling. */ +int nghttp2_enable_strict_preface = 1; + +static int session_new(nghttp2_session **session_ptr, + const nghttp2_session_callbacks *callbacks, + void *user_data, int server, + const nghttp2_option *option, nghttp2_mem *mem) { + int rv; + + if (mem == NULL) { + mem = nghttp2_mem_default(); + } + + *session_ptr = nghttp2_mem_calloc(mem, 1, sizeof(nghttp2_session)); + if (*session_ptr == NULL) { + rv = NGHTTP2_ERR_NOMEM; + goto fail_session; + } + + (*session_ptr)->mem = *mem; + mem = &(*session_ptr)->mem; + + /* next_stream_id is initialized in either + nghttp2_session_client_new2 or nghttp2_session_server_new2 */ + + rv = nghttp2_pq_init(&(*session_ptr)->ob_da_pq, outbound_item_less, mem); + if (rv != 0) { + goto fail_ob_da_pq; + } + + rv = nghttp2_hd_deflate_init(&(*session_ptr)->hd_deflater, mem); + if (rv != 0) { + goto fail_hd_deflater; + } + rv = nghttp2_hd_inflate_init(&(*session_ptr)->hd_inflater, mem); + if (rv != 0) { + goto fail_hd_inflater; + } + rv = nghttp2_map_init(&(*session_ptr)->streams, mem); + if (rv != 0) { + goto fail_map; + } + + nghttp2_stream_roots_init(&(*session_ptr)->roots); + + (*session_ptr)->remote_window_size = NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE; + (*session_ptr)->recv_window_size = 0; + (*session_ptr)->consumed_size = 0; + (*session_ptr)->recv_reduction = 0; + (*session_ptr)->local_window_size = NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE; + + (*session_ptr)->goaway_flags = NGHTTP2_GOAWAY_NONE; + (*session_ptr)->local_last_stream_id = (1u << 31) - 1; + (*session_ptr)->remote_last_stream_id = (1u << 31) - 1; + + (*session_ptr)->inflight_niv = -1; + + (*session_ptr)->pending_local_max_concurrent_stream = + NGHTTP2_INITIAL_MAX_CONCURRENT_STREAMS; + (*session_ptr)->pending_enable_push = 1; + + if (server) { + (*session_ptr)->server = 1; + } + + /* 1 for Pad Field. */ + rv = nghttp2_bufs_init3(&(*session_ptr)->aob.framebufs, + NGHTTP2_FRAMEBUF_CHUNKLEN, NGHTTP2_FRAMEBUF_MAX_NUM, + 1, NGHTTP2_FRAME_HDLEN + 1, mem); + if (rv != 0) { + goto fail_aob_framebuf; + } + + active_outbound_item_reset(&(*session_ptr)->aob, mem); + + init_settings(&(*session_ptr)->remote_settings); + init_settings(&(*session_ptr)->local_settings); + + if (option) { + if ((option->opt_set_mask & NGHTTP2_OPT_NO_AUTO_WINDOW_UPDATE) && + option->no_auto_window_update) { + + (*session_ptr)->opt_flags |= NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE; + } + + if (option->opt_set_mask & NGHTTP2_OPT_PEER_MAX_CONCURRENT_STREAMS) { + + (*session_ptr)->remote_settings.max_concurrent_streams = + option->peer_max_concurrent_streams; + } + + if ((option->opt_set_mask & NGHTTP2_OPT_NO_RECV_CLIENT_MAGIC) && + option->no_recv_client_magic) { + + (*session_ptr)->opt_flags |= NGHTTP2_OPTMASK_NO_RECV_CLIENT_MAGIC; + } + + if ((option->opt_set_mask & NGHTTP2_OPT_NO_HTTP_MESSAGING) && + option->no_http_messaging) { + + (*session_ptr)->opt_flags |= NGHTTP2_OPTMASK_NO_HTTP_MESSAGING; + } + } + + (*session_ptr)->callbacks = *callbacks; + (*session_ptr)->user_data = user_data; + + session_inbound_frame_reset(*session_ptr); + + if (nghttp2_enable_strict_preface) { + nghttp2_inbound_frame *iframe = &(*session_ptr)->iframe; + + if (server && + ((*session_ptr)->opt_flags & NGHTTP2_OPTMASK_NO_RECV_CLIENT_MAGIC) == + 0) { + iframe->state = NGHTTP2_IB_READ_CLIENT_MAGIC; + iframe->payloadleft = NGHTTP2_CLIENT_MAGIC_LEN; + } else { + iframe->state = NGHTTP2_IB_READ_FIRST_SETTINGS; + } + + if (!server) { + (*session_ptr)->aob.state = NGHTTP2_OB_SEND_CLIENT_MAGIC; + nghttp2_bufs_add(&(*session_ptr)->aob.framebufs, NGHTTP2_CLIENT_MAGIC, + NGHTTP2_CLIENT_MAGIC_LEN); + } + } + + return 0; + +fail_aob_framebuf: + nghttp2_map_free(&(*session_ptr)->streams); +fail_map: + nghttp2_hd_inflate_free(&(*session_ptr)->hd_inflater); +fail_hd_inflater: + nghttp2_hd_deflate_free(&(*session_ptr)->hd_deflater); +fail_hd_deflater: + nghttp2_pq_free(&(*session_ptr)->ob_da_pq); +fail_ob_da_pq: + nghttp2_mem_free(mem, *session_ptr); +fail_session: + return rv; +} + +int nghttp2_session_client_new(nghttp2_session **session_ptr, + const nghttp2_session_callbacks *callbacks, + void *user_data) { + return nghttp2_session_client_new3(session_ptr, callbacks, user_data, NULL, + NULL); +} + +int nghttp2_session_client_new2(nghttp2_session **session_ptr, + const nghttp2_session_callbacks *callbacks, + void *user_data, const nghttp2_option *option) { + return nghttp2_session_client_new3(session_ptr, callbacks, user_data, option, + NULL); +} + +int nghttp2_session_client_new3(nghttp2_session **session_ptr, + const nghttp2_session_callbacks *callbacks, + void *user_data, const nghttp2_option *option, + nghttp2_mem *mem) { + int rv; + nghttp2_session *session; + + rv = session_new(&session, callbacks, user_data, 0, option, mem); + + if (rv != 0) { + return rv; + } + /* IDs for use in client */ + session->next_stream_id = 1; + + *session_ptr = session; + + return 0; +} + +int nghttp2_session_server_new(nghttp2_session **session_ptr, + const nghttp2_session_callbacks *callbacks, + void *user_data) { + return nghttp2_session_server_new3(session_ptr, callbacks, user_data, NULL, + NULL); +} + +int nghttp2_session_server_new2(nghttp2_session **session_ptr, + const nghttp2_session_callbacks *callbacks, + void *user_data, const nghttp2_option *option) { + return nghttp2_session_server_new3(session_ptr, callbacks, user_data, option, + NULL); +} + +int nghttp2_session_server_new3(nghttp2_session **session_ptr, + const nghttp2_session_callbacks *callbacks, + void *user_data, const nghttp2_option *option, + nghttp2_mem *mem) { + int rv; + nghttp2_session *session; + + rv = session_new(&session, callbacks, user_data, 1, option, mem); + + if (rv != 0) { + return rv; + } + /* IDs for use in client */ + session->next_stream_id = 2; + + *session_ptr = session; + + return 0; +} + +static int free_streams(nghttp2_map_entry *entry, void *ptr) { + nghttp2_session *session; + nghttp2_stream *stream; + nghttp2_outbound_item *item; + nghttp2_mem *mem; + + session = (nghttp2_session *)ptr; + mem = &session->mem; + stream = (nghttp2_stream *)entry; + item = stream->item; + + if (item && !item->queued && item != session->aob.item) { + nghttp2_outbound_item_free(item, mem); + nghttp2_mem_free(mem, item); + } + + nghttp2_stream_free(stream); + nghttp2_mem_free(mem, stream); + + return 0; +} + +static void ob_pq_free(nghttp2_pq *pq, nghttp2_mem *mem) { + while (!nghttp2_pq_empty(pq)) { + nghttp2_outbound_item *item = (nghttp2_outbound_item *)nghttp2_pq_top(pq); + nghttp2_outbound_item_free(item, mem); + nghttp2_mem_free(mem, item); + nghttp2_pq_pop(pq); + } + nghttp2_pq_free(pq); +} + +static void ob_q_free(nghttp2_outbound_queue *q, nghttp2_mem *mem) { + nghttp2_outbound_item *item, *next; + for (item = q->head; item;) { + next = item->qnext; + nghttp2_outbound_item_free(item, mem); + nghttp2_mem_free(mem, item); + item = next; + } +} + +void nghttp2_session_del(nghttp2_session *session) { + nghttp2_mem *mem; + + if (session == NULL) { + return; + } + + mem = &session->mem; + + nghttp2_mem_free(mem, session->inflight_iv); + + nghttp2_stream_roots_free(&session->roots); + + /* Have to free streams first, so that we can check + stream->item->queued */ + nghttp2_map_each_free(&session->streams, free_streams, session); + nghttp2_map_free(&session->streams); + + ob_q_free(&session->ob_urgent, mem); + ob_q_free(&session->ob_reg, mem); + ob_q_free(&session->ob_syn, mem); + ob_pq_free(&session->ob_da_pq, mem); + active_outbound_item_reset(&session->aob, mem); + session_inbound_frame_reset(session); + nghttp2_hd_deflate_free(&session->hd_deflater); + nghttp2_hd_inflate_free(&session->hd_inflater); + nghttp2_bufs_free(&session->aob.framebufs); + nghttp2_mem_free(mem, session); +} + +int +nghttp2_session_reprioritize_stream(nghttp2_session *session, + nghttp2_stream *stream, + const nghttp2_priority_spec *pri_spec_in) { + int rv; + nghttp2_stream *dep_stream = NULL; + nghttp2_stream *root_stream; + nghttp2_priority_spec pri_spec_default; + const nghttp2_priority_spec *pri_spec = pri_spec_in; + + if (!nghttp2_stream_in_dep_tree(stream)) { + return 0; + } + + if (pri_spec->stream_id == stream->stream_id) { + return nghttp2_session_terminate_session_with_reason( + session, NGHTTP2_PROTOCOL_ERROR, "depend on itself"); + } + + if (pri_spec->stream_id != 0) { + dep_stream = nghttp2_session_get_stream_raw(session, pri_spec->stream_id); + + if (session->server && !dep_stream && + session_detect_idle_stream(session, pri_spec->stream_id)) { + + nghttp2_priority_spec_default_init(&pri_spec_default); + + dep_stream = nghttp2_session_open_stream( + session, pri_spec->stream_id, NGHTTP2_FLAG_NONE, &pri_spec_default, + NGHTTP2_STREAM_IDLE, NULL); + + if (dep_stream == NULL) { + return NGHTTP2_ERR_NOMEM; + } + } else if (!dep_stream || !nghttp2_stream_in_dep_tree(dep_stream)) { + nghttp2_priority_spec_default_init(&pri_spec_default); + pri_spec = &pri_spec_default; + } + } + + if (pri_spec->stream_id == 0) { + nghttp2_stream_dep_remove_subtree(stream); + + /* We have to update weight after removing stream from tree */ + stream->weight = pri_spec->weight; + + if (pri_spec->exclusive && + session->roots.num_streams <= NGHTTP2_MAX_DEP_TREE_LENGTH) { + + rv = nghttp2_stream_dep_all_your_stream_are_belong_to_us(stream, session); + } else { + rv = nghttp2_stream_dep_make_root(stream, session); + } + + return rv; + } + + assert(dep_stream); + + if (nghttp2_stream_dep_subtree_find(stream, dep_stream)) { + DEBUGF(fprintf(stderr, "stream: cycle detected, dep_stream(%p)=%d " + "stream(%p)=%d\n", + dep_stream, dep_stream->stream_id, stream, + stream->stream_id)); + + nghttp2_stream_dep_remove_subtree(dep_stream); + nghttp2_stream_dep_make_root(dep_stream, session); + } + + nghttp2_stream_dep_remove_subtree(stream); + + /* We have to update weight after removing stream from tree */ + stream->weight = pri_spec->weight; + + root_stream = nghttp2_stream_get_dep_root(dep_stream); + + if (root_stream->num_substreams + stream->num_substreams > + NGHTTP2_MAX_DEP_TREE_LENGTH) { + stream->weight = NGHTTP2_DEFAULT_WEIGHT; + + rv = nghttp2_stream_dep_make_root(stream, session); + } else { + if (pri_spec->exclusive) { + rv = nghttp2_stream_dep_insert_subtree(dep_stream, stream, session); + } else { + rv = nghttp2_stream_dep_add_subtree(dep_stream, stream, session); + } + } + + if (rv != 0) { + return rv; + } + + return 0; +} + +int nghttp2_session_add_item(nghttp2_session *session, + nghttp2_outbound_item *item) { + /* TODO Return error if stream is not found for the frame requiring + stream presence. */ + int rv = 0; + nghttp2_stream *stream; + nghttp2_frame *frame; + + frame = &item->frame; + stream = nghttp2_session_get_stream(session, frame->hd.stream_id); + + if (frame->hd.type != NGHTTP2_DATA) { + + switch (frame->hd.type) { + case NGHTTP2_HEADERS: + /* We push request HEADERS and push response HEADERS to + dedicated queue because their transmission is affected by + SETTINGS_MAX_CONCURRENT_STREAMS */ + /* TODO If 2 HEADERS are submitted for reserved stream, then + both of them are queued into ob_syn, which is not + desirable. */ + if (frame->headers.cat == NGHTTP2_HCAT_REQUEST) { + nghttp2_outbound_queue_push(&session->ob_syn, item); + item->queued = 1; + break; + } + + if (stream && (stream->state == NGHTTP2_STREAM_RESERVED || + item->aux_data.headers.attach_stream)) { + rv = nghttp2_stream_attach_item(stream, item, session); + + if (rv != 0) { + return rv; + } + + break; + } + + nghttp2_outbound_queue_push(&session->ob_reg, item); + item->queued = 1; + break; + case NGHTTP2_SETTINGS: + case NGHTTP2_PING: + nghttp2_outbound_queue_push(&session->ob_urgent, item); + item->queued = 1; + break; + case NGHTTP2_RST_STREAM: + if (stream) { + stream->state = NGHTTP2_STREAM_CLOSING; + } + /* fall through */ + default: + nghttp2_outbound_queue_push(&session->ob_reg, item); + item->queued = 1; + } + + return 0; + } + + if (!stream) { + return NGHTTP2_ERR_STREAM_CLOSED; + } + + if (stream->item) { + return NGHTTP2_ERR_DATA_EXIST; + } + + rv = nghttp2_stream_attach_item(stream, item, session); + + if (rv != 0) { + return rv; + } + + return 0; +} + +int nghttp2_session_add_rst_stream(nghttp2_session *session, int32_t stream_id, + uint32_t error_code) { + int rv; + nghttp2_outbound_item *item; + nghttp2_frame *frame; + nghttp2_stream *stream; + nghttp2_mem *mem; + + mem = &session->mem; + stream = nghttp2_session_get_stream(session, stream_id); + if (stream && stream->state == NGHTTP2_STREAM_CLOSING) { + return 0; + } + + /* Cancel pending request HEADERS in ob_syn if this RST_STREAM + refers to that stream. */ + if (!session->server && nghttp2_session_is_my_stream_id(session, stream_id) && + nghttp2_outbound_queue_top(&session->ob_syn)) { + nghttp2_headers_aux_data *aux_data; + nghttp2_frame *headers_frame; + + headers_frame = &nghttp2_outbound_queue_top(&session->ob_syn)->frame; + assert(headers_frame->hd.type == NGHTTP2_HEADERS); + + if (headers_frame->hd.stream_id <= stream_id && + (uint32_t)stream_id < session->next_stream_id) { + + for (item = session->ob_syn.head; item; item = item->qnext) { + aux_data = &item->aux_data.headers; + + if (item->frame.hd.stream_id < stream_id) { + continue; + } + + /* stream_id in ob_syn queue must be strictly increasing. If + we found larger ID, then we can break here. */ + if (item->frame.hd.stream_id > stream_id || aux_data->canceled) { + break; + } + + aux_data->error_code = error_code; + aux_data->canceled = 1; + + return 0; + } + } + } + + item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item)); + if (item == NULL) { + return NGHTTP2_ERR_NOMEM; + } + + nghttp2_outbound_item_init(item); + + frame = &item->frame; + + nghttp2_frame_rst_stream_init(&frame->rst_stream, stream_id, error_code); + rv = nghttp2_session_add_item(session, item); + if (rv != 0) { + nghttp2_frame_rst_stream_free(&frame->rst_stream); + nghttp2_mem_free(mem, item); + return rv; + } + return 0; +} + +nghttp2_stream *nghttp2_session_open_stream(nghttp2_session *session, + int32_t stream_id, uint8_t flags, + nghttp2_priority_spec *pri_spec_in, + nghttp2_stream_state initial_state, + void *stream_user_data) { + int rv; + nghttp2_stream *stream; + nghttp2_stream *dep_stream = NULL; + nghttp2_stream *root_stream; + int stream_alloc = 0; + nghttp2_priority_spec pri_spec_default; + nghttp2_priority_spec *pri_spec = pri_spec_in; + nghttp2_mem *mem; + + mem = &session->mem; + stream = nghttp2_session_get_stream_raw(session, stream_id); + + if (stream) { + assert(stream->state == NGHTTP2_STREAM_IDLE); + assert(nghttp2_stream_in_dep_tree(stream)); + nghttp2_session_detach_idle_stream(session, stream); + nghttp2_stream_dep_remove(stream); + } else { + if (session->server && initial_state != NGHTTP2_STREAM_IDLE && + !nghttp2_session_is_my_stream_id(session, stream_id)) { + + nghttp2_session_adjust_closed_stream(session, 1); + } + + stream = nghttp2_mem_malloc(mem, sizeof(nghttp2_stream)); + if (stream == NULL) { + return NULL; + } + + stream_alloc = 1; + } + + if (pri_spec->stream_id != 0) { + dep_stream = nghttp2_session_get_stream_raw(session, pri_spec->stream_id); + + if (session->server && !dep_stream && + session_detect_idle_stream(session, pri_spec->stream_id)) { + /* Depends on idle stream, which does not exist in memory. + Assign default priority for it. */ + nghttp2_priority_spec_default_init(&pri_spec_default); + + dep_stream = nghttp2_session_open_stream( + session, pri_spec->stream_id, NGHTTP2_FLAG_NONE, &pri_spec_default, + NGHTTP2_STREAM_IDLE, NULL); + + if (dep_stream == NULL) { + if (stream_alloc) { + nghttp2_mem_free(mem, stream); + } + + return NULL; + } + } else if (!dep_stream || !nghttp2_stream_in_dep_tree(dep_stream)) { + /* If dep_stream is not part of dependency tree, stream will get + default priority. */ + nghttp2_priority_spec_default_init(&pri_spec_default); + pri_spec = &pri_spec_default; + } + } + + if (initial_state == NGHTTP2_STREAM_RESERVED) { + flags |= NGHTTP2_STREAM_FLAG_PUSH; + } + + nghttp2_stream_init( + stream, stream_id, flags, initial_state, pri_spec->weight, + &session->roots, session->remote_settings.initial_window_size, + session->local_settings.initial_window_size, stream_user_data); + + if (stream_alloc) { + rv = nghttp2_map_insert(&session->streams, &stream->map_entry); + if (rv != 0) { + nghttp2_mem_free(mem, stream); + return NULL; + } + } + + switch (initial_state) { + case NGHTTP2_STREAM_RESERVED: + if (nghttp2_session_is_my_stream_id(session, stream_id)) { + /* half closed (remote) */ + nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_RD); + } else { + /* half closed (local) */ + nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_WR); + } + /* Reserved stream does not count in the concurrent streams + limit. That is one of the DOS vector. */ + break; + case NGHTTP2_STREAM_IDLE: + /* Idle stream does not count toward the concurrent streams limit. + This is used as anchor node in dependency tree. */ + assert(session->server); + nghttp2_session_keep_idle_stream(session, stream); + break; + default: + if (nghttp2_session_is_my_stream_id(session, stream_id)) { + ++session->num_outgoing_streams; + } else { + ++session->num_incoming_streams; + } + } + + /* We don't have to track dependency of received reserved stream */ + if (stream->shut_flags & NGHTTP2_SHUT_WR) { + return stream; + } + + if (pri_spec->stream_id == 0) { + + ++session->roots.num_streams; + + if (pri_spec->exclusive && + session->roots.num_streams <= NGHTTP2_MAX_DEP_TREE_LENGTH) { + rv = nghttp2_stream_dep_all_your_stream_are_belong_to_us(stream, session); + + /* Since no dpri is changed in dependency tree, the above + function call never fail. */ + assert(rv == 0); + } else { + nghttp2_stream_roots_add(&session->roots, stream); + } + + return stream; + } + + /* TODO Client does not have to track dependencies of streams except + for those which have upload data. Currently, we just track + everything. */ + + assert(dep_stream); + + root_stream = nghttp2_stream_get_dep_root(dep_stream); + + if (root_stream->num_substreams < NGHTTP2_MAX_DEP_TREE_LENGTH) { + if (pri_spec->exclusive) { + nghttp2_stream_dep_insert(dep_stream, stream); + } else { + nghttp2_stream_dep_add(dep_stream, stream); + } + } else { + stream->weight = NGHTTP2_DEFAULT_WEIGHT; + + nghttp2_stream_roots_add(&session->roots, stream); + } + + return stream; +} + +int nghttp2_session_close_stream(nghttp2_session *session, int32_t stream_id, + uint32_t error_code) { + int rv; + nghttp2_stream *stream; + nghttp2_mem *mem; + + mem = &session->mem; + stream = nghttp2_session_get_stream(session, stream_id); + + if (!stream) { + return NGHTTP2_ERR_INVALID_ARGUMENT; + } + + DEBUGF(fprintf(stderr, "stream: stream(%p)=%d close\n", stream, + stream->stream_id)); + + if (stream->item) { + nghttp2_outbound_item *item; + + item = stream->item; + + rv = nghttp2_stream_detach_item(stream, session); + + if (rv != 0) { + return rv; + } + + /* If item is queued, it will be deleted when it is popped + (nghttp2_session_prep_frame() will fail). If session->aob.item + points to this item, let active_outbound_item_reset() + free the item. */ + if (!item->queued && item != session->aob.item) { + nghttp2_outbound_item_free(item, mem); + nghttp2_mem_free(mem, item); + } + } + + /* We call on_stream_close_callback even if stream->state is + NGHTTP2_STREAM_INITIAL. This will happen while sending request + HEADERS, a local endpoint receives RST_STREAM for that stream. It + may be PROTOCOL_ERROR, but without notifying stream closure will + hang the stream in a local endpoint. + */ + + if (session->callbacks.on_stream_close_callback) { + if (session->callbacks.on_stream_close_callback( + session, stream_id, error_code, session->user_data) != 0) { + + return NGHTTP2_ERR_CALLBACK_FAILURE; + } + } + + /* pushed streams which is not opened yet is not counted toward max + concurrent limits */ + if ((stream->flags & NGHTTP2_STREAM_FLAG_PUSH) == 0) { + if (nghttp2_session_is_my_stream_id(session, stream_id)) { + --session->num_outgoing_streams; + } else { + --session->num_incoming_streams; + } + } + + /* Closes both directions just in case they are not closed yet */ + stream->flags |= NGHTTP2_STREAM_FLAG_CLOSED; + + if (session->server && nghttp2_stream_in_dep_tree(stream)) { + /* On server side, retain stream at most MAX_CONCURRENT_STREAMS + combined with the current active incoming streams to make + dependency tree work better. */ + nghttp2_session_keep_closed_stream(session, stream); + } else { + nghttp2_session_destroy_stream(session, stream); + } + + return 0; +} + +void nghttp2_session_destroy_stream(nghttp2_session *session, + nghttp2_stream *stream) { + nghttp2_mem *mem; + + DEBUGF(fprintf(stderr, "stream: destroy closed stream(%p)=%d\n", stream, + stream->stream_id)); + + mem = &session->mem; + + nghttp2_stream_dep_remove(stream); + + nghttp2_map_remove(&session->streams, stream->stream_id); + nghttp2_stream_free(stream); + nghttp2_mem_free(mem, stream); +} + +void nghttp2_session_keep_closed_stream(nghttp2_session *session, + nghttp2_stream *stream) { + DEBUGF(fprintf(stderr, "stream: keep closed stream(%p)=%d, state=%d\n", + stream, stream->stream_id, stream->state)); + + if (session->closed_stream_tail) { + session->closed_stream_tail->closed_next = stream; + stream->closed_prev = session->closed_stream_tail; + } else { + session->closed_stream_head = stream; + } + session->closed_stream_tail = stream; + + ++session->num_closed_streams; + + nghttp2_session_adjust_closed_stream(session, 0); +} + +void nghttp2_session_keep_idle_stream(nghttp2_session *session, + nghttp2_stream *stream) { + DEBUGF(fprintf(stderr, "stream: keep idle stream(%p)=%d, state=%d\n", stream, + stream->stream_id, stream->state)); + + if (session->idle_stream_tail) { + session->idle_stream_tail->closed_next = stream; + stream->closed_prev = session->idle_stream_tail; + } else { + session->idle_stream_head = stream; + } + session->idle_stream_tail = stream; + + ++session->num_idle_streams; + + nghttp2_session_adjust_idle_stream(session); +} + +void nghttp2_session_detach_idle_stream(nghttp2_session *session, + nghttp2_stream *stream) { + nghttp2_stream *prev_stream, *next_stream; + + DEBUGF(fprintf(stderr, "stream: detach idle stream(%p)=%d, state=%d\n", + stream, stream->stream_id, stream->state)); + + prev_stream = stream->closed_prev; + next_stream = stream->closed_next; + + if (prev_stream) { + prev_stream->closed_next = next_stream; + } else { + session->idle_stream_head = next_stream; + } + + if (next_stream) { + next_stream->closed_prev = prev_stream; + } else { + session->idle_stream_tail = prev_stream; + } + + stream->closed_prev = NULL; + stream->closed_next = NULL; + + --session->num_idle_streams; +} + +void nghttp2_session_adjust_closed_stream(nghttp2_session *session, + ssize_t offset) { + size_t num_stream_max; + + num_stream_max = nghttp2_min(session->local_settings.max_concurrent_streams, + session->pending_local_max_concurrent_stream); + + DEBUGF(fprintf(stderr, "stream: adjusting kept closed streams " + "num_closed_streams=%zu, num_incoming_streams=%zu, " + "max_concurrent_streams=%zu\n", + session->num_closed_streams, session->num_incoming_streams, + num_stream_max)); + + while (session->num_closed_streams > 0 && + session->num_closed_streams + session->num_incoming_streams + offset > + num_stream_max) { + nghttp2_stream *head_stream; + + head_stream = session->closed_stream_head; + + assert(head_stream); + + session->closed_stream_head = head_stream->closed_next; + + if (session->closed_stream_head) { + session->closed_stream_head->closed_prev = NULL; + } else { + session->closed_stream_tail = NULL; + } + + nghttp2_session_destroy_stream(session, head_stream); + /* head_stream is now freed */ + --session->num_closed_streams; + } +} + +void nghttp2_session_adjust_idle_stream(nghttp2_session *session) { + size_t max; + + /* Make minimum number of idle streams 2 so that allocating 2 + streams at once is easy. This happens when PRIORITY frame to + idle stream, which depends on idle stream which does not + exist. */ + max = + nghttp2_max(2, nghttp2_min(session->local_settings.max_concurrent_streams, + session->pending_local_max_concurrent_stream)); + + DEBUGF(fprintf(stderr, "stream: adjusting kept idle streams " + "num_idle_streams=%zu, max=%zu\n", + session->num_idle_streams, max)); + + while (session->num_idle_streams > max) { + nghttp2_stream *head; + + head = session->idle_stream_head; + assert(head); + + session->idle_stream_head = head->closed_next; + + if (session->idle_stream_head) { + session->idle_stream_head->closed_prev = NULL; + } else { + session->idle_stream_tail = NULL; + } + + nghttp2_session_destroy_stream(session, head); + /* head is now destroyed */ + --session->num_idle_streams; + } +} + +/* + * Closes stream with stream ID |stream_id| if both transmission and + * reception of the stream were disallowed. The |error_code| indicates + * the reason of the closure. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_INVALID_ARGUMENT + * The stream is not found. + * NGHTTP2_ERR_CALLBACK_FAILURE + * The callback function failed. + */ +int nghttp2_session_close_stream_if_shut_rdwr(nghttp2_session *session, + nghttp2_stream *stream) { + if ((stream->shut_flags & NGHTTP2_SHUT_RDWR) == NGHTTP2_SHUT_RDWR) { + return nghttp2_session_close_stream(session, stream->stream_id, + NGHTTP2_NO_ERROR); + } + return 0; +} + +/* + * This function returns nonzero if session is closing. + */ +static int session_is_closing(nghttp2_session *session) { + return (session->goaway_flags & NGHTTP2_GOAWAY_TERM_ON_SEND) != 0; +} + +/* + * Check that we can send a frame to the |stream|. This function + * returns 0 if we can send a frame to the |frame|, or one of the + * following negative error codes: + * + * NGHTTP2_ERR_STREAM_CLOSED + * The stream is already closed. + * NGHTTP2_ERR_STREAM_SHUT_WR + * The stream is half-closed for transmission. + * NGHTTP2_ERR_SESSION_CLOSING + * This session is closing. + */ +static int session_predicate_for_stream_send(nghttp2_session *session, + nghttp2_stream *stream) { + if (stream == NULL) { + return NGHTTP2_ERR_STREAM_CLOSED; + } + if (session_is_closing(session)) { + return NGHTTP2_ERR_SESSION_CLOSING; + } + if (stream->shut_flags & NGHTTP2_SHUT_WR) { + return NGHTTP2_ERR_STREAM_SHUT_WR; + } + return 0; +} + +/* + * This function checks request HEADERS frame, which opens stream, can + * be sent at this time. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_START_STREAM_NOT_ALLOWED + * New stream cannot be created because of GOAWAY: session is + * going down or received last_stream_id is strictly less than + * frame->hd.stream_id. + * NGHTTP2_ERR_STREAM_CLOSING + * request HEADERS was canceled by RST_STREAM while it is in queue. + */ +static int session_predicate_request_headers_send(nghttp2_session *session, + nghttp2_outbound_item *item) { + if (item->aux_data.headers.canceled) { + return NGHTTP2_ERR_STREAM_CLOSING; + } + /* If we are terminating session (NGHTTP2_GOAWAY_TERM_ON_SEND) or + GOAWAY was received from peer, new request is not allowed. */ + if (session->goaway_flags & + (NGHTTP2_GOAWAY_TERM_ON_SEND | NGHTTP2_GOAWAY_RECV)) { + return NGHTTP2_ERR_START_STREAM_NOT_ALLOWED; + } + return 0; +} + +/* + * This function checks HEADERS, which is the first frame from the + * server, with the |stream| can be sent at this time. The |stream| + * can be NULL. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_STREAM_CLOSED + * The stream is already closed or does not exist. + * NGHTTP2_ERR_STREAM_SHUT_WR + * The transmission is not allowed for this stream (e.g., a frame + * with END_STREAM flag set has already sent) + * NGHTTP2_ERR_INVALID_STREAM_ID + * The stream ID is invalid. + * NGHTTP2_ERR_STREAM_CLOSING + * RST_STREAM was queued for this stream. + * NGHTTP2_ERR_INVALID_STREAM_STATE + * The state of the stream is not valid. + * NGHTTP2_ERR_SESSION_CLOSING + * This session is closing. + */ +static int session_predicate_response_headers_send(nghttp2_session *session, + nghttp2_stream *stream) { + int rv; + rv = session_predicate_for_stream_send(session, stream); + if (rv != 0) { + return rv; + } + assert(stream); + if (nghttp2_session_is_my_stream_id(session, stream->stream_id)) { + return NGHTTP2_ERR_INVALID_STREAM_ID; + } + if (stream->state == NGHTTP2_STREAM_OPENING) { + return 0; + } + if (stream->state == NGHTTP2_STREAM_CLOSING) { + return NGHTTP2_ERR_STREAM_CLOSING; + } + return NGHTTP2_ERR_INVALID_STREAM_STATE; +} + +/* + * This function checks HEADERS for reserved stream can be sent. The + * |stream| must be reserved state and the |session| is server side. + * The |stream| can be NULL. + * + * This function returns 0 if it succeeds, or one of the following + * error codes: + * + * NGHTTP2_ERR_STREAM_CLOSED + * The stream is already closed. + * NGHTTP2_ERR_STREAM_SHUT_WR + * The stream is half-closed for transmission. + * NGHTTP2_ERR_PROTO + * The stream is not reserved state + * NGHTTP2_ERR_STREAM_CLOSED + * RST_STREAM was queued for this stream. + * NGHTTP2_ERR_SESSION_CLOSING + * This session is closing. + */ +static int +session_predicate_push_response_headers_send(nghttp2_session *session, + nghttp2_stream *stream) { + int rv; + /* TODO Should disallow HEADERS if GOAWAY has already been issued? */ + rv = session_predicate_for_stream_send(session, stream); + if (rv != 0) { + return rv; + } + assert(stream); + if (stream->state != NGHTTP2_STREAM_RESERVED) { + return NGHTTP2_ERR_PROTO; + } + if (stream->state == NGHTTP2_STREAM_CLOSING) { + return NGHTTP2_ERR_STREAM_CLOSING; + } + return 0; +} + +/* + * This function checks HEADERS, which is neither stream-opening nor + * first response header, with the |stream| can be sent at this time. + * The |stream| can be NULL. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_STREAM_CLOSED + * The stream is already closed or does not exist. + * NGHTTP2_ERR_STREAM_SHUT_WR + * The transmission is not allowed for this stream (e.g., a frame + * with END_STREAM flag set has already sent) + * NGHTTP2_ERR_STREAM_CLOSING + * RST_STREAM was queued for this stream. + * NGHTTP2_ERR_INVALID_STREAM_STATE + * The state of the stream is not valid. + * NGHTTP2_ERR_SESSION_CLOSING + * This session is closing. + */ +static int session_predicate_headers_send(nghttp2_session *session, + nghttp2_stream *stream) { + int rv; + rv = session_predicate_for_stream_send(session, stream); + if (rv != 0) { + return rv; + } + assert(stream); + if (nghttp2_session_is_my_stream_id(session, stream->stream_id)) { + if (stream->state == NGHTTP2_STREAM_CLOSING) { + return NGHTTP2_ERR_STREAM_CLOSING; + } + return 0; + } + if (stream->state == NGHTTP2_STREAM_OPENED) { + return 0; + } + if (stream->state == NGHTTP2_STREAM_CLOSING) { + return NGHTTP2_ERR_STREAM_CLOSING; + } + return NGHTTP2_ERR_INVALID_STREAM_STATE; +} + +/* + * This function checks PUSH_PROMISE frame |frame| with the |stream| + * can be sent at this time. The |stream| can be NULL. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_START_STREAM_NOT_ALLOWED + * New stream cannot be created because GOAWAY is already sent or + * received. + * NGHTTP2_ERR_PROTO + * The client side attempts to send PUSH_PROMISE, or the server + * sends PUSH_PROMISE for the stream not initiated by the client. + * NGHTTP2_ERR_STREAM_CLOSED + * The stream is already closed or does not exist. + * NGHTTP2_ERR_STREAM_CLOSING + * RST_STREAM was queued for this stream. + * NGHTTP2_ERR_STREAM_SHUT_WR + * The transmission is not allowed for this stream (e.g., a frame + * with END_STREAM flag set has already sent) + * NGHTTP2_ERR_PUSH_DISABLED + * The remote peer disabled reception of PUSH_PROMISE. + * NGHTTP2_ERR_SESSION_CLOSING + * This session is closing. + */ +static int session_predicate_push_promise_send(nghttp2_session *session, + nghttp2_stream *stream) { + int rv; + + if (!session->server) { + return NGHTTP2_ERR_PROTO; + } + + rv = session_predicate_for_stream_send(session, stream); + if (rv != 0) { + return rv; + } + + assert(stream); + + if (session->remote_settings.enable_push == 0) { + return NGHTTP2_ERR_PUSH_DISABLED; + } + if (stream->state == NGHTTP2_STREAM_CLOSING) { + return NGHTTP2_ERR_STREAM_CLOSING; + } + if (session->goaway_flags & NGHTTP2_GOAWAY_RECV) { + return NGHTTP2_ERR_START_STREAM_NOT_ALLOWED; + } + return 0; +} + +/* + * This function checks WINDOW_UPDATE with the stream ID |stream_id| + * can be sent at this time. Note that END_STREAM flag of the previous + * frame does not affect the transmission of the WINDOW_UPDATE frame. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_STREAM_CLOSED + * The stream is already closed or does not exist. + * NGHTTP2_ERR_STREAM_CLOSING + * RST_STREAM was queued for this stream. + * NGHTTP2_ERR_INVALID_STREAM_STATE + * The state of the stream is not valid. + * NGHTTP2_ERR_SESSION_CLOSING + * This session is closing. + */ +static int session_predicate_window_update_send(nghttp2_session *session, + int32_t stream_id) { + nghttp2_stream *stream; + if (stream_id == 0) { + /* Connection-level window update */ + return 0; + } + stream = nghttp2_session_get_stream(session, stream_id); + if (stream == NULL) { + return NGHTTP2_ERR_STREAM_CLOSED; + } + if (session_is_closing(session)) { + return NGHTTP2_ERR_SESSION_CLOSING; + } + if (stream->state == NGHTTP2_STREAM_CLOSING) { + return NGHTTP2_ERR_STREAM_CLOSING; + } + if (state_reserved_local(session, stream)) { + return NGHTTP2_ERR_INVALID_STREAM_STATE; + } + return 0; +} + +/* Take into account settings max frame size and both connection-level + flow control here */ +static ssize_t +nghttp2_session_enforce_flow_control_limits(nghttp2_session *session, + nghttp2_stream *stream, + ssize_t requested_window_size) { + DEBUGF(fprintf(stderr, "send: remote windowsize connection=%d, " + "remote maxframsize=%u, stream(id %d)=%d\n", + session->remote_window_size, + session->remote_settings.max_frame_size, stream->stream_id, + stream->remote_window_size)); + + return nghttp2_min(nghttp2_min(nghttp2_min(requested_window_size, + stream->remote_window_size), + session->remote_window_size), + (int32_t)session->remote_settings.max_frame_size); +} + +/* + * Returns the maximum length of next data read. If the + * connection-level and/or stream-wise flow control are enabled, the + * return value takes into account those current window sizes. The remote + * settings for max frame size is also taken into account. + */ +static size_t nghttp2_session_next_data_read(nghttp2_session *session, + nghttp2_stream *stream) { + ssize_t window_size; + + window_size = nghttp2_session_enforce_flow_control_limits( + session, stream, NGHTTP2_DATA_PAYLOADLEN); + + DEBUGF(fprintf(stderr, "send: available window=%zd\n", window_size)); + + return window_size > 0 ? (size_t)window_size : 0; +} + +/* + * This function checks DATA with the |stream| can be sent at this + * time. The |stream| can be NULL. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_STREAM_CLOSED + * The stream is already closed or does not exist. + * NGHTTP2_ERR_STREAM_SHUT_WR + * The transmission is not allowed for this stream (e.g., a frame + * with END_STREAM flag set has already sent) + * NGHTTP2_ERR_STREAM_CLOSING + * RST_STREAM was queued for this stream. + * NGHTTP2_ERR_INVALID_STREAM_STATE + * The state of the stream is not valid. + * NGHTTP2_ERR_SESSION_CLOSING + * This session is closing. + */ +static int nghttp2_session_predicate_data_send(nghttp2_session *session, + nghttp2_stream *stream) { + int rv; + rv = session_predicate_for_stream_send(session, stream); + if (rv != 0) { + return rv; + } + assert(stream); + if (nghttp2_session_is_my_stream_id(session, stream->stream_id)) { + /* Request body data */ + /* If stream->state is NGHTTP2_STREAM_CLOSING, RST_STREAM was + queued but not yet sent. In this case, we won't send DATA + frames. */ + if (stream->state == NGHTTP2_STREAM_CLOSING) { + return NGHTTP2_ERR_STREAM_CLOSING; + } + if (stream->state == NGHTTP2_STREAM_RESERVED) { + return NGHTTP2_ERR_INVALID_STREAM_STATE; + } + return 0; + } + /* Response body data */ + if (stream->state == NGHTTP2_STREAM_OPENED) { + return 0; + } + if (stream->state == NGHTTP2_STREAM_CLOSING) { + return NGHTTP2_ERR_STREAM_CLOSING; + } + return NGHTTP2_ERR_INVALID_STREAM_STATE; +} + +static ssize_t session_call_select_padding(nghttp2_session *session, + const nghttp2_frame *frame, + size_t max_payloadlen) { + ssize_t rv; + + if (frame->hd.length >= max_payloadlen) { + return frame->hd.length; + } + + if (session->callbacks.select_padding_callback) { + size_t max_paddedlen; + + max_paddedlen = + nghttp2_min(frame->hd.length + NGHTTP2_MAX_PADLEN, max_payloadlen); + + rv = session->callbacks.select_padding_callback( + session, frame, max_paddedlen, session->user_data); + if (rv < (ssize_t)frame->hd.length || rv > (ssize_t)max_paddedlen) { + return NGHTTP2_ERR_CALLBACK_FAILURE; + } + return rv; + } + return frame->hd.length; +} + +/* Add padding to HEADERS or PUSH_PROMISE. We use + frame->headers.padlen in this function to use the fact that + frame->push_promise has also padlen in the same position. */ +static int session_headers_add_pad(nghttp2_session *session, + nghttp2_frame *frame) { + int rv; + ssize_t padded_payloadlen; + nghttp2_active_outbound_item *aob; + nghttp2_bufs *framebufs; + size_t padlen; + size_t max_payloadlen; + + aob = &session->aob; + framebufs = &aob->framebufs; + + max_payloadlen = nghttp2_min(NGHTTP2_MAX_PAYLOADLEN, + frame->hd.length + NGHTTP2_MAX_PADLEN); + + padded_payloadlen = + session_call_select_padding(session, frame, max_payloadlen); + + if (nghttp2_is_fatal((int)padded_payloadlen)) { + return (int)padded_payloadlen; + } + + padlen = padded_payloadlen - frame->hd.length; + + DEBUGF(fprintf(stderr, "send: padding selected: payloadlen=%zd, padlen=%zu\n", + padded_payloadlen, padlen)); + + rv = nghttp2_frame_add_pad(framebufs, &frame->hd, padlen, 0); + + if (rv != 0) { + return rv; + } + + frame->headers.padlen = padlen; + + return 0; +} + +static size_t session_estimate_headers_payload(nghttp2_session *session, + const nghttp2_nv *nva, + size_t nvlen, + size_t additional) { + return nghttp2_hd_deflate_bound(&session->hd_deflater, nva, nvlen) + + additional; +} + +/* + * This function serializes frame for transmission. + * + * This function returns 0 if it succeeds, or one of negative error + * codes, including both fatal and non-fatal ones. + */ +static int session_prep_frame(nghttp2_session *session, + nghttp2_outbound_item *item) { + int rv; + nghttp2_frame *frame; + nghttp2_mem *mem; + + mem = &session->mem; + frame = &item->frame; + + if (frame->hd.type != NGHTTP2_DATA) { + switch (frame->hd.type) { + case NGHTTP2_HEADERS: { + nghttp2_headers_aux_data *aux_data; + size_t estimated_payloadlen; + + aux_data = &item->aux_data.headers; + + estimated_payloadlen = session_estimate_headers_payload( + session, frame->headers.nva, frame->headers.nvlen, + NGHTTP2_PRIORITY_SPECLEN); + + if (estimated_payloadlen > NGHTTP2_MAX_HEADERSLEN) { + return NGHTTP2_ERR_FRAME_SIZE_ERROR; + } + + if (frame->headers.cat == NGHTTP2_HCAT_REQUEST) { + /* initial HEADERS, which opens stream */ + nghttp2_stream *stream; + + stream = nghttp2_session_open_stream( + session, frame->hd.stream_id, NGHTTP2_STREAM_FLAG_NONE, + &frame->headers.pri_spec, NGHTTP2_STREAM_INITIAL, + aux_data->stream_user_data); + + if (stream == NULL) { + return NGHTTP2_ERR_NOMEM; + } + + rv = session_predicate_request_headers_send(session, item); + if (rv != 0) { + return rv; + } + + if (session_enforce_http_messaging(session)) { + nghttp2_http_record_request_method(stream, frame); + } + } else { + nghttp2_stream *stream; + + stream = nghttp2_session_get_stream(session, frame->hd.stream_id); + + if (session_predicate_push_response_headers_send(session, stream) == + 0) { + frame->headers.cat = NGHTTP2_HCAT_PUSH_RESPONSE; + + if (aux_data->stream_user_data) { + stream->stream_user_data = aux_data->stream_user_data; + } + } else if (session_predicate_response_headers_send(session, stream) == + 0) { + frame->headers.cat = NGHTTP2_HCAT_RESPONSE; + } else { + frame->headers.cat = NGHTTP2_HCAT_HEADERS; + + rv = session_predicate_headers_send(session, stream); + + if (rv != 0) { + if (stream && stream->item == item) { + int rv2; + + rv2 = nghttp2_stream_detach_item(stream, session); + + if (nghttp2_is_fatal(rv2)) { + return rv2; + } + } + + return rv; + } + } + } + + rv = nghttp2_frame_pack_headers(&session->aob.framebufs, &frame->headers, + &session->hd_deflater); + + if (rv != 0) { + return rv; + } + + DEBUGF(fprintf(stderr, + "send: before padding, HEADERS serialized in %zd bytes\n", + nghttp2_bufs_len(&session->aob.framebufs))); + + rv = session_headers_add_pad(session, frame); + + if (rv != 0) { + return rv; + } + + DEBUGF(fprintf(stderr, "send: HEADERS finally serialized in %zd bytes\n", + nghttp2_bufs_len(&session->aob.framebufs))); + + break; + } + case NGHTTP2_PRIORITY: { + if (session_is_closing(session)) { + return NGHTTP2_ERR_SESSION_CLOSING; + } + /* PRIORITY frame can be sent at any time and to any stream + ID. */ + nghttp2_frame_pack_priority(&session->aob.framebufs, &frame->priority); + + /* Peer can send PRIORITY frame against idle stream to create + "anchor" in dependency tree. Only client can do this in + nghttp2. In nghttp2, only server retains non-active (closed + or idle) streams in memory, so we don't open stream here. */ + break; + } + case NGHTTP2_RST_STREAM: + if (session_is_closing(session)) { + return NGHTTP2_ERR_SESSION_CLOSING; + } + nghttp2_frame_pack_rst_stream(&session->aob.framebufs, + &frame->rst_stream); + break; + case NGHTTP2_SETTINGS: { + rv = nghttp2_frame_pack_settings(&session->aob.framebufs, + &frame->settings); + if (rv != 0) { + return rv; + } + break; + } + case NGHTTP2_PUSH_PROMISE: { + nghttp2_stream *stream; + nghttp2_headers_aux_data *aux_data; + nghttp2_priority_spec pri_spec; + size_t estimated_payloadlen; + + aux_data = &item->aux_data.headers; + + stream = nghttp2_session_get_stream(session, frame->hd.stream_id); + + /* stream could be NULL if associated stream was already + closed. */ + if (stream) { + nghttp2_priority_spec_init(&pri_spec, stream->stream_id, + NGHTTP2_DEFAULT_WEIGHT, 0); + } else { + nghttp2_priority_spec_default_init(&pri_spec); + } + + if (!nghttp2_session_open_stream( + session, frame->push_promise.promised_stream_id, + NGHTTP2_STREAM_FLAG_NONE, &pri_spec, NGHTTP2_STREAM_RESERVED, + aux_data->stream_user_data)) { + return NGHTTP2_ERR_NOMEM; + } + + estimated_payloadlen = session_estimate_headers_payload( + session, frame->push_promise.nva, frame->push_promise.nvlen, 0); + + if (estimated_payloadlen > NGHTTP2_MAX_HEADERSLEN) { + return NGHTTP2_ERR_FRAME_SIZE_ERROR; + } + + /* predicte should fail if stream is NULL. */ + rv = session_predicate_push_promise_send(session, stream); + if (rv != 0) { + return rv; + } + + assert(stream); + + rv = nghttp2_frame_pack_push_promise( + &session->aob.framebufs, &frame->push_promise, &session->hd_deflater); + if (rv != 0) { + return rv; + } + rv = session_headers_add_pad(session, frame); + if (rv != 0) { + return rv; + } + + break; + } + case NGHTTP2_PING: + if (session_is_closing(session)) { + return NGHTTP2_ERR_SESSION_CLOSING; + } + nghttp2_frame_pack_ping(&session->aob.framebufs, &frame->ping); + break; + case NGHTTP2_WINDOW_UPDATE: { + rv = session_predicate_window_update_send(session, frame->hd.stream_id); + if (rv != 0) { + return rv; + } + nghttp2_frame_pack_window_update(&session->aob.framebufs, + &frame->window_update); + break; + } + case NGHTTP2_GOAWAY: + rv = nghttp2_frame_pack_goaway(&session->aob.framebufs, &frame->goaway); + if (rv != 0) { + return rv; + } + session->local_last_stream_id = frame->goaway.last_stream_id; + + break; + default: + return NGHTTP2_ERR_INVALID_ARGUMENT; + } + return 0; + } else { + size_t next_readmax; + nghttp2_stream *stream; + + stream = nghttp2_session_get_stream(session, frame->hd.stream_id); + + if (stream) { + assert(stream->item == item); + } + + rv = nghttp2_session_predicate_data_send(session, stream); + if (rv != 0) { + if (stream) { + int rv2; + + rv2 = nghttp2_stream_detach_item(stream, session); + + if (nghttp2_is_fatal(rv2)) { + return rv2; + } + } + + return rv; + } + /* Assuming stream is not NULL */ + assert(stream); + next_readmax = nghttp2_session_next_data_read(session, stream); + + if (next_readmax == 0) { + + /* This must be true since we only pop DATA frame item from + queue when session->remote_window_size > 0 */ + assert(session->remote_window_size > 0); + + rv = nghttp2_stream_defer_item( + stream, NGHTTP2_STREAM_FLAG_DEFERRED_FLOW_CONTROL, session); + + if (nghttp2_is_fatal(rv)) { + return rv; + } + + session->aob.item = NULL; + active_outbound_item_reset(&session->aob, mem); + return NGHTTP2_ERR_DEFERRED; + } + + rv = nghttp2_session_pack_data(session, &session->aob.framebufs, + next_readmax, frame, &item->aux_data.data, + stream); + if (rv == NGHTTP2_ERR_DEFERRED) { + rv = nghttp2_stream_defer_item(stream, NGHTTP2_STREAM_FLAG_DEFERRED_USER, + session); + + if (nghttp2_is_fatal(rv)) { + return rv; + } + + session->aob.item = NULL; + active_outbound_item_reset(&session->aob, mem); + return NGHTTP2_ERR_DEFERRED; + } + if (rv == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) { + rv = nghttp2_stream_detach_item(stream, session); + + if (nghttp2_is_fatal(rv)) { + return rv; + } + + rv = nghttp2_session_add_rst_stream(session, frame->hd.stream_id, + NGHTTP2_INTERNAL_ERROR); + if (nghttp2_is_fatal(rv)) { + return rv; + } + return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; + } + if (rv != 0) { + int rv2; + + rv2 = nghttp2_stream_detach_item(stream, session); + + if (nghttp2_is_fatal(rv2)) { + return rv2; + } + + return rv; + } + return 0; + } +} + +nghttp2_outbound_item * +nghttp2_session_get_next_ob_item(nghttp2_session *session) { + if (nghttp2_outbound_queue_top(&session->ob_urgent)) { + return nghttp2_outbound_queue_top(&session->ob_urgent); + } + + if (nghttp2_outbound_queue_top(&session->ob_reg)) { + return nghttp2_outbound_queue_top(&session->ob_reg); + } + + if (!session_is_outgoing_concurrent_streams_max(session)) { + if (nghttp2_outbound_queue_top(&session->ob_syn)) { + return nghttp2_outbound_queue_top(&session->ob_syn); + } + } + + if (session->remote_window_size > 0 && + !nghttp2_pq_empty(&session->ob_da_pq)) { + return nghttp2_pq_top(&session->ob_da_pq); + } + + return NULL; +} + +nghttp2_outbound_item * +nghttp2_session_pop_next_ob_item(nghttp2_session *session) { + nghttp2_outbound_item *item; + + item = nghttp2_outbound_queue_top(&session->ob_urgent); + if (item) { + nghttp2_outbound_queue_pop(&session->ob_urgent); + item->queued = 0; + return item; + } + + item = nghttp2_outbound_queue_top(&session->ob_reg); + if (item) { + nghttp2_outbound_queue_pop(&session->ob_reg); + item->queued = 0; + return item; + } + + if (!session_is_outgoing_concurrent_streams_max(session)) { + item = nghttp2_outbound_queue_top(&session->ob_syn); + if (item) { + nghttp2_outbound_queue_pop(&session->ob_syn); + item->queued = 0; + return item; + } + } + + if (session->remote_window_size > 0 && + !nghttp2_pq_empty(&session->ob_da_pq)) { + item = nghttp2_pq_top(&session->ob_da_pq); + nghttp2_pq_pop(&session->ob_da_pq); + item->queued = 0; + return item; + } + + return NULL; +} + +static int session_call_before_frame_send(nghttp2_session *session, + nghttp2_frame *frame) { + int rv; + if (session->callbacks.before_frame_send_callback) { + rv = session->callbacks.before_frame_send_callback(session, frame, + session->user_data); + if (rv != 0) { + return NGHTTP2_ERR_CALLBACK_FAILURE; + } + } + return 0; +} + +static int session_call_on_frame_send(nghttp2_session *session, + nghttp2_frame *frame) { + int rv; + if (session->callbacks.on_frame_send_callback) { + rv = session->callbacks.on_frame_send_callback(session, frame, + session->user_data); + if (rv != 0) { + return NGHTTP2_ERR_CALLBACK_FAILURE; + } + } + return 0; +} + +static int find_stream_on_goaway_func(nghttp2_map_entry *entry, void *ptr) { + nghttp2_close_stream_on_goaway_arg *arg; + nghttp2_stream *stream; + + arg = (nghttp2_close_stream_on_goaway_arg *)ptr; + stream = (nghttp2_stream *)entry; + + if (nghttp2_session_is_my_stream_id(arg->session, stream->stream_id)) { + if (arg->incoming) { + return 0; + } + } else if (!arg->incoming) { + return 0; + } + + if (stream->state != NGHTTP2_STREAM_IDLE && + (stream->flags & NGHTTP2_STREAM_FLAG_CLOSED) == 0 && + stream->stream_id > arg->last_stream_id) { + /* We are collecting streams to close because we cannot call + nghttp2_session_close_stream() inside nghttp2_map_each(). + Reuse closed_next member.. bad choice? */ + assert(stream->closed_next == NULL); + assert(stream->closed_prev == NULL); + + if (arg->head) { + stream->closed_next = arg->head; + arg->head = stream; + } else { + arg->head = stream; + } + } + + return 0; +} + +/* Closes non-idle and non-closed streams whose stream ID > + last_stream_id. If incoming is nonzero, we are going to close + incoming streams. Otherwise, close outgoing streams. */ +static int session_close_stream_on_goaway(nghttp2_session *session, + int32_t last_stream_id, + int incoming) { + int rv; + nghttp2_stream *stream, *next_stream; + nghttp2_close_stream_on_goaway_arg arg = {session, NULL, last_stream_id, + incoming}; + + rv = nghttp2_map_each(&session->streams, find_stream_on_goaway_func, &arg); + assert(rv == 0); + + stream = arg.head; + while (stream) { + next_stream = stream->closed_next; + stream->closed_next = NULL; + rv = nghttp2_session_close_stream(session, stream->stream_id, + NGHTTP2_REFUSED_STREAM); + + /* stream may be deleted here */ + + stream = next_stream; + + if (nghttp2_is_fatal(rv)) { + /* Clean up closed_next member just in case */ + while (stream) { + next_stream = stream->closed_next; + stream->closed_next = NULL; + stream = next_stream; + } + return rv; + } + } + + return 0; +} + +static void session_outbound_item_schedule(nghttp2_session *session, + nghttp2_outbound_item *item, + int32_t weight) { + /* Schedule next write. Offset proportional to the write size. + Stream with heavier weight is scheduled earlier. */ + size_t delta = item->frame.hd.length * NGHTTP2_MAX_WEIGHT / weight; + + if (session->last_cycle < item->cycle) { + session->last_cycle = item->cycle; + } + + /* We pretend to ignore overflow given that the value range of + item->cycle, which is uint64_t. nghttp2 won't explode even when + overflow occurs, there might be some disturbance of priority. */ + item->cycle = session->last_cycle + delta; +} + +/* + * Called after a frame is sent. This function runs + * on_frame_send_callback and handles stream closure upon END_STREAM + * or RST_STREAM. This function does not reset session->aob. It is a + * responsibility of session_after_frame_sent2. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + * NGHTTP2_ERR_CALLBACK_FAILURE + * The callback function failed. + */ +static int session_after_frame_sent1(nghttp2_session *session) { + int rv; + nghttp2_active_outbound_item *aob = &session->aob; + nghttp2_outbound_item *item = aob->item; + nghttp2_bufs *framebufs = &aob->framebufs; + nghttp2_frame *frame; + + frame = &item->frame; + + if (frame->hd.type != NGHTTP2_DATA) { + + if (frame->hd.type == NGHTTP2_HEADERS || + frame->hd.type == NGHTTP2_PUSH_PROMISE) { + + if (nghttp2_bufs_next_present(framebufs)) { + DEBUGF(fprintf(stderr, "send: CONTINUATION exists, just return\n")); + return 0; + } + } + rv = session_call_on_frame_send(session, frame); + if (nghttp2_is_fatal(rv)) { + return rv; + } + switch (frame->hd.type) { + case NGHTTP2_HEADERS: { + nghttp2_headers_aux_data *aux_data; + nghttp2_stream *stream; + + stream = nghttp2_session_get_stream(session, frame->hd.stream_id); + if (!stream) { + break; + } + + if (stream->item == item) { + rv = nghttp2_stream_detach_item(stream, session); + + if (nghttp2_is_fatal(rv)) { + return rv; + } + } + + switch (frame->headers.cat) { + case NGHTTP2_HCAT_REQUEST: { + stream->state = NGHTTP2_STREAM_OPENING; + if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) { + nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_WR); + } + rv = nghttp2_session_close_stream_if_shut_rdwr(session, stream); + if (nghttp2_is_fatal(rv)) { + return rv; + } + /* We assume aux_data is a pointer to nghttp2_headers_aux_data */ + aux_data = &item->aux_data.headers; + if (aux_data->data_prd.read_callback) { + /* nghttp2_submit_data() makes a copy of aux_data->data_prd */ + rv = nghttp2_submit_data(session, NGHTTP2_FLAG_END_STREAM, + frame->hd.stream_id, &aux_data->data_prd); + if (nghttp2_is_fatal(rv)) { + return rv; + } + /* TODO nghttp2_submit_data() may fail if stream has already + DATA frame item. We might have to handle it here. */ + } + break; + } + case NGHTTP2_HCAT_PUSH_RESPONSE: + stream->flags &= ~NGHTTP2_STREAM_FLAG_PUSH; + ++session->num_outgoing_streams; + /* Fall through */ + case NGHTTP2_HCAT_RESPONSE: + stream->state = NGHTTP2_STREAM_OPENED; + /* Fall through */ + case NGHTTP2_HCAT_HEADERS: + if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) { + nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_WR); + } + rv = nghttp2_session_close_stream_if_shut_rdwr(session, stream); + if (nghttp2_is_fatal(rv)) { + return rv; + } + /* We assume aux_data is a pointer to nghttp2_headers_aux_data */ + aux_data = &item->aux_data.headers; + if (aux_data->data_prd.read_callback) { + rv = nghttp2_submit_data(session, NGHTTP2_FLAG_END_STREAM, + frame->hd.stream_id, &aux_data->data_prd); + if (nghttp2_is_fatal(rv)) { + return rv; + } + /* TODO nghttp2_submit_data() may fail if stream has already + DATA frame item. We might have to handle it here. */ + } + break; + } + break; + } + case NGHTTP2_PRIORITY: { + nghttp2_stream *stream; + + if (session->server) { + break; + } + + stream = nghttp2_session_get_stream_raw(session, frame->hd.stream_id); + + if (!stream) { + break; + } + + rv = nghttp2_session_reprioritize_stream(session, stream, + &frame->priority.pri_spec); + + if (nghttp2_is_fatal(rv)) { + return rv; + } + + break; + } + case NGHTTP2_RST_STREAM: + rv = nghttp2_session_close_stream(session, frame->hd.stream_id, + frame->rst_stream.error_code); + if (nghttp2_is_fatal(rv)) { + return rv; + } + break; + case NGHTTP2_GOAWAY: { + nghttp2_goaway_aux_data *aux_data; + + aux_data = &item->aux_data.goaway; + + if ((aux_data->flags & NGHTTP2_GOAWAY_AUX_SHUTDOWN_NOTICE) == 0) { + + if (aux_data->flags & NGHTTP2_GOAWAY_AUX_TERM_ON_SEND) { + session->goaway_flags |= NGHTTP2_GOAWAY_TERM_SENT; + } + + session->goaway_flags |= NGHTTP2_GOAWAY_SENT; + + rv = session_close_stream_on_goaway(session, + frame->goaway.last_stream_id, 1); + + if (nghttp2_is_fatal(rv)) { + return rv; + } + } + + break; + } + default: + break; + } + + return 0; + } else { + nghttp2_stream *stream; + nghttp2_data_aux_data *aux_data; + + aux_data = &item->aux_data.data; + + stream = nghttp2_session_get_stream(session, frame->hd.stream_id); + /* We update flow control window after a frame was completely + sent. This is possible because we choose payload length not to + exceed the window */ + session->remote_window_size -= frame->hd.length; + if (stream) { + stream->remote_window_size -= frame->hd.length; + } + + if (stream && aux_data->eof) { + rv = nghttp2_stream_detach_item(stream, session); + + if (nghttp2_is_fatal(rv)) { + return rv; + } + + /* Call on_frame_send_callback after + nghttp2_stream_detach_item(), so that application can issue + nghttp2_submit_data() in the callback. */ + if (session->callbacks.on_frame_send_callback) { + rv = session_call_on_frame_send(session, frame); + + if (nghttp2_is_fatal(rv)) { + return rv; + } + } + + if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) { + int stream_closed; + + stream_closed = + (stream->shut_flags & NGHTTP2_SHUT_RDWR) == NGHTTP2_SHUT_RDWR; + + nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_WR); + + rv = nghttp2_session_close_stream_if_shut_rdwr(session, stream); + if (nghttp2_is_fatal(rv)) { + return rv; + } + /* stream may be NULL if it was closed */ + if (stream_closed) { + stream = NULL; + } + } + return 0; + } + + if (session->callbacks.on_frame_send_callback) { + rv = session_call_on_frame_send(session, frame); + + if (nghttp2_is_fatal(rv)) { + return rv; + } + } + + return 0; + } + /* Unreachable */ + assert(0); + return 0; +} + +/* + * Called after a frame is sent and session_after_frame_sent1. This + * function is responsible to reset session->aob. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + * NGHTTP2_ERR_CALLBACK_FAILURE + * The callback function failed. + */ +static int session_after_frame_sent2(nghttp2_session *session) { + int rv; + nghttp2_active_outbound_item *aob = &session->aob; + nghttp2_outbound_item *item = aob->item; + nghttp2_bufs *framebufs = &aob->framebufs; + nghttp2_frame *frame; + nghttp2_mem *mem; + + mem = &session->mem; + frame = &item->frame; + + if (frame->hd.type != NGHTTP2_DATA) { + + if (frame->hd.type == NGHTTP2_HEADERS || + frame->hd.type == NGHTTP2_PUSH_PROMISE) { + + if (nghttp2_bufs_next_present(framebufs)) { + framebufs->cur = framebufs->cur->next; + + DEBUGF(fprintf(stderr, "send: next CONTINUATION frame, %zu bytes\n", + nghttp2_buf_len(&framebufs->cur->buf))); + + return 0; + } + } + + active_outbound_item_reset(&session->aob, mem); + + return 0; + } else { + nghttp2_outbound_item *next_item; + nghttp2_stream *stream; + nghttp2_data_aux_data *aux_data; + + aux_data = &item->aux_data.data; + + /* On EOF, we have already detached data. Please note that + application may issue nghttp2_submit_data() in + on_frame_send_callback (call from session_after_frame_sent1), + which attach data to stream. We don't want to detach it. */ + if (aux_data->eof) { + active_outbound_item_reset(aob, mem); + + return 0; + } + + /* Reset no_copy here because next write may not use this. */ + aux_data->no_copy = 0; + + stream = nghttp2_session_get_stream(session, frame->hd.stream_id); + + /* If session is closed or RST_STREAM was queued, we won't send + further data. */ + if (nghttp2_session_predicate_data_send(session, stream) != 0) { + if (stream) { + rv = nghttp2_stream_detach_item(stream, session); + + if (nghttp2_is_fatal(rv)) { + return rv; + } + } + + active_outbound_item_reset(aob, mem); + + return 0; + } + + /* Assuming stream is not NULL */ + assert(stream); + next_item = nghttp2_session_get_next_ob_item(session); + + /* If priority of this stream is higher or equal to other stream + waiting at the top of the queue, we continue to send this + data. */ + if (stream->dpri == NGHTTP2_STREAM_DPRI_TOP && + (next_item == NULL || (next_item->frame.hd.type == NGHTTP2_DATA && + outbound_item_less(item, next_item)))) { + size_t next_readmax; + + next_readmax = nghttp2_session_next_data_read(session, stream); + + if (next_readmax == 0) { + + if (session->remote_window_size == 0 && + stream->remote_window_size > 0) { + + /* If DATA cannot be sent solely due to connection level + window size, just push item to queue again. We never pop + DATA item while connection level window size is 0. */ + rv = nghttp2_pq_push(&session->ob_da_pq, aob->item); + + if (nghttp2_is_fatal(rv)) { + return rv; + } + + aob->item->queued = 1; + } else { + rv = nghttp2_stream_defer_item( + stream, NGHTTP2_STREAM_FLAG_DEFERRED_FLOW_CONTROL, session); + + if (nghttp2_is_fatal(rv)) { + return rv; + } + } + + aob->item = NULL; + active_outbound_item_reset(aob, mem); + + return 0; + } + + nghttp2_bufs_reset(framebufs); + + rv = nghttp2_session_pack_data(session, framebufs, next_readmax, frame, + aux_data, stream); + if (nghttp2_is_fatal(rv)) { + return rv; + } + if (rv == NGHTTP2_ERR_DEFERRED) { + rv = nghttp2_stream_defer_item( + stream, NGHTTP2_STREAM_FLAG_DEFERRED_USER, session); + + if (nghttp2_is_fatal(rv)) { + return rv; + } + + aob->item = NULL; + active_outbound_item_reset(aob, mem); + + return 0; + } + + if (rv == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) { + /* Stop DATA frame chain and issue RST_STREAM to close the + stream. We don't return + NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE intentionally. */ + rv = nghttp2_session_add_rst_stream(session, frame->hd.stream_id, + NGHTTP2_INTERNAL_ERROR); + + if (nghttp2_is_fatal(rv)) { + return rv; + } + + rv = nghttp2_stream_detach_item(stream, session); + + if (nghttp2_is_fatal(rv)) { + return rv; + } + + active_outbound_item_reset(aob, mem); + + return 0; + } + assert(rv == 0); + + if (aux_data->no_copy) { + aob->state = NGHTTP2_OB_SEND_NO_COPY; + } else { + aob->state = NGHTTP2_OB_SEND_DATA; + } + + return 0; + } + + if (stream->dpri == NGHTTP2_STREAM_DPRI_TOP) { + rv = nghttp2_pq_push(&session->ob_da_pq, aob->item); + + if (nghttp2_is_fatal(rv)) { + return rv; + } + + aob->item->queued = 1; + } + + aob->item = NULL; + active_outbound_item_reset(&session->aob, mem); + return 0; + } + /* Unreachable */ + assert(0); + return 0; +} + +static int session_call_send_data(nghttp2_session *session, + nghttp2_outbound_item *item, + nghttp2_bufs *framebufs) { + int rv; + nghttp2_buf *buf; + size_t length; + nghttp2_frame *frame; + nghttp2_data_aux_data *aux_data; + + buf = &framebufs->cur->buf; + frame = &item->frame; + length = frame->hd.length - frame->data.padlen; + aux_data = &item->aux_data.data; + + rv = session->callbacks.send_data_callback(session, frame, buf->pos, length, + &aux_data->data_prd.source, + session->user_data); + + if (rv == 0 || rv == NGHTTP2_ERR_WOULDBLOCK || + rv == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) { + return rv; + } + + return NGHTTP2_ERR_CALLBACK_FAILURE; +} + +static ssize_t nghttp2_session_mem_send_internal(nghttp2_session *session, + const uint8_t **data_ptr, + int fast_cb) { + int rv; + nghttp2_active_outbound_item *aob; + nghttp2_bufs *framebufs; + nghttp2_mem *mem; + + mem = &session->mem; + aob = &session->aob; + framebufs = &aob->framebufs; + + *data_ptr = NULL; + for (;;) { + switch (aob->state) { + case NGHTTP2_OB_POP_ITEM: { + nghttp2_outbound_item *item; + + item = nghttp2_session_pop_next_ob_item(session); + if (item == NULL) { + return 0; + } + + if (item->frame.hd.type == NGHTTP2_DATA || + item->frame.hd.type == NGHTTP2_HEADERS) { + nghttp2_frame *frame; + nghttp2_stream *stream; + + frame = &item->frame; + stream = nghttp2_session_get_stream(session, frame->hd.stream_id); + + if (stream && item == stream->item && + stream->dpri != NGHTTP2_STREAM_DPRI_TOP) { + /* We have DATA with higher priority in queue within the + same dependency tree. */ + break; + } + } + + rv = session_prep_frame(session, item); + if (rv == NGHTTP2_ERR_DEFERRED) { + DEBUGF(fprintf(stderr, "send: frame transmission deferred\n")); + break; + } + if (rv < 0) { + int32_t opened_stream_id = 0; + uint32_t error_code = NGHTTP2_INTERNAL_ERROR; + + DEBUGF(fprintf(stderr, "send: frame preparation failed with %s\n", + nghttp2_strerror(rv))); + /* TODO If the error comes from compressor, the connection + must be closed. */ + if (item->frame.hd.type != NGHTTP2_DATA && + session->callbacks.on_frame_not_send_callback && is_non_fatal(rv)) { + nghttp2_frame *frame = &item->frame; + /* The library is responsible for the transmission of + WINDOW_UPDATE frame, so we don't call error callback for + it. */ + if (frame->hd.type != NGHTTP2_WINDOW_UPDATE && + session->callbacks.on_frame_not_send_callback( + session, frame, rv, session->user_data) != 0) { + + nghttp2_outbound_item_free(item, mem); + nghttp2_mem_free(mem, item); + + return NGHTTP2_ERR_CALLBACK_FAILURE; + } + } + /* We have to close stream opened by failed request HEADERS + or PUSH_PROMISE. */ + switch (item->frame.hd.type) { + case NGHTTP2_HEADERS: + if (item->frame.headers.cat == NGHTTP2_HCAT_REQUEST) { + opened_stream_id = item->frame.hd.stream_id; + if (item->aux_data.headers.canceled) { + error_code = item->aux_data.headers.error_code; + } + } + break; + case NGHTTP2_PUSH_PROMISE: + opened_stream_id = item->frame.push_promise.promised_stream_id; + break; + } + if (opened_stream_id) { + /* careful not to override rv */ + int rv2; + rv2 = nghttp2_session_close_stream(session, opened_stream_id, + error_code); + + if (nghttp2_is_fatal(rv2)) { + return rv2; + } + } + + nghttp2_outbound_item_free(item, mem); + nghttp2_mem_free(mem, item); + active_outbound_item_reset(aob, mem); + + if (rv == NGHTTP2_ERR_HEADER_COMP) { + /* If header compression error occurred, should terminiate + connection. */ + rv = nghttp2_session_terminate_session(session, + NGHTTP2_INTERNAL_ERROR); + } + if (nghttp2_is_fatal(rv)) { + return rv; + } + break; + } + + aob->item = item; + + nghttp2_bufs_rewind(framebufs); + + if (item->frame.hd.type != NGHTTP2_DATA) { + nghttp2_frame *frame; + + frame = &item->frame; + + DEBUGF(fprintf(stderr, "send: next frame: payloadlen=%zu, type=%u, " + "flags=0x%02x, stream_id=%d\n", + frame->hd.length, frame->hd.type, frame->hd.flags, + frame->hd.stream_id)); + + rv = session_call_before_frame_send(session, frame); + if (nghttp2_is_fatal(rv)) { + return rv; + } + } else { + DEBUGF(fprintf(stderr, "send: next frame: DATA\n")); + + if (item->aux_data.data.no_copy) { + aob->state = NGHTTP2_OB_SEND_NO_COPY; + break; + } + } + + DEBUGF(fprintf(stderr, + "send: start transmitting frame type=%u, length=%zd\n", + framebufs->cur->buf.pos[3], + framebufs->cur->buf.last - framebufs->cur->buf.pos)); + + aob->state = NGHTTP2_OB_SEND_DATA; + + break; + } + case NGHTTP2_OB_SEND_DATA: { + size_t datalen; + nghttp2_buf *buf; + + buf = &framebufs->cur->buf; + + if (buf->pos == buf->last) { + DEBUGF(fprintf(stderr, "send: end transmission of a frame\n")); + + /* Frame has completely sent */ + if (fast_cb) { + rv = session_after_frame_sent2(session); + } else { + rv = session_after_frame_sent1(session); + if (rv < 0) { + /* FATAL */ + assert(nghttp2_is_fatal(rv)); + return rv; + } + rv = session_after_frame_sent2(session); + } + if (rv < 0) { + /* FATAL */ + assert(nghttp2_is_fatal(rv)); + return rv; + } + /* We have already adjusted the next state */ + break; + } + + *data_ptr = buf->pos; + datalen = nghttp2_buf_len(buf); + + /* We increment the offset here. If send_callback does not send + everything, we will adjust it. */ + buf->pos += datalen; + + return datalen; + } + case NGHTTP2_OB_SEND_NO_COPY: { + nghttp2_stream *stream; + nghttp2_frame *frame; + + DEBUGF(fprintf(stderr, "send: no copy DATA\n")); + + frame = &aob->item->frame; + + stream = nghttp2_session_get_stream(session, frame->hd.stream_id); + if (stream == NULL) { + DEBUGF(fprintf( + stderr, + "send: no copy DATA cancelled because stream was closed\n")); + + active_outbound_item_reset(aob, mem); + + break; + } + + rv = session_call_send_data(session, aob->item, framebufs); + if (nghttp2_is_fatal(rv)) { + return rv; + } + + if (rv == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) { + rv = nghttp2_stream_detach_item(stream, session); + + if (nghttp2_is_fatal(rv)) { + return rv; + } + + rv = nghttp2_session_add_rst_stream(session, frame->hd.stream_id, + NGHTTP2_INTERNAL_ERROR); + if (nghttp2_is_fatal(rv)) { + return rv; + } + + active_outbound_item_reset(aob, mem); + + break; + } + + if (rv == NGHTTP2_ERR_WOULDBLOCK) { + return 0; + } + + assert(rv == 0); + + rv = session_after_frame_sent1(session); + if (rv < 0) { + assert(nghttp2_is_fatal(rv)); + return rv; + } + rv = session_after_frame_sent2(session); + if (rv < 0) { + assert(nghttp2_is_fatal(rv)); + return rv; + } + + /* We have already adjusted the next state */ + + break; + } + case NGHTTP2_OB_SEND_CLIENT_MAGIC: { + size_t datalen; + nghttp2_buf *buf; + + buf = &framebufs->cur->buf; + + if (buf->pos == buf->last) { + DEBUGF(fprintf(stderr, "send: end transmission of client magic\n")); + active_outbound_item_reset(aob, mem); + break; + } + + *data_ptr = buf->pos; + datalen = nghttp2_buf_len(buf); + + buf->pos += datalen; + + return datalen; + } + } + } +} + +ssize_t nghttp2_session_mem_send(nghttp2_session *session, + const uint8_t **data_ptr) { + int rv; + ssize_t len; + + len = nghttp2_session_mem_send_internal(session, data_ptr, 1); + if (len <= 0) { + return len; + } + + if (session->aob.item) { + /* We have to call session_after_frame_sent1 here to handle stream + closure upon transmission of frames. Otherwise, END_STREAM may + be reached to client before we call nghttp2_session_mem_send + again and we may get exceeding number of incoming streams. */ + rv = session_after_frame_sent1(session); + if (rv < 0) { + assert(nghttp2_is_fatal(rv)); + return (ssize_t)rv; + } + } + + return len; +} + +int nghttp2_session_send(nghttp2_session *session) { + const uint8_t *data; + ssize_t datalen; + ssize_t sentlen; + nghttp2_bufs *framebufs; + + framebufs = &session->aob.framebufs; + + for (;;) { + datalen = nghttp2_session_mem_send_internal(session, &data, 0); + if (datalen <= 0) { + return (int)datalen; + } + sentlen = session->callbacks.send_callback(session, data, datalen, 0, + session->user_data); + if (sentlen < 0) { + if (sentlen == NGHTTP2_ERR_WOULDBLOCK) { + /* Transmission canceled. Rewind the offset */ + framebufs->cur->buf.pos -= datalen; + + return 0; + } + return NGHTTP2_ERR_CALLBACK_FAILURE; + } + /* Rewind the offset to the amount of unsent bytes */ + framebufs->cur->buf.pos -= datalen - sentlen; + } +} + +static ssize_t session_recv(nghttp2_session *session, uint8_t *buf, + size_t len) { + ssize_t rv; + rv = session->callbacks.recv_callback(session, buf, len, 0, + session->user_data); + if (rv > 0) { + if ((size_t)rv > len) { + return NGHTTP2_ERR_CALLBACK_FAILURE; + } + } else if (rv < 0 && rv != NGHTTP2_ERR_WOULDBLOCK && rv != NGHTTP2_ERR_EOF) { + return NGHTTP2_ERR_CALLBACK_FAILURE; + } + return rv; +} + +static int session_call_on_begin_frame(nghttp2_session *session, + const nghttp2_frame_hd *hd) { + int rv; + + if (session->callbacks.on_begin_frame_callback) { + + rv = session->callbacks.on_begin_frame_callback(session, hd, + session->user_data); + + if (rv != 0) { + return NGHTTP2_ERR_CALLBACK_FAILURE; + } + } + + return 0; +} + +static int session_call_on_frame_received(nghttp2_session *session, + nghttp2_frame *frame) { + int rv; + if (session->callbacks.on_frame_recv_callback) { + rv = session->callbacks.on_frame_recv_callback(session, frame, + session->user_data); + if (rv != 0) { + return NGHTTP2_ERR_CALLBACK_FAILURE; + } + } + return 0; +} + +static int session_call_on_begin_headers(nghttp2_session *session, + nghttp2_frame *frame) { + int rv; + DEBUGF(fprintf(stderr, "recv: call on_begin_headers callback stream_id=%d\n", + frame->hd.stream_id)); + if (session->callbacks.on_begin_headers_callback) { + rv = session->callbacks.on_begin_headers_callback(session, frame, + session->user_data); + if (rv == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) { + return rv; + } + if (rv != 0) { + return NGHTTP2_ERR_CALLBACK_FAILURE; + } + } + return 0; +} + +static int session_call_on_header(nghttp2_session *session, + const nghttp2_frame *frame, + const nghttp2_nv *nv) { + int rv; + if (session->callbacks.on_header_callback) { + rv = session->callbacks.on_header_callback( + session, frame, nv->name, nv->namelen, nv->value, nv->valuelen, + nv->flags, session->user_data); + if (rv == NGHTTP2_ERR_PAUSE || + rv == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) { + return rv; + } + if (rv != 0) { + return NGHTTP2_ERR_CALLBACK_FAILURE; + } + } + return 0; +} + +/* + * Handles frame size error. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + */ +static int session_handle_frame_size_error(nghttp2_session *session, + nghttp2_frame *frame _U_) { + /* TODO Currently no callback is called for this error, because we + call this callback before reading any payload */ + return nghttp2_session_terminate_session(session, NGHTTP2_FRAME_SIZE_ERROR); +} + +static int get_error_code_from_lib_error_code(int lib_error_code) { + switch (lib_error_code) { + case NGHTTP2_ERR_STREAM_CLOSED: + return NGHTTP2_STREAM_CLOSED; + case NGHTTP2_ERR_HEADER_COMP: + return NGHTTP2_COMPRESSION_ERROR; + case NGHTTP2_ERR_FRAME_SIZE_ERROR: + return NGHTTP2_FRAME_SIZE_ERROR; + case NGHTTP2_ERR_FLOW_CONTROL: + return NGHTTP2_FLOW_CONTROL_ERROR; + case NGHTTP2_ERR_REFUSED_STREAM: + return NGHTTP2_REFUSED_STREAM; + case NGHTTP2_ERR_PROTO: + case NGHTTP2_ERR_HTTP_HEADER: + case NGHTTP2_ERR_HTTP_MESSAGING: + return NGHTTP2_PROTOCOL_ERROR; + default: + return NGHTTP2_INTERNAL_ERROR; + } +} + +static int session_handle_invalid_stream2(nghttp2_session *session, + int32_t stream_id, + nghttp2_frame *frame, + int lib_error_code) { + int rv; + rv = nghttp2_session_add_rst_stream( + session, stream_id, get_error_code_from_lib_error_code(lib_error_code)); + if (rv != 0) { + return rv; + } + if (session->callbacks.on_invalid_frame_recv_callback) { + if (session->callbacks.on_invalid_frame_recv_callback( + session, frame, lib_error_code, session->user_data) != 0) { + return NGHTTP2_ERR_CALLBACK_FAILURE; + } + } + return 0; +} + +static int session_handle_invalid_stream(nghttp2_session *session, + nghttp2_frame *frame, + int lib_error_code) { + return session_handle_invalid_stream2(session, frame->hd.stream_id, frame, + lib_error_code); +} + +static int session_inflate_handle_invalid_stream(nghttp2_session *session, + nghttp2_frame *frame, + int lib_error_code) { + int rv; + rv = session_handle_invalid_stream(session, frame, lib_error_code); + if (nghttp2_is_fatal(rv)) { + return rv; + } + return NGHTTP2_ERR_IGN_HEADER_BLOCK; +} + +/* + * Handles invalid frame which causes connection error. + */ +static int session_handle_invalid_connection(nghttp2_session *session, + nghttp2_frame *frame, + int lib_error_code, + const char *reason) { + if (session->callbacks.on_invalid_frame_recv_callback) { + if (session->callbacks.on_invalid_frame_recv_callback( + session, frame, lib_error_code, session->user_data) != 0) { + return NGHTTP2_ERR_CALLBACK_FAILURE; + } + } + return nghttp2_session_terminate_session_with_reason( + session, get_error_code_from_lib_error_code(lib_error_code), reason); +} + +static int session_inflate_handle_invalid_connection(nghttp2_session *session, + nghttp2_frame *frame, + int lib_error_code, + const char *reason) { + int rv; + rv = + session_handle_invalid_connection(session, frame, lib_error_code, reason); + if (nghttp2_is_fatal(rv)) { + return rv; + } + return NGHTTP2_ERR_IGN_HEADER_BLOCK; +} + +/* + * Inflates header block in the memory pointed by |in| with |inlen| + * bytes. If this function returns NGHTTP2_ERR_PAUSE, the caller must + * call this function again, until it returns 0 or one of negative + * error code. If |call_header_cb| is zero, the on_header_callback + * are not invoked and the function never return NGHTTP2_ERR_PAUSE. If + * the given |in| is the last chunk of header block, the |final| must + * be nonzero. If header block is successfully processed (which is + * indicated by the return value 0, NGHTTP2_ERR_PAUSE or + * NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE), the number of processed + * input bytes is assigned to the |*readlen_ptr|. + * + * This function return 0 if it succeeds, or one of the negative error + * codes: + * + * NGHTTP2_ERR_CALLBACK_FAILURE + * The callback function failed. + * NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE + * The callback returns this error code, indicating that this + * stream should be RST_STREAMed. + * NGHTTP2_ERR_NOMEM + * Out of memory. + * NGHTTP2_ERR_PAUSE + * The callback function returned NGHTTP2_ERR_PAUSE + * NGHTTP2_ERR_HEADER_COMP + * Header decompression failed + */ +static int inflate_header_block(nghttp2_session *session, nghttp2_frame *frame, + size_t *readlen_ptr, uint8_t *in, size_t inlen, + int final, int call_header_cb) { + ssize_t proclen; + int rv; + int inflate_flags; + nghttp2_nv nv; + nghttp2_stream *stream; + nghttp2_stream *subject_stream; + int trailer = 0; + int token; + + *readlen_ptr = 0; + stream = nghttp2_session_get_stream(session, frame->hd.stream_id); + + if (frame->hd.type == NGHTTP2_PUSH_PROMISE) { + subject_stream = nghttp2_session_get_stream( + session, frame->push_promise.promised_stream_id); + } else { + subject_stream = stream; + trailer = session_trailer_headers(session, stream, frame); + } + + DEBUGF(fprintf(stderr, "recv: decoding header block %zu bytes\n", inlen)); + for (;;) { + inflate_flags = 0; + proclen = nghttp2_hd_inflate_hd2(&session->hd_inflater, &nv, &inflate_flags, + &token, in, inlen, final); + if (nghttp2_is_fatal((int)proclen)) { + return (int)proclen; + } + if (proclen < 0) { + if (session->iframe.state == NGHTTP2_IB_READ_HEADER_BLOCK) { + if (subject_stream && subject_stream->state != NGHTTP2_STREAM_CLOSING) { + /* Adding RST_STREAM here is very important. It prevents + from invoking subsequent callbacks for the same stream + ID. */ + rv = nghttp2_session_add_rst_stream( + session, subject_stream->stream_id, NGHTTP2_COMPRESSION_ERROR); + + if (nghttp2_is_fatal(rv)) { + return rv; + } + } + } + rv = + nghttp2_session_terminate_session(session, NGHTTP2_COMPRESSION_ERROR); + if (nghttp2_is_fatal(rv)) { + return rv; + } + + return NGHTTP2_ERR_HEADER_COMP; + } + in += proclen; + inlen -= proclen; + *readlen_ptr += proclen; + + DEBUGF(fprintf(stderr, "recv: proclen=%zd\n", proclen)); + + if (call_header_cb && (inflate_flags & NGHTTP2_HD_INFLATE_EMIT)) { + rv = 0; + if (subject_stream && session_enforce_http_messaging(session)) { + rv = nghttp2_http_on_header(session, subject_stream, frame, &nv, token, + trailer); + if (rv == NGHTTP2_ERR_HTTP_HEADER) { + DEBUGF(fprintf( + stderr, "recv: HTTP error: type=%d, id=%d, header %.*s: %.*s\n", + frame->hd.type, subject_stream->stream_id, (int)nv.namelen, + nv.name, (int)nv.valuelen, nv.value)); + + rv = + session_handle_invalid_stream2(session, subject_stream->stream_id, + frame, NGHTTP2_ERR_HTTP_HEADER); + if (nghttp2_is_fatal(rv)) { + return rv; + } + return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; + } + + if (rv == NGHTTP2_ERR_IGN_HTTP_HEADER) { + /* header is ignored */ + DEBUGF(fprintf( + stderr, "recv: HTTP ignored: type=%d, id=%d, header %.*s: %.*s\n", + frame->hd.type, subject_stream->stream_id, (int)nv.namelen, + nv.name, (int)nv.valuelen, nv.value)); + } + } + if (rv == 0) { + rv = session_call_on_header(session, frame, &nv); + /* This handles NGHTTP2_ERR_PAUSE and + NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE as well */ + if (rv != 0) { + return rv; + } + } + } + if (inflate_flags & NGHTTP2_HD_INFLATE_FINAL) { + nghttp2_hd_inflate_end_headers(&session->hd_inflater); + break; + } + if ((inflate_flags & NGHTTP2_HD_INFLATE_EMIT) == 0 && inlen == 0) { + break; + } + } + return 0; +} + +/* + * Decompress header blocks of incoming request HEADERS and also call + * additional callbacks. This function can be called again if this + * function returns NGHTTP2_ERR_PAUSE. + * + * This function returns 0 if it succeeds, or one of negative error + * codes: + * + * NGHTTP2_ERR_CALLBACK_FAILURE + * The callback function failed. + * NGHTTP2_ERR_NOMEM + * Out of memory. + */ +int nghttp2_session_end_request_headers_received(nghttp2_session *session _U_, + nghttp2_frame *frame, + nghttp2_stream *stream) { + if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) { + nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_RD); + } + /* Here we assume that stream is not shutdown in NGHTTP2_SHUT_WR */ + return 0; +} + +/* + * Decompress header blocks of incoming (push-)response HEADERS and + * also call additional callbacks. This function can be called again + * if this function returns NGHTTP2_ERR_PAUSE. + * + * This function returns 0 if it succeeds, or one of negative error + * codes: + * + * NGHTTP2_ERR_CALLBACK_FAILURE + * The callback function failed. + * NGHTTP2_ERR_NOMEM + * Out of memory. + */ +int nghttp2_session_end_response_headers_received(nghttp2_session *session, + nghttp2_frame *frame, + nghttp2_stream *stream) { + int rv; + if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) { + /* This is the last frame of this stream, so disallow + further receptions. */ + nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_RD); + rv = nghttp2_session_close_stream_if_shut_rdwr(session, stream); + if (nghttp2_is_fatal(rv)) { + return rv; + } + } + return 0; +} + +/* + * Decompress header blocks of incoming HEADERS and also call + * additional callbacks. This function can be called again if this + * function returns NGHTTP2_ERR_PAUSE. + * + * This function returns 0 if it succeeds, or one of negative error + * codes: + * + * NGHTTP2_ERR_CALLBACK_FAILURE + * The callback function failed. + * NGHTTP2_ERR_NOMEM + * Out of memory. + */ +int nghttp2_session_end_headers_received(nghttp2_session *session, + nghttp2_frame *frame, + nghttp2_stream *stream) { + int rv; + if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) { + if (!nghttp2_session_is_my_stream_id(session, frame->hd.stream_id)) { + } + nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_RD); + rv = nghttp2_session_close_stream_if_shut_rdwr(session, stream); + if (nghttp2_is_fatal(rv)) { + return rv; + } + } + return 0; +} + +static int session_after_header_block_received(nghttp2_session *session) { + int rv = 0; + int call_cb = 1; + nghttp2_frame *frame = &session->iframe.frame; + nghttp2_stream *stream; + + /* We don't call on_frame_recv_callback if stream has been closed + already or being closed. */ + stream = nghttp2_session_get_stream(session, frame->hd.stream_id); + if (!stream || stream->state == NGHTTP2_STREAM_CLOSING) { + return 0; + } + + if (session_enforce_http_messaging(session)) { + if (frame->hd.type == NGHTTP2_PUSH_PROMISE) { + nghttp2_stream *subject_stream; + + subject_stream = nghttp2_session_get_stream( + session, frame->push_promise.promised_stream_id); + if (subject_stream) { + rv = nghttp2_http_on_request_headers(subject_stream, frame); + } + } else { + assert(frame->hd.type == NGHTTP2_HEADERS); + switch (frame->headers.cat) { + case NGHTTP2_HCAT_REQUEST: + rv = nghttp2_http_on_request_headers(stream, frame); + break; + case NGHTTP2_HCAT_RESPONSE: + case NGHTTP2_HCAT_PUSH_RESPONSE: + rv = nghttp2_http_on_response_headers(stream); + break; + case NGHTTP2_HCAT_HEADERS: + if (stream->http_flags & NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE) { + assert(!session->server); + rv = nghttp2_http_on_response_headers(stream); + } else { + rv = nghttp2_http_on_trailer_headers(stream, frame); + } + break; + default: + assert(0); + } + if (rv == 0 && (frame->hd.flags & NGHTTP2_FLAG_END_STREAM)) { + rv = nghttp2_http_on_remote_end_stream(stream); + } + } + if (rv != 0) { + int32_t stream_id; + + if (frame->hd.type == NGHTTP2_PUSH_PROMISE) { + stream_id = frame->push_promise.promised_stream_id; + } else { + stream_id = frame->hd.stream_id; + } + + call_cb = 0; + + rv = session_handle_invalid_stream2(session, stream_id, frame, + NGHTTP2_ERR_HTTP_MESSAGING); + if (nghttp2_is_fatal(rv)) { + return rv; + } + } + } + + if (call_cb) { + rv = session_call_on_frame_received(session, frame); + if (nghttp2_is_fatal(rv)) { + return rv; + } + } + + if (frame->hd.type != NGHTTP2_HEADERS) { + return 0; + } + + switch (frame->headers.cat) { + case NGHTTP2_HCAT_REQUEST: + return nghttp2_session_end_request_headers_received(session, frame, stream); + case NGHTTP2_HCAT_RESPONSE: + case NGHTTP2_HCAT_PUSH_RESPONSE: + return nghttp2_session_end_response_headers_received(session, frame, + stream); + case NGHTTP2_HCAT_HEADERS: + return nghttp2_session_end_headers_received(session, frame, stream); + default: + assert(0); + } + return 0; +} + +int nghttp2_session_on_request_headers_received(nghttp2_session *session, + nghttp2_frame *frame) { + int rv = 0; + nghttp2_stream *stream; + if (frame->hd.stream_id == 0) { + return session_inflate_handle_invalid_connection( + session, frame, NGHTTP2_ERR_PROTO, "request HEADERS: stream_id == 0"); + } + + /* If client recieves idle stream from server, it is invalid + regardless stream ID is even or odd. This is because client is + not expected to receive request from server. */ + if (!session->server) { + if (session_detect_idle_stream(session, frame->hd.stream_id)) { + return session_inflate_handle_invalid_connection( + session, frame, NGHTTP2_ERR_PROTO, + "request HEADERS: client received request"); + } + + return NGHTTP2_ERR_IGN_HEADER_BLOCK; + } + + if (!session_is_new_peer_stream_id(session, frame->hd.stream_id)) { + /* The spec says if an endpoint receives a HEADERS with invalid + stream ID, it MUST issue connection error with error code + PROTOCOL_ERROR. But we could get trailer HEADERS after we have + sent RST_STREAM to this stream and peer have not received it. + Then connection error is too harsh. It means that we only use + connection error if stream ID refers idle stream. OTherwise we + just ignore HEADERS for now. */ + if (session_detect_idle_stream(session, frame->hd.stream_id)) { + return session_inflate_handle_invalid_connection( + session, frame, NGHTTP2_ERR_PROTO, + "request HEADERS: invalid stream_id"); + } + + return NGHTTP2_ERR_IGN_HEADER_BLOCK; + } + session->last_recv_stream_id = frame->hd.stream_id; + + if (session->goaway_flags & NGHTTP2_GOAWAY_SENT) { + /* We just ignore stream after GOAWAY was queued */ + return NGHTTP2_ERR_IGN_HEADER_BLOCK; + } + + if (session_is_incoming_concurrent_streams_max(session)) { + return session_inflate_handle_invalid_connection( + session, frame, NGHTTP2_ERR_PROTO, + "request HEADERS: max concurrent streams exceeded"); + } + + if (frame->headers.pri_spec.stream_id == frame->hd.stream_id) { + return session_inflate_handle_invalid_connection( + session, frame, NGHTTP2_ERR_PROTO, "request HEADERS: depend on itself"); + } + + if (session_is_incoming_concurrent_streams_pending_max(session)) { + return session_inflate_handle_invalid_stream(session, frame, + NGHTTP2_ERR_REFUSED_STREAM); + } + + stream = nghttp2_session_open_stream( + session, frame->hd.stream_id, NGHTTP2_STREAM_FLAG_NONE, + &frame->headers.pri_spec, NGHTTP2_STREAM_OPENING, NULL); + if (!stream) { + return NGHTTP2_ERR_NOMEM; + } + session->last_proc_stream_id = session->last_recv_stream_id; + rv = session_call_on_begin_headers(session, frame); + if (rv != 0) { + return rv; + } + return 0; +} + +int nghttp2_session_on_response_headers_received(nghttp2_session *session, + nghttp2_frame *frame, + nghttp2_stream *stream) { + int rv; + /* This function is only called if stream->state == + NGHTTP2_STREAM_OPENING and stream_id is local side initiated. */ + assert(stream->state == NGHTTP2_STREAM_OPENING && + nghttp2_session_is_my_stream_id(session, frame->hd.stream_id)); + if (frame->hd.stream_id == 0) { + return session_inflate_handle_invalid_connection( + session, frame, NGHTTP2_ERR_PROTO, "response HEADERS: stream_id == 0"); + } + if (stream->shut_flags & NGHTTP2_SHUT_RD) { + /* half closed (remote): from the spec: + + If an endpoint receives additional frames for a stream that is + in this state it MUST respond with a stream error (Section + 5.4.2) of type STREAM_CLOSED. + */ + return session_inflate_handle_invalid_stream(session, frame, + NGHTTP2_ERR_STREAM_CLOSED); + } + stream->state = NGHTTP2_STREAM_OPENED; + rv = session_call_on_begin_headers(session, frame); + if (rv != 0) { + return rv; + } + return 0; +} + +int nghttp2_session_on_push_response_headers_received(nghttp2_session *session, + nghttp2_frame *frame, + nghttp2_stream *stream) { + int rv = 0; + assert(stream->state == NGHTTP2_STREAM_RESERVED); + if (frame->hd.stream_id == 0) { + return session_inflate_handle_invalid_connection( + session, frame, NGHTTP2_ERR_PROTO, + "push response HEADERS: stream_id == 0"); + } + if (session->goaway_flags) { + /* We don't accept new stream after GOAWAY is sent or received. */ + return NGHTTP2_ERR_IGN_HEADER_BLOCK; + } + + if (session_is_incoming_concurrent_streams_max(session)) { + return session_inflate_handle_invalid_connection( + session, frame, NGHTTP2_ERR_PROTO, + "push response HEADERS: max concurrent streams exceeded"); + } + if (session_is_incoming_concurrent_streams_pending_max(session)) { + return session_inflate_handle_invalid_stream(session, frame, + NGHTTP2_ERR_REFUSED_STREAM); + } + + nghttp2_stream_promise_fulfilled(stream); + ++session->num_incoming_streams; + rv = session_call_on_begin_headers(session, frame); + if (rv != 0) { + return rv; + } + return 0; +} + +int nghttp2_session_on_headers_received(nghttp2_session *session, + nghttp2_frame *frame, + nghttp2_stream *stream) { + int rv = 0; + if (frame->hd.stream_id == 0) { + return session_inflate_handle_invalid_connection( + session, frame, NGHTTP2_ERR_PROTO, "HEADERS: stream_id == 0"); + } + if (stream->state == NGHTTP2_STREAM_RESERVED) { + /* reserved. The valid push response HEADERS is processed by + nghttp2_session_on_push_response_headers_received(). This + generic HEADERS is called invalid cases for HEADERS against + reserved state. */ + return session_inflate_handle_invalid_connection( + session, frame, NGHTTP2_ERR_PROTO, "HEADERS: stream in reserved"); + } + if ((stream->shut_flags & NGHTTP2_SHUT_RD)) { + /* half closed (remote): from the spec: + + If an endpoint receives additional frames for a stream that is + in this state it MUST respond with a stream error (Section + 5.4.2) of type STREAM_CLOSED. + */ + return session_inflate_handle_invalid_stream(session, frame, + NGHTTP2_ERR_STREAM_CLOSED); + } + if (nghttp2_session_is_my_stream_id(session, frame->hd.stream_id)) { + if (stream->state == NGHTTP2_STREAM_OPENED) { + rv = session_call_on_begin_headers(session, frame); + if (rv != 0) { + return rv; + } + return 0; + } else if (stream->state == NGHTTP2_STREAM_CLOSING) { + /* This is race condition. NGHTTP2_STREAM_CLOSING indicates + that we queued RST_STREAM but it has not been sent. It will + eventually sent, so we just ignore this frame. */ + return NGHTTP2_ERR_IGN_HEADER_BLOCK; + } else { + return session_inflate_handle_invalid_stream(session, frame, + NGHTTP2_ERR_PROTO); + } + } + /* If this is remote peer initiated stream, it is OK unless it + has sent END_STREAM frame already. But if stream is in + NGHTTP2_STREAM_CLOSING, we discard the frame. This is a race + condition. */ + if (stream->state != NGHTTP2_STREAM_CLOSING) { + rv = session_call_on_begin_headers(session, frame); + if (rv != 0) { + return rv; + } + return 0; + } + return NGHTTP2_ERR_IGN_HEADER_BLOCK; +} + +static int session_process_headers_frame(nghttp2_session *session) { + int rv; + nghttp2_inbound_frame *iframe = &session->iframe; + nghttp2_frame *frame = &iframe->frame; + nghttp2_stream *stream; + + rv = nghttp2_frame_unpack_headers_payload(&frame->headers, iframe->sbuf.pos, + nghttp2_buf_len(&iframe->sbuf)); + + if (rv != 0) { + return nghttp2_session_terminate_session_with_reason( + session, NGHTTP2_PROTOCOL_ERROR, "HEADERS: could not unpack"); + } + stream = nghttp2_session_get_stream(session, frame->hd.stream_id); + if (!stream) { + frame->headers.cat = NGHTTP2_HCAT_REQUEST; + return nghttp2_session_on_request_headers_received(session, frame); + } + + if (nghttp2_session_is_my_stream_id(session, frame->hd.stream_id)) { + if (stream->state == NGHTTP2_STREAM_OPENING) { + frame->headers.cat = NGHTTP2_HCAT_RESPONSE; + return nghttp2_session_on_response_headers_received(session, frame, + stream); + } + frame->headers.cat = NGHTTP2_HCAT_HEADERS; + return nghttp2_session_on_headers_received(session, frame, stream); + } + if (stream->state == NGHTTP2_STREAM_RESERVED) { + frame->headers.cat = NGHTTP2_HCAT_PUSH_RESPONSE; + return nghttp2_session_on_push_response_headers_received(session, frame, + stream); + } + frame->headers.cat = NGHTTP2_HCAT_HEADERS; + return nghttp2_session_on_headers_received(session, frame, stream); +} + +int nghttp2_session_on_priority_received(nghttp2_session *session, + nghttp2_frame *frame) { + int rv; + nghttp2_stream *stream; + + if (frame->hd.stream_id == 0) { + return session_handle_invalid_connection(session, frame, NGHTTP2_ERR_PROTO, + "PRIORITY: stream_id == 0"); + } + + if (!session->server) { + /* Re-prioritization works only in server */ + return session_call_on_frame_received(session, frame); + } + + stream = nghttp2_session_get_stream_raw(session, frame->hd.stream_id); + + if (!stream) { + /* PRIORITY against idle stream can create anchor node in + dependency tree. */ + if (!session_detect_idle_stream(session, frame->hd.stream_id)) { + return 0; + } + + stream = nghttp2_session_open_stream( + session, frame->hd.stream_id, NGHTTP2_STREAM_FLAG_NONE, + &frame->priority.pri_spec, NGHTTP2_STREAM_IDLE, NULL); + + if (stream == NULL) { + return NGHTTP2_ERR_NOMEM; + } + } else { + rv = nghttp2_session_reprioritize_stream(session, stream, + &frame->priority.pri_spec); + + if (nghttp2_is_fatal(rv)) { + return rv; + } + } + + return session_call_on_frame_received(session, frame); +} + +static int session_process_priority_frame(nghttp2_session *session) { + nghttp2_inbound_frame *iframe = &session->iframe; + nghttp2_frame *frame = &iframe->frame; + + nghttp2_frame_unpack_priority_payload(&frame->priority, iframe->sbuf.pos, + nghttp2_buf_len(&iframe->sbuf)); + + return nghttp2_session_on_priority_received(session, frame); +} + +int nghttp2_session_on_rst_stream_received(nghttp2_session *session, + nghttp2_frame *frame) { + int rv; + nghttp2_stream *stream; + if (frame->hd.stream_id == 0) { + return session_handle_invalid_connection(session, frame, NGHTTP2_ERR_PROTO, + "RST_STREAM: stream_id == 0"); + } + stream = nghttp2_session_get_stream(session, frame->hd.stream_id); + if (!stream) { + if (session_detect_idle_stream(session, frame->hd.stream_id)) { + return session_handle_invalid_connection( + session, frame, NGHTTP2_ERR_PROTO, "RST_STREAM: stream in idle"); + } + } + + rv = session_call_on_frame_received(session, frame); + if (rv != 0) { + return rv; + } + rv = nghttp2_session_close_stream(session, frame->hd.stream_id, + frame->rst_stream.error_code); + if (nghttp2_is_fatal(rv)) { + return rv; + } + return 0; +} + +static int session_process_rst_stream_frame(nghttp2_session *session) { + nghttp2_inbound_frame *iframe = &session->iframe; + nghttp2_frame *frame = &iframe->frame; + + nghttp2_frame_unpack_rst_stream_payload(&frame->rst_stream, iframe->sbuf.pos, + nghttp2_buf_len(&iframe->sbuf)); + + return nghttp2_session_on_rst_stream_received(session, frame); +} + +static int update_remote_initial_window_size_func(nghttp2_map_entry *entry, + void *ptr) { + int rv; + nghttp2_update_window_size_arg *arg; + nghttp2_stream *stream; + + arg = (nghttp2_update_window_size_arg *)ptr; + stream = (nghttp2_stream *)entry; + + rv = nghttp2_stream_update_remote_initial_window_size( + stream, arg->new_window_size, arg->old_window_size); + if (rv != 0) { + return nghttp2_session_terminate_session(arg->session, + NGHTTP2_FLOW_CONTROL_ERROR); + } + + /* If window size gets positive, push deferred DATA frame to + outbound queue. */ + if (stream->remote_window_size > 0 && + nghttp2_stream_check_deferred_by_flow_control(stream)) { + + rv = nghttp2_stream_resume_deferred_item( + stream, NGHTTP2_STREAM_FLAG_DEFERRED_FLOW_CONTROL, arg->session); + + if (nghttp2_is_fatal(rv)) { + return rv; + } + } + return 0; +} + +/* + * Updates the remote initial window size of all active streams. If + * error occurs, all streams may not be updated. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + */ +static int +session_update_remote_initial_window_size(nghttp2_session *session, + int32_t new_initial_window_size) { + nghttp2_update_window_size_arg arg; + + arg.session = session; + arg.new_window_size = new_initial_window_size; + arg.old_window_size = session->remote_settings.initial_window_size; + + return nghttp2_map_each(&session->streams, + update_remote_initial_window_size_func, &arg); +} + +static int update_local_initial_window_size_func(nghttp2_map_entry *entry, + void *ptr) { + int rv; + nghttp2_update_window_size_arg *arg; + nghttp2_stream *stream; + arg = (nghttp2_update_window_size_arg *)ptr; + stream = (nghttp2_stream *)entry; + rv = nghttp2_stream_update_local_initial_window_size( + stream, arg->new_window_size, arg->old_window_size); + if (rv != 0) { + return nghttp2_session_terminate_session(arg->session, + NGHTTP2_FLOW_CONTROL_ERROR); + } + if (!(arg->session->opt_flags & NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE)) { + + if (nghttp2_should_send_window_update(stream->local_window_size, + stream->recv_window_size)) { + + rv = nghttp2_session_add_window_update(arg->session, NGHTTP2_FLAG_NONE, + stream->stream_id, + stream->recv_window_size); + if (rv != 0) { + return rv; + } + stream->recv_window_size = 0; + } + } + return 0; +} + +/* + * Updates the local initial window size of all active streams. If + * error occurs, all streams may not be updated. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + */ +static int +session_update_local_initial_window_size(nghttp2_session *session, + int32_t new_initial_window_size, + int32_t old_initial_window_size) { + nghttp2_update_window_size_arg arg; + arg.session = session; + arg.new_window_size = new_initial_window_size; + arg.old_window_size = old_initial_window_size; + return nghttp2_map_each(&session->streams, + update_local_initial_window_size_func, &arg); +} + +/* + * Apply SETTINGS values |iv| having |niv| elements to the local + * settings. We assumes that all values in |iv| is correct, since we + * validated them in nghttp2_session_add_settings() already. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_HEADER_COMP + * The header table size is out of range + * NGHTTP2_ERR_NOMEM + * Out of memory + */ +int nghttp2_session_update_local_settings(nghttp2_session *session, + nghttp2_settings_entry *iv, + size_t niv) { + int rv; + size_t i; + int32_t new_initial_window_size = -1; + int32_t header_table_size = -1; + uint8_t header_table_size_seen = 0; + /* Use the value last seen. */ + for (i = 0; i < niv; ++i) { + switch (iv[i].settings_id) { + case NGHTTP2_SETTINGS_HEADER_TABLE_SIZE: + header_table_size_seen = 1; + header_table_size = iv[i].value; + break; + case NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE: + new_initial_window_size = iv[i].value; + break; + } + } + if (header_table_size_seen) { + rv = nghttp2_hd_inflate_change_table_size(&session->hd_inflater, + header_table_size); + if (rv != 0) { + return rv; + } + } + if (new_initial_window_size != -1) { + rv = session_update_local_initial_window_size( + session, new_initial_window_size, + session->local_settings.initial_window_size); + if (rv != 0) { + return rv; + } + } + + for (i = 0; i < niv; ++i) { + switch (iv[i].settings_id) { + case NGHTTP2_SETTINGS_HEADER_TABLE_SIZE: + session->local_settings.header_table_size = iv[i].value; + break; + case NGHTTP2_SETTINGS_ENABLE_PUSH: + session->local_settings.enable_push = iv[i].value; + break; + case NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS: + session->local_settings.max_concurrent_streams = iv[i].value; + break; + case NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE: + session->local_settings.initial_window_size = iv[i].value; + break; + case NGHTTP2_SETTINGS_MAX_FRAME_SIZE: + session->local_settings.max_frame_size = iv[i].value; + break; + case NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE: + session->local_settings.max_header_list_size = iv[i].value; + break; + } + } + + session->pending_local_max_concurrent_stream = + NGHTTP2_INITIAL_MAX_CONCURRENT_STREAMS; + session->pending_enable_push = 1; + + return 0; +} + +int nghttp2_session_on_settings_received(nghttp2_session *session, + nghttp2_frame *frame, int noack) { + int rv; + size_t i; + nghttp2_mem *mem; + + mem = &session->mem; + + if (frame->hd.stream_id != 0) { + return session_handle_invalid_connection(session, frame, NGHTTP2_ERR_PROTO, + "SETTINGS: stream_id != 0"); + } + if (frame->hd.flags & NGHTTP2_FLAG_ACK) { + if (frame->settings.niv != 0) { + return session_handle_invalid_connection( + session, frame, NGHTTP2_ERR_FRAME_SIZE_ERROR, + "SETTINGS: ACK and payload != 0"); + } + if (session->inflight_niv == -1) { + return session_handle_invalid_connection( + session, frame, NGHTTP2_ERR_PROTO, "SETTINGS: unexpected ACK"); + } + rv = nghttp2_session_update_local_settings(session, session->inflight_iv, + session->inflight_niv); + nghttp2_mem_free(mem, session->inflight_iv); + session->inflight_iv = NULL; + session->inflight_niv = -1; + if (rv != 0) { + if (nghttp2_is_fatal(rv)) { + return rv; + } + return session_handle_invalid_connection(session, frame, rv, NULL); + } + return session_call_on_frame_received(session, frame); + } + + for (i = 0; i < frame->settings.niv; ++i) { + nghttp2_settings_entry *entry = &frame->settings.iv[i]; + + switch (entry->settings_id) { + case NGHTTP2_SETTINGS_HEADER_TABLE_SIZE: + + if (entry->value > NGHTTP2_MAX_HEADER_TABLE_SIZE) { + return session_handle_invalid_connection( + session, frame, NGHTTP2_ERR_HEADER_COMP, + "SETTINGS: too large SETTINGS_HEADER_TABLE_SIZE"); + } + + rv = nghttp2_hd_deflate_change_table_size(&session->hd_deflater, + entry->value); + if (rv != 0) { + if (nghttp2_is_fatal(rv)) { + return rv; + } else { + return session_handle_invalid_connection( + session, frame, NGHTTP2_ERR_HEADER_COMP, NULL); + } + } + + session->remote_settings.header_table_size = entry->value; + + break; + case NGHTTP2_SETTINGS_ENABLE_PUSH: + + if (entry->value != 0 && entry->value != 1) { + return session_handle_invalid_connection( + session, frame, NGHTTP2_ERR_PROTO, + "SETTINGS: invalid SETTINGS_ENBLE_PUSH"); + } + + if (!session->server && entry->value != 0) { + return session_handle_invalid_connection( + session, frame, NGHTTP2_ERR_PROTO, + "SETTINGS: server attempted to enable push"); + } + + session->remote_settings.enable_push = entry->value; + + break; + case NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS: + + session->remote_settings.max_concurrent_streams = entry->value; + + break; + case NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE: + + /* Update the initial window size of the all active streams */ + /* Check that initial_window_size < (1u << 31) */ + if (entry->value > NGHTTP2_MAX_WINDOW_SIZE) { + return session_handle_invalid_connection( + session, frame, NGHTTP2_ERR_FLOW_CONTROL, + "SETTINGS: too large SETTINGS_INITIAL_WINDOW_SIZE"); + } + + rv = session_update_remote_initial_window_size(session, entry->value); + + if (nghttp2_is_fatal(rv)) { + return rv; + } + + if (rv != 0) { + return session_handle_invalid_connection( + session, frame, NGHTTP2_ERR_FLOW_CONTROL, NULL); + } + + session->remote_settings.initial_window_size = entry->value; + + break; + case NGHTTP2_SETTINGS_MAX_FRAME_SIZE: + + if (entry->value < NGHTTP2_MAX_FRAME_SIZE_MIN || + entry->value > NGHTTP2_MAX_FRAME_SIZE_MAX) { + return session_handle_invalid_connection( + session, frame, NGHTTP2_ERR_PROTO, + "SETTINGS: invalid SETTINGS_MAX_FRAME_SIZE"); + } + + session->remote_settings.max_frame_size = entry->value; + + break; + case NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE: + + session->remote_settings.max_header_list_size = entry->value; + + break; + } + } + + if (!noack && !session_is_closing(session)) { + rv = nghttp2_session_add_settings(session, NGHTTP2_FLAG_ACK, NULL, 0); + + if (rv != 0) { + if (nghttp2_is_fatal(rv)) { + return rv; + } + + return session_handle_invalid_connection(session, frame, + NGHTTP2_ERR_INTERNAL, NULL); + } + } + + return session_call_on_frame_received(session, frame); +} + +static int session_process_settings_frame(nghttp2_session *session) { + int rv; + nghttp2_inbound_frame *iframe = &session->iframe; + nghttp2_frame *frame = &iframe->frame; + size_t i; + nghttp2_settings_entry min_header_size_entry; + nghttp2_mem *mem; + + mem = &session->mem; + min_header_size_entry = iframe->iv[NGHTTP2_INBOUND_NUM_IV - 1]; + + if (min_header_size_entry.value < UINT32_MAX) { + /* If we have less value, then we must have + SETTINGS_HEADER_TABLE_SIZE in i < iframe->niv */ + for (i = 0; i < iframe->niv; ++i) { + if (iframe->iv[i].settings_id == NGHTTP2_SETTINGS_HEADER_TABLE_SIZE) { + break; + } + } + + assert(i < iframe->niv); + + if (min_header_size_entry.value != iframe->iv[i].value) { + iframe->iv[iframe->niv++] = iframe->iv[i]; + iframe->iv[i] = min_header_size_entry; + } + } + + rv = nghttp2_frame_unpack_settings_payload(&frame->settings, iframe->iv, + iframe->niv, mem); + if (rv != 0) { + assert(nghttp2_is_fatal(rv)); + return rv; + } + return nghttp2_session_on_settings_received(session, frame, 0 /* ACK */); +} + +int nghttp2_session_on_push_promise_received(nghttp2_session *session, + nghttp2_frame *frame) { + int rv; + nghttp2_stream *stream; + nghttp2_stream *promised_stream; + nghttp2_priority_spec pri_spec; + + if (frame->hd.stream_id == 0) { + return session_inflate_handle_invalid_connection( + session, frame, NGHTTP2_ERR_PROTO, "PUSH_PROMISE: stream_id == 0"); + } + if (session->server || session->local_settings.enable_push == 0) { + return session_inflate_handle_invalid_connection( + session, frame, NGHTTP2_ERR_PROTO, "PUSH_PROMISE: push disabled"); + } + if (session->goaway_flags) { + /* We just dicard PUSH_PROMISE after GOAWAY is sent or + received. */ + return NGHTTP2_ERR_IGN_HEADER_BLOCK; + } + + if (!nghttp2_session_is_my_stream_id(session, frame->hd.stream_id)) { + return session_inflate_handle_invalid_connection( + session, frame, NGHTTP2_ERR_PROTO, "PUSH_PROMISE: invalid stream_id"); + } + + if (!session_is_new_peer_stream_id(session, + frame->push_promise.promised_stream_id)) { + /* The spec says if an endpoint receives a PUSH_PROMISE with + illegal stream ID is subject to a connection error of type + PROTOCOL_ERROR. */ + return session_inflate_handle_invalid_connection( + session, frame, NGHTTP2_ERR_PROTO, + "PUSH_PROMISE: invalid promised_stream_id"); + } + session->last_recv_stream_id = frame->push_promise.promised_stream_id; + stream = nghttp2_session_get_stream(session, frame->hd.stream_id); + if (!stream || stream->state == NGHTTP2_STREAM_CLOSING || + !session->pending_enable_push) { + if (!stream) { + if (session_detect_idle_stream(session, frame->hd.stream_id)) { + return session_inflate_handle_invalid_connection( + session, frame, NGHTTP2_ERR_PROTO, "PUSH_PROMISE: stream in idle"); + } + } + rv = nghttp2_session_add_rst_stream(session, + frame->push_promise.promised_stream_id, + NGHTTP2_REFUSED_STREAM); + if (rv != 0) { + return rv; + } + return NGHTTP2_ERR_IGN_HEADER_BLOCK; + } + if (stream->shut_flags & NGHTTP2_SHUT_RD) { + if (session->callbacks.on_invalid_frame_recv_callback) { + if (session->callbacks.on_invalid_frame_recv_callback( + session, frame, NGHTTP2_PROTOCOL_ERROR, session->user_data) != + 0) { + return NGHTTP2_ERR_CALLBACK_FAILURE; + } + } + rv = nghttp2_session_add_rst_stream(session, + frame->push_promise.promised_stream_id, + NGHTTP2_PROTOCOL_ERROR); + if (rv != 0) { + return rv; + } + return NGHTTP2_ERR_IGN_HEADER_BLOCK; + } + + /* TODO It is unclear reserved stream dpeneds on associated + stream with or without exclusive flag set */ + nghttp2_priority_spec_init(&pri_spec, stream->stream_id, + NGHTTP2_DEFAULT_WEIGHT, 0); + + promised_stream = nghttp2_session_open_stream( + session, frame->push_promise.promised_stream_id, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec, NGHTTP2_STREAM_RESERVED, NULL); + + if (!promised_stream) { + return NGHTTP2_ERR_NOMEM; + } + + session->last_proc_stream_id = session->last_recv_stream_id; + rv = session_call_on_begin_headers(session, frame); + if (rv != 0) { + return rv; + } + return 0; +} + +static int session_process_push_promise_frame(nghttp2_session *session) { + int rv; + nghttp2_inbound_frame *iframe = &session->iframe; + nghttp2_frame *frame = &iframe->frame; + + rv = nghttp2_frame_unpack_push_promise_payload( + &frame->push_promise, iframe->sbuf.pos, nghttp2_buf_len(&iframe->sbuf)); + + if (rv != 0) { + return nghttp2_session_terminate_session_with_reason( + session, NGHTTP2_PROTOCOL_ERROR, "PUSH_PROMISE: could not unpack"); + } + + return nghttp2_session_on_push_promise_received(session, frame); +} + +int nghttp2_session_on_ping_received(nghttp2_session *session, + nghttp2_frame *frame) { + int rv = 0; + if (frame->hd.stream_id != 0) { + return session_handle_invalid_connection(session, frame, NGHTTP2_ERR_PROTO, + "PING: stream_id != 0"); + } + if ((frame->hd.flags & NGHTTP2_FLAG_ACK) == 0 && + !session_is_closing(session)) { + /* Peer sent ping, so ping it back */ + rv = nghttp2_session_add_ping(session, NGHTTP2_FLAG_ACK, + frame->ping.opaque_data); + if (rv != 0) { + return rv; + } + } + return session_call_on_frame_received(session, frame); +} + +static int session_process_ping_frame(nghttp2_session *session) { + nghttp2_inbound_frame *iframe = &session->iframe; + nghttp2_frame *frame = &iframe->frame; + + nghttp2_frame_unpack_ping_payload(&frame->ping, iframe->sbuf.pos, + nghttp2_buf_len(&iframe->sbuf)); + + return nghttp2_session_on_ping_received(session, frame); +} + +int nghttp2_session_on_goaway_received(nghttp2_session *session, + nghttp2_frame *frame) { + int rv; + + if (frame->hd.stream_id != 0) { + return session_handle_invalid_connection(session, frame, NGHTTP2_ERR_PROTO, + "GOAWAY: stream_id != 0"); + } + /* Spec says Endpoints MUST NOT increase the value they send in the + last stream identifier. */ + if ((frame->goaway.last_stream_id > 0 && + !nghttp2_session_is_my_stream_id(session, + frame->goaway.last_stream_id)) || + session->remote_last_stream_id < frame->goaway.last_stream_id) { + return session_handle_invalid_connection(session, frame, NGHTTP2_ERR_PROTO, + "GOAWAY: invalid last_stream_id"); + } + + session->goaway_flags |= NGHTTP2_GOAWAY_RECV; + + session->remote_last_stream_id = frame->goaway.last_stream_id; + + rv = session_call_on_frame_received(session, frame); + + if (nghttp2_is_fatal(rv)) { + return rv; + } + + return session_close_stream_on_goaway(session, frame->goaway.last_stream_id, + 0); +} + +static int session_process_goaway_frame(nghttp2_session *session) { + nghttp2_inbound_frame *iframe = &session->iframe; + nghttp2_frame *frame = &iframe->frame; + + nghttp2_frame_unpack_goaway_payload( + &frame->goaway, iframe->sbuf.pos, nghttp2_buf_len(&iframe->sbuf), + iframe->lbuf.pos, nghttp2_buf_len(&iframe->lbuf)); + + nghttp2_buf_wrap_init(&iframe->lbuf, NULL, 0); + + return nghttp2_session_on_goaway_received(session, frame); +} + +static int +session_on_connection_window_update_received(nghttp2_session *session, + nghttp2_frame *frame) { + /* Handle connection-level flow control */ + if (frame->window_update.window_size_increment == 0) { + return session_handle_invalid_connection(session, frame, NGHTTP2_ERR_PROTO, + NULL); + } + + if (NGHTTP2_MAX_WINDOW_SIZE - frame->window_update.window_size_increment < + session->remote_window_size) { + return session_handle_invalid_connection(session, frame, + NGHTTP2_ERR_FLOW_CONTROL, NULL); + } + session->remote_window_size += frame->window_update.window_size_increment; + + return session_call_on_frame_received(session, frame); +} + +static int session_on_stream_window_update_received(nghttp2_session *session, + nghttp2_frame *frame) { + int rv; + nghttp2_stream *stream; + stream = nghttp2_session_get_stream(session, frame->hd.stream_id); + if (!stream) { + if (session_detect_idle_stream(session, frame->hd.stream_id)) { + return session_handle_invalid_connection( + session, frame, NGHTTP2_ERR_PROTO, "WINDOW_UPDATE to idle stream"); + } + return 0; + } + if (state_reserved_remote(session, stream)) { + return session_handle_invalid_connection( + session, frame, NGHTTP2_ERR_PROTO, "WINDOW_UPADATE to reserved stream"); + } + if (frame->window_update.window_size_increment == 0) { + return session_handle_invalid_stream(session, frame, NGHTTP2_ERR_PROTO); + } + if (NGHTTP2_MAX_WINDOW_SIZE - frame->window_update.window_size_increment < + stream->remote_window_size) { + return session_handle_invalid_stream(session, frame, + NGHTTP2_ERR_FLOW_CONTROL); + } + stream->remote_window_size += frame->window_update.window_size_increment; + + if (stream->remote_window_size > 0 && + nghttp2_stream_check_deferred_by_flow_control(stream)) { + + rv = nghttp2_stream_resume_deferred_item( + stream, NGHTTP2_STREAM_FLAG_DEFERRED_FLOW_CONTROL, session); + + if (nghttp2_is_fatal(rv)) { + return rv; + } + } + return session_call_on_frame_received(session, frame); +} + +int nghttp2_session_on_window_update_received(nghttp2_session *session, + nghttp2_frame *frame) { + if (frame->hd.stream_id == 0) { + return session_on_connection_window_update_received(session, frame); + } else { + return session_on_stream_window_update_received(session, frame); + } +} + +static int session_process_window_update_frame(nghttp2_session *session) { + nghttp2_inbound_frame *iframe = &session->iframe; + nghttp2_frame *frame = &iframe->frame; + + nghttp2_frame_unpack_window_update_payload( + &frame->window_update, iframe->sbuf.pos, nghttp2_buf_len(&iframe->sbuf)); + + return nghttp2_session_on_window_update_received(session, frame); +} + +int nghttp2_session_on_data_received(nghttp2_session *session, + nghttp2_frame *frame) { + int rv = 0; + int call_cb = 1; + nghttp2_stream *stream; + + /* We don't call on_frame_recv_callback if stream has been closed + already or being closed. */ + stream = nghttp2_session_get_stream(session, frame->hd.stream_id); + if (!stream || stream->state == NGHTTP2_STREAM_CLOSING) { + /* This should be treated as stream error, but it results in lots + of RST_STREAM. So just ignore frame against nonexistent stream + for now. */ + return 0; + } + + if (session_enforce_http_messaging(session) && + (frame->hd.flags & NGHTTP2_FLAG_END_STREAM)) { + if (nghttp2_http_on_remote_end_stream(stream) != 0) { + call_cb = 0; + rv = nghttp2_session_add_rst_stream(session, stream->stream_id, + NGHTTP2_PROTOCOL_ERROR); + if (nghttp2_is_fatal(rv)) { + return rv; + } + } + } + + if (call_cb) { + rv = session_call_on_frame_received(session, frame); + if (nghttp2_is_fatal(rv)) { + return rv; + } + } + + if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) { + nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_RD); + rv = nghttp2_session_close_stream_if_shut_rdwr(session, stream); + if (nghttp2_is_fatal(rv)) { + return rv; + } + } + return 0; +} + +/* For errors, this function only returns FATAL error. */ +static int session_process_data_frame(nghttp2_session *session) { + int rv; + nghttp2_frame *public_data_frame = &session->iframe.frame; + rv = nghttp2_session_on_data_received(session, public_data_frame); + if (nghttp2_is_fatal(rv)) { + return rv; + } + return 0; +} + +/* + * Now we have SETTINGS synchronization, flow control error can be + * detected strictly. If DATA frame is received with length > 0 and + * current received window size + delta length is strictly larger than + * local window size, it is subject to FLOW_CONTROL_ERROR, so return + * -1. Note that local_window_size is calculated after SETTINGS ACK is + * received from peer, so peer must honor this limit. If the resulting + * recv_window_size is strictly larger than NGHTTP2_MAX_WINDOW_SIZE, + * return -1 too. + */ +static int adjust_recv_window_size(int32_t *recv_window_size_ptr, size_t delta, + int32_t local_window_size) { + if (*recv_window_size_ptr > local_window_size - (int32_t)delta || + *recv_window_size_ptr > NGHTTP2_MAX_WINDOW_SIZE - (int32_t)delta) { + return -1; + } + *recv_window_size_ptr += delta; + return 0; +} + +/* + * Accumulates received bytes |delta_size| for stream-level flow + * control and decides whether to send WINDOW_UPDATE to that stream. + * If NGHTTP2_OPT_NO_AUTO_WINDOW_UPDATE is set, WINDOW_UPDATE will not + * be sent. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + */ +static int session_update_recv_stream_window_size(nghttp2_session *session, + nghttp2_stream *stream, + size_t delta_size, + int send_window_update) { + int rv; + rv = adjust_recv_window_size(&stream->recv_window_size, delta_size, + stream->local_window_size); + if (rv != 0) { + return nghttp2_session_add_rst_stream(session, stream->stream_id, + NGHTTP2_FLOW_CONTROL_ERROR); + } + /* We don't have to send WINDOW_UPDATE if the data received is the + last chunk in the incoming stream. */ + if (send_window_update && + !(session->opt_flags & NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE)) { + /* We have to use local_settings here because it is the constraint + the remote endpoint should honor. */ + if (nghttp2_should_send_window_update(stream->local_window_size, + stream->recv_window_size)) { + rv = nghttp2_session_add_window_update(session, NGHTTP2_FLAG_NONE, + stream->stream_id, + stream->recv_window_size); + if (rv == 0) { + stream->recv_window_size = 0; + } else { + return rv; + } + } + } + return 0; +} + +/* + * Accumulates received bytes |delta_size| for connection-level flow + * control and decides whether to send WINDOW_UPDATE to the + * connection. If NGHTTP2_OPT_NO_AUTO_WINDOW_UPDATE is set, + * WINDOW_UPDATE will not be sent. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + */ +static int session_update_recv_connection_window_size(nghttp2_session *session, + size_t delta_size) { + int rv; + rv = adjust_recv_window_size(&session->recv_window_size, delta_size, + session->local_window_size); + if (rv != 0) { + return nghttp2_session_terminate_session(session, + NGHTTP2_FLOW_CONTROL_ERROR); + } + if (!(session->opt_flags & NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE)) { + + if (nghttp2_should_send_window_update(session->local_window_size, + session->recv_window_size)) { + /* Use stream ID 0 to update connection-level flow control + window */ + rv = nghttp2_session_add_window_update(session, NGHTTP2_FLAG_NONE, 0, + session->recv_window_size); + if (rv != 0) { + return rv; + } + + session->recv_window_size = 0; + } + } + return 0; +} + +static int session_update_consumed_size(nghttp2_session *session, + int32_t *consumed_size_ptr, + int32_t *recv_window_size_ptr, + int32_t stream_id, size_t delta_size, + int32_t local_window_size) { + int32_t recv_size; + int rv; + + if ((size_t)*consumed_size_ptr > NGHTTP2_MAX_WINDOW_SIZE - delta_size) { + return nghttp2_session_terminate_session(session, + NGHTTP2_FLOW_CONTROL_ERROR); + } + + *consumed_size_ptr += delta_size; + + /* recv_window_size may be smaller than consumed_size, because it + may be decreased by negative value with + nghttp2_submit_window_update(). */ + recv_size = nghttp2_min(*consumed_size_ptr, *recv_window_size_ptr); + + if (nghttp2_should_send_window_update(local_window_size, recv_size)) { + rv = nghttp2_session_add_window_update(session, NGHTTP2_FLAG_NONE, + stream_id, recv_size); + + if (rv != 0) { + return rv; + } + + *recv_window_size_ptr -= recv_size; + *consumed_size_ptr -= recv_size; + } + + return 0; +} + +static int session_update_stream_consumed_size(nghttp2_session *session, + nghttp2_stream *stream, + size_t delta_size) { + return session_update_consumed_size( + session, &stream->consumed_size, &stream->recv_window_size, + stream->stream_id, delta_size, stream->local_window_size); +} + +static int session_update_connection_consumed_size(nghttp2_session *session, + size_t delta_size) { + return session_update_consumed_size(session, &session->consumed_size, + &session->recv_window_size, 0, delta_size, + session->local_window_size); +} + +/* + * Checks that we can receive the DATA frame for stream, which is + * indicated by |session->iframe.frame.hd.stream_id|. If it is a + * connection error situation, GOAWAY frame will be issued by this + * function. + * + * If the DATA frame is allowed, returns 0. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_IGN_PAYLOAD + * The reception of DATA frame is connection error; or should be + * ignored. + * NGHTTP2_ERR_NOMEM + * Out of memory. + */ +static int session_on_data_received_fail_fast(nghttp2_session *session) { + int rv; + nghttp2_stream *stream; + nghttp2_inbound_frame *iframe; + int32_t stream_id; + const char *failure_reason; + uint32_t error_code = NGHTTP2_PROTOCOL_ERROR; + + iframe = &session->iframe; + stream_id = iframe->frame.hd.stream_id; + + if (stream_id == 0) { + /* The spec says that if a DATA frame is received whose stream ID + is 0, the recipient MUST respond with a connection error of + type PROTOCOL_ERROR. */ + failure_reason = "DATA: stream_id == 0"; + goto fail; + } + stream = nghttp2_session_get_stream(session, stream_id); + if (!stream) { + if (session_detect_idle_stream(session, stream_id)) { + failure_reason = "DATA: stream in idle"; + error_code = NGHTTP2_STREAM_CLOSED; + goto fail; + } + return NGHTTP2_ERR_IGN_PAYLOAD; + } + if (stream->shut_flags & NGHTTP2_SHUT_RD) { + failure_reason = "DATA: stream in half-closed(remote)"; + error_code = NGHTTP2_STREAM_CLOSED; + goto fail; + } + + if (nghttp2_session_is_my_stream_id(session, stream_id)) { + if (stream->state == NGHTTP2_STREAM_CLOSING) { + return NGHTTP2_ERR_IGN_PAYLOAD; + } + if (stream->state != NGHTTP2_STREAM_OPENED) { + failure_reason = "DATA: stream not opened"; + goto fail; + } + return 0; + } + if (stream->state == NGHTTP2_STREAM_RESERVED) { + failure_reason = "DATA: stream in reserved"; + goto fail; + } + if (stream->state == NGHTTP2_STREAM_CLOSING) { + return NGHTTP2_ERR_IGN_PAYLOAD; + } + return 0; +fail: + rv = nghttp2_session_terminate_session_with_reason(session, error_code, + failure_reason); + if (nghttp2_is_fatal(rv)) { + return rv; + } + return NGHTTP2_ERR_IGN_PAYLOAD; +} + +static size_t inbound_frame_payload_readlen(nghttp2_inbound_frame *iframe, + const uint8_t *in, + const uint8_t *last) { + return nghttp2_min((size_t)(last - in), iframe->payloadleft); +} + +/* + * Resets iframe->sbuf and advance its mark pointer by |left| bytes. + */ +static void inbound_frame_set_mark(nghttp2_inbound_frame *iframe, size_t left) { + nghttp2_buf_reset(&iframe->sbuf); + iframe->sbuf.mark += left; +} + +static size_t inbound_frame_buf_read(nghttp2_inbound_frame *iframe, + const uint8_t *in, const uint8_t *last) { + size_t readlen; + + readlen = nghttp2_min(last - in, nghttp2_buf_mark_avail(&iframe->sbuf)); + + iframe->sbuf.last = nghttp2_cpymem(iframe->sbuf.last, in, readlen); + + return readlen; +} + +/* + * Unpacks SETTINGS entry in iframe->sbuf. + */ +static void inbound_frame_set_settings_entry(nghttp2_inbound_frame *iframe) { + nghttp2_settings_entry iv; + size_t i; + + nghttp2_frame_unpack_settings_entry(&iv, iframe->sbuf.pos); + + switch (iv.settings_id) { + case NGHTTP2_SETTINGS_HEADER_TABLE_SIZE: + case NGHTTP2_SETTINGS_ENABLE_PUSH: + case NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS: + case NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE: + case NGHTTP2_SETTINGS_MAX_FRAME_SIZE: + case NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE: + break; + default: + DEBUGF(fprintf(stderr, "recv: ignore unknown settings id=0x%02x\n", + iv.settings_id)); + return; + } + + for (i = 0; i < iframe->niv; ++i) { + if (iframe->iv[i].settings_id == iv.settings_id) { + iframe->iv[i] = iv; + break; + } + } + + if (i == iframe->niv) { + iframe->iv[iframe->niv++] = iv; + } + + if (iv.settings_id == NGHTTP2_SETTINGS_HEADER_TABLE_SIZE && + iv.value < iframe->iv[NGHTTP2_INBOUND_NUM_IV - 1].value) { + + iframe->iv[NGHTTP2_INBOUND_NUM_IV - 1] = iv; + } +} + +/* + * Checks PADDED flags and set iframe->sbuf to read them accordingly. + * If padding is set, this function returns 1. If no padding is set, + * this function returns 0. On error, returns -1. + */ +static int inbound_frame_handle_pad(nghttp2_inbound_frame *iframe, + nghttp2_frame_hd *hd) { + if (hd->flags & NGHTTP2_FLAG_PADDED) { + if (hd->length < 1) { + return -1; + } + inbound_frame_set_mark(iframe, 1); + return 1; + } + DEBUGF(fprintf(stderr, "recv: no padding in payload\n")); + return 0; +} + +/* + * Computes number of padding based on flags. This function returns + * the calculated length if it succeeds, or -1. + */ +static ssize_t inbound_frame_compute_pad(nghttp2_inbound_frame *iframe) { + size_t padlen; + + /* 1 for Pad Length field */ + padlen = iframe->sbuf.pos[0] + 1; + + DEBUGF(fprintf(stderr, "recv: padlen=%zu\n", padlen)); + + /* We cannot use iframe->frame.hd.length because of CONTINUATION */ + if (padlen - 1 > iframe->payloadleft) { + return -1; + } + + iframe->padlen = padlen; + + return padlen; +} + +/* + * This function returns the effective payload length in the data of + * length |readlen| when the remaning payload is |payloadleft|. The + * |payloadleft| does not include |readlen|. If padding was started + * strictly before this data chunk, this function returns -1. + */ +static ssize_t inbound_frame_effective_readlen(nghttp2_inbound_frame *iframe, + size_t payloadleft, + size_t readlen) { + size_t trail_padlen = + nghttp2_frame_trail_padlen(&iframe->frame, iframe->padlen); + + if (trail_padlen > payloadleft) { + size_t padlen; + padlen = trail_padlen - payloadleft; + if (readlen < padlen) { + return -1; + } else { + return readlen - padlen; + } + } + return readlen; +} + +ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in, + size_t inlen) { + const uint8_t *first = in, *last = in + inlen; + nghttp2_inbound_frame *iframe = &session->iframe; + size_t readlen; + ssize_t padlen; + int rv; + int busy = 0; + nghttp2_frame_hd cont_hd; + nghttp2_stream *stream; + size_t pri_fieldlen; + nghttp2_mem *mem; + + DEBUGF(fprintf(stderr, + "recv: connection recv_window_size=%d, local_window=%d\n", + session->recv_window_size, session->local_window_size)); + + mem = &session->mem; + + for (;;) { + switch (iframe->state) { + case NGHTTP2_IB_READ_CLIENT_MAGIC: + readlen = nghttp2_min(inlen, iframe->payloadleft); + + if (memcmp(NGHTTP2_CLIENT_MAGIC + NGHTTP2_CLIENT_MAGIC_LEN - + iframe->payloadleft, + in, readlen) != 0) { + return NGHTTP2_ERR_BAD_CLIENT_MAGIC; + } + + iframe->payloadleft -= readlen; + in += readlen; + + if (iframe->payloadleft == 0) { + session_inbound_frame_reset(session); + iframe->state = NGHTTP2_IB_READ_FIRST_SETTINGS; + } + + break; + case NGHTTP2_IB_READ_FIRST_SETTINGS: + DEBUGF(fprintf(stderr, "recv: [IB_READ_FIRST_SETTINGS]\n")); + + readlen = inbound_frame_buf_read(iframe, in, last); + in += readlen; + + if (nghttp2_buf_mark_avail(&iframe->sbuf)) { + return in - first; + } + + if (iframe->sbuf.pos[3] != NGHTTP2_SETTINGS || + (iframe->sbuf.pos[4] & NGHTTP2_FLAG_ACK)) { + + iframe->state = NGHTTP2_IB_IGN_ALL; + + rv = nghttp2_session_terminate_session_with_reason( + session, NGHTTP2_PROTOCOL_ERROR, "SETTINGS expected"); + + if (nghttp2_is_fatal(rv)) { + return rv; + } + + return inlen; + } + + iframe->state = NGHTTP2_IB_READ_HEAD; + + /* Fall through */ + case NGHTTP2_IB_READ_HEAD: { + int on_begin_frame_called = 0; + + DEBUGF(fprintf(stderr, "recv: [IB_READ_HEAD]\n")); + + readlen = inbound_frame_buf_read(iframe, in, last); + in += readlen; + + if (nghttp2_buf_mark_avail(&iframe->sbuf)) { + return in - first; + } + + nghttp2_frame_unpack_frame_hd(&iframe->frame.hd, iframe->sbuf.pos); + iframe->payloadleft = iframe->frame.hd.length; + + DEBUGF(fprintf(stderr, "recv: payloadlen=%zu, type=%u, flags=0x%02x, " + "stream_id=%d\n", + iframe->frame.hd.length, iframe->frame.hd.type, + iframe->frame.hd.flags, iframe->frame.hd.stream_id)); + + if (iframe->frame.hd.length > session->local_settings.max_frame_size) { + DEBUGF(fprintf(stderr, "recv: length is too large %zu > %u\n", + iframe->frame.hd.length, + session->local_settings.max_frame_size)); + + busy = 1; + + iframe->state = NGHTTP2_IB_IGN_PAYLOAD; + + rv = nghttp2_session_terminate_session_with_reason( + session, NGHTTP2_FRAME_SIZE_ERROR, "too large frame size"); + + if (nghttp2_is_fatal(rv)) { + return rv; + } + + break; + } + + switch (iframe->frame.hd.type) { + case NGHTTP2_DATA: { + DEBUGF(fprintf(stderr, "recv: DATA\n")); + + iframe->frame.hd.flags &= + (NGHTTP2_FLAG_END_STREAM | NGHTTP2_FLAG_PADDED); + /* Check stream is open. If it is not open or closing, + ignore payload. */ + busy = 1; + + rv = session_on_data_received_fail_fast(session); + if (rv == NGHTTP2_ERR_IGN_PAYLOAD) { + DEBUGF(fprintf(stderr, "recv: DATA not allowed stream_id=%d\n", + iframe->frame.hd.stream_id)); + iframe->state = NGHTTP2_IB_IGN_DATA; + break; + } + + if (nghttp2_is_fatal(rv)) { + return rv; + } + + rv = inbound_frame_handle_pad(iframe, &iframe->frame.hd); + if (rv < 0) { + iframe->state = NGHTTP2_IB_IGN_DATA; + rv = nghttp2_session_terminate_session_with_reason( + session, NGHTTP2_PROTOCOL_ERROR, + "DATA: insufficient padding space"); + + if (nghttp2_is_fatal(rv)) { + return rv; + } + break; + } + + if (rv == 1) { + iframe->state = NGHTTP2_IB_READ_PAD_DATA; + break; + } + + iframe->state = NGHTTP2_IB_READ_DATA; + break; + } + case NGHTTP2_HEADERS: + + DEBUGF(fprintf(stderr, "recv: HEADERS\n")); + + iframe->frame.hd.flags &= + (NGHTTP2_FLAG_END_STREAM | NGHTTP2_FLAG_END_HEADERS | + NGHTTP2_FLAG_PADDED | NGHTTP2_FLAG_PRIORITY); + + rv = inbound_frame_handle_pad(iframe, &iframe->frame.hd); + if (rv < 0) { + busy = 1; + + iframe->state = NGHTTP2_IB_IGN_PAYLOAD; + + rv = nghttp2_session_terminate_session_with_reason( + session, NGHTTP2_PROTOCOL_ERROR, + "HEADERS: insufficient padding space"); + if (nghttp2_is_fatal(rv)) { + return rv; + } + break; + } + + if (rv == 1) { + iframe->state = NGHTTP2_IB_READ_NBYTE; + break; + } + + pri_fieldlen = nghttp2_frame_priority_len(iframe->frame.hd.flags); + + if (pri_fieldlen > 0) { + if (iframe->payloadleft < pri_fieldlen) { + busy = 1; + iframe->state = NGHTTP2_IB_FRAME_SIZE_ERROR; + break; + } + + iframe->state = NGHTTP2_IB_READ_NBYTE; + + inbound_frame_set_mark(iframe, pri_fieldlen); + + break; + } + + /* Call on_begin_frame_callback here because + session_process_headers_frame() may call + on_begin_headers_callback */ + rv = session_call_on_begin_frame(session, &iframe->frame.hd); + + if (nghttp2_is_fatal(rv)) { + return rv; + } + + on_begin_frame_called = 1; + + rv = session_process_headers_frame(session); + if (nghttp2_is_fatal(rv)) { + return rv; + } + + busy = 1; + + if (rv == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) { + rv = nghttp2_session_add_rst_stream( + session, iframe->frame.hd.stream_id, NGHTTP2_INTERNAL_ERROR); + if (nghttp2_is_fatal(rv)) { + return rv; + } + iframe->state = NGHTTP2_IB_IGN_HEADER_BLOCK; + break; + } + + if (rv == NGHTTP2_ERR_IGN_HEADER_BLOCK) { + iframe->state = NGHTTP2_IB_IGN_HEADER_BLOCK; + break; + } + + iframe->state = NGHTTP2_IB_READ_HEADER_BLOCK; + + break; + case NGHTTP2_PRIORITY: + DEBUGF(fprintf(stderr, "recv: PRIORITY\n")); + + iframe->frame.hd.flags = NGHTTP2_FLAG_NONE; + + if (iframe->payloadleft != NGHTTP2_PRIORITY_SPECLEN) { + busy = 1; + + iframe->state = NGHTTP2_IB_FRAME_SIZE_ERROR; + + break; + } + + iframe->state = NGHTTP2_IB_READ_NBYTE; + + inbound_frame_set_mark(iframe, NGHTTP2_PRIORITY_SPECLEN); + + break; + case NGHTTP2_RST_STREAM: + case NGHTTP2_WINDOW_UPDATE: +#ifdef DEBUGBUILD + switch (iframe->frame.hd.type) { + case NGHTTP2_RST_STREAM: + DEBUGF(fprintf(stderr, "recv: RST_STREAM\n")); + break; + case NGHTTP2_WINDOW_UPDATE: + DEBUGF(fprintf(stderr, "recv: WINDOW_UPDATE\n")); + break; + } +#endif /* DEBUGBUILD */ + + iframe->frame.hd.flags = NGHTTP2_FLAG_NONE; + + if (iframe->payloadleft != 4) { + busy = 1; + iframe->state = NGHTTP2_IB_FRAME_SIZE_ERROR; + break; + } + + iframe->state = NGHTTP2_IB_READ_NBYTE; + + inbound_frame_set_mark(iframe, 4); + + break; + case NGHTTP2_SETTINGS: + DEBUGF(fprintf(stderr, "recv: SETTINGS\n")); + + iframe->frame.hd.flags &= NGHTTP2_FLAG_ACK; + + if ((iframe->frame.hd.length % NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH) || + ((iframe->frame.hd.flags & NGHTTP2_FLAG_ACK) && + iframe->payloadleft > 0)) { + busy = 1; + iframe->state = NGHTTP2_IB_FRAME_SIZE_ERROR; + break; + } + + iframe->state = NGHTTP2_IB_READ_SETTINGS; + + if (iframe->payloadleft) { + inbound_frame_set_mark(iframe, NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH); + break; + } + + busy = 1; + + inbound_frame_set_mark(iframe, 0); + + break; + case NGHTTP2_PUSH_PROMISE: + DEBUGF(fprintf(stderr, "recv: PUSH_PROMISE\n")); + + iframe->frame.hd.flags &= + (NGHTTP2_FLAG_END_HEADERS | NGHTTP2_FLAG_PADDED); + + rv = inbound_frame_handle_pad(iframe, &iframe->frame.hd); + if (rv < 0) { + busy = 1; + iframe->state = NGHTTP2_IB_IGN_PAYLOAD; + rv = nghttp2_session_terminate_session_with_reason( + session, NGHTTP2_PROTOCOL_ERROR, + "PUSH_PROMISE: insufficient padding space"); + if (nghttp2_is_fatal(rv)) { + return rv; + } + break; + } + + if (rv == 1) { + iframe->state = NGHTTP2_IB_READ_NBYTE; + break; + } + + if (iframe->payloadleft < 4) { + busy = 1; + iframe->state = NGHTTP2_IB_FRAME_SIZE_ERROR; + break; + } + + iframe->state = NGHTTP2_IB_READ_NBYTE; + + inbound_frame_set_mark(iframe, 4); + + break; + case NGHTTP2_PING: + DEBUGF(fprintf(stderr, "recv: PING\n")); + + iframe->frame.hd.flags &= NGHTTP2_FLAG_ACK; + + if (iframe->payloadleft != 8) { + busy = 1; + iframe->state = NGHTTP2_IB_FRAME_SIZE_ERROR; + break; + } + + iframe->state = NGHTTP2_IB_READ_NBYTE; + inbound_frame_set_mark(iframe, 8); + + break; + case NGHTTP2_GOAWAY: + DEBUGF(fprintf(stderr, "recv: GOAWAY\n")); + + iframe->frame.hd.flags = NGHTTP2_FLAG_NONE; + + if (iframe->payloadleft < 8) { + busy = 1; + iframe->state = NGHTTP2_IB_FRAME_SIZE_ERROR; + break; + } + + iframe->state = NGHTTP2_IB_READ_NBYTE; + inbound_frame_set_mark(iframe, 8); + + break; + case NGHTTP2_CONTINUATION: + DEBUGF(fprintf(stderr, "recv: unexpected CONTINUATION\n")); + + /* Receiving CONTINUATION in this state are subject to + connection error of type PROTOCOL_ERROR */ + rv = nghttp2_session_terminate_session_with_reason( + session, NGHTTP2_PROTOCOL_ERROR, "CONTINUATION: unexpected"); + if (nghttp2_is_fatal(rv)) { + return rv; + } + + busy = 1; + + iframe->state = NGHTTP2_IB_IGN_PAYLOAD; + + break; + default: + DEBUGF(fprintf(stderr, "recv: unknown frame\n")); + + /* Silently ignore unknown frame type. */ + + busy = 1; + + iframe->state = NGHTTP2_IB_IGN_PAYLOAD; + + break; + } + + if (!on_begin_frame_called) { + switch (iframe->state) { + case NGHTTP2_IB_IGN_HEADER_BLOCK: + case NGHTTP2_IB_IGN_PAYLOAD: + case NGHTTP2_IB_FRAME_SIZE_ERROR: + case NGHTTP2_IB_IGN_DATA: + break; + default: + rv = session_call_on_begin_frame(session, &iframe->frame.hd); + + if (nghttp2_is_fatal(rv)) { + return rv; + } + } + } + + break; + } + case NGHTTP2_IB_READ_NBYTE: + DEBUGF(fprintf(stderr, "recv: [IB_READ_NBYTE]\n")); + + readlen = inbound_frame_buf_read(iframe, in, last); + in += readlen; + iframe->payloadleft -= readlen; + + DEBUGF(fprintf(stderr, "recv: readlen=%zu, payloadleft=%zu, left=%zd\n", + readlen, iframe->payloadleft, + nghttp2_buf_mark_avail(&iframe->sbuf))); + + if (nghttp2_buf_mark_avail(&iframe->sbuf)) { + return in - first; + } + + switch (iframe->frame.hd.type) { + case NGHTTP2_HEADERS: + if (iframe->padlen == 0 && + (iframe->frame.hd.flags & NGHTTP2_FLAG_PADDED)) { + padlen = inbound_frame_compute_pad(iframe); + if (padlen < 0) { + busy = 1; + rv = nghttp2_session_terminate_session_with_reason( + session, NGHTTP2_PROTOCOL_ERROR, "HEADERS: invalid padding"); + if (nghttp2_is_fatal(rv)) { + return rv; + } + iframe->state = NGHTTP2_IB_IGN_PAYLOAD; + break; + } + iframe->frame.headers.padlen = padlen; + + pri_fieldlen = nghttp2_frame_priority_len(iframe->frame.hd.flags); + + if (pri_fieldlen > 0) { + if (iframe->payloadleft < pri_fieldlen) { + busy = 1; + iframe->state = NGHTTP2_IB_FRAME_SIZE_ERROR; + break; + } + iframe->state = NGHTTP2_IB_READ_NBYTE; + inbound_frame_set_mark(iframe, pri_fieldlen); + break; + } else { + /* Truncate buffers used for padding spec */ + inbound_frame_set_mark(iframe, 0); + } + } + + rv = session_process_headers_frame(session); + if (nghttp2_is_fatal(rv)) { + return rv; + } + + busy = 1; + + if (rv == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) { + rv = nghttp2_session_add_rst_stream( + session, iframe->frame.hd.stream_id, NGHTTP2_INTERNAL_ERROR); + if (nghttp2_is_fatal(rv)) { + return rv; + } + iframe->state = NGHTTP2_IB_IGN_HEADER_BLOCK; + break; + } + + if (rv == NGHTTP2_ERR_IGN_HEADER_BLOCK) { + iframe->state = NGHTTP2_IB_IGN_HEADER_BLOCK; + break; + } + + iframe->state = NGHTTP2_IB_READ_HEADER_BLOCK; + + break; + case NGHTTP2_PRIORITY: + rv = session_process_priority_frame(session); + if (nghttp2_is_fatal(rv)) { + return rv; + } + + session_inbound_frame_reset(session); + + break; + case NGHTTP2_RST_STREAM: + rv = session_process_rst_stream_frame(session); + if (nghttp2_is_fatal(rv)) { + return rv; + } + + session_inbound_frame_reset(session); + + break; + case NGHTTP2_PUSH_PROMISE: + if (iframe->padlen == 0 && + (iframe->frame.hd.flags & NGHTTP2_FLAG_PADDED)) { + padlen = inbound_frame_compute_pad(iframe); + if (padlen < 0) { + busy = 1; + rv = nghttp2_session_terminate_session_with_reason( + session, NGHTTP2_PROTOCOL_ERROR, + "PUSH_PROMISE: invalid padding"); + if (nghttp2_is_fatal(rv)) { + return rv; + } + iframe->state = NGHTTP2_IB_IGN_PAYLOAD; + break; + } + + iframe->frame.push_promise.padlen = padlen; + + if (iframe->payloadleft < 4) { + busy = 1; + iframe->state = NGHTTP2_IB_FRAME_SIZE_ERROR; + break; + } + + iframe->state = NGHTTP2_IB_READ_NBYTE; + + inbound_frame_set_mark(iframe, 4); + + break; + } + + rv = session_process_push_promise_frame(session); + if (nghttp2_is_fatal(rv)) { + return rv; + } + + busy = 1; + + if (rv == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) { + rv = nghttp2_session_add_rst_stream( + session, iframe->frame.push_promise.promised_stream_id, + NGHTTP2_INTERNAL_ERROR); + if (nghttp2_is_fatal(rv)) { + return rv; + } + iframe->state = NGHTTP2_IB_IGN_HEADER_BLOCK; + break; + } + + if (rv == NGHTTP2_ERR_IGN_HEADER_BLOCK) { + iframe->state = NGHTTP2_IB_IGN_HEADER_BLOCK; + break; + } + + iframe->state = NGHTTP2_IB_READ_HEADER_BLOCK; + + break; + case NGHTTP2_PING: + rv = session_process_ping_frame(session); + if (nghttp2_is_fatal(rv)) { + return rv; + } + + session_inbound_frame_reset(session); + + break; + case NGHTTP2_GOAWAY: { + size_t debuglen; + + /* 8 is Last-stream-ID + Error Code */ + debuglen = iframe->frame.hd.length - 8; + + if (debuglen > 0) { + iframe->raw_lbuf = nghttp2_mem_malloc(mem, debuglen); + + if (iframe->raw_lbuf == NULL) { + return NGHTTP2_ERR_NOMEM; + } + + nghttp2_buf_wrap_init(&iframe->lbuf, iframe->raw_lbuf, debuglen); + } + + busy = 1; + + iframe->state = NGHTTP2_IB_READ_GOAWAY_DEBUG; + + break; + } + case NGHTTP2_WINDOW_UPDATE: + rv = session_process_window_update_frame(session); + if (nghttp2_is_fatal(rv)) { + return rv; + } + + session_inbound_frame_reset(session); + + break; + default: + /* This is unknown frame */ + session_inbound_frame_reset(session); + + break; + } + break; + case NGHTTP2_IB_READ_HEADER_BLOCK: + case NGHTTP2_IB_IGN_HEADER_BLOCK: { + ssize_t data_readlen; +#ifdef DEBUGBUILD + if (iframe->state == NGHTTP2_IB_READ_HEADER_BLOCK) { + fprintf(stderr, "recv: [IB_READ_HEADER_BLOCK]\n"); + } else { + fprintf(stderr, "recv: [IB_IGN_HEADER_BLOCK]\n"); + } +#endif /* DEBUGBUILD */ + + readlen = inbound_frame_payload_readlen(iframe, in, last); + + DEBUGF(fprintf(stderr, "recv: readlen=%zu, payloadleft=%zu\n", readlen, + iframe->payloadleft - readlen)); + + data_readlen = inbound_frame_effective_readlen( + iframe, iframe->payloadleft - readlen, readlen); + if (data_readlen >= 0) { + size_t trail_padlen; + size_t hd_proclen = 0; + trail_padlen = + nghttp2_frame_trail_padlen(&iframe->frame, iframe->padlen); + DEBUGF(fprintf(stderr, "recv: block final=%d\n", + (iframe->frame.hd.flags & NGHTTP2_FLAG_END_HEADERS) && + iframe->payloadleft - data_readlen == trail_padlen)); + + rv = inflate_header_block( + session, &iframe->frame, &hd_proclen, (uint8_t *)in, data_readlen, + (iframe->frame.hd.flags & NGHTTP2_FLAG_END_HEADERS) && + iframe->payloadleft - data_readlen == trail_padlen, + iframe->state == NGHTTP2_IB_READ_HEADER_BLOCK); + + if (nghttp2_is_fatal(rv)) { + return rv; + } + + if (rv == NGHTTP2_ERR_PAUSE) { + in += hd_proclen; + iframe->payloadleft -= hd_proclen; + + return in - first; + } + + if (rv == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) { + /* The application says no more headers. We decompress the + rest of the header block but not invoke on_header_callback + and on_frame_recv_callback. */ + in += hd_proclen; + iframe->payloadleft -= hd_proclen; + + /* Use promised stream ID for PUSH_PROMISE */ + rv = nghttp2_session_add_rst_stream( + session, iframe->frame.hd.type == NGHTTP2_PUSH_PROMISE + ? iframe->frame.push_promise.promised_stream_id + : iframe->frame.hd.stream_id, + NGHTTP2_INTERNAL_ERROR); + if (nghttp2_is_fatal(rv)) { + return rv; + } + busy = 1; + iframe->state = NGHTTP2_IB_IGN_HEADER_BLOCK; + break; + } + + in += readlen; + iframe->payloadleft -= readlen; + + if (rv == NGHTTP2_ERR_HEADER_COMP) { + /* GOAWAY is already issued */ + if (iframe->payloadleft == 0) { + session_inbound_frame_reset(session); + } else { + busy = 1; + iframe->state = NGHTTP2_IB_IGN_PAYLOAD; + } + break; + } + } else { + in += readlen; + iframe->payloadleft -= readlen; + } + + if (iframe->payloadleft) { + break; + } + + if ((iframe->frame.hd.flags & NGHTTP2_FLAG_END_HEADERS) == 0) { + + inbound_frame_set_mark(iframe, NGHTTP2_FRAME_HDLEN); + + iframe->padlen = 0; + + if (iframe->state == NGHTTP2_IB_READ_HEADER_BLOCK) { + iframe->state = NGHTTP2_IB_EXPECT_CONTINUATION; + } else { + iframe->state = NGHTTP2_IB_IGN_CONTINUATION; + } + } else { + if (iframe->state == NGHTTP2_IB_READ_HEADER_BLOCK) { + rv = session_after_header_block_received(session); + if (nghttp2_is_fatal(rv)) { + return rv; + } + } + session_inbound_frame_reset(session); + } + break; + } + case NGHTTP2_IB_IGN_PAYLOAD: + DEBUGF(fprintf(stderr, "recv: [IB_IGN_PAYLOAD]\n")); + + readlen = inbound_frame_payload_readlen(iframe, in, last); + iframe->payloadleft -= readlen; + in += readlen; + + DEBUGF(fprintf(stderr, "recv: readlen=%zu, payloadleft=%zu\n", readlen, + iframe->payloadleft)); + + if (iframe->payloadleft) { + break; + } + + switch (iframe->frame.hd.type) { + case NGHTTP2_HEADERS: + case NGHTTP2_PUSH_PROMISE: + case NGHTTP2_CONTINUATION: + /* Mark inflater bad so that we won't perform further decoding */ + session->hd_inflater.ctx.bad = 1; + break; + default: + break; + } + + session_inbound_frame_reset(session); + + break; + case NGHTTP2_IB_FRAME_SIZE_ERROR: + DEBUGF(fprintf(stderr, "recv: [IB_FRAME_SIZE_ERROR]\n")); + + rv = session_handle_frame_size_error(session, &iframe->frame); + if (nghttp2_is_fatal(rv)) { + return rv; + } + + busy = 1; + + iframe->state = NGHTTP2_IB_IGN_PAYLOAD; + + break; + case NGHTTP2_IB_READ_SETTINGS: + DEBUGF(fprintf(stderr, "recv: [IB_READ_SETTINGS]\n")); + + readlen = inbound_frame_buf_read(iframe, in, last); + iframe->payloadleft -= readlen; + in += readlen; + + DEBUGF(fprintf(stderr, "recv: readlen=%zu, payloadleft=%zu\n", readlen, + iframe->payloadleft)); + + if (nghttp2_buf_mark_avail(&iframe->sbuf)) { + break; + } + + if (readlen > 0) { + inbound_frame_set_settings_entry(iframe); + } + if (iframe->payloadleft) { + inbound_frame_set_mark(iframe, NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH); + break; + } + + rv = session_process_settings_frame(session); + + if (nghttp2_is_fatal(rv)) { + return rv; + } + + session_inbound_frame_reset(session); + + break; + case NGHTTP2_IB_READ_GOAWAY_DEBUG: + DEBUGF(fprintf(stderr, "recv: [IB_READ_GOAWAY_DEBUG]\n")); + + readlen = inbound_frame_payload_readlen(iframe, in, last); + + iframe->lbuf.last = nghttp2_cpymem(iframe->lbuf.last, in, readlen); + + iframe->payloadleft -= readlen; + in += readlen; + + DEBUGF(fprintf(stderr, "recv: readlen=%zu, payloadleft=%zu\n", readlen, + iframe->payloadleft)); + + if (iframe->payloadleft) { + assert(nghttp2_buf_avail(&iframe->lbuf) > 0); + + break; + } + + rv = session_process_goaway_frame(session); + + if (nghttp2_is_fatal(rv)) { + return rv; + } + + session_inbound_frame_reset(session); + + break; + case NGHTTP2_IB_EXPECT_CONTINUATION: + case NGHTTP2_IB_IGN_CONTINUATION: +#ifdef DEBUGBUILD + if (iframe->state == NGHTTP2_IB_EXPECT_CONTINUATION) { + fprintf(stderr, "recv: [IB_EXPECT_CONTINUATION]\n"); + } else { + fprintf(stderr, "recv: [IB_IGN_CONTINUATION]\n"); + } +#endif /* DEBUGBUILD */ + + readlen = inbound_frame_buf_read(iframe, in, last); + in += readlen; + + if (nghttp2_buf_mark_avail(&iframe->sbuf)) { + return in - first; + } + + nghttp2_frame_unpack_frame_hd(&cont_hd, iframe->sbuf.pos); + iframe->payloadleft = cont_hd.length; + + DEBUGF(fprintf(stderr, "recv: payloadlen=%zu, type=%u, flags=0x%02x, " + "stream_id=%d\n", + cont_hd.length, cont_hd.type, cont_hd.flags, + cont_hd.stream_id)); + + if (cont_hd.type != NGHTTP2_CONTINUATION || + cont_hd.stream_id != iframe->frame.hd.stream_id) { + DEBUGF(fprintf(stderr, "recv: expected stream_id=%d, type=%d, but " + "got stream_id=%d, type=%d\n", + iframe->frame.hd.stream_id, NGHTTP2_CONTINUATION, + cont_hd.stream_id, cont_hd.type)); + rv = nghttp2_session_terminate_session_with_reason( + session, NGHTTP2_PROTOCOL_ERROR, + "unexpected non-CONTINUATION frame or stream_id is invalid"); + if (nghttp2_is_fatal(rv)) { + return rv; + } + + busy = 1; + + iframe->state = NGHTTP2_IB_IGN_PAYLOAD; + + break; + } + + /* CONTINUATION won't bear NGHTTP2_PADDED flag */ + + iframe->frame.hd.flags |= cont_hd.flags & NGHTTP2_FLAG_END_HEADERS; + iframe->frame.hd.length += cont_hd.length; + + busy = 1; + + if (iframe->state == NGHTTP2_IB_EXPECT_CONTINUATION) { + iframe->state = NGHTTP2_IB_READ_HEADER_BLOCK; + + rv = session_call_on_begin_frame(session, &cont_hd); + + if (nghttp2_is_fatal(rv)) { + return rv; + } + } else { + iframe->state = NGHTTP2_IB_IGN_HEADER_BLOCK; + } + + break; + case NGHTTP2_IB_READ_PAD_DATA: + DEBUGF(fprintf(stderr, "recv: [IB_READ_PAD_DATA]\n")); + + readlen = inbound_frame_buf_read(iframe, in, last); + in += readlen; + iframe->payloadleft -= readlen; + + DEBUGF(fprintf(stderr, "recv: readlen=%zu, payloadleft=%zu, left=%zu\n", + readlen, iframe->payloadleft, + nghttp2_buf_mark_avail(&iframe->sbuf))); + + if (nghttp2_buf_mark_avail(&iframe->sbuf)) { + return in - first; + } + + /* Pad Length field is subject to flow control */ + rv = session_update_recv_connection_window_size(session, readlen); + if (nghttp2_is_fatal(rv)) { + return rv; + } + + /* Pad Length field is consumed immediately */ + rv = + nghttp2_session_consume(session, iframe->frame.hd.stream_id, readlen); + + if (nghttp2_is_fatal(rv)) { + return rv; + } + + stream = nghttp2_session_get_stream(session, iframe->frame.hd.stream_id); + if (stream) { + rv = session_update_recv_stream_window_size( + session, stream, readlen, + iframe->payloadleft || + (iframe->frame.hd.flags & NGHTTP2_FLAG_END_STREAM) == 0); + if (nghttp2_is_fatal(rv)) { + return rv; + } + } + + busy = 1; + + padlen = inbound_frame_compute_pad(iframe); + if (padlen < 0) { + rv = nghttp2_session_terminate_session_with_reason( + session, NGHTTP2_PROTOCOL_ERROR, "DATA: invalid padding"); + if (nghttp2_is_fatal(rv)) { + return rv; + } + iframe->state = NGHTTP2_IB_IGN_DATA; + break; + } + + iframe->frame.data.padlen = padlen; + + iframe->state = NGHTTP2_IB_READ_DATA; + + break; + case NGHTTP2_IB_READ_DATA: + DEBUGF(fprintf(stderr, "recv: [IB_READ_DATA]\n")); + + readlen = inbound_frame_payload_readlen(iframe, in, last); + iframe->payloadleft -= readlen; + in += readlen; + + DEBUGF(fprintf(stderr, "recv: readlen=%zu, payloadleft=%zu\n", readlen, + iframe->payloadleft)); + + if (readlen > 0) { + ssize_t data_readlen; + + rv = session_update_recv_connection_window_size(session, readlen); + if (nghttp2_is_fatal(rv)) { + return rv; + } + + stream = + nghttp2_session_get_stream(session, iframe->frame.hd.stream_id); + if (stream) { + rv = session_update_recv_stream_window_size( + session, stream, readlen, + iframe->payloadleft || + (iframe->frame.hd.flags & NGHTTP2_FLAG_END_STREAM) == 0); + if (nghttp2_is_fatal(rv)) { + return rv; + } + } + + data_readlen = inbound_frame_effective_readlen( + iframe, iframe->payloadleft, readlen); + + padlen = readlen - data_readlen; + + if (padlen > 0) { + /* Padding is considered as "consumed" immediately */ + rv = nghttp2_session_consume(session, iframe->frame.hd.stream_id, + padlen); + + if (nghttp2_is_fatal(rv)) { + return rv; + } + } + + DEBUGF(fprintf(stderr, "recv: data_readlen=%zd\n", data_readlen)); + + if (stream && data_readlen > 0) { + if (session_enforce_http_messaging(session)) { + if (nghttp2_http_on_data_chunk(stream, data_readlen) != 0) { + rv = nghttp2_session_add_rst_stream( + session, iframe->frame.hd.stream_id, NGHTTP2_PROTOCOL_ERROR); + if (nghttp2_is_fatal(rv)) { + return rv; + } + busy = 1; + iframe->state = NGHTTP2_IB_IGN_DATA; + break; + } + } + if (session->callbacks.on_data_chunk_recv_callback) { + rv = session->callbacks.on_data_chunk_recv_callback( + session, iframe->frame.hd.flags, iframe->frame.hd.stream_id, + in - readlen, data_readlen, session->user_data); + if (rv == NGHTTP2_ERR_PAUSE) { + return in - first; + } + + if (nghttp2_is_fatal(rv)) { + return NGHTTP2_ERR_CALLBACK_FAILURE; + } + } + } + } + + if (iframe->payloadleft) { + break; + } + + rv = session_process_data_frame(session); + if (nghttp2_is_fatal(rv)) { + return rv; + } + + session_inbound_frame_reset(session); + + break; + case NGHTTP2_IB_IGN_DATA: + DEBUGF(fprintf(stderr, "recv: [IB_IGN_DATA]\n")); + + readlen = inbound_frame_payload_readlen(iframe, in, last); + iframe->payloadleft -= readlen; + in += readlen; + + DEBUGF(fprintf(stderr, "recv: readlen=%zu, payloadleft=%zu\n", readlen, + iframe->payloadleft)); + + if (readlen > 0) { + /* Update connection-level flow control window for ignored + DATA frame too */ + rv = session_update_recv_connection_window_size(session, readlen); + if (nghttp2_is_fatal(rv)) { + return rv; + } + + if (session->opt_flags & NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE) { + + /* Ignored DATA is considered as "consumed" immediately. */ + rv = session_update_connection_consumed_size(session, readlen); + + if (nghttp2_is_fatal(rv)) { + return rv; + } + } + } + + if (iframe->payloadleft) { + break; + } + + session_inbound_frame_reset(session); + + break; + case NGHTTP2_IB_IGN_ALL: + return inlen; + } + + if (!busy && in == last) { + break; + } + + busy = 0; + } + + assert(in == last); + + return in - first; +} + +int nghttp2_session_recv(nghttp2_session *session) { + uint8_t buf[NGHTTP2_INBOUND_BUFFER_LENGTH]; + while (1) { + ssize_t readlen; + readlen = session_recv(session, buf, sizeof(buf)); + if (readlen > 0) { + ssize_t proclen = nghttp2_session_mem_recv(session, buf, readlen); + if (proclen < 0) { + return (int)proclen; + } + assert(proclen == readlen); + } else if (readlen == 0 || readlen == NGHTTP2_ERR_WOULDBLOCK) { + return 0; + } else if (readlen == NGHTTP2_ERR_EOF) { + return NGHTTP2_ERR_EOF; + } else if (readlen < 0) { + return NGHTTP2_ERR_CALLBACK_FAILURE; + } + } +} + +/* + * Returns the number of active streams, which includes streams in + * reserved state. + */ +static size_t session_get_num_active_streams(nghttp2_session *session) { + return nghttp2_map_size(&session->streams) - session->num_closed_streams - + session->num_idle_streams; +} + +int nghttp2_session_want_read(nghttp2_session *session) { + size_t num_active_streams; + + /* If this flag is set, we don't want to read. The application + should drop the connection. */ + if (session->goaway_flags & NGHTTP2_GOAWAY_TERM_SENT) { + return 0; + } + + num_active_streams = session_get_num_active_streams(session); + + /* Unless termination GOAWAY is sent or received, we always want to + read incoming frames. */ + + if (num_active_streams > 0) { + return 1; + } + + /* If there is no active streams and GOAWAY has been sent or + received, we are done with this session. */ + return (session->goaway_flags & + (NGHTTP2_GOAWAY_SENT | NGHTTP2_GOAWAY_RECV)) == 0; +} + +int nghttp2_session_want_write(nghttp2_session *session) { + /* If these flag is set, we don't want to write any data. The + application should drop the connection. */ + if (session->goaway_flags & NGHTTP2_GOAWAY_TERM_SENT) { + return 0; + } + + /* + * Unless termination GOAWAY is sent or received, we want to write + * frames if there is pending ones. If pending frame is request/push + * response HEADERS and concurrent stream limit is reached, we don't + * want to write them. + */ + + if (session->aob.item == NULL && + nghttp2_outbound_queue_top(&session->ob_urgent) == NULL && + nghttp2_outbound_queue_top(&session->ob_reg) == NULL && + (nghttp2_pq_empty(&session->ob_da_pq) || + session->remote_window_size == 0) && + (nghttp2_outbound_queue_top(&session->ob_syn) == NULL || + session_is_outgoing_concurrent_streams_max(session))) { + return 0; + } + + /* If there is no active streams and GOAWAY has been sent or + received, we are done with this session. */ + return (session->goaway_flags & + (NGHTTP2_GOAWAY_SENT | NGHTTP2_GOAWAY_RECV)) == 0; +} + +int nghttp2_session_add_ping(nghttp2_session *session, uint8_t flags, + const uint8_t *opaque_data) { + int rv; + nghttp2_outbound_item *item; + nghttp2_frame *frame; + nghttp2_mem *mem; + + mem = &session->mem; + item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item)); + if (item == NULL) { + return NGHTTP2_ERR_NOMEM; + } + + nghttp2_outbound_item_init(item); + + frame = &item->frame; + + nghttp2_frame_ping_init(&frame->ping, flags, opaque_data); + + rv = nghttp2_session_add_item(session, item); + + if (rv != 0) { + nghttp2_frame_ping_free(&frame->ping); + nghttp2_mem_free(mem, item); + return rv; + } + return 0; +} + +int nghttp2_session_add_goaway(nghttp2_session *session, int32_t last_stream_id, + uint32_t error_code, const uint8_t *opaque_data, + size_t opaque_data_len, uint8_t aux_flags) { + int rv; + nghttp2_outbound_item *item; + nghttp2_frame *frame; + uint8_t *opaque_data_copy = NULL; + nghttp2_goaway_aux_data *aux_data; + nghttp2_mem *mem; + + mem = &session->mem; + + if (nghttp2_session_is_my_stream_id(session, last_stream_id)) { + return NGHTTP2_ERR_INVALID_ARGUMENT; + } + + if (opaque_data_len) { + if (opaque_data_len + 8 > NGHTTP2_MAX_PAYLOADLEN) { + return NGHTTP2_ERR_INVALID_ARGUMENT; + } + opaque_data_copy = nghttp2_mem_malloc(mem, opaque_data_len); + if (opaque_data_copy == NULL) { + return NGHTTP2_ERR_NOMEM; + } + memcpy(opaque_data_copy, opaque_data, opaque_data_len); + } + + item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item)); + if (item == NULL) { + nghttp2_mem_free(mem, opaque_data_copy); + return NGHTTP2_ERR_NOMEM; + } + + nghttp2_outbound_item_init(item); + + frame = &item->frame; + + /* last_stream_id must not be increased from the value previously + sent */ + last_stream_id = nghttp2_min(last_stream_id, session->local_last_stream_id); + + nghttp2_frame_goaway_init(&frame->goaway, last_stream_id, error_code, + opaque_data_copy, opaque_data_len); + + aux_data = &item->aux_data.goaway; + aux_data->flags = aux_flags; + + rv = nghttp2_session_add_item(session, item); + if (rv != 0) { + nghttp2_frame_goaway_free(&frame->goaway, mem); + nghttp2_mem_free(mem, item); + return rv; + } + return 0; +} + +int nghttp2_session_add_window_update(nghttp2_session *session, uint8_t flags, + int32_t stream_id, + int32_t window_size_increment) { + int rv; + nghttp2_outbound_item *item; + nghttp2_frame *frame; + nghttp2_mem *mem; + + mem = &session->mem; + item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item)); + if (item == NULL) { + return NGHTTP2_ERR_NOMEM; + } + + nghttp2_outbound_item_init(item); + + frame = &item->frame; + + nghttp2_frame_window_update_init(&frame->window_update, flags, stream_id, + window_size_increment); + + rv = nghttp2_session_add_item(session, item); + + if (rv != 0) { + nghttp2_frame_window_update_free(&frame->window_update); + nghttp2_mem_free(mem, item); + return rv; + } + return 0; +} + +int nghttp2_session_add_settings(nghttp2_session *session, uint8_t flags, + const nghttp2_settings_entry *iv, size_t niv) { + nghttp2_outbound_item *item; + nghttp2_frame *frame; + nghttp2_settings_entry *iv_copy; + size_t i; + int rv; + nghttp2_mem *mem; + + mem = &session->mem; + + if (flags & NGHTTP2_FLAG_ACK) { + if (niv != 0) { + return NGHTTP2_ERR_INVALID_ARGUMENT; + } + } else if (session->inflight_niv != -1) { + return NGHTTP2_ERR_TOO_MANY_INFLIGHT_SETTINGS; + } + + if (!nghttp2_iv_check(iv, niv)) { + return NGHTTP2_ERR_INVALID_ARGUMENT; + } + + item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item)); + if (item == NULL) { + return NGHTTP2_ERR_NOMEM; + } + + if (niv > 0) { + iv_copy = nghttp2_frame_iv_copy(iv, niv, mem); + if (iv_copy == NULL) { + nghttp2_mem_free(mem, item); + return NGHTTP2_ERR_NOMEM; + } + } else { + iv_copy = NULL; + } + + if ((flags & NGHTTP2_FLAG_ACK) == 0) { + if (niv > 0) { + session->inflight_iv = nghttp2_frame_iv_copy(iv, niv, mem); + + if (session->inflight_iv == NULL) { + nghttp2_mem_free(mem, iv_copy); + nghttp2_mem_free(mem, item); + return NGHTTP2_ERR_NOMEM; + } + } else { + session->inflight_iv = NULL; + } + + session->inflight_niv = niv; + } + + nghttp2_outbound_item_init(item); + + frame = &item->frame; + + nghttp2_frame_settings_init(&frame->settings, flags, iv_copy, niv); + rv = nghttp2_session_add_item(session, item); + if (rv != 0) { + /* The only expected error is fatal one */ + assert(nghttp2_is_fatal(rv)); + + if ((flags & NGHTTP2_FLAG_ACK) == 0) { + nghttp2_mem_free(mem, session->inflight_iv); + session->inflight_iv = NULL; + session->inflight_niv = -1; + } + + nghttp2_frame_settings_free(&frame->settings, mem); + nghttp2_mem_free(mem, item); + + return rv; + } + + /* Extract NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS and ENABLE_PUSH + here. We use it to refuse the incoming stream and PUSH_PROMISE + with RST_STREAM. */ + + for (i = niv; i > 0; --i) { + if (iv[i - 1].settings_id == NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS) { + session->pending_local_max_concurrent_stream = iv[i - 1].value; + break; + } + } + + for (i = niv; i > 0; --i) { + if (iv[i - 1].settings_id == NGHTTP2_SETTINGS_ENABLE_PUSH) { + session->pending_enable_push = iv[i - 1].value; + break; + } + } + + return 0; +} + +int nghttp2_session_pack_data(nghttp2_session *session, nghttp2_bufs *bufs, + size_t datamax, nghttp2_frame *frame, + nghttp2_data_aux_data *aux_data, + nghttp2_stream *stream) { + int rv; + uint32_t data_flags; + ssize_t payloadlen; + ssize_t padded_payloadlen; + nghttp2_buf *buf; + size_t max_payloadlen; + + assert(bufs->head == bufs->cur); + + buf = &bufs->cur->buf; + + if (session->callbacks.read_length_callback) { + + payloadlen = session->callbacks.read_length_callback( + session, frame->hd.type, stream->stream_id, session->remote_window_size, + stream->remote_window_size, session->remote_settings.max_frame_size, + session->user_data); + + DEBUGF(fprintf(stderr, "send: read_length_callback=%zd\n", payloadlen)); + + payloadlen = nghttp2_session_enforce_flow_control_limits(session, stream, + payloadlen); + + DEBUGF(fprintf(stderr, + "send: read_length_callback after flow control=%zd\n", + payloadlen)); + + if (payloadlen <= 0) { + return NGHTTP2_ERR_CALLBACK_FAILURE; + } + + if (payloadlen > nghttp2_buf_avail(buf)) { + /* Resize the current buffer(s). The reason why we do +1 for + buffer size is for possible padding field. */ + rv = nghttp2_bufs_realloc(&session->aob.framebufs, + NGHTTP2_FRAME_HDLEN + 1 + payloadlen); + + if (rv != 0) { + DEBUGF(fprintf(stderr, "send: realloc buffer failed rv=%d", rv)); + /* If reallocation failed, old buffers are still in tact. So + use safe limit. */ + payloadlen = datamax; + + DEBUGF( + fprintf(stderr, "send: use safe limit payloadlen=%zd", payloadlen)); + } else { + assert(&session->aob.framebufs == bufs); + + buf = &bufs->cur->buf; + } + } + datamax = (size_t)payloadlen; + } + + /* Current max DATA length is less then buffer chunk size */ + assert(nghttp2_buf_avail(buf) >= (ssize_t)datamax); + + data_flags = NGHTTP2_DATA_FLAG_NONE; + payloadlen = aux_data->data_prd.read_callback( + session, frame->hd.stream_id, buf->pos, datamax, &data_flags, + &aux_data->data_prd.source, session->user_data); + + if (payloadlen == NGHTTP2_ERR_DEFERRED || + payloadlen == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) { + DEBUGF(fprintf(stderr, "send: DATA postponed due to %s\n", + nghttp2_strerror((int)payloadlen))); + + return (int)payloadlen; + } + + if (payloadlen < 0 || datamax < (size_t)payloadlen) { + /* This is the error code when callback is failed. */ + return NGHTTP2_ERR_CALLBACK_FAILURE; + } + + buf->last = buf->pos + payloadlen; + buf->pos -= NGHTTP2_FRAME_HDLEN; + + /* Clear flags, because this may contain previous flags of previous + DATA */ + frame->hd.flags = NGHTTP2_FLAG_NONE; + + if (data_flags & NGHTTP2_DATA_FLAG_EOF) { + aux_data->eof = 1; + /* If NGHTTP2_DATA_FLAG_NO_END_STREAM is set, don't set + NGHTTP2_FLAG_END_STREAM */ + if ((aux_data->flags & NGHTTP2_FLAG_END_STREAM) && + (data_flags & NGHTTP2_DATA_FLAG_NO_END_STREAM) == 0) { + frame->hd.flags |= NGHTTP2_FLAG_END_STREAM; + } + } + + if (data_flags & NGHTTP2_DATA_FLAG_NO_COPY) { + if (session->callbacks.send_data_callback == NULL) { + DEBUGF(fprintf( + stderr, + "NGHTTP2_DATA_FLAG_NO_COPY requires send_data_callback set\n")); + + return NGHTTP2_ERR_CALLBACK_FAILURE; + } + aux_data->no_copy = 1; + } + + frame->hd.length = payloadlen; + frame->data.padlen = 0; + + max_payloadlen = nghttp2_min(datamax, frame->hd.length + NGHTTP2_MAX_PADLEN); + + padded_payloadlen = + session_call_select_padding(session, frame, max_payloadlen); + + if (nghttp2_is_fatal((int)padded_payloadlen)) { + return (int)padded_payloadlen; + } + + frame->data.padlen = padded_payloadlen - payloadlen; + + nghttp2_frame_pack_frame_hd(buf->pos, &frame->hd); + + rv = nghttp2_frame_add_pad(bufs, &frame->hd, frame->data.padlen, + aux_data->no_copy); + if (rv != 0) { + return rv; + } + + session_outbound_item_schedule(session, stream->item, + stream->effective_weight); + + return 0; +} + +void *nghttp2_session_get_stream_user_data(nghttp2_session *session, + int32_t stream_id) { + nghttp2_stream *stream; + stream = nghttp2_session_get_stream(session, stream_id); + if (stream) { + return stream->stream_user_data; + } else { + return NULL; + } +} + +int nghttp2_session_set_stream_user_data(nghttp2_session *session, + int32_t stream_id, + void *stream_user_data) { + nghttp2_stream *stream; + stream = nghttp2_session_get_stream(session, stream_id); + if (!stream) { + return NGHTTP2_ERR_INVALID_ARGUMENT; + } + stream->stream_user_data = stream_user_data; + return 0; +} + +int nghttp2_session_resume_data(nghttp2_session *session, int32_t stream_id) { + int rv; + nghttp2_stream *stream; + stream = nghttp2_session_get_stream(session, stream_id); + if (stream == NULL || !nghttp2_stream_check_deferred_item(stream)) { + return NGHTTP2_ERR_INVALID_ARGUMENT; + } + + rv = nghttp2_stream_resume_deferred_item( + stream, NGHTTP2_STREAM_FLAG_DEFERRED_USER, session); + + if (nghttp2_is_fatal(rv)) { + return rv; + } + + return rv; +} + +size_t nghttp2_session_get_outbound_queue_size(nghttp2_session *session) { + return nghttp2_outbound_queue_size(&session->ob_urgent) + + nghttp2_outbound_queue_size(&session->ob_reg) + + nghttp2_outbound_queue_size(&session->ob_syn) + + nghttp2_pq_size(&session->ob_da_pq); +} + +int32_t +nghttp2_session_get_stream_effective_recv_data_length(nghttp2_session *session, + int32_t stream_id) { + nghttp2_stream *stream; + stream = nghttp2_session_get_stream(session, stream_id); + if (stream == NULL) { + return -1; + } + return stream->recv_window_size < 0 ? 0 : stream->recv_window_size; +} + +int32_t +nghttp2_session_get_stream_effective_local_window_size(nghttp2_session *session, + int32_t stream_id) { + nghttp2_stream *stream; + stream = nghttp2_session_get_stream(session, stream_id); + if (stream == NULL) { + return -1; + } + return stream->local_window_size; +} + +int32_t +nghttp2_session_get_effective_recv_data_length(nghttp2_session *session) { + return session->recv_window_size < 0 ? 0 : session->recv_window_size; +} + +int32_t +nghttp2_session_get_effective_local_window_size(nghttp2_session *session) { + return session->local_window_size; +} + +int32_t nghttp2_session_get_stream_remote_window_size(nghttp2_session *session, + int32_t stream_id) { + nghttp2_stream *stream; + + stream = nghttp2_session_get_stream(session, stream_id); + if (stream == NULL) { + return -1; + } + + /* stream->remote_window_size can be negative when + SETTINGS_INITIAL_WINDOW_SIZE is changed. */ + return nghttp2_max(0, stream->remote_window_size); +} + +int32_t nghttp2_session_get_remote_window_size(nghttp2_session *session) { + return session->remote_window_size; +} + +uint32_t nghttp2_session_get_remote_settings(nghttp2_session *session, + nghttp2_settings_id id) { + switch (id) { + case NGHTTP2_SETTINGS_HEADER_TABLE_SIZE: + return session->remote_settings.header_table_size; + case NGHTTP2_SETTINGS_ENABLE_PUSH: + return session->remote_settings.enable_push; + case NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS: + return session->remote_settings.max_concurrent_streams; + case NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE: + return session->remote_settings.initial_window_size; + case NGHTTP2_SETTINGS_MAX_FRAME_SIZE: + return session->remote_settings.max_frame_size; + case NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE: + return session->remote_settings.max_header_list_size; + } + + assert(0); +} + +int nghttp2_session_upgrade(nghttp2_session *session, + const uint8_t *settings_payload, + size_t settings_payloadlen, + void *stream_user_data) { + nghttp2_stream *stream; + nghttp2_frame frame; + nghttp2_settings_entry *iv; + size_t niv; + int rv; + nghttp2_priority_spec pri_spec; + nghttp2_mem *mem; + + mem = &session->mem; + + if ((!session->server && session->next_stream_id != 1) || + (session->server && session->last_recv_stream_id >= 1)) { + return NGHTTP2_ERR_PROTO; + } + if (settings_payloadlen % NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH) { + return NGHTTP2_ERR_INVALID_ARGUMENT; + } + rv = nghttp2_frame_unpack_settings_payload2(&iv, &niv, settings_payload, + settings_payloadlen, mem); + if (rv != 0) { + return rv; + } + + if (session->server) { + nghttp2_frame_hd_init(&frame.hd, settings_payloadlen, NGHTTP2_SETTINGS, + NGHTTP2_FLAG_NONE, 0); + frame.settings.iv = iv; + frame.settings.niv = niv; + rv = nghttp2_session_on_settings_received(session, &frame, 1 /* No ACK */); + } else { + rv = nghttp2_submit_settings(session, NGHTTP2_FLAG_NONE, iv, niv); + } + nghttp2_mem_free(mem, iv); + if (rv != 0) { + return rv; + } + + nghttp2_priority_spec_default_init(&pri_spec); + + stream = nghttp2_session_open_stream( + session, 1, NGHTTP2_STREAM_FLAG_NONE, &pri_spec, NGHTTP2_STREAM_OPENING, + session->server ? NULL : stream_user_data); + if (stream == NULL) { + return NGHTTP2_ERR_NOMEM; + } + if (session->server) { + nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_RD); + session->last_recv_stream_id = 1; + session->last_proc_stream_id = 1; + } else { + nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_WR); + session->next_stream_id += 2; + } + return 0; +} + +int nghttp2_session_get_stream_local_close(nghttp2_session *session, + int32_t stream_id) { + nghttp2_stream *stream; + + stream = nghttp2_session_get_stream(session, stream_id); + + if (!stream) { + return -1; + } + + return (stream->shut_flags & NGHTTP2_SHUT_WR) != 0; +} + +int nghttp2_session_get_stream_remote_close(nghttp2_session *session, + int32_t stream_id) { + nghttp2_stream *stream; + + stream = nghttp2_session_get_stream(session, stream_id); + + if (!stream) { + return -1; + } + + return (stream->shut_flags & NGHTTP2_SHUT_RD) != 0; +} + +int nghttp2_session_consume(nghttp2_session *session, int32_t stream_id, + size_t size) { + int rv; + nghttp2_stream *stream; + + if (stream_id == 0) { + return NGHTTP2_ERR_INVALID_ARGUMENT; + } + + if (!(session->opt_flags & NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE)) { + return NGHTTP2_ERR_INVALID_STATE; + } + + rv = session_update_connection_consumed_size(session, size); + + if (nghttp2_is_fatal(rv)) { + return rv; + } + + stream = nghttp2_session_get_stream(session, stream_id); + + if (!stream) { + return 0; + } + + rv = session_update_stream_consumed_size(session, stream, size); + + if (nghttp2_is_fatal(rv)) { + return rv; + } + + return 0; +} + +int nghttp2_session_consume_connection(nghttp2_session *session, size_t size) { + int rv; + + if (!(session->opt_flags & NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE)) { + return NGHTTP2_ERR_INVALID_STATE; + } + + rv = session_update_connection_consumed_size(session, size); + + if (nghttp2_is_fatal(rv)) { + return rv; + } + + return 0; +} + +int nghttp2_session_consume_stream(nghttp2_session *session, int32_t stream_id, + size_t size) { + int rv; + nghttp2_stream *stream; + + if (stream_id == 0) { + return NGHTTP2_ERR_INVALID_ARGUMENT; + } + + if (!(session->opt_flags & NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE)) { + return NGHTTP2_ERR_INVALID_STATE; + } + + stream = nghttp2_session_get_stream(session, stream_id); + + if (!stream) { + return 0; + } + + rv = session_update_stream_consumed_size(session, stream, size); + + if (nghttp2_is_fatal(rv)) { + return rv; + } + + return 0; +} + +int nghttp2_session_set_next_stream_id(nghttp2_session *session, + int32_t next_stream_id) { + if (next_stream_id <= 0 || + session->next_stream_id > (uint32_t)next_stream_id) { + return NGHTTP2_ERR_INVALID_ARGUMENT; + } + + if (session->server) { + if (next_stream_id % 2) { + return NGHTTP2_ERR_INVALID_ARGUMENT; + } + } else if (next_stream_id % 2 == 0) { + return NGHTTP2_ERR_INVALID_ARGUMENT; + } + + session->next_stream_id = next_stream_id; + return 0; +} + +uint32_t nghttp2_session_get_next_stream_id(nghttp2_session *session) { + return session->next_stream_id; +} + +int32_t nghttp2_session_get_last_proc_stream_id(nghttp2_session *session) { + return session->last_proc_stream_id; +} diff --git a/lib/nghttp2_session.h b/lib/nghttp2_session.h new file mode 100644 index 0000000..41b1608 --- /dev/null +++ b/lib/nghttp2_session.h @@ -0,0 +1,762 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGHTTP2_SESSION_H +#define NGHTTP2_SESSION_H + +#ifdef HAVE_CONFIG_H +#include +#endif /* HAVE_CONFIG_H */ + +#include +#include "nghttp2_pq.h" +#include "nghttp2_map.h" +#include "nghttp2_frame.h" +#include "nghttp2_hd.h" +#include "nghttp2_stream.h" +#include "nghttp2_outbound_item.h" +#include "nghttp2_int.h" +#include "nghttp2_buf.h" +#include "nghttp2_callbacks.h" +#include "nghttp2_mem.h" + +/* + * Option flags. + */ +typedef enum { + NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE = 1 << 0, + NGHTTP2_OPTMASK_NO_RECV_CLIENT_MAGIC = 1 << 1, + NGHTTP2_OPTMASK_NO_HTTP_MESSAGING = 1 << 2 +} nghttp2_optmask; + +typedef enum { + NGHTTP2_OB_POP_ITEM, + NGHTTP2_OB_SEND_DATA, + NGHTTP2_OB_SEND_NO_COPY, + NGHTTP2_OB_SEND_CLIENT_MAGIC +} nghttp2_outbound_state; + +typedef struct { + nghttp2_outbound_item *item; + nghttp2_bufs framebufs; + nghttp2_outbound_state state; +} nghttp2_active_outbound_item; + +/* Buffer length for inbound raw byte stream used in + nghttp2_session_recv(). */ +#define NGHTTP2_INBOUND_BUFFER_LENGTH 16384 + +/* Internal state when receiving incoming frame */ +typedef enum { + /* Receiving frame header */ + NGHTTP2_IB_READ_CLIENT_MAGIC, + NGHTTP2_IB_READ_FIRST_SETTINGS, + NGHTTP2_IB_READ_HEAD, + NGHTTP2_IB_READ_NBYTE, + NGHTTP2_IB_READ_HEADER_BLOCK, + NGHTTP2_IB_IGN_HEADER_BLOCK, + NGHTTP2_IB_IGN_PAYLOAD, + NGHTTP2_IB_FRAME_SIZE_ERROR, + NGHTTP2_IB_READ_SETTINGS, + NGHTTP2_IB_READ_GOAWAY_DEBUG, + NGHTTP2_IB_EXPECT_CONTINUATION, + NGHTTP2_IB_IGN_CONTINUATION, + NGHTTP2_IB_READ_PAD_DATA, + NGHTTP2_IB_READ_DATA, + NGHTTP2_IB_IGN_DATA, + NGHTTP2_IB_IGN_ALL +} nghttp2_inbound_state; + +#define NGHTTP2_INBOUND_NUM_IV 7 + +typedef struct { + nghttp2_frame frame; + /* Storage for extension frame payload. frame->ext.payload points + to this structure to avoid frequent memory allocation. */ + nghttp2_ext_frame_payload ext_frame_payload; + /* The received SETTINGS entry. The protocol says that we only cares + about the defined settings ID. If unknown ID is received, it is + ignored. We use last entry to hold minimum header table size if + same settings are multiple times. */ + nghttp2_settings_entry iv[NGHTTP2_INBOUND_NUM_IV]; + /* buffer pointers to small buffer, raw_sbuf */ + nghttp2_buf sbuf; + /* buffer pointers to large buffer, raw_lbuf */ + nghttp2_buf lbuf; + /* Large buffer, malloced on demand */ + uint8_t *raw_lbuf; + /* The number of entry filled in |iv| */ + size_t niv; + /* How many bytes we still need to receive for current frame */ + size_t payloadleft; + /* padding length for the current frame */ + size_t padlen; + nghttp2_inbound_state state; + /* Small buffer. Currently the largest contiguous chunk to buffer + is frame header. We buffer part of payload, but they are smaller + than frame header. */ + uint8_t raw_sbuf[NGHTTP2_FRAME_HDLEN]; +} nghttp2_inbound_frame; + +typedef struct { + uint32_t header_table_size; + uint32_t enable_push; + uint32_t max_concurrent_streams; + uint32_t initial_window_size; + uint32_t max_frame_size; + uint32_t max_header_list_size; +} nghttp2_settings_storage; + +typedef enum { + NGHTTP2_GOAWAY_NONE = 0, + /* Flag means that connection should be terminated after sending GOAWAY. */ + NGHTTP2_GOAWAY_TERM_ON_SEND = 0x1, + /* Flag means GOAWAY to terminate session has been sent */ + NGHTTP2_GOAWAY_TERM_SENT = 0x2, + /* Flag means GOAWAY was sent */ + NGHTTP2_GOAWAY_SENT = 0x4, + /* Flag means GOAWAY was received */ + NGHTTP2_GOAWAY_RECV = 0x8 +} nghttp2_goaway_flag; + +struct nghttp2_session { + nghttp2_map /* */ streams; + nghttp2_stream_roots roots; + /* Queue for outbound urgent frames (PING and SETTINGS) */ + nghttp2_outbound_queue ob_urgent; + /* Queue for non-DATA frames */ + nghttp2_outbound_queue ob_reg; + /* Queue for outbound stream-creating HEADERS (request or push + response) frame, which are subject to + SETTINGS_MAX_CONCURRENT_STREAMS limit. */ + nghttp2_outbound_queue ob_syn; + /* Queue for DATA frame */ + nghttp2_pq /* */ ob_da_pq; + nghttp2_active_outbound_item aob; + nghttp2_inbound_frame iframe; + nghttp2_hd_deflater hd_deflater; + nghttp2_hd_inflater hd_inflater; + nghttp2_session_callbacks callbacks; + /* Memory allocator */ + nghttp2_mem mem; + /* Base value when we schedule next DATA frame write. This is + updated when one frame was written. */ + uint64_t last_cycle; + void *user_data; + /* Points to the latest closed stream. NULL if there is no closed + stream. Only used when session is initialized as server. */ + nghttp2_stream *closed_stream_head; + /* Points to the oldest closed stream. NULL if there is no closed + stream. Only used when session is initialized as server. */ + nghttp2_stream *closed_stream_tail; + /* Points to the latest idle stream. NULL if there is no idle + stream. Only used when session is initialized as server .*/ + nghttp2_stream *idle_stream_head; + /* Points to the oldest idle stream. NULL if there is no idle + stream. Only used when session is initialized as erver. */ + nghttp2_stream *idle_stream_tail; + /* In-flight SETTINGS values. NULL does not necessarily mean there + is no in-flight SETTINGS. */ + nghttp2_settings_entry *inflight_iv; + /* The number of entries in |inflight_iv|. -1 if there is no + in-flight SETTINGS. */ + ssize_t inflight_niv; + /* The number of outgoing streams. This will be capped by + remote_settings.max_concurrent_streams. */ + size_t num_outgoing_streams; + /* The number of incoming streams. This will be capped by + local_settings.max_concurrent_streams. */ + size_t num_incoming_streams; + /* The number of closed streams still kept in |streams| hash. The + closed streams can be accessed through single linked list + |closed_stream_head|. The current implementation only keeps + incoming streams and session is initialized as server. */ + size_t num_closed_streams; + /* The number of idle streams kept in |streams| hash. The idle + streams can be accessed through doubly linked list + |idle_stream_head|. The current implementation only keeps idle + streams if session is initialized as server. */ + size_t num_idle_streams; + /* The number of bytes allocated for nvbuf */ + size_t nvbuflen; + /* Next Stream ID. Made unsigned int to detect >= (1 << 31). */ + uint32_t next_stream_id; + /* The largest stream ID received so far */ + int32_t last_recv_stream_id; + /* The largest stream ID which has been processed in some way. This + value will be used as last-stream-id when sending GOAWAY + frame. */ + int32_t last_proc_stream_id; + /* Counter of unique ID of PING. Wraps when it exceeds + NGHTTP2_MAX_UNIQUE_ID */ + uint32_t next_unique_id; + /* This is the last-stream-ID we have sent in GOAWAY */ + int32_t local_last_stream_id; + /* This is the value in GOAWAY frame received from remote endpoint. */ + int32_t remote_last_stream_id; + /* Current sender window size. This value is computed against the + current initial window size of remote endpoint. */ + int32_t remote_window_size; + /* Keep track of the number of bytes received without + WINDOW_UPDATE. This could be negative after submitting negative + value to WINDOW_UPDATE. */ + int32_t recv_window_size; + /* The number of bytes consumed by the application and now is + subject to WINDOW_UPDATE. This is only used when auto + WINDOW_UPDATE is turned off. */ + int32_t consumed_size; + /* The amount of recv_window_size cut using submitting negative + value to WINDOW_UPDATE */ + int32_t recv_reduction; + /* window size for local flow control. It is initially set to + NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE and could be + increased/decreased by submitting WINDOW_UPDATE. See + nghttp2_submit_window_update(). */ + int32_t local_window_size; + /* Settings value received from the remote endpoint. We just use ID + as index. The index = 0 is unused. */ + nghttp2_settings_storage remote_settings; + /* Settings value of the local endpoint. */ + nghttp2_settings_storage local_settings; + /* Option flags. This is bitwise-OR of 0 or more of nghttp2_optmask. */ + uint32_t opt_flags; + /* Unacked local SETTINGS_MAX_CONCURRENT_STREAMS value. We use this + to refuse the incoming stream if it exceeds this value. */ + uint32_t pending_local_max_concurrent_stream; + /* Unacked local ENABLE_PUSH value. We use this to refuse + PUSH_PROMISE before SETTINGS ACK is received. */ + uint8_t pending_enable_push; + /* Nonzero if the session is server side. */ + uint8_t server; + /* Flags indicating GOAWAY is sent and/or recieved. The flags are + composed by bitwise OR-ing nghttp2_goaway_flag. */ + uint8_t goaway_flags; +}; + +/* Struct used when updating initial window size of each active + stream. */ +typedef struct { + nghttp2_session *session; + int32_t new_window_size, old_window_size; +} nghttp2_update_window_size_arg; + +typedef struct { + nghttp2_session *session; + /* linked list of streams to close */ + nghttp2_stream *head; + int32_t last_stream_id; + /* nonzero if GOAWAY is sent to peer, which means we are going to + close incoming streams. zero if GOAWAY is received from peer and + we are going to close outgoing streams. */ + int incoming; +} nghttp2_close_stream_on_goaway_arg; + +/* TODO stream timeout etc */ + +/* + * Returns nonzero value if |stream_id| is initiated by local + * endpoint. + */ +int nghttp2_session_is_my_stream_id(nghttp2_session *session, + int32_t stream_id); + +/* + * Adds |item| to the outbound queue in |session|. When this function + * succeeds, it takes ownership of |item|. So caller must not free it + * on success. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + * NGHTTP2_ERR_STREAM_CLOSED + * Stream already closed (DATA frame only) + */ +int nghttp2_session_add_item(nghttp2_session *session, + nghttp2_outbound_item *item); + +/* + * Adds RST_STREAM frame for the stream |stream_id| with the error + * code |error_code|. This is a convenient function built on top of + * nghttp2_session_add_frame() to add RST_STREAM easily. + * + * This function simply returns 0 without adding RST_STREAM frame if + * given stream is in NGHTTP2_STREAM_CLOSING state, because multiple + * RST_STREAM for a stream is redundant. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + */ +int nghttp2_session_add_rst_stream(nghttp2_session *session, int32_t stream_id, + uint32_t error_code); + +/* + * Adds PING frame. This is a convenient functin built on top of + * nghttp2_session_add_frame() to add PING easily. + * + * If the |opaque_data| is not NULL, it must point to 8 bytes memory + * region of data. The data pointed by |opaque_data| is copied. It can + * be NULL. In this case, 8 bytes NULL is used. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + */ +int nghttp2_session_add_ping(nghttp2_session *session, uint8_t flags, + const uint8_t *opaque_data); + +/* + * Adds GOAWAY frame with the last-stream-ID |last_stream_id| and the + * error code |error_code|. This is a convenient function built on top + * of nghttp2_session_add_frame() to add GOAWAY easily. The + * |aux_flags| are bitwise-OR of one or more of + * nghttp2_goaway_aux_flag. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + * NGHTTP2_ERR_INVALID_ARGUMENT + * The |opaque_data_len| is too large. + */ +int nghttp2_session_add_goaway(nghttp2_session *session, int32_t last_stream_id, + uint32_t error_code, const uint8_t *opaque_data, + size_t opaque_data_len, uint8_t aux_flags); + +/* + * Adds WINDOW_UPDATE frame with stream ID |stream_id| and + * window-size-increment |window_size_increment|. This is a convenient + * function built on top of nghttp2_session_add_frame() to add + * WINDOW_UPDATE easily. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + */ +int nghttp2_session_add_window_update(nghttp2_session *session, uint8_t flags, + int32_t stream_id, + int32_t window_size_increment); + +/* + * Adds SETTINGS frame. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + */ +int nghttp2_session_add_settings(nghttp2_session *session, uint8_t flags, + const nghttp2_settings_entry *iv, size_t niv); + +/* + * Creates new stream in |session| with stream ID |stream_id|, + * priority |pri_spec| and flags |flags|. The |flags| is bitwise OR + * of nghttp2_stream_flag. Since this function is called when initial + * HEADERS is sent or received, these flags are taken from it. The + * state of stream is set to |initial_state|. The |stream_user_data| + * is a pointer to the arbitrary user supplied data to be associated + * to this stream. + * + * If |initial_state| is NGHTTP2_STREAM_RESERVED, this function sets + * NGHTTP2_STREAM_FLAG_PUSH flag set. + * + * This function returns a pointer to created new stream object, or + * NULL. + */ +nghttp2_stream *nghttp2_session_open_stream(nghttp2_session *session, + int32_t stream_id, uint8_t flags, + nghttp2_priority_spec *pri_spec, + nghttp2_stream_state initial_state, + void *stream_user_data); + +/* + * Closes stream whose stream ID is |stream_id|. The reason of closure + * is indicated by the |error_code|. When closing the stream, + * on_stream_close_callback will be called. + * + * If the session is initialized as server and |stream| is incoming + * stream, stream is just marked closed and this function calls + * nghttp2_session_keep_closed_stream() with |stream|. Otherwise, + * |stream| will be deleted from memory. + * + * This function returns 0 if it succeeds, or one the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory + * NGHTTP2_ERR_INVALID_ARGUMENT + * The specified stream does not exist. + * NGHTTP2_ERR_CALLBACK_FAILURE + * The callback function failed. + */ +int nghttp2_session_close_stream(nghttp2_session *session, int32_t stream_id, + uint32_t error_code); + +/* + * Deletes |stream| from memory. After this function returns, stream + * cannot be accessed. + * + */ +void nghttp2_session_destroy_stream(nghttp2_session *session, + nghttp2_stream *stream); + +/* + * Tries to keep incoming closed stream |stream|. Due to the + * limitation of maximum number of streams in memory, |stream| is not + * closed and just deleted from memory (see + * nghttp2_session_destroy_stream). + */ +void nghttp2_session_keep_closed_stream(nghttp2_session *session, + nghttp2_stream *stream); + +/* + * Appends |stream| to linked list |session->idle_stream_head|. We + * apply fixed limit for list size. To fit into that limit, one or + * more oldest streams are removed from list as necessary. + */ +void nghttp2_session_keep_idle_stream(nghttp2_session *session, + nghttp2_stream *stream); + +/* + * Detaches |stream| from idle streams linked list. + */ +void nghttp2_session_detach_idle_stream(nghttp2_session *session, + nghttp2_stream *stream); + +/* + * Deletes closed stream to ensure that number of incoming streams + * including active and closed is in the maximum number of allowed + * stream. If |offset| is nonzero, it is decreased from the maximum + * number of allowed stream when comparing number of active and closed + * stream and the maximum number. + */ +void nghttp2_session_adjust_closed_stream(nghttp2_session *session, + ssize_t offset); + +/* + * Deletes idle stream to ensure that number of idle streams is in + * certain limit. + */ +void nghttp2_session_adjust_idle_stream(nghttp2_session *session); + +/* + * If further receptions and transmissions over the stream |stream_id| + * are disallowed, close the stream with error code NGHTTP2_NO_ERROR. + * + * This function returns 0 if it + * succeeds, or one of the following negative error codes: + * + * NGHTTP2_ERR_INVALID_ARGUMENT + * The specified stream does not exist. + */ +int nghttp2_session_close_stream_if_shut_rdwr(nghttp2_session *session, + nghttp2_stream *stream); + +int nghttp2_session_end_request_headers_received(nghttp2_session *session, + nghttp2_frame *frame, + nghttp2_stream *stream); + +int nghttp2_session_end_response_headers_received(nghttp2_session *session, + nghttp2_frame *frame, + nghttp2_stream *stream); + +int nghttp2_session_end_headers_received(nghttp2_session *session, + nghttp2_frame *frame, + nghttp2_stream *stream); + +int nghttp2_session_on_request_headers_received(nghttp2_session *session, + nghttp2_frame *frame); + +int nghttp2_session_on_response_headers_received(nghttp2_session *session, + nghttp2_frame *frame, + nghttp2_stream *stream); + +int nghttp2_session_on_push_response_headers_received(nghttp2_session *session, + nghttp2_frame *frame, + nghttp2_stream *stream); + +/* + * Called when HEADERS is received, assuming |frame| is properly + * initialized. This function does first validate received frame and + * then open stream and call callback functions. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + * NGHTTP2_ERR_IGN_HEADER_BLOCK + * Frame was rejected and header block must be decoded but + * result must be ignored. + * NGHTTP2_ERR_CALLBACK_FAILURE + * The read_callback failed + */ +int nghttp2_session_on_headers_received(nghttp2_session *session, + nghttp2_frame *frame, + nghttp2_stream *stream); + +/* + * Called when PRIORITY is received, assuming |frame| is properly + * initialized. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + * NGHTTP2_ERR_CALLBACK_FAILURE + * The read_callback failed + */ +int nghttp2_session_on_priority_received(nghttp2_session *session, + nghttp2_frame *frame); + +/* + * Called when RST_STREAM is received, assuming |frame| is properly + * initialized. + * + * This function returns 0 if it succeeds, or one the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory + * NGHTTP2_ERR_CALLBACK_FAILURE + * The read_callback failed + */ +int nghttp2_session_on_rst_stream_received(nghttp2_session *session, + nghttp2_frame *frame); + +/* + * Called when SETTINGS is received, assuming |frame| is properly + * initialized. If |noack| is non-zero, SETTINGS with ACK will not be + * submitted. If |frame| has NGHTTP2_FLAG_ACK flag set, no SETTINGS + * with ACK will not be submitted regardless of |noack|. + * + * This function returns 0 if it succeeds, or one the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory + * NGHTTP2_ERR_CALLBACK_FAILURE + * The read_callback failed + */ +int nghttp2_session_on_settings_received(nghttp2_session *session, + nghttp2_frame *frame, int noack); + +/* + * Called when PUSH_PROMISE is received, assuming |frame| is properly + * initialized. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + * NGHTTP2_ERR_IGN_HEADER_BLOCK + * Frame was rejected and header block must be decoded but + * result must be ignored. + * NGHTTP2_ERR_CALLBACK_FAILURE + * The read_callback failed + */ +int nghttp2_session_on_push_promise_received(nghttp2_session *session, + nghttp2_frame *frame); + +/* + * Called when PING is received, assuming |frame| is properly + * initialized. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + * NGHTTP2_ERR_CALLBACK_FAILURE + * The callback function failed. + */ +int nghttp2_session_on_ping_received(nghttp2_session *session, + nghttp2_frame *frame); + +/* + * Called when GOAWAY is received, assuming |frame| is properly + * initialized. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + * NGHTTP2_ERR_CALLBACK_FAILURE + * The callback function failed. + */ +int nghttp2_session_on_goaway_received(nghttp2_session *session, + nghttp2_frame *frame); + +/* + * Called when WINDOW_UPDATE is recieved, assuming |frame| is properly + * initialized. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + * NGHTTP2_ERR_CALLBACK_FAILURE + * The callback function failed. + */ +int nghttp2_session_on_window_update_received(nghttp2_session *session, + nghttp2_frame *frame); + +/* + * Called when DATA is received, assuming |frame| is properly + * initialized. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + * NGHTTP2_ERR_CALLBACK_FAILURE + * The callback function failed. + */ +int nghttp2_session_on_data_received(nghttp2_session *session, + nghttp2_frame *frame); + +/* + * Returns nghttp2_stream* object whose stream ID is |stream_id|. It + * could be NULL if such stream does not exist. This function returns + * NULL if stream is marked as closed. + */ +nghttp2_stream *nghttp2_session_get_stream(nghttp2_session *session, + int32_t stream_id); + +/* + * This function behaves like nghttp2_session_get_stream(), but it + * returns stream object even if it is marked as closed or in + * NGHTTP2_STREAM_IDLE state. + */ +nghttp2_stream *nghttp2_session_get_stream_raw(nghttp2_session *session, + int32_t stream_id); + +/* + * Packs DATA frame |frame| in wire frame format and stores it in + * |bufs|. Payload will be read using |aux_data->data_prd|. The + * length of payload is at most |datamax| bytes. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_DEFERRED + * The DATA frame is postponed. + * NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE + * The read_callback failed (stream error). + * NGHTTP2_ERR_NOMEM + * Out of memory. + * NGHTTP2_ERR_CALLBACK_FAILURE + * The read_callback failed (session error). + */ +int nghttp2_session_pack_data(nghttp2_session *session, nghttp2_bufs *bufs, + size_t datamax, nghttp2_frame *frame, + nghttp2_data_aux_data *aux_data, + nghttp2_stream *stream); + +/* + * Pops and returns next item to send. If there is no such item, + * returns NULL. This function takes into account max concurrent + * streams. That means if session->ob_pq is empty but + * session->ob_ss_pq has item and max concurrent streams is reached, + * then this function returns NULL. + */ +nghttp2_outbound_item * +nghttp2_session_pop_next_ob_item(nghttp2_session *session); + +/* + * Returns next item to send. If there is no such item, this function + * returns NULL. This function takes into account max concurrent + * streams. That means if session->ob_pq is empty but + * session->ob_ss_pq has item and max concurrent streams is reached, + * then this function returns NULL. + */ +nghttp2_outbound_item * +nghttp2_session_get_next_ob_item(nghttp2_session *session); + +/* + * Updates local settings with the |iv|. The number of elements in the + * array pointed by the |iv| is given by the |niv|. This function + * assumes that the all settings_id member in |iv| are in range 1 to + * NGHTTP2_SETTINGS_MAX, inclusive. + * + * While updating individual stream's local window size, if the window + * size becomes strictly larger than NGHTTP2_MAX_WINDOW_SIZE, + * RST_STREAM is issued against such a stream. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory + */ +int nghttp2_session_update_local_settings(nghttp2_session *session, + nghttp2_settings_entry *iv, + size_t niv); + +/* + * Re-prioritize |stream|. The new priority specification is + * |pri_spec|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory + */ +int nghttp2_session_reprioritize_stream(nghttp2_session *session, + nghttp2_stream *stream, + const nghttp2_priority_spec *pri_spec); + +/* + * Terminates current |session| with the |error_code|. The |reason| + * is NULL-terminated debug string. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + * NGHTTP2_ERR_INVALID_ARGUMENT + * The |reason| is too long. + */ +int nghttp2_session_terminate_session_with_reason(nghttp2_session *session, + uint32_t error_code, + const char *reason); + +#endif /* NGHTTP2_SESSION_H */ diff --git a/lib/nghttp2_stream.c b/lib/nghttp2_stream.c new file mode 100644 index 0000000..753932f --- /dev/null +++ b/lib/nghttp2_stream.c @@ -0,0 +1,1010 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "nghttp2_stream.h" + +#include +#include + +#include "nghttp2_session.h" +#include "nghttp2_helper.h" + +void nghttp2_stream_init(nghttp2_stream *stream, int32_t stream_id, + uint8_t flags, nghttp2_stream_state initial_state, + int32_t weight, nghttp2_stream_roots *roots, + int32_t remote_initial_window_size, + int32_t local_initial_window_size, + void *stream_user_data) { + nghttp2_map_entry_init(&stream->map_entry, stream_id); + stream->stream_id = stream_id; + stream->flags = flags; + stream->state = initial_state; + stream->shut_flags = NGHTTP2_SHUT_NONE; + stream->stream_user_data = stream_user_data; + stream->item = NULL; + stream->remote_window_size = remote_initial_window_size; + stream->local_window_size = local_initial_window_size; + stream->recv_window_size = 0; + stream->consumed_size = 0; + stream->recv_reduction = 0; + + stream->dep_prev = NULL; + stream->dep_next = NULL; + stream->sib_prev = NULL; + stream->sib_next = NULL; + + stream->closed_prev = NULL; + stream->closed_next = NULL; + + stream->dpri = NGHTTP2_STREAM_DPRI_NO_ITEM; + stream->num_substreams = 1; + stream->weight = weight; + stream->effective_weight = stream->weight; + stream->sum_dep_weight = 0; + stream->sum_norest_weight = 0; + + stream->roots = roots; + stream->root_prev = NULL; + stream->root_next = NULL; + + stream->http_flags = NGHTTP2_HTTP_FLAG_NONE; + stream->content_length = -1; + stream->recv_content_length = 0; + stream->status_code = -1; +} + +void nghttp2_stream_free(nghttp2_stream *stream _U_) { + /* We don't free stream->item. If it is assigned to aob, then + active_outbound_item_reset() will delete it. If it is queued, + then it is deleted when pq is deleted in nghttp2_session_del(). + Otherwise, nghttp2_session_del() will delete it. */ +} + +void nghttp2_stream_shutdown(nghttp2_stream *stream, nghttp2_shut_flag flag) { + stream->shut_flags |= flag; +} + +static int stream_push_item(nghttp2_stream *stream, nghttp2_session *session) { + /* This is required for Android NDK r10d */ + int rv = 0; + nghttp2_outbound_item *item; + + assert(stream->item); + assert(stream->item->queued == 0); + + item = stream->item; + + /* If item is now sent, don't push it to the queue. Otherwise, we + may push same item twice. */ + if (session->aob.item == item) { + return 0; + } + + switch (item->frame.hd.type) { + case NGHTTP2_DATA: + /* Penalize item by delaying scheduling according to effective + weight. This will delay low priority stream, which is good. + OTOH, this may incur delay for high priority item. Will + see. */ + item->cycle = + session->last_cycle + + NGHTTP2_DATA_PAYLOADLEN * NGHTTP2_MAX_WEIGHT / stream->effective_weight; + + rv = nghttp2_pq_push(&session->ob_da_pq, item); + if (rv != 0) { + return rv; + } + break; + case NGHTTP2_HEADERS: + if (stream->state == NGHTTP2_STREAM_RESERVED) { + nghttp2_outbound_queue_push(&session->ob_syn, item); + } else { + nghttp2_outbound_queue_push(&session->ob_reg, item); + } + break; + default: + /* should not reach here */ + assert(0); + } + + item->queued = 1; + + return 0; +} + +static nghttp2_stream *stream_first_sib(nghttp2_stream *stream) { + for (; stream->sib_prev; stream = stream->sib_prev) + ; + + return stream; +} + +static nghttp2_stream *stream_last_sib(nghttp2_stream *stream) { + for (; stream->sib_next; stream = stream->sib_next) + ; + + return stream; +} + +static nghttp2_stream *stream_update_dep_length(nghttp2_stream *stream, + ssize_t delta) { + stream->num_substreams += delta; + + stream = stream_first_sib(stream); + + if (stream->dep_prev) { + return stream_update_dep_length(stream->dep_prev, delta); + } + + return stream; +} + +int32_t nghttp2_stream_dep_distributed_weight(nghttp2_stream *stream, + int32_t weight) { + weight = stream->weight * weight / stream->sum_dep_weight; + + return nghttp2_max(1, weight); +} + +int32_t nghttp2_stream_dep_distributed_effective_weight(nghttp2_stream *stream, + int32_t weight) { + if (stream->sum_norest_weight == 0) { + return stream->effective_weight; + } + + weight = stream->effective_weight * weight / stream->sum_norest_weight; + + return nghttp2_max(1, weight); +} + +static void stream_update_dep_set_rest(nghttp2_stream *stream); + +/* Updates effective_weight of descendant streams in subtree of + |stream|. We assume that stream->effective_weight is already set + right. */ +static void stream_update_dep_effective_weight(nghttp2_stream *stream) { + nghttp2_stream *si; + + DEBUGF(fprintf(stderr, "stream: update_dep_effective_weight " + "stream(%p)=%d, weight=%d, sum_norest_weight=%d\n", + stream, stream->stream_id, stream->weight, + stream->sum_norest_weight)); + + /* stream->sum_norest_weight == 0 means there is no + NGHTTP2_STREAM_DPRI_TOP under stream */ + if (stream->dpri != NGHTTP2_STREAM_DPRI_NO_ITEM || + stream->sum_norest_weight == 0) { + return; + } + + for (si = stream->dep_next; si; si = si->sib_next) { + if (si->dpri != NGHTTP2_STREAM_DPRI_REST) { + si->effective_weight = + nghttp2_stream_dep_distributed_effective_weight(stream, si->weight); + } + + stream_update_dep_effective_weight(si); + } +} + +static void stream_update_dep_set_rest(nghttp2_stream *stream) { + if (stream == NULL) { + return; + } + + DEBUGF(fprintf(stderr, "stream: stream=%d is rest\n", stream->stream_id)); + + if (stream->dpri == NGHTTP2_STREAM_DPRI_REST) { + return; + } + + if (stream->dpri == NGHTTP2_STREAM_DPRI_TOP) { + stream->dpri = NGHTTP2_STREAM_DPRI_REST; + + stream_update_dep_set_rest(stream->sib_next); + + return; + } + + stream_update_dep_set_rest(stream->sib_next); + stream_update_dep_set_rest(stream->dep_next); +} + +/* + * Performs dfs starting |stream|, search stream which can become + * NGHTTP2_STREAM_DPRI_TOP and set its dpri. + */ +static void stream_update_dep_set_top(nghttp2_stream *stream) { + nghttp2_stream *si; + + if (stream->dpri == NGHTTP2_STREAM_DPRI_TOP) { + return; + } + + if (stream->dpri == NGHTTP2_STREAM_DPRI_REST) { + DEBUGF( + fprintf(stderr, "stream: stream=%d item is top\n", stream->stream_id)); + + stream->dpri = NGHTTP2_STREAM_DPRI_TOP; + + return; + } + + for (si = stream->dep_next; si; si = si->sib_next) { + stream_update_dep_set_top(si); + } +} + +/* + * Performs dfs starting |stream|, and dueue stream whose dpri is + * NGHTTP2_STREAM_DPRI_TOP and has not been queued yet. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + */ +static int stream_update_dep_queue_top(nghttp2_stream *stream, + nghttp2_session *session) { + int rv; + nghttp2_stream *si; + + if (stream->dpri == NGHTTP2_STREAM_DPRI_REST) { + return 0; + } + + if (stream->dpri == NGHTTP2_STREAM_DPRI_TOP) { + if (!stream->item->queued) { + DEBUGF(fprintf(stderr, "stream: stream=%d enqueue\n", stream->stream_id)); + rv = stream_push_item(stream, session); + + if (rv != 0) { + return rv; + } + } + + return 0; + } + + for (si = stream->dep_next; si; si = si->sib_next) { + rv = stream_update_dep_queue_top(si, session); + + if (rv != 0) { + return rv; + } + } + + return 0; +} + +/* + * Updates stream->sum_norest_weight recursively. We have to gather + * effective sum of weight of descendants. If stream->dpri == + * NGHTTP2_STREAM_DPRI_NO_ITEM, we have to go deeper and check that + * any of its descendants has dpri value of NGHTTP2_STREAM_DPRI_TOP. + * If so, we have to add weight of its direct descendants to + * stream->sum_norest_weight. To make this work, this function + * returns 1 if any of its descendants has dpri value of + * NGHTTP2_STREAM_DPRI_TOP, otherwise 0. + */ +static int stream_update_dep_sum_norest_weight(nghttp2_stream *stream) { + nghttp2_stream *si; + int rv; + + stream->sum_norest_weight = 0; + + if (stream->dpri == NGHTTP2_STREAM_DPRI_TOP) { + return 1; + } + + if (stream->dpri == NGHTTP2_STREAM_DPRI_REST) { + return 0; + } + + rv = 0; + + for (si = stream->dep_next; si; si = si->sib_next) { + + if (stream_update_dep_sum_norest_weight(si)) { + rv = 1; + stream->sum_norest_weight += si->weight; + } + } + + return rv; +} + +static int stream_update_dep_on_attach_item(nghttp2_stream *stream, + nghttp2_session *session) { + nghttp2_stream *root_stream; + + stream->dpri = NGHTTP2_STREAM_DPRI_REST; + + stream_update_dep_set_rest(stream->dep_next); + + root_stream = nghttp2_stream_get_dep_root(stream); + + DEBUGF(fprintf(stderr, "root=%p, stream=%p\n", root_stream, stream)); + + stream_update_dep_set_top(root_stream); + + stream_update_dep_sum_norest_weight(root_stream); + stream_update_dep_effective_weight(root_stream); + + return stream_update_dep_queue_top(root_stream, session); +} + +static int stream_update_dep_on_detach_item(nghttp2_stream *stream, + nghttp2_session *session) { + nghttp2_stream *root_stream; + + stream->dpri = NGHTTP2_STREAM_DPRI_NO_ITEM; + + root_stream = nghttp2_stream_get_dep_root(stream); + + stream_update_dep_set_top(root_stream); + + stream_update_dep_sum_norest_weight(root_stream); + stream_update_dep_effective_weight(root_stream); + + return stream_update_dep_queue_top(root_stream, session); +} + +int nghttp2_stream_attach_item(nghttp2_stream *stream, + nghttp2_outbound_item *item, + nghttp2_session *session) { + assert((stream->flags & NGHTTP2_STREAM_FLAG_DEFERRED_ALL) == 0); + assert(stream->item == NULL); + + DEBUGF(fprintf(stderr, "stream: stream=%d attach item=%p\n", + stream->stream_id, item)); + + stream->item = item; + + return stream_update_dep_on_attach_item(stream, session); +} + +int nghttp2_stream_detach_item(nghttp2_stream *stream, + nghttp2_session *session) { + DEBUGF(fprintf(stderr, "stream: stream=%d detach item=%p\n", + stream->stream_id, stream->item)); + + stream->item = NULL; + stream->flags &= ~NGHTTP2_STREAM_FLAG_DEFERRED_ALL; + + return stream_update_dep_on_detach_item(stream, session); +} + +int nghttp2_stream_defer_item(nghttp2_stream *stream, uint8_t flags, + nghttp2_session *session) { + assert(stream->item); + + DEBUGF(fprintf(stderr, "stream: stream=%d defer item=%p cause=%02x\n", + stream->stream_id, stream->item, flags)); + + stream->flags |= flags; + + return stream_update_dep_on_detach_item(stream, session); +} + +int nghttp2_stream_resume_deferred_item(nghttp2_stream *stream, uint8_t flags, + nghttp2_session *session) { + assert(stream->item); + + DEBUGF(fprintf(stderr, "stream: stream=%d resume item=%p flags=%02x\n", + stream->stream_id, stream->item, flags)); + + stream->flags &= ~flags; + + if (stream->flags & NGHTTP2_STREAM_FLAG_DEFERRED_ALL) { + return 0; + } + + return stream_update_dep_on_attach_item(stream, session); +} + +int nghttp2_stream_check_deferred_item(nghttp2_stream *stream) { + return stream->item && (stream->flags & NGHTTP2_STREAM_FLAG_DEFERRED_ALL); +} + +int nghttp2_stream_check_deferred_by_flow_control(nghttp2_stream *stream) { + return stream->item && + (stream->flags & NGHTTP2_STREAM_FLAG_DEFERRED_FLOW_CONTROL); +} + +static int update_initial_window_size(int32_t *window_size_ptr, + int32_t new_initial_window_size, + int32_t old_initial_window_size) { + int64_t new_window_size = (int64_t)(*window_size_ptr) + + new_initial_window_size - old_initial_window_size; + if (INT32_MIN > new_window_size || + new_window_size > NGHTTP2_MAX_WINDOW_SIZE) { + return -1; + } + *window_size_ptr = (int32_t)new_window_size; + return 0; +} + +int nghttp2_stream_update_remote_initial_window_size( + nghttp2_stream *stream, int32_t new_initial_window_size, + int32_t old_initial_window_size) { + return update_initial_window_size(&stream->remote_window_size, + new_initial_window_size, + old_initial_window_size); +} + +int nghttp2_stream_update_local_initial_window_size( + nghttp2_stream *stream, int32_t new_initial_window_size, + int32_t old_initial_window_size) { + return update_initial_window_size(&stream->local_window_size, + new_initial_window_size, + old_initial_window_size); +} + +void nghttp2_stream_promise_fulfilled(nghttp2_stream *stream) { + stream->state = NGHTTP2_STREAM_OPENED; + stream->flags &= ~NGHTTP2_STREAM_FLAG_PUSH; +} + +nghttp2_stream *nghttp2_stream_get_dep_root(nghttp2_stream *stream) { + for (;;) { + if (stream->sib_prev) { + stream = stream->sib_prev; + + continue; + } + + if (stream->dep_prev) { + stream = stream->dep_prev; + + continue; + } + + break; + } + + return stream; +} + +int nghttp2_stream_dep_subtree_find(nghttp2_stream *stream, + nghttp2_stream *target) { + if (stream == NULL) { + return 0; + } + + if (stream == target) { + return 1; + } + + if (nghttp2_stream_dep_subtree_find(stream->sib_next, target)) { + return 1; + } + + return nghttp2_stream_dep_subtree_find(stream->dep_next, target); +} + +void nghttp2_stream_dep_insert(nghttp2_stream *dep_stream, + nghttp2_stream *stream) { + nghttp2_stream *si; + nghttp2_stream *root_stream; + + assert(stream->item == NULL); + + DEBUGF(fprintf(stderr, + "stream: dep_insert dep_stream(%p)=%d, stream(%p)=%d\n", + dep_stream, dep_stream->stream_id, stream, stream->stream_id)); + + stream->sum_dep_weight = dep_stream->sum_dep_weight; + dep_stream->sum_dep_weight = stream->weight; + + if (dep_stream->dep_next) { + for (si = dep_stream->dep_next; si; si = si->sib_next) { + stream->num_substreams += si->num_substreams; + } + + stream->dep_next = dep_stream->dep_next; + stream->dep_next->dep_prev = stream; + } + + dep_stream->dep_next = stream; + stream->dep_prev = dep_stream; + + root_stream = stream_update_dep_length(dep_stream, 1); + + stream_update_dep_sum_norest_weight(root_stream); + stream_update_dep_effective_weight(root_stream); + + ++stream->roots->num_streams; +} + +static void link_dep(nghttp2_stream *dep_stream, nghttp2_stream *stream) { + dep_stream->dep_next = stream; + stream->dep_prev = dep_stream; +} + +static void link_sib(nghttp2_stream *prev_stream, nghttp2_stream *stream) { + prev_stream->sib_next = stream; + stream->sib_prev = prev_stream; +} + +static void insert_link_dep(nghttp2_stream *dep_stream, + nghttp2_stream *stream) { + nghttp2_stream *sib_next; + + assert(stream->sib_prev == NULL); + + sib_next = dep_stream->dep_next; + + link_sib(stream, sib_next); + + sib_next->dep_prev = NULL; + + link_dep(dep_stream, stream); +} + +static void unlink_sib(nghttp2_stream *stream) { + nghttp2_stream *prev, *next, *dep_next; + + prev = stream->sib_prev; + dep_next = stream->dep_next; + + assert(prev); + + if (dep_next) { + /* + * prev--stream(--sib_next--...) + * | + * dep_next + */ + dep_next->dep_prev = NULL; + + link_sib(prev, dep_next); + + if (stream->sib_next) { + link_sib(stream_last_sib(dep_next), stream->sib_next); + } + } else { + /* + * prev--stream(--sib_next--...) + */ + next = stream->sib_next; + + prev->sib_next = next; + + if (next) { + next->sib_prev = prev; + } + } +} + +static void unlink_dep(nghttp2_stream *stream) { + nghttp2_stream *prev, *next, *dep_next; + + prev = stream->dep_prev; + dep_next = stream->dep_next; + + assert(prev); + + if (dep_next) { + /* + * prev + * | + * stream(--sib_next--...) + * | + * dep_next + */ + link_dep(prev, dep_next); + + if (stream->sib_next) { + link_sib(stream_last_sib(dep_next), stream->sib_next); + } + } else if (stream->sib_next) { + /* + * prev + * | + * stream--sib_next + */ + next = stream->sib_next; + + next->sib_prev = NULL; + + link_dep(prev, next); + } else { + prev->dep_next = NULL; + } +} + +void nghttp2_stream_dep_add(nghttp2_stream *dep_stream, + nghttp2_stream *stream) { + nghttp2_stream *root_stream; + + assert(stream->item == NULL); + + DEBUGF(fprintf(stderr, "stream: dep_add dep_stream(%p)=%d, stream(%p)=%d\n", + dep_stream, dep_stream->stream_id, stream, stream->stream_id)); + + root_stream = stream_update_dep_length(dep_stream, 1); + + dep_stream->sum_dep_weight += stream->weight; + + if (dep_stream->dep_next == NULL) { + link_dep(dep_stream, stream); + } else { + insert_link_dep(dep_stream, stream); + } + + stream_update_dep_sum_norest_weight(root_stream); + stream_update_dep_effective_weight(root_stream); + + ++stream->roots->num_streams; +} + +void nghttp2_stream_dep_remove(nghttp2_stream *stream) { + nghttp2_stream *prev, *next, *dep_prev, *si, *root_stream; + int32_t sum_dep_weight_delta; + + root_stream = NULL; + + DEBUGF(fprintf(stderr, "stream: dep_remove stream(%p)=%d\n", stream, + stream->stream_id)); + + /* Distribute weight of |stream| to direct descendants */ + sum_dep_weight_delta = -stream->weight; + + for (si = stream->dep_next; si; si = si->sib_next) { + si->weight = nghttp2_stream_dep_distributed_weight(stream, si->weight); + + sum_dep_weight_delta += si->weight; + } + + prev = stream_first_sib(stream); + + dep_prev = prev->dep_prev; + + if (dep_prev) { + root_stream = stream_update_dep_length(dep_prev, -1); + + dep_prev->sum_dep_weight += sum_dep_weight_delta; + } + + if (stream->sib_prev) { + unlink_sib(stream); + } else if (stream->dep_prev) { + unlink_dep(stream); + } else { + nghttp2_stream_roots_remove(stream->roots, stream); + + /* stream is a root of tree. Removing stream makes its + descendants a root of its own subtree. */ + + for (si = stream->dep_next; si;) { + next = si->sib_next; + + si->dep_prev = NULL; + si->sib_prev = NULL; + si->sib_next = NULL; + + /* We already distributed weight of |stream| to this. */ + si->effective_weight = si->weight; + + nghttp2_stream_roots_add(si->roots, si); + + si = next; + } + } + + if (root_stream) { + stream_update_dep_sum_norest_weight(root_stream); + stream_update_dep_effective_weight(root_stream); + } + + stream->num_substreams = 1; + stream->sum_dep_weight = 0; + + stream->dep_prev = NULL; + stream->dep_next = NULL; + stream->sib_prev = NULL; + stream->sib_next = NULL; + + --stream->roots->num_streams; +} + +int nghttp2_stream_dep_insert_subtree(nghttp2_stream *dep_stream, + nghttp2_stream *stream, + nghttp2_session *session) { + nghttp2_stream *last_sib; + nghttp2_stream *dep_next; + nghttp2_stream *root_stream; + size_t delta_substreams; + + DEBUGF(fprintf(stderr, "stream: dep_insert_subtree dep_stream(%p)=%d " + "stream(%p)=%d\n", + dep_stream, dep_stream->stream_id, stream, stream->stream_id)); + + delta_substreams = stream->num_substreams; + + stream_update_dep_set_rest(stream); + + if (dep_stream->dep_next) { + /* dep_stream->num_substreams includes dep_stream itself */ + stream->num_substreams += dep_stream->num_substreams - 1; + + stream->sum_dep_weight += dep_stream->sum_dep_weight; + dep_stream->sum_dep_weight = stream->weight; + + dep_next = dep_stream->dep_next; + + stream_update_dep_set_rest(dep_next); + + link_dep(dep_stream, stream); + + if (stream->dep_next) { + last_sib = stream_last_sib(stream->dep_next); + + link_sib(last_sib, dep_next); + + dep_next->dep_prev = NULL; + } else { + link_dep(stream, dep_next); + } + } else { + link_dep(dep_stream, stream); + + assert(dep_stream->sum_dep_weight == 0); + dep_stream->sum_dep_weight = stream->weight; + } + + root_stream = stream_update_dep_length(dep_stream, delta_substreams); + + stream_update_dep_set_top(root_stream); + + stream_update_dep_sum_norest_weight(root_stream); + stream_update_dep_effective_weight(root_stream); + + return stream_update_dep_queue_top(root_stream, session); +} + +int nghttp2_stream_dep_add_subtree(nghttp2_stream *dep_stream, + nghttp2_stream *stream, + nghttp2_session *session) { + nghttp2_stream *root_stream; + + DEBUGF(fprintf(stderr, "stream: dep_add_subtree dep_stream(%p)=%d " + "stream(%p)=%d\n", + dep_stream, dep_stream->stream_id, stream, stream->stream_id)); + + stream_update_dep_set_rest(stream); + + if (dep_stream->dep_next) { + dep_stream->sum_dep_weight += stream->weight; + + insert_link_dep(dep_stream, stream); + } else { + link_dep(dep_stream, stream); + + assert(dep_stream->sum_dep_weight == 0); + dep_stream->sum_dep_weight = stream->weight; + } + + root_stream = stream_update_dep_length(dep_stream, stream->num_substreams); + + stream_update_dep_set_top(root_stream); + + stream_update_dep_sum_norest_weight(root_stream); + stream_update_dep_effective_weight(root_stream); + + return stream_update_dep_queue_top(root_stream, session); +} + +void nghttp2_stream_dep_remove_subtree(nghttp2_stream *stream) { + nghttp2_stream *prev, *next, *dep_prev, *root_stream; + + DEBUGF(fprintf(stderr, "stream: dep_remove_subtree stream(%p)=%d\n", stream, + stream->stream_id)); + + if (stream->sib_prev) { + prev = stream->sib_prev; + + prev->sib_next = stream->sib_next; + if (prev->sib_next) { + prev->sib_next->sib_prev = prev; + } + + prev = stream_first_sib(prev); + + dep_prev = prev->dep_prev; + + } else if (stream->dep_prev) { + dep_prev = stream->dep_prev; + next = stream->sib_next; + + dep_prev->dep_next = next; + + if (next) { + next->dep_prev = dep_prev; + + next->sib_prev = NULL; + } + + } else { + nghttp2_stream_roots_remove(stream->roots, stream); + + dep_prev = NULL; + } + + if (dep_prev) { + dep_prev->sum_dep_weight -= stream->weight; + + root_stream = stream_update_dep_length(dep_prev, -stream->num_substreams); + + stream_update_dep_sum_norest_weight(root_stream); + stream_update_dep_effective_weight(root_stream); + } + + stream->sib_prev = NULL; + stream->sib_next = NULL; + stream->dep_prev = NULL; +} + +int nghttp2_stream_dep_make_root(nghttp2_stream *stream, + nghttp2_session *session) { + DEBUGF(fprintf(stderr, "stream: dep_make_root stream(%p)=%d\n", stream, + stream->stream_id)); + + nghttp2_stream_roots_add(stream->roots, stream); + + stream_update_dep_set_rest(stream); + + stream->effective_weight = stream->weight; + + stream_update_dep_set_top(stream); + + stream_update_dep_sum_norest_weight(stream); + stream_update_dep_effective_weight(stream); + + return stream_update_dep_queue_top(stream, session); +} + +int +nghttp2_stream_dep_all_your_stream_are_belong_to_us(nghttp2_stream *stream, + nghttp2_session *session) { + nghttp2_stream *first, *si; + + DEBUGF(fprintf(stderr, "stream: ALL YOUR STREAM ARE BELONG TO US " + "stream(%p)=%d\n", + stream, stream->stream_id)); + + first = stream->roots->head; + + /* stream must not be include in stream->roots->head list */ + assert(first != stream); + + if (first) { + nghttp2_stream *prev; + + prev = first; + + DEBUGF(fprintf(stderr, "stream: root stream(%p)=%d\n", first, + first->stream_id)); + + stream->sum_dep_weight += first->weight; + stream->num_substreams += first->num_substreams; + + for (si = first->root_next; si; si = si->root_next) { + + assert(si != stream); + + DEBUGF( + fprintf(stderr, "stream: root stream(%p)=%d\n", si, si->stream_id)); + + stream->sum_dep_weight += si->weight; + stream->num_substreams += si->num_substreams; + + link_sib(prev, si); + + prev = si; + } + + if (stream->dep_next) { + nghttp2_stream *sib_next; + + sib_next = stream->dep_next; + + sib_next->dep_prev = NULL; + + link_sib(first, sib_next); + link_dep(stream, prev); + } else { + link_dep(stream, first); + } + } + + nghttp2_stream_roots_remove_all(stream->roots); + + return nghttp2_stream_dep_make_root(stream, session); +} + +int nghttp2_stream_in_dep_tree(nghttp2_stream *stream) { + return stream->dep_prev || stream->dep_next || stream->sib_prev || + stream->sib_next || stream->root_next || stream->root_prev || + stream->roots->head == stream; +} + +void nghttp2_stream_roots_init(nghttp2_stream_roots *roots) { + roots->head = NULL; + roots->num_streams = 0; +} + +void nghttp2_stream_roots_free(nghttp2_stream_roots *roots _U_) {} + +void nghttp2_stream_roots_add(nghttp2_stream_roots *roots, + nghttp2_stream *stream) { + if (roots->head) { + stream->root_next = roots->head; + roots->head->root_prev = stream; + } + + roots->head = stream; +} + +void nghttp2_stream_roots_remove(nghttp2_stream_roots *roots, + nghttp2_stream *stream) { + nghttp2_stream *root_prev, *root_next; + + root_prev = stream->root_prev; + root_next = stream->root_next; + + if (root_prev) { + root_prev->root_next = root_next; + + if (root_next) { + root_next->root_prev = root_prev; + } + } else { + if (root_next) { + root_next->root_prev = NULL; + } + + roots->head = root_next; + } + + stream->root_prev = NULL; + stream->root_next = NULL; +} + +void nghttp2_stream_roots_remove_all(nghttp2_stream_roots *roots) { + nghttp2_stream *si, *next; + + for (si = roots->head; si;) { + next = si->root_next; + + si->root_prev = NULL; + si->root_next = NULL; + + si = next; + } + + roots->head = NULL; +} diff --git a/lib/nghttp2_stream.h b/lib/nghttp2_stream.h new file mode 100644 index 0000000..684459b --- /dev/null +++ b/lib/nghttp2_stream.h @@ -0,0 +1,486 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGHTTP2_STREAM_H +#define NGHTTP2_STREAM_H + +#ifdef HAVE_CONFIG_H +#include +#endif /* HAVE_CONFIG_H */ + +#include +#include "nghttp2_outbound_item.h" +#include "nghttp2_map.h" +#include "nghttp2_pq.h" +#include "nghttp2_int.h" + +/* + * Maximum number of streams in one dependency tree. + */ +#define NGHTTP2_MAX_DEP_TREE_LENGTH 100 + +/* + * If local peer is stream initiator: + * NGHTTP2_STREAM_OPENING : upon sending request HEADERS + * NGHTTP2_STREAM_OPENED : upon receiving response HEADERS + * NGHTTP2_STREAM_CLOSING : upon queuing RST_STREAM + * + * If remote peer is stream initiator: + * NGHTTP2_STREAM_OPENING : upon receiving request HEADERS + * NGHTTP2_STREAM_OPENED : upon sending response HEADERS + * NGHTTP2_STREAM_CLOSING : upon queuing RST_STREAM + */ +typedef enum { + /* Initial state */ + NGHTTP2_STREAM_INITIAL, + /* For stream initiator: request HEADERS has been sent, but response + HEADERS has not been received yet. For receiver: request HEADERS + has been received, but it does not send response HEADERS yet. */ + NGHTTP2_STREAM_OPENING, + /* For stream initiator: response HEADERS is received. For receiver: + response HEADERS is sent. */ + NGHTTP2_STREAM_OPENED, + /* RST_STREAM is received, but somehow we need to keep stream in + memory. */ + NGHTTP2_STREAM_CLOSING, + /* PUSH_PROMISE is received or sent */ + NGHTTP2_STREAM_RESERVED, + /* Stream is created in this state if it is used as anchor in + dependency tree. */ + NGHTTP2_STREAM_IDLE +} nghttp2_stream_state; + +typedef enum { + NGHTTP2_SHUT_NONE = 0, + /* Indicates further receptions will be disallowed. */ + NGHTTP2_SHUT_RD = 0x01, + /* Indicates further transmissions will be disallowed. */ + NGHTTP2_SHUT_WR = 0x02, + /* Indicates both further receptions and transmissions will be + disallowed. */ + NGHTTP2_SHUT_RDWR = NGHTTP2_SHUT_RD | NGHTTP2_SHUT_WR +} nghttp2_shut_flag; + +typedef enum { + NGHTTP2_STREAM_FLAG_NONE = 0, + /* Indicates that this stream is pushed stream and not opened + yet. */ + NGHTTP2_STREAM_FLAG_PUSH = 0x01, + /* Indicates that this stream was closed */ + NGHTTP2_STREAM_FLAG_CLOSED = 0x02, + /* Indicates the item is deferred due to flow control. */ + NGHTTP2_STREAM_FLAG_DEFERRED_FLOW_CONTROL = 0x04, + /* Indicates the item is deferred by user callback */ + NGHTTP2_STREAM_FLAG_DEFERRED_USER = 0x08, + /* bitwise OR of NGHTTP2_STREAM_FLAG_DEFERRED_FLOW_CONTROL and + NGHTTP2_STREAM_FLAG_DEFERRED_USER. */ + NGHTTP2_STREAM_FLAG_DEFERRED_ALL = 0x0c + +} nghttp2_stream_flag; + +/* HTTP related flags to enforce HTTP semantics */ +typedef enum { + NGHTTP2_HTTP_FLAG_NONE = 0, + /* header field seen so far */ + NGHTTP2_HTTP_FLAG__AUTHORITY = 1, + NGHTTP2_HTTP_FLAG__PATH = 1 << 1, + NGHTTP2_HTTP_FLAG__METHOD = 1 << 2, + NGHTTP2_HTTP_FLAG__SCHEME = 1 << 3, + /* host is not pseudo header, but we require either host or + :authority */ + NGHTTP2_HTTP_FLAG_HOST = 1 << 4, + NGHTTP2_HTTP_FLAG__STATUS = 1 << 5, + /* required header fields for HTTP request except for CONNECT + method. */ + NGHTTP2_HTTP_FLAG_REQ_HEADERS = NGHTTP2_HTTP_FLAG__METHOD | + NGHTTP2_HTTP_FLAG__PATH | + NGHTTP2_HTTP_FLAG__SCHEME, + NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED = 1 << 6, + /* HTTP method flags */ + NGHTTP2_HTTP_FLAG_METH_CONNECT = 1 << 7, + NGHTTP2_HTTP_FLAG_METH_HEAD = 1 << 8, + NGHTTP2_HTTP_FLAG_METH_OPTIONS = 1 << 9, + NGHTTP2_HTTP_FLAG_METH_ALL = NGHTTP2_HTTP_FLAG_METH_CONNECT | + NGHTTP2_HTTP_FLAG_METH_HEAD | + NGHTTP2_HTTP_FLAG_METH_OPTIONS, + /* :path category */ + /* path starts with "/" */ + NGHTTP2_HTTP_FLAG_PATH_REGULAR = 1 << 10, + /* path "*" */ + NGHTTP2_HTTP_FLAG_PATH_ASTERISK = 1 << 11, + /* scheme */ + /* "http" or "https" scheme */ + NGHTTP2_HTTP_FLAG_SCHEME_HTTP = 1 << 12, + /* set if final response is expected */ + NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE = 1 << 13 +} nghttp2_http_flag; + +typedef enum { + NGHTTP2_STREAM_DPRI_NONE = 0, + NGHTTP2_STREAM_DPRI_NO_ITEM = 0x01, + NGHTTP2_STREAM_DPRI_TOP = 0x02, + NGHTTP2_STREAM_DPRI_REST = 0x04 +} nghttp2_stream_dpri; + +struct nghttp2_stream_roots; + +typedef struct nghttp2_stream_roots nghttp2_stream_roots; + +struct nghttp2_stream; + +typedef struct nghttp2_stream nghttp2_stream; + +struct nghttp2_stream { + /* Intrusive Map */ + nghttp2_map_entry map_entry; + /* Content-Length of request/response body. -1 if unknown. */ + int64_t content_length; + /* Received body so far */ + int64_t recv_content_length; + /* pointers to form dependency tree. If multiple streams depend on + a stream, only one stream (left most) has non-NULL dep_prev which + points to the stream it depends on. The remaining streams are + linked using sib_prev and sib_next. The stream which has + non-NULL dep_prev always NULL sib_prev. The right most stream + has NULL sib_next. If this stream is a root of dependency tree, + dep_prev and sib_prev are NULL. */ + nghttp2_stream *dep_prev, *dep_next; + nghttp2_stream *sib_prev, *sib_next; + /* pointers to track dependency tree root streams. This is + doubly-linked list and first element is pointed by + roots->head. */ + nghttp2_stream *root_prev, *root_next; + /* When stream is kept after closure, it may be kept in doubly + linked list pointed by nghttp2_session closed_stream_head. + closed_next points to the next stream object if it is the element + of the list. */ + nghttp2_stream *closed_prev, *closed_next; + /* pointer to roots, which tracks dependency tree roots */ + nghttp2_stream_roots *roots; + /* The arbitrary data provided by user for this stream. */ + void *stream_user_data; + /* Item to send */ + nghttp2_outbound_item *item; + /* stream ID */ + int32_t stream_id; + /* categorized priority of this stream. Only stream bearing + NGHTTP2_STREAM_DPRI_TOP can send item. */ + nghttp2_stream_dpri dpri; + /* the number of streams in subtree */ + size_t num_substreams; + /* Current remote window size. This value is computed against the + current initial window size of remote endpoint. */ + int32_t remote_window_size; + /* Keep track of the number of bytes received without + WINDOW_UPDATE. This could be negative after submitting negative + value to WINDOW_UPDATE */ + int32_t recv_window_size; + /* The number of bytes consumed by the application and now is + subject to WINDOW_UPDATE. This is only used when auto + WINDOW_UPDATE is turned off. */ + int32_t consumed_size; + /* The amount of recv_window_size cut using submitting negative + value to WINDOW_UPDATE */ + int32_t recv_reduction; + /* window size for local flow control. It is initially set to + NGHTTP2_INITIAL_WINDOW_SIZE and could be increased/decreased by + submitting WINDOW_UPDATE. See nghttp2_submit_window_update(). */ + int32_t local_window_size; + /* weight of this stream */ + int32_t weight; + /* effective weight of this stream in belonging dependency tree */ + int32_t effective_weight; + /* sum of weight (not effective_weight) of direct descendants */ + int32_t sum_dep_weight; + /* sum of weight of direct descendants which have at least one + descendant with dpri == NGHTTP2_STREAM_DPRI_TOP. We use this + value to calculate effective weight. */ + int32_t sum_norest_weight; + nghttp2_stream_state state; + /* status code from remote server */ + int16_t status_code; + /* Bitwise OR of zero or more nghttp2_http_flag values */ + uint16_t http_flags; + /* This is bitwise-OR of 0 or more of nghttp2_stream_flag. */ + uint8_t flags; + /* Bitwise OR of zero or more nghttp2_shut_flag values */ + uint8_t shut_flags; +}; + +void nghttp2_stream_init(nghttp2_stream *stream, int32_t stream_id, + uint8_t flags, nghttp2_stream_state initial_state, + int32_t weight, nghttp2_stream_roots *roots, + int32_t remote_initial_window_size, + int32_t local_initial_window_size, + void *stream_user_data); + +void nghttp2_stream_free(nghttp2_stream *stream); + +/* + * Disallow either further receptions or transmissions, or both. + * |flag| is bitwise OR of one or more of nghttp2_shut_flag. + */ +void nghttp2_stream_shutdown(nghttp2_stream *stream, nghttp2_shut_flag flag); + +/* + * Defer |stream->item|. We won't call this function in the situation + * where |stream->item| == NULL. The |flags| is bitwise OR of zero or + * more of NGHTTP2_STREAM_FLAG_DEFERRED_USER and + * NGHTTP2_STREAM_FLAG_DEFERRED_FLOW_CONTROL. The |flags| indicates + * the reason of this action. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory + */ +int nghttp2_stream_defer_item(nghttp2_stream *stream, uint8_t flags, + nghttp2_session *session); + +/* + * Put back deferred data in this stream to active state. The |flags| + * are one or more of bitwise OR of the following values: + * NGHTTP2_STREAM_FLAG_DEFERRED_USER and + * NGHTTP2_STREAM_FLAG_DEFERRED_FLOW_CONTROL and given masks are + * cleared if they are set. So even if this function is called, if + * one of flag is still set, data does not become active. + */ +int nghttp2_stream_resume_deferred_item(nghttp2_stream *stream, uint8_t flags, + nghttp2_session *session); + +/* + * Returns nonzero if item is deferred by whatever reason. + */ +int nghttp2_stream_check_deferred_item(nghttp2_stream *stream); + +/* + * Returns nonzero if item is deferred by flow control. + */ +int nghttp2_stream_check_deferred_by_flow_control(nghttp2_stream *stream); + +/* + * Updates the remote window size with the new value + * |new_initial_window_size|. The |old_initial_window_size| is used to + * calculate the current window size. + * + * This function returns 0 if it succeeds or -1. The failure is due to + * overflow. + */ +int nghttp2_stream_update_remote_initial_window_size( + nghttp2_stream *stream, int32_t new_initial_window_size, + int32_t old_initial_window_size); + +/* + * Updates the local window size with the new value + * |new_initial_window_size|. The |old_initial_window_size| is used to + * calculate the current window size. + * + * This function returns 0 if it succeeds or -1. The failure is due to + * overflow. + */ +int nghttp2_stream_update_local_initial_window_size( + nghttp2_stream *stream, int32_t new_initial_window_size, + int32_t old_initial_window_size); + +/* + * Call this function if promised stream |stream| is replied with + * HEADERS. This function makes the state of the |stream| to + * NGHTTP2_STREAM_OPENED. + */ +void nghttp2_stream_promise_fulfilled(nghttp2_stream *stream); + +/* + * Returns the stream positioned in root of the dependency tree the + * |stream| belongs to. + */ +nghttp2_stream *nghttp2_stream_get_dep_root(nghttp2_stream *stream); + +/* + * Returns nonzero if |target| is found in subtree of |stream|. + */ +int nghttp2_stream_dep_subtree_find(nghttp2_stream *stream, + nghttp2_stream *target); + +/* + * Computes distributed weight of a stream of the |weight| under the + * |stream| if |stream| is removed from a dependency tree. The result + * is computed using stream->weight rather than + * stream->effective_weight. + */ +int32_t nghttp2_stream_dep_distributed_weight(nghttp2_stream *stream, + int32_t weight); + +/* + * Computes effective weight of a stream of the |weight| under the + * |stream|. The result is computed using stream->effective_weight + * rather than stream->weight. This function is used to determine + * weight in dependency tree. + */ +int32_t nghttp2_stream_dep_distributed_effective_weight(nghttp2_stream *stream, + int32_t weight); + +/* + * Makes the |stream| depend on the |dep_stream|. This dependency is + * exclusive. All existing direct descendants of |dep_stream| become + * the descendants of the |stream|. This function assumes + * |stream->data| is NULL and no dpri members are changed in this + * dependency tree. + */ +void nghttp2_stream_dep_insert(nghttp2_stream *dep_stream, + nghttp2_stream *stream); + +/* + * Makes the |stream| depend on the |dep_stream|. This dependency is + * not exclusive. This function assumes |stream->data| is NULL and no + * dpri members are changed in this dependency tree. + */ +void nghttp2_stream_dep_add(nghttp2_stream *dep_stream, nghttp2_stream *stream); + +/* + * Removes the |stream| from the current dependency tree. This + * function assumes |stream->data| is NULL. + */ +void nghttp2_stream_dep_remove(nghttp2_stream *stream); + +/* + * Attaches |item| to |stream|. Updates dpri members in this + * dependency tree. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory + */ +int nghttp2_stream_attach_item(nghttp2_stream *stream, + nghttp2_outbound_item *item, + nghttp2_session *session); + +/* + * Detaches |stream->item|. Updates dpri members in this dependency + * tree. This function does not free |stream->item|. The caller must + * free it. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory + */ +int nghttp2_stream_detach_item(nghttp2_stream *stream, + nghttp2_session *session); + +/* + * Makes the |stream| depend on the |dep_stream|. This dependency is + * exclusive. Updates dpri members in this dependency tree. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory + */ +int nghttp2_stream_dep_insert_subtree(nghttp2_stream *dep_stream, + nghttp2_stream *stream, + nghttp2_session *session); + +/* + * Makes the |stream| depend on the |dep_stream|. This dependency is + * not exclusive. Updates dpri members in this dependency tree. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory + */ +int nghttp2_stream_dep_add_subtree(nghttp2_stream *dep_stream, + nghttp2_stream *stream, + nghttp2_session *session); + +/* + * Removes subtree whose root stream is |stream|. Removing subtree + * does not change dpri values. The effective_weight of streams in + * removed subtree is not updated. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory + */ +void nghttp2_stream_dep_remove_subtree(nghttp2_stream *stream); + +/* + * Makes the |stream| as root. Updates dpri members in this + * dependency tree. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory + */ +int nghttp2_stream_dep_make_root(nghttp2_stream *stream, + nghttp2_session *session); + +/* + * Makes the |stream| as root and all existing root streams become + * direct children of |stream|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory + */ +int +nghttp2_stream_dep_all_your_stream_are_belong_to_us(nghttp2_stream *stream, + nghttp2_session *session); + +/* + * Returns nonzero if |stream| is in any dependency tree. + */ +int nghttp2_stream_in_dep_tree(nghttp2_stream *stream); + +struct nghttp2_stream_roots { + nghttp2_stream *head; + + int32_t num_streams; +}; + +void nghttp2_stream_roots_init(nghttp2_stream_roots *roots); + +void nghttp2_stream_roots_free(nghttp2_stream_roots *roots); + +void nghttp2_stream_roots_add(nghttp2_stream_roots *roots, + nghttp2_stream *stream); + +void nghttp2_stream_roots_remove(nghttp2_stream_roots *roots, + nghttp2_stream *stream); + +void nghttp2_stream_roots_remove_all(nghttp2_stream_roots *roots); + +#endif /* NGHTTP2_STREAM */ diff --git a/lib/nghttp2_submit.c b/lib/nghttp2_submit.c new file mode 100644 index 0000000..0f6d9f5 --- /dev/null +++ b/lib/nghttp2_submit.c @@ -0,0 +1,479 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012, 2013 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "nghttp2_submit.h" + +#include +#include + +#include "nghttp2_session.h" +#include "nghttp2_frame.h" +#include "nghttp2_helper.h" +#include "nghttp2_priority_spec.h" + +/* This function takes ownership of |nva_copy|. Regardless of the + return value, the caller must not free |nva_copy| after this + function returns. */ +static int32_t submit_headers_shared(nghttp2_session *session, uint8_t flags, + int32_t stream_id, + const nghttp2_priority_spec *pri_spec, + nghttp2_nv *nva_copy, size_t nvlen, + const nghttp2_data_provider *data_prd, + void *stream_user_data, + uint8_t attach_stream) { + int rv; + uint8_t flags_copy; + nghttp2_outbound_item *item = NULL; + nghttp2_frame *frame = NULL; + nghttp2_headers_category hcat; + nghttp2_mem *mem; + + mem = &session->mem; + + if (stream_id == 0) { + rv = NGHTTP2_ERR_INVALID_ARGUMENT; + goto fail; + } + + item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item)); + if (item == NULL) { + rv = NGHTTP2_ERR_NOMEM; + goto fail; + } + + nghttp2_outbound_item_init(item); + + if (data_prd != NULL && data_prd->read_callback != NULL) { + item->aux_data.headers.data_prd = *data_prd; + } + + item->aux_data.headers.stream_user_data = stream_user_data; + item->aux_data.headers.attach_stream = attach_stream; + + flags_copy = (flags & (NGHTTP2_FLAG_END_STREAM | NGHTTP2_FLAG_PRIORITY)) | + NGHTTP2_FLAG_END_HEADERS; + + if (stream_id == -1) { + if (session->next_stream_id > INT32_MAX) { + rv = NGHTTP2_ERR_STREAM_ID_NOT_AVAILABLE; + goto fail; + } + + stream_id = session->next_stream_id; + session->next_stream_id += 2; + + hcat = NGHTTP2_HCAT_REQUEST; + } else { + /* More specific categorization will be done later. */ + hcat = NGHTTP2_HCAT_HEADERS; + } + + frame = &item->frame; + + nghttp2_frame_headers_init(&frame->headers, flags_copy, stream_id, hcat, + pri_spec, nva_copy, nvlen); + + rv = nghttp2_session_add_item(session, item); + + if (rv != 0) { + nghttp2_frame_headers_free(&frame->headers, mem); + goto fail2; + } + + if (hcat == NGHTTP2_HCAT_REQUEST) { + return stream_id; + } + + return 0; + +fail: + /* nghttp2_frame_headers_init() takes ownership of nva_copy. */ + nghttp2_nv_array_del(nva_copy, mem); +fail2: + nghttp2_mem_free(mem, item); + + return rv; +} + +static void adjust_priority_spec_weight(nghttp2_priority_spec *pri_spec) { + if (pri_spec->weight < NGHTTP2_MIN_WEIGHT) { + pri_spec->weight = NGHTTP2_MIN_WEIGHT; + } else if (pri_spec->weight > NGHTTP2_MAX_WEIGHT) { + pri_spec->weight = NGHTTP2_MAX_WEIGHT; + } +} + +static int32_t submit_headers_shared_nva(nghttp2_session *session, + uint8_t flags, int32_t stream_id, + const nghttp2_priority_spec *pri_spec, + const nghttp2_nv *nva, size_t nvlen, + const nghttp2_data_provider *data_prd, + void *stream_user_data, + uint8_t attach_stream) { + int rv; + nghttp2_nv *nva_copy; + nghttp2_priority_spec copy_pri_spec; + nghttp2_mem *mem; + + mem = &session->mem; + + if (pri_spec) { + copy_pri_spec = *pri_spec; + adjust_priority_spec_weight(©_pri_spec); + } else { + nghttp2_priority_spec_default_init(©_pri_spec); + } + + rv = nghttp2_nv_array_copy(&nva_copy, nva, nvlen, mem); + if (rv < 0) { + return rv; + } + + return submit_headers_shared(session, flags, stream_id, ©_pri_spec, + nva_copy, nvlen, data_prd, stream_user_data, + attach_stream); +} + +int32_t nghttp2_submit_trailer(nghttp2_session *session, int32_t stream_id, + const nghttp2_nv *nva, size_t nvlen) { + return submit_headers_shared_nva(session, NGHTTP2_FLAG_END_STREAM, stream_id, + NULL, nva, nvlen, NULL, NULL, 0); +} + +int32_t nghttp2_submit_headers(nghttp2_session *session, uint8_t flags, + int32_t stream_id, + const nghttp2_priority_spec *pri_spec, + const nghttp2_nv *nva, size_t nvlen, + void *stream_user_data) { + flags &= NGHTTP2_FLAG_END_STREAM; + + if (pri_spec && !nghttp2_priority_spec_check_default(pri_spec)) { + flags |= NGHTTP2_FLAG_PRIORITY; + } else { + pri_spec = NULL; + } + + return submit_headers_shared_nva(session, flags, stream_id, pri_spec, nva, + nvlen, NULL, stream_user_data, 0); +} + +int nghttp2_submit_ping(nghttp2_session *session, uint8_t flags _U_, + const uint8_t *opaque_data) { + return nghttp2_session_add_ping(session, NGHTTP2_FLAG_NONE, opaque_data); +} + +int nghttp2_submit_priority(nghttp2_session *session, uint8_t flags _U_, + int32_t stream_id, + const nghttp2_priority_spec *pri_spec) { + int rv; + nghttp2_outbound_item *item; + nghttp2_frame *frame; + nghttp2_priority_spec copy_pri_spec; + nghttp2_mem *mem; + + mem = &session->mem; + + if (stream_id == 0 || pri_spec == NULL) { + return NGHTTP2_ERR_INVALID_ARGUMENT; + } + + if (stream_id == pri_spec->stream_id) { + return NGHTTP2_ERR_INVALID_ARGUMENT; + } + + copy_pri_spec = *pri_spec; + + adjust_priority_spec_weight(©_pri_spec); + + item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item)); + + if (item == NULL) { + return NGHTTP2_ERR_NOMEM; + } + + nghttp2_outbound_item_init(item); + + frame = &item->frame; + + nghttp2_frame_priority_init(&frame->priority, stream_id, ©_pri_spec); + + rv = nghttp2_session_add_item(session, item); + + if (rv != 0) { + nghttp2_frame_priority_free(&frame->priority); + nghttp2_mem_free(mem, item); + + return rv; + } + + return 0; +} + +int nghttp2_submit_rst_stream(nghttp2_session *session, uint8_t flags _U_, + int32_t stream_id, uint32_t error_code) { + if (stream_id == 0) { + return NGHTTP2_ERR_INVALID_ARGUMENT; + } + + return nghttp2_session_add_rst_stream(session, stream_id, error_code); +} + +int nghttp2_submit_goaway(nghttp2_session *session, uint8_t flags _U_, + int32_t last_stream_id, uint32_t error_code, + const uint8_t *opaque_data, size_t opaque_data_len) { + if (session->goaway_flags & NGHTTP2_GOAWAY_TERM_ON_SEND) { + return 0; + } + return nghttp2_session_add_goaway(session, last_stream_id, error_code, + opaque_data, opaque_data_len, + NGHTTP2_GOAWAY_AUX_NONE); +} + +int nghttp2_submit_shutdown_notice(nghttp2_session *session) { + if (!session->server) { + return NGHTTP2_ERR_INVALID_STATE; + } + if (session->goaway_flags) { + return 0; + } + return nghttp2_session_add_goaway(session, (1u << 31) - 1, NGHTTP2_NO_ERROR, + NULL, 0, + NGHTTP2_GOAWAY_AUX_SHUTDOWN_NOTICE); +} + +int nghttp2_submit_settings(nghttp2_session *session, uint8_t flags _U_, + const nghttp2_settings_entry *iv, size_t niv) { + return nghttp2_session_add_settings(session, NGHTTP2_FLAG_NONE, iv, niv); +} + +int32_t nghttp2_submit_push_promise(nghttp2_session *session, uint8_t flags _U_, + int32_t stream_id, const nghttp2_nv *nva, + size_t nvlen, + void *promised_stream_user_data) { + nghttp2_outbound_item *item; + nghttp2_frame *frame; + nghttp2_nv *nva_copy; + uint8_t flags_copy; + int32_t promised_stream_id; + int rv; + nghttp2_mem *mem; + + mem = &session->mem; + + if (stream_id == 0 || nghttp2_session_is_my_stream_id(session, stream_id)) { + return NGHTTP2_ERR_INVALID_ARGUMENT; + } + + if (!session->server) { + return NGHTTP2_ERR_PROTO; + } + + /* All 32bit signed stream IDs are spent. */ + if (session->next_stream_id > INT32_MAX) { + return NGHTTP2_ERR_STREAM_ID_NOT_AVAILABLE; + } + + item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item)); + if (item == NULL) { + return NGHTTP2_ERR_NOMEM; + } + + nghttp2_outbound_item_init(item); + + item->aux_data.headers.stream_user_data = promised_stream_user_data; + + frame = &item->frame; + + rv = nghttp2_nv_array_copy(&nva_copy, nva, nvlen, mem); + if (rv < 0) { + nghttp2_mem_free(mem, item); + return rv; + } + + flags_copy = NGHTTP2_FLAG_END_HEADERS; + + promised_stream_id = session->next_stream_id; + session->next_stream_id += 2; + + nghttp2_frame_push_promise_init(&frame->push_promise, flags_copy, stream_id, + promised_stream_id, nva_copy, nvlen); + + rv = nghttp2_session_add_item(session, item); + + if (rv != 0) { + nghttp2_frame_push_promise_free(&frame->push_promise, mem); + nghttp2_mem_free(mem, item); + + return rv; + } + + return promised_stream_id; +} + +int nghttp2_submit_window_update(nghttp2_session *session, uint8_t flags, + int32_t stream_id, + int32_t window_size_increment) { + int rv; + nghttp2_stream *stream = 0; + if (window_size_increment == 0) { + return 0; + } + flags = 0; + if (stream_id == 0) { + rv = nghttp2_adjust_local_window_size( + &session->local_window_size, &session->recv_window_size, + &session->recv_reduction, &window_size_increment); + if (rv != 0) { + return rv; + } + } else { + stream = nghttp2_session_get_stream(session, stream_id); + if (!stream) { + return 0; + } + + rv = nghttp2_adjust_local_window_size( + &stream->local_window_size, &stream->recv_window_size, + &stream->recv_reduction, &window_size_increment); + if (rv != 0) { + return rv; + } + } + + if (window_size_increment > 0) { + if (stream_id == 0) { + session->consumed_size = + nghttp2_max(0, session->consumed_size - window_size_increment); + } else { + stream->consumed_size = + nghttp2_max(0, stream->consumed_size - window_size_increment); + } + + return nghttp2_session_add_window_update(session, flags, stream_id, + window_size_increment); + } + return 0; +} + +static uint8_t set_request_flags(const nghttp2_priority_spec *pri_spec, + const nghttp2_data_provider *data_prd) { + uint8_t flags = NGHTTP2_FLAG_NONE; + if (data_prd == NULL || data_prd->read_callback == NULL) { + flags |= NGHTTP2_FLAG_END_STREAM; + } + + if (pri_spec) { + flags |= NGHTTP2_FLAG_PRIORITY; + } + + return flags; +} + +int32_t nghttp2_submit_request(nghttp2_session *session, + const nghttp2_priority_spec *pri_spec, + const nghttp2_nv *nva, size_t nvlen, + const nghttp2_data_provider *data_prd, + void *stream_user_data) { + uint8_t flags; + + if (pri_spec && nghttp2_priority_spec_check_default(pri_spec)) { + pri_spec = NULL; + } + + flags = set_request_flags(pri_spec, data_prd); + + return submit_headers_shared_nva(session, flags, -1, pri_spec, nva, nvlen, + data_prd, stream_user_data, 0); +} + +static uint8_t set_response_flags(const nghttp2_data_provider *data_prd) { + uint8_t flags = NGHTTP2_FLAG_NONE; + if (data_prd == NULL || data_prd->read_callback == NULL) { + flags |= NGHTTP2_FLAG_END_STREAM; + } + return flags; +} + +int nghttp2_submit_response(nghttp2_session *session, int32_t stream_id, + const nghttp2_nv *nva, size_t nvlen, + const nghttp2_data_provider *data_prd) { + uint8_t flags = set_response_flags(data_prd); + return submit_headers_shared_nva(session, flags, stream_id, NULL, nva, nvlen, + data_prd, NULL, 1); +} + +int nghttp2_submit_data(nghttp2_session *session, uint8_t flags, + int32_t stream_id, + const nghttp2_data_provider *data_prd) { + int rv; + nghttp2_outbound_item *item; + nghttp2_frame *frame; + nghttp2_data_aux_data *aux_data; + uint8_t nflags = flags & NGHTTP2_FLAG_END_STREAM; + nghttp2_mem *mem; + + mem = &session->mem; + + if (stream_id == 0) { + return NGHTTP2_ERR_INVALID_ARGUMENT; + } + + item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item)); + if (item == NULL) { + return NGHTTP2_ERR_NOMEM; + } + + nghttp2_outbound_item_init(item); + + frame = &item->frame; + aux_data = &item->aux_data.data; + aux_data->data_prd = *data_prd; + aux_data->eof = 0; + aux_data->flags = nflags; + + /* flags are sent on transmission */ + nghttp2_frame_data_init(&frame->data, NGHTTP2_FLAG_NONE, stream_id); + + rv = nghttp2_session_add_item(session, item); + if (rv != 0) { + nghttp2_frame_data_free(&frame->data); + nghttp2_mem_free(mem, item); + return rv; + } + return 0; +} + +ssize_t nghttp2_pack_settings_payload(uint8_t *buf, size_t buflen, + const nghttp2_settings_entry *iv, + size_t niv) { + if (!nghttp2_iv_check(iv, niv)) { + return NGHTTP2_ERR_INVALID_ARGUMENT; + } + + if (buflen < (niv * NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH)) { + return NGHTTP2_ERR_INSUFF_BUFSIZE; + } + + return nghttp2_frame_pack_settings_payload(buf, iv, niv); +} diff --git a/lib/nghttp2_submit.h b/lib/nghttp2_submit.h new file mode 100644 index 0000000..545388c --- /dev/null +++ b/lib/nghttp2_submit.h @@ -0,0 +1,34 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGHTTP2_SUBMIT_H +#define NGHTTP2_SUBMIT_H + +#ifdef HAVE_CONFIG_H +#include +#endif /* HAVE_CONFIG_H */ + +#include + +#endif /* NGHTTP2_SUBMIT_H */ diff --git a/lib/nghttp2_version.c b/lib/nghttp2_version.c new file mode 100644 index 0000000..8c5710d --- /dev/null +++ b/lib/nghttp2_version.c @@ -0,0 +1,38 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012, 2013 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifdef HAVE_CONFIG_H +#include +#endif /* HAVE_CONFIG_H */ + +#include + +static nghttp2_info version = {NGHTTP2_VERSION_AGE, NGHTTP2_VERSION_NUM, + NGHTTP2_VERSION, NGHTTP2_PROTO_VERSION_ID}; + +nghttp2_info *nghttp2_version(int least_version) { + if (least_version > NGHTTP2_VERSION_NUM) + return NULL; + return &version; +} diff --git a/ltmain.sh b/ltmain.sh new file mode 100644 index 0000000..bffda54 --- /dev/null +++ b/ltmain.sh @@ -0,0 +1,9661 @@ + +# libtool (GNU libtool) 2.4.2 +# Written by Gordon Matzigkeit , 1996 + +# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2003, 2004, 2005, 2006, +# 2007, 2008, 2009, 2010, 2011 Free Software Foundation, Inc. +# This is free software; see the source for copying conditions. There is NO +# warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +# GNU Libtool 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 2 of the License, or +# (at your option) any later version. +# +# As a special exception to the GNU General Public License, +# if you distribute this file as part of a program or library that +# is built using GNU Libtool, you may include this file under the +# same distribution terms that you use for the rest of that program. +# +# GNU Libtool 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 GNU Libtool; see the file COPYING. If not, a copy +# can be downloaded from http://www.gnu.org/licenses/gpl.html, +# or obtained by writing to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +# Usage: $progname [OPTION]... [MODE-ARG]... +# +# Provide generalized library-building support services. +# +# --config show all configuration variables +# --debug enable verbose shell tracing +# -n, --dry-run display commands without modifying any files +# --features display basic configuration information and exit +# --mode=MODE use operation mode MODE +# --preserve-dup-deps don't remove duplicate dependency libraries +# --quiet, --silent don't print informational messages +# --no-quiet, --no-silent +# print informational messages (default) +# --no-warn don't display warning messages +# --tag=TAG use configuration variables from tag TAG +# -v, --verbose print more informational messages than default +# --no-verbose don't print the extra informational messages +# --version print version information +# -h, --help, --help-all print short, long, or detailed help message +# +# MODE must be one of the following: +# +# clean remove files from the build directory +# compile compile a source file into a libtool object +# execute automatically set library path, then run a program +# finish complete the installation of libtool libraries +# install install libraries or executables +# link create a library or an executable +# uninstall remove libraries from an installed directory +# +# MODE-ARGS vary depending on the MODE. When passed as first option, +# `--mode=MODE' may be abbreviated as `MODE' or a unique abbreviation of that. +# Try `$progname --help --mode=MODE' for a more detailed description of MODE. +# +# When reporting a bug, please describe a test case to reproduce it and +# include the following information: +# +# host-triplet: $host +# shell: $SHELL +# compiler: $LTCC +# compiler flags: $LTCFLAGS +# linker: $LD (gnu? $with_gnu_ld) +# $progname: (GNU libtool) 2.4.2 Debian-2.4.2-1.11 +# automake: $automake_version +# autoconf: $autoconf_version +# +# Report bugs to . +# GNU libtool home page: . +# General help using GNU software: . + +PROGRAM=libtool +PACKAGE=libtool +VERSION="2.4.2 Debian-2.4.2-1.11" +TIMESTAMP="" +package_revision=1.3337 + +# Be Bourne compatible +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then + emulate sh + NULLCMD=: + # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' + setopt NO_GLOB_SUBST +else + case `(set -o) 2>/dev/null` in *posix*) set -o posix;; esac +fi +BIN_SH=xpg4; export BIN_SH # for Tru64 +DUALCASE=1; export DUALCASE # for MKS sh + +# A function that is used when there is no print builtin or printf. +func_fallback_echo () +{ + eval 'cat <<_LTECHO_EOF +$1 +_LTECHO_EOF' +} + +# NLS nuisances: We save the old values to restore during execute mode. +lt_user_locale= +lt_safe_locale= +for lt_var in LANG LANGUAGE LC_ALL LC_CTYPE LC_COLLATE LC_MESSAGES +do + eval "if test \"\${$lt_var+set}\" = set; then + save_$lt_var=\$$lt_var + $lt_var=C + export $lt_var + lt_user_locale=\"$lt_var=\\\$save_\$lt_var; \$lt_user_locale\" + lt_safe_locale=\"$lt_var=C; \$lt_safe_locale\" + fi" +done +LC_ALL=C +LANGUAGE=C +export LANGUAGE LC_ALL + +$lt_unset CDPATH + + +# Work around backward compatibility issue on IRIX 6.5. On IRIX 6.4+, sh +# is ksh but when the shell is invoked as "sh" and the current value of +# the _XPG environment variable is not equal to 1 (one), the special +# positional parameter $0, within a function call, is the name of the +# function. +progpath="$0" + + + +: ${CP="cp -f"} +test "${ECHO+set}" = set || ECHO=${as_echo-'printf %s\n'} +: ${MAKE="make"} +: ${MKDIR="mkdir"} +: ${MV="mv -f"} +: ${RM="rm -f"} +: ${SHELL="${CONFIG_SHELL-/bin/sh}"} +: ${Xsed="$SED -e 1s/^X//"} + +# Global variables: +EXIT_SUCCESS=0 +EXIT_FAILURE=1 +EXIT_MISMATCH=63 # $? = 63 is used to indicate version mismatch to missing. +EXIT_SKIP=77 # $? = 77 is used to indicate a skipped test to automake. + +exit_status=$EXIT_SUCCESS + +# Make sure IFS has a sensible default +lt_nl=' +' +IFS=" $lt_nl" + +dirname="s,/[^/]*$,," +basename="s,^.*/,," + +# func_dirname file append nondir_replacement +# Compute the dirname of FILE. If nonempty, add APPEND to the result, +# otherwise set result to NONDIR_REPLACEMENT. +func_dirname () +{ + func_dirname_result=`$ECHO "${1}" | $SED "$dirname"` + if test "X$func_dirname_result" = "X${1}"; then + func_dirname_result="${3}" + else + func_dirname_result="$func_dirname_result${2}" + fi +} # func_dirname may be replaced by extended shell implementation + + +# func_basename file +func_basename () +{ + func_basename_result=`$ECHO "${1}" | $SED "$basename"` +} # func_basename may be replaced by extended shell implementation + + +# func_dirname_and_basename file append nondir_replacement +# perform func_basename and func_dirname in a single function +# call: +# dirname: Compute the dirname of FILE. If nonempty, +# add APPEND to the result, otherwise set result +# to NONDIR_REPLACEMENT. +# value returned in "$func_dirname_result" +# basename: Compute filename of FILE. +# value retuned in "$func_basename_result" +# Implementation must be kept synchronized with func_dirname +# and func_basename. For efficiency, we do not delegate to +# those functions but instead duplicate the functionality here. +func_dirname_and_basename () +{ + # Extract subdirectory from the argument. + func_dirname_result=`$ECHO "${1}" | $SED -e "$dirname"` + if test "X$func_dirname_result" = "X${1}"; then + func_dirname_result="${3}" + else + func_dirname_result="$func_dirname_result${2}" + fi + func_basename_result=`$ECHO "${1}" | $SED -e "$basename"` +} # func_dirname_and_basename may be replaced by extended shell implementation + + +# func_stripname prefix suffix name +# strip PREFIX and SUFFIX off of NAME. +# PREFIX and SUFFIX must not contain globbing or regex special +# characters, hashes, percent signs, but SUFFIX may contain a leading +# dot (in which case that matches only a dot). +# func_strip_suffix prefix name +func_stripname () +{ + case ${2} in + .*) func_stripname_result=`$ECHO "${3}" | $SED "s%^${1}%%; s%\\\\${2}\$%%"`;; + *) func_stripname_result=`$ECHO "${3}" | $SED "s%^${1}%%; s%${2}\$%%"`;; + esac +} # func_stripname may be replaced by extended shell implementation + + +# These SED scripts presuppose an absolute path with a trailing slash. +pathcar='s,^/\([^/]*\).*$,\1,' +pathcdr='s,^/[^/]*,,' +removedotparts=':dotsl + s@/\./@/@g + t dotsl + s,/\.$,/,' +collapseslashes='s@/\{1,\}@/@g' +finalslash='s,/*$,/,' + +# func_normal_abspath PATH +# Remove doubled-up and trailing slashes, "." path components, +# and cancel out any ".." path components in PATH after making +# it an absolute path. +# value returned in "$func_normal_abspath_result" +func_normal_abspath () +{ + # Start from root dir and reassemble the path. + func_normal_abspath_result= + func_normal_abspath_tpath=$1 + func_normal_abspath_altnamespace= + case $func_normal_abspath_tpath in + "") + # Empty path, that just means $cwd. + func_stripname '' '/' "`pwd`" + func_normal_abspath_result=$func_stripname_result + return + ;; + # The next three entries are used to spot a run of precisely + # two leading slashes without using negated character classes; + # we take advantage of case's first-match behaviour. + ///*) + # Unusual form of absolute path, do nothing. + ;; + //*) + # Not necessarily an ordinary path; POSIX reserves leading '//' + # and for example Cygwin uses it to access remote file shares + # over CIFS/SMB, so we conserve a leading double slash if found. + func_normal_abspath_altnamespace=/ + ;; + /*) + # Absolute path, do nothing. + ;; + *) + # Relative path, prepend $cwd. + func_normal_abspath_tpath=`pwd`/$func_normal_abspath_tpath + ;; + esac + # Cancel out all the simple stuff to save iterations. We also want + # the path to end with a slash for ease of parsing, so make sure + # there is one (and only one) here. + func_normal_abspath_tpath=`$ECHO "$func_normal_abspath_tpath" | $SED \ + -e "$removedotparts" -e "$collapseslashes" -e "$finalslash"` + while :; do + # Processed it all yet? + if test "$func_normal_abspath_tpath" = / ; then + # If we ascended to the root using ".." the result may be empty now. + if test -z "$func_normal_abspath_result" ; then + func_normal_abspath_result=/ + fi + break + fi + func_normal_abspath_tcomponent=`$ECHO "$func_normal_abspath_tpath" | $SED \ + -e "$pathcar"` + func_normal_abspath_tpath=`$ECHO "$func_normal_abspath_tpath" | $SED \ + -e "$pathcdr"` + # Figure out what to do with it + case $func_normal_abspath_tcomponent in + "") + # Trailing empty path component, ignore it. + ;; + ..) + # Parent dir; strip last assembled component from result. + func_dirname "$func_normal_abspath_result" + func_normal_abspath_result=$func_dirname_result + ;; + *) + # Actual path component, append it. + func_normal_abspath_result=$func_normal_abspath_result/$func_normal_abspath_tcomponent + ;; + esac + done + # Restore leading double-slash if one was found on entry. + func_normal_abspath_result=$func_normal_abspath_altnamespace$func_normal_abspath_result +} + +# func_relative_path SRCDIR DSTDIR +# generates a relative path from SRCDIR to DSTDIR, with a trailing +# slash if non-empty, suitable for immediately appending a filename +# without needing to append a separator. +# value returned in "$func_relative_path_result" +func_relative_path () +{ + func_relative_path_result= + func_normal_abspath "$1" + func_relative_path_tlibdir=$func_normal_abspath_result + func_normal_abspath "$2" + func_relative_path_tbindir=$func_normal_abspath_result + + # Ascend the tree starting from libdir + while :; do + # check if we have found a prefix of bindir + case $func_relative_path_tbindir in + $func_relative_path_tlibdir) + # found an exact match + func_relative_path_tcancelled= + break + ;; + $func_relative_path_tlibdir*) + # found a matching prefix + func_stripname "$func_relative_path_tlibdir" '' "$func_relative_path_tbindir" + func_relative_path_tcancelled=$func_stripname_result + if test -z "$func_relative_path_result"; then + func_relative_path_result=. + fi + break + ;; + *) + func_dirname $func_relative_path_tlibdir + func_relative_path_tlibdir=${func_dirname_result} + if test "x$func_relative_path_tlibdir" = x ; then + # Have to descend all the way to the root! + func_relative_path_result=../$func_relative_path_result + func_relative_path_tcancelled=$func_relative_path_tbindir + break + fi + func_relative_path_result=../$func_relative_path_result + ;; + esac + done + + # Now calculate path; take care to avoid doubling-up slashes. + func_stripname '' '/' "$func_relative_path_result" + func_relative_path_result=$func_stripname_result + func_stripname '/' '/' "$func_relative_path_tcancelled" + if test "x$func_stripname_result" != x ; then + func_relative_path_result=${func_relative_path_result}/${func_stripname_result} + fi + + # Normalisation. If bindir is libdir, return empty string, + # else relative path ending with a slash; either way, target + # file name can be directly appended. + if test ! -z "$func_relative_path_result"; then + func_stripname './' '' "$func_relative_path_result/" + func_relative_path_result=$func_stripname_result + fi +} + +# The name of this program: +func_dirname_and_basename "$progpath" +progname=$func_basename_result + +# Make sure we have an absolute path for reexecution: +case $progpath in + [\\/]*|[A-Za-z]:\\*) ;; + *[\\/]*) + progdir=$func_dirname_result + progdir=`cd "$progdir" && pwd` + progpath="$progdir/$progname" + ;; + *) + save_IFS="$IFS" + IFS=${PATH_SEPARATOR-:} + for progdir in $PATH; do + IFS="$save_IFS" + test -x "$progdir/$progname" && break + done + IFS="$save_IFS" + test -n "$progdir" || progdir=`pwd` + progpath="$progdir/$progname" + ;; +esac + +# Sed substitution that helps us do robust quoting. It backslashifies +# metacharacters that are still active within double-quoted strings. +Xsed="${SED}"' -e 1s/^X//' +sed_quote_subst='s/\([`"$\\]\)/\\\1/g' + +# Same as above, but do not quote variable references. +double_quote_subst='s/\(["`\\]\)/\\\1/g' + +# Sed substitution that turns a string into a regex matching for the +# string literally. +sed_make_literal_regex='s,[].[^$\\*\/],\\&,g' + +# Sed substitution that converts a w32 file name or path +# which contains forward slashes, into one that contains +# (escaped) backslashes. A very naive implementation. +lt_sed_naive_backslashify='s|\\\\*|\\|g;s|/|\\|g;s|\\|\\\\|g' + +# Re-`\' parameter expansions in output of double_quote_subst that were +# `\'-ed in input to the same. If an odd number of `\' preceded a '$' +# in input to double_quote_subst, that '$' was protected from expansion. +# Since each input `\' is now two `\'s, look for any number of runs of +# four `\'s followed by two `\'s and then a '$'. `\' that '$'. +bs='\\' +bs2='\\\\' +bs4='\\\\\\\\' +dollar='\$' +sed_double_backslash="\ + s/$bs4/&\\ +/g + s/^$bs2$dollar/$bs&/ + s/\\([^$bs]\\)$bs2$dollar/\\1$bs2$bs$dollar/g + s/\n//g" + +# Standard options: +opt_dry_run=false +opt_help=false +opt_quiet=false +opt_verbose=false +opt_warning=: + +# func_echo arg... +# Echo program name prefixed message, along with the current mode +# name if it has been set yet. +func_echo () +{ + $ECHO "$progname: ${opt_mode+$opt_mode: }$*" +} + +# func_verbose arg... +# Echo program name prefixed message in verbose mode only. +func_verbose () +{ + $opt_verbose && func_echo ${1+"$@"} + + # A bug in bash halts the script if the last line of a function + # fails when set -e is in force, so we need another command to + # work around that: + : +} + +# func_echo_all arg... +# Invoke $ECHO with all args, space-separated. +func_echo_all () +{ + $ECHO "$*" +} + +# func_error arg... +# Echo program name prefixed message to standard error. +func_error () +{ + $ECHO "$progname: ${opt_mode+$opt_mode: }"${1+"$@"} 1>&2 +} + +# func_warning arg... +# Echo program name prefixed warning message to standard error. +func_warning () +{ + $opt_warning && $ECHO "$progname: ${opt_mode+$opt_mode: }warning: "${1+"$@"} 1>&2 + + # bash bug again: + : +} + +# func_fatal_error arg... +# Echo program name prefixed message to standard error, and exit. +func_fatal_error () +{ + func_error ${1+"$@"} + exit $EXIT_FAILURE +} + +# func_fatal_help arg... +# Echo program name prefixed message to standard error, followed by +# a help hint, and exit. +func_fatal_help () +{ + func_error ${1+"$@"} + func_fatal_error "$help" +} +help="Try \`$progname --help' for more information." ## default + + +# func_grep expression filename +# Check whether EXPRESSION matches any line of FILENAME, without output. +func_grep () +{ + $GREP "$1" "$2" >/dev/null 2>&1 +} + + +# func_mkdir_p directory-path +# Make sure the entire path to DIRECTORY-PATH is available. +func_mkdir_p () +{ + my_directory_path="$1" + my_dir_list= + + if test -n "$my_directory_path" && test "$opt_dry_run" != ":"; then + + # Protect directory names starting with `-' + case $my_directory_path in + -*) my_directory_path="./$my_directory_path" ;; + esac + + # While some portion of DIR does not yet exist... + while test ! -d "$my_directory_path"; do + # ...make a list in topmost first order. Use a colon delimited + # list incase some portion of path contains whitespace. + my_dir_list="$my_directory_path:$my_dir_list" + + # If the last portion added has no slash in it, the list is done + case $my_directory_path in */*) ;; *) break ;; esac + + # ...otherwise throw away the child directory and loop + my_directory_path=`$ECHO "$my_directory_path" | $SED -e "$dirname"` + done + my_dir_list=`$ECHO "$my_dir_list" | $SED 's,:*$,,'` + + save_mkdir_p_IFS="$IFS"; IFS=':' + for my_dir in $my_dir_list; do + IFS="$save_mkdir_p_IFS" + # mkdir can fail with a `File exist' error if two processes + # try to create one of the directories concurrently. Don't + # stop in that case! + $MKDIR "$my_dir" 2>/dev/null || : + done + IFS="$save_mkdir_p_IFS" + + # Bail out if we (or some other process) failed to create a directory. + test -d "$my_directory_path" || \ + func_fatal_error "Failed to create \`$1'" + fi +} + + +# func_mktempdir [string] +# Make a temporary directory that won't clash with other running +# libtool processes, and avoids race conditions if possible. If +# given, STRING is the basename for that directory. +func_mktempdir () +{ + my_template="${TMPDIR-/tmp}/${1-$progname}" + + if test "$opt_dry_run" = ":"; then + # Return a directory name, but don't create it in dry-run mode + my_tmpdir="${my_template}-$$" + else + + # If mktemp works, use that first and foremost + my_tmpdir=`mktemp -d "${my_template}-XXXXXXXX" 2>/dev/null` + + if test ! -d "$my_tmpdir"; then + # Failing that, at least try and use $RANDOM to avoid a race + my_tmpdir="${my_template}-${RANDOM-0}$$" + + save_mktempdir_umask=`umask` + umask 0077 + $MKDIR "$my_tmpdir" + umask $save_mktempdir_umask + fi + + # If we're not in dry-run mode, bomb out on failure + test -d "$my_tmpdir" || \ + func_fatal_error "cannot create temporary directory \`$my_tmpdir'" + fi + + $ECHO "$my_tmpdir" +} + + +# func_quote_for_eval arg +# Aesthetically quote ARG to be evaled later. +# This function returns two values: FUNC_QUOTE_FOR_EVAL_RESULT +# is double-quoted, suitable for a subsequent eval, whereas +# FUNC_QUOTE_FOR_EVAL_UNQUOTED_RESULT has merely all characters +# which are still active within double quotes backslashified. +func_quote_for_eval () +{ + case $1 in + *[\\\`\"\$]*) + func_quote_for_eval_unquoted_result=`$ECHO "$1" | $SED "$sed_quote_subst"` ;; + *) + func_quote_for_eval_unquoted_result="$1" ;; + esac + + case $func_quote_for_eval_unquoted_result in + # Double-quote args containing shell metacharacters to delay + # word splitting, command substitution and and variable + # expansion for a subsequent eval. + # Many Bourne shells cannot handle close brackets correctly + # in scan sets, so we specify it separately. + *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") + func_quote_for_eval_result="\"$func_quote_for_eval_unquoted_result\"" + ;; + *) + func_quote_for_eval_result="$func_quote_for_eval_unquoted_result" + esac +} + + +# func_quote_for_expand arg +# Aesthetically quote ARG to be evaled later; same as above, +# but do not quote variable references. +func_quote_for_expand () +{ + case $1 in + *[\\\`\"]*) + my_arg=`$ECHO "$1" | $SED \ + -e "$double_quote_subst" -e "$sed_double_backslash"` ;; + *) + my_arg="$1" ;; + esac + + case $my_arg in + # Double-quote args containing shell metacharacters to delay + # word splitting and command substitution for a subsequent eval. + # Many Bourne shells cannot handle close brackets correctly + # in scan sets, so we specify it separately. + *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") + my_arg="\"$my_arg\"" + ;; + esac + + func_quote_for_expand_result="$my_arg" +} + + +# func_show_eval cmd [fail_exp] +# Unless opt_silent is true, then output CMD. Then, if opt_dryrun is +# not true, evaluate CMD. If the evaluation of CMD fails, and FAIL_EXP +# is given, then evaluate it. +func_show_eval () +{ + my_cmd="$1" + my_fail_exp="${2-:}" + + ${opt_silent-false} || { + func_quote_for_expand "$my_cmd" + eval "func_echo $func_quote_for_expand_result" + } + + if ${opt_dry_run-false}; then :; else + eval "$my_cmd" + my_status=$? + if test "$my_status" -eq 0; then :; else + eval "(exit $my_status); $my_fail_exp" + fi + fi +} + + +# func_show_eval_locale cmd [fail_exp] +# Unless opt_silent is true, then output CMD. Then, if opt_dryrun is +# not true, evaluate CMD. If the evaluation of CMD fails, and FAIL_EXP +# is given, then evaluate it. Use the saved locale for evaluation. +func_show_eval_locale () +{ + my_cmd="$1" + my_fail_exp="${2-:}" + + ${opt_silent-false} || { + func_quote_for_expand "$my_cmd" + eval "func_echo $func_quote_for_expand_result" + } + + if ${opt_dry_run-false}; then :; else + eval "$lt_user_locale + $my_cmd" + my_status=$? + eval "$lt_safe_locale" + if test "$my_status" -eq 0; then :; else + eval "(exit $my_status); $my_fail_exp" + fi + fi +} + +# func_tr_sh +# Turn $1 into a string suitable for a shell variable name. +# Result is stored in $func_tr_sh_result. All characters +# not in the set a-zA-Z0-9_ are replaced with '_'. Further, +# if $1 begins with a digit, a '_' is prepended as well. +func_tr_sh () +{ + case $1 in + [0-9]* | *[!a-zA-Z0-9_]*) + func_tr_sh_result=`$ECHO "$1" | $SED 's/^\([0-9]\)/_\1/; s/[^a-zA-Z0-9_]/_/g'` + ;; + * ) + func_tr_sh_result=$1 + ;; + esac +} + + +# func_version +# Echo version message to standard output and exit. +func_version () +{ + $opt_debug + + $SED -n '/(C)/!b go + :more + /\./!{ + N + s/\n# / / + b more + } + :go + /^# '$PROGRAM' (GNU /,/# warranty; / { + s/^# // + s/^# *$// + s/\((C)\)[ 0-9,-]*\( [1-9][0-9]*\)/\1\2/ + p + }' < "$progpath" + exit $? +} + +# func_usage +# Echo short help message to standard output and exit. +func_usage () +{ + $opt_debug + + $SED -n '/^# Usage:/,/^# *.*--help/ { + s/^# // + s/^# *$// + s/\$progname/'$progname'/ + p + }' < "$progpath" + echo + $ECHO "run \`$progname --help | more' for full usage" + exit $? +} + +# func_help [NOEXIT] +# Echo long help message to standard output and exit, +# unless 'noexit' is passed as argument. +func_help () +{ + $opt_debug + + $SED -n '/^# Usage:/,/# Report bugs to/ { + :print + s/^# // + s/^# *$// + s*\$progname*'$progname'* + s*\$host*'"$host"'* + s*\$SHELL*'"$SHELL"'* + s*\$LTCC*'"$LTCC"'* + s*\$LTCFLAGS*'"$LTCFLAGS"'* + s*\$LD*'"$LD"'* + s/\$with_gnu_ld/'"$with_gnu_ld"'/ + s/\$automake_version/'"`(${AUTOMAKE-automake} --version) 2>/dev/null |$SED 1q`"'/ + s/\$autoconf_version/'"`(${AUTOCONF-autoconf} --version) 2>/dev/null |$SED 1q`"'/ + p + d + } + /^# .* home page:/b print + /^# General help using/b print + ' < "$progpath" + ret=$? + if test -z "$1"; then + exit $ret + fi +} + +# func_missing_arg argname +# Echo program name prefixed message to standard error and set global +# exit_cmd. +func_missing_arg () +{ + $opt_debug + + func_error "missing argument for $1." + exit_cmd=exit +} + + +# func_split_short_opt shortopt +# Set func_split_short_opt_name and func_split_short_opt_arg shell +# variables after splitting SHORTOPT after the 2nd character. +func_split_short_opt () +{ + my_sed_short_opt='1s/^\(..\).*$/\1/;q' + my_sed_short_rest='1s/^..\(.*\)$/\1/;q' + + func_split_short_opt_name=`$ECHO "$1" | $SED "$my_sed_short_opt"` + func_split_short_opt_arg=`$ECHO "$1" | $SED "$my_sed_short_rest"` +} # func_split_short_opt may be replaced by extended shell implementation + + +# func_split_long_opt longopt +# Set func_split_long_opt_name and func_split_long_opt_arg shell +# variables after splitting LONGOPT at the `=' sign. +func_split_long_opt () +{ + my_sed_long_opt='1s/^\(--[^=]*\)=.*/\1/;q' + my_sed_long_arg='1s/^--[^=]*=//' + + func_split_long_opt_name=`$ECHO "$1" | $SED "$my_sed_long_opt"` + func_split_long_opt_arg=`$ECHO "$1" | $SED "$my_sed_long_arg"` +} # func_split_long_opt may be replaced by extended shell implementation + +exit_cmd=: + + + + + +magic="%%%MAGIC variable%%%" +magic_exe="%%%MAGIC EXE variable%%%" + +# Global variables. +nonopt= +preserve_args= +lo2o="s/\\.lo\$/.${objext}/" +o2lo="s/\\.${objext}\$/.lo/" +extracted_archives= +extracted_serial=0 + +# If this variable is set in any of the actions, the command in it +# will be execed at the end. This prevents here-documents from being +# left over by shells. +exec_cmd= + +# func_append var value +# Append VALUE to the end of shell variable VAR. +func_append () +{ + eval "${1}=\$${1}\${2}" +} # func_append may be replaced by extended shell implementation + +# func_append_quoted var value +# Quote VALUE and append to the end of shell variable VAR, separated +# by a space. +func_append_quoted () +{ + func_quote_for_eval "${2}" + eval "${1}=\$${1}\\ \$func_quote_for_eval_result" +} # func_append_quoted may be replaced by extended shell implementation + + +# func_arith arithmetic-term... +func_arith () +{ + func_arith_result=`expr "${@}"` +} # func_arith may be replaced by extended shell implementation + + +# func_len string +# STRING may not start with a hyphen. +func_len () +{ + func_len_result=`expr "${1}" : ".*" 2>/dev/null || echo $max_cmd_len` +} # func_len may be replaced by extended shell implementation + + +# func_lo2o object +func_lo2o () +{ + func_lo2o_result=`$ECHO "${1}" | $SED "$lo2o"` +} # func_lo2o may be replaced by extended shell implementation + + +# func_xform libobj-or-source +func_xform () +{ + func_xform_result=`$ECHO "${1}" | $SED 's/\.[^.]*$/.lo/'` +} # func_xform may be replaced by extended shell implementation + + +# func_fatal_configuration arg... +# Echo program name prefixed message to standard error, followed by +# a configuration failure hint, and exit. +func_fatal_configuration () +{ + func_error ${1+"$@"} + func_error "See the $PACKAGE documentation for more information." + func_fatal_error "Fatal configuration error." +} + + +# func_config +# Display the configuration for all the tags in this script. +func_config () +{ + re_begincf='^# ### BEGIN LIBTOOL' + re_endcf='^# ### END LIBTOOL' + + # Default configuration. + $SED "1,/$re_begincf CONFIG/d;/$re_endcf CONFIG/,\$d" < "$progpath" + + # Now print the configurations for the tags. + for tagname in $taglist; do + $SED -n "/$re_begincf TAG CONFIG: $tagname\$/,/$re_endcf TAG CONFIG: $tagname\$/p" < "$progpath" + done + + exit $? +} + +# func_features +# Display the features supported by this script. +func_features () +{ + echo "host: $host" + if test "$build_libtool_libs" = yes; then + echo "enable shared libraries" + else + echo "disable shared libraries" + fi + if test "$build_old_libs" = yes; then + echo "enable static libraries" + else + echo "disable static libraries" + fi + + exit $? +} + +# func_enable_tag tagname +# Verify that TAGNAME is valid, and either flag an error and exit, or +# enable the TAGNAME tag. We also add TAGNAME to the global $taglist +# variable here. +func_enable_tag () +{ + # Global variable: + tagname="$1" + + re_begincf="^# ### BEGIN LIBTOOL TAG CONFIG: $tagname\$" + re_endcf="^# ### END LIBTOOL TAG CONFIG: $tagname\$" + sed_extractcf="/$re_begincf/,/$re_endcf/p" + + # Validate tagname. + case $tagname in + *[!-_A-Za-z0-9,/]*) + func_fatal_error "invalid tag name: $tagname" + ;; + esac + + # Don't test for the "default" C tag, as we know it's + # there but not specially marked. + case $tagname in + CC) ;; + *) + if $GREP "$re_begincf" "$progpath" >/dev/null 2>&1; then + taglist="$taglist $tagname" + + # Evaluate the configuration. Be careful to quote the path + # and the sed script, to avoid splitting on whitespace, but + # also don't use non-portable quotes within backquotes within + # quotes we have to do it in 2 steps: + extractedcf=`$SED -n -e "$sed_extractcf" < "$progpath"` + eval "$extractedcf" + else + func_error "ignoring unknown tag $tagname" + fi + ;; + esac +} + +# func_check_version_match +# Ensure that we are using m4 macros, and libtool script from the same +# release of libtool. +func_check_version_match () +{ + if test "$package_revision" != "$macro_revision"; then + if test "$VERSION" != "$macro_version"; then + if test -z "$macro_version"; then + cat >&2 <<_LT_EOF +$progname: Version mismatch error. This is $PACKAGE $VERSION, but the +$progname: definition of this LT_INIT comes from an older release. +$progname: You should recreate aclocal.m4 with macros from $PACKAGE $VERSION +$progname: and run autoconf again. +_LT_EOF + else + cat >&2 <<_LT_EOF +$progname: Version mismatch error. This is $PACKAGE $VERSION, but the +$progname: definition of this LT_INIT comes from $PACKAGE $macro_version. +$progname: You should recreate aclocal.m4 with macros from $PACKAGE $VERSION +$progname: and run autoconf again. +_LT_EOF + fi + else + cat >&2 <<_LT_EOF +$progname: Version mismatch error. This is $PACKAGE $VERSION, revision $package_revision, +$progname: but the definition of this LT_INIT comes from revision $macro_revision. +$progname: You should recreate aclocal.m4 with macros from revision $package_revision +$progname: of $PACKAGE $VERSION and run autoconf again. +_LT_EOF + fi + + exit $EXIT_MISMATCH + fi +} + + +# Shorthand for --mode=foo, only valid as the first argument +case $1 in +clean|clea|cle|cl) + shift; set dummy --mode clean ${1+"$@"}; shift + ;; +compile|compil|compi|comp|com|co|c) + shift; set dummy --mode compile ${1+"$@"}; shift + ;; +execute|execut|execu|exec|exe|ex|e) + shift; set dummy --mode execute ${1+"$@"}; shift + ;; +finish|finis|fini|fin|fi|f) + shift; set dummy --mode finish ${1+"$@"}; shift + ;; +install|instal|insta|inst|ins|in|i) + shift; set dummy --mode install ${1+"$@"}; shift + ;; +link|lin|li|l) + shift; set dummy --mode link ${1+"$@"}; shift + ;; +uninstall|uninstal|uninsta|uninst|unins|unin|uni|un|u) + shift; set dummy --mode uninstall ${1+"$@"}; shift + ;; +esac + + + +# Option defaults: +opt_debug=: +opt_dry_run=false +opt_config=false +opt_preserve_dup_deps=false +opt_features=false +opt_finish=false +opt_help=false +opt_help_all=false +opt_silent=: +opt_warning=: +opt_verbose=: +opt_silent=false +opt_verbose=false + + +# Parse options once, thoroughly. This comes as soon as possible in the +# script to make things like `--version' happen as quickly as we can. +{ + # this just eases exit handling + while test $# -gt 0; do + opt="$1" + shift + case $opt in + --debug|-x) opt_debug='set -x' + func_echo "enabling shell trace mode" + $opt_debug + ;; + --dry-run|--dryrun|-n) + opt_dry_run=: + ;; + --config) + opt_config=: +func_config + ;; + --dlopen|-dlopen) + optarg="$1" + opt_dlopen="${opt_dlopen+$opt_dlopen +}$optarg" + shift + ;; + --preserve-dup-deps) + opt_preserve_dup_deps=: + ;; + --features) + opt_features=: +func_features + ;; + --finish) + opt_finish=: +set dummy --mode finish ${1+"$@"}; shift + ;; + --help) + opt_help=: + ;; + --help-all) + opt_help_all=: +opt_help=': help-all' + ;; + --mode) + test $# = 0 && func_missing_arg $opt && break + optarg="$1" + opt_mode="$optarg" +case $optarg in + # Valid mode arguments: + clean|compile|execute|finish|install|link|relink|uninstall) ;; + + # Catch anything else as an error + *) func_error "invalid argument for $opt" + exit_cmd=exit + break + ;; +esac + shift + ;; + --no-silent|--no-quiet) + opt_silent=false +func_append preserve_args " $opt" + ;; + --no-warning|--no-warn) + opt_warning=false +func_append preserve_args " $opt" + ;; + --no-verbose) + opt_verbose=false +func_append preserve_args " $opt" + ;; + --silent|--quiet) + opt_silent=: +func_append preserve_args " $opt" + opt_verbose=false + ;; + --verbose|-v) + opt_verbose=: +func_append preserve_args " $opt" +opt_silent=false + ;; + --tag) + test $# = 0 && func_missing_arg $opt && break + optarg="$1" + opt_tag="$optarg" +func_append preserve_args " $opt $optarg" +func_enable_tag "$optarg" + shift + ;; + + -\?|-h) func_usage ;; + --help) func_help ;; + --version) func_version ;; + + # Separate optargs to long options: + --*=*) + func_split_long_opt "$opt" + set dummy "$func_split_long_opt_name" "$func_split_long_opt_arg" ${1+"$@"} + shift + ;; + + # Separate non-argument short options: + -\?*|-h*|-n*|-v*) + func_split_short_opt "$opt" + set dummy "$func_split_short_opt_name" "-$func_split_short_opt_arg" ${1+"$@"} + shift + ;; + + --) break ;; + -*) func_fatal_help "unrecognized option \`$opt'" ;; + *) set dummy "$opt" ${1+"$@"}; shift; break ;; + esac + done + + # Validate options: + + # save first non-option argument + if test "$#" -gt 0; then + nonopt="$opt" + shift + fi + + # preserve --debug + test "$opt_debug" = : || func_append preserve_args " --debug" + + case $host in + *cygwin* | *mingw* | *pw32* | *cegcc*) + # don't eliminate duplications in $postdeps and $predeps + opt_duplicate_compiler_generated_deps=: + ;; + *) + opt_duplicate_compiler_generated_deps=$opt_preserve_dup_deps + ;; + esac + + $opt_help || { + # Sanity checks first: + func_check_version_match + + if test "$build_libtool_libs" != yes && test "$build_old_libs" != yes; then + func_fatal_configuration "not configured to build any kind of library" + fi + + # Darwin sucks + eval std_shrext=\"$shrext_cmds\" + + # Only execute mode is allowed to have -dlopen flags. + if test -n "$opt_dlopen" && test "$opt_mode" != execute; then + func_error "unrecognized option \`-dlopen'" + $ECHO "$help" 1>&2 + exit $EXIT_FAILURE + fi + + # Change the help message to a mode-specific one. + generic_help="$help" + help="Try \`$progname --help --mode=$opt_mode' for more information." + } + + + # Bail if the options were screwed + $exit_cmd $EXIT_FAILURE +} + + + + +## ----------- ## +## Main. ## +## ----------- ## + +# func_lalib_p file +# True iff FILE is a libtool `.la' library or `.lo' object file. +# This function is only a basic sanity check; it will hardly flush out +# determined imposters. +func_lalib_p () +{ + test -f "$1" && + $SED -e 4q "$1" 2>/dev/null \ + | $GREP "^# Generated by .*$PACKAGE" > /dev/null 2>&1 +} + +# func_lalib_unsafe_p file +# True iff FILE is a libtool `.la' library or `.lo' object file. +# This function implements the same check as func_lalib_p without +# resorting to external programs. To this end, it redirects stdin and +# closes it afterwards, without saving the original file descriptor. +# As a safety measure, use it only where a negative result would be +# fatal anyway. Works if `file' does not exist. +func_lalib_unsafe_p () +{ + lalib_p=no + if test -f "$1" && test -r "$1" && exec 5<&0 <"$1"; then + for lalib_p_l in 1 2 3 4 + do + read lalib_p_line + case "$lalib_p_line" in + \#\ Generated\ by\ *$PACKAGE* ) lalib_p=yes; break;; + esac + done + exec 0<&5 5<&- + fi + test "$lalib_p" = yes +} + +# func_ltwrapper_script_p file +# True iff FILE is a libtool wrapper script +# This function is only a basic sanity check; it will hardly flush out +# determined imposters. +func_ltwrapper_script_p () +{ + func_lalib_p "$1" +} + +# func_ltwrapper_executable_p file +# True iff FILE is a libtool wrapper executable +# This function is only a basic sanity check; it will hardly flush out +# determined imposters. +func_ltwrapper_executable_p () +{ + func_ltwrapper_exec_suffix= + case $1 in + *.exe) ;; + *) func_ltwrapper_exec_suffix=.exe ;; + esac + $GREP "$magic_exe" "$1$func_ltwrapper_exec_suffix" >/dev/null 2>&1 +} + +# func_ltwrapper_scriptname file +# Assumes file is an ltwrapper_executable +# uses $file to determine the appropriate filename for a +# temporary ltwrapper_script. +func_ltwrapper_scriptname () +{ + func_dirname_and_basename "$1" "" "." + func_stripname '' '.exe' "$func_basename_result" + func_ltwrapper_scriptname_result="$func_dirname_result/$objdir/${func_stripname_result}_ltshwrapper" +} + +# func_ltwrapper_p file +# True iff FILE is a libtool wrapper script or wrapper executable +# This function is only a basic sanity check; it will hardly flush out +# determined imposters. +func_ltwrapper_p () +{ + func_ltwrapper_script_p "$1" || func_ltwrapper_executable_p "$1" +} + + +# func_execute_cmds commands fail_cmd +# Execute tilde-delimited COMMANDS. +# If FAIL_CMD is given, eval that upon failure. +# FAIL_CMD may read-access the current command in variable CMD! +func_execute_cmds () +{ + $opt_debug + save_ifs=$IFS; IFS='~' + for cmd in $1; do + IFS=$save_ifs + eval cmd=\"$cmd\" + func_show_eval "$cmd" "${2-:}" + done + IFS=$save_ifs +} + + +# func_source file +# Source FILE, adding directory component if necessary. +# Note that it is not necessary on cygwin/mingw to append a dot to +# FILE even if both FILE and FILE.exe exist: automatic-append-.exe +# behavior happens only for exec(3), not for open(2)! Also, sourcing +# `FILE.' does not work on cygwin managed mounts. +func_source () +{ + $opt_debug + case $1 in + */* | *\\*) . "$1" ;; + *) . "./$1" ;; + esac +} + + +# func_resolve_sysroot PATH +# Replace a leading = in PATH with a sysroot. Store the result into +# func_resolve_sysroot_result +func_resolve_sysroot () +{ + func_resolve_sysroot_result=$1 + case $func_resolve_sysroot_result in + =*) + func_stripname '=' '' "$func_resolve_sysroot_result" + func_resolve_sysroot_result=$lt_sysroot$func_stripname_result + ;; + esac +} + +# func_replace_sysroot PATH +# If PATH begins with the sysroot, replace it with = and +# store the result into func_replace_sysroot_result. +func_replace_sysroot () +{ + case "$lt_sysroot:$1" in + ?*:"$lt_sysroot"*) + func_stripname "$lt_sysroot" '' "$1" + func_replace_sysroot_result="=$func_stripname_result" + ;; + *) + # Including no sysroot. + func_replace_sysroot_result=$1 + ;; + esac +} + +# func_infer_tag arg +# Infer tagged configuration to use if any are available and +# if one wasn't chosen via the "--tag" command line option. +# Only attempt this if the compiler in the base compile +# command doesn't match the default compiler. +# arg is usually of the form 'gcc ...' +func_infer_tag () +{ + $opt_debug + if test -n "$available_tags" && test -z "$tagname"; then + CC_quoted= + for arg in $CC; do + func_append_quoted CC_quoted "$arg" + done + CC_expanded=`func_echo_all $CC` + CC_quoted_expanded=`func_echo_all $CC_quoted` + case $@ in + # Blanks in the command may have been stripped by the calling shell, + # but not from the CC environment variable when configure was run. + " $CC "* | "$CC "* | " $CC_expanded "* | "$CC_expanded "* | \ + " $CC_quoted"* | "$CC_quoted "* | " $CC_quoted_expanded "* | "$CC_quoted_expanded "*) ;; + # Blanks at the start of $base_compile will cause this to fail + # if we don't check for them as well. + *) + for z in $available_tags; do + if $GREP "^# ### BEGIN LIBTOOL TAG CONFIG: $z$" < "$progpath" > /dev/null; then + # Evaluate the configuration. + eval "`${SED} -n -e '/^# ### BEGIN LIBTOOL TAG CONFIG: '$z'$/,/^# ### END LIBTOOL TAG CONFIG: '$z'$/p' < $progpath`" + CC_quoted= + for arg in $CC; do + # Double-quote args containing other shell metacharacters. + func_append_quoted CC_quoted "$arg" + done + CC_expanded=`func_echo_all $CC` + CC_quoted_expanded=`func_echo_all $CC_quoted` + case "$@ " in + " $CC "* | "$CC "* | " $CC_expanded "* | "$CC_expanded "* | \ + " $CC_quoted"* | "$CC_quoted "* | " $CC_quoted_expanded "* | "$CC_quoted_expanded "*) + # The compiler in the base compile command matches + # the one in the tagged configuration. + # Assume this is the tagged configuration we want. + tagname=$z + break + ;; + esac + fi + done + # If $tagname still isn't set, then no tagged configuration + # was found and let the user know that the "--tag" command + # line option must be used. + if test -z "$tagname"; then + func_echo "unable to infer tagged configuration" + func_fatal_error "specify a tag with \`--tag'" +# else +# func_verbose "using $tagname tagged configuration" + fi + ;; + esac + fi +} + + + +# func_write_libtool_object output_name pic_name nonpic_name +# Create a libtool object file (analogous to a ".la" file), +# but don't create it if we're doing a dry run. +func_write_libtool_object () +{ + write_libobj=${1} + if test "$build_libtool_libs" = yes; then + write_lobj=\'${2}\' + else + write_lobj=none + fi + + if test "$build_old_libs" = yes; then + write_oldobj=\'${3}\' + else + write_oldobj=none + fi + + $opt_dry_run || { + cat >${write_libobj}T </dev/null` + if test "$?" -eq 0 && test -n "${func_convert_core_file_wine_to_w32_tmp}"; then + func_convert_core_file_wine_to_w32_result=`$ECHO "$func_convert_core_file_wine_to_w32_tmp" | + $SED -e "$lt_sed_naive_backslashify"` + else + func_convert_core_file_wine_to_w32_result= + fi + fi +} +# end: func_convert_core_file_wine_to_w32 + + +# func_convert_core_path_wine_to_w32 ARG +# Helper function used by path conversion functions when $build is *nix, and +# $host is mingw, cygwin, or some other w32 environment. Relies on a correctly +# configured wine environment available, with the winepath program in $build's +# $PATH. Assumes ARG has no leading or trailing path separator characters. +# +# ARG is path to be converted from $build format to win32. +# Result is available in $func_convert_core_path_wine_to_w32_result. +# Unconvertible file (directory) names in ARG are skipped; if no directory names +# are convertible, then the result may be empty. +func_convert_core_path_wine_to_w32 () +{ + $opt_debug + # unfortunately, winepath doesn't convert paths, only file names + func_convert_core_path_wine_to_w32_result="" + if test -n "$1"; then + oldIFS=$IFS + IFS=: + for func_convert_core_path_wine_to_w32_f in $1; do + IFS=$oldIFS + func_convert_core_file_wine_to_w32 "$func_convert_core_path_wine_to_w32_f" + if test -n "$func_convert_core_file_wine_to_w32_result" ; then + if test -z "$func_convert_core_path_wine_to_w32_result"; then + func_convert_core_path_wine_to_w32_result="$func_convert_core_file_wine_to_w32_result" + else + func_append func_convert_core_path_wine_to_w32_result ";$func_convert_core_file_wine_to_w32_result" + fi + fi + done + IFS=$oldIFS + fi +} +# end: func_convert_core_path_wine_to_w32 + + +# func_cygpath ARGS... +# Wrapper around calling the cygpath program via LT_CYGPATH. This is used when +# when (1) $build is *nix and Cygwin is hosted via a wine environment; or (2) +# $build is MSYS and $host is Cygwin, or (3) $build is Cygwin. In case (1) or +# (2), returns the Cygwin file name or path in func_cygpath_result (input +# file name or path is assumed to be in w32 format, as previously converted +# from $build's *nix or MSYS format). In case (3), returns the w32 file name +# or path in func_cygpath_result (input file name or path is assumed to be in +# Cygwin format). Returns an empty string on error. +# +# ARGS are passed to cygpath, with the last one being the file name or path to +# be converted. +# +# Specify the absolute *nix (or w32) name to cygpath in the LT_CYGPATH +# environment variable; do not put it in $PATH. +func_cygpath () +{ + $opt_debug + if test -n "$LT_CYGPATH" && test -f "$LT_CYGPATH"; then + func_cygpath_result=`$LT_CYGPATH "$@" 2>/dev/null` + if test "$?" -ne 0; then + # on failure, ensure result is empty + func_cygpath_result= + fi + else + func_cygpath_result= + func_error "LT_CYGPATH is empty or specifies non-existent file: \`$LT_CYGPATH'" + fi +} +#end: func_cygpath + + +# func_convert_core_msys_to_w32 ARG +# Convert file name or path ARG from MSYS format to w32 format. Return +# result in func_convert_core_msys_to_w32_result. +func_convert_core_msys_to_w32 () +{ + $opt_debug + # awkward: cmd appends spaces to result + func_convert_core_msys_to_w32_result=`( cmd //c echo "$1" ) 2>/dev/null | + $SED -e 's/[ ]*$//' -e "$lt_sed_naive_backslashify"` +} +#end: func_convert_core_msys_to_w32 + + +# func_convert_file_check ARG1 ARG2 +# Verify that ARG1 (a file name in $build format) was converted to $host +# format in ARG2. Otherwise, emit an error message, but continue (resetting +# func_to_host_file_result to ARG1). +func_convert_file_check () +{ + $opt_debug + if test -z "$2" && test -n "$1" ; then + func_error "Could not determine host file name corresponding to" + func_error " \`$1'" + func_error "Continuing, but uninstalled executables may not work." + # Fallback: + func_to_host_file_result="$1" + fi +} +# end func_convert_file_check + + +# func_convert_path_check FROM_PATHSEP TO_PATHSEP FROM_PATH TO_PATH +# Verify that FROM_PATH (a path in $build format) was converted to $host +# format in TO_PATH. Otherwise, emit an error message, but continue, resetting +# func_to_host_file_result to a simplistic fallback value (see below). +func_convert_path_check () +{ + $opt_debug + if test -z "$4" && test -n "$3"; then + func_error "Could not determine the host path corresponding to" + func_error " \`$3'" + func_error "Continuing, but uninstalled executables may not work." + # Fallback. This is a deliberately simplistic "conversion" and + # should not be "improved". See libtool.info. + if test "x$1" != "x$2"; then + lt_replace_pathsep_chars="s|$1|$2|g" + func_to_host_path_result=`echo "$3" | + $SED -e "$lt_replace_pathsep_chars"` + else + func_to_host_path_result="$3" + fi + fi +} +# end func_convert_path_check + + +# func_convert_path_front_back_pathsep FRONTPAT BACKPAT REPL ORIG +# Modifies func_to_host_path_result by prepending REPL if ORIG matches FRONTPAT +# and appending REPL if ORIG matches BACKPAT. +func_convert_path_front_back_pathsep () +{ + $opt_debug + case $4 in + $1 ) func_to_host_path_result="$3$func_to_host_path_result" + ;; + esac + case $4 in + $2 ) func_append func_to_host_path_result "$3" + ;; + esac +} +# end func_convert_path_front_back_pathsep + + +################################################## +# $build to $host FILE NAME CONVERSION FUNCTIONS # +################################################## +# invoked via `$to_host_file_cmd ARG' +# +# In each case, ARG is the path to be converted from $build to $host format. +# Result will be available in $func_to_host_file_result. + + +# func_to_host_file ARG +# Converts the file name ARG from $build format to $host format. Return result +# in func_to_host_file_result. +func_to_host_file () +{ + $opt_debug + $to_host_file_cmd "$1" +} +# end func_to_host_file + + +# func_to_tool_file ARG LAZY +# converts the file name ARG from $build format to toolchain format. Return +# result in func_to_tool_file_result. If the conversion in use is listed +# in (the comma separated) LAZY, no conversion takes place. +func_to_tool_file () +{ + $opt_debug + case ,$2, in + *,"$to_tool_file_cmd",*) + func_to_tool_file_result=$1 + ;; + *) + $to_tool_file_cmd "$1" + func_to_tool_file_result=$func_to_host_file_result + ;; + esac +} +# end func_to_tool_file + + +# func_convert_file_noop ARG +# Copy ARG to func_to_host_file_result. +func_convert_file_noop () +{ + func_to_host_file_result="$1" +} +# end func_convert_file_noop + + +# func_convert_file_msys_to_w32 ARG +# Convert file name ARG from (mingw) MSYS to (mingw) w32 format; automatic +# conversion to w32 is not available inside the cwrapper. Returns result in +# func_to_host_file_result. +func_convert_file_msys_to_w32 () +{ + $opt_debug + func_to_host_file_result="$1" + if test -n "$1"; then + func_convert_core_msys_to_w32 "$1" + func_to_host_file_result="$func_convert_core_msys_to_w32_result" + fi + func_convert_file_check "$1" "$func_to_host_file_result" +} +# end func_convert_file_msys_to_w32 + + +# func_convert_file_cygwin_to_w32 ARG +# Convert file name ARG from Cygwin to w32 format. Returns result in +# func_to_host_file_result. +func_convert_file_cygwin_to_w32 () +{ + $opt_debug + func_to_host_file_result="$1" + if test -n "$1"; then + # because $build is cygwin, we call "the" cygpath in $PATH; no need to use + # LT_CYGPATH in this case. + func_to_host_file_result=`cygpath -m "$1"` + fi + func_convert_file_check "$1" "$func_to_host_file_result" +} +# end func_convert_file_cygwin_to_w32 + + +# func_convert_file_nix_to_w32 ARG +# Convert file name ARG from *nix to w32 format. Requires a wine environment +# and a working winepath. Returns result in func_to_host_file_result. +func_convert_file_nix_to_w32 () +{ + $opt_debug + func_to_host_file_result="$1" + if test -n "$1"; then + func_convert_core_file_wine_to_w32 "$1" + func_to_host_file_result="$func_convert_core_file_wine_to_w32_result" + fi + func_convert_file_check "$1" "$func_to_host_file_result" +} +# end func_convert_file_nix_to_w32 + + +# func_convert_file_msys_to_cygwin ARG +# Convert file name ARG from MSYS to Cygwin format. Requires LT_CYGPATH set. +# Returns result in func_to_host_file_result. +func_convert_file_msys_to_cygwin () +{ + $opt_debug + func_to_host_file_result="$1" + if test -n "$1"; then + func_convert_core_msys_to_w32 "$1" + func_cygpath -u "$func_convert_core_msys_to_w32_result" + func_to_host_file_result="$func_cygpath_result" + fi + func_convert_file_check "$1" "$func_to_host_file_result" +} +# end func_convert_file_msys_to_cygwin + + +# func_convert_file_nix_to_cygwin ARG +# Convert file name ARG from *nix to Cygwin format. Requires Cygwin installed +# in a wine environment, working winepath, and LT_CYGPATH set. Returns result +# in func_to_host_file_result. +func_convert_file_nix_to_cygwin () +{ + $opt_debug + func_to_host_file_result="$1" + if test -n "$1"; then + # convert from *nix to w32, then use cygpath to convert from w32 to cygwin. + func_convert_core_file_wine_to_w32 "$1" + func_cygpath -u "$func_convert_core_file_wine_to_w32_result" + func_to_host_file_result="$func_cygpath_result" + fi + func_convert_file_check "$1" "$func_to_host_file_result" +} +# end func_convert_file_nix_to_cygwin + + +############################################# +# $build to $host PATH CONVERSION FUNCTIONS # +############################################# +# invoked via `$to_host_path_cmd ARG' +# +# In each case, ARG is the path to be converted from $build to $host format. +# The result will be available in $func_to_host_path_result. +# +# Path separators are also converted from $build format to $host format. If +# ARG begins or ends with a path separator character, it is preserved (but +# converted to $host format) on output. +# +# All path conversion functions are named using the following convention: +# file name conversion function : func_convert_file_X_to_Y () +# path conversion function : func_convert_path_X_to_Y () +# where, for any given $build/$host combination the 'X_to_Y' value is the +# same. If conversion functions are added for new $build/$host combinations, +# the two new functions must follow this pattern, or func_init_to_host_path_cmd +# will break. + + +# func_init_to_host_path_cmd +# Ensures that function "pointer" variable $to_host_path_cmd is set to the +# appropriate value, based on the value of $to_host_file_cmd. +to_host_path_cmd= +func_init_to_host_path_cmd () +{ + $opt_debug + if test -z "$to_host_path_cmd"; then + func_stripname 'func_convert_file_' '' "$to_host_file_cmd" + to_host_path_cmd="func_convert_path_${func_stripname_result}" + fi +} + + +# func_to_host_path ARG +# Converts the path ARG from $build format to $host format. Return result +# in func_to_host_path_result. +func_to_host_path () +{ + $opt_debug + func_init_to_host_path_cmd + $to_host_path_cmd "$1" +} +# end func_to_host_path + + +# func_convert_path_noop ARG +# Copy ARG to func_to_host_path_result. +func_convert_path_noop () +{ + func_to_host_path_result="$1" +} +# end func_convert_path_noop + + +# func_convert_path_msys_to_w32 ARG +# Convert path ARG from (mingw) MSYS to (mingw) w32 format; automatic +# conversion to w32 is not available inside the cwrapper. Returns result in +# func_to_host_path_result. +func_convert_path_msys_to_w32 () +{ + $opt_debug + func_to_host_path_result="$1" + if test -n "$1"; then + # Remove leading and trailing path separator characters from ARG. MSYS + # behavior is inconsistent here; cygpath turns them into '.;' and ';.'; + # and winepath ignores them completely. + func_stripname : : "$1" + func_to_host_path_tmp1=$func_stripname_result + func_convert_core_msys_to_w32 "$func_to_host_path_tmp1" + func_to_host_path_result="$func_convert_core_msys_to_w32_result" + func_convert_path_check : ";" \ + "$func_to_host_path_tmp1" "$func_to_host_path_result" + func_convert_path_front_back_pathsep ":*" "*:" ";" "$1" + fi +} +# end func_convert_path_msys_to_w32 + + +# func_convert_path_cygwin_to_w32 ARG +# Convert path ARG from Cygwin to w32 format. Returns result in +# func_to_host_file_result. +func_convert_path_cygwin_to_w32 () +{ + $opt_debug + func_to_host_path_result="$1" + if test -n "$1"; then + # See func_convert_path_msys_to_w32: + func_stripname : : "$1" + func_to_host_path_tmp1=$func_stripname_result + func_to_host_path_result=`cygpath -m -p "$func_to_host_path_tmp1"` + func_convert_path_check : ";" \ + "$func_to_host_path_tmp1" "$func_to_host_path_result" + func_convert_path_front_back_pathsep ":*" "*:" ";" "$1" + fi +} +# end func_convert_path_cygwin_to_w32 + + +# func_convert_path_nix_to_w32 ARG +# Convert path ARG from *nix to w32 format. Requires a wine environment and +# a working winepath. Returns result in func_to_host_file_result. +func_convert_path_nix_to_w32 () +{ + $opt_debug + func_to_host_path_result="$1" + if test -n "$1"; then + # See func_convert_path_msys_to_w32: + func_stripname : : "$1" + func_to_host_path_tmp1=$func_stripname_result + func_convert_core_path_wine_to_w32 "$func_to_host_path_tmp1" + func_to_host_path_result="$func_convert_core_path_wine_to_w32_result" + func_convert_path_check : ";" \ + "$func_to_host_path_tmp1" "$func_to_host_path_result" + func_convert_path_front_back_pathsep ":*" "*:" ";" "$1" + fi +} +# end func_convert_path_nix_to_w32 + + +# func_convert_path_msys_to_cygwin ARG +# Convert path ARG from MSYS to Cygwin format. Requires LT_CYGPATH set. +# Returns result in func_to_host_file_result. +func_convert_path_msys_to_cygwin () +{ + $opt_debug + func_to_host_path_result="$1" + if test -n "$1"; then + # See func_convert_path_msys_to_w32: + func_stripname : : "$1" + func_to_host_path_tmp1=$func_stripname_result + func_convert_core_msys_to_w32 "$func_to_host_path_tmp1" + func_cygpath -u -p "$func_convert_core_msys_to_w32_result" + func_to_host_path_result="$func_cygpath_result" + func_convert_path_check : : \ + "$func_to_host_path_tmp1" "$func_to_host_path_result" + func_convert_path_front_back_pathsep ":*" "*:" : "$1" + fi +} +# end func_convert_path_msys_to_cygwin + + +# func_convert_path_nix_to_cygwin ARG +# Convert path ARG from *nix to Cygwin format. Requires Cygwin installed in a +# a wine environment, working winepath, and LT_CYGPATH set. Returns result in +# func_to_host_file_result. +func_convert_path_nix_to_cygwin () +{ + $opt_debug + func_to_host_path_result="$1" + if test -n "$1"; then + # Remove leading and trailing path separator characters from + # ARG. msys behavior is inconsistent here, cygpath turns them + # into '.;' and ';.', and winepath ignores them completely. + func_stripname : : "$1" + func_to_host_path_tmp1=$func_stripname_result + func_convert_core_path_wine_to_w32 "$func_to_host_path_tmp1" + func_cygpath -u -p "$func_convert_core_path_wine_to_w32_result" + func_to_host_path_result="$func_cygpath_result" + func_convert_path_check : : \ + "$func_to_host_path_tmp1" "$func_to_host_path_result" + func_convert_path_front_back_pathsep ":*" "*:" : "$1" + fi +} +# end func_convert_path_nix_to_cygwin + + +# func_mode_compile arg... +func_mode_compile () +{ + $opt_debug + # Get the compilation command and the source file. + base_compile= + srcfile="$nonopt" # always keep a non-empty value in "srcfile" + suppress_opt=yes + suppress_output= + arg_mode=normal + libobj= + later= + pie_flag= + + for arg + do + case $arg_mode in + arg ) + # do not "continue". Instead, add this to base_compile + lastarg="$arg" + arg_mode=normal + ;; + + target ) + libobj="$arg" + arg_mode=normal + continue + ;; + + normal ) + # Accept any command-line options. + case $arg in + -o) + test -n "$libobj" && \ + func_fatal_error "you cannot specify \`-o' more than once" + arg_mode=target + continue + ;; + + -pie | -fpie | -fPIE) + func_append pie_flag " $arg" + continue + ;; + + -shared | -static | -prefer-pic | -prefer-non-pic) + func_append later " $arg" + continue + ;; + + -no-suppress) + suppress_opt=no + continue + ;; + + -Xcompiler) + arg_mode=arg # the next one goes into the "base_compile" arg list + continue # The current "srcfile" will either be retained or + ;; # replaced later. I would guess that would be a bug. + + -Wc,*) + func_stripname '-Wc,' '' "$arg" + args=$func_stripname_result + lastarg= + save_ifs="$IFS"; IFS=',' + for arg in $args; do + IFS="$save_ifs" + func_append_quoted lastarg "$arg" + done + IFS="$save_ifs" + func_stripname ' ' '' "$lastarg" + lastarg=$func_stripname_result + + # Add the arguments to base_compile. + func_append base_compile " $lastarg" + continue + ;; + + *) + # Accept the current argument as the source file. + # The previous "srcfile" becomes the current argument. + # + lastarg="$srcfile" + srcfile="$arg" + ;; + esac # case $arg + ;; + esac # case $arg_mode + + # Aesthetically quote the previous argument. + func_append_quoted base_compile "$lastarg" + done # for arg + + case $arg_mode in + arg) + func_fatal_error "you must specify an argument for -Xcompile" + ;; + target) + func_fatal_error "you must specify a target with \`-o'" + ;; + *) + # Get the name of the library object. + test -z "$libobj" && { + func_basename "$srcfile" + libobj="$func_basename_result" + } + ;; + esac + + # Recognize several different file suffixes. + # If the user specifies -o file.o, it is replaced with file.lo + case $libobj in + *.[cCFSifmso] | \ + *.ada | *.adb | *.ads | *.asm | \ + *.c++ | *.cc | *.ii | *.class | *.cpp | *.cxx | \ + *.[fF][09]? | *.for | *.java | *.go | *.obj | *.sx | *.cu | *.cup) + func_xform "$libobj" + libobj=$func_xform_result + ;; + esac + + case $libobj in + *.lo) func_lo2o "$libobj"; obj=$func_lo2o_result ;; + *) + func_fatal_error "cannot determine name of library object from \`$libobj'" + ;; + esac + + func_infer_tag $base_compile + + for arg in $later; do + case $arg in + -shared) + test "$build_libtool_libs" != yes && \ + func_fatal_configuration "can not build a shared library" + build_old_libs=no + continue + ;; + + -static) + build_libtool_libs=no + build_old_libs=yes + continue + ;; + + -prefer-pic) + pic_mode=yes + continue + ;; + + -prefer-non-pic) + pic_mode=no + continue + ;; + esac + done + + func_quote_for_eval "$libobj" + test "X$libobj" != "X$func_quote_for_eval_result" \ + && $ECHO "X$libobj" | $GREP '[]~#^*{};<>?"'"'"' &()|`$[]' \ + && func_warning "libobj name \`$libobj' may not contain shell special characters." + func_dirname_and_basename "$obj" "/" "" + objname="$func_basename_result" + xdir="$func_dirname_result" + lobj=${xdir}$objdir/$objname + + test -z "$base_compile" && \ + func_fatal_help "you must specify a compilation command" + + # Delete any leftover library objects. + if test "$build_old_libs" = yes; then + removelist="$obj $lobj $libobj ${libobj}T" + else + removelist="$lobj $libobj ${libobj}T" + fi + + # On Cygwin there's no "real" PIC flag so we must build both object types + case $host_os in + cygwin* | mingw* | pw32* | os2* | cegcc*) + pic_mode=default + ;; + esac + if test "$pic_mode" = no && test "$deplibs_check_method" != pass_all; then + # non-PIC code in shared libraries is not supported + pic_mode=default + fi + + # Calculate the filename of the output object if compiler does + # not support -o with -c + if test "$compiler_c_o" = no; then + output_obj=`$ECHO "$srcfile" | $SED 's%^.*/%%; s%\.[^.]*$%%'`.${objext} + lockfile="$output_obj.lock" + else + output_obj= + need_locks=no + lockfile= + fi + + # Lock this critical section if it is needed + # We use this script file to make the link, it avoids creating a new file + if test "$need_locks" = yes; then + until $opt_dry_run || ln "$progpath" "$lockfile" 2>/dev/null; do + func_echo "Waiting for $lockfile to be removed" + sleep 2 + done + elif test "$need_locks" = warn; then + if test -f "$lockfile"; then + $ECHO "\ +*** ERROR, $lockfile exists and contains: +`cat $lockfile 2>/dev/null` + +This indicates that another process is trying to use the same +temporary object file, and libtool could not work around it because +your compiler does not support \`-c' and \`-o' together. If you +repeat this compilation, it may succeed, by chance, but you had better +avoid parallel builds (make -j) in this platform, or get a better +compiler." + + $opt_dry_run || $RM $removelist + exit $EXIT_FAILURE + fi + func_append removelist " $output_obj" + $ECHO "$srcfile" > "$lockfile" + fi + + $opt_dry_run || $RM $removelist + func_append removelist " $lockfile" + trap '$opt_dry_run || $RM $removelist; exit $EXIT_FAILURE' 1 2 15 + + func_to_tool_file "$srcfile" func_convert_file_msys_to_w32 + srcfile=$func_to_tool_file_result + func_quote_for_eval "$srcfile" + qsrcfile=$func_quote_for_eval_result + + # Only build a PIC object if we are building libtool libraries. + if test "$build_libtool_libs" = yes; then + # Without this assignment, base_compile gets emptied. + fbsd_hideous_sh_bug=$base_compile + + if test "$pic_mode" != no; then + command="$base_compile $qsrcfile $pic_flag" + else + # Don't build PIC code + command="$base_compile $qsrcfile" + fi + + func_mkdir_p "$xdir$objdir" + + if test -z "$output_obj"; then + # Place PIC objects in $objdir + func_append command " -o $lobj" + fi + + func_show_eval_locale "$command" \ + 'test -n "$output_obj" && $RM $removelist; exit $EXIT_FAILURE' + + if test "$need_locks" = warn && + test "X`cat $lockfile 2>/dev/null`" != "X$srcfile"; then + $ECHO "\ +*** ERROR, $lockfile contains: +`cat $lockfile 2>/dev/null` + +but it should contain: +$srcfile + +This indicates that another process is trying to use the same +temporary object file, and libtool could not work around it because +your compiler does not support \`-c' and \`-o' together. If you +repeat this compilation, it may succeed, by chance, but you had better +avoid parallel builds (make -j) in this platform, or get a better +compiler." + + $opt_dry_run || $RM $removelist + exit $EXIT_FAILURE + fi + + # Just move the object if needed, then go on to compile the next one + if test -n "$output_obj" && test "X$output_obj" != "X$lobj"; then + func_show_eval '$MV "$output_obj" "$lobj"' \ + 'error=$?; $opt_dry_run || $RM $removelist; exit $error' + fi + + # Allow error messages only from the first compilation. + if test "$suppress_opt" = yes; then + suppress_output=' >/dev/null 2>&1' + fi + fi + + # Only build a position-dependent object if we build old libraries. + if test "$build_old_libs" = yes; then + if test "$pic_mode" != yes; then + # Don't build PIC code + command="$base_compile $qsrcfile$pie_flag" + else + command="$base_compile $qsrcfile $pic_flag" + fi + if test "$compiler_c_o" = yes; then + func_append command " -o $obj" + fi + + # Suppress compiler output if we already did a PIC compilation. + func_append command "$suppress_output" + func_show_eval_locale "$command" \ + '$opt_dry_run || $RM $removelist; exit $EXIT_FAILURE' + + if test "$need_locks" = warn && + test "X`cat $lockfile 2>/dev/null`" != "X$srcfile"; then + $ECHO "\ +*** ERROR, $lockfile contains: +`cat $lockfile 2>/dev/null` + +but it should contain: +$srcfile + +This indicates that another process is trying to use the same +temporary object file, and libtool could not work around it because +your compiler does not support \`-c' and \`-o' together. If you +repeat this compilation, it may succeed, by chance, but you had better +avoid parallel builds (make -j) in this platform, or get a better +compiler." + + $opt_dry_run || $RM $removelist + exit $EXIT_FAILURE + fi + + # Just move the object if needed + if test -n "$output_obj" && test "X$output_obj" != "X$obj"; then + func_show_eval '$MV "$output_obj" "$obj"' \ + 'error=$?; $opt_dry_run || $RM $removelist; exit $error' + fi + fi + + $opt_dry_run || { + func_write_libtool_object "$libobj" "$objdir/$objname" "$objname" + + # Unlock the critical section if it was locked + if test "$need_locks" != no; then + removelist=$lockfile + $RM "$lockfile" + fi + } + + exit $EXIT_SUCCESS +} + +$opt_help || { + test "$opt_mode" = compile && func_mode_compile ${1+"$@"} +} + +func_mode_help () +{ + # We need to display help for each of the modes. + case $opt_mode in + "") + # Generic help is extracted from the usage comments + # at the start of this file. + func_help + ;; + + clean) + $ECHO \ +"Usage: $progname [OPTION]... --mode=clean RM [RM-OPTION]... FILE... + +Remove files from the build directory. + +RM is the name of the program to use to delete files associated with each FILE +(typically \`/bin/rm'). RM-OPTIONS are options (such as \`-f') to be passed +to RM. + +If FILE is a libtool library, object or program, all the files associated +with it are deleted. Otherwise, only FILE itself is deleted using RM." + ;; + + compile) + $ECHO \ +"Usage: $progname [OPTION]... --mode=compile COMPILE-COMMAND... SOURCEFILE + +Compile a source file into a libtool library object. + +This mode accepts the following additional options: + + -o OUTPUT-FILE set the output file name to OUTPUT-FILE + -no-suppress do not suppress compiler output for multiple passes + -prefer-pic try to build PIC objects only + -prefer-non-pic try to build non-PIC objects only + -shared do not build a \`.o' file suitable for static linking + -static only build a \`.o' file suitable for static linking + -Wc,FLAG pass FLAG directly to the compiler + +COMPILE-COMMAND is a command to be used in creating a \`standard' object file +from the given SOURCEFILE. + +The output file name is determined by removing the directory component from +SOURCEFILE, then substituting the C source code suffix \`.c' with the +library object suffix, \`.lo'." + ;; + + execute) + $ECHO \ +"Usage: $progname [OPTION]... --mode=execute COMMAND [ARGS]... + +Automatically set library path, then run a program. + +This mode accepts the following additional options: + + -dlopen FILE add the directory containing FILE to the library path + +This mode sets the library path environment variable according to \`-dlopen' +flags. + +If any of the ARGS are libtool executable wrappers, then they are translated +into their corresponding uninstalled binary, and any of their required library +directories are added to the library path. + +Then, COMMAND is executed, with ARGS as arguments." + ;; + + finish) + $ECHO \ +"Usage: $progname [OPTION]... --mode=finish [LIBDIR]... + +Complete the installation of libtool libraries. + +Each LIBDIR is a directory that contains libtool libraries. + +The commands that this mode executes may require superuser privileges. Use +the \`--dry-run' option if you just want to see what would be executed." + ;; + + install) + $ECHO \ +"Usage: $progname [OPTION]... --mode=install INSTALL-COMMAND... + +Install executables or libraries. + +INSTALL-COMMAND is the installation command. The first component should be +either the \`install' or \`cp' program. + +The following components of INSTALL-COMMAND are treated specially: + + -inst-prefix-dir PREFIX-DIR Use PREFIX-DIR as a staging area for installation + +The rest of the components are interpreted as arguments to that command (only +BSD-compatible install options are recognized)." + ;; + + link) + $ECHO \ +"Usage: $progname [OPTION]... --mode=link LINK-COMMAND... + +Link object files or libraries together to form another library, or to +create an executable program. + +LINK-COMMAND is a command using the C compiler that you would use to create +a program from several object files. + +The following components of LINK-COMMAND are treated specially: + + -all-static do not do any dynamic linking at all + -avoid-version do not add a version suffix if possible + -bindir BINDIR specify path to binaries directory (for systems where + libraries must be found in the PATH setting at runtime) + -dlopen FILE \`-dlpreopen' FILE if it cannot be dlopened at runtime + -dlpreopen FILE link in FILE and add its symbols to lt_preloaded_symbols + -export-dynamic allow symbols from OUTPUT-FILE to be resolved with dlsym(3) + -export-symbols SYMFILE + try to export only the symbols listed in SYMFILE + -export-symbols-regex REGEX + try to export only the symbols matching REGEX + -LLIBDIR search LIBDIR for required installed libraries + -lNAME OUTPUT-FILE requires the installed library libNAME + -module build a library that can dlopened + -no-fast-install disable the fast-install mode + -no-install link a not-installable executable + -no-undefined declare that a library does not refer to external symbols + -o OUTPUT-FILE create OUTPUT-FILE from the specified objects + -objectlist FILE Use a list of object files found in FILE to specify objects + -precious-files-regex REGEX + don't remove output files matching REGEX + -release RELEASE specify package release information + -rpath LIBDIR the created library will eventually be installed in LIBDIR + -R[ ]LIBDIR add LIBDIR to the runtime path of programs and libraries + -shared only do dynamic linking of libtool libraries + -shrext SUFFIX override the standard shared library file extension + -static do not do any dynamic linking of uninstalled libtool libraries + -static-libtool-libs + do not do any dynamic linking of libtool libraries + -version-info CURRENT[:REVISION[:AGE]] + specify library version info [each variable defaults to 0] + -weak LIBNAME declare that the target provides the LIBNAME interface + -Wc,FLAG + -Xcompiler FLAG pass linker-specific FLAG directly to the compiler + -Wl,FLAG + -Xlinker FLAG pass linker-specific FLAG directly to the linker + -XCClinker FLAG pass link-specific FLAG to the compiler driver (CC) + +All other options (arguments beginning with \`-') are ignored. + +Every other argument is treated as a filename. Files ending in \`.la' are +treated as uninstalled libtool libraries, other files are standard or library +object files. + +If the OUTPUT-FILE ends in \`.la', then a libtool library is created, +only library objects (\`.lo' files) may be specified, and \`-rpath' is +required, except when creating a convenience library. + +If OUTPUT-FILE ends in \`.a' or \`.lib', then a standard library is created +using \`ar' and \`ranlib', or on Windows using \`lib'. + +If OUTPUT-FILE ends in \`.lo' or \`.${objext}', then a reloadable object file +is created, otherwise an executable program is created." + ;; + + uninstall) + $ECHO \ +"Usage: $progname [OPTION]... --mode=uninstall RM [RM-OPTION]... FILE... + +Remove libraries from an installation directory. + +RM is the name of the program to use to delete files associated with each FILE +(typically \`/bin/rm'). RM-OPTIONS are options (such as \`-f') to be passed +to RM. + +If FILE is a libtool library, all the files associated with it are deleted. +Otherwise, only FILE itself is deleted using RM." + ;; + + *) + func_fatal_help "invalid operation mode \`$opt_mode'" + ;; + esac + + echo + $ECHO "Try \`$progname --help' for more information about other modes." +} + +# Now that we've collected a possible --mode arg, show help if necessary +if $opt_help; then + if test "$opt_help" = :; then + func_mode_help + else + { + func_help noexit + for opt_mode in compile link execute install finish uninstall clean; do + func_mode_help + done + } | sed -n '1p; 2,$s/^Usage:/ or: /p' + { + func_help noexit + for opt_mode in compile link execute install finish uninstall clean; do + echo + func_mode_help + done + } | + sed '1d + /^When reporting/,/^Report/{ + H + d + } + $x + /information about other modes/d + /more detailed .*MODE/d + s/^Usage:.*--mode=\([^ ]*\) .*/Description of \1 mode:/' + fi + exit $? +fi + + +# func_mode_execute arg... +func_mode_execute () +{ + $opt_debug + # The first argument is the command name. + cmd="$nonopt" + test -z "$cmd" && \ + func_fatal_help "you must specify a COMMAND" + + # Handle -dlopen flags immediately. + for file in $opt_dlopen; do + test -f "$file" \ + || func_fatal_help "\`$file' is not a file" + + dir= + case $file in + *.la) + func_resolve_sysroot "$file" + file=$func_resolve_sysroot_result + + # Check to see that this really is a libtool archive. + func_lalib_unsafe_p "$file" \ + || func_fatal_help "\`$lib' is not a valid libtool archive" + + # Read the libtool library. + dlname= + library_names= + func_source "$file" + + # Skip this library if it cannot be dlopened. + if test -z "$dlname"; then + # Warn if it was a shared library. + test -n "$library_names" && \ + func_warning "\`$file' was not linked with \`-export-dynamic'" + continue + fi + + func_dirname "$file" "" "." + dir="$func_dirname_result" + + if test -f "$dir/$objdir/$dlname"; then + func_append dir "/$objdir" + else + if test ! -f "$dir/$dlname"; then + func_fatal_error "cannot find \`$dlname' in \`$dir' or \`$dir/$objdir'" + fi + fi + ;; + + *.lo) + # Just add the directory containing the .lo file. + func_dirname "$file" "" "." + dir="$func_dirname_result" + ;; + + *) + func_warning "\`-dlopen' is ignored for non-libtool libraries and objects" + continue + ;; + esac + + # Get the absolute pathname. + absdir=`cd "$dir" && pwd` + test -n "$absdir" && dir="$absdir" + + # Now add the directory to shlibpath_var. + if eval "test -z \"\$$shlibpath_var\""; then + eval "$shlibpath_var=\"\$dir\"" + else + eval "$shlibpath_var=\"\$dir:\$$shlibpath_var\"" + fi + done + + # This variable tells wrapper scripts just to set shlibpath_var + # rather than running their programs. + libtool_execute_magic="$magic" + + # Check if any of the arguments is a wrapper script. + args= + for file + do + case $file in + -* | *.la | *.lo ) ;; + *) + # Do a test to see if this is really a libtool program. + if func_ltwrapper_script_p "$file"; then + func_source "$file" + # Transform arg to wrapped name. + file="$progdir/$program" + elif func_ltwrapper_executable_p "$file"; then + func_ltwrapper_scriptname "$file" + func_source "$func_ltwrapper_scriptname_result" + # Transform arg to wrapped name. + file="$progdir/$program" + fi + ;; + esac + # Quote arguments (to preserve shell metacharacters). + func_append_quoted args "$file" + done + + if test "X$opt_dry_run" = Xfalse; then + if test -n "$shlibpath_var"; then + # Export the shlibpath_var. + eval "export $shlibpath_var" + fi + + # Restore saved environment variables + for lt_var in LANG LANGUAGE LC_ALL LC_CTYPE LC_COLLATE LC_MESSAGES + do + eval "if test \"\${save_$lt_var+set}\" = set; then + $lt_var=\$save_$lt_var; export $lt_var + else + $lt_unset $lt_var + fi" + done + + # Now prepare to actually exec the command. + exec_cmd="\$cmd$args" + else + # Display what would be done. + if test -n "$shlibpath_var"; then + eval "\$ECHO \"\$shlibpath_var=\$$shlibpath_var\"" + echo "export $shlibpath_var" + fi + $ECHO "$cmd$args" + exit $EXIT_SUCCESS + fi +} + +test "$opt_mode" = execute && func_mode_execute ${1+"$@"} + + +# func_mode_finish arg... +func_mode_finish () +{ + $opt_debug + libs= + libdirs= + admincmds= + + for opt in "$nonopt" ${1+"$@"} + do + if test -d "$opt"; then + func_append libdirs " $opt" + + elif test -f "$opt"; then + if func_lalib_unsafe_p "$opt"; then + func_append libs " $opt" + else + func_warning "\`$opt' is not a valid libtool archive" + fi + + else + func_fatal_error "invalid argument \`$opt'" + fi + done + + if test -n "$libs"; then + if test -n "$lt_sysroot"; then + sysroot_regex=`$ECHO "$lt_sysroot" | $SED "$sed_make_literal_regex"` + sysroot_cmd="s/\([ ']\)$sysroot_regex/\1/g;" + else + sysroot_cmd= + fi + + # Remove sysroot references + if $opt_dry_run; then + for lib in $libs; do + echo "removing references to $lt_sysroot and \`=' prefixes from $lib" + done + else + tmpdir=`func_mktempdir` + for lib in $libs; do + sed -e "${sysroot_cmd} s/\([ ']-[LR]\)=/\1/g; s/\([ ']\)=/\1/g" $lib \ + > $tmpdir/tmp-la + mv -f $tmpdir/tmp-la $lib + done + ${RM}r "$tmpdir" + fi + fi + + if test -n "$finish_cmds$finish_eval" && test -n "$libdirs"; then + for libdir in $libdirs; do + if test -n "$finish_cmds"; then + # Do each command in the finish commands. + func_execute_cmds "$finish_cmds" 'admincmds="$admincmds +'"$cmd"'"' + fi + if test -n "$finish_eval"; then + # Do the single finish_eval. + eval cmds=\"$finish_eval\" + $opt_dry_run || eval "$cmds" || func_append admincmds " + $cmds" + fi + done + fi + + # Exit here if they wanted silent mode. + $opt_silent && exit $EXIT_SUCCESS + + if test -n "$finish_cmds$finish_eval" && test -n "$libdirs"; then + echo "----------------------------------------------------------------------" + echo "Libraries have been installed in:" + for libdir in $libdirs; do + $ECHO " $libdir" + done + echo + echo "If you ever happen to want to link against installed libraries" + echo "in a given directory, LIBDIR, you must either use libtool, and" + echo "specify the full pathname of the library, or use the \`-LLIBDIR'" + echo "flag during linking and do at least one of the following:" + if test -n "$shlibpath_var"; then + echo " - add LIBDIR to the \`$shlibpath_var' environment variable" + echo " during execution" + fi + if test -n "$runpath_var"; then + echo " - add LIBDIR to the \`$runpath_var' environment variable" + echo " during linking" + fi + if test -n "$hardcode_libdir_flag_spec"; then + libdir=LIBDIR + eval flag=\"$hardcode_libdir_flag_spec\" + + $ECHO " - use the \`$flag' linker flag" + fi + if test -n "$admincmds"; then + $ECHO " - have your system administrator run these commands:$admincmds" + fi + if test -f /etc/ld.so.conf; then + echo " - have your system administrator add LIBDIR to \`/etc/ld.so.conf'" + fi + echo + + echo "See any operating system documentation about shared libraries for" + case $host in + solaris2.[6789]|solaris2.1[0-9]) + echo "more information, such as the ld(1), crle(1) and ld.so(8) manual" + echo "pages." + ;; + *) + echo "more information, such as the ld(1) and ld.so(8) manual pages." + ;; + esac + echo "----------------------------------------------------------------------" + fi + exit $EXIT_SUCCESS +} + +test "$opt_mode" = finish && func_mode_finish ${1+"$@"} + + +# func_mode_install arg... +func_mode_install () +{ + $opt_debug + # There may be an optional sh(1) argument at the beginning of + # install_prog (especially on Windows NT). + if test "$nonopt" = "$SHELL" || test "$nonopt" = /bin/sh || + # Allow the use of GNU shtool's install command. + case $nonopt in *shtool*) :;; *) false;; esac; then + # Aesthetically quote it. + func_quote_for_eval "$nonopt" + install_prog="$func_quote_for_eval_result " + arg=$1 + shift + else + install_prog= + arg=$nonopt + fi + + # The real first argument should be the name of the installation program. + # Aesthetically quote it. + func_quote_for_eval "$arg" + func_append install_prog "$func_quote_for_eval_result" + install_shared_prog=$install_prog + case " $install_prog " in + *[\\\ /]cp\ *) install_cp=: ;; + *) install_cp=false ;; + esac + + # We need to accept at least all the BSD install flags. + dest= + files= + opts= + prev= + install_type= + isdir=no + stripme= + no_mode=: + for arg + do + arg2= + if test -n "$dest"; then + func_append files " $dest" + dest=$arg + continue + fi + + case $arg in + -d) isdir=yes ;; + -f) + if $install_cp; then :; else + prev=$arg + fi + ;; + -g | -m | -o) + prev=$arg + ;; + -s) + stripme=" -s" + continue + ;; + -*) + ;; + *) + # If the previous option needed an argument, then skip it. + if test -n "$prev"; then + if test "x$prev" = x-m && test -n "$install_override_mode"; then + arg2=$install_override_mode + no_mode=false + fi + prev= + else + dest=$arg + continue + fi + ;; + esac + + # Aesthetically quote the argument. + func_quote_for_eval "$arg" + func_append install_prog " $func_quote_for_eval_result" + if test -n "$arg2"; then + func_quote_for_eval "$arg2" + fi + func_append install_shared_prog " $func_quote_for_eval_result" + done + + test -z "$install_prog" && \ + func_fatal_help "you must specify an install program" + + test -n "$prev" && \ + func_fatal_help "the \`$prev' option requires an argument" + + if test -n "$install_override_mode" && $no_mode; then + if $install_cp; then :; else + func_quote_for_eval "$install_override_mode" + func_append install_shared_prog " -m $func_quote_for_eval_result" + fi + fi + + if test -z "$files"; then + if test -z "$dest"; then + func_fatal_help "no file or destination specified" + else + func_fatal_help "you must specify a destination" + fi + fi + + # Strip any trailing slash from the destination. + func_stripname '' '/' "$dest" + dest=$func_stripname_result + + # Check to see that the destination is a directory. + test -d "$dest" && isdir=yes + if test "$isdir" = yes; then + destdir="$dest" + destname= + else + func_dirname_and_basename "$dest" "" "." + destdir="$func_dirname_result" + destname="$func_basename_result" + + # Not a directory, so check to see that there is only one file specified. + set dummy $files; shift + test "$#" -gt 1 && \ + func_fatal_help "\`$dest' is not a directory" + fi + case $destdir in + [\\/]* | [A-Za-z]:[\\/]*) ;; + *) + for file in $files; do + case $file in + *.lo) ;; + *) + func_fatal_help "\`$destdir' must be an absolute directory name" + ;; + esac + done + ;; + esac + + # This variable tells wrapper scripts just to set variables rather + # than running their programs. + libtool_install_magic="$magic" + + staticlibs= + future_libdirs= + current_libdirs= + for file in $files; do + + # Do each installation. + case $file in + *.$libext) + # Do the static libraries later. + func_append staticlibs " $file" + ;; + + *.la) + func_resolve_sysroot "$file" + file=$func_resolve_sysroot_result + + # Check to see that this really is a libtool archive. + func_lalib_unsafe_p "$file" \ + || func_fatal_help "\`$file' is not a valid libtool archive" + + library_names= + old_library= + relink_command= + func_source "$file" + + # Add the libdir to current_libdirs if it is the destination. + if test "X$destdir" = "X$libdir"; then + case "$current_libdirs " in + *" $libdir "*) ;; + *) func_append current_libdirs " $libdir" ;; + esac + else + # Note the libdir as a future libdir. + case "$future_libdirs " in + *" $libdir "*) ;; + *) func_append future_libdirs " $libdir" ;; + esac + fi + + func_dirname "$file" "/" "" + dir="$func_dirname_result" + func_append dir "$objdir" + + if test -n "$relink_command"; then + # Determine the prefix the user has applied to our future dir. + inst_prefix_dir=`$ECHO "$destdir" | $SED -e "s%$libdir\$%%"` + + # Don't allow the user to place us outside of our expected + # location b/c this prevents finding dependent libraries that + # are installed to the same prefix. + # At present, this check doesn't affect windows .dll's that + # are installed into $libdir/../bin (currently, that works fine) + # but it's something to keep an eye on. + test "$inst_prefix_dir" = "$destdir" && \ + func_fatal_error "error: cannot install \`$file' to a directory not ending in $libdir" + + if test -n "$inst_prefix_dir"; then + # Stick the inst_prefix_dir data into the link command. + relink_command=`$ECHO "$relink_command" | $SED "s%@inst_prefix_dir@%-inst-prefix-dir $inst_prefix_dir%"` + else + relink_command=`$ECHO "$relink_command" | $SED "s%@inst_prefix_dir@%%"` + fi + + func_warning "relinking \`$file'" + func_show_eval "$relink_command" \ + 'func_fatal_error "error: relink \`$file'\'' with the above command before installing it"' + fi + + # See the names of the shared library. + set dummy $library_names; shift + if test -n "$1"; then + realname="$1" + shift + + srcname="$realname" + test -n "$relink_command" && srcname="$realname"T + + # Install the shared library and build the symlinks. + func_show_eval "$install_shared_prog $dir/$srcname $destdir/$realname" \ + 'exit $?' + tstripme="$stripme" + case $host_os in + cygwin* | mingw* | pw32* | cegcc*) + case $realname in + *.dll.a) + tstripme="" + ;; + esac + ;; + esac + if test -n "$tstripme" && test -n "$striplib"; then + func_show_eval "$striplib $destdir/$realname" 'exit $?' + fi + + if test "$#" -gt 0; then + # Delete the old symlinks, and create new ones. + # Try `ln -sf' first, because the `ln' binary might depend on + # the symlink we replace! Solaris /bin/ln does not understand -f, + # so we also need to try rm && ln -s. + for linkname + do + test "$linkname" != "$realname" \ + && func_show_eval "(cd $destdir && { $LN_S -f $realname $linkname || { $RM $linkname && $LN_S $realname $linkname; }; })" + done + fi + + # Do each command in the postinstall commands. + lib="$destdir/$realname" + func_execute_cmds "$postinstall_cmds" 'exit $?' + fi + + # Install the pseudo-library for information purposes. + func_basename "$file" + name="$func_basename_result" + instname="$dir/$name"i + func_show_eval "$install_prog $instname $destdir/$name" 'exit $?' + + # Maybe install the static library, too. + test -n "$old_library" && func_append staticlibs " $dir/$old_library" + ;; + + *.lo) + # Install (i.e. copy) a libtool object. + + # Figure out destination file name, if it wasn't already specified. + if test -n "$destname"; then + destfile="$destdir/$destname" + else + func_basename "$file" + destfile="$func_basename_result" + destfile="$destdir/$destfile" + fi + + # Deduce the name of the destination old-style object file. + case $destfile in + *.lo) + func_lo2o "$destfile" + staticdest=$func_lo2o_result + ;; + *.$objext) + staticdest="$destfile" + destfile= + ;; + *) + func_fatal_help "cannot copy a libtool object to \`$destfile'" + ;; + esac + + # Install the libtool object if requested. + test -n "$destfile" && \ + func_show_eval "$install_prog $file $destfile" 'exit $?' + + # Install the old object if enabled. + if test "$build_old_libs" = yes; then + # Deduce the name of the old-style object file. + func_lo2o "$file" + staticobj=$func_lo2o_result + func_show_eval "$install_prog \$staticobj \$staticdest" 'exit $?' + fi + exit $EXIT_SUCCESS + ;; + + *) + # Figure out destination file name, if it wasn't already specified. + if test -n "$destname"; then + destfile="$destdir/$destname" + else + func_basename "$file" + destfile="$func_basename_result" + destfile="$destdir/$destfile" + fi + + # If the file is missing, and there is a .exe on the end, strip it + # because it is most likely a libtool script we actually want to + # install + stripped_ext="" + case $file in + *.exe) + if test ! -f "$file"; then + func_stripname '' '.exe' "$file" + file=$func_stripname_result + stripped_ext=".exe" + fi + ;; + esac + + # Do a test to see if this is really a libtool program. + case $host in + *cygwin* | *mingw*) + if func_ltwrapper_executable_p "$file"; then + func_ltwrapper_scriptname "$file" + wrapper=$func_ltwrapper_scriptname_result + else + func_stripname '' '.exe' "$file" + wrapper=$func_stripname_result + fi + ;; + *) + wrapper=$file + ;; + esac + if func_ltwrapper_script_p "$wrapper"; then + notinst_deplibs= + relink_command= + + func_source "$wrapper" + + # Check the variables that should have been set. + test -z "$generated_by_libtool_version" && \ + func_fatal_error "invalid libtool wrapper script \`$wrapper'" + + finalize=yes + for lib in $notinst_deplibs; do + # Check to see that each library is installed. + libdir= + if test -f "$lib"; then + func_source "$lib" + fi + libfile="$libdir/"`$ECHO "$lib" | $SED 's%^.*/%%g'` ### testsuite: skip nested quoting test + if test -n "$libdir" && test ! -f "$libfile"; then + func_warning "\`$lib' has not been installed in \`$libdir'" + finalize=no + fi + done + + relink_command= + func_source "$wrapper" + + outputname= + if test "$fast_install" = no && test -n "$relink_command"; then + $opt_dry_run || { + if test "$finalize" = yes; then + tmpdir=`func_mktempdir` + func_basename "$file$stripped_ext" + file="$func_basename_result" + outputname="$tmpdir/$file" + # Replace the output file specification. + relink_command=`$ECHO "$relink_command" | $SED 's%@OUTPUT@%'"$outputname"'%g'` + + $opt_silent || { + func_quote_for_expand "$relink_command" + eval "func_echo $func_quote_for_expand_result" + } + if eval "$relink_command"; then : + else + func_error "error: relink \`$file' with the above command before installing it" + $opt_dry_run || ${RM}r "$tmpdir" + continue + fi + file="$outputname" + else + func_warning "cannot relink \`$file'" + fi + } + else + # Install the binary that we compiled earlier. + file=`$ECHO "$file$stripped_ext" | $SED "s%\([^/]*\)$%$objdir/\1%"` + fi + fi + + # remove .exe since cygwin /usr/bin/install will append another + # one anyway + case $install_prog,$host in + */usr/bin/install*,*cygwin*) + case $file:$destfile in + *.exe:*.exe) + # this is ok + ;; + *.exe:*) + destfile=$destfile.exe + ;; + *:*.exe) + func_stripname '' '.exe' "$destfile" + destfile=$func_stripname_result + ;; + esac + ;; + esac + func_show_eval "$install_prog\$stripme \$file \$destfile" 'exit $?' + $opt_dry_run || if test -n "$outputname"; then + ${RM}r "$tmpdir" + fi + ;; + esac + done + + for file in $staticlibs; do + func_basename "$file" + name="$func_basename_result" + + # Set up the ranlib parameters. + oldlib="$destdir/$name" + func_to_tool_file "$oldlib" func_convert_file_msys_to_w32 + tool_oldlib=$func_to_tool_file_result + + func_show_eval "$install_prog \$file \$oldlib" 'exit $?' + + if test -n "$stripme" && test -n "$old_striplib"; then + func_show_eval "$old_striplib $tool_oldlib" 'exit $?' + fi + + # Do each command in the postinstall commands. + func_execute_cmds "$old_postinstall_cmds" 'exit $?' + done + + test -n "$future_libdirs" && \ + func_warning "remember to run \`$progname --finish$future_libdirs'" + + if test -n "$current_libdirs"; then + # Maybe just do a dry run. + $opt_dry_run && current_libdirs=" -n$current_libdirs" + exec_cmd='$SHELL $progpath $preserve_args --finish$current_libdirs' + else + exit $EXIT_SUCCESS + fi +} + +test "$opt_mode" = install && func_mode_install ${1+"$@"} + + +# func_generate_dlsyms outputname originator pic_p +# Extract symbols from dlprefiles and create ${outputname}S.o with +# a dlpreopen symbol table. +func_generate_dlsyms () +{ + $opt_debug + my_outputname="$1" + my_originator="$2" + my_pic_p="${3-no}" + my_prefix=`$ECHO "$my_originator" | sed 's%[^a-zA-Z0-9]%_%g'` + my_dlsyms= + + if test -n "$dlfiles$dlprefiles" || test "$dlself" != no; then + if test -n "$NM" && test -n "$global_symbol_pipe"; then + my_dlsyms="${my_outputname}S.c" + else + func_error "not configured to extract global symbols from dlpreopened files" + fi + fi + + if test -n "$my_dlsyms"; then + case $my_dlsyms in + "") ;; + *.c) + # Discover the nlist of each of the dlfiles. + nlist="$output_objdir/${my_outputname}.nm" + + func_show_eval "$RM $nlist ${nlist}S ${nlist}T" + + # Parse the name list into a source file. + func_verbose "creating $output_objdir/$my_dlsyms" + + $opt_dry_run || $ECHO > "$output_objdir/$my_dlsyms" "\ +/* $my_dlsyms - symbol resolution table for \`$my_outputname' dlsym emulation. */ +/* Generated by $PROGRAM (GNU $PACKAGE$TIMESTAMP) $VERSION */ + +#ifdef __cplusplus +extern \"C\" { +#endif + +#if defined(__GNUC__) && (((__GNUC__ == 4) && (__GNUC_MINOR__ >= 4)) || (__GNUC__ > 4)) +#pragma GCC diagnostic ignored \"-Wstrict-prototypes\" +#endif + +/* Keep this code in sync between libtool.m4, ltmain, lt_system.h, and tests. */ +#if defined(_WIN32) || defined(__CYGWIN__) || defined(_WIN32_WCE) +/* DATA imports from DLLs on WIN32 con't be const, because runtime + relocations are performed -- see ld's documentation on pseudo-relocs. */ +# define LT_DLSYM_CONST +#elif defined(__osf__) +/* This system does not cope well with relocations in const data. */ +# define LT_DLSYM_CONST +#else +# define LT_DLSYM_CONST const +#endif + +/* External symbol declarations for the compiler. */\ +" + + if test "$dlself" = yes; then + func_verbose "generating symbol list for \`$output'" + + $opt_dry_run || echo ': @PROGRAM@ ' > "$nlist" + + # Add our own program objects to the symbol list. + progfiles=`$ECHO "$objs$old_deplibs" | $SP2NL | $SED "$lo2o" | $NL2SP` + for progfile in $progfiles; do + func_to_tool_file "$progfile" func_convert_file_msys_to_w32 + func_verbose "extracting global C symbols from \`$func_to_tool_file_result'" + $opt_dry_run || eval "$NM $func_to_tool_file_result | $global_symbol_pipe >> '$nlist'" + done + + if test -n "$exclude_expsyms"; then + $opt_dry_run || { + eval '$EGREP -v " ($exclude_expsyms)$" "$nlist" > "$nlist"T' + eval '$MV "$nlist"T "$nlist"' + } + fi + + if test -n "$export_symbols_regex"; then + $opt_dry_run || { + eval '$EGREP -e "$export_symbols_regex" "$nlist" > "$nlist"T' + eval '$MV "$nlist"T "$nlist"' + } + fi + + # Prepare the list of exported symbols + if test -z "$export_symbols"; then + export_symbols="$output_objdir/$outputname.exp" + $opt_dry_run || { + $RM $export_symbols + eval "${SED} -n -e '/^: @PROGRAM@ $/d' -e 's/^.* \(.*\)$/\1/p' "'< "$nlist" > "$export_symbols"' + case $host in + *cygwin* | *mingw* | *cegcc* ) + eval "echo EXPORTS "'> "$output_objdir/$outputname.def"' + eval 'cat "$export_symbols" >> "$output_objdir/$outputname.def"' + ;; + esac + } + else + $opt_dry_run || { + eval "${SED} -e 's/\([].[*^$]\)/\\\\\1/g' -e 's/^/ /' -e 's/$/$/'"' < "$export_symbols" > "$output_objdir/$outputname.exp"' + eval '$GREP -f "$output_objdir/$outputname.exp" < "$nlist" > "$nlist"T' + eval '$MV "$nlist"T "$nlist"' + case $host in + *cygwin* | *mingw* | *cegcc* ) + eval "echo EXPORTS "'> "$output_objdir/$outputname.def"' + eval 'cat "$nlist" >> "$output_objdir/$outputname.def"' + ;; + esac + } + fi + fi + + for dlprefile in $dlprefiles; do + func_verbose "extracting global C symbols from \`$dlprefile'" + func_basename "$dlprefile" + name="$func_basename_result" + case $host in + *cygwin* | *mingw* | *cegcc* ) + # if an import library, we need to obtain dlname + if func_win32_import_lib_p "$dlprefile"; then + func_tr_sh "$dlprefile" + eval "curr_lafile=\$libfile_$func_tr_sh_result" + dlprefile_dlbasename="" + if test -n "$curr_lafile" && func_lalib_p "$curr_lafile"; then + # Use subshell, to avoid clobbering current variable values + dlprefile_dlname=`source "$curr_lafile" && echo "$dlname"` + if test -n "$dlprefile_dlname" ; then + func_basename "$dlprefile_dlname" + dlprefile_dlbasename="$func_basename_result" + else + # no lafile. user explicitly requested -dlpreopen . + $sharedlib_from_linklib_cmd "$dlprefile" + dlprefile_dlbasename=$sharedlib_from_linklib_result + fi + fi + $opt_dry_run || { + if test -n "$dlprefile_dlbasename" ; then + eval '$ECHO ": $dlprefile_dlbasename" >> "$nlist"' + else + func_warning "Could not compute DLL name from $name" + eval '$ECHO ": $name " >> "$nlist"' + fi + func_to_tool_file "$dlprefile" func_convert_file_msys_to_w32 + eval "$NM \"$func_to_tool_file_result\" 2>/dev/null | $global_symbol_pipe | + $SED -e '/I __imp/d' -e 's/I __nm_/D /;s/_nm__//' >> '$nlist'" + } + else # not an import lib + $opt_dry_run || { + eval '$ECHO ": $name " >> "$nlist"' + func_to_tool_file "$dlprefile" func_convert_file_msys_to_w32 + eval "$NM \"$func_to_tool_file_result\" 2>/dev/null | $global_symbol_pipe >> '$nlist'" + } + fi + ;; + *) + $opt_dry_run || { + eval '$ECHO ": $name " >> "$nlist"' + func_to_tool_file "$dlprefile" func_convert_file_msys_to_w32 + eval "$NM \"$func_to_tool_file_result\" 2>/dev/null | $global_symbol_pipe >> '$nlist'" + } + ;; + esac + done + + $opt_dry_run || { + # Make sure we have at least an empty file. + test -f "$nlist" || : > "$nlist" + + if test -n "$exclude_expsyms"; then + $EGREP -v " ($exclude_expsyms)$" "$nlist" > "$nlist"T + $MV "$nlist"T "$nlist" + fi + + # Try sorting and uniquifying the output. + if $GREP -v "^: " < "$nlist" | + if sort -k 3 /dev/null 2>&1; then + sort -k 3 + else + sort +2 + fi | + uniq > "$nlist"S; then + : + else + $GREP -v "^: " < "$nlist" > "$nlist"S + fi + + if test -f "$nlist"S; then + eval "$global_symbol_to_cdecl"' < "$nlist"S >> "$output_objdir/$my_dlsyms"' + else + echo '/* NONE */' >> "$output_objdir/$my_dlsyms" + fi + + echo >> "$output_objdir/$my_dlsyms" "\ + +/* The mapping between symbol names and symbols. */ +typedef struct { + const char *name; + void *address; +} lt_dlsymlist; +extern LT_DLSYM_CONST lt_dlsymlist +lt_${my_prefix}_LTX_preloaded_symbols[]; +LT_DLSYM_CONST lt_dlsymlist +lt_${my_prefix}_LTX_preloaded_symbols[] = +{\ + { \"$my_originator\", (void *) 0 }," + + case $need_lib_prefix in + no) + eval "$global_symbol_to_c_name_address" < "$nlist" >> "$output_objdir/$my_dlsyms" + ;; + *) + eval "$global_symbol_to_c_name_address_lib_prefix" < "$nlist" >> "$output_objdir/$my_dlsyms" + ;; + esac + echo >> "$output_objdir/$my_dlsyms" "\ + {0, (void *) 0} +}; + +/* This works around a problem in FreeBSD linker */ +#ifdef FREEBSD_WORKAROUND +static const void *lt_preloaded_setup() { + return lt_${my_prefix}_LTX_preloaded_symbols; +} +#endif + +#ifdef __cplusplus +} +#endif\ +" + } # !$opt_dry_run + + pic_flag_for_symtable= + case "$compile_command " in + *" -static "*) ;; + *) + case $host in + # compiling the symbol table file with pic_flag works around + # a FreeBSD bug that causes programs to crash when -lm is + # linked before any other PIC object. But we must not use + # pic_flag when linking with -static. The problem exists in + # FreeBSD 2.2.6 and is fixed in FreeBSD 3.1. + *-*-freebsd2.*|*-*-freebsd3.0*|*-*-freebsdelf3.0*) + pic_flag_for_symtable=" $pic_flag -DFREEBSD_WORKAROUND" ;; + *-*-hpux*) + pic_flag_for_symtable=" $pic_flag" ;; + *) + if test "X$my_pic_p" != Xno; then + pic_flag_for_symtable=" $pic_flag" + fi + ;; + esac + ;; + esac + symtab_cflags= + for arg in $LTCFLAGS; do + case $arg in + -pie | -fpie | -fPIE) ;; + *) func_append symtab_cflags " $arg" ;; + esac + done + + # Now compile the dynamic symbol file. + func_show_eval '(cd $output_objdir && $LTCC$symtab_cflags -c$no_builtin_flag$pic_flag_for_symtable "$my_dlsyms")' 'exit $?' + + # Clean up the generated files. + func_show_eval '$RM "$output_objdir/$my_dlsyms" "$nlist" "${nlist}S" "${nlist}T"' + + # Transform the symbol file into the correct name. + symfileobj="$output_objdir/${my_outputname}S.$objext" + case $host in + *cygwin* | *mingw* | *cegcc* ) + if test -f "$output_objdir/$my_outputname.def"; then + compile_command=`$ECHO "$compile_command" | $SED "s%@SYMFILE@%$output_objdir/$my_outputname.def $symfileobj%"` + finalize_command=`$ECHO "$finalize_command" | $SED "s%@SYMFILE@%$output_objdir/$my_outputname.def $symfileobj%"` + else + compile_command=`$ECHO "$compile_command" | $SED "s%@SYMFILE@%$symfileobj%"` + finalize_command=`$ECHO "$finalize_command" | $SED "s%@SYMFILE@%$symfileobj%"` + fi + ;; + *) + compile_command=`$ECHO "$compile_command" | $SED "s%@SYMFILE@%$symfileobj%"` + finalize_command=`$ECHO "$finalize_command" | $SED "s%@SYMFILE@%$symfileobj%"` + ;; + esac + ;; + *) + func_fatal_error "unknown suffix for \`$my_dlsyms'" + ;; + esac + else + # We keep going just in case the user didn't refer to + # lt_preloaded_symbols. The linker will fail if global_symbol_pipe + # really was required. + + # Nullify the symbol file. + compile_command=`$ECHO "$compile_command" | $SED "s% @SYMFILE@%%"` + finalize_command=`$ECHO "$finalize_command" | $SED "s% @SYMFILE@%%"` + fi +} + +# func_win32_libid arg +# return the library type of file 'arg' +# +# Need a lot of goo to handle *both* DLLs and import libs +# Has to be a shell function in order to 'eat' the argument +# that is supplied when $file_magic_command is called. +# Despite the name, also deal with 64 bit binaries. +func_win32_libid () +{ + $opt_debug + win32_libid_type="unknown" + win32_fileres=`file -L $1 2>/dev/null` + case $win32_fileres in + *ar\ archive\ import\ library*) # definitely import + win32_libid_type="x86 archive import" + ;; + *ar\ archive*) # could be an import, or static + # Keep the egrep pattern in sync with the one in _LT_CHECK_MAGIC_METHOD. + if eval $OBJDUMP -f $1 | $SED -e '10q' 2>/dev/null | + $EGREP 'file format (pei*-i386(.*architecture: i386)?|pe-arm-wince|pe-x86-64)' >/dev/null; then + func_to_tool_file "$1" func_convert_file_msys_to_w32 + win32_nmres=`eval $NM -f posix -A \"$func_to_tool_file_result\" | + $SED -n -e ' + 1,100{ + / I /{ + s,.*,import, + p + q + } + }'` + case $win32_nmres in + import*) win32_libid_type="x86 archive import";; + *) win32_libid_type="x86 archive static";; + esac + fi + ;; + *DLL*) + win32_libid_type="x86 DLL" + ;; + *executable*) # but shell scripts are "executable" too... + case $win32_fileres in + *MS\ Windows\ PE\ Intel*) + win32_libid_type="x86 DLL" + ;; + esac + ;; + esac + $ECHO "$win32_libid_type" +} + +# func_cygming_dll_for_implib ARG +# +# Platform-specific function to extract the +# name of the DLL associated with the specified +# import library ARG. +# Invoked by eval'ing the libtool variable +# $sharedlib_from_linklib_cmd +# Result is available in the variable +# $sharedlib_from_linklib_result +func_cygming_dll_for_implib () +{ + $opt_debug + sharedlib_from_linklib_result=`$DLLTOOL --identify-strict --identify "$1"` +} + +# func_cygming_dll_for_implib_fallback_core SECTION_NAME LIBNAMEs +# +# The is the core of a fallback implementation of a +# platform-specific function to extract the name of the +# DLL associated with the specified import library LIBNAME. +# +# SECTION_NAME is either .idata$6 or .idata$7, depending +# on the platform and compiler that created the implib. +# +# Echos the name of the DLL associated with the +# specified import library. +func_cygming_dll_for_implib_fallback_core () +{ + $opt_debug + match_literal=`$ECHO "$1" | $SED "$sed_make_literal_regex"` + $OBJDUMP -s --section "$1" "$2" 2>/dev/null | + $SED '/^Contents of section '"$match_literal"':/{ + # Place marker at beginning of archive member dllname section + s/.*/====MARK====/ + p + d + } + # These lines can sometimes be longer than 43 characters, but + # are always uninteresting + /:[ ]*file format pe[i]\{,1\}-/d + /^In archive [^:]*:/d + # Ensure marker is printed + /^====MARK====/p + # Remove all lines with less than 43 characters + /^.\{43\}/!d + # From remaining lines, remove first 43 characters + s/^.\{43\}//' | + $SED -n ' + # Join marker and all lines until next marker into a single line + /^====MARK====/ b para + H + $ b para + b + :para + x + s/\n//g + # Remove the marker + s/^====MARK====// + # Remove trailing dots and whitespace + s/[\. \t]*$// + # Print + /./p' | + # we now have a list, one entry per line, of the stringified + # contents of the appropriate section of all members of the + # archive which possess that section. Heuristic: eliminate + # all those which have a first or second character that is + # a '.' (that is, objdump's representation of an unprintable + # character.) This should work for all archives with less than + # 0x302f exports -- but will fail for DLLs whose name actually + # begins with a literal '.' or a single character followed by + # a '.'. + # + # Of those that remain, print the first one. + $SED -e '/^\./d;/^.\./d;q' +} + +# func_cygming_gnu_implib_p ARG +# This predicate returns with zero status (TRUE) if +# ARG is a GNU/binutils-style import library. Returns +# with nonzero status (FALSE) otherwise. +func_cygming_gnu_implib_p () +{ + $opt_debug + func_to_tool_file "$1" func_convert_file_msys_to_w32 + func_cygming_gnu_implib_tmp=`$NM "$func_to_tool_file_result" | eval "$global_symbol_pipe" | $EGREP ' (_head_[A-Za-z0-9_]+_[ad]l*|[A-Za-z0-9_]+_[ad]l*_iname)$'` + test -n "$func_cygming_gnu_implib_tmp" +} + +# func_cygming_ms_implib_p ARG +# This predicate returns with zero status (TRUE) if +# ARG is an MS-style import library. Returns +# with nonzero status (FALSE) otherwise. +func_cygming_ms_implib_p () +{ + $opt_debug + func_to_tool_file "$1" func_convert_file_msys_to_w32 + func_cygming_ms_implib_tmp=`$NM "$func_to_tool_file_result" | eval "$global_symbol_pipe" | $GREP '_NULL_IMPORT_DESCRIPTOR'` + test -n "$func_cygming_ms_implib_tmp" +} + +# func_cygming_dll_for_implib_fallback ARG +# Platform-specific function to extract the +# name of the DLL associated with the specified +# import library ARG. +# +# This fallback implementation is for use when $DLLTOOL +# does not support the --identify-strict option. +# Invoked by eval'ing the libtool variable +# $sharedlib_from_linklib_cmd +# Result is available in the variable +# $sharedlib_from_linklib_result +func_cygming_dll_for_implib_fallback () +{ + $opt_debug + if func_cygming_gnu_implib_p "$1" ; then + # binutils import library + sharedlib_from_linklib_result=`func_cygming_dll_for_implib_fallback_core '.idata$7' "$1"` + elif func_cygming_ms_implib_p "$1" ; then + # ms-generated import library + sharedlib_from_linklib_result=`func_cygming_dll_for_implib_fallback_core '.idata$6' "$1"` + else + # unknown + sharedlib_from_linklib_result="" + fi +} + + +# func_extract_an_archive dir oldlib +func_extract_an_archive () +{ + $opt_debug + f_ex_an_ar_dir="$1"; shift + f_ex_an_ar_oldlib="$1" + if test "$lock_old_archive_extraction" = yes; then + lockfile=$f_ex_an_ar_oldlib.lock + until $opt_dry_run || ln "$progpath" "$lockfile" 2>/dev/null; do + func_echo "Waiting for $lockfile to be removed" + sleep 2 + done + fi + func_show_eval "(cd \$f_ex_an_ar_dir && $AR x \"\$f_ex_an_ar_oldlib\")" \ + 'stat=$?; rm -f "$lockfile"; exit $stat' + if test "$lock_old_archive_extraction" = yes; then + $opt_dry_run || rm -f "$lockfile" + fi + if ($AR t "$f_ex_an_ar_oldlib" | sort | sort -uc >/dev/null 2>&1); then + : + else + func_fatal_error "object name conflicts in archive: $f_ex_an_ar_dir/$f_ex_an_ar_oldlib" + fi +} + + +# func_extract_archives gentop oldlib ... +func_extract_archives () +{ + $opt_debug + my_gentop="$1"; shift + my_oldlibs=${1+"$@"} + my_oldobjs="" + my_xlib="" + my_xabs="" + my_xdir="" + + for my_xlib in $my_oldlibs; do + # Extract the objects. + case $my_xlib in + [\\/]* | [A-Za-z]:[\\/]*) my_xabs="$my_xlib" ;; + *) my_xabs=`pwd`"/$my_xlib" ;; + esac + func_basename "$my_xlib" + my_xlib="$func_basename_result" + my_xlib_u=$my_xlib + while :; do + case " $extracted_archives " in + *" $my_xlib_u "*) + func_arith $extracted_serial + 1 + extracted_serial=$func_arith_result + my_xlib_u=lt$extracted_serial-$my_xlib ;; + *) break ;; + esac + done + extracted_archives="$extracted_archives $my_xlib_u" + my_xdir="$my_gentop/$my_xlib_u" + + func_mkdir_p "$my_xdir" + + case $host in + *-darwin*) + func_verbose "Extracting $my_xabs" + # Do not bother doing anything if just a dry run + $opt_dry_run || { + darwin_orig_dir=`pwd` + cd $my_xdir || exit $? + darwin_archive=$my_xabs + darwin_curdir=`pwd` + darwin_base_archive=`basename "$darwin_archive"` + darwin_arches=`$LIPO -info "$darwin_archive" 2>/dev/null | $GREP Architectures 2>/dev/null || true` + if test -n "$darwin_arches"; then + darwin_arches=`$ECHO "$darwin_arches" | $SED -e 's/.*are://'` + darwin_arch= + func_verbose "$darwin_base_archive has multiple architectures $darwin_arches" + for darwin_arch in $darwin_arches ; do + func_mkdir_p "unfat-$$/${darwin_base_archive}-${darwin_arch}" + $LIPO -thin $darwin_arch -output "unfat-$$/${darwin_base_archive}-${darwin_arch}/${darwin_base_archive}" "${darwin_archive}" + cd "unfat-$$/${darwin_base_archive}-${darwin_arch}" + func_extract_an_archive "`pwd`" "${darwin_base_archive}" + cd "$darwin_curdir" + $RM "unfat-$$/${darwin_base_archive}-${darwin_arch}/${darwin_base_archive}" + done # $darwin_arches + ## Okay now we've a bunch of thin objects, gotta fatten them up :) + darwin_filelist=`find unfat-$$ -type f -name \*.o -print -o -name \*.lo -print | $SED -e "$basename" | sort -u` + darwin_file= + darwin_files= + for darwin_file in $darwin_filelist; do + darwin_files=`find unfat-$$ -name $darwin_file -print | sort | $NL2SP` + $LIPO -create -output "$darwin_file" $darwin_files + done # $darwin_filelist + $RM -rf unfat-$$ + cd "$darwin_orig_dir" + else + cd $darwin_orig_dir + func_extract_an_archive "$my_xdir" "$my_xabs" + fi # $darwin_arches + } # !$opt_dry_run + ;; + *) + func_extract_an_archive "$my_xdir" "$my_xabs" + ;; + esac + my_oldobjs="$my_oldobjs "`find $my_xdir -name \*.$objext -print -o -name \*.lo -print | sort | $NL2SP` + done + + func_extract_archives_result="$my_oldobjs" +} + + +# func_emit_wrapper [arg=no] +# +# Emit a libtool wrapper script on stdout. +# Don't directly open a file because we may want to +# incorporate the script contents within a cygwin/mingw +# wrapper executable. Must ONLY be called from within +# func_mode_link because it depends on a number of variables +# set therein. +# +# ARG is the value that the WRAPPER_SCRIPT_BELONGS_IN_OBJDIR +# variable will take. If 'yes', then the emitted script +# will assume that the directory in which it is stored is +# the $objdir directory. This is a cygwin/mingw-specific +# behavior. +func_emit_wrapper () +{ + func_emit_wrapper_arg1=${1-no} + + $ECHO "\ +#! $SHELL + +# $output - temporary wrapper script for $objdir/$outputname +# Generated by $PROGRAM (GNU $PACKAGE$TIMESTAMP) $VERSION +# +# The $output program cannot be directly executed until all the libtool +# libraries that it depends on are installed. +# +# This wrapper script should never be moved out of the build directory. +# If it is, it will not operate correctly. + +# Sed substitution that helps us do robust quoting. It backslashifies +# metacharacters that are still active within double-quoted strings. +sed_quote_subst='$sed_quote_subst' + +# Be Bourne compatible +if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then + emulate sh + NULLCMD=: + # Zsh 3.x and 4.x performs word splitting on \${1+\"\$@\"}, which + # is contrary to our usage. Disable this feature. + alias -g '\${1+\"\$@\"}'='\"\$@\"' + setopt NO_GLOB_SUBST +else + case \`(set -o) 2>/dev/null\` in *posix*) set -o posix;; esac +fi +BIN_SH=xpg4; export BIN_SH # for Tru64 +DUALCASE=1; export DUALCASE # for MKS sh + +# The HP-UX ksh and POSIX shell print the target directory to stdout +# if CDPATH is set. +(unset CDPATH) >/dev/null 2>&1 && unset CDPATH + +relink_command=\"$relink_command\" + +# This environment variable determines our operation mode. +if test \"\$libtool_install_magic\" = \"$magic\"; then + # install mode needs the following variables: + generated_by_libtool_version='$macro_version' + notinst_deplibs='$notinst_deplibs' +else + # When we are sourced in execute mode, \$file and \$ECHO are already set. + if test \"\$libtool_execute_magic\" != \"$magic\"; then + file=\"\$0\"" + + qECHO=`$ECHO "$ECHO" | $SED "$sed_quote_subst"` + $ECHO "\ + +# A function that is used when there is no print builtin or printf. +func_fallback_echo () +{ + eval 'cat <<_LTECHO_EOF +\$1 +_LTECHO_EOF' +} + ECHO=\"$qECHO\" + fi + +# Very basic option parsing. These options are (a) specific to +# the libtool wrapper, (b) are identical between the wrapper +# /script/ and the wrapper /executable/ which is used only on +# windows platforms, and (c) all begin with the string "--lt-" +# (application programs are unlikely to have options which match +# this pattern). +# +# There are only two supported options: --lt-debug and +# --lt-dump-script. There is, deliberately, no --lt-help. +# +# The first argument to this parsing function should be the +# script's $0 value, followed by "$@". +lt_option_debug= +func_parse_lt_options () +{ + lt_script_arg0=\$0 + shift + for lt_opt + do + case \"\$lt_opt\" in + --lt-debug) lt_option_debug=1 ;; + --lt-dump-script) + lt_dump_D=\`\$ECHO \"X\$lt_script_arg0\" | $SED -e 's/^X//' -e 's%/[^/]*$%%'\` + test \"X\$lt_dump_D\" = \"X\$lt_script_arg0\" && lt_dump_D=. + lt_dump_F=\`\$ECHO \"X\$lt_script_arg0\" | $SED -e 's/^X//' -e 's%^.*/%%'\` + cat \"\$lt_dump_D/\$lt_dump_F\" + exit 0 + ;; + --lt-*) + \$ECHO \"Unrecognized --lt- option: '\$lt_opt'\" 1>&2 + exit 1 + ;; + esac + done + + # Print the debug banner immediately: + if test -n \"\$lt_option_debug\"; then + echo \"${outputname}:${output}:\${LINENO}: libtool wrapper (GNU $PACKAGE$TIMESTAMP) $VERSION\" 1>&2 + fi +} + +# Used when --lt-debug. Prints its arguments to stdout +# (redirection is the responsibility of the caller) +func_lt_dump_args () +{ + lt_dump_args_N=1; + for lt_arg + do + \$ECHO \"${outputname}:${output}:\${LINENO}: newargv[\$lt_dump_args_N]: \$lt_arg\" + lt_dump_args_N=\`expr \$lt_dump_args_N + 1\` + done +} + +# Core function for launching the target application +func_exec_program_core () +{ +" + case $host in + # Backslashes separate directories on plain windows + *-*-mingw | *-*-os2* | *-cegcc*) + $ECHO "\ + if test -n \"\$lt_option_debug\"; then + \$ECHO \"${outputname}:${output}:\${LINENO}: newargv[0]: \$progdir\\\\\$program\" 1>&2 + func_lt_dump_args \${1+\"\$@\"} 1>&2 + fi + exec \"\$progdir\\\\\$program\" \${1+\"\$@\"} +" + ;; + + *) + $ECHO "\ + if test -n \"\$lt_option_debug\"; then + \$ECHO \"${outputname}:${output}:\${LINENO}: newargv[0]: \$progdir/\$program\" 1>&2 + func_lt_dump_args \${1+\"\$@\"} 1>&2 + fi + exec \"\$progdir/\$program\" \${1+\"\$@\"} +" + ;; + esac + $ECHO "\ + \$ECHO \"\$0: cannot exec \$program \$*\" 1>&2 + exit 1 +} + +# A function to encapsulate launching the target application +# Strips options in the --lt-* namespace from \$@ and +# launches target application with the remaining arguments. +func_exec_program () +{ + case \" \$* \" in + *\\ --lt-*) + for lt_wr_arg + do + case \$lt_wr_arg in + --lt-*) ;; + *) set x \"\$@\" \"\$lt_wr_arg\"; shift;; + esac + shift + done ;; + esac + func_exec_program_core \${1+\"\$@\"} +} + + # Parse options + func_parse_lt_options \"\$0\" \${1+\"\$@\"} + + # Find the directory that this script lives in. + thisdir=\`\$ECHO \"\$file\" | $SED 's%/[^/]*$%%'\` + test \"x\$thisdir\" = \"x\$file\" && thisdir=. + + # Follow symbolic links until we get to the real thisdir. + file=\`ls -ld \"\$file\" | $SED -n 's/.*-> //p'\` + while test -n \"\$file\"; do + destdir=\`\$ECHO \"\$file\" | $SED 's%/[^/]*\$%%'\` + + # If there was a directory component, then change thisdir. + if test \"x\$destdir\" != \"x\$file\"; then + case \"\$destdir\" in + [\\\\/]* | [A-Za-z]:[\\\\/]*) thisdir=\"\$destdir\" ;; + *) thisdir=\"\$thisdir/\$destdir\" ;; + esac + fi + + file=\`\$ECHO \"\$file\" | $SED 's%^.*/%%'\` + file=\`ls -ld \"\$thisdir/\$file\" | $SED -n 's/.*-> //p'\` + done + + # Usually 'no', except on cygwin/mingw when embedded into + # the cwrapper. + WRAPPER_SCRIPT_BELONGS_IN_OBJDIR=$func_emit_wrapper_arg1 + if test \"\$WRAPPER_SCRIPT_BELONGS_IN_OBJDIR\" = \"yes\"; then + # special case for '.' + if test \"\$thisdir\" = \".\"; then + thisdir=\`pwd\` + fi + # remove .libs from thisdir + case \"\$thisdir\" in + *[\\\\/]$objdir ) thisdir=\`\$ECHO \"\$thisdir\" | $SED 's%[\\\\/][^\\\\/]*$%%'\` ;; + $objdir ) thisdir=. ;; + esac + fi + + # Try to get the absolute directory name. + absdir=\`cd \"\$thisdir\" && pwd\` + test -n \"\$absdir\" && thisdir=\"\$absdir\" +" + + if test "$fast_install" = yes; then + $ECHO "\ + program=lt-'$outputname'$exeext + progdir=\"\$thisdir/$objdir\" + + if test ! -f \"\$progdir/\$program\" || + { file=\`ls -1dt \"\$progdir/\$program\" \"\$progdir/../\$program\" 2>/dev/null | ${SED} 1q\`; \\ + test \"X\$file\" != \"X\$progdir/\$program\"; }; then + + file=\"\$\$-\$program\" + + if test ! -d \"\$progdir\"; then + $MKDIR \"\$progdir\" + else + $RM \"\$progdir/\$file\" + fi" + + $ECHO "\ + + # relink executable if necessary + if test -n \"\$relink_command\"; then + if relink_command_output=\`eval \$relink_command 2>&1\`; then : + else + $ECHO \"\$relink_command_output\" >&2 + $RM \"\$progdir/\$file\" + exit 1 + fi + fi + + $MV \"\$progdir/\$file\" \"\$progdir/\$program\" 2>/dev/null || + { $RM \"\$progdir/\$program\"; + $MV \"\$progdir/\$file\" \"\$progdir/\$program\"; } + $RM \"\$progdir/\$file\" + fi" + else + $ECHO "\ + program='$outputname' + progdir=\"\$thisdir/$objdir\" +" + fi + + $ECHO "\ + + if test -f \"\$progdir/\$program\"; then" + + # fixup the dll searchpath if we need to. + # + # Fix the DLL searchpath if we need to. Do this before prepending + # to shlibpath, because on Windows, both are PATH and uninstalled + # libraries must come first. + if test -n "$dllsearchpath"; then + $ECHO "\ + # Add the dll search path components to the executable PATH + PATH=$dllsearchpath:\$PATH +" + fi + + # Export our shlibpath_var if we have one. + if test "$shlibpath_overrides_runpath" = yes && test -n "$shlibpath_var" && test -n "$temp_rpath"; then + $ECHO "\ + # Add our own library path to $shlibpath_var + $shlibpath_var=\"$temp_rpath\$$shlibpath_var\" + + # Some systems cannot cope with colon-terminated $shlibpath_var + # The second colon is a workaround for a bug in BeOS R4 sed + $shlibpath_var=\`\$ECHO \"\$$shlibpath_var\" | $SED 's/::*\$//'\` + + export $shlibpath_var +" + fi + + $ECHO "\ + if test \"\$libtool_execute_magic\" != \"$magic\"; then + # Run the actual program with our arguments. + func_exec_program \${1+\"\$@\"} + fi + else + # The program doesn't exist. + \$ECHO \"\$0: error: \\\`\$progdir/\$program' does not exist\" 1>&2 + \$ECHO \"This script is just a wrapper for \$program.\" 1>&2 + \$ECHO \"See the $PACKAGE documentation for more information.\" 1>&2 + exit 1 + fi +fi\ +" +} + + +# func_emit_cwrapperexe_src +# emit the source code for a wrapper executable on stdout +# Must ONLY be called from within func_mode_link because +# it depends on a number of variable set therein. +func_emit_cwrapperexe_src () +{ + cat < +#include +#ifdef _MSC_VER +# include +# include +# include +#else +# include +# include +# ifdef __CYGWIN__ +# include +# endif +#endif +#include +#include +#include +#include +#include +#include +#include +#include + +/* declarations of non-ANSI functions */ +#if defined(__MINGW32__) +# ifdef __STRICT_ANSI__ +int _putenv (const char *); +# endif +#elif defined(__CYGWIN__) +# ifdef __STRICT_ANSI__ +char *realpath (const char *, char *); +int putenv (char *); +int setenv (const char *, const char *, int); +# endif +/* #elif defined (other platforms) ... */ +#endif + +/* portability defines, excluding path handling macros */ +#if defined(_MSC_VER) +# define setmode _setmode +# define stat _stat +# define chmod _chmod +# define getcwd _getcwd +# define putenv _putenv +# define S_IXUSR _S_IEXEC +# ifndef _INTPTR_T_DEFINED +# define _INTPTR_T_DEFINED +# define intptr_t int +# endif +#elif defined(__MINGW32__) +# define setmode _setmode +# define stat _stat +# define chmod _chmod +# define getcwd _getcwd +# define putenv _putenv +#elif defined(__CYGWIN__) +# define HAVE_SETENV +# define FOPEN_WB "wb" +/* #elif defined (other platforms) ... */ +#endif + +#if defined(PATH_MAX) +# define LT_PATHMAX PATH_MAX +#elif defined(MAXPATHLEN) +# define LT_PATHMAX MAXPATHLEN +#else +# define LT_PATHMAX 1024 +#endif + +#ifndef S_IXOTH +# define S_IXOTH 0 +#endif +#ifndef S_IXGRP +# define S_IXGRP 0 +#endif + +/* path handling portability macros */ +#ifndef DIR_SEPARATOR +# define DIR_SEPARATOR '/' +# define PATH_SEPARATOR ':' +#endif + +#if defined (_WIN32) || defined (__MSDOS__) || defined (__DJGPP__) || \ + defined (__OS2__) +# define HAVE_DOS_BASED_FILE_SYSTEM +# define FOPEN_WB "wb" +# ifndef DIR_SEPARATOR_2 +# define DIR_SEPARATOR_2 '\\' +# endif +# ifndef PATH_SEPARATOR_2 +# define PATH_SEPARATOR_2 ';' +# endif +#endif + +#ifndef DIR_SEPARATOR_2 +# define IS_DIR_SEPARATOR(ch) ((ch) == DIR_SEPARATOR) +#else /* DIR_SEPARATOR_2 */ +# define IS_DIR_SEPARATOR(ch) \ + (((ch) == DIR_SEPARATOR) || ((ch) == DIR_SEPARATOR_2)) +#endif /* DIR_SEPARATOR_2 */ + +#ifndef PATH_SEPARATOR_2 +# define IS_PATH_SEPARATOR(ch) ((ch) == PATH_SEPARATOR) +#else /* PATH_SEPARATOR_2 */ +# define IS_PATH_SEPARATOR(ch) ((ch) == PATH_SEPARATOR_2) +#endif /* PATH_SEPARATOR_2 */ + +#ifndef FOPEN_WB +# define FOPEN_WB "w" +#endif +#ifndef _O_BINARY +# define _O_BINARY 0 +#endif + +#define XMALLOC(type, num) ((type *) xmalloc ((num) * sizeof(type))) +#define XFREE(stale) do { \ + if (stale) { free ((void *) stale); stale = 0; } \ +} while (0) + +#if defined(LT_DEBUGWRAPPER) +static int lt_debug = 1; +#else +static int lt_debug = 0; +#endif + +const char *program_name = "libtool-wrapper"; /* in case xstrdup fails */ + +void *xmalloc (size_t num); +char *xstrdup (const char *string); +const char *base_name (const char *name); +char *find_executable (const char *wrapper); +char *chase_symlinks (const char *pathspec); +int make_executable (const char *path); +int check_executable (const char *path); +char *strendzap (char *str, const char *pat); +void lt_debugprintf (const char *file, int line, const char *fmt, ...); +void lt_fatal (const char *file, int line, const char *message, ...); +static const char *nonnull (const char *s); +static const char *nonempty (const char *s); +void lt_setenv (const char *name, const char *value); +char *lt_extend_str (const char *orig_value, const char *add, int to_end); +void lt_update_exe_path (const char *name, const char *value); +void lt_update_lib_path (const char *name, const char *value); +char **prepare_spawn (char **argv); +void lt_dump_script (FILE *f); +EOF + + cat <= 0) + && (st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))) + return 1; + else + return 0; +} + +int +make_executable (const char *path) +{ + int rval = 0; + struct stat st; + + lt_debugprintf (__FILE__, __LINE__, "(make_executable): %s\n", + nonempty (path)); + if ((!path) || (!*path)) + return 0; + + if (stat (path, &st) >= 0) + { + rval = chmod (path, st.st_mode | S_IXOTH | S_IXGRP | S_IXUSR); + } + return rval; +} + +/* Searches for the full path of the wrapper. Returns + newly allocated full path name if found, NULL otherwise + Does not chase symlinks, even on platforms that support them. +*/ +char * +find_executable (const char *wrapper) +{ + int has_slash = 0; + const char *p; + const char *p_next; + /* static buffer for getcwd */ + char tmp[LT_PATHMAX + 1]; + int tmp_len; + char *concat_name; + + lt_debugprintf (__FILE__, __LINE__, "(find_executable): %s\n", + nonempty (wrapper)); + + if ((wrapper == NULL) || (*wrapper == '\0')) + return NULL; + + /* Absolute path? */ +#if defined (HAVE_DOS_BASED_FILE_SYSTEM) + if (isalpha ((unsigned char) wrapper[0]) && wrapper[1] == ':') + { + concat_name = xstrdup (wrapper); + if (check_executable (concat_name)) + return concat_name; + XFREE (concat_name); + } + else + { +#endif + if (IS_DIR_SEPARATOR (wrapper[0])) + { + concat_name = xstrdup (wrapper); + if (check_executable (concat_name)) + return concat_name; + XFREE (concat_name); + } +#if defined (HAVE_DOS_BASED_FILE_SYSTEM) + } +#endif + + for (p = wrapper; *p; p++) + if (*p == '/') + { + has_slash = 1; + break; + } + if (!has_slash) + { + /* no slashes; search PATH */ + const char *path = getenv ("PATH"); + if (path != NULL) + { + for (p = path; *p; p = p_next) + { + const char *q; + size_t p_len; + for (q = p; *q; q++) + if (IS_PATH_SEPARATOR (*q)) + break; + p_len = q - p; + p_next = (*q == '\0' ? q : q + 1); + if (p_len == 0) + { + /* empty path: current directory */ + if (getcwd (tmp, LT_PATHMAX) == NULL) + lt_fatal (__FILE__, __LINE__, "getcwd failed: %s", + nonnull (strerror (errno))); + tmp_len = strlen (tmp); + concat_name = + XMALLOC (char, tmp_len + 1 + strlen (wrapper) + 1); + memcpy (concat_name, tmp, tmp_len); + concat_name[tmp_len] = '/'; + strcpy (concat_name + tmp_len + 1, wrapper); + } + else + { + concat_name = + XMALLOC (char, p_len + 1 + strlen (wrapper) + 1); + memcpy (concat_name, p, p_len); + concat_name[p_len] = '/'; + strcpy (concat_name + p_len + 1, wrapper); + } + if (check_executable (concat_name)) + return concat_name; + XFREE (concat_name); + } + } + /* not found in PATH; assume curdir */ + } + /* Relative path | not found in path: prepend cwd */ + if (getcwd (tmp, LT_PATHMAX) == NULL) + lt_fatal (__FILE__, __LINE__, "getcwd failed: %s", + nonnull (strerror (errno))); + tmp_len = strlen (tmp); + concat_name = XMALLOC (char, tmp_len + 1 + strlen (wrapper) + 1); + memcpy (concat_name, tmp, tmp_len); + concat_name[tmp_len] = '/'; + strcpy (concat_name + tmp_len + 1, wrapper); + + if (check_executable (concat_name)) + return concat_name; + XFREE (concat_name); + return NULL; +} + +char * +chase_symlinks (const char *pathspec) +{ +#ifndef S_ISLNK + return xstrdup (pathspec); +#else + char buf[LT_PATHMAX]; + struct stat s; + char *tmp_pathspec = xstrdup (pathspec); + char *p; + int has_symlinks = 0; + while (strlen (tmp_pathspec) && !has_symlinks) + { + lt_debugprintf (__FILE__, __LINE__, + "checking path component for symlinks: %s\n", + tmp_pathspec); + if (lstat (tmp_pathspec, &s) == 0) + { + if (S_ISLNK (s.st_mode) != 0) + { + has_symlinks = 1; + break; + } + + /* search backwards for last DIR_SEPARATOR */ + p = tmp_pathspec + strlen (tmp_pathspec) - 1; + while ((p > tmp_pathspec) && (!IS_DIR_SEPARATOR (*p))) + p--; + if ((p == tmp_pathspec) && (!IS_DIR_SEPARATOR (*p))) + { + /* no more DIR_SEPARATORS left */ + break; + } + *p = '\0'; + } + else + { + lt_fatal (__FILE__, __LINE__, + "error accessing file \"%s\": %s", + tmp_pathspec, nonnull (strerror (errno))); + } + } + XFREE (tmp_pathspec); + + if (!has_symlinks) + { + return xstrdup (pathspec); + } + + tmp_pathspec = realpath (pathspec, buf); + if (tmp_pathspec == 0) + { + lt_fatal (__FILE__, __LINE__, + "could not follow symlinks for %s", pathspec); + } + return xstrdup (tmp_pathspec); +#endif +} + +char * +strendzap (char *str, const char *pat) +{ + size_t len, patlen; + + assert (str != NULL); + assert (pat != NULL); + + len = strlen (str); + patlen = strlen (pat); + + if (patlen <= len) + { + str += len - patlen; + if (strcmp (str, pat) == 0) + *str = '\0'; + } + return str; +} + +void +lt_debugprintf (const char *file, int line, const char *fmt, ...) +{ + va_list args; + if (lt_debug) + { + (void) fprintf (stderr, "%s:%s:%d: ", program_name, file, line); + va_start (args, fmt); + (void) vfprintf (stderr, fmt, args); + va_end (args); + } +} + +static void +lt_error_core (int exit_status, const char *file, + int line, const char *mode, + const char *message, va_list ap) +{ + fprintf (stderr, "%s:%s:%d: %s: ", program_name, file, line, mode); + vfprintf (stderr, message, ap); + fprintf (stderr, ".\n"); + + if (exit_status >= 0) + exit (exit_status); +} + +void +lt_fatal (const char *file, int line, const char *message, ...) +{ + va_list ap; + va_start (ap, message); + lt_error_core (EXIT_FAILURE, file, line, "FATAL", message, ap); + va_end (ap); +} + +static const char * +nonnull (const char *s) +{ + return s ? s : "(null)"; +} + +static const char * +nonempty (const char *s) +{ + return (s && !*s) ? "(empty)" : nonnull (s); +} + +void +lt_setenv (const char *name, const char *value) +{ + lt_debugprintf (__FILE__, __LINE__, + "(lt_setenv) setting '%s' to '%s'\n", + nonnull (name), nonnull (value)); + { +#ifdef HAVE_SETENV + /* always make a copy, for consistency with !HAVE_SETENV */ + char *str = xstrdup (value); + setenv (name, str, 1); +#else + int len = strlen (name) + 1 + strlen (value) + 1; + char *str = XMALLOC (char, len); + sprintf (str, "%s=%s", name, value); + if (putenv (str) != EXIT_SUCCESS) + { + XFREE (str); + } +#endif + } +} + +char * +lt_extend_str (const char *orig_value, const char *add, int to_end) +{ + char *new_value; + if (orig_value && *orig_value) + { + int orig_value_len = strlen (orig_value); + int add_len = strlen (add); + new_value = XMALLOC (char, add_len + orig_value_len + 1); + if (to_end) + { + strcpy (new_value, orig_value); + strcpy (new_value + orig_value_len, add); + } + else + { + strcpy (new_value, add); + strcpy (new_value + add_len, orig_value); + } + } + else + { + new_value = xstrdup (add); + } + return new_value; +} + +void +lt_update_exe_path (const char *name, const char *value) +{ + lt_debugprintf (__FILE__, __LINE__, + "(lt_update_exe_path) modifying '%s' by prepending '%s'\n", + nonnull (name), nonnull (value)); + + if (name && *name && value && *value) + { + char *new_value = lt_extend_str (getenv (name), value, 0); + /* some systems can't cope with a ':'-terminated path #' */ + int len = strlen (new_value); + while (((len = strlen (new_value)) > 0) && IS_PATH_SEPARATOR (new_value[len-1])) + { + new_value[len-1] = '\0'; + } + lt_setenv (name, new_value); + XFREE (new_value); + } +} + +void +lt_update_lib_path (const char *name, const char *value) +{ + lt_debugprintf (__FILE__, __LINE__, + "(lt_update_lib_path) modifying '%s' by prepending '%s'\n", + nonnull (name), nonnull (value)); + + if (name && *name && value && *value) + { + char *new_value = lt_extend_str (getenv (name), value, 0); + lt_setenv (name, new_value); + XFREE (new_value); + } +} + +EOF + case $host_os in + mingw*) + cat <<"EOF" + +/* Prepares an argument vector before calling spawn(). + Note that spawn() does not by itself call the command interpreter + (getenv ("COMSPEC") != NULL ? getenv ("COMSPEC") : + ({ OSVERSIONINFO v; v.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); + GetVersionEx(&v); + v.dwPlatformId == VER_PLATFORM_WIN32_NT; + }) ? "cmd.exe" : "command.com"). + Instead it simply concatenates the arguments, separated by ' ', and calls + CreateProcess(). We must quote the arguments since Win32 CreateProcess() + interprets characters like ' ', '\t', '\\', '"' (but not '<' and '>') in a + special way: + - Space and tab are interpreted as delimiters. They are not treated as + delimiters if they are surrounded by double quotes: "...". + - Unescaped double quotes are removed from the input. Their only effect is + that within double quotes, space and tab are treated like normal + characters. + - Backslashes not followed by double quotes are not special. + - But 2*n+1 backslashes followed by a double quote become + n backslashes followed by a double quote (n >= 0): + \" -> " + \\\" -> \" + \\\\\" -> \\" + */ +#define SHELL_SPECIAL_CHARS "\"\\ \001\002\003\004\005\006\007\010\011\012\013\014\015\016\017\020\021\022\023\024\025\026\027\030\031\032\033\034\035\036\037" +#define SHELL_SPACE_CHARS " \001\002\003\004\005\006\007\010\011\012\013\014\015\016\017\020\021\022\023\024\025\026\027\030\031\032\033\034\035\036\037" +char ** +prepare_spawn (char **argv) +{ + size_t argc; + char **new_argv; + size_t i; + + /* Count number of arguments. */ + for (argc = 0; argv[argc] != NULL; argc++) + ; + + /* Allocate new argument vector. */ + new_argv = XMALLOC (char *, argc + 1); + + /* Put quoted arguments into the new argument vector. */ + for (i = 0; i < argc; i++) + { + const char *string = argv[i]; + + if (string[0] == '\0') + new_argv[i] = xstrdup ("\"\""); + else if (strpbrk (string, SHELL_SPECIAL_CHARS) != NULL) + { + int quote_around = (strpbrk (string, SHELL_SPACE_CHARS) != NULL); + size_t length; + unsigned int backslashes; + const char *s; + char *quoted_string; + char *p; + + length = 0; + backslashes = 0; + if (quote_around) + length++; + for (s = string; *s != '\0'; s++) + { + char c = *s; + if (c == '"') + length += backslashes + 1; + length++; + if (c == '\\') + backslashes++; + else + backslashes = 0; + } + if (quote_around) + length += backslashes + 1; + + quoted_string = XMALLOC (char, length + 1); + + p = quoted_string; + backslashes = 0; + if (quote_around) + *p++ = '"'; + for (s = string; *s != '\0'; s++) + { + char c = *s; + if (c == '"') + { + unsigned int j; + for (j = backslashes + 1; j > 0; j--) + *p++ = '\\'; + } + *p++ = c; + if (c == '\\') + backslashes++; + else + backslashes = 0; + } + if (quote_around) + { + unsigned int j; + for (j = backslashes; j > 0; j--) + *p++ = '\\'; + *p++ = '"'; + } + *p = '\0'; + + new_argv[i] = quoted_string; + } + else + new_argv[i] = (char *) string; + } + new_argv[argc] = NULL; + + return new_argv; +} +EOF + ;; + esac + + cat <<"EOF" +void lt_dump_script (FILE* f) +{ +EOF + func_emit_wrapper yes | + $SED -n -e ' +s/^\(.\{79\}\)\(..*\)/\1\ +\2/ +h +s/\([\\"]\)/\\\1/g +s/$/\\n/ +s/\([^\n]*\).*/ fputs ("\1", f);/p +g +D' + cat <<"EOF" +} +EOF +} +# end: func_emit_cwrapperexe_src + +# func_win32_import_lib_p ARG +# True if ARG is an import lib, as indicated by $file_magic_cmd +func_win32_import_lib_p () +{ + $opt_debug + case `eval $file_magic_cmd \"\$1\" 2>/dev/null | $SED -e 10q` in + *import*) : ;; + *) false ;; + esac +} + +# func_mode_link arg... +func_mode_link () +{ + $opt_debug + case $host in + *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-cegcc*) + # It is impossible to link a dll without this setting, and + # we shouldn't force the makefile maintainer to figure out + # which system we are compiling for in order to pass an extra + # flag for every libtool invocation. + # allow_undefined=no + + # FIXME: Unfortunately, there are problems with the above when trying + # to make a dll which has undefined symbols, in which case not + # even a static library is built. For now, we need to specify + # -no-undefined on the libtool link line when we can be certain + # that all symbols are satisfied, otherwise we get a static library. + allow_undefined=yes + ;; + *) + allow_undefined=yes + ;; + esac + libtool_args=$nonopt + base_compile="$nonopt $@" + compile_command=$nonopt + finalize_command=$nonopt + + compile_rpath= + finalize_rpath= + compile_shlibpath= + finalize_shlibpath= + convenience= + old_convenience= + deplibs= + old_deplibs= + compiler_flags= + linker_flags= + dllsearchpath= + lib_search_path=`pwd` + inst_prefix_dir= + new_inherited_linker_flags= + + avoid_version=no + bindir= + dlfiles= + dlprefiles= + dlself=no + export_dynamic=no + export_symbols= + export_symbols_regex= + generated= + libobjs= + ltlibs= + module=no + no_install=no + objs= + non_pic_objects= + precious_files_regex= + prefer_static_libs=no + preload=no + prev= + prevarg= + release= + rpath= + xrpath= + perm_rpath= + temp_rpath= + thread_safe=no + vinfo= + vinfo_number=no + weak_libs= + single_module="${wl}-single_module" + func_infer_tag $base_compile + + # We need to know -static, to get the right output filenames. + for arg + do + case $arg in + -shared) + test "$build_libtool_libs" != yes && \ + func_fatal_configuration "can not build a shared library" + build_old_libs=no + break + ;; + -all-static | -static | -static-libtool-libs) + case $arg in + -all-static) + if test "$build_libtool_libs" = yes && test -z "$link_static_flag"; then + func_warning "complete static linking is impossible in this configuration" + fi + if test -n "$link_static_flag"; then + dlopen_self=$dlopen_self_static + fi + prefer_static_libs=yes + ;; + -static) + if test -z "$pic_flag" && test -n "$link_static_flag"; then + dlopen_self=$dlopen_self_static + fi + prefer_static_libs=built + ;; + -static-libtool-libs) + if test -z "$pic_flag" && test -n "$link_static_flag"; then + dlopen_self=$dlopen_self_static + fi + prefer_static_libs=yes + ;; + esac + build_libtool_libs=no + build_old_libs=yes + break + ;; + esac + done + + # See if our shared archives depend on static archives. + test -n "$old_archive_from_new_cmds" && build_old_libs=yes + + # Go through the arguments, transforming them on the way. + while test "$#" -gt 0; do + arg="$1" + shift + func_quote_for_eval "$arg" + qarg=$func_quote_for_eval_unquoted_result + func_append libtool_args " $func_quote_for_eval_result" + + # If the previous option needs an argument, assign it. + if test -n "$prev"; then + case $prev in + output) + func_append compile_command " @OUTPUT@" + func_append finalize_command " @OUTPUT@" + ;; + esac + + case $prev in + bindir) + bindir="$arg" + prev= + continue + ;; + dlfiles|dlprefiles) + if test "$preload" = no; then + # Add the symbol object into the linking commands. + func_append compile_command " @SYMFILE@" + func_append finalize_command " @SYMFILE@" + preload=yes + fi + case $arg in + *.la | *.lo) ;; # We handle these cases below. + force) + if test "$dlself" = no; then + dlself=needless + export_dynamic=yes + fi + prev= + continue + ;; + self) + if test "$prev" = dlprefiles; then + dlself=yes + elif test "$prev" = dlfiles && test "$dlopen_self" != yes; then + dlself=yes + else + dlself=needless + export_dynamic=yes + fi + prev= + continue + ;; + *) + if test "$prev" = dlfiles; then + func_append dlfiles " $arg" + else + func_append dlprefiles " $arg" + fi + prev= + continue + ;; + esac + ;; + expsyms) + export_symbols="$arg" + test -f "$arg" \ + || func_fatal_error "symbol file \`$arg' does not exist" + prev= + continue + ;; + expsyms_regex) + export_symbols_regex="$arg" + prev= + continue + ;; + framework) + case $host in + *-*-darwin*) + case "$deplibs " in + *" $qarg.ltframework "*) ;; + *) func_append deplibs " $qarg.ltframework" # this is fixed later + ;; + esac + ;; + esac + prev= + continue + ;; + inst_prefix) + inst_prefix_dir="$arg" + prev= + continue + ;; + objectlist) + if test -f "$arg"; then + save_arg=$arg + moreargs= + for fil in `cat "$save_arg"` + do +# func_append moreargs " $fil" + arg=$fil + # A libtool-controlled object. + + # Check to see that this really is a libtool object. + if func_lalib_unsafe_p "$arg"; then + pic_object= + non_pic_object= + + # Read the .lo file + func_source "$arg" + + if test -z "$pic_object" || + test -z "$non_pic_object" || + test "$pic_object" = none && + test "$non_pic_object" = none; then + func_fatal_error "cannot find name of object for \`$arg'" + fi + + # Extract subdirectory from the argument. + func_dirname "$arg" "/" "" + xdir="$func_dirname_result" + + if test "$pic_object" != none; then + # Prepend the subdirectory the object is found in. + pic_object="$xdir$pic_object" + + if test "$prev" = dlfiles; then + if test "$build_libtool_libs" = yes && test "$dlopen_support" = yes; then + func_append dlfiles " $pic_object" + prev= + continue + else + # If libtool objects are unsupported, then we need to preload. + prev=dlprefiles + fi + fi + + # CHECK ME: I think I busted this. -Ossama + if test "$prev" = dlprefiles; then + # Preload the old-style object. + func_append dlprefiles " $pic_object" + prev= + fi + + # A PIC object. + func_append libobjs " $pic_object" + arg="$pic_object" + fi + + # Non-PIC object. + if test "$non_pic_object" != none; then + # Prepend the subdirectory the object is found in. + non_pic_object="$xdir$non_pic_object" + + # A standard non-PIC object + func_append non_pic_objects " $non_pic_object" + if test -z "$pic_object" || test "$pic_object" = none ; then + arg="$non_pic_object" + fi + else + # If the PIC object exists, use it instead. + # $xdir was prepended to $pic_object above. + non_pic_object="$pic_object" + func_append non_pic_objects " $non_pic_object" + fi + else + # Only an error if not doing a dry-run. + if $opt_dry_run; then + # Extract subdirectory from the argument. + func_dirname "$arg" "/" "" + xdir="$func_dirname_result" + + func_lo2o "$arg" + pic_object=$xdir$objdir/$func_lo2o_result + non_pic_object=$xdir$func_lo2o_result + func_append libobjs " $pic_object" + func_append non_pic_objects " $non_pic_object" + else + func_fatal_error "\`$arg' is not a valid libtool object" + fi + fi + done + else + func_fatal_error "link input file \`$arg' does not exist" + fi + arg=$save_arg + prev= + continue + ;; + precious_regex) + precious_files_regex="$arg" + prev= + continue + ;; + release) + release="-$arg" + prev= + continue + ;; + rpath | xrpath) + # We need an absolute path. + case $arg in + [\\/]* | [A-Za-z]:[\\/]*) ;; + *) + func_fatal_error "only absolute run-paths are allowed" + ;; + esac + if test "$prev" = rpath; then + case "$rpath " in + *" $arg "*) ;; + *) func_append rpath " $arg" ;; + esac + else + case "$xrpath " in + *" $arg "*) ;; + *) func_append xrpath " $arg" ;; + esac + fi + prev= + continue + ;; + shrext) + shrext_cmds="$arg" + prev= + continue + ;; + weak) + func_append weak_libs " $arg" + prev= + continue + ;; + xcclinker) + func_append linker_flags " $qarg" + func_append compiler_flags " $qarg" + prev= + func_append compile_command " $qarg" + func_append finalize_command " $qarg" + continue + ;; + xcompiler) + func_append compiler_flags " $qarg" + prev= + func_append compile_command " $qarg" + func_append finalize_command " $qarg" + continue + ;; + xlinker) + func_append linker_flags " $qarg" + func_append compiler_flags " $wl$qarg" + prev= + func_append compile_command " $wl$qarg" + func_append finalize_command " $wl$qarg" + continue + ;; + *) + eval "$prev=\"\$arg\"" + prev= + continue + ;; + esac + fi # test -n "$prev" + + prevarg="$arg" + + case $arg in + -all-static) + if test -n "$link_static_flag"; then + # See comment for -static flag below, for more details. + func_append compile_command " $link_static_flag" + func_append finalize_command " $link_static_flag" + fi + continue + ;; + + -allow-undefined) + # FIXME: remove this flag sometime in the future. + func_fatal_error "\`-allow-undefined' must not be used because it is the default" + ;; + + -avoid-version) + avoid_version=yes + continue + ;; + + -bindir) + prev=bindir + continue + ;; + + -dlopen) + prev=dlfiles + continue + ;; + + -dlpreopen) + prev=dlprefiles + continue + ;; + + -export-dynamic) + export_dynamic=yes + continue + ;; + + -export-symbols | -export-symbols-regex) + if test -n "$export_symbols" || test -n "$export_symbols_regex"; then + func_fatal_error "more than one -exported-symbols argument is not allowed" + fi + if test "X$arg" = "X-export-symbols"; then + prev=expsyms + else + prev=expsyms_regex + fi + continue + ;; + + -framework) + prev=framework + continue + ;; + + -inst-prefix-dir) + prev=inst_prefix + continue + ;; + + # The native IRIX linker understands -LANG:*, -LIST:* and -LNO:* + # so, if we see these flags be careful not to treat them like -L + -L[A-Z][A-Z]*:*) + case $with_gcc/$host in + no/*-*-irix* | /*-*-irix*) + func_append compile_command " $arg" + func_append finalize_command " $arg" + ;; + esac + continue + ;; + + -L*) + func_stripname "-L" '' "$arg" + if test -z "$func_stripname_result"; then + if test "$#" -gt 0; then + func_fatal_error "require no space between \`-L' and \`$1'" + else + func_fatal_error "need path for \`-L' option" + fi + fi + func_resolve_sysroot "$func_stripname_result" + dir=$func_resolve_sysroot_result + # We need an absolute path. + case $dir in + [\\/]* | [A-Za-z]:[\\/]*) ;; + *) + absdir=`cd "$dir" && pwd` + test -z "$absdir" && \ + func_fatal_error "cannot determine absolute directory name of \`$dir'" + dir="$absdir" + ;; + esac + case "$deplibs " in + *" -L$dir "* | *" $arg "*) + # Will only happen for absolute or sysroot arguments + ;; + *) + # Preserve sysroot, but never include relative directories + case $dir in + [\\/]* | [A-Za-z]:[\\/]* | =*) func_append deplibs " $arg" ;; + *) func_append deplibs " -L$dir" ;; + esac + func_append lib_search_path " $dir" + ;; + esac + case $host in + *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-cegcc*) + testbindir=`$ECHO "$dir" | $SED 's*/lib$*/bin*'` + case :$dllsearchpath: in + *":$dir:"*) ;; + ::) dllsearchpath=$dir;; + *) func_append dllsearchpath ":$dir";; + esac + case :$dllsearchpath: in + *":$testbindir:"*) ;; + ::) dllsearchpath=$testbindir;; + *) func_append dllsearchpath ":$testbindir";; + esac + ;; + esac + continue + ;; + + -l*) + if test "X$arg" = "X-lc" || test "X$arg" = "X-lm"; then + case $host in + *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-beos* | *-cegcc* | *-*-haiku*) + # These systems don't actually have a C or math library (as such) + continue + ;; + *-*-os2*) + # These systems don't actually have a C library (as such) + test "X$arg" = "X-lc" && continue + ;; + *-*-openbsd* | *-*-freebsd* | *-*-dragonfly*) + # Do not include libc due to us having libc/libc_r. + test "X$arg" = "X-lc" && continue + ;; + *-*-rhapsody* | *-*-darwin1.[012]) + # Rhapsody C and math libraries are in the System framework + func_append deplibs " System.ltframework" + continue + ;; + *-*-sco3.2v5* | *-*-sco5v6*) + # Causes problems with __ctype + test "X$arg" = "X-lc" && continue + ;; + *-*-sysv4.2uw2* | *-*-sysv5* | *-*-unixware* | *-*-OpenUNIX*) + # Compiler inserts libc in the correct place for threads to work + test "X$arg" = "X-lc" && continue + ;; + esac + elif test "X$arg" = "X-lc_r"; then + case $host in + *-*-openbsd* | *-*-freebsd* | *-*-dragonfly*) + # Do not include libc_r directly, use -pthread flag. + continue + ;; + esac + fi + func_append deplibs " $arg" + continue + ;; + + -module) + module=yes + continue + ;; + + # Tru64 UNIX uses -model [arg] to determine the layout of C++ + # classes, name mangling, and exception handling. + # Darwin uses the -arch flag to determine output architecture. + -model|-arch|-isysroot|--sysroot) + func_append compiler_flags " $arg" + func_append compile_command " $arg" + func_append finalize_command " $arg" + prev=xcompiler + continue + ;; + + -mt|-mthreads|-kthread|-Kthread|-pthread|-pthreads|--thread-safe \ + |-threads|-fopenmp|-openmp|-mp|-xopenmp|-omp|-qsmp=*) + func_append compiler_flags " $arg" + func_append compile_command " $arg" + func_append finalize_command " $arg" + case "$new_inherited_linker_flags " in + *" $arg "*) ;; + * ) func_append new_inherited_linker_flags " $arg" ;; + esac + continue + ;; + + -multi_module) + single_module="${wl}-multi_module" + continue + ;; + + -no-fast-install) + fast_install=no + continue + ;; + + -no-install) + case $host in + *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-*-darwin* | *-cegcc*) + # The PATH hackery in wrapper scripts is required on Windows + # and Darwin in order for the loader to find any dlls it needs. + func_warning "\`-no-install' is ignored for $host" + func_warning "assuming \`-no-fast-install' instead" + fast_install=no + ;; + *) no_install=yes ;; + esac + continue + ;; + + -no-undefined) + allow_undefined=no + continue + ;; + + -objectlist) + prev=objectlist + continue + ;; + + -o) prev=output ;; + + -precious-files-regex) + prev=precious_regex + continue + ;; + + -release) + prev=release + continue + ;; + + -rpath) + prev=rpath + continue + ;; + + -R) + prev=xrpath + continue + ;; + + -R*) + func_stripname '-R' '' "$arg" + dir=$func_stripname_result + # We need an absolute path. + case $dir in + [\\/]* | [A-Za-z]:[\\/]*) ;; + =*) + func_stripname '=' '' "$dir" + dir=$lt_sysroot$func_stripname_result + ;; + *) + func_fatal_error "only absolute run-paths are allowed" + ;; + esac + case "$xrpath " in + *" $dir "*) ;; + *) func_append xrpath " $dir" ;; + esac + continue + ;; + + -shared) + # The effects of -shared are defined in a previous loop. + continue + ;; + + -shrext) + prev=shrext + continue + ;; + + -static | -static-libtool-libs) + # The effects of -static are defined in a previous loop. + # We used to do the same as -all-static on platforms that + # didn't have a PIC flag, but the assumption that the effects + # would be equivalent was wrong. It would break on at least + # Digital Unix and AIX. + continue + ;; + + -thread-safe) + thread_safe=yes + continue + ;; + + -version-info) + prev=vinfo + continue + ;; + + -version-number) + prev=vinfo + vinfo_number=yes + continue + ;; + + -weak) + prev=weak + continue + ;; + + -Wc,*) + func_stripname '-Wc,' '' "$arg" + args=$func_stripname_result + arg= + save_ifs="$IFS"; IFS=',' + for flag in $args; do + IFS="$save_ifs" + func_quote_for_eval "$flag" + func_append arg " $func_quote_for_eval_result" + func_append compiler_flags " $func_quote_for_eval_result" + done + IFS="$save_ifs" + func_stripname ' ' '' "$arg" + arg=$func_stripname_result + ;; + + -Wl,*) + func_stripname '-Wl,' '' "$arg" + args=$func_stripname_result + arg= + save_ifs="$IFS"; IFS=',' + for flag in $args; do + IFS="$save_ifs" + func_quote_for_eval "$flag" + func_append arg " $wl$func_quote_for_eval_result" + func_append compiler_flags " $wl$func_quote_for_eval_result" + func_append linker_flags " $func_quote_for_eval_result" + done + IFS="$save_ifs" + func_stripname ' ' '' "$arg" + arg=$func_stripname_result + ;; + + -Xcompiler) + prev=xcompiler + continue + ;; + + -Xlinker) + prev=xlinker + continue + ;; + + -XCClinker) + prev=xcclinker + continue + ;; + + # -msg_* for osf cc + -msg_*) + func_quote_for_eval "$arg" + arg="$func_quote_for_eval_result" + ;; + + # Flags to be passed through unchanged, with rationale: + # -64, -mips[0-9] enable 64-bit mode for the SGI compiler + # -r[0-9][0-9]* specify processor for the SGI compiler + # -xarch=*, -xtarget=* enable 64-bit mode for the Sun compiler + # +DA*, +DD* enable 64-bit mode for the HP compiler + # -q* compiler args for the IBM compiler + # -m*, -t[45]*, -txscale* architecture-specific flags for GCC + # -F/path path to uninstalled frameworks, gcc on darwin + # -p, -pg, --coverage, -fprofile-* profiling flags for GCC + # @file GCC response files + # -tp=* Portland pgcc target processor selection + # --sysroot=* for sysroot support + # -O*, -flto*, -fwhopr*, -fuse-linker-plugin GCC link-time optimization + -64|-mips[0-9]|-r[0-9][0-9]*|-xarch=*|-xtarget=*|+DA*|+DD*|-q*|-m*| \ + -t[45]*|-txscale*|-p|-pg|--coverage|-fprofile-*|-F*|@*|-tp=*|--sysroot=*| \ + -O*|-flto*|-fwhopr*|-fuse-linker-plugin) + func_quote_for_eval "$arg" + arg="$func_quote_for_eval_result" + func_append compile_command " $arg" + func_append finalize_command " $arg" + func_append compiler_flags " $arg" + continue + ;; + + # Some other compiler flag. + -* | +*) + func_quote_for_eval "$arg" + arg="$func_quote_for_eval_result" + ;; + + *.$objext) + # A standard object. + func_append objs " $arg" + ;; + + *.lo) + # A libtool-controlled object. + + # Check to see that this really is a libtool object. + if func_lalib_unsafe_p "$arg"; then + pic_object= + non_pic_object= + + # Read the .lo file + func_source "$arg" + + if test -z "$pic_object" || + test -z "$non_pic_object" || + test "$pic_object" = none && + test "$non_pic_object" = none; then + func_fatal_error "cannot find name of object for \`$arg'" + fi + + # Extract subdirectory from the argument. + func_dirname "$arg" "/" "" + xdir="$func_dirname_result" + + if test "$pic_object" != none; then + # Prepend the subdirectory the object is found in. + pic_object="$xdir$pic_object" + + if test "$prev" = dlfiles; then + if test "$build_libtool_libs" = yes && test "$dlopen_support" = yes; then + func_append dlfiles " $pic_object" + prev= + continue + else + # If libtool objects are unsupported, then we need to preload. + prev=dlprefiles + fi + fi + + # CHECK ME: I think I busted this. -Ossama + if test "$prev" = dlprefiles; then + # Preload the old-style object. + func_append dlprefiles " $pic_object" + prev= + fi + + # A PIC object. + func_append libobjs " $pic_object" + arg="$pic_object" + fi + + # Non-PIC object. + if test "$non_pic_object" != none; then + # Prepend the subdirectory the object is found in. + non_pic_object="$xdir$non_pic_object" + + # A standard non-PIC object + func_append non_pic_objects " $non_pic_object" + if test -z "$pic_object" || test "$pic_object" = none ; then + arg="$non_pic_object" + fi + else + # If the PIC object exists, use it instead. + # $xdir was prepended to $pic_object above. + non_pic_object="$pic_object" + func_append non_pic_objects " $non_pic_object" + fi + else + # Only an error if not doing a dry-run. + if $opt_dry_run; then + # Extract subdirectory from the argument. + func_dirname "$arg" "/" "" + xdir="$func_dirname_result" + + func_lo2o "$arg" + pic_object=$xdir$objdir/$func_lo2o_result + non_pic_object=$xdir$func_lo2o_result + func_append libobjs " $pic_object" + func_append non_pic_objects " $non_pic_object" + else + func_fatal_error "\`$arg' is not a valid libtool object" + fi + fi + ;; + + *.$libext) + # An archive. + func_append deplibs " $arg" + func_append old_deplibs " $arg" + continue + ;; + + *.la) + # A libtool-controlled library. + + func_resolve_sysroot "$arg" + if test "$prev" = dlfiles; then + # This library was specified with -dlopen. + func_append dlfiles " $func_resolve_sysroot_result" + prev= + elif test "$prev" = dlprefiles; then + # The library was specified with -dlpreopen. + func_append dlprefiles " $func_resolve_sysroot_result" + prev= + else + func_append deplibs " $func_resolve_sysroot_result" + fi + continue + ;; + + # Some other compiler argument. + *) + # Unknown arguments in both finalize_command and compile_command need + # to be aesthetically quoted because they are evaled later. + func_quote_for_eval "$arg" + arg="$func_quote_for_eval_result" + ;; + esac # arg + + # Now actually substitute the argument into the commands. + if test -n "$arg"; then + func_append compile_command " $arg" + func_append finalize_command " $arg" + fi + done # argument parsing loop + + test -n "$prev" && \ + func_fatal_help "the \`$prevarg' option requires an argument" + + if test "$export_dynamic" = yes && test -n "$export_dynamic_flag_spec"; then + eval arg=\"$export_dynamic_flag_spec\" + func_append compile_command " $arg" + func_append finalize_command " $arg" + fi + + oldlibs= + # calculate the name of the file, without its directory + func_basename "$output" + outputname="$func_basename_result" + libobjs_save="$libobjs" + + if test -n "$shlibpath_var"; then + # get the directories listed in $shlibpath_var + eval shlib_search_path=\`\$ECHO \"\${$shlibpath_var}\" \| \$SED \'s/:/ /g\'\` + else + shlib_search_path= + fi + eval sys_lib_search_path=\"$sys_lib_search_path_spec\" + eval sys_lib_dlsearch_path=\"$sys_lib_dlsearch_path_spec\" + + func_dirname "$output" "/" "" + output_objdir="$func_dirname_result$objdir" + func_to_tool_file "$output_objdir/" + tool_output_objdir=$func_to_tool_file_result + # Create the object directory. + func_mkdir_p "$output_objdir" + + # Determine the type of output + case $output in + "") + func_fatal_help "you must specify an output file" + ;; + *.$libext) linkmode=oldlib ;; + *.lo | *.$objext) linkmode=obj ;; + *.la) linkmode=lib ;; + *) linkmode=prog ;; # Anything else should be a program. + esac + + specialdeplibs= + + libs= + # Find all interdependent deplibs by searching for libraries + # that are linked more than once (e.g. -la -lb -la) + for deplib in $deplibs; do + if $opt_preserve_dup_deps ; then + case "$libs " in + *" $deplib "*) func_append specialdeplibs " $deplib" ;; + esac + fi + func_append libs " $deplib" + done + + if test "$linkmode" = lib; then + libs="$predeps $libs $compiler_lib_search_path $postdeps" + + # Compute libraries that are listed more than once in $predeps + # $postdeps and mark them as special (i.e., whose duplicates are + # not to be eliminated). + pre_post_deps= + if $opt_duplicate_compiler_generated_deps; then + for pre_post_dep in $predeps $postdeps; do + case "$pre_post_deps " in + *" $pre_post_dep "*) func_append specialdeplibs " $pre_post_deps" ;; + esac + func_append pre_post_deps " $pre_post_dep" + done + fi + pre_post_deps= + fi + + deplibs= + newdependency_libs= + newlib_search_path= + need_relink=no # whether we're linking any uninstalled libtool libraries + notinst_deplibs= # not-installed libtool libraries + notinst_path= # paths that contain not-installed libtool libraries + + case $linkmode in + lib) + passes="conv dlpreopen link" + for file in $dlfiles $dlprefiles; do + case $file in + *.la) ;; + *) + func_fatal_help "libraries can \`-dlopen' only libtool libraries: $file" + ;; + esac + done + ;; + prog) + compile_deplibs= + finalize_deplibs= + alldeplibs=no + newdlfiles= + newdlprefiles= + passes="conv scan dlopen dlpreopen link" + ;; + *) passes="conv" + ;; + esac + + for pass in $passes; do + # The preopen pass in lib mode reverses $deplibs; put it back here + # so that -L comes before libs that need it for instance... + if test "$linkmode,$pass" = "lib,link"; then + ## FIXME: Find the place where the list is rebuilt in the wrong + ## order, and fix it there properly + tmp_deplibs= + for deplib in $deplibs; do + tmp_deplibs="$deplib $tmp_deplibs" + done + deplibs="$tmp_deplibs" + fi + + if test "$linkmode,$pass" = "lib,link" || + test "$linkmode,$pass" = "prog,scan"; then + libs="$deplibs" + deplibs= + fi + if test "$linkmode" = prog; then + case $pass in + dlopen) libs="$dlfiles" ;; + dlpreopen) libs="$dlprefiles" ;; + link) + libs="$deplibs %DEPLIBS%" + test "X$link_all_deplibs" != Xno && libs="$libs $dependency_libs" + ;; + esac + fi + if test "$linkmode,$pass" = "lib,dlpreopen"; then + # Collect and forward deplibs of preopened libtool libs + for lib in $dlprefiles; do + # Ignore non-libtool-libs + dependency_libs= + func_resolve_sysroot "$lib" + case $lib in + *.la) func_source "$func_resolve_sysroot_result" ;; + esac + + # Collect preopened libtool deplibs, except any this library + # has declared as weak libs + for deplib in $dependency_libs; do + func_basename "$deplib" + deplib_base=$func_basename_result + case " $weak_libs " in + *" $deplib_base "*) ;; + *) func_append deplibs " $deplib" ;; + esac + done + done + libs="$dlprefiles" + fi + if test "$pass" = dlopen; then + # Collect dlpreopened libraries + save_deplibs="$deplibs" + deplibs= + fi + + for deplib in $libs; do + lib= + found=no + case $deplib in + -mt|-mthreads|-kthread|-Kthread|-pthread|-pthreads|--thread-safe \ + |-threads|-fopenmp|-openmp|-mp|-xopenmp|-omp|-qsmp=*) + if test "$linkmode,$pass" = "prog,link"; then + compile_deplibs="$deplib $compile_deplibs" + finalize_deplibs="$deplib $finalize_deplibs" + else + func_append compiler_flags " $deplib" + if test "$linkmode" = lib ; then + case "$new_inherited_linker_flags " in + *" $deplib "*) ;; + * ) func_append new_inherited_linker_flags " $deplib" ;; + esac + fi + fi + continue + ;; + -l*) + if test "$linkmode" != lib && test "$linkmode" != prog; then + func_warning "\`-l' is ignored for archives/objects" + continue + fi + func_stripname '-l' '' "$deplib" + name=$func_stripname_result + if test "$linkmode" = lib; then + searchdirs="$newlib_search_path $lib_search_path $compiler_lib_search_dirs $sys_lib_search_path $shlib_search_path" + else + searchdirs="$newlib_search_path $lib_search_path $sys_lib_search_path $shlib_search_path" + fi + for searchdir in $searchdirs; do + for search_ext in .la $std_shrext .so .a; do + # Search the libtool library + lib="$searchdir/lib${name}${search_ext}" + if test -f "$lib"; then + if test "$search_ext" = ".la"; then + found=yes + else + found=no + fi + break 2 + fi + done + done + if test "$found" != yes; then + # deplib doesn't seem to be a libtool library + if test "$linkmode,$pass" = "prog,link"; then + compile_deplibs="$deplib $compile_deplibs" + finalize_deplibs="$deplib $finalize_deplibs" + else + deplibs="$deplib $deplibs" + test "$linkmode" = lib && newdependency_libs="$deplib $newdependency_libs" + fi + continue + else # deplib is a libtool library + # If $allow_libtool_libs_with_static_runtimes && $deplib is a stdlib, + # We need to do some special things here, and not later. + if test "X$allow_libtool_libs_with_static_runtimes" = "Xyes" ; then + case " $predeps $postdeps " in + *" $deplib "*) + if func_lalib_p "$lib"; then + library_names= + old_library= + func_source "$lib" + for l in $old_library $library_names; do + ll="$l" + done + if test "X$ll" = "X$old_library" ; then # only static version available + found=no + func_dirname "$lib" "" "." + ladir="$func_dirname_result" + lib=$ladir/$old_library + if test "$linkmode,$pass" = "prog,link"; then + compile_deplibs="$deplib $compile_deplibs" + finalize_deplibs="$deplib $finalize_deplibs" + else + deplibs="$deplib $deplibs" + test "$linkmode" = lib && newdependency_libs="$deplib $newdependency_libs" + fi + continue + fi + fi + ;; + *) ;; + esac + fi + fi + ;; # -l + *.ltframework) + if test "$linkmode,$pass" = "prog,link"; then + compile_deplibs="$deplib $compile_deplibs" + finalize_deplibs="$deplib $finalize_deplibs" + else + deplibs="$deplib $deplibs" + if test "$linkmode" = lib ; then + case "$new_inherited_linker_flags " in + *" $deplib "*) ;; + * ) func_append new_inherited_linker_flags " $deplib" ;; + esac + fi + fi + continue + ;; + -L*) + case $linkmode in + lib) + deplibs="$deplib $deplibs" + test "$pass" = conv && continue + newdependency_libs="$deplib $newdependency_libs" + func_stripname '-L' '' "$deplib" + func_resolve_sysroot "$func_stripname_result" + func_append newlib_search_path " $func_resolve_sysroot_result" + ;; + prog) + if test "$pass" = conv; then + deplibs="$deplib $deplibs" + continue + fi + if test "$pass" = scan; then + deplibs="$deplib $deplibs" + else + compile_deplibs="$deplib $compile_deplibs" + finalize_deplibs="$deplib $finalize_deplibs" + fi + func_stripname '-L' '' "$deplib" + func_resolve_sysroot "$func_stripname_result" + func_append newlib_search_path " $func_resolve_sysroot_result" + ;; + *) + func_warning "\`-L' is ignored for archives/objects" + ;; + esac # linkmode + continue + ;; # -L + -R*) + if test "$pass" = link; then + func_stripname '-R' '' "$deplib" + func_resolve_sysroot "$func_stripname_result" + dir=$func_resolve_sysroot_result + # Make sure the xrpath contains only unique directories. + case "$xrpath " in + *" $dir "*) ;; + *) func_append xrpath " $dir" ;; + esac + fi + deplibs="$deplib $deplibs" + continue + ;; + *.la) + func_resolve_sysroot "$deplib" + lib=$func_resolve_sysroot_result + ;; + *.$libext) + if test "$pass" = conv; then + deplibs="$deplib $deplibs" + continue + fi + case $linkmode in + lib) + # Linking convenience modules into shared libraries is allowed, + # but linking other static libraries is non-portable. + case " $dlpreconveniencelibs " in + *" $deplib "*) ;; + *) + valid_a_lib=no + case $deplibs_check_method in + match_pattern*) + set dummy $deplibs_check_method; shift + match_pattern_regex=`expr "$deplibs_check_method" : "$1 \(.*\)"` + if eval "\$ECHO \"$deplib\"" 2>/dev/null | $SED 10q \ + | $EGREP "$match_pattern_regex" > /dev/null; then + valid_a_lib=yes + fi + ;; + pass_all) + valid_a_lib=yes + ;; + esac + if test "$valid_a_lib" != yes; then + echo + $ECHO "*** Warning: Trying to link with static lib archive $deplib." + echo "*** I have the capability to make that library automatically link in when" + echo "*** you link to this library. But I can only do this if you have a" + echo "*** shared version of the library, which you do not appear to have" + echo "*** because the file extensions .$libext of this argument makes me believe" + echo "*** that it is just a static archive that I should not use here." + else + echo + $ECHO "*** Warning: Linking the shared library $output against the" + $ECHO "*** static library $deplib is not portable!" + deplibs="$deplib $deplibs" + fi + ;; + esac + continue + ;; + prog) + if test "$pass" != link; then + deplibs="$deplib $deplibs" + else + compile_deplibs="$deplib $compile_deplibs" + finalize_deplibs="$deplib $finalize_deplibs" + fi + continue + ;; + esac # linkmode + ;; # *.$libext + *.lo | *.$objext) + if test "$pass" = conv; then + deplibs="$deplib $deplibs" + elif test "$linkmode" = prog; then + if test "$pass" = dlpreopen || test "$dlopen_support" != yes || test "$build_libtool_libs" = no; then + # If there is no dlopen support or we're linking statically, + # we need to preload. + func_append newdlprefiles " $deplib" + compile_deplibs="$deplib $compile_deplibs" + finalize_deplibs="$deplib $finalize_deplibs" + else + func_append newdlfiles " $deplib" + fi + fi + continue + ;; + %DEPLIBS%) + alldeplibs=yes + continue + ;; + esac # case $deplib + + if test "$found" = yes || test -f "$lib"; then : + else + func_fatal_error "cannot find the library \`$lib' or unhandled argument \`$deplib'" + fi + + # Check to see that this really is a libtool archive. + func_lalib_unsafe_p "$lib" \ + || func_fatal_error "\`$lib' is not a valid libtool archive" + + func_dirname "$lib" "" "." + ladir="$func_dirname_result" + + dlname= + dlopen= + dlpreopen= + libdir= + library_names= + old_library= + inherited_linker_flags= + # If the library was installed with an old release of libtool, + # it will not redefine variables installed, or shouldnotlink + installed=yes + shouldnotlink=no + avoidtemprpath= + + + # Read the .la file + func_source "$lib" + + # Convert "-framework foo" to "foo.ltframework" + if test -n "$inherited_linker_flags"; then + tmp_inherited_linker_flags=`$ECHO "$inherited_linker_flags" | $SED 's/-framework \([^ $]*\)/\1.ltframework/g'` + for tmp_inherited_linker_flag in $tmp_inherited_linker_flags; do + case " $new_inherited_linker_flags " in + *" $tmp_inherited_linker_flag "*) ;; + *) func_append new_inherited_linker_flags " $tmp_inherited_linker_flag";; + esac + done + fi + dependency_libs=`$ECHO " $dependency_libs" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'` + if test "$linkmode,$pass" = "lib,link" || + test "$linkmode,$pass" = "prog,scan" || + { test "$linkmode" != prog && test "$linkmode" != lib; }; then + test -n "$dlopen" && func_append dlfiles " $dlopen" + test -n "$dlpreopen" && func_append dlprefiles " $dlpreopen" + fi + + if test "$pass" = conv; then + # Only check for convenience libraries + deplibs="$lib $deplibs" + if test -z "$libdir"; then + if test -z "$old_library"; then + func_fatal_error "cannot find name of link library for \`$lib'" + fi + # It is a libtool convenience library, so add in its objects. + func_append convenience " $ladir/$objdir/$old_library" + func_append old_convenience " $ladir/$objdir/$old_library" + tmp_libs= + for deplib in $dependency_libs; do + deplibs="$deplib $deplibs" + if $opt_preserve_dup_deps ; then + case "$tmp_libs " in + *" $deplib "*) func_append specialdeplibs " $deplib" ;; + esac + fi + func_append tmp_libs " $deplib" + done + elif test "$linkmode" != prog && test "$linkmode" != lib; then + func_fatal_error "\`$lib' is not a convenience library" + fi + continue + fi # $pass = conv + + + # Get the name of the library we link against. + linklib= + if test -n "$old_library" && + { test "$prefer_static_libs" = yes || + test "$prefer_static_libs,$installed" = "built,no"; }; then + linklib=$old_library + else + for l in $old_library $library_names; do + linklib="$l" + done + fi + if test -z "$linklib"; then + func_fatal_error "cannot find name of link library for \`$lib'" + fi + + # This library was specified with -dlopen. + if test "$pass" = dlopen; then + if test -z "$libdir"; then + func_fatal_error "cannot -dlopen a convenience library: \`$lib'" + fi + if test -z "$dlname" || + test "$dlopen_support" != yes || + test "$build_libtool_libs" = no; then + # If there is no dlname, no dlopen support or we're linking + # statically, we need to preload. We also need to preload any + # dependent libraries so libltdl's deplib preloader doesn't + # bomb out in the load deplibs phase. + func_append dlprefiles " $lib $dependency_libs" + else + func_append newdlfiles " $lib" + fi + continue + fi # $pass = dlopen + + # We need an absolute path. + case $ladir in + [\\/]* | [A-Za-z]:[\\/]*) abs_ladir="$ladir" ;; + *) + abs_ladir=`cd "$ladir" && pwd` + if test -z "$abs_ladir"; then + func_warning "cannot determine absolute directory name of \`$ladir'" + func_warning "passing it literally to the linker, although it might fail" + abs_ladir="$ladir" + fi + ;; + esac + func_basename "$lib" + laname="$func_basename_result" + + # Find the relevant object directory and library name. + if test "X$installed" = Xyes; then + if test ! -f "$lt_sysroot$libdir/$linklib" && test -f "$abs_ladir/$linklib"; then + func_warning "library \`$lib' was moved." + dir="$ladir" + absdir="$abs_ladir" + libdir="$abs_ladir" + else + dir="$lt_sysroot$libdir" + absdir="$lt_sysroot$libdir" + fi + test "X$hardcode_automatic" = Xyes && avoidtemprpath=yes + else + if test ! -f "$ladir/$objdir/$linklib" && test -f "$abs_ladir/$linklib"; then + dir="$ladir" + absdir="$abs_ladir" + # Remove this search path later + func_append notinst_path " $abs_ladir" + else + dir="$ladir/$objdir" + absdir="$abs_ladir/$objdir" + # Remove this search path later + func_append notinst_path " $abs_ladir" + fi + fi # $installed = yes + func_stripname 'lib' '.la' "$laname" + name=$func_stripname_result + + # This library was specified with -dlpreopen. + if test "$pass" = dlpreopen; then + if test -z "$libdir" && test "$linkmode" = prog; then + func_fatal_error "only libraries may -dlpreopen a convenience library: \`$lib'" + fi + case "$host" in + # special handling for platforms with PE-DLLs. + *cygwin* | *mingw* | *cegcc* ) + # Linker will automatically link against shared library if both + # static and shared are present. Therefore, ensure we extract + # symbols from the import library if a shared library is present + # (otherwise, the dlopen module name will be incorrect). We do + # this by putting the import library name into $newdlprefiles. + # We recover the dlopen module name by 'saving' the la file + # name in a special purpose variable, and (later) extracting the + # dlname from the la file. + if test -n "$dlname"; then + func_tr_sh "$dir/$linklib" + eval "libfile_$func_tr_sh_result=\$abs_ladir/\$laname" + func_append newdlprefiles " $dir/$linklib" + else + func_append newdlprefiles " $dir/$old_library" + # Keep a list of preopened convenience libraries to check + # that they are being used correctly in the link pass. + test -z "$libdir" && \ + func_append dlpreconveniencelibs " $dir/$old_library" + fi + ;; + * ) + # Prefer using a static library (so that no silly _DYNAMIC symbols + # are required to link). + if test -n "$old_library"; then + func_append newdlprefiles " $dir/$old_library" + # Keep a list of preopened convenience libraries to check + # that they are being used correctly in the link pass. + test -z "$libdir" && \ + func_append dlpreconveniencelibs " $dir/$old_library" + # Otherwise, use the dlname, so that lt_dlopen finds it. + elif test -n "$dlname"; then + func_append newdlprefiles " $dir/$dlname" + else + func_append newdlprefiles " $dir/$linklib" + fi + ;; + esac + fi # $pass = dlpreopen + + if test -z "$libdir"; then + # Link the convenience library + if test "$linkmode" = lib; then + deplibs="$dir/$old_library $deplibs" + elif test "$linkmode,$pass" = "prog,link"; then + compile_deplibs="$dir/$old_library $compile_deplibs" + finalize_deplibs="$dir/$old_library $finalize_deplibs" + else + deplibs="$lib $deplibs" # used for prog,scan pass + fi + continue + fi + + + if test "$linkmode" = prog && test "$pass" != link; then + func_append newlib_search_path " $ladir" + deplibs="$lib $deplibs" + + linkalldeplibs=no + if test "$link_all_deplibs" != no || test -z "$library_names" || + test "$build_libtool_libs" = no; then + linkalldeplibs=yes + fi + + tmp_libs= + for deplib in $dependency_libs; do + case $deplib in + -L*) func_stripname '-L' '' "$deplib" + func_resolve_sysroot "$func_stripname_result" + func_append newlib_search_path " $func_resolve_sysroot_result" + ;; + esac + # Need to link against all dependency_libs? + if test "$linkalldeplibs" = yes; then + deplibs="$deplib $deplibs" + else + # Need to hardcode shared library paths + # or/and link against static libraries + newdependency_libs="$deplib $newdependency_libs" + fi + if $opt_preserve_dup_deps ; then + case "$tmp_libs " in + *" $deplib "*) func_append specialdeplibs " $deplib" ;; + esac + fi + func_append tmp_libs " $deplib" + done # for deplib + continue + fi # $linkmode = prog... + + if test "$linkmode,$pass" = "prog,link"; then + if test -n "$library_names" && + { { test "$prefer_static_libs" = no || + test "$prefer_static_libs,$installed" = "built,yes"; } || + test -z "$old_library"; }; then + # We need to hardcode the library path + if test -n "$shlibpath_var" && test -z "$avoidtemprpath" ; then + # Make sure the rpath contains only unique directories. + case "$temp_rpath:" in + *"$absdir:"*) ;; + *) func_append temp_rpath "$absdir:" ;; + esac + fi + + # Hardcode the library path. + # Skip directories that are in the system default run-time + # search path. + case " $sys_lib_dlsearch_path " in + *" $absdir "*) ;; + *) + case "$compile_rpath " in + *" $absdir "*) ;; + *) func_append compile_rpath " $absdir" ;; + esac + ;; + esac + case " $sys_lib_dlsearch_path " in + *" $libdir "*) ;; + *) + case "$finalize_rpath " in + *" $libdir "*) ;; + *) func_append finalize_rpath " $libdir" ;; + esac + ;; + esac + fi # $linkmode,$pass = prog,link... + + if test "$alldeplibs" = yes && + { test "$deplibs_check_method" = pass_all || + { test "$build_libtool_libs" = yes && + test -n "$library_names"; }; }; then + # We only need to search for static libraries + continue + fi + fi + + link_static=no # Whether the deplib will be linked statically + use_static_libs=$prefer_static_libs + if test "$use_static_libs" = built && test "$installed" = yes; then + use_static_libs=no + fi + if test -n "$library_names" && + { test "$use_static_libs" = no || test -z "$old_library"; }; then + case $host in + *cygwin* | *mingw* | *cegcc*) + # No point in relinking DLLs because paths are not encoded + func_append notinst_deplibs " $lib" + need_relink=no + ;; + *) + if test "$installed" = no; then + func_append notinst_deplibs " $lib" + need_relink=yes + fi + ;; + esac + # This is a shared library + + # Warn about portability, can't link against -module's on some + # systems (darwin). Don't bleat about dlopened modules though! + dlopenmodule="" + for dlpremoduletest in $dlprefiles; do + if test "X$dlpremoduletest" = "X$lib"; then + dlopenmodule="$dlpremoduletest" + break + fi + done + if test -z "$dlopenmodule" && test "$shouldnotlink" = yes && test "$pass" = link; then + echo + if test "$linkmode" = prog; then + $ECHO "*** Warning: Linking the executable $output against the loadable module" + else + $ECHO "*** Warning: Linking the shared library $output against the loadable module" + fi + $ECHO "*** $linklib is not portable!" + fi + if test "$linkmode" = lib && + test "$hardcode_into_libs" = yes; then + # Hardcode the library path. + # Skip directories that are in the system default run-time + # search path. + case " $sys_lib_dlsearch_path " in + *" $absdir "*) ;; + *) + case "$compile_rpath " in + *" $absdir "*) ;; + *) func_append compile_rpath " $absdir" ;; + esac + ;; + esac + case " $sys_lib_dlsearch_path " in + *" $libdir "*) ;; + *) + case "$finalize_rpath " in + *" $libdir "*) ;; + *) func_append finalize_rpath " $libdir" ;; + esac + ;; + esac + fi + + if test -n "$old_archive_from_expsyms_cmds"; then + # figure out the soname + set dummy $library_names + shift + realname="$1" + shift + libname=`eval "\\$ECHO \"$libname_spec\""` + # use dlname if we got it. it's perfectly good, no? + if test -n "$dlname"; then + soname="$dlname" + elif test -n "$soname_spec"; then + # bleh windows + case $host in + *cygwin* | mingw* | *cegcc*) + func_arith $current - $age + major=$func_arith_result + versuffix="-$major" + ;; + esac + eval soname=\"$soname_spec\" + else + soname="$realname" + fi + + # Make a new name for the extract_expsyms_cmds to use + soroot="$soname" + func_basename "$soroot" + soname="$func_basename_result" + func_stripname 'lib' '.dll' "$soname" + newlib=libimp-$func_stripname_result.a + + # If the library has no export list, then create one now + if test -f "$output_objdir/$soname-def"; then : + else + func_verbose "extracting exported symbol list from \`$soname'" + func_execute_cmds "$extract_expsyms_cmds" 'exit $?' + fi + + # Create $newlib + if test -f "$output_objdir/$newlib"; then :; else + func_verbose "generating import library for \`$soname'" + func_execute_cmds "$old_archive_from_expsyms_cmds" 'exit $?' + fi + # make sure the library variables are pointing to the new library + dir=$output_objdir + linklib=$newlib + fi # test -n "$old_archive_from_expsyms_cmds" + + if test "$linkmode" = prog || test "$opt_mode" != relink; then + add_shlibpath= + add_dir= + add= + lib_linked=yes + case $hardcode_action in + immediate | unsupported) + if test "$hardcode_direct" = no; then + add="$dir/$linklib" + case $host in + *-*-sco3.2v5.0.[024]*) add_dir="-L$dir" ;; + *-*-sysv4*uw2*) add_dir="-L$dir" ;; + *-*-sysv5OpenUNIX* | *-*-sysv5UnixWare7.[01].[10]* | \ + *-*-unixware7*) add_dir="-L$dir" ;; + *-*-darwin* ) + # if the lib is a (non-dlopened) module then we can not + # link against it, someone is ignoring the earlier warnings + if /usr/bin/file -L $add 2> /dev/null | + $GREP ": [^:]* bundle" >/dev/null ; then + if test "X$dlopenmodule" != "X$lib"; then + $ECHO "*** Warning: lib $linklib is a module, not a shared library" + if test -z "$old_library" ; then + echo + echo "*** And there doesn't seem to be a static archive available" + echo "*** The link will probably fail, sorry" + else + add="$dir/$old_library" + fi + elif test -n "$old_library"; then + add="$dir/$old_library" + fi + fi + esac + elif test "$hardcode_minus_L" = no; then + case $host in + *-*-sunos*) add_shlibpath="$dir" ;; + esac + add_dir="-L$dir" + add="-l$name" + elif test "$hardcode_shlibpath_var" = no; then + add_shlibpath="$dir" + add="-l$name" + else + lib_linked=no + fi + ;; + relink) + if test "$hardcode_direct" = yes && + test "$hardcode_direct_absolute" = no; then + add="$dir/$linklib" + elif test "$hardcode_minus_L" = yes; then + add_dir="-L$absdir" + # Try looking first in the location we're being installed to. + if test -n "$inst_prefix_dir"; then + case $libdir in + [\\/]*) + func_append add_dir " -L$inst_prefix_dir$libdir" + ;; + esac + fi + add="-l$name" + elif test "$hardcode_shlibpath_var" = yes; then + add_shlibpath="$dir" + add="-l$name" + else + lib_linked=no + fi + ;; + *) lib_linked=no ;; + esac + + if test "$lib_linked" != yes; then + func_fatal_configuration "unsupported hardcode properties" + fi + + if test -n "$add_shlibpath"; then + case :$compile_shlibpath: in + *":$add_shlibpath:"*) ;; + *) func_append compile_shlibpath "$add_shlibpath:" ;; + esac + fi + if test "$linkmode" = prog; then + test -n "$add_dir" && compile_deplibs="$add_dir $compile_deplibs" + test -n "$add" && compile_deplibs="$add $compile_deplibs" + else + test -n "$add_dir" && deplibs="$add_dir $deplibs" + test -n "$add" && deplibs="$add $deplibs" + if test "$hardcode_direct" != yes && + test "$hardcode_minus_L" != yes && + test "$hardcode_shlibpath_var" = yes; then + case :$finalize_shlibpath: in + *":$libdir:"*) ;; + *) func_append finalize_shlibpath "$libdir:" ;; + esac + fi + fi + fi + + if test "$linkmode" = prog || test "$opt_mode" = relink; then + add_shlibpath= + add_dir= + add= + # Finalize command for both is simple: just hardcode it. + if test "$hardcode_direct" = yes && + test "$hardcode_direct_absolute" = no; then + add="$libdir/$linklib" + elif test "$hardcode_minus_L" = yes; then + add_dir="-L$libdir" + add="-l$name" + elif test "$hardcode_shlibpath_var" = yes; then + case :$finalize_shlibpath: in + *":$libdir:"*) ;; + *) func_append finalize_shlibpath "$libdir:" ;; + esac + add="-l$name" + elif test "$hardcode_automatic" = yes; then + if test -n "$inst_prefix_dir" && + test -f "$inst_prefix_dir$libdir/$linklib" ; then + add="$inst_prefix_dir$libdir/$linklib" + else + add="$libdir/$linklib" + fi + else + # We cannot seem to hardcode it, guess we'll fake it. + add_dir="-L$libdir" + # Try looking first in the location we're being installed to. + if test -n "$inst_prefix_dir"; then + case $libdir in + [\\/]*) + func_append add_dir " -L$inst_prefix_dir$libdir" + ;; + esac + fi + add="-l$name" + fi + + if test "$linkmode" = prog; then + test -n "$add_dir" && finalize_deplibs="$add_dir $finalize_deplibs" + test -n "$add" && finalize_deplibs="$add $finalize_deplibs" + else + test -n "$add_dir" && deplibs="$add_dir $deplibs" + test -n "$add" && deplibs="$add $deplibs" + fi + fi + elif test "$linkmode" = prog; then + # Here we assume that one of hardcode_direct or hardcode_minus_L + # is not unsupported. This is valid on all known static and + # shared platforms. + if test "$hardcode_direct" != unsupported; then + test -n "$old_library" && linklib="$old_library" + compile_deplibs="$dir/$linklib $compile_deplibs" + finalize_deplibs="$dir/$linklib $finalize_deplibs" + else + compile_deplibs="-l$name -L$dir $compile_deplibs" + finalize_deplibs="-l$name -L$dir $finalize_deplibs" + fi + elif test "$build_libtool_libs" = yes; then + # Not a shared library + if test "$deplibs_check_method" != pass_all; then + # We're trying link a shared library against a static one + # but the system doesn't support it. + + # Just print a warning and add the library to dependency_libs so + # that the program can be linked against the static library. + echo + $ECHO "*** Warning: This system can not link to static lib archive $lib." + echo "*** I have the capability to make that library automatically link in when" + echo "*** you link to this library. But I can only do this if you have a" + echo "*** shared version of the library, which you do not appear to have." + if test "$module" = yes; then + echo "*** But as you try to build a module library, libtool will still create " + echo "*** a static module, that should work as long as the dlopening application" + echo "*** is linked with the -dlopen flag to resolve symbols at runtime." + if test -z "$global_symbol_pipe"; then + echo + echo "*** However, this would only work if libtool was able to extract symbol" + echo "*** lists from a program, using \`nm' or equivalent, but libtool could" + echo "*** not find such a program. So, this module is probably useless." + echo "*** \`nm' from GNU binutils and a full rebuild may help." + fi + if test "$build_old_libs" = no; then + build_libtool_libs=module + build_old_libs=yes + else + build_libtool_libs=no + fi + fi + else + deplibs="$dir/$old_library $deplibs" + link_static=yes + fi + fi # link shared/static library? + + if test "$linkmode" = lib; then + if test -n "$dependency_libs" && + { test "$hardcode_into_libs" != yes || + test "$build_old_libs" = yes || + test "$link_static" = yes; }; then + # Extract -R from dependency_libs + temp_deplibs= + for libdir in $dependency_libs; do + case $libdir in + -R*) func_stripname '-R' '' "$libdir" + temp_xrpath=$func_stripname_result + case " $xrpath " in + *" $temp_xrpath "*) ;; + *) func_append xrpath " $temp_xrpath";; + esac;; + *) func_append temp_deplibs " $libdir";; + esac + done + dependency_libs="$temp_deplibs" + fi + + func_append newlib_search_path " $absdir" + # Link against this library + test "$link_static" = no && newdependency_libs="$abs_ladir/$laname $newdependency_libs" + # ... and its dependency_libs + tmp_libs= + for deplib in $dependency_libs; do + newdependency_libs="$deplib $newdependency_libs" + case $deplib in + -L*) func_stripname '-L' '' "$deplib" + func_resolve_sysroot "$func_stripname_result";; + *) func_resolve_sysroot "$deplib" ;; + esac + if $opt_preserve_dup_deps ; then + case "$tmp_libs " in + *" $func_resolve_sysroot_result "*) + func_append specialdeplibs " $func_resolve_sysroot_result" ;; + esac + fi + func_append tmp_libs " $func_resolve_sysroot_result" + done + + if test "$link_all_deplibs" != no; then + # Add the search paths of all dependency libraries + for deplib in $dependency_libs; do + path= + case $deplib in + -L*) path="$deplib" ;; + *.la) + func_resolve_sysroot "$deplib" + deplib=$func_resolve_sysroot_result + func_dirname "$deplib" "" "." + dir=$func_dirname_result + # We need an absolute path. + case $dir in + [\\/]* | [A-Za-z]:[\\/]*) absdir="$dir" ;; + *) + absdir=`cd "$dir" && pwd` + if test -z "$absdir"; then + func_warning "cannot determine absolute directory name of \`$dir'" + absdir="$dir" + fi + ;; + esac + if $GREP "^installed=no" $deplib > /dev/null; then + case $host in + *-*-darwin*) + depdepl= + eval deplibrary_names=`${SED} -n -e 's/^library_names=\(.*\)$/\1/p' $deplib` + if test -n "$deplibrary_names" ; then + for tmp in $deplibrary_names ; do + depdepl=$tmp + done + if test -f "$absdir/$objdir/$depdepl" ; then + depdepl="$absdir/$objdir/$depdepl" + darwin_install_name=`${OTOOL} -L $depdepl | awk '{if (NR == 2) {print $1;exit}}'` + if test -z "$darwin_install_name"; then + darwin_install_name=`${OTOOL64} -L $depdepl | awk '{if (NR == 2) {print $1;exit}}'` + fi + func_append compiler_flags " ${wl}-dylib_file ${wl}${darwin_install_name}:${depdepl}" + func_append linker_flags " -dylib_file ${darwin_install_name}:${depdepl}" + path= + fi + fi + ;; + *) + path="-L$absdir/$objdir" + ;; + esac + else + eval libdir=`${SED} -n -e 's/^libdir=\(.*\)$/\1/p' $deplib` + test -z "$libdir" && \ + func_fatal_error "\`$deplib' is not a valid libtool archive" + test "$absdir" != "$libdir" && \ + func_warning "\`$deplib' seems to be moved" + + path="-L$absdir" + fi + ;; + esac + case " $deplibs " in + *" $path "*) ;; + *) deplibs="$path $deplibs" ;; + esac + done + fi # link_all_deplibs != no + fi # linkmode = lib + done # for deplib in $libs + if test "$pass" = link; then + if test "$linkmode" = "prog"; then + compile_deplibs="$new_inherited_linker_flags $compile_deplibs" + finalize_deplibs="$new_inherited_linker_flags $finalize_deplibs" + else + compiler_flags="$compiler_flags "`$ECHO " $new_inherited_linker_flags" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'` + fi + fi + dependency_libs="$newdependency_libs" + if test "$pass" = dlpreopen; then + # Link the dlpreopened libraries before other libraries + for deplib in $save_deplibs; do + deplibs="$deplib $deplibs" + done + fi + if test "$pass" != dlopen; then + if test "$pass" != conv; then + # Make sure lib_search_path contains only unique directories. + lib_search_path= + for dir in $newlib_search_path; do + case "$lib_search_path " in + *" $dir "*) ;; + *) func_append lib_search_path " $dir" ;; + esac + done + newlib_search_path= + fi + + if test "$linkmode,$pass" != "prog,link"; then + vars="deplibs" + else + vars="compile_deplibs finalize_deplibs" + fi + for var in $vars dependency_libs; do + # Add libraries to $var in reverse order + eval tmp_libs=\"\$$var\" + new_libs= + for deplib in $tmp_libs; do + # FIXME: Pedantically, this is the right thing to do, so + # that some nasty dependency loop isn't accidentally + # broken: + #new_libs="$deplib $new_libs" + # Pragmatically, this seems to cause very few problems in + # practice: + case $deplib in + -L*) new_libs="$deplib $new_libs" ;; + -R*) ;; + *) + # And here is the reason: when a library appears more + # than once as an explicit dependence of a library, or + # is implicitly linked in more than once by the + # compiler, it is considered special, and multiple + # occurrences thereof are not removed. Compare this + # with having the same library being listed as a + # dependency of multiple other libraries: in this case, + # we know (pedantically, we assume) the library does not + # need to be listed more than once, so we keep only the + # last copy. This is not always right, but it is rare + # enough that we require users that really mean to play + # such unportable linking tricks to link the library + # using -Wl,-lname, so that libtool does not consider it + # for duplicate removal. + case " $specialdeplibs " in + *" $deplib "*) new_libs="$deplib $new_libs" ;; + *) + case " $new_libs " in + *" $deplib "*) ;; + *) new_libs="$deplib $new_libs" ;; + esac + ;; + esac + ;; + esac + done + tmp_libs= + for deplib in $new_libs; do + case $deplib in + -L*) + case " $tmp_libs " in + *" $deplib "*) ;; + *) func_append tmp_libs " $deplib" ;; + esac + ;; + *) func_append tmp_libs " $deplib" ;; + esac + done + eval $var=\"$tmp_libs\" + done # for var + fi + # Last step: remove runtime libs from dependency_libs + # (they stay in deplibs) + tmp_libs= + for i in $dependency_libs ; do + case " $predeps $postdeps $compiler_lib_search_path " in + *" $i "*) + i="" + ;; + esac + if test -n "$i" ; then + func_append tmp_libs " $i" + fi + done + dependency_libs=$tmp_libs + done # for pass + if test "$linkmode" = prog; then + dlfiles="$newdlfiles" + fi + if test "$linkmode" = prog || test "$linkmode" = lib; then + dlprefiles="$newdlprefiles" + fi + + case $linkmode in + oldlib) + if test -n "$dlfiles$dlprefiles" || test "$dlself" != no; then + func_warning "\`-dlopen' is ignored for archives" + fi + + case " $deplibs" in + *\ -l* | *\ -L*) + func_warning "\`-l' and \`-L' are ignored for archives" ;; + esac + + test -n "$rpath" && \ + func_warning "\`-rpath' is ignored for archives" + + test -n "$xrpath" && \ + func_warning "\`-R' is ignored for archives" + + test -n "$vinfo" && \ + func_warning "\`-version-info/-version-number' is ignored for archives" + + test -n "$release" && \ + func_warning "\`-release' is ignored for archives" + + test -n "$export_symbols$export_symbols_regex" && \ + func_warning "\`-export-symbols' is ignored for archives" + + # Now set the variables for building old libraries. + build_libtool_libs=no + oldlibs="$output" + func_append objs "$old_deplibs" + ;; + + lib) + # Make sure we only generate libraries of the form `libNAME.la'. + case $outputname in + lib*) + func_stripname 'lib' '.la' "$outputname" + name=$func_stripname_result + eval shared_ext=\"$shrext_cmds\" + eval libname=\"$libname_spec\" + ;; + *) + test "$module" = no && \ + func_fatal_help "libtool library \`$output' must begin with \`lib'" + + if test "$need_lib_prefix" != no; then + # Add the "lib" prefix for modules if required + func_stripname '' '.la' "$outputname" + name=$func_stripname_result + eval shared_ext=\"$shrext_cmds\" + eval libname=\"$libname_spec\" + else + func_stripname '' '.la' "$outputname" + libname=$func_stripname_result + fi + ;; + esac + + if test -n "$objs"; then + if test "$deplibs_check_method" != pass_all; then + func_fatal_error "cannot build libtool library \`$output' from non-libtool objects on this host:$objs" + else + echo + $ECHO "*** Warning: Linking the shared library $output against the non-libtool" + $ECHO "*** objects $objs is not portable!" + func_append libobjs " $objs" + fi + fi + + test "$dlself" != no && \ + func_warning "\`-dlopen self' is ignored for libtool libraries" + + set dummy $rpath + shift + test "$#" -gt 1 && \ + func_warning "ignoring multiple \`-rpath's for a libtool library" + + install_libdir="$1" + + oldlibs= + if test -z "$rpath"; then + if test "$build_libtool_libs" = yes; then + # Building a libtool convenience library. + # Some compilers have problems with a `.al' extension so + # convenience libraries should have the same extension an + # archive normally would. + oldlibs="$output_objdir/$libname.$libext $oldlibs" + build_libtool_libs=convenience + build_old_libs=yes + fi + + test -n "$vinfo" && \ + func_warning "\`-version-info/-version-number' is ignored for convenience libraries" + + test -n "$release" && \ + func_warning "\`-release' is ignored for convenience libraries" + else + + # Parse the version information argument. + save_ifs="$IFS"; IFS=':' + set dummy $vinfo 0 0 0 + shift + IFS="$save_ifs" + + test -n "$7" && \ + func_fatal_help "too many parameters to \`-version-info'" + + # convert absolute version numbers to libtool ages + # this retains compatibility with .la files and attempts + # to make the code below a bit more comprehensible + + case $vinfo_number in + yes) + number_major="$1" + number_minor="$2" + number_revision="$3" + # + # There are really only two kinds -- those that + # use the current revision as the major version + # and those that subtract age and use age as + # a minor version. But, then there is irix + # which has an extra 1 added just for fun + # + case $version_type in + # correct linux to gnu/linux during the next big refactor + darwin|linux|osf|windows|none) + func_arith $number_major + $number_minor + current=$func_arith_result + age="$number_minor" + revision="$number_revision" + ;; + freebsd-aout|freebsd-elf|qnx|sunos) + current="$number_major" + revision="$number_minor" + age="0" + ;; + irix|nonstopux) + func_arith $number_major + $number_minor + current=$func_arith_result + age="$number_minor" + revision="$number_minor" + lt_irix_increment=no + ;; + *) + func_fatal_configuration "$modename: unknown library version type \`$version_type'" + ;; + esac + ;; + no) + current="$1" + revision="$2" + age="$3" + ;; + esac + + # Check that each of the things are valid numbers. + case $current in + 0|[1-9]|[1-9][0-9]|[1-9][0-9][0-9]|[1-9][0-9][0-9][0-9]|[1-9][0-9][0-9][0-9][0-9]) ;; + *) + func_error "CURRENT \`$current' must be a nonnegative integer" + func_fatal_error "\`$vinfo' is not valid version information" + ;; + esac + + case $revision in + 0|[1-9]|[1-9][0-9]|[1-9][0-9][0-9]|[1-9][0-9][0-9][0-9]|[1-9][0-9][0-9][0-9][0-9]) ;; + *) + func_error "REVISION \`$revision' must be a nonnegative integer" + func_fatal_error "\`$vinfo' is not valid version information" + ;; + esac + + case $age in + 0|[1-9]|[1-9][0-9]|[1-9][0-9][0-9]|[1-9][0-9][0-9][0-9]|[1-9][0-9][0-9][0-9][0-9]) ;; + *) + func_error "AGE \`$age' must be a nonnegative integer" + func_fatal_error "\`$vinfo' is not valid version information" + ;; + esac + + if test "$age" -gt "$current"; then + func_error "AGE \`$age' is greater than the current interface number \`$current'" + func_fatal_error "\`$vinfo' is not valid version information" + fi + + # Calculate the version variables. + major= + versuffix= + verstring= + case $version_type in + none) ;; + + darwin) + # Like Linux, but with the current version available in + # verstring for coding it into the library header + func_arith $current - $age + major=.$func_arith_result + versuffix="$major.$age.$revision" + # Darwin ld doesn't like 0 for these options... + func_arith $current + 1 + minor_current=$func_arith_result + xlcverstring="${wl}-compatibility_version ${wl}$minor_current ${wl}-current_version ${wl}$minor_current.$revision" + verstring="-compatibility_version $minor_current -current_version $minor_current.$revision" + ;; + + freebsd-aout) + major=".$current" + versuffix=".$current.$revision"; + ;; + + freebsd-elf) + major=".$current" + versuffix=".$current" + ;; + + irix | nonstopux) + if test "X$lt_irix_increment" = "Xno"; then + func_arith $current - $age + else + func_arith $current - $age + 1 + fi + major=$func_arith_result + + case $version_type in + nonstopux) verstring_prefix=nonstopux ;; + *) verstring_prefix=sgi ;; + esac + verstring="$verstring_prefix$major.$revision" + + # Add in all the interfaces that we are compatible with. + loop=$revision + while test "$loop" -ne 0; do + func_arith $revision - $loop + iface=$func_arith_result + func_arith $loop - 1 + loop=$func_arith_result + verstring="$verstring_prefix$major.$iface:$verstring" + done + + # Before this point, $major must not contain `.'. + major=.$major + versuffix="$major.$revision" + ;; + + linux) # correct to gnu/linux during the next big refactor + func_arith $current - $age + major=.$func_arith_result + versuffix="$major.$age.$revision" + ;; + + osf) + func_arith $current - $age + major=.$func_arith_result + versuffix=".$current.$age.$revision" + verstring="$current.$age.$revision" + + # Add in all the interfaces that we are compatible with. + loop=$age + while test "$loop" -ne 0; do + func_arith $current - $loop + iface=$func_arith_result + func_arith $loop - 1 + loop=$func_arith_result + verstring="$verstring:${iface}.0" + done + + # Make executables depend on our current version. + func_append verstring ":${current}.0" + ;; + + qnx) + major=".$current" + versuffix=".$current" + ;; + + sunos) + major=".$current" + versuffix=".$current.$revision" + ;; + + windows) + # Use '-' rather than '.', since we only want one + # extension on DOS 8.3 filesystems. + func_arith $current - $age + major=$func_arith_result + versuffix="-$major" + ;; + + *) + func_fatal_configuration "unknown library version type \`$version_type'" + ;; + esac + + # Clear the version info if we defaulted, and they specified a release. + if test -z "$vinfo" && test -n "$release"; then + major= + case $version_type in + darwin) + # we can't check for "0.0" in archive_cmds due to quoting + # problems, so we reset it completely + verstring= + ;; + *) + verstring="0.0" + ;; + esac + if test "$need_version" = no; then + versuffix= + else + versuffix=".0.0" + fi + fi + + # Remove version info from name if versioning should be avoided + if test "$avoid_version" = yes && test "$need_version" = no; then + major= + versuffix= + verstring="" + fi + + # Check to see if the archive will have undefined symbols. + if test "$allow_undefined" = yes; then + if test "$allow_undefined_flag" = unsupported; then + func_warning "undefined symbols not allowed in $host shared libraries" + build_libtool_libs=no + build_old_libs=yes + fi + else + # Don't allow undefined symbols. + allow_undefined_flag="$no_undefined_flag" + fi + + fi + + func_generate_dlsyms "$libname" "$libname" "yes" + func_append libobjs " $symfileobj" + test "X$libobjs" = "X " && libobjs= + + if test "$opt_mode" != relink; then + # Remove our outputs, but don't remove object files since they + # may have been created when compiling PIC objects. + removelist= + tempremovelist=`$ECHO "$output_objdir/*"` + for p in $tempremovelist; do + case $p in + *.$objext | *.gcno) + ;; + $output_objdir/$outputname | $output_objdir/$libname.* | $output_objdir/${libname}${release}.*) + if test "X$precious_files_regex" != "X"; then + if $ECHO "$p" | $EGREP -e "$precious_files_regex" >/dev/null 2>&1 + then + continue + fi + fi + func_append removelist " $p" + ;; + *) ;; + esac + done + test -n "$removelist" && \ + func_show_eval "${RM}r \$removelist" + fi + + # Now set the variables for building old libraries. + if test "$build_old_libs" = yes && test "$build_libtool_libs" != convenience ; then + func_append oldlibs " $output_objdir/$libname.$libext" + + # Transform .lo files to .o files. + oldobjs="$objs "`$ECHO "$libobjs" | $SP2NL | $SED "/\.${libext}$/d; $lo2o" | $NL2SP` + fi + + # Eliminate all temporary directories. + #for path in $notinst_path; do + # lib_search_path=`$ECHO "$lib_search_path " | $SED "s% $path % %g"` + # deplibs=`$ECHO "$deplibs " | $SED "s% -L$path % %g"` + # dependency_libs=`$ECHO "$dependency_libs " | $SED "s% -L$path % %g"` + #done + + if test -n "$xrpath"; then + # If the user specified any rpath flags, then add them. + temp_xrpath= + for libdir in $xrpath; do + func_replace_sysroot "$libdir" + func_append temp_xrpath " -R$func_replace_sysroot_result" + case "$finalize_rpath " in + *" $libdir "*) ;; + *) func_append finalize_rpath " $libdir" ;; + esac + done + if test "$hardcode_into_libs" != yes || test "$build_old_libs" = yes; then + dependency_libs="$temp_xrpath $dependency_libs" + fi + fi + + # Make sure dlfiles contains only unique files that won't be dlpreopened + old_dlfiles="$dlfiles" + dlfiles= + for lib in $old_dlfiles; do + case " $dlprefiles $dlfiles " in + *" $lib "*) ;; + *) func_append dlfiles " $lib" ;; + esac + done + + # Make sure dlprefiles contains only unique files + old_dlprefiles="$dlprefiles" + dlprefiles= + for lib in $old_dlprefiles; do + case "$dlprefiles " in + *" $lib "*) ;; + *) func_append dlprefiles " $lib" ;; + esac + done + + if test "$build_libtool_libs" = yes; then + if test -n "$rpath"; then + case $host in + *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-*-beos* | *-cegcc* | *-*-haiku*) + # these systems don't actually have a c library (as such)! + ;; + *-*-rhapsody* | *-*-darwin1.[012]) + # Rhapsody C library is in the System framework + func_append deplibs " System.ltframework" + ;; + *-*-netbsd*) + # Don't link with libc until the a.out ld.so is fixed. + ;; + *-*-openbsd* | *-*-freebsd* | *-*-dragonfly*) + # Do not include libc due to us having libc/libc_r. + ;; + *-*-sco3.2v5* | *-*-sco5v6*) + # Causes problems with __ctype + ;; + *-*-sysv4.2uw2* | *-*-sysv5* | *-*-unixware* | *-*-OpenUNIX*) + # Compiler inserts libc in the correct place for threads to work + ;; + *) + # Add libc to deplibs on all other systems if necessary. + if test "$build_libtool_need_lc" = "yes"; then + func_append deplibs " -lc" + fi + ;; + esac + fi + + # Transform deplibs into only deplibs that can be linked in shared. + name_save=$name + libname_save=$libname + release_save=$release + versuffix_save=$versuffix + major_save=$major + # I'm not sure if I'm treating the release correctly. I think + # release should show up in the -l (ie -lgmp5) so we don't want to + # add it in twice. Is that correct? + release="" + versuffix="" + major="" + newdeplibs= + droppeddeps=no + case $deplibs_check_method in + pass_all) + # Don't check for shared/static. Everything works. + # This might be a little naive. We might want to check + # whether the library exists or not. But this is on + # osf3 & osf4 and I'm not really sure... Just + # implementing what was already the behavior. + newdeplibs=$deplibs + ;; + test_compile) + # This code stresses the "libraries are programs" paradigm to its + # limits. Maybe even breaks it. We compile a program, linking it + # against the deplibs as a proxy for the library. Then we can check + # whether they linked in statically or dynamically with ldd. + $opt_dry_run || $RM conftest.c + cat > conftest.c </dev/null` + $nocaseglob + else + potential_libs=`ls $i/$libnameglob[.-]* 2>/dev/null` + fi + for potent_lib in $potential_libs; do + # Follow soft links. + if ls -lLd "$potent_lib" 2>/dev/null | + $GREP " -> " >/dev/null; then + continue + fi + # The statement above tries to avoid entering an + # endless loop below, in case of cyclic links. + # We might still enter an endless loop, since a link + # loop can be closed while we follow links, + # but so what? + potlib="$potent_lib" + while test -h "$potlib" 2>/dev/null; do + potliblink=`ls -ld $potlib | ${SED} 's/.* -> //'` + case $potliblink in + [\\/]* | [A-Za-z]:[\\/]*) potlib="$potliblink";; + *) potlib=`$ECHO "$potlib" | $SED 's,[^/]*$,,'`"$potliblink";; + esac + done + if eval $file_magic_cmd \"\$potlib\" 2>/dev/null | + $SED -e 10q | + $EGREP "$file_magic_regex" > /dev/null; then + func_append newdeplibs " $a_deplib" + a_deplib="" + break 2 + fi + done + done + fi + if test -n "$a_deplib" ; then + droppeddeps=yes + echo + $ECHO "*** Warning: linker path does not have real file for library $a_deplib." + echo "*** I have the capability to make that library automatically link in when" + echo "*** you link to this library. But I can only do this if you have a" + echo "*** shared version of the library, which you do not appear to have" + echo "*** because I did check the linker path looking for a file starting" + if test -z "$potlib" ; then + $ECHO "*** with $libname but no candidates were found. (...for file magic test)" + else + $ECHO "*** with $libname and none of the candidates passed a file format test" + $ECHO "*** using a file magic. Last file checked: $potlib" + fi + fi + ;; + *) + # Add a -L argument. + func_append newdeplibs " $a_deplib" + ;; + esac + done # Gone through all deplibs. + ;; + match_pattern*) + set dummy $deplibs_check_method; shift + match_pattern_regex=`expr "$deplibs_check_method" : "$1 \(.*\)"` + for a_deplib in $deplibs; do + case $a_deplib in + -l*) + func_stripname -l '' "$a_deplib" + name=$func_stripname_result + if test "X$allow_libtool_libs_with_static_runtimes" = "Xyes" ; then + case " $predeps $postdeps " in + *" $a_deplib "*) + func_append newdeplibs " $a_deplib" + a_deplib="" + ;; + esac + fi + if test -n "$a_deplib" ; then + libname=`eval "\\$ECHO \"$libname_spec\""` + for i in $lib_search_path $sys_lib_search_path $shlib_search_path; do + potential_libs=`ls $i/$libname[.-]* 2>/dev/null` + for potent_lib in $potential_libs; do + potlib="$potent_lib" # see symlink-check above in file_magic test + if eval "\$ECHO \"$potent_lib\"" 2>/dev/null | $SED 10q | \ + $EGREP "$match_pattern_regex" > /dev/null; then + func_append newdeplibs " $a_deplib" + a_deplib="" + break 2 + fi + done + done + fi + if test -n "$a_deplib" ; then + droppeddeps=yes + echo + $ECHO "*** Warning: linker path does not have real file for library $a_deplib." + echo "*** I have the capability to make that library automatically link in when" + echo "*** you link to this library. But I can only do this if you have a" + echo "*** shared version of the library, which you do not appear to have" + echo "*** because I did check the linker path looking for a file starting" + if test -z "$potlib" ; then + $ECHO "*** with $libname but no candidates were found. (...for regex pattern test)" + else + $ECHO "*** with $libname and none of the candidates passed a file format test" + $ECHO "*** using a regex pattern. Last file checked: $potlib" + fi + fi + ;; + *) + # Add a -L argument. + func_append newdeplibs " $a_deplib" + ;; + esac + done # Gone through all deplibs. + ;; + none | unknown | *) + newdeplibs="" + tmp_deplibs=`$ECHO " $deplibs" | $SED 's/ -lc$//; s/ -[LR][^ ]*//g'` + if test "X$allow_libtool_libs_with_static_runtimes" = "Xyes" ; then + for i in $predeps $postdeps ; do + # can't use Xsed below, because $i might contain '/' + tmp_deplibs=`$ECHO " $tmp_deplibs" | $SED "s,$i,,"` + done + fi + case $tmp_deplibs in + *[!\ \ ]*) + echo + if test "X$deplibs_check_method" = "Xnone"; then + echo "*** Warning: inter-library dependencies are not supported in this platform." + else + echo "*** Warning: inter-library dependencies are not known to be supported." + fi + echo "*** All declared inter-library dependencies are being dropped." + droppeddeps=yes + ;; + esac + ;; + esac + versuffix=$versuffix_save + major=$major_save + release=$release_save + libname=$libname_save + name=$name_save + + case $host in + *-*-rhapsody* | *-*-darwin1.[012]) + # On Rhapsody replace the C library with the System framework + newdeplibs=`$ECHO " $newdeplibs" | $SED 's/ -lc / System.ltframework /'` + ;; + esac + + if test "$droppeddeps" = yes; then + if test "$module" = yes; then + echo + echo "*** Warning: libtool could not satisfy all declared inter-library" + $ECHO "*** dependencies of module $libname. Therefore, libtool will create" + echo "*** a static module, that should work as long as the dlopening" + echo "*** application is linked with the -dlopen flag." + if test -z "$global_symbol_pipe"; then + echo + echo "*** However, this would only work if libtool was able to extract symbol" + echo "*** lists from a program, using \`nm' or equivalent, but libtool could" + echo "*** not find such a program. So, this module is probably useless." + echo "*** \`nm' from GNU binutils and a full rebuild may help." + fi + if test "$build_old_libs" = no; then + oldlibs="$output_objdir/$libname.$libext" + build_libtool_libs=module + build_old_libs=yes + else + build_libtool_libs=no + fi + else + echo "*** The inter-library dependencies that have been dropped here will be" + echo "*** automatically added whenever a program is linked with this library" + echo "*** or is declared to -dlopen it." + + if test "$allow_undefined" = no; then + echo + echo "*** Since this library must not contain undefined symbols," + echo "*** because either the platform does not support them or" + echo "*** it was explicitly requested with -no-undefined," + echo "*** libtool will only create a static version of it." + if test "$build_old_libs" = no; then + oldlibs="$output_objdir/$libname.$libext" + build_libtool_libs=module + build_old_libs=yes + else + build_libtool_libs=no + fi + fi + fi + fi + # Done checking deplibs! + deplibs=$newdeplibs + fi + # Time to change all our "foo.ltframework" stuff back to "-framework foo" + case $host in + *-*-darwin*) + newdeplibs=`$ECHO " $newdeplibs" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'` + new_inherited_linker_flags=`$ECHO " $new_inherited_linker_flags" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'` + deplibs=`$ECHO " $deplibs" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'` + ;; + esac + + # move library search paths that coincide with paths to not yet + # installed libraries to the beginning of the library search list + new_libs= + for path in $notinst_path; do + case " $new_libs " in + *" -L$path/$objdir "*) ;; + *) + case " $deplibs " in + *" -L$path/$objdir "*) + func_append new_libs " -L$path/$objdir" ;; + esac + ;; + esac + done + for deplib in $deplibs; do + case $deplib in + -L*) + case " $new_libs " in + *" $deplib "*) ;; + *) func_append new_libs " $deplib" ;; + esac + ;; + *) func_append new_libs " $deplib" ;; + esac + done + deplibs="$new_libs" + + # All the library-specific variables (install_libdir is set above). + library_names= + old_library= + dlname= + + # Test again, we may have decided not to build it any more + if test "$build_libtool_libs" = yes; then + # Remove ${wl} instances when linking with ld. + # FIXME: should test the right _cmds variable. + case $archive_cmds in + *\$LD\ *) wl= ;; + esac + if test "$hardcode_into_libs" = yes; then + # Hardcode the library paths + hardcode_libdirs= + dep_rpath= + rpath="$finalize_rpath" + test "$opt_mode" != relink && rpath="$compile_rpath$rpath" + for libdir in $rpath; do + if test -n "$hardcode_libdir_flag_spec"; then + if test -n "$hardcode_libdir_separator"; then + func_replace_sysroot "$libdir" + libdir=$func_replace_sysroot_result + if test -z "$hardcode_libdirs"; then + hardcode_libdirs="$libdir" + else + # Just accumulate the unique libdirs. + case $hardcode_libdir_separator$hardcode_libdirs$hardcode_libdir_separator in + *"$hardcode_libdir_separator$libdir$hardcode_libdir_separator"*) + ;; + *) + func_append hardcode_libdirs "$hardcode_libdir_separator$libdir" + ;; + esac + fi + else + eval flag=\"$hardcode_libdir_flag_spec\" + func_append dep_rpath " $flag" + fi + elif test -n "$runpath_var"; then + case "$perm_rpath " in + *" $libdir "*) ;; + *) func_append perm_rpath " $libdir" ;; + esac + fi + done + # Substitute the hardcoded libdirs into the rpath. + if test -n "$hardcode_libdir_separator" && + test -n "$hardcode_libdirs"; then + libdir="$hardcode_libdirs" + eval "dep_rpath=\"$hardcode_libdir_flag_spec\"" + fi + if test -n "$runpath_var" && test -n "$perm_rpath"; then + # We should set the runpath_var. + rpath= + for dir in $perm_rpath; do + func_append rpath "$dir:" + done + eval "$runpath_var='$rpath\$$runpath_var'; export $runpath_var" + fi + test -n "$dep_rpath" && deplibs="$dep_rpath $deplibs" + fi + + shlibpath="$finalize_shlibpath" + test "$opt_mode" != relink && shlibpath="$compile_shlibpath$shlibpath" + if test -n "$shlibpath"; then + eval "$shlibpath_var='$shlibpath\$$shlibpath_var'; export $shlibpath_var" + fi + + # Get the real and link names of the library. + eval shared_ext=\"$shrext_cmds\" + eval library_names=\"$library_names_spec\" + set dummy $library_names + shift + realname="$1" + shift + + if test -n "$soname_spec"; then + eval soname=\"$soname_spec\" + else + soname="$realname" + fi + if test -z "$dlname"; then + dlname=$soname + fi + + lib="$output_objdir/$realname" + linknames= + for link + do + func_append linknames " $link" + done + + # Use standard objects if they are pic + test -z "$pic_flag" && libobjs=`$ECHO "$libobjs" | $SP2NL | $SED "$lo2o" | $NL2SP` + test "X$libobjs" = "X " && libobjs= + + delfiles= + if test -n "$export_symbols" && test -n "$include_expsyms"; then + $opt_dry_run || cp "$export_symbols" "$output_objdir/$libname.uexp" + export_symbols="$output_objdir/$libname.uexp" + func_append delfiles " $export_symbols" + fi + + orig_export_symbols= + case $host_os in + cygwin* | mingw* | cegcc*) + if test -n "$export_symbols" && test -z "$export_symbols_regex"; then + # exporting using user supplied symfile + if test "x`$SED 1q $export_symbols`" != xEXPORTS; then + # and it's NOT already a .def file. Must figure out + # which of the given symbols are data symbols and tag + # them as such. So, trigger use of export_symbols_cmds. + # export_symbols gets reassigned inside the "prepare + # the list of exported symbols" if statement, so the + # include_expsyms logic still works. + orig_export_symbols="$export_symbols" + export_symbols= + always_export_symbols=yes + fi + fi + ;; + esac + + # Prepare the list of exported symbols + if test -z "$export_symbols"; then + if test "$always_export_symbols" = yes || test -n "$export_symbols_regex"; then + func_verbose "generating symbol list for \`$libname.la'" + export_symbols="$output_objdir/$libname.exp" + $opt_dry_run || $RM $export_symbols + cmds=$export_symbols_cmds + save_ifs="$IFS"; IFS='~' + for cmd1 in $cmds; do + IFS="$save_ifs" + # Take the normal branch if the nm_file_list_spec branch + # doesn't work or if tool conversion is not needed. + case $nm_file_list_spec~$to_tool_file_cmd in + *~func_convert_file_noop | *~func_convert_file_msys_to_w32 | ~*) + try_normal_branch=yes + eval cmd=\"$cmd1\" + func_len " $cmd" + len=$func_len_result + ;; + *) + try_normal_branch=no + ;; + esac + if test "$try_normal_branch" = yes \ + && { test "$len" -lt "$max_cmd_len" \ + || test "$max_cmd_len" -le -1; } + then + func_show_eval "$cmd" 'exit $?' + skipped_export=false + elif test -n "$nm_file_list_spec"; then + func_basename "$output" + output_la=$func_basename_result + save_libobjs=$libobjs + save_output=$output + output=${output_objdir}/${output_la}.nm + func_to_tool_file "$output" + libobjs=$nm_file_list_spec$func_to_tool_file_result + func_append delfiles " $output" + func_verbose "creating $NM input file list: $output" + for obj in $save_libobjs; do + func_to_tool_file "$obj" + $ECHO "$func_to_tool_file_result" + done > "$output" + eval cmd=\"$cmd1\" + func_show_eval "$cmd" 'exit $?' + output=$save_output + libobjs=$save_libobjs + skipped_export=false + else + # The command line is too long to execute in one step. + func_verbose "using reloadable object file for export list..." + skipped_export=: + # Break out early, otherwise skipped_export may be + # set to false by a later but shorter cmd. + break + fi + done + IFS="$save_ifs" + if test -n "$export_symbols_regex" && test "X$skipped_export" != "X:"; then + func_show_eval '$EGREP -e "$export_symbols_regex" "$export_symbols" > "${export_symbols}T"' + func_show_eval '$MV "${export_symbols}T" "$export_symbols"' + fi + fi + fi + + if test -n "$export_symbols" && test -n "$include_expsyms"; then + tmp_export_symbols="$export_symbols" + test -n "$orig_export_symbols" && tmp_export_symbols="$orig_export_symbols" + $opt_dry_run || eval '$ECHO "$include_expsyms" | $SP2NL >> "$tmp_export_symbols"' + fi + + if test "X$skipped_export" != "X:" && test -n "$orig_export_symbols"; then + # The given exports_symbols file has to be filtered, so filter it. + func_verbose "filter symbol list for \`$libname.la' to tag DATA exports" + # FIXME: $output_objdir/$libname.filter potentially contains lots of + # 's' commands which not all seds can handle. GNU sed should be fine + # though. Also, the filter scales superlinearly with the number of + # global variables. join(1) would be nice here, but unfortunately + # isn't a blessed tool. + $opt_dry_run || $SED -e '/[ ,]DATA/!d;s,\(.*\)\([ \,].*\),s|^\1$|\1\2|,' < $export_symbols > $output_objdir/$libname.filter + func_append delfiles " $export_symbols $output_objdir/$libname.filter" + export_symbols=$output_objdir/$libname.def + $opt_dry_run || $SED -f $output_objdir/$libname.filter < $orig_export_symbols > $export_symbols + fi + + tmp_deplibs= + for test_deplib in $deplibs; do + case " $convenience " in + *" $test_deplib "*) ;; + *) + func_append tmp_deplibs " $test_deplib" + ;; + esac + done + deplibs="$tmp_deplibs" + + if test -n "$convenience"; then + if test -n "$whole_archive_flag_spec" && + test "$compiler_needs_object" = yes && + test -z "$libobjs"; then + # extract the archives, so we have objects to list. + # TODO: could optimize this to just extract one archive. + whole_archive_flag_spec= + fi + if test -n "$whole_archive_flag_spec"; then + save_libobjs=$libobjs + eval libobjs=\"\$libobjs $whole_archive_flag_spec\" + test "X$libobjs" = "X " && libobjs= + else + gentop="$output_objdir/${outputname}x" + func_append generated " $gentop" + + func_extract_archives $gentop $convenience + func_append libobjs " $func_extract_archives_result" + test "X$libobjs" = "X " && libobjs= + fi + fi + + if test "$thread_safe" = yes && test -n "$thread_safe_flag_spec"; then + eval flag=\"$thread_safe_flag_spec\" + func_append linker_flags " $flag" + fi + + # Make a backup of the uninstalled library when relinking + if test "$opt_mode" = relink; then + $opt_dry_run || eval '(cd $output_objdir && $RM ${realname}U && $MV $realname ${realname}U)' || exit $? + fi + + # Do each of the archive commands. + if test "$module" = yes && test -n "$module_cmds" ; then + if test -n "$export_symbols" && test -n "$module_expsym_cmds"; then + eval test_cmds=\"$module_expsym_cmds\" + cmds=$module_expsym_cmds + else + eval test_cmds=\"$module_cmds\" + cmds=$module_cmds + fi + else + if test -n "$export_symbols" && test -n "$archive_expsym_cmds"; then + eval test_cmds=\"$archive_expsym_cmds\" + cmds=$archive_expsym_cmds + else + eval test_cmds=\"$archive_cmds\" + cmds=$archive_cmds + fi + fi + + if test "X$skipped_export" != "X:" && + func_len " $test_cmds" && + len=$func_len_result && + test "$len" -lt "$max_cmd_len" || test "$max_cmd_len" -le -1; then + : + else + # The command line is too long to link in one step, link piecewise + # or, if using GNU ld and skipped_export is not :, use a linker + # script. + + # Save the value of $output and $libobjs because we want to + # use them later. If we have whole_archive_flag_spec, we + # want to use save_libobjs as it was before + # whole_archive_flag_spec was expanded, because we can't + # assume the linker understands whole_archive_flag_spec. + # This may have to be revisited, in case too many + # convenience libraries get linked in and end up exceeding + # the spec. + if test -z "$convenience" || test -z "$whole_archive_flag_spec"; then + save_libobjs=$libobjs + fi + save_output=$output + func_basename "$output" + output_la=$func_basename_result + + # Clear the reloadable object creation command queue and + # initialize k to one. + test_cmds= + concat_cmds= + objlist= + last_robj= + k=1 + + if test -n "$save_libobjs" && test "X$skipped_export" != "X:" && test "$with_gnu_ld" = yes; then + output=${output_objdir}/${output_la}.lnkscript + func_verbose "creating GNU ld script: $output" + echo 'INPUT (' > $output + for obj in $save_libobjs + do + func_to_tool_file "$obj" + $ECHO "$func_to_tool_file_result" >> $output + done + echo ')' >> $output + func_append delfiles " $output" + func_to_tool_file "$output" + output=$func_to_tool_file_result + elif test -n "$save_libobjs" && test "X$skipped_export" != "X:" && test "X$file_list_spec" != X; then + output=${output_objdir}/${output_la}.lnk + func_verbose "creating linker input file list: $output" + : > $output + set x $save_libobjs + shift + firstobj= + if test "$compiler_needs_object" = yes; then + firstobj="$1 " + shift + fi + for obj + do + func_to_tool_file "$obj" + $ECHO "$func_to_tool_file_result" >> $output + done + func_append delfiles " $output" + func_to_tool_file "$output" + output=$firstobj\"$file_list_spec$func_to_tool_file_result\" + else + if test -n "$save_libobjs"; then + func_verbose "creating reloadable object files..." + output=$output_objdir/$output_la-${k}.$objext + eval test_cmds=\"$reload_cmds\" + func_len " $test_cmds" + len0=$func_len_result + len=$len0 + + # Loop over the list of objects to be linked. + for obj in $save_libobjs + do + func_len " $obj" + func_arith $len + $func_len_result + len=$func_arith_result + if test "X$objlist" = X || + test "$len" -lt "$max_cmd_len"; then + func_append objlist " $obj" + else + # The command $test_cmds is almost too long, add a + # command to the queue. + if test "$k" -eq 1 ; then + # The first file doesn't have a previous command to add. + reload_objs=$objlist + eval concat_cmds=\"$reload_cmds\" + else + # All subsequent reloadable object files will link in + # the last one created. + reload_objs="$objlist $last_robj" + eval concat_cmds=\"\$concat_cmds~$reload_cmds~\$RM $last_robj\" + fi + last_robj=$output_objdir/$output_la-${k}.$objext + func_arith $k + 1 + k=$func_arith_result + output=$output_objdir/$output_la-${k}.$objext + objlist=" $obj" + func_len " $last_robj" + func_arith $len0 + $func_len_result + len=$func_arith_result + fi + done + # Handle the remaining objects by creating one last + # reloadable object file. All subsequent reloadable object + # files will link in the last one created. + test -z "$concat_cmds" || concat_cmds=$concat_cmds~ + reload_objs="$objlist $last_robj" + eval concat_cmds=\"\${concat_cmds}$reload_cmds\" + if test -n "$last_robj"; then + eval concat_cmds=\"\${concat_cmds}~\$RM $last_robj\" + fi + func_append delfiles " $output" + + else + output= + fi + + if ${skipped_export-false}; then + func_verbose "generating symbol list for \`$libname.la'" + export_symbols="$output_objdir/$libname.exp" + $opt_dry_run || $RM $export_symbols + libobjs=$output + # Append the command to create the export file. + test -z "$concat_cmds" || concat_cmds=$concat_cmds~ + eval concat_cmds=\"\$concat_cmds$export_symbols_cmds\" + if test -n "$last_robj"; then + eval concat_cmds=\"\$concat_cmds~\$RM $last_robj\" + fi + fi + + test -n "$save_libobjs" && + func_verbose "creating a temporary reloadable object file: $output" + + # Loop through the commands generated above and execute them. + save_ifs="$IFS"; IFS='~' + for cmd in $concat_cmds; do + IFS="$save_ifs" + $opt_silent || { + func_quote_for_expand "$cmd" + eval "func_echo $func_quote_for_expand_result" + } + $opt_dry_run || eval "$cmd" || { + lt_exit=$? + + # Restore the uninstalled library and exit + if test "$opt_mode" = relink; then + ( cd "$output_objdir" && \ + $RM "${realname}T" && \ + $MV "${realname}U" "$realname" ) + fi + + exit $lt_exit + } + done + IFS="$save_ifs" + + if test -n "$export_symbols_regex" && ${skipped_export-false}; then + func_show_eval '$EGREP -e "$export_symbols_regex" "$export_symbols" > "${export_symbols}T"' + func_show_eval '$MV "${export_symbols}T" "$export_symbols"' + fi + fi + + if ${skipped_export-false}; then + if test -n "$export_symbols" && test -n "$include_expsyms"; then + tmp_export_symbols="$export_symbols" + test -n "$orig_export_symbols" && tmp_export_symbols="$orig_export_symbols" + $opt_dry_run || eval '$ECHO "$include_expsyms" | $SP2NL >> "$tmp_export_symbols"' + fi + + if test -n "$orig_export_symbols"; then + # The given exports_symbols file has to be filtered, so filter it. + func_verbose "filter symbol list for \`$libname.la' to tag DATA exports" + # FIXME: $output_objdir/$libname.filter potentially contains lots of + # 's' commands which not all seds can handle. GNU sed should be fine + # though. Also, the filter scales superlinearly with the number of + # global variables. join(1) would be nice here, but unfortunately + # isn't a blessed tool. + $opt_dry_run || $SED -e '/[ ,]DATA/!d;s,\(.*\)\([ \,].*\),s|^\1$|\1\2|,' < $export_symbols > $output_objdir/$libname.filter + func_append delfiles " $export_symbols $output_objdir/$libname.filter" + export_symbols=$output_objdir/$libname.def + $opt_dry_run || $SED -f $output_objdir/$libname.filter < $orig_export_symbols > $export_symbols + fi + fi + + libobjs=$output + # Restore the value of output. + output=$save_output + + if test -n "$convenience" && test -n "$whole_archive_flag_spec"; then + eval libobjs=\"\$libobjs $whole_archive_flag_spec\" + test "X$libobjs" = "X " && libobjs= + fi + # Expand the library linking commands again to reset the + # value of $libobjs for piecewise linking. + + # Do each of the archive commands. + if test "$module" = yes && test -n "$module_cmds" ; then + if test -n "$export_symbols" && test -n "$module_expsym_cmds"; then + cmds=$module_expsym_cmds + else + cmds=$module_cmds + fi + else + if test -n "$export_symbols" && test -n "$archive_expsym_cmds"; then + cmds=$archive_expsym_cmds + else + cmds=$archive_cmds + fi + fi + fi + + if test -n "$delfiles"; then + # Append the command to remove temporary files to $cmds. + eval cmds=\"\$cmds~\$RM $delfiles\" + fi + + # Add any objects from preloaded convenience libraries + if test -n "$dlprefiles"; then + gentop="$output_objdir/${outputname}x" + func_append generated " $gentop" + + func_extract_archives $gentop $dlprefiles + func_append libobjs " $func_extract_archives_result" + test "X$libobjs" = "X " && libobjs= + fi + + save_ifs="$IFS"; IFS='~' + for cmd in $cmds; do + IFS="$save_ifs" + eval cmd=\"$cmd\" + $opt_silent || { + func_quote_for_expand "$cmd" + eval "func_echo $func_quote_for_expand_result" + } + $opt_dry_run || eval "$cmd" || { + lt_exit=$? + + # Restore the uninstalled library and exit + if test "$opt_mode" = relink; then + ( cd "$output_objdir" && \ + $RM "${realname}T" && \ + $MV "${realname}U" "$realname" ) + fi + + exit $lt_exit + } + done + IFS="$save_ifs" + + # Restore the uninstalled library and exit + if test "$opt_mode" = relink; then + $opt_dry_run || eval '(cd $output_objdir && $RM ${realname}T && $MV $realname ${realname}T && $MV ${realname}U $realname)' || exit $? + + if test -n "$convenience"; then + if test -z "$whole_archive_flag_spec"; then + func_show_eval '${RM}r "$gentop"' + fi + fi + + exit $EXIT_SUCCESS + fi + + # Create links to the real library. + for linkname in $linknames; do + if test "$realname" != "$linkname"; then + func_show_eval '(cd "$output_objdir" && $RM "$linkname" && $LN_S "$realname" "$linkname")' 'exit $?' + fi + done + + # If -module or -export-dynamic was specified, set the dlname. + if test "$module" = yes || test "$export_dynamic" = yes; then + # On all known operating systems, these are identical. + dlname="$soname" + fi + fi + ;; + + obj) + if test -n "$dlfiles$dlprefiles" || test "$dlself" != no; then + func_warning "\`-dlopen' is ignored for objects" + fi + + case " $deplibs" in + *\ -l* | *\ -L*) + func_warning "\`-l' and \`-L' are ignored for objects" ;; + esac + + test -n "$rpath" && \ + func_warning "\`-rpath' is ignored for objects" + + test -n "$xrpath" && \ + func_warning "\`-R' is ignored for objects" + + test -n "$vinfo" && \ + func_warning "\`-version-info' is ignored for objects" + + test -n "$release" && \ + func_warning "\`-release' is ignored for objects" + + case $output in + *.lo) + test -n "$objs$old_deplibs" && \ + func_fatal_error "cannot build library object \`$output' from non-libtool objects" + + libobj=$output + func_lo2o "$libobj" + obj=$func_lo2o_result + ;; + *) + libobj= + obj="$output" + ;; + esac + + # Delete the old objects. + $opt_dry_run || $RM $obj $libobj + + # Objects from convenience libraries. This assumes + # single-version convenience libraries. Whenever we create + # different ones for PIC/non-PIC, this we'll have to duplicate + # the extraction. + reload_conv_objs= + gentop= + # reload_cmds runs $LD directly, so let us get rid of + # -Wl from whole_archive_flag_spec and hope we can get by with + # turning comma into space.. + wl= + + if test -n "$convenience"; then + if test -n "$whole_archive_flag_spec"; then + eval tmp_whole_archive_flags=\"$whole_archive_flag_spec\" + reload_conv_objs=$reload_objs\ `$ECHO "$tmp_whole_archive_flags" | $SED 's|,| |g'` + else + gentop="$output_objdir/${obj}x" + func_append generated " $gentop" + + func_extract_archives $gentop $convenience + reload_conv_objs="$reload_objs $func_extract_archives_result" + fi + fi + + # If we're not building shared, we need to use non_pic_objs + test "$build_libtool_libs" != yes && libobjs="$non_pic_objects" + + # Create the old-style object. + reload_objs="$objs$old_deplibs "`$ECHO "$libobjs" | $SP2NL | $SED "/\.${libext}$/d; /\.lib$/d; $lo2o" | $NL2SP`" $reload_conv_objs" ### testsuite: skip nested quoting test + + output="$obj" + func_execute_cmds "$reload_cmds" 'exit $?' + + # Exit if we aren't doing a library object file. + if test -z "$libobj"; then + if test -n "$gentop"; then + func_show_eval '${RM}r "$gentop"' + fi + + exit $EXIT_SUCCESS + fi + + if test "$build_libtool_libs" != yes; then + if test -n "$gentop"; then + func_show_eval '${RM}r "$gentop"' + fi + + # Create an invalid libtool object if no PIC, so that we don't + # accidentally link it into a program. + # $show "echo timestamp > $libobj" + # $opt_dry_run || eval "echo timestamp > $libobj" || exit $? + exit $EXIT_SUCCESS + fi + + if test -n "$pic_flag" || test "$pic_mode" != default; then + # Only do commands if we really have different PIC objects. + reload_objs="$libobjs $reload_conv_objs" + output="$libobj" + func_execute_cmds "$reload_cmds" 'exit $?' + fi + + if test -n "$gentop"; then + func_show_eval '${RM}r "$gentop"' + fi + + exit $EXIT_SUCCESS + ;; + + prog) + case $host in + *cygwin*) func_stripname '' '.exe' "$output" + output=$func_stripname_result.exe;; + esac + test -n "$vinfo" && \ + func_warning "\`-version-info' is ignored for programs" + + test -n "$release" && \ + func_warning "\`-release' is ignored for programs" + + test "$preload" = yes \ + && test "$dlopen_support" = unknown \ + && test "$dlopen_self" = unknown \ + && test "$dlopen_self_static" = unknown && \ + func_warning "\`LT_INIT([dlopen])' not used. Assuming no dlopen support." + + case $host in + *-*-rhapsody* | *-*-darwin1.[012]) + # On Rhapsody replace the C library is the System framework + compile_deplibs=`$ECHO " $compile_deplibs" | $SED 's/ -lc / System.ltframework /'` + finalize_deplibs=`$ECHO " $finalize_deplibs" | $SED 's/ -lc / System.ltframework /'` + ;; + esac + + case $host in + *-*-darwin*) + # Don't allow lazy linking, it breaks C++ global constructors + # But is supposedly fixed on 10.4 or later (yay!). + if test "$tagname" = CXX ; then + case ${MACOSX_DEPLOYMENT_TARGET-10.0} in + 10.[0123]) + func_append compile_command " ${wl}-bind_at_load" + func_append finalize_command " ${wl}-bind_at_load" + ;; + esac + fi + # Time to change all our "foo.ltframework" stuff back to "-framework foo" + compile_deplibs=`$ECHO " $compile_deplibs" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'` + finalize_deplibs=`$ECHO " $finalize_deplibs" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'` + ;; + esac + + + # move library search paths that coincide with paths to not yet + # installed libraries to the beginning of the library search list + new_libs= + for path in $notinst_path; do + case " $new_libs " in + *" -L$path/$objdir "*) ;; + *) + case " $compile_deplibs " in + *" -L$path/$objdir "*) + func_append new_libs " -L$path/$objdir" ;; + esac + ;; + esac + done + for deplib in $compile_deplibs; do + case $deplib in + -L*) + case " $new_libs " in + *" $deplib "*) ;; + *) func_append new_libs " $deplib" ;; + esac + ;; + *) func_append new_libs " $deplib" ;; + esac + done + compile_deplibs="$new_libs" + + + func_append compile_command " $compile_deplibs" + func_append finalize_command " $finalize_deplibs" + + if test -n "$rpath$xrpath"; then + # If the user specified any rpath flags, then add them. + for libdir in $rpath $xrpath; do + # This is the magic to use -rpath. + case "$finalize_rpath " in + *" $libdir "*) ;; + *) func_append finalize_rpath " $libdir" ;; + esac + done + fi + + # Now hardcode the library paths + rpath= + hardcode_libdirs= + for libdir in $compile_rpath $finalize_rpath; do + if test -n "$hardcode_libdir_flag_spec"; then + if test -n "$hardcode_libdir_separator"; then + if test -z "$hardcode_libdirs"; then + hardcode_libdirs="$libdir" + else + # Just accumulate the unique libdirs. + case $hardcode_libdir_separator$hardcode_libdirs$hardcode_libdir_separator in + *"$hardcode_libdir_separator$libdir$hardcode_libdir_separator"*) + ;; + *) + func_append hardcode_libdirs "$hardcode_libdir_separator$libdir" + ;; + esac + fi + else + eval flag=\"$hardcode_libdir_flag_spec\" + func_append rpath " $flag" + fi + elif test -n "$runpath_var"; then + case "$perm_rpath " in + *" $libdir "*) ;; + *) func_append perm_rpath " $libdir" ;; + esac + fi + case $host in + *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-cegcc*) + testbindir=`${ECHO} "$libdir" | ${SED} -e 's*/lib$*/bin*'` + case :$dllsearchpath: in + *":$libdir:"*) ;; + ::) dllsearchpath=$libdir;; + *) func_append dllsearchpath ":$libdir";; + esac + case :$dllsearchpath: in + *":$testbindir:"*) ;; + ::) dllsearchpath=$testbindir;; + *) func_append dllsearchpath ":$testbindir";; + esac + ;; + esac + done + # Substitute the hardcoded libdirs into the rpath. + if test -n "$hardcode_libdir_separator" && + test -n "$hardcode_libdirs"; then + libdir="$hardcode_libdirs" + eval rpath=\" $hardcode_libdir_flag_spec\" + fi + compile_rpath="$rpath" + + rpath= + hardcode_libdirs= + for libdir in $finalize_rpath; do + if test -n "$hardcode_libdir_flag_spec"; then + if test -n "$hardcode_libdir_separator"; then + if test -z "$hardcode_libdirs"; then + hardcode_libdirs="$libdir" + else + # Just accumulate the unique libdirs. + case $hardcode_libdir_separator$hardcode_libdirs$hardcode_libdir_separator in + *"$hardcode_libdir_separator$libdir$hardcode_libdir_separator"*) + ;; + *) + func_append hardcode_libdirs "$hardcode_libdir_separator$libdir" + ;; + esac + fi + else + eval flag=\"$hardcode_libdir_flag_spec\" + func_append rpath " $flag" + fi + elif test -n "$runpath_var"; then + case "$finalize_perm_rpath " in + *" $libdir "*) ;; + *) func_append finalize_perm_rpath " $libdir" ;; + esac + fi + done + # Substitute the hardcoded libdirs into the rpath. + if test -n "$hardcode_libdir_separator" && + test -n "$hardcode_libdirs"; then + libdir="$hardcode_libdirs" + eval rpath=\" $hardcode_libdir_flag_spec\" + fi + finalize_rpath="$rpath" + + if test -n "$libobjs" && test "$build_old_libs" = yes; then + # Transform all the library objects into standard objects. + compile_command=`$ECHO "$compile_command" | $SP2NL | $SED "$lo2o" | $NL2SP` + finalize_command=`$ECHO "$finalize_command" | $SP2NL | $SED "$lo2o" | $NL2SP` + fi + + func_generate_dlsyms "$outputname" "@PROGRAM@" "no" + + # template prelinking step + if test -n "$prelink_cmds"; then + func_execute_cmds "$prelink_cmds" 'exit $?' + fi + + wrappers_required=yes + case $host in + *cegcc* | *mingw32ce*) + # Disable wrappers for cegcc and mingw32ce hosts, we are cross compiling anyway. + wrappers_required=no + ;; + *cygwin* | *mingw* ) + if test "$build_libtool_libs" != yes; then + wrappers_required=no + fi + ;; + *) + if test "$need_relink" = no || test "$build_libtool_libs" != yes; then + wrappers_required=no + fi + ;; + esac + if test "$wrappers_required" = no; then + # Replace the output file specification. + compile_command=`$ECHO "$compile_command" | $SED 's%@OUTPUT@%'"$output"'%g'` + link_command="$compile_command$compile_rpath" + + # We have no uninstalled library dependencies, so finalize right now. + exit_status=0 + func_show_eval "$link_command" 'exit_status=$?' + + if test -n "$postlink_cmds"; then + func_to_tool_file "$output" + postlink_cmds=`func_echo_all "$postlink_cmds" | $SED -e 's%@OUTPUT@%'"$output"'%g' -e 's%@TOOL_OUTPUT@%'"$func_to_tool_file_result"'%g'` + func_execute_cmds "$postlink_cmds" 'exit $?' + fi + + # Delete the generated files. + if test -f "$output_objdir/${outputname}S.${objext}"; then + func_show_eval '$RM "$output_objdir/${outputname}S.${objext}"' + fi + + exit $exit_status + fi + + if test -n "$compile_shlibpath$finalize_shlibpath"; then + compile_command="$shlibpath_var=\"$compile_shlibpath$finalize_shlibpath\$$shlibpath_var\" $compile_command" + fi + if test -n "$finalize_shlibpath"; then + finalize_command="$shlibpath_var=\"$finalize_shlibpath\$$shlibpath_var\" $finalize_command" + fi + + compile_var= + finalize_var= + if test -n "$runpath_var"; then + if test -n "$perm_rpath"; then + # We should set the runpath_var. + rpath= + for dir in $perm_rpath; do + func_append rpath "$dir:" + done + compile_var="$runpath_var=\"$rpath\$$runpath_var\" " + fi + if test -n "$finalize_perm_rpath"; then + # We should set the runpath_var. + rpath= + for dir in $finalize_perm_rpath; do + func_append rpath "$dir:" + done + finalize_var="$runpath_var=\"$rpath\$$runpath_var\" " + fi + fi + + if test "$no_install" = yes; then + # We don't need to create a wrapper script. + link_command="$compile_var$compile_command$compile_rpath" + # Replace the output file specification. + link_command=`$ECHO "$link_command" | $SED 's%@OUTPUT@%'"$output"'%g'` + # Delete the old output file. + $opt_dry_run || $RM $output + # Link the executable and exit + func_show_eval "$link_command" 'exit $?' + + if test -n "$postlink_cmds"; then + func_to_tool_file "$output" + postlink_cmds=`func_echo_all "$postlink_cmds" | $SED -e 's%@OUTPUT@%'"$output"'%g' -e 's%@TOOL_OUTPUT@%'"$func_to_tool_file_result"'%g'` + func_execute_cmds "$postlink_cmds" 'exit $?' + fi + + exit $EXIT_SUCCESS + fi + + if test "$hardcode_action" = relink; then + # Fast installation is not supported + link_command="$compile_var$compile_command$compile_rpath" + relink_command="$finalize_var$finalize_command$finalize_rpath" + + func_warning "this platform does not like uninstalled shared libraries" + func_warning "\`$output' will be relinked during installation" + else + if test "$fast_install" != no; then + link_command="$finalize_var$compile_command$finalize_rpath" + if test "$fast_install" = yes; then + relink_command=`$ECHO "$compile_var$compile_command$compile_rpath" | $SED 's%@OUTPUT@%\$progdir/\$file%g'` + else + # fast_install is set to needless + relink_command= + fi + else + link_command="$compile_var$compile_command$compile_rpath" + relink_command="$finalize_var$finalize_command$finalize_rpath" + fi + fi + + # Replace the output file specification. + link_command=`$ECHO "$link_command" | $SED 's%@OUTPUT@%'"$output_objdir/$outputname"'%g'` + + # Delete the old output files. + $opt_dry_run || $RM $output $output_objdir/$outputname $output_objdir/lt-$outputname + + func_show_eval "$link_command" 'exit $?' + + if test -n "$postlink_cmds"; then + func_to_tool_file "$output_objdir/$outputname" + postlink_cmds=`func_echo_all "$postlink_cmds" | $SED -e 's%@OUTPUT@%'"$output_objdir/$outputname"'%g' -e 's%@TOOL_OUTPUT@%'"$func_to_tool_file_result"'%g'` + func_execute_cmds "$postlink_cmds" 'exit $?' + fi + + # Now create the wrapper script. + func_verbose "creating $output" + + # Quote the relink command for shipping. + if test -n "$relink_command"; then + # Preserve any variables that may affect compiler behavior + for var in $variables_saved_for_relink; do + if eval test -z \"\${$var+set}\"; then + relink_command="{ test -z \"\${$var+set}\" || $lt_unset $var || { $var=; export $var; }; }; $relink_command" + elif eval var_value=\$$var; test -z "$var_value"; then + relink_command="$var=; export $var; $relink_command" + else + func_quote_for_eval "$var_value" + relink_command="$var=$func_quote_for_eval_result; export $var; $relink_command" + fi + done + relink_command="(cd `pwd`; $relink_command)" + relink_command=`$ECHO "$relink_command" | $SED "$sed_quote_subst"` + fi + + # Only actually do things if not in dry run mode. + $opt_dry_run || { + # win32 will think the script is a binary if it has + # a .exe suffix, so we strip it off here. + case $output in + *.exe) func_stripname '' '.exe' "$output" + output=$func_stripname_result ;; + esac + # test for cygwin because mv fails w/o .exe extensions + case $host in + *cygwin*) + exeext=.exe + func_stripname '' '.exe' "$outputname" + outputname=$func_stripname_result ;; + *) exeext= ;; + esac + case $host in + *cygwin* | *mingw* ) + func_dirname_and_basename "$output" "" "." + output_name=$func_basename_result + output_path=$func_dirname_result + cwrappersource="$output_path/$objdir/lt-$output_name.c" + cwrapper="$output_path/$output_name.exe" + $RM $cwrappersource $cwrapper + trap "$RM $cwrappersource $cwrapper; exit $EXIT_FAILURE" 1 2 15 + + func_emit_cwrapperexe_src > $cwrappersource + + # The wrapper executable is built using the $host compiler, + # because it contains $host paths and files. If cross- + # compiling, it, like the target executable, must be + # executed on the $host or under an emulation environment. + $opt_dry_run || { + $LTCC $LTCFLAGS -o $cwrapper $cwrappersource + $STRIP $cwrapper + } + + # Now, create the wrapper script for func_source use: + func_ltwrapper_scriptname $cwrapper + $RM $func_ltwrapper_scriptname_result + trap "$RM $func_ltwrapper_scriptname_result; exit $EXIT_FAILURE" 1 2 15 + $opt_dry_run || { + # note: this script will not be executed, so do not chmod. + if test "x$build" = "x$host" ; then + $cwrapper --lt-dump-script > $func_ltwrapper_scriptname_result + else + func_emit_wrapper no > $func_ltwrapper_scriptname_result + fi + } + ;; + * ) + $RM $output + trap "$RM $output; exit $EXIT_FAILURE" 1 2 15 + + func_emit_wrapper no > $output + chmod +x $output + ;; + esac + } + exit $EXIT_SUCCESS + ;; + esac + + # See if we need to build an old-fashioned archive. + for oldlib in $oldlibs; do + + if test "$build_libtool_libs" = convenience; then + oldobjs="$libobjs_save $symfileobj" + addlibs="$convenience" + build_libtool_libs=no + else + if test "$build_libtool_libs" = module; then + oldobjs="$libobjs_save" + build_libtool_libs=no + else + oldobjs="$old_deplibs $non_pic_objects" + if test "$preload" = yes && test -f "$symfileobj"; then + func_append oldobjs " $symfileobj" + fi + fi + addlibs="$old_convenience" + fi + + if test -n "$addlibs"; then + gentop="$output_objdir/${outputname}x" + func_append generated " $gentop" + + func_extract_archives $gentop $addlibs + func_append oldobjs " $func_extract_archives_result" + fi + + # Do each command in the archive commands. + if test -n "$old_archive_from_new_cmds" && test "$build_libtool_libs" = yes; then + cmds=$old_archive_from_new_cmds + else + + # Add any objects from preloaded convenience libraries + if test -n "$dlprefiles"; then + gentop="$output_objdir/${outputname}x" + func_append generated " $gentop" + + func_extract_archives $gentop $dlprefiles + func_append oldobjs " $func_extract_archives_result" + fi + + # POSIX demands no paths to be encoded in archives. We have + # to avoid creating archives with duplicate basenames if we + # might have to extract them afterwards, e.g., when creating a + # static archive out of a convenience library, or when linking + # the entirety of a libtool archive into another (currently + # not supported by libtool). + if (for obj in $oldobjs + do + func_basename "$obj" + $ECHO "$func_basename_result" + done | sort | sort -uc >/dev/null 2>&1); then + : + else + echo "copying selected object files to avoid basename conflicts..." + gentop="$output_objdir/${outputname}x" + func_append generated " $gentop" + func_mkdir_p "$gentop" + save_oldobjs=$oldobjs + oldobjs= + counter=1 + for obj in $save_oldobjs + do + func_basename "$obj" + objbase="$func_basename_result" + case " $oldobjs " in + " ") oldobjs=$obj ;; + *[\ /]"$objbase "*) + while :; do + # Make sure we don't pick an alternate name that also + # overlaps. + newobj=lt$counter-$objbase + func_arith $counter + 1 + counter=$func_arith_result + case " $oldobjs " in + *[\ /]"$newobj "*) ;; + *) if test ! -f "$gentop/$newobj"; then break; fi ;; + esac + done + func_show_eval "ln $obj $gentop/$newobj || cp $obj $gentop/$newobj" + func_append oldobjs " $gentop/$newobj" + ;; + *) func_append oldobjs " $obj" ;; + esac + done + fi + func_to_tool_file "$oldlib" func_convert_file_msys_to_w32 + tool_oldlib=$func_to_tool_file_result + eval cmds=\"$old_archive_cmds\" + + func_len " $cmds" + len=$func_len_result + if test "$len" -lt "$max_cmd_len" || test "$max_cmd_len" -le -1; then + cmds=$old_archive_cmds + elif test -n "$archiver_list_spec"; then + func_verbose "using command file archive linking..." + for obj in $oldobjs + do + func_to_tool_file "$obj" + $ECHO "$func_to_tool_file_result" + done > $output_objdir/$libname.libcmd + func_to_tool_file "$output_objdir/$libname.libcmd" + oldobjs=" $archiver_list_spec$func_to_tool_file_result" + cmds=$old_archive_cmds + else + # the command line is too long to link in one step, link in parts + func_verbose "using piecewise archive linking..." + save_RANLIB=$RANLIB + RANLIB=: + objlist= + concat_cmds= + save_oldobjs=$oldobjs + oldobjs= + # Is there a better way of finding the last object in the list? + for obj in $save_oldobjs + do + last_oldobj=$obj + done + eval test_cmds=\"$old_archive_cmds\" + func_len " $test_cmds" + len0=$func_len_result + len=$len0 + for obj in $save_oldobjs + do + func_len " $obj" + func_arith $len + $func_len_result + len=$func_arith_result + func_append objlist " $obj" + if test "$len" -lt "$max_cmd_len"; then + : + else + # the above command should be used before it gets too long + oldobjs=$objlist + if test "$obj" = "$last_oldobj" ; then + RANLIB=$save_RANLIB + fi + test -z "$concat_cmds" || concat_cmds=$concat_cmds~ + eval concat_cmds=\"\${concat_cmds}$old_archive_cmds\" + objlist= + len=$len0 + fi + done + RANLIB=$save_RANLIB + oldobjs=$objlist + if test "X$oldobjs" = "X" ; then + eval cmds=\"\$concat_cmds\" + else + eval cmds=\"\$concat_cmds~\$old_archive_cmds\" + fi + fi + fi + func_execute_cmds "$cmds" 'exit $?' + done + + test -n "$generated" && \ + func_show_eval "${RM}r$generated" + + # Now create the libtool archive. + case $output in + *.la) + old_library= + test "$build_old_libs" = yes && old_library="$libname.$libext" + func_verbose "creating $output" + + # Preserve any variables that may affect compiler behavior + for var in $variables_saved_for_relink; do + if eval test -z \"\${$var+set}\"; then + relink_command="{ test -z \"\${$var+set}\" || $lt_unset $var || { $var=; export $var; }; }; $relink_command" + elif eval var_value=\$$var; test -z "$var_value"; then + relink_command="$var=; export $var; $relink_command" + else + func_quote_for_eval "$var_value" + relink_command="$var=$func_quote_for_eval_result; export $var; $relink_command" + fi + done + # Quote the link command for shipping. + relink_command="(cd `pwd`; $SHELL $progpath $preserve_args --mode=relink $libtool_args @inst_prefix_dir@)" + relink_command=`$ECHO "$relink_command" | $SED "$sed_quote_subst"` + if test "$hardcode_automatic" = yes ; then + relink_command= + fi + + # Only create the output if not a dry run. + $opt_dry_run || { + for installed in no yes; do + if test "$installed" = yes; then + if test -z "$install_libdir"; then + break + fi + output="$output_objdir/$outputname"i + # Replace all uninstalled libtool libraries with the installed ones + newdependency_libs= + for deplib in $dependency_libs; do + case $deplib in + *.la) + func_basename "$deplib" + name="$func_basename_result" + func_resolve_sysroot "$deplib" + eval libdir=`${SED} -n -e 's/^libdir=\(.*\)$/\1/p' $func_resolve_sysroot_result` + test -z "$libdir" && \ + func_fatal_error "\`$deplib' is not a valid libtool archive" + func_append newdependency_libs " ${lt_sysroot:+=}$libdir/$name" + ;; + -L*) + func_stripname -L '' "$deplib" + func_replace_sysroot "$func_stripname_result" + func_append newdependency_libs " -L$func_replace_sysroot_result" + ;; + -R*) + func_stripname -R '' "$deplib" + func_replace_sysroot "$func_stripname_result" + func_append newdependency_libs " -R$func_replace_sysroot_result" + ;; + *) func_append newdependency_libs " $deplib" ;; + esac + done + dependency_libs="$newdependency_libs" + newdlfiles= + + for lib in $dlfiles; do + case $lib in + *.la) + func_basename "$lib" + name="$func_basename_result" + eval libdir=`${SED} -n -e 's/^libdir=\(.*\)$/\1/p' $lib` + test -z "$libdir" && \ + func_fatal_error "\`$lib' is not a valid libtool archive" + func_append newdlfiles " ${lt_sysroot:+=}$libdir/$name" + ;; + *) func_append newdlfiles " $lib" ;; + esac + done + dlfiles="$newdlfiles" + newdlprefiles= + for lib in $dlprefiles; do + case $lib in + *.la) + # Only pass preopened files to the pseudo-archive (for + # eventual linking with the app. that links it) if we + # didn't already link the preopened objects directly into + # the library: + func_basename "$lib" + name="$func_basename_result" + eval libdir=`${SED} -n -e 's/^libdir=\(.*\)$/\1/p' $lib` + test -z "$libdir" && \ + func_fatal_error "\`$lib' is not a valid libtool archive" + func_append newdlprefiles " ${lt_sysroot:+=}$libdir/$name" + ;; + esac + done + dlprefiles="$newdlprefiles" + else + newdlfiles= + for lib in $dlfiles; do + case $lib in + [\\/]* | [A-Za-z]:[\\/]*) abs="$lib" ;; + *) abs=`pwd`"/$lib" ;; + esac + func_append newdlfiles " $abs" + done + dlfiles="$newdlfiles" + newdlprefiles= + for lib in $dlprefiles; do + case $lib in + [\\/]* | [A-Za-z]:[\\/]*) abs="$lib" ;; + *) abs=`pwd`"/$lib" ;; + esac + func_append newdlprefiles " $abs" + done + dlprefiles="$newdlprefiles" + fi + $RM $output + # place dlname in correct position for cygwin + # In fact, it would be nice if we could use this code for all target + # systems that can't hard-code library paths into their executables + # and that have no shared library path variable independent of PATH, + # but it turns out we can't easily determine that from inspecting + # libtool variables, so we have to hard-code the OSs to which it + # applies here; at the moment, that means platforms that use the PE + # object format with DLL files. See the long comment at the top of + # tests/bindir.at for full details. + tdlname=$dlname + case $host,$output,$installed,$module,$dlname in + *cygwin*,*lai,yes,no,*.dll | *mingw*,*lai,yes,no,*.dll | *cegcc*,*lai,yes,no,*.dll) + # If a -bindir argument was supplied, place the dll there. + if test "x$bindir" != x ; + then + func_relative_path "$install_libdir" "$bindir" + tdlname=$func_relative_path_result$dlname + else + # Otherwise fall back on heuristic. + tdlname=../bin/$dlname + fi + ;; + esac + $ECHO > $output "\ +# $outputname - a libtool library file +# Generated by $PROGRAM (GNU $PACKAGE$TIMESTAMP) $VERSION +# +# Please DO NOT delete this file! +# It is necessary for linking the library. + +# The name that we can dlopen(3). +dlname='$tdlname' + +# Names of this library. +library_names='$library_names' + +# The name of the static archive. +old_library='$old_library' + +# Linker flags that can not go in dependency_libs. +inherited_linker_flags='$new_inherited_linker_flags' + +# Libraries that this one depends upon. +dependency_libs='$dependency_libs' + +# Names of additional weak libraries provided by this library +weak_library_names='$weak_libs' + +# Version information for $libname. +current=$current +age=$age +revision=$revision + +# Is this an already installed library? +installed=$installed + +# Should we warn about portability when linking against -modules? +shouldnotlink=$module + +# Files to dlopen/dlpreopen +dlopen='$dlfiles' +dlpreopen='$dlprefiles' + +# Directory that this library needs to be installed in: +libdir='$install_libdir'" + if test "$installed" = no && test "$need_relink" = yes; then + $ECHO >> $output "\ +relink_command=\"$relink_command\"" + fi + done + } + + # Do a symbolic link so that the libtool archive can be found in + # LD_LIBRARY_PATH before the program is installed. + func_show_eval '( cd "$output_objdir" && $RM "$outputname" && $LN_S "../$outputname" "$outputname" )' 'exit $?' + ;; + esac + exit $EXIT_SUCCESS +} + +{ test "$opt_mode" = link || test "$opt_mode" = relink; } && + func_mode_link ${1+"$@"} + + +# func_mode_uninstall arg... +func_mode_uninstall () +{ + $opt_debug + RM="$nonopt" + files= + rmforce= + exit_status=0 + + # This variable tells wrapper scripts just to set variables rather + # than running their programs. + libtool_install_magic="$magic" + + for arg + do + case $arg in + -f) func_append RM " $arg"; rmforce=yes ;; + -*) func_append RM " $arg" ;; + *) func_append files " $arg" ;; + esac + done + + test -z "$RM" && \ + func_fatal_help "you must specify an RM program" + + rmdirs= + + for file in $files; do + func_dirname "$file" "" "." + dir="$func_dirname_result" + if test "X$dir" = X.; then + odir="$objdir" + else + odir="$dir/$objdir" + fi + func_basename "$file" + name="$func_basename_result" + test "$opt_mode" = uninstall && odir="$dir" + + # Remember odir for removal later, being careful to avoid duplicates + if test "$opt_mode" = clean; then + case " $rmdirs " in + *" $odir "*) ;; + *) func_append rmdirs " $odir" ;; + esac + fi + + # Don't error if the file doesn't exist and rm -f was used. + if { test -L "$file"; } >/dev/null 2>&1 || + { test -h "$file"; } >/dev/null 2>&1 || + test -f "$file"; then + : + elif test -d "$file"; then + exit_status=1 + continue + elif test "$rmforce" = yes; then + continue + fi + + rmfiles="$file" + + case $name in + *.la) + # Possibly a libtool archive, so verify it. + if func_lalib_p "$file"; then + func_source $dir/$name + + # Delete the libtool libraries and symlinks. + for n in $library_names; do + func_append rmfiles " $odir/$n" + done + test -n "$old_library" && func_append rmfiles " $odir/$old_library" + + case "$opt_mode" in + clean) + case " $library_names " in + *" $dlname "*) ;; + *) test -n "$dlname" && func_append rmfiles " $odir/$dlname" ;; + esac + test -n "$libdir" && func_append rmfiles " $odir/$name $odir/${name}i" + ;; + uninstall) + if test -n "$library_names"; then + # Do each command in the postuninstall commands. + func_execute_cmds "$postuninstall_cmds" 'test "$rmforce" = yes || exit_status=1' + fi + + if test -n "$old_library"; then + # Do each command in the old_postuninstall commands. + func_execute_cmds "$old_postuninstall_cmds" 'test "$rmforce" = yes || exit_status=1' + fi + # FIXME: should reinstall the best remaining shared library. + ;; + esac + fi + ;; + + *.lo) + # Possibly a libtool object, so verify it. + if func_lalib_p "$file"; then + + # Read the .lo file + func_source $dir/$name + + # Add PIC object to the list of files to remove. + if test -n "$pic_object" && + test "$pic_object" != none; then + func_append rmfiles " $dir/$pic_object" + fi + + # Add non-PIC object to the list of files to remove. + if test -n "$non_pic_object" && + test "$non_pic_object" != none; then + func_append rmfiles " $dir/$non_pic_object" + fi + fi + ;; + + *) + if test "$opt_mode" = clean ; then + noexename=$name + case $file in + *.exe) + func_stripname '' '.exe' "$file" + file=$func_stripname_result + func_stripname '' '.exe' "$name" + noexename=$func_stripname_result + # $file with .exe has already been added to rmfiles, + # add $file without .exe + func_append rmfiles " $file" + ;; + esac + # Do a test to see if this is a libtool program. + if func_ltwrapper_p "$file"; then + if func_ltwrapper_executable_p "$file"; then + func_ltwrapper_scriptname "$file" + relink_command= + func_source $func_ltwrapper_scriptname_result + func_append rmfiles " $func_ltwrapper_scriptname_result" + else + relink_command= + func_source $dir/$noexename + fi + + # note $name still contains .exe if it was in $file originally + # as does the version of $file that was added into $rmfiles + func_append rmfiles " $odir/$name $odir/${name}S.${objext}" + if test "$fast_install" = yes && test -n "$relink_command"; then + func_append rmfiles " $odir/lt-$name" + fi + if test "X$noexename" != "X$name" ; then + func_append rmfiles " $odir/lt-${noexename}.c" + fi + fi + fi + ;; + esac + func_show_eval "$RM $rmfiles" 'exit_status=1' + done + + # Try to remove the ${objdir}s in the directories where we deleted files + for dir in $rmdirs; do + if test -d "$dir"; then + func_show_eval "rmdir $dir >/dev/null 2>&1" + fi + done + + exit $exit_status +} + +{ test "$opt_mode" = uninstall || test "$opt_mode" = clean; } && + func_mode_uninstall ${1+"$@"} + +test -z "$opt_mode" && { + help="$generic_help" + func_fatal_help "you must specify a MODE" +} + +test -z "$exec_cmd" && \ + func_fatal_help "invalid operation mode \`$opt_mode'" + +if test -n "$exec_cmd"; then + eval exec "$exec_cmd" + exit $EXIT_FAILURE +fi + +exit $exit_status + + +# The TAGs below are defined such that we never get into a situation +# in which we disable both kinds of libraries. Given conflicting +# choices, we go for a static library, that is the most portable, +# since we can't tell whether shared libraries were disabled because +# the user asked for that or because the platform doesn't support +# them. This is particularly important on AIX, because we don't +# support having both static and shared libraries enabled at the same +# time on that platform, so we default to a shared-only configuration. +# If a disable-shared tag is given, we'll fallback to a static-only +# configuration. But we'll never go from static-only to shared-only. + +# ### BEGIN LIBTOOL TAG CONFIG: disable-shared +build_libtool_libs=no +build_old_libs=yes +# ### END LIBTOOL TAG CONFIG: disable-shared + +# ### BEGIN LIBTOOL TAG CONFIG: disable-static +build_old_libs=`case $build_libtool_libs in yes) echo no;; *) echo yes;; esac` +# ### END LIBTOOL TAG CONFIG: disable-static + +# Local Variables: +# mode:shell-script +# sh-indentation:2 +# End: +# vi:sw=2 + diff --git a/m4/ax_boost_asio.m4 b/m4/ax_boost_asio.m4 new file mode 100644 index 0000000..b57d487 --- /dev/null +++ b/m4/ax_boost_asio.m4 @@ -0,0 +1,110 @@ +# =========================================================================== +# http://www.gnu.org/software/autoconf-archive/ax_boost_asio.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_BOOST_ASIO +# +# DESCRIPTION +# +# Test for Asio library from the Boost C++ libraries. The macro requires a +# preceding call to AX_BOOST_BASE. Further documentation is available at +# . +# +# This macro calls: +# +# AC_SUBST(BOOST_ASIO_LIB) +# +# And sets: +# +# HAVE_BOOST_ASIO +# +# LICENSE +# +# Copyright (c) 2008 Thomas Porschberg +# Copyright (c) 2008 Pete Greenwell +# +# 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 any +# warranty. + +#serial 16 + +AC_DEFUN([AX_BOOST_ASIO], +[ + AC_ARG_WITH([boost-asio], + AS_HELP_STRING([--with-boost-asio@<:@=special-lib@:>@], + [use the ASIO library from boost - it is possible to specify a certain library for the linker + e.g. --with-boost-asio=boost_system-gcc41-mt-1_34 ]), + [ + if test "$withval" = "no"; then + want_boost="no" + elif test "$withval" = "yes"; then + want_boost="yes" + ax_boost_user_asio_lib="" + else + want_boost="yes" + ax_boost_user_asio_lib="$withval" + fi + ], + [want_boost="yes"] + ) + + if test "x$want_boost" = "xyes"; then + AC_REQUIRE([AC_PROG_CC]) + CPPFLAGS_SAVED="$CPPFLAGS" + CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS" + export CPPFLAGS + + LDFLAGS_SAVED="$LDFLAGS" + LDFLAGS="$LDFLAGS $BOOST_LDFLAGS" + export LDFLAGS + + AC_CACHE_CHECK(whether the Boost::ASIO library is available, + ax_cv_boost_asio, + [AC_LANG_PUSH([C++]) + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ @%:@include + ]], + [[ + + boost::asio::io_service io; + boost::system::error_code timer_result; + boost::asio::deadline_timer t(io); + t.cancel(); + io.run_one(); + return 0; + ]])], + ax_cv_boost_asio=yes, ax_cv_boost_asio=no) + AC_LANG_POP([C++]) + ]) + if test "x$ax_cv_boost_asio" = "xyes"; then + AC_DEFINE(HAVE_BOOST_ASIO,,[define if the Boost::ASIO library is available]) + BN=boost_system + BOOSTLIBDIR=`echo $BOOST_LDFLAGS | sed -e 's/@<:@^\/@:>@*//'` + if test "x$ax_boost_user_asio_lib" = "x"; then + for ax_lib in `ls $BOOSTLIBDIR/libboost_system*.so* $BOOSTLIBDIR/libboost_system*.dylib* $BOOSTLIBDIR/libboost_system*.a* 2>/dev/null | sed 's,.*/,,' | sed -e 's;^lib\(boost_system.*\)\.so.*$;\1;' -e 's;^lib\(boost_system.*\)\.dylib.*$;\1;' -e 's;^lib\(boost_system.*\)\.a.*$;\1;' ` ; do + AC_CHECK_LIB($ax_lib, main, [BOOST_ASIO_LIB="-l$ax_lib" AC_SUBST(BOOST_ASIO_LIB) link_thread="yes" break], + [link_thread="no"]) + done + else + for ax_lib in $ax_boost_user_asio_lib $BN-$ax_boost_user_asio_lib; do + AC_CHECK_LIB($ax_lib, main, + [BOOST_ASIO_LIB="-l$ax_lib" AC_SUBST(BOOST_ASIO_LIB) link_asio="yes" break], + [link_asio="no"]) + done + + fi + if test "x$ax_lib" = "x"; then + AC_MSG_ERROR(Could not find a version of the library!) + fi + if test "x$link_asio" = "xno"; then + AC_MSG_ERROR(Could not link against $ax_lib !) + fi + fi + + CPPFLAGS="$CPPFLAGS_SAVED" + LDFLAGS="$LDFLAGS_SAVED" + fi +]) diff --git a/m4/ax_boost_base.m4 b/m4/ax_boost_base.m4 new file mode 100644 index 0000000..d7c9d0d --- /dev/null +++ b/m4/ax_boost_base.m4 @@ -0,0 +1,275 @@ +# =========================================================================== +# http://www.gnu.org/software/autoconf-archive/ax_boost_base.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_BOOST_BASE([MINIMUM-VERSION], [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) +# +# DESCRIPTION +# +# Test for the Boost C++ libraries of a particular version (or newer) +# +# If no path to the installed boost library is given the macro searchs +# under /usr, /usr/local, /opt and /opt/local and evaluates the +# $BOOST_ROOT environment variable. Further documentation is available at +# . +# +# This macro calls: +# +# AC_SUBST(BOOST_CPPFLAGS) / AC_SUBST(BOOST_LDFLAGS) +# +# And sets: +# +# HAVE_BOOST +# +# LICENSE +# +# Copyright (c) 2008 Thomas Porschberg +# Copyright (c) 2009 Peter Adolphs +# +# 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 any +# warranty. + +#serial 25 + +AC_DEFUN([AX_BOOST_BASE], +[ +AC_ARG_WITH([boost], + [AS_HELP_STRING([--with-boost@<:@=ARG@:>@], + [use Boost library from a standard location (ARG=yes), + from the specified location (ARG=), + or disable it (ARG=no) + @<:@ARG=yes@:>@ ])], + [ + if test "$withval" = "no"; then + want_boost="no" + elif test "$withval" = "yes"; then + want_boost="yes" + ac_boost_path="" + else + want_boost="yes" + ac_boost_path="$withval" + fi + ], + [want_boost="yes"]) + + +AC_ARG_WITH([boost-libdir], + AS_HELP_STRING([--with-boost-libdir=LIB_DIR], + [Force given directory for boost libraries. Note that this will override library path detection, so use this parameter only if default library detection fails and you know exactly where your boost libraries are located.]), + [ + if test -d "$withval" + then + ac_boost_lib_path="$withval" + else + AC_MSG_ERROR(--with-boost-libdir expected directory name) + fi + ], + [ac_boost_lib_path=""] +) + +if test "x$want_boost" = "xyes"; then + boost_lib_version_req=ifelse([$1], ,1.20.0,$1) + boost_lib_version_req_shorten=`expr $boost_lib_version_req : '\([[0-9]]*\.[[0-9]]*\)'` + boost_lib_version_req_major=`expr $boost_lib_version_req : '\([[0-9]]*\)'` + boost_lib_version_req_minor=`expr $boost_lib_version_req : '[[0-9]]*\.\([[0-9]]*\)'` + boost_lib_version_req_sub_minor=`expr $boost_lib_version_req : '[[0-9]]*\.[[0-9]]*\.\([[0-9]]*\)'` + if test "x$boost_lib_version_req_sub_minor" = "x" ; then + boost_lib_version_req_sub_minor="0" + fi + WANT_BOOST_VERSION=`expr $boost_lib_version_req_major \* 100000 \+ $boost_lib_version_req_minor \* 100 \+ $boost_lib_version_req_sub_minor` + AC_MSG_CHECKING(for boostlib >= $boost_lib_version_req) + succeeded=no + + dnl On 64-bit systems check for system libraries in both lib64 and lib. + dnl The former is specified by FHS, but e.g. Debian does not adhere to + dnl this (as it rises problems for generic multi-arch support). + dnl The last entry in the list is chosen by default when no libraries + dnl are found, e.g. when only header-only libraries are installed! + libsubdirs="lib" + ax_arch=`uname -m` + case $ax_arch in + x86_64) + libsubdirs="lib64 libx32 lib lib64" + ;; + ppc64|s390x|sparc64|aarch64|ppc64le) + libsubdirs="lib64 lib lib64 ppc64le" + ;; + esac + + dnl allow for real multi-arch paths e.g. /usr/lib/x86_64-linux-gnu. Give + dnl them priority over the other paths since, if libs are found there, they + dnl are almost assuredly the ones desired. + AC_REQUIRE([AC_CANONICAL_HOST]) + libsubdirs="lib/${host_cpu}-${host_os} $libsubdirs" + + case ${host_cpu} in + i?86) + libsubdirs="lib/i386-${host_os} $libsubdirs" + ;; + esac + + dnl first we check the system location for boost libraries + dnl this location ist chosen if boost libraries are installed with the --layout=system option + dnl or if you install boost with RPM + if test "$ac_boost_path" != ""; then + BOOST_CPPFLAGS="-I$ac_boost_path/include" + for ac_boost_path_tmp in $libsubdirs; do + if test -d "$ac_boost_path"/"$ac_boost_path_tmp" ; then + BOOST_LDFLAGS="-L$ac_boost_path/$ac_boost_path_tmp" + break + fi + done + elif test "$cross_compiling" != yes; then + for ac_boost_path_tmp in /usr /usr/local /opt /opt/local ; do + if test -d "$ac_boost_path_tmp/include/boost" && test -r "$ac_boost_path_tmp/include/boost"; then + for libsubdir in $libsubdirs ; do + if ls "$ac_boost_path_tmp/$libsubdir/libboost_"* >/dev/null 2>&1 ; then break; fi + done + BOOST_LDFLAGS="-L$ac_boost_path_tmp/$libsubdir" + BOOST_CPPFLAGS="-I$ac_boost_path_tmp/include" + break; + fi + done + fi + + dnl overwrite ld flags if we have required special directory with + dnl --with-boost-libdir parameter + if test "$ac_boost_lib_path" != ""; then + BOOST_LDFLAGS="-L$ac_boost_lib_path" + fi + + CPPFLAGS_SAVED="$CPPFLAGS" + CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS" + export CPPFLAGS + + LDFLAGS_SAVED="$LDFLAGS" + LDFLAGS="$LDFLAGS $BOOST_LDFLAGS" + export LDFLAGS + + AC_REQUIRE([AC_PROG_CXX]) + AC_LANG_PUSH(C++) + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ + @%:@include + ]], [[ + #if BOOST_VERSION >= $WANT_BOOST_VERSION + // Everything is okay + #else + # error Boost version is too old + #endif + ]])],[ + AC_MSG_RESULT(yes) + succeeded=yes + found_system=yes + ],[ + ]) + AC_LANG_POP([C++]) + + + + dnl if we found no boost with system layout we search for boost libraries + dnl built and installed without the --layout=system option or for a staged(not installed) version + if test "x$succeeded" != "xyes"; then + _version=0 + if test "$ac_boost_path" != ""; then + if test -d "$ac_boost_path" && test -r "$ac_boost_path"; then + for i in `ls -d $ac_boost_path/include/boost-* 2>/dev/null`; do + _version_tmp=`echo $i | sed "s#$ac_boost_path##" | sed 's/\/include\/boost-//' | sed 's/_/./'` + V_CHECK=`expr $_version_tmp \> $_version` + if test "$V_CHECK" = "1" ; then + _version=$_version_tmp + fi + VERSION_UNDERSCORE=`echo $_version | sed 's/\./_/'` + BOOST_CPPFLAGS="-I$ac_boost_path/include/boost-$VERSION_UNDERSCORE" + done + fi + else + if test "$cross_compiling" != yes; then + for ac_boost_path in /usr /usr/local /opt /opt/local ; do + if test -d "$ac_boost_path" && test -r "$ac_boost_path"; then + for i in `ls -d $ac_boost_path/include/boost-* 2>/dev/null`; do + _version_tmp=`echo $i | sed "s#$ac_boost_path##" | sed 's/\/include\/boost-//' | sed 's/_/./'` + V_CHECK=`expr $_version_tmp \> $_version` + if test "$V_CHECK" = "1" ; then + _version=$_version_tmp + best_path=$ac_boost_path + fi + done + fi + done + + VERSION_UNDERSCORE=`echo $_version | sed 's/\./_/'` + BOOST_CPPFLAGS="-I$best_path/include/boost-$VERSION_UNDERSCORE" + if test "$ac_boost_lib_path" = ""; then + for libsubdir in $libsubdirs ; do + if ls "$best_path/$libsubdir/libboost_"* >/dev/null 2>&1 ; then break; fi + done + BOOST_LDFLAGS="-L$best_path/$libsubdir" + fi + fi + + if test "x$BOOST_ROOT" != "x"; then + for libsubdir in $libsubdirs ; do + if ls "$BOOST_ROOT/stage/$libsubdir/libboost_"* >/dev/null 2>&1 ; then break; fi + done + if test -d "$BOOST_ROOT" && test -r "$BOOST_ROOT" && test -d "$BOOST_ROOT/stage/$libsubdir" && test -r "$BOOST_ROOT/stage/$libsubdir"; then + version_dir=`expr //$BOOST_ROOT : '.*/\(.*\)'` + stage_version=`echo $version_dir | sed 's/boost_//' | sed 's/_/./g'` + stage_version_shorten=`expr $stage_version : '\([[0-9]]*\.[[0-9]]*\)'` + V_CHECK=`expr $stage_version_shorten \>\= $_version` + if test "$V_CHECK" = "1" -a "$ac_boost_lib_path" = "" ; then + AC_MSG_NOTICE(We will use a staged boost library from $BOOST_ROOT) + BOOST_CPPFLAGS="-I$BOOST_ROOT" + BOOST_LDFLAGS="-L$BOOST_ROOT/stage/$libsubdir" + fi + fi + fi + fi + + CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS" + export CPPFLAGS + LDFLAGS="$LDFLAGS $BOOST_LDFLAGS" + export LDFLAGS + + AC_LANG_PUSH(C++) + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ + @%:@include + ]], [[ + #if BOOST_VERSION >= $WANT_BOOST_VERSION + // Everything is okay + #else + # error Boost version is too old + #endif + ]])],[ + AC_MSG_RESULT(yes) + succeeded=yes + found_system=yes + ],[ + ]) + AC_LANG_POP([C++]) + fi + + if test "$succeeded" != "yes" ; then + if test "$_version" = "0" ; then + AC_MSG_NOTICE([[We could not detect the boost libraries (version $boost_lib_version_req_shorten or higher). If you have a staged boost library (still not installed) please specify \$BOOST_ROOT in your environment and do not give a PATH to --with-boost option. If you are sure you have boost installed, then check your version number looking in . See http://randspringer.de/boost for more documentation.]]) + else + AC_MSG_NOTICE([Your boost libraries seems to old (version $_version).]) + fi + # execute ACTION-IF-NOT-FOUND (if present): + ifelse([$3], , :, [$3]) + else + AC_SUBST(BOOST_CPPFLAGS) + AC_SUBST(BOOST_LDFLAGS) + AC_DEFINE(HAVE_BOOST,,[define if the Boost library is available]) + # execute ACTION-IF-FOUND (if present): + ifelse([$2], , :, [$2]) + fi + + CPPFLAGS="$CPPFLAGS_SAVED" + LDFLAGS="$LDFLAGS_SAVED" +fi + +]) diff --git a/m4/ax_boost_system.m4 b/m4/ax_boost_system.m4 new file mode 100644 index 0000000..c4c4555 --- /dev/null +++ b/m4/ax_boost_system.m4 @@ -0,0 +1,120 @@ +# =========================================================================== +# http://www.gnu.org/software/autoconf-archive/ax_boost_system.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_BOOST_SYSTEM +# +# DESCRIPTION +# +# Test for System library from the Boost C++ libraries. The macro requires +# a preceding call to AX_BOOST_BASE. Further documentation is available at +# . +# +# This macro calls: +# +# AC_SUBST(BOOST_SYSTEM_LIB) +# +# And sets: +# +# HAVE_BOOST_SYSTEM +# +# LICENSE +# +# Copyright (c) 2008 Thomas Porschberg +# Copyright (c) 2008 Michael Tindal +# Copyright (c) 2008 Daniel Casimiro +# +# 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 any +# warranty. + +#serial 17 + +AC_DEFUN([AX_BOOST_SYSTEM], +[ + AC_ARG_WITH([boost-system], + AS_HELP_STRING([--with-boost-system@<:@=special-lib@:>@], + [use the System library from boost - it is possible to specify a certain library for the linker + e.g. --with-boost-system=boost_system-gcc-mt ]), + [ + if test "$withval" = "no"; then + want_boost="no" + elif test "$withval" = "yes"; then + want_boost="yes" + ax_boost_user_system_lib="" + else + want_boost="yes" + ax_boost_user_system_lib="$withval" + fi + ], + [want_boost="yes"] + ) + + if test "x$want_boost" = "xyes"; then + AC_REQUIRE([AC_PROG_CC]) + AC_REQUIRE([AC_CANONICAL_BUILD]) + CPPFLAGS_SAVED="$CPPFLAGS" + CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS" + export CPPFLAGS + + LDFLAGS_SAVED="$LDFLAGS" + LDFLAGS="$LDFLAGS $BOOST_LDFLAGS" + export LDFLAGS + + AC_CACHE_CHECK(whether the Boost::System library is available, + ax_cv_boost_system, + [AC_LANG_PUSH([C++]) + CXXFLAGS_SAVE=$CXXFLAGS + + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[@%:@include ]], + [[boost::system::system_category]])], + ax_cv_boost_system=yes, ax_cv_boost_system=no) + CXXFLAGS=$CXXFLAGS_SAVE + AC_LANG_POP([C++]) + ]) + if test "x$ax_cv_boost_system" = "xyes"; then + AC_SUBST(BOOST_CPPFLAGS) + + AC_DEFINE(HAVE_BOOST_SYSTEM,,[define if the Boost::System library is available]) + BOOSTLIBDIR=`echo $BOOST_LDFLAGS | sed -e 's/@<:@^\/@:>@*//'` + + LDFLAGS_SAVE=$LDFLAGS + if test "x$ax_boost_user_system_lib" = "x"; then + for libextension in `ls -r $BOOSTLIBDIR/libboost_system* 2>/dev/null | sed 's,.*/lib,,' | sed 's,\..*,,'` ; do + ax_lib=${libextension} + AC_CHECK_LIB($ax_lib, exit, + [BOOST_SYSTEM_LIB="-l$ax_lib"; AC_SUBST(BOOST_SYSTEM_LIB) link_system="yes"; break], + [link_system="no"]) + done + if test "x$link_system" != "xyes"; then + for libextension in `ls -r $BOOSTLIBDIR/boost_system* 2>/dev/null | sed 's,.*/,,' | sed -e 's,\..*,,'` ; do + ax_lib=${libextension} + AC_CHECK_LIB($ax_lib, exit, + [BOOST_SYSTEM_LIB="-l$ax_lib"; AC_SUBST(BOOST_SYSTEM_LIB) link_system="yes"; break], + [link_system="no"]) + done + fi + + else + for ax_lib in $ax_boost_user_system_lib boost_system-$ax_boost_user_system_lib; do + AC_CHECK_LIB($ax_lib, exit, + [BOOST_SYSTEM_LIB="-l$ax_lib"; AC_SUBST(BOOST_SYSTEM_LIB) link_system="yes"; break], + [link_system="no"]) + done + + fi + if test "x$ax_lib" = "x"; then + AC_MSG_ERROR(Could not find a version of the library!) + fi + if test "x$link_system" = "xno"; then + AC_MSG_ERROR(Could not link against $ax_lib !) + fi + fi + + CPPFLAGS="$CPPFLAGS_SAVED" + LDFLAGS="$LDFLAGS_SAVED" + fi +]) diff --git a/m4/ax_boost_thread.m4 b/m4/ax_boost_thread.m4 new file mode 100644 index 0000000..79e12cd --- /dev/null +++ b/m4/ax_boost_thread.m4 @@ -0,0 +1,149 @@ +# =========================================================================== +# http://www.gnu.org/software/autoconf-archive/ax_boost_thread.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_BOOST_THREAD +# +# DESCRIPTION +# +# Test for Thread library from the Boost C++ libraries. The macro requires +# a preceding call to AX_BOOST_BASE. Further documentation is available at +# . +# +# This macro calls: +# +# AC_SUBST(BOOST_THREAD_LIB) +# +# And sets: +# +# HAVE_BOOST_THREAD +# +# LICENSE +# +# Copyright (c) 2009 Thomas Porschberg +# Copyright (c) 2009 Michael Tindal +# +# 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 any +# warranty. + +#serial 27 + +AC_DEFUN([AX_BOOST_THREAD], +[ + AC_ARG_WITH([boost-thread], + AS_HELP_STRING([--with-boost-thread@<:@=special-lib@:>@], + [use the Thread library from boost - it is possible to specify a certain library for the linker + e.g. --with-boost-thread=boost_thread-gcc-mt ]), + [ + if test "$withval" = "no"; then + want_boost="no" + elif test "$withval" = "yes"; then + want_boost="yes" + ax_boost_user_thread_lib="" + else + want_boost="yes" + ax_boost_user_thread_lib="$withval" + fi + ], + [want_boost="yes"] + ) + + if test "x$want_boost" = "xyes"; then + AC_REQUIRE([AC_PROG_CC]) + AC_REQUIRE([AC_CANONICAL_BUILD]) + CPPFLAGS_SAVED="$CPPFLAGS" + CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS" + export CPPFLAGS + + LDFLAGS_SAVED="$LDFLAGS" + LDFLAGS="$LDFLAGS $BOOST_LDFLAGS" + export LDFLAGS + + AC_CACHE_CHECK(whether the Boost::Thread library is available, + ax_cv_boost_thread, + [AC_LANG_PUSH([C++]) + CXXFLAGS_SAVE=$CXXFLAGS + + if test "x$host_os" = "xsolaris" ; then + CXXFLAGS="-pthreads $CXXFLAGS" + elif test "x$host_os" = "xmingw32" ; then + CXXFLAGS="-mthreads $CXXFLAGS" + else + CXXFLAGS="-pthread $CXXFLAGS" + fi + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[@%:@include ]], + [[boost::thread_group thrds; + return 0;]])], + ax_cv_boost_thread=yes, ax_cv_boost_thread=no) + CXXFLAGS=$CXXFLAGS_SAVE + AC_LANG_POP([C++]) + ]) + if test "x$ax_cv_boost_thread" = "xyes"; then + if test "x$host_os" = "xsolaris" ; then + BOOST_CPPFLAGS="-pthreads $BOOST_CPPFLAGS" + elif test "x$host_os" = "xmingw32" ; then + BOOST_CPPFLAGS="-mthreads $BOOST_CPPFLAGS" + else + BOOST_CPPFLAGS="-pthread $BOOST_CPPFLAGS" + fi + + AC_SUBST(BOOST_CPPFLAGS) + + AC_DEFINE(HAVE_BOOST_THREAD,,[define if the Boost::Thread library is available]) + BOOSTLIBDIR=`echo $BOOST_LDFLAGS | sed -e 's/@<:@^\/@:>@*//'` + + LDFLAGS_SAVE=$LDFLAGS + case "x$host_os" in + *bsd* ) + LDFLAGS="-pthread $LDFLAGS" + break; + ;; + esac + if test "x$ax_boost_user_thread_lib" = "x"; then + for libextension in `ls -r $BOOSTLIBDIR/libboost_thread* 2>/dev/null | sed 's,.*/lib,,' | sed 's,\..*,,'`; do + ax_lib=${libextension} + AC_CHECK_LIB($ax_lib, exit, + [BOOST_THREAD_LIB="-l$ax_lib"; AC_SUBST(BOOST_THREAD_LIB) link_thread="yes"; break], + [link_thread="no"]) + done + if test "x$link_thread" != "xyes"; then + for libextension in `ls -r $BOOSTLIBDIR/boost_thread* 2>/dev/null | sed 's,.*/,,' | sed 's,\..*,,'`; do + ax_lib=${libextension} + AC_CHECK_LIB($ax_lib, exit, + [BOOST_THREAD_LIB="-l$ax_lib"; AC_SUBST(BOOST_THREAD_LIB) link_thread="yes"; break], + [link_thread="no"]) + done + fi + + else + for ax_lib in $ax_boost_user_thread_lib boost_thread-$ax_boost_user_thread_lib; do + AC_CHECK_LIB($ax_lib, exit, + [BOOST_THREAD_LIB="-l$ax_lib"; AC_SUBST(BOOST_THREAD_LIB) link_thread="yes"; break], + [link_thread="no"]) + done + + fi + if test "x$ax_lib" = "x"; then + AC_MSG_ERROR(Could not find a version of the library!) + fi + if test "x$link_thread" = "xno"; then + AC_MSG_ERROR(Could not link against $ax_lib !) + else + case "x$host_os" in + *bsd* ) + BOOST_LDFLAGS="-pthread $BOOST_LDFLAGS" + break; + ;; + esac + + fi + fi + + CPPFLAGS="$CPPFLAGS_SAVED" + LDFLAGS="$LDFLAGS_SAVED" + fi +]) diff --git a/m4/ax_check_compile_flag.m4 b/m4/ax_check_compile_flag.m4 new file mode 100644 index 0000000..51df0c0 --- /dev/null +++ b/m4/ax_check_compile_flag.m4 @@ -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 +# Copyright (c) 2011 Maarten Bosmans +# +# 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 . +# +# 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/m4/ax_cxx_compile_stdcxx_11.m4 b/m4/ax_cxx_compile_stdcxx_11.m4 new file mode 100644 index 0000000..af37acd --- /dev/null +++ b/m4/ax_cxx_compile_stdcxx_11.m4 @@ -0,0 +1,133 @@ +# ============================================================================ +# http://www.gnu.org/software/autoconf-archive/ax_cxx_compile_stdcxx_11.html +# ============================================================================ +# +# SYNOPSIS +# +# AX_CXX_COMPILE_STDCXX_11([ext|noext],[mandatory|optional]) +# +# DESCRIPTION +# +# Check for baseline language coverage in the compiler for the C++11 +# standard; if necessary, add switches to CXXFLAGS to enable support. +# +# The first argument, if specified, indicates whether you insist on an +# extended mode (e.g. -std=gnu++11) or a strict conformance mode (e.g. +# -std=c++11). If neither is specified, you get whatever works, with +# preference for an extended mode. +# +# The second argument, if specified 'mandatory' or if left unspecified, +# indicates that baseline C++11 support is required and that the macro +# should error out if no mode with that support is found. If specified +# 'optional', then configuration proceeds regardless, after defining +# HAVE_CXX11 if and only if a supporting mode is found. +# +# LICENSE +# +# Copyright (c) 2008 Benjamin Kosnik +# Copyright (c) 2012 Zack Weinberg +# Copyright (c) 2013 Roy Stogner +# +# 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 any +# warranty. + +#serial 3 + +m4_define([_AX_CXX_COMPILE_STDCXX_11_testbody], [ + template + struct check + { + static_assert(sizeof(int) <= sizeof(T), "not big enough"); + }; + + typedef check> right_angle_brackets; + + int a; + decltype(a) b; + + typedef check check_type; + check_type c; + check_type&& cr = static_cast(c); + + auto d = a; +]) + +AC_DEFUN([AX_CXX_COMPILE_STDCXX_11], [dnl + m4_if([$1], [], [], + [$1], [ext], [], + [$1], [noext], [], + [m4_fatal([invalid argument `$1' to AX_CXX_COMPILE_STDCXX_11])])dnl + m4_if([$2], [], [ax_cxx_compile_cxx11_required=true], + [$2], [mandatory], [ax_cxx_compile_cxx11_required=true], + [$2], [optional], [ax_cxx_compile_cxx11_required=false], + [m4_fatal([invalid second argument `$2' to AX_CXX_COMPILE_STDCXX_11])])dnl + AC_LANG_PUSH([C++])dnl + ac_success=no + AC_CACHE_CHECK(whether $CXX supports C++11 features by default, + ax_cv_cxx_compile_cxx11, + [AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_11_testbody])], + [ax_cv_cxx_compile_cxx11=yes], + [ax_cv_cxx_compile_cxx11=no])]) + if test x$ax_cv_cxx_compile_cxx11 = xyes; then + ac_success=yes + fi + + m4_if([$1], [noext], [], [dnl + if test x$ac_success = xno; then + for switch in -std=gnu++11 -std=gnu++0x; do + cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx11_$switch]) + AC_CACHE_CHECK(whether $CXX supports C++11 features with $switch, + $cachevar, + [ac_save_CXXFLAGS="$CXXFLAGS" + CXXFLAGS="$CXXFLAGS $switch" + AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_11_testbody])], + [eval $cachevar=yes], + [eval $cachevar=no]) + CXXFLAGS="$ac_save_CXXFLAGS"]) + if eval test x\$$cachevar = xyes; then + CXXFLAGS="$CXXFLAGS $switch" + ac_success=yes + break + fi + done + fi]) + + m4_if([$1], [ext], [], [dnl + if test x$ac_success = xno; then + for switch in -std=c++11 -std=c++0x; do + cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx11_$switch]) + AC_CACHE_CHECK(whether $CXX supports C++11 features with $switch, + $cachevar, + [ac_save_CXXFLAGS="$CXXFLAGS" + CXXFLAGS="$CXXFLAGS $switch" + AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_11_testbody])], + [eval $cachevar=yes], + [eval $cachevar=no]) + CXXFLAGS="$ac_save_CXXFLAGS"]) + if eval test x\$$cachevar = xyes; then + CXXFLAGS="$CXXFLAGS $switch" + ac_success=yes + break + fi + done + fi]) + AC_LANG_POP([C++]) + if test x$ax_cxx_compile_cxx11_required = xtrue; then + if test x$ac_success = xno; then + AC_MSG_ERROR([*** A compiler with support for C++11 language features is required.]) + fi + else + if test x$ac_success = xno; then + HAVE_CXX11=0 + AC_MSG_NOTICE([No compiler with C++11 support was found]) + else + HAVE_CXX11=1 + AC_DEFINE(HAVE_CXX11,1, + [define if the compiler supports basic C++11 syntax]) + fi + + AC_SUBST(HAVE_CXX11) + fi +]) diff --git a/m4/ax_have_epoll.m4 b/m4/ax_have_epoll.m4 new file mode 100644 index 0000000..07ceb49 --- /dev/null +++ b/m4/ax_have_epoll.m4 @@ -0,0 +1,104 @@ +# =========================================================================== +# http://www.gnu.org/software/autoconf-archive/ax_have_epoll.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_HAVE_EPOLL([ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) +# AX_HAVE_EPOLL_PWAIT([ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) +# +# DESCRIPTION +# +# This macro determines whether the system supports the epoll I/O event +# interface. A neat usage example would be: +# +# AX_HAVE_EPOLL( +# [AX_CONFIG_FEATURE_ENABLE(epoll)], +# [AX_CONFIG_FEATURE_DISABLE(epoll)]) +# AX_CONFIG_FEATURE( +# [epoll], [This platform supports epoll(7)], +# [HAVE_EPOLL], [This platform supports epoll(7).]) +# +# The epoll interface was added to the Linux kernel in version 2.5.45, and +# the macro verifies that a kernel newer than this is installed. This +# check is somewhat unreliable if doesn't match the +# running kernel, but it is necessary regardless, because glibc comes with +# stubs for the epoll_create(), epoll_wait(), etc. that allow programs to +# compile and link even if the kernel is too old; the problem would then +# be detected only at runtime. +# +# Linux kernel version 2.6.19 adds the epoll_pwait() call in addition to +# epoll_wait(). The availability of that function can be tested with the +# second macro. Generally speaking, it is safe to assume that +# AX_HAVE_EPOLL would succeed if AX_HAVE_EPOLL_PWAIT has, but not the +# other way round. +# +# LICENSE +# +# Copyright (c) 2008 Peter Simons +# +# 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 any +# warranty. + +#serial 10 + +AC_DEFUN([AX_HAVE_EPOLL], [dnl + ax_have_epoll_cppflags="${CPPFLAGS}" + AC_CHECK_HEADER([linux/version.h], [CPPFLAGS="${CPPFLAGS} -DHAVE_LINUX_VERSION_H"]) + AC_MSG_CHECKING([for Linux epoll(7) interface]) + AC_CACHE_VAL([ax_cv_have_epoll], [dnl + AC_LINK_IFELSE([dnl + AC_LANG_PROGRAM([dnl +#include +#ifdef HAVE_LINUX_VERSION_H +# include +# if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,45) +# error linux kernel version is too old to have epoll +# endif +#endif +], [dnl +int fd, rc; +struct epoll_event ev; +fd = epoll_create(128); +rc = epoll_wait(fd, &ev, 1, 0);])], + [ax_cv_have_epoll=yes], + [ax_cv_have_epoll=no])]) + CPPFLAGS="${ax_have_epoll_cppflags}" + AS_IF([test "${ax_cv_have_epoll}" = "yes"], + [AC_MSG_RESULT([yes]) +$1],[AC_MSG_RESULT([no]) +$2]) +])dnl + +AC_DEFUN([AX_HAVE_EPOLL_PWAIT], [dnl + ax_have_epoll_cppflags="${CPPFLAGS}" + AC_CHECK_HEADER([linux/version.h], + [CPPFLAGS="${CPPFLAGS} -DHAVE_LINUX_VERSION_H"]) + AC_MSG_CHECKING([for Linux epoll(7) interface with signals extension]) + AC_CACHE_VAL([ax_cv_have_epoll_pwait], [dnl + AC_LINK_IFELSE([dnl + AC_LANG_PROGRAM([dnl +#ifdef HAVE_LINUX_VERSION_H +# include +# if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19) +# error linux kernel version is too old to have epoll_pwait +# endif +#endif +#include +#include +], [dnl +int fd, rc; +struct epoll_event ev; +fd = epoll_create(128); +rc = epoll_wait(fd, &ev, 1, 0); +rc = epoll_pwait(fd, &ev, 1, 0, (sigset_t const *)(0));])], + [ax_cv_have_epoll_pwait=yes], + [ax_cv_have_epoll_pwait=no])]) + CPPFLAGS="${ax_have_epoll_cppflags}" + AS_IF([test "${ax_cv_have_epoll_pwait}" = "yes"], + [AC_MSG_RESULT([yes]) +$1],[AC_MSG_RESULT([no]) +$2]) +])dnl diff --git a/m4/ax_python_devel.m4 b/m4/ax_python_devel.m4 new file mode 100644 index 0000000..067fbcf --- /dev/null +++ b/m4/ax_python_devel.m4 @@ -0,0 +1,344 @@ +# =========================================================================== +# http://www.gnu.org/software/autoconf-archive/ax_python_devel.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_PYTHON_DEVEL([version]) +# +# DESCRIPTION +# +# Note: Defines as a precious variable "PYTHON_VERSION". Don't override it +# in your configure.ac. +# +# This macro checks for Python and tries to get the include path to +# 'Python.h'. It provides the $(PYTHON_CPPFLAGS) and $(PYTHON_LDFLAGS) +# output variables. It also exports $(PYTHON_EXTRA_LIBS) and +# $(PYTHON_EXTRA_LDFLAGS) for embedding Python in your code. +# +# You can search for some particular version of Python by passing a +# parameter to this macro, for example ">= '2.3.1'", or "== '2.4'". Please +# note that you *have* to pass also an operator along with the version to +# match, and pay special attention to the single quotes surrounding the +# version number. Don't use "PYTHON_VERSION" for this: that environment +# variable is declared as precious and thus reserved for the end-user. +# +# This macro should work for all versions of Python >= 2.1.0. As an end +# user, you can disable the check for the python version by setting the +# PYTHON_NOVERSIONCHECK environment variable to something else than the +# empty string. +# +# If you need to use this macro for an older Python version, please +# contact the authors. We're always open for feedback. +# +# LICENSE +# +# Copyright (c) 2009 Sebastian Huber +# Copyright (c) 2009 Alan W. Irwin +# Copyright (c) 2009 Rafael Laboissiere +# Copyright (c) 2009 Andrew Collier +# Copyright (c) 2009 Matteo Settenvini +# Copyright (c) 2009 Horst Knorr +# Copyright (c) 2013 Daniel Mullner +# +# 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 . +# +# 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 16 + +AU_ALIAS([AC_PYTHON_DEVEL], [AX_PYTHON_DEVEL]) +AC_DEFUN([AX_PYTHON_DEVEL],[ + # + # Allow the use of a (user set) custom python version + # + AC_ARG_VAR([PYTHON_VERSION],[The installed Python + version to use, for example '2.3'. This string + will be appended to the Python interpreter + canonical name.]) + + AC_PATH_PROG([PYTHON],[python[$PYTHON_VERSION]]) + if test -z "$PYTHON"; then + AC_MSG_WARN([Cannot find python$PYTHON_VERSION in your system path]) + PYTHON_VERSION="" + no_python_devel=yes + fi + +AS_IF([test -z "$no_python_devel"], [ + # + # Check for a version of Python >= 2.1.0 + # + AC_MSG_CHECKING([for a version of Python >= '2.1.0']) + ac_supports_python_ver=`$PYTHON -c "import sys; \ + ver = sys.version.split ()[[0]]; \ + print (ver >= '2.1.0')"` + if test "$ac_supports_python_ver" != "True"; then + if test -z "$PYTHON_NOVERSIONCHECK"; then + AC_MSG_RESULT([no]) + AC_MSG_WARN([ +This version of the AC@&t@_PYTHON_DEVEL macro +doesn't work properly with versions of Python before +2.1.0. You may need to re-run configure, setting the +variables PYTHON_CPPFLAGS, PYTHON_LDFLAGS, PYTHON_SITE_PKG, +PYTHON_EXTRA_LIBS and PYTHON_EXTRA_LDFLAGS by hand. +Moreover, to disable this check, set PYTHON_NOVERSIONCHECK +to something else than an empty string. +]) + no_python_devel=yes + else + AC_MSG_RESULT([skip at user request]) + fi + else + AC_MSG_RESULT([yes]) + fi +]) # AS_IF + +AS_IF([test -z "$no_python_devel"], [ + # + # if the macro parameter ``version'' is set, honour it + # + if test -n "$1"; then + AC_MSG_CHECKING([for a version of Python $1]) + ac_supports_python_ver=`$PYTHON -c "import sys; \ + ver = sys.version.split ()[[0]]; \ + print (ver $1)"` + if test "$ac_supports_python_ver" = "True"; then + AC_MSG_RESULT([yes]) + else + AC_MSG_RESULT([no]) + AC_MSG_WARN([this package requires Python $1. +If you have it installed, but it isn't the default Python +interpreter in your system path, please pass the PYTHON_VERSION +variable to configure. See ``configure --help'' for reference. +]) + PYTHON_VERSION="" + no_python_devel=yes + fi + fi +]) # AS_IF + +AS_IF([test -z "$no_python_devel"], [ + # + # Check if you have distutils, else fail + # + AC_MSG_CHECKING([for the distutils Python package]) + ac_distutils_result=`$PYTHON -c "import distutils" 2>&1` + if test -z "$ac_distutils_result"; then + AC_MSG_RESULT([yes]) + else + AC_MSG_RESULT([no]) + AC_MSG_WARN([cannot import Python module "distutils". +Please check your Python installation. The error was: +$ac_distutils_result]) + PYTHON_VERSION="" + no_python_devel=yes + fi +]) # AS_IF + +AS_IF([test -z "$no_python_devel"], [ + # + # Check for Python include path + # + AC_MSG_CHECKING([for Python include path]) + if test -z "$PYTHON_CPPFLAGS"; then + python_path=`$PYTHON -c "import distutils.sysconfig; \ + print (distutils.sysconfig.get_python_inc ());"` + plat_python_path=`$PYTHON -c "import distutils.sysconfig; \ + print (distutils.sysconfig.get_python_inc (plat_specific=1));"` + if test -n "${python_path}"; then + if test "${plat_python_path}" != "${python_path}"; then + python_path="-I$python_path -I$plat_python_path" + else + python_path="-I$python_path" + fi + fi + PYTHON_CPPFLAGS=$python_path + fi + AC_MSG_RESULT([$PYTHON_CPPFLAGS]) + AC_SUBST([PYTHON_CPPFLAGS]) + + # + # Check for Python library path + # + AC_MSG_CHECKING([for Python library path]) + if test -z "$PYTHON_LDFLAGS"; then + # (makes two attempts to ensure we've got a version number + # from the interpreter) + ac_python_version=`cat<]], + [[Py_Initialize();]]) + ],[pythonexists=yes],[pythonexists=no]) + AC_LANG_POP([C]) + # turn back to default flags + CPPFLAGS="$ac_save_CPPFLAGS" + LIBS="$ac_save_LIBS" + + AC_MSG_RESULT([$pythonexists]) + + if test ! "x$pythonexists" = "xyes"; then + AC_MSG_WARN([ + Could not link test program to Python. Maybe the main Python library has been + installed in some non-standard library path. If so, pass it to configure, + via the LDFLAGS environment variable. + Example: ./configure LDFLAGS="-L/usr/non-standard-path/python/lib" + ============================================================================ + ERROR! + You probably have to install the development version of the Python package + for your distribution. The exact name of this package varies among them. + ============================================================================ + ]) + PYTHON_VERSION="" + no_python_devel=yes + fi + + # + # all done! + # +]) # AS_IF + +AS_IF([test -z "$no_python_devel"], + [have_python_dev=yes], [have_python_dev=no]) + +]) # AS_IF diff --git a/m4/libtool.m4 b/m4/libtool.m4 new file mode 100644 index 0000000..d7c043f --- /dev/null +++ b/m4/libtool.m4 @@ -0,0 +1,7997 @@ +# libtool.m4 - Configure libtool for the host system. -*-Autoconf-*- +# +# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2003, 2004, 2005, +# 2006, 2007, 2008, 2009, 2010, 2011 Free Software +# Foundation, Inc. +# Written by Gordon Matzigkeit, 1996 +# +# This file is free software; the Free Software Foundation gives +# unlimited permission to copy and/or distribute it, with or without +# modifications, as long as this notice is preserved. + +m4_define([_LT_COPYING], [dnl +# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2003, 2004, 2005, +# 2006, 2007, 2008, 2009, 2010, 2011 Free Software +# Foundation, Inc. +# Written by Gordon Matzigkeit, 1996 +# +# This file is part of GNU Libtool. +# +# GNU Libtool 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 2 of +# the License, or (at your option) any later version. +# +# As a special exception to the GNU General Public License, +# if you distribute this file as part of a program or library that +# is built using GNU Libtool, you may include this file under the +# same distribution terms that you use for the rest of that program. +# +# GNU Libtool 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 GNU Libtool; see the file COPYING. If not, a copy +# can be downloaded from http://www.gnu.org/licenses/gpl.html, or +# obtained by writing to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +]) + +# serial 57 LT_INIT + + +# LT_PREREQ(VERSION) +# ------------------ +# Complain and exit if this libtool version is less that VERSION. +m4_defun([LT_PREREQ], +[m4_if(m4_version_compare(m4_defn([LT_PACKAGE_VERSION]), [$1]), -1, + [m4_default([$3], + [m4_fatal([Libtool version $1 or higher is required], + 63)])], + [$2])]) + + +# _LT_CHECK_BUILDDIR +# ------------------ +# Complain if the absolute build directory name contains unusual characters +m4_defun([_LT_CHECK_BUILDDIR], +[case `pwd` in + *\ * | *\ *) + AC_MSG_WARN([Libtool does not cope well with whitespace in `pwd`]) ;; +esac +]) + + +# LT_INIT([OPTIONS]) +# ------------------ +AC_DEFUN([LT_INIT], +[AC_PREREQ([2.58])dnl We use AC_INCLUDES_DEFAULT +AC_REQUIRE([AC_CONFIG_AUX_DIR_DEFAULT])dnl +AC_BEFORE([$0], [LT_LANG])dnl +AC_BEFORE([$0], [LT_OUTPUT])dnl +AC_BEFORE([$0], [LTDL_INIT])dnl +m4_require([_LT_CHECK_BUILDDIR])dnl + +dnl Autoconf doesn't catch unexpanded LT_ macros by default: +m4_pattern_forbid([^_?LT_[A-Z_]+$])dnl +m4_pattern_allow([^(_LT_EOF|LT_DLGLOBAL|LT_DLLAZY_OR_NOW|LT_MULTI_MODULE)$])dnl +dnl aclocal doesn't pull ltoptions.m4, ltsugar.m4, or ltversion.m4 +dnl unless we require an AC_DEFUNed macro: +AC_REQUIRE([LTOPTIONS_VERSION])dnl +AC_REQUIRE([LTSUGAR_VERSION])dnl +AC_REQUIRE([LTVERSION_VERSION])dnl +AC_REQUIRE([LTOBSOLETE_VERSION])dnl +m4_require([_LT_PROG_LTMAIN])dnl + +_LT_SHELL_INIT([SHELL=${CONFIG_SHELL-/bin/sh}]) + +dnl Parse OPTIONS +_LT_SET_OPTIONS([$0], [$1]) + +# This can be used to rebuild libtool when needed +LIBTOOL_DEPS="$ltmain" + +# Always use our own libtool. +LIBTOOL='$(SHELL) $(top_builddir)/libtool' +AC_SUBST(LIBTOOL)dnl + +_LT_SETUP + +# Only expand once: +m4_define([LT_INIT]) +])# LT_INIT + +# Old names: +AU_ALIAS([AC_PROG_LIBTOOL], [LT_INIT]) +AU_ALIAS([AM_PROG_LIBTOOL], [LT_INIT]) +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AC_PROG_LIBTOOL], []) +dnl AC_DEFUN([AM_PROG_LIBTOOL], []) + + +# _LT_CC_BASENAME(CC) +# ------------------- +# Calculate cc_basename. Skip known compiler wrappers and cross-prefix. +m4_defun([_LT_CC_BASENAME], +[for cc_temp in $1""; do + case $cc_temp in + compile | *[[\\/]]compile | ccache | *[[\\/]]ccache ) ;; + distcc | *[[\\/]]distcc | purify | *[[\\/]]purify ) ;; + \-*) ;; + *) break;; + esac +done +cc_basename=`$ECHO "$cc_temp" | $SED "s%.*/%%; s%^$host_alias-%%"` +]) + + +# _LT_FILEUTILS_DEFAULTS +# ---------------------- +# It is okay to use these file commands and assume they have been set +# sensibly after `m4_require([_LT_FILEUTILS_DEFAULTS])'. +m4_defun([_LT_FILEUTILS_DEFAULTS], +[: ${CP="cp -f"} +: ${MV="mv -f"} +: ${RM="rm -f"} +])# _LT_FILEUTILS_DEFAULTS + + +# _LT_SETUP +# --------- +m4_defun([_LT_SETUP], +[AC_REQUIRE([AC_CANONICAL_HOST])dnl +AC_REQUIRE([AC_CANONICAL_BUILD])dnl +AC_REQUIRE([_LT_PREPARE_SED_QUOTE_VARS])dnl +AC_REQUIRE([_LT_PROG_ECHO_BACKSLASH])dnl + +_LT_DECL([], [PATH_SEPARATOR], [1], [The PATH separator for the build system])dnl +dnl +_LT_DECL([], [host_alias], [0], [The host system])dnl +_LT_DECL([], [host], [0])dnl +_LT_DECL([], [host_os], [0])dnl +dnl +_LT_DECL([], [build_alias], [0], [The build system])dnl +_LT_DECL([], [build], [0])dnl +_LT_DECL([], [build_os], [0])dnl +dnl +AC_REQUIRE([AC_PROG_CC])dnl +AC_REQUIRE([LT_PATH_LD])dnl +AC_REQUIRE([LT_PATH_NM])dnl +dnl +AC_REQUIRE([AC_PROG_LN_S])dnl +test -z "$LN_S" && LN_S="ln -s" +_LT_DECL([], [LN_S], [1], [Whether we need soft or hard links])dnl +dnl +AC_REQUIRE([LT_CMD_MAX_LEN])dnl +_LT_DECL([objext], [ac_objext], [0], [Object file suffix (normally "o")])dnl +_LT_DECL([], [exeext], [0], [Executable file suffix (normally "")])dnl +dnl +m4_require([_LT_FILEUTILS_DEFAULTS])dnl +m4_require([_LT_CHECK_SHELL_FEATURES])dnl +m4_require([_LT_PATH_CONVERSION_FUNCTIONS])dnl +m4_require([_LT_CMD_RELOAD])dnl +m4_require([_LT_CHECK_MAGIC_METHOD])dnl +m4_require([_LT_CHECK_SHAREDLIB_FROM_LINKLIB])dnl +m4_require([_LT_CMD_OLD_ARCHIVE])dnl +m4_require([_LT_CMD_GLOBAL_SYMBOLS])dnl +m4_require([_LT_WITH_SYSROOT])dnl + +_LT_CONFIG_LIBTOOL_INIT([ +# See if we are running on zsh, and set the options which allow our +# commands through without removal of \ escapes INIT. +if test -n "\${ZSH_VERSION+set}" ; then + setopt NO_GLOB_SUBST +fi +]) +if test -n "${ZSH_VERSION+set}" ; then + setopt NO_GLOB_SUBST +fi + +_LT_CHECK_OBJDIR + +m4_require([_LT_TAG_COMPILER])dnl + +case $host_os in +aix3*) + # AIX sometimes has problems with the GCC collect2 program. For some + # reason, if we set the COLLECT_NAMES environment variable, the problems + # vanish in a puff of smoke. + if test "X${COLLECT_NAMES+set}" != Xset; then + COLLECT_NAMES= + export COLLECT_NAMES + fi + ;; +esac + +# Global variables: +ofile=libtool +can_build_shared=yes + +# All known linkers require a `.a' archive for static linking (except MSVC, +# which needs '.lib'). +libext=a + +with_gnu_ld="$lt_cv_prog_gnu_ld" + +old_CC="$CC" +old_CFLAGS="$CFLAGS" + +# Set sane defaults for various variables +test -z "$CC" && CC=cc +test -z "$LTCC" && LTCC=$CC +test -z "$LTCFLAGS" && LTCFLAGS=$CFLAGS +test -z "$LD" && LD=ld +test -z "$ac_objext" && ac_objext=o + +_LT_CC_BASENAME([$compiler]) + +# Only perform the check for file, if the check method requires it +test -z "$MAGIC_CMD" && MAGIC_CMD=file +case $deplibs_check_method in +file_magic*) + if test "$file_magic_cmd" = '$MAGIC_CMD'; then + _LT_PATH_MAGIC + fi + ;; +esac + +# Use C for the default configuration in the libtool script +LT_SUPPORTED_TAG([CC]) +_LT_LANG_C_CONFIG +_LT_LANG_DEFAULT_CONFIG +_LT_CONFIG_COMMANDS +])# _LT_SETUP + + +# _LT_PREPARE_SED_QUOTE_VARS +# -------------------------- +# Define a few sed substitution that help us do robust quoting. +m4_defun([_LT_PREPARE_SED_QUOTE_VARS], +[# Backslashify metacharacters that are still active within +# double-quoted strings. +sed_quote_subst='s/\([["`$\\]]\)/\\\1/g' + +# Same as above, but do not quote variable references. +double_quote_subst='s/\([["`\\]]\)/\\\1/g' + +# Sed substitution to delay expansion of an escaped shell variable in a +# double_quote_subst'ed string. +delay_variable_subst='s/\\\\\\\\\\\$/\\\\\\$/g' + +# Sed substitution to delay expansion of an escaped single quote. +delay_single_quote_subst='s/'\''/'\'\\\\\\\'\''/g' + +# Sed substitution to avoid accidental globbing in evaled expressions +no_glob_subst='s/\*/\\\*/g' +]) + +# _LT_PROG_LTMAIN +# --------------- +# Note that this code is called both from `configure', and `config.status' +# now that we use AC_CONFIG_COMMANDS to generate libtool. Notably, +# `config.status' has no value for ac_aux_dir unless we are using Automake, +# so we pass a copy along to make sure it has a sensible value anyway. +m4_defun([_LT_PROG_LTMAIN], +[m4_ifdef([AC_REQUIRE_AUX_FILE], [AC_REQUIRE_AUX_FILE([ltmain.sh])])dnl +_LT_CONFIG_LIBTOOL_INIT([ac_aux_dir='$ac_aux_dir']) +ltmain="$ac_aux_dir/ltmain.sh" +])# _LT_PROG_LTMAIN + + +## ------------------------------------- ## +## Accumulate code for creating libtool. ## +## ------------------------------------- ## + +# So that we can recreate a full libtool script including additional +# tags, we accumulate the chunks of code to send to AC_CONFIG_COMMANDS +# in macros and then make a single call at the end using the `libtool' +# label. + + +# _LT_CONFIG_LIBTOOL_INIT([INIT-COMMANDS]) +# ---------------------------------------- +# Register INIT-COMMANDS to be passed to AC_CONFIG_COMMANDS later. +m4_define([_LT_CONFIG_LIBTOOL_INIT], +[m4_ifval([$1], + [m4_append([_LT_OUTPUT_LIBTOOL_INIT], + [$1 +])])]) + +# Initialize. +m4_define([_LT_OUTPUT_LIBTOOL_INIT]) + + +# _LT_CONFIG_LIBTOOL([COMMANDS]) +# ------------------------------ +# Register COMMANDS to be passed to AC_CONFIG_COMMANDS later. +m4_define([_LT_CONFIG_LIBTOOL], +[m4_ifval([$1], + [m4_append([_LT_OUTPUT_LIBTOOL_COMMANDS], + [$1 +])])]) + +# Initialize. +m4_define([_LT_OUTPUT_LIBTOOL_COMMANDS]) + + +# _LT_CONFIG_SAVE_COMMANDS([COMMANDS], [INIT_COMMANDS]) +# ----------------------------------------------------- +m4_defun([_LT_CONFIG_SAVE_COMMANDS], +[_LT_CONFIG_LIBTOOL([$1]) +_LT_CONFIG_LIBTOOL_INIT([$2]) +]) + + +# _LT_FORMAT_COMMENT([COMMENT]) +# ----------------------------- +# Add leading comment marks to the start of each line, and a trailing +# full-stop to the whole comment if one is not present already. +m4_define([_LT_FORMAT_COMMENT], +[m4_ifval([$1], [ +m4_bpatsubst([m4_bpatsubst([$1], [^ *], [# ])], + [['`$\]], [\\\&])]m4_bmatch([$1], [[!?.]$], [], [.]) +)]) + + + +## ------------------------ ## +## FIXME: Eliminate VARNAME ## +## ------------------------ ## + + +# _LT_DECL([CONFIGNAME], VARNAME, VALUE, [DESCRIPTION], [IS-TAGGED?]) +# ------------------------------------------------------------------- +# CONFIGNAME is the name given to the value in the libtool script. +# VARNAME is the (base) name used in the configure script. +# VALUE may be 0, 1 or 2 for a computed quote escaped value based on +# VARNAME. Any other value will be used directly. +m4_define([_LT_DECL], +[lt_if_append_uniq([lt_decl_varnames], [$2], [, ], + [lt_dict_add_subkey([lt_decl_dict], [$2], [libtool_name], + [m4_ifval([$1], [$1], [$2])]) + lt_dict_add_subkey([lt_decl_dict], [$2], [value], [$3]) + m4_ifval([$4], + [lt_dict_add_subkey([lt_decl_dict], [$2], [description], [$4])]) + lt_dict_add_subkey([lt_decl_dict], [$2], + [tagged?], [m4_ifval([$5], [yes], [no])])]) +]) + + +# _LT_TAGDECL([CONFIGNAME], VARNAME, VALUE, [DESCRIPTION]) +# -------------------------------------------------------- +m4_define([_LT_TAGDECL], [_LT_DECL([$1], [$2], [$3], [$4], [yes])]) + + +# lt_decl_tag_varnames([SEPARATOR], [VARNAME1...]) +# ------------------------------------------------ +m4_define([lt_decl_tag_varnames], +[_lt_decl_filter([tagged?], [yes], $@)]) + + +# _lt_decl_filter(SUBKEY, VALUE, [SEPARATOR], [VARNAME1..]) +# --------------------------------------------------------- +m4_define([_lt_decl_filter], +[m4_case([$#], + [0], [m4_fatal([$0: too few arguments: $#])], + [1], [m4_fatal([$0: too few arguments: $#: $1])], + [2], [lt_dict_filter([lt_decl_dict], [$1], [$2], [], lt_decl_varnames)], + [3], [lt_dict_filter([lt_decl_dict], [$1], [$2], [$3], lt_decl_varnames)], + [lt_dict_filter([lt_decl_dict], $@)])[]dnl +]) + + +# lt_decl_quote_varnames([SEPARATOR], [VARNAME1...]) +# -------------------------------------------------- +m4_define([lt_decl_quote_varnames], +[_lt_decl_filter([value], [1], $@)]) + + +# lt_decl_dquote_varnames([SEPARATOR], [VARNAME1...]) +# --------------------------------------------------- +m4_define([lt_decl_dquote_varnames], +[_lt_decl_filter([value], [2], $@)]) + + +# lt_decl_varnames_tagged([SEPARATOR], [VARNAME1...]) +# --------------------------------------------------- +m4_define([lt_decl_varnames_tagged], +[m4_assert([$# <= 2])dnl +_$0(m4_quote(m4_default([$1], [[, ]])), + m4_ifval([$2], [[$2]], [m4_dquote(lt_decl_tag_varnames)]), + m4_split(m4_normalize(m4_quote(_LT_TAGS)), [ ]))]) +m4_define([_lt_decl_varnames_tagged], +[m4_ifval([$3], [lt_combine([$1], [$2], [_], $3)])]) + + +# lt_decl_all_varnames([SEPARATOR], [VARNAME1...]) +# ------------------------------------------------ +m4_define([lt_decl_all_varnames], +[_$0(m4_quote(m4_default([$1], [[, ]])), + m4_if([$2], [], + m4_quote(lt_decl_varnames), + m4_quote(m4_shift($@))))[]dnl +]) +m4_define([_lt_decl_all_varnames], +[lt_join($@, lt_decl_varnames_tagged([$1], + lt_decl_tag_varnames([[, ]], m4_shift($@))))dnl +]) + + +# _LT_CONFIG_STATUS_DECLARE([VARNAME]) +# ------------------------------------ +# Quote a variable value, and forward it to `config.status' so that its +# declaration there will have the same value as in `configure'. VARNAME +# must have a single quote delimited value for this to work. +m4_define([_LT_CONFIG_STATUS_DECLARE], +[$1='`$ECHO "$][$1" | $SED "$delay_single_quote_subst"`']) + + +# _LT_CONFIG_STATUS_DECLARATIONS +# ------------------------------ +# We delimit libtool config variables with single quotes, so when +# we write them to config.status, we have to be sure to quote all +# embedded single quotes properly. In configure, this macro expands +# each variable declared with _LT_DECL (and _LT_TAGDECL) into: +# +# ='`$ECHO "$" | $SED "$delay_single_quote_subst"`' +m4_defun([_LT_CONFIG_STATUS_DECLARATIONS], +[m4_foreach([_lt_var], m4_quote(lt_decl_all_varnames), + [m4_n([_LT_CONFIG_STATUS_DECLARE(_lt_var)])])]) + + +# _LT_LIBTOOL_TAGS +# ---------------- +# Output comment and list of tags supported by the script +m4_defun([_LT_LIBTOOL_TAGS], +[_LT_FORMAT_COMMENT([The names of the tagged configurations supported by this script])dnl +available_tags="_LT_TAGS"dnl +]) + + +# _LT_LIBTOOL_DECLARE(VARNAME, [TAG]) +# ----------------------------------- +# Extract the dictionary values for VARNAME (optionally with TAG) and +# expand to a commented shell variable setting: +# +# # Some comment about what VAR is for. +# visible_name=$lt_internal_name +m4_define([_LT_LIBTOOL_DECLARE], +[_LT_FORMAT_COMMENT(m4_quote(lt_dict_fetch([lt_decl_dict], [$1], + [description])))[]dnl +m4_pushdef([_libtool_name], + m4_quote(lt_dict_fetch([lt_decl_dict], [$1], [libtool_name])))[]dnl +m4_case(m4_quote(lt_dict_fetch([lt_decl_dict], [$1], [value])), + [0], [_libtool_name=[$]$1], + [1], [_libtool_name=$lt_[]$1], + [2], [_libtool_name=$lt_[]$1], + [_libtool_name=lt_dict_fetch([lt_decl_dict], [$1], [value])])[]dnl +m4_ifval([$2], [_$2])[]m4_popdef([_libtool_name])[]dnl +]) + + +# _LT_LIBTOOL_CONFIG_VARS +# ----------------------- +# Produce commented declarations of non-tagged libtool config variables +# suitable for insertion in the LIBTOOL CONFIG section of the `libtool' +# script. Tagged libtool config variables (even for the LIBTOOL CONFIG +# section) are produced by _LT_LIBTOOL_TAG_VARS. +m4_defun([_LT_LIBTOOL_CONFIG_VARS], +[m4_foreach([_lt_var], + m4_quote(_lt_decl_filter([tagged?], [no], [], lt_decl_varnames)), + [m4_n([_LT_LIBTOOL_DECLARE(_lt_var)])])]) + + +# _LT_LIBTOOL_TAG_VARS(TAG) +# ------------------------- +m4_define([_LT_LIBTOOL_TAG_VARS], +[m4_foreach([_lt_var], m4_quote(lt_decl_tag_varnames), + [m4_n([_LT_LIBTOOL_DECLARE(_lt_var, [$1])])])]) + + +# _LT_TAGVAR(VARNAME, [TAGNAME]) +# ------------------------------ +m4_define([_LT_TAGVAR], [m4_ifval([$2], [$1_$2], [$1])]) + + +# _LT_CONFIG_COMMANDS +# ------------------- +# Send accumulated output to $CONFIG_STATUS. Thanks to the lists of +# variables for single and double quote escaping we saved from calls +# to _LT_DECL, we can put quote escaped variables declarations +# into `config.status', and then the shell code to quote escape them in +# for loops in `config.status'. Finally, any additional code accumulated +# from calls to _LT_CONFIG_LIBTOOL_INIT is expanded. +m4_defun([_LT_CONFIG_COMMANDS], +[AC_PROVIDE_IFELSE([LT_OUTPUT], + dnl If the libtool generation code has been placed in $CONFIG_LT, + dnl instead of duplicating it all over again into config.status, + dnl then we will have config.status run $CONFIG_LT later, so it + dnl needs to know what name is stored there: + [AC_CONFIG_COMMANDS([libtool], + [$SHELL $CONFIG_LT || AS_EXIT(1)], [CONFIG_LT='$CONFIG_LT'])], + dnl If the libtool generation code is destined for config.status, + dnl expand the accumulated commands and init code now: + [AC_CONFIG_COMMANDS([libtool], + [_LT_OUTPUT_LIBTOOL_COMMANDS], [_LT_OUTPUT_LIBTOOL_COMMANDS_INIT])]) +])#_LT_CONFIG_COMMANDS + + +# Initialize. +m4_define([_LT_OUTPUT_LIBTOOL_COMMANDS_INIT], +[ + +# The HP-UX ksh and POSIX shell print the target directory to stdout +# if CDPATH is set. +(unset CDPATH) >/dev/null 2>&1 && unset CDPATH + +sed_quote_subst='$sed_quote_subst' +double_quote_subst='$double_quote_subst' +delay_variable_subst='$delay_variable_subst' +_LT_CONFIG_STATUS_DECLARATIONS +LTCC='$LTCC' +LTCFLAGS='$LTCFLAGS' +compiler='$compiler_DEFAULT' + +# A function that is used when there is no print builtin or printf. +func_fallback_echo () +{ + eval 'cat <<_LTECHO_EOF +\$[]1 +_LTECHO_EOF' +} + +# Quote evaled strings. +for var in lt_decl_all_varnames([[ \ +]], lt_decl_quote_varnames); do + case \`eval \\\\\$ECHO \\\\""\\\\\$\$var"\\\\"\` in + *[[\\\\\\\`\\"\\\$]]*) + eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"\\\$\$var\\" | \\\$SED \\"\\\$sed_quote_subst\\"\\\`\\\\\\"" + ;; + *) + eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\"" + ;; + esac +done + +# Double-quote double-evaled strings. +for var in lt_decl_all_varnames([[ \ +]], lt_decl_dquote_varnames); do + case \`eval \\\\\$ECHO \\\\""\\\\\$\$var"\\\\"\` in + *[[\\\\\\\`\\"\\\$]]*) + eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"\\\$\$var\\" | \\\$SED -e \\"\\\$double_quote_subst\\" -e \\"\\\$sed_quote_subst\\" -e \\"\\\$delay_variable_subst\\"\\\`\\\\\\"" + ;; + *) + eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\"" + ;; + esac +done + +_LT_OUTPUT_LIBTOOL_INIT +]) + +# _LT_GENERATED_FILE_INIT(FILE, [COMMENT]) +# ------------------------------------ +# Generate a child script FILE with all initialization necessary to +# reuse the environment learned by the parent script, and make the +# file executable. If COMMENT is supplied, it is inserted after the +# `#!' sequence but before initialization text begins. After this +# macro, additional text can be appended to FILE to form the body of +# the child script. The macro ends with non-zero status if the +# file could not be fully written (such as if the disk is full). +m4_ifdef([AS_INIT_GENERATED], +[m4_defun([_LT_GENERATED_FILE_INIT],[AS_INIT_GENERATED($@)])], +[m4_defun([_LT_GENERATED_FILE_INIT], +[m4_require([AS_PREPARE])]dnl +[m4_pushdef([AS_MESSAGE_LOG_FD])]dnl +[lt_write_fail=0 +cat >$1 <<_ASEOF || lt_write_fail=1 +#! $SHELL +# Generated by $as_me. +$2 +SHELL=\${CONFIG_SHELL-$SHELL} +export SHELL +_ASEOF +cat >>$1 <<\_ASEOF || lt_write_fail=1 +AS_SHELL_SANITIZE +_AS_PREPARE +exec AS_MESSAGE_FD>&1 +_ASEOF +test $lt_write_fail = 0 && chmod +x $1[]dnl +m4_popdef([AS_MESSAGE_LOG_FD])])])# _LT_GENERATED_FILE_INIT + +# LT_OUTPUT +# --------- +# This macro allows early generation of the libtool script (before +# AC_OUTPUT is called), incase it is used in configure for compilation +# tests. +AC_DEFUN([LT_OUTPUT], +[: ${CONFIG_LT=./config.lt} +AC_MSG_NOTICE([creating $CONFIG_LT]) +_LT_GENERATED_FILE_INIT(["$CONFIG_LT"], +[# Run this file to recreate a libtool stub with the current configuration.]) + +cat >>"$CONFIG_LT" <<\_LTEOF +lt_cl_silent=false +exec AS_MESSAGE_LOG_FD>>config.log +{ + echo + AS_BOX([Running $as_me.]) +} >&AS_MESSAGE_LOG_FD + +lt_cl_help="\ +\`$as_me' creates a local libtool stub from the current configuration, +for use in further configure time tests before the real libtool is +generated. + +Usage: $[0] [[OPTIONS]] + + -h, --help print this help, then exit + -V, --version print version number, then exit + -q, --quiet do not print progress messages + -d, --debug don't remove temporary files + +Report bugs to ." + +lt_cl_version="\ +m4_ifset([AC_PACKAGE_NAME], [AC_PACKAGE_NAME ])config.lt[]dnl +m4_ifset([AC_PACKAGE_VERSION], [ AC_PACKAGE_VERSION]) +configured by $[0], generated by m4_PACKAGE_STRING. + +Copyright (C) 2011 Free Software Foundation, Inc. +This config.lt script is free software; the Free Software Foundation +gives unlimited permision to copy, distribute and modify it." + +while test $[#] != 0 +do + case $[1] in + --version | --v* | -V ) + echo "$lt_cl_version"; exit 0 ;; + --help | --h* | -h ) + echo "$lt_cl_help"; exit 0 ;; + --debug | --d* | -d ) + debug=: ;; + --quiet | --q* | --silent | --s* | -q ) + lt_cl_silent=: ;; + + -*) AC_MSG_ERROR([unrecognized option: $[1] +Try \`$[0] --help' for more information.]) ;; + + *) AC_MSG_ERROR([unrecognized argument: $[1] +Try \`$[0] --help' for more information.]) ;; + esac + shift +done + +if $lt_cl_silent; then + exec AS_MESSAGE_FD>/dev/null +fi +_LTEOF + +cat >>"$CONFIG_LT" <<_LTEOF +_LT_OUTPUT_LIBTOOL_COMMANDS_INIT +_LTEOF + +cat >>"$CONFIG_LT" <<\_LTEOF +AC_MSG_NOTICE([creating $ofile]) +_LT_OUTPUT_LIBTOOL_COMMANDS +AS_EXIT(0) +_LTEOF +chmod +x "$CONFIG_LT" + +# configure is writing to config.log, but config.lt does its own redirection, +# appending to config.log, which fails on DOS, as config.log is still kept +# open by configure. Here we exec the FD to /dev/null, effectively closing +# config.log, so it can be properly (re)opened and appended to by config.lt. +lt_cl_success=: +test "$silent" = yes && + lt_config_lt_args="$lt_config_lt_args --quiet" +exec AS_MESSAGE_LOG_FD>/dev/null +$SHELL "$CONFIG_LT" $lt_config_lt_args || lt_cl_success=false +exec AS_MESSAGE_LOG_FD>>config.log +$lt_cl_success || AS_EXIT(1) +])# LT_OUTPUT + + +# _LT_CONFIG(TAG) +# --------------- +# If TAG is the built-in tag, create an initial libtool script with a +# default configuration from the untagged config vars. Otherwise add code +# to config.status for appending the configuration named by TAG from the +# matching tagged config vars. +m4_defun([_LT_CONFIG], +[m4_require([_LT_FILEUTILS_DEFAULTS])dnl +_LT_CONFIG_SAVE_COMMANDS([ + m4_define([_LT_TAG], m4_if([$1], [], [C], [$1]))dnl + m4_if(_LT_TAG, [C], [ + # See if we are running on zsh, and set the options which allow our + # commands through without removal of \ escapes. + if test -n "${ZSH_VERSION+set}" ; then + setopt NO_GLOB_SUBST + fi + + cfgfile="${ofile}T" + trap "$RM \"$cfgfile\"; exit 1" 1 2 15 + $RM "$cfgfile" + + cat <<_LT_EOF >> "$cfgfile" +#! $SHELL + +# `$ECHO "$ofile" | sed 's%^.*/%%'` - Provide generalized library-building support services. +# Generated automatically by $as_me ($PACKAGE$TIMESTAMP) $VERSION +# Libtool was configured on host `(hostname || uname -n) 2>/dev/null | sed 1q`: +# NOTE: Changes made to this file will be lost: look at ltmain.sh. +# +_LT_COPYING +_LT_LIBTOOL_TAGS + +# ### BEGIN LIBTOOL CONFIG +_LT_LIBTOOL_CONFIG_VARS +_LT_LIBTOOL_TAG_VARS +# ### END LIBTOOL CONFIG + +_LT_EOF + + case $host_os in + aix3*) + cat <<\_LT_EOF >> "$cfgfile" +# AIX sometimes has problems with the GCC collect2 program. For some +# reason, if we set the COLLECT_NAMES environment variable, the problems +# vanish in a puff of smoke. +if test "X${COLLECT_NAMES+set}" != Xset; then + COLLECT_NAMES= + export COLLECT_NAMES +fi +_LT_EOF + ;; + esac + + _LT_PROG_LTMAIN + + # We use sed instead of cat because bash on DJGPP gets confused if + # if finds mixed CR/LF and LF-only lines. Since sed operates in + # text mode, it properly converts lines to CR/LF. This bash problem + # is reportedly fixed, but why not run on old versions too? + sed '$q' "$ltmain" >> "$cfgfile" \ + || (rm -f "$cfgfile"; exit 1) + + _LT_PROG_REPLACE_SHELLFNS + + mv -f "$cfgfile" "$ofile" || + (rm -f "$ofile" && cp "$cfgfile" "$ofile" && rm -f "$cfgfile") + chmod +x "$ofile" +], +[cat <<_LT_EOF >> "$ofile" + +dnl Unfortunately we have to use $1 here, since _LT_TAG is not expanded +dnl in a comment (ie after a #). +# ### BEGIN LIBTOOL TAG CONFIG: $1 +_LT_LIBTOOL_TAG_VARS(_LT_TAG) +# ### END LIBTOOL TAG CONFIG: $1 +_LT_EOF +])dnl /m4_if +], +[m4_if([$1], [], [ + PACKAGE='$PACKAGE' + VERSION='$VERSION' + TIMESTAMP='$TIMESTAMP' + RM='$RM' + ofile='$ofile'], []) +])dnl /_LT_CONFIG_SAVE_COMMANDS +])# _LT_CONFIG + + +# LT_SUPPORTED_TAG(TAG) +# --------------------- +# Trace this macro to discover what tags are supported by the libtool +# --tag option, using: +# autoconf --trace 'LT_SUPPORTED_TAG:$1' +AC_DEFUN([LT_SUPPORTED_TAG], []) + + +# C support is built-in for now +m4_define([_LT_LANG_C_enabled], []) +m4_define([_LT_TAGS], []) + + +# LT_LANG(LANG) +# ------------- +# Enable libtool support for the given language if not already enabled. +AC_DEFUN([LT_LANG], +[AC_BEFORE([$0], [LT_OUTPUT])dnl +m4_case([$1], + [C], [_LT_LANG(C)], + [C++], [_LT_LANG(CXX)], + [Go], [_LT_LANG(GO)], + [Java], [_LT_LANG(GCJ)], + [Fortran 77], [_LT_LANG(F77)], + [Fortran], [_LT_LANG(FC)], + [Windows Resource], [_LT_LANG(RC)], + [m4_ifdef([_LT_LANG_]$1[_CONFIG], + [_LT_LANG($1)], + [m4_fatal([$0: unsupported language: "$1"])])])dnl +])# LT_LANG + + +# _LT_LANG(LANGNAME) +# ------------------ +m4_defun([_LT_LANG], +[m4_ifdef([_LT_LANG_]$1[_enabled], [], + [LT_SUPPORTED_TAG([$1])dnl + m4_append([_LT_TAGS], [$1 ])dnl + m4_define([_LT_LANG_]$1[_enabled], [])dnl + _LT_LANG_$1_CONFIG($1)])dnl +])# _LT_LANG + + +m4_ifndef([AC_PROG_GO], [ +############################################################ +# NOTE: This macro has been submitted for inclusion into # +# GNU Autoconf as AC_PROG_GO. When it is available in # +# a released version of Autoconf we should remove this # +# macro and use it instead. # +############################################################ +m4_defun([AC_PROG_GO], +[AC_LANG_PUSH(Go)dnl +AC_ARG_VAR([GOC], [Go compiler command])dnl +AC_ARG_VAR([GOFLAGS], [Go compiler flags])dnl +_AC_ARG_VAR_LDFLAGS()dnl +AC_CHECK_TOOL(GOC, gccgo) +if test -z "$GOC"; then + if test -n "$ac_tool_prefix"; then + AC_CHECK_PROG(GOC, [${ac_tool_prefix}gccgo], [${ac_tool_prefix}gccgo]) + fi +fi +if test -z "$GOC"; then + AC_CHECK_PROG(GOC, gccgo, gccgo, false) +fi +])#m4_defun +])#m4_ifndef + + +# _LT_LANG_DEFAULT_CONFIG +# ----------------------- +m4_defun([_LT_LANG_DEFAULT_CONFIG], +[AC_PROVIDE_IFELSE([AC_PROG_CXX], + [LT_LANG(CXX)], + [m4_define([AC_PROG_CXX], defn([AC_PROG_CXX])[LT_LANG(CXX)])]) + +AC_PROVIDE_IFELSE([AC_PROG_F77], + [LT_LANG(F77)], + [m4_define([AC_PROG_F77], defn([AC_PROG_F77])[LT_LANG(F77)])]) + +AC_PROVIDE_IFELSE([AC_PROG_FC], + [LT_LANG(FC)], + [m4_define([AC_PROG_FC], defn([AC_PROG_FC])[LT_LANG(FC)])]) + +dnl The call to [A][M_PROG_GCJ] is quoted like that to stop aclocal +dnl pulling things in needlessly. +AC_PROVIDE_IFELSE([AC_PROG_GCJ], + [LT_LANG(GCJ)], + [AC_PROVIDE_IFELSE([A][M_PROG_GCJ], + [LT_LANG(GCJ)], + [AC_PROVIDE_IFELSE([LT_PROG_GCJ], + [LT_LANG(GCJ)], + [m4_ifdef([AC_PROG_GCJ], + [m4_define([AC_PROG_GCJ], defn([AC_PROG_GCJ])[LT_LANG(GCJ)])]) + m4_ifdef([A][M_PROG_GCJ], + [m4_define([A][M_PROG_GCJ], defn([A][M_PROG_GCJ])[LT_LANG(GCJ)])]) + m4_ifdef([LT_PROG_GCJ], + [m4_define([LT_PROG_GCJ], defn([LT_PROG_GCJ])[LT_LANG(GCJ)])])])])]) + +AC_PROVIDE_IFELSE([AC_PROG_GO], + [LT_LANG(GO)], + [m4_define([AC_PROG_GO], defn([AC_PROG_GO])[LT_LANG(GO)])]) + +AC_PROVIDE_IFELSE([LT_PROG_RC], + [LT_LANG(RC)], + [m4_define([LT_PROG_RC], defn([LT_PROG_RC])[LT_LANG(RC)])]) +])# _LT_LANG_DEFAULT_CONFIG + +# Obsolete macros: +AU_DEFUN([AC_LIBTOOL_CXX], [LT_LANG(C++)]) +AU_DEFUN([AC_LIBTOOL_F77], [LT_LANG(Fortran 77)]) +AU_DEFUN([AC_LIBTOOL_FC], [LT_LANG(Fortran)]) +AU_DEFUN([AC_LIBTOOL_GCJ], [LT_LANG(Java)]) +AU_DEFUN([AC_LIBTOOL_RC], [LT_LANG(Windows Resource)]) +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AC_LIBTOOL_CXX], []) +dnl AC_DEFUN([AC_LIBTOOL_F77], []) +dnl AC_DEFUN([AC_LIBTOOL_FC], []) +dnl AC_DEFUN([AC_LIBTOOL_GCJ], []) +dnl AC_DEFUN([AC_LIBTOOL_RC], []) + + +# _LT_TAG_COMPILER +# ---------------- +m4_defun([_LT_TAG_COMPILER], +[AC_REQUIRE([AC_PROG_CC])dnl + +_LT_DECL([LTCC], [CC], [1], [A C compiler])dnl +_LT_DECL([LTCFLAGS], [CFLAGS], [1], [LTCC compiler flags])dnl +_LT_TAGDECL([CC], [compiler], [1], [A language specific compiler])dnl +_LT_TAGDECL([with_gcc], [GCC], [0], [Is the compiler the GNU compiler?])dnl + +# If no C compiler was specified, use CC. +LTCC=${LTCC-"$CC"} + +# If no C compiler flags were specified, use CFLAGS. +LTCFLAGS=${LTCFLAGS-"$CFLAGS"} + +# Allow CC to be a program name with arguments. +compiler=$CC +])# _LT_TAG_COMPILER + + +# _LT_COMPILER_BOILERPLATE +# ------------------------ +# Check for compiler boilerplate output or warnings with +# the simple compiler test code. +m4_defun([_LT_COMPILER_BOILERPLATE], +[m4_require([_LT_DECL_SED])dnl +ac_outfile=conftest.$ac_objext +echo "$lt_simple_compile_test_code" >conftest.$ac_ext +eval "$ac_compile" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err +_lt_compiler_boilerplate=`cat conftest.err` +$RM conftest* +])# _LT_COMPILER_BOILERPLATE + + +# _LT_LINKER_BOILERPLATE +# ---------------------- +# Check for linker boilerplate output or warnings with +# the simple link test code. +m4_defun([_LT_LINKER_BOILERPLATE], +[m4_require([_LT_DECL_SED])dnl +ac_outfile=conftest.$ac_objext +echo "$lt_simple_link_test_code" >conftest.$ac_ext +eval "$ac_link" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err +_lt_linker_boilerplate=`cat conftest.err` +$RM -r conftest* +])# _LT_LINKER_BOILERPLATE + +# _LT_REQUIRED_DARWIN_CHECKS +# ------------------------- +m4_defun_once([_LT_REQUIRED_DARWIN_CHECKS],[ + case $host_os in + rhapsody* | darwin*) + AC_CHECK_TOOL([DSYMUTIL], [dsymutil], [:]) + AC_CHECK_TOOL([NMEDIT], [nmedit], [:]) + AC_CHECK_TOOL([LIPO], [lipo], [:]) + AC_CHECK_TOOL([OTOOL], [otool], [:]) + AC_CHECK_TOOL([OTOOL64], [otool64], [:]) + _LT_DECL([], [DSYMUTIL], [1], + [Tool to manipulate archived DWARF debug symbol files on Mac OS X]) + _LT_DECL([], [NMEDIT], [1], + [Tool to change global to local symbols on Mac OS X]) + _LT_DECL([], [LIPO], [1], + [Tool to manipulate fat objects and archives on Mac OS X]) + _LT_DECL([], [OTOOL], [1], + [ldd/readelf like tool for Mach-O binaries on Mac OS X]) + _LT_DECL([], [OTOOL64], [1], + [ldd/readelf like tool for 64 bit Mach-O binaries on Mac OS X 10.4]) + + AC_CACHE_CHECK([for -single_module linker flag],[lt_cv_apple_cc_single_mod], + [lt_cv_apple_cc_single_mod=no + if test -z "${LT_MULTI_MODULE}"; then + # By default we will add the -single_module flag. You can override + # by either setting the environment variable LT_MULTI_MODULE + # non-empty at configure time, or by adding -multi_module to the + # link flags. + rm -rf libconftest.dylib* + echo "int foo(void){return 1;}" > conftest.c + echo "$LTCC $LTCFLAGS $LDFLAGS -o libconftest.dylib \ +-dynamiclib -Wl,-single_module conftest.c" >&AS_MESSAGE_LOG_FD + $LTCC $LTCFLAGS $LDFLAGS -o libconftest.dylib \ + -dynamiclib -Wl,-single_module conftest.c 2>conftest.err + _lt_result=$? + # If there is a non-empty error log, and "single_module" + # appears in it, assume the flag caused a linker warning + if test -s conftest.err && $GREP single_module conftest.err; then + cat conftest.err >&AS_MESSAGE_LOG_FD + # Otherwise, if the output was created with a 0 exit code from + # the compiler, it worked. + elif test -f libconftest.dylib && test $_lt_result -eq 0; then + lt_cv_apple_cc_single_mod=yes + else + cat conftest.err >&AS_MESSAGE_LOG_FD + fi + rm -rf libconftest.dylib* + rm -f conftest.* + fi]) + + AC_CACHE_CHECK([for -exported_symbols_list linker flag], + [lt_cv_ld_exported_symbols_list], + [lt_cv_ld_exported_symbols_list=no + save_LDFLAGS=$LDFLAGS + echo "_main" > conftest.sym + LDFLAGS="$LDFLAGS -Wl,-exported_symbols_list,conftest.sym" + AC_LINK_IFELSE([AC_LANG_PROGRAM([],[])], + [lt_cv_ld_exported_symbols_list=yes], + [lt_cv_ld_exported_symbols_list=no]) + LDFLAGS="$save_LDFLAGS" + ]) + + AC_CACHE_CHECK([for -force_load linker flag],[lt_cv_ld_force_load], + [lt_cv_ld_force_load=no + cat > conftest.c << _LT_EOF +int forced_loaded() { return 2;} +_LT_EOF + echo "$LTCC $LTCFLAGS -c -o conftest.o conftest.c" >&AS_MESSAGE_LOG_FD + $LTCC $LTCFLAGS -c -o conftest.o conftest.c 2>&AS_MESSAGE_LOG_FD + echo "$AR cru libconftest.a conftest.o" >&AS_MESSAGE_LOG_FD + $AR cru libconftest.a conftest.o 2>&AS_MESSAGE_LOG_FD + echo "$RANLIB libconftest.a" >&AS_MESSAGE_LOG_FD + $RANLIB libconftest.a 2>&AS_MESSAGE_LOG_FD + cat > conftest.c << _LT_EOF +int main() { return 0;} +_LT_EOF + echo "$LTCC $LTCFLAGS $LDFLAGS -o conftest conftest.c -Wl,-force_load,./libconftest.a" >&AS_MESSAGE_LOG_FD + $LTCC $LTCFLAGS $LDFLAGS -o conftest conftest.c -Wl,-force_load,./libconftest.a 2>conftest.err + _lt_result=$? + if test -s conftest.err && $GREP force_load conftest.err; then + cat conftest.err >&AS_MESSAGE_LOG_FD + elif test -f conftest && test $_lt_result -eq 0 && $GREP forced_load conftest >/dev/null 2>&1 ; then + lt_cv_ld_force_load=yes + else + cat conftest.err >&AS_MESSAGE_LOG_FD + fi + rm -f conftest.err libconftest.a conftest conftest.c + rm -rf conftest.dSYM + ]) + case $host_os in + rhapsody* | darwin1.[[012]]) + _lt_dar_allow_undefined='${wl}-undefined ${wl}suppress' ;; + darwin1.*) + _lt_dar_allow_undefined='${wl}-flat_namespace ${wl}-undefined ${wl}suppress' ;; + darwin*) # darwin 5.x on + # if running on 10.5 or later, the deployment target defaults + # to the OS version, if on x86, and 10.4, the deployment + # target defaults to 10.4. Don't you love it? + case ${MACOSX_DEPLOYMENT_TARGET-10.0},$host in + 10.0,*86*-darwin8*|10.0,*-darwin[[91]]*) + _lt_dar_allow_undefined='${wl}-undefined ${wl}dynamic_lookup' ;; + 10.[[012]]*) + _lt_dar_allow_undefined='${wl}-flat_namespace ${wl}-undefined ${wl}suppress' ;; + 10.*) + _lt_dar_allow_undefined='${wl}-undefined ${wl}dynamic_lookup' ;; + esac + ;; + esac + if test "$lt_cv_apple_cc_single_mod" = "yes"; then + _lt_dar_single_mod='$single_module' + fi + if test "$lt_cv_ld_exported_symbols_list" = "yes"; then + _lt_dar_export_syms=' ${wl}-exported_symbols_list,$output_objdir/${libname}-symbols.expsym' + else + _lt_dar_export_syms='~$NMEDIT -s $output_objdir/${libname}-symbols.expsym ${lib}' + fi + if test "$DSYMUTIL" != ":" && test "$lt_cv_ld_force_load" = "no"; then + _lt_dsymutil='~$DSYMUTIL $lib || :' + else + _lt_dsymutil= + fi + ;; + esac +]) + + +# _LT_DARWIN_LINKER_FEATURES([TAG]) +# --------------------------------- +# Checks for linker and compiler features on darwin +m4_defun([_LT_DARWIN_LINKER_FEATURES], +[ + m4_require([_LT_REQUIRED_DARWIN_CHECKS]) + _LT_TAGVAR(archive_cmds_need_lc, $1)=no + _LT_TAGVAR(hardcode_direct, $1)=no + _LT_TAGVAR(hardcode_automatic, $1)=yes + _LT_TAGVAR(hardcode_shlibpath_var, $1)=unsupported + if test "$lt_cv_ld_force_load" = "yes"; then + _LT_TAGVAR(whole_archive_flag_spec, $1)='`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience ${wl}-force_load,$conv\"; done; func_echo_all \"$new_convenience\"`' + m4_case([$1], [F77], [_LT_TAGVAR(compiler_needs_object, $1)=yes], + [FC], [_LT_TAGVAR(compiler_needs_object, $1)=yes]) + else + _LT_TAGVAR(whole_archive_flag_spec, $1)='' + fi + _LT_TAGVAR(link_all_deplibs, $1)=yes + _LT_TAGVAR(allow_undefined_flag, $1)="$_lt_dar_allow_undefined" + case $cc_basename in + ifort*) _lt_dar_can_shared=yes ;; + *) _lt_dar_can_shared=$GCC ;; + esac + if test "$_lt_dar_can_shared" = "yes"; then + output_verbose_link_cmd=func_echo_all + _LT_TAGVAR(archive_cmds, $1)="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod${_lt_dsymutil}" + _LT_TAGVAR(module_cmds, $1)="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dsymutil}" + _LT_TAGVAR(archive_expsym_cmds, $1)="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring ${_lt_dar_single_mod}${_lt_dar_export_syms}${_lt_dsymutil}" + _LT_TAGVAR(module_expsym_cmds, $1)="sed -e 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dar_export_syms}${_lt_dsymutil}" + m4_if([$1], [CXX], +[ if test "$lt_cv_apple_cc_single_mod" != "yes"; then + _LT_TAGVAR(archive_cmds, $1)="\$CC -r -keep_private_externs -nostdlib -o \${lib}-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \${lib}-master.o \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring${_lt_dsymutil}" + _LT_TAGVAR(archive_expsym_cmds, $1)="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -r -keep_private_externs -nostdlib -o \${lib}-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \${lib}-master.o \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring${_lt_dar_export_syms}${_lt_dsymutil}" + fi +],[]) + else + _LT_TAGVAR(ld_shlibs, $1)=no + fi +]) + +# _LT_SYS_MODULE_PATH_AIX([TAGNAME]) +# ---------------------------------- +# Links a minimal program and checks the executable +# for the system default hardcoded library path. In most cases, +# this is /usr/lib:/lib, but when the MPI compilers are used +# the location of the communication and MPI libs are included too. +# If we don't find anything, use the default library path according +# to the aix ld manual. +# Store the results from the different compilers for each TAGNAME. +# Allow to override them for all tags through lt_cv_aix_libpath. +m4_defun([_LT_SYS_MODULE_PATH_AIX], +[m4_require([_LT_DECL_SED])dnl +if test "${lt_cv_aix_libpath+set}" = set; then + aix_libpath=$lt_cv_aix_libpath +else + AC_CACHE_VAL([_LT_TAGVAR([lt_cv_aix_libpath_], [$1])], + [AC_LINK_IFELSE([AC_LANG_PROGRAM],[ + lt_aix_libpath_sed='[ + /Import File Strings/,/^$/ { + /^0/ { + s/^0 *\([^ ]*\) *$/\1/ + p + } + }]' + _LT_TAGVAR([lt_cv_aix_libpath_], [$1])=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` + # Check for a 64-bit object if we didn't find anything. + if test -z "$_LT_TAGVAR([lt_cv_aix_libpath_], [$1])"; then + _LT_TAGVAR([lt_cv_aix_libpath_], [$1])=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` + fi],[]) + if test -z "$_LT_TAGVAR([lt_cv_aix_libpath_], [$1])"; then + _LT_TAGVAR([lt_cv_aix_libpath_], [$1])="/usr/lib:/lib" + fi + ]) + aix_libpath=$_LT_TAGVAR([lt_cv_aix_libpath_], [$1]) +fi +])# _LT_SYS_MODULE_PATH_AIX + + +# _LT_SHELL_INIT(ARG) +# ------------------- +m4_define([_LT_SHELL_INIT], +[m4_divert_text([M4SH-INIT], [$1 +])])# _LT_SHELL_INIT + + + +# _LT_PROG_ECHO_BACKSLASH +# ----------------------- +# Find how we can fake an echo command that does not interpret backslash. +# In particular, with Autoconf 2.60 or later we add some code to the start +# of the generated configure script which will find a shell with a builtin +# printf (which we can use as an echo command). +m4_defun([_LT_PROG_ECHO_BACKSLASH], +[ECHO='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' +ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO +ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO$ECHO + +AC_MSG_CHECKING([how to print strings]) +# Test print first, because it will be a builtin if present. +if test "X`( print -r -- -n ) 2>/dev/null`" = X-n && \ + test "X`print -r -- $ECHO 2>/dev/null`" = "X$ECHO"; then + ECHO='print -r --' +elif test "X`printf %s $ECHO 2>/dev/null`" = "X$ECHO"; then + ECHO='printf %s\n' +else + # Use this function as a fallback that always works. + func_fallback_echo () + { + eval 'cat <<_LTECHO_EOF +$[]1 +_LTECHO_EOF' + } + ECHO='func_fallback_echo' +fi + +# func_echo_all arg... +# Invoke $ECHO with all args, space-separated. +func_echo_all () +{ + $ECHO "$*" +} + +case "$ECHO" in + printf*) AC_MSG_RESULT([printf]) ;; + print*) AC_MSG_RESULT([print -r]) ;; + *) AC_MSG_RESULT([cat]) ;; +esac + +m4_ifdef([_AS_DETECT_SUGGESTED], +[_AS_DETECT_SUGGESTED([ + test -n "${ZSH_VERSION+set}${BASH_VERSION+set}" || ( + ECHO='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' + ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO + ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO$ECHO + PATH=/empty FPATH=/empty; export PATH FPATH + test "X`printf %s $ECHO`" = "X$ECHO" \ + || test "X`print -r -- $ECHO`" = "X$ECHO" )])]) + +_LT_DECL([], [SHELL], [1], [Shell to use when invoking shell scripts]) +_LT_DECL([], [ECHO], [1], [An echo program that protects backslashes]) +])# _LT_PROG_ECHO_BACKSLASH + + +# _LT_WITH_SYSROOT +# ---------------- +AC_DEFUN([_LT_WITH_SYSROOT], +[AC_MSG_CHECKING([for sysroot]) +AC_ARG_WITH([sysroot], +[ --with-sysroot[=DIR] Search for dependent libraries within DIR + (or the compiler's sysroot if not specified).], +[], [with_sysroot=no]) + +dnl lt_sysroot will always be passed unquoted. We quote it here +dnl in case the user passed a directory name. +lt_sysroot= +case ${with_sysroot} in #( + yes) + if test "$GCC" = yes; then + lt_sysroot=`$CC --print-sysroot 2>/dev/null` + fi + ;; #( + /*) + lt_sysroot=`echo "$with_sysroot" | sed -e "$sed_quote_subst"` + ;; #( + no|'') + ;; #( + *) + AC_MSG_RESULT([${with_sysroot}]) + AC_MSG_ERROR([The sysroot must be an absolute path.]) + ;; +esac + + AC_MSG_RESULT([${lt_sysroot:-no}]) +_LT_DECL([], [lt_sysroot], [0], [The root where to search for ]dnl +[dependent libraries, and in which our libraries should be installed.])]) + +# _LT_ENABLE_LOCK +# --------------- +m4_defun([_LT_ENABLE_LOCK], +[AC_ARG_ENABLE([libtool-lock], + [AS_HELP_STRING([--disable-libtool-lock], + [avoid locking (might break parallel builds)])]) +test "x$enable_libtool_lock" != xno && enable_libtool_lock=yes + +# Some flags need to be propagated to the compiler or linker for good +# libtool support. +case $host in +ia64-*-hpux*) + # Find out which ABI we are using. + echo 'int i;' > conftest.$ac_ext + if AC_TRY_EVAL(ac_compile); then + case `/usr/bin/file conftest.$ac_objext` in + *ELF-32*) + HPUX_IA64_MODE="32" + ;; + *ELF-64*) + HPUX_IA64_MODE="64" + ;; + esac + fi + rm -rf conftest* + ;; +*-*-irix6*) + # Find out which ABI we are using. + echo '[#]line '$LINENO' "configure"' > conftest.$ac_ext + if AC_TRY_EVAL(ac_compile); then + if test "$lt_cv_prog_gnu_ld" = yes; then + case `/usr/bin/file conftest.$ac_objext` in + *32-bit*) + LD="${LD-ld} -melf32bsmip" + ;; + *N32*) + LD="${LD-ld} -melf32bmipn32" + ;; + *64-bit*) + LD="${LD-ld} -melf64bmip" + ;; + esac + else + case `/usr/bin/file conftest.$ac_objext` in + *32-bit*) + LD="${LD-ld} -32" + ;; + *N32*) + LD="${LD-ld} -n32" + ;; + *64-bit*) + LD="${LD-ld} -64" + ;; + esac + fi + fi + rm -rf conftest* + ;; + +x86_64-*kfreebsd*-gnu|x86_64-*linux*|powerpc*-*linux*| \ +s390*-*linux*|s390*-*tpf*|sparc*-*linux*) + # Find out which ABI we are using. + echo 'int i;' > conftest.$ac_ext + if AC_TRY_EVAL(ac_compile); then + case `/usr/bin/file conftest.o` in + *32-bit*) + case $host in + x86_64-*kfreebsd*-gnu) + LD="${LD-ld} -m elf_i386_fbsd" + ;; + x86_64-*linux*) + case `/usr/bin/file conftest.o` in + *x86-64*) + LD="${LD-ld} -m elf32_x86_64" + ;; + *) + LD="${LD-ld} -m elf_i386" + ;; + esac + ;; + powerpc64le-*) + LD="${LD-ld} -m elf32lppclinux" + ;; + powerpc64-*) + LD="${LD-ld} -m elf32ppclinux" + ;; + s390x-*linux*) + LD="${LD-ld} -m elf_s390" + ;; + sparc64-*linux*) + LD="${LD-ld} -m elf32_sparc" + ;; + esac + ;; + *64-bit*) + case $host in + x86_64-*kfreebsd*-gnu) + LD="${LD-ld} -m elf_x86_64_fbsd" + ;; + x86_64-*linux*) + LD="${LD-ld} -m elf_x86_64" + ;; + powerpcle-*) + LD="${LD-ld} -m elf64lppc" + ;; + powerpc-*) + LD="${LD-ld} -m elf64ppc" + ;; + s390*-*linux*|s390*-*tpf*) + LD="${LD-ld} -m elf64_s390" + ;; + sparc*-*linux*) + LD="${LD-ld} -m elf64_sparc" + ;; + esac + ;; + esac + fi + rm -rf conftest* + ;; + +*-*-sco3.2v5*) + # On SCO OpenServer 5, we need -belf to get full-featured binaries. + SAVE_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS -belf" + AC_CACHE_CHECK([whether the C compiler needs -belf], lt_cv_cc_needs_belf, + [AC_LANG_PUSH(C) + AC_LINK_IFELSE([AC_LANG_PROGRAM([[]],[[]])],[lt_cv_cc_needs_belf=yes],[lt_cv_cc_needs_belf=no]) + AC_LANG_POP]) + if test x"$lt_cv_cc_needs_belf" != x"yes"; then + # this is probably gcc 2.8.0, egcs 1.0 or newer; no need for -belf + CFLAGS="$SAVE_CFLAGS" + fi + ;; +*-*solaris*) + # Find out which ABI we are using. + echo 'int i;' > conftest.$ac_ext + if AC_TRY_EVAL(ac_compile); then + case `/usr/bin/file conftest.o` in + *64-bit*) + case $lt_cv_prog_gnu_ld in + yes*) + case $host in + i?86-*-solaris*) + LD="${LD-ld} -m elf_x86_64" + ;; + sparc*-*-solaris*) + LD="${LD-ld} -m elf64_sparc" + ;; + esac + # GNU ld 2.21 introduced _sol2 emulations. Use them if available. + if ${LD-ld} -V | grep _sol2 >/dev/null 2>&1; then + LD="${LD-ld}_sol2" + fi + ;; + *) + if ${LD-ld} -64 -r -o conftest2.o conftest.o >/dev/null 2>&1; then + LD="${LD-ld} -64" + fi + ;; + esac + ;; + esac + fi + rm -rf conftest* + ;; +esac + +need_locks="$enable_libtool_lock" +])# _LT_ENABLE_LOCK + + +# _LT_PROG_AR +# ----------- +m4_defun([_LT_PROG_AR], +[AC_CHECK_TOOLS(AR, [ar], false) +: ${AR=ar} +: ${AR_FLAGS=cru} +_LT_DECL([], [AR], [1], [The archiver]) +_LT_DECL([], [AR_FLAGS], [1], [Flags to create an archive]) + +AC_CACHE_CHECK([for archiver @FILE support], [lt_cv_ar_at_file], + [lt_cv_ar_at_file=no + AC_COMPILE_IFELSE([AC_LANG_PROGRAM], + [echo conftest.$ac_objext > conftest.lst + lt_ar_try='$AR $AR_FLAGS libconftest.a @conftest.lst >&AS_MESSAGE_LOG_FD' + AC_TRY_EVAL([lt_ar_try]) + if test "$ac_status" -eq 0; then + # Ensure the archiver fails upon bogus file names. + rm -f conftest.$ac_objext libconftest.a + AC_TRY_EVAL([lt_ar_try]) + if test "$ac_status" -ne 0; then + lt_cv_ar_at_file=@ + fi + fi + rm -f conftest.* libconftest.a + ]) + ]) + +if test "x$lt_cv_ar_at_file" = xno; then + archiver_list_spec= +else + archiver_list_spec=$lt_cv_ar_at_file +fi +_LT_DECL([], [archiver_list_spec], [1], + [How to feed a file listing to the archiver]) +])# _LT_PROG_AR + + +# _LT_CMD_OLD_ARCHIVE +# ------------------- +m4_defun([_LT_CMD_OLD_ARCHIVE], +[_LT_PROG_AR + +AC_CHECK_TOOL(STRIP, strip, :) +test -z "$STRIP" && STRIP=: +_LT_DECL([], [STRIP], [1], [A symbol stripping program]) + +AC_CHECK_TOOL(RANLIB, ranlib, :) +test -z "$RANLIB" && RANLIB=: +_LT_DECL([], [RANLIB], [1], + [Commands used to install an old-style archive]) + +# Determine commands to create old-style static archives. +old_archive_cmds='$AR $AR_FLAGS $oldlib$oldobjs' +old_postinstall_cmds='chmod 644 $oldlib' +old_postuninstall_cmds= + +if test -n "$RANLIB"; then + case $host_os in + openbsd*) + old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB -t \$tool_oldlib" + ;; + *) + old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB \$tool_oldlib" + ;; + esac + old_archive_cmds="$old_archive_cmds~\$RANLIB \$tool_oldlib" +fi + +case $host_os in + darwin*) + lock_old_archive_extraction=yes ;; + *) + lock_old_archive_extraction=no ;; +esac +_LT_DECL([], [old_postinstall_cmds], [2]) +_LT_DECL([], [old_postuninstall_cmds], [2]) +_LT_TAGDECL([], [old_archive_cmds], [2], + [Commands used to build an old-style archive]) +_LT_DECL([], [lock_old_archive_extraction], [0], + [Whether to use a lock for old archive extraction]) +])# _LT_CMD_OLD_ARCHIVE + + +# _LT_COMPILER_OPTION(MESSAGE, VARIABLE-NAME, FLAGS, +# [OUTPUT-FILE], [ACTION-SUCCESS], [ACTION-FAILURE]) +# ---------------------------------------------------------------- +# Check whether the given compiler option works +AC_DEFUN([_LT_COMPILER_OPTION], +[m4_require([_LT_FILEUTILS_DEFAULTS])dnl +m4_require([_LT_DECL_SED])dnl +AC_CACHE_CHECK([$1], [$2], + [$2=no + m4_if([$4], , [ac_outfile=conftest.$ac_objext], [ac_outfile=$4]) + echo "$lt_simple_compile_test_code" > conftest.$ac_ext + lt_compiler_flag="$3" + # Insert the option either (1) after the last *FLAGS variable, or + # (2) before a word containing "conftest.", or (3) at the end. + # Note that $ac_compile itself does not contain backslashes and begins + # with a dollar sign (not a hyphen), so the echo should work correctly. + # The option is referenced via a variable to avoid confusing sed. + lt_compile=`echo "$ac_compile" | $SED \ + -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ + -e 's: [[^ ]]*conftest\.: $lt_compiler_flag&:; t' \ + -e 's:$: $lt_compiler_flag:'` + (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&AS_MESSAGE_LOG_FD) + (eval "$lt_compile" 2>conftest.err) + ac_status=$? + cat conftest.err >&AS_MESSAGE_LOG_FD + echo "$as_me:$LINENO: \$? = $ac_status" >&AS_MESSAGE_LOG_FD + if (exit $ac_status) && test -s "$ac_outfile"; then + # The compiler can only warn and ignore the option if not recognized + # So say no if there are warnings other than the usual output. + $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' >conftest.exp + $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 + if test ! -s conftest.er2 || diff conftest.exp conftest.er2 >/dev/null; then + $2=yes + fi + fi + $RM conftest* +]) + +if test x"[$]$2" = xyes; then + m4_if([$5], , :, [$5]) +else + m4_if([$6], , :, [$6]) +fi +])# _LT_COMPILER_OPTION + +# Old name: +AU_ALIAS([AC_LIBTOOL_COMPILER_OPTION], [_LT_COMPILER_OPTION]) +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AC_LIBTOOL_COMPILER_OPTION], []) + + +# _LT_LINKER_OPTION(MESSAGE, VARIABLE-NAME, FLAGS, +# [ACTION-SUCCESS], [ACTION-FAILURE]) +# ---------------------------------------------------- +# Check whether the given linker option works +AC_DEFUN([_LT_LINKER_OPTION], +[m4_require([_LT_FILEUTILS_DEFAULTS])dnl +m4_require([_LT_DECL_SED])dnl +AC_CACHE_CHECK([$1], [$2], + [$2=no + save_LDFLAGS="$LDFLAGS" + LDFLAGS="$LDFLAGS $3" + echo "$lt_simple_link_test_code" > conftest.$ac_ext + if (eval $ac_link 2>conftest.err) && test -s conftest$ac_exeext; then + # The linker can only warn and ignore the option if not recognized + # So say no if there are warnings + if test -s conftest.err; then + # Append any errors to the config.log. + cat conftest.err 1>&AS_MESSAGE_LOG_FD + $ECHO "$_lt_linker_boilerplate" | $SED '/^$/d' > conftest.exp + $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 + if diff conftest.exp conftest.er2 >/dev/null; then + $2=yes + fi + else + $2=yes + fi + fi + $RM -r conftest* + LDFLAGS="$save_LDFLAGS" +]) + +if test x"[$]$2" = xyes; then + m4_if([$4], , :, [$4]) +else + m4_if([$5], , :, [$5]) +fi +])# _LT_LINKER_OPTION + +# Old name: +AU_ALIAS([AC_LIBTOOL_LINKER_OPTION], [_LT_LINKER_OPTION]) +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AC_LIBTOOL_LINKER_OPTION], []) + + +# LT_CMD_MAX_LEN +#--------------- +AC_DEFUN([LT_CMD_MAX_LEN], +[AC_REQUIRE([AC_CANONICAL_HOST])dnl +# find the maximum length of command line arguments +AC_MSG_CHECKING([the maximum length of command line arguments]) +AC_CACHE_VAL([lt_cv_sys_max_cmd_len], [dnl + i=0 + teststring="ABCD" + + case $build_os in + msdosdjgpp*) + # On DJGPP, this test can blow up pretty badly due to problems in libc + # (any single argument exceeding 2000 bytes causes a buffer overrun + # during glob expansion). Even if it were fixed, the result of this + # check would be larger than it should be. + lt_cv_sys_max_cmd_len=12288; # 12K is about right + ;; + + gnu*) + # Under GNU Hurd, this test is not required because there is + # no limit to the length of command line arguments. + # Libtool will interpret -1 as no limit whatsoever + lt_cv_sys_max_cmd_len=-1; + ;; + + cygwin* | mingw* | cegcc*) + # On Win9x/ME, this test blows up -- it succeeds, but takes + # about 5 minutes as the teststring grows exponentially. + # Worse, since 9x/ME are not pre-emptively multitasking, + # you end up with a "frozen" computer, even though with patience + # the test eventually succeeds (with a max line length of 256k). + # Instead, let's just punt: use the minimum linelength reported by + # all of the supported platforms: 8192 (on NT/2K/XP). + lt_cv_sys_max_cmd_len=8192; + ;; + + mint*) + # On MiNT this can take a long time and run out of memory. + lt_cv_sys_max_cmd_len=8192; + ;; + + amigaos*) + # On AmigaOS with pdksh, this test takes hours, literally. + # So we just punt and use a minimum line length of 8192. + lt_cv_sys_max_cmd_len=8192; + ;; + + netbsd* | freebsd* | openbsd* | darwin* | dragonfly*) + # This has been around since 386BSD, at least. Likely further. + if test -x /sbin/sysctl; then + lt_cv_sys_max_cmd_len=`/sbin/sysctl -n kern.argmax` + elif test -x /usr/sbin/sysctl; then + lt_cv_sys_max_cmd_len=`/usr/sbin/sysctl -n kern.argmax` + else + lt_cv_sys_max_cmd_len=65536 # usable default for all BSDs + fi + # And add a safety zone + lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4` + lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3` + ;; + + interix*) + # We know the value 262144 and hardcode it with a safety zone (like BSD) + lt_cv_sys_max_cmd_len=196608 + ;; + + os2*) + # The test takes a long time on OS/2. + lt_cv_sys_max_cmd_len=8192 + ;; + + osf*) + # Dr. Hans Ekkehard Plesser reports seeing a kernel panic running configure + # due to this test when exec_disable_arg_limit is 1 on Tru64. It is not + # nice to cause kernel panics so lets avoid the loop below. + # First set a reasonable default. + lt_cv_sys_max_cmd_len=16384 + # + if test -x /sbin/sysconfig; then + case `/sbin/sysconfig -q proc exec_disable_arg_limit` in + *1*) lt_cv_sys_max_cmd_len=-1 ;; + esac + fi + ;; + sco3.2v5*) + lt_cv_sys_max_cmd_len=102400 + ;; + sysv5* | sco5v6* | sysv4.2uw2*) + kargmax=`grep ARG_MAX /etc/conf/cf.d/stune 2>/dev/null` + if test -n "$kargmax"; then + lt_cv_sys_max_cmd_len=`echo $kargmax | sed 's/.*[[ ]]//'` + else + lt_cv_sys_max_cmd_len=32768 + fi + ;; + *) + lt_cv_sys_max_cmd_len=`(getconf ARG_MAX) 2> /dev/null` + if test -n "$lt_cv_sys_max_cmd_len" && \ + test undefined != "$lt_cv_sys_max_cmd_len"; then + lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4` + lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3` + else + # Make teststring a little bigger before we do anything with it. + # a 1K string should be a reasonable start. + for i in 1 2 3 4 5 6 7 8 ; do + teststring=$teststring$teststring + done + SHELL=${SHELL-${CONFIG_SHELL-/bin/sh}} + # If test is not a shell built-in, we'll probably end up computing a + # maximum length that is only half of the actual maximum length, but + # we can't tell. + while { test "X"`env echo "$teststring$teststring" 2>/dev/null` \ + = "X$teststring$teststring"; } >/dev/null 2>&1 && + test $i != 17 # 1/2 MB should be enough + do + i=`expr $i + 1` + teststring=$teststring$teststring + done + # Only check the string length outside the loop. + lt_cv_sys_max_cmd_len=`expr "X$teststring" : ".*" 2>&1` + teststring= + # Add a significant safety factor because C++ compilers can tack on + # massive amounts of additional arguments before passing them to the + # linker. It appears as though 1/2 is a usable value. + lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 2` + fi + ;; + esac +]) +if test -n $lt_cv_sys_max_cmd_len ; then + AC_MSG_RESULT($lt_cv_sys_max_cmd_len) +else + AC_MSG_RESULT(none) +fi +max_cmd_len=$lt_cv_sys_max_cmd_len +_LT_DECL([], [max_cmd_len], [0], + [What is the maximum length of a command?]) +])# LT_CMD_MAX_LEN + +# Old name: +AU_ALIAS([AC_LIBTOOL_SYS_MAX_CMD_LEN], [LT_CMD_MAX_LEN]) +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AC_LIBTOOL_SYS_MAX_CMD_LEN], []) + + +# _LT_HEADER_DLFCN +# ---------------- +m4_defun([_LT_HEADER_DLFCN], +[AC_CHECK_HEADERS([dlfcn.h], [], [], [AC_INCLUDES_DEFAULT])dnl +])# _LT_HEADER_DLFCN + + +# _LT_TRY_DLOPEN_SELF (ACTION-IF-TRUE, ACTION-IF-TRUE-W-USCORE, +# ACTION-IF-FALSE, ACTION-IF-CROSS-COMPILING) +# ---------------------------------------------------------------- +m4_defun([_LT_TRY_DLOPEN_SELF], +[m4_require([_LT_HEADER_DLFCN])dnl +if test "$cross_compiling" = yes; then : + [$4] +else + lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 + lt_status=$lt_dlunknown + cat > conftest.$ac_ext <<_LT_EOF +[#line $LINENO "configure" +#include "confdefs.h" + +#if HAVE_DLFCN_H +#include +#endif + +#include + +#ifdef RTLD_GLOBAL +# define LT_DLGLOBAL RTLD_GLOBAL +#else +# ifdef DL_GLOBAL +# define LT_DLGLOBAL DL_GLOBAL +# else +# define LT_DLGLOBAL 0 +# endif +#endif + +/* We may have to define LT_DLLAZY_OR_NOW in the command line if we + find out it does not work in some platform. */ +#ifndef LT_DLLAZY_OR_NOW +# ifdef RTLD_LAZY +# define LT_DLLAZY_OR_NOW RTLD_LAZY +# else +# ifdef DL_LAZY +# define LT_DLLAZY_OR_NOW DL_LAZY +# else +# ifdef RTLD_NOW +# define LT_DLLAZY_OR_NOW RTLD_NOW +# else +# ifdef DL_NOW +# define LT_DLLAZY_OR_NOW DL_NOW +# else +# define LT_DLLAZY_OR_NOW 0 +# endif +# endif +# endif +# endif +#endif + +/* When -fvisbility=hidden is used, assume the code has been annotated + correspondingly for the symbols needed. */ +#if defined(__GNUC__) && (((__GNUC__ == 3) && (__GNUC_MINOR__ >= 3)) || (__GNUC__ > 3)) +int fnord () __attribute__((visibility("default"))); +#endif + +int fnord () { return 42; } +int main () +{ + void *self = dlopen (0, LT_DLGLOBAL|LT_DLLAZY_OR_NOW); + int status = $lt_dlunknown; + + if (self) + { + if (dlsym (self,"fnord")) status = $lt_dlno_uscore; + else + { + if (dlsym( self,"_fnord")) status = $lt_dlneed_uscore; + else puts (dlerror ()); + } + /* dlclose (self); */ + } + else + puts (dlerror ()); + + return status; +}] +_LT_EOF + if AC_TRY_EVAL(ac_link) && test -s conftest${ac_exeext} 2>/dev/null; then + (./conftest; exit; ) >&AS_MESSAGE_LOG_FD 2>/dev/null + lt_status=$? + case x$lt_status in + x$lt_dlno_uscore) $1 ;; + x$lt_dlneed_uscore) $2 ;; + x$lt_dlunknown|x*) $3 ;; + esac + else : + # compilation failed + $3 + fi +fi +rm -fr conftest* +])# _LT_TRY_DLOPEN_SELF + + +# LT_SYS_DLOPEN_SELF +# ------------------ +AC_DEFUN([LT_SYS_DLOPEN_SELF], +[m4_require([_LT_HEADER_DLFCN])dnl +if test "x$enable_dlopen" != xyes; then + enable_dlopen=unknown + enable_dlopen_self=unknown + enable_dlopen_self_static=unknown +else + lt_cv_dlopen=no + lt_cv_dlopen_libs= + + case $host_os in + beos*) + lt_cv_dlopen="load_add_on" + lt_cv_dlopen_libs= + lt_cv_dlopen_self=yes + ;; + + mingw* | pw32* | cegcc*) + lt_cv_dlopen="LoadLibrary" + lt_cv_dlopen_libs= + ;; + + cygwin*) + lt_cv_dlopen="dlopen" + lt_cv_dlopen_libs= + ;; + + darwin*) + # if libdl is installed we need to link against it + AC_CHECK_LIB([dl], [dlopen], + [lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-ldl"],[ + lt_cv_dlopen="dyld" + lt_cv_dlopen_libs= + lt_cv_dlopen_self=yes + ]) + ;; + + *) + AC_CHECK_FUNC([shl_load], + [lt_cv_dlopen="shl_load"], + [AC_CHECK_LIB([dld], [shl_load], + [lt_cv_dlopen="shl_load" lt_cv_dlopen_libs="-ldld"], + [AC_CHECK_FUNC([dlopen], + [lt_cv_dlopen="dlopen"], + [AC_CHECK_LIB([dl], [dlopen], + [lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-ldl"], + [AC_CHECK_LIB([svld], [dlopen], + [lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-lsvld"], + [AC_CHECK_LIB([dld], [dld_link], + [lt_cv_dlopen="dld_link" lt_cv_dlopen_libs="-ldld"]) + ]) + ]) + ]) + ]) + ]) + ;; + esac + + if test "x$lt_cv_dlopen" != xno; then + enable_dlopen=yes + else + enable_dlopen=no + fi + + case $lt_cv_dlopen in + dlopen) + save_CPPFLAGS="$CPPFLAGS" + test "x$ac_cv_header_dlfcn_h" = xyes && CPPFLAGS="$CPPFLAGS -DHAVE_DLFCN_H" + + save_LDFLAGS="$LDFLAGS" + wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $export_dynamic_flag_spec\" + + save_LIBS="$LIBS" + LIBS="$lt_cv_dlopen_libs $LIBS" + + AC_CACHE_CHECK([whether a program can dlopen itself], + lt_cv_dlopen_self, [dnl + _LT_TRY_DLOPEN_SELF( + lt_cv_dlopen_self=yes, lt_cv_dlopen_self=yes, + lt_cv_dlopen_self=no, lt_cv_dlopen_self=cross) + ]) + + if test "x$lt_cv_dlopen_self" = xyes; then + wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $lt_prog_compiler_static\" + AC_CACHE_CHECK([whether a statically linked program can dlopen itself], + lt_cv_dlopen_self_static, [dnl + _LT_TRY_DLOPEN_SELF( + lt_cv_dlopen_self_static=yes, lt_cv_dlopen_self_static=yes, + lt_cv_dlopen_self_static=no, lt_cv_dlopen_self_static=cross) + ]) + fi + + CPPFLAGS="$save_CPPFLAGS" + LDFLAGS="$save_LDFLAGS" + LIBS="$save_LIBS" + ;; + esac + + case $lt_cv_dlopen_self in + yes|no) enable_dlopen_self=$lt_cv_dlopen_self ;; + *) enable_dlopen_self=unknown ;; + esac + + case $lt_cv_dlopen_self_static in + yes|no) enable_dlopen_self_static=$lt_cv_dlopen_self_static ;; + *) enable_dlopen_self_static=unknown ;; + esac +fi +_LT_DECL([dlopen_support], [enable_dlopen], [0], + [Whether dlopen is supported]) +_LT_DECL([dlopen_self], [enable_dlopen_self], [0], + [Whether dlopen of programs is supported]) +_LT_DECL([dlopen_self_static], [enable_dlopen_self_static], [0], + [Whether dlopen of statically linked programs is supported]) +])# LT_SYS_DLOPEN_SELF + +# Old name: +AU_ALIAS([AC_LIBTOOL_DLOPEN_SELF], [LT_SYS_DLOPEN_SELF]) +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AC_LIBTOOL_DLOPEN_SELF], []) + + +# _LT_COMPILER_C_O([TAGNAME]) +# --------------------------- +# Check to see if options -c and -o are simultaneously supported by compiler. +# This macro does not hard code the compiler like AC_PROG_CC_C_O. +m4_defun([_LT_COMPILER_C_O], +[m4_require([_LT_DECL_SED])dnl +m4_require([_LT_FILEUTILS_DEFAULTS])dnl +m4_require([_LT_TAG_COMPILER])dnl +AC_CACHE_CHECK([if $compiler supports -c -o file.$ac_objext], + [_LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)], + [_LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)=no + $RM -r conftest 2>/dev/null + mkdir conftest + cd conftest + mkdir out + echo "$lt_simple_compile_test_code" > conftest.$ac_ext + + lt_compiler_flag="-o out/conftest2.$ac_objext" + # Insert the option either (1) after the last *FLAGS variable, or + # (2) before a word containing "conftest.", or (3) at the end. + # Note that $ac_compile itself does not contain backslashes and begins + # with a dollar sign (not a hyphen), so the echo should work correctly. + lt_compile=`echo "$ac_compile" | $SED \ + -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ + -e 's: [[^ ]]*conftest\.: $lt_compiler_flag&:; t' \ + -e 's:$: $lt_compiler_flag:'` + (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&AS_MESSAGE_LOG_FD) + (eval "$lt_compile" 2>out/conftest.err) + ac_status=$? + cat out/conftest.err >&AS_MESSAGE_LOG_FD + echo "$as_me:$LINENO: \$? = $ac_status" >&AS_MESSAGE_LOG_FD + if (exit $ac_status) && test -s out/conftest2.$ac_objext + then + # The compiler can only warn and ignore the option if not recognized + # So say no if there are warnings + $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' > out/conftest.exp + $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2 + if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then + _LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)=yes + fi + fi + chmod u+w . 2>&AS_MESSAGE_LOG_FD + $RM conftest* + # SGI C++ compiler will create directory out/ii_files/ for + # template instantiation + test -d out/ii_files && $RM out/ii_files/* && rmdir out/ii_files + $RM out/* && rmdir out + cd .. + $RM -r conftest + $RM conftest* +]) +_LT_TAGDECL([compiler_c_o], [lt_cv_prog_compiler_c_o], [1], + [Does compiler simultaneously support -c and -o options?]) +])# _LT_COMPILER_C_O + + +# _LT_COMPILER_FILE_LOCKS([TAGNAME]) +# ---------------------------------- +# Check to see if we can do hard links to lock some files if needed +m4_defun([_LT_COMPILER_FILE_LOCKS], +[m4_require([_LT_ENABLE_LOCK])dnl +m4_require([_LT_FILEUTILS_DEFAULTS])dnl +_LT_COMPILER_C_O([$1]) + +hard_links="nottested" +if test "$_LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)" = no && test "$need_locks" != no; then + # do not overwrite the value of need_locks provided by the user + AC_MSG_CHECKING([if we can lock with hard links]) + hard_links=yes + $RM conftest* + ln conftest.a conftest.b 2>/dev/null && hard_links=no + touch conftest.a + ln conftest.a conftest.b 2>&5 || hard_links=no + ln conftest.a conftest.b 2>/dev/null && hard_links=no + AC_MSG_RESULT([$hard_links]) + if test "$hard_links" = no; then + AC_MSG_WARN([`$CC' does not support `-c -o', so `make -j' may be unsafe]) + need_locks=warn + fi +else + need_locks=no +fi +_LT_DECL([], [need_locks], [1], [Must we lock files when doing compilation?]) +])# _LT_COMPILER_FILE_LOCKS + + +# _LT_CHECK_OBJDIR +# ---------------- +m4_defun([_LT_CHECK_OBJDIR], +[AC_CACHE_CHECK([for objdir], [lt_cv_objdir], +[rm -f .libs 2>/dev/null +mkdir .libs 2>/dev/null +if test -d .libs; then + lt_cv_objdir=.libs +else + # MS-DOS does not allow filenames that begin with a dot. + lt_cv_objdir=_libs +fi +rmdir .libs 2>/dev/null]) +objdir=$lt_cv_objdir +_LT_DECL([], [objdir], [0], + [The name of the directory that contains temporary libtool files])dnl +m4_pattern_allow([LT_OBJDIR])dnl +AC_DEFINE_UNQUOTED(LT_OBJDIR, "$lt_cv_objdir/", + [Define to the sub-directory in which libtool stores uninstalled libraries.]) +])# _LT_CHECK_OBJDIR + + +# _LT_LINKER_HARDCODE_LIBPATH([TAGNAME]) +# -------------------------------------- +# Check hardcoding attributes. +m4_defun([_LT_LINKER_HARDCODE_LIBPATH], +[AC_MSG_CHECKING([how to hardcode library paths into programs]) +_LT_TAGVAR(hardcode_action, $1)= +if test -n "$_LT_TAGVAR(hardcode_libdir_flag_spec, $1)" || + test -n "$_LT_TAGVAR(runpath_var, $1)" || + test "X$_LT_TAGVAR(hardcode_automatic, $1)" = "Xyes" ; then + + # We can hardcode non-existent directories. + if test "$_LT_TAGVAR(hardcode_direct, $1)" != no && + # If the only mechanism to avoid hardcoding is shlibpath_var, we + # have to relink, otherwise we might link with an installed library + # when we should be linking with a yet-to-be-installed one + ## test "$_LT_TAGVAR(hardcode_shlibpath_var, $1)" != no && + test "$_LT_TAGVAR(hardcode_minus_L, $1)" != no; then + # Linking always hardcodes the temporary library directory. + _LT_TAGVAR(hardcode_action, $1)=relink + else + # We can link without hardcoding, and we can hardcode nonexisting dirs. + _LT_TAGVAR(hardcode_action, $1)=immediate + fi +else + # We cannot hardcode anything, or else we can only hardcode existing + # directories. + _LT_TAGVAR(hardcode_action, $1)=unsupported +fi +AC_MSG_RESULT([$_LT_TAGVAR(hardcode_action, $1)]) + +if test "$_LT_TAGVAR(hardcode_action, $1)" = relink || + test "$_LT_TAGVAR(inherit_rpath, $1)" = yes; then + # Fast installation is not supported + enable_fast_install=no +elif test "$shlibpath_overrides_runpath" = yes || + test "$enable_shared" = no; then + # Fast installation is not necessary + enable_fast_install=needless +fi +_LT_TAGDECL([], [hardcode_action], [0], + [How to hardcode a shared library path into an executable]) +])# _LT_LINKER_HARDCODE_LIBPATH + + +# _LT_CMD_STRIPLIB +# ---------------- +m4_defun([_LT_CMD_STRIPLIB], +[m4_require([_LT_DECL_EGREP]) +striplib= +old_striplib= +AC_MSG_CHECKING([whether stripping libraries is possible]) +if test -n "$STRIP" && $STRIP -V 2>&1 | $GREP "GNU strip" >/dev/null; then + test -z "$old_striplib" && old_striplib="$STRIP --strip-debug" + test -z "$striplib" && striplib="$STRIP --strip-unneeded" + AC_MSG_RESULT([yes]) +else +# FIXME - insert some real tests, host_os isn't really good enough + case $host_os in + darwin*) + if test -n "$STRIP" ; then + striplib="$STRIP -x" + old_striplib="$STRIP -S" + AC_MSG_RESULT([yes]) + else + AC_MSG_RESULT([no]) + fi + ;; + *) + AC_MSG_RESULT([no]) + ;; + esac +fi +_LT_DECL([], [old_striplib], [1], [Commands to strip libraries]) +_LT_DECL([], [striplib], [1]) +])# _LT_CMD_STRIPLIB + + +# _LT_SYS_DYNAMIC_LINKER([TAG]) +# ----------------------------- +# PORTME Fill in your ld.so characteristics +m4_defun([_LT_SYS_DYNAMIC_LINKER], +[AC_REQUIRE([AC_CANONICAL_HOST])dnl +m4_require([_LT_DECL_EGREP])dnl +m4_require([_LT_FILEUTILS_DEFAULTS])dnl +m4_require([_LT_DECL_OBJDUMP])dnl +m4_require([_LT_DECL_SED])dnl +m4_require([_LT_CHECK_SHELL_FEATURES])dnl +AC_MSG_CHECKING([dynamic linker characteristics]) +m4_if([$1], + [], [ +if test "$GCC" = yes; then + case $host_os in + darwin*) lt_awk_arg="/^libraries:/,/LR/" ;; + *) lt_awk_arg="/^libraries:/" ;; + esac + case $host_os in + mingw* | cegcc*) lt_sed_strip_eq="s,=\([[A-Za-z]]:\),\1,g" ;; + *) lt_sed_strip_eq="s,=/,/,g" ;; + esac + lt_search_path_spec=`$CC -print-search-dirs | awk $lt_awk_arg | $SED -e "s/^libraries://" -e $lt_sed_strip_eq` + case $lt_search_path_spec in + *\;*) + # if the path contains ";" then we assume it to be the separator + # otherwise default to the standard path separator (i.e. ":") - it is + # assumed that no part of a normal pathname contains ";" but that should + # okay in the real world where ";" in dirpaths is itself problematic. + lt_search_path_spec=`$ECHO "$lt_search_path_spec" | $SED 's/;/ /g'` + ;; + *) + lt_search_path_spec=`$ECHO "$lt_search_path_spec" | $SED "s/$PATH_SEPARATOR/ /g"` + ;; + esac + # Ok, now we have the path, separated by spaces, we can step through it + # and add multilib dir if necessary. + lt_tmp_lt_search_path_spec= + lt_multi_os_dir=`$CC $CPPFLAGS $CFLAGS $LDFLAGS -print-multi-os-directory 2>/dev/null` + for lt_sys_path in $lt_search_path_spec; do + if test -d "$lt_sys_path/$lt_multi_os_dir"; then + lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec $lt_sys_path/$lt_multi_os_dir" + else + test -d "$lt_sys_path" && \ + lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec $lt_sys_path" + fi + done + lt_search_path_spec=`$ECHO "$lt_tmp_lt_search_path_spec" | awk ' +BEGIN {RS=" "; FS="/|\n";} { + lt_foo=""; + lt_count=0; + for (lt_i = NF; lt_i > 0; lt_i--) { + if ($lt_i != "" && $lt_i != ".") { + if ($lt_i == "..") { + lt_count++; + } else { + if (lt_count == 0) { + lt_foo="/" $lt_i lt_foo; + } else { + lt_count--; + } + } + } + } + if (lt_foo != "") { lt_freq[[lt_foo]]++; } + if (lt_freq[[lt_foo]] == 1) { print lt_foo; } +}'` + # AWK program above erroneously prepends '/' to C:/dos/paths + # for these hosts. + case $host_os in + mingw* | cegcc*) lt_search_path_spec=`$ECHO "$lt_search_path_spec" |\ + $SED 's,/\([[A-Za-z]]:\),\1,g'` ;; + esac + sys_lib_search_path_spec=`$ECHO "$lt_search_path_spec" | $lt_NL2SP` +else + sys_lib_search_path_spec="/lib /usr/lib /usr/local/lib" +fi]) +library_names_spec= +libname_spec='lib$name' +soname_spec= +shrext_cmds=".so" +postinstall_cmds= +postuninstall_cmds= +finish_cmds= +finish_eval= +shlibpath_var= +shlibpath_overrides_runpath=unknown +version_type=none +dynamic_linker="$host_os ld.so" +sys_lib_dlsearch_path_spec="/lib /usr/lib" +need_lib_prefix=unknown +hardcode_into_libs=no + +# when you set need_version to no, make sure it does not cause -set_version +# flags to be left without arguments +need_version=unknown + +case $host_os in +aix3*) + version_type=linux # correct to gnu/linux during the next big refactor + library_names_spec='${libname}${release}${shared_ext}$versuffix $libname.a' + shlibpath_var=LIBPATH + + # AIX 3 has no versioning support, so we append a major version to the name. + soname_spec='${libname}${release}${shared_ext}$major' + ;; + +aix[[4-9]]*) + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + hardcode_into_libs=yes + if test "$host_cpu" = ia64; then + # AIX 5 supports IA64 + library_names_spec='${libname}${release}${shared_ext}$major ${libname}${release}${shared_ext}$versuffix $libname${shared_ext}' + shlibpath_var=LD_LIBRARY_PATH + else + # With GCC up to 2.95.x, collect2 would create an import file + # for dependence libraries. The import file would start with + # the line `#! .'. This would cause the generated library to + # depend on `.', always an invalid library. This was fixed in + # development snapshots of GCC prior to 3.0. + case $host_os in + aix4 | aix4.[[01]] | aix4.[[01]].*) + if { echo '#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 97)' + echo ' yes ' + echo '#endif'; } | ${CC} -E - | $GREP yes > /dev/null; then + : + else + can_build_shared=no + fi + ;; + esac + # AIX (on Power*) has no versioning support, so currently we can not hardcode correct + # soname into executable. Probably we can add versioning support to + # collect2, so additional links can be useful in future. + if test "$aix_use_runtimelinking" = yes; then + # If using run time linking (on AIX 4.2 or later) use lib.so + # instead of lib.a to let people know that these are not + # typical AIX shared libraries. + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + else + # We preserve .a as extension for shared libraries through AIX4.2 + # and later when we are not doing run time linking. + library_names_spec='${libname}${release}.a $libname.a' + soname_spec='${libname}${release}${shared_ext}$major' + fi + shlibpath_var=LIBPATH + fi + ;; + +amigaos*) + case $host_cpu in + powerpc) + # Since July 2007 AmigaOS4 officially supports .so libraries. + # When compiling the executable, add -use-dynld -Lsobjs: to the compileline. + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + ;; + m68k) + library_names_spec='$libname.ixlibrary $libname.a' + # Create ${libname}_ixlibrary.a entries in /sys/libs. + finish_eval='for lib in `ls $libdir/*.ixlibrary 2>/dev/null`; do libname=`func_echo_all "$lib" | $SED '\''s%^.*/\([[^/]]*\)\.ixlibrary$%\1%'\''`; test $RM /sys/libs/${libname}_ixlibrary.a; $show "cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a"; cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a || exit 1; done' + ;; + esac + ;; + +beos*) + library_names_spec='${libname}${shared_ext}' + dynamic_linker="$host_os ld.so" + shlibpath_var=LIBRARY_PATH + ;; + +bsdi[[45]]*) + version_type=linux # correct to gnu/linux during the next big refactor + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + finish_cmds='PATH="\$PATH:/sbin" ldconfig $libdir' + shlibpath_var=LD_LIBRARY_PATH + sys_lib_search_path_spec="/shlib /usr/lib /usr/X11/lib /usr/contrib/lib /lib /usr/local/lib" + sys_lib_dlsearch_path_spec="/shlib /usr/lib /usr/local/lib" + # the default ld.so.conf also contains /usr/contrib/lib and + # /usr/X11R6/lib (/usr/X11 is a link to /usr/X11R6), but let us allow + # libtool to hard-code these into programs + ;; + +cygwin* | mingw* | pw32* | cegcc*) + version_type=windows + shrext_cmds=".dll" + need_version=no + need_lib_prefix=no + + case $GCC,$cc_basename in + yes,*) + # gcc + library_names_spec='$libname.dll.a' + # DLL is installed to $(libdir)/../bin by postinstall_cmds + postinstall_cmds='base_file=`basename \${file}`~ + dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\${base_file}'\''i; echo \$dlname'\''`~ + dldir=$destdir/`dirname \$dlpath`~ + test -d \$dldir || mkdir -p \$dldir~ + $install_prog $dir/$dlname \$dldir/$dlname~ + chmod a+x \$dldir/$dlname~ + if test -n '\''$stripme'\'' && test -n '\''$striplib'\''; then + eval '\''$striplib \$dldir/$dlname'\'' || exit \$?; + fi' + postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~ + dlpath=$dir/\$dldll~ + $RM \$dlpath' + shlibpath_overrides_runpath=yes + + case $host_os in + cygwin*) + # Cygwin DLLs use 'cyg' prefix rather than 'lib' + soname_spec='`echo ${libname} | sed -e 's/^lib/cyg/'``echo ${release} | $SED -e 's/[[.]]/-/g'`${versuffix}${shared_ext}' +m4_if([$1], [],[ + sys_lib_search_path_spec="$sys_lib_search_path_spec /usr/lib/w32api"]) + ;; + mingw* | cegcc*) + # MinGW DLLs use traditional 'lib' prefix + soname_spec='${libname}`echo ${release} | $SED -e 's/[[.]]/-/g'`${versuffix}${shared_ext}' + ;; + pw32*) + # pw32 DLLs use 'pw' prefix rather than 'lib' + library_names_spec='`echo ${libname} | sed -e 's/^lib/pw/'``echo ${release} | $SED -e 's/[[.]]/-/g'`${versuffix}${shared_ext}' + ;; + esac + dynamic_linker='Win32 ld.exe' + ;; + + *,cl*) + # Native MSVC + libname_spec='$name' + soname_spec='${libname}`echo ${release} | $SED -e 's/[[.]]/-/g'`${versuffix}${shared_ext}' + library_names_spec='${libname}.dll.lib' + + case $build_os in + mingw*) + sys_lib_search_path_spec= + lt_save_ifs=$IFS + IFS=';' + for lt_path in $LIB + do + IFS=$lt_save_ifs + # Let DOS variable expansion print the short 8.3 style file name. + lt_path=`cd "$lt_path" 2>/dev/null && cmd //C "for %i in (".") do @echo %~si"` + sys_lib_search_path_spec="$sys_lib_search_path_spec $lt_path" + done + IFS=$lt_save_ifs + # Convert to MSYS style. + sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | sed -e 's|\\\\|/|g' -e 's| \\([[a-zA-Z]]\\):| /\\1|g' -e 's|^ ||'` + ;; + cygwin*) + # Convert to unix form, then to dos form, then back to unix form + # but this time dos style (no spaces!) so that the unix form looks + # like /cygdrive/c/PROGRA~1:/cygdr... + sys_lib_search_path_spec=`cygpath --path --unix "$LIB"` + sys_lib_search_path_spec=`cygpath --path --dos "$sys_lib_search_path_spec" 2>/dev/null` + sys_lib_search_path_spec=`cygpath --path --unix "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"` + ;; + *) + sys_lib_search_path_spec="$LIB" + if $ECHO "$sys_lib_search_path_spec" | [$GREP ';[c-zC-Z]:/' >/dev/null]; then + # It is most probably a Windows format PATH. + sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e 's/;/ /g'` + else + sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"` + fi + # FIXME: find the short name or the path components, as spaces are + # common. (e.g. "Program Files" -> "PROGRA~1") + ;; + esac + + # DLL is installed to $(libdir)/../bin by postinstall_cmds + postinstall_cmds='base_file=`basename \${file}`~ + dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\${base_file}'\''i; echo \$dlname'\''`~ + dldir=$destdir/`dirname \$dlpath`~ + test -d \$dldir || mkdir -p \$dldir~ + $install_prog $dir/$dlname \$dldir/$dlname' + postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~ + dlpath=$dir/\$dldll~ + $RM \$dlpath' + shlibpath_overrides_runpath=yes + dynamic_linker='Win32 link.exe' + ;; + + *) + # Assume MSVC wrapper + library_names_spec='${libname}`echo ${release} | $SED -e 's/[[.]]/-/g'`${versuffix}${shared_ext} $libname.lib' + dynamic_linker='Win32 ld.exe' + ;; + esac + # FIXME: first we should search . and the directory the executable is in + shlibpath_var=PATH + ;; + +darwin* | rhapsody*) + dynamic_linker="$host_os dyld" + version_type=darwin + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${major}$shared_ext ${libname}$shared_ext' + soname_spec='${libname}${release}${major}$shared_ext' + shlibpath_overrides_runpath=yes + shlibpath_var=DYLD_LIBRARY_PATH + shrext_cmds='`test .$module = .yes && echo .so || echo .dylib`' +m4_if([$1], [],[ + sys_lib_search_path_spec="$sys_lib_search_path_spec /usr/local/lib"]) + sys_lib_dlsearch_path_spec='/usr/local/lib /lib /usr/lib' + ;; + +dgux*) + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname$shared_ext' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + ;; + +freebsd* | dragonfly*) + # DragonFly does not have aout. When/if they implement a new + # versioning mechanism, adjust this. + if test -x /usr/bin/objformat; then + objformat=`/usr/bin/objformat` + else + case $host_os in + freebsd[[23]].*) objformat=aout ;; + *) objformat=elf ;; + esac + fi + version_type=freebsd-$objformat + case $version_type in + freebsd-elf*) + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext} $libname${shared_ext}' + need_version=no + need_lib_prefix=no + ;; + freebsd-*) + library_names_spec='${libname}${release}${shared_ext}$versuffix $libname${shared_ext}$versuffix' + need_version=yes + ;; + esac + shlibpath_var=LD_LIBRARY_PATH + case $host_os in + freebsd2.*) + shlibpath_overrides_runpath=yes + ;; + freebsd3.[[01]]* | freebsdelf3.[[01]]*) + shlibpath_overrides_runpath=yes + hardcode_into_libs=yes + ;; + freebsd3.[[2-9]]* | freebsdelf3.[[2-9]]* | \ + freebsd4.[[0-5]] | freebsdelf4.[[0-5]] | freebsd4.1.1 | freebsdelf4.1.1) + shlibpath_overrides_runpath=no + hardcode_into_libs=yes + ;; + *) # from 4.6 on, and DragonFly + shlibpath_overrides_runpath=yes + hardcode_into_libs=yes + ;; + esac + ;; + +haiku*) + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + dynamic_linker="$host_os runtime_loader" + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}${major} ${libname}${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LIBRARY_PATH + shlibpath_overrides_runpath=yes + sys_lib_dlsearch_path_spec='/boot/home/config/lib /boot/common/lib /boot/system/lib' + hardcode_into_libs=yes + ;; + +hpux9* | hpux10* | hpux11*) + # Give a soname corresponding to the major version so that dld.sl refuses to + # link against other versions. + version_type=sunos + need_lib_prefix=no + need_version=no + case $host_cpu in + ia64*) + shrext_cmds='.so' + hardcode_into_libs=yes + dynamic_linker="$host_os dld.so" + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes # Unless +noenvvar is specified. + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + if test "X$HPUX_IA64_MODE" = X32; then + sys_lib_search_path_spec="/usr/lib/hpux32 /usr/local/lib/hpux32 /usr/local/lib" + else + sys_lib_search_path_spec="/usr/lib/hpux64 /usr/local/lib/hpux64" + fi + sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec + ;; + hppa*64*) + shrext_cmds='.sl' + hardcode_into_libs=yes + dynamic_linker="$host_os dld.sl" + shlibpath_var=LD_LIBRARY_PATH # How should we handle SHLIB_PATH + shlibpath_overrides_runpath=yes # Unless +noenvvar is specified. + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + sys_lib_search_path_spec="/usr/lib/pa20_64 /usr/ccs/lib/pa20_64" + sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec + ;; + *) + shrext_cmds='.sl' + dynamic_linker="$host_os dld.sl" + shlibpath_var=SHLIB_PATH + shlibpath_overrides_runpath=no # +s is required to enable SHLIB_PATH + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + ;; + esac + # HP-UX runs *really* slowly unless shared libraries are mode 555, ... + postinstall_cmds='chmod 555 $lib' + # or fails outright, so override atomically: + install_override_mode=555 + ;; + +interix[[3-9]]*) + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + dynamic_linker='Interix 3.x ld.so.1 (PE, like ELF)' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=no + hardcode_into_libs=yes + ;; + +irix5* | irix6* | nonstopux*) + case $host_os in + nonstopux*) version_type=nonstopux ;; + *) + if test "$lt_cv_prog_gnu_ld" = yes; then + version_type=linux # correct to gnu/linux during the next big refactor + else + version_type=irix + fi ;; + esac + need_lib_prefix=no + need_version=no + soname_spec='${libname}${release}${shared_ext}$major' + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${release}${shared_ext} $libname${shared_ext}' + case $host_os in + irix5* | nonstopux*) + libsuff= shlibsuff= + ;; + *) + case $LD in # libtool.m4 will add one of these switches to LD + *-32|*"-32 "|*-melf32bsmip|*"-melf32bsmip ") + libsuff= shlibsuff= libmagic=32-bit;; + *-n32|*"-n32 "|*-melf32bmipn32|*"-melf32bmipn32 ") + libsuff=32 shlibsuff=N32 libmagic=N32;; + *-64|*"-64 "|*-melf64bmip|*"-melf64bmip ") + libsuff=64 shlibsuff=64 libmagic=64-bit;; + *) libsuff= shlibsuff= libmagic=never-match;; + esac + ;; + esac + shlibpath_var=LD_LIBRARY${shlibsuff}_PATH + shlibpath_overrides_runpath=no + sys_lib_search_path_spec="/usr/lib${libsuff} /lib${libsuff} /usr/local/lib${libsuff}" + sys_lib_dlsearch_path_spec="/usr/lib${libsuff} /lib${libsuff}" + hardcode_into_libs=yes + ;; + +# No shared lib support for Linux oldld, aout, or coff. +linux*oldld* | linux*aout* | linux*coff*) + dynamic_linker=no + ;; + +# This must be glibc/ELF. +linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + finish_cmds='PATH="\$PATH:/sbin" ldconfig -n $libdir' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=no + + # Some binutils ld are patched to set DT_RUNPATH + AC_CACHE_VAL([lt_cv_shlibpath_overrides_runpath], + [lt_cv_shlibpath_overrides_runpath=no + save_LDFLAGS=$LDFLAGS + save_libdir=$libdir + eval "libdir=/foo; wl=\"$_LT_TAGVAR(lt_prog_compiler_wl, $1)\"; \ + LDFLAGS=\"\$LDFLAGS $_LT_TAGVAR(hardcode_libdir_flag_spec, $1)\"" + AC_LINK_IFELSE([AC_LANG_PROGRAM([],[])], + [AS_IF([ ($OBJDUMP -p conftest$ac_exeext) 2>/dev/null | grep "RUNPATH.*$libdir" >/dev/null], + [lt_cv_shlibpath_overrides_runpath=yes])]) + LDFLAGS=$save_LDFLAGS + libdir=$save_libdir + ]) + shlibpath_overrides_runpath=$lt_cv_shlibpath_overrides_runpath + + # This implies no fast_install, which is unacceptable. + # Some rework will be needed to allow for fast_install + # before this can be enabled. + hardcode_into_libs=yes + + # Append ld.so.conf contents to the search path + if test -f /etc/ld.so.conf; then + lt_ld_extra=`awk '/^include / { system(sprintf("cd /etc; cat %s 2>/dev/null", \[$]2)); skip = 1; } { if (!skip) print \[$]0; skip = 0; }' < /etc/ld.so.conf | $SED -e 's/#.*//;/^[ ]*hwcap[ ]/d;s/[:, ]/ /g;s/=[^=]*$//;s/=[^= ]* / /g;s/"//g;/^$/d' | tr '\n' ' '` + sys_lib_dlsearch_path_spec="/lib /usr/lib $lt_ld_extra" + fi + + # We used to test for /lib/ld.so.1 and disable shared libraries on + # powerpc, because MkLinux only supported shared libraries with the + # GNU dynamic linker. Since this was broken with cross compilers, + # most powerpc-linux boxes support dynamic linking these days and + # people can always --disable-shared, the test was removed, and we + # assume the GNU/Linux dynamic linker is in use. + dynamic_linker='GNU/Linux ld.so' + ;; + +netbsdelf*-gnu) + version_type=linux + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=no + hardcode_into_libs=yes + dynamic_linker='NetBSD ld.elf_so' + ;; + +netbsd*) + version_type=sunos + need_lib_prefix=no + need_version=no + if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix' + finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' + dynamic_linker='NetBSD (a.out) ld.so' + else + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + dynamic_linker='NetBSD ld.elf_so' + fi + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + hardcode_into_libs=yes + ;; + +newsos6) + version_type=linux # correct to gnu/linux during the next big refactor + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + ;; + +*nto* | *qnx*) + version_type=qnx + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=no + hardcode_into_libs=yes + dynamic_linker='ldqnx.so' + ;; + +openbsd*) + version_type=sunos + sys_lib_dlsearch_path_spec="/usr/lib" + need_lib_prefix=no + # Some older versions of OpenBSD (3.3 at least) *do* need versioned libs. + case $host_os in + openbsd3.3 | openbsd3.3.*) need_version=yes ;; + *) need_version=no ;; + esac + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix' + finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' + shlibpath_var=LD_LIBRARY_PATH + if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then + case $host_os in + openbsd2.[[89]] | openbsd2.[[89]].*) + shlibpath_overrides_runpath=no + ;; + *) + shlibpath_overrides_runpath=yes + ;; + esac + else + shlibpath_overrides_runpath=yes + fi + ;; + +os2*) + libname_spec='$name' + shrext_cmds=".dll" + need_lib_prefix=no + library_names_spec='$libname${shared_ext} $libname.a' + dynamic_linker='OS/2 ld.exe' + shlibpath_var=LIBPATH + ;; + +osf3* | osf4* | osf5*) + version_type=osf + need_lib_prefix=no + need_version=no + soname_spec='${libname}${release}${shared_ext}$major' + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + shlibpath_var=LD_LIBRARY_PATH + sys_lib_search_path_spec="/usr/shlib /usr/ccs/lib /usr/lib/cmplrs/cc /usr/lib /usr/local/lib /var/shlib" + sys_lib_dlsearch_path_spec="$sys_lib_search_path_spec" + ;; + +rdos*) + dynamic_linker=no + ;; + +solaris*) + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + hardcode_into_libs=yes + # ldd complains unless libraries are executable + postinstall_cmds='chmod +x $lib' + ;; + +sunos4*) + version_type=sunos + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix' + finish_cmds='PATH="\$PATH:/usr/etc" ldconfig $libdir' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + if test "$with_gnu_ld" = yes; then + need_lib_prefix=no + fi + need_version=yes + ;; + +sysv4 | sysv4.3*) + version_type=linux # correct to gnu/linux during the next big refactor + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + case $host_vendor in + sni) + shlibpath_overrides_runpath=no + need_lib_prefix=no + runpath_var=LD_RUN_PATH + ;; + siemens) + need_lib_prefix=no + ;; + motorola) + need_lib_prefix=no + need_version=no + shlibpath_overrides_runpath=no + sys_lib_search_path_spec='/lib /usr/lib /usr/ccs/lib' + ;; + esac + ;; + +sysv4*MP*) + if test -d /usr/nec ;then + version_type=linux # correct to gnu/linux during the next big refactor + library_names_spec='$libname${shared_ext}.$versuffix $libname${shared_ext}.$major $libname${shared_ext}' + soname_spec='$libname${shared_ext}.$major' + shlibpath_var=LD_LIBRARY_PATH + fi + ;; + +sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*) + version_type=freebsd-elf + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext} $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + hardcode_into_libs=yes + if test "$with_gnu_ld" = yes; then + sys_lib_search_path_spec='/usr/local/lib /usr/gnu/lib /usr/ccs/lib /usr/lib /lib' + else + sys_lib_search_path_spec='/usr/ccs/lib /usr/lib' + case $host_os in + sco3.2v5*) + sys_lib_search_path_spec="$sys_lib_search_path_spec /lib" + ;; + esac + fi + sys_lib_dlsearch_path_spec='/usr/lib' + ;; + +tpf*) + # TPF is a cross-target only. Preferred cross-host = GNU/Linux. + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=no + hardcode_into_libs=yes + ;; + +uts4*) + version_type=linux # correct to gnu/linux during the next big refactor + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + ;; + +*) + dynamic_linker=no + ;; +esac +AC_MSG_RESULT([$dynamic_linker]) +test "$dynamic_linker" = no && can_build_shared=no + +variables_saved_for_relink="PATH $shlibpath_var $runpath_var" +if test "$GCC" = yes; then + variables_saved_for_relink="$variables_saved_for_relink GCC_EXEC_PREFIX COMPILER_PATH LIBRARY_PATH" +fi + +if test "${lt_cv_sys_lib_search_path_spec+set}" = set; then + sys_lib_search_path_spec="$lt_cv_sys_lib_search_path_spec" +fi +if test "${lt_cv_sys_lib_dlsearch_path_spec+set}" = set; then + sys_lib_dlsearch_path_spec="$lt_cv_sys_lib_dlsearch_path_spec" +fi + +_LT_DECL([], [variables_saved_for_relink], [1], + [Variables whose values should be saved in libtool wrapper scripts and + restored at link time]) +_LT_DECL([], [need_lib_prefix], [0], + [Do we need the "lib" prefix for modules?]) +_LT_DECL([], [need_version], [0], [Do we need a version for libraries?]) +_LT_DECL([], [version_type], [0], [Library versioning type]) +_LT_DECL([], [runpath_var], [0], [Shared library runtime path variable]) +_LT_DECL([], [shlibpath_var], [0],[Shared library path variable]) +_LT_DECL([], [shlibpath_overrides_runpath], [0], + [Is shlibpath searched before the hard-coded library search path?]) +_LT_DECL([], [libname_spec], [1], [Format of library name prefix]) +_LT_DECL([], [library_names_spec], [1], + [[List of archive names. First name is the real one, the rest are links. + The last name is the one that the linker finds with -lNAME]]) +_LT_DECL([], [soname_spec], [1], + [[The coded name of the library, if different from the real name]]) +_LT_DECL([], [install_override_mode], [1], + [Permission mode override for installation of shared libraries]) +_LT_DECL([], [postinstall_cmds], [2], + [Command to use after installation of a shared archive]) +_LT_DECL([], [postuninstall_cmds], [2], + [Command to use after uninstallation of a shared archive]) +_LT_DECL([], [finish_cmds], [2], + [Commands used to finish a libtool library installation in a directory]) +_LT_DECL([], [finish_eval], [1], + [[As "finish_cmds", except a single script fragment to be evaled but + not shown]]) +_LT_DECL([], [hardcode_into_libs], [0], + [Whether we should hardcode library paths into libraries]) +_LT_DECL([], [sys_lib_search_path_spec], [2], + [Compile-time system search path for libraries]) +_LT_DECL([], [sys_lib_dlsearch_path_spec], [2], + [Run-time system search path for libraries]) +])# _LT_SYS_DYNAMIC_LINKER + + +# _LT_PATH_TOOL_PREFIX(TOOL) +# -------------------------- +# find a file program which can recognize shared library +AC_DEFUN([_LT_PATH_TOOL_PREFIX], +[m4_require([_LT_DECL_EGREP])dnl +AC_MSG_CHECKING([for $1]) +AC_CACHE_VAL(lt_cv_path_MAGIC_CMD, +[case $MAGIC_CMD in +[[\\/*] | ?:[\\/]*]) + lt_cv_path_MAGIC_CMD="$MAGIC_CMD" # Let the user override the test with a path. + ;; +*) + lt_save_MAGIC_CMD="$MAGIC_CMD" + lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR +dnl $ac_dummy forces splitting on constant user-supplied paths. +dnl POSIX.2 word splitting is done only on the output of word expansions, +dnl not every word. This closes a longstanding sh security hole. + ac_dummy="m4_if([$2], , $PATH, [$2])" + for ac_dir in $ac_dummy; do + IFS="$lt_save_ifs" + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/$1; then + lt_cv_path_MAGIC_CMD="$ac_dir/$1" + if test -n "$file_magic_test_file"; then + case $deplibs_check_method in + "file_magic "*) + file_magic_regex=`expr "$deplibs_check_method" : "file_magic \(.*\)"` + MAGIC_CMD="$lt_cv_path_MAGIC_CMD" + if eval $file_magic_cmd \$file_magic_test_file 2> /dev/null | + $EGREP "$file_magic_regex" > /dev/null; then + : + else + cat <<_LT_EOF 1>&2 + +*** Warning: the command libtool uses to detect shared libraries, +*** $file_magic_cmd, produces output that libtool cannot recognize. +*** The result is that libtool may fail to recognize shared libraries +*** as such. This will affect the creation of libtool libraries that +*** depend on shared libraries, but programs linked with such libtool +*** libraries will work regardless of this problem. Nevertheless, you +*** may want to report the problem to your system manager and/or to +*** bug-libtool@gnu.org + +_LT_EOF + fi ;; + esac + fi + break + fi + done + IFS="$lt_save_ifs" + MAGIC_CMD="$lt_save_MAGIC_CMD" + ;; +esac]) +MAGIC_CMD="$lt_cv_path_MAGIC_CMD" +if test -n "$MAGIC_CMD"; then + AC_MSG_RESULT($MAGIC_CMD) +else + AC_MSG_RESULT(no) +fi +_LT_DECL([], [MAGIC_CMD], [0], + [Used to examine libraries when file_magic_cmd begins with "file"])dnl +])# _LT_PATH_TOOL_PREFIX + +# Old name: +AU_ALIAS([AC_PATH_TOOL_PREFIX], [_LT_PATH_TOOL_PREFIX]) +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AC_PATH_TOOL_PREFIX], []) + + +# _LT_PATH_MAGIC +# -------------- +# find a file program which can recognize a shared library +m4_defun([_LT_PATH_MAGIC], +[_LT_PATH_TOOL_PREFIX(${ac_tool_prefix}file, /usr/bin$PATH_SEPARATOR$PATH) +if test -z "$lt_cv_path_MAGIC_CMD"; then + if test -n "$ac_tool_prefix"; then + _LT_PATH_TOOL_PREFIX(file, /usr/bin$PATH_SEPARATOR$PATH) + else + MAGIC_CMD=: + fi +fi +])# _LT_PATH_MAGIC + + +# LT_PATH_LD +# ---------- +# find the pathname to the GNU or non-GNU linker +AC_DEFUN([LT_PATH_LD], +[AC_REQUIRE([AC_PROG_CC])dnl +AC_REQUIRE([AC_CANONICAL_HOST])dnl +AC_REQUIRE([AC_CANONICAL_BUILD])dnl +m4_require([_LT_DECL_SED])dnl +m4_require([_LT_DECL_EGREP])dnl +m4_require([_LT_PROG_ECHO_BACKSLASH])dnl + +AC_ARG_WITH([gnu-ld], + [AS_HELP_STRING([--with-gnu-ld], + [assume the C compiler uses GNU ld @<:@default=no@:>@])], + [test "$withval" = no || with_gnu_ld=yes], + [with_gnu_ld=no])dnl + +ac_prog=ld +if test "$GCC" = yes; then + # Check if gcc -print-prog-name=ld gives a path. + AC_MSG_CHECKING([for ld used by $CC]) + case $host in + *-*-mingw*) + # gcc leaves a trailing carriage return which upsets mingw + ac_prog=`($CC -print-prog-name=ld) 2>&5 | tr -d '\015'` ;; + *) + ac_prog=`($CC -print-prog-name=ld) 2>&5` ;; + esac + case $ac_prog in + # Accept absolute paths. + [[\\/]]* | ?:[[\\/]]*) + re_direlt='/[[^/]][[^/]]*/\.\./' + # Canonicalize the pathname of ld + ac_prog=`$ECHO "$ac_prog"| $SED 's%\\\\%/%g'` + while $ECHO "$ac_prog" | $GREP "$re_direlt" > /dev/null 2>&1; do + ac_prog=`$ECHO $ac_prog| $SED "s%$re_direlt%/%"` + done + test -z "$LD" && LD="$ac_prog" + ;; + "") + # If it fails, then pretend we aren't using GCC. + ac_prog=ld + ;; + *) + # If it is relative, then search for the first ld in PATH. + with_gnu_ld=unknown + ;; + esac +elif test "$with_gnu_ld" = yes; then + AC_MSG_CHECKING([for GNU ld]) +else + AC_MSG_CHECKING([for non-GNU ld]) +fi +AC_CACHE_VAL(lt_cv_path_LD, +[if test -z "$LD"; then + lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR + for ac_dir in $PATH; do + IFS="$lt_save_ifs" + test -z "$ac_dir" && ac_dir=. + if test -f "$ac_dir/$ac_prog" || test -f "$ac_dir/$ac_prog$ac_exeext"; then + lt_cv_path_LD="$ac_dir/$ac_prog" + # Check to see if the program is GNU ld. I'd rather use --version, + # but apparently some variants of GNU ld only accept -v. + # Break only if it was the GNU/non-GNU ld that we prefer. + case `"$lt_cv_path_LD" -v 2>&1 &1 /dev/null 2>&1; then + lt_cv_deplibs_check_method='file_magic ^x86 archive import|^x86 DLL' + lt_cv_file_magic_cmd='func_win32_libid' + else + # Keep this pattern in sync with the one in func_win32_libid. + lt_cv_deplibs_check_method='file_magic file format (pei*-i386(.*architecture: i386)?|pe-arm-wince|pe-x86-64)' + lt_cv_file_magic_cmd='$OBJDUMP -f' + fi + ;; + +cegcc*) + # use the weaker test based on 'objdump'. See mingw*. + lt_cv_deplibs_check_method='file_magic file format pe-arm-.*little(.*architecture: arm)?' + lt_cv_file_magic_cmd='$OBJDUMP -f' + ;; + +darwin* | rhapsody*) + lt_cv_deplibs_check_method=pass_all + ;; + +freebsd* | dragonfly*) + if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then + case $host_cpu in + i*86 ) + # Not sure whether the presence of OpenBSD here was a mistake. + # Let's accept both of them until this is cleared up. + lt_cv_deplibs_check_method='file_magic (FreeBSD|OpenBSD|DragonFly)/i[[3-9]]86 (compact )?demand paged shared library' + lt_cv_file_magic_cmd=/usr/bin/file + lt_cv_file_magic_test_file=`echo /usr/lib/libc.so.*` + ;; + esac + else + lt_cv_deplibs_check_method=pass_all + fi + ;; + +haiku*) + lt_cv_deplibs_check_method=pass_all + ;; + +hpux10.20* | hpux11*) + lt_cv_file_magic_cmd=/usr/bin/file + case $host_cpu in + ia64*) + lt_cv_deplibs_check_method='file_magic (s[[0-9]][[0-9]][[0-9]]|ELF-[[0-9]][[0-9]]) shared object file - IA64' + lt_cv_file_magic_test_file=/usr/lib/hpux32/libc.so + ;; + hppa*64*) + [lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|ELF[ -][0-9][0-9])(-bit)?( [LM]SB)? shared object( file)?[, -]* PA-RISC [0-9]\.[0-9]'] + lt_cv_file_magic_test_file=/usr/lib/pa20_64/libc.sl + ;; + *) + lt_cv_deplibs_check_method='file_magic (s[[0-9]][[0-9]][[0-9]]|PA-RISC[[0-9]]\.[[0-9]]) shared library' + lt_cv_file_magic_test_file=/usr/lib/libc.sl + ;; + esac + ;; + +interix[[3-9]]*) + # PIC code is broken on Interix 3.x, that's why |\.a not |_pic\.a here + lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so|\.a)$' + ;; + +irix5* | irix6* | nonstopux*) + case $LD in + *-32|*"-32 ") libmagic=32-bit;; + *-n32|*"-n32 ") libmagic=N32;; + *-64|*"-64 ") libmagic=64-bit;; + *) libmagic=never-match;; + esac + lt_cv_deplibs_check_method=pass_all + ;; + +# This must be glibc/ELF. +linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) + lt_cv_deplibs_check_method=pass_all + ;; + +netbsd* | netbsdelf*-gnu) + if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then + lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so\.[[0-9]]+\.[[0-9]]+|_pic\.a)$' + else + lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so|_pic\.a)$' + fi + ;; + +newos6*) + lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[ML]]SB (executable|dynamic lib)' + lt_cv_file_magic_cmd=/usr/bin/file + lt_cv_file_magic_test_file=/usr/lib/libnls.so + ;; + +*nto* | *qnx*) + lt_cv_deplibs_check_method=pass_all + ;; + +openbsd*) + if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then + lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so\.[[0-9]]+\.[[0-9]]+|\.so|_pic\.a)$' + else + lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so\.[[0-9]]+\.[[0-9]]+|_pic\.a)$' + fi + ;; + +osf3* | osf4* | osf5*) + lt_cv_deplibs_check_method=pass_all + ;; + +rdos*) + lt_cv_deplibs_check_method=pass_all + ;; + +solaris*) + lt_cv_deplibs_check_method=pass_all + ;; + +sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*) + lt_cv_deplibs_check_method=pass_all + ;; + +sysv4 | sysv4.3*) + case $host_vendor in + motorola) + lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[ML]]SB (shared object|dynamic lib) M[[0-9]][[0-9]]* Version [[0-9]]' + lt_cv_file_magic_test_file=`echo /usr/lib/libc.so*` + ;; + ncr) + lt_cv_deplibs_check_method=pass_all + ;; + sequent) + lt_cv_file_magic_cmd='/bin/file' + lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[LM]]SB (shared object|dynamic lib )' + ;; + sni) + lt_cv_file_magic_cmd='/bin/file' + lt_cv_deplibs_check_method="file_magic ELF [[0-9]][[0-9]]*-bit [[LM]]SB dynamic lib" + lt_cv_file_magic_test_file=/lib/libc.so + ;; + siemens) + lt_cv_deplibs_check_method=pass_all + ;; + pc) + lt_cv_deplibs_check_method=pass_all + ;; + esac + ;; + +tpf*) + lt_cv_deplibs_check_method=pass_all + ;; +esac +]) + +file_magic_glob= +want_nocaseglob=no +if test "$build" = "$host"; then + case $host_os in + mingw* | pw32*) + if ( shopt | grep nocaseglob ) >/dev/null 2>&1; then + want_nocaseglob=yes + else + file_magic_glob=`echo aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ | $SED -e "s/\(..\)/s\/[[\1]]\/[[\1]]\/g;/g"` + fi + ;; + esac +fi + +file_magic_cmd=$lt_cv_file_magic_cmd +deplibs_check_method=$lt_cv_deplibs_check_method +test -z "$deplibs_check_method" && deplibs_check_method=unknown + +_LT_DECL([], [deplibs_check_method], [1], + [Method to check whether dependent libraries are shared objects]) +_LT_DECL([], [file_magic_cmd], [1], + [Command to use when deplibs_check_method = "file_magic"]) +_LT_DECL([], [file_magic_glob], [1], + [How to find potential files when deplibs_check_method = "file_magic"]) +_LT_DECL([], [want_nocaseglob], [1], + [Find potential files using nocaseglob when deplibs_check_method = "file_magic"]) +])# _LT_CHECK_MAGIC_METHOD + + +# LT_PATH_NM +# ---------- +# find the pathname to a BSD- or MS-compatible name lister +AC_DEFUN([LT_PATH_NM], +[AC_REQUIRE([AC_PROG_CC])dnl +AC_CACHE_CHECK([for BSD- or MS-compatible name lister (nm)], lt_cv_path_NM, +[if test -n "$NM"; then + # Let the user override the test. + lt_cv_path_NM="$NM" +else + lt_nm_to_check="${ac_tool_prefix}nm" + if test -n "$ac_tool_prefix" && test "$build" = "$host"; then + lt_nm_to_check="$lt_nm_to_check nm" + fi + for lt_tmp_nm in $lt_nm_to_check; do + lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR + for ac_dir in $PATH /usr/ccs/bin/elf /usr/ccs/bin /usr/ucb /bin; do + IFS="$lt_save_ifs" + test -z "$ac_dir" && ac_dir=. + tmp_nm="$ac_dir/$lt_tmp_nm" + if test -f "$tmp_nm" || test -f "$tmp_nm$ac_exeext" ; then + # Check to see if the nm accepts a BSD-compat flag. + # Adding the `sed 1q' prevents false positives on HP-UX, which says: + # nm: unknown option "B" ignored + # Tru64's nm complains that /dev/null is an invalid object file + case `"$tmp_nm" -B /dev/null 2>&1 | sed '1q'` in + */dev/null* | *'Invalid file or object type'*) + lt_cv_path_NM="$tmp_nm -B" + break + ;; + *) + case `"$tmp_nm" -p /dev/null 2>&1 | sed '1q'` in + */dev/null*) + lt_cv_path_NM="$tmp_nm -p" + break + ;; + *) + lt_cv_path_NM=${lt_cv_path_NM="$tmp_nm"} # keep the first match, but + continue # so that we can try to find one that supports BSD flags + ;; + esac + ;; + esac + fi + done + IFS="$lt_save_ifs" + done + : ${lt_cv_path_NM=no} +fi]) +if test "$lt_cv_path_NM" != "no"; then + NM="$lt_cv_path_NM" +else + # Didn't find any BSD compatible name lister, look for dumpbin. + if test -n "$DUMPBIN"; then : + # Let the user override the test. + else + AC_CHECK_TOOLS(DUMPBIN, [dumpbin "link -dump"], :) + case `$DUMPBIN -symbols /dev/null 2>&1 | sed '1q'` in + *COFF*) + DUMPBIN="$DUMPBIN -symbols" + ;; + *) + DUMPBIN=: + ;; + esac + fi + AC_SUBST([DUMPBIN]) + if test "$DUMPBIN" != ":"; then + NM="$DUMPBIN" + fi +fi +test -z "$NM" && NM=nm +AC_SUBST([NM]) +_LT_DECL([], [NM], [1], [A BSD- or MS-compatible name lister])dnl + +AC_CACHE_CHECK([the name lister ($NM) interface], [lt_cv_nm_interface], + [lt_cv_nm_interface="BSD nm" + echo "int some_variable = 0;" > conftest.$ac_ext + (eval echo "\"\$as_me:$LINENO: $ac_compile\"" >&AS_MESSAGE_LOG_FD) + (eval "$ac_compile" 2>conftest.err) + cat conftest.err >&AS_MESSAGE_LOG_FD + (eval echo "\"\$as_me:$LINENO: $NM \\\"conftest.$ac_objext\\\"\"" >&AS_MESSAGE_LOG_FD) + (eval "$NM \"conftest.$ac_objext\"" 2>conftest.err > conftest.out) + cat conftest.err >&AS_MESSAGE_LOG_FD + (eval echo "\"\$as_me:$LINENO: output\"" >&AS_MESSAGE_LOG_FD) + cat conftest.out >&AS_MESSAGE_LOG_FD + if $GREP 'External.*some_variable' conftest.out > /dev/null; then + lt_cv_nm_interface="MS dumpbin" + fi + rm -f conftest*]) +])# LT_PATH_NM + +# Old names: +AU_ALIAS([AM_PROG_NM], [LT_PATH_NM]) +AU_ALIAS([AC_PROG_NM], [LT_PATH_NM]) +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AM_PROG_NM], []) +dnl AC_DEFUN([AC_PROG_NM], []) + +# _LT_CHECK_SHAREDLIB_FROM_LINKLIB +# -------------------------------- +# how to determine the name of the shared library +# associated with a specific link library. +# -- PORTME fill in with the dynamic library characteristics +m4_defun([_LT_CHECK_SHAREDLIB_FROM_LINKLIB], +[m4_require([_LT_DECL_EGREP]) +m4_require([_LT_DECL_OBJDUMP]) +m4_require([_LT_DECL_DLLTOOL]) +AC_CACHE_CHECK([how to associate runtime and link libraries], +lt_cv_sharedlib_from_linklib_cmd, +[lt_cv_sharedlib_from_linklib_cmd='unknown' + +case $host_os in +cygwin* | mingw* | pw32* | cegcc*) + # two different shell functions defined in ltmain.sh + # decide which to use based on capabilities of $DLLTOOL + case `$DLLTOOL --help 2>&1` in + *--identify-strict*) + lt_cv_sharedlib_from_linklib_cmd=func_cygming_dll_for_implib + ;; + *) + lt_cv_sharedlib_from_linklib_cmd=func_cygming_dll_for_implib_fallback + ;; + esac + ;; +*) + # fallback: assume linklib IS sharedlib + lt_cv_sharedlib_from_linklib_cmd="$ECHO" + ;; +esac +]) +sharedlib_from_linklib_cmd=$lt_cv_sharedlib_from_linklib_cmd +test -z "$sharedlib_from_linklib_cmd" && sharedlib_from_linklib_cmd=$ECHO + +_LT_DECL([], [sharedlib_from_linklib_cmd], [1], + [Command to associate shared and link libraries]) +])# _LT_CHECK_SHAREDLIB_FROM_LINKLIB + + +# _LT_PATH_MANIFEST_TOOL +# ---------------------- +# locate the manifest tool +m4_defun([_LT_PATH_MANIFEST_TOOL], +[AC_CHECK_TOOL(MANIFEST_TOOL, mt, :) +test -z "$MANIFEST_TOOL" && MANIFEST_TOOL=mt +AC_CACHE_CHECK([if $MANIFEST_TOOL is a manifest tool], [lt_cv_path_mainfest_tool], + [lt_cv_path_mainfest_tool=no + echo "$as_me:$LINENO: $MANIFEST_TOOL '-?'" >&AS_MESSAGE_LOG_FD + $MANIFEST_TOOL '-?' 2>conftest.err > conftest.out + cat conftest.err >&AS_MESSAGE_LOG_FD + if $GREP 'Manifest Tool' conftest.out > /dev/null; then + lt_cv_path_mainfest_tool=yes + fi + rm -f conftest*]) +if test "x$lt_cv_path_mainfest_tool" != xyes; then + MANIFEST_TOOL=: +fi +_LT_DECL([], [MANIFEST_TOOL], [1], [Manifest tool])dnl +])# _LT_PATH_MANIFEST_TOOL + + +# LT_LIB_M +# -------- +# check for math library +AC_DEFUN([LT_LIB_M], +[AC_REQUIRE([AC_CANONICAL_HOST])dnl +LIBM= +case $host in +*-*-beos* | *-*-cegcc* | *-*-cygwin* | *-*-haiku* | *-*-pw32* | *-*-darwin*) + # These system don't have libm, or don't need it + ;; +*-ncr-sysv4.3*) + AC_CHECK_LIB(mw, _mwvalidcheckl, LIBM="-lmw") + AC_CHECK_LIB(m, cos, LIBM="$LIBM -lm") + ;; +*) + AC_CHECK_LIB(m, cos, LIBM="-lm") + ;; +esac +AC_SUBST([LIBM]) +])# LT_LIB_M + +# Old name: +AU_ALIAS([AC_CHECK_LIBM], [LT_LIB_M]) +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AC_CHECK_LIBM], []) + + +# _LT_COMPILER_NO_RTTI([TAGNAME]) +# ------------------------------- +m4_defun([_LT_COMPILER_NO_RTTI], +[m4_require([_LT_TAG_COMPILER])dnl + +_LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)= + +if test "$GCC" = yes; then + case $cc_basename in + nvcc*) + _LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)=' -Xcompiler -fno-builtin' ;; + *) + _LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)=' -fno-builtin' ;; + esac + + _LT_COMPILER_OPTION([if $compiler supports -fno-rtti -fno-exceptions], + lt_cv_prog_compiler_rtti_exceptions, + [-fno-rtti -fno-exceptions], [], + [_LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)="$_LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1) -fno-rtti -fno-exceptions"]) +fi +_LT_TAGDECL([no_builtin_flag], [lt_prog_compiler_no_builtin_flag], [1], + [Compiler flag to turn off builtin functions]) +])# _LT_COMPILER_NO_RTTI + + +# _LT_CMD_GLOBAL_SYMBOLS +# ---------------------- +m4_defun([_LT_CMD_GLOBAL_SYMBOLS], +[AC_REQUIRE([AC_CANONICAL_HOST])dnl +AC_REQUIRE([AC_PROG_CC])dnl +AC_REQUIRE([AC_PROG_AWK])dnl +AC_REQUIRE([LT_PATH_NM])dnl +AC_REQUIRE([LT_PATH_LD])dnl +m4_require([_LT_DECL_SED])dnl +m4_require([_LT_DECL_EGREP])dnl +m4_require([_LT_TAG_COMPILER])dnl + +# Check for command to grab the raw symbol name followed by C symbol from nm. +AC_MSG_CHECKING([command to parse $NM output from $compiler object]) +AC_CACHE_VAL([lt_cv_sys_global_symbol_pipe], +[ +# These are sane defaults that work on at least a few old systems. +# [They come from Ultrix. What could be older than Ultrix?!! ;)] + +# Character class describing NM global symbol codes. +symcode='[[BCDEGRST]]' + +# Regexp to match symbols that can be accessed directly from C. +sympat='\([[_A-Za-z]][[_A-Za-z0-9]]*\)' + +# Define system-specific variables. +case $host_os in +aix*) + symcode='[[BCDT]]' + ;; +cygwin* | mingw* | pw32* | cegcc*) + symcode='[[ABCDGISTW]]' + ;; +hpux*) + if test "$host_cpu" = ia64; then + symcode='[[ABCDEGRST]]' + fi + ;; +irix* | nonstopux*) + symcode='[[BCDEGRST]]' + ;; +osf*) + symcode='[[BCDEGQRST]]' + ;; +solaris*) + symcode='[[BDRT]]' + ;; +sco3.2v5*) + symcode='[[DT]]' + ;; +sysv4.2uw2*) + symcode='[[DT]]' + ;; +sysv5* | sco5v6* | unixware* | OpenUNIX*) + symcode='[[ABDT]]' + ;; +sysv4) + symcode='[[DFNSTU]]' + ;; +esac + +# If we're using GNU nm, then use its standard symbol codes. +case `$NM -V 2>&1` in +*GNU* | *'with BFD'*) + symcode='[[ABCDGIRSTW]]' ;; +esac + +# Transform an extracted symbol line into a proper C declaration. +# Some systems (esp. on ia64) link data and code symbols differently, +# so use this general approach. +lt_cv_sys_global_symbol_to_cdecl="sed -n -e 's/^T .* \(.*\)$/extern int \1();/p' -e 's/^$symcode* .* \(.*\)$/extern char \1;/p'" + +# Transform an extracted symbol line into symbol name and symbol address +lt_cv_sys_global_symbol_to_c_name_address="sed -n -e 's/^: \([[^ ]]*\)[[ ]]*$/ {\\\"\1\\\", (void *) 0},/p' -e 's/^$symcode* \([[^ ]]*\) \([[^ ]]*\)$/ {\"\2\", (void *) \&\2},/p'" +lt_cv_sys_global_symbol_to_c_name_address_lib_prefix="sed -n -e 's/^: \([[^ ]]*\)[[ ]]*$/ {\\\"\1\\\", (void *) 0},/p' -e 's/^$symcode* \([[^ ]]*\) \(lib[[^ ]]*\)$/ {\"\2\", (void *) \&\2},/p' -e 's/^$symcode* \([[^ ]]*\) \([[^ ]]*\)$/ {\"lib\2\", (void *) \&\2},/p'" + +# Handle CRLF in mingw tool chain +opt_cr= +case $build_os in +mingw*) + opt_cr=`$ECHO 'x\{0,1\}' | tr x '\015'` # option cr in regexp + ;; +esac + +# Try without a prefix underscore, then with it. +for ac_symprfx in "" "_"; do + + # Transform symcode, sympat, and symprfx into a raw symbol and a C symbol. + symxfrm="\\1 $ac_symprfx\\2 \\2" + + # Write the raw and C identifiers. + if test "$lt_cv_nm_interface" = "MS dumpbin"; then + # Fake it for dumpbin and say T for any non-static function + # and D for any global variable. + # Also find C++ and __fastcall symbols from MSVC++, + # which start with @ or ?. + lt_cv_sys_global_symbol_pipe="$AWK ['"\ +" {last_section=section; section=\$ 3};"\ +" /^COFF SYMBOL TABLE/{for(i in hide) delete hide[i]};"\ +" /Section length .*#relocs.*(pick any)/{hide[last_section]=1};"\ +" \$ 0!~/External *\|/{next};"\ +" / 0+ UNDEF /{next}; / UNDEF \([^|]\)*()/{next};"\ +" {if(hide[section]) next};"\ +" {f=0}; \$ 0~/\(\).*\|/{f=1}; {printf f ? \"T \" : \"D \"};"\ +" {split(\$ 0, a, /\||\r/); split(a[2], s)};"\ +" s[1]~/^[@?]/{print s[1], s[1]; next};"\ +" s[1]~prfx {split(s[1],t,\"@\"); print t[1], substr(t[1],length(prfx))}"\ +" ' prfx=^$ac_symprfx]" + else + lt_cv_sys_global_symbol_pipe="sed -n -e 's/^.*[[ ]]\($symcode$symcode*\)[[ ]][[ ]]*$ac_symprfx$sympat$opt_cr$/$symxfrm/p'" + fi + lt_cv_sys_global_symbol_pipe="$lt_cv_sys_global_symbol_pipe | sed '/ __gnu_lto/d'" + + # Check to see that the pipe works correctly. + pipe_works=no + + rm -f conftest* + cat > conftest.$ac_ext <<_LT_EOF +#ifdef __cplusplus +extern "C" { +#endif +char nm_test_var; +void nm_test_func(void); +void nm_test_func(void){} +#ifdef __cplusplus +} +#endif +int main(){nm_test_var='a';nm_test_func();return(0);} +_LT_EOF + + if AC_TRY_EVAL(ac_compile); then + # Now try to grab the symbols. + nlist=conftest.nm + if AC_TRY_EVAL(NM conftest.$ac_objext \| "$lt_cv_sys_global_symbol_pipe" \> $nlist) && test -s "$nlist"; then + # Try sorting and uniquifying the output. + if sort "$nlist" | uniq > "$nlist"T; then + mv -f "$nlist"T "$nlist" + else + rm -f "$nlist"T + fi + + # Make sure that we snagged all the symbols we need. + if $GREP ' nm_test_var$' "$nlist" >/dev/null; then + if $GREP ' nm_test_func$' "$nlist" >/dev/null; then + cat <<_LT_EOF > conftest.$ac_ext +/* Keep this code in sync between libtool.m4, ltmain, lt_system.h, and tests. */ +#if defined(_WIN32) || defined(__CYGWIN__) || defined(_WIN32_WCE) +/* DATA imports from DLLs on WIN32 con't be const, because runtime + relocations are performed -- see ld's documentation on pseudo-relocs. */ +# define LT@&t@_DLSYM_CONST +#elif defined(__osf__) +/* This system does not cope well with relocations in const data. */ +# define LT@&t@_DLSYM_CONST +#else +# define LT@&t@_DLSYM_CONST const +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +_LT_EOF + # Now generate the symbol file. + eval "$lt_cv_sys_global_symbol_to_cdecl"' < "$nlist" | $GREP -v main >> conftest.$ac_ext' + + cat <<_LT_EOF >> conftest.$ac_ext + +/* The mapping between symbol names and symbols. */ +LT@&t@_DLSYM_CONST struct { + const char *name; + void *address; +} +lt__PROGRAM__LTX_preloaded_symbols[[]] = +{ + { "@PROGRAM@", (void *) 0 }, +_LT_EOF + $SED "s/^$symcode$symcode* \(.*\) \(.*\)$/ {\"\2\", (void *) \&\2},/" < "$nlist" | $GREP -v main >> conftest.$ac_ext + cat <<\_LT_EOF >> conftest.$ac_ext + {0, (void *) 0} +}; + +/* This works around a problem in FreeBSD linker */ +#ifdef FREEBSD_WORKAROUND +static const void *lt_preloaded_setup() { + return lt__PROGRAM__LTX_preloaded_symbols; +} +#endif + +#ifdef __cplusplus +} +#endif +_LT_EOF + # Now try linking the two files. + mv conftest.$ac_objext conftstm.$ac_objext + lt_globsym_save_LIBS=$LIBS + lt_globsym_save_CFLAGS=$CFLAGS + LIBS="conftstm.$ac_objext" + CFLAGS="$CFLAGS$_LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)" + if AC_TRY_EVAL(ac_link) && test -s conftest${ac_exeext}; then + pipe_works=yes + fi + LIBS=$lt_globsym_save_LIBS + CFLAGS=$lt_globsym_save_CFLAGS + else + echo "cannot find nm_test_func in $nlist" >&AS_MESSAGE_LOG_FD + fi + else + echo "cannot find nm_test_var in $nlist" >&AS_MESSAGE_LOG_FD + fi + else + echo "cannot run $lt_cv_sys_global_symbol_pipe" >&AS_MESSAGE_LOG_FD + fi + else + echo "$progname: failed program was:" >&AS_MESSAGE_LOG_FD + cat conftest.$ac_ext >&5 + fi + rm -rf conftest* conftst* + + # Do not use the global_symbol_pipe unless it works. + if test "$pipe_works" = yes; then + break + else + lt_cv_sys_global_symbol_pipe= + fi +done +]) +if test -z "$lt_cv_sys_global_symbol_pipe"; then + lt_cv_sys_global_symbol_to_cdecl= +fi +if test -z "$lt_cv_sys_global_symbol_pipe$lt_cv_sys_global_symbol_to_cdecl"; then + AC_MSG_RESULT(failed) +else + AC_MSG_RESULT(ok) +fi + +# Response file support. +if test "$lt_cv_nm_interface" = "MS dumpbin"; then + nm_file_list_spec='@' +elif $NM --help 2>/dev/null | grep '[[@]]FILE' >/dev/null; then + nm_file_list_spec='@' +fi + +_LT_DECL([global_symbol_pipe], [lt_cv_sys_global_symbol_pipe], [1], + [Take the output of nm and produce a listing of raw symbols and C names]) +_LT_DECL([global_symbol_to_cdecl], [lt_cv_sys_global_symbol_to_cdecl], [1], + [Transform the output of nm in a proper C declaration]) +_LT_DECL([global_symbol_to_c_name_address], + [lt_cv_sys_global_symbol_to_c_name_address], [1], + [Transform the output of nm in a C name address pair]) +_LT_DECL([global_symbol_to_c_name_address_lib_prefix], + [lt_cv_sys_global_symbol_to_c_name_address_lib_prefix], [1], + [Transform the output of nm in a C name address pair when lib prefix is needed]) +_LT_DECL([], [nm_file_list_spec], [1], + [Specify filename containing input files for $NM]) +]) # _LT_CMD_GLOBAL_SYMBOLS + + +# _LT_COMPILER_PIC([TAGNAME]) +# --------------------------- +m4_defun([_LT_COMPILER_PIC], +[m4_require([_LT_TAG_COMPILER])dnl +_LT_TAGVAR(lt_prog_compiler_wl, $1)= +_LT_TAGVAR(lt_prog_compiler_pic, $1)= +_LT_TAGVAR(lt_prog_compiler_static, $1)= + +m4_if([$1], [CXX], [ + # C++ specific cases for pic, static, wl, etc. + if test "$GXX" = yes; then + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' + + case $host_os in + aix*) + # All AIX code is PIC. + if test "$host_cpu" = ia64; then + # AIX 5 now supports IA64 processor + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + fi + ;; + + amigaos*) + case $host_cpu in + powerpc) + # see comment about AmigaOS4 .so support + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' + ;; + m68k) + # FIXME: we need at least 68020 code to build shared libraries, but + # adding the `-m68020' flag to GCC prevents building anything better, + # like `-m68040'. + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-m68020 -resident32 -malways-restore-a4' + ;; + esac + ;; + + beos* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*) + # PIC is the default for these OSes. + ;; + mingw* | cygwin* | os2* | pw32* | cegcc*) + # This hack is so that the source file can tell whether it is being + # built for inclusion in a dll (and should export symbols for example). + # Although the cygwin gcc ignores -fPIC, still need this for old-style + # (--disable-auto-import) libraries + m4_if([$1], [GCJ], [], + [_LT_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT']) + ;; + darwin* | rhapsody*) + # PIC is the default on this platform + # Common symbols not allowed in MH_DYLIB files + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fno-common' + ;; + *djgpp*) + # DJGPP does not support shared libraries at all + _LT_TAGVAR(lt_prog_compiler_pic, $1)= + ;; + haiku*) + # PIC is the default for Haiku. + # The "-static" flag exists, but is broken. + _LT_TAGVAR(lt_prog_compiler_static, $1)= + ;; + interix[[3-9]]*) + # Interix 3.x gcc -fpic/-fPIC options generate broken code. + # Instead, we relocate shared libraries at runtime. + ;; + sysv4*MP*) + if test -d /usr/nec; then + _LT_TAGVAR(lt_prog_compiler_pic, $1)=-Kconform_pic + fi + ;; + hpux*) + # PIC is the default for 64-bit PA HP-UX, but not for 32-bit + # PA HP-UX. On IA64 HP-UX, PIC is the default but the pic flag + # sets the default TLS model and affects inlining. + case $host_cpu in + hppa*64*) + ;; + *) + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' + ;; + esac + ;; + *qnx* | *nto*) + # QNX uses GNU C++, but need to define -shared option too, otherwise + # it will coredump. + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC -shared' + ;; + *) + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' + ;; + esac + else + case $host_os in + aix[[4-9]]*) + # All AIX code is PIC. + if test "$host_cpu" = ia64; then + # AIX 5 now supports IA64 processor + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + else + _LT_TAGVAR(lt_prog_compiler_static, $1)='-bnso -bI:/lib/syscalls.exp' + fi + ;; + chorus*) + case $cc_basename in + cxch68*) + # Green Hills C++ Compiler + # _LT_TAGVAR(lt_prog_compiler_static, $1)="--no_auto_instantiation -u __main -u __premain -u _abort -r $COOL_DIR/lib/libOrb.a $MVME_DIR/lib/CC/libC.a $MVME_DIR/lib/classix/libcx.s.a" + ;; + esac + ;; + mingw* | cygwin* | os2* | pw32* | cegcc*) + # This hack is so that the source file can tell whether it is being + # built for inclusion in a dll (and should export symbols for example). + m4_if([$1], [GCJ], [], + [_LT_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT']) + ;; + dgux*) + case $cc_basename in + ec++*) + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + ;; + ghcx*) + # Green Hills C++ Compiler + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic' + ;; + *) + ;; + esac + ;; + freebsd* | dragonfly*) + # FreeBSD uses GNU C++ + ;; + hpux9* | hpux10* | hpux11*) + case $cc_basename in + CC*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_static, $1)='${wl}-a ${wl}archive' + if test "$host_cpu" != ia64; then + _LT_TAGVAR(lt_prog_compiler_pic, $1)='+Z' + fi + ;; + aCC*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_static, $1)='${wl}-a ${wl}archive' + case $host_cpu in + hppa*64*|ia64*) + # +Z the default + ;; + *) + _LT_TAGVAR(lt_prog_compiler_pic, $1)='+Z' + ;; + esac + ;; + *) + ;; + esac + ;; + interix*) + # This is c89, which is MS Visual C++ (no shared libs) + # Anyone wants to do a port? + ;; + irix5* | irix6* | nonstopux*) + case $cc_basename in + CC*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' + # CC pic flag -KPIC is the default. + ;; + *) + ;; + esac + ;; + linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) + case $cc_basename in + KCC*) + # KAI C++ Compiler + _LT_TAGVAR(lt_prog_compiler_wl, $1)='--backend -Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' + ;; + ecpc* ) + # old Intel C++ for x86_64 which still supported -KPIC. + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' + ;; + icpc* ) + # Intel C++, used to be incompatible with GCC. + # ICC 10 doesn't accept -KPIC any more. + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' + ;; + pgCC* | pgcpp*) + # Portland Group C++ compiler + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fpic' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + ;; + cxx*) + # Compaq C++ + # Make sure the PIC flag is empty. It appears that all Alpha + # Linux and Compaq Tru64 Unix objects are PIC. + _LT_TAGVAR(lt_prog_compiler_pic, $1)= + _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' + ;; + xlc* | xlC* | bgxl[[cC]]* | mpixl[[cC]]*) + # IBM XL 8.0, 9.0 on PPC and BlueGene + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-qpic' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-qstaticlink' + ;; + *) + case `$CC -V 2>&1 | sed 5q` in + *Sun\ C*) + # Sun C++ 5.9 + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld ' + ;; + esac + ;; + esac + ;; + lynxos*) + ;; + m88k*) + ;; + mvs*) + case $cc_basename in + cxx*) + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-W c,exportall' + ;; + *) + ;; + esac + ;; + netbsd* | netbsdelf*-gnu) + ;; + *qnx* | *nto*) + # QNX uses GNU C++, but need to define -shared option too, otherwise + # it will coredump. + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC -shared' + ;; + osf3* | osf4* | osf5*) + case $cc_basename in + KCC*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='--backend -Wl,' + ;; + RCC*) + # Rational C++ 2.4.1 + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic' + ;; + cxx*) + # Digital/Compaq C++ + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + # Make sure the PIC flag is empty. It appears that all Alpha + # Linux and Compaq Tru64 Unix objects are PIC. + _LT_TAGVAR(lt_prog_compiler_pic, $1)= + _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' + ;; + *) + ;; + esac + ;; + psos*) + ;; + solaris*) + case $cc_basename in + CC* | sunCC*) + # Sun C++ 4.2, 5.x and Centerline C++ + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld ' + ;; + gcx*) + # Green Hills C++ Compiler + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-PIC' + ;; + *) + ;; + esac + ;; + sunos4*) + case $cc_basename in + CC*) + # Sun C++ 4.x + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + ;; + lcc*) + # Lucid + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic' + ;; + *) + ;; + esac + ;; + sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*) + case $cc_basename in + CC*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + ;; + esac + ;; + tandem*) + case $cc_basename in + NCC*) + # NonStop-UX NCC 3.20 + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + ;; + *) + ;; + esac + ;; + vxworks*) + ;; + *) + _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no + ;; + esac + fi +], +[ + if test "$GCC" = yes; then + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' + + case $host_os in + aix*) + # All AIX code is PIC. + if test "$host_cpu" = ia64; then + # AIX 5 now supports IA64 processor + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + fi + ;; + + amigaos*) + case $host_cpu in + powerpc) + # see comment about AmigaOS4 .so support + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' + ;; + m68k) + # FIXME: we need at least 68020 code to build shared libraries, but + # adding the `-m68020' flag to GCC prevents building anything better, + # like `-m68040'. + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-m68020 -resident32 -malways-restore-a4' + ;; + esac + ;; + + beos* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*) + # PIC is the default for these OSes. + ;; + + mingw* | cygwin* | pw32* | os2* | cegcc*) + # This hack is so that the source file can tell whether it is being + # built for inclusion in a dll (and should export symbols for example). + # Although the cygwin gcc ignores -fPIC, still need this for old-style + # (--disable-auto-import) libraries + m4_if([$1], [GCJ], [], + [_LT_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT']) + ;; + + darwin* | rhapsody*) + # PIC is the default on this platform + # Common symbols not allowed in MH_DYLIB files + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fno-common' + ;; + + haiku*) + # PIC is the default for Haiku. + # The "-static" flag exists, but is broken. + _LT_TAGVAR(lt_prog_compiler_static, $1)= + ;; + + hpux*) + # PIC is the default for 64-bit PA HP-UX, but not for 32-bit + # PA HP-UX. On IA64 HP-UX, PIC is the default but the pic flag + # sets the default TLS model and affects inlining. + case $host_cpu in + hppa*64*) + # +Z the default + ;; + *) + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' + ;; + esac + ;; + + interix[[3-9]]*) + # Interix 3.x gcc -fpic/-fPIC options generate broken code. + # Instead, we relocate shared libraries at runtime. + ;; + + msdosdjgpp*) + # Just because we use GCC doesn't mean we suddenly get shared libraries + # on systems that don't support them. + _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no + enable_shared=no + ;; + + *nto* | *qnx*) + # QNX uses GNU C++, but need to define -shared option too, otherwise + # it will coredump. + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC -shared' + ;; + + sysv4*MP*) + if test -d /usr/nec; then + _LT_TAGVAR(lt_prog_compiler_pic, $1)=-Kconform_pic + fi + ;; + + *) + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' + ;; + esac + + case $cc_basename in + nvcc*) # Cuda Compiler Driver 2.2 + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Xlinker ' + if test -n "$_LT_TAGVAR(lt_prog_compiler_pic, $1)"; then + _LT_TAGVAR(lt_prog_compiler_pic, $1)="-Xcompiler $_LT_TAGVAR(lt_prog_compiler_pic, $1)" + fi + ;; + esac + else + # PORTME Check for flag to pass linker flags through the system compiler. + case $host_os in + aix*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + if test "$host_cpu" = ia64; then + # AIX 5 now supports IA64 processor + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + else + _LT_TAGVAR(lt_prog_compiler_static, $1)='-bnso -bI:/lib/syscalls.exp' + fi + ;; + + mingw* | cygwin* | pw32* | os2* | cegcc*) + # This hack is so that the source file can tell whether it is being + # built for inclusion in a dll (and should export symbols for example). + m4_if([$1], [GCJ], [], + [_LT_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT']) + ;; + + hpux9* | hpux10* | hpux11*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + # PIC is the default for IA64 HP-UX and 64-bit HP-UX, but + # not for PA HP-UX. + case $host_cpu in + hppa*64*|ia64*) + # +Z the default + ;; + *) + _LT_TAGVAR(lt_prog_compiler_pic, $1)='+Z' + ;; + esac + # Is there a better lt_prog_compiler_static that works with the bundled CC? + _LT_TAGVAR(lt_prog_compiler_static, $1)='${wl}-a ${wl}archive' + ;; + + irix5* | irix6* | nonstopux*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + # PIC (with -KPIC) is the default. + _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' + ;; + + linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) + case $cc_basename in + # old Intel for x86_64 which still supported -KPIC. + ecc*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' + ;; + # icc used to be incompatible with GCC. + # ICC 10 doesn't accept -KPIC any more. + icc* | ifort*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' + ;; + # Lahey Fortran 8.1. + lf95*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='--shared' + _LT_TAGVAR(lt_prog_compiler_static, $1)='--static' + ;; + nagfor*) + # NAG Fortran compiler + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,-Wl,,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-PIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + ;; + pgcc* | pgf77* | pgf90* | pgf95* | pgfortran*) + # Portland Group compilers (*not* the Pentium gcc compiler, + # which looks to be a dead project) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fpic' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + ;; + ccc*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + # All Alpha code is PIC. + _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' + ;; + xl* | bgxl* | bgf* | mpixl*) + # IBM XL C 8.0/Fortran 10.1, 11.1 on PPC and BlueGene + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-qpic' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-qstaticlink' + ;; + *) + case `$CC -V 2>&1 | sed 5q` in + *Sun\ Ceres\ Fortran* | *Sun*Fortran*\ [[1-7]].* | *Sun*Fortran*\ 8.[[0-3]]*) + # Sun Fortran 8.3 passes all unrecognized flags to the linker + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + _LT_TAGVAR(lt_prog_compiler_wl, $1)='' + ;; + *Sun\ F* | *Sun*Fortran*) + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld ' + ;; + *Sun\ C*) + # Sun C 5.9 + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + ;; + *Intel*\ [[CF]]*Compiler*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' + ;; + *Portland\ Group*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fpic' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + ;; + esac + ;; + esac + ;; + + newsos6) + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + ;; + + *nto* | *qnx*) + # QNX uses GNU C++, but need to define -shared option too, otherwise + # it will coredump. + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC -shared' + ;; + + osf3* | osf4* | osf5*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + # All OSF/1 code is PIC. + _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' + ;; + + rdos*) + _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' + ;; + + solaris*) + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + case $cc_basename in + f77* | f90* | f95* | sunf77* | sunf90* | sunf95*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld ';; + *) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,';; + esac + ;; + + sunos4*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld ' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-PIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + ;; + + sysv4 | sysv4.2uw2* | sysv4.3*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + ;; + + sysv4*MP*) + if test -d /usr/nec ;then + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-Kconform_pic' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + fi + ;; + + sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + ;; + + unicos*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no + ;; + + uts4*) + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + ;; + + *) + _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no + ;; + esac + fi +]) +case $host_os in + # For platforms which do not support PIC, -DPIC is meaningless: + *djgpp*) + _LT_TAGVAR(lt_prog_compiler_pic, $1)= + ;; + *) + _LT_TAGVAR(lt_prog_compiler_pic, $1)="$_LT_TAGVAR(lt_prog_compiler_pic, $1)@&t@m4_if([$1],[],[ -DPIC],[m4_if([$1],[CXX],[ -DPIC],[])])" + ;; +esac + +AC_CACHE_CHECK([for $compiler option to produce PIC], + [_LT_TAGVAR(lt_cv_prog_compiler_pic, $1)], + [_LT_TAGVAR(lt_cv_prog_compiler_pic, $1)=$_LT_TAGVAR(lt_prog_compiler_pic, $1)]) +_LT_TAGVAR(lt_prog_compiler_pic, $1)=$_LT_TAGVAR(lt_cv_prog_compiler_pic, $1) + +# +# Check to make sure the PIC flag actually works. +# +if test -n "$_LT_TAGVAR(lt_prog_compiler_pic, $1)"; then + _LT_COMPILER_OPTION([if $compiler PIC flag $_LT_TAGVAR(lt_prog_compiler_pic, $1) works], + [_LT_TAGVAR(lt_cv_prog_compiler_pic_works, $1)], + [$_LT_TAGVAR(lt_prog_compiler_pic, $1)@&t@m4_if([$1],[],[ -DPIC],[m4_if([$1],[CXX],[ -DPIC],[])])], [], + [case $_LT_TAGVAR(lt_prog_compiler_pic, $1) in + "" | " "*) ;; + *) _LT_TAGVAR(lt_prog_compiler_pic, $1)=" $_LT_TAGVAR(lt_prog_compiler_pic, $1)" ;; + esac], + [_LT_TAGVAR(lt_prog_compiler_pic, $1)= + _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no]) +fi +_LT_TAGDECL([pic_flag], [lt_prog_compiler_pic], [1], + [Additional compiler flags for building library objects]) + +_LT_TAGDECL([wl], [lt_prog_compiler_wl], [1], + [How to pass a linker flag through the compiler]) +# +# Check to make sure the static flag actually works. +# +wl=$_LT_TAGVAR(lt_prog_compiler_wl, $1) eval lt_tmp_static_flag=\"$_LT_TAGVAR(lt_prog_compiler_static, $1)\" +_LT_LINKER_OPTION([if $compiler static flag $lt_tmp_static_flag works], + _LT_TAGVAR(lt_cv_prog_compiler_static_works, $1), + $lt_tmp_static_flag, + [], + [_LT_TAGVAR(lt_prog_compiler_static, $1)=]) +_LT_TAGDECL([link_static_flag], [lt_prog_compiler_static], [1], + [Compiler flag to prevent dynamic linking]) +])# _LT_COMPILER_PIC + + +# _LT_LINKER_SHLIBS([TAGNAME]) +# ---------------------------- +# See if the linker supports building shared libraries. +m4_defun([_LT_LINKER_SHLIBS], +[AC_REQUIRE([LT_PATH_LD])dnl +AC_REQUIRE([LT_PATH_NM])dnl +m4_require([_LT_PATH_MANIFEST_TOOL])dnl +m4_require([_LT_FILEUTILS_DEFAULTS])dnl +m4_require([_LT_DECL_EGREP])dnl +m4_require([_LT_DECL_SED])dnl +m4_require([_LT_CMD_GLOBAL_SYMBOLS])dnl +m4_require([_LT_TAG_COMPILER])dnl +AC_MSG_CHECKING([whether the $compiler linker ($LD) supports shared libraries]) +m4_if([$1], [CXX], [ + _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' + _LT_TAGVAR(exclude_expsyms, $1)=['_GLOBAL_OFFSET_TABLE_|_GLOBAL__F[ID]_.*'] + case $host_os in + aix[[4-9]]*) + # If we're using GNU nm, then we don't want the "-C" option. + # -C means demangle to AIX nm, but means don't demangle with GNU nm + # Also, AIX nm treats weak defined symbols like other global defined + # symbols, whereas GNU nm marks them as "W". + if $NM -V 2>&1 | $GREP 'GNU' > /dev/null; then + _LT_TAGVAR(export_symbols_cmds, $1)='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W")) && ([substr](\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols' + else + _LT_TAGVAR(export_symbols_cmds, $1)='$NM -BCpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B")) && ([substr](\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols' + fi + ;; + pw32*) + _LT_TAGVAR(export_symbols_cmds, $1)="$ltdll_cmds" + ;; + cygwin* | mingw* | cegcc*) + case $cc_basename in + cl*) + _LT_TAGVAR(exclude_expsyms, $1)='_NULL_IMPORT_DESCRIPTOR|_IMPORT_DESCRIPTOR_.*' + ;; + *) + _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[[BCDGRS]][[ ]]/s/.*[[ ]]\([[^ ]]*\)/\1 DATA/;s/^.*[[ ]]__nm__\([[^ ]]*\)[[ ]][[^ ]]*/\1 DATA/;/^I[[ ]]/d;/^[[AITW]][[ ]]/s/.* //'\'' | sort | uniq > $export_symbols' + _LT_TAGVAR(exclude_expsyms, $1)=['[_]+GLOBAL_OFFSET_TABLE_|[_]+GLOBAL__[FID]_.*|[_]+head_[A-Za-z0-9_]+_dll|[A-Za-z0-9_]+_dll_iname'] + ;; + esac + ;; + linux* | k*bsd*-gnu | gnu*) + _LT_TAGVAR(link_all_deplibs, $1)=no + ;; + *) + _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' + ;; + esac +], [ + runpath_var= + _LT_TAGVAR(allow_undefined_flag, $1)= + _LT_TAGVAR(always_export_symbols, $1)=no + _LT_TAGVAR(archive_cmds, $1)= + _LT_TAGVAR(archive_expsym_cmds, $1)= + _LT_TAGVAR(compiler_needs_object, $1)=no + _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=no + _LT_TAGVAR(export_dynamic_flag_spec, $1)= + _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' + _LT_TAGVAR(hardcode_automatic, $1)=no + _LT_TAGVAR(hardcode_direct, $1)=no + _LT_TAGVAR(hardcode_direct_absolute, $1)=no + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)= + _LT_TAGVAR(hardcode_libdir_separator, $1)= + _LT_TAGVAR(hardcode_minus_L, $1)=no + _LT_TAGVAR(hardcode_shlibpath_var, $1)=unsupported + _LT_TAGVAR(inherit_rpath, $1)=no + _LT_TAGVAR(link_all_deplibs, $1)=unknown + _LT_TAGVAR(module_cmds, $1)= + _LT_TAGVAR(module_expsym_cmds, $1)= + _LT_TAGVAR(old_archive_from_new_cmds, $1)= + _LT_TAGVAR(old_archive_from_expsyms_cmds, $1)= + _LT_TAGVAR(thread_safe_flag_spec, $1)= + _LT_TAGVAR(whole_archive_flag_spec, $1)= + # include_expsyms should be a list of space-separated symbols to be *always* + # included in the symbol list + _LT_TAGVAR(include_expsyms, $1)= + # exclude_expsyms can be an extended regexp of symbols to exclude + # it will be wrapped by ` (' and `)$', so one must not match beginning or + # end of line. Example: `a|bc|.*d.*' will exclude the symbols `a' and `bc', + # as well as any symbol that contains `d'. + _LT_TAGVAR(exclude_expsyms, $1)=['_GLOBAL_OFFSET_TABLE_|_GLOBAL__F[ID]_.*'] + # Although _GLOBAL_OFFSET_TABLE_ is a valid symbol C name, most a.out + # platforms (ab)use it in PIC code, but their linkers get confused if + # the symbol is explicitly referenced. Since portable code cannot + # rely on this symbol name, it's probably fine to never include it in + # preloaded symbol tables. + # Exclude shared library initialization/finalization symbols. +dnl Note also adjust exclude_expsyms for C++ above. + extract_expsyms_cmds= + + case $host_os in + cygwin* | mingw* | pw32* | cegcc*) + # FIXME: the MSVC++ port hasn't been tested in a loooong time + # When not using gcc, we currently assume that we are using + # Microsoft Visual C++. + if test "$GCC" != yes; then + with_gnu_ld=no + fi + ;; + interix*) + # we just hope/assume this is gcc and not c89 (= MSVC++) + with_gnu_ld=yes + ;; + openbsd*) + with_gnu_ld=no + ;; + linux* | k*bsd*-gnu | gnu*) + _LT_TAGVAR(link_all_deplibs, $1)=no + ;; + esac + + _LT_TAGVAR(ld_shlibs, $1)=yes + + # On some targets, GNU ld is compatible enough with the native linker + # that we're better off using the native interface for both. + lt_use_gnu_ld_interface=no + if test "$with_gnu_ld" = yes; then + case $host_os in + aix*) + # The AIX port of GNU ld has always aspired to compatibility + # with the native linker. However, as the warning in the GNU ld + # block says, versions before 2.19.5* couldn't really create working + # shared libraries, regardless of the interface used. + case `$LD -v 2>&1` in + *\ \(GNU\ Binutils\)\ 2.19.5*) ;; + *\ \(GNU\ Binutils\)\ 2.[[2-9]]*) ;; + *\ \(GNU\ Binutils\)\ [[3-9]]*) ;; + *) + lt_use_gnu_ld_interface=yes + ;; + esac + ;; + *) + lt_use_gnu_ld_interface=yes + ;; + esac + fi + + if test "$lt_use_gnu_ld_interface" = yes; then + # If archive_cmds runs LD, not CC, wlarc should be empty + wlarc='${wl}' + + # Set some defaults for GNU ld with shared library support. These + # are reset later if shared libraries are not supported. Putting them + # here allows them to be overridden if necessary. + runpath_var=LD_RUN_PATH + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' + _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic' + # ancient GNU ld didn't support --whole-archive et. al. + if $LD --help 2>&1 | $GREP 'no-whole-archive' > /dev/null; then + _LT_TAGVAR(whole_archive_flag_spec, $1)="$wlarc"'--whole-archive$convenience '"$wlarc"'--no-whole-archive' + else + _LT_TAGVAR(whole_archive_flag_spec, $1)= + fi + supports_anon_versioning=no + case `$LD -v 2>&1` in + *GNU\ gold*) supports_anon_versioning=yes ;; + *\ [[01]].* | *\ 2.[[0-9]].* | *\ 2.10.*) ;; # catch versions < 2.11 + *\ 2.11.93.0.2\ *) supports_anon_versioning=yes ;; # RH7.3 ... + *\ 2.11.92.0.12\ *) supports_anon_versioning=yes ;; # Mandrake 8.2 ... + *\ 2.11.*) ;; # other 2.11 versions + *) supports_anon_versioning=yes ;; + esac + + # See if GNU ld supports shared libraries. + case $host_os in + aix[[3-9]]*) + # On AIX/PPC, the GNU linker is very broken + if test "$host_cpu" != ia64; then + _LT_TAGVAR(ld_shlibs, $1)=no + cat <<_LT_EOF 1>&2 + +*** Warning: the GNU linker, at least up to release 2.19, is reported +*** to be unable to reliably create shared libraries on AIX. +*** Therefore, libtool is disabling shared libraries support. If you +*** really care for shared libraries, you may want to install binutils +*** 2.20 or above, or modify your PATH so that a non-GNU linker is found. +*** You will then need to restart the configuration process. + +_LT_EOF + fi + ;; + + amigaos*) + case $host_cpu in + powerpc) + # see comment about AmigaOS4 .so support + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='' + ;; + m68k) + _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/a2ixlibrary.data~$ECHO "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$ECHO "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$ECHO "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$ECHO "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' + _LT_TAGVAR(hardcode_minus_L, $1)=yes + ;; + esac + ;; + + beos*) + if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then + _LT_TAGVAR(allow_undefined_flag, $1)=unsupported + # Joseph Beckenbach says some releases of gcc + # support --undefined. This deserves some investigation. FIXME + _LT_TAGVAR(archive_cmds, $1)='$CC -nostart $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + else + _LT_TAGVAR(ld_shlibs, $1)=no + fi + ;; + + cygwin* | mingw* | pw32* | cegcc*) + # _LT_TAGVAR(hardcode_libdir_flag_spec, $1) is actually meaningless, + # as there is no search path for DLLs. + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' + _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-all-symbols' + _LT_TAGVAR(allow_undefined_flag, $1)=unsupported + _LT_TAGVAR(always_export_symbols, $1)=no + _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes + _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[[BCDGRS]][[ ]]/s/.*[[ ]]\([[^ ]]*\)/\1 DATA/;s/^.*[[ ]]__nm__\([[^ ]]*\)[[ ]][[^ ]]*/\1 DATA/;/^I[[ ]]/d;/^[[AITW]][[ ]]/s/.* //'\'' | sort | uniq > $export_symbols' + _LT_TAGVAR(exclude_expsyms, $1)=['[_]+GLOBAL_OFFSET_TABLE_|[_]+GLOBAL__[FID]_.*|[_]+head_[A-Za-z0-9_]+_dll|[A-Za-z0-9_]+_dll_iname'] + + if $LD --help 2>&1 | $GREP 'auto-import' > /dev/null; then + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' + # If the export-symbols file already is a .def file (1st line + # is EXPORTS), use it as is; otherwise, prepend... + _LT_TAGVAR(archive_expsym_cmds, $1)='if test "x`$SED 1q $export_symbols`" = xEXPORTS; then + cp $export_symbols $output_objdir/$soname.def; + else + echo EXPORTS > $output_objdir/$soname.def; + cat $export_symbols >> $output_objdir/$soname.def; + fi~ + $CC -shared $output_objdir/$soname.def $libobjs $deplibs $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' + else + _LT_TAGVAR(ld_shlibs, $1)=no + fi + ;; + + haiku*) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + _LT_TAGVAR(link_all_deplibs, $1)=yes + ;; + + interix[[3-9]]*) + _LT_TAGVAR(hardcode_direct, $1)=no + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir' + _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' + # Hack: On Interix 3.x, we cannot compile PIC because of a broken gcc. + # Instead, shared libraries are loaded at an image base (0x10000000 by + # default) and relocated if they conflict, which is a slow very memory + # consuming and fragmenting process. To avoid this, we pick a random, + # 256 KiB-aligned image base between 0x50000000 and 0x6FFC0000 at link + # time. Moving up from 0x10000000 also allows more sbrk(2) space. + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='sed "s,^,_," $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--retain-symbols-file,$output_objdir/$soname.expsym ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' + ;; + + gnu* | linux* | tpf* | k*bsd*-gnu | kopensolaris*-gnu) + tmp_diet=no + if test "$host_os" = linux-dietlibc; then + case $cc_basename in + diet\ *) tmp_diet=yes;; # linux-dietlibc with static linking (!diet-dyn) + esac + fi + if $LD --help 2>&1 | $EGREP ': supported targets:.* elf' > /dev/null \ + && test "$tmp_diet" = no + then + tmp_addflag=' $pic_flag' + tmp_sharedflag='-shared' + case $cc_basename,$host_cpu in + pgcc*) # Portland Group C compiler + _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` ${wl}--no-whole-archive' + tmp_addflag=' $pic_flag' + ;; + pgf77* | pgf90* | pgf95* | pgfortran*) + # Portland Group f77 and f90 compilers + _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` ${wl}--no-whole-archive' + tmp_addflag=' $pic_flag -Mnomain' ;; + ecc*,ia64* | icc*,ia64*) # Intel C compiler on ia64 + tmp_addflag=' -i_dynamic' ;; + efc*,ia64* | ifort*,ia64*) # Intel Fortran compiler on ia64 + tmp_addflag=' -i_dynamic -nofor_main' ;; + ifc* | ifort*) # Intel Fortran compiler + tmp_addflag=' -nofor_main' ;; + lf95*) # Lahey Fortran 8.1 + _LT_TAGVAR(whole_archive_flag_spec, $1)= + tmp_sharedflag='--shared' ;; + xl[[cC]]* | bgxl[[cC]]* | mpixl[[cC]]*) # IBM XL C 8.0 on PPC (deal with xlf below) + tmp_sharedflag='-qmkshrobj' + tmp_addflag= ;; + nvcc*) # Cuda Compiler Driver 2.2 + _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` ${wl}--no-whole-archive' + _LT_TAGVAR(compiler_needs_object, $1)=yes + ;; + esac + case `$CC -V 2>&1 | sed 5q` in + *Sun\ C*) # Sun C 5.9 + _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive`new_convenience=; for conv in $convenience\"\"; do test -z \"$conv\" || new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` ${wl}--no-whole-archive' + _LT_TAGVAR(compiler_needs_object, $1)=yes + tmp_sharedflag='-G' ;; + *Sun\ F*) # Sun Fortran 8.3 + tmp_sharedflag='-G' ;; + esac + _LT_TAGVAR(archive_cmds, $1)='$CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + + if test "x$supports_anon_versioning" = xyes; then + _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $output_objdir/$libname.ver~ + cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ + echo "local: *; };" >> $output_objdir/$libname.ver~ + $CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-version-script ${wl}$output_objdir/$libname.ver -o $lib' + fi + + case $cc_basename in + xlf* | bgf* | bgxlf* | mpixlf*) + # IBM XL Fortran 10.1 on PPC cannot create shared libs itself + _LT_TAGVAR(whole_archive_flag_spec, $1)='--whole-archive$convenience --no-whole-archive' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' + _LT_TAGVAR(archive_cmds, $1)='$LD -shared $libobjs $deplibs $linker_flags -soname $soname -o $lib' + if test "x$supports_anon_versioning" = xyes; then + _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $output_objdir/$libname.ver~ + cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ + echo "local: *; };" >> $output_objdir/$libname.ver~ + $LD -shared $libobjs $deplibs $linker_flags -soname $soname -version-script $output_objdir/$libname.ver -o $lib' + fi + ;; + esac + else + _LT_TAGVAR(ld_shlibs, $1)=no + fi + ;; + + netbsd* | netbsdelf*-gnu) + if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then + _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable $libobjs $deplibs $linker_flags -o $lib' + wlarc= + else + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' + fi + ;; + + solaris*) + if $LD -v 2>&1 | $GREP 'BFD 2\.8' > /dev/null; then + _LT_TAGVAR(ld_shlibs, $1)=no + cat <<_LT_EOF 1>&2 + +*** Warning: The releases 2.8.* of the GNU linker cannot reliably +*** create shared libraries on Solaris systems. Therefore, libtool +*** is disabling shared libraries support. We urge you to upgrade GNU +*** binutils to release 2.9.1 or newer. Another option is to modify +*** your PATH or compiler configuration so that the native linker is +*** used, and then restart. + +_LT_EOF + elif $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' + else + _LT_TAGVAR(ld_shlibs, $1)=no + fi + ;; + + sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX*) + case `$LD -v 2>&1` in + *\ [[01]].* | *\ 2.[[0-9]].* | *\ 2.1[[0-5]].*) + _LT_TAGVAR(ld_shlibs, $1)=no + cat <<_LT_EOF 1>&2 + +*** Warning: Releases of the GNU linker prior to 2.16.91.0.3 can not +*** reliably create shared libraries on SCO systems. Therefore, libtool +*** is disabling shared libraries support. We urge you to upgrade GNU +*** binutils to release 2.16.91.0.3 or newer. Another option is to modify +*** your PATH or compiler configuration so that the native linker is +*** used, and then restart. + +_LT_EOF + ;; + *) + # For security reasons, it is highly recommended that you always + # use absolute paths for naming shared libraries, and exclude the + # DT_RUNPATH tag from executables and libraries. But doing so + # requires that you compile everything twice, which is a pain. + if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' + else + _LT_TAGVAR(ld_shlibs, $1)=no + fi + ;; + esac + ;; + + sunos4*) + _LT_TAGVAR(archive_cmds, $1)='$LD -assert pure-text -Bshareable -o $lib $libobjs $deplibs $linker_flags' + wlarc= + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; + + *) + if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' + else + _LT_TAGVAR(ld_shlibs, $1)=no + fi + ;; + esac + + if test "$_LT_TAGVAR(ld_shlibs, $1)" = no; then + runpath_var= + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)= + _LT_TAGVAR(export_dynamic_flag_spec, $1)= + _LT_TAGVAR(whole_archive_flag_spec, $1)= + fi + else + # PORTME fill in a description of your system's linker (not GNU ld) + case $host_os in + aix3*) + _LT_TAGVAR(allow_undefined_flag, $1)=unsupported + _LT_TAGVAR(always_export_symbols, $1)=yes + _LT_TAGVAR(archive_expsym_cmds, $1)='$LD -o $output_objdir/$soname $libobjs $deplibs $linker_flags -bE:$export_symbols -T512 -H512 -bM:SRE~$AR $AR_FLAGS $lib $output_objdir/$soname' + # Note: this linker hardcodes the directories in LIBPATH if there + # are no directories specified by -L. + _LT_TAGVAR(hardcode_minus_L, $1)=yes + if test "$GCC" = yes && test -z "$lt_prog_compiler_static"; then + # Neither direct hardcoding nor static linking is supported with a + # broken collect2. + _LT_TAGVAR(hardcode_direct, $1)=unsupported + fi + ;; + + aix[[4-9]]*) + if test "$host_cpu" = ia64; then + # On IA64, the linker does run time linking by default, so we don't + # have to do anything special. + aix_use_runtimelinking=no + exp_sym_flag='-Bexport' + no_entry_flag="" + else + # If we're using GNU nm, then we don't want the "-C" option. + # -C means demangle to AIX nm, but means don't demangle with GNU nm + # Also, AIX nm treats weak defined symbols like other global + # defined symbols, whereas GNU nm marks them as "W". + if $NM -V 2>&1 | $GREP 'GNU' > /dev/null; then + _LT_TAGVAR(export_symbols_cmds, $1)='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W")) && ([substr](\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols' + else + _LT_TAGVAR(export_symbols_cmds, $1)='$NM -BCpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B")) && ([substr](\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols' + fi + aix_use_runtimelinking=no + + # Test if we are trying to use run time linking or normal + # AIX style linking. If -brtl is somewhere in LDFLAGS, we + # need to do runtime linking. + case $host_os in aix4.[[23]]|aix4.[[23]].*|aix[[5-9]]*) + for ld_flag in $LDFLAGS; do + if (test $ld_flag = "-brtl" || test $ld_flag = "-Wl,-brtl"); then + aix_use_runtimelinking=yes + break + fi + done + ;; + esac + + exp_sym_flag='-bexport' + no_entry_flag='-bnoentry' + fi + + # When large executables or shared objects are built, AIX ld can + # have problems creating the table of contents. If linking a library + # or program results in "error TOC overflow" add -mminimal-toc to + # CXXFLAGS/CFLAGS for g++/gcc. In the cases where that is not + # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS. + + _LT_TAGVAR(archive_cmds, $1)='' + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_direct_absolute, $1)=yes + _LT_TAGVAR(hardcode_libdir_separator, $1)=':' + _LT_TAGVAR(link_all_deplibs, $1)=yes + _LT_TAGVAR(file_list_spec, $1)='${wl}-f,' + + if test "$GCC" = yes; then + case $host_os in aix4.[[012]]|aix4.[[012]].*) + # We only want to do this on AIX 4.2 and lower, the check + # below for broken collect2 doesn't work under 4.3+ + collect2name=`${CC} -print-prog-name=collect2` + if test -f "$collect2name" && + strings "$collect2name" | $GREP resolve_lib_name >/dev/null + then + # We have reworked collect2 + : + else + # We have old collect2 + _LT_TAGVAR(hardcode_direct, $1)=unsupported + # It fails to find uninstalled libraries when the uninstalled + # path is not listed in the libpath. Setting hardcode_minus_L + # to unsupported forces relinking + _LT_TAGVAR(hardcode_minus_L, $1)=yes + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)= + fi + ;; + esac + shared_flag='-shared' + if test "$aix_use_runtimelinking" = yes; then + shared_flag="$shared_flag "'${wl}-G' + fi + _LT_TAGVAR(link_all_deplibs, $1)=no + else + # not using gcc + if test "$host_cpu" = ia64; then + # VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release + # chokes on -Wl,-G. The following line is correct: + shared_flag='-G' + else + if test "$aix_use_runtimelinking" = yes; then + shared_flag='${wl}-G' + else + shared_flag='${wl}-bM:SRE' + fi + fi + fi + + _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-bexpall' + # It seems that -bexpall does not export symbols beginning with + # underscore (_), so it is better to generate a list of symbols to export. + _LT_TAGVAR(always_export_symbols, $1)=yes + if test "$aix_use_runtimelinking" = yes; then + # Warning - without using the other runtime loading flags (-brtl), + # -berok will link without error, but may produce a broken library. + _LT_TAGVAR(allow_undefined_flag, $1)='-berok' + # Determine the default libpath from the value encoded in an + # empty executable. + _LT_SYS_MODULE_PATH_AIX([$1]) + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-blibpath:$libdir:'"$aix_libpath" + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags `if test "x${allow_undefined_flag}" != "x"; then func_echo_all "${wl}${allow_undefined_flag}"; else :; fi` '"\${wl}$exp_sym_flag:\$export_symbols $shared_flag" + else + if test "$host_cpu" = ia64; then + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-R $libdir:/usr/lib:/lib' + _LT_TAGVAR(allow_undefined_flag, $1)="-z nodefs" + _LT_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags ${wl}${allow_undefined_flag} '"\${wl}$exp_sym_flag:\$export_symbols" + else + # Determine the default libpath from the value encoded in an + # empty executable. + _LT_SYS_MODULE_PATH_AIX([$1]) + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-blibpath:$libdir:'"$aix_libpath" + # Warning - without using the other run time loading flags, + # -berok will link without error, but may produce a broken library. + _LT_TAGVAR(no_undefined_flag, $1)=' ${wl}-bernotok' + _LT_TAGVAR(allow_undefined_flag, $1)=' ${wl}-berok' + if test "$with_gnu_ld" = yes; then + # We only use this code for GNU lds that support --whole-archive. + _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive$convenience ${wl}--no-whole-archive' + else + # Exported symbols can be pulled into shared objects from archives + _LT_TAGVAR(whole_archive_flag_spec, $1)='$convenience' + fi + _LT_TAGVAR(archive_cmds_need_lc, $1)=yes + # This is similar to how AIX traditionally builds its shared libraries. + _LT_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs ${wl}-bnoentry $compiler_flags ${wl}-bE:$export_symbols${allow_undefined_flag}~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$soname' + fi + fi + ;; + + amigaos*) + case $host_cpu in + powerpc) + # see comment about AmigaOS4 .so support + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='' + ;; + m68k) + _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/a2ixlibrary.data~$ECHO "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$ECHO "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$ECHO "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$ECHO "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' + _LT_TAGVAR(hardcode_minus_L, $1)=yes + ;; + esac + ;; + + bsdi[[45]]*) + _LT_TAGVAR(export_dynamic_flag_spec, $1)=-rdynamic + ;; + + cygwin* | mingw* | pw32* | cegcc*) + # When not using gcc, we currently assume that we are using + # Microsoft Visual C++. + # hardcode_libdir_flag_spec is actually meaningless, as there is + # no search path for DLLs. + case $cc_basename in + cl*) + # Native MSVC + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)=' ' + _LT_TAGVAR(allow_undefined_flag, $1)=unsupported + _LT_TAGVAR(always_export_symbols, $1)=yes + _LT_TAGVAR(file_list_spec, $1)='@' + # Tell ltmain to make .lib files, not .a files. + libext=lib + # Tell ltmain to make .dll files, not .so files. + shrext_cmds=".dll" + # FIXME: Setting linknames here is a bad hack. + _LT_TAGVAR(archive_cmds, $1)='$CC -o $output_objdir/$soname $libobjs $compiler_flags $deplibs -Wl,-dll~linknames=' + _LT_TAGVAR(archive_expsym_cmds, $1)='if test "x`$SED 1q $export_symbols`" = xEXPORTS; then + sed -n -e 's/\\\\\\\(.*\\\\\\\)/-link\\\ -EXPORT:\\\\\\\1/' -e '1\\\!p' < $export_symbols > $output_objdir/$soname.exp; + else + sed -e 's/\\\\\\\(.*\\\\\\\)/-link\\\ -EXPORT:\\\\\\\1/' < $export_symbols > $output_objdir/$soname.exp; + fi~ + $CC -o $tool_output_objdir$soname $libobjs $compiler_flags $deplibs "@$tool_output_objdir$soname.exp" -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~ + linknames=' + # The linker will not automatically build a static lib if we build a DLL. + # _LT_TAGVAR(old_archive_from_new_cmds, $1)='true' + _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes + _LT_TAGVAR(exclude_expsyms, $1)='_NULL_IMPORT_DESCRIPTOR|_IMPORT_DESCRIPTOR_.*' + _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[[BCDGRS]][[ ]]/s/.*[[ ]]\([[^ ]]*\)/\1,DATA/'\'' | $SED -e '\''/^[[AITW]][[ ]]/s/.*[[ ]]//'\'' | sort | uniq > $export_symbols' + # Don't use ranlib + _LT_TAGVAR(old_postinstall_cmds, $1)='chmod 644 $oldlib' + _LT_TAGVAR(postlink_cmds, $1)='lt_outputfile="@OUTPUT@"~ + lt_tool_outputfile="@TOOL_OUTPUT@"~ + case $lt_outputfile in + *.exe|*.EXE) ;; + *) + lt_outputfile="$lt_outputfile.exe" + lt_tool_outputfile="$lt_tool_outputfile.exe" + ;; + esac~ + if test "$MANIFEST_TOOL" != ":" && test -f "$lt_outputfile.manifest"; then + $MANIFEST_TOOL -manifest "$lt_tool_outputfile.manifest" -outputresource:"$lt_tool_outputfile" || exit 1; + $RM "$lt_outputfile.manifest"; + fi' + ;; + *) + # Assume MSVC wrapper + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)=' ' + _LT_TAGVAR(allow_undefined_flag, $1)=unsupported + # Tell ltmain to make .lib files, not .a files. + libext=lib + # Tell ltmain to make .dll files, not .so files. + shrext_cmds=".dll" + # FIXME: Setting linknames here is a bad hack. + _LT_TAGVAR(archive_cmds, $1)='$CC -o $lib $libobjs $compiler_flags `func_echo_all "$deplibs" | $SED '\''s/ -lc$//'\''` -link -dll~linknames=' + # The linker will automatically build a .lib file if we build a DLL. + _LT_TAGVAR(old_archive_from_new_cmds, $1)='true' + # FIXME: Should let the user specify the lib program. + _LT_TAGVAR(old_archive_cmds, $1)='lib -OUT:$oldlib$oldobjs$old_deplibs' + _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes + ;; + esac + ;; + + darwin* | rhapsody*) + _LT_DARWIN_LINKER_FEATURES($1) + ;; + + dgux*) + _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; + + # FreeBSD 2.2.[012] allows us to include c++rt0.o to get C++ constructor + # support. Future versions do this automatically, but an explicit c++rt0.o + # does not break anything, and helps significantly (at the cost of a little + # extra space). + freebsd2.2*) + _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags /usr/lib/c++rt0.o' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; + + # Unfortunately, older versions of FreeBSD 2 do not have this feature. + freebsd2.*) + _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_minus_L, $1)=yes + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; + + # FreeBSD 3 and greater uses gcc -shared to do shared libraries. + freebsd* | dragonfly*) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; + + hpux9*) + if test "$GCC" = yes; then + _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$CC -shared $pic_flag ${wl}+b ${wl}$install_libdir -o $output_objdir/$soname $libobjs $deplibs $compiler_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib' + else + _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$LD -b +b $install_libdir -o $output_objdir/$soname $libobjs $deplibs $linker_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib' + fi + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}+b ${wl}$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=: + _LT_TAGVAR(hardcode_direct, $1)=yes + + # hardcode_minus_L: Not really in the search PATH, + # but as the default location of the library. + _LT_TAGVAR(hardcode_minus_L, $1)=yes + _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' + ;; + + hpux10*) + if test "$GCC" = yes && test "$with_gnu_ld" = no; then + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags' + else + _LT_TAGVAR(archive_cmds, $1)='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags' + fi + if test "$with_gnu_ld" = no; then + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}+b ${wl}$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=: + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_direct_absolute, $1)=yes + _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' + # hardcode_minus_L: Not really in the search PATH, + # but as the default location of the library. + _LT_TAGVAR(hardcode_minus_L, $1)=yes + fi + ;; + + hpux11*) + if test "$GCC" = yes && test "$with_gnu_ld" = no; then + case $host_cpu in + hppa*64*) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}+h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags' + ;; + ia64*) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags' + ;; + *) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags' + ;; + esac + else + case $host_cpu in + hppa*64*) + _LT_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags' + ;; + ia64*) + _LT_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags' + ;; + *) + m4_if($1, [], [ + # Older versions of the 11.00 compiler do not understand -b yet + # (HP92453-01 A.11.01.20 doesn't, HP92453-01 B.11.X.35175-35176.GP does) + _LT_LINKER_OPTION([if $CC understands -b], + _LT_TAGVAR(lt_cv_prog_compiler__b, $1), [-b], + [_LT_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags'], + [_LT_TAGVAR(archive_cmds, $1)='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags'])], + [_LT_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags']) + ;; + esac + fi + if test "$with_gnu_ld" = no; then + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}+b ${wl}$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=: + + case $host_cpu in + hppa*64*|ia64*) + _LT_TAGVAR(hardcode_direct, $1)=no + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; + *) + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_direct_absolute, $1)=yes + _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' + + # hardcode_minus_L: Not really in the search PATH, + # but as the default location of the library. + _LT_TAGVAR(hardcode_minus_L, $1)=yes + ;; + esac + fi + ;; + + irix5* | irix6* | nonstopux*) + if test "$GCC" = yes; then + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' + # Try to use the -exported_symbol ld option, if it does not + # work, assume that -exports_file does not work either and + # implicitly export all symbols. + # This should be the same for all languages, so no per-tag cache variable. + AC_CACHE_CHECK([whether the $host_os linker accepts -exported_symbol], + [lt_cv_irix_exported_symbol], + [save_LDFLAGS="$LDFLAGS" + LDFLAGS="$LDFLAGS -shared ${wl}-exported_symbol ${wl}foo ${wl}-update_registry ${wl}/dev/null" + AC_LINK_IFELSE( + [AC_LANG_SOURCE( + [AC_LANG_CASE([C], [[int foo (void) { return 0; }]], + [C++], [[int foo (void) { return 0; }]], + [Fortran 77], [[ + subroutine foo + end]], + [Fortran], [[ + subroutine foo + end]])])], + [lt_cv_irix_exported_symbol=yes], + [lt_cv_irix_exported_symbol=no]) + LDFLAGS="$save_LDFLAGS"]) + if test "$lt_cv_irix_exported_symbol" = yes; then + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations ${wl}-exports_file ${wl}$export_symbols -o $lib' + fi + else + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -exports_file $export_symbols -o $lib' + fi + _LT_TAGVAR(archive_cmds_need_lc, $1)='no' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=: + _LT_TAGVAR(inherit_rpath, $1)=yes + _LT_TAGVAR(link_all_deplibs, $1)=yes + ;; + + netbsd* | netbsdelf*-gnu) + if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then + _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' # a.out + else + _LT_TAGVAR(archive_cmds, $1)='$LD -shared -o $lib $libobjs $deplibs $linker_flags' # ELF + fi + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; + + newsos6) + _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=: + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; + + *nto* | *qnx*) + ;; + + openbsd*) + if test -f /usr/libexec/ld.so; then + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + _LT_TAGVAR(hardcode_direct_absolute, $1)=yes + if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags ${wl}-retain-symbols-file,$export_symbols' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir' + _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' + else + case $host_os in + openbsd[[01]].* | openbsd2.[[0-7]] | openbsd2.[[0-7]].*) + _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' + ;; + *) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir' + ;; + esac + fi + else + _LT_TAGVAR(ld_shlibs, $1)=no + fi + ;; + + os2*) + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' + _LT_TAGVAR(hardcode_minus_L, $1)=yes + _LT_TAGVAR(allow_undefined_flag, $1)=unsupported + _LT_TAGVAR(archive_cmds, $1)='$ECHO "LIBRARY $libname INITINSTANCE" > $output_objdir/$libname.def~$ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~echo DATA >> $output_objdir/$libname.def~echo " SINGLE NONSHARED" >> $output_objdir/$libname.def~echo EXPORTS >> $output_objdir/$libname.def~emxexp $libobjs >> $output_objdir/$libname.def~$CC -Zdll -Zcrtdll -o $lib $libobjs $deplibs $compiler_flags $output_objdir/$libname.def' + _LT_TAGVAR(old_archive_from_new_cmds, $1)='emximp -o $output_objdir/$libname.a $output_objdir/$libname.def' + ;; + + osf3*) + if test "$GCC" = yes; then + _LT_TAGVAR(allow_undefined_flag, $1)=' ${wl}-expect_unresolved ${wl}\*' + _LT_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' + else + _LT_TAGVAR(allow_undefined_flag, $1)=' -expect_unresolved \*' + _LT_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib' + fi + _LT_TAGVAR(archive_cmds_need_lc, $1)='no' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=: + ;; + + osf4* | osf5*) # as osf3* with the addition of -msym flag + if test "$GCC" = yes; then + _LT_TAGVAR(allow_undefined_flag, $1)=' ${wl}-expect_unresolved ${wl}\*' + _LT_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $pic_flag $libobjs $deplibs $compiler_flags ${wl}-msym ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' + else + _LT_TAGVAR(allow_undefined_flag, $1)=' -expect_unresolved \*' + _LT_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags -msym -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done; printf "%s\\n" "-hidden">> $lib.exp~ + $CC -shared${allow_undefined_flag} ${wl}-input ${wl}$lib.exp $compiler_flags $libobjs $deplibs -soname $soname `test -n "$verstring" && $ECHO "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib~$RM $lib.exp' + + # Both c and cxx compiler support -rpath directly + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-rpath $libdir' + fi + _LT_TAGVAR(archive_cmds_need_lc, $1)='no' + _LT_TAGVAR(hardcode_libdir_separator, $1)=: + ;; + + solaris*) + _LT_TAGVAR(no_undefined_flag, $1)=' -z defs' + if test "$GCC" = yes; then + wlarc='${wl}' + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag ${wl}-z ${wl}text ${wl}-h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ + $CC -shared $pic_flag ${wl}-z ${wl}text ${wl}-M ${wl}$lib.exp ${wl}-h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp' + else + case `$CC -V 2>&1` in + *"Compilers 5.0"*) + wlarc='' + _LT_TAGVAR(archive_cmds, $1)='$LD -G${allow_undefined_flag} -h $soname -o $lib $libobjs $deplibs $linker_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ + $LD -G${allow_undefined_flag} -M $lib.exp -h $soname -o $lib $libobjs $deplibs $linker_flags~$RM $lib.exp' + ;; + *) + wlarc='${wl}' + _LT_TAGVAR(archive_cmds, $1)='$CC -G${allow_undefined_flag} -h $soname -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ + $CC -G${allow_undefined_flag} -M $lib.exp -h $soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp' + ;; + esac + fi + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + case $host_os in + solaris2.[[0-5]] | solaris2.[[0-5]].*) ;; + *) + # The compiler driver will combine and reorder linker options, + # but understands `-z linker_flag'. GCC discards it without `$wl', + # but is careful enough not to reorder. + # Supported since Solaris 2.6 (maybe 2.5.1?) + if test "$GCC" = yes; then + _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}-z ${wl}allextract$convenience ${wl}-z ${wl}defaultextract' + else + _LT_TAGVAR(whole_archive_flag_spec, $1)='-z allextract$convenience -z defaultextract' + fi + ;; + esac + _LT_TAGVAR(link_all_deplibs, $1)=yes + ;; + + sunos4*) + if test "x$host_vendor" = xsequent; then + # Use $CC to link under sequent, because it throws in some extra .o + # files that make .init and .fini sections work. + _LT_TAGVAR(archive_cmds, $1)='$CC -G ${wl}-h $soname -o $lib $libobjs $deplibs $compiler_flags' + else + _LT_TAGVAR(archive_cmds, $1)='$LD -assert pure-text -Bstatic -o $lib $libobjs $deplibs $linker_flags' + fi + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_minus_L, $1)=yes + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; + + sysv4) + case $host_vendor in + sni) + _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + _LT_TAGVAR(hardcode_direct, $1)=yes # is this really true??? + ;; + siemens) + ## LD is ld it makes a PLAMLIB + ## CC just makes a GrossModule. + _LT_TAGVAR(archive_cmds, $1)='$LD -G -o $lib $libobjs $deplibs $linker_flags' + _LT_TAGVAR(reload_cmds, $1)='$CC -r -o $output$reload_objs' + _LT_TAGVAR(hardcode_direct, $1)=no + ;; + motorola) + _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + _LT_TAGVAR(hardcode_direct, $1)=no #Motorola manual says yes, but my tests say they lie + ;; + esac + runpath_var='LD_RUN_PATH' + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; + + sysv4.3*) + _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + _LT_TAGVAR(export_dynamic_flag_spec, $1)='-Bexport' + ;; + + sysv4*MP*) + if test -d /usr/nec; then + _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + runpath_var=LD_RUN_PATH + hardcode_runpath_var=yes + _LT_TAGVAR(ld_shlibs, $1)=yes + fi + ;; + + sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[[01]].[[10]]* | unixware7* | sco3.2v5.0.[[024]]*) + _LT_TAGVAR(no_undefined_flag, $1)='${wl}-z,text' + _LT_TAGVAR(archive_cmds_need_lc, $1)=no + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + runpath_var='LD_RUN_PATH' + + if test "$GCC" = yes; then + _LT_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + else + _LT_TAGVAR(archive_cmds, $1)='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + fi + ;; + + sysv5* | sco3.2v5* | sco5v6*) + # Note: We can NOT use -z defs as we might desire, because we do not + # link with -lc, and that would cause any symbols used from libc to + # always be unresolved, which means just about no library would + # ever link correctly. If we're not using GNU ld we use -z text + # though, which does catch some bad symbols but isn't as heavy-handed + # as -z defs. + _LT_TAGVAR(no_undefined_flag, $1)='${wl}-z,text' + _LT_TAGVAR(allow_undefined_flag, $1)='${wl}-z,nodefs' + _LT_TAGVAR(archive_cmds_need_lc, $1)=no + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-R,$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=':' + _LT_TAGVAR(link_all_deplibs, $1)=yes + _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-Bexport' + runpath_var='LD_RUN_PATH' + + if test "$GCC" = yes; then + _LT_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + else + _LT_TAGVAR(archive_cmds, $1)='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + fi + ;; + + uts4*) + _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; + + *) + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + esac + + if test x$host_vendor = xsni; then + case $host in + sysv4 | sysv4.2uw2* | sysv4.3* | sysv5*) + _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-Blargedynsym' + ;; + esac + fi + fi +]) +AC_MSG_RESULT([$_LT_TAGVAR(ld_shlibs, $1)]) +test "$_LT_TAGVAR(ld_shlibs, $1)" = no && can_build_shared=no + +_LT_TAGVAR(with_gnu_ld, $1)=$with_gnu_ld + +_LT_DECL([], [libext], [0], [Old archive suffix (normally "a")])dnl +_LT_DECL([], [shrext_cmds], [1], [Shared library suffix (normally ".so")])dnl +_LT_DECL([], [extract_expsyms_cmds], [2], + [The commands to extract the exported symbol list from a shared archive]) + +# +# Do we need to explicitly link libc? +# +case "x$_LT_TAGVAR(archive_cmds_need_lc, $1)" in +x|xyes) + # Assume -lc should be added + _LT_TAGVAR(archive_cmds_need_lc, $1)=yes + + if test "$enable_shared" = yes && test "$GCC" = yes; then + case $_LT_TAGVAR(archive_cmds, $1) in + *'~'*) + # FIXME: we may have to deal with multi-command sequences. + ;; + '$CC '*) + # Test whether the compiler implicitly links with -lc since on some + # systems, -lgcc has to come before -lc. If gcc already passes -lc + # to ld, don't add -lc before -lgcc. + AC_CACHE_CHECK([whether -lc should be explicitly linked in], + [lt_cv_]_LT_TAGVAR(archive_cmds_need_lc, $1), + [$RM conftest* + echo "$lt_simple_compile_test_code" > conftest.$ac_ext + + if AC_TRY_EVAL(ac_compile) 2>conftest.err; then + soname=conftest + lib=conftest + libobjs=conftest.$ac_objext + deplibs= + wl=$_LT_TAGVAR(lt_prog_compiler_wl, $1) + pic_flag=$_LT_TAGVAR(lt_prog_compiler_pic, $1) + compiler_flags=-v + linker_flags=-v + verstring= + output_objdir=. + libname=conftest + lt_save_allow_undefined_flag=$_LT_TAGVAR(allow_undefined_flag, $1) + _LT_TAGVAR(allow_undefined_flag, $1)= + if AC_TRY_EVAL(_LT_TAGVAR(archive_cmds, $1) 2\>\&1 \| $GREP \" -lc \" \>/dev/null 2\>\&1) + then + lt_cv_[]_LT_TAGVAR(archive_cmds_need_lc, $1)=no + else + lt_cv_[]_LT_TAGVAR(archive_cmds_need_lc, $1)=yes + fi + _LT_TAGVAR(allow_undefined_flag, $1)=$lt_save_allow_undefined_flag + else + cat conftest.err 1>&5 + fi + $RM conftest* + ]) + _LT_TAGVAR(archive_cmds_need_lc, $1)=$lt_cv_[]_LT_TAGVAR(archive_cmds_need_lc, $1) + ;; + esac + fi + ;; +esac + +_LT_TAGDECL([build_libtool_need_lc], [archive_cmds_need_lc], [0], + [Whether or not to add -lc for building shared libraries]) +_LT_TAGDECL([allow_libtool_libs_with_static_runtimes], + [enable_shared_with_static_runtimes], [0], + [Whether or not to disallow shared libs when runtime libs are static]) +_LT_TAGDECL([], [export_dynamic_flag_spec], [1], + [Compiler flag to allow reflexive dlopens]) +_LT_TAGDECL([], [whole_archive_flag_spec], [1], + [Compiler flag to generate shared objects directly from archives]) +_LT_TAGDECL([], [compiler_needs_object], [1], + [Whether the compiler copes with passing no objects directly]) +_LT_TAGDECL([], [old_archive_from_new_cmds], [2], + [Create an old-style archive from a shared archive]) +_LT_TAGDECL([], [old_archive_from_expsyms_cmds], [2], + [Create a temporary old-style archive to link instead of a shared archive]) +_LT_TAGDECL([], [archive_cmds], [2], [Commands used to build a shared archive]) +_LT_TAGDECL([], [archive_expsym_cmds], [2]) +_LT_TAGDECL([], [module_cmds], [2], + [Commands used to build a loadable module if different from building + a shared archive.]) +_LT_TAGDECL([], [module_expsym_cmds], [2]) +_LT_TAGDECL([], [with_gnu_ld], [1], + [Whether we are building with GNU ld or not]) +_LT_TAGDECL([], [allow_undefined_flag], [1], + [Flag that allows shared libraries with undefined symbols to be built]) +_LT_TAGDECL([], [no_undefined_flag], [1], + [Flag that enforces no undefined symbols]) +_LT_TAGDECL([], [hardcode_libdir_flag_spec], [1], + [Flag to hardcode $libdir into a binary during linking. + This must work even if $libdir does not exist]) +_LT_TAGDECL([], [hardcode_libdir_separator], [1], + [Whether we need a single "-rpath" flag with a separated argument]) +_LT_TAGDECL([], [hardcode_direct], [0], + [Set to "yes" if using DIR/libNAME${shared_ext} during linking hardcodes + DIR into the resulting binary]) +_LT_TAGDECL([], [hardcode_direct_absolute], [0], + [Set to "yes" if using DIR/libNAME${shared_ext} during linking hardcodes + DIR into the resulting binary and the resulting library dependency is + "absolute", i.e impossible to change by setting ${shlibpath_var} if the + library is relocated]) +_LT_TAGDECL([], [hardcode_minus_L], [0], + [Set to "yes" if using the -LDIR flag during linking hardcodes DIR + into the resulting binary]) +_LT_TAGDECL([], [hardcode_shlibpath_var], [0], + [Set to "yes" if using SHLIBPATH_VAR=DIR during linking hardcodes DIR + into the resulting binary]) +_LT_TAGDECL([], [hardcode_automatic], [0], + [Set to "yes" if building a shared library automatically hardcodes DIR + into the library and all subsequent libraries and executables linked + against it]) +_LT_TAGDECL([], [inherit_rpath], [0], + [Set to yes if linker adds runtime paths of dependent libraries + to runtime path list]) +_LT_TAGDECL([], [link_all_deplibs], [0], + [Whether libtool must link a program against all its dependency libraries]) +_LT_TAGDECL([], [always_export_symbols], [0], + [Set to "yes" if exported symbols are required]) +_LT_TAGDECL([], [export_symbols_cmds], [2], + [The commands to list exported symbols]) +_LT_TAGDECL([], [exclude_expsyms], [1], + [Symbols that should not be listed in the preloaded symbols]) +_LT_TAGDECL([], [include_expsyms], [1], + [Symbols that must always be exported]) +_LT_TAGDECL([], [prelink_cmds], [2], + [Commands necessary for linking programs (against libraries) with templates]) +_LT_TAGDECL([], [postlink_cmds], [2], + [Commands necessary for finishing linking programs]) +_LT_TAGDECL([], [file_list_spec], [1], + [Specify filename containing input files]) +dnl FIXME: Not yet implemented +dnl _LT_TAGDECL([], [thread_safe_flag_spec], [1], +dnl [Compiler flag to generate thread safe objects]) +])# _LT_LINKER_SHLIBS + + +# _LT_LANG_C_CONFIG([TAG]) +# ------------------------ +# Ensure that the configuration variables for a C compiler are suitably +# defined. These variables are subsequently used by _LT_CONFIG to write +# the compiler configuration to `libtool'. +m4_defun([_LT_LANG_C_CONFIG], +[m4_require([_LT_DECL_EGREP])dnl +lt_save_CC="$CC" +AC_LANG_PUSH(C) + +# Source file extension for C test sources. +ac_ext=c + +# Object file extension for compiled C test sources. +objext=o +_LT_TAGVAR(objext, $1)=$objext + +# Code to be used in simple compile tests +lt_simple_compile_test_code="int some_variable = 0;" + +# Code to be used in simple link tests +lt_simple_link_test_code='int main(){return(0);}' + +_LT_TAG_COMPILER +# Save the default compiler, since it gets overwritten when the other +# tags are being tested, and _LT_TAGVAR(compiler, []) is a NOP. +compiler_DEFAULT=$CC + +# save warnings/boilerplate of simple test code +_LT_COMPILER_BOILERPLATE +_LT_LINKER_BOILERPLATE + +## CAVEAT EMPTOR: +## There is no encapsulation within the following macros, do not change +## the running order or otherwise move them around unless you know exactly +## what you are doing... +if test -n "$compiler"; then + _LT_COMPILER_NO_RTTI($1) + _LT_COMPILER_PIC($1) + _LT_COMPILER_C_O($1) + _LT_COMPILER_FILE_LOCKS($1) + _LT_LINKER_SHLIBS($1) + _LT_SYS_DYNAMIC_LINKER($1) + _LT_LINKER_HARDCODE_LIBPATH($1) + LT_SYS_DLOPEN_SELF + _LT_CMD_STRIPLIB + + # Report which library types will actually be built + AC_MSG_CHECKING([if libtool supports shared libraries]) + AC_MSG_RESULT([$can_build_shared]) + + AC_MSG_CHECKING([whether to build shared libraries]) + test "$can_build_shared" = "no" && enable_shared=no + + # On AIX, shared libraries and static libraries use the same namespace, and + # are all built from PIC. + case $host_os in + aix3*) + test "$enable_shared" = yes && enable_static=no + if test -n "$RANLIB"; then + archive_cmds="$archive_cmds~\$RANLIB \$lib" + postinstall_cmds='$RANLIB $lib' + fi + ;; + + aix[[4-9]]*) + if test "$host_cpu" != ia64 && test "$aix_use_runtimelinking" = no ; then + test "$enable_shared" = yes && enable_static=no + fi + ;; + esac + AC_MSG_RESULT([$enable_shared]) + + AC_MSG_CHECKING([whether to build static libraries]) + # Make sure either enable_shared or enable_static is yes. + test "$enable_shared" = yes || enable_static=yes + AC_MSG_RESULT([$enable_static]) + + _LT_CONFIG($1) +fi +AC_LANG_POP +CC="$lt_save_CC" +])# _LT_LANG_C_CONFIG + + +# _LT_LANG_CXX_CONFIG([TAG]) +# -------------------------- +# Ensure that the configuration variables for a C++ compiler are suitably +# defined. These variables are subsequently used by _LT_CONFIG to write +# the compiler configuration to `libtool'. +m4_defun([_LT_LANG_CXX_CONFIG], +[m4_require([_LT_FILEUTILS_DEFAULTS])dnl +m4_require([_LT_DECL_EGREP])dnl +m4_require([_LT_PATH_MANIFEST_TOOL])dnl +if test -n "$CXX" && ( test "X$CXX" != "Xno" && + ( (test "X$CXX" = "Xg++" && `g++ -v >/dev/null 2>&1` ) || + (test "X$CXX" != "Xg++"))) ; then + AC_PROG_CXXCPP +else + _lt_caught_CXX_error=yes +fi + +AC_LANG_PUSH(C++) +_LT_TAGVAR(archive_cmds_need_lc, $1)=no +_LT_TAGVAR(allow_undefined_flag, $1)= +_LT_TAGVAR(always_export_symbols, $1)=no +_LT_TAGVAR(archive_expsym_cmds, $1)= +_LT_TAGVAR(compiler_needs_object, $1)=no +_LT_TAGVAR(export_dynamic_flag_spec, $1)= +_LT_TAGVAR(hardcode_direct, $1)=no +_LT_TAGVAR(hardcode_direct_absolute, $1)=no +_LT_TAGVAR(hardcode_libdir_flag_spec, $1)= +_LT_TAGVAR(hardcode_libdir_separator, $1)= +_LT_TAGVAR(hardcode_minus_L, $1)=no +_LT_TAGVAR(hardcode_shlibpath_var, $1)=unsupported +_LT_TAGVAR(hardcode_automatic, $1)=no +_LT_TAGVAR(inherit_rpath, $1)=no +_LT_TAGVAR(module_cmds, $1)= +_LT_TAGVAR(module_expsym_cmds, $1)= +_LT_TAGVAR(link_all_deplibs, $1)=unknown +_LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds +_LT_TAGVAR(reload_flag, $1)=$reload_flag +_LT_TAGVAR(reload_cmds, $1)=$reload_cmds +_LT_TAGVAR(no_undefined_flag, $1)= +_LT_TAGVAR(whole_archive_flag_spec, $1)= +_LT_TAGVAR(enable_shared_with_static_runtimes, $1)=no + +# Source file extension for C++ test sources. +ac_ext=cpp + +# Object file extension for compiled C++ test sources. +objext=o +_LT_TAGVAR(objext, $1)=$objext + +# No sense in running all these tests if we already determined that +# the CXX compiler isn't working. Some variables (like enable_shared) +# are currently assumed to apply to all compilers on this platform, +# and will be corrupted by setting them based on a non-working compiler. +if test "$_lt_caught_CXX_error" != yes; then + # Code to be used in simple compile tests + lt_simple_compile_test_code="int some_variable = 0;" + + # Code to be used in simple link tests + lt_simple_link_test_code='int main(int, char *[[]]) { return(0); }' + + # ltmain only uses $CC for tagged configurations so make sure $CC is set. + _LT_TAG_COMPILER + + # save warnings/boilerplate of simple test code + _LT_COMPILER_BOILERPLATE + _LT_LINKER_BOILERPLATE + + # Allow CC to be a program name with arguments. + lt_save_CC=$CC + lt_save_CFLAGS=$CFLAGS + lt_save_LD=$LD + lt_save_GCC=$GCC + GCC=$GXX + lt_save_with_gnu_ld=$with_gnu_ld + lt_save_path_LD=$lt_cv_path_LD + if test -n "${lt_cv_prog_gnu_ldcxx+set}"; then + lt_cv_prog_gnu_ld=$lt_cv_prog_gnu_ldcxx + else + $as_unset lt_cv_prog_gnu_ld + fi + if test -n "${lt_cv_path_LDCXX+set}"; then + lt_cv_path_LD=$lt_cv_path_LDCXX + else + $as_unset lt_cv_path_LD + fi + test -z "${LDCXX+set}" || LD=$LDCXX + CC=${CXX-"c++"} + CFLAGS=$CXXFLAGS + compiler=$CC + _LT_TAGVAR(compiler, $1)=$CC + _LT_CC_BASENAME([$compiler]) + + if test -n "$compiler"; then + # We don't want -fno-exception when compiling C++ code, so set the + # no_builtin_flag separately + if test "$GXX" = yes; then + _LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)=' -fno-builtin' + else + _LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)= + fi + + if test "$GXX" = yes; then + # Set up default GNU C++ configuration + + LT_PATH_LD + + # Check if GNU C++ uses GNU ld as the underlying linker, since the + # archiving commands below assume that GNU ld is being used. + if test "$with_gnu_ld" = yes; then + _LT_TAGVAR(archive_cmds, $1)='$CC $pic_flag -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC $pic_flag -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' + + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' + _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic' + + # If archive_cmds runs LD, not CC, wlarc should be empty + # XXX I think wlarc can be eliminated in ltcf-cxx, but I need to + # investigate it a little bit more. (MM) + wlarc='${wl}' + + # ancient GNU ld didn't support --whole-archive et. al. + if eval "`$CC -print-prog-name=ld` --help 2>&1" | + $GREP 'no-whole-archive' > /dev/null; then + _LT_TAGVAR(whole_archive_flag_spec, $1)="$wlarc"'--whole-archive$convenience '"$wlarc"'--no-whole-archive' + else + _LT_TAGVAR(whole_archive_flag_spec, $1)= + fi + else + with_gnu_ld=no + wlarc= + + # A generic and very simple default shared library creation + # command for GNU C++ for the case where it uses the native + # linker, instead of GNU ld. If possible, this setting should + # overridden to take advantage of the native linker features on + # the platform it is being used on. + _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $lib' + fi + + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"' + + else + GXX=no + with_gnu_ld=no + wlarc= + fi + + # PORTME: fill in a description of your system's C++ link characteristics + AC_MSG_CHECKING([whether the $compiler linker ($LD) supports shared libraries]) + _LT_TAGVAR(ld_shlibs, $1)=yes + case $host_os in + aix3*) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + aix[[4-9]]*) + if test "$host_cpu" = ia64; then + # On IA64, the linker does run time linking by default, so we don't + # have to do anything special. + aix_use_runtimelinking=no + exp_sym_flag='-Bexport' + no_entry_flag="" + else + aix_use_runtimelinking=no + + # Test if we are trying to use run time linking or normal + # AIX style linking. If -brtl is somewhere in LDFLAGS, we + # need to do runtime linking. + case $host_os in aix4.[[23]]|aix4.[[23]].*|aix[[5-9]]*) + for ld_flag in $LDFLAGS; do + case $ld_flag in + *-brtl*) + aix_use_runtimelinking=yes + break + ;; + esac + done + ;; + esac + + exp_sym_flag='-bexport' + no_entry_flag='-bnoentry' + fi + + # When large executables or shared objects are built, AIX ld can + # have problems creating the table of contents. If linking a library + # or program results in "error TOC overflow" add -mminimal-toc to + # CXXFLAGS/CFLAGS for g++/gcc. In the cases where that is not + # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS. + + _LT_TAGVAR(archive_cmds, $1)='' + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_direct_absolute, $1)=yes + _LT_TAGVAR(hardcode_libdir_separator, $1)=':' + _LT_TAGVAR(link_all_deplibs, $1)=yes + _LT_TAGVAR(file_list_spec, $1)='${wl}-f,' + + if test "$GXX" = yes; then + case $host_os in aix4.[[012]]|aix4.[[012]].*) + # We only want to do this on AIX 4.2 and lower, the check + # below for broken collect2 doesn't work under 4.3+ + collect2name=`${CC} -print-prog-name=collect2` + if test -f "$collect2name" && + strings "$collect2name" | $GREP resolve_lib_name >/dev/null + then + # We have reworked collect2 + : + else + # We have old collect2 + _LT_TAGVAR(hardcode_direct, $1)=unsupported + # It fails to find uninstalled libraries when the uninstalled + # path is not listed in the libpath. Setting hardcode_minus_L + # to unsupported forces relinking + _LT_TAGVAR(hardcode_minus_L, $1)=yes + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)= + fi + esac + shared_flag='-shared' + if test "$aix_use_runtimelinking" = yes; then + shared_flag="$shared_flag "'${wl}-G' + fi + else + # not using gcc + if test "$host_cpu" = ia64; then + # VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release + # chokes on -Wl,-G. The following line is correct: + shared_flag='-G' + else + if test "$aix_use_runtimelinking" = yes; then + shared_flag='${wl}-G' + else + shared_flag='${wl}-bM:SRE' + fi + fi + fi + + _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-bexpall' + # It seems that -bexpall does not export symbols beginning with + # underscore (_), so it is better to generate a list of symbols to + # export. + _LT_TAGVAR(always_export_symbols, $1)=yes + if test "$aix_use_runtimelinking" = yes; then + # Warning - without using the other runtime loading flags (-brtl), + # -berok will link without error, but may produce a broken library. + _LT_TAGVAR(allow_undefined_flag, $1)='-berok' + # Determine the default libpath from the value encoded in an empty + # executable. + _LT_SYS_MODULE_PATH_AIX([$1]) + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-blibpath:$libdir:'"$aix_libpath" + + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags `if test "x${allow_undefined_flag}" != "x"; then func_echo_all "${wl}${allow_undefined_flag}"; else :; fi` '"\${wl}$exp_sym_flag:\$export_symbols $shared_flag" + else + if test "$host_cpu" = ia64; then + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-R $libdir:/usr/lib:/lib' + _LT_TAGVAR(allow_undefined_flag, $1)="-z nodefs" + _LT_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags ${wl}${allow_undefined_flag} '"\${wl}$exp_sym_flag:\$export_symbols" + else + # Determine the default libpath from the value encoded in an + # empty executable. + _LT_SYS_MODULE_PATH_AIX([$1]) + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-blibpath:$libdir:'"$aix_libpath" + # Warning - without using the other run time loading flags, + # -berok will link without error, but may produce a broken library. + _LT_TAGVAR(no_undefined_flag, $1)=' ${wl}-bernotok' + _LT_TAGVAR(allow_undefined_flag, $1)=' ${wl}-berok' + if test "$with_gnu_ld" = yes; then + # We only use this code for GNU lds that support --whole-archive. + _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive$convenience ${wl}--no-whole-archive' + else + # Exported symbols can be pulled into shared objects from archives + _LT_TAGVAR(whole_archive_flag_spec, $1)='$convenience' + fi + _LT_TAGVAR(archive_cmds_need_lc, $1)=yes + # This is similar to how AIX traditionally builds its shared + # libraries. + _LT_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs ${wl}-bnoentry $compiler_flags ${wl}-bE:$export_symbols${allow_undefined_flag}~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$soname' + fi + fi + ;; + + beos*) + if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then + _LT_TAGVAR(allow_undefined_flag, $1)=unsupported + # Joseph Beckenbach says some releases of gcc + # support --undefined. This deserves some investigation. FIXME + _LT_TAGVAR(archive_cmds, $1)='$CC -nostart $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + else + _LT_TAGVAR(ld_shlibs, $1)=no + fi + ;; + + chorus*) + case $cc_basename in + *) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + esac + ;; + + cygwin* | mingw* | pw32* | cegcc*) + case $GXX,$cc_basename in + ,cl* | no,cl*) + # Native MSVC + # hardcode_libdir_flag_spec is actually meaningless, as there is + # no search path for DLLs. + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)=' ' + _LT_TAGVAR(allow_undefined_flag, $1)=unsupported + _LT_TAGVAR(always_export_symbols, $1)=yes + _LT_TAGVAR(file_list_spec, $1)='@' + # Tell ltmain to make .lib files, not .a files. + libext=lib + # Tell ltmain to make .dll files, not .so files. + shrext_cmds=".dll" + # FIXME: Setting linknames here is a bad hack. + _LT_TAGVAR(archive_cmds, $1)='$CC -o $output_objdir/$soname $libobjs $compiler_flags $deplibs -Wl,-dll~linknames=' + _LT_TAGVAR(archive_expsym_cmds, $1)='if test "x`$SED 1q $export_symbols`" = xEXPORTS; then + $SED -n -e 's/\\\\\\\(.*\\\\\\\)/-link\\\ -EXPORT:\\\\\\\1/' -e '1\\\!p' < $export_symbols > $output_objdir/$soname.exp; + else + $SED -e 's/\\\\\\\(.*\\\\\\\)/-link\\\ -EXPORT:\\\\\\\1/' < $export_symbols > $output_objdir/$soname.exp; + fi~ + $CC -o $tool_output_objdir$soname $libobjs $compiler_flags $deplibs "@$tool_output_objdir$soname.exp" -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~ + linknames=' + # The linker will not automatically build a static lib if we build a DLL. + # _LT_TAGVAR(old_archive_from_new_cmds, $1)='true' + _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes + # Don't use ranlib + _LT_TAGVAR(old_postinstall_cmds, $1)='chmod 644 $oldlib' + _LT_TAGVAR(postlink_cmds, $1)='lt_outputfile="@OUTPUT@"~ + lt_tool_outputfile="@TOOL_OUTPUT@"~ + case $lt_outputfile in + *.exe|*.EXE) ;; + *) + lt_outputfile="$lt_outputfile.exe" + lt_tool_outputfile="$lt_tool_outputfile.exe" + ;; + esac~ + func_to_tool_file "$lt_outputfile"~ + if test "$MANIFEST_TOOL" != ":" && test -f "$lt_outputfile.manifest"; then + $MANIFEST_TOOL -manifest "$lt_tool_outputfile.manifest" -outputresource:"$lt_tool_outputfile" || exit 1; + $RM "$lt_outputfile.manifest"; + fi' + ;; + *) + # g++ + # _LT_TAGVAR(hardcode_libdir_flag_spec, $1) is actually meaningless, + # as there is no search path for DLLs. + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' + _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-all-symbols' + _LT_TAGVAR(allow_undefined_flag, $1)=unsupported + _LT_TAGVAR(always_export_symbols, $1)=no + _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes + + if $LD --help 2>&1 | $GREP 'auto-import' > /dev/null; then + _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' + # If the export-symbols file already is a .def file (1st line + # is EXPORTS), use it as is; otherwise, prepend... + _LT_TAGVAR(archive_expsym_cmds, $1)='if test "x`$SED 1q $export_symbols`" = xEXPORTS; then + cp $export_symbols $output_objdir/$soname.def; + else + echo EXPORTS > $output_objdir/$soname.def; + cat $export_symbols >> $output_objdir/$soname.def; + fi~ + $CC -shared -nostdlib $output_objdir/$soname.def $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' + else + _LT_TAGVAR(ld_shlibs, $1)=no + fi + ;; + esac + ;; + darwin* | rhapsody*) + _LT_DARWIN_LINKER_FEATURES($1) + ;; + + dgux*) + case $cc_basename in + ec++*) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + ghcx*) + # Green Hills C++ Compiler + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + *) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + esac + ;; + + freebsd2.*) + # C++ shared libraries reported to be fairly broken before + # switch to ELF + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + + freebsd-elf*) + _LT_TAGVAR(archive_cmds_need_lc, $1)=no + ;; + + freebsd* | dragonfly*) + # FreeBSD 3 and later use GNU C++ and GNU ld with standard ELF + # conventions + _LT_TAGVAR(ld_shlibs, $1)=yes + ;; + + haiku*) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + _LT_TAGVAR(link_all_deplibs, $1)=yes + ;; + + hpux9*) + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}+b ${wl}$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=: + _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_minus_L, $1)=yes # Not in the search PATH, + # but as the default + # location of the library. + + case $cc_basename in + CC*) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + aCC*) + _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$CC -b ${wl}+b ${wl}$install_libdir -o $output_objdir/$soname $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib' + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + # + # There doesn't appear to be a way to prevent this compiler from + # explicitly linking system object files so we need to strip them + # from the output so that they don't get included in the library + # dependencies. + output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | $EGREP "\-L"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"' + ;; + *) + if test "$GXX" = yes; then + _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$CC -shared -nostdlib $pic_flag ${wl}+b ${wl}$install_libdir -o $output_objdir/$soname $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib' + else + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + fi + ;; + esac + ;; + + hpux10*|hpux11*) + if test $with_gnu_ld = no; then + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}+b ${wl}$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=: + + case $host_cpu in + hppa*64*|ia64*) + ;; + *) + _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' + ;; + esac + fi + case $host_cpu in + hppa*64*|ia64*) + _LT_TAGVAR(hardcode_direct, $1)=no + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; + *) + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_direct_absolute, $1)=yes + _LT_TAGVAR(hardcode_minus_L, $1)=yes # Not in the search PATH, + # but as the default + # location of the library. + ;; + esac + + case $cc_basename in + CC*) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + aCC*) + case $host_cpu in + hppa*64*) + _LT_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + ;; + ia64*) + _LT_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + ;; + *) + _LT_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + ;; + esac + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + # + # There doesn't appear to be a way to prevent this compiler from + # explicitly linking system object files so we need to strip them + # from the output so that they don't get included in the library + # dependencies. + output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | $GREP "\-L"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"' + ;; + *) + if test "$GXX" = yes; then + if test $with_gnu_ld = no; then + case $host_cpu in + hppa*64*) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib -fPIC ${wl}+h ${wl}$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + ;; + ia64*) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $pic_flag ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + ;; + *) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $pic_flag ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + ;; + esac + fi + else + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + fi + ;; + esac + ;; + + interix[[3-9]]*) + _LT_TAGVAR(hardcode_direct, $1)=no + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir' + _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' + # Hack: On Interix 3.x, we cannot compile PIC because of a broken gcc. + # Instead, shared libraries are loaded at an image base (0x10000000 by + # default) and relocated if they conflict, which is a slow very memory + # consuming and fragmenting process. To avoid this, we pick a random, + # 256 KiB-aligned image base between 0x50000000 and 0x6FFC0000 at link + # time. Moving up from 0x10000000 also allows more sbrk(2) space. + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='sed "s,^,_," $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--retain-symbols-file,$output_objdir/$soname.expsym ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' + ;; + irix5* | irix6*) + case $cc_basename in + CC*) + # SGI C++ + _LT_TAGVAR(archive_cmds, $1)='$CC -shared -all -multigot $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib' + + # Archives containing C++ object files must be created using + # "CC -ar", where "CC" is the IRIX C++ compiler. This is + # necessary to make sure instantiated templates are included + # in the archive. + _LT_TAGVAR(old_archive_cmds, $1)='$CC -ar -WR,-u -o $oldlib $oldobjs' + ;; + *) + if test "$GXX" = yes; then + if test "$with_gnu_ld" = no; then + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' + else + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` -o $lib' + fi + fi + _LT_TAGVAR(link_all_deplibs, $1)=yes + ;; + esac + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=: + _LT_TAGVAR(inherit_rpath, $1)=yes + ;; + + linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) + case $cc_basename in + KCC*) + # Kuck and Associates, Inc. (KAI) C++ Compiler + + # KCC will only create a shared library if the output file + # ends with ".so" (or ".sl" for HP-UX), so rename the library + # to its proper name (with version) after linking. + _LT_TAGVAR(archive_cmds, $1)='tempext=`echo $shared_ext | $SED -e '\''s/\([[^()0-9A-Za-z{}]]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\${tempext}\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib; mv \$templib $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='tempext=`echo $shared_ext | $SED -e '\''s/\([[^()0-9A-Za-z{}]]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\${tempext}\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib ${wl}-retain-symbols-file,$export_symbols; mv \$templib $lib' + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + # + # There doesn't appear to be a way to prevent this compiler from + # explicitly linking system object files so we need to strip them + # from the output so that they don't get included in the library + # dependencies. + output_verbose_link_cmd='templist=`$CC $CFLAGS -v conftest.$objext -o libconftest$shared_ext 2>&1 | $GREP "ld"`; rm -f libconftest$shared_ext; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"' + + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir' + _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic' + + # Archives containing C++ object files must be created using + # "CC -Bstatic", where "CC" is the KAI C++ compiler. + _LT_TAGVAR(old_archive_cmds, $1)='$CC -Bstatic -o $oldlib $oldobjs' + ;; + icpc* | ecpc* ) + # Intel C++ + with_gnu_ld=yes + # version 8.0 and above of icpc choke on multiply defined symbols + # if we add $predep_objects and $postdep_objects, however 7.1 and + # earlier do not add the objects themselves. + case `$CC -V 2>&1` in + *"Version 7."*) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' + ;; + *) # Version 8.0 or newer + tmp_idyn= + case $host_cpu in + ia64*) tmp_idyn=' -i_dynamic';; + esac + _LT_TAGVAR(archive_cmds, $1)='$CC -shared'"$tmp_idyn"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared'"$tmp_idyn"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' + ;; + esac + _LT_TAGVAR(archive_cmds_need_lc, $1)=no + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir' + _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic' + _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive$convenience ${wl}--no-whole-archive' + ;; + pgCC* | pgcpp*) + # Portland Group C++ compiler + case `$CC -V` in + *pgCC\ [[1-5]].* | *pgcpp\ [[1-5]].*) + _LT_TAGVAR(prelink_cmds, $1)='tpldir=Template.dir~ + rm -rf $tpldir~ + $CC --prelink_objects --instantiation_dir $tpldir $objs $libobjs $compile_deplibs~ + compile_command="$compile_command `find $tpldir -name \*.o | sort | $NL2SP`"' + _LT_TAGVAR(old_archive_cmds, $1)='tpldir=Template.dir~ + rm -rf $tpldir~ + $CC --prelink_objects --instantiation_dir $tpldir $oldobjs$old_deplibs~ + $AR $AR_FLAGS $oldlib$oldobjs$old_deplibs `find $tpldir -name \*.o | sort | $NL2SP`~ + $RANLIB $oldlib' + _LT_TAGVAR(archive_cmds, $1)='tpldir=Template.dir~ + rm -rf $tpldir~ + $CC --prelink_objects --instantiation_dir $tpldir $predep_objects $libobjs $deplibs $convenience $postdep_objects~ + $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | sort | $NL2SP` $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='tpldir=Template.dir~ + rm -rf $tpldir~ + $CC --prelink_objects --instantiation_dir $tpldir $predep_objects $libobjs $deplibs $convenience $postdep_objects~ + $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | sort | $NL2SP` $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname ${wl}-retain-symbols-file ${wl}$export_symbols -o $lib' + ;; + *) # Version 6 and above use weak symbols + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname ${wl}-retain-symbols-file ${wl}$export_symbols -o $lib' + ;; + esac + + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}--rpath ${wl}$libdir' + _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic' + _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` ${wl}--no-whole-archive' + ;; + cxx*) + # Compaq C++ + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib ${wl}-retain-symbols-file $wl$export_symbols' + + runpath_var=LD_RUN_PATH + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-rpath $libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=: + + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + # + # There doesn't appear to be a way to prevent this compiler from + # explicitly linking system object files so we need to strip them + # from the output so that they don't get included in the library + # dependencies. + output_verbose_link_cmd='templist=`$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP "ld"`; templist=`func_echo_all "$templist" | $SED "s/\(^.*ld.*\)\( .*ld .*$\)/\1/"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "X$list" | $Xsed' + ;; + xl* | mpixl* | bgxl*) + # IBM XL 8.0 on PPC, with GNU ld + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' + _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic' + _LT_TAGVAR(archive_cmds, $1)='$CC -qmkshrobj $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + if test "x$supports_anon_versioning" = xyes; then + _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $output_objdir/$libname.ver~ + cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ + echo "local: *; };" >> $output_objdir/$libname.ver~ + $CC -qmkshrobj $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-version-script ${wl}$output_objdir/$libname.ver -o $lib' + fi + ;; + *) + case `$CC -V 2>&1 | sed 5q` in + *Sun\ C*) + # Sun C++ 5.9 + _LT_TAGVAR(no_undefined_flag, $1)=' -zdefs' + _LT_TAGVAR(archive_cmds, $1)='$CC -G${allow_undefined_flag} -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G${allow_undefined_flag} -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-retain-symbols-file ${wl}$export_symbols' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' + _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive`new_convenience=; for conv in $convenience\"\"; do test -z \"$conv\" || new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` ${wl}--no-whole-archive' + _LT_TAGVAR(compiler_needs_object, $1)=yes + + # Not sure whether something based on + # $CC $CFLAGS -v conftest.$objext -o libconftest$shared_ext 2>&1 + # would be better. + output_verbose_link_cmd='func_echo_all' + + # Archives containing C++ object files must be created using + # "CC -xar", where "CC" is the Sun C++ compiler. This is + # necessary to make sure instantiated templates are included + # in the archive. + _LT_TAGVAR(old_archive_cmds, $1)='$CC -xar -o $oldlib $oldobjs' + ;; + esac + ;; + esac + ;; + + lynxos*) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + + m88k*) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + + mvs*) + case $cc_basename in + cxx*) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + *) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + esac + ;; + + netbsd*) + if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then + _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $predep_objects $libobjs $deplibs $postdep_objects $linker_flags' + wlarc= + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + fi + # Workaround some broken pre-1.5 toolchains + output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP conftest.$objext | $SED -e "s:-lgcc -lc -lgcc::"' + ;; + + *nto* | *qnx*) + _LT_TAGVAR(ld_shlibs, $1)=yes + ;; + + openbsd2*) + # C++ shared libraries are fairly broken + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + + openbsd*) + if test -f /usr/libexec/ld.so; then + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + _LT_TAGVAR(hardcode_direct_absolute, $1)=yes + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $lib' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir' + if test -z "`echo __ELF__ | $CC -E - | grep __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-retain-symbols-file,$export_symbols -o $lib' + _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' + _LT_TAGVAR(whole_archive_flag_spec, $1)="$wlarc"'--whole-archive$convenience '"$wlarc"'--no-whole-archive' + fi + output_verbose_link_cmd=func_echo_all + else + _LT_TAGVAR(ld_shlibs, $1)=no + fi + ;; + + osf3* | osf4* | osf5*) + case $cc_basename in + KCC*) + # Kuck and Associates, Inc. (KAI) C++ Compiler + + # KCC will only create a shared library if the output file + # ends with ".so" (or ".sl" for HP-UX), so rename the library + # to its proper name (with version) after linking. + _LT_TAGVAR(archive_cmds, $1)='tempext=`echo $shared_ext | $SED -e '\''s/\([[^()0-9A-Za-z{}]]\)/\\\\\1/g'\''`; templib=`echo "$lib" | $SED -e "s/\${tempext}\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib; mv \$templib $lib' + + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=: + + # Archives containing C++ object files must be created using + # the KAI C++ compiler. + case $host in + osf3*) _LT_TAGVAR(old_archive_cmds, $1)='$CC -Bstatic -o $oldlib $oldobjs' ;; + *) _LT_TAGVAR(old_archive_cmds, $1)='$CC -o $oldlib $oldobjs' ;; + esac + ;; + RCC*) + # Rational C++ 2.4.1 + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + cxx*) + case $host in + osf3*) + _LT_TAGVAR(allow_undefined_flag, $1)=' ${wl}-expect_unresolved ${wl}\*' + _LT_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $soname `test -n "$verstring" && func_echo_all "${wl}-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' + ;; + *) + _LT_TAGVAR(allow_undefined_flag, $1)=' -expect_unresolved \*' + _LT_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -msym -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done~ + echo "-hidden">> $lib.exp~ + $CC -shared$allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -msym -soname $soname ${wl}-input ${wl}$lib.exp `test -n "$verstring" && $ECHO "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib~ + $RM $lib.exp' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-rpath $libdir' + ;; + esac + + _LT_TAGVAR(hardcode_libdir_separator, $1)=: + + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + # + # There doesn't appear to be a way to prevent this compiler from + # explicitly linking system object files so we need to strip them + # from the output so that they don't get included in the library + # dependencies. + output_verbose_link_cmd='templist=`$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP "ld" | $GREP -v "ld:"`; templist=`func_echo_all "$templist" | $SED "s/\(^.*ld.*\)\( .*ld.*$\)/\1/"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"' + ;; + *) + if test "$GXX" = yes && test "$with_gnu_ld" = no; then + _LT_TAGVAR(allow_undefined_flag, $1)=' ${wl}-expect_unresolved ${wl}\*' + case $host in + osf3*) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib ${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' + ;; + *) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -nostdlib ${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-msym ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' + ;; + esac + + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=: + + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"' + + else + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + fi + ;; + esac + ;; + + psos*) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + + sunos4*) + case $cc_basename in + CC*) + # Sun C++ 4.x + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + lcc*) + # Lucid + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + *) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + esac + ;; + + solaris*) + case $cc_basename in + CC* | sunCC*) + # Sun C++ 4.2, 5.x and Centerline C++ + _LT_TAGVAR(archive_cmds_need_lc,$1)=yes + _LT_TAGVAR(no_undefined_flag, $1)=' -zdefs' + _LT_TAGVAR(archive_cmds, $1)='$CC -G${allow_undefined_flag} -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ + $CC -G${allow_undefined_flag} ${wl}-M ${wl}$lib.exp -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp' + + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + case $host_os in + solaris2.[[0-5]] | solaris2.[[0-5]].*) ;; + *) + # The compiler driver will combine and reorder linker options, + # but understands `-z linker_flag'. + # Supported since Solaris 2.6 (maybe 2.5.1?) + _LT_TAGVAR(whole_archive_flag_spec, $1)='-z allextract$convenience -z defaultextract' + ;; + esac + _LT_TAGVAR(link_all_deplibs, $1)=yes + + output_verbose_link_cmd='func_echo_all' + + # Archives containing C++ object files must be created using + # "CC -xar", where "CC" is the Sun C++ compiler. This is + # necessary to make sure instantiated templates are included + # in the archive. + _LT_TAGVAR(old_archive_cmds, $1)='$CC -xar -o $oldlib $oldobjs' + ;; + gcx*) + # Green Hills C++ Compiler + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-h $wl$soname -o $lib' + + # The C++ compiler must be used to create the archive. + _LT_TAGVAR(old_archive_cmds, $1)='$CC $LDFLAGS -archive -o $oldlib $oldobjs' + ;; + *) + # GNU C++ compiler with Solaris linker + if test "$GXX" = yes && test "$with_gnu_ld" = no; then + _LT_TAGVAR(no_undefined_flag, $1)=' ${wl}-z ${wl}defs' + if $CC --version | $GREP -v '^2\.7' > /dev/null; then + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -nostdlib $LDFLAGS $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-h $wl$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ + $CC -shared $pic_flag -nostdlib ${wl}-M $wl$lib.exp -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp' + + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"' + else + # g++ 2.7 appears to require `-G' NOT `-shared' on this + # platform. + _LT_TAGVAR(archive_cmds, $1)='$CC -G -nostdlib $LDFLAGS $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-h $wl$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ + $CC -G -nostdlib ${wl}-M $wl$lib.exp -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp' + + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + output_verbose_link_cmd='$CC -G $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"' + fi + + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-R $wl$libdir' + case $host_os in + solaris2.[[0-5]] | solaris2.[[0-5]].*) ;; + *) + _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}-z ${wl}allextract$convenience ${wl}-z ${wl}defaultextract' + ;; + esac + fi + ;; + esac + ;; + + sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[[01]].[[10]]* | unixware7* | sco3.2v5.0.[[024]]*) + _LT_TAGVAR(no_undefined_flag, $1)='${wl}-z,text' + _LT_TAGVAR(archive_cmds_need_lc, $1)=no + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + runpath_var='LD_RUN_PATH' + + case $cc_basename in + CC*) + _LT_TAGVAR(archive_cmds, $1)='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + ;; + *) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + ;; + esac + ;; + + sysv5* | sco3.2v5* | sco5v6*) + # Note: We can NOT use -z defs as we might desire, because we do not + # link with -lc, and that would cause any symbols used from libc to + # always be unresolved, which means just about no library would + # ever link correctly. If we're not using GNU ld we use -z text + # though, which does catch some bad symbols but isn't as heavy-handed + # as -z defs. + _LT_TAGVAR(no_undefined_flag, $1)='${wl}-z,text' + _LT_TAGVAR(allow_undefined_flag, $1)='${wl}-z,nodefs' + _LT_TAGVAR(archive_cmds_need_lc, $1)=no + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-R,$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=':' + _LT_TAGVAR(link_all_deplibs, $1)=yes + _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-Bexport' + runpath_var='LD_RUN_PATH' + + case $cc_basename in + CC*) + _LT_TAGVAR(archive_cmds, $1)='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(old_archive_cmds, $1)='$CC -Tprelink_objects $oldobjs~ + '"$_LT_TAGVAR(old_archive_cmds, $1)" + _LT_TAGVAR(reload_cmds, $1)='$CC -Tprelink_objects $reload_objs~ + '"$_LT_TAGVAR(reload_cmds, $1)" + ;; + *) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + ;; + esac + ;; + + tandem*) + case $cc_basename in + NCC*) + # NonStop-UX NCC 3.20 + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + *) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + esac + ;; + + vxworks*) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + + *) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + esac + + AC_MSG_RESULT([$_LT_TAGVAR(ld_shlibs, $1)]) + test "$_LT_TAGVAR(ld_shlibs, $1)" = no && can_build_shared=no + + _LT_TAGVAR(GCC, $1)="$GXX" + _LT_TAGVAR(LD, $1)="$LD" + + ## CAVEAT EMPTOR: + ## There is no encapsulation within the following macros, do not change + ## the running order or otherwise move them around unless you know exactly + ## what you are doing... + _LT_SYS_HIDDEN_LIBDEPS($1) + _LT_COMPILER_PIC($1) + _LT_COMPILER_C_O($1) + _LT_COMPILER_FILE_LOCKS($1) + _LT_LINKER_SHLIBS($1) + _LT_SYS_DYNAMIC_LINKER($1) + _LT_LINKER_HARDCODE_LIBPATH($1) + + _LT_CONFIG($1) + fi # test -n "$compiler" + + CC=$lt_save_CC + CFLAGS=$lt_save_CFLAGS + LDCXX=$LD + LD=$lt_save_LD + GCC=$lt_save_GCC + with_gnu_ld=$lt_save_with_gnu_ld + lt_cv_path_LDCXX=$lt_cv_path_LD + lt_cv_path_LD=$lt_save_path_LD + lt_cv_prog_gnu_ldcxx=$lt_cv_prog_gnu_ld + lt_cv_prog_gnu_ld=$lt_save_with_gnu_ld +fi # test "$_lt_caught_CXX_error" != yes + +AC_LANG_POP +])# _LT_LANG_CXX_CONFIG + + +# _LT_FUNC_STRIPNAME_CNF +# ---------------------- +# func_stripname_cnf prefix suffix name +# strip PREFIX and SUFFIX off of NAME. +# PREFIX and SUFFIX must not contain globbing or regex special +# characters, hashes, percent signs, but SUFFIX may contain a leading +# dot (in which case that matches only a dot). +# +# This function is identical to the (non-XSI) version of func_stripname, +# except this one can be used by m4 code that may be executed by configure, +# rather than the libtool script. +m4_defun([_LT_FUNC_STRIPNAME_CNF],[dnl +AC_REQUIRE([_LT_DECL_SED]) +AC_REQUIRE([_LT_PROG_ECHO_BACKSLASH]) +func_stripname_cnf () +{ + case ${2} in + .*) func_stripname_result=`$ECHO "${3}" | $SED "s%^${1}%%; s%\\\\${2}\$%%"`;; + *) func_stripname_result=`$ECHO "${3}" | $SED "s%^${1}%%; s%${2}\$%%"`;; + esac +} # func_stripname_cnf +])# _LT_FUNC_STRIPNAME_CNF + +# _LT_SYS_HIDDEN_LIBDEPS([TAGNAME]) +# --------------------------------- +# Figure out "hidden" library dependencies from verbose +# compiler output when linking a shared library. +# Parse the compiler output and extract the necessary +# objects, libraries and library flags. +m4_defun([_LT_SYS_HIDDEN_LIBDEPS], +[m4_require([_LT_FILEUTILS_DEFAULTS])dnl +AC_REQUIRE([_LT_FUNC_STRIPNAME_CNF])dnl +# Dependencies to place before and after the object being linked: +_LT_TAGVAR(predep_objects, $1)= +_LT_TAGVAR(postdep_objects, $1)= +_LT_TAGVAR(predeps, $1)= +_LT_TAGVAR(postdeps, $1)= +_LT_TAGVAR(compiler_lib_search_path, $1)= + +dnl we can't use the lt_simple_compile_test_code here, +dnl because it contains code intended for an executable, +dnl not a library. It's possible we should let each +dnl tag define a new lt_????_link_test_code variable, +dnl but it's only used here... +m4_if([$1], [], [cat > conftest.$ac_ext <<_LT_EOF +int a; +void foo (void) { a = 0; } +_LT_EOF +], [$1], [CXX], [cat > conftest.$ac_ext <<_LT_EOF +class Foo +{ +public: + Foo (void) { a = 0; } +private: + int a; +}; +_LT_EOF +], [$1], [F77], [cat > conftest.$ac_ext <<_LT_EOF + subroutine foo + implicit none + integer*4 a + a=0 + return + end +_LT_EOF +], [$1], [FC], [cat > conftest.$ac_ext <<_LT_EOF + subroutine foo + implicit none + integer a + a=0 + return + end +_LT_EOF +], [$1], [GCJ], [cat > conftest.$ac_ext <<_LT_EOF +public class foo { + private int a; + public void bar (void) { + a = 0; + } +}; +_LT_EOF +], [$1], [GO], [cat > conftest.$ac_ext <<_LT_EOF +package foo +func foo() { +} +_LT_EOF +]) + +_lt_libdeps_save_CFLAGS=$CFLAGS +case "$CC $CFLAGS " in #( +*\ -flto*\ *) CFLAGS="$CFLAGS -fno-lto" ;; +*\ -fwhopr*\ *) CFLAGS="$CFLAGS -fno-whopr" ;; +*\ -fuse-linker-plugin*\ *) CFLAGS="$CFLAGS -fno-use-linker-plugin" ;; +esac + +dnl Parse the compiler output and extract the necessary +dnl objects, libraries and library flags. +if AC_TRY_EVAL(ac_compile); then + # Parse the compiler output and extract the necessary + # objects, libraries and library flags. + + # Sentinel used to keep track of whether or not we are before + # the conftest object file. + pre_test_object_deps_done=no + + for p in `eval "$output_verbose_link_cmd"`; do + case ${prev}${p} in + + -L* | -R* | -l*) + # Some compilers place space between "-{L,R}" and the path. + # Remove the space. + if test $p = "-L" || + test $p = "-R"; then + prev=$p + continue + fi + + # Expand the sysroot to ease extracting the directories later. + if test -z "$prev"; then + case $p in + -L*) func_stripname_cnf '-L' '' "$p"; prev=-L; p=$func_stripname_result ;; + -R*) func_stripname_cnf '-R' '' "$p"; prev=-R; p=$func_stripname_result ;; + -l*) func_stripname_cnf '-l' '' "$p"; prev=-l; p=$func_stripname_result ;; + esac + fi + case $p in + =*) func_stripname_cnf '=' '' "$p"; p=$lt_sysroot$func_stripname_result ;; + esac + if test "$pre_test_object_deps_done" = no; then + case ${prev} in + -L | -R) + # Internal compiler library paths should come after those + # provided the user. The postdeps already come after the + # user supplied libs so there is no need to process them. + if test -z "$_LT_TAGVAR(compiler_lib_search_path, $1)"; then + _LT_TAGVAR(compiler_lib_search_path, $1)="${prev}${p}" + else + _LT_TAGVAR(compiler_lib_search_path, $1)="${_LT_TAGVAR(compiler_lib_search_path, $1)} ${prev}${p}" + fi + ;; + # The "-l" case would never come before the object being + # linked, so don't bother handling this case. + esac + else + if test -z "$_LT_TAGVAR(postdeps, $1)"; then + _LT_TAGVAR(postdeps, $1)="${prev}${p}" + else + _LT_TAGVAR(postdeps, $1)="${_LT_TAGVAR(postdeps, $1)} ${prev}${p}" + fi + fi + prev= + ;; + + *.lto.$objext) ;; # Ignore GCC LTO objects + *.$objext) + # This assumes that the test object file only shows up + # once in the compiler output. + if test "$p" = "conftest.$objext"; then + pre_test_object_deps_done=yes + continue + fi + + if test "$pre_test_object_deps_done" = no; then + if test -z "$_LT_TAGVAR(predep_objects, $1)"; then + _LT_TAGVAR(predep_objects, $1)="$p" + else + _LT_TAGVAR(predep_objects, $1)="$_LT_TAGVAR(predep_objects, $1) $p" + fi + else + if test -z "$_LT_TAGVAR(postdep_objects, $1)"; then + _LT_TAGVAR(postdep_objects, $1)="$p" + else + _LT_TAGVAR(postdep_objects, $1)="$_LT_TAGVAR(postdep_objects, $1) $p" + fi + fi + ;; + + *) ;; # Ignore the rest. + + esac + done + + # Clean up. + rm -f a.out a.exe +else + echo "libtool.m4: error: problem compiling $1 test program" +fi + +$RM -f confest.$objext +CFLAGS=$_lt_libdeps_save_CFLAGS + +# PORTME: override above test on systems where it is broken +m4_if([$1], [CXX], +[case $host_os in +interix[[3-9]]*) + # Interix 3.5 installs completely hosed .la files for C++, so rather than + # hack all around it, let's just trust "g++" to DTRT. + _LT_TAGVAR(predep_objects,$1)= + _LT_TAGVAR(postdep_objects,$1)= + _LT_TAGVAR(postdeps,$1)= + ;; + +linux*) + case `$CC -V 2>&1 | sed 5q` in + *Sun\ C*) + # Sun C++ 5.9 + + # The more standards-conforming stlport4 library is + # incompatible with the Cstd library. Avoid specifying + # it if it's in CXXFLAGS. Ignore libCrun as + # -library=stlport4 depends on it. + case " $CXX $CXXFLAGS " in + *" -library=stlport4 "*) + solaris_use_stlport4=yes + ;; + esac + + if test "$solaris_use_stlport4" != yes; then + _LT_TAGVAR(postdeps,$1)='-library=Cstd -library=Crun' + fi + ;; + esac + ;; + +solaris*) + case $cc_basename in + CC* | sunCC*) + # The more standards-conforming stlport4 library is + # incompatible with the Cstd library. Avoid specifying + # it if it's in CXXFLAGS. Ignore libCrun as + # -library=stlport4 depends on it. + case " $CXX $CXXFLAGS " in + *" -library=stlport4 "*) + solaris_use_stlport4=yes + ;; + esac + + # Adding this requires a known-good setup of shared libraries for + # Sun compiler versions before 5.6, else PIC objects from an old + # archive will be linked into the output, leading to subtle bugs. + if test "$solaris_use_stlport4" != yes; then + _LT_TAGVAR(postdeps,$1)='-library=Cstd -library=Crun' + fi + ;; + esac + ;; +esac +]) + +case " $_LT_TAGVAR(postdeps, $1) " in +*" -lc "*) _LT_TAGVAR(archive_cmds_need_lc, $1)=no ;; +esac + _LT_TAGVAR(compiler_lib_search_dirs, $1)= +if test -n "${_LT_TAGVAR(compiler_lib_search_path, $1)}"; then + _LT_TAGVAR(compiler_lib_search_dirs, $1)=`echo " ${_LT_TAGVAR(compiler_lib_search_path, $1)}" | ${SED} -e 's! -L! !g' -e 's!^ !!'` +fi +_LT_TAGDECL([], [compiler_lib_search_dirs], [1], + [The directories searched by this compiler when creating a shared library]) +_LT_TAGDECL([], [predep_objects], [1], + [Dependencies to place before and after the objects being linked to + create a shared library]) +_LT_TAGDECL([], [postdep_objects], [1]) +_LT_TAGDECL([], [predeps], [1]) +_LT_TAGDECL([], [postdeps], [1]) +_LT_TAGDECL([], [compiler_lib_search_path], [1], + [The library search path used internally by the compiler when linking + a shared library]) +])# _LT_SYS_HIDDEN_LIBDEPS + + +# _LT_LANG_F77_CONFIG([TAG]) +# -------------------------- +# Ensure that the configuration variables for a Fortran 77 compiler are +# suitably defined. These variables are subsequently used by _LT_CONFIG +# to write the compiler configuration to `libtool'. +m4_defun([_LT_LANG_F77_CONFIG], +[AC_LANG_PUSH(Fortran 77) +if test -z "$F77" || test "X$F77" = "Xno"; then + _lt_disable_F77=yes +fi + +_LT_TAGVAR(archive_cmds_need_lc, $1)=no +_LT_TAGVAR(allow_undefined_flag, $1)= +_LT_TAGVAR(always_export_symbols, $1)=no +_LT_TAGVAR(archive_expsym_cmds, $1)= +_LT_TAGVAR(export_dynamic_flag_spec, $1)= +_LT_TAGVAR(hardcode_direct, $1)=no +_LT_TAGVAR(hardcode_direct_absolute, $1)=no +_LT_TAGVAR(hardcode_libdir_flag_spec, $1)= +_LT_TAGVAR(hardcode_libdir_separator, $1)= +_LT_TAGVAR(hardcode_minus_L, $1)=no +_LT_TAGVAR(hardcode_automatic, $1)=no +_LT_TAGVAR(inherit_rpath, $1)=no +_LT_TAGVAR(module_cmds, $1)= +_LT_TAGVAR(module_expsym_cmds, $1)= +_LT_TAGVAR(link_all_deplibs, $1)=unknown +_LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds +_LT_TAGVAR(reload_flag, $1)=$reload_flag +_LT_TAGVAR(reload_cmds, $1)=$reload_cmds +_LT_TAGVAR(no_undefined_flag, $1)= +_LT_TAGVAR(whole_archive_flag_spec, $1)= +_LT_TAGVAR(enable_shared_with_static_runtimes, $1)=no + +# Source file extension for f77 test sources. +ac_ext=f + +# Object file extension for compiled f77 test sources. +objext=o +_LT_TAGVAR(objext, $1)=$objext + +# No sense in running all these tests if we already determined that +# the F77 compiler isn't working. Some variables (like enable_shared) +# are currently assumed to apply to all compilers on this platform, +# and will be corrupted by setting them based on a non-working compiler. +if test "$_lt_disable_F77" != yes; then + # Code to be used in simple compile tests + lt_simple_compile_test_code="\ + subroutine t + return + end +" + + # Code to be used in simple link tests + lt_simple_link_test_code="\ + program t + end +" + + # ltmain only uses $CC for tagged configurations so make sure $CC is set. + _LT_TAG_COMPILER + + # save warnings/boilerplate of simple test code + _LT_COMPILER_BOILERPLATE + _LT_LINKER_BOILERPLATE + + # Allow CC to be a program name with arguments. + lt_save_CC="$CC" + lt_save_GCC=$GCC + lt_save_CFLAGS=$CFLAGS + CC=${F77-"f77"} + CFLAGS=$FFLAGS + compiler=$CC + _LT_TAGVAR(compiler, $1)=$CC + _LT_CC_BASENAME([$compiler]) + GCC=$G77 + if test -n "$compiler"; then + AC_MSG_CHECKING([if libtool supports shared libraries]) + AC_MSG_RESULT([$can_build_shared]) + + AC_MSG_CHECKING([whether to build shared libraries]) + test "$can_build_shared" = "no" && enable_shared=no + + # On AIX, shared libraries and static libraries use the same namespace, and + # are all built from PIC. + case $host_os in + aix3*) + test "$enable_shared" = yes && enable_static=no + if test -n "$RANLIB"; then + archive_cmds="$archive_cmds~\$RANLIB \$lib" + postinstall_cmds='$RANLIB $lib' + fi + ;; + aix[[4-9]]*) + if test "$host_cpu" != ia64 && test "$aix_use_runtimelinking" = no ; then + test "$enable_shared" = yes && enable_static=no + fi + ;; + esac + AC_MSG_RESULT([$enable_shared]) + + AC_MSG_CHECKING([whether to build static libraries]) + # Make sure either enable_shared or enable_static is yes. + test "$enable_shared" = yes || enable_static=yes + AC_MSG_RESULT([$enable_static]) + + _LT_TAGVAR(GCC, $1)="$G77" + _LT_TAGVAR(LD, $1)="$LD" + + ## CAVEAT EMPTOR: + ## There is no encapsulation within the following macros, do not change + ## the running order or otherwise move them around unless you know exactly + ## what you are doing... + _LT_COMPILER_PIC($1) + _LT_COMPILER_C_O($1) + _LT_COMPILER_FILE_LOCKS($1) + _LT_LINKER_SHLIBS($1) + _LT_SYS_DYNAMIC_LINKER($1) + _LT_LINKER_HARDCODE_LIBPATH($1) + + _LT_CONFIG($1) + fi # test -n "$compiler" + + GCC=$lt_save_GCC + CC="$lt_save_CC" + CFLAGS="$lt_save_CFLAGS" +fi # test "$_lt_disable_F77" != yes + +AC_LANG_POP +])# _LT_LANG_F77_CONFIG + + +# _LT_LANG_FC_CONFIG([TAG]) +# ------------------------- +# Ensure that the configuration variables for a Fortran compiler are +# suitably defined. These variables are subsequently used by _LT_CONFIG +# to write the compiler configuration to `libtool'. +m4_defun([_LT_LANG_FC_CONFIG], +[AC_LANG_PUSH(Fortran) + +if test -z "$FC" || test "X$FC" = "Xno"; then + _lt_disable_FC=yes +fi + +_LT_TAGVAR(archive_cmds_need_lc, $1)=no +_LT_TAGVAR(allow_undefined_flag, $1)= +_LT_TAGVAR(always_export_symbols, $1)=no +_LT_TAGVAR(archive_expsym_cmds, $1)= +_LT_TAGVAR(export_dynamic_flag_spec, $1)= +_LT_TAGVAR(hardcode_direct, $1)=no +_LT_TAGVAR(hardcode_direct_absolute, $1)=no +_LT_TAGVAR(hardcode_libdir_flag_spec, $1)= +_LT_TAGVAR(hardcode_libdir_separator, $1)= +_LT_TAGVAR(hardcode_minus_L, $1)=no +_LT_TAGVAR(hardcode_automatic, $1)=no +_LT_TAGVAR(inherit_rpath, $1)=no +_LT_TAGVAR(module_cmds, $1)= +_LT_TAGVAR(module_expsym_cmds, $1)= +_LT_TAGVAR(link_all_deplibs, $1)=unknown +_LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds +_LT_TAGVAR(reload_flag, $1)=$reload_flag +_LT_TAGVAR(reload_cmds, $1)=$reload_cmds +_LT_TAGVAR(no_undefined_flag, $1)= +_LT_TAGVAR(whole_archive_flag_spec, $1)= +_LT_TAGVAR(enable_shared_with_static_runtimes, $1)=no + +# Source file extension for fc test sources. +ac_ext=${ac_fc_srcext-f} + +# Object file extension for compiled fc test sources. +objext=o +_LT_TAGVAR(objext, $1)=$objext + +# No sense in running all these tests if we already determined that +# the FC compiler isn't working. Some variables (like enable_shared) +# are currently assumed to apply to all compilers on this platform, +# and will be corrupted by setting them based on a non-working compiler. +if test "$_lt_disable_FC" != yes; then + # Code to be used in simple compile tests + lt_simple_compile_test_code="\ + subroutine t + return + end +" + + # Code to be used in simple link tests + lt_simple_link_test_code="\ + program t + end +" + + # ltmain only uses $CC for tagged configurations so make sure $CC is set. + _LT_TAG_COMPILER + + # save warnings/boilerplate of simple test code + _LT_COMPILER_BOILERPLATE + _LT_LINKER_BOILERPLATE + + # Allow CC to be a program name with arguments. + lt_save_CC="$CC" + lt_save_GCC=$GCC + lt_save_CFLAGS=$CFLAGS + CC=${FC-"f95"} + CFLAGS=$FCFLAGS + compiler=$CC + GCC=$ac_cv_fc_compiler_gnu + + _LT_TAGVAR(compiler, $1)=$CC + _LT_CC_BASENAME([$compiler]) + + if test -n "$compiler"; then + AC_MSG_CHECKING([if libtool supports shared libraries]) + AC_MSG_RESULT([$can_build_shared]) + + AC_MSG_CHECKING([whether to build shared libraries]) + test "$can_build_shared" = "no" && enable_shared=no + + # On AIX, shared libraries and static libraries use the same namespace, and + # are all built from PIC. + case $host_os in + aix3*) + test "$enable_shared" = yes && enable_static=no + if test -n "$RANLIB"; then + archive_cmds="$archive_cmds~\$RANLIB \$lib" + postinstall_cmds='$RANLIB $lib' + fi + ;; + aix[[4-9]]*) + if test "$host_cpu" != ia64 && test "$aix_use_runtimelinking" = no ; then + test "$enable_shared" = yes && enable_static=no + fi + ;; + esac + AC_MSG_RESULT([$enable_shared]) + + AC_MSG_CHECKING([whether to build static libraries]) + # Make sure either enable_shared or enable_static is yes. + test "$enable_shared" = yes || enable_static=yes + AC_MSG_RESULT([$enable_static]) + + _LT_TAGVAR(GCC, $1)="$ac_cv_fc_compiler_gnu" + _LT_TAGVAR(LD, $1)="$LD" + + ## CAVEAT EMPTOR: + ## There is no encapsulation within the following macros, do not change + ## the running order or otherwise move them around unless you know exactly + ## what you are doing... + _LT_SYS_HIDDEN_LIBDEPS($1) + _LT_COMPILER_PIC($1) + _LT_COMPILER_C_O($1) + _LT_COMPILER_FILE_LOCKS($1) + _LT_LINKER_SHLIBS($1) + _LT_SYS_DYNAMIC_LINKER($1) + _LT_LINKER_HARDCODE_LIBPATH($1) + + _LT_CONFIG($1) + fi # test -n "$compiler" + + GCC=$lt_save_GCC + CC=$lt_save_CC + CFLAGS=$lt_save_CFLAGS +fi # test "$_lt_disable_FC" != yes + +AC_LANG_POP +])# _LT_LANG_FC_CONFIG + + +# _LT_LANG_GCJ_CONFIG([TAG]) +# -------------------------- +# Ensure that the configuration variables for the GNU Java Compiler compiler +# are suitably defined. These variables are subsequently used by _LT_CONFIG +# to write the compiler configuration to `libtool'. +m4_defun([_LT_LANG_GCJ_CONFIG], +[AC_REQUIRE([LT_PROG_GCJ])dnl +AC_LANG_SAVE + +# Source file extension for Java test sources. +ac_ext=java + +# Object file extension for compiled Java test sources. +objext=o +_LT_TAGVAR(objext, $1)=$objext + +# Code to be used in simple compile tests +lt_simple_compile_test_code="class foo {}" + +# Code to be used in simple link tests +lt_simple_link_test_code='public class conftest { public static void main(String[[]] argv) {}; }' + +# ltmain only uses $CC for tagged configurations so make sure $CC is set. +_LT_TAG_COMPILER + +# save warnings/boilerplate of simple test code +_LT_COMPILER_BOILERPLATE +_LT_LINKER_BOILERPLATE + +# Allow CC to be a program name with arguments. +lt_save_CC=$CC +lt_save_CFLAGS=$CFLAGS +lt_save_GCC=$GCC +GCC=yes +CC=${GCJ-"gcj"} +CFLAGS=$GCJFLAGS +compiler=$CC +_LT_TAGVAR(compiler, $1)=$CC +_LT_TAGVAR(LD, $1)="$LD" +_LT_CC_BASENAME([$compiler]) + +# GCJ did not exist at the time GCC didn't implicitly link libc in. +_LT_TAGVAR(archive_cmds_need_lc, $1)=no + +_LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds +_LT_TAGVAR(reload_flag, $1)=$reload_flag +_LT_TAGVAR(reload_cmds, $1)=$reload_cmds + +## CAVEAT EMPTOR: +## There is no encapsulation within the following macros, do not change +## the running order or otherwise move them around unless you know exactly +## what you are doing... +if test -n "$compiler"; then + _LT_COMPILER_NO_RTTI($1) + _LT_COMPILER_PIC($1) + _LT_COMPILER_C_O($1) + _LT_COMPILER_FILE_LOCKS($1) + _LT_LINKER_SHLIBS($1) + _LT_LINKER_HARDCODE_LIBPATH($1) + + _LT_CONFIG($1) +fi + +AC_LANG_RESTORE + +GCC=$lt_save_GCC +CC=$lt_save_CC +CFLAGS=$lt_save_CFLAGS +])# _LT_LANG_GCJ_CONFIG + + +# _LT_LANG_GO_CONFIG([TAG]) +# -------------------------- +# Ensure that the configuration variables for the GNU Go compiler +# are suitably defined. These variables are subsequently used by _LT_CONFIG +# to write the compiler configuration to `libtool'. +m4_defun([_LT_LANG_GO_CONFIG], +[AC_REQUIRE([LT_PROG_GO])dnl +AC_LANG_SAVE + +# Source file extension for Go test sources. +ac_ext=go + +# Object file extension for compiled Go test sources. +objext=o +_LT_TAGVAR(objext, $1)=$objext + +# Code to be used in simple compile tests +lt_simple_compile_test_code="package main; func main() { }" + +# Code to be used in simple link tests +lt_simple_link_test_code='package main; func main() { }' + +# ltmain only uses $CC for tagged configurations so make sure $CC is set. +_LT_TAG_COMPILER + +# save warnings/boilerplate of simple test code +_LT_COMPILER_BOILERPLATE +_LT_LINKER_BOILERPLATE + +# Allow CC to be a program name with arguments. +lt_save_CC=$CC +lt_save_CFLAGS=$CFLAGS +lt_save_GCC=$GCC +GCC=yes +CC=${GOC-"gccgo"} +CFLAGS=$GOFLAGS +compiler=$CC +_LT_TAGVAR(compiler, $1)=$CC +_LT_TAGVAR(LD, $1)="$LD" +_LT_CC_BASENAME([$compiler]) + +# Go did not exist at the time GCC didn't implicitly link libc in. +_LT_TAGVAR(archive_cmds_need_lc, $1)=no + +_LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds +_LT_TAGVAR(reload_flag, $1)=$reload_flag +_LT_TAGVAR(reload_cmds, $1)=$reload_cmds + +## CAVEAT EMPTOR: +## There is no encapsulation within the following macros, do not change +## the running order or otherwise move them around unless you know exactly +## what you are doing... +if test -n "$compiler"; then + _LT_COMPILER_NO_RTTI($1) + _LT_COMPILER_PIC($1) + _LT_COMPILER_C_O($1) + _LT_COMPILER_FILE_LOCKS($1) + _LT_LINKER_SHLIBS($1) + _LT_LINKER_HARDCODE_LIBPATH($1) + + _LT_CONFIG($1) +fi + +AC_LANG_RESTORE + +GCC=$lt_save_GCC +CC=$lt_save_CC +CFLAGS=$lt_save_CFLAGS +])# _LT_LANG_GO_CONFIG + + +# _LT_LANG_RC_CONFIG([TAG]) +# ------------------------- +# Ensure that the configuration variables for the Windows resource compiler +# are suitably defined. These variables are subsequently used by _LT_CONFIG +# to write the compiler configuration to `libtool'. +m4_defun([_LT_LANG_RC_CONFIG], +[AC_REQUIRE([LT_PROG_RC])dnl +AC_LANG_SAVE + +# Source file extension for RC test sources. +ac_ext=rc + +# Object file extension for compiled RC test sources. +objext=o +_LT_TAGVAR(objext, $1)=$objext + +# Code to be used in simple compile tests +lt_simple_compile_test_code='sample MENU { MENUITEM "&Soup", 100, CHECKED }' + +# Code to be used in simple link tests +lt_simple_link_test_code="$lt_simple_compile_test_code" + +# ltmain only uses $CC for tagged configurations so make sure $CC is set. +_LT_TAG_COMPILER + +# save warnings/boilerplate of simple test code +_LT_COMPILER_BOILERPLATE +_LT_LINKER_BOILERPLATE + +# Allow CC to be a program name with arguments. +lt_save_CC="$CC" +lt_save_CFLAGS=$CFLAGS +lt_save_GCC=$GCC +GCC= +CC=${RC-"windres"} +CFLAGS= +compiler=$CC +_LT_TAGVAR(compiler, $1)=$CC +_LT_CC_BASENAME([$compiler]) +_LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)=yes + +if test -n "$compiler"; then + : + _LT_CONFIG($1) +fi + +GCC=$lt_save_GCC +AC_LANG_RESTORE +CC=$lt_save_CC +CFLAGS=$lt_save_CFLAGS +])# _LT_LANG_RC_CONFIG + + +# LT_PROG_GCJ +# ----------- +AC_DEFUN([LT_PROG_GCJ], +[m4_ifdef([AC_PROG_GCJ], [AC_PROG_GCJ], + [m4_ifdef([A][M_PROG_GCJ], [A][M_PROG_GCJ], + [AC_CHECK_TOOL(GCJ, gcj,) + test "x${GCJFLAGS+set}" = xset || GCJFLAGS="-g -O2" + AC_SUBST(GCJFLAGS)])])[]dnl +]) + +# Old name: +AU_ALIAS([LT_AC_PROG_GCJ], [LT_PROG_GCJ]) +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([LT_AC_PROG_GCJ], []) + + +# LT_PROG_GO +# ---------- +AC_DEFUN([LT_PROG_GO], +[AC_CHECK_TOOL(GOC, gccgo,) +]) + + +# LT_PROG_RC +# ---------- +AC_DEFUN([LT_PROG_RC], +[AC_CHECK_TOOL(RC, windres,) +]) + +# Old name: +AU_ALIAS([LT_AC_PROG_RC], [LT_PROG_RC]) +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([LT_AC_PROG_RC], []) + + +# _LT_DECL_EGREP +# -------------- +# If we don't have a new enough Autoconf to choose the best grep +# available, choose the one first in the user's PATH. +m4_defun([_LT_DECL_EGREP], +[AC_REQUIRE([AC_PROG_EGREP])dnl +AC_REQUIRE([AC_PROG_FGREP])dnl +test -z "$GREP" && GREP=grep +_LT_DECL([], [GREP], [1], [A grep program that handles long lines]) +_LT_DECL([], [EGREP], [1], [An ERE matcher]) +_LT_DECL([], [FGREP], [1], [A literal string matcher]) +dnl Non-bleeding-edge autoconf doesn't subst GREP, so do it here too +AC_SUBST([GREP]) +]) + + +# _LT_DECL_OBJDUMP +# -------------- +# If we don't have a new enough Autoconf to choose the best objdump +# available, choose the one first in the user's PATH. +m4_defun([_LT_DECL_OBJDUMP], +[AC_CHECK_TOOL(OBJDUMP, objdump, false) +test -z "$OBJDUMP" && OBJDUMP=objdump +_LT_DECL([], [OBJDUMP], [1], [An object symbol dumper]) +AC_SUBST([OBJDUMP]) +]) + +# _LT_DECL_DLLTOOL +# ---------------- +# Ensure DLLTOOL variable is set. +m4_defun([_LT_DECL_DLLTOOL], +[AC_CHECK_TOOL(DLLTOOL, dlltool, false) +test -z "$DLLTOOL" && DLLTOOL=dlltool +_LT_DECL([], [DLLTOOL], [1], [DLL creation program]) +AC_SUBST([DLLTOOL]) +]) + +# _LT_DECL_SED +# ------------ +# Check for a fully-functional sed program, that truncates +# as few characters as possible. Prefer GNU sed if found. +m4_defun([_LT_DECL_SED], +[AC_PROG_SED +test -z "$SED" && SED=sed +Xsed="$SED -e 1s/^X//" +_LT_DECL([], [SED], [1], [A sed program that does not truncate output]) +_LT_DECL([], [Xsed], ["\$SED -e 1s/^X//"], + [Sed that helps us avoid accidentally triggering echo(1) options like -n]) +])# _LT_DECL_SED + +m4_ifndef([AC_PROG_SED], [ +############################################################ +# NOTE: This macro has been submitted for inclusion into # +# GNU Autoconf as AC_PROG_SED. When it is available in # +# a released version of Autoconf we should remove this # +# macro and use it instead. # +############################################################ + +m4_defun([AC_PROG_SED], +[AC_MSG_CHECKING([for a sed that does not truncate output]) +AC_CACHE_VAL(lt_cv_path_SED, +[# Loop through the user's path and test for sed and gsed. +# Then use that list of sed's as ones to test for truncation. +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for lt_ac_prog in sed gsed; do + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$lt_ac_prog$ac_exec_ext"; then + lt_ac_sed_list="$lt_ac_sed_list $as_dir/$lt_ac_prog$ac_exec_ext" + fi + done + done +done +IFS=$as_save_IFS +lt_ac_max=0 +lt_ac_count=0 +# Add /usr/xpg4/bin/sed as it is typically found on Solaris +# along with /bin/sed that truncates output. +for lt_ac_sed in $lt_ac_sed_list /usr/xpg4/bin/sed; do + test ! -f $lt_ac_sed && continue + cat /dev/null > conftest.in + lt_ac_count=0 + echo $ECHO_N "0123456789$ECHO_C" >conftest.in + # Check for GNU sed and select it if it is found. + if "$lt_ac_sed" --version 2>&1 < /dev/null | grep 'GNU' > /dev/null; then + lt_cv_path_SED=$lt_ac_sed + break + fi + while true; do + cat conftest.in conftest.in >conftest.tmp + mv conftest.tmp conftest.in + cp conftest.in conftest.nl + echo >>conftest.nl + $lt_ac_sed -e 's/a$//' < conftest.nl >conftest.out || break + cmp -s conftest.out conftest.nl || break + # 10000 chars as input seems more than enough + test $lt_ac_count -gt 10 && break + lt_ac_count=`expr $lt_ac_count + 1` + if test $lt_ac_count -gt $lt_ac_max; then + lt_ac_max=$lt_ac_count + lt_cv_path_SED=$lt_ac_sed + fi + done +done +]) +SED=$lt_cv_path_SED +AC_SUBST([SED]) +AC_MSG_RESULT([$SED]) +])#AC_PROG_SED +])#m4_ifndef + +# Old name: +AU_ALIAS([LT_AC_PROG_SED], [AC_PROG_SED]) +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([LT_AC_PROG_SED], []) + + +# _LT_CHECK_SHELL_FEATURES +# ------------------------ +# Find out whether the shell is Bourne or XSI compatible, +# or has some other useful features. +m4_defun([_LT_CHECK_SHELL_FEATURES], +[AC_MSG_CHECKING([whether the shell understands some XSI constructs]) +# Try some XSI features +xsi_shell=no +( _lt_dummy="a/b/c" + test "${_lt_dummy##*/},${_lt_dummy%/*},${_lt_dummy#??}"${_lt_dummy%"$_lt_dummy"}, \ + = c,a/b,b/c, \ + && eval 'test $(( 1 + 1 )) -eq 2 \ + && test "${#_lt_dummy}" -eq 5' ) >/dev/null 2>&1 \ + && xsi_shell=yes +AC_MSG_RESULT([$xsi_shell]) +_LT_CONFIG_LIBTOOL_INIT([xsi_shell='$xsi_shell']) + +AC_MSG_CHECKING([whether the shell understands "+="]) +lt_shell_append=no +( foo=bar; set foo baz; eval "$[1]+=\$[2]" && test "$foo" = barbaz ) \ + >/dev/null 2>&1 \ + && lt_shell_append=yes +AC_MSG_RESULT([$lt_shell_append]) +_LT_CONFIG_LIBTOOL_INIT([lt_shell_append='$lt_shell_append']) + +if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then + lt_unset=unset +else + lt_unset=false +fi +_LT_DECL([], [lt_unset], [0], [whether the shell understands "unset"])dnl + +# test EBCDIC or ASCII +case `echo X|tr X '\101'` in + A) # ASCII based system + # \n is not interpreted correctly by Solaris 8 /usr/ucb/tr + lt_SP2NL='tr \040 \012' + lt_NL2SP='tr \015\012 \040\040' + ;; + *) # EBCDIC based system + lt_SP2NL='tr \100 \n' + lt_NL2SP='tr \r\n \100\100' + ;; +esac +_LT_DECL([SP2NL], [lt_SP2NL], [1], [turn spaces into newlines])dnl +_LT_DECL([NL2SP], [lt_NL2SP], [1], [turn newlines into spaces])dnl +])# _LT_CHECK_SHELL_FEATURES + + +# _LT_PROG_FUNCTION_REPLACE (FUNCNAME, REPLACEMENT-BODY) +# ------------------------------------------------------ +# In `$cfgfile', look for function FUNCNAME delimited by `^FUNCNAME ()$' and +# '^} FUNCNAME ', and replace its body with REPLACEMENT-BODY. +m4_defun([_LT_PROG_FUNCTION_REPLACE], +[dnl { +sed -e '/^$1 ()$/,/^} # $1 /c\ +$1 ()\ +{\ +m4_bpatsubsts([$2], [$], [\\], [^\([ ]\)], [\\\1]) +} # Extended-shell $1 implementation' "$cfgfile" > $cfgfile.tmp \ + && mv -f "$cfgfile.tmp" "$cfgfile" \ + || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") +test 0 -eq $? || _lt_function_replace_fail=: +]) + + +# _LT_PROG_REPLACE_SHELLFNS +# ------------------------- +# Replace existing portable implementations of several shell functions with +# equivalent extended shell implementations where those features are available.. +m4_defun([_LT_PROG_REPLACE_SHELLFNS], +[if test x"$xsi_shell" = xyes; then + _LT_PROG_FUNCTION_REPLACE([func_dirname], [dnl + case ${1} in + */*) func_dirname_result="${1%/*}${2}" ;; + * ) func_dirname_result="${3}" ;; + esac]) + + _LT_PROG_FUNCTION_REPLACE([func_basename], [dnl + func_basename_result="${1##*/}"]) + + _LT_PROG_FUNCTION_REPLACE([func_dirname_and_basename], [dnl + case ${1} in + */*) func_dirname_result="${1%/*}${2}" ;; + * ) func_dirname_result="${3}" ;; + esac + func_basename_result="${1##*/}"]) + + _LT_PROG_FUNCTION_REPLACE([func_stripname], [dnl + # pdksh 5.2.14 does not do ${X%$Y} correctly if both X and Y are + # positional parameters, so assign one to ordinary parameter first. + func_stripname_result=${3} + func_stripname_result=${func_stripname_result#"${1}"} + func_stripname_result=${func_stripname_result%"${2}"}]) + + _LT_PROG_FUNCTION_REPLACE([func_split_long_opt], [dnl + func_split_long_opt_name=${1%%=*} + func_split_long_opt_arg=${1#*=}]) + + _LT_PROG_FUNCTION_REPLACE([func_split_short_opt], [dnl + func_split_short_opt_arg=${1#??} + func_split_short_opt_name=${1%"$func_split_short_opt_arg"}]) + + _LT_PROG_FUNCTION_REPLACE([func_lo2o], [dnl + case ${1} in + *.lo) func_lo2o_result=${1%.lo}.${objext} ;; + *) func_lo2o_result=${1} ;; + esac]) + + _LT_PROG_FUNCTION_REPLACE([func_xform], [ func_xform_result=${1%.*}.lo]) + + _LT_PROG_FUNCTION_REPLACE([func_arith], [ func_arith_result=$(( $[*] ))]) + + _LT_PROG_FUNCTION_REPLACE([func_len], [ func_len_result=${#1}]) +fi + +if test x"$lt_shell_append" = xyes; then + _LT_PROG_FUNCTION_REPLACE([func_append], [ eval "${1}+=\\${2}"]) + + _LT_PROG_FUNCTION_REPLACE([func_append_quoted], [dnl + func_quote_for_eval "${2}" +dnl m4 expansion turns \\\\ into \\, and then the shell eval turns that into \ + eval "${1}+=\\\\ \\$func_quote_for_eval_result"]) + + # Save a `func_append' function call where possible by direct use of '+=' + sed -e 's%func_append \([[a-zA-Z_]]\{1,\}\) "%\1+="%g' $cfgfile > $cfgfile.tmp \ + && mv -f "$cfgfile.tmp" "$cfgfile" \ + || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") + test 0 -eq $? || _lt_function_replace_fail=: +else + # Save a `func_append' function call even when '+=' is not available + sed -e 's%func_append \([[a-zA-Z_]]\{1,\}\) "%\1="$\1%g' $cfgfile > $cfgfile.tmp \ + && mv -f "$cfgfile.tmp" "$cfgfile" \ + || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") + test 0 -eq $? || _lt_function_replace_fail=: +fi + +if test x"$_lt_function_replace_fail" = x":"; then + AC_MSG_WARN([Unable to substitute extended shell functions in $ofile]) +fi +]) + +# _LT_PATH_CONVERSION_FUNCTIONS +# ----------------------------- +# Determine which file name conversion functions should be used by +# func_to_host_file (and, implicitly, by func_to_host_path). These are needed +# for certain cross-compile configurations and native mingw. +m4_defun([_LT_PATH_CONVERSION_FUNCTIONS], +[AC_REQUIRE([AC_CANONICAL_HOST])dnl +AC_REQUIRE([AC_CANONICAL_BUILD])dnl +AC_MSG_CHECKING([how to convert $build file names to $host format]) +AC_CACHE_VAL(lt_cv_to_host_file_cmd, +[case $host in + *-*-mingw* ) + case $build in + *-*-mingw* ) # actually msys + lt_cv_to_host_file_cmd=func_convert_file_msys_to_w32 + ;; + *-*-cygwin* ) + lt_cv_to_host_file_cmd=func_convert_file_cygwin_to_w32 + ;; + * ) # otherwise, assume *nix + lt_cv_to_host_file_cmd=func_convert_file_nix_to_w32 + ;; + esac + ;; + *-*-cygwin* ) + case $build in + *-*-mingw* ) # actually msys + lt_cv_to_host_file_cmd=func_convert_file_msys_to_cygwin + ;; + *-*-cygwin* ) + lt_cv_to_host_file_cmd=func_convert_file_noop + ;; + * ) # otherwise, assume *nix + lt_cv_to_host_file_cmd=func_convert_file_nix_to_cygwin + ;; + esac + ;; + * ) # unhandled hosts (and "normal" native builds) + lt_cv_to_host_file_cmd=func_convert_file_noop + ;; +esac +]) +to_host_file_cmd=$lt_cv_to_host_file_cmd +AC_MSG_RESULT([$lt_cv_to_host_file_cmd]) +_LT_DECL([to_host_file_cmd], [lt_cv_to_host_file_cmd], + [0], [convert $build file names to $host format])dnl + +AC_MSG_CHECKING([how to convert $build file names to toolchain format]) +AC_CACHE_VAL(lt_cv_to_tool_file_cmd, +[#assume ordinary cross tools, or native build. +lt_cv_to_tool_file_cmd=func_convert_file_noop +case $host in + *-*-mingw* ) + case $build in + *-*-mingw* ) # actually msys + lt_cv_to_tool_file_cmd=func_convert_file_msys_to_w32 + ;; + esac + ;; +esac +]) +to_tool_file_cmd=$lt_cv_to_tool_file_cmd +AC_MSG_RESULT([$lt_cv_to_tool_file_cmd]) +_LT_DECL([to_tool_file_cmd], [lt_cv_to_tool_file_cmd], + [0], [convert $build files to toolchain format])dnl +])# _LT_PATH_CONVERSION_FUNCTIONS diff --git a/m4/libxml2.m4 b/m4/libxml2.m4 new file mode 100644 index 0000000..68cd824 --- /dev/null +++ b/m4/libxml2.m4 @@ -0,0 +1,188 @@ +# Configure paths for LIBXML2 +# Mike Hommey 2004-06-19 +# use CPPFLAGS instead of CFLAGS +# Toshio Kuratomi 2001-04-21 +# Adapted from: +# Configure paths for GLIB +# Owen Taylor 97-11-3 + +dnl AM_PATH_XML2([MINIMUM-VERSION, [ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND]]]) +dnl Test for XML, and define XML_CPPFLAGS and XML_LIBS +dnl +AC_DEFUN([AM_PATH_XML2],[ +AC_ARG_WITH(xml-prefix, + [ --with-xml-prefix=PFX Prefix where libxml is installed (optional)], + xml_config_prefix="$withval", xml_config_prefix="") +AC_ARG_WITH(xml-exec-prefix, + [ --with-xml-exec-prefix=PFX Exec prefix where libxml is installed (optional)], + xml_config_exec_prefix="$withval", xml_config_exec_prefix="") +AC_ARG_ENABLE(xmltest, + [ --disable-xmltest Do not try to compile and run a test LIBXML program],, + enable_xmltest=yes) + + if test x$xml_config_exec_prefix != x ; then + xml_config_args="$xml_config_args" + if test x${XML2_CONFIG+set} != xset ; then + XML2_CONFIG=$xml_config_exec_prefix/bin/xml2-config + fi + fi + if test x$xml_config_prefix != x ; then + xml_config_args="$xml_config_args --prefix=$xml_config_prefix" + if test x${XML2_CONFIG+set} != xset ; then + XML2_CONFIG=$xml_config_prefix/bin/xml2-config + fi + fi + + AC_PATH_PROG(XML2_CONFIG, xml2-config, no) + min_xml_version=ifelse([$1], ,2.0.0,[$1]) + AC_MSG_CHECKING(for libxml - version >= $min_xml_version) + no_xml="" + if test "$XML2_CONFIG" = "no" ; then + no_xml=yes + else + XML_CPPFLAGS=`$XML2_CONFIG $xml_config_args --cflags` + XML_LIBS=`$XML2_CONFIG $xml_config_args --libs` + xml_config_major_version=`$XML2_CONFIG $xml_config_args --version | \ + sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\1/'` + xml_config_minor_version=`$XML2_CONFIG $xml_config_args --version | \ + sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\2/'` + xml_config_micro_version=`$XML2_CONFIG $xml_config_args --version | \ + sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\3/'` + if test "x$enable_xmltest" = "xyes" ; then + ac_save_CPPFLAGS="$CPPFLAGS" + ac_save_LIBS="$LIBS" + CPPFLAGS="$CPPFLAGS $XML_CPPFLAGS" + LIBS="$XML_LIBS $LIBS" +dnl +dnl Now check if the installed libxml is sufficiently new. +dnl (Also sanity checks the results of xml2-config to some extent) +dnl + rm -f conf.xmltest + AC_TRY_RUN([ +#include +#include +#include +#include + +int +main() +{ + int xml_major_version, xml_minor_version, xml_micro_version; + int major, minor, micro; + char *tmp_version; + + system("touch conf.xmltest"); + + /* Capture xml2-config output via autoconf/configure variables */ + /* HP/UX 9 (%@#!) writes to sscanf strings */ + tmp_version = (char *)strdup("$min_xml_version"); + if (sscanf(tmp_version, "%d.%d.%d", &major, &minor, µ) != 3) { + printf("%s, bad version string from xml2-config\n", "$min_xml_version"); + exit(1); + } + free(tmp_version); + + /* Capture the version information from the header files */ + tmp_version = (char *)strdup(LIBXML_DOTTED_VERSION); + if (sscanf(tmp_version, "%d.%d.%d", &xml_major_version, &xml_minor_version, &xml_micro_version) != 3) { + printf("%s, bad version string from libxml includes\n", "LIBXML_DOTTED_VERSION"); + exit(1); + } + free(tmp_version); + + /* Compare xml2-config output to the libxml headers */ + if ((xml_major_version != $xml_config_major_version) || + (xml_minor_version != $xml_config_minor_version) || + (xml_micro_version != $xml_config_micro_version)) + { + printf("*** libxml header files (version %d.%d.%d) do not match\n", + xml_major_version, xml_minor_version, xml_micro_version); + printf("*** xml2-config (version %d.%d.%d)\n", + $xml_config_major_version, $xml_config_minor_version, $xml_config_micro_version); + return 1; + } +/* Compare the headers to the library to make sure we match */ + /* Less than ideal -- doesn't provide us with return value feedback, + * only exits if there's a serious mismatch between header and library. + */ + LIBXML_TEST_VERSION; + + /* Test that the library is greater than our minimum version */ + if ((xml_major_version > major) || + ((xml_major_version == major) && (xml_minor_version > minor)) || + ((xml_major_version == major) && (xml_minor_version == minor) && + (xml_micro_version >= micro))) + { + return 0; + } + else + { + printf("\n*** An old version of libxml (%d.%d.%d) was found.\n", + xml_major_version, xml_minor_version, xml_micro_version); + printf("*** You need a version of libxml newer than %d.%d.%d. The latest version of\n", + major, minor, micro); + printf("*** libxml is always available from ftp://ftp.xmlsoft.org.\n"); + printf("***\n"); + printf("*** If you have already installed a sufficiently new version, this error\n"); + printf("*** probably means that the wrong copy of the xml2-config shell script is\n"); + printf("*** being found. The easiest way to fix this is to remove the old version\n"); + printf("*** of LIBXML, but you can also set the XML2_CONFIG environment to point to the\n"); + printf("*** correct copy of xml2-config. (In this case, you will have to\n"); + printf("*** modify your LD_LIBRARY_PATH enviroment variable, or edit /etc/ld.so.conf\n"); + printf("*** so that the correct libraries are found at run-time))\n"); + } + return 1; +} +],, no_xml=yes,[echo $ac_n "cross compiling; assumed OK... $ac_c"]) + CPPFLAGS="$ac_save_CPPFLAGS" + LIBS="$ac_save_LIBS" + fi + fi + + if test "x$no_xml" = x ; then + AC_MSG_RESULT(yes (version $xml_config_major_version.$xml_config_minor_version.$xml_config_micro_version)) + ifelse([$2], , :, [$2]) + else + AC_MSG_RESULT(no) + if test "$XML2_CONFIG" = "no" ; then + echo "*** The xml2-config script installed by LIBXML could not be found" + echo "*** If libxml was installed in PREFIX, make sure PREFIX/bin is in" + echo "*** your path, or set the XML2_CONFIG environment variable to the" + echo "*** full path to xml2-config." + else + if test -f conf.xmltest ; then + : + else + echo "*** Could not run libxml test program, checking why..." + CPPFLAGS="$CPPFLAGS $XML_CPPFLAGS" + LIBS="$LIBS $XML_LIBS" + AC_TRY_LINK([ +#include +#include +], [ LIBXML_TEST_VERSION; return 0;], + [ echo "*** The test program compiled, but did not run. This usually means" + echo "*** that the run-time linker is not finding LIBXML or finding the wrong" + echo "*** version of LIBXML. If it is not finding LIBXML, you'll need to set your" + echo "*** LD_LIBRARY_PATH environment variable, or edit /etc/ld.so.conf to point" + echo "*** to the installed location Also, make sure you have run ldconfig if that" + echo "*** is required on your system" + echo "***" + echo "*** If you have an old version installed, it is best to remove it, although" + echo "*** you may also be able to get things to work by modifying LD_LIBRARY_PATH" ], + [ echo "*** The test program failed to compile or link. See the file config.log for the" + echo "*** exact error that occured. This usually means LIBXML was incorrectly installed" + echo "*** or that you have moved LIBXML since it was installed. In the latter case, you" + echo "*** may want to edit the xml2-config script: $XML2_CONFIG" ]) + CPPFLAGS="$ac_save_CPPFLAGS" + LIBS="$ac_save_LIBS" + fi + fi + + XML_CPPFLAGS="" + XML_LIBS="" + ifelse([$3], , :, [$3]) + fi + AC_SUBST(XML_CPPFLAGS) + AC_SUBST(XML_LIBS) + rm -f conf.xmltest +]) diff --git a/m4/ltoptions.m4 b/m4/ltoptions.m4 new file mode 100644 index 0000000..5d9acd8 --- /dev/null +++ b/m4/ltoptions.m4 @@ -0,0 +1,384 @@ +# Helper functions for option handling. -*- Autoconf -*- +# +# Copyright (C) 2004, 2005, 2007, 2008, 2009 Free Software Foundation, +# Inc. +# Written by Gary V. Vaughan, 2004 +# +# This file is free software; the Free Software Foundation gives +# unlimited permission to copy and/or distribute it, with or without +# modifications, as long as this notice is preserved. + +# serial 7 ltoptions.m4 + +# This is to help aclocal find these macros, as it can't see m4_define. +AC_DEFUN([LTOPTIONS_VERSION], [m4_if([1])]) + + +# _LT_MANGLE_OPTION(MACRO-NAME, OPTION-NAME) +# ------------------------------------------ +m4_define([_LT_MANGLE_OPTION], +[[_LT_OPTION_]m4_bpatsubst($1__$2, [[^a-zA-Z0-9_]], [_])]) + + +# _LT_SET_OPTION(MACRO-NAME, OPTION-NAME) +# --------------------------------------- +# Set option OPTION-NAME for macro MACRO-NAME, and if there is a +# matching handler defined, dispatch to it. Other OPTION-NAMEs are +# saved as a flag. +m4_define([_LT_SET_OPTION], +[m4_define(_LT_MANGLE_OPTION([$1], [$2]))dnl +m4_ifdef(_LT_MANGLE_DEFUN([$1], [$2]), + _LT_MANGLE_DEFUN([$1], [$2]), + [m4_warning([Unknown $1 option `$2'])])[]dnl +]) + + +# _LT_IF_OPTION(MACRO-NAME, OPTION-NAME, IF-SET, [IF-NOT-SET]) +# ------------------------------------------------------------ +# Execute IF-SET if OPTION is set, IF-NOT-SET otherwise. +m4_define([_LT_IF_OPTION], +[m4_ifdef(_LT_MANGLE_OPTION([$1], [$2]), [$3], [$4])]) + + +# _LT_UNLESS_OPTIONS(MACRO-NAME, OPTION-LIST, IF-NOT-SET) +# ------------------------------------------------------- +# Execute IF-NOT-SET unless all options in OPTION-LIST for MACRO-NAME +# are set. +m4_define([_LT_UNLESS_OPTIONS], +[m4_foreach([_LT_Option], m4_split(m4_normalize([$2])), + [m4_ifdef(_LT_MANGLE_OPTION([$1], _LT_Option), + [m4_define([$0_found])])])[]dnl +m4_ifdef([$0_found], [m4_undefine([$0_found])], [$3 +])[]dnl +]) + + +# _LT_SET_OPTIONS(MACRO-NAME, OPTION-LIST) +# ---------------------------------------- +# OPTION-LIST is a space-separated list of Libtool options associated +# with MACRO-NAME. If any OPTION has a matching handler declared with +# LT_OPTION_DEFINE, dispatch to that macro; otherwise complain about +# the unknown option and exit. +m4_defun([_LT_SET_OPTIONS], +[# Set options +m4_foreach([_LT_Option], m4_split(m4_normalize([$2])), + [_LT_SET_OPTION([$1], _LT_Option)]) + +m4_if([$1],[LT_INIT],[ + dnl + dnl Simply set some default values (i.e off) if boolean options were not + dnl specified: + _LT_UNLESS_OPTIONS([LT_INIT], [dlopen], [enable_dlopen=no + ]) + _LT_UNLESS_OPTIONS([LT_INIT], [win32-dll], [enable_win32_dll=no + ]) + dnl + dnl If no reference was made to various pairs of opposing options, then + dnl we run the default mode handler for the pair. For example, if neither + dnl `shared' nor `disable-shared' was passed, we enable building of shared + dnl archives by default: + _LT_UNLESS_OPTIONS([LT_INIT], [shared disable-shared], [_LT_ENABLE_SHARED]) + _LT_UNLESS_OPTIONS([LT_INIT], [static disable-static], [_LT_ENABLE_STATIC]) + _LT_UNLESS_OPTIONS([LT_INIT], [pic-only no-pic], [_LT_WITH_PIC]) + _LT_UNLESS_OPTIONS([LT_INIT], [fast-install disable-fast-install], + [_LT_ENABLE_FAST_INSTALL]) + ]) +])# _LT_SET_OPTIONS + + +## --------------------------------- ## +## Macros to handle LT_INIT options. ## +## --------------------------------- ## + +# _LT_MANGLE_DEFUN(MACRO-NAME, OPTION-NAME) +# ----------------------------------------- +m4_define([_LT_MANGLE_DEFUN], +[[_LT_OPTION_DEFUN_]m4_bpatsubst(m4_toupper([$1__$2]), [[^A-Z0-9_]], [_])]) + + +# LT_OPTION_DEFINE(MACRO-NAME, OPTION-NAME, CODE) +# ----------------------------------------------- +m4_define([LT_OPTION_DEFINE], +[m4_define(_LT_MANGLE_DEFUN([$1], [$2]), [$3])[]dnl +])# LT_OPTION_DEFINE + + +# dlopen +# ------ +LT_OPTION_DEFINE([LT_INIT], [dlopen], [enable_dlopen=yes +]) + +AU_DEFUN([AC_LIBTOOL_DLOPEN], +[_LT_SET_OPTION([LT_INIT], [dlopen]) +AC_DIAGNOSE([obsolete], +[$0: Remove this warning and the call to _LT_SET_OPTION when you +put the `dlopen' option into LT_INIT's first parameter.]) +]) + +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AC_LIBTOOL_DLOPEN], []) + + +# win32-dll +# --------- +# Declare package support for building win32 dll's. +LT_OPTION_DEFINE([LT_INIT], [win32-dll], +[enable_win32_dll=yes + +case $host in +*-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-cegcc*) + AC_CHECK_TOOL(AS, as, false) + AC_CHECK_TOOL(DLLTOOL, dlltool, false) + AC_CHECK_TOOL(OBJDUMP, objdump, false) + ;; +esac + +test -z "$AS" && AS=as +_LT_DECL([], [AS], [1], [Assembler program])dnl + +test -z "$DLLTOOL" && DLLTOOL=dlltool +_LT_DECL([], [DLLTOOL], [1], [DLL creation program])dnl + +test -z "$OBJDUMP" && OBJDUMP=objdump +_LT_DECL([], [OBJDUMP], [1], [Object dumper program])dnl +])# win32-dll + +AU_DEFUN([AC_LIBTOOL_WIN32_DLL], +[AC_REQUIRE([AC_CANONICAL_HOST])dnl +_LT_SET_OPTION([LT_INIT], [win32-dll]) +AC_DIAGNOSE([obsolete], +[$0: Remove this warning and the call to _LT_SET_OPTION when you +put the `win32-dll' option into LT_INIT's first parameter.]) +]) + +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AC_LIBTOOL_WIN32_DLL], []) + + +# _LT_ENABLE_SHARED([DEFAULT]) +# ---------------------------- +# implement the --enable-shared flag, and supports the `shared' and +# `disable-shared' LT_INIT options. +# DEFAULT is either `yes' or `no'. If omitted, it defaults to `yes'. +m4_define([_LT_ENABLE_SHARED], +[m4_define([_LT_ENABLE_SHARED_DEFAULT], [m4_if($1, no, no, yes)])dnl +AC_ARG_ENABLE([shared], + [AS_HELP_STRING([--enable-shared@<:@=PKGS@:>@], + [build shared libraries @<:@default=]_LT_ENABLE_SHARED_DEFAULT[@:>@])], + [p=${PACKAGE-default} + case $enableval in + yes) enable_shared=yes ;; + no) enable_shared=no ;; + *) + enable_shared=no + # Look at the argument we got. We use all the common list separators. + lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR," + for pkg in $enableval; do + IFS="$lt_save_ifs" + if test "X$pkg" = "X$p"; then + enable_shared=yes + fi + done + IFS="$lt_save_ifs" + ;; + esac], + [enable_shared=]_LT_ENABLE_SHARED_DEFAULT) + + _LT_DECL([build_libtool_libs], [enable_shared], [0], + [Whether or not to build shared libraries]) +])# _LT_ENABLE_SHARED + +LT_OPTION_DEFINE([LT_INIT], [shared], [_LT_ENABLE_SHARED([yes])]) +LT_OPTION_DEFINE([LT_INIT], [disable-shared], [_LT_ENABLE_SHARED([no])]) + +# Old names: +AC_DEFUN([AC_ENABLE_SHARED], +[_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[shared]) +]) + +AC_DEFUN([AC_DISABLE_SHARED], +[_LT_SET_OPTION([LT_INIT], [disable-shared]) +]) + +AU_DEFUN([AM_ENABLE_SHARED], [AC_ENABLE_SHARED($@)]) +AU_DEFUN([AM_DISABLE_SHARED], [AC_DISABLE_SHARED($@)]) + +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AM_ENABLE_SHARED], []) +dnl AC_DEFUN([AM_DISABLE_SHARED], []) + + + +# _LT_ENABLE_STATIC([DEFAULT]) +# ---------------------------- +# implement the --enable-static flag, and support the `static' and +# `disable-static' LT_INIT options. +# DEFAULT is either `yes' or `no'. If omitted, it defaults to `yes'. +m4_define([_LT_ENABLE_STATIC], +[m4_define([_LT_ENABLE_STATIC_DEFAULT], [m4_if($1, no, no, yes)])dnl +AC_ARG_ENABLE([static], + [AS_HELP_STRING([--enable-static@<:@=PKGS@:>@], + [build static libraries @<:@default=]_LT_ENABLE_STATIC_DEFAULT[@:>@])], + [p=${PACKAGE-default} + case $enableval in + yes) enable_static=yes ;; + no) enable_static=no ;; + *) + enable_static=no + # Look at the argument we got. We use all the common list separators. + lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR," + for pkg in $enableval; do + IFS="$lt_save_ifs" + if test "X$pkg" = "X$p"; then + enable_static=yes + fi + done + IFS="$lt_save_ifs" + ;; + esac], + [enable_static=]_LT_ENABLE_STATIC_DEFAULT) + + _LT_DECL([build_old_libs], [enable_static], [0], + [Whether or not to build static libraries]) +])# _LT_ENABLE_STATIC + +LT_OPTION_DEFINE([LT_INIT], [static], [_LT_ENABLE_STATIC([yes])]) +LT_OPTION_DEFINE([LT_INIT], [disable-static], [_LT_ENABLE_STATIC([no])]) + +# Old names: +AC_DEFUN([AC_ENABLE_STATIC], +[_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[static]) +]) + +AC_DEFUN([AC_DISABLE_STATIC], +[_LT_SET_OPTION([LT_INIT], [disable-static]) +]) + +AU_DEFUN([AM_ENABLE_STATIC], [AC_ENABLE_STATIC($@)]) +AU_DEFUN([AM_DISABLE_STATIC], [AC_DISABLE_STATIC($@)]) + +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AM_ENABLE_STATIC], []) +dnl AC_DEFUN([AM_DISABLE_STATIC], []) + + + +# _LT_ENABLE_FAST_INSTALL([DEFAULT]) +# ---------------------------------- +# implement the --enable-fast-install flag, and support the `fast-install' +# and `disable-fast-install' LT_INIT options. +# DEFAULT is either `yes' or `no'. If omitted, it defaults to `yes'. +m4_define([_LT_ENABLE_FAST_INSTALL], +[m4_define([_LT_ENABLE_FAST_INSTALL_DEFAULT], [m4_if($1, no, no, yes)])dnl +AC_ARG_ENABLE([fast-install], + [AS_HELP_STRING([--enable-fast-install@<:@=PKGS@:>@], + [optimize for fast installation @<:@default=]_LT_ENABLE_FAST_INSTALL_DEFAULT[@:>@])], + [p=${PACKAGE-default} + case $enableval in + yes) enable_fast_install=yes ;; + no) enable_fast_install=no ;; + *) + enable_fast_install=no + # Look at the argument we got. We use all the common list separators. + lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR," + for pkg in $enableval; do + IFS="$lt_save_ifs" + if test "X$pkg" = "X$p"; then + enable_fast_install=yes + fi + done + IFS="$lt_save_ifs" + ;; + esac], + [enable_fast_install=]_LT_ENABLE_FAST_INSTALL_DEFAULT) + +_LT_DECL([fast_install], [enable_fast_install], [0], + [Whether or not to optimize for fast installation])dnl +])# _LT_ENABLE_FAST_INSTALL + +LT_OPTION_DEFINE([LT_INIT], [fast-install], [_LT_ENABLE_FAST_INSTALL([yes])]) +LT_OPTION_DEFINE([LT_INIT], [disable-fast-install], [_LT_ENABLE_FAST_INSTALL([no])]) + +# Old names: +AU_DEFUN([AC_ENABLE_FAST_INSTALL], +[_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[fast-install]) +AC_DIAGNOSE([obsolete], +[$0: Remove this warning and the call to _LT_SET_OPTION when you put +the `fast-install' option into LT_INIT's first parameter.]) +]) + +AU_DEFUN([AC_DISABLE_FAST_INSTALL], +[_LT_SET_OPTION([LT_INIT], [disable-fast-install]) +AC_DIAGNOSE([obsolete], +[$0: Remove this warning and the call to _LT_SET_OPTION when you put +the `disable-fast-install' option into LT_INIT's first parameter.]) +]) + +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AC_ENABLE_FAST_INSTALL], []) +dnl AC_DEFUN([AM_DISABLE_FAST_INSTALL], []) + + +# _LT_WITH_PIC([MODE]) +# -------------------- +# implement the --with-pic flag, and support the `pic-only' and `no-pic' +# LT_INIT options. +# MODE is either `yes' or `no'. If omitted, it defaults to `both'. +m4_define([_LT_WITH_PIC], +[AC_ARG_WITH([pic], + [AS_HELP_STRING([--with-pic@<:@=PKGS@:>@], + [try to use only PIC/non-PIC objects @<:@default=use both@:>@])], + [lt_p=${PACKAGE-default} + case $withval in + yes|no) pic_mode=$withval ;; + *) + pic_mode=default + # Look at the argument we got. We use all the common list separators. + lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR," + for lt_pkg in $withval; do + IFS="$lt_save_ifs" + if test "X$lt_pkg" = "X$lt_p"; then + pic_mode=yes + fi + done + IFS="$lt_save_ifs" + ;; + esac], + [pic_mode=default]) + +test -z "$pic_mode" && pic_mode=m4_default([$1], [default]) + +_LT_DECL([], [pic_mode], [0], [What type of objects to build])dnl +])# _LT_WITH_PIC + +LT_OPTION_DEFINE([LT_INIT], [pic-only], [_LT_WITH_PIC([yes])]) +LT_OPTION_DEFINE([LT_INIT], [no-pic], [_LT_WITH_PIC([no])]) + +# Old name: +AU_DEFUN([AC_LIBTOOL_PICMODE], +[_LT_SET_OPTION([LT_INIT], [pic-only]) +AC_DIAGNOSE([obsolete], +[$0: Remove this warning and the call to _LT_SET_OPTION when you +put the `pic-only' option into LT_INIT's first parameter.]) +]) + +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AC_LIBTOOL_PICMODE], []) + +## ----------------- ## +## LTDL_INIT Options ## +## ----------------- ## + +m4_define([_LTDL_MODE], []) +LT_OPTION_DEFINE([LTDL_INIT], [nonrecursive], + [m4_define([_LTDL_MODE], [nonrecursive])]) +LT_OPTION_DEFINE([LTDL_INIT], [recursive], + [m4_define([_LTDL_MODE], [recursive])]) +LT_OPTION_DEFINE([LTDL_INIT], [subproject], + [m4_define([_LTDL_MODE], [subproject])]) + +m4_define([_LTDL_TYPE], []) +LT_OPTION_DEFINE([LTDL_INIT], [installable], + [m4_define([_LTDL_TYPE], [installable])]) +LT_OPTION_DEFINE([LTDL_INIT], [convenience], + [m4_define([_LTDL_TYPE], [convenience])]) diff --git a/m4/ltsugar.m4 b/m4/ltsugar.m4 new file mode 100644 index 0000000..9000a05 --- /dev/null +++ b/m4/ltsugar.m4 @@ -0,0 +1,123 @@ +# ltsugar.m4 -- libtool m4 base layer. -*-Autoconf-*- +# +# Copyright (C) 2004, 2005, 2007, 2008 Free Software Foundation, Inc. +# Written by Gary V. Vaughan, 2004 +# +# This file is free software; the Free Software Foundation gives +# unlimited permission to copy and/or distribute it, with or without +# modifications, as long as this notice is preserved. + +# serial 6 ltsugar.m4 + +# This is to help aclocal find these macros, as it can't see m4_define. +AC_DEFUN([LTSUGAR_VERSION], [m4_if([0.1])]) + + +# lt_join(SEP, ARG1, [ARG2...]) +# ----------------------------- +# Produce ARG1SEPARG2...SEPARGn, omitting [] arguments and their +# associated separator. +# Needed until we can rely on m4_join from Autoconf 2.62, since all earlier +# versions in m4sugar had bugs. +m4_define([lt_join], +[m4_if([$#], [1], [], + [$#], [2], [[$2]], + [m4_if([$2], [], [], [[$2]_])$0([$1], m4_shift(m4_shift($@)))])]) +m4_define([_lt_join], +[m4_if([$#$2], [2], [], + [m4_if([$2], [], [], [[$1$2]])$0([$1], m4_shift(m4_shift($@)))])]) + + +# lt_car(LIST) +# lt_cdr(LIST) +# ------------ +# Manipulate m4 lists. +# These macros are necessary as long as will still need to support +# Autoconf-2.59 which quotes differently. +m4_define([lt_car], [[$1]]) +m4_define([lt_cdr], +[m4_if([$#], 0, [m4_fatal([$0: cannot be called without arguments])], + [$#], 1, [], + [m4_dquote(m4_shift($@))])]) +m4_define([lt_unquote], $1) + + +# lt_append(MACRO-NAME, STRING, [SEPARATOR]) +# ------------------------------------------ +# Redefine MACRO-NAME to hold its former content plus `SEPARATOR'`STRING'. +# Note that neither SEPARATOR nor STRING are expanded; they are appended +# to MACRO-NAME as is (leaving the expansion for when MACRO-NAME is invoked). +# No SEPARATOR is output if MACRO-NAME was previously undefined (different +# than defined and empty). +# +# This macro is needed until we can rely on Autoconf 2.62, since earlier +# versions of m4sugar mistakenly expanded SEPARATOR but not STRING. +m4_define([lt_append], +[m4_define([$1], + m4_ifdef([$1], [m4_defn([$1])[$3]])[$2])]) + + + +# lt_combine(SEP, PREFIX-LIST, INFIX, SUFFIX1, [SUFFIX2...]) +# ---------------------------------------------------------- +# Produce a SEP delimited list of all paired combinations of elements of +# PREFIX-LIST with SUFFIX1 through SUFFIXn. Each element of the list +# has the form PREFIXmINFIXSUFFIXn. +# Needed until we can rely on m4_combine added in Autoconf 2.62. +m4_define([lt_combine], +[m4_if(m4_eval([$# > 3]), [1], + [m4_pushdef([_Lt_sep], [m4_define([_Lt_sep], m4_defn([lt_car]))])]]dnl +[[m4_foreach([_Lt_prefix], [$2], + [m4_foreach([_Lt_suffix], + ]m4_dquote(m4_dquote(m4_shift(m4_shift(m4_shift($@)))))[, + [_Lt_sep([$1])[]m4_defn([_Lt_prefix])[$3]m4_defn([_Lt_suffix])])])])]) + + +# lt_if_append_uniq(MACRO-NAME, VARNAME, [SEPARATOR], [UNIQ], [NOT-UNIQ]) +# ----------------------------------------------------------------------- +# Iff MACRO-NAME does not yet contain VARNAME, then append it (delimited +# by SEPARATOR if supplied) and expand UNIQ, else NOT-UNIQ. +m4_define([lt_if_append_uniq], +[m4_ifdef([$1], + [m4_if(m4_index([$3]m4_defn([$1])[$3], [$3$2$3]), [-1], + [lt_append([$1], [$2], [$3])$4], + [$5])], + [lt_append([$1], [$2], [$3])$4])]) + + +# lt_dict_add(DICT, KEY, VALUE) +# ----------------------------- +m4_define([lt_dict_add], +[m4_define([$1($2)], [$3])]) + + +# lt_dict_add_subkey(DICT, KEY, SUBKEY, VALUE) +# -------------------------------------------- +m4_define([lt_dict_add_subkey], +[m4_define([$1($2:$3)], [$4])]) + + +# lt_dict_fetch(DICT, KEY, [SUBKEY]) +# ---------------------------------- +m4_define([lt_dict_fetch], +[m4_ifval([$3], + m4_ifdef([$1($2:$3)], [m4_defn([$1($2:$3)])]), + m4_ifdef([$1($2)], [m4_defn([$1($2)])]))]) + + +# lt_if_dict_fetch(DICT, KEY, [SUBKEY], VALUE, IF-TRUE, [IF-FALSE]) +# ----------------------------------------------------------------- +m4_define([lt_if_dict_fetch], +[m4_if(lt_dict_fetch([$1], [$2], [$3]), [$4], + [$5], + [$6])]) + + +# lt_dict_filter(DICT, [SUBKEY], VALUE, [SEPARATOR], KEY, [...]) +# -------------------------------------------------------------- +m4_define([lt_dict_filter], +[m4_if([$5], [], [], + [lt_join(m4_quote(m4_default([$4], [[, ]])), + lt_unquote(m4_split(m4_normalize(m4_foreach(_Lt_key, lt_car([m4_shiftn(4, $@)]), + [lt_if_dict_fetch([$1], _Lt_key, [$2], [$3], [_Lt_key ])])))))])[]dnl +]) diff --git a/m4/ltversion.m4 b/m4/ltversion.m4 new file mode 100644 index 0000000..07a8602 --- /dev/null +++ b/m4/ltversion.m4 @@ -0,0 +1,23 @@ +# ltversion.m4 -- version numbers -*- Autoconf -*- +# +# Copyright (C) 2004 Free Software Foundation, Inc. +# Written by Scott James Remnant, 2004 +# +# This file is free software; the Free Software Foundation gives +# unlimited permission to copy and/or distribute it, with or without +# modifications, as long as this notice is preserved. + +# @configure_input@ + +# serial 3337 ltversion.m4 +# This file is part of GNU Libtool + +m4_define([LT_PACKAGE_VERSION], [2.4.2]) +m4_define([LT_PACKAGE_REVISION], [1.3337]) + +AC_DEFUN([LTVERSION_VERSION], +[macro_version='2.4.2' +macro_revision='1.3337' +_LT_DECL(, macro_version, 0, [Which release of libtool.m4 was used?]) +_LT_DECL(, macro_revision, 0) +]) diff --git a/m4/lt~obsolete.m4 b/m4/lt~obsolete.m4 new file mode 100644 index 0000000..c573da9 --- /dev/null +++ b/m4/lt~obsolete.m4 @@ -0,0 +1,98 @@ +# lt~obsolete.m4 -- aclocal satisfying obsolete definitions. -*-Autoconf-*- +# +# Copyright (C) 2004, 2005, 2007, 2009 Free Software Foundation, Inc. +# Written by Scott James Remnant, 2004. +# +# This file is free software; the Free Software Foundation gives +# unlimited permission to copy and/or distribute it, with or without +# modifications, as long as this notice is preserved. + +# serial 5 lt~obsolete.m4 + +# These exist entirely to fool aclocal when bootstrapping libtool. +# +# In the past libtool.m4 has provided macros via AC_DEFUN (or AU_DEFUN) +# which have later been changed to m4_define as they aren't part of the +# exported API, or moved to Autoconf or Automake where they belong. +# +# The trouble is, aclocal is a bit thick. It'll see the old AC_DEFUN +# in /usr/share/aclocal/libtool.m4 and remember it, then when it sees us +# using a macro with the same name in our local m4/libtool.m4 it'll +# pull the old libtool.m4 in (it doesn't see our shiny new m4_define +# and doesn't know about Autoconf macros at all.) +# +# So we provide this file, which has a silly filename so it's always +# included after everything else. This provides aclocal with the +# AC_DEFUNs it wants, but when m4 processes it, it doesn't do anything +# because those macros already exist, or will be overwritten later. +# We use AC_DEFUN over AU_DEFUN for compatibility with aclocal-1.6. +# +# Anytime we withdraw an AC_DEFUN or AU_DEFUN, remember to add it here. +# Yes, that means every name once taken will need to remain here until +# we give up compatibility with versions before 1.7, at which point +# we need to keep only those names which we still refer to. + +# This is to help aclocal find these macros, as it can't see m4_define. +AC_DEFUN([LTOBSOLETE_VERSION], [m4_if([1])]) + +m4_ifndef([AC_LIBTOOL_LINKER_OPTION], [AC_DEFUN([AC_LIBTOOL_LINKER_OPTION])]) +m4_ifndef([AC_PROG_EGREP], [AC_DEFUN([AC_PROG_EGREP])]) +m4_ifndef([_LT_AC_PROG_ECHO_BACKSLASH], [AC_DEFUN([_LT_AC_PROG_ECHO_BACKSLASH])]) +m4_ifndef([_LT_AC_SHELL_INIT], [AC_DEFUN([_LT_AC_SHELL_INIT])]) +m4_ifndef([_LT_AC_SYS_LIBPATH_AIX], [AC_DEFUN([_LT_AC_SYS_LIBPATH_AIX])]) +m4_ifndef([_LT_PROG_LTMAIN], [AC_DEFUN([_LT_PROG_LTMAIN])]) +m4_ifndef([_LT_AC_TAGVAR], [AC_DEFUN([_LT_AC_TAGVAR])]) +m4_ifndef([AC_LTDL_ENABLE_INSTALL], [AC_DEFUN([AC_LTDL_ENABLE_INSTALL])]) +m4_ifndef([AC_LTDL_PREOPEN], [AC_DEFUN([AC_LTDL_PREOPEN])]) +m4_ifndef([_LT_AC_SYS_COMPILER], [AC_DEFUN([_LT_AC_SYS_COMPILER])]) +m4_ifndef([_LT_AC_LOCK], [AC_DEFUN([_LT_AC_LOCK])]) +m4_ifndef([AC_LIBTOOL_SYS_OLD_ARCHIVE], [AC_DEFUN([AC_LIBTOOL_SYS_OLD_ARCHIVE])]) +m4_ifndef([_LT_AC_TRY_DLOPEN_SELF], [AC_DEFUN([_LT_AC_TRY_DLOPEN_SELF])]) +m4_ifndef([AC_LIBTOOL_PROG_CC_C_O], [AC_DEFUN([AC_LIBTOOL_PROG_CC_C_O])]) +m4_ifndef([AC_LIBTOOL_SYS_HARD_LINK_LOCKS], [AC_DEFUN([AC_LIBTOOL_SYS_HARD_LINK_LOCKS])]) +m4_ifndef([AC_LIBTOOL_OBJDIR], [AC_DEFUN([AC_LIBTOOL_OBJDIR])]) +m4_ifndef([AC_LTDL_OBJDIR], [AC_DEFUN([AC_LTDL_OBJDIR])]) +m4_ifndef([AC_LIBTOOL_PROG_LD_HARDCODE_LIBPATH], [AC_DEFUN([AC_LIBTOOL_PROG_LD_HARDCODE_LIBPATH])]) +m4_ifndef([AC_LIBTOOL_SYS_LIB_STRIP], [AC_DEFUN([AC_LIBTOOL_SYS_LIB_STRIP])]) +m4_ifndef([AC_PATH_MAGIC], [AC_DEFUN([AC_PATH_MAGIC])]) +m4_ifndef([AC_PROG_LD_GNU], [AC_DEFUN([AC_PROG_LD_GNU])]) +m4_ifndef([AC_PROG_LD_RELOAD_FLAG], [AC_DEFUN([AC_PROG_LD_RELOAD_FLAG])]) +m4_ifndef([AC_DEPLIBS_CHECK_METHOD], [AC_DEFUN([AC_DEPLIBS_CHECK_METHOD])]) +m4_ifndef([AC_LIBTOOL_PROG_COMPILER_NO_RTTI], [AC_DEFUN([AC_LIBTOOL_PROG_COMPILER_NO_RTTI])]) +m4_ifndef([AC_LIBTOOL_SYS_GLOBAL_SYMBOL_PIPE], [AC_DEFUN([AC_LIBTOOL_SYS_GLOBAL_SYMBOL_PIPE])]) +m4_ifndef([AC_LIBTOOL_PROG_COMPILER_PIC], [AC_DEFUN([AC_LIBTOOL_PROG_COMPILER_PIC])]) +m4_ifndef([AC_LIBTOOL_PROG_LD_SHLIBS], [AC_DEFUN([AC_LIBTOOL_PROG_LD_SHLIBS])]) +m4_ifndef([AC_LIBTOOL_POSTDEP_PREDEP], [AC_DEFUN([AC_LIBTOOL_POSTDEP_PREDEP])]) +m4_ifndef([LT_AC_PROG_EGREP], [AC_DEFUN([LT_AC_PROG_EGREP])]) +m4_ifndef([LT_AC_PROG_SED], [AC_DEFUN([LT_AC_PROG_SED])]) +m4_ifndef([_LT_CC_BASENAME], [AC_DEFUN([_LT_CC_BASENAME])]) +m4_ifndef([_LT_COMPILER_BOILERPLATE], [AC_DEFUN([_LT_COMPILER_BOILERPLATE])]) +m4_ifndef([_LT_LINKER_BOILERPLATE], [AC_DEFUN([_LT_LINKER_BOILERPLATE])]) +m4_ifndef([_AC_PROG_LIBTOOL], [AC_DEFUN([_AC_PROG_LIBTOOL])]) +m4_ifndef([AC_LIBTOOL_SETUP], [AC_DEFUN([AC_LIBTOOL_SETUP])]) +m4_ifndef([_LT_AC_CHECK_DLFCN], [AC_DEFUN([_LT_AC_CHECK_DLFCN])]) +m4_ifndef([AC_LIBTOOL_SYS_DYNAMIC_LINKER], [AC_DEFUN([AC_LIBTOOL_SYS_DYNAMIC_LINKER])]) +m4_ifndef([_LT_AC_TAGCONFIG], [AC_DEFUN([_LT_AC_TAGCONFIG])]) +m4_ifndef([AC_DISABLE_FAST_INSTALL], [AC_DEFUN([AC_DISABLE_FAST_INSTALL])]) +m4_ifndef([_LT_AC_LANG_CXX], [AC_DEFUN([_LT_AC_LANG_CXX])]) +m4_ifndef([_LT_AC_LANG_F77], [AC_DEFUN([_LT_AC_LANG_F77])]) +m4_ifndef([_LT_AC_LANG_GCJ], [AC_DEFUN([_LT_AC_LANG_GCJ])]) +m4_ifndef([AC_LIBTOOL_LANG_C_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_C_CONFIG])]) +m4_ifndef([_LT_AC_LANG_C_CONFIG], [AC_DEFUN([_LT_AC_LANG_C_CONFIG])]) +m4_ifndef([AC_LIBTOOL_LANG_CXX_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_CXX_CONFIG])]) +m4_ifndef([_LT_AC_LANG_CXX_CONFIG], [AC_DEFUN([_LT_AC_LANG_CXX_CONFIG])]) +m4_ifndef([AC_LIBTOOL_LANG_F77_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_F77_CONFIG])]) +m4_ifndef([_LT_AC_LANG_F77_CONFIG], [AC_DEFUN([_LT_AC_LANG_F77_CONFIG])]) +m4_ifndef([AC_LIBTOOL_LANG_GCJ_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_GCJ_CONFIG])]) +m4_ifndef([_LT_AC_LANG_GCJ_CONFIG], [AC_DEFUN([_LT_AC_LANG_GCJ_CONFIG])]) +m4_ifndef([AC_LIBTOOL_LANG_RC_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_RC_CONFIG])]) +m4_ifndef([_LT_AC_LANG_RC_CONFIG], [AC_DEFUN([_LT_AC_LANG_RC_CONFIG])]) +m4_ifndef([AC_LIBTOOL_CONFIG], [AC_DEFUN([AC_LIBTOOL_CONFIG])]) +m4_ifndef([_LT_AC_FILE_LTDLL_C], [AC_DEFUN([_LT_AC_FILE_LTDLL_C])]) +m4_ifndef([_LT_REQUIRED_DARWIN_CHECKS], [AC_DEFUN([_LT_REQUIRED_DARWIN_CHECKS])]) +m4_ifndef([_LT_AC_PROG_CXXCPP], [AC_DEFUN([_LT_AC_PROG_CXXCPP])]) +m4_ifndef([_LT_PREPARE_SED_QUOTE_VARS], [AC_DEFUN([_LT_PREPARE_SED_QUOTE_VARS])]) +m4_ifndef([_LT_PROG_ECHO_BACKSLASH], [AC_DEFUN([_LT_PROG_ECHO_BACKSLASH])]) +m4_ifndef([_LT_PROG_F77], [AC_DEFUN([_LT_PROG_F77])]) +m4_ifndef([_LT_PROG_FC], [AC_DEFUN([_LT_PROG_FC])]) +m4_ifndef([_LT_PROG_CXX], [AC_DEFUN([_LT_PROG_CXX])]) diff --git a/missing b/missing new file mode 100755 index 0000000..db98974 --- /dev/null +++ b/missing @@ -0,0 +1,215 @@ +#! /bin/sh +# Common wrapper for a few potentially missing GNU programs. + +scriptversion=2013-10-28.13; # UTC + +# Copyright (C) 1996-2013 Free Software Foundation, Inc. +# Originally written by Fran,cois Pinard , 1996. + +# 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 2, 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 . + +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +if test $# -eq 0; then + echo 1>&2 "Try '$0 --help' for more information" + exit 1 +fi + +case $1 in + + --is-lightweight) + # Used by our autoconf macros to check whether the available missing + # script is modern enough. + exit 0 + ;; + + --run) + # Back-compat with the calling convention used by older automake. + shift + ;; + + -h|--h|--he|--hel|--help) + echo "\ +$0 [OPTION]... PROGRAM [ARGUMENT]... + +Run 'PROGRAM [ARGUMENT]...', returning a proper advice when this fails due +to PROGRAM being missing or too old. + +Options: + -h, --help display this help and exit + -v, --version output version information and exit + +Supported PROGRAM values: + aclocal autoconf autoheader autom4te automake makeinfo + bison yacc flex lex help2man + +Version suffixes to PROGRAM as well as the prefixes 'gnu-', 'gnu', and +'g' are ignored when checking the name. + +Send bug reports to ." + exit $? + ;; + + -v|--v|--ve|--ver|--vers|--versi|--versio|--version) + echo "missing $scriptversion (GNU Automake)" + exit $? + ;; + + -*) + echo 1>&2 "$0: unknown '$1' option" + echo 1>&2 "Try '$0 --help' for more information" + exit 1 + ;; + +esac + +# Run the given program, remember its exit status. +"$@"; st=$? + +# If it succeeded, we are done. +test $st -eq 0 && exit 0 + +# Also exit now if we it failed (or wasn't found), and '--version' was +# passed; such an option is passed most likely to detect whether the +# program is present and works. +case $2 in --version|--help) exit $st;; esac + +# Exit code 63 means version mismatch. This often happens when the user +# tries to use an ancient version of a tool on a file that requires a +# minimum version. +if test $st -eq 63; then + msg="probably too old" +elif test $st -eq 127; then + # Program was missing. + msg="missing on your system" +else + # Program was found and executed, but failed. Give up. + exit $st +fi + +perl_URL=http://www.perl.org/ +flex_URL=http://flex.sourceforge.net/ +gnu_software_URL=http://www.gnu.org/software + +program_details () +{ + case $1 in + aclocal|automake) + echo "The '$1' program is part of the GNU Automake package:" + echo "<$gnu_software_URL/automake>" + echo "It also requires GNU Autoconf, GNU m4 and Perl in order to run:" + echo "<$gnu_software_URL/autoconf>" + echo "<$gnu_software_URL/m4/>" + echo "<$perl_URL>" + ;; + autoconf|autom4te|autoheader) + echo "The '$1' program is part of the GNU Autoconf package:" + echo "<$gnu_software_URL/autoconf/>" + echo "It also requires GNU m4 and Perl in order to run:" + echo "<$gnu_software_URL/m4/>" + echo "<$perl_URL>" + ;; + esac +} + +give_advice () +{ + # Normalize program name to check for. + normalized_program=`echo "$1" | sed ' + s/^gnu-//; t + s/^gnu//; t + s/^g//; t'` + + printf '%s\n' "'$1' is $msg." + + configure_deps="'configure.ac' or m4 files included by 'configure.ac'" + case $normalized_program in + autoconf*) + echo "You should only need it if you modified 'configure.ac'," + echo "or m4 files included by it." + program_details 'autoconf' + ;; + autoheader*) + echo "You should only need it if you modified 'acconfig.h' or" + echo "$configure_deps." + program_details 'autoheader' + ;; + automake*) + echo "You should only need it if you modified 'Makefile.am' or" + echo "$configure_deps." + program_details 'automake' + ;; + aclocal*) + echo "You should only need it if you modified 'acinclude.m4' or" + echo "$configure_deps." + program_details 'aclocal' + ;; + autom4te*) + echo "You might have modified some maintainer files that require" + echo "the 'autom4te' program to be rebuilt." + program_details 'autom4te' + ;; + bison*|yacc*) + echo "You should only need it if you modified a '.y' file." + echo "You may want to install the GNU Bison package:" + echo "<$gnu_software_URL/bison/>" + ;; + lex*|flex*) + echo "You should only need it if you modified a '.l' file." + echo "You may want to install the Fast Lexical Analyzer package:" + echo "<$flex_URL>" + ;; + help2man*) + echo "You should only need it if you modified a dependency" \ + "of a man page." + echo "You may want to install the GNU Help2man package:" + echo "<$gnu_software_URL/help2man/>" + ;; + makeinfo*) + echo "You should only need it if you modified a '.texi' file, or" + echo "any other file indirectly affecting the aspect of the manual." + echo "You might want to install the Texinfo package:" + echo "<$gnu_software_URL/texinfo/>" + echo "The spurious makeinfo call might also be the consequence of" + echo "using a buggy 'make' (AIX, DU, IRIX), in which case you might" + echo "want to install GNU make:" + echo "<$gnu_software_URL/make/>" + ;; + *) + echo "You might have modified some files without having the proper" + echo "tools for further handling them. Check the 'README' file, it" + echo "often tells you about the needed prerequisites for installing" + echo "this package. You may also peek at any GNU archive site, in" + echo "case some other package contains this missing '$1' program." + ;; + esac +} + +give_advice "$1" | sed -e '1s/^/WARNING: /' \ + -e '2,$s/^/ /' >&2 + +# Propagate the correct exit status (expected to be 127 for a program +# not found, 63 for a program that failed due to version mismatch). +exit $st + +# Local variables: +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "scriptversion=" +# time-stamp-format: "%:y-%02m-%02d.%02H" +# time-stamp-time-zone: "UTC" +# time-stamp-end: "; # UTC" +# End: diff --git a/nghttpx.conf.sample b/nghttpx.conf.sample new file mode 100644 index 0000000..97d7954 --- /dev/null +++ b/nghttpx.conf.sample @@ -0,0 +1,29 @@ +# +# Sample configuration file for nghttpx. +# +# * Line staring '#' is treated as comment. +# +# * The option name in the configuration file is the long command-line +# option name with leading '--' stripped (e.g., frontend). Put '=' +# between option name and value. Don't put extra leading or trailing +# spaces. +# +# * The options which do not take argument in the command-line *take* +# argument in the configuration file. Specify 'yes' as argument +# (e.g., http2-proxy=yes). If other string is given, it disables the +# option. +# +# * To specify private key and certificate file, use private-key-file +# and certificate-file. See the examples below. +# +# * conf option cannot be used in the configuration file. It will be +# ignored. +# +# Examples: +# +# frontend=0.0.0.0,3000 +# backend=127.0.0.1,80 +# private-key-file=/path/to/server.key +# certificate-file=/path/to/server.crt +# http2-proxy=no +# workers=1 diff --git a/proxy.pac.sample b/proxy.pac.sample new file mode 100644 index 0000000..9283920 --- /dev/null +++ b/proxy.pac.sample @@ -0,0 +1,6 @@ +function FindProxyForURL(url, host) { + // For SPDY proxy + return "HTTPS localhost:3000"; + // For conventional HTTP proxy + // return "PROXY localhost:3000"; +} diff --git a/python/Makefile.am b/python/Makefile.am new file mode 100644 index 0000000..911888f --- /dev/null +++ b/python/Makefile.am @@ -0,0 +1,48 @@ +# nghttp2 - HTTP/2 C Library + +# Copyright (c) 2013 Tatsuhiro Tsujikawa + +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: + +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. + +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +# This will avoid that setup.py gets deleted before it is executed in +# clean-local in parallel build. +.NOTPARALLEL: + +EXTRA_DIST = cnghttp2.pxd nghttp2.pyx + +if ENABLE_PYTHON_BINDINGS + +all-local: nghttp2.c + $(PYTHON) setup.py build + +install-exec-local: + $(PYTHON) setup.py install --prefix=$(DESTDIR)$(prefix) + +uninstall-local: + rm -rf $(DESTDIR)$(libdir)/python*/site-packages/*nghttp2* + +clean-local: + $(PYTHON) setup.py clean --all + -rm -f $(builddir)/nghttp2.c + +.pyx.c: + $(CYTHON) -o $@ $< + +endif # ENABLE_PYTHON_BINDINGS diff --git a/python/Makefile.in b/python/Makefile.in new file mode 100644 index 0000000..ea9c308 --- /dev/null +++ b/python/Makefile.in @@ -0,0 +1,541 @@ +# Makefile.in generated by automake 1.14.1 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2013 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +# nghttp2 - HTTP/2 C Library + +# Copyright (c) 2013 Tatsuhiro Tsujikawa + +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: + +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. + +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +VPATH = @srcdir@ +am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)' +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +target_triplet = @target@ +subdir = python +DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \ + $(srcdir)/setup.py.in +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/ax_boost_asio.m4 \ + $(top_srcdir)/m4/ax_boost_base.m4 \ + $(top_srcdir)/m4/ax_boost_system.m4 \ + $(top_srcdir)/m4/ax_boost_thread.m4 \ + $(top_srcdir)/m4/ax_check_compile_flag.m4 \ + $(top_srcdir)/m4/ax_cxx_compile_stdcxx_11.m4 \ + $(top_srcdir)/m4/ax_have_epoll.m4 \ + $(top_srcdir)/m4/ax_python_devel.m4 \ + $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/libxml2.m4 \ + $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ + $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = setup.py +CONFIG_CLEAN_VPATH_FILES = +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +SOURCES = +DIST_SOURCES = +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +APPLDFLAGS = @APPLDFLAGS@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +BOOST_ASIO_LIB = @BOOST_ASIO_LIB@ +BOOST_CPPFLAGS = @BOOST_CPPFLAGS@ +BOOST_LDFLAGS = @BOOST_LDFLAGS@ +BOOST_SYSTEM_LIB = @BOOST_SYSTEM_LIB@ +BOOST_THREAD_LIB = @BOOST_THREAD_LIB@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CUNIT_CFLAGS = @CUNIT_CFLAGS@ +CUNIT_LIBS = @CUNIT_LIBS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +CYTHON = @CYTHON@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DLLTOOL = @DLLTOOL@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +GREP = @GREP@ +HAVE_CXX11 = @HAVE_CXX11@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +JANSSON_CFLAGS = @JANSSON_CFLAGS@ +JANSSON_LIBS = @JANSSON_LIBS@ +JEMALLOC_LIBS = @JEMALLOC_LIBS@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBEVENT_OPENSSL_CFLAGS = @LIBEVENT_OPENSSL_CFLAGS@ +LIBEVENT_OPENSSL_LIBS = @LIBEVENT_OPENSSL_LIBS@ +LIBEV_CFLAGS = @LIBEV_CFLAGS@ +LIBEV_LIBS = @LIBEV_LIBS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBSPDYLAY_CFLAGS = @LIBSPDYLAY_CFLAGS@ +LIBSPDYLAY_LIBS = @LIBSPDYLAY_LIBS@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +LT_AGE = @LT_AGE@ +LT_CURRENT = @LT_CURRENT@ +LT_REVISION = @LT_REVISION@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MKDIR_P = @MKDIR_P@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OPENSSL_CFLAGS = @OPENSSL_CFLAGS@ +OPENSSL_LIBS = @OPENSSL_LIBS@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PACKAGE_VERSION_NUM = @PACKAGE_VERSION_NUM@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PKG_CONFIG = @PKG_CONFIG@ +PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ +PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ +PYTHON = @PYTHON@ +PYTHON_CPPFLAGS = @PYTHON_CPPFLAGS@ +PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@ +PYTHON_EXTRA_LDFLAGS = @PYTHON_EXTRA_LDFLAGS@ +PYTHON_EXTRA_LIBS = @PYTHON_EXTRA_LIBS@ +PYTHON_LDFLAGS = @PYTHON_LDFLAGS@ +PYTHON_PLATFORM = @PYTHON_PLATFORM@ +PYTHON_PREFIX = @PYTHON_PREFIX@ +PYTHON_SITE_PKG = @PYTHON_SITE_PKG@ +PYTHON_VERSION = @PYTHON_VERSION@ +RANLIB = @RANLIB@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +TESTLDADD = @TESTLDADD@ +VERSION = @VERSION@ +WARNCFLAGS = @WARNCFLAGS@ +XML2_CONFIG = @XML2_CONFIG@ +XML_CPPFLAGS = @XML_CPPFLAGS@ +XML_LIBS = @XML_LIBS@ +ZLIB_CFLAGS = @ZLIB_CFLAGS@ +ZLIB_LIBS = @ZLIB_LIBS@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +pkgpyexecdir = @pkgpyexecdir@ +pkgpythondir = @pkgpythondir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +pyexecdir = @pyexecdir@ +pythondir = @pythondir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target = @target@ +target_alias = @target_alias@ +target_cpu = @target_cpu@ +target_os = @target_os@ +target_vendor = @target_vendor@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +EXTRA_DIST = cnghttp2.pxd nghttp2.pyx +all: all-am + +.SUFFIXES: +.SUFFIXES: .c .pyx +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu python/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu python/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): +setup.py: $(top_builddir)/config.status $(srcdir)/setup.py.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs +tags TAGS: + +ctags CTAGS: + +cscope cscopelist: + + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +@ENABLE_PYTHON_BINDINGS_FALSE@all-local: +all-am: Makefile all-local +installdirs: +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +@ENABLE_PYTHON_BINDINGS_FALSE@clean-local: +@ENABLE_PYTHON_BINDINGS_FALSE@install-exec-local: +@ENABLE_PYTHON_BINDINGS_FALSE@uninstall-local: +clean: clean-am + +clean-am: clean-generic clean-libtool clean-local mostlyclean-am + +distclean: distclean-am + -rm -f Makefile +distclean-am: clean-am distclean-generic + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: install-exec-local + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-generic mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-local + +.MAKE: install-am install-strip + +.PHONY: all all-am all-local check check-am clean clean-generic \ + clean-libtool clean-local cscopelist-am ctags-am distclean \ + distclean-generic distclean-libtool distdir dvi dvi-am html \ + html-am info info-am install install-am install-data \ + install-data-am install-dvi install-dvi-am install-exec \ + install-exec-am install-exec-local install-html \ + install-html-am install-info install-info-am install-man \ + install-pdf install-pdf-am install-ps install-ps-am \ + install-strip installcheck installcheck-am installdirs \ + maintainer-clean maintainer-clean-generic mostlyclean \ + mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ + tags-am uninstall uninstall-am uninstall-local + + +# This will avoid that setup.py gets deleted before it is executed in +# clean-local in parallel build. +.NOTPARALLEL: + +@ENABLE_PYTHON_BINDINGS_TRUE@all-local: nghttp2.c +@ENABLE_PYTHON_BINDINGS_TRUE@ $(PYTHON) setup.py build + +@ENABLE_PYTHON_BINDINGS_TRUE@install-exec-local: +@ENABLE_PYTHON_BINDINGS_TRUE@ $(PYTHON) setup.py install --prefix=$(DESTDIR)$(prefix) + +@ENABLE_PYTHON_BINDINGS_TRUE@uninstall-local: +@ENABLE_PYTHON_BINDINGS_TRUE@ rm -rf $(DESTDIR)$(libdir)/python*/site-packages/*nghttp2* + +@ENABLE_PYTHON_BINDINGS_TRUE@clean-local: +@ENABLE_PYTHON_BINDINGS_TRUE@ $(PYTHON) setup.py clean --all +@ENABLE_PYTHON_BINDINGS_TRUE@ -rm -f $(builddir)/nghttp2.c + +@ENABLE_PYTHON_BINDINGS_TRUE@.pyx.c: +@ENABLE_PYTHON_BINDINGS_TRUE@ $(CYTHON) -o $@ $< + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/python/cnghttp2.pxd b/python/cnghttp2.pxd new file mode 100644 index 0000000..2718304 --- /dev/null +++ b/python/cnghttp2.pxd @@ -0,0 +1,345 @@ +# nghttp2 - HTTP/2 C Library + +# Copyright (c) 2013 Tatsuhiro Tsujikawa + +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: + +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. + +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +from libc.stdint cimport uint8_t, uint16_t, uint32_t, int32_t + +cdef extern from 'nghttp2/nghttp2.h': + + const char NGHTTP2_PROTO_VERSION_ID[] + const char NGHTTP2_CLIENT_CONNECTION_PREFACE[] + const size_t NGHTTP2_INITIAL_WINDOW_SIZE + const size_t NGHTTP2_DEFAULT_HEADER_TABLE_SIZE + + ctypedef struct nghttp2_session: + pass + + ctypedef enum nghttp2_error: + NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE + NGHTTP2_ERR_DEFERRED + + ctypedef enum nghttp2_flag: + NGHTTP2_FLAG_NONE + NGHTTP2_FLAG_END_STREAM + NGHTTP2_FLAG_ACK + + ctypedef enum nghttp2_error_code: + NGHTTP2_NO_ERROR + NGHTTP2_PROTOCOL_ERROR + NGHTTP2_INTERNAL_ERROR + NGHTTP2_SETTINGS_TIMEOUT + + ctypedef enum nghttp2_frame_type: + NGHTTP2_DATA + NGHTTP2_HEADERS + NGHTTP2_RST_STREAM + NGHTTP2_SETTINGS + NGHTTP2_PUSH_PROMISE + NGHTTP2_GOAWAY + + ctypedef enum nghttp2_nv_flag: + NGHTTP2_NV_FLAG_NONE + NGHTTP2_NV_FLAG_NO_INDEX + + ctypedef struct nghttp2_nv: + uint8_t *name + uint8_t *value + uint16_t namelen + uint16_t valuelen + uint8_t flags + + ctypedef enum nghttp2_settings_id: + SETTINGS_HEADER_TABLE_SIZE + NGHTTP2_SETTINGS_HEADER_TABLE_SIZE + NGHTTP2_SETTINGS_ENABLE_PUSH + NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS + NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE + + ctypedef struct nghttp2_settings_entry: + int32_t settings_id + uint32_t value + + ctypedef struct nghttp2_frame_hd: + size_t length + int32_t stream_id + uint8_t type + uint8_t flags + + ctypedef struct nghttp2_data: + nghttp2_frame_hd hd + size_t padlen + + ctypedef enum nghttp2_headers_category: + NGHTTP2_HCAT_REQUEST + NGHTTP2_HCAT_RESPONSE + NGHTTP2_HCAT_PUSH_RESPONSE + NGHTTP2_HCAT_HEADERS + + ctypedef struct nghttp2_headers: + nghttp2_frame_hd hd + size_t padlen + nghttp2_nv *nva + size_t nvlen + nghttp2_headers_category cat + int32_t pri + + ctypedef struct nghttp2_rst_stream: + nghttp2_frame_hd hd + uint32_t error_code + + + ctypedef struct nghttp2_push_promise: + nghttp2_frame_hd hd + nghttp2_nv *nva + size_t nvlen + int32_t promised_stream_id + + ctypedef struct nghttp2_goaway: + nghttp2_frame_hd hd + int32_t last_stream_id + uint32_t error_code + uint8_t *opaque_data + size_t opaque_data_len + + ctypedef union nghttp2_frame: + nghttp2_frame_hd hd + nghttp2_data data + nghttp2_headers headers + nghttp2_rst_stream rst_stream + nghttp2_push_promise push_promise + nghttp2_goaway goaway + + ctypedef ssize_t (*nghttp2_send_callback)\ + (nghttp2_session *session, const uint8_t *data, size_t length, + int flags, void *user_data) + + ctypedef int (*nghttp2_on_frame_recv_callback)\ + (nghttp2_session *session, const nghttp2_frame *frame, void *user_data) + + ctypedef int (*nghttp2_on_data_chunk_recv_callback)\ + (nghttp2_session *session, uint8_t flags, int32_t stream_id, + const uint8_t *data, size_t length, void *user_data) + + ctypedef int (*nghttp2_before_frame_send_callback)\ + (nghttp2_session *session, const nghttp2_frame *frame, void *user_data) + + ctypedef int (*nghttp2_on_stream_close_callback)\ + (nghttp2_session *session, int32_t stream_id, + uint32_t error_code, void *user_data) + + ctypedef int (*nghttp2_on_begin_headers_callback)\ + (nghttp2_session *session, const nghttp2_frame *frame, void *user_data) + + ctypedef int (*nghttp2_on_header_callback)\ + (nghttp2_session *session, + const nghttp2_frame *frame, + const uint8_t *name, size_t namelen, + const uint8_t *value, size_t valuelen, + uint8_t flags, + void *user_data) + + ctypedef int (*nghttp2_on_frame_send_callback)\ + (nghttp2_session *session, const nghttp2_frame *frame, void *user_data) + + ctypedef int (*nghttp2_on_frame_not_send_callback)\ + (nghttp2_session *session, const nghttp2_frame *frame, + int lib_error_code, void *user_data) + + ctypedef struct nghttp2_session_callbacks: + pass + + int nghttp2_session_callbacks_new( + nghttp2_session_callbacks **callbacks_ptr) + + void nghttp2_session_callbacks_del(nghttp2_session_callbacks *callbacks) + + void nghttp2_session_callbacks_set_send_callback( + nghttp2_session_callbacks *cbs, nghttp2_send_callback send_callback) + + void nghttp2_session_callbacks_set_on_frame_recv_callback( + nghttp2_session_callbacks *cbs, + nghttp2_on_frame_recv_callback on_frame_recv_callback) + + void nghttp2_session_callbacks_set_on_data_chunk_recv_callback( + nghttp2_session_callbacks *cbs, + nghttp2_on_data_chunk_recv_callback on_data_chunk_recv_callback) + + void nghttp2_session_callbacks_set_before_frame_send_callback( + nghttp2_session_callbacks *cbs, + nghttp2_before_frame_send_callback before_frame_send_callback) + + void nghttp2_session_callbacks_set_on_frame_send_callback( + nghttp2_session_callbacks *cbs, + nghttp2_on_frame_send_callback on_frame_send_callback) + + void nghttp2_session_callbacks_set_on_frame_not_send_callback( + nghttp2_session_callbacks *cbs, + nghttp2_on_frame_not_send_callback on_frame_not_send_callback) + + void nghttp2_session_callbacks_set_on_stream_close_callback( + nghttp2_session_callbacks *cbs, + nghttp2_on_stream_close_callback on_stream_close_callback) + + void nghttp2_session_callbacks_set_on_begin_headers_callback( + nghttp2_session_callbacks *cbs, + nghttp2_on_begin_headers_callback on_begin_headers_callback) + + void nghttp2_session_callbacks_set_on_header_callback( + nghttp2_session_callbacks *cbs, + nghttp2_on_header_callback on_header_callback) + + int nghttp2_session_client_new(nghttp2_session **session_ptr, + const nghttp2_session_callbacks *callbacks, + void *user_data) + + int nghttp2_session_server_new(nghttp2_session **session_ptr, + const nghttp2_session_callbacks *callbacks, + void *user_data) + + void nghttp2_session_del(nghttp2_session *session) + + + ssize_t nghttp2_session_mem_recv(nghttp2_session *session, + const uint8_t *data, size_t datalen) + + ssize_t nghttp2_session_mem_send(nghttp2_session *session, + const uint8_t **data_ptr) + + int nghttp2_session_send(nghttp2_session *session) + + int nghttp2_session_want_read(nghttp2_session *session) + + int nghttp2_session_want_write(nghttp2_session *session) + + ctypedef union nghttp2_data_source: + int fd + void *ptr + + ctypedef enum nghttp2_data_flag: + NGHTTP2_DATA_FLAG_NONE + NGHTTP2_DATA_FLAG_EOF + + ctypedef ssize_t (*nghttp2_data_source_read_callback)\ + (nghttp2_session *session, int32_t stream_id, + uint8_t *buf, size_t length, uint32_t *data_flags, + nghttp2_data_source *source, void *user_data) + + ctypedef struct nghttp2_data_provider: + nghttp2_data_source source + nghttp2_data_source_read_callback read_callback + + ctypedef struct nghttp2_priority_spec: + int32_t stream_id + int32_t weight + uint8_t exclusive + + int nghttp2_submit_request(nghttp2_session *session, const nghttp2_priority_spec *pri_spec, + const nghttp2_nv *nva, size_t nvlen, + const nghttp2_data_provider *data_prd, + void *stream_user_data) + + int nghttp2_submit_response(nghttp2_session *session, + int32_t stream_id, + const nghttp2_nv *nva, size_t nvlen, + const nghttp2_data_provider *data_prd) + + int nghttp2_submit_push_promise(nghttp2_session *session, uint8_t flags, + int32_t stream_id, + const nghttp2_nv *nva, size_t nvlen, + void *stream_user_data) + + int nghttp2_submit_settings(nghttp2_session *session, uint8_t flags, + const nghttp2_settings_entry *iv, size_t niv) + + int nghttp2_submit_rst_stream(nghttp2_session *session, uint8_t flags, + int32_t stream_id, + uint32_t error_code) + + void* nghttp2_session_get_stream_user_data(nghttp2_session *session, + uint32_t stream_id) + + int nghttp2_session_set_stream_user_data(nghttp2_session *session, + uint32_t stream_id, + void *stream_user_data) + + int nghttp2_session_terminate_session(nghttp2_session *session, + uint32_t error_code) + + int nghttp2_session_resume_data(nghttp2_session *session, + int32_t stream_id) + + const char* nghttp2_strerror(int lib_error_code) + + int nghttp2_hd_deflate_new(nghttp2_hd_deflater **deflater_ptr, + size_t deflate_hd_table_bufsize_max) + + void nghttp2_hd_deflate_del(nghttp2_hd_deflater *deflater) + + int nghttp2_hd_deflate_change_table_size(nghttp2_hd_deflater *deflater, + size_t hd_table_bufsize_max) + + ssize_t nghttp2_hd_deflate_hd(nghttp2_hd_deflater *deflater, + uint8_t *buf, size_t buflen, + const nghttp2_nv *nva, size_t nvlen) + + size_t nghttp2_hd_deflate_bound(nghttp2_hd_deflater *deflater, + const nghttp2_nv *nva, size_t nvlen) + + int nghttp2_hd_inflate_new(nghttp2_hd_inflater **inflater_ptr) + + void nghttp2_hd_inflate_del(nghttp2_hd_inflater *inflater) + + int nghttp2_hd_inflate_change_table_size(nghttp2_hd_inflater *inflater, + size_t hd_table_bufsize_max) + + ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_inflater *inflater, + nghttp2_nv *nv_out, int *inflate_flags, + uint8_t *input, size_t inlen, int in_final) + + int nghttp2_hd_inflate_end_headers(nghttp2_hd_inflater *inflater) + +cdef extern from 'nghttp2_hd.h': + + # This is macro + int NGHTTP2_HD_ENTRY_OVERHEAD + + ctypedef enum nghttp2_hd_inflate_flag: + NGHTTP2_HD_INFLATE_EMIT + NGHTTP2_HD_INFLATE_FINAL + + ctypedef struct nghttp2_hd_entry: + nghttp2_nv nv + uint8_t flags + + ctypedef struct nghttp2_hd_ringbuf: + size_t len + + ctypedef struct nghttp2_hd_context: + nghttp2_hd_ringbuf hd_table + + ctypedef struct nghttp2_hd_deflater: + nghttp2_hd_context ctx + + ctypedef struct nghttp2_hd_inflater: + nghttp2_hd_context ctx + + nghttp2_hd_entry* nghttp2_hd_table_get(nghttp2_hd_context *context, + size_t index) diff --git a/python/nghttp2.pyx b/python/nghttp2.pyx new file mode 100644 index 0000000..c6b9b66 --- /dev/null +++ b/python/nghttp2.pyx @@ -0,0 +1,1591 @@ +# nghttp2 - HTTP/2 C Library + +# Copyright (c) 2013 Tatsuhiro Tsujikawa + +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: + +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. + +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +cimport cnghttp2 + +from libc.stdlib cimport malloc, free +from libc.string cimport memcpy, memset +from libc.stdint cimport uint8_t, uint16_t, uint32_t, int32_t +import logging + + +DEFAULT_HEADER_TABLE_SIZE = cnghttp2.NGHTTP2_DEFAULT_HEADER_TABLE_SIZE +DEFLATE_MAX_HEADER_TABLE_SIZE = 4096 + +HD_ENTRY_OVERHEAD = cnghttp2.NGHTTP2_HD_ENTRY_OVERHEAD + +class HDTableEntry: + + def __init__(self, name, namelen, value, valuelen): + self.name = name + self.namelen = namelen + self.value = value + self.valuelen = valuelen + + def space(self): + return self.namelen + self.valuelen + HD_ENTRY_OVERHEAD + +cdef _get_hd_table(cnghttp2.nghttp2_hd_context *ctx): + cdef int length = ctx.hd_table.len + cdef cnghttp2.nghttp2_hd_entry *entry + res = [] + for i in range(length): + entry = cnghttp2.nghttp2_hd_table_get(ctx, i) + k = _get_pybytes(entry.nv.name, entry.nv.namelen) + v = _get_pybytes(entry.nv.value, entry.nv.valuelen) + res.append(HDTableEntry(k, entry.nv.namelen, + v, entry.nv.valuelen)) + return res + +cdef _get_pybytes(uint8_t *b, uint16_t blen): + return b[:blen] + +cdef class HDDeflater: + '''Performs header compression. The constructor takes + |hd_table_bufsize_max| parameter, which limits the usage of header + table in the given amount of bytes. This is necessary because the + header compressor and decompressor share the same amount of + header table and the decompressor decides that number. The + compressor may not want to use all header table size because of + limited memory availability. In that case, the + |hd_table_bufsize_max| can be used to cap the upper limit of table + size whatever the header table size is chosen by the decompressor. + The default value of |hd_table_bufsize_max| is 4096 bytes. + + The following example shows how to compress request header sets: + + import binascii, nghttp2 + + deflater = nghttp2.HDDeflater() + res = deflater.deflate([(b'foo', b'bar'), + (b'baz', b'buz')]) + print(binascii.b2a_hex(res)) + + ''' + + cdef cnghttp2.nghttp2_hd_deflater *_deflater + + def __cinit__(self, hd_table_bufsize_max = DEFLATE_MAX_HEADER_TABLE_SIZE): + rv = cnghttp2.nghttp2_hd_deflate_new(&self._deflater, + hd_table_bufsize_max) + if rv != 0: + raise Exception(_strerror(rv)) + + def __dealloc__(self): + cnghttp2.nghttp2_hd_deflate_del(self._deflater) + + def deflate(self, headers): + '''Compresses the |headers|. The |headers| must be sequence of tuple + of name/value pair, which are sequence of bytes (not unicode + string). + + This function returns the encoded header block in byte string. + An exception will be raised on error. + + ''' + cdef cnghttp2.nghttp2_nv *nva = \ + malloc(sizeof(cnghttp2.nghttp2_nv)*\ + len(headers)) + cdef cnghttp2.nghttp2_nv *nvap = nva + + for k, v in headers: + nvap[0].name = k + nvap[0].namelen = len(k) + nvap[0].value = v + nvap[0].valuelen = len(v) + nvap[0].flags = cnghttp2.NGHTTP2_NV_FLAG_NONE + nvap += 1 + + cdef size_t outcap = 0 + cdef ssize_t rv + cdef uint8_t *out + cdef size_t outlen + + outlen = cnghttp2.nghttp2_hd_deflate_bound(self._deflater, + nva, len(headers)) + + out = malloc(outlen) + + rv = cnghttp2.nghttp2_hd_deflate_hd(self._deflater, out, outlen, + nva, len(headers)) + free(nva) + + if rv < 0: + free(out) + + raise Exception(_strerror(rv)) + + cdef bytes res + + try: + res = out[:rv] + finally: + free(out) + + return res + + def change_table_size(self, hd_table_bufsize_max): + '''Changes header table size to |hd_table_bufsize_max| byte. + + An exception will be raised on error. + + ''' + cdef int rv + rv = cnghttp2.nghttp2_hd_deflate_change_table_size(self._deflater, + hd_table_bufsize_max) + if rv != 0: + raise Exception(_strerror(rv)) + + def get_hd_table(self): + '''Returns copy of current dynamic header table.''' + return _get_hd_table(&self._deflater.ctx) + +cdef class HDInflater: + '''Performs header decompression. + + The following example shows how to compress request header sets: + + data = b'0082c5ad82bd0f000362617a0362757a' + inflater = nghttp2.HDInflater() + hdrs = inflater.inflate(data) + print(hdrs) + + ''' + + cdef cnghttp2.nghttp2_hd_inflater *_inflater + + def __cinit__(self): + rv = cnghttp2.nghttp2_hd_inflate_new(&self._inflater) + if rv != 0: + raise Exception(_strerror(rv)) + + def __dealloc__(self): + cnghttp2.nghttp2_hd_inflate_del(self._inflater) + + def inflate(self, data): + '''Decompresses the compressed header block |data|. The |data| must be + byte string (not unicode string). + + ''' + cdef cnghttp2.nghttp2_nv nv + cdef int inflate_flags + cdef ssize_t rv + cdef uint8_t *buf = data + cdef size_t buflen = len(data) + res = [] + while True: + inflate_flags = 0 + rv = cnghttp2.nghttp2_hd_inflate_hd(self._inflater, &nv, + &inflate_flags, + buf, buflen, 1) + if rv < 0: + raise Exception(_strerror(rv)) + buf += rv + buflen -= rv + if inflate_flags & cnghttp2.NGHTTP2_HD_INFLATE_EMIT: + # may throw + res.append((nv.name[:nv.namelen], nv.value[:nv.valuelen])) + if inflate_flags & cnghttp2.NGHTTP2_HD_INFLATE_FINAL: + break + + cnghttp2.nghttp2_hd_inflate_end_headers(self._inflater) + return res + + def change_table_size(self, hd_table_bufsize_max): + '''Changes header table size to |hd_table_bufsize_max| byte. + + An exception will be raised on error. + + ''' + cdef int rv + rv = cnghttp2.nghttp2_hd_inflate_change_table_size(self._inflater, + hd_table_bufsize_max) + if rv != 0: + raise Exception(_strerror(rv)) + + def get_hd_table(self): + '''Returns copy of current dynamic header table.''' + return _get_hd_table(&self._inflater.ctx) + +cdef _strerror(int liberror_code): + return cnghttp2.nghttp2_strerror(liberror_code).decode('utf-8') + +def print_hd_table(hdtable): + '''Convenient function to print |hdtable| to the standard output. This + function does not work if header name/value cannot be decoded using + UTF-8 encoding. + + s=N means the entry occupies N bytes in header table. + + ''' + idx = 0 + for entry in hdtable: + idx += 1 + print('[{}] (s={}) {}: {}'\ + .format(idx, entry.space(), + entry.name.decode('utf-8'), + entry.value.decode('utf-8'))) + +try: + import socket + import io + import asyncio + import traceback + import sys + import email.utils + import datetime + import time + from urllib.parse import urlparse +except ImportError: + asyncio = None + +# body generator flags +DATA_OK = 0 +DATA_EOF = 1 +DATA_DEFERRED = 2 + +class _ByteIOWrapper: + + def __init__(self, b): + self.b = b + + def generate(self, n): + data = self.b.read1(n) + if not data: + return None, DATA_EOF + return data, DATA_OK + +def wrap_body(body): + if body is None: + return body + elif isinstance(body, str): + return _ByteIOWrapper(io.BytesIO(body.encode('utf-8'))).generate + elif isinstance(body, bytes): + return _ByteIOWrapper(io.BytesIO(body)).generate + elif isinstance(body, io.IOBase): + return _ByteIOWrapper(body).generate + else: + # assume that callable in the form f(n) returning tuple byte + # string and flag. + return body + +cdef _get_stream_user_data(cnghttp2.nghttp2_session *session, + int32_t stream_id): + cdef void *stream_user_data + + stream_user_data = cnghttp2.nghttp2_session_get_stream_user_data\ + (session, stream_id) + if stream_user_data == NULL: + return None + + return stream_user_data + +cdef size_t _make_nva(cnghttp2.nghttp2_nv **nva_ptr, headers): + cdef cnghttp2.nghttp2_nv *nva + cdef size_t nvlen + + nvlen = len(headers) + nva = malloc(sizeof(cnghttp2.nghttp2_nv) * nvlen) + for i, (k, v) in enumerate(headers): + nva[i].name = k + nva[i].namelen = len(k) + nva[i].value = v + nva[i].valuelen = len(v) + nva[i].flags = cnghttp2.NGHTTP2_NV_FLAG_NONE + + nva_ptr[0] = nva + + return nvlen + +cdef int server_on_header(cnghttp2.nghttp2_session *session, + const cnghttp2.nghttp2_frame *frame, + const uint8_t *name, size_t namelen, + const uint8_t *value, size_t valuelen, + uint8_t flags, + void *user_data): + cdef http2 = <_HTTP2SessionCoreBase>user_data + logging.debug('server_on_header, type:%s, stream_id:%s', frame.hd.type, frame.hd.stream_id) + + handler = _get_stream_user_data(session, frame.hd.stream_id) + return on_header(name, namelen, value, valuelen, flags, handler) + +cdef int client_on_header(cnghttp2.nghttp2_session *session, + const cnghttp2.nghttp2_frame *frame, + const uint8_t *name, size_t namelen, + const uint8_t *value, size_t valuelen, + uint8_t flags, + void *user_data): + cdef http2 = <_HTTP2SessionCoreBase>user_data + logging.debug('client_on_header, type:%s, stream_id:%s', frame.hd.type, frame.hd.stream_id) + + if frame.hd.type == cnghttp2.NGHTTP2_HEADERS: + handler = _get_stream_user_data(session, frame.hd.stream_id) + elif frame.hd.type == cnghttp2.NGHTTP2_PUSH_PROMISE: + handler = _get_stream_user_data(session, frame.push_promise.promised_stream_id) + + return on_header(name, namelen, value, valuelen, flags, handler) + + +cdef int on_header(const uint8_t *name, size_t namelen, + const uint8_t *value, size_t valuelen, + uint8_t flags, + object handler): + if not handler: + return 0 + + key = name[:namelen] + values = value[:valuelen].split(b'\x00') + if key == b':scheme': + handler.scheme = values[0] + elif key == b':method': + handler.method = values[0] + elif key == b':authority' or key == b'host': + handler.host = values[0] + elif key == b':path': + handler.path = values[0] + elif key == b':status': + handler.status = values[0] + + if key == b'cookie': + handler.cookies.extend(values) + else: + for v in values: + handler.headers.append((key, v)) + + return 0 + +cdef int server_on_begin_request_headers(cnghttp2.nghttp2_session *session, + const cnghttp2.nghttp2_frame *frame, + void *user_data): + cdef http2 = <_HTTP2SessionCore>user_data + + handler = http2._make_handler(frame.hd.stream_id) + cnghttp2.nghttp2_session_set_stream_user_data(session, frame.hd.stream_id, + handler) + + return 0 + +cdef int server_on_begin_headers(cnghttp2.nghttp2_session *session, + const cnghttp2.nghttp2_frame *frame, + void *user_data): + if frame.hd.type == cnghttp2.NGHTTP2_HEADERS: + if frame.headers.cat == cnghttp2.NGHTTP2_HCAT_REQUEST: + return server_on_begin_request_headers(session, frame, user_data) + + return 0 + +cdef int server_on_frame_recv(cnghttp2.nghttp2_session *session, + const cnghttp2.nghttp2_frame *frame, + void *user_data): + cdef http2 = <_HTTP2SessionCore>user_data + logging.debug('server_on_frame_recv, type:%s, stream_id:%s', frame.hd.type, frame.hd.stream_id) + + if frame.hd.type == cnghttp2.NGHTTP2_DATA: + if frame.hd.flags & cnghttp2.NGHTTP2_FLAG_END_STREAM: + handler = _get_stream_user_data(session, frame.hd.stream_id) + if not handler: + return 0 + try: + handler.on_request_done() + except: + sys.stderr.write(traceback.format_exc()) + return http2._rst_stream(frame.hd.stream_id) + elif frame.hd.type == cnghttp2.NGHTTP2_HEADERS: + if frame.headers.cat == cnghttp2.NGHTTP2_HCAT_REQUEST: + handler = _get_stream_user_data(session, frame.hd.stream_id) + if not handler: + return 0 + if handler.cookies: + handler.headers.append((b'cookie', + b'; '.join(handler.cookies))) + handler.cookies = None + try: + handler.on_headers() + if frame.hd.flags & cnghttp2.NGHTTP2_FLAG_END_STREAM: + handler.on_request_done() + except: + sys.stderr.write(traceback.format_exc()) + return http2._rst_stream(frame.hd.stream_id) + elif frame.hd.type == cnghttp2.NGHTTP2_SETTINGS: + if (frame.hd.flags & cnghttp2.NGHTTP2_FLAG_ACK): + http2._stop_settings_timer() + + return 0 + +cdef int on_data_chunk_recv(cnghttp2.nghttp2_session *session, + uint8_t flags, + int32_t stream_id, const uint8_t *data, + size_t length, void *user_data): + cdef http2 = <_HTTP2SessionCoreBase>user_data + + handler = _get_stream_user_data(session, stream_id) + if not handler: + return 0 + + try: + handler.on_data(data[:length]) + except: + sys.stderr.write(traceback.format_exc()) + return http2._rst_stream(stream_id) + + return 0 + +cdef int server_on_frame_send(cnghttp2.nghttp2_session *session, + const cnghttp2.nghttp2_frame *frame, + void *user_data): + cdef http2 = <_HTTP2SessionCore>user_data + logging.debug('server_on_frame_send, type:%s, stream_id:%s', frame.hd.type, frame.hd.stream_id) + + if frame.hd.type == cnghttp2.NGHTTP2_PUSH_PROMISE: + # For PUSH_PROMISE, send push response immediately + handler = _get_stream_user_data\ + (session, frame.push_promise.promised_stream_id) + if not handler: + return 0 + + http2.send_response(handler) + elif frame.hd.type == cnghttp2.NGHTTP2_SETTINGS: + if (frame.hd.flags & cnghttp2.NGHTTP2_FLAG_ACK) != 0: + return 0 + http2._start_settings_timer() + +cdef int server_on_frame_not_send(cnghttp2.nghttp2_session *session, + const cnghttp2.nghttp2_frame *frame, + int lib_error_code, + void *user_data): + cdef http2 = <_HTTP2SessionCore>user_data + logging.debug('server_on_frame_not_send, type:%s, stream_id:%s', frame.hd.type, frame.hd.stream_id) + + if frame.hd.type == cnghttp2.NGHTTP2_PUSH_PROMISE: + # We have to remove handler here. Without this, it is not + # removed until session is terminated. + handler = _get_stream_user_data\ + (session, frame.push_promise.promised_stream_id) + if not handler: + return 0 + http2._remove_handler(handler) + +cdef int on_stream_close(cnghttp2.nghttp2_session *session, + int32_t stream_id, + uint32_t error_code, + void *user_data): + cdef http2 = <_HTTP2SessionCoreBase>user_data + logging.debug('on_stream_close, stream_id:%s', stream_id) + + handler = _get_stream_user_data(session, stream_id) + if not handler: + return 0 + + try: + handler.on_close(error_code) + except: + sys.stderr.write(traceback.format_exc()) + + http2._remove_handler(handler) + + return 0 + +cdef ssize_t data_source_read(cnghttp2.nghttp2_session *session, + int32_t stream_id, + uint8_t *buf, size_t length, + uint32_t *data_flags, + cnghttp2.nghttp2_data_source *source, + void *user_data): + cdef http2 = <_HTTP2SessionCoreBase>user_data + generator = source.ptr + + http2.enter_callback() + try: + data, flag = generator(length) + except: + sys.stderr.write(traceback.format_exc()) + return cnghttp2.NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; + finally: + http2.leave_callback() + + if flag == DATA_DEFERRED: + return cnghttp2.NGHTTP2_ERR_DEFERRED + + if data: + nread = len(data) + memcpy(buf, data, nread) + else: + nread = 0 + + if flag == DATA_EOF: + data_flags[0] = cnghttp2.NGHTTP2_DATA_FLAG_EOF + elif flag != DATA_OK: + return cnghttp2.NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE + + return nread + +cdef int client_on_begin_headers(cnghttp2.nghttp2_session *session, + const cnghttp2.nghttp2_frame *frame, + void *user_data): + cdef http2 = <_HTTP2ClientSessionCore>user_data + + if frame.hd.type == cnghttp2.NGHTTP2_PUSH_PROMISE: + # Generate a temporary handler until the headers are all received + push_handler = BaseResponseHandler() + http2._add_handler(push_handler, frame.push_promise.promised_stream_id) + cnghttp2.nghttp2_session_set_stream_user_data(session, frame.push_promise.promised_stream_id, + push_handler) + + return 0 + +cdef int client_on_frame_recv(cnghttp2.nghttp2_session *session, + const cnghttp2.nghttp2_frame *frame, + void *user_data): + cdef http2 = <_HTTP2ClientSessionCore>user_data + logging.debug('client_on_frame_recv, type:%s, stream_id:%s', frame.hd.type, frame.hd.stream_id) + + if frame.hd.type == cnghttp2.NGHTTP2_DATA: + if frame.hd.flags & cnghttp2.NGHTTP2_FLAG_END_STREAM: + handler = _get_stream_user_data(session, frame.hd.stream_id) + if not handler: + return 0 + try: + handler.on_response_done() + except: + sys.stderr.write(traceback.format_exc()) + return http2._rst_stream(frame.hd.stream_id) + elif frame.hd.type == cnghttp2.NGHTTP2_HEADERS: + if frame.headers.cat == cnghttp2.NGHTTP2_HCAT_RESPONSE or frame.headers.cat == cnghttp2.NGHTTP2_HCAT_PUSH_RESPONSE: + handler = _get_stream_user_data(session, frame.hd.stream_id) + + if not handler: + return 0 + # TODO handle 1xx non-final response + if handler.cookies: + handler.headers.append((b'cookie', + b'; '.join(handler.cookies))) + handler.cookies = None + try: + handler.on_headers() + if frame.hd.flags & cnghttp2.NGHTTP2_FLAG_END_STREAM: + handler.on_response_done() + except: + sys.stderr.write(traceback.format_exc()) + return http2._rst_stream(frame.hd.stream_id) + elif frame.hd.type == cnghttp2.NGHTTP2_SETTINGS: + if (frame.hd.flags & cnghttp2.NGHTTP2_FLAG_ACK): + http2._stop_settings_timer() + elif frame.hd.type == cnghttp2.NGHTTP2_PUSH_PROMISE: + handler = _get_stream_user_data(session, frame.hd.stream_id) + if not handler: + return 0 + # Get the temporary push_handler which now should have all of the header data + push_handler = _get_stream_user_data(session, frame.push_promise.promised_stream_id) + if not push_handler: + return 0 + # Remove the temporary handler + http2._remove_handler(push_handler) + cnghttp2.nghttp2_session_set_stream_user_data(session, frame.push_promise.promised_stream_id, + NULL) + + try: + handler.on_push_promise(push_handler) + except: + sys.stderr.write(traceback.format_exc()) + return http2._rst_stream(frame.hd.stream_id) + + return 0 + +cdef int client_on_frame_send(cnghttp2.nghttp2_session *session, + const cnghttp2.nghttp2_frame *frame, + void *user_data): + cdef http2 = <_HTTP2ClientSessionCore>user_data + logging.debug('client_on_frame_send, type:%s, stream_id:%s', frame.hd.type, frame.hd.stream_id) + + if frame.hd.type == cnghttp2.NGHTTP2_SETTINGS: + if (frame.hd.flags & cnghttp2.NGHTTP2_FLAG_ACK) != 0: + return 0 + http2._start_settings_timer() + +cdef class _HTTP2SessionCoreBase: + cdef cnghttp2.nghttp2_session *session + cdef transport + cdef handler_class + cdef handlers + cdef settings_timer + cdef inside_callback + + def __cinit__(self, transport, handler_class=None): + self.session = NULL + self.transport = transport + self.handler_class = handler_class + self.handlers = set() + self.settings_timer = None + self.inside_callback = False + + def __dealloc__(self): + cnghttp2.nghttp2_session_del(self.session) + + def data_received(self, data): + cdef ssize_t rv + + rv = cnghttp2.nghttp2_session_mem_recv(self.session, data, len(data)) + if rv < 0: + raise Exception('nghttp2_session_mem_recv failed: {}'.format\ + (_strerror(rv))) + self.send_data() + + OUTBUF_MAX = 65535 + SETTINGS_TIMEOUT = 5.0 + + def send_data(self): + cdef ssize_t outbuflen + cdef const uint8_t *outbuf + + while True: + if self.transport.get_write_buffer_size() > self.OUTBUF_MAX: + break + outbuflen = cnghttp2.nghttp2_session_mem_send(self.session, &outbuf) + if outbuflen == 0: + break + if outbuflen < 0: + raise Exception('nghttp2_session_mem_send faild: {}'.format\ + (_strerror(outbuflen))) + self.transport.write(outbuf[:outbuflen]) + + if self.transport.get_write_buffer_size() == 0 and \ + cnghttp2.nghttp2_session_want_read(self.session) == 0 and \ + cnghttp2.nghttp2_session_want_write(self.session) == 0: + self.transport.close() + + def resume(self, stream_id): + cnghttp2.nghttp2_session_resume_data(self.session, stream_id) + if not self.inside_callback: + self.send_data() + + def enter_callback(self): + self.inside_callback = True + + def leave_callback(self): + self.inside_callback = False + + def _make_handler(self, stream_id): + logging.debug('_make_handler, stream_id:%s', stream_id) + handler = self.handler_class(self, stream_id) + self.handlers.add(handler) + return handler + + def _remove_handler(self, handler): + logging.debug('_remove_handler, stream_id:%s', handler.stream_id) + self.handlers.remove(handler) + + def _add_handler(self, handler, stream_id): + logging.debug('_add_handler, stream_id:%s', stream_id) + handler.stream_id = stream_id + handler.http2 = self + handler.remote_address = self._get_remote_address() + self.handlers.add(handler) + + def _rst_stream(self, stream_id, + error_code=cnghttp2.NGHTTP2_INTERNAL_ERROR): + cdef int rv + + rv = cnghttp2.nghttp2_submit_rst_stream\ + (self.session, cnghttp2.NGHTTP2_FLAG_NONE, + stream_id, error_code) + + return rv + + def _get_remote_address(self): + return self.transport.get_extra_info('peername') + + def _start_settings_timer(self): + loop = asyncio.get_event_loop() + self.settings_timer = loop.call_later(self.SETTINGS_TIMEOUT, + self._settings_timeout) + + def _stop_settings_timer(self): + if self.settings_timer: + self.settings_timer.cancel() + self.settings_timer = None + + def _settings_timeout(self): + cdef int rv + + logging.debug('_settings_timeout') + + self.settings_timer = None + + rv = cnghttp2.nghttp2_session_terminate_session\ + (self.session, cnghttp2.NGHTTP2_SETTINGS_TIMEOUT) + try: + self.send_data() + except Exception as err: + sys.stderr.write(traceback.format_exc()) + self.transport.close() + return + + def _log_request(self, handler): + now = datetime.datetime.now() + tv = time.mktime(now.timetuple()) + datestr = email.utils.formatdate(timeval=tv, localtime=False, + usegmt=True) + try: + method = handler.method.decode('utf-8') + except: + method = handler.method + try: + path = handler.path.decode('utf-8') + except: + path = handler.path + logging.info('%s - - [%s] "%s %s HTTP/2" %s - %s', handler.remote_address[0], + datestr, method, path, handler.status, + 'P' if handler.pushed else '-') + + def close(self): + rv = cnghttp2.nghttp2_session_terminate_session\ + (self.session, cnghttp2.NGHTTP2_NO_ERROR) + try: + self.send_data() + except Exception as err: + sys.stderr.write(traceback.format_exc()) + self.transport.close() + return + +cdef class _HTTP2SessionCore(_HTTP2SessionCoreBase): + def __cinit__(self, *args, **kwargs): + cdef cnghttp2.nghttp2_session_callbacks *callbacks + cdef cnghttp2.nghttp2_settings_entry iv[2] + cdef int rv + + super(_HTTP2SessionCore, self).__init__(*args, **kwargs) + + rv = cnghttp2.nghttp2_session_callbacks_new(&callbacks) + + if rv != 0: + raise Exception('nghttp2_session_callbacks_new failed: {}'.format\ + (_strerror(rv))) + + cnghttp2.nghttp2_session_callbacks_set_on_header_callback( + callbacks, server_on_header) + cnghttp2.nghttp2_session_callbacks_set_on_begin_headers_callback( + callbacks, server_on_begin_headers) + cnghttp2.nghttp2_session_callbacks_set_on_frame_recv_callback( + callbacks, server_on_frame_recv) + cnghttp2.nghttp2_session_callbacks_set_on_stream_close_callback( + callbacks, on_stream_close) + cnghttp2.nghttp2_session_callbacks_set_on_frame_send_callback( + callbacks, server_on_frame_send) + cnghttp2.nghttp2_session_callbacks_set_on_frame_not_send_callback( + callbacks, server_on_frame_not_send) + cnghttp2.nghttp2_session_callbacks_set_on_data_chunk_recv_callback( + callbacks, on_data_chunk_recv) + + rv = cnghttp2.nghttp2_session_server_new(&self.session, callbacks, + self) + + cnghttp2.nghttp2_session_callbacks_del(callbacks) + + if rv != 0: + raise Exception('nghttp2_session_server_new failed: {}'.format\ + (_strerror(rv))) + + iv[0].settings_id = cnghttp2.NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS + iv[0].value = 100 + iv[1].settings_id = cnghttp2.NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE + iv[1].value = cnghttp2.NGHTTP2_INITIAL_WINDOW_SIZE + + rv = cnghttp2.nghttp2_submit_settings(self.session, + cnghttp2.NGHTTP2_FLAG_NONE, + iv, sizeof(iv) / sizeof(iv[0])) + + if rv != 0: + raise Exception('nghttp2_submit_settings failed: {}'.format\ + (_strerror(rv))) + + def send_response(self, handler): + cdef cnghttp2.nghttp2_data_provider prd + cdef cnghttp2.nghttp2_data_provider *prd_ptr + cdef cnghttp2.nghttp2_nv *nva + cdef size_t nvlen + cdef int rv + + logging.debug('send_response, stream_id:%s', handler.stream_id) + + nva = NULL + nvlen = _make_nva(&nva, handler.response_headers) + + if handler.response_body: + prd.source.ptr = handler.response_body + prd.read_callback = data_source_read + prd_ptr = &prd + else: + prd_ptr = NULL + + rv = cnghttp2.nghttp2_submit_response(self.session, handler.stream_id, + nva, nvlen, prd_ptr) + + free(nva) + + if rv != 0: + # TODO Ignore return value + self._rst_stream(handler.stream_id) + raise Exception('nghttp2_submit_response failed: {}'.format\ + (_strerror(rv))) + + self._log_request(handler) + + def push(self, handler, promised_handler): + cdef cnghttp2.nghttp2_nv *nva + cdef size_t nvlen + cdef int32_t promised_stream_id + + self.handlers.add(promised_handler) + + nva = NULL + nvlen = _make_nva(&nva, promised_handler.headers) + + promised_stream_id = cnghttp2.nghttp2_submit_push_promise\ + (self.session, + cnghttp2.NGHTTP2_FLAG_NONE, + handler.stream_id, + nva, nvlen, + promised_handler) + if promised_stream_id < 0: + raise Exception('nghttp2_submit_push_promise failed: {}'.format\ + (_strerror(promised_stream_id))) + + promised_handler.stream_id = promised_stream_id + + logging.debug('push, stream_id:%s', promised_stream_id) + + return promised_handler + +cdef class _HTTP2ClientSessionCore(_HTTP2SessionCoreBase): + def __cinit__(self, *args, **kwargs): + cdef cnghttp2.nghttp2_session_callbacks *callbacks + cdef cnghttp2.nghttp2_settings_entry iv[2] + cdef int rv + + super(_HTTP2ClientSessionCore, self).__init__(*args, **kwargs) + + rv = cnghttp2.nghttp2_session_callbacks_new(&callbacks) + + if rv != 0: + raise Exception('nghttp2_session_callbacks_new failed: {}'.format\ + (_strerror(rv))) + + cnghttp2.nghttp2_session_callbacks_set_on_header_callback( + callbacks, client_on_header) + cnghttp2.nghttp2_session_callbacks_set_on_begin_headers_callback( + callbacks, client_on_begin_headers) + cnghttp2.nghttp2_session_callbacks_set_on_frame_recv_callback( + callbacks, client_on_frame_recv) + cnghttp2.nghttp2_session_callbacks_set_on_stream_close_callback( + callbacks, on_stream_close) + cnghttp2.nghttp2_session_callbacks_set_on_frame_send_callback( + callbacks, client_on_frame_send) + cnghttp2.nghttp2_session_callbacks_set_on_data_chunk_recv_callback( + callbacks, on_data_chunk_recv) + + rv = cnghttp2.nghttp2_session_client_new(&self.session, callbacks, + self) + + cnghttp2.nghttp2_session_callbacks_del(callbacks) + + if rv != 0: + raise Exception('nghttp2_session_client_new failed: {}'.format\ + (_strerror(rv))) + + iv[0].settings_id = cnghttp2.NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS + iv[0].value = 100 + iv[1].settings_id = cnghttp2.NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE + iv[1].value = cnghttp2.NGHTTP2_INITIAL_WINDOW_SIZE + + rv = cnghttp2.nghttp2_submit_settings(self.session, + cnghttp2.NGHTTP2_FLAG_NONE, + iv, sizeof(iv) / sizeof(iv[0])) + + if rv != 0: + raise Exception('nghttp2_submit_settings failed: {}'.format\ + (_strerror(rv))) + + def send_request(self, method, scheme, host, path, headers, body, handler): + cdef cnghttp2.nghttp2_data_provider prd + cdef cnghttp2.nghttp2_data_provider *prd_ptr + cdef cnghttp2.nghttp2_priority_spec *pri_ptr + cdef cnghttp2.nghttp2_nv *nva + cdef size_t nvlen + cdef int32_t stream_id + + body = wrap_body(body) + + custom_headers = _encode_headers(headers) + headers = [ + (b':method', method.encode('utf-8')), + (b':scheme', scheme.encode('utf-8')), + (b':authority', host.encode('utf-8')), + (b':path', path.encode('utf-8')) + ] + headers.extend(custom_headers) + + nva = NULL + nvlen = _make_nva(&nva, headers) + + if body: + prd.source.ptr = body + prd.read_callback = data_source_read + prd_ptr = &prd + else: + prd_ptr = NULL + + # TODO: Enable priorities + pri_ptr = NULL + + stream_id = cnghttp2.nghttp2_submit_request\ + (self.session, pri_ptr, + nva, nvlen, prd_ptr, + handler) + free(nva) + + if stream_id < 0: + raise Exception('nghttp2_submit_request failed: {}'.format\ + (_strerror(stream_id))) + + logging.debug('request, stream_id:%s', stream_id) + + self._add_handler(handler, stream_id) + cnghttp2.nghttp2_session_set_stream_user_data(self.session, stream_id, + handler) + + return handler + + def push(self, push_promise, handler): + if handler: + # push_promise accepted, fill in the handler with the stored + # headers from the push_promise + handler.status = push_promise.status + handler.scheme = push_promise.scheme + handler.method = push_promise.method + handler.host = push_promise.host + handler.path = push_promise.path + handler.cookies = push_promise.cookies + handler.stream_id = push_promise.stream_id + handler.http2 = self + handler.pushed = True + + self._add_handler(handler, handler.stream_id) + + cnghttp2.nghttp2_session_set_stream_user_data(self.session, handler.stream_id, + handler) + else: + # push_promise rejected, reset the stream + self._rst_stream(push_promise.stream_id, + error_code=cnghttp2.NGHTTP2_NO_ERROR) + +if asyncio: + + class BaseRequestHandler: + + """HTTP/2 request (stream) handler base class. + + The class is used to handle the HTTP/2 stream. By default, it does + not nothing. It must be subclassed to handle each event callback + method. + + The first callback method invoked is on_headers(). It is called + when HEADERS frame, which includes request header fields, is + arrived. + + If request has request body, on_data(data) is invoked for each + chunk of received data. + + When whole request is received, on_request_done() is invoked. + + When stream is closed, on_close(error_code) is called. + + The application can send response using send_response() method. It + can be used in on_headers(), on_data() or on_request_done(). + + The application can push resource using push() method. It must be + used before send_response() call. + + The following instance variables are available: + + client_address + Contains a tuple of the form (host, port) referring to the client's + address. + + stream_id + Stream ID of this stream + + scheme + Scheme of the request URI. This is a value of :scheme header field. + + method + Method of this stream. This is a value of :method header field. + + host + This is a value of :authority or host header field. + + path + This is a value of :path header field. + + headers + Request header fields + + """ + + def __init__(self, http2, stream_id): + self.headers = [] + self.cookies = [] + # Stream ID. For promised stream, it is initially -1. + self.stream_id = stream_id + self.http2 = http2 + # address of the client + self.remote_address = self.http2._get_remote_address() + # :scheme header field in request + self.scheme = None + # :method header field in request + self.method = None + # :authority or host header field in request + self.host = None + # :path header field in request + self.path = None + # HTTP status + self.status = None + # True if this is a handler for pushed resource + self.pushed = False + + @property + def client_address(self): + return self.remote_address + + def on_headers(self): + + '''Called when request HEADERS is arrived. + + ''' + pass + + def on_data(self, data): + + '''Called when a chunk of request body is arrived. This method + will be called multiple times until all data are received. + + ''' + pass + + def on_request_done(self): + + '''Called when whole request was received + + ''' + pass + + def on_close(self, error_code): + + '''Called when stream is about to close. + + ''' + pass + + def send_response(self, status=200, headers=None, body=None): + + '''Send response. The status is HTTP status code. The headers is + additional response headers. The :status header field is + appended by the library. The body is the response body. It + could be None if response body is empty. Or it must be + instance of either str, bytes, io.IOBase or callable, + called body generator, which takes one parameter, + size. The body generator generates response body. It can + pause generation of response so that it can wait for slow + backend data generation. When invoked, it should return + tuple, byte string and flag. The flag is either DATA_OK, + DATA_EOF and DATA_DEFERRED. For non-empty byte string and + it is not the last chunk of response, DATA_OK is returned + as flag. If this is the last chunk of the response (byte + string is possibly None), DATA_EOF must be returned as + flag. If there is no data available right now, but + additional data are anticipated, return tuple (None, + DATA_DEFERRD). When data arrived, call resume() and + restart response body transmission. + + Only the body generator can pause response body + generation; instance of io.IOBase must not block. + + If instance of str is specified as body, it is encoded + using UTF-8. + + The headers is a list of tuple of the form (name, + value). The name and value can be either unicode string or + byte string. + + On error, exception will be thrown. + + ''' + if self.status is not None: + raise Exception('response has already been sent') + + if not status: + raise Exception('status must not be empty') + + body = wrap_body(body) + + self._set_response_prop(status, headers, body) + self.http2.send_response(self) + + def push(self, path, method='GET', request_headers=None, + status=200, headers=None, body=None): + + '''Push a resource. The path is a path portion of request URI + for this + resource. The method is a method to access this + resource. The request_headers is additional request + headers to access this resource. The :scheme, :method, + :authority and :path are appended by the library. The + :scheme and :authority are inherited from the request (not + request_headers parameter). + + The status is HTTP status code. The headers is additional + response headers. The :status header field is appended by + the library. The body is the response body. It has the + same semantics of body parameter of send_response(). + + The headers and request_headers are a list of tuple of the + form (name, value). The name and value can be either + unicode string or byte string. + + On error, exception will be thrown. + + ''' + if not status: + raise Exception('status must not be empty') + + if not method: + raise Exception('method must not be empty') + + if not path: + raise Exception('path must not be empty') + + body = wrap_body(body) + + promised_handler = self.http2._make_handler(-1) + promised_handler.pushed = True + promised_handler.scheme = self.scheme + promised_handler.method = method.encode('utf-8') + promised_handler.host = self.host + promised_handler.path = path.encode('utf-8') + promised_handler._set_response_prop(status, headers, body) + + headers = [ + (b':method', promised_handler.method), + (b':scheme', promised_handler.scheme), + (b':authority', promised_handler.host), + (b':path', promised_handler.path) + ] + headers.extend(_encode_headers(request_headers)) + + promised_handler.headers = headers + + return self.http2.push(self, promised_handler) + + def _set_response_prop(self, status, headers, body): + self.status = status + + if headers is None: + headers = [] + + self.response_headers = [(b':status', str(status).encode('utf-8'))] + self.response_headers.extend(_encode_headers(headers)) + + self.response_body = body + + def resume(self): + self.http2.resume(self.stream_id) + + def _encode_headers(headers): + if not headers: + return [] + return [(k if isinstance(k, bytes) else k.encode('utf-8'), + v if isinstance(v, bytes) else v.encode('utf-8')) \ + for k, v in headers] + + class _HTTP2Session(asyncio.Protocol): + + def __init__(self, RequestHandlerClass): + asyncio.Protocol.__init__(self) + self.RequestHandlerClass = RequestHandlerClass + self.http2 = None + + def connection_made(self, transport): + address = transport.get_extra_info('peername') + logging.info('connection_made, address:%s, port:%s', address[0], address[1]) + + self.transport = transport + sock = self.transport.get_extra_info('socket') + sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) + ssl_ctx = self.transport.get_extra_info('sslcontext') + if ssl_ctx: + protocol = sock.selected_npn_protocol() + logging.info('npn, protocol:%s', protocol) + if protocol.encode('utf-8') != \ + cnghttp2.NGHTTP2_PROTO_VERSION_ID: + self.transport.abort() + return + try: + self.http2 = _HTTP2SessionCore\ + (self.transport, + self.RequestHandlerClass) + except Exception as err: + sys.stderr.write(traceback.format_exc()) + self.transport.abort() + return + + + def connection_lost(self, exc): + logging.info('connection_lost') + if self.http2: + self.http2 = None + + def data_received(self, data): + try: + self.http2.data_received(data) + except Exception as err: + sys.stderr.write(traceback.format_exc()) + self.transport.close() + return + + def resume_writing(self): + try: + self.http2.send_data() + except Exception as err: + sys.stderr.write(traceback.format_exc()) + self.transport.close() + return + + class HTTP2Server: + + '''HTTP/2 server. + + This class builds on top of the asyncio event loop. On + construction, RequestHandlerClass must be given, which must be a + subclass of BaseRequestHandler class. + + ''' + def __init__(self, address, RequestHandlerClass, ssl=None): + + '''address is a tuple of the listening address and port (e.g., + ('127.0.0.1', 8080)). RequestHandlerClass must be a subclass + of BaseRequestHandler class to handle a HTTP/2 stream. The + ssl can be ssl.SSLContext instance. If it is not None, the + resulting server is SSL/TLS capable. + + ''' + def session_factory(): + return _HTTP2Session(RequestHandlerClass) + + self.loop = asyncio.get_event_loop() + + if ssl: + ssl.set_npn_protocols([cnghttp2.NGHTTP2_PROTO_VERSION_ID\ + .decode('utf-8')]) + + coro = self.loop.create_server(session_factory, + host=address[0], port=address[1], + ssl=ssl) + self.server = self.loop.run_until_complete(coro) + logging.info('listen, address:%s, port:%s', address[0], address[1]) + + def serve_forever(self): + try: + self.loop.run_forever() + finally: + self.server.close() + self.loop.close() + + + + class BaseResponseHandler: + + """HTTP/2 response (stream) handler base class. + + The class is used to handle the HTTP/2 stream. By default, it does + not nothing. It must be subclassed to handle each event callback + method. + + The first callback method invoked is on_headers(). It is called + when HEADERS frame, which includes response header fields, is + arrived. + + If response has a body, on_data(data) is invoked for each + chunk of received data. + + When whole response is received, on_response_done() is invoked. + + When stream is closed, on_close(error_code) is called. + + The application can send follow up requests using HTTP2Client.send_request() method. + + The application can handle push resource using on_push_promise() method. + + The following instance variables are available: + + server_address + Contains a tuple of the form (host, port) referring to the server's + address. + + stream_id + Stream ID of this stream + + scheme + Scheme of the request URI. This is a value of :scheme header field. + + method + Method of this stream. This is a value of :method header field. + + host + This is a value of :authority or host header field. + + path + This is a value of :path header field. + + headers + Response header fields. There is a special exception. If this + object is passed to push_promise(), this instance variable contains + pushed request header fields. + + """ + + def __init__(self, http2=None, stream_id=-1): + self.headers = [] + self.cookies = [] + # Stream ID. For promised stream, it is initially -1. + self.stream_id = stream_id + self.http2 = http2 + # address of the server + self.remote_address = None + # :scheme header field in request + self.scheme = None + # :method header field in request + self.method = None + # :authority or host header field in request + self.host = None + # :path header field in request + self.path = None + # HTTP status + self.status = None + # True if this is a handler for pushed resource + self.pushed = False + + @property + def server_address(self): + return self.remote_address + + def on_headers(self): + + '''Called when response HEADERS is arrived. + + ''' + pass + + def on_data(self, data): + + '''Called when a chunk of response body is arrived. This method + will be called multiple times until all data are received. + + ''' + pass + + def on_response_done(self): + + '''Called when whole response was received + + ''' + pass + + def on_close(self, error_code): + + '''Called when stream is about to close. + + ''' + pass + + def on_push_promise(self, push_promise): + + '''Called when a push is promised. Default behavior is to + cancel the push. If application overrides this method, + it should call either accept_push or reject_push. + + ''' + self.reject_push(push_promise) + + def reject_push(self, push_promise): + + '''Convenience method equivalent to calling accept_push + with a falsy value. + + ''' + self.http2.push(push_promise, None) + + def accept_push(self, push_promise, handler=None): + + '''Accept a push_promise and provider a handler for the + new stream. If a falsy value is supplied for the handler, + the push is rejected. + + ''' + self.http2.push(push_promise, handler) + + def resume(self): + self.http2.resume(self.stream_id) + + class _HTTP2ClientSession(asyncio.Protocol): + + def __init__(self, client): + asyncio.Protocol.__init__(self) + self.http2 = None + self.pending = [] + self.client = client + + def connection_made(self, transport): + address = transport.get_extra_info('peername') + logging.info('connection_made, address:%s, port:%s', address[0], address[1]) + + self.transport = transport + sock = self.transport.get_extra_info('socket') + sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) + ssl_ctx = self.transport.get_extra_info('sslcontext') + if ssl_ctx: + protocol = sock.selected_npn_protocol() + logging.info('npn, protocol:%s', protocol) + if protocol is None or protocol.encode('utf-8') != \ + cnghttp2.NGHTTP2_PROTO_VERSION_ID: + self.transport.abort() + + self.http2 = _HTTP2ClientSessionCore(self.transport) + + # Clear pending requests + send_pending = self.pending + self.pending = [] + for method,scheme,host,path,headers,body,handler in send_pending: + self.send_request(method=method, scheme=scheme, host=host, path=path,\ + headers=headers, body=body, handler=handler) + self.http2.send_data() + + def connection_lost(self, exc): + logging.info('connection_lost') + if self.http2: + self.http2 = None + self.client.close() + + def data_received(self, data): + try: + self.http2.data_received(data) + except Exception as err: + sys.stderr.write(traceback.format_exc()) + self.transport.close() + return + + def resume_writing(self): + try: + self.http2.send_data() + except Exception as err: + sys.stderr.write(traceback.format_exc()) + self.transport.close() + return + + def send_request(self, method, scheme, host, path, headers, body, handler): + try: + # Waiting until connection established + if not self.http2: + self.pending.append([method, scheme, host, path, headers, body, handler]) + return + + self.http2.send_request(method=method, scheme=scheme, host=host, path=path,\ + headers=headers, body=body, handler=handler) + self.http2.send_data() + except Exception as err: + sys.stderr.write(traceback.format_exc()) + self.transport.close() + return + + def close(self): + if self.http2: + self.http2.close() + + + class HTTP2Client: + + '''HTTP/2 client. + + This class builds on top of the asyncio event loop. + + ''' + def __init__(self, address, loop=None, ssl=None): + + '''address is a tuple of the connect address and port (e.g., + ('127.0.0.1', 8080)). The ssl can be ssl.SSLContext instance. + If it is not None, the resulting client is SSL/TLS capable. + ''' + + self.address = address + self.session = _HTTP2ClientSession(self) + def session_factory(): + return self.session + + if ssl: + ssl.set_npn_protocols([cnghttp2.NGHTTP2_PROTO_VERSION_ID\ + .decode('utf-8')]) + + self.loop = loop + if not self.loop: + self.loop = asyncio.get_event_loop() + + coro = self.loop.create_connection(session_factory, + host=address[0], port=address[1], + ssl=ssl) + + if ssl: + self.scheme = 'https' + else: + self.scheme = 'http' + + self.transport,_ = self.loop.run_until_complete(coro) + logging.info('connect, address:%s, port:%s', self.address[0], self.address[1]) + + @property + def io_loop(self): + return self.loop + + def close(self): + self.session.close() + + def send_request(self, method='GET', url='/', headers=None, body=None, handler=None): + url = urlparse(url) + scheme = url.scheme if url.scheme else self.scheme + host = url.netloc if url.netloc else self.address[0]+':'+str(self.address[1]) + path = url.path + if url.params: + path += ';'+url.params + if url.query: + path += '?'+url.query + if url.fragment: + path += '#'+url.fragment + + self.session.send_request(method=method, scheme=scheme, host=host, path=path,\ + headers=headers, body=body, handler=handler) diff --git a/python/setup.py.in b/python/setup.py.in new file mode 100644 index 0000000..7f9de48 --- /dev/null +++ b/python/setup.py.in @@ -0,0 +1,49 @@ +# nghttp2 - HTTP/2 C Library + +# Copyright (c) 2013 Tatsuhiro Tsujikawa + +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: + +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. + +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +import sys +from distutils.core import setup +from distutils.extension import Extension + +if sys.platform == "win32": + LIBS = ['nghttp2_imp', 'ws2_32'] +else: + LIBS = ['nghttp2'] + +setup( + name = 'python-nghttp2', + description = 'Python HTTP/2 library on top of nghttp2', + author = 'Tatsuhiro Tsujikawa', + version = '@PACKAGE_VERSION@', + author_email = 'tatsuhiro.t@gmail.com', + url = 'https://nghttp2.org/', + keywords = [], + ext_modules = [Extension("nghttp2", + ["nghttp2.c"], + include_dirs=['@top_srcdir@/lib', + '@top_srcdir@/lib/includes', + '@top_builddir@/lib/includes'], + library_dirs=['@top_builddir@/lib/.libs'], + libraries=LIBS)], + long_description='TBD' + ) diff --git a/src/HtmlParser.cc b/src/HtmlParser.cc new file mode 100644 index 0000000..bcbfd13 --- /dev/null +++ b/src/HtmlParser.cc @@ -0,0 +1,187 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "HtmlParser.h" + +#include + +#include "util.h" + +namespace nghttp2 { + +ParserData::ParserData(const std::string &base_uri) + : base_uri(base_uri), inside_head(0) {} + +HtmlParser::HtmlParser(const std::string &base_uri) + : base_uri_(base_uri), parser_ctx_(nullptr), parser_data_(base_uri) {} + +HtmlParser::~HtmlParser() { htmlFreeParserCtxt(parser_ctx_); } + +namespace { +const char *get_attr(const xmlChar **attrs, const char *name) { + if (attrs == nullptr) { + return nullptr; + } + for (; *attrs; attrs += 2) { + if (util::strieq(reinterpret_cast(attrs[0]), name)) { + return reinterpret_cast(attrs[1]); + } + } + return nullptr; +} +} // namespace + +namespace { +void add_link(ParserData *parser_data, const char *uri, ResourceType res_type) { + auto u = xmlBuildURI( + reinterpret_cast(uri), + reinterpret_cast(parser_data->base_uri.c_str())); + if (u) { + parser_data->links.push_back( + std::make_pair(reinterpret_cast(u), res_type)); + free(u); + } +} +} // namespace + +namespace { +void start_element_func(void *user_data, const xmlChar *name, + const xmlChar **attrs) { + auto parser_data = static_cast(user_data); + if (util::strieq(reinterpret_cast(name), "head")) { + ++parser_data->inside_head; + } + if (util::strieq(reinterpret_cast(name), "link")) { + auto rel_attr = get_attr(attrs, "rel"); + auto href_attr = get_attr(attrs, "href"); + if (!href_attr) { + return; + } + if (util::strieq(rel_attr, "shortcut icon")) { + add_link(parser_data, href_attr, REQ_OTHERS); + } else if (util::strieq(rel_attr, "stylesheet")) { + add_link(parser_data, href_attr, REQ_CSS); + } + } else if (util::strieq(reinterpret_cast(name), "img")) { + auto src_attr = get_attr(attrs, "src"); + if (!src_attr) { + return; + } + add_link(parser_data, src_attr, REQ_IMG); + } else if (util::strieq(reinterpret_cast(name), "script")) { + auto src_attr = get_attr(attrs, "src"); + if (!src_attr) { + return; + } + if (parser_data->inside_head) { + add_link(parser_data, src_attr, REQ_JS); + } else { + add_link(parser_data, src_attr, REQ_UNBLOCK_JS); + } + } +} +} // namespace + +namespace { +void end_element_func(void *user_data, const xmlChar *name) { + auto parser_data = static_cast(user_data); + if (util::strieq(reinterpret_cast(name), "head")) { + --parser_data->inside_head; + } +} +} // namespace + +namespace { +xmlSAXHandler saxHandler = { + nullptr, // internalSubsetSAXFunc + nullptr, // isStandaloneSAXFunc + nullptr, // hasInternalSubsetSAXFunc + nullptr, // hasExternalSubsetSAXFunc + nullptr, // resolveEntitySAXFunc + nullptr, // getEntitySAXFunc + nullptr, // entityDeclSAXFunc + nullptr, // notationDeclSAXFunc + nullptr, // attributeDeclSAXFunc + nullptr, // elementDeclSAXFunc + nullptr, // unparsedEntityDeclSAXFunc + nullptr, // setDocumentLocatorSAXFunc + nullptr, // startDocumentSAXFunc + nullptr, // endDocumentSAXFunc + &start_element_func, // startElementSAXFunc + &end_element_func, // endElementSAXFunc + nullptr, // referenceSAXFunc + nullptr, // charactersSAXFunc + nullptr, // ignorableWhitespaceSAXFunc + nullptr, // processingInstructionSAXFunc + nullptr, // commentSAXFunc + nullptr, // warningSAXFunc + nullptr, // errorSAXFunc + nullptr, // fatalErrorSAXFunc + nullptr, // getParameterEntitySAXFunc + nullptr, // cdataBlockSAXFunc + nullptr, // externalSubsetSAXFunc + 0, // unsigned int initialized + nullptr, // void * _private + nullptr, // startElementNsSAX2Func + nullptr, // endElementNsSAX2Func + nullptr, // xmlStructuredErrorFunc +}; +} // namespace + +int HtmlParser::parse_chunk(const char *chunk, size_t size, int fin) { + if (!parser_ctx_) { + parser_ctx_ = + htmlCreatePushParserCtxt(&saxHandler, &parser_data_, chunk, size, + base_uri_.c_str(), XML_CHAR_ENCODING_NONE); + if (!parser_ctx_) { + return -1; + } else { + if (fin) { + return parse_chunk_internal(nullptr, 0, fin); + } else { + return 0; + } + } + } else { + return parse_chunk_internal(chunk, size, fin); + } +} + +int HtmlParser::parse_chunk_internal(const char *chunk, size_t size, int fin) { + int rv = htmlParseChunk(parser_ctx_, chunk, size, fin); + if (rv == 0) { + return 0; + } else { + return -1; + } +} + +const std::vector> & +HtmlParser::get_links() const { + return parser_data_.links; +} + +void HtmlParser::clear_links() { parser_data_.links.clear(); } + +} // namespace nghttp2 diff --git a/src/HtmlParser.h b/src/HtmlParser.h new file mode 100644 index 0000000..fc2f2e5 --- /dev/null +++ b/src/HtmlParser.h @@ -0,0 +1,94 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef HTML_PARSER_H +#define HTML_PARSER_H + +#include "nghttp2_config.h" + +#include +#include + +#ifdef HAVE_LIBXML2 + +#include + +#endif // HAVE_LIBXML2 + +namespace nghttp2 { + +enum ResourceType { + REQ_CSS = 1, + REQ_JS, + REQ_UNBLOCK_JS, + REQ_IMG, + REQ_OTHERS, +}; + +struct ParserData { + std::string base_uri; + std::vector> links; + // > 0 if we are inside "head" element. + int inside_head; + ParserData(const std::string &base_uri); +}; + +#ifdef HAVE_LIBXML2 + +class HtmlParser { +public: + HtmlParser(const std::string &base_uri); + ~HtmlParser(); + int parse_chunk(const char *chunk, size_t size, int fin); + const std::vector> &get_links() const; + void clear_links(); + +private: + int parse_chunk_internal(const char *chunk, size_t size, int fin); + + std::string base_uri_; + htmlParserCtxtPtr parser_ctx_; + ParserData parser_data_; +}; + +#else // !HAVE_LIBXML2 + +class HtmlParser { +public: + HtmlParser(const std::string &base_uri) {} + int parse_chunk(const char *chunk, size_t size, int fin) { return 0; } + const std::vector> &get_links() const { + return links_; + } + void clear_links() {} + +private: + std::vector> links_; +}; + +#endif // !HAVE_LIBXML2 + +} // namespace nghttp2 + +#endif // HTML_PARSER_H diff --git a/src/HttpServer.cc b/src/HttpServer.cc new file mode 100644 index 0000000..0465395 --- /dev/null +++ b/src/HttpServer.cc @@ -0,0 +1,1889 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2013 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "HttpServer.h" + +#include +#ifdef HAVE_SYS_SOCKET_H +#include +#endif // HAVE_SYS_SOCKET_H +#ifdef HAVE_NETDB_H +#include +#endif // HAVE_NETDB_H +#ifdef HAVE_UNISTD_H +#include +#endif // HAVE_UNISTD_H +#ifdef HAVE_FCNTL_H +#include +#endif // HAVE_FCNTL_H +#ifdef HAVE_NETINET_IN_H +#include +#endif // HAVE_NETINET_IN_H +#include +#ifdef HAVE_ARPA_INET_H +#include +#endif // HAVE_ARPA_INET_H + +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include "app_helper.h" +#include "http2.h" +#include "util.h" +#include "ssl.h" +#include "template.h" + +#ifndef O_BINARY +#define O_BINARY (0) +#endif // O_BINARY + +namespace nghttp2 { + +namespace { +const std::string DEFAULT_HTML = "index.html"; +const std::string NGHTTPD_SERVER = "nghttpd nghttp2/" NGHTTP2_VERSION; +} // namespace + +namespace { +void delete_handler(Http2Handler *handler) { + handler->remove_self(); + delete handler; +} +} // namespace + +namespace { +void print_session_id(int64_t id) { std::cout << "[id=" << id << "] "; } +} // namespace + +namespace { +template void append_nv(Stream *stream, const Array &nva) { + for (size_t i = 0; i < nva.size(); ++i) { + auto &nv = nva[i]; + auto token = http2::lookup_token(nv.name, nv.namelen); + if (token != -1) { + http2::index_header(stream->hdidx, token, i); + } + http2::add_header(stream->headers, nv.name, nv.namelen, nv.value, + nv.valuelen, nv.flags & NGHTTP2_NV_FLAG_NO_INDEX, token); + } +} +} // namespace + +Config::Config() + : stream_read_timeout(60.), stream_write_timeout(60.), data_ptr(nullptr), + padding(0), num_worker(1), max_concurrent_streams(100), + header_table_size(-1), port(0), verbose(false), daemon(false), + verify_client(false), no_tls(false), error_gzip(false), + early_response(false), hexdump(false), echo_upload(false) {} + +Config::~Config() {} + +namespace { +void stream_timeout_cb(struct ev_loop *loop, ev_timer *w, int revents) { + int rv; + auto stream = static_cast(w->data); + auto hd = stream->handler; + auto config = hd->get_config(); + + ev_timer_stop(hd->get_loop(), &stream->rtimer); + ev_timer_stop(hd->get_loop(), &stream->wtimer); + + if (config->verbose) { + print_session_id(hd->session_id()); + print_timer(); + std::cout << " timeout stream_id=" << stream->stream_id << std::endl; + } + + hd->submit_rst_stream(stream, NGHTTP2_INTERNAL_ERROR); + + rv = hd->on_write(); + if (rv == -1) { + delete_handler(hd); + } +} +} // namespace + +namespace { +void add_stream_read_timeout(Stream *stream) { + auto hd = stream->handler; + ev_timer_again(hd->get_loop(), &stream->rtimer); +} +} // namespace + +namespace { +void add_stream_read_timeout_if_pending(Stream *stream) { + auto hd = stream->handler; + if (ev_is_active(&stream->rtimer)) { + ev_timer_again(hd->get_loop(), &stream->rtimer); + } +} +} // namespace + +namespace { +void add_stream_write_timeout(Stream *stream) { + auto hd = stream->handler; + ev_timer_again(hd->get_loop(), &stream->wtimer); +} +} // namespace + +namespace { +void remove_stream_read_timeout(Stream *stream) { + auto hd = stream->handler; + ev_timer_stop(hd->get_loop(), &stream->rtimer); +} +} // namespace + +namespace { +void remove_stream_write_timeout(Stream *stream) { + auto hd = stream->handler; + ev_timer_stop(hd->get_loop(), &stream->wtimer); +} +} // namespace + +namespace { +void fill_callback(nghttp2_session_callbacks *callbacks, const Config *config); +} // namespace + +class Sessions { +public: + Sessions(HttpServer *sv, struct ev_loop *loop, const Config *config, + SSL_CTX *ssl_ctx) + : sv_(sv), loop_(loop), config_(config), ssl_ctx_(ssl_ctx), + callbacks_(nullptr), next_session_id_(1), tstamp_cached_(ev_now(loop)), + cached_date_(util::http_date(tstamp_cached_)) { + nghttp2_session_callbacks_new(&callbacks_); + + fill_callback(callbacks_, config_); + } + ~Sessions() { + for (auto handler : handlers_) { + delete handler; + } + nghttp2_session_callbacks_del(callbacks_); + } + void add_handler(Http2Handler *handler) { handlers_.insert(handler); } + void remove_handler(Http2Handler *handler) { handlers_.erase(handler); } + SSL_CTX *get_ssl_ctx() const { return ssl_ctx_; } + SSL *ssl_session_new(int fd) { + SSL *ssl = SSL_new(ssl_ctx_); + if (!ssl) { + std::cerr << "SSL_new() failed" << std::endl; + return nullptr; + } + if (SSL_set_fd(ssl, fd) == 0) { + std::cerr << "SSL_set_fd() failed" << std::endl; + SSL_free(ssl); + return nullptr; + } + return ssl; + } + const Config *get_config() const { return config_; } + struct ev_loop *get_loop() const { + return loop_; + } + int64_t get_next_session_id() { + auto session_id = next_session_id_; + if (next_session_id_ == std::numeric_limits::max()) { + next_session_id_ = 1; + } else { + ++next_session_id_; + } + return session_id; + } + const nghttp2_session_callbacks *get_callbacks() const { return callbacks_; } + void accept_connection(int fd) { + util::make_socket_nodelay(fd); + SSL *ssl = nullptr; + if (ssl_ctx_) { + ssl = ssl_session_new(fd); + if (!ssl) { + close(fd); + return; + } + } + auto handler = + make_unique(this, fd, ssl, get_next_session_id()); + handler->setup_bev(); + if (!ssl) { + if (handler->connection_made() != 0) { + return; + } + } + add_handler(handler.release()); + } + void update_cached_date() { cached_date_ = util::http_date(tstamp_cached_); } + const std::string &get_cached_date() { + auto t = ev_now(loop_); + if (t != tstamp_cached_) { + tstamp_cached_ = t; + update_cached_date(); + } + return cached_date_; + } + FileEntry *get_cached_fd(const std::string &path) { + auto i = fd_cache_.find(path); + if (i == std::end(fd_cache_)) { + return nullptr; + } + auto &ent = (*i).second; + ++ent.usecount; + return &ent; + } + FileEntry *cache_fd(const std::string &path, const FileEntry &ent) { + auto rv = fd_cache_.emplace(path, ent); + return &(*rv.first).second; + } + void release_fd(const std::string &path) { + auto i = fd_cache_.find(path); + if (i == std::end(fd_cache_)) { + return; + } + auto &ent = (*i).second; + if (--ent.usecount == 0) { + close(ent.fd); + fd_cache_.erase(i); + } + } + const HttpServer *get_server() const { return sv_; } + +private: + std::set handlers_; + // cache for file descriptors to read file. + std::map fd_cache_; + HttpServer *sv_; + struct ev_loop *loop_; + const Config *config_; + SSL_CTX *ssl_ctx_; + nghttp2_session_callbacks *callbacks_; + int64_t next_session_id_; + ev_tstamp tstamp_cached_; + std::string cached_date_; +}; + +Stream::Stream(Http2Handler *handler, int32_t stream_id) + : handler(handler), file_ent(nullptr), body_length(0), body_offset(0), + stream_id(stream_id), echo_upload(false) { + auto config = handler->get_config(); + ev_timer_init(&rtimer, stream_timeout_cb, 0., config->stream_read_timeout); + ev_timer_init(&wtimer, stream_timeout_cb, 0., config->stream_write_timeout); + rtimer.data = this; + wtimer.data = this; + + headers.reserve(10); + + http2::init_hdidx(hdidx); +} + +Stream::~Stream() { + if (file_ent != nullptr) { + auto sessions = handler->get_sessions(); + sessions->release_fd(file_ent->path); + } + + auto loop = handler->get_loop(); + ev_timer_stop(loop, &rtimer); + ev_timer_stop(loop, &wtimer); +} + +namespace { +void on_session_closed(Http2Handler *hd, int64_t session_id) { + if (hd->get_config()->verbose) { + print_session_id(session_id); + print_timer(); + std::cout << " closed" << std::endl; + } +} +} // namespace + +namespace { +void settings_timeout_cb(struct ev_loop *loop, ev_timer *w, int revents) { + auto hd = static_cast(w->data); + hd->terminate_session(NGHTTP2_SETTINGS_TIMEOUT); + hd->on_write(); +} +} // namespace + +namespace { +void readcb(struct ev_loop *loop, ev_io *w, int revents) { + int rv; + auto handler = static_cast(w->data); + + rv = handler->on_read(); + if (rv == -1) { + delete_handler(handler); + } +} +} // namespace + +namespace { +void writecb(struct ev_loop *loop, ev_io *w, int revents) { + int rv; + auto handler = static_cast(w->data); + + rv = handler->on_write(); + if (rv == -1) { + delete_handler(handler); + } +} +} // namespace + +Http2Handler::Http2Handler(Sessions *sessions, int fd, SSL *ssl, + int64_t session_id) + : session_id_(session_id), session_(nullptr), sessions_(sessions), + ssl_(ssl), data_pending_(nullptr), data_pendinglen_(0), fd_(fd) { + ev_timer_init(&settings_timerev_, settings_timeout_cb, 10., 0.); + ev_io_init(&wev_, writecb, fd, EV_WRITE); + ev_io_init(&rev_, readcb, fd, EV_READ); + + settings_timerev_.data = this; + wev_.data = this; + rev_.data = this; + + auto loop = sessions_->get_loop(); + ev_io_start(loop, &rev_); + + if (ssl) { + SSL_set_accept_state(ssl); + read_ = &Http2Handler::tls_handshake; + write_ = &Http2Handler::tls_handshake; + } else { + read_ = &Http2Handler::read_clear; + write_ = &Http2Handler::write_clear; + } +} + +Http2Handler::~Http2Handler() { + on_session_closed(this, session_id_); + nghttp2_session_del(session_); + if (ssl_) { + SSL_set_shutdown(ssl_, SSL_RECEIVED_SHUTDOWN); + ERR_clear_error(); + SSL_shutdown(ssl_); + } + auto loop = sessions_->get_loop(); + ev_timer_stop(loop, &settings_timerev_); + ev_io_stop(loop, &rev_); + ev_io_stop(loop, &wev_); + if (ssl_) { + SSL_free(ssl_); + } + shutdown(fd_, SHUT_WR); + close(fd_); +} + +void Http2Handler::remove_self() { sessions_->remove_handler(this); } + +struct ev_loop *Http2Handler::get_loop() const { + return sessions_->get_loop(); +} + +Http2Handler::WriteBuf *Http2Handler::get_wb() { return &wb_; } + +int Http2Handler::setup_bev() { return 0; } + +int Http2Handler::fill_wb() { + if (data_pending_) { + auto n = std::min(wb_.wleft(), data_pendinglen_); + wb_.write(data_pending_, n); + if (n < data_pendinglen_) { + data_pending_ += n; + data_pendinglen_ -= n; + return 0; + } + + data_pending_ = nullptr; + data_pendinglen_ = 0; + } + + for (;;) { + const uint8_t *data; + auto datalen = nghttp2_session_mem_send(session_, &data); + + if (datalen < 0) { + std::cerr << "nghttp2_session_mem_send() returned error: " + << nghttp2_strerror(datalen) << std::endl; + return -1; + } + if (datalen == 0) { + break; + } + auto n = wb_.write(data, datalen); + if (n < static_cast(datalen)) { + data_pending_ = data + n; + data_pendinglen_ = datalen - n; + break; + } + } + return 0; +} + +int Http2Handler::read_clear() { + int rv; + std::array buf; + + for (;;) { + ssize_t nread; + while ((nread = read(fd_, buf.data(), buf.size())) == -1 && errno == EINTR) + ; + if (nread == -1) { + if (errno == EAGAIN || errno == EWOULDBLOCK) { + break; + } + return -1; + } + if (nread == 0) { + return -1; + } + + if (get_config()->hexdump) { + util::hexdump(stdout, buf.data(), nread); + } + + rv = nghttp2_session_mem_recv(session_, buf.data(), nread); + if (rv < 0) { + if (rv != NGHTTP2_ERR_BAD_CLIENT_MAGIC) { + std::cerr << "nghttp2_session_mem_recv() returned error: " + << nghttp2_strerror(rv) << std::endl; + } + return -1; + } + } + + return write_(*this); +} + +int Http2Handler::write_clear() { + auto loop = sessions_->get_loop(); + for (;;) { + if (wb_.rleft() > 0) { + ssize_t nwrite; + while ((nwrite = write(fd_, wb_.pos, wb_.rleft())) == -1 && + errno == EINTR) + ; + if (nwrite == -1) { + if (errno == EAGAIN || errno == EWOULDBLOCK) { + ev_io_start(loop, &wev_); + return 0; + } + return -1; + } + wb_.drain(nwrite); + continue; + } + wb_.reset(); + if (fill_wb() != 0) { + return -1; + } + if (wb_.rleft() == 0) { + break; + } + } + + if (wb_.rleft() == 0) { + ev_io_stop(loop, &wev_); + } else { + ev_io_start(loop, &wev_); + } + + if (nghttp2_session_want_read(session_) == 0 && + nghttp2_session_want_write(session_) == 0 && wb_.rleft() == 0) { + return -1; + } + + return 0; +} + +int Http2Handler::tls_handshake() { + ev_io_stop(sessions_->get_loop(), &wev_); + + ERR_clear_error(); + + auto rv = SSL_do_handshake(ssl_); + + if (rv == 0) { + return -1; + } + + if (rv < 0) { + auto err = SSL_get_error(ssl_, rv); + switch (err) { + case SSL_ERROR_WANT_READ: + return 0; + case SSL_ERROR_WANT_WRITE: + ev_io_start(sessions_->get_loop(), &wev_); + return 0; + default: + return -1; + } + } + + if (sessions_->get_config()->verbose) { + std::cerr << "SSL/TLS handshake completed" << std::endl; + } + + if (verify_npn_result() != 0) { + return -1; + } + + read_ = &Http2Handler::read_tls; + write_ = &Http2Handler::write_tls; + + if (connection_made() != 0) { + return -1; + } + + return 0; +} + +int Http2Handler::read_tls() { + std::array buf; + + ERR_clear_error(); + + for (;;) { + auto rv = SSL_read(ssl_, buf.data(), buf.size()); + + if (rv == 0) { + return -1; + } + + if (rv < 0) { + auto err = SSL_get_error(ssl_, rv); + switch (err) { + case SSL_ERROR_WANT_READ: + goto fin; + case SSL_ERROR_WANT_WRITE: + // renegotiation started + return -1; + default: + return -1; + } + } + + auto nread = rv; + + if (get_config()->hexdump) { + util::hexdump(stdout, buf.data(), nread); + } + + rv = nghttp2_session_mem_recv(session_, buf.data(), nread); + if (rv < 0) { + if (rv != NGHTTP2_ERR_BAD_CLIENT_MAGIC) { + std::cerr << "nghttp2_session_mem_recv() returned error: " + << nghttp2_strerror(rv) << std::endl; + } + return -1; + } + } + +fin: + return write_(*this); +} + +int Http2Handler::write_tls() { + auto loop = sessions_->get_loop(); + + ERR_clear_error(); + + for (;;) { + if (wb_.rleft() > 0) { + auto rv = SSL_write(ssl_, wb_.pos, wb_.rleft()); + + if (rv == 0) { + return -1; + } + + if (rv < 0) { + auto err = SSL_get_error(ssl_, rv); + switch (err) { + case SSL_ERROR_WANT_READ: + // renegotiation started + return -1; + case SSL_ERROR_WANT_WRITE: + ev_io_start(sessions_->get_loop(), &wev_); + return 0; + default: + return -1; + } + } + + wb_.drain(rv); + continue; + } + wb_.reset(); + if (fill_wb() != 0) { + return -1; + } + if (wb_.rleft() == 0) { + break; + } + } + + if (wb_.rleft() == 0) { + ev_io_stop(loop, &wev_); + } else { + ev_io_start(loop, &wev_); + } + + if (nghttp2_session_want_read(session_) == 0 && + nghttp2_session_want_write(session_) == 0 && wb_.rleft() == 0) { + return -1; + } + + return 0; +} + +int Http2Handler::on_read() { return read_(*this); } + +int Http2Handler::on_write() { return write_(*this); } + +int Http2Handler::connection_made() { + int r; + + r = nghttp2_session_server_new(&session_, sessions_->get_callbacks(), this); + + if (r != 0) { + return r; + } + + auto config = sessions_->get_config(); + std::array entry; + size_t niv = 1; + + entry[0].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS; + entry[0].value = config->max_concurrent_streams; + + if (config->header_table_size >= 0) { + entry[niv].settings_id = NGHTTP2_SETTINGS_HEADER_TABLE_SIZE; + entry[niv].value = config->header_table_size; + ++niv; + } + r = nghttp2_submit_settings(session_, NGHTTP2_FLAG_NONE, entry.data(), niv); + if (r != 0) { + return r; + } + + ev_timer_start(sessions_->get_loop(), &settings_timerev_); + + return on_write(); +} + +int Http2Handler::verify_npn_result() { + const unsigned char *next_proto = nullptr; + unsigned int next_proto_len; + // Check the negotiated protocol in NPN or ALPN + SSL_get0_next_proto_negotiated(ssl_, &next_proto, &next_proto_len); + for (int i = 0; i < 2; ++i) { + if (next_proto) { + if (sessions_->get_config()->verbose) { + std::string proto(next_proto, next_proto + next_proto_len); + std::cout << "The negotiated protocol: " << proto << std::endl; + } + if (util::check_h2_is_selected(next_proto, next_proto_len)) { + return 0; + } + break; + } else { +#if OPENSSL_VERSION_NUMBER >= 0x10002000L + SSL_get0_alpn_selected(ssl_, &next_proto, &next_proto_len); +#else // OPENSSL_VERSION_NUMBER < 0x10002000L + break; +#endif // OPENSSL_VERSION_NUMBER < 0x10002000L + } + } + if (sessions_->get_config()->verbose) { + std::cerr << "Client did not advertise HTTP/2 protocol." + << " (nghttp2 expects " << NGHTTP2_PROTO_VERSION_ID << ")" + << std::endl; + } + return -1; +} + +int Http2Handler::submit_file_response(const std::string &status, + Stream *stream, time_t last_modified, + off_t file_length, + nghttp2_data_provider *data_prd) { + std::string content_length = util::utos(file_length); + std::string last_modified_str; + auto nva = make_array(http2::make_nv_ls(":status", status), + http2::make_nv_ls("server", NGHTTPD_SERVER), + http2::make_nv_ls("content-length", content_length), + http2::make_nv_ll("cache-control", "max-age=3600"), + http2::make_nv_ls("date", sessions_->get_cached_date()), + http2::make_nv_ll("", ""), http2::make_nv_ll("", "")); + size_t nvlen = 5; + if (last_modified != 0) { + last_modified_str = util::http_date(last_modified); + nva[nvlen++] = http2::make_nv_ls("last-modified", last_modified_str); + } + auto &trailer = get_config()->trailer; + std::string trailer_names; + if (!trailer.empty()) { + trailer_names = trailer[0].name; + for (size_t i = 1; i < trailer.size(); ++i) { + trailer_names += ", "; + trailer_names += trailer[i].name; + } + nva[nvlen++] = http2::make_nv_ls("trailer", trailer_names); + } + return nghttp2_submit_response(session_, stream->stream_id, nva.data(), nvlen, + data_prd); +} + +int Http2Handler::submit_response(const std::string &status, int32_t stream_id, + const Headers &headers, + nghttp2_data_provider *data_prd) { + auto nva = std::vector(); + nva.reserve(3 + headers.size()); + nva.push_back(http2::make_nv_ls(":status", status)); + nva.push_back(http2::make_nv_ls("server", NGHTTPD_SERVER)); + nva.push_back(http2::make_nv_ls("date", sessions_->get_cached_date())); + for (auto &nv : headers) { + nva.push_back(http2::make_nv(nv.name, nv.value, nv.no_index)); + } + int r = nghttp2_submit_response(session_, stream_id, nva.data(), nva.size(), + data_prd); + return r; +} + +int Http2Handler::submit_response(const std::string &status, int32_t stream_id, + nghttp2_data_provider *data_prd) { + auto nva = make_array(http2::make_nv_ls(":status", status), + http2::make_nv_ls("server", NGHTTPD_SERVER)); + return nghttp2_submit_response(session_, stream_id, nva.data(), nva.size(), + data_prd); +} + +int Http2Handler::submit_non_final_response(const std::string &status, + int32_t stream_id) { + auto nva = make_array(http2::make_nv_ls(":status", status)); + return nghttp2_submit_headers(session_, NGHTTP2_FLAG_NONE, stream_id, nullptr, + nva.data(), nva.size(), nullptr); +} + +int Http2Handler::submit_push_promise(Stream *stream, + const std::string &push_path) { + auto authority = + http2::get_header(stream->hdidx, http2::HD__AUTHORITY, stream->headers); + + if (!authority) { + authority = + http2::get_header(stream->hdidx, http2::HD_HOST, stream->headers); + } + + auto nva = + make_array(http2::make_nv_ll(":method", "GET"), + http2::make_nv_ls(":path", push_path), + get_config()->no_tls ? http2::make_nv_ll(":scheme", "http") + : http2::make_nv_ll(":scheme", "https"), + http2::make_nv_ls(":authority", authority->value)); + + auto promised_stream_id = nghttp2_submit_push_promise( + session_, NGHTTP2_FLAG_END_HEADERS, stream->stream_id, nva.data(), + nva.size(), nullptr); + + if (promised_stream_id < 0) { + return promised_stream_id; + } + + auto promised_stream = make_unique(this, promised_stream_id); + + append_nv(promised_stream.get(), nva); + add_stream(promised_stream_id, std::move(promised_stream)); + + return 0; +} + +int Http2Handler::submit_rst_stream(Stream *stream, uint32_t error_code) { + remove_stream_read_timeout(stream); + remove_stream_write_timeout(stream); + + return nghttp2_submit_rst_stream(session_, NGHTTP2_FLAG_NONE, + stream->stream_id, error_code); +} + +void Http2Handler::add_stream(int32_t stream_id, + std::unique_ptr stream) { + id2stream_[stream_id] = std::move(stream); +} + +void Http2Handler::remove_stream(int32_t stream_id) { + id2stream_.erase(stream_id); +} + +Stream *Http2Handler::get_stream(int32_t stream_id) { + auto itr = id2stream_.find(stream_id); + if (itr == std::end(id2stream_)) { + return nullptr; + } else { + return (*itr).second.get(); + } +} + +int64_t Http2Handler::session_id() const { return session_id_; } + +Sessions *Http2Handler::get_sessions() const { return sessions_; } + +const Config *Http2Handler::get_config() const { + return sessions_->get_config(); +} + +void Http2Handler::remove_settings_timer() { + ev_timer_stop(sessions_->get_loop(), &settings_timerev_); +} + +void Http2Handler::terminate_session(uint32_t error_code) { + nghttp2_session_terminate_session(session_, error_code); +} + +ssize_t file_read_callback(nghttp2_session *session, int32_t stream_id, + uint8_t *buf, size_t length, uint32_t *data_flags, + nghttp2_data_source *source, void *user_data) { + int rv; + auto hd = static_cast(user_data); + auto stream = hd->get_stream(stream_id); + + auto nread = std::min(stream->body_length - stream->body_offset, + static_cast(length)); + + *data_flags |= NGHTTP2_DATA_FLAG_NO_COPY; + + if (nread == 0 || stream->body_length == stream->body_offset + nread) { + *data_flags |= NGHTTP2_DATA_FLAG_EOF; + + auto config = hd->get_config(); + if (!config->trailer.empty()) { + std::vector nva; + nva.reserve(config->trailer.size()); + for (auto &kv : config->trailer) { + nva.push_back(http2::make_nv(kv.name, kv.value, kv.no_index)); + } + rv = nghttp2_submit_trailer(session, stream_id, nva.data(), nva.size()); + if (rv != 0) { + if (nghttp2_is_fatal(rv)) { + return NGHTTP2_ERR_CALLBACK_FAILURE; + } + } else { + *data_flags |= NGHTTP2_DATA_FLAG_NO_END_STREAM; + } + } + + if (nghttp2_session_get_stream_remote_close(session, stream_id) == 0) { + remove_stream_read_timeout(stream); + remove_stream_write_timeout(stream); + + hd->submit_rst_stream(stream, NGHTTP2_NO_ERROR); + } + } + + return nread; +} + +namespace { +void prepare_status_response(Stream *stream, Http2Handler *hd, int status) { + auto sessions = hd->get_sessions(); + auto status_page = sessions->get_server()->get_status_page(status); + auto file_ent = &status_page->file_ent; + + // we don't set stream->file_ent since we don't want to expire it. + stream->body_length = file_ent->length; + nghttp2_data_provider data_prd; + data_prd.source.fd = file_ent->fd; + data_prd.read_callback = file_read_callback; + + Headers headers; + headers.emplace_back("content-type", "text/html; charset=UTF-8"); + hd->submit_response(status_page->status, stream->stream_id, headers, + &data_prd); +} +} // namespace + +namespace { +void prepare_echo_response(Stream *stream, Http2Handler *hd) { + auto length = lseek(stream->file_ent->fd, 0, SEEK_END); + if (length == -1) { + hd->submit_rst_stream(stream, NGHTTP2_INTERNAL_ERROR); + return; + } + stream->body_length = length; + if (lseek(stream->file_ent->fd, 0, SEEK_SET) == -1) { + hd->submit_rst_stream(stream, NGHTTP2_INTERNAL_ERROR); + return; + } + nghttp2_data_provider data_prd; + data_prd.source.fd = stream->file_ent->fd; + data_prd.read_callback = file_read_callback; + + Headers headers; + headers.emplace_back("nghttpd-response", "echo"); + headers.emplace_back("content-length", util::utos(length)); + + hd->submit_response("200", stream->stream_id, headers, &data_prd); +} +} // namespace + +namespace { +bool prepare_upload_temp_store(Stream *stream, Http2Handler *hd) { + auto sessions = hd->get_sessions(); + + char tempfn[] = "/tmp/nghttpd.temp.XXXXXX"; + auto fd = mkstemp(tempfn); + if (fd == -1) { + return false; + } + unlink(tempfn); + // Ordinary request never start with "echo:". The length is 0 for + // now. We will update it when we get whole request body. + stream->file_ent = sessions->cache_fd(std::string("echo:") + tempfn, + FileEntry(tempfn, 0, 0, fd)); + stream->echo_upload = true; + return true; +} +} // namespace + +namespace { +void prepare_redirect_response(Stream *stream, Http2Handler *hd, + const std::string &path, int status) { + auto scheme = + http2::get_header(stream->hdidx, http2::HD__SCHEME, stream->headers); + auto authority = + http2::get_header(stream->hdidx, http2::HD__AUTHORITY, stream->headers); + if (!authority) { + authority = + http2::get_header(stream->hdidx, http2::HD_HOST, stream->headers); + } + + auto redirect_url = scheme->value; + redirect_url += "://"; + redirect_url += authority->value; + redirect_url += path; + + auto headers = Headers{{"location", redirect_url}}; + + auto sessions = hd->get_sessions(); + auto status_page = sessions->get_server()->get_status_page(status); + + hd->submit_response(status_page->status, stream->stream_id, headers, nullptr); +} +} // namespace + +namespace { +void prepare_response(Stream *stream, Http2Handler *hd, + bool allow_push = true) { + int rv; + auto reqpath = + http2::get_header(stream->hdidx, http2::HD__PATH, stream->headers)->value; + auto ims = + get_header(stream->hdidx, http2::HD_IF_MODIFIED_SINCE, stream->headers); + + time_t last_mod = 0; + bool last_mod_found = false; + if (ims) { + last_mod_found = true; + last_mod = util::parse_http_date(ims->value); + } + auto query_pos = reqpath.find("?"); + std::string url; + if (query_pos != std::string::npos) { + // Do not response to this request to allow clients to test timeouts. + if (reqpath.find("nghttpd_do_not_respond_to_req=yes", query_pos) != + std::string::npos) { + return; + } + url = reqpath.substr(0, query_pos); + } else { + url = reqpath; + } + + auto sessions = hd->get_sessions(); + + url = util::percentDecode(std::begin(url), std::end(url)); + if (!util::check_path(url)) { + if (stream->file_ent) { + sessions->release_fd(stream->file_ent->path); + stream->file_ent = nullptr; + } + prepare_status_response(stream, hd, 404); + return; + } + auto push_itr = hd->get_config()->push.find(url); + if (allow_push && push_itr != std::end(hd->get_config()->push)) { + for (auto &push_path : (*push_itr).second) { + rv = hd->submit_push_promise(stream, push_path); + if (rv != 0) { + std::cerr << "nghttp2_submit_push_promise() returned error: " + << nghttp2_strerror(rv) << std::endl; + } + } + } + + std::string path = hd->get_config()->htdocs + url; + if (path[path.size() - 1] == '/') { + path += DEFAULT_HTML; + } + + if (stream->echo_upload) { + assert(stream->file_ent); + prepare_echo_response(stream, hd); + return; + } + + auto file_ent = sessions->get_cached_fd(path); + + if (file_ent == nullptr) { + int file = open(path.c_str(), O_RDONLY | O_BINARY); + if (file == -1) { + prepare_status_response(stream, hd, 404); + + return; + } + + struct stat buf; + + if (fstat(file, &buf) == -1) { + close(file); + prepare_status_response(stream, hd, 404); + + return; + } + + if (buf.st_mode & S_IFDIR) { + close(file); + + if (query_pos == std::string::npos) { + reqpath += "/"; + } else { + reqpath.insert(query_pos, "/"); + } + + prepare_redirect_response(stream, hd, reqpath, 301); + + return; + } + + if (last_mod_found && buf.st_mtime <= last_mod) { + close(file); + prepare_status_response(stream, hd, 304); + + return; + } + + file_ent = sessions->cache_fd( + path, FileEntry(path, buf.st_size, buf.st_mtime, file)); + } else if (last_mod_found && file_ent->mtime <= last_mod) { + sessions->release_fd(file_ent->path); + prepare_status_response(stream, hd, 304); + + return; + } + + stream->file_ent = file_ent; + stream->body_length = file_ent->length; + + nghttp2_data_provider data_prd; + + data_prd.source.fd = file_ent->fd; + data_prd.read_callback = file_read_callback; + + hd->submit_file_response("200", stream, file_ent->mtime, file_ent->length, + &data_prd); +} +} // namespace + +namespace { +int on_header_callback(nghttp2_session *session, const nghttp2_frame *frame, + const uint8_t *name, size_t namelen, + const uint8_t *value, size_t valuelen, uint8_t flags, + void *user_data) { + auto hd = static_cast(user_data); + if (hd->get_config()->verbose) { + print_session_id(hd->session_id()); + verbose_on_header_callback(session, frame, name, namelen, value, valuelen, + flags, user_data); + } + if (frame->hd.type != NGHTTP2_HEADERS || + frame->headers.cat != NGHTTP2_HCAT_REQUEST) { + return 0; + } + auto stream = hd->get_stream(frame->hd.stream_id); + if (!stream) { + return 0; + } + + auto token = http2::lookup_token(name, namelen); + + http2::index_header(stream->hdidx, token, stream->headers.size()); + http2::add_header(stream->headers, name, namelen, value, valuelen, + flags & NGHTTP2_NV_FLAG_NO_INDEX, token); + return 0; +} +} // namespace + +namespace { +int on_begin_headers_callback(nghttp2_session *session, + const nghttp2_frame *frame, void *user_data) { + auto hd = static_cast(user_data); + + if (frame->hd.type != NGHTTP2_HEADERS || + frame->headers.cat != NGHTTP2_HCAT_REQUEST) { + return 0; + } + + auto stream = make_unique(hd, frame->hd.stream_id); + + add_stream_read_timeout(stream.get()); + + hd->add_stream(frame->hd.stream_id, std::move(stream)); + + return 0; +} +} // namespace + +namespace { +int hd_on_frame_recv_callback(nghttp2_session *session, + const nghttp2_frame *frame, void *user_data) { + auto hd = static_cast(user_data); + if (hd->get_config()->verbose) { + print_session_id(hd->session_id()); + verbose_on_frame_recv_callback(session, frame, user_data); + } + switch (frame->hd.type) { + case NGHTTP2_DATA: { + // TODO Handle POST + auto stream = hd->get_stream(frame->hd.stream_id); + if (!stream) { + return 0; + } + + if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) { + remove_stream_read_timeout(stream); + if (stream->echo_upload || !hd->get_config()->early_response) { + prepare_response(stream, hd); + } + } else { + add_stream_read_timeout(stream); + } + + break; + } + case NGHTTP2_HEADERS: { + auto stream = hd->get_stream(frame->hd.stream_id); + if (!stream) { + return 0; + } + + if (frame->headers.cat == NGHTTP2_HCAT_REQUEST) { + + auto expect100 = + http2::get_header(stream->hdidx, http2::HD_EXPECT, stream->headers); + + if (expect100 && util::strieq_l("100-continue", expect100->value)) { + hd->submit_non_final_response("100", frame->hd.stream_id); + } + + auto &method = http2::get_header(stream->hdidx, http2::HD__METHOD, + stream->headers)->value; + if (hd->get_config()->echo_upload && + (method == "POST" || method == "PUT")) { + if (!prepare_upload_temp_store(stream, hd)) { + hd->submit_rst_stream(stream, NGHTTP2_INTERNAL_ERROR); + return 0; + } + } else if (hd->get_config()->early_response) { + prepare_response(stream, hd); + } + } + + if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) { + remove_stream_read_timeout(stream); + if (stream->echo_upload || !hd->get_config()->early_response) { + prepare_response(stream, hd); + } + } else { + add_stream_read_timeout(stream); + } + + break; + } + case NGHTTP2_SETTINGS: + if (frame->hd.flags & NGHTTP2_FLAG_ACK) { + hd->remove_settings_timer(); + } + break; + default: + break; + } + return 0; +} +} // namespace + +namespace { +int hd_on_frame_send_callback(nghttp2_session *session, + const nghttp2_frame *frame, void *user_data) { + auto hd = static_cast(user_data); + + if (hd->get_config()->verbose) { + print_session_id(hd->session_id()); + verbose_on_frame_send_callback(session, frame, user_data); + } + + switch (frame->hd.type) { + case NGHTTP2_DATA: + case NGHTTP2_HEADERS: { + auto stream = hd->get_stream(frame->hd.stream_id); + + if (!stream) { + return 0; + } + + if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) { + remove_stream_write_timeout(stream); + } else if (std::min(nghttp2_session_get_stream_remote_window_size( + session, frame->hd.stream_id), + nghttp2_session_get_remote_window_size(session)) <= 0) { + // If stream is blocked by flow control, enable write timeout. + add_stream_read_timeout_if_pending(stream); + add_stream_write_timeout(stream); + } else { + add_stream_read_timeout_if_pending(stream); + remove_stream_write_timeout(stream); + } + + break; + } + case NGHTTP2_PUSH_PROMISE: { + auto promised_stream_id = frame->push_promise.promised_stream_id; + auto promised_stream = hd->get_stream(promised_stream_id); + auto stream = hd->get_stream(frame->hd.stream_id); + + if (!stream || !promised_stream) { + return 0; + } + + add_stream_read_timeout_if_pending(stream); + add_stream_write_timeout(stream); + + prepare_response(promised_stream, hd, /*allow_push */ false); + } + } + return 0; +} +} // namespace + +namespace { +int send_data_callback(nghttp2_session *session, nghttp2_frame *frame, + const uint8_t *framehd, size_t length, + nghttp2_data_source *source, void *user_data) { + auto hd = static_cast(user_data); + auto wb = hd->get_wb(); + auto padlen = frame->data.padlen; + auto stream = hd->get_stream(frame->hd.stream_id); + + if (wb->wleft() < 9 + length + padlen) { + return NGHTTP2_ERR_WOULDBLOCK; + } + + int fd = source->fd; + + auto p = wb->last; + + p = std::copy_n(framehd, 9, p); + + if (padlen) { + *p++ = padlen - 1; + } + + while (length) { + ssize_t nread; + while ((nread = pread(fd, p, length, stream->body_offset)) == -1 && + errno == EINTR) + ; + + if (nread == -1) { + remove_stream_read_timeout(stream); + remove_stream_write_timeout(stream); + + return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; + } + + stream->body_offset += nread; + length -= nread; + p += nread; + } + + if (padlen) { + std::fill(p, p + padlen - 1, 0); + p += padlen - 1; + } + + wb->last = p; + + return 0; +} +} // namespace + +namespace { +ssize_t select_padding_callback(nghttp2_session *session, + const nghttp2_frame *frame, size_t max_payload, + void *user_data) { + auto hd = static_cast(user_data); + return std::min(max_payload, frame->hd.length + hd->get_config()->padding); +} +} // namespace + +namespace { +int on_data_chunk_recv_callback(nghttp2_session *session, uint8_t flags, + int32_t stream_id, const uint8_t *data, + size_t len, void *user_data) { + auto hd = static_cast(user_data); + auto stream = hd->get_stream(stream_id); + + if (!stream) { + return 0; + } + + if (stream->echo_upload) { + assert(stream->file_ent); + while (len) { + ssize_t n; + while ((n = write(stream->file_ent->fd, data, len)) == -1 && + errno == EINTR) + ; + if (n == -1) { + hd->submit_rst_stream(stream, NGHTTP2_INTERNAL_ERROR); + return 0; + } + len -= n; + data += n; + } + } + // TODO Handle POST + + add_stream_read_timeout(stream); + + return 0; +} +} // namespace + +namespace { +int on_stream_close_callback(nghttp2_session *session, int32_t stream_id, + uint32_t error_code, void *user_data) { + auto hd = static_cast(user_data); + hd->remove_stream(stream_id); + if (hd->get_config()->verbose) { + print_session_id(hd->session_id()); + print_timer(); + printf(" stream_id=%d closed\n", stream_id); + fflush(stdout); + } + return 0; +} +} // namespace + +namespace { +void fill_callback(nghttp2_session_callbacks *callbacks, const Config *config) { + nghttp2_session_callbacks_set_on_stream_close_callback( + callbacks, on_stream_close_callback); + + nghttp2_session_callbacks_set_on_frame_recv_callback( + callbacks, hd_on_frame_recv_callback); + + nghttp2_session_callbacks_set_on_frame_send_callback( + callbacks, hd_on_frame_send_callback); + + if (config->verbose) { + nghttp2_session_callbacks_set_on_invalid_frame_recv_callback( + callbacks, verbose_on_invalid_frame_recv_callback); + } + + nghttp2_session_callbacks_set_on_data_chunk_recv_callback( + callbacks, on_data_chunk_recv_callback); + + nghttp2_session_callbacks_set_on_header_callback(callbacks, + on_header_callback); + + nghttp2_session_callbacks_set_on_begin_headers_callback( + callbacks, on_begin_headers_callback); + + nghttp2_session_callbacks_set_send_data_callback(callbacks, + send_data_callback); + + if (config->padding) { + nghttp2_session_callbacks_set_select_padding_callback( + callbacks, select_padding_callback); + } +} +} // namespace + +struct ClientInfo { + int fd; +}; + +struct Worker { + std::unique_ptr sessions; + ev_async w; + // protectes q + std::mutex m; + std::deque q; +}; + +namespace { +void worker_acceptcb(struct ev_loop *loop, ev_async *w, int revents) { + auto worker = static_cast(w->data); + auto &sessions = worker->sessions; + + std::deque q; + { + std::lock_guard lock(worker->m); + q.swap(worker->q); + } + + for (auto c : q) { + sessions->accept_connection(c.fd); + } +} +} // namespace + +namespace { +void run_worker(Worker *worker) { + auto loop = worker->sessions->get_loop(); + + ev_run(loop, 0); +} +} // namespace + +class AcceptHandler { +public: + AcceptHandler(HttpServer *sv, Sessions *sessions, const Config *config) + : sessions_(sessions), config_(config), next_worker_(0) { + if (config_->num_worker == 1) { + return; + } + for (size_t i = 0; i < config_->num_worker; ++i) { + if (config_->verbose) { + std::cerr << "spawning thread #" << i << std::endl; + } + auto worker = make_unique(); + auto loop = ev_loop_new(0); + worker->sessions = + make_unique(sv, loop, config_, sessions_->get_ssl_ctx()); + ev_async_init(&worker->w, worker_acceptcb); + worker->w.data = worker.get(); + ev_async_start(loop, &worker->w); + + auto t = std::thread(run_worker, worker.get()); + t.detach(); + workers_.push_back(std::move(worker)); + } + } + void accept_connection(int fd) { + if (config_->num_worker == 1) { + sessions_->accept_connection(fd); + return; + } + + // Dispatch client to the one of the worker threads, in a round + // robin manner. + auto &worker = workers_[next_worker_]; + if (next_worker_ == config_->num_worker - 1) { + next_worker_ = 0; + } else { + ++next_worker_; + } + { + std::lock_guard lock(worker->m); + worker->q.push_back({fd}); + } + ev_async_send(worker->sessions->get_loop(), &worker->w); + } + +private: + std::vector> workers_; + Sessions *sessions_; + const Config *config_; + // In multi threading mode, this points to the next thread that + // client will be dispatched. + size_t next_worker_; +}; + +namespace { +void acceptcb(struct ev_loop *loop, ev_io *w, int revents); +} // namespace + +class ListenEventHandler { +public: + ListenEventHandler(Sessions *sessions, int fd, + std::shared_ptr acceptor) + : acceptor_(acceptor), sessions_(sessions), fd_(fd) { + ev_io_init(&w_, acceptcb, fd, EV_READ); + w_.data = this; + ev_io_start(sessions_->get_loop(), &w_); + } + void accept_connection() { + for (;;) { +#ifdef HAVE_ACCEPT4 + auto fd = accept4(fd_, nullptr, nullptr, SOCK_NONBLOCK); +#else // !HAVE_ACCEPT4 + auto fd = accept(fd_, nullptr, nullptr); +#endif // !HAVE_ACCEPT4 + if (fd == -1) { + break; + } +#ifndef HAVE_ACCEPT4 + util::make_socket_nonblocking(fd); +#endif // !HAVE_ACCEPT4 + acceptor_->accept_connection(fd); + } + } + +private: + ev_io w_; + std::shared_ptr acceptor_; + Sessions *sessions_; + int fd_; +}; + +namespace { +void acceptcb(struct ev_loop *loop, ev_io *w, int revents) { + auto handler = static_cast(w->data); + handler->accept_connection(); +} +} // namespace + +namespace { +FileEntry make_status_body(int status, uint16_t port) { + std::string body; + body = ""; + body += http2::get_status_string(status); + body += "

        "; + body += http2::get_status_string(status); + body += "


        "; + body += NGHTTPD_SERVER; + body += " at port "; + body += util::utos(port); + body += "
        "; + body += ""; + + char tempfn[] = "/tmp/nghttpd.temp.XXXXXX"; + int fd = mkstemp(tempfn); + if (fd == -1) { + auto error = errno; + std::cerr << "Could not open status response body file: errno=" << error; + assert(0); + } + unlink(tempfn); + ssize_t nwrite; + while ((nwrite = write(fd, body.c_str(), body.size())) == -1 && + errno == EINTR) + ; + if (nwrite == -1) { + auto error = errno; + std::cerr << "Could not write status response body into file: errno=" + << error; + assert(0); + } + + return FileEntry(util::utos(status), nwrite, 0, fd); +} +} // namespace + +// index into HttpServer::status_pages_ +enum { + IDX_200, + IDX_301, + IDX_304, + IDX_400, + IDX_404, +}; + +HttpServer::HttpServer(const Config *config) : config_(config) { + status_pages_ = std::vector{ + {"200", make_status_body(200, config_->port)}, + {"301", make_status_body(301, config_->port)}, + {"304", make_status_body(304, config_->port)}, + {"400", make_status_body(400, config_->port)}, + {"404", make_status_body(404, config_->port)}, + }; +} + +namespace { +int next_proto_cb(SSL *s, const unsigned char **data, unsigned int *len, + void *arg) { + auto next_proto = static_cast *>(arg); + *data = next_proto->data(); + *len = next_proto->size(); + return SSL_TLSEXT_ERR_OK; +} +} // namespace + +namespace { +int verify_callback(int preverify_ok, X509_STORE_CTX *ctx) { + // We don't verify the client certificate. Just request it for the + // testing purpose. + return 1; +} +} // namespace + +namespace { +int start_listen(HttpServer *sv, struct ev_loop *loop, Sessions *sessions, + const Config *config) { + addrinfo hints; + int r; + bool ok = false; + const char *addr = nullptr; + + auto acceptor = std::make_shared(sv, sessions, config); + auto service = util::utos(config->port); + + memset(&hints, 0, sizeof(addrinfo)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_PASSIVE; +#ifdef AI_ADDRCONFIG + hints.ai_flags |= AI_ADDRCONFIG; +#endif // AI_ADDRCONFIG + + if (!config->address.empty()) { + addr = config->address.c_str(); + } + + addrinfo *res, *rp; + r = getaddrinfo(addr, service.c_str(), &hints, &res); + if (r != 0) { + std::cerr << "getaddrinfo() failed: " << gai_strerror(r) << std::endl; + return -1; + } + + for (rp = res; rp; rp = rp->ai_next) { + int fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); + if (fd == -1) { + continue; + } + int val = 1; + if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &val, + static_cast(sizeof(val))) == -1) { + close(fd); + continue; + } + (void)util::make_socket_nonblocking(fd); +#ifdef IPV6_V6ONLY + if (rp->ai_family == AF_INET6) { + if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &val, + static_cast(sizeof(val))) == -1) { + close(fd); + continue; + } + } +#endif // IPV6_V6ONLY + if (bind(fd, rp->ai_addr, rp->ai_addrlen) == 0 && listen(fd, 1000) == 0) { + new ListenEventHandler(sessions, fd, acceptor); + + if (config->verbose) { + std::string s = util::numeric_name(rp->ai_addr, rp->ai_addrlen); + std::cout << (rp->ai_family == AF_INET ? "IPv4" : "IPv6") << ": listen " + << s << ":" << config->port << std::endl; + } + ok = true; + continue; + } else { + std::cerr << strerror(errno) << std::endl; + } + close(fd); + } + freeaddrinfo(res); + + if (!ok) { + return -1; + } + return 0; +} +} // namespace + +#if OPENSSL_VERSION_NUMBER >= 0x10002000L +namespace { +int alpn_select_proto_cb(SSL *ssl, const unsigned char **out, + unsigned char *outlen, const unsigned char *in, + unsigned int inlen, void *arg) { + auto config = static_cast(arg)->get_config(); + if (config->verbose) { + std::cout << "[ALPN] client offers:" << std::endl; + } + if (config->verbose) { + for (unsigned int i = 0; i < inlen; i += in [i] + 1) { + std::cout << " * "; + std::cout.write(reinterpret_cast(&in[i + 1]), in[i]); + std::cout << std::endl; + } + } + if (!util::select_h2(out, outlen, in, inlen)) { + return SSL_TLSEXT_ERR_NOACK; + } + return SSL_TLSEXT_ERR_OK; +} +} // namespace +#endif // OPENSSL_VERSION_NUMBER >= 0x10002000L + +int HttpServer::run() { + SSL_CTX *ssl_ctx = nullptr; + std::vector next_proto; + + if (!config_->no_tls) { + ssl_ctx = SSL_CTX_new(SSLv23_server_method()); + if (!ssl_ctx) { + std::cerr << ERR_error_string(ERR_get_error(), nullptr) << std::endl; + return -1; + } + + SSL_CTX_set_options(ssl_ctx, + SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | + SSL_OP_NO_COMPRESSION | + SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION | + SSL_OP_SINGLE_ECDH_USE | SSL_OP_NO_TICKET | + SSL_OP_CIPHER_SERVER_PREFERENCE); + SSL_CTX_set_mode(ssl_ctx, SSL_MODE_AUTO_RETRY); + SSL_CTX_set_mode(ssl_ctx, SSL_MODE_RELEASE_BUFFERS); + + if (SSL_CTX_set_cipher_list(ssl_ctx, ssl::DEFAULT_CIPHER_LIST) == 0) { + std::cerr << ERR_error_string(ERR_get_error(), nullptr) << std::endl; + return -1; + } + + const unsigned char sid_ctx[] = "nghttpd"; + SSL_CTX_set_session_id_context(ssl_ctx, sid_ctx, sizeof(sid_ctx) - 1); + SSL_CTX_set_session_cache_mode(ssl_ctx, SSL_SESS_CACHE_SERVER); + +#ifndef OPENSSL_NO_EC + + // Disabled SSL_CTX_set_ecdh_auto, because computational cost of + // chosen curve is much higher than P-256. + + // #if OPENSSL_VERSION_NUMBER >= 0x10002000L + // SSL_CTX_set_ecdh_auto(ssl_ctx, 1); + // #else // OPENSSL_VERSION_NUBMER < 0x10002000L + // Use P-256, which is sufficiently secure at the time of this + // writing. + auto ecdh = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1); + if (ecdh == nullptr) { + std::cerr << "EC_KEY_new_by_curv_name failed: " + << ERR_error_string(ERR_get_error(), nullptr); + return -1; + } + SSL_CTX_set_tmp_ecdh(ssl_ctx, ecdh); + EC_KEY_free(ecdh); +// #endif // OPENSSL_VERSION_NUBMER < 0x10002000L + +#endif // OPENSSL_NO_EC + + if (!config_->dh_param_file.empty()) { + // Read DH parameters from file + auto bio = BIO_new_file(config_->dh_param_file.c_str(), "r"); + if (bio == nullptr) { + std::cerr << "BIO_new_file() failed: " + << ERR_error_string(ERR_get_error(), nullptr) << std::endl; + return -1; + } + + auto dh = PEM_read_bio_DHparams(bio, nullptr, nullptr, nullptr); + + if (dh == nullptr) { + std::cerr << "PEM_read_bio_DHparams() failed: " + << ERR_error_string(ERR_get_error(), nullptr) << std::endl; + return -1; + } + + SSL_CTX_set_tmp_dh(ssl_ctx, dh); + DH_free(dh); + BIO_free(bio); + } + + if (SSL_CTX_use_PrivateKey_file(ssl_ctx, config_->private_key_file.c_str(), + SSL_FILETYPE_PEM) != 1) { + std::cerr << "SSL_CTX_use_PrivateKey_file failed." << std::endl; + return -1; + } + if (SSL_CTX_use_certificate_chain_file(ssl_ctx, + config_->cert_file.c_str()) != 1) { + std::cerr << "SSL_CTX_use_certificate_file failed." << std::endl; + return -1; + } + if (SSL_CTX_check_private_key(ssl_ctx) != 1) { + std::cerr << "SSL_CTX_check_private_key failed." << std::endl; + return -1; + } + if (config_->verify_client) { + SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE | + SSL_VERIFY_FAIL_IF_NO_PEER_CERT, + verify_callback); + } + + next_proto = util::get_default_alpn(); + + SSL_CTX_set_next_protos_advertised_cb(ssl_ctx, next_proto_cb, &next_proto); +#if OPENSSL_VERSION_NUMBER >= 0x10002000L + // ALPN selection callback + SSL_CTX_set_alpn_select_cb(ssl_ctx, alpn_select_proto_cb, this); +#endif // OPENSSL_VERSION_NUMBER >= 0x10002000L + } + + auto loop = EV_DEFAULT; + + Sessions sessions(this, loop, config_, ssl_ctx); + if (start_listen(this, loop, &sessions, config_) != 0) { + std::cerr << "Could not listen" << std::endl; + return -1; + } + + ev_run(loop, 0); + return 0; +} + +const Config *HttpServer::get_config() const { return config_; } + +const StatusPage *HttpServer::get_status_page(int status) const { + switch (status) { + case 200: + return &status_pages_[IDX_200]; + case 301: + return &status_pages_[IDX_301]; + case 304: + return &status_pages_[IDX_304]; + case 400: + return &status_pages_[IDX_400]; + case 404: + return &status_pages_[IDX_404]; + default: + assert(0); + } + return nullptr; +} + +} // namespace nghttp2 diff --git a/src/HttpServer.h b/src/HttpServer.h new file mode 100644 index 0000000..212f12a --- /dev/null +++ b/src/HttpServer.h @@ -0,0 +1,202 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2013 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef HTTP_SERVER_H +#define HTTP_SERVER_H + +#include "nghttp2_config.h" + +#include + +#include +#include + +#include +#include +#include +#include + +#include + +#include + +#include + +#include "http2.h" +#include "buffer.h" + +namespace nghttp2 { + +struct Config { + std::map> push; + Headers trailer; + std::string htdocs; + std::string host; + std::string private_key_file; + std::string cert_file; + std::string dh_param_file; + std::string address; + ev_tstamp stream_read_timeout; + ev_tstamp stream_write_timeout; + void *data_ptr; + size_t padding; + size_t num_worker; + size_t max_concurrent_streams; + ssize_t header_table_size; + uint16_t port; + bool verbose; + bool daemon; + bool verify_client; + bool no_tls; + bool error_gzip; + bool early_response; + bool hexdump; + bool echo_upload; + Config(); + ~Config(); +}; + +class Http2Handler; + +struct FileEntry { + FileEntry(std::string path, int64_t length, int64_t mtime, int fd) + : path(std::move(path)), length(length), mtime(mtime), dlprev(nullptr), + dlnext(nullptr), fd(fd), usecount(1) {} + std::string path; + int64_t length; + int64_t mtime; + FileEntry *dlprev, *dlnext; + int fd; + int usecount; +}; + +struct Stream { + Headers headers; + Http2Handler *handler; + FileEntry *file_ent; + ev_timer rtimer; + ev_timer wtimer; + int64_t body_length; + int64_t body_offset; + int32_t stream_id; + http2::HeaderIndex hdidx; + bool echo_upload; + Stream(Http2Handler *handler, int32_t stream_id); + ~Stream(); +}; + +class Sessions; + +class Http2Handler { +public: + Http2Handler(Sessions *sessions, int fd, SSL *ssl, int64_t session_id); + ~Http2Handler(); + + void remove_self(); + int setup_bev(); + int on_read(); + int on_write(); + int connection_made(); + int verify_npn_result(); + + int submit_file_response(const std::string &status, Stream *stream, + time_t last_modified, off_t file_length, + nghttp2_data_provider *data_prd); + + int submit_response(const std::string &status, int32_t stream_id, + nghttp2_data_provider *data_prd); + + int submit_response(const std::string &status, int32_t stream_id, + const Headers &headers, nghttp2_data_provider *data_prd); + + int submit_non_final_response(const std::string &status, int32_t stream_id); + + int submit_push_promise(Stream *stream, const std::string &push_path); + + int submit_rst_stream(Stream *stream, uint32_t error_code); + + void add_stream(int32_t stream_id, std::unique_ptr stream); + void remove_stream(int32_t stream_id); + Stream *get_stream(int32_t stream_id); + int64_t session_id() const; + Sessions *get_sessions() const; + const Config *get_config() const; + void remove_settings_timer(); + void terminate_session(uint32_t error_code); + + int fill_wb(); + + int read_clear(); + int write_clear(); + int tls_handshake(); + int read_tls(); + int write_tls(); + + struct ev_loop *get_loop() const; + + using WriteBuf = Buffer<65536>; + + WriteBuf *get_wb(); + +private: + ev_io wev_; + ev_io rev_; + ev_timer settings_timerev_; + std::map> id2stream_; + WriteBuf wb_; + std::function read_, write_; + int64_t session_id_; + nghttp2_session *session_; + Sessions *sessions_; + SSL *ssl_; + const uint8_t *data_pending_; + size_t data_pendinglen_; + int fd_; +}; + +struct StatusPage { + std::string status; + FileEntry file_ent; +}; + +class HttpServer { +public: + HttpServer(const Config *config); + int listen(); + int run(); + const Config *get_config() const; + const StatusPage *get_status_page(int status) const; + +private: + std::vector status_pages_; + const Config *config_; +}; + +ssize_t file_read_callback(nghttp2_session *session, int32_t stream_id, + uint8_t *buf, size_t length, int *eof, + nghttp2_data_source *source, void *user_data); + +} // namespace nghttp2 + +#endif // HTTP_SERVER_H diff --git a/src/Makefile.am b/src/Makefile.am new file mode 100644 index 0000000..b1336ae --- /dev/null +++ b/src/Makefile.am @@ -0,0 +1,216 @@ +# nghttp2 - HTTP/2 C Library + +# Copyright (c) 2012 Tatsuhiro Tsujikawa + +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: + +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. + +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +SUBDIRS = includes + +bin_PROGRAMS = +check_PROGRAMS = +TESTS = + +AM_CFLAGS = $(WARNCFLAGS) +AM_CPPFLAGS = \ + -DPKGDATADIR='"$(pkgdatadir)"' \ + -Wall \ + -I$(top_srcdir)/lib/includes \ + -I$(top_builddir)/lib/includes \ + -I$(top_srcdir)/lib \ + -I$(top_srcdir)/src/includes \ + -I$(top_srcdir)/third-party \ + @LIBSPDYLAY_CFLAGS@ \ + @XML_CPPFLAGS@ \ + @LIBEV_CFLAGS@ \ + @OPENSSL_CFLAGS@ \ + @JANSSON_CFLAGS@ \ + @ZLIB_CFLAGS@ \ + @DEFS@ + +LDADD = $(top_builddir)/lib/libnghttp2.la \ + $(top_builddir)/third-party/libhttp-parser.la \ + @JEMALLOC_LIBS@ \ + @LIBSPDYLAY_LIBS@ \ + @XML_LIBS@ \ + @LIBEV_LIBS@ \ + @OPENSSL_LIBS@ \ + @JANSSON_LIBS@ \ + @ZLIB_LIBS@ \ + @APPLDFLAGS@ + +if ENABLE_APP + +bin_PROGRAMS += nghttp nghttpd nghttpx + +HELPER_OBJECTS = util.cc \ + http2.cc timegm.c app_helper.cc nghttp2_gzip.c +HELPER_HFILES = util.h \ + http2.h timegm.h app_helper.h nghttp2_config.h \ + nghttp2_gzip.h + +HTML_PARSER_OBJECTS = +HTML_PARSER_HFILES = HtmlParser.h + +if HAVE_LIBXML2 +HTML_PARSER_OBJECTS += HtmlParser.cc +endif # HAVE_LIBXML2 + +nghttp_SOURCES = ${HELPER_OBJECTS} ${HELPER_HFILES} nghttp.cc nghttp.h \ + ${HTML_PARSER_OBJECTS} ${HTML_PARSER_HFILES} \ + ssl.cc ssl.h + +nghttpd_SOURCES = ${HELPER_OBJECTS} ${HELPER_HFILES} nghttpd.cc \ + ssl.cc ssl.h \ + HttpServer.cc HttpServer.h + +bin_PROGRAMS += h2load + +h2load_SOURCES = util.cc util.h \ + http2.cc http2.h h2load.cc h2load.h \ + timegm.c timegm.h \ + ssl.cc ssl.h \ + h2load_session.h \ + h2load_http2_session.cc h2load_http2_session.h + +if HAVE_SPDYLAY +h2load_SOURCES += h2load_spdy_session.cc h2load_spdy_session.h +endif # HAVE_SPDYLAY + +NGHTTPX_SRCS = \ + util.cc util.h http2.cc http2.h timegm.c timegm.h base64.h \ + app_helper.cc app_helper.h \ + ssl.cc ssl.h \ + shrpx_config.cc shrpx_config.h \ + shrpx_error.h \ + shrpx_accept_handler.cc shrpx_accept_handler.h \ + shrpx_connection_handler.cc shrpx_connection_handler.h \ + shrpx_client_handler.cc shrpx_client_handler.h \ + shrpx_upstream.h \ + shrpx_http2_upstream.cc shrpx_http2_upstream.h \ + shrpx_https_upstream.cc shrpx_https_upstream.h \ + shrpx_downstream.cc shrpx_downstream.h \ + shrpx_downstream_connection.cc shrpx_downstream_connection.h \ + shrpx_http_downstream_connection.cc shrpx_http_downstream_connection.h \ + shrpx_http2_downstream_connection.cc shrpx_http2_downstream_connection.h \ + shrpx_http2_session.cc shrpx_http2_session.h \ + shrpx_downstream_queue.cc shrpx_downstream_queue.h \ + shrpx_log.cc shrpx_log.h \ + shrpx_http.cc shrpx_http.h \ + shrpx_io_control.cc shrpx_io_control.h \ + shrpx_ssl.cc shrpx_ssl.h \ + shrpx_worker.cc shrpx_worker.h \ + shrpx_log_config.cc shrpx_log_config.h \ + shrpx_connect_blocker.cc shrpx_connect_blocker.h \ + shrpx_downstream_connection_pool.cc shrpx_downstream_connection_pool.h \ + shrpx_rate_limit.cc shrpx_rate_limit.h \ + shrpx_connection.cc shrpx_connection.h \ + buffer.h memchunk.h template.h + +if HAVE_SPDYLAY +NGHTTPX_SRCS += shrpx_spdy_upstream.cc shrpx_spdy_upstream.h +endif # HAVE_SPDYLAY + +noinst_LIBRARIES = libnghttpx.a +libnghttpx_a_SOURCES = ${NGHTTPX_SRCS} + +nghttpx_SOURCES = shrpx.cc shrpx.h +nghttpx_LDADD = libnghttpx.a ${LDADD} + +if HAVE_CUNIT +check_PROGRAMS += nghttpx-unittest +nghttpx_unittest_SOURCES = shrpx-unittest.cc \ + shrpx_ssl_test.cc shrpx_ssl_test.h \ + shrpx_downstream_test.cc shrpx_downstream_test.h \ + shrpx_config_test.cc shrpx_config_test.h \ + http2_test.cc http2_test.h \ + util_test.cc util_test.h \ + nghttp2_gzip_test.c nghttp2_gzip_test.h \ + nghttp2_gzip.c nghttp2_gzip.h \ + buffer_test.cc buffer_test.h \ + memchunk_test.cc memchunk_test.h +nghttpx_unittest_CPPFLAGS = ${AM_CPPFLAGS}\ + -DNGHTTP2_TESTS_DIR=\"$(top_srcdir)/tests\" +nghttpx_unittest_LDADD = libnghttpx.a ${LDADD} @CUNIT_LIBS@ @TESTLDADD@ + +TESTS += nghttpx-unittest +endif # HAVE_CUNIT + +endif # ENABLE_APP + +if ENABLE_HPACK_TOOLS + +bin_PROGRAMS += inflatehd deflatehd + +HPACK_TOOLS_COMMON_SRCS = comp_helper.c comp_helper.h + +inflatehd_SOURCES = inflatehd.cc $(HPACK_TOOLS_COMMON_SRCS) + +deflatehd_SOURCES = deflatehd.cc $(HPACK_TOOLS_COMMON_SRCS) + +endif # ENABLE_HPACK_TOOLS + +if ENABLE_ASIO_LIB + +pkgconfigdir = $(libdir)/pkgconfig +pkgconfig_DATA = libnghttp2_asio.pc +DISTCLEANFILES = $(pkgconfig_DATA) + +lib_LTLIBRARIES = libnghttp2_asio.la + +libnghttp2_asio_la_SOURCES = \ + util.cc util.h http2.cc http2.h \ + ssl.cc ssl.h \ + asio_common.cc asio_common.h \ + asio_io_service_pool.cc asio_io_service_pool.h \ + asio_server_http2.cc \ + asio_server_http2_impl.cc asio_server_http2_impl.h \ + asio_server.cc asio_server.h \ + asio_server_http2_handler.cc asio_server_http2_handler.h \ + asio_server_connection.h \ + asio_server_request.cc \ + asio_server_request_impl.cc asio_server_request_impl.h \ + asio_server_response.cc \ + asio_server_response_impl.cc asio_server_response_impl.h \ + asio_server_stream.cc asio_server_stream.h \ + asio_server_serve_mux.cc asio_server_serve_mux.h \ + asio_server_request_handler.cc asio_server_request_handler.h \ + asio_server_tls_context.cc asio_server_tls_context.h \ + asio_client_session.cc \ + asio_client_session_impl.cc asio_client_session_impl.h \ + asio_client_session_tcp_impl.cc asio_client_session_tcp_impl.h \ + asio_client_session_tls_impl.cc asio_client_session_tls_impl.h \ + asio_client_response.cc \ + asio_client_response_impl.cc asio_client_response_impl.h \ + asio_client_request.cc \ + asio_client_request_impl.cc asio_client_request_impl.h \ + asio_client_stream.cc asio_client_stream.h \ + asio_client_tls_context.cc asio_client_tls_context.h + +libnghttp2_asio_la_CPPFLAGS = ${AM_CPPFLAGS} ${BOOST_CPPFLAGS} +libnghttp2_asio_la_LDFLAGS = -no-undefined -version-info 1:0:0 +libnghttp2_asio_la_LIBADD = \ + $(top_builddir)/lib/libnghttp2.la \ + $(top_builddir)/third-party/libhttp-parser.la \ + ${BOOST_LDFLAGS} \ + ${BOOST_ASIO_LIB} \ + ${BOOST_THREAD_LIB} \ + ${BOOST_SYSTEM_LIB} \ + @OPENSSL_LIBS@ + +endif # ENABLE_ASIO_LIB diff --git a/src/Makefile.in b/src/Makefile.in new file mode 100644 index 0000000..5702edb --- /dev/null +++ b/src/Makefile.in @@ -0,0 +1,2137 @@ +# Makefile.in generated by automake 1.14.1 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2013 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +# nghttp2 - HTTP/2 C Library + +# Copyright (c) 2012 Tatsuhiro Tsujikawa + +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: + +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. + + + + +VPATH = @srcdir@ +am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)' +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +target_triplet = @target@ +bin_PROGRAMS = $(am__EXEEXT_1) $(am__EXEEXT_2) +check_PROGRAMS = $(am__EXEEXT_3) +TESTS = $(am__EXEEXT_3) +@ENABLE_APP_TRUE@am__append_1 = nghttp nghttpd nghttpx h2load +@ENABLE_APP_TRUE@@HAVE_LIBXML2_TRUE@am__append_2 = HtmlParser.cc +@ENABLE_APP_TRUE@@HAVE_SPDYLAY_TRUE@am__append_3 = h2load_spdy_session.cc h2load_spdy_session.h +@ENABLE_APP_TRUE@@HAVE_SPDYLAY_TRUE@am__append_4 = shrpx_spdy_upstream.cc shrpx_spdy_upstream.h +@ENABLE_APP_TRUE@@HAVE_CUNIT_TRUE@am__append_5 = nghttpx-unittest +@ENABLE_APP_TRUE@@HAVE_CUNIT_TRUE@am__append_6 = nghttpx-unittest +@ENABLE_HPACK_TOOLS_TRUE@am__append_7 = inflatehd deflatehd +subdir = src +DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \ + $(srcdir)/libnghttp2_asio.pc.in $(top_srcdir)/depcomp \ + $(top_srcdir)/test-driver +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/ax_boost_asio.m4 \ + $(top_srcdir)/m4/ax_boost_base.m4 \ + $(top_srcdir)/m4/ax_boost_system.m4 \ + $(top_srcdir)/m4/ax_boost_thread.m4 \ + $(top_srcdir)/m4/ax_check_compile_flag.m4 \ + $(top_srcdir)/m4/ax_cxx_compile_stdcxx_11.m4 \ + $(top_srcdir)/m4/ax_have_epoll.m4 \ + $(top_srcdir)/m4/ax_python_devel.m4 \ + $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/libxml2.m4 \ + $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ + $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = libnghttp2_asio.pc +CONFIG_CLEAN_VPATH_FILES = +LIBRARIES = $(noinst_LIBRARIES) +ARFLAGS = cru +AM_V_AR = $(am__v_AR_@AM_V@) +am__v_AR_ = $(am__v_AR_@AM_DEFAULT_V@) +am__v_AR_0 = @echo " AR " $@; +am__v_AR_1 = +libnghttpx_a_AR = $(AR) $(ARFLAGS) +libnghttpx_a_LIBADD = +am__libnghttpx_a_SOURCES_DIST = util.cc util.h http2.cc http2.h \ + timegm.c timegm.h base64.h app_helper.cc app_helper.h ssl.cc \ + ssl.h shrpx_config.cc shrpx_config.h shrpx_error.h \ + shrpx_accept_handler.cc shrpx_accept_handler.h \ + shrpx_connection_handler.cc shrpx_connection_handler.h \ + shrpx_client_handler.cc shrpx_client_handler.h \ + shrpx_upstream.h shrpx_http2_upstream.cc \ + shrpx_http2_upstream.h shrpx_https_upstream.cc \ + shrpx_https_upstream.h shrpx_downstream.cc shrpx_downstream.h \ + shrpx_downstream_connection.cc shrpx_downstream_connection.h \ + shrpx_http_downstream_connection.cc \ + shrpx_http_downstream_connection.h \ + shrpx_http2_downstream_connection.cc \ + shrpx_http2_downstream_connection.h shrpx_http2_session.cc \ + shrpx_http2_session.h shrpx_downstream_queue.cc \ + shrpx_downstream_queue.h shrpx_log.cc shrpx_log.h \ + shrpx_http.cc shrpx_http.h shrpx_io_control.cc \ + shrpx_io_control.h shrpx_ssl.cc shrpx_ssl.h shrpx_worker.cc \ + shrpx_worker.h shrpx_log_config.cc shrpx_log_config.h \ + shrpx_connect_blocker.cc shrpx_connect_blocker.h \ + shrpx_downstream_connection_pool.cc \ + shrpx_downstream_connection_pool.h shrpx_rate_limit.cc \ + shrpx_rate_limit.h shrpx_connection.cc shrpx_connection.h \ + buffer.h memchunk.h template.h shrpx_spdy_upstream.cc \ + shrpx_spdy_upstream.h +@ENABLE_APP_TRUE@@HAVE_SPDYLAY_TRUE@am__objects_1 = shrpx_spdy_upstream.$(OBJEXT) +@ENABLE_APP_TRUE@am__objects_2 = util.$(OBJEXT) http2.$(OBJEXT) \ +@ENABLE_APP_TRUE@ timegm.$(OBJEXT) app_helper.$(OBJEXT) \ +@ENABLE_APP_TRUE@ ssl.$(OBJEXT) shrpx_config.$(OBJEXT) \ +@ENABLE_APP_TRUE@ shrpx_accept_handler.$(OBJEXT) \ +@ENABLE_APP_TRUE@ shrpx_connection_handler.$(OBJEXT) \ +@ENABLE_APP_TRUE@ shrpx_client_handler.$(OBJEXT) \ +@ENABLE_APP_TRUE@ shrpx_http2_upstream.$(OBJEXT) \ +@ENABLE_APP_TRUE@ shrpx_https_upstream.$(OBJEXT) \ +@ENABLE_APP_TRUE@ shrpx_downstream.$(OBJEXT) \ +@ENABLE_APP_TRUE@ shrpx_downstream_connection.$(OBJEXT) \ +@ENABLE_APP_TRUE@ shrpx_http_downstream_connection.$(OBJEXT) \ +@ENABLE_APP_TRUE@ shrpx_http2_downstream_connection.$(OBJEXT) \ +@ENABLE_APP_TRUE@ shrpx_http2_session.$(OBJEXT) \ +@ENABLE_APP_TRUE@ shrpx_downstream_queue.$(OBJEXT) \ +@ENABLE_APP_TRUE@ shrpx_log.$(OBJEXT) shrpx_http.$(OBJEXT) \ +@ENABLE_APP_TRUE@ shrpx_io_control.$(OBJEXT) \ +@ENABLE_APP_TRUE@ shrpx_ssl.$(OBJEXT) shrpx_worker.$(OBJEXT) \ +@ENABLE_APP_TRUE@ shrpx_log_config.$(OBJEXT) \ +@ENABLE_APP_TRUE@ shrpx_connect_blocker.$(OBJEXT) \ +@ENABLE_APP_TRUE@ shrpx_downstream_connection_pool.$(OBJEXT) \ +@ENABLE_APP_TRUE@ shrpx_rate_limit.$(OBJEXT) \ +@ENABLE_APP_TRUE@ shrpx_connection.$(OBJEXT) $(am__objects_1) +@ENABLE_APP_TRUE@am_libnghttpx_a_OBJECTS = $(am__objects_2) +libnghttpx_a_OBJECTS = $(am_libnghttpx_a_OBJECTS) +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +am__uninstall_files_from_dir = { \ + test -z "$$files" \ + || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ + || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ + $(am__cd) "$$dir" && rm -f $$files; }; \ + } +am__installdirs = "$(DESTDIR)$(libdir)" "$(DESTDIR)$(bindir)" \ + "$(DESTDIR)$(pkgconfigdir)" +LTLIBRARIES = $(lib_LTLIBRARIES) +am__DEPENDENCIES_1 = +@ENABLE_ASIO_LIB_TRUE@libnghttp2_asio_la_DEPENDENCIES = \ +@ENABLE_ASIO_LIB_TRUE@ $(top_builddir)/lib/libnghttp2.la \ +@ENABLE_ASIO_LIB_TRUE@ $(top_builddir)/third-party/libhttp-parser.la \ +@ENABLE_ASIO_LIB_TRUE@ $(am__DEPENDENCIES_1) \ +@ENABLE_ASIO_LIB_TRUE@ $(am__DEPENDENCIES_1) \ +@ENABLE_ASIO_LIB_TRUE@ $(am__DEPENDENCIES_1) \ +@ENABLE_ASIO_LIB_TRUE@ $(am__DEPENDENCIES_1) +am__libnghttp2_asio_la_SOURCES_DIST = util.cc util.h http2.cc http2.h \ + ssl.cc ssl.h asio_common.cc asio_common.h \ + asio_io_service_pool.cc asio_io_service_pool.h \ + asio_server_http2.cc asio_server_http2_impl.cc \ + asio_server_http2_impl.h asio_server.cc asio_server.h \ + asio_server_http2_handler.cc asio_server_http2_handler.h \ + asio_server_connection.h asio_server_request.cc \ + asio_server_request_impl.cc asio_server_request_impl.h \ + asio_server_response.cc asio_server_response_impl.cc \ + asio_server_response_impl.h asio_server_stream.cc \ + asio_server_stream.h asio_server_serve_mux.cc \ + asio_server_serve_mux.h asio_server_request_handler.cc \ + asio_server_request_handler.h asio_server_tls_context.cc \ + asio_server_tls_context.h asio_client_session.cc \ + asio_client_session_impl.cc asio_client_session_impl.h \ + asio_client_session_tcp_impl.cc asio_client_session_tcp_impl.h \ + asio_client_session_tls_impl.cc asio_client_session_tls_impl.h \ + asio_client_response.cc asio_client_response_impl.cc \ + asio_client_response_impl.h asio_client_request.cc \ + asio_client_request_impl.cc asio_client_request_impl.h \ + asio_client_stream.cc asio_client_stream.h \ + asio_client_tls_context.cc asio_client_tls_context.h +@ENABLE_ASIO_LIB_TRUE@am_libnghttp2_asio_la_OBJECTS = \ +@ENABLE_ASIO_LIB_TRUE@ libnghttp2_asio_la-util.lo \ +@ENABLE_ASIO_LIB_TRUE@ libnghttp2_asio_la-http2.lo \ +@ENABLE_ASIO_LIB_TRUE@ libnghttp2_asio_la-ssl.lo \ +@ENABLE_ASIO_LIB_TRUE@ libnghttp2_asio_la-asio_common.lo \ +@ENABLE_ASIO_LIB_TRUE@ libnghttp2_asio_la-asio_io_service_pool.lo \ +@ENABLE_ASIO_LIB_TRUE@ libnghttp2_asio_la-asio_server_http2.lo \ +@ENABLE_ASIO_LIB_TRUE@ libnghttp2_asio_la-asio_server_http2_impl.lo \ +@ENABLE_ASIO_LIB_TRUE@ libnghttp2_asio_la-asio_server.lo \ +@ENABLE_ASIO_LIB_TRUE@ libnghttp2_asio_la-asio_server_http2_handler.lo \ +@ENABLE_ASIO_LIB_TRUE@ libnghttp2_asio_la-asio_server_request.lo \ +@ENABLE_ASIO_LIB_TRUE@ libnghttp2_asio_la-asio_server_request_impl.lo \ +@ENABLE_ASIO_LIB_TRUE@ libnghttp2_asio_la-asio_server_response.lo \ +@ENABLE_ASIO_LIB_TRUE@ libnghttp2_asio_la-asio_server_response_impl.lo \ +@ENABLE_ASIO_LIB_TRUE@ libnghttp2_asio_la-asio_server_stream.lo \ +@ENABLE_ASIO_LIB_TRUE@ libnghttp2_asio_la-asio_server_serve_mux.lo \ +@ENABLE_ASIO_LIB_TRUE@ libnghttp2_asio_la-asio_server_request_handler.lo \ +@ENABLE_ASIO_LIB_TRUE@ libnghttp2_asio_la-asio_server_tls_context.lo \ +@ENABLE_ASIO_LIB_TRUE@ libnghttp2_asio_la-asio_client_session.lo \ +@ENABLE_ASIO_LIB_TRUE@ libnghttp2_asio_la-asio_client_session_impl.lo \ +@ENABLE_ASIO_LIB_TRUE@ libnghttp2_asio_la-asio_client_session_tcp_impl.lo \ +@ENABLE_ASIO_LIB_TRUE@ libnghttp2_asio_la-asio_client_session_tls_impl.lo \ +@ENABLE_ASIO_LIB_TRUE@ libnghttp2_asio_la-asio_client_response.lo \ +@ENABLE_ASIO_LIB_TRUE@ libnghttp2_asio_la-asio_client_response_impl.lo \ +@ENABLE_ASIO_LIB_TRUE@ libnghttp2_asio_la-asio_client_request.lo \ +@ENABLE_ASIO_LIB_TRUE@ libnghttp2_asio_la-asio_client_request_impl.lo \ +@ENABLE_ASIO_LIB_TRUE@ libnghttp2_asio_la-asio_client_stream.lo \ +@ENABLE_ASIO_LIB_TRUE@ libnghttp2_asio_la-asio_client_tls_context.lo +libnghttp2_asio_la_OBJECTS = $(am_libnghttp2_asio_la_OBJECTS) +AM_V_lt = $(am__v_lt_@AM_V@) +am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) +am__v_lt_0 = --silent +am__v_lt_1 = +libnghttp2_asio_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CXXLD) \ + $(AM_CXXFLAGS) $(CXXFLAGS) $(libnghttp2_asio_la_LDFLAGS) \ + $(LDFLAGS) -o $@ +@ENABLE_ASIO_LIB_TRUE@am_libnghttp2_asio_la_rpath = -rpath $(libdir) +@ENABLE_APP_TRUE@am__EXEEXT_1 = nghttp$(EXEEXT) nghttpd$(EXEEXT) \ +@ENABLE_APP_TRUE@ nghttpx$(EXEEXT) h2load$(EXEEXT) +@ENABLE_HPACK_TOOLS_TRUE@am__EXEEXT_2 = inflatehd$(EXEEXT) \ +@ENABLE_HPACK_TOOLS_TRUE@ deflatehd$(EXEEXT) +@ENABLE_APP_TRUE@@HAVE_CUNIT_TRUE@am__EXEEXT_3 = \ +@ENABLE_APP_TRUE@@HAVE_CUNIT_TRUE@ nghttpx-unittest$(EXEEXT) +PROGRAMS = $(bin_PROGRAMS) +am__deflatehd_SOURCES_DIST = deflatehd.cc comp_helper.c comp_helper.h +@ENABLE_HPACK_TOOLS_TRUE@am__objects_3 = comp_helper.$(OBJEXT) +@ENABLE_HPACK_TOOLS_TRUE@am_deflatehd_OBJECTS = deflatehd.$(OBJEXT) \ +@ENABLE_HPACK_TOOLS_TRUE@ $(am__objects_3) +deflatehd_OBJECTS = $(am_deflatehd_OBJECTS) +deflatehd_LDADD = $(LDADD) +deflatehd_DEPENDENCIES = $(top_builddir)/lib/libnghttp2.la \ + $(top_builddir)/third-party/libhttp-parser.la +am__h2load_SOURCES_DIST = util.cc util.h http2.cc http2.h h2load.cc \ + h2load.h timegm.c timegm.h ssl.cc ssl.h h2load_session.h \ + h2load_http2_session.cc h2load_http2_session.h \ + h2load_spdy_session.cc h2load_spdy_session.h +@ENABLE_APP_TRUE@@HAVE_SPDYLAY_TRUE@am__objects_4 = h2load_spdy_session.$(OBJEXT) +@ENABLE_APP_TRUE@am_h2load_OBJECTS = util.$(OBJEXT) http2.$(OBJEXT) \ +@ENABLE_APP_TRUE@ h2load.$(OBJEXT) timegm.$(OBJEXT) \ +@ENABLE_APP_TRUE@ ssl.$(OBJEXT) h2load_http2_session.$(OBJEXT) \ +@ENABLE_APP_TRUE@ $(am__objects_4) +h2load_OBJECTS = $(am_h2load_OBJECTS) +h2load_LDADD = $(LDADD) +h2load_DEPENDENCIES = $(top_builddir)/lib/libnghttp2.la \ + $(top_builddir)/third-party/libhttp-parser.la +am__inflatehd_SOURCES_DIST = inflatehd.cc comp_helper.c comp_helper.h +@ENABLE_HPACK_TOOLS_TRUE@am_inflatehd_OBJECTS = inflatehd.$(OBJEXT) \ +@ENABLE_HPACK_TOOLS_TRUE@ $(am__objects_3) +inflatehd_OBJECTS = $(am_inflatehd_OBJECTS) +inflatehd_LDADD = $(LDADD) +inflatehd_DEPENDENCIES = $(top_builddir)/lib/libnghttp2.la \ + $(top_builddir)/third-party/libhttp-parser.la +am__nghttp_SOURCES_DIST = util.cc http2.cc timegm.c app_helper.cc \ + nghttp2_gzip.c util.h http2.h timegm.h app_helper.h \ + nghttp2_config.h nghttp2_gzip.h nghttp.cc nghttp.h \ + HtmlParser.cc HtmlParser.h ssl.cc ssl.h +@ENABLE_APP_TRUE@am__objects_5 = util.$(OBJEXT) http2.$(OBJEXT) \ +@ENABLE_APP_TRUE@ timegm.$(OBJEXT) app_helper.$(OBJEXT) \ +@ENABLE_APP_TRUE@ nghttp2_gzip.$(OBJEXT) +am__objects_6 = +@ENABLE_APP_TRUE@@HAVE_LIBXML2_TRUE@am__objects_7 = \ +@ENABLE_APP_TRUE@@HAVE_LIBXML2_TRUE@ HtmlParser.$(OBJEXT) +@ENABLE_APP_TRUE@am__objects_8 = $(am__objects_7) +@ENABLE_APP_TRUE@am_nghttp_OBJECTS = $(am__objects_5) $(am__objects_6) \ +@ENABLE_APP_TRUE@ nghttp.$(OBJEXT) $(am__objects_8) \ +@ENABLE_APP_TRUE@ $(am__objects_6) ssl.$(OBJEXT) +nghttp_OBJECTS = $(am_nghttp_OBJECTS) +nghttp_LDADD = $(LDADD) +nghttp_DEPENDENCIES = $(top_builddir)/lib/libnghttp2.la \ + $(top_builddir)/third-party/libhttp-parser.la +am__nghttpd_SOURCES_DIST = util.cc http2.cc timegm.c app_helper.cc \ + nghttp2_gzip.c util.h http2.h timegm.h app_helper.h \ + nghttp2_config.h nghttp2_gzip.h nghttpd.cc ssl.cc ssl.h \ + HttpServer.cc HttpServer.h +@ENABLE_APP_TRUE@am_nghttpd_OBJECTS = $(am__objects_5) \ +@ENABLE_APP_TRUE@ $(am__objects_6) nghttpd.$(OBJEXT) \ +@ENABLE_APP_TRUE@ ssl.$(OBJEXT) HttpServer.$(OBJEXT) +nghttpd_OBJECTS = $(am_nghttpd_OBJECTS) +nghttpd_LDADD = $(LDADD) +nghttpd_DEPENDENCIES = $(top_builddir)/lib/libnghttp2.la \ + $(top_builddir)/third-party/libhttp-parser.la +am__nghttpx_SOURCES_DIST = shrpx.cc shrpx.h +@ENABLE_APP_TRUE@am_nghttpx_OBJECTS = shrpx.$(OBJEXT) +nghttpx_OBJECTS = $(am_nghttpx_OBJECTS) +am__DEPENDENCIES_2 = $(top_builddir)/lib/libnghttp2.la \ + $(top_builddir)/third-party/libhttp-parser.la +@ENABLE_APP_TRUE@nghttpx_DEPENDENCIES = libnghttpx.a \ +@ENABLE_APP_TRUE@ $(am__DEPENDENCIES_2) +am__nghttpx_unittest_SOURCES_DIST = shrpx-unittest.cc \ + shrpx_ssl_test.cc shrpx_ssl_test.h shrpx_downstream_test.cc \ + shrpx_downstream_test.h shrpx_config_test.cc \ + shrpx_config_test.h http2_test.cc http2_test.h util_test.cc \ + util_test.h nghttp2_gzip_test.c nghttp2_gzip_test.h \ + nghttp2_gzip.c nghttp2_gzip.h buffer_test.cc buffer_test.h \ + memchunk_test.cc memchunk_test.h +@ENABLE_APP_TRUE@@HAVE_CUNIT_TRUE@am_nghttpx_unittest_OBJECTS = nghttpx_unittest-shrpx-unittest.$(OBJEXT) \ +@ENABLE_APP_TRUE@@HAVE_CUNIT_TRUE@ nghttpx_unittest-shrpx_ssl_test.$(OBJEXT) \ +@ENABLE_APP_TRUE@@HAVE_CUNIT_TRUE@ nghttpx_unittest-shrpx_downstream_test.$(OBJEXT) \ +@ENABLE_APP_TRUE@@HAVE_CUNIT_TRUE@ nghttpx_unittest-shrpx_config_test.$(OBJEXT) \ +@ENABLE_APP_TRUE@@HAVE_CUNIT_TRUE@ nghttpx_unittest-http2_test.$(OBJEXT) \ +@ENABLE_APP_TRUE@@HAVE_CUNIT_TRUE@ nghttpx_unittest-util_test.$(OBJEXT) \ +@ENABLE_APP_TRUE@@HAVE_CUNIT_TRUE@ nghttpx_unittest-nghttp2_gzip_test.$(OBJEXT) \ +@ENABLE_APP_TRUE@@HAVE_CUNIT_TRUE@ nghttpx_unittest-nghttp2_gzip.$(OBJEXT) \ +@ENABLE_APP_TRUE@@HAVE_CUNIT_TRUE@ nghttpx_unittest-buffer_test.$(OBJEXT) \ +@ENABLE_APP_TRUE@@HAVE_CUNIT_TRUE@ nghttpx_unittest-memchunk_test.$(OBJEXT) +nghttpx_unittest_OBJECTS = $(am_nghttpx_unittest_OBJECTS) +@ENABLE_APP_TRUE@@HAVE_CUNIT_TRUE@nghttpx_unittest_DEPENDENCIES = \ +@ENABLE_APP_TRUE@@HAVE_CUNIT_TRUE@ libnghttpx.a \ +@ENABLE_APP_TRUE@@HAVE_CUNIT_TRUE@ $(am__DEPENDENCIES_2) +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__depfiles_maybe = depfiles +am__mv = mv -f +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CFLAGS) $(CFLAGS) +AM_V_CC = $(am__v_CC_@AM_V@) +am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) +am__v_CC_0 = @echo " CC " $@; +am__v_CC_1 = +CCLD = $(CC) +LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CCLD = $(am__v_CCLD_@AM_V@) +am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) +am__v_CCLD_0 = @echo " CCLD " $@; +am__v_CCLD_1 = +CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) +LTCXXCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CXXFLAGS) $(CXXFLAGS) +AM_V_CXX = $(am__v_CXX_@AM_V@) +am__v_CXX_ = $(am__v_CXX_@AM_DEFAULT_V@) +am__v_CXX_0 = @echo " CXX " $@; +am__v_CXX_1 = +CXXLD = $(CXX) +CXXLINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CXXLD) $(AM_CXXFLAGS) \ + $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CXXLD = $(am__v_CXXLD_@AM_V@) +am__v_CXXLD_ = $(am__v_CXXLD_@AM_DEFAULT_V@) +am__v_CXXLD_0 = @echo " CXXLD " $@; +am__v_CXXLD_1 = +SOURCES = $(libnghttpx_a_SOURCES) $(libnghttp2_asio_la_SOURCES) \ + $(deflatehd_SOURCES) $(h2load_SOURCES) $(inflatehd_SOURCES) \ + $(nghttp_SOURCES) $(nghttpd_SOURCES) $(nghttpx_SOURCES) \ + $(nghttpx_unittest_SOURCES) +DIST_SOURCES = $(am__libnghttpx_a_SOURCES_DIST) \ + $(am__libnghttp2_asio_la_SOURCES_DIST) \ + $(am__deflatehd_SOURCES_DIST) $(am__h2load_SOURCES_DIST) \ + $(am__inflatehd_SOURCES_DIST) $(am__nghttp_SOURCES_DIST) \ + $(am__nghttpd_SOURCES_DIST) $(am__nghttpx_SOURCES_DIST) \ + $(am__nghttpx_unittest_SOURCES_DIST) +RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \ + ctags-recursive dvi-recursive html-recursive info-recursive \ + install-data-recursive install-dvi-recursive \ + install-exec-recursive install-html-recursive \ + install-info-recursive install-pdf-recursive \ + install-ps-recursive install-recursive installcheck-recursive \ + installdirs-recursive pdf-recursive ps-recursive \ + tags-recursive uninstall-recursive +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +DATA = $(pkgconfig_DATA) +RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \ + distclean-recursive maintainer-clean-recursive +am__recursive_targets = \ + $(RECURSIVE_TARGETS) \ + $(RECURSIVE_CLEAN_TARGETS) \ + $(am__extra_recursive_targets) +AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \ + check recheck distdir +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +ETAGS = etags +CTAGS = ctags +am__tty_colors_dummy = \ + mgn= red= grn= lgn= blu= brg= std=; \ + am__color_tests=no +am__tty_colors = { \ + $(am__tty_colors_dummy); \ + if test "X$(AM_COLOR_TESTS)" = Xno; then \ + am__color_tests=no; \ + elif test "X$(AM_COLOR_TESTS)" = Xalways; then \ + am__color_tests=yes; \ + elif test "X$$TERM" != Xdumb && { test -t 1; } 2>/dev/null; then \ + am__color_tests=yes; \ + fi; \ + if test $$am__color_tests = yes; then \ + red=''; \ + grn=''; \ + lgn=''; \ + blu=''; \ + mgn=''; \ + brg=''; \ + std=''; \ + fi; \ +} +am__recheck_rx = ^[ ]*:recheck:[ ]* +am__global_test_result_rx = ^[ ]*:global-test-result:[ ]* +am__copy_in_global_log_rx = ^[ ]*:copy-in-global-log:[ ]* +# A command that, given a newline-separated list of test names on the +# standard input, print the name of the tests that are to be re-run +# upon "make recheck". +am__list_recheck_tests = $(AWK) '{ \ + recheck = 1; \ + while ((rc = (getline line < ($$0 ".trs"))) != 0) \ + { \ + if (rc < 0) \ + { \ + if ((getline line2 < ($$0 ".log")) < 0) \ + recheck = 0; \ + break; \ + } \ + else if (line ~ /$(am__recheck_rx)[nN][Oo]/) \ + { \ + recheck = 0; \ + break; \ + } \ + else if (line ~ /$(am__recheck_rx)[yY][eE][sS]/) \ + { \ + break; \ + } \ + }; \ + if (recheck) \ + print $$0; \ + close ($$0 ".trs"); \ + close ($$0 ".log"); \ +}' +# A command that, given a newline-separated list of test names on the +# standard input, create the global log from their .trs and .log files. +am__create_global_log = $(AWK) ' \ +function fatal(msg) \ +{ \ + print "fatal: making $@: " msg | "cat >&2"; \ + exit 1; \ +} \ +function rst_section(header) \ +{ \ + print header; \ + len = length(header); \ + for (i = 1; i <= len; i = i + 1) \ + printf "="; \ + printf "\n\n"; \ +} \ +{ \ + copy_in_global_log = 1; \ + global_test_result = "RUN"; \ + while ((rc = (getline line < ($$0 ".trs"))) != 0) \ + { \ + if (rc < 0) \ + fatal("failed to read from " $$0 ".trs"); \ + if (line ~ /$(am__global_test_result_rx)/) \ + { \ + sub("$(am__global_test_result_rx)", "", line); \ + sub("[ ]*$$", "", line); \ + global_test_result = line; \ + } \ + else if (line ~ /$(am__copy_in_global_log_rx)[nN][oO]/) \ + copy_in_global_log = 0; \ + }; \ + if (copy_in_global_log) \ + { \ + rst_section(global_test_result ": " $$0); \ + while ((rc = (getline line < ($$0 ".log"))) != 0) \ + { \ + if (rc < 0) \ + fatal("failed to read from " $$0 ".log"); \ + print line; \ + }; \ + printf "\n"; \ + }; \ + close ($$0 ".trs"); \ + close ($$0 ".log"); \ +}' +# Restructured Text title. +am__rst_title = { sed 's/.*/ & /;h;s/./=/g;p;x;s/ *$$//;p;g' && echo; } +# Solaris 10 'make', and several other traditional 'make' implementations, +# pass "-e" to $(SHELL), and POSIX 2008 even requires this. Work around it +# by disabling -e (using the XSI extension "set +e") if it's set. +am__sh_e_setup = case $$- in *e*) set +e;; esac +# Default flags passed to test drivers. +am__common_driver_flags = \ + --color-tests "$$am__color_tests" \ + --enable-hard-errors "$$am__enable_hard_errors" \ + --expect-failure "$$am__expect_failure" +# To be inserted before the command running the test. Creates the +# directory for the log if needed. Stores in $dir the directory +# containing $f, in $tst the test, in $log the log. Executes the +# developer- defined test setup AM_TESTS_ENVIRONMENT (if any), and +# passes TESTS_ENVIRONMENT. Set up options for the wrapper that +# will run the test scripts (or their associated LOG_COMPILER, if +# thy have one). +am__check_pre = \ +$(am__sh_e_setup); \ +$(am__vpath_adj_setup) $(am__vpath_adj) \ +$(am__tty_colors); \ +srcdir=$(srcdir); export srcdir; \ +case "$@" in \ + */*) am__odir=`echo "./$@" | sed 's|/[^/]*$$||'`;; \ + *) am__odir=.;; \ +esac; \ +test "x$$am__odir" = x"." || test -d "$$am__odir" \ + || $(MKDIR_P) "$$am__odir" || exit $$?; \ +if test -f "./$$f"; then dir=./; \ +elif test -f "$$f"; then dir=; \ +else dir="$(srcdir)/"; fi; \ +tst=$$dir$$f; log='$@'; \ +if test -n '$(DISABLE_HARD_ERRORS)'; then \ + am__enable_hard_errors=no; \ +else \ + am__enable_hard_errors=yes; \ +fi; \ +case " $(XFAIL_TESTS) " in \ + *[\ \ ]$$f[\ \ ]* | *[\ \ ]$$dir$$f[\ \ ]*) \ + am__expect_failure=yes;; \ + *) \ + am__expect_failure=no;; \ +esac; \ +$(AM_TESTS_ENVIRONMENT) $(TESTS_ENVIRONMENT) +# A shell command to get the names of the tests scripts with any registered +# extension removed (i.e., equivalently, the names of the test logs, with +# the '.log' extension removed). The result is saved in the shell variable +# '$bases'. This honors runtime overriding of TESTS and TEST_LOGS. Sadly, +# we cannot use something simpler, involving e.g., "$(TEST_LOGS:.log=)", +# since that might cause problem with VPATH rewrites for suffix-less tests. +# See also 'test-harness-vpath-rewrite.sh' and 'test-trs-basic.sh'. +am__set_TESTS_bases = \ + bases='$(TEST_LOGS)'; \ + bases=`for i in $$bases; do echo $$i; done | sed 's/\.log$$//'`; \ + bases=`echo $$bases` +RECHECK_LOGS = $(TEST_LOGS) +TEST_SUITE_LOG = test-suite.log +TEST_EXTENSIONS = @EXEEXT@ .test +LOG_DRIVER = $(SHELL) $(top_srcdir)/test-driver +LOG_COMPILE = $(LOG_COMPILER) $(AM_LOG_FLAGS) $(LOG_FLAGS) +am__set_b = \ + case '$@' in \ + */*) \ + case '$*' in \ + */*) b='$*';; \ + *) b=`echo '$@' | sed 's/\.log$$//'`; \ + esac;; \ + *) \ + b='$*';; \ + esac +am__test_logs1 = $(TESTS:=.log) +am__test_logs2 = $(am__test_logs1:@EXEEXT@.log=.log) +TEST_LOGS = $(am__test_logs2:.test.log=.log) +TEST_LOG_DRIVER = $(SHELL) $(top_srcdir)/test-driver +TEST_LOG_COMPILE = $(TEST_LOG_COMPILER) $(AM_TEST_LOG_FLAGS) \ + $(TEST_LOG_FLAGS) +DIST_SUBDIRS = $(SUBDIRS) +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +am__relativize = \ + dir0=`pwd`; \ + sed_first='s,^\([^/]*\)/.*$$,\1,'; \ + sed_rest='s,^[^/]*/*,,'; \ + sed_last='s,^.*/\([^/]*\)$$,\1,'; \ + sed_butlast='s,/*[^/]*$$,,'; \ + while test -n "$$dir1"; do \ + first=`echo "$$dir1" | sed -e "$$sed_first"`; \ + if test "$$first" != "."; then \ + if test "$$first" = ".."; then \ + dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \ + dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \ + else \ + first2=`echo "$$dir2" | sed -e "$$sed_first"`; \ + if test "$$first2" = "$$first"; then \ + dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \ + else \ + dir2="../$$dir2"; \ + fi; \ + dir0="$$dir0"/"$$first"; \ + fi; \ + fi; \ + dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \ + done; \ + reldir="$$dir2" +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +APPLDFLAGS = @APPLDFLAGS@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +BOOST_ASIO_LIB = @BOOST_ASIO_LIB@ +BOOST_CPPFLAGS = @BOOST_CPPFLAGS@ +BOOST_LDFLAGS = @BOOST_LDFLAGS@ +BOOST_SYSTEM_LIB = @BOOST_SYSTEM_LIB@ +BOOST_THREAD_LIB = @BOOST_THREAD_LIB@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CUNIT_CFLAGS = @CUNIT_CFLAGS@ +CUNIT_LIBS = @CUNIT_LIBS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +CYTHON = @CYTHON@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DLLTOOL = @DLLTOOL@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +GREP = @GREP@ +HAVE_CXX11 = @HAVE_CXX11@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +JANSSON_CFLAGS = @JANSSON_CFLAGS@ +JANSSON_LIBS = @JANSSON_LIBS@ +JEMALLOC_LIBS = @JEMALLOC_LIBS@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBEVENT_OPENSSL_CFLAGS = @LIBEVENT_OPENSSL_CFLAGS@ +LIBEVENT_OPENSSL_LIBS = @LIBEVENT_OPENSSL_LIBS@ +LIBEV_CFLAGS = @LIBEV_CFLAGS@ +LIBEV_LIBS = @LIBEV_LIBS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBSPDYLAY_CFLAGS = @LIBSPDYLAY_CFLAGS@ +LIBSPDYLAY_LIBS = @LIBSPDYLAY_LIBS@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +LT_AGE = @LT_AGE@ +LT_CURRENT = @LT_CURRENT@ +LT_REVISION = @LT_REVISION@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MKDIR_P = @MKDIR_P@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OPENSSL_CFLAGS = @OPENSSL_CFLAGS@ +OPENSSL_LIBS = @OPENSSL_LIBS@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PACKAGE_VERSION_NUM = @PACKAGE_VERSION_NUM@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PKG_CONFIG = @PKG_CONFIG@ +PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ +PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ +PYTHON = @PYTHON@ +PYTHON_CPPFLAGS = @PYTHON_CPPFLAGS@ +PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@ +PYTHON_EXTRA_LDFLAGS = @PYTHON_EXTRA_LDFLAGS@ +PYTHON_EXTRA_LIBS = @PYTHON_EXTRA_LIBS@ +PYTHON_LDFLAGS = @PYTHON_LDFLAGS@ +PYTHON_PLATFORM = @PYTHON_PLATFORM@ +PYTHON_PREFIX = @PYTHON_PREFIX@ +PYTHON_SITE_PKG = @PYTHON_SITE_PKG@ +PYTHON_VERSION = @PYTHON_VERSION@ +RANLIB = @RANLIB@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +TESTLDADD = @TESTLDADD@ +VERSION = @VERSION@ +WARNCFLAGS = @WARNCFLAGS@ +XML2_CONFIG = @XML2_CONFIG@ +XML_CPPFLAGS = @XML_CPPFLAGS@ +XML_LIBS = @XML_LIBS@ +ZLIB_CFLAGS = @ZLIB_CFLAGS@ +ZLIB_LIBS = @ZLIB_LIBS@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +pkgpyexecdir = @pkgpyexecdir@ +pkgpythondir = @pkgpythondir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +pyexecdir = @pyexecdir@ +pythondir = @pythondir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target = @target@ +target_alias = @target_alias@ +target_cpu = @target_cpu@ +target_os = @target_os@ +target_vendor = @target_vendor@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ + +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +SUBDIRS = includes +AM_CFLAGS = $(WARNCFLAGS) +AM_CPPFLAGS = \ + -DPKGDATADIR='"$(pkgdatadir)"' \ + -Wall \ + -I$(top_srcdir)/lib/includes \ + -I$(top_builddir)/lib/includes \ + -I$(top_srcdir)/lib \ + -I$(top_srcdir)/src/includes \ + -I$(top_srcdir)/third-party \ + @LIBSPDYLAY_CFLAGS@ \ + @XML_CPPFLAGS@ \ + @LIBEV_CFLAGS@ \ + @OPENSSL_CFLAGS@ \ + @JANSSON_CFLAGS@ \ + @ZLIB_CFLAGS@ \ + @DEFS@ + +LDADD = $(top_builddir)/lib/libnghttp2.la \ + $(top_builddir)/third-party/libhttp-parser.la \ + @JEMALLOC_LIBS@ \ + @LIBSPDYLAY_LIBS@ \ + @XML_LIBS@ \ + @LIBEV_LIBS@ \ + @OPENSSL_LIBS@ \ + @JANSSON_LIBS@ \ + @ZLIB_LIBS@ \ + @APPLDFLAGS@ + +@ENABLE_APP_TRUE@HELPER_OBJECTS = util.cc \ +@ENABLE_APP_TRUE@ http2.cc timegm.c app_helper.cc nghttp2_gzip.c + +@ENABLE_APP_TRUE@HELPER_HFILES = util.h \ +@ENABLE_APP_TRUE@ http2.h timegm.h app_helper.h nghttp2_config.h \ +@ENABLE_APP_TRUE@ nghttp2_gzip.h + +@ENABLE_APP_TRUE@HTML_PARSER_OBJECTS = $(am__append_2) +@ENABLE_APP_TRUE@HTML_PARSER_HFILES = HtmlParser.h +@ENABLE_APP_TRUE@nghttp_SOURCES = ${HELPER_OBJECTS} ${HELPER_HFILES} nghttp.cc nghttp.h \ +@ENABLE_APP_TRUE@ ${HTML_PARSER_OBJECTS} ${HTML_PARSER_HFILES} \ +@ENABLE_APP_TRUE@ ssl.cc ssl.h + +@ENABLE_APP_TRUE@nghttpd_SOURCES = ${HELPER_OBJECTS} ${HELPER_HFILES} nghttpd.cc \ +@ENABLE_APP_TRUE@ ssl.cc ssl.h \ +@ENABLE_APP_TRUE@ HttpServer.cc HttpServer.h + +@ENABLE_APP_TRUE@h2load_SOURCES = util.cc util.h http2.cc http2.h \ +@ENABLE_APP_TRUE@ h2load.cc h2load.h timegm.c timegm.h ssl.cc \ +@ENABLE_APP_TRUE@ ssl.h h2load_session.h \ +@ENABLE_APP_TRUE@ h2load_http2_session.cc \ +@ENABLE_APP_TRUE@ h2load_http2_session.h $(am__append_3) +@ENABLE_APP_TRUE@NGHTTPX_SRCS = util.cc util.h http2.cc http2.h \ +@ENABLE_APP_TRUE@ timegm.c timegm.h base64.h app_helper.cc \ +@ENABLE_APP_TRUE@ app_helper.h ssl.cc ssl.h shrpx_config.cc \ +@ENABLE_APP_TRUE@ shrpx_config.h shrpx_error.h \ +@ENABLE_APP_TRUE@ shrpx_accept_handler.cc \ +@ENABLE_APP_TRUE@ shrpx_accept_handler.h \ +@ENABLE_APP_TRUE@ shrpx_connection_handler.cc \ +@ENABLE_APP_TRUE@ shrpx_connection_handler.h \ +@ENABLE_APP_TRUE@ shrpx_client_handler.cc \ +@ENABLE_APP_TRUE@ shrpx_client_handler.h shrpx_upstream.h \ +@ENABLE_APP_TRUE@ shrpx_http2_upstream.cc \ +@ENABLE_APP_TRUE@ shrpx_http2_upstream.h \ +@ENABLE_APP_TRUE@ shrpx_https_upstream.cc \ +@ENABLE_APP_TRUE@ shrpx_https_upstream.h shrpx_downstream.cc \ +@ENABLE_APP_TRUE@ shrpx_downstream.h \ +@ENABLE_APP_TRUE@ shrpx_downstream_connection.cc \ +@ENABLE_APP_TRUE@ shrpx_downstream_connection.h \ +@ENABLE_APP_TRUE@ shrpx_http_downstream_connection.cc \ +@ENABLE_APP_TRUE@ shrpx_http_downstream_connection.h \ +@ENABLE_APP_TRUE@ shrpx_http2_downstream_connection.cc \ +@ENABLE_APP_TRUE@ shrpx_http2_downstream_connection.h \ +@ENABLE_APP_TRUE@ shrpx_http2_session.cc shrpx_http2_session.h \ +@ENABLE_APP_TRUE@ shrpx_downstream_queue.cc \ +@ENABLE_APP_TRUE@ shrpx_downstream_queue.h shrpx_log.cc \ +@ENABLE_APP_TRUE@ shrpx_log.h shrpx_http.cc shrpx_http.h \ +@ENABLE_APP_TRUE@ shrpx_io_control.cc shrpx_io_control.h \ +@ENABLE_APP_TRUE@ shrpx_ssl.cc shrpx_ssl.h shrpx_worker.cc \ +@ENABLE_APP_TRUE@ shrpx_worker.h shrpx_log_config.cc \ +@ENABLE_APP_TRUE@ shrpx_log_config.h shrpx_connect_blocker.cc \ +@ENABLE_APP_TRUE@ shrpx_connect_blocker.h \ +@ENABLE_APP_TRUE@ shrpx_downstream_connection_pool.cc \ +@ENABLE_APP_TRUE@ shrpx_downstream_connection_pool.h \ +@ENABLE_APP_TRUE@ shrpx_rate_limit.cc shrpx_rate_limit.h \ +@ENABLE_APP_TRUE@ shrpx_connection.cc shrpx_connection.h \ +@ENABLE_APP_TRUE@ buffer.h memchunk.h template.h \ +@ENABLE_APP_TRUE@ $(am__append_4) +@ENABLE_APP_TRUE@noinst_LIBRARIES = libnghttpx.a +@ENABLE_APP_TRUE@libnghttpx_a_SOURCES = ${NGHTTPX_SRCS} +@ENABLE_APP_TRUE@nghttpx_SOURCES = shrpx.cc shrpx.h +@ENABLE_APP_TRUE@nghttpx_LDADD = libnghttpx.a ${LDADD} +@ENABLE_APP_TRUE@@HAVE_CUNIT_TRUE@nghttpx_unittest_SOURCES = shrpx-unittest.cc \ +@ENABLE_APP_TRUE@@HAVE_CUNIT_TRUE@ shrpx_ssl_test.cc shrpx_ssl_test.h \ +@ENABLE_APP_TRUE@@HAVE_CUNIT_TRUE@ shrpx_downstream_test.cc shrpx_downstream_test.h \ +@ENABLE_APP_TRUE@@HAVE_CUNIT_TRUE@ shrpx_config_test.cc shrpx_config_test.h \ +@ENABLE_APP_TRUE@@HAVE_CUNIT_TRUE@ http2_test.cc http2_test.h \ +@ENABLE_APP_TRUE@@HAVE_CUNIT_TRUE@ util_test.cc util_test.h \ +@ENABLE_APP_TRUE@@HAVE_CUNIT_TRUE@ nghttp2_gzip_test.c nghttp2_gzip_test.h \ +@ENABLE_APP_TRUE@@HAVE_CUNIT_TRUE@ nghttp2_gzip.c nghttp2_gzip.h \ +@ENABLE_APP_TRUE@@HAVE_CUNIT_TRUE@ buffer_test.cc buffer_test.h \ +@ENABLE_APP_TRUE@@HAVE_CUNIT_TRUE@ memchunk_test.cc memchunk_test.h + +@ENABLE_APP_TRUE@@HAVE_CUNIT_TRUE@nghttpx_unittest_CPPFLAGS = ${AM_CPPFLAGS}\ +@ENABLE_APP_TRUE@@HAVE_CUNIT_TRUE@ -DNGHTTP2_TESTS_DIR=\"$(top_srcdir)/tests\" + +@ENABLE_APP_TRUE@@HAVE_CUNIT_TRUE@nghttpx_unittest_LDADD = libnghttpx.a ${LDADD} @CUNIT_LIBS@ @TESTLDADD@ +@ENABLE_HPACK_TOOLS_TRUE@HPACK_TOOLS_COMMON_SRCS = comp_helper.c comp_helper.h +@ENABLE_HPACK_TOOLS_TRUE@inflatehd_SOURCES = inflatehd.cc $(HPACK_TOOLS_COMMON_SRCS) +@ENABLE_HPACK_TOOLS_TRUE@deflatehd_SOURCES = deflatehd.cc $(HPACK_TOOLS_COMMON_SRCS) +@ENABLE_ASIO_LIB_TRUE@pkgconfigdir = $(libdir)/pkgconfig +@ENABLE_ASIO_LIB_TRUE@pkgconfig_DATA = libnghttp2_asio.pc +@ENABLE_ASIO_LIB_TRUE@DISTCLEANFILES = $(pkgconfig_DATA) +@ENABLE_ASIO_LIB_TRUE@lib_LTLIBRARIES = libnghttp2_asio.la +@ENABLE_ASIO_LIB_TRUE@libnghttp2_asio_la_SOURCES = \ +@ENABLE_ASIO_LIB_TRUE@ util.cc util.h http2.cc http2.h \ +@ENABLE_ASIO_LIB_TRUE@ ssl.cc ssl.h \ +@ENABLE_ASIO_LIB_TRUE@ asio_common.cc asio_common.h \ +@ENABLE_ASIO_LIB_TRUE@ asio_io_service_pool.cc asio_io_service_pool.h \ +@ENABLE_ASIO_LIB_TRUE@ asio_server_http2.cc \ +@ENABLE_ASIO_LIB_TRUE@ asio_server_http2_impl.cc asio_server_http2_impl.h \ +@ENABLE_ASIO_LIB_TRUE@ asio_server.cc asio_server.h \ +@ENABLE_ASIO_LIB_TRUE@ asio_server_http2_handler.cc asio_server_http2_handler.h \ +@ENABLE_ASIO_LIB_TRUE@ asio_server_connection.h \ +@ENABLE_ASIO_LIB_TRUE@ asio_server_request.cc \ +@ENABLE_ASIO_LIB_TRUE@ asio_server_request_impl.cc asio_server_request_impl.h \ +@ENABLE_ASIO_LIB_TRUE@ asio_server_response.cc \ +@ENABLE_ASIO_LIB_TRUE@ asio_server_response_impl.cc asio_server_response_impl.h \ +@ENABLE_ASIO_LIB_TRUE@ asio_server_stream.cc asio_server_stream.h \ +@ENABLE_ASIO_LIB_TRUE@ asio_server_serve_mux.cc asio_server_serve_mux.h \ +@ENABLE_ASIO_LIB_TRUE@ asio_server_request_handler.cc asio_server_request_handler.h \ +@ENABLE_ASIO_LIB_TRUE@ asio_server_tls_context.cc asio_server_tls_context.h \ +@ENABLE_ASIO_LIB_TRUE@ asio_client_session.cc \ +@ENABLE_ASIO_LIB_TRUE@ asio_client_session_impl.cc asio_client_session_impl.h \ +@ENABLE_ASIO_LIB_TRUE@ asio_client_session_tcp_impl.cc asio_client_session_tcp_impl.h \ +@ENABLE_ASIO_LIB_TRUE@ asio_client_session_tls_impl.cc asio_client_session_tls_impl.h \ +@ENABLE_ASIO_LIB_TRUE@ asio_client_response.cc \ +@ENABLE_ASIO_LIB_TRUE@ asio_client_response_impl.cc asio_client_response_impl.h \ +@ENABLE_ASIO_LIB_TRUE@ asio_client_request.cc \ +@ENABLE_ASIO_LIB_TRUE@ asio_client_request_impl.cc asio_client_request_impl.h \ +@ENABLE_ASIO_LIB_TRUE@ asio_client_stream.cc asio_client_stream.h \ +@ENABLE_ASIO_LIB_TRUE@ asio_client_tls_context.cc asio_client_tls_context.h + +@ENABLE_ASIO_LIB_TRUE@libnghttp2_asio_la_CPPFLAGS = ${AM_CPPFLAGS} ${BOOST_CPPFLAGS} +@ENABLE_ASIO_LIB_TRUE@libnghttp2_asio_la_LDFLAGS = -no-undefined -version-info 1:0:0 +@ENABLE_ASIO_LIB_TRUE@libnghttp2_asio_la_LIBADD = \ +@ENABLE_ASIO_LIB_TRUE@ $(top_builddir)/lib/libnghttp2.la \ +@ENABLE_ASIO_LIB_TRUE@ $(top_builddir)/third-party/libhttp-parser.la \ +@ENABLE_ASIO_LIB_TRUE@ ${BOOST_LDFLAGS} \ +@ENABLE_ASIO_LIB_TRUE@ ${BOOST_ASIO_LIB} \ +@ENABLE_ASIO_LIB_TRUE@ ${BOOST_THREAD_LIB} \ +@ENABLE_ASIO_LIB_TRUE@ ${BOOST_SYSTEM_LIB} \ +@ENABLE_ASIO_LIB_TRUE@ @OPENSSL_LIBS@ + +all: all-recursive + +.SUFFIXES: +.SUFFIXES: .c .cc .lo .log .o .obj .test .test$(EXEEXT) .trs +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu src/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): +libnghttp2_asio.pc: $(top_builddir)/config.status $(srcdir)/libnghttp2_asio.pc.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ + +clean-noinstLIBRARIES: + -test -z "$(noinst_LIBRARIES)" || rm -f $(noinst_LIBRARIES) + +libnghttpx.a: $(libnghttpx_a_OBJECTS) $(libnghttpx_a_DEPENDENCIES) $(EXTRA_libnghttpx_a_DEPENDENCIES) + $(AM_V_at)-rm -f libnghttpx.a + $(AM_V_AR)$(libnghttpx_a_AR) libnghttpx.a $(libnghttpx_a_OBJECTS) $(libnghttpx_a_LIBADD) + $(AM_V_at)$(RANLIB) libnghttpx.a + +install-libLTLIBRARIES: $(lib_LTLIBRARIES) + @$(NORMAL_INSTALL) + @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \ + list2=; for p in $$list; do \ + if test -f $$p; then \ + list2="$$list2 $$p"; \ + else :; fi; \ + done; \ + test -z "$$list2" || { \ + echo " $(MKDIR_P) '$(DESTDIR)$(libdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(libdir)" || exit 1; \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(libdir)'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(libdir)"; \ + } + +uninstall-libLTLIBRARIES: + @$(NORMAL_UNINSTALL) + @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \ + for p in $$list; do \ + $(am__strip_dir) \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$f'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$f"; \ + done + +clean-libLTLIBRARIES: + -test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES) + @list='$(lib_LTLIBRARIES)'; \ + locs=`for p in $$list; do echo $$p; done | \ + sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ + sort -u`; \ + test -z "$$locs" || { \ + echo rm -f $${locs}; \ + rm -f $${locs}; \ + } + +libnghttp2_asio.la: $(libnghttp2_asio_la_OBJECTS) $(libnghttp2_asio_la_DEPENDENCIES) $(EXTRA_libnghttp2_asio_la_DEPENDENCIES) + $(AM_V_CXXLD)$(libnghttp2_asio_la_LINK) $(am_libnghttp2_asio_la_rpath) $(libnghttp2_asio_la_OBJECTS) $(libnghttp2_asio_la_LIBADD) $(LIBS) +install-binPROGRAMS: $(bin_PROGRAMS) + @$(NORMAL_INSTALL) + @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(bindir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(bindir)" || exit 1; \ + fi; \ + for p in $$list; do echo "$$p $$p"; done | \ + sed 's/$(EXEEXT)$$//' | \ + while read p p1; do if test -f $$p \ + || test -f $$p1 \ + ; then echo "$$p"; echo "$$p"; else :; fi; \ + done | \ + sed -e 'p;s,.*/,,;n;h' \ + -e 's|.*|.|' \ + -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \ + sed 'N;N;N;s,\n, ,g' | \ + $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \ + { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ + if ($$2 == $$4) files[d] = files[d] " " $$1; \ + else { print "f", $$3 "/" $$4, $$1; } } \ + END { for (d in files) print "f", d, files[d] }' | \ + while read type dir files; do \ + if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ + test -z "$$files" || { \ + echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(bindir)$$dir'"; \ + $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(bindir)$$dir" || exit $$?; \ + } \ + ; done + +uninstall-binPROGRAMS: + @$(NORMAL_UNINSTALL) + @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \ + files=`for p in $$list; do echo "$$p"; done | \ + sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \ + -e 's/$$/$(EXEEXT)/' \ + `; \ + test -n "$$list" || exit 0; \ + echo " ( cd '$(DESTDIR)$(bindir)' && rm -f" $$files ")"; \ + cd "$(DESTDIR)$(bindir)" && rm -f $$files + +clean-binPROGRAMS: + @list='$(bin_PROGRAMS)'; test -n "$$list" || exit 0; \ + echo " rm -f" $$list; \ + rm -f $$list || exit $$?; \ + test -n "$(EXEEXT)" || exit 0; \ + list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ + echo " rm -f" $$list; \ + rm -f $$list + +clean-checkPROGRAMS: + @list='$(check_PROGRAMS)'; test -n "$$list" || exit 0; \ + echo " rm -f" $$list; \ + rm -f $$list || exit $$?; \ + test -n "$(EXEEXT)" || exit 0; \ + list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ + echo " rm -f" $$list; \ + rm -f $$list + +deflatehd$(EXEEXT): $(deflatehd_OBJECTS) $(deflatehd_DEPENDENCIES) $(EXTRA_deflatehd_DEPENDENCIES) + @rm -f deflatehd$(EXEEXT) + $(AM_V_CXXLD)$(CXXLINK) $(deflatehd_OBJECTS) $(deflatehd_LDADD) $(LIBS) + +h2load$(EXEEXT): $(h2load_OBJECTS) $(h2load_DEPENDENCIES) $(EXTRA_h2load_DEPENDENCIES) + @rm -f h2load$(EXEEXT) + $(AM_V_CXXLD)$(CXXLINK) $(h2load_OBJECTS) $(h2load_LDADD) $(LIBS) + +inflatehd$(EXEEXT): $(inflatehd_OBJECTS) $(inflatehd_DEPENDENCIES) $(EXTRA_inflatehd_DEPENDENCIES) + @rm -f inflatehd$(EXEEXT) + $(AM_V_CXXLD)$(CXXLINK) $(inflatehd_OBJECTS) $(inflatehd_LDADD) $(LIBS) + +nghttp$(EXEEXT): $(nghttp_OBJECTS) $(nghttp_DEPENDENCIES) $(EXTRA_nghttp_DEPENDENCIES) + @rm -f nghttp$(EXEEXT) + $(AM_V_CXXLD)$(CXXLINK) $(nghttp_OBJECTS) $(nghttp_LDADD) $(LIBS) + +nghttpd$(EXEEXT): $(nghttpd_OBJECTS) $(nghttpd_DEPENDENCIES) $(EXTRA_nghttpd_DEPENDENCIES) + @rm -f nghttpd$(EXEEXT) + $(AM_V_CXXLD)$(CXXLINK) $(nghttpd_OBJECTS) $(nghttpd_LDADD) $(LIBS) + +nghttpx$(EXEEXT): $(nghttpx_OBJECTS) $(nghttpx_DEPENDENCIES) $(EXTRA_nghttpx_DEPENDENCIES) + @rm -f nghttpx$(EXEEXT) + $(AM_V_CXXLD)$(CXXLINK) $(nghttpx_OBJECTS) $(nghttpx_LDADD) $(LIBS) + +nghttpx-unittest$(EXEEXT): $(nghttpx_unittest_OBJECTS) $(nghttpx_unittest_DEPENDENCIES) $(EXTRA_nghttpx_unittest_DEPENDENCIES) + @rm -f nghttpx-unittest$(EXEEXT) + $(AM_V_CXXLD)$(CXXLINK) $(nghttpx_unittest_OBJECTS) $(nghttpx_unittest_LDADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/HtmlParser.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/HttpServer.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/app_helper.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/comp_helper.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/deflatehd.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/h2load.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/h2load_http2_session.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/h2load_spdy_session.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/http2.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/inflatehd.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttp2_asio_la-asio_client_request.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttp2_asio_la-asio_client_request_impl.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttp2_asio_la-asio_client_response.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttp2_asio_la-asio_client_response_impl.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttp2_asio_la-asio_client_session.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttp2_asio_la-asio_client_session_impl.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttp2_asio_la-asio_client_session_tcp_impl.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttp2_asio_la-asio_client_session_tls_impl.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttp2_asio_la-asio_client_stream.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttp2_asio_la-asio_client_tls_context.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttp2_asio_la-asio_common.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttp2_asio_la-asio_io_service_pool.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttp2_asio_la-asio_server.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttp2_asio_la-asio_server_http2.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttp2_asio_la-asio_server_http2_handler.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttp2_asio_la-asio_server_http2_impl.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttp2_asio_la-asio_server_request.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttp2_asio_la-asio_server_request_handler.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttp2_asio_la-asio_server_request_impl.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttp2_asio_la-asio_server_response.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttp2_asio_la-asio_server_response_impl.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttp2_asio_la-asio_server_serve_mux.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttp2_asio_la-asio_server_stream.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttp2_asio_la-asio_server_tls_context.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttp2_asio_la-http2.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttp2_asio_la-ssl.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttp2_asio_la-util.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_gzip.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttpd.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttpx_unittest-buffer_test.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttpx_unittest-http2_test.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttpx_unittest-memchunk_test.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttpx_unittest-nghttp2_gzip.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttpx_unittest-nghttp2_gzip_test.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttpx_unittest-shrpx-unittest.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttpx_unittest-shrpx_config_test.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttpx_unittest-shrpx_downstream_test.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttpx_unittest-shrpx_ssl_test.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttpx_unittest-util_test.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/shrpx.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/shrpx_accept_handler.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/shrpx_client_handler.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/shrpx_config.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/shrpx_connect_blocker.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/shrpx_connection.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/shrpx_connection_handler.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/shrpx_downstream.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/shrpx_downstream_connection.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/shrpx_downstream_connection_pool.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/shrpx_downstream_queue.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/shrpx_http.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/shrpx_http2_downstream_connection.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/shrpx_http2_session.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/shrpx_http2_upstream.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/shrpx_http_downstream_connection.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/shrpx_https_upstream.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/shrpx_io_control.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/shrpx_log.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/shrpx_log_config.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/shrpx_rate_limit.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/shrpx_spdy_upstream.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/shrpx_ssl.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/shrpx_worker.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ssl.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/timegm.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/util.Po@am__quote@ + +.c.o: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\ +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< + +.c.obj: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\ +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.c.lo: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\ +@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< + +nghttpx_unittest-nghttp2_gzip_test.o: nghttp2_gzip_test.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT nghttpx_unittest-nghttp2_gzip_test.o -MD -MP -MF $(DEPDIR)/nghttpx_unittest-nghttp2_gzip_test.Tpo -c -o nghttpx_unittest-nghttp2_gzip_test.o `test -f 'nghttp2_gzip_test.c' || echo '$(srcdir)/'`nghttp2_gzip_test.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nghttpx_unittest-nghttp2_gzip_test.Tpo $(DEPDIR)/nghttpx_unittest-nghttp2_gzip_test.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='nghttp2_gzip_test.c' object='nghttpx_unittest-nghttp2_gzip_test.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o nghttpx_unittest-nghttp2_gzip_test.o `test -f 'nghttp2_gzip_test.c' || echo '$(srcdir)/'`nghttp2_gzip_test.c + +nghttpx_unittest-nghttp2_gzip_test.obj: nghttp2_gzip_test.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT nghttpx_unittest-nghttp2_gzip_test.obj -MD -MP -MF $(DEPDIR)/nghttpx_unittest-nghttp2_gzip_test.Tpo -c -o nghttpx_unittest-nghttp2_gzip_test.obj `if test -f 'nghttp2_gzip_test.c'; then $(CYGPATH_W) 'nghttp2_gzip_test.c'; else $(CYGPATH_W) '$(srcdir)/nghttp2_gzip_test.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nghttpx_unittest-nghttp2_gzip_test.Tpo $(DEPDIR)/nghttpx_unittest-nghttp2_gzip_test.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='nghttp2_gzip_test.c' object='nghttpx_unittest-nghttp2_gzip_test.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o nghttpx_unittest-nghttp2_gzip_test.obj `if test -f 'nghttp2_gzip_test.c'; then $(CYGPATH_W) 'nghttp2_gzip_test.c'; else $(CYGPATH_W) '$(srcdir)/nghttp2_gzip_test.c'; fi` + +nghttpx_unittest-nghttp2_gzip.o: nghttp2_gzip.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT nghttpx_unittest-nghttp2_gzip.o -MD -MP -MF $(DEPDIR)/nghttpx_unittest-nghttp2_gzip.Tpo -c -o nghttpx_unittest-nghttp2_gzip.o `test -f 'nghttp2_gzip.c' || echo '$(srcdir)/'`nghttp2_gzip.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nghttpx_unittest-nghttp2_gzip.Tpo $(DEPDIR)/nghttpx_unittest-nghttp2_gzip.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='nghttp2_gzip.c' object='nghttpx_unittest-nghttp2_gzip.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o nghttpx_unittest-nghttp2_gzip.o `test -f 'nghttp2_gzip.c' || echo '$(srcdir)/'`nghttp2_gzip.c + +nghttpx_unittest-nghttp2_gzip.obj: nghttp2_gzip.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT nghttpx_unittest-nghttp2_gzip.obj -MD -MP -MF $(DEPDIR)/nghttpx_unittest-nghttp2_gzip.Tpo -c -o nghttpx_unittest-nghttp2_gzip.obj `if test -f 'nghttp2_gzip.c'; then $(CYGPATH_W) 'nghttp2_gzip.c'; else $(CYGPATH_W) '$(srcdir)/nghttp2_gzip.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nghttpx_unittest-nghttp2_gzip.Tpo $(DEPDIR)/nghttpx_unittest-nghttp2_gzip.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='nghttp2_gzip.c' object='nghttpx_unittest-nghttp2_gzip.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o nghttpx_unittest-nghttp2_gzip.obj `if test -f 'nghttp2_gzip.c'; then $(CYGPATH_W) 'nghttp2_gzip.c'; else $(CYGPATH_W) '$(srcdir)/nghttp2_gzip.c'; fi` + +.cc.o: +@am__fastdepCXX_TRUE@ $(AM_V_CXX)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\ +@am__fastdepCXX_TRUE@ $(CXXCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCXX_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ $< + +.cc.obj: +@am__fastdepCXX_TRUE@ $(AM_V_CXX)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\ +@am__fastdepCXX_TRUE@ $(CXXCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\ +@am__fastdepCXX_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.cc.lo: +@am__fastdepCXX_TRUE@ $(AM_V_CXX)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\ +@am__fastdepCXX_TRUE@ $(LTCXXCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCXX_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LTCXXCOMPILE) -c -o $@ $< + +libnghttp2_asio_la-util.lo: util.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttp2_asio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttp2_asio_la-util.lo -MD -MP -MF $(DEPDIR)/libnghttp2_asio_la-util.Tpo -c -o libnghttp2_asio_la-util.lo `test -f 'util.cc' || echo '$(srcdir)/'`util.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttp2_asio_la-util.Tpo $(DEPDIR)/libnghttp2_asio_la-util.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='util.cc' object='libnghttp2_asio_la-util.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttp2_asio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttp2_asio_la-util.lo `test -f 'util.cc' || echo '$(srcdir)/'`util.cc + +libnghttp2_asio_la-http2.lo: http2.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttp2_asio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttp2_asio_la-http2.lo -MD -MP -MF $(DEPDIR)/libnghttp2_asio_la-http2.Tpo -c -o libnghttp2_asio_la-http2.lo `test -f 'http2.cc' || echo '$(srcdir)/'`http2.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttp2_asio_la-http2.Tpo $(DEPDIR)/libnghttp2_asio_la-http2.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='http2.cc' object='libnghttp2_asio_la-http2.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttp2_asio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttp2_asio_la-http2.lo `test -f 'http2.cc' || echo '$(srcdir)/'`http2.cc + +libnghttp2_asio_la-ssl.lo: ssl.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttp2_asio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttp2_asio_la-ssl.lo -MD -MP -MF $(DEPDIR)/libnghttp2_asio_la-ssl.Tpo -c -o libnghttp2_asio_la-ssl.lo `test -f 'ssl.cc' || echo '$(srcdir)/'`ssl.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttp2_asio_la-ssl.Tpo $(DEPDIR)/libnghttp2_asio_la-ssl.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='ssl.cc' object='libnghttp2_asio_la-ssl.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttp2_asio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttp2_asio_la-ssl.lo `test -f 'ssl.cc' || echo '$(srcdir)/'`ssl.cc + +libnghttp2_asio_la-asio_common.lo: asio_common.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttp2_asio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttp2_asio_la-asio_common.lo -MD -MP -MF $(DEPDIR)/libnghttp2_asio_la-asio_common.Tpo -c -o libnghttp2_asio_la-asio_common.lo `test -f 'asio_common.cc' || echo '$(srcdir)/'`asio_common.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttp2_asio_la-asio_common.Tpo $(DEPDIR)/libnghttp2_asio_la-asio_common.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='asio_common.cc' object='libnghttp2_asio_la-asio_common.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttp2_asio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttp2_asio_la-asio_common.lo `test -f 'asio_common.cc' || echo '$(srcdir)/'`asio_common.cc + +libnghttp2_asio_la-asio_io_service_pool.lo: asio_io_service_pool.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttp2_asio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttp2_asio_la-asio_io_service_pool.lo -MD -MP -MF $(DEPDIR)/libnghttp2_asio_la-asio_io_service_pool.Tpo -c -o libnghttp2_asio_la-asio_io_service_pool.lo `test -f 'asio_io_service_pool.cc' || echo '$(srcdir)/'`asio_io_service_pool.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttp2_asio_la-asio_io_service_pool.Tpo $(DEPDIR)/libnghttp2_asio_la-asio_io_service_pool.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='asio_io_service_pool.cc' object='libnghttp2_asio_la-asio_io_service_pool.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttp2_asio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttp2_asio_la-asio_io_service_pool.lo `test -f 'asio_io_service_pool.cc' || echo '$(srcdir)/'`asio_io_service_pool.cc + +libnghttp2_asio_la-asio_server_http2.lo: asio_server_http2.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttp2_asio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttp2_asio_la-asio_server_http2.lo -MD -MP -MF $(DEPDIR)/libnghttp2_asio_la-asio_server_http2.Tpo -c -o libnghttp2_asio_la-asio_server_http2.lo `test -f 'asio_server_http2.cc' || echo '$(srcdir)/'`asio_server_http2.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttp2_asio_la-asio_server_http2.Tpo $(DEPDIR)/libnghttp2_asio_la-asio_server_http2.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='asio_server_http2.cc' object='libnghttp2_asio_la-asio_server_http2.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttp2_asio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttp2_asio_la-asio_server_http2.lo `test -f 'asio_server_http2.cc' || echo '$(srcdir)/'`asio_server_http2.cc + +libnghttp2_asio_la-asio_server_http2_impl.lo: asio_server_http2_impl.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttp2_asio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttp2_asio_la-asio_server_http2_impl.lo -MD -MP -MF $(DEPDIR)/libnghttp2_asio_la-asio_server_http2_impl.Tpo -c -o libnghttp2_asio_la-asio_server_http2_impl.lo `test -f 'asio_server_http2_impl.cc' || echo '$(srcdir)/'`asio_server_http2_impl.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttp2_asio_la-asio_server_http2_impl.Tpo $(DEPDIR)/libnghttp2_asio_la-asio_server_http2_impl.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='asio_server_http2_impl.cc' object='libnghttp2_asio_la-asio_server_http2_impl.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttp2_asio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttp2_asio_la-asio_server_http2_impl.lo `test -f 'asio_server_http2_impl.cc' || echo '$(srcdir)/'`asio_server_http2_impl.cc + +libnghttp2_asio_la-asio_server.lo: asio_server.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttp2_asio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttp2_asio_la-asio_server.lo -MD -MP -MF $(DEPDIR)/libnghttp2_asio_la-asio_server.Tpo -c -o libnghttp2_asio_la-asio_server.lo `test -f 'asio_server.cc' || echo '$(srcdir)/'`asio_server.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttp2_asio_la-asio_server.Tpo $(DEPDIR)/libnghttp2_asio_la-asio_server.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='asio_server.cc' object='libnghttp2_asio_la-asio_server.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttp2_asio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttp2_asio_la-asio_server.lo `test -f 'asio_server.cc' || echo '$(srcdir)/'`asio_server.cc + +libnghttp2_asio_la-asio_server_http2_handler.lo: asio_server_http2_handler.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttp2_asio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttp2_asio_la-asio_server_http2_handler.lo -MD -MP -MF $(DEPDIR)/libnghttp2_asio_la-asio_server_http2_handler.Tpo -c -o libnghttp2_asio_la-asio_server_http2_handler.lo `test -f 'asio_server_http2_handler.cc' || echo '$(srcdir)/'`asio_server_http2_handler.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttp2_asio_la-asio_server_http2_handler.Tpo $(DEPDIR)/libnghttp2_asio_la-asio_server_http2_handler.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='asio_server_http2_handler.cc' object='libnghttp2_asio_la-asio_server_http2_handler.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttp2_asio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttp2_asio_la-asio_server_http2_handler.lo `test -f 'asio_server_http2_handler.cc' || echo '$(srcdir)/'`asio_server_http2_handler.cc + +libnghttp2_asio_la-asio_server_request.lo: asio_server_request.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttp2_asio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttp2_asio_la-asio_server_request.lo -MD -MP -MF $(DEPDIR)/libnghttp2_asio_la-asio_server_request.Tpo -c -o libnghttp2_asio_la-asio_server_request.lo `test -f 'asio_server_request.cc' || echo '$(srcdir)/'`asio_server_request.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttp2_asio_la-asio_server_request.Tpo $(DEPDIR)/libnghttp2_asio_la-asio_server_request.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='asio_server_request.cc' object='libnghttp2_asio_la-asio_server_request.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttp2_asio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttp2_asio_la-asio_server_request.lo `test -f 'asio_server_request.cc' || echo '$(srcdir)/'`asio_server_request.cc + +libnghttp2_asio_la-asio_server_request_impl.lo: asio_server_request_impl.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttp2_asio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttp2_asio_la-asio_server_request_impl.lo -MD -MP -MF $(DEPDIR)/libnghttp2_asio_la-asio_server_request_impl.Tpo -c -o libnghttp2_asio_la-asio_server_request_impl.lo `test -f 'asio_server_request_impl.cc' || echo '$(srcdir)/'`asio_server_request_impl.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttp2_asio_la-asio_server_request_impl.Tpo $(DEPDIR)/libnghttp2_asio_la-asio_server_request_impl.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='asio_server_request_impl.cc' object='libnghttp2_asio_la-asio_server_request_impl.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttp2_asio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttp2_asio_la-asio_server_request_impl.lo `test -f 'asio_server_request_impl.cc' || echo '$(srcdir)/'`asio_server_request_impl.cc + +libnghttp2_asio_la-asio_server_response.lo: asio_server_response.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttp2_asio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttp2_asio_la-asio_server_response.lo -MD -MP -MF $(DEPDIR)/libnghttp2_asio_la-asio_server_response.Tpo -c -o libnghttp2_asio_la-asio_server_response.lo `test -f 'asio_server_response.cc' || echo '$(srcdir)/'`asio_server_response.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttp2_asio_la-asio_server_response.Tpo $(DEPDIR)/libnghttp2_asio_la-asio_server_response.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='asio_server_response.cc' object='libnghttp2_asio_la-asio_server_response.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttp2_asio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttp2_asio_la-asio_server_response.lo `test -f 'asio_server_response.cc' || echo '$(srcdir)/'`asio_server_response.cc + +libnghttp2_asio_la-asio_server_response_impl.lo: asio_server_response_impl.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttp2_asio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttp2_asio_la-asio_server_response_impl.lo -MD -MP -MF $(DEPDIR)/libnghttp2_asio_la-asio_server_response_impl.Tpo -c -o libnghttp2_asio_la-asio_server_response_impl.lo `test -f 'asio_server_response_impl.cc' || echo '$(srcdir)/'`asio_server_response_impl.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttp2_asio_la-asio_server_response_impl.Tpo $(DEPDIR)/libnghttp2_asio_la-asio_server_response_impl.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='asio_server_response_impl.cc' object='libnghttp2_asio_la-asio_server_response_impl.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttp2_asio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttp2_asio_la-asio_server_response_impl.lo `test -f 'asio_server_response_impl.cc' || echo '$(srcdir)/'`asio_server_response_impl.cc + +libnghttp2_asio_la-asio_server_stream.lo: asio_server_stream.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttp2_asio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttp2_asio_la-asio_server_stream.lo -MD -MP -MF $(DEPDIR)/libnghttp2_asio_la-asio_server_stream.Tpo -c -o libnghttp2_asio_la-asio_server_stream.lo `test -f 'asio_server_stream.cc' || echo '$(srcdir)/'`asio_server_stream.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttp2_asio_la-asio_server_stream.Tpo $(DEPDIR)/libnghttp2_asio_la-asio_server_stream.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='asio_server_stream.cc' object='libnghttp2_asio_la-asio_server_stream.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttp2_asio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttp2_asio_la-asio_server_stream.lo `test -f 'asio_server_stream.cc' || echo '$(srcdir)/'`asio_server_stream.cc + +libnghttp2_asio_la-asio_server_serve_mux.lo: asio_server_serve_mux.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttp2_asio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttp2_asio_la-asio_server_serve_mux.lo -MD -MP -MF $(DEPDIR)/libnghttp2_asio_la-asio_server_serve_mux.Tpo -c -o libnghttp2_asio_la-asio_server_serve_mux.lo `test -f 'asio_server_serve_mux.cc' || echo '$(srcdir)/'`asio_server_serve_mux.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttp2_asio_la-asio_server_serve_mux.Tpo $(DEPDIR)/libnghttp2_asio_la-asio_server_serve_mux.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='asio_server_serve_mux.cc' object='libnghttp2_asio_la-asio_server_serve_mux.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttp2_asio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttp2_asio_la-asio_server_serve_mux.lo `test -f 'asio_server_serve_mux.cc' || echo '$(srcdir)/'`asio_server_serve_mux.cc + +libnghttp2_asio_la-asio_server_request_handler.lo: asio_server_request_handler.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttp2_asio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttp2_asio_la-asio_server_request_handler.lo -MD -MP -MF $(DEPDIR)/libnghttp2_asio_la-asio_server_request_handler.Tpo -c -o libnghttp2_asio_la-asio_server_request_handler.lo `test -f 'asio_server_request_handler.cc' || echo '$(srcdir)/'`asio_server_request_handler.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttp2_asio_la-asio_server_request_handler.Tpo $(DEPDIR)/libnghttp2_asio_la-asio_server_request_handler.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='asio_server_request_handler.cc' object='libnghttp2_asio_la-asio_server_request_handler.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttp2_asio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttp2_asio_la-asio_server_request_handler.lo `test -f 'asio_server_request_handler.cc' || echo '$(srcdir)/'`asio_server_request_handler.cc + +libnghttp2_asio_la-asio_server_tls_context.lo: asio_server_tls_context.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttp2_asio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttp2_asio_la-asio_server_tls_context.lo -MD -MP -MF $(DEPDIR)/libnghttp2_asio_la-asio_server_tls_context.Tpo -c -o libnghttp2_asio_la-asio_server_tls_context.lo `test -f 'asio_server_tls_context.cc' || echo '$(srcdir)/'`asio_server_tls_context.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttp2_asio_la-asio_server_tls_context.Tpo $(DEPDIR)/libnghttp2_asio_la-asio_server_tls_context.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='asio_server_tls_context.cc' object='libnghttp2_asio_la-asio_server_tls_context.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttp2_asio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttp2_asio_la-asio_server_tls_context.lo `test -f 'asio_server_tls_context.cc' || echo '$(srcdir)/'`asio_server_tls_context.cc + +libnghttp2_asio_la-asio_client_session.lo: asio_client_session.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttp2_asio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttp2_asio_la-asio_client_session.lo -MD -MP -MF $(DEPDIR)/libnghttp2_asio_la-asio_client_session.Tpo -c -o libnghttp2_asio_la-asio_client_session.lo `test -f 'asio_client_session.cc' || echo '$(srcdir)/'`asio_client_session.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttp2_asio_la-asio_client_session.Tpo $(DEPDIR)/libnghttp2_asio_la-asio_client_session.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='asio_client_session.cc' object='libnghttp2_asio_la-asio_client_session.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttp2_asio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttp2_asio_la-asio_client_session.lo `test -f 'asio_client_session.cc' || echo '$(srcdir)/'`asio_client_session.cc + +libnghttp2_asio_la-asio_client_session_impl.lo: asio_client_session_impl.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttp2_asio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttp2_asio_la-asio_client_session_impl.lo -MD -MP -MF $(DEPDIR)/libnghttp2_asio_la-asio_client_session_impl.Tpo -c -o libnghttp2_asio_la-asio_client_session_impl.lo `test -f 'asio_client_session_impl.cc' || echo '$(srcdir)/'`asio_client_session_impl.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttp2_asio_la-asio_client_session_impl.Tpo $(DEPDIR)/libnghttp2_asio_la-asio_client_session_impl.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='asio_client_session_impl.cc' object='libnghttp2_asio_la-asio_client_session_impl.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttp2_asio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttp2_asio_la-asio_client_session_impl.lo `test -f 'asio_client_session_impl.cc' || echo '$(srcdir)/'`asio_client_session_impl.cc + +libnghttp2_asio_la-asio_client_session_tcp_impl.lo: asio_client_session_tcp_impl.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttp2_asio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttp2_asio_la-asio_client_session_tcp_impl.lo -MD -MP -MF $(DEPDIR)/libnghttp2_asio_la-asio_client_session_tcp_impl.Tpo -c -o libnghttp2_asio_la-asio_client_session_tcp_impl.lo `test -f 'asio_client_session_tcp_impl.cc' || echo '$(srcdir)/'`asio_client_session_tcp_impl.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttp2_asio_la-asio_client_session_tcp_impl.Tpo $(DEPDIR)/libnghttp2_asio_la-asio_client_session_tcp_impl.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='asio_client_session_tcp_impl.cc' object='libnghttp2_asio_la-asio_client_session_tcp_impl.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttp2_asio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttp2_asio_la-asio_client_session_tcp_impl.lo `test -f 'asio_client_session_tcp_impl.cc' || echo '$(srcdir)/'`asio_client_session_tcp_impl.cc + +libnghttp2_asio_la-asio_client_session_tls_impl.lo: asio_client_session_tls_impl.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttp2_asio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttp2_asio_la-asio_client_session_tls_impl.lo -MD -MP -MF $(DEPDIR)/libnghttp2_asio_la-asio_client_session_tls_impl.Tpo -c -o libnghttp2_asio_la-asio_client_session_tls_impl.lo `test -f 'asio_client_session_tls_impl.cc' || echo '$(srcdir)/'`asio_client_session_tls_impl.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttp2_asio_la-asio_client_session_tls_impl.Tpo $(DEPDIR)/libnghttp2_asio_la-asio_client_session_tls_impl.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='asio_client_session_tls_impl.cc' object='libnghttp2_asio_la-asio_client_session_tls_impl.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttp2_asio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttp2_asio_la-asio_client_session_tls_impl.lo `test -f 'asio_client_session_tls_impl.cc' || echo '$(srcdir)/'`asio_client_session_tls_impl.cc + +libnghttp2_asio_la-asio_client_response.lo: asio_client_response.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttp2_asio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttp2_asio_la-asio_client_response.lo -MD -MP -MF $(DEPDIR)/libnghttp2_asio_la-asio_client_response.Tpo -c -o libnghttp2_asio_la-asio_client_response.lo `test -f 'asio_client_response.cc' || echo '$(srcdir)/'`asio_client_response.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttp2_asio_la-asio_client_response.Tpo $(DEPDIR)/libnghttp2_asio_la-asio_client_response.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='asio_client_response.cc' object='libnghttp2_asio_la-asio_client_response.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttp2_asio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttp2_asio_la-asio_client_response.lo `test -f 'asio_client_response.cc' || echo '$(srcdir)/'`asio_client_response.cc + +libnghttp2_asio_la-asio_client_response_impl.lo: asio_client_response_impl.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttp2_asio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttp2_asio_la-asio_client_response_impl.lo -MD -MP -MF $(DEPDIR)/libnghttp2_asio_la-asio_client_response_impl.Tpo -c -o libnghttp2_asio_la-asio_client_response_impl.lo `test -f 'asio_client_response_impl.cc' || echo '$(srcdir)/'`asio_client_response_impl.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttp2_asio_la-asio_client_response_impl.Tpo $(DEPDIR)/libnghttp2_asio_la-asio_client_response_impl.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='asio_client_response_impl.cc' object='libnghttp2_asio_la-asio_client_response_impl.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttp2_asio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttp2_asio_la-asio_client_response_impl.lo `test -f 'asio_client_response_impl.cc' || echo '$(srcdir)/'`asio_client_response_impl.cc + +libnghttp2_asio_la-asio_client_request.lo: asio_client_request.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttp2_asio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttp2_asio_la-asio_client_request.lo -MD -MP -MF $(DEPDIR)/libnghttp2_asio_la-asio_client_request.Tpo -c -o libnghttp2_asio_la-asio_client_request.lo `test -f 'asio_client_request.cc' || echo '$(srcdir)/'`asio_client_request.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttp2_asio_la-asio_client_request.Tpo $(DEPDIR)/libnghttp2_asio_la-asio_client_request.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='asio_client_request.cc' object='libnghttp2_asio_la-asio_client_request.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttp2_asio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttp2_asio_la-asio_client_request.lo `test -f 'asio_client_request.cc' || echo '$(srcdir)/'`asio_client_request.cc + +libnghttp2_asio_la-asio_client_request_impl.lo: asio_client_request_impl.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttp2_asio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttp2_asio_la-asio_client_request_impl.lo -MD -MP -MF $(DEPDIR)/libnghttp2_asio_la-asio_client_request_impl.Tpo -c -o libnghttp2_asio_la-asio_client_request_impl.lo `test -f 'asio_client_request_impl.cc' || echo '$(srcdir)/'`asio_client_request_impl.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttp2_asio_la-asio_client_request_impl.Tpo $(DEPDIR)/libnghttp2_asio_la-asio_client_request_impl.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='asio_client_request_impl.cc' object='libnghttp2_asio_la-asio_client_request_impl.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttp2_asio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttp2_asio_la-asio_client_request_impl.lo `test -f 'asio_client_request_impl.cc' || echo '$(srcdir)/'`asio_client_request_impl.cc + +libnghttp2_asio_la-asio_client_stream.lo: asio_client_stream.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttp2_asio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttp2_asio_la-asio_client_stream.lo -MD -MP -MF $(DEPDIR)/libnghttp2_asio_la-asio_client_stream.Tpo -c -o libnghttp2_asio_la-asio_client_stream.lo `test -f 'asio_client_stream.cc' || echo '$(srcdir)/'`asio_client_stream.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttp2_asio_la-asio_client_stream.Tpo $(DEPDIR)/libnghttp2_asio_la-asio_client_stream.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='asio_client_stream.cc' object='libnghttp2_asio_la-asio_client_stream.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttp2_asio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttp2_asio_la-asio_client_stream.lo `test -f 'asio_client_stream.cc' || echo '$(srcdir)/'`asio_client_stream.cc + +libnghttp2_asio_la-asio_client_tls_context.lo: asio_client_tls_context.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttp2_asio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libnghttp2_asio_la-asio_client_tls_context.lo -MD -MP -MF $(DEPDIR)/libnghttp2_asio_la-asio_client_tls_context.Tpo -c -o libnghttp2_asio_la-asio_client_tls_context.lo `test -f 'asio_client_tls_context.cc' || echo '$(srcdir)/'`asio_client_tls_context.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnghttp2_asio_la-asio_client_tls_context.Tpo $(DEPDIR)/libnghttp2_asio_la-asio_client_tls_context.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='asio_client_tls_context.cc' object='libnghttp2_asio_la-asio_client_tls_context.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnghttp2_asio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libnghttp2_asio_la-asio_client_tls_context.lo `test -f 'asio_client_tls_context.cc' || echo '$(srcdir)/'`asio_client_tls_context.cc + +nghttpx_unittest-shrpx-unittest.o: shrpx-unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT nghttpx_unittest-shrpx-unittest.o -MD -MP -MF $(DEPDIR)/nghttpx_unittest-shrpx-unittest.Tpo -c -o nghttpx_unittest-shrpx-unittest.o `test -f 'shrpx-unittest.cc' || echo '$(srcdir)/'`shrpx-unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nghttpx_unittest-shrpx-unittest.Tpo $(DEPDIR)/nghttpx_unittest-shrpx-unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='shrpx-unittest.cc' object='nghttpx_unittest-shrpx-unittest.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o nghttpx_unittest-shrpx-unittest.o `test -f 'shrpx-unittest.cc' || echo '$(srcdir)/'`shrpx-unittest.cc + +nghttpx_unittest-shrpx-unittest.obj: shrpx-unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT nghttpx_unittest-shrpx-unittest.obj -MD -MP -MF $(DEPDIR)/nghttpx_unittest-shrpx-unittest.Tpo -c -o nghttpx_unittest-shrpx-unittest.obj `if test -f 'shrpx-unittest.cc'; then $(CYGPATH_W) 'shrpx-unittest.cc'; else $(CYGPATH_W) '$(srcdir)/shrpx-unittest.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nghttpx_unittest-shrpx-unittest.Tpo $(DEPDIR)/nghttpx_unittest-shrpx-unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='shrpx-unittest.cc' object='nghttpx_unittest-shrpx-unittest.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o nghttpx_unittest-shrpx-unittest.obj `if test -f 'shrpx-unittest.cc'; then $(CYGPATH_W) 'shrpx-unittest.cc'; else $(CYGPATH_W) '$(srcdir)/shrpx-unittest.cc'; fi` + +nghttpx_unittest-shrpx_ssl_test.o: shrpx_ssl_test.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT nghttpx_unittest-shrpx_ssl_test.o -MD -MP -MF $(DEPDIR)/nghttpx_unittest-shrpx_ssl_test.Tpo -c -o nghttpx_unittest-shrpx_ssl_test.o `test -f 'shrpx_ssl_test.cc' || echo '$(srcdir)/'`shrpx_ssl_test.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nghttpx_unittest-shrpx_ssl_test.Tpo $(DEPDIR)/nghttpx_unittest-shrpx_ssl_test.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='shrpx_ssl_test.cc' object='nghttpx_unittest-shrpx_ssl_test.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o nghttpx_unittest-shrpx_ssl_test.o `test -f 'shrpx_ssl_test.cc' || echo '$(srcdir)/'`shrpx_ssl_test.cc + +nghttpx_unittest-shrpx_ssl_test.obj: shrpx_ssl_test.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT nghttpx_unittest-shrpx_ssl_test.obj -MD -MP -MF $(DEPDIR)/nghttpx_unittest-shrpx_ssl_test.Tpo -c -o nghttpx_unittest-shrpx_ssl_test.obj `if test -f 'shrpx_ssl_test.cc'; then $(CYGPATH_W) 'shrpx_ssl_test.cc'; else $(CYGPATH_W) '$(srcdir)/shrpx_ssl_test.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nghttpx_unittest-shrpx_ssl_test.Tpo $(DEPDIR)/nghttpx_unittest-shrpx_ssl_test.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='shrpx_ssl_test.cc' object='nghttpx_unittest-shrpx_ssl_test.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o nghttpx_unittest-shrpx_ssl_test.obj `if test -f 'shrpx_ssl_test.cc'; then $(CYGPATH_W) 'shrpx_ssl_test.cc'; else $(CYGPATH_W) '$(srcdir)/shrpx_ssl_test.cc'; fi` + +nghttpx_unittest-shrpx_downstream_test.o: shrpx_downstream_test.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT nghttpx_unittest-shrpx_downstream_test.o -MD -MP -MF $(DEPDIR)/nghttpx_unittest-shrpx_downstream_test.Tpo -c -o nghttpx_unittest-shrpx_downstream_test.o `test -f 'shrpx_downstream_test.cc' || echo '$(srcdir)/'`shrpx_downstream_test.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nghttpx_unittest-shrpx_downstream_test.Tpo $(DEPDIR)/nghttpx_unittest-shrpx_downstream_test.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='shrpx_downstream_test.cc' object='nghttpx_unittest-shrpx_downstream_test.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o nghttpx_unittest-shrpx_downstream_test.o `test -f 'shrpx_downstream_test.cc' || echo '$(srcdir)/'`shrpx_downstream_test.cc + +nghttpx_unittest-shrpx_downstream_test.obj: shrpx_downstream_test.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT nghttpx_unittest-shrpx_downstream_test.obj -MD -MP -MF $(DEPDIR)/nghttpx_unittest-shrpx_downstream_test.Tpo -c -o nghttpx_unittest-shrpx_downstream_test.obj `if test -f 'shrpx_downstream_test.cc'; then $(CYGPATH_W) 'shrpx_downstream_test.cc'; else $(CYGPATH_W) '$(srcdir)/shrpx_downstream_test.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nghttpx_unittest-shrpx_downstream_test.Tpo $(DEPDIR)/nghttpx_unittest-shrpx_downstream_test.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='shrpx_downstream_test.cc' object='nghttpx_unittest-shrpx_downstream_test.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o nghttpx_unittest-shrpx_downstream_test.obj `if test -f 'shrpx_downstream_test.cc'; then $(CYGPATH_W) 'shrpx_downstream_test.cc'; else $(CYGPATH_W) '$(srcdir)/shrpx_downstream_test.cc'; fi` + +nghttpx_unittest-shrpx_config_test.o: shrpx_config_test.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT nghttpx_unittest-shrpx_config_test.o -MD -MP -MF $(DEPDIR)/nghttpx_unittest-shrpx_config_test.Tpo -c -o nghttpx_unittest-shrpx_config_test.o `test -f 'shrpx_config_test.cc' || echo '$(srcdir)/'`shrpx_config_test.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nghttpx_unittest-shrpx_config_test.Tpo $(DEPDIR)/nghttpx_unittest-shrpx_config_test.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='shrpx_config_test.cc' object='nghttpx_unittest-shrpx_config_test.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o nghttpx_unittest-shrpx_config_test.o `test -f 'shrpx_config_test.cc' || echo '$(srcdir)/'`shrpx_config_test.cc + +nghttpx_unittest-shrpx_config_test.obj: shrpx_config_test.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT nghttpx_unittest-shrpx_config_test.obj -MD -MP -MF $(DEPDIR)/nghttpx_unittest-shrpx_config_test.Tpo -c -o nghttpx_unittest-shrpx_config_test.obj `if test -f 'shrpx_config_test.cc'; then $(CYGPATH_W) 'shrpx_config_test.cc'; else $(CYGPATH_W) '$(srcdir)/shrpx_config_test.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nghttpx_unittest-shrpx_config_test.Tpo $(DEPDIR)/nghttpx_unittest-shrpx_config_test.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='shrpx_config_test.cc' object='nghttpx_unittest-shrpx_config_test.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o nghttpx_unittest-shrpx_config_test.obj `if test -f 'shrpx_config_test.cc'; then $(CYGPATH_W) 'shrpx_config_test.cc'; else $(CYGPATH_W) '$(srcdir)/shrpx_config_test.cc'; fi` + +nghttpx_unittest-http2_test.o: http2_test.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT nghttpx_unittest-http2_test.o -MD -MP -MF $(DEPDIR)/nghttpx_unittest-http2_test.Tpo -c -o nghttpx_unittest-http2_test.o `test -f 'http2_test.cc' || echo '$(srcdir)/'`http2_test.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nghttpx_unittest-http2_test.Tpo $(DEPDIR)/nghttpx_unittest-http2_test.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='http2_test.cc' object='nghttpx_unittest-http2_test.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o nghttpx_unittest-http2_test.o `test -f 'http2_test.cc' || echo '$(srcdir)/'`http2_test.cc + +nghttpx_unittest-http2_test.obj: http2_test.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT nghttpx_unittest-http2_test.obj -MD -MP -MF $(DEPDIR)/nghttpx_unittest-http2_test.Tpo -c -o nghttpx_unittest-http2_test.obj `if test -f 'http2_test.cc'; then $(CYGPATH_W) 'http2_test.cc'; else $(CYGPATH_W) '$(srcdir)/http2_test.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nghttpx_unittest-http2_test.Tpo $(DEPDIR)/nghttpx_unittest-http2_test.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='http2_test.cc' object='nghttpx_unittest-http2_test.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o nghttpx_unittest-http2_test.obj `if test -f 'http2_test.cc'; then $(CYGPATH_W) 'http2_test.cc'; else $(CYGPATH_W) '$(srcdir)/http2_test.cc'; fi` + +nghttpx_unittest-util_test.o: util_test.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT nghttpx_unittest-util_test.o -MD -MP -MF $(DEPDIR)/nghttpx_unittest-util_test.Tpo -c -o nghttpx_unittest-util_test.o `test -f 'util_test.cc' || echo '$(srcdir)/'`util_test.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nghttpx_unittest-util_test.Tpo $(DEPDIR)/nghttpx_unittest-util_test.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='util_test.cc' object='nghttpx_unittest-util_test.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o nghttpx_unittest-util_test.o `test -f 'util_test.cc' || echo '$(srcdir)/'`util_test.cc + +nghttpx_unittest-util_test.obj: util_test.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT nghttpx_unittest-util_test.obj -MD -MP -MF $(DEPDIR)/nghttpx_unittest-util_test.Tpo -c -o nghttpx_unittest-util_test.obj `if test -f 'util_test.cc'; then $(CYGPATH_W) 'util_test.cc'; else $(CYGPATH_W) '$(srcdir)/util_test.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nghttpx_unittest-util_test.Tpo $(DEPDIR)/nghttpx_unittest-util_test.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='util_test.cc' object='nghttpx_unittest-util_test.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o nghttpx_unittest-util_test.obj `if test -f 'util_test.cc'; then $(CYGPATH_W) 'util_test.cc'; else $(CYGPATH_W) '$(srcdir)/util_test.cc'; fi` + +nghttpx_unittest-buffer_test.o: buffer_test.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT nghttpx_unittest-buffer_test.o -MD -MP -MF $(DEPDIR)/nghttpx_unittest-buffer_test.Tpo -c -o nghttpx_unittest-buffer_test.o `test -f 'buffer_test.cc' || echo '$(srcdir)/'`buffer_test.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nghttpx_unittest-buffer_test.Tpo $(DEPDIR)/nghttpx_unittest-buffer_test.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='buffer_test.cc' object='nghttpx_unittest-buffer_test.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o nghttpx_unittest-buffer_test.o `test -f 'buffer_test.cc' || echo '$(srcdir)/'`buffer_test.cc + +nghttpx_unittest-buffer_test.obj: buffer_test.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT nghttpx_unittest-buffer_test.obj -MD -MP -MF $(DEPDIR)/nghttpx_unittest-buffer_test.Tpo -c -o nghttpx_unittest-buffer_test.obj `if test -f 'buffer_test.cc'; then $(CYGPATH_W) 'buffer_test.cc'; else $(CYGPATH_W) '$(srcdir)/buffer_test.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nghttpx_unittest-buffer_test.Tpo $(DEPDIR)/nghttpx_unittest-buffer_test.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='buffer_test.cc' object='nghttpx_unittest-buffer_test.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o nghttpx_unittest-buffer_test.obj `if test -f 'buffer_test.cc'; then $(CYGPATH_W) 'buffer_test.cc'; else $(CYGPATH_W) '$(srcdir)/buffer_test.cc'; fi` + +nghttpx_unittest-memchunk_test.o: memchunk_test.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT nghttpx_unittest-memchunk_test.o -MD -MP -MF $(DEPDIR)/nghttpx_unittest-memchunk_test.Tpo -c -o nghttpx_unittest-memchunk_test.o `test -f 'memchunk_test.cc' || echo '$(srcdir)/'`memchunk_test.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nghttpx_unittest-memchunk_test.Tpo $(DEPDIR)/nghttpx_unittest-memchunk_test.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='memchunk_test.cc' object='nghttpx_unittest-memchunk_test.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o nghttpx_unittest-memchunk_test.o `test -f 'memchunk_test.cc' || echo '$(srcdir)/'`memchunk_test.cc + +nghttpx_unittest-memchunk_test.obj: memchunk_test.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT nghttpx_unittest-memchunk_test.obj -MD -MP -MF $(DEPDIR)/nghttpx_unittest-memchunk_test.Tpo -c -o nghttpx_unittest-memchunk_test.obj `if test -f 'memchunk_test.cc'; then $(CYGPATH_W) 'memchunk_test.cc'; else $(CYGPATH_W) '$(srcdir)/memchunk_test.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nghttpx_unittest-memchunk_test.Tpo $(DEPDIR)/nghttpx_unittest-memchunk_test.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='memchunk_test.cc' object='nghttpx_unittest-memchunk_test.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nghttpx_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o nghttpx_unittest-memchunk_test.obj `if test -f 'memchunk_test.cc'; then $(CYGPATH_W) 'memchunk_test.cc'; else $(CYGPATH_W) '$(srcdir)/memchunk_test.cc'; fi` + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs +install-pkgconfigDATA: $(pkgconfig_DATA) + @$(NORMAL_INSTALL) + @list='$(pkgconfig_DATA)'; test -n "$(pkgconfigdir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(pkgconfigdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(pkgconfigdir)" || exit 1; \ + fi; \ + for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + echo "$$d$$p"; \ + done | $(am__base_list) | \ + while read files; do \ + echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(pkgconfigdir)'"; \ + $(INSTALL_DATA) $$files "$(DESTDIR)$(pkgconfigdir)" || exit $$?; \ + done + +uninstall-pkgconfigDATA: + @$(NORMAL_UNINSTALL) + @list='$(pkgconfig_DATA)'; test -n "$(pkgconfigdir)" || list=; \ + files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ + dir='$(DESTDIR)$(pkgconfigdir)'; $(am__uninstall_files_from_dir) + +# This directory's subdirectories are mostly independent; you can cd +# into them and run 'make' without going through this Makefile. +# To change the values of 'make' variables: instead of editing Makefiles, +# (1) if the variable is set in 'config.status', edit 'config.status' +# (which will cause the Makefiles to be regenerated when you run 'make'); +# (2) otherwise, pass the desired values on the 'make' command line. +$(am__recursive_targets): + @fail=; \ + if $(am__make_keepgoing); then \ + failcom='fail=yes'; \ + else \ + failcom='exit 1'; \ + fi; \ + dot_seen=no; \ + target=`echo $@ | sed s/-recursive//`; \ + case "$@" in \ + distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ + *) list='$(SUBDIRS)' ;; \ + esac; \ + for subdir in $$list; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + dot_seen=yes; \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || eval $$failcom; \ + done; \ + if test "$$dot_seen" = "no"; then \ + $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ + fi; test -z "$$fail" + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-recursive +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \ + include_option=--etags-include; \ + empty_fix=.; \ + else \ + include_option=--include; \ + empty_fix=; \ + fi; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + test ! -f $$subdir/TAGS || \ + set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \ + fi; \ + done; \ + $(am__define_uniq_tagged_files); \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: ctags-recursive + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" +cscopelist: cscopelist-recursive + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +# Recover from deleted '.trs' file; this should ensure that +# "rm -f foo.log; make foo.trs" re-run 'foo.test', and re-create +# both 'foo.log' and 'foo.trs'. Break the recipe in two subshells +# to avoid problems with "make -n". +.log.trs: + rm -f $< $@ + $(MAKE) $(AM_MAKEFLAGS) $< + +# Leading 'am--fnord' is there to ensure the list of targets does not +# expand to empty, as could happen e.g. with make check TESTS=''. +am--fnord $(TEST_LOGS) $(TEST_LOGS:.log=.trs): $(am__force_recheck) +am--force-recheck: + @: + +$(TEST_SUITE_LOG): $(TEST_LOGS) + @$(am__set_TESTS_bases); \ + am__f_ok () { test -f "$$1" && test -r "$$1"; }; \ + redo_bases=`for i in $$bases; do \ + am__f_ok $$i.trs && am__f_ok $$i.log || echo $$i; \ + done`; \ + if test -n "$$redo_bases"; then \ + redo_logs=`for i in $$redo_bases; do echo $$i.log; done`; \ + redo_results=`for i in $$redo_bases; do echo $$i.trs; done`; \ + if $(am__make_dryrun); then :; else \ + rm -f $$redo_logs && rm -f $$redo_results || exit 1; \ + fi; \ + fi; \ + if test -n "$$am__remaking_logs"; then \ + echo "fatal: making $(TEST_SUITE_LOG): possible infinite" \ + "recursion detected" >&2; \ + else \ + am__remaking_logs=yes $(MAKE) $(AM_MAKEFLAGS) $$redo_logs; \ + fi; \ + if $(am__make_dryrun); then :; else \ + st=0; \ + errmsg="fatal: making $(TEST_SUITE_LOG): failed to create"; \ + for i in $$redo_bases; do \ + test -f $$i.trs && test -r $$i.trs \ + || { echo "$$errmsg $$i.trs" >&2; st=1; }; \ + test -f $$i.log && test -r $$i.log \ + || { echo "$$errmsg $$i.log" >&2; st=1; }; \ + done; \ + test $$st -eq 0 || exit 1; \ + fi + @$(am__sh_e_setup); $(am__tty_colors); $(am__set_TESTS_bases); \ + ws='[ ]'; \ + results=`for b in $$bases; do echo $$b.trs; done`; \ + test -n "$$results" || results=/dev/null; \ + all=` grep "^$$ws*:test-result:" $$results | wc -l`; \ + pass=` grep "^$$ws*:test-result:$$ws*PASS" $$results | wc -l`; \ + fail=` grep "^$$ws*:test-result:$$ws*FAIL" $$results | wc -l`; \ + skip=` grep "^$$ws*:test-result:$$ws*SKIP" $$results | wc -l`; \ + xfail=`grep "^$$ws*:test-result:$$ws*XFAIL" $$results | wc -l`; \ + xpass=`grep "^$$ws*:test-result:$$ws*XPASS" $$results | wc -l`; \ + error=`grep "^$$ws*:test-result:$$ws*ERROR" $$results | wc -l`; \ + if test `expr $$fail + $$xpass + $$error` -eq 0; then \ + success=true; \ + else \ + success=false; \ + fi; \ + br='==================='; br=$$br$$br$$br$$br; \ + result_count () \ + { \ + if test x"$$1" = x"--maybe-color"; then \ + maybe_colorize=yes; \ + elif test x"$$1" = x"--no-color"; then \ + maybe_colorize=no; \ + else \ + echo "$@: invalid 'result_count' usage" >&2; exit 4; \ + fi; \ + shift; \ + desc=$$1 count=$$2; \ + if test $$maybe_colorize = yes && test $$count -gt 0; then \ + color_start=$$3 color_end=$$std; \ + else \ + color_start= color_end=; \ + fi; \ + echo "$${color_start}# $$desc $$count$${color_end}"; \ + }; \ + create_testsuite_report () \ + { \ + result_count $$1 "TOTAL:" $$all "$$brg"; \ + result_count $$1 "PASS: " $$pass "$$grn"; \ + result_count $$1 "SKIP: " $$skip "$$blu"; \ + result_count $$1 "XFAIL:" $$xfail "$$lgn"; \ + result_count $$1 "FAIL: " $$fail "$$red"; \ + result_count $$1 "XPASS:" $$xpass "$$red"; \ + result_count $$1 "ERROR:" $$error "$$mgn"; \ + }; \ + { \ + echo "$(PACKAGE_STRING): $(subdir)/$(TEST_SUITE_LOG)" | \ + $(am__rst_title); \ + create_testsuite_report --no-color; \ + echo; \ + echo ".. contents:: :depth: 2"; \ + echo; \ + for b in $$bases; do echo $$b; done \ + | $(am__create_global_log); \ + } >$(TEST_SUITE_LOG).tmp || exit 1; \ + mv $(TEST_SUITE_LOG).tmp $(TEST_SUITE_LOG); \ + if $$success; then \ + col="$$grn"; \ + else \ + col="$$red"; \ + test x"$$VERBOSE" = x || cat $(TEST_SUITE_LOG); \ + fi; \ + echo "$${col}$$br$${std}"; \ + echo "$${col}Testsuite summary for $(PACKAGE_STRING)$${std}"; \ + echo "$${col}$$br$${std}"; \ + create_testsuite_report --maybe-color; \ + echo "$$col$$br$$std"; \ + if $$success; then :; else \ + echo "$${col}See $(subdir)/$(TEST_SUITE_LOG)$${std}"; \ + if test -n "$(PACKAGE_BUGREPORT)"; then \ + echo "$${col}Please report to $(PACKAGE_BUGREPORT)$${std}"; \ + fi; \ + echo "$$col$$br$$std"; \ + fi; \ + $$success || exit 1 + +check-TESTS: + @list='$(RECHECK_LOGS)'; test -z "$$list" || rm -f $$list + @list='$(RECHECK_LOGS:.log=.trs)'; test -z "$$list" || rm -f $$list + @test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG) + @set +e; $(am__set_TESTS_bases); \ + log_list=`for i in $$bases; do echo $$i.log; done`; \ + trs_list=`for i in $$bases; do echo $$i.trs; done`; \ + log_list=`echo $$log_list`; trs_list=`echo $$trs_list`; \ + $(MAKE) $(AM_MAKEFLAGS) $(TEST_SUITE_LOG) TEST_LOGS="$$log_list"; \ + exit $$?; +recheck: all $(check_PROGRAMS) + @test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG) + @set +e; $(am__set_TESTS_bases); \ + bases=`for i in $$bases; do echo $$i; done \ + | $(am__list_recheck_tests)` || exit 1; \ + log_list=`for i in $$bases; do echo $$i.log; done`; \ + log_list=`echo $$log_list`; \ + $(MAKE) $(AM_MAKEFLAGS) $(TEST_SUITE_LOG) \ + am__force_recheck=am--force-recheck \ + TEST_LOGS="$$log_list"; \ + exit $$? +nghttpx-unittest.log: nghttpx-unittest$(EXEEXT) + @p='nghttpx-unittest$(EXEEXT)'; \ + b='nghttpx-unittest'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +.test.log: + @p='$<'; \ + $(am__set_b); \ + $(am__check_pre) $(TEST_LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_TEST_LOG_DRIVER_FLAGS) $(TEST_LOG_DRIVER_FLAGS) -- $(TEST_LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +@am__EXEEXT_TRUE@.test$(EXEEXT).log: +@am__EXEEXT_TRUE@ @p='$<'; \ +@am__EXEEXT_TRUE@ $(am__set_b); \ +@am__EXEEXT_TRUE@ $(am__check_pre) $(TEST_LOG_DRIVER) --test-name "$$f" \ +@am__EXEEXT_TRUE@ --log-file $$b.log --trs-file $$b.trs \ +@am__EXEEXT_TRUE@ $(am__common_driver_flags) $(AM_TEST_LOG_DRIVER_FLAGS) $(TEST_LOG_DRIVER_FLAGS) -- $(TEST_LOG_COMPILE) \ +@am__EXEEXT_TRUE@ "$$tst" $(AM_TESTS_FD_REDIRECT) + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done + @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + $(am__make_dryrun) \ + || test -d "$(distdir)/$$subdir" \ + || $(MKDIR_P) "$(distdir)/$$subdir" \ + || exit 1; \ + dir1=$$subdir; dir2="$(distdir)/$$subdir"; \ + $(am__relativize); \ + new_distdir=$$reldir; \ + dir1=$$subdir; dir2="$(top_distdir)"; \ + $(am__relativize); \ + new_top_distdir=$$reldir; \ + echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \ + echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \ + ($(am__cd) $$subdir && \ + $(MAKE) $(AM_MAKEFLAGS) \ + top_distdir="$$new_top_distdir" \ + distdir="$$new_distdir" \ + am__remove_distdir=: \ + am__skip_length_check=: \ + am__skip_mode_fix=: \ + distdir) \ + || exit 1; \ + fi; \ + done +check-am: all-am + $(MAKE) $(AM_MAKEFLAGS) $(check_PROGRAMS) + $(MAKE) $(AM_MAKEFLAGS) check-TESTS +check: check-recursive +all-am: Makefile $(LIBRARIES) $(LTLIBRARIES) $(PROGRAMS) $(DATA) +install-binPROGRAMS: install-libLTLIBRARIES + +installdirs: installdirs-recursive +installdirs-am: + for dir in "$(DESTDIR)$(libdir)" "$(DESTDIR)$(bindir)" "$(DESTDIR)$(pkgconfigdir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: install-recursive +install-exec: install-exec-recursive +install-data: install-data-recursive +uninstall: uninstall-recursive + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-recursive +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + -test -z "$(TEST_LOGS)" || rm -f $(TEST_LOGS) + -test -z "$(TEST_LOGS:.log=.trs)" || rm -f $(TEST_LOGS:.log=.trs) + -test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG) + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + -test -z "$(DISTCLEANFILES)" || rm -f $(DISTCLEANFILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-recursive + +clean-am: clean-binPROGRAMS clean-checkPROGRAMS clean-generic \ + clean-libLTLIBRARIES clean-libtool clean-noinstLIBRARIES \ + mostlyclean-am + +distclean: distclean-recursive + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-recursive + +dvi-am: + +html: html-recursive + +html-am: + +info: info-recursive + +info-am: + +install-data-am: install-pkgconfigDATA + +install-dvi: install-dvi-recursive + +install-dvi-am: + +install-exec-am: install-binPROGRAMS install-libLTLIBRARIES + +install-html: install-html-recursive + +install-html-am: + +install-info: install-info-recursive + +install-info-am: + +install-man: + +install-pdf: install-pdf-recursive + +install-pdf-am: + +install-ps: install-ps-recursive + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-recursive + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-recursive + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-recursive + +pdf-am: + +ps: ps-recursive + +ps-am: + +uninstall-am: uninstall-binPROGRAMS uninstall-libLTLIBRARIES \ + uninstall-pkgconfigDATA + +.MAKE: $(am__recursive_targets) check-am install-am install-strip + +.PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am check \ + check-TESTS check-am clean clean-binPROGRAMS \ + clean-checkPROGRAMS clean-generic clean-libLTLIBRARIES \ + clean-libtool clean-noinstLIBRARIES cscopelist-am ctags \ + ctags-am distclean distclean-compile distclean-generic \ + distclean-libtool distclean-tags distdir dvi dvi-am html \ + html-am info info-am install install-am install-binPROGRAMS \ + install-data install-data-am install-dvi install-dvi-am \ + install-exec install-exec-am install-html install-html-am \ + install-info install-info-am install-libLTLIBRARIES \ + install-man install-pdf install-pdf-am install-pkgconfigDATA \ + install-ps install-ps-am install-strip installcheck \ + installcheck-am installdirs installdirs-am maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-compile \ + mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ + recheck tags tags-am uninstall uninstall-am \ + uninstall-binPROGRAMS uninstall-libLTLIBRARIES \ + uninstall-pkgconfigDATA + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/src/app_helper.cc b/src/app_helper.cc new file mode 100644 index 0000000..58f8439 --- /dev/null +++ b/src/app_helper.cc @@ -0,0 +1,505 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include +#ifdef HAVE_SYS_SOCKET_H +#include +#endif // HAVE_SYS_SOCKET_H +#ifdef HAVE_NETDB_H +#include +#endif // HAVE_NETDB_H +#ifdef HAVE_UNISTD_H +#include +#endif // HAVE_UNISTD_H +#ifdef HAVE_FCNTL_H +#include +#endif // HAVE_FCNTL_H +#ifdef HAVE_NETINET_IN_H +#include +#endif // HAVE_NETINET_IN_H +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "app_helper.h" +#include "util.h" +#include "http2.h" + +namespace nghttp2 { + +namespace { +const char *strstatus(uint32_t error_code) { + switch (error_code) { + case NGHTTP2_NO_ERROR: + return "NO_ERROR"; + case NGHTTP2_PROTOCOL_ERROR: + return "PROTOCOL_ERROR"; + case NGHTTP2_INTERNAL_ERROR: + return "INTERNAL_ERROR"; + case NGHTTP2_FLOW_CONTROL_ERROR: + return "FLOW_CONTROL_ERROR"; + case NGHTTP2_SETTINGS_TIMEOUT: + return "SETTINGS_TIMEOUT"; + case NGHTTP2_STREAM_CLOSED: + return "STREAM_CLOSED"; + case NGHTTP2_FRAME_SIZE_ERROR: + return "FRAME_SIZE_ERROR"; + case NGHTTP2_REFUSED_STREAM: + return "REFUSED_STREAM"; + case NGHTTP2_CANCEL: + return "CANCEL"; + case NGHTTP2_COMPRESSION_ERROR: + return "COMPRESSION_ERROR"; + case NGHTTP2_CONNECT_ERROR: + return "CONNECT_ERROR"; + case NGHTTP2_ENHANCE_YOUR_CALM: + return "ENHANCE_YOUR_CALM"; + case NGHTTP2_INADEQUATE_SECURITY: + return "INADEQUATE_SECURITY"; + case NGHTTP2_HTTP_1_1_REQUIRED: + return "HTTP_1_1_REQUIRED"; + default: + return "UNKNOWN"; + } +} +} // namespace + +namespace { +const char *strsettingsid(int32_t id) { + switch (id) { + case NGHTTP2_SETTINGS_HEADER_TABLE_SIZE: + return "SETTINGS_HEADER_TABLE_SIZE"; + case NGHTTP2_SETTINGS_ENABLE_PUSH: + return "SETTINGS_ENABLE_PUSH"; + case NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS: + return "SETTINGS_MAX_CONCURRENT_STREAMS"; + case NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE: + return "SETTINGS_INITIAL_WINDOW_SIZE"; + case NGHTTP2_SETTINGS_MAX_FRAME_SIZE: + return "SETTINGS_MAX_FRAME_SIZE"; + case NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE: + return "SETTINGS_MAX_HEADER_LIST_SIZE"; + default: + return "UNKNOWN"; + } +} +} // namespace + +namespace { +const char *strframetype(uint8_t type) { + switch (type) { + case NGHTTP2_DATA: + return "DATA"; + case NGHTTP2_HEADERS: + return "HEADERS"; + case NGHTTP2_PRIORITY: + return "PRIORITY"; + case NGHTTP2_RST_STREAM: + return "RST_STREAM"; + case NGHTTP2_SETTINGS: + return "SETTINGS"; + case NGHTTP2_PUSH_PROMISE: + return "PUSH_PROMISE"; + case NGHTTP2_PING: + return "PING"; + case NGHTTP2_GOAWAY: + return "GOAWAY"; + case NGHTTP2_WINDOW_UPDATE: + return "WINDOW_UPDATE"; + default: + return "UNKNOWN"; + } +}; +} // namespace + +namespace { +bool color_output = false; +} // namespace + +void set_color_output(bool f) { color_output = f; } + +namespace { +FILE *outfile = stdout; +} // namespace + +void set_output(FILE *file) { outfile = file; } + +namespace { +void print_frame_attr_indent() { fprintf(outfile, " "); } +} // namespace + +namespace { +const char *ansi_esc(const char *code) { return color_output ? code : ""; } +} // namespace + +namespace { +const char *ansi_escend() { return color_output ? "\033[0m" : ""; } +} // namespace + +namespace { +void print_nv(nghttp2_nv *nv) { + fprintf(outfile, "%s%s%s: %s\n", ansi_esc("\033[1;34m"), nv->name, + ansi_escend(), nv->value); +} +} // namespace +namespace { +void print_nv(nghttp2_nv *nva, size_t nvlen) { + auto end = nva + nvlen; + for (; nva != end; ++nva) { + print_frame_attr_indent(); + + print_nv(nva); + } +} +} // namelen + +void print_timer() { + auto millis = get_timer(); + fprintf(outfile, "%s[%3ld.%03ld]%s", ansi_esc("\033[33m"), + (long int)(millis.count() / 1000), (long int)(millis.count() % 1000), + ansi_escend()); +} + +namespace { +void print_frame_hd(const nghttp2_frame_hd &hd) { + fprintf(outfile, "\n", hd.length, + hd.flags, hd.stream_id); +} +} // namespace + +namespace { +void print_flags(const nghttp2_frame_hd &hd) { + std::string s; + switch (hd.type) { + case NGHTTP2_DATA: + if (hd.flags & NGHTTP2_FLAG_END_STREAM) { + s += "END_STREAM"; + } + if (hd.flags & NGHTTP2_FLAG_PADDED) { + if (!s.empty()) { + s += " | "; + } + s += "PADDED"; + } + break; + case NGHTTP2_HEADERS: + if (hd.flags & NGHTTP2_FLAG_END_STREAM) { + s += "END_STREAM"; + } + if (hd.flags & NGHTTP2_FLAG_END_HEADERS) { + if (!s.empty()) { + s += " | "; + } + s += "END_HEADERS"; + } + if (hd.flags & NGHTTP2_FLAG_PADDED) { + if (!s.empty()) { + s += " | "; + } + s += "PADDED"; + } + if (hd.flags & NGHTTP2_FLAG_PRIORITY) { + if (!s.empty()) { + s += " | "; + } + s += "PRIORITY"; + } + + break; + case NGHTTP2_PRIORITY: + break; + case NGHTTP2_SETTINGS: + if (hd.flags & NGHTTP2_FLAG_ACK) { + s += "ACK"; + } + break; + case NGHTTP2_PUSH_PROMISE: + if (hd.flags & NGHTTP2_FLAG_END_HEADERS) { + s += "END_HEADERS"; + } + if (hd.flags & NGHTTP2_FLAG_PADDED) { + if (!s.empty()) { + s += " | "; + } + s += "PADDED"; + } + break; + case NGHTTP2_PING: + if (hd.flags & NGHTTP2_FLAG_ACK) { + s += "ACK"; + } + break; + } + fprintf(outfile, "; %s\n", s.c_str()); +} +} // namespace + +enum print_type { PRINT_SEND, PRINT_RECV }; + +namespace { +const char *frame_name_ansi_esc(print_type ptype) { + return ansi_esc(ptype == PRINT_SEND ? "\033[1;35m" : "\033[1;36m"); +} +} // namespace + +namespace { +void print_frame(print_type ptype, const nghttp2_frame *frame) { + fprintf(outfile, "%s%s%s frame ", frame_name_ansi_esc(ptype), + strframetype(frame->hd.type), ansi_escend()); + print_frame_hd(frame->hd); + if (frame->hd.flags) { + print_frame_attr_indent(); + print_flags(frame->hd); + } + switch (frame->hd.type) { + case NGHTTP2_DATA: + if (frame->data.padlen > 0) { + print_frame_attr_indent(); + fprintf(outfile, "(padlen=%zu)\n", frame->data.padlen); + } + break; + case NGHTTP2_HEADERS: + print_frame_attr_indent(); + fprintf(outfile, "(padlen=%zu", frame->headers.padlen); + if (frame->hd.flags & NGHTTP2_FLAG_PRIORITY) { + fprintf(outfile, ", dep_stream_id=%d, weight=%u, exclusive=%d", + frame->headers.pri_spec.stream_id, frame->headers.pri_spec.weight, + frame->headers.pri_spec.exclusive); + } + fprintf(outfile, ")\n"); + switch (frame->headers.cat) { + case NGHTTP2_HCAT_REQUEST: + print_frame_attr_indent(); + fprintf(outfile, "; Open new stream\n"); + break; + case NGHTTP2_HCAT_RESPONSE: + print_frame_attr_indent(); + fprintf(outfile, "; First response header\n"); + break; + case NGHTTP2_HCAT_PUSH_RESPONSE: + print_frame_attr_indent(); + fprintf(outfile, "; First push response header\n"); + break; + default: + break; + } + print_nv(frame->headers.nva, frame->headers.nvlen); + break; + case NGHTTP2_PRIORITY: + print_frame_attr_indent(); + + fprintf(outfile, "(dep_stream_id=%d, weight=%u, exclusive=%d)\n", + frame->priority.pri_spec.stream_id, frame->priority.pri_spec.weight, + frame->priority.pri_spec.exclusive); + + break; + case NGHTTP2_RST_STREAM: + print_frame_attr_indent(); + fprintf(outfile, "(error_code=%s(0x%02x))\n", + strstatus(frame->rst_stream.error_code), + frame->rst_stream.error_code); + break; + case NGHTTP2_SETTINGS: + print_frame_attr_indent(); + fprintf(outfile, "(niv=%lu)\n", + static_cast(frame->settings.niv)); + for (size_t i = 0; i < frame->settings.niv; ++i) { + print_frame_attr_indent(); + fprintf(outfile, "[%s(0x%02x):%u]\n", + strsettingsid(frame->settings.iv[i].settings_id), + frame->settings.iv[i].settings_id, frame->settings.iv[i].value); + } + break; + case NGHTTP2_PUSH_PROMISE: + print_frame_attr_indent(); + fprintf(outfile, "(padlen=%zu, promised_stream_id=%d)\n", + frame->push_promise.padlen, frame->push_promise.promised_stream_id); + print_nv(frame->push_promise.nva, frame->push_promise.nvlen); + break; + case NGHTTP2_PING: + print_frame_attr_indent(); + fprintf(outfile, "(opaque_data=%s)\n", + util::format_hex(frame->ping.opaque_data, 8).c_str()); + break; + case NGHTTP2_GOAWAY: + print_frame_attr_indent(); + fprintf(outfile, "(last_stream_id=%d, error_code=%s(0x%02x), " + "opaque_data(%u)=[%s])\n", + frame->goaway.last_stream_id, strstatus(frame->goaway.error_code), + frame->goaway.error_code, + static_cast(frame->goaway.opaque_data_len), + util::ascii_dump(frame->goaway.opaque_data, + frame->goaway.opaque_data_len).c_str()); + break; + case NGHTTP2_WINDOW_UPDATE: + print_frame_attr_indent(); + fprintf(outfile, "(window_size_increment=%d)\n", + frame->window_update.window_size_increment); + break; + default: + break; + } +} +} // namespace + +int verbose_on_header_callback(nghttp2_session *session, + const nghttp2_frame *frame, const uint8_t *name, + size_t namelen, const uint8_t *value, + size_t valuelen, uint8_t flags, + void *user_data) { + nghttp2_nv nv = {const_cast(name), const_cast(value), + namelen, valuelen}; + + print_timer(); + fprintf(outfile, " recv (stream_id=%d", frame->hd.stream_id); + if (flags & NGHTTP2_NV_FLAG_NO_INDEX) { + fprintf(outfile, ", sensitive"); + } + fprintf(outfile, ") "); + + print_nv(&nv); + fflush(outfile); + + return 0; +} + +int verbose_on_frame_recv_callback(nghttp2_session *session, + const nghttp2_frame *frame, + void *user_data) { + print_timer(); + fprintf(outfile, " recv "); + print_frame(PRINT_RECV, frame); + fflush(outfile); + return 0; +} + +int verbose_on_invalid_frame_recv_callback(nghttp2_session *session, + const nghttp2_frame *frame, + int lib_error_code, + void *user_data) { + print_timer(); + fprintf(outfile, " [INVALID; error=%s] recv ", + nghttp2_strerror(lib_error_code)); + print_frame(PRINT_RECV, frame); + fflush(outfile); + return 0; +} + +int verbose_on_frame_send_callback(nghttp2_session *session, + const nghttp2_frame *frame, + void *user_data) { + print_timer(); + fprintf(outfile, " send "); + print_frame(PRINT_SEND, frame); + fflush(outfile); + return 0; +} + +int verbose_on_data_chunk_recv_callback(nghttp2_session *session, uint8_t flags, + int32_t stream_id, const uint8_t *data, + size_t len, void *user_data) { + print_timer(); + auto srecv = + nghttp2_session_get_stream_effective_recv_data_length(session, stream_id); + auto crecv = nghttp2_session_get_effective_recv_data_length(session); + + fprintf(outfile, + " recv (stream_id=%d, length=%zu, srecv=%d, crecv=%d) DATA\n", + stream_id, len, srecv, crecv); + fflush(outfile); + + return 0; +} + +namespace { +std::chrono::steady_clock::time_point base_tv; +} // namespace + +void reset_timer() { base_tv = std::chrono::steady_clock::now(); } + +std::chrono::milliseconds get_timer() { + return time_delta(std::chrono::steady_clock::now(), base_tv); +} + +std::chrono::steady_clock::time_point get_time() { + return std::chrono::steady_clock::now(); +} + +ssize_t deflate_data(uint8_t *out, size_t outlen, const uint8_t *in, + size_t inlen) { + int rv; + z_stream zst; + uint8_t temp_out[8192]; + auto temp_outlen = sizeof(temp_out); + + zst.next_in = Z_NULL; + zst.zalloc = Z_NULL; + zst.zfree = Z_NULL; + zst.opaque = Z_NULL; + + rv = deflateInit2(&zst, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 31, 9, + Z_DEFAULT_STRATEGY); + + if (rv != Z_OK) { + return -1; + } + + zst.avail_in = inlen; + zst.next_in = (uint8_t *)in; + zst.avail_out = temp_outlen; + zst.next_out = temp_out; + + rv = deflate(&zst, Z_FINISH); + + deflateEnd(&zst); + + if (rv != Z_STREAM_END) { + return -1; + } + + temp_outlen -= zst.avail_out; + + if (temp_outlen > outlen) { + return -1; + } + + memcpy(out, temp_out, temp_outlen); + + return temp_outlen; +} + +} // namespace nghttp2 diff --git a/src/app_helper.h b/src/app_helper.h new file mode 100644 index 0000000..408adac --- /dev/null +++ b/src/app_helper.h @@ -0,0 +1,95 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef APP_HELPER_H +#define APP_HELPER_H + +#include "nghttp2_config.h" + +#include +#include +#ifdef HAVE_SYS_TIME_H +#include +#endif // HAVE_SYS_TIME_H +#include + +#include +#include + +#include + +namespace nghttp2 { + +int verbose_on_header_callback(nghttp2_session *session, + const nghttp2_frame *frame, const uint8_t *name, + size_t namelen, const uint8_t *value, + size_t valuelen, uint8_t flags, void *user_data); + +int verbose_on_frame_recv_callback(nghttp2_session *session, + const nghttp2_frame *frame, void *user_data); + +int verbose_on_invalid_frame_recv_callback(nghttp2_session *session, + const nghttp2_frame *frame, + int lib_error_code, void *user_data); + +int verbose_on_frame_send_callback(nghttp2_session *session, + const nghttp2_frame *frame, void *user_data); + +int verbose_on_data_chunk_recv_callback(nghttp2_session *session, uint8_t flags, + int32_t stream_id, const uint8_t *data, + size_t len, void *user_data); + +// Returns difference between |a| and |b| in milliseconds, assuming +// |a| is more recent than |b|. +template +std::chrono::milliseconds time_delta(const TimePoint &a, const TimePoint &b) { + return std::chrono::duration_cast(a - b); +} + +// Resets timer +void reset_timer(); + +// Returns the duration since timer reset. +std::chrono::milliseconds get_timer(); + +// Returns current time point. +std::chrono::steady_clock::time_point get_time(); + +void print_timer(); + +// Setting true will print characters with ANSI color escape codes +// when printing HTTP2 frames. This function changes a static +// variable. +void set_color_output(bool f); + +// Set output file when printing HTTP2 frames. By default, stdout is +// used. +void set_output(FILE *file); + +ssize_t deflate_data(uint8_t *out, size_t outlen, const uint8_t *in, + size_t inlen); + +} // namespace nghttp2 + +#endif // APP_HELPER_H diff --git a/src/asio_client_request.cc b/src/asio_client_request.cc new file mode 100644 index 0000000..1fa93ca --- /dev/null +++ b/src/asio_client_request.cc @@ -0,0 +1,67 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2015 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "nghttp2_config.h" + +#include + +#include "asio_client_request_impl.h" + +#include "template.h" + +namespace nghttp2 { +namespace asio_http2 { +namespace client { + +request::request() : impl_(make_unique()) {} + +request::~request() {} + +void request::write_trailer(header_map h) const { + impl_->write_trailer(std::move(h)); +} + +void request::cancel(uint32_t error_code) const { impl_->cancel(error_code); } + +void request::on_response(response_cb cb) const { + impl_->on_response(std::move(cb)); +} + +void request::on_push(request_cb cb) const { impl_->on_push(std::move(cb)); } + +void request::on_close(close_cb cb) const { impl_->on_close(std::move(cb)); } + +const uri_ref &request::uri() const { return impl_->uri(); } + +const std::string &request::method() const { return impl_->method(); } + +const header_map &request::header() const { return impl_->header(); } + +void request::resume() const { impl_->resume(); } + +request_impl &request::impl() const { return *impl_; } + +} // namespace client +} // namespace asio_http2 +} // namespace nghttp2 diff --git a/src/asio_client_request_impl.cc b/src/asio_client_request_impl.cc new file mode 100644 index 0000000..5512c96 --- /dev/null +++ b/src/asio_client_request_impl.cc @@ -0,0 +1,110 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2015 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "asio_client_request_impl.h" + +#include "asio_client_stream.h" +#include "asio_client_session_impl.h" +#include "template.h" + +namespace nghttp2 { +namespace asio_http2 { +namespace client { + +request_impl::request_impl() : strm_(nullptr) {} + +void request_impl::write_trailer(header_map h) { + auto sess = strm_->session(); + sess->write_trailer(*strm_, std::move(h)); +} + +void request_impl::cancel(uint32_t error_code) { + auto sess = strm_->session(); + sess->cancel(*strm_, error_code); +} + +void request_impl::on_response(response_cb cb) { response_cb_ = std::move(cb); } + +void request_impl::call_on_response(response &res) { + if (response_cb_) { + response_cb_(res); + } +} + +void request_impl::on_push(request_cb cb) { push_request_cb_ = std::move(cb); } + +void request_impl::call_on_push(request &push_req) { + if (push_request_cb_) { + push_request_cb_(push_req); + } +}; + +void request_impl::on_close(close_cb cb) { close_cb_ = std::move(cb); } + +void request_impl::call_on_close(uint32_t error_code) { + if (close_cb_) { + close_cb_(error_code); + } +} + +void request_impl::on_read(generator_cb cb) { generator_cb_ = std::move(cb); } + +generator_cb::result_type request_impl::call_on_read(uint8_t *buf, + std::size_t len, + uint32_t *data_flags) { + if (generator_cb_) { + return generator_cb_(buf, len, data_flags); + } + + *data_flags |= NGHTTP2_DATA_FLAG_EOF; + + return 0; +} + +void request_impl::resume() { + auto sess = strm_->session(); + sess->resume(*strm_); +} + +void request_impl::header(header_map h) { header_ = std::move(h); } + +header_map &request_impl::header() { return header_; } + +const header_map &request_impl::header() const { return header_; } + +void request_impl::stream(class stream *strm) { strm_ = strm; } + +void request_impl::uri(uri_ref uri) { uri_ = std::move(uri); } + +const uri_ref &request_impl::uri() const { return uri_; } + +uri_ref &request_impl::uri() { return uri_; } + +void request_impl::method(std::string s) { method_ = std::move(s); } + +const std::string &request_impl::method() const { return method_; } + +} // namespace client +} // namespace asio_http2 +} // namespace nghttp2 diff --git a/src/asio_client_request_impl.h b/src/asio_client_request_impl.h new file mode 100644 index 0000000..802a83d --- /dev/null +++ b/src/asio_client_request_impl.h @@ -0,0 +1,93 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2015 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef ASIO_CLIENT_REQUEST_IMPL_H +#define ASIO_CLIENT_REQUEST_IMPL_H + +#include "nghttp2_config.h" + +#include + +namespace nghttp2 { +namespace asio_http2 { +namespace client { + +class response; +class stream; + +class request_impl { +public: + request_impl(); + + request_impl(const request_impl &) = delete; + request_impl &operator=(const request_impl &) = delete; + + void write_trailer(header_map h); + + void cancel(uint32_t error_code); + + void on_response(response_cb cb); + void call_on_response(response &res); + + void on_push(request_cb cb); + void call_on_push(request &push_req); + + void on_close(close_cb cb); + void call_on_close(uint32_t error_code); + + void on_read(generator_cb cb); + generator_cb::result_type call_on_read(uint8_t *buf, std::size_t len, + uint32_t *data_flags); + + void resume(); + + void header(header_map h); + header_map &header(); + const header_map &header() const; + + void stream(class stream *strm); + + void uri(uri_ref uri); + const uri_ref &uri() const; + uri_ref &uri(); + + void method(std::string s); + const std::string &method() const; + +private: + header_map header_; + response_cb response_cb_; + request_cb push_request_cb_; + close_cb close_cb_; + generator_cb generator_cb_; + class stream *strm_; + uri_ref uri_; + std::string method_; +}; + +} // namespace client +} // namespace asio_http2 +} // namespace nghttp2 + +#endif // ASIO_CLIENT_REQUEST_IMPL_H diff --git a/src/asio_client_response.cc b/src/asio_client_response.cc new file mode 100644 index 0000000..4805422 --- /dev/null +++ b/src/asio_client_response.cc @@ -0,0 +1,53 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2015 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "nghttp2_config.h" + +#include + +#include "asio_client_response_impl.h" + +#include "template.h" + +namespace nghttp2 { +namespace asio_http2 { +namespace client { + +response::response() : impl_(make_unique()) {} + +response::~response() {} + +void response::on_data(data_cb cb) const { impl_->on_data(std::move(cb)); } + +int response::status_code() const { return impl_->status_code(); } + +int64_t response::content_length() const { return impl_->content_length(); } + +const header_map &response::header() const { return impl_->header(); } + +response_impl &response::impl() const { return *impl_; } + +} // namespace client +} // namespace asio_http2 +} // namespace nghttp2 diff --git a/src/asio_client_response_impl.cc b/src/asio_client_response_impl.cc new file mode 100644 index 0000000..fce25a4 --- /dev/null +++ b/src/asio_client_response_impl.cc @@ -0,0 +1,57 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2015 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "asio_client_response_impl.h" + +#include "template.h" + +namespace nghttp2 { +namespace asio_http2 { +namespace client { + +response_impl::response_impl() : content_length_(-1), status_code_(0) {} + +void response_impl::on_data(data_cb cb) { data_cb_ = std::move(cb); } + +void response_impl::call_on_data(const uint8_t *data, std::size_t len) { + if (data_cb_) { + data_cb_(data, len); + } +} + +void response_impl::status_code(int sc) { status_code_ = sc; } + +int response_impl::status_code() const { return status_code_; } + +void response_impl::content_length(int64_t n) { content_length_ = n; } + +int64_t response_impl::content_length() const { return content_length_; } + +header_map &response_impl::header() { return header_; } + +const header_map &response_impl::header() const { return header_; } + +} // namespace client +} // namespace asio_http2 +} // namespace nghttp2 diff --git a/src/asio_client_response_impl.h b/src/asio_client_response_impl.h new file mode 100644 index 0000000..e2c2286 --- /dev/null +++ b/src/asio_client_response_impl.h @@ -0,0 +1,69 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2015 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef ASIO_CLIENT_RESPONSE_IMPL_H +#define ASIO_CLIENT_RESPONSE_IMPL_H + +#include "nghttp2_config.h" + +#include + +namespace nghttp2 { +namespace asio_http2 { +namespace client { + +class response_impl { +public: + response_impl(); + + response_impl(const response_impl &) = delete; + response_impl &operator=(const response_impl &) = delete; + + void on_data(data_cb cb); + + void call_on_data(const uint8_t *data, std::size_t len); + + void status_code(int sc); + int status_code() const; + + void content_length(int64_t n); + int64_t content_length() const; + + header_map &header(); + const header_map &header() const; + +private: + data_cb data_cb_; + + header_map header_; + + int64_t content_length_; + int status_code_; +}; + +} // namespace client +} // namespace asio_http2 +} // namespace nghttp2 + +#endif // ASIO_CLIENT_RESPONSE_IMPL_H diff --git a/src/asio_client_session.cc b/src/asio_client_session.cc new file mode 100644 index 0000000..aaee1e7 --- /dev/null +++ b/src/asio_client_session.cc @@ -0,0 +1,98 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2015 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "nghttp2_config.h" + +#include + +#include "asio_client_session_tcp_impl.h" +#include "asio_client_session_tls_impl.h" +#include "asio_common.h" +#include "template.h" + +namespace nghttp2 { +namespace asio_http2 { +namespace client { + +using boost::asio::ip::tcp; + +session::session(boost::asio::io_service &io_service, const std::string &host, + const std::string &service) + : impl_(make_unique(io_service, host, service)) {} + +session::session(boost::asio::io_service &io_service, + boost::asio::ssl::context &tls_ctx, const std::string &host, + const std::string &service) + : impl_(make_unique(io_service, tls_ctx, host, service)) { +} + +session::~session() {} + +session::session(session &&other) noexcept : impl_(std::move(other.impl_)) {} + +session &session::operator=(session &&other) noexcept { + if (this == &other) { + return *this; + } + + impl_ = std::move(other.impl_); + return *this; +} + +void session::on_connect(connect_cb cb) const { + impl_->on_connect(std::move(cb)); +} + +void session::on_error(error_cb cb) const { impl_->on_error(std::move(cb)); } + +void session::shutdown() const { impl_->shutdown(); } + +boost::asio::io_service &session::io_service() const { + return impl_->io_service(); +} + +const request *session::submit(boost::system::error_code &ec, + const std::string &method, + const std::string &uri, header_map h) const { + return impl_->submit(ec, method, uri, generator_cb(), std::move(h)); +} + +const request *session::submit(boost::system::error_code &ec, + const std::string &method, + const std::string &uri, std::string data, + header_map h) const { + return impl_->submit(ec, method, uri, string_generator(std::move(data)), + std::move(h)); +} + +const request *session::submit(boost::system::error_code &ec, + const std::string &method, + const std::string &uri, generator_cb cb, + header_map h) const { + return impl_->submit(ec, method, uri, std::move(cb), std::move(h)); +} + +} // namespace client +} // namespace asio_http2 +} // nghttp2 diff --git a/src/asio_client_session_impl.cc b/src/asio_client_session_impl.cc new file mode 100644 index 0000000..9cb57c9 --- /dev/null +++ b/src/asio_client_session_impl.cc @@ -0,0 +1,625 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2015 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "asio_client_session_impl.h" + +#include + +#include "asio_client_stream.h" +#include "asio_client_request_impl.h" +#include "asio_client_response_impl.h" +#include "asio_common.h" +#include "template.h" +#include "util.h" +#include "http2.h" + +namespace nghttp2 { +namespace asio_http2 { +namespace client { + +session_impl::session_impl(boost::asio::io_service &io_service) + : wblen_(0), io_service_(io_service), resolver_(io_service), + session_(nullptr), data_pending_(nullptr), data_pendinglen_(0), + writing_(false), inside_callback_(false) {} + +session_impl::~session_impl() { + // finish up all active stream + for (auto &p : streams_) { + auto &strm = p.second; + auto &req = strm->request().impl(); + req.call_on_close(NGHTTP2_INTERNAL_ERROR); + } + + nghttp2_session_del(session_); +} + +void session_impl::start_resolve(const std::string &host, + const std::string &service) { + resolver_.async_resolve({host, service}, + [this](const boost::system::error_code &ec, + tcp::resolver::iterator endpoint_it) { + if (ec) { + not_connected(ec); + return; + } + + start_connect(endpoint_it); + }); +} + +void session_impl::connected(tcp::resolver::iterator endpoint_it) { + if (!setup_session()) { + return; + } + + socket().set_option(boost::asio::ip::tcp::no_delay(true)); + + do_write(); + do_read(); + + auto &connect_cb = on_connect(); + if (connect_cb) { + connect_cb(endpoint_it); + } +} + +void session_impl::not_connected(const boost::system::error_code &ec) { + call_error_cb(ec); +} + +void session_impl::on_connect(connect_cb cb) { connect_cb_ = std::move(cb); } + +void session_impl::on_error(error_cb cb) { error_cb_ = std::move(cb); } + +const connect_cb &session_impl::on_connect() const { return connect_cb_; } + +const error_cb &session_impl::on_error() const { return error_cb_; } + +void session_impl::call_error_cb(const boost::system::error_code &ec) { + auto &error_cb = on_error(); + if (!error_cb) { + return; + } + error_cb(ec); +} + +namespace { +int on_begin_headers_callback(nghttp2_session *session, + const nghttp2_frame *frame, void *user_data) { + if (frame->hd.type != NGHTTP2_PUSH_PROMISE) { + return 0; + } + + auto sess = static_cast(user_data); + sess->create_push_stream(frame->push_promise.promised_stream_id); + + return 0; +} +} // namespace + +namespace { +int on_header_callback(nghttp2_session *session, const nghttp2_frame *frame, + const uint8_t *name, size_t namelen, + const uint8_t *value, size_t valuelen, uint8_t flags, + void *user_data) { + auto sess = static_cast(user_data); + stream *strm; + + switch (frame->hd.type) { + case NGHTTP2_HEADERS: { + strm = sess->find_stream(frame->hd.stream_id); + if (!strm) { + return 0; + } + + // ignore trailers + if (frame->headers.cat == NGHTTP2_HCAT_HEADERS && + !strm->expect_final_response()) { + return 0; + } + + auto token = http2::lookup_token(name, namelen); + + auto &res = strm->response().impl(); + if (token == http2::HD__STATUS) { + res.status_code(util::parse_uint(value, valuelen)); + } else { + + if (token == http2::HD_CONTENT_LENGTH) { + res.content_length(util::parse_uint(value, valuelen)); + } + + res.header().emplace( + std::string(name, name + namelen), + header_value{std::string(value, value + valuelen), + (flags & NGHTTP2_NV_FLAG_NO_INDEX) != 0}); + } + break; + } + case NGHTTP2_PUSH_PROMISE: { + strm = sess->find_stream(frame->push_promise.promised_stream_id); + if (!strm) { + return 0; + } + + auto &req = strm->request().impl(); + auto &uri = req.uri(); + + switch (http2::lookup_token(name, namelen)) { + case http2::HD__METHOD: + req.method(std::string(value, value + valuelen)); + break; + case http2::HD__SCHEME: + uri.scheme.assign(value, value + valuelen); + break; + case http2::HD__PATH: + split_path(uri, value, value + valuelen); + break; + case http2::HD__AUTHORITY: + uri.host.assign(value, value + valuelen); + break; + case http2::HD_HOST: + if (uri.host.empty()) { + uri.host.assign(value, value + valuelen); + } + // fall through + default: + req.header().emplace( + std::string(name, name + namelen), + header_value{std::string(value, value + valuelen), + (flags & NGHTTP2_NV_FLAG_NO_INDEX) != 0}); + } + + break; + } + default: + return 0; + } + + return 0; +} +} // namespace + +namespace { +int on_frame_recv_callback(nghttp2_session *session, const nghttp2_frame *frame, + void *user_data) { + auto sess = static_cast(user_data); + auto strm = sess->find_stream(frame->hd.stream_id); + + switch (frame->hd.type) { + case NGHTTP2_DATA: { + if (!strm) { + return 0; + } + if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) { + strm->response().impl().call_on_data(nullptr, 0); + } + break; + } + case NGHTTP2_HEADERS: { + if (!strm) { + return 0; + } + + // ignore trailers + if (frame->headers.cat == NGHTTP2_HCAT_HEADERS && + !strm->expect_final_response()) { + return 0; + } + + if (strm->expect_final_response()) { + // wait for final response + return 0; + } + + auto &req = strm->request().impl(); + req.call_on_response(strm->response()); + if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) { + strm->response().impl().call_on_data(nullptr, 0); + } + break; + } + case NGHTTP2_PUSH_PROMISE: { + if (!strm) { + return 0; + } + + auto push_strm = sess->find_stream(frame->push_promise.promised_stream_id); + if (!push_strm) { + return 0; + } + + strm->request().impl().call_on_push(push_strm->request()); + + break; + } + } + return 0; +} +} // namespace + +namespace { +int on_data_chunk_recv_callback(nghttp2_session *session, uint8_t flags, + int32_t stream_id, const uint8_t *data, + size_t len, void *user_data) { + auto sess = static_cast(user_data); + auto strm = sess->find_stream(stream_id); + if (!strm) { + return 0; + } + + auto &res = strm->response().impl(); + res.call_on_data(data, len); + + return 0; +} +} // namespace + +namespace { +int on_stream_close_callback(nghttp2_session *session, int32_t stream_id, + uint32_t error_code, void *user_data) { + auto sess = static_cast(user_data); + auto strm = sess->pop_stream(stream_id); + if (!strm) { + return 0; + } + + strm->request().impl().call_on_close(error_code); + + return 0; +} +} // namespace + +bool session_impl::setup_session() { + nghttp2_session_callbacks *callbacks; + nghttp2_session_callbacks_new(&callbacks); + auto cb_del = defer(nghttp2_session_callbacks_del, callbacks); + + nghttp2_session_callbacks_set_on_begin_headers_callback( + callbacks, on_begin_headers_callback); + nghttp2_session_callbacks_set_on_header_callback(callbacks, + on_header_callback); + nghttp2_session_callbacks_set_on_frame_recv_callback(callbacks, + on_frame_recv_callback); + nghttp2_session_callbacks_set_on_data_chunk_recv_callback( + callbacks, on_data_chunk_recv_callback); + nghttp2_session_callbacks_set_on_stream_close_callback( + callbacks, on_stream_close_callback); + + auto rv = nghttp2_session_client_new(&session_, callbacks, this); + if (rv != 0) { + call_error_cb(make_error_code(static_cast(rv))); + return false; + } + + const uint32_t window_size = 256 * 1024 * 1024; + + std::array iv{ + {{NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100}, + // typically client is just a *sink* and just process data as + // much as possible. Use large window size by default. + {NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE, window_size}}}; + nghttp2_submit_settings(session_, NGHTTP2_FLAG_NONE, iv.data(), iv.size()); + // increase connection window size up to window_size + nghttp2_submit_window_update(session_, NGHTTP2_FLAG_NONE, 0, + window_size - + NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE); + return true; +} + +int session_impl::write_trailer(stream &strm, header_map h) { + int rv; + auto nva = std::vector(); + nva.reserve(h.size()); + for (auto &hd : h) { + nva.push_back(nghttp2::http2::make_nv(hd.first, hd.second.value, + hd.second.sensitive)); + } + + rv = nghttp2_submit_trailer(session_, strm.stream_id(), nva.data(), + nva.size()); + + if (rv != 0) { + return -1; + } + + signal_write(); + + return 0; +} + +void session_impl::cancel(stream &strm, uint32_t error_code) { + nghttp2_submit_rst_stream(session_, NGHTTP2_FLAG_NONE, strm.stream_id(), + error_code); + signal_write(); +} + +void session_impl::resume(stream &strm) { + nghttp2_session_resume_data(session_, strm.stream_id()); + signal_write(); +} + +stream *session_impl::find_stream(int32_t stream_id) { + auto it = streams_.find(stream_id); + if (it == std::end(streams_)) { + return nullptr; + } + return (*it).second.get(); +} + +std::unique_ptr session_impl::pop_stream(int32_t stream_id) { + auto it = streams_.find(stream_id); + if (it == std::end(streams_)) { + return nullptr; + } + auto strm = std::move((*it).second); + streams_.erase(it); + return strm; +} + +stream *session_impl::create_push_stream(int32_t stream_id) { + auto strm = create_stream(); + strm->stream_id(stream_id); + auto p = streams_.emplace(stream_id, std::move(strm)); + assert(p.second); + return (*p.first).second.get(); +} + +std::unique_ptr session_impl::create_stream() { + return make_unique(this); +} + +const request *session_impl::submit(boost::system::error_code &ec, + const std::string &method, + const std::string &uri, generator_cb cb, + header_map h) { + ec.clear(); + + http_parser_url u{}; + // TODO Handle CONNECT method + if (http_parser_parse_url(uri.c_str(), uri.size(), 0, &u) != 0) { + ec = make_error_code(boost::system::errc::invalid_argument); + return nullptr; + } + + if ((u.field_set & (1 << UF_SCHEMA)) == 0 || + (u.field_set & (1 << UF_HOST)) == 0) { + ec = make_error_code(boost::system::errc::invalid_argument); + return nullptr; + } + + auto strm = create_stream(); + auto &req = strm->request().impl(); + auto &uref = req.uri(); + + http2::copy_url_component(uref.scheme, &u, UF_SCHEMA, uri.c_str()); + http2::copy_url_component(uref.host, &u, UF_HOST, uri.c_str()); + http2::copy_url_component(uref.raw_path, &u, UF_PATH, uri.c_str()); + http2::copy_url_component(uref.raw_query, &u, UF_QUERY, uri.c_str()); + + if (util::ipv6_numeric_addr(uref.host.c_str())) { + uref.host = "[" + uref.host; + uref.host += "]"; + } + if (u.field_set & (1 << UF_PORT)) { + uref.host += ":"; + uref.host += util::utos(u.port); + } + + if (uref.raw_path.empty()) { + uref.raw_path = "/"; + } + + uref.path = percent_decode(uref.raw_path); + + auto path = uref.raw_path; + if (u.field_set & (1 << UF_QUERY)) { + path += "?"; + path += uref.raw_query; + } + + auto nva = std::vector(); + nva.reserve(3 + h.size()); + nva.push_back(http2::make_nv_ls(":method", method)); + nva.push_back(http2::make_nv_ls(":scheme", uref.scheme)); + nva.push_back(http2::make_nv_ls(":path", path)); + nva.push_back(http2::make_nv_ls(":authority", uref.host)); + for (auto &kv : h) { + nva.push_back( + http2::make_nv(kv.first, kv.second.value, kv.second.sensitive)); + } + + req.header(std::move(h)); + + nghttp2_data_provider *prdptr = nullptr; + nghttp2_data_provider prd; + + if (cb) { + strm->request().impl().on_read(std::move(cb)); + prd.source.ptr = strm.get(); + prd.read_callback = + [](nghttp2_session *session, int32_t stream_id, uint8_t *buf, + size_t length, uint32_t *data_flags, nghttp2_data_source *source, + void *user_data) -> ssize_t { + auto strm = static_cast(source->ptr); + return strm->request().impl().call_on_read(buf, length, data_flags); + }; + prdptr = &prd; + } + + auto stream_id = nghttp2_submit_request(session_, nullptr, nva.data(), + nva.size(), prdptr, strm.get()); + if (stream_id < 0) { + ec = make_error_code(static_cast(stream_id)); + return nullptr; + } + + signal_write(); + + strm->stream_id(stream_id); + + auto p = streams_.emplace(stream_id, std::move(strm)); + assert(p.second); + return &(*p.first).second->request(); +} + +void session_impl::shutdown() { + nghttp2_session_terminate_session(session_, NGHTTP2_NO_ERROR); + signal_write(); +} + +boost::asio::io_service &session_impl::io_service() { return io_service_; } + +void session_impl::signal_write() { + if (!inside_callback_) { + do_write(); + } +} + +bool session_impl::should_stop() const { + return !writing_ && !nghttp2_session_want_read(session_) && + !nghttp2_session_want_write(session_); +} + +namespace { +struct callback_guard { + callback_guard(session_impl &sess) : sess(sess) { sess.enter_callback(); } + ~callback_guard() { sess.leave_callback(); } + + session_impl &sess; +}; +} // namespace + +void session_impl::enter_callback() { + assert(!inside_callback_); + inside_callback_ = true; +} + +void session_impl::leave_callback() { + assert(inside_callback_); + inside_callback_ = false; +} + +void session_impl::do_read() { + read_socket([this](const boost::system::error_code &ec, + std::size_t bytes_transferred) { + if (ec) { + if (ec.value() == boost::asio::error::operation_aborted) { + call_error_cb(ec); + shutdown_socket(); + } + return; + } + + { + callback_guard cg(*this); + + auto rv = + nghttp2_session_mem_recv(session_, rb_.data(), bytes_transferred); + + if (rv != static_cast(bytes_transferred)) { + call_error_cb(make_error_code( + static_cast(rv < 0 ? rv : NGHTTP2_ERR_PROTO))); + shutdown_socket(); + return; + } + } + + do_write(); + + if (should_stop()) { + shutdown_socket(); + return; + } + + do_read(); + }); +} + +void session_impl::do_write() { + if (writing_) { + return; + } + + if (data_pending_) { + std::copy_n(data_pending_, data_pendinglen_, std::begin(wb_) + wblen_); + + wblen_ += data_pendinglen_; + + data_pending_ = nullptr; + data_pendinglen_ = 0; + } + + { + callback_guard cg(*this); + + for (;;) { + const uint8_t *data; + auto n = nghttp2_session_mem_send(session_, &data); + if (n < 0) { + call_error_cb(make_error_code(static_cast(n))); + shutdown_socket(); + return; + } + + if (n == 0) { + break; + } + + if (wblen_ + n > wb_.size()) { + data_pending_ = data; + data_pendinglen_ = n; + + break; + } + + std::copy_n(data, n, std::begin(wb_) + wblen_); + + wblen_ += n; + } + } + + if (wblen_ == 0) { + return; + } + + writing_ = true; + + write_socket([this](const boost::system::error_code &ec, std::size_t n) { + if (ec) { + call_error_cb(ec); + shutdown_socket(); + return; + } + + wblen_ = 0; + writing_ = false; + + do_write(); + }); +} + +} // namespace client +} // namespace asio_http2 +} // nghttp2 diff --git a/src/asio_client_session_impl.h b/src/asio_client_session_impl.h new file mode 100644 index 0000000..88bbad8 --- /dev/null +++ b/src/asio_client_session_impl.h @@ -0,0 +1,123 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2015 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef ASIO_CLIENT_SESSION_IMPL_H +#define ASIO_CLIENT_SESSION_IMPL_H + +#include "nghttp2_config.h" + +#include + +#include + +namespace nghttp2 { +namespace asio_http2 { +namespace client { + +class stream; + +using boost::asio::ip::tcp; + +class session_impl { +public: + session_impl(boost::asio::io_service &io_service); + virtual ~session_impl(); + + void start_resolve(const std::string &host, const std::string &service); + + void connected(tcp::resolver::iterator endpoint_it); + void not_connected(const boost::system::error_code &ec); + + void on_connect(connect_cb cb); + void on_error(error_cb cb); + + const connect_cb &on_connect() const; + const error_cb &on_error() const; + + int write_trailer(stream &strm, header_map h); + + void cancel(stream &strm, uint32_t error_code); + void resume(stream &strm); + + std::unique_ptr create_stream(); + std::unique_ptr pop_stream(int32_t stream_id); + stream *create_push_stream(int32_t stream_id); + stream *find_stream(int32_t stream_id); + + const request *submit(boost::system::error_code &ec, + const std::string &method, const std::string &uri, + generator_cb cb, header_map h); + + virtual void start_connect(tcp::resolver::iterator endpoint_it) = 0; + virtual tcp::socket &socket() = 0; + virtual void read_socket(std::function< + void(const boost::system::error_code &ec, std::size_t n)> h) = 0; + virtual void write_socket(std::function< + void(const boost::system::error_code &ec, std::size_t n)> h) = 0; + virtual void shutdown_socket() = 0; + + void shutdown(); + + boost::asio::io_service &io_service(); + + void signal_write(); + + void enter_callback(); + void leave_callback(); + + void do_read(); + void do_write(); + +protected: + boost::array rb_; + boost::array wb_; + std::size_t wblen_; + +private: + bool should_stop() const; + bool setup_session(); + void call_error_cb(const boost::system::error_code &ec); + + boost::asio::io_service &io_service_; + tcp::resolver resolver_; + + std::map> streams_; + + connect_cb connect_cb_; + error_cb error_cb_; + + nghttp2_session *session_; + + const uint8_t *data_pending_; + std::size_t data_pendinglen_; + + bool writing_; + bool inside_callback_; +}; + +} // namespace client +} // namespace asio_http2 +} // namespace nghttp2 + +#endif // ASIO_CLIENT_SESSION_IMPL_H diff --git a/src/asio_client_session_tcp_impl.cc b/src/asio_client_session_tcp_impl.cc new file mode 100644 index 0000000..41195ba --- /dev/null +++ b/src/asio_client_session_tcp_impl.cc @@ -0,0 +1,69 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2015 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "asio_client_session_tcp_impl.h" + +namespace nghttp2 { +namespace asio_http2 { +namespace client { + +session_tcp_impl::session_tcp_impl(boost::asio::io_service &io_service, + const std::string &host, + const std::string &service) + : session_impl(io_service), socket_(io_service) { + start_resolve(host, service); +} + +session_tcp_impl::~session_tcp_impl() {} + +void session_tcp_impl::start_connect(tcp::resolver::iterator endpoint_it) { + boost::asio::async_connect(socket_, endpoint_it, + [this](const boost::system::error_code &ec, + tcp::resolver::iterator endpoint_it) { + if (ec) { + not_connected(ec); + return; + } + + connected(endpoint_it); + }); +} + +tcp::socket &session_tcp_impl::socket() { return socket_; } + +void session_tcp_impl::read_socket( + std::function h) { + socket_.async_read_some(boost::asio::buffer(rb_), h); +} + +void session_tcp_impl::write_socket( + std::function h) { + boost::asio::async_write(socket_, boost::asio::buffer(wb_, wblen_), h); +} + +void session_tcp_impl::shutdown_socket() { socket_.close(); } + +} // namespace client +} // namespace asio_http2 +} // namespace nghttp2 diff --git a/src/asio_client_session_tcp_impl.h b/src/asio_client_session_tcp_impl.h new file mode 100644 index 0000000..6138828 --- /dev/null +++ b/src/asio_client_session_tcp_impl.h @@ -0,0 +1,60 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2015 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef ASIO_CLIENT_SESSION_TCP_IMPL_H +#define ASIO_CLIENT_SESSION_TCP_IMPL_H + +#include "asio_client_session_impl.h" + +#include + +namespace nghttp2 { +namespace asio_http2 { +namespace client { + +using boost::asio::ip::tcp; + +class session_tcp_impl : public session_impl { +public: + session_tcp_impl(boost::asio::io_service &io_service, const std::string &host, + const std::string &service); + virtual ~session_tcp_impl(); + + virtual void start_connect(tcp::resolver::iterator endpoint_it); + virtual tcp::socket &socket(); + virtual void read_socket(std::function< + void(const boost::system::error_code &ec, std::size_t n)> h); + virtual void write_socket(std::function< + void(const boost::system::error_code &ec, std::size_t n)> h); + virtual void shutdown_socket(); + +private: + tcp::socket socket_; +}; + +} // namespace client +} // namespace asio_http2 +} // namespace nghttp2 + +#endif // ASIO_CLIENT_SESSION_TCP_IMPL_H diff --git a/src/asio_client_session_tls_impl.cc b/src/asio_client_session_tls_impl.cc new file mode 100644 index 0000000..cde2ab3 --- /dev/null +++ b/src/asio_client_session_tls_impl.cc @@ -0,0 +1,85 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2015 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "asio_client_session_tls_impl.h" + +namespace nghttp2 { +namespace asio_http2 { +namespace client { + +session_tls_impl::session_tls_impl(boost::asio::io_service &io_service, + boost::asio::ssl::context &tls_ctx, + const std::string &host, + const std::string &service) + : session_impl(io_service), socket_(io_service, tls_ctx) { + // this callback setting is no effect is + // ssl::context::set_verify_mode(boost::asio::ssl::verify_peer) is + // not used, which is what we want. + socket_.set_verify_callback(boost::asio::ssl::rfc2818_verification(host)); + + start_resolve(host, service); +} + +session_tls_impl::~session_tls_impl() {} + +void session_tls_impl::start_connect(tcp::resolver::iterator endpoint_it) { + boost::asio::async_connect(socket(), endpoint_it, + [this](const boost::system::error_code &ec, + tcp::resolver::iterator endpoint_it) { + if (ec) { + not_connected(ec); + return; + } + + socket_.async_handshake( + boost::asio::ssl::stream_base::client, + [this, endpoint_it](const boost::system::error_code &ec) { + if (ec) { + not_connected(ec); + return; + } + connected(endpoint_it); + }); + }); +} + +tcp::socket &session_tls_impl::socket() { return socket_.next_layer(); } + +void session_tls_impl::read_socket( + std::function h) { + socket_.async_read_some(boost::asio::buffer(rb_), h); +} + +void session_tls_impl::write_socket( + std::function h) { + boost::asio::async_write(socket_, boost::asio::buffer(wb_, wblen_), h); +} + +void session_tls_impl::shutdown_socket() { + socket_.async_shutdown([](const boost::system::error_code &ec) {}); +} + +} // namespace client +} // namespace asio_http2 +} // namespace nghttp2 diff --git a/src/asio_client_session_tls_impl.h b/src/asio_client_session_tls_impl.h new file mode 100644 index 0000000..484e3d4 --- /dev/null +++ b/src/asio_client_session_tls_impl.h @@ -0,0 +1,63 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2015 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef ASIO_CLIENT_SESSION_TLS_IMPL_H +#define ASIO_CLIENT_SESSION_TLS_IMPL_H + +#include "asio_client_session_impl.h" + +#include + +namespace nghttp2 { +namespace asio_http2 { +namespace client { + +using boost::asio::ip::tcp; + +using ssl_socket = boost::asio::ssl::stream; + +class session_tls_impl : public session_impl { +public: + session_tls_impl(boost::asio::io_service &io_service, + boost::asio::ssl::context &tls_ctx, const std::string &host, + const std::string &service); + virtual ~session_tls_impl(); + + virtual void start_connect(tcp::resolver::iterator endpoint_it); + virtual tcp::socket &socket(); + virtual void read_socket(std::function< + void(const boost::system::error_code &ec, std::size_t n)> h); + virtual void write_socket(std::function< + void(const boost::system::error_code &ec, std::size_t n)> h); + virtual void shutdown_socket(); + +private: + ssl_socket socket_; +}; + +} // namespace client +} // namespace asio_http2 +} // namespace nghttp2 + +#endif // ASIO_CLIENT_SESSION_TLS_IMPL_H diff --git a/src/asio_client_stream.cc b/src/asio_client_stream.cc new file mode 100644 index 0000000..2d3aa5b --- /dev/null +++ b/src/asio_client_stream.cc @@ -0,0 +1,59 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2015 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "asio_client_stream.h" + +#include "asio_client_request_impl.h" +#include "asio_client_response_impl.h" +#include "asio_client_session_impl.h" + +namespace nghttp2 { +namespace asio_http2 { +namespace client { + +stream::stream(session_impl *sess) : sess_(sess), stream_id_(0) { + request_.impl().stream(this); +} + +void stream::stream_id(int32_t stream_id) { stream_id_ = stream_id; } + +int32_t stream::stream_id() const { return stream_id_; } + +class request &stream::request() { + return request_; +} + +class response &stream::response() { + return response_; +} + +session_impl *stream::session() const { return sess_; } + +bool stream::expect_final_response() const { + return response_.status_code() / 100 == 1; +} + +} // namespace client +} // namespace asio_http2 +} // namespace nghttp2 diff --git a/src/asio_client_stream.h b/src/asio_client_stream.h new file mode 100644 index 0000000..e3e027a --- /dev/null +++ b/src/asio_client_stream.h @@ -0,0 +1,68 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2015 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef ASIO_CLIENT_STREAM_H +#define ASIO_CLIENT_STREAM_H + +#include "nghttp2_config.h" + +#include + +namespace nghttp2 { +namespace asio_http2 { +namespace client { + +class request; +class response; +class session_impl; + +class stream { +public: + stream(session_impl *sess); + + stream(const stream &) = delete; + stream &operator=(const stream &) = delete; + + void stream_id(int32_t stream_id); + int32_t stream_id() const; + + class request &request(); + class response &response(); + + session_impl *session() const; + + bool expect_final_response() const; + +private: + nghttp2::asio_http2::client::request request_; + nghttp2::asio_http2::client::response response_; + session_impl *sess_; + uint32_t stream_id_; +}; + +} // namespace client +} // namespace asio_http2 +} // namespace nghttp2 + +#endif // ASIO_CLIENT_STREAM_H diff --git a/src/asio_client_tls_context.cc b/src/asio_client_tls_context.cc new file mode 100644 index 0000000..5513834 --- /dev/null +++ b/src/asio_client_tls_context.cc @@ -0,0 +1,64 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2015 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "asio_client_tls_context.h" + +#include + +#include + +#include "ssl.h" +#include "util.h" + +namespace nghttp2 { +namespace asio_http2 { +namespace client { + +namespace { +int client_select_next_proto_cb(SSL *ssl, unsigned char **out, + unsigned char *outlen, const unsigned char *in, + unsigned int inlen, void *arg) { + if (!util::select_h2(const_cast(out), outlen, in, + inlen)) { + return SSL_TLSEXT_ERR_NOACK; + } + return SSL_TLSEXT_ERR_OK; +} +} // namespace + +boost::system::error_code +configure_tls_context(boost::system::error_code &ec, + boost::asio::ssl::context &tls_ctx) { + ec.clear(); + + auto ctx = tls_ctx.native_handle(); + + SSL_CTX_set_next_proto_select_cb(ctx, client_select_next_proto_cb, nullptr); + + return ec; +} + +} // namespace client +} // namespace asio_http2 +} // namespace nghttp2 diff --git a/src/asio_client_tls_context.h b/src/asio_client_tls_context.h new file mode 100644 index 0000000..6287dab --- /dev/null +++ b/src/asio_client_tls_context.h @@ -0,0 +1,32 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2015 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef ASIO_CLIENT_TLS_CONTEXT_H +#define ASIO_CLIENT_TLS_CONTEXT_H + +#include "nghttp2_config.h" + +#include + +#endif // ASIO_CLIENT_TLS_CONTEXT_H diff --git a/src/asio_common.cc b/src/asio_common.cc new file mode 100644 index 0000000..c3f98e7 --- /dev/null +++ b/src/asio_common.cc @@ -0,0 +1,148 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2015 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "asio_common.h" + +#include + +#include "util.h" +#include "template.h" +#include "http2.h" + +namespace nghttp2 { +namespace asio_http2 { + +class nghttp2_category_impl : public boost::system::error_category { +public: + const char *name() const noexcept { return "nghttp2"; } + std::string message(int ev) const { return nghttp2_strerror(ev); } +}; + +const boost::system::error_category &nghttp2_category() noexcept { + static nghttp2_category_impl cat; + return cat; +} + +boost::system::error_code make_error_code(nghttp2_error ev) { + return boost::system::error_code(static_cast(ev), nghttp2_category()); +} + +generator_cb string_generator(std::string data) { + auto strio = std::make_shared>(std::move(data), + data.size()); + return [strio](uint8_t *buf, size_t len, uint32_t *data_flags) { + auto &data = strio->first; + auto &left = strio->second; + auto n = std::min(len, left); + std::copy_n(data.c_str() + data.size() - left, n, buf); + left -= n; + if (left == 0) { + *data_flags |= NGHTTP2_DATA_FLAG_EOF; + } + return n; + }; +} + +generator_cb deferred_generator() { + return [](uint8_t *buf, size_t len, + uint32_t *data_flags) { return NGHTTP2_ERR_DEFERRED; }; +} + +template +std::shared_ptr> defer_shared(F &&f, T &&... t) { + return std::make_shared>(std::forward(f), + std::forward(t)...); +} + +generator_cb file_generator(const std::string &path) { + auto fd = open(path.c_str(), O_RDONLY); + if (fd == -1) { + return generator_cb(); + } + + return file_generator_from_fd(fd); +} + +generator_cb file_generator_from_fd(int fd) { + auto d = defer_shared(close, fd); + + return [fd, d](uint8_t *buf, size_t len, uint32_t *data_flags) + -> generator_cb::result_type { + ssize_t n; + while ((n = read(fd, buf, len)) == -1 && errno == EINTR) + ; + + if (n == -1) { + return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; + } + + if (n == 0) { + *data_flags |= NGHTTP2_DATA_FLAG_EOF; + } + + return n; + }; +} + +bool check_path(const std::string &path) { return util::check_path(path); } + +std::string percent_decode(const std::string &s) { + return util::percentDecode(std::begin(s), std::end(s)); +} + +std::string http_date(int64_t t) { return util::http_date(t); } + +boost::system::error_code host_service_from_uri(boost::system::error_code &ec, + std::string &scheme, + std::string &host, + std::string &service, + const std::string &uri) { + ec.clear(); + + http_parser_url u{}; + if (http_parser_parse_url(uri.c_str(), uri.size(), 0, &u) != 0) { + ec = make_error_code(boost::system::errc::invalid_argument); + return ec; + } + + if ((u.field_set & (1 << UF_SCHEMA)) == 0 || + (u.field_set & (1 << UF_HOST)) == 0) { + ec = make_error_code(boost::system::errc::invalid_argument); + return ec; + } + + http2::copy_url_component(scheme, &u, UF_SCHEMA, uri.c_str()); + http2::copy_url_component(host, &u, UF_HOST, uri.c_str()); + + if (u.field_set & (1 << UF_PORT)) { + http2::copy_url_component(service, &u, UF_PORT, uri.c_str()); + } else { + service = scheme; + } + + return ec; +} + +} // namespace asio_http2 +} // namespace nghttp2 diff --git a/src/asio_common.h b/src/asio_common.h new file mode 100644 index 0000000..8f59a0d --- /dev/null +++ b/src/asio_common.h @@ -0,0 +1,65 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2015 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef ASIO_COMMON_H +#define ASIO_COMMON_H + +#include "nghttp2_config.h" + +#include + +#include + +#include "util.h" + +namespace nghttp2 { + +namespace asio_http2 { + +boost::system::error_code make_error_code(nghttp2_error ev); + +generator_cb string_generator(std::string data); + +// Returns generator_cb, which just returns NGHTTP2_ERR_DEFERRED +generator_cb deferred_generator(); + +template +void split_path(uri_ref &dst, InputIt first, InputIt last) { + auto path_last = std::find(first, last, '?'); + InputIt query_first; + if (path_last == last) { + query_first = path_last = last; + } else { + query_first = path_last + 1; + } + dst.path = util::percentDecode(first, path_last); + dst.raw_path.assign(first, path_last); + dst.raw_query.assign(query_first, last); +} + +} // namespace asio_http2 + +} // namespace nghttp2 + +#endif // ASIO_COMMON_H diff --git a/src/asio_io_service_pool.cc b/src/asio_io_service_pool.cc new file mode 100644 index 0000000..ba40541 --- /dev/null +++ b/src/asio_io_service_pool.cc @@ -0,0 +1,97 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2014 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +// We wrote this code based on the original code which has the +// following license: +// +// io_service_pool.cpp +// ~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2013 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +#include "asio_io_service_pool.h" + +namespace nghttp2 { + +namespace asio_http2 { + +io_service_pool::io_service_pool(std::size_t pool_size) : next_io_service_(0) { + if (pool_size == 0) { + throw std::runtime_error("io_service_pool size is 0"); + } + + // Give all the io_services work to do so that their run() functions will not + // exit until they are explicitly stopped. + for (std::size_t i = 0; i < pool_size; ++i) { + auto io_service = std::make_shared(); + auto work = std::make_shared(*io_service); + io_services_.push_back(io_service); + work_.push_back(work); + } +} + +void io_service_pool::run(bool asynchronous) { + // Create a pool of threads to run all of the io_services. + for (std::size_t i = 0; i < io_services_.size(); ++i) { + futures_.push_back(std::async(std::launch::async, + (size_t (boost::asio::io_service::*)(void)) & + boost::asio::io_service::run, + io_services_[i])); + } + + if (!asynchronous) { + join(); + } +} + +void io_service_pool::join() { + // Wait for all threads in the pool to exit. + for (auto &fut : futures_) { + fut.get(); + } +} + +void io_service_pool::stop() { + // Explicitly stop all io_services. + for (auto &iosv : io_services_) { + iosv->stop(); + } +} + +boost::asio::io_service &io_service_pool::get_io_service() { + // Use a round-robin scheme to choose the next io_service to use. + auto &io_service = *io_services_[next_io_service_]; + ++next_io_service_; + if (next_io_service_ == io_services_.size()) { + next_io_service_ = 0; + } + return io_service; +} + +} // namespace asio_http2 + +} // namespace nghttp2 diff --git a/src/asio_io_service_pool.h b/src/asio_io_service_pool.h new file mode 100644 index 0000000..242cac8 --- /dev/null +++ b/src/asio_io_service_pool.h @@ -0,0 +1,91 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2014 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +// We wrote this code based on the original code which has the +// following license: +// +// io_service_pool.hpp +// ~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2013 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_IO_SERVICE_POOL_H +#define ASIO_IO_SERVICE_POOL_H + +#include "nghttp2_config.h" + +#include +#include +#include + +#include +#include + +#include + +namespace nghttp2 { + +namespace asio_http2 { + +/// A pool of io_service objects. +class io_service_pool : private boost::noncopyable { +public: + /// Construct the io_service pool. + explicit io_service_pool(std::size_t pool_size); + + /// Run all io_service objects in the pool. + void run(bool asynchronous = false); + + /// Stop all io_service objects in the pool. + void stop(); + + /// Join on all io_service objects in the pool. + void join(); + + /// Get an io_service to use. + boost::asio::io_service &get_io_service(); + +private: + /// The pool of io_services. + std::vector> io_services_; + + /// The work that keeps the io_services running. + std::vector> work_; + + /// The next io_service to use for a connection. + std::size_t next_io_service_; + + /// Futures to all the io_service objects + std::vector> futures_; +}; + +} // namespace asio_http2 + +} // namespace nghttp2 + +#endif // ASIO_IO_SERVICE_POOL_H diff --git a/src/asio_server.cc b/src/asio_server.cc new file mode 100644 index 0000000..310e672 --- /dev/null +++ b/src/asio_server.cc @@ -0,0 +1,165 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2014 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +// We wrote this code based on the original code which has the +// following license: +// +// server.cpp +// ~~~~~~~~~~ +// +// Copyright (c) 2003-2013 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#include "asio_server.h" + +#include "asio_server_connection.h" +#include "util.h" + +namespace nghttp2 { +namespace asio_http2 { +namespace server { + +server::server(std::size_t io_service_pool_size) + : io_service_pool_(io_service_pool_size) {} + +boost::system::error_code +server::listen_and_serve(boost::system::error_code &ec, + boost::asio::ssl::context *tls_context, + const std::string &address, const std::string &port, + int backlog, serve_mux &mux, bool asynchronous) { + ec.clear(); + + if (bind_and_listen(ec, address, port, backlog)) { + return ec; + } + + for (auto &acceptor : acceptors_) { + if (tls_context) { + start_accept(*tls_context, acceptor, mux); + } else { + start_accept(acceptor, mux); + } + } + + io_service_pool_.run(asynchronous); + + return ec; +} + +boost::system::error_code server::bind_and_listen(boost::system::error_code &ec, + const std::string &address, + const std::string &port, + int backlog) { + // Open the acceptor with the option to reuse the address (i.e. + // SO_REUSEADDR). + tcp::resolver resolver(io_service_pool_.get_io_service()); + tcp::resolver::query query(address, port); + auto it = resolver.resolve(query, ec); + if (ec) { + return ec; + } + + for (; it != tcp::resolver::iterator(); ++it) { + tcp::endpoint endpoint = *it; + auto acceptor = tcp::acceptor(io_service_pool_.get_io_service()); + + if (acceptor.open(endpoint.protocol(), ec)) { + continue; + } + + acceptor.set_option(tcp::acceptor::reuse_address(true)); + + if (acceptor.bind(endpoint, ec)) { + continue; + } + + if (acceptor.listen( + backlog == -1 ? boost::asio::socket_base::max_connections : backlog, + ec)) { + continue; + } + + acceptors_.push_back(std::move(acceptor)); + } + + if (acceptors_.empty()) { + return ec; + } + + // ec could have some errors since we may have failed to bind some + // interfaces. + ec.clear(); + + return ec; +} + +void server::start_accept(boost::asio::ssl::context &tls_context, + tcp::acceptor &acceptor, serve_mux &mux) { + auto new_connection = std::make_shared>( + mux, io_service_pool_.get_io_service(), tls_context); + + acceptor.async_accept(new_connection->socket().lowest_layer(), + [this, &tls_context, &acceptor, &mux, new_connection]( + const boost::system::error_code &e) { + if (!e) { + new_connection->socket().lowest_layer().set_option(tcp::no_delay(true)); + new_connection->socket().async_handshake( + boost::asio::ssl::stream_base::server, + [new_connection](const boost::system::error_code &e) { + if (!e) { + new_connection->start(); + } + }); + } + + start_accept(tls_context, acceptor, mux); + }); +} + +void server::start_accept(tcp::acceptor &acceptor, serve_mux &mux) { + auto new_connection = std::make_shared>( + mux, io_service_pool_.get_io_service()); + + acceptor.async_accept(new_connection->socket(), + [this, &acceptor, &mux, new_connection]( + const boost::system::error_code &e) { + if (!e) { + new_connection->socket().set_option(tcp::no_delay(true)); + new_connection->start(); + } + + start_accept(acceptor, mux); + }); +} + +void server::stop() { io_service_pool_.stop(); } + +void server::join() { io_service_pool_.join(); } + +} // namespace server +} // namespace asio_http2 +} // namespace nghttp2 diff --git a/src/asio_server.h b/src/asio_server.h new file mode 100644 index 0000000..7f60120 --- /dev/null +++ b/src/asio_server.h @@ -0,0 +1,105 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2014 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +// We wrote this code based on the original code which has the +// following license: +// +// server.hpp +// ~~~~~~~~~~ +// +// Copyright (c) 2003-2013 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_SERVER_H +#define ASIO_SERVER_H + +#include "nghttp2_config.h" + +#include +#include +#include + +#include + +#include + +#include "asio_io_service_pool.h" + +namespace nghttp2 { + +namespace asio_http2 { + +namespace server { + +class serve_mux; + +using boost::asio::ip::tcp; + +using ssl_socket = boost::asio::ssl::stream; + +class server : private boost::noncopyable { +public: + explicit server(std::size_t io_service_pool_size); + + boost::system::error_code + listen_and_serve(boost::system::error_code &ec, + boost::asio::ssl::context *tls_context, + const std::string &address, const std::string &port, + int backlog, serve_mux &mux, bool asynchronous = false); + void join(); + void stop(); + +private: + /// Initiate an asynchronous accept operation. + void start_accept(tcp::acceptor &acceptor, serve_mux &mux); + /// Same as above but with tls_context + void start_accept(boost::asio::ssl::context &tls_context, + tcp::acceptor &acceptor, serve_mux &mux); + + /// Resolves address and bind socket to the resolved addresses. + boost::system::error_code bind_and_listen(boost::system::error_code &ec, + const std::string &address, + const std::string &port, + int backlog); + + /// The pool of io_service objects used to perform asynchronous + /// operations. + io_service_pool io_service_pool_; + + /// Acceptor used to listen for incoming connections. + std::vector acceptors_; + + std::unique_ptr ssl_ctx_; +}; + +} // namespace server + +} // namespace asio_http2 + +} // namespace nghttp2 + +#endif // ASIO_SERVER_H diff --git a/src/asio_server_connection.h b/src/asio_server_connection.h new file mode 100644 index 0000000..5ce48d9 --- /dev/null +++ b/src/asio_server_connection.h @@ -0,0 +1,169 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2014 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +// We wrote this code based on the original code which has the +// following license: +// +// connection.hpp +// ~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2013 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_SERVER_CONNECTION_H +#define ASIO_SERVER_CONNECTION_H + +#include "nghttp2_config.h" + +#include + +#include +#include + +#include + +#include "asio_server_http2_handler.h" +#include "asio_server_serve_mux.h" +#include "util.h" + +namespace nghttp2 { + +namespace asio_http2 { + +namespace server { + +/// Represents a single connection from a client. +template +class connection : public std::enable_shared_from_this>, + private boost::noncopyable { +public: + /// Construct a connection with the given io_service. + template + explicit connection(serve_mux &mux, SocketArgs &&... args) + : socket_(std::forward(args)...), mux_(mux), writing_(false) { + } + + /// Start the first asynchronous operation for the connection. + void start() { + handler_ = std::make_shared(socket_.get_io_service(), + [this]() { do_write(); }, mux_); + if (handler_->start() != 0) { + return; + } + do_read(); + } + + socket_type &socket() { return socket_; } + + void do_read() { + auto self = this->shared_from_this(); + + socket_.async_read_some(boost::asio::buffer(buffer_), + [this, self](const boost::system::error_code &e, + std::size_t bytes_transferred) { + if (!e) { + if (handler_->on_read(buffer_, bytes_transferred) != 0) { + return; + } + + do_write(); + + if (!writing_ && handler_->should_stop()) { + return; + } + + do_read(); + } + + // If an error occurs then no new asynchronous operations are + // started. This means that all shared_ptr references to the + // connection object will disappear and the object will be + // destroyed automatically after this handler returns. The + // connection class's destructor closes the socket. + }); + } + + void do_write() { + auto self = this->shared_from_this(); + + if (writing_) { + return; + } + + int rv; + std::size_t nwrite; + + rv = handler_->on_write(outbuf_, nwrite); + + if (rv != 0) { + return; + } + + if (nwrite == 0) { + return; + } + + writing_ = true; + + boost::asio::async_write( + socket_, boost::asio::buffer(outbuf_, nwrite), + [this, self](const boost::system::error_code &e, std::size_t) { + if (!e) { + writing_ = false; + + do_write(); + } + }); + + // No new asynchronous operations are started. This means that all + // shared_ptr references to the connection object will disappear and + // the object will be destroyed automatically after this handler + // returns. The connection class's destructor closes the socket. + } + +private: + socket_type socket_; + + serve_mux &mux_; + + std::shared_ptr handler_; + + /// Buffer for incoming data. + boost::array buffer_; + + boost::array outbuf_; + + bool writing_; +}; + +} // namespace server + +} // namespace asio_http2 + +} // namespace nghttp2 + +#endif // ASIO_SERVER_CONNECTION_H diff --git a/src/asio_server_http2.cc b/src/asio_server_http2.cc new file mode 100644 index 0000000..7d162e3 --- /dev/null +++ b/src/asio_server_http2.cc @@ -0,0 +1,84 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2014 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "nghttp2_config.h" + +#include + +#include "asio_server_http2_impl.h" +#include "asio_server.h" +#include "template.h" + +namespace nghttp2 { + +namespace asio_http2 { + +namespace server { + +http2::http2() : impl_(make_unique()) {} + +http2::~http2() {} + +http2::http2(http2 &&other) noexcept : impl_(std::move(other.impl_)) {} + +http2 &http2::operator=(http2 &&other) noexcept { + if (this == &other) { + return *this; + } + + impl_ = std::move(other.impl_); + + return *this; +} + +boost::system::error_code http2::listen_and_serve(boost::system::error_code &ec, + const std::string &address, + const std::string &port, + bool asynchronous) { + return impl_->listen_and_serve(ec, nullptr, address, port, asynchronous); +} + +boost::system::error_code http2::listen_and_serve( + boost::system::error_code &ec, boost::asio::ssl::context &tls_context, + const std::string &address, const std::string &port, bool asynchronous) { + return impl_->listen_and_serve(ec, &tls_context, address, port, asynchronous); +} + +void http2::num_threads(size_t num_threads) { impl_->num_threads(num_threads); } + +void http2::backlog(int backlog) { impl_->backlog(backlog); } + +bool http2::handle(std::string pattern, request_cb cb) { + return impl_->handle(std::move(pattern), std::move(cb)); +} + +void http2::stop() { impl_->stop(); } + +void http2::join() { return impl_->join(); } + +} // namespace server + +} // namespace asio_http2 + +} // namespace nghttp2 diff --git a/src/asio_server_http2_handler.cc b/src/asio_server_http2_handler.cc new file mode 100644 index 0000000..c55cb32 --- /dev/null +++ b/src/asio_server_http2_handler.cc @@ -0,0 +1,462 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2014 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "asio_server_http2_handler.h" + +#include + +#include "asio_common.h" +#include "asio_server_serve_mux.h" +#include "asio_server_stream.h" +#include "asio_server_request_impl.h" +#include "asio_server_response_impl.h" +#include "http2.h" +#include "util.h" +#include "template.h" + +namespace nghttp2 { + +namespace asio_http2 { + +namespace server { + +namespace { +int stream_error(nghttp2_session *session, int32_t stream_id, + uint32_t error_code) { + return nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, stream_id, + error_code); +} +} // namespace + +namespace { +int on_begin_headers_callback(nghttp2_session *session, + const nghttp2_frame *frame, void *user_data) { + auto handler = static_cast(user_data); + + if (frame->hd.type != NGHTTP2_HEADERS || + frame->headers.cat != NGHTTP2_HCAT_REQUEST) { + return 0; + } + + handler->create_stream(frame->hd.stream_id); + + return 0; +} +} // namespace + +namespace { +int on_header_callback(nghttp2_session *session, const nghttp2_frame *frame, + const uint8_t *name, size_t namelen, + const uint8_t *value, size_t valuelen, uint8_t flags, + void *user_data) { + auto handler = static_cast(user_data); + auto stream_id = frame->hd.stream_id; + + if (frame->hd.type != NGHTTP2_HEADERS || + frame->headers.cat != NGHTTP2_HCAT_REQUEST) { + return 0; + } + + auto strm = handler->find_stream(stream_id); + if (!strm) { + return 0; + } + + auto &req = strm->request().impl(); + auto &uref = req.uri(); + + switch (nghttp2::http2::lookup_token(name, namelen)) { + case nghttp2::http2::HD__METHOD: + req.method(std::string(value, value + valuelen)); + break; + case nghttp2::http2::HD__SCHEME: + uref.scheme.assign(value, value + valuelen); + break; + case nghttp2::http2::HD__AUTHORITY: + uref.host.assign(value, value + valuelen); + break; + case nghttp2::http2::HD__PATH: + split_path(uref, value, value + valuelen); + break; + case nghttp2::http2::HD_HOST: + if (uref.host.empty()) { + uref.host.assign(value, value + valuelen); + } + // fall through + default: + req.header().emplace(std::string(name, name + namelen), + header_value{std::string(value, value + valuelen), + (flags & NGHTTP2_NV_FLAG_NO_INDEX) != 0}); + } + + return 0; +} +} // namespace + +namespace { +int on_frame_recv_callback(nghttp2_session *session, const nghttp2_frame *frame, + void *user_data) { + auto handler = static_cast(user_data); + auto strm = handler->find_stream(frame->hd.stream_id); + + switch (frame->hd.type) { + case NGHTTP2_DATA: + if (!strm) { + break; + } + + if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) { + strm->request().impl().call_on_data(nullptr, 0); + } + + break; + case NGHTTP2_HEADERS: { + if (!strm || frame->headers.cat != NGHTTP2_HCAT_REQUEST) { + break; + } + + handler->call_on_request(*strm); + + if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) { + strm->request().impl().call_on_data(nullptr, 0); + } + + break; + } + } + + return 0; +} +} // namespace + +namespace { +int on_data_chunk_recv_callback(nghttp2_session *session, uint8_t flags, + int32_t stream_id, const uint8_t *data, + size_t len, void *user_data) { + auto handler = static_cast(user_data); + auto strm = handler->find_stream(stream_id); + + if (!strm) { + return 0; + } + + strm->request().impl().call_on_data(data, len); + + return 0; +} + +} // namespace + +namespace { +int on_stream_close_callback(nghttp2_session *session, int32_t stream_id, + uint32_t error_code, void *user_data) { + auto handler = static_cast(user_data); + + auto strm = handler->find_stream(stream_id); + if (!strm) { + return 0; + } + + strm->response().impl().call_on_close(error_code); + + handler->close_stream(stream_id); + + return 0; +} +} // namespace + +namespace { +int on_frame_send_callback(nghttp2_session *session, const nghttp2_frame *frame, + void *user_data) { + auto handler = static_cast(user_data); + + if (frame->hd.type != NGHTTP2_PUSH_PROMISE) { + return 0; + } + + auto strm = handler->find_stream(frame->push_promise.promised_stream_id); + + if (!strm) { + return 0; + } + + auto &res = strm->response().impl(); + res.push_promise_sent(); + + return 0; +} +} // namespace + +namespace { +int on_frame_not_send_callback(nghttp2_session *session, + const nghttp2_frame *frame, int lib_error_code, + void *user_data) { + if (frame->hd.type != NGHTTP2_HEADERS) { + return 0; + } + + // Issue RST_STREAM so that stream does not hang around. + nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, frame->hd.stream_id, + NGHTTP2_INTERNAL_ERROR); + + return 0; +} +} // namespace + +http2_handler::http2_handler(boost::asio::io_service &io_service, + connection_write writefun, serve_mux &mux) + : writefun_(writefun), mux_(mux), io_service_(io_service), + session_(nullptr), buf_(nullptr), buflen_(0), inside_callback_(false), + tstamp_cached_(time(nullptr)), + formatted_date_(util::http_date(tstamp_cached_)) {} + +http2_handler::~http2_handler() { nghttp2_session_del(session_); } + +const std::string &http2_handler::http_date() { + auto t = time(nullptr); + if (t != tstamp_cached_) { + tstamp_cached_ = t; + formatted_date_ = util::http_date(t); + } + return formatted_date_; +} + +int http2_handler::start() { + int rv; + + nghttp2_session_callbacks *callbacks; + rv = nghttp2_session_callbacks_new(&callbacks); + if (rv != 0) { + return -1; + } + + auto cb_del = defer(nghttp2_session_callbacks_del, callbacks); + + nghttp2_session_callbacks_set_on_begin_headers_callback( + callbacks, on_begin_headers_callback); + nghttp2_session_callbacks_set_on_header_callback(callbacks, + on_header_callback); + nghttp2_session_callbacks_set_on_frame_recv_callback(callbacks, + on_frame_recv_callback); + nghttp2_session_callbacks_set_on_data_chunk_recv_callback( + callbacks, on_data_chunk_recv_callback); + nghttp2_session_callbacks_set_on_stream_close_callback( + callbacks, on_stream_close_callback); + nghttp2_session_callbacks_set_on_frame_send_callback(callbacks, + on_frame_send_callback); + nghttp2_session_callbacks_set_on_frame_not_send_callback( + callbacks, on_frame_not_send_callback); + + rv = nghttp2_session_server_new(&session_, callbacks, this); + if (rv != 0) { + return -1; + } + + nghttp2_settings_entry ent{NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100}; + nghttp2_submit_settings(session_, NGHTTP2_FLAG_NONE, &ent, 1); + + return 0; +} + +stream *http2_handler::create_stream(int32_t stream_id) { + auto p = streams_.emplace(stream_id, make_unique(this, stream_id)); + assert(p.second); + return (*p.first).second.get(); +} + +void http2_handler::close_stream(int32_t stream_id) { + streams_.erase(stream_id); +} + +stream *http2_handler::find_stream(int32_t stream_id) { + auto i = streams_.find(stream_id); + if (i == std::end(streams_)) { + return nullptr; + } + + return (*i).second.get(); +} + +void http2_handler::call_on_request(stream &strm) { + auto cb = mux_.handler(strm.request().impl()); + cb(strm.request(), strm.response()); +} + +bool http2_handler::should_stop() const { + return !nghttp2_session_want_read(session_) && + !nghttp2_session_want_write(session_); +} + +int http2_handler::start_response(stream &strm) { + int rv; + + auto &res = strm.response().impl(); + auto &header = res.header(); + auto nva = std::vector(); + nva.reserve(2 + header.size()); + auto status = util::utos(res.status_code()); + auto date = http_date(); + nva.push_back(nghttp2::http2::make_nv_ls(":status", status)); + nva.push_back(nghttp2::http2::make_nv_ls("date", date)); + for (auto &hd : header) { + nva.push_back(nghttp2::http2::make_nv(hd.first, hd.second.value, + hd.second.sensitive)); + } + + nghttp2_data_provider *prd_ptr = nullptr, prd; + auto &req = strm.request().impl(); + if (::nghttp2::http2::expect_response_body(req.method(), res.status_code())) { + prd.source.ptr = &strm; + prd.read_callback = + [](nghttp2_session *session, int32_t stream_id, uint8_t *buf, + size_t length, uint32_t *data_flags, nghttp2_data_source *source, + void *user_data) -> ssize_t { + auto &strm = *static_cast(source->ptr); + return strm.response().impl().call_read(buf, length, data_flags); + }; + prd_ptr = &prd; + } + rv = nghttp2_submit_response(session_, strm.get_stream_id(), nva.data(), + nva.size(), prd_ptr); + + if (rv != 0) { + return -1; + } + + signal_write(); + + return 0; +} + +int http2_handler::submit_trailer(stream &strm, header_map h) { + int rv; + auto nva = std::vector(); + nva.reserve(h.size()); + for (auto &hd : h) { + nva.push_back(nghttp2::http2::make_nv(hd.first, hd.second.value, + hd.second.sensitive)); + } + + rv = nghttp2_submit_trailer(session_, strm.get_stream_id(), nva.data(), + nva.size()); + + if (rv != 0) { + return -1; + } + + signal_write(); + + return 0; +} + +void http2_handler::enter_callback() { + assert(!inside_callback_); + inside_callback_ = true; +} + +void http2_handler::leave_callback() { + assert(inside_callback_); + inside_callback_ = false; +} + +void http2_handler::stream_error(int32_t stream_id, uint32_t error_code) { + ::nghttp2::asio_http2::server::stream_error(session_, stream_id, error_code); + signal_write(); +} + +void http2_handler::signal_write() { + if (!inside_callback_) { + initiate_write(); + } +} + +void http2_handler::initiate_write() { writefun_(); } + +void http2_handler::resume(stream &strm) { + nghttp2_session_resume_data(session_, strm.get_stream_id()); + signal_write(); +} + +response *http2_handler::push_promise(boost::system::error_code &ec, + stream &strm, std::string method, + std::string raw_path_query, + header_map h) { + int rv; + + ec.clear(); + + auto &req = strm.request().impl(); + + auto nva = std::vector(); + nva.reserve(4 + h.size()); + nva.push_back(nghttp2::http2::make_nv_ls(":method", method)); + nva.push_back(nghttp2::http2::make_nv_ls(":scheme", req.uri().scheme)); + nva.push_back(nghttp2::http2::make_nv_ls(":authority", req.uri().host)); + nva.push_back(nghttp2::http2::make_nv_ls(":path", raw_path_query)); + + for (auto &hd : h) { + nva.push_back(nghttp2::http2::make_nv(hd.first, hd.second.value, + hd.second.sensitive)); + } + + rv = nghttp2_submit_push_promise(session_, NGHTTP2_FLAG_NONE, + strm.get_stream_id(), nva.data(), nva.size(), + nullptr); + + if (rv < 0) { + ec = make_error_code(static_cast(rv)); + return nullptr; + } + + auto promised_strm = create_stream(rv); + auto &promised_req = promised_strm->request().impl(); + promised_req.header(std::move(h)); + promised_req.method(std::move(method)); + + auto &uref = promised_req.uri(); + uref.scheme = req.uri().scheme; + uref.host = req.uri().host; + split_path(uref, std::begin(raw_path_query), std::end(raw_path_query)); + + auto &promised_res = promised_strm->response().impl(); + promised_res.pushed(true); + + signal_write(); + + return &promised_strm->response(); +} + +boost::asio::io_service &http2_handler::io_service() { return io_service_; } + +callback_guard::callback_guard(http2_handler &h) : handler(h) { + handler.enter_callback(); +} + +callback_guard::~callback_guard() { handler.leave_callback(); } + +} // namespace server + +} // namespace asio_http2 + +} // namespace nghttp2 diff --git a/src/asio_server_http2_handler.h b/src/asio_server_http2_handler.h new file mode 100644 index 0000000..068d452 --- /dev/null +++ b/src/asio_server_http2_handler.h @@ -0,0 +1,167 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2014 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef ASIO_SERVER_HTTP2_HANDLER_H +#define ASIO_SERVER_HTTP2_HANDLER_H + +#include "nghttp2_config.h" + +#include +#include +#include + +#include + +#include + +namespace nghttp2 { +namespace asio_http2 { +namespace server { + +class http2_handler; +class stream; +class serve_mux; + +struct callback_guard { + callback_guard(http2_handler &h); + ~callback_guard(); + http2_handler &handler; +}; + +using connection_write = std::function; + +class http2_handler : public std::enable_shared_from_this { +public: + http2_handler(boost::asio::io_service &io_service, connection_write writefun, + serve_mux &mux); + + ~http2_handler(); + + int start(); + + stream *create_stream(int32_t stream_id); + void close_stream(int32_t stream_id); + stream *find_stream(int32_t stream_id); + + void call_on_request(stream &s); + + bool should_stop() const; + + int start_response(stream &s); + + int submit_trailer(stream &s, header_map h); + + void stream_error(int32_t stream_id, uint32_t error_code); + + void initiate_write(); + + void enter_callback(); + void leave_callback(); + + void resume(stream &s); + + response *push_promise(boost::system::error_code &ec, stream &s, + std::string method, std::string raw_path_query, + header_map h); + + void signal_write(); + + boost::asio::io_service &io_service(); + + const std::string &http_date(); + + template + int on_read(const boost::array &buffer, std::size_t len) { + callback_guard cg(*this); + + int rv; + + rv = nghttp2_session_mem_recv(session_, buffer.data(), len); + + if (rv < 0) { + return -1; + } + + return 0; + } + + template + int on_write(boost::array &buffer, std::size_t &len) { + callback_guard cg(*this); + + len = 0; + + if (buf_) { + std::copy_n(buf_, buflen_, std::begin(buffer)); + + len += buflen_; + + buf_ = nullptr; + buflen_ = 0; + } + + for (;;) { + const uint8_t *data; + auto nread = nghttp2_session_mem_send(session_, &data); + if (nread < 0) { + return -1; + } + + if (nread == 0) { + break; + } + + if (len + nread > buffer.size()) { + buf_ = data; + buflen_ = nread; + + break; + } + + std::copy_n(data, nread, std::begin(buffer) + len); + + len += nread; + } + + return 0; + } + +private: + std::map> streams_; + connection_write writefun_; + serve_mux &mux_; + boost::asio::io_service &io_service_; + nghttp2_session *session_; + const uint8_t *buf_; + std::size_t buflen_; + bool inside_callback_; + time_t tstamp_cached_; + std::string formatted_date_; +}; + +} // namespace server +} // namespace asio_http2 +} // namespace nghttp + +#endif // ASIO_SERVER_HTTP2_HANDLER_H diff --git a/src/asio_server_http2_impl.cc b/src/asio_server_http2_impl.cc new file mode 100644 index 0000000..79994a8 --- /dev/null +++ b/src/asio_server_http2_impl.cc @@ -0,0 +1,66 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2014 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "asio_server_http2_impl.h" + +#include + +#include "asio_server.h" +#include "util.h" +#include "ssl.h" +#include "template.h" + +namespace nghttp2 { + +namespace asio_http2 { + +namespace server { + +http2_impl::http2_impl() : num_threads_(1), backlog_(-1) {} + +boost::system::error_code http2_impl::listen_and_serve( + boost::system::error_code &ec, boost::asio::ssl::context *tls_context, + const std::string &address, const std::string &port, bool asynchronous) { + server_.reset(new server(num_threads_)); + return server_->listen_and_serve(ec, tls_context, address, port, backlog_, + mux_, asynchronous); +} + +void http2_impl::num_threads(size_t num_threads) { num_threads_ = num_threads; } + +void http2_impl::backlog(int backlog) { backlog_ = backlog; } + +bool http2_impl::handle(std::string pattern, request_cb cb) { + return mux_.handle(std::move(pattern), std::move(cb)); +} + +void http2_impl::stop() { return server_->stop(); } + +void http2_impl::join() { return server_->join(); } + +} // namespace server + +} // namespace asio_http2 + +} // namespace nghttp2 diff --git a/src/asio_server_http2_impl.h b/src/asio_server_http2_impl.h new file mode 100644 index 0000000..e406869 --- /dev/null +++ b/src/asio_server_http2_impl.h @@ -0,0 +1,67 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2014 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef ASIO_SERVER_HTTP2_IMPL_H +#define ASIO_SERVER_HTTP2_IMPL_H + +#include "nghttp2_config.h" + +#include + +#include "asio_server_serve_mux.h" + +namespace nghttp2 { + +namespace asio_http2 { + +namespace server { + +class server; + +class http2_impl { +public: + http2_impl(); + boost::system::error_code listen_and_serve( + boost::system::error_code &ec, boost::asio::ssl::context *tls_context, + const std::string &address, const std::string &port, bool asynchronous); + void num_threads(size_t num_threads); + void backlog(int backlog); + bool handle(std::string pattern, request_cb cb); + void stop(); + void join(); + +private: + std::unique_ptr server_; + std::size_t num_threads_; + int backlog_; + serve_mux mux_; +}; + +} // namespace server + +} // namespace asio_http2 + +} // namespace nghttp2 + +#endif // ASIO_SERVER_HTTP2_IMPL_H diff --git a/src/asio_server_request.cc b/src/asio_server_request.cc new file mode 100644 index 0000000..0241c48 --- /dev/null +++ b/src/asio_server_request.cc @@ -0,0 +1,55 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2015 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "nghttp2_config.h" + +#include + +#include "asio_server_request_impl.h" + +#include "template.h" + +namespace nghttp2 { +namespace asio_http2 { +namespace server { + +request::request() : impl_(make_unique()) {} + +request::~request() {} + +const header_map &request::header() const { return impl_->header(); } + +const std::string &request::method() const { return impl_->method(); } + +const uri_ref &request::uri() const { return impl_->uri(); } + +void request::on_data(data_cb cb) const { + return impl_->on_data(std::move(cb)); +} + +request_impl &request::impl() const { return *impl_; } + +} // namespace server +} // namespace asio_http2 +} // namespace nghttp2 diff --git a/src/asio_server_request_handler.cc b/src/asio_server_request_handler.cc new file mode 100644 index 0000000..7f113bd --- /dev/null +++ b/src/asio_server_request_handler.cc @@ -0,0 +1,84 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2015 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "asio_server_request_handler.h" + +#include "util.h" +#include "http2.h" + +namespace nghttp2 { +namespace asio_http2 { +namespace server { + +namespace { +std::string create_html(int status_code) { + std::string res; + res.reserve(512); + auto status = ::nghttp2::http2::get_status_string(status_code); + res += R"()"; + res += status; + res += "

        "; + res += status; + res += "

        "; + return res; +} +} // namespace + +request_cb redirect_handler(int status_code, std::string uri) { + return [status_code, uri](const request &req, const response &res) { + header_map h; + h.emplace("location", header_value{std::move(uri)}); + std::string html; + if (req.method() == "GET") { + html = create_html(status_code); + } + h.emplace("content-length", header_value{util::utos(html.size())}); + + res.write_head(status_code, std::move(h)); + res.end(std::move(html)); + }; +} + +request_cb status_handler(int status_code) { + return [status_code](const request &req, const response &res) { + if (!::nghttp2::http2::expect_response_body(status_code)) { + res.write_head(status_code); + res.end(); + return; + } + // we supply content-length for HEAD request, but body will not be + // sent. + auto html = create_html(status_code); + header_map h; + h.emplace("content-length", header_value{util::utos(html.size())}); + h.emplace("content-type", header_value{"text/html; charset=utf-8"}); + + res.write_head(status_code, std::move(h)); + res.end(std::move(html)); + }; +} + +} // namespace server +} // namespace asio_http2 +} // namespace nghttp2 diff --git a/src/asio_server_request_handler.h b/src/asio_server_request_handler.h new file mode 100644 index 0000000..5eefcfd --- /dev/null +++ b/src/asio_server_request_handler.h @@ -0,0 +1,32 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2015 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef ASIO_SERVER_REQUEST_HANDLER_H +#define ASIO_SERVER_REQUEST_HANDLER_H + +#include "nghttp2_config.h" + +#include + +#endif // ASIO_SERVER_REQUEST_HANDLER_H diff --git a/src/asio_server_request_impl.cc b/src/asio_server_request_impl.cc new file mode 100644 index 0000000..3a78208 --- /dev/null +++ b/src/asio_server_request_impl.cc @@ -0,0 +1,59 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2015 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "asio_server_request_impl.h" + +namespace nghttp2 { +namespace asio_http2 { +namespace server { + +request_impl::request_impl() : strm_(nullptr) {} + +const header_map &request_impl::header() const { return header_; } + +const std::string &request_impl::method() const { return method_; } + +const uri_ref &request_impl::uri() const { return uri_; } + +uri_ref &request_impl::uri() { return uri_; } + +void request_impl::header(header_map h) { header_ = std::move(h); } + +header_map &request_impl::header() { return header_; } + +void request_impl::method(std::string arg) { method_ = std::move(arg); } + +void request_impl::on_data(data_cb cb) { on_data_cb_ = std::move(cb); } + +void request_impl::stream(class stream *s) { strm_ = s; } + +void request_impl::call_on_data(const uint8_t *data, std::size_t len) { + if (on_data_cb_) { + on_data_cb_(data, len); + } +} + +} // namespace server +} // namespace asio_http2 +} // namespace nghttp2 diff --git a/src/asio_server_request_impl.h b/src/asio_server_request_impl.h new file mode 100644 index 0000000..8d4b014 --- /dev/null +++ b/src/asio_server_request_impl.h @@ -0,0 +1,69 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2015 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef ASIO_SERVER_REQUEST_IMPL_H +#define ASIO_SERVER_REQUEST_IMPL_H + +#include "nghttp2_config.h" + +#include + +namespace nghttp2 { +namespace asio_http2 { +namespace server { + +class stream; + +class request_impl { +public: + request_impl(); + + void header(header_map h); + const header_map &header() const; + header_map &header(); + + void method(std::string method); + const std::string &method() const; + + const uri_ref &uri() const; + uri_ref &uri(); + + void on_data(data_cb cb); + + void stream(class stream *s); + void call_on_data(const uint8_t *data, std::size_t len); + +private: + class stream *strm_; + header_map header_; + std::string method_; + uri_ref uri_; + data_cb on_data_cb_; +}; + +} // namespace server +} // namespace asio_http2 +} // namespace nghttp2 + +#endif // ASIO_SERVER_REQUEST_IMPL_H diff --git a/src/asio_server_response.cc b/src/asio_server_response.cc new file mode 100644 index 0000000..f420693 --- /dev/null +++ b/src/asio_server_response.cc @@ -0,0 +1,75 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2015 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "nghttp2_config.h" + +#include + +#include "asio_server_response_impl.h" + +#include "template.h" + +namespace nghttp2 { +namespace asio_http2 { +namespace server { + +response::response() : impl_(make_unique()) {} + +response::~response() {} + +void response::write_head(unsigned int status_code, header_map h) const { + impl_->write_head(status_code, std::move(h)); +} + +void response::end(std::string data) const { impl_->end(std::move(data)); } + +void response::end(generator_cb cb) const { impl_->end(std::move(cb)); } + +void response::write_trailer(header_map h) const { + impl_->write_trailer(std::move(h)); +} + +void response::on_close(close_cb cb) const { impl_->on_close(std::move(cb)); } + +void response::cancel(uint32_t error_code) const { impl_->cancel(error_code); } + +const response *response::push(boost::system::error_code &ec, + std::string method, std::string path, + header_map h) const { + return impl_->push(ec, std::move(method), std::move(path), std::move(h)); +} + +void response::resume() const { impl_->resume(); } + +unsigned int response::status_code() const { return impl_->status_code(); } + +boost::asio::io_service &response::io_service() const { + return impl_->io_service(); +} + +response_impl &response::impl() const { return *impl_; } + +} // namespace server +} // namespace asio_http2 +} // namespace nghttp2 diff --git a/src/asio_server_response_impl.cc b/src/asio_server_response_impl.cc new file mode 100644 index 0000000..1cdbc59 --- /dev/null +++ b/src/asio_server_response_impl.cc @@ -0,0 +1,163 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2015 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "asio_server_response_impl.h" + +#include "asio_server_stream.h" +#include "asio_server_request_impl.h" +#include "asio_server_http2_handler.h" +#include "asio_common.h" + +#include "http2.h" + +namespace nghttp2 { +namespace asio_http2 { +namespace server { + +response_impl::response_impl() + : strm_(nullptr), generator_cb_(deferred_generator()), status_code_(200), + state_(response_state::INITIAL), pushed_(false), + push_promise_sent_(false) {} + +unsigned int response_impl::status_code() const { return status_code_; } + +void response_impl::write_head(unsigned int status_code, header_map h) { + if (state_ != response_state::INITIAL) { + return; + } + + status_code_ = status_code; + header_ = std::move(h); + + state_ = response_state::HEADER_DONE; + + if (pushed_ && !push_promise_sent_) { + return; + } + + start_response(); +} + +void response_impl::end(std::string data) { + end(string_generator(std::move(data))); +} + +void response_impl::end(generator_cb cb) { + if (state_ == response_state::BODY_STARTED) { + return; + } + + generator_cb_ = std::move(cb); + + if (state_ == response_state::INITIAL) { + write_head(status_code_); + } else { + // generator_cb is changed, start writing in case it is deferred. + auto handler = strm_->handler(); + handler->resume(*strm_); + } + + state_ = response_state::BODY_STARTED; +} + +void response_impl::write_trailer(header_map h) { + auto handler = strm_->handler(); + handler->submit_trailer(*strm_, std::move(h)); +} + +void response_impl::start_response() { + auto handler = strm_->handler(); + + auto &req = strm_->request().impl(); + + if (!::nghttp2::http2::expect_response_body(req.method(), status_code_)) { + state_ = response_state::BODY_STARTED; + } + + if (handler->start_response(*strm_) != 0) { + handler->stream_error(strm_->get_stream_id(), NGHTTP2_INTERNAL_ERROR); + return; + } +} + +void response_impl::on_close(close_cb cb) { close_cb_ = std::move(cb); } + +void response_impl::call_on_close(uint32_t error_code) { + if (close_cb_) { + close_cb_(error_code); + } +} + +void response_impl::cancel(uint32_t error_code) { + auto handler = strm_->handler(); + handler->stream_error(strm_->get_stream_id(), error_code); +} + +response *response_impl::push(boost::system::error_code &ec, std::string method, + std::string raw_path_query, header_map h) const { + auto handler = strm_->handler(); + return handler->push_promise(ec, *strm_, std::move(method), + std::move(raw_path_query), std::move(h)); +} + +void response_impl::resume() { + auto handler = strm_->handler(); + handler->resume(*strm_); +} + +boost::asio::io_service &response_impl::io_service() { + return strm_->handler()->io_service(); +} + +void response_impl::pushed(bool f) { pushed_ = f; } + +void response_impl::push_promise_sent() { + if (push_promise_sent_) { + return; + } + push_promise_sent_ = true; + if (state_ == response_state::INITIAL) { + return; + } + start_response(); +} + +const header_map &response_impl::header() const { return header_; } + +void response_impl::stream(class stream *s) { strm_ = s; } + +generator_cb::result_type +response_impl::call_read(uint8_t *data, std::size_t len, uint32_t *data_flags) { + if (generator_cb_) { + return generator_cb_(data, len, data_flags); + } + + *data_flags |= NGHTTP2_DATA_FLAG_EOF; + + return 0; +} + +} // namespace server +} // namespace asio_http2 +} // namespace nghttp2 diff --git a/src/asio_server_response_impl.h b/src/asio_server_response_impl.h new file mode 100644 index 0000000..41df7a6 --- /dev/null +++ b/src/asio_server_response_impl.h @@ -0,0 +1,92 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2015 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef ASIO_SERVER_RESPONSE_IMPL_H +#define ASIO_SERVER_RESPONSE_IMPL_H + +#include "nghttp2_config.h" + +#include + +namespace nghttp2 { +namespace asio_http2 { +namespace server { + +class stream; + +enum class response_state { + INITIAL, + // response_impl::write_head() was called + HEADER_DONE, + // response_impl::end() was called + BODY_STARTED, +}; + +class response_impl { +public: + response_impl(); + void write_head(unsigned int status_code, header_map h = header_map{}); + void end(std::string data = ""); + void end(generator_cb cb); + void write_trailer(header_map h); + void on_close(close_cb cb); + void resume(); + + void cancel(uint32_t error_code); + + response *push(boost::system::error_code &ec, std::string method, + std::string raw_path_query, header_map) const; + + boost::asio::io_service &io_service(); + + void start_response(); + + unsigned int status_code() const; + const header_map &header() const; + void pushed(bool f); + void push_promise_sent(); + void stream(class stream *s); + generator_cb::result_type call_read(uint8_t *data, std::size_t len, + uint32_t *data_flags); + void call_on_close(uint32_t error_code); + +private: + class stream *strm_; + header_map header_; + generator_cb generator_cb_; + close_cb close_cb_; + unsigned int status_code_; + response_state state_; + // true if this is pushed stream's response + bool pushed_; + // true if PUSH_PROMISE is sent if this is response of a pushed + // stream + bool push_promise_sent_; +}; + +} // namespace server +} // namespace asio_http2 +} // namespace nghttp2 + +#endif // ASIO_SERVER_RESPONSE_IMPL_H diff --git a/src/asio_server_serve_mux.cc b/src/asio_server_serve_mux.cc new file mode 100644 index 0000000..829568b --- /dev/null +++ b/src/asio_server_serve_mux.cc @@ -0,0 +1,138 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2015 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "asio_server_serve_mux.h" + +#include "asio_server_request_impl.h" +#include "asio_server_request_handler.h" +#include "util.h" +#include "http2.h" + +namespace nghttp2 { + +namespace asio_http2 { + +namespace server { + +bool serve_mux::handle(std::string pattern, request_cb cb) { + if (pattern.empty() || !cb) { + return false; + } + + auto it = mux_.find(pattern); + if (it != std::end(mux_) && (*it).second.user_defined) { + return false; + } + + // if pattern ends with '/' (e.g., /foo/), add implicit permanent + // redirect for '/foo'. + if (pattern.size() >= 2 && pattern.back() == '/') { + auto redirect_pattern = pattern.substr(0, pattern.size() - 1); + auto it = mux_.find(redirect_pattern); + if (it == std::end(mux_) || !(*it).second.user_defined) { + std::string path; + if (pattern[0] == '/') { + path = pattern; + } else { + // skip host part + path = pattern.substr(pattern.find('/')); + } + if (it == std::end(mux_)) { + mux_.emplace(std::move(redirect_pattern), + handler_entry{false, + redirect_handler(301, std::move(path)), + pattern}); + } else { + (*it).second = handler_entry{ + false, redirect_handler(301, std::move(path)), pattern}; + } + } + } + mux_.emplace(pattern, handler_entry{true, std::move(cb), pattern}); + + return true; +} + +request_cb serve_mux::handler(request_impl &req) const { + auto &path = req.uri().path; + if (req.method() != "CONNECT") { + auto clean_path = ::nghttp2::http2::path_join( + nullptr, 0, nullptr, 0, path.c_str(), path.size(), nullptr, 0); + if (clean_path != path) { + auto new_uri = util::percent_encode_path(clean_path); + auto &uref = req.uri(); + if (!uref.raw_query.empty()) { + new_uri += "?"; + new_uri += uref.raw_query; + } + + return redirect_handler(301, std::move(new_uri)); + } + } + auto &host = req.uri().host; + + auto cb = match(host + path); + if (cb) { + return cb; + } + cb = match(path); + if (cb) { + return cb; + } + return status_handler(404); +} + +namespace { +bool path_match(const std::string &pattern, const std::string &path) { + if (pattern.back() != '/') { + return pattern == path; + } + return util::startsWith(path, pattern); +} +} // namespace + +request_cb serve_mux::match(const std::string &path) const { + const handler_entry *ent = nullptr; + size_t best = 0; + for (auto &kv : mux_) { + auto &pattern = kv.first; + if (!path_match(pattern, path)) { + continue; + } + if (!ent || best < pattern.size()) { + best = pattern.size(); + ent = &kv.second; + } + } + if (ent) { + return ent->cb; + } + return request_cb(); +} + +} // namespace server + +} // namespace asio_http2 + +} // namespace nghttp2 diff --git a/src/asio_server_serve_mux.h b/src/asio_server_serve_mux.h new file mode 100644 index 0000000..017a6bc --- /dev/null +++ b/src/asio_server_serve_mux.h @@ -0,0 +1,64 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2015 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef ASIO_SERVER_SERVE_MUX_H +#define ASIO_SERVER_SERVE_MUX_H + +#include "nghttp2_config.h" + +#include + +namespace nghttp2 { + +namespace asio_http2 { + +namespace server { + +class request_impl; + +// port from go's ServeMux + +struct handler_entry { + bool user_defined; + request_cb cb; + std::string pattern; +}; + +class serve_mux { +public: + bool handle(std::string pattern, request_cb cb); + request_cb handler(request_impl &req) const; + request_cb match(const std::string &path) const; + +private: + std::map mux_; +}; + +} // namespace server + +} // namespace asio_http2 + +} // namespace nghttp2 + +#endif // ASIO_SERVER_SERVE_MUX_H diff --git a/src/asio_server_stream.cc b/src/asio_server_stream.cc new file mode 100644 index 0000000..f763c1e --- /dev/null +++ b/src/asio_server_stream.cc @@ -0,0 +1,55 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2015 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "asio_server_stream.h" + +#include "asio_server_http2_handler.h" +#include "asio_server_request_impl.h" +#include "asio_server_response_impl.h" + +namespace nghttp2 { +namespace asio_http2 { +namespace server { + +stream::stream(http2_handler *h, int32_t stream_id) + : handler_(h), stream_id_(stream_id) { + request_.impl().stream(this); + response_.impl().stream(this); +} + +int32_t stream::get_stream_id() const { return stream_id_; } + +class request &stream::request() { + return request_; +} + +class response &stream::response() { + return response_; +} + +http2_handler *stream::handler() const { return handler_; } + +} // namespace server +} // namespace asio_http2 +} // namespace nghttp2 diff --git a/src/asio_server_stream.h b/src/asio_server_stream.h new file mode 100644 index 0000000..cd7e081 --- /dev/null +++ b/src/asio_server_stream.h @@ -0,0 +1,59 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2015 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef ASIO_SERVER_STREAM_H +#define ASIO_SERVER_STREAM_H + +#include "nghttp2_config.h" + +#include + +namespace nghttp2 { +namespace asio_http2 { +namespace server { + +class http2_handler; + +class stream { +public: + stream(http2_handler *h, int32_t stream_id); + + int32_t get_stream_id() const; + class request &request(); + class response &response(); + + http2_handler *handler() const; + +private: + http2_handler *handler_; + class request request_; + class response response_; + int32_t stream_id_; +}; + +} // namespace server +} // namespace asio_http2 +} // namespace nghttp2 + +#endif // ASIO_SERVER_STREAM_H diff --git a/src/asio_server_tls_context.cc b/src/asio_server_tls_context.cc new file mode 100644 index 0000000..11336c9 --- /dev/null +++ b/src/asio_server_tls_context.cc @@ -0,0 +1,85 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2015 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "asio_server_tls_context.h" + +#include + +#include + +#include "ssl.h" +#include "util.h" + +namespace nghttp2 { +namespace asio_http2 { +namespace server { + +namespace { +std::vector &get_alpn_token() { + static auto alpn_token = util::get_default_alpn(); + return alpn_token; +} +} // namespace + +boost::system::error_code +configure_tls_context_easy(boost::system::error_code &ec, + boost::asio::ssl::context &tls_context) { + ec.clear(); + + auto ctx = tls_context.native_handle(); + + SSL_CTX_set_options(ctx, SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | + SSL_OP_NO_COMPRESSION | + SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION | + SSL_OP_SINGLE_ECDH_USE | SSL_OP_NO_TICKET | + SSL_OP_CIPHER_SERVER_PREFERENCE); + SSL_CTX_set_mode(ctx, SSL_MODE_AUTO_RETRY); + SSL_CTX_set_mode(ctx, SSL_MODE_RELEASE_BUFFERS); + + SSL_CTX_set_cipher_list(ctx, ssl::DEFAULT_CIPHER_LIST); + + auto ecdh = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1); + if (ecdh) { + SSL_CTX_set_tmp_ecdh(ctx, ecdh); + EC_KEY_free(ecdh); + } + + SSL_CTX_set_next_protos_advertised_cb( + ctx, + [](SSL *s, const unsigned char **data, unsigned int *len, void *arg) { + auto &token = get_alpn_token(); + + *data = token.data(); + *len = token.size(); + + return SSL_TLSEXT_ERR_OK; + }, + nullptr); + + return ec; +} + +} // namespace server +} // namespace asio_http2 +} // namespace nghttp2 diff --git a/src/asio_server_tls_context.h b/src/asio_server_tls_context.h new file mode 100644 index 0000000..0f9b563 --- /dev/null +++ b/src/asio_server_tls_context.h @@ -0,0 +1,32 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2015 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef ASIO_SERVER_TLS_CONTEXT_H +#define ASIO_SERVER_TLS_CONTEXT_H + +#include "nghttp2_config.h" + +#include + +#endif // ASIO_SERVER_TLS_CONTEXT_H diff --git a/src/base64.h b/src/base64.h new file mode 100644 index 0000000..88e3add --- /dev/null +++ b/src/base64.h @@ -0,0 +1,170 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2013 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef BASE64_H +#define BASE64_H + +#include "nghttp2_config.h" + +#include + +namespace nghttp2 { + +namespace base64 { + +template +std::string encode(InputIterator first, InputIterator last) { + static const char CHAR_TABLE[] = { + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', + 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', + 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', + 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/', + }; + std::string res; + size_t len = last - first; + if (len == 0) { + return res; + } + size_t r = len % 3; + InputIterator j = last - r; + char temp[4]; + while (first != j) { + int n = static_cast(*first++) << 16; + n += static_cast(*first++) << 8; + n += static_cast(*first++); + temp[0] = CHAR_TABLE[n >> 18]; + temp[1] = CHAR_TABLE[(n >> 12) & 0x3fu]; + temp[2] = CHAR_TABLE[(n >> 6) & 0x3fu]; + temp[3] = CHAR_TABLE[n & 0x3fu]; + res.append(temp, sizeof(temp)); + } + if (r == 2) { + int n = static_cast(*first++) << 16; + n += static_cast(*first++) << 8; + temp[0] = CHAR_TABLE[n >> 18]; + temp[1] = CHAR_TABLE[(n >> 12) & 0x3fu]; + temp[2] = CHAR_TABLE[(n >> 6) & 0x3fu]; + temp[3] = '='; + res.append(temp, sizeof(temp)); + } else if (r == 1) { + int n = static_cast(*first++) << 16; + temp[0] = CHAR_TABLE[n >> 18]; + temp[1] = CHAR_TABLE[(n >> 12) & 0x3fu]; + temp[2] = '='; + temp[3] = '='; + res.append(temp, sizeof(temp)); + } + return res; +} + +template +InputIterator getNext(InputIterator first, InputIterator last, const int *tbl) { + for (; first != last; ++first) { + if (tbl[static_cast(*first)] != -1 || *first == '=') { + break; + } + } + return first; +} + +template +std::string decode(InputIterator first, InputIterator last) { + static const int INDEX_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}; + std::string res; + InputIterator k[4]; + int eq = 0; + for (; first != last;) { + for (int i = 1; i <= 4; ++i) { + k[i - 1] = getNext(first, last, INDEX_TABLE); + if (k[i - 1] == last) { + // If i == 1, input may look like this: "TWFu\n" (i.e., + // garbage at the end) + if (i != 1) { + res.clear(); + } + return res; + } else if (*k[i - 1] == '=' && eq == 0) { + eq = i; + } + first = k[i - 1] + 1; + } + if (eq) { + break; + } + int n = (INDEX_TABLE[static_cast(*k[0])] << 18) + + (INDEX_TABLE[static_cast(*k[1])] << 12) + + (INDEX_TABLE[static_cast(*k[2])] << 6) + + INDEX_TABLE[static_cast(*k[3])]; + res += n >> 16; + res += n >> 8 & 0xffu; + res += n & 0xffu; + } + if (eq) { + if (eq <= 2) { + res.clear(); + return res; + } else { + for (int i = eq; i <= 4; ++i) { + if (*k[i - 1] != '=') { + res.clear(); + return res; + } + } + if (eq == 3) { + int n = (INDEX_TABLE[static_cast(*k[0])] << 18) + + (INDEX_TABLE[static_cast(*k[1])] << 12); + res += n >> 16; + } else if (eq == 4) { + int n = (INDEX_TABLE[static_cast(*k[0])] << 18) + + (INDEX_TABLE[static_cast(*k[1])] << 12) + + (INDEX_TABLE[static_cast(*k[2])] << 6); + res += n >> 16; + res += n >> 8 & 0xffu; + } + } + } + return res; +} + +} // namespace base64 + +} // namespace nghttp2 + +#endif // BASE64_H diff --git a/src/buffer.h b/src/buffer.h new file mode 100644 index 0000000..e8c9a5e --- /dev/null +++ b/src/buffer.h @@ -0,0 +1,68 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2014 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef BUFFER_H +#define BUFFER_H + +#include "nghttp2_config.h" + +#include +#include +#include + +namespace nghttp2 { + +template struct Buffer { + Buffer() : pos(std::begin(buf)), last(pos) {} + // Returns the number of bytes to read. + size_t rleft() const { return last - pos; } + // Returns the number of bytes this buffer can store. + size_t wleft() const { return std::end(buf) - last; } + // Writes up to min(wleft(), |count|) bytes from buffer pointed by + // |src|. Returns number of bytes written. + size_t write(const void *src, size_t count) { + count = std::min(count, wleft()); + auto p = static_cast(src); + last = std::copy_n(p, count, last); + return count; + } + size_t write(size_t count) { + count = std::min(count, wleft()); + last += count; + return count; + } + // Drains min(rleft(), |count|) bytes from start of the buffer. + size_t drain(size_t count) { + count = std::min(count, rleft()); + pos += count; + return count; + } + void reset() { pos = last = std::begin(buf); } + std::array buf; + uint8_t *pos, *last; +}; + +} // namespace nghttp2 + +#endif // BUFFER_H diff --git a/src/buffer_test.cc b/src/buffer_test.cc new file mode 100644 index 0000000..38688ed --- /dev/null +++ b/src/buffer_test.cc @@ -0,0 +1,78 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2015 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "buffer_test.h" + +#include +#include +#include + +#include + +#include + +#include "buffer.h" + +namespace nghttp2 { + +void test_buffer_write(void) { + Buffer<16> b; + CU_ASSERT(0 == b.rleft()); + CU_ASSERT(16 == b.wleft()); + + b.write("012", 3); + + CU_ASSERT(3 == b.rleft()); + CU_ASSERT(13 == b.wleft()); + CU_ASSERT(b.pos == std::begin(b.buf)); + + b.drain(3); + + CU_ASSERT(0 == b.rleft()); + CU_ASSERT(13 == b.wleft()); + CU_ASSERT(3 == b.pos - std::begin(b.buf)); + + auto n = b.write("0123456789ABCDEF", 16); + + CU_ASSERT(n == 13); + + CU_ASSERT(13 == b.rleft()); + CU_ASSERT(0 == b.wleft()); + CU_ASSERT(3 == b.pos - std::begin(b.buf)); + CU_ASSERT(0 == memcmp(b.pos, "0123456789ABC", 13)); + + b.reset(); + + CU_ASSERT(0 == b.rleft()); + CU_ASSERT(16 == b.wleft()); + CU_ASSERT(b.pos == std::begin(b.buf)); + + b.write(5); + + CU_ASSERT(5 == b.rleft()); + CU_ASSERT(11 == b.wleft()); + CU_ASSERT(b.pos == std::begin(b.buf)); +} + +} // namespace nghttp2 diff --git a/src/buffer_test.h b/src/buffer_test.h new file mode 100644 index 0000000..abdc987 --- /dev/null +++ b/src/buffer_test.h @@ -0,0 +1,38 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2015 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef BUFFER_TEST_H +#define BUFFER_TEST_H + +#ifdef HAVE_CONFIG_H +#include +#endif // HAVE_CONFIG_H + +namespace nghttp2 { + +void test_buffer_write(void); + +} // namespace nghttp2 + +#endif // BUFFER_TEST_H diff --git a/src/comp_helper.c b/src/comp_helper.c new file mode 100644 index 0000000..8e0c03d --- /dev/null +++ b/src/comp_helper.c @@ -0,0 +1,91 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2013 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "comp_helper.h" +#include + +static void dump_val(json_t *jent, const char *key, uint8_t *val, size_t len) { + json_object_set_new(jent, key, json_pack("s#", val, len)); +} + +json_t *dump_header_table(nghttp2_hd_context *context) { + json_t *obj, *entries; + size_t i; + + obj = json_object(); + entries = json_array(); + for (i = 0; i < context->hd_table.len; ++i) { + nghttp2_hd_entry *ent = nghttp2_hd_table_get(context, i); + json_t *outent = json_object(); + json_object_set_new(outent, "index", json_integer(i + 1)); + dump_val(outent, "name", ent->nv.name, ent->nv.namelen); + dump_val(outent, "value", ent->nv.value, ent->nv.valuelen); + json_object_set_new(outent, "size", + json_integer(ent->nv.namelen + ent->nv.valuelen + + NGHTTP2_HD_ENTRY_OVERHEAD)); + json_array_append_new(entries, outent); + } + json_object_set_new(obj, "entries", entries); + json_object_set_new(obj, "size", json_integer(context->hd_table_bufsize)); + json_object_set_new(obj, "max_size", + json_integer(context->hd_table_bufsize_max)); + return obj; +} + +json_t *dump_header(const uint8_t *name, size_t namelen, const uint8_t *value, + size_t valuelen) { + json_t *nv_pair = json_object(); + char *cname = malloc(namelen + 1); + if (cname == NULL) { + return NULL; + } + memcpy(cname, name, namelen); + cname[namelen] = '\0'; + json_object_set_new(nv_pair, cname, json_pack("s#", value, valuelen)); + free(cname); + return nv_pair; +} + +json_t *dump_headers(const nghttp2_nv *nva, size_t nvlen) { + json_t *headers; + size_t i; + + headers = json_array(); + for (i = 0; i < nvlen; ++i) { + json_array_append_new(headers, dump_header(nva[i].name, nva[i].namelen, + nva[i].value, nva[i].valuelen)); + } + return headers; +} + +void output_json_header(void) { + printf("{\n" + " \"cases\":\n" + " [\n"); +} + +void output_json_footer(void) { + printf(" ]\n" + "}\n"); +} diff --git a/src/comp_helper.h b/src/comp_helper.h new file mode 100644 index 0000000..e86defc --- /dev/null +++ b/src/comp_helper.h @@ -0,0 +1,47 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2013 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGHTTP2_COMP_HELPER_H +#define NGHTTP2_COMP_HELPER_H + +#ifdef HAVE_CONFIG_H +#include +#endif // HAVE_CONFIG_H + +#include + +#include "nghttp2_hd.h" + +json_t *dump_header_table(nghttp2_hd_context *context); + +json_t *dump_header(const uint8_t *name, size_t namelen, const uint8_t *value, + size_t vlauelen); + +json_t *dump_headers(const nghttp2_nv *nva, size_t nvlen); + +void output_json_header(void); + +void output_json_footer(void); + +#endif // NGHTTP2_COMP_HELPER_H diff --git a/src/deflatehd.cc b/src/deflatehd.cc new file mode 100644 index 0000000..cd0c5d5 --- /dev/null +++ b/src/deflatehd.cc @@ -0,0 +1,450 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2013 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifdef HAVE_CONFIG_H +#include +#endif // HAVE_CONFIG_H + +#ifdef HAVE_UNISTD_H +#include +#endif // HAVE_UNISTD_H +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +extern "C" { + +#include "nghttp2_hd.h" +#include "nghttp2_frame.h" + +#include "comp_helper.h" +} + +typedef struct { + size_t table_size; + size_t deflate_table_size; + int http1text; + int dump_header_table; +} deflate_config; + +static deflate_config config; + +static size_t input_sum; +static size_t output_sum; + +static char to_hex_digit(uint8_t n) { + if (n > 9) { + return n - 10 + 'a'; + } + return n + '0'; +} + +static void to_hex(char *dest, const uint8_t *src, size_t len) { + size_t i; + for (i = 0; i < len; ++i) { + *dest++ = to_hex_digit(src[i] >> 4); + *dest++ = to_hex_digit(src[i] & 0xf); + } +} + +static void output_to_json(nghttp2_hd_deflater *deflater, nghttp2_bufs *bufs, + size_t inputlen, const std::vector &nva, + int seq) { + auto len = nghttp2_bufs_len(bufs); + auto hex = std::vector(len * 2); + auto obj = json_object(); + auto comp_ratio = inputlen == 0 ? 0.0 : (double)len / inputlen * 100; + + json_object_set_new(obj, "seq", json_integer(seq)); + json_object_set_new(obj, "input_length", json_integer(inputlen)); + json_object_set_new(obj, "output_length", json_integer(len)); + json_object_set_new(obj, "percentage_of_original_size", + json_real(comp_ratio)); + + auto hexp = hex.data(); + for (auto ci = bufs->head; ci; ci = ci->next) { + auto buf = &ci->buf; + to_hex(hexp, buf->pos, nghttp2_buf_len(buf)); + hexp += nghttp2_buf_len(buf); + } + + if (len == 0) { + json_object_set_new(obj, "wire", json_string("")); + } else { + json_object_set_new(obj, "wire", json_pack("s#", hex.data(), hex.size())); + } + json_object_set_new(obj, "headers", dump_headers(nva.data(), nva.size())); + if (seq == 0) { + // We only change the header table size only once at the beginning + json_object_set_new(obj, "header_table_size", + json_integer(config.table_size)); + } + if (config.dump_header_table) { + json_object_set_new(obj, "header_table", dump_header_table(&deflater->ctx)); + } + json_dumpf(obj, stdout, JSON_PRESERVE_ORDER | JSON_INDENT(2)); + printf("\n"); + json_decref(obj); +} + +static void deflate_hd(nghttp2_hd_deflater *deflater, + const std::vector &nva, size_t inputlen, + int seq) { + ssize_t rv; + nghttp2_bufs bufs; + + nghttp2_bufs_init2(&bufs, 4096, 16, 0, nghttp2_mem_default()); + + rv = nghttp2_hd_deflate_hd_bufs(deflater, &bufs, (nghttp2_nv *)nva.data(), + nva.size()); + if (rv < 0) { + fprintf(stderr, "deflate failed with error code %zd at %d\n", rv, seq); + exit(EXIT_FAILURE); + } + + input_sum += inputlen; + output_sum += nghttp2_bufs_len(&bufs); + + output_to_json(deflater, &bufs, inputlen, nva, seq); + nghttp2_bufs_free(&bufs); +} + +static int deflate_hd_json(json_t *obj, nghttp2_hd_deflater *deflater, + int seq) { + size_t inputlen = 0; + + auto js = json_object_get(obj, "headers"); + if (js == nullptr) { + fprintf(stderr, "'headers' key is missing at %d\n", seq); + return -1; + } + if (!json_is_array(js)) { + fprintf(stderr, "The value of 'headers' key must be an array at %d\n", seq); + return -1; + } + + auto len = json_array_size(js); + auto nva = std::vector(len); + + for (size_t i = 0; i < len; ++i) { + auto nv_pair = json_array_get(js, i); + const char *name; + json_t *value; + + if (!json_is_object(nv_pair) || json_object_size(nv_pair) != 1) { + fprintf(stderr, "bad formatted name/value pair object at %d\n", seq); + return -1; + } + + json_object_foreach(nv_pair, name, value) { + nva[i].name = (uint8_t *)name; + nva[i].namelen = strlen(name); + + if (!json_is_string(value)) { + fprintf(stderr, "value is not string at %d\n", seq); + return -1; + } + + nva[i].value = (uint8_t *)json_string_value(value); + nva[i].valuelen = strlen(json_string_value(value)); + + nva[i].flags = NGHTTP2_NV_FLAG_NONE; + } + + inputlen += nva[i].namelen + nva[i].valuelen; + } + + deflate_hd(deflater, nva, inputlen, seq); + + return 0; +} + +static nghttp2_hd_deflater *init_deflater() { + nghttp2_hd_deflater *deflater; + nghttp2_hd_deflate_new(&deflater, config.deflate_table_size); + nghttp2_hd_deflate_change_table_size(deflater, config.table_size); + return deflater; +} + +static void deinit_deflater(nghttp2_hd_deflater *deflater) { + nghttp2_hd_deflate_del(deflater); +} + +static int perform(void) { + json_error_t error; + + auto json = json_loadf(stdin, 0, &error); + + if (json == nullptr) { + fprintf(stderr, "JSON loading failed\n"); + exit(EXIT_FAILURE); + } + + auto cases = json_object_get(json, "cases"); + + if (cases == nullptr) { + fprintf(stderr, "Missing 'cases' key in root object\n"); + exit(EXIT_FAILURE); + } + + if (!json_is_array(cases)) { + fprintf(stderr, "'cases' must be JSON array\n"); + exit(EXIT_FAILURE); + } + + auto deflater = init_deflater(); + output_json_header(); + auto len = json_array_size(cases); + + for (size_t i = 0; i < len; ++i) { + auto obj = json_array_get(cases, i); + if (!json_is_object(obj)) { + fprintf(stderr, "Unexpected JSON type at %zu. It should be object.\n", i); + continue; + } + if (deflate_hd_json(obj, deflater, i) != 0) { + continue; + } + if (i + 1 < len) { + printf(",\n"); + } + } + output_json_footer(); + deinit_deflater(deflater); + json_decref(json); + return 0; +} + +static int perform_from_http1text(void) { + char line[1 << 14]; + int seq = 0; + + auto deflater = init_deflater(); + output_json_header(); + for (;;) { + std::vector nva; + int end = 0; + size_t inputlen = 0; + + for (;;) { + char *rv = fgets(line, sizeof(line), stdin); + char *val, *val_end; + if (rv == nullptr) { + end = 1; + break; + } else if (line[0] == '\n') { + break; + } + + nva.emplace_back(); + auto &nv = nva.back(); + + val = strchr(line + 1, ':'); + if (val == nullptr) { + fprintf(stderr, "Bad HTTP/1 header field format at %d.\n", seq); + exit(EXIT_FAILURE); + } + *val = '\0'; + ++val; + for (; *val && (*val == ' ' || *val == '\t'); ++val) + ; + for (val_end = val; *val_end && (*val_end != '\r' && *val_end != '\n'); + ++val_end) + ; + *val_end = '\0'; + + nv.namelen = strlen(line); + nv.valuelen = strlen(val); + nv.name = (uint8_t *)strdup(line); + nv.value = (uint8_t *)strdup(val); + nv.flags = NGHTTP2_NV_FLAG_NONE; + + inputlen += nv.namelen + nv.valuelen; + } + + if (!end) { + if (seq > 0) { + printf(",\n"); + } + deflate_hd(deflater, nva, inputlen, seq); + } + + for (auto &nv : nva) { + free(nv.name); + free(nv.value); + } + + if (end) + break; + ++seq; + } + output_json_footer(); + deinit_deflater(deflater); + return 0; +} + +static void print_help(void) { + std::cout << R"(HPACK HTTP/2 header encoder +Usage: deflatehd [OPTIONS] < INPUT + +Reads JSON data or HTTP/1-style header fields from stdin and outputs +deflated header block in JSON array. + +For the JSON input, the root JSON object must contain "context" key, +which indicates which compression context is used. If it is +"request", request compression context is used. Otherwise, response +compression context is used. The value of "cases" key contains the +sequence of input header set. They share the same compression context +and are processed in the order they appear. Each item in the sequence +is a JSON object and it must have at least "headers" key. Its value +is an array of a JSON object containing exactly one name/value pair. + +Example: +{ + "context": "request", + "cases": + [ + { + "headers": [ + { ":method": "GET" }, + { ":path": "/" } + ] + }, + { + "headers": [ + { ":method": "POST" }, + { ":path": "/" } + ] + } + ] +} + +With -t option, the program can accept more familiar HTTP/1 style +header field block. Each header set must be followed by one empty +line: + +Example: + +:method: GET +:scheme: https +:path: / + +:method: POST +user-agent: nghttp2 + +The output of this program can be used as input for inflatehd. + +OPTIONS: + -t, --http1text Use HTTP/1 style header field text as input. + Each header set is delimited by single empty + line. + -s, --table-size= + Set dynamic table size. In the HPACK + specification, this value is denoted by + SETTINGS_HEADER_TABLE_SIZE. + Default: 4096 + -S, --deflate-table-size= + Use first N bytes of dynamic header table + buffer. + Default: 4096 + -d, --dump-header-table + Output dynamic header table.)" << std::endl; +} + +static struct option long_options[] = { + {"http1text", no_argument, nullptr, 't'}, + {"table-size", required_argument, nullptr, 's'}, + {"deflate-table-size", required_argument, nullptr, 'S'}, + {"dump-header-table", no_argument, nullptr, 'd'}, + {nullptr, 0, nullptr, 0}}; + +int main(int argc, char **argv) { + char *end; + + config.table_size = NGHTTP2_HD_DEFAULT_MAX_BUFFER_SIZE; + config.deflate_table_size = NGHTTP2_HD_DEFAULT_MAX_DEFLATE_BUFFER_SIZE; + config.http1text = 0; + config.dump_header_table = 0; + while (1) { + int option_index = 0; + int c = getopt_long(argc, argv, "S:dhs:t", long_options, &option_index); + if (c == -1) { + break; + } + switch (c) { + case 'h': + print_help(); + exit(EXIT_SUCCESS); + case 't': + // --http1text + config.http1text = 1; + break; + case 's': + // --table-size + errno = 0; + config.table_size = strtoul(optarg, &end, 10); + if (errno == ERANGE || *end != '\0') { + fprintf(stderr, "-s: Bad option value\n"); + exit(EXIT_FAILURE); + } + break; + case 'S': + // --deflate-table-size + errno = 0; + config.deflate_table_size = strtoul(optarg, &end, 10); + if (errno == ERANGE || *end != '\0') { + fprintf(stderr, "-S: Bad option value\n"); + exit(EXIT_FAILURE); + } + break; + case 'd': + // --dump-header-table + config.dump_header_table = 1; + break; + case '?': + exit(EXIT_FAILURE); + default: + break; + } + } + if (config.http1text) { + perform_from_http1text(); + } else { + perform(); + } + + auto comp_ratio = input_sum == 0 ? 0.0 : (double)output_sum / input_sum; + + fprintf(stderr, "Overall: input=%zu output=%zu ratio=%.02f\n", input_sum, + output_sum, comp_ratio); + return 0; +} diff --git a/src/h2load.cc b/src/h2load.cc new file mode 100644 index 0000000..5578415 --- /dev/null +++ b/src/h2load.cc @@ -0,0 +1,1528 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2014 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "h2load.h" + +#include +#include +#ifdef HAVE_NETINET_IN_H +#include +#endif // HAVE_NETINET_IN_H +#include +#include +#ifdef HAVE_FCNTL_H +#include +#endif // HAVE_FCNTL_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_SPDYLAY +#include +#endif // HAVE_SPDYLAY + +#include +#include + +#include "http-parser/http_parser.h" + +#include "h2load_http2_session.h" +#ifdef HAVE_SPDYLAY +#include "h2load_spdy_session.h" +#endif // HAVE_SPDYLAY +#include "ssl.h" +#include "http2.h" +#include "util.h" +#include "template.h" + +#ifndef O_BINARY +#define O_BINARY (0) +#endif // O_BINARY + +using namespace nghttp2; + +namespace h2load { + +Config::Config() + : data_length(-1), addrs(nullptr), nreqs(1), nclients(1), nthreads(1), + max_concurrent_streams(-1), window_bits(30), connection_window_bits(30), + no_tls_proto(PROTO_HTTP2), data_fd(-1), port(0), default_port(0), + verbose(false) {} + +Config::~Config() { + freeaddrinfo(addrs); + + if (data_fd != -1) { + close(data_fd); + } +} + +Config config; + +namespace { +void debug(const char *format, ...) { + if (config.verbose) { + fprintf(stderr, "[DEBUG] "); + va_list ap; + va_start(ap, format); + vfprintf(stderr, format, ap); + va_end(ap); + } +} +} // namespace + +namespace { +void debug_nextproto_error() { +#ifdef HAVE_SPDYLAY + debug("no supported protocol was negotiated, expected: %s, " + "spdy/2, spdy/3, spdy/3.1\n", + NGHTTP2_PROTO_VERSION_ID); +#else // !HAVE_SPDYLAY + debug("no supported protocol was negotiated, expected: %s\n", + NGHTTP2_PROTO_VERSION_ID); +#endif // !HAVE_SPDYLAY +} +} // namespace + +RequestStat::RequestStat() : data_offset(0), completed(false) {} + +Stats::Stats(size_t req_todo) + : req_todo(0), req_started(0), req_done(0), req_success(0), + req_status_success(0), req_failed(0), req_error(0), bytes_total(0), + bytes_head(0), bytes_body(0), status(), req_stats(req_todo) {} + +Stream::Stream() : status_success(-1) {} + +namespace { +void writecb(struct ev_loop *loop, ev_io *w, int revents) { + auto client = static_cast(w->data); + auto rv = client->do_write(); + if (rv == Client::ERR_CONNECT_FAIL) { + client->disconnect(); + rv = client->connect(); + if (rv != 0) { + client->fail(); + return; + } + return; + } + if (rv != 0) { + client->fail(); + } +} +} // namespace + +namespace { +void readcb(struct ev_loop *loop, ev_io *w, int revents) { + auto client = static_cast(w->data); + if (client->do_read() != 0) { + client->fail(); + return; + } + writecb(loop, &client->wev, revents); + // client->disconnect() and client->fail() may be called +} +} // namespace + +Client::Client(Worker *worker, size_t req_todo) + : worker(worker), ssl(nullptr), next_addr(config.addrs), reqidx(0), + state(CLIENT_IDLE), first_byte_received(false), req_todo(req_todo), + req_started(0), req_done(0), fd(-1) { + ev_io_init(&wev, writecb, 0, EV_WRITE); + ev_io_init(&rev, readcb, 0, EV_READ); + + wev.data = this; + rev.data = this; +} + +Client::~Client() { disconnect(); } + +int Client::do_read() { return readfn(*this); } +int Client::do_write() { return writefn(*this); } + +int Client::connect() { + record_start_time(&worker->stats); + + while (next_addr) { + auto addr = next_addr; + next_addr = next_addr->ai_next; + fd = util::create_nonblock_socket(addr->ai_family); + if (fd == -1) { + continue; + } + if (config.scheme == "https") { + ssl = SSL_new(worker->ssl_ctx); + + auto config = worker->config; + + if (!util::numeric_host(config->host.c_str())) { + SSL_set_tlsext_host_name(ssl, config->host.c_str()); + } + + SSL_set_fd(ssl, fd); + SSL_set_connect_state(ssl); + } + + auto rv = ::connect(fd, addr->ai_addr, addr->ai_addrlen); + if (rv != 0 && errno != EINPROGRESS) { + if (ssl) { + SSL_free(ssl); + ssl = nullptr; + } + close(fd); + fd = -1; + continue; + } + break; + } + + if (fd == -1) { + return -1; + } + + writefn = &Client::connected; + + ev_io_set(&rev, fd, EV_READ); + ev_io_set(&wev, fd, EV_WRITE); + + ev_io_start(worker->loop, &wev); + + return 0; +} + +void Client::fail() { + process_abandoned_streams(); + + disconnect(); +} + +void Client::disconnect() { + streams.clear(); + session.reset(); + state = CLIENT_IDLE; + ev_io_stop(worker->loop, &wev); + ev_io_stop(worker->loop, &rev); + if (ssl) { + SSL_set_shutdown(ssl, SSL_RECEIVED_SHUTDOWN); + ERR_clear_error(); + SSL_shutdown(ssl); + SSL_free(ssl); + ssl = nullptr; + } + if (fd != -1) { + shutdown(fd, SHUT_WR); + close(fd); + fd = -1; + } +} + +void Client::submit_request() { + auto req_stat = &worker->stats.req_stats[worker->stats.req_started++]; + session->submit_request(req_stat); + ++req_started; +} + +void Client::process_abandoned_streams() { + auto req_abandoned = req_todo - req_done; + + worker->stats.req_failed += req_abandoned; + worker->stats.req_error += req_abandoned; + worker->stats.req_done += req_abandoned; + + req_done = req_todo; +} + +void Client::report_progress() { + if (worker->id == 0 && + worker->stats.req_done % worker->progress_interval == 0) { + std::cout << "progress: " + << worker->stats.req_done * 100 / worker->stats.req_todo + << "% done" << std::endl; + } +} + +namespace { +const char *get_tls_protocol(SSL *ssl) { + auto session = SSL_get_session(ssl); + + switch (session->ssl_version) { + case SSL2_VERSION: + return "SSLv2"; + case SSL3_VERSION: + return "SSLv3"; + case TLS1_2_VERSION: + return "TLSv1.2"; + case TLS1_1_VERSION: + return "TLSv1.1"; + case TLS1_VERSION: + return "TLSv1"; + default: + return "unknown"; + } +} +} // namespace + +namespace { +void print_server_tmp_key(SSL *ssl) { +#if OPENSSL_VERSION_NUMBER >= 0x10002000L + EVP_PKEY *key; + + if (!SSL_get_server_tmp_key(ssl, &key)) { + return; + } + + auto key_del = defer(EVP_PKEY_free, key); + + std::cout << "Server Temp Key: "; + + switch (EVP_PKEY_id(key)) { + case EVP_PKEY_RSA: + std::cout << "RSA " << EVP_PKEY_bits(key) << " bits" << std::endl; + break; + case EVP_PKEY_DH: + std::cout << "DH " << EVP_PKEY_bits(key) << " bits" << std::endl; + break; + case EVP_PKEY_EC: { + auto ec = EVP_PKEY_get1_EC_KEY(key); + auto ec_del = defer(EC_KEY_free, ec); + auto nid = EC_GROUP_get_curve_name(EC_KEY_get0_group(ec)); + auto cname = EC_curve_nid2nist(nid); + if (!cname) { + cname = OBJ_nid2sn(nid); + } + + std::cout << "ECDH " << cname << " " << EVP_PKEY_bits(key) << " bits" + << std::endl; + break; + } + } +#endif // OPENSSL_VERSION_NUMBER >= 0x10002000L +} +} // namespace + +void Client::report_tls_info() { + if (worker->id == 0 && !worker->tls_info_report_done) { + worker->tls_info_report_done = true; + auto cipher = SSL_get_current_cipher(ssl); + std::cout << "Protocol: " << get_tls_protocol(ssl) << "\n" + << "Cipher: " << SSL_CIPHER_get_name(cipher) << std::endl; + print_server_tmp_key(ssl); + } +} + +void Client::terminate_session() { session->terminate(); } + +void Client::on_request(int32_t stream_id) { streams[stream_id] = Stream(); } + +void Client::on_header(int32_t stream_id, const uint8_t *name, size_t namelen, + const uint8_t *value, size_t valuelen) { + auto itr = streams.find(stream_id); + if (itr == std::end(streams)) { + return; + } + auto &stream = (*itr).second; + if (stream.status_success == -1 && namelen == 7 && + util::streq_l(":status", name, namelen)) { + int status = 0; + for (size_t i = 0; i < valuelen; ++i) { + if ('0' <= value[i] && value[i] <= '9') { + status *= 10; + status += value[i] - '0'; + if (status > 999) { + stream.status_success = 0; + return; + } + } else { + break; + } + } + + if (status >= 200 && status < 300) { + ++worker->stats.status[2]; + stream.status_success = 1; + } else if (status < 400) { + ++worker->stats.status[3]; + stream.status_success = 1; + } else if (status < 600) { + ++worker->stats.status[status / 100]; + stream.status_success = 0; + } else { + stream.status_success = 0; + } + } +} + +void Client::on_stream_close(int32_t stream_id, bool success, + RequestStat *req_stat) { + req_stat->stream_close_time = std::chrono::steady_clock::now(); + if (success) { + req_stat->completed = true; + ++worker->stats.req_success; + } + ++worker->stats.req_done; + ++req_done; + if (success && streams[stream_id].status_success == 1) { + ++worker->stats.req_status_success; + } else { + ++worker->stats.req_failed; + } + report_progress(); + streams.erase(stream_id); + if (req_done == req_todo) { + terminate_session(); + return; + } + + if (req_started < req_todo) { + submit_request(); + return; + } +} + +int Client::connection_made() { + if (ssl) { + report_tls_info(); + + const unsigned char *next_proto = nullptr; + unsigned int next_proto_len; + SSL_get0_next_proto_negotiated(ssl, &next_proto, &next_proto_len); + for (int i = 0; i < 2; ++i) { + if (next_proto) { + if (util::check_h2_is_selected(next_proto, next_proto_len)) { + session = make_unique(this); + } else { +#ifdef HAVE_SPDYLAY + auto spdy_version = + spdylay_npn_get_version(next_proto, next_proto_len); + if (spdy_version) { + session = make_unique(this, spdy_version); + } else { + debug_nextproto_error(); + fail(); + return -1; + } +#else // !HAVE_SPDYLAY + debug_nextproto_error(); + fail(); + return -1; +#endif // !HAVE_SPDYLAY + } + } + +#if OPENSSL_VERSION_NUMBER >= 0x10002000L + SSL_get0_alpn_selected(ssl, &next_proto, &next_proto_len); +#else // OPENSSL_VERSION_NUMBER < 0x10002000L + break; +#endif // OPENSSL_VERSION_NUMBER < 0x10002000L + } + + if (!next_proto) { + debug_nextproto_error(); + fail(); + return -1; + } + } else { + switch (config.no_tls_proto) { + case Config::PROTO_HTTP2: + session = make_unique(this); + break; +#ifdef HAVE_SPDYLAY + case Config::PROTO_SPDY2: + session = make_unique(this, SPDYLAY_PROTO_SPDY2); + break; + case Config::PROTO_SPDY3: + session = make_unique(this, SPDYLAY_PROTO_SPDY3); + break; + case Config::PROTO_SPDY3_1: + session = make_unique(this, SPDYLAY_PROTO_SPDY3_1); + break; +#endif // HAVE_SPDYLAY + default: + // unreachable + assert(0); + } + } + + state = CLIENT_CONNECTED; + + session->on_connect(); + + record_connect_time(&worker->stats); + + auto nreq = + std::min(req_todo - req_started, (size_t)config.max_concurrent_streams); + + for (; nreq > 0; --nreq) { + submit_request(); + } + + signal_write(); + + return 0; +} + +int Client::on_read(const uint8_t *data, size_t len) { + auto rv = session->on_read(data, len); + if (rv != 0) { + return -1; + } + worker->stats.bytes_total += len; + signal_write(); + return 0; +} + +int Client::on_write() { + if (session->on_write() != 0) { + return -1; + } + return 0; +} + +int Client::read_clear() { + uint8_t buf[8192]; + + for (;;) { + ssize_t nread; + while ((nread = read(fd, buf, sizeof(buf))) == -1 && errno == EINTR) + ; + if (nread == -1) { + if (errno == EAGAIN || errno == EWOULDBLOCK) { + return 0; + } + return -1; + } + + if (nread == 0) { + return -1; + } + + if (on_read(buf, nread) != 0) { + return -1; + } + + if (!first_byte_received) { + first_byte_received = true; + record_ttfb(&worker->stats); + } + } + + return 0; +} + +int Client::write_clear() { + for (;;) { + if (wb.rleft() > 0) { + ssize_t nwrite; + while ((nwrite = write(fd, wb.pos, wb.rleft())) == -1 && errno == EINTR) + ; + if (nwrite == -1) { + if (errno == EAGAIN || errno == EWOULDBLOCK) { + ev_io_start(worker->loop, &wev); + return 0; + } + return -1; + } + wb.drain(nwrite); + continue; + } + wb.reset(); + if (on_write() != 0) { + return -1; + } + if (wb.rleft() == 0) { + break; + } + } + + ev_io_stop(worker->loop, &wev); + + return 0; +} + +int Client::connected() { + if (!util::check_socket_connected(fd)) { + return ERR_CONNECT_FAIL; + } + ev_io_start(worker->loop, &rev); + ev_io_stop(worker->loop, &wev); + + if (ssl) { + readfn = &Client::tls_handshake; + writefn = &Client::tls_handshake; + + return do_write(); + } + + readfn = &Client::read_clear; + writefn = &Client::write_clear; + + if (connection_made() != 0) { + return -1; + } + + return 0; +} + +int Client::tls_handshake() { + ERR_clear_error(); + + auto rv = SSL_do_handshake(ssl); + + if (rv == 0) { + return -1; + } + + if (rv < 0) { + auto err = SSL_get_error(ssl, rv); + switch (err) { + case SSL_ERROR_WANT_READ: + ev_io_stop(worker->loop, &wev); + return 0; + case SSL_ERROR_WANT_WRITE: + ev_io_start(worker->loop, &wev); + return 0; + default: + return -1; + } + } + + ev_io_stop(worker->loop, &wev); + + readfn = &Client::read_tls; + writefn = &Client::write_tls; + + if (connection_made() != 0) { + return -1; + } + + return 0; +} + +int Client::read_tls() { + uint8_t buf[8192]; + + ERR_clear_error(); + + for (;;) { + auto rv = SSL_read(ssl, buf, sizeof(buf)); + + if (rv == 0) { + return -1; + } + + if (rv < 0) { + auto err = SSL_get_error(ssl, rv); + switch (err) { + case SSL_ERROR_WANT_READ: + return 0; + case SSL_ERROR_WANT_WRITE: + // renegotiation started + return -1; + default: + return -1; + } + } + + if (on_read(buf, rv) != 0) { + return -1; + } + + if (!first_byte_received) { + first_byte_received = true; + record_ttfb(&worker->stats); + } + } +} + +int Client::write_tls() { + ERR_clear_error(); + + for (;;) { + if (wb.rleft() > 0) { + auto rv = SSL_write(ssl, wb.pos, wb.rleft()); + + if (rv == 0) { + return -1; + } + + if (rv < 0) { + auto err = SSL_get_error(ssl, rv); + switch (err) { + case SSL_ERROR_WANT_READ: + // renegotiation started + return -1; + case SSL_ERROR_WANT_WRITE: + ev_io_start(worker->loop, &wev); + return 0; + default: + return -1; + } + } + + wb.drain(rv); + + continue; + } + wb.reset(); + if (on_write() != 0) { + return -1; + } + if (wb.rleft() == 0) { + break; + } + } + + ev_io_stop(worker->loop, &wev); + + return 0; +} + +void Client::record_request_time(RequestStat *req_stat) { + req_stat->request_time = std::chrono::steady_clock::now(); +} + +void Client::record_start_time(Stats *stat) { + stat->start_times.push_back(std::chrono::steady_clock::now()); +} + +void Client::record_connect_time(Stats *stat) { + stat->connect_times.push_back(std::chrono::steady_clock::now()); +} + +void Client::record_ttfb(Stats *stat) { + stat->ttfbs.push_back(std::chrono::steady_clock::now()); +} + +void Client::signal_write() { ev_io_start(worker->loop, &wev); } + +Worker::Worker(uint32_t id, SSL_CTX *ssl_ctx, size_t req_todo, size_t nclients, + Config *config) + : stats(req_todo), loop(ev_loop_new(0)), ssl_ctx(ssl_ctx), config(config), + id(id), tls_info_report_done(false) { + stats.req_todo = req_todo; + progress_interval = std::max((size_t)1, req_todo / 10); + + auto nreqs_per_client = req_todo / nclients; + auto nreqs_rem = req_todo % nclients; + + for (size_t i = 0; i < nclients; ++i) { + auto req_todo = nreqs_per_client; + if (nreqs_rem > 0) { + ++req_todo; + --nreqs_rem; + } + clients.push_back(make_unique(this, req_todo)); + } +} + +Worker::~Worker() { + // first clear clients so that io watchers are stopped before + // destructing ev_loop. + clients.clear(); + ev_loop_destroy(loop); +} + +void Worker::run() { + for (auto &client : clients) { + if (client->connect() != 0) { + std::cerr << "client could not connect to host" << std::endl; + client->fail(); + } + } + ev_run(loop, 0); +} + +namespace { +// Returns percentage of number of samples within mean +/- sd. +template +double within_sd(const std::vector &samples, const Duration &mean, + const Duration &sd) { + if (samples.size() == 0) { + return 0.0; + } + auto lower = mean - sd; + auto upper = mean + sd; + auto m = std::count_if( + std::begin(samples), std::end(samples), + [&lower, &upper](const Duration &t) { return lower <= t && t <= upper; }); + return (m / static_cast(samples.size())) * 100; +} +} // namespace + +namespace { +// Computes statistics using |samples|. The min, max, mean, sd, and +// percentage of number of samples within mean +/- sd are computed. +template +TimeStat compute_time_stat(const std::vector &samples) { + if (samples.size() == 0) { + return {Duration::zero(), Duration::zero(), Duration::zero(), + Duration::zero(), 0.0}; + } + // standard deviation calculated using Rapid calculation method: + // http://en.wikipedia.org/wiki/Standard_deviation#Rapid_calculation_methods + double a = 0, q = 0; + size_t n = 0; + int64_t sum = 0; + auto res = TimeStat{Duration::max(), Duration::min()}; + for (const auto &t : samples) { + ++n; + res.min = std::min(res.min, t); + res.max = std::max(res.max, t); + sum += t.count(); + + auto na = a + (t.count() - a) / n; + q += (t.count() - a) * (t.count() - na); + a = na; + } + + res.mean = Duration(sum / n); + res.sd = Duration(static_cast(sqrt(q / n))); + res.within_sd = within_sd(samples, res.mean, res.sd); + + return res; +} +} // namespace + +namespace { +TimeStats +process_time_stats(const std::vector> &workers) { + size_t nrequest_times = 0, nttfb_times = 0; + for (const auto &w : workers) { + nrequest_times += w->stats.req_stats.size(); + nttfb_times += w->stats.ttfbs.size(); + } + + std::vector request_times; + request_times.reserve(nrequest_times); + std::vector connect_times, ttfb_times; + connect_times.reserve(nttfb_times); + ttfb_times.reserve(nttfb_times); + + for (const auto &w : workers) { + for (const auto &req_stat : w->stats.req_stats) { + if (!req_stat.completed) { + continue; + } + request_times.push_back( + std::chrono::duration_cast( + req_stat.stream_close_time - req_stat.request_time)); + } + + const auto &stat = w->stats; + // rule out cases where we started but didn't connect or get the + // first byte (errors). We will get connect event before FFTB. + assert(stat.start_times.size() >= stat.ttfbs.size()); + assert(stat.connect_times.size() >= stat.ttfbs.size()); + for (size_t i = 0; i < stat.ttfbs.size(); ++i) { + connect_times.push_back( + std::chrono::duration_cast( + stat.connect_times[i] - stat.start_times[i])); + + ttfb_times.push_back( + std::chrono::duration_cast( + stat.ttfbs[i] - stat.start_times[i])); + } + } + + return {compute_time_stat(request_times), compute_time_stat(connect_times), + compute_time_stat(ttfb_times)}; +} +} // namespace + +namespace { +void resolve_host() { + int rv; + addrinfo hints, *res; + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = 0; + hints.ai_flags = AI_ADDRCONFIG; + + rv = getaddrinfo(config.host.c_str(), util::utos(config.port).c_str(), &hints, + &res); + if (rv != 0) { + std::cerr << "getaddrinfo() failed: " << gai_strerror(rv) << std::endl; + exit(EXIT_FAILURE); + } + if (res == nullptr) { + std::cerr << "No address returned" << std::endl; + exit(EXIT_FAILURE); + } + config.addrs = res; +} +} // namespace + +namespace { +std::string get_reqline(const char *uri, const http_parser_url &u) { + std::string reqline; + + if (util::has_uri_field(u, UF_PATH)) { + reqline = util::get_uri_field(uri, u, UF_PATH); + } else { + reqline = "/"; + } + + if (util::has_uri_field(u, UF_QUERY)) { + reqline += "?"; + reqline += util::get_uri_field(uri, u, UF_QUERY); + } + + return reqline; +} +} // namespace + +namespace { +int client_select_next_proto_cb(SSL *ssl, unsigned char **out, + unsigned char *outlen, const unsigned char *in, + unsigned int inlen, void *arg) { + if (util::select_h2(const_cast(out), outlen, in, + inlen)) { + return SSL_TLSEXT_ERR_OK; + } +#ifdef HAVE_SPDYLAY + if (spdylay_select_next_protocol(out, outlen, in, inlen) > 0) { + return SSL_TLSEXT_ERR_OK; + } +#endif + return SSL_TLSEXT_ERR_NOACK; +} +} // namespace + +namespace { +template +std::vector parse_uris(Iterator first, Iterator last) { + std::vector reqlines; + + // First URI is treated specially. We use scheme, host and port of + // this URI and ignore those in the remaining URIs if present. + http_parser_url u; + memset(&u, 0, sizeof(u)); + + if (first == last) { + std::cerr << "no URI available" << std::endl; + exit(EXIT_FAILURE); + } + + auto uri = (*first).c_str(); + ++first; + + if (http_parser_parse_url(uri, strlen(uri), 0, &u) != 0 || + !util::has_uri_field(u, UF_SCHEMA) || !util::has_uri_field(u, UF_HOST)) { + std::cerr << "invalid URI: " << uri << std::endl; + exit(EXIT_FAILURE); + } + + config.scheme = util::get_uri_field(uri, u, UF_SCHEMA); + config.host = util::get_uri_field(uri, u, UF_HOST); + config.default_port = util::get_default_port(uri, u); + if (util::has_uri_field(u, UF_PORT)) { + config.port = u.port; + } else { + config.port = config.default_port; + } + + reqlines.push_back(get_reqline(uri, u)); + + for (; first != last; ++first) { + http_parser_url u; + memset(&u, 0, sizeof(u)); + + auto uri = (*first).c_str(); + + if (http_parser_parse_url(uri, strlen(uri), 0, &u) != 0) { + std::cerr << "invalid URI: " << uri << std::endl; + exit(EXIT_FAILURE); + } + + reqlines.push_back(get_reqline(uri, u)); + } + + return reqlines; +} +} // namespace + +namespace { +std::vector read_uri_from_file(std::istream &infile) { + std::vector uris; + std::string line_uri; + while (std::getline(infile, line_uri)) { + uris.push_back(line_uri); + } + + return uris; +} +} // namespace + +namespace { +void print_version(std::ostream &out) { + out << "h2load nghttp2/" NGHTTP2_VERSION << std::endl; +} +} // namespace + +namespace { +void print_usage(std::ostream &out) { + out << R"(Usage: h2load [OPTIONS]... [URI]... +benchmarking tool for HTTP/2 and SPDY server)" << std::endl; +} +} // namespace + +namespace { +void print_help(std::ostream &out) { + print_usage(out); + + out << R"( + Specify URI to access. Multiple URIs can be specified. + URIs are used in this order for each client. All URIs + are used, then first URI is used and then 2nd URI, and + so on. The scheme, host and port in the subsequent + URIs, if present, are ignored. Those in the first URI + are used solely. +Options: + -n, --requests= + Number of requests. + Default: )" << config.nreqs << R"( + -c, --clients= + Number of concurrent clients. + Default: )" << config.nclients << R"( + -t, --threads= + Number of native threads. + Default: )" << config.nthreads << R"( + -i, --input-file= + Path of a file with multiple URIs are separated by EOLs. + This option will disable URIs getting from command-line. + If '-' is given as , URIs will be read from stdin. + URIs are used in this order for each client. All URIs + are used, then first URI is used and then 2nd URI, and + so on. The scheme, host and port in the subsequent + URIs, if present, are ignored. Those in the first URI + are used solely. + -m, --max-concurrent-streams=(auto|) + Max concurrent streams to issue per session. If "auto" + is given, the number of given URIs is used. + Default: auto + -w, --window-bits= + Sets the stream level initial window size to (2**)-1. + For SPDY, 2** is used instead. + Default: )" << config.window_bits << R"( + -W, --connection-window-bits= + Sets the connection level initial window size to + (2**)-1. For SPDY, if is strictly less than 16, + this option is ignored. Otherwise 2** is used for + SPDY. + Default: )" << config.connection_window_bits << R"( + -H, --header=
        + Add/Override a header to the requests. + -p, --no-tls-proto= + Specify ALPN identifier of the protocol to be used when + accessing http URI without SSL/TLS.)"; +#ifdef HAVE_SPDYLAY + out << R"( + Available protocols: spdy/2, spdy/3, spdy/3.1 and )"; +#else // !HAVE_SPDYLAY + out << R"( + Available protocol: )"; +#endif // !HAVE_SPDYLAY + out << NGHTTP2_CLEARTEXT_PROTO_VERSION_ID << R"( + Default: )" << NGHTTP2_CLEARTEXT_PROTO_VERSION_ID << R"( + -d, --data= + Post FILE to server. The request method is changed to + POST. + -v, --verbose + Output debug information. + --version Display version information and exit. + -h, --help Display this help and exit.)" << std::endl; +} +} // namespace + +int main(int argc, char **argv) { + std::string datafile; + while (1) { + static int flag = 0; + static option long_options[] = { + {"requests", required_argument, nullptr, 'n'}, + {"clients", required_argument, nullptr, 'c'}, + {"data", required_argument, nullptr, 'd'}, + {"threads", required_argument, nullptr, 't'}, + {"max-concurrent-streams", required_argument, nullptr, 'm'}, + {"window-bits", required_argument, nullptr, 'w'}, + {"connection-window-bits", required_argument, nullptr, 'W'}, + {"input-file", required_argument, nullptr, 'i'}, + {"header", required_argument, nullptr, 'H'}, + {"no-tls-proto", required_argument, nullptr, 'p'}, + {"verbose", no_argument, nullptr, 'v'}, + {"help", no_argument, nullptr, 'h'}, + {"version", no_argument, &flag, 1}, + {nullptr, 0, nullptr, 0}}; + int option_index = 0; + auto c = getopt_long(argc, argv, "hvW:c:d:m:n:p:t:w:H:i:", long_options, + &option_index); + if (c == -1) { + break; + } + switch (c) { + case 'n': + config.nreqs = strtoul(optarg, nullptr, 10); + break; + case 'c': + config.nclients = strtoul(optarg, nullptr, 10); + break; + case 'd': + datafile = optarg; + break; + case 't': +#ifdef NOTHREADS + std::cerr << "-t: WARNING: Threading disabled at build time, " + << "no threads created." << std::endl; +#else + config.nthreads = strtoul(optarg, nullptr, 10); +#endif // NOTHREADS + break; + case 'm': + if (util::strieq("auto", optarg)) { + config.max_concurrent_streams = -1; + } else { + config.max_concurrent_streams = strtoul(optarg, nullptr, 10); + } + break; + case 'w': + case 'W': { + errno = 0; + char *endptr = nullptr; + auto n = strtoul(optarg, &endptr, 10); + if (errno == 0 && *endptr == '\0' && n < 31) { + if (c == 'w') { + config.window_bits = n; + } else { + config.connection_window_bits = n; + } + } else { + std::cerr << "-" << static_cast(c) + << ": specify the integer in the range [0, 30], inclusive" + << std::endl; + exit(EXIT_FAILURE); + } + break; + } + case 'H': { + char *header = optarg; + // Skip first possible ':' in the header name + char *value = strchr(optarg + 1, ':'); + if (!value || (header[0] == ':' && header + 1 == value)) { + std::cerr << "-H: invalid header: " << optarg << std::endl; + exit(EXIT_FAILURE); + } + *value = 0; + value++; + while (isspace(*value)) { + value++; + } + if (*value == 0) { + // This could also be a valid case for suppressing a header + // similar to curl + std::cerr << "-H: invalid header - value missing: " << optarg + << std::endl; + exit(EXIT_FAILURE); + } + // Note that there is no processing currently to handle multiple + // message-header fields with the same field name + config.custom_headers.emplace_back(header, value); + util::inp_strlower(config.custom_headers.back().name); + break; + } + case 'i': { + config.ifile = std::string(optarg); + break; + } + case 'p': + if (util::strieq(NGHTTP2_CLEARTEXT_PROTO_VERSION_ID, optarg)) { + config.no_tls_proto = Config::PROTO_HTTP2; +#ifdef HAVE_SPDYLAY + } else if (util::strieq("spdy/2", optarg)) { + config.no_tls_proto = Config::PROTO_SPDY2; + } else if (util::strieq("spdy/3", optarg)) { + config.no_tls_proto = Config::PROTO_SPDY3; + } else if (util::strieq("spdy/3.1", optarg)) { + config.no_tls_proto = Config::PROTO_SPDY3_1; +#endif // HAVE_SPDYLAY + } else { + std::cerr << "-p: unsupported protocol " << optarg << std::endl; + exit(EXIT_FAILURE); + } + break; + case 'v': + config.verbose = true; + break; + case 'h': + print_help(std::cout); + exit(EXIT_SUCCESS); + case '?': + util::show_candidates(argv[optind - 1], long_options); + exit(EXIT_FAILURE); + case 0: + switch (flag) { + case 1: + // version option + print_version(std::cout); + exit(EXIT_SUCCESS); + } + break; + default: + break; + } + } + + if (argc == optind) { + if (config.ifile.empty()) { + std::cerr << "no URI or input file given" << std::endl; + exit(EXIT_FAILURE); + } + } + + if (config.nreqs == 0) { + std::cerr << "-n: the number of requests must be strictly greater than 0." + << std::endl; + exit(EXIT_FAILURE); + } + + if (config.max_concurrent_streams == 0) { + std::cerr << "-m: the max concurrent streams must be strictly greater " + << "than 0." << std::endl; + exit(EXIT_FAILURE); + } + + if (config.nthreads == 0) { + std::cerr << "-t: the number of threads must be strictly greater than 0." + << std::endl; + exit(EXIT_FAILURE); + } + + if (config.nreqs < config.nclients) { + std::cerr << "-n, -c: the number of requests must be greater than or " + << "equal to the concurrent clients." << std::endl; + exit(EXIT_FAILURE); + } + + if (config.nclients < config.nthreads) { + std::cerr << "-c, -t: the number of client must be greater than or equal " + "to the number of threads." << std::endl; + exit(EXIT_FAILURE); + } + + if (config.nthreads > std::thread::hardware_concurrency()) { + std::cerr << "-t: warning: the number of threads is greater than hardware " + << "cores." << std::endl; + } + + if (!datafile.empty()) { + config.data_fd = open(datafile.c_str(), O_RDONLY | O_BINARY); + if (config.data_fd == -1) { + std::cerr << "-d: Could not open file " << datafile << std::endl; + exit(EXIT_FAILURE); + } + struct stat data_stat; + if (fstat(config.data_fd, &data_stat) == -1) { + std::cerr << "-d: Could not stat file " << datafile << std::endl; + exit(EXIT_FAILURE); + } + config.data_length = data_stat.st_size; + } + + struct sigaction act; + memset(&act, 0, sizeof(struct sigaction)); + act.sa_handler = SIG_IGN; + sigaction(SIGPIPE, &act, nullptr); + OPENSSL_config(nullptr); + OpenSSL_add_all_algorithms(); + SSL_load_error_strings(); + SSL_library_init(); + +#ifndef NOTHREADS + ssl::LibsslGlobalLock lock; +#endif // NOTHREADS + + auto ssl_ctx = SSL_CTX_new(SSLv23_client_method()); + if (!ssl_ctx) { + std::cerr << "Failed to create SSL_CTX: " + << ERR_error_string(ERR_get_error(), nullptr) << std::endl; + exit(EXIT_FAILURE); + } + + SSL_CTX_set_options(ssl_ctx, + SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | + SSL_OP_NO_COMPRESSION | + SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION); + SSL_CTX_set_mode(ssl_ctx, SSL_MODE_AUTO_RETRY); + SSL_CTX_set_mode(ssl_ctx, SSL_MODE_RELEASE_BUFFERS); + + if (SSL_CTX_set_cipher_list(ssl_ctx, ssl::DEFAULT_CIPHER_LIST) == 0) { + std::cerr << "SSL_CTX_set_cipher_list failed: " + << ERR_error_string(ERR_get_error(), nullptr) << std::endl; + exit(EXIT_FAILURE); + } + + SSL_CTX_set_next_proto_select_cb(ssl_ctx, client_select_next_proto_cb, + nullptr); + +#if OPENSSL_VERSION_NUMBER >= 0x10002000L + auto proto_list = util::get_default_alpn(); +#ifdef HAVE_SPDYLAY + static const char spdy_proto_list[] = "\x8spdy/3.1\x6spdy/3\x6spdy/2"; + std::copy_n(spdy_proto_list, sizeof(spdy_proto_list) - 1, + std::back_inserter(proto_list)); +#endif // HAVE_SPDYLAY + SSL_CTX_set_alpn_protos(ssl_ctx, proto_list.data(), proto_list.size()); +#endif // OPENSSL_VERSION_NUMBER >= 0x10002000L + + std::vector reqlines; + + if (config.ifile.empty()) { + std::vector uris; + std::copy(&argv[optind], &argv[argc], std::back_inserter(uris)); + reqlines = parse_uris(std::begin(uris), std::end(uris)); + } else { + std::vector uris; + if (config.ifile == "-") { + uris = read_uri_from_file(std::cin); + } else { + std::ifstream infile(config.ifile); + if (!infile) { + std::cerr << "cannot read input file: " << config.ifile << std::endl; + exit(EXIT_FAILURE); + } + + uris = read_uri_from_file(infile); + } + + reqlines = parse_uris(std::begin(uris), std::end(uris)); + } + + if (config.max_concurrent_streams == -1) { + config.max_concurrent_streams = reqlines.size(); + } + + Headers shared_nva; + shared_nva.emplace_back(":scheme", config.scheme); + if (config.port != config.default_port) { + shared_nva.emplace_back(":authority", + config.host + ":" + util::utos(config.port)); + } else { + shared_nva.emplace_back(":authority", config.host); + } + shared_nva.emplace_back(":method", config.data_fd == -1 ? "GET" : "POST"); + + // list overridalbe headers + auto override_hdrs = + make_array(":authority", ":host", ":method", ":scheme"); + + for (auto &kv : config.custom_headers) { + if (std::find(std::begin(override_hdrs), std::end(override_hdrs), + kv.name) != std::end(override_hdrs)) { + // override header + for (auto &nv : shared_nva) { + if ((nv.name == ":authority" && kv.name == ":host") || + (nv.name == kv.name)) { + nv.value = kv.value; + } + } + } else { + // add additional headers + shared_nva.push_back(kv); + } + } + + for (auto &req : reqlines) { + // For nghttp2 + std::vector nva; + + nva.push_back(http2::make_nv_ls(":path", req)); + + for (auto &nv : shared_nva) { + nva.push_back(http2::make_nv(nv.name, nv.value, false)); + } + + config.nva.push_back(std::move(nva)); + + // For spdylay + std::vector cva; + + cva.push_back(":path"); + cva.push_back(req.c_str()); + + for (auto &nv : shared_nva) { + if (nv.name == ":authority") { + cva.push_back(":host"); + } else { + cva.push_back(nv.name.c_str()); + } + cva.push_back(nv.value.c_str()); + } + cva.push_back(":version"); + cva.push_back("HTTP/1.1"); + cva.push_back(nullptr); + + config.nv.push_back(std::move(cva)); + } + + resolve_host(); + + if (config.nclients == 1) { + config.nthreads = 1; + } + + size_t nreqs_per_thread = config.nreqs / config.nthreads; + ssize_t nreqs_rem = config.nreqs % config.nthreads; + + size_t nclients_per_thread = config.nclients / config.nthreads; + ssize_t nclients_rem = config.nclients % config.nthreads; + + std::cout << "starting benchmark..." << std::endl; + + auto start = std::chrono::steady_clock::now(); + + std::vector> workers; + workers.reserve(config.nthreads); + +#ifndef NOTHREADS + std::vector> futures; + for (size_t i = 0; i < config.nthreads - 1; ++i) { + auto nreqs = nreqs_per_thread + (nreqs_rem-- > 0); + auto nclients = nclients_per_thread + (nclients_rem-- > 0); + std::cout << "spawning thread #" << i << ": " << nclients + << " concurrent clients, " << nreqs << " total requests" + << std::endl; + workers.push_back( + make_unique(i, ssl_ctx, nreqs, nclients, &config)); + auto &worker = workers.back(); + futures.push_back( + std::async(std::launch::async, [&worker]() { worker->run(); })); + } +#endif // NOTHREADS + + auto nreqs_last = nreqs_per_thread + (nreqs_rem-- > 0); + auto nclients_last = nclients_per_thread + (nclients_rem-- > 0); + std::cout << "spawning thread #" << (config.nthreads - 1) << ": " + << nclients_last << " concurrent clients, " << nreqs_last + << " total requests" << std::endl; + workers.push_back(make_unique(config.nthreads - 1, ssl_ctx, + nreqs_last, nclients_last, &config)); + workers.back()->run(); + +#ifndef NOTHREADS + for (auto &fut : futures) { + fut.get(); + } +#endif // NOTHREADS + + auto end = std::chrono::steady_clock::now(); + auto duration = + std::chrono::duration_cast(end - start); + + Stats stats(0); + for (const auto &w : workers) { + const auto &s = w->stats; + + stats.req_todo += s.req_todo; + stats.req_started += s.req_started; + stats.req_done += s.req_done; + stats.req_success += s.req_success; + stats.req_status_success += s.req_status_success; + stats.req_failed += s.req_failed; + stats.req_error += s.req_error; + stats.bytes_total += s.bytes_total; + stats.bytes_head += s.bytes_head; + stats.bytes_body += s.bytes_body; + + for (size_t i = 0; i < stats.status.size(); ++i) { + stats.status[i] += s.status[i]; + } + } + + auto ts = process_time_stats(workers); + + // Requests which have not been issued due to connection errors, are + // counted towards req_failed and req_error. + auto req_not_issued = + stats.req_todo - stats.req_status_success - stats.req_failed; + stats.req_failed += req_not_issued; + stats.req_error += req_not_issued; + + // UI is heavily inspired by weighttp[1] and wrk[2] + // + // [1] https://github.com/lighttpd/weighttp + // [2] https://github.com/wg/wrk + size_t rps = 0; + int64_t bps = 0; + if (duration.count() > 0) { + auto secd = static_cast(duration.count()) / (1000 * 1000); + rps = stats.req_success / secd; + bps = stats.bytes_total / secd; + } + + std::cout << R"( +finished in )" << util::format_duration(duration) << ", " << rps << " req/s, " + << util::utos_with_funit(bps) << R"(B/s +requests: )" << stats.req_todo << " total, " << stats.req_started + << " started, " << stats.req_done << " done, " + << stats.req_status_success << " succeeded, " << stats.req_failed + << " failed, " << stats.req_error << R"( errored +status codes: )" << stats.status[2] << " 2xx, " << stats.status[3] << " 3xx, " + << stats.status[4] << " 4xx, " << stats.status[5] << R"( 5xx +traffic: )" << stats.bytes_total << " bytes total, " << stats.bytes_head + << " bytes headers, " << stats.bytes_body << R"( bytes data + min max mean sd +/- sd +time for request: )" << std::setw(10) << util::format_duration(ts.request.min) + << " " << std::setw(10) << util::format_duration(ts.request.max) + << " " << std::setw(10) << util::format_duration(ts.request.mean) + << " " << std::setw(10) << util::format_duration(ts.request.sd) + << std::setw(9) << util::dtos(ts.request.within_sd) << "%" + << "\ntime for connect: " << std::setw(10) + << util::format_duration(ts.connect.min) << " " << std::setw(10) + << util::format_duration(ts.connect.max) << " " << std::setw(10) + << util::format_duration(ts.connect.mean) << " " << std::setw(10) + << util::format_duration(ts.connect.sd) << std::setw(9) + << util::dtos(ts.connect.within_sd) << "%" + << "\ntime to 1st byte: " << std::setw(10) + << util::format_duration(ts.ttfb.min) << " " << std::setw(10) + << util::format_duration(ts.ttfb.max) << " " << std::setw(10) + << util::format_duration(ts.ttfb.mean) << " " << std::setw(10) + << util::format_duration(ts.ttfb.sd) << std::setw(9) + << util::dtos(ts.ttfb.within_sd) << "%" << std::endl; + + SSL_CTX_free(ssl_ctx); + + return 0; +} + +} // namespace h2load + +int main(int argc, char **argv) { return h2load::main(argc, argv); } diff --git a/src/h2load.h b/src/h2load.h new file mode 100644 index 0000000..78a2038 --- /dev/null +++ b/src/h2load.h @@ -0,0 +1,249 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2014 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef H2LOAD_H +#define H2LOAD_H + +#include "nghttp2_config.h" + +#include +#ifdef HAVE_SYS_SOCKET_H +#include +#endif // HAVE_SYS_SOCKET_H +#ifdef HAVE_NETDB_H +#include +#endif // HAVE_NETDB_H + +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include + +#include "http2.h" +#include "buffer.h" + +using namespace nghttp2; + +namespace h2load { + +class Session; + +struct Config { + std::vector> nva; + std::vector> nv; + nghttp2::Headers custom_headers; + std::string scheme; + std::string host; + std::string ifile; + // length of upload data + int64_t data_length; + addrinfo *addrs; + size_t nreqs; + size_t nclients; + size_t nthreads; + // The maximum number of concurrent streams per session. + ssize_t max_concurrent_streams; + size_t window_bits; + size_t connection_window_bits; + enum { PROTO_HTTP2, PROTO_SPDY2, PROTO_SPDY3, PROTO_SPDY3_1 } no_tls_proto; + // file descriptor for upload data + int data_fd; + uint16_t port; + uint16_t default_port; + bool verbose; + + Config(); + ~Config(); +}; + +struct RequestStat { + RequestStat(); + // time point when request was sent + std::chrono::steady_clock::time_point request_time; + // time point when stream was closed + std::chrono::steady_clock::time_point stream_close_time; + // upload data length sent so far + int64_t data_offset; + // true if stream was successfully closed. This means stream was + // not reset, but it does not mean HTTP level error (e.g., 404). + bool completed; +}; + +template struct TimeStat { + // min, max, mean and sd (standard deviation) + Duration min, max, mean, sd; + // percentage of samples inside mean -/+ sd + double within_sd; +}; + +struct TimeStats { + // time for request + TimeStat request; + // time for connect + TimeStat connect; + // time to first byte (TTFB) + TimeStat ttfb; +}; + +enum TimeStatType { STAT_REQUEST, STAT_CONNECT, STAT_FIRST_BYTE }; + +struct Stats { + Stats(size_t req_todo); + // The total number of requests + size_t req_todo; + // The number of requests issued so far + size_t req_started; + // The number of requests finished + size_t req_done; + // The number of requests completed successfull, but not necessarily + // means successful HTTP status code. + size_t req_success; + // The number of requests marked as success. HTTP status code is + // also considered as success. This is subset of req_done. + size_t req_status_success; + // The number of requests failed. This is subset of req_done. + size_t req_failed; + // The number of requests failed due to network errors. This is + // subset of req_failed. + size_t req_error; + // The number of bytes received on the "wire". If SSL/TLS is used, + // this is the number of decrypted bytes the application received. + int64_t bytes_total; + // The number of bytes received in HEADERS frame payload. + int64_t bytes_head; + // The number of bytes received in DATA frame. + int64_t bytes_body; + // The number of each HTTP status category, status[i] is status code + // in the range [i*100, (i+1)*100). + std::array status; + // The statistics per request + std::vector req_stats; + // time connect starts + std::vector start_times; + // time to connect + std::vector connect_times; + // time to first byte (TTFB) + std::vector ttfbs; +}; + +enum ClientState { CLIENT_IDLE, CLIENT_CONNECTED }; + +struct Client; + +struct Worker { + std::vector> clients; + Stats stats; + struct ev_loop *loop; + SSL_CTX *ssl_ctx; + Config *config; + size_t progress_interval; + uint32_t id; + bool tls_info_report_done; + + Worker(uint32_t id, SSL_CTX *ssl_ctx, size_t nreq_todo, size_t nclients, + Config *config); + ~Worker(); + Worker(Worker &&o) = default; + void run(); +}; + +struct Stream { + int status_success; + Stream(); +}; + +struct Client { + std::unordered_map streams; + std::unique_ptr session; + ev_io wev; + ev_io rev; + std::function readfn, writefn; + Worker *worker; + SSL *ssl; + addrinfo *next_addr; + size_t reqidx; + ClientState state; + bool first_byte_received; + // The number of requests this client has to issue. + size_t req_todo; + // The number of requests this client has issued so far. + size_t req_started; + // The number of requests this client has done so far. + size_t req_done; + int fd; + Buffer<65536> wb; + + enum { ERR_CONNECT_FAIL = -100 }; + + Client(Worker *worker, size_t req_todo); + ~Client(); + int connect(); + void disconnect(); + void fail(); + void submit_request(); + void process_abandoned_streams(); + void report_progress(); + void report_tls_info(); + void terminate_session(); + + int do_read(); + int do_write(); + + // low-level I/O callback functions called by do_read/do_write + int connected(); + int read_clear(); + int write_clear(); + int tls_handshake(); + int read_tls(); + int write_tls(); + + int on_read(const uint8_t *data, size_t len); + int on_write(); + + int connection_made(); + + void on_request(int32_t stream_id); + void on_header(int32_t stream_id, const uint8_t *name, size_t namelen, + const uint8_t *value, size_t valuelen); + void on_stream_close(int32_t stream_id, bool success, RequestStat *req_stat); + + void record_request_time(RequestStat *req_stat); + void record_start_time(Stats *stat); + void record_connect_time(Stats *stat); + void record_ttfb(Stats *stat); + + void signal_write(); +}; + +} // namespace h2load + +#endif // H2LOAD_H diff --git a/src/h2load_http2_session.cc b/src/h2load_http2_session.cc new file mode 100644 index 0000000..0cbb702 --- /dev/null +++ b/src/h2load_http2_session.cc @@ -0,0 +1,260 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2014 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "h2load_http2_session.h" + +#include +#include + +#include "h2load.h" +#include "util.h" +#include "template.h" + +using namespace nghttp2; + +namespace h2load { + +Http2Session::Http2Session(Client *client) + : client_(client), session_(nullptr) {} + +Http2Session::~Http2Session() { nghttp2_session_del(session_); } + +namespace { +int on_header_callback(nghttp2_session *session, const nghttp2_frame *frame, + const uint8_t *name, size_t namelen, + const uint8_t *value, size_t valuelen, uint8_t flags, + void *user_data) { + auto client = static_cast(user_data); + if (frame->hd.type != NGHTTP2_HEADERS || + frame->headers.cat != NGHTTP2_HCAT_RESPONSE) { + return 0; + } + client->on_header(frame->hd.stream_id, name, namelen, value, valuelen); + return 0; +} +} // namespace + +namespace { +int on_frame_recv_callback(nghttp2_session *session, const nghttp2_frame *frame, + void *user_data) { + auto client = static_cast(user_data); + if (frame->hd.type != NGHTTP2_HEADERS || + frame->headers.cat != NGHTTP2_HCAT_RESPONSE) { + return 0; + } + client->worker->stats.bytes_head += frame->hd.length; + return 0; +} +} // namespace + +namespace { +int on_data_chunk_recv_callback(nghttp2_session *session, uint8_t flags, + int32_t stream_id, const uint8_t *data, + size_t len, void *user_data) { + auto client = static_cast(user_data); + client->worker->stats.bytes_body += len; + return 0; +} +} // namespace + +namespace { +int on_stream_close_callback(nghttp2_session *session, int32_t stream_id, + uint32_t error_code, void *user_data) { + auto client = static_cast(user_data); + auto req_stat = static_cast( + nghttp2_session_get_stream_user_data(session, stream_id)); + if (!req_stat) { + return 0; + } + client->on_stream_close(stream_id, error_code == NGHTTP2_NO_ERROR, req_stat); + return 0; +} +} // namespace + +namespace { +int before_frame_send_callback(nghttp2_session *session, + const nghttp2_frame *frame, void *user_data) { + if (frame->hd.type != NGHTTP2_HEADERS || + frame->headers.cat != NGHTTP2_HCAT_REQUEST) { + return 0; + } + + auto client = static_cast(user_data); + client->on_request(frame->hd.stream_id); + auto req_stat = static_cast( + nghttp2_session_get_stream_user_data(session, frame->hd.stream_id)); + assert(req_stat); + client->record_request_time(req_stat); + + return 0; +} +} // namespace + +namespace { +ssize_t file_read_callback(nghttp2_session *session, int32_t stream_id, + uint8_t *buf, size_t length, uint32_t *data_flags, + nghttp2_data_source *source, void *user_data) { + auto client = static_cast(user_data); + auto config = client->worker->config; + auto req_stat = static_cast( + nghttp2_session_get_stream_user_data(session, stream_id)); + assert(req_stat); + ssize_t nread; + while ((nread = pread(config->data_fd, buf, length, req_stat->data_offset)) == + -1 && + errno == EINTR) + ; + + if (nread == -1) { + return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; + } + + req_stat->data_offset += nread; + + if (nread == 0 || req_stat->data_offset == config->data_length) { + *data_flags |= NGHTTP2_DATA_FLAG_EOF; + } + + return nread; +} + +} // namespace + +namespace { +ssize_t send_callback(nghttp2_session *session, const uint8_t *data, + size_t length, int flags, void *user_data) { + auto client = static_cast(user_data); + auto &wb = client->wb; + + if (wb.wleft() == 0) { + return NGHTTP2_ERR_WOULDBLOCK; + } + + return wb.write(data, length); +} +} // namespace + +void Http2Session::on_connect() { + int rv; + + nghttp2_session_callbacks *callbacks; + + nghttp2_session_callbacks_new(&callbacks); + + auto callbacks_deleter = defer(nghttp2_session_callbacks_del, callbacks); + + nghttp2_session_callbacks_set_on_frame_recv_callback(callbacks, + on_frame_recv_callback); + + nghttp2_session_callbacks_set_on_data_chunk_recv_callback( + callbacks, on_data_chunk_recv_callback); + + nghttp2_session_callbacks_set_on_stream_close_callback( + callbacks, on_stream_close_callback); + + nghttp2_session_callbacks_set_on_header_callback(callbacks, + on_header_callback); + + nghttp2_session_callbacks_set_before_frame_send_callback( + callbacks, before_frame_send_callback); + + nghttp2_session_callbacks_set_send_callback(callbacks, send_callback); + + nghttp2_session_client_new(&session_, callbacks, client_); + + std::array iv; + iv[0].settings_id = NGHTTP2_SETTINGS_ENABLE_PUSH; + iv[0].value = 0; + iv[1].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE; + iv[1].value = (1 << client_->worker->config->window_bits) - 1; + + rv = nghttp2_submit_settings(session_, NGHTTP2_FLAG_NONE, iv.data(), + iv.size()); + + assert(rv == 0); + + auto extra_connection_window = + (1 << client_->worker->config->connection_window_bits) - 1 - + NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE; + if (extra_connection_window != 0) { + nghttp2_submit_window_update(session_, NGHTTP2_FLAG_NONE, 0, + extra_connection_window); + } + + client_->signal_write(); +} + +void Http2Session::submit_request(RequestStat *req_stat) { + auto config = client_->worker->config; + auto &nva = config->nva[client_->reqidx++]; + + if (client_->reqidx == config->nva.size()) { + client_->reqidx = 0; + } + + nghttp2_data_provider prd{{0}, file_read_callback}; + + auto stream_id = + nghttp2_submit_request(session_, nullptr, nva.data(), nva.size(), + config->data_fd == -1 ? nullptr : &prd, req_stat); + assert(stream_id > 0); +} + +int Http2Session::on_read(const uint8_t *data, size_t len) { + auto rv = nghttp2_session_mem_recv(session_, data, len); + if (rv < 0) { + return -1; + } + + assert(static_cast(rv) == len); + + if (nghttp2_session_want_read(session_) == 0 && + nghttp2_session_want_write(session_) == 0 && client_->wb.rleft() == 0) { + return -1; + } + + client_->signal_write(); + + return 0; +} + +int Http2Session::on_write() { + auto rv = nghttp2_session_send(session_); + if (rv != 0) { + return -1; + } + + if (nghttp2_session_want_read(session_) == 0 && + nghttp2_session_want_write(session_) == 0 && client_->wb.rleft() == 0) { + return -1; + } + + return 0; +} + +void Http2Session::terminate() { + nghttp2_session_terminate_session(session_, NGHTTP2_NO_ERROR); +} + +} // namespace h2load diff --git a/src/h2load_http2_session.h b/src/h2load_http2_session.h new file mode 100644 index 0000000..a06e411 --- /dev/null +++ b/src/h2load_http2_session.h @@ -0,0 +1,53 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2014 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef H2LOAD_HTTP2_SESSION_H +#define H2LOAD_HTTP2_SESSION_H + +#include "h2load_session.h" + +#include + +namespace h2load { + +struct Client; + +class Http2Session : public Session { +public: + Http2Session(Client *client); + virtual ~Http2Session(); + virtual void on_connect(); + virtual void submit_request(RequestStat *req_stat); + virtual int on_read(const uint8_t *data, size_t len); + virtual int on_write(); + virtual void terminate(); + +private: + Client *client_; + nghttp2_session *session_; +}; + +} // namespace h2load + +#endif // H2LOAD_HTTP2_SESSION_H diff --git a/src/h2load_session.h b/src/h2load_session.h new file mode 100644 index 0000000..1817574 --- /dev/null +++ b/src/h2load_session.h @@ -0,0 +1,57 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2014 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef H2LOAD_SESSION_H +#define H2LOAD_SESSION_H + +#include "nghttp2_config.h" + +#include + +#include + +#include "h2load.h" + +namespace h2load { + +class Session { +public: + virtual ~Session() {} + // Called when the connection was made. + virtual void on_connect() = 0; + // Called when one request must be issued. + virtual void submit_request(RequestStat *req_stat) = 0; + // Called when incoming bytes are available. The subclass has to + // return the number of bytes read. + virtual int on_read(const uint8_t *data, size_t len) = 0; + // Called when write is available. Returns 0 on success, otherwise + // return -1. + virtual int on_write() = 0; + // Called when the underlying session must be terminated. + virtual void terminate() = 0; +}; + +} // namespace h2load + +#endif // H2LOAD_SESSION_H diff --git a/src/h2load_spdy_session.cc b/src/h2load_spdy_session.cc new file mode 100644 index 0000000..60258b7 --- /dev/null +++ b/src/h2load_spdy_session.cc @@ -0,0 +1,270 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2014 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "h2load_spdy_session.h" + +#include +#include + +#include "h2load.h" +#include "util.h" + +using namespace nghttp2; + +namespace h2load { + +SpdySession::SpdySession(Client *client, uint16_t spdy_version) + : client_(client), session_(nullptr), spdy_version_(spdy_version) {} + +SpdySession::~SpdySession() { spdylay_session_del(session_); } + +namespace { +void before_ctrl_send_callback(spdylay_session *session, + spdylay_frame_type type, spdylay_frame *frame, + void *user_data) { + auto client = static_cast(user_data); + if (type != SPDYLAY_SYN_STREAM) { + return; + } + client->on_request(frame->syn_stream.stream_id); + auto req_stat = + static_cast(spdylay_session_get_stream_user_data( + session, frame->syn_stream.stream_id)); + client->record_request_time(req_stat); +} +} // namespace + +namespace { +void on_ctrl_recv_callback(spdylay_session *session, spdylay_frame_type type, + spdylay_frame *frame, void *user_data) { + auto client = static_cast(user_data); + if (type != SPDYLAY_SYN_REPLY) { + return; + } + for (auto p = frame->syn_reply.nv; *p; p += 2) { + auto name = *p; + auto value = *(p + 1); + client->on_header(frame->syn_reply.stream_id, + reinterpret_cast(name), strlen(name), + reinterpret_cast(value), strlen(value)); + } + client->worker->stats.bytes_head += frame->syn_reply.hd.length; +} +} // namespace + +namespace { +void on_data_chunk_recv_callback(spdylay_session *session, uint8_t flags, + int32_t stream_id, const uint8_t *data, + size_t len, void *user_data) { + auto client = static_cast(user_data); + client->worker->stats.bytes_body += len; + + auto spdy_session = static_cast(client->session.get()); + + spdy_session->handle_window_update(stream_id, len); +} +} // namespace + +namespace { +void on_stream_close_callback(spdylay_session *session, int32_t stream_id, + spdylay_status_code status_code, + void *user_data) { + auto client = static_cast(user_data); + auto req_stat = static_cast( + spdylay_session_get_stream_user_data(session, stream_id)); + client->on_stream_close(stream_id, status_code == SPDYLAY_OK, req_stat); +} +} // namespace + +namespace { +ssize_t send_callback(spdylay_session *session, const uint8_t *data, + size_t length, int flags, void *user_data) { + auto client = static_cast(user_data); + auto &wb = client->wb; + + if (wb.wleft() == 0) { + return SPDYLAY_ERR_DEFERRED; + } + + return wb.write(data, length); +} +} // namespace + +namespace { +ssize_t file_read_callback(spdylay_session *session, int32_t stream_id, + uint8_t *buf, size_t length, int *eof, + spdylay_data_source *source, void *user_data) { + auto client = static_cast(user_data); + auto config = client->worker->config; + auto req_stat = static_cast( + spdylay_session_get_stream_user_data(session, stream_id)); + + ssize_t nread; + while ((nread = pread(config->data_fd, buf, length, req_stat->data_offset)) == + -1 && + errno == EINTR) + ; + + if (nread == -1) { + return SPDYLAY_ERR_TEMPORAL_CALLBACK_FAILURE; + } + + req_stat->data_offset += nread; + + if (nread == 0 || req_stat->data_offset == config->data_length) { + *eof = 1; + } + + return nread; +} +} // namespace + +void SpdySession::on_connect() { + spdylay_session_callbacks callbacks = {0}; + callbacks.send_callback = send_callback; + callbacks.before_ctrl_send_callback = before_ctrl_send_callback; + callbacks.on_data_chunk_recv_callback = on_data_chunk_recv_callback; + callbacks.on_stream_close_callback = on_stream_close_callback; + callbacks.on_ctrl_recv_callback = on_ctrl_recv_callback; + + spdylay_session_client_new(&session_, spdy_version_, &callbacks, client_); + + int val = 1; + spdylay_session_set_option(session_, SPDYLAY_OPT_NO_AUTO_WINDOW_UPDATE, &val, + sizeof(val)); + + spdylay_settings_entry iv; + iv.settings_id = SPDYLAY_SETTINGS_INITIAL_WINDOW_SIZE; + iv.flags = SPDYLAY_ID_FLAG_SETTINGS_NONE; + iv.value = (1 << client_->worker->config->window_bits); + spdylay_submit_settings(session_, SPDYLAY_FLAG_SETTINGS_NONE, &iv, 1); + + auto config = client_->worker->config; + + if (spdy_version_ >= SPDYLAY_PROTO_SPDY3_1 && + config->connection_window_bits > 16) { + auto delta = + (1 << config->connection_window_bits) - SPDYLAY_INITIAL_WINDOW_SIZE; + spdylay_submit_window_update(session_, 0, delta); + } + + client_->signal_write(); +} + +void SpdySession::submit_request(RequestStat *req_stat) { + auto config = client_->worker->config; + auto &nv = config->nv[client_->reqidx++]; + + if (client_->reqidx == config->nv.size()) { + client_->reqidx = 0; + } + + spdylay_data_provider prd{{0}, file_read_callback}; + + spdylay_submit_request(session_, 0, nv.data(), + config->data_fd == -1 ? nullptr : &prd, req_stat); +} + +int SpdySession::on_read(const uint8_t *data, size_t len) { + auto rv = spdylay_session_mem_recv(session_, data, len); + if (rv < 0) { + return -1; + } + + assert(static_cast(rv) == len); + + if (spdylay_session_want_read(session_) == 0 && + spdylay_session_want_write(session_) == 0 && client_->wb.rleft() == 0) { + return -1; + } + + client_->signal_write(); + + return 0; +} + +int SpdySession::on_write() { + auto rv = spdylay_session_send(session_); + if (rv != 0) { + return -1; + } + + if (spdylay_session_want_read(session_) == 0 && + spdylay_session_want_write(session_) == 0 && client_->wb.rleft() == 0) { + return -1; + } + return 0; +} + +void SpdySession::terminate() { + spdylay_session_fail_session(session_, SPDYLAY_OK); +} + +namespace { +int32_t determine_window_update_transmission(spdylay_session *session, + int32_t stream_id, + size_t window_bits) { + int32_t recv_length; + + if (stream_id == 0) { + recv_length = spdylay_session_get_recv_data_length(session); + } else { + recv_length = + spdylay_session_get_stream_recv_data_length(session, stream_id); + } + + auto window_size = 1 << window_bits; + + if (recv_length != -1 && recv_length >= window_size / 2) { + return recv_length; + } + + return -1; +} +} // namespace + +void SpdySession::handle_window_update(int32_t stream_id, size_t recvlen) { + auto config = client_->worker->config; + size_t connection_window_bits; + + if (config->connection_window_bits > 16) { + connection_window_bits = config->connection_window_bits; + } else { + connection_window_bits = 16; + } + + auto delta = + determine_window_update_transmission(session_, 0, connection_window_bits); + if (delta > 0) { + spdylay_submit_window_update(session_, 0, delta); + } + + delta = determine_window_update_transmission(session_, stream_id, + config->window_bits); + if (delta > 0) { + spdylay_submit_window_update(session_, stream_id, delta); + } +} + +} // namespace h2load diff --git a/src/h2load_spdy_session.h b/src/h2load_spdy_session.h new file mode 100644 index 0000000..f76f25a --- /dev/null +++ b/src/h2load_spdy_session.h @@ -0,0 +1,57 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2014 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef H2LOAD_SPDY_SESSION_H +#define H2LOAD_SPDY_SESSION_H + +#include "h2load_session.h" + +#include + +#include "util.h" + +namespace h2load { + +struct Client; + +class SpdySession : public Session { +public: + SpdySession(Client *client, uint16_t spdy_version); + virtual ~SpdySession(); + virtual void on_connect(); + virtual void submit_request(RequestStat *req_stat); + virtual int on_read(const uint8_t *data, size_t len); + virtual int on_write(); + virtual void terminate(); + void handle_window_update(int32_t stream_id, size_t recvlen); + +private: + Client *client_; + spdylay_session *session_; + uint16_t spdy_version_; +}; + +} // namespace h2load + +#endif // H2LOAD_SPDY_SESSION_H diff --git a/src/http2.cc b/src/http2.cc new file mode 100644 index 0000000..65b2cee --- /dev/null +++ b/src/http2.cc @@ -0,0 +1,1118 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "http2.h" + +#include "util.h" + +namespace nghttp2 { + +namespace http2 { + +std::string get_status_string(unsigned int status_code) { + switch (status_code) { + case 100: + return "100 Continue"; + case 101: + return "101 Switching Protocols"; + case 200: + return "200 OK"; + case 201: + return "201 Created"; + case 202: + return "202 Accepted"; + case 203: + return "203 Non-Authoritative Information"; + case 204: + return "204 No Content"; + case 205: + return "205 Reset Content"; + case 206: + return "206 Partial Content"; + case 300: + return "300 Multiple Choices"; + case 301: + return "301 Moved Permanently"; + case 302: + return "302 Found"; + case 303: + return "303 See Other"; + case 304: + return "304 Not Modified"; + case 305: + return "305 Use Proxy"; + // case 306: return "306 (Unused)"; + case 307: + return "307 Temporary Redirect"; + case 308: + return "308 Permanent Redirect"; + case 400: + return "400 Bad Request"; + case 401: + return "401 Unauthorized"; + case 402: + return "402 Payment Required"; + case 403: + return "403 Forbidden"; + case 404: + return "404 Not Found"; + case 405: + return "405 Method Not Allowed"; + case 406: + return "406 Not Acceptable"; + case 407: + return "407 Proxy Authentication Required"; + case 408: + return "408 Request Timeout"; + case 409: + return "409 Conflict"; + case 410: + return "410 Gone"; + case 411: + return "411 Length Required"; + case 412: + return "412 Precondition Failed"; + case 413: + return "413 Payload Too Large"; + case 414: + return "414 URI Too Long"; + case 415: + return "415 Unsupported Media Type"; + case 416: + return "416 Requested Range Not Satisfiable"; + case 417: + return "417 Expectation Failed"; + case 421: + return "421 Misdirected Request"; + case 426: + return "426 Upgrade Required"; + case 428: + return "428 Precondition Required"; + case 429: + return "429 Too Many Requests"; + case 431: + return "431 Request Header Fields Too Large"; + case 500: + return "500 Internal Server Error"; + case 501: + return "501 Not Implemented"; + case 502: + return "502 Bad Gateway"; + case 503: + return "503 Service Unavailable"; + case 504: + return "504 Gateway Timeout"; + case 505: + return "505 HTTP Version Not Supported"; + case 511: + return "511 Network Authentication Required"; + default: + return util::utos(status_code); + } +} + +void capitalize(std::string &s, size_t offset) { + s[offset] = util::upcase(s[offset]); + for (size_t i = offset + 1, eoi = s.size(); i < eoi; ++i) { + if (s[i - 1] == '-') { + s[i] = util::upcase(s[i]); + } else { + s[i] = util::lowcase(s[i]); + } + } +} + +bool lws(const char *value) { + for (; *value; ++value) { + switch (*value) { + case '\t': + case ' ': + continue; + default: + return false; + } + } + return true; +} + +void copy_url_component(std::string &dest, const http_parser_url *u, int field, + const char *url) { + if (u->field_set & (1 << field)) { + dest.assign(url + u->field_data[field].off, u->field_data[field].len); + } +} + +Headers::value_type to_header(const uint8_t *name, size_t namelen, + const uint8_t *value, size_t valuelen, + bool no_index, int16_t token) { + return Header(std::string(reinterpret_cast(name), namelen), + std::string(reinterpret_cast(value), valuelen), + no_index, token); +} + +void add_header(Headers &nva, const uint8_t *name, size_t namelen, + const uint8_t *value, size_t valuelen, bool no_index, + int16_t token) { + if (valuelen > 0) { + size_t i, j; + for (i = 0; i < valuelen && (value[i] == ' ' || value[i] == '\t'); ++i) + ; + for (j = valuelen - 1; j > i && (value[j] == ' ' || value[j] == '\t'); --j) + ; + value += i; + valuelen -= i + (valuelen - j - 1); + } + nva.push_back(to_header(name, namelen, value, valuelen, no_index, token)); +} + +const Headers::value_type *get_header(const Headers &nva, const char *name) { + const Headers::value_type *res = nullptr; + for (auto &nv : nva) { + if (nv.name == name) { + res = &nv; + } + } + return res; +} + +std::string value_to_str(const Headers::value_type *nv) { + if (nv) { + return nv->value; + } + return ""; +} + +bool non_empty_value(const Headers::value_type *nv) { + return nv && !nv->value.empty(); +} + +nghttp2_nv make_nv(const std::string &name, const std::string &value, + bool no_index) { + uint8_t flags; + + flags = no_index ? NGHTTP2_NV_FLAG_NO_INDEX : NGHTTP2_NV_FLAG_NONE; + + return {(uint8_t *)name.c_str(), (uint8_t *)value.c_str(), name.size(), + value.size(), flags}; +} + +void copy_headers_to_nva(std::vector &nva, const Headers &headers) { + for (auto &kv : headers) { + if (kv.name.empty() || kv.name[0] == ':') { + continue; + } + switch (kv.token) { + case HD_COOKIE: + case HD_CONNECTION: + case HD_HOST: + case HD_HTTP2_SETTINGS: + case HD_KEEP_ALIVE: + case HD_PROXY_CONNECTION: + case HD_SERVER: + case HD_TE: + case HD_TRANSFER_ENCODING: + case HD_UPGRADE: + case HD_VIA: + case HD_X_FORWARDED_FOR: + case HD_X_FORWARDED_PROTO: + continue; + } + nva.push_back(make_nv(kv.name, kv.value, kv.no_index)); + } +} + +void build_http1_headers_from_headers(std::string &hdrs, + const Headers &headers) { + for (auto &kv : headers) { + if (kv.name.empty() || kv.name[0] == ':') { + continue; + } + switch (kv.token) { + case HD_CONNECTION: + case HD_COOKIE: + case HD_HOST: + case HD_HTTP2_SETTINGS: + case HD_KEEP_ALIVE: + case HD_PROXY_CONNECTION: + case HD_SERVER: + case HD_UPGRADE: + case HD_VIA: + case HD_X_FORWARDED_FOR: + case HD_X_FORWARDED_PROTO: + continue; + } + hdrs += kv.name; + capitalize(hdrs, hdrs.size() - kv.name.size()); + hdrs += ": "; + hdrs += kv.value; + hdrs += "\r\n"; + } +} + +int32_t determine_window_update_transmission(nghttp2_session *session, + int32_t stream_id) { + int32_t recv_length, window_size; + if (stream_id == 0) { + recv_length = nghttp2_session_get_effective_recv_data_length(session); + window_size = nghttp2_session_get_effective_local_window_size(session); + } else { + recv_length = nghttp2_session_get_stream_effective_recv_data_length( + session, stream_id); + window_size = nghttp2_session_get_stream_effective_local_window_size( + session, stream_id); + } + if (recv_length != -1 && window_size != -1) { + if (recv_length >= window_size / 2) { + return recv_length; + } + } + return -1; +} + +void dump_nv(FILE *out, const char **nv) { + for (size_t i = 0; nv[i]; i += 2) { + fprintf(out, "%s: %s\n", nv[i], nv[i + 1]); + } + fputc('\n', out); + fflush(out); +} + +void dump_nv(FILE *out, const nghttp2_nv *nva, size_t nvlen) { + auto end = nva + nvlen; + for (; nva != end; ++nva) { + fprintf(out, "%s: %s\n", nva->name, nva->value); + } + fputc('\n', out); + fflush(out); +} + +void dump_nv(FILE *out, const Headers &nva) { + for (auto &nv : nva) { + fprintf(out, "%s: %s\n", nv.name.c_str(), nv.value.c_str()); + } + fputc('\n', out); + fflush(out); +} + +std::string rewrite_location_uri(const std::string &uri, + const http_parser_url &u, + const std::string &match_host, + const std::string &request_authority, + const std::string &upstream_scheme) { + // We just rewrite scheme and authority. + if ((u.field_set & (1 << UF_HOST)) == 0) { + return ""; + } + auto field = &u.field_data[UF_HOST]; + if (!util::startsWith(std::begin(match_host), std::end(match_host), + &uri[field->off], &uri[field->off] + field->len) || + (match_host.size() != field->len && match_host[field->len] != ':')) { + return ""; + } + std::string res; + if (!request_authority.empty()) { + res += upstream_scheme; + res += "://"; + res += request_authority; + } + if (u.field_set & (1 << UF_PATH)) { + field = &u.field_data[UF_PATH]; + res.append(&uri[field->off], field->len); + } + if (u.field_set & (1 << UF_QUERY)) { + field = &u.field_data[UF_QUERY]; + res += "?"; + res.append(&uri[field->off], field->len); + } + if (u.field_set & (1 << UF_FRAGMENT)) { + field = &u.field_data[UF_FRAGMENT]; + res += "#"; + res.append(&uri[field->off], field->len); + } + return res; +} + +int check_nv(const uint8_t *name, size_t namelen, const uint8_t *value, + size_t valuelen) { + if (!nghttp2_check_header_name(name, namelen)) { + return 0; + } + if (!nghttp2_check_header_value(value, valuelen)) { + return 0; + } + return 1; +} + +int parse_http_status_code(const std::string &src) { + if (src.size() != 3) { + return -1; + } + + int status = 0; + for (auto c : src) { + if (!isdigit(c)) { + return -1; + } + status *= 10; + status += c - '0'; + } + + if (status < 100) { + return -1; + } + + return status; +} + +int lookup_token(const std::string &name) { + return lookup_token(reinterpret_cast(name.c_str()), + name.size()); +} + +// This function was generated by genheaderfunc.py. Inspired by h2o +// header lookup. https://github.com/h2o/h2o +int lookup_token(const uint8_t *name, size_t namelen) { + switch (namelen) { + case 2: + switch (name[1]) { + case 'e': + if (util::streq_l("t", name, 1)) { + return HD_TE; + } + break; + } + break; + case 3: + switch (name[2]) { + case 'a': + if (util::streq_l("vi", name, 2)) { + return HD_VIA; + } + break; + } + break; + case 4: + switch (name[3]) { + case 'k': + if (util::streq_l("lin", name, 3)) { + return HD_LINK; + } + break; + case 't': + if (util::streq_l("hos", name, 3)) { + return HD_HOST; + } + break; + } + break; + case 5: + switch (name[4]) { + case 'h': + if (util::streq_l(":pat", name, 4)) { + return HD__PATH; + } + break; + case 't': + if (util::streq_l(":hos", name, 4)) { + return HD__HOST; + } + break; + } + break; + case 6: + switch (name[5]) { + case 'e': + if (util::streq_l("cooki", name, 5)) { + return HD_COOKIE; + } + break; + case 'r': + if (util::streq_l("serve", name, 5)) { + return HD_SERVER; + } + break; + case 't': + if (util::streq_l("expec", name, 5)) { + return HD_EXPECT; + } + break; + } + break; + case 7: + switch (name[6]) { + case 'c': + if (util::streq_l("alt-sv", name, 6)) { + return HD_ALT_SVC; + } + break; + case 'd': + if (util::streq_l(":metho", name, 6)) { + return HD__METHOD; + } + break; + case 'e': + if (util::streq_l(":schem", name, 6)) { + return HD__SCHEME; + } + if (util::streq_l("upgrad", name, 6)) { + return HD_UPGRADE; + } + break; + case 'r': + if (util::streq_l("traile", name, 6)) { + return HD_TRAILER; + } + break; + case 's': + if (util::streq_l(":statu", name, 6)) { + return HD__STATUS; + } + break; + } + break; + case 8: + switch (name[7]) { + case 'n': + if (util::streq_l("locatio", name, 7)) { + return HD_LOCATION; + } + break; + } + break; + case 10: + switch (name[9]) { + case 'e': + if (util::streq_l("keep-aliv", name, 9)) { + return HD_KEEP_ALIVE; + } + break; + case 'n': + if (util::streq_l("connectio", name, 9)) { + return HD_CONNECTION; + } + break; + case 't': + if (util::streq_l("user-agen", name, 9)) { + return HD_USER_AGENT; + } + break; + case 'y': + if (util::streq_l(":authorit", name, 9)) { + return HD__AUTHORITY; + } + break; + } + break; + case 13: + switch (name[12]) { + case 'l': + if (util::streq_l("cache-contro", name, 12)) { + return HD_CACHE_CONTROL; + } + break; + } + break; + case 14: + switch (name[13]) { + case 'h': + if (util::streq_l("content-lengt", name, 13)) { + return HD_CONTENT_LENGTH; + } + break; + case 's': + if (util::streq_l("http2-setting", name, 13)) { + return HD_HTTP2_SETTINGS; + } + break; + } + break; + case 15: + switch (name[14]) { + case 'e': + if (util::streq_l("accept-languag", name, 14)) { + return HD_ACCEPT_LANGUAGE; + } + break; + case 'g': + if (util::streq_l("accept-encodin", name, 14)) { + return HD_ACCEPT_ENCODING; + } + break; + case 'r': + if (util::streq_l("x-forwarded-fo", name, 14)) { + return HD_X_FORWARDED_FOR; + } + break; + } + break; + case 16: + switch (name[15]) { + case 'n': + if (util::streq_l("proxy-connectio", name, 15)) { + return HD_PROXY_CONNECTION; + } + break; + } + break; + case 17: + switch (name[16]) { + case 'e': + if (util::streq_l("if-modified-sinc", name, 16)) { + return HD_IF_MODIFIED_SINCE; + } + break; + case 'g': + if (util::streq_l("transfer-encodin", name, 16)) { + return HD_TRANSFER_ENCODING; + } + break; + case 'o': + if (util::streq_l("x-forwarded-prot", name, 16)) { + return HD_X_FORWARDED_PROTO; + } + break; + } + break; + } + return -1; +} + +void init_hdidx(HeaderIndex &hdidx) { + std::fill(std::begin(hdidx), std::end(hdidx), -1); +} + +void index_header(HeaderIndex &hdidx, int16_t token, size_t idx) { + if (token == -1) { + return; + } + assert(token < HD_MAXIDX); + hdidx[token] = idx; +} + +bool check_http2_request_pseudo_header(const HeaderIndex &hdidx, + int16_t token) { + switch (token) { + case HD__AUTHORITY: + case HD__METHOD: + case HD__PATH: + case HD__SCHEME: + return hdidx[token] == -1; + default: + return false; + } +} + +bool check_http2_response_pseudo_header(const HeaderIndex &hdidx, + int16_t token) { + switch (token) { + case HD__STATUS: + return hdidx[token] == -1; + default: + return false; + } +} + +bool http2_header_allowed(int16_t token) { + switch (token) { + case HD_CONNECTION: + case HD_KEEP_ALIVE: + case HD_PROXY_CONNECTION: + case HD_TRANSFER_ENCODING: + case HD_UPGRADE: + return false; + default: + return true; + } +} + +bool http2_mandatory_request_headers_presence(const HeaderIndex &hdidx) { + if (hdidx[HD__METHOD] == -1 || hdidx[HD__PATH] == -1 || + hdidx[HD__SCHEME] == -1 || + (hdidx[HD__AUTHORITY] == -1 && hdidx[HD_HOST] == -1)) { + return false; + } + return true; +} + +const Headers::value_type *get_header(const HeaderIndex &hdidx, int16_t token, + const Headers &nva) { + auto i = hdidx[token]; + if (i == -1) { + return nullptr; + } + return &nva[i]; +} + +namespace { +template InputIt skip_lws(InputIt first, InputIt last) { + for (; first != last; ++first) { + switch (*first) { + case ' ': + case '\t': + continue; + default: + return first; + } + } + return first; +} +} // namespace + +namespace { +template +InputIt skip_to_next_field(InputIt first, InputIt last) { + for (; first != last; ++first) { + switch (*first) { + case ' ': + case '\t': + case ',': + continue; + default: + return first; + } + } + return first; +} +} // namespace + +namespace { +// Skip to the right dquote ('"'), handling backslash escapes. +// Returns |last| if input is not terminated with '"'. +template +InputIt skip_to_right_dquote(InputIt first, InputIt last) { + for (; first != last;) { + switch (*first) { + case '"': + return first; + case '\\': + ++first; + if (first == last) { + return first; + } + break; + } + ++first; + } + return first; +} +} // namespace + +namespace { +// Returns true if link-param does not match pattern |pat| of length +// |patlen| or it has empty value (""). |pat| should be parmname +// followed by "=". +bool check_link_param_empty(const char *first, const char *last, + const char *pat, size_t patlen) { + if (first + patlen <= last) { + if (std::equal(pat, pat + patlen, first, util::CaseCmp())) { + // we only accept URI if pat is followd by "" (e.g., + // loadpolicy="") here. + if (first + patlen + 2 <= last) { + if (*(first + patlen) != '"' || *(first + patlen + 1) != '"') { + return false; + } + } else { + // here we got invalid production (anchor=") or anchor=? + return false; + } + } + } + return true; +} +} // namespace + +namespace { +std::pair +parse_next_link_header_once(const char *first, const char *last) { + first = skip_to_next_field(first, last); + if (first == last || *first != '<') { + return {{{nullptr, nullptr}}, last}; + } + auto url_first = ++first; + first = std::find(first, last, '>'); + if (first == last) { + return {{{nullptr, nullptr}}, first}; + } + auto url_last = first++; + if (first == last) { + return {{{nullptr, nullptr}}, first}; + } + // we expect ';' or ',' here + switch (*first) { + case ',': + return {{{nullptr, nullptr}}, ++first}; + case ';': + ++first; + break; + default: + return {{{nullptr, nullptr}}, last}; + } + + auto ok = false; + auto ign = false; + for (;;) { + first = skip_lws(first, last); + if (first == last) { + return {{{nullptr, nullptr}}, first}; + } + // we expect link-param + + // rel can take several relations using quoted form. + static constexpr char PLP[] = "rel=\""; + static constexpr size_t PLPLEN = sizeof(PLP) - 1; + + static constexpr char PLT[] = "preload"; + static constexpr size_t PLTLEN = sizeof(PLT) - 1; + if (first + PLPLEN < last && *(first + PLPLEN - 1) == '"' && + std::equal(PLP, PLP + PLPLEN, first, util::CaseCmp())) { + // we have to search preload in whitespace separated list: + // rel="preload something http://example.org/foo" + first += PLPLEN; + auto start = first; + for (; first != last;) { + if (*first != ' ' && *first != '"') { + ++first; + continue; + } + + if (start == first) { + return {{{nullptr, nullptr}}, last}; + } + + if (!ok && start + PLTLEN == first && + std::equal(PLT, PLT + PLTLEN, start, util::CaseCmp())) { + ok = true; + } + + if (*first == '"') { + break; + } + first = skip_lws(first, last); + start = first; + } + if (first == last) { + return {{{nullptr, nullptr}}, first}; + } + assert(*first == '"'); + ++first; + if (first == last || *first == ',') { + goto almost_done; + } + if (*first == ';') { + ++first; + // parse next link-param + continue; + } + return {{{nullptr, nullptr}}, last}; + } + // we are only interested in rel=preload parameter. Others are + // simply skipped. + static constexpr char PL[] = "rel=preload"; + static constexpr size_t PLLEN = sizeof(PL) - 1; + if (first + PLLEN == last) { + if (std::equal(PL, PL + PLLEN, first, util::CaseCmp())) { + ok = true; + // this is the end of sequence + return {{{url_first, url_last}}, last}; + } + } else if (first + PLLEN + 1 <= last) { + switch (*(first + PLLEN)) { + case ',': + if (!std::equal(PL, PL + PLLEN, first, util::CaseCmp())) { + break; + } + ok = true; + // skip including ',' + first += PLLEN + 1; + return {{{url_first, url_last}}, first}; + case ';': + if (!std::equal(PL, PL + PLLEN, first, util::CaseCmp())) { + break; + } + ok = true; + // skip including ';' + first += PLLEN + 1; + // continue parse next link-param + continue; + } + } + // we have to reject URI if we have nonempty anchor parameter. + static constexpr char ANCHOR[] = "anchor="; + static constexpr size_t ANCHORLEN = sizeof(ANCHOR) - 1; + if (!ign && !check_link_param_empty(first, last, ANCHOR, ANCHORLEN)) { + ign = true; + } + + // reject URI if we have non-empty loadpolicy. This could be + // tightened up to just pick up "next" or "insert". + static constexpr char LOADPOLICY[] = "loadpolicy="; + static constexpr size_t LOADPOLICYLEN = sizeof(LOADPOLICY) - 1; + if (!ign && + !check_link_param_empty(first, last, LOADPOLICY, LOADPOLICYLEN)) { + ign = true; + } + + auto param_first = first; + for (; first != last;) { + if (util::in_attr_char(*first)) { + ++first; + continue; + } + // '*' is only allowed at the end of parameter name and must be + // followed by '=' + if (last - first >= 2 && first != param_first) { + if (*first == '*' && *(first + 1) == '=') { + ++first; + break; + } + } + if (*first == '=' || *first == ';' || *first == ',') { + break; + } + return {{{nullptr, nullptr}}, last}; + } + if (param_first == first) { + // empty parmname + return {{{nullptr, nullptr}}, last}; + } + // link-param without value is acceptable (see link-extension) if + // it is not followed by '=' + if (first == last || *first == ',') { + goto almost_done; + } + if (*first == ';') { + ++first; + // parse next link-param + continue; + } + // now parsing link-param value + assert(*first == '='); + ++first; + if (first == last) { + // empty value is not acceptable + return {{{nullptr, nullptr}}, first}; + } + if (*first == '"') { + // quoted-string + first = skip_to_right_dquote(first + 1, last); + if (first == last) { + return {{{nullptr, nullptr}}, first}; + } + ++first; + if (first == last || *first == ',') { + goto almost_done; + } + if (*first == ';') { + ++first; + // parse next link-param + continue; + } + return {{{nullptr, nullptr}}, last}; + } + // not quoted-string, skip to next ',' or ';' + if (*first == ',' || *first == ';') { + // empty value + return {{{nullptr, nullptr}}, last}; + } + for (; first != last; ++first) { + if (*first == ',' || *first == ';') { + break; + } + } + if (first == last || *first == ',') { + goto almost_done; + } + assert(*first == ';'); + ++first; + // parse next link-param + } + +almost_done: + assert(first == last || *first == ','); + + if (first != last) { + ++first; + } + if (ok && !ign) { + return {{{url_first, url_last}}, first}; + } + return {{{nullptr, nullptr}}, first}; +} +} // namespace + +std::vector parse_link_header(const char *src, size_t len) { + auto first = src; + auto last = src + len; + std::vector res; + for (; first != last;) { + auto rv = parse_next_link_header_once(first, last); + first = rv.second; + if (rv.first.uri.first != nullptr && rv.first.uri.second != nullptr) { + res.push_back(rv.first); + } + } + return res; +} + +namespace { +void eat_file(std::string &path) { + if (path.empty()) { + path = "/"; + return; + } + auto p = path.size() - 1; + if (path[p] == '/') { + return; + } + p = path.rfind('/', p); + if (p == std::string::npos) { + // this should not happend in normal case, where we expect path + // starts with '/' + path = "/"; + return; + } + path.erase(std::begin(path) + p + 1, std::end(path)); +} +} // namespace + +namespace { +void eat_dir(std::string &path) { + if (path.empty()) { + path = "/"; + return; + } + auto p = path.size() - 1; + if (path[p] != '/') { + p = path.rfind('/', p); + if (p == std::string::npos) { + // this should not happend in normal case, where we expect path + // starts with '/' + path = "/"; + return; + } + } + if (path[p] == '/') { + if (p == 0) { + return; + } + --p; + } + p = path.rfind('/', p); + if (p == std::string::npos) { + // this should not happend in normal case, where we expect path + // starts with '/' + path = "/"; + return; + } + path.erase(std::begin(path) + p + 1, std::end(path)); +} +} // namespace + +std::string path_join(const char *base_path, size_t base_pathlen, + const char *base_query, size_t base_querylen, + const char *rel_path, size_t rel_pathlen, + const char *rel_query, size_t rel_querylen) { + std::string res; + if (rel_pathlen == 0) { + if (base_pathlen == 0) { + res = "/"; + } else { + res.assign(base_path, base_pathlen); + } + if (rel_querylen == 0) { + if (base_querylen) { + res += "?"; + res.append(base_query, base_querylen); + } + return res; + } + res += "?"; + res.append(rel_query, rel_querylen); + return res; + } + + auto first = rel_path; + auto last = rel_path + rel_pathlen; + + if (rel_path[0] == '/') { + res = "/"; + ++first; + } else if (base_pathlen == 0) { + res = "/"; + } else { + res.assign(base_path, base_pathlen); + } + + for (; first != last;) { + if (*first == '.') { + if (first + 1 == last) { + break; + } + if (*(first + 1) == '/') { + first += 2; + continue; + } + if (*(first + 1) == '.') { + if (first + 2 == last) { + eat_dir(res); + break; + } + if (*(first + 2) == '/') { + eat_dir(res); + first += 3; + continue; + } + } + } + if (res.back() != '/') { + eat_file(res); + } + auto slash = std::find(first, last, '/'); + if (slash == last) { + res.append(first, last); + break; + } + res.append(first, slash + 1); + first = slash + 1; + for (; first != last && *first == '/'; ++first) + ; + } + if (rel_querylen) { + res += "?"; + res.append(rel_query, rel_querylen); + } + return res; +} + +bool expect_response_body(int status_code) { + return status_code / 100 != 1 && status_code != 304 && status_code != 204; +} + +bool expect_response_body(const std::string &method, int status_code) { + return method != "HEAD" && expect_response_body(status_code); +} + +} // namespace http2 + +} // namespace nghttp2 diff --git a/src/http2.h b/src/http2.h new file mode 100644 index 0000000..af8fd10 --- /dev/null +++ b/src/http2.h @@ -0,0 +1,296 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2013 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef HTTP2_H +#define HTTP2_H + +#include "nghttp2_config.h" + +#include +#include +#include +#include +#include + +#include + +#include "http-parser/http_parser.h" + +namespace nghttp2 { + +struct Header { + Header(std::string name, std::string value, bool no_index = false, + int16_t token = -1) + : name(std::move(name)), value(std::move(value)), token(token), + no_index(no_index) {} + + Header() : token(-1), no_index(false) {} + + bool operator==(const Header &other) const { + return name == other.name && value == other.value; + } + + bool operator<(const Header &rhs) const { + return name < rhs.name || (name == rhs.name && value < rhs.value); + } + + std::string name; + std::string value; + int16_t token; + bool no_index; +}; + +typedef std::vector
        Headers; + +namespace http2 { + +std::string get_status_string(unsigned int status_code); + +void capitalize(std::string &s, size_t offset); + +// Returns true if |value| is LWS +bool lws(const char *value); + +// Copies the |field| component value from |u| and |url| to the +// |dest|. If |u| does not have |field|, then this function does +// nothing. +void copy_url_component(std::string &dest, const http_parser_url *u, int field, + const char *url); + +Headers::value_type to_header(const uint8_t *name, size_t namelen, + const uint8_t *value, size_t valuelen, + bool no_index, int16_t token); + +// Add name/value pairs to |nva|. If |no_index| is true, this +// name/value pair won't be indexed when it is forwarded to the next +// hop. This function strips white spaces around |value|. +void add_header(Headers &nva, const uint8_t *name, size_t namelen, + const uint8_t *value, size_t valuelen, bool no_index, + int16_t token); + +// Returns pointer to the entry in |nva| which has name |name|. If +// more than one entries which have the name |name|, last occurrence +// in |nva| is returned. If no such entry exist, returns nullptr. +const Headers::value_type *get_header(const Headers &nva, const char *name); + +// Returns nv->second if nv is not nullptr. Otherwise, returns "". +std::string value_to_str(const Headers::value_type *nv); + +// Returns true if the value of |nv| is not empty. +bool non_empty_value(const Headers::value_type *nv); + +// Creates nghttp2_nv using |name| and |value| and returns it. The +// returned value only references the data pointer to name.c_str() and +// value.c_str(). If |no_index| is true, nghttp2_nv flags member has +// NGHTTP2_NV_FLAG_NO_INDEX flag set. +nghttp2_nv make_nv(const std::string &name, const std::string &value, + bool no_index = false); + +// Create nghttp2_nv from string literal |name| and |value|. +template +nghttp2_nv make_nv_ll(const char (&name)[N], const char (&value)[M]) { + return {(uint8_t *)name, (uint8_t *)value, N - 1, M - 1, + NGHTTP2_NV_FLAG_NONE}; +} + +// Create nghttp2_nv from string literal |name| and c-string |value|. +template +nghttp2_nv make_nv_lc(const char (&name)[N], const char *value) { + return {(uint8_t *)name, (uint8_t *)value, N - 1, strlen(value), + NGHTTP2_NV_FLAG_NONE}; +} + +// Create nghttp2_nv from string literal |name| and std::string +// |value|. +template +nghttp2_nv make_nv_ls(const char (&name)[N], const std::string &value) { + return {(uint8_t *)name, (uint8_t *)value.c_str(), N - 1, value.size(), + NGHTTP2_NV_FLAG_NONE}; +} + +// Appends headers in |headers| to |nv|. |headers| must be indexed +// before this call (its element's token field is assigned). Certain +// headers, including disallowed headers in HTTP/2 spec and headers +// which require special handling (i.e. via), are not copied. +void copy_headers_to_nva(std::vector &nva, const Headers &headers); + +// Appends HTTP/1.1 style header lines to |hdrs| from headers in +// |headers|. |headers| must be indexed before this call (its +// element's token field is assigned). Certain headers, which +// requires special handling (i.e. via and cookie), are not appended. +void build_http1_headers_from_headers(std::string &hdrs, + const Headers &headers); + +// Return positive window_size_increment if WINDOW_UPDATE should be +// sent for the stream |stream_id|. If |stream_id| == 0, this function +// determines the necessity of the WINDOW_UPDATE for a connection. +// +// If the function determines WINDOW_UPDATE is not necessary at the +// moment, it returns -1. +int32_t determine_window_update_transmission(nghttp2_session *session, + int32_t stream_id); + +// Dumps name/value pairs in |nv| to |out|. The |nv| must be +// terminated by nullptr. +void dump_nv(FILE *out, const char **nv); + +// Dumps name/value pairs in |nva| to |out|. +void dump_nv(FILE *out, const nghttp2_nv *nva, size_t nvlen); + +// Dumps name/value pairs in |nva| to |out|. +void dump_nv(FILE *out, const Headers &nva); + +// Rewrites redirection URI which usually appears in location header +// field. The |uri| is the URI in the location header field. The |u| +// stores the result of parsed |uri|. The |request_authority| is the +// host or :authority header field value in the request. The +// |upstream_scheme| is either "https" or "http" in the upstream +// interface. Rewrite is done only if location header field value +// contains |match_host| as host excluding port. The |match_host| and +// |request_authority| could be different. If |request_authority| is +// empty, strip authority. +// +// This function returns the new rewritten URI on success. If the +// location URI is not subject to the rewrite, this function returns +// emtpy string. +std::string rewrite_location_uri(const std::string &uri, + const http_parser_url &u, + const std::string &match_host, + const std::string &request_authority, + const std::string &upstream_scheme); + +// Checks the header name/value pair using nghttp2_check_header_name() +// and nghttp2_check_header_value(). If both function returns nonzero, +// this function returns nonzero. +int check_nv(const uint8_t *name, size_t namelen, const uint8_t *value, + size_t valuelen); + +// Returns parsed HTTP status code. Returns -1 on failure. +int parse_http_status_code(const std::string &src); + +// Header fields to be indexed, except HD_MAXIDX which is convenient +// member to get maximum value. +enum { + HD__AUTHORITY, + HD__HOST, + HD__METHOD, + HD__PATH, + HD__SCHEME, + HD__STATUS, + HD_ACCEPT_ENCODING, + HD_ACCEPT_LANGUAGE, + HD_ALT_SVC, + HD_CACHE_CONTROL, + HD_CONNECTION, + HD_CONTENT_LENGTH, + HD_COOKIE, + HD_EXPECT, + HD_HOST, + HD_HTTP2_SETTINGS, + HD_IF_MODIFIED_SINCE, + HD_KEEP_ALIVE, + HD_LINK, + HD_LOCATION, + HD_PROXY_CONNECTION, + HD_SERVER, + HD_TE, + HD_TRAILER, + HD_TRANSFER_ENCODING, + HD_UPGRADE, + HD_USER_AGENT, + HD_VIA, + HD_X_FORWARDED_FOR, + HD_X_FORWARDED_PROTO, + HD_MAXIDX, +}; + +using HeaderIndex = std::array; + +// Looks up header token for header name |name| of length |namelen|. +// Only headers we are interested in are tokenized. If header name +// cannot be tokenized, returns -1. +int lookup_token(const uint8_t *name, size_t namelen); +int lookup_token(const std::string &name); + +// Initializes |hdidx|, header index. The |hdidx| must point to the +// array containing at least HD_MAXIDX elements. +void init_hdidx(HeaderIndex &hdidx); +// Indexes header |token| using index |idx|. +void index_header(HeaderIndex &hdidx, int16_t token, size_t idx); + +// Returns true if HTTP/2 request pseudo header |token| is not indexed +// yet and not -1. +bool check_http2_request_pseudo_header(const HeaderIndex &hdidx, int16_t token); + +// Returns true if HTTP/2 response pseudo header |token| is not +// indexed yet and not -1. +bool check_http2_response_pseudo_header(const HeaderIndex &hdidx, + int16_t token); + +// Returns true if header field denoted by |token| is allowed for +// HTTP/2. +bool http2_header_allowed(int16_t token); + +// Returns true that |hdidx| contains mandatory HTTP/2 request +// headers. +bool http2_mandatory_request_headers_presence(const HeaderIndex &hdidx); + +// Returns header denoted by |token| using index |hdidx|. +const Headers::value_type *get_header(const HeaderIndex &hdidx, int16_t token, + const Headers &nva); + +struct LinkHeader { + // The region of URI is [uri.first, uri.second). + std::pair uri; +}; + +// Returns next URI-reference in Link header field value |src| of +// length |len|. If no URI-reference found after searching all input, +// returned uri field is empty. This imply that empty URI-reference +// is ignored during parsing. +std::vector parse_link_header(const char *src, size_t len); + +// Constructs path by combining base path |base_path| of length +// |base_pathlen| with another path |rel_path| of length +// |rel_pathlen|. The base path and another path can have optional +// query component. This function assumes |base_path| is normalized. +// In other words, it does not contain ".." or "." path components +// and starts with "/" if it is not empty. +std::string path_join(const char *base_path, size_t base_pathlen, + const char *base_query, size_t base_querylen, + const char *rel_path, size_t rel_pathlen, + const char *rel_query, size_t rel_querylen); + +// true if response has body, taking into account the request method +// and status code. +bool expect_response_body(const std::string &method, int status_code); + +// true if response has body, taking into account status code only. +bool expect_response_body(int status_code); + +} // namespace http2 + +} // namespace nghttp2 + +#endif // HTTP2_H diff --git a/src/http2_test.cc b/src/http2_test.cc new file mode 100644 index 0000000..e2975d1 --- /dev/null +++ b/src/http2_test.cc @@ -0,0 +1,826 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2013 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "http2_test.h" + +#include +#include +#include + +#include + +#include "http-parser/http_parser.h" + +#include "http2.h" +#include "util.h" + +using namespace nghttp2; + +#define MAKE_NV(K, V) \ + { \ + (uint8_t *) K, (uint8_t *)V, sizeof(K) - 1, sizeof(V) - 1, \ + NGHTTP2_NV_FLAG_NONE \ + } + +namespace shrpx { + +namespace { +void check_nv(const Header &a, const nghttp2_nv *b) { + CU_ASSERT(a.name.size() == b->namelen); + CU_ASSERT(a.value.size() == b->valuelen); + CU_ASSERT(memcmp(a.name.c_str(), b->name, b->namelen) == 0); + CU_ASSERT(memcmp(a.value.c_str(), b->value, b->valuelen) == 0); +} +} // namespace + +void test_http2_add_header(void) { + auto nva = Headers(); + + http2::add_header(nva, (const uint8_t *)"alpha", 5, (const uint8_t *)"123", 3, + false, -1); + CU_ASSERT(Headers::value_type("alpha", "123") == nva[0]); + CU_ASSERT(!nva[0].no_index); + + nva.clear(); + + http2::add_header(nva, (const uint8_t *)"alpha", 5, (const uint8_t *)"", 0, + true, -1); + CU_ASSERT(Headers::value_type("alpha", "") == nva[0]); + CU_ASSERT(nva[0].no_index); + + nva.clear(); + + http2::add_header(nva, (const uint8_t *)"a", 1, (const uint8_t *)" b", 2, + false, -1); + CU_ASSERT(Headers::value_type("a", "b") == nva[0]); + + nva.clear(); + + http2::add_header(nva, (const uint8_t *)"a", 1, (const uint8_t *)"b ", 2, + false, -1); + CU_ASSERT(Headers::value_type("a", "b") == nva[0]); + + nva.clear(); + + http2::add_header(nva, (const uint8_t *)"a", 1, (const uint8_t *)" b ", 5, + false, -1); + CU_ASSERT(Headers::value_type("a", "b") == nva[0]); + + nva.clear(); + + http2::add_header(nva, (const uint8_t *)"a", 1, (const uint8_t *)" bravo ", + 9, false, -1); + CU_ASSERT(Headers::value_type("a", "bravo") == nva[0]); + + nva.clear(); + + http2::add_header(nva, (const uint8_t *)"a", 1, (const uint8_t *)" ", 4, + false, -1); + CU_ASSERT(Headers::value_type("a", "") == nva[0]); + + nva.clear(); + + http2::add_header(nva, (const uint8_t *)"te", 2, (const uint8_t *)"trailers", + 8, false, http2::HD_TE); + CU_ASSERT(http2::HD_TE == nva[0].token); +} + +void test_http2_get_header(void) { + auto nva = Headers{{"alpha", "1"}, + {"bravo", "2"}, + {"bravo", "3"}, + {"charlie", "4"}, + {"delta", "5"}, + {"echo", "6"}, + {"content-length", "7"}}; + const Headers::value_type *rv; + rv = http2::get_header(nva, "delta"); + CU_ASSERT(rv != nullptr); + CU_ASSERT("delta" == rv->name); + + rv = http2::get_header(nva, "bravo"); + CU_ASSERT(rv != nullptr); + CU_ASSERT("bravo" == rv->name); + + rv = http2::get_header(nva, "foxtrot"); + CU_ASSERT(rv == nullptr); + + http2::HeaderIndex hdidx; + http2::init_hdidx(hdidx); + hdidx[http2::HD_CONTENT_LENGTH] = 6; + rv = http2::get_header(hdidx, http2::HD_CONTENT_LENGTH, nva); + CU_ASSERT("content-length" == rv->name); +} + +namespace { +auto headers = + Headers{{"alpha", "0", true}, + {"bravo", "1"}, + {"connection", "2", false, http2::HD_CONNECTION}, + {"connection", "3", false, http2::HD_CONNECTION}, + {"delta", "4"}, + {"expect", "5"}, + {"foxtrot", "6"}, + {"tango", "7"}, + {"te", "8", false, http2::HD_TE}, + {"te", "9", false, http2::HD_TE}, + {"x-forwarded-proto", "10", false, http2::HD_X_FORWARDED_FOR}, + {"x-forwarded-proto", "11", false, http2::HD_X_FORWARDED_FOR}, + {"zulu", "12"}}; +} // namespace + +void test_http2_copy_headers_to_nva(void) { + std::vector nva; + http2::copy_headers_to_nva(nva, headers); + CU_ASSERT(7 == nva.size()); + auto ans = std::vector{0, 1, 4, 5, 6, 7, 12}; + for (size_t i = 0; i < ans.size(); ++i) { + check_nv(headers[ans[i]], &nva[i]); + + if (ans[i] == 0) { + CU_ASSERT(nva[i].flags & NGHTTP2_NV_FLAG_NO_INDEX); + } else { + CU_ASSERT(NGHTTP2_NV_FLAG_NONE == nva[i].flags); + } + } +} + +void test_http2_build_http1_headers_from_headers(void) { + std::string hdrs; + http2::build_http1_headers_from_headers(hdrs, headers); + CU_ASSERT(hdrs == "Alpha: 0\r\n" + "Bravo: 1\r\n" + "Delta: 4\r\n" + "Expect: 5\r\n" + "Foxtrot: 6\r\n" + "Tango: 7\r\n" + "Te: 8\r\n" + "Te: 9\r\n" + "Zulu: 12\r\n"); +} + +void test_http2_lws(void) { + CU_ASSERT(!http2::lws("alpha")); + CU_ASSERT(http2::lws(" ")); + CU_ASSERT(http2::lws("")); +} + +namespace { +void check_rewrite_location_uri(const std::string &want, const std::string &uri, + const std::string &match_host, + const std::string &req_authority, + const std::string &upstream_scheme) { + http_parser_url u; + memset(&u, 0, sizeof(u)); + CU_ASSERT(0 == http_parser_parse_url(uri.c_str(), uri.size(), 0, &u)); + auto got = http2::rewrite_location_uri(uri, u, match_host, req_authority, + upstream_scheme); + CU_ASSERT(want == got); +} +} // namespace + +void test_http2_rewrite_location_uri(void) { + check_rewrite_location_uri("https://localhost:3000/alpha?bravo#charlie", + "http://localhost:3001/alpha?bravo#charlie", + "localhost:3001", "localhost:3000", "https"); + check_rewrite_location_uri("https://localhost/", "http://localhost:3001/", + "localhost", "localhost", "https"); + check_rewrite_location_uri("http://localhost/", "http://localhost:3001/", + "localhost", "localhost", "http"); + check_rewrite_location_uri("http://localhost:443/", "http://localhost:3001/", + "localhost", "localhost:443", "http"); + check_rewrite_location_uri("https://localhost:80/", "http://localhost:3001/", + "localhost", "localhost:80", "https"); + check_rewrite_location_uri("", "http://localhost:3001/", "127.0.0.1", + "127.0.0.1", "https"); + check_rewrite_location_uri("https://localhost:3000/", + "http://localhost:3001/", "localhost", + "localhost:3000", "https"); + check_rewrite_location_uri("https://localhost:3000/", "http://localhost/", + "localhost", "localhost:3000", "https"); + + // match_host != req_authority + check_rewrite_location_uri("https://example.org", "http://127.0.0.1:8080", + "127.0.0.1", "example.org", "https"); + check_rewrite_location_uri("", "http://example.org", "127.0.0.1", + "example.org", "https"); +} + +void test_http2_parse_http_status_code(void) { + CU_ASSERT(200 == http2::parse_http_status_code("200")); + CU_ASSERT(102 == http2::parse_http_status_code("102")); + CU_ASSERT(-1 == http2::parse_http_status_code("099")); + CU_ASSERT(-1 == http2::parse_http_status_code("99")); + CU_ASSERT(-1 == http2::parse_http_status_code("-1")); + CU_ASSERT(-1 == http2::parse_http_status_code("20a")); + CU_ASSERT(-1 == http2::parse_http_status_code("")); +} + +void test_http2_index_header(void) { + http2::HeaderIndex hdidx; + http2::init_hdidx(hdidx); + + http2::index_header(hdidx, http2::HD__AUTHORITY, 0); + http2::index_header(hdidx, -1, 1); + + CU_ASSERT(0 == hdidx[http2::HD__AUTHORITY]); +} + +void test_http2_lookup_token(void) { + CU_ASSERT(http2::HD__AUTHORITY == http2::lookup_token(":authority")); + CU_ASSERT(-1 == http2::lookup_token(":authorit")); + CU_ASSERT(-1 == http2::lookup_token(":Authority")); + CU_ASSERT(http2::HD_EXPECT == http2::lookup_token("expect")); +} + +void test_http2_check_http2_pseudo_header(void) { + http2::HeaderIndex hdidx; + http2::init_hdidx(hdidx); + + CU_ASSERT(http2::check_http2_request_pseudo_header(hdidx, http2::HD__METHOD)); + hdidx[http2::HD__PATH] = 0; + CU_ASSERT(http2::check_http2_request_pseudo_header(hdidx, http2::HD__METHOD)); + hdidx[http2::HD__METHOD] = 1; + CU_ASSERT( + !http2::check_http2_request_pseudo_header(hdidx, http2::HD__METHOD)); + CU_ASSERT(!http2::check_http2_request_pseudo_header(hdidx, http2::HD_VIA)); + + http2::init_hdidx(hdidx); + + CU_ASSERT( + http2::check_http2_response_pseudo_header(hdidx, http2::HD__STATUS)); + hdidx[http2::HD__STATUS] = 0; + CU_ASSERT( + !http2::check_http2_response_pseudo_header(hdidx, http2::HD__STATUS)); + CU_ASSERT(!http2::check_http2_response_pseudo_header(hdidx, http2::HD_VIA)); +} + +void test_http2_http2_header_allowed(void) { + CU_ASSERT(http2::http2_header_allowed(http2::HD__PATH)); + CU_ASSERT(http2::http2_header_allowed(http2::HD_CONTENT_LENGTH)); + CU_ASSERT(!http2::http2_header_allowed(http2::HD_CONNECTION)); +} + +void test_http2_mandatory_request_headers_presence(void) { + http2::HeaderIndex hdidx; + http2::init_hdidx(hdidx); + + CU_ASSERT(!http2::http2_mandatory_request_headers_presence(hdidx)); + hdidx[http2::HD__AUTHORITY] = 0; + CU_ASSERT(!http2::http2_mandatory_request_headers_presence(hdidx)); + hdidx[http2::HD__METHOD] = 1; + CU_ASSERT(!http2::http2_mandatory_request_headers_presence(hdidx)); + hdidx[http2::HD__PATH] = 2; + CU_ASSERT(!http2::http2_mandatory_request_headers_presence(hdidx)); + hdidx[http2::HD__SCHEME] = 3; + CU_ASSERT(http2::http2_mandatory_request_headers_presence(hdidx)); + + hdidx[http2::HD__AUTHORITY] = -1; + hdidx[http2::HD_HOST] = 0; + CU_ASSERT(http2::http2_mandatory_request_headers_presence(hdidx)); +} + +void test_http2_parse_link_header(void) { + { + // only URI appears; we don't extract URI unless it bears rel=preload + const char s[] = ""; + auto res = http2::parse_link_header(s, sizeof(s) - 1); + CU_ASSERT(0 == res.size()); + } + { + // URI url should be extracted + const char s[] = "; rel=preload"; + auto res = http2::parse_link_header(s, sizeof(s) - 1); + CU_ASSERT(1 == res.size()); + CU_ASSERT(std::make_pair(&s[1], &s[4]) == res[0].uri); + } + { + // With extra link-param. URI url should be extracted + const char s[] = "; rel=preload; as=file"; + auto res = http2::parse_link_header(s, sizeof(s) - 1); + CU_ASSERT(1 == res.size()); + CU_ASSERT(std::make_pair(&s[1], &s[4]) == res[0].uri); + } + { + // With extra link-param. URI url should be extracted + const char s[] = "; as=file; rel=preload"; + auto res = http2::parse_link_header(s, sizeof(s) - 1); + CU_ASSERT(1 == res.size()); + CU_ASSERT(std::make_pair(&s[1], &s[4]) == res[0].uri); + } + { + // With extra link-param and quote-string. URI url should be + // extracted + const char s[] = R"(; rel=preload; title="foo,bar")"; + auto res = http2::parse_link_header(s, sizeof(s) - 1); + CU_ASSERT(1 == res.size()); + CU_ASSERT(std::make_pair(&s[1], &s[4]) == res[0].uri); + } + { + // With extra link-param and quote-string. URI url should be + // extracted + const char s[] = R"(; title="foo,bar"; rel=preload)"; + auto res = http2::parse_link_header(s, sizeof(s) - 1); + CU_ASSERT(1 == res.size()); + CU_ASSERT(std::make_pair(&s[1], &s[4]) == res[0].uri); + } + { + // ',' after quote-string + const char s[] = R"(; title="foo,bar", ; rel=preload)"; + auto res = http2::parse_link_header(s, sizeof(s) - 1); + CU_ASSERT(1 == res.size()); + CU_ASSERT(std::make_pair(&s[25], &s[28]) == res[0].uri); + } + { + // Only first URI should be extracted. + const char s[] = "; rel=preload, "; + auto res = http2::parse_link_header(s, sizeof(s) - 1); + CU_ASSERT(1 == res.size()); + CU_ASSERT(std::make_pair(&s[1], &s[4]) == res[0].uri); + } + { + // Both have rel=preload, so both urls should be extracted + const char s[] = "; rel=preload, ; rel=preload"; + auto res = http2::parse_link_header(s, sizeof(s) - 1); + CU_ASSERT(2 == res.size()); + CU_ASSERT(std::make_pair(&s[1], &s[4]) == res[0].uri); + CU_ASSERT(std::make_pair(&s[21], &s[24]) == res[1].uri); + } + { + // Second URI uri should be extracted. + const char s[] = ", ;rel=preload"; + auto res = http2::parse_link_header(s, sizeof(s) - 1); + CU_ASSERT(1 == res.size()); + CU_ASSERT(std::make_pair(&s[8], &s[11]) == res[0].uri); + } + { + // Error if input ends with ';' + const char s[] = ";rel=preload;"; + auto res = http2::parse_link_header(s, sizeof(s) - 1); + CU_ASSERT(0 == res.size()); + } + { + // OK if input ends with ',' + const char s[] = ";rel=preload,"; + auto res = http2::parse_link_header(s, sizeof(s) - 1); + CU_ASSERT(1 == res.size()); + CU_ASSERT(std::make_pair(&s[1], &s[4]) == res[0].uri); + } + { + // Multiple repeated ','s between fields is OK + const char s[] = ",,,;rel=preload"; + auto res = http2::parse_link_header(s, sizeof(s) - 1); + CU_ASSERT(1 == res.size()); + CU_ASSERT(std::make_pair(&s[9], &s[12]) == res[0].uri); + } + { + // Error if url is not enclosed by <> + const char s[] = "url>;rel=preload;"; + auto res = http2::parse_link_header(s, sizeof(s) - 1); + CU_ASSERT(0 == res.size()); + } + { + // Error if url is not enclosed by <> + const char s[] = "' is not followed by ';' + const char s[] = " rel=preload"; + auto res = http2::parse_link_header(s, sizeof(s) - 1); + CU_ASSERT(0 == res.size()); + } + { + // Starting with whitespace is no problem. + const char s[] = " ; rel=preload"; + auto res = http2::parse_link_header(s, sizeof(s) - 1); + CU_ASSERT(1 == res.size()); + CU_ASSERT(std::make_pair(&s[3], &s[6]) == res[0].uri); + } + { + // preload is a prefix of bogus rel parameter value + const char s[] = "; rel=preloadx"; + auto res = http2::parse_link_header(s, sizeof(s) - 1); + CU_ASSERT(0 == res.size()); + } + { + // preload in relation-types list + const char s[] = R"(; rel="preload")"; + auto res = http2::parse_link_header(s, sizeof(s) - 1); + CU_ASSERT(1 == res.size()); + CU_ASSERT(std::make_pair(&s[1], &s[4]) == res[0].uri); + } + { + // preload in relation-types list followed by another parameter + const char s[] = R"(; rel="preload foo")"; + auto res = http2::parse_link_header(s, sizeof(s) - 1); + CU_ASSERT(1 == res.size()); + CU_ASSERT(std::make_pair(&s[1], &s[4]) == res[0].uri); + } + { + // preload in relation-types list following another parameter + const char s[] = R"(; rel="foo preload")"; + auto res = http2::parse_link_header(s, sizeof(s) - 1); + CU_ASSERT(1 == res.size()); + CU_ASSERT(std::make_pair(&s[1], &s[4]) == res[0].uri); + } + { + // preload in relation-types list between other parameters + const char s[] = R"(; rel="foo preload bar")"; + auto res = http2::parse_link_header(s, sizeof(s) - 1); + CU_ASSERT(1 == res.size()); + CU_ASSERT(std::make_pair(&s[1], &s[4]) == res[0].uri); + } + { + // preload in relation-types list between other parameters + const char s[] = R"(; rel="foo preload bar")"; + auto res = http2::parse_link_header(s, sizeof(s) - 1); + CU_ASSERT(1 == res.size()); + CU_ASSERT(std::make_pair(&s[1], &s[4]) == res[0].uri); + } + { + // no preload in relation-types list + const char s[] = R"(; rel="foo")"; + auto res = http2::parse_link_header(s, sizeof(s) - 1); + CU_ASSERT(0 == res.size()); + } + { + // no preload in relation-types list, multiple unrelated elements. + const char s[] = R"(; rel="foo bar")"; + auto res = http2::parse_link_header(s, sizeof(s) - 1); + CU_ASSERT(0 == res.size()); + } + { + // preload in relation-types list, followed by another link-value. + const char s[] = R"(; rel="preload", )"; + auto res = http2::parse_link_header(s, sizeof(s) - 1); + CU_ASSERT(1 == res.size()); + CU_ASSERT(std::make_pair(&s[1], &s[4]) == res[0].uri); + } + { + // preload in relation-types list, following another link-value. + const char s[] = R"(, ; rel="preload")"; + auto res = http2::parse_link_header(s, sizeof(s) - 1); + CU_ASSERT(1 == res.size()); + CU_ASSERT(std::make_pair(&s[8], &s[11]) == res[0].uri); + } + { + // preload in relation-types list, followed by another link-param. + const char s[] = R"(; rel="preload"; as="font")"; + auto res = http2::parse_link_header(s, sizeof(s) - 1); + CU_ASSERT(1 == res.size()); + CU_ASSERT(std::make_pair(&s[1], &s[4]) == res[0].uri); + } + { + // preload in relation-types list, followed by character other + // than ';' or ',' + const char s[] = R"(; rel="preload".)"; + auto res = http2::parse_link_header(s, sizeof(s) - 1); + CU_ASSERT(0 == res.size()); + } + { + // preload in relation-types list, followed by ';' but it + // terminates input + const char s[] = R"(; rel="preload";)"; + auto res = http2::parse_link_header(s, sizeof(s) - 1); + CU_ASSERT(0 == res.size()); + } + { + // preload in relation-types list, followed by ',' but it + // terminates input + const char s[] = R"(; rel="preload",)"; + auto res = http2::parse_link_header(s, sizeof(s) - 1); + CU_ASSERT(1 == res.size()); + CU_ASSERT(std::make_pair(&s[1], &s[4]) == res[0].uri); + } + { + // preload in relation-types list but there is preceding white + // space. + const char s[] = R"(; rel=" preload")"; + auto res = http2::parse_link_header(s, sizeof(s) - 1); + CU_ASSERT(0 == res.size()); + } + { + // preload in relation-types list but there is trailing white + // space. + const char s[] = R"(; rel="preload ")"; + auto res = http2::parse_link_header(s, sizeof(s) - 1); + CU_ASSERT(0 == res.size()); + } + { + // backslash escaped characters in quoted-string + const char s[] = R"(; rel=preload; title="foo\"baz\"bar")"; + auto res = http2::parse_link_header(s, sizeof(s) - 1); + CU_ASSERT(1 == res.size()); + CU_ASSERT(std::make_pair(&s[1], &s[4]) == res[0].uri); + } + { + // anchor="" is acceptable + const char s[] = R"(; rel=preload; anchor="")"; + auto res = http2::parse_link_header(s, sizeof(s) - 1); + CU_ASSERT(1 == res.size()); + CU_ASSERT(std::make_pair(&s[1], &s[4]) == res[0].uri); + } + { + // With anchor="#foo", url should be ignored + const char s[] = R"(; rel=preload; anchor="#foo")"; + auto res = http2::parse_link_header(s, sizeof(s) - 1); + CU_ASSERT(0 == res.size()); + } + { + // With anchor=f, url should be ignored + const char s[] = "; rel=preload; anchor=f"; + auto res = http2::parse_link_header(s, sizeof(s) - 1); + CU_ASSERT(0 == res.size()); + } + { + // First url is ignored With anchor="#foo", but url should be + // accepted. + const char s[] = R"(; rel=preload; anchor="#foo", ; rel=preload)"; + auto res = http2::parse_link_header(s, sizeof(s) - 1); + CU_ASSERT(1 == res.size()); + CU_ASSERT(std::make_pair(&s[36], &s[39]) == res[0].uri); + } + { + // With loadpolicy="next", url should be ignored + const char s[] = R"(; rel=preload; loadpolicy="next")"; + auto res = http2::parse_link_header(s, sizeof(s) - 1); + CU_ASSERT(0 == res.size()); + } + { + // url should be picked up if empty loadpolicy is specified + const char s[] = R"(; rel=preload; loadpolicy="")"; + auto res = http2::parse_link_header(s, sizeof(s) - 1); + CU_ASSERT(1 == res.size()); + CU_ASSERT(std::make_pair(&s[1], &s[4]) == res[0].uri); + } + { + // case-insensitive match + const char s[] = R"(; rel=preload; ANCHOR="#foo", ; )" + R"(REL=PRELOAD, ; REL="foo PRELOAD bar")"; + auto res = http2::parse_link_header(s, sizeof(s) - 1); + CU_ASSERT(2 == res.size()); + CU_ASSERT(std::make_pair(&s[36], &s[39]) == res[0].uri); + CU_ASSERT(std::make_pair(&s[42 + 14], &s[42 + 17]) == res[1].uri); + } +} + +void test_http2_path_join(void) { + { + const char base[] = "/"; + const char rel[] = "/"; + CU_ASSERT("/" == http2::path_join(base, sizeof(base) - 1, nullptr, 0, rel, + sizeof(rel) - 1, nullptr, 0)); + } + { + const char base[] = "/"; + const char rel[] = "/alpha"; + CU_ASSERT("/alpha" == http2::path_join(base, sizeof(base) - 1, nullptr, 0, + rel, sizeof(rel) - 1, nullptr, 0)); + } + { + // rel ends with trailing '/' + const char base[] = "/"; + const char rel[] = "/alpha/"; + CU_ASSERT("/alpha/" == http2::path_join(base, sizeof(base) - 1, nullptr, 0, + rel, sizeof(rel) - 1, nullptr, 0)); + } + { + // rel contains multiple components + const char base[] = "/"; + const char rel[] = "/alpha/bravo"; + CU_ASSERT("/alpha/bravo" == http2::path_join(base, sizeof(base) - 1, + nullptr, 0, rel, + sizeof(rel) - 1, nullptr, 0)); + } + { + // rel is relative + const char base[] = "/"; + const char rel[] = "alpha/bravo"; + CU_ASSERT("/alpha/bravo" == http2::path_join(base, sizeof(base) - 1, + nullptr, 0, rel, + sizeof(rel) - 1, nullptr, 0)); + } + { + // rel is relative and base ends without /, which means it refers + // to file. + const char base[] = "/alpha"; + const char rel[] = "bravo/charlie"; + CU_ASSERT("/bravo/charlie" == + http2::path_join(base, sizeof(base) - 1, nullptr, 0, rel, + sizeof(rel) - 1, nullptr, 0)); + } + { + // rel contains repeated '/'s + const char base[] = "/"; + const char rel[] = "/alpha/////bravo/////"; + CU_ASSERT("/alpha/bravo/" == http2::path_join(base, sizeof(base) - 1, + nullptr, 0, rel, + sizeof(rel) - 1, nullptr, 0)); + } + { + // base ends with '/', so '..' eats 'bravo' + const char base[] = "/alpha/bravo/"; + const char rel[] = "../charlie/delta"; + CU_ASSERT("/alpha/charlie/delta" == + http2::path_join(base, sizeof(base) - 1, nullptr, 0, rel, + sizeof(rel) - 1, nullptr, 0)); + } + { + // base does not end with '/', so '..' eats 'alpha/bravo' + const char base[] = "/alpha/bravo"; + const char rel[] = "../charlie"; + CU_ASSERT("/charlie" == http2::path_join(base, sizeof(base) - 1, nullptr, 0, + rel, sizeof(rel) - 1, nullptr, 0)); + } + { + // 'charlie' is eaten by following '..' + const char base[] = "/alpha/bravo/"; + const char rel[] = "../charlie/../delta"; + CU_ASSERT("/alpha/delta" == http2::path_join(base, sizeof(base) - 1, + nullptr, 0, rel, + sizeof(rel) - 1, nullptr, 0)); + } + { + // excessive '..' results in '/' + const char base[] = "/alpha/bravo/"; + const char rel[] = "../../../"; + CU_ASSERT("/" == http2::path_join(base, sizeof(base) - 1, nullptr, 0, rel, + sizeof(rel) - 1, nullptr, 0)); + } + { + // excessive '..' and path component + const char base[] = "/alpha/bravo/"; + const char rel[] = "../../../charlie"; + CU_ASSERT("/charlie" == http2::path_join(base, sizeof(base) - 1, nullptr, 0, + rel, sizeof(rel) - 1, nullptr, 0)); + } + { + // rel ends with '..' + const char base[] = "/alpha/bravo/"; + const char rel[] = "charlie/.."; + CU_ASSERT("/alpha/bravo/" == http2::path_join(base, sizeof(base) - 1, + nullptr, 0, rel, + sizeof(rel) - 1, nullptr, 0)); + } + { + // base empty and rel contains '..' + const char base[] = ""; + const char rel[] = "charlie/.."; + CU_ASSERT("/" == http2::path_join(base, sizeof(base) - 1, nullptr, 0, rel, + sizeof(rel) - 1, nullptr, 0)); + } + { + // '.' is ignored + const char base[] = "/"; + const char rel[] = "charlie/././././delta"; + CU_ASSERT("/charlie/delta" == + http2::path_join(base, sizeof(base) - 1, nullptr, 0, rel, + sizeof(rel) - 1, nullptr, 0)); + } + { + // trailing '.' is ignored + const char base[] = "/"; + const char rel[] = "charlie/."; + CU_ASSERT("/charlie/" == http2::path_join(base, sizeof(base) - 1, nullptr, + 0, rel, sizeof(rel) - 1, nullptr, + 0)); + } + { + // query + const char base[] = "/"; + const char rel[] = "/"; + const char relq[] = "q"; + CU_ASSERT("/?q" == http2::path_join(base, sizeof(base) - 1, nullptr, 0, rel, + sizeof(rel) - 1, relq, + sizeof(relq) - 1)); + } + { + // empty rel and query + const char base[] = "/alpha"; + const char rel[] = ""; + const char relq[] = "q"; + CU_ASSERT("/alpha?q" == http2::path_join(base, sizeof(base) - 1, nullptr, 0, + rel, sizeof(rel) - 1, relq, + sizeof(relq) - 1)); + } + { + // both rel and query are empty + const char base[] = "/alpha"; + const char baseq[] = "r"; + const char rel[] = ""; + const char relq[] = ""; + CU_ASSERT("/alpha?r" == + http2::path_join(base, sizeof(base) - 1, baseq, sizeof(baseq) - 1, + rel, sizeof(rel) - 1, relq, sizeof(relq) - 1)); + } + { + // empty base + const char base[] = ""; + const char rel[] = "/alpha"; + CU_ASSERT("/alpha" == http2::path_join(base, sizeof(base) - 1, nullptr, 0, + rel, sizeof(rel) - 1, nullptr, 0)); + } + { + // everything is empty + CU_ASSERT("/" == + http2::path_join(nullptr, 0, nullptr, 0, nullptr, 0, nullptr, 0)); + } + { + // only baseq is not empty + const char base[] = ""; + const char baseq[] = "r"; + const char rel[] = ""; + CU_ASSERT("/?r" == http2::path_join(base, sizeof(base) - 1, baseq, + sizeof(baseq) - 1, rel, sizeof(rel) - 1, + nullptr, 0)); + } +} + +} // namespace shrpx diff --git a/src/http2_test.h b/src/http2_test.h new file mode 100644 index 0000000..36c5a59 --- /dev/null +++ b/src/http2_test.h @@ -0,0 +1,51 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2013 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef SHRPX_HTTP2_TEST_H +#define SHRPX_HTTP2_TEST_H + +#ifdef HAVE_CONFIG_H +#include +#endif // HAVE_CONFIG_H + +namespace shrpx { + +void test_http2_add_header(void); +void test_http2_get_header(void); +void test_http2_copy_headers_to_nva(void); +void test_http2_build_http1_headers_from_headers(void); +void test_http2_lws(void); +void test_http2_rewrite_location_uri(void); +void test_http2_parse_http_status_code(void); +void test_http2_index_header(void); +void test_http2_lookup_token(void); +void test_http2_check_http2_pseudo_header(void); +void test_http2_http2_header_allowed(void); +void test_http2_mandatory_request_headers_presence(void); +void test_http2_parse_link_header(void); +void test_http2_path_join(void); + +} // namespace shrpx + +#endif // SHRPX_HTTP2_TEST_H diff --git a/src/includes/Makefile.am b/src/includes/Makefile.am new file mode 100644 index 0000000..abc93c1 --- /dev/null +++ b/src/includes/Makefile.am @@ -0,0 +1,27 @@ +# nghttp2 - HTTP/2 C Library + +# Copyright (c) 2014 Tatsuhiro Tsujikawa + +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: + +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. + +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +if ENABLE_ASIO_LIB +nobase_include_HEADERS = nghttp2/asio_http2.h nghttp2/asio_http2_client.h \ + nghttp2/asio_http2_server.h +endif # ENABLE_ASIO_LIB diff --git a/src/includes/Makefile.in b/src/includes/Makefile.in new file mode 100644 index 0000000..9892cef --- /dev/null +++ b/src/includes/Makefile.in @@ -0,0 +1,640 @@ +# Makefile.in generated by automake 1.14.1 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2013 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +# nghttp2 - HTTP/2 C Library + +# Copyright (c) 2014 Tatsuhiro Tsujikawa + +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: + +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. + +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +VPATH = @srcdir@ +am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)' +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +target_triplet = @target@ +subdir = src/includes +DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \ + $(am__nobase_include_HEADERS_DIST) +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/ax_boost_asio.m4 \ + $(top_srcdir)/m4/ax_boost_base.m4 \ + $(top_srcdir)/m4/ax_boost_system.m4 \ + $(top_srcdir)/m4/ax_boost_thread.m4 \ + $(top_srcdir)/m4/ax_check_compile_flag.m4 \ + $(top_srcdir)/m4/ax_cxx_compile_stdcxx_11.m4 \ + $(top_srcdir)/m4/ax_have_epoll.m4 \ + $(top_srcdir)/m4/ax_python_devel.m4 \ + $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/libxml2.m4 \ + $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ + $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +SOURCES = +DIST_SOURCES = +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +am__nobase_include_HEADERS_DIST = nghttp2/asio_http2.h \ + nghttp2/asio_http2_client.h nghttp2/asio_http2_server.h +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +am__uninstall_files_from_dir = { \ + test -z "$$files" \ + || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ + || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ + $(am__cd) "$$dir" && rm -f $$files; }; \ + } +am__installdirs = "$(DESTDIR)$(includedir)" +HEADERS = $(nobase_include_HEADERS) +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +ETAGS = etags +CTAGS = ctags +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +APPLDFLAGS = @APPLDFLAGS@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +BOOST_ASIO_LIB = @BOOST_ASIO_LIB@ +BOOST_CPPFLAGS = @BOOST_CPPFLAGS@ +BOOST_LDFLAGS = @BOOST_LDFLAGS@ +BOOST_SYSTEM_LIB = @BOOST_SYSTEM_LIB@ +BOOST_THREAD_LIB = @BOOST_THREAD_LIB@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CUNIT_CFLAGS = @CUNIT_CFLAGS@ +CUNIT_LIBS = @CUNIT_LIBS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +CYTHON = @CYTHON@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DLLTOOL = @DLLTOOL@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +GREP = @GREP@ +HAVE_CXX11 = @HAVE_CXX11@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +JANSSON_CFLAGS = @JANSSON_CFLAGS@ +JANSSON_LIBS = @JANSSON_LIBS@ +JEMALLOC_LIBS = @JEMALLOC_LIBS@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBEVENT_OPENSSL_CFLAGS = @LIBEVENT_OPENSSL_CFLAGS@ +LIBEVENT_OPENSSL_LIBS = @LIBEVENT_OPENSSL_LIBS@ +LIBEV_CFLAGS = @LIBEV_CFLAGS@ +LIBEV_LIBS = @LIBEV_LIBS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBSPDYLAY_CFLAGS = @LIBSPDYLAY_CFLAGS@ +LIBSPDYLAY_LIBS = @LIBSPDYLAY_LIBS@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +LT_AGE = @LT_AGE@ +LT_CURRENT = @LT_CURRENT@ +LT_REVISION = @LT_REVISION@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MKDIR_P = @MKDIR_P@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OPENSSL_CFLAGS = @OPENSSL_CFLAGS@ +OPENSSL_LIBS = @OPENSSL_LIBS@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PACKAGE_VERSION_NUM = @PACKAGE_VERSION_NUM@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PKG_CONFIG = @PKG_CONFIG@ +PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ +PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ +PYTHON = @PYTHON@ +PYTHON_CPPFLAGS = @PYTHON_CPPFLAGS@ +PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@ +PYTHON_EXTRA_LDFLAGS = @PYTHON_EXTRA_LDFLAGS@ +PYTHON_EXTRA_LIBS = @PYTHON_EXTRA_LIBS@ +PYTHON_LDFLAGS = @PYTHON_LDFLAGS@ +PYTHON_PLATFORM = @PYTHON_PLATFORM@ +PYTHON_PREFIX = @PYTHON_PREFIX@ +PYTHON_SITE_PKG = @PYTHON_SITE_PKG@ +PYTHON_VERSION = @PYTHON_VERSION@ +RANLIB = @RANLIB@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +TESTLDADD = @TESTLDADD@ +VERSION = @VERSION@ +WARNCFLAGS = @WARNCFLAGS@ +XML2_CONFIG = @XML2_CONFIG@ +XML_CPPFLAGS = @XML_CPPFLAGS@ +XML_LIBS = @XML_LIBS@ +ZLIB_CFLAGS = @ZLIB_CFLAGS@ +ZLIB_LIBS = @ZLIB_LIBS@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +pkgpyexecdir = @pkgpyexecdir@ +pkgpythondir = @pkgpythondir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +pyexecdir = @pyexecdir@ +pythondir = @pythondir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target = @target@ +target_alias = @target_alias@ +target_cpu = @target_cpu@ +target_os = @target_os@ +target_vendor = @target_vendor@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +@ENABLE_ASIO_LIB_TRUE@nobase_include_HEADERS = nghttp2/asio_http2.h nghttp2/asio_http2_client.h \ +@ENABLE_ASIO_LIB_TRUE@ nghttp2/asio_http2_server.h + +all: all-am + +.SUFFIXES: +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/includes/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu src/includes/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs +install-nobase_includeHEADERS: $(nobase_include_HEADERS) + @$(NORMAL_INSTALL) + @list='$(nobase_include_HEADERS)'; test -n "$(includedir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(includedir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(includedir)" || exit 1; \ + fi; \ + $(am__nobase_list) | while read dir files; do \ + xfiles=; for file in $$files; do \ + if test -f "$$file"; then xfiles="$$xfiles $$file"; \ + else xfiles="$$xfiles $(srcdir)/$$file"; fi; done; \ + test -z "$$xfiles" || { \ + test "x$$dir" = x. || { \ + echo " $(MKDIR_P) '$(DESTDIR)$(includedir)/$$dir'"; \ + $(MKDIR_P) "$(DESTDIR)$(includedir)/$$dir"; }; \ + echo " $(INSTALL_HEADER) $$xfiles '$(DESTDIR)$(includedir)/$$dir'"; \ + $(INSTALL_HEADER) $$xfiles "$(DESTDIR)$(includedir)/$$dir" || exit $$?; }; \ + done + +uninstall-nobase_includeHEADERS: + @$(NORMAL_UNINSTALL) + @list='$(nobase_include_HEADERS)'; test -n "$(includedir)" || list=; \ + $(am__nobase_strip_setup); files=`$(am__nobase_strip)`; \ + dir='$(DESTDIR)$(includedir)'; $(am__uninstall_files_from_dir) + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-am +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + $(am__define_uniq_tagged_files); \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: ctags-am + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" +cscopelist: cscopelist-am + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(HEADERS) +installdirs: + for dir in "$(DESTDIR)$(includedir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic clean-libtool mostlyclean-am + +distclean: distclean-am + -rm -f Makefile +distclean-am: clean-am distclean-generic distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: install-nobase_includeHEADERS + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-generic mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-nobase_includeHEADERS + +.MAKE: install-am install-strip + +.PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ + clean-libtool cscopelist-am ctags ctags-am distclean \ + distclean-generic distclean-libtool distclean-tags distdir dvi \ + dvi-am html html-am info info-am install install-am \ + install-data install-data-am install-dvi install-dvi-am \ + install-exec install-exec-am install-html install-html-am \ + install-info install-info-am install-man \ + install-nobase_includeHEADERS install-pdf install-pdf-am \ + install-ps install-ps-am install-strip installcheck \ + installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-generic \ + mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \ + uninstall-am uninstall-nobase_includeHEADERS + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/src/includes/nghttp2/asio_http2.h b/src/includes/nghttp2/asio_http2.h new file mode 100644 index 0000000..a54a9e8 --- /dev/null +++ b/src/includes/nghttp2/asio_http2.h @@ -0,0 +1,139 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2014 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef ASIO_HTTP2_H +#define ASIO_HTTP2_H + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +namespace boost { +namespace system { + +template <> struct is_error_code_enum { + BOOST_STATIC_CONSTANT(bool, value = true); +}; + +} // namespace system +} // namespace boost + +namespace nghttp2 { + +namespace asio_http2 { + +struct header_value { + // header field value + std::string value; + // true if the header field value is sensitive information, such as + // authorization information or short length secret cookies. If + // true, those header fields are not indexed by HPACK (but still + // huffman-encoded), which results in lesser compression. + bool sensitive; +}; + +// header fields. The header field name must be lower-cased. +using header_map = std::multimap; + +const boost::system::error_category &nghttp2_category() noexcept; + +struct uri_ref { + std::string scheme; + std::string host; + // form after percent-encoding decoded + std::string path; + // original path, percent-encoded + std::string raw_path; + // original query, percent-encoded + std::string raw_query; + std::string fragment; +}; + +// Callback function when data is arrived. EOF is indicated by +// passing 0 to the second parameter. +typedef std::function data_cb; +typedef std::function void_cb; +typedef std::function error_cb; +// Callback function when request and response are finished. The +// parameter indicates the cause of closure. +typedef std::function close_cb; + +// Callback function to generate response body. This function has the +// same semantics with nghttp2_data_source_read_callback. Just source +// and user_data parameters are removed. +// +// Basically, write at most |len| bytes to |data| and returns the +// number of bytes written. If there is no data left to send, set +// NGHTTP2_DATA_FLAG_EOF to *data_flags (e.g., *data_flags |= +// NGHTTP2_DATA_FLAG_EOF). If there is still data to send but they +// are not available right now, return NGHTTP2_ERR_DEFERRED. In case +// of the error and request/response must be closed, return +// NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE. +typedef std::function< + ssize_t(uint8_t *buf, std::size_t len, uint32_t *data_flags)> generator_cb; + +// Convenient function to create function to read file denoted by +// |path|. This can be passed to response::end(). +generator_cb file_generator(const std::string &path); + +// Like file_generator(const std::string&), but it takes opened file +// descriptor. The passed descriptor will be closed when returned +// function object is destroyed. +generator_cb file_generator_from_fd(int fd); + +// Validates path so that it does not contain directory traversal +// vector. Returns true if path is safe. The |path| must start with +// "/" otherwise returns false. This function should be called after +// percent-decode was performed. +bool check_path(const std::string &path); + +// Performs percent-decode against string |s|. +std::string percent_decode(const std::string &s); + +// Returns HTTP date representation of current posix time |t|. +std::string http_date(int64_t t); + +// Parses |uri| and extract scheme, host and service. The service is +// port component of URI (e.g., "8443") if available, otherwise it is +// scheme (e.g., "https"). +boost::system::error_code host_service_from_uri(boost::system::error_code &ec, + std::string &scheme, + std::string &host, + std::string &service, + const std::string &uri); + +} // namespace asio_http2 + +} // namespace nghttp2 + +#endif // ASIO_HTTP2_H diff --git a/src/includes/nghttp2/asio_http2_client.h b/src/includes/nghttp2/asio_http2_client.h new file mode 100644 index 0000000..a2b51ad --- /dev/null +++ b/src/includes/nghttp2/asio_http2_client.h @@ -0,0 +1,195 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2015 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef ASIO_HTTP2_CLIENT_H +#define ASIO_HTTP2_CLIENT_H + +#include + +namespace nghttp2 { + +namespace asio_http2 { + +namespace client { + +class response_impl; + +class response { +public: + // Application must not call this directly. + response(); + ~response(); + + // Sets callback which is invoked when chunk of response body is + // received. + void on_data(data_cb cb) const; + + // Returns status code. + int status_code() const; + + // Returns content-length. -1 if it is unknown. + int64_t content_length() const; + + // Returns the response header fields. The pusedo header fields, + // which start with colon (:), are exluced from this list. + const header_map &header() const; + + // Application must not call this directly. + response_impl &impl() const; + +private: + std::unique_ptr impl_; +}; + +class request; + +using response_cb = std::function; +using request_cb = std::function; +using connect_cb = + std::function; + +class request_impl; + +class request { +public: + // Application must not call this directly. + request(); + ~request(); + + // Sets callback which is invoked when response header is received. + void on_response(response_cb cb) const; + + // Sets callback which is invoked when push request header is + // received. + void on_push(request_cb cb) const; + + // Sets callback which is invoked when this request and response are + // finished. After the invocation of this callback, the application + // must not access request and response object. + void on_close(close_cb cb) const; + + // Write trailer part. This must be called after setting both + // NGHTTP2_DATA_FLAG_EOF and NGHTTP2_DATA_FLAG_NO_END_STREAM set in + // *data_flag parameter in generator_cb passed to session::submit() + // function. + void write_trailer(header_map h) const; + + // Cancels this request and response with given error code. + void cancel(uint32_t error_code = NGHTTP2_INTERNAL_ERROR) const; + + // Resumes deferred uploading. + void resume() const; + + // Returns method (e.g., GET). + const std::string &method() const; + + // Returns request URI, split into components. + const uri_ref &uri() const; + + // Returns request header fields. The pusedo header fields, which + // start with colon (:), are exluced from this list. + const header_map &header() const; + + // Application must not call this directly. + request_impl &impl() const; + +private: + std::unique_ptr impl_; +}; + +class session_impl; + +class session { +public: + // Starts HTTP/2 session by connecting to |host| and |service| + // (e.g., "80") using clear text TCP connection. + session(boost::asio::io_service &io_service, const std::string &host, + const std::string &service); + + // Starts HTTP/2 session by connecting to |host| and |service| + // (e.g., "443") using encrypted SSL/TLS connection. + session(boost::asio::io_service &io_service, + boost::asio::ssl::context &tls_context, const std::string &host, + const std::string &service); + ~session(); + + session(session &&other) noexcept; + session &operator=(session &&other) noexcept; + + // Sets callback which is invoked after connection is established. + void on_connect(connect_cb cb) const; + + // Sets callback which is invoked there is connection level error + // and session is terminated. + void on_error(error_cb cb) const; + + // Shutdowns connection. + void shutdown() const; + + // Returns underlying io_service object. + boost::asio::io_service &io_service() const; + + // Submits request to server using |method| (e.g., "GET"), |uri| + // (e.g., "http://localhost/") and optionally additional header + // fields. This function returns pointer to request object if it + // succeeds, or nullptr and |ec| contains error message. + const request *submit(boost::system::error_code &ec, + const std::string &method, const std::string &uri, + header_map h = header_map{}) const; + + // Submits request to server using |method| (e.g., "GET"), |uri| + // (e.g., "http://localhost/") and optionally additional header + // fields. The |data| is request body. This function returns + // pointer to request object if it succeeds, or nullptr and |ec| + // contains error message. + const request *submit(boost::system::error_code &ec, + const std::string &method, const std::string &uri, + std::string data, header_map h = header_map{}) const; + + // Submits request to server using |method| (e.g., "GET"), |uri| + // (e.g., "http://localhost/") and optionally additional header + // fields. The |cb| is used to generate request body. This + // function returns pointer to request object if it succeeds, or + // nullptr and |ec| contains error message. + const request *submit(boost::system::error_code &ec, + const std::string &method, const std::string &uri, + generator_cb cb, header_map h = header_map{}) const; + +private: + std::unique_ptr impl_; +}; + +// configure |tls_ctx| for client use. Currently, we just set NPN +// callback for HTTP/2. +boost::system::error_code +configure_tls_context(boost::system::error_code &ec, + boost::asio::ssl::context &tls_ctx); + +} // namespace client + +} // namespace asio_http2 + +} // namespace nghttp2 + +#endif // ASIO_HTTP2_CLIENT_H diff --git a/src/includes/nghttp2/asio_http2_server.h b/src/includes/nghttp2/asio_http2_server.h new file mode 100644 index 0000000..1c5a71f --- /dev/null +++ b/src/includes/nghttp2/asio_http2_server.h @@ -0,0 +1,229 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2015 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef ASIO_HTTP2_SERVER_H +#define ASIO_HTTP2_SERVER_H + +#include + +namespace nghttp2 { + +namespace asio_http2 { + +namespace server { + +class request_impl; +class response_impl; + +class request { +public: + // Application must not call this directly. + request(); + ~request(); + + // Returns request header fields. The pusedo header fields, which + // start with colon (:), are exluced from this list. + const header_map &header() const; + + // Returns method (e.g., GET). + const std::string &method() const; + + // Returns request URI, split into components. + const uri_ref &uri() const; + + // Sets callback which is invoked when chunk of request body is + // received. + void on_data(data_cb cb) const; + + // Application must not call this directly. + request_impl &impl() const; + +private: + std::unique_ptr impl_; +}; + +class response { +public: + // Application must not call this directly. + response(); + ~response(); + + // Write response header using |status_code| (e.g., 200) and + // additional header fields in |h|. + void write_head(unsigned int status_code, header_map h = header_map{}) const; + + // Sends |data| as request body. No further call of end() is + // allowed. + void end(std::string data = "") const; + + // Sets callback as a generator of the response body. No further + // call of end() is allowed. + void end(generator_cb cb) const; + + // Write trailer part. This must be called after setting both + // NGHTTP2_DATA_FLAG_EOF and NGHTTP2_DATA_FLAG_NO_END_STREAM set in + // *data_flag parameter in generator_cb passed to end() function. + void write_trailer(header_map h) const; + + // Sets callback which is invoked when this request and response are + // finished. After the invocation of this callback, the application + // must not access request and response object. + void on_close(close_cb cb) const; + + // Cancels this request and response with given error code. + void cancel(uint32_t error_code = NGHTTP2_INTERNAL_ERROR) const; + + // Resumes deferred response. + void resume() const; + + // Pushes resource denoted by |raw_path_query| using |method|. The + // additional header fields can be given in |h|. This function + // returns pointer to response object for promised stream, otherwise + // nullptr and error code is filled in |ec|. Be aware that the + // header field name given in |h| must be lower-cased. + const response *push(boost::system::error_code &ec, std::string method, + std::string raw_path_query, + header_map h = header_map{}) const; + + // Returns status code. + unsigned int status_code() const; + + // Returns boost::asio::io_service this response is running on. + boost::asio::io_service &io_service() const; + + // Application must not call this directly. + response_impl &impl() const; + +private: + std::unique_ptr impl_; +}; + +// This is so called request callback. Called every time request is +// received. The life time of |request| and |response| objects end +// when callback set by response::on_close() is called. After that, +// the application must not access to those objects. +typedef std::function request_cb; + +class http2_impl; + +class http2 { +public: + http2(); + ~http2(); + + http2(http2 &&other) noexcept; + http2 &operator=(http2 &&other) noexcept; + + // Starts listening connection on given address and port and serves + // incoming requests in cleartext TCP connection. If |asynchronous| + // is false, this function blocks forever unless there is an error. + // If it is true, after server has started, this function returns + // immediately, and the caller should call join() to shutdown server + // gracefully. + boost::system::error_code listen_and_serve(boost::system::error_code &ec, + const std::string &address, + const std::string &port, + bool asynchronous = false); + + // Starts listening connection on given address and port and serves + // incoming requests in SSL/TLS encrypted connection. For + // |asynchronous| parameter, see cleartext version + // |listen_and_serve|. + boost::system::error_code + listen_and_serve(boost::system::error_code &ec, + boost::asio::ssl::context &tls_context, + const std::string &address, const std::string &port, + bool asynchronous = false); + + // Registers request handler |cb| with path pattern |pattern|. This + // function will fail and returns false if same pattern has been + // already registered or |pattern| is empty string. Otherwise + // returns true. The pattern match rule is the same as + // net/http/ServeMux in golang. Quoted from golang manual + // (http://golang.org/pkg/net/http/#ServeMux): + // + // Patterns name fixed, rooted paths, like "/favicon.ico", or + // rooted subtrees, like "/images/" (note the trailing + // slash). Longer patterns take precedence over shorter ones, so + // that if there are handlers registered for both "/images/" and + // "/images/thumbnails/", the latter handler will be called for + // paths beginning "/images/thumbnails/" and the former will + // receive requests for any other paths in the "/images/" subtree. + // + // Note that since a pattern ending in a slash names a rooted + // subtree, the pattern "/" matches all paths not matched by other + // registered patterns, not just the URL with Path == "/". + // + // Patterns may optionally begin with a host name, restricting + // matches to URLs on that host only. Host-specific patterns take + // precedence over general patterns, so that a handler might + // register for the two patterns "/codesearch" and + // "codesearch.google.com/" without also taking over requests for + // "http://www.google.com/". + // + // Just like ServeMux in golang, URL request path is sanitized and + // if they contains . or .. elements, they are redirected to an + // equivalent .- and ..-free URL. + bool handle(std::string pattern, request_cb cb); + + // Sets number of native threads to handle incoming HTTP request. + // It defaults to 1. + void num_threads(size_t num_threads); + + // Sets the maximum length to which the queue of pending + // connections. + void backlog(int backlog); + + // Gracefully stop http2 server + void stop(); + + // Join on http2 server and wait for it to fully stop + void join(); + +private: + std::unique_ptr impl_; +}; + +// Configures |tls_context| for server use. This function sets couple +// of OpenSSL options (disables SSLv2 and SSLv3 and compression) and +// enables ECDHE ciphers. NPN callback is also configured. +boost::system::error_code +configure_tls_context_easy(boost::system::error_code &ec, + boost::asio::ssl::context &tls_context); + +// Returns request handler to do redirect to |uri| using +// |status_code|. The |uri| appears in "location" header field as is. +request_cb redirect_handler(int status_code, std::string uri); + +// Returns request handler to reply with given |status_code| and HTML +// including message about status code. +request_cb status_handler(int status_code); + +} // namespace server + +} // namespace asio_http2 + +} // namespace nghttp2 + +#endif // ASIO_HTTP2_SERVER_H diff --git a/src/inflatehd.cc b/src/inflatehd.cc new file mode 100644 index 0000000..98338be --- /dev/null +++ b/src/inflatehd.cc @@ -0,0 +1,277 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2013 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifdef HAVE_CONFIG_H +#include +#endif // HAVE_CONFIG_H + +#ifdef HAVE_UNISTD_H +#include +#endif // HAVE_UNISTD_H +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +extern "C" { + +#include "nghttp2_hd.h" +#include "nghttp2_frame.h" + +#include "comp_helper.h" +} + +typedef struct { int dump_header_table; } inflate_config; + +static inflate_config config; + +static uint8_t to_ud(char c) { + if (c >= 'A' && c <= 'Z') { + return c - 'A' + 10; + } else if (c >= 'a' && c <= 'z') { + return c - 'a' + 10; + } else { + return c - '0'; + } +} + +static void decode_hex(uint8_t *dest, const char *src, size_t len) { + size_t i; + for (i = 0; i < len; i += 2) { + *dest++ = to_ud(src[i]) << 4 | to_ud(src[i + 1]); + } +} + +static void to_json(nghttp2_hd_inflater *inflater, json_t *headers, + json_t *wire, int seq, size_t old_settings_table_size) { + auto obj = json_object(); + json_object_set_new(obj, "seq", json_integer(seq)); + json_object_set(obj, "wire", wire); + json_object_set(obj, "headers", headers); + if (old_settings_table_size != inflater->settings_hd_table_bufsize_max) { + json_object_set_new(obj, "header_table_size", + json_integer(inflater->settings_hd_table_bufsize_max)); + } + if (config.dump_header_table) { + json_object_set_new(obj, "header_table", dump_header_table(&inflater->ctx)); + } + json_dumpf(obj, stdout, JSON_INDENT(2) | JSON_PRESERVE_ORDER); + json_decref(obj); + printf("\n"); +} + +static int inflate_hd(json_t *obj, nghttp2_hd_inflater *inflater, int seq) { + ssize_t rv; + nghttp2_nv nv; + int inflate_flags; + size_t old_settings_table_size = inflater->settings_hd_table_bufsize_max; + + auto wire = json_object_get(obj, "wire"); + + if (wire == nullptr) { + fprintf(stderr, "'wire' key is missing at %d\n", seq); + return -1; + } + + if (!json_is_string(wire)) { + fprintf(stderr, "'wire' value is not string at %d\n", seq); + return -1; + } + + auto table_size = json_object_get(obj, "header_table_size"); + + if (table_size) { + if (!json_is_integer(table_size)) { + fprintf(stderr, + "The value of 'header_table_size key' is not integer at %d\n", + seq); + return -1; + } + rv = nghttp2_hd_inflate_change_table_size(inflater, + json_integer_value(table_size)); + if (rv != 0) { + fprintf(stderr, + "nghttp2_hd_change_table_size() failed with error %s at %d\n", + nghttp2_strerror(rv), seq); + return -1; + } + } + + auto inputlen = strlen(json_string_value(wire)); + + if (inputlen & 1) { + fprintf(stderr, "Badly formatted output value at %d\n", seq); + exit(EXIT_FAILURE); + } + + auto buflen = inputlen / 2; + auto buf = std::vector(buflen); + + decode_hex(buf.data(), json_string_value(wire), inputlen); + + auto headers = json_array(); + + auto p = buf.data(); + for (;;) { + inflate_flags = 0; + rv = nghttp2_hd_inflate_hd(inflater, &nv, &inflate_flags, p, buflen, 1); + if (rv < 0) { + fprintf(stderr, "inflate failed with error code %zd at %d\n", rv, seq); + exit(EXIT_FAILURE); + } + p += rv; + buflen -= rv; + if (inflate_flags & NGHTTP2_HD_INFLATE_EMIT) { + json_array_append_new( + headers, dump_header(nv.name, nv.namelen, nv.value, nv.valuelen)); + } + if (inflate_flags & NGHTTP2_HD_INFLATE_FINAL) { + break; + } + } + assert(buflen == 0); + nghttp2_hd_inflate_end_headers(inflater); + to_json(inflater, headers, wire, seq, old_settings_table_size); + json_decref(headers); + + return 0; +} + +static int perform(void) { + nghttp2_hd_inflater *inflater = NULL; + json_error_t error; + + auto json = json_loadf(stdin, 0, &error); + + if (json == nullptr) { + fprintf(stderr, "JSON loading failed\n"); + exit(EXIT_FAILURE); + } + + auto cases = json_object_get(json, "cases"); + + if (cases == nullptr) { + fprintf(stderr, "Missing 'cases' key in root object\n"); + exit(EXIT_FAILURE); + } + + if (!json_is_array(cases)) { + fprintf(stderr, "'cases' must be JSON array\n"); + exit(EXIT_FAILURE); + } + + nghttp2_hd_inflate_new(&inflater); + output_json_header(); + auto len = json_array_size(cases); + + for (size_t i = 0; i < len; ++i) { + auto obj = json_array_get(cases, i); + if (!json_is_object(obj)) { + fprintf(stderr, "Unexpected JSON type at %zu. It should be object.\n", i); + continue; + } + if (inflate_hd(obj, inflater, i) != 0) { + continue; + } + if (i + 1 < len) { + printf(",\n"); + } + } + output_json_footer(); + nghttp2_hd_inflate_del(inflater); + json_decref(json); + + return 0; +} + +static void print_help(void) { + std::cout << R"(HPACK HTTP/2 header decoder +Usage: inflatehd [OPTIONS] < INPUT + +Reads JSON data from stdin and outputs inflated name/value pairs in +JSON. + +The root JSON object must contain "context" key, which indicates which +compression context is used. If it is "request", request compression +context is used. Otherwise, response compression context is used. +The value of "cases" key contains the sequence of compressed header +block. They share the same compression context and are processed in +the order they appear. Each item in the sequence is a JSON object and +it must have at least "wire" key. Its value is a string containing +compressed header block in hex string. + +Example: + +{ + "context": "request", + "cases": + [ + { "wire": "0284f77778ff" }, + { "wire": "0185fafd3c3c7f81" } + ] +} + +The output of this program can be used as input for deflatehd. + +OPTIONS: + -d, --dump-header-table + Output dynamic header table.)" << std::endl; + ; +} + +static struct option long_options[] = { + {"dump-header-table", no_argument, nullptr, 'd'}, {nullptr, 0, nullptr, 0}}; + +int main(int argc, char **argv) { + config.dump_header_table = 0; + while (1) { + int option_index = 0; + int c = getopt_long(argc, argv, "dh", long_options, &option_index); + if (c == -1) { + break; + } + switch (c) { + case 'h': + print_help(); + exit(EXIT_SUCCESS); + case 'd': + // --dump-header-table + config.dump_header_table = 1; + break; + case '?': + exit(EXIT_FAILURE); + default: + break; + } + } + perform(); + return 0; +} diff --git a/src/libnghttp2_asio.pc.in b/src/libnghttp2_asio.pc.in new file mode 100644 index 0000000..ec4fbbb --- /dev/null +++ b/src/libnghttp2_asio.pc.in @@ -0,0 +1,33 @@ +# nghttp2 - HTTP/2 C Library + +# Copyright (c) 2014 Tatsuhiro Tsujikawa + +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: + +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. + +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: libnghttp2_asio +Description: HTTP/2 C++ library +URL: https://github.com/tatsuhiro-t/nghttp2 +Version: @VERSION@ +Libs: -L${libdir} -lnghttp2_asio +Cflags: -I${includedir} diff --git a/src/memchunk.h b/src/memchunk.h new file mode 100644 index 0000000..0f12c15 --- /dev/null +++ b/src/memchunk.h @@ -0,0 +1,260 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2014 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef MEMCHUNK_H +#define MEMCHUNK_H + +#include "nghttp2_config.h" + +#include + +#include +#include +#include +#include +#include + +#include "template.h" + +namespace nghttp2 { + +template struct Memchunk { + Memchunk(std::unique_ptr next_chunk) + : pos(std::begin(buf)), last(pos), knext(std::move(next_chunk)), + kprev(nullptr), next(nullptr) { + if (knext) { + knext->kprev = this; + } + } + size_t len() const { return last - pos; } + size_t left() const { return std::end(buf) - last; } + void reset() { pos = last = std::begin(buf); } + std::array buf; + uint8_t *pos, *last; + std::unique_ptr knext; + Memchunk *kprev; + Memchunk *next; + static const size_t size = N; +}; + +template struct Pool { + Pool() : pool(nullptr), freelist(nullptr), poolsize(0) {} + T *get() { + if (freelist) { + auto m = freelist; + freelist = freelist->next; + m->next = nullptr; + m->reset(); + return m; + } + + pool = make_unique(std::move(pool)); + poolsize += T::size; + return pool.get(); + } + void recycle(T *m) { + if (freelist) { + m->next = freelist; + } else { + m->next = nullptr; + } + freelist = m; + } + void shrink(size_t max) { + auto m = freelist; + for (; m && poolsize > max;) { + auto next = m->next; + poolsize -= T::size; + auto p = m->kprev; + if (p) { + p->knext = std::move(m->knext); + if (p->knext) { + p->knext->kprev = p; + } + } else { + pool = std::move(m->knext); + if (pool) { + pool->kprev = nullptr; + } + } + m = next; + } + freelist = m; + } + void clear() { + freelist = nullptr; + pool = nullptr; + poolsize = 0; + } + using value_type = T; + std::unique_ptr pool; + T *freelist; + size_t poolsize; +}; + +template struct Memchunks { + Memchunks(Pool *pool) + : pool(pool), head(nullptr), tail(nullptr), len(0) {} + ~Memchunks() { + if (!pool) { + return; + } + for (auto m = head; m;) { + auto next = m->next; + pool->recycle(m); + m = next; + } + } + size_t append(const void *src, size_t count) { + if (count == 0) { + return 0; + } + + auto first = static_cast(src); + auto last = first + count; + + if (!tail) { + head = tail = pool->get(); + } + + for (;;) { + auto n = std::min(static_cast(last - first), tail->left()); + tail->last = std::copy_n(first, n, tail->last); + first += n; + len += n; + if (first == last) { + break; + } + + tail->next = pool->get(); + tail = tail->next; + } + + return count; + } + template size_t append(const char (&s)[N]) { + return append(s, N - 1); + } + size_t remove(void *dest, size_t count) { + if (!tail || count == 0) { + return 0; + } + + auto first = static_cast(dest); + auto last = first + count; + + auto m = head; + + while (m) { + auto next = m->next; + auto n = std::min(static_cast(last - first), m->len()); + + assert(m->len()); + first = std::copy_n(m->pos, n, first); + m->pos += n; + len -= n; + if (m->len() > 0) { + break; + } + pool->recycle(m); + m = next; + } + head = m; + if (head == nullptr) { + tail = nullptr; + } + + return first - static_cast(dest); + } + size_t drain(size_t count) { + auto ndata = count; + auto m = head; + while (m) { + auto next = m->next; + auto n = std::min(count, m->len()); + m->pos += n; + count -= n; + len -= n; + if (m->len() > 0) { + break; + } + + pool->recycle(m); + m = next; + } + head = m; + if (head == nullptr) { + tail = nullptr; + } + return ndata - count; + } + int riovec(struct iovec *iov, int iovcnt) { + if (!head) { + return 0; + } + auto m = head; + int i; + for (i = 0; i < iovcnt && m; ++i, m = m->next) { + iov[i].iov_base = m->pos; + iov[i].iov_len = m->len(); + } + return i; + } + size_t rleft() const { return len; } + + Pool *pool; + Memchunk *head, *tail; + size_t len; +}; + +using Memchunk16K = Memchunk<16384>; +using MemchunkPool = Pool; +using DefaultMemchunks = Memchunks; + +#define DEFAULT_WR_IOVCNT 16 + +#if defined(IOV_MAX) && IOV_MAX < DEFAULT_WR_IOVCNT +#define MAX_WR_IOVCNT IOV_MAX +#else // !defined(IOV_MAX) || IOV_MAX >= DEFAULT_WR_IOVCNT +#define MAX_WR_IOVCNT DEFAULT_WR_IOVCNT +#endif // !defined(IOV_MAX) || IOV_MAX >= DEFAULT_WR_IOVCNT + +inline int limit_iovec(struct iovec *iov, int iovcnt, size_t max) { + if (max == 0) { + return 0; + } + for (int i = 0; i < iovcnt; ++i) { + auto d = std::min(max, iov[i].iov_len); + iov[i].iov_len = d; + max -= d; + if (max == 0) { + return i + 1; + } + } + return iovcnt; +} + +} // namespace nghttp2 + +#endif // MEMCHUNK_H diff --git a/src/memchunk_test.cc b/src/memchunk_test.cc new file mode 100644 index 0000000..f95f303 --- /dev/null +++ b/src/memchunk_test.cc @@ -0,0 +1,199 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2015 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "memchunk_test.h" + +#include + +#include + +#include "memchunk.h" +#include "util.h" + +namespace nghttp2 { + +void test_pool_recycle(void) { + MemchunkPool pool; + + CU_ASSERT(!pool.pool); + CU_ASSERT(0 == pool.poolsize); + CU_ASSERT(nullptr == pool.freelist); + + auto m1 = pool.get(); + + CU_ASSERT(m1 == pool.pool.get()); + CU_ASSERT(MemchunkPool::value_type::size == pool.poolsize); + CU_ASSERT(nullptr == pool.freelist); + + auto m2 = pool.get(); + + CU_ASSERT(m2 == pool.pool.get()); + CU_ASSERT(2 * MemchunkPool::value_type::size == pool.poolsize); + CU_ASSERT(nullptr == pool.freelist); + CU_ASSERT(m1 == m2->knext.get()); + CU_ASSERT(nullptr == m1->knext.get()); + + auto m3 = pool.get(); + + CU_ASSERT(m3 == pool.pool.get()); + CU_ASSERT(3 * MemchunkPool::value_type::size == pool.poolsize); + CU_ASSERT(nullptr == pool.freelist); + + pool.recycle(m3); + + CU_ASSERT(m3 == pool.pool.get()); + CU_ASSERT(3 * MemchunkPool::value_type::size == pool.poolsize); + CU_ASSERT(m3 == pool.freelist); + + auto m4 = pool.get(); + + CU_ASSERT(m3 == m4); + CU_ASSERT(m4 == pool.pool.get()); + CU_ASSERT(3 * MemchunkPool::value_type::size == pool.poolsize); + CU_ASSERT(nullptr == pool.freelist); + + pool.recycle(m2); + pool.recycle(m1); + + CU_ASSERT(m1 == pool.freelist); + CU_ASSERT(m2 == m1->next); + CU_ASSERT(nullptr == m2->next); +} + +using Memchunk16 = Memchunk<16>; +using MemchunkPool16 = Pool; +using Memchunks16 = Memchunks; + +void test_memchunks_append(void) { + MemchunkPool16 pool; + Memchunks16 chunks(&pool); + + chunks.append("012"); + + auto m = chunks.tail; + + CU_ASSERT(3 == m->len()); + CU_ASSERT(13 == m->left()); + + chunks.append("3456789abcdef@"); + + CU_ASSERT(16 == m->len()); + CU_ASSERT(0 == m->left()); + + m = chunks.tail; + + CU_ASSERT(1 == m->len()); + CU_ASSERT(15 == m->left()); + CU_ASSERT(17 == chunks.rleft()); + + char buf[16]; + size_t nread; + + nread = chunks.remove(buf, 8); + + CU_ASSERT(8 == nread); + CU_ASSERT(0 == memcmp("01234567", buf, nread)); + CU_ASSERT(9 == chunks.rleft()); + + nread = chunks.remove(buf, sizeof(buf)); + + CU_ASSERT(9 == nread); + CU_ASSERT(0 == memcmp("89abcdef@", buf, nread)); + CU_ASSERT(0 == chunks.rleft()); + CU_ASSERT(nullptr == chunks.head); + CU_ASSERT(nullptr == chunks.tail); + CU_ASSERT(32 == pool.poolsize); +} + +void test_memchunks_drain(void) { + MemchunkPool16 pool; + Memchunks16 chunks(&pool); + + chunks.append("0123456789"); + + size_t nread; + + nread = chunks.drain(3); + + CU_ASSERT(3 == nread); + + char buf[16]; + + nread = chunks.remove(buf, sizeof(buf)); + + CU_ASSERT(7 == nread); + CU_ASSERT(0 == memcmp("3456789", buf, nread)); +} + +void test_memchunks_riovec(void) { + MemchunkPool16 pool; + Memchunks16 chunks(&pool); + + char buf[3 * 16]; + + chunks.append(buf, sizeof(buf)); + + std::array iov; + auto iovcnt = chunks.riovec(iov.data(), iov.size()); + + auto m = chunks.head; + + CU_ASSERT(2 == iovcnt); + CU_ASSERT(m->buf.data() == iov[0].iov_base); + CU_ASSERT(m->len() == iov[0].iov_len); + + m = m->next; + + CU_ASSERT(m->buf.data() == iov[1].iov_base); + CU_ASSERT(m->len() == iov[1].iov_len); + + chunks.drain(2 * 16); + + iovcnt = chunks.riovec(iov.data(), iov.size()); + + CU_ASSERT(1 == iovcnt); + + m = chunks.head; + CU_ASSERT(m->buf.data() == iov[0].iov_base); + CU_ASSERT(m->len() == iov[0].iov_len); +} + +void test_memchunks_recycle(void) { + MemchunkPool16 pool; + { + Memchunks16 chunks(&pool); + char buf[32]; + chunks.append(buf, sizeof(buf)); + } + CU_ASSERT(32 == pool.poolsize); + CU_ASSERT(nullptr != pool.freelist); + + auto m = pool.freelist; + m = m->next; + + CU_ASSERT(nullptr != m); + CU_ASSERT(nullptr == m->next); +} + +} // namespace nghttp2 diff --git a/src/memchunk_test.h b/src/memchunk_test.h new file mode 100644 index 0000000..9d170a4 --- /dev/null +++ b/src/memchunk_test.h @@ -0,0 +1,42 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2015 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef MEMCHUNK_TEST_H +#define MEMCHUNK_TEST_H + +#ifdef HAVE_CONFIG_H +#include +#endif // HAVE_CONFIG_H + +namespace nghttp2 { + +void test_pool_recycle(void); +void test_memchunks_append(void); +void test_memchunks_drain(void); +void test_memchunks_riovec(void); +void test_memchunks_recycle(void); + +} // namespace nghttp2 + +#endif // MEMCHUNK_TEST_H diff --git a/src/nghttp.cc b/src/nghttp.cc new file mode 100644 index 0000000..8f6361e --- /dev/null +++ b/src/nghttp.cc @@ -0,0 +1,2652 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2013 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "nghttp.h" + +#include +#ifdef HAVE_UNISTD_H +#include +#endif // HAVE_UNISTD_H +#ifdef HAVE_FCNTL_H +#include +#endif // HAVE_FCNTL_H +#ifdef HAVE_NETINET_IN_H +#include +#endif // HAVE_NETINET_IN_H +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#ifdef HAVE_JANSSON +#include +#endif // HAVE_JANSSON + +#include "app_helper.h" +#include "HtmlParser.h" +#include "util.h" +#include "base64.h" +#include "ssl.h" +#include "template.h" + +#ifndef O_BINARY +#define O_BINARY (0) +#endif // O_BINARY + +namespace nghttp2 { + +// The anchor stream nodes when --no-dep is not used. The stream ID = +// 1 is excluded since it is used as first stream in upgrade case. We +// follows the same dependency anchor nodes as Firefox does. +struct Anchor { + int32_t stream_id; + // stream ID this anchor depends on + int32_t dep_stream_id; + // .. with this weight. + int32_t weight; +}; + +// This is index into anchors. Firefox uses ANCHOR_FOLLOWERS for html +// file. +enum { + ANCHOR_LEADERS, + ANCHOR_UNBLOCKED, + ANCHOR_BACKGROUND, + ANCHOR_SPECULATIVE, + ANCHOR_FOLLOWERS, +}; + +namespace { +auto anchors = std::array{{ + {3, 0, 201}, {5, 0, 101}, {7, 0, 1}, {9, 7, 1}, {11, 3, 1}, +}}; +} // namespace + +Config::Config() + : output_upper_thres(1024 * 1024), padding(0), + peer_max_concurrent_streams(NGHTTP2_INITIAL_MAX_CONCURRENT_STREAMS), + header_table_size(-1), weight(NGHTTP2_DEFAULT_WEIGHT), multiply(1), + timeout(0.), window_bits(-1), connection_window_bits(-1), verbose(0), + null_out(false), remote_name(false), get_assets(false), stat(false), + upgrade(false), continuation(false), no_content_length(false), + no_dep(false), hexdump(false), no_push(false) { + nghttp2_option_new(&http2_option); + nghttp2_option_set_peer_max_concurrent_streams(http2_option, + peer_max_concurrent_streams); +} + +Config::~Config() { nghttp2_option_del(http2_option); } + +namespace { +Config config; +} // namespace + +namespace { +void print_protocol_nego_error() { + std::cerr << "[ERROR] HTTP/2 protocol was not selected." + << " (nghttp2 expects " << NGHTTP2_PROTO_VERSION_ID << ")" + << std::endl; +} +} // namespace + +namespace { +std::string strip_fragment(const char *raw_uri) { + const char *end; + for (end = raw_uri; *end && *end != '#'; ++end) + ; + size_t len = end - raw_uri; + return std::string(raw_uri, len); +} +} // namespace + +Request::Request(const std::string &uri, const http_parser_url &u, + const nghttp2_data_provider *data_prd, int64_t data_length, + const nghttp2_priority_spec &pri_spec, int level) + : uri(uri), u(u), pri_spec(pri_spec), data_length(data_length), + data_offset(0), response_len(0), inflater(nullptr), html_parser(nullptr), + data_prd(data_prd), stream_id(-1), status(0), level(level), + expect_final_response(false) { + http2::init_hdidx(res_hdidx); + http2::init_hdidx(req_hdidx); +} + +Request::~Request() { + nghttp2_gzip_inflate_del(inflater); + delete html_parser; +} + +void Request::init_inflater() { + int rv; + rv = nghttp2_gzip_inflate_new(&inflater); + assert(rv == 0); +} + +void Request::init_html_parser() { html_parser = new HtmlParser(uri); } + +int Request::update_html_parser(const uint8_t *data, size_t len, int fin) { + if (!html_parser) { + return 0; + } + return html_parser->parse_chunk(reinterpret_cast(data), len, + fin); +} + +std::string Request::make_reqpath() const { + std::string path = util::has_uri_field(u, UF_PATH) + ? util::get_uri_field(uri.c_str(), u, UF_PATH) + : "/"; + if (util::has_uri_field(u, UF_QUERY)) { + path += "?"; + path.append(uri.c_str() + u.field_data[UF_QUERY].off, + u.field_data[UF_QUERY].len); + } + return path; +} + +namespace { +nghttp2_priority_spec resolve_dep(int res_type) { + nghttp2_priority_spec pri_spec; + + if (config.no_dep) { + nghttp2_priority_spec_default_init(&pri_spec); + + return pri_spec; + } + + int32_t anchor_id; + int32_t weight; + switch (res_type) { + case REQ_CSS: + case REQ_JS: + anchor_id = anchors[ANCHOR_LEADERS].stream_id; + weight = 2; + break; + case REQ_UNBLOCK_JS: + anchor_id = anchors[ANCHOR_UNBLOCKED].stream_id; + weight = 2; + break; + case REQ_IMG: + anchor_id = anchors[ANCHOR_FOLLOWERS].stream_id; + weight = 12; + break; + default: + anchor_id = anchors[ANCHOR_FOLLOWERS].stream_id; + weight = 2; + } + + nghttp2_priority_spec_init(&pri_spec, anchor_id, weight, 0); + return pri_spec; +} +} // namespace + +bool Request::is_ipv6_literal_addr() const { + if (util::has_uri_field(u, UF_HOST)) { + return memchr(uri.c_str() + u.field_data[UF_HOST].off, ':', + u.field_data[UF_HOST].len); + } else { + return false; + } +} + +bool Request::response_pseudo_header_allowed(int16_t token) const { + if (!res_nva.empty() && res_nva.back().name.c_str()[0] != ':') { + return false; + } + switch (token) { + case http2::HD__STATUS: + return res_hdidx[token] == -1; + default: + return false; + } +} + +bool Request::push_request_pseudo_header_allowed(int16_t token) const { + if (!req_nva.empty() && req_nva.back().name.c_str()[0] != ':') { + return false; + } + switch (token) { + case http2::HD__AUTHORITY: + case http2::HD__METHOD: + case http2::HD__PATH: + case http2::HD__SCHEME: + return req_hdidx[token] == -1; + default: + return false; + } +} + +Headers::value_type *Request::get_res_header(int16_t token) { + auto idx = res_hdidx[token]; + if (idx == -1) { + return nullptr; + } + return &res_nva[idx]; +} + +Headers::value_type *Request::get_req_header(int16_t token) { + auto idx = req_hdidx[token]; + if (idx == -1) { + return nullptr; + } + return &req_nva[idx]; +} + +void Request::record_request_start_time() { + timing.state = RequestState::ON_REQUEST; + timing.request_start_time = get_time(); +} + +void Request::record_response_start_time() { + timing.state = RequestState::ON_RESPONSE; + timing.response_start_time = get_time(); +} + +void Request::record_response_end_time() { + timing.state = RequestState::ON_COMPLETE; + timing.response_end_time = get_time(); +} + +namespace { +int htp_msg_begincb(http_parser *htp) { + if (config.verbose) { + print_timer(); + std::cout << " HTTP Upgrade response" << std::endl; + } + return 0; +} +} // namespace + +namespace { +int htp_statuscb(http_parser *htp, const char *at, size_t length) { + auto client = static_cast(htp->data); + client->upgrade_response_status_code = htp->status_code; + return 0; +} +} // namespace + +namespace { +int htp_msg_completecb(http_parser *htp) { + auto client = static_cast(htp->data); + client->upgrade_response_complete = true; + return 0; +} +} // namespace + +namespace { +http_parser_settings htp_hooks = { + htp_msg_begincb, // http_cb on_message_begin; + nullptr, // http_data_cb on_url; + htp_statuscb, // http_data_cb on_status; + nullptr, // http_data_cb on_header_field; + nullptr, // http_data_cb on_header_value; + nullptr, // http_cb on_headers_complete; + nullptr, // http_data_cb on_body; + htp_msg_completecb // http_cb on_message_complete; +}; +} // namespace + +namespace { +int submit_request(HttpClient *client, const Headers &headers, Request *req) { + auto path = req->make_reqpath(); + auto scheme = util::get_uri_field(req->uri.c_str(), req->u, UF_SCHEMA); + auto build_headers = Headers{{":method", req->data_prd ? "POST" : "GET"}, + {":path", path}, + {":scheme", scheme}, + {":authority", client->hostport}, + {"accept", "*/*"}, + {"accept-encoding", "gzip, deflate"}, + {"user-agent", "nghttp2/" NGHTTP2_VERSION}}; + if (config.continuation) { + for (size_t i = 0; i < 6; ++i) { + build_headers.emplace_back("continuation-test-" + util::utos(i + 1), + std::string(4096, '-')); + } + } + auto num_initial_headers = build_headers.size(); + if (!config.no_content_length && req->data_prd) { + build_headers.emplace_back("content-length", util::utos(req->data_length)); + } + for (auto &kv : headers) { + size_t i; + for (i = 0; i < num_initial_headers; ++i) { + if (kv.name == build_headers[i].name) { + build_headers[i].value = kv.value; + break; + } + } + if (i < num_initial_headers) { + continue; + } + + build_headers.emplace_back(kv.name, kv.value, kv.no_index); + } + + auto nva = std::vector(); + nva.reserve(build_headers.size()); + + for (auto &kv : build_headers) { + nva.push_back(http2::make_nv(kv.name, kv.value, kv.no_index)); + } + + std::string trailer_names; + if (!config.trailer.empty()) { + trailer_names = config.trailer[0].name; + for (size_t i = 1; i < config.trailer.size(); ++i) { + trailer_names += ", "; + trailer_names += config.trailer[i].name; + } + nva.push_back(http2::make_nv_ls("trailer", trailer_names)); + } + + auto stream_id = + nghttp2_submit_request(client->session, &req->pri_spec, nva.data(), + nva.size(), req->data_prd, req); + if (stream_id < 0) { + std::cerr << "[ERROR] nghttp2_submit_request() returned error: " + << nghttp2_strerror(stream_id) << std::endl; + return -1; + } + + req->stream_id = stream_id; + client->request_done(req); + + req->req_nva = std::move(build_headers); + + return 0; +} +} // namespace + +namespace { +void readcb(struct ev_loop *loop, ev_io *w, int revents) { + auto client = static_cast(w->data); + if (client->do_read() != 0) { + client->disconnect(); + } +} +} // namespace + +namespace { +void writecb(struct ev_loop *loop, ev_io *w, int revents) { + auto client = static_cast(w->data); + auto rv = client->do_write(); + if (rv == HttpClient::ERR_CONNECT_FAIL) { + client->connect_fail(); + return; + } + if (rv != 0) { + client->disconnect(); + } +} +} // namespace + +namespace { +void timeoutcb(struct ev_loop *loop, ev_timer *w, int revents) { + auto client = static_cast(w->data); + std::cerr << "[ERROR] Timeout" << std::endl; + client->disconnect(); +} +} // namespace + +namespace { +void settings_timeout_cb(struct ev_loop *loop, ev_timer *w, int revents) { + auto client = static_cast(w->data); + ev_timer_stop(loop, w); + + nghttp2_session_terminate_session(client->session, NGHTTP2_SETTINGS_TIMEOUT); + + client->signal_write(); +} +} // namespace + +HttpClient::HttpClient(const nghttp2_session_callbacks *callbacks, + struct ev_loop *loop, SSL_CTX *ssl_ctx) + : session(nullptr), callbacks(callbacks), loop(loop), ssl_ctx(ssl_ctx), + ssl(nullptr), addrs(nullptr), next_addr(nullptr), cur_addr(nullptr), + complete(0), success(0), settings_payloadlen(0), state(ClientState::IDLE), + upgrade_response_status_code(0), fd(-1), + upgrade_response_complete(false) { + ev_io_init(&wev, writecb, 0, EV_WRITE); + ev_io_init(&rev, readcb, 0, EV_READ); + + wev.data = this; + rev.data = this; + + ev_timer_init(&wt, timeoutcb, 0., config.timeout); + ev_timer_init(&rt, timeoutcb, 0., config.timeout); + + wt.data = this; + rt.data = this; + + ev_timer_init(&settings_timer, settings_timeout_cb, 0., 10.); + + settings_timer.data = this; +} + +HttpClient::~HttpClient() { + disconnect(); + + if (addrs) { + freeaddrinfo(addrs); + addrs = nullptr; + next_addr = nullptr; + } +} + +bool HttpClient::need_upgrade() const { + return config.upgrade && scheme == "http"; +} + +int HttpClient::resolve_host(const std::string &host, uint16_t port) { + int rv; + addrinfo hints; + this->host = host; + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = 0; + hints.ai_flags = AI_ADDRCONFIG; + rv = getaddrinfo(host.c_str(), util::utos(port).c_str(), &hints, &addrs); + if (rv != 0) { + std::cerr << "[ERROR] getaddrinfo() failed: " << gai_strerror(rv) + << std::endl; + return -1; + } + if (addrs == nullptr) { + std::cerr << "[ERROR] No address returned" << std::endl; + return -1; + } + next_addr = addrs; + return 0; +} + +int HttpClient::initiate_connection() { + int rv; + + cur_addr = nullptr; + while (next_addr) { + cur_addr = next_addr; + next_addr = next_addr->ai_next; + fd = util::create_nonblock_socket(cur_addr->ai_family); + if (fd == -1) { + continue; + } + + if (ssl_ctx) { + // We are establishing TLS connection. + ssl = SSL_new(ssl_ctx); + if (!ssl) { + std::cerr << "[ERROR] SSL_new() failed: " + << ERR_error_string(ERR_get_error(), nullptr) << std::endl; + return -1; + } + + SSL_set_fd(ssl, fd); + SSL_set_connect_state(ssl); + + // If the user overrode the :authority or host header, use that + // value for the SNI extension + const char *host_string = nullptr; + auto i = std::find_if(std::begin(config.headers), + std::end(config.headers), [](const Header &nv) { + return ":authority" == nv.name || "host" == nv.name; + }); + if (i != std::end(config.headers)) { + host_string = (*i).value.c_str(); + } else { + host_string = host.c_str(); + } + + if (!util::numeric_host(host_string)) { + SSL_set_tlsext_host_name(ssl, host_string); + } + } + + rv = connect(fd, cur_addr->ai_addr, cur_addr->ai_addrlen); + + if (rv != 0 && errno != EINPROGRESS) { + if (ssl) { + SSL_free(ssl); + ssl = nullptr; + } + close(fd); + fd = -1; + continue; + } + break; + } + + if (fd == -1) { + return -1; + } + + writefn = &HttpClient::connected; + + if (need_upgrade()) { + on_readfn = &HttpClient::on_upgrade_read; + on_writefn = &HttpClient::on_upgrade_connect; + } else { + on_readfn = &HttpClient::on_read; + on_writefn = &HttpClient::on_write; + } + + ev_io_set(&rev, fd, EV_READ); + ev_io_set(&wev, fd, EV_WRITE); + + ev_io_start(loop, &wev); + + ev_timer_again(loop, &wt); + + return 0; +} + +void HttpClient::disconnect() { + state = ClientState::IDLE; + + ev_timer_stop(loop, &settings_timer); + + ev_timer_stop(loop, &rt); + ev_timer_stop(loop, &wt); + + ev_io_stop(loop, &rev); + ev_io_stop(loop, &wev); + + nghttp2_session_del(session); + session = nullptr; + + if (ssl) { + SSL_set_shutdown(ssl, SSL_RECEIVED_SHUTDOWN); + ERR_clear_error(); + SSL_shutdown(ssl); + SSL_free(ssl); + ssl = nullptr; + } + + if (fd != -1) { + shutdown(fd, SHUT_WR); + close(fd); + fd = -1; + } +} + +int HttpClient::read_clear() { + ev_timer_again(loop, &rt); + + std::array buf; + + for (;;) { + ssize_t nread; + while ((nread = read(fd, buf.data(), buf.size())) == -1 && errno == EINTR) + ; + if (nread == -1) { + if (errno == EAGAIN || errno == EWOULDBLOCK) { + return 0; + } + return -1; + } + + if (nread == 0) { + return -1; + } + + if (on_readfn(*this, buf.data(), nread) != 0) { + return -1; + } + } + + return 0; +} + +int HttpClient::write_clear() { + ev_timer_again(loop, &rt); + + for (;;) { + if (wb.rleft() > 0) { + ssize_t nwrite; + while ((nwrite = write(fd, wb.pos, wb.rleft())) == -1 && errno == EINTR) + ; + if (nwrite == -1) { + if (errno == EAGAIN || errno == EWOULDBLOCK) { + ev_io_start(loop, &wev); + ev_timer_again(loop, &wt); + return 0; + } + return -1; + } + wb.drain(nwrite); + continue; + } + wb.reset(); + if (on_writefn(*this) != 0) { + return -1; + } + if (wb.rleft() == 0) { + break; + } + } + + ev_io_stop(loop, &wev); + ev_timer_stop(loop, &wt); + + return 0; +} + +int HttpClient::noop() { return 0; } + +void HttpClient::connect_fail() { + if (state == ClientState::IDLE) { + std::cerr << "[ERROR] Could not connect to the address " + << util::numeric_name(cur_addr->ai_addr, cur_addr->ai_addrlen) + << std::endl; + } + auto cur_state = state; + disconnect(); + if (cur_state == ClientState::IDLE) { + if (initiate_connection() == 0) { + std::cerr << "Trying next address " + << util::numeric_name(cur_addr->ai_addr, cur_addr->ai_addrlen) + << std::endl; + } + } +} + +int HttpClient::connected() { + if (!util::check_socket_connected(fd)) { + return ERR_CONNECT_FAIL; + } + + if (config.verbose) { + print_timer(); + std::cout << " Connected" << std::endl; + } + + state = ClientState::CONNECTED; + + ev_io_start(loop, &rev); + ev_io_stop(loop, &wev); + + ev_timer_again(loop, &rt); + ev_timer_stop(loop, &wt); + + if (ssl) { + readfn = &HttpClient::tls_handshake; + writefn = &HttpClient::tls_handshake; + + return do_write(); + } + + readfn = &HttpClient::read_clear; + writefn = &HttpClient::write_clear; + + if (need_upgrade()) { + htp = make_unique(); + http_parser_init(htp.get(), HTTP_RESPONSE); + htp->data = this; + + return do_write(); + } + + if (connection_made() != 0) { + return -1; + } + + return 0; +} + +namespace { +size_t populate_settings(nghttp2_settings_entry *iv) { + size_t niv = 2; + + iv[0].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS; + iv[0].value = 100; + + iv[1].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE; + if (config.window_bits != -1) { + iv[1].value = (1 << config.window_bits) - 1; + } else { + iv[1].value = NGHTTP2_INITIAL_WINDOW_SIZE; + } + + if (config.header_table_size >= 0) { + iv[niv].settings_id = NGHTTP2_SETTINGS_HEADER_TABLE_SIZE; + iv[niv].value = config.header_table_size; + ++niv; + } + + if (config.no_push) { + iv[niv].settings_id = NGHTTP2_SETTINGS_ENABLE_PUSH; + iv[niv].value = 0; + ++niv; + } + + return niv; +} +} // namespace + +int HttpClient::on_upgrade_connect() { + ssize_t rv; + record_connect_end_time(); + assert(!reqvec.empty()); + std::array iv; + size_t niv = populate_settings(iv.data()); + assert(settings_payload.size() >= 8 * niv); + rv = nghttp2_pack_settings_payload(settings_payload.data(), + settings_payload.size(), iv.data(), niv); + if (rv < 0) { + return -1; + } + settings_payloadlen = rv; + auto token68 = + base64::encode(std::begin(settings_payload), + std::begin(settings_payload) + settings_payloadlen); + util::to_token68(token68); + std::string req; + if (reqvec[0]->data_prd) { + // If the request contains upload data, use OPTIONS * to upgrade + req = "OPTIONS *"; + } else { + req = "GET "; + req += reqvec[0]->make_reqpath(); + } + + auto headers = Headers{{"Host", hostport}, + {"Connection", "Upgrade, HTTP2-Settings"}, + {"Upgrade", NGHTTP2_CLEARTEXT_PROTO_VERSION_ID}, + {"HTTP2-Settings", token68}, + {"Accept", "*/*"}, + {"User-Agent", "nghttp2/" NGHTTP2_VERSION}}; + auto initial_headerslen = headers.size(); + + for (auto &kv : config.headers) { + size_t i; + for (i = 0; i < initial_headerslen; ++i) { + if (util::strieq(kv.name, headers[i].name)) { + headers[i].value = kv.value; + break; + } + } + if (i < initial_headerslen) { + continue; + } + if (kv.name.size() != 0 && kv.name[0] != ':') { + headers.emplace_back(kv.name, kv.value, kv.no_index); + } + } + + req += " HTTP/1.1\r\n"; + + for (auto &kv : headers) { + req += kv.name; + req += ": "; + req += kv.value; + req += "\r\n"; + } + req += "\r\n"; + + wb.write(req.c_str(), req.size()); + + if (config.verbose) { + print_timer(); + std::cout << " HTTP Upgrade request\n" << req << std::endl; + } + + // record request time if this is GET request + if (!reqvec[0]->data_prd) { + reqvec[0]->record_request_start_time(); + } + + on_writefn = &HttpClient::noop; + + signal_write(); + + return 0; +} + +int HttpClient::on_upgrade_read(const uint8_t *data, size_t len) { + int rv; + + auto nread = http_parser_execute(htp.get(), &htp_hooks, + reinterpret_cast(data), len); + + if (config.verbose) { + std::cout.write(reinterpret_cast(data), nread); + } + + auto htperr = HTTP_PARSER_ERRNO(htp.get()); + + if (htperr != HPE_OK) { + std::cerr << "[ERROR] Failed to parse HTTP Upgrade response header: " + << "(" << http_errno_name(htperr) << ") " + << http_errno_description(htperr) << std::endl; + return -1; + } + + if (!upgrade_response_complete) { + return 0; + } + + if (config.verbose) { + std::cout << std::endl; + } + + if (upgrade_response_status_code != 101) { + std::cerr << "[ERROR] HTTP Upgrade failed" << std::endl; + + return -1; + } + + if (config.verbose) { + print_timer(); + std::cout << " HTTP Upgrade success" << std::endl; + } + + on_readfn = &HttpClient::on_read; + on_writefn = &HttpClient::on_write; + + rv = connection_made(); + if (rv != 0) { + return rv; + } + + // Read remaining data in the buffer because it is not notified + // callback anymore. + rv = on_readfn(*this, data + nread, len - nread); + if (rv != 0) { + return rv; + } + + return 0; +} + +int HttpClient::do_read() { return readfn(*this); } +int HttpClient::do_write() { return writefn(*this); } + +int HttpClient::connection_made() { + int rv; + + if (!need_upgrade()) { + record_connect_end_time(); + } + + if (ssl) { + // Check NPN or ALPN result + const unsigned char *next_proto = nullptr; + unsigned int next_proto_len; + SSL_get0_next_proto_negotiated(ssl, &next_proto, &next_proto_len); + for (int i = 0; i < 2; ++i) { + if (next_proto) { + if (config.verbose) { + std::cout << "The negotiated protocol: "; + std::cout.write(reinterpret_cast(next_proto), + next_proto_len); + std::cout << std::endl; + } + if (!util::check_h2_is_selected(next_proto, next_proto_len)) { + next_proto = nullptr; + } + break; + } +#if OPENSSL_VERSION_NUMBER >= 0x10002000L + SSL_get0_alpn_selected(ssl, &next_proto, &next_proto_len); +#else // OPENSSL_VERSION_NUMBER < 0x10002000L + break; +#endif // OPENSSL_VERSION_NUMBER < 0x10002000L + } + if (!next_proto) { + print_protocol_nego_error(); + return -1; + } + } + + rv = nghttp2_session_client_new2(&session, callbacks, this, + config.http2_option); + + if (rv != 0) { + return -1; + } + if (need_upgrade()) { + // Adjust stream user-data depending on the existence of upload + // data + Request *stream_user_data = nullptr; + if (!reqvec[0]->data_prd) { + stream_user_data = reqvec[0].get(); + } + rv = nghttp2_session_upgrade(session, settings_payload.data(), + settings_payloadlen, stream_user_data); + if (rv != 0) { + std::cerr << "[ERROR] nghttp2_session_upgrade() returned error: " + << nghttp2_strerror(rv) << std::endl; + return -1; + } + if (stream_user_data) { + stream_user_data->stream_id = 1; + request_done(stream_user_data); + } + } + // If upgrade succeeds, the SETTINGS value sent with + // HTTP2-Settings header field has already been submitted to + // session object. + if (!need_upgrade()) { + std::array iv; + auto niv = populate_settings(iv.data()); + rv = nghttp2_submit_settings(session, NGHTTP2_FLAG_NONE, iv.data(), niv); + if (rv != 0) { + return -1; + } + } + if (!config.no_dep) { + // Create anchor stream nodes + nghttp2_priority_spec pri_spec; + + for (auto &anchor : anchors) { + nghttp2_priority_spec_init(&pri_spec, anchor.dep_stream_id, anchor.weight, + 0); + rv = nghttp2_submit_priority(session, NGHTTP2_FLAG_NONE, anchor.stream_id, + &pri_spec); + if (rv != 0) { + return -1; + } + } + + rv = nghttp2_session_set_next_stream_id( + session, anchors[ANCHOR_FOLLOWERS].stream_id + 2); + if (rv != 0) { + return -1; + } + + if (need_upgrade()) { + // Amend the priority because we cannot send priority in + // HTTP/1.1 Upgrade. + auto &anchor = anchors[ANCHOR_FOLLOWERS]; + nghttp2_priority_spec_init(&pri_spec, anchor.stream_id, config.weight, 0); + + rv = nghttp2_submit_priority(session, NGHTTP2_FLAG_NONE, 1, &pri_spec); + if (rv != 0) { + return -1; + } + } + } else if (need_upgrade() && config.weight != NGHTTP2_DEFAULT_WEIGHT) { + // Amend the priority because we cannot send priority in + // HTTP/1.1 Upgrade. + nghttp2_priority_spec pri_spec; + + nghttp2_priority_spec_init(&pri_spec, 0, config.weight, 0); + + rv = nghttp2_submit_priority(session, NGHTTP2_FLAG_NONE, 1, &pri_spec); + if (rv != 0) { + return -1; + } + } + + ev_timer_again(loop, &settings_timer); + + if (config.connection_window_bits != -1) { + int32_t wininc = (1 << config.connection_window_bits) - 1 - + NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE; + rv = nghttp2_submit_window_update(session, NGHTTP2_FLAG_NONE, 0, wininc); + if (rv != 0) { + return -1; + } + } + // Adjust first request depending on the existence of the upload + // data + for (auto i = std::begin(reqvec) + (need_upgrade() && !reqvec[0]->data_prd); + i != std::end(reqvec); ++i) { + if (submit_request(this, config.headers, (*i).get()) != 0) { + return -1; + } + } + + signal_write(); + + return 0; +} + +int HttpClient::on_read(const uint8_t *data, size_t len) { + if (config.hexdump) { + util::hexdump(stdout, data, len); + } + + auto rv = nghttp2_session_mem_recv(session, data, len); + if (rv < 0) { + std::cerr << "[ERROR] nghttp2_session_mem_recv() returned error: " + << nghttp2_strerror(rv) << std::endl; + return -1; + } + + assert(static_cast(rv) == len); + + if (nghttp2_session_want_read(session) == 0 && + nghttp2_session_want_write(session) == 0 && wb.rleft() == 0) { + return -1; + } + + signal_write(); + + return 0; +} + +int HttpClient::on_write() { + auto rv = nghttp2_session_send(session); + if (rv != 0) { + std::cerr << "[ERROR] nghttp2_session_send() returned error: " + << nghttp2_strerror(rv) << std::endl; + return -1; + } + + if (nghttp2_session_want_read(session) == 0 && + nghttp2_session_want_write(session) == 0 && wb.rleft() == 0) { + return -1; + } + + return 0; +} + +int HttpClient::tls_handshake() { + ev_timer_again(loop, &rt); + + ERR_clear_error(); + + auto rv = SSL_do_handshake(ssl); + + if (rv == 0) { + return -1; + } + + if (rv < 0) { + auto err = SSL_get_error(ssl, rv); + switch (err) { + case SSL_ERROR_WANT_READ: + ev_io_stop(loop, &wev); + ev_timer_stop(loop, &wt); + return 0; + case SSL_ERROR_WANT_WRITE: + ev_io_start(loop, &wev); + ev_timer_again(loop, &wt); + return 0; + default: + return -1; + } + } + + ev_io_stop(loop, &wev); + ev_timer_stop(loop, &wt); + + readfn = &HttpClient::read_tls; + writefn = &HttpClient::write_tls; + + if (connection_made() != 0) { + return -1; + } + + return 0; +} + +int HttpClient::read_tls() { + ev_timer_again(loop, &rt); + + ERR_clear_error(); + + std::array buf; + for (;;) { + auto rv = SSL_read(ssl, buf.data(), buf.size()); + + if (rv == 0) { + return -1; + } + + if (rv < 0) { + auto err = SSL_get_error(ssl, rv); + switch (err) { + case SSL_ERROR_WANT_READ: + return 0; + case SSL_ERROR_WANT_WRITE: + // renegotiation started + return -1; + default: + return -1; + } + } + + if (on_readfn(*this, buf.data(), rv) != 0) { + return -1; + } + } +} + +int HttpClient::write_tls() { + ev_timer_again(loop, &rt); + + ERR_clear_error(); + + for (;;) { + if (wb.rleft() > 0) { + auto rv = SSL_write(ssl, wb.pos, wb.rleft()); + + if (rv == 0) { + return -1; + } + + if (rv < 0) { + auto err = SSL_get_error(ssl, rv); + switch (err) { + case SSL_ERROR_WANT_READ: + // renegotiation started + return -1; + case SSL_ERROR_WANT_WRITE: + ev_io_start(loop, &wev); + ev_timer_again(loop, &wt); + return 0; + default: + return -1; + } + } + + wb.drain(rv); + + continue; + } + wb.reset(); + if (on_writefn(*this) != 0) { + return -1; + } + if (wb.rleft() == 0) { + break; + } + } + + ev_io_stop(loop, &wev); + ev_timer_stop(loop, &wt); + + return 0; +} + +void HttpClient::signal_write() { ev_io_start(loop, &wev); } + +bool HttpClient::all_requests_processed() const { + return complete == reqvec.size(); +} + +void HttpClient::update_hostport() { + if (reqvec.empty()) { + return; + } + scheme = util::get_uri_field(reqvec[0]->uri.c_str(), reqvec[0]->u, UF_SCHEMA); + std::stringstream ss; + if (reqvec[0]->is_ipv6_literal_addr()) { + ss << "["; + util::write_uri_field(ss, reqvec[0]->uri.c_str(), reqvec[0]->u, UF_HOST); + ss << "]"; + } else { + util::write_uri_field(ss, reqvec[0]->uri.c_str(), reqvec[0]->u, UF_HOST); + } + if (util::has_uri_field(reqvec[0]->u, UF_PORT) && + reqvec[0]->u.port != + util::get_default_port(reqvec[0]->uri.c_str(), reqvec[0]->u)) { + ss << ":" << reqvec[0]->u.port; + } + hostport = ss.str(); +} + +bool HttpClient::add_request(const std::string &uri, + const nghttp2_data_provider *data_prd, + int64_t data_length, + const nghttp2_priority_spec &pri_spec, int level) { + http_parser_url u; + memset(&u, 0, sizeof(u)); + if (http_parser_parse_url(uri.c_str(), uri.size(), 0, &u) != 0) { + return false; + } + if (path_cache.count(uri)) { + return false; + } + + if (config.multiply == 1) { + path_cache.insert(uri); + } + + reqvec.push_back( + make_unique(uri, u, data_prd, data_length, pri_spec, level)); + return true; +} + +void HttpClient::record_start_time() { + timing.system_start_time = std::chrono::system_clock::now(); + timing.start_time = get_time(); +} + +void HttpClient::record_domain_lookup_end_time() { + timing.domain_lookup_end_time = get_time(); +} + +void HttpClient::record_connect_end_time() { + timing.connect_end_time = get_time(); +} + +void HttpClient::request_done(Request *req) { + if (req->stream_id % 2 == 0) { + return; + } +} + +#ifdef HAVE_JANSSON +void HttpClient::output_har(FILE *outfile) { + static auto PAGE_ID = "page_0"; + + auto root = json_object(); + auto log = json_object(); + json_object_set_new(root, "log", log); + json_object_set_new(log, "version", json_string("1.2")); + + auto creator = json_object(); + json_object_set_new(log, "creator", creator); + + json_object_set_new(creator, "name", json_string("nghttp")); + json_object_set_new(creator, "version", json_string(NGHTTP2_VERSION)); + + auto pages = json_array(); + json_object_set_new(log, "pages", pages); + + auto page = json_object(); + json_array_append_new(pages, page); + + json_object_set_new( + page, "startedDateTime", + json_string(util::format_iso8601(timing.system_start_time).c_str())); + json_object_set_new(page, "id", json_string(PAGE_ID)); + json_object_set_new(page, "title", json_string("")); + + json_object_set_new(page, "pageTimings", json_object()); + + auto entries = json_array(); + json_object_set_new(log, "entries", entries); + + auto dns_delta = + std::chrono::duration_cast( + timing.domain_lookup_end_time - timing.start_time).count() / + 1000.0; + auto connect_delta = + std::chrono::duration_cast( + timing.connect_end_time - timing.domain_lookup_end_time).count() / + 1000.0; + + for (size_t i = 0; i < reqvec.size(); ++i) { + auto &req = reqvec[i]; + + if (req->timing.state != RequestState::ON_COMPLETE) { + continue; + } + + auto entry = json_object(); + json_array_append_new(entries, entry); + + auto &req_timing = req->timing; + auto request_time = + (i == 0) ? timing.system_start_time + : timing.system_start_time + + std::chrono::duration_cast< + std::chrono::system_clock::duration>( + req_timing.request_start_time - timing.start_time); + + auto wait_delta = std::chrono::duration_cast( + req_timing.response_start_time - + req_timing.request_start_time).count() / + 1000.0; + auto receive_delta = std::chrono::duration_cast( + req_timing.response_end_time - + req_timing.response_start_time).count() / + 1000.0; + + auto time_sum = + std::chrono::duration_cast( + (i == 0) ? (req_timing.response_end_time - timing.start_time) + : (req_timing.response_end_time - + req_timing.request_start_time)).count() / + 1000.0; + + json_object_set_new( + entry, "startedDateTime", + json_string(util::format_iso8601(request_time).c_str())); + json_object_set_new(entry, "time", json_real(time_sum)); + + auto request = json_object(); + json_object_set_new(entry, "request", request); + + auto method_ptr = http2::get_header(req->req_nva, ":method"); + + const char *method = "GET"; + if (method_ptr) { + method = (*method_ptr).value.c_str(); + } + + auto req_headers = json_array(); + json_object_set_new(request, "headers", req_headers); + + for (auto &nv : req->req_nva) { + auto hd = json_object(); + json_array_append_new(req_headers, hd); + + json_object_set_new(hd, "name", json_string(nv.name.c_str())); + json_object_set_new(hd, "value", json_string(nv.value.c_str())); + } + + json_object_set_new(request, "method", json_string(method)); + json_object_set_new(request, "url", json_string(req->uri.c_str())); + json_object_set_new(request, "httpVersion", json_string("HTTP/2.0")); + json_object_set_new(request, "cookies", json_array()); + json_object_set_new(request, "queryString", json_array()); + json_object_set_new(request, "headersSize", json_integer(-1)); + json_object_set_new(request, "bodySize", json_integer(-1)); + + auto response = json_object(); + json_object_set_new(entry, "response", response); + + auto res_headers = json_array(); + json_object_set_new(response, "headers", res_headers); + + for (auto &nv : req->res_nva) { + auto hd = json_object(); + json_array_append_new(res_headers, hd); + + json_object_set_new(hd, "name", json_string(nv.name.c_str())); + json_object_set_new(hd, "value", json_string(nv.value.c_str())); + } + + json_object_set_new(response, "status", json_integer(req->status)); + json_object_set_new(response, "statusText", json_string("")); + json_object_set_new(response, "httpVersion", json_string("HTTP/2.0")); + json_object_set_new(response, "cookies", json_array()); + + auto content = json_object(); + json_object_set_new(response, "content", content); + + json_object_set_new(content, "size", json_integer(req->response_len)); + + auto content_type_ptr = http2::get_header(req->res_nva, "content-type"); + + const char *content_type = ""; + if (content_type_ptr) { + content_type = content_type_ptr->value.c_str(); + } + + json_object_set_new(content, "mimeType", json_string(content_type)); + + json_object_set_new(response, "redirectURL", json_string("")); + json_object_set_new(response, "headersSize", json_integer(-1)); + json_object_set_new(response, "bodySize", json_integer(-1)); + + json_object_set_new(entry, "cache", json_object()); + + auto timings = json_object(); + json_object_set_new(entry, "timings", timings); + + auto dns_timing = (i == 0) ? dns_delta : 0; + auto connect_timing = (i == 0) ? connect_delta : 0; + + json_object_set_new(timings, "dns", json_real(dns_timing)); + json_object_set_new(timings, "connect", json_real(connect_timing)); + + json_object_set_new(timings, "blocked", json_real(0.0)); + json_object_set_new(timings, "send", json_real(0.0)); + json_object_set_new(timings, "wait", json_real(wait_delta)); + json_object_set_new(timings, "receive", json_real(receive_delta)); + + json_object_set_new(entry, "pageref", json_string(PAGE_ID)); + } + + json_dumpf(root, outfile, JSON_PRESERVE_ORDER | JSON_INDENT(2)); + json_decref(root); +} +#endif // HAVE_JANSSON + +namespace { +void update_html_parser(HttpClient *client, Request *req, const uint8_t *data, + size_t len, int fin) { + if (!req->html_parser) { + return; + } + req->update_html_parser(data, len, fin); + + for (auto &p : req->html_parser->get_links()) { + auto uri = strip_fragment(p.first.c_str()); + auto res_type = p.second; + + http_parser_url u; + memset(&u, 0, sizeof(u)); + if (http_parser_parse_url(uri.c_str(), uri.size(), 0, &u) == 0 && + util::fieldeq(uri.c_str(), u, req->uri.c_str(), req->u, UF_SCHEMA) && + util::fieldeq(uri.c_str(), u, req->uri.c_str(), req->u, UF_HOST) && + util::porteq(uri.c_str(), u, req->uri.c_str(), req->u)) { + // No POST data for assets + auto pri_spec = resolve_dep(res_type); + + if (client->add_request(uri, nullptr, 0, pri_spec, req->level + 1)) { + + submit_request(client, config.headers, client->reqvec.back().get()); + } + } + } + req->html_parser->clear_links(); +} +} // namespace + +namespace { +HttpClient *get_client(void *user_data) { + return static_cast(user_data); +} +} // namespace + +namespace { +int on_data_chunk_recv_callback(nghttp2_session *session, uint8_t flags, + int32_t stream_id, const uint8_t *data, + size_t len, void *user_data) { + auto client = get_client(user_data); + auto req = static_cast( + nghttp2_session_get_stream_user_data(session, stream_id)); + + if (!req) { + return 0; + } + + if (config.verbose >= 2) { + verbose_on_data_chunk_recv_callback(session, flags, stream_id, data, len, + user_data); + } + + req->response_len += len; + + if (req->inflater) { + while (len > 0) { + const size_t MAX_OUTLEN = 4096; + std::array out; + size_t outlen = MAX_OUTLEN; + size_t tlen = len; + int rv = + nghttp2_gzip_inflate(req->inflater, out.data(), &outlen, data, &tlen); + if (rv != 0) { + nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, stream_id, + NGHTTP2_INTERNAL_ERROR); + break; + } + + if (!config.null_out) { + std::cout.write(reinterpret_cast(out.data()), outlen); + } + + update_html_parser(client, req, out.data(), outlen, 0); + data += tlen; + len -= tlen; + } + + return 0; + } + + if (!config.null_out) { + std::cout.write(reinterpret_cast(data), len); + } + + update_html_parser(client, req, data, len, 0); + + return 0; +} +} // namespace + +namespace { +ssize_t select_padding_callback(nghttp2_session *session, + const nghttp2_frame *frame, size_t max_payload, + void *user_data) { + return std::min(max_payload, frame->hd.length + config.padding); +} +} // namespace + +namespace { +void check_response_header(nghttp2_session *session, Request *req) { + bool gzip = false; + + req->expect_final_response = false; + + auto status_hd = req->get_res_header(http2::HD__STATUS); + + if (!status_hd) { + nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, req->stream_id, + NGHTTP2_PROTOCOL_ERROR); + return; + } + + auto status = http2::parse_http_status_code(status_hd->value); + if (status == -1) { + nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, req->stream_id, + NGHTTP2_PROTOCOL_ERROR); + return; + } + + req->status = status; + + for (auto &nv : req->res_nva) { + if ("content-encoding" == nv.name) { + gzip = util::strieq_l("gzip", nv.value) || + util::strieq_l("deflate", nv.value); + continue; + } + } + + if (req->status / 100 == 1) { + req->expect_final_response = true; + req->status = 0; + req->res_nva.clear(); + http2::init_hdidx(req->res_hdidx); + return; + } + + if (gzip) { + if (!req->inflater) { + req->init_inflater(); + } + } + if (config.get_assets && req->level == 0) { + if (!req->html_parser) { + req->init_html_parser(); + } + } +} +} // namespace + +namespace { +int on_begin_headers_callback(nghttp2_session *session, + const nghttp2_frame *frame, void *user_data) { + auto client = get_client(user_data); + switch (frame->hd.type) { + case NGHTTP2_HEADERS: { + auto req = static_cast( + nghttp2_session_get_stream_user_data(session, frame->hd.stream_id)); + if (!req) { + break; + } + + switch (frame->headers.cat) { + case NGHTTP2_HCAT_RESPONSE: + case NGHTTP2_HCAT_PUSH_RESPONSE: + req->record_response_start_time(); + break; + default: + break; + } + + break; + } + case NGHTTP2_PUSH_PROMISE: { + auto stream_id = frame->push_promise.promised_stream_id; + http_parser_url u; + memset(&u, 0, sizeof(u)); + // TODO Set pri and level + nghttp2_priority_spec pri_spec; + + nghttp2_priority_spec_default_init(&pri_spec); + + auto req = make_unique("", u, nullptr, 0, pri_spec); + req->stream_id = stream_id; + + nghttp2_session_set_stream_user_data(session, stream_id, req.get()); + + client->request_done(req.get()); + req->record_request_start_time(); + client->reqvec.push_back(std::move(req)); + + break; + } + } + return 0; +} +} // namespace + +namespace { +int on_header_callback(nghttp2_session *session, const nghttp2_frame *frame, + const uint8_t *name, size_t namelen, + const uint8_t *value, size_t valuelen, uint8_t flags, + void *user_data) { + if (config.verbose) { + verbose_on_header_callback(session, frame, name, namelen, value, valuelen, + flags, user_data); + } + + switch (frame->hd.type) { + case NGHTTP2_HEADERS: { + auto req = static_cast( + nghttp2_session_get_stream_user_data(session, frame->hd.stream_id)); + + if (!req) { + break; + } + + /* ignore trailer header */ + if (frame->headers.cat == NGHTTP2_HCAT_HEADERS && + !req->expect_final_response) { + break; + } + + auto token = http2::lookup_token(name, namelen); + + http2::index_header(req->res_hdidx, token, req->res_nva.size()); + http2::add_header(req->res_nva, name, namelen, value, valuelen, + flags & NGHTTP2_NV_FLAG_NO_INDEX, token); + break; + } + case NGHTTP2_PUSH_PROMISE: { + auto req = static_cast(nghttp2_session_get_stream_user_data( + session, frame->push_promise.promised_stream_id)); + + if (!req) { + break; + } + + auto token = http2::lookup_token(name, namelen); + + http2::index_header(req->req_hdidx, token, req->req_nva.size()); + http2::add_header(req->req_nva, name, namelen, value, valuelen, + flags & NGHTTP2_NV_FLAG_NO_INDEX, token); + break; + } + } + return 0; +} +} // namespace + +namespace { +int on_frame_recv_callback2(nghttp2_session *session, + const nghttp2_frame *frame, void *user_data) { + int rv = 0; + + if (config.verbose) { + verbose_on_frame_recv_callback(session, frame, user_data); + } + + auto client = get_client(user_data); + switch (frame->hd.type) { + case NGHTTP2_DATA: { + auto req = static_cast( + nghttp2_session_get_stream_user_data(session, frame->hd.stream_id)); + if (!req) { + return 0; + ; + } + + if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) { + req->record_response_end_time(); + ++client->success; + } + + break; + } + case NGHTTP2_HEADERS: { + auto req = static_cast( + nghttp2_session_get_stream_user_data(session, frame->hd.stream_id)); + // If this is the HTTP Upgrade with OPTIONS method to avoid POST, + // req is nullptr. + if (!req) { + return 0; + ; + } + + switch (frame->headers.cat) { + case NGHTTP2_HCAT_RESPONSE: + case NGHTTP2_HCAT_PUSH_RESPONSE: + check_response_header(session, req); + break; + case NGHTTP2_HCAT_HEADERS: + if (req->expect_final_response) { + check_response_header(session, req); + break; + } + if ((frame->hd.flags & NGHTTP2_FLAG_END_STREAM) == 0) { + nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, + frame->hd.stream_id, NGHTTP2_PROTOCOL_ERROR); + return 0; + } + break; + default: + assert(0); + } + + if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) { + req->record_response_end_time(); + ++client->success; + } + + break; + } + case NGHTTP2_SETTINGS: + if ((frame->hd.flags & NGHTTP2_FLAG_ACK) == 0) { + break; + } + ev_timer_stop(client->loop, &client->settings_timer); + break; + case NGHTTP2_PUSH_PROMISE: { + auto req = static_cast(nghttp2_session_get_stream_user_data( + session, frame->push_promise.promised_stream_id)); + if (!req) { + break; + } + auto scheme = req->get_req_header(http2::HD__SCHEME); + auto authority = req->get_req_header(http2::HD__AUTHORITY); + auto path = req->get_req_header(http2::HD__PATH); + + if (!authority) { + authority = req->get_req_header(http2::HD_HOST); + } + + // libnghttp2 guarantees :scheme, :method, :path and (:authority | + // host) exist and non-empty. + if (path->value[0] != '/') { + nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, + frame->push_promise.promised_stream_id, + NGHTTP2_PROTOCOL_ERROR); + break; + } + std::string uri = scheme->value; + uri += "://"; + uri += authority->value; + uri += path->value; + http_parser_url u; + memset(&u, 0, sizeof(u)); + if (http_parser_parse_url(uri.c_str(), uri.size(), 0, &u) != 0) { + nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, + frame->push_promise.promised_stream_id, + NGHTTP2_PROTOCOL_ERROR); + break; + } + req->uri = uri; + req->u = u; + + if (client->path_cache.count(uri)) { + nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, + frame->push_promise.promised_stream_id, + NGHTTP2_CANCEL); + break; + } + + if (config.multiply == 1) { + client->path_cache.insert(uri); + } + + break; + } + } + return rv; +} +} // namespace + +namespace { +int before_frame_send_callback(nghttp2_session *session, + const nghttp2_frame *frame, void *user_data) { + if (frame->hd.type != NGHTTP2_HEADERS || + frame->headers.cat != NGHTTP2_HCAT_REQUEST) { + return 0; + } + auto req = static_cast( + nghttp2_session_get_stream_user_data(session, frame->hd.stream_id)); + assert(req); + req->record_request_start_time(); + return 0; +} + +} // namespace + +namespace { +int on_stream_close_callback(nghttp2_session *session, int32_t stream_id, + uint32_t error_code, void *user_data) { + auto client = get_client(user_data); + auto req = static_cast( + nghttp2_session_get_stream_user_data(session, stream_id)); + + if (!req) { + return 0; + } + + update_html_parser(client, req, nullptr, 0, 1); + ++client->complete; + + if (client->all_requests_processed()) { + nghttp2_session_terminate_session(session, NGHTTP2_NO_ERROR); + } + + return 0; +} +} // namespace + +struct RequestResult { + std::chrono::microseconds time; +}; + +namespace { +void print_stats(const HttpClient &client) { + std::cout << "***** Statistics *****" << std::endl; + + std::vector reqs; + reqs.reserve(client.reqvec.size()); + for (const auto &req : client.reqvec) { + if (req->timing.state == RequestState::ON_COMPLETE) { + reqs.push_back(req.get()); + } + } + + std::sort(std::begin(reqs), std::end(reqs), + [](const Request *lhs, const Request *rhs) { + const auto <iming = lhs->timing; + const auto &rtiming = rhs->timing; + return ltiming.response_end_time < rtiming.response_end_time || + (ltiming.response_end_time == rtiming.response_end_time && + ltiming.request_start_time < rtiming.request_start_time); + }); + + std::cout << R"( +Request timing: + responseEnd: the time when last byte of response was received + relative to connectEnd + requestStart: the time just before first byte of request was sent + relative to connectEnd. If '*' is shown, this was + pushed by server. + process: responseEnd - requestStart + code: HTTP status code + size: number of bytes received as response body without + inflation. + URI: request URI + +see http://www.w3.org/TR/resource-timing/#processing-model + +sorted by 'complete' + +id responseEnd requestStart process code size request path)" << std::endl; + + const auto &base = client.timing.connect_end_time; + for (const auto &req : reqs) { + auto response_end = std::chrono::duration_cast( + req->timing.response_end_time - base); + auto request_start = std::chrono::duration_cast( + req->timing.request_start_time - base); + auto total = std::chrono::duration_cast( + req->timing.response_end_time - req->timing.request_start_time); + auto pushed = req->stream_id % 2 == 0; + + std::cout << std::setw(3) << req->stream_id << " " << std::setw(11) + << ("+" + util::format_duration(response_end)) << " " + << (pushed ? "*" : " ") << std::setw(11) + << ("+" + util::format_duration(request_start)) << " " + << std::setw(8) << util::format_duration(total) << " " + << std::setw(4) << req->status << " " << std::setw(4) + << util::utos_with_unit(req->response_len) << " " + << req->make_reqpath() << std::endl; + } +} +} // namespace + +namespace { +int client_select_next_proto_cb(SSL *ssl, unsigned char **out, + unsigned char *outlen, const unsigned char *in, + unsigned int inlen, void *arg) { + if (config.verbose) { + print_timer(); + std::cout << "[NPN] server offers:" << std::endl; + } + for (unsigned int i = 0; i < inlen; i += in [i] + 1) { + if (config.verbose) { + std::cout << " * "; + std::cout.write(reinterpret_cast(&in[i + 1]), in[i]); + std::cout << std::endl; + } + } + if (!util::select_h2(const_cast(out), outlen, in, + inlen)) { + print_protocol_nego_error(); + return SSL_TLSEXT_ERR_NOACK; + } + return SSL_TLSEXT_ERR_OK; +} +} // namespace + +namespace { +// Recommended general purpose "Intermediate compatibility" cipher by +// mozilla. +// +// https://wiki.mozilla.org/Security/Server_Side_TLS +const char *const CIPHER_LIST = + "ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-" + "AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:" + "DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-" + "AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-" + "AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-" + "AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:" + "DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-" + "SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-" + "SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!" + "aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA"; +} // namespace + +namespace { +int communicate( + const std::string &scheme, const std::string &host, uint16_t port, + std::vector> + requests, const nghttp2_session_callbacks *callbacks) { + int result = 0; + auto loop = EV_DEFAULT; + SSL_CTX *ssl_ctx = nullptr; + if (scheme == "https") { + ssl_ctx = SSL_CTX_new(SSLv23_client_method()); + if (!ssl_ctx) { + std::cerr << "[ERROR] Failed to create SSL_CTX: " + << ERR_error_string(ERR_get_error(), nullptr) << std::endl; + result = -1; + goto fin; + } + SSL_CTX_set_options(ssl_ctx, + SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | + SSL_OP_NO_COMPRESSION | + SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION); + SSL_CTX_set_mode(ssl_ctx, SSL_MODE_AUTO_RETRY); + SSL_CTX_set_mode(ssl_ctx, SSL_MODE_RELEASE_BUFFERS); + if (SSL_CTX_set_cipher_list(ssl_ctx, CIPHER_LIST) == 0) { + std::cerr << "[ERROR] " << ERR_error_string(ERR_get_error(), nullptr) + << std::endl; + result = -1; + goto fin; + } + if (!config.keyfile.empty()) { + if (SSL_CTX_use_PrivateKey_file(ssl_ctx, config.keyfile.c_str(), + SSL_FILETYPE_PEM) != 1) { + std::cerr << "[ERROR] " << ERR_error_string(ERR_get_error(), nullptr) + << std::endl; + result = -1; + goto fin; + } + } + if (!config.certfile.empty()) { + if (SSL_CTX_use_certificate_chain_file(ssl_ctx, + config.certfile.c_str()) != 1) { + std::cerr << "[ERROR] " << ERR_error_string(ERR_get_error(), nullptr) + << std::endl; + result = -1; + goto fin; + } + } + SSL_CTX_set_next_proto_select_cb(ssl_ctx, client_select_next_proto_cb, + nullptr); + +#if OPENSSL_VERSION_NUMBER >= 0x10002000L + auto proto_list = util::get_default_alpn(); + + SSL_CTX_set_alpn_protos(ssl_ctx, proto_list.data(), proto_list.size()); +#endif // OPENSSL_VERSION_NUMBER >= 0x10002000L + } + { + HttpClient client{callbacks, loop, ssl_ctx}; + + nghttp2_priority_spec pri_spec; + int32_t dep_stream_id = 0; + + if (!config.no_dep) { + dep_stream_id = anchors[ANCHOR_FOLLOWERS].stream_id; + } + + nghttp2_priority_spec_init(&pri_spec, dep_stream_id, config.weight, 0); + + for (auto req : requests) { + for (int i = 0; i < config.multiply; ++i) { + client.add_request(std::get<0>(req), std::get<1>(req), std::get<2>(req), + pri_spec); + } + } + client.update_hostport(); + + client.record_start_time(); + + if (client.resolve_host(host, port) != 0) { + goto fin; + } + + client.record_domain_lookup_end_time(); + + if (client.initiate_connection() != 0) { + goto fin; + } + ev_run(loop, 0); + +#ifdef HAVE_JANSSON + if (!config.harfile.empty()) { + FILE *outfile; + if (config.harfile == "-") { + outfile = stdout; + } else { + outfile = fopen(config.harfile.c_str(), "wb"); + } + + if (outfile) { + client.output_har(outfile); + + if (outfile != stdout) { + fclose(outfile); + } + } else { + std::cerr << "Cannot open file " << config.harfile << ". " + << "har file could not be created." << std::endl; + } + } +#endif // HAVE_JANSSON + + if (client.success != client.reqvec.size()) { + std::cerr << "Some requests were not processed. total=" + << client.reqvec.size() << ", processed=" << client.success + << std::endl; + } + if (config.stat) { + print_stats(client); + } + } +fin: + if (ssl_ctx) { + SSL_CTX_free(ssl_ctx); + } + return result; +} +} // namespace + +namespace { +ssize_t file_read_callback(nghttp2_session *session, int32_t stream_id, + uint8_t *buf, size_t length, uint32_t *data_flags, + nghttp2_data_source *source, void *user_data) { + int rv; + auto req = static_cast( + nghttp2_session_get_stream_user_data(session, stream_id)); + assert(req); + int fd = source->fd; + ssize_t nread; + + while ((nread = pread(fd, buf, length, req->data_offset)) == -1 && + errno == EINTR) + ; + + if (nread == -1) { + return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; + } + + if (nread == 0) { + *data_flags |= NGHTTP2_DATA_FLAG_EOF; + if (!config.trailer.empty()) { + std::vector nva; + nva.reserve(config.trailer.size()); + for (auto &kv : config.trailer) { + nva.push_back(http2::make_nv(kv.name, kv.value, kv.no_index)); + } + rv = nghttp2_submit_trailer(session, stream_id, nva.data(), nva.size()); + if (rv != 0) { + if (nghttp2_is_fatal(rv)) { + return NGHTTP2_ERR_CALLBACK_FAILURE; + } + } else { + *data_flags |= NGHTTP2_DATA_FLAG_NO_END_STREAM; + } + } + } else { + req->data_offset += nread; + } + + return nread; +} +} // namespace + +namespace { +ssize_t send_callback(nghttp2_session *session, const uint8_t *data, + size_t length, int flags, void *user_data) { + auto client = static_cast(user_data); + auto &wb = client->wb; + + if (wb.wleft() == 0) { + return NGHTTP2_ERR_WOULDBLOCK; + } + + return wb.write(data, length); +} +} // namespace + +namespace { +int run(char **uris, int n) { + nghttp2_session_callbacks *callbacks; + + nghttp2_session_callbacks_new(&callbacks); + auto cbsdel = defer(nghttp2_session_callbacks_del, callbacks); + + nghttp2_session_callbacks_set_on_stream_close_callback( + callbacks, on_stream_close_callback); + + nghttp2_session_callbacks_set_on_frame_recv_callback(callbacks, + on_frame_recv_callback2); + + if (config.verbose) { + nghttp2_session_callbacks_set_on_frame_send_callback( + callbacks, verbose_on_frame_send_callback); + + nghttp2_session_callbacks_set_on_invalid_frame_recv_callback( + callbacks, verbose_on_invalid_frame_recv_callback); + } + + nghttp2_session_callbacks_set_on_data_chunk_recv_callback( + callbacks, on_data_chunk_recv_callback); + + nghttp2_session_callbacks_set_on_begin_headers_callback( + callbacks, on_begin_headers_callback); + + nghttp2_session_callbacks_set_on_header_callback(callbacks, + on_header_callback); + + nghttp2_session_callbacks_set_before_frame_send_callback( + callbacks, before_frame_send_callback); + + nghttp2_session_callbacks_set_send_callback(callbacks, send_callback); + + if (config.padding) { + nghttp2_session_callbacks_set_select_padding_callback( + callbacks, select_padding_callback); + } + + std::string prev_scheme; + std::string prev_host; + uint16_t prev_port = 0; + int failures = 0; + int data_fd = -1; + nghttp2_data_provider data_prd; + struct stat data_stat; + + if (!config.datafile.empty()) { + if (config.datafile == "-") { + if (fstat(0, &data_stat) == 0 && + (data_stat.st_mode & S_IFMT) == S_IFREG) { + // use STDIN if it is a regular file + data_fd = 0; + } else { + // copy the contents of STDIN to a temporary file + char tempfn[] = "/tmp/nghttp.temp.XXXXXX"; + data_fd = mkstemp(tempfn); + if (data_fd == -1) { + std::cerr << "[ERROR] Could not create a temporary file in /tmp" + << std::endl; + return 1; + } + if (unlink(tempfn) != 0) { + std::cerr << "[WARNING] failed to unlink temporary file:" << tempfn + << std::endl; + } + while (1) { + std::array buf; + ssize_t rret, wret; + while ((rret = read(0, buf.data(), buf.size())) == -1 && + errno == EINTR) + ; + if (rret == 0) + break; + if (rret == -1) { + std::cerr << "[ERROR] I/O error while reading from STDIN" + << std::endl; + return 1; + } + while ((wret = write(data_fd, buf.data(), rret)) == -1 && + errno == EINTR) + ; + if (wret != rret) { + std::cerr << "[ERROR] I/O error while writing to temporary file" + << std::endl; + return 1; + } + } + if (fstat(data_fd, &data_stat) == -1) { + close(data_fd); + std::cerr << "[ERROR] Could not stat temporary file" << std::endl; + return 1; + } + } + } else { + data_fd = open(config.datafile.c_str(), O_RDONLY | O_BINARY); + if (data_fd == -1) { + std::cerr << "[ERROR] Could not open file " << config.datafile + << std::endl; + return 1; + } + if (fstat(data_fd, &data_stat) == -1) { + close(data_fd); + std::cerr << "[ERROR] Could not stat file " << config.datafile + << std::endl; + return 1; + } + } + data_prd.source.fd = data_fd; + data_prd.read_callback = file_read_callback; + } + std::vector> + requests; + for (int i = 0; i < n; ++i) { + http_parser_url u; + memset(&u, 0, sizeof(u)); + auto uri = strip_fragment(uris[i]); + if (http_parser_parse_url(uri.c_str(), uri.size(), 0, &u) == 0 && + util::has_uri_field(u, UF_SCHEMA)) { + uint16_t port = util::has_uri_field(u, UF_PORT) + ? u.port + : util::get_default_port(uri.c_str(), u); + if (!util::fieldeq(uri.c_str(), u, UF_SCHEMA, prev_scheme.c_str()) || + !util::fieldeq(uri.c_str(), u, UF_HOST, prev_host.c_str()) || + port != prev_port) { + if (!requests.empty()) { + if (communicate(prev_scheme, prev_host, prev_port, + std::move(requests), callbacks) != 0) { + ++failures; + } + requests.clear(); + } + prev_scheme = util::get_uri_field(uri.c_str(), u, UF_SCHEMA); + prev_host = util::get_uri_field(uri.c_str(), u, UF_HOST); + prev_port = port; + } + requests.emplace_back(uri, data_fd == -1 ? nullptr : &data_prd, + data_stat.st_size); + } + } + if (!requests.empty()) { + if (communicate(prev_scheme, prev_host, prev_port, std::move(requests), + callbacks) != 0) { + ++failures; + } + } + return failures; +} +} // namespace + +namespace { +void print_version(std::ostream &out) { + out << "nghttp nghttp2/" NGHTTP2_VERSION << std::endl; +} +} // namespace + +namespace { +void print_usage(std::ostream &out) { + out << R"(Usage: nghttp [OPTIONS]... ... +HTTP/2 experimental client)" << std::endl; +} +} // namespace + +namespace { +void print_help(std::ostream &out) { + print_usage(out); + out << R"( + Specify URI to access. +Options: + -v, --verbose + Print debug information such as reception and + transmission of frames and name/value pairs. Specifying + this option multiple times increases verbosity. + -n, --null-out + Discard downloaded data. + -O, --remote-name + Save download data in the current directory. The + filename is dereived from URI. If URI ends with '/', + 'index.html' is used as a filename. Not implemented + yet. + -t, --timeout= + Timeout each request after . Set 0 to disable + timeout. + -w, --window-bits= + Sets the stream level initial window size to 2**-1. + -W, --connection-window-bits= + Sets the connection level initial window size to + 2**-1. + -a, --get-assets + Download assets such as stylesheets, images and script + files linked from the downloaded resource. Only links + whose origins are the same with the linking resource + will be downloaded. nghttp prioritizes resources using + HTTP/2 dependency based priority. The priority order, + from highest to lowest, is html itself, css, javascript + and images. + -s, --stat Print statistics. + -H, --header=
        + Add a header to the requests. Example: -H':method: PUT' + --trailer=
        + Add a trailer header to the requests.
        must not + include pseudo header field (header field name starting + with ':'). To send trailer, one must use -d option to + send request body. Example: --trailer 'foo: bar'. + --cert= + Use the specified client certificate file. The file + must be in PEM format. + --key= Use the client private key file. The file must be in + PEM format. + -d, --data= + Post FILE to server. If '-' is given, data will be read + from stdin. + -m, --multiply= + Request each URI times. By default, same URI is not + requested twice. This option disables it too. + -u, --upgrade + Perform HTTP Upgrade for HTTP/2. This option is ignored + if the request URI has https scheme. If -d is used, the + HTTP upgrade request is performed with OPTIONS method. + -p, --weight= + Sets priority group weight. The valid value range is + [)" << NGHTTP2_MIN_WEIGHT << ", " << NGHTTP2_MAX_WEIGHT + << R"(], inclusive. + Default: )" << NGHTTP2_DEFAULT_WEIGHT << R"( + -M, --peer-max-concurrent-streams= + Use as SETTINGS_MAX_CONCURRENT_STREAMS value of + remote endpoint as if it is received in SETTINGS frame. + The default is large enough as it is seen as unlimited. + -c, --header-table-size= + Specify decoder header table size. + -b, --padding= + Add at most bytes to a frame payload as padding. + Specify 0 to disable padding. + -r, --har= + Output HTTP transactions in HAR format. If '-' + is given, data is written to stdout. + --color Force colored log output. + --continuation + Send large header to test CONTINUATION. + --no-content-length + Don't send content-length header field. + --no-dep Don't send dependency based priority hint to server. + --hexdump Display the incoming traffic in hexadecimal (Canonical + hex+ASCII display). If SSL/TLS is used, decrypted data + are used. + --no-push Disable server push. + --version Display version information and exit. + -h, --help Display this help and exit. + +-- + + The argument is an integer and an optional unit (e.g., 10K is + 10 * 1024). Units are K, M and G (powers of 1024). + + The argument is an integer and an optional unit (e.g., 1s + is 1 second and 500ms is 500 milliseconds). Units are h, m, s or ms + (hours, minutes, seconds and milliseconds, respectively). If a unit + is omitted, a second is used as unit.)" << std::endl; +} +} // namespace + +int main(int argc, char **argv) { + bool color = false; + while (1) { + static int flag = 0; + static option long_options[] = { + {"verbose", no_argument, nullptr, 'v'}, + {"null-out", no_argument, nullptr, 'n'}, + {"remote-name", no_argument, nullptr, 'O'}, + {"timeout", required_argument, nullptr, 't'}, + {"window-bits", required_argument, nullptr, 'w'}, + {"connection-window-bits", required_argument, nullptr, 'W'}, + {"get-assets", no_argument, nullptr, 'a'}, + {"stat", no_argument, nullptr, 's'}, + {"help", no_argument, nullptr, 'h'}, + {"header", required_argument, nullptr, 'H'}, + {"data", required_argument, nullptr, 'd'}, + {"multiply", required_argument, nullptr, 'm'}, + {"upgrade", no_argument, nullptr, 'u'}, + {"weight", required_argument, nullptr, 'p'}, + {"peer-max-concurrent-streams", required_argument, nullptr, 'M'}, + {"header-table-size", required_argument, nullptr, 'c'}, + {"padding", required_argument, nullptr, 'b'}, + {"har", required_argument, nullptr, 'r'}, + {"cert", required_argument, &flag, 1}, + {"key", required_argument, &flag, 2}, + {"color", no_argument, &flag, 3}, + {"continuation", no_argument, &flag, 4}, + {"version", no_argument, &flag, 5}, + {"no-content-length", no_argument, &flag, 6}, + {"no-dep", no_argument, &flag, 7}, + {"trailer", required_argument, &flag, 9}, + {"hexdump", no_argument, &flag, 10}, + {"no-push", no_argument, &flag, 11}, + {nullptr, 0, nullptr, 0}}; + int option_index = 0; + int c = getopt_long(argc, argv, "M:Oab:c:d:gm:np:r:hH:vst:uw:W:", + long_options, &option_index); + if (c == -1) { + break; + } + switch (c) { + case 'M': + // peer-max-concurrent-streams option + config.peer_max_concurrent_streams = strtoul(optarg, nullptr, 10); + break; + case 'O': + config.remote_name = true; + break; + case 'h': + print_help(std::cout); + exit(EXIT_SUCCESS); + case 'b': + config.padding = strtol(optarg, nullptr, 10); + break; + case 'n': + config.null_out = true; + break; + case 'p': { + errno = 0; + auto n = strtoul(optarg, nullptr, 10); + if (errno == 0 && NGHTTP2_MIN_WEIGHT <= n && n <= NGHTTP2_MAX_WEIGHT) { + config.weight = n; + } else { + std::cerr << "-p: specify the integer in the range [" + << NGHTTP2_MIN_WEIGHT << ", " << NGHTTP2_MAX_WEIGHT + << "], inclusive" << std::endl; + exit(EXIT_FAILURE); + } + break; + } + case 'r': +#ifdef HAVE_JANSSON + config.harfile = optarg; +#else // !HAVE_JANSSON + std::cerr << "[WARNING]: -r, --har option is ignored because\n" + << "the binary was not compiled with libjansson." << std::endl; +#endif // !HAVE_JANSSON + break; + case 'v': + ++config.verbose; + break; + case 't': + config.timeout = util::parse_duration_with_unit(optarg); + if (config.timeout == std::numeric_limits::infinity()) { + std::cerr << "-t: bad timeout value: " << optarg << std::endl; + exit(EXIT_FAILURE); + } + break; + case 'u': + config.upgrade = true; + break; + case 'w': + case 'W': { + errno = 0; + char *endptr = nullptr; + unsigned long int n = strtoul(optarg, &endptr, 10); + if (errno == 0 && *endptr == '\0' && n < 31) { + if (c == 'w') { + config.window_bits = n; + } else { + config.connection_window_bits = n; + } + } else { + std::cerr << "-" << static_cast(c) + << ": specify the integer in the range [0, 30], inclusive" + << std::endl; + exit(EXIT_FAILURE); + } + break; + } + case 'H': { + char *header = optarg; + // Skip first possible ':' in the header name + char *value = strchr(optarg + 1, ':'); + if (!value || (header[0] == ':' && header + 1 == value)) { + std::cerr << "-H: invalid header: " << optarg << std::endl; + exit(EXIT_FAILURE); + } + *value = 0; + value++; + while (isspace(*value)) { + value++; + } + if (*value == 0) { + // This could also be a valid case for suppressing a header + // similar to curl + std::cerr << "-H: invalid header - value missing: " << optarg + << std::endl; + exit(EXIT_FAILURE); + } + config.headers.emplace_back(header, value, false); + util::inp_strlower(config.headers.back().name); + break; + } + case 'a': +#ifdef HAVE_LIBXML2 + config.get_assets = true; +#else // !HAVE_LIBXML2 + std::cerr << "[WARNING]: -a, --get-assets option is ignored because\n" + << "the binary was not compiled with libxml2." << std::endl; +#endif // !HAVE_LIBXML2 + break; + case 's': + config.stat = true; + break; + case 'd': + config.datafile = optarg; + break; + case 'm': + config.multiply = strtoul(optarg, nullptr, 10); + break; + case 'c': + errno = 0; + config.header_table_size = util::parse_uint_with_unit(optarg); + if (config.header_table_size == -1) { + std::cerr << "-c: Bad option value: " << optarg << std::endl; + exit(EXIT_FAILURE); + } + break; + case '?': + util::show_candidates(argv[optind - 1], long_options); + exit(EXIT_FAILURE); + case 0: + switch (flag) { + case 1: + // cert option + config.certfile = optarg; + break; + case 2: + // key option + config.keyfile = optarg; + break; + case 3: + // color option + color = true; + break; + case 4: + // continuation option + config.continuation = true; + break; + case 5: + // version option + print_version(std::cout); + exit(EXIT_SUCCESS); + case 6: + // no-content-length option + config.no_content_length = true; + break; + case 7: + // no-dep option + config.no_dep = true; + break; + case 9: { + // trailer option + auto header = optarg; + auto value = strchr(optarg, ':'); + if (!value) { + std::cerr << "--trailer: invalid header: " << optarg << std::endl; + exit(EXIT_FAILURE); + } + *value = 0; + value++; + while (isspace(*value)) { + value++; + } + if (*value == 0) { + // This could also be a valid case for suppressing a header + // similar to curl + std::cerr << "--trailer: invalid header - value missing: " << optarg + << std::endl; + exit(EXIT_FAILURE); + } + config.trailer.emplace_back(header, value, false); + util::inp_strlower(config.trailer.back().name); + break; + } + case 10: + // hexdump option + config.hexdump = true; + break; + case 11: + // no-push option + config.no_push = true; + break; + } + break; + default: + break; + } + } + + set_color_output(color || isatty(fileno(stdout))); + + nghttp2_option_set_peer_max_concurrent_streams( + config.http2_option, config.peer_max_concurrent_streams); + + struct sigaction act; + memset(&act, 0, sizeof(struct sigaction)); + act.sa_handler = SIG_IGN; + sigaction(SIGPIPE, &act, nullptr); + OPENSSL_config(nullptr); + OpenSSL_add_all_algorithms(); + SSL_load_error_strings(); + SSL_library_init(); + reset_timer(); + return run(argv + optind, argc - optind); +} + +} // namespace nghttp2 + +int main(int argc, char **argv) { return nghttp2::main(argc, argv); } diff --git a/src/nghttp.h b/src/nghttp.h new file mode 100644 index 0000000..0a7ee35 --- /dev/null +++ b/src/nghttp.h @@ -0,0 +1,272 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2015 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGHTTP_H +#define NGHTTP_H + +#include "nghttp2_config.h" + +#include +#ifdef HAVE_SYS_SOCKET_H +#include +#endif // HAVE_SYS_SOCKET_H +#ifdef HAVE_NETDB_H +#include +#endif // HAVE_NETDB_H + +#include +#include +#include +#include +#include + +#include + +#include + +#include + +#include "http-parser/http_parser.h" + +#include "buffer.h" +#include "http2.h" +#include "nghttp2_gzip.h" + +namespace nghttp2 { + +class HtmlParser; + +struct Config { + Config(); + ~Config(); + + Headers headers; + Headers trailer; + std::string certfile; + std::string keyfile; + std::string datafile; + std::string harfile; + nghttp2_option *http2_option; + size_t output_upper_thres; + size_t padding; + ssize_t peer_max_concurrent_streams; + ssize_t header_table_size; + int32_t weight; + int multiply; + // milliseconds + ev_tstamp timeout; + int window_bits; + int connection_window_bits; + int verbose; + bool null_out; + bool remote_name; + bool get_assets; + bool stat; + bool upgrade; + bool continuation; + bool no_content_length; + bool no_dep; + bool hexdump; + bool no_push; +}; + +enum class RequestState { INITIAL, ON_REQUEST, ON_RESPONSE, ON_COMPLETE }; + +struct RequestTiming { + // The point in time when request is started to be sent. + // Corresponds to requestStart in Resource Timing TR. + std::chrono::steady_clock::time_point request_start_time; + // The point in time when first byte of response is received. + // Corresponds to responseStart in Resource Timing TR. + std::chrono::steady_clock::time_point response_start_time; + // The point in time when last byte of response is received. + // Corresponds to responseEnd in Resource Timing TR. + std::chrono::steady_clock::time_point response_end_time; + RequestState state; + RequestTiming() : state(RequestState::INITIAL) {} +}; + +struct Request { + // For pushed request, |uri| is empty and |u| is zero-cleared. + Request(const std::string &uri, const http_parser_url &u, + const nghttp2_data_provider *data_prd, int64_t data_length, + const nghttp2_priority_spec &pri_spec, int level = 0); + ~Request(); + + void init_inflater(); + + void init_html_parser(); + int update_html_parser(const uint8_t *data, size_t len, int fin); + + std::string make_reqpath() const; + + bool is_ipv6_literal_addr() const; + + bool response_pseudo_header_allowed(int16_t token) const; + bool push_request_pseudo_header_allowed(int16_t token) const; + + Headers::value_type *get_res_header(int16_t token); + Headers::value_type *get_req_header(int16_t token); + + void record_request_start_time(); + void record_response_start_time(); + void record_response_end_time(); + + Headers res_nva; + Headers req_nva; + // URI without fragment + std::string uri; + http_parser_url u; + nghttp2_priority_spec pri_spec; + RequestTiming timing; + int64_t data_length; + int64_t data_offset; + // Number of bytes received from server + int64_t response_len; + nghttp2_gzip *inflater; + HtmlParser *html_parser; + const nghttp2_data_provider *data_prd; + int32_t stream_id; + int status; + // Recursion level: 0: first entity, 1: entity linked from first entity + int level; + http2::HeaderIndex res_hdidx; + // used for incoming PUSH_PROMISE + http2::HeaderIndex req_hdidx; + bool expect_final_response; +}; + +struct SessionTiming { + // The point in time when operation was started. Corresponds to + // startTime in Resource Timing TR, but recorded in system clock time. + std::chrono::system_clock::time_point system_start_time; + // Same as above, but recorded in steady clock time. + std::chrono::steady_clock::time_point start_time; + // The point in time when DNS resolution was completed. Corresponds + // to domainLookupEnd in Resource Timing TR. + std::chrono::steady_clock::time_point domain_lookup_end_time; + // The point in time when connection was established or SSL/TLS + // handshake was completed. Corresponds to connectEnd in Resource + // Timing TR. + std::chrono::steady_clock::time_point connect_end_time; +}; + +enum class ClientState { IDLE, CONNECTED }; + +struct HttpClient { + HttpClient(const nghttp2_session_callbacks *callbacks, struct ev_loop *loop, + SSL_CTX *ssl_ctx); + ~HttpClient(); + + bool need_upgrade() const; + int resolve_host(const std::string &host, uint16_t port); + int initiate_connection(); + void disconnect(); + + int noop(); + int read_clear(); + int write_clear(); + int connected(); + int tls_handshake(); + int read_tls(); + int write_tls(); + + int do_read(); + int do_write(); + + int on_upgrade_connect(); + int on_upgrade_read(const uint8_t *data, size_t len); + int on_read(const uint8_t *data, size_t len); + int on_write(); + + int connection_made(); + void connect_fail(); + void request_done(Request *req); + + void signal_write(); + + bool all_requests_processed() const; + void update_hostport(); + bool add_request(const std::string &uri, + const nghttp2_data_provider *data_prd, int64_t data_length, + const nghttp2_priority_spec &pri_spec, int level = 0); + + void record_start_time(); + void record_domain_lookup_end_time(); + void record_connect_end_time(); + +#ifdef HAVE_JANSSON + void output_har(FILE *outfile); +#endif // HAVE_JANSSON + + std::vector> reqvec; + // Insert path already added in reqvec to prevent multiple request + // for 1 resource. + std::set path_cache; + std::string scheme; + std::string host; + std::string hostport; + // Used for parse the HTTP upgrade response from server + std::unique_ptr htp; + SessionTiming timing; + ev_io wev; + ev_io rev; + ev_timer wt; + ev_timer rt; + ev_timer settings_timer; + std::function readfn, writefn; + std::function on_readfn; + std::function on_writefn; + nghttp2_session *session; + const nghttp2_session_callbacks *callbacks; + struct ev_loop *loop; + SSL_CTX *ssl_ctx; + SSL *ssl; + addrinfo *addrs; + addrinfo *next_addr; + addrinfo *cur_addr; + // The number of completed requests, including failed ones. + size_t complete; + // The number of requests that local endpoint received END_STREAM + // from peer. + size_t success; + // The length of settings_payload + size_t settings_payloadlen; + ClientState state; + // The HTTP status code of the response message of HTTP Upgrade. + unsigned int upgrade_response_status_code; + int fd; + // true if the response message of HTTP Upgrade request is fully + // received. It is not relevant the upgrade succeeds, or not. + bool upgrade_response_complete; + Buffer<65536> wb; + // SETTINGS payload sent as token68 in HTTP Upgrade + std::array settings_payload; + + enum { ERR_CONNECT_FAIL = -100 }; +}; + +} // namespace nghttp2 + +#endif // NGHTTP_H diff --git a/src/nghttp2_config.h b/src/nghttp2_config.h new file mode 100644 index 0000000..9dede35 --- /dev/null +++ b/src/nghttp2_config.h @@ -0,0 +1,38 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGHTTP2_CONFIG_H +#define NGHTTP2_CONFIG_H + +#ifdef HAVE_CONFIG_H +#include +#endif // HAVE_CONFIG_H + +// gcc 4.6 has std::chrono::monotonic_clock, which was renamed as +// std::chrono::steady_clock in C++11 standard. +#ifndef HAVE_STEADY_CLOCK +#define steady_clock monotonic_clock +#endif // !HAVE_STEADY_CLOCK + +#endif // NGHTTP2_CONFIG_H diff --git a/src/nghttp2_gzip.c b/src/nghttp2_gzip.c new file mode 100644 index 0000000..b12f311 --- /dev/null +++ b/src/nghttp2_gzip.c @@ -0,0 +1,92 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "nghttp2_gzip.h" + +#include + +int nghttp2_gzip_inflate_new(nghttp2_gzip **inflater_ptr) { + int rv; + *inflater_ptr = malloc(sizeof(nghttp2_gzip)); + if (*inflater_ptr == NULL) { + return -1; + } + (*inflater_ptr)->finished = 0; + (*inflater_ptr)->zst.next_in = Z_NULL; + (*inflater_ptr)->zst.avail_in = 0; + (*inflater_ptr)->zst.zalloc = Z_NULL; + (*inflater_ptr)->zst.zfree = Z_NULL; + (*inflater_ptr)->zst.opaque = Z_NULL; + rv = inflateInit2(&(*inflater_ptr)->zst, 47); + if (rv != Z_OK) { + free(*inflater_ptr); + return -1; + } + return 0; +} + +void nghttp2_gzip_inflate_del(nghttp2_gzip *inflater) { + if (inflater != NULL) { + inflateEnd(&inflater->zst); + free(inflater); + } +} + +int nghttp2_gzip_inflate(nghttp2_gzip *inflater, uint8_t *out, + size_t *outlen_ptr, const uint8_t *in, + size_t *inlen_ptr) { + int rv; + if (inflater->finished) { + return -1; + } + inflater->zst.avail_in = (unsigned int)*inlen_ptr; + inflater->zst.next_in = (unsigned char *)in; + inflater->zst.avail_out = (unsigned int)*outlen_ptr; + inflater->zst.next_out = out; + + rv = inflate(&inflater->zst, Z_NO_FLUSH); + + *inlen_ptr -= inflater->zst.avail_in; + *outlen_ptr -= inflater->zst.avail_out; + switch (rv) { + case Z_STREAM_END: + inflater->finished = 1; + case Z_OK: + case Z_BUF_ERROR: + return 0; + case Z_DATA_ERROR: + case Z_STREAM_ERROR: + case Z_NEED_DICT: + case Z_MEM_ERROR: + return -1; + default: + assert(0); + /* We need this for some compilers */ + return 0; + } +} + +int nghttp2_gzip_inflate_finished(nghttp2_gzip *inflater) { + return inflater->finished; +} diff --git a/src/nghttp2_gzip.h b/src/nghttp2_gzip.h new file mode 100644 index 0000000..2fa905a --- /dev/null +++ b/src/nghttp2_gzip.h @@ -0,0 +1,122 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGHTTP2_GZIP_H + +#ifdef HAVE_CONFIG_H +#include +#endif /* HAVE_CONFIG_H */ +#include + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @struct + * + * The gzip stream to inflate data. + */ +typedef struct { + z_stream zst; + int8_t finished; +} nghttp2_gzip; + +/** + * @function + * + * A helper function to set up a per request gzip stream to inflate + * data. + * + * This function returns 0 if it succeeds, or -1. + */ +int nghttp2_gzip_inflate_new(nghttp2_gzip **inflater_ptr); + +/** + * @function + * + * Frees the inflate stream. The |inflater| may be ``NULL``. + */ +void nghttp2_gzip_inflate_del(nghttp2_gzip *inflater); + +/** + * @function + * + * Inflates data in |in| with the length |*inlen_ptr| and stores the + * inflated data to |out| which has allocated size at least + * |*outlen_ptr|. On return, |*outlen_ptr| is updated to represent + * the number of data written in |out|. Similarly, |*inlen_ptr| is + * updated to represent the number of input bytes processed. + * + * This function returns 0 if it succeeds, or -1. + * + * The example follows:: + * + * void on_data_chunk_recv_callback(nghttp2_session *session, + * uint8_t flags, + * int32_t stream_id, + * const uint8_t *data, size_t len, + * void *user_data) + * { + * ... + * req = nghttp2_session_get_stream_user_data(session, stream_id); + * nghttp2_gzip *inflater = req->inflater; + * while(len > 0) { + * uint8_t out[MAX_OUTLEN]; + * size_t outlen = MAX_OUTLEN; + * size_t tlen = len; + * int rv; + * rv = nghttp2_gzip_inflate(inflater, out, &outlen, data, &tlen); + * if(rv != 0) { + * nghttp2_submit_rst_stream(session, stream_id, + * NGHTTP2_INTERNAL_ERROR); + * break; + * } + * ... Do stuff ... + * data += tlen; + * len -= tlen; + * } + * .... + * } + */ +int nghttp2_gzip_inflate(nghttp2_gzip *inflater, uint8_t *out, + size_t *outlen_ptr, const uint8_t *in, + size_t *inlen_ptr); + +/** + * @function + * + * Returns nonzero if |inflater| sees the end of deflate stream. + * After this function returns nonzero, `nghttp2_gzip_inflate()` with + * |inflater| gets to return error. + */ +int nghttp2_gzip_inflate_finished(nghttp2_gzip *inflater); + +#ifdef __cplusplus +} +#endif + +#endif /* NGHTTP2_GZIP_H */ diff --git a/src/nghttp2_gzip_test.c b/src/nghttp2_gzip_test.c new file mode 100644 index 0000000..eb43ae6 --- /dev/null +++ b/src/nghttp2_gzip_test.c @@ -0,0 +1,115 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "nghttp2_gzip_test.h" + +#include +#include + +#include + +#include + +#include "nghttp2_gzip.h" + +static ssize_t deflate_data(uint8_t *out, size_t outlen, const uint8_t *in, + size_t inlen) { + int rv; + z_stream zst; + zst.next_in = Z_NULL; + zst.zalloc = Z_NULL; + zst.zfree = Z_NULL; + zst.opaque = Z_NULL; + + rv = deflateInit(&zst, Z_DEFAULT_COMPRESSION); + assert(rv == Z_OK); + + zst.avail_in = (unsigned int)inlen; + zst.next_in = (uint8_t *)in; + zst.avail_out = (unsigned int)outlen; + zst.next_out = out; + rv = deflate(&zst, Z_SYNC_FLUSH); + assert(rv == Z_OK); + + deflateEnd(&zst); + + return outlen - zst.avail_out; +} + +static const char input[] = + "THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND " + "EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF " + "MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND " + "NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE " + "LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION " + "OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION " + "WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE."; + +void test_nghttp2_gzip_inflate(void) { + nghttp2_gzip *inflater; + uint8_t in[4096], out[4096], *inptr; + size_t inlen = sizeof(in); + size_t inproclen, outproclen; + const char *inputptr = input; + + inlen = deflate_data(in, inlen, (const uint8_t *)input, sizeof(input) - 1); + + CU_ASSERT(0 == nghttp2_gzip_inflate_new(&inflater)); + /* First 16 bytes */ + inptr = in; + inproclen = inlen; + outproclen = 16; + CU_ASSERT( + 0 == nghttp2_gzip_inflate(inflater, out, &outproclen, inptr, &inproclen)); + CU_ASSERT(16 == outproclen); + CU_ASSERT(inproclen > 0); + CU_ASSERT(0 == memcmp(inputptr, out, outproclen)); + /* Next 32 bytes */ + inptr += inproclen; + inlen -= inproclen; + inproclen = inlen; + inputptr += outproclen; + outproclen = 32; + CU_ASSERT( + 0 == nghttp2_gzip_inflate(inflater, out, &outproclen, inptr, &inproclen)); + CU_ASSERT(32 == outproclen); + CU_ASSERT(inproclen > 0); + CU_ASSERT(0 == memcmp(inputptr, out, outproclen)); + /* Rest */ + inptr += inproclen; + inlen -= inproclen; + inproclen = inlen; + inputptr += outproclen; + outproclen = sizeof(out); + CU_ASSERT( + 0 == nghttp2_gzip_inflate(inflater, out, &outproclen, inptr, &inproclen)); + CU_ASSERT(sizeof(input) - 49 == outproclen); + CU_ASSERT(inproclen > 0); + CU_ASSERT(0 == memcmp(inputptr, out, outproclen)); + + inlen -= inproclen; + CU_ASSERT(0 == inlen); + + nghttp2_gzip_inflate_del(inflater); +} diff --git a/src/nghttp2_gzip_test.h b/src/nghttp2_gzip_test.h new file mode 100644 index 0000000..2defcdc --- /dev/null +++ b/src/nghttp2_gzip_test.h @@ -0,0 +1,42 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGHTTP2_GZIP_TEST_H +#define NGHTTP2_GZIP_TEST_H + +#ifdef HAVE_CONFIG_H +#include +#endif /* HAVE_CONFIG_H */ + +#ifdef __cplusplus +extern "C" { +#endif + +void test_nghttp2_gzip_inflate(void); + +#ifdef __cplusplus +} +#endif + +#endif /* NGHTTP2_GZIP_TEST_H */ diff --git a/src/nghttpd.cc b/src/nghttpd.cc new file mode 100644 index 0000000..4ffc0bc --- /dev/null +++ b/src/nghttpd.cc @@ -0,0 +1,381 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "nghttp2_config.h" + +#ifdef HAVE_UNISTD_H +#include +#endif // HAVE_UNISTD_H +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "app_helper.h" +#include "HttpServer.h" +#include "util.h" +#include "ssl.h" + +namespace nghttp2 { + +namespace { +int parse_push_config(Config &config, const char *optarg) { + const char *eq = strchr(optarg, '='); + if (eq == NULL) { + return -1; + } + auto &paths = config.push[std::string(optarg, eq)]; + auto optarg_end = optarg + strlen(optarg); + auto i = eq + 1; + for (;;) { + const char *j = strchr(i, ','); + if (j == NULL) { + j = optarg_end; + } + paths.emplace_back(i, j); + if (j == optarg_end) { + break; + } + i = j; + ++i; + } + + return 0; +} +} // namespace + +namespace { +void print_version(std::ostream &out) { + out << "nghttpd nghttp2/" NGHTTP2_VERSION << std::endl; +} +} // namespace + +namespace { +void print_usage(std::ostream &out) { + out << "Usage: nghttpd [OPTION]... [ ]\n" + << "HTTP/2 experimental server" << std::endl; +} +} // namespace + +namespace { +void print_help(std::ostream &out) { + Config config; + print_usage(out); + out << R"( + Specify listening port number. + + Set path to server's private key. Required unless + --no-tls is specified. + Set path to server's certificate. Required unless + --no-tls is specified. +Options: + -a, --address= + The address to bind to. If not specified the default IP + address determined by getaddrinfo is used. + -D, --daemon + Run in a background. If -D is used, the current working + directory is changed to '/'. Therefore if this option + is used, -d option must be specified. + -V, --verify-client + The server sends a client certificate request. If the + client did not return a certificate, the handshake is + terminated. Currently, this option just requests a + client certificate and does not verify it. + -d, --htdocs= + Specify document root. If this option is not specified, + the document root is the current working directory. + -v, --verbose + Print debug information such as reception/ transmission + of frames and name/value pairs. + --no-tls Disable SSL/TLS. + -c, --header-table-size= + Specify decoder header table size. + --color Force colored log output. + -p, --push== + Push resources s when is requested. + This option can be used repeatedly to specify multiple + push configurations. and s are + relative to document root. See --htdocs option. + Example: -p/=/foo.png -p/doc=/bar.css + -b, --padding= + Add at most bytes to a frame payload as padding. + Specify 0 to disable padding. + -m, --max-concurrent-streams= + Set the maximum number of the concurrent streams in one + HTTP/2 session. + Default: )" << config.max_concurrent_streams << R"( + -n, --workers= + Set the number of worker threads. + Default: 1 + -e, --error-gzip + Make error response gzipped. + --dh-param-file= + Path to file that contains DH parameters in PEM format. + Without this option, DHE cipher suites are not + available. + --early-response + Start sending response when request HEADERS is received, + rather than complete request is received. + --trailer=
        + Add a trailer header to a response.
        must not + include pseudo header field (header field name starting + with ':'). The trailer is sent only if a response has + body part. Example: --trailer 'foo: bar'. + --hexdump Display the incoming traffic in hexadecimal (Canonical + hex+ASCII display). If SSL/TLS is used, decrypted data + are used. + --echo-upload + Send back uploaded content if method is POST or PUT. + --version Display version information and exit. + -h, --help Display this help and exit. + +-- + + The argument is an integer and an optional unit (e.g., 10K is + 10 * 1024). Units are K, M and G (powers of 1024).)" << std::endl; +} +} // namespace + +int main(int argc, char **argv) { + Config config; + bool color = false; + while (1) { + static int flag = 0; + static option long_options[] = { + {"address", required_argument, nullptr, 'a'}, + {"daemon", no_argument, nullptr, 'D'}, + {"htdocs", required_argument, nullptr, 'd'}, + {"help", no_argument, nullptr, 'h'}, + {"verbose", no_argument, nullptr, 'v'}, + {"verify-client", no_argument, nullptr, 'V'}, + {"header-table-size", required_argument, nullptr, 'c'}, + {"push", required_argument, nullptr, 'p'}, + {"padding", required_argument, nullptr, 'b'}, + {"max-concurrent-streams", required_argument, nullptr, 'm'}, + {"workers", required_argument, nullptr, 'n'}, + {"error-gzip", no_argument, nullptr, 'e'}, + {"no-tls", no_argument, &flag, 1}, + {"color", no_argument, &flag, 2}, + {"version", no_argument, &flag, 3}, + {"dh-param-file", required_argument, &flag, 4}, + {"early-response", no_argument, &flag, 5}, + {"trailer", required_argument, &flag, 6}, + {"hexdump", no_argument, &flag, 7}, + {"echo-upload", no_argument, &flag, 8}, + {nullptr, 0, nullptr, 0}}; + int option_index = 0; + int c = getopt_long(argc, argv, "DVb:c:d:ehm:n:p:va:", long_options, + &option_index); + char *end; + if (c == -1) { + break; + } + switch (c) { + case 'a': + config.address = optarg; + break; + case 'D': + config.daemon = true; + break; + case 'V': + config.verify_client = true; + break; + case 'b': + config.padding = strtol(optarg, nullptr, 10); + break; + case 'd': + config.htdocs = optarg; + break; + case 'e': + config.error_gzip = true; + break; + case 'm': { + // max-concurrent-streams option + auto n = util::parse_uint(optarg); + if (n == -1) { + std::cerr << "-m: invalid argument: " << optarg << std::endl; + exit(EXIT_FAILURE); + } + config.max_concurrent_streams = n; + break; + } + case 'n': +#ifdef NOTHREADS + std::cerr << "-n: WARNING: Threading disabled at build time, " + << "no threads created." << std::endl; +#else + errno = 0; + config.num_worker = strtoul(optarg, &end, 10); + if (errno == ERANGE || *end != '\0' || config.num_worker == 0) { + std::cerr << "-n: Bad option value: " << optarg << std::endl; + exit(EXIT_FAILURE); + } +#endif // NOTHREADS + break; + case 'h': + print_help(std::cout); + exit(EXIT_SUCCESS); + case 'v': + config.verbose = true; + break; + case 'c': + errno = 0; + config.header_table_size = util::parse_uint_with_unit(optarg); + if (config.header_table_size == -1) { + std::cerr << "-c: Bad option value: " << optarg << std::endl; + exit(EXIT_FAILURE); + } + break; + case 'p': + if (parse_push_config(config, optarg) != 0) { + std::cerr << "-p: Bad option value: " << optarg << std::endl; + } + break; + case '?': + util::show_candidates(argv[optind - 1], long_options); + exit(EXIT_FAILURE); + case 0: + switch (flag) { + case 1: + // no-tls option + config.no_tls = true; + break; + case 2: + // color option + color = true; + break; + case 3: + // version + print_version(std::cout); + exit(EXIT_SUCCESS); + case 4: + // dh-param-file + config.dh_param_file = optarg; + break; + case 5: + // early-response + config.early_response = true; + break; + case 6: { + // trailer option + auto header = optarg; + auto value = strchr(optarg, ':'); + if (!value) { + std::cerr << "--trailer: invalid header: " << optarg << std::endl; + exit(EXIT_FAILURE); + } + *value = 0; + value++; + while (isspace(*value)) { + value++; + } + if (*value == 0) { + // This could also be a valid case for suppressing a header + // similar to curl + std::cerr << "--trailer: invalid header - value missing: " << optarg + << std::endl; + exit(EXIT_FAILURE); + } + config.trailer.emplace_back(header, value, false); + util::inp_strlower(config.trailer.back().name); + break; + } + case 7: + // hexdump option + config.hexdump = true; + break; + case 8: + // echo-upload option + config.echo_upload = true; + break; + } + break; + default: + break; + } + } + if (argc - optind < (config.no_tls ? 1 : 3)) { + print_usage(std::cerr); + std::cerr << "Too few arguments" << std::endl; + exit(EXIT_FAILURE); + } + + config.port = strtol(argv[optind++], nullptr, 10); + + if (!config.no_tls) { + config.private_key_file = argv[optind++]; + config.cert_file = argv[optind++]; + } + + if (config.daemon) { + if (config.htdocs.empty()) { + print_usage(std::cerr); + std::cerr << "-d option must be specified when -D is used." << std::endl; + exit(EXIT_FAILURE); + } + if (daemon(0, 0) == -1) { + perror("daemon"); + exit(EXIT_FAILURE); + } + } + if (config.htdocs.empty()) { + config.htdocs = "./"; + } + + set_color_output(color || isatty(fileno(stdout))); + + struct sigaction act; + memset(&act, 0, sizeof(struct sigaction)); + act.sa_handler = SIG_IGN; + sigaction(SIGPIPE, &act, nullptr); + OPENSSL_config(nullptr); + OpenSSL_add_all_algorithms(); + SSL_load_error_strings(); + SSL_library_init(); +#ifndef NOTHREADS + ssl::LibsslGlobalLock lock; +#endif // NOTHREADS + + reset_timer(); + + HttpServer server(&config); + if (server.run() != 0) { + exit(EXIT_FAILURE); + } + return 0; +} + +} // namespace nghttp2 + +int main(int argc, char **argv) { return nghttp2::main(argc, argv); } diff --git a/src/shrpx-unittest.cc b/src/shrpx-unittest.cc new file mode 100644 index 0000000..86edb1e --- /dev/null +++ b/src/shrpx-unittest.cc @@ -0,0 +1,178 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2013 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifdef HAVE_CONFIG_H +#include +#endif // HAVE_CONFIG_H + +#include +#include +#include +#include +#include +// include test cases' include files here +#include "shrpx_ssl_test.h" +#include "shrpx_downstream_test.h" +#include "shrpx_config_test.h" +#include "http2_test.h" +#include "util_test.h" +#include "nghttp2_gzip_test.h" +#include "buffer_test.h" +#include "memchunk_test.h" +#include "shrpx_config.h" + +static int init_suite1(void) { return 0; } + +static int clean_suite1(void) { return 0; } + +int main(int argc, char *argv[]) { + CU_pSuite pSuite = NULL; + unsigned int num_tests_failed; + + OpenSSL_add_all_algorithms(); + SSL_load_error_strings(); + SSL_library_init(); + + shrpx::create_config(); + + // initialize the CUnit test registry + if (CUE_SUCCESS != CU_initialize_registry()) + return CU_get_error(); + + // add a suite to the registry + pSuite = CU_add_suite("shrpx_TestSuite", init_suite1, clean_suite1); + if (NULL == pSuite) { + CU_cleanup_registry(); + return CU_get_error(); + } + + // add the tests to the suite + if (!CU_add_test(pSuite, "ssl_create_lookup_tree", + shrpx::test_shrpx_ssl_create_lookup_tree) || + !CU_add_test(pSuite, "ssl_cert_lookup_tree_add_cert_from_file", + shrpx::test_shrpx_ssl_cert_lookup_tree_add_cert_from_file) || + !CU_add_test(pSuite, "http2_add_header", shrpx::test_http2_add_header) || + !CU_add_test(pSuite, "http2_get_header", shrpx::test_http2_get_header) || + !CU_add_test(pSuite, "http2_copy_headers_to_nva", + shrpx::test_http2_copy_headers_to_nva) || + !CU_add_test(pSuite, "http2_build_http1_headers_from_headers", + shrpx::test_http2_build_http1_headers_from_headers) || + !CU_add_test(pSuite, "http2_lws", shrpx::test_http2_lws) || + !CU_add_test(pSuite, "http2_rewrite_location_uri", + shrpx::test_http2_rewrite_location_uri) || + !CU_add_test(pSuite, "http2_parse_http_status_code", + shrpx::test_http2_parse_http_status_code) || + !CU_add_test(pSuite, "http2_index_header", + shrpx::test_http2_index_header) || + !CU_add_test(pSuite, "http2_lookup_token", + shrpx::test_http2_lookup_token) || + !CU_add_test(pSuite, "http2_check_http2_pseudo_header", + shrpx::test_http2_check_http2_pseudo_header) || + !CU_add_test(pSuite, "http2_http2_header_allowed", + shrpx::test_http2_http2_header_allowed) || + !CU_add_test(pSuite, "http2_mandatory_request_headers_presence", + shrpx::test_http2_mandatory_request_headers_presence) || + !CU_add_test(pSuite, "http2_parse_link_header", + shrpx::test_http2_parse_link_header) || + !CU_add_test(pSuite, "http2_path_join", shrpx::test_http2_path_join) || + !CU_add_test(pSuite, "downstream_index_request_headers", + shrpx::test_downstream_index_request_headers) || + !CU_add_test(pSuite, "downstream_index_response_headers", + shrpx::test_downstream_index_response_headers) || + !CU_add_test(pSuite, "downstream_get_request_header", + shrpx::test_downstream_get_request_header) || + !CU_add_test(pSuite, "downstream_get_response_header", + shrpx::test_downstream_get_response_header) || + !CU_add_test(pSuite, "downstream_crumble_request_cookie", + shrpx::test_downstream_crumble_request_cookie) || + !CU_add_test(pSuite, "downstream_assemble_request_cookie", + shrpx::test_downstream_assemble_request_cookie) || + !CU_add_test(pSuite, "downstream_rewrite_location_response_header", + shrpx::test_downstream_rewrite_location_response_header) || + !CU_add_test(pSuite, "config_parse_config_str_list", + shrpx::test_shrpx_config_parse_config_str_list) || + !CU_add_test(pSuite, "config_parse_header", + shrpx::test_shrpx_config_parse_header) || + !CU_add_test(pSuite, "config_parse_log_format", + shrpx::test_shrpx_config_parse_log_format) || + !CU_add_test(pSuite, "config_read_tls_ticket_key_file", + shrpx::test_shrpx_config_read_tls_ticket_key_file) || + !CU_add_test(pSuite, "util_streq", shrpx::test_util_streq) || + !CU_add_test(pSuite, "util_strieq", shrpx::test_util_strieq) || + !CU_add_test(pSuite, "util_inp_strlower", + shrpx::test_util_inp_strlower) || + !CU_add_test(pSuite, "util_to_base64", shrpx::test_util_to_base64) || + !CU_add_test(pSuite, "util_to_token68", shrpx::test_util_to_token68) || + !CU_add_test(pSuite, "util_percent_encode_token", + shrpx::test_util_percent_encode_token) || + !CU_add_test(pSuite, "util_percent_encode_path", + shrpx::test_util_percent_encode_path) || + !CU_add_test(pSuite, "util_percent_decode", + shrpx::test_util_percent_decode) || + !CU_add_test(pSuite, "util_quote_string", + shrpx::test_util_quote_string) || + !CU_add_test(pSuite, "util_utox", shrpx::test_util_utox) || + !CU_add_test(pSuite, "util_http_date", shrpx::test_util_http_date) || + !CU_add_test(pSuite, "util_select_h2", shrpx::test_util_select_h2) || + !CU_add_test(pSuite, "util_ipv6_numeric_addr", + shrpx::test_util_ipv6_numeric_addr) || + !CU_add_test(pSuite, "util_utos_with_unit", + shrpx::test_util_utos_with_unit) || + !CU_add_test(pSuite, "util_utos_with_funit", + shrpx::test_util_utos_with_funit) || + !CU_add_test(pSuite, "util_parse_uint_with_unit", + shrpx::test_util_parse_uint_with_unit) || + !CU_add_test(pSuite, "util_parse_uint", shrpx::test_util_parse_uint) || + !CU_add_test(pSuite, "util_parse_duration_with_unit", + shrpx::test_util_parse_duration_with_unit) || + !CU_add_test(pSuite, "util_duration_str", + shrpx::test_util_duration_str) || + !CU_add_test(pSuite, "util_format_duration", + shrpx::test_util_format_duration) || + !CU_add_test(pSuite, "util_starts_with", shrpx::test_util_starts_with) || + !CU_add_test(pSuite, "util_ends_with", shrpx::test_util_ends_with) || + !CU_add_test(pSuite, "gzip_inflate", test_nghttp2_gzip_inflate) || + !CU_add_test(pSuite, "buffer_write", nghttp2::test_buffer_write) || + !CU_add_test(pSuite, "pool_recycle", nghttp2::test_pool_recycle) || + !CU_add_test(pSuite, "memchunk_append", nghttp2::test_memchunks_append) || + !CU_add_test(pSuite, "memchunk_drain", nghttp2::test_memchunks_drain) || + !CU_add_test(pSuite, "memchunk_riovec", nghttp2::test_memchunks_riovec) || + !CU_add_test(pSuite, "memchunk_recycle", + nghttp2::test_memchunks_recycle)) { + CU_cleanup_registry(); + return CU_get_error(); + } + + // Run all tests using the CUnit Basic interface + CU_basic_set_mode(CU_BRM_VERBOSE); + CU_basic_run_tests(); + num_tests_failed = CU_get_number_of_tests_failed(); + CU_cleanup_registry(); + if (CU_get_error() == CUE_SUCCESS) { + return num_tests_failed; + } else { + printf("CUnit Error: %s\n", CU_get_error_msg()); + return CU_get_error(); + } +} diff --git a/src/shrpx.cc b/src/shrpx.cc new file mode 100644 index 0000000..5becb79 --- /dev/null +++ b/src/shrpx.cc @@ -0,0 +1,2165 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "shrpx.h" + +#include +#include +#ifdef HAVE_SYS_SOCKET_H +#include +#endif // HAVE_SYS_SOCKET_H +#include +#ifdef HAVE_NETDB_H +#include +#endif // HAVE_NETDB_H +#include +#ifdef HAVE_NETINET_IN_H +#include +#endif // HAVE_NETINET_IN_H +#include +#ifdef HAVE_ARPA_INET_H +#include +#endif // HAVE_ARPA_INET_H +#ifdef HAVE_UNISTD_H +#include +#endif // HAVE_UNISTD_H +#include +#ifdef HAVE_SYSLOG_H +#include +#endif // HAVE_SYSLOG_H +#include +#ifdef HAVE_LIMITS_H +#include +#endif // HAVE_LIMITS_H +#ifdef HAVE_SYS_TIME_H +#include +#endif // HAVE_SYS_TIME_H +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include + +#include "shrpx_config.h" +#include "shrpx_connection_handler.h" +#include "shrpx_ssl.h" +#include "shrpx_log_config.h" +#include "shrpx_worker.h" +#include "shrpx_accept_handler.h" +#include "shrpx_http2_upstream.h" +#include "shrpx_http2_session.h" +#include "util.h" +#include "app_helper.h" +#include "ssl.h" +#include "template.h" + +extern char **environ; + +using namespace nghttp2; + +namespace shrpx { + +namespace { +const int REOPEN_LOG_SIGNAL = SIGUSR1; +const int EXEC_BINARY_SIGNAL = SIGUSR2; +const int GRACEFUL_SHUTDOWN_SIGNAL = SIGQUIT; +} // namespace + +// Environment variables to tell new binary the listening socket's +// file descriptors. They are not close-on-exec. +#define ENV_LISTENER4_FD "NGHTTPX_LISTENER4_FD" +#define ENV_LISTENER6_FD "NGHTTPX_LISTENER6_FD" + +// Environment variable to tell new binary the port number the current +// binary is listening to. +#define ENV_PORT "NGHTTPX_PORT" + +// Environment variable to tell new binary the listening socket's file +// descriptor if frontend listens UNIX domain socket. +#define ENV_UNIX_FD "NGHTTP2_UNIX_FD" +// Environment variable to tell new binary the UNIX domain socket +// path. +#define ENV_UNIX_PATH "NGHTTP2_UNIX_PATH" + +namespace { +int resolve_hostname(sockaddr_union *addr, size_t *addrlen, + const char *hostname, uint16_t port, int family) { + addrinfo hints; + int rv; + + auto service = util::utos(port); + memset(&hints, 0, sizeof(addrinfo)); + + hints.ai_family = family; + hints.ai_socktype = SOCK_STREAM; +#ifdef AI_ADDRCONFIG + hints.ai_flags |= AI_ADDRCONFIG; +#endif // AI_ADDRCONFIG + addrinfo *res; + + rv = getaddrinfo(hostname, service.c_str(), &hints, &res); + if (rv != 0) { + LOG(FATAL) << "Unable to resolve address for " << hostname << ": " + << gai_strerror(rv); + return -1; + } + + char host[NI_MAXHOST]; + rv = getnameinfo(res->ai_addr, res->ai_addrlen, host, sizeof(host), 0, 0, + NI_NUMERICHOST); + if (rv != 0) { + LOG(FATAL) << "Address resolution for " << hostname + << " failed: " << gai_strerror(rv); + + freeaddrinfo(res); + + return -1; + } + + if (LOG_ENABLED(INFO)) { + LOG(INFO) << "Address resolution for " << hostname + << " succeeded: " << host; + } + + memcpy(addr, res->ai_addr, res->ai_addrlen); + *addrlen = res->ai_addrlen; + freeaddrinfo(res); + return 0; +} +} // namespace + +namespace { +void close_env_fd(std::initializer_list envnames) { + for (auto envname : envnames) { + auto envfd = getenv(envname); + if (!envfd) { + continue; + } + auto fd = strtol(envfd, nullptr, 10); + close(fd); + } +} +} // namespace + +namespace { +std::unique_ptr +create_unix_domain_acceptor(ConnectionHandler *handler) { + auto path = get_config()->host.get(); + auto pathlen = strlen(path); + { + auto envfd = getenv(ENV_UNIX_FD); + auto envpath = getenv(ENV_UNIX_PATH); + if (envfd && envpath) { + auto fd = strtoul(envfd, nullptr, 10); + + if (util::streq(envpath, path)) { + LOG(NOTICE) << "Listening on UNIX domain socket " << path; + + return make_unique(fd, handler); + } + + LOG(WARN) << "UNIX domain socket path was changed between old binary (" + << envpath << ") and new binary (" << path << ")"; + close(fd); + } + } + +#ifdef SOCK_NONBLOCK + auto fd = socket(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK, 0); + if (fd == -1) { + return nullptr; + } +#else // !SOCK_NONBLOCK + auto fd = socket(AF_UNIX, SOCK_STREAM, 0); + if (fd == -1) { + return nullptr; + } + util::make_socket_nonblocking(fd); +#endif // !SOCK_NONBLOCK + int val = 1; + if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &val, + static_cast(sizeof(val))) == -1) { + close(fd); + return nullptr; + } + + sockaddr_union addr; + addr.un.sun_family = AF_UNIX; + if (pathlen + 1 > sizeof(addr.un.sun_path)) { + LOG(FATAL) << "UNIX domain socket path " << path << " is too long > " + << sizeof(addr.un.sun_path); + close(fd); + return nullptr; + } + // copy path including terminal NULL + std::copy_n(path, pathlen + 1, addr.un.sun_path); + + // unlink (remove) already existing UNIX domain socket path + unlink(path); + + if (bind(fd, &addr.sa, sizeof(addr.un)) != 0) { + auto error = errno; + LOG(FATAL) << "Failed to bind UNIX domain socket, error=" << error; + close(fd); + return nullptr; + } + + if (listen(fd, get_config()->backlog) != 0) { + auto error = errno; + LOG(FATAL) << "Failed to listen to UNIX domain socket, error=" << error; + close(fd); + return nullptr; + } + + LOG(NOTICE) << "Listening on UNIX domain socket " << path; + + return make_unique(fd, handler); +} +} // namespace + +namespace { +std::unique_ptr create_acceptor(ConnectionHandler *handler, + int family) { + { + auto envfd = + getenv(family == AF_INET ? ENV_LISTENER4_FD : ENV_LISTENER6_FD); + auto envport = getenv(ENV_PORT); + + if (envfd && envport) { + auto fd = strtoul(envfd, nullptr, 10); + auto port = strtoul(envport, nullptr, 10); + + // Only do this iff NGHTTPX_PORT == get_config()->port. + // Otherwise, close fd, and create server socket as usual. + + if (port == get_config()->port) { + LOG(NOTICE) << "Listening on port " << get_config()->port; + + return make_unique(fd, handler); + } + + LOG(WARN) << "Port was changed between old binary (" << port + << ") and new binary (" << get_config()->port << ")"; + close(fd); + } + } + + addrinfo hints; + int fd = -1; + int rv; + + auto service = util::utos(get_config()->port); + memset(&hints, 0, sizeof(addrinfo)); + hints.ai_family = family; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_PASSIVE; +#ifdef AI_ADDRCONFIG + hints.ai_flags |= AI_ADDRCONFIG; +#endif // AI_ADDRCONFIG + + auto node = strcmp("*", get_config()->host.get()) == 0 + ? nullptr + : get_config()->host.get(); + + addrinfo *res, *rp; + rv = getaddrinfo(node, service.c_str(), &hints, &res); + if (rv != 0) { + if (LOG_ENABLED(INFO)) { + LOG(INFO) << "Unable to get IPv" << (family == AF_INET ? "4" : "6") + << " address for " << get_config()->host.get() << ": " + << gai_strerror(rv); + } + return nullptr; + } + for (rp = res; rp; rp = rp->ai_next) { +#ifdef SOCK_NONBLOCK + fd = + socket(rp->ai_family, rp->ai_socktype | SOCK_NONBLOCK, rp->ai_protocol); + if (fd == -1) { + continue; + } +#else // !SOCK_NONBLOCK + fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); + if (fd == -1) { + continue; + } + util::make_socket_nonblocking(fd); +#endif // !SOCK_NONBLOCK + int val = 1; + if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &val, + static_cast(sizeof(val))) == -1) { + close(fd); + continue; + } + +#ifdef IPV6_V6ONLY + if (family == AF_INET6) { + if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &val, + static_cast(sizeof(val))) == -1) { + close(fd); + continue; + } + } +#endif // IPV6_V6ONLY + +#ifdef TCP_DEFER_ACCEPT + val = 3; + if (setsockopt(fd, IPPROTO_TCP, TCP_DEFER_ACCEPT, &val, + static_cast(sizeof(val))) == -1) { + LOG(WARN) << "Failed to set TCP_DEFER_ACCEPT option to listener socket"; + } +#endif // TCP_DEFER_ACCEPT + + if (bind(fd, rp->ai_addr, rp->ai_addrlen) == 0 && + listen(fd, get_config()->backlog) == 0) { + break; + } + close(fd); + } + + if (!rp) { + LOG(WARN) << "Listening " << (family == AF_INET ? "IPv4" : "IPv6") + << " socket failed"; + + freeaddrinfo(res); + + return nullptr; + } + + char host[NI_MAXHOST]; + rv = getnameinfo(rp->ai_addr, rp->ai_addrlen, host, sizeof(host), nullptr, 0, + NI_NUMERICHOST); + + freeaddrinfo(res); + + if (rv != 0) { + LOG(WARN) << gai_strerror(rv); + + close(fd); + + return nullptr; + } + + LOG(NOTICE) << "Listening on " << host << ", port " << get_config()->port; + + return make_unique(fd, handler); +} +} // namespace + +namespace { +void drop_privileges() { + if (getuid() == 0 && get_config()->uid != 0) { + if (initgroups(get_config()->user.get(), get_config()->gid) != 0) { + auto error = errno; + LOG(FATAL) << "Could not change supplementary groups: " + << strerror(error); + exit(EXIT_FAILURE); + } + if (setgid(get_config()->gid) != 0) { + auto error = errno; + LOG(FATAL) << "Could not change gid: " << strerror(error); + exit(EXIT_FAILURE); + } + if (setuid(get_config()->uid) != 0) { + auto error = errno; + LOG(FATAL) << "Could not change uid: " << strerror(error); + exit(EXIT_FAILURE); + } + if (setuid(0) != -1) { + LOG(FATAL) << "Still have root privileges?"; + exit(EXIT_FAILURE); + } + } +} +} // namespace + +namespace { +void save_pid() { + std::ofstream out(get_config()->pid_file.get(), std::ios::binary); + out << get_config()->pid << "\n"; + out.close(); + if (!out) { + LOG(ERROR) << "Could not save PID to file " << get_config()->pid_file.get(); + exit(EXIT_FAILURE); + } + + if (get_config()->uid != 0) { + if (chown(get_config()->pid_file.get(), get_config()->uid, + get_config()->gid) == -1) { + auto error = errno; + LOG(WARN) << "Changing owner of pid file " << get_config()->pid_file.get() + << " failed: " << strerror(error); + } + } +} +} // namespace + +namespace { +void reopen_log_signal_cb(struct ev_loop *loop, ev_signal *w, int revents) { + auto conn_handler = static_cast(w->data); + + LOG(NOTICE) << "Reopening log files: main"; + + (void)reopen_log_files(); + redirect_stderr_to_errorlog(); + + if (get_config()->num_worker > 1) { + conn_handler->worker_reopen_log_files(); + } +} +} // namespace + +namespace { +void exec_binary_signal_cb(struct ev_loop *loop, ev_signal *w, int revents) { + auto conn_handler = static_cast(w->data); + + LOG(NOTICE) << "Executing new binary"; + + auto pid = fork(); + + if (pid == -1) { + auto error = errno; + LOG(ERROR) << "fork() failed errno=" << error; + return; + } + + if (pid != 0) { + return; + } + + auto exec_path = util::get_exec_path(get_config()->argc, get_config()->argv, + get_config()->cwd); + + if (!exec_path) { + LOG(ERROR) << "Could not resolve the executable path"; + return; + } + + auto argv = make_unique(get_config()->argc + 1); + + argv[0] = exec_path; + for (int i = 1; i < get_config()->argc; ++i) { + argv[i] = strdup(get_config()->argv[i]); + } + argv[get_config()->argc] = nullptr; + + size_t envlen = 0; + for (char **p = environ; *p; ++p, ++envlen) + ; + // 3 for missing (fd4, fd6 and port) or (unix fd and unix path) + auto envp = make_unique(envlen + 3 + 1); + size_t envidx = 0; + + if (get_config()->host_unix) { + auto acceptor = conn_handler->get_acceptor(); + std::string fd = ENV_UNIX_FD "="; + fd += util::utos(acceptor->get_fd()); + envp[envidx++] = strdup(fd.c_str()); + + std::string path = ENV_UNIX_PATH "="; + path += get_config()->host.get(); + envp[envidx++] = strdup(path.c_str()); + } else { + auto acceptor4 = conn_handler->get_acceptor(); + if (acceptor4) { + std::string fd4 = ENV_LISTENER4_FD "="; + fd4 += util::utos(acceptor4->get_fd()); + envp[envidx++] = strdup(fd4.c_str()); + } + + auto acceptor6 = conn_handler->get_acceptor6(); + if (acceptor6) { + std::string fd6 = ENV_LISTENER6_FD "="; + fd6 += util::utos(acceptor6->get_fd()); + envp[envidx++] = strdup(fd6.c_str()); + } + + std::string port = ENV_PORT "="; + port += util::utos(get_config()->port); + envp[envidx++] = strdup(port.c_str()); + } + + for (size_t i = 0; i < envlen; ++i) { + if (util::startsWith(environ[i], ENV_LISTENER4_FD) || + util::startsWith(environ[i], ENV_LISTENER6_FD) || + util::startsWith(environ[i], ENV_PORT) || + util::startsWith(environ[i], ENV_UNIX_FD) || + util::startsWith(environ[i], ENV_UNIX_PATH)) { + continue; + } + + envp[envidx++] = environ[i]; + } + + envp[envidx++] = nullptr; + + if (LOG_ENABLED(INFO)) { + LOG(INFO) << "cmdline"; + for (int i = 0; argv[i]; ++i) { + LOG(INFO) << i << ": " << argv[i]; + } + LOG(INFO) << "environ"; + for (int i = 0; envp[i]; ++i) { + LOG(INFO) << i << ": " << envp[i]; + } + } + + if (execve(argv[0], argv.get(), envp.get()) == -1) { + auto error = errno; + LOG(ERROR) << "execve failed: errno=" << error; + _Exit(EXIT_FAILURE); + } +} +} // namespace + +namespace { +void graceful_shutdown_signal_cb(struct ev_loop *loop, ev_signal *w, + int revents) { + auto conn_handler = static_cast(w->data); + + if (conn_handler->get_graceful_shutdown()) { + return; + } + + LOG(NOTICE) << "Graceful shutdown signal received"; + + conn_handler->set_graceful_shutdown(true); + + conn_handler->disable_acceptor(); + + // After disabling accepting new connection, disptach incoming + // connection in backlog. + + conn_handler->accept_pending_connection(); + + conn_handler->graceful_shutdown_worker(); + + if (get_config()->num_worker == 1 && + conn_handler->get_single_worker()->get_worker_stat()->num_connections > + 0) { + return; + } + + // We have accepted all pending connections. Shutdown main event + // loop. + ev_break(loop); +} +} // namespace + +namespace { +void renew_ticket_key_cb(struct ev_loop *loop, ev_timer *w, int revents) { + auto conn_handler = static_cast(w->data); + const auto &old_ticket_keys = conn_handler->get_ticket_keys(); + + auto ticket_keys = std::make_shared(); + LOG(NOTICE) << "Renew ticket keys: main"; + + // We store at most 2 ticket keys + if (old_ticket_keys) { + auto &old_keys = old_ticket_keys->keys; + auto &new_keys = ticket_keys->keys; + + assert(!old_keys.empty()); + + new_keys.resize(2); + new_keys[1] = old_keys[0]; + } else { + ticket_keys->keys.resize(1); + } + + if (RAND_bytes(reinterpret_cast(&ticket_keys->keys[0]), + sizeof(ticket_keys->keys[0])) == 0) { + if (LOG_ENABLED(INFO)) { + LOG(INFO) << "failed to renew ticket key"; + } + return; + } + + if (LOG_ENABLED(INFO)) { + LOG(INFO) << "ticket keys generation done"; + for (auto &key : ticket_keys->keys) { + LOG(INFO) << "name: " << util::format_hex(key.name, sizeof(key.name)); + } + } + + conn_handler->set_ticket_keys(ticket_keys); + conn_handler->worker_renew_ticket_keys(ticket_keys); +} +} // namespace + +namespace { +int event_loop() { + auto loop = EV_DEFAULT; + + auto conn_handler = make_unique(loop); + if (get_config()->daemon) { + if (daemon(0, 0) == -1) { + auto error = errno; + LOG(FATAL) << "Failed to daemonize: " << strerror(error); + exit(EXIT_FAILURE); + } + + // We get new PID after successful daemon(). + mod_config()->pid = getpid(); + + // daemon redirects stderr file descriptor to /dev/null, so we + // need this. + redirect_stderr_to_errorlog(); + } + + if (get_config()->pid_file) { + save_pid(); + } + + if (get_config()->host_unix) { + close_env_fd({ENV_LISTENER4_FD, ENV_LISTENER6_FD}); + auto acceptor = create_unix_domain_acceptor(conn_handler.get()); + if (!acceptor) { + LOG(FATAL) << "Failed to listen on UNIX domain socket " + << get_config()->host.get(); + exit(EXIT_FAILURE); + } + + conn_handler->set_acceptor(std::move(acceptor)); + } else { + close_env_fd({ENV_UNIX_FD}); + auto acceptor6 = create_acceptor(conn_handler.get(), AF_INET6); + auto acceptor4 = create_acceptor(conn_handler.get(), AF_INET); + if (!acceptor6 && !acceptor4) { + LOG(FATAL) << "Failed to listen on address " << get_config()->host.get() + << ", port " << get_config()->port; + exit(EXIT_FAILURE); + } + + conn_handler->set_acceptor(std::move(acceptor4)); + conn_handler->set_acceptor6(std::move(acceptor6)); + } + + ev_timer renew_ticket_key_timer; + if (!get_config()->upstream_no_tls) { + bool auto_tls_ticket_key = true; + if (!get_config()->tls_ticket_key_files.empty()) { + auto ticket_keys = + read_tls_ticket_key_file(get_config()->tls_ticket_key_files); + if (!ticket_keys) { + LOG(WARN) << "Use internal session ticket key generator"; + } else { + conn_handler->set_ticket_keys(std::move(ticket_keys)); + auto_tls_ticket_key = false; + } + } + if (auto_tls_ticket_key) { + // Renew ticket key every 12hrs + ev_timer_init(&renew_ticket_key_timer, renew_ticket_key_cb, 0., + 12 * 3600.); + renew_ticket_key_timer.data = conn_handler.get(); + ev_timer_again(loop, &renew_ticket_key_timer); + + // Generate first session ticket key before running workers. + renew_ticket_key_cb(loop, &renew_ticket_key_timer, 0); + } + } + + // ListenHandler loads private key, and we listen on a priveleged port. + // After that, we drop the root privileges if needed. + drop_privileges(); + +#ifndef NOTHREADS + int rv; + sigset_t signals; + sigemptyset(&signals); + sigaddset(&signals, REOPEN_LOG_SIGNAL); + sigaddset(&signals, EXEC_BINARY_SIGNAL); + sigaddset(&signals, GRACEFUL_SHUTDOWN_SIGNAL); + rv = pthread_sigmask(SIG_BLOCK, &signals, nullptr); + if (rv != 0) { + LOG(ERROR) << "Blocking signals failed: " << strerror(rv); + } +#endif // !NOTHREADS + + if (get_config()->num_worker == 1) { + conn_handler->create_single_worker(); + } else { + conn_handler->create_worker_thread(get_config()->num_worker); + } + +#ifndef NOTHREADS + rv = pthread_sigmask(SIG_UNBLOCK, &signals, nullptr); + if (rv != 0) { + LOG(ERROR) << "Unblocking signals failed: " << strerror(rv); + } +#endif // !NOTHREADS + + ev_signal reopen_log_sig; + ev_signal_init(&reopen_log_sig, reopen_log_signal_cb, REOPEN_LOG_SIGNAL); + reopen_log_sig.data = conn_handler.get(); + ev_signal_start(loop, &reopen_log_sig); + + ev_signal exec_bin_sig; + ev_signal_init(&exec_bin_sig, exec_binary_signal_cb, EXEC_BINARY_SIGNAL); + exec_bin_sig.data = conn_handler.get(); + ev_signal_start(loop, &exec_bin_sig); + + ev_signal graceful_shutdown_sig; + ev_signal_init(&graceful_shutdown_sig, graceful_shutdown_signal_cb, + GRACEFUL_SHUTDOWN_SIGNAL); + graceful_shutdown_sig.data = conn_handler.get(); + ev_signal_start(loop, &graceful_shutdown_sig); + + if (!get_config()->upstream_no_tls && !get_config()->no_ocsp) { + conn_handler->proceed_next_cert_ocsp(); + } + + if (LOG_ENABLED(INFO)) { + LOG(INFO) << "Entering event loop"; + } + + ev_run(loop, 0); + + conn_handler->join_worker(); + conn_handler->cancel_ocsp_update(); + + return 0; +} +} // namespace + +namespace { +// Returns true if regular file or symbolic link |path| exists. +bool conf_exists(const char *path) { + struct stat buf; + int rv = stat(path, &buf); + return rv == 0 && (buf.st_mode & (S_IFREG | S_IFLNK)); +} +} // namespace + +namespace { +const char *DEFAULT_NPN_LIST = "h2,h2-16,h2-14," +#ifdef HAVE_SPDYLAY + "spdy/3.1," +#endif // HAVE_SPDYLAY + "http/1.1"; +} // namespace + +namespace { +const char *DEFAULT_TLS_PROTO_LIST = "TLSv1.2,TLSv1.1"; +} // namespace + +namespace { +const char *DEFAULT_ACCESSLOG_FORMAT = "$remote_addr - - [$time_local] " + "\"$request\" $status $body_bytes_sent " + "\"$http_referer\" \"$http_user_agent\""; +} // namespace + +namespace { +auto DEFAULT_DOWNSTREAM_HOST = "127.0.0.1"; +int16_t DEFAULT_DOWNSTREAM_PORT = 80; +} // namespace; + +namespace { +void fill_default_config() { + memset(mod_config(), 0, sizeof(*mod_config())); + + mod_config()->verbose = false; + mod_config()->daemon = false; + + mod_config()->server_name = "nghttpx nghttp2/" NGHTTP2_VERSION; + mod_config()->host = strcopy("*"); + mod_config()->port = 3000; + mod_config()->private_key_file = nullptr; + mod_config()->private_key_passwd = nullptr; + mod_config()->cert_file = nullptr; + + // Read timeout for HTTP2 upstream connection + mod_config()->http2_upstream_read_timeout = 180.; + + // Read timeout for non-HTTP2 upstream connection + mod_config()->upstream_read_timeout = 180.; + + // Write timeout for HTTP2/non-HTTP2 upstream connection + mod_config()->upstream_write_timeout = 30.; + + // Read/Write timeouts for downstream connection + mod_config()->downstream_read_timeout = 180.; + mod_config()->downstream_write_timeout = 30.; + + // Read timeout for HTTP/2 stream + mod_config()->stream_read_timeout = 0.; + + // Write timeout for HTTP/2 stream + mod_config()->stream_write_timeout = 0.; + + // Timeout for pooled (idle) connections + mod_config()->downstream_idle_read_timeout = 2.; + + // window bits for HTTP/2 and SPDY upstream/downstream connection + // per stream. 2**16-1 = 64KiB-1, which is HTTP/2 default. Please + // note that SPDY/3 default is 64KiB. + mod_config()->http2_upstream_window_bits = 16; + mod_config()->http2_downstream_window_bits = 16; + + // HTTP/2 SPDY/3.1 has connection-level flow control. The default + // window size for HTTP/2 is 64KiB - 1. SPDY/3's default is 64KiB + mod_config()->http2_upstream_connection_window_bits = 16; + mod_config()->http2_downstream_connection_window_bits = 16; + + mod_config()->upstream_no_tls = false; + mod_config()->downstream_no_tls = false; + + mod_config()->num_worker = 1; + mod_config()->http2_max_concurrent_streams = 100; + mod_config()->add_x_forwarded_for = false; + mod_config()->strip_incoming_x_forwarded_for = false; + mod_config()->no_via = false; + mod_config()->accesslog_file = nullptr; + mod_config()->accesslog_syslog = false; + mod_config()->accesslog_format = parse_log_format(DEFAULT_ACCESSLOG_FORMAT); +#if defined(__ANDROID__) || defined(ANDROID) + // Android does not have /dev/stderr. Use /proc/self/fd/2 instead. + mod_config()->errorlog_file = strcopy("/proc/self/fd/2"); +#else // !__ANDROID__ && ANDROID + mod_config()->errorlog_file = strcopy("/dev/stderr"); +#endif // !__ANDROID__ && ANDROID + mod_config()->errorlog_syslog = false; + mod_config()->conf_path = strcopy("/etc/nghttpx/nghttpx.conf"); + mod_config()->syslog_facility = LOG_DAEMON; + // Default accept() backlog + mod_config()->backlog = 512; + mod_config()->ciphers = nullptr; + mod_config()->http2_proxy = false; + mod_config()->http2_bridge = false; + mod_config()->client_proxy = false; + mod_config()->client = false; + mod_config()->client_mode = false; + mod_config()->insecure = false; + mod_config()->cacert = nullptr; + mod_config()->pid_file = nullptr; + mod_config()->user = nullptr; + mod_config()->uid = 0; + mod_config()->gid = 0; + mod_config()->pid = getpid(); + mod_config()->backend_ipv4 = false; + mod_config()->backend_ipv6 = false; + mod_config()->downstream_http_proxy_userinfo = nullptr; + mod_config()->downstream_http_proxy_host = nullptr; + mod_config()->downstream_http_proxy_port = 0; + mod_config()->downstream_http_proxy_addrlen = 0; + mod_config()->read_rate = 0; + mod_config()->read_burst = 0; + mod_config()->write_rate = 0; + mod_config()->write_burst = 0; + mod_config()->worker_read_rate = 0; + mod_config()->worker_read_burst = 0; + mod_config()->worker_write_rate = 0; + mod_config()->worker_write_burst = 0; + mod_config()->verify_client = false; + mod_config()->verify_client_cacert = nullptr; + mod_config()->client_private_key_file = nullptr; + mod_config()->client_cert_file = nullptr; + mod_config()->http2_upstream_dump_request_header = nullptr; + mod_config()->http2_upstream_dump_response_header = nullptr; + mod_config()->http2_no_cookie_crumbling = false; + mod_config()->upstream_frame_debug = false; + mod_config()->padding = 0; + mod_config()->worker_frontend_connections = 0; + + mod_config()->http2_upstream_callbacks = create_http2_upstream_callbacks(); + mod_config()->http2_downstream_callbacks = + create_http2_downstream_callbacks(); + + nghttp2_option_new(&mod_config()->http2_option); + nghttp2_option_set_no_auto_window_update(get_config()->http2_option, 1); + nghttp2_option_set_no_recv_client_magic(get_config()->http2_option, 1); + + nghttp2_option_new(&mod_config()->http2_client_option); + nghttp2_option_set_no_auto_window_update(get_config()->http2_client_option, + 1); + nghttp2_option_set_peer_max_concurrent_streams( + get_config()->http2_client_option, 100); + + mod_config()->tls_proto_mask = 0; + mod_config()->no_location_rewrite = false; + mod_config()->no_host_rewrite = false; + mod_config()->argc = 0; + mod_config()->argv = nullptr; + mod_config()->downstream_connections_per_host = 8; + mod_config()->downstream_connections_per_frontend = 0; + mod_config()->listener_disable_timeout = 0.; + mod_config()->downstream_request_buffer_size = 16 * 1024; + mod_config()->downstream_response_buffer_size = 16 * 1024; + mod_config()->no_server_push = false; + mod_config()->host_unix = false; + mod_config()->http2_downstream_connections_per_worker = 0; + // ocsp update interval = 14400 secs = 4 hours, borrowed from h2o + mod_config()->ocsp_update_interval = 14400.; + mod_config()->fetch_ocsp_response_file = + strcopy(PKGDATADIR "/fetch-ocsp-response"); + mod_config()->no_ocsp = false; + mod_config()->header_field_buffer = 64 * 1024; + mod_config()->max_header_fields = 100; +} +} // namespace + +namespace { +void print_version(std::ostream &out) { + out << get_config()->server_name << std::endl; +} +} // namespace + +namespace { +void print_usage(std::ostream &out) { + out << R"(Usage: nghttpx [OPTIONS]... [ ] +A reverse proxy for HTTP/2, HTTP/1 and SPDY.)" << std::endl; +} +} // namespace + +namespace { +void print_help(std::ostream &out) { + print_usage(out); + out << R"( + + Set path to server's private key. Required unless -p, + --client or --frontend-no-tls are given. + Set path to server's certificate. Required unless -p, + --client or --frontend-no-tls are given. To make OCSP + stapling work, this must be absolute path. + +Options: + The options are categorized into several groups. + +Connections: + -b, --backend= + Set backend host and port. The multiple backend + addresses are accepted by repeating this option. UNIX + domain socket can be specified by prefixing path name + with "unix:" (e.g., unix:/var/run/backend.sock) + Default: )" << DEFAULT_DOWNSTREAM_HOST << "," + << DEFAULT_DOWNSTREAM_PORT << R"( + -f, --frontend= + Set frontend host and port. If is '*', it + assumes all addresses including both IPv4 and IPv6. + UNIX domain socket can be specified by prefixing path + name with "unix:" (e.g., unix:/var/run/nghttpx.sock) + Default: )" << get_config()->host.get() << "," + << get_config()->port << R"( + --backlog= + Set listen backlog size. + Default: )" << get_config()->backlog << R"( + --backend-ipv4 + Resolve backend hostname to IPv4 address only. + --backend-ipv6 + Resolve backend hostname to IPv6 address only. + --backend-http-proxy-uri= + Specify proxy URI in the form + http://[:@]:. If a proxy + requires authentication, specify and . + Note that they must be properly percent-encoded. This + proxy is used when the backend connection is HTTP/2. + First, make a CONNECT request to the proxy and it + connects to the backend on behalf of nghttpx. This + forms tunnel. After that, nghttpx performs SSL/TLS + handshake with the downstream through the tunnel. The + timeouts when connecting and making CONNECT request can + be specified by --backend-read-timeout and + --backend-write-timeout options. + +Performance: + -n, --workers= + Set the number of worker threads. + Default: )" << get_config()->num_worker << R"( + --read-rate= + Set maximum average read rate on frontend connection. + Setting 0 to this option means read rate is unlimited. + Default: )" << get_config()->read_rate << R"( + --read-burst= + Set maximum read burst size on frontend connection. + Setting 0 to this option means read burst size is + unlimited. + Default: )" << get_config()->read_burst << R"( + --write-rate= + Set maximum average write rate on frontend connection. + Setting 0 to this option means write rate is unlimited. + Default: )" << get_config()->write_rate << R"( + --write-burst= + Set maximum write burst size on frontend connection. + Setting 0 to this option means write burst size is + unlimited. + Default: )" << get_config()->write_burst << R"( + --worker-read-rate= + Set maximum average read rate on frontend connection per + worker. Setting 0 to this option means read rate is + unlimited. Not implemented yet. + Default: )" << get_config()->worker_read_rate << R"( + --worker-read-burst= + Set maximum read burst size on frontend connection per + worker. Setting 0 to this option means read burst size + is unlimited. Not implemented yet. + Default: )" << get_config()->worker_read_burst << R"( + --worker-write-rate= + Set maximum average write rate on frontend connection + per worker. Setting 0 to this option means write rate + is unlimited. Not implemented yet. + Default: )" << get_config()->worker_write_rate << R"( + --worker-write-burst= + Set maximum write burst size on frontend connection per + worker. Setting 0 to this option means write burst size + is unlimited. Not implemented yet. + Default: )" << get_config()->worker_write_burst << R"( + --worker-frontend-connections= + Set maximum number of simultaneous connections frontend + accepts. Setting 0 means unlimited. + Default: )" << get_config()->worker_frontend_connections << R"( + --backend-http2-connections-per-worker= + Set maximum number of HTTP/2 connections per worker. + The default value is 0, which means the number of + backend addresses specified by -b option. + --backend-http1-connections-per-host= + Set maximum number of backend concurrent HTTP/1 + connections per host. This option is meaningful when -s + option is used. To limit the number of connections per + frontend for default mode, use + --backend-http1-connections-per-frontend. + Default: )" << get_config()->downstream_connections_per_host + << R"( + --backend-http1-connections-per-frontend= + Set maximum number of backend concurrent HTTP/1 + connections per frontend. This option is only used for + default mode. 0 means unlimited. To limit the number + of connections per host for HTTP/2 or SPDY proxy mode + (-s option), use --backend-http1-connections-per-host. + Default: )" << get_config()->downstream_connections_per_frontend + << R"( + --rlimit-nofile= + Set maximum number of open files (RLIMIT_NOFILE) to . + If 0 is given, nghttpx does not set the limit. + Default: )" << get_config()->rlimit_nofile << R"( + --backend-request-buffer= + Set buffer size used to store backend request. + Default: )" + << util::utos_with_unit(get_config()->downstream_request_buffer_size) + << R"( + --backend-response-buffer= + Set buffer size used to store backend response. + Default: )" + << util::utos_with_unit(get_config()->downstream_response_buffer_size) + << R"( + +Timeout: + --frontend-http2-read-timeout= + Specify read timeout for HTTP/2 and SPDY frontend + connection. + Default: )" + << util::duration_str(get_config()->http2_upstream_read_timeout) << R"( + --frontend-read-timeout= + Specify read timeout for HTTP/1.1 frontend connection. + Default: )" + << util::duration_str(get_config()->upstream_read_timeout) << R"( + --frontend-write-timeout= + Specify write timeout for all frontend connections. + Default: )" + << util::duration_str(get_config()->upstream_write_timeout) << R"( + --stream-read-timeout= + Specify read timeout for HTTP/2 and SPDY streams. 0 + means no timeout. + Default: )" + << util::duration_str(get_config()->stream_read_timeout) << R"( + --stream-write-timeout= + Specify write timeout for HTTP/2 and SPDY streams. 0 + means no timeout. + Default: )" + << util::duration_str(get_config()->stream_write_timeout) << R"( + --backend-read-timeout= + Specify read timeout for backend connection. + Default: )" + << util::duration_str(get_config()->downstream_read_timeout) << R"( + --backend-write-timeout= + Specify write timeout for backend connection. + Default: )" + << util::duration_str(get_config()->downstream_write_timeout) << R"( + --backend-keep-alive-timeout= + Specify keep-alive timeout for backend connection. + Default: )" + << util::duration_str(get_config()->downstream_idle_read_timeout) << R"( + --listener-disable-timeout= + After accepting connection failed, connection listener + is disabled for a given amount of time. Specifying 0 + disables this feature. + Default: )" + << util::duration_str(get_config()->listener_disable_timeout) << R"( + +SSL/TLS: + --ciphers= + Set allowed cipher list. The format of the string is + described in OpenSSL ciphers(1). + -k, --insecure + Don't verify backend server's certificate if -p, + --client or --http2-bridge are given and + --backend-no-tls is not given. + --cacert= + Set path to trusted CA certificate file if -p, --client + or --http2-bridge are given and --backend-no-tls is not + given. The file must be in PEM format. It can contain + multiple certificates. If the linked OpenSSL is + configured to load system wide certificates, they are + loaded at startup regardless of this option. + --private-key-passwd-file= + Path to file that contains password for the server's + private key. If none is given and the private key is + password protected it'll be requested interactively. + --subcert=: + Specify additional certificate and private key file. + nghttpx will choose certificates based on the hostname + indicated by client using TLS SNI extension. This + option can be used multiple times. To make OCSP + stapling work, must be absolute path. + --backend-tls-sni-field= + Explicitly set the content of the TLS SNI extension. + This will default to the backend HOST name. + --dh-param-file= + Path to file that contains DH parameters in PEM format. + Without this option, DHE cipher suites are not + available. + --npn-list= + Comma delimited list of ALPN protocol identifier sorted + in the order of preference. That means most desirable + protocol comes first. This is used in both ALPN and + NPN. The parameter must be delimited by a single comma + only and any white spaces are treated as a part of + protocol string. + Default: )" << DEFAULT_NPN_LIST << R"( + --verify-client + Require and verify client certificate. + --verify-client-cacert= + Path to file that contains CA certificates to verify + client certificate. The file must be in PEM format. It + can contain multiple certificates. + --client-private-key-file= + Path to file that contains client private key used in + backend client authentication. + --client-cert-file= + Path to file that contains client certificate used in + backend client authentication. + --tls-proto-list= + Comma delimited list of SSL/TLS protocol to be enabled. + The following protocols are available: TLSv1.2, TLSv1.1 + and TLSv1.0. The name matching is done in + case-insensitive manner. The parameter must be + delimited by a single comma only and any white spaces + are treated as a part of protocol string. + Default: )" << DEFAULT_TLS_PROTO_LIST << R"( + --tls-ticket-key-file= + Path to file that contains 48 bytes random data to + construct TLS session ticket parameters. This options + can be used repeatedly to specify multiple ticket + parameters. If several files are given, only the first + key is used to encrypt TLS session tickets. Other keys + are accepted but server will issue new session ticket + with first key. This allows session key rotation. + Please note that key rotation does not occur + automatically. User should rearrange files or change + options values and restart nghttpx gracefully. If + opening or reading given file fails, all loaded keys are + discarded and it is treated as if none of this option is + given. If this option is not given or an error occurred + while opening or reading a file, key is generated + automatically and renewed every 12hrs. At most 2 keys + are stored in memory. + --fetch-ocsp-response-file= + Path to fetch-ocsp-response script file. It should be + absolute path. + Default: )" << get_config()->fetch_ocsp_response_file.get() << R"( + --ocsp-update-interval= + Set interval to update OCSP response cache. + Default: )" + << util::duration_str(get_config()->ocsp_update_interval) << R"( + --no-ocsp Disable OCSP stapling. + +HTTP/2 and SPDY: + -c, --http2-max-concurrent-streams= + Set the maximum number of the concurrent streams in one + HTTP/2 and SPDY session. + Default: )" << get_config()->http2_max_concurrent_streams << R"( + --frontend-http2-window-bits= + Sets the per-stream initial window size of HTTP/2 SPDY + frontend connection. For HTTP/2, the size is 2**-1. + For SPDY, the size is 2**. + Default: )" << get_config()->http2_upstream_window_bits << R"( + --frontend-http2-connection-window-bits= + Sets the per-connection window size of HTTP/2 and SPDY + frontend connection. For HTTP/2, the size is + 2**-1. For SPDY, the size is 2**. + Default: )" << get_config()->http2_upstream_connection_window_bits + << R"( + --frontend-no-tls + Disable SSL/TLS on frontend connections. + --backend-http2-window-bits= + Sets the initial window size of HTTP/2 backend + connection to 2**-1. + Default: )" << get_config()->http2_downstream_window_bits << R"( + --backend-http2-connection-window-bits= + Sets the per-connection window size of HTTP/2 backend + connection to 2**-1. + Default: )" + << get_config()->http2_downstream_connection_window_bits << R"( + --backend-no-tls + Disable SSL/TLS on backend connections. + --http2-no-cookie-crumbling + Don't crumble cookie header field. + --padding= + Add at most bytes to a HTTP/2 frame payload as + padding. Specify 0 to disable padding. This option is + meant for debugging purpose and not intended to enhance + protocol security. + --no-server-push + Disable HTTP/2 server push. Server push is only + supported by default mode and HTTP/2 frontend. SPDY + frontend does not support server push. + +Mode: + (default mode) + Accept HTTP/2, SPDY and HTTP/1.1 over SSL/TLS. If + --frontend-no-tls is used, accept HTTP/2 and HTTP/1.1. + The incoming HTTP/1.1 connection can be upgraded to + HTTP/2 through HTTP Upgrade. The protocol to the + backend is HTTP/1.1. + -s, --http2-proxy + Like default mode, but enable secure proxy mode. + --http2-bridge + Like default mode, but communicate with the backend in + HTTP/2 over SSL/TLS. Thus the incoming all connections + are converted to HTTP/2 connection and relayed to the + backend. See --backend-http-proxy-uri option if you are + behind the proxy and want to connect to the outside + HTTP/2 proxy. + --client Accept HTTP/2 and HTTP/1.1 without SSL/TLS. The + incoming HTTP/1.1 connection can be upgraded to HTTP/2 + connection through HTTP Upgrade. The protocol to the + backend is HTTP/2. To use nghttpx as a forward proxy, + use -p option instead. + -p, --client-proxy + Like --client option, but it also requires the request + path from frontend must be an absolute URI, suitable for + use as a forward proxy. + +Logging: + -L, --log-level= + Set the severity level of log output. must be + one of INFO, NOTICE, WARN, ERROR and FATAL. + Default: NOTICE + --accesslog-file= + Set path to write access log. To reopen file, send USR1 + signal to nghttpx. + --accesslog-syslog + Send access log to syslog. If this option is used, + --accesslog-file option is ignored. + --accesslog-format= + Specify format string for access log. The default + format is combined format. The following variables are + available: + + * $remote_addr: client IP address. + * $time_local: local time in Common Log format. + * $time_iso8601: local time in ISO 8601 format. + * $request: HTTP request line. + * $status: HTTP response status code. + * $body_bytes_sent: the number of bytes sent to client + as response body. + * $http_: value of HTTP request header where + '_' in is replaced with '-'. + * $remote_port: client port. + * $server_port: server port. + * $request_time: request processing time in seconds with + milliseconds resolution. + * $pid: PID of the running process. + * $alpn: ALPN identifier of the protocol which generates + the response. For HTTP/1, ALPN is always http/1.1, + regardless of minor version. + + Default: )" << DEFAULT_ACCESSLOG_FORMAT << R"( + --errorlog-file= + Set path to write error log. To reopen file, send USR1 + signal to nghttpx. stderr will be redirected to the + error log file unless --errorlog-syslog is used. + Default: )" << get_config()->errorlog_file.get() << R"( + --errorlog-syslog + Send error log to syslog. If this option is used, + --errorlog-file option is ignored. + --syslog-facility= + Set syslog facility to . + Default: )" << str_syslog_facility(get_config()->syslog_facility) + << R"( + +HTTP: + --add-x-forwarded-for + Append X-Forwarded-For header field to the downstream + request. + --strip-incoming-x-forwarded-for + Strip X-Forwarded-For header field from inbound client + requests. + --no-via Don't append to Via header field. If Via header field + is received, it is left unaltered. + --no-location-rewrite + Don't rewrite location header field on --http2-bridge, + --client and default mode. For --http2-proxy and + --client-proxy mode, location header field will not be + altered regardless of this option. + --no-host-rewrite + Don't rewrite host and :authority header fields on + --http2-bridge, --client and default mode. For + --http2-proxy and --client-proxy mode, these headers + will not be altered regardless of this option. + --altsvc= + Specify protocol ID, port, host and origin of + alternative service. and are optional. + They are advertised in alt-svc header field only in + HTTP/1.1 frontend. This option can be used multiple + times to specify multiple alternative services. + Example: --altsvc=h2,443 + --add-response-header=
        + Specify additional header field to add to response + header set. This option just appends header field and + won't replace anything already set. This option can be + used several times to specify multiple header fields. + Example: --add-response-header="foo: bar" + --header-field-buffer= + Set maximum buffer size for incoming HTTP header field + list. This is the sum of header name and value in + bytes. + Default: )" + << util::utos_with_unit(get_config()->header_field_buffer) << R"( + --max-header-fields= + Set maximum number of incoming HTTP header fields, which + appear in one request or response header field list. + Default: )" << get_config()->max_header_fields << R"( + +Debug: + --frontend-http2-dump-request-header= + Dumps request headers received by HTTP/2 frontend to the + file denoted in . The output is done in HTTP/1 + header field format and each header block is followed by + an empty line. This option is not thread safe and MUST + NOT be used with option -n, where >= 2. + --frontend-http2-dump-response-header= + Dumps response headers sent from HTTP/2 frontend to the + file denoted in . The output is done in HTTP/1 + header field format and each header block is followed by + an empty line. This option is not thread safe and MUST + NOT be used with option -n, where >= 2. + -o, --frontend-frame-debug + Print HTTP/2 frames in frontend to stderr. This option + is not thread safe and MUST NOT be used with option + -n=N, where N >= 2. + +Process: + -D, --daemon + Run in a background. If -D is used, the current working + directory is changed to '/'. + --pid-file= + Set path to save PID of this program. + --user= + Run this program as . This option is intended to + be used to drop root privileges. + +Misc: + --conf= + Load configuration from . + Default: )" << get_config()->conf_path.get() << R"( + -v, --version + Print version and exit. + -h, --help Print this help and exit. + +-- + + The argument is an integer and an optional unit (e.g., 10K is + 10 * 1024). Units are K, M and G (powers of 1024). + + The argument is an integer and an optional unit (e.g., 1s + is 1 second and 500ms is 500 milliseconds). Units are h, m, s or ms + (hours, minutes, seconds and milliseconds, respectively). If a unit + is omitted, a second is used as unit.)" << std::endl; +} +} // namespace + +int main(int argc, char **argv) { + Log::set_severity_level(NOTICE); + create_config(); + fill_default_config(); + + // We have to copy argv, since getopt_long may change its content. + mod_config()->argc = argc; + mod_config()->argv = new char *[argc]; + + for (int i = 0; i < argc; ++i) { + mod_config()->argv[i] = strdup(argv[i]); + } + + mod_config()->cwd = getcwd(nullptr, 0); + if (mod_config()->cwd == nullptr) { + auto error = errno; + LOG(FATAL) << "failed to get current working directory: errno=" << error; + exit(EXIT_FAILURE); + } + + std::vector> cmdcfgs; + while (1) { + static int flag = 0; + static option long_options[] = { + {"daemon", no_argument, nullptr, 'D'}, + {"log-level", required_argument, nullptr, 'L'}, + {"backend", required_argument, nullptr, 'b'}, + {"http2-max-concurrent-streams", required_argument, nullptr, 'c'}, + {"frontend", required_argument, nullptr, 'f'}, + {"help", no_argument, nullptr, 'h'}, + {"insecure", no_argument, nullptr, 'k'}, + {"workers", required_argument, nullptr, 'n'}, + {"client-proxy", no_argument, nullptr, 'p'}, + {"http2-proxy", no_argument, nullptr, 's'}, + {"version", no_argument, nullptr, 'v'}, + {"frontend-frame-debug", no_argument, nullptr, 'o'}, + {"add-x-forwarded-for", no_argument, &flag, 1}, + {"frontend-http2-read-timeout", required_argument, &flag, 2}, + {"frontend-read-timeout", required_argument, &flag, 3}, + {"frontend-write-timeout", required_argument, &flag, 4}, + {"backend-read-timeout", required_argument, &flag, 5}, + {"backend-write-timeout", required_argument, &flag, 6}, + {"accesslog-file", required_argument, &flag, 7}, + {"backend-keep-alive-timeout", required_argument, &flag, 8}, + {"frontend-http2-window-bits", required_argument, &flag, 9}, + {"pid-file", required_argument, &flag, 10}, + {"user", required_argument, &flag, 11}, + {"conf", required_argument, &flag, 12}, + {"syslog-facility", required_argument, &flag, 14}, + {"backlog", required_argument, &flag, 15}, + {"ciphers", required_argument, &flag, 16}, + {"client", no_argument, &flag, 17}, + {"backend-http2-window-bits", required_argument, &flag, 18}, + {"cacert", required_argument, &flag, 19}, + {"backend-ipv4", no_argument, &flag, 20}, + {"backend-ipv6", no_argument, &flag, 21}, + {"private-key-passwd-file", required_argument, &flag, 22}, + {"no-via", no_argument, &flag, 23}, + {"subcert", required_argument, &flag, 24}, + {"http2-bridge", no_argument, &flag, 25}, + {"backend-http-proxy-uri", required_argument, &flag, 26}, + {"backend-no-tls", no_argument, &flag, 27}, + {"frontend-no-tls", no_argument, &flag, 29}, + {"backend-tls-sni-field", required_argument, &flag, 31}, + {"dh-param-file", required_argument, &flag, 33}, + {"read-rate", required_argument, &flag, 34}, + {"read-burst", required_argument, &flag, 35}, + {"write-rate", required_argument, &flag, 36}, + {"write-burst", required_argument, &flag, 37}, + {"npn-list", required_argument, &flag, 38}, + {"verify-client", no_argument, &flag, 39}, + {"verify-client-cacert", required_argument, &flag, 40}, + {"client-private-key-file", required_argument, &flag, 41}, + {"client-cert-file", required_argument, &flag, 42}, + {"frontend-http2-dump-request-header", required_argument, &flag, 43}, + {"frontend-http2-dump-response-header", required_argument, &flag, 44}, + {"http2-no-cookie-crumbling", no_argument, &flag, 45}, + {"frontend-http2-connection-window-bits", required_argument, &flag, 46}, + {"backend-http2-connection-window-bits", required_argument, &flag, 47}, + {"tls-proto-list", required_argument, &flag, 48}, + {"padding", required_argument, &flag, 49}, + {"worker-read-rate", required_argument, &flag, 50}, + {"worker-read-burst", required_argument, &flag, 51}, + {"worker-write-rate", required_argument, &flag, 52}, + {"worker-write-burst", required_argument, &flag, 53}, + {"altsvc", required_argument, &flag, 54}, + {"add-response-header", required_argument, &flag, 55}, + {"worker-frontend-connections", required_argument, &flag, 56}, + {"accesslog-syslog", no_argument, &flag, 57}, + {"errorlog-file", required_argument, &flag, 58}, + {"errorlog-syslog", no_argument, &flag, 59}, + {"stream-read-timeout", required_argument, &flag, 60}, + {"stream-write-timeout", required_argument, &flag, 61}, + {"no-location-rewrite", no_argument, &flag, 62}, + {"backend-http1-connections-per-host", required_argument, &flag, 63}, + {"listener-disable-timeout", required_argument, &flag, 64}, + {"strip-incoming-x-forwarded-for", no_argument, &flag, 65}, + {"accesslog-format", required_argument, &flag, 66}, + {"backend-http1-connections-per-frontend", required_argument, &flag, + 67}, + {"tls-ticket-key-file", required_argument, &flag, 68}, + {"rlimit-nofile", required_argument, &flag, 69}, + {"tls-ctx-per-worker", no_argument, &flag, 70}, + {"backend-response-buffer", required_argument, &flag, 71}, + {"backend-request-buffer", required_argument, &flag, 72}, + {"no-host-rewrite", no_argument, &flag, 73}, + {"no-server-push", no_argument, &flag, 74}, + {"backend-http2-connections-per-worker", required_argument, &flag, 76}, + {"fetch-ocsp-response-file", required_argument, &flag, 77}, + {"ocsp-update-interval", required_argument, &flag, 78}, + {"no-ocsp", no_argument, &flag, 79}, + {"header-field-buffer", required_argument, &flag, 80}, + {"max-header-fields", required_argument, &flag, 81}, + {nullptr, 0, nullptr, 0}}; + + int option_index = 0; + int c = getopt_long(argc, argv, "DL:b:c:f:hkn:opsv", long_options, + &option_index); + if (c == -1) { + break; + } + switch (c) { + case 'D': + cmdcfgs.emplace_back(SHRPX_OPT_DAEMON, "yes"); + break; + case 'L': + cmdcfgs.emplace_back(SHRPX_OPT_LOG_LEVEL, optarg); + break; + case 'b': + cmdcfgs.emplace_back(SHRPX_OPT_BACKEND, optarg); + break; + case 'c': + cmdcfgs.emplace_back(SHRPX_OPT_HTTP2_MAX_CONCURRENT_STREAMS, optarg); + break; + case 'f': + cmdcfgs.emplace_back(SHRPX_OPT_FRONTEND, optarg); + break; + case 'h': + print_help(std::cout); + exit(EXIT_SUCCESS); + case 'k': + cmdcfgs.emplace_back(SHRPX_OPT_INSECURE, "yes"); + break; + case 'n': +#ifdef NOTHREADS + LOG(WARN) << "Threading disabled at build time, no threads created."; +#else + cmdcfgs.emplace_back(SHRPX_OPT_WORKERS, optarg); +#endif // NOTHREADS + break; + case 'o': + cmdcfgs.emplace_back(SHRPX_OPT_FRONTEND_FRAME_DEBUG, "yes"); + break; + case 'p': + cmdcfgs.emplace_back(SHRPX_OPT_CLIENT_PROXY, "yes"); + break; + case 's': + cmdcfgs.emplace_back(SHRPX_OPT_HTTP2_PROXY, "yes"); + break; + case 'v': + print_version(std::cout); + exit(EXIT_SUCCESS); + case '?': + util::show_candidates(argv[optind - 1], long_options); + exit(EXIT_FAILURE); + case 0: + switch (flag) { + case 1: + // --add-x-forwarded-for + cmdcfgs.emplace_back(SHRPX_OPT_ADD_X_FORWARDED_FOR, "yes"); + break; + case 2: + // --frontend-http2-read-timeout + cmdcfgs.emplace_back(SHRPX_OPT_FRONTEND_HTTP2_READ_TIMEOUT, optarg); + break; + case 3: + // --frontend-read-timeout + cmdcfgs.emplace_back(SHRPX_OPT_FRONTEND_READ_TIMEOUT, optarg); + break; + case 4: + // --frontend-write-timeout + cmdcfgs.emplace_back(SHRPX_OPT_FRONTEND_WRITE_TIMEOUT, optarg); + break; + case 5: + // --backend-read-timeout + cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_READ_TIMEOUT, optarg); + break; + case 6: + // --backend-write-timeout + cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_WRITE_TIMEOUT, optarg); + break; + case 7: + cmdcfgs.emplace_back(SHRPX_OPT_ACCESSLOG_FILE, optarg); + break; + case 8: + // --backend-keep-alive-timeout + cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_KEEP_ALIVE_TIMEOUT, optarg); + break; + case 9: + // --frontend-http2-window-bits + cmdcfgs.emplace_back(SHRPX_OPT_FRONTEND_HTTP2_WINDOW_BITS, optarg); + break; + case 10: + cmdcfgs.emplace_back(SHRPX_OPT_PID_FILE, optarg); + break; + case 11: + cmdcfgs.emplace_back(SHRPX_OPT_USER, optarg); + break; + case 12: + // --conf + mod_config()->conf_path = strcopy(optarg); + break; + case 14: + // --syslog-facility + cmdcfgs.emplace_back(SHRPX_OPT_SYSLOG_FACILITY, optarg); + break; + case 15: + // --backlog + cmdcfgs.emplace_back(SHRPX_OPT_BACKLOG, optarg); + break; + case 16: + // --ciphers + cmdcfgs.emplace_back(SHRPX_OPT_CIPHERS, optarg); + break; + case 17: + // --client + cmdcfgs.emplace_back(SHRPX_OPT_CLIENT, "yes"); + break; + case 18: + // --backend-http2-window-bits + cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_HTTP2_WINDOW_BITS, optarg); + break; + case 19: + // --cacert + cmdcfgs.emplace_back(SHRPX_OPT_CACERT, optarg); + break; + case 20: + // --backend-ipv4 + cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_IPV4, "yes"); + break; + case 21: + // --backend-ipv6 + cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_IPV6, "yes"); + break; + case 22: + // --private-key-passwd-file + cmdcfgs.emplace_back(SHRPX_OPT_PRIVATE_KEY_PASSWD_FILE, optarg); + break; + case 23: + // --no-via + cmdcfgs.emplace_back(SHRPX_OPT_NO_VIA, "yes"); + break; + case 24: + // --subcert + cmdcfgs.emplace_back(SHRPX_OPT_SUBCERT, optarg); + break; + case 25: + // --http2-bridge + cmdcfgs.emplace_back(SHRPX_OPT_HTTP2_BRIDGE, "yes"); + break; + case 26: + // --backend-http-proxy-uri + cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_HTTP_PROXY_URI, optarg); + break; + case 27: + // --backend-no-tls + cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_NO_TLS, "yes"); + break; + case 29: + // --frontend-no-tls + cmdcfgs.emplace_back(SHRPX_OPT_FRONTEND_NO_TLS, "yes"); + break; + case 31: + // --backend-tls-sni-field + cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_TLS_SNI_FIELD, optarg); + break; + case 33: + // --dh-param-file + cmdcfgs.emplace_back(SHRPX_OPT_DH_PARAM_FILE, optarg); + break; + case 34: + // --read-rate + cmdcfgs.emplace_back(SHRPX_OPT_READ_RATE, optarg); + break; + case 35: + // --read-burst + cmdcfgs.emplace_back(SHRPX_OPT_READ_BURST, optarg); + break; + case 36: + // --write-rate + cmdcfgs.emplace_back(SHRPX_OPT_WRITE_RATE, optarg); + break; + case 37: + // --write-burst + cmdcfgs.emplace_back(SHRPX_OPT_WRITE_BURST, optarg); + break; + case 38: + // --npn-list + cmdcfgs.emplace_back(SHRPX_OPT_NPN_LIST, optarg); + break; + case 39: + // --verify-client + cmdcfgs.emplace_back(SHRPX_OPT_VERIFY_CLIENT, "yes"); + break; + case 40: + // --verify-client-cacert + cmdcfgs.emplace_back(SHRPX_OPT_VERIFY_CLIENT_CACERT, optarg); + break; + case 41: + // --client-private-key-file + cmdcfgs.emplace_back(SHRPX_OPT_CLIENT_PRIVATE_KEY_FILE, optarg); + break; + case 42: + // --client-cert-file + cmdcfgs.emplace_back(SHRPX_OPT_CLIENT_CERT_FILE, optarg); + break; + case 43: + // --frontend-http2-dump-request-header + cmdcfgs.emplace_back(SHRPX_OPT_FRONTEND_HTTP2_DUMP_REQUEST_HEADER, + optarg); + break; + case 44: + // --frontend-http2-dump-response-header + cmdcfgs.emplace_back(SHRPX_OPT_FRONTEND_HTTP2_DUMP_RESPONSE_HEADER, + optarg); + break; + case 45: + // --http2-no-cookie-crumbling + cmdcfgs.emplace_back(SHRPX_OPT_HTTP2_NO_COOKIE_CRUMBLING, "yes"); + break; + case 46: + // --frontend-http2-connection-window-bits + cmdcfgs.emplace_back(SHRPX_OPT_FRONTEND_HTTP2_CONNECTION_WINDOW_BITS, + optarg); + break; + case 47: + // --backend-http2-connection-window-bits + cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_HTTP2_CONNECTION_WINDOW_BITS, + optarg); + break; + case 48: + // --tls-proto-list + cmdcfgs.emplace_back(SHRPX_OPT_TLS_PROTO_LIST, optarg); + break; + case 49: + // --padding + cmdcfgs.emplace_back(SHRPX_OPT_PADDING, optarg); + break; + case 50: + // --worker-read-rate + cmdcfgs.emplace_back(SHRPX_OPT_WORKER_READ_RATE, optarg); + break; + case 51: + // --worker-read-burst + cmdcfgs.emplace_back(SHRPX_OPT_WORKER_READ_BURST, optarg); + break; + case 52: + // --worker-write-rate + cmdcfgs.emplace_back(SHRPX_OPT_WORKER_WRITE_RATE, optarg); + break; + case 53: + // --worker-write-burst + cmdcfgs.emplace_back(SHRPX_OPT_WORKER_WRITE_BURST, optarg); + break; + case 54: + // --altsvc + cmdcfgs.emplace_back(SHRPX_OPT_ALTSVC, optarg); + break; + case 55: + // --add-response-header + cmdcfgs.emplace_back(SHRPX_OPT_ADD_RESPONSE_HEADER, optarg); + break; + case 56: + // --worker-frontend-connections + cmdcfgs.emplace_back(SHRPX_OPT_WORKER_FRONTEND_CONNECTIONS, optarg); + break; + case 57: + // --accesslog-syslog + cmdcfgs.emplace_back(SHRPX_OPT_ACCESSLOG_SYSLOG, "yes"); + break; + case 58: + // --errorlog-file + cmdcfgs.emplace_back(SHRPX_OPT_ERRORLOG_FILE, optarg); + break; + case 59: + // --errorlog-syslog + cmdcfgs.emplace_back(SHRPX_OPT_ERRORLOG_SYSLOG, "yes"); + break; + case 60: + // --stream-read-timeout + cmdcfgs.emplace_back(SHRPX_OPT_STREAM_READ_TIMEOUT, optarg); + break; + case 61: + // --stream-write-timeout + cmdcfgs.emplace_back(SHRPX_OPT_STREAM_WRITE_TIMEOUT, optarg); + break; + case 62: + // --no-location-rewrite + cmdcfgs.emplace_back(SHRPX_OPT_NO_LOCATION_REWRITE, "yes"); + break; + case 63: + // --backend-http1-connections-per-host + cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_HTTP1_CONNECTIONS_PER_HOST, + optarg); + break; + case 64: + // --listener-disable-timeout + cmdcfgs.emplace_back(SHRPX_OPT_LISTENER_DISABLE_TIMEOUT, optarg); + break; + case 65: + // --strip-incoming-x-forwarded-for + cmdcfgs.emplace_back(SHRPX_OPT_STRIP_INCOMING_X_FORWARDED_FOR, "yes"); + break; + case 66: + // --accesslog-format + cmdcfgs.emplace_back(SHRPX_OPT_ACCESSLOG_FORMAT, optarg); + break; + case 67: + // --backend-http1-connections-per-frontend + cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_HTTP1_CONNECTIONS_PER_FRONTEND, + optarg); + break; + case 68: + // --tls-ticket-key-file + cmdcfgs.emplace_back(SHRPX_OPT_TLS_TICKET_KEY_FILE, optarg); + break; + case 69: + // --rlimit-nofile + cmdcfgs.emplace_back(SHRPX_OPT_RLIMIT_NOFILE, optarg); + break; + case 71: + // --backend-response-buffer + cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_RESPONSE_BUFFER, optarg); + break; + case 72: + // --backend-request-buffer + cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_REQUEST_BUFFER, optarg); + break; + case 73: + // --no-host-rewrite + cmdcfgs.emplace_back(SHRPX_OPT_NO_HOST_REWRITE, "yes"); + break; + case 74: + // --no-server-push + cmdcfgs.emplace_back(SHRPX_OPT_NO_SERVER_PUSH, "yes"); + break; + case 76: + // --backend-http2-connections-per-worker + cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_HTTP2_CONNECTIONS_PER_WORKER, + optarg); + break; + case 77: + // --fetch-ocsp-response-file + cmdcfgs.emplace_back(SHRPX_OPT_FETCH_OCSP_RESPONSE_FILE, optarg); + break; + case 78: + // --ocsp-update-interval + cmdcfgs.emplace_back(SHRPX_OPT_OCSP_UPDATE_INTERVAL, optarg); + break; + case 79: + // --no-ocsp + cmdcfgs.emplace_back(SHRPX_OPT_NO_OCSP, "yes"); + break; + case 80: + // --header-field-buffer + cmdcfgs.emplace_back(SHRPX_OPT_HEADER_FIELD_BUFFER, optarg); + break; + case 81: + // --max-header-fields + cmdcfgs.emplace_back(SHRPX_OPT_MAX_HEADER_FIELDS, optarg); + break; + default: + break; + } + break; + default: + break; + } + } + + // Initialize OpenSSL before parsing options because we create + // SSL_CTX there. + OPENSSL_config(nullptr); + OpenSSL_add_all_algorithms(); + SSL_load_error_strings(); + SSL_library_init(); + + if (conf_exists(get_config()->conf_path.get())) { + if (load_config(get_config()->conf_path.get()) == -1) { + LOG(FATAL) << "Failed to load configuration from " + << get_config()->conf_path.get(); + exit(EXIT_FAILURE); + } + } + + if (argc - optind >= 2) { + cmdcfgs.emplace_back(SHRPX_OPT_PRIVATE_KEY_FILE, argv[optind++]); + cmdcfgs.emplace_back(SHRPX_OPT_CERTIFICATE_FILE, argv[optind++]); + } + + // First open default log files to deal with errors occurred while + // parsing option values. + reopen_log_files(); + + for (size_t i = 0, len = cmdcfgs.size(); i < len; ++i) { + if (parse_config(cmdcfgs[i].first, cmdcfgs[i].second) == -1) { + LOG(FATAL) << "Failed to parse command-line argument."; + exit(EXIT_FAILURE); + } + } + +#ifndef NOTHREADS + auto lock = make_unique(); +#endif // NOTHREADS + + if (get_config()->accesslog_syslog || get_config()->errorlog_syslog) { + openlog("nghttpx", LOG_NDELAY | LOG_NOWAIT | LOG_PID, + get_config()->syslog_facility); + } + + if (reopen_log_files() != 0) { + LOG(FATAL) << "Failed to open log file"; + exit(EXIT_FAILURE); + } + + redirect_stderr_to_errorlog(); + + if (get_config()->uid != 0) { + if (log_config()->accesslog_fd != -1 && + fchown(log_config()->accesslog_fd, get_config()->uid, + get_config()->gid) == -1) { + auto error = errno; + LOG(WARN) << "Changing owner of access log file failed: " + << strerror(error); + } + if (log_config()->errorlog_fd != -1 && + fchown(log_config()->errorlog_fd, get_config()->uid, + get_config()->gid) == -1) { + auto error = errno; + LOG(WARN) << "Changing owner of error log file failed: " + << strerror(error); + } + } + + if (get_config()->http2_upstream_dump_request_header_file) { + auto path = get_config()->http2_upstream_dump_request_header_file.get(); + auto f = open_file_for_write(path); + + if (f == nullptr) { + LOG(FATAL) << "Failed to open http2 upstream request header file: " + << path; + exit(EXIT_FAILURE); + } + + mod_config()->http2_upstream_dump_request_header = f; + + if (get_config()->uid != 0) { + if (chown(path, get_config()->uid, get_config()->gid) == -1) { + auto error = errno; + LOG(WARN) << "Changing owner of http2 upstream request header file " + << path << " failed: " << strerror(error); + } + } + } + + if (get_config()->http2_upstream_dump_response_header_file) { + auto path = get_config()->http2_upstream_dump_response_header_file.get(); + auto f = open_file_for_write(path); + + if (f == nullptr) { + LOG(FATAL) << "Failed to open http2 upstream response header file: " + << path; + exit(EXIT_FAILURE); + } + + mod_config()->http2_upstream_dump_response_header = f; + + if (get_config()->uid != 0) { + if (chown(path, get_config()->uid, get_config()->gid) == -1) { + auto error = errno; + LOG(WARN) << "Changing owner of http2 upstream response header file" + << " " << path << " failed: " << strerror(error); + } + } + } + + if (get_config()->npn_list.empty()) { + mod_config()->npn_list = parse_config_str_list(DEFAULT_NPN_LIST); + } + if (get_config()->tls_proto_list.empty()) { + mod_config()->tls_proto_list = + parse_config_str_list(DEFAULT_TLS_PROTO_LIST); + } + + mod_config()->tls_proto_mask = + ssl::create_tls_proto_mask(get_config()->tls_proto_list); + + mod_config()->alpn_prefs = ssl::set_alpn_prefs(get_config()->npn_list); + + if (get_config()->backend_ipv4 && get_config()->backend_ipv6) { + LOG(FATAL) << "--backend-ipv4 and --backend-ipv6 cannot be used at the " + << "same time."; + exit(EXIT_FAILURE); + } + + if (get_config()->worker_frontend_connections == 0) { + mod_config()->worker_frontend_connections = + std::numeric_limits::max(); + } + + if (get_config()->http2_proxy + get_config()->http2_bridge + + get_config()->client_proxy + get_config()->client > + 1) { + LOG(FATAL) << "--http2-proxy, --http2-bridge, --client-proxy and --client " + << "cannot be used at the same time."; + exit(EXIT_FAILURE); + } + + if (get_config()->client || get_config()->client_proxy) { + mod_config()->client_mode = true; + mod_config()->upstream_no_tls = true; + } + + if (get_config()->client_mode || get_config()->http2_bridge) { + mod_config()->downstream_proto = PROTO_HTTP2; + } else { + mod_config()->downstream_proto = PROTO_HTTP; + } + + if (!get_config()->upstream_no_tls && + (!get_config()->private_key_file || !get_config()->cert_file)) { + print_usage(std::cerr); + LOG(FATAL) << "Too few arguments"; + exit(EXIT_FAILURE); + } + + if (!get_config()->upstream_no_tls && !get_config()->no_ocsp) { + struct stat buf; + if (stat(get_config()->fetch_ocsp_response_file.get(), &buf) != 0) { + mod_config()->no_ocsp = true; + LOG(WARN) << "--fetch-ocsp-response-file: " + << get_config()->fetch_ocsp_response_file.get() + << " not found. OCSP stapling has been disabled."; + } + } + + if (get_config()->downstream_addrs.empty()) { + DownstreamAddr addr; + addr.host = strcopy(DEFAULT_DOWNSTREAM_HOST); + addr.port = DEFAULT_DOWNSTREAM_PORT; + + mod_config()->downstream_addrs.push_back(std::move(addr)); + } + + if (LOG_ENABLED(INFO)) { + LOG(INFO) << "Resolving backend address"; + } + + for (auto &addr : mod_config()->downstream_addrs) { + + if (addr.host_unix) { + // for AF_UNIX socket, we use "localhost" as host for backend + // hostport. This is used as Host header field to backend and + // not going to be passed to any syscalls. + addr.hostport = + strcopy(util::make_hostport("localhost", get_config()->port)); + + auto path = addr.host.get(); + auto pathlen = strlen(path); + + if (pathlen + 1 > sizeof(addr.addr.un.sun_path)) { + LOG(FATAL) << "UNIX domain socket path " << path << " is too long > " + << sizeof(addr.addr.un.sun_path); + exit(EXIT_FAILURE); + } + + LOG(INFO) << "Use UNIX domain socket path " << path + << " for backend connection"; + + addr.addr.un.sun_family = AF_UNIX; + // copy path including terminal NULL + std::copy_n(path, pathlen + 1, addr.addr.un.sun_path); + addr.addrlen = sizeof(addr.addr.un); + + continue; + } + + addr.hostport = strcopy(util::make_hostport(addr.host.get(), addr.port)); + + if (resolve_hostname( + &addr.addr, &addr.addrlen, addr.host.get(), addr.port, + get_config()->backend_ipv4 + ? AF_INET + : (get_config()->backend_ipv6 ? AF_INET6 : AF_UNSPEC)) == -1) { + exit(EXIT_FAILURE); + } + } + + if (get_config()->downstream_http_proxy_host) { + if (LOG_ENABLED(INFO)) { + LOG(INFO) << "Resolving backend http proxy address"; + } + if (resolve_hostname(&mod_config()->downstream_http_proxy_addr, + &mod_config()->downstream_http_proxy_addrlen, + get_config()->downstream_http_proxy_host.get(), + get_config()->downstream_http_proxy_port, + AF_UNSPEC) == -1) { + exit(EXIT_FAILURE); + } + } + + if (get_config()->http2_downstream_connections_per_worker == 0) { + mod_config()->http2_downstream_connections_per_worker = + get_config()->downstream_addrs.size(); + } + + if (get_config()->rlimit_nofile) { + struct rlimit lim = {static_cast(get_config()->rlimit_nofile), + static_cast(get_config()->rlimit_nofile)}; + if (setrlimit(RLIMIT_NOFILE, &lim) != 0) { + auto error = errno; + LOG(WARN) << "Setting rlimit-nofile failed: " << strerror(error); + } + } + + if (get_config()->upstream_frame_debug) { + // To make it sync to logging + set_output(stderr); + if (isatty(fileno(stdout))) { + set_color_output(true); + } + reset_timer(); + } + + struct sigaction act; + memset(&act, 0, sizeof(struct sigaction)); + act.sa_handler = SIG_IGN; + sigaction(SIGPIPE, &act, nullptr); + + event_loop(); + + LOG(NOTICE) << "Shutdown momentarily"; + + return 0; +} + +} // namespace shrpx + +int main(int argc, char **argv) { return shrpx::main(argc, argv); } diff --git a/src/shrpx.h b/src/shrpx.h new file mode 100644 index 0000000..bd91c76 --- /dev/null +++ b/src/shrpx.h @@ -0,0 +1,49 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef SHRPX_H +#define SHRPX_H + +#ifdef HAVE_CONFIG_H +#include +#endif // HAVE_CONFIG_H + +#include +#ifdef HAVE_SYS_SOCKET_H +#include +#endif // HAVE_SYS_SOCKET_H + +#include + +#include "shrpx_log.h" + +#ifndef HAVE__EXIT +#define _Exit(status) _exit(status) +#endif // !HAVE__EXIT + +#define DIE() exit(EXIT_FAILURE) + +#define SHRPX_READ_WATERMARK (16 * 1024) + +#endif // SHRPX_H diff --git a/src/shrpx_accept_handler.cc b/src/shrpx_accept_handler.cc new file mode 100644 index 0000000..0d44830 --- /dev/null +++ b/src/shrpx_accept_handler.cc @@ -0,0 +1,114 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2014 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "shrpx_accept_handler.h" + +#ifdef HAVE_UNISTD_H +#include +#endif // HAVE_UNISTD_H + +#include + +#include "shrpx_connection_handler.h" +#include "shrpx_config.h" +#include "util.h" + +using namespace nghttp2; + +namespace shrpx { + +namespace { +void acceptcb(struct ev_loop *loop, ev_io *w, int revent) { + auto h = static_cast(w->data); + h->accept_connection(); +} +} // namespace + +AcceptHandler::AcceptHandler(int fd, ConnectionHandler *h) + : conn_hnr_(h), fd_(fd) { + ev_io_init(&wev_, acceptcb, fd_, EV_READ); + wev_.data = this; + ev_io_start(conn_hnr_->get_loop(), &wev_); +} + +AcceptHandler::~AcceptHandler() { + ev_io_stop(conn_hnr_->get_loop(), &wev_); + close(fd_); +} + +void AcceptHandler::accept_connection() { + for (;;) { + sockaddr_union sockaddr; + socklen_t addrlen = sizeof(sockaddr); + +#ifdef HAVE_ACCEPT4 + auto cfd = + accept4(fd_, &sockaddr.sa, &addrlen, SOCK_NONBLOCK | SOCK_CLOEXEC); +#else // !HAVE_ACCEPT4 + auto cfd = accept(fd_, &sockaddr.sa, &addrlen); +#endif // !HAVE_ACCEPT4 + + if (cfd == -1) { + switch (errno) { + case EINTR: + case ENETDOWN: + case EPROTO: + case ENOPROTOOPT: + case EHOSTDOWN: +#ifdef ENONET + case ENONET: +#endif // ENONET + case EHOSTUNREACH: + case EOPNOTSUPP: + case ENETUNREACH: + continue; + case EMFILE: + case ENFILE: + LOG(WARN) << "acceptor: running out file descriptor; disable acceptor " + "temporarily"; + conn_hnr_->disable_acceptor_temporary(30.); + break; + } + + break; + } + +#ifndef HAVE_ACCEPT4 + util::make_socket_nonblocking(cfd); + util::make_socket_closeonexec(cfd); +#endif // !HAVE_ACCEPT4 + + util::make_socket_nodelay(cfd); + + conn_hnr_->handle_connection(cfd, &sockaddr.sa, addrlen); + } +} + +void AcceptHandler::enable() { ev_io_start(conn_hnr_->get_loop(), &wev_); } + +void AcceptHandler::disable() { ev_io_stop(conn_hnr_->get_loop(), &wev_); } + +int AcceptHandler::get_fd() const { return fd_; } + +} // namespace shrpx diff --git a/src/shrpx_accept_handler.h b/src/shrpx_accept_handler.h new file mode 100644 index 0000000..194788b --- /dev/null +++ b/src/shrpx_accept_handler.h @@ -0,0 +1,53 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2014 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef SHRPX_ACCEPT_HANDLER_H +#define SHRPX_ACCEPT_HANDLER_H + +#include "shrpx.h" + +#include + +namespace shrpx { + +class ConnectionHandler; + +class AcceptHandler { +public: + AcceptHandler(int fd, ConnectionHandler *h); + ~AcceptHandler(); + void accept_connection(); + void enable(); + void disable(); + int get_fd() const; + +private: + ev_io wev_; + ConnectionHandler *conn_hnr_; + int fd_; +}; + +} // namespace shrpx + +#endif // SHRPX_ACCEPT_HANDLER_H diff --git a/src/shrpx_client_handler.cc b/src/shrpx_client_handler.cc new file mode 100644 index 0000000..354b95e --- /dev/null +++ b/src/shrpx_client_handler.cc @@ -0,0 +1,793 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "shrpx_client_handler.h" + +#ifdef HAVE_UNISTD_H +#include +#endif // HAVE_UNISTD_H +#include + +#include "shrpx_upstream.h" +#include "shrpx_http2_upstream.h" +#include "shrpx_https_upstream.h" +#include "shrpx_config.h" +#include "shrpx_http_downstream_connection.h" +#include "shrpx_http2_downstream_connection.h" +#include "shrpx_ssl.h" +#include "shrpx_worker.h" +#include "shrpx_downstream_connection_pool.h" +#include "shrpx_downstream.h" +#ifdef HAVE_SPDYLAY +#include "shrpx_spdy_upstream.h" +#endif // HAVE_SPDYLAY +#include "util.h" +#include "template.h" + +using namespace nghttp2; + +namespace shrpx { + +namespace { +void timeoutcb(struct ev_loop *loop, ev_timer *w, int revents) { + auto conn = static_cast(w->data); + auto handler = static_cast(conn->data); + + if (LOG_ENABLED(INFO)) { + CLOG(INFO, handler) << "Time out"; + } + + delete handler; +} +} // namespace + +namespace { +void shutdowncb(struct ev_loop *loop, ev_timer *w, int revents) { + auto handler = static_cast(w->data); + + if (LOG_ENABLED(INFO)) { + CLOG(INFO, handler) << "Close connection due to TLS renegotiation"; + } + + delete handler; +} +} // namespace + +namespace { +void readcb(struct ev_loop *loop, ev_io *w, int revents) { + auto conn = static_cast(w->data); + auto handler = static_cast(conn->data); + + if (handler->do_read() != 0) { + delete handler; + return; + } + if (handler->do_write() != 0) { + delete handler; + return; + } +} +} // namespace + +namespace { +void writecb(struct ev_loop *loop, ev_io *w, int revents) { + auto conn = static_cast(w->data); + auto handler = static_cast(conn->data); + + if (handler->do_write() != 0) { + delete handler; + return; + } +} +} // namespace + +int ClientHandler::read_clear() { + ev_timer_again(conn_.loop, &conn_.rt); + + for (;;) { + if (rb_.rleft() && on_read() != 0) { + return -1; + } + if (rb_.rleft() == 0) { + rb_.reset(); + } else if (rb_.wleft() == 0) { + conn_.rlimit.stopw(); + return 0; + } + + auto nread = conn_.read_clear(rb_.last, rb_.wleft()); + + if (nread == 0) { + return 0; + } + + if (nread < 0) { + return -1; + } + + rb_.write(nread); + } +} + +int ClientHandler::write_clear() { + ev_timer_again(conn_.loop, &conn_.rt); + + for (;;) { + if (wb_.rleft() > 0) { + auto nwrite = conn_.write_clear(wb_.pos, wb_.rleft()); + if (nwrite == 0) { + return 0; + } + if (nwrite < 0) { + return -1; + } + wb_.drain(nwrite); + continue; + } + wb_.reset(); + if (on_write() != 0) { + return -1; + } + if (wb_.rleft() == 0) { + break; + } + } + + conn_.wlimit.stopw(); + ev_timer_stop(conn_.loop, &conn_.wt); + + return 0; +} + +int ClientHandler::tls_handshake() { + ev_timer_again(conn_.loop, &conn_.rt); + + ERR_clear_error(); + + auto rv = conn_.tls_handshake(); + + if (rv == SHRPX_ERR_INPROGRESS) { + return 0; + } + + if (rv < 0) { + return -1; + } + + if (LOG_ENABLED(INFO)) { + CLOG(INFO, this) << "SSL/TLS handshake completed"; + } + + if (validate_next_proto() != 0) { + return -1; + } + + read_ = &ClientHandler::read_tls; + write_ = &ClientHandler::write_tls; + + return 0; +} + +int ClientHandler::read_tls() { + ev_timer_again(conn_.loop, &conn_.rt); + + ERR_clear_error(); + + for (;;) { + // we should process buffered data first before we read EOF. + if (rb_.rleft() && on_read() != 0) { + return -1; + } + if (rb_.rleft() == 0) { + rb_.reset(); + } else if (rb_.wleft() == 0) { + conn_.rlimit.stopw(); + return 0; + } + + auto nread = conn_.read_tls(rb_.last, rb_.wleft()); + + if (nread == 0) { + return 0; + } + + if (nread < 0) { + return -1; + } + + rb_.write(nread); + } +} + +int ClientHandler::write_tls() { + ev_timer_again(conn_.loop, &conn_.rt); + + ERR_clear_error(); + + for (;;) { + if (wb_.rleft() > 0) { + auto nwrite = conn_.write_tls(wb_.pos, wb_.rleft()); + + if (nwrite == 0) { + return 0; + } + + if (nwrite < 0) { + return -1; + } + + wb_.drain(nwrite); + + continue; + } + wb_.reset(); + if (on_write() != 0) { + return -1; + } + if (wb_.rleft() == 0) { + break; + } + } + + conn_.wlimit.stopw(); + ev_timer_stop(conn_.loop, &conn_.wt); + + return 0; +} + +int ClientHandler::upstream_noop() { return 0; } + +int ClientHandler::upstream_read() { + assert(upstream_); + if (upstream_->on_read() != 0) { + return -1; + } + return 0; +} + +int ClientHandler::upstream_write() { + assert(upstream_); + if (upstream_->on_write() != 0) { + return -1; + } + + if (get_should_close_after_write() && wb_.rleft() == 0) { + return -1; + } + + return 0; +} + +int ClientHandler::upstream_http2_connhd_read() { + auto nread = std::min(left_connhd_len_, rb_.rleft()); + if (memcmp(NGHTTP2_CLIENT_MAGIC + NGHTTP2_CLIENT_MAGIC_LEN - left_connhd_len_, + rb_.pos, nread) != 0) { + // There is no downgrade path here. Just drop the connection. + if (LOG_ENABLED(INFO)) { + CLOG(INFO, this) << "invalid client connection header"; + } + + return -1; + } + + left_connhd_len_ -= nread; + rb_.drain(nread); + conn_.rlimit.startw(); + + if (left_connhd_len_ == 0) { + on_read_ = &ClientHandler::upstream_read; + // Run on_read to process data left in buffer since they are not + // notified further + if (on_read() != 0) { + return -1; + } + return 0; + } + + return 0; +} + +int ClientHandler::upstream_http1_connhd_read() { + auto nread = std::min(left_connhd_len_, rb_.rleft()); + if (memcmp(NGHTTP2_CLIENT_MAGIC + NGHTTP2_CLIENT_MAGIC_LEN - left_connhd_len_, + rb_.pos, nread) != 0) { + if (LOG_ENABLED(INFO)) { + CLOG(INFO, this) << "This is HTTP/1.1 connection, " + << "but may be upgraded to HTTP/2 later."; + } + + // Reset header length for later HTTP/2 upgrade + left_connhd_len_ = NGHTTP2_CLIENT_MAGIC_LEN; + on_read_ = &ClientHandler::upstream_read; + on_write_ = &ClientHandler::upstream_write; + + if (on_read() != 0) { + return -1; + } + + return 0; + } + + left_connhd_len_ -= nread; + rb_.drain(nread); + conn_.rlimit.startw(); + + if (left_connhd_len_ == 0) { + if (LOG_ENABLED(INFO)) { + CLOG(INFO, this) << "direct HTTP/2 connection"; + } + + direct_http2_upgrade(); + on_read_ = &ClientHandler::upstream_read; + on_write_ = &ClientHandler::upstream_write; + + // Run on_read to process data left in buffer since they are not + // notified further + if (on_read() != 0) { + return -1; + } + + return 0; + } + + return 0; +} + +ClientHandler::ClientHandler(Worker *worker, int fd, SSL *ssl, + const char *ipaddr, const char *port) + : conn_(worker->get_loop(), fd, ssl, get_config()->upstream_write_timeout, + get_config()->upstream_read_timeout, get_config()->write_rate, + get_config()->write_burst, get_config()->read_rate, + get_config()->read_burst, writecb, readcb, timeoutcb, this), + ipaddr_(ipaddr), port_(port), worker_(worker), + http2session_(worker_->next_http2_session()), + left_connhd_len_(NGHTTP2_CLIENT_MAGIC_LEN), + should_close_after_write_(false) { + + ++worker_->get_worker_stat()->num_connections; + + ev_timer_init(&reneg_shutdown_timer_, shutdowncb, 0., 0.); + + reneg_shutdown_timer_.data = this; + + conn_.rlimit.startw(); + ev_timer_again(conn_.loop, &conn_.rt); + + if (conn_.tls.ssl) { + SSL_set_app_data(conn_.tls.ssl, &conn_); + read_ = write_ = &ClientHandler::tls_handshake; + on_read_ = &ClientHandler::upstream_noop; + on_write_ = &ClientHandler::upstream_write; + } else { + // For non-TLS version, first create HttpsUpstream. It may be + // upgraded to HTTP/2 through HTTP Upgrade or direct HTTP/2 + // connection. + upstream_ = make_unique(this); + alpn_ = "http/1.1"; + read_ = &ClientHandler::read_clear; + write_ = &ClientHandler::write_clear; + on_read_ = &ClientHandler::upstream_http1_connhd_read; + on_write_ = &ClientHandler::upstream_noop; + } +} + +ClientHandler::~ClientHandler() { + if (LOG_ENABLED(INFO)) { + CLOG(INFO, this) << "Deleting"; + } + + if (upstream_) { + upstream_->on_handler_delete(); + } + + auto worker_stat = worker_->get_worker_stat(); + --worker_stat->num_connections; + + if (worker_stat->num_connections == 0) { + worker_->schedule_clear_mcpool(); + } + + ev_timer_stop(conn_.loop, &reneg_shutdown_timer_); + + // TODO If backend is http/2, and it is in CONNECTED state, signal + // it and make it loopbreak when output is zero. + if (worker_->get_graceful_shutdown() && worker_stat->num_connections == 0) { + ev_break(conn_.loop); + } + + if (LOG_ENABLED(INFO)) { + CLOG(INFO, this) << "Deleted"; + } +} + +Upstream *ClientHandler::get_upstream() { return upstream_.get(); } + +struct ev_loop *ClientHandler::get_loop() const { + return conn_.loop; +} + +void ClientHandler::reset_upstream_read_timeout(ev_tstamp t) { + conn_.rt.repeat = t; + if (ev_is_active(&conn_.rt)) { + ev_timer_again(conn_.loop, &conn_.rt); + } +} + +void ClientHandler::reset_upstream_write_timeout(ev_tstamp t) { + conn_.wt.repeat = t; + if (ev_is_active(&conn_.wt)) { + ev_timer_again(conn_.loop, &conn_.wt); + } +} + +int ClientHandler::validate_next_proto() { + const unsigned char *next_proto = nullptr; + unsigned int next_proto_len; + int rv; + + // First set callback for catch all cases + on_read_ = &ClientHandler::upstream_read; + + SSL_get0_next_proto_negotiated(conn_.tls.ssl, &next_proto, &next_proto_len); + for (int i = 0; i < 2; ++i) { + if (next_proto) { + if (LOG_ENABLED(INFO)) { + std::string proto(next_proto, next_proto + next_proto_len); + CLOG(INFO, this) << "The negotiated next protocol: " << proto; + } + if (!ssl::in_proto_list(get_config()->npn_list, next_proto, + next_proto_len)) { + break; + } + if (util::check_h2_is_selected(next_proto, next_proto_len)) { + + on_read_ = &ClientHandler::upstream_http2_connhd_read; + + auto http2_upstream = make_unique(this); + + if (!ssl::check_http2_requirement(conn_.tls.ssl)) { + rv = http2_upstream->terminate_session(NGHTTP2_INADEQUATE_SECURITY); + + if (rv != 0) { + return -1; + } + } + + upstream_ = std::move(http2_upstream); + alpn_.assign(next_proto, next_proto + next_proto_len); + + // At this point, input buffer is already filled with some + // bytes. The read callback is not called until new data + // come. So consume input buffer here. + if (on_read() != 0) { + return -1; + } + + return 0; + } else { +#ifdef HAVE_SPDYLAY + uint16_t version = spdylay_npn_get_version(next_proto, next_proto_len); + if (version) { + upstream_ = make_unique(version, this); + + switch (version) { + case SPDYLAY_PROTO_SPDY2: + alpn_ = "spdy/2"; + break; + case SPDYLAY_PROTO_SPDY3: + alpn_ = "spdy/3"; + break; + case SPDYLAY_PROTO_SPDY3_1: + alpn_ = "spdy/3.1"; + break; + default: + alpn_ = "spdy/unknown"; + } + + // At this point, input buffer is already filled with some + // bytes. The read callback is not called until new data + // come. So consume input buffer here. + if (on_read() != 0) { + return -1; + } + + return 0; + } +#endif // HAVE_SPDYLAY + if (next_proto_len == 8 && memcmp("http/1.1", next_proto, 8) == 0) { + upstream_ = make_unique(this); + alpn_ = "http/1.1"; + + // At this point, input buffer is already filled with some + // bytes. The read callback is not called until new data + // come. So consume input buffer here. + if (on_read() != 0) { + return -1; + } + + return 0; + } + } + break; + } +#if OPENSSL_VERSION_NUMBER >= 0x10002000L + SSL_get0_alpn_selected(conn_.tls.ssl, &next_proto, &next_proto_len); +#else // OPENSSL_VERSION_NUMBER < 0x10002000L + break; +#endif // OPENSSL_VERSION_NUMBER < 0x10002000L + } + if (!next_proto) { + if (LOG_ENABLED(INFO)) { + CLOG(INFO, this) << "No protocol negotiated. Fallback to HTTP/1.1"; + } + upstream_ = make_unique(this); + alpn_ = "http/1.1"; + + // At this point, input buffer is already filled with some bytes. + // The read callback is not called until new data come. So consume + // input buffer here. + if (on_read() != 0) { + return -1; + } + + return 0; + } + if (LOG_ENABLED(INFO)) { + CLOG(INFO, this) << "The negotiated protocol is not supported"; + } + return -1; +} + +int ClientHandler::do_read() { return read_(*this); } +int ClientHandler::do_write() { return write_(*this); } + +int ClientHandler::on_read() { + auto rv = on_read_(*this); + if (rv != 0) { + return rv; + } + conn_.handle_tls_pending_read(); + return 0; +} +int ClientHandler::on_write() { return on_write_(*this); } + +const std::string &ClientHandler::get_ipaddr() const { return ipaddr_; } + +bool ClientHandler::get_should_close_after_write() const { + return should_close_after_write_; +} + +void ClientHandler::set_should_close_after_write(bool f) { + should_close_after_write_ = f; +} + +void ClientHandler::pool_downstream_connection( + std::unique_ptr dconn) { + if (!dconn->poolable()) { + return; + } + if (LOG_ENABLED(INFO)) { + CLOG(INFO, this) << "Pooling downstream connection DCONN:" << dconn.get(); + } + dconn->set_client_handler(nullptr); + auto dconn_pool = worker_->get_dconn_pool(); + dconn_pool->add_downstream_connection(std::move(dconn)); +} + +void ClientHandler::remove_downstream_connection(DownstreamConnection *dconn) { + if (LOG_ENABLED(INFO)) { + CLOG(INFO, this) << "Removing downstream connection DCONN:" << dconn + << " from pool"; + } + auto dconn_pool = worker_->get_dconn_pool(); + dconn_pool->remove_downstream_connection(dconn); +} + +std::unique_ptr +ClientHandler::get_downstream_connection() { + auto dconn_pool = worker_->get_dconn_pool(); + auto dconn = dconn_pool->pop_downstream_connection(); + + if (!dconn) { + if (LOG_ENABLED(INFO)) { + CLOG(INFO, this) << "Downstream connection pool is empty." + << " Create new one"; + } + + auto dconn_pool = worker_->get_dconn_pool(); + + if (http2session_) { + dconn = make_unique(dconn_pool, http2session_); + } else { + dconn = make_unique(dconn_pool, conn_.loop); + } + dconn->set_client_handler(this); + return dconn; + } + + dconn->set_client_handler(this); + + if (LOG_ENABLED(INFO)) { + CLOG(INFO, this) << "Reuse downstream connection DCONN:" << dconn.get() + << " from pool"; + } + + return dconn; +} + +MemchunkPool *ClientHandler::get_mcpool() { return worker_->get_mcpool(); } + +SSL *ClientHandler::get_ssl() const { return conn_.tls.ssl; } + +ConnectBlocker *ClientHandler::get_connect_blocker() const { + return worker_->get_connect_blocker(); +} + +void ClientHandler::direct_http2_upgrade() { + upstream_ = make_unique(this); + alpn_ = NGHTTP2_CLEARTEXT_PROTO_VERSION_ID; + on_read_ = &ClientHandler::upstream_read; +} + +int ClientHandler::perform_http2_upgrade(HttpsUpstream *http) { + auto upstream = make_unique(this); + if (upstream->upgrade_upstream(http) != 0) { + return -1; + } + // http pointer is now owned by upstream. + upstream_.release(); + upstream_ = std::move(upstream); + // TODO We might get other version id in HTTP2-settings, if we + // support aliasing for h2, but we just use library default for now. + alpn_ = NGHTTP2_CLEARTEXT_PROTO_VERSION_ID; + on_read_ = &ClientHandler::upstream_http2_connhd_read; + + static char res[] = "HTTP/1.1 101 Switching Protocols\r\n" + "Connection: Upgrade\r\n" + "Upgrade: " NGHTTP2_CLEARTEXT_PROTO_VERSION_ID "\r\n" + "\r\n"; + wb_.write(res, sizeof(res) - 1); + signal_write(); + return 0; +} + +bool ClientHandler::get_http2_upgrade_allowed() const { return !conn_.tls.ssl; } + +std::string ClientHandler::get_upstream_scheme() const { + if (conn_.tls.ssl) { + return "https"; + } else { + return "http"; + } +} + +void ClientHandler::start_immediate_shutdown() { + ev_timer_start(conn_.loop, &reneg_shutdown_timer_); +} + +namespace { +// Construct absolute request URI from |downstream|, mainly to log +// request URI for proxy request (HTTP/2 proxy or client proxy). This +// is mostly same routine found in +// HttpDownstreamConnection::push_request_headers(), but vastly +// simplified since we only care about absolute URI. +std::string construct_absolute_request_uri(Downstream *downstream) { + const char *authority = nullptr, *host = nullptr; + if (!downstream->get_request_http2_authority().empty()) { + authority = downstream->get_request_http2_authority().c_str(); + } + auto h = downstream->get_request_header(http2::HD_HOST); + if (h) { + host = h->value.c_str(); + } + if (!authority && !host) { + return downstream->get_request_path(); + } + std::string uri; + if (downstream->get_request_http2_scheme().empty()) { + // this comes from HTTP/1 upstream without scheme. Just use http. + uri += "http://"; + } else { + uri += downstream->get_request_http2_scheme(); + uri += "://"; + } + if (authority) { + uri += authority; + } else { + uri += host; + } + + // Server-wide OPTIONS takes following form in proxy request: + // + // OPTIONS http://example.org HTTP/1.1 + // + // Notice that no slash after authority. See + // http://tools.ietf.org/html/rfc7230#section-5.3.4 + if (downstream->get_request_path() != "*") { + uri += downstream->get_request_path(); + } + return uri; +} +} // namespace + +void ClientHandler::write_accesslog(Downstream *downstream) { + upstream_accesslog( + get_config()->accesslog_format, + LogSpec{ + downstream, ipaddr_.c_str(), downstream->get_request_method().c_str(), + + (downstream->get_request_method() != "CONNECT" && + (get_config()->http2_proxy || get_config()->client_proxy)) + ? construct_absolute_request_uri(downstream).c_str() + : downstream->get_request_path().empty() + ? downstream->get_request_http2_authority().c_str() + : downstream->get_request_path().c_str(), + + alpn_.c_str(), + + std::chrono::system_clock::now(), // time_now + downstream->get_request_start_time(), // request_start_time + std::chrono::high_resolution_clock::now(), // request_end_time + + downstream->get_request_major(), downstream->get_request_minor(), + downstream->get_response_http_status(), + downstream->get_response_sent_bodylen(), port_.c_str(), + get_config()->port, get_config()->pid, + }); +} + +void ClientHandler::write_accesslog(int major, int minor, unsigned int status, + int64_t body_bytes_sent) { + auto time_now = std::chrono::system_clock::now(); + auto highres_now = std::chrono::high_resolution_clock::now(); + + upstream_accesslog(get_config()->accesslog_format, + LogSpec{ + nullptr, ipaddr_.c_str(), + "-", // method + "-", // path, + alpn_.c_str(), time_now, + highres_now, // request_start_time TODO is + // there a better value? + highres_now, // request_end_time + major, minor, // major, minor + status, body_bytes_sent, port_.c_str(), + get_config()->port, get_config()->pid, + }); +} + +ClientHandler::WriteBuf *ClientHandler::get_wb() { return &wb_; } + +ClientHandler::ReadBuf *ClientHandler::get_rb() { return &rb_; } + +void ClientHandler::signal_write() { conn_.wlimit.startw(); } + +RateLimit *ClientHandler::get_rlimit() { return &conn_.rlimit; } +RateLimit *ClientHandler::get_wlimit() { return &conn_.wlimit; } + +ev_io *ClientHandler::get_wev() { return &conn_.wev; } + +Worker *ClientHandler::get_worker() const { return worker_; } + +} // namespace shrpx diff --git a/src/shrpx_client_handler.h b/src/shrpx_client_handler.h new file mode 100644 index 0000000..4367c2b --- /dev/null +++ b/src/shrpx_client_handler.h @@ -0,0 +1,154 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef SHRPX_CLIENT_HANDLER_H +#define SHRPX_CLIENT_HANDLER_H + +#include "shrpx.h" + +#include + +#include + +#include + +#include "shrpx_rate_limit.h" +#include "shrpx_connection.h" +#include "buffer.h" +#include "memchunk.h" + +using namespace nghttp2; + +namespace shrpx { + +class Upstream; +class DownstreamConnection; +class Http2Session; +class HttpsUpstream; +class ConnectBlocker; +class DownstreamConnectionPool; +class Worker; +struct WorkerStat; + +class ClientHandler { +public: + ClientHandler(Worker *worker, int fd, SSL *ssl, const char *ipaddr, + const char *port); + ~ClientHandler(); + + // Performs clear text I/O + int read_clear(); + int write_clear(); + // Performs TLS handshake + int tls_handshake(); + // Performs TLS I/O + int read_tls(); + int write_tls(); + + int upstream_noop(); + int upstream_read(); + int upstream_http2_connhd_read(); + int upstream_http1_connhd_read(); + int upstream_write(); + + // Performs I/O operation. Internally calls on_read()/on_write(). + int do_read(); + int do_write(); + + // Processes buffers. No underlying I/O operation will be done. + int on_read(); + int on_write(); + + struct ev_loop *get_loop() const; + void reset_upstream_read_timeout(ev_tstamp t); + void reset_upstream_write_timeout(ev_tstamp t); + int validate_next_proto(); + const std::string &get_ipaddr() const; + const std::string &get_port() const; + bool get_should_close_after_write() const; + void set_should_close_after_write(bool f); + Upstream *get_upstream(); + + void pool_downstream_connection(std::unique_ptr dconn); + void remove_downstream_connection(DownstreamConnection *dconn); + std::unique_ptr get_downstream_connection(); + MemchunkPool *get_mcpool(); + SSL *get_ssl() const; + ConnectBlocker *get_connect_blocker() const; + // Call this function when HTTP/2 connection header is received at + // the start of the connection. + void direct_http2_upgrade(); + // Performs HTTP/2 Upgrade from the connection managed by + // |http|. If this function fails, the connection must be + // terminated. This function returns 0 if it succeeds, or -1. + int perform_http2_upgrade(HttpsUpstream *http); + bool get_http2_upgrade_allowed() const; + // Returns upstream scheme, either "http" or "https" + std::string get_upstream_scheme() const; + void start_immediate_shutdown(); + + // Writes upstream accesslog using |downstream|. The |downstream| + // must not be nullptr. + void write_accesslog(Downstream *downstream); + + // Writes upstream accesslog. This function is used if + // corresponding Downstream object is not available. + void write_accesslog(int major, int minor, unsigned int status, + int64_t body_bytes_sent); + Worker *get_worker() const; + + using WriteBuf = Buffer<32768>; + using ReadBuf = Buffer<8192>; + + WriteBuf *get_wb(); + ReadBuf *get_rb(); + + RateLimit *get_rlimit(); + RateLimit *get_wlimit(); + + void signal_write(); + ev_io *get_wev(); + +private: + Connection conn_; + ev_timer reneg_shutdown_timer_; + std::unique_ptr upstream_; + std::string ipaddr_; + std::string port_; + // The ALPN identifier negotiated for this connection. + std::string alpn_; + std::function read_, write_; + std::function on_read_, on_write_; + Worker *worker_; + Http2Session *http2session_; + // The number of bytes of HTTP/2 client connection header to read + size_t left_connhd_len_; + bool should_close_after_write_; + WriteBuf wb_; + ReadBuf rb_; +}; + +} // namespace shrpx + +#endif // SHRPX_CLIENT_HANDLER_H diff --git a/src/shrpx_config.cc b/src/shrpx_config.cc new file mode 100644 index 0000000..6577343 --- /dev/null +++ b/src/shrpx_config.cc @@ -0,0 +1,1406 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "shrpx_config.h" + +#ifdef HAVE_PWD_H +#include +#endif // HAVE_PWD_H +#ifdef HAVE_NETDB_H +#include +#endif // HAVE_NETDB_H +#ifdef HAVE_SYSLOG_H +#include +#endif // HAVE_SYSLOG_H +#include +#include +#ifdef HAVE_FCNTL_H +#include +#endif // HAVE_FCNTL_H +#ifdef HAVE_UNISTD_H +#include +#endif // HAVE_UNISTD_H + +#include +#include +#include +#include + +#include + +#include "http-parser/http_parser.h" + +#include "shrpx_log.h" +#include "shrpx_ssl.h" +#include "shrpx_http.h" +#include "http2.h" +#include "util.h" +#include "template.h" + +using namespace nghttp2; + +namespace shrpx { + +const char SHRPX_OPT_PRIVATE_KEY_FILE[] = "private-key-file"; +const char SHRPX_OPT_PRIVATE_KEY_PASSWD_FILE[] = "private-key-passwd-file"; +const char SHRPX_OPT_CERTIFICATE_FILE[] = "certificate-file"; +const char SHRPX_OPT_DH_PARAM_FILE[] = "dh-param-file"; +const char SHRPX_OPT_SUBCERT[] = "subcert"; + +const char SHRPX_OPT_BACKEND[] = "backend"; +const char SHRPX_OPT_FRONTEND[] = "frontend"; +const char SHRPX_OPT_WORKERS[] = "workers"; +const char SHRPX_OPT_HTTP2_MAX_CONCURRENT_STREAMS[] = + "http2-max-concurrent-streams"; +const char SHRPX_OPT_LOG_LEVEL[] = "log-level"; +const char SHRPX_OPT_DAEMON[] = "daemon"; +const char SHRPX_OPT_HTTP2_PROXY[] = "http2-proxy"; +const char SHRPX_OPT_HTTP2_BRIDGE[] = "http2-bridge"; +const char SHRPX_OPT_CLIENT_PROXY[] = "client-proxy"; +const char SHRPX_OPT_ADD_X_FORWARDED_FOR[] = "add-x-forwarded-for"; +const char SHRPX_OPT_STRIP_INCOMING_X_FORWARDED_FOR[] = + "strip-incoming-x-forwarded-for"; +const char SHRPX_OPT_NO_VIA[] = "no-via"; +const char SHRPX_OPT_FRONTEND_HTTP2_READ_TIMEOUT[] = + "frontend-http2-read-timeout"; +const char SHRPX_OPT_FRONTEND_READ_TIMEOUT[] = "frontend-read-timeout"; +const char SHRPX_OPT_FRONTEND_WRITE_TIMEOUT[] = "frontend-write-timeout"; +const char SHRPX_OPT_BACKEND_READ_TIMEOUT[] = "backend-read-timeout"; +const char SHRPX_OPT_BACKEND_WRITE_TIMEOUT[] = "backend-write-timeout"; +const char SHRPX_OPT_STREAM_READ_TIMEOUT[] = "stream-read-timeout"; +const char SHRPX_OPT_STREAM_WRITE_TIMEOUT[] = "stream-write-timeout"; +const char SHRPX_OPT_ACCESSLOG_FILE[] = "accesslog-file"; +const char SHRPX_OPT_ACCESSLOG_SYSLOG[] = "accesslog-syslog"; +const char SHRPX_OPT_ACCESSLOG_FORMAT[] = "accesslog-format"; +const char SHRPX_OPT_ERRORLOG_FILE[] = "errorlog-file"; +const char SHRPX_OPT_ERRORLOG_SYSLOG[] = "errorlog-syslog"; +const char SHRPX_OPT_BACKEND_KEEP_ALIVE_TIMEOUT[] = + "backend-keep-alive-timeout"; +const char SHRPX_OPT_FRONTEND_HTTP2_WINDOW_BITS[] = + "frontend-http2-window-bits"; +const char SHRPX_OPT_BACKEND_HTTP2_WINDOW_BITS[] = "backend-http2-window-bits"; +const char SHRPX_OPT_FRONTEND_HTTP2_CONNECTION_WINDOW_BITS[] = + "frontend-http2-connection-window-bits"; +const char SHRPX_OPT_BACKEND_HTTP2_CONNECTION_WINDOW_BITS[] = + "backend-http2-connection-window-bits"; +const char SHRPX_OPT_FRONTEND_NO_TLS[] = "frontend-no-tls"; +const char SHRPX_OPT_BACKEND_NO_TLS[] = "backend-no-tls"; +const char SHRPX_OPT_BACKEND_TLS_SNI_FIELD[] = "backend-tls-sni-field"; +const char SHRPX_OPT_PID_FILE[] = "pid-file"; +const char SHRPX_OPT_USER[] = "user"; +const char SHRPX_OPT_SYSLOG_FACILITY[] = "syslog-facility"; +const char SHRPX_OPT_BACKLOG[] = "backlog"; +const char SHRPX_OPT_CIPHERS[] = "ciphers"; +const char SHRPX_OPT_CLIENT[] = "client"; +const char SHRPX_OPT_INSECURE[] = "insecure"; +const char SHRPX_OPT_CACERT[] = "cacert"; +const char SHRPX_OPT_BACKEND_IPV4[] = "backend-ipv4"; +const char SHRPX_OPT_BACKEND_IPV6[] = "backend-ipv6"; +const char SHRPX_OPT_BACKEND_HTTP_PROXY_URI[] = "backend-http-proxy-uri"; +const char SHRPX_OPT_READ_RATE[] = "read-rate"; +const char SHRPX_OPT_READ_BURST[] = "read-burst"; +const char SHRPX_OPT_WRITE_RATE[] = "write-rate"; +const char SHRPX_OPT_WRITE_BURST[] = "write-burst"; +const char SHRPX_OPT_WORKER_READ_RATE[] = "worker-read-rate"; +const char SHRPX_OPT_WORKER_READ_BURST[] = "worker-read-burst"; +const char SHRPX_OPT_WORKER_WRITE_RATE[] = "worker-write-rate"; +const char SHRPX_OPT_WORKER_WRITE_BURST[] = "worker-write-burst"; +const char SHRPX_OPT_NPN_LIST[] = "npn-list"; +const char SHRPX_OPT_TLS_PROTO_LIST[] = "tls-proto-list"; +const char SHRPX_OPT_VERIFY_CLIENT[] = "verify-client"; +const char SHRPX_OPT_VERIFY_CLIENT_CACERT[] = "verify-client-cacert"; +const char SHRPX_OPT_CLIENT_PRIVATE_KEY_FILE[] = "client-private-key-file"; +const char SHRPX_OPT_CLIENT_CERT_FILE[] = "client-cert-file"; +const char SHRPX_OPT_FRONTEND_HTTP2_DUMP_REQUEST_HEADER[] = + "frontend-http2-dump-request-header"; +const char SHRPX_OPT_FRONTEND_HTTP2_DUMP_RESPONSE_HEADER[] = + "frontend-http2-dump-response-header"; +const char SHRPX_OPT_HTTP2_NO_COOKIE_CRUMBLING[] = "http2-no-cookie-crumbling"; +const char SHRPX_OPT_FRONTEND_FRAME_DEBUG[] = "frontend-frame-debug"; +const char SHRPX_OPT_PADDING[] = "padding"; +const char SHRPX_OPT_ALTSVC[] = "altsvc"; +const char SHRPX_OPT_ADD_RESPONSE_HEADER[] = "add-response-header"; +const char SHRPX_OPT_WORKER_FRONTEND_CONNECTIONS[] = + "worker-frontend-connections"; +const char SHRPX_OPT_NO_LOCATION_REWRITE[] = "no-location-rewrite"; +const char SHRPX_OPT_NO_HOST_REWRITE[] = "no-host-rewrite"; +const char SHRPX_OPT_BACKEND_HTTP1_CONNECTIONS_PER_HOST[] = + "backend-http1-connections-per-host"; +const char SHRPX_OPT_BACKEND_HTTP1_CONNECTIONS_PER_FRONTEND[] = + "backend-http1-connections-per-frontend"; +const char SHRPX_OPT_LISTENER_DISABLE_TIMEOUT[] = "listener-disable-timeout"; +const char SHRPX_OPT_TLS_TICKET_KEY_FILE[] = "tls-ticket-key-file"; +const char SHRPX_OPT_RLIMIT_NOFILE[] = "rlimit-nofile"; +const char SHRPX_OPT_BACKEND_REQUEST_BUFFER[] = "backend-request-buffer"; +const char SHRPX_OPT_BACKEND_RESPONSE_BUFFER[] = "backend-response-buffer"; +const char SHRPX_OPT_NO_SERVER_PUSH[] = "no-server-push"; +const char SHRPX_OPT_BACKEND_HTTP2_CONNECTIONS_PER_WORKER[] = + "backend-http2-connections-per-worker"; +const char SHRPX_OPT_FETCH_OCSP_RESPONSE_FILE[] = "fetch-ocsp-response-file"; +const char SHRPX_OPT_OCSP_UPDATE_INTERVAL[] = "ocsp-update-interval"; +const char SHRPX_OPT_NO_OCSP[] = "no-ocsp"; +const char SHRPX_OPT_HEADER_FIELD_BUFFER[] = "header-field-buffer"; +const char SHRPX_OPT_MAX_HEADER_FIELDS[] = "max-header-fields"; + +namespace { +Config *config = nullptr; +} // namespace + +const Config *get_config() { return config; } + +Config *mod_config() { return config; } + +void create_config() { config = new Config(); } + +TicketKeys::~TicketKeys() { + /* Erase keys from memory */ + for (auto &key : keys) { + memset(&key, 0, sizeof(key)); + } +} + +namespace { +int split_host_port(char *host, size_t hostlen, uint16_t *port_ptr, + const char *hostport) { + // host and port in |hostport| is separated by single ','. + const char *p = strchr(hostport, ','); + if (!p) { + LOG(ERROR) << "Invalid host, port: " << hostport; + return -1; + } + size_t len = p - hostport; + if (hostlen < len + 1) { + LOG(ERROR) << "Hostname too long: " << hostport; + return -1; + } + memcpy(host, hostport, len); + host[len] = '\0'; + + errno = 0; + unsigned long d = strtoul(p + 1, nullptr, 10); + if (errno == 0 && 1 <= d && d <= std::numeric_limits::max()) { + *port_ptr = d; + return 0; + } else { + LOG(ERROR) << "Port is invalid: " << p + 1; + return -1; + } +} +} // namespace + +namespace { +bool is_secure(const char *filename) { + struct stat buf; + int rv = stat(filename, &buf); + if (rv == 0) { + if ((buf.st_mode & S_IRWXU) && !(buf.st_mode & S_IRWXG) && + !(buf.st_mode & S_IRWXO)) { + return true; + } + } + + return false; +} +} // namespace + +std::unique_ptr +read_tls_ticket_key_file(const std::vector &files) { + auto ticket_keys = make_unique(); + auto &keys = ticket_keys->keys; + keys.resize(files.size()); + size_t i = 0; + for (auto &file : files) { + std::ifstream f(file.c_str()); + if (!f) { + LOG(ERROR) << "tls-ticket-key-file: could not open file " << file; + return nullptr; + } + char buf[48]; + f.read(buf, sizeof(buf)); + if (f.gcount() != sizeof(buf)) { + LOG(ERROR) << "tls-ticket-key-file: want to read 48 bytes but read " + << f.gcount() << " bytes from " << file; + return nullptr; + } + + auto &key = keys[i++]; + auto p = buf; + memcpy(key.name, p, sizeof(key.name)); + p += sizeof(key.name); + memcpy(key.aes_key, p, sizeof(key.aes_key)); + p += sizeof(key.aes_key); + memcpy(key.hmac_key, p, sizeof(key.hmac_key)); + + if (LOG_ENABLED(INFO)) { + LOG(INFO) << "session ticket key: " << util::format_hex(key.name, + sizeof(key.name)); + } + } + return ticket_keys; +} + +FILE *open_file_for_write(const char *filename) { +#if defined O_CLOEXEC + auto fd = open(filename, O_WRONLY | O_CLOEXEC | O_CREAT | O_TRUNC, + S_IRUSR | S_IWUSR); +#else + auto fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR); + + // We get race condition if execve is called at the same time. + if (fd != -1) { + util::make_socket_closeonexec(fd); + } +#endif + if (fd == -1) { + LOG(ERROR) << "Failed to open " << filename + << " for writing. Cause: " << strerror(errno); + return nullptr; + } + auto f = fdopen(fd, "wb"); + if (f == nullptr) { + LOG(ERROR) << "Failed to open " << filename + << " for writing. Cause: " << strerror(errno); + return nullptr; + } + + return f; +} + +std::string read_passwd_from_file(const char *filename) { + std::string line; + + if (!is_secure(filename)) { + LOG(ERROR) << "Private key passwd file " << filename + << " has insecure mode."; + return line; + } + + std::ifstream in(filename, std::ios::binary); + if (!in) { + LOG(ERROR) << "Could not open key passwd file " << filename; + return line; + } + + std::getline(in, line); + return line; +} + +std::unique_ptr strcopy(const char *val) { + return strcopy(val, strlen(val)); +} + +std::unique_ptr strcopy(const char *val, size_t len) { + auto res = make_unique(len + 1); + memcpy(res.get(), val, len); + res[len] = '\0'; + return res; +} + +std::unique_ptr strcopy(const std::string &val) { + return strcopy(val.c_str(), val.size()); +} + +std::vector parse_config_str_list(const char *s) { + size_t len = 1; + for (const char *first = s, *p = nullptr; (p = strchr(first, ',')); + ++len, first = p + 1) + ; + auto list = std::vector(len); + auto first = strdup(s); + len = 0; + for (;;) { + auto p = strchr(first, ','); + if (p == nullptr) { + break; + } + list[len++] = first; + *p = '\0'; + first = p + 1; + } + list[len++] = first; + + return list; +} + +void clear_config_str_list(std::vector &list) { + if (list.empty()) { + return; + } + + free(list[0]); + list.clear(); +} + +std::pair parse_header(const char *optarg) { + // We skip possible ":" at the start of optarg. + const auto *colon = strchr(optarg + 1, ':'); + + // name = ":" is not allowed + if (colon == nullptr || (optarg[0] == ':' && colon == optarg + 1)) { + return {"", ""}; + } + + auto value = colon + 1; + for (; *value == '\t' || *value == ' '; ++value) + ; + + return {std::string(optarg, colon), std::string(value, strlen(value))}; +} + +template +int parse_uint(T *dest, const char *opt, const char *optarg) { + char *end = nullptr; + + errno = 0; + + auto val = strtol(optarg, &end, 10); + + if (!optarg[0] || errno != 0 || *end || val < 0) { + LOG(ERROR) << opt << ": bad value. Specify an integer >= 0."; + return -1; + } + + *dest = val; + + return 0; +} + +namespace { +template +int parse_uint_with_unit(T *dest, const char *opt, const char *optarg) { + auto n = util::parse_uint_with_unit(optarg); + if (n == -1) { + LOG(ERROR) << opt << ": bad value: '" << optarg << "'"; + return -1; + } + + *dest = n; + + return 0; +} +} // namespace + +template +int parse_int(T *dest, const char *opt, const char *optarg) { + char *end = nullptr; + + errno = 0; + + auto val = strtol(optarg, &end, 10); + + if (!optarg[0] || errno != 0 || *end) { + LOG(ERROR) << opt << ": bad value. Specify an integer."; + return -1; + } + + *dest = val; + + return 0; +} + +namespace { +LogFragment make_log_fragment(LogFragmentType type, + std::unique_ptr value = nullptr) { + return LogFragment{type, std::move(value)}; +} +} // namespace + +namespace { +bool var_token(char c) { + return util::isAlpha(c) || util::isDigit(c) || c == '_'; +} +} // namespace + +std::vector parse_log_format(const char *optarg) { + auto literal_start = optarg; + auto p = optarg; + auto eop = p + strlen(optarg); + + auto res = std::vector(); + + for (; p != eop;) { + if (*p != '$') { + ++p; + continue; + } + + auto var_start = p; + + ++p; + + for (; p != eop && var_token(*p); ++p) + ; + + auto varlen = p - var_start; + + auto type = SHRPX_LOGF_NONE; + const char *value = nullptr; + size_t valuelen = 0; + + if (util::strieq_l("$remote_addr", var_start, varlen)) { + type = SHRPX_LOGF_REMOTE_ADDR; + } else if (util::strieq_l("$time_local", var_start, varlen)) { + type = SHRPX_LOGF_TIME_LOCAL; + } else if (util::strieq_l("$time_iso8601", var_start, varlen)) { + type = SHRPX_LOGF_TIME_ISO8601; + } else if (util::strieq_l("$request", var_start, varlen)) { + type = SHRPX_LOGF_REQUEST; + } else if (util::strieq_l("$status", var_start, varlen)) { + type = SHRPX_LOGF_STATUS; + } else if (util::strieq_l("$body_bytes_sent", var_start, varlen)) { + type = SHRPX_LOGF_BODY_BYTES_SENT; + } else if (util::istartsWith(var_start, varlen, "$http_")) { + type = SHRPX_LOGF_HTTP; + value = var_start + sizeof("$http_") - 1; + valuelen = varlen - (sizeof("$http_") - 1); + } else if (util::strieq_l("$remote_port", var_start, varlen)) { + type = SHRPX_LOGF_REMOTE_PORT; + } else if (util::strieq_l("$server_port", var_start, varlen)) { + type = SHRPX_LOGF_SERVER_PORT; + } else if (util::strieq_l("$request_time", var_start, varlen)) { + type = SHRPX_LOGF_REQUEST_TIME; + } else if (util::strieq_l("$pid", var_start, varlen)) { + type = SHRPX_LOGF_PID; + } else if (util::strieq_l("$alpn", var_start, varlen)) { + type = SHRPX_LOGF_ALPN; + } else { + LOG(WARN) << "Unrecognized log format variable: " + << std::string(var_start, varlen); + continue; + } + + if (literal_start < var_start) { + res.push_back( + make_log_fragment(SHRPX_LOGF_LITERAL, + strcopy(literal_start, var_start - literal_start))); + } + + if (value == nullptr) { + res.push_back(make_log_fragment(type)); + } else { + res.push_back(make_log_fragment(type, strcopy(value, valuelen))); + auto &v = res.back().value; + for (size_t i = 0; v[i]; ++i) { + if (v[i] == '_') { + v[i] = '-'; + } + } + } + + literal_start = var_start + varlen; + } + + if (literal_start != eop) { + res.push_back(make_log_fragment( + SHRPX_LOGF_LITERAL, strcopy(literal_start, eop - literal_start))); + } + + return res; +} + +namespace { +int parse_duration(ev_tstamp *dest, const char *opt, const char *optarg) { + auto t = util::parse_duration_with_unit(optarg); + if (t == std::numeric_limits::infinity()) { + LOG(ERROR) << opt << ": bad value: '" << optarg << "'"; + return -1; + } + + *dest = t; + + return 0; +} +} // namespace + +int parse_config(const char *opt, const char *optarg) { + char host[NI_MAXHOST]; + uint16_t port; + if (util::strieq(opt, SHRPX_OPT_BACKEND)) { + if (util::istartsWith(optarg, SHRPX_UNIX_PATH_PREFIX)) { + DownstreamAddr addr; + auto path = optarg + str_size(SHRPX_UNIX_PATH_PREFIX); + addr.host = strcopy(path); + addr.host_unix = true; + + mod_config()->downstream_addrs.push_back(std::move(addr)); + + return 0; + } + + if (split_host_port(host, sizeof(host), &port, optarg) == -1) { + return -1; + } + + DownstreamAddr addr; + addr.host = strcopy(host); + addr.port = port; + + mod_config()->downstream_addrs.push_back(std::move(addr)); + + return 0; + } + + if (util::strieq(opt, SHRPX_OPT_FRONTEND)) { + if (util::istartsWith(optarg, SHRPX_UNIX_PATH_PREFIX)) { + auto path = optarg + str_size(SHRPX_UNIX_PATH_PREFIX); + mod_config()->host = strcopy(path); + mod_config()->port = 0; + mod_config()->host_unix = true; + + return 0; + } + + if (split_host_port(host, sizeof(host), &port, optarg) == -1) { + return -1; + } + + mod_config()->host = strcopy(host); + mod_config()->port = port; + mod_config()->host_unix = false; + + return 0; + } + + if (util::strieq(opt, SHRPX_OPT_WORKERS)) { + return parse_uint(&mod_config()->num_worker, opt, optarg); + } + + if (util::strieq(opt, SHRPX_OPT_HTTP2_MAX_CONCURRENT_STREAMS)) { + return parse_uint(&mod_config()->http2_max_concurrent_streams, opt, optarg); + } + + if (util::strieq(opt, SHRPX_OPT_LOG_LEVEL)) { + if (Log::set_severity_level_by_name(optarg) == -1) { + LOG(ERROR) << opt << ": Invalid severity level: " << optarg; + return -1; + } + + return 0; + } + + if (util::strieq(opt, SHRPX_OPT_DAEMON)) { + mod_config()->daemon = util::strieq(optarg, "yes"); + + return 0; + } + + if (util::strieq(opt, SHRPX_OPT_HTTP2_PROXY)) { + mod_config()->http2_proxy = util::strieq(optarg, "yes"); + + return 0; + } + + if (util::strieq(opt, SHRPX_OPT_HTTP2_BRIDGE)) { + mod_config()->http2_bridge = util::strieq(optarg, "yes"); + + return 0; + } + + if (util::strieq(opt, SHRPX_OPT_CLIENT_PROXY)) { + mod_config()->client_proxy = util::strieq(optarg, "yes"); + + return 0; + } + + if (util::strieq(opt, SHRPX_OPT_ADD_X_FORWARDED_FOR)) { + mod_config()->add_x_forwarded_for = util::strieq(optarg, "yes"); + + return 0; + } + + if (util::strieq(opt, SHRPX_OPT_STRIP_INCOMING_X_FORWARDED_FOR)) { + mod_config()->strip_incoming_x_forwarded_for = util::strieq(optarg, "yes"); + + return 0; + } + + if (util::strieq(opt, SHRPX_OPT_NO_VIA)) { + mod_config()->no_via = util::strieq(optarg, "yes"); + + return 0; + } + + if (util::strieq(opt, SHRPX_OPT_FRONTEND_HTTP2_READ_TIMEOUT)) { + return parse_duration(&mod_config()->http2_upstream_read_timeout, opt, + optarg); + } + + if (util::strieq(opt, SHRPX_OPT_FRONTEND_READ_TIMEOUT)) { + return parse_duration(&mod_config()->upstream_read_timeout, opt, optarg); + } + + if (util::strieq(opt, SHRPX_OPT_FRONTEND_WRITE_TIMEOUT)) { + return parse_duration(&mod_config()->upstream_write_timeout, opt, optarg); + } + + if (util::strieq(opt, SHRPX_OPT_BACKEND_READ_TIMEOUT)) { + return parse_duration(&mod_config()->downstream_read_timeout, opt, optarg); + } + + if (util::strieq(opt, SHRPX_OPT_BACKEND_WRITE_TIMEOUT)) { + return parse_duration(&mod_config()->downstream_write_timeout, opt, optarg); + } + + if (util::strieq(opt, SHRPX_OPT_STREAM_READ_TIMEOUT)) { + return parse_duration(&mod_config()->stream_read_timeout, opt, optarg); + } + + if (util::strieq(opt, SHRPX_OPT_STREAM_WRITE_TIMEOUT)) { + return parse_duration(&mod_config()->stream_write_timeout, opt, optarg); + } + + if (util::strieq(opt, SHRPX_OPT_ACCESSLOG_FILE)) { + mod_config()->accesslog_file = strcopy(optarg); + + return 0; + } + + if (util::strieq(opt, SHRPX_OPT_ACCESSLOG_SYSLOG)) { + mod_config()->accesslog_syslog = util::strieq(optarg, "yes"); + + return 0; + } + + if (util::strieq(opt, SHRPX_OPT_ACCESSLOG_FORMAT)) { + mod_config()->accesslog_format = parse_log_format(optarg); + + return 0; + } + + if (util::strieq(opt, SHRPX_OPT_ERRORLOG_FILE)) { + mod_config()->errorlog_file = strcopy(optarg); + + return 0; + } + + if (util::strieq(opt, SHRPX_OPT_ERRORLOG_SYSLOG)) { + mod_config()->errorlog_syslog = util::strieq(optarg, "yes"); + + return 0; + } + + if (util::strieq(opt, SHRPX_OPT_BACKEND_KEEP_ALIVE_TIMEOUT)) { + return parse_duration(&mod_config()->downstream_idle_read_timeout, opt, + optarg); + } + + if (util::strieq(opt, SHRPX_OPT_FRONTEND_HTTP2_WINDOW_BITS) || + util::strieq(opt, SHRPX_OPT_BACKEND_HTTP2_WINDOW_BITS)) { + + size_t *resp; + + if (util::strieq(opt, SHRPX_OPT_FRONTEND_HTTP2_WINDOW_BITS)) { + resp = &mod_config()->http2_upstream_window_bits; + } else { + resp = &mod_config()->http2_downstream_window_bits; + } + + errno = 0; + + int n; + + if (parse_uint(&n, opt, optarg) != 0) { + return -1; + } + + if (n >= 31) { + LOG(ERROR) << opt + << ": specify the integer in the range [0, 30], inclusive"; + return -1; + } + + *resp = n; + + return 0; + } + + if (util::strieq(opt, SHRPX_OPT_FRONTEND_HTTP2_CONNECTION_WINDOW_BITS) || + util::strieq(opt, SHRPX_OPT_BACKEND_HTTP2_CONNECTION_WINDOW_BITS)) { + + size_t *resp; + + if (util::strieq(opt, SHRPX_OPT_FRONTEND_HTTP2_CONNECTION_WINDOW_BITS)) { + resp = &mod_config()->http2_upstream_connection_window_bits; + } else { + resp = &mod_config()->http2_downstream_connection_window_bits; + } + + errno = 0; + + int n; + + if (parse_uint(&n, opt, optarg) != 0) { + return -1; + } + + if (n < 16 || n >= 31) { + LOG(ERROR) << opt + << ": specify the integer in the range [16, 30], inclusive"; + return -1; + } + + *resp = n; + + return 0; + } + + if (util::strieq(opt, SHRPX_OPT_FRONTEND_NO_TLS)) { + mod_config()->upstream_no_tls = util::strieq(optarg, "yes"); + + return 0; + } + + if (util::strieq(opt, SHRPX_OPT_BACKEND_NO_TLS)) { + mod_config()->downstream_no_tls = util::strieq(optarg, "yes"); + + return 0; + } + + if (util::strieq(opt, SHRPX_OPT_BACKEND_TLS_SNI_FIELD)) { + mod_config()->backend_tls_sni_name = strcopy(optarg); + + return 0; + } + + if (util::strieq(opt, SHRPX_OPT_PID_FILE)) { + mod_config()->pid_file = strcopy(optarg); + + return 0; + } + + if (util::strieq(opt, SHRPX_OPT_USER)) { + auto pwd = getpwnam(optarg); + if (!pwd) { + LOG(ERROR) << opt << ": failed to get uid from " << optarg << ": " + << strerror(errno); + return -1; + } + mod_config()->user = strcopy(pwd->pw_name); + mod_config()->uid = pwd->pw_uid; + mod_config()->gid = pwd->pw_gid; + + return 0; + } + + if (util::strieq(opt, SHRPX_OPT_PRIVATE_KEY_FILE)) { + mod_config()->private_key_file = strcopy(optarg); + + return 0; + } + + if (util::strieq(opt, SHRPX_OPT_PRIVATE_KEY_PASSWD_FILE)) { + auto passwd = read_passwd_from_file(optarg); + if (passwd.empty()) { + LOG(ERROR) << opt << ": Couldn't read key file's passwd from " << optarg; + return -1; + } + mod_config()->private_key_passwd = strcopy(passwd); + + return 0; + } + + if (util::strieq(opt, SHRPX_OPT_CERTIFICATE_FILE)) { + mod_config()->cert_file = strcopy(optarg); + + return 0; + } + + if (util::strieq(opt, SHRPX_OPT_DH_PARAM_FILE)) { + mod_config()->dh_param_file = strcopy(optarg); + + return 0; + } + + if (util::strieq(opt, SHRPX_OPT_SUBCERT)) { + // Private Key file and certificate file separated by ':'. + const char *sp = strchr(optarg, ':'); + if (sp) { + std::string keyfile(optarg, sp); + // TODO Do we need private key for subcert? + mod_config()->subcerts.emplace_back(keyfile, sp + 1); + } + + return 0; + } + + if (util::strieq(opt, SHRPX_OPT_SYSLOG_FACILITY)) { + int facility = int_syslog_facility(optarg); + if (facility == -1) { + LOG(ERROR) << opt << ": Unknown syslog facility: " << optarg; + return -1; + } + mod_config()->syslog_facility = facility; + + return 0; + } + + if (util::strieq(opt, SHRPX_OPT_BACKLOG)) { + int n; + if (parse_int(&n, opt, optarg) != 0) { + return -1; + } + + if (n < -1) { + LOG(ERROR) << opt << ": " << optarg << " is not allowed"; + + return -1; + } + + mod_config()->backlog = n; + + return 0; + } + + if (util::strieq(opt, SHRPX_OPT_CIPHERS)) { + mod_config()->ciphers = strcopy(optarg); + + return 0; + } + + if (util::strieq(opt, SHRPX_OPT_CLIENT)) { + mod_config()->client = util::strieq(optarg, "yes"); + + return 0; + } + + if (util::strieq(opt, SHRPX_OPT_INSECURE)) { + mod_config()->insecure = util::strieq(optarg, "yes"); + + return 0; + } + + if (util::strieq(opt, SHRPX_OPT_CACERT)) { + mod_config()->cacert = strcopy(optarg); + + return 0; + } + + if (util::strieq(opt, SHRPX_OPT_BACKEND_IPV4)) { + mod_config()->backend_ipv4 = util::strieq(optarg, "yes"); + + return 0; + } + + if (util::strieq(opt, SHRPX_OPT_BACKEND_IPV6)) { + mod_config()->backend_ipv6 = util::strieq(optarg, "yes"); + + return 0; + } + + if (util::strieq(opt, SHRPX_OPT_BACKEND_HTTP_PROXY_URI)) { + // parse URI and get hostname, port and optionally userinfo. + http_parser_url u; + memset(&u, 0, sizeof(u)); + int rv = http_parser_parse_url(optarg, strlen(optarg), 0, &u); + if (rv == 0) { + std::string val; + if (u.field_set & UF_USERINFO) { + http2::copy_url_component(val, &u, UF_USERINFO, optarg); + // Surprisingly, u.field_set & UF_USERINFO is nonzero even if + // userinfo component is empty string. + if (!val.empty()) { + val = util::percentDecode(val.begin(), val.end()); + mod_config()->downstream_http_proxy_userinfo = strcopy(val); + } + } + if (u.field_set & UF_HOST) { + http2::copy_url_component(val, &u, UF_HOST, optarg); + mod_config()->downstream_http_proxy_host = strcopy(val); + } else { + LOG(ERROR) << opt << ": no hostname specified"; + return -1; + } + if (u.field_set & UF_PORT) { + mod_config()->downstream_http_proxy_port = u.port; + } else { + LOG(ERROR) << opt << ": no port specified"; + return -1; + } + } else { + LOG(ERROR) << opt << ": parse error"; + return -1; + } + + return 0; + } + + if (util::strieq(opt, SHRPX_OPT_READ_RATE)) { + return parse_uint_with_unit(&mod_config()->read_rate, opt, optarg); + } + + if (util::strieq(opt, SHRPX_OPT_READ_BURST)) { + return parse_uint_with_unit(&mod_config()->read_burst, opt, optarg); + } + + if (util::strieq(opt, SHRPX_OPT_WRITE_RATE)) { + return parse_uint_with_unit(&mod_config()->write_rate, opt, optarg); + } + + if (util::strieq(opt, SHRPX_OPT_WRITE_BURST)) { + return parse_uint_with_unit(&mod_config()->write_burst, opt, optarg); + } + + if (util::strieq(opt, SHRPX_OPT_WORKER_READ_RATE)) { + LOG(WARN) << opt << ": not implemented yet"; + return parse_uint_with_unit(&mod_config()->worker_read_rate, opt, optarg); + } + + if (util::strieq(opt, SHRPX_OPT_WORKER_READ_BURST)) { + LOG(WARN) << opt << ": not implemented yet"; + return parse_uint_with_unit(&mod_config()->worker_read_burst, opt, optarg); + } + + if (util::strieq(opt, SHRPX_OPT_WORKER_WRITE_RATE)) { + LOG(WARN) << opt << ": not implemented yet"; + return parse_uint_with_unit(&mod_config()->worker_write_rate, opt, optarg); + } + + if (util::strieq(opt, SHRPX_OPT_WORKER_WRITE_BURST)) { + LOG(WARN) << opt << ": not implemented yet"; + return parse_uint_with_unit(&mod_config()->worker_write_burst, opt, optarg); + } + + if (util::strieq(opt, SHRPX_OPT_NPN_LIST)) { + clear_config_str_list(mod_config()->npn_list); + + mod_config()->npn_list = parse_config_str_list(optarg); + + return 0; + } + + if (util::strieq(opt, SHRPX_OPT_TLS_PROTO_LIST)) { + clear_config_str_list(mod_config()->tls_proto_list); + + mod_config()->tls_proto_list = parse_config_str_list(optarg); + + return 0; + } + + if (util::strieq(opt, SHRPX_OPT_VERIFY_CLIENT)) { + mod_config()->verify_client = util::strieq(optarg, "yes"); + + return 0; + } + + if (util::strieq(opt, SHRPX_OPT_VERIFY_CLIENT_CACERT)) { + mod_config()->verify_client_cacert = strcopy(optarg); + + return 0; + } + + if (util::strieq(opt, SHRPX_OPT_CLIENT_PRIVATE_KEY_FILE)) { + mod_config()->client_private_key_file = strcopy(optarg); + + return 0; + } + + if (util::strieq(opt, SHRPX_OPT_CLIENT_CERT_FILE)) { + mod_config()->client_cert_file = strcopy(optarg); + + return 0; + } + + if (util::strieq(opt, SHRPX_OPT_FRONTEND_HTTP2_DUMP_REQUEST_HEADER)) { + mod_config()->http2_upstream_dump_request_header_file = strcopy(optarg); + + return 0; + } + + if (util::strieq(opt, SHRPX_OPT_FRONTEND_HTTP2_DUMP_RESPONSE_HEADER)) { + mod_config()->http2_upstream_dump_response_header_file = strcopy(optarg); + + return 0; + } + + if (util::strieq(opt, SHRPX_OPT_HTTP2_NO_COOKIE_CRUMBLING)) { + mod_config()->http2_no_cookie_crumbling = util::strieq(optarg, "yes"); + + return 0; + } + + if (util::strieq(opt, SHRPX_OPT_FRONTEND_FRAME_DEBUG)) { + mod_config()->upstream_frame_debug = util::strieq(optarg, "yes"); + + return 0; + } + + if (util::strieq(opt, SHRPX_OPT_PADDING)) { + return parse_uint(&mod_config()->padding, opt, optarg); + } + + if (util::strieq(opt, SHRPX_OPT_ALTSVC)) { + auto tokens = parse_config_str_list(optarg); + + if (tokens.size() < 2) { + // Requires at least protocol_id and port + LOG(ERROR) << opt << ": too few parameters: " << optarg; + return -1; + } + + if (tokens.size() > 4) { + // We only need protocol_id, port, host and origin + LOG(ERROR) << opt << ": too many parameters: " << optarg; + return -1; + } + + int port; + + if (parse_uint(&port, opt, tokens[1]) != 0) { + return -1; + } + + if (port < 1 || + port > static_cast(std::numeric_limits::max())) { + LOG(ERROR) << opt << ": port is invalid: " << tokens[1]; + return -1; + } + + AltSvc altsvc; + + altsvc.port = port; + + altsvc.protocol_id = tokens[0]; + altsvc.protocol_id_len = strlen(altsvc.protocol_id); + + if (tokens.size() > 2) { + altsvc.host = tokens[2]; + altsvc.host_len = strlen(altsvc.host); + + if (tokens.size() > 3) { + altsvc.origin = tokens[3]; + altsvc.origin_len = strlen(altsvc.origin); + } + } + + mod_config()->altsvcs.push_back(std::move(altsvc)); + + return 0; + } + + if (util::strieq(opt, SHRPX_OPT_ADD_RESPONSE_HEADER)) { + auto p = parse_header(optarg); + if (p.first.empty()) { + LOG(ERROR) << opt << ": header field name is empty: " << optarg; + return -1; + } + mod_config()->add_response_headers.push_back(std::move(p)); + + return 0; + } + + if (util::strieq(opt, SHRPX_OPT_WORKER_FRONTEND_CONNECTIONS)) { + return parse_uint(&mod_config()->worker_frontend_connections, opt, optarg); + } + + if (util::strieq(opt, SHRPX_OPT_NO_LOCATION_REWRITE)) { + mod_config()->no_location_rewrite = util::strieq(optarg, "yes"); + + return 0; + } + + if (util::strieq(opt, SHRPX_OPT_NO_HOST_REWRITE)) { + mod_config()->no_host_rewrite = util::strieq(optarg, "yes"); + + return 0; + } + + if (util::strieq(opt, SHRPX_OPT_BACKEND_HTTP1_CONNECTIONS_PER_HOST)) { + int n; + + if (parse_uint(&n, opt, optarg) != 0) { + return -1; + } + + if (n == 0) { + LOG(ERROR) << opt << ": specify an integer strictly more than 0"; + + return -1; + } + + mod_config()->downstream_connections_per_host = n; + + return 0; + } + + if (util::strieq(opt, SHRPX_OPT_BACKEND_HTTP1_CONNECTIONS_PER_FRONTEND)) { + return parse_uint(&mod_config()->downstream_connections_per_frontend, opt, + optarg); + } + + if (util::strieq(opt, SHRPX_OPT_LISTENER_DISABLE_TIMEOUT)) { + return parse_duration(&mod_config()->listener_disable_timeout, opt, optarg); + } + + if (util::strieq(opt, SHRPX_OPT_TLS_TICKET_KEY_FILE)) { + mod_config()->tls_ticket_key_files.push_back(optarg); + return 0; + } + + if (util::strieq(opt, SHRPX_OPT_RLIMIT_NOFILE)) { + int n; + + if (parse_uint(&n, opt, optarg) != 0) { + return -1; + } + + if (n < 0) { + LOG(ERROR) << opt << ": specify the integer more than or equal to 0"; + + return -1; + } + + mod_config()->rlimit_nofile = n; + + return 0; + } + + if (util::strieq(opt, SHRPX_OPT_BACKEND_REQUEST_BUFFER) || + util::strieq(opt, SHRPX_OPT_BACKEND_RESPONSE_BUFFER)) { + size_t n; + if (parse_uint_with_unit(&n, opt, optarg) != 0) { + return -1; + } + + if (n == 0) { + LOG(ERROR) << opt << ": specify an integer strictly more than 0"; + + return -1; + } + + if (util::strieq(opt, SHRPX_OPT_BACKEND_REQUEST_BUFFER)) { + mod_config()->downstream_request_buffer_size = n; + } else { + mod_config()->downstream_response_buffer_size = n; + } + + return 0; + } + + if (util::strieq(opt, SHRPX_OPT_NO_SERVER_PUSH)) { + mod_config()->no_server_push = util::strieq(optarg, "yes"); + + return 0; + } + + if (util::strieq(opt, SHRPX_OPT_BACKEND_HTTP2_CONNECTIONS_PER_WORKER)) { + return parse_uint(&mod_config()->http2_downstream_connections_per_worker, + opt, optarg); + } + + if (util::strieq(opt, SHRPX_OPT_FETCH_OCSP_RESPONSE_FILE)) { + mod_config()->fetch_ocsp_response_file = strcopy(optarg); + + return 0; + } + + if (util::strieq(opt, SHRPX_OPT_OCSP_UPDATE_INTERVAL)) { + return parse_duration(&mod_config()->ocsp_update_interval, opt, optarg); + } + + if (util::strieq(opt, SHRPX_OPT_NO_OCSP)) { + mod_config()->no_ocsp = util::strieq(optarg, "yes"); + + return 0; + } + + if (util::strieq(opt, SHRPX_OPT_HEADER_FIELD_BUFFER)) { + return parse_uint_with_unit(&mod_config()->header_field_buffer, opt, + optarg); + } + + if (util::strieq(opt, SHRPX_OPT_MAX_HEADER_FIELDS)) { + return parse_uint(&mod_config()->max_header_fields, opt, optarg); + } + + if (util::strieq(opt, "conf")) { + LOG(WARN) << "conf: ignored"; + + return 0; + } + + LOG(ERROR) << "Unknown option: " << opt; + + return -1; +} + +int load_config(const char *filename) { + std::ifstream in(filename, std::ios::binary); + if (!in) { + LOG(ERROR) << "Could not open config file " << filename; + return -1; + } + std::string line; + int linenum = 0; + while (std::getline(in, line)) { + ++linenum; + if (line.empty() || line[0] == '#') { + continue; + } + size_t i; + size_t size = line.size(); + for (i = 0; i < size && line[i] != '='; ++i) + ; + if (i == size) { + LOG(ERROR) << "Bad configuration format at line " << linenum; + return -1; + } + line[i] = '\0'; + auto s = line.c_str(); + if (parse_config(s, s + i + 1) == -1) { + return -1; + } + } + return 0; +} + +const char *str_syslog_facility(int facility) { + switch (facility) { + case (LOG_AUTH): + return "auth"; + case (LOG_AUTHPRIV): + return "authpriv"; + case (LOG_CRON): + return "cron"; + case (LOG_DAEMON): + return "daemon"; + case (LOG_FTP): + return "ftp"; + case (LOG_KERN): + return "kern"; + case (LOG_LOCAL0): + return "local0"; + case (LOG_LOCAL1): + return "local1"; + case (LOG_LOCAL2): + return "local2"; + case (LOG_LOCAL3): + return "local3"; + case (LOG_LOCAL4): + return "local4"; + case (LOG_LOCAL5): + return "local5"; + case (LOG_LOCAL6): + return "local6"; + case (LOG_LOCAL7): + return "local7"; + case (LOG_LPR): + return "lpr"; + case (LOG_MAIL): + return "mail"; + case (LOG_SYSLOG): + return "syslog"; + case (LOG_USER): + return "user"; + case (LOG_UUCP): + return "uucp"; + default: + return "(unknown)"; + } +} + +int int_syslog_facility(const char *strfacility) { + if (util::strieq(strfacility, "auth")) { + return LOG_AUTH; + } + + if (util::strieq(strfacility, "authpriv")) { + return LOG_AUTHPRIV; + } + + if (util::strieq(strfacility, "cron")) { + return LOG_CRON; + } + + if (util::strieq(strfacility, "daemon")) { + return LOG_DAEMON; + } + + if (util::strieq(strfacility, "ftp")) { + return LOG_FTP; + } + + if (util::strieq(strfacility, "kern")) { + return LOG_KERN; + } + + if (util::strieq(strfacility, "local0")) { + return LOG_LOCAL0; + } + + if (util::strieq(strfacility, "local1")) { + return LOG_LOCAL1; + } + + if (util::strieq(strfacility, "local2")) { + return LOG_LOCAL2; + } + + if (util::strieq(strfacility, "local3")) { + return LOG_LOCAL3; + } + + if (util::strieq(strfacility, "local4")) { + return LOG_LOCAL4; + } + + if (util::strieq(strfacility, "local5")) { + return LOG_LOCAL5; + } + + if (util::strieq(strfacility, "local6")) { + return LOG_LOCAL6; + } + + if (util::strieq(strfacility, "local7")) { + return LOG_LOCAL7; + } + + if (util::strieq(strfacility, "lpr")) { + return LOG_LPR; + } + + if (util::strieq(strfacility, "mail")) { + return LOG_MAIL; + } + + if (util::strieq(strfacility, "news")) { + return LOG_NEWS; + } + + if (util::strieq(strfacility, "syslog")) { + return LOG_SYSLOG; + } + + if (util::strieq(strfacility, "user")) { + return LOG_USER; + } + + if (util::strieq(strfacility, "uucp")) { + return LOG_UUCP; + } + + return -1; +} + +} // namespace shrpx diff --git a/src/shrpx_config.h b/src/shrpx_config.h new file mode 100644 index 0000000..8afccd5 --- /dev/null +++ b/src/shrpx_config.h @@ -0,0 +1,409 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef SHRPX_CONFIG_H +#define SHRPX_CONFIG_H + +#include "shrpx.h" + +#include +#ifdef HAVE_SYS_SOCKET_H +#include +#endif // HAVE_SYS_SOCKET_H +#include +#ifdef HAVE_NETINET_IN_H +#include +#endif // HAVE_NETINET_IN_H +#ifdef HAVE_ARPA_INET_H +#include +#endif // HAVE_ARPA_INET_H +#include +#include +#include +#include + +#include + +#include + +#include + +namespace shrpx { + +struct LogFragment; + +namespace ssl { + +class CertLookupTree; + +} // namespace ssl + +#define SHRPX_UNIX_PATH_PREFIX "unix:" + +extern const char SHRPX_OPT_PRIVATE_KEY_FILE[]; +extern const char SHRPX_OPT_PRIVATE_KEY_PASSWD_FILE[]; +extern const char SHRPX_OPT_CERTIFICATE_FILE[]; +extern const char SHRPX_OPT_DH_PARAM_FILE[]; +extern const char SHRPX_OPT_SUBCERT[]; +extern const char SHRPX_OPT_BACKEND[]; +extern const char SHRPX_OPT_FRONTEND[]; +extern const char SHRPX_OPT_WORKERS[]; +extern const char SHRPX_OPT_HTTP2_MAX_CONCURRENT_STREAMS[]; +extern const char SHRPX_OPT_LOG_LEVEL[]; +extern const char SHRPX_OPT_DAEMON[]; +extern const char SHRPX_OPT_HTTP2_PROXY[]; +extern const char SHRPX_OPT_HTTP2_BRIDGE[]; +extern const char SHRPX_OPT_CLIENT_PROXY[]; +extern const char SHRPX_OPT_ADD_X_FORWARDED_FOR[]; +extern const char SHRPX_OPT_STRIP_INCOMING_X_FORWARDED_FOR[]; +extern const char SHRPX_OPT_NO_VIA[]; +extern const char SHRPX_OPT_FRONTEND_HTTP2_READ_TIMEOUT[]; +extern const char SHRPX_OPT_FRONTEND_READ_TIMEOUT[]; +extern const char SHRPX_OPT_FRONTEND_WRITE_TIMEOUT[]; +extern const char SHRPX_OPT_BACKEND_READ_TIMEOUT[]; +extern const char SHRPX_OPT_BACKEND_WRITE_TIMEOUT[]; +extern const char SHRPX_OPT_STREAM_READ_TIMEOUT[]; +extern const char SHRPX_OPT_STREAM_WRITE_TIMEOUT[]; +extern const char SHRPX_OPT_ACCESSLOG_FILE[]; +extern const char SHRPX_OPT_ACCESSLOG_SYSLOG[]; +extern const char SHRPX_OPT_ACCESSLOG_FORMAT[]; +extern const char SHRPX_OPT_ERRORLOG_FILE[]; +extern const char SHRPX_OPT_ERRORLOG_SYSLOG[]; +extern const char SHRPX_OPT_BACKEND_KEEP_ALIVE_TIMEOUT[]; +extern const char SHRPX_OPT_FRONTEND_HTTP2_WINDOW_BITS[]; +extern const char SHRPX_OPT_BACKEND_HTTP2_WINDOW_BITS[]; +extern const char SHRPX_OPT_FRONTEND_HTTP2_CONNECTION_WINDOW_BITS[]; +extern const char SHRPX_OPT_BACKEND_HTTP2_CONNECTION_WINDOW_BITS[]; +extern const char SHRPX_OPT_FRONTEND_NO_TLS[]; +extern const char SHRPX_OPT_BACKEND_NO_TLS[]; +extern const char SHRPX_OPT_PID_FILE[]; +extern const char SHRPX_OPT_USER[]; +extern const char SHRPX_OPT_SYSLOG_FACILITY[]; +extern const char SHRPX_OPT_BACKLOG[]; +extern const char SHRPX_OPT_CIPHERS[]; +extern const char SHRPX_OPT_CLIENT[]; +extern const char SHRPX_OPT_INSECURE[]; +extern const char SHRPX_OPT_CACERT[]; +extern const char SHRPX_OPT_BACKEND_IPV4[]; +extern const char SHRPX_OPT_BACKEND_IPV6[]; +extern const char SHRPX_OPT_BACKEND_HTTP_PROXY_URI[]; +extern const char SHRPX_OPT_BACKEND_TLS_SNI_FIELD[]; +extern const char SHRPX_OPT_READ_RATE[]; +extern const char SHRPX_OPT_READ_BURST[]; +extern const char SHRPX_OPT_WRITE_RATE[]; +extern const char SHRPX_OPT_WRITE_BURST[]; +extern const char SHRPX_OPT_WORKER_READ_RATE[]; +extern const char SHRPX_OPT_WORKER_READ_BURST[]; +extern const char SHRPX_OPT_WORKER_WRITE_RATE[]; +extern const char SHRPX_OPT_WORKER_WRITE_BURST[]; +extern const char SHRPX_OPT_NPN_LIST[]; +extern const char SHRPX_OPT_TLS_PROTO_LIST[]; +extern const char SHRPX_OPT_VERIFY_CLIENT[]; +extern const char SHRPX_OPT_VERIFY_CLIENT_CACERT[]; +extern const char SHRPX_OPT_CLIENT_PRIVATE_KEY_FILE[]; +extern const char SHRPX_OPT_CLIENT_CERT_FILE[]; +extern const char SHRPX_OPT_FRONTEND_HTTP2_DUMP_REQUEST_HEADER[]; +extern const char SHRPX_OPT_FRONTEND_HTTP2_DUMP_RESPONSE_HEADER[]; +extern const char SHRPX_OPT_HTTP2_NO_COOKIE_CRUMBLING[]; +extern const char SHRPX_OPT_FRONTEND_FRAME_DEBUG[]; +extern const char SHRPX_OPT_PADDING[]; +extern const char SHRPX_OPT_ALTSVC[]; +extern const char SHRPX_OPT_ADD_RESPONSE_HEADER[]; +extern const char SHRPX_OPT_WORKER_FRONTEND_CONNECTIONS[]; +extern const char SHRPX_OPT_NO_LOCATION_REWRITE[]; +extern const char SHRPX_OPT_NO_HOST_REWRITE[]; +extern const char SHRPX_OPT_BACKEND_HTTP1_CONNECTIONS_PER_HOST[]; +extern const char SHRPX_OPT_BACKEND_HTTP1_CONNECTIONS_PER_FRONTEND[]; +extern const char SHRPX_OPT_LISTENER_DISABLE_TIMEOUT[]; +extern const char SHRPX_OPT_TLS_TICKET_KEY_FILE[]; +extern const char SHRPX_OPT_RLIMIT_NOFILE[]; +extern const char SHRPX_OPT_BACKEND_REQUEST_BUFFER[]; +extern const char SHRPX_OPT_BACKEND_RESPONSE_BUFFER[]; +extern const char SHRPX_OPT_NO_SERVER_PUSH[]; +extern const char SHRPX_OPT_BACKEND_HTTP2_CONNECTIONS_PER_WORKER[]; +extern const char SHRPX_OPT_FETCH_OCSP_RESPONSE_FILE[]; +extern const char SHRPX_OPT_OCSP_UPDATE_INTERVAL[]; +extern const char SHRPX_OPT_NO_OCSP[]; +extern const char SHRPX_OPT_HEADER_FIELD_BUFFER[]; +extern const char SHRPX_OPT_MAX_HEADER_FIELDS[]; + +union sockaddr_union { + sockaddr_storage storage; + sockaddr sa; + sockaddr_in6 in6; + sockaddr_in in; + sockaddr_un un; +}; + +enum shrpx_proto { PROTO_HTTP2, PROTO_HTTP }; + +struct AltSvc { + AltSvc() + : protocol_id(nullptr), host(nullptr), origin(nullptr), + protocol_id_len(0), host_len(0), origin_len(0), port(0) {} + + char *protocol_id; + char *host; + char *origin; + + size_t protocol_id_len; + size_t host_len; + size_t origin_len; + + uint16_t port; +}; + +struct DownstreamAddr { + DownstreamAddr() : addr{{0}}, addrlen(0), port(0), host_unix(false) {} + sockaddr_union addr; + // backend address. If |host_unix| is true, this is UNIX domain + // socket path. + std::unique_ptr host; + std::unique_ptr hostport; + size_t addrlen; + // backend port. 0 if |host_unix| is true. + uint16_t port; + // true if |host| contains UNIX domain socket path. + bool host_unix; +}; + +struct TicketKey { + uint8_t name[16]; + uint8_t aes_key[16]; + uint8_t hmac_key[16]; +}; + +struct TicketKeys { + ~TicketKeys(); + std::vector keys; +}; + +struct Config { + // The list of (private key file, certificate file) pair + std::vector> subcerts; + std::vector altsvcs; + std::vector> add_response_headers; + std::vector alpn_prefs; + std::vector accesslog_format; + std::vector downstream_addrs; + std::vector tls_ticket_key_files; + // binary form of http proxy host and port + sockaddr_union downstream_http_proxy_addr; + ev_tstamp http2_upstream_read_timeout; + ev_tstamp upstream_read_timeout; + ev_tstamp upstream_write_timeout; + ev_tstamp downstream_read_timeout; + ev_tstamp downstream_write_timeout; + ev_tstamp stream_read_timeout; + ev_tstamp stream_write_timeout; + ev_tstamp downstream_idle_read_timeout; + ev_tstamp listener_disable_timeout; + ev_tstamp ocsp_update_interval; + // address of frontend connection. This could be a path to UNIX + // domain socket. In this case, |host_unix| must be true. + std::unique_ptr host; + std::unique_ptr private_key_file; + std::unique_ptr private_key_passwd; + std::unique_ptr cert_file; + std::unique_ptr dh_param_file; + const char *server_name; + std::unique_ptr backend_tls_sni_name; + std::unique_ptr pid_file; + std::unique_ptr conf_path; + std::unique_ptr ciphers; + std::unique_ptr cacert; + // userinfo in http proxy URI, not percent-encoded form + std::unique_ptr downstream_http_proxy_userinfo; + // host in http proxy URI + std::unique_ptr downstream_http_proxy_host; + std::unique_ptr http2_upstream_dump_request_header_file; + std::unique_ptr http2_upstream_dump_response_header_file; + // // Rate limit configuration per connection + // ev_token_bucket_cfg *rate_limit_cfg; + // // Rate limit configuration per worker (thread) + // ev_token_bucket_cfg *worker_rate_limit_cfg; + // list of supported NPN/ALPN protocol strings in the order of + // preference. The each element of this list is a NULL-terminated + // string. + std::vector npn_list; + // list of supported SSL/TLS protocol strings. The each element of + // this list is a NULL-terminated string. + std::vector tls_proto_list; + // Path to file containing CA certificate solely used for client + // certificate validation + std::unique_ptr verify_client_cacert; + std::unique_ptr client_private_key_file; + std::unique_ptr client_cert_file; + std::unique_ptr accesslog_file; + std::unique_ptr errorlog_file; + std::unique_ptr fetch_ocsp_response_file; + FILE *http2_upstream_dump_request_header; + FILE *http2_upstream_dump_response_header; + nghttp2_session_callbacks *http2_upstream_callbacks; + nghttp2_session_callbacks *http2_downstream_callbacks; + nghttp2_option *http2_option; + nghttp2_option *http2_client_option; + char **argv; + char *cwd; + size_t num_worker; + size_t http2_max_concurrent_streams; + size_t http2_upstream_window_bits; + size_t http2_downstream_window_bits; + size_t http2_upstream_connection_window_bits; + size_t http2_downstream_connection_window_bits; + size_t http2_downstream_connections_per_worker; + size_t downstream_connections_per_host; + size_t downstream_connections_per_frontend; + // actual size of downstream_http_proxy_addr + size_t downstream_http_proxy_addrlen; + size_t read_rate; + size_t read_burst; + size_t write_rate; + size_t write_burst; + size_t worker_read_rate; + size_t worker_read_burst; + size_t worker_write_rate; + size_t worker_write_burst; + size_t padding; + size_t worker_frontend_connections; + size_t rlimit_nofile; + size_t downstream_request_buffer_size; + size_t downstream_response_buffer_size; + size_t header_field_buffer; + size_t max_header_fields; + // Bit mask to disable SSL/TLS protocol versions. This will be + // passed to SSL_CTX_set_options(). + long int tls_proto_mask; + // downstream protocol; this will be determined by given options. + shrpx_proto downstream_proto; + int syslog_facility; + int backlog; + int argc; + std::unique_ptr user; + uid_t uid; + gid_t gid; + pid_t pid; + // frontend listening port. 0 if frontend listens on UNIX domain + // socket, in this case |host_unix| must be true. + uint16_t port; + // port in http proxy URI + uint16_t downstream_http_proxy_port; + bool verbose; + bool daemon; + bool verify_client; + bool http2_proxy; + bool http2_bridge; + bool client_proxy; + bool add_x_forwarded_for; + bool strip_incoming_x_forwarded_for; + bool no_via; + bool upstream_no_tls; + bool downstream_no_tls; + // Send accesslog to syslog, ignoring accesslog_file. + bool accesslog_syslog; + // Send errorlog to syslog, ignoring errorlog_file. + bool errorlog_syslog; + bool client; + // true if --client or --client-proxy are enabled. + bool client_mode; + bool insecure; + bool backend_ipv4; + bool backend_ipv6; + bool http2_no_cookie_crumbling; + bool upstream_frame_debug; + bool no_location_rewrite; + bool no_host_rewrite; + bool no_server_push; + // true if host contains UNIX domain socket path + bool host_unix; + bool no_ocsp; +}; + +const Config *get_config(); +Config *mod_config(); +void create_config(); + +// Parses option name |opt| and value |optarg|. The results are +// stored into statically allocated Config object. This function +// returns 0 if it succeeds, or -1. +int parse_config(const char *opt, const char *optarg); + +// Loads configurations from |filename| and stores them in statically +// allocated Config object. This function returns 0 if it succeeds, or +// -1. +int load_config(const char *filename); + +// Read passwd from |filename| +std::string read_passwd_from_file(const char *filename); + +// Parses comma delimited strings in |s| and returns the array of +// pointers, each element points to the each substring in |s|. The +// |s| must be comma delimited list of strings. The strings must be +// delimited by a single comma and any white spaces around it are +// treated as a part of protocol strings. This function may modify +// |s| and the caller must leave it as is after this call. This +// function copies |s| and first element in the return value points to +// it. It is caller's responsibility to deallocate its memory. +std::vector parse_config_str_list(const char *s); + +// Clears all elements of |list|, which is returned by +// parse_config_str_list(). If list is not empty, list[0] is freed by +// free(2). After this call, list.empty() must be true. +void clear_config_str_list(std::vector &list); + +// Parses header field in |optarg|. We expect header field is formed +// like "NAME: VALUE". We require that NAME is non empty string. ":" +// is allowed at the start of the NAME, but NAME == ":" is not +// allowed. This function returns pair of NAME and VALUE. +std::pair parse_header(const char *optarg); + +std::vector parse_log_format(const char *optarg); + +// Returns a copy of NULL-terminated string |val|. +std::unique_ptr strcopy(const char *val); + +// Returns a copy of string |val| of length |n|. The returned string +// will be NULL-terminated. +std::unique_ptr strcopy(const char *val, size_t n); + +// Returns a copy of val.c_str(). +std::unique_ptr strcopy(const std::string &val); + +// Returns string for syslog |facility|. +const char *str_syslog_facility(int facility); + +// Returns integer value of syslog |facility| string. +int int_syslog_facility(const char *strfacility); + +FILE *open_file_for_write(const char *filename); + +// Reads TLS ticket key file in |files| and returns TicketKey which +// stores read key data. This function returns TicketKey if it +// succeeds, or nullptr. +std::unique_ptr +read_tls_ticket_key_file(const std::vector &files); + +} // namespace shrpx + +#endif // SHRPX_CONFIG_H diff --git a/src/shrpx_config_test.cc b/src/shrpx_config_test.cc new file mode 100644 index 0000000..ad570bb --- /dev/null +++ b/src/shrpx_config_test.cc @@ -0,0 +1,175 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2014 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "shrpx_config_test.h" + +#ifdef HAVE_UNISTD_H +#include +#endif // HAVE_UNISTD_H + +#include + +#include + +#include "shrpx_config.h" + +namespace shrpx { + +void test_shrpx_config_parse_config_str_list(void) { + auto res = parse_config_str_list("a"); + CU_ASSERT(1 == res.size()); + CU_ASSERT(0 == strcmp("a", res[0])); + clear_config_str_list(res); + + res = parse_config_str_list("a,"); + CU_ASSERT(2 == res.size()); + CU_ASSERT(0 == strcmp("a", res[0])); + CU_ASSERT(0 == strcmp("", res[1])); + clear_config_str_list(res); + + res = parse_config_str_list(",a,,"); + CU_ASSERT(4 == res.size()); + CU_ASSERT(0 == strcmp("", res[0])); + CU_ASSERT(0 == strcmp("a", res[1])); + CU_ASSERT(0 == strcmp("", res[2])); + CU_ASSERT(0 == strcmp("", res[3])); + clear_config_str_list(res); + + res = parse_config_str_list(""); + CU_ASSERT(1 == res.size()); + CU_ASSERT(0 == strcmp("", res[0])); + clear_config_str_list(res); + + res = parse_config_str_list("alpha,bravo,charlie"); + CU_ASSERT(3 == res.size()); + CU_ASSERT(0 == strcmp("alpha", res[0])); + CU_ASSERT(0 == strcmp("bravo", res[1])); + CU_ASSERT(0 == strcmp("charlie", res[2])); + clear_config_str_list(res); +} + +void test_shrpx_config_parse_header(void) { + auto p = parse_header("a: b"); + CU_ASSERT("a" == p.first); + CU_ASSERT("b" == p.second); + + p = parse_header("a: b"); + CU_ASSERT("a" == p.first); + CU_ASSERT("b" == p.second); + + p = parse_header(":a: b"); + CU_ASSERT(":a" == p.first); + CU_ASSERT("b" == p.second); + + p = parse_header("a: :b"); + CU_ASSERT("a" == p.first); + CU_ASSERT(":b" == p.second); + + p = parse_header(": b"); + CU_ASSERT(p.first.empty()); + + p = parse_header("alpha: bravo charlie"); + CU_ASSERT("alpha" == p.first); + CU_ASSERT("bravo charlie" == p.second); +} + +void test_shrpx_config_parse_log_format(void) { + auto res = parse_log_format("$remote_addr - $remote_user [$time_local] " + "\"$request\" $status $body_bytes_sent " + "\"$http_referer\" \"$http_user_agent\""); + CU_ASSERT(14 == res.size()); + + CU_ASSERT(SHRPX_LOGF_REMOTE_ADDR == res[0].type); + + CU_ASSERT(SHRPX_LOGF_LITERAL == res[1].type); + CU_ASSERT(0 == strcmp(" - $remote_user [", res[1].value.get())); + + CU_ASSERT(SHRPX_LOGF_TIME_LOCAL == res[2].type); + + CU_ASSERT(SHRPX_LOGF_LITERAL == res[3].type); + CU_ASSERT(0 == strcmp("] \"", res[3].value.get())); + + CU_ASSERT(SHRPX_LOGF_REQUEST == res[4].type); + + CU_ASSERT(SHRPX_LOGF_LITERAL == res[5].type); + CU_ASSERT(0 == strcmp("\" ", res[5].value.get())); + + CU_ASSERT(SHRPX_LOGF_STATUS == res[6].type); + + CU_ASSERT(SHRPX_LOGF_LITERAL == res[7].type); + CU_ASSERT(0 == strcmp(" ", res[7].value.get())); + + CU_ASSERT(SHRPX_LOGF_BODY_BYTES_SENT == res[8].type); + + CU_ASSERT(SHRPX_LOGF_LITERAL == res[9].type); + CU_ASSERT(0 == strcmp(" \"", res[9].value.get())); + + CU_ASSERT(SHRPX_LOGF_HTTP == res[10].type); + CU_ASSERT(0 == strcmp("referer", res[10].value.get())); + + CU_ASSERT(SHRPX_LOGF_LITERAL == res[11].type); + CU_ASSERT(0 == strcmp("\" \"", res[11].value.get())); + + CU_ASSERT(SHRPX_LOGF_HTTP == res[12].type); + CU_ASSERT(0 == strcmp("user-agent", res[12].value.get())); + + CU_ASSERT(SHRPX_LOGF_LITERAL == res[13].type); + CU_ASSERT(0 == strcmp("\"", res[13].value.get())); +} + +void test_shrpx_config_read_tls_ticket_key_file(void) { + char file1[] = "/tmp/nghttpx-unittest.XXXXXX"; + auto fd1 = mkstemp(file1); + assert(fd1 != -1); + assert(48 == + write(fd1, "0..............12..............34..............5", 48)); + char file2[] = "/tmp/nghttpx-unittest.XXXXXX"; + auto fd2 = mkstemp(file2); + assert(fd2 != -1); + assert(48 == + write(fd2, "6..............78..............9a..............b", 48)); + + close(fd1); + close(fd2); + auto ticket_keys = read_tls_ticket_key_file({file1, file2}); + unlink(file1); + unlink(file2); + CU_ASSERT(ticket_keys.get() != nullptr); + CU_ASSERT(2 == ticket_keys->keys.size()); + auto key = &ticket_keys->keys[0]; + CU_ASSERT(0 == memcmp("0..............1", key->name, sizeof(key->name))); + CU_ASSERT(0 == + memcmp("2..............3", key->aes_key, sizeof(key->aes_key))); + CU_ASSERT(0 == + memcmp("4..............5", key->hmac_key, sizeof(key->hmac_key))); + + key = &ticket_keys->keys[1]; + CU_ASSERT(0 == memcmp("6..............7", key->name, sizeof(key->name))); + CU_ASSERT(0 == + memcmp("8..............9", key->aes_key, sizeof(key->aes_key))); + CU_ASSERT(0 == + memcmp("a..............b", key->hmac_key, sizeof(key->hmac_key))); +} + +} // namespace shrpx diff --git a/src/shrpx_config_test.h b/src/shrpx_config_test.h new file mode 100644 index 0000000..6d6d035 --- /dev/null +++ b/src/shrpx_config_test.h @@ -0,0 +1,41 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2014 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef SHRPX_CONFIG_TEST_H +#define SHRPX_CONFIG_TEST_H + +#ifdef HAVE_CONFIG_H +#include +#endif // HAVE_CONFIG_H + +namespace shrpx { + +void test_shrpx_config_parse_config_str_list(void); +void test_shrpx_config_parse_header(void); +void test_shrpx_config_parse_log_format(void); +void test_shrpx_config_read_tls_ticket_key_file(void); + +} // namespace shrpx + +#endif // SHRPX_CONFIG_TEST_H diff --git a/src/shrpx_connect_blocker.cc b/src/shrpx_connect_blocker.cc new file mode 100644 index 0000000..3eb6de1 --- /dev/null +++ b/src/shrpx_connect_blocker.cc @@ -0,0 +1,65 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2014 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "shrpx_connect_blocker.h" + +namespace shrpx { + +namespace { +const ev_tstamp INITIAL_SLEEP = 2.; +} // namespace + +namespace { +void connect_blocker_cb(struct ev_loop *loop, ev_timer *w, int revents) { + if (LOG_ENABLED(INFO)) { + LOG(INFO) << "unblock downstream connection"; + } +} +} // namespace + +ConnectBlocker::ConnectBlocker(struct ev_loop *loop) + : loop_(loop), sleep_(INITIAL_SLEEP) { + ev_timer_init(&timer_, connect_blocker_cb, 0., 0.); +} + +ConnectBlocker::~ConnectBlocker() { ev_timer_stop(loop_, &timer_); } + +bool ConnectBlocker::blocked() const { return ev_is_active(&timer_); } + +void ConnectBlocker::on_success() { sleep_ = INITIAL_SLEEP; } + +void ConnectBlocker::on_failure() { + if (ev_is_active(&timer_)) { + return; + } + + sleep_ = std::min(128., sleep_ * 2); + + LOG(WARN) << "connect failure, start sleeping " << sleep_; + + ev_timer_set(&timer_, sleep_, 0.); + ev_timer_start(loop_, &timer_); +} + +} // namespace shrpx diff --git a/src/shrpx_connect_blocker.h b/src/shrpx_connect_blocker.h new file mode 100644 index 0000000..af44564 --- /dev/null +++ b/src/shrpx_connect_blocker.h @@ -0,0 +1,56 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2014 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef SHRPX_CONNECT_BLOCKER_H +#define SHRPX_CONNECT_BLOCKER_H + +#include "shrpx.h" + +#include + +namespace shrpx { + +class ConnectBlocker { +public: + ConnectBlocker(struct ev_loop *loop); + ~ConnectBlocker(); + + // Returns true if making connection is not allowed. + bool blocked() const; + // Call this function if connect operation succeeded. This will + // reset sleep_ to minimum value. + void on_success(); + // Call this function if connect operation failed. This will start + // timer and blocks connection establishment for sleep_ seconds. + void on_failure(); + +private: + ev_timer timer_; + struct ev_loop *loop_; + ev_tstamp sleep_; +}; + +} // namespace + +#endif // SHRPX_CONNECT_BLOCKER_H diff --git a/src/shrpx_connection.cc b/src/shrpx_connection.cc new file mode 100644 index 0000000..30c2ec9 --- /dev/null +++ b/src/shrpx_connection.cc @@ -0,0 +1,333 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2015 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "shrpx_connection.h" + +#ifdef HAVE_UNISTD_H +#include +#endif // HAVE_UNISTD_H + +#include + +#include + +#include "memchunk.h" + +using namespace nghttp2; + +namespace shrpx { +Connection::Connection(struct ev_loop *loop, int fd, SSL *ssl, + ev_tstamp write_timeout, ev_tstamp read_timeout, + size_t write_rate, size_t write_burst, size_t read_rate, + size_t read_burst, IOCb writecb, IOCb readcb, + TimerCb timeoutcb, void *data) + : tls{ssl}, wlimit(loop, &wev, write_rate, write_burst), + rlimit(loop, &rev, read_rate, read_burst, ssl), writecb(writecb), + readcb(readcb), timeoutcb(timeoutcb), loop(loop), data(data), fd(fd) { + + ev_io_init(&wev, writecb, fd, EV_WRITE); + ev_io_init(&rev, readcb, fd, EV_READ); + + wev.data = this; + rev.data = this; + + ev_timer_init(&wt, timeoutcb, 0., write_timeout); + ev_timer_init(&rt, timeoutcb, 0., read_timeout); + + wt.data = this; + rt.data = this; + + // set 0. to double field explicitly just in case + tls.last_write_time = 0.; +} + +Connection::~Connection() { disconnect(); } + +void Connection::disconnect() { + ev_timer_stop(loop, &rt); + ev_timer_stop(loop, &wt); + + rlimit.stopw(); + wlimit.stopw(); + + if (tls.ssl) { + SSL_set_app_data(tls.ssl, nullptr); + SSL_set_shutdown(tls.ssl, SSL_RECEIVED_SHUTDOWN); + ERR_clear_error(); + SSL_shutdown(tls.ssl); + SSL_free(tls.ssl); + tls.ssl = nullptr; + } + + if (fd != -1) { + shutdown(fd, SHUT_WR); + close(fd); + fd = -1; + } +} + +int Connection::tls_handshake() { + auto rv = SSL_do_handshake(tls.ssl); + + if (rv == 0) { + return SHRPX_ERR_NETWORK; + } + + if (rv < 0) { + auto err = SSL_get_error(tls.ssl, rv); + switch (err) { + case SSL_ERROR_WANT_READ: + wlimit.stopw(); + ev_timer_stop(loop, &wt); + return SHRPX_ERR_INPROGRESS; + case SSL_ERROR_WANT_WRITE: + wlimit.startw(); + ev_timer_again(loop, &wt); + return SHRPX_ERR_INPROGRESS; + default: + return SHRPX_ERR_NETWORK; + } + } + + wlimit.stopw(); + ev_timer_stop(loop, &wt); + + tls.initial_handshake_done = true; + + if (LOG_ENABLED(INFO)) { + LOG(INFO) << "SSL/TLS handshake completed"; + if (SSL_session_reused(tls.ssl)) { + LOG(INFO) << "SSL/TLS session reused"; + } + } + + return 0; +} + +namespace { +const size_t SHRPX_SMALL_WRITE_LIMIT = 1300; +const size_t SHRPX_WARMUP_THRESHOLD = 1 << 20; +} // namespace + +size_t Connection::get_tls_write_limit() { + auto t = ev_now(loop); + + if (t - tls.last_write_time > 1.) { + // Time out, use small record size + tls.warmup_writelen = 0; + return SHRPX_SMALL_WRITE_LIMIT; + } + + if (tls.warmup_writelen >= SHRPX_WARMUP_THRESHOLD) { + return std::numeric_limits::max(); + } + + return SHRPX_SMALL_WRITE_LIMIT; +} + +void Connection::update_tls_warmup_writelen(size_t n) { + if (tls.warmup_writelen < SHRPX_WARMUP_THRESHOLD) { + tls.warmup_writelen += n; + } +} + +ssize_t Connection::write_tls(const void *data, size_t len) { + // SSL_write requires the same arguments (buf pointer and its + // length) on SSL_ERROR_WANT_READ or SSL_ERROR_WANT_WRITE. + // get_write_limit() may return smaller length than previously + // passed to SSL_write, which violates OpenSSL assumption. To avoid + // this, we keep last legnth passed to SSL_write to + // tls.last_writelen if SSL_write indicated I/O blocking. + if (tls.last_writelen == 0) { + len = std::min(len, wlimit.avail()); + len = std::min(len, get_tls_write_limit()); + if (len == 0) { + return 0; + } + } else { + len = tls.last_writelen; + tls.last_writelen = 0; + } + + auto rv = SSL_write(tls.ssl, data, len); + + if (rv == 0) { + return SHRPX_ERR_NETWORK; + } + + tls.last_write_time = ev_now(loop); + + if (rv < 0) { + auto err = SSL_get_error(tls.ssl, rv); + switch (err) { + case SSL_ERROR_WANT_READ: + if (LOG_ENABLED(INFO)) { + LOG(INFO) << "Close connection due to TLS renegotiation"; + } + return SHRPX_ERR_NETWORK; + case SSL_ERROR_WANT_WRITE: + tls.last_writelen = len; + wlimit.startw(); + ev_timer_again(loop, &wt); + return 0; + default: + if (LOG_ENABLED(INFO)) { + LOG(INFO) << "SSL_write: SSL_get_error returned " << err; + } + return SHRPX_ERR_NETWORK; + } + } + + wlimit.drain(rv); + + update_tls_warmup_writelen(rv); + + return rv; +} + +ssize_t Connection::read_tls(void *data, size_t len) { + // SSL_read requires the same arguments (buf pointer and its + // length) on SSL_ERROR_WANT_READ or SSL_ERROR_WANT_WRITE. + // rlimit_.avail() or rlimit_.avail() may return different length + // than the length previously passed to SSL_read, which violates + // OpenSSL assumption. To avoid this, we keep last legnth passed + // to SSL_read to tls_last_readlen_ if SSL_read indicated I/O + // blocking. + if (tls.last_readlen == 0) { + len = std::min(len, rlimit.avail()); + if (len == 0) { + return 0; + } + } else { + len = tls.last_readlen; + tls.last_readlen = 0; + } + + auto rv = SSL_read(tls.ssl, data, len); + + if (rv <= 0) { + auto err = SSL_get_error(tls.ssl, rv); + switch (err) { + case SSL_ERROR_WANT_READ: + tls.last_readlen = len; + return 0; + case SSL_ERROR_WANT_WRITE: + if (LOG_ENABLED(INFO)) { + LOG(INFO) << "Close connection due to TLS renegotiation"; + } + return SHRPX_ERR_NETWORK; + case SSL_ERROR_ZERO_RETURN: + return SHRPX_ERR_EOF; + default: + if (LOG_ENABLED(INFO)) { + LOG(INFO) << "SSL_read: SSL_get_error returned " << err; + } + return SHRPX_ERR_NETWORK; + } + } + + rlimit.drain(rv); + + return rv; +} + +ssize_t Connection::write_clear(const void *data, size_t len) { + len = std::min(len, wlimit.avail()); + if (len == 0) { + return 0; + } + + ssize_t nwrite; + while ((nwrite = write(fd, data, len)) == -1 && errno == EINTR) + ; + if (nwrite == -1) { + if (errno == EAGAIN || errno == EWOULDBLOCK) { + wlimit.startw(); + ev_timer_again(loop, &wt); + return 0; + } + return SHRPX_ERR_NETWORK; + } + + wlimit.drain(nwrite); + + return nwrite; +} + +ssize_t Connection::writev_clear(struct iovec *iov, int iovcnt) { + iovcnt = limit_iovec(iov, iovcnt, wlimit.avail()); + if (iovcnt == 0) { + return 0; + } + + ssize_t nwrite; + while ((nwrite = writev(fd, iov, iovcnt)) == -1 && errno == EINTR) + ; + if (nwrite == -1) { + if (errno == EAGAIN || errno == EWOULDBLOCK) { + wlimit.startw(); + ev_timer_again(loop, &wt); + return 0; + } + return SHRPX_ERR_NETWORK; + } + + wlimit.drain(nwrite); + + return nwrite; +} + +ssize_t Connection::read_clear(void *data, size_t len) { + len = std::min(len, rlimit.avail()); + if (len == 0) { + return 0; + } + + ssize_t nread; + while ((nread = read(fd, data, len)) == -1 && errno == EINTR) + ; + if (nread == -1) { + if (errno == EAGAIN || errno == EWOULDBLOCK) { + return 0; + } + return SHRPX_ERR_NETWORK; + } + + if (nread == 0) { + return SHRPX_ERR_EOF; + } + + rlimit.drain(nread); + + return nread; +} + +void Connection::handle_tls_pending_read() { + if (!ev_is_active(&rev)) { + return; + } + rlimit.handle_tls_pending_read(); +} + +} // namespace shrpx diff --git a/src/shrpx_connection.h b/src/shrpx_connection.h new file mode 100644 index 0000000..055d32c --- /dev/null +++ b/src/shrpx_connection.h @@ -0,0 +1,109 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2015 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef SHRPX_CONNECTION_H +#define SHRPX_CONNECTION_H + +#include "shrpx_config.h" + +#include + +#include + +#include + +#include "shrpx_rate_limit.h" +#include "shrpx_error.h" + +namespace shrpx { + +struct TLSConnection { + SSL *ssl; + ev_tstamp last_write_time; + size_t warmup_writelen; + // length passed to SSL_write and SSL_read last time. This is + // required since these functions require the exact same parameters + // on non-blocking I/O. + size_t last_writelen, last_readlen; + bool initial_handshake_done; + bool reneg_started; +}; + +template using EVCb = void (*)(struct ev_loop *, T *, int); + +using IOCb = EVCb; +using TimerCb = EVCb; + +struct Connection { + Connection(struct ev_loop *loop, int fd, SSL *ssl, ev_tstamp write_timeout, + ev_tstamp read_timeout, size_t write_rate, size_t write_burst, + size_t read_rate, size_t read_burst, IOCb writecb, IOCb readcb, + TimerCb timeoutcb, void *data); + ~Connection(); + + void disconnect(); + + int tls_handshake(); + + // All write_* and writev_clear functions return number of bytes + // written. If nothing cannot be written (e.g., there is no + // allowance in RateLimit or underlying connection blocks), return + // 0. SHRPX_ERR_NETWORK is returned in case of error. + // + // All read_* functions return number of bytes read. If nothing + // cannot be read (e.g., there is no allowance in Ratelimit or + // underlying connection blocks), return 0. SHRPX_ERR_EOF is + // returned in case of EOF and no data was read. Otherwise + // SHRPX_ERR_NETWORK is return in case of error. + ssize_t write_tls(const void *data, size_t len); + ssize_t read_tls(void *data, size_t len); + + size_t get_tls_write_limit(); + // Updates the number of bytes written in warm up period. + void update_tls_warmup_writelen(size_t n); + + ssize_t write_clear(const void *data, size_t len); + ssize_t writev_clear(struct iovec *iov, int iovcnt); + ssize_t read_clear(void *data, size_t len); + + void handle_tls_pending_read(); + + TLSConnection tls; + ev_io wev; + ev_io rev; + ev_timer wt; + ev_timer rt; + RateLimit wlimit; + RateLimit rlimit; + IOCb writecb; + IOCb readcb; + TimerCb timeoutcb; + struct ev_loop *loop; + void *data; + int fd; +}; + +} // namespace shrpx + +#endif // SHRPX_CONNECTION_H diff --git a/src/shrpx_connection_handler.cc b/src/shrpx_connection_handler.cc new file mode 100644 index 0000000..ceebf61 --- /dev/null +++ b/src/shrpx_connection_handler.cc @@ -0,0 +1,539 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "shrpx_connection_handler.h" + +#ifdef HAVE_UNISTD_H +#include +#endif // HAVE_UNISTD_H +#include +#include + +#include +#include + +#include "shrpx_client_handler.h" +#include "shrpx_ssl.h" +#include "shrpx_worker.h" +#include "shrpx_config.h" +#include "shrpx_http2_session.h" +#include "shrpx_connect_blocker.h" +#include "shrpx_downstream_connection.h" +#include "shrpx_accept_handler.h" +#include "util.h" +#include "template.h" + +using namespace nghttp2; + +namespace shrpx { + +namespace { +void acceptor_disable_cb(struct ev_loop *loop, ev_timer *w, int revent) { + auto h = static_cast(w->data); + + // If we are in graceful shutdown period, we must not enable + // acceptors again. + if (h->get_graceful_shutdown()) { + return; + } + + h->enable_acceptor(); +} +} // namespace + +namespace { +void ocsp_cb(struct ev_loop *loop, ev_timer *w, int revent) { + auto h = static_cast(w->data); + + // If we are in graceful shutdown period, we won't do ocsp query. + if (h->get_graceful_shutdown()) { + return; + } + + LOG(NOTICE) << "Start ocsp update"; + + h->proceed_next_cert_ocsp(); +} +} // namespace + +namespace { +void ocsp_read_cb(struct ev_loop *loop, ev_io *w, int revent) { + auto h = static_cast(w->data); + + h->read_ocsp_chunk(); +} +} // namespace + +namespace { +void ocsp_chld_cb(struct ev_loop *loop, ev_child *w, int revent) { + auto h = static_cast(w->data); + + h->handle_ocsp_complete(); +} +} // namespace + +ConnectionHandler::ConnectionHandler(struct ev_loop *loop) + : single_worker_(nullptr), loop_(loop), worker_round_robin_cnt_(0), + graceful_shutdown_(false) { + ev_timer_init(&disable_acceptor_timer_, acceptor_disable_cb, 0., 0.); + disable_acceptor_timer_.data = this; + + ev_timer_init(&ocsp_timer_, ocsp_cb, 0., 0.); + ocsp_timer_.data = this; + + ev_io_init(&ocsp_.rev, ocsp_read_cb, -1, EV_READ); + ocsp_.rev.data = this; + + ev_child_init(&ocsp_.chldev, ocsp_chld_cb, 0, 0); + ocsp_.chldev.data = this; + + ocsp_.next = 0; + ocsp_.fd = -1; + + reset_ocsp(); +} + +ConnectionHandler::~ConnectionHandler() { + ev_timer_stop(loop_, &disable_acceptor_timer_); + ev_timer_stop(loop_, &ocsp_timer_); +} + +void ConnectionHandler::worker_reopen_log_files() { + WorkerEvent wev; + + memset(&wev, 0, sizeof(wev)); + wev.type = REOPEN_LOG; + + for (auto &worker : workers_) { + worker->send(wev); + } +} + +void ConnectionHandler::worker_renew_ticket_keys( + const std::shared_ptr &ticket_keys) { + WorkerEvent wev; + + memset(&wev, 0, sizeof(wev)); + wev.type = RENEW_TICKET_KEYS; + wev.ticket_keys = ticket_keys; + + for (auto &worker : workers_) { + worker->send(wev); + } +} + +void ConnectionHandler::create_single_worker() { + auto cert_tree = ssl::create_cert_lookup_tree(); + auto sv_ssl_ctx = ssl::setup_server_ssl_context(all_ssl_ctx_, cert_tree); + auto cl_ssl_ctx = ssl::setup_client_ssl_context(); + + single_worker_ = make_unique(loop_, sv_ssl_ctx, cl_ssl_ctx, cert_tree, + ticket_keys_); +} + +void ConnectionHandler::create_worker_thread(size_t num) { +#ifndef NOTHREADS + assert(workers_.size() == 0); + + auto cert_tree = ssl::create_cert_lookup_tree(); + auto sv_ssl_ctx = ssl::setup_server_ssl_context(all_ssl_ctx_, cert_tree); + auto cl_ssl_ctx = ssl::setup_client_ssl_context(); + + for (size_t i = 0; i < num; ++i) { + auto loop = ev_loop_new(0); + + auto worker = make_unique(loop, sv_ssl_ctx, cl_ssl_ctx, cert_tree, + ticket_keys_); + worker->run_async(); + workers_.push_back(std::move(worker)); + + if (LOG_ENABLED(INFO)) { + LLOG(INFO, this) << "Created thread #" << workers_.size() - 1; + } + } +#endif // NOTHREADS +} + +void ConnectionHandler::join_worker() { +#ifndef NOTHREADS + int n = 0; + + if (LOG_ENABLED(INFO)) { + LLOG(INFO, this) << "Waiting for worker thread to join: n=" + << workers_.size(); + } + + for (auto &worker : workers_) { + worker->wait(); + if (LOG_ENABLED(INFO)) { + LLOG(INFO, this) << "Thread #" << n << " joined"; + } + ++n; + } +#endif // NOTHREADS +} + +void ConnectionHandler::graceful_shutdown_worker() { + if (get_config()->num_worker == 1) { + return; + } + + WorkerEvent wev; + memset(&wev, 0, sizeof(wev)); + wev.type = GRACEFUL_SHUTDOWN; + + if (LOG_ENABLED(INFO)) { + LLOG(INFO, this) << "Sending graceful shutdown signal to worker"; + } + + for (auto &worker : workers_) { + + worker->send(wev); + } +} + +int ConnectionHandler::handle_connection(int fd, sockaddr *addr, int addrlen) { + if (LOG_ENABLED(INFO)) { + LLOG(INFO, this) << "Accepted connection. fd=" << fd; + } + + if (get_config()->num_worker == 1) { + + if (single_worker_->get_worker_stat()->num_connections >= + get_config()->worker_frontend_connections) { + + if (LOG_ENABLED(INFO)) { + LLOG(INFO, this) << "Too many connections >=" + << get_config()->worker_frontend_connections; + } + + close(fd); + return -1; + } + + auto client = + ssl::accept_connection(single_worker_.get(), fd, addr, addrlen); + if (!client) { + LLOG(ERROR, this) << "ClientHandler creation failed"; + + close(fd); + return -1; + } + + return 0; + } + + size_t idx = worker_round_robin_cnt_ % workers_.size(); + if (LOG_ENABLED(INFO)) { + LOG(INFO) << "Dispatch connection to worker #" << idx; + } + ++worker_round_robin_cnt_; + WorkerEvent wev; + memset(&wev, 0, sizeof(wev)); + wev.type = NEW_CONNECTION; + wev.client_fd = fd; + memcpy(&wev.client_addr, addr, addrlen); + wev.client_addrlen = addrlen; + + workers_[idx]->send(wev); + + return 0; +} + +struct ev_loop *ConnectionHandler::get_loop() const { + return loop_; +} + +Worker *ConnectionHandler::get_single_worker() const { + return single_worker_.get(); +} + +void ConnectionHandler::set_acceptor(std::unique_ptr h) { + acceptor_ = std::move(h); +} + +AcceptHandler *ConnectionHandler::get_acceptor() const { + return acceptor_.get(); +} + +void ConnectionHandler::set_acceptor6(std::unique_ptr h) { + acceptor6_ = std::move(h); +} + +AcceptHandler *ConnectionHandler::get_acceptor6() const { + return acceptor6_.get(); +} + +void ConnectionHandler::enable_acceptor() { + if (acceptor_) { + acceptor_->enable(); + } + + if (acceptor6_) { + acceptor6_->enable(); + } +} + +void ConnectionHandler::disable_acceptor() { + if (acceptor_) { + acceptor_->disable(); + } + + if (acceptor6_) { + acceptor6_->disable(); + } +} + +void ConnectionHandler::disable_acceptor_temporary(ev_tstamp t) { + if (t == 0. || ev_is_active(&disable_acceptor_timer_)) { + return; + } + + disable_acceptor(); + + ev_timer_set(&disable_acceptor_timer_, t, 0.); + ev_timer_start(loop_, &disable_acceptor_timer_); +} + +void ConnectionHandler::accept_pending_connection() { + if (acceptor_) { + acceptor_->accept_connection(); + } + if (acceptor6_) { + acceptor6_->accept_connection(); + } +} + +void +ConnectionHandler::set_ticket_keys(std::shared_ptr ticket_keys) { + ticket_keys_ = std::move(ticket_keys); + if (single_worker_) { + single_worker_->set_ticket_keys(ticket_keys_); + } +} + +const std::shared_ptr &ConnectionHandler::get_ticket_keys() const { + return ticket_keys_; +} + +void ConnectionHandler::set_graceful_shutdown(bool f) { + graceful_shutdown_ = f; + if (single_worker_) { + single_worker_->set_graceful_shutdown(f); + } +} + +bool ConnectionHandler::get_graceful_shutdown() const { + return graceful_shutdown_; +} + +void ConnectionHandler::cancel_ocsp_update() { + if (ocsp_.pid == 0) { + return; + } + + kill(ocsp_.pid, SIGTERM); +} + +// inspired by h2o_read_command function from h2o project: +// https://github.com/h2o/h2o +int ConnectionHandler::start_ocsp_update(const char *cert_file) { + int rv; + int pfd[2]; + + if (LOG_ENABLED(INFO)) { + LOG(INFO) << "Start ocsp update for " << cert_file; + } + + assert(!ev_is_active(&ocsp_.rev)); + assert(!ev_is_active(&ocsp_.chldev)); + + char *const argv[] = { + const_cast(get_config()->fetch_ocsp_response_file.get()), + const_cast(cert_file), nullptr}; + char *const envp[] = {nullptr}; + +#ifdef O_CLOEXEC + if (pipe2(pfd, O_CLOEXEC) == -1) { + return -1; + } +#else // !O_CLOEXEC + if (pipe(pfd) == -1) { + return -1; + } + util::make_socket_closeonexec(pfd[0]); + util::make_socket_closeonexec(pfd[1]); +#endif // !O_CLOEXEC + + auto closer = defer([&pfd]() { + if (pfd[0] != -1) { + close(pfd[0]); + } + + if (pfd[1] != -1) { + close(pfd[1]); + } + }); + + auto pid = fork(); + if (pid == -1) { + auto error = errno; + LOG(WARN) << "Could not execute ocsp query command for " << cert_file + << ": " << argv[0] << ", fork() failed, errno=" << error; + return -1; + } + + if (pid == 0) { + // child process + dup2(pfd[1], 1); + close(pfd[0]); + + rv = execve(argv[0], argv, envp); + if (rv == -1) { + auto error = errno; + LOG(WARN) << "Could not execute ocsp query command: " << argv[0] + << ", execve() faild, errno=" << error; + _Exit(EXIT_FAILURE); + } + // unreachable + } + + // parent process + close(pfd[1]); + pfd[1] = -1; + + ocsp_.pid = pid; + ocsp_.fd = pfd[0]; + pfd[0] = -1; + + util::make_socket_nonblocking(ocsp_.fd); + ev_io_set(&ocsp_.rev, ocsp_.fd, EV_READ); + ev_io_start(loop_, &ocsp_.rev); + + ev_child_set(&ocsp_.chldev, ocsp_.pid, 0); + ev_child_start(loop_, &ocsp_.chldev); + + return 0; +} + +void ConnectionHandler::read_ocsp_chunk() { + std::array buf; + for (;;) { + ssize_t n; + while ((n = read(ocsp_.fd, buf.data(), buf.size())) == -1 && errno == EINTR) + ; + + if (n == -1) { + if (errno == EAGAIN || errno == EWOULDBLOCK) { + return; + } + auto error = errno; + LOG(WARN) << "Reading from ocsp query command failed: errno=" << error; + ocsp_.error = error; + + break; + } + + if (n == 0) { + break; + } + + std::copy_n(std::begin(buf), n, std::back_inserter(ocsp_.resp)); + } + + ev_io_stop(loop_, &ocsp_.rev); +} + +void ConnectionHandler::handle_ocsp_complete() { + ev_io_stop(loop_, &ocsp_.rev); + ev_child_stop(loop_, &ocsp_.chldev); + + assert(ocsp_.next < all_ssl_ctx_.size()); + + auto ssl_ctx = all_ssl_ctx_[ocsp_.next]; + auto tls_ctx_data = + static_cast(SSL_CTX_get_app_data(ssl_ctx)); + + auto rstatus = ocsp_.chldev.rstatus; + auto status = WEXITSTATUS(rstatus); + if (ocsp_.error || !WIFEXITED(rstatus) || status != 0) { + LOG(WARN) << "ocsp query command for " << tls_ctx_data->cert_file + << " failed: error=" << ocsp_.error << ", rstatus=" << rstatus + << ", status=" << status; + ++ocsp_.next; + proceed_next_cert_ocsp(); + return; + } + + if (LOG_ENABLED(INFO)) { + LOG(INFO) << "ocsp update for " << tls_ctx_data->cert_file + << " finished successfully"; + } + + { + std::lock_guard g(tls_ctx_data->mu); + tls_ctx_data->ocsp_data = std::move(ocsp_.resp); + } + + ++ocsp_.next; + proceed_next_cert_ocsp(); +} + +void ConnectionHandler::reset_ocsp() { + if (ocsp_.fd != -1) { + close(ocsp_.fd); + } + + ocsp_.fd = -1; + ocsp_.pid = 0; + ocsp_.error = 0; + ocsp_.resp = std::vector(); +} + +void ConnectionHandler::proceed_next_cert_ocsp() { + for (;;) { + reset_ocsp(); + if (ocsp_.next == all_ssl_ctx_.size()) { + ocsp_.next = 0; + // We have updated all ocsp response, and schedule next update. + ev_timer_set(&ocsp_timer_, get_config()->ocsp_update_interval, 0.); + ev_timer_start(loop_, &ocsp_timer_); + return; + } + + auto ssl_ctx = all_ssl_ctx_[ocsp_.next]; + auto tls_ctx_data = + static_cast(SSL_CTX_get_app_data(ssl_ctx)); + auto cert_file = tls_ctx_data->cert_file; + + if (start_ocsp_update(cert_file) != 0) { + ++ocsp_.next; + continue; + } + + break; + } +} + +} // namespace shrpx diff --git a/src/shrpx_connection_handler.h b/src/shrpx_connection_handler.h new file mode 100644 index 0000000..00e4be4 --- /dev/null +++ b/src/shrpx_connection_handler.h @@ -0,0 +1,138 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef SHRPX_CONNECTION_HANDLER_H +#define SHRPX_CONNECTION_HANDLER_H + +#include "shrpx.h" + +#include +#ifdef HAVE_SYS_SOCKET_H +#include +#endif // HAVE_SYS_SOCKET_H + +#include +#include + +#include + +#include + +#include "shrpx_downstream_connection_pool.h" + +namespace shrpx { + +class Http2Session; +class ConnectBlocker; +class AcceptHandler; +class Worker; +struct WorkerStat; +struct TicketKeys; + +struct OCSPUpdateContext { + // ocsp response buffer + std::vector resp; + // index to ConnectionHandler::all_ssl_ctx_, which points to next + // SSL_CTX to update ocsp response cache. + size_t next; + ev_child chldev; + ev_io rev; + // fd to read response from fetch-ocsp-response script + int fd; + // errno encountered while processing response + int error; + // pid of forked fetch-ocsp-response script process + pid_t pid; +}; + +class ConnectionHandler { +public: + ConnectionHandler(struct ev_loop *loop); + ~ConnectionHandler(); + int handle_connection(int fd, sockaddr *addr, int addrlen); + // Creates Worker object for single threaded configuration. + void create_single_worker(); + // Creates |num| Worker objects for multi threaded configuration. + // The |num| must be strictly more than 1. + void create_worker_thread(size_t num); + void worker_reopen_log_files(); + void worker_renew_ticket_keys(const std::shared_ptr &ticket_keys); + void set_ticket_keys(std::shared_ptr ticket_keys); + const std::shared_ptr &get_ticket_keys() const; + struct ev_loop *get_loop() const; + Worker *get_single_worker() const; + void set_acceptor(std::unique_ptr h); + AcceptHandler *get_acceptor() const; + void set_acceptor6(std::unique_ptr h); + AcceptHandler *get_acceptor6() const; + void enable_acceptor(); + void disable_acceptor(); + void disable_acceptor_temporary(ev_tstamp t); + void accept_pending_connection(); + void graceful_shutdown_worker(); + void set_graceful_shutdown(bool f); + bool get_graceful_shutdown() const; + void join_worker(); + + // Cancels ocsp update process + void cancel_ocsp_update(); + // Starts ocsp update for certficate |cert_file|. + int start_ocsp_update(const char *cert_file); + // Reads incoming data from ocsp update process + void read_ocsp_chunk(); + // Handles the completion of one ocsp update + void handle_ocsp_complete(); + // Resets ocsp_; + void reset_ocsp(); + // Proceeds to the next certificate's ocsp update. If all + // certificates' ocsp update has been done, schedule next ocsp + // update. + void proceed_next_cert_ocsp(); + +private: + std::vector all_ssl_ctx_; + OCSPUpdateContext ocsp_; + // Worker instances when multi threaded mode (-nN, N >= 2) is used. + std::vector> workers_; + // Worker instance used when single threaded mode (-n1) is used. + // Otherwise, nullptr and workers_ has instances of Worker instead. + std::unique_ptr single_worker_; + // Current TLS session ticket keys. Note that TLS connection does + // not refer to this field directly. They use TicketKeys object in + // Worker object. + std::shared_ptr ticket_keys_; + struct ev_loop *loop_; + // acceptor for IPv4 address or UNIX domain socket. + std::unique_ptr acceptor_; + // acceptor for IPv6 address + std::unique_ptr acceptor6_; + ev_timer disable_acceptor_timer_; + ev_timer ocsp_timer_; + unsigned int worker_round_robin_cnt_; + bool graceful_shutdown_; +}; + +} // namespace shrpx + +#endif // SHRPX_CONNECTION_HANDLER_H diff --git a/src/shrpx_downstream.cc b/src/shrpx_downstream.cc new file mode 100644 index 0000000..6042878 --- /dev/null +++ b/src/shrpx_downstream.cc @@ -0,0 +1,1218 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "shrpx_downstream.h" + +#include + +#include "http-parser/http_parser.h" + +#include "shrpx_upstream.h" +#include "shrpx_client_handler.h" +#include "shrpx_config.h" +#include "shrpx_error.h" +#include "shrpx_downstream_connection.h" +#include "shrpx_downstream_queue.h" +#include "util.h" +#include "http2.h" + +namespace shrpx { + +namespace { +void upstream_timeoutcb(struct ev_loop *loop, ev_timer *w, int revents) { + auto downstream = static_cast(w->data); + auto upstream = downstream->get_upstream(); + + auto which = revents == EV_READ ? "read" : "write"; + + if (LOG_ENABLED(INFO)) { + DLOG(INFO, downstream) << "upstream timeout stream_id=" + << downstream->get_stream_id() << " event=" << which; + } + + downstream->disable_upstream_rtimer(); + downstream->disable_upstream_wtimer(); + + upstream->on_timeout(downstream); +} +} // namespace + +namespace { +void upstream_rtimeoutcb(struct ev_loop *loop, ev_timer *w, int revents) { + upstream_timeoutcb(loop, w, EV_READ); +} +} // namespace + +namespace { +void upstream_wtimeoutcb(struct ev_loop *loop, ev_timer *w, int revents) { + upstream_timeoutcb(loop, w, EV_WRITE); +} +} // namespace + +namespace { +void downstream_timeoutcb(struct ev_loop *loop, ev_timer *w, int revents) { + auto downstream = static_cast(w->data); + + auto which = revents == EV_READ ? "read" : "write"; + + if (LOG_ENABLED(INFO)) { + DLOG(INFO, downstream) << "downstream timeout stream_id=" + << downstream->get_downstream_stream_id() + << " event=" << which; + } + + downstream->disable_downstream_rtimer(); + downstream->disable_downstream_wtimer(); + + auto dconn = downstream->get_downstream_connection(); + + if (dconn) { + dconn->on_timeout(); + } +} +} // namespace + +namespace { +void downstream_rtimeoutcb(struct ev_loop *loop, ev_timer *w, int revents) { + downstream_timeoutcb(loop, w, EV_READ); +} +} // namespace + +namespace { +void downstream_wtimeoutcb(struct ev_loop *loop, ev_timer *w, int revents) { + downstream_timeoutcb(loop, w, EV_WRITE); +} +} // namespace + +// upstream could be nullptr for unittests +Downstream::Downstream(Upstream *upstream, MemchunkPool *mcpool, + int32_t stream_id, int32_t priority) + : dlnext(nullptr), dlprev(nullptr), + request_start_time_(std::chrono::high_resolution_clock::now()), + request_buf_(mcpool), response_buf_(mcpool), request_bodylen_(0), + response_bodylen_(0), response_sent_bodylen_(0), + request_content_length_(-1), response_content_length_(-1), + upstream_(upstream), blocked_link_(nullptr), request_headers_sum_(0), + response_headers_sum_(0), request_datalen_(0), response_datalen_(0), + num_retry_(0), stream_id_(stream_id), priority_(priority), + downstream_stream_id_(-1), + response_rst_stream_error_code_(NGHTTP2_NO_ERROR), + request_state_(INITIAL), request_major_(1), request_minor_(1), + response_state_(INITIAL), response_http_status_(0), response_major_(1), + response_minor_(1), dispatch_state_(DISPATCH_NONE), + upgrade_request_(false), upgraded_(false), http2_upgrade_seen_(false), + chunked_request_(false), request_connection_close_(false), + request_header_key_prev_(false), request_trailer_key_prev_(false), + request_http2_expect_body_(false), chunked_response_(false), + response_connection_close_(false), response_header_key_prev_(false), + response_trailer_key_prev_(false), expect_final_response_(false), + request_pending_(false) { + + ev_timer_init(&upstream_rtimer_, &upstream_rtimeoutcb, 0., + get_config()->stream_read_timeout); + ev_timer_init(&upstream_wtimer_, &upstream_wtimeoutcb, 0., + get_config()->stream_write_timeout); + ev_timer_init(&downstream_rtimer_, &downstream_rtimeoutcb, 0., + get_config()->stream_read_timeout); + ev_timer_init(&downstream_wtimer_, &downstream_wtimeoutcb, 0., + get_config()->stream_write_timeout); + + upstream_rtimer_.data = this; + upstream_wtimer_.data = this; + downstream_rtimer_.data = this; + downstream_wtimer_.data = this; + + http2::init_hdidx(request_hdidx_); + http2::init_hdidx(response_hdidx_); +} + +Downstream::~Downstream() { + if (LOG_ENABLED(INFO)) { + DLOG(INFO, this) << "Deleting"; + } + + if (blocked_link_) { + detach_blocked_link(blocked_link_); + } + + // check nullptr for unittest + if (upstream_) { + auto loop = upstream_->get_client_handler()->get_loop(); + + ev_timer_stop(loop, &upstream_rtimer_); + ev_timer_stop(loop, &upstream_wtimer_); + ev_timer_stop(loop, &downstream_rtimer_); + ev_timer_stop(loop, &downstream_wtimer_); + } + + if (LOG_ENABLED(INFO)) { + DLOG(INFO, this) << "Deleted"; + } +} + +int Downstream::attach_downstream_connection( + std::unique_ptr dconn) { + if (dconn->attach_downstream(this) != 0) { + return -1; + } + + dconn_ = std::move(dconn); + + return 0; +} + +void Downstream::detach_downstream_connection() { + if (!dconn_) { + return; + } + + dconn_->detach_downstream(this); + + auto handler = dconn_->get_client_handler(); + + handler->pool_downstream_connection( + std::unique_ptr(dconn_.release())); +} + +void Downstream::release_downstream_connection() { dconn_.release(); } + +DownstreamConnection *Downstream::get_downstream_connection() { + return dconn_.get(); +} + +std::unique_ptr Downstream::pop_downstream_connection() { + return std::unique_ptr(dconn_.release()); +} + +void Downstream::pause_read(IOCtrlReason reason) { + if (dconn_) { + dconn_->pause_read(reason); + } +} + +int Downstream::resume_read(IOCtrlReason reason, size_t consumed) { + if (dconn_) { + return dconn_->resume_read(reason, consumed); + } + + return 0; +} + +void Downstream::force_resume_read() { + if (dconn_) { + dconn_->force_resume_read(); + } +} + +namespace { +const Headers::value_type *get_header_linear(const Headers &headers, + const std::string &name) { + const Headers::value_type *res = nullptr; + for (auto &kv : headers) { + if (kv.name == name) { + res = &kv; + } + } + return res; +} +} // namespace + +const Headers &Downstream::get_request_headers() const { + return request_headers_; +} + +void Downstream::assemble_request_cookie() { + std::string &cookie = assembled_request_cookie_; + cookie = ""; + for (auto &kv : request_headers_) { + if (kv.name.size() != 6 || kv.name[5] != 'e' || + !util::streq_l("cooki", kv.name.c_str(), 5)) { + continue; + } + + auto end = kv.value.find_last_not_of(" ;"); + if (end == std::string::npos) { + cookie += kv.value; + } else { + cookie.append(std::begin(kv.value), std::begin(kv.value) + end + 1); + } + cookie += "; "; + } + if (cookie.size() >= 2) { + cookie.erase(cookie.size() - 2); + } +} + +Headers Downstream::crumble_request_cookie() { + Headers cookie_hdrs; + for (auto &kv : request_headers_) { + if (kv.name.size() != 6 || kv.name[5] != 'e' || + !util::streq_l("cooki", kv.name.c_str(), 5)) { + continue; + } + size_t last = kv.value.size(); + + for (size_t j = 0; j < last;) { + j = kv.value.find_first_not_of("\t ;", j); + if (j == std::string::npos) { + break; + } + auto first = j; + + j = kv.value.find(';', j); + if (j == std::string::npos) { + j = last; + } + + cookie_hdrs.push_back( + Header("cookie", kv.value.substr(first, j - first), kv.no_index)); + } + } + return cookie_hdrs; +} + +const std::string &Downstream::get_assembled_request_cookie() const { + return assembled_request_cookie_; +} + +namespace { +void add_header(bool &key_prev, size_t &sum, Headers &headers, std::string name, + std::string value) { + key_prev = true; + sum += name.size() + value.size(); + headers.emplace_back(std::move(name), std::move(value)); +} +} // namespace + +namespace { +void append_last_header_key(bool key_prev, size_t &sum, Headers &headers, + const char *data, size_t len) { + assert(key_prev); + sum += len; + auto &item = headers.back(); + item.name.append(data, len); +} +} // namespace + +namespace { +void append_last_header_value(bool key_prev, size_t &sum, Headers &headers, + const char *data, size_t len) { + assert(!key_prev); + sum += len; + auto &item = headers.back(); + item.value.append(data, len); +} +} // namespace + +namespace { +void set_last_header_value(bool &key_prev, size_t &sum, Headers &headers, + const char *data, size_t len) { + key_prev = false; + sum += len; + auto &item = headers.back(); + item.value.assign(data, len); +} +} // namespace + +namespace { +int index_headers(http2::HeaderIndex &hdidx, Headers &headers, + int64_t &content_length) { + for (size_t i = 0; i < headers.size(); ++i) { + auto &kv = headers[i]; + util::inp_strlower(kv.name); + + auto token = http2::lookup_token( + reinterpret_cast(kv.name.c_str()), kv.name.size()); + if (token < 0) { + continue; + } + + kv.token = token; + http2::index_header(hdidx, token, i); + + if (token == http2::HD_CONTENT_LENGTH) { + auto len = util::parse_uint(kv.value); + if (len == -1) { + return -1; + } + if (content_length != -1) { + return -1; + } + content_length = len; + } + } + return 0; +} +} // namespace + +int Downstream::index_request_headers() { + return index_headers(request_hdidx_, request_headers_, + request_content_length_); +} + +const Headers::value_type *Downstream::get_request_header(int16_t token) const { + return http2::get_header(request_hdidx_, token, request_headers_); +} + +const Headers::value_type * +Downstream::get_request_header(const std::string &name) const { + return get_header_linear(request_headers_, name); +} + +void Downstream::add_request_header(std::string name, std::string value) { + add_header(request_header_key_prev_, request_headers_sum_, request_headers_, + std::move(name), std::move(value)); +} + +void Downstream::set_last_request_header_value(const char *data, size_t len) { + set_last_header_value(request_header_key_prev_, request_headers_sum_, + request_headers_, data, len); +} + +void Downstream::add_request_header(std::string name, std::string value, + int16_t token) { + http2::index_header(request_hdidx_, token, request_headers_.size()); + request_headers_sum_ += name.size() + value.size(); + request_headers_.emplace_back(std::move(name), std::move(value), false, + token); +} + +void Downstream::add_request_header(const uint8_t *name, size_t namelen, + const uint8_t *value, size_t valuelen, + bool no_index, int16_t token) { + http2::index_header(request_hdidx_, token, request_headers_.size()); + request_headers_sum_ += namelen + valuelen; + http2::add_header(request_headers_, name, namelen, value, valuelen, no_index, + token); +} + +bool Downstream::get_request_header_key_prev() const { + return request_header_key_prev_; +} + +void Downstream::append_last_request_header_key(const char *data, size_t len) { + append_last_header_key(request_header_key_prev_, request_headers_sum_, + request_headers_, data, len); +} + +void Downstream::append_last_request_header_value(const char *data, + size_t len) { + append_last_header_value(request_header_key_prev_, request_headers_sum_, + request_headers_, data, len); +} + +void Downstream::clear_request_headers() { + Headers().swap(request_headers_); + http2::init_hdidx(request_hdidx_); +} + +size_t Downstream::get_request_headers_sum() const { + return request_headers_sum_; +} + +void Downstream::add_request_trailer(const uint8_t *name, size_t namelen, + const uint8_t *value, size_t valuelen, + bool no_index, int16_t token) { + // we never index trailer part. Header size limit should be applied + // to all request header fields combined. + request_headers_sum_ += namelen + valuelen; + http2::add_header(request_trailers_, name, namelen, value, valuelen, no_index, + -1); +} + +const Headers &Downstream::get_request_trailers() const { + return request_trailers_; +} + +void Downstream::add_request_trailer(std::string name, std::string value) { + add_header(request_trailer_key_prev_, request_headers_sum_, request_trailers_, + std::move(name), std::move(value)); +} + +void Downstream::set_last_request_trailer_value(const char *data, size_t len) { + set_last_header_value(request_trailer_key_prev_, request_headers_sum_, + request_trailers_, data, len); +} + +bool Downstream::get_request_trailer_key_prev() const { + return request_trailer_key_prev_; +} + +void Downstream::append_last_request_trailer_key(const char *data, size_t len) { + append_last_header_key(request_trailer_key_prev_, request_headers_sum_, + request_trailers_, data, len); +} + +void Downstream::append_last_request_trailer_value(const char *data, + size_t len) { + append_last_header_value(request_trailer_key_prev_, request_headers_sum_, + request_trailers_, data, len); +} + +void Downstream::set_request_method(std::string method) { + request_method_ = std::move(method); +} + +const std::string &Downstream::get_request_method() const { + return request_method_; +} + +void Downstream::set_request_path(std::string path) { + request_path_ = std::move(path); +} + +void Downstream::append_request_path(const char *data, size_t len) { + request_path_.append(data, len); +} + +const std::string &Downstream::get_request_path() const { + return request_path_; +} + +void Downstream::set_request_start_time( + std::chrono::high_resolution_clock::time_point time) { + request_start_time_ = std::move(time); +} + +const std::chrono::high_resolution_clock::time_point & +Downstream::get_request_start_time() const { + return request_start_time_; +} + +const std::string &Downstream::get_request_http2_scheme() const { + return request_http2_scheme_; +} + +void Downstream::set_request_http2_scheme(std::string scheme) { + request_http2_scheme_ = std::move(scheme); +} + +const std::string &Downstream::get_request_http2_authority() const { + return request_http2_authority_; +} + +void Downstream::set_request_http2_authority(std::string authority) { + request_http2_authority_ = std::move(authority); +} + +void Downstream::set_request_major(int major) { request_major_ = major; } + +void Downstream::set_request_minor(int minor) { request_minor_ = minor; } + +int Downstream::get_request_major() const { return request_major_; } + +int Downstream::get_request_minor() const { return request_minor_; } + +void Downstream::reset_upstream(Upstream *upstream) { + upstream_ = upstream; + if (dconn_) { + dconn_->on_upstream_change(upstream); + } +} + +Upstream *Downstream::get_upstream() const { return upstream_; } + +void Downstream::set_stream_id(int32_t stream_id) { stream_id_ = stream_id; } + +int32_t Downstream::get_stream_id() const { return stream_id_; } + +void Downstream::set_request_state(int state) { request_state_ = state; } + +int Downstream::get_request_state() const { return request_state_; } + +bool Downstream::get_chunked_request() const { return chunked_request_; } + +void Downstream::set_chunked_request(bool f) { chunked_request_ = f; } + +bool Downstream::get_request_connection_close() const { + return request_connection_close_; +} + +void Downstream::set_request_connection_close(bool f) { + request_connection_close_ = f; +} + +bool Downstream::get_request_http2_expect_body() const { + return request_http2_expect_body_; +} + +void Downstream::set_request_http2_expect_body(bool f) { + request_http2_expect_body_ = f; +} + +bool Downstream::request_buf_full() { + if (dconn_) { + return request_buf_.rleft() >= get_config()->downstream_request_buffer_size; + } else { + return false; + } +} + +DefaultMemchunks *Downstream::get_request_buf() { return &request_buf_; } + +// Call this function after this object is attached to +// Downstream. Otherwise, the program will crash. +int Downstream::push_request_headers() { + if (!dconn_) { + DLOG(INFO, this) << "dconn_ is NULL"; + return -1; + } + return dconn_->push_request_headers(); +} + +int Downstream::push_upload_data_chunk(const uint8_t *data, size_t datalen) { + // Assumes that request headers have already been pushed to output + // buffer using push_request_headers(). + if (!dconn_) { + DLOG(INFO, this) << "dconn_ is NULL"; + return -1; + } + request_bodylen_ += datalen; + if (dconn_->push_upload_data_chunk(data, datalen) != 0) { + return -1; + } + + request_datalen_ += datalen; + + return 0; +} + +int Downstream::end_upload_data() { + if (!dconn_) { + DLOG(INFO, this) << "dconn_ is NULL"; + return -1; + } + return dconn_->end_upload_data(); +} + +const Headers &Downstream::get_response_headers() const { + return response_headers_; +} + +int Downstream::index_response_headers() { + return index_headers(response_hdidx_, response_headers_, + response_content_length_); +} + +const Headers::value_type * +Downstream::get_response_header(int16_t token) const { + return http2::get_header(response_hdidx_, token, response_headers_); +} + +void Downstream::rewrite_location_response_header( + const std::string &upstream_scheme) { + auto hd = + http2::get_header(response_hdidx_, http2::HD_LOCATION, response_headers_); + if (!hd) { + return; + } + http_parser_url u; + memset(&u, 0, sizeof(u)); + int rv = + http_parser_parse_url((*hd).value.c_str(), (*hd).value.size(), 0, &u); + if (rv != 0) { + return; + } + std::string new_uri; + if (get_config()->no_host_rewrite || request_method_ == "CONNECT") { + if (!request_http2_authority_.empty()) { + new_uri = http2::rewrite_location_uri( + (*hd).value, u, request_http2_authority_, request_http2_authority_, + upstream_scheme); + } + if (new_uri.empty()) { + auto host = get_request_header(http2::HD_HOST); + if (host) { + new_uri = http2::rewrite_location_uri((*hd).value, u, (*host).value, + (*host).value, upstream_scheme); + } else if (!request_downstream_host_.empty()) { + new_uri = http2::rewrite_location_uri( + (*hd).value, u, request_downstream_host_, "", upstream_scheme); + } else { + return; + } + } + } else { + if (request_downstream_host_.empty()) { + return; + } + if (!request_http2_authority_.empty()) { + new_uri = http2::rewrite_location_uri( + (*hd).value, u, request_downstream_host_, request_http2_authority_, + upstream_scheme); + } else { + auto host = get_request_header(http2::HD_HOST); + if (host) { + new_uri = http2::rewrite_location_uri((*hd).value, u, + request_downstream_host_, + (*host).value, upstream_scheme); + } else { + new_uri = http2::rewrite_location_uri( + (*hd).value, u, request_downstream_host_, "", upstream_scheme); + } + } + } + if (!new_uri.empty()) { + auto idx = response_hdidx_[http2::HD_LOCATION]; + response_headers_[idx].value = std::move(new_uri); + } +} + +void Downstream::add_response_header(std::string name, std::string value) { + add_header(response_header_key_prev_, response_headers_sum_, + response_headers_, std::move(name), std::move(value)); +} + +void Downstream::set_last_response_header_value(const char *data, size_t len) { + set_last_header_value(response_header_key_prev_, response_headers_sum_, + response_headers_, data, len); +} + +void Downstream::add_response_header(std::string name, std::string value, + int16_t token) { + http2::index_header(response_hdidx_, token, response_headers_.size()); + response_headers_sum_ += name.size() + value.size(); + response_headers_.emplace_back(std::move(name), std::move(value), false, + token); +} + +void Downstream::add_response_header(const uint8_t *name, size_t namelen, + const uint8_t *value, size_t valuelen, + bool no_index, int16_t token) { + http2::index_header(response_hdidx_, token, response_headers_.size()); + response_headers_sum_ += namelen + valuelen; + http2::add_header(response_headers_, name, namelen, value, valuelen, no_index, + token); +} + +bool Downstream::get_response_header_key_prev() const { + return response_header_key_prev_; +} + +void Downstream::append_last_response_header_key(const char *data, size_t len) { + append_last_header_key(response_header_key_prev_, response_headers_sum_, + response_headers_, data, len); +} + +void Downstream::append_last_response_header_value(const char *data, + size_t len) { + append_last_header_value(response_header_key_prev_, response_headers_sum_, + response_headers_, data, len); +} + +void Downstream::clear_response_headers() { + Headers().swap(response_headers_); + http2::init_hdidx(response_hdidx_); +} + +size_t Downstream::get_response_headers_sum() const { + return response_headers_sum_; +} + +const Headers &Downstream::get_response_trailers() const { + return response_trailers_; +} + +void Downstream::add_response_trailer(const uint8_t *name, size_t namelen, + const uint8_t *value, size_t valuelen, + bool no_index, int16_t token) { + response_headers_sum_ += namelen + valuelen; + http2::add_header(response_trailers_, name, namelen, value, valuelen, + no_index, -1); +} + +unsigned int Downstream::get_response_http_status() const { + return response_http_status_; +} + +void Downstream::add_response_trailer(std::string name, std::string value) { + add_header(response_trailer_key_prev_, response_headers_sum_, + response_trailers_, std::move(name), std::move(value)); +} + +void Downstream::set_last_response_trailer_value(const char *data, size_t len) { + set_last_header_value(response_trailer_key_prev_, response_headers_sum_, + response_trailers_, data, len); +} + +bool Downstream::get_response_trailer_key_prev() const { + return response_trailer_key_prev_; +} + +void Downstream::append_last_response_trailer_key(const char *data, + size_t len) { + append_last_header_key(response_trailer_key_prev_, response_headers_sum_, + response_trailers_, data, len); +} + +void Downstream::append_last_response_trailer_value(const char *data, + size_t len) { + append_last_header_value(response_trailer_key_prev_, response_headers_sum_, + response_trailers_, data, len); +} + +void Downstream::set_response_http_status(unsigned int status) { + response_http_status_ = status; +} + +void Downstream::set_response_major(int major) { response_major_ = major; } + +void Downstream::set_response_minor(int minor) { response_minor_ = minor; } + +int Downstream::get_response_major() const { return response_major_; } + +int Downstream::get_response_minor() const { return response_minor_; } + +int Downstream::get_response_version() const { + return response_major_ * 100 + response_minor_; +} + +bool Downstream::get_chunked_response() const { return chunked_response_; } + +void Downstream::set_chunked_response(bool f) { chunked_response_ = f; } + +bool Downstream::get_response_connection_close() const { + return response_connection_close_; +} + +void Downstream::set_response_connection_close(bool f) { + response_connection_close_ = f; +} + +int Downstream::on_read() { + if (!dconn_) { + DLOG(INFO, this) << "dconn_ is NULL"; + return -1; + } + return dconn_->on_read(); +} + +int Downstream::change_priority(int32_t pri) { + if (!dconn_) { + DLOG(INFO, this) << "dconn_ is NULL"; + return -1; + } + return dconn_->on_priority_change(pri); +} + +void Downstream::set_response_state(int state) { response_state_ = state; } + +int Downstream::get_response_state() const { return response_state_; } + +DefaultMemchunks *Downstream::get_response_buf() { return &response_buf_; } + +bool Downstream::response_buf_full() { + if (dconn_) { + return response_buf_.rleft() >= + get_config()->downstream_response_buffer_size; + } else { + return false; + } +} + +void Downstream::add_response_bodylen(size_t amount) { + response_bodylen_ += amount; +} + +int64_t Downstream::get_response_bodylen() const { return response_bodylen_; } + +void Downstream::add_response_sent_bodylen(size_t amount) { + response_sent_bodylen_ += amount; +} + +int64_t Downstream::get_response_sent_bodylen() const { + return response_sent_bodylen_; +} + +int64_t Downstream::get_response_content_length() const { + return response_content_length_; +} + +void Downstream::set_response_content_length(int64_t len) { + response_content_length_ = len; +} + +int64_t Downstream::get_request_content_length() const { + return request_content_length_; +} + +void Downstream::set_request_content_length(int64_t len) { + request_content_length_ = len; +} + +bool Downstream::validate_request_bodylen() const { + if (request_content_length_ == -1) { + return true; + } + + if (request_content_length_ != request_bodylen_) { + if (LOG_ENABLED(INFO)) { + DLOG(INFO, this) << "request invalid bodylen: content-length=" + << request_content_length_ + << ", received=" << request_bodylen_; + } + return false; + } + + return true; +} + +bool Downstream::validate_response_bodylen() const { + if (!expect_response_body() || response_content_length_ == -1) { + return true; + } + + if (response_content_length_ != response_bodylen_) { + if (LOG_ENABLED(INFO)) { + DLOG(INFO, this) << "response invalid bodylen: content-length=" + << response_content_length_ + << ", received=" << response_bodylen_; + } + return false; + } + + return true; +} + +void Downstream::set_priority(int32_t pri) { priority_ = pri; } + +int32_t Downstream::get_priority() const { return priority_; } + +void Downstream::check_upgrade_fulfilled() { + if (request_method_ == "CONNECT") { + upgraded_ = 200 <= response_http_status_ && response_http_status_ < 300; + + return; + } + + if (response_http_status_ == 101) { + // TODO Do more strict checking for upgrade headers + upgraded_ = upgrade_request_; + + return; + } +} + +void Downstream::inspect_http2_request() { + if (request_method_ == "CONNECT") { + upgrade_request_ = true; + } +} + +void Downstream::inspect_http1_request() { + if (request_method_ == "CONNECT") { + upgrade_request_ = true; + } + + if (!upgrade_request_) { + auto idx = request_hdidx_[http2::HD_UPGRADE]; + if (idx != -1) { + upgrade_request_ = true; + + auto &val = request_headers_[idx].value; + // TODO Perform more strict checking for upgrade headers + if (util::streq_l(NGHTTP2_CLEARTEXT_PROTO_VERSION_ID, val.c_str(), + val.size())) { + http2_upgrade_seen_ = true; + } + } + } + auto idx = request_hdidx_[http2::HD_TRANSFER_ENCODING]; + if (idx != -1) { + request_content_length_ = -1; + if (util::strifind(request_headers_[idx].value.c_str(), "chunked")) { + chunked_request_ = true; + } + } +} + +void Downstream::inspect_http1_response() { + auto idx = response_hdidx_[http2::HD_TRANSFER_ENCODING]; + if (idx != -1) { + response_content_length_ = -1; + if (util::strifind(response_headers_[idx].value.c_str(), "chunked")) { + chunked_response_ = true; + } + } +} + +void Downstream::reset_response() { + response_http_status_ = 0; + response_major_ = 1; + response_minor_ = 1; +} + +bool Downstream::get_non_final_response() const { + return response_http_status_ / 100 == 1; +} + +bool Downstream::get_upgraded() const { return upgraded_; } + +bool Downstream::get_upgrade_request() const { return upgrade_request_; } + +bool Downstream::get_http2_upgrade_request() const { + return request_bodylen_ == 0 && http2_upgrade_seen_ && + request_hdidx_[http2::HD_HTTP2_SETTINGS] != -1; +} + +namespace { +const std::string EMPTY; +} // namespace + +const std::string &Downstream::get_http2_settings() const { + auto idx = request_hdidx_[http2::HD_HTTP2_SETTINGS]; + if (idx == -1) { + return EMPTY; + } + return request_headers_[idx].value; +} + +void Downstream::set_downstream_stream_id(int32_t stream_id) { + downstream_stream_id_ = stream_id; +} + +int32_t Downstream::get_downstream_stream_id() const { + return downstream_stream_id_; +} + +uint32_t Downstream::get_response_rst_stream_error_code() const { + return response_rst_stream_error_code_; +} + +void Downstream::set_response_rst_stream_error_code(uint32_t error_code) { + response_rst_stream_error_code_ = error_code; +} + +void Downstream::set_expect_final_response(bool f) { + expect_final_response_ = f; +} + +bool Downstream::get_expect_final_response() const { + return expect_final_response_; +} + +size_t Downstream::get_request_datalen() const { return request_datalen_; } + +void Downstream::dec_request_datalen(size_t len) { + assert(request_datalen_ >= len); + request_datalen_ -= len; +} + +void Downstream::reset_request_datalen() { request_datalen_ = 0; } + +void Downstream::add_response_datalen(size_t len) { response_datalen_ += len; } + +void Downstream::dec_response_datalen(size_t len) { + assert(response_datalen_ >= len); + response_datalen_ -= len; +} + +size_t Downstream::get_response_datalen() const { return response_datalen_; } + +void Downstream::reset_response_datalen() { response_datalen_ = 0; } + +bool Downstream::expect_response_body() const { + return http2::expect_response_body(request_method_, response_http_status_); +} + +namespace { +bool pseudo_header_allowed(const Headers &headers) { + if (headers.empty()) { + return true; + } + + return headers.back().name.c_str()[0] == ':'; +} +} // namespace + +bool Downstream::request_pseudo_header_allowed(int16_t token) const { + if (!pseudo_header_allowed(request_headers_)) { + return false; + } + return http2::check_http2_request_pseudo_header(request_hdidx_, token); +} + +bool Downstream::response_pseudo_header_allowed(int16_t token) const { + if (!pseudo_header_allowed(response_headers_)) { + return false; + } + return http2::check_http2_response_pseudo_header(response_hdidx_, token); +} + +namespace { +void reset_timer(struct ev_loop *loop, ev_timer *w) { ev_timer_again(loop, w); } +} // namespace + +namespace { +void try_reset_timer(struct ev_loop *loop, ev_timer *w) { + if (!ev_is_active(w)) { + return; + } + ev_timer_again(loop, w); +} +} // namespace + +namespace { +void ensure_timer(struct ev_loop *loop, ev_timer *w) { + if (ev_is_active(w)) { + return; + } + ev_timer_again(loop, w); +} +} // namespace + +namespace { +void disable_timer(struct ev_loop *loop, ev_timer *w) { + ev_timer_stop(loop, w); +} +} // namespace + +void Downstream::reset_upstream_rtimer() { + if (get_config()->stream_read_timeout == 0.) { + return; + } + auto loop = upstream_->get_client_handler()->get_loop(); + reset_timer(loop, &upstream_rtimer_); +} + +void Downstream::reset_upstream_wtimer() { + auto loop = upstream_->get_client_handler()->get_loop(); + if (get_config()->stream_write_timeout != 0.) { + reset_timer(loop, &upstream_wtimer_); + } + if (get_config()->stream_read_timeout != 0.) { + try_reset_timer(loop, &upstream_rtimer_); + } +} + +void Downstream::ensure_upstream_wtimer() { + if (get_config()->stream_write_timeout == 0.) { + return; + } + auto loop = upstream_->get_client_handler()->get_loop(); + ensure_timer(loop, &upstream_wtimer_); +} + +void Downstream::disable_upstream_rtimer() { + if (get_config()->stream_read_timeout == 0.) { + return; + } + auto loop = upstream_->get_client_handler()->get_loop(); + disable_timer(loop, &upstream_rtimer_); +} + +void Downstream::disable_upstream_wtimer() { + if (get_config()->stream_write_timeout == 0.) { + return; + } + auto loop = upstream_->get_client_handler()->get_loop(); + disable_timer(loop, &upstream_wtimer_); +} + +void Downstream::reset_downstream_rtimer() { + if (get_config()->stream_read_timeout == 0.) { + return; + } + auto loop = upstream_->get_client_handler()->get_loop(); + reset_timer(loop, &downstream_rtimer_); +} + +void Downstream::reset_downstream_wtimer() { + auto loop = upstream_->get_client_handler()->get_loop(); + if (get_config()->stream_write_timeout != 0.) { + reset_timer(loop, &downstream_wtimer_); + } + if (get_config()->stream_read_timeout != 0.) { + try_reset_timer(loop, &downstream_rtimer_); + } +} + +void Downstream::ensure_downstream_wtimer() { + if (get_config()->stream_write_timeout == 0.) { + return; + } + auto loop = upstream_->get_client_handler()->get_loop(); + ensure_timer(loop, &downstream_wtimer_); +} + +void Downstream::disable_downstream_rtimer() { + if (get_config()->stream_read_timeout == 0.) { + return; + } + auto loop = upstream_->get_client_handler()->get_loop(); + disable_timer(loop, &downstream_rtimer_); +} + +void Downstream::disable_downstream_wtimer() { + if (get_config()->stream_write_timeout == 0.) { + return; + } + auto loop = upstream_->get_client_handler()->get_loop(); + disable_timer(loop, &downstream_wtimer_); +} + +bool Downstream::accesslog_ready() const { return response_http_status_ > 0; } + +void Downstream::add_retry() { ++num_retry_; } + +bool Downstream::no_more_retry() const { return num_retry_ > 5; } + +void Downstream::set_request_downstream_host(std::string host) { + request_downstream_host_ = std::move(host); +} + +void Downstream::set_request_pending(bool f) { request_pending_ = f; } + +bool Downstream::get_request_pending() const { return request_pending_; } + +bool Downstream::request_submission_ready() const { + return (request_state_ == Downstream::HEADER_COMPLETE || + request_state_ == Downstream::MSG_COMPLETE) && + request_pending_ && response_state_ == Downstream::INITIAL; +} + +int Downstream::get_dispatch_state() const { return dispatch_state_; } + +void Downstream::set_dispatch_state(int s) { dispatch_state_ = s; } + +void Downstream::attach_blocked_link(BlockedLink *l) { + assert(!blocked_link_); + + l->downstream = this; + blocked_link_ = l; +} + +void Downstream::detach_blocked_link(BlockedLink *l) { + assert(blocked_link_); + assert(l->downstream == this); + + l->downstream = nullptr; + blocked_link_ = nullptr; +} + +void Downstream::add_request_headers_sum(size_t amount) { + request_headers_sum_ += amount; +} + +} // namespace shrpx diff --git a/src/shrpx_downstream.h b/src/shrpx_downstream.h new file mode 100644 index 0000000..2bd2931 --- /dev/null +++ b/src/shrpx_downstream.h @@ -0,0 +1,456 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef SHRPX_DOWNSTREAM_H +#define SHRPX_DOWNSTREAM_H + +#include "shrpx.h" + +#include +#include +#include +#include +#include + +#include + +#include + +#include "shrpx_io_control.h" +#include "http2.h" +#include "memchunk.h" + +using namespace nghttp2; + +namespace shrpx { + +class Upstream; +class DownstreamConnection; +struct BlockedLink; + +class Downstream { +public: + Downstream(Upstream *upstream, MemchunkPool *mcpool, int32_t stream_id, + int32_t priority); + ~Downstream(); + void reset_upstream(Upstream *upstream); + Upstream *get_upstream() const; + void set_stream_id(int32_t stream_id); + int32_t get_stream_id() const; + void set_priority(int32_t pri); + int32_t get_priority() const; + void pause_read(IOCtrlReason reason); + int resume_read(IOCtrlReason reason, size_t consumed); + void force_resume_read(); + // Set stream ID for downstream HTTP2 connection. + void set_downstream_stream_id(int32_t stream_id); + int32_t get_downstream_stream_id() const; + + int attach_downstream_connection(std::unique_ptr dconn); + void detach_downstream_connection(); + // Releases dconn_, without freeing it. + void release_downstream_connection(); + DownstreamConnection *get_downstream_connection(); + // Returns dconn_ and nullifies dconn_. + std::unique_ptr pop_downstream_connection(); + + // Returns true if output buffer is full. If underlying dconn_ is + // NULL, this function always returns false. + bool request_buf_full(); + // Returns true if upgrade (HTTP Upgrade or CONNECT) is succeeded. + void check_upgrade_fulfilled(); + // Returns true if the request is upgrade. + bool get_upgrade_request() const; + // Returns true if the upgrade is succeded as a result of the call + // check_upgrade_fulfilled(). + bool get_upgraded() const; + // Inspects HTTP/2 request. + void inspect_http2_request(); + // Inspects HTTP/1 request. This checks whether the request is + // upgrade request and tranfer-encoding etc. + void inspect_http1_request(); + // Returns true if the request is HTTP Upgrade for HTTP/2 + bool get_http2_upgrade_request() const; + // Returns the value of HTTP2-Settings request header field. + const std::string &get_http2_settings() const; + // downstream request API + const Headers &get_request_headers() const; + // Crumbles (split cookie by ";") in request_headers_ and returns + // them. Headers::no_index is inherited. + Headers crumble_request_cookie(); + void assemble_request_cookie(); + const std::string &get_assembled_request_cookie() const; + // Lower the request header field names and indexes request headers. + // If there is any invalid headers (e.g., multiple Content-Length + // having different values), returns -1. + int index_request_headers(); + // Returns pointer to the request header with the name |name|. If + // multiple header have |name| as name, return last occurrence from + // the beginning. If no such header is found, returns nullptr. + // This function must be called after headers are indexed + const Headers::value_type *get_request_header(int16_t token) const; + // Returns pointer to the request header with the name |name|. If + // no such header is found, returns nullptr. + const Headers::value_type *get_request_header(const std::string &name) const; + void add_request_header(std::string name, std::string value); + void set_last_request_header_value(const char *data, size_t len); + + void add_request_header(std::string name, std::string value, int16_t token); + void add_request_header(const uint8_t *name, size_t namelen, + const uint8_t *value, size_t valuelen, bool no_index, + int16_t token); + + bool get_request_header_key_prev() const; + void append_last_request_header_key(const char *data, size_t len); + void append_last_request_header_value(const char *data, size_t len); + // Empties request headers. + void clear_request_headers(); + + size_t get_request_headers_sum() const; + + const Headers &get_request_trailers() const; + void add_request_trailer(const uint8_t *name, size_t namelen, + const uint8_t *value, size_t valuelen, bool no_index, + int16_t token); + void add_request_trailer(std::string name, std::string value); + void set_last_request_trailer_value(const char *data, size_t len); + bool get_request_trailer_key_prev() const; + void append_last_request_trailer_key(const char *data, size_t len); + void append_last_request_trailer_value(const char *data, size_t len); + + void set_request_method(std::string method); + const std::string &get_request_method() const; + void set_request_path(std::string path); + void add_request_headers_sum(size_t amount); + void + set_request_start_time(std::chrono::high_resolution_clock::time_point time); + const std::chrono::high_resolution_clock::time_point & + get_request_start_time() const; + void append_request_path(const char *data, size_t len); + // Returns request path. For HTTP/1.1, this is request-target. For + // HTTP/2, this is :path header field value. + const std::string &get_request_path() const; + // Returns HTTP/2 :scheme header field value. + const std::string &get_request_http2_scheme() const; + void set_request_http2_scheme(std::string scheme); + // Returns HTTP/2 :authority header field value. We also set the + // value retrieved from absolute-form HTTP/1 request. + const std::string &get_request_http2_authority() const; + void set_request_http2_authority(std::string authority); + void set_request_major(int major); + void set_request_minor(int minor); + int get_request_major() const; + int get_request_minor() const; + int push_request_headers(); + bool get_chunked_request() const; + void set_chunked_request(bool f); + bool get_request_connection_close() const; + void set_request_connection_close(bool f); + bool get_request_http2_expect_body() const; + void set_request_http2_expect_body(bool f); + int push_upload_data_chunk(const uint8_t *data, size_t datalen); + int end_upload_data(); + size_t get_request_datalen() const; + void dec_request_datalen(size_t len); + void reset_request_datalen(); + // Validates that received request body length and content-length + // matches. + bool validate_request_bodylen() const; + int64_t get_request_content_length() const; + void set_request_content_length(int64_t len); + bool request_pseudo_header_allowed(int16_t token) const; + void set_request_downstream_host(std::string host); + bool expect_response_body() const; + enum { + INITIAL, + HEADER_COMPLETE, + MSG_COMPLETE, + STREAM_CLOSED, + CONNECT_FAIL, + IDLE, + MSG_RESET, + // header contains invalid header field. We can safely send error + // response (502) to a client. + MSG_BAD_HEADER, + // header fields in HTTP/1 request exceed the configuration limit. + // This state is only transitioned from INITIAL state, and solely + // used to signal 431 status code to the client. + HTTP1_REQUEST_HEADER_TOO_LARGE, + }; + void set_request_state(int state); + int get_request_state() const; + DefaultMemchunks *get_request_buf(); + void set_request_pending(bool f); + bool get_request_pending() const; + // Returns true if request is ready to be submitted to downstream. + bool request_submission_ready() const; + // downstream response API + const Headers &get_response_headers() const; + // Lower the response header field names and indexes response + // headers. If there are invalid headers (e.g., multiple + // Content-Length with different values), returns -1. + int index_response_headers(); + // Returns pointer to the response header with the name |name|. If + // multiple header have |name| as name, return last occurrence from + // the beginning. If no such header is found, returns nullptr. + // This function must be called after response headers are indexed. + const Headers::value_type *get_response_header(int16_t token) const; + // Rewrites the location response header field. + void rewrite_location_response_header(const std::string &upstream_scheme); + void add_response_header(std::string name, std::string value); + void set_last_response_header_value(const char *data, size_t len); + + void add_response_header(std::string name, std::string value, int16_t token); + void add_response_header(const uint8_t *name, size_t namelen, + const uint8_t *value, size_t valuelen, bool no_index, + int16_t token); + + bool get_response_header_key_prev() const; + void append_last_response_header_key(const char *data, size_t len); + void append_last_response_header_value(const char *data, size_t len); + // Empties response headers. + void clear_response_headers(); + + size_t get_response_headers_sum() const; + + const Headers &get_response_trailers() const; + void add_response_trailer(const uint8_t *name, size_t namelen, + const uint8_t *value, size_t valuelen, + bool no_index, int16_t token); + void add_response_trailer(std::string name, std::string value); + void set_last_response_trailer_value(const char *data, size_t len); + bool get_response_trailer_key_prev() const; + void append_last_response_trailer_key(const char *data, size_t len); + void append_last_response_trailer_value(const char *data, size_t len); + + unsigned int get_response_http_status() const; + void set_response_http_status(unsigned int status); + void set_response_major(int major); + void set_response_minor(int minor); + int get_response_major() const; + int get_response_minor() const; + int get_response_version() const; + bool get_chunked_response() const; + void set_chunked_response(bool f); + bool get_response_connection_close() const; + void set_response_connection_close(bool f); + void set_response_state(int state); + int get_response_state() const; + DefaultMemchunks *get_response_buf(); + bool response_buf_full(); + void add_response_bodylen(size_t amount); + int64_t get_response_bodylen() const; + void add_response_sent_bodylen(size_t amount); + int64_t get_response_sent_bodylen() const; + int64_t get_response_content_length() const; + void set_response_content_length(int64_t len); + // Validates that received response body length and content-length + // matches. + bool validate_response_bodylen() const; + uint32_t get_response_rst_stream_error_code() const; + void set_response_rst_stream_error_code(uint32_t error_code); + // Inspects HTTP/1 response. This checks tranfer-encoding etc. + void inspect_http1_response(); + // Clears some of member variables for response. + void reset_response(); + bool get_non_final_response() const; + void set_expect_final_response(bool f); + bool get_expect_final_response() const; + void add_response_datalen(size_t len); + void dec_response_datalen(size_t len); + size_t get_response_datalen() const; + void reset_response_datalen(); + bool response_pseudo_header_allowed(int16_t token) const; + + // Call this method when there is incoming data in downstream + // connection. + int on_read(); + + // Change the priority of downstream + int change_priority(int32_t pri); + + bool get_rst_stream_after_end_stream() const; + void set_rst_stream_after_end_stream(bool f); + + // Resets upstream read timer. If it is active, timeout value is + // reset. If it is not active, timer will be started. + void reset_upstream_rtimer(); + // Resets upstream write timer. If it is active, timeout value is + // reset. If it is not active, timer will be started. This + // function also resets read timer if it has been started. + void reset_upstream_wtimer(); + // Makes sure that upstream write timer is started. If it has been + // started, do nothing. Otherwise, write timer will be started. + void ensure_upstream_wtimer(); + // Disables upstream read timer. + void disable_upstream_rtimer(); + // Disables upstream write timer. + void disable_upstream_wtimer(); + + // Downstream timer functions. They works in a similar way just + // like the upstream timer function. + void reset_downstream_rtimer(); + void reset_downstream_wtimer(); + void ensure_downstream_wtimer(); + void disable_downstream_rtimer(); + void disable_downstream_wtimer(); + + // Returns true if accesslog can be written for this downstream. + bool accesslog_ready() const; + + // Increment retry count + void add_retry(); + // true if retry attempt should not be done. + bool no_more_retry() const; + + int get_dispatch_state() const; + void set_dispatch_state(int s); + + void attach_blocked_link(BlockedLink *l); + void detach_blocked_link(BlockedLink *l); + + enum { + EVENT_ERROR = 0x1, + EVENT_TIMEOUT = 0x2, + }; + + enum { + DISPATCH_NONE, + DISPATCH_PENDING, + DISPATCH_BLOCKED, + DISPATCH_ACTIVE, + DISPATCH_FAILURE, + }; + + Downstream *dlnext, *dlprev; + +private: + Headers request_headers_; + Headers response_headers_; + + // trailer part. For HTTP/1.1, trailer part is only included with + // chunked encoding. For HTTP/2, there is no such limit. + Headers request_trailers_; + Headers response_trailers_; + + std::chrono::high_resolution_clock::time_point request_start_time_; + + std::string request_method_; + std::string request_path_; + std::string request_http2_scheme_; + std::string request_http2_authority_; + // host we requested to downstream. This is used to rewrite + // location header field to decide the location should be rewritten + // or not. + std::string request_downstream_host_; + std::string assembled_request_cookie_; + + DefaultMemchunks request_buf_; + DefaultMemchunks response_buf_; + + ev_timer upstream_rtimer_; + ev_timer upstream_wtimer_; + + ev_timer downstream_rtimer_; + ev_timer downstream_wtimer_; + + // the length of request body received so far + int64_t request_bodylen_; + // the length of response body received so far + int64_t response_bodylen_; + + // the length of response body sent to upstream client + int64_t response_sent_bodylen_; + + // content-length of request body, -1 if it is unknown. + int64_t request_content_length_; + // content-length of response body, -1 if it is unknown. + int64_t response_content_length_; + + Upstream *upstream_; + std::unique_ptr dconn_; + + // only used by HTTP/2 or SPDY upstream + BlockedLink *blocked_link_; + + size_t request_headers_sum_; + size_t response_headers_sum_; + + // The number of bytes not consumed by the application yet. + size_t request_datalen_; + size_t response_datalen_; + + size_t num_retry_; + + int32_t stream_id_; + int32_t priority_; + // stream ID in backend connection + int32_t downstream_stream_id_; + + // RST_STREAM error_code from downstream HTTP2 connection + uint32_t response_rst_stream_error_code_; + + int request_state_; + int request_major_; + int request_minor_; + + int response_state_; + unsigned int response_http_status_; + int response_major_; + int response_minor_; + + // only used by HTTP/2 or SPDY upstream + int dispatch_state_; + + http2::HeaderIndex request_hdidx_; + http2::HeaderIndex response_hdidx_; + + // true if the request contains upgrade token (HTTP Upgrade or + // CONNECT) + bool upgrade_request_; + // true if the connection is upgraded (HTTP Upgrade or CONNECT) + bool upgraded_; + + bool http2_upgrade_seen_; + + bool chunked_request_; + bool request_connection_close_; + bool request_header_key_prev_; + bool request_trailer_key_prev_; + bool request_http2_expect_body_; + + bool chunked_response_; + bool response_connection_close_; + bool response_header_key_prev_; + bool response_trailer_key_prev_; + bool expect_final_response_; + // true if downstream request is pending because backend connection + // has not been established or should be checked before use; + // currently used only with HTTP/2 connection. + bool request_pending_; +}; + +} // namespace shrpx + +#endif // SHRPX_DOWNSTREAM_H diff --git a/src/shrpx_downstream_connection.cc b/src/shrpx_downstream_connection.cc new file mode 100644 index 0000000..77dcd44 --- /dev/null +++ b/src/shrpx_downstream_connection.cc @@ -0,0 +1,52 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "shrpx_downstream_connection.h" + +#include "shrpx_client_handler.h" +#include "shrpx_downstream.h" +#include "shrpx_downstream_connection_pool.h" + +namespace shrpx { + +DownstreamConnection::DownstreamConnection(DownstreamConnectionPool *dconn_pool) + : dconn_pool_(dconn_pool), client_handler_(nullptr), downstream_(nullptr) {} + +DownstreamConnection::~DownstreamConnection() {} + +void DownstreamConnection::set_client_handler(ClientHandler *handler) { + client_handler_ = handler; +} + +ClientHandler *DownstreamConnection::get_client_handler() { + return client_handler_; +} + +Downstream *DownstreamConnection::get_downstream() { return downstream_; } + +DownstreamConnectionPool *DownstreamConnection::get_dconn_pool() const { + return dconn_pool_; +} + +} // namespace shrpx diff --git a/src/shrpx_downstream_connection.h b/src/shrpx_downstream_connection.h new file mode 100644 index 0000000..49ca1be --- /dev/null +++ b/src/shrpx_downstream_connection.h @@ -0,0 +1,77 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef SHRPX_DOWNSTREAM_CONNECTION_H +#define SHRPX_DOWNSTREAM_CONNECTION_H + +#include "shrpx.h" + +#include "shrpx_io_control.h" + +namespace shrpx { + +class ClientHandler; +class Upstream; +class Downstream; +class DownstreamConnectionPool; + +class DownstreamConnection { +public: + DownstreamConnection(DownstreamConnectionPool *dconn_pool); + virtual ~DownstreamConnection(); + virtual int attach_downstream(Downstream *downstream) = 0; + virtual void detach_downstream(Downstream *downstream) = 0; + + virtual int push_request_headers() = 0; + virtual int push_upload_data_chunk(const uint8_t *data, size_t datalen) = 0; + virtual int end_upload_data() = 0; + + virtual void pause_read(IOCtrlReason reason) = 0; + virtual int resume_read(IOCtrlReason reason, size_t consumed) = 0; + virtual void force_resume_read() = 0; + + virtual int on_read() = 0; + virtual int on_write() = 0; + virtual int on_timeout() { return 0; } + + virtual void on_upstream_change(Upstream *uptream) = 0; + virtual int on_priority_change(int32_t pri) = 0; + + // true if this object is poolable. + virtual bool poolable() const = 0; + + void set_client_handler(ClientHandler *client_handler); + ClientHandler *get_client_handler(); + Downstream *get_downstream(); + DownstreamConnectionPool *get_dconn_pool() const; + +protected: + DownstreamConnectionPool *dconn_pool_; + ClientHandler *client_handler_; + Downstream *downstream_; +}; + +} // namespace shrpx + +#endif // SHRPX_DOWNSTREAM_CONNECTION_H diff --git a/src/shrpx_downstream_connection_pool.cc b/src/shrpx_downstream_connection_pool.cc new file mode 100644 index 0000000..d762b77 --- /dev/null +++ b/src/shrpx_downstream_connection_pool.cc @@ -0,0 +1,60 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2014 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "shrpx_downstream_connection_pool.h" +#include "shrpx_downstream_connection.h" + +namespace shrpx { + +DownstreamConnectionPool::DownstreamConnectionPool() {} + +DownstreamConnectionPool::~DownstreamConnectionPool() { + for (auto dconn : pool_) { + delete dconn; + } +} + +void DownstreamConnectionPool::add_downstream_connection( + std::unique_ptr dconn) { + pool_.insert(dconn.release()); +} + +std::unique_ptr +DownstreamConnectionPool::pop_downstream_connection() { + if (pool_.empty()) { + return nullptr; + } + + auto dconn = std::unique_ptr(*std::begin(pool_)); + pool_.erase(std::begin(pool_)); + return dconn; +} + +void DownstreamConnectionPool::remove_downstream_connection( + DownstreamConnection *dconn) { + pool_.erase(dconn); + delete dconn; +} + +} // namespace shrpx diff --git a/src/shrpx_downstream_connection_pool.h b/src/shrpx_downstream_connection_pool.h new file mode 100644 index 0000000..c2edce4 --- /dev/null +++ b/src/shrpx_downstream_connection_pool.h @@ -0,0 +1,52 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2014 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef SHRPX_DOWNSTREAM_CONNECTION_POOL_H +#define SHRPX_DOWNSTREAM_CONNECTION_POOL_H + +#include "shrpx.h" + +#include +#include + +namespace shrpx { + +class DownstreamConnection; + +class DownstreamConnectionPool { +public: + DownstreamConnectionPool(); + ~DownstreamConnectionPool(); + + void add_downstream_connection(std::unique_ptr dconn); + std::unique_ptr pop_downstream_connection(); + void remove_downstream_connection(DownstreamConnection *dconn); + +private: + std::set pool_; +}; + +} // namespace shrpx + +#endif // SHRPX_DOWNSTREAM_CONNECTION_POOL_H diff --git a/src/shrpx_downstream_queue.cc b/src/shrpx_downstream_queue.cc new file mode 100644 index 0000000..32ff4e6 --- /dev/null +++ b/src/shrpx_downstream_queue.cc @@ -0,0 +1,167 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "shrpx_downstream_queue.h" + +#include +#include + +#include "shrpx_downstream.h" + +namespace shrpx { + +DownstreamQueue::HostEntry::HostEntry() : num_active(0) {} + +DownstreamQueue::DownstreamQueue(size_t conn_max_per_host, bool unified_host) + : conn_max_per_host_(conn_max_per_host == 0 + ? std::numeric_limits::max() + : conn_max_per_host), + unified_host_(unified_host) {} + +DownstreamQueue::~DownstreamQueue() { + dlist_delete_all(downstreams_); + for (auto &p : host_entries_) { + auto &ent = p.second; + dlist_delete_all(ent.blocked); + } +} + +void DownstreamQueue::add_pending(std::unique_ptr downstream) { + downstream->set_dispatch_state(Downstream::DISPATCH_PENDING); + downstreams_.append(downstream.release()); +} + +void DownstreamQueue::mark_failure(Downstream *downstream) { + downstream->set_dispatch_state(Downstream::DISPATCH_FAILURE); +} + +DownstreamQueue::HostEntry & +DownstreamQueue::find_host_entry(const std::string &host) { + auto itr = host_entries_.find(host); + if (itr == std::end(host_entries_)) { +#ifdef HAVE_STD_MAP_EMPLACE + std::tie(itr, std::ignore) = host_entries_.emplace(host, HostEntry()); +#else // !HAVE_STD_MAP_EMPLACE + // for g++-4.7 + std::tie(itr, std::ignore) = host_entries_.insert({host, HostEntry()}); +#endif // !HAVE_STD_MAP_EMPLACE + } + return (*itr).second; +} + +const std::string & +DownstreamQueue::make_host_key(const std::string &host) const { + static std::string empty_key; + return unified_host_ ? empty_key : host; +} + +const std::string & +DownstreamQueue::make_host_key(Downstream *downstream) const { + return make_host_key(downstream->get_request_http2_authority()); +} + +void DownstreamQueue::mark_active(Downstream *downstream) { + auto &ent = find_host_entry(make_host_key(downstream)); + ++ent.num_active; + + downstream->set_dispatch_state(Downstream::DISPATCH_ACTIVE); +} + +void DownstreamQueue::mark_blocked(Downstream *downstream) { + auto &ent = find_host_entry(make_host_key(downstream)); + + downstream->set_dispatch_state(Downstream::DISPATCH_BLOCKED); + + auto link = new BlockedLink{}; + downstream->attach_blocked_link(link); + ent.blocked.append(link); +} + +bool DownstreamQueue::can_activate(const std::string &host) const { + auto itr = host_entries_.find(make_host_key(host)); + if (itr == std::end(host_entries_)) { + return true; + } + auto &ent = (*itr).second; + return ent.num_active < conn_max_per_host_; +} + +namespace { +bool remove_host_entry_if_empty(const DownstreamQueue::HostEntry &ent, + DownstreamQueue::HostEntryMap &host_entries, + const std::string &host) { + if (ent.blocked.empty() && ent.num_active == 0) { + host_entries.erase(host); + return true; + } + return false; +} +} // namespace + +Downstream *DownstreamQueue::remove_and_get_blocked(Downstream *downstream) { + // Delete downstream when this function returns. + auto delptr = std::unique_ptr(downstream); + + if (downstream->get_dispatch_state() != Downstream::DISPATCH_ACTIVE) { + assert(downstream->get_dispatch_state() != Downstream::DISPATCH_NONE); + downstreams_.remove(downstream); + return nullptr; + } + + downstreams_.remove(downstream); + + auto &host = make_host_key(downstream); + auto &ent = find_host_entry(host); + --ent.num_active; + + if (remove_host_entry_if_empty(ent, host_entries_, host)) { + return nullptr; + } + + if (ent.num_active >= conn_max_per_host_) { + return nullptr; + } + + for (auto link = ent.blocked.head; link;) { + auto next = link->dlnext; + if (!link->downstream) { + ent.blocked.remove(link); + link = next; + continue; + } + auto next_downstream = link->downstream; + next_downstream->detach_blocked_link(link); + ent.blocked.remove(link); + delete link; + remove_host_entry_if_empty(ent, host_entries_, host); + return next_downstream; + } + return nullptr; +} + +Downstream *DownstreamQueue::get_downstreams() const { + return downstreams_.head; +} + +} // namespace shrpx diff --git a/src/shrpx_downstream_queue.h b/src/shrpx_downstream_queue.h new file mode 100644 index 0000000..d327a05 --- /dev/null +++ b/src/shrpx_downstream_queue.h @@ -0,0 +1,105 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef SHRPX_DOWNSTREAM_QUEUE_H +#define SHRPX_DOWNSTREAM_QUEUE_H + +#include "shrpx.h" + +#include +#include +#include +#include + +#include "template.h" + +using namespace nghttp2; + +namespace shrpx { + +class Downstream; + +// Link entry in HostEntry.blocked and downstream because downstream +// could be deleted in anytime and we'd like to find Downstream in +// O(1). Downstream has field to link back to this object. +struct BlockedLink { + Downstream *downstream; + BlockedLink *dlnext, *dlprev; +}; + +class DownstreamQueue { +public: + struct HostEntry { + // Set of stream ID that blocked by conn_max_per_host_. + DList blocked; + // The number of connections currently made to this host. + size_t num_active; + HostEntry(); + }; + + typedef std::map HostEntryMap; + + // conn_max_per_host == 0 means no limit for downstream connection. + DownstreamQueue(size_t conn_max_per_host = 0, bool unified_host = true); + ~DownstreamQueue(); + // Add |downstream| to this queue. This is entry point for + // Downstream object. + void add_pending(std::unique_ptr downstream); + // Set |downstream| to failure state, which means that downstream + // failed to connect to backend. + void mark_failure(Downstream *downstream); + // Set |downstream| to active state, which means that downstream + // connection has started. + void mark_active(Downstream *downstream); + // Set |downstream| to blocked state, which means that download + // connection was blocked because conn_max_per_host_ limit. + void mark_blocked(Downstream *downstream); + // Returns true if we can make downstream connection to given + // |host|. + bool can_activate(const std::string &host) const; + // Removes and frees |downstream| object. If |downstream| is in + // Downstream::DISPATCH_ACTIVE, this function may return Downstream + // object with the same target host in Downstream::DISPATCH_BLOCKED + // if its connection is now not blocked by conn_max_per_host_ limit. + Downstream *remove_and_get_blocked(Downstream *downstream); + Downstream *get_downstreams() const; + HostEntry &find_host_entry(const std::string &host); + const std::string &make_host_key(const std::string &host) const; + const std::string &make_host_key(Downstream *downstream) const; + +private: + // Per target host structure to keep track of the number of + // connections to the same host. + std::map host_entries_; + DList downstreams_; + // Maximum number of concurrent connections to the same host. + size_t conn_max_per_host_; + // true if downstream host is treated as the same. Used for reverse + // proxying. + bool unified_host_; +}; + +} // namespace shrpx + +#endif // SHRPX_DOWNSTREAM_QUEUE_H diff --git a/src/shrpx_downstream_test.cc b/src/shrpx_downstream_test.cc new file mode 100644 index 0000000..2e7a696 --- /dev/null +++ b/src/shrpx_downstream_test.cc @@ -0,0 +1,160 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2013 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "shrpx_downstream_test.h" + +#include + +#include + +#include "shrpx_downstream.h" + +namespace shrpx { + +void test_downstream_index_request_headers(void) { + Downstream d(nullptr, nullptr, 0, 0); + d.add_request_header("1", "0"); + d.add_request_header("2", "1"); + d.add_request_header("Charlie", "2"); + d.add_request_header("Alpha", "3"); + d.add_request_header("Delta", "4"); + d.add_request_header("BravO", "5"); + d.add_request_header(":method", "6"); + d.add_request_header(":authority", "7"); + d.index_request_headers(); + + auto ans = Headers{{"1", "0"}, + {"2", "1"}, + {"charlie", "2"}, + {"alpha", "3"}, + {"delta", "4"}, + {"bravo", "5"}, + {":method", "6"}, + {":authority", "7"}}; + CU_ASSERT(ans == d.get_request_headers()); +} + +void test_downstream_index_response_headers(void) { + Downstream d(nullptr, nullptr, 0, 0); + d.add_response_header("Charlie", "0"); + d.add_response_header("Alpha", "1"); + d.add_response_header("Delta", "2"); + d.add_response_header("BravO", "3"); + d.index_response_headers(); + + auto ans = + Headers{{"charlie", "0"}, {"alpha", "1"}, {"delta", "2"}, {"bravo", "3"}}; + CU_ASSERT(ans == d.get_response_headers()); +} + +void test_downstream_get_request_header(void) { + Downstream d(nullptr, nullptr, 0, 0); + d.add_request_header("alpha", "0"); + d.add_request_header(":authority", "1"); + d.add_request_header("content-length", "2"); + d.index_request_headers(); + + // By token + CU_ASSERT(Header(":authority", "1") == + *d.get_request_header(http2::HD__AUTHORITY)); + CU_ASSERT(nullptr == d.get_request_header(http2::HD__METHOD)); + + // By name + CU_ASSERT(Header("alpha", "0") == *d.get_request_header("alpha")); + CU_ASSERT(nullptr == d.get_request_header("bravo")); +} + +void test_downstream_get_response_header(void) { + Downstream d(nullptr, nullptr, 0, 0); + d.add_response_header("alpha", "0"); + d.add_response_header(":status", "1"); + d.add_response_header("content-length", "2"); + d.index_response_headers(); + + // By token + CU_ASSERT(Header(":status", "1") == + *d.get_response_header(http2::HD__STATUS)); + CU_ASSERT(nullptr == d.get_response_header(http2::HD__METHOD)); +} + +void test_downstream_crumble_request_cookie(void) { + Downstream d(nullptr, nullptr, 0, 0); + d.add_request_header(":method", "get"); + d.add_request_header(":path", "/"); + auto val = "alpha; bravo; ; ;; charlie;;"; + d.add_request_header( + reinterpret_cast("cookie"), sizeof("cookie") - 1, + reinterpret_cast(val), strlen(val), true, -1); + d.add_request_header("cookie", ";delta"); + d.add_request_header("cookie", "echo"); + auto cookies = d.crumble_request_cookie(); + + Headers ans = {{"cookie", "alpha"}, + {"cookie", "bravo"}, + {"cookie", "charlie"}, + {"cookie", "delta"}, + {"cookie", "echo"}}; + CU_ASSERT(ans == cookies); + CU_ASSERT(cookies[0].no_index); + CU_ASSERT(cookies[1].no_index); + CU_ASSERT(cookies[2].no_index); +} + +void test_downstream_assemble_request_cookie(void) { + Downstream d(nullptr, nullptr, 0, 0); + d.add_request_header(":method", "get"); + d.add_request_header(":path", "/"); + d.add_request_header("cookie", "alpha"); + d.add_request_header("cookie", "bravo;"); + d.add_request_header("cookie", "charlie; "); + d.add_request_header("cookie", "delta;;"); + d.assemble_request_cookie(); + CU_ASSERT("alpha; bravo; charlie; delta" == d.get_assembled_request_cookie()); +} + +void test_downstream_rewrite_location_response_header(void) { + { + Downstream d(nullptr, nullptr, 0, 0); + d.set_request_downstream_host("localhost:3000"); + d.add_request_header("host", "localhost"); + d.add_response_header("location", "http://localhost:3000/"); + d.index_request_headers(); + d.index_response_headers(); + d.rewrite_location_response_header("https"); + auto location = d.get_response_header(http2::HD_LOCATION); + CU_ASSERT("https://localhost/" == (*location).value); + } + { + Downstream d(nullptr, nullptr, 0, 0); + d.set_request_downstream_host("localhost"); + d.set_request_http2_authority("localhost"); + d.add_response_header("location", "http://localhost:3000/"); + d.index_response_headers(); + d.rewrite_location_response_header("https"); + auto location = d.get_response_header(http2::HD_LOCATION); + CU_ASSERT("https://localhost/" == (*location).value); + } +} + +} // namespace shrpx diff --git a/src/shrpx_downstream_test.h b/src/shrpx_downstream_test.h new file mode 100644 index 0000000..67e8f6b --- /dev/null +++ b/src/shrpx_downstream_test.h @@ -0,0 +1,44 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2013 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef SHRPX_DOWNSTREAM_TEST_H +#define SHRPX_DOWNSTREAM_TEST_H + +#ifdef HAVE_CONFIG_H +#include +#endif // HAVE_CONFIG_H + +namespace shrpx { + +void test_downstream_index_request_headers(void); +void test_downstream_index_response_headers(void); +void test_downstream_get_request_header(void); +void test_downstream_get_response_header(void); +void test_downstream_crumble_request_cookie(void); +void test_downstream_assemble_request_cookie(void); +void test_downstream_rewrite_location_response_header(void); + +} // namespace shrpx + +#endif // SHRPX_DOWNSTREAM_TEST_H diff --git a/src/shrpx_error.h b/src/shrpx_error.h new file mode 100644 index 0000000..f1d1bff --- /dev/null +++ b/src/shrpx_error.h @@ -0,0 +1,43 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef SHRPX_ERROR_H +#define SHRPX_ERROR_H + +#include "shrpx.h" + +namespace shrpx { + +// Deprecated, do not use. +enum ErrorCode { + SHRPX_ERR_SUCCESS = 0, + SHRPX_ERR_ERROR = -1, + SHRPX_ERR_NETWORK = -100, + SHRPX_ERR_EOF = -101, + SHRPX_ERR_INPROGRESS = -102, +}; + +} // namespace shrpx + +#endif // SHRPX_ERROR_H diff --git a/src/shrpx_http.cc b/src/shrpx_http.cc new file mode 100644 index 0000000..c3f1aa7 --- /dev/null +++ b/src/shrpx_http.cc @@ -0,0 +1,103 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "shrpx_http.h" + +#include "shrpx_config.h" +#include "shrpx_log.h" +#include "http2.h" +#include "util.h" + +using namespace nghttp2; + +namespace shrpx { + +namespace http { + +std::string create_error_html(unsigned int status_code) { + std::string res; + res.reserve(512); + auto status = http2::get_status_string(status_code); + res += R"()"; + res += status; + res += "

        "; + res += status; + res += "

        "; + res += get_config()->server_name; + res += " at port "; + res += util::utos(get_config()->port); + res += "
        "; + return res; +} + +std::string create_via_header_value(int major, int minor) { + std::string hdrs; + hdrs += static_cast(major + '0'); + if (major < 2) { + hdrs += "."; + hdrs += static_cast(minor + '0'); + } + hdrs += " nghttpx"; + return hdrs; +} + +std::string colorizeHeaders(const char *hdrs) { + std::string nhdrs; + const char *p = strchr(hdrs, '\n'); + if (!p) { + // Not valid HTTP header + return hdrs; + } + nhdrs.append(hdrs, p + 1); + ++p; + while (1) { + const char *np = strchr(p, ':'); + if (!np) { + nhdrs.append(p); + break; + } + nhdrs += TTY_HTTP_HD; + nhdrs.append(p, np); + nhdrs += TTY_RST; + p = np; + np = strchr(p, '\n'); + if (!np) { + nhdrs.append(p); + break; + } + nhdrs.append(p, np + 1); + p = np + 1; + } + return nhdrs; +} + +ssize_t select_padding_callback(nghttp2_session *session, + const nghttp2_frame *frame, size_t max_payload, + void *user_data) { + return std::min(max_payload, frame->hd.length + get_config()->padding); +} + +} // namespace http + +} // namespace shrpx diff --git a/src/shrpx_http.h b/src/shrpx_http.h new file mode 100644 index 0000000..65dbe0e --- /dev/null +++ b/src/shrpx_http.h @@ -0,0 +1,53 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef SHRPX_HTTP_H +#define SHRPX_HTTP_H + +#include "shrpx.h" + +#include + +#include + +namespace shrpx { + +namespace http { + +std::string create_error_html(unsigned int status_code); + +std::string create_via_header_value(int major, int minor); + +// Adds ANSI color codes to HTTP headers |hdrs|. +std::string colorizeHeaders(const char *hdrs); + +ssize_t select_padding_callback(nghttp2_session *session, + const nghttp2_frame *frame, size_t max_payload, + void *user_data); + +} // namespace http + +} // namespace shrpx + +#endif // SHRPX_HTTP_H diff --git a/src/shrpx_http2_downstream_connection.cc b/src/shrpx_http2_downstream_connection.cc new file mode 100644 index 0000000..f5cba83 --- /dev/null +++ b/src/shrpx_http2_downstream_connection.cc @@ -0,0 +1,569 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "shrpx_http2_downstream_connection.h" + +#ifdef HAVE_UNISTD_H +#include +#endif // HAVE_UNISTD_H + +#include "http-parser/http_parser.h" + +#include "shrpx_client_handler.h" +#include "shrpx_upstream.h" +#include "shrpx_downstream.h" +#include "shrpx_config.h" +#include "shrpx_error.h" +#include "shrpx_http.h" +#include "shrpx_http2_session.h" +#include "http2.h" +#include "util.h" + +using namespace nghttp2; + +namespace shrpx { + +Http2DownstreamConnection::Http2DownstreamConnection( + DownstreamConnectionPool *dconn_pool, Http2Session *http2session) + : DownstreamConnection(dconn_pool), dlnext(nullptr), dlprev(nullptr), + http2session_(http2session), sd_(nullptr) {} + +Http2DownstreamConnection::~Http2DownstreamConnection() { + if (LOG_ENABLED(INFO)) { + DCLOG(INFO, this) << "Deleting"; + } + if (downstream_) { + downstream_->disable_downstream_rtimer(); + downstream_->disable_downstream_wtimer(); + + uint32_t error_code; + if (downstream_->get_request_state() == Downstream::STREAM_CLOSED && + downstream_->get_upgraded()) { + // For upgraded connection, send NO_ERROR. Should we consider + // request states other than Downstream::STREAM_CLOSED ? + error_code = NGHTTP2_NO_ERROR; + } else { + error_code = NGHTTP2_INTERNAL_ERROR; + } + + if (downstream_->get_downstream_stream_id() != -1) { + if (LOG_ENABLED(INFO)) { + DCLOG(INFO, this) << "Submit RST_STREAM for DOWNSTREAM:" << downstream_ + << ", stream_id=" + << downstream_->get_downstream_stream_id() + << ", error_code=" << error_code; + } + + submit_rst_stream(downstream_, error_code); + + http2session_->consume(downstream_->get_downstream_stream_id(), + downstream_->get_response_datalen()); + + downstream_->reset_response_datalen(); + + http2session_->signal_write(); + } + } + http2session_->remove_downstream_connection(this); + // Downstream and DownstreamConnection may be deleted + // asynchronously. + if (downstream_) { + downstream_->release_downstream_connection(); + } + if (LOG_ENABLED(INFO)) { + DCLOG(INFO, this) << "Deleted"; + } +} + +int Http2DownstreamConnection::attach_downstream(Downstream *downstream) { + if (LOG_ENABLED(INFO)) { + DCLOG(INFO, this) << "Attaching to DOWNSTREAM:" << downstream; + } + http2session_->add_downstream_connection(this); + if (http2session_->get_state() == Http2Session::DISCONNECTED) { + http2session_->signal_write(); + } + + downstream_ = downstream; + downstream_->reset_downstream_rtimer(); + + return 0; +} + +void Http2DownstreamConnection::detach_downstream(Downstream *downstream) { + if (LOG_ENABLED(INFO)) { + DCLOG(INFO, this) << "Detaching from DOWNSTREAM:" << downstream; + } + if (submit_rst_stream(downstream) == 0) { + http2session_->signal_write(); + } + + if (downstream_->get_downstream_stream_id() != -1) { + http2session_->consume(downstream_->get_downstream_stream_id(), + downstream_->get_response_datalen()); + + downstream_->reset_response_datalen(); + + http2session_->signal_write(); + } + + downstream->disable_downstream_rtimer(); + downstream->disable_downstream_wtimer(); + downstream_ = nullptr; +} + +int Http2DownstreamConnection::submit_rst_stream(Downstream *downstream, + uint32_t error_code) { + int rv = -1; + if (http2session_->get_state() == Http2Session::CONNECTED && + downstream->get_downstream_stream_id() != -1) { + switch (downstream->get_response_state()) { + case Downstream::MSG_RESET: + case Downstream::MSG_BAD_HEADER: + case Downstream::MSG_COMPLETE: + break; + default: + if (LOG_ENABLED(INFO)) { + DCLOG(INFO, this) << "Submit RST_STREAM for DOWNSTREAM:" << downstream + << ", stream_id=" + << downstream->get_downstream_stream_id(); + } + rv = http2session_->submit_rst_stream( + downstream->get_downstream_stream_id(), error_code); + } + } + return rv; +} + +namespace { +ssize_t http2_data_read_callback(nghttp2_session *session, int32_t stream_id, + uint8_t *buf, size_t length, + uint32_t *data_flags, + nghttp2_data_source *source, void *user_data) { + int rv; + auto sd = static_cast( + nghttp2_session_get_stream_user_data(session, stream_id)); + if (!sd || !sd->dconn) { + return NGHTTP2_ERR_DEFERRED; + } + auto dconn = static_cast(source->ptr); + auto downstream = dconn->get_downstream(); + if (!downstream) { + // In this case, RST_STREAM should have been issued. But depending + // on the priority, DATA frame may come first. + return NGHTTP2_ERR_DEFERRED; + } + auto input = downstream->get_request_buf(); + auto nread = input->remove(buf, length); + auto input_empty = input->rleft() == 0; + + if (nread > 0) { + // This is important because it will handle flow control + // stuff. + if (downstream->get_upstream()->resume_read(SHRPX_NO_BUFFER, downstream, + nread) != 0) { + // In this case, downstream may be deleted. + return NGHTTP2_ERR_CALLBACK_FAILURE; + } + + // Check dconn is still alive because Upstream::resume_read() + // may delete downstream which will delete dconn. + if (sd->dconn == nullptr) { + return NGHTTP2_ERR_DEFERRED; + } + } + + if (input_empty && + downstream->get_request_state() == Downstream::MSG_COMPLETE && + // If connection is upgraded, don't set EOF flag, since HTTP/1 + // will set MSG_COMPLETE to request state after upgrade response + // header is seen. + (!downstream->get_upgrade_request() || + (downstream->get_response_state() == Downstream::HEADER_COMPLETE && + !downstream->get_upgraded()))) { + + *data_flags |= NGHTTP2_DATA_FLAG_EOF; + + auto &trailers = downstream->get_request_trailers(); + if (!trailers.empty()) { + std::vector nva; + nva.reserve(trailers.size()); + http2::copy_headers_to_nva(nva, trailers); + if (!nva.empty()) { + rv = nghttp2_submit_trailer(session, stream_id, nva.data(), nva.size()); + if (rv != 0) { + if (nghttp2_is_fatal(rv)) { + return NGHTTP2_ERR_CALLBACK_FAILURE; + } + } else { + *data_flags |= NGHTTP2_DATA_FLAG_NO_END_STREAM; + } + } + } + } + + if (!input_empty) { + downstream->reset_downstream_wtimer(); + } else { + downstream->disable_downstream_wtimer(); + } + + if (nread == 0 && (*data_flags & NGHTTP2_DATA_FLAG_EOF) == 0) { + downstream->disable_downstream_wtimer(); + + return NGHTTP2_ERR_DEFERRED; + } + + return nread; +} +} // namespace + +int Http2DownstreamConnection::push_request_headers() { + int rv; + if (!downstream_) { + return 0; + } + if (!http2session_->can_push_request()) { + // The HTTP2 session to the backend has not been established or + // connection is now being checked. This function will be called + // again just after it is established. + downstream_->set_request_pending(true); + http2session_->start_checking_connection(); + return 0; + } + + downstream_->set_request_pending(false); + + auto no_host_rewrite = get_config()->no_host_rewrite || + get_config()->http2_proxy || + get_config()->client_proxy || + downstream_->get_request_method() == "CONNECT"; + + // http2session_ has already in CONNECTED state, so we can get + // addr_idx here. + auto addr_idx = http2session_->get_addr_idx(); + auto downstream_hostport = + get_config()->downstream_addrs[addr_idx].hostport.get(); + + const char *authority = nullptr, *host = nullptr; + if (!no_host_rewrite) { + if (!downstream_->get_request_http2_authority().empty()) { + authority = downstream_hostport; + } + if (downstream_->get_request_header(http2::HD_HOST)) { + host = downstream_hostport; + } + } else { + if (!downstream_->get_request_http2_authority().empty()) { + authority = downstream_->get_request_http2_authority().c_str(); + } + auto h = downstream_->get_request_header(http2::HD_HOST); + if (h) { + host = h->value.c_str(); + } + } + + if (!authority && !host) { + // upstream is HTTP/1.0. We use backend server's host + // nonetheless. + host = downstream_hostport; + } + + if (authority) { + downstream_->set_request_downstream_host(authority); + } else { + downstream_->set_request_downstream_host(host); + } + + size_t nheader = downstream_->get_request_headers().size(); + + Headers cookies; + if (!get_config()->http2_no_cookie_crumbling) { + cookies = downstream_->crumble_request_cookie(); + } + + // 8 means: + // 1. :method + // 2. :scheme + // 3. :path + // 4. :authority or host (at least either of them exists) + // 5. via (optional) + // 6. x-forwarded-for (optional) + // 7. x-forwarded-proto (optional) + // 8. te (optional) + auto nva = std::vector(); + nva.reserve(nheader + 8 + cookies.size()); + + std::string via_value; + std::string xff_value; + std::string scheme, uri_authority, path, query; + + nva.push_back( + http2::make_nv_ls(":method", downstream_->get_request_method())); + + if (downstream_->get_request_method() == "CONNECT") { + if (authority) { + nva.push_back(http2::make_nv_lc(":authority", authority)); + } else { + nva.push_back( + http2::make_nv_ls(":authority", downstream_->get_request_path())); + } + } else { + if (!downstream_->get_request_http2_scheme().empty()) { + nva.push_back(http2::make_nv_ls(":scheme", + downstream_->get_request_http2_scheme())); + } else if (client_handler_->get_ssl()) { + nva.push_back(http2::make_nv_ll(":scheme", "https")); + } else { + nva.push_back(http2::make_nv_ll(":scheme", "http")); + } + + if (authority) { + nva.push_back(http2::make_nv_lc(":authority", authority)); + } + + nva.push_back(http2::make_nv_ls(":path", downstream_->get_request_path())); + } + + // only emit host header field if :authority is not emitted. They + // both must be the same value. + if (!authority && host) { + nva.push_back(http2::make_nv_lc("host", host)); + } + + http2::copy_headers_to_nva(nva, downstream_->get_request_headers()); + + bool chunked_encoding = false; + auto transfer_encoding = + downstream_->get_request_header(http2::HD_TRANSFER_ENCODING); + if (transfer_encoding && + util::strieq_l("chunked", (*transfer_encoding).value)) { + chunked_encoding = true; + } + + for (auto &nv : cookies) { + nva.push_back(http2::make_nv(nv.name, nv.value, nv.no_index)); + } + + auto xff = downstream_->get_request_header(http2::HD_X_FORWARDED_FOR); + if (get_config()->add_x_forwarded_for) { + if (xff && !get_config()->strip_incoming_x_forwarded_for) { + xff_value = (*xff).value; + xff_value += ", "; + } + xff_value += + downstream_->get_upstream()->get_client_handler()->get_ipaddr(); + nva.push_back(http2::make_nv_ls("x-forwarded-for", xff_value)); + } else if (xff && !get_config()->strip_incoming_x_forwarded_for) { + nva.push_back(http2::make_nv_ls("x-forwarded-for", (*xff).value)); + } + + if (!get_config()->http2_proxy && !get_config()->client_proxy && + downstream_->get_request_method() != "CONNECT") { + // We use same protocol with :scheme header field + if (scheme.empty()) { + if (client_handler_->get_ssl()) { + nva.push_back(http2::make_nv_ll("x-forwarded-proto", "https")); + } else { + nva.push_back(http2::make_nv_ll("x-forwarded-proto", "http")); + } + } else { + nva.push_back(http2::make_nv_ls("x-forwarded-proto", scheme)); + } + } + + auto via = downstream_->get_request_header(http2::HD_VIA); + if (get_config()->no_via) { + if (via) { + nva.push_back(http2::make_nv_ls("via", (*via).value)); + } + } else { + if (via) { + via_value = (*via).value; + via_value += ", "; + } + via_value += http::create_via_header_value( + downstream_->get_request_major(), downstream_->get_request_minor()); + nva.push_back(http2::make_nv_ls("via", via_value)); + } + + auto te = downstream_->get_request_header(http2::HD_TE); + // HTTP/1 upstream request can contain keyword other than + // "trailers". We just forward "trailers". + // TODO more strict handling required here. + if (te && util::strifind(te->value.c_str(), "trailers")) { + nva.push_back(http2::make_nv_ll("te", "trailers")); + } + + if (LOG_ENABLED(INFO)) { + std::stringstream ss; + for (auto &nv : nva) { + ss << TTY_HTTP_HD << nv.name << TTY_RST << ": " << nv.value << "\n"; + } + DCLOG(INFO, this) << "HTTP request headers\n" << ss.str(); + } + + auto content_length = + downstream_->get_request_header(http2::HD_CONTENT_LENGTH); + // TODO check content-length: 0 case + + if (downstream_->get_request_method() == "CONNECT" || chunked_encoding || + content_length || downstream_->get_request_http2_expect_body()) { + // Request-body is expected. + nghttp2_data_provider data_prd; + data_prd.source.ptr = this; + data_prd.read_callback = http2_data_read_callback; + rv = http2session_->submit_request(this, downstream_->get_priority(), + nva.data(), nva.size(), &data_prd); + } else { + rv = http2session_->submit_request(this, downstream_->get_priority(), + nva.data(), nva.size(), nullptr); + } + if (rv != 0) { + DCLOG(FATAL, this) << "nghttp2_submit_request() failed"; + return -1; + } + + downstream_->reset_downstream_wtimer(); + + http2session_->signal_write(); + return 0; +} + +int Http2DownstreamConnection::push_upload_data_chunk(const uint8_t *data, + size_t datalen) { + int rv; + auto output = downstream_->get_request_buf(); + output->append(data, datalen); + if (downstream_->get_downstream_stream_id() != -1) { + rv = http2session_->resume_data(this); + if (rv != 0) { + return -1; + } + + downstream_->ensure_downstream_wtimer(); + + http2session_->signal_write(); + } + return 0; +} + +int Http2DownstreamConnection::end_upload_data() { + int rv; + if (downstream_->get_downstream_stream_id() != -1) { + rv = http2session_->resume_data(this); + if (rv != 0) { + return -1; + } + + downstream_->ensure_downstream_wtimer(); + + http2session_->signal_write(); + } + return 0; +} + +int Http2DownstreamConnection::resume_read(IOCtrlReason reason, + size_t consumed) { + int rv; + + if (http2session_->get_state() != Http2Session::CONNECTED || + !http2session_->get_flow_control()) { + return 0; + } + + if (!downstream_ || downstream_->get_downstream_stream_id() == -1) { + return 0; + } + + if (consumed > 0) { + assert(downstream_->get_response_datalen() >= consumed); + + rv = http2session_->consume(downstream_->get_downstream_stream_id(), + consumed); + + if (rv != 0) { + return -1; + } + + downstream_->dec_response_datalen(consumed); + + http2session_->signal_write(); + } + + return 0; +} + +int Http2DownstreamConnection::on_read() { return 0; } + +int Http2DownstreamConnection::on_write() { return 0; } + +void Http2DownstreamConnection::attach_stream_data(StreamData *sd) { + // It is possible sd->dconn is not NULL. sd is detached when + // on_stream_close_callback. Before that, after MSG_COMPLETE is set + // to Downstream::set_response_state(), upstream's readcb is called + // and execution path eventually could reach here. Since the + // response was already handled, we just detach sd. + detach_stream_data(); + sd_ = sd; + sd_->dconn = this; +} + +StreamData *Http2DownstreamConnection::detach_stream_data() { + if (sd_) { + auto sd = sd_; + sd_ = nullptr; + sd->dconn = nullptr; + return sd; + } + return nullptr; +} + +int Http2DownstreamConnection::on_priority_change(int32_t pri) { + int rv; + if (downstream_->get_priority() == pri) { + return 0; + } + downstream_->set_priority(pri); + if (http2session_->get_state() != Http2Session::CONNECTED) { + return 0; + } + rv = http2session_->submit_priority(this, pri); + if (rv != 0) { + DLOG(FATAL, this) << "nghttp2_submit_priority() failed"; + return -1; + } + http2session_->signal_write(); + return 0; +} + +int Http2DownstreamConnection::on_timeout() { + if (!downstream_) { + return 0; + } + + return submit_rst_stream(downstream_, NGHTTP2_NO_ERROR); +} + +} // namespace shrpx diff --git a/src/shrpx_http2_downstream_connection.h b/src/shrpx_http2_downstream_connection.h new file mode 100644 index 0000000..a2a78a3 --- /dev/null +++ b/src/shrpx_http2_downstream_connection.h @@ -0,0 +1,86 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef SHRPX_HTTP2_DOWNSTREAM_CONNECTION_H +#define SHRPX_HTTP2_DOWNSTREAM_CONNECTION_H + +#include "shrpx.h" + +#include + +#include + +#include "shrpx_downstream_connection.h" + +namespace shrpx { + +struct StreamData; +class Http2Session; +class DownstreamConnectionPool; + +class Http2DownstreamConnection : public DownstreamConnection { +public: + Http2DownstreamConnection(DownstreamConnectionPool *dconn_pool, + Http2Session *http2session); + virtual ~Http2DownstreamConnection(); + virtual int attach_downstream(Downstream *downstream); + virtual void detach_downstream(Downstream *downstream); + + virtual int push_request_headers(); + virtual int push_upload_data_chunk(const uint8_t *data, size_t datalen); + virtual int end_upload_data(); + + virtual void pause_read(IOCtrlReason reason) {} + virtual int resume_read(IOCtrlReason reason, size_t consumed); + virtual void force_resume_read() {} + + virtual int on_read(); + virtual int on_write(); + virtual int on_timeout(); + + virtual void on_upstream_change(Upstream *upstream) {} + virtual int on_priority_change(int32_t pri); + + // This object is not poolable because we dont' have facility to + // migrate to another Http2Session object. + virtual bool poolable() const { return false; } + + int send(); + + void attach_stream_data(StreamData *sd); + StreamData *detach_stream_data(); + + int submit_rst_stream(Downstream *downstream, + uint32_t error_code = NGHTTP2_INTERNAL_ERROR); + + Http2DownstreamConnection *dlnext, *dlprev; + +private: + Http2Session *http2session_; + StreamData *sd_; +}; + +} // namespace shrpx + +#endif // SHRPX_HTTP2_DOWNSTREAM_CONNECTION_H diff --git a/src/shrpx_http2_session.cc b/src/shrpx_http2_session.cc new file mode 100644 index 0000000..c071a72 --- /dev/null +++ b/src/shrpx_http2_session.cc @@ -0,0 +1,1747 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "shrpx_http2_session.h" + +#include +#ifdef HAVE_UNISTD_H +#include +#endif // HAVE_UNISTD_H + +#include + +#include + +#include "shrpx_upstream.h" +#include "shrpx_downstream.h" +#include "shrpx_config.h" +#include "shrpx_error.h" +#include "shrpx_http2_downstream_connection.h" +#include "shrpx_client_handler.h" +#include "shrpx_ssl.h" +#include "shrpx_http.h" +#include "shrpx_worker.h" +#include "shrpx_connect_blocker.h" +#include "http2.h" +#include "util.h" +#include "base64.h" + +using namespace nghttp2; + +namespace shrpx { + +namespace { +const ev_tstamp CONNCHK_TIMEOUT = 5.; +const ev_tstamp CONNCHK_PING_TIMEOUT = 1.; +} // namespace + +namespace { +void connchk_timeout_cb(struct ev_loop *loop, ev_timer *w, int revents) { + auto http2session = static_cast(w->data); + + ev_timer_stop(loop, w); + + switch (http2session->get_connection_check_state()) { + case Http2Session::CONNECTION_CHECK_STARTED: + // ping timeout; disconnect + if (LOG_ENABLED(INFO)) { + SSLOG(INFO, http2session) << "ping timeout"; + } + http2session->disconnect(); + return; + default: + if (LOG_ENABLED(INFO)) { + SSLOG(INFO, http2session) << "connection check required"; + } + http2session->set_connection_check_state( + Http2Session::CONNECTION_CHECK_REQUIRED); + } +} +} // namespace + +namespace { +void settings_timeout_cb(struct ev_loop *loop, ev_timer *w, int revents) { + auto http2session = static_cast(w->data); + http2session->stop_settings_timer(); + SSLOG(INFO, http2session) << "SETTINGS timeout"; + if (http2session->terminate_session(NGHTTP2_SETTINGS_TIMEOUT) != 0) { + http2session->disconnect(); + return; + } + http2session->signal_write(); +} +} // namespace + +namespace { +void timeoutcb(struct ev_loop *loop, ev_timer *w, int revents) { + auto conn = static_cast(w->data); + auto http2session = static_cast(conn->data); + + if (LOG_ENABLED(INFO)) { + SSLOG(INFO, http2session) << "Timeout"; + } + + http2session->disconnect(http2session->get_state() == + Http2Session::CONNECTING); +} +} // namespace + +namespace { +void readcb(struct ev_loop *loop, ev_io *w, int revents) { + int rv; + auto conn = static_cast(w->data); + auto http2session = static_cast(conn->data); + rv = http2session->do_read(); + if (rv != 0) { + http2session->disconnect(http2session->should_hard_fail()); + return; + } + http2session->connection_alive(); + + rv = http2session->do_write(); + if (rv != 0) { + http2session->disconnect(http2session->should_hard_fail()); + return; + } +} +} // namespace + +namespace { +void writecb(struct ev_loop *loop, ev_io *w, int revents) { + int rv; + auto conn = static_cast(w->data); + auto http2session = static_cast(conn->data); + rv = http2session->do_write(); + if (rv != 0) { + http2session->disconnect(http2session->should_hard_fail()); + return; + } + http2session->reset_connection_check_timer_if_not_checking(); +} +} // namespace + +Http2Session::Http2Session(struct ev_loop *loop, SSL_CTX *ssl_ctx, + ConnectBlocker *connect_blocker, Worker *worker) + : conn_(loop, -1, nullptr, get_config()->downstream_write_timeout, + get_config()->downstream_read_timeout, 0, 0, 0, 0, writecb, readcb, + timeoutcb, this), + worker_(worker), connect_blocker_(connect_blocker), ssl_ctx_(ssl_ctx), + session_(nullptr), data_pending_(nullptr), data_pendinglen_(0), + addr_idx_(0), state_(DISCONNECTED), + connection_check_state_(CONNECTION_CHECK_NONE), flow_control_(false) { + + read_ = write_ = &Http2Session::noop; + on_read_ = on_write_ = &Http2Session::noop; + + // We will resuse this many times, so use repeat timeout value. The + // timeout value is set later. + ev_timer_init(&connchk_timer_, connchk_timeout_cb, 0., 0.); + + connchk_timer_.data = this; + + // SETTINGS ACK timeout is 10 seconds for now. We will resuse this + // many times, so use repeat timeout value. + ev_timer_init(&settings_timer_, settings_timeout_cb, 0., 10.); + + settings_timer_.data = this; +} + +Http2Session::~Http2Session() { disconnect(); } + +int Http2Session::disconnect(bool hard) { + if (LOG_ENABLED(INFO)) { + SSLOG(INFO, this) << "Disconnecting"; + } + nghttp2_session_del(session_); + session_ = nullptr; + + rb_.reset(); + wb_.reset(); + + conn_.rlimit.stopw(); + conn_.wlimit.stopw(); + + ev_timer_stop(conn_.loop, &settings_timer_); + ev_timer_stop(conn_.loop, &connchk_timer_); + + read_ = write_ = &Http2Session::noop; + on_read_ = on_write_ = &Http2Session::noop; + + conn_.disconnect(); + + addr_idx_ = 0; + + if (proxy_htp_) { + proxy_htp_.reset(); + } + + connection_check_state_ = CONNECTION_CHECK_NONE; + state_ = DISCONNECTED; + + // Delete all client handler associated to Downstream. When deleting + // Http2DownstreamConnection, it calls this object's + // remove_downstream_connection(). The multiple + // Http2DownstreamConnection objects belong to the same + // ClientHandler object. So first dump ClientHandler objects. We + // want to allow creating new pending Http2DownstreamConnection with + // this object. In order to achieve this, we first swap dconns_ and + // streams_. Upstream::on_downstream_reset() may add + // Http2DownstreamConnection. + auto dconns = std::move(dconns_); + auto streams = std::move(streams_); + + std::set handlers; + for (auto dc = dconns.head; dc; dc = dc->dlnext) { + if (!dc->get_client_handler()) { + continue; + } + handlers.insert(dc->get_client_handler()); + } + for (auto h : handlers) { + if (h->get_upstream()->on_downstream_reset(hard) != 0) { + delete h; + } + } + + for (auto s = streams.head; s;) { + auto next = s->dlnext; + delete s; + s = next; + } + + return 0; +} + +int Http2Session::check_cert() { return ssl::check_cert(conn_.tls.ssl); } + +int Http2Session::initiate_connection() { + int rv = 0; + + if (state_ == DISCONNECTED) { + if (connect_blocker_->blocked()) { + if (LOG_ENABLED(INFO)) { + DCLOG(INFO, this) + << "Downstream connection was blocked by connect_blocker"; + } + return -1; + } + + auto worker_stat = worker_->get_worker_stat(); + addr_idx_ = worker_stat->next_downstream; + ++worker_stat->next_downstream; + worker_stat->next_downstream %= get_config()->downstream_addrs.size(); + + if (LOG_ENABLED(INFO)) { + SSLOG(INFO, this) << "Using downstream address idx=" << addr_idx_ + << " out of " << get_config()->downstream_addrs.size(); + } + } + + auto &downstream_addr = get_config()->downstream_addrs[addr_idx_]; + + if (get_config()->downstream_http_proxy_host && state_ == DISCONNECTED) { + if (LOG_ENABLED(INFO)) { + SSLOG(INFO, this) << "Connecting to the proxy " + << get_config()->downstream_http_proxy_host.get() << ":" + << get_config()->downstream_http_proxy_port; + } + + conn_.fd = util::create_nonblock_socket( + get_config()->downstream_http_proxy_addr.storage.ss_family); + + if (conn_.fd == -1) { + connect_blocker_->on_failure(); + return -1; + } + + rv = connect(conn_.fd, &get_config()->downstream_http_proxy_addr.sa, + get_config()->downstream_http_proxy_addrlen); + if (rv != 0 && errno != EINPROGRESS) { + SSLOG(ERROR, this) << "Failed to connect to the proxy " + << get_config()->downstream_http_proxy_host.get() + << ":" << get_config()->downstream_http_proxy_port; + connect_blocker_->on_failure(); + return -1; + } + + ev_io_set(&conn_.rev, conn_.fd, EV_READ); + ev_io_set(&conn_.wev, conn_.fd, EV_WRITE); + + conn_.wlimit.startw(); + + // TODO we should have timeout for connection establishment + ev_timer_again(conn_.loop, &conn_.wt); + + write_ = &Http2Session::connected; + + on_read_ = &Http2Session::downstream_read_proxy; + on_write_ = &Http2Session::downstream_connect_proxy; + + proxy_htp_ = make_unique(); + http_parser_init(proxy_htp_.get(), HTTP_RESPONSE); + proxy_htp_->data = this; + + state_ = PROXY_CONNECTING; + + return 0; + } + + if (state_ == DISCONNECTED || state_ == PROXY_CONNECTED) { + if (LOG_ENABLED(INFO)) { + SSLOG(INFO, this) << "Connecting to downstream server"; + } + if (ssl_ctx_) { + // We are establishing TLS connection. + conn_.tls.ssl = SSL_new(ssl_ctx_); + if (!conn_.tls.ssl) { + SSLOG(ERROR, this) << "SSL_new() failed: " + << ERR_error_string(ERR_get_error(), NULL); + return -1; + } + + const char *sni_name = nullptr; + if (get_config()->backend_tls_sni_name) { + sni_name = get_config()->backend_tls_sni_name.get(); + } else { + sni_name = downstream_addr.host.get(); + } + + if (sni_name && !util::numeric_host(sni_name)) { + // TLS extensions: SNI. There is no documentation about the return + // code for this function (actually this is macro wrapping SSL_ctrl + // at the time of this writing). + SSL_set_tlsext_host_name(conn_.tls.ssl, sni_name); + } + // If state_ == PROXY_CONNECTED, we has connected to the proxy + // using conn_.fd and tunnel has been established. + if (state_ == DISCONNECTED) { + assert(conn_.fd == -1); + + conn_.fd = util::create_nonblock_socket( + downstream_addr.addr.storage.ss_family); + if (conn_.fd == -1) { + connect_blocker_->on_failure(); + return -1; + } + + rv = connect(conn_.fd, + // TODO maybe not thread-safe? + const_cast(&downstream_addr.addr.sa), + downstream_addr.addrlen); + if (rv != 0 && errno != EINPROGRESS) { + connect_blocker_->on_failure(); + return -1; + } + + ev_io_set(&conn_.rev, conn_.fd, EV_READ); + ev_io_set(&conn_.wev, conn_.fd, EV_WRITE); + } + + if (SSL_set_fd(conn_.tls.ssl, conn_.fd) == 0) { + return -1; + } + + SSL_set_connect_state(conn_.tls.ssl); + } else { + if (state_ == DISCONNECTED) { + // Without TLS and proxy. + assert(conn_.fd == -1); + + conn_.fd = util::create_nonblock_socket( + downstream_addr.addr.storage.ss_family); + + if (conn_.fd == -1) { + connect_blocker_->on_failure(); + return -1; + } + + rv = connect(conn_.fd, const_cast(&downstream_addr.addr.sa), + downstream_addr.addrlen); + if (rv != 0 && errno != EINPROGRESS) { + connect_blocker_->on_failure(); + return -1; + } + + ev_io_set(&conn_.rev, conn_.fd, EV_READ); + ev_io_set(&conn_.wev, conn_.fd, EV_WRITE); + } + } + + write_ = &Http2Session::connected; + + on_write_ = &Http2Session::downstream_write; + on_read_ = &Http2Session::downstream_read; + + // We have been already connected when no TLS and proxy is used. + if (state_ != CONNECTED) { + state_ = CONNECTING; + conn_.wlimit.startw(); + // TODO we should have timeout for connection establishment + ev_timer_again(conn_.loop, &conn_.wt); + } else { + conn_.rlimit.startw(); + ev_timer_again(conn_.loop, &conn_.rt); + } + + return 0; + } + + // Unreachable + DIE(); + return 0; +} + +namespace { +int htp_hdrs_completecb(http_parser *htp) { + auto http2session = static_cast(htp->data); + + // We only read HTTP header part. If tunneling succeeds, response + // body is a different protocol (HTTP/2 in this case), we don't read + // them here. + // + // Here is a caveat: http-parser returns 1 less bytes if we pause + // here. The reason why they do this is probably they want to eat + // last 1 byte in s_headers_done state, on the other hand, this + // callback is called its previous state s_headers_almost_done. We + // will do "+ 1" to the return value to workaround this. + http_parser_pause(htp, 1); + + // We just check status code here + if (htp->status_code == 200) { + if (LOG_ENABLED(INFO)) { + SSLOG(INFO, http2session) << "Tunneling success"; + } + http2session->set_state(Http2Session::PROXY_CONNECTED); + + return 0; + } + + SSLOG(WARN, http2session) << "Tunneling failed: " << htp->status_code; + http2session->set_state(Http2Session::PROXY_FAILED); + + return 0; +} +} // namespace + +namespace { +http_parser_settings htp_hooks = { + nullptr, // http_cb on_message_begin; + nullptr, // http_data_cb on_url; + nullptr, // http_data_cb on_status; + nullptr, // http_data_cb on_header_field; + nullptr, // http_data_cb on_header_value; + htp_hdrs_completecb, // http_cb on_headers_complete; + nullptr, // http_data_cb on_body; + nullptr // http_cb on_message_complete; +}; +} // namespace + +int Http2Session::downstream_read_proxy() { + if (rb_.rleft() == 0) { + return 0; + } + + size_t nread = + http_parser_execute(proxy_htp_.get(), &htp_hooks, + reinterpret_cast(rb_.pos), rb_.rleft()); + + rb_.drain(nread); + + auto htperr = HTTP_PARSER_ERRNO(proxy_htp_.get()); + + if (htperr == HPE_PAUSED) { + switch (state_) { + case Http2Session::PROXY_CONNECTED: + // we need to increment nread by 1 since http_parser_execute() + // returns 1 less value we expect. This means taht + // rb_.pos[nread] points to \x0a (LF), which is last byte of + // empty line to terminate headers. We want to eat that byte + // here. + rb_.drain(1); + + // Initiate SSL/TLS handshake through established tunnel. + if (initiate_connection() != 0) { + return -1; + } + return 0; + case Http2Session::PROXY_FAILED: + return -1; + } + // should not be here + assert(0); + } + + if (htperr != HPE_OK) { + return -1; + } + + return 0; +} + +int Http2Session::downstream_connect_proxy() { + if (LOG_ENABLED(INFO)) { + SSLOG(INFO, this) << "Connected to the proxy"; + } + auto &downstream_addr = get_config()->downstream_addrs[addr_idx_]; + + std::string req = "CONNECT "; + req += downstream_addr.hostport.get(); + if (downstream_addr.port == 80 || downstream_addr.port == 443) { + req += ":"; + req += util::utos(downstream_addr.port); + } + req += " HTTP/1.1\r\nHost: "; + req += downstream_addr.host.get(); + req += "\r\n"; + if (get_config()->downstream_http_proxy_userinfo) { + req += "Proxy-Authorization: Basic "; + size_t len = strlen(get_config()->downstream_http_proxy_userinfo.get()); + req += base64::encode(get_config()->downstream_http_proxy_userinfo.get(), + get_config()->downstream_http_proxy_userinfo.get() + + len); + req += "\r\n"; + } + req += "\r\n"; + if (LOG_ENABLED(INFO)) { + SSLOG(INFO, this) << "HTTP proxy request headers\n" << req; + } + auto nwrite = wb_.write(req.c_str(), req.size()); + if (nwrite != req.size()) { + SSLOG(WARN, this) << "HTTP proxy request is too large"; + return -1; + } + on_write_ = &Http2Session::noop; + + signal_write(); + return 0; +} + +void Http2Session::add_downstream_connection(Http2DownstreamConnection *dconn) { + dconns_.append(dconn); +} + +void +Http2Session::remove_downstream_connection(Http2DownstreamConnection *dconn) { + dconns_.remove(dconn); + dconn->detach_stream_data(); +} + +void Http2Session::remove_stream_data(StreamData *sd) { + streams_.remove(sd); + if (sd->dconn) { + sd->dconn->detach_stream_data(); + } + delete sd; +} + +int Http2Session::submit_request(Http2DownstreamConnection *dconn, int32_t pri, + const nghttp2_nv *nva, size_t nvlen, + const nghttp2_data_provider *data_prd) { + assert(state_ == CONNECTED); + auto sd = make_unique(); + sd->dlnext = sd->dlprev = nullptr; + // TODO Specify nullptr to pri_spec for now + auto stream_id = + nghttp2_submit_request(session_, nullptr, nva, nvlen, data_prd, sd.get()); + if (stream_id < 0) { + SSLOG(FATAL, this) << "nghttp2_submit_request() failed: " + << nghttp2_strerror(stream_id); + return -1; + } + + dconn->attach_stream_data(sd.get()); + dconn->get_downstream()->set_downstream_stream_id(stream_id); + streams_.append(sd.release()); + + return 0; +} + +int Http2Session::submit_rst_stream(int32_t stream_id, uint32_t error_code) { + assert(state_ == CONNECTED); + if (LOG_ENABLED(INFO)) { + SSLOG(INFO, this) << "RST_STREAM stream_id=" << stream_id + << " with error_code=" << error_code; + } + int rv = nghttp2_submit_rst_stream(session_, NGHTTP2_FLAG_NONE, stream_id, + error_code); + if (rv != 0) { + SSLOG(FATAL, this) << "nghttp2_submit_rst_stream() failed: " + << nghttp2_strerror(rv); + return -1; + } + return 0; +} + +int Http2Session::submit_priority(Http2DownstreamConnection *dconn, + int32_t pri) { + assert(state_ == CONNECTED); + if (!dconn) { + return 0; + } + int rv; + + // TODO Disabled temporarily + + // rv = nghttp2_submit_priority(session_, NGHTTP2_FLAG_NONE, + // dconn->get_downstream()-> + // get_downstream_stream_id(), pri); + + rv = 0; + + if (rv < NGHTTP2_ERR_FATAL) { + SSLOG(FATAL, this) << "nghttp2_submit_priority() failed: " + << nghttp2_strerror(rv); + return -1; + } + return 0; +} + +nghttp2_session *Http2Session::get_session() const { return session_; } + +bool Http2Session::get_flow_control() const { return flow_control_; } + +int Http2Session::resume_data(Http2DownstreamConnection *dconn) { + assert(state_ == CONNECTED); + auto downstream = dconn->get_downstream(); + int rv = nghttp2_session_resume_data(session_, + downstream->get_downstream_stream_id()); + switch (rv) { + case 0: + case NGHTTP2_ERR_INVALID_ARGUMENT: + return 0; + default: + SSLOG(FATAL, this) << "nghttp2_resume_session() failed: " + << nghttp2_strerror(rv); + return -1; + } +} + +namespace { +void call_downstream_readcb(Http2Session *http2session, + Downstream *downstream) { + auto upstream = downstream->get_upstream(); + if (!upstream) { + return; + } + if (upstream->downstream_read(downstream->get_downstream_connection()) != 0) { + delete upstream->get_client_handler(); + } +} +} // namespace + +namespace { +int on_stream_close_callback(nghttp2_session *session, int32_t stream_id, + uint32_t error_code, void *user_data) { + auto http2session = static_cast(user_data); + if (LOG_ENABLED(INFO)) { + SSLOG(INFO, http2session) << "Stream stream_id=" << stream_id + << " is being closed with error code " + << error_code; + } + auto sd = static_cast( + nghttp2_session_get_stream_user_data(session, stream_id)); + if (sd == 0) { + // We might get this close callback when pushed streams are + // closed. + return 0; + } + auto dconn = sd->dconn; + if (dconn) { + auto downstream = dconn->get_downstream(); + if (downstream && downstream->get_downstream_stream_id() == stream_id) { + + if (downstream->get_upgraded() && + downstream->get_response_state() == Downstream::HEADER_COMPLETE) { + // For tunneled connection, we have to submit RST_STREAM to + // upstream *after* whole response body is sent. We just set + // MSG_COMPLETE here. Upstream will take care of that. + downstream->get_upstream()->on_downstream_body_complete(downstream); + downstream->set_response_state(Downstream::MSG_COMPLETE); + } else if (error_code == NGHTTP2_NO_ERROR) { + switch (downstream->get_response_state()) { + case Downstream::MSG_COMPLETE: + case Downstream::MSG_BAD_HEADER: + break; + default: + downstream->set_response_state(Downstream::MSG_RESET); + } + } else if (downstream->get_response_state() != + Downstream::MSG_BAD_HEADER) { + downstream->set_response_state(Downstream::MSG_RESET); + } + if (downstream->get_response_state() == Downstream::MSG_RESET && + downstream->get_response_rst_stream_error_code() == + NGHTTP2_NO_ERROR) { + downstream->set_response_rst_stream_error_code(error_code); + } + call_downstream_readcb(http2session, downstream); + // dconn may be deleted + } + } + // The life time of StreamData ends here + http2session->remove_stream_data(sd); + return 0; +} +} // namespace + +void Http2Session::start_settings_timer() { + ev_timer_again(conn_.loop, &settings_timer_); +} + +void Http2Session::stop_settings_timer() { + ev_timer_stop(conn_.loop, &settings_timer_); +} + +namespace { +int on_header_callback(nghttp2_session *session, const nghttp2_frame *frame, + const uint8_t *name, size_t namelen, + const uint8_t *value, size_t valuelen, uint8_t flags, + void *user_data) { + auto sd = static_cast( + nghttp2_session_get_stream_user_data(session, frame->hd.stream_id)); + if (!sd || !sd->dconn) { + return 0; + } + auto downstream = sd->dconn->get_downstream(); + if (!downstream) { + return 0; + } + + if (frame->hd.type != NGHTTP2_HEADERS) { + return 0; + } + + auto trailer = frame->headers.cat != NGHTTP2_HCAT_RESPONSE && + !downstream->get_expect_final_response(); + + if (downstream->get_response_headers_sum() + namelen + valuelen > + get_config()->header_field_buffer || + downstream->get_response_headers().size() >= + get_config()->max_header_fields) { + if (LOG_ENABLED(INFO)) { + DLOG(INFO, downstream) + << "Too large or many header field size=" + << downstream->get_response_headers_sum() + namelen + valuelen + << ", num=" << downstream->get_response_headers().size() + 1; + } + + if (trailer) { + // we don't care trailer part exceeds header size limit; just + // discard it. + return 0; + } + + return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; + } + + if (trailer) { + // just store header fields for trailer part + downstream->add_response_trailer(name, namelen, value, valuelen, + flags & NGHTTP2_NV_FLAG_NO_INDEX, -1); + return 0; + } + + auto token = http2::lookup_token(name, namelen); + + downstream->add_response_header(name, namelen, value, valuelen, + flags & NGHTTP2_NV_FLAG_NO_INDEX, token); + return 0; +} +} // namespace + +namespace { +int on_begin_headers_callback(nghttp2_session *session, + const nghttp2_frame *frame, void *user_data) { + auto http2session = static_cast(user_data); + if (frame->hd.type != NGHTTP2_HEADERS || + frame->headers.cat != NGHTTP2_HCAT_RESPONSE) { + return 0; + } + auto sd = static_cast( + nghttp2_session_get_stream_user_data(session, frame->hd.stream_id)); + if (!sd || !sd->dconn) { + http2session->submit_rst_stream(frame->hd.stream_id, + NGHTTP2_INTERNAL_ERROR); + return 0; + } + auto downstream = sd->dconn->get_downstream(); + if (!downstream || + downstream->get_downstream_stream_id() != frame->hd.stream_id) { + http2session->submit_rst_stream(frame->hd.stream_id, + NGHTTP2_INTERNAL_ERROR); + return 0; + } + return 0; +} +} // namespace + +namespace { +int on_response_headers(Http2Session *http2session, Downstream *downstream, + nghttp2_session *session, const nghttp2_frame *frame) { + int rv; + + auto upstream = downstream->get_upstream(); + + auto &nva = downstream->get_response_headers(); + + downstream->set_expect_final_response(false); + + auto status = downstream->get_response_header(http2::HD__STATUS); + // libnghttp2 guarantees this exists and can be parsed + auto status_code = http2::parse_http_status_code(status->value); + + downstream->set_response_http_status(status_code); + downstream->set_response_major(2); + downstream->set_response_minor(0); + + if (LOG_ENABLED(INFO)) { + std::stringstream ss; + for (auto &nv : nva) { + ss << TTY_HTTP_HD << nv.name << TTY_RST << ": " << nv.value << "\n"; + } + SSLOG(INFO, http2session) + << "HTTP response headers. stream_id=" << frame->hd.stream_id << "\n" + << ss.str(); + } + + if (downstream->get_non_final_response()) { + + if (LOG_ENABLED(INFO)) { + SSLOG(INFO, http2session) << "This is non-final response."; + } + + downstream->set_expect_final_response(true); + rv = upstream->on_downstream_header_complete(downstream); + + // Now Dowstream's response headers are erased. + + if (rv != 0) { + http2session->submit_rst_stream(frame->hd.stream_id, + NGHTTP2_PROTOCOL_ERROR); + downstream->set_response_state(Downstream::MSG_RESET); + } + + return 0; + } + + downstream->set_response_state(Downstream::HEADER_COMPLETE); + downstream->check_upgrade_fulfilled(); + + if (downstream->get_upgraded()) { + downstream->set_response_connection_close(true); + // On upgrade sucess, both ends can send data + if (upstream->resume_read(SHRPX_NO_BUFFER, downstream, 0) != 0) { + // If resume_read fails, just drop connection. Not ideal. + delete upstream->get_client_handler(); + return -1; + } + downstream->set_request_state(Downstream::HEADER_COMPLETE); + if (LOG_ENABLED(INFO)) { + SSLOG(INFO, http2session) + << "HTTP upgrade success. stream_id=" << frame->hd.stream_id; + } + } else { + auto content_length = + downstream->get_response_header(http2::HD_CONTENT_LENGTH); + if (content_length) { + // libnghttp2 guarantees this can be parsed + auto len = util::parse_uint(content_length->value); + downstream->set_response_content_length(len); + } + + if (downstream->get_response_content_length() == -1 && + downstream->expect_response_body()) { + // Here we have response body but Content-Length is not known in + // advance. + if (downstream->get_request_major() <= 0 || + (downstream->get_request_major() == 1 && + downstream->get_request_minor() == 0)) { + // We simply close connection for pre-HTTP/1.1 in this case. + downstream->set_response_connection_close(true); + } else { + // Otherwise, use chunked encoding to keep upstream connection + // open. In HTTP2, we are supporsed not to receive + // transfer-encoding. + downstream->add_response_header("transfer-encoding", "chunked", + http2::HD_TRANSFER_ENCODING); + downstream->set_chunked_response(true); + } + } + } + + rv = upstream->on_downstream_header_complete(downstream); + if (rv != 0) { + http2session->submit_rst_stream(frame->hd.stream_id, + NGHTTP2_PROTOCOL_ERROR); + downstream->set_response_state(Downstream::MSG_RESET); + } + + return 0; +} +} // namespace + +namespace { +int on_frame_recv_callback(nghttp2_session *session, const nghttp2_frame *frame, + void *user_data) { + int rv; + auto http2session = static_cast(user_data); + + switch (frame->hd.type) { + case NGHTTP2_DATA: { + auto sd = static_cast( + nghttp2_session_get_stream_user_data(session, frame->hd.stream_id)); + if (!sd || !sd->dconn) { + return 0; + } + auto downstream = sd->dconn->get_downstream(); + if (!downstream || + downstream->get_downstream_stream_id() != frame->hd.stream_id) { + return 0; + } + + auto upstream = downstream->get_upstream(); + rv = upstream->on_downstream_body(downstream, nullptr, 0, true); + if (rv != 0) { + http2session->submit_rst_stream(frame->hd.stream_id, + NGHTTP2_INTERNAL_ERROR); + downstream->set_response_state(Downstream::MSG_RESET); + + } else if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) { + + downstream->disable_downstream_rtimer(); + + if (downstream->get_response_state() == Downstream::HEADER_COMPLETE) { + + downstream->set_response_state(Downstream::MSG_COMPLETE); + + rv = upstream->on_downstream_body_complete(downstream); + + if (rv != 0) { + downstream->set_response_state(Downstream::MSG_RESET); + } + } + } + + call_downstream_readcb(http2session, downstream); + return 0; + } + case NGHTTP2_HEADERS: { + auto sd = static_cast( + nghttp2_session_get_stream_user_data(session, frame->hd.stream_id)); + if (!sd || !sd->dconn) { + return 0; + } + auto downstream = sd->dconn->get_downstream(); + + if (!downstream) { + return 0; + } + + if (frame->headers.cat == NGHTTP2_HCAT_RESPONSE) { + rv = on_response_headers(http2session, downstream, session, frame); + + if (rv != 0) { + return 0; + } + } else if (frame->headers.cat == NGHTTP2_HCAT_HEADERS) { + if (downstream->get_expect_final_response()) { + rv = on_response_headers(http2session, downstream, session, frame); + + if (rv != 0) { + return 0; + } + } + } + + if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) { + downstream->disable_downstream_rtimer(); + + if (downstream->get_response_state() == Downstream::HEADER_COMPLETE) { + downstream->set_response_state(Downstream::MSG_COMPLETE); + + auto upstream = downstream->get_upstream(); + + rv = upstream->on_downstream_body_complete(downstream); + + if (rv != 0) { + downstream->set_response_state(Downstream::MSG_RESET); + } + } + } else { + downstream->reset_downstream_rtimer(); + } + + // This may delete downstream + call_downstream_readcb(http2session, downstream); + + return 0; + } + case NGHTTP2_RST_STREAM: { + auto sd = static_cast( + nghttp2_session_get_stream_user_data(session, frame->hd.stream_id)); + if (sd && sd->dconn) { + auto downstream = sd->dconn->get_downstream(); + if (downstream && + downstream->get_downstream_stream_id() == frame->hd.stream_id) { + + downstream->set_response_rst_stream_error_code( + frame->rst_stream.error_code); + call_downstream_readcb(http2session, downstream); + } + } + return 0; + } + case NGHTTP2_SETTINGS: + if ((frame->hd.flags & NGHTTP2_FLAG_ACK) == 0) { + return 0; + } + http2session->stop_settings_timer(); + return 0; + case NGHTTP2_PING: + if (frame->hd.flags & NGHTTP2_FLAG_ACK) { + if (LOG_ENABLED(INFO)) { + LOG(INFO) << "PING ACK received"; + } + http2session->connection_alive(); + } + return 0; + case NGHTTP2_PUSH_PROMISE: + if (LOG_ENABLED(INFO)) { + SSLOG(INFO, http2session) + << "Received downstream PUSH_PROMISE stream_id=" + << frame->hd.stream_id + << ", promised_stream_id=" << frame->push_promise.promised_stream_id; + } + // We just respond with RST_STREAM. + http2session->submit_rst_stream(frame->push_promise.promised_stream_id, + NGHTTP2_REFUSED_STREAM); + return 0; + default: + return 0; + } +} +} // namespace + +namespace { +int on_data_chunk_recv_callback(nghttp2_session *session, uint8_t flags, + int32_t stream_id, const uint8_t *data, + size_t len, void *user_data) { + int rv; + auto http2session = static_cast(user_data); + auto sd = static_cast( + nghttp2_session_get_stream_user_data(session, stream_id)); + if (!sd || !sd->dconn) { + http2session->submit_rst_stream(stream_id, NGHTTP2_INTERNAL_ERROR); + + if (http2session->consume(stream_id, len) != 0) { + return NGHTTP2_ERR_CALLBACK_FAILURE; + } + + return 0; + } + auto downstream = sd->dconn->get_downstream(); + if (!downstream || downstream->get_downstream_stream_id() != stream_id || + !downstream->expect_response_body()) { + + http2session->submit_rst_stream(stream_id, NGHTTP2_INTERNAL_ERROR); + + if (http2session->consume(stream_id, len) != 0) { + return NGHTTP2_ERR_CALLBACK_FAILURE; + } + + return 0; + } + + // We don't want DATA after non-final response, which is illegal in + // HTTP. + if (downstream->get_non_final_response()) { + http2session->submit_rst_stream(stream_id, NGHTTP2_PROTOCOL_ERROR); + + if (http2session->consume(stream_id, len) != 0) { + return NGHTTP2_ERR_CALLBACK_FAILURE; + } + + return 0; + } + + downstream->reset_downstream_rtimer(); + + downstream->add_response_bodylen(len); + + auto upstream = downstream->get_upstream(); + rv = upstream->on_downstream_body(downstream, data, len, false); + if (rv != 0) { + http2session->submit_rst_stream(stream_id, NGHTTP2_INTERNAL_ERROR); + + if (http2session->consume(stream_id, len) != 0) { + return NGHTTP2_ERR_CALLBACK_FAILURE; + } + + downstream->set_response_state(Downstream::MSG_RESET); + } + + downstream->add_response_datalen(len); + + call_downstream_readcb(http2session, downstream); + return 0; +} +} // namespace + +namespace { +int on_frame_send_callback(nghttp2_session *session, const nghttp2_frame *frame, + void *user_data) { + auto http2session = static_cast(user_data); + + if (frame->hd.type == NGHTTP2_DATA || frame->hd.type == NGHTTP2_HEADERS) { + if ((frame->hd.flags & NGHTTP2_FLAG_END_STREAM) == 0) { + return 0; + } + + auto sd = static_cast( + nghttp2_session_get_stream_user_data(session, frame->hd.stream_id)); + + if (!sd || !sd->dconn) { + return 0; + } + + auto downstream = sd->dconn->get_downstream(); + + if (!downstream || + downstream->get_downstream_stream_id() != frame->hd.stream_id) { + return 0; + } + + downstream->reset_downstream_rtimer(); + + return 0; + } + + if (frame->hd.type == NGHTTP2_SETTINGS && + (frame->hd.flags & NGHTTP2_FLAG_ACK) == 0) { + http2session->start_settings_timer(); + } + return 0; +} +} // namespace + +namespace { +int on_frame_not_send_callback(nghttp2_session *session, + const nghttp2_frame *frame, int lib_error_code, + void *user_data) { + auto http2session = static_cast(user_data); + if (LOG_ENABLED(INFO)) { + SSLOG(INFO, http2session) << "Failed to send control frame type=" + << static_cast(frame->hd.type) + << "lib_error_code=" << lib_error_code << ":" + << nghttp2_strerror(lib_error_code); + } + if (frame->hd.type == NGHTTP2_HEADERS && + lib_error_code != NGHTTP2_ERR_STREAM_CLOSED && + lib_error_code != NGHTTP2_ERR_STREAM_CLOSING) { + // To avoid stream hanging around, flag Downstream::MSG_RESET. + auto sd = static_cast( + nghttp2_session_get_stream_user_data(session, frame->hd.stream_id)); + if (!sd) { + return 0; + } + if (!sd->dconn) { + return 0; + } + auto downstream = sd->dconn->get_downstream(); + if (!downstream || + downstream->get_downstream_stream_id() != frame->hd.stream_id) { + return 0; + } + downstream->set_response_state(Downstream::MSG_RESET); + call_downstream_readcb(http2session, downstream); + } + return 0; +} +} // namespace + +nghttp2_session_callbacks *create_http2_downstream_callbacks() { + int rv; + nghttp2_session_callbacks *callbacks; + + rv = nghttp2_session_callbacks_new(&callbacks); + + if (rv != 0) { + return nullptr; + } + + nghttp2_session_callbacks_set_on_stream_close_callback( + callbacks, on_stream_close_callback); + + nghttp2_session_callbacks_set_on_frame_recv_callback(callbacks, + on_frame_recv_callback); + + nghttp2_session_callbacks_set_on_data_chunk_recv_callback( + callbacks, on_data_chunk_recv_callback); + + nghttp2_session_callbacks_set_on_frame_send_callback(callbacks, + on_frame_send_callback); + + nghttp2_session_callbacks_set_on_frame_not_send_callback( + callbacks, on_frame_not_send_callback); + + nghttp2_session_callbacks_set_on_header_callback(callbacks, + on_header_callback); + + nghttp2_session_callbacks_set_on_begin_headers_callback( + callbacks, on_begin_headers_callback); + + if (get_config()->padding) { + nghttp2_session_callbacks_set_select_padding_callback( + callbacks, http::select_padding_callback); + } + + return callbacks; +} + +int Http2Session::connection_made() { + int rv; + + state_ = Http2Session::CONNECTED; + + if (ssl_ctx_) { + const unsigned char *next_proto = nullptr; + unsigned int next_proto_len; + SSL_get0_next_proto_negotiated(conn_.tls.ssl, &next_proto, &next_proto_len); + for (int i = 0; i < 2; ++i) { + if (next_proto) { + if (LOG_ENABLED(INFO)) { + std::string proto(next_proto, next_proto + next_proto_len); + SSLOG(INFO, this) << "Negotiated next protocol: " << proto; + } + if (!util::check_h2_is_selected(next_proto, next_proto_len)) { + return -1; + } + break; + } +#if OPENSSL_VERSION_NUMBER >= 0x10002000L + SSL_get0_alpn_selected(conn_.tls.ssl, &next_proto, &next_proto_len); +#else // OPENSSL_VERSION_NUMBER < 0x10002000L + break; +#endif // OPENSSL_VERSION_NUMBER < 0x10002000L + } + if (!next_proto) { + return -1; + } + } + + rv = nghttp2_session_client_new2(&session_, + get_config()->http2_downstream_callbacks, + this, get_config()->http2_client_option); + + if (rv != 0) { + return -1; + } + + flow_control_ = true; + + std::array entry; + entry[0].settings_id = NGHTTP2_SETTINGS_ENABLE_PUSH; + entry[0].value = 0; + entry[1].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS; + entry[1].value = get_config()->http2_max_concurrent_streams; + + entry[2].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE; + entry[2].value = (1 << get_config()->http2_downstream_window_bits) - 1; + + rv = nghttp2_submit_settings(session_, NGHTTP2_FLAG_NONE, entry.data(), + entry.size()); + if (rv != 0) { + return -1; + } + + if (get_config()->http2_downstream_connection_window_bits > 16) { + int32_t delta = + (1 << get_config()->http2_downstream_connection_window_bits) - 1 - + NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE; + rv = nghttp2_submit_window_update(session_, NGHTTP2_FLAG_NONE, 0, delta); + if (rv != 0) { + return -1; + } + } + + auto must_terminate = !get_config()->downstream_no_tls && + !ssl::check_http2_requirement(conn_.tls.ssl); + + if (must_terminate) { + rv = terminate_session(NGHTTP2_INADEQUATE_SECURITY); + + if (rv != 0) { + return -1; + } + } + + if (must_terminate) { + return 0; + } + + reset_connection_check_timer(CONNCHK_TIMEOUT); + + submit_pending_requests(); + + signal_write(); + return 0; +} + +int Http2Session::do_read() { return read_(*this); } +int Http2Session::do_write() { return write_(*this); } + +int Http2Session::on_read() { return on_read_(*this); } +int Http2Session::on_write() { return on_write_(*this); } + +int Http2Session::downstream_read() { + ssize_t rv = 0; + + if (rb_.rleft() > 0) { + rv = nghttp2_session_mem_recv( + session_, reinterpret_cast(rb_.pos), rb_.rleft()); + + if (rv < 0) { + SSLOG(ERROR, this) << "nghttp2_session_recv() returned error: " + << nghttp2_strerror(rv); + return -1; + } + + // nghttp2_session_mem_recv() should consume all input data in + // case of success. + rb_.reset(); + } + + if (nghttp2_session_want_read(session_) == 0 && + nghttp2_session_want_write(session_) == 0 && wb_.rleft() == 0) { + if (LOG_ENABLED(INFO)) { + SSLOG(INFO, this) << "No more read/write for this HTTP2 session"; + } + return -1; + } + + signal_write(); + return 0; +} + +int Http2Session::downstream_write() { + if (data_pending_) { + auto n = std::min(wb_.wleft(), data_pendinglen_); + wb_.write(data_pending_, n); + if (n < data_pendinglen_) { + data_pending_ += n; + data_pendinglen_ -= n; + return 0; + } + + data_pending_ = nullptr; + data_pendinglen_ = 0; + } + + for (;;) { + const uint8_t *data; + auto datalen = nghttp2_session_mem_send(session_, &data); + + if (datalen < 0) { + SSLOG(ERROR, this) << "nghttp2_session_mem_send() returned error: " + << nghttp2_strerror(datalen); + return -1; + } + if (datalen == 0) { + break; + } + auto n = wb_.write(data, datalen); + if (n < static_cast(datalen)) { + data_pending_ = data + n; + data_pendinglen_ = datalen - n; + return 0; + } + } + + if (nghttp2_session_want_read(session_) == 0 && + nghttp2_session_want_write(session_) == 0 && wb_.rleft() == 0) { + if (LOG_ENABLED(INFO)) { + SSLOG(INFO, this) << "No more read/write for this session"; + } + return -1; + } + + return 0; +} + +void Http2Session::signal_write() { + switch (state_) { + case Http2Session::DISCONNECTED: + if (LOG_ENABLED(INFO)) { + LOG(INFO) << "Start connecting to backend server"; + } + if (initiate_connection() != 0) { + if (LOG_ENABLED(INFO)) { + SSLOG(INFO, this) << "Could not initiate backend connection"; + } + disconnect(true); + } + break; + case Http2Session::CONNECTED: + conn_.wlimit.startw(); + break; + } +} + +struct ev_loop *Http2Session::get_loop() const { + return conn_.loop; +} + +ev_io *Http2Session::get_wev() { return &conn_.wev; } + +int Http2Session::get_state() const { return state_; } + +void Http2Session::set_state(int state) { state_ = state; } + +int Http2Session::terminate_session(uint32_t error_code) { + int rv; + rv = nghttp2_session_terminate_session(session_, error_code); + if (rv != 0) { + return -1; + } + return 0; +} + +SSL *Http2Session::get_ssl() const { return conn_.tls.ssl; } + +int Http2Session::consume(int32_t stream_id, size_t len) { + int rv; + + if (!session_) { + return 0; + } + + rv = nghttp2_session_consume(session_, stream_id, len); + + if (rv != 0) { + SSLOG(WARN, this) << "nghttp2_session_consume() returned error: " + << nghttp2_strerror(rv); + + return -1; + } + + return 0; +} + +bool Http2Session::can_push_request() const { + return state_ == CONNECTED && + connection_check_state_ == CONNECTION_CHECK_NONE; +} + +void Http2Session::start_checking_connection() { + if (state_ != CONNECTED || + connection_check_state_ != CONNECTION_CHECK_REQUIRED) { + return; + } + connection_check_state_ = CONNECTION_CHECK_STARTED; + + SSLOG(INFO, this) << "Start checking connection"; + // If connection is down, we may get error when writing data. Issue + // ping frame to see whether connection is alive. + nghttp2_submit_ping(session_, NGHTTP2_FLAG_NONE, NULL); + + // set ping timeout and start timer again + reset_connection_check_timer(CONNCHK_PING_TIMEOUT); + + signal_write(); +} + +void Http2Session::reset_connection_check_timer(ev_tstamp t) { + connchk_timer_.repeat = t; + ev_timer_again(conn_.loop, &connchk_timer_); +} + +void Http2Session::reset_connection_check_timer_if_not_checking() { + if (connection_check_state_ != CONNECTION_CHECK_NONE) { + return; + } + + reset_connection_check_timer(CONNCHK_TIMEOUT); +} + +void Http2Session::connection_alive() { + reset_connection_check_timer(CONNCHK_TIMEOUT); + + if (connection_check_state_ == CONNECTION_CHECK_NONE) { + return; + } + + if (LOG_ENABLED(INFO)) { + SSLOG(INFO, this) << "Connection alive"; + } + + connection_check_state_ = CONNECTION_CHECK_NONE; + + submit_pending_requests(); +} + +void Http2Session::submit_pending_requests() { + for (auto dconn = dconns_.head; dconn; dconn = dconn->dlnext) { + auto downstream = dconn->get_downstream(); + + if (!downstream || !downstream->request_submission_ready()) { + continue; + } + + auto upstream = downstream->get_upstream(); + + if (dconn->push_request_headers() != 0) { + if (LOG_ENABLED(INFO)) { + SSLOG(INFO, this) << "backend request failed"; + } + + upstream->on_downstream_abort_request(downstream, 400); + + continue; + } + + upstream->resume_read(SHRPX_NO_BUFFER, downstream, 0); + } +} + +void Http2Session::set_connection_check_state(int state) { + connection_check_state_ = state; +} + +int Http2Session::get_connection_check_state() const { + return connection_check_state_; +} + +int Http2Session::noop() { return 0; } + +int Http2Session::connected() { + if (!util::check_socket_connected(conn_.fd)) { + return -1; + } + + connect_blocker_->on_success(); + + if (LOG_ENABLED(INFO)) { + SSLOG(INFO, this) << "Connection established"; + } + + conn_.rlimit.startw(); + + if (conn_.tls.ssl) { + read_ = &Http2Session::tls_handshake; + write_ = &Http2Session::tls_handshake; + + return do_write(); + } + + read_ = &Http2Session::read_clear; + write_ = &Http2Session::write_clear; + + if (state_ == PROXY_CONNECTING) { + return do_write(); + } + + if (connection_made() != 0) { + state_ = CONNECT_FAILING; + return -1; + } + + return 0; +} + +int Http2Session::read_clear() { + ev_timer_again(conn_.loop, &conn_.rt); + + for (;;) { + // we should process buffered data first before we read EOF. + if (rb_.rleft() && on_read() != 0) { + return -1; + } + if (rb_.rleft()) { + return 0; + } + rb_.reset(); + + auto nread = conn_.read_clear(rb_.last, rb_.wleft()); + + if (nread == 0) { + return 0; + } + + if (nread < 0) { + return nread; + } + + rb_.write(nread); + } +} + +int Http2Session::write_clear() { + ev_timer_again(conn_.loop, &conn_.rt); + + for (;;) { + if (wb_.rleft() > 0) { + auto nwrite = conn_.write_clear(wb_.pos, wb_.rleft()); + + if (nwrite == 0) { + return 0; + } + + if (nwrite < 0) { + return nwrite; + } + + wb_.drain(nwrite); + continue; + } + + wb_.reset(); + if (on_write() != 0) { + return -1; + } + if (wb_.rleft() == 0) { + break; + } + } + + conn_.wlimit.stopw(); + ev_timer_stop(conn_.loop, &conn_.wt); + + return 0; +} + +int Http2Session::tls_handshake() { + ev_timer_again(conn_.loop, &conn_.rt); + + ERR_clear_error(); + + auto rv = conn_.tls_handshake(); + + if (rv == SHRPX_ERR_INPROGRESS) { + return 0; + } + + if (rv < 0) { + return rv; + } + + if (LOG_ENABLED(INFO)) { + SSLOG(INFO, this) << "SSL/TLS handshake completed"; + } + + if (!get_config()->downstream_no_tls && !get_config()->insecure && + check_cert() != 0) { + return -1; + } + + read_ = &Http2Session::read_tls; + write_ = &Http2Session::write_tls; + + if (connection_made() != 0) { + state_ = CONNECT_FAILING; + return -1; + } + + return 0; +} + +int Http2Session::read_tls() { + ev_timer_again(conn_.loop, &conn_.rt); + + ERR_clear_error(); + + for (;;) { + // we should process buffered data first before we read EOF. + if (rb_.rleft() && on_read() != 0) { + return -1; + } + if (rb_.rleft()) { + return 0; + } + rb_.reset(); + + auto nread = conn_.read_tls(rb_.last, rb_.wleft()); + + if (nread == 0) { + return 0; + } + + if (nread < 0) { + return nread; + } + + rb_.write(nread); + } +} + +int Http2Session::write_tls() { + ev_timer_again(conn_.loop, &conn_.rt); + + ERR_clear_error(); + + for (;;) { + if (wb_.rleft() > 0) { + auto nwrite = conn_.write_tls(wb_.pos, wb_.rleft()); + + if (nwrite == 0) { + return 0; + } + + if (nwrite < 0) { + return nwrite; + } + + wb_.drain(nwrite); + + continue; + } + wb_.reset(); + if (on_write() != 0) { + return -1; + } + if (wb_.rleft() == 0) { + break; + } + } + + conn_.wlimit.stopw(); + ev_timer_stop(conn_.loop, &conn_.wt); + + return 0; +} + +bool Http2Session::should_hard_fail() const { + switch (state_) { + case PROXY_CONNECTING: + case PROXY_FAILED: + case CONNECTING: + case CONNECT_FAILING: + return true; + default: + return false; + } +} + +size_t Http2Session::get_addr_idx() const { return addr_idx_; } + +} // namespace shrpx diff --git a/src/shrpx_http2_session.h b/src/shrpx_http2_session.h new file mode 100644 index 0000000..35d3376 --- /dev/null +++ b/src/shrpx_http2_session.h @@ -0,0 +1,219 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef SHRPX_HTTP2_SESSION_H +#define SHRPX_HTTP2_SESSION_H + +#include "shrpx.h" + +#include +#include + +#include + +#include + +#include + +#include "http-parser/http_parser.h" + +#include "shrpx_connection.h" +#include "buffer.h" +#include "template.h" + +using namespace nghttp2; + +namespace shrpx { + +class Http2DownstreamConnection; +class Worker; +class ConnectBlocker; + +struct StreamData { + StreamData *dlnext, *dlprev; + Http2DownstreamConnection *dconn; +}; + +class Http2Session { +public: + Http2Session(struct ev_loop *loop, SSL_CTX *ssl_ctx, + ConnectBlocker *connect_blocker, Worker *worker); + ~Http2Session(); + + int check_cert(); + + // If hard is true, all pending requests are abandoned and + // associated ClientHandlers will be deleted. + int disconnect(bool hard = false); + int initiate_connection(); + + void add_downstream_connection(Http2DownstreamConnection *dconn); + void remove_downstream_connection(Http2DownstreamConnection *dconn); + + void remove_stream_data(StreamData *sd); + + int submit_request(Http2DownstreamConnection *dconn, int32_t pri, + const nghttp2_nv *nva, size_t nvlen, + const nghttp2_data_provider *data_prd); + + int submit_rst_stream(int32_t stream_id, uint32_t error_code); + + int submit_priority(Http2DownstreamConnection *dconn, int32_t pri); + + int terminate_session(uint32_t error_code); + + nghttp2_session *get_session() const; + + bool get_flow_control() const; + + int resume_data(Http2DownstreamConnection *dconn); + + int connection_made(); + + int do_read(); + int do_write(); + + int on_read(); + int on_write(); + + int connected(); + int read_clear(); + int write_clear(); + int tls_handshake(); + int read_tls(); + int write_tls(); + + int downstream_read_proxy(); + int downstream_connect_proxy(); + + int downstream_read(); + int downstream_write(); + + int noop(); + + void signal_write(); + + struct ev_loop *get_loop() const; + + ev_io *get_wev(); + + int get_state() const; + void set_state(int state); + + void start_settings_timer(); + void stop_settings_timer(); + + SSL *get_ssl() const; + + int consume(int32_t stream_id, size_t len); + + // Returns true if request can be issued on downstream connection. + bool can_push_request() const; + // Initiates the connection checking if downstream connection has + // been established and connection checking is required. + void start_checking_connection(); + // Resets connection check timer to timeout |t|. After timeout, we + // require connection checking. If connection checking is already + // enabled, this timeout is for PING ACK timeout. + void reset_connection_check_timer(ev_tstamp t); + void reset_connection_check_timer_if_not_checking(); + // Signals that connection is alive. Internally + // reset_connection_check_timer() is called. + void connection_alive(); + // Change connection check state. + void set_connection_check_state(int state); + int get_connection_check_state() const; + + bool should_hard_fail() const; + + void submit_pending_requests(); + + size_t get_addr_idx() const; + + enum { + // Disconnected + DISCONNECTED, + // Connecting proxy and making CONNECT request + PROXY_CONNECTING, + // Tunnel is established with proxy + PROXY_CONNECTED, + // Establishing tunnel is failed + PROXY_FAILED, + // Connecting to downstream and/or performing SSL/TLS handshake + CONNECTING, + // Connected to downstream + CONNECTED, + // Connection is started to fail + CONNECT_FAILING, + }; + + static const size_t OUTBUF_MAX_THRES = 64 * 1024; + + enum { + // Connection checking is not required + CONNECTION_CHECK_NONE, + // Connection checking is required + CONNECTION_CHECK_REQUIRED, + // Connection checking has been started + CONNECTION_CHECK_STARTED + }; + + using ReadBuf = Buffer<8192>; + using WriteBuf = Buffer<32768>; + +private: + Connection conn_; + ev_timer settings_timer_; + // This timer has 2 purpose: when it first timeout, set + // connection_check_state_ = CONNECTION_CHECK_REQUIRED. After + // connection check has started, this timer is started again and + // traps PING ACK timeout. + ev_timer connchk_timer_; + DList dconns_; + DList streams_; + std::function read_, write_; + std::function on_read_, on_write_; + // Used to parse the response from HTTP proxy + std::unique_ptr proxy_htp_; + Worker *worker_; + ConnectBlocker *connect_blocker_; + // NULL if no TLS is configured + SSL_CTX *ssl_ctx_; + nghttp2_session *session_; + const uint8_t *data_pending_; + size_t data_pendinglen_; + // index of get_config()->downstream_addrs this object uses + size_t addr_idx_; + int state_; + int connection_check_state_; + bool flow_control_; + WriteBuf wb_; + ReadBuf rb_; +}; + +nghttp2_session_callbacks *create_http2_downstream_callbacks(); + +} // namespace shrpx + +#endif // SHRPX_HTTP2_SESSION_H diff --git a/src/shrpx_http2_upstream.cc b/src/shrpx_http2_upstream.cc new file mode 100644 index 0000000..e3aa67f --- /dev/null +++ b/src/shrpx_http2_upstream.cc @@ -0,0 +1,1661 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "shrpx_http2_upstream.h" + +#include +#include +#include +#include + +#include "shrpx_client_handler.h" +#include "shrpx_https_upstream.h" +#include "shrpx_downstream.h" +#include "shrpx_downstream_connection.h" +#include "shrpx_config.h" +#include "shrpx_http.h" +#include "shrpx_worker.h" +#include "http2.h" +#include "util.h" +#include "base64.h" +#include "app_helper.h" +#include "template.h" + +using namespace nghttp2; + +namespace shrpx { + +namespace { +int on_stream_close_callback(nghttp2_session *session, int32_t stream_id, + uint32_t error_code, void *user_data) { + auto upstream = static_cast(user_data); + if (LOG_ENABLED(INFO)) { + ULOG(INFO, upstream) << "Stream stream_id=" << stream_id + << " is being closed"; + } + + auto downstream = static_cast( + nghttp2_session_get_stream_user_data(session, stream_id)); + + if (!downstream) { + return 0; + } + + upstream->consume(stream_id, downstream->get_request_datalen()); + + downstream->reset_request_datalen(); + + if (downstream->get_request_state() == Downstream::CONNECT_FAIL) { + upstream->remove_downstream(downstream); + // downstream was deleted + + return 0; + } + + downstream->set_request_state(Downstream::STREAM_CLOSED); + + if (downstream->get_response_state() == Downstream::MSG_COMPLETE) { + // At this point, downstream response was read + if (!downstream->get_upgraded() && + !downstream->get_response_connection_close()) { + // Keep-alive + downstream->detach_downstream_connection(); + } + + upstream->remove_downstream(downstream); + // downstream was deleted + + return 0; + } + + // At this point, downstream read may be paused. + + // If shrpx_downstream::push_request_headers() failed, the + // error is handled here. + upstream->remove_downstream(downstream); + // downstream was deleted + + // How to test this case? Request sufficient large download + // and make client send RST_STREAM after it gets first DATA + // frame chunk. + + return 0; +} +} // namespace + +int Http2Upstream::upgrade_upstream(HttpsUpstream *http) { + int rv; + + // to deduce :scheme and :authority, first we have to parse request + // URI. + http_parser_url u = {}; + auto &url = http->get_downstream()->get_request_path(); + + std::string scheme, authority; + rv = http_parser_parse_url(url.c_str(), url.size(), 0, &u); + if (rv == 0) { + http2::copy_url_component(scheme, &u, UF_SCHEMA, url.c_str()); + http2::copy_url_component(authority, &u, UF_HOST, url.c_str()); + } + if (scheme.empty()) { + if (handler_->get_ssl()) { + scheme = "https"; + } else { + scheme = "http"; + } + } + if (!authority.empty()) { + if (authority.find(":") != std::string::npos) { + authority = "[" + authority; + authority += "]"; + } + if (u.field_set & (1 << UF_PORT)) { + authority += ":"; + authority += util::utos(u.port); + } + } + + auto http2_settings = http->get_downstream()->get_http2_settings(); + util::to_base64(http2_settings); + + auto settings_payload = + base64::decode(std::begin(http2_settings), std::end(http2_settings)); + + rv = nghttp2_session_upgrade( + session_, reinterpret_cast(settings_payload.c_str()), + settings_payload.size(), nullptr); + if (rv != 0) { + if (LOG_ENABLED(INFO)) { + ULOG(INFO, this) << "nghttp2_session_upgrade() returned error: " + << nghttp2_strerror(rv); + } + return -1; + } + pre_upstream_.reset(http); + auto downstream = http->pop_downstream(); + downstream->reset_upstream(this); + downstream->set_stream_id(1); + downstream->reset_upstream_rtimer(); + downstream->set_stream_id(1); + downstream->set_priority(0); + downstream->set_request_http2_authority(authority); + downstream->set_request_http2_scheme(scheme); + + auto ptr = downstream.get(); + + nghttp2_session_set_stream_user_data(session_, 1, ptr); + downstream_queue_.add_pending(std::move(downstream)); + downstream_queue_.mark_active(ptr); + + if (LOG_ENABLED(INFO)) { + ULOG(INFO, this) << "Connection upgraded to HTTP/2"; + } + + return 0; +} + +void Http2Upstream::start_settings_timer() { + ev_timer_start(handler_->get_loop(), &settings_timer_); +} + +void Http2Upstream::stop_settings_timer() { + ev_timer_stop(handler_->get_loop(), &settings_timer_); +} + +namespace { +int on_header_callback(nghttp2_session *session, const nghttp2_frame *frame, + const uint8_t *name, size_t namelen, + const uint8_t *value, size_t valuelen, uint8_t flags, + void *user_data) { + if (get_config()->upstream_frame_debug) { + verbose_on_header_callback(session, frame, name, namelen, value, valuelen, + flags, user_data); + } + if (frame->hd.type != NGHTTP2_HEADERS) { + return 0; + } + auto upstream = static_cast(user_data); + auto downstream = static_cast( + nghttp2_session_get_stream_user_data(session, frame->hd.stream_id)); + if (!downstream) { + return 0; + } + + if (downstream->get_request_headers_sum() + namelen + valuelen > + get_config()->header_field_buffer || + downstream->get_request_headers().size() >= + get_config()->max_header_fields) { + if (downstream->get_response_state() == Downstream::MSG_COMPLETE) { + return 0; + } + + if (LOG_ENABLED(INFO)) { + ULOG(INFO, upstream) << "Too large or many header field size=" + << downstream->get_request_headers_sum() + namelen + + valuelen << ", num=" + << downstream->get_request_headers().size() + 1; + } + + // just ignore header fields if this is trailer part. + if (frame->headers.cat == NGHTTP2_HCAT_HEADERS) { + return 0; + } + + if (upstream->error_reply(downstream, 431) != 0) { + return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; + } + + return 0; + } + + if (frame->headers.cat == NGHTTP2_HCAT_HEADERS) { + // just store header fields for trailer part + downstream->add_request_trailer(name, namelen, value, valuelen, + flags & NGHTTP2_NV_FLAG_NO_INDEX, -1); + return 0; + } + + auto token = http2::lookup_token(name, namelen); + + downstream->add_request_header(name, namelen, value, valuelen, + flags & NGHTTP2_NV_FLAG_NO_INDEX, token); + return 0; +} +} // namespace + +namespace { +int on_begin_headers_callback(nghttp2_session *session, + const nghttp2_frame *frame, void *user_data) { + auto upstream = static_cast(user_data); + + if (frame->headers.cat != NGHTTP2_HCAT_REQUEST) { + return 0; + } + if (LOG_ENABLED(INFO)) { + ULOG(INFO, upstream) << "Received upstream request HEADERS stream_id=" + << frame->hd.stream_id; + } + + auto handler = upstream->get_client_handler(); + + // TODO Use priority 0 for now + auto downstream = make_unique(upstream, handler->get_mcpool(), + frame->hd.stream_id, 0); + nghttp2_session_set_stream_user_data(session, frame->hd.stream_id, + downstream.get()); + + downstream->reset_upstream_rtimer(); + + // Although, we deprecated minor version from HTTP/2, we supply + // minor version 0 to use via header field in a conventional way. + downstream->set_request_major(2); + downstream->set_request_minor(0); + + upstream->add_pending_downstream(std::move(downstream)); + + return 0; +} +} // namespace + +int Http2Upstream::on_request_headers(Downstream *downstream, + const nghttp2_frame *frame) { + if (downstream->get_response_state() == Downstream::MSG_COMPLETE) { + return 0; + } + + auto &nva = downstream->get_request_headers(); + + if (LOG_ENABLED(INFO)) { + std::stringstream ss; + for (auto &nv : nva) { + ss << TTY_HTTP_HD << nv.name << TTY_RST << ": " << nv.value << "\n"; + } + ULOG(INFO, this) << "HTTP request headers. stream_id=" + << downstream->get_stream_id() << "\n" << ss.str(); + } + + if (get_config()->http2_upstream_dump_request_header) { + http2::dump_nv(get_config()->http2_upstream_dump_request_header, nva); + } + + auto content_length = + downstream->get_request_header(http2::HD_CONTENT_LENGTH); + if (content_length) { + // libnghttp2 guarantees this can be parsed + auto len = util::parse_uint(content_length->value); + downstream->set_request_content_length(len); + } + + auto authority = downstream->get_request_header(http2::HD__AUTHORITY); + auto path = downstream->get_request_header(http2::HD__PATH); + auto method = downstream->get_request_header(http2::HD__METHOD); + auto scheme = downstream->get_request_header(http2::HD__SCHEME); + + // presence of mandatory header fields are guaranteed by libnghttp2. + + // For HTTP/2 proxy, we request :authority. + if (method->value != "CONNECT" && get_config()->http2_proxy && !authority) { + rst_stream(downstream, NGHTTP2_PROTOCOL_ERROR); + return 0; + } + + downstream->set_request_method(http2::value_to_str(method)); + downstream->set_request_http2_scheme(http2::value_to_str(scheme)); + downstream->set_request_http2_authority(http2::value_to_str(authority)); + downstream->set_request_path(http2::value_to_str(path)); + + if (!(frame->hd.flags & NGHTTP2_FLAG_END_STREAM)) { + downstream->set_request_http2_expect_body(true); + } + + downstream->inspect_http2_request(); + + downstream->set_request_state(Downstream::HEADER_COMPLETE); + if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) { + downstream->disable_upstream_rtimer(); + + downstream->set_request_state(Downstream::MSG_COMPLETE); + } + + start_downstream(downstream); + + return 0; +} + +void Http2Upstream::start_downstream(Downstream *downstream) { + if (downstream_queue_.can_activate( + downstream->get_request_http2_authority())) { + initiate_downstream(downstream); + return; + } + + downstream_queue_.mark_blocked(downstream); +} + +void Http2Upstream::initiate_downstream(Downstream *downstream) { + int rv; + + rv = downstream->attach_downstream_connection( + handler_->get_downstream_connection()); + if (rv != 0) { + // downstream connection fails, send error page + if (error_reply(downstream, 503) != 0) { + rst_stream(downstream, NGHTTP2_INTERNAL_ERROR); + } + + downstream->set_request_state(Downstream::CONNECT_FAIL); + + downstream_queue_.mark_failure(downstream); + + return; + } + rv = downstream->push_request_headers(); + if (rv != 0) { + + if (error_reply(downstream, 503) != 0) { + rst_stream(downstream, NGHTTP2_INTERNAL_ERROR); + } + + downstream_queue_.mark_failure(downstream); + + return; + } + + downstream_queue_.mark_active(downstream); + + return; +} + +namespace { +int on_frame_recv_callback(nghttp2_session *session, const nghttp2_frame *frame, + void *user_data) { + if (get_config()->upstream_frame_debug) { + verbose_on_frame_recv_callback(session, frame, user_data); + } + auto upstream = static_cast(user_data); + + switch (frame->hd.type) { + case NGHTTP2_DATA: { + auto downstream = static_cast( + nghttp2_session_get_stream_user_data(session, frame->hd.stream_id)); + if (!downstream) { + return 0; + } + + if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) { + downstream->disable_upstream_rtimer(); + + downstream->end_upload_data(); + downstream->set_request_state(Downstream::MSG_COMPLETE); + } + + return 0; + } + case NGHTTP2_HEADERS: { + auto downstream = static_cast( + nghttp2_session_get_stream_user_data(session, frame->hd.stream_id)); + if (!downstream) { + return 0; + } + + if (frame->headers.cat == NGHTTP2_HCAT_REQUEST) { + downstream->reset_upstream_rtimer(); + + return upstream->on_request_headers(downstream, frame); + } + + if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) { + downstream->disable_upstream_rtimer(); + + downstream->end_upload_data(); + downstream->set_request_state(Downstream::MSG_COMPLETE); + } + + return 0; + } + case NGHTTP2_SETTINGS: + if ((frame->hd.flags & NGHTTP2_FLAG_ACK) == 0) { + return 0; + } + upstream->stop_settings_timer(); + return 0; + case NGHTTP2_GOAWAY: + if (LOG_ENABLED(INFO)) { + auto debug_data = util::ascii_dump(frame->goaway.opaque_data, + frame->goaway.opaque_data_len); + + ULOG(INFO, upstream) << "GOAWAY received: last-stream-id=" + << frame->goaway.last_stream_id + << ", error_code=" << frame->goaway.error_code + << ", debug_data=" << debug_data; + } + return 0; + default: + return 0; + } +} +} // namespace + +namespace { +int on_data_chunk_recv_callback(nghttp2_session *session, uint8_t flags, + int32_t stream_id, const uint8_t *data, + size_t len, void *user_data) { + auto upstream = static_cast(user_data); + auto downstream = static_cast( + nghttp2_session_get_stream_user_data(session, stream_id)); + + if (!downstream || !downstream->get_downstream_connection()) { + if (upstream->consume(stream_id, len) != 0) { + return NGHTTP2_ERR_CALLBACK_FAILURE; + } + + return 0; + } + + downstream->reset_upstream_rtimer(); + + if (downstream->push_upload_data_chunk(data, len) != 0) { + upstream->rst_stream(downstream, NGHTTP2_INTERNAL_ERROR); + + if (upstream->consume(stream_id, len) != 0) { + return NGHTTP2_ERR_CALLBACK_FAILURE; + } + + return 0; + } + + return 0; +} +} // namespace + +namespace { +int on_frame_send_callback(nghttp2_session *session, const nghttp2_frame *frame, + void *user_data) { + if (get_config()->upstream_frame_debug) { + verbose_on_frame_send_callback(session, frame, user_data); + } + auto upstream = static_cast(user_data); + auto handler = upstream->get_client_handler(); + + switch (frame->hd.type) { + case NGHTTP2_DATA: + case NGHTTP2_HEADERS: { + if ((frame->hd.flags & NGHTTP2_FLAG_END_STREAM) == 0) { + return 0; + } + // RST_STREAM if request is still incomplete. + auto stream_id = frame->hd.stream_id; + auto downstream = static_cast( + nghttp2_session_get_stream_user_data(session, stream_id)); + + if (!downstream) { + return 0; + } + + // For tunneling, issue RST_STREAM to finish the stream. + if (downstream->get_upgraded() || + nghttp2_session_get_stream_remote_close(session, stream_id) == 0) { + if (LOG_ENABLED(INFO)) { + ULOG(INFO, upstream) + << "Send RST_STREAM to " + << (downstream->get_upgraded() ? "tunneled " : "") + << "stream stream_id=" << downstream->get_stream_id() + << " to finish off incomplete request"; + } + + upstream->rst_stream(downstream, NGHTTP2_NO_ERROR); + } + + return 0; + } + case NGHTTP2_SETTINGS: + if ((frame->hd.flags & NGHTTP2_FLAG_ACK) == 0) { + upstream->start_settings_timer(); + } + return 0; + case NGHTTP2_PUSH_PROMISE: { + auto promised_stream_id = frame->push_promise.promised_stream_id; + auto downstream = make_unique(upstream, handler->get_mcpool(), + promised_stream_id, 0); + + nghttp2_session_set_stream_user_data(session, promised_stream_id, + downstream.get()); + + downstream->disable_upstream_rtimer(); + + downstream->set_request_major(2); + downstream->set_request_minor(0); + + for (size_t i = 0; i < frame->push_promise.nvlen; ++i) { + auto &nv = frame->push_promise.nva[i]; + auto token = http2::lookup_token(nv.name, nv.namelen); + switch (token) { + case http2::HD__METHOD: + downstream->set_request_method({nv.value, nv.value + nv.valuelen}); + break; + case http2::HD__SCHEME: + downstream->set_request_http2_scheme( + {nv.value, nv.value + nv.valuelen}); + break; + case http2::HD__AUTHORITY: + downstream->set_request_http2_authority( + {nv.value, nv.value + nv.valuelen}); + break; + case http2::HD__PATH: + downstream->set_request_path({nv.value, nv.value + nv.valuelen}); + break; + } + downstream->add_request_header(nv.name, nv.namelen, nv.value, nv.valuelen, + nv.flags & NGHTTP2_NV_FLAG_NO_INDEX, + token); + } + + downstream->inspect_http2_request(); + + downstream->set_request_state(Downstream::MSG_COMPLETE); + + // a bit weird but start_downstream() expects that given + // downstream is in pending queue. + auto ptr = downstream.get(); + upstream->add_pending_downstream(std::move(downstream)); + upstream->start_downstream(ptr); + + return 0; + } + case NGHTTP2_GOAWAY: + if (LOG_ENABLED(INFO)) { + auto debug_data = util::ascii_dump(frame->goaway.opaque_data, + frame->goaway.opaque_data_len); + + ULOG(INFO, upstream) << "Sending GOAWAY: last-stream-id=" + << frame->goaway.last_stream_id + << ", error_code=" << frame->goaway.error_code + << ", debug_data=" << debug_data; + } + return 0; + default: + return 0; + } +} +} // namespace + +namespace { +int on_frame_not_send_callback(nghttp2_session *session, + const nghttp2_frame *frame, int lib_error_code, + void *user_data) { + auto upstream = static_cast(user_data); + if (LOG_ENABLED(INFO)) { + ULOG(INFO, upstream) << "Failed to send control frame type=" + << static_cast(frame->hd.type) + << ", lib_error_code=" << lib_error_code << ":" + << nghttp2_strerror(lib_error_code); + } + if (frame->hd.type == NGHTTP2_HEADERS && + lib_error_code != NGHTTP2_ERR_STREAM_CLOSED && + lib_error_code != NGHTTP2_ERR_STREAM_CLOSING) { + // To avoid stream hanging around, issue RST_STREAM. + auto downstream = static_cast( + nghttp2_session_get_stream_user_data(session, frame->hd.stream_id)); + if (downstream) { + upstream->rst_stream(downstream, NGHTTP2_INTERNAL_ERROR); + } + } + return 0; +} +} // namespace + +namespace { +uint32_t infer_upstream_rst_stream_error_code(uint32_t downstream_error_code) { + // NGHTTP2_REFUSED_STREAM is important because it tells upstream + // client to retry. + switch (downstream_error_code) { + case NGHTTP2_NO_ERROR: + case NGHTTP2_REFUSED_STREAM: + return downstream_error_code; + default: + return NGHTTP2_INTERNAL_ERROR; + } +} +} // namespace + +namespace { +void settings_timeout_cb(struct ev_loop *loop, ev_timer *w, int revents) { + auto upstream = static_cast(w->data); + auto handler = upstream->get_client_handler(); + ULOG(INFO, upstream) << "SETTINGS timeout"; + if (upstream->terminate_session(NGHTTP2_SETTINGS_TIMEOUT) != 0) { + delete handler; + return; + } + handler->signal_write(); +} +} // namespace + +namespace { +void shutdown_timeout_cb(struct ev_loop *loop, ev_timer *w, int revents) { + auto upstream = static_cast(w->data); + auto handler = upstream->get_client_handler(); + upstream->submit_goaway(); + handler->signal_write(); +} +} // namespace + +namespace { +void prepare_cb(struct ev_loop *loop, ev_prepare *w, int revents) { + auto upstream = static_cast(w->data); + upstream->check_shutdown(); +} +} // namespace + +void Http2Upstream::submit_goaway() { + auto last_stream_id = nghttp2_session_get_last_proc_stream_id(session_); + nghttp2_submit_goaway(session_, NGHTTP2_FLAG_NONE, last_stream_id, + NGHTTP2_NO_ERROR, nullptr, 0); +} + +void Http2Upstream::check_shutdown() { + int rv; + if (shutdown_handled_) { + return; + } + + auto worker = handler_->get_worker(); + + if (worker->get_graceful_shutdown()) { + shutdown_handled_ = true; + rv = nghttp2_submit_shutdown_notice(session_); + if (rv != 0) { + ULOG(FATAL, this) << "nghttp2_submit_shutdown_notice() failed: " + << nghttp2_strerror(rv); + return; + } + handler_->signal_write(); + ev_timer_start(handler_->get_loop(), &shutdown_timer_); + } +} + +nghttp2_session_callbacks *create_http2_upstream_callbacks() { + int rv; + nghttp2_session_callbacks *callbacks; + + rv = nghttp2_session_callbacks_new(&callbacks); + + if (rv != 0) { + return nullptr; + } + + nghttp2_session_callbacks_set_on_stream_close_callback( + callbacks, on_stream_close_callback); + + nghttp2_session_callbacks_set_on_frame_recv_callback(callbacks, + on_frame_recv_callback); + + nghttp2_session_callbacks_set_on_data_chunk_recv_callback( + callbacks, on_data_chunk_recv_callback); + + nghttp2_session_callbacks_set_on_frame_send_callback(callbacks, + on_frame_send_callback); + + nghttp2_session_callbacks_set_on_frame_not_send_callback( + callbacks, on_frame_not_send_callback); + + nghttp2_session_callbacks_set_on_header_callback(callbacks, + on_header_callback); + + nghttp2_session_callbacks_set_on_begin_headers_callback( + callbacks, on_begin_headers_callback); + + if (get_config()->padding) { + nghttp2_session_callbacks_set_select_padding_callback( + callbacks, http::select_padding_callback); + } + + return callbacks; +} + +Http2Upstream::Http2Upstream(ClientHandler *handler) + : downstream_queue_( + get_config()->http2_proxy + ? get_config()->downstream_connections_per_host + : get_config()->downstream_proto == PROTO_HTTP + ? get_config()->downstream_connections_per_frontend + : 0, + !get_config()->http2_proxy), + handler_(handler), session_(nullptr), data_pending_(nullptr), + data_pendinglen_(0), shutdown_handled_(false) { + + int rv; + + rv = nghttp2_session_server_new2(&session_, + get_config()->http2_upstream_callbacks, this, + get_config()->http2_option); + + assert(rv == 0); + + flow_control_ = true; + + // TODO Maybe call from outside? + std::array entry; + entry[0].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS; + entry[0].value = get_config()->http2_max_concurrent_streams; + + entry[1].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE; + entry[1].value = (1 << get_config()->http2_upstream_window_bits) - 1; + + rv = nghttp2_submit_settings(session_, NGHTTP2_FLAG_NONE, entry.data(), + entry.size()); + if (rv != 0) { + ULOG(ERROR, this) << "nghttp2_submit_settings() returned error: " + << nghttp2_strerror(rv); + } + + if (get_config()->http2_upstream_connection_window_bits > 16) { + int32_t delta = (1 << get_config()->http2_upstream_connection_window_bits) - + 1 - NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE; + rv = nghttp2_submit_window_update(session_, NGHTTP2_FLAG_NONE, 0, delta); + + if (rv != 0) { + ULOG(ERROR, this) << "nghttp2_submit_window_update() returned error: " + << nghttp2_strerror(rv); + } + } + + // We wait for SETTINGS ACK at least 10 seconds. + ev_timer_init(&settings_timer_, settings_timeout_cb, 10., 0.); + + settings_timer_.data = this; + + // timer for 2nd GOAWAY. HTTP/2 spec recommend 1 RTT. We wait for + // 2 seconds. + ev_timer_init(&shutdown_timer_, shutdown_timeout_cb, 2., 0); + shutdown_timer_.data = this; + + ev_prepare_init(&prep_, prepare_cb); + prep_.data = this; + ev_prepare_start(handler_->get_loop(), &prep_); + + handler_->reset_upstream_read_timeout( + get_config()->http2_upstream_read_timeout); + + handler_->signal_write(); +} + +Http2Upstream::~Http2Upstream() { + nghttp2_session_del(session_); + ev_prepare_stop(handler_->get_loop(), &prep_); + ev_timer_stop(handler_->get_loop(), &shutdown_timer_); + ev_timer_stop(handler_->get_loop(), &settings_timer_); +} + +int Http2Upstream::on_read() { + ssize_t rv = 0; + auto rb = handler_->get_rb(); + auto rlimit = handler_->get_rlimit(); + + if (rb->rleft()) { + rv = nghttp2_session_mem_recv(session_, rb->pos, rb->rleft()); + if (rv < 0) { + if (rv != NGHTTP2_ERR_BAD_CLIENT_MAGIC) { + ULOG(ERROR, this) << "nghttp2_session_recv() returned error: " + << nghttp2_strerror(rv); + } + return -1; + } + + // nghttp2_session_mem_recv should consume all input bytes on + // success. + assert(static_cast(rv) == rb->rleft()); + rb->reset(); + rlimit->startw(); + } + + auto wb = handler_->get_wb(); + if (nghttp2_session_want_read(session_) == 0 && + nghttp2_session_want_write(session_) == 0 && wb->rleft() == 0) { + if (LOG_ENABLED(INFO)) { + ULOG(INFO, this) << "No more read/write for this HTTP2 session"; + } + return -1; + } + + handler_->signal_write(); + return 0; +} + +// After this function call, downstream may be deleted. +int Http2Upstream::on_write() { + auto wb = handler_->get_wb(); + + if (data_pending_) { + auto n = std::min(wb->wleft(), data_pendinglen_); + wb->write(data_pending_, n); + if (n < data_pendinglen_) { + data_pending_ += n; + data_pendinglen_ -= n; + return 0; + } + + data_pending_ = nullptr; + data_pendinglen_ = 0; + } + + for (;;) { + const uint8_t *data; + auto datalen = nghttp2_session_mem_send(session_, &data); + + if (datalen < 0) { + ULOG(ERROR, this) << "nghttp2_session_mem_send() returned error: " + << nghttp2_strerror(datalen); + return -1; + } + if (datalen == 0) { + break; + } + auto n = wb->write(data, datalen); + if (n < static_cast(datalen)) { + data_pending_ = data + n; + data_pendinglen_ = datalen - n; + return 0; + } + } + + if (nghttp2_session_want_read(session_) == 0 && + nghttp2_session_want_write(session_) == 0 && wb->rleft() == 0) { + if (LOG_ENABLED(INFO)) { + ULOG(INFO, this) << "No more read/write for this HTTP2 session"; + } + return -1; + } + + return 0; +} + +ClientHandler *Http2Upstream::get_client_handler() const { return handler_; } + +int Http2Upstream::downstream_read(DownstreamConnection *dconn) { + auto downstream = dconn->get_downstream(); + + if (downstream->get_request_state() == Downstream::STREAM_CLOSED) { + // If upstream HTTP2 stream was closed, we just close downstream, + // because there is no consumer now. Downstream connection is also + // closed in this case. + remove_downstream(downstream); + // downstream was deleted + + return 0; + } + + if (downstream->get_response_state() == Downstream::MSG_RESET) { + // The downstream stream was reset (canceled). In this case, + // RST_STREAM to the upstream and delete downstream connection + // here. Deleting downstream will be taken place at + // on_stream_close_callback. + rst_stream(downstream, + infer_upstream_rst_stream_error_code( + downstream->get_response_rst_stream_error_code())); + downstream->pop_downstream_connection(); + // dconn was deleted + dconn = nullptr; + } else if (downstream->get_response_state() == Downstream::MSG_BAD_HEADER) { + if (error_reply(downstream, 502) != 0) { + return -1; + } + downstream->pop_downstream_connection(); + // dconn was deleted + dconn = nullptr; + } else { + auto rv = downstream->on_read(); + if (rv == SHRPX_ERR_EOF) { + return downstream_eof(dconn); + } + if (rv != 0) { + if (rv != SHRPX_ERR_NETWORK) { + if (LOG_ENABLED(INFO)) { + DCLOG(INFO, dconn) << "HTTP parser failure"; + } + } + return downstream_error(dconn, Downstream::EVENT_ERROR); + } + // Detach downstream connection early so that it could be reused + // without hitting server's request timeout. + if (downstream->get_response_state() == Downstream::MSG_COMPLETE && + !downstream->get_response_connection_close()) { + // Keep-alive + downstream->detach_downstream_connection(); + } + } + + handler_->signal_write(); + + // At this point, downstream may be deleted. + + return 0; +} + +int Http2Upstream::downstream_write(DownstreamConnection *dconn) { + int rv; + rv = dconn->on_write(); + if (rv == SHRPX_ERR_NETWORK) { + return downstream_error(dconn, Downstream::EVENT_ERROR); + } + if (rv != 0) { + return -1; + } + return 0; +} + +int Http2Upstream::downstream_eof(DownstreamConnection *dconn) { + auto downstream = dconn->get_downstream(); + + if (LOG_ENABLED(INFO)) { + DCLOG(INFO, dconn) << "EOF. stream_id=" << downstream->get_stream_id(); + } + if (downstream->get_request_state() == Downstream::STREAM_CLOSED) { + // If stream was closed already, we don't need to send reply at + // the first place. We can delete downstream. + remove_downstream(downstream); + // downstream was deleted + + return 0; + } + + // Delete downstream connection. If we don't delete it here, it will + // be pooled in on_stream_close_callback. + downstream->pop_downstream_connection(); + // dconn was deleted + dconn = nullptr; + // downstream wil be deleted in on_stream_close_callback. + if (downstream->get_response_state() == Downstream::HEADER_COMPLETE) { + // Server may indicate the end of the request by EOF + if (LOG_ENABLED(INFO)) { + ULOG(INFO, this) << "Downstream body was ended by EOF"; + } + downstream->set_response_state(Downstream::MSG_COMPLETE); + + // For tunneled connection, MSG_COMPLETE signals + // downstream_data_read_callback to send RST_STREAM after pending + // response body is sent. This is needed to ensure that RST_STREAM + // is sent after all pending data are sent. + on_downstream_body_complete(downstream); + } else if (downstream->get_response_state() != Downstream::MSG_COMPLETE) { + // If stream was not closed, then we set MSG_COMPLETE and let + // on_stream_close_callback delete downstream. + if (error_reply(downstream, 502) != 0) { + return -1; + } + } + handler_->signal_write(); + // At this point, downstream may be deleted. + return 0; +} + +int Http2Upstream::downstream_error(DownstreamConnection *dconn, int events) { + auto downstream = dconn->get_downstream(); + + if (LOG_ENABLED(INFO)) { + if (events & Downstream::EVENT_ERROR) { + DCLOG(INFO, dconn) << "Downstream network/general error"; + } else { + DCLOG(INFO, dconn) << "Timeout"; + } + if (downstream->get_upgraded()) { + DCLOG(INFO, dconn) << "Note: this is tunnel connection"; + } + } + + if (downstream->get_request_state() == Downstream::STREAM_CLOSED) { + remove_downstream(downstream); + // downstream was deleted + + return 0; + } + + // Delete downstream connection. If we don't delete it here, it will + // be pooled in on_stream_close_callback. + downstream->pop_downstream_connection(); + // dconn was deleted + dconn = nullptr; + + if (downstream->get_response_state() == Downstream::MSG_COMPLETE) { + // For SSL tunneling, we issue RST_STREAM. For other types of + // stream, we don't have to do anything since response was + // complete. + if (downstream->get_upgraded()) { + rst_stream(downstream, NGHTTP2_NO_ERROR); + } + } else { + if (downstream->get_response_state() == Downstream::HEADER_COMPLETE) { + if (downstream->get_upgraded()) { + on_downstream_body_complete(downstream); + } else { + rst_stream(downstream, NGHTTP2_INTERNAL_ERROR); + } + } else { + unsigned int status; + if (events & Downstream::EVENT_TIMEOUT) { + status = 504; + } else { + status = 502; + } + if (error_reply(downstream, status) != 0) { + return -1; + } + } + downstream->set_response_state(Downstream::MSG_COMPLETE); + } + handler_->signal_write(); + // At this point, downstream may be deleted. + return 0; +} + +int Http2Upstream::rst_stream(Downstream *downstream, uint32_t error_code) { + if (LOG_ENABLED(INFO)) { + ULOG(INFO, this) << "RST_STREAM stream_id=" << downstream->get_stream_id() + << " with error_code=" << error_code; + } + int rv; + rv = nghttp2_submit_rst_stream(session_, NGHTTP2_FLAG_NONE, + downstream->get_stream_id(), error_code); + if (rv < NGHTTP2_ERR_FATAL) { + ULOG(FATAL, this) << "nghttp2_submit_rst_stream() failed: " + << nghttp2_strerror(rv); + DIE(); + } + return 0; +} + +int Http2Upstream::terminate_session(uint32_t error_code) { + int rv; + rv = nghttp2_session_terminate_session(session_, error_code); + if (rv != 0) { + return -1; + } + return 0; +} + +namespace { +ssize_t downstream_data_read_callback(nghttp2_session *session, + int32_t stream_id, uint8_t *buf, + size_t length, uint32_t *data_flags, + nghttp2_data_source *source, + void *user_data) { + int rv; + auto downstream = static_cast(source->ptr); + auto upstream = static_cast(downstream->get_upstream()); + auto body = downstream->get_response_buf(); + assert(body); + + auto dconn = downstream->get_downstream_connection(); + + if (body->rleft() == 0 && dconn && + downstream->get_response_state() != Downstream::MSG_COMPLETE) { + // Try to read more if buffer is empty. This will help small + // buffer and make priority handling a bit better. + if (upstream->downstream_read(dconn) != 0) { + return NGHTTP2_ERR_CALLBACK_FAILURE; + } + } + + auto nread = body->remove(buf, length); + auto body_empty = body->rleft() == 0; + + if (body_empty && + downstream->get_response_state() == Downstream::MSG_COMPLETE) { + + *data_flags |= NGHTTP2_DATA_FLAG_EOF; + + if (!downstream->get_upgraded()) { + auto &trailers = downstream->get_response_trailers(); + if (!trailers.empty()) { + std::vector nva; + nva.reserve(trailers.size()); + http2::copy_headers_to_nva(nva, trailers); + if (!nva.empty()) { + rv = nghttp2_submit_trailer(session, stream_id, nva.data(), + nva.size()); + if (rv != 0) { + if (nghttp2_is_fatal(rv)) { + return NGHTTP2_ERR_CALLBACK_FAILURE; + } + } else { + *data_flags |= NGHTTP2_DATA_FLAG_NO_END_STREAM; + } + } + } + } + } + + if (body_empty) { + downstream->disable_upstream_wtimer(); + } else { + downstream->reset_upstream_wtimer(); + } + + if (nread > 0 && downstream->resume_read(SHRPX_NO_BUFFER, nread) != 0) { + return NGHTTP2_ERR_CALLBACK_FAILURE; + } + + if (nread == 0 && ((*data_flags) & NGHTTP2_DATA_FLAG_EOF) == 0) { + return NGHTTP2_ERR_DEFERRED; + } + + if (nread > 0) { + downstream->add_response_sent_bodylen(nread); + } + + return nread; +} +} // namespace + +int Http2Upstream::error_reply(Downstream *downstream, + unsigned int status_code) { + int rv; + auto html = http::create_error_html(status_code); + downstream->set_response_http_status(status_code); + auto body = downstream->get_response_buf(); + body->append(html.c_str(), html.size()); + downstream->set_response_state(Downstream::MSG_COMPLETE); + + nghttp2_data_provider data_prd; + data_prd.source.ptr = downstream; + data_prd.read_callback = downstream_data_read_callback; + + auto content_length = util::utos(html.size()); + auto status_code_str = util::utos(status_code); + auto nva = + make_array(http2::make_nv_ls(":status", status_code_str), + http2::make_nv_ll("content-type", "text/html; charset=UTF-8"), + http2::make_nv_lc("server", get_config()->server_name), + http2::make_nv_ls("content-length", content_length)); + + rv = nghttp2_submit_response(session_, downstream->get_stream_id(), + nva.data(), nva.size(), &data_prd); + if (rv < NGHTTP2_ERR_FATAL) { + ULOG(FATAL, this) << "nghttp2_submit_response() failed: " + << nghttp2_strerror(rv); + return -1; + } + + return 0; +} + +void +Http2Upstream::add_pending_downstream(std::unique_ptr downstream) { + downstream_queue_.add_pending(std::move(downstream)); +} + +void Http2Upstream::remove_downstream(Downstream *downstream) { + if (downstream->accesslog_ready()) { + handler_->write_accesslog(downstream); + } + + nghttp2_session_set_stream_user_data(session_, downstream->get_stream_id(), + nullptr); + + auto next_downstream = downstream_queue_.remove_and_get_blocked(downstream); + + if (next_downstream) { + initiate_downstream(next_downstream); + } +} + +// WARNING: Never call directly or indirectly nghttp2_session_send or +// nghttp2_session_recv. These calls may delete downstream. +int Http2Upstream::on_downstream_header_complete(Downstream *downstream) { + int rv; + + if (LOG_ENABLED(INFO)) { + if (downstream->get_non_final_response()) { + DLOG(INFO, downstream) << "HTTP non-final response header"; + } else { + DLOG(INFO, downstream) << "HTTP response header completed"; + } + } + + if (!get_config()->http2_proxy && !get_config()->client_proxy && + !get_config()->no_location_rewrite) { + downstream->rewrite_location_response_header( + downstream->get_request_http2_scheme()); + } + + size_t nheader = downstream->get_response_headers().size(); + auto nva = std::vector(); + // 3 means :status and possible server and via header field. + nva.reserve(nheader + 3 + get_config()->add_response_headers.size()); + std::string via_value; + auto response_status = util::utos(downstream->get_response_http_status()); + nva.push_back(http2::make_nv_ls(":status", response_status)); + + http2::copy_headers_to_nva(nva, downstream->get_response_headers()); + + if (downstream->get_non_final_response()) { + if (LOG_ENABLED(INFO)) { + log_response_headers(downstream, nva); + } + + rv = nghttp2_submit_headers(session_, NGHTTP2_FLAG_NONE, + downstream->get_stream_id(), nullptr, + nva.data(), nva.size(), nullptr); + + downstream->clear_response_headers(); + + if (rv != 0) { + ULOG(FATAL, this) << "nghttp2_submit_headers() failed"; + return -1; + } + + return 0; + } + + if (!get_config()->http2_proxy && !get_config()->client_proxy) { + nva.push_back(http2::make_nv_lc("server", get_config()->server_name)); + } else { + auto server = downstream->get_response_header(http2::HD_SERVER); + if (server) { + nva.push_back(http2::make_nv_ls("server", (*server).value)); + } + } + + auto via = downstream->get_response_header(http2::HD_VIA); + if (get_config()->no_via) { + if (via) { + nva.push_back(http2::make_nv_ls("via", (*via).value)); + } + } else { + if (via) { + via_value = (*via).value; + via_value += ", "; + } + via_value += http::create_via_header_value( + downstream->get_response_major(), downstream->get_response_minor()); + nva.push_back(http2::make_nv_ls("via", via_value)); + } + + for (auto &p : get_config()->add_response_headers) { + nva.push_back(http2::make_nv(p.first, p.second)); + } + + if (LOG_ENABLED(INFO)) { + log_response_headers(downstream, nva); + } + + if (get_config()->http2_upstream_dump_response_header) { + http2::dump_nv(get_config()->http2_upstream_dump_response_header, + nva.data(), nva.size()); + } + + nghttp2_data_provider data_prd; + data_prd.source.ptr = downstream; + data_prd.read_callback = downstream_data_read_callback; + + nghttp2_data_provider *data_prdptr; + + if (downstream->expect_response_body()) { + data_prdptr = &data_prd; + } else { + data_prdptr = nullptr; + } + + rv = nghttp2_submit_response(session_, downstream->get_stream_id(), + nva.data(), nva.size(), data_prdptr); + if (rv != 0) { + ULOG(FATAL, this) << "nghttp2_submit_response() failed"; + return -1; + } + + // We need some conditions that must be fulfilled to initiate server + // push. + // + // * Server push is disabled for http2 proxy, since incoming headers + // are mixed origins. We don't know how to reliably determine the + // authority yet. + // + // * If downstream is http/2, it is likely that PUSH_PROMISE is + // coming from there, so we don't initiate PUSH_RPOMISE here. + // + // * We need 200 response code for associated resource. This is too + // restrictive, we will review this later. + // + // * We requires GET or POST for associated resource. Probably we + // don't want to push for HEAD request. Not sure other methods + // are also eligible for push. + if (!get_config()->no_server_push && + get_config()->downstream_proto == PROTO_HTTP && + !get_config()->http2_proxy && (downstream->get_stream_id() % 2) && + downstream->get_response_header(http2::HD_LINK) && + downstream->get_response_http_status() == 200 && + (downstream->get_request_method() == "GET" || + downstream->get_request_method() == "POST")) { + + if (prepare_push_promise(downstream) != 0) { + return -1; + } + } + + return 0; +} + +// WARNING: Never call directly or indirectly nghttp2_session_send or +// nghttp2_session_recv. These calls may delete downstream. +int Http2Upstream::on_downstream_body(Downstream *downstream, + const uint8_t *data, size_t len, + bool flush) { + auto body = downstream->get_response_buf(); + body->append(data, len); + + if (flush) { + nghttp2_session_resume_data(session_, downstream->get_stream_id()); + + downstream->ensure_upstream_wtimer(); + } + + return 0; +} + +// WARNING: Never call directly or indirectly nghttp2_session_send or +// nghttp2_session_recv. These calls may delete downstream. +int Http2Upstream::on_downstream_body_complete(Downstream *downstream) { + if (LOG_ENABLED(INFO)) { + DLOG(INFO, downstream) << "HTTP response completed"; + } + + if (!downstream->validate_response_bodylen()) { + rst_stream(downstream, NGHTTP2_PROTOCOL_ERROR); + downstream->set_response_connection_close(true); + return 0; + } + + nghttp2_session_resume_data(session_, downstream->get_stream_id()); + downstream->ensure_upstream_wtimer(); + + return 0; +} + +bool Http2Upstream::get_flow_control() const { return flow_control_; } + +void Http2Upstream::pause_read(IOCtrlReason reason) {} + +int Http2Upstream::resume_read(IOCtrlReason reason, Downstream *downstream, + size_t consumed) { + if (get_flow_control()) { + assert(downstream->get_request_datalen() >= consumed); + + if (consume(downstream->get_stream_id(), consumed) != 0) { + return -1; + } + + downstream->dec_request_datalen(consumed); + } + + handler_->signal_write(); + return 0; +} + +int Http2Upstream::on_downstream_abort_request(Downstream *downstream, + unsigned int status_code) { + int rv; + + rv = error_reply(downstream, status_code); + + if (rv != 0) { + return -1; + } + + handler_->signal_write(); + return 0; +} + +int Http2Upstream::consume(int32_t stream_id, size_t len) { + int rv; + + rv = nghttp2_session_consume(session_, stream_id, len); + + if (rv != 0) { + ULOG(WARN, this) << "nghttp2_session_consume() returned error: " + << nghttp2_strerror(rv); + return -1; + } + + return 0; +} + +void +Http2Upstream::log_response_headers(Downstream *downstream, + const std::vector &nva) const { + std::stringstream ss; + for (auto &nv : nva) { + ss << TTY_HTTP_HD << nv.name << TTY_RST << ": " << nv.value << "\n"; + } + ULOG(INFO, this) << "HTTP response headers. stream_id=" + << downstream->get_stream_id() << "\n" << ss.str(); +} + +int Http2Upstream::on_timeout(Downstream *downstream) { + if (LOG_ENABLED(INFO)) { + ULOG(INFO, this) << "Stream timeout stream_id=" + << downstream->get_stream_id(); + } + + rst_stream(downstream, NGHTTP2_NO_ERROR); + + return 0; +} + +void Http2Upstream::on_handler_delete() { + for (auto d = downstream_queue_.get_downstreams(); d; d = d->dlnext) { + if (d->get_dispatch_state() == Downstream::DISPATCH_ACTIVE && + d->accesslog_ready()) { + handler_->write_accesslog(d); + } + } +} + +int Http2Upstream::on_downstream_reset(bool no_retry) { + int rv; + + for (auto downstream = downstream_queue_.get_downstreams(); downstream; + downstream = downstream->dlnext) { + if (downstream->get_dispatch_state() != Downstream::DISPATCH_ACTIVE) { + continue; + } + + if (!downstream->request_submission_ready()) { + rst_stream(downstream, NGHTTP2_INTERNAL_ERROR); + downstream->pop_downstream_connection(); + continue; + } + + downstream->pop_downstream_connection(); + + downstream->add_retry(); + + if (no_retry || downstream->no_more_retry()) { + goto fail; + } + + // downstream connection is clean; we can retry with new + // downstream connection. + + rv = downstream->attach_downstream_connection( + handler_->get_downstream_connection()); + if (rv != 0) { + goto fail; + } + + continue; + + fail: + if (on_downstream_abort_request(downstream, 503) != 0) { + return -1; + } + downstream->pop_downstream_connection(); + } + + handler_->signal_write(); + + return 0; +} + +int Http2Upstream::prepare_push_promise(Downstream *downstream) { + int rv; + http_parser_url u; + memset(&u, 0, sizeof(u)); + rv = http_parser_parse_url(downstream->get_request_path().c_str(), + downstream->get_request_path().size(), 0, &u); + if (rv != 0) { + return 0; + } + const char *base; + size_t baselen; + if (u.field_set & (1 << UF_PATH)) { + auto &f = u.field_data[UF_PATH]; + base = downstream->get_request_path().c_str() + f.off; + baselen = f.len; + } else { + base = "/"; + baselen = 1; + } + for (auto &kv : downstream->get_response_headers()) { + if (kv.token != http2::HD_LINK) { + continue; + } + for (auto &link : + http2::parse_link_header(kv.value.c_str(), kv.value.size())) { + auto link_url = link.uri.first; + auto link_urllen = link.uri.second - link.uri.first; + + const char *rel; + size_t rellen; + const char *relq = nullptr; + size_t relqlen = 0; + + http_parser_url v; + memset(&v, 0, sizeof(v)); + rv = http_parser_parse_url(link_url, link_urllen, 0, &v); + if (rv != 0) { + assert(link_urllen); + if (link_url[0] == '/') { + continue; + } + // treat link_url as relative URI. + auto end = std::find(link_url, link_url + link_urllen, '#'); + auto q = std::find(link_url, end, '?'); + rel = link_url; + rellen = q - link_url; + if (q != end) { + relq = q + 1; + relqlen = end - relq; + } + } else { + if (v.field_set & (1 << UF_HOST)) { + continue; + } + if (v.field_set & (1 << UF_PATH)) { + auto &f = v.field_data[UF_PATH]; + rel = link_url + f.off; + rellen = f.len; + } else { + rel = "/"; + rellen = 1; + } + + if (v.field_set & (1 << UF_QUERY)) { + auto &f = v.field_data[UF_QUERY]; + relq = link_url + f.off; + relqlen = f.len; + } + } + auto path = http2::path_join(base, baselen, nullptr, 0, rel, rellen, relq, + relqlen); + rv = submit_push_promise(path, downstream); + if (rv != 0) { + return -1; + } + } + } + return 0; +} + +int Http2Upstream::submit_push_promise(const std::string &path, + Downstream *downstream) { + int rv; + std::vector nva; + nva.reserve(downstream->get_request_headers().size()); + + // juse use "GET" for now + nva.push_back(http2::make_nv_ll(":method", "GET")); + nva.push_back( + http2::make_nv_ls(":scheme", downstream->get_request_http2_scheme())); + nva.push_back(http2::make_nv_ls(":path", path)); + auto &authority = downstream->get_request_http2_authority(); + if (!authority.empty()) { + nva.push_back(http2::make_nv_ls(":authority", authority)); + } + + for (auto &kv : downstream->get_request_headers()) { + switch (kv.token) { + // TODO generate referer + case http2::HD__AUTHORITY: + case http2::HD__SCHEME: + case http2::HD__METHOD: + case http2::HD__PATH: + continue; + case http2::HD_ACCEPT_ENCODING: + case http2::HD_ACCEPT_LANGUAGE: + case http2::HD_CACHE_CONTROL: + case http2::HD_HOST: + case http2::HD_USER_AGENT: + nva.push_back(http2::make_nv(kv.name, kv.value, kv.no_index)); + break; + } + } + + rv = nghttp2_submit_push_promise(session_, NGHTTP2_FLAG_NONE, + downstream->get_stream_id(), nva.data(), + nva.size(), nullptr); + + if (rv < 0) { + if (LOG_ENABLED(INFO)) { + ULOG(INFO, this) << "nghttp2_submit_push_promise() failed: " + << nghttp2_strerror(rv); + } + if (nghttp2_is_fatal(rv)) { + return -1; + } + return 0; + } + + if (LOG_ENABLED(INFO)) { + std::stringstream ss; + for (auto &nv : nva) { + ss << TTY_HTTP_HD << nv.name << TTY_RST << ": " << nv.value << "\n"; + } + ULOG(INFO, this) << "HTTP push request headers. promised_stream_id=" << rv + << "\n" << ss.str(); + } + + return 0; +} + +} // namespace shrpx diff --git a/src/shrpx_http2_upstream.h b/src/shrpx_http2_upstream.h new file mode 100644 index 0000000..ef8c0be --- /dev/null +++ b/src/shrpx_http2_upstream.h @@ -0,0 +1,121 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef SHRPX_HTTP2_UPSTREAM_H +#define SHRPX_HTTP2_UPSTREAM_H + +#include "shrpx.h" + +#include + +#include + +#include + +#include "shrpx_upstream.h" +#include "shrpx_downstream_queue.h" +#include "memchunk.h" + +using namespace nghttp2; + +namespace shrpx { + +class ClientHandler; +class HttpsUpstream; + +class Http2Upstream : public Upstream { +public: + Http2Upstream(ClientHandler *handler); + virtual ~Http2Upstream(); + virtual int on_read(); + virtual int on_write(); + virtual int on_timeout(Downstream *downstream); + virtual int on_downstream_abort_request(Downstream *downstream, + unsigned int status_code); + virtual ClientHandler *get_client_handler() const; + + virtual int downstream_read(DownstreamConnection *dconn); + virtual int downstream_write(DownstreamConnection *dconn); + virtual int downstream_eof(DownstreamConnection *dconn); + virtual int downstream_error(DownstreamConnection *dconn, int events); + + void add_pending_downstream(std::unique_ptr downstream); + void remove_downstream(Downstream *downstream); + + int rst_stream(Downstream *downstream, uint32_t error_code); + int terminate_session(uint32_t error_code); + int error_reply(Downstream *downstream, unsigned int status_code); + + virtual void pause_read(IOCtrlReason reason); + virtual int resume_read(IOCtrlReason reason, Downstream *downstream, + size_t consumed); + + virtual int on_downstream_header_complete(Downstream *downstream); + virtual int on_downstream_body(Downstream *downstream, const uint8_t *data, + size_t len, bool flush); + virtual int on_downstream_body_complete(Downstream *downstream); + + virtual void on_handler_delete(); + virtual int on_downstream_reset(bool no_retry); + + bool get_flow_control() const; + // Perform HTTP/2 upgrade from |upstream|. On success, this object + // takes ownership of the |upstream|. This function returns 0 if it + // succeeds, or -1. + int upgrade_upstream(HttpsUpstream *upstream); + void start_settings_timer(); + void stop_settings_timer(); + int consume(int32_t stream_id, size_t len); + void log_response_headers(Downstream *downstream, + const std::vector &nva) const; + void start_downstream(Downstream *downstream); + void initiate_downstream(Downstream *downstream); + + void submit_goaway(); + void check_shutdown(); + + int prepare_push_promise(Downstream *downstream); + int submit_push_promise(const std::string &path, Downstream *downstream); + + int on_request_headers(Downstream *downstream, const nghttp2_frame *frame); + +private: + std::unique_ptr pre_upstream_; + DownstreamQueue downstream_queue_; + ev_timer settings_timer_; + ev_timer shutdown_timer_; + ev_prepare prep_; + ClientHandler *handler_; + nghttp2_session *session_; + const uint8_t *data_pending_; + size_t data_pendinglen_; + bool flow_control_; + bool shutdown_handled_; +}; + +nghttp2_session_callbacks *create_http2_upstream_callbacks(); + +} // namespace shrpx + +#endif // SHRPX_HTTP2_UPSTREAM_H diff --git a/src/shrpx_http_downstream_connection.cc b/src/shrpx_http_downstream_connection.cc new file mode 100644 index 0000000..1840f75 --- /dev/null +++ b/src/shrpx_http_downstream_connection.cc @@ -0,0 +1,844 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "shrpx_http_downstream_connection.h" + +#include "shrpx_client_handler.h" +#include "shrpx_upstream.h" +#include "shrpx_downstream.h" +#include "shrpx_config.h" +#include "shrpx_error.h" +#include "shrpx_http.h" +#include "shrpx_log_config.h" +#include "shrpx_connect_blocker.h" +#include "shrpx_downstream_connection_pool.h" +#include "shrpx_worker.h" +#include "http2.h" +#include "util.h" + +using namespace nghttp2; + +namespace shrpx { + +namespace { +void timeoutcb(struct ev_loop *loop, ev_timer *w, int revents) { + auto conn = static_cast(w->data); + auto dconn = static_cast(conn->data); + + if (LOG_ENABLED(INFO)) { + DCLOG(INFO, dconn) << "Time out"; + } + + auto downstream = dconn->get_downstream(); + auto upstream = downstream->get_upstream(); + auto handler = upstream->get_client_handler(); + + // Do this so that dconn is not pooled + downstream->set_response_connection_close(true); + + if (upstream->downstream_error(dconn, Downstream::EVENT_TIMEOUT) != 0) { + delete handler; + } +} +} // namespace + +namespace { +void readcb(struct ev_loop *loop, ev_io *w, int revents) { + auto conn = static_cast(w->data); + auto dconn = static_cast(conn->data); + auto downstream = dconn->get_downstream(); + auto upstream = downstream->get_upstream(); + auto handler = upstream->get_client_handler(); + + if (upstream->downstream_read(dconn) != 0) { + delete handler; + } +} +} // namespace + +namespace { +void writecb(struct ev_loop *loop, ev_io *w, int revents) { + auto conn = static_cast(w->data); + auto dconn = static_cast(conn->data); + auto downstream = dconn->get_downstream(); + auto upstream = downstream->get_upstream(); + auto handler = upstream->get_client_handler(); + + if (upstream->downstream_write(dconn) != 0) { + delete handler; + } +} +} // namespace + +namespace { +void connectcb(struct ev_loop *loop, ev_io *w, int revents) { + auto conn = static_cast(w->data); + auto dconn = static_cast(conn->data); + auto downstream = dconn->get_downstream(); + auto upstream = downstream->get_upstream(); + auto handler = upstream->get_client_handler(); + if (dconn->on_connect() != 0) { + if (upstream->on_downstream_abort_request(downstream, 503) != 0) { + delete handler; + } + return; + } + writecb(loop, w, revents); +} +} // namespace + +HttpDownstreamConnection::HttpDownstreamConnection( + DownstreamConnectionPool *dconn_pool, struct ev_loop *loop) + : DownstreamConnection(dconn_pool), + conn_(loop, -1, nullptr, get_config()->downstream_write_timeout, + get_config()->downstream_read_timeout, 0, 0, 0, 0, connectcb, + readcb, timeoutcb, this), + ioctrl_(&conn_.rlimit), response_htp_{0}, addr_idx_(0), + connected_(false) {} + +HttpDownstreamConnection::~HttpDownstreamConnection() { + // Downstream and DownstreamConnection may be deleted + // asynchronously. + if (downstream_) { + downstream_->release_downstream_connection(); + } +} + +int HttpDownstreamConnection::attach_downstream(Downstream *downstream) { + if (LOG_ENABLED(INFO)) { + DCLOG(INFO, this) << "Attaching to DOWNSTREAM:" << downstream; + } + + if (conn_.fd == -1) { + auto connect_blocker = client_handler_->get_connect_blocker(); + + if (connect_blocker->blocked()) { + if (LOG_ENABLED(INFO)) { + DCLOG(INFO, this) + << "Downstream connection was blocked by connect_blocker"; + } + return -1; + } + + auto worker = client_handler_->get_worker(); + auto worker_stat = worker->get_worker_stat(); + auto end = worker_stat->next_downstream; + for (;;) { + auto i = worker_stat->next_downstream; + ++worker_stat->next_downstream; + worker_stat->next_downstream %= get_config()->downstream_addrs.size(); + + conn_.fd = util::create_nonblock_socket( + get_config()->downstream_addrs[i].addr.storage.ss_family); + + if (conn_.fd == -1) { + auto error = errno; + DCLOG(WARN, this) << "socket() failed; errno=" << error; + + connect_blocker->on_failure(); + + return SHRPX_ERR_NETWORK; + } + + int rv; + rv = connect(conn_.fd, &get_config()->downstream_addrs[i].addr.sa, + get_config()->downstream_addrs[i].addrlen); + if (rv != 0 && errno != EINPROGRESS) { + auto error = errno; + DCLOG(WARN, this) << "connect() failed; errno=" << error; + + connect_blocker->on_failure(); + close(conn_.fd); + conn_.fd = -1; + + if (end == worker_stat->next_downstream) { + return SHRPX_ERR_NETWORK; + } + + // Try again with the next downstream server + continue; + } + + if (LOG_ENABLED(INFO)) { + DCLOG(INFO, this) << "Connecting to downstream server"; + } + + addr_idx_ = i; + + ev_io_set(&conn_.wev, conn_.fd, EV_WRITE); + ev_io_set(&conn_.rev, conn_.fd, EV_READ); + + conn_.wlimit.startw(); + + break; + } + } + + downstream_ = downstream; + + http_parser_init(&response_htp_, HTTP_RESPONSE); + response_htp_.data = downstream_; + + ev_set_cb(&conn_.rev, readcb); + + conn_.rt.repeat = get_config()->downstream_read_timeout; + ev_timer_again(conn_.loop, &conn_.rt); + // TODO we should have timeout for connection establishment + ev_timer_again(conn_.loop, &conn_.wt); + + return 0; +} + +int HttpDownstreamConnection::push_request_headers() { + const char *authority = nullptr, *host = nullptr; + auto downstream_hostport = + get_config()->downstream_addrs[addr_idx_].hostport.get(); + + if (!get_config()->no_host_rewrite && !get_config()->http2_proxy && + !get_config()->client_proxy && + downstream_->get_request_method() != "CONNECT") { + if (!downstream_->get_request_http2_authority().empty()) { + authority = downstream_hostport; + } + if (downstream_->get_request_header(http2::HD_HOST)) { + host = downstream_hostport; + } + } else { + if (!downstream_->get_request_http2_authority().empty()) { + authority = downstream_->get_request_http2_authority().c_str(); + } + auto h = downstream_->get_request_header(http2::HD_HOST); + if (h) { + host = h->value.c_str(); + } + } + + if (!authority && !host) { + // upstream is HTTP/1.0. We use backend server's host + // nonetheless. + host = downstream_hostport; + } + + if (authority) { + downstream_->set_request_downstream_host(authority); + } else { + downstream_->set_request_downstream_host(host); + } + + downstream_->assemble_request_cookie(); + + // Assume that method and request path do not contain \r\n. + std::string hdrs = downstream_->get_request_method(); + hdrs += ' '; + if (downstream_->get_request_method() == "CONNECT") { + if (authority) { + hdrs += authority; + } else { + hdrs += downstream_->get_request_path(); + } + } else if (get_config()->http2_proxy || get_config()->client_proxy) { + // Construct absolute-form request target because we are going to + // send a request to a HTTP/1 proxy. + if (downstream_->get_request_http2_scheme().empty()) { + // this comes from HTTP/1 upstream, use http scheme. We don't + // support https forward link yet. + hdrs += "http://"; + } else { + hdrs += downstream_->get_request_http2_scheme(); + hdrs += "://"; + } + if (authority) { + hdrs += authority; + } else { + hdrs += host; + } + + // Server-wide OPTIONS takes following form in proxy request: + // + // OPTIONS http://example.org HTTP/1.1 + // + // Notice that no slash after authority. See + // http://tools.ietf.org/html/rfc7230#section-5.3.4 + if (downstream_->get_request_path() != "*") { + hdrs += downstream_->get_request_path(); + } + } else { + // No proxy case. + hdrs += downstream_->get_request_path(); + } + hdrs += " HTTP/1.1\r\nHost: "; + if (authority) { + hdrs += authority; + } else { + hdrs += host; + } + hdrs += "\r\n"; + + http2::build_http1_headers_from_headers(hdrs, + downstream_->get_request_headers()); + + if (!downstream_->get_assembled_request_cookie().empty()) { + hdrs += "Cookie: "; + hdrs += downstream_->get_assembled_request_cookie(); + hdrs += "\r\n"; + } + + if (downstream_->get_request_method() != "CONNECT" && + downstream_->get_request_http2_expect_body() && + !downstream_->get_request_header(http2::HD_CONTENT_LENGTH)) { + + downstream_->set_chunked_request(true); + hdrs += "Transfer-Encoding: chunked\r\n"; + } + + if (downstream_->get_request_connection_close()) { + hdrs += "Connection: close\r\n"; + } + auto xff = downstream_->get_request_header(http2::HD_X_FORWARDED_FOR); + if (get_config()->add_x_forwarded_for) { + hdrs += "X-Forwarded-For: "; + if (xff && !get_config()->strip_incoming_x_forwarded_for) { + hdrs += (*xff).value; + hdrs += ", "; + } + hdrs += client_handler_->get_ipaddr(); + hdrs += "\r\n"; + } else if (xff && !get_config()->strip_incoming_x_forwarded_for) { + hdrs += "X-Forwarded-For: "; + hdrs += (*xff).value; + hdrs += "\r\n"; + } + if (!get_config()->http2_proxy && !get_config()->client_proxy && + downstream_->get_request_method() != "CONNECT") { + hdrs += "X-Forwarded-Proto: "; + if (!downstream_->get_request_http2_scheme().empty()) { + hdrs += downstream_->get_request_http2_scheme(); + hdrs += "\r\n"; + } else if (client_handler_->get_ssl()) { + hdrs += "https\r\n"; + } else { + hdrs += "http\r\n"; + } + } + auto expect = downstream_->get_request_header(http2::HD_EXPECT); + if (expect && !util::strifind((*expect).value.c_str(), "100-continue")) { + hdrs += "Expect: "; + hdrs += (*expect).value; + hdrs += "\r\n"; + } + auto via = downstream_->get_request_header(http2::HD_VIA); + if (get_config()->no_via) { + if (via) { + hdrs += "Via: "; + hdrs += (*via).value; + hdrs += "\r\n"; + } + } else { + hdrs += "Via: "; + if (via) { + hdrs += (*via).value; + hdrs += ", "; + } + hdrs += http::create_via_header_value(downstream_->get_request_major(), + downstream_->get_request_minor()); + hdrs += "\r\n"; + } + + hdrs += "\r\n"; + if (LOG_ENABLED(INFO)) { + const char *hdrp; + std::string nhdrs; + if (log_config()->errorlog_tty) { + nhdrs = http::colorizeHeaders(hdrs.c_str()); + hdrp = nhdrs.c_str(); + } else { + hdrp = hdrs.c_str(); + } + DCLOG(INFO, this) << "HTTP request headers. stream_id=" + << downstream_->get_stream_id() << "\n" << hdrp; + } + auto output = downstream_->get_request_buf(); + output->append(hdrs.c_str(), hdrs.size()); + + signal_write(); + + return 0; +} + +int HttpDownstreamConnection::push_upload_data_chunk(const uint8_t *data, + size_t datalen) { + auto chunked = downstream_->get_chunked_request(); + auto output = downstream_->get_request_buf(); + + if (chunked) { + auto chunk_size_hex = util::utox(datalen); + output->append(chunk_size_hex.c_str(), chunk_size_hex.size()); + output->append("\r\n"); + } + + output->append(data, datalen); + + if (chunked) { + output->append("\r\n"); + } + + signal_write(); + + return 0; +} + +int HttpDownstreamConnection::end_upload_data() { + if (!downstream_->get_chunked_request()) { + return 0; + } + + auto output = downstream_->get_request_buf(); + auto &trailers = downstream_->get_request_trailers(); + if (trailers.empty()) { + output->append("0\r\n\r\n"); + } else { + output->append("0\r\n"); + std::string trailer_part; + http2::build_http1_headers_from_headers(trailer_part, trailers); + output->append(trailer_part.c_str(), trailer_part.size()); + output->append("\r\n"); + } + + signal_write(); + + return 0; +} + +namespace { +void idle_readcb(struct ev_loop *loop, ev_io *w, int revents) { + auto conn = static_cast(w->data); + auto dconn = static_cast(conn->data); + if (LOG_ENABLED(INFO)) { + DCLOG(INFO, dconn) << "Idle connection EOF"; + } + auto dconn_pool = dconn->get_dconn_pool(); + dconn_pool->remove_downstream_connection(dconn); + // dconn was deleted +} +} // namespace + +namespace { +void idle_timeoutcb(struct ev_loop *loop, ev_timer *w, int revents) { + auto conn = static_cast(w->data); + auto dconn = static_cast(conn->data); + if (LOG_ENABLED(INFO)) { + DCLOG(INFO, dconn) << "Idle connection timeout"; + } + auto dconn_pool = dconn->get_dconn_pool(); + dconn_pool->remove_downstream_connection(dconn); + // dconn was deleted +} +} // namespace + +void HttpDownstreamConnection::detach_downstream(Downstream *downstream) { + if (LOG_ENABLED(INFO)) { + DCLOG(INFO, this) << "Detaching from DOWNSTREAM:" << downstream; + } + downstream_ = nullptr; + ioctrl_.force_resume_read(); + + conn_.rlimit.startw(); + conn_.wlimit.stopw(); + + ev_set_cb(&conn_.rev, idle_readcb); + + ev_timer_stop(conn_.loop, &conn_.wt); + + conn_.rt.repeat = get_config()->downstream_idle_read_timeout; + ev_set_cb(&conn_.rt, idle_timeoutcb); + ev_timer_again(conn_.loop, &conn_.rt); +} + +void HttpDownstreamConnection::pause_read(IOCtrlReason reason) { + ioctrl_.pause_read(reason); +} + +int HttpDownstreamConnection::resume_read(IOCtrlReason reason, + size_t consumed) { + if (!downstream_->response_buf_full()) { + ioctrl_.resume_read(reason); + } + + return 0; +} + +void HttpDownstreamConnection::force_resume_read() { + ioctrl_.force_resume_read(); +} + +namespace { +int htp_msg_begincb(http_parser *htp) { + auto downstream = static_cast(htp->data); + + if (downstream->get_response_state() != Downstream::INITIAL) { + return -1; + } + + return 0; +} +} // namespace + +namespace { +int htp_hdrs_completecb(http_parser *htp) { + auto downstream = static_cast(htp->data); + auto upstream = downstream->get_upstream(); + int rv; + + downstream->set_response_http_status(htp->status_code); + downstream->set_response_major(htp->http_major); + downstream->set_response_minor(htp->http_minor); + + if (downstream->index_response_headers() != 0) { + downstream->set_response_state(Downstream::MSG_BAD_HEADER); + return -1; + } + + if (downstream->get_non_final_response()) { + // Reset content-length because we reuse same Downstream for the + // next response. + downstream->set_response_content_length(-1); + // For non-final response code, we just call + // on_downstream_header_complete() without changing response + // state. + rv = upstream->on_downstream_header_complete(downstream); + + if (rv != 0) { + return -1; + } + + return 0; + } + + downstream->set_response_connection_close(!http_should_keep_alive(htp)); + downstream->set_response_state(Downstream::HEADER_COMPLETE); + downstream->inspect_http1_response(); + downstream->check_upgrade_fulfilled(); + if (downstream->get_upgraded()) { + // content-length must be ignored for upgraded connection. + downstream->set_response_content_length(-1); + downstream->set_response_connection_close(true); + // transfer-encoding not applied to upgraded connection + downstream->set_chunked_response(false); + } + if (upstream->on_downstream_header_complete(downstream) != 0) { + return -1; + } + + if (downstream->get_upgraded()) { + // Upgrade complete, read until EOF in both ends + if (upstream->resume_read(SHRPX_NO_BUFFER, downstream, 0) != 0) { + return -1; + } + downstream->set_request_state(Downstream::HEADER_COMPLETE); + if (LOG_ENABLED(INFO)) { + LOG(INFO) << "HTTP upgrade success. stream_id=" + << downstream->get_stream_id(); + } + } + + unsigned int status = downstream->get_response_http_status(); + // Ignore the response body. HEAD response may contain + // Content-Length or Transfer-Encoding: chunked. Some server send + // 304 status code with nonzero Content-Length, but without response + // body. See + // https://tools.ietf.org/html/rfc7230#section-3.3 + + // TODO It seems that the cases other than HEAD are handled by + // http-parser. Need test. + return downstream->get_request_method() == "HEAD" || + (100 <= status && status <= 199) || status == 204 || + status == 304 + ? 1 + : 0; +} +} // namespace + +namespace { +int htp_hdr_keycb(http_parser *htp, const char *data, size_t len) { + auto downstream = static_cast(htp->data); + + if (downstream->get_response_headers_sum() + len > + get_config()->header_field_buffer) { + if (LOG_ENABLED(INFO)) { + DLOG(INFO, downstream) << "Too large header block size=" + << downstream->get_response_headers_sum() + len; + } + return -1; + } + + if (downstream->get_response_state() == Downstream::INITIAL) { + if (downstream->get_response_header_key_prev()) { + downstream->append_last_response_header_key(data, len); + } else { + if (downstream->get_response_headers().size() >= + get_config()->max_header_fields) { + if (LOG_ENABLED(INFO)) { + DLOG(INFO, downstream) + << "Too many header field num=" + << downstream->get_response_headers().size() + 1; + } + return -1; + } + downstream->add_response_header(std::string(data, len), ""); + } + } else { + // trailer part + if (downstream->get_response_trailer_key_prev()) { + downstream->append_last_response_trailer_key(data, len); + } else { + if (downstream->get_response_headers().size() >= + get_config()->max_header_fields) { + if (LOG_ENABLED(INFO)) { + DLOG(INFO, downstream) + << "Too many header field num=" + << downstream->get_response_headers().size() + 1; + } + return -1; + } + downstream->add_response_trailer(std::string(data, len), ""); + } + } + return 0; +} +} // namespace + +namespace { +int htp_hdr_valcb(http_parser *htp, const char *data, size_t len) { + auto downstream = static_cast(htp->data); + if (downstream->get_response_headers_sum() + len > + get_config()->header_field_buffer) { + if (LOG_ENABLED(INFO)) { + DLOG(INFO, downstream) << "Too large header block size=" + << downstream->get_response_headers_sum() + len; + } + return -1; + } + if (downstream->get_response_state() == Downstream::INITIAL) { + if (downstream->get_response_header_key_prev()) { + downstream->set_last_response_header_value(data, len); + } else { + downstream->append_last_response_header_value(data, len); + } + } else { + if (downstream->get_response_trailer_key_prev()) { + downstream->set_last_response_trailer_value(data, len); + } else { + downstream->append_last_response_trailer_value(data, len); + } + } + return 0; +} +} // namespace + +namespace { +int htp_bodycb(http_parser *htp, const char *data, size_t len) { + auto downstream = static_cast(htp->data); + + downstream->add_response_bodylen(len); + + return downstream->get_upstream()->on_downstream_body( + downstream, reinterpret_cast(data), len, true); +} +} // namespace + +namespace { +int htp_msg_completecb(http_parser *htp) { + auto downstream = static_cast(htp->data); + + if (downstream->get_non_final_response()) { + downstream->reset_response(); + + return 0; + } + + downstream->set_response_state(Downstream::MSG_COMPLETE); + // Block reading another response message from (broken?) + // server. This callback is not called if the connection is + // tunneled. + downstream->pause_read(SHRPX_MSG_BLOCK); + return downstream->get_upstream()->on_downstream_body_complete(downstream); +} +} // namespace + +namespace { +http_parser_settings htp_hooks = { + htp_msg_begincb, // http_cb on_message_begin; + nullptr, // http_data_cb on_url; + nullptr, // http_data_cb on_status; + htp_hdr_keycb, // http_data_cb on_header_field; + htp_hdr_valcb, // http_data_cb on_header_value; + htp_hdrs_completecb, // http_cb on_headers_complete; + htp_bodycb, // http_data_cb on_body; + htp_msg_completecb // http_cb on_message_complete; +}; +} // namespace + +int HttpDownstreamConnection::on_read() { + if (!connected_) { + return 0; + } + + ev_timer_again(conn_.loop, &conn_.rt); + std::array buf; + int rv; + + if (downstream_->get_upgraded()) { + // For upgraded connection, just pass data to the upstream. + for (;;) { + auto nread = conn_.read_clear(buf.data(), buf.size()); + + if (nread == 0) { + return 0; + } + + if (nread < 0) { + return nread; + } + + rv = downstream_->get_upstream()->on_downstream_body( + downstream_, buf.data(), nread, true); + if (rv != 0) { + return rv; + } + + if (downstream_->response_buf_full()) { + downstream_->pause_read(SHRPX_NO_BUFFER); + return 0; + } + } + } + + for (;;) { + auto nread = conn_.read_clear(buf.data(), buf.size()); + + if (nread == 0) { + return 0; + } + + if (nread < 0) { + return nread; + } + + auto nproc = + http_parser_execute(&response_htp_, &htp_hooks, + reinterpret_cast(buf.data()), nread); + + auto htperr = HTTP_PARSER_ERRNO(&response_htp_); + + if (htperr != HPE_OK) { + if (LOG_ENABLED(INFO)) { + DCLOG(INFO, this) << "HTTP parser failure: " + << "(" << http_errno_name(htperr) << ") " + << http_errno_description(htperr); + } + + return -1; + } + + if (nproc != static_cast(nread)) { + if (LOG_ENABLED(INFO)) { + DCLOG(INFO, this) << "nproc != nread"; + } + return -1; + } + + if (downstream_->response_buf_full()) { + downstream_->pause_read(SHRPX_NO_BUFFER); + return 0; + } + } +} + +int HttpDownstreamConnection::on_write() { + if (!connected_) { + return 0; + } + + ev_timer_again(conn_.loop, &conn_.rt); + + auto upstream = downstream_->get_upstream(); + auto input = downstream_->get_request_buf(); + + std::array iov; + + while (input->rleft() > 0) { + auto iovcnt = input->riovec(iov.data(), iov.size()); + + auto nwrite = conn_.writev_clear(iov.data(), iovcnt); + + if (nwrite == 0) { + return 0; + } + + if (nwrite < 0) { + return nwrite; + } + + input->drain(nwrite); + } + + conn_.wlimit.stopw(); + ev_timer_stop(conn_.loop, &conn_.wt); + + if (input->rleft() == 0) { + upstream->resume_read(SHRPX_NO_BUFFER, downstream_, + downstream_->get_request_datalen()); + } + + return 0; +} + +int HttpDownstreamConnection::on_connect() { + auto connect_blocker = client_handler_->get_connect_blocker(); + + if (!util::check_socket_connected(conn_.fd)) { + conn_.wlimit.stopw(); + + if (LOG_ENABLED(INFO)) { + DLOG(INFO, this) << "downstream connect failed"; + } + + return -1; + } + + connected_ = true; + + connect_blocker->on_success(); + + conn_.rlimit.startw(); + ev_set_cb(&conn_.wev, writecb); + + return 0; +} + +void HttpDownstreamConnection::on_upstream_change(Upstream *upstream) {} + +void HttpDownstreamConnection::signal_write() { conn_.wlimit.startw(); } + +} // namespace shrpx diff --git a/src/shrpx_http_downstream_connection.h b/src/shrpx_http_downstream_connection.h new file mode 100644 index 0000000..02480d5 --- /dev/null +++ b/src/shrpx_http_downstream_connection.h @@ -0,0 +1,78 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef SHRPX_HTTP_DOWNSTREAM_CONNECTION_H +#define SHRPX_HTTP_DOWNSTREAM_CONNECTION_H + +#include "shrpx.h" + +#include "http-parser/http_parser.h" + +#include "shrpx_downstream_connection.h" +#include "shrpx_io_control.h" +#include "shrpx_connection.h" + +namespace shrpx { + +class DownstreamConnectionPool; + +class HttpDownstreamConnection : public DownstreamConnection { +public: + HttpDownstreamConnection(DownstreamConnectionPool *dconn_pool, + struct ev_loop *loop); + virtual ~HttpDownstreamConnection(); + virtual int attach_downstream(Downstream *downstream); + virtual void detach_downstream(Downstream *downstream); + + virtual int push_request_headers(); + virtual int push_upload_data_chunk(const uint8_t *data, size_t datalen); + virtual int end_upload_data(); + + virtual void pause_read(IOCtrlReason reason); + virtual int resume_read(IOCtrlReason reason, size_t consumed); + virtual void force_resume_read(); + + virtual int on_read(); + virtual int on_write(); + + virtual void on_upstream_change(Upstream *upstream); + virtual int on_priority_change(int32_t pri) { return 0; } + + virtual bool poolable() const { return true; } + + int on_connect(); + void signal_write(); + +private: + Connection conn_; + IOControl ioctrl_; + http_parser response_htp_; + // index of get_config()->downstream_addrs this object is using + size_t addr_idx_; + bool connected_; +}; + +} // namespace shrpx + +#endif // SHRPX_HTTP_DOWNSTREAM_CONNECTION_H diff --git a/src/shrpx_https_upstream.cc b/src/shrpx_https_upstream.cc new file mode 100644 index 0000000..674f17b --- /dev/null +++ b/src/shrpx_https_upstream.cc @@ -0,0 +1,983 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "shrpx_https_upstream.h" + +#include +#include +#include + +#include "shrpx_client_handler.h" +#include "shrpx_downstream.h" +#include "shrpx_downstream_connection.h" +#include "shrpx_http.h" +#include "shrpx_config.h" +#include "shrpx_error.h" +#include "shrpx_log_config.h" +#include "shrpx_worker.h" +#include "http2.h" +#include "util.h" +#include "template.h" + +using namespace nghttp2; + +namespace shrpx { + +HttpsUpstream::HttpsUpstream(ClientHandler *handler) + : handler_(handler), current_header_length_(0), + ioctrl_(handler->get_rlimit()) { + http_parser_init(&htp_, HTTP_REQUEST); + htp_.data = this; +} + +HttpsUpstream::~HttpsUpstream() {} + +void HttpsUpstream::reset_current_header_length() { + current_header_length_ = 0; +} + +namespace { +int htp_msg_begin(http_parser *htp) { + auto upstream = static_cast(htp->data); + if (LOG_ENABLED(INFO)) { + ULOG(INFO, upstream) << "HTTP request started"; + } + upstream->reset_current_header_length(); + + auto handler = upstream->get_client_handler(); + + // TODO specify 0 as priority for now + upstream->attach_downstream( + make_unique(upstream, handler->get_mcpool(), 0, 0)); + return 0; +} +} // namespace + +namespace { +int htp_uricb(http_parser *htp, const char *data, size_t len) { + auto upstream = static_cast(htp->data); + auto downstream = upstream->get_downstream(); + if (downstream->get_request_headers_sum() + len > + get_config()->header_field_buffer) { + if (LOG_ENABLED(INFO)) { + ULOG(INFO, upstream) << "Too large URI size=" + << downstream->get_request_headers_sum() + len; + } + assert(downstream->get_request_state() == Downstream::INITIAL); + downstream->set_request_state(Downstream::HTTP1_REQUEST_HEADER_TOO_LARGE); + return -1; + } + downstream->add_request_headers_sum(len); + downstream->append_request_path(data, len); + return 0; +} +} // namespace + +namespace { +int htp_hdr_keycb(http_parser *htp, const char *data, size_t len) { + auto upstream = static_cast(htp->data); + auto downstream = upstream->get_downstream(); + if (downstream->get_request_headers_sum() + len > + get_config()->header_field_buffer) { + if (LOG_ENABLED(INFO)) { + ULOG(INFO, upstream) << "Too large header block size=" + << downstream->get_request_headers_sum() + len; + } + if (downstream->get_request_state() == Downstream::INITIAL) { + downstream->set_request_state(Downstream::HTTP1_REQUEST_HEADER_TOO_LARGE); + } + return -1; + } + if (downstream->get_request_state() == Downstream::INITIAL) { + if (downstream->get_request_header_key_prev()) { + downstream->append_last_request_header_key(data, len); + } else { + if (downstream->get_request_headers().size() >= + get_config()->max_header_fields) { + if (LOG_ENABLED(INFO)) { + ULOG(INFO, upstream) << "Too many header field num=" + << downstream->get_request_headers().size() + 1; + } + downstream->set_request_state( + Downstream::HTTP1_REQUEST_HEADER_TOO_LARGE); + return -1; + } + downstream->add_request_header(std::string(data, len), ""); + } + } else { + // trailer part + if (downstream->get_request_trailer_key_prev()) { + downstream->append_last_request_trailer_key(data, len); + } else { + if (downstream->get_request_headers().size() >= + get_config()->max_header_fields) { + if (LOG_ENABLED(INFO)) { + ULOG(INFO, upstream) << "Too many header field num=" + << downstream->get_request_headers().size() + 1; + } + return -1; + } + downstream->add_request_trailer(std::string(data, len), ""); + } + } + return 0; +} +} // namespace + +namespace { +int htp_hdr_valcb(http_parser *htp, const char *data, size_t len) { + auto upstream = static_cast(htp->data); + auto downstream = upstream->get_downstream(); + if (downstream->get_request_headers_sum() + len > + get_config()->header_field_buffer) { + if (LOG_ENABLED(INFO)) { + ULOG(INFO, upstream) << "Too large header block size=" + << downstream->get_request_headers_sum() + len; + } + if (downstream->get_request_state() == Downstream::INITIAL) { + downstream->set_request_state(Downstream::HTTP1_REQUEST_HEADER_TOO_LARGE); + } + return -1; + } + if (downstream->get_request_state() == Downstream::INITIAL) { + if (downstream->get_request_header_key_prev()) { + downstream->set_last_request_header_value(data, len); + } else { + downstream->append_last_request_header_value(data, len); + } + } else { + if (downstream->get_request_trailer_key_prev()) { + downstream->set_last_request_trailer_value(data, len); + } else { + downstream->append_last_request_trailer_value(data, len); + } + } + return 0; +} +} // namespace + +namespace { +void rewrite_request_host_path_from_uri(Downstream *downstream, const char *uri, + http_parser_url &u) { + assert(u.field_set & (1 << UF_HOST)); + + // As per https://tools.ietf.org/html/rfc7230#section-5.4, we + // rewrite host header field with authority component. + std::string authority; + http2::copy_url_component(authority, &u, UF_HOST, uri); + // TODO properly check IPv6 numeric address + if (authority.find(':') != std::string::npos) { + authority = '[' + authority; + authority += ']'; + } + if (u.field_set & (1 << UF_PORT)) { + authority += ':'; + authority += util::utos(u.port); + } + downstream->set_request_http2_authority(authority); + + std::string path; + if (u.field_set & (1 << UF_PATH)) { + http2::copy_url_component(path, &u, UF_PATH, uri); + } else if (downstream->get_request_method() == "OPTIONS") { + // Server-wide OPTIONS takes following form in proxy request: + // + // OPTIONS http://example.org HTTP/1.1 + // + // Notice that no slash after authority. See + // http://tools.ietf.org/html/rfc7230#section-5.3.4 + downstream->set_request_path("*"); + // we ignore query component here + return; + } else { + path = "/"; + } + if (u.field_set & (1 << UF_QUERY)) { + auto &fdata = u.field_data[UF_QUERY]; + path += '?'; + path.append(uri + fdata.off, fdata.len); + } + downstream->set_request_path(std::move(path)); + if (get_config()->http2_proxy || get_config()->client_proxy) { + std::string scheme; + http2::copy_url_component(scheme, &u, UF_SCHEMA, uri); + downstream->set_request_http2_scheme(std::move(scheme)); + } +} +} // namespace + +namespace { +int htp_hdrs_completecb(http_parser *htp) { + int rv; + auto upstream = static_cast(htp->data); + if (LOG_ENABLED(INFO)) { + ULOG(INFO, upstream) << "HTTP request headers completed"; + } + auto downstream = upstream->get_downstream(); + + downstream->set_request_method( + http_method_str((enum http_method)htp->method)); + downstream->set_request_major(htp->http_major); + downstream->set_request_minor(htp->http_minor); + + downstream->set_request_connection_close(!http_should_keep_alive(htp)); + + if (LOG_ENABLED(INFO)) { + std::stringstream ss; + ss << downstream->get_request_method() << " " + << downstream->get_request_path() << " " + << "HTTP/" << downstream->get_request_major() << "." + << downstream->get_request_minor() << "\n"; + const auto &headers = downstream->get_request_headers(); + for (size_t i = 0; i < headers.size(); ++i) { + ss << TTY_HTTP_HD << headers[i].name << TTY_RST << ": " + << headers[i].value << "\n"; + } + ULOG(INFO, upstream) << "HTTP request headers\n" << ss.str(); + } + + if (downstream->index_request_headers() != 0) { + return -1; + } + + if (downstream->get_request_major() == 1 && + downstream->get_request_minor() == 1 && + !downstream->get_request_header(http2::HD_HOST)) { + return -1; + } + + downstream->inspect_http1_request(); + + if (downstream->get_request_method() != "CONNECT") { + http_parser_url u{}; + // make a copy of request path, since we may set request path + // while we are refering to original request path. + auto uri = downstream->get_request_path(); + rv = http_parser_parse_url(uri.c_str(), + downstream->get_request_path().size(), 0, &u); + if (rv != 0) { + // Expect to respond with 400 bad request + return -1; + } + // checking UF_HOST could be redundant, but just in case ... + if (!(u.field_set & (1 << UF_SCHEMA)) || !(u.field_set & (1 << UF_HOST))) { + if (get_config()->http2_proxy || get_config()->client_proxy) { + // Request URI should be absolute-form for client proxy mode + return -1; + } + } else { + rewrite_request_host_path_from_uri(downstream, uri.c_str(), u); + } + } + + rv = downstream->attach_downstream_connection( + upstream->get_client_handler()->get_downstream_connection()); + + if (rv != 0) { + downstream->set_request_state(Downstream::CONNECT_FAIL); + + return -1; + } + + rv = downstream->push_request_headers(); + + if (rv != 0) { + return -1; + } + + downstream->set_request_state(Downstream::HEADER_COMPLETE); + + return 0; +} +} // namespace + +namespace { +int htp_bodycb(http_parser *htp, const char *data, size_t len) { + int rv; + auto upstream = static_cast(htp->data); + auto downstream = upstream->get_downstream(); + rv = downstream->push_upload_data_chunk( + reinterpret_cast(data), len); + if (rv != 0) { + return -1; + } + return 0; +} +} // namespace + +namespace { +int htp_msg_completecb(http_parser *htp) { + int rv; + auto upstream = static_cast(htp->data); + if (LOG_ENABLED(INFO)) { + ULOG(INFO, upstream) << "HTTP request completed"; + } + auto handler = upstream->get_client_handler(); + auto downstream = upstream->get_downstream(); + downstream->set_request_state(Downstream::MSG_COMPLETE); + rv = downstream->end_upload_data(); + if (rv != 0) { + return -1; + } + + if (handler->get_http2_upgrade_allowed() && + downstream->get_http2_upgrade_request() && + handler->perform_http2_upgrade(upstream) != 0) { + if (LOG_ENABLED(INFO)) { + ULOG(INFO, upstream) << "HTTP Upgrade to HTTP/2 failed"; + } + } + + // Stop further processing to complete this request + http_parser_pause(htp, 1); + return 0; +} +} // namespace + +namespace { +http_parser_settings htp_hooks = { + htp_msg_begin, // http_cb on_message_begin; + htp_uricb, // http_data_cb on_url; + nullptr, // http_data_cb on_status; + htp_hdr_keycb, // http_data_cb on_header_field; + htp_hdr_valcb, // http_data_cb on_header_value; + htp_hdrs_completecb, // http_cb on_headers_complete; + htp_bodycb, // http_data_cb on_body; + htp_msg_completecb // http_cb on_message_complete; +}; +} // namespace + +// on_read() does not consume all available data in input buffer if +// one http request is fully received. +int HttpsUpstream::on_read() { + auto rb = handler_->get_rb(); + auto rlimit = handler_->get_rlimit(); + auto downstream = get_downstream(); + + if (rb->rleft() == 0) { + return 0; + } + + // downstream can be nullptr here, because it is initialized in the + // callback chain called by http_parser_execute() + if (downstream && downstream->get_upgraded()) { + + auto rv = downstream->push_upload_data_chunk(rb->pos, rb->rleft()); + + if (rv != 0) { + return -1; + } + + rb->reset(); + rlimit->startw(); + + if (downstream->request_buf_full()) { + if (LOG_ENABLED(INFO)) { + ULOG(INFO, this) << "Downstream request buf is full"; + } + pause_read(SHRPX_NO_BUFFER); + + return 0; + } + + return 0; + } + + if (downstream) { + // To avoid reading next pipelined request + switch (downstream->get_request_state()) { + case Downstream::INITIAL: + case Downstream::HEADER_COMPLETE: + break; + default: + return 0; + } + } + + // http_parser_execute() does nothing once it entered error state. + auto nread = http_parser_execute( + &htp_, &htp_hooks, reinterpret_cast(rb->pos), rb->rleft()); + + rb->drain(nread); + rlimit->startw(); + + // Well, actually header length + some body bytes + current_header_length_ += nread; + + // Get downstream again because it may be initialized in http parser + // execution + downstream = get_downstream(); + + auto htperr = HTTP_PARSER_ERRNO(&htp_); + + if (htperr == HPE_PAUSED) { + return 0; + } + + if (htperr != HPE_OK) { + if (LOG_ENABLED(INFO)) { + ULOG(INFO, this) << "HTTP parse failure: " + << "(" << http_errno_name(htperr) << ") " + << http_errno_description(htperr); + } + + if (downstream && downstream->get_response_state() != Downstream::INITIAL) { + handler_->set_should_close_after_write(true); + handler_->signal_write(); + return 0; + } + + unsigned int status_code; + + if (downstream) { + if (downstream->get_request_state() == Downstream::CONNECT_FAIL) { + status_code = 503; + } else if (downstream->get_request_state() == + Downstream::HTTP1_REQUEST_HEADER_TOO_LARGE) { + status_code = 431; + } else { + status_code = 400; + } + } else { + status_code = 400; + } + + error_reply(status_code); + + handler_->signal_write(); + + return 0; + } + + // downstream can be NULL here. + if (downstream && downstream->request_buf_full()) { + if (LOG_ENABLED(INFO)) { + ULOG(INFO, this) << "Downstream request buffer is full"; + } + + pause_read(SHRPX_NO_BUFFER); + + return 0; + } + + return 0; +} + +int HttpsUpstream::on_write() { + auto downstream = get_downstream(); + if (!downstream) { + return 0; + } + auto wb = handler_->get_wb(); + if (wb->wleft() == 0) { + return 0; + } + + auto dconn = downstream->get_downstream_connection(); + auto output = downstream->get_response_buf(); + + if (output->rleft() == 0 && dconn && + downstream->get_response_state() != Downstream::MSG_COMPLETE) { + if (downstream->resume_read(SHRPX_NO_BUFFER, + downstream->get_response_datalen()) != 0) { + return -1; + } + + if (downstream_read(dconn) != 0) { + return -1; + } + } + + auto n = output->remove(wb->last, wb->wleft()); + wb->write(n); + + if (wb->rleft() > 0) { + return 0; + } + + // We need to postpone detachment until all data are sent so that + // we can notify nghttp2 library all data consumed. + if (downstream->get_response_state() == Downstream::MSG_COMPLETE) { + if (downstream->get_response_connection_close()) { + // Connection close + downstream->pop_downstream_connection(); + // dconn was deleted + } else { + // Keep-alive + downstream->detach_downstream_connection(); + } + // We need this if response ends before request. + if (downstream->get_request_state() == Downstream::MSG_COMPLETE) { + delete_downstream(); + return resume_read(SHRPX_NO_BUFFER, nullptr, 0); + } + } + + return downstream->resume_read(SHRPX_NO_BUFFER, + downstream->get_response_datalen()); +} + +int HttpsUpstream::on_event() { return 0; } + +ClientHandler *HttpsUpstream::get_client_handler() const { return handler_; } + +void HttpsUpstream::pause_read(IOCtrlReason reason) { + ioctrl_.pause_read(reason); +} + +int HttpsUpstream::resume_read(IOCtrlReason reason, Downstream *downstream, + size_t consumed) { + // downstream could be nullptr + if (downstream && downstream->request_buf_full()) { + return 0; + } + if (ioctrl_.resume_read(reason)) { + // Process remaining data in input buffer here because these bytes + // are not notified by readcb until new data arrive. + http_parser_pause(&htp_, 0); + return on_read(); + } + + return 0; +} + +int HttpsUpstream::downstream_read(DownstreamConnection *dconn) { + auto downstream = dconn->get_downstream(); + int rv; + + rv = downstream->on_read(); + + if (rv == SHRPX_ERR_EOF) { + return downstream_eof(dconn); + } + + if (rv < 0) { + return downstream_error(dconn, Downstream::EVENT_ERROR); + } + + if (downstream->get_response_state() == Downstream::MSG_RESET) { + return -1; + } + + if (downstream->get_response_state() == Downstream::MSG_BAD_HEADER) { + error_reply(502); + downstream->pop_downstream_connection(); + goto end; + } + + // Detach downstream connection early so that it could be reused + // without hitting server's request timeout. + if (downstream->get_response_state() == Downstream::MSG_COMPLETE && + !downstream->get_response_connection_close()) { + // Keep-alive + downstream->detach_downstream_connection(); + } + +end: + handler_->signal_write(); + + return 0; +} + +int HttpsUpstream::downstream_write(DownstreamConnection *dconn) { + int rv; + rv = dconn->on_write(); + if (rv == SHRPX_ERR_NETWORK) { + return downstream_error(dconn, Downstream::EVENT_ERROR); + } + + if (rv != 0) { + return -1; + } + + return 0; +} + +int HttpsUpstream::downstream_eof(DownstreamConnection *dconn) { + auto downstream = dconn->get_downstream(); + + if (LOG_ENABLED(INFO)) { + DCLOG(INFO, dconn) << "EOF"; + } + + if (downstream->get_response_state() == Downstream::MSG_COMPLETE) { + goto end; + } + + if (downstream->get_response_state() == Downstream::HEADER_COMPLETE) { + // Server may indicate the end of the request by EOF + if (LOG_ENABLED(INFO)) { + DCLOG(INFO, dconn) << "The end of the response body was indicated by " + << "EOF"; + } + on_downstream_body_complete(downstream); + downstream->set_response_state(Downstream::MSG_COMPLETE); + downstream->pop_downstream_connection(); + goto end; + } + + if (downstream->get_response_state() == Downstream::INITIAL) { + // we did not send any response headers, so we can reply error + // message. + if (LOG_ENABLED(INFO)) { + DCLOG(INFO, dconn) << "Return error reply"; + } + error_reply(502); + downstream->pop_downstream_connection(); + goto end; + } + + // Otherwise, we don't know how to recover from this situation. Just + // drop connection. + return -1; +end: + handler_->signal_write(); + + return 0; +} + +int HttpsUpstream::downstream_error(DownstreamConnection *dconn, int events) { + auto downstream = dconn->get_downstream(); + if (LOG_ENABLED(INFO)) { + if (events & Downstream::EVENT_ERROR) { + DCLOG(INFO, dconn) << "Network error/general error"; + } else { + DCLOG(INFO, dconn) << "Timeout"; + } + } + if (downstream->get_response_state() != Downstream::INITIAL) { + return -1; + } + + unsigned int status; + if (events & Downstream::EVENT_TIMEOUT) { + status = 504; + } else { + status = 502; + } + error_reply(status); + + downstream->pop_downstream_connection(); + + handler_->signal_write(); + return 0; +} + +void HttpsUpstream::error_reply(unsigned int status_code) { + auto html = http::create_error_html(status_code); + auto downstream = get_downstream(); + + if (!downstream) { + attach_downstream( + make_unique(this, handler_->get_mcpool(), 1, 1)); + downstream = get_downstream(); + } + + downstream->set_response_http_status(status_code); + // we are going to close connection for both frontend and backend in + // error condition. This is safest option. + downstream->set_response_connection_close(true); + handler_->set_should_close_after_write(true); + + auto output = downstream->get_response_buf(); + + output->append("HTTP/1.1 "); + auto status_str = http2::get_status_string(status_code); + output->append(status_str.c_str(), status_str.size()); + output->append("\r\nServer: "); + output->append(get_config()->server_name, strlen(get_config()->server_name)); + output->append("\r\nContent-Length: "); + auto cl = util::utos(html.size()); + output->append(cl.c_str(), cl.size()); + output->append("\r\nContent-Type: text/html; " + "charset=UTF-8\r\nConnection: close\r\n\r\n"); + output->append(html.c_str(), html.size()); + + downstream->add_response_sent_bodylen(html.size()); + downstream->set_response_state(Downstream::MSG_COMPLETE); +} + +void HttpsUpstream::attach_downstream(std::unique_ptr downstream) { + assert(!downstream_); + downstream_ = std::move(downstream); +} + +void HttpsUpstream::delete_downstream() { + if (downstream_ && downstream_->accesslog_ready()) { + handler_->write_accesslog(downstream_.get()); + } + + downstream_.reset(); +} + +Downstream *HttpsUpstream::get_downstream() const { return downstream_.get(); } + +std::unique_ptr HttpsUpstream::pop_downstream() { + return std::unique_ptr(downstream_.release()); +} + +int HttpsUpstream::on_downstream_header_complete(Downstream *downstream) { + if (LOG_ENABLED(INFO)) { + if (downstream->get_non_final_response()) { + DLOG(INFO, downstream) << "HTTP non-final response header"; + } else { + DLOG(INFO, downstream) << "HTTP response header completed"; + } + } + + std::string hdrs = "HTTP/"; + hdrs += util::utos(downstream->get_request_major()); + hdrs += "."; + hdrs += util::utos(downstream->get_request_minor()); + hdrs += " "; + hdrs += http2::get_status_string(downstream->get_response_http_status()); + hdrs += "\r\n"; + + if (!get_config()->http2_proxy && !get_config()->client_proxy && + !get_config()->no_location_rewrite) { + downstream->rewrite_location_response_header( + get_client_handler()->get_upstream_scheme()); + } + + http2::build_http1_headers_from_headers(hdrs, + downstream->get_response_headers()); + + auto output = downstream->get_response_buf(); + + if (downstream->get_non_final_response()) { + hdrs += "\r\n"; + + if (LOG_ENABLED(INFO)) { + log_response_headers(hdrs); + } + + output->append(hdrs.c_str(), hdrs.size()); + + downstream->clear_response_headers(); + + return 0; + } + + auto worker = handler_->get_worker(); + + // after graceful shutdown commenced, add connection: close header + // field. + if (worker->get_graceful_shutdown()) { + downstream->set_response_connection_close(true); + } + + // We check downstream->get_response_connection_close() in case when + // the Content-Length is not available. + if (!downstream->get_request_connection_close() && + !downstream->get_response_connection_close()) { + if (downstream->get_request_major() <= 0 || + downstream->get_request_minor() <= 0) { + // We add this header for HTTP/1.0 or HTTP/0.9 clients + hdrs += "Connection: Keep-Alive\r\n"; + } + } else if (!downstream->get_upgraded() || + downstream->get_request_method() != "CONNECT") { + hdrs += "Connection: close\r\n"; + } + + if (!downstream->get_response_header(http2::HD_ALT_SVC)) { + // We won't change or alter alt-svc from backend for now + if (!get_config()->altsvcs.empty()) { + hdrs += "Alt-Svc: "; + + for (auto &altsvc : get_config()->altsvcs) { + hdrs += util::percent_encode_token(altsvc.protocol_id); + hdrs += "=\""; + hdrs += util::quote_string(std::string(altsvc.host, altsvc.host_len)); + hdrs += ":"; + hdrs += util::utos(altsvc.port); + hdrs += "\", "; + } + + hdrs[hdrs.size() - 2] = '\r'; + hdrs[hdrs.size() - 1] = '\n'; + } + } + + if (!get_config()->http2_proxy && !get_config()->client_proxy) { + hdrs += "Server: "; + hdrs += get_config()->server_name; + hdrs += "\r\n"; + } else { + auto server = downstream->get_response_header(http2::HD_SERVER); + if (server) { + hdrs += "Server: "; + hdrs += (*server).value; + hdrs += "\r\n"; + } + } + + auto via = downstream->get_response_header(http2::HD_VIA); + if (get_config()->no_via) { + if (via) { + hdrs += "Via: "; + hdrs += (*via).value; + hdrs += "\r\n"; + } + } else { + hdrs += "Via: "; + if (via) { + hdrs += (*via).value; + hdrs += ", "; + } + hdrs += http::create_via_header_value(downstream->get_response_major(), + downstream->get_response_minor()); + hdrs += "\r\n"; + } + + for (auto &p : get_config()->add_response_headers) { + hdrs += p.first; + hdrs += ": "; + hdrs += p.second; + hdrs += "\r\n"; + } + + hdrs += "\r\n"; + + if (LOG_ENABLED(INFO)) { + log_response_headers(hdrs); + } + + output->append(hdrs.c_str(), hdrs.size()); + + return 0; +} + +int HttpsUpstream::on_downstream_body(Downstream *downstream, + const uint8_t *data, size_t len, + bool flush) { + if (len == 0) { + return 0; + } + auto output = downstream->get_response_buf(); + if (downstream->get_chunked_response()) { + auto chunk_size_hex = util::utox(len); + chunk_size_hex += "\r\n"; + + output->append(chunk_size_hex.c_str(), chunk_size_hex.size()); + } + output->append(data, len); + + downstream->add_response_sent_bodylen(len); + + if (downstream->get_chunked_response()) { + output->append("\r\n"); + } + return 0; +} + +int HttpsUpstream::on_downstream_body_complete(Downstream *downstream) { + if (downstream->get_chunked_response()) { + auto output = downstream->get_response_buf(); + auto &trailers = downstream->get_response_trailers(); + if (trailers.empty()) { + output->append("0\r\n\r\n"); + } else { + output->append("0\r\n"); + std::string trailer_part; + http2::build_http1_headers_from_headers(trailer_part, trailers); + output->append(trailer_part.c_str(), trailer_part.size()); + output->append("\r\n"); + } + } + if (LOG_ENABLED(INFO)) { + DLOG(INFO, downstream) << "HTTP response completed"; + } + + if (!downstream->validate_response_bodylen()) { + downstream->set_response_connection_close(true); + } + + if (downstream->get_request_connection_close() || + downstream->get_response_connection_close()) { + auto handler = get_client_handler(); + handler->set_should_close_after_write(true); + } + return 0; +} + +int HttpsUpstream::on_downstream_abort_request(Downstream *downstream, + unsigned int status_code) { + error_reply(status_code); + handler_->signal_write(); + return 0; +} + +void HttpsUpstream::log_response_headers(const std::string &hdrs) const { + const char *hdrp; + std::string nhdrs; + if (log_config()->errorlog_tty) { + nhdrs = http::colorizeHeaders(hdrs.c_str()); + hdrp = nhdrs.c_str(); + } else { + hdrp = hdrs.c_str(); + } + ULOG(INFO, this) << "HTTP response headers\n" << hdrp; +} + +void HttpsUpstream::on_handler_delete() { + if (downstream_ && downstream_->accesslog_ready()) { + handler_->write_accesslog(downstream_.get()); + } +} + +int HttpsUpstream::on_downstream_reset(bool no_retry) { + int rv; + + if (!downstream_->request_submission_ready()) { + // Return error so that caller can delete handler + return -1; + } + + downstream_->pop_downstream_connection(); + + downstream_->add_retry(); + + if (no_retry || downstream_->no_more_retry()) { + goto fail; + } + + rv = downstream_->attach_downstream_connection( + handler_->get_downstream_connection()); + if (rv != 0) { + goto fail; + } + + return 0; + +fail: + if (on_downstream_abort_request(downstream_.get(), 503) != 0) { + return -1; + } + downstream_->pop_downstream_connection(); + + return 0; +} + +} // namespace shrpx diff --git a/src/shrpx_https_upstream.h b/src/shrpx_https_upstream.h new file mode 100644 index 0000000..3b9d532 --- /dev/null +++ b/src/shrpx_https_upstream.h @@ -0,0 +1,91 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef SHRPX_HTTPS_UPSTREAM_H +#define SHRPX_HTTPS_UPSTREAM_H + +#include "shrpx.h" + +#include +#include + +#include "http-parser/http_parser.h" + +#include "shrpx_upstream.h" +#include "memchunk.h" + +using namespace nghttp2; + +namespace shrpx { + +class ClientHandler; + +class HttpsUpstream : public Upstream { +public: + HttpsUpstream(ClientHandler *handler); + virtual ~HttpsUpstream(); + virtual int on_read(); + virtual int on_write(); + virtual int on_event(); + virtual int on_downstream_abort_request(Downstream *downstream, + unsigned int status_code); + virtual ClientHandler *get_client_handler() const; + + virtual int downstream_read(DownstreamConnection *dconn); + virtual int downstream_write(DownstreamConnection *dconn); + virtual int downstream_eof(DownstreamConnection *dconn); + virtual int downstream_error(DownstreamConnection *dconn, int events); + + void attach_downstream(std::unique_ptr downstream); + void delete_downstream(); + Downstream *get_downstream() const; + std::unique_ptr pop_downstream(); + void error_reply(unsigned int status_code); + + virtual void pause_read(IOCtrlReason reason); + virtual int resume_read(IOCtrlReason reason, Downstream *downstream, + size_t consumed); + + virtual int on_downstream_header_complete(Downstream *downstream); + virtual int on_downstream_body(Downstream *downstream, const uint8_t *data, + size_t len, bool flush); + virtual int on_downstream_body_complete(Downstream *downstream); + + virtual void on_handler_delete(); + virtual int on_downstream_reset(bool no_retry); + + void reset_current_header_length(); + void log_response_headers(const std::string &hdrs) const; + +private: + ClientHandler *handler_; + http_parser htp_; + size_t current_header_length_; + std::unique_ptr downstream_; + IOControl ioctrl_; +}; + +} // namespace shrpx + +#endif // SHRPX_HTTPS_UPSTREAM_H diff --git a/src/shrpx_io_control.cc b/src/shrpx_io_control.cc new file mode 100644 index 0000000..f43a257 --- /dev/null +++ b/src/shrpx_io_control.cc @@ -0,0 +1,66 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "shrpx_io_control.h" + +#include + +#include "shrpx_rate_limit.h" +#include "util.h" + +using namespace nghttp2; + +namespace shrpx { + +IOControl::IOControl(RateLimit *lim) : lim_(lim), rdbits_(0) {} + +IOControl::~IOControl() {} + +void IOControl::pause_read(IOCtrlReason reason) { + rdbits_ |= reason; + if (lim_) { + lim_->stopw(); + } +} + +bool IOControl::resume_read(IOCtrlReason reason) { + rdbits_ &= ~reason; + if (rdbits_ == 0) { + if (lim_) { + lim_->startw(); + } + return true; + } + + return false; +} + +void IOControl::force_resume_read() { + rdbits_ = 0; + if (lim_) { + lim_->startw(); + } +} + +} // namespace shrpx diff --git a/src/shrpx_io_control.h b/src/shrpx_io_control.h new file mode 100644 index 0000000..9baf95a --- /dev/null +++ b/src/shrpx_io_control.h @@ -0,0 +1,57 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef SHRPX_IO_CONTROL_H +#define SHRPX_IO_CONTROL_H + +#include "shrpx.h" + +#include + +#include + +#include "shrpx_rate_limit.h" + +namespace shrpx { + +enum IOCtrlReason { SHRPX_NO_BUFFER = 1 << 0, SHRPX_MSG_BLOCK = 1 << 1 }; + +class IOControl { +public: + IOControl(RateLimit *lim); + ~IOControl(); + void pause_read(IOCtrlReason reason); + // Returns true if read operation is enabled after this call + bool resume_read(IOCtrlReason reason); + // Clear all pause flags and enable read + void force_resume_read(); + +private: + RateLimit *lim_; + uint32_t rdbits_; +}; + +} // namespace shrpx + +#endif // SHRPX_IO_CONTROL_H diff --git a/src/shrpx_log.cc b/src/shrpx_log.cc new file mode 100644 index 0000000..1446df6 --- /dev/null +++ b/src/shrpx_log.cc @@ -0,0 +1,343 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "shrpx_log.h" + +#ifdef HAVE_SYSLOG_H +#include +#endif // HAVE_SYSLOG_H +#ifdef HAVE_UNISTD_H +#include +#endif // HAVE_UNISTD_H +#ifdef HAVE_INTTYPES_H +#include +#endif // HAVE_INTTYPES_H + +#include +#include +#include +#include +#include +#include + +#include "shrpx_config.h" +#include "shrpx_downstream.h" +#include "util.h" +#include "template.h" + +using namespace nghttp2; + +namespace shrpx { + +namespace { +const char *SEVERITY_STR[] = {"INFO", "NOTICE", "WARN", "ERROR", "FATAL"}; +} // namespace + +namespace { +const char *SEVERITY_COLOR[] = { + "\033[1;32m", // INFO + "\033[1;36m", // NOTICE + "\033[1;33m", // WARN + "\033[1;31m", // ERROR + "\033[1;35m", // FATAL +}; +} // namespace + +int Log::severity_thres_ = NOTICE; + +void Log::set_severity_level(int severity) { severity_thres_ = severity; } + +int Log::set_severity_level_by_name(const char *name) { + for (size_t i = 0, max = array_size(SEVERITY_STR); i < max; ++i) { + if (strcmp(SEVERITY_STR[i], name) == 0) { + severity_thres_ = i; + return 0; + } + } + return -1; +} + +int severity_to_syslog_level(int severity) { + switch (severity) { + case (INFO): + return LOG_INFO; + case (NOTICE): + return LOG_NOTICE; + case (WARN): + return LOG_WARNING; + case (ERROR): + return LOG_ERR; + case (FATAL): + return LOG_CRIT; + default: + return -1; + } +} + +Log::Log(int severity, const char *filename, int linenum) + : filename_(filename), severity_(severity), linenum_(linenum) {} + +Log::~Log() { + int rv; + + if (!get_config()) { + return; + } + + auto lgconf = log_config(); + + if (!log_enabled(severity_) || + (lgconf->errorlog_fd == -1 && !get_config()->errorlog_syslog)) { + return; + } + + if (get_config()->errorlog_syslog) { + if (severity_ == NOTICE) { + syslog(severity_to_syslog_level(severity_), "[%s] %s", + SEVERITY_STR[severity_], stream_.str().c_str()); + } else { + syslog(severity_to_syslog_level(severity_), "[%s] %s (%s:%d)", + SEVERITY_STR[severity_], stream_.str().c_str(), filename_, + linenum_); + } + + return; + } + + char buf[4096]; + auto tty = lgconf->errorlog_tty; + + lgconf->update_tstamp(std::chrono::system_clock::now()); + auto &time_local = lgconf->time_local_str; + + if (severity_ == NOTICE) { + rv = snprintf(buf, sizeof(buf), "%s PID%d [%s%s%s] %s\n", + time_local.c_str(), get_config()->pid, + tty ? SEVERITY_COLOR[severity_] : "", SEVERITY_STR[severity_], + tty ? "\033[0m" : "", stream_.str().c_str()); + } else { + rv = snprintf(buf, sizeof(buf), "%s PID%d [%s%s%s] %s%s:%d%s %s\n", + time_local.c_str(), get_config()->pid, + tty ? SEVERITY_COLOR[severity_] : "", SEVERITY_STR[severity_], + tty ? "\033[0m" : "", tty ? "\033[1;30m" : "", filename_, + linenum_, tty ? "\033[0m" : "", stream_.str().c_str()); + } + + if (rv < 0) { + return; + } + + auto nwrite = std::min(static_cast(rv), sizeof(buf) - 1); + + while (write(lgconf->errorlog_fd, buf, nwrite) == -1 && errno == EINTR) + ; +} + +namespace { +template +std::pair copy(const char *src, size_t avail, + OutputIterator oitr) { + auto nwrite = std::min(strlen(src), avail); + auto noitr = std::copy_n(src, nwrite, oitr); + return std::make_pair(noitr, avail - nwrite); +} +} // namespace + +void upstream_accesslog(const std::vector &lfv, + const LogSpec &lgsp) { + auto lgconf = log_config(); + + if (lgconf->accesslog_fd == -1 && !get_config()->accesslog_syslog) { + return; + } + + char buf[4096]; + + auto downstream = lgsp.downstream; + + auto p = buf; + auto avail = sizeof(buf) - 2; + + lgconf->update_tstamp(lgsp.time_now); + auto &time_local = lgconf->time_local_str; + auto &time_iso8601 = lgconf->time_iso8601_str; + + for (auto &lf : lfv) { + switch (lf.type) { + case SHRPX_LOGF_LITERAL: + std::tie(p, avail) = copy(lf.value.get(), avail, p); + break; + case SHRPX_LOGF_REMOTE_ADDR: + std::tie(p, avail) = copy(lgsp.remote_addr, avail, p); + break; + case SHRPX_LOGF_TIME_LOCAL: + std::tie(p, avail) = copy(time_local.c_str(), avail, p); + break; + case SHRPX_LOGF_TIME_ISO8601: + std::tie(p, avail) = copy(time_iso8601.c_str(), avail, p); + break; + case SHRPX_LOGF_REQUEST: + std::tie(p, avail) = copy(lgsp.method, avail, p); + std::tie(p, avail) = copy(" ", avail, p); + std::tie(p, avail) = copy(lgsp.path, avail, p); + std::tie(p, avail) = copy(" HTTP/", avail, p); + std::tie(p, avail) = copy(util::utos(lgsp.major).c_str(), avail, p); + if (lgsp.major < 2) { + std::tie(p, avail) = copy(".", avail, p); + std::tie(p, avail) = copy(util::utos(lgsp.minor).c_str(), avail, p); + } + break; + case SHRPX_LOGF_STATUS: + std::tie(p, avail) = copy(util::utos(lgsp.status).c_str(), avail, p); + break; + case SHRPX_LOGF_BODY_BYTES_SENT: + std::tie(p, avail) = + copy(util::utos(lgsp.body_bytes_sent).c_str(), avail, p); + break; + case SHRPX_LOGF_HTTP: + if (downstream) { + auto hd = downstream->get_request_header(lf.value.get()); + if (hd) { + std::tie(p, avail) = copy((*hd).value.c_str(), avail, p); + break; + } + } + + std::tie(p, avail) = copy("-", avail, p); + + break; + case SHRPX_LOGF_REMOTE_PORT: + std::tie(p, avail) = copy(lgsp.remote_port, avail, p); + break; + case SHRPX_LOGF_SERVER_PORT: + std::tie(p, avail) = copy(util::utos(lgsp.server_port).c_str(), avail, p); + break; + case SHRPX_LOGF_REQUEST_TIME: { + auto t = std::chrono::duration_cast( + lgsp.request_end_time - lgsp.request_start_time).count(); + + auto frac = util::utos(t % 1000); + auto sec = util::utos(t / 1000); + if (frac.size() < 3) { + frac = std::string(3 - frac.size(), '0') + frac; + } + sec += "."; + sec += frac; + + std::tie(p, avail) = copy(sec.c_str(), avail, p); + } break; + case SHRPX_LOGF_PID: + std::tie(p, avail) = copy(util::utos(lgsp.pid).c_str(), avail, p); + break; + case SHRPX_LOGF_ALPN: + std::tie(p, avail) = copy(lgsp.alpn, avail, p); + break; + case SHRPX_LOGF_NONE: + break; + default: + break; + } + } + + *p = '\0'; + + if (get_config()->accesslog_syslog) { + syslog(LOG_INFO, "%s", buf); + + return; + } + + *p++ = '\n'; + + auto nwrite = p - buf; + while (write(lgconf->accesslog_fd, buf, nwrite) == -1 && errno == EINTR) + ; +} + +int reopen_log_files() { + int res = 0; + + auto lgconf = log_config(); + + if (lgconf->accesslog_fd != -1) { + close(lgconf->accesslog_fd); + lgconf->accesslog_fd = -1; + } + + if (!get_config()->accesslog_syslog && get_config()->accesslog_file) { + + lgconf->accesslog_fd = + util::reopen_log_file(get_config()->accesslog_file.get()); + + if (lgconf->accesslog_fd == -1) { + LOG(ERROR) << "Failed to open accesslog file " + << get_config()->accesslog_file.get(); + res = -1; + } + } + + int new_errorlog_fd = -1; + + if (!get_config()->errorlog_syslog && get_config()->errorlog_file) { + + new_errorlog_fd = util::reopen_log_file(get_config()->errorlog_file.get()); + + if (new_errorlog_fd == -1) { + if (lgconf->errorlog_fd != -1) { + LOG(ERROR) << "Failed to open errorlog file " + << get_config()->errorlog_file.get(); + } else { + std::cerr << "Failed to open errorlog file " + << get_config()->errorlog_file.get() << std::endl; + } + + res = -1; + } + } + + if (lgconf->errorlog_fd != -1) { + close(lgconf->errorlog_fd); + lgconf->errorlog_fd = -1; + lgconf->errorlog_tty = false; + } + + if (new_errorlog_fd != -1) { + lgconf->errorlog_fd = new_errorlog_fd; + lgconf->errorlog_tty = isatty(lgconf->errorlog_fd); + } + + return res; +} + +void redirect_stderr_to_errorlog() { + auto lgconf = log_config(); + + if (get_config()->errorlog_syslog || lgconf->errorlog_fd == -1) { + return; + } + + dup2(lgconf->errorlog_fd, STDERR_FILENO); +} + +} // namespace shrpx diff --git a/src/shrpx_log.h b/src/shrpx_log.h new file mode 100644 index 0000000..41645d8 --- /dev/null +++ b/src/shrpx_log.h @@ -0,0 +1,151 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef SHRPX_LOG_H +#define SHRPX_LOG_H + +#include "shrpx.h" + +#include + +#include +#include +#include +#include + +#include "shrpx_log_config.h" + +namespace shrpx { + +class Downstream; + +#define ENABLE_LOG 1 + +#define LOG_ENABLED(SEVERITY) (ENABLE_LOG && Log::log_enabled(SEVERITY)) + +#define LOG(SEVERITY) Log(SEVERITY, __FILE__, __LINE__) + +// Listener log +#define LLOG(SEVERITY, LISTEN) \ + (Log(SEVERITY, __FILE__, __LINE__) << "[LISTEN:" << LISTEN << "] ") + +// Worker log +#define WLOG(SEVERITY, WORKER) \ + (Log(SEVERITY, __FILE__, __LINE__) << "[WORKER:" << WORKER << "] ") + +// ClientHandler log +#define CLOG(SEVERITY, CLIENT_HANDLER) \ + (Log(SEVERITY, __FILE__, __LINE__) << "[CLIENT_HANDLER:" << CLIENT_HANDLER \ + << "] ") + +// Upstream log +#define ULOG(SEVERITY, UPSTREAM) \ + (Log(SEVERITY, __FILE__, __LINE__) << "[UPSTREAM:" << UPSTREAM << "] ") + +// Downstream log +#define DLOG(SEVERITY, DOWNSTREAM) \ + (Log(SEVERITY, __FILE__, __LINE__) << "[DOWNSTREAM:" << DOWNSTREAM << "] ") + +// Downstream connection log +#define DCLOG(SEVERITY, DCONN) \ + (Log(SEVERITY, __FILE__, __LINE__) << "[DCONN:" << DCONN << "] ") + +// Downstream HTTP2 session log +#define SSLOG(SEVERITY, HTTP2) \ + (Log(SEVERITY, __FILE__, __LINE__) << "[DHTTP2:" << HTTP2 << "] ") + +enum SeverityLevel { INFO, NOTICE, WARN, ERROR, FATAL }; + +class Log { +public: + Log(int severity, const char *filename, int linenum); + ~Log(); + template Log &operator<<(Type s) { + stream_ << s; + return *this; + } + static void set_severity_level(int severity); + static int set_severity_level_by_name(const char *name); + static bool log_enabled(int severity) { return severity >= severity_thres_; } + +private: + std::stringstream stream_; + const char *filename_; + int severity_; + int linenum_; + static int severity_thres_; +}; + +#define TTY_HTTP_HD (log_config()->errorlog_tty ? "\033[1;34m" : "") +#define TTY_RST (log_config()->errorlog_tty ? "\033[0m" : "") + +enum LogFragmentType { + SHRPX_LOGF_NONE, + SHRPX_LOGF_LITERAL, + SHRPX_LOGF_REMOTE_ADDR, + SHRPX_LOGF_TIME_LOCAL, + SHRPX_LOGF_TIME_ISO8601, + SHRPX_LOGF_REQUEST, + SHRPX_LOGF_STATUS, + SHRPX_LOGF_BODY_BYTES_SENT, + SHRPX_LOGF_HTTP, + SHRPX_LOGF_REMOTE_PORT, + SHRPX_LOGF_SERVER_PORT, + SHRPX_LOGF_REQUEST_TIME, + SHRPX_LOGF_PID, + SHRPX_LOGF_ALPN, +}; + +struct LogFragment { + LogFragmentType type; + std::unique_ptr value; +}; + +struct LogSpec { + Downstream *downstream; + const char *remote_addr; + const char *method; + const char *path; + const char *alpn; + std::chrono::system_clock::time_point time_now; + std::chrono::high_resolution_clock::time_point request_start_time; + std::chrono::high_resolution_clock::time_point request_end_time; + int major, minor; + unsigned int status; + int64_t body_bytes_sent; + const char *remote_port; + uint16_t server_port; + pid_t pid; +}; + +void upstream_accesslog(const std::vector &lf, + const LogSpec &lgsp); + +int reopen_log_files(); + +void redirect_stderr_to_errorlog(); + +} // namespace shrpx + +#endif // SHRPX_LOG_H diff --git a/src/shrpx_log_config.cc b/src/shrpx_log_config.cc new file mode 100644 index 0000000..6c21bc0 --- /dev/null +++ b/src/shrpx_log_config.cc @@ -0,0 +1,69 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2014 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "shrpx_log_config.h" +#include "util.h" + +using namespace nghttp2; + +namespace shrpx { + +LogConfig::LogConfig() + : accesslog_fd(-1), errorlog_fd(-1), errorlog_tty(false) {} + +#ifndef NOTHREADS +static pthread_key_t lckey; +static pthread_once_t lckey_once = PTHREAD_ONCE_INIT; + +static void make_key(void) { pthread_key_create(&lckey, NULL); } + +LogConfig *log_config(void) { + pthread_once(&lckey_once, make_key); + LogConfig *config = (LogConfig *)pthread_getspecific(lckey); + if (!config) { + config = new LogConfig(); + pthread_setspecific(lckey, config); + } + return config; +} +#else +static LogConfig *config = new LogConfig(); +LogConfig *log_config(void) { return config; } +#endif // NOTHREADS + +void +LogConfig::update_tstamp(const std::chrono::system_clock::time_point &now) { + auto t0 = std::chrono::system_clock::to_time_t(time_str_updated_); + auto t1 = std::chrono::system_clock::to_time_t(now); + if (t0 == t1) { + return; + } + + time_str_updated_ = now; + + time_local_str = util::format_common_log(now); + time_iso8601_str = util::format_iso8601(now); +} + +} // namespace shrpx diff --git a/src/shrpx_log_config.h b/src/shrpx_log_config.h new file mode 100644 index 0000000..46b6357 --- /dev/null +++ b/src/shrpx_log_config.h @@ -0,0 +1,53 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2014 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef SHRPX_LOG_CONFIG_H +#define SHRPX_LOG_CONFIG_H + +#include "shrpx.h" + +#include + +namespace shrpx { + +struct LogConfig { + std::chrono::system_clock::time_point time_str_updated_; + std::string time_local_str; + std::string time_iso8601_str; + int accesslog_fd; + int errorlog_fd; + // true if errorlog_fd is referring to a terminal. + bool errorlog_tty; + + LogConfig(); + void update_tstamp(const std::chrono::system_clock::time_point &now); +}; + +// We need LogConfig per thread to avoid data race around opening file +// descriptor for log files. +extern LogConfig *log_config(void); + +} // namespace shrpx + +#endif // SHRPX_LOG_CONFIG_H diff --git a/src/shrpx_rate_limit.cc b/src/shrpx_rate_limit.cc new file mode 100644 index 0000000..85836c1 --- /dev/null +++ b/src/shrpx_rate_limit.cc @@ -0,0 +1,109 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2015 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "shrpx_rate_limit.h" + +#include + +namespace shrpx { + +namespace { +void regencb(struct ev_loop *loop, ev_timer *w, int revents) { + auto r = static_cast(w->data); + r->regen(); +} +} // namespace + +RateLimit::RateLimit(struct ev_loop *loop, ev_io *w, size_t rate, size_t burst, + SSL *ssl) + : w_(w), loop_(loop), ssl_(ssl), rate_(rate), burst_(burst), avail_(burst), + startw_req_(false) { + ev_timer_init(&t_, regencb, 0., 1.); + t_.data = this; + if (rate_ > 0) { + ev_timer_again(loop_, &t_); + } +} + +RateLimit::~RateLimit() { ev_timer_stop(loop_, &t_); } + +size_t RateLimit::avail() const { + if (rate_ == 0) { + return std::numeric_limits::max(); + } + return avail_; +} + +void RateLimit::drain(size_t n) { + if (rate_ == 0) { + return; + } + n = std::min(avail_, n); + avail_ -= n; + if (avail_ == 0) { + ev_io_stop(loop_, w_); + } +} + +void RateLimit::regen() { + if (rate_ == 0) { + return; + } + if (avail_ + rate_ > burst_) { + avail_ = burst_; + } else { + avail_ += rate_; + } + + if (avail_ > 0 && startw_req_) { + ev_io_start(loop_, w_); + handle_tls_pending_read(); + } +} + +void RateLimit::startw() { + startw_req_ = true; + if (rate_ == 0 || avail_ > 0) { + ev_io_start(loop_, w_); + handle_tls_pending_read(); + return; + } +} + +void RateLimit::stopw() { + startw_req_ = false; + ev_io_stop(loop_, w_); +} + +void RateLimit::handle_tls_pending_read() { + if (!ssl_ || SSL_pending(ssl_) == 0) { + return; + } + + // Note that ev_feed_event works without starting watcher, but we + // only call this function if watcher is active. + ev_feed_event(loop_, w_, EV_READ); +} + +} // namespace shrpx diff --git a/src/shrpx_rate_limit.h b/src/shrpx_rate_limit.h new file mode 100644 index 0000000..4550d01 --- /dev/null +++ b/src/shrpx_rate_limit.h @@ -0,0 +1,65 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2015 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef SHRPX_RATE_LIMIT_H +#define SHRPX_RATE_LIMIT_H + +#include "shrpx.h" + +#include + +#include + +namespace shrpx { + +class RateLimit { +public: + // We need |ssl| object to check that it has unread decrypted bytes. + RateLimit(struct ev_loop *loop, ev_io *w, size_t rate, size_t burst, + SSL *ssl = nullptr); + ~RateLimit(); + size_t avail() const; + void drain(size_t n); + void regen(); + void startw(); + void stopw(); + // Feeds event if ssl_ object has unread decrypted bytes. This is + // required since it is buffered in ssl_ object, io event is not + // generated unless new incoming data is received. + void handle_tls_pending_read(); + +private: + ev_timer t_; + ev_io *w_; + struct ev_loop *loop_; + SSL *ssl_; + size_t rate_; + size_t burst_; + size_t avail_; + bool startw_req_; +}; + +} // namespace shrpx + +#endif // SHRPX_RATE_LIMIT_H diff --git a/src/shrpx_spdy_upstream.cc b/src/shrpx_spdy_upstream.cc new file mode 100644 index 0000000..a14b0ed --- /dev/null +++ b/src/shrpx_spdy_upstream.cc @@ -0,0 +1,1103 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "shrpx_spdy_upstream.h" + +#include +#include +#include +#include + +#include + +#include "shrpx_client_handler.h" +#include "shrpx_downstream.h" +#include "shrpx_downstream_connection.h" +#include "shrpx_config.h" +#include "shrpx_http.h" +#include "http2.h" +#include "util.h" +#include "template.h" + +using namespace nghttp2; + +namespace shrpx { + +namespace { +ssize_t send_callback(spdylay_session *session, const uint8_t *data, size_t len, + int flags, void *user_data) { + auto upstream = static_cast(user_data); + auto handler = upstream->get_client_handler(); + auto wb = handler->get_wb(); + + if (wb->wleft() == 0) { + return SPDYLAY_ERR_WOULDBLOCK; + } + + auto nread = wb->write(data, len); + + return nread; +} +} // namespace + +namespace { +ssize_t recv_callback(spdylay_session *session, uint8_t *buf, size_t len, + int flags, void *user_data) { + auto upstream = static_cast(user_data); + auto handler = upstream->get_client_handler(); + auto rb = handler->get_rb(); + auto rlimit = handler->get_rlimit(); + + if (rb->rleft() == 0) { + return SPDYLAY_ERR_WOULDBLOCK; + } + + auto nread = std::min(rb->rleft(), len); + + memcpy(buf, rb->pos, nread); + rb->drain(nread); + rlimit->startw(); + + return nread; +} +} // namespace + +namespace { +void on_stream_close_callback(spdylay_session *session, int32_t stream_id, + spdylay_status_code status_code, + void *user_data) { + auto upstream = static_cast(user_data); + if (LOG_ENABLED(INFO)) { + ULOG(INFO, upstream) << "Stream stream_id=" << stream_id + << " is being closed"; + } + auto downstream = static_cast( + spdylay_session_get_stream_user_data(session, stream_id)); + if (!downstream) { + return; + } + + upstream->consume(stream_id, downstream->get_request_datalen()); + + downstream->reset_request_datalen(); + + if (downstream->get_request_state() == Downstream::CONNECT_FAIL) { + upstream->remove_downstream(downstream); + // downstrea was deleted + + return; + } + + downstream->set_request_state(Downstream::STREAM_CLOSED); + if (downstream->get_response_state() == Downstream::MSG_COMPLETE) { + // At this point, downstream response was read + if (!downstream->get_upgraded() && + !downstream->get_response_connection_close()) { + // Keep-alive + downstream->detach_downstream_connection(); + } + upstream->remove_downstream(downstream); + // downstrea was deleted + + return; + } + + // At this point, downstream read may be paused. + + // If shrpx_downstream::push_request_headers() failed, the + // error is handled here. + upstream->remove_downstream(downstream); + // downstrea was deleted + + // How to test this case? Request sufficient large download + // and make client send RST_STREAM after it gets first DATA + // frame chunk. +} +} // namespace + +namespace { +void on_ctrl_recv_callback(spdylay_session *session, spdylay_frame_type type, + spdylay_frame *frame, void *user_data) { + auto upstream = static_cast(user_data); + switch (type) { + case SPDYLAY_SYN_STREAM: { + if (LOG_ENABLED(INFO)) { + ULOG(INFO, upstream) << "Received upstream SYN_STREAM stream_id=" + << frame->syn_stream.stream_id; + } + + auto downstream = upstream->add_pending_downstream( + frame->syn_stream.stream_id, frame->syn_stream.pri); + + downstream->reset_upstream_rtimer(); + + auto nv = frame->syn_stream.nv; + + if (LOG_ENABLED(INFO)) { + std::stringstream ss; + for (size_t i = 0; nv[i]; i += 2) { + ss << TTY_HTTP_HD << nv[i] << TTY_RST << ": " << nv[i + 1] << "\n"; + } + ULOG(INFO, upstream) << "HTTP request headers. stream_id=" + << downstream->get_stream_id() << "\n" << ss.str(); + } + + size_t num_headers = 0; + size_t header_buffer = 0; + for (size_t i = 0; nv[i]; i += 2) { + ++num_headers; + header_buffer += strlen(nv[i]) + strlen(nv[i + 1]); + } + + if (header_buffer > get_config()->header_field_buffer || + num_headers > get_config()->max_header_fields) { + upstream->rst_stream(downstream, SPDYLAY_INTERNAL_ERROR); + return; + } + + for (size_t i = 0; nv[i]; i += 2) { + downstream->add_request_header(nv[i], nv[i + 1]); + } + + if (downstream->index_request_headers() != 0) { + if (upstream->error_reply(downstream, 400) != 0) { + ULOG(FATAL, upstream) << "error_reply failed"; + } + return; + } + + auto path = downstream->get_request_header(http2::HD__PATH); + auto scheme = downstream->get_request_header(http2::HD__SCHEME); + auto host = downstream->get_request_header(http2::HD__HOST); + auto method = downstream->get_request_header(http2::HD__METHOD); + + bool is_connect = method && "CONNECT" == method->value; + if (!path || !host || !method || !http2::non_empty_value(host) || + !http2::non_empty_value(path) || !http2::non_empty_value(method) || + (!is_connect && (!scheme || !http2::non_empty_value(scheme)))) { + upstream->rst_stream(downstream, SPDYLAY_INTERNAL_ERROR); + return; + } + + downstream->set_request_method(method->value); + if (is_connect) { + downstream->set_request_http2_authority(path->value); + } else { + downstream->set_request_http2_scheme(scheme->value); + downstream->set_request_http2_authority(host->value); + downstream->set_request_path(path->value); + } + + if (!(frame->syn_stream.hd.flags & SPDYLAY_CTRL_FLAG_FIN)) { + downstream->set_request_http2_expect_body(true); + } + + downstream->inspect_http2_request(); + + downstream->set_request_state(Downstream::HEADER_COMPLETE); + if (frame->syn_stream.hd.flags & SPDYLAY_CTRL_FLAG_FIN) { + if (!downstream->validate_request_bodylen()) { + upstream->rst_stream(downstream, SPDYLAY_PROTOCOL_ERROR); + return; + } + + downstream->disable_upstream_rtimer(); + downstream->set_request_state(Downstream::MSG_COMPLETE); + } + + upstream->start_downstream(downstream); + + break; + } + default: + break; + } +} +} // namespace + +void SpdyUpstream::start_downstream(Downstream *downstream) { + if (downstream_queue_.can_activate( + downstream->get_request_http2_authority())) { + initiate_downstream(downstream); + return; + } + + downstream_queue_.mark_blocked(downstream); +} + +void SpdyUpstream::initiate_downstream(Downstream *downstream) { + int rv = downstream->attach_downstream_connection( + handler_->get_downstream_connection()); + if (rv != 0) { + // If downstream connection fails, issue RST_STREAM. + rst_stream(downstream, SPDYLAY_INTERNAL_ERROR); + downstream->set_request_state(Downstream::CONNECT_FAIL); + + downstream_queue_.mark_failure(downstream); + + return; + } + rv = downstream->push_request_headers(); + if (rv != 0) { + rst_stream(downstream, SPDYLAY_INTERNAL_ERROR); + + downstream_queue_.mark_failure(downstream); + + return; + } + + downstream_queue_.mark_active(downstream); +} + +namespace { +void on_data_chunk_recv_callback(spdylay_session *session, uint8_t flags, + int32_t stream_id, const uint8_t *data, + size_t len, void *user_data) { + auto upstream = static_cast(user_data); + auto downstream = static_cast( + spdylay_session_get_stream_user_data(session, stream_id)); + + if (!downstream) { + upstream->consume(stream_id, len); + + return; + } + + downstream->reset_upstream_rtimer(); + + if (downstream->push_upload_data_chunk(data, len) != 0) { + upstream->rst_stream(downstream, SPDYLAY_INTERNAL_ERROR); + + upstream->consume(stream_id, len); + + return; + } + + if (!upstream->get_flow_control()) { + return; + } + + // If connection-level window control is not enabled (e.g, + // spdy/3), spdylay_session_get_recv_data_length() is always + // returns 0. + if (spdylay_session_get_recv_data_length(session) > + std::max(SPDYLAY_INITIAL_WINDOW_SIZE, + 1 << get_config()->http2_upstream_connection_window_bits)) { + if (LOG_ENABLED(INFO)) { + ULOG(INFO, upstream) + << "Flow control error on connection: " + << "recv_window_size=" + << spdylay_session_get_recv_data_length(session) << ", window_size=" + << (1 << get_config()->http2_upstream_connection_window_bits); + } + spdylay_session_fail_session(session, SPDYLAY_GOAWAY_PROTOCOL_ERROR); + return; + } + if (spdylay_session_get_stream_recv_data_length(session, stream_id) > + std::max(SPDYLAY_INITIAL_WINDOW_SIZE, + 1 << get_config()->http2_upstream_window_bits)) { + if (LOG_ENABLED(INFO)) { + ULOG(INFO, upstream) << "Flow control error: recv_window_size=" + << spdylay_session_get_stream_recv_data_length( + session, stream_id) + << ", initial_window_size=" + << (1 << get_config()->http2_upstream_window_bits); + } + upstream->rst_stream(downstream, SPDYLAY_FLOW_CONTROL_ERROR); + return; + } +} +} // namespace + +namespace { +void on_data_recv_callback(spdylay_session *session, uint8_t flags, + int32_t stream_id, int32_t length, void *user_data) { + auto upstream = static_cast(user_data); + auto downstream = static_cast( + spdylay_session_get_stream_user_data(session, stream_id)); + if (downstream && (flags & SPDYLAY_DATA_FLAG_FIN)) { + if (!downstream->validate_request_bodylen()) { + upstream->rst_stream(downstream, SPDYLAY_PROTOCOL_ERROR); + return; + } + + downstream->disable_upstream_rtimer(); + downstream->end_upload_data(); + downstream->set_request_state(Downstream::MSG_COMPLETE); + } +} +} // namespace + +namespace { +void on_ctrl_not_send_callback(spdylay_session *session, + spdylay_frame_type type, spdylay_frame *frame, + int error_code, void *user_data) { + auto upstream = static_cast(user_data); + if (LOG_ENABLED(INFO)) { + ULOG(INFO, upstream) << "Failed to send control frame type=" << type + << ", error_code=" << error_code << ":" + << spdylay_strerror(error_code); + } + if (type == SPDYLAY_SYN_REPLY && error_code != SPDYLAY_ERR_STREAM_CLOSED && + error_code != SPDYLAY_ERR_STREAM_CLOSING) { + // To avoid stream hanging around, issue RST_STREAM. + auto stream_id = frame->syn_reply.stream_id; + // TODO Could be always nullptr + auto downstream = static_cast( + spdylay_session_get_stream_user_data(session, stream_id)); + if (downstream) { + upstream->rst_stream(downstream, SPDYLAY_INTERNAL_ERROR); + } + } +} +} // namespace + +namespace { +void on_ctrl_recv_parse_error_callback(spdylay_session *session, + spdylay_frame_type type, + const uint8_t *head, size_t headlen, + const uint8_t *payload, + size_t payloadlen, int error_code, + void *user_data) { + auto upstream = static_cast(user_data); + if (LOG_ENABLED(INFO)) { + ULOG(INFO, upstream) << "Failed to parse received control frame. type=" + << type << ", error_code=" << error_code << ":" + << spdylay_strerror(error_code); + } +} +} // namespace + +namespace { +void on_unknown_ctrl_recv_callback(spdylay_session *session, + const uint8_t *head, size_t headlen, + const uint8_t *payload, size_t payloadlen, + void *user_data) { + auto upstream = static_cast(user_data); + if (LOG_ENABLED(INFO)) { + ULOG(INFO, upstream) << "Received unknown control frame."; + } +} +} // namespace + +namespace { +// Infer upstream RST_STREAM status code from downstream HTTP/2 +// error code. +uint32_t infer_upstream_rst_stream_status_code(uint32_t downstream_error_code) { + // Only propagate *_REFUSED_STREAM so that upstream client can + // resend request. + if (downstream_error_code == NGHTTP2_REFUSED_STREAM) { + return SPDYLAY_REFUSED_STREAM; + } else { + return SPDYLAY_INTERNAL_ERROR; + } +} +} // namespace + +SpdyUpstream::SpdyUpstream(uint16_t version, ClientHandler *handler) + : downstream_queue_( + get_config()->http2_proxy + ? get_config()->downstream_connections_per_host + : get_config()->downstream_proto == PROTO_HTTP + ? get_config()->downstream_connections_per_frontend + : 0, + !get_config()->http2_proxy), + handler_(handler), session_(nullptr) { + spdylay_session_callbacks callbacks; + memset(&callbacks, 0, sizeof(callbacks)); + callbacks.send_callback = send_callback; + callbacks.recv_callback = recv_callback; + callbacks.on_stream_close_callback = on_stream_close_callback; + callbacks.on_ctrl_recv_callback = on_ctrl_recv_callback; + callbacks.on_data_chunk_recv_callback = on_data_chunk_recv_callback; + callbacks.on_data_recv_callback = on_data_recv_callback; + callbacks.on_ctrl_not_send_callback = on_ctrl_not_send_callback; + callbacks.on_ctrl_recv_parse_error_callback = + on_ctrl_recv_parse_error_callback; + callbacks.on_unknown_ctrl_recv_callback = on_unknown_ctrl_recv_callback; + + int rv; + rv = spdylay_session_server_new(&session_, version, &callbacks, this); + assert(rv == 0); + + uint32_t max_buffer = 65536; + rv = spdylay_session_set_option(session_, + SPDYLAY_OPT_MAX_RECV_CTRL_FRAME_BUFFER, + &max_buffer, sizeof(max_buffer)); + assert(rv == 0); + + if (version >= SPDYLAY_PROTO_SPDY3) { + int val = 1; + flow_control_ = true; + initial_window_size_ = 1 << get_config()->http2_upstream_window_bits; + rv = spdylay_session_set_option( + session_, SPDYLAY_OPT_NO_AUTO_WINDOW_UPDATE2, &val, sizeof(val)); + assert(rv == 0); + } else { + flow_control_ = false; + initial_window_size_ = 0; + } + // TODO Maybe call from outside? + std::array entry; + entry[0].settings_id = SPDYLAY_SETTINGS_MAX_CONCURRENT_STREAMS; + entry[0].value = get_config()->http2_max_concurrent_streams; + entry[0].flags = SPDYLAY_ID_FLAG_SETTINGS_NONE; + + entry[1].settings_id = SPDYLAY_SETTINGS_INITIAL_WINDOW_SIZE; + entry[1].value = initial_window_size_; + entry[1].flags = SPDYLAY_ID_FLAG_SETTINGS_NONE; + + rv = spdylay_submit_settings(session_, SPDYLAY_FLAG_SETTINGS_NONE, + entry.data(), entry.size()); + assert(rv == 0); + + if (version >= SPDYLAY_PROTO_SPDY3_1 && + get_config()->http2_upstream_connection_window_bits > 16) { + int32_t delta = (1 << get_config()->http2_upstream_connection_window_bits) - + SPDYLAY_INITIAL_WINDOW_SIZE; + rv = spdylay_submit_window_update(session_, 0, delta); + assert(rv == 0); + } + + handler_->reset_upstream_read_timeout( + get_config()->http2_upstream_read_timeout); + + handler_->signal_write(); +} + +SpdyUpstream::~SpdyUpstream() { spdylay_session_del(session_); } + +int SpdyUpstream::on_read() { + int rv = 0; + + rv = spdylay_session_recv(session_); + if (rv < 0) { + if (rv != SPDYLAY_ERR_EOF) { + ULOG(ERROR, this) << "spdylay_session_recv() returned error: " + << spdylay_strerror(rv); + } + return rv; + } + + handler_->signal_write(); + + return 0; +} + +// After this function call, downstream may be deleted. +int SpdyUpstream::on_write() { + int rv = 0; + + rv = spdylay_session_send(session_); + if (rv != 0) { + ULOG(ERROR, this) << "spdylay_session_send() returned error: " + << spdylay_strerror(rv); + return rv; + } + + if (spdylay_session_want_read(session_) == 0 && + spdylay_session_want_write(session_) == 0 && + handler_->get_wb()->rleft() == 0) { + if (LOG_ENABLED(INFO)) { + ULOG(INFO, this) << "No more read/write for this SPDY session"; + } + return -1; + } + return 0; +} + +ClientHandler *SpdyUpstream::get_client_handler() const { return handler_; } + +int SpdyUpstream::downstream_read(DownstreamConnection *dconn) { + auto downstream = dconn->get_downstream(); + + if (downstream->get_request_state() == Downstream::STREAM_CLOSED) { + // If upstream SPDY stream was closed, we just close downstream, + // because there is no consumer now. Downstream connection is also + // closed in this case. + remove_downstream(downstream); + // downstrea was deleted + + return 0; + } + + if (downstream->get_response_state() == Downstream::MSG_RESET) { + // The downstream stream was reset (canceled). In this case, + // RST_STREAM to the upstream and delete downstream connection + // here. Deleting downstream will be taken place at + // on_stream_close_callback. + rst_stream(downstream, + infer_upstream_rst_stream_status_code( + downstream->get_response_rst_stream_error_code())); + downstream->pop_downstream_connection(); + dconn = nullptr; + } else if (downstream->get_response_state() == Downstream::MSG_BAD_HEADER) { + if (error_reply(downstream, 502) != 0) { + return -1; + } + downstream->pop_downstream_connection(); + // dconn was deleted + dconn = nullptr; + } else { + auto rv = downstream->on_read(); + if (rv == SHRPX_ERR_EOF) { + return downstream_eof(dconn); + } + if (rv != 0) { + if (rv != SHRPX_ERR_NETWORK) { + if (LOG_ENABLED(INFO)) { + DCLOG(INFO, dconn) << "HTTP parser failure"; + } + } + return downstream_error(dconn, Downstream::EVENT_ERROR); + } + // Detach downstream connection early so that it could be reused + // without hitting server's request timeout. + if (downstream->get_response_state() == Downstream::MSG_COMPLETE && + !downstream->get_response_connection_close()) { + // Keep-alive + downstream->detach_downstream_connection(); + } + } + + handler_->signal_write(); + // At this point, downstream may be deleted. + + return 0; +} + +int SpdyUpstream::downstream_write(DownstreamConnection *dconn) { + int rv; + rv = dconn->on_write(); + if (rv == SHRPX_ERR_NETWORK) { + return downstream_error(dconn, Downstream::EVENT_ERROR); + } + if (rv != 0) { + return -1; + } + return 0; +} + +int SpdyUpstream::downstream_eof(DownstreamConnection *dconn) { + auto downstream = dconn->get_downstream(); + + if (LOG_ENABLED(INFO)) { + DCLOG(INFO, dconn) << "EOF. stream_id=" << downstream->get_stream_id(); + } + if (downstream->get_request_state() == Downstream::STREAM_CLOSED) { + // If stream was closed already, we don't need to send reply at + // the first place. We can delete downstream. + remove_downstream(downstream); + // downstream was deleted + + return 0; + } + + // Delete downstream connection. If we don't delete it here, it will + // be pooled in on_stream_close_callback. + downstream->pop_downstream_connection(); + // dconn was deleted + dconn = nullptr; + // downstream wil be deleted in on_stream_close_callback. + if (downstream->get_response_state() == Downstream::HEADER_COMPLETE) { + // Server may indicate the end of the request by EOF + if (LOG_ENABLED(INFO)) { + ULOG(INFO, this) << "Downstream body was ended by EOF"; + } + downstream->set_response_state(Downstream::MSG_COMPLETE); + + // For tunneled connection, MSG_COMPLETE signals + // downstream_data_read_callback to send RST_STREAM after pending + // response body is sent. This is needed to ensure that RST_STREAM + // is sent after all pending data are sent. + on_downstream_body_complete(downstream); + } else if (downstream->get_response_state() != Downstream::MSG_COMPLETE) { + // If stream was not closed, then we set MSG_COMPLETE and let + // on_stream_close_callback delete downstream. + if (error_reply(downstream, 502) != 0) { + return -1; + } + } + handler_->signal_write(); + // At this point, downstream may be deleted. + return 0; +} + +int SpdyUpstream::downstream_error(DownstreamConnection *dconn, int events) { + auto downstream = dconn->get_downstream(); + + if (LOG_ENABLED(INFO)) { + if (events & Downstream::EVENT_ERROR) { + DCLOG(INFO, dconn) << "Downstream network/general error"; + } else { + DCLOG(INFO, dconn) << "Timeout"; + } + if (downstream->get_upgraded()) { + DCLOG(INFO, dconn) << "Note: this is tunnel connection"; + } + } + + if (downstream->get_request_state() == Downstream::STREAM_CLOSED) { + remove_downstream(downstream); + // downstream was deleted + + return 0; + } + + // Delete downstream connection. If we don't delete it here, it will + // be pooled in on_stream_close_callback. + downstream->pop_downstream_connection(); + // dconn was deleted + dconn = nullptr; + + if (downstream->get_response_state() == Downstream::MSG_COMPLETE) { + // For SSL tunneling, we issue RST_STREAM. For other types of + // stream, we don't have to do anything since response was + // complete. + if (downstream->get_upgraded()) { + // We want "NO_ERROR" error code but SPDY does not have such + // code for RST_STREAM. + rst_stream(downstream, SPDYLAY_INTERNAL_ERROR); + } + } else { + if (downstream->get_response_state() == Downstream::HEADER_COMPLETE) { + if (downstream->get_upgraded()) { + on_downstream_body_complete(downstream); + } else { + rst_stream(downstream, SPDYLAY_INTERNAL_ERROR); + } + } else { + unsigned int status; + if (events & Downstream::EVENT_TIMEOUT) { + status = 504; + } else { + status = 502; + } + if (error_reply(downstream, status) != 0) { + return -1; + } + } + downstream->set_response_state(Downstream::MSG_COMPLETE); + } + handler_->signal_write(); + // At this point, downstream may be deleted. + return 0; +} + +int SpdyUpstream::rst_stream(Downstream *downstream, int status_code) { + if (LOG_ENABLED(INFO)) { + ULOG(INFO, this) << "RST_STREAM stream_id=" << downstream->get_stream_id(); + } + int rv; + rv = spdylay_submit_rst_stream(session_, downstream->get_stream_id(), + status_code); + if (rv < SPDYLAY_ERR_FATAL) { + ULOG(FATAL, this) << "spdylay_submit_rst_stream() failed: " + << spdylay_strerror(rv); + DIE(); + } + return 0; +} + +namespace { +ssize_t spdy_data_read_callback(spdylay_session *session, int32_t stream_id, + uint8_t *buf, size_t length, int *eof, + spdylay_data_source *source, void *user_data) { + auto downstream = static_cast(source->ptr); + auto upstream = static_cast(downstream->get_upstream()); + auto body = downstream->get_response_buf(); + assert(body); + + auto dconn = downstream->get_downstream_connection(); + + if (body->rleft() == 0 && dconn && + downstream->get_response_state() != Downstream::MSG_COMPLETE) { + // Try to read more if buffer is empty. This will help small + // buffer and make priority handling a bit better. + if (upstream->downstream_read(dconn) != 0) { + return SPDYLAY_ERR_CALLBACK_FAILURE; + } + } + + auto nread = body->remove(buf, length); + auto body_empty = body->rleft() == 0; + + if (nread == 0 && + downstream->get_response_state() == Downstream::MSG_COMPLETE) { + if (!downstream->get_upgraded()) { + *eof = 1; + } else { + // For tunneling, issue RST_STREAM to finish the stream. + if (LOG_ENABLED(INFO)) { + ULOG(INFO, upstream) + << "RST_STREAM to tunneled stream stream_id=" << stream_id; + } + upstream->rst_stream( + downstream, infer_upstream_rst_stream_status_code( + downstream->get_response_rst_stream_error_code())); + } + } + + if (body_empty) { + downstream->disable_upstream_wtimer(); + } else { + downstream->reset_upstream_wtimer(); + } + + if (nread > 0 && downstream->resume_read(SHRPX_NO_BUFFER, nread) != 0) { + return SPDYLAY_ERR_CALLBACK_FAILURE; + } + + if (nread == 0 && *eof != 1) { + return SPDYLAY_ERR_DEFERRED; + } + + if (nread > 0) { + downstream->add_response_sent_bodylen(nread); + } + + return nread; +} +} // namespace + +int SpdyUpstream::error_reply(Downstream *downstream, + unsigned int status_code) { + int rv; + auto html = http::create_error_html(status_code); + downstream->set_response_http_status(status_code); + auto body = downstream->get_response_buf(); + body->append(html.c_str(), html.size()); + downstream->set_response_state(Downstream::MSG_COMPLETE); + + spdylay_data_provider data_prd; + data_prd.source.ptr = downstream; + data_prd.read_callback = spdy_data_read_callback; + + std::string content_length = util::utos(html.size()); + std::string status_string = http2::get_status_string(status_code); + const char *nv[] = {":status", status_string.c_str(), + ":version", "http/1.1", + "content-type", "text/html; charset=UTF-8", + "server", get_config()->server_name, + "content-length", content_length.c_str(), + nullptr}; + + rv = spdylay_submit_response(session_, downstream->get_stream_id(), nv, + &data_prd); + if (rv < SPDYLAY_ERR_FATAL) { + ULOG(FATAL, this) << "spdylay_submit_response() failed: " + << spdylay_strerror(rv); + return -1; + } + + return 0; +} + +Downstream *SpdyUpstream::add_pending_downstream(int32_t stream_id, + int32_t priority) { + auto downstream = make_unique(this, handler_->get_mcpool(), + stream_id, priority); + spdylay_session_set_stream_user_data(session_, stream_id, downstream.get()); + auto res = downstream.get(); + + downstream_queue_.add_pending(std::move(downstream)); + + return res; +} + +void SpdyUpstream::remove_downstream(Downstream *downstream) { + if (downstream->accesslog_ready()) { + handler_->write_accesslog(downstream); + } + + spdylay_session_set_stream_user_data(session_, downstream->get_stream_id(), + nullptr); + + auto next_downstream = downstream_queue_.remove_and_get_blocked(downstream); + + if (next_downstream) { + initiate_downstream(next_downstream); + } +} + +// WARNING: Never call directly or indirectly spdylay_session_send or +// spdylay_session_recv. These calls may delete downstream. +int SpdyUpstream::on_downstream_header_complete(Downstream *downstream) { + if (downstream->get_non_final_response()) { + // SPDY does not support non-final response. We could send it + // with HEADERS and final response in SYN_REPLY, but it is not + // official way. + downstream->clear_response_headers(); + + return 0; + } + + if (LOG_ENABLED(INFO)) { + DLOG(INFO, downstream) << "HTTP response header completed"; + } + + if (!get_config()->http2_proxy && !get_config()->client_proxy && + !get_config()->no_location_rewrite) { + downstream->rewrite_location_response_header( + downstream->get_request_http2_scheme()); + } + size_t nheader = downstream->get_response_headers().size(); + // 8 means server, :status, :version and possible via header field. + auto nv = make_unique( + nheader * 2 + 8 + get_config()->add_response_headers.size() * 2 + 1); + + size_t hdidx = 0; + std::string via_value; + std::string status_string = + http2::get_status_string(downstream->get_response_http_status()); + nv[hdidx++] = ":status"; + nv[hdidx++] = status_string.c_str(); + nv[hdidx++] = ":version"; + nv[hdidx++] = "HTTP/1.1"; + for (auto &hd : downstream->get_response_headers()) { + if (hd.name.empty() || hd.name.c_str()[0] == ':') { + continue; + } + switch (hd.token) { + case http2::HD_CONNECTION: + case http2::HD_KEEP_ALIVE: + case http2::HD_PROXY_CONNECTION: + case http2::HD_TRANSFER_ENCODING: + case http2::HD_VIA: + case http2::HD_SERVER: + continue; + } + + nv[hdidx++] = hd.name.c_str(); + nv[hdidx++] = hd.value.c_str(); + } + + if (!get_config()->http2_proxy && !get_config()->client_proxy) { + nv[hdidx++] = "server"; + nv[hdidx++] = get_config()->server_name; + } else { + auto server = downstream->get_response_header(http2::HD_SERVER); + if (server) { + nv[hdidx++] = "server"; + nv[hdidx++] = server->value.c_str(); + } + } + + auto via = downstream->get_response_header(http2::HD_VIA); + if (get_config()->no_via) { + if (via) { + nv[hdidx++] = "via"; + nv[hdidx++] = via->value.c_str(); + } + } else { + if (via) { + via_value = via->value; + via_value += ", "; + } + via_value += http::create_via_header_value( + downstream->get_response_major(), downstream->get_response_minor()); + nv[hdidx++] = "via"; + nv[hdidx++] = via_value.c_str(); + } + + for (auto &p : get_config()->add_response_headers) { + nv[hdidx++] = p.first.c_str(); + nv[hdidx++] = p.second.c_str(); + } + + nv[hdidx++] = 0; + if (LOG_ENABLED(INFO)) { + std::stringstream ss; + for (size_t i = 0; nv[i]; i += 2) { + ss << TTY_HTTP_HD << nv[i] << TTY_RST << ": " << nv[i + 1] << "\n"; + } + ULOG(INFO, this) << "HTTP response headers. stream_id=" + << downstream->get_stream_id() << "\n" << ss.str(); + } + spdylay_data_provider data_prd; + data_prd.source.ptr = downstream; + data_prd.read_callback = spdy_data_read_callback; + + int rv; + rv = spdylay_submit_response(session_, downstream->get_stream_id(), nv.get(), + &data_prd); + if (rv != 0) { + ULOG(FATAL, this) << "spdylay_submit_response() failed"; + return -1; + } + + return 0; +} + +// WARNING: Never call directly or indirectly spdylay_session_send or +// spdylay_session_recv. These calls may delete downstream. +int SpdyUpstream::on_downstream_body(Downstream *downstream, + const uint8_t *data, size_t len, + bool flush) { + auto body = downstream->get_response_buf(); + body->append(data, len); + + if (flush) { + spdylay_session_resume_data(session_, downstream->get_stream_id()); + + downstream->ensure_upstream_wtimer(); + } + + return 0; +} + +// WARNING: Never call directly or indirectly spdylay_session_send or +// spdylay_session_recv. These calls may delete downstream. +int SpdyUpstream::on_downstream_body_complete(Downstream *downstream) { + if (LOG_ENABLED(INFO)) { + DLOG(INFO, downstream) << "HTTP response completed"; + } + + if (!downstream->validate_response_bodylen()) { + rst_stream(downstream, SPDYLAY_PROTOCOL_ERROR); + downstream->set_response_connection_close(true); + return 0; + } + + spdylay_session_resume_data(session_, downstream->get_stream_id()); + downstream->ensure_upstream_wtimer(); + + return 0; +} + +bool SpdyUpstream::get_flow_control() const { return flow_control_; } + +void SpdyUpstream::pause_read(IOCtrlReason reason) {} + +int SpdyUpstream::resume_read(IOCtrlReason reason, Downstream *downstream, + size_t consumed) { + if (get_flow_control()) { + assert(downstream->get_request_datalen() >= consumed); + + if (consume(downstream->get_stream_id(), consumed) != 0) { + return -1; + } + + downstream->dec_request_datalen(consumed); + } + + handler_->signal_write(); + return 0; +} + +int SpdyUpstream::on_downstream_abort_request(Downstream *downstream, + unsigned int status_code) { + int rv; + + rv = error_reply(downstream, status_code); + + if (rv != 0) { + return -1; + } + + handler_->signal_write(); + return 0; +} + +int SpdyUpstream::consume(int32_t stream_id, size_t len) { + int rv; + + rv = spdylay_session_consume(session_, stream_id, len); + + if (rv != 0) { + ULOG(WARN, this) << "spdylay_session_consume() returned error: " + << spdylay_strerror(rv); + return -1; + } + + return 0; +} + +int SpdyUpstream::on_timeout(Downstream *downstream) { + if (LOG_ENABLED(INFO)) { + ULOG(INFO, this) << "Stream timeout stream_id=" + << downstream->get_stream_id(); + } + + rst_stream(downstream, SPDYLAY_INTERNAL_ERROR); + + return 0; +} + +void SpdyUpstream::on_handler_delete() { + for (auto d = downstream_queue_.get_downstreams(); d; d = d->dlnext) { + if (d->get_dispatch_state() == Downstream::DISPATCH_ACTIVE && + d->accesslog_ready()) { + handler_->write_accesslog(d); + } + } +} + +int SpdyUpstream::on_downstream_reset(bool no_retry) { + int rv; + + for (auto downstream = downstream_queue_.get_downstreams(); downstream; + downstream = downstream->dlnext) { + if (downstream->get_dispatch_state() != Downstream::DISPATCH_ACTIVE) { + continue; + } + + if (!downstream->request_submission_ready()) { + rst_stream(downstream, SPDYLAY_INTERNAL_ERROR); + downstream->pop_downstream_connection(); + continue; + } + + downstream->pop_downstream_connection(); + + downstream->add_retry(); + + if (no_retry || downstream->no_more_retry()) { + goto fail; + } + + // downstream connection is clean; we can retry with new + // downstream connection. + + rv = downstream->attach_downstream_connection( + handler_->get_downstream_connection()); + if (rv != 0) { + goto fail; + } + + continue; + + fail: + if (on_downstream_abort_request(downstream, 503) != 0) { + return -1; + } + downstream->pop_downstream_connection(); + } + + handler_->signal_write(); + + return 0; +} + +} // namespace shrpx diff --git a/src/shrpx_spdy_upstream.h b/src/shrpx_spdy_upstream.h new file mode 100644 index 0000000..2e4aec4 --- /dev/null +++ b/src/shrpx_spdy_upstream.h @@ -0,0 +1,94 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef SHRPX_SPDY_UPSTREAM_H +#define SHRPX_SPDY_UPSTREAM_H + +#include "shrpx.h" + +#include + +#include + +#include + +#include "shrpx_upstream.h" +#include "shrpx_downstream_queue.h" +#include "memchunk.h" +#include "util.h" + +namespace shrpx { + +class ClientHandler; + +class SpdyUpstream : public Upstream { +public: + SpdyUpstream(uint16_t version, ClientHandler *handler); + virtual ~SpdyUpstream(); + virtual int on_read(); + virtual int on_write(); + virtual int on_timeout(Downstream *downstream); + virtual int on_downstream_abort_request(Downstream *downstream, + unsigned int status_code); + virtual ClientHandler *get_client_handler() const; + virtual int downstream_read(DownstreamConnection *dconn); + virtual int downstream_write(DownstreamConnection *dconn); + virtual int downstream_eof(DownstreamConnection *dconn); + virtual int downstream_error(DownstreamConnection *dconn, int events); + Downstream *add_pending_downstream(int32_t stream_id, int32_t priority); + void remove_downstream(Downstream *downstream); + + int rst_stream(Downstream *downstream, int status_code); + int error_reply(Downstream *downstream, unsigned int status_code); + + virtual void pause_read(IOCtrlReason reason); + virtual int resume_read(IOCtrlReason reason, Downstream *downstream, + size_t consumed); + + virtual int on_downstream_header_complete(Downstream *downstream); + virtual int on_downstream_body(Downstream *downstream, const uint8_t *data, + size_t len, bool flush); + virtual int on_downstream_body_complete(Downstream *downstream); + + virtual void on_handler_delete(); + virtual int on_downstream_reset(bool no_retry); + + bool get_flow_control() const; + + int consume(int32_t stream_id, size_t len); + + void start_downstream(Downstream *downstream); + void initiate_downstream(Downstream *downstream); + +private: + DownstreamQueue downstream_queue_; + ClientHandler *handler_; + spdylay_session *session_; + int32_t initial_window_size_; + bool flow_control_; +}; + +} // namespace shrpx + +#endif // SHRPX_SPDY_UPSTREAM_H diff --git a/src/shrpx_ssl.cc b/src/shrpx_ssl.cc new file mode 100644 index 0000000..3d3687e --- /dev/null +++ b/src/shrpx_ssl.cc @@ -0,0 +1,1028 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "shrpx_ssl.h" + +#ifdef HAVE_SYS_SOCKET_H +#include +#endif // HAVE_SYS_SOCKET_H +#ifdef HAVE_NETDB_H +#include +#endif // HAVE_NETDB_H +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include + +#ifdef HAVE_SPDYLAY +#include +#endif // HAVE_SPDYLAY + +#include "shrpx_log.h" +#include "shrpx_client_handler.h" +#include "shrpx_config.h" +#include "shrpx_worker.h" +#include "shrpx_downstream_connection_pool.h" +#include "util.h" +#include "ssl.h" +#include "template.h" + +using namespace nghttp2; + +namespace shrpx { + +namespace ssl { + +namespace { +int next_proto_cb(SSL *s, const unsigned char **data, unsigned int *len, + void *arg) { + auto &prefs = get_config()->alpn_prefs; + *data = prefs.data(); + *len = prefs.size(); + return SSL_TLSEXT_ERR_OK; +} +} // namespace + +namespace { +int verify_callback(int preverify_ok, X509_STORE_CTX *ctx) { + if (!preverify_ok) { + int err = X509_STORE_CTX_get_error(ctx); + int depth = X509_STORE_CTX_get_error_depth(ctx); + LOG(ERROR) << "client certificate verify error:num=" << err << ":" + << X509_verify_cert_error_string(err) << ":depth=" << depth; + } + return preverify_ok; +} +} // namespace + +std::vector set_alpn_prefs(const std::vector &protos) { + size_t len = 0; + + for (auto proto : protos) { + auto n = strlen(proto); + + if (n > 255) { + LOG(FATAL) << "Too long ALPN identifier: " << n; + DIE(); + } + + len += 1 + n; + } + + if (len > (1 << 16) - 1) { + LOG(FATAL) << "Too long ALPN identifier list: " << len; + DIE(); + } + + auto out = std::vector(len); + auto ptr = out.data(); + + for (auto proto : protos) { + auto proto_len = strlen(proto); + + *ptr++ = proto_len; + memcpy(ptr, proto, proto_len); + ptr += proto_len; + } + + return out; +} + +namespace { +int ssl_pem_passwd_cb(char *buf, int size, int rwflag, void *user_data) { + auto config = static_cast(user_data); + int len = (int)strlen(config->private_key_passwd.get()); + if (size < len + 1) { + LOG(ERROR) << "ssl_pem_passwd_cb: buf is too small " << size; + return 0; + } + // Copy string including last '\0'. + memcpy(buf, config->private_key_passwd.get(), len + 1); + return len; +} +} // namespace + +namespace { +int servername_callback(SSL *ssl, int *al, void *arg) { + auto handler = static_cast(SSL_get_app_data(ssl)); + auto worker = handler->get_worker(); + auto cert_tree = worker->get_cert_lookup_tree(); + if (cert_tree) { + const char *hostname = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name); + if (hostname) { + auto ssl_ctx = cert_tree->lookup(hostname, strlen(hostname)); + if (ssl_ctx) { + SSL_set_SSL_CTX(ssl, ssl_ctx); + } + } + } + return SSL_TLSEXT_ERR_OK; +} +} // namespace + +namespace { +int ocsp_resp_cb(SSL *ssl, void *arg) { + auto ssl_ctx = SSL_get_SSL_CTX(ssl); + auto tls_ctx_data = + static_cast(SSL_CTX_get_app_data(ssl_ctx)); + { + std::lock_guard g(tls_ctx_data->mu); + auto &data = tls_ctx_data->ocsp_data; + + if (!data.empty()) { + auto buf = static_cast( + CRYPTO_malloc(data.size(), __FILE__, __LINE__)); + + if (!buf) { + return SSL_TLSEXT_ERR_OK; + } + + std::copy(std::begin(data), std::end(data), buf); + + SSL_set_tlsext_status_ocsp_resp(ssl, buf, data.size()); + } + } + return SSL_TLSEXT_ERR_OK; +} +} // namespace + +namespace { +int ticket_key_cb(SSL *ssl, unsigned char *key_name, unsigned char *iv, + EVP_CIPHER_CTX *ctx, HMAC_CTX *hctx, int enc) { + auto handler = static_cast(SSL_get_app_data(ssl)); + auto worker = handler->get_worker(); + const auto &ticket_keys = worker->get_ticket_keys(); + + if (!ticket_keys) { + // No ticket keys available. + return -1; + } + + auto &keys = ticket_keys->keys; + assert(!keys.empty()); + + if (enc) { + if (RAND_bytes(iv, EVP_MAX_IV_LENGTH) == 0) { + if (LOG_ENABLED(INFO)) { + CLOG(INFO, handler) << "session ticket key: RAND_bytes failed"; + } + return -1; + } + + auto &key = keys[0]; + + if (LOG_ENABLED(INFO)) { + CLOG(INFO, handler) << "encrypt session ticket key: " + << util::format_hex(key.name, 16); + } + + memcpy(key_name, key.name, sizeof(key.name)); + + EVP_EncryptInit_ex(ctx, EVP_aes_128_cbc(), nullptr, key.aes_key, iv); + HMAC_Init_ex(hctx, key.hmac_key, sizeof(key.hmac_key), EVP_sha256(), + nullptr); + return 1; + } + + size_t i; + for (i = 0; i < keys.size(); ++i) { + auto &key = keys[0]; + if (memcmp(key.name, key_name, sizeof(key.name)) == 0) { + break; + } + } + + if (i == keys.size()) { + if (LOG_ENABLED(INFO)) { + CLOG(INFO, handler) << "session ticket key " + << util::format_hex(key_name, 16) << " not found"; + } + return 0; + } + + if (LOG_ENABLED(INFO)) { + CLOG(INFO, handler) << "decrypt session ticket key: " + << util::format_hex(key_name, 16); + } + + auto &key = keys[i]; + HMAC_Init_ex(hctx, key.hmac_key, sizeof(key.hmac_key), EVP_sha256(), nullptr); + EVP_DecryptInit_ex(ctx, EVP_aes_128_cbc(), nullptr, key.aes_key, iv); + + return i == 0 ? 1 : 2; +} +} // namespace + +namespace { +void info_callback(const SSL *ssl, int where, int ret) { + // To mitigate possible DOS attack using lots of renegotiations, we + // disable renegotiation. Since OpenSSL does not provide an easy way + // to disable it, we check that renegotiation is started in this + // callback. + if (where & SSL_CB_HANDSHAKE_START) { + auto conn = static_cast(SSL_get_app_data(ssl)); + if (conn && conn->tls.initial_handshake_done) { + // We only set SSL_get_app_data for ClientHandler for now. + auto handler = static_cast(conn->data); + if (LOG_ENABLED(INFO)) { + CLOG(INFO, handler) << "TLS renegotiation started"; + } + handler->start_immediate_shutdown(); + } + } +} +} // namespace + +#if OPENSSL_VERSION_NUMBER >= 0x10002000L +namespace { +int alpn_select_proto_cb(SSL *ssl, const unsigned char **out, + unsigned char *outlen, const unsigned char *in, + unsigned int inlen, void *arg) { + // We assume that get_config()->npn_list contains ALPN protocol + // identifier sorted by preference order. So we just break when we + // found the first overlap. + for (auto target_proto_id : get_config()->npn_list) { + auto target_proto_len = + strlen(reinterpret_cast(target_proto_id)); + + for (auto p = in, end = in + inlen; p < end;) { + auto proto_id = p + 1; + auto proto_len = *p; + + if (proto_id + proto_len <= end && target_proto_len == proto_len && + memcmp(target_proto_id, proto_id, proto_len) == 0) { + + *out = reinterpret_cast(proto_id); + *outlen = proto_len; + + return SSL_TLSEXT_ERR_OK; + } + + p += 1 + proto_len; + } + } + + return SSL_TLSEXT_ERR_NOACK; +} +} // namespace +#endif // OPENSSL_VERSION_NUMBER >= 0x10002000L + +namespace { +const char *tls_names[] = {"TLSv1.2", "TLSv1.1", "TLSv1.0"}; +const size_t tls_namelen = array_size(tls_names); +const long int tls_masks[] = {SSL_OP_NO_TLSv1_2, SSL_OP_NO_TLSv1_1, + SSL_OP_NO_TLSv1}; +} // namespace + +long int create_tls_proto_mask(const std::vector &tls_proto_list) { + long int res = 0; + + for (size_t i = 0; i < tls_namelen; ++i) { + size_t j; + for (j = 0; j < tls_proto_list.size(); ++j) { + if (util::strieq(tls_names[i], tls_proto_list[j])) { + break; + } + } + if (j == tls_proto_list.size()) { + res |= tls_masks[i]; + } + } + return res; +} + +SSL_CTX *create_ssl_context(const char *private_key_file, + const char *cert_file) { + auto ssl_ctx = SSL_CTX_new(SSLv23_server_method()); + if (!ssl_ctx) { + LOG(FATAL) << ERR_error_string(ERR_get_error(), nullptr); + DIE(); + } + + SSL_CTX_set_options( + ssl_ctx, + SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_COMPRESSION | + SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION | + SSL_OP_SINGLE_ECDH_USE | SSL_OP_SINGLE_DH_USE | + SSL_OP_CIPHER_SERVER_PREFERENCE | get_config()->tls_proto_mask); + + const unsigned char sid_ctx[] = "shrpx"; + SSL_CTX_set_session_id_context(ssl_ctx, sid_ctx, sizeof(sid_ctx) - 1); + SSL_CTX_set_session_cache_mode(ssl_ctx, SSL_SESS_CACHE_SERVER); + + const char *ciphers; + if (get_config()->ciphers) { + ciphers = get_config()->ciphers.get(); + } else { + ciphers = nghttp2::ssl::DEFAULT_CIPHER_LIST; + } + + if (SSL_CTX_set_cipher_list(ssl_ctx, ciphers) == 0) { + LOG(FATAL) << "SSL_CTX_set_cipher_list " << ciphers + << " failed: " << ERR_error_string(ERR_get_error(), nullptr); + DIE(); + } + +#ifndef OPENSSL_NO_EC + + // Disabled SSL_CTX_set_ecdh_auto, because computational cost of + // chosen curve is much higher than P-256. + + // #if OPENSSL_VERSION_NUMBER >= 0x10002000L + // SSL_CTX_set_ecdh_auto(ssl_ctx, 1); + // #else // OPENSSL_VERSION_NUBMER < 0x10002000L + // Use P-256, which is sufficiently secure at the time of this + // writing. + auto ecdh = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1); + if (ecdh == nullptr) { + LOG(FATAL) << "EC_KEY_new_by_curv_name failed: " + << ERR_error_string(ERR_get_error(), nullptr); + DIE(); + } + SSL_CTX_set_tmp_ecdh(ssl_ctx, ecdh); + EC_KEY_free(ecdh); +// #endif // OPENSSL_VERSION_NUBMER < 0x10002000L + +#endif // OPENSSL_NO_EC + + if (get_config()->dh_param_file) { + // Read DH parameters from file + auto bio = BIO_new_file(get_config()->dh_param_file.get(), "r"); + if (bio == nullptr) { + LOG(FATAL) << "BIO_new_file() failed: " + << ERR_error_string(ERR_get_error(), nullptr); + DIE(); + } + auto dh = PEM_read_bio_DHparams(bio, nullptr, nullptr, nullptr); + if (dh == nullptr) { + LOG(FATAL) << "PEM_read_bio_DHparams() failed: " + << ERR_error_string(ERR_get_error(), nullptr); + DIE(); + } + SSL_CTX_set_tmp_dh(ssl_ctx, dh); + DH_free(dh); + BIO_free(bio); + } + + SSL_CTX_set_mode(ssl_ctx, SSL_MODE_AUTO_RETRY); + SSL_CTX_set_mode(ssl_ctx, SSL_MODE_RELEASE_BUFFERS); + if (get_config()->private_key_passwd) { + SSL_CTX_set_default_passwd_cb(ssl_ctx, ssl_pem_passwd_cb); + SSL_CTX_set_default_passwd_cb_userdata(ssl_ctx, (void *)get_config()); + } + if (SSL_CTX_use_PrivateKey_file(ssl_ctx, private_key_file, + SSL_FILETYPE_PEM) != 1) { + LOG(FATAL) << "SSL_CTX_use_PrivateKey_file failed: " + << ERR_error_string(ERR_get_error(), nullptr); + DIE(); + } + if (SSL_CTX_use_certificate_chain_file(ssl_ctx, cert_file) != 1) { + LOG(FATAL) << "SSL_CTX_use_certificate_file failed: " + << ERR_error_string(ERR_get_error(), nullptr); + DIE(); + } + if (SSL_CTX_check_private_key(ssl_ctx) != 1) { + LOG(FATAL) << "SSL_CTX_check_private_key failed: " + << ERR_error_string(ERR_get_error(), nullptr); + DIE(); + } + if (get_config()->verify_client) { + if (get_config()->verify_client_cacert) { + if (SSL_CTX_load_verify_locations( + ssl_ctx, get_config()->verify_client_cacert.get(), nullptr) != + 1) { + + LOG(FATAL) << "Could not load trusted ca certificates from " + << get_config()->verify_client_cacert.get() << ": " + << ERR_error_string(ERR_get_error(), nullptr); + DIE(); + } + // It is heard that SSL_CTX_load_verify_locations() may leave + // error even though it returns success. See + // http://forum.nginx.org/read.php?29,242540 + ERR_clear_error(); + auto list = + SSL_load_client_CA_file(get_config()->verify_client_cacert.get()); + if (!list) { + LOG(FATAL) << "Could not load ca certificates from " + << get_config()->verify_client_cacert.get() << ": " + << ERR_error_string(ERR_get_error(), nullptr); + DIE(); + } + SSL_CTX_set_client_CA_list(ssl_ctx, list); + } + SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE | + SSL_VERIFY_FAIL_IF_NO_PEER_CERT, + verify_callback); + } + SSL_CTX_set_tlsext_servername_callback(ssl_ctx, servername_callback); + SSL_CTX_set_tlsext_ticket_key_cb(ssl_ctx, ticket_key_cb); + SSL_CTX_set_tlsext_status_cb(ssl_ctx, ocsp_resp_cb); + SSL_CTX_set_info_callback(ssl_ctx, info_callback); + + // NPN advertisement + SSL_CTX_set_next_protos_advertised_cb(ssl_ctx, next_proto_cb, nullptr); +#if OPENSSL_VERSION_NUMBER >= 0x10002000L + // ALPN selection callback + SSL_CTX_set_alpn_select_cb(ssl_ctx, alpn_select_proto_cb, nullptr); +#endif // OPENSSL_VERSION_NUMBER >= 0x10002000L + + auto tls_ctx_data = new TLSContextData(); + tls_ctx_data->cert_file = cert_file; + + SSL_CTX_set_app_data(ssl_ctx, tls_ctx_data); + + return ssl_ctx; +} + +namespace { +int select_next_proto_cb(SSL *ssl, unsigned char **out, unsigned char *outlen, + const unsigned char *in, unsigned int inlen, + void *arg) { + if (!util::select_h2(const_cast(out), outlen, in, + inlen)) { + return SSL_TLSEXT_ERR_NOACK; + } + + return SSL_TLSEXT_ERR_OK; +} +} // namespace + +SSL_CTX *create_ssl_client_context() { + auto ssl_ctx = SSL_CTX_new(SSLv23_client_method()); + if (!ssl_ctx) { + LOG(FATAL) << ERR_error_string(ERR_get_error(), nullptr); + DIE(); + } + SSL_CTX_set_options(ssl_ctx, + SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | + SSL_OP_NO_COMPRESSION | + SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION | + get_config()->tls_proto_mask); + + const char *ciphers; + if (get_config()->ciphers) { + ciphers = get_config()->ciphers.get(); + } else { + ciphers = "HIGH:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!3DES:!MD5:!PSK"; + } + if (SSL_CTX_set_cipher_list(ssl_ctx, ciphers) == 0) { + LOG(FATAL) << "SSL_CTX_set_cipher_list " << ciphers + << " failed: " << ERR_error_string(ERR_get_error(), nullptr); + DIE(); + } + + SSL_CTX_set_mode(ssl_ctx, SSL_MODE_AUTO_RETRY); + SSL_CTX_set_mode(ssl_ctx, SSL_MODE_RELEASE_BUFFERS); + + if (SSL_CTX_set_default_verify_paths(ssl_ctx) != 1) { + LOG(WARN) << "Could not load system trusted ca certificates: " + << ERR_error_string(ERR_get_error(), nullptr); + } + + if (get_config()->cacert) { + if (SSL_CTX_load_verify_locations(ssl_ctx, get_config()->cacert.get(), + nullptr) != 1) { + + LOG(FATAL) << "Could not load trusted ca certificates from " + << get_config()->cacert.get() << ": " + << ERR_error_string(ERR_get_error(), nullptr); + DIE(); + } + } + + if (get_config()->client_private_key_file) { + if (SSL_CTX_use_PrivateKey_file(ssl_ctx, + get_config()->client_private_key_file.get(), + SSL_FILETYPE_PEM) != 1) { + LOG(FATAL) << "Could not load client private key from " + << get_config()->client_private_key_file.get() << ": " + << ERR_error_string(ERR_get_error(), nullptr); + DIE(); + } + } + if (get_config()->client_cert_file) { + if (SSL_CTX_use_certificate_chain_file( + ssl_ctx, get_config()->client_cert_file.get()) != 1) { + + LOG(FATAL) << "Could not load client certificate from " + << get_config()->client_cert_file.get() << ": " + << ERR_error_string(ERR_get_error(), nullptr); + DIE(); + } + } + // NPN selection callback + SSL_CTX_set_next_proto_select_cb(ssl_ctx, select_next_proto_cb, nullptr); + +#if OPENSSL_VERSION_NUMBER >= 0x10002000L + // ALPN advertisement; We only advertise HTTP/2 + auto proto_list = util::get_default_alpn(); + + SSL_CTX_set_alpn_protos(ssl_ctx, proto_list.data(), proto_list.size()); +#endif // OPENSSL_VERSION_NUMBER >= 0x10002000L + + return ssl_ctx; +} + +ClientHandler *accept_connection(Worker *worker, int fd, sockaddr *addr, + int addrlen) { + char host[NI_MAXHOST]; + char service[NI_MAXSERV]; + int rv; + rv = getnameinfo(addr, addrlen, host, sizeof(host), service, sizeof(service), + NI_NUMERICHOST | NI_NUMERICSERV); + if (rv != 0) { + LOG(ERROR) << "getnameinfo() failed: " << gai_strerror(rv); + + return nullptr; + } + + int val = 1; + rv = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, reinterpret_cast(&val), + sizeof(val)); + if (rv == -1) { + LOG(WARN) << "Setting option TCP_NODELAY failed: errno=" << errno; + } + SSL *ssl = nullptr; + auto ssl_ctx = worker->get_sv_ssl_ctx(); + if (ssl_ctx) { + ssl = SSL_new(ssl_ctx); + if (!ssl) { + LOG(ERROR) << "SSL_new() failed: " << ERR_error_string(ERR_get_error(), + nullptr); + return nullptr; + } + + if (SSL_set_fd(ssl, fd) == 0) { + LOG(ERROR) << "SSL_set_fd() failed: " << ERR_error_string(ERR_get_error(), + nullptr); + SSL_free(ssl); + return nullptr; + } + + SSL_set_accept_state(ssl); + } + + return new ClientHandler(worker, fd, ssl, host, service); +} + +namespace { +bool tls_hostname_match(const char *pattern, const char *hostname) { + const char *ptWildcard = strchr(pattern, '*'); + if (ptWildcard == nullptr) { + return util::strieq(pattern, hostname); + } + const char *ptLeftLabelEnd = strchr(pattern, '.'); + bool wildcardEnabled = true; + // Do case-insensitive match. At least 2 dots are required to enable + // wildcard match. Also wildcard must be in the left-most label. + // Don't attempt to match a presented identifier where the wildcard + // character is embedded within an A-label. + if (ptLeftLabelEnd == 0 || strchr(ptLeftLabelEnd + 1, '.') == 0 || + ptLeftLabelEnd < ptWildcard || util::istartsWith(pattern, "xn--")) { + wildcardEnabled = false; + } + if (!wildcardEnabled) { + return util::strieq(pattern, hostname); + } + const char *hnLeftLabelEnd = strchr(hostname, '.'); + if (hnLeftLabelEnd == 0 || !util::strieq(ptLeftLabelEnd, hnLeftLabelEnd)) { + return false; + } + // Perform wildcard match. Here '*' must match at least one + // character. + if (hnLeftLabelEnd - hostname < ptLeftLabelEnd - pattern) { + return false; + } + return util::istartsWith(hostname, hnLeftLabelEnd, pattern, ptWildcard) && + util::iendsWith(hostname, hnLeftLabelEnd, ptWildcard + 1, + ptLeftLabelEnd); +} +} // namespace + +namespace { +int verify_hostname(const char *hostname, const sockaddr_union *su, + size_t salen, const std::vector &dns_names, + const std::vector &ip_addrs, + const std::string &common_name) { + if (util::numeric_host(hostname)) { + if (ip_addrs.empty()) { + return util::strieq(common_name.c_str(), hostname) ? 0 : -1; + } + const void *saddr; + switch (su->storage.ss_family) { + case AF_INET: + saddr = &su->in.sin_addr; + break; + case AF_INET6: + saddr = &su->in6.sin6_addr; + break; + default: + return -1; + } + for (size_t i = 0; i < ip_addrs.size(); ++i) { + if (salen == ip_addrs[i].size() && + memcmp(saddr, ip_addrs[i].c_str(), salen) == 0) { + return 0; + } + } + } else { + if (dns_names.empty()) { + return tls_hostname_match(common_name.c_str(), hostname) ? 0 : -1; + } + for (size_t i = 0; i < dns_names.size(); ++i) { + if (tls_hostname_match(dns_names[i].c_str(), hostname)) { + return 0; + } + } + } + return -1; +} +} // namespace + +void get_altnames(X509 *cert, std::vector &dns_names, + std::vector &ip_addrs, + std::string &common_name) { + GENERAL_NAMES *altnames = static_cast( + X509_get_ext_d2i(cert, NID_subject_alt_name, nullptr, nullptr)); + if (altnames) { + auto altnames_deleter = defer(GENERAL_NAMES_free, altnames); + size_t n = sk_GENERAL_NAME_num(altnames); + for (size_t i = 0; i < n; ++i) { + const GENERAL_NAME *altname = sk_GENERAL_NAME_value(altnames, i); + if (altname->type == GEN_DNS) { + const char *name; + name = reinterpret_cast(ASN1_STRING_data(altname->d.ia5)); + if (!name) { + continue; + } + size_t len = ASN1_STRING_length(altname->d.ia5); + if (std::find(name, name + len, '\0') != name + len) { + // Embedded NULL is not permitted. + continue; + } + dns_names.push_back(std::string(name, len)); + } else if (altname->type == GEN_IPADD) { + const unsigned char *ip_addr = altname->d.iPAddress->data; + if (!ip_addr) { + continue; + } + size_t len = altname->d.iPAddress->length; + ip_addrs.push_back( + std::string(reinterpret_cast(ip_addr), len)); + } + } + } + X509_NAME *subjectname = X509_get_subject_name(cert); + if (!subjectname) { + LOG(WARN) << "Could not get X509 name object from the certificate."; + return; + } + int lastpos = -1; + while (1) { + lastpos = X509_NAME_get_index_by_NID(subjectname, NID_commonName, lastpos); + if (lastpos == -1) { + break; + } + X509_NAME_ENTRY *entry = X509_NAME_get_entry(subjectname, lastpos); + unsigned char *out; + int outlen = ASN1_STRING_to_UTF8(&out, X509_NAME_ENTRY_get_data(entry)); + if (outlen < 0) { + continue; + } + if (std::find(out, out + outlen, '\0') != out + outlen) { + // Embedded NULL is not permitted. + continue; + } + common_name.assign(&out[0], &out[outlen]); + OPENSSL_free(out); + break; + } +} + +int check_cert(SSL *ssl) { + auto cert = SSL_get_peer_certificate(ssl); + if (!cert) { + LOG(ERROR) << "No certificate found"; + return -1; + } + auto cert_deleter = defer(X509_free, cert); + long verify_res = SSL_get_verify_result(ssl); + if (verify_res != X509_V_OK) { + LOG(ERROR) << "Certificate verification failed: " + << X509_verify_cert_error_string(verify_res); + return -1; + } + std::string common_name; + std::vector dns_names; + std::vector ip_addrs; + get_altnames(cert, dns_names, ip_addrs, common_name); + if (verify_hostname(get_config()->downstream_addrs[0].host.get(), + &get_config()->downstream_addrs[0].addr, + get_config()->downstream_addrs[0].addrlen, dns_names, + ip_addrs, common_name) != 0) { + LOG(ERROR) << "Certificate verification failed: hostname does not match"; + return -1; + } + return 0; +} + +CertLookupTree::CertLookupTree() { + root_.ssl_ctx = nullptr; + root_.str = nullptr; + root_.first = root_.last = 0; +} + +namespace { +// The |offset| is the index in the hostname we are examining. We are +// going to scan from |offset| in backwards. +void cert_lookup_tree_add_cert(CertNode *node, SSL_CTX *ssl_ctx, char *hostname, + size_t len, int offset) { + int i, next_len = node->next.size(); + char c = hostname[offset]; + CertNode *cn = nullptr; + for (i = 0; i < next_len; ++i) { + cn = node->next[i].get(); + if (cn->str[cn->first] == c) { + break; + } + } + if (i == next_len) { + if (c == '*') { + // We assume hostname as wildcard hostname when first '*' is + // encountered. Note that as per RFC 6125 (6.4.3), there are + // some restrictions for wildcard hostname. We just ignore + // these rules here but do the proper check when we do the + // match. + node->wildcard_certs.emplace_back(hostname, ssl_ctx); + return; + } + + int j; + auto new_node = make_unique(); + new_node->str = hostname; + new_node->first = offset; + // If wildcard is found, set the region before it because we + // don't include it in [first, last). + for (j = offset; j >= 0 && hostname[j] != '*'; --j) + ; + new_node->last = j; + if (j == -1) { + new_node->ssl_ctx = ssl_ctx; + } else { + new_node->ssl_ctx = nullptr; + new_node->wildcard_certs.emplace_back(hostname, ssl_ctx); + } + node->next.push_back(std::move(new_node)); + return; + } + + int j; + for (i = cn->first, j = offset; + i > cn->last && j >= 0 && cn->str[i] == hostname[j]; --i, --j) + ; + if (i == cn->last) { + if (j == -1) { + // If the same hostname already exists, we don't overwrite + // exiting ssl_ctx + if (!cn->ssl_ctx) { + cn->ssl_ctx = ssl_ctx; + } + return; + } + + // The existing hostname is a suffix of this hostname. Continue + // matching at potion j. + cert_lookup_tree_add_cert(cn, ssl_ctx, hostname, len, j); + return; + } + + { + auto new_node = make_unique(); + new_node->ssl_ctx = cn->ssl_ctx; + new_node->str = cn->str; + new_node->first = i; + new_node->last = cn->last; + new_node->wildcard_certs.swap(cn->wildcard_certs); + new_node->next.swap(cn->next); + + cn->next.push_back(std::move(new_node)); + } + + cn->last = i; + if (j == -1) { + // This hostname is a suffix of the existing hostname. + cn->ssl_ctx = ssl_ctx; + return; + } + + // This hostname and existing one share suffix. + cn->ssl_ctx = nullptr; + cert_lookup_tree_add_cert(cn, ssl_ctx, hostname, len, j); +} +} // namespace + +void CertLookupTree::add_cert(SSL_CTX *ssl_ctx, const char *hostname, + size_t len) { + if (len == 0) { + return; + } + // Copy hostname including terminal NULL + hosts_.push_back(make_unique(len + 1)); + const auto &host_copy = hosts_.back(); + for (size_t i = 0; i < len; ++i) { + host_copy[i] = util::lowcase(hostname[i]); + } + host_copy[len] = '\0'; + cert_lookup_tree_add_cert(&root_, ssl_ctx, host_copy.get(), len, len - 1); +} + +namespace { +SSL_CTX *cert_lookup_tree_lookup(CertNode *node, const char *hostname, + size_t len, int offset) { + int i, j; + for (i = node->first, j = offset; + i > node->last && j >= 0 && node->str[i] == util::lowcase(hostname[j]); + --i, --j) + ; + if (i != node->last) { + return nullptr; + } + if (j == -1) { + if (node->ssl_ctx) { + // exact match + return node->ssl_ctx; + } + + // Do not perform wildcard-match because '*' must match at least + // one character. + return nullptr; + } + for (const auto &wildcert : node->wildcard_certs) { + if (tls_hostname_match(wildcert.first, hostname)) { + return wildcert.second; + } + } + auto c = util::lowcase(hostname[j]); + for (const auto &next_node : node->next) { + if (next_node->str[next_node->first] == c) { + return cert_lookup_tree_lookup(next_node.get(), hostname, len, j); + } + } + return nullptr; +} +} // namespace + +SSL_CTX *CertLookupTree::lookup(const char *hostname, size_t len) { + return cert_lookup_tree_lookup(&root_, hostname, len, len - 1); +} + +int cert_lookup_tree_add_cert_from_file(CertLookupTree *lt, SSL_CTX *ssl_ctx, + const char *certfile) { + auto bio = BIO_new(BIO_s_file()); + if (!bio) { + LOG(ERROR) << "BIO_new failed"; + return -1; + } + auto bio_deleter = defer(BIO_vfree, bio); + if (!BIO_read_filename(bio, certfile)) { + LOG(ERROR) << "Could not read certificate file '" << certfile << "'"; + return -1; + } + auto cert = PEM_read_bio_X509(bio, nullptr, nullptr, nullptr); + if (!cert) { + LOG(ERROR) << "Could not read X509 structure from file '" << certfile + << "'"; + return -1; + } + auto cert_deleter = defer(X509_free, cert); + std::string common_name; + std::vector dns_names; + std::vector ip_addrs; + get_altnames(cert, dns_names, ip_addrs, common_name); + for (auto &dns_name : dns_names) { + lt->add_cert(ssl_ctx, dns_name.c_str(), dns_name.size()); + } + lt->add_cert(ssl_ctx, common_name.c_str(), common_name.size()); + return 0; +} + +bool in_proto_list(const std::vector &protos, + const unsigned char *needle, size_t len) { + for (auto proto : protos) { + if (strlen(proto) == len && memcmp(proto, needle, len) == 0) { + return true; + } + } + return false; +} + +bool check_http2_requirement(SSL *ssl) { + auto tls_ver = SSL_version(ssl); + + switch (tls_ver) { + case TLS1_2_VERSION: + break; + default: + if (LOG_ENABLED(INFO)) { + LOG(INFO) << "TLSv1.2 was not negotiated. " + << "HTTP/2 must not be negotiated."; + } + return false; + } + + return true; +} + +SSL_CTX *setup_server_ssl_context(std::vector &all_ssl_ctx, + CertLookupTree *cert_tree) { + if (get_config()->upstream_no_tls) { + return nullptr; + } + + auto ssl_ctx = ssl::create_ssl_context(get_config()->private_key_file.get(), + get_config()->cert_file.get()); + + all_ssl_ctx.push_back(ssl_ctx); + + if (get_config()->subcerts.empty()) { + return ssl_ctx; + } + + if (!cert_tree) { + LOG(WARN) << "We have multiple additional certificates (--subcert), but " + "cert_tree is not given. SNI may not work."; + return ssl_ctx; + } + + for (auto &keycert : get_config()->subcerts) { + auto ssl_ctx = + ssl::create_ssl_context(keycert.first.c_str(), keycert.second.c_str()); + all_ssl_ctx.push_back(ssl_ctx); + if (ssl::cert_lookup_tree_add_cert_from_file( + cert_tree, ssl_ctx, keycert.second.c_str()) == -1) { + LOG(FATAL) << "Failed to add sub certificate."; + DIE(); + } + } + + if (ssl::cert_lookup_tree_add_cert_from_file( + cert_tree, ssl_ctx, get_config()->cert_file.get()) == -1) { + LOG(FATAL) << "Failed to add default certificate."; + DIE(); + } + + return ssl_ctx; +} + +SSL_CTX *setup_client_ssl_context() { + if (get_config()->client_mode) { + return get_config()->downstream_no_tls ? nullptr + : ssl::create_ssl_client_context(); + } + + return get_config()->http2_bridge && !get_config()->downstream_no_tls + ? ssl::create_ssl_client_context() + : nullptr; +} + +CertLookupTree *create_cert_lookup_tree() { + if (get_config()->upstream_no_tls || get_config()->subcerts.empty()) { + return nullptr; + } + return new ssl::CertLookupTree(); +} + +} // namespace ssl + +} // namespace shrpx diff --git a/src/shrpx_ssl.h b/src/shrpx_ssl.h new file mode 100644 index 0000000..5f808cb --- /dev/null +++ b/src/shrpx_ssl.h @@ -0,0 +1,177 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef SHRPX_SSL_H +#define SHRPX_SSL_H + +#include "shrpx.h" + +#include +#include + +#include +#include + +#include + +namespace shrpx { + +class ClientHandler; +class Worker; +class DownstreamConnectionPool; + +namespace ssl { + +// This struct stores the additional information per SSL_CTX. This is +// attached to SSL_CTX using SSL_CTX_set_app_data(). +struct TLSContextData { + // Protects ocsp_data; + std::mutex mu; + // OCSP resonse + std::vector ocsp_data; + + // Path to certificate file + const char *cert_file; +}; + +// Create server side SSL_CTX +SSL_CTX *create_ssl_context(const char *private_key_file, + const char *cert_file); + +// Create client side SSL_CTX +SSL_CTX *create_ssl_client_context(); + +ClientHandler *accept_connection(Worker *worker, int fd, sockaddr *addr, + int addrlen); + +// Check peer's certificate against first downstream address in +// Config::downstream_addrs. We only consider first downstream since +// we use this function for HTTP/2 downstream link only. +int check_cert(SSL *ssl); + +// Retrieves DNS and IP address in subjectAltNames and commonName from +// the |cert|. +void get_altnames(X509 *cert, std::vector &dns_names, + std::vector &ip_addrs, std::string &common_name); + +// CertLookupTree forms lookup tree to get SSL_CTX whose DNS or +// commonName matches hostname in query. The tree is patricia trie +// data structure formed from the tail of the hostname pattern. Each +// CertNode contains part of hostname str member in range [first, +// last) member and the next member contains the following CertNode +// pointers ('following' means character before the current one). The +// CertNode where a hostname pattern ends contains its SSL_CTX pointer +// in the ssl_ctx member. For wildcard hostname pattern, we store the +// its pattern and SSL_CTX in CertNode one before first "*" found from +// the tail. +// +// When querying SSL_CTX with particular hostname, we match from its +// tail in our lookup tree. If the query goes to the first character +// of the hostname and current CertNode has non-NULL ssl_ctx member, +// then it is the exact match. The ssl_ctx member is returned. Along +// the way, if CertNode which contains non-empty wildcard_certs member +// is encountered, wildcard hostname matching is performed against +// them. If there is a match, its SSL_CTX is returned. If none +// matches, query is continued to the next character. + +struct CertNode { + // list of wildcard domain name and its SSL_CTX pair, the wildcard + // '*' appears in this position. + std::vector> wildcard_certs; + // Next CertNode index of CertLookupTree::nodes + std::vector> next; + // SSL_CTX for exact match + SSL_CTX *ssl_ctx; + char *str; + // [first, last) in the reverse direction in str, first >= + // last. This indices only work for str member. + int first, last; +}; + +class CertLookupTree { +public: + CertLookupTree(); + + // Adds |ssl_ctx| with hostname pattern |hostname| with length |len| + // to the lookup tree. The |hostname| must be NULL-terminated. + void add_cert(SSL_CTX *ssl_ctx, const char *hostname, size_t len); + + // Looks up SSL_CTX using the given |hostname| with length |len|. + // If more than one SSL_CTX which matches the query, it is undefined + // which one is returned. The |hostname| must be NULL-terminated. + // If no matching SSL_CTX found, returns NULL. + SSL_CTX *lookup(const char *hostname, size_t len); + +private: + CertNode root_; + // Stores pointers to copied hostname when adding hostname and + // ssl_ctx pair. + std::vector> hosts_; +}; + +// Adds |ssl_ctx| to lookup tree |lt| using hostnames read from +// |certfile|. The subjectAltNames and commonName are considered as +// eligible hostname. This function returns 0 if it succeeds, or -1. +// Even if no ssl_ctx is added to tree, this function returns 0. +int cert_lookup_tree_add_cert_from_file(CertLookupTree *lt, SSL_CTX *ssl_ctx, + const char *certfile); + +// Returns true if |needle| which has |len| bytes is included in the +// protocol list |protos|. +bool in_proto_list(const std::vector &protos, + const unsigned char *needle, size_t len); + +// Returns true if security requirement for HTTP/2 is fulfilled. +bool check_http2_requirement(SSL *ssl); + +// Returns SSL/TLS option mask to disable SSL/TLS protocol version not +// included in |tls_proto_list|. The returned mask can be directly +// passed to SSL_CTX_set_options(). +long int create_tls_proto_mask(const std::vector &tls_proto_list); + +std::vector set_alpn_prefs(const std::vector &protos); + +// Setups server side SSL_CTX. This function inspects get_config() +// and if upstream_no_tls is true, returns nullptr. Otherwise +// construct default SSL_CTX. If subcerts are available +// (get_config()->subcerts), caller should provide CertLookupTree +// object as |cert_tree| parameter, otherwise SNI does not work. All +// the created SSL_CTX is stored into |all_ssl_ctx|. +SSL_CTX *setup_server_ssl_context(std::vector &all_ssl_ctx, + CertLookupTree *cert_tree); + +// Setups client side SSL_CTX. This function inspects get_config() +// and if downstream_no_tls is true, returns nullptr. Otherwise, only +// construct SSL_CTX if either client_mode or http2_bridge is true. +SSL_CTX *setup_client_ssl_context(); + +// Creates CertLookupTree. If frontend is configured not to use TLS, +// this function returns nullptr. +CertLookupTree *create_cert_lookup_tree(); + +} // namespace ssl + +} // namespace shrpx + +#endif // SHRPX_SSL_H diff --git a/src/shrpx_ssl_test.cc b/src/shrpx_ssl_test.cc new file mode 100644 index 0000000..22bac63 --- /dev/null +++ b/src/shrpx_ssl_test.cc @@ -0,0 +1,119 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2013 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "shrpx_ssl_test.h" + +#include + +#include "shrpx_ssl.h" +#include "util.h" +#include "template.h" + +using namespace nghttp2; + +namespace shrpx { + +void test_shrpx_ssl_create_lookup_tree(void) { + auto tree = make_unique(); + SSL_CTX *ctxs[] = { + SSL_CTX_new(SSLv23_method()), SSL_CTX_new(SSLv23_method()), + SSL_CTX_new(SSLv23_method()), SSL_CTX_new(SSLv23_method()), + SSL_CTX_new(SSLv23_method()), SSL_CTX_new(SSLv23_method()), + SSL_CTX_new(SSLv23_method()), SSL_CTX_new(SSLv23_method()), + SSL_CTX_new(SSLv23_method()), SSL_CTX_new(SSLv23_method())}; + + const char *hostnames[] = {"example.com", "www.example.org", + "*www.example.org", "x*.host.domain", + "*yy.host.domain", "nghttp2.sourceforge.net", + "sourceforge.net", + "sourceforge.net", // duplicate + "*.foo.bar", // oo.bar is suffix of *.foo.bar + "oo.bar"}; + int num = array_size(ctxs); + for (int i = 0; i < num; ++i) { + tree->add_cert(ctxs[i], hostnames[i], strlen(hostnames[i])); + } + + CU_ASSERT(ctxs[0] == tree->lookup(hostnames[0], strlen(hostnames[0]))); + CU_ASSERT(ctxs[1] == tree->lookup(hostnames[1], strlen(hostnames[1]))); + const char h1[] = "2www.example.org"; + CU_ASSERT(ctxs[2] == tree->lookup(h1, strlen(h1))); + const char h2[] = "www2.example.org"; + CU_ASSERT(0 == tree->lookup(h2, strlen(h2))); + const char h3[] = "x1.host.domain"; + CU_ASSERT(ctxs[3] == tree->lookup(h3, strlen(h3))); + // Does not match *yy.host.domain, because * must match at least 1 + // character. + const char h4[] = "yy.Host.domain"; + CU_ASSERT(0 == tree->lookup(h4, strlen(h4))); + const char h5[] = "zyy.host.domain"; + CU_ASSERT(ctxs[4] == tree->lookup(h5, strlen(h5))); + CU_ASSERT(0 == tree->lookup("", 0)); + CU_ASSERT(ctxs[5] == tree->lookup(hostnames[5], strlen(hostnames[5]))); + CU_ASSERT(ctxs[6] == tree->lookup(hostnames[6], strlen(hostnames[6]))); + const char h6[] = "pdylay.sourceforge.net"; + for (int i = 0; i < 7; ++i) { + CU_ASSERT(0 == tree->lookup(h6 + i, strlen(h6) - i)); + } + const char h7[] = "x.foo.bar"; + CU_ASSERT(ctxs[8] == tree->lookup(h7, strlen(h7))); + CU_ASSERT(ctxs[9] == tree->lookup(hostnames[9], strlen(hostnames[9]))); + + for (int i = 0; i < num; ++i) { + SSL_CTX_free(ctxs[i]); + } + + SSL_CTX *ctxs2[] = { + SSL_CTX_new(SSLv23_method()), SSL_CTX_new(SSLv23_method()), + SSL_CTX_new(SSLv23_method()), SSL_CTX_new(SSLv23_method())}; + const char *names[] = {"rab", "zab", "zzub", "ab"}; + num = array_size(ctxs2); + + tree = make_unique(); + for (int i = 0; i < num; ++i) { + tree->add_cert(ctxs2[i], names[i], strlen(names[i])); + } + for (int i = 0; i < num; ++i) { + CU_ASSERT(ctxs2[i] == tree->lookup(names[i], strlen(names[i]))); + } + + for (int i = 0; i < num; ++i) { + SSL_CTX_free(ctxs2[i]); + } +} + +void test_shrpx_ssl_cert_lookup_tree_add_cert_from_file(void) { + int rv; + ssl::CertLookupTree tree; + auto ssl_ctx = SSL_CTX_new(SSLv23_method()); + const char certfile[] = NGHTTP2_TESTS_DIR "/testdata/cacert.pem"; + rv = ssl::cert_lookup_tree_add_cert_from_file(&tree, ssl_ctx, certfile); + CU_ASSERT(0 == rv); + const char localhost[] = "localhost"; + CU_ASSERT(ssl_ctx == tree.lookup(localhost, sizeof(localhost) - 1)); + + SSL_CTX_free(ssl_ctx); +} + +} // namespace shrpx diff --git a/src/shrpx_ssl_test.h b/src/shrpx_ssl_test.h new file mode 100644 index 0000000..89b0044 --- /dev/null +++ b/src/shrpx_ssl_test.h @@ -0,0 +1,39 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2013 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef SHRPX_SSL_TEST_H +#define SHRPX_SSL_TEST_H + +#ifdef HAVE_CONFIG_H +#include +#endif // HAVE_CONFIG_H + +namespace shrpx { + +void test_shrpx_ssl_create_lookup_tree(void); +void test_shrpx_ssl_cert_lookup_tree_add_cert_from_file(void); + +} // namespace shrpx + +#endif // SHRPX_SSL_TEST_H diff --git a/src/shrpx_upstream.h b/src/shrpx_upstream.h new file mode 100644 index 0000000..fe9a673 --- /dev/null +++ b/src/shrpx_upstream.h @@ -0,0 +1,69 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef SHRPX_UPSTREAM_H +#define SHRPX_UPSTREAM_H + +#include "shrpx.h" +#include "shrpx_io_control.h" + +namespace shrpx { + +class ClientHandler; +class Downstream; +class DownstreamConnection; + +class Upstream { +public: + virtual ~Upstream() {} + virtual int on_read() = 0; + virtual int on_write() = 0; + virtual int on_timeout(Downstream *downstream) { return 0; }; + virtual int on_downstream_abort_request(Downstream *downstream, + unsigned int status_code) = 0; + virtual int downstream_read(DownstreamConnection *dconn) = 0; + virtual int downstream_write(DownstreamConnection *dconn) = 0; + virtual int downstream_eof(DownstreamConnection *dconn) = 0; + virtual int downstream_error(DownstreamConnection *dconn, int events) = 0; + virtual ClientHandler *get_client_handler() const = 0; + + virtual int on_downstream_header_complete(Downstream *downstream) = 0; + virtual int on_downstream_body(Downstream *downstream, const uint8_t *data, + size_t len, bool flush) = 0; + virtual int on_downstream_body_complete(Downstream *downstream) = 0; + + virtual void on_handler_delete() = 0; + // Called when downstream connection is reset. Currently this is + // only used by Http2Session. If |no_retry| is true, another + // connection attempt using new DownstreamConnection is not allowed. + virtual int on_downstream_reset(bool no_retry) = 0; + + virtual void pause_read(IOCtrlReason reason) = 0; + virtual int resume_read(IOCtrlReason reason, Downstream *downstream, + size_t consumed) = 0; +}; + +} // namespace shrpx + +#endif // SHRPX_UPSTREAM_H diff --git a/src/shrpx_worker.cc b/src/shrpx_worker.cc new file mode 100644 index 0000000..a9bf6e6 --- /dev/null +++ b/src/shrpx_worker.cc @@ -0,0 +1,242 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "shrpx_worker.h" + +#ifdef HAVE_UNISTD_H +#include +#endif // HAVE_UNISTD_H + +#include + +#include "shrpx_ssl.h" +#include "shrpx_log.h" +#include "shrpx_client_handler.h" +#include "shrpx_http2_session.h" +#include "shrpx_log_config.h" +#include "shrpx_connect_blocker.h" +#include "util.h" +#include "template.h" + +namespace shrpx { + +namespace { +void eventcb(struct ev_loop *loop, ev_async *w, int revents) { + auto worker = static_cast(w->data); + worker->process_events(); +} +} // namespace + +namespace { +void mcpool_clear_cb(struct ev_loop *loop, ev_timer *w, int revents) { + auto worker = static_cast(w->data); + if (worker->get_worker_stat()->num_connections != 0) { + return; + } + worker->get_mcpool()->clear(); +} +} // namespace + +Worker::Worker(struct ev_loop *loop, SSL_CTX *sv_ssl_ctx, SSL_CTX *cl_ssl_ctx, + ssl::CertLookupTree *cert_tree, + const std::shared_ptr &ticket_keys) + : next_http2session_(0), loop_(loop), sv_ssl_ctx_(sv_ssl_ctx), + cl_ssl_ctx_(cl_ssl_ctx), cert_tree_(cert_tree), ticket_keys_(ticket_keys), + connect_blocker_(make_unique(loop_)), + graceful_shutdown_(false) { + ev_async_init(&w_, eventcb); + w_.data = this; + ev_async_start(loop_, &w_); + + ev_timer_init(&mcpool_clear_timer_, mcpool_clear_cb, 0., 0.); + mcpool_clear_timer_.data = this; + + if (get_config()->downstream_proto == PROTO_HTTP2) { + auto n = get_config()->http2_downstream_connections_per_worker; + for (; n > 0; --n) { + http2sessions_.push_back(make_unique( + loop_, cl_ssl_ctx, connect_blocker_.get(), this)); + } + } +} + +Worker::~Worker() { + ev_async_stop(loop_, &w_); + ev_timer_stop(loop_, &mcpool_clear_timer_); +} + +void Worker::schedule_clear_mcpool() { + // libev manual says: "If the watcher is already active nothing will + // happen." Since we don't change any timeout here, we don't have + // to worry about querying ev_is_active. + ev_timer_start(loop_, &mcpool_clear_timer_); +} + +void Worker::wait() { +#ifndef NOTHREADS + fut_.get(); +#endif // !NOTHREADS +} + +void Worker::run_async() { +#ifndef NOTHREADS + fut_ = std::async(std::launch::async, [this] { + (void)reopen_log_files(); + ev_run(loop_); + }); +#endif // !NOTHREADS +} + +void Worker::send(const WorkerEvent &event) { + { + std::lock_guard g(m_); + + q_.push_back(event); + } + + ev_async_send(loop_, &w_); +} + +void Worker::process_events() { + std::deque q; + { + std::lock_guard g(m_); + q.swap(q_); + } + for (auto &wev : q) { + switch (wev.type) { + case NEW_CONNECTION: { + if (LOG_ENABLED(INFO)) { + WLOG(INFO, this) << "WorkerEvent: client_fd=" << wev.client_fd + << ", addrlen=" << wev.client_addrlen; + } + + if (worker_stat_.num_connections >= + get_config()->worker_frontend_connections) { + + if (LOG_ENABLED(INFO)) { + WLOG(INFO, this) << "Too many connections >= " + << get_config()->worker_frontend_connections; + } + + close(wev.client_fd); + + break; + } + + auto client_handler = ssl::accept_connection( + this, wev.client_fd, &wev.client_addr.sa, wev.client_addrlen); + if (!client_handler) { + if (LOG_ENABLED(INFO)) { + WLOG(ERROR, this) << "ClientHandler creation failed"; + } + close(wev.client_fd); + break; + } + + if (LOG_ENABLED(INFO)) { + WLOG(INFO, this) << "CLIENT_HANDLER:" << client_handler << " created "; + } + + break; + } + case RENEW_TICKET_KEYS: + WLOG(NOTICE, this) << "Renew ticket keys: worker(" << this << ")"; + + ticket_keys_ = wev.ticket_keys; + + break; + case REOPEN_LOG: + WLOG(NOTICE, this) << "Reopening log files: worker(" << this << ")"; + + reopen_log_files(); + + break; + case GRACEFUL_SHUTDOWN: + WLOG(NOTICE, this) << "Graceful shutdown commencing"; + + graceful_shutdown_ = true; + + if (worker_stat_.num_connections == 0) { + ev_break(loop_); + + return; + } + + break; + default: + if (LOG_ENABLED(INFO)) { + WLOG(INFO, this) << "unknown event type " << wev.type; + } + } + } +} + +ssl::CertLookupTree *Worker::get_cert_lookup_tree() const { return cert_tree_; } + +const std::shared_ptr &Worker::get_ticket_keys() const { + return ticket_keys_; +} + +void Worker::set_ticket_keys(std::shared_ptr ticket_keys) { + ticket_keys_ = std::move(ticket_keys); +} + +WorkerStat *Worker::get_worker_stat() { return &worker_stat_; } + +DownstreamConnectionPool *Worker::get_dconn_pool() { return &dconn_pool_; } + +Http2Session *Worker::next_http2_session() { + if (http2sessions_.empty()) { + return nullptr; + } + + auto res = http2sessions_[next_http2session_].get(); + ++next_http2session_; + if (next_http2session_ >= http2sessions_.size()) { + next_http2session_ = 0; + } + + return res; +} + +ConnectBlocker *Worker::get_connect_blocker() const { + return connect_blocker_.get(); +} + +struct ev_loop *Worker::get_loop() const { + return loop_; +} + +SSL_CTX *Worker::get_sv_ssl_ctx() const { return sv_ssl_ctx_; } + +SSL_CTX *Worker::get_cl_ssl_ctx() const { return cl_ssl_ctx_; } + +void Worker::set_graceful_shutdown(bool f) { graceful_shutdown_ = f; } + +bool Worker::get_graceful_shutdown() const { return graceful_shutdown_; } + +MemchunkPool *Worker::get_mcpool() { return &mcpool_; } + +} // namespace shrpx diff --git a/src/shrpx_worker.h b/src/shrpx_worker.h new file mode 100644 index 0000000..4721371 --- /dev/null +++ b/src/shrpx_worker.h @@ -0,0 +1,142 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef SHRPX_WORKER_H +#define SHRPX_WORKER_H + +#include "shrpx.h" + +#include +#include +#include +#include +#ifndef NOTHREADS +#include +#endif // NOTHREADS + +#include +#include + +#include + +#include "shrpx_config.h" +#include "shrpx_downstream_connection_pool.h" +#include "memchunk.h" + +using namespace nghttp2; + +namespace shrpx { + +class Http2Session; +class ConnectBlocker; + +namespace ssl { +class CertLookupTree; +} // namespace ssl + +struct WorkerStat { + WorkerStat() : num_connections(0), next_downstream(0) {} + + size_t num_connections; + // Next downstream index in Config::downstream_addrs. For HTTP/2 + // downstream connections, this is always 0. For HTTP/1, this is + // used as load balancing. + size_t next_downstream; +}; + +enum WorkerEventType { + NEW_CONNECTION = 0x01, + REOPEN_LOG = 0x02, + GRACEFUL_SHUTDOWN = 0x03, + RENEW_TICKET_KEYS = 0x04, +}; + +struct WorkerEvent { + WorkerEventType type; + struct { + sockaddr_union client_addr; + size_t client_addrlen; + int client_fd; + }; + std::shared_ptr ticket_keys; +}; + +class Worker { +public: + Worker(struct ev_loop *loop, SSL_CTX *sv_ssl_ctx, SSL_CTX *cl_ssl_ctx, + ssl::CertLookupTree *cert_tree, + const std::shared_ptr &ticket_keys); + ~Worker(); + void run_async(); + void wait(); + void process_events(); + void send(const WorkerEvent &event); + + ssl::CertLookupTree *get_cert_lookup_tree() const; + const std::shared_ptr &get_ticket_keys() const; + void set_ticket_keys(std::shared_ptr ticket_keys); + WorkerStat *get_worker_stat(); + DownstreamConnectionPool *get_dconn_pool(); + Http2Session *next_http2_session(); + ConnectBlocker *get_connect_blocker() const; + struct ev_loop *get_loop() const; + SSL_CTX *get_sv_ssl_ctx() const; + SSL_CTX *get_cl_ssl_ctx() const; + + void set_graceful_shutdown(bool f); + bool get_graceful_shutdown() const; + + MemchunkPool *get_mcpool(); + void schedule_clear_mcpool(); + +private: + std::vector> http2sessions_; + size_t next_http2session_; +#ifndef NOTHREADS + std::future fut_; +#endif // NOTHREADS + std::mutex m_; + std::deque q_; + ev_async w_; + ev_timer mcpool_clear_timer_; + MemchunkPool mcpool_; + DownstreamConnectionPool dconn_pool_; + WorkerStat worker_stat_; + struct ev_loop *loop_; + + // Following fields are shared across threads if + // get_config()->tls_ctx_per_worker == true. + SSL_CTX *sv_ssl_ctx_; + SSL_CTX *cl_ssl_ctx_; + ssl::CertLookupTree *cert_tree_; + + std::shared_ptr ticket_keys_; + std::unique_ptr connect_blocker_; + + bool graceful_shutdown_; +}; + +} // namespace shrpx + +#endif // SHRPX_WORKER_H diff --git a/src/ssl.cc b/src/ssl.cc new file mode 100644 index 0000000..8696801 --- /dev/null +++ b/src/ssl.cc @@ -0,0 +1,83 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2014 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "ssl.h" + +#include +#include +#include +#include + +#include + +namespace nghttp2 { + +namespace ssl { + +// Recommended general purpose "Non-Backward Compatible" cipher by +// mozilla. +// +// https://wiki.mozilla.org/Security/Server_Side_TLS +const char *const DEFAULT_CIPHER_LIST = + "ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-" + "AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:" + "DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-" + "AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-" + "AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-" + "AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:" + "DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:!aNULL:!eNULL:" + "!EXPORT:!DES:!RC4:!3DES:!MD5:!PSK"; + +namespace { +std::vector ssl_global_locks; +} // namespace + +namespace { +void ssl_locking_cb(int mode, int type, const char *file, int line) { + if (mode & CRYPTO_LOCK) { + ssl_global_locks[type].lock(); + } else { + ssl_global_locks[type].unlock(); + } +} +} // namespace + +LibsslGlobalLock::LibsslGlobalLock() { + if (!ssl_global_locks.empty()) { + std::cerr << "OpenSSL global lock has been already set" << std::endl; + assert(0); + } + ssl_global_locks = std::vector(CRYPTO_num_locks()); + // CRYPTO_set_id_callback(ssl_thread_id); OpenSSL manual says that + // if threadid_func is not specified using + // CRYPTO_THREADID_set_callback(), then default implementation is + // used. We use this default one. + CRYPTO_set_locking_callback(ssl_locking_cb); +} + +LibsslGlobalLock::~LibsslGlobalLock() { ssl_global_locks.clear(); } + +} // namespace ssl + +} // namespace nghttp2 diff --git a/src/ssl.h b/src/ssl.h new file mode 100644 index 0000000..97f3933 --- /dev/null +++ b/src/ssl.h @@ -0,0 +1,45 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "nghttp2_config.h" + +namespace nghttp2 { + +namespace ssl { + +// Acquire OpenSSL global lock to share SSL_CTX across multiple +// threads. The constructor acquires lock and destructor unlocks. +class LibsslGlobalLock { +public: + LibsslGlobalLock(); + ~LibsslGlobalLock(); + LibsslGlobalLock(const LibsslGlobalLock &) = delete; + LibsslGlobalLock &operator=(const LibsslGlobalLock &) = delete; +}; + +extern const char *const DEFAULT_CIPHER_LIST; + +} // namespace ssl + +} // namespace nghttp2 diff --git a/src/template.h b/src/template.h new file mode 100644 index 0000000..144aed1 --- /dev/null +++ b/src/template.h @@ -0,0 +1,150 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2015 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef TEMPLATE_H +#define TEMPLATE_H + +#include "nghttp2_config.h" + +#include +#include +#include + +namespace nghttp2 { + +template +typename std::enable_if::value, std::unique_ptr>::type +make_unique(U &&... u) { + return std::unique_ptr(new T(std::forward(u)...)); +} + +template +typename std::enable_if::value, std::unique_ptr>::type +make_unique(size_t size) { + return std::unique_ptr(new typename std::remove_extent::type[size]()); +} + +template +std::array make_array(T &&t, Rest &&... rest) { + return std::array{ + {std::forward(t), std::forward(rest)...}}; +} + +template constexpr size_t array_size(T (&)[N]) { + return N; +} + +template constexpr size_t str_size(T (&)[N]) { + return N - 1; +} + +// inspired by , but our +// template can take functions returning other than void. +template struct Defer { + Defer(F &&f, T &&... t) + : f(std::bind(std::forward(f), std::forward(t)...)) {} + Defer(Defer &&o) : f(std::move(o.f)) {} + ~Defer() { f(); } + + using ResultType = typename std::result_of< + typename std::decay::type(typename std::decay::type...)>::type; + std::function f; +}; + +template Defer defer(F &&f, T &&... t) { + return Defer(std::forward(f), std::forward(t)...); +} + +template bool test_flags(T t, F flags) { + return (t & flags) == flags; +} + +// doubly linked list of element T*. T must have field T *dlprev and +// T *dlnext, which point to previous element and next element in the +// list respectively. +template struct DList { + DList() : head(nullptr), tail(nullptr) {} + + DList(const DList &) = delete; + + DList &operator=(const DList &) = delete; + + DList(DList &&other) : head(other.head), tail(other.tail) { + other.head = other.tail = nullptr; + } + + DList &operator=(DList &&other) { + if (this == &other) { + return *this; + } + head = other.head; + tail = other.tail; + other.head = other.tail = nullptr; + return *this; + } + + void append(T *t) { + if (tail) { + tail->dlnext = t; + t->dlprev = tail; + tail = t; + return; + } + head = tail = t; + } + + void remove(T *t) { + auto p = t->dlprev; + auto n = t->dlnext; + if (p) { + p->dlnext = n; + } + if (head == t) { + head = n; + } + if (n) { + n->dlprev = p; + } + if (tail == t) { + tail = p; + } + t->dlprev = t->dlnext = nullptr; + } + + bool empty() const { return head == nullptr; } + + T *head, *tail; +}; + +template void dlist_delete_all(DList &dl) { + for (auto e = dl.head; e;) { + auto next = e->dlnext; + delete e; + e = next; + } +} + +} // namespace nghttp2 + +#endif // TEMPLATE_H diff --git a/src/timegm.c b/src/timegm.c new file mode 100644 index 0000000..90bf53f --- /dev/null +++ b/src/timegm.c @@ -0,0 +1,70 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2013 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "timegm.h" + +#ifndef HAVE_TIMEGM + +#include + +/* Counter the number of leap year in the range [0, y). The |y| is the + year, including century (e.g., 2012) */ +static int count_leap_year(int y) { + y -= 1; + return y / 4 - y / 100 + y / 400; +} + +/* Returns nonzero if the |y| is the leap year. The |y| is the year, + including century (e.g., 2012) */ +static int is_leap_year(int y) { + return y % 4 == 0 && (y % 100 != 0 || y % 400 == 0); +} + +/* The number of days before ith month begins */ +static int daysum[] = {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334}; + +/* Based on the algorithm of Python 2.7 calendar.timegm. */ +time_t timegm(struct tm *tm) { + int days; + int num_leap_year; + int64_t t; + if (tm->tm_mon > 11) { + return -1; + } + num_leap_year = count_leap_year(tm->tm_year + 1900) - count_leap_year(1970); + days = (tm->tm_year - 70) * 365 + num_leap_year + daysum[tm->tm_mon] + + tm->tm_mday - 1; + if (tm->tm_mon >= 2 && is_leap_year(tm->tm_year + 1900)) { + ++days; + } + t = ((int64_t)days * 24 + tm->tm_hour) * 3600 + tm->tm_min * 60 + tm->tm_sec; + if (sizeof(time_t) == 4) { + if (t < INT32_MIN || t > INT32_MAX) { + return -1; + } + } + return (time_t)t; +} + +#endif /* !HAVE_TIMEGM */ diff --git a/src/timegm.h b/src/timegm.h new file mode 100644 index 0000000..32a9526 --- /dev/null +++ b/src/timegm.h @@ -0,0 +1,50 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2013 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef TIMEGM_H +#define TIMEGM_H + +#ifdef HAVE_CONFIG_H +#include +#endif /* HAVE_CONFIG_H */ + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#ifdef HAVE_TIME_H +#include +#endif // HAVE_TIME_H + +#ifndef HAVE_TIMEGM + +time_t timegm(struct tm *tm); + +#endif /* HAVE_TIMEGM */ + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* TIMEGM_H */ diff --git a/src/util.cc b/src/util.cc new file mode 100644 index 0000000..9eb5258 --- /dev/null +++ b/src/util.cc @@ -0,0 +1,1161 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "util.h" + +#ifdef HAVE_TIME_H +#include +#endif // HAVE_TIME_H +#include +#ifdef HAVE_SYS_SOCKET_H +#include +#endif // HAVE_SYS_SOCKET_H +#ifdef HAVE_NETDB_H +#include +#endif // HAVE_NETDB_H +#include +#ifdef HAVE_FCNTL_H +#include +#endif // HAVE_FCNTL_H +#ifdef HAVE_NETINET_IN_H +#include +#endif // HAVE_NETINET_IN_H +#include +#ifdef HAVE_ARPA_INET_H +#include +#endif // HAVE_ARPA_INET_H + +#include +#include +#include +#include +#include +#include + +#include + +#include "timegm.h" +#include "template.h" + +namespace nghttp2 { + +namespace util { + +const char DEFAULT_STRIP_CHARSET[] = "\r\n\t "; + +const char UPPER_XDIGITS[] = "0123456789ABCDEF"; + +bool isAlpha(const char c) { + return ('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z'); +} + +bool isDigit(const char c) { return '0' <= c && c <= '9'; } + +bool isHexDigit(const char c) { + return isDigit(c) || ('A' <= c && c <= 'F') || ('a' <= c && c <= 'f'); +} + +bool inRFC3986UnreservedChars(const char c) { + static const char unreserved[] = {'-', '.', '_', '~'}; + return isAlpha(c) || isDigit(c) || + std::find(&unreserved[0], &unreserved[4], c) != &unreserved[4]; +} + +bool in_rfc3986_sub_delims(const char c) { + static const char sub_delims[] = {'!', '$', '&', '\'', '(', ')', + '*', '+', ',', ';', '='}; + return std::find(std::begin(sub_delims), std::end(sub_delims), c) != + std::end(sub_delims); +} + +std::string percentEncode(const unsigned char *target, size_t len) { + std::string dest; + for (size_t i = 0; i < len; ++i) { + unsigned char c = target[i]; + + if (inRFC3986UnreservedChars(c)) { + dest += c; + } else { + dest += "%"; + dest += UPPER_XDIGITS[c >> 4]; + dest += UPPER_XDIGITS[(c & 0x0f)]; + } + } + return dest; +} + +std::string percentEncode(const std::string &target) { + return percentEncode(reinterpret_cast(target.c_str()), + target.size()); +} + +std::string percent_encode_path(const std::string &s) { + std::string dest; + for (auto c : s) { + if (inRFC3986UnreservedChars(c) || in_rfc3986_sub_delims(c) || c == '/') { + dest += c; + continue; + } + + dest += "%"; + dest += UPPER_XDIGITS[(c >> 4) & 0x0f]; + dest += UPPER_XDIGITS[(c & 0x0f)]; + } + return dest; +} + +bool in_token(char c) { + static const char extra[] = {'!', '#', '$', '%', '&', '\'', '*', '+', + '-', '.', '^', '_', '`', '|', '~'}; + + return isAlpha(c) || isDigit(c) || + std::find(&extra[0], &extra[sizeof(extra)], c) != + &extra[sizeof(extra)]; +} + +bool in_attr_char(char c) { + static const char bad[] = {'*', '\'', '%'}; + return util::in_token(c) && + std::find(std::begin(bad), std::end(bad) - 1, c) == std::end(bad) - 1; +} + +std::string percent_encode_token(const std::string &target) { + auto len = target.size(); + std::string dest; + + for (size_t i = 0; i < len; ++i) { + unsigned char c = target[i]; + + if (c != '%' && in_token(c)) { + dest += c; + } else { + dest += "%"; + dest += UPPER_XDIGITS[c >> 4]; + dest += UPPER_XDIGITS[(c & 0x0f)]; + } + } + return dest; +} + +uint32_t hex_to_uint(char c) { + if (c <= '9') { + return c - '0'; + } + if (c <= 'Z') { + return c - 'A' + 10; + } + if (c <= 'z') { + return c - 'a' + 10; + } + return c; +} + +std::string quote_string(const std::string &target) { + auto cnt = std::count(std::begin(target), std::end(target), '"'); + + if (cnt == 0) { + return target; + } + + std::string res; + res.reserve(target.size() + cnt); + + for (auto c : target) { + if (c == '"') { + res += "\\\""; + } else { + res += c; + } + } + + return res; +} + +namespace { +template +Iterator cpydig(Iterator d, uint32_t n, size_t len) { + auto p = d + len - 1; + + do { + *p-- = (n % 10) + '0'; + n /= 10; + } while (p >= d); + + return d + len; +} +} // namespace + +namespace { +const char *MONTH[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; +const char *DAY_OF_WEEK[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}; +} // namespace + +std::string http_date(time_t t) { + struct tm tms; + std::string res; + + if (gmtime_r(&t, &tms) == nullptr) { + return res; + } + + /* Sat, 27 Sep 2014 06:31:15 GMT */ + res.resize(29); + + auto p = std::begin(res); + + auto s = DAY_OF_WEEK[tms.tm_wday]; + p = std::copy_n(s, 3, p); + *p++ = ','; + *p++ = ' '; + p = cpydig(p, tms.tm_mday, 2); + *p++ = ' '; + s = MONTH[tms.tm_mon]; + p = std::copy_n(s, 3, p); + *p++ = ' '; + p = cpydig(p, tms.tm_year + 1900, 4); + *p++ = ' '; + p = cpydig(p, tms.tm_hour, 2); + *p++ = ':'; + p = cpydig(p, tms.tm_min, 2); + *p++ = ':'; + p = cpydig(p, tms.tm_sec, 2); + s = " GMT"; + p = std::copy_n(s, 4, p); + + return res; +} + +std::string common_log_date(time_t t) { + struct tm tms; + + if (localtime_r(&t, &tms) == nullptr) { + return ""; + } + +#ifdef HAVE_STRUCT_TM_TM_GMTOFF + // Format data like this: + // 03/Jul/2014:00:19:38 +0900 + std::string res; + res.resize(26); + + auto p = std::begin(res); + + p = cpydig(p, tms.tm_mday, 2); + *p++ = '/'; + auto s = MONTH[tms.tm_mon]; + p = std::copy_n(s, 3, p); + *p++ = '/'; + p = cpydig(p, tms.tm_year + 1900, 4); + *p++ = ':'; + p = cpydig(p, tms.tm_hour, 2); + *p++ = ':'; + p = cpydig(p, tms.tm_min, 2); + *p++ = ':'; + p = cpydig(p, tms.tm_sec, 2); + *p++ = ' '; + + auto gmtoff = tms.tm_gmtoff; + if (gmtoff >= 0) { + *p++ = '+'; + } else { + *p++ = '-'; + gmtoff = -gmtoff; + } + + p = cpydig(p, gmtoff / 3600, 2); + p = cpydig(p, (gmtoff % 3600) / 60, 2); + + return res; +#else // !HAVE_STRUCT_TM_TM_GMTOFF + char buf[32]; + + strftime(buf, sizeof(buf), "%d/%b/%Y:%T %z", &tms); + + return buf; +#endif // !HAVE_STRUCT_TM_TM_GMTOFF +} + +std::string iso8601_date(int64_t ms) { + time_t sec = ms / 1000; + + tm tms; + if (localtime_r(&sec, &tms) == nullptr) { + return ""; + } + +#ifdef HAVE_STRUCT_TM_TM_GMTOFF + // Format data like this: + // 2014-11-15T12:58:24.741Z + // 2014-11-15T12:58:24.741+09:00 + std::string res; + res.resize(29); + + auto p = std::begin(res); + + p = cpydig(p, tms.tm_year + 1900, 4); + *p++ = '-'; + p = cpydig(p, tms.tm_mon + 1, 2); + *p++ = '-'; + p = cpydig(p, tms.tm_mday, 2); + *p++ = 'T'; + p = cpydig(p, tms.tm_hour, 2); + *p++ = ':'; + p = cpydig(p, tms.tm_min, 2); + *p++ = ':'; + p = cpydig(p, tms.tm_sec, 2); + *p++ = '.'; + p = cpydig(p, ms % 1000, 3); + + auto gmtoff = tms.tm_gmtoff; + if (gmtoff == 0) { + *p++ = 'Z'; + } else { + if (gmtoff > 0) { + *p++ = '+'; + } else { + *p++ = '-'; + gmtoff = -gmtoff; + } + p = cpydig(p, gmtoff / 3600, 2); + *p++ = ':'; + p = cpydig(p, (gmtoff % 3600) / 60, 2); + } + + res.resize(p - std::begin(res)); + + return res; +#else // !HAVE_STRUCT_TM_TM_GMTOFF + char buf[128]; + + auto nwrite = strftime(buf, sizeof(buf), "%Y-%m-%dT%H:%M:%S", &tms); + nwrite += snprintf(&buf[nwrite], sizeof(buf) - nwrite, ".%03d", + static_cast(ms % 1000)); + auto nzone = strftime(&buf[nwrite], sizeof(buf) - nwrite, "%z", &tms); + + // %z of strftime writes +hhmm or -hhmm not Z, +hh:mm or -hh:mm. Do + // %nothing if nzone is not 5. we don't know how to cope with this. + if (nzone == 5) { + if (memcmp(&buf[nwrite], "+0000", 5) == 0) { + // 0000 should be Z + memcpy(&buf[nwrite], "Z", 2); + } else { + // Move mm part to right by 1 including terminal \0 + memmove(&buf[nwrite + 4], &buf[nwrite + 3], 3); + // Insert ':' between hh and mm + buf[nwrite + 3] = ':'; + } + } + return buf; +#endif // !HAVE_STRUCT_TM_TM_GMTOFF +} + +time_t parse_http_date(const std::string &s) { + tm tm; + memset(&tm, 0, sizeof(tm)); + char *r = strptime(s.c_str(), "%a, %d %b %Y %H:%M:%S GMT", &tm); + if (r == 0) { + return 0; + } + return timegm(&tm); +} + +namespace { +void streq_advance(const char **ap, const char **bp) { + for (; **ap && **bp && lowcase(**ap) == lowcase(**bp); ++*ap, ++*bp) + ; +} +} // namespace + +bool istartsWith(const char *a, const char *b) { + if (!a || !b) { + return false; + } + streq_advance(&a, &b); + return !*b; +} + +bool strieq(const char *a, const char *b) { + if (!a || !b) { + return false; + } + for (; *a && *b && lowcase(*a) == lowcase(*b); ++a, ++b) + ; + return !*a && !*b; +} + +int strcompare(const char *a, const uint8_t *b, size_t bn) { + assert(a && b); + const uint8_t *blast = b + bn; + for (; *a && b != blast; ++a, ++b) { + if (*a < *b) { + return -1; + } else if (*a > *b) { + return 1; + } + } + if (!*a && b == blast) { + return 0; + } else if (b == blast) { + return 1; + } else { + return -1; + } +} + +bool strifind(const char *a, const char *b) { + if (!a || !b) { + return false; + } + for (size_t i = 0; a[i]; ++i) { + const char *ap = &a[i], *bp = b; + for (; *ap && *bp && lowcase(*ap) == lowcase(*bp); ++ap, ++bp) + ; + if (!*bp) { + return true; + } + } + return false; +} + +char upcase(char c) { + if ('a' <= c && c <= 'z') { + return c - 'a' + 'A'; + } else { + return c; + } +} + +namespace { +const char LOWER_XDIGITS[] = "0123456789abcdef"; +} // namespace + +std::string format_hex(const unsigned char *s, size_t len) { + std::string res; + res.resize(len * 2); + + for (size_t i = 0; i < len; ++i) { + unsigned char c = s[i]; + + res[i * 2] = LOWER_XDIGITS[c >> 4]; + res[i * 2 + 1] = LOWER_XDIGITS[c & 0x0f]; + } + return res; +} + +void to_token68(std::string &base64str) { + std::transform(std::begin(base64str), std::end(base64str), + std::begin(base64str), [](char c) { + switch (c) { + case '+': + return '-'; + case '/': + return '_'; + default: + return c; + } + }); + base64str.erase(std::find(std::begin(base64str), std::end(base64str), '='), + std::end(base64str)); +} + +void to_base64(std::string &token68str) { + std::transform(std::begin(token68str), std::end(token68str), + std::begin(token68str), [](char c) { + switch (c) { + case '-': + return '+'; + case '_': + return '/'; + default: + return c; + } + }); + if (token68str.size() & 0x3) { + token68str.append(4 - (token68str.size() & 0x3), '='); + } + return; +} + +namespace { +// Calculates Damerau–Levenshtein distance between c-string a and b +// with given costs. swapcost, subcost, addcost and delcost are cost +// to swap 2 adjacent characters, substitute characters, add character +// and delete character respectively. +int levenshtein(const char *a, int alen, const char *b, int blen, int swapcost, + int subcost, int addcost, int delcost) { + auto dp = std::vector>(3, std::vector(blen + 1)); + for (int i = 0; i <= blen; ++i) { + dp[1][i] = i; + } + for (int i = 1; i <= alen; ++i) { + dp[0][0] = i; + for (int j = 1; j <= blen; ++j) { + dp[0][j] = dp[1][j - 1] + (a[i - 1] == b[j - 1] ? 0 : subcost); + if (i >= 2 && j >= 2 && a[i - 1] != b[j - 1] && a[i - 2] == b[j - 1] && + a[i - 1] == b[j - 2]) { + dp[0][j] = std::min(dp[0][j], dp[2][j - 2] + swapcost); + } + dp[0][j] = std::min(dp[0][j], + std::min(dp[1][j] + delcost, dp[0][j - 1] + addcost)); + } + std::rotate(std::begin(dp), std::begin(dp) + 2, std::end(dp)); + } + return dp[1][blen]; +} +} // namespace + +void show_candidates(const char *unkopt, option *options) { + for (; *unkopt == '-'; ++unkopt) + ; + if (*unkopt == '\0') { + return; + } + auto unkoptend = unkopt; + for (; *unkoptend && *unkoptend != '='; ++unkoptend) + ; + auto unkoptlen = unkoptend - unkopt; + if (unkoptlen == 0) { + return; + } + int prefix_match = 0; + auto cands = std::vector>(); + for (size_t i = 0; options[i].name != nullptr; ++i) { + auto optnamelen = strlen(options[i].name); + // Use cost 0 for prefix match + if (istartsWith(options[i].name, options[i].name + optnamelen, unkopt, + unkopt + unkoptlen)) { + if (optnamelen == static_cast(unkoptlen)) { + // Exact match, then we don't show any condidates. + return; + } + ++prefix_match; + cands.emplace_back(0, options[i].name); + continue; + } + // Use cost 0 for suffix match, but match at least 3 characters + if (unkoptlen >= 3 && + iendsWith(options[i].name, options[i].name + optnamelen, unkopt, + unkopt + unkoptlen)) { + cands.emplace_back(0, options[i].name); + continue; + } + // cost values are borrowed from git, help.c. + int sim = + levenshtein(unkopt, unkoptlen, options[i].name, optnamelen, 0, 2, 1, 3); + cands.emplace_back(sim, options[i].name); + } + if (prefix_match == 1 || cands.empty()) { + return; + } + std::sort(std::begin(cands), std::end(cands)); + int threshold = cands[0].first; + // threshold value is a magic value. + if (threshold > 6) { + return; + } + std::cerr << "\nDid you mean:\n"; + for (auto &item : cands) { + if (item.first > threshold) { + break; + } + std::cerr << "\t--" << item.second << "\n"; + } +} + +bool has_uri_field(const http_parser_url &u, http_parser_url_fields field) { + return u.field_set & (1 << field); +} + +bool fieldeq(const char *uri1, const http_parser_url &u1, const char *uri2, + const http_parser_url &u2, http_parser_url_fields field) { + if (!has_uri_field(u1, field)) { + if (!has_uri_field(u2, field)) { + return true; + } else { + return false; + } + } else if (!has_uri_field(u2, field)) { + return false; + } + if (u1.field_data[field].len != u2.field_data[field].len) { + return false; + } + return memcmp(uri1 + u1.field_data[field].off, + uri2 + u2.field_data[field].off, u1.field_data[field].len) == 0; +} + +bool fieldeq(const char *uri, const http_parser_url &u, + http_parser_url_fields field, const char *t) { + if (!has_uri_field(u, field)) { + if (!t[0]) { + return true; + } else { + return false; + } + } else if (!t[0]) { + return false; + } + int i, len = u.field_data[field].len; + const char *p = uri + u.field_data[field].off; + for (i = 0; i < len && t[i] && p[i] == t[i]; ++i) + ; + return i == len && !t[i]; +} + +std::string get_uri_field(const char *uri, const http_parser_url &u, + http_parser_url_fields field) { + if (util::has_uri_field(u, field)) { + return std::string(uri + u.field_data[field].off, u.field_data[field].len); + } else { + return ""; + } +} + +uint16_t get_default_port(const char *uri, const http_parser_url &u) { + if (util::fieldeq(uri, u, UF_SCHEMA, "https")) { + return 443; + } else if (util::fieldeq(uri, u, UF_SCHEMA, "http")) { + return 80; + } else { + return 443; + } +} + +bool porteq(const char *uri1, const http_parser_url &u1, const char *uri2, + const http_parser_url &u2) { + uint16_t port1, port2; + port1 = + util::has_uri_field(u1, UF_PORT) ? u1.port : get_default_port(uri1, u1); + port2 = + util::has_uri_field(u2, UF_PORT) ? u2.port : get_default_port(uri2, u2); + return port1 == port2; +} + +void write_uri_field(std::ostream &o, const char *uri, const http_parser_url &u, + http_parser_url_fields field) { + if (util::has_uri_field(u, field)) { + o.write(uri + u.field_data[field].off, u.field_data[field].len); + } +} + +bool numeric_host(const char *hostname) { + struct addrinfo hints; + struct addrinfo *res; + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; + hints.ai_flags = AI_NUMERICHOST; + if (getaddrinfo(hostname, nullptr, &hints, &res)) { + return false; + } + freeaddrinfo(res); + return true; +} + +std::string numeric_name(const struct sockaddr *sa, socklen_t salen) { + std::array host; + auto rv = getnameinfo(sa, salen, host.data(), host.size(), nullptr, 0, + NI_NUMERICHOST); + if (rv != 0) { + return "unknown"; + } + return host.data(); +} + +int reopen_log_file(const char *path) { +#if defined(__ANDROID__) || defined(ANDROID) + int fd; + + if (strcmp("/proc/self/fd/1", path) == 0 || + strcmp("/proc/self/fd/2", path) == 0) { + + // We will get permission denied error when O_APPEND is used for + // these paths. + fd = + open(path, O_WRONLY | O_CREAT | O_CLOEXEC, S_IRUSR | S_IWUSR | S_IRGRP); + } else { + fd = open(path, O_WRONLY | O_APPEND | O_CREAT | O_CLOEXEC, + S_IRUSR | S_IWUSR | S_IRGRP); + } +#elif defined O_CLOEXEC + + auto fd = open(path, O_WRONLY | O_APPEND | O_CREAT | O_CLOEXEC, + S_IRUSR | S_IWUSR | S_IRGRP); +#else // !O_CLOEXEC + + auto fd = + open(path, O_WRONLY | O_APPEND | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP); + + // We get race condition if execve is called at the same time. + if (fd != -1) { + make_socket_closeonexec(fd); + } + +#endif // !O_CLOEXEC + + if (fd == -1) { + return -1; + } + + return fd; +} + +std::string ascii_dump(const uint8_t *data, size_t len) { + std::string res; + + for (size_t i = 0; i < len; ++i) { + auto c = data[i]; + + if (c >= 0x20 && c < 0x7f) { + res += c; + } else { + res += "."; + } + } + + return res; +} + +char *get_exec_path(int argc, char **const argv, const char *cwd) { + if (argc == 0 || cwd == nullptr) { + return nullptr; + } + + auto argv0 = argv[0]; + auto len = strlen(argv0); + + char *path; + + if (argv0[0] == '/') { + path = static_cast(malloc(len + 1)); + if (path == nullptr) { + return nullptr; + } + memcpy(path, argv0, len + 1); + } else { + auto cwdlen = strlen(cwd); + path = static_cast(malloc(len + 1 + cwdlen + 1)); + if (path == nullptr) { + return nullptr; + } + memcpy(path, cwd, cwdlen); + path[cwdlen] = '/'; + memcpy(path + cwdlen + 1, argv0, len + 1); + } + + return path; +} + +bool check_path(const std::string &path) { + // We don't like '\' in path. + return !path.empty() && path[0] == '/' && + path.find('\\') == std::string::npos && + path.find("/../") == std::string::npos && + path.find("/./") == std::string::npos && + !util::endsWith(path, "/..") && !util::endsWith(path, "/."); +} + +int64_t to_time64(const timeval &tv) { + return tv.tv_sec * 1000000 + tv.tv_usec; +} + +bool check_h2_is_selected(const unsigned char *proto, size_t len) { + return streq_l(NGHTTP2_PROTO_VERSION_ID, proto, len) || + streq_l(NGHTTP2_H2_16, proto, len) || + streq_l(NGHTTP2_H2_14, proto, len); +} + +namespace { +bool select_h2(const unsigned char **out, unsigned char *outlen, + const unsigned char *in, unsigned int inlen, const char *key, + unsigned int keylen) { + for (auto p = in, end = in + inlen; p + keylen <= end; p += *p + 1) { + if (std::equal(key, key + keylen, p)) { + *out = p + 1; + *outlen = *p; + return true; + } + } + return false; +} +} // namespace + +bool select_h2(const unsigned char **out, unsigned char *outlen, + const unsigned char *in, unsigned int inlen) { + return select_h2(out, outlen, in, inlen, NGHTTP2_PROTO_ALPN, + str_size(NGHTTP2_PROTO_ALPN)) || + select_h2(out, outlen, in, inlen, NGHTTP2_H2_16_ALPN, + str_size(NGHTTP2_H2_16_ALPN)) || + select_h2(out, outlen, in, inlen, NGHTTP2_H2_14_ALPN, + str_size(NGHTTP2_H2_14_ALPN)); +} + +std::vector get_default_alpn() { + auto res = std::vector(str_size(NGHTTP2_PROTO_ALPN) + + str_size(NGHTTP2_H2_16_ALPN) + + str_size(NGHTTP2_H2_14_ALPN)); + auto p = std::begin(res); + + p = std::copy_n(NGHTTP2_PROTO_ALPN, str_size(NGHTTP2_PROTO_ALPN), p); + p = std::copy_n(NGHTTP2_H2_16_ALPN, str_size(NGHTTP2_H2_16_ALPN), p); + p = std::copy_n(NGHTTP2_H2_14_ALPN, str_size(NGHTTP2_H2_14_ALPN), p); + + return res; +} + +int make_socket_closeonexec(int fd) { + int flags; + int rv; + while ((flags = fcntl(fd, F_GETFD)) == -1 && errno == EINTR) + ; + while ((rv = fcntl(fd, F_SETFD, flags | FD_CLOEXEC)) == -1 && errno == EINTR) + ; + return rv; +} + +int make_socket_nonblocking(int fd) { + int flags; + int rv; + while ((flags = fcntl(fd, F_GETFL, 0)) == -1 && errno == EINTR) + ; + while ((rv = fcntl(fd, F_SETFL, flags | O_NONBLOCK)) == -1 && errno == EINTR) + ; + return rv; +} + +int make_socket_nodelay(int fd) { + int val = 1; + if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, reinterpret_cast(&val), + sizeof(val)) == -1) { + return -1; + } + return 0; +} + +int create_nonblock_socket(int family) { +#ifdef SOCK_NONBLOCK + auto fd = socket(family, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0); + + if (fd == -1) { + return -1; + } +#else // !SOCK_NONBLOCK + auto fd = socket(family, SOCK_STREAM, 0); + + if (fd == -1) { + return -1; + } + + make_socket_nonblocking(fd); + make_socket_closeonexec(fd); +#endif // !SOCK_NONBLOCK + + if (family == AF_INET || family == AF_INET6) { + make_socket_nodelay(fd); + } + + return fd; +} + +bool check_socket_connected(int fd) { + int error; + socklen_t len = sizeof(error); + if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &error, &len) == 0) { + if (error != 0) { + return false; + } + } + return true; +} + +bool ipv6_numeric_addr(const char *host) { + uint8_t dst[16]; + return inet_pton(AF_INET6, host, dst) == 1; +} + +namespace { +std::pair parse_uint_digits(const void *ss, size_t len) { + const uint8_t *s = static_cast(ss); + int64_t n = 0; + size_t i; + if (len == 0) { + return {-1, 0}; + } + constexpr int64_t max = std::numeric_limits::max(); + for (i = 0; i < len; ++i) { + if ('0' <= s[i] && s[i] <= '9') { + if (n > max / 10) { + return {-1, 0}; + } + n *= 10; + if (n > max - (s[i] - '0')) { + return {-1, 0}; + } + n += s[i] - '0'; + continue; + } + break; + } + if (i == 0) { + return {-1, 0}; + } + return {n, i}; +} +} // namespace + +int64_t parse_uint_with_unit(const char *s) { + int64_t n; + size_t i; + auto len = strlen(s); + std::tie(n, i) = parse_uint_digits(s, len); + if (n == -1) { + return -1; + } + if (i == len) { + return n; + } + if (i + 1 != len) { + return -1; + } + int mul = 1; + switch (s[i]) { + case 'K': + case 'k': + mul = 1 << 10; + break; + case 'M': + case 'm': + mul = 1 << 20; + break; + case 'G': + case 'g': + mul = 1 << 30; + break; + default: + return -1; + } + constexpr int64_t max = std::numeric_limits::max(); + if (n > max / mul) { + return -1; + } + return n * mul; +} + +int64_t parse_uint(const char *s) { + return parse_uint(reinterpret_cast(s), strlen(s)); +} + +int64_t parse_uint(const std::string &s) { + return parse_uint(reinterpret_cast(s.c_str()), s.size()); +} + +int64_t parse_uint(const uint8_t *s, size_t len) { + int64_t n; + size_t i; + std::tie(n, i) = parse_uint_digits(s, len); + if (n == -1 || i != len) { + return -1; + } + return n; +} + +double parse_duration_with_unit(const char *s) { + constexpr auto max = std::numeric_limits::max(); + int64_t n; + size_t i; + auto len = strlen(s); + std::tie(n, i) = parse_uint_digits(s, len); + if (n == -1) { + goto fail; + } + if (i == len) { + return static_cast(n); + } + switch (s[i]) { + case 'S': + case 's': + // seconds + if (i + 1 != len) { + goto fail; + } + return static_cast(n); + case 'M': + case 'm': + if (i + 1 == len) { + // minutes + if (n > max / 60) { + goto fail; + } + return static_cast(n) * 60; + } + + if (i + 2 != len || (s[i + 1] != 's' && s[i + 1] != 'S')) { + goto fail; + } + // milliseconds + return static_cast(n) / 1000.; + case 'H': + case 'h': + // hours + if (i + 1 != len) { + goto fail; + } + if (n > max / 3600) { + goto fail; + } + return static_cast(n) * 3600; + } +fail: + return std::numeric_limits::infinity(); +} + +std::string duration_str(double t) { + if (t == 0.) { + return "0"; + } + auto frac = static_cast(t * 1000) % 1000; + if (frac > 0) { + return utos(static_cast(t * 1000)) + "ms"; + } + auto v = static_cast(t); + if (v % 60) { + return utos(v) + "s"; + } + v /= 60; + if (v % 60) { + return utos(v) + "m"; + } + v /= 60; + return utos(v) + "h"; +} + +std::string format_duration(const std::chrono::microseconds &u) { + const char *unit = "us"; + int d = 0; + auto t = u.count(); + if (t >= 1000000) { + d = 1000000; + unit = "s"; + } else if (t >= 1000) { + d = 1000; + unit = "ms"; + } else { + return utos(t) + unit; + } + return dtos(static_cast(t) / d) + unit; +} + +std::string dtos(double n) { + auto f = utos(static_cast(round(100. * n)) % 100); + return utos(static_cast(n)) + "." + (f.size() == 1 ? "0" : "") + f; +} + +std::string make_hostport(const char *host, uint16_t port) { + auto ipv6 = ipv6_numeric_addr(host); + std::string hostport; + + if (ipv6) { + hostport += "["; + } + + hostport += host; + + if (ipv6) { + hostport += "]"; + } + + if (port != 80 && port != 443) { + hostport += ":"; + hostport += utos(port); + } + + return hostport; +} + +namespace { +void hexdump8(FILE *out, const uint8_t *first, const uint8_t *last) { + auto stop = std::min(first + 8, last); + for (auto k = first; k != stop; ++k) { + fprintf(out, "%02x ", *k); + } + // each byte needs 3 spaces (2 hex value and space) + for (; stop != first + 8; ++stop) { + fputs(" ", out); + } + // we have extra space after 8 bytes + fputc(' ', out); +} +} // namespace + +void hexdump(FILE *out, const uint8_t *src, size_t len) { + if (len == 0) { + return; + } + size_t buflen = 0; + auto repeated = false; + std::array buf{}; + auto end = src + len; + auto i = src; + for (;;) { + auto nextlen = + std::min(static_cast(16), static_cast(end - i)); + if (nextlen == buflen && + std::equal(std::begin(buf), std::begin(buf) + buflen, i)) { + // as long as adjacent 16 bytes block are the same, we just + // print single '*'. + if (!repeated) { + repeated = true; + fputs("*\n", out); + } + i += nextlen; + continue; + } + repeated = false; + fprintf(out, "%08lx", static_cast(i - src)); + if (i == end) { + fputc('\n', out); + break; + } + fputs(" ", out); + hexdump8(out, i, end); + hexdump8(out, i + 8, std::max(i + 8, end)); + fputc('|', out); + auto stop = std::min(i + 16, end); + buflen = stop - i; + auto p = buf.data(); + for (; i != stop; ++i) { + *p++ = *i; + if (0x20 <= *i && *i <= 0x7e) { + fputc(*i, out); + } else { + fputc('.', out); + } + } + fputs("|\n", out); + } +} + +} // namespace util + +} // namespace nghttp2 diff --git a/src/util.h b/src/util.h new file mode 100644 index 0000000..88e1ff5 --- /dev/null +++ b/src/util.h @@ -0,0 +1,626 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef UTIL_H +#define UTIL_H + +#include "nghttp2_config.h" + +#ifdef HAVE_UNISTD_H +#include +#endif // HAVE_UNISTD_H +#include +#ifdef HAVE_NETDB_H +#include +#endif // HAVE_NETDB_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "http-parser/http_parser.h" + +namespace nghttp2 { + +// The additional HTTP/2 protocol ALPN protocol identifier we also +// supports for our applications to make smooth migration into final +// h2 ALPN ID. +#define NGHTTP2_H2_16_ALPN "\x5h2-16" +#define NGHTTP2_H2_16 "h2-16" + +#define NGHTTP2_H2_14_ALPN "\x5h2-14" +#define NGHTTP2_H2_14 "h2-14" + +namespace util { + +extern const char DEFAULT_STRIP_CHARSET[]; + +template +std::pair +stripIter(InputIterator first, InputIterator last, + const char *chars = DEFAULT_STRIP_CHARSET) { + for (; first != last && strchr(chars, *first) != 0; ++first) + ; + if (first == last) { + return std::make_pair(first, last); + } + InputIterator left = last - 1; + for (; left != first && strchr(chars, *left) != 0; --left) + ; + return std::make_pair(first, left + 1); +} + +template +OutputIterator splitIter(InputIterator first, InputIterator last, + OutputIterator out, char delim, bool doStrip = false, + bool allowEmpty = false) { + for (InputIterator i = first; i != last;) { + InputIterator j = std::find(i, last, delim); + std::pair p(i, j); + if (doStrip) { + p = stripIter(i, j); + } + if (allowEmpty || p.first != p.second) { + *out++ = p; + } + i = j; + if (j != last) { + ++i; + } + } + if (allowEmpty && (first == last || *(last - 1) == delim)) { + *out++ = std::make_pair(last, last); + } + return out; +} + +template +OutputIterator split(InputIterator first, InputIterator last, + OutputIterator out, char delim, bool doStrip = false, + bool allowEmpty = false) { + for (InputIterator i = first; i != last;) { + InputIterator j = std::find(i, last, delim); + std::pair p(i, j); + if (doStrip) { + p = stripIter(i, j); + } + if (allowEmpty || p.first != p.second) { + *out++ = std::string(p.first, p.second); + } + i = j; + if (j != last) { + ++i; + } + } + if (allowEmpty && (first == last || *(last - 1) == delim)) { + *out++ = std::string(last, last); + } + return out; +} + +template +std::string strjoin(InputIterator first, InputIterator last, + const DelimiterType &delim) { + std::string result; + if (first == last) { + return result; + } + InputIterator beforeLast = last - 1; + for (; first != beforeLast; ++first) { + result += *first; + result += delim; + } + result += *beforeLast; + return result; +} + +template +std::string joinPath(InputIterator first, InputIterator last) { + std::vector elements; + for (; first != last; ++first) { + if (*first == "..") { + if (!elements.empty()) { + elements.pop_back(); + } + } else if (*first == ".") { + // do nothing + } else { + elements.push_back(*first); + } + } + return strjoin(elements.begin(), elements.end(), "/"); +} + +bool isAlpha(const char c); + +bool isDigit(const char c); + +bool isHexDigit(const char c); + +bool inRFC3986UnreservedChars(const char c); + +bool in_rfc3986_sub_delims(const char c); + +// Returns true if |c| is in token (HTTP-p1, Section 3.2.6) +bool in_token(char c); + +bool in_attr_char(char c); + +// Returns integer corresponding to hex notation |c|. It is undefined +// if isHexDigit(c) is false. +uint32_t hex_to_uint(char c); + +std::string percentEncode(const unsigned char *target, size_t len); + +std::string percentEncode(const std::string &target); + +// percent-encode path component of URI |s|. +std::string percent_encode_path(const std::string &s); + +template +std::string percentDecode(InputIt first, InputIt last) { + std::string result; + for (; first != last; ++first) { + if (*first == '%') { + if (first + 1 != last && first + 2 != last && isHexDigit(*(first + 1)) && + isHexDigit(*(first + 2))) { + result += (hex_to_uint(*(first + 1)) << 4) + hex_to_uint(*(first + 2)); + first += 2; + continue; + } + result += *first; + continue; + } + result += *first; + } + return result; +} + +// Percent encode |target| if character is not in token or '%'. +std::string percent_encode_token(const std::string &target); + +// Returns quotedString version of |target|. Currently, this function +// just replace '"' with '\"'. +std::string quote_string(const std::string &target); + +std::string format_hex(const unsigned char *s, size_t len); + +std::string http_date(time_t t); + +// Returns given time |t| from epoch in Common Log format (e.g., +// 03/Jul/2014:00:19:38 +0900) +std::string common_log_date(time_t t); + +// Returns given millisecond |ms| from epoch in ISO 8601 format (e.g., +// 2014-11-15T12:58:24.741Z) +std::string iso8601_date(int64_t ms); + +time_t parse_http_date(const std::string &s); + +char upcase(char c); + +inline char lowcase(char c) { + static unsigned char tbl[] = { + 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, 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, 52, 53, 54, 55, 56, 57, 58, 59, + 60, 61, 62, 63, 64, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', + 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', + 'z', 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, + 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, + 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, + 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, + 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, + 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, + 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, + 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, + 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, + 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, + 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, + 255, + }; + return tbl[static_cast(c)]; +} + +template +bool startsWith(InputIterator1 first1, InputIterator1 last1, + InputIterator2 first2, InputIterator2 last2) { + if (last1 - first1 < last2 - first2) { + return false; + } + return std::equal(first2, last2, first1); +} + +inline bool startsWith(const std::string &a, const std::string &b) { + return startsWith(std::begin(a), std::end(a), std::begin(b), std::end(b)); +} + +inline bool startsWith(const char *a, const char *b) { + return startsWith(a, a + strlen(a), b, b + strlen(b)); +} + +struct CaseCmp { + bool operator()(char lhs, char rhs) const { + return lowcase(lhs) == lowcase(rhs); + } +}; + +template +bool istartsWith(InputIterator1 first1, InputIterator1 last1, + InputIterator2 first2, InputIterator2 last2) { + if (last1 - first1 < last2 - first2) { + return false; + } + return std::equal(first2, last2, first1, CaseCmp()); +} + +inline bool istartsWith(const std::string &a, const std::string &b) { + return istartsWith(std::begin(a), std::end(a), std::begin(b), std::end(b)); +} + +template +bool istartsWith(InputIt a, size_t an, const char *b) { + return istartsWith(a, a + an, b, b + strlen(b)); +} + +bool istartsWith(const char *a, const char *b); + +template +bool endsWith(InputIterator1 first1, InputIterator1 last1, + InputIterator2 first2, InputIterator2 last2) { + if (last1 - first1 < last2 - first2) { + return false; + } + return std::equal(first2, last2, last1 - (last2 - first2)); +} + +inline bool endsWith(const std::string &a, const std::string &b) { + return endsWith(std::begin(a), std::end(a), std::begin(b), std::end(b)); +} + +template +bool iendsWith(InputIterator1 first1, InputIterator1 last1, + InputIterator2 first2, InputIterator2 last2) { + if (last1 - first1 < last2 - first2) { + return false; + } + return std::equal(first2, last2, last1 - (last2 - first2), CaseCmp()); +} + +inline bool iendsWith(const std::string &a, const std::string &b) { + return iendsWith(std::begin(a), std::end(a), std::begin(b), std::end(b)); +} + +int strcompare(const char *a, const uint8_t *b, size_t n); + +template bool strieq(const char *a, InputIt b, size_t bn) { + if (!a) { + return false; + } + auto blast = b + bn; + for (; *a && b != blast && lowcase(*a) == lowcase(*b); ++a, ++b) + ; + return !*a && b == blast; +} + +template +bool strieq(InputIt1 a, size_t alen, InputIt2 b, size_t blen) { + if (alen != blen) { + return false; + } + return std::equal(a, a + alen, b, CaseCmp()); +} + +inline bool strieq(const std::string &a, const std::string &b) { + return strieq(std::begin(a), a.size(), std::begin(b), b.size()); +} + +bool strieq(const char *a, const char *b); + +template +bool strieq_l(const char (&a)[N], InputIt b, size_t blen) { + return strieq(a, N - 1, b, blen); +} + +template bool strieq_l(const char (&a)[N], const std::string &b) { + return strieq(a, N - 1, std::begin(b), b.size()); +} + +template bool streq(const char *a, InputIt b, size_t bn) { + if (!a) { + return false; + } + auto blast = b + bn; + for (; *a && b != blast && *a == *b; ++a, ++b) + ; + return !*a && b == blast; +} + +template +bool streq(InputIt1 a, size_t alen, InputIt2 b, size_t blen) { + if (alen != blen) { + return false; + } + return std::equal(a, a + alen, b); +} + +inline bool streq(const char *a, const char *b) { + if (!a || !b) { + return false; + } + return streq(a, strlen(a), b, strlen(b)); +} + +template +bool streq_l(const char (&a)[N], InputIt b, size_t blen) { + return streq(a, N - 1, b, blen); +} + +bool strifind(const char *a, const char *b); + +// Lowercase |s| in place. +inline void inp_strlower(std::string &s) { + std::transform(std::begin(s), std::end(s), std::begin(s), lowcase); +} + +// Returns string representation of |n| with 2 fractional digits. +std::string dtos(double n); + +template std::string utos(T n) { + std::string res; + if (n == 0) { + res = "0"; + return res; + } + int i = 0; + T t = n; + for (; t; t /= 10, ++i) + ; + res.resize(i); + --i; + for (; n; --i, n /= 10) { + res[i] = (n % 10) + '0'; + } + return res; +} + +template std::string utos_with_unit(T n) { + char u = 0; + if (n >= (1 << 30)) { + u = 'G'; + n /= (1 << 30); + } else if (n >= (1 << 20)) { + u = 'M'; + n /= (1 << 20); + } else if (n >= (1 << 10)) { + u = 'K'; + n /= (1 << 10); + } + if (u == 0) { + return utos(n); + } + return utos(n) + u; +} + +// Like utos_with_unit(), but 2 digits fraction part is followed. +template std::string utos_with_funit(T n) { + char u = 0; + int b = 0; + if (n >= (1 << 30)) { + u = 'G'; + b = 30; + } else if (n >= (1 << 20)) { + u = 'M'; + b = 20; + } else if (n >= (1 << 10)) { + u = 'K'; + b = 10; + } + if (b == 0) { + return utos(n); + } + return dtos(static_cast(n) / (1 << b)) + u; +} + +extern const char UPPER_XDIGITS[]; + +template std::string utox(T n) { + std::string res; + if (n == 0) { + res = "0"; + return res; + } + int i = 0; + T t = n; + for (; t; t /= 16, ++i) + ; + res.resize(i); + --i; + for (; n; --i, n /= 16) { + res[i] = UPPER_XDIGITS[(n & 0x0f)]; + } + return res; +} + +void to_token68(std::string &base64str); +void to_base64(std::string &token68str); + +void show_candidates(const char *unkopt, option *options); + +bool has_uri_field(const http_parser_url &u, http_parser_url_fields field); + +bool fieldeq(const char *uri1, const http_parser_url &u1, const char *uri2, + const http_parser_url &u2, http_parser_url_fields field); + +bool fieldeq(const char *uri, const http_parser_url &u, + http_parser_url_fields field, const char *t); + +std::string get_uri_field(const char *uri, const http_parser_url &u, + http_parser_url_fields field); + +uint16_t get_default_port(const char *uri, const http_parser_url &u); + +bool porteq(const char *uri1, const http_parser_url &u1, const char *uri2, + const http_parser_url &u2); + +void write_uri_field(std::ostream &o, const char *uri, const http_parser_url &u, + http_parser_url_fields field); + +bool numeric_host(const char *hostname); + +// Returns numeric address string of |addr|. If getnameinfo() is +// failed, "unknown" is returned. +std::string numeric_name(const struct sockaddr *sa, socklen_t salen); + +// Opens |path| with O_APPEND enabled. If file does not exist, it is +// created first. This function returns file descriptor referring the +// opened file if it succeeds, or -1. +int reopen_log_file(const char *path); + +// Returns ASCII dump of |data| of length |len|. Only ASCII printable +// characters are preserved. Other characters are replaced with ".". +std::string ascii_dump(const uint8_t *data, size_t len); + +// Returns absolute path of executable path. If argc == 0 or |cwd| is +// nullptr, this function returns nullptr. If argv[0] starts with +// '/', this function returns argv[0]. Oterwise return cwd + "/" + +// argv[0]. If non-null is returned, it is NULL-terminated string and +// dynamically allocated by malloc. The caller is responsible to free +// it. +char *get_exec_path(int argc, char **const argv, const char *cwd); + +// Validates path so that it does not contain directory traversal +// vector. Returns true if path is safe. The |path| must start with +// "/" otherwise returns false. This function should be called after +// percent-decode was performed. +bool check_path(const std::string &path); + +// Returns the |tv| value as 64 bit integer using a microsecond as an +// unit. +int64_t to_time64(const timeval &tv); + +// Returns true if ALPN ID |proto| of length |len| is supported HTTP/2 +// protocol identifier. +bool check_h2_is_selected(const unsigned char *alpn, size_t len); + +// Selects h2 protocol ALPN ID if one of supported h2 versions are +// present in |in| of length inlen. Returns true if h2 version is +// selected. +bool select_h2(const unsigned char **out, unsigned char *outlen, + const unsigned char *in, unsigned int inlen); + +// Returns default ALPN protocol list, which only contains supported +// HTTP/2 protocol identifier. +std::vector get_default_alpn(); + +// Returns given time |tp| in Common Log format (e.g., +// 03/Jul/2014:00:19:38 +0900) +// Expected type of |tp| is std::chrono::timepoint +template std::string format_common_log(const T &tp) { + auto t = + std::chrono::duration_cast(tp.time_since_epoch()); + return common_log_date(t.count()); +} + +// Returns given time |tp| in ISO 8601 format (e.g., +// 2014-11-15T12:58:24.741Z) +// Expected type of |tp| is std::chrono::timepoint +template std::string format_iso8601(const T &tp) { + auto t = std::chrono::duration_cast( + tp.time_since_epoch()); + return iso8601_date(t.count()); +} + +// Return the system precision of the template parameter |Clock| as +// a nanosecond value of type |Rep| +template Rep clock_precision() { + std::chrono::duration duration = typename Clock::duration(1); + + return duration.count(); +} + +int make_socket_closeonexec(int fd); +int make_socket_nonblocking(int fd); +int make_socket_nodelay(int fd); + +int create_nonblock_socket(int family); + +bool check_socket_connected(int fd); + +// Returns true if |host| is IPv6 numeric address (e.g., ::1) +bool ipv6_numeric_addr(const char *host); + +// Parses NULL terminated string |s| as unsigned integer and returns +// the parsed integer. Additionally, if |s| ends with 'k', 'm', 'g' +// and its upper case characters, multiply the integer by 1024, 1024 * +// 1024 and 1024 * 1024 respectively. If there is an error, returns +// -1. +int64_t parse_uint_with_unit(const char *s); + +// Parses NULL terminated string |s| as unsigned integer and returns +// the parsed integer. If there is an error, returns -1. +int64_t parse_uint(const char *s); +int64_t parse_uint(const uint8_t *s, size_t len); +int64_t parse_uint(const std::string &s); + +// Parses NULL terminated string |s| as unsigned integer and returns +// the parsed integer casted to double. If |s| ends with "s", the +// parsed value's unit is a second. If |s| ends with "ms", the unit +// is millisecond. If none of them are given, the unit is second. +// This function returns std::numeric_limits::infinity() if +// error occurs. +double parse_duration_with_unit(const char *s); + +// Returns string representation of time duration |t|. If t has +// fractional part (at least more than or equal to 1e-3), |t| is +// multiplied by 1000 and the unit "ms" is appended. Otherwise, |t| +// is left as is and "s" is appended. +std::string duration_str(double t); + +// Returns string representation of time duration |t|. It appends +// unit after the formatting. The available units are s, ms and us. +// The unit which is equal to or less than |t| is used and 2 +// fractional digits follow. +std::string format_duration(const std::chrono::microseconds &u); + +// Creates "host:port" string using given |host| and |port|. If +// |host| is numeric IPv6 address (e.g., ::1), it is enclosed by "[" +// and "]". If |port| is 80 or 443, port part is omitted. +std::string make_hostport(const char *host, uint16_t port); + +// Dumps |src| of length |len| in the format similar to `hexdump -C`. +void hexdump(FILE *out, const uint8_t *src, size_t len); + +} // namespace util + +} // namespace nghttp2 + +#endif // UTIL_H diff --git a/src/util_test.cc b/src/util_test.cc new file mode 100644 index 0000000..c1100b7 --- /dev/null +++ b/src/util_test.cc @@ -0,0 +1,370 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2013 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "util_test.h" + +#include +#include + +#include + +#include + +#include "util.h" +#include "template.h" + +using namespace nghttp2; + +namespace shrpx { + +void test_util_streq(void) { + CU_ASSERT(util::streq("alpha", "alpha", 5)); + CU_ASSERT(util::streq("alpha", "alphabravo", 5)); + CU_ASSERT(!util::streq("alpha", "alphabravo", 6)); + CU_ASSERT(!util::streq("alphabravo", "alpha", 5)); + CU_ASSERT(!util::streq("alpha", "alphA", 5)); + CU_ASSERT(!util::streq("", "a", 1)); + CU_ASSERT(util::streq("", "", 0)); + CU_ASSERT(!util::streq("alpha", "", 0)); + + CU_ASSERT(util::streq("alpha", 5, "alpha", 5)); + CU_ASSERT(!util::streq("alpha", 4, "alpha", 5)); + CU_ASSERT(!util::streq("alpha", 5, "alpha", 4)); + CU_ASSERT(!util::streq("alpha", 5, "alphA", 5)); + char *a = nullptr; + char *b = nullptr; + CU_ASSERT(util::streq(a, 0, b, 0)); + + CU_ASSERT(util::streq_l("alpha", "alpha", 5)); + CU_ASSERT(util::streq_l("alpha", "alphabravo", 5)); + CU_ASSERT(!util::streq_l("alpha", "alphabravo", 6)); + CU_ASSERT(!util::streq_l("alphabravo", "alpha", 5)); + CU_ASSERT(!util::streq_l("alpha", "alphA", 5)); + CU_ASSERT(!util::streq_l("", "a", 1)); + CU_ASSERT(util::streq_l("", "", 0)); + CU_ASSERT(!util::streq_l("alpha", "", 0)); +} + +void test_util_strieq(void) { + CU_ASSERT(util::strieq(std::string("alpha"), std::string("alpha"))); + CU_ASSERT(util::strieq(std::string("alpha"), std::string("AlPhA"))); + CU_ASSERT(util::strieq(std::string(), std::string())); + CU_ASSERT(!util::strieq(std::string("alpha"), std::string("AlPhA "))); + CU_ASSERT(!util::strieq(std::string(), std::string("AlPhA "))); + + CU_ASSERT(util::strieq("alpha", "alpha", 5)); + CU_ASSERT(util::strieq("alpha", "AlPhA", 5)); + CU_ASSERT(util::strieq("", static_cast(nullptr), 0)); + CU_ASSERT(!util::strieq("alpha", "AlPhA ", 6)); + CU_ASSERT(!util::strieq("", "AlPhA ", 6)); + + CU_ASSERT(util::strieq("alpha", "alpha")); + CU_ASSERT(util::strieq("alpha", "AlPhA")); + CU_ASSERT(util::strieq("", "")); + CU_ASSERT(!util::strieq("alpha", "AlPhA ")); + CU_ASSERT(!util::strieq("", "AlPhA ")); + + CU_ASSERT(util::strieq_l("alpha", "alpha", 5)); + CU_ASSERT(util::strieq_l("alpha", "AlPhA", 5)); + CU_ASSERT(util::strieq_l("", static_cast(nullptr), 0)); + CU_ASSERT(!util::strieq_l("alpha", "AlPhA ", 6)); + CU_ASSERT(!util::strieq_l("", "AlPhA ", 6)); + + CU_ASSERT(util::strieq_l("alpha", "alpha")); + CU_ASSERT(util::strieq_l("alpha", "AlPhA")); + CU_ASSERT(util::strieq_l("", "")); + CU_ASSERT(!util::strieq_l("alpha", "AlPhA ")); + CU_ASSERT(!util::strieq_l("", "AlPhA ")); +} + +void test_util_inp_strlower(void) { + std::string a("alPha"); + util::inp_strlower(a); + CU_ASSERT("alpha" == a); + + a = "ALPHA123BRAVO"; + util::inp_strlower(a); + CU_ASSERT("alpha123bravo" == a); + + a = ""; + util::inp_strlower(a); + CU_ASSERT("" == a); +} + +void test_util_to_base64(void) { + std::string x = "AAA--B_"; + util::to_base64(x); + CU_ASSERT("AAA++B/=" == x); + + x = "AAA--B_B"; + util::to_base64(x); + CU_ASSERT("AAA++B/B" == x); +} + +void test_util_to_token68(void) { + std::string x = "AAA++B/="; + util::to_token68(x); + CU_ASSERT("AAA--B_" == x); + + x = "AAA++B/B"; + util::to_token68(x); + CU_ASSERT("AAA--B_B" == x); +} + +void test_util_percent_encode_token(void) { + CU_ASSERT("h2" == util::percent_encode_token("h2")); + CU_ASSERT("h3~" == util::percent_encode_token("h3~")); + CU_ASSERT("100%25" == util::percent_encode_token("100%")); + CU_ASSERT("http%202" == util::percent_encode_token("http 2")); +} + +void test_util_percent_encode_path(void) { + CU_ASSERT("/foo1/bar%3F&/%0A" == util::percent_encode_path("/foo1/bar?&/" + "\x0a")); +} + +void test_util_percent_decode(void) { + { + std::string s = "%66%6F%6f%62%61%72"; + CU_ASSERT("foobar" == util::percentDecode(std::begin(s), std::end(s))); + } + { + std::string s = "%66%6"; + CU_ASSERT("f%6" == util::percentDecode(std::begin(s), std::end(s))); + } + { + std::string s = "%66%"; + CU_ASSERT("f%" == util::percentDecode(std::begin(s), std::end(s))); + } +} + +void test_util_quote_string(void) { + CU_ASSERT("alpha" == util::quote_string("alpha")); + CU_ASSERT("" == util::quote_string("")); + CU_ASSERT("\\\"alpha\\\"" == util::quote_string("\"alpha\"")); +} + +void test_util_utox(void) { + CU_ASSERT("0" == util::utox(0)); + CU_ASSERT("1" == util::utox(1)); + CU_ASSERT("F" == util::utox(15)); + CU_ASSERT("10" == util::utox(16)); + CU_ASSERT("3B9ACA07" == util::utox(1000000007)); + CU_ASSERT("100000000" == util::utox(1LL << 32)); +} + +void test_util_http_date(void) { + CU_ASSERT("Thu, 01 Jan 1970 00:00:00 GMT" == util::http_date(0)); + CU_ASSERT("Wed, 29 Feb 2012 09:15:16 GMT" == util::http_date(1330506916)); +} + +void test_util_select_h2(void) { + const unsigned char *out = NULL; + unsigned char outlen = 0; + + // Check single entry and select it. + const unsigned char t1[] = "\x2h2"; + CU_ASSERT(util::select_h2(&out, &outlen, t1, sizeof(t1) - 1)); + CU_ASSERT( + memcmp(NGHTTP2_PROTO_VERSION_ID, out, NGHTTP2_PROTO_VERSION_ID_LEN) == 0); + CU_ASSERT(NGHTTP2_PROTO_VERSION_ID_LEN == outlen); + + out = NULL; + outlen = 0; + + // Check the case where id is correct but length is invalid and too + // long. + const unsigned char t2[] = "\x6h2-14"; + CU_ASSERT(!util::select_h2(&out, &outlen, t2, sizeof(t2) - 1)); + + // Check the case where h2 is located after bogus ID. + const unsigned char t3[] = "\x2h3\x2h2"; + CU_ASSERT(util::select_h2(&out, &outlen, t3, sizeof(t3) - 1)); + + CU_ASSERT( + memcmp(NGHTTP2_PROTO_VERSION_ID, out, NGHTTP2_PROTO_VERSION_ID_LEN) == 0); + CU_ASSERT(NGHTTP2_PROTO_VERSION_ID_LEN == outlen); + + out = NULL; + outlen = 0; + + // Check the case that last entry's length is invalid and too long. + const unsigned char t4[] = "\x2h3\x6h2-14"; + CU_ASSERT(!util::select_h2(&out, &outlen, t4, sizeof(t4) - 1)); + + // Check the case that all entries are not supported. + const unsigned char t5[] = "\x2h3\x2h4"; + CU_ASSERT(!util::select_h2(&out, &outlen, t5, sizeof(t5) - 1)); + + // Check the case where 2 values are eligible, but last one is + // picked up because it has precedence over the other. + const unsigned char t6[] = "\x5h2-14\x5h2-16"; + CU_ASSERT(util::select_h2(&out, &outlen, t6, sizeof(t6) - 1)); + CU_ASSERT(memcmp(NGHTTP2_H2_16, out, str_size(NGHTTP2_H2_16)) == 0); + CU_ASSERT(str_size(NGHTTP2_H2_16) == outlen); +} + +void test_util_ipv6_numeric_addr(void) { + CU_ASSERT(util::ipv6_numeric_addr("::1")); + CU_ASSERT(util::ipv6_numeric_addr("2001:0db8:85a3:0042:1000:8a2e:0370:7334")); + // IPv4 + CU_ASSERT(!util::ipv6_numeric_addr("127.0.0.1")); + // not numeric address + CU_ASSERT(!util::ipv6_numeric_addr("localhost")); +} + +void test_util_utos_with_unit(void) { + CU_ASSERT("0" == util::utos_with_unit(0)); + CU_ASSERT("1023" == util::utos_with_unit(1023)); + CU_ASSERT("1K" == util::utos_with_unit(1024)); + CU_ASSERT("1K" == util::utos_with_unit(1025)); + CU_ASSERT("1M" == util::utos_with_unit(1 << 20)); + CU_ASSERT("1G" == util::utos_with_unit(1 << 30)); + CU_ASSERT("1024G" == util::utos_with_unit(1LL << 40)); +} + +void test_util_utos_with_funit(void) { + CU_ASSERT("0" == util::utos_with_funit(0)); + CU_ASSERT("1023" == util::utos_with_funit(1023)); + CU_ASSERT("1.00K" == util::utos_with_funit(1024)); + CU_ASSERT("1.00K" == util::utos_with_funit(1025)); + CU_ASSERT("1.09K" == util::utos_with_funit(1119)); + CU_ASSERT("1.27K" == util::utos_with_funit(1300)); + CU_ASSERT("1.00M" == util::utos_with_funit(1 << 20)); + CU_ASSERT("1.18M" == util::utos_with_funit(1234567)); + CU_ASSERT("1.00G" == util::utos_with_funit(1 << 30)); + CU_ASSERT("4492450797.23G" == util::utos_with_funit(4823732313248234343LL)); + CU_ASSERT("1024.00G" == util::utos_with_funit(1LL << 40)); +} + +void test_util_parse_uint_with_unit(void) { + CU_ASSERT(0 == util::parse_uint_with_unit("0")); + CU_ASSERT(1023 == util::parse_uint_with_unit("1023")); + CU_ASSERT(1024 == util::parse_uint_with_unit("1k")); + CU_ASSERT(2048 == util::parse_uint_with_unit("2K")); + CU_ASSERT(1 << 20 == util::parse_uint_with_unit("1m")); + CU_ASSERT(1 << 21 == util::parse_uint_with_unit("2M")); + CU_ASSERT(1 << 30 == util::parse_uint_with_unit("1g")); + CU_ASSERT(1LL << 31 == util::parse_uint_with_unit("2G")); + CU_ASSERT(9223372036854775807LL == + util::parse_uint_with_unit("9223372036854775807")); + // check overflow case + CU_ASSERT(-1 == util::parse_uint_with_unit("9223372036854775808")); + CU_ASSERT(-1 == util::parse_uint_with_unit("10000000000000000000")); + CU_ASSERT(-1 == util::parse_uint_with_unit("9223372036854775807G")); + // bad characters + CU_ASSERT(-1 == util::parse_uint_with_unit("1.1")); + CU_ASSERT(-1 == util::parse_uint_with_unit("1a")); + CU_ASSERT(-1 == util::parse_uint_with_unit("a1")); + CU_ASSERT(-1 == util::parse_uint_with_unit("1T")); + CU_ASSERT(-1 == util::parse_uint_with_unit("")); +} + +void test_util_parse_uint(void) { + CU_ASSERT(0 == util::parse_uint("0")); + CU_ASSERT(1023 == util::parse_uint("1023")); + CU_ASSERT(-1 == util::parse_uint("1k")); + CU_ASSERT(9223372036854775807LL == util::parse_uint("9223372036854775807")); + // check overflow case + CU_ASSERT(-1 == util::parse_uint("9223372036854775808")); + CU_ASSERT(-1 == util::parse_uint("10000000000000000000")); + // bad characters + CU_ASSERT(-1 == util::parse_uint("1.1")); + CU_ASSERT(-1 == util::parse_uint("1a")); + CU_ASSERT(-1 == util::parse_uint("a1")); + CU_ASSERT(-1 == util::parse_uint("1T")); + CU_ASSERT(-1 == util::parse_uint("")); +} + +void test_util_parse_duration_with_unit(void) { + CU_ASSERT(0. == util::parse_duration_with_unit("0")); + CU_ASSERT(123. == util::parse_duration_with_unit("123")); + CU_ASSERT(123. == util::parse_duration_with_unit("123s")); + CU_ASSERT(0.500 == util::parse_duration_with_unit("500ms")); + CU_ASSERT(123. == util::parse_duration_with_unit("123S")); + CU_ASSERT(0.500 == util::parse_duration_with_unit("500MS")); + CU_ASSERT(180 == util::parse_duration_with_unit("3m")); + CU_ASSERT(3600 * 5 == util::parse_duration_with_unit("5h")); + + auto err = std::numeric_limits::infinity(); + // check overflow case + CU_ASSERT(err == util::parse_duration_with_unit("9223372036854775808")); + // bad characters + CU_ASSERT(err == util::parse_duration_with_unit("0u")); + CU_ASSERT(err == util::parse_duration_with_unit("0xs")); + CU_ASSERT(err == util::parse_duration_with_unit("0mt")); + CU_ASSERT(err == util::parse_duration_with_unit("0mss")); + CU_ASSERT(err == util::parse_duration_with_unit("s")); + CU_ASSERT(err == util::parse_duration_with_unit("ms")); +} + +void test_util_duration_str(void) { + CU_ASSERT("0" == util::duration_str(0.)); + CU_ASSERT("1s" == util::duration_str(1.)); + CU_ASSERT("500ms" == util::duration_str(0.5)); + CU_ASSERT("1500ms" == util::duration_str(1.5)); + CU_ASSERT("2m" == util::duration_str(120.)); + CU_ASSERT("121s" == util::duration_str(121.)); + CU_ASSERT("1h" == util::duration_str(3600.)); +} + +void test_util_format_duration(void) { + CU_ASSERT("0us" == util::format_duration(std::chrono::microseconds(0))); + CU_ASSERT("999us" == util::format_duration(std::chrono::microseconds(999))); + CU_ASSERT("1.00ms" == util::format_duration(std::chrono::microseconds(1000))); + CU_ASSERT("1.09ms" == util::format_duration(std::chrono::microseconds(1090))); + CU_ASSERT("1.01ms" == util::format_duration(std::chrono::microseconds(1009))); + CU_ASSERT("999.99ms" == + util::format_duration(std::chrono::microseconds(999990))); + CU_ASSERT("1.00s" == + util::format_duration(std::chrono::microseconds(1000000))); + CU_ASSERT("1.05s" == + util::format_duration(std::chrono::microseconds(1050000))); +} + +void test_util_starts_with(void) { + CU_ASSERT(util::startsWith("foo", "foo")); + CU_ASSERT(util::startsWith("fooo", "foo")); + CU_ASSERT(util::startsWith("ofoo", "")); + CU_ASSERT(!util::startsWith("ofoo", "foo")); + + CU_ASSERT(util::istartsWith("FOO", "fOO")); + CU_ASSERT(util::startsWith("ofoo", "")); + CU_ASSERT(util::istartsWith("fOOo", "Foo")); + CU_ASSERT(!util::istartsWith("ofoo", "foo")); +} + +void test_util_ends_with(void) { + CU_ASSERT(util::endsWith("foo", "foo")); + CU_ASSERT(util::endsWith("foo", "")); + CU_ASSERT(util::endsWith("ofoo", "foo")); + CU_ASSERT(!util::endsWith("ofoo", "fo")); + + CU_ASSERT(util::iendsWith("fOo", "Foo")); + CU_ASSERT(util::iendsWith("foo", "")); + CU_ASSERT(util::iendsWith("oFoo", "fOO")); + CU_ASSERT(!util::iendsWith("ofoo", "fo")); +} + +} // namespace shrpx diff --git a/src/util_test.h b/src/util_test.h new file mode 100644 index 0000000..00290b8 --- /dev/null +++ b/src/util_test.h @@ -0,0 +1,59 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2013 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef UTIL_TEST_H +#define UTIL_TEST_H + +#ifdef HAVE_CONFIG_H +#include +#endif // HAVE_CONFIG_H + +namespace shrpx { + +void test_util_streq(void); +void test_util_strieq(void); +void test_util_inp_strlower(void); +void test_util_to_base64(void); +void test_util_to_token68(void); +void test_util_percent_encode_token(void); +void test_util_percent_encode_path(void); +void test_util_percent_decode(void); +void test_util_quote_string(void); +void test_util_utox(void); +void test_util_http_date(void); +void test_util_select_h2(void); +void test_util_ipv6_numeric_addr(void); +void test_util_utos_with_unit(void); +void test_util_utos_with_funit(void); +void test_util_parse_uint_with_unit(void); +void test_util_parse_uint(void); +void test_util_parse_duration_with_unit(void); +void test_util_duration_str(void); +void test_util_format_duration(void); +void test_util_starts_with(void); +void test_util_ends_with(void); + +} // namespace shrpx + +#endif // UTIL_TEST_H diff --git a/test-driver b/test-driver new file mode 100755 index 0000000..d306056 --- /dev/null +++ b/test-driver @@ -0,0 +1,139 @@ +#! /bin/sh +# test-driver - basic testsuite driver script. + +scriptversion=2013-07-13.22; # UTC + +# Copyright (C) 2011-2013 Free Software Foundation, Inc. +# +# 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 2, 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 . + +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +# This file is maintained in Automake, please report +# bugs to or send patches to +# . + +# Make unconditional expansion of undefined variables an error. This +# helps a lot in preventing typo-related bugs. +set -u + +usage_error () +{ + echo "$0: $*" >&2 + print_usage >&2 + exit 2 +} + +print_usage () +{ + cat <$log_file 2>&1 +estatus=$? +if test $enable_hard_errors = no && test $estatus -eq 99; then + estatus=1 +fi + +case $estatus:$expect_failure in + 0:yes) col=$red res=XPASS recheck=yes gcopy=yes;; + 0:*) col=$grn res=PASS recheck=no gcopy=no;; + 77:*) col=$blu res=SKIP recheck=no gcopy=yes;; + 99:*) col=$mgn res=ERROR recheck=yes gcopy=yes;; + *:yes) col=$lgn res=XFAIL recheck=no gcopy=yes;; + *:*) col=$red res=FAIL recheck=yes gcopy=yes;; +esac + +# Report outcome to console. +echo "${col}${res}${std}: $test_name" + +# Register the test result, and other relevant metadata. +echo ":test-result: $res" > $trs_file +echo ":global-test-result: $res" >> $trs_file +echo ":recheck: $recheck" >> $trs_file +echo ":copy-in-global-log: $gcopy" >> $trs_file + +# Local Variables: +# mode: shell-script +# sh-indentation: 2 +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "scriptversion=" +# time-stamp-format: "%:y-%02m-%02d.%02H" +# time-stamp-time-zone: "UTC" +# time-stamp-end: "; # UTC" +# End: diff --git a/tests/Makefile.am b/tests/Makefile.am new file mode 100644 index 0000000..4ef17bb --- /dev/null +++ b/tests/Makefile.am @@ -0,0 +1,82 @@ +# nghttp2 - HTTP/2 C Library + +# Copyright (c) 2012 Tatsuhiro Tsujikawa + +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: + +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. + +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +SUBDIRS = testdata + +if HAVE_CUNIT + +check_PROGRAMS = main + +if ENABLE_FAILMALLOC +check_PROGRAMS += failmalloc +endif # ENABLE_FAILMALLOC + +OBJECTS = main.c nghttp2_pq_test.c nghttp2_map_test.c nghttp2_queue_test.c \ + nghttp2_test_helper.c \ + nghttp2_frame_test.c \ + nghttp2_stream_test.c \ + nghttp2_session_test.c \ + nghttp2_hd_test.c \ + nghttp2_npn_test.c \ + nghttp2_helper_test.c \ + nghttp2_buf_test.c + +HFILES = nghttp2_pq_test.h nghttp2_map_test.h nghttp2_queue_test.h \ + nghttp2_session_test.h \ + nghttp2_frame_test.h nghttp2_stream_test.h nghttp2_hd_test.h \ + nghttp2_npn_test.h nghttp2_helper_test.h \ + nghttp2_test_helper.h \ + nghttp2_buf_test.h + +main_SOURCES = $(HFILES) $(OBJECTS) + +main_LDADD = ${top_builddir}/lib/libnghttp2.la @CUNIT_LIBS@ @TESTLDADD@ +main_LDFLAGS = -static + +if ENABLE_FAILMALLOC +failmalloc_SOURCES = failmalloc.c failmalloc_test.c failmalloc_test.h \ + malloc_wrapper.c malloc_wrapper.h \ + nghttp2_test_helper.c nghttp2_test_helper.h +failmalloc_LDADD = $(main_LDADD) +failmalloc_LDFLAGS = $(main_LDFLAGS) +endif # ENABLE_FAILMALLOC + +AM_CFLAGS = $(WARNCFLAGS) \ + -I${top_srcdir}/lib \ + -I${top_srcdir}/lib/includes \ + -I${top_builddir}/lib/includes \ + @CUNIT_CFLAGS@ @DEFS@ + +TESTS = main + +if ENABLE_FAILMALLOC +TESTS += failmalloc +endif # ENABLE_FAILMALLOC + +if ENABLE_APP + +# EXTRA_DIST = end_to_end.py +# TESTS += end_to_end.py + +endif # ENABLE_APP + +endif # HAVE_CUNIT diff --git a/tests/Makefile.in b/tests/Makefile.in new file mode 100644 index 0000000..3c9a4c2 --- /dev/null +++ b/tests/Makefile.in @@ -0,0 +1,1253 @@ +# Makefile.in generated by automake 1.14.1 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2013 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +# nghttp2 - HTTP/2 C Library + +# Copyright (c) 2012 Tatsuhiro Tsujikawa + +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: + +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +VPATH = @srcdir@ +am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)' +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +target_triplet = @target@ +@HAVE_CUNIT_TRUE@check_PROGRAMS = main$(EXEEXT) $(am__EXEEXT_1) +@ENABLE_FAILMALLOC_TRUE@@HAVE_CUNIT_TRUE@am__append_1 = failmalloc +@HAVE_CUNIT_TRUE@TESTS = main$(EXEEXT) $(am__EXEEXT_1) +@ENABLE_FAILMALLOC_TRUE@@HAVE_CUNIT_TRUE@am__append_2 = failmalloc +subdir = tests +DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \ + $(top_srcdir)/depcomp $(top_srcdir)/test-driver +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/ax_boost_asio.m4 \ + $(top_srcdir)/m4/ax_boost_base.m4 \ + $(top_srcdir)/m4/ax_boost_system.m4 \ + $(top_srcdir)/m4/ax_boost_thread.m4 \ + $(top_srcdir)/m4/ax_check_compile_flag.m4 \ + $(top_srcdir)/m4/ax_cxx_compile_stdcxx_11.m4 \ + $(top_srcdir)/m4/ax_have_epoll.m4 \ + $(top_srcdir)/m4/ax_python_devel.m4 \ + $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/libxml2.m4 \ + $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ + $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +@ENABLE_FAILMALLOC_TRUE@@HAVE_CUNIT_TRUE@am__EXEEXT_1 = \ +@ENABLE_FAILMALLOC_TRUE@@HAVE_CUNIT_TRUE@ failmalloc$(EXEEXT) +am__failmalloc_SOURCES_DIST = failmalloc.c failmalloc_test.c \ + failmalloc_test.h malloc_wrapper.c malloc_wrapper.h \ + nghttp2_test_helper.c nghttp2_test_helper.h +@ENABLE_FAILMALLOC_TRUE@@HAVE_CUNIT_TRUE@am_failmalloc_OBJECTS = \ +@ENABLE_FAILMALLOC_TRUE@@HAVE_CUNIT_TRUE@ failmalloc.$(OBJEXT) \ +@ENABLE_FAILMALLOC_TRUE@@HAVE_CUNIT_TRUE@ failmalloc_test.$(OBJEXT) \ +@ENABLE_FAILMALLOC_TRUE@@HAVE_CUNIT_TRUE@ malloc_wrapper.$(OBJEXT) \ +@ENABLE_FAILMALLOC_TRUE@@HAVE_CUNIT_TRUE@ nghttp2_test_helper.$(OBJEXT) +failmalloc_OBJECTS = $(am_failmalloc_OBJECTS) +@HAVE_CUNIT_TRUE@am__DEPENDENCIES_1 = \ +@HAVE_CUNIT_TRUE@ ${top_builddir}/lib/libnghttp2.la +@ENABLE_FAILMALLOC_TRUE@@HAVE_CUNIT_TRUE@failmalloc_DEPENDENCIES = $(am__DEPENDENCIES_1) +AM_V_lt = $(am__v_lt_@AM_V@) +am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) +am__v_lt_0 = --silent +am__v_lt_1 = +failmalloc_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(failmalloc_LDFLAGS) $(LDFLAGS) -o $@ +am__main_SOURCES_DIST = nghttp2_pq_test.h nghttp2_map_test.h \ + nghttp2_queue_test.h nghttp2_session_test.h \ + nghttp2_frame_test.h nghttp2_stream_test.h nghttp2_hd_test.h \ + nghttp2_npn_test.h nghttp2_helper_test.h nghttp2_test_helper.h \ + nghttp2_buf_test.h main.c nghttp2_pq_test.c nghttp2_map_test.c \ + nghttp2_queue_test.c nghttp2_test_helper.c \ + nghttp2_frame_test.c nghttp2_stream_test.c \ + nghttp2_session_test.c nghttp2_hd_test.c nghttp2_npn_test.c \ + nghttp2_helper_test.c nghttp2_buf_test.c +am__objects_1 = +@HAVE_CUNIT_TRUE@am__objects_2 = main.$(OBJEXT) \ +@HAVE_CUNIT_TRUE@ nghttp2_pq_test.$(OBJEXT) \ +@HAVE_CUNIT_TRUE@ nghttp2_map_test.$(OBJEXT) \ +@HAVE_CUNIT_TRUE@ nghttp2_queue_test.$(OBJEXT) \ +@HAVE_CUNIT_TRUE@ nghttp2_test_helper.$(OBJEXT) \ +@HAVE_CUNIT_TRUE@ nghttp2_frame_test.$(OBJEXT) \ +@HAVE_CUNIT_TRUE@ nghttp2_stream_test.$(OBJEXT) \ +@HAVE_CUNIT_TRUE@ nghttp2_session_test.$(OBJEXT) \ +@HAVE_CUNIT_TRUE@ nghttp2_hd_test.$(OBJEXT) \ +@HAVE_CUNIT_TRUE@ nghttp2_npn_test.$(OBJEXT) \ +@HAVE_CUNIT_TRUE@ nghttp2_helper_test.$(OBJEXT) \ +@HAVE_CUNIT_TRUE@ nghttp2_buf_test.$(OBJEXT) +@HAVE_CUNIT_TRUE@am_main_OBJECTS = $(am__objects_1) $(am__objects_2) +main_OBJECTS = $(am_main_OBJECTS) +@HAVE_CUNIT_TRUE@main_DEPENDENCIES = \ +@HAVE_CUNIT_TRUE@ ${top_builddir}/lib/libnghttp2.la +main_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(main_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__depfiles_maybe = depfiles +am__mv = mv -f +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CFLAGS) $(CFLAGS) +AM_V_CC = $(am__v_CC_@AM_V@) +am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) +am__v_CC_0 = @echo " CC " $@; +am__v_CC_1 = +CCLD = $(CC) +LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CCLD = $(am__v_CCLD_@AM_V@) +am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) +am__v_CCLD_0 = @echo " CCLD " $@; +am__v_CCLD_1 = +SOURCES = $(failmalloc_SOURCES) $(main_SOURCES) +DIST_SOURCES = $(am__failmalloc_SOURCES_DIST) $(am__main_SOURCES_DIST) +RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \ + ctags-recursive dvi-recursive html-recursive info-recursive \ + install-data-recursive install-dvi-recursive \ + install-exec-recursive install-html-recursive \ + install-info-recursive install-pdf-recursive \ + install-ps-recursive install-recursive installcheck-recursive \ + installdirs-recursive pdf-recursive ps-recursive \ + tags-recursive uninstall-recursive +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \ + distclean-recursive maintainer-clean-recursive +am__recursive_targets = \ + $(RECURSIVE_TARGETS) \ + $(RECURSIVE_CLEAN_TARGETS) \ + $(am__extra_recursive_targets) +AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \ + check recheck distdir +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +ETAGS = etags +CTAGS = ctags +am__tty_colors_dummy = \ + mgn= red= grn= lgn= blu= brg= std=; \ + am__color_tests=no +am__tty_colors = { \ + $(am__tty_colors_dummy); \ + if test "X$(AM_COLOR_TESTS)" = Xno; then \ + am__color_tests=no; \ + elif test "X$(AM_COLOR_TESTS)" = Xalways; then \ + am__color_tests=yes; \ + elif test "X$$TERM" != Xdumb && { test -t 1; } 2>/dev/null; then \ + am__color_tests=yes; \ + fi; \ + if test $$am__color_tests = yes; then \ + red=''; \ + grn=''; \ + lgn=''; \ + blu=''; \ + mgn=''; \ + brg=''; \ + std=''; \ + fi; \ +} +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +am__uninstall_files_from_dir = { \ + test -z "$$files" \ + || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ + || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ + $(am__cd) "$$dir" && rm -f $$files; }; \ + } +am__recheck_rx = ^[ ]*:recheck:[ ]* +am__global_test_result_rx = ^[ ]*:global-test-result:[ ]* +am__copy_in_global_log_rx = ^[ ]*:copy-in-global-log:[ ]* +# A command that, given a newline-separated list of test names on the +# standard input, print the name of the tests that are to be re-run +# upon "make recheck". +am__list_recheck_tests = $(AWK) '{ \ + recheck = 1; \ + while ((rc = (getline line < ($$0 ".trs"))) != 0) \ + { \ + if (rc < 0) \ + { \ + if ((getline line2 < ($$0 ".log")) < 0) \ + recheck = 0; \ + break; \ + } \ + else if (line ~ /$(am__recheck_rx)[nN][Oo]/) \ + { \ + recheck = 0; \ + break; \ + } \ + else if (line ~ /$(am__recheck_rx)[yY][eE][sS]/) \ + { \ + break; \ + } \ + }; \ + if (recheck) \ + print $$0; \ + close ($$0 ".trs"); \ + close ($$0 ".log"); \ +}' +# A command that, given a newline-separated list of test names on the +# standard input, create the global log from their .trs and .log files. +am__create_global_log = $(AWK) ' \ +function fatal(msg) \ +{ \ + print "fatal: making $@: " msg | "cat >&2"; \ + exit 1; \ +} \ +function rst_section(header) \ +{ \ + print header; \ + len = length(header); \ + for (i = 1; i <= len; i = i + 1) \ + printf "="; \ + printf "\n\n"; \ +} \ +{ \ + copy_in_global_log = 1; \ + global_test_result = "RUN"; \ + while ((rc = (getline line < ($$0 ".trs"))) != 0) \ + { \ + if (rc < 0) \ + fatal("failed to read from " $$0 ".trs"); \ + if (line ~ /$(am__global_test_result_rx)/) \ + { \ + sub("$(am__global_test_result_rx)", "", line); \ + sub("[ ]*$$", "", line); \ + global_test_result = line; \ + } \ + else if (line ~ /$(am__copy_in_global_log_rx)[nN][oO]/) \ + copy_in_global_log = 0; \ + }; \ + if (copy_in_global_log) \ + { \ + rst_section(global_test_result ": " $$0); \ + while ((rc = (getline line < ($$0 ".log"))) != 0) \ + { \ + if (rc < 0) \ + fatal("failed to read from " $$0 ".log"); \ + print line; \ + }; \ + printf "\n"; \ + }; \ + close ($$0 ".trs"); \ + close ($$0 ".log"); \ +}' +# Restructured Text title. +am__rst_title = { sed 's/.*/ & /;h;s/./=/g;p;x;s/ *$$//;p;g' && echo; } +# Solaris 10 'make', and several other traditional 'make' implementations, +# pass "-e" to $(SHELL), and POSIX 2008 even requires this. Work around it +# by disabling -e (using the XSI extension "set +e") if it's set. +am__sh_e_setup = case $$- in *e*) set +e;; esac +# Default flags passed to test drivers. +am__common_driver_flags = \ + --color-tests "$$am__color_tests" \ + --enable-hard-errors "$$am__enable_hard_errors" \ + --expect-failure "$$am__expect_failure" +# To be inserted before the command running the test. Creates the +# directory for the log if needed. Stores in $dir the directory +# containing $f, in $tst the test, in $log the log. Executes the +# developer- defined test setup AM_TESTS_ENVIRONMENT (if any), and +# passes TESTS_ENVIRONMENT. Set up options for the wrapper that +# will run the test scripts (or their associated LOG_COMPILER, if +# thy have one). +am__check_pre = \ +$(am__sh_e_setup); \ +$(am__vpath_adj_setup) $(am__vpath_adj) \ +$(am__tty_colors); \ +srcdir=$(srcdir); export srcdir; \ +case "$@" in \ + */*) am__odir=`echo "./$@" | sed 's|/[^/]*$$||'`;; \ + *) am__odir=.;; \ +esac; \ +test "x$$am__odir" = x"." || test -d "$$am__odir" \ + || $(MKDIR_P) "$$am__odir" || exit $$?; \ +if test -f "./$$f"; then dir=./; \ +elif test -f "$$f"; then dir=; \ +else dir="$(srcdir)/"; fi; \ +tst=$$dir$$f; log='$@'; \ +if test -n '$(DISABLE_HARD_ERRORS)'; then \ + am__enable_hard_errors=no; \ +else \ + am__enable_hard_errors=yes; \ +fi; \ +case " $(XFAIL_TESTS) " in \ + *[\ \ ]$$f[\ \ ]* | *[\ \ ]$$dir$$f[\ \ ]*) \ + am__expect_failure=yes;; \ + *) \ + am__expect_failure=no;; \ +esac; \ +$(AM_TESTS_ENVIRONMENT) $(TESTS_ENVIRONMENT) +# A shell command to get the names of the tests scripts with any registered +# extension removed (i.e., equivalently, the names of the test logs, with +# the '.log' extension removed). The result is saved in the shell variable +# '$bases'. This honors runtime overriding of TESTS and TEST_LOGS. Sadly, +# we cannot use something simpler, involving e.g., "$(TEST_LOGS:.log=)", +# since that might cause problem with VPATH rewrites for suffix-less tests. +# See also 'test-harness-vpath-rewrite.sh' and 'test-trs-basic.sh'. +am__set_TESTS_bases = \ + bases='$(TEST_LOGS)'; \ + bases=`for i in $$bases; do echo $$i; done | sed 's/\.log$$//'`; \ + bases=`echo $$bases` +RECHECK_LOGS = $(TEST_LOGS) +TEST_SUITE_LOG = test-suite.log +TEST_EXTENSIONS = @EXEEXT@ .test +LOG_DRIVER = $(SHELL) $(top_srcdir)/test-driver +LOG_COMPILE = $(LOG_COMPILER) $(AM_LOG_FLAGS) $(LOG_FLAGS) +am__set_b = \ + case '$@' in \ + */*) \ + case '$*' in \ + */*) b='$*';; \ + *) b=`echo '$@' | sed 's/\.log$$//'`; \ + esac;; \ + *) \ + b='$*';; \ + esac +am__test_logs1 = $(TESTS:=.log) +am__test_logs2 = $(am__test_logs1:@EXEEXT@.log=.log) +TEST_LOGS = $(am__test_logs2:.test.log=.log) +TEST_LOG_DRIVER = $(SHELL) $(top_srcdir)/test-driver +TEST_LOG_COMPILE = $(TEST_LOG_COMPILER) $(AM_TEST_LOG_FLAGS) \ + $(TEST_LOG_FLAGS) +DIST_SUBDIRS = $(SUBDIRS) +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +am__relativize = \ + dir0=`pwd`; \ + sed_first='s,^\([^/]*\)/.*$$,\1,'; \ + sed_rest='s,^[^/]*/*,,'; \ + sed_last='s,^.*/\([^/]*\)$$,\1,'; \ + sed_butlast='s,/*[^/]*$$,,'; \ + while test -n "$$dir1"; do \ + first=`echo "$$dir1" | sed -e "$$sed_first"`; \ + if test "$$first" != "."; then \ + if test "$$first" = ".."; then \ + dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \ + dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \ + else \ + first2=`echo "$$dir2" | sed -e "$$sed_first"`; \ + if test "$$first2" = "$$first"; then \ + dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \ + else \ + dir2="../$$dir2"; \ + fi; \ + dir0="$$dir0"/"$$first"; \ + fi; \ + fi; \ + dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \ + done; \ + reldir="$$dir2" +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +APPLDFLAGS = @APPLDFLAGS@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +BOOST_ASIO_LIB = @BOOST_ASIO_LIB@ +BOOST_CPPFLAGS = @BOOST_CPPFLAGS@ +BOOST_LDFLAGS = @BOOST_LDFLAGS@ +BOOST_SYSTEM_LIB = @BOOST_SYSTEM_LIB@ +BOOST_THREAD_LIB = @BOOST_THREAD_LIB@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CUNIT_CFLAGS = @CUNIT_CFLAGS@ +CUNIT_LIBS = @CUNIT_LIBS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +CYTHON = @CYTHON@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DLLTOOL = @DLLTOOL@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +GREP = @GREP@ +HAVE_CXX11 = @HAVE_CXX11@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +JANSSON_CFLAGS = @JANSSON_CFLAGS@ +JANSSON_LIBS = @JANSSON_LIBS@ +JEMALLOC_LIBS = @JEMALLOC_LIBS@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBEVENT_OPENSSL_CFLAGS = @LIBEVENT_OPENSSL_CFLAGS@ +LIBEVENT_OPENSSL_LIBS = @LIBEVENT_OPENSSL_LIBS@ +LIBEV_CFLAGS = @LIBEV_CFLAGS@ +LIBEV_LIBS = @LIBEV_LIBS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBSPDYLAY_CFLAGS = @LIBSPDYLAY_CFLAGS@ +LIBSPDYLAY_LIBS = @LIBSPDYLAY_LIBS@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +LT_AGE = @LT_AGE@ +LT_CURRENT = @LT_CURRENT@ +LT_REVISION = @LT_REVISION@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MKDIR_P = @MKDIR_P@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OPENSSL_CFLAGS = @OPENSSL_CFLAGS@ +OPENSSL_LIBS = @OPENSSL_LIBS@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PACKAGE_VERSION_NUM = @PACKAGE_VERSION_NUM@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PKG_CONFIG = @PKG_CONFIG@ +PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ +PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ +PYTHON = @PYTHON@ +PYTHON_CPPFLAGS = @PYTHON_CPPFLAGS@ +PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@ +PYTHON_EXTRA_LDFLAGS = @PYTHON_EXTRA_LDFLAGS@ +PYTHON_EXTRA_LIBS = @PYTHON_EXTRA_LIBS@ +PYTHON_LDFLAGS = @PYTHON_LDFLAGS@ +PYTHON_PLATFORM = @PYTHON_PLATFORM@ +PYTHON_PREFIX = @PYTHON_PREFIX@ +PYTHON_SITE_PKG = @PYTHON_SITE_PKG@ +PYTHON_VERSION = @PYTHON_VERSION@ +RANLIB = @RANLIB@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +TESTLDADD = @TESTLDADD@ +VERSION = @VERSION@ +WARNCFLAGS = @WARNCFLAGS@ +XML2_CONFIG = @XML2_CONFIG@ +XML_CPPFLAGS = @XML_CPPFLAGS@ +XML_LIBS = @XML_LIBS@ +ZLIB_CFLAGS = @ZLIB_CFLAGS@ +ZLIB_LIBS = @ZLIB_LIBS@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +pkgpyexecdir = @pkgpyexecdir@ +pkgpythondir = @pkgpythondir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +pyexecdir = @pyexecdir@ +pythondir = @pythondir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target = @target@ +target_alias = @target_alias@ +target_cpu = @target_cpu@ +target_os = @target_os@ +target_vendor = @target_vendor@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ + +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +SUBDIRS = testdata +@HAVE_CUNIT_TRUE@OBJECTS = main.c nghttp2_pq_test.c nghttp2_map_test.c nghttp2_queue_test.c \ +@HAVE_CUNIT_TRUE@ nghttp2_test_helper.c \ +@HAVE_CUNIT_TRUE@ nghttp2_frame_test.c \ +@HAVE_CUNIT_TRUE@ nghttp2_stream_test.c \ +@HAVE_CUNIT_TRUE@ nghttp2_session_test.c \ +@HAVE_CUNIT_TRUE@ nghttp2_hd_test.c \ +@HAVE_CUNIT_TRUE@ nghttp2_npn_test.c \ +@HAVE_CUNIT_TRUE@ nghttp2_helper_test.c \ +@HAVE_CUNIT_TRUE@ nghttp2_buf_test.c + +@HAVE_CUNIT_TRUE@HFILES = nghttp2_pq_test.h nghttp2_map_test.h nghttp2_queue_test.h \ +@HAVE_CUNIT_TRUE@ nghttp2_session_test.h \ +@HAVE_CUNIT_TRUE@ nghttp2_frame_test.h nghttp2_stream_test.h nghttp2_hd_test.h \ +@HAVE_CUNIT_TRUE@ nghttp2_npn_test.h nghttp2_helper_test.h \ +@HAVE_CUNIT_TRUE@ nghttp2_test_helper.h \ +@HAVE_CUNIT_TRUE@ nghttp2_buf_test.h + +@HAVE_CUNIT_TRUE@main_SOURCES = $(HFILES) $(OBJECTS) +@HAVE_CUNIT_TRUE@main_LDADD = ${top_builddir}/lib/libnghttp2.la @CUNIT_LIBS@ @TESTLDADD@ +@HAVE_CUNIT_TRUE@main_LDFLAGS = -static +@ENABLE_FAILMALLOC_TRUE@@HAVE_CUNIT_TRUE@failmalloc_SOURCES = failmalloc.c failmalloc_test.c failmalloc_test.h \ +@ENABLE_FAILMALLOC_TRUE@@HAVE_CUNIT_TRUE@ malloc_wrapper.c malloc_wrapper.h \ +@ENABLE_FAILMALLOC_TRUE@@HAVE_CUNIT_TRUE@ nghttp2_test_helper.c nghttp2_test_helper.h + +@ENABLE_FAILMALLOC_TRUE@@HAVE_CUNIT_TRUE@failmalloc_LDADD = $(main_LDADD) +@ENABLE_FAILMALLOC_TRUE@@HAVE_CUNIT_TRUE@failmalloc_LDFLAGS = $(main_LDFLAGS) +@HAVE_CUNIT_TRUE@AM_CFLAGS = $(WARNCFLAGS) \ +@HAVE_CUNIT_TRUE@ -I${top_srcdir}/lib \ +@HAVE_CUNIT_TRUE@ -I${top_srcdir}/lib/includes \ +@HAVE_CUNIT_TRUE@ -I${top_builddir}/lib/includes \ +@HAVE_CUNIT_TRUE@ @CUNIT_CFLAGS@ @DEFS@ + +all: all-recursive + +.SUFFIXES: +.SUFFIXES: .c .lo .log .o .obj .test .test$(EXEEXT) .trs +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu tests/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu tests/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): + +clean-checkPROGRAMS: + @list='$(check_PROGRAMS)'; test -n "$$list" || exit 0; \ + echo " rm -f" $$list; \ + rm -f $$list || exit $$?; \ + test -n "$(EXEEXT)" || exit 0; \ + list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ + echo " rm -f" $$list; \ + rm -f $$list + +failmalloc$(EXEEXT): $(failmalloc_OBJECTS) $(failmalloc_DEPENDENCIES) $(EXTRA_failmalloc_DEPENDENCIES) + @rm -f failmalloc$(EXEEXT) + $(AM_V_CCLD)$(failmalloc_LINK) $(failmalloc_OBJECTS) $(failmalloc_LDADD) $(LIBS) + +main$(EXEEXT): $(main_OBJECTS) $(main_DEPENDENCIES) $(EXTRA_main_DEPENDENCIES) + @rm -f main$(EXEEXT) + $(AM_V_CCLD)$(main_LINK) $(main_OBJECTS) $(main_LDADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/failmalloc.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/failmalloc_test.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/main.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/malloc_wrapper.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_buf_test.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_frame_test.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_hd_test.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_helper_test.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_map_test.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_npn_test.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_pq_test.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_queue_test.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_session_test.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_stream_test.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_test_helper.Po@am__quote@ + +.c.o: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\ +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< + +.c.obj: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\ +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.c.lo: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\ +@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +# This directory's subdirectories are mostly independent; you can cd +# into them and run 'make' without going through this Makefile. +# To change the values of 'make' variables: instead of editing Makefiles, +# (1) if the variable is set in 'config.status', edit 'config.status' +# (which will cause the Makefiles to be regenerated when you run 'make'); +# (2) otherwise, pass the desired values on the 'make' command line. +$(am__recursive_targets): + @fail=; \ + if $(am__make_keepgoing); then \ + failcom='fail=yes'; \ + else \ + failcom='exit 1'; \ + fi; \ + dot_seen=no; \ + target=`echo $@ | sed s/-recursive//`; \ + case "$@" in \ + distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ + *) list='$(SUBDIRS)' ;; \ + esac; \ + for subdir in $$list; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + dot_seen=yes; \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || eval $$failcom; \ + done; \ + if test "$$dot_seen" = "no"; then \ + $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ + fi; test -z "$$fail" + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-recursive +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \ + include_option=--etags-include; \ + empty_fix=.; \ + else \ + include_option=--include; \ + empty_fix=; \ + fi; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + test ! -f $$subdir/TAGS || \ + set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \ + fi; \ + done; \ + $(am__define_uniq_tagged_files); \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: ctags-recursive + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" +cscopelist: cscopelist-recursive + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +# Recover from deleted '.trs' file; this should ensure that +# "rm -f foo.log; make foo.trs" re-run 'foo.test', and re-create +# both 'foo.log' and 'foo.trs'. Break the recipe in two subshells +# to avoid problems with "make -n". +.log.trs: + rm -f $< $@ + $(MAKE) $(AM_MAKEFLAGS) $< + +# Leading 'am--fnord' is there to ensure the list of targets does not +# expand to empty, as could happen e.g. with make check TESTS=''. +am--fnord $(TEST_LOGS) $(TEST_LOGS:.log=.trs): $(am__force_recheck) +am--force-recheck: + @: + +$(TEST_SUITE_LOG): $(TEST_LOGS) + @$(am__set_TESTS_bases); \ + am__f_ok () { test -f "$$1" && test -r "$$1"; }; \ + redo_bases=`for i in $$bases; do \ + am__f_ok $$i.trs && am__f_ok $$i.log || echo $$i; \ + done`; \ + if test -n "$$redo_bases"; then \ + redo_logs=`for i in $$redo_bases; do echo $$i.log; done`; \ + redo_results=`for i in $$redo_bases; do echo $$i.trs; done`; \ + if $(am__make_dryrun); then :; else \ + rm -f $$redo_logs && rm -f $$redo_results || exit 1; \ + fi; \ + fi; \ + if test -n "$$am__remaking_logs"; then \ + echo "fatal: making $(TEST_SUITE_LOG): possible infinite" \ + "recursion detected" >&2; \ + else \ + am__remaking_logs=yes $(MAKE) $(AM_MAKEFLAGS) $$redo_logs; \ + fi; \ + if $(am__make_dryrun); then :; else \ + st=0; \ + errmsg="fatal: making $(TEST_SUITE_LOG): failed to create"; \ + for i in $$redo_bases; do \ + test -f $$i.trs && test -r $$i.trs \ + || { echo "$$errmsg $$i.trs" >&2; st=1; }; \ + test -f $$i.log && test -r $$i.log \ + || { echo "$$errmsg $$i.log" >&2; st=1; }; \ + done; \ + test $$st -eq 0 || exit 1; \ + fi + @$(am__sh_e_setup); $(am__tty_colors); $(am__set_TESTS_bases); \ + ws='[ ]'; \ + results=`for b in $$bases; do echo $$b.trs; done`; \ + test -n "$$results" || results=/dev/null; \ + all=` grep "^$$ws*:test-result:" $$results | wc -l`; \ + pass=` grep "^$$ws*:test-result:$$ws*PASS" $$results | wc -l`; \ + fail=` grep "^$$ws*:test-result:$$ws*FAIL" $$results | wc -l`; \ + skip=` grep "^$$ws*:test-result:$$ws*SKIP" $$results | wc -l`; \ + xfail=`grep "^$$ws*:test-result:$$ws*XFAIL" $$results | wc -l`; \ + xpass=`grep "^$$ws*:test-result:$$ws*XPASS" $$results | wc -l`; \ + error=`grep "^$$ws*:test-result:$$ws*ERROR" $$results | wc -l`; \ + if test `expr $$fail + $$xpass + $$error` -eq 0; then \ + success=true; \ + else \ + success=false; \ + fi; \ + br='==================='; br=$$br$$br$$br$$br; \ + result_count () \ + { \ + if test x"$$1" = x"--maybe-color"; then \ + maybe_colorize=yes; \ + elif test x"$$1" = x"--no-color"; then \ + maybe_colorize=no; \ + else \ + echo "$@: invalid 'result_count' usage" >&2; exit 4; \ + fi; \ + shift; \ + desc=$$1 count=$$2; \ + if test $$maybe_colorize = yes && test $$count -gt 0; then \ + color_start=$$3 color_end=$$std; \ + else \ + color_start= color_end=; \ + fi; \ + echo "$${color_start}# $$desc $$count$${color_end}"; \ + }; \ + create_testsuite_report () \ + { \ + result_count $$1 "TOTAL:" $$all "$$brg"; \ + result_count $$1 "PASS: " $$pass "$$grn"; \ + result_count $$1 "SKIP: " $$skip "$$blu"; \ + result_count $$1 "XFAIL:" $$xfail "$$lgn"; \ + result_count $$1 "FAIL: " $$fail "$$red"; \ + result_count $$1 "XPASS:" $$xpass "$$red"; \ + result_count $$1 "ERROR:" $$error "$$mgn"; \ + }; \ + { \ + echo "$(PACKAGE_STRING): $(subdir)/$(TEST_SUITE_LOG)" | \ + $(am__rst_title); \ + create_testsuite_report --no-color; \ + echo; \ + echo ".. contents:: :depth: 2"; \ + echo; \ + for b in $$bases; do echo $$b; done \ + | $(am__create_global_log); \ + } >$(TEST_SUITE_LOG).tmp || exit 1; \ + mv $(TEST_SUITE_LOG).tmp $(TEST_SUITE_LOG); \ + if $$success; then \ + col="$$grn"; \ + else \ + col="$$red"; \ + test x"$$VERBOSE" = x || cat $(TEST_SUITE_LOG); \ + fi; \ + echo "$${col}$$br$${std}"; \ + echo "$${col}Testsuite summary for $(PACKAGE_STRING)$${std}"; \ + echo "$${col}$$br$${std}"; \ + create_testsuite_report --maybe-color; \ + echo "$$col$$br$$std"; \ + if $$success; then :; else \ + echo "$${col}See $(subdir)/$(TEST_SUITE_LOG)$${std}"; \ + if test -n "$(PACKAGE_BUGREPORT)"; then \ + echo "$${col}Please report to $(PACKAGE_BUGREPORT)$${std}"; \ + fi; \ + echo "$$col$$br$$std"; \ + fi; \ + $$success || exit 1 + +check-TESTS: + @list='$(RECHECK_LOGS)'; test -z "$$list" || rm -f $$list + @list='$(RECHECK_LOGS:.log=.trs)'; test -z "$$list" || rm -f $$list + @test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG) + @set +e; $(am__set_TESTS_bases); \ + log_list=`for i in $$bases; do echo $$i.log; done`; \ + trs_list=`for i in $$bases; do echo $$i.trs; done`; \ + log_list=`echo $$log_list`; trs_list=`echo $$trs_list`; \ + $(MAKE) $(AM_MAKEFLAGS) $(TEST_SUITE_LOG) TEST_LOGS="$$log_list"; \ + exit $$?; +recheck: all $(check_PROGRAMS) + @test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG) + @set +e; $(am__set_TESTS_bases); \ + bases=`for i in $$bases; do echo $$i; done \ + | $(am__list_recheck_tests)` || exit 1; \ + log_list=`for i in $$bases; do echo $$i.log; done`; \ + log_list=`echo $$log_list`; \ + $(MAKE) $(AM_MAKEFLAGS) $(TEST_SUITE_LOG) \ + am__force_recheck=am--force-recheck \ + TEST_LOGS="$$log_list"; \ + exit $$? +main.log: main$(EXEEXT) + @p='main$(EXEEXT)'; \ + b='main'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +failmalloc.log: failmalloc$(EXEEXT) + @p='failmalloc$(EXEEXT)'; \ + b='failmalloc'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +.test.log: + @p='$<'; \ + $(am__set_b); \ + $(am__check_pre) $(TEST_LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_TEST_LOG_DRIVER_FLAGS) $(TEST_LOG_DRIVER_FLAGS) -- $(TEST_LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +@am__EXEEXT_TRUE@.test$(EXEEXT).log: +@am__EXEEXT_TRUE@ @p='$<'; \ +@am__EXEEXT_TRUE@ $(am__set_b); \ +@am__EXEEXT_TRUE@ $(am__check_pre) $(TEST_LOG_DRIVER) --test-name "$$f" \ +@am__EXEEXT_TRUE@ --log-file $$b.log --trs-file $$b.trs \ +@am__EXEEXT_TRUE@ $(am__common_driver_flags) $(AM_TEST_LOG_DRIVER_FLAGS) $(TEST_LOG_DRIVER_FLAGS) -- $(TEST_LOG_COMPILE) \ +@am__EXEEXT_TRUE@ "$$tst" $(AM_TESTS_FD_REDIRECT) + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done + @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + $(am__make_dryrun) \ + || test -d "$(distdir)/$$subdir" \ + || $(MKDIR_P) "$(distdir)/$$subdir" \ + || exit 1; \ + dir1=$$subdir; dir2="$(distdir)/$$subdir"; \ + $(am__relativize); \ + new_distdir=$$reldir; \ + dir1=$$subdir; dir2="$(top_distdir)"; \ + $(am__relativize); \ + new_top_distdir=$$reldir; \ + echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \ + echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \ + ($(am__cd) $$subdir && \ + $(MAKE) $(AM_MAKEFLAGS) \ + top_distdir="$$new_top_distdir" \ + distdir="$$new_distdir" \ + am__remove_distdir=: \ + am__skip_length_check=: \ + am__skip_mode_fix=: \ + distdir) \ + || exit 1; \ + fi; \ + done +check-am: all-am + $(MAKE) $(AM_MAKEFLAGS) $(check_PROGRAMS) + $(MAKE) $(AM_MAKEFLAGS) check-TESTS +check: check-recursive +all-am: Makefile +installdirs: installdirs-recursive +installdirs-am: +install: install-recursive +install-exec: install-exec-recursive +install-data: install-data-recursive +uninstall: uninstall-recursive + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-recursive +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + -test -z "$(TEST_LOGS)" || rm -f $(TEST_LOGS) + -test -z "$(TEST_LOGS:.log=.trs)" || rm -f $(TEST_LOGS:.log=.trs) + -test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG) + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-recursive + +clean-am: clean-checkPROGRAMS clean-generic clean-libtool \ + mostlyclean-am + +distclean: distclean-recursive + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-recursive + +dvi-am: + +html: html-recursive + +html-am: + +info: info-recursive + +info-am: + +install-data-am: + +install-dvi: install-dvi-recursive + +install-dvi-am: + +install-exec-am: + +install-html: install-html-recursive + +install-html-am: + +install-info: install-info-recursive + +install-info-am: + +install-man: + +install-pdf: install-pdf-recursive + +install-pdf-am: + +install-ps: install-ps-recursive + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-recursive + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-recursive + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-recursive + +pdf-am: + +ps: ps-recursive + +ps-am: + +uninstall-am: + +.MAKE: $(am__recursive_targets) check-am install-am install-strip + +.PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am check \ + check-TESTS check-am clean clean-checkPROGRAMS clean-generic \ + clean-libtool cscopelist-am ctags ctags-am distclean \ + distclean-compile distclean-generic distclean-libtool \ + distclean-tags distdir dvi dvi-am html html-am info info-am \ + install install-am install-data install-data-am install-dvi \ + install-dvi-am install-exec install-exec-am install-html \ + install-html-am install-info install-info-am install-man \ + install-pdf install-pdf-am install-ps install-ps-am \ + install-strip installcheck installcheck-am installdirs \ + installdirs-am maintainer-clean maintainer-clean-generic \ + mostlyclean mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool pdf pdf-am ps ps-am recheck tags tags-am \ + uninstall uninstall-am + + +# EXTRA_DIST = end_to_end.py +# TESTS += end_to_end.py + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/tests/failmalloc.c b/tests/failmalloc.c new file mode 100644 index 0000000..8c9f16d --- /dev/null +++ b/tests/failmalloc.c @@ -0,0 +1,77 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifdef HAVE_CONFIG_H +#include +#endif /* HAVE_CONFIG_H */ + +#include +#include +#include +/* include test cases' include files here */ + +#include "failmalloc_test.h" + +static int init_suite1(void) { return 0; } + +static int clean_suite1(void) { return 0; } + +int main(int argc _U_, char *argv[] _U_) { + CU_pSuite pSuite = NULL; + unsigned int num_tests_failed; + + /* initialize the CUnit test registry */ + if (CUE_SUCCESS != CU_initialize_registry()) + return CU_get_error(); + + /* add a suite to the registry */ + pSuite = CU_add_suite("libnghttp2_TestSuite", init_suite1, clean_suite1); + if (NULL == pSuite) { + CU_cleanup_registry(); + return CU_get_error(); + } + + /* add the tests to the suite */ + if (!CU_add_test(pSuite, "failmalloc_session_send", + test_nghttp2_session_send) || + !CU_add_test(pSuite, "failmalloc_session_recv", + test_nghttp2_session_recv) || + !CU_add_test(pSuite, "failmalloc_frame", test_nghttp2_frame) || + !CU_add_test(pSuite, "failmalloc_hd", test_nghttp2_hd)) { + CU_cleanup_registry(); + return CU_get_error(); + } + + /* Run all tests using the CUnit Basic interface */ + CU_basic_set_mode(CU_BRM_VERBOSE); + CU_basic_run_tests(); + num_tests_failed = CU_get_number_of_tests_failed(); + CU_cleanup_registry(); + if (CU_get_error() == CUE_SUCCESS) { + return num_tests_failed; + } else { + printf("CUnit Error: %s\n", CU_get_error_msg()); + return CU_get_error(); + } +} diff --git a/tests/failmalloc_test.c b/tests/failmalloc_test.c new file mode 100644 index 0000000..42062d3 --- /dev/null +++ b/tests/failmalloc_test.c @@ -0,0 +1,509 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012, 2014 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "failmalloc_test.h" + +#include + +#include +#include + +#include "nghttp2_session.h" +#include "nghttp2_stream.h" +#include "nghttp2_frame.h" +#include "nghttp2_helper.h" +#include "malloc_wrapper.h" +#include "nghttp2_test_helper.h" + +typedef struct { + uint8_t data[8192]; + uint8_t *datamark, *datalimit; +} data_feed; + +typedef struct { + data_feed *df; + size_t data_source_length; +} my_user_data; + +static void data_feed_init(data_feed *df, nghttp2_bufs *bufs) { + nghttp2_buf *buf; + size_t data_length; + + buf = &bufs->head->buf; + data_length = nghttp2_buf_len(buf); + + assert(data_length <= sizeof(df->data)); + memcpy(df->data, buf->pos, data_length); + df->datamark = df->data; + df->datalimit = df->data + data_length; +} + +static ssize_t null_send_callback(nghttp2_session *session _U_, + const uint8_t *data _U_, size_t len, + int flags _U_, void *user_data _U_) { + return len; +} + +static ssize_t data_feed_recv_callback(nghttp2_session *session _U_, + uint8_t *data, size_t len, int flags _U_, + void *user_data) { + data_feed *df = ((my_user_data *)user_data)->df; + size_t avail = df->datalimit - df->datamark; + size_t wlen = nghttp2_min(avail, len); + memcpy(data, df->datamark, wlen); + df->datamark += wlen; + return wlen; +} + +static ssize_t fixed_length_data_source_read_callback( + nghttp2_session *session _U_, int32_t stream_id _U_, uint8_t *buf _U_, + size_t len, uint32_t *data_flags, nghttp2_data_source *source _U_, + void *user_data) { + my_user_data *ud = (my_user_data *)user_data; + size_t wlen; + if (len < ud->data_source_length) { + wlen = len; + } else { + wlen = ud->data_source_length; + } + ud->data_source_length -= wlen; + if (ud->data_source_length == 0) { + *data_flags = NGHTTP2_DATA_FLAG_EOF; + } + return wlen; +} + +#define TEST_FAILMALLOC_RUN(FUN) \ + do { \ + int nmalloc, i; \ + \ + nghttp2_failmalloc = 0; \ + nghttp2_nmalloc = 0; \ + FUN(); \ + nmalloc = nghttp2_nmalloc; \ + \ + nghttp2_failmalloc = 1; \ + for (i = 0; i < nmalloc; ++i) { \ + nghttp2_nmalloc = 0; \ + nghttp2_failstart = i; \ + /* printf("i=%zu\n", i); */ \ + FUN(); \ + /* printf("nmalloc=%d\n", nghttp2_nmalloc); */ \ + } \ + nghttp2_failmalloc = 0; \ + } while (0) + +static void run_nghttp2_session_send(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + nghttp2_nv nv[] = {MAKE_NV(":host", "example.org"), + MAKE_NV(":scheme", "https")}; + nghttp2_data_provider data_prd; + nghttp2_settings_entry iv[2]; + my_user_data ud; + int rv; + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.send_callback = null_send_callback; + + data_prd.read_callback = fixed_length_data_source_read_callback; + ud.data_source_length = 64 * 1024; + + iv[0].settings_id = NGHTTP2_SETTINGS_HEADER_TABLE_SIZE; + iv[0].value = 4096; + iv[1].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS; + iv[1].value = 100; + + rv = nghttp2_session_client_new3(&session, &callbacks, &ud, NULL, + nghttp2_mem_fm()); + if (rv != 0) { + goto client_new_fail; + } + rv = nghttp2_submit_request(session, NULL, nv, ARRLEN(nv), &data_prd, NULL); + if (rv < 0) { + goto fail; + } + rv = nghttp2_submit_headers(session, NGHTTP2_FLAG_NONE, -1, NULL, nv, + ARRLEN(nv), NULL); + if (rv < 0) { + goto fail; + } + rv = nghttp2_session_send(session); + if (rv != 0) { + goto fail; + } + /* The HEADERS submitted by the previous nghttp2_submit_headers will + have stream ID 3. Send HEADERS to that stream. */ + rv = nghttp2_submit_headers(session, NGHTTP2_FLAG_NONE, 3, NULL, nv, + ARRLEN(nv), NULL); + if (rv != 0) { + goto fail; + } + rv = nghttp2_submit_data(session, NGHTTP2_FLAG_END_STREAM, 3, &data_prd); + if (rv != 0) { + goto fail; + } + rv = nghttp2_session_send(session); + if (rv != 0) { + goto fail; + } + rv = nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, 3, NGHTTP2_CANCEL); + if (rv != 0) { + goto fail; + } + rv = nghttp2_session_send(session); + if (rv != 0) { + goto fail; + } + /* Sending against half-closed stream */ + rv = nghttp2_submit_headers(session, NGHTTP2_FLAG_NONE, 3, NULL, nv, + ARRLEN(nv), NULL); + if (rv != 0) { + goto fail; + } + rv = nghttp2_submit_data(session, NGHTTP2_FLAG_END_STREAM, 3, &data_prd); + if (rv != 0) { + goto fail; + } + rv = nghttp2_submit_ping(session, NGHTTP2_FLAG_NONE, NULL); + if (rv != 0) { + goto fail; + } + rv = nghttp2_submit_settings(session, NGHTTP2_FLAG_NONE, iv, 2); + if (rv != 0) { + goto fail; + } + rv = nghttp2_session_send(session); + if (rv != 0) { + goto fail; + } + rv = nghttp2_submit_goaway(session, NGHTTP2_FLAG_NONE, 100, NGHTTP2_NO_ERROR, + NULL, 0); + if (rv != 0) { + goto fail; + } + rv = nghttp2_session_send(session); + if (rv != 0) { + goto fail; + } + +fail: + nghttp2_session_del(session); +client_new_fail: + ; +} + +void test_nghttp2_session_send(void) { + TEST_FAILMALLOC_RUN(run_nghttp2_session_send); +} + +static void run_nghttp2_session_recv(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + nghttp2_hd_deflater deflater; + nghttp2_frame frame; + nghttp2_bufs bufs; + nghttp2_nv nv[] = {MAKE_NV(":authority", "example.org"), + MAKE_NV(":scheme", "https")}; + nghttp2_settings_entry iv[2]; + my_user_data ud; + data_feed df; + int rv; + nghttp2_nv *nva; + ssize_t nvlen; + + rv = frame_pack_bufs_init(&bufs); + + if (rv != 0) { + return; + } + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.recv_callback = data_feed_recv_callback; + ud.df = &df; + + nghttp2_failmalloc_pause(); + nvlen = ARRLEN(nv); + nghttp2_nv_array_copy(&nva, nv, nvlen, nghttp2_mem_fm()); + nghttp2_hd_deflate_init(&deflater, nghttp2_mem_fm()); + nghttp2_session_server_new3(&session, &callbacks, &ud, NULL, + nghttp2_mem_fm()); + nghttp2_failmalloc_unpause(); + + /* HEADERS */ + nghttp2_failmalloc_pause(); + nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_END_STREAM, 1, + NGHTTP2_HCAT_REQUEST, NULL, nva, nvlen); + nghttp2_frame_pack_headers(&bufs, &frame.headers, &deflater); + nghttp2_frame_headers_free(&frame.headers, nghttp2_mem_fm()); + data_feed_init(&df, &bufs); + nghttp2_bufs_reset(&bufs); + + nghttp2_failmalloc_unpause(); + + rv = nghttp2_session_recv(session); + if (rv != 0) { + goto fail; + } + + /* PING */ + nghttp2_failmalloc_pause(); + nghttp2_frame_ping_init(&frame.ping, NGHTTP2_FLAG_NONE, NULL); + nghttp2_frame_pack_ping(&bufs, &frame.ping); + nghttp2_frame_ping_free(&frame.ping); + data_feed_init(&df, &bufs); + nghttp2_bufs_reset(&bufs); + + nghttp2_failmalloc_unpause(); + + rv = nghttp2_session_recv(session); + if (rv != 0) { + goto fail; + } + + /* RST_STREAM */ + nghttp2_failmalloc_pause(); + nghttp2_frame_rst_stream_init(&frame.rst_stream, 1, NGHTTP2_PROTOCOL_ERROR); + nghttp2_frame_pack_rst_stream(&bufs, &frame.rst_stream); + nghttp2_frame_rst_stream_free(&frame.rst_stream); + nghttp2_bufs_reset(&bufs); + + nghttp2_failmalloc_unpause(); + + rv = nghttp2_session_recv(session); + if (rv != 0) { + goto fail; + } + + /* SETTINGS */ + nghttp2_failmalloc_pause(); + iv[0].settings_id = NGHTTP2_SETTINGS_HEADER_TABLE_SIZE; + iv[0].value = 4096; + iv[1].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS; + iv[1].value = 100; + nghttp2_frame_settings_init(&frame.settings, NGHTTP2_FLAG_NONE, + nghttp2_frame_iv_copy(iv, 2, nghttp2_mem_fm()), + 2); + nghttp2_frame_pack_settings(&bufs, &frame.settings); + nghttp2_frame_settings_free(&frame.settings, nghttp2_mem_fm()); + nghttp2_bufs_reset(&bufs); + + nghttp2_failmalloc_unpause(); + + rv = nghttp2_session_recv(session); + if (rv != 0) { + goto fail; + } + +fail: + nghttp2_bufs_free(&bufs); + nghttp2_session_del(session); + nghttp2_hd_deflate_free(&deflater); +} + +void test_nghttp2_session_recv(void) { + TEST_FAILMALLOC_RUN(run_nghttp2_session_recv); +} + +static void run_nghttp2_frame_pack_headers(void) { + nghttp2_hd_deflater deflater; + nghttp2_hd_inflater inflater; + nghttp2_frame frame, oframe; + nghttp2_bufs bufs; + nghttp2_nv nv[] = {MAKE_NV(":host", "example.org"), + MAKE_NV(":scheme", "https")}; + int rv; + nghttp2_nv *nva; + ssize_t nvlen; + + rv = frame_pack_bufs_init(&bufs); + + if (rv != 0) { + return; + } + + rv = nghttp2_hd_deflate_init(&deflater, nghttp2_mem_fm()); + if (rv != 0) { + goto deflate_init_fail; + } + rv = nghttp2_hd_inflate_init(&inflater, nghttp2_mem_fm()); + if (rv != 0) { + goto inflate_init_fail; + } + nvlen = ARRLEN(nv); + rv = nghttp2_nv_array_copy(&nva, nv, nvlen, nghttp2_mem_fm()); + if (rv < 0) { + goto nv_copy_fail; + } + nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_END_STREAM, 1, + NGHTTP2_HCAT_REQUEST, NULL, nva, nvlen); + rv = nghttp2_frame_pack_headers(&bufs, &frame.headers, &deflater); + if (rv != 0) { + goto fail; + } + rv = unpack_framebuf(&oframe, &bufs); + if (rv != 0) { + goto fail; + } + nghttp2_frame_headers_free(&oframe.headers, nghttp2_mem_fm()); + +fail: + nghttp2_frame_headers_free(&frame.headers, nghttp2_mem_fm()); +nv_copy_fail: + nghttp2_hd_inflate_free(&inflater); +inflate_init_fail: + nghttp2_hd_deflate_free(&deflater); +deflate_init_fail: + nghttp2_bufs_free(&bufs); +} + +static void run_nghttp2_frame_pack_settings(void) { + nghttp2_frame frame, oframe; + nghttp2_bufs bufs; + nghttp2_buf *buf; + nghttp2_settings_entry iv[2], *iv_copy; + int rv; + + rv = frame_pack_bufs_init(&bufs); + + if (rv != 0) { + return; + } + + iv[0].settings_id = NGHTTP2_SETTINGS_HEADER_TABLE_SIZE; + iv[0].value = 4096; + iv[1].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS; + iv[1].value = 100; + + iv_copy = nghttp2_frame_iv_copy(iv, 2, nghttp2_mem_fm()); + + if (iv_copy == NULL) { + goto iv_copy_fail; + } + + nghttp2_frame_settings_init(&frame.settings, NGHTTP2_FLAG_NONE, iv_copy, 2); + + rv = nghttp2_frame_pack_settings(&bufs, &frame.settings); + + if (rv != 0) { + goto fail; + } + + buf = &bufs.head->buf; + + rv = nghttp2_frame_unpack_settings_payload2( + &oframe.settings.iv, &oframe.settings.niv, buf->pos + NGHTTP2_FRAME_HDLEN, + nghttp2_buf_len(buf) - NGHTTP2_FRAME_HDLEN, nghttp2_mem_fm()); + + if (rv != 0) { + goto fail; + } + nghttp2_frame_settings_free(&oframe.settings, nghttp2_mem_fm()); + +fail: + nghttp2_frame_settings_free(&frame.settings, nghttp2_mem_fm()); +iv_copy_fail: + nghttp2_bufs_free(&bufs); +} + +void test_nghttp2_frame(void) { + TEST_FAILMALLOC_RUN(run_nghttp2_frame_pack_headers); + TEST_FAILMALLOC_RUN(run_nghttp2_frame_pack_settings); +} + +static int deflate_inflate(nghttp2_hd_deflater *deflater, + nghttp2_hd_inflater *inflater, nghttp2_bufs *bufs, + nghttp2_nv *nva, size_t nvlen, nghttp2_mem *mem) { + int rv; + + rv = nghttp2_hd_deflate_hd_bufs(deflater, bufs, nva, nvlen); + + if (rv != 0) { + return rv; + } + + rv = (int)inflate_hd(inflater, NULL, bufs, 0, mem); + + if (rv < 0) { + return rv; + } + + nghttp2_bufs_reset(bufs); + + return 0; +} + +static void run_nghttp2_hd(void) { + nghttp2_hd_deflater deflater; + nghttp2_hd_inflater inflater; + nghttp2_bufs bufs; + int rv; + nghttp2_nv nva1[] = {MAKE_NV(":scheme", "https"), + MAKE_NV(":authority", "example.org"), + MAKE_NV(":path", "/slashdot"), + MAKE_NV("accept-encoding", "gzip, deflate")}; + nghttp2_nv nva2[] = { + MAKE_NV(":scheme", "https"), MAKE_NV(":authority", "example.org"), + MAKE_NV(":path", "/style.css"), MAKE_NV("cookie", "nghttp2=FTW")}; + + rv = frame_pack_bufs_init(&bufs); + + if (rv != 0) { + return; + } + + rv = nghttp2_hd_deflate_init(&deflater, nghttp2_mem_fm()); + + if (rv != 0) { + goto deflate_init_fail; + } + + rv = nghttp2_hd_inflate_init(&inflater, nghttp2_mem_fm()); + + if (rv != 0) { + goto inflate_init_fail; + } + + rv = deflate_inflate(&deflater, &inflater, &bufs, nva1, ARRLEN(nva1), + nghttp2_mem_fm()); + + if (rv != 0) { + goto deflate_hd_fail; + } + + rv = deflate_inflate(&deflater, &inflater, &bufs, nva2, ARRLEN(nva2), + nghttp2_mem_fm()); + + if (rv != 0) { + goto deflate_hd_fail; + } + +deflate_hd_fail: + nghttp2_hd_inflate_free(&inflater); +inflate_init_fail: + nghttp2_hd_deflate_free(&deflater); +deflate_init_fail: + nghttp2_bufs_free(&bufs); +} + +void test_nghttp2_hd(void) { TEST_FAILMALLOC_RUN(run_nghttp2_hd); } diff --git a/tests/failmalloc_test.h b/tests/failmalloc_test.h new file mode 100644 index 0000000..1969bc1 --- /dev/null +++ b/tests/failmalloc_test.h @@ -0,0 +1,33 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef FAILMALLOC_TEST_H +#define FAILMALLOC_TEST_H + +void test_nghttp2_session_send(void); +void test_nghttp2_session_recv(void); +void test_nghttp2_frame(void); +void test_nghttp2_hd(void); + +#endif /* FAILMALLOC_TEST_H */ diff --git a/tests/main.c b/tests/main.c new file mode 100644 index 0000000..1d187f7 --- /dev/null +++ b/tests/main.c @@ -0,0 +1,368 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifdef HAVE_CONFIG_H +#include +#endif /* HAVE_CONFIG_H */ + +#include +#include +#include +/* include test cases' include files here */ +#include "nghttp2_pq_test.h" +#include "nghttp2_map_test.h" +#include "nghttp2_queue_test.h" +#include "nghttp2_session_test.h" +#include "nghttp2_frame_test.h" +#include "nghttp2_stream_test.h" +#include "nghttp2_hd_test.h" +#include "nghttp2_npn_test.h" +#include "nghttp2_helper_test.h" +#include "nghttp2_buf_test.h" + +extern int nghttp2_enable_strict_preface; + +static int init_suite1(void) { return 0; } + +static int clean_suite1(void) { return 0; } + +int main(int argc _U_, char *argv[] _U_) { + CU_pSuite pSuite = NULL; + unsigned int num_tests_failed; + + nghttp2_enable_strict_preface = 0; + + /* initialize the CUnit test registry */ + if (CUE_SUCCESS != CU_initialize_registry()) + return CU_get_error(); + + /* add a suite to the registry */ + pSuite = CU_add_suite("libnghttp2_TestSuite", init_suite1, clean_suite1); + if (NULL == pSuite) { + CU_cleanup_registry(); + return CU_get_error(); + } + + /* add the tests to the suite */ + if (!CU_add_test(pSuite, "pq", test_nghttp2_pq) || + !CU_add_test(pSuite, "pq_update", test_nghttp2_pq_update) || + !CU_add_test(pSuite, "map", test_nghttp2_map) || + !CU_add_test(pSuite, "map_functional", test_nghttp2_map_functional) || + !CU_add_test(pSuite, "map_each_free", test_nghttp2_map_each_free) || + !CU_add_test(pSuite, "queue", test_nghttp2_queue) || + !CU_add_test(pSuite, "npn", test_nghttp2_npn) || + !CU_add_test(pSuite, "session_recv", test_nghttp2_session_recv) || + !CU_add_test(pSuite, "session_recv_invalid_stream_id", + test_nghttp2_session_recv_invalid_stream_id) || + !CU_add_test(pSuite, "session_recv_invalid_frame", + test_nghttp2_session_recv_invalid_frame) || + !CU_add_test(pSuite, "session_recv_eof", test_nghttp2_session_recv_eof) || + !CU_add_test(pSuite, "session_recv_data", + test_nghttp2_session_recv_data) || + !CU_add_test(pSuite, "session_recv_continuation", + test_nghttp2_session_recv_continuation) || + !CU_add_test(pSuite, "session_recv_headers_with_priority", + test_nghttp2_session_recv_headers_with_priority) || + !CU_add_test(pSuite, "session_recv_premature_headers", + test_nghttp2_session_recv_premature_headers) || + !CU_add_test(pSuite, "session_recv_unknown_frame", + test_nghttp2_session_recv_unknown_frame) || + !CU_add_test(pSuite, "session_recv_unexpected_continuation", + test_nghttp2_session_recv_unexpected_continuation) || + !CU_add_test(pSuite, "session_recv_settings_header_table_size", + test_nghttp2_session_recv_settings_header_table_size) || + !CU_add_test(pSuite, "session_recv_too_large_frame_length", + test_nghttp2_session_recv_too_large_frame_length) || + !CU_add_test(pSuite, "session_continue", test_nghttp2_session_continue) || + !CU_add_test(pSuite, "session_add_frame", + test_nghttp2_session_add_frame) || + !CU_add_test(pSuite, "session_on_request_headers_received", + test_nghttp2_session_on_request_headers_received) || + !CU_add_test(pSuite, "session_on_response_headers_received", + test_nghttp2_session_on_response_headers_received) || + !CU_add_test(pSuite, "session_on_headers_received", + test_nghttp2_session_on_headers_received) || + !CU_add_test(pSuite, "session_on_push_response_headers_received", + test_nghttp2_session_on_push_response_headers_received) || + !CU_add_test(pSuite, "session_on_priority_received", + test_nghttp2_session_on_priority_received) || + !CU_add_test(pSuite, "session_on_rst_stream_received", + test_nghttp2_session_on_rst_stream_received) || + !CU_add_test(pSuite, "session_on_settings_received", + test_nghttp2_session_on_settings_received) || + !CU_add_test(pSuite, "session_on_push_promise_received", + test_nghttp2_session_on_push_promise_received) || + !CU_add_test(pSuite, "session_on_ping_received", + test_nghttp2_session_on_ping_received) || + !CU_add_test(pSuite, "session_on_goaway_received", + test_nghttp2_session_on_goaway_received) || + !CU_add_test(pSuite, "session_on_window_update_received", + test_nghttp2_session_on_window_update_received) || + !CU_add_test(pSuite, "session_on_data_received", + test_nghttp2_session_on_data_received) || + !CU_add_test(pSuite, "session_send_headers_start_stream", + test_nghttp2_session_send_headers_start_stream) || + !CU_add_test(pSuite, "session_send_headers_reply", + test_nghttp2_session_send_headers_reply) || + !CU_add_test(pSuite, "session_send_headers_frame_size_error", + test_nghttp2_session_send_headers_frame_size_error) || + !CU_add_test(pSuite, "session_send_headers_push_reply", + test_nghttp2_session_send_headers_push_reply) || + !CU_add_test(pSuite, "session_send_rst_stream", + test_nghttp2_session_send_rst_stream) || + !CU_add_test(pSuite, "session_send_push_promise", + test_nghttp2_session_send_push_promise) || + !CU_add_test(pSuite, "session_is_my_stream_id", + test_nghttp2_session_is_my_stream_id) || + !CU_add_test(pSuite, "session_upgrade", test_nghttp2_session_upgrade) || + !CU_add_test(pSuite, "session_reprioritize_stream", + test_nghttp2_session_reprioritize_stream) || + !CU_add_test( + pSuite, "session_reprioritize_stream_with_idle_stream_dep", + test_nghttp2_session_reprioritize_stream_with_idle_stream_dep) || + !CU_add_test(pSuite, "submit_data", test_nghttp2_submit_data) || + !CU_add_test(pSuite, "submit_data_read_length_too_large", + test_nghttp2_submit_data_read_length_too_large) || + !CU_add_test(pSuite, "submit_data_twice", + test_nghttp2_submit_data_twice) || + !CU_add_test(pSuite, "submit_request_with_data", + test_nghttp2_submit_request_with_data) || + !CU_add_test(pSuite, "submit_request_without_data", + test_nghttp2_submit_request_without_data) || + !CU_add_test(pSuite, "submit_response_with_data", + test_nghttp2_submit_response_with_data) || + !CU_add_test(pSuite, "submit_response_without_data", + test_nghttp2_submit_response_without_data) || + !CU_add_test(pSuite, "submit_trailer", test_nghttp2_submit_trailer) || + !CU_add_test(pSuite, "submit_headers_start_stream", + test_nghttp2_submit_headers_start_stream) || + !CU_add_test(pSuite, "submit_headers_reply", + test_nghttp2_submit_headers_reply) || + !CU_add_test(pSuite, "submit_headers_push_reply", + test_nghttp2_submit_headers_push_reply) || + !CU_add_test(pSuite, "submit_headers", test_nghttp2_submit_headers) || + !CU_add_test(pSuite, "submit_headers_continuation", + test_nghttp2_submit_headers_continuation) || + !CU_add_test(pSuite, "submit_priority", test_nghttp2_submit_priority) || + !CU_add_test(pSuite, "session_submit_settings", + test_nghttp2_submit_settings) || + !CU_add_test(pSuite, "session_submit_settings_update_local_window_size", + test_nghttp2_submit_settings_update_local_window_size) || + !CU_add_test(pSuite, "session_submit_push_promise", + test_nghttp2_submit_push_promise) || + !CU_add_test(pSuite, "submit_window_update", + test_nghttp2_submit_window_update) || + !CU_add_test(pSuite, "submit_window_update_local_window_size", + test_nghttp2_submit_window_update_local_window_size) || + !CU_add_test(pSuite, "submit_shutdown_notice", + test_nghttp2_submit_shutdown_notice) || + !CU_add_test(pSuite, "submit_invalid_nv", + test_nghttp2_submit_invalid_nv) || + !CU_add_test(pSuite, "session_open_stream", + test_nghttp2_session_open_stream) || + !CU_add_test(pSuite, "session_open_stream_with_idle_stream_dep", + test_nghttp2_session_open_stream_with_idle_stream_dep) || + !CU_add_test(pSuite, "session_get_next_ob_item", + test_nghttp2_session_get_next_ob_item) || + !CU_add_test(pSuite, "session_pop_next_ob_item", + test_nghttp2_session_pop_next_ob_item) || + !CU_add_test(pSuite, "session_reply_fail", + test_nghttp2_session_reply_fail) || + !CU_add_test(pSuite, "session_max_concurrent_streams", + test_nghttp2_session_max_concurrent_streams) || + !CU_add_test(pSuite, "session_stream_close_on_headers_push", + test_nghttp2_session_stream_close_on_headers_push) || + !CU_add_test(pSuite, "session_stop_data_with_rst_stream", + test_nghttp2_session_stop_data_with_rst_stream) || + !CU_add_test(pSuite, "session_defer_data", + test_nghttp2_session_defer_data) || + !CU_add_test(pSuite, "session_flow_control", + test_nghttp2_session_flow_control) || + !CU_add_test(pSuite, "session_flow_control_data_recv", + test_nghttp2_session_flow_control_data_recv) || + !CU_add_test(pSuite, "session_flow_control_data_with_padding_recv", + test_nghttp2_session_flow_control_data_with_padding_recv) || + !CU_add_test(pSuite, "session_data_read_temporal_failure", + test_nghttp2_session_data_read_temporal_failure) || + !CU_add_test(pSuite, "session_on_stream_close", + test_nghttp2_session_on_stream_close) || + !CU_add_test(pSuite, "session_on_ctrl_not_send", + test_nghttp2_session_on_ctrl_not_send) || + !CU_add_test(pSuite, "session_get_outbound_queue_size", + test_nghttp2_session_get_outbound_queue_size) || + !CU_add_test(pSuite, "session_get_effective_local_window_size", + test_nghttp2_session_get_effective_local_window_size) || + !CU_add_test(pSuite, "session_set_option", + test_nghttp2_session_set_option) || + !CU_add_test(pSuite, "session_data_backoff_by_high_pri_frame", + test_nghttp2_session_data_backoff_by_high_pri_frame) || + !CU_add_test(pSuite, "session_pack_data_with_padding", + test_nghttp2_session_pack_data_with_padding) || + !CU_add_test(pSuite, "session_pack_headers_with_padding", + test_nghttp2_session_pack_headers_with_padding) || + !CU_add_test(pSuite, "pack_settings_payload", + test_nghttp2_pack_settings_payload) || + !CU_add_test(pSuite, "session_stream_dep_add", + test_nghttp2_session_stream_dep_add) || + !CU_add_test(pSuite, "session_stream_dep_remove", + test_nghttp2_session_stream_dep_remove) || + !CU_add_test(pSuite, "session_stream_dep_add_subtree", + test_nghttp2_session_stream_dep_add_subtree) || + !CU_add_test(pSuite, "session_stream_dep_remove_subtree", + test_nghttp2_session_stream_dep_remove_subtree) || + !CU_add_test( + pSuite, "session_stream_dep_all_your_stream_are_belong_to_us", + test_nghttp2_session_stream_dep_all_your_stream_are_belong_to_us) || + !CU_add_test(pSuite, "session_stream_attach_item", + test_nghttp2_session_stream_attach_item) || + !CU_add_test(pSuite, "session_stream_attach_item_subtree", + test_nghttp2_session_stream_attach_item_subtree) || + !CU_add_test(pSuite, "session_stream_keep_closed_stream", + test_nghttp2_session_keep_closed_stream) || + !CU_add_test(pSuite, "session_stream_keep_idle_stream", + test_nghttp2_session_keep_idle_stream) || + !CU_add_test(pSuite, "session_detach_idle_stream", + test_nghttp2_session_detach_idle_stream) || + !CU_add_test(pSuite, "session_large_dep_tree", + test_nghttp2_session_large_dep_tree) || + !CU_add_test(pSuite, "session_graceful_shutdown", + test_nghttp2_session_graceful_shutdown) || + !CU_add_test(pSuite, "session_on_header_temporal_failure", + test_nghttp2_session_on_header_temporal_failure) || + !CU_add_test(pSuite, "session_recv_client_magic", + test_nghttp2_session_recv_client_magic) || + !CU_add_test(pSuite, "session_delete_data_item", + test_nghttp2_session_delete_data_item) || + !CU_add_test(pSuite, "session_open_idle_stream", + test_nghttp2_session_open_idle_stream) || + !CU_add_test(pSuite, "session_cancel_reserved_remote", + test_nghttp2_session_cancel_reserved_remote) || + !CU_add_test(pSuite, "session_reset_pending_headers", + test_nghttp2_session_reset_pending_headers) || + !CU_add_test(pSuite, "session_send_data_callback", + test_nghttp2_session_send_data_callback) || + !CU_add_test(pSuite, "session_on_begin_headers_temporal_failure", + test_nghttp2_session_on_begin_headers_temporal_failure) || + !CU_add_test(pSuite, "http_mandatory_headers", + test_nghttp2_http_mandatory_headers) || + !CU_add_test(pSuite, "http_content_length", + test_nghttp2_http_content_length) || + !CU_add_test(pSuite, "http_content_length_mismatch", + test_nghttp2_http_content_length_mismatch) || + !CU_add_test(pSuite, "http_non_final_response", + test_nghttp2_http_non_final_response) || + !CU_add_test(pSuite, "http_trailer_headers", + test_nghttp2_http_trailer_headers) || + !CU_add_test(pSuite, "http_ignore_regular_header", + test_nghttp2_http_ignore_regular_header) || + !CU_add_test(pSuite, "http_ignore_content_length", + test_nghttp2_http_ignore_content_length) || + !CU_add_test(pSuite, "http_record_request_method", + test_nghttp2_http_record_request_method) || + !CU_add_test(pSuite, "http_push_promise", + test_nghttp2_http_push_promise) || + !CU_add_test(pSuite, "frame_pack_headers", + test_nghttp2_frame_pack_headers) || + !CU_add_test(pSuite, "frame_pack_headers_frame_too_large", + test_nghttp2_frame_pack_headers_frame_too_large) || + !CU_add_test(pSuite, "frame_pack_headers_frame_smallest", + test_nghttp2_submit_data_read_length_smallest) || + !CU_add_test(pSuite, "frame_pack_priority", + test_nghttp2_frame_pack_priority) || + !CU_add_test(pSuite, "frame_pack_rst_stream", + test_nghttp2_frame_pack_rst_stream) || + !CU_add_test(pSuite, "frame_pack_settings", + test_nghttp2_frame_pack_settings) || + !CU_add_test(pSuite, "frame_pack_push_promise", + test_nghttp2_frame_pack_push_promise) || + !CU_add_test(pSuite, "frame_pack_ping", test_nghttp2_frame_pack_ping) || + !CU_add_test(pSuite, "frame_pack_goaway", + test_nghttp2_frame_pack_goaway) || + !CU_add_test(pSuite, "frame_pack_window_update", + test_nghttp2_frame_pack_window_update) || + !CU_add_test(pSuite, "nv_array_copy", test_nghttp2_nv_array_copy) || + !CU_add_test(pSuite, "iv_check", test_nghttp2_iv_check) || + !CU_add_test(pSuite, "hd_deflate", test_nghttp2_hd_deflate) || + !CU_add_test(pSuite, "hd_deflate_same_indexed_repr", + test_nghttp2_hd_deflate_same_indexed_repr) || + !CU_add_test(pSuite, "hd_inflate_indexed", + test_nghttp2_hd_inflate_indexed) || + !CU_add_test(pSuite, "hd_inflate_indname_noinc", + test_nghttp2_hd_inflate_indname_noinc) || + !CU_add_test(pSuite, "hd_inflate_indname_inc", + test_nghttp2_hd_inflate_indname_inc) || + !CU_add_test(pSuite, "hd_inflate_indname_inc_eviction", + test_nghttp2_hd_inflate_indname_inc_eviction) || + !CU_add_test(pSuite, "hd_inflate_newname_noinc", + test_nghttp2_hd_inflate_newname_noinc) || + !CU_add_test(pSuite, "hd_inflate_newname_inc", + test_nghttp2_hd_inflate_newname_inc) || + !CU_add_test(pSuite, "hd_inflate_clearall_inc", + test_nghttp2_hd_inflate_clearall_inc) || + !CU_add_test(pSuite, "hd_inflate_zero_length_huffman", + test_nghttp2_hd_inflate_zero_length_huffman) || + !CU_add_test(pSuite, "hd_ringbuf_reserve", + test_nghttp2_hd_ringbuf_reserve) || + !CU_add_test(pSuite, "hd_change_table_size", + test_nghttp2_hd_change_table_size) || + !CU_add_test(pSuite, "hd_deflate_inflate", + test_nghttp2_hd_deflate_inflate) || + !CU_add_test(pSuite, "hd_no_index", test_nghttp2_hd_no_index) || + !CU_add_test(pSuite, "hd_deflate_bound", test_nghttp2_hd_deflate_bound) || + !CU_add_test(pSuite, "hd_public_api", test_nghttp2_hd_public_api) || + !CU_add_test(pSuite, "hd_decode_length", test_nghttp2_hd_decode_length) || + !CU_add_test(pSuite, "hd_huff_encode", test_nghttp2_hd_huff_encode) || + !CU_add_test(pSuite, "adjust_local_window_size", + test_nghttp2_adjust_local_window_size) || + !CU_add_test(pSuite, "check_header_name", + test_nghttp2_check_header_name) || + !CU_add_test(pSuite, "check_header_value", + test_nghttp2_check_header_value) || + !CU_add_test(pSuite, "bufs_add", test_nghttp2_bufs_add) || + !CU_add_test(pSuite, "bufs_add_stack_buffer_overflow_bug", + test_nghttp2_bufs_add_stack_buffer_overflow_bug) || + !CU_add_test(pSuite, "bufs_addb", test_nghttp2_bufs_addb) || + !CU_add_test(pSuite, "bufs_orb", test_nghttp2_bufs_orb) || + !CU_add_test(pSuite, "bufs_remove", test_nghttp2_bufs_remove) || + !CU_add_test(pSuite, "bufs_reset", test_nghttp2_bufs_reset) || + !CU_add_test(pSuite, "bufs_advance", test_nghttp2_bufs_advance) || + !CU_add_test(pSuite, "bufs_next_present", + test_nghttp2_bufs_next_present) || + !CU_add_test(pSuite, "bufs_realloc", test_nghttp2_bufs_realloc)) { + CU_cleanup_registry(); + return CU_get_error(); + } + + /* Run all tests using the CUnit Basic interface */ + CU_basic_set_mode(CU_BRM_VERBOSE); + CU_basic_run_tests(); + num_tests_failed = CU_get_number_of_tests_failed(); + CU_cleanup_registry(); + if (CU_get_error() == CUE_SUCCESS) { + return num_tests_failed; + } else { + printf("CUnit Error: %s\n", CU_get_error_msg()); + return CU_get_error(); + } +} diff --git a/tests/malloc_wrapper.c b/tests/malloc_wrapper.c new file mode 100644 index 0000000..6873bff --- /dev/null +++ b/tests/malloc_wrapper.c @@ -0,0 +1,75 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "malloc_wrapper.h" + +int nghttp2_failmalloc = 0; +int nghttp2_failstart = 0; +int nghttp2_countmalloc = 1; +int nghttp2_nmalloc = 0; + +#define CHECK_PREREQ \ + do { \ + if (nghttp2_failmalloc && nghttp2_nmalloc >= nghttp2_failstart) { \ + return NULL; \ + } \ + if (nghttp2_countmalloc) { \ + ++nghttp2_nmalloc; \ + } \ + } while (0) + +static void *my_malloc(size_t size, void *mud _U_) { + CHECK_PREREQ; + return malloc(size); +} + +static void my_free(void *ptr, void *mud _U_) { free(ptr); } + +static void *my_calloc(size_t nmemb, size_t size, void *mud _U_) { + CHECK_PREREQ; + return calloc(nmemb, size); +} + +static void *my_realloc(void *ptr, size_t size, void *mud _U_) { + CHECK_PREREQ; + return realloc(ptr, size); +} + +static nghttp2_mem mem = {NULL, my_malloc, my_free, my_calloc, my_realloc}; + +nghttp2_mem *nghttp2_mem_fm(void) { return &mem; } + +static int failmalloc_bk, countmalloc_bk; + +void nghttp2_failmalloc_pause(void) { + failmalloc_bk = nghttp2_failmalloc; + countmalloc_bk = nghttp2_countmalloc; + nghttp2_failmalloc = 0; + nghttp2_countmalloc = 0; +} + +void nghttp2_failmalloc_unpause(void) { + nghttp2_failmalloc = failmalloc_bk; + nghttp2_countmalloc = countmalloc_bk; +} diff --git a/tests/malloc_wrapper.h b/tests/malloc_wrapper.h new file mode 100644 index 0000000..5405d09 --- /dev/null +++ b/tests/malloc_wrapper.h @@ -0,0 +1,61 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef MALLOC_WRAPPER_H +#define MALLOC_WRAPPER_H + +#include + +#include "nghttp2_mem.h" + +/* Global variables to control the behavior of malloc() */ + +/* If nonzero, malloc failure mode is on */ +extern int nghttp2_failmalloc; +/* If nghttp2_failstart <= nghttp2_nmalloc and nghttp2_failmalloc is + nonzero, malloc() fails. */ +extern int nghttp2_failstart; +/* If nonzero, nghttp2_nmalloc is incremented if malloc() succeeds. */ +extern int nghttp2_countmalloc; +/* The number of successful invocation of malloc(). This value is only + incremented if nghttp2_nmalloc is nonzero. */ +extern int nghttp2_nmalloc; + +/* Returns pointer to nghttp2_mem, which, when dereferenced, contains + specifically instrumented memory allocators for failmalloc + tests. */ +nghttp2_mem *nghttp2_mem_fm(void); + +/* Copies nghttp2_failmalloc and nghttp2_countmalloc to statically + allocated space and sets 0 to them. This will effectively make + malloc() work like normal malloc(). This is useful when you want to + disable malloc() failure mode temporarily. */ +void nghttp2_failmalloc_pause(void); + +/* Restores the values of nghttp2_failmalloc and nghttp2_countmalloc + with the values saved by the previous + nghttp2_failmalloc_pause(). */ +void nghttp2_failmalloc_unpause(void); + +#endif /* MALLOC_WRAPPER_H */ diff --git a/tests/nghttp2_buf_test.c b/tests/nghttp2_buf_test.c new file mode 100644 index 0000000..8860554 --- /dev/null +++ b/tests/nghttp2_buf_test.c @@ -0,0 +1,342 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2014 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "nghttp2_buf_test.h" + +#include + +#include "nghttp2_buf.h" +#include "nghttp2_test_helper.h" + +void test_nghttp2_bufs_add(void) { + int rv; + nghttp2_bufs bufs; + uint8_t data[2048]; + nghttp2_mem *mem; + + mem = nghttp2_mem_default(); + + rv = nghttp2_bufs_init(&bufs, 1000, 3, mem); + CU_ASSERT(0 == rv); + + CU_ASSERT(bufs.cur->buf.pos == bufs.cur->buf.last); + + rv = nghttp2_bufs_add(&bufs, data, 493); + CU_ASSERT(0 == rv); + CU_ASSERT(493 == nghttp2_buf_len(&bufs.cur->buf)); + CU_ASSERT(493 == nghttp2_bufs_len(&bufs)); + CU_ASSERT(507 == nghttp2_bufs_cur_avail(&bufs)); + + rv = nghttp2_bufs_add(&bufs, data, 507); + CU_ASSERT(0 == rv); + CU_ASSERT(1000 == nghttp2_buf_len(&bufs.cur->buf)); + CU_ASSERT(1000 == nghttp2_bufs_len(&bufs)); + CU_ASSERT(bufs.cur == bufs.head); + + rv = nghttp2_bufs_add(&bufs, data, 1); + CU_ASSERT(0 == rv); + CU_ASSERT(1 == nghttp2_buf_len(&bufs.cur->buf)); + CU_ASSERT(1001 == nghttp2_bufs_len(&bufs)); + CU_ASSERT(bufs.cur == bufs.head->next); + + nghttp2_bufs_free(&bufs); +} + +/* Test for GH-232, stack-buffer-overflow */ +void test_nghttp2_bufs_add_stack_buffer_overflow_bug(void) { + int rv; + nghttp2_bufs bufs; + uint8_t data[1024]; + nghttp2_mem *mem; + + mem = nghttp2_mem_default(); + + rv = nghttp2_bufs_init(&bufs, 100, 200, mem); + CU_ASSERT(0 == rv); + + rv = nghttp2_bufs_add(&bufs, data, sizeof(data)); + + CU_ASSERT(0 == rv); + CU_ASSERT(sizeof(data) == nghttp2_bufs_len(&bufs)); + + nghttp2_bufs_free(&bufs); +} + +void test_nghttp2_bufs_addb(void) { + int rv; + nghttp2_bufs bufs; + ssize_t i; + nghttp2_mem *mem; + + mem = nghttp2_mem_default(); + + rv = nghttp2_bufs_init(&bufs, 1000, 3, mem); + CU_ASSERT(0 == rv); + + rv = nghttp2_bufs_addb(&bufs, 14); + CU_ASSERT(0 == rv); + CU_ASSERT(1 == nghttp2_buf_len(&bufs.cur->buf)); + CU_ASSERT(1 == nghttp2_bufs_len(&bufs)); + CU_ASSERT(14 == *bufs.cur->buf.pos); + + for (i = 0; i < 999; ++i) { + rv = nghttp2_bufs_addb(&bufs, 254); + + CU_ASSERT(0 == rv); + CU_ASSERT(i + 2 == nghttp2_buf_len(&bufs.cur->buf)); + CU_ASSERT(i + 2 == nghttp2_bufs_len(&bufs)); + CU_ASSERT(254 == *(bufs.cur->buf.last - 1)); + CU_ASSERT(bufs.cur == bufs.head); + } + + rv = nghttp2_bufs_addb(&bufs, 253); + CU_ASSERT(0 == rv); + CU_ASSERT(1 == nghttp2_buf_len(&bufs.cur->buf)); + CU_ASSERT(1001 == nghttp2_bufs_len(&bufs)); + CU_ASSERT(253 == *(bufs.cur->buf.last - 1)); + CU_ASSERT(bufs.cur == bufs.head->next); + + rv = nghttp2_bufs_addb_hold(&bufs, 15); + CU_ASSERT(0 == rv); + CU_ASSERT(1 == nghttp2_buf_len(&bufs.cur->buf)); + CU_ASSERT(1001 == nghttp2_bufs_len(&bufs)); + CU_ASSERT(15 == *(bufs.cur->buf.last)); + + /* test fast version */ + + nghttp2_bufs_fast_addb(&bufs, 240); + + CU_ASSERT(2 == nghttp2_buf_len(&bufs.cur->buf)); + CU_ASSERT(1002 == nghttp2_bufs_len(&bufs)); + CU_ASSERT(240 == *(bufs.cur->buf.last - 1)); + + nghttp2_bufs_fast_addb_hold(&bufs, 113); + + CU_ASSERT(2 == nghttp2_buf_len(&bufs.cur->buf)); + CU_ASSERT(1002 == nghttp2_bufs_len(&bufs)); + CU_ASSERT(113 == *(bufs.cur->buf.last)); + + /* addb_hold when last == end */ + bufs.cur->buf.last = bufs.cur->buf.end; + + rv = nghttp2_bufs_addb_hold(&bufs, 19); + CU_ASSERT(0 == rv); + CU_ASSERT(0 == nghttp2_buf_len(&bufs.cur->buf)); + CU_ASSERT(2000 == nghttp2_bufs_len(&bufs)); + CU_ASSERT(19 == *(bufs.cur->buf.last)); + + nghttp2_bufs_free(&bufs); +} + +void test_nghttp2_bufs_orb(void) { + int rv; + nghttp2_bufs bufs; + nghttp2_mem *mem; + + mem = nghttp2_mem_default(); + + rv = nghttp2_bufs_init(&bufs, 1000, 3, mem); + CU_ASSERT(0 == rv); + + *(bufs.cur->buf.last) = 0; + + rv = nghttp2_bufs_orb_hold(&bufs, 15); + CU_ASSERT(0 == rv); + CU_ASSERT(0 == nghttp2_buf_len(&bufs.cur->buf)); + CU_ASSERT(0 == nghttp2_bufs_len(&bufs)); + CU_ASSERT(15 == *(bufs.cur->buf.last)); + + rv = nghttp2_bufs_orb(&bufs, 240); + CU_ASSERT(0 == rv); + CU_ASSERT(1 == nghttp2_buf_len(&bufs.cur->buf)); + CU_ASSERT(1 == nghttp2_bufs_len(&bufs)); + CU_ASSERT(255 == *(bufs.cur->buf.last - 1)); + + *(bufs.cur->buf.last) = 0; + nghttp2_bufs_fast_orb_hold(&bufs, 240); + CU_ASSERT(240 == *(bufs.cur->buf.last)); + + nghttp2_bufs_fast_orb(&bufs, 15); + CU_ASSERT(255 == *(bufs.cur->buf.last - 1)); + + nghttp2_bufs_free(&bufs); +} + +void test_nghttp2_bufs_remove(void) { + int rv; + nghttp2_bufs bufs; + nghttp2_buf_chain *chain; + int i; + uint8_t *out; + ssize_t outlen; + nghttp2_mem *mem; + + mem = nghttp2_mem_default(); + + rv = nghttp2_bufs_init(&bufs, 1000, 3, mem); + CU_ASSERT(0 == rv); + + nghttp2_buf_shift_right(&bufs.cur->buf, 10); + + rv = nghttp2_bufs_add(&bufs, "hello ", 6); + CU_ASSERT(0 == rv); + + for (i = 0; i < 2; ++i) { + chain = bufs.cur; + + rv = nghttp2_bufs_advance(&bufs); + CU_ASSERT(0 == rv); + + CU_ASSERT(chain->next == bufs.cur); + } + + rv = nghttp2_bufs_add(&bufs, "world", 5); + CU_ASSERT(0 == rv); + + outlen = nghttp2_bufs_remove(&bufs, &out); + CU_ASSERT(11 == outlen); + + CU_ASSERT(0 == memcmp("hello world", out, outlen)); + CU_ASSERT(11 == nghttp2_bufs_len(&bufs)); + + mem->free(out, NULL); + nghttp2_bufs_free(&bufs); +} + +void test_nghttp2_bufs_reset(void) { + int rv; + nghttp2_bufs bufs; + nghttp2_buf_chain *ci; + ssize_t offset = 9; + nghttp2_mem *mem; + + mem = nghttp2_mem_default(); + + rv = nghttp2_bufs_init3(&bufs, 250, 3, 1, offset, mem); + CU_ASSERT(0 == rv); + + rv = nghttp2_bufs_add(&bufs, "foo", 3); + CU_ASSERT(0 == rv); + + rv = nghttp2_bufs_advance(&bufs); + CU_ASSERT(0 == rv); + + rv = nghttp2_bufs_add(&bufs, "bar", 3); + CU_ASSERT(0 == rv); + + CU_ASSERT(6 == nghttp2_bufs_len(&bufs)); + + nghttp2_bufs_reset(&bufs); + + CU_ASSERT(0 == nghttp2_bufs_len(&bufs)); + CU_ASSERT(bufs.cur == bufs.head); + + for (ci = bufs.head; ci; ci = ci->next) { + CU_ASSERT(offset == ci->buf.pos - ci->buf.begin); + CU_ASSERT(ci->buf.pos == ci->buf.last); + } + + CU_ASSERT(bufs.head->next == NULL); + + nghttp2_bufs_free(&bufs); +} + +void test_nghttp2_bufs_advance(void) { + int rv; + nghttp2_bufs bufs; + int i; + nghttp2_mem *mem; + + mem = nghttp2_mem_default(); + + rv = nghttp2_bufs_init(&bufs, 250, 3, mem); + CU_ASSERT(0 == rv); + + for (i = 0; i < 2; ++i) { + rv = nghttp2_bufs_advance(&bufs); + CU_ASSERT(0 == rv); + } + + rv = nghttp2_bufs_advance(&bufs); + CU_ASSERT(NGHTTP2_ERR_BUFFER_ERROR == rv); + + nghttp2_bufs_free(&bufs); +} + +void test_nghttp2_bufs_next_present(void) { + int rv; + nghttp2_bufs bufs; + nghttp2_mem *mem; + + mem = nghttp2_mem_default(); + + rv = nghttp2_bufs_init(&bufs, 250, 3, mem); + CU_ASSERT(0 == rv); + + CU_ASSERT(0 == nghttp2_bufs_next_present(&bufs)); + + rv = nghttp2_bufs_advance(&bufs); + CU_ASSERT(0 == rv); + + nghttp2_bufs_rewind(&bufs); + + CU_ASSERT(0 == nghttp2_bufs_next_present(&bufs)); + + bufs.cur = bufs.head->next; + + rv = nghttp2_bufs_addb(&bufs, 1); + CU_ASSERT(0 == rv); + + nghttp2_bufs_rewind(&bufs); + + CU_ASSERT(0 != nghttp2_bufs_next_present(&bufs)); + + nghttp2_bufs_free(&bufs); +} + +void test_nghttp2_bufs_realloc(void) { + int rv; + nghttp2_bufs bufs; + nghttp2_mem *mem; + + mem = nghttp2_mem_default(); + + rv = nghttp2_bufs_init3(&bufs, 266, 3, 1, 10, mem); + CU_ASSERT(0 == rv); + + /* Create new buffer to see that these buffers are deallocated on + realloc */ + rv = nghttp2_bufs_advance(&bufs); + CU_ASSERT(0 == rv); + + rv = nghttp2_bufs_realloc(&bufs, 522); + CU_ASSERT(0 == rv); + + CU_ASSERT(512 == nghttp2_bufs_cur_avail(&bufs)); + + rv = nghttp2_bufs_realloc(&bufs, 9); + CU_ASSERT(NGHTTP2_ERR_INVALID_ARGUMENT == rv); + + nghttp2_bufs_free(&bufs); +} diff --git a/tests/nghttp2_buf_test.h b/tests/nghttp2_buf_test.h new file mode 100644 index 0000000..0b94a3f --- /dev/null +++ b/tests/nghttp2_buf_test.h @@ -0,0 +1,38 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2014 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGHTTP2_BUF_TEST_H +#define NGHTTP2_BUF_TEST_H + +void test_nghttp2_bufs_add(void); +void test_nghttp2_bufs_add_stack_buffer_overflow_bug(void); +void test_nghttp2_bufs_addb(void); +void test_nghttp2_bufs_orb(void); +void test_nghttp2_bufs_remove(void); +void test_nghttp2_bufs_reset(void); +void test_nghttp2_bufs_advance(void); +void test_nghttp2_bufs_next_present(void); +void test_nghttp2_bufs_realloc(void); + +#endif /* NGHTTP2_BUF_TEST_H */ diff --git a/tests/nghttp2_frame_test.c b/tests/nghttp2_frame_test.c new file mode 100644 index 0000000..0e0d1be --- /dev/null +++ b/tests/nghttp2_frame_test.c @@ -0,0 +1,561 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "nghttp2_frame_test.h" + +#include +#include + +#include + +#include "nghttp2_frame.h" +#include "nghttp2_helper.h" +#include "nghttp2_test_helper.h" +#include "nghttp2_priority_spec.h" + +static nghttp2_nv make_nv(const char *name, const char *value) { + nghttp2_nv nv; + nv.name = (uint8_t *)name; + nv.value = (uint8_t *)value; + nv.namelen = strlen(name); + nv.valuelen = strlen(value); + nv.flags = NGHTTP2_NV_FLAG_NONE; + + return nv; +} + +#define HEADERS_LENGTH 7 + +static nghttp2_nv *headers(nghttp2_mem *mem) { + nghttp2_nv *nva = mem->malloc(sizeof(nghttp2_nv) * HEADERS_LENGTH, NULL); + nva[0] = make_nv("method", "GET"); + nva[1] = make_nv("scheme", "https"); + nva[2] = make_nv("url", "/"); + nva[3] = make_nv("x-head", "foo"); + nva[4] = make_nv("x-head", "bar"); + nva[5] = make_nv("version", "HTTP/1.1"); + nva[6] = make_nv("x-empty", ""); + return nva; +} + +static void check_frame_header(size_t length, uint8_t type, uint8_t flags, + int32_t stream_id, nghttp2_frame_hd *hd) { + CU_ASSERT(length == hd->length); + CU_ASSERT(type == hd->type); + CU_ASSERT(flags == hd->flags); + CU_ASSERT(stream_id == hd->stream_id); + CU_ASSERT(0 == hd->reserved); +} + +void test_nghttp2_frame_pack_headers() { + nghttp2_hd_deflater deflater; + nghttp2_hd_inflater inflater; + nghttp2_headers frame, oframe; + nghttp2_bufs bufs; + nghttp2_nv *nva; + nghttp2_priority_spec pri_spec; + ssize_t nvlen; + nva_out out; + ssize_t hdblocklen; + int rv; + nghttp2_mem *mem; + + mem = nghttp2_mem_default(); + frame_pack_bufs_init(&bufs); + + nva_out_init(&out); + nghttp2_hd_deflate_init(&deflater, mem); + nghttp2_hd_inflate_init(&inflater, mem); + + nva = headers(mem); + nvlen = HEADERS_LENGTH; + + nghttp2_priority_spec_default_init(&pri_spec); + + nghttp2_frame_headers_init( + &frame, NGHTTP2_FLAG_END_STREAM | NGHTTP2_FLAG_END_HEADERS, 1000000007, + NGHTTP2_HCAT_REQUEST, &pri_spec, nva, nvlen); + rv = nghttp2_frame_pack_headers(&bufs, &frame, &deflater); + + nghttp2_bufs_rewind(&bufs); + + CU_ASSERT(0 == rv); + CU_ASSERT(nghttp2_bufs_len(&bufs) > 0); + CU_ASSERT(0 == unpack_framebuf((nghttp2_frame *)&oframe, &bufs)); + + check_frame_header(nghttp2_bufs_len(&bufs) - NGHTTP2_FRAME_HDLEN, + NGHTTP2_HEADERS, + NGHTTP2_FLAG_END_STREAM | NGHTTP2_FLAG_END_HEADERS, + 1000000007, &oframe.hd); + /* We did not include PRIORITY flag */ + CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT == oframe.pri_spec.weight); + + hdblocklen = nghttp2_bufs_len(&bufs) - NGHTTP2_FRAME_HDLEN; + CU_ASSERT(hdblocklen == + inflate_hd(&inflater, &out, &bufs, NGHTTP2_FRAME_HDLEN, mem)); + + CU_ASSERT(7 == out.nvlen); + CU_ASSERT(nvnameeq("method", &out.nva[0])); + CU_ASSERT(nvvalueeq("GET", &out.nva[0])); + + nghttp2_frame_headers_free(&oframe, mem); + nva_out_reset(&out, mem); + nghttp2_bufs_reset(&bufs); + + memset(&oframe, 0, sizeof(oframe)); + /* Next, include NGHTTP2_FLAG_PRIORITY */ + nghttp2_priority_spec_init(&frame.pri_spec, 1000000009, 12, 1); + frame.hd.flags |= NGHTTP2_FLAG_PRIORITY; + + rv = nghttp2_frame_pack_headers(&bufs, &frame, &deflater); + + CU_ASSERT(0 == rv); + CU_ASSERT(nghttp2_bufs_len(&bufs) > 0); + CU_ASSERT(0 == unpack_framebuf((nghttp2_frame *)&oframe, &bufs)); + + check_frame_header(nghttp2_bufs_len(&bufs) - NGHTTP2_FRAME_HDLEN, + NGHTTP2_HEADERS, + NGHTTP2_FLAG_END_STREAM | NGHTTP2_FLAG_END_HEADERS | + NGHTTP2_FLAG_PRIORITY, + 1000000007, &oframe.hd); + + CU_ASSERT(1000000009 == oframe.pri_spec.stream_id); + CU_ASSERT(12 == oframe.pri_spec.weight); + CU_ASSERT(1 == oframe.pri_spec.exclusive); + + hdblocklen = nghttp2_bufs_len(&bufs) - NGHTTP2_FRAME_HDLEN - + nghttp2_frame_priority_len(oframe.hd.flags); + CU_ASSERT(hdblocklen == + inflate_hd(&inflater, &out, &bufs, + NGHTTP2_FRAME_HDLEN + + nghttp2_frame_priority_len(oframe.hd.flags), + mem)); + + nghttp2_nv_array_sort(out.nva, out.nvlen); + CU_ASSERT(nvnameeq("method", &out.nva[0])); + + nghttp2_frame_headers_free(&oframe, mem); + nva_out_reset(&out, mem); + nghttp2_bufs_reset(&bufs); + + nghttp2_bufs_free(&bufs); + nghttp2_frame_headers_free(&frame, mem); + nghttp2_hd_inflate_free(&inflater); + nghttp2_hd_deflate_free(&deflater); +} + +void test_nghttp2_frame_pack_headers_frame_too_large(void) { + nghttp2_hd_deflater deflater; + nghttp2_headers frame; + nghttp2_bufs bufs; + nghttp2_nv *nva; + size_t big_vallen = NGHTTP2_HD_MAX_NV; + nghttp2_nv big_hds[16]; + size_t big_hdslen = ARRLEN(big_hds); + size_t i; + int rv; + nghttp2_mem *mem; + + mem = nghttp2_mem_default(); + frame_pack_bufs_init(&bufs); + + for (i = 0; i < big_hdslen; ++i) { + big_hds[i].name = (uint8_t *)"header"; + big_hds[i].value = mem->malloc(big_vallen + 1, NULL); + memset(big_hds[i].value, '0' + (int)i, big_vallen); + big_hds[i].value[big_vallen] = '\0'; + big_hds[i].namelen = strlen((char *)big_hds[i].name); + big_hds[i].valuelen = big_vallen; + big_hds[i].flags = NGHTTP2_NV_FLAG_NONE; + } + + nghttp2_nv_array_copy(&nva, big_hds, big_hdslen, mem); + nghttp2_hd_deflate_init(&deflater, mem); + nghttp2_frame_headers_init( + &frame, NGHTTP2_FLAG_END_STREAM | NGHTTP2_FLAG_END_HEADERS, 1000000007, + NGHTTP2_HCAT_REQUEST, NULL, nva, big_hdslen); + rv = nghttp2_frame_pack_headers(&bufs, &frame, &deflater); + CU_ASSERT(NGHTTP2_ERR_HEADER_COMP == rv); + + nghttp2_frame_headers_free(&frame, mem); + nghttp2_bufs_free(&bufs); + for (i = 0; i < big_hdslen; ++i) { + mem->free(big_hds[i].value, NULL); + } + nghttp2_hd_deflate_free(&deflater); +} + +void test_nghttp2_frame_pack_priority(void) { + nghttp2_priority frame, oframe; + nghttp2_bufs bufs; + nghttp2_priority_spec pri_spec; + int rv; + + frame_pack_bufs_init(&bufs); + + /* First, pack priority with priority group and weight */ + nghttp2_priority_spec_init(&pri_spec, 1000000009, 12, 1); + + nghttp2_frame_priority_init(&frame, 1000000007, &pri_spec); + rv = nghttp2_frame_pack_priority(&bufs, &frame); + + CU_ASSERT(0 == rv); + CU_ASSERT(NGHTTP2_FRAME_HDLEN + 5 == nghttp2_bufs_len(&bufs)); + CU_ASSERT(0 == unpack_framebuf((nghttp2_frame *)&oframe, &bufs)); + check_frame_header(5, NGHTTP2_PRIORITY, NGHTTP2_FLAG_NONE, 1000000007, + &oframe.hd); + + CU_ASSERT(1000000009 == oframe.pri_spec.stream_id); + CU_ASSERT(12 == oframe.pri_spec.weight); + CU_ASSERT(1 == oframe.pri_spec.exclusive); + + nghttp2_frame_priority_free(&oframe); + nghttp2_bufs_reset(&bufs); + + nghttp2_bufs_free(&bufs); + nghttp2_frame_priority_free(&frame); +} + +void test_nghttp2_frame_pack_rst_stream(void) { + nghttp2_rst_stream frame, oframe; + nghttp2_bufs bufs; + int rv; + + frame_pack_bufs_init(&bufs); + + nghttp2_frame_rst_stream_init(&frame, 1000000007, NGHTTP2_PROTOCOL_ERROR); + rv = nghttp2_frame_pack_rst_stream(&bufs, &frame); + + CU_ASSERT(0 == rv); + CU_ASSERT(NGHTTP2_FRAME_HDLEN + 4 == nghttp2_bufs_len(&bufs)); + CU_ASSERT(0 == unpack_framebuf((nghttp2_frame *)&oframe, &bufs)); + check_frame_header(4, NGHTTP2_RST_STREAM, NGHTTP2_FLAG_NONE, 1000000007, + &oframe.hd); + CU_ASSERT(NGHTTP2_PROTOCOL_ERROR == oframe.error_code); + + nghttp2_frame_rst_stream_free(&oframe); + nghttp2_bufs_reset(&bufs); + + /* Unknown error code is passed to callback as is */ + frame.error_code = 1000000009; + rv = nghttp2_frame_pack_rst_stream(&bufs, &frame); + + CU_ASSERT(0 == rv); + CU_ASSERT(0 == unpack_framebuf((nghttp2_frame *)&oframe, &bufs)); + + check_frame_header(4, NGHTTP2_RST_STREAM, NGHTTP2_FLAG_NONE, 1000000007, + &oframe.hd); + + CU_ASSERT(1000000009 == oframe.error_code); + + nghttp2_frame_rst_stream_free(&oframe); + + nghttp2_frame_rst_stream_free(&frame); + + nghttp2_bufs_free(&bufs); +} + +void test_nghttp2_frame_pack_settings() { + nghttp2_settings frame, oframe; + nghttp2_bufs bufs; + int i; + int rv; + nghttp2_settings_entry iv[] = {{NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 256}, + {NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE, 16384}, + {NGHTTP2_SETTINGS_HEADER_TABLE_SIZE, 4096}}; + nghttp2_mem *mem; + + mem = nghttp2_mem_default(); + frame_pack_bufs_init(&bufs); + + nghttp2_frame_settings_init(&frame, NGHTTP2_FLAG_NONE, + nghttp2_frame_iv_copy(iv, 3, mem), 3); + rv = nghttp2_frame_pack_settings(&bufs, &frame); + + CU_ASSERT(0 == rv); + CU_ASSERT(NGHTTP2_FRAME_HDLEN + 3 * NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH == + nghttp2_bufs_len(&bufs)); + + CU_ASSERT(0 == unpack_framebuf((nghttp2_frame *)&oframe, &bufs)); + check_frame_header(3 * NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH, NGHTTP2_SETTINGS, + NGHTTP2_FLAG_NONE, 0, &oframe.hd); + CU_ASSERT(3 == oframe.niv); + for (i = 0; i < 3; ++i) { + CU_ASSERT(iv[i].settings_id == oframe.iv[i].settings_id); + CU_ASSERT(iv[i].value == oframe.iv[i].value); + } + + nghttp2_bufs_free(&bufs); + nghttp2_frame_settings_free(&frame, mem); + nghttp2_frame_settings_free(&oframe, mem); +} + +void test_nghttp2_frame_pack_push_promise() { + nghttp2_hd_deflater deflater; + nghttp2_hd_inflater inflater; + nghttp2_push_promise frame, oframe; + nghttp2_bufs bufs; + nghttp2_nv *nva; + ssize_t nvlen; + nva_out out; + ssize_t hdblocklen; + int rv; + nghttp2_mem *mem; + + mem = nghttp2_mem_default(); + frame_pack_bufs_init(&bufs); + + nva_out_init(&out); + nghttp2_hd_deflate_init(&deflater, mem); + nghttp2_hd_inflate_init(&inflater, mem); + + nva = headers(mem); + nvlen = HEADERS_LENGTH; + nghttp2_frame_push_promise_init(&frame, NGHTTP2_FLAG_END_HEADERS, 1000000007, + (1U << 31) - 1, nva, nvlen); + rv = nghttp2_frame_pack_push_promise(&bufs, &frame, &deflater); + + CU_ASSERT(0 == rv); + CU_ASSERT(nghttp2_bufs_len(&bufs) > 0); + CU_ASSERT(0 == unpack_framebuf((nghttp2_frame *)&oframe, &bufs)); + + check_frame_header(nghttp2_bufs_len(&bufs) - NGHTTP2_FRAME_HDLEN, + NGHTTP2_PUSH_PROMISE, NGHTTP2_FLAG_END_HEADERS, 1000000007, + &oframe.hd); + CU_ASSERT((1U << 31) - 1 == oframe.promised_stream_id); + + hdblocklen = nghttp2_bufs_len(&bufs) - NGHTTP2_FRAME_HDLEN - 4; + CU_ASSERT(hdblocklen == + inflate_hd(&inflater, &out, &bufs, NGHTTP2_FRAME_HDLEN + 4, mem)); + + CU_ASSERT(7 == out.nvlen); + CU_ASSERT(nvnameeq("method", &out.nva[0])); + CU_ASSERT(nvvalueeq("GET", &out.nva[0])); + + nva_out_reset(&out, mem); + nghttp2_bufs_free(&bufs); + nghttp2_frame_push_promise_free(&oframe, mem); + nghttp2_frame_push_promise_free(&frame, mem); + nghttp2_hd_inflate_free(&inflater); + nghttp2_hd_deflate_free(&deflater); +} + +void test_nghttp2_frame_pack_ping(void) { + nghttp2_ping frame, oframe; + nghttp2_bufs bufs; + const uint8_t opaque_data[] = "01234567"; + int rv; + + frame_pack_bufs_init(&bufs); + + nghttp2_frame_ping_init(&frame, NGHTTP2_FLAG_ACK, opaque_data); + rv = nghttp2_frame_pack_ping(&bufs, &frame); + + CU_ASSERT(0 == rv); + CU_ASSERT(NGHTTP2_FRAME_HDLEN + 8 == nghttp2_bufs_len(&bufs)); + CU_ASSERT(0 == unpack_framebuf((nghttp2_frame *)&oframe, &bufs)); + check_frame_header(8, NGHTTP2_PING, NGHTTP2_FLAG_ACK, 0, &oframe.hd); + CU_ASSERT(memcmp(opaque_data, oframe.opaque_data, sizeof(opaque_data) - 1) == + 0); + + nghttp2_bufs_free(&bufs); + nghttp2_frame_ping_free(&oframe); + nghttp2_frame_ping_free(&frame); +} + +void test_nghttp2_frame_pack_goaway() { + nghttp2_goaway frame, oframe; + nghttp2_bufs bufs; + size_t opaque_data_len = 16; + uint8_t *opaque_data; + int rv; + nghttp2_mem *mem; + + mem = nghttp2_mem_default(); + frame_pack_bufs_init(&bufs); + + opaque_data = mem->malloc(opaque_data_len, NULL); + memcpy(opaque_data, "0123456789abcdef", opaque_data_len); + nghttp2_frame_goaway_init(&frame, 1000000007, NGHTTP2_PROTOCOL_ERROR, + opaque_data, opaque_data_len); + rv = nghttp2_frame_pack_goaway(&bufs, &frame); + + CU_ASSERT(0 == rv); + CU_ASSERT((ssize_t)(NGHTTP2_FRAME_HDLEN + 8 + opaque_data_len) == + nghttp2_bufs_len(&bufs)); + CU_ASSERT(0 == unpack_framebuf((nghttp2_frame *)&oframe, &bufs)); + check_frame_header(24, NGHTTP2_GOAWAY, NGHTTP2_FLAG_NONE, 0, &oframe.hd); + CU_ASSERT(1000000007 == oframe.last_stream_id); + CU_ASSERT(NGHTTP2_PROTOCOL_ERROR == oframe.error_code); + + CU_ASSERT(opaque_data_len == oframe.opaque_data_len); + CU_ASSERT(memcmp(opaque_data, oframe.opaque_data, opaque_data_len) == 0); + + nghttp2_frame_goaway_free(&oframe, mem); + nghttp2_bufs_reset(&bufs); + + /* Unknown error code is passed to callback as is */ + frame.error_code = 1000000009; + + rv = nghttp2_frame_pack_goaway(&bufs, &frame); + + CU_ASSERT(0 == rv); + CU_ASSERT(0 == unpack_framebuf((nghttp2_frame *)&oframe, &bufs)); + check_frame_header(24, NGHTTP2_GOAWAY, NGHTTP2_FLAG_NONE, 0, &oframe.hd); + CU_ASSERT(1000000009 == oframe.error_code); + + nghttp2_frame_goaway_free(&oframe, mem); + + nghttp2_frame_goaway_free(&frame, mem); + + nghttp2_bufs_free(&bufs); +} + +void test_nghttp2_frame_pack_window_update(void) { + nghttp2_window_update frame, oframe; + nghttp2_bufs bufs; + int rv; + + frame_pack_bufs_init(&bufs); + + nghttp2_frame_window_update_init(&frame, NGHTTP2_FLAG_NONE, 1000000007, 4096); + rv = nghttp2_frame_pack_window_update(&bufs, &frame); + + CU_ASSERT(0 == rv); + CU_ASSERT(NGHTTP2_FRAME_HDLEN + 4 == nghttp2_bufs_len(&bufs)); + CU_ASSERT(0 == unpack_framebuf((nghttp2_frame *)&oframe, &bufs)); + check_frame_header(4, NGHTTP2_WINDOW_UPDATE, NGHTTP2_FLAG_NONE, 1000000007, + &oframe.hd); + CU_ASSERT(4096 == oframe.window_size_increment); + + nghttp2_bufs_free(&bufs); + nghttp2_frame_window_update_free(&oframe); + nghttp2_frame_window_update_free(&frame); +} + +void test_nghttp2_nv_array_copy(void) { + nghttp2_nv *nva; + ssize_t rv; + nghttp2_nv emptynv[] = {MAKE_NV("", ""), MAKE_NV("", "")}; + nghttp2_nv nv[] = {MAKE_NV("alpha", "bravo"), MAKE_NV("charlie", "delta")}; + nghttp2_nv bignv; + nghttp2_mem *mem; + + mem = nghttp2_mem_default(); + + bignv.name = (uint8_t *)"echo"; + bignv.namelen = strlen("echo"); + bignv.valuelen = (1 << 14) - 1; + bignv.value = mem->malloc(bignv.valuelen, NULL); + memset(bignv.value, '0', bignv.valuelen); + + rv = nghttp2_nv_array_copy(&nva, NULL, 0, mem); + CU_ASSERT(0 == rv); + CU_ASSERT(NULL == nva); + + rv = nghttp2_nv_array_copy(&nva, emptynv, ARRLEN(emptynv), mem); + CU_ASSERT(0 == rv); + CU_ASSERT(nva[0].namelen == 0); + CU_ASSERT(nva[0].valuelen == 0); + CU_ASSERT(nva[1].namelen == 0); + CU_ASSERT(nva[1].valuelen == 0); + + nghttp2_nv_array_del(nva, mem); + + rv = nghttp2_nv_array_copy(&nva, nv, ARRLEN(nv), mem); + CU_ASSERT(0 == rv); + CU_ASSERT(nva[0].namelen == 5); + CU_ASSERT(0 == memcmp("alpha", nva[0].name, 5)); + CU_ASSERT(nva[0].valuelen = 5); + CU_ASSERT(0 == memcmp("bravo", nva[0].value, 5)); + CU_ASSERT(nva[1].namelen == 7); + CU_ASSERT(0 == memcmp("charlie", nva[1].name, 7)); + CU_ASSERT(nva[1].valuelen == 5); + CU_ASSERT(0 == memcmp("delta", nva[1].value, 5)); + + nghttp2_nv_array_del(nva, mem); + + /* Large header field is acceptable */ + rv = nghttp2_nv_array_copy(&nva, &bignv, 1, mem); + CU_ASSERT(0 == rv); + + nghttp2_nv_array_del(nva, mem); + + mem->free(bignv.value, NULL); +} + +void test_nghttp2_iv_check(void) { + nghttp2_settings_entry iv[5]; + + iv[0].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS; + iv[0].value = 100; + iv[1].settings_id = NGHTTP2_SETTINGS_HEADER_TABLE_SIZE; + iv[1].value = 1024; + + CU_ASSERT(nghttp2_iv_check(iv, 2)); + + iv[1].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE; + iv[1].value = NGHTTP2_MAX_WINDOW_SIZE; + CU_ASSERT(nghttp2_iv_check(iv, 2)); + + /* Too large window size */ + iv[1].value = (uint32_t)NGHTTP2_MAX_WINDOW_SIZE + 1; + CU_ASSERT(0 == nghttp2_iv_check(iv, 2)); + + /* ENABLE_PUSH only allows 0 or 1 */ + iv[1].settings_id = NGHTTP2_SETTINGS_ENABLE_PUSH; + iv[1].value = 0; + CU_ASSERT(nghttp2_iv_check(iv, 2)); + iv[1].value = 1; + CU_ASSERT(nghttp2_iv_check(iv, 2)); + iv[1].value = 3; + CU_ASSERT(!nghttp2_iv_check(iv, 2)); + + /* Undefined SETTINGS ID is allowed */ + iv[1].settings_id = 1000000009; + iv[1].value = 0; + CU_ASSERT(nghttp2_iv_check(iv, 2)); + + /* Too large SETTINGS_HEADER_TABLE_SIZE */ + iv[1].settings_id = NGHTTP2_SETTINGS_HEADER_TABLE_SIZE; + iv[1].value = UINT32_MAX; + CU_ASSERT(!nghttp2_iv_check(iv, 2)); + + /* Too small SETTINGS_MAX_FRAME_SIZE */ + iv[0].settings_id = NGHTTP2_SETTINGS_MAX_FRAME_SIZE; + iv[0].value = NGHTTP2_MAX_FRAME_SIZE_MIN - 1; + CU_ASSERT(!nghttp2_iv_check(iv, 1)); + + /* Too large SETTINGS_MAX_FRAME_SIZE */ + iv[0].settings_id = NGHTTP2_SETTINGS_MAX_FRAME_SIZE; + iv[0].value = NGHTTP2_MAX_FRAME_SIZE_MAX + 1; + CU_ASSERT(!nghttp2_iv_check(iv, 1)); + + /* Max and min SETTINGS_MAX_FRAME_SIZE */ + iv[0].settings_id = NGHTTP2_SETTINGS_MAX_FRAME_SIZE; + iv[0].value = NGHTTP2_MAX_FRAME_SIZE_MIN; + iv[1].settings_id = NGHTTP2_SETTINGS_MAX_FRAME_SIZE; + iv[1].value = NGHTTP2_MAX_FRAME_SIZE_MAX; + CU_ASSERT(nghttp2_iv_check(iv, 2)); +} diff --git a/tests/nghttp2_frame_test.h b/tests/nghttp2_frame_test.h new file mode 100644 index 0000000..a0ce37b --- /dev/null +++ b/tests/nghttp2_frame_test.h @@ -0,0 +1,40 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGHTTP2_FRAME_TEST_H +#define NGHTTP2_FRAME_TEST_H + +void test_nghttp2_frame_pack_headers(void); +void test_nghttp2_frame_pack_headers_frame_too_large(void); +void test_nghttp2_frame_pack_priority(void); +void test_nghttp2_frame_pack_rst_stream(void); +void test_nghttp2_frame_pack_settings(void); +void test_nghttp2_frame_pack_push_promise(void); +void test_nghttp2_frame_pack_ping(void); +void test_nghttp2_frame_pack_goaway(void); +void test_nghttp2_frame_pack_window_update(void); +void test_nghttp2_nv_array_copy(void); +void test_nghttp2_iv_check(void); + +#endif /* NGHTTP2_FRAME_TEST_H */ diff --git a/tests/nghttp2_hd_test.c b/tests/nghttp2_hd_test.c new file mode 100644 index 0000000..ecd7bc1 --- /dev/null +++ b/tests/nghttp2_hd_test.c @@ -0,0 +1,1257 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2013 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "nghttp2_hd_test.h" + +#include +#include + +#include + +#include "nghttp2_hd.h" +#include "nghttp2_frame.h" +#include "nghttp2_test_helper.h" + +#define GET_TABLE_ENT(context, index) nghttp2_hd_table_get(context, index) + +void test_nghttp2_hd_deflate(void) { + nghttp2_hd_deflater deflater; + nghttp2_hd_inflater inflater; + nghttp2_nv nva1[] = {MAKE_NV(":path", "/my-example/index.html"), + MAKE_NV(":scheme", "https"), MAKE_NV("hello", "world")}; + nghttp2_nv nva2[] = {MAKE_NV(":path", "/script.js"), + MAKE_NV(":scheme", "https")}; + nghttp2_nv nva3[] = {MAKE_NV("cookie", "k1=v1"), MAKE_NV("cookie", "k2=v2"), + MAKE_NV("via", "proxy")}; + nghttp2_nv nva4[] = {MAKE_NV(":path", "/style.css"), + MAKE_NV("cookie", "k1=v1"), MAKE_NV("cookie", "k1=v1")}; + nghttp2_nv nva5[] = {MAKE_NV(":path", "/style.css"), + MAKE_NV("x-nghttp2", "")}; + nghttp2_bufs bufs; + ssize_t blocklen; + nva_out out; + int rv; + nghttp2_mem *mem; + + mem = nghttp2_mem_default(); + frame_pack_bufs_init(&bufs); + + nva_out_init(&out); + CU_ASSERT(0 == nghttp2_hd_deflate_init(&deflater, mem)); + CU_ASSERT(0 == nghttp2_hd_inflate_init(&inflater, mem)); + + rv = nghttp2_hd_deflate_hd_bufs(&deflater, &bufs, nva1, ARRLEN(nva1)); + blocklen = nghttp2_bufs_len(&bufs); + + CU_ASSERT(0 == rv); + CU_ASSERT(blocklen > 0); + CU_ASSERT(blocklen == inflate_hd(&inflater, &out, &bufs, 0, mem)); + + CU_ASSERT(3 == out.nvlen); + assert_nv_equal(nva1, out.nva, 3, mem); + + nva_out_reset(&out, mem); + nghttp2_bufs_reset(&bufs); + + /* Second headers */ + rv = nghttp2_hd_deflate_hd_bufs(&deflater, &bufs, nva2, ARRLEN(nva2)); + blocklen = nghttp2_bufs_len(&bufs); + + CU_ASSERT(0 == rv); + CU_ASSERT(blocklen > 0); + CU_ASSERT(blocklen == inflate_hd(&inflater, &out, &bufs, 0, mem)); + + CU_ASSERT(2 == out.nvlen); + assert_nv_equal(nva2, out.nva, 2, mem); + + nva_out_reset(&out, mem); + nghttp2_bufs_reset(&bufs); + + /* Third headers, including same header field name, but value is not + the same. */ + rv = nghttp2_hd_deflate_hd_bufs(&deflater, &bufs, nva3, ARRLEN(nva3)); + blocklen = nghttp2_bufs_len(&bufs); + + CU_ASSERT(0 == rv); + CU_ASSERT(blocklen > 0); + CU_ASSERT(blocklen == inflate_hd(&inflater, &out, &bufs, 0, mem)); + + CU_ASSERT(3 == out.nvlen); + assert_nv_equal(nva3, out.nva, 3, mem); + + nva_out_reset(&out, mem); + nghttp2_bufs_reset(&bufs); + + /* Fourth headers, including duplicate header fields. */ + rv = nghttp2_hd_deflate_hd_bufs(&deflater, &bufs, nva4, ARRLEN(nva4)); + blocklen = nghttp2_bufs_len(&bufs); + + CU_ASSERT(0 == rv); + CU_ASSERT(blocklen > 0); + CU_ASSERT(blocklen == inflate_hd(&inflater, &out, &bufs, 0, mem)); + + CU_ASSERT(3 == out.nvlen); + assert_nv_equal(nva4, out.nva, 3, mem); + + nva_out_reset(&out, mem); + nghttp2_bufs_reset(&bufs); + + /* Fifth headers includes empty value */ + rv = nghttp2_hd_deflate_hd_bufs(&deflater, &bufs, nva5, ARRLEN(nva5)); + blocklen = nghttp2_bufs_len(&bufs); + + CU_ASSERT(0 == rv); + CU_ASSERT(blocklen > 0); + CU_ASSERT(blocklen == inflate_hd(&inflater, &out, &bufs, 0, mem)); + + CU_ASSERT(2 == out.nvlen); + assert_nv_equal(nva5, out.nva, 2, mem); + + nva_out_reset(&out, mem); + nghttp2_bufs_reset(&bufs); + + /* Cleanup */ + nghttp2_bufs_free(&bufs); + nghttp2_hd_inflate_free(&inflater); + nghttp2_hd_deflate_free(&deflater); +} + +void test_nghttp2_hd_deflate_same_indexed_repr(void) { + nghttp2_hd_deflater deflater; + nghttp2_hd_inflater inflater; + nghttp2_nv nva1[] = {MAKE_NV("host", "alpha"), MAKE_NV("host", "alpha")}; + nghttp2_nv nva2[] = {MAKE_NV("host", "alpha"), MAKE_NV("host", "alpha"), + MAKE_NV("host", "alpha")}; + nghttp2_bufs bufs; + ssize_t blocklen; + nva_out out; + int rv; + nghttp2_mem *mem; + + mem = nghttp2_mem_default(); + frame_pack_bufs_init(&bufs); + + nva_out_init(&out); + CU_ASSERT(0 == nghttp2_hd_deflate_init(&deflater, mem)); + CU_ASSERT(0 == nghttp2_hd_inflate_init(&inflater, mem)); + + /* Encode 2 same headers. Emit 1 literal reprs and 1 index repr. */ + rv = nghttp2_hd_deflate_hd_bufs(&deflater, &bufs, nva1, ARRLEN(nva1)); + blocklen = nghttp2_bufs_len(&bufs); + + CU_ASSERT(0 == rv); + CU_ASSERT(blocklen > 0); + CU_ASSERT(blocklen == inflate_hd(&inflater, &out, &bufs, 0, mem)); + + CU_ASSERT(2 == out.nvlen); + assert_nv_equal(nva1, out.nva, 2, mem); + + nva_out_reset(&out, mem); + nghttp2_bufs_reset(&bufs); + + /* Encode 3 same headers. This time, emits 3 index reprs. */ + rv = nghttp2_hd_deflate_hd_bufs(&deflater, &bufs, nva2, ARRLEN(nva2)); + blocklen = nghttp2_bufs_len(&bufs); + + CU_ASSERT(0 == rv); + CU_ASSERT(blocklen == 3); + CU_ASSERT(blocklen == inflate_hd(&inflater, &out, &bufs, 0, mem)); + + CU_ASSERT(3 == out.nvlen); + assert_nv_equal(nva2, out.nva, 3, mem); + + nva_out_reset(&out, mem); + nghttp2_bufs_reset(&bufs); + + /* Cleanup */ + nghttp2_bufs_free(&bufs); + nghttp2_hd_inflate_free(&inflater); + nghttp2_hd_deflate_free(&deflater); +} + +void test_nghttp2_hd_inflate_indexed(void) { + nghttp2_hd_inflater inflater; + nghttp2_bufs bufs; + ssize_t blocklen; + nghttp2_nv nv = MAKE_NV(":path", "/"); + nva_out out; + nghttp2_mem *mem; + + mem = nghttp2_mem_default(); + frame_pack_bufs_init(&bufs); + + nva_out_init(&out); + nghttp2_hd_inflate_init(&inflater, mem); + + nghttp2_bufs_addb(&bufs, (1 << 7) | 4); + + blocklen = nghttp2_bufs_len(&bufs); + + CU_ASSERT(1 == blocklen); + CU_ASSERT(blocklen == inflate_hd(&inflater, &out, &bufs, 0, mem)); + + CU_ASSERT(1 == out.nvlen); + + assert_nv_equal(&nv, out.nva, 1, mem); + + nva_out_reset(&out, mem); + nghttp2_bufs_reset(&bufs); + + /* index = 0 is error */ + nghttp2_bufs_addb(&bufs, 1 << 7); + + blocklen = nghttp2_bufs_len(&bufs); + + CU_ASSERT(1 == blocklen); + CU_ASSERT(NGHTTP2_ERR_HEADER_COMP == + inflate_hd(&inflater, &out, &bufs, 0, mem)); + + nghttp2_bufs_free(&bufs); + nghttp2_hd_inflate_free(&inflater); +} + +void test_nghttp2_hd_inflate_indname_noinc(void) { + nghttp2_hd_inflater inflater; + nghttp2_bufs bufs; + ssize_t blocklen; + nghttp2_nv nv[] = {/* Huffman */ + MAKE_NV("user-agent", "nghttp2"), + /* Expecting no huffman */ + MAKE_NV("user-agent", "x")}; + size_t i; + nva_out out; + nghttp2_mem *mem; + + mem = nghttp2_mem_default(); + frame_pack_bufs_init(&bufs); + + nva_out_init(&out); + nghttp2_hd_inflate_init(&inflater, mem); + + for (i = 0; i < ARRLEN(nv); ++i) { + CU_ASSERT(0 == nghttp2_hd_emit_indname_block(&bufs, 57, &nv[i], + NGHTTP2_HD_WITHOUT_INDEXING)); + + blocklen = nghttp2_bufs_len(&bufs); + + CU_ASSERT(blocklen > 0); + CU_ASSERT(blocklen == inflate_hd(&inflater, &out, &bufs, 0, mem)); + + CU_ASSERT(1 == out.nvlen); + assert_nv_equal(&nv[i], out.nva, 1, mem); + CU_ASSERT(0 == inflater.ctx.hd_table.len); + + nva_out_reset(&out, mem); + nghttp2_bufs_reset(&bufs); + } + + nghttp2_bufs_free(&bufs); + nghttp2_hd_inflate_free(&inflater); +} + +void test_nghttp2_hd_inflate_indname_inc(void) { + nghttp2_hd_inflater inflater; + nghttp2_bufs bufs; + ssize_t blocklen; + nghttp2_nv nv = MAKE_NV("user-agent", "nghttp2"); + nva_out out; + nghttp2_mem *mem; + + mem = nghttp2_mem_default(); + frame_pack_bufs_init(&bufs); + + nva_out_init(&out); + nghttp2_hd_inflate_init(&inflater, mem); + + CU_ASSERT(0 == nghttp2_hd_emit_indname_block(&bufs, 57, &nv, + NGHTTP2_HD_WITH_INDEXING)); + + blocklen = nghttp2_bufs_len(&bufs); + + CU_ASSERT(blocklen > 0); + CU_ASSERT(blocklen == inflate_hd(&inflater, &out, &bufs, 0, mem)); + + CU_ASSERT(1 == out.nvlen); + assert_nv_equal(&nv, out.nva, 1, mem); + CU_ASSERT(1 == inflater.ctx.hd_table.len); + assert_nv_equal( + &nv, &GET_TABLE_ENT(&inflater.ctx, NGHTTP2_STATIC_TABLE_LENGTH + + inflater.ctx.hd_table.len - 1)->nv, + 1, mem); + + nva_out_reset(&out, mem); + nghttp2_bufs_free(&bufs); + nghttp2_hd_inflate_free(&inflater); +} + +void test_nghttp2_hd_inflate_indname_inc_eviction(void) { + nghttp2_hd_inflater inflater; + nghttp2_bufs bufs; + ssize_t blocklen; + uint8_t value[1025]; + nva_out out; + nghttp2_nv nv; + nghttp2_mem *mem; + + mem = nghttp2_mem_default(); + frame_pack_bufs_init(&bufs); + + nva_out_init(&out); + nghttp2_hd_inflate_init(&inflater, mem); + + memset(value, '0', sizeof(value)); + value[sizeof(value) - 1] = '\0'; + nv.value = value; + nv.valuelen = sizeof(value) - 1; + + nv.flags = NGHTTP2_NV_FLAG_NONE; + + CU_ASSERT(0 == nghttp2_hd_emit_indname_block(&bufs, 14, &nv, + NGHTTP2_HD_WITH_INDEXING)); + CU_ASSERT(0 == nghttp2_hd_emit_indname_block(&bufs, 15, &nv, + NGHTTP2_HD_WITH_INDEXING)); + CU_ASSERT(0 == nghttp2_hd_emit_indname_block(&bufs, 16, &nv, + NGHTTP2_HD_WITH_INDEXING)); + CU_ASSERT(0 == nghttp2_hd_emit_indname_block(&bufs, 17, &nv, + NGHTTP2_HD_WITH_INDEXING)); + + blocklen = nghttp2_bufs_len(&bufs); + + CU_ASSERT(blocklen > 0); + + CU_ASSERT(blocklen == inflate_hd(&inflater, &out, &bufs, 0, mem)); + + CU_ASSERT(4 == out.nvlen); + CU_ASSERT(14 == out.nva[0].namelen); + CU_ASSERT(0 == memcmp("accept-charset", out.nva[0].name, out.nva[0].namelen)); + CU_ASSERT(sizeof(value) - 1 == out.nva[0].valuelen); + + nva_out_reset(&out, mem); + nghttp2_bufs_reset(&bufs); + + CU_ASSERT(3 == inflater.ctx.hd_table.len); + + nghttp2_bufs_free(&bufs); + nghttp2_hd_inflate_free(&inflater); +} + +void test_nghttp2_hd_inflate_newname_noinc(void) { + nghttp2_hd_inflater inflater; + nghttp2_bufs bufs; + ssize_t blocklen; + nghttp2_nv nv[] = {/* Expecting huffman for both */ + MAKE_NV("my-long-content-length", "nghttp2"), + /* Expecting no huffman for both */ + MAKE_NV("x", "y"), + /* Huffman for key only */ + MAKE_NV("my-long-content-length", "y"), + /* Huffman for value only */ + MAKE_NV("x", "nghttp2")}; + size_t i; + nva_out out; + nghttp2_mem *mem; + + mem = nghttp2_mem_default(); + frame_pack_bufs_init(&bufs); + + nva_out_init(&out); + nghttp2_hd_inflate_init(&inflater, mem); + for (i = 0; i < ARRLEN(nv); ++i) { + CU_ASSERT(0 == nghttp2_hd_emit_newname_block(&bufs, &nv[i], + NGHTTP2_HD_WITHOUT_INDEXING)); + + blocklen = nghttp2_bufs_len(&bufs); + + CU_ASSERT(blocklen > 0); + CU_ASSERT(blocklen == inflate_hd(&inflater, &out, &bufs, 0, mem)); + + CU_ASSERT(1 == out.nvlen); + assert_nv_equal(&nv[i], out.nva, 1, mem); + CU_ASSERT(0 == inflater.ctx.hd_table.len); + + nva_out_reset(&out, mem); + nghttp2_bufs_reset(&bufs); + } + + nghttp2_bufs_free(&bufs); + nghttp2_hd_inflate_free(&inflater); +} + +void test_nghttp2_hd_inflate_newname_inc(void) { + nghttp2_hd_inflater inflater; + nghttp2_bufs bufs; + ssize_t blocklen; + nghttp2_nv nv = MAKE_NV("x-rel", "nghttp2"); + nva_out out; + nghttp2_mem *mem; + + mem = nghttp2_mem_default(); + frame_pack_bufs_init(&bufs); + + nva_out_init(&out); + nghttp2_hd_inflate_init(&inflater, mem); + + CU_ASSERT( + 0 == nghttp2_hd_emit_newname_block(&bufs, &nv, NGHTTP2_HD_WITH_INDEXING)); + + blocklen = nghttp2_bufs_len(&bufs); + + CU_ASSERT(blocklen > 0); + CU_ASSERT(blocklen == inflate_hd(&inflater, &out, &bufs, 0, mem)); + + CU_ASSERT(1 == out.nvlen); + assert_nv_equal(&nv, out.nva, 1, mem); + CU_ASSERT(1 == inflater.ctx.hd_table.len); + assert_nv_equal( + &nv, &GET_TABLE_ENT(&inflater.ctx, NGHTTP2_STATIC_TABLE_LENGTH + + inflater.ctx.hd_table.len - 1)->nv, + 1, mem); + + nva_out_reset(&out, mem); + nghttp2_bufs_free(&bufs); + nghttp2_hd_inflate_free(&inflater); +} + +void test_nghttp2_hd_inflate_clearall_inc(void) { + nghttp2_hd_inflater inflater; + nghttp2_bufs bufs; + ssize_t blocklen; + nghttp2_nv nv; + uint8_t value[4061]; + nva_out out; + nghttp2_mem *mem; + + mem = nghttp2_mem_default(); + bufs_large_init(&bufs, 8192); + + nva_out_init(&out); + /* Total 4097 bytes space required to hold this entry */ + nv.name = (uint8_t *)"alpha"; + nv.namelen = strlen((char *)nv.name); + memset(value, '0', sizeof(value)); + value[sizeof(value) - 1] = '\0'; + nv.value = value; + nv.valuelen = sizeof(value) - 1; + + nv.flags = NGHTTP2_NV_FLAG_NONE; + + nghttp2_hd_inflate_init(&inflater, mem); + + CU_ASSERT( + 0 == nghttp2_hd_emit_newname_block(&bufs, &nv, NGHTTP2_HD_WITH_INDEXING)); + + blocklen = nghttp2_bufs_len(&bufs); + + CU_ASSERT(blocklen > 0); + CU_ASSERT(blocklen == inflate_hd(&inflater, &out, &bufs, 0, mem)); + + CU_ASSERT(1 == out.nvlen); + assert_nv_equal(&nv, out.nva, 1, mem); + CU_ASSERT(0 == inflater.ctx.hd_table.len); + + nva_out_reset(&out, mem); + + /* Do it again */ + CU_ASSERT(blocklen == inflate_hd(&inflater, &out, &bufs, 0, mem)); + + CU_ASSERT(1 == out.nvlen); + assert_nv_equal(&nv, out.nva, 1, mem); + CU_ASSERT(0 == inflater.ctx.hd_table.len); + + nva_out_reset(&out, mem); + nghttp2_bufs_reset(&bufs); + + /* This time, 4096 bytes space required, which is just fits in the + header table */ + nv.valuelen = sizeof(value) - 2; + + CU_ASSERT( + 0 == nghttp2_hd_emit_newname_block(&bufs, &nv, NGHTTP2_HD_WITH_INDEXING)); + + blocklen = nghttp2_bufs_len(&bufs); + + CU_ASSERT(blocklen > 0); + CU_ASSERT(blocklen == inflate_hd(&inflater, &out, &bufs, 0, mem)); + + CU_ASSERT(1 == out.nvlen); + assert_nv_equal(&nv, out.nva, 1, mem); + CU_ASSERT(1 == inflater.ctx.hd_table.len); + + nva_out_reset(&out, mem); + nghttp2_bufs_reset(&bufs); + + nghttp2_bufs_free(&bufs); + nghttp2_hd_inflate_free(&inflater); +} + +void test_nghttp2_hd_inflate_zero_length_huffman(void) { + nghttp2_hd_inflater inflater; + nghttp2_bufs bufs; + /* Literal header without indexing - new name */ + uint8_t data[] = {0x40, 0x01, 0x78 /* 'x' */, 0x80}; + nva_out out; + nghttp2_mem *mem; + + mem = nghttp2_mem_default(); + frame_pack_bufs_init(&bufs); + + nva_out_init(&out); + + nghttp2_bufs_add(&bufs, data, sizeof(data)); + + /* /\* Literal header without indexing - new name *\/ */ + /* ptr[0] = 0x40; */ + /* ptr[1] = 1; */ + /* ptr[2] = 'x'; */ + /* ptr[3] = 0x80; */ + + nghttp2_hd_inflate_init(&inflater, mem); + CU_ASSERT(4 == inflate_hd(&inflater, &out, &bufs, 0, mem)); + + CU_ASSERT(1 == out.nvlen); + CU_ASSERT(1 == out.nva[0].namelen); + CU_ASSERT('x' == out.nva[0].name[0]); + CU_ASSERT(NULL == out.nva[0].value); + CU_ASSERT(0 == out.nva[0].valuelen); + + nva_out_reset(&out, mem); + nghttp2_bufs_free(&bufs); + nghttp2_hd_inflate_free(&inflater); +} + +void test_nghttp2_hd_ringbuf_reserve(void) { + nghttp2_hd_deflater deflater; + nghttp2_hd_inflater inflater; + nghttp2_nv nv; + nghttp2_bufs bufs; + nva_out out; + int i; + ssize_t rv; + ssize_t blocklen; + nghttp2_mem *mem; + + mem = nghttp2_mem_default(); + frame_pack_bufs_init(&bufs); + nva_out_init(&out); + + nv.flags = NGHTTP2_NV_FLAG_NONE; + nv.name = (uint8_t *)"a"; + nv.namelen = strlen((const char *)nv.name); + nv.valuelen = 4; + nv.value = mem->malloc(nv.valuelen + 1, NULL); + memset(nv.value, 0, nv.valuelen); + + nghttp2_hd_deflate_init2(&deflater, 8000, mem); + nghttp2_hd_inflate_init(&inflater, mem); + + nghttp2_hd_inflate_change_table_size(&inflater, 8000); + nghttp2_hd_deflate_change_table_size(&deflater, 8000); + + for (i = 0; i < 150; ++i) { + memcpy(nv.value, &i, sizeof(i)); + rv = nghttp2_hd_deflate_hd_bufs(&deflater, &bufs, &nv, 1); + blocklen = nghttp2_bufs_len(&bufs); + + CU_ASSERT(0 == rv); + CU_ASSERT(blocklen > 0); + + CU_ASSERT(blocklen == inflate_hd(&inflater, &out, &bufs, 0, mem)); + + CU_ASSERT(1 == out.nvlen); + assert_nv_equal(&nv, out.nva, 1, mem); + + nva_out_reset(&out, mem); + nghttp2_bufs_reset(&bufs); + } + + nghttp2_bufs_free(&bufs); + nghttp2_hd_inflate_free(&inflater); + nghttp2_hd_deflate_free(&deflater); + + mem->free(nv.value, NULL); +} + +void test_nghttp2_hd_change_table_size(void) { + nghttp2_hd_deflater deflater; + nghttp2_hd_inflater inflater; + nghttp2_nv nva[] = {MAKE_NV("alpha", "bravo"), MAKE_NV("charlie", "delta")}; + nghttp2_nv nva2[] = {MAKE_NV(":path", "/")}; + nghttp2_bufs bufs; + ssize_t rv; + nva_out out; + ssize_t blocklen; + nghttp2_mem *mem; + + mem = nghttp2_mem_default(); + frame_pack_bufs_init(&bufs); + + nva_out_init(&out); + + nghttp2_hd_deflate_init(&deflater, mem); + nghttp2_hd_inflate_init(&inflater, mem); + + /* inflater changes notifies 8000 max header table size */ + CU_ASSERT(0 == nghttp2_hd_inflate_change_table_size(&inflater, 8000)); + CU_ASSERT(0 == nghttp2_hd_deflate_change_table_size(&deflater, 8000)); + + CU_ASSERT(4096 == deflater.ctx.hd_table_bufsize_max); + + CU_ASSERT(8000 == inflater.ctx.hd_table_bufsize_max); + CU_ASSERT(8000 == inflater.settings_hd_table_bufsize_max); + + /* This will emit encoding context update with header table size 4096 */ + rv = nghttp2_hd_deflate_hd_bufs(&deflater, &bufs, nva, 2); + blocklen = nghttp2_bufs_len(&bufs); + + CU_ASSERT(0 == rv); + CU_ASSERT(blocklen > 0); + CU_ASSERT(2 == deflater.ctx.hd_table.len); + CU_ASSERT(4096 == deflater.ctx.hd_table_bufsize_max); + + CU_ASSERT(blocklen == inflate_hd(&inflater, &out, &bufs, 0, mem)); + CU_ASSERT(2 == inflater.ctx.hd_table.len); + CU_ASSERT(4096 == inflater.ctx.hd_table_bufsize_max); + CU_ASSERT(8000 == inflater.settings_hd_table_bufsize_max); + + nva_out_reset(&out, mem); + nghttp2_bufs_reset(&bufs); + + /* inflater changes header table size to 1024 */ + CU_ASSERT(0 == nghttp2_hd_inflate_change_table_size(&inflater, 1024)); + CU_ASSERT(0 == nghttp2_hd_deflate_change_table_size(&deflater, 1024)); + + CU_ASSERT(1024 == deflater.ctx.hd_table_bufsize_max); + + CU_ASSERT(1024 == inflater.ctx.hd_table_bufsize_max); + CU_ASSERT(1024 == inflater.settings_hd_table_bufsize_max); + + rv = nghttp2_hd_deflate_hd_bufs(&deflater, &bufs, nva, 2); + blocklen = nghttp2_bufs_len(&bufs); + + CU_ASSERT(0 == rv); + CU_ASSERT(blocklen > 0); + CU_ASSERT(2 == deflater.ctx.hd_table.len); + CU_ASSERT(1024 == deflater.ctx.hd_table_bufsize_max); + + CU_ASSERT(blocklen == inflate_hd(&inflater, &out, &bufs, 0, mem)); + CU_ASSERT(2 == inflater.ctx.hd_table.len); + CU_ASSERT(1024 == inflater.ctx.hd_table_bufsize_max); + CU_ASSERT(1024 == inflater.settings_hd_table_bufsize_max); + + nva_out_reset(&out, mem); + nghttp2_bufs_reset(&bufs); + + /* inflater changes header table size to 0 */ + CU_ASSERT(0 == nghttp2_hd_inflate_change_table_size(&inflater, 0)); + CU_ASSERT(0 == nghttp2_hd_deflate_change_table_size(&deflater, 0)); + + CU_ASSERT(0 == deflater.ctx.hd_table.len); + CU_ASSERT(0 == deflater.ctx.hd_table_bufsize_max); + + CU_ASSERT(0 == inflater.ctx.hd_table.len); + CU_ASSERT(0 == inflater.ctx.hd_table_bufsize_max); + CU_ASSERT(0 == inflater.settings_hd_table_bufsize_max); + + rv = nghttp2_hd_deflate_hd_bufs(&deflater, &bufs, nva, 2); + blocklen = nghttp2_bufs_len(&bufs); + + CU_ASSERT(0 == rv); + CU_ASSERT(blocklen > 0); + CU_ASSERT(0 == deflater.ctx.hd_table.len); + CU_ASSERT(0 == deflater.ctx.hd_table_bufsize_max); + + CU_ASSERT(blocklen == inflate_hd(&inflater, &out, &bufs, 0, mem)); + CU_ASSERT(0 == inflater.ctx.hd_table.len); + CU_ASSERT(0 == inflater.ctx.hd_table_bufsize_max); + CU_ASSERT(0 == inflater.settings_hd_table_bufsize_max); + + nva_out_reset(&out, mem); + nghttp2_bufs_reset(&bufs); + + nghttp2_bufs_free(&bufs); + nghttp2_hd_inflate_free(&inflater); + nghttp2_hd_deflate_free(&deflater); + + /* Check table buffer is expanded */ + frame_pack_bufs_init(&bufs); + + nghttp2_hd_deflate_init2(&deflater, 8192, mem); + nghttp2_hd_inflate_init(&inflater, mem); + + /* First inflater changes header table size to 8000 */ + CU_ASSERT(0 == nghttp2_hd_inflate_change_table_size(&inflater, 8000)); + CU_ASSERT(0 == nghttp2_hd_deflate_change_table_size(&deflater, 8000)); + + CU_ASSERT(8000 == deflater.ctx.hd_table_bufsize_max); + + CU_ASSERT(8000 == inflater.ctx.hd_table_bufsize_max); + CU_ASSERT(8000 == inflater.settings_hd_table_bufsize_max); + + rv = nghttp2_hd_deflate_hd_bufs(&deflater, &bufs, nva, 2); + blocklen = nghttp2_bufs_len(&bufs); + + CU_ASSERT(0 == rv); + CU_ASSERT(blocklen > 0); + CU_ASSERT(2 == deflater.ctx.hd_table.len); + CU_ASSERT(8000 == deflater.ctx.hd_table_bufsize_max); + + CU_ASSERT(blocklen == inflate_hd(&inflater, &out, &bufs, 0, mem)); + CU_ASSERT(2 == inflater.ctx.hd_table.len); + CU_ASSERT(8000 == inflater.ctx.hd_table_bufsize_max); + CU_ASSERT(8000 == inflater.settings_hd_table_bufsize_max); + + nva_out_reset(&out, mem); + nghttp2_bufs_reset(&bufs); + + CU_ASSERT(0 == nghttp2_hd_inflate_change_table_size(&inflater, 16383)); + CU_ASSERT(0 == nghttp2_hd_deflate_change_table_size(&deflater, 16383)); + + CU_ASSERT(8192 == deflater.ctx.hd_table_bufsize_max); + + CU_ASSERT(16383 == inflater.ctx.hd_table_bufsize_max); + CU_ASSERT(16383 == inflater.settings_hd_table_bufsize_max); + + rv = nghttp2_hd_deflate_hd_bufs(&deflater, &bufs, nva, 2); + blocklen = nghttp2_bufs_len(&bufs); + + CU_ASSERT(0 == rv); + CU_ASSERT(blocklen > 0); + CU_ASSERT(2 == deflater.ctx.hd_table.len); + CU_ASSERT(8192 == deflater.ctx.hd_table_bufsize_max); + + CU_ASSERT(blocklen == inflate_hd(&inflater, &out, &bufs, 0, mem)); + CU_ASSERT(2 == inflater.ctx.hd_table.len); + CU_ASSERT(8192 == inflater.ctx.hd_table_bufsize_max); + CU_ASSERT(16383 == inflater.settings_hd_table_bufsize_max); + + nva_out_reset(&out, mem); + nghttp2_bufs_reset(&bufs); + + /* Lastly, check the error condition */ + + rv = nghttp2_hd_emit_table_size(&bufs, 25600); + CU_ASSERT(rv == 0); + CU_ASSERT(NGHTTP2_ERR_HEADER_COMP == + inflate_hd(&inflater, &out, &bufs, 0, mem)); + + nva_out_reset(&out, mem); + nghttp2_bufs_reset(&bufs); + + nghttp2_hd_inflate_free(&inflater); + nghttp2_hd_deflate_free(&deflater); + + /* Check that encoder can handle the case where its allowable buffer + size is less than default size, 4096 */ + nghttp2_hd_deflate_init2(&deflater, 1024, mem); + nghttp2_hd_inflate_init(&inflater, mem); + + CU_ASSERT(1024 == deflater.ctx.hd_table_bufsize_max); + + /* This emits context update with buffer size 1024 */ + rv = nghttp2_hd_deflate_hd_bufs(&deflater, &bufs, nva, 2); + blocklen = nghttp2_bufs_len(&bufs); + + CU_ASSERT(0 == rv); + CU_ASSERT(blocklen > 0); + CU_ASSERT(2 == deflater.ctx.hd_table.len); + CU_ASSERT(1024 == deflater.ctx.hd_table_bufsize_max); + + CU_ASSERT(blocklen == inflate_hd(&inflater, &out, &bufs, 0, mem)); + CU_ASSERT(2 == inflater.ctx.hd_table.len); + CU_ASSERT(1024 == inflater.ctx.hd_table_bufsize_max); + CU_ASSERT(4096 == inflater.settings_hd_table_bufsize_max); + + nva_out_reset(&out, mem); + nghttp2_bufs_reset(&bufs); + + nghttp2_hd_inflate_free(&inflater); + nghttp2_hd_deflate_free(&deflater); + + /* Check that table size UINT32_MAX can be received */ + nghttp2_hd_deflate_init2(&deflater, UINT32_MAX, mem); + nghttp2_hd_inflate_init(&inflater, mem); + + CU_ASSERT(0 == nghttp2_hd_inflate_change_table_size(&inflater, UINT32_MAX)); + CU_ASSERT(0 == nghttp2_hd_deflate_change_table_size(&deflater, UINT32_MAX)); + + rv = nghttp2_hd_deflate_hd_bufs(&deflater, &bufs, nva, 2); + blocklen = nghttp2_bufs_len(&bufs); + + CU_ASSERT(0 == rv); + CU_ASSERT(UINT32_MAX == deflater.ctx.hd_table_bufsize_max); + + CU_ASSERT(blocklen == inflate_hd(&inflater, &out, &bufs, 0, mem)); + CU_ASSERT(UINT32_MAX == inflater.ctx.hd_table_bufsize_max); + CU_ASSERT(UINT32_MAX == inflater.settings_hd_table_bufsize_max); + + nva_out_reset(&out, mem); + nghttp2_bufs_reset(&bufs); + + nghttp2_hd_inflate_free(&inflater); + nghttp2_hd_deflate_free(&deflater); + + /* Check that context update emitted twice */ + nghttp2_hd_deflate_init2(&deflater, 4096, mem); + nghttp2_hd_inflate_init(&inflater, mem); + + CU_ASSERT(0 == nghttp2_hd_inflate_change_table_size(&inflater, 0)); + CU_ASSERT(0 == nghttp2_hd_inflate_change_table_size(&inflater, 3000)); + CU_ASSERT(0 == nghttp2_hd_deflate_change_table_size(&deflater, 0)); + CU_ASSERT(0 == nghttp2_hd_deflate_change_table_size(&deflater, 3000)); + + CU_ASSERT(0 == deflater.min_hd_table_bufsize_max); + CU_ASSERT(3000 == deflater.ctx.hd_table_bufsize_max); + + rv = nghttp2_hd_deflate_hd_bufs(&deflater, &bufs, nva2, 1); + blocklen = nghttp2_bufs_len(&bufs); + + CU_ASSERT(0 == rv); + CU_ASSERT(3 < blocklen); + CU_ASSERT(3000 == deflater.ctx.hd_table_bufsize_max); + CU_ASSERT(UINT32_MAX == deflater.min_hd_table_bufsize_max); + + CU_ASSERT(blocklen == inflate_hd(&inflater, &out, &bufs, 0, mem)); + CU_ASSERT(3000 == inflater.ctx.hd_table_bufsize_max); + CU_ASSERT(3000 == inflater.settings_hd_table_bufsize_max); + + nva_out_reset(&out, mem); + nghttp2_bufs_reset(&bufs); + + nghttp2_hd_inflate_free(&inflater); + nghttp2_hd_deflate_free(&deflater); + + nghttp2_bufs_free(&bufs); +} + +static void check_deflate_inflate(nghttp2_hd_deflater *deflater, + nghttp2_hd_inflater *inflater, + nghttp2_nv *nva, size_t nvlen, + nghttp2_mem *mem) { + nghttp2_bufs bufs; + ssize_t blocklen; + nva_out out; + int rv; + + frame_pack_bufs_init(&bufs); + + nva_out_init(&out); + rv = nghttp2_hd_deflate_hd_bufs(deflater, &bufs, nva, nvlen); + blocklen = nghttp2_bufs_len(&bufs); + + CU_ASSERT(0 == rv); + CU_ASSERT(blocklen >= 0); + + CU_ASSERT(blocklen == inflate_hd(inflater, &out, &bufs, 0, mem)); + + CU_ASSERT(nvlen == out.nvlen); + assert_nv_equal(nva, out.nva, nvlen, mem); + + nva_out_reset(&out, mem); + nghttp2_bufs_free(&bufs); +} + +void test_nghttp2_hd_deflate_inflate(void) { + nghttp2_hd_deflater deflater; + nghttp2_hd_inflater inflater; + nghttp2_nv nv1[] = { + MAKE_NV(":status", "200 OK"), + MAKE_NV("access-control-allow-origin", "*"), + MAKE_NV("cache-control", "private, max-age=0, must-revalidate"), + MAKE_NV("content-length", "76073"), + MAKE_NV("content-type", "text/html"), + MAKE_NV("date", "Sat, 27 Jul 2013 06:22:12 GMT"), + MAKE_NV("expires", "Sat, 27 Jul 2013 06:22:12 GMT"), + MAKE_NV("server", "Apache"), + MAKE_NV("vary", "foobar"), + MAKE_NV("via", "1.1 alphabravo (squid/3.x.x), 1.1 nghttpx"), + MAKE_NV("x-cache", "MISS from alphabravo"), + MAKE_NV("x-cache-action", "MISS"), + MAKE_NV("x-cache-age", "0"), + MAKE_NV("x-cache-lookup", "MISS from alphabravo:3128"), + MAKE_NV("x-lb-nocache", "true"), + }; + nghttp2_nv nv2[] = { + MAKE_NV(":status", "304 Not Modified"), + MAKE_NV("age", "0"), + MAKE_NV("cache-control", "max-age=56682045"), + MAKE_NV("content-type", "text/css"), + MAKE_NV("date", "Sat, 27 Jul 2013 06:22:12 GMT"), + MAKE_NV("expires", "Thu, 14 May 2015 07:22:57 GMT"), + MAKE_NV("last-modified", "Tue, 14 May 2013 07:22:15 GMT"), + MAKE_NV("vary", "Accept-Encoding"), + MAKE_NV("via", "1.1 alphabravo (squid/3.x.x), 1.1 nghttpx"), + MAKE_NV("x-cache", "HIT from alphabravo"), + MAKE_NV("x-cache-lookup", "HIT from alphabravo:3128")}; + nghttp2_nv nv3[] = { + MAKE_NV(":status", "304 Not Modified"), + MAKE_NV("age", "0"), + MAKE_NV("cache-control", "max-age=56682072"), + MAKE_NV("content-type", "text/css"), + MAKE_NV("date", "Sat, 27 Jul 2013 06:22:12 GMT"), + MAKE_NV("expires", "Thu, 14 May 2015 07:23:24 GMT"), + MAKE_NV("last-modified", "Tue, 14 May 2013 07:22:13 GMT"), + MAKE_NV("vary", "Accept-Encoding"), + MAKE_NV("via", "1.1 alphabravo (squid/3.x.x), 1.1 nghttpx"), + MAKE_NV("x-cache", "HIT from alphabravo"), + MAKE_NV("x-cache-lookup", "HIT from alphabravo:3128"), + }; + nghttp2_nv nv4[] = { + MAKE_NV(":status", "304 Not Modified"), + MAKE_NV("age", "0"), + MAKE_NV("cache-control", "max-age=56682022"), + MAKE_NV("content-type", "text/css"), + MAKE_NV("date", "Sat, 27 Jul 2013 06:22:12 GMT"), + MAKE_NV("expires", "Thu, 14 May 2015 07:22:34 GMT"), + MAKE_NV("last-modified", "Tue, 14 May 2013 07:22:14 GMT"), + MAKE_NV("vary", "Accept-Encoding"), + MAKE_NV("via", "1.1 alphabravo (squid/3.x.x), 1.1 nghttpx"), + MAKE_NV("x-cache", "HIT from alphabravo"), + MAKE_NV("x-cache-lookup", "HIT from alphabravo:3128"), + }; + nghttp2_nv nv5[] = { + MAKE_NV(":status", "304 Not Modified"), + MAKE_NV("age", "0"), + MAKE_NV("cache-control", "max-age=4461139"), + MAKE_NV("content-type", "application/x-javascript"), + MAKE_NV("date", "Sat, 27 Jul 2013 06:22:12 GMT"), + MAKE_NV("expires", "Mon, 16 Sep 2013 21:34:31 GMT"), + MAKE_NV("last-modified", "Thu, 05 May 2011 09:15:59 GMT"), + MAKE_NV("vary", "Accept-Encoding"), + MAKE_NV("via", "1.1 alphabravo (squid/3.x.x), 1.1 nghttpx"), + MAKE_NV("x-cache", "HIT from alphabravo"), + MAKE_NV("x-cache-lookup", "HIT from alphabravo:3128"), + }; + nghttp2_nv nv6[] = { + MAKE_NV(":status", "304 Not Modified"), + MAKE_NV("age", "0"), + MAKE_NV("cache-control", "max-age=18645951"), + MAKE_NV("content-type", "application/x-javascript"), + MAKE_NV("date", "Sat, 27 Jul 2013 06:22:12 GMT"), + MAKE_NV("expires", "Fri, 28 Feb 2014 01:48:03 GMT"), + MAKE_NV("last-modified", "Tue, 12 Jul 2011 16:02:59 GMT"), + MAKE_NV("vary", "Accept-Encoding"), + MAKE_NV("via", "1.1 alphabravo (squid/3.x.x), 1.1 nghttpx"), + MAKE_NV("x-cache", "HIT from alphabravo"), + MAKE_NV("x-cache-lookup", "HIT from alphabravo:3128"), + }; + nghttp2_nv nv7[] = { + MAKE_NV(":status", "304 Not Modified"), + MAKE_NV("age", "0"), + MAKE_NV("cache-control", "max-age=31536000"), + MAKE_NV("content-type", "application/javascript"), + MAKE_NV("date", "Sat, 27 Jul 2013 06:22:12 GMT"), + MAKE_NV("etag", "\"6807-4dc5b54e0dcc0\""), + MAKE_NV("expires", "Wed, 21 May 2014 08:32:17 GMT"), + MAKE_NV("last-modified", "Fri, 10 May 2013 11:18:51 GMT"), + MAKE_NV("via", "1.1 alphabravo (squid/3.x.x), 1.1 nghttpx"), + MAKE_NV("x-cache", "HIT from alphabravo"), + MAKE_NV("x-cache-lookup", "HIT from alphabravo:3128"), + }; + nghttp2_nv nv8[] = { + MAKE_NV(":status", "304 Not Modified"), + MAKE_NV("age", "0"), + MAKE_NV("cache-control", "max-age=31536000"), + MAKE_NV("content-type", "application/javascript"), + MAKE_NV("date", "Sat, 27 Jul 2013 06:22:12 GMT"), + MAKE_NV("etag", "\"41c6-4de7d28585b00\""), + MAKE_NV("expires", "Thu, 12 Jun 2014 10:00:58 GMT"), + MAKE_NV("last-modified", "Thu, 06 Jun 2013 14:30:36 GMT"), + MAKE_NV("via", "1.1 alphabravo (squid/3.x.x), 1.1 nghttpx"), + MAKE_NV("x-cache", "HIT from alphabravo"), + MAKE_NV("x-cache-lookup", "HIT from alphabravo:3128"), + }; + nghttp2_nv nv9[] = { + MAKE_NV(":status", "304 Not Modified"), + MAKE_NV("age", "0"), + MAKE_NV("cache-control", "max-age=31536000"), + MAKE_NV("content-type", "application/javascript"), + MAKE_NV("date", "Sat, 27 Jul 2013 06:22:12 GMT"), + MAKE_NV("etag", "\"19d6e-4dc5b35a541c0\""), + MAKE_NV("expires", "Wed, 21 May 2014 08:32:18 GMT"), + MAKE_NV("last-modified", "Fri, 10 May 2013 11:10:07 GMT"), + MAKE_NV("via", "1.1 alphabravo (squid/3.x.x), 1.1 nghttpx"), + MAKE_NV("x-cache", "HIT from alphabravo"), + MAKE_NV("x-cache-lookup", "HIT from alphabravo:3128"), + }; + nghttp2_nv nv10[] = { + MAKE_NV(":status", "304 Not Modified"), + MAKE_NV("age", "0"), + MAKE_NV("cache-control", "max-age=56682045"), + MAKE_NV("content-type", "text/css"), + MAKE_NV("date", "Sat, 27 Jul 2013 06:22:12 GMT"), + MAKE_NV("expires", "Thu, 14 May 2015 07:22:57 GMT"), + MAKE_NV("last-modified", "Tue, 14 May 2013 07:21:53 GMT"), + MAKE_NV("vary", "Accept-Encoding"), + MAKE_NV("via", "1.1 alphabravo (squid/3.x.x), 1.1 nghttpx"), + MAKE_NV("x-cache", "HIT from alphabravo"), + MAKE_NV("x-cache-lookup", "HIT from alphabravo:3128"), + }; + nghttp2_mem *mem; + + mem = nghttp2_mem_default(); + + nghttp2_hd_deflate_init(&deflater, mem); + nghttp2_hd_inflate_init(&inflater, mem); + + check_deflate_inflate(&deflater, &inflater, nv1, ARRLEN(nv1), mem); + check_deflate_inflate(&deflater, &inflater, nv2, ARRLEN(nv2), mem); + check_deflate_inflate(&deflater, &inflater, nv3, ARRLEN(nv3), mem); + check_deflate_inflate(&deflater, &inflater, nv4, ARRLEN(nv4), mem); + check_deflate_inflate(&deflater, &inflater, nv5, ARRLEN(nv5), mem); + check_deflate_inflate(&deflater, &inflater, nv6, ARRLEN(nv6), mem); + check_deflate_inflate(&deflater, &inflater, nv7, ARRLEN(nv7), mem); + check_deflate_inflate(&deflater, &inflater, nv8, ARRLEN(nv8), mem); + check_deflate_inflate(&deflater, &inflater, nv9, ARRLEN(nv9), mem); + check_deflate_inflate(&deflater, &inflater, nv10, ARRLEN(nv10), mem); + + nghttp2_hd_inflate_free(&inflater); + nghttp2_hd_deflate_free(&deflater); +} + +void test_nghttp2_hd_no_index(void) { + nghttp2_hd_deflater deflater; + nghttp2_hd_inflater inflater; + nghttp2_bufs bufs; + ssize_t blocklen; + nghttp2_nv nva[] = { + MAKE_NV(":method", "GET"), MAKE_NV(":method", "POST"), + MAKE_NV(":path", "/foo"), MAKE_NV("version", "HTTP/1.1"), + MAKE_NV(":method", "GET"), + }; + size_t i; + nva_out out; + int rv; + nghttp2_mem *mem; + + mem = nghttp2_mem_default(); + + /* 1st :method: GET can be indexable, last one is not */ + for (i = 1; i < ARRLEN(nva); ++i) { + nva[i].flags = NGHTTP2_NV_FLAG_NO_INDEX; + } + + frame_pack_bufs_init(&bufs); + + nva_out_init(&out); + + nghttp2_hd_deflate_init(&deflater, mem); + nghttp2_hd_inflate_init(&inflater, mem); + + rv = nghttp2_hd_deflate_hd_bufs(&deflater, &bufs, nva, ARRLEN(nva)); + blocklen = nghttp2_bufs_len(&bufs); + + CU_ASSERT(0 == rv); + CU_ASSERT(blocklen > 0); + CU_ASSERT(blocklen == inflate_hd(&inflater, &out, &bufs, 0, mem)); + + CU_ASSERT(ARRLEN(nva) == out.nvlen); + assert_nv_equal(nva, out.nva, ARRLEN(nva), mem); + + CU_ASSERT(out.nva[0].flags == NGHTTP2_NV_FLAG_NONE); + for (i = 1; i < ARRLEN(nva); ++i) { + CU_ASSERT(out.nva[i].flags == NGHTTP2_NV_FLAG_NO_INDEX); + } + + nva_out_reset(&out, mem); + + nghttp2_bufs_free(&bufs); + nghttp2_hd_inflate_free(&inflater); + nghttp2_hd_deflate_free(&deflater); +} + +void test_nghttp2_hd_deflate_bound(void) { + nghttp2_hd_deflater deflater; + nghttp2_nv nva[] = {MAKE_NV(":method", "GET"), MAKE_NV("alpha", "bravo")}; + nghttp2_bufs bufs; + size_t bound, bound2; + nghttp2_mem *mem; + + mem = nghttp2_mem_default(); + frame_pack_bufs_init(&bufs); + + nghttp2_hd_deflate_init(&deflater, mem); + + bound = nghttp2_hd_deflate_bound(&deflater, nva, ARRLEN(nva)); + + CU_ASSERT(12 + 6 * 2 * 2 + nva[0].namelen + nva[0].valuelen + nva[1].namelen + + nva[1].valuelen == + bound); + + nghttp2_hd_deflate_hd_bufs(&deflater, &bufs, nva, ARRLEN(nva)); + + CU_ASSERT(bound > (size_t)nghttp2_bufs_len(&bufs)); + + bound2 = nghttp2_hd_deflate_bound(&deflater, nva, ARRLEN(nva)); + + CU_ASSERT(bound == bound2); + + nghttp2_bufs_free(&bufs); + nghttp2_hd_deflate_free(&deflater); +} + +void test_nghttp2_hd_public_api(void) { + nghttp2_hd_deflater *deflater; + nghttp2_hd_inflater *inflater; + nghttp2_nv nva[] = {MAKE_NV("alpha", "bravo"), MAKE_NV("charlie", "delta")}; + uint8_t buf[4096]; + size_t buflen; + ssize_t blocklen; + nghttp2_bufs bufs; + nghttp2_mem *mem; + + mem = nghttp2_mem_default(); + + CU_ASSERT(0 == nghttp2_hd_deflate_new(&deflater, 4096)); + CU_ASSERT(0 == nghttp2_hd_inflate_new(&inflater)); + + buflen = nghttp2_hd_deflate_bound(deflater, nva, ARRLEN(nva)); + + blocklen = nghttp2_hd_deflate_hd(deflater, buf, buflen, nva, ARRLEN(nva)); + + CU_ASSERT(blocklen > 0); + + nghttp2_bufs_wrap_init(&bufs, buf, blocklen, mem); + bufs.head->buf.last += blocklen; + + CU_ASSERT(blocklen == inflate_hd(inflater, NULL, &bufs, 0, mem)); + + nghttp2_bufs_wrap_free(&bufs); + + nghttp2_hd_inflate_del(inflater); + nghttp2_hd_deflate_del(deflater); + + /* See NGHTTP2_ERR_INSUFF_BUFSIZE */ + CU_ASSERT(0 == nghttp2_hd_deflate_new(&deflater, 4096)); + + blocklen = + nghttp2_hd_deflate_hd(deflater, buf, blocklen - 1, nva, ARRLEN(nva)); + + CU_ASSERT(NGHTTP2_ERR_INSUFF_BUFSIZE == blocklen); + + nghttp2_hd_deflate_del(deflater); +} + +static size_t encode_length(uint8_t *buf, uint64_t n, size_t prefix) { + size_t k = (1 << prefix) - 1; + size_t len = 0; + *buf &= ~k; + if (n >= k) { + *buf++ |= k; + n -= k; + ++len; + } else { + *buf++ |= n; + return 1; + } + do { + ++len; + if (n >= 128) { + *buf++ = (1 << 7) | (n & 0x7f); + n >>= 7; + } else { + *buf++ = (uint8_t)n; + break; + } + } while (n); + return len; +} + +void test_nghttp2_hd_decode_length(void) { + uint32_t out; + size_t shift; + int final; + uint8_t buf[16]; + uint8_t *bufp; + size_t len; + ssize_t rv; + size_t i; + + memset(buf, 0, sizeof(buf)); + len = encode_length(buf, UINT32_MAX, 7); + + rv = nghttp2_hd_decode_length(&out, &shift, &final, 0, 0, buf, buf + len, 7); + + CU_ASSERT((ssize_t)len == rv); + CU_ASSERT(0 != final); + CU_ASSERT(UINT32_MAX == out); + + /* Make sure that we can decode integer if we feed 1 byte at a + time */ + out = 0; + shift = 0; + final = 0; + bufp = buf; + + for (i = 0; i < len; ++i, ++bufp) { + rv = nghttp2_hd_decode_length(&out, &shift, &final, out, shift, bufp, + bufp + 1, 7); + + CU_ASSERT(rv == 1); + + if (final) { + break; + } + } + + CU_ASSERT(i == len - 1); + CU_ASSERT(0 != final); + CU_ASSERT(UINT32_MAX == out); + + /* Check overflow case */ + memset(buf, 0, sizeof(buf)); + len = encode_length(buf, 1ll << 32, 7); + + rv = nghttp2_hd_decode_length(&out, &shift, &final, 0, 0, buf, buf + len, 7); + + CU_ASSERT(-1 == rv); +} + +void test_nghttp2_hd_huff_encode(void) { + int rv; + ssize_t len; + nghttp2_bufs bufs, outbufs; + nghttp2_hd_huff_decode_context ctx; + const uint8_t t1[] = {22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, + 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0}; + + frame_pack_bufs_init(&bufs); + frame_pack_bufs_init(&outbufs); + + rv = nghttp2_hd_huff_encode(&bufs, t1, sizeof(t1)); + + CU_ASSERT(rv == 0); + + nghttp2_hd_huff_decode_context_init(&ctx); + + len = nghttp2_hd_huff_decode(&ctx, &outbufs, bufs.cur->buf.pos, + nghttp2_bufs_len(&bufs), 1); + + CU_ASSERT(nghttp2_bufs_len(&bufs) == len); + CU_ASSERT((ssize_t)sizeof(t1) == nghttp2_bufs_len(&outbufs)); + + CU_ASSERT(0 == memcmp(t1, outbufs.cur->buf.pos, sizeof(t1))); + + nghttp2_bufs_free(&bufs); + nghttp2_bufs_free(&outbufs); +} diff --git a/tests/nghttp2_hd_test.h b/tests/nghttp2_hd_test.h new file mode 100644 index 0000000..e41fad7 --- /dev/null +++ b/tests/nghttp2_hd_test.h @@ -0,0 +1,47 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2013 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGHTTP2_HD_TEST_H +#define NGHTTP2_HD_TEST_H + +void test_nghttp2_hd_deflate(void); +void test_nghttp2_hd_deflate_same_indexed_repr(void); +void test_nghttp2_hd_inflate_indexed(void); +void test_nghttp2_hd_inflate_indname_noinc(void); +void test_nghttp2_hd_inflate_indname_inc(void); +void test_nghttp2_hd_inflate_indname_inc_eviction(void); +void test_nghttp2_hd_inflate_newname_noinc(void); +void test_nghttp2_hd_inflate_newname_inc(void); +void test_nghttp2_hd_inflate_clearall_inc(void); +void test_nghttp2_hd_inflate_zero_length_huffman(void); +void test_nghttp2_hd_ringbuf_reserve(void); +void test_nghttp2_hd_change_table_size(void); +void test_nghttp2_hd_deflate_inflate(void); +void test_nghttp2_hd_no_index(void); +void test_nghttp2_hd_deflate_bound(void); +void test_nghttp2_hd_public_api(void); +void test_nghttp2_hd_decode_length(void); +void test_nghttp2_hd_huff_encode(void); + +#endif /* NGHTTP2_HD_TEST_H */ diff --git a/tests/nghttp2_helper_test.c b/tests/nghttp2_helper_test.c new file mode 100644 index 0000000..b29e67b --- /dev/null +++ b/tests/nghttp2_helper_test.c @@ -0,0 +1,169 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2013 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "nghttp2_helper_test.h" + +#include + +#include "nghttp2_helper.h" + +void test_nghttp2_adjust_local_window_size(void) { + int32_t local_window_size = 100; + int32_t recv_window_size = 50; + int32_t recv_reduction = 0; + int32_t delta; + + delta = 0; + CU_ASSERT(0 == nghttp2_adjust_local_window_size(&local_window_size, + &recv_window_size, + &recv_reduction, &delta)); + CU_ASSERT(100 == local_window_size); + CU_ASSERT(50 == recv_window_size); + CU_ASSERT(0 == recv_reduction); + CU_ASSERT(0 == delta); + + delta = 49; + CU_ASSERT(0 == nghttp2_adjust_local_window_size(&local_window_size, + &recv_window_size, + &recv_reduction, &delta)); + CU_ASSERT(100 == local_window_size); + CU_ASSERT(1 == recv_window_size); + CU_ASSERT(0 == recv_reduction); + CU_ASSERT(49 == delta); + + delta = 1; + CU_ASSERT(0 == nghttp2_adjust_local_window_size(&local_window_size, + &recv_window_size, + &recv_reduction, &delta)); + CU_ASSERT(100 == local_window_size); + CU_ASSERT(0 == recv_window_size); + CU_ASSERT(0 == recv_reduction); + CU_ASSERT(1 == delta); + + delta = 1; + CU_ASSERT(0 == nghttp2_adjust_local_window_size(&local_window_size, + &recv_window_size, + &recv_reduction, &delta)); + CU_ASSERT(101 == local_window_size); + CU_ASSERT(0 == recv_window_size); + CU_ASSERT(0 == recv_reduction); + CU_ASSERT(1 == delta); + + delta = -1; + CU_ASSERT(0 == nghttp2_adjust_local_window_size(&local_window_size, + &recv_window_size, + &recv_reduction, &delta)); + CU_ASSERT(100 == local_window_size); + CU_ASSERT(-1 == recv_window_size); + CU_ASSERT(1 == recv_reduction); + CU_ASSERT(0 == delta); + + delta = 1; + CU_ASSERT(0 == nghttp2_adjust_local_window_size(&local_window_size, + &recv_window_size, + &recv_reduction, &delta)); + CU_ASSERT(101 == local_window_size); + CU_ASSERT(0 == recv_window_size); + CU_ASSERT(0 == recv_reduction); + CU_ASSERT(0 == delta); + + delta = 100; + CU_ASSERT(0 == nghttp2_adjust_local_window_size(&local_window_size, + &recv_window_size, + &recv_reduction, &delta)); + CU_ASSERT(201 == local_window_size); + CU_ASSERT(0 == recv_window_size); + CU_ASSERT(0 == recv_reduction); + CU_ASSERT(100 == delta); + + delta = -3; + CU_ASSERT(0 == nghttp2_adjust_local_window_size(&local_window_size, + &recv_window_size, + &recv_reduction, &delta)); + CU_ASSERT(198 == local_window_size); + CU_ASSERT(-3 == recv_window_size); + CU_ASSERT(3 == recv_reduction); + CU_ASSERT(0 == delta); + + recv_window_size += 3; + + delta = 3; + CU_ASSERT(0 == nghttp2_adjust_local_window_size(&local_window_size, + &recv_window_size, + &recv_reduction, &delta)); + CU_ASSERT(201 == local_window_size); + CU_ASSERT(3 == recv_window_size); + CU_ASSERT(0 == recv_reduction); + CU_ASSERT(0 == delta); + + local_window_size = 100; + recv_window_size = 50; + recv_reduction = 0; + delta = INT32_MAX; + CU_ASSERT(NGHTTP2_ERR_FLOW_CONTROL == + nghttp2_adjust_local_window_size(&local_window_size, + &recv_window_size, &recv_reduction, + &delta)); + CU_ASSERT(100 == local_window_size); + CU_ASSERT(50 == recv_window_size); + CU_ASSERT(0 == recv_reduction); + CU_ASSERT(INT32_MAX == delta); + + delta = INT32_MIN; + CU_ASSERT(NGHTTP2_ERR_FLOW_CONTROL == + nghttp2_adjust_local_window_size(&local_window_size, + &recv_window_size, &recv_reduction, + &delta)); + CU_ASSERT(100 == local_window_size); + CU_ASSERT(50 == recv_window_size); + CU_ASSERT(0 == recv_reduction); + CU_ASSERT(INT32_MIN == delta); +} + +#define check_header_name(S) \ + nghttp2_check_header_name((const uint8_t *)S, sizeof(S) - 1) + +void test_nghttp2_check_header_name(void) { + CU_ASSERT(check_header_name(":path")); + CU_ASSERT(check_header_name("path")); + CU_ASSERT(check_header_name("!#$%&'*+-.^_`|~")); + CU_ASSERT(!check_header_name(":PATH")); + CU_ASSERT(!check_header_name("path:")); + CU_ASSERT(!check_header_name("")); + CU_ASSERT(!check_header_name(":")); +} + +#define check_header_value(S) \ + nghttp2_check_header_value((const uint8_t *)S, sizeof(S) - 1) + +void test_nghttp2_check_header_value(void) { + uint8_t goodval[] = {'a', 'b', 0x80u, 'c', 0xffu, 'd', '\t', ' '}; + uint8_t badval1[] = {'a', 0x1fu, 'b'}; + uint8_t badval2[] = {'a', 0x7fu, 'b'}; + + CU_ASSERT(check_header_value(" !|}~")); + CU_ASSERT(check_header_value(goodval)); + CU_ASSERT(!check_header_value(badval1)); + CU_ASSERT(!check_header_value(badval2)); +} diff --git a/tests/nghttp2_helper_test.h b/tests/nghttp2_helper_test.h new file mode 100644 index 0000000..173fde6 --- /dev/null +++ b/tests/nghttp2_helper_test.h @@ -0,0 +1,32 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2013 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGHTTP2_HELPER_TEST_H +#define NGHTTP2_HELPER_TEST_H + +void test_nghttp2_adjust_local_window_size(void); +void test_nghttp2_check_header_name(void); +void test_nghttp2_check_header_value(void); + +#endif /* NGHTTP2_HELPER_TEST_H */ diff --git a/tests/nghttp2_map_test.c b/tests/nghttp2_map_test.c new file mode 100644 index 0000000..c2ec825 --- /dev/null +++ b/tests/nghttp2_map_test.c @@ -0,0 +1,176 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "nghttp2_map_test.h" + +#include + +#include "nghttp2_map.h" + +typedef struct strentry { + nghttp2_map_entry map_entry; + const char *str; +} strentry; + +static void strentry_init(strentry *entry, key_type key, const char *str) { + nghttp2_map_entry_init(&entry->map_entry, key); + entry->str = str; +} + +void test_nghttp2_map(void) { + strentry foo, FOO, bar, baz, shrubbery; + nghttp2_map map; + nghttp2_map_init(&map, nghttp2_mem_default()); + + strentry_init(&foo, 1, "foo"); + strentry_init(&FOO, 1, "FOO"); + strentry_init(&bar, 2, "bar"); + strentry_init(&baz, 3, "baz"); + strentry_init(&shrubbery, 4, "shrubbery"); + + CU_ASSERT(0 == nghttp2_map_insert(&map, &foo.map_entry)); + CU_ASSERT(strcmp("foo", ((strentry *)nghttp2_map_find(&map, 1))->str) == 0); + CU_ASSERT(1 == nghttp2_map_size(&map)); + + CU_ASSERT(NGHTTP2_ERR_INVALID_ARGUMENT == + nghttp2_map_insert(&map, &FOO.map_entry)); + + CU_ASSERT(1 == nghttp2_map_size(&map)); + CU_ASSERT(strcmp("foo", ((strentry *)nghttp2_map_find(&map, 1))->str) == 0); + + CU_ASSERT(0 == nghttp2_map_insert(&map, &bar.map_entry)); + CU_ASSERT(2 == nghttp2_map_size(&map)); + + CU_ASSERT(0 == nghttp2_map_insert(&map, &baz.map_entry)); + CU_ASSERT(3 == nghttp2_map_size(&map)); + + CU_ASSERT(0 == nghttp2_map_insert(&map, &shrubbery.map_entry)); + CU_ASSERT(4 == nghttp2_map_size(&map)); + + CU_ASSERT(strcmp("baz", ((strentry *)nghttp2_map_find(&map, 3))->str) == 0); + + nghttp2_map_remove(&map, 3); + CU_ASSERT(3 == nghttp2_map_size(&map)); + CU_ASSERT(NULL == nghttp2_map_find(&map, 3)); + + nghttp2_map_remove(&map, 1); + CU_ASSERT(2 == nghttp2_map_size(&map)); + CU_ASSERT(NULL == nghttp2_map_find(&map, 1)); + + /* Erasing non-existent entry */ + nghttp2_map_remove(&map, 1); + CU_ASSERT(2 == nghttp2_map_size(&map)); + CU_ASSERT(NULL == nghttp2_map_find(&map, 1)); + + CU_ASSERT(strcmp("bar", ((strentry *)nghttp2_map_find(&map, 2))->str) == 0); + CU_ASSERT(strcmp("shrubbery", ((strentry *)nghttp2_map_find(&map, 4))->str) == + 0); + + nghttp2_map_free(&map); +} + +static void shuffle(int *a, int n) { + int i; + for (i = n - 1; i >= 1; --i) { + size_t j = (int)((double)(i + 1) * rand() / (RAND_MAX + 1.0)); + int t = a[j]; + a[j] = a[i]; + a[i] = t; + } +} + +static int eachfun(nghttp2_map_entry *entry _U_, void *ptr _U_) { return 0; } + +#define NUM_ENT 6000 +strentry arr[NUM_ENT]; +int order[NUM_ENT]; + +void test_nghttp2_map_functional(void) { + nghttp2_map map; + int i; + + nghttp2_map_init(&map, nghttp2_mem_default()); + for (i = 0; i < NUM_ENT; ++i) { + strentry_init(&arr[i], i + 1, "foo"); + order[i] = i + 1; + } + /* insertion */ + shuffle(order, NUM_ENT); + for (i = 0; i < NUM_ENT; ++i) { + CU_ASSERT(0 == nghttp2_map_insert(&map, &arr[order[i] - 1].map_entry)); + } + /* traverse */ + nghttp2_map_each(&map, eachfun, NULL); + /* find */ + shuffle(order, NUM_ENT); + for (i = 0; i < NUM_ENT; ++i) { + nghttp2_map_find(&map, order[i]); + } + /* remove */ + shuffle(order, NUM_ENT); + for (i = 0; i < NUM_ENT; ++i) { + CU_ASSERT(0 == nghttp2_map_remove(&map, order[i])); + } + + /* each_free (but no op function for testing purpose) */ + for (i = 0; i < NUM_ENT; ++i) { + strentry_init(&arr[i], i + 1, "foo"); + } + /* insert once again */ + for (i = 0; i < NUM_ENT; ++i) { + CU_ASSERT(0 == nghttp2_map_insert(&map, &arr[i].map_entry)); + } + nghttp2_map_each_free(&map, eachfun, NULL); + nghttp2_map_free(&map); +} + +static int entry_free(nghttp2_map_entry *entry, void *ptr) { + nghttp2_mem *mem = ptr; + + mem->free(entry, NULL); + return 0; +} + +void test_nghttp2_map_each_free(void) { + nghttp2_mem *mem = nghttp2_mem_default(); + strentry *foo = mem->malloc(sizeof(strentry), NULL), + *bar = mem->malloc(sizeof(strentry), NULL), + *baz = mem->malloc(sizeof(strentry), NULL), + *shrubbery = mem->malloc(sizeof(strentry), NULL); + nghttp2_map map; + nghttp2_map_init(&map, nghttp2_mem_default()); + + strentry_init(foo, 1, "foo"); + strentry_init(bar, 2, "bar"); + strentry_init(baz, 3, "baz"); + strentry_init(shrubbery, 4, "shrubbery"); + + nghttp2_map_insert(&map, &foo->map_entry); + nghttp2_map_insert(&map, &bar->map_entry); + nghttp2_map_insert(&map, &baz->map_entry); + nghttp2_map_insert(&map, &shrubbery->map_entry); + + nghttp2_map_each_free(&map, entry_free, mem); + nghttp2_map_free(&map); +} diff --git a/tests/nghttp2_map_test.h b/tests/nghttp2_map_test.h new file mode 100644 index 0000000..f0b723b --- /dev/null +++ b/tests/nghttp2_map_test.h @@ -0,0 +1,32 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGHTTP2_MAP_TEST_H +#define NGHTTP2_MAP_TEST_H + +void test_nghttp2_map(void); +void test_nghttp2_map_functional(void); +void test_nghttp2_map_each_free(void); + +#endif /* NGHTTP2_MAP_TEST_H */ diff --git a/tests/nghttp2_npn_test.c b/tests/nghttp2_npn_test.c new file mode 100644 index 0000000..cbd65b7 --- /dev/null +++ b/tests/nghttp2_npn_test.c @@ -0,0 +1,72 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Twist Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "nghttp2_npn_test.h" + +#include + +#include +#include + +static void http2(void) { + const unsigned char p[] = {8, 'h', 't', 't', 'p', '/', '1', '.', '1', 2, + 'h', '2', 6, 's', 'p', 'd', 'y', '/', '3'}; + unsigned char outlen; + unsigned char *out; + CU_ASSERT(1 == nghttp2_select_next_protocol(&out, &outlen, p, sizeof(p))); + CU_ASSERT(NGHTTP2_PROTO_VERSION_ID_LEN == outlen); + CU_ASSERT(memcmp(NGHTTP2_PROTO_VERSION_ID, out, outlen) == 0); +} + +static void http11(void) { + const unsigned char spdy[] = { + 6, 's', 'p', 'd', 'y', '/', '4', 8, 's', 'p', 'd', 'y', '/', + '2', '.', '1', 8, 'h', 't', 't', 'p', '/', '1', '.', '1', + }; + unsigned char outlen; + unsigned char *out; + CU_ASSERT(0 == + nghttp2_select_next_protocol(&out, &outlen, spdy, sizeof(spdy))); + CU_ASSERT(8 == outlen); + CU_ASSERT(memcmp("http/1.1", out, outlen) == 0); +} + +static void no_overlap(void) { + const unsigned char spdy[] = { + 6, 's', 'p', 'd', 'y', '/', '4', 8, 's', 'p', 'd', 'y', '/', + '2', '.', '1', 8, 'h', 't', 't', 'p', '/', '1', '.', '0', + }; + unsigned char outlen = 0; + unsigned char *out = NULL; + CU_ASSERT(-1 == + nghttp2_select_next_protocol(&out, &outlen, spdy, sizeof(spdy))); + CU_ASSERT(0 == outlen); + CU_ASSERT(NULL == out); +} + +void test_nghttp2_npn(void) { + http2(); + http11(); + no_overlap(); +} diff --git a/tests/nghttp2_npn_test.h b/tests/nghttp2_npn_test.h new file mode 100644 index 0000000..e806f3a --- /dev/null +++ b/tests/nghttp2_npn_test.h @@ -0,0 +1,30 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Twist Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGHTTP2_NPN_TEST_H +#define NGHTTP2_NPN_TEST_H + +void test_nghttp2_npn(void); + +#endif /* NGHTTP2_NPN_TEST_H */ diff --git a/tests/nghttp2_pq_test.c b/tests/nghttp2_pq_test.c new file mode 100644 index 0000000..a88f75b --- /dev/null +++ b/tests/nghttp2_pq_test.c @@ -0,0 +1,123 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "nghttp2_pq_test.h" + +#include + +#include "nghttp2_pq.h" + +static int pq_less(const void *lhs, const void *rhs) { + return strcmp(lhs, rhs) < 0; +} + +void test_nghttp2_pq(void) { + int i; + nghttp2_pq pq; + nghttp2_pq_init(&pq, pq_less, nghttp2_mem_default()); + CU_ASSERT(nghttp2_pq_empty(&pq)); + CU_ASSERT(0 == nghttp2_pq_size(&pq)); + CU_ASSERT(0 == nghttp2_pq_push(&pq, (void *)"foo")); + CU_ASSERT(0 == nghttp2_pq_empty(&pq)); + CU_ASSERT(1 == nghttp2_pq_size(&pq)); + CU_ASSERT(strcmp("foo", nghttp2_pq_top(&pq)) == 0); + CU_ASSERT(0 == nghttp2_pq_push(&pq, (void *)"bar")); + CU_ASSERT(strcmp("bar", nghttp2_pq_top(&pq)) == 0); + CU_ASSERT(0 == nghttp2_pq_push(&pq, (void *)"baz")); + CU_ASSERT(strcmp("bar", nghttp2_pq_top(&pq)) == 0); + CU_ASSERT(0 == nghttp2_pq_push(&pq, (void *)"C")); + CU_ASSERT(4 == nghttp2_pq_size(&pq)); + CU_ASSERT(strcmp("C", nghttp2_pq_top(&pq)) == 0); + nghttp2_pq_pop(&pq); + CU_ASSERT(3 == nghttp2_pq_size(&pq)); + CU_ASSERT(strcmp("bar", nghttp2_pq_top(&pq)) == 0); + nghttp2_pq_pop(&pq); + CU_ASSERT(strcmp("baz", nghttp2_pq_top(&pq)) == 0); + nghttp2_pq_pop(&pq); + CU_ASSERT(strcmp("foo", nghttp2_pq_top(&pq)) == 0); + nghttp2_pq_pop(&pq); + CU_ASSERT(nghttp2_pq_empty(&pq)); + CU_ASSERT(0 == nghttp2_pq_size(&pq)); + CU_ASSERT(NULL == nghttp2_pq_top(&pq)); + + /* Add bunch of entry to see realloc works */ + for (i = 0; i < 10000; ++i) { + CU_ASSERT(0 == nghttp2_pq_push(&pq, (void *)"foo")); + CU_ASSERT((size_t)(i + 1) == nghttp2_pq_size(&pq)); + } + for (i = 10000; i > 0; --i) { + CU_ASSERT(NULL != nghttp2_pq_top(&pq)); + nghttp2_pq_pop(&pq); + CU_ASSERT((size_t)(i - 1) == nghttp2_pq_size(&pq)); + } + + nghttp2_pq_free(&pq); +} + +typedef struct { + int key; + int val; +} node; + +static int node_less(const void *lhs, const void *rhs) { + node *ln = (node *)lhs; + node *rn = (node *)rhs; + return ln->key < rn->key; +} + +static int node_update(void *item, void *arg _U_) { + node *nd = (node *)item; + if ((nd->key % 2) == 0) { + nd->key *= -1; + return 1; + } else { + return 0; + } +} + +void test_nghttp2_pq_update(void) { + nghttp2_pq pq; + node nodes[10]; + int i; + node *nd; + int ans[] = {-8, -6, -4, -2, 0, 1, 3, 5, 7, 9}; + + nghttp2_pq_init(&pq, node_less, nghttp2_mem_default()); + + for (i = 0; i < (int)(sizeof(nodes) / sizeof(nodes[0])); ++i) { + nodes[i].key = i; + nodes[i].val = i; + nghttp2_pq_push(&pq, &nodes[i]); + } + + nghttp2_pq_update(&pq, node_update, NULL); + + for (i = 0; i < (int)(sizeof(nodes) / sizeof(nodes[0])); ++i) { + nd = nghttp2_pq_top(&pq); + CU_ASSERT(ans[i] == nd->key); + nghttp2_pq_pop(&pq); + } + + nghttp2_pq_free(&pq); +} diff --git a/tests/nghttp2_pq_test.h b/tests/nghttp2_pq_test.h new file mode 100644 index 0000000..7818194 --- /dev/null +++ b/tests/nghttp2_pq_test.h @@ -0,0 +1,31 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGHTTP2_PQ_TEST_H +#define NGHTTP2_PQ_TEST_H + +void test_nghttp2_pq(void); +void test_nghttp2_pq_update(void); + +#endif /* NGHTTP2_PQ_TEST_H */ diff --git a/tests/nghttp2_queue_test.c b/tests/nghttp2_queue_test.c new file mode 100644 index 0000000..76cc98a --- /dev/null +++ b/tests/nghttp2_queue_test.c @@ -0,0 +1,48 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "nghttp2_queue_test.h" + +#include + +#include "nghttp2_queue.h" + +void test_nghttp2_queue(void) { + int ints[] = {1, 2, 3, 4, 5}; + int i; + nghttp2_queue queue; + nghttp2_queue_init(&queue); + CU_ASSERT(nghttp2_queue_empty(&queue)); + for (i = 0; i < 5; ++i) { + nghttp2_queue_push(&queue, &ints[i]); + CU_ASSERT_EQUAL(ints[0], *(int *)(nghttp2_queue_front(&queue))); + CU_ASSERT(!nghttp2_queue_empty(&queue)); + } + for (i = 0; i < 5; ++i) { + CU_ASSERT_EQUAL(ints[i], *(int *)(nghttp2_queue_front(&queue))); + nghttp2_queue_pop(&queue); + } + CU_ASSERT(nghttp2_queue_empty(&queue)); + nghttp2_queue_free(&queue); +} diff --git a/tests/nghttp2_queue_test.h b/tests/nghttp2_queue_test.h new file mode 100644 index 0000000..944697e --- /dev/null +++ b/tests/nghttp2_queue_test.h @@ -0,0 +1,30 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGHTTP2_QUEUE_TEST_H +#define NGHTTP2_QUEUE_TEST_H + +void test_nghttp2_queue(void); + +#endif /* NGHTTP2_QUEUE_TEST_H */ diff --git a/tests/nghttp2_session_test.c b/tests/nghttp2_session_test.c new file mode 100644 index 0000000..7783e17 --- /dev/null +++ b/tests/nghttp2_session_test.c @@ -0,0 +1,8125 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2013 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "nghttp2_session_test.h" + +#include +#include + +#include + +#include "nghttp2_session.h" +#include "nghttp2_stream.h" +#include "nghttp2_net.h" +#include "nghttp2_helper.h" +#include "nghttp2_test_helper.h" +#include "nghttp2_priority_spec.h" + +extern int nghttp2_enable_strict_preface; + +#define OB_CTRL(ITEM) nghttp2_outbound_item_get_ctrl_frame(ITEM) +#define OB_CTRL_TYPE(ITEM) nghttp2_outbound_item_get_ctrl_frame_type(ITEM) +#define OB_DATA(ITEM) nghttp2_outbound_item_get_data_frame(ITEM) + +typedef struct { + uint8_t buf[65535]; + size_t length; +} accumulator; + +typedef struct { + uint8_t data[8192]; + uint8_t *datamark; + uint8_t *datalimit; + size_t feedseq[8192]; + size_t seqidx; +} scripted_data_feed; + +typedef struct { + accumulator *acc; + scripted_data_feed *df; + int frame_recv_cb_called, invalid_frame_recv_cb_called; + uint8_t recv_frame_type; + int frame_send_cb_called; + uint8_t sent_frame_type; + int frame_not_send_cb_called; + uint8_t not_sent_frame_type; + int not_sent_error; + int stream_close_cb_called; + uint32_t stream_close_error_code; + size_t data_source_length; + int32_t stream_id; + size_t block_count; + int data_chunk_recv_cb_called; + const nghttp2_frame *frame; + size_t fixed_sendlen; + int header_cb_called; + int begin_headers_cb_called; + nghttp2_nv nv; + size_t data_chunk_len; + size_t padlen; + int begin_frame_cb_called; +} my_user_data; + +static const nghttp2_nv reqnv[] = { + MAKE_NV(":method", "GET"), MAKE_NV(":path", "/"), + MAKE_NV(":scheme", "https"), MAKE_NV(":authority", "localhost"), +}; + +static const nghttp2_nv resnv[] = { + MAKE_NV(":status", "200"), +}; + +static const nghttp2_nv trailernv[] = { + // from http://tools.ietf.org/html/rfc6249#section-7 + MAKE_NV("digest", "SHA-256=" + "MWVkMWQxYTRiMzk5MDQ0MzI3NGU5NDEyZTk5OWY1ZGFmNzgyZTJlODYz" + "YjRjYzFhOTlmNTQwYzI2M2QwM2U2MQ=="), +}; + +static void scripted_data_feed_init2(scripted_data_feed *df, + nghttp2_bufs *bufs) { + nghttp2_buf_chain *ci; + nghttp2_buf *buf; + uint8_t *ptr; + size_t len; + + memset(df, 0, sizeof(scripted_data_feed)); + ptr = df->data; + len = 0; + + for (ci = bufs->head; ci; ci = ci->next) { + buf = &ci->buf; + ptr = nghttp2_cpymem(ptr, buf->pos, nghttp2_buf_len(buf)); + len += nghttp2_buf_len(buf); + } + + df->datamark = df->data; + df->datalimit = df->data + len; + df->feedseq[0] = len; +} + +static ssize_t null_send_callback(nghttp2_session *session _U_, + const uint8_t *data _U_, size_t len, + int flags _U_, void *user_data _U_) { + return len; +} + +static ssize_t fail_send_callback(nghttp2_session *session _U_, + const uint8_t *data _U_, size_t len _U_, + int flags _U_, void *user_data _U_) { + return NGHTTP2_ERR_CALLBACK_FAILURE; +} + +static ssize_t fixed_bytes_send_callback(nghttp2_session *session _U_, + const uint8_t *data _U_, size_t len, + int flags _U_, void *user_data) { + size_t fixed_sendlen = ((my_user_data *)user_data)->fixed_sendlen; + return fixed_sendlen < len ? fixed_sendlen : len; +} + +static ssize_t scripted_recv_callback(nghttp2_session *session _U_, + uint8_t *data, size_t len, int flags _U_, + void *user_data) { + scripted_data_feed *df = ((my_user_data *)user_data)->df; + size_t wlen = df->feedseq[df->seqidx] > len ? len : df->feedseq[df->seqidx]; + memcpy(data, df->datamark, wlen); + df->datamark += wlen; + df->feedseq[df->seqidx] -= wlen; + if (df->feedseq[df->seqidx] == 0) { + ++df->seqidx; + } + return wlen; +} + +static ssize_t eof_recv_callback(nghttp2_session *session _U_, + uint8_t *data _U_, size_t len _U_, + int flags _U_, void *user_data _U_) { + return NGHTTP2_ERR_EOF; +} + +static ssize_t accumulator_send_callback(nghttp2_session *session _U_, + const uint8_t *buf, size_t len, + int flags _U_, void *user_data) { + accumulator *acc = ((my_user_data *)user_data)->acc; + assert(acc->length + len < sizeof(acc->buf)); + memcpy(acc->buf + acc->length, buf, len); + acc->length += len; + return len; +} + +static int on_begin_frame_callback(nghttp2_session *session _U_, + const nghttp2_frame_hd *hd _U_, + void *user_data) { + my_user_data *ud = (my_user_data *)user_data; + ++ud->begin_frame_cb_called; + return 0; +} + +static int on_frame_recv_callback(nghttp2_session *session _U_, + const nghttp2_frame *frame, void *user_data) { + my_user_data *ud = (my_user_data *)user_data; + ++ud->frame_recv_cb_called; + ud->recv_frame_type = frame->hd.type; + return 0; +} + +static int on_invalid_frame_recv_callback(nghttp2_session *session _U_, + const nghttp2_frame *frame _U_, + int lib_error_code _U_, + void *user_data) { + my_user_data *ud = (my_user_data *)user_data; + ++ud->invalid_frame_recv_cb_called; + return 0; +} + +static int on_frame_send_callback(nghttp2_session *session _U_, + const nghttp2_frame *frame, void *user_data) { + my_user_data *ud = (my_user_data *)user_data; + ++ud->frame_send_cb_called; + ud->sent_frame_type = frame->hd.type; + return 0; +} + +static int on_frame_not_send_callback(nghttp2_session *session _U_, + const nghttp2_frame *frame, int lib_error, + void *user_data) { + my_user_data *ud = (my_user_data *)user_data; + ++ud->frame_not_send_cb_called; + ud->not_sent_frame_type = frame->hd.type; + ud->not_sent_error = lib_error; + return 0; +} + +static int on_data_chunk_recv_callback(nghttp2_session *session _U_, + uint8_t flags _U_, int32_t stream_id _U_, + const uint8_t *data _U_, size_t len, + void *user_data) { + my_user_data *ud = (my_user_data *)user_data; + ++ud->data_chunk_recv_cb_called; + ud->data_chunk_len = len; + return 0; +} + +static int pause_on_data_chunk_recv_callback(nghttp2_session *session _U_, + uint8_t flags _U_, + int32_t stream_id _U_, + const uint8_t *data _U_, + size_t len _U_, void *user_data) { + my_user_data *ud = (my_user_data *)user_data; + ++ud->data_chunk_recv_cb_called; + return NGHTTP2_ERR_PAUSE; +} + +static ssize_t select_padding_callback(nghttp2_session *session _U_, + const nghttp2_frame *frame, + size_t max_payloadlen, void *user_data) { + my_user_data *ud = (my_user_data *)user_data; + return nghttp2_min(max_payloadlen, frame->hd.length + ud->padlen); +} + +static ssize_t too_large_data_source_length_callback( + nghttp2_session *session _U_, uint8_t frame_type _U_, int32_t stream_id _U_, + int32_t session_remote_window_size _U_, + int32_t stream_remote_window_size _U_, uint32_t remote_max_frame_size _U_, + void *user_data _U_) { + return NGHTTP2_MAX_FRAME_SIZE_MAX + 1; +} + +static ssize_t smallest_length_data_source_length_callback( + nghttp2_session *session _U_, uint8_t frame_type _U_, int32_t stream_id _U_, + int32_t session_remote_window_size _U_, + int32_t stream_remote_window_size _U_, uint32_t remote_max_frame_size _U_, + void *user_data _U_) { + return 1; +} + +static ssize_t fixed_length_data_source_read_callback( + nghttp2_session *session _U_, int32_t stream_id _U_, uint8_t *buf _U_, + size_t len, uint32_t *data_flags, nghttp2_data_source *source _U_, + void *user_data) { + my_user_data *ud = (my_user_data *)user_data; + size_t wlen; + if (len < ud->data_source_length) { + wlen = len; + } else { + wlen = ud->data_source_length; + } + ud->data_source_length -= wlen; + if (ud->data_source_length == 0) { + *data_flags |= NGHTTP2_DATA_FLAG_EOF; + } + return wlen; +} + +static ssize_t temporal_failure_data_source_read_callback( + nghttp2_session *session _U_, int32_t stream_id _U_, uint8_t *buf _U_, + size_t len _U_, uint32_t *data_flags _U_, nghttp2_data_source *source _U_, + void *user_data _U_) { + return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; +} + +static ssize_t fail_data_source_read_callback(nghttp2_session *session _U_, + int32_t stream_id _U_, + uint8_t *buf _U_, size_t len _U_, + uint32_t *data_flags _U_, + nghttp2_data_source *source _U_, + void *user_data _U_) { + return NGHTTP2_ERR_CALLBACK_FAILURE; +} + +static ssize_t no_end_stream_data_source_read_callback( + nghttp2_session *session _U_, int32_t stream_id _U_, uint8_t *buf _U_, + size_t len _U_, uint32_t *data_flags, nghttp2_data_source *source _U_, + void *user_data _U_) { + *data_flags |= NGHTTP2_DATA_FLAG_EOF | NGHTTP2_DATA_FLAG_NO_END_STREAM; + return 0; +} + +static ssize_t no_copy_data_source_read_callback( + nghttp2_session *session _U_, int32_t stream_id _U_, uint8_t *buf _U_, + size_t len, uint32_t *data_flags, nghttp2_data_source *source _U_, + void *user_data) { + my_user_data *ud = (my_user_data *)user_data; + size_t wlen; + if (len < ud->data_source_length) { + wlen = len; + } else { + wlen = ud->data_source_length; + } + + ud->data_source_length -= wlen; + + *data_flags |= NGHTTP2_DATA_FLAG_NO_COPY; + + if (ud->data_source_length == 0) { + *data_flags |= NGHTTP2_DATA_FLAG_EOF; + } + return wlen; +} + +static int send_data_callback(nghttp2_session *session _U_, + nghttp2_frame *frame, const uint8_t *framehd, + size_t length, nghttp2_data_source *source _U_, + void *user_data) { + accumulator *acc = ((my_user_data *)user_data)->acc; + + memcpy(acc->buf + acc->length, framehd, NGHTTP2_FRAME_HDLEN); + acc->length += NGHTTP2_FRAME_HDLEN; + + if (frame->data.padlen) { + *(acc->buf + acc->length++) = frame->data.padlen - 1; + } + + acc->length += length; + + if (frame->data.padlen) { + acc->length += frame->data.padlen - 1; + } + + return 0; +} + +/* static void no_stream_user_data_stream_close_callback */ +/* (nghttp2_session *session, */ +/* int32_t stream_id, */ +/* nghttp2_error_code error_code, */ +/* void *user_data) */ +/* { */ +/* my_user_data* my_data = (my_user_data*)user_data; */ +/* ++my_data->stream_close_cb_called; */ +/* } */ + +static ssize_t block_count_send_callback(nghttp2_session *session _U_, + const uint8_t *data _U_, size_t len, + int flags _U_, void *user_data) { + my_user_data *ud = (my_user_data *)user_data; + ssize_t r; + if (ud->block_count == 0) { + r = NGHTTP2_ERR_WOULDBLOCK; + } else { + --ud->block_count; + r = len; + } + return r; +} + +static int on_header_callback(nghttp2_session *session _U_, + const nghttp2_frame *frame, const uint8_t *name, + size_t namelen, const uint8_t *value, + size_t valuelen, uint8_t flags _U_, + void *user_data) { + my_user_data *ud = (my_user_data *)user_data; + ++ud->header_cb_called; + ud->nv.name = (uint8_t *)name; + ud->nv.namelen = namelen; + ud->nv.value = (uint8_t *)value; + ud->nv.valuelen = valuelen; + + ud->frame = frame; + return 0; +} + +static int pause_on_header_callback(nghttp2_session *session, + const nghttp2_frame *frame, + const uint8_t *name, size_t namelen, + const uint8_t *value, size_t valuelen, + uint8_t flags, void *user_data) { + on_header_callback(session, frame, name, namelen, value, valuelen, flags, + user_data); + return NGHTTP2_ERR_PAUSE; +} + +static int temporal_failure_on_header_callback( + nghttp2_session *session, const nghttp2_frame *frame, const uint8_t *name, + size_t namelen, const uint8_t *value, size_t valuelen, uint8_t flags, + void *user_data) { + on_header_callback(session, frame, name, namelen, value, valuelen, flags, + user_data); + return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; +} + +static int on_begin_headers_callback(nghttp2_session *session _U_, + const nghttp2_frame *frame _U_, + void *user_data) { + my_user_data *ud = (my_user_data *)user_data; + ++ud->begin_headers_cb_called; + return 0; +} + +static int temporal_failure_on_begin_headers_callback( + nghttp2_session *session, const nghttp2_frame *frame, void *user_data) { + on_begin_headers_callback(session, frame, user_data); + return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; +} + +static ssize_t defer_data_source_read_callback(nghttp2_session *session _U_, + int32_t stream_id _U_, + uint8_t *buf _U_, size_t len _U_, + uint32_t *data_flags _U_, + nghttp2_data_source *source _U_, + void *user_data _U_) { + return NGHTTP2_ERR_DEFERRED; +} + +static int on_stream_close_callback(nghttp2_session *session _U_, + int32_t stream_id _U_, + nghttp2_error_code error_code _U_, + void *user_data) { + my_user_data *my_data = (my_user_data *)user_data; + ++my_data->stream_close_cb_called; + my_data->stream_close_error_code = error_code; + + return 0; +} + +static nghttp2_settings_entry *dup_iv(const nghttp2_settings_entry *iv, + size_t niv) { + return nghttp2_frame_iv_copy(iv, niv, nghttp2_mem_default()); +} + +static nghttp2_priority_spec pri_spec_default = {0, NGHTTP2_DEFAULT_WEIGHT, 0}; + +void test_nghttp2_session_recv(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + scripted_data_feed df; + my_user_data user_data; + nghttp2_bufs bufs; + ssize_t framelen; + nghttp2_frame frame; + ssize_t i; + nghttp2_outbound_item *item; + nghttp2_nv *nva; + ssize_t nvlen; + nghttp2_hd_deflater deflater; + int rv; + nghttp2_mem *mem; + + mem = nghttp2_mem_default(); + frame_pack_bufs_init(&bufs); + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.send_callback = null_send_callback; + callbacks.recv_callback = scripted_recv_callback; + callbacks.on_frame_recv_callback = on_frame_recv_callback; + callbacks.on_begin_frame_callback = on_begin_frame_callback; + + user_data.df = &df; + + nghttp2_session_server_new(&session, &callbacks, &user_data); + nghttp2_hd_deflate_init(&deflater, mem); + + nvlen = ARRLEN(reqnv); + nghttp2_nv_array_copy(&nva, reqnv, nvlen, mem); + nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_END_HEADERS, 1, + NGHTTP2_HCAT_HEADERS, NULL, nva, nvlen); + rv = nghttp2_frame_pack_headers(&bufs, &frame.headers, &deflater); + + CU_ASSERT(0 == rv); + + scripted_data_feed_init2(&df, &bufs); + + framelen = nghttp2_bufs_len(&bufs); + + /* Send 1 byte per each read */ + for (i = 0; i < framelen; ++i) { + df.feedseq[i] = 1; + } + + nghttp2_frame_headers_free(&frame.headers, mem); + + user_data.frame_recv_cb_called = 0; + user_data.begin_frame_cb_called = 0; + + while ((ssize_t)df.seqidx < framelen) { + CU_ASSERT(0 == nghttp2_session_recv(session)); + } + CU_ASSERT(1 == user_data.frame_recv_cb_called); + CU_ASSERT(1 == user_data.begin_frame_cb_called); + + nghttp2_bufs_reset(&bufs); + + /* Receive PRIORITY */ + nghttp2_frame_priority_init(&frame.priority, 5, &pri_spec_default); + + rv = nghttp2_frame_pack_priority(&bufs, &frame.priority); + + CU_ASSERT(0 == rv); + + nghttp2_frame_priority_free(&frame.priority); + + scripted_data_feed_init2(&df, &bufs); + + user_data.frame_recv_cb_called = 0; + user_data.begin_frame_cb_called = 0; + + CU_ASSERT(0 == nghttp2_session_recv(session)); + CU_ASSERT(1 == user_data.frame_recv_cb_called); + CU_ASSERT(1 == user_data.begin_frame_cb_called); + + nghttp2_bufs_reset(&bufs); + + nghttp2_hd_deflate_free(&deflater); + nghttp2_session_del(session); + + /* Some tests for frame too large */ + nghttp2_session_server_new(&session, &callbacks, &user_data); + + /* Receive PING with too large payload */ + nghttp2_frame_ping_init(&frame.ping, NGHTTP2_FLAG_NONE, NULL); + + rv = nghttp2_frame_pack_ping(&bufs, &frame.ping); + + CU_ASSERT(0 == rv); + + /* Add extra 16 bytes */ + nghttp2_bufs_seek_last_present(&bufs); + assert(nghttp2_buf_len(&bufs.cur->buf) >= 16); + + bufs.cur->buf.last += 16; + nghttp2_put_uint32be( + bufs.cur->buf.pos, + (uint32_t)(((frame.hd.length + 16) << 8) + bufs.cur->buf.pos[3])); + + nghttp2_frame_ping_free(&frame.ping); + + scripted_data_feed_init2(&df, &bufs); + user_data.frame_recv_cb_called = 0; + user_data.begin_frame_cb_called = 0; + + CU_ASSERT(0 == nghttp2_session_recv(session)); + CU_ASSERT(0 == user_data.frame_recv_cb_called); + CU_ASSERT(0 == user_data.begin_frame_cb_called); + + item = nghttp2_session_get_next_ob_item(session); + CU_ASSERT(NGHTTP2_GOAWAY == item->frame.hd.type); + CU_ASSERT(NGHTTP2_FRAME_SIZE_ERROR == item->frame.goaway.error_code); + CU_ASSERT(0 == nghttp2_session_send(session)); + + nghttp2_bufs_free(&bufs); + nghttp2_session_del(session); +} + +void test_nghttp2_session_recv_invalid_stream_id(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + scripted_data_feed df; + my_user_data user_data; + nghttp2_bufs bufs; + nghttp2_frame frame; + nghttp2_hd_deflater deflater; + int rv; + nghttp2_mem *mem; + nghttp2_nv *nva; + size_t nvlen; + + mem = nghttp2_mem_default(); + frame_pack_bufs_init(&bufs); + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.recv_callback = scripted_recv_callback; + callbacks.on_invalid_frame_recv_callback = on_invalid_frame_recv_callback; + + user_data.df = &df; + user_data.invalid_frame_recv_cb_called = 0; + nghttp2_session_server_new(&session, &callbacks, &user_data); + nghttp2_hd_deflate_init(&deflater, mem); + + nvlen = ARRLEN(reqnv); + nghttp2_nv_array_copy(&nva, reqnv, nvlen, mem); + nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_END_HEADERS, 2, + NGHTTP2_HCAT_HEADERS, NULL, nva, nvlen); + rv = nghttp2_frame_pack_headers(&bufs, &frame.headers, &deflater); + + CU_ASSERT(0 == rv); + CU_ASSERT(nghttp2_bufs_len(&bufs) > 0); + + scripted_data_feed_init2(&df, &bufs); + nghttp2_frame_headers_free(&frame.headers, mem); + + CU_ASSERT(0 == nghttp2_session_recv(session)); + CU_ASSERT(1 == user_data.invalid_frame_recv_cb_called); + + nghttp2_bufs_free(&bufs); + nghttp2_hd_deflate_free(&deflater); + nghttp2_session_del(session); +} + +void test_nghttp2_session_recv_invalid_frame(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + scripted_data_feed df; + my_user_data user_data; + nghttp2_bufs bufs; + nghttp2_frame frame; + nghttp2_nv *nva; + ssize_t nvlen; + nghttp2_hd_deflater deflater; + int rv; + nghttp2_mem *mem; + + mem = nghttp2_mem_default(); + frame_pack_bufs_init(&bufs); + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.recv_callback = scripted_recv_callback; + callbacks.send_callback = null_send_callback; + callbacks.on_frame_send_callback = on_frame_send_callback; + + user_data.df = &df; + user_data.frame_send_cb_called = 0; + nghttp2_session_server_new(&session, &callbacks, &user_data); + nghttp2_hd_deflate_init(&deflater, mem); + nvlen = ARRLEN(reqnv); + nghttp2_nv_array_copy(&nva, reqnv, nvlen, mem); + nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_END_HEADERS, 1, + NGHTTP2_HCAT_HEADERS, NULL, nva, nvlen); + rv = nghttp2_frame_pack_headers(&bufs, &frame.headers, &deflater); + + CU_ASSERT(0 == rv); + CU_ASSERT(nghttp2_bufs_len(&bufs) > 0); + + scripted_data_feed_init2(&df, &bufs); + + CU_ASSERT(0 == nghttp2_session_recv(session)); + CU_ASSERT(0 == nghttp2_session_send(session)); + CU_ASSERT(0 == user_data.frame_send_cb_called); + + /* Receive exactly same bytes of HEADERS is treated as error, because it has + * pseudo headers and without END_STREAM flag set */ + scripted_data_feed_init2(&df, &bufs); + + CU_ASSERT(0 == nghttp2_session_recv(session)); + CU_ASSERT(0 == nghttp2_session_send(session)); + CU_ASSERT(1 == user_data.frame_send_cb_called); + CU_ASSERT(NGHTTP2_RST_STREAM == user_data.sent_frame_type); + + nghttp2_bufs_free(&bufs); + nghttp2_frame_headers_free(&frame.headers, mem); + + nghttp2_hd_deflate_free(&deflater); + nghttp2_session_del(session); +} + +void test_nghttp2_session_recv_eof(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.send_callback = null_send_callback; + callbacks.recv_callback = eof_recv_callback; + + nghttp2_session_client_new(&session, &callbacks, NULL); + CU_ASSERT(NGHTTP2_ERR_EOF == nghttp2_session_recv(session)); + + nghttp2_session_del(session); +} + +void test_nghttp2_session_recv_data(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + my_user_data ud; + uint8_t data[8092]; + ssize_t rv; + nghttp2_outbound_item *item; + nghttp2_stream *stream; + nghttp2_frame_hd hd; + int i; + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.send_callback = null_send_callback; + callbacks.on_data_chunk_recv_callback = on_data_chunk_recv_callback; + callbacks.on_frame_recv_callback = on_frame_recv_callback; + + nghttp2_session_client_new(&session, &callbacks, &ud); + + /* Create DATA frame with length 4KiB */ + memset(data, 0, sizeof(data)); + hd.length = 4096; + hd.type = NGHTTP2_DATA; + hd.flags = NGHTTP2_FLAG_NONE; + hd.stream_id = 1; + nghttp2_frame_pack_frame_hd(data, &hd); + + /* stream 1 is not opened, so it must be responded with connection + error. This is not mandated by the spec */ + ud.data_chunk_recv_cb_called = 0; + ud.frame_recv_cb_called = 0; + rv = nghttp2_session_mem_recv(session, data, NGHTTP2_FRAME_HDLEN + 4096); + CU_ASSERT(NGHTTP2_FRAME_HDLEN + 4096 == rv); + + CU_ASSERT(0 == ud.data_chunk_recv_cb_called); + CU_ASSERT(0 == ud.frame_recv_cb_called); + item = nghttp2_session_get_next_ob_item(session); + CU_ASSERT(NGHTTP2_GOAWAY == item->frame.hd.type); + + nghttp2_session_del(session); + + nghttp2_session_client_new(&session, &callbacks, &ud); + + /* Create stream 1 with CLOSING state. DATA is ignored. */ + stream = nghttp2_session_open_stream(session, 1, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, + NGHTTP2_STREAM_CLOSING, NULL); + /* Set initial window size 16383 to check stream flow control, + isolating it from the conneciton flow control */ + stream->local_window_size = 16383; + + ud.data_chunk_recv_cb_called = 0; + ud.frame_recv_cb_called = 0; + rv = nghttp2_session_mem_recv(session, data, NGHTTP2_FRAME_HDLEN + 4096); + CU_ASSERT(NGHTTP2_FRAME_HDLEN + 4096 == rv); + + CU_ASSERT(0 == ud.data_chunk_recv_cb_called); + CU_ASSERT(0 == ud.frame_recv_cb_called); + item = nghttp2_session_get_next_ob_item(session); + CU_ASSERT(NULL == item); + + /* This is normal case. DATA is acceptable. */ + stream->state = NGHTTP2_STREAM_OPENED; + + ud.data_chunk_recv_cb_called = 0; + ud.frame_recv_cb_called = 0; + rv = nghttp2_session_mem_recv(session, data, NGHTTP2_FRAME_HDLEN + 4096); + CU_ASSERT(NGHTTP2_FRAME_HDLEN + 4096 == rv); + + CU_ASSERT(1 == ud.data_chunk_recv_cb_called); + CU_ASSERT(1 == ud.frame_recv_cb_called); + + CU_ASSERT(NULL == nghttp2_session_get_next_ob_item(session)); + + ud.data_chunk_recv_cb_called = 0; + ud.frame_recv_cb_called = 0; + rv = nghttp2_session_mem_recv(session, data, NGHTTP2_FRAME_HDLEN + 4096); + CU_ASSERT(NGHTTP2_FRAME_HDLEN + 4096 == rv); + + /* Now we got data more than initial-window-size / 2, WINDOW_UPDATE + must be queued */ + CU_ASSERT(1 == ud.data_chunk_recv_cb_called); + CU_ASSERT(1 == ud.frame_recv_cb_called); + item = nghttp2_session_get_next_ob_item(session); + CU_ASSERT(NGHTTP2_WINDOW_UPDATE == item->frame.hd.type); + CU_ASSERT(1 == item->frame.window_update.hd.stream_id); + CU_ASSERT(0 == nghttp2_session_send(session)); + + /* Set initial window size to 1MiB, so that we can check connection + flow control individually */ + stream->local_window_size = 1 << 20; + /* Connection flow control takes into account DATA which is received + in the error condition. We have received 4096 * 4 bytes of + DATA. Additional 4 DATA frames, connection flow control will kick + in. */ + for (i = 0; i < 5; ++i) { + rv = nghttp2_session_mem_recv(session, data, NGHTTP2_FRAME_HDLEN + 4096); + CU_ASSERT(NGHTTP2_FRAME_HDLEN + 4096 == rv); + } + item = nghttp2_session_get_next_ob_item(session); + CU_ASSERT(NGHTTP2_WINDOW_UPDATE == item->frame.hd.type); + CU_ASSERT(0 == item->frame.window_update.hd.stream_id); + CU_ASSERT(0 == nghttp2_session_send(session)); + + /* Reception of DATA with stream ID = 0 causes connection error */ + hd.length = 4096; + hd.type = NGHTTP2_DATA; + hd.flags = NGHTTP2_FLAG_NONE; + hd.stream_id = 0; + nghttp2_frame_pack_frame_hd(data, &hd); + + ud.data_chunk_recv_cb_called = 0; + ud.frame_recv_cb_called = 0; + rv = nghttp2_session_mem_recv(session, data, NGHTTP2_FRAME_HDLEN + 4096); + CU_ASSERT(NGHTTP2_FRAME_HDLEN + 4096 == rv); + + CU_ASSERT(0 == ud.data_chunk_recv_cb_called); + CU_ASSERT(0 == ud.frame_recv_cb_called); + item = nghttp2_session_get_next_ob_item(session); + CU_ASSERT(NGHTTP2_GOAWAY == item->frame.hd.type); + CU_ASSERT(NGHTTP2_PROTOCOL_ERROR == item->frame.goaway.error_code); + + nghttp2_session_del(session); +} + +void test_nghttp2_session_recv_continuation(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + nghttp2_nv *nva; + size_t nvlen; + nghttp2_frame frame; + nghttp2_bufs bufs; + nghttp2_buf *buf; + ssize_t rv; + my_user_data ud; + nghttp2_hd_deflater deflater; + uint8_t data[1024]; + size_t datalen; + nghttp2_frame_hd cont_hd; + nghttp2_priority_spec pri_spec; + nghttp2_mem *mem; + + mem = nghttp2_mem_default(); + frame_pack_bufs_init(&bufs); + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.on_header_callback = on_header_callback; + callbacks.on_begin_headers_callback = on_begin_headers_callback; + callbacks.on_begin_frame_callback = on_begin_frame_callback; + + nghttp2_session_server_new(&session, &callbacks, &ud); + + nghttp2_hd_deflate_init(&deflater, mem); + + /* Make 1 HEADERS and insert CONTINUATION header */ + nvlen = ARRLEN(reqnv); + nghttp2_nv_array_copy(&nva, reqnv, nvlen, mem); + nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_NONE, 1, + NGHTTP2_HCAT_HEADERS, NULL, nva, nvlen); + rv = nghttp2_frame_pack_headers(&bufs, &frame.headers, &deflater); + + CU_ASSERT(0 == rv); + CU_ASSERT(nghttp2_bufs_len(&bufs) > 0); + + /* make sure that all data is in the first buf */ + buf = &bufs.head->buf; + assert(nghttp2_bufs_len(&bufs) == nghttp2_buf_len(buf)); + + nghttp2_frame_headers_free(&frame.headers, mem); + + /* HEADERS's payload is 1 byte */ + memcpy(data, buf->pos, NGHTTP2_FRAME_HDLEN + 1); + datalen = NGHTTP2_FRAME_HDLEN + 1; + buf->pos += NGHTTP2_FRAME_HDLEN + 1; + + nghttp2_put_uint32be(data, (1 << 8) + data[3]); + + /* First CONTINUATION, 2 bytes */ + nghttp2_frame_hd_init(&cont_hd, 2, NGHTTP2_CONTINUATION, NGHTTP2_FLAG_NONE, + 1); + + nghttp2_frame_pack_frame_hd(data + datalen, &cont_hd); + datalen += NGHTTP2_FRAME_HDLEN; + + memcpy(data + datalen, buf->pos, cont_hd.length); + datalen += cont_hd.length; + buf->pos += cont_hd.length; + + /* Second CONTINUATION, rest of the bytes */ + nghttp2_frame_hd_init(&cont_hd, nghttp2_buf_len(buf), NGHTTP2_CONTINUATION, + NGHTTP2_FLAG_END_HEADERS, 1); + + nghttp2_frame_pack_frame_hd(data + datalen, &cont_hd); + datalen += NGHTTP2_FRAME_HDLEN; + + memcpy(data + datalen, buf->pos, cont_hd.length); + datalen += cont_hd.length; + buf->pos += cont_hd.length; + + CU_ASSERT(0 == nghttp2_buf_len(buf)); + + ud.header_cb_called = 0; + ud.begin_frame_cb_called = 0; + + rv = nghttp2_session_mem_recv(session, data, datalen); + CU_ASSERT((ssize_t)datalen == rv); + CU_ASSERT(4 == ud.header_cb_called); + CU_ASSERT(3 == ud.begin_frame_cb_called); + + nghttp2_hd_deflate_free(&deflater); + nghttp2_session_del(session); + + /* Expecting CONTINUATION, but get the other frame */ + nghttp2_session_server_new(&session, &callbacks, &ud); + + nghttp2_hd_deflate_init(&deflater, mem); + + /* HEADERS without END_HEADERS flag */ + nvlen = ARRLEN(reqnv); + nghttp2_nv_array_copy(&nva, reqnv, nvlen, mem); + nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_NONE, 1, + NGHTTP2_HCAT_HEADERS, NULL, nva, nvlen); + nghttp2_bufs_reset(&bufs); + rv = nghttp2_frame_pack_headers(&bufs, &frame.headers, &deflater); + + CU_ASSERT(0 == rv); + CU_ASSERT(nghttp2_bufs_len(&bufs) > 0); + + nghttp2_frame_headers_free(&frame.headers, mem); + + /* make sure that all data is in the first buf */ + buf = &bufs.head->buf; + assert(nghttp2_bufs_len(&bufs) == nghttp2_buf_len(buf)); + + memcpy(data, buf->pos, nghttp2_buf_len(buf)); + datalen = nghttp2_buf_len(buf); + + /* Followed by PRIORITY */ + nghttp2_priority_spec_default_init(&pri_spec); + + nghttp2_frame_priority_init(&frame.priority, 1, &pri_spec); + nghttp2_bufs_reset(&bufs); + + rv = nghttp2_frame_pack_priority(&bufs, &frame.priority); + + CU_ASSERT(0 == rv); + CU_ASSERT(nghttp2_bufs_len(&bufs) > 0); + + memcpy(data + datalen, buf->pos, nghttp2_buf_len(buf)); + datalen += nghttp2_buf_len(buf); + + ud.begin_headers_cb_called = 0; + rv = nghttp2_session_mem_recv(session, data, datalen); + CU_ASSERT((ssize_t)datalen == rv); + + CU_ASSERT(1 == ud.begin_headers_cb_called); + CU_ASSERT(NGHTTP2_GOAWAY == + nghttp2_session_get_next_ob_item(session)->frame.hd.type); + + nghttp2_bufs_free(&bufs); + nghttp2_hd_deflate_free(&deflater); + nghttp2_session_del(session); +} + +void test_nghttp2_session_recv_headers_with_priority(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + nghttp2_nv *nva; + size_t nvlen; + nghttp2_frame frame; + nghttp2_bufs bufs; + nghttp2_buf *buf; + ssize_t rv; + my_user_data ud; + nghttp2_hd_deflater deflater; + nghttp2_outbound_item *item; + nghttp2_priority_spec pri_spec; + nghttp2_stream *stream; + nghttp2_mem *mem; + + mem = nghttp2_mem_default(); + frame_pack_bufs_init(&bufs); + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.on_frame_recv_callback = on_frame_recv_callback; + + nghttp2_session_server_new(&session, &callbacks, &ud); + + nghttp2_hd_deflate_init(&deflater, mem); + + open_stream(session, 1); + + /* With NGHTTP2_FLAG_PRIORITY without exclusive flag set */ + nvlen = ARRLEN(reqnv); + nghttp2_nv_array_copy(&nva, reqnv, nvlen, mem); + + nghttp2_priority_spec_init(&pri_spec, 1, 99, 0); + + nghttp2_frame_headers_init(&frame.headers, + NGHTTP2_FLAG_END_HEADERS | NGHTTP2_FLAG_PRIORITY, + 3, NGHTTP2_HCAT_HEADERS, &pri_spec, nva, nvlen); + + rv = nghttp2_frame_pack_headers(&bufs, &frame.headers, &deflater); + + CU_ASSERT(0 == rv); + CU_ASSERT(nghttp2_bufs_len(&bufs) > 0); + + nghttp2_frame_headers_free(&frame.headers, mem); + + buf = &bufs.head->buf; + assert(nghttp2_bufs_len(&bufs) == nghttp2_buf_len(buf)); + + ud.frame_recv_cb_called = 0; + + rv = nghttp2_session_mem_recv(session, buf->pos, nghttp2_buf_len(buf)); + + CU_ASSERT(nghttp2_buf_len(buf) == rv); + CU_ASSERT(1 == ud.frame_recv_cb_called); + + stream = nghttp2_session_get_stream(session, 3); + + CU_ASSERT(99 == stream->weight); + CU_ASSERT(1 == stream->dep_prev->stream_id); + + nghttp2_bufs_reset(&bufs); + + /* With NGHTTP2_FLAG_PRIORITY, but cut last 1 byte to make it + invalid. */ + nvlen = ARRLEN(reqnv); + nghttp2_nv_array_copy(&nva, reqnv, nvlen, mem); + + nghttp2_priority_spec_init(&pri_spec, 0, 99, 0); + + nghttp2_frame_headers_init(&frame.headers, + NGHTTP2_FLAG_END_HEADERS | NGHTTP2_FLAG_PRIORITY, + 5, NGHTTP2_HCAT_HEADERS, &pri_spec, nva, nvlen); + + rv = nghttp2_frame_pack_headers(&bufs, &frame.headers, &deflater); + + CU_ASSERT(0 == rv); + CU_ASSERT(nghttp2_bufs_len(&bufs) > NGHTTP2_FRAME_HDLEN + 5); + + nghttp2_frame_headers_free(&frame.headers, mem); + + buf = &bufs.head->buf; + /* Make payload shorter than required length to store priority + group */ + nghttp2_put_uint32be(buf->pos, (4 << 8) + buf->pos[3]); + + ud.frame_recv_cb_called = 0; + + rv = nghttp2_session_mem_recv(session, buf->pos, nghttp2_buf_len(buf)); + + CU_ASSERT(nghttp2_buf_len(buf) == rv); + CU_ASSERT(0 == ud.frame_recv_cb_called); + + stream = nghttp2_session_get_stream(session, 5); + + CU_ASSERT(NULL == stream); + + item = nghttp2_session_get_next_ob_item(session); + CU_ASSERT(NULL != item); + CU_ASSERT(NGHTTP2_GOAWAY == item->frame.hd.type); + CU_ASSERT(NGHTTP2_FRAME_SIZE_ERROR == item->frame.goaway.error_code); + + nghttp2_bufs_reset(&bufs); + + nghttp2_hd_deflate_free(&deflater); + nghttp2_session_del(session); + + /* Check dep_stream_id == stream_id */ + nghttp2_session_server_new(&session, &callbacks, &ud); + + nghttp2_hd_deflate_init(&deflater, mem); + + nvlen = ARRLEN(reqnv); + nghttp2_nv_array_copy(&nva, reqnv, nvlen, mem); + + nghttp2_priority_spec_init(&pri_spec, 1, 0, 0); + + nghttp2_frame_headers_init(&frame.headers, + NGHTTP2_FLAG_END_HEADERS | NGHTTP2_FLAG_PRIORITY, + 1, NGHTTP2_HCAT_HEADERS, &pri_spec, nva, nvlen); + + rv = nghttp2_frame_pack_headers(&bufs, &frame.headers, &deflater); + + CU_ASSERT(0 == rv); + CU_ASSERT(nghttp2_bufs_len(&bufs) > 0); + + nghttp2_frame_headers_free(&frame.headers, mem); + + buf = &bufs.head->buf; + assert(nghttp2_bufs_len(&bufs) == nghttp2_buf_len(buf)); + + ud.frame_recv_cb_called = 0; + + rv = nghttp2_session_mem_recv(session, buf->pos, nghttp2_buf_len(buf)); + + CU_ASSERT(nghttp2_buf_len(buf) == rv); + CU_ASSERT(0 == ud.frame_recv_cb_called); + + stream = nghttp2_session_get_stream(session, 1); + + CU_ASSERT(NULL == stream); + + item = nghttp2_session_get_next_ob_item(session); + CU_ASSERT(NULL != item); + CU_ASSERT(NGHTTP2_GOAWAY == item->frame.hd.type); + CU_ASSERT(NGHTTP2_PROTOCOL_ERROR == item->frame.goaway.error_code); + + nghttp2_bufs_reset(&bufs); + + nghttp2_bufs_free(&bufs); + nghttp2_hd_deflate_free(&deflater); + nghttp2_session_del(session); +} + +void test_nghttp2_session_recv_premature_headers(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + nghttp2_bufs bufs; + nghttp2_buf *buf; + ssize_t rv; + my_user_data ud; + nghttp2_hd_deflater deflater; + nghttp2_outbound_item *item; + nghttp2_mem *mem; + uint32_t payloadlen; + + mem = nghttp2_mem_default(); + frame_pack_bufs_init(&bufs); + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.send_callback = null_send_callback; + + nghttp2_session_server_new(&session, &callbacks, &ud); + + nghttp2_hd_deflate_init(&deflater, mem); + + rv = pack_headers(&bufs, &deflater, 1, NGHTTP2_FLAG_END_HEADERS, reqnv, + ARRLEN(reqnv), mem); + + buf = &bufs.head->buf; + /* Intentionally feed payload cutting last 1 byte off */ + payloadlen = nghttp2_get_uint32(buf->pos) >> 8; + nghttp2_put_uint32be(buf->pos, ((payloadlen - 1) << 8) + buf->pos[3]); + rv = nghttp2_session_mem_recv(session, buf->pos, nghttp2_buf_len(buf) - 1); + + CU_ASSERT(rv == nghttp2_buf_len(buf) - 1); + + item = nghttp2_session_get_next_ob_item(session); + + CU_ASSERT(NULL != item); + CU_ASSERT(NGHTTP2_RST_STREAM == item->frame.hd.type); + CU_ASSERT(NGHTTP2_COMPRESSION_ERROR == item->frame.rst_stream.error_code); + CU_ASSERT(1 == item->frame.hd.stream_id); + CU_ASSERT(0 == nghttp2_session_send(session)); + + nghttp2_bufs_reset(&bufs); + nghttp2_hd_deflate_free(&deflater); + nghttp2_session_del(session); + + /* Test for PUSH_PROMISE */ + nghttp2_session_client_new(&session, &callbacks, &ud); + nghttp2_hd_deflate_init(&deflater, mem); + + nghttp2_session_open_stream(session, 1, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, NGHTTP2_STREAM_OPENING, NULL); + + rv = pack_push_promise(&bufs, &deflater, 1, NGHTTP2_FLAG_END_HEADERS, 2, + reqnv, ARRLEN(reqnv), mem); + + CU_ASSERT(0 == rv); + + buf = &bufs.head->buf; + payloadlen = nghttp2_get_uint32(buf->pos) >> 8; + /* Intentionally feed payload cutting last 1 byte off */ + nghttp2_put_uint32be(buf->pos, ((payloadlen - 1) << 8) + buf->pos[3]); + rv = nghttp2_session_mem_recv(session, buf->pos, nghttp2_buf_len(buf) - 1); + + CU_ASSERT(rv == nghttp2_buf_len(buf) - 1); + + item = nghttp2_session_get_next_ob_item(session); + + CU_ASSERT(NULL != item); + CU_ASSERT(NGHTTP2_RST_STREAM == item->frame.hd.type); + CU_ASSERT(NGHTTP2_COMPRESSION_ERROR == item->frame.rst_stream.error_code); + CU_ASSERT(2 == item->frame.hd.stream_id); + CU_ASSERT(0 == nghttp2_session_send(session)); + + nghttp2_hd_deflate_free(&deflater); + nghttp2_session_del(session); + nghttp2_bufs_free(&bufs); +} + +void test_nghttp2_session_recv_unknown_frame(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + my_user_data ud; + uint8_t data[16384]; + size_t datalen; + nghttp2_frame_hd hd; + ssize_t rv; + + nghttp2_frame_hd_init(&hd, 16000, 99, NGHTTP2_FLAG_NONE, 0); + + nghttp2_frame_pack_frame_hd(data, &hd); + datalen = NGHTTP2_FRAME_HDLEN + hd.length; + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.on_frame_recv_callback = on_frame_recv_callback; + + nghttp2_session_server_new(&session, &callbacks, &ud); + + ud.frame_recv_cb_called = 0; + + /* Unknown frame must be ignored */ + rv = nghttp2_session_mem_recv(session, data, datalen); + + CU_ASSERT(rv == (ssize_t)datalen); + CU_ASSERT(0 == ud.frame_recv_cb_called); + CU_ASSERT(NULL == nghttp2_session_get_next_ob_item(session)); + + nghttp2_session_del(session); +} + +void test_nghttp2_session_recv_unexpected_continuation(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + my_user_data ud; + uint8_t data[16384]; + size_t datalen; + nghttp2_frame_hd hd; + ssize_t rv; + nghttp2_outbound_item *item; + + nghttp2_frame_hd_init(&hd, 16000, NGHTTP2_CONTINUATION, + NGHTTP2_FLAG_END_HEADERS, 1); + + nghttp2_frame_pack_frame_hd(data, &hd); + datalen = NGHTTP2_FRAME_HDLEN + hd.length; + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.on_frame_recv_callback = on_frame_recv_callback; + + nghttp2_session_server_new(&session, &callbacks, &ud); + + open_stream(session, 1); + + ud.frame_recv_cb_called = 0; + + /* unexpected CONTINUATION must be treated as connection error */ + rv = nghttp2_session_mem_recv(session, data, datalen); + + CU_ASSERT(rv == (ssize_t)datalen); + CU_ASSERT(0 == ud.frame_recv_cb_called); + + item = nghttp2_session_get_next_ob_item(session); + + CU_ASSERT(NGHTTP2_GOAWAY == item->frame.hd.type); + + nghttp2_session_del(session); +} + +void test_nghttp2_session_recv_settings_header_table_size(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + nghttp2_frame frame; + nghttp2_bufs bufs; + nghttp2_buf *buf; + ssize_t rv; + my_user_data ud; + nghttp2_settings_entry iv[3]; + nghttp2_nv nv = MAKE_NV(":authority", "example.org"); + nghttp2_mem *mem; + + mem = nghttp2_mem_default(); + frame_pack_bufs_init(&bufs); + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.on_frame_recv_callback = on_frame_recv_callback; + callbacks.send_callback = null_send_callback; + + nghttp2_session_client_new(&session, &callbacks, &ud); + + iv[0].settings_id = NGHTTP2_SETTINGS_HEADER_TABLE_SIZE; + iv[0].value = 3000; + + iv[1].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE; + iv[1].value = 16384; + + nghttp2_frame_settings_init(&frame.settings, NGHTTP2_FLAG_NONE, dup_iv(iv, 2), + 2); + + rv = nghttp2_frame_pack_settings(&bufs, &frame.settings); + + CU_ASSERT(0 == rv); + CU_ASSERT(nghttp2_bufs_len(&bufs) > 0); + + nghttp2_frame_settings_free(&frame.settings, mem); + + buf = &bufs.head->buf; + assert(nghttp2_bufs_len(&bufs) == nghttp2_buf_len(buf)); + + ud.frame_recv_cb_called = 0; + + rv = nghttp2_session_mem_recv(session, buf->pos, nghttp2_buf_len(buf)); + + CU_ASSERT(rv == nghttp2_buf_len(buf)); + CU_ASSERT(1 == ud.frame_recv_cb_called); + + CU_ASSERT(3000 == session->remote_settings.header_table_size); + CU_ASSERT(16384 == session->remote_settings.initial_window_size); + + nghttp2_bufs_reset(&bufs); + + /* 2 SETTINGS_HEADER_TABLE_SIZE */ + iv[0].settings_id = NGHTTP2_SETTINGS_HEADER_TABLE_SIZE; + iv[0].value = 3001; + + iv[1].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE; + iv[1].value = 16383; + + iv[2].settings_id = NGHTTP2_SETTINGS_HEADER_TABLE_SIZE; + iv[2].value = 3001; + + nghttp2_frame_settings_init(&frame.settings, NGHTTP2_FLAG_NONE, dup_iv(iv, 3), + 3); + + rv = nghttp2_frame_pack_settings(&bufs, &frame.settings); + + CU_ASSERT(0 == rv); + CU_ASSERT(nghttp2_bufs_len(&bufs) > 0); + + nghttp2_frame_settings_free(&frame.settings, mem); + + buf = &bufs.head->buf; + assert(nghttp2_bufs_len(&bufs) == nghttp2_buf_len(buf)); + + ud.frame_recv_cb_called = 0; + + rv = nghttp2_session_mem_recv(session, buf->pos, nghttp2_buf_len(buf)); + + CU_ASSERT(rv == nghttp2_buf_len(buf)); + CU_ASSERT(1 == ud.frame_recv_cb_called); + + CU_ASSERT(3001 == session->remote_settings.header_table_size); + CU_ASSERT(16383 == session->remote_settings.initial_window_size); + + nghttp2_bufs_reset(&bufs); + + /* 2 SETTINGS_HEADER_TABLE_SIZE; first entry clears dynamic header + table. */ + + nghttp2_submit_request(session, NULL, &nv, 1, NULL, NULL); + nghttp2_session_send(session); + + CU_ASSERT(0 < session->hd_deflater.ctx.hd_table.len); + + iv[0].settings_id = NGHTTP2_SETTINGS_HEADER_TABLE_SIZE; + iv[0].value = 0; + + iv[1].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE; + iv[1].value = 16382; + + iv[2].settings_id = NGHTTP2_SETTINGS_HEADER_TABLE_SIZE; + iv[2].value = 4096; + + nghttp2_frame_settings_init(&frame.settings, NGHTTP2_FLAG_NONE, dup_iv(iv, 3), + 3); + + rv = nghttp2_frame_pack_settings(&bufs, &frame.settings); + + CU_ASSERT(0 == rv); + CU_ASSERT(nghttp2_bufs_len(&bufs) > 0); + + nghttp2_frame_settings_free(&frame.settings, mem); + + buf = &bufs.head->buf; + assert(nghttp2_bufs_len(&bufs) == nghttp2_buf_len(buf)); + + ud.frame_recv_cb_called = 0; + + rv = nghttp2_session_mem_recv(session, buf->pos, nghttp2_buf_len(buf)); + + CU_ASSERT(rv == nghttp2_buf_len(buf)); + CU_ASSERT(1 == ud.frame_recv_cb_called); + + CU_ASSERT(4096 == session->remote_settings.header_table_size); + CU_ASSERT(16382 == session->remote_settings.initial_window_size); + CU_ASSERT(0 == session->hd_deflater.ctx.hd_table.len); + + nghttp2_bufs_reset(&bufs); + + /* 2 SETTINGS_HEADER_TABLE_SIZE; second entry clears dynamic header + table. */ + + nghttp2_submit_request(session, NULL, &nv, 1, NULL, NULL); + nghttp2_session_send(session); + + CU_ASSERT(0 < session->hd_deflater.ctx.hd_table.len); + + iv[0].settings_id = NGHTTP2_SETTINGS_HEADER_TABLE_SIZE; + iv[0].value = 3000; + + iv[1].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE; + iv[1].value = 16381; + + iv[2].settings_id = NGHTTP2_SETTINGS_HEADER_TABLE_SIZE; + iv[2].value = 0; + + nghttp2_frame_settings_init(&frame.settings, NGHTTP2_FLAG_NONE, dup_iv(iv, 3), + 3); + + rv = nghttp2_frame_pack_settings(&bufs, &frame.settings); + + CU_ASSERT(0 == rv); + CU_ASSERT(nghttp2_bufs_len(&bufs) > 0); + + nghttp2_frame_settings_free(&frame.settings, mem); + + buf = &bufs.head->buf; + assert(nghttp2_bufs_len(&bufs) == nghttp2_buf_len(buf)); + + ud.frame_recv_cb_called = 0; + + rv = nghttp2_session_mem_recv(session, buf->pos, nghttp2_buf_len(buf)); + + CU_ASSERT(rv == nghttp2_buf_len(buf)); + CU_ASSERT(1 == ud.frame_recv_cb_called); + + CU_ASSERT(0 == session->remote_settings.header_table_size); + CU_ASSERT(16381 == session->remote_settings.initial_window_size); + CU_ASSERT(0 == session->hd_deflater.ctx.hd_table.len); + + nghttp2_bufs_reset(&bufs); + + nghttp2_bufs_free(&bufs); + nghttp2_session_del(session); +} + +void test_nghttp2_session_recv_too_large_frame_length(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + uint8_t buf[NGHTTP2_FRAME_HDLEN]; + nghttp2_outbound_item *item; + nghttp2_frame_hd hd; + + /* Initial max frame size is NGHTTP2_MAX_FRAME_SIZE_MIN */ + nghttp2_frame_hd_init(&hd, NGHTTP2_MAX_FRAME_SIZE_MIN + 1, NGHTTP2_HEADERS, + NGHTTP2_FLAG_NONE, 1); + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + + nghttp2_session_server_new(&session, &callbacks, NULL); + + nghttp2_frame_pack_frame_hd(buf, &hd); + + CU_ASSERT(sizeof(buf) == nghttp2_session_mem_recv(session, buf, sizeof(buf))); + + item = nghttp2_session_get_next_ob_item(session); + + CU_ASSERT(item != NULL); + CU_ASSERT(NGHTTP2_GOAWAY == item->frame.hd.type); + + nghttp2_session_del(session); +} + +void test_nghttp2_session_continue(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + my_user_data user_data; + const nghttp2_nv nv1[] = {MAKE_NV(":method", "GET"), MAKE_NV(":path", "/")}; + const nghttp2_nv nv2[] = {MAKE_NV("user-agent", "nghttp2/1.0.0"), + MAKE_NV("alpha", "bravo")}; + nghttp2_bufs bufs; + nghttp2_buf *buf; + size_t framelen1, framelen2; + ssize_t rv; + uint8_t buffer[4096]; + nghttp2_buf databuf; + nghttp2_frame frame; + nghttp2_nv *nva; + ssize_t nvlen; + const nghttp2_frame *recv_frame; + nghttp2_frame_hd data_hd; + nghttp2_hd_deflater deflater; + nghttp2_mem *mem; + + mem = nghttp2_mem_default(); + frame_pack_bufs_init(&bufs); + nghttp2_buf_wrap_init(&databuf, buffer, sizeof(buffer)); + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.send_callback = null_send_callback; + callbacks.on_frame_recv_callback = on_frame_recv_callback; + callbacks.on_data_chunk_recv_callback = pause_on_data_chunk_recv_callback; + callbacks.on_header_callback = pause_on_header_callback; + callbacks.on_begin_headers_callback = on_begin_headers_callback; + + nghttp2_session_server_new(&session, &callbacks, &user_data); + /* disable strict HTTP layering checks */ + session->opt_flags |= NGHTTP2_OPTMASK_NO_HTTP_MESSAGING; + + nghttp2_hd_deflate_init(&deflater, mem); + + /* Make 2 HEADERS frames */ + nvlen = ARRLEN(nv1); + nghttp2_nv_array_copy(&nva, nv1, nvlen, mem); + nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_END_HEADERS, 1, + NGHTTP2_HCAT_HEADERS, NULL, nva, nvlen); + rv = nghttp2_frame_pack_headers(&bufs, &frame.headers, &deflater); + + CU_ASSERT(0 == rv); + CU_ASSERT(nghttp2_bufs_len(&bufs) > 0); + + nghttp2_frame_headers_free(&frame.headers, mem); + + buf = &bufs.head->buf; + assert(nghttp2_bufs_len(&bufs) == nghttp2_buf_len(buf)); + + framelen1 = nghttp2_buf_len(buf); + databuf.last = nghttp2_cpymem(databuf.last, buf->pos, nghttp2_buf_len(buf)); + + nvlen = ARRLEN(nv2); + nghttp2_nv_array_copy(&nva, nv2, nvlen, mem); + nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_END_HEADERS, 3, + NGHTTP2_HCAT_HEADERS, NULL, nva, nvlen); + nghttp2_bufs_reset(&bufs); + rv = nghttp2_frame_pack_headers(&bufs, &frame.headers, &deflater); + + CU_ASSERT(0 == rv); + CU_ASSERT(nghttp2_bufs_len(&bufs) > 0); + + nghttp2_frame_headers_free(&frame.headers, mem); + + assert(nghttp2_bufs_len(&bufs) == nghttp2_buf_len(buf)); + + framelen2 = nghttp2_buf_len(buf); + databuf.last = nghttp2_cpymem(databuf.last, buf->pos, nghttp2_buf_len(buf)); + + /* Receive 1st HEADERS and pause */ + user_data.begin_headers_cb_called = 0; + user_data.header_cb_called = 0; + rv = + nghttp2_session_mem_recv(session, databuf.pos, nghttp2_buf_len(&databuf)); + + CU_ASSERT(rv >= 0); + databuf.pos += rv; + + recv_frame = user_data.frame; + CU_ASSERT(NGHTTP2_HEADERS == recv_frame->hd.type); + CU_ASSERT(framelen1 - NGHTTP2_FRAME_HDLEN == recv_frame->hd.length); + + CU_ASSERT(1 == user_data.begin_headers_cb_called); + CU_ASSERT(1 == user_data.header_cb_called); + + CU_ASSERT(nghttp2_nv_equal(&nv1[0], &user_data.nv)); + + /* get 2nd header field */ + user_data.begin_headers_cb_called = 0; + user_data.header_cb_called = 0; + rv = + nghttp2_session_mem_recv(session, databuf.pos, nghttp2_buf_len(&databuf)); + + CU_ASSERT(rv >= 0); + databuf.pos += rv; + + CU_ASSERT(0 == user_data.begin_headers_cb_called); + CU_ASSERT(1 == user_data.header_cb_called); + + CU_ASSERT(nghttp2_nv_equal(&nv1[1], &user_data.nv)); + + /* will call end_headers_callback and receive 2nd HEADERS and pause */ + user_data.begin_headers_cb_called = 0; + user_data.header_cb_called = 0; + rv = + nghttp2_session_mem_recv(session, databuf.pos, nghttp2_buf_len(&databuf)); + + CU_ASSERT(rv >= 0); + databuf.pos += rv; + + recv_frame = user_data.frame; + CU_ASSERT(NGHTTP2_HEADERS == recv_frame->hd.type); + CU_ASSERT(framelen2 - NGHTTP2_FRAME_HDLEN == recv_frame->hd.length); + + CU_ASSERT(1 == user_data.begin_headers_cb_called); + CU_ASSERT(1 == user_data.header_cb_called); + + CU_ASSERT(nghttp2_nv_equal(&nv2[0], &user_data.nv)); + + /* get 2nd header field */ + user_data.begin_headers_cb_called = 0; + user_data.header_cb_called = 0; + rv = + nghttp2_session_mem_recv(session, databuf.pos, nghttp2_buf_len(&databuf)); + + CU_ASSERT(rv >= 0); + databuf.pos += rv; + + CU_ASSERT(0 == user_data.begin_headers_cb_called); + CU_ASSERT(1 == user_data.header_cb_called); + + CU_ASSERT(nghttp2_nv_equal(&nv2[1], &user_data.nv)); + + /* No input data, frame_recv_callback is called */ + user_data.begin_headers_cb_called = 0; + user_data.header_cb_called = 0; + user_data.frame_recv_cb_called = 0; + rv = + nghttp2_session_mem_recv(session, databuf.pos, nghttp2_buf_len(&databuf)); + + CU_ASSERT(rv >= 0); + databuf.pos += rv; + + CU_ASSERT(0 == user_data.begin_headers_cb_called); + CU_ASSERT(0 == user_data.header_cb_called); + CU_ASSERT(1 == user_data.frame_recv_cb_called); + + /* Receive DATA */ + nghttp2_frame_hd_init(&data_hd, 16, NGHTTP2_DATA, NGHTTP2_FLAG_NONE, 1); + + nghttp2_buf_reset(&databuf); + nghttp2_frame_pack_frame_hd(databuf.pos, &data_hd); + + /* Intentionally specify larger buffer size to see pause is kicked + in. */ + databuf.last = databuf.end; + + user_data.frame_recv_cb_called = 0; + rv = + nghttp2_session_mem_recv(session, databuf.pos, nghttp2_buf_len(&databuf)); + + CU_ASSERT(16 + NGHTTP2_FRAME_HDLEN == rv); + CU_ASSERT(0 == user_data.frame_recv_cb_called); + + /* Next nghttp2_session_mem_recv invokes on_frame_recv_callback and + pause again in on_data_chunk_recv_callback since we pass same + DATA frame. */ + user_data.frame_recv_cb_called = 0; + rv = + nghttp2_session_mem_recv(session, databuf.pos, nghttp2_buf_len(&databuf)); + CU_ASSERT(16 + NGHTTP2_FRAME_HDLEN == rv); + CU_ASSERT(1 == user_data.frame_recv_cb_called); + + /* And finally call on_frame_recv_callback with 0 size input */ + user_data.frame_recv_cb_called = 0; + rv = nghttp2_session_mem_recv(session, NULL, 0); + CU_ASSERT(0 == rv); + CU_ASSERT(1 == user_data.frame_recv_cb_called); + + nghttp2_bufs_free(&bufs); + nghttp2_hd_deflate_free(&deflater); + nghttp2_session_del(session); +} + +void test_nghttp2_session_add_frame(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + accumulator acc; + my_user_data user_data; + nghttp2_outbound_item *item; + nghttp2_frame *frame; + nghttp2_nv *nva; + ssize_t nvlen; + nghttp2_mem *mem; + + mem = nghttp2_mem_default(); + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.send_callback = accumulator_send_callback; + + acc.length = 0; + user_data.acc = &acc; + + CU_ASSERT(0 == nghttp2_session_client_new(&session, &callbacks, &user_data)); + + item = mem->malloc(sizeof(nghttp2_outbound_item), NULL); + + nghttp2_outbound_item_init(item); + + frame = &item->frame; + + nvlen = ARRLEN(reqnv); + nghttp2_nv_array_copy(&nva, reqnv, nvlen, mem); + + nghttp2_frame_headers_init( + &frame->headers, NGHTTP2_FLAG_END_HEADERS | NGHTTP2_FLAG_PRIORITY, + session->next_stream_id, NGHTTP2_HCAT_REQUEST, NULL, nva, nvlen); + + session->next_stream_id += 2; + + CU_ASSERT(0 == nghttp2_session_add_item(session, item)); + CU_ASSERT(NULL != nghttp2_outbound_queue_top(&session->ob_syn)); + CU_ASSERT(0 == nghttp2_session_send(session)); + CU_ASSERT(NGHTTP2_HEADERS == acc.buf[3]); + CU_ASSERT((NGHTTP2_FLAG_END_HEADERS | NGHTTP2_FLAG_PRIORITY) == acc.buf[4]); + /* check stream id */ + CU_ASSERT(1 == nghttp2_get_uint32(&acc.buf[5])); + + nghttp2_session_del(session); +} + +void test_nghttp2_session_on_request_headers_received(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + my_user_data user_data; + nghttp2_frame frame; + nghttp2_stream *stream; + int32_t stream_id = 1; + nghttp2_nv malformed_nva[] = {MAKE_NV(":path", "\x01")}; + nghttp2_nv *nva; + size_t nvlen; + nghttp2_priority_spec pri_spec; + nghttp2_mem *mem; + + mem = nghttp2_mem_default(); + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.on_begin_headers_callback = on_begin_headers_callback; + callbacks.on_invalid_frame_recv_callback = on_invalid_frame_recv_callback; + + nghttp2_session_server_new(&session, &callbacks, &user_data); + + nghttp2_priority_spec_init(&pri_spec, 0, 255, 0); + + nghttp2_frame_headers_init( + &frame.headers, NGHTTP2_FLAG_END_HEADERS | NGHTTP2_FLAG_PRIORITY, + stream_id, NGHTTP2_HCAT_REQUEST, &pri_spec, NULL, 0); + + user_data.begin_headers_cb_called = 0; + user_data.invalid_frame_recv_cb_called = 0; + + CU_ASSERT(0 == nghttp2_session_on_request_headers_received(session, &frame)); + CU_ASSERT(1 == user_data.begin_headers_cb_called); + stream = nghttp2_session_get_stream(session, stream_id); + CU_ASSERT(NGHTTP2_STREAM_OPENING == stream->state); + CU_ASSERT(255 == stream->weight); + + nghttp2_frame_headers_free(&frame.headers, mem); + + /* More than un-ACKed max concurrent streams leads REFUSED_STREAM */ + session->pending_local_max_concurrent_stream = 1; + nghttp2_frame_headers_init(&frame.headers, + NGHTTP2_FLAG_END_HEADERS | NGHTTP2_FLAG_PRIORITY, + 3, NGHTTP2_HCAT_HEADERS, NULL, NULL, 0); + user_data.invalid_frame_recv_cb_called = 0; + CU_ASSERT(NGHTTP2_ERR_IGN_HEADER_BLOCK == + nghttp2_session_on_request_headers_received(session, &frame)); + CU_ASSERT(1 == user_data.invalid_frame_recv_cb_called); + CU_ASSERT(0 == (session->goaway_flags & NGHTTP2_GOAWAY_TERM_ON_SEND)); + + nghttp2_frame_headers_free(&frame.headers, mem); + session->local_settings.max_concurrent_streams = + NGHTTP2_INITIAL_MAX_CONCURRENT_STREAMS; + + /* Stream ID less than or equal to the previouly received request + HEADERS is just ignored due to race condition */ + nghttp2_frame_headers_init(&frame.headers, + NGHTTP2_FLAG_END_HEADERS | NGHTTP2_FLAG_PRIORITY, + 3, NGHTTP2_HCAT_HEADERS, NULL, NULL, 0); + user_data.invalid_frame_recv_cb_called = 0; + CU_ASSERT(NGHTTP2_ERR_IGN_HEADER_BLOCK == + nghttp2_session_on_request_headers_received(session, &frame)); + CU_ASSERT(0 == user_data.invalid_frame_recv_cb_called); + CU_ASSERT(0 == (session->goaway_flags & NGHTTP2_GOAWAY_TERM_ON_SEND)); + + nghttp2_frame_headers_free(&frame.headers, mem); + + /* Stream ID is our side and it is idle stream ID, then treat it as + connection error */ + nghttp2_frame_headers_init(&frame.headers, + NGHTTP2_FLAG_END_HEADERS | NGHTTP2_FLAG_PRIORITY, + 2, NGHTTP2_HCAT_HEADERS, NULL, NULL, 0); + user_data.invalid_frame_recv_cb_called = 0; + CU_ASSERT(NGHTTP2_ERR_IGN_HEADER_BLOCK == + nghttp2_session_on_request_headers_received(session, &frame)); + CU_ASSERT(1 == user_data.invalid_frame_recv_cb_called); + CU_ASSERT(session->goaway_flags & NGHTTP2_GOAWAY_TERM_ON_SEND); + + nghttp2_frame_headers_free(&frame.headers, mem); + + nghttp2_session_del(session); + + /* Check malformed headers. The library accept it. */ + nghttp2_session_server_new(&session, &callbacks, &user_data); + + nvlen = ARRLEN(malformed_nva); + nghttp2_nv_array_copy(&nva, malformed_nva, nvlen, mem); + nghttp2_frame_headers_init(&frame.headers, + NGHTTP2_FLAG_END_HEADERS | NGHTTP2_FLAG_PRIORITY, + 1, NGHTTP2_HCAT_HEADERS, NULL, nva, nvlen); + user_data.begin_headers_cb_called = 0; + user_data.invalid_frame_recv_cb_called = 0; + CU_ASSERT(0 == nghttp2_session_on_request_headers_received(session, &frame)); + CU_ASSERT(1 == user_data.begin_headers_cb_called); + CU_ASSERT(0 == user_data.invalid_frame_recv_cb_called); + + nghttp2_frame_headers_free(&frame.headers, mem); + + nghttp2_session_del(session); + + /* Check client side */ + nghttp2_session_client_new(&session, &callbacks, &user_data); + + /* Receiving peer's idle stream ID is subject to connection error */ + nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_END_HEADERS, 2, + NGHTTP2_HCAT_REQUEST, NULL, NULL, 0); + + user_data.invalid_frame_recv_cb_called = 0; + CU_ASSERT(NGHTTP2_ERR_IGN_HEADER_BLOCK == + nghttp2_session_on_request_headers_received(session, &frame)); + CU_ASSERT(1 == user_data.invalid_frame_recv_cb_called); + CU_ASSERT(session->goaway_flags & NGHTTP2_GOAWAY_TERM_ON_SEND); + + nghttp2_frame_headers_free(&frame.headers, mem); + + nghttp2_session_del(session); + + nghttp2_session_client_new(&session, &callbacks, &user_data); + + /* Receiving our's idle stream ID is subject to connection error */ + nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_END_HEADERS, 1, + NGHTTP2_HCAT_REQUEST, NULL, NULL, 0); + + user_data.invalid_frame_recv_cb_called = 0; + CU_ASSERT(NGHTTP2_ERR_IGN_HEADER_BLOCK == + nghttp2_session_on_request_headers_received(session, &frame)); + CU_ASSERT(1 == user_data.invalid_frame_recv_cb_called); + CU_ASSERT(session->goaway_flags & NGHTTP2_GOAWAY_TERM_ON_SEND); + + nghttp2_frame_headers_free(&frame.headers, mem); + + nghttp2_session_del(session); + + nghttp2_session_client_new(&session, &callbacks, &user_data); + + session->next_stream_id = 5; + + /* Stream ID which is not idle and not in stream map is just + ignored */ + nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_END_HEADERS, 3, + NGHTTP2_HCAT_REQUEST, NULL, NULL, 0); + + user_data.invalid_frame_recv_cb_called = 0; + CU_ASSERT(NGHTTP2_ERR_IGN_HEADER_BLOCK == + nghttp2_session_on_request_headers_received(session, &frame)); + CU_ASSERT(0 == user_data.invalid_frame_recv_cb_called); + CU_ASSERT(0 == (session->goaway_flags & NGHTTP2_GOAWAY_TERM_ON_SEND)); + + nghttp2_frame_headers_free(&frame.headers, mem); + + nghttp2_session_del(session); + + nghttp2_session_server_new(&session, &callbacks, &user_data); + + /* Stream ID which is equal to local_last_stream_id is ok. */ + session->local_last_stream_id = 3; + + nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_END_HEADERS, 3, + NGHTTP2_HCAT_REQUEST, NULL, NULL, 0); + + CU_ASSERT(0 == nghttp2_session_on_request_headers_received(session, &frame)); + + nghttp2_frame_headers_free(&frame.headers, mem); + + /* If GOAWAY has been sent, new stream is ignored */ + nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_END_HEADERS, 5, + NGHTTP2_HCAT_REQUEST, NULL, NULL, 0); + + session->goaway_flags |= NGHTTP2_GOAWAY_SENT; + user_data.invalid_frame_recv_cb_called = 0; + CU_ASSERT(NGHTTP2_ERR_IGN_HEADER_BLOCK == + nghttp2_session_on_request_headers_received(session, &frame)); + CU_ASSERT(0 == user_data.invalid_frame_recv_cb_called); + CU_ASSERT(0 == (session->goaway_flags & NGHTTP2_GOAWAY_TERM_ON_SEND)); + + nghttp2_frame_headers_free(&frame.headers, mem); + + nghttp2_session_del(session); +} + +void test_nghttp2_session_on_response_headers_received(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + my_user_data user_data; + nghttp2_frame frame; + nghttp2_stream *stream; + nghttp2_mem *mem; + + mem = nghttp2_mem_default(); + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.on_begin_headers_callback = on_begin_headers_callback; + callbacks.on_invalid_frame_recv_callback = on_invalid_frame_recv_callback; + + nghttp2_session_client_new(&session, &callbacks, &user_data); + stream = nghttp2_session_open_stream(session, 1, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, + NGHTTP2_STREAM_OPENING, NULL); + nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_END_HEADERS, 1, + NGHTTP2_HCAT_HEADERS, NULL, NULL, 0); + + user_data.begin_headers_cb_called = 0; + user_data.invalid_frame_recv_cb_called = 0; + + CU_ASSERT(0 == nghttp2_session_on_response_headers_received(session, &frame, + stream)); + CU_ASSERT(1 == user_data.begin_headers_cb_called); + CU_ASSERT(NGHTTP2_STREAM_OPENED == stream->state); + + nghttp2_frame_headers_free(&frame.headers, mem); + nghttp2_session_del(session); +} + +void test_nghttp2_session_on_headers_received(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + my_user_data user_data; + nghttp2_frame frame; + nghttp2_stream *stream; + nghttp2_mem *mem; + + mem = nghttp2_mem_default(); + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.on_begin_headers_callback = on_begin_headers_callback; + callbacks.on_invalid_frame_recv_callback = on_invalid_frame_recv_callback; + + nghttp2_session_client_new(&session, &callbacks, &user_data); + stream = nghttp2_session_open_stream(session, 1, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, NGHTTP2_STREAM_OPENED, + NULL); + nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_WR); + nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_END_HEADERS, 1, + NGHTTP2_HCAT_HEADERS, NULL, NULL, 0); + + user_data.begin_headers_cb_called = 0; + user_data.invalid_frame_recv_cb_called = 0; + + CU_ASSERT(0 == nghttp2_session_on_headers_received(session, &frame, stream)); + CU_ASSERT(1 == user_data.begin_headers_cb_called); + CU_ASSERT(NGHTTP2_STREAM_OPENED == stream->state); + + /* stream closed */ + frame.hd.flags |= NGHTTP2_FLAG_END_STREAM; + + CU_ASSERT(0 == nghttp2_session_on_headers_received(session, &frame, stream)); + CU_ASSERT(2 == user_data.begin_headers_cb_called); + + /* Check to see when NGHTTP2_STREAM_CLOSING, incoming HEADERS is + discarded. */ + stream = nghttp2_session_open_stream(session, 3, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, + NGHTTP2_STREAM_CLOSING, NULL); + frame.hd.stream_id = 3; + frame.hd.flags = NGHTTP2_FLAG_END_HEADERS; + CU_ASSERT(NGHTTP2_ERR_IGN_HEADER_BLOCK == + nghttp2_session_on_headers_received(session, &frame, stream)); + /* See no counters are updated */ + CU_ASSERT(2 == user_data.begin_headers_cb_called); + CU_ASSERT(0 == user_data.invalid_frame_recv_cb_called); + + /* Server initiated stream */ + stream = nghttp2_session_open_stream(session, 2, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, + NGHTTP2_STREAM_OPENING, NULL); + + /* half closed (remote) */ + frame.hd.flags = NGHTTP2_FLAG_END_HEADERS | NGHTTP2_FLAG_END_STREAM; + frame.hd.stream_id = 2; + + CU_ASSERT(0 == nghttp2_session_on_headers_received(session, &frame, stream)); + CU_ASSERT(3 == user_data.begin_headers_cb_called); + CU_ASSERT(NGHTTP2_STREAM_OPENING == stream->state); + + nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_RD); + + /* Further reception of HEADERS is subject to stream error */ + CU_ASSERT(NGHTTP2_ERR_IGN_HEADER_BLOCK == + nghttp2_session_on_headers_received(session, &frame, stream)); + CU_ASSERT(1 == user_data.invalid_frame_recv_cb_called); + + nghttp2_frame_headers_free(&frame.headers, mem); + + nghttp2_session_del(session); +} + +void test_nghttp2_session_on_push_response_headers_received(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + my_user_data user_data; + nghttp2_frame frame; + nghttp2_stream *stream; + nghttp2_outbound_item *item; + nghttp2_mem *mem; + + mem = nghttp2_mem_default(); + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.send_callback = null_send_callback; + callbacks.on_begin_headers_callback = on_begin_headers_callback; + callbacks.on_invalid_frame_recv_callback = on_invalid_frame_recv_callback; + + nghttp2_session_client_new(&session, &callbacks, &user_data); + stream = nghttp2_session_open_stream(session, 2, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, + NGHTTP2_STREAM_RESERVED, NULL); + nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_END_HEADERS, 2, + NGHTTP2_HCAT_HEADERS, NULL, NULL, 0); + /* nghttp2_session_on_push_response_headers_received assumes + stream's state is NGHTTP2_STREAM_RESERVED and session->server is + 0. */ + + user_data.begin_headers_cb_called = 0; + user_data.invalid_frame_recv_cb_called = 0; + + CU_ASSERT(0 == nghttp2_session_on_push_response_headers_received( + session, &frame, stream)); + CU_ASSERT(1 == user_data.begin_headers_cb_called); + CU_ASSERT(NGHTTP2_STREAM_OPENED == stream->state); + CU_ASSERT(1 == session->num_incoming_streams); + CU_ASSERT(0 == (stream->flags & NGHTTP2_STREAM_FLAG_PUSH)); + + /* If un-ACKed max concurrent streams limit is exceeded, + RST_STREAMed */ + session->pending_local_max_concurrent_stream = 1; + stream = nghttp2_session_open_stream(session, 4, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, + NGHTTP2_STREAM_RESERVED, NULL); + frame.hd.stream_id = 4; + CU_ASSERT(NGHTTP2_ERR_IGN_HEADER_BLOCK == + nghttp2_session_on_push_response_headers_received(session, &frame, + stream)); + item = nghttp2_session_get_next_ob_item(session); + CU_ASSERT(NGHTTP2_RST_STREAM == item->frame.hd.type); + CU_ASSERT(NGHTTP2_REFUSED_STREAM == item->frame.rst_stream.error_code); + CU_ASSERT(1 == session->num_incoming_streams); + + CU_ASSERT(0 == nghttp2_session_send(session)); + CU_ASSERT(1 == session->num_incoming_streams); + + /* If ACKed max concurrent streams limit is exceeded, GOAWAY is + issued */ + session->local_settings.max_concurrent_streams = 1; + + stream = nghttp2_session_open_stream(session, 6, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, + NGHTTP2_STREAM_RESERVED, NULL); + frame.hd.stream_id = 6; + + CU_ASSERT(NGHTTP2_ERR_IGN_HEADER_BLOCK == + nghttp2_session_on_push_response_headers_received(session, &frame, + stream)); + item = nghttp2_session_get_next_ob_item(session); + CU_ASSERT(NGHTTP2_GOAWAY == item->frame.hd.type); + CU_ASSERT(NGHTTP2_PROTOCOL_ERROR == item->frame.goaway.error_code); + CU_ASSERT(1 == session->num_incoming_streams); + + nghttp2_frame_headers_free(&frame.headers, mem); + nghttp2_session_del(session); +} + +void test_nghttp2_session_on_priority_received(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + my_user_data user_data; + nghttp2_frame frame; + nghttp2_stream *stream, *dep_stream; + nghttp2_priority_spec pri_spec; + nghttp2_outbound_item *item; + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.on_frame_recv_callback = on_frame_recv_callback; + callbacks.on_invalid_frame_recv_callback = on_invalid_frame_recv_callback; + + nghttp2_session_server_new(&session, &callbacks, &user_data); + stream = nghttp2_session_open_stream(session, 1, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, + NGHTTP2_STREAM_OPENING, NULL); + + nghttp2_priority_spec_init(&pri_spec, 0, 2, 0); + + nghttp2_frame_priority_init(&frame.priority, 1, &pri_spec); + + /* depend on stream 0 */ + CU_ASSERT(0 == nghttp2_session_on_priority_received(session, &frame)); + + CU_ASSERT(2 == stream->weight); + + stream = nghttp2_session_open_stream(session, 2, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, + NGHTTP2_STREAM_OPENING, NULL); + + dep_stream = nghttp2_session_open_stream(session, 3, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, + NGHTTP2_STREAM_OPENING, NULL); + + frame.hd.stream_id = 2; + + /* using dependency stream */ + nghttp2_priority_spec_init(&frame.priority.pri_spec, 3, 1, 0); + + CU_ASSERT(0 == nghttp2_session_on_priority_received(session, &frame)); + CU_ASSERT(dep_stream == stream->dep_prev); + + /* PRIORITY against idle stream */ + + frame.hd.stream_id = 100; + + CU_ASSERT(0 == nghttp2_session_on_priority_received(session, &frame)); + + stream = nghttp2_session_get_stream_raw(session, frame.hd.stream_id); + + CU_ASSERT(NGHTTP2_STREAM_IDLE == stream->state); + CU_ASSERT(dep_stream == stream->dep_prev); + + nghttp2_frame_priority_free(&frame.priority); + nghttp2_session_del(session); + + /* Check dep_stream_id == stream_id case */ + nghttp2_session_server_new(&session, &callbacks, &user_data); + nghttp2_session_open_stream(session, 1, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, NGHTTP2_STREAM_OPENED, NULL); + + nghttp2_priority_spec_init(&pri_spec, 1, 0, 0); + + nghttp2_frame_priority_init(&frame.priority, 1, &pri_spec); + + CU_ASSERT(0 == nghttp2_session_on_priority_received(session, &frame)); + + item = nghttp2_session_get_next_ob_item(session); + + CU_ASSERT(NGHTTP2_GOAWAY == item->frame.hd.type); + + nghttp2_frame_priority_free(&frame.priority); + nghttp2_session_del(session); +} + +void test_nghttp2_session_on_rst_stream_received(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + my_user_data user_data; + nghttp2_frame frame; + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + nghttp2_session_server_new(&session, &callbacks, &user_data); + nghttp2_session_open_stream(session, 1, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, NGHTTP2_STREAM_OPENING, NULL); + + nghttp2_frame_rst_stream_init(&frame.rst_stream, 1, NGHTTP2_PROTOCOL_ERROR); + + CU_ASSERT(0 == nghttp2_session_on_rst_stream_received(session, &frame)); + CU_ASSERT(NULL == nghttp2_session_get_stream(session, 1)); + + nghttp2_frame_rst_stream_free(&frame.rst_stream); + nghttp2_session_del(session); +} + +void test_nghttp2_session_on_settings_received(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + my_user_data user_data; + nghttp2_stream *stream1, *stream2; + nghttp2_frame frame; + const size_t niv = 5; + nghttp2_settings_entry iv[255]; + nghttp2_outbound_item *item; + nghttp2_nv nv = MAKE_NV(":authority", "example.org"); + nghttp2_mem *mem; + + mem = nghttp2_mem_default(); + + iv[0].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS; + iv[0].value = 50; + + iv[1].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS; + iv[1].value = 1000000009; + + iv[2].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE; + iv[2].value = 64 * 1024; + + iv[3].settings_id = NGHTTP2_SETTINGS_HEADER_TABLE_SIZE; + iv[3].value = 1024; + + iv[4].settings_id = NGHTTP2_SETTINGS_ENABLE_PUSH; + iv[4].value = 0; + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.send_callback = null_send_callback; + + nghttp2_session_client_new(&session, &callbacks, &user_data); + session->remote_settings.initial_window_size = 16 * 1024; + + stream1 = nghttp2_session_open_stream(session, 1, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, + NGHTTP2_STREAM_OPENING, NULL); + stream2 = nghttp2_session_open_stream(session, 2, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, + NGHTTP2_STREAM_OPENING, NULL); + /* Set window size for each streams and will see how settings + updates these values */ + stream1->remote_window_size = 16 * 1024; + stream2->remote_window_size = -48 * 1024; + + nghttp2_frame_settings_init(&frame.settings, NGHTTP2_FLAG_NONE, + dup_iv(iv, niv), niv); + + CU_ASSERT(0 == nghttp2_session_on_settings_received(session, &frame, 0)); + CU_ASSERT(1000000009 == session->remote_settings.max_concurrent_streams); + CU_ASSERT(64 * 1024 == session->remote_settings.initial_window_size); + CU_ASSERT(1024 == session->remote_settings.header_table_size); + CU_ASSERT(0 == session->remote_settings.enable_push); + + CU_ASSERT(64 * 1024 == stream1->remote_window_size); + CU_ASSERT(0 == stream2->remote_window_size); + + frame.settings.iv[2].value = 16 * 1024; + + CU_ASSERT(0 == nghttp2_session_on_settings_received(session, &frame, 0)); + + CU_ASSERT(16 * 1024 == stream1->remote_window_size); + CU_ASSERT(-48 * 1024 == stream2->remote_window_size); + + CU_ASSERT(16 * 1024 == nghttp2_session_get_stream_remote_window_size( + session, stream1->stream_id)); + CU_ASSERT(0 == nghttp2_session_get_stream_remote_window_size( + session, stream2->stream_id)); + + nghttp2_frame_settings_free(&frame.settings, mem); + + nghttp2_session_del(session); + + /* Check ACK with niv > 0 */ + nghttp2_session_server_new(&session, &callbacks, NULL); + nghttp2_frame_settings_init(&frame.settings, NGHTTP2_FLAG_ACK, dup_iv(iv, 1), + 1); + /* Specify inflight_iv deliberately */ + session->inflight_iv = frame.settings.iv; + session->inflight_niv = frame.settings.niv; + + CU_ASSERT(0 == nghttp2_session_on_settings_received(session, &frame, 0)); + item = nghttp2_session_get_next_ob_item(session); + CU_ASSERT(item != NULL); + CU_ASSERT(NGHTTP2_GOAWAY == item->frame.hd.type); + + session->inflight_iv = NULL; + session->inflight_niv = -1; + + nghttp2_frame_settings_free(&frame.settings, mem); + nghttp2_session_del(session); + + /* Check ACK against no inflight SETTINGS */ + nghttp2_session_server_new(&session, &callbacks, NULL); + nghttp2_frame_settings_init(&frame.settings, NGHTTP2_FLAG_ACK, NULL, 0); + + CU_ASSERT(0 == nghttp2_session_on_settings_received(session, &frame, 0)); + item = nghttp2_session_get_next_ob_item(session); + CU_ASSERT(item != NULL); + CU_ASSERT(NGHTTP2_GOAWAY == item->frame.hd.type); + + nghttp2_frame_settings_free(&frame.settings, mem); + nghttp2_session_del(session); + + /* Check that 2 SETTINGS_HEADER_TABLE_SIZE 0 and 4096 are included + and header table size is once cleared to 0. */ + nghttp2_session_client_new(&session, &callbacks, NULL); + + nghttp2_submit_request(session, NULL, &nv, 1, NULL, NULL); + + nghttp2_session_send(session); + + CU_ASSERT(session->hd_deflater.ctx.hd_table.len > 0); + + iv[0].settings_id = NGHTTP2_SETTINGS_HEADER_TABLE_SIZE; + iv[0].value = 0; + + iv[1].settings_id = NGHTTP2_SETTINGS_HEADER_TABLE_SIZE; + iv[1].value = 2048; + + nghttp2_frame_settings_init(&frame.settings, NGHTTP2_FLAG_NONE, dup_iv(iv, 2), + 2); + + CU_ASSERT(0 == nghttp2_session_on_settings_received(session, &frame, 0)); + + CU_ASSERT(0 == session->hd_deflater.ctx.hd_table.len); + CU_ASSERT(2048 == session->hd_deflater.ctx.hd_table_bufsize_max); + CU_ASSERT(2048 == session->remote_settings.header_table_size); + + nghttp2_frame_settings_free(&frame.settings, mem); + nghttp2_session_del(session); + + /* Check too large SETTINGS_MAX_FRAME_SIZE */ + nghttp2_session_server_new(&session, &callbacks, NULL); + + iv[0].settings_id = NGHTTP2_SETTINGS_MAX_FRAME_SIZE; + iv[0].value = NGHTTP2_MAX_FRAME_SIZE_MAX + 1; + + nghttp2_frame_settings_init(&frame.settings, NGHTTP2_FLAG_NONE, dup_iv(iv, 1), + 1); + + CU_ASSERT(0 == nghttp2_session_on_settings_received(session, &frame, 0)); + + item = nghttp2_session_get_next_ob_item(session); + + CU_ASSERT(item != NULL); + CU_ASSERT(NGHTTP2_GOAWAY == item->frame.hd.type); + + nghttp2_frame_settings_free(&frame.settings, mem); + nghttp2_session_del(session); +} + +void test_nghttp2_session_on_push_promise_received(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + my_user_data user_data; + nghttp2_frame frame; + nghttp2_stream *stream, *promised_stream; + nghttp2_outbound_item *item; + nghttp2_nv malformed_nva[] = {MAKE_NV(":path", "\x01")}; + nghttp2_nv *nva; + size_t nvlen; + nghttp2_mem *mem; + nghttp2_settings_entry iv = {NGHTTP2_SETTINGS_ENABLE_PUSH, 0}; + + mem = nghttp2_mem_default(); + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.send_callback = null_send_callback; + callbacks.on_begin_headers_callback = on_begin_headers_callback; + callbacks.on_invalid_frame_recv_callback = on_invalid_frame_recv_callback; + + nghttp2_session_client_new(&session, &callbacks, &user_data); + + stream = nghttp2_session_open_stream(session, 1, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, + NGHTTP2_STREAM_OPENING, NULL); + nghttp2_frame_push_promise_init(&frame.push_promise, NGHTTP2_FLAG_END_HEADERS, + 1, 2, NULL, 0); + + user_data.begin_headers_cb_called = 0; + user_data.invalid_frame_recv_cb_called = 0; + + CU_ASSERT(0 == nghttp2_session_on_push_promise_received(session, &frame)); + + CU_ASSERT(1 == user_data.begin_headers_cb_called); + promised_stream = nghttp2_session_get_stream(session, 2); + CU_ASSERT(NGHTTP2_STREAM_RESERVED == promised_stream->state); + CU_ASSERT(2 == session->last_recv_stream_id); + + /* Attempt to PUSH_PROMISE against half close (remote) */ + nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_RD); + frame.push_promise.promised_stream_id = 4; + + user_data.begin_headers_cb_called = 0; + user_data.invalid_frame_recv_cb_called = 0; + CU_ASSERT(NGHTTP2_ERR_IGN_HEADER_BLOCK == + nghttp2_session_on_push_promise_received(session, &frame)); + + CU_ASSERT(0 == user_data.begin_headers_cb_called); + CU_ASSERT(1 == user_data.invalid_frame_recv_cb_called); + CU_ASSERT(NULL == nghttp2_session_get_stream(session, 4)); + item = nghttp2_session_get_next_ob_item(session); + CU_ASSERT(NGHTTP2_RST_STREAM == item->frame.hd.type); + CU_ASSERT(4 == item->frame.hd.stream_id); + CU_ASSERT(NGHTTP2_PROTOCOL_ERROR == item->frame.rst_stream.error_code); + CU_ASSERT(0 == nghttp2_session_send(session)); + CU_ASSERT(4 == session->last_recv_stream_id); + + /* Attempt to PUSH_PROMISE against stream in closing state */ + stream->shut_flags = NGHTTP2_SHUT_NONE; + stream->state = NGHTTP2_STREAM_CLOSING; + frame.push_promise.promised_stream_id = 6; + + user_data.begin_headers_cb_called = 0; + user_data.invalid_frame_recv_cb_called = 0; + CU_ASSERT(NGHTTP2_ERR_IGN_HEADER_BLOCK == + nghttp2_session_on_push_promise_received(session, &frame)); + + CU_ASSERT(0 == user_data.begin_headers_cb_called); + CU_ASSERT(NULL == nghttp2_session_get_stream(session, 6)); + item = nghttp2_session_get_next_ob_item(session); + CU_ASSERT(NGHTTP2_RST_STREAM == item->frame.hd.type); + CU_ASSERT(6 == item->frame.hd.stream_id); + CU_ASSERT(NGHTTP2_REFUSED_STREAM == item->frame.rst_stream.error_code); + CU_ASSERT(0 == nghttp2_session_send(session)); + + /* Attempt to PUSH_PROMISE against non-existent stream */ + frame.hd.stream_id = 3; + frame.push_promise.promised_stream_id = 8; + + user_data.begin_headers_cb_called = 0; + user_data.invalid_frame_recv_cb_called = 0; + CU_ASSERT(NGHTTP2_ERR_IGN_HEADER_BLOCK == + nghttp2_session_on_push_promise_received(session, &frame)); + + CU_ASSERT(0 == user_data.begin_headers_cb_called); + CU_ASSERT(NULL == nghttp2_session_get_stream(session, 8)); + item = nghttp2_session_get_next_ob_item(session); + CU_ASSERT(NGHTTP2_GOAWAY == item->frame.hd.type); + CU_ASSERT(0 == item->frame.hd.stream_id); + CU_ASSERT(NGHTTP2_PROTOCOL_ERROR == item->frame.goaway.error_code); + CU_ASSERT(0 == nghttp2_session_send(session)); + + nghttp2_session_del(session); + + nghttp2_session_client_new(&session, &callbacks, &user_data); + + stream = nghttp2_session_open_stream(session, 1, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, + NGHTTP2_STREAM_OPENING, NULL); + + /* Same ID twice */ + stream->state = NGHTTP2_STREAM_OPENING; + + user_data.begin_headers_cb_called = 0; + user_data.invalid_frame_recv_cb_called = 0; + CU_ASSERT(NGHTTP2_ERR_IGN_HEADER_BLOCK == + nghttp2_session_on_push_promise_received(session, &frame)); + + CU_ASSERT(0 == user_data.begin_headers_cb_called); + CU_ASSERT(NULL == nghttp2_session_get_stream(session, 8)); + item = nghttp2_session_get_next_ob_item(session); + CU_ASSERT(NGHTTP2_GOAWAY == item->frame.hd.type); + CU_ASSERT(NGHTTP2_PROTOCOL_ERROR == item->frame.goaway.error_code); + CU_ASSERT(0 == nghttp2_session_send(session)); + + /* After GOAWAY, PUSH_PROMISE will be discarded */ + frame.push_promise.promised_stream_id = 10; + + user_data.begin_headers_cb_called = 0; + user_data.invalid_frame_recv_cb_called = 0; + CU_ASSERT(NGHTTP2_ERR_IGN_HEADER_BLOCK == + nghttp2_session_on_push_promise_received(session, &frame)); + + CU_ASSERT(0 == user_data.begin_headers_cb_called); + CU_ASSERT(NULL == nghttp2_session_get_stream(session, 10)); + CU_ASSERT(NULL == nghttp2_session_get_next_ob_item(session)); + + nghttp2_frame_push_promise_free(&frame.push_promise, mem); + nghttp2_session_del(session); + + nghttp2_session_client_new(&session, &callbacks, &user_data); + + nghttp2_session_open_stream(session, 2, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, NGHTTP2_STREAM_RESERVED, NULL); + /* Attempt to PUSH_PROMISE against reserved (remote) stream */ + nghttp2_frame_push_promise_init(&frame.push_promise, NGHTTP2_FLAG_END_HEADERS, + 2, 4, NULL, 0); + + user_data.begin_headers_cb_called = 0; + user_data.invalid_frame_recv_cb_called = 0; + CU_ASSERT(NGHTTP2_ERR_IGN_HEADER_BLOCK == + nghttp2_session_on_push_promise_received(session, &frame)); + + CU_ASSERT(0 == user_data.begin_headers_cb_called); + CU_ASSERT(1 == user_data.invalid_frame_recv_cb_called); + + nghttp2_frame_push_promise_free(&frame.push_promise, mem); + nghttp2_session_del(session); + + /* Disable PUSH */ + nghttp2_session_client_new(&session, &callbacks, &user_data); + + nghttp2_session_open_stream(session, 1, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, NGHTTP2_STREAM_OPENING, NULL); + + session->local_settings.enable_push = 0; + + nghttp2_frame_push_promise_init(&frame.push_promise, NGHTTP2_FLAG_END_HEADERS, + 1, 2, NULL, 0); + + user_data.begin_headers_cb_called = 0; + user_data.invalid_frame_recv_cb_called = 0; + CU_ASSERT(NGHTTP2_ERR_IGN_HEADER_BLOCK == + nghttp2_session_on_push_promise_received(session, &frame)); + + CU_ASSERT(0 == user_data.begin_headers_cb_called); + CU_ASSERT(1 == user_data.invalid_frame_recv_cb_called); + + nghttp2_frame_push_promise_free(&frame.push_promise, mem); + nghttp2_session_del(session); + + /* Check malformed headers. We accept malformed headers */ + nghttp2_session_client_new(&session, &callbacks, &user_data); + + nghttp2_session_open_stream(session, 1, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, NGHTTP2_STREAM_OPENING, NULL); + nvlen = ARRLEN(malformed_nva); + nghttp2_nv_array_copy(&nva, malformed_nva, nvlen, mem); + nghttp2_frame_push_promise_init(&frame.push_promise, NGHTTP2_FLAG_END_HEADERS, + 1, 2, nva, nvlen); + user_data.begin_headers_cb_called = 0; + user_data.invalid_frame_recv_cb_called = 0; + CU_ASSERT(0 == nghttp2_session_on_push_promise_received(session, &frame)); + + CU_ASSERT(1 == user_data.begin_headers_cb_called); + CU_ASSERT(0 == user_data.invalid_frame_recv_cb_called); + + nghttp2_frame_push_promise_free(&frame.push_promise, mem); + nghttp2_session_del(session); + + /* If local_settings.enable_push = 0 is pending, but not acked from + peer, incoming PUSH_PROMISE is rejected */ + nghttp2_session_client_new(&session, &callbacks, &user_data); + + nghttp2_session_open_stream(session, 1, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, NGHTTP2_STREAM_OPENING, NULL); + /* Submit settings with ENABLE_PUSH = 0 (thus disabling push) */ + nghttp2_submit_settings(session, NGHTTP2_FLAG_NONE, &iv, 1); + + nghttp2_frame_push_promise_init(&frame.push_promise, NGHTTP2_FLAG_END_HEADERS, + 1, 2, NULL, 0); + + CU_ASSERT(NGHTTP2_ERR_IGN_HEADER_BLOCK == + nghttp2_session_on_push_promise_received(session, &frame)); + + nghttp2_frame_push_promise_free(&frame.push_promise, mem); + nghttp2_session_del(session); +} + +void test_nghttp2_session_on_ping_received(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + my_user_data user_data; + nghttp2_frame frame; + nghttp2_outbound_item *top; + const uint8_t opaque_data[] = "01234567"; + + user_data.frame_recv_cb_called = 0; + user_data.invalid_frame_recv_cb_called = 0; + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.on_frame_recv_callback = on_frame_recv_callback; + callbacks.on_invalid_frame_recv_callback = on_invalid_frame_recv_callback; + + nghttp2_session_client_new(&session, &callbacks, &user_data); + nghttp2_frame_ping_init(&frame.ping, NGHTTP2_FLAG_ACK, opaque_data); + + CU_ASSERT(0 == nghttp2_session_on_ping_received(session, &frame)); + CU_ASSERT(1 == user_data.frame_recv_cb_called); + + /* Since this ping frame has ACK flag set, no further action is + performed. */ + CU_ASSERT(NULL == nghttp2_outbound_queue_top(&session->ob_urgent)); + + /* Clear the flag, and receive it again */ + frame.hd.flags = NGHTTP2_FLAG_NONE; + + CU_ASSERT(0 == nghttp2_session_on_ping_received(session, &frame)); + CU_ASSERT(2 == user_data.frame_recv_cb_called); + top = nghttp2_outbound_queue_top(&session->ob_urgent); + CU_ASSERT(NGHTTP2_PING == top->frame.hd.type); + CU_ASSERT(NGHTTP2_FLAG_ACK == top->frame.hd.flags); + CU_ASSERT(memcmp(opaque_data, top->frame.ping.opaque_data, 8) == 0); + + nghttp2_frame_ping_free(&frame.ping); + nghttp2_session_del(session); +} + +void test_nghttp2_session_on_goaway_received(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + my_user_data user_data; + nghttp2_frame frame; + int i; + nghttp2_mem *mem; + + mem = nghttp2_mem_default(); + user_data.frame_recv_cb_called = 0; + user_data.invalid_frame_recv_cb_called = 0; + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.on_frame_recv_callback = on_frame_recv_callback; + callbacks.on_invalid_frame_recv_callback = on_invalid_frame_recv_callback; + callbacks.on_stream_close_callback = on_stream_close_callback; + + nghttp2_session_client_new(&session, &callbacks, &user_data); + + for (i = 1; i <= 7; ++i) { + open_stream(session, i); + } + + nghttp2_frame_goaway_init(&frame.goaway, 3, NGHTTP2_PROTOCOL_ERROR, NULL, 0); + + user_data.stream_close_cb_called = 0; + + CU_ASSERT(0 == nghttp2_session_on_goaway_received(session, &frame)); + + CU_ASSERT(1 == user_data.frame_recv_cb_called); + CU_ASSERT(3 == session->remote_last_stream_id); + /* on_stream_close should be callsed for 2 times (stream 5 and 7) */ + CU_ASSERT(2 == user_data.stream_close_cb_called); + + CU_ASSERT(NULL != nghttp2_session_get_stream(session, 1)); + CU_ASSERT(NULL != nghttp2_session_get_stream(session, 2)); + CU_ASSERT(NULL != nghttp2_session_get_stream(session, 3)); + CU_ASSERT(NULL != nghttp2_session_get_stream(session, 4)); + CU_ASSERT(NULL == nghttp2_session_get_stream(session, 5)); + CU_ASSERT(NULL != nghttp2_session_get_stream(session, 6)); + CU_ASSERT(NULL == nghttp2_session_get_stream(session, 7)); + + nghttp2_frame_goaway_free(&frame.goaway, mem); + nghttp2_session_del(session); +} + +void test_nghttp2_session_on_window_update_received(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + my_user_data user_data; + nghttp2_frame frame; + nghttp2_stream *stream; + nghttp2_outbound_item *data_item; + nghttp2_mem *mem; + + mem = nghttp2_mem_default(); + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.on_frame_recv_callback = on_frame_recv_callback; + callbacks.on_invalid_frame_recv_callback = on_invalid_frame_recv_callback; + user_data.frame_recv_cb_called = 0; + user_data.invalid_frame_recv_cb_called = 0; + + nghttp2_session_client_new(&session, &callbacks, &user_data); + + stream = nghttp2_session_open_stream(session, 1, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, NGHTTP2_STREAM_OPENED, + NULL); + + data_item = create_data_ob_item(mem); + + CU_ASSERT(0 == nghttp2_stream_attach_item(stream, data_item, session)); + + nghttp2_frame_window_update_init(&frame.window_update, NGHTTP2_FLAG_NONE, 1, + 16 * 1024); + + CU_ASSERT(0 == nghttp2_session_on_window_update_received(session, &frame)); + CU_ASSERT(1 == user_data.frame_recv_cb_called); + CU_ASSERT(NGHTTP2_INITIAL_WINDOW_SIZE + 16 * 1024 == + stream->remote_window_size); + + CU_ASSERT(0 == + nghttp2_stream_defer_item( + stream, NGHTTP2_STREAM_FLAG_DEFERRED_FLOW_CONTROL, session)); + + CU_ASSERT(0 == nghttp2_session_on_window_update_received(session, &frame)); + CU_ASSERT(2 == user_data.frame_recv_cb_called); + CU_ASSERT(NGHTTP2_INITIAL_WINDOW_SIZE + 16 * 1024 * 2 == + stream->remote_window_size); + CU_ASSERT(0 == (stream->flags & NGHTTP2_STREAM_FLAG_DEFERRED_ALL)); + + nghttp2_frame_window_update_free(&frame.window_update); + + /* Receiving WINDOW_UPDATE on reserved (remote) stream is a + connection error */ + nghttp2_session_open_stream(session, 2, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, NGHTTP2_STREAM_RESERVED, NULL); + + nghttp2_frame_window_update_init(&frame.window_update, NGHTTP2_FLAG_NONE, 2, + 4096); + + CU_ASSERT(!(session->goaway_flags & NGHTTP2_GOAWAY_TERM_ON_SEND)); + CU_ASSERT(0 == nghttp2_session_on_window_update_received(session, &frame)); + CU_ASSERT(session->goaway_flags & NGHTTP2_GOAWAY_TERM_ON_SEND); + + nghttp2_frame_window_update_free(&frame.window_update); + + nghttp2_session_del(session); + + /* Receiving WINDOW_UPDATE on reserved (local) stream is allowed */ + nghttp2_session_server_new(&session, &callbacks, &user_data); + + stream = nghttp2_session_open_stream(session, 2, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, + NGHTTP2_STREAM_RESERVED, NULL); + + nghttp2_frame_window_update_init(&frame.window_update, NGHTTP2_FLAG_NONE, 2, + 4096); + + CU_ASSERT(0 == nghttp2_session_on_window_update_received(session, &frame)); + CU_ASSERT(!(session->goaway_flags & NGHTTP2_GOAWAY_TERM_ON_SEND)); + + CU_ASSERT(NGHTTP2_INITIAL_WINDOW_SIZE + 4096 == stream->remote_window_size); + + nghttp2_frame_window_update_free(&frame.window_update); + + nghttp2_session_del(session); +} + +void test_nghttp2_session_on_data_received(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + my_user_data user_data; + nghttp2_outbound_item *top; + nghttp2_stream *stream; + nghttp2_frame frame; + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + + nghttp2_session_client_new(&session, &callbacks, &user_data); + stream = nghttp2_session_open_stream(session, 2, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, + NGHTTP2_STREAM_OPENING, NULL); + + nghttp2_frame_hd_init(&frame.hd, 4096, NGHTTP2_DATA, NGHTTP2_FLAG_NONE, 2); + + CU_ASSERT(0 == nghttp2_session_on_data_received(session, &frame)); + CU_ASSERT(0 == stream->shut_flags); + + frame.hd.flags = NGHTTP2_FLAG_END_STREAM; + + CU_ASSERT(0 == nghttp2_session_on_data_received(session, &frame)); + CU_ASSERT(NGHTTP2_SHUT_RD == stream->shut_flags); + + /* If NGHTTP2_STREAM_CLOSING state, DATA frame is discarded. */ + nghttp2_session_open_stream(session, 4, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, NGHTTP2_STREAM_CLOSING, NULL); + + frame.hd.flags = NGHTTP2_FLAG_NONE; + frame.hd.stream_id = 4; + + CU_ASSERT(0 == nghttp2_session_on_data_received(session, &frame)); + CU_ASSERT(NULL == nghttp2_outbound_queue_top(&session->ob_reg)); + + /* Check INVALID_STREAM case: DATA frame with stream ID which does + not exist. */ + + frame.hd.stream_id = 6; + + CU_ASSERT(0 == nghttp2_session_on_data_received(session, &frame)); + top = nghttp2_outbound_queue_top(&session->ob_reg); + /* DATA against nonexistent stream is just ignored for now */ + CU_ASSERT(top == NULL); + /* CU_ASSERT(NGHTTP2_RST_STREAM == top->frame.hd.type); */ + /* CU_ASSERT(NGHTTP2_PROTOCOL_ERROR == top->frame.rst_stream.error_code); */ + + nghttp2_session_del(session); +} + +void test_nghttp2_session_send_headers_start_stream(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + nghttp2_outbound_item *item; + nghttp2_frame *frame; + nghttp2_stream *stream; + nghttp2_mem *mem; + + mem = nghttp2_mem_default(); + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.send_callback = null_send_callback; + + nghttp2_session_client_new(&session, &callbacks, NULL); + + item = mem->malloc(sizeof(nghttp2_outbound_item), NULL); + + nghttp2_outbound_item_init(item); + + frame = &item->frame; + + nghttp2_frame_headers_init(&frame->headers, NGHTTP2_FLAG_END_HEADERS, + session->next_stream_id, NGHTTP2_HCAT_REQUEST, + NULL, NULL, 0); + session->next_stream_id += 2; + + nghttp2_session_add_item(session, item); + CU_ASSERT(0 == nghttp2_session_send(session)); + stream = nghttp2_session_get_stream(session, 1); + CU_ASSERT(NGHTTP2_STREAM_OPENING == stream->state); + + nghttp2_session_del(session); +} + +void test_nghttp2_session_send_headers_reply(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + nghttp2_outbound_item *item; + nghttp2_frame *frame; + nghttp2_stream *stream; + nghttp2_mem *mem; + + mem = nghttp2_mem_default(); + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.send_callback = null_send_callback; + + CU_ASSERT(0 == nghttp2_session_client_new(&session, &callbacks, NULL)); + nghttp2_session_open_stream(session, 2, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, NGHTTP2_STREAM_OPENING, NULL); + + item = mem->malloc(sizeof(nghttp2_outbound_item), NULL); + + nghttp2_outbound_item_init(item); + + frame = &item->frame; + + nghttp2_frame_headers_init(&frame->headers, NGHTTP2_FLAG_END_HEADERS, 2, + NGHTTP2_HCAT_HEADERS, NULL, NULL, 0); + nghttp2_session_add_item(session, item); + CU_ASSERT(0 == nghttp2_session_send(session)); + stream = nghttp2_session_get_stream(session, 2); + CU_ASSERT(NGHTTP2_STREAM_OPENED == stream->state); + + nghttp2_session_del(session); +} + +void test_nghttp2_session_send_headers_frame_size_error(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + nghttp2_outbound_item *item; + nghttp2_frame *frame; + nghttp2_nv *nva; + ssize_t nvlen; + size_t vallen = NGHTTP2_HD_MAX_NV; + nghttp2_nv nv[28]; + size_t nnv = ARRLEN(nv); + size_t i; + my_user_data ud; + nghttp2_mem *mem; + + mem = nghttp2_mem_default(); + + for (i = 0; i < nnv; ++i) { + nv[i].name = (uint8_t *)"header"; + nv[i].namelen = strlen((const char *)nv[i].name); + nv[i].value = mem->malloc(vallen + 1, NULL); + memset(nv[i].value, '0' + (int)i, vallen); + nv[i].value[vallen] = '\0'; + nv[i].valuelen = vallen; + nv[i].flags = NGHTTP2_NV_FLAG_NONE; + } + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.send_callback = null_send_callback; + callbacks.on_frame_not_send_callback = on_frame_not_send_callback; + + nghttp2_session_client_new(&session, &callbacks, &ud); + nvlen = nnv; + nghttp2_nv_array_copy(&nva, nv, nvlen, mem); + + item = mem->malloc(sizeof(nghttp2_outbound_item), NULL); + + nghttp2_outbound_item_init(item); + + frame = &item->frame; + + nghttp2_frame_headers_init(&frame->headers, NGHTTP2_FLAG_END_HEADERS, + session->next_stream_id, NGHTTP2_HCAT_REQUEST, + NULL, nva, nvlen); + + session->next_stream_id += 2; + + nghttp2_session_add_item(session, item); + + ud.frame_not_send_cb_called = 0; + + CU_ASSERT(0 == nghttp2_session_send(session)); + + CU_ASSERT(1 == ud.frame_not_send_cb_called); + CU_ASSERT(NGHTTP2_HEADERS == ud.not_sent_frame_type); + CU_ASSERT(NGHTTP2_ERR_FRAME_SIZE_ERROR == ud.not_sent_error); + + for (i = 0; i < nnv; ++i) { + mem->free(nv[i].value, NULL); + } + nghttp2_session_del(session); +} + +void test_nghttp2_session_send_headers_push_reply(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + nghttp2_outbound_item *item; + nghttp2_frame *frame; + nghttp2_stream *stream; + nghttp2_mem *mem; + + mem = nghttp2_mem_default(); + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.send_callback = null_send_callback; + + CU_ASSERT(0 == nghttp2_session_server_new(&session, &callbacks, NULL)); + nghttp2_session_open_stream(session, 2, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, NGHTTP2_STREAM_RESERVED, NULL); + + item = mem->malloc(sizeof(nghttp2_outbound_item), NULL); + + nghttp2_outbound_item_init(item); + + frame = &item->frame; + + nghttp2_frame_headers_init(&frame->headers, NGHTTP2_FLAG_END_HEADERS, 2, + NGHTTP2_HCAT_HEADERS, NULL, NULL, 0); + nghttp2_session_add_item(session, item); + CU_ASSERT(0 == session->num_outgoing_streams); + CU_ASSERT(0 == nghttp2_session_send(session)); + CU_ASSERT(1 == session->num_outgoing_streams); + stream = nghttp2_session_get_stream(session, 2); + CU_ASSERT(NGHTTP2_STREAM_OPENED == stream->state); + CU_ASSERT(0 == (stream->flags & NGHTTP2_STREAM_FLAG_PUSH)); + nghttp2_session_del(session); +} + +void test_nghttp2_session_send_rst_stream(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + my_user_data user_data; + nghttp2_outbound_item *item; + nghttp2_frame *frame; + nghttp2_mem *mem; + + mem = nghttp2_mem_default(); + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.send_callback = null_send_callback; + nghttp2_session_client_new(&session, &callbacks, &user_data); + nghttp2_session_open_stream(session, 1, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, NGHTTP2_STREAM_OPENING, NULL); + + item = mem->malloc(sizeof(nghttp2_outbound_item), NULL); + + nghttp2_outbound_item_init(item); + + frame = &item->frame; + + nghttp2_frame_rst_stream_init(&frame->rst_stream, 1, NGHTTP2_PROTOCOL_ERROR); + nghttp2_session_add_item(session, item); + CU_ASSERT(0 == nghttp2_session_send(session)); + + CU_ASSERT(NULL == nghttp2_session_get_stream(session, 1)); + + nghttp2_session_del(session); +} + +void test_nghttp2_session_send_push_promise(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + nghttp2_outbound_item *item; + nghttp2_frame *frame; + nghttp2_stream *stream; + nghttp2_settings_entry iv; + my_user_data ud; + nghttp2_mem *mem; + + mem = nghttp2_mem_default(); + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.send_callback = null_send_callback; + callbacks.on_frame_not_send_callback = on_frame_not_send_callback; + + nghttp2_session_server_new(&session, &callbacks, &ud); + nghttp2_session_open_stream(session, 1, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, NGHTTP2_STREAM_OPENING, NULL); + + item = mem->malloc(sizeof(nghttp2_outbound_item), NULL); + + nghttp2_outbound_item_init(item); + + frame = &item->frame; + + nghttp2_frame_push_promise_init(&frame->push_promise, + NGHTTP2_FLAG_END_HEADERS, 1, + session->next_stream_id, NULL, 0); + + session->next_stream_id += 2; + + nghttp2_session_add_item(session, item); + + CU_ASSERT(0 == nghttp2_session_send(session)); + stream = nghttp2_session_get_stream(session, 2); + CU_ASSERT(NGHTTP2_STREAM_RESERVED == stream->state); + + /* Received ENABLE_PUSH = 0 */ + iv.settings_id = NGHTTP2_SETTINGS_ENABLE_PUSH; + iv.value = 0; + frame = mem->malloc(sizeof(nghttp2_frame), NULL); + nghttp2_frame_settings_init(&frame->settings, NGHTTP2_FLAG_NONE, + dup_iv(&iv, 1), 1); + nghttp2_session_on_settings_received(session, frame, 1); + nghttp2_frame_settings_free(&frame->settings, mem); + mem->free(frame, NULL); + + item = mem->malloc(sizeof(nghttp2_outbound_item), NULL); + + nghttp2_outbound_item_init(item); + + frame = &item->frame; + + nghttp2_frame_push_promise_init(&frame->push_promise, + NGHTTP2_FLAG_END_HEADERS, 1, -1, NULL, 0); + nghttp2_session_add_item(session, item); + + ud.frame_not_send_cb_called = 0; + CU_ASSERT(0 == nghttp2_session_send(session)); + + CU_ASSERT(1 == ud.frame_not_send_cb_called); + CU_ASSERT(NGHTTP2_PUSH_PROMISE == ud.not_sent_frame_type); + CU_ASSERT(NGHTTP2_ERR_PUSH_DISABLED == ud.not_sent_error); + + nghttp2_session_del(session); + + /* PUSH_PROMISE from client is error */ + nghttp2_session_client_new(&session, &callbacks, &ud); + nghttp2_session_open_stream(session, 1, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, NGHTTP2_STREAM_OPENING, NULL); + item = mem->malloc(sizeof(nghttp2_outbound_item), NULL); + + nghttp2_outbound_item_init(item); + + frame = &item->frame; + + nghttp2_frame_push_promise_init(&frame->push_promise, + NGHTTP2_FLAG_END_HEADERS, 1, -1, NULL, 0); + nghttp2_session_add_item(session, item); + + CU_ASSERT(0 == nghttp2_session_send(session)); + CU_ASSERT(NULL == nghttp2_session_get_stream(session, 3)); + + nghttp2_session_del(session); +} + +void test_nghttp2_session_is_my_stream_id(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + nghttp2_session_server_new(&session, &callbacks, NULL); + + CU_ASSERT(0 == nghttp2_session_is_my_stream_id(session, 0)); + CU_ASSERT(0 == nghttp2_session_is_my_stream_id(session, 1)); + CU_ASSERT(1 == nghttp2_session_is_my_stream_id(session, 2)); + + nghttp2_session_del(session); + + nghttp2_session_client_new(&session, &callbacks, NULL); + + CU_ASSERT(0 == nghttp2_session_is_my_stream_id(session, 0)); + CU_ASSERT(1 == nghttp2_session_is_my_stream_id(session, 1)); + CU_ASSERT(0 == nghttp2_session_is_my_stream_id(session, 2)); + + nghttp2_session_del(session); +} + +void test_nghttp2_session_upgrade(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + uint8_t settings_payload[128]; + size_t settings_payloadlen; + nghttp2_settings_entry iv[16]; + nghttp2_stream *stream; + nghttp2_outbound_item *item; + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.send_callback = null_send_callback; + iv[0].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS; + iv[0].value = 1; + iv[1].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE; + iv[1].value = 4095; + settings_payloadlen = nghttp2_pack_settings_payload( + settings_payload, sizeof(settings_payload), iv, 2); + + /* Check client side */ + nghttp2_session_client_new(&session, &callbacks, NULL); + CU_ASSERT(0 == nghttp2_session_upgrade(session, settings_payload, + settings_payloadlen, &callbacks)); + stream = nghttp2_session_get_stream(session, 1); + CU_ASSERT(stream != NULL); + CU_ASSERT(&callbacks == stream->stream_user_data); + CU_ASSERT(NGHTTP2_SHUT_WR == stream->shut_flags); + item = nghttp2_session_get_next_ob_item(session); + CU_ASSERT(NGHTTP2_SETTINGS == item->frame.hd.type); + CU_ASSERT(2 == item->frame.settings.niv); + CU_ASSERT(NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS == + item->frame.settings.iv[0].settings_id); + CU_ASSERT(1 == item->frame.settings.iv[0].value); + CU_ASSERT(NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE == + item->frame.settings.iv[1].settings_id); + CU_ASSERT(4095 == item->frame.settings.iv[1].value); + + /* Call nghttp2_session_upgrade() again is error */ + CU_ASSERT(NGHTTP2_ERR_PROTO == + nghttp2_session_upgrade(session, settings_payload, + settings_payloadlen, &callbacks)); + nghttp2_session_del(session); + + /* Check server side */ + nghttp2_session_server_new(&session, &callbacks, NULL); + CU_ASSERT(0 == nghttp2_session_upgrade(session, settings_payload, + settings_payloadlen, &callbacks)); + stream = nghttp2_session_get_stream(session, 1); + CU_ASSERT(stream != NULL); + CU_ASSERT(NULL == stream->stream_user_data); + CU_ASSERT(NGHTTP2_SHUT_RD == stream->shut_flags); + CU_ASSERT(NULL == nghttp2_session_get_next_ob_item(session)); + CU_ASSERT(1 == session->remote_settings.max_concurrent_streams); + CU_ASSERT(4095 == session->remote_settings.initial_window_size); + /* Call nghttp2_session_upgrade() again is error */ + CU_ASSERT(NGHTTP2_ERR_PROTO == + nghttp2_session_upgrade(session, settings_payload, + settings_payloadlen, &callbacks)); + nghttp2_session_del(session); + + /* Empty SETTINGS is OK */ + settings_payloadlen = nghttp2_pack_settings_payload( + settings_payload, sizeof(settings_payload), NULL, 0); + + nghttp2_session_client_new(&session, &callbacks, NULL); + CU_ASSERT(0 == nghttp2_session_upgrade(session, settings_payload, + settings_payloadlen, NULL)); + nghttp2_session_del(session); +} + +void test_nghttp2_session_reprioritize_stream(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + my_user_data ud; + nghttp2_stream *stream; + nghttp2_stream *dep_stream; + nghttp2_priority_spec pri_spec; + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.send_callback = block_count_send_callback; + + nghttp2_session_server_new(&session, &callbacks, &ud); + + stream = nghttp2_session_open_stream(session, 1, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, + NGHTTP2_STREAM_OPENING, NULL); + + nghttp2_priority_spec_init(&pri_spec, 0, 10, 0); + + nghttp2_session_reprioritize_stream(session, stream, &pri_spec); + + CU_ASSERT(10 == stream->weight); + CU_ASSERT(NULL == stream->dep_prev); + + /* If depenency to idle stream which is not in depdenency tree yet */ + + nghttp2_priority_spec_init(&pri_spec, 3, 99, 0); + + nghttp2_session_reprioritize_stream(session, stream, &pri_spec); + + CU_ASSERT(99 == stream->weight); + CU_ASSERT(3 == stream->dep_prev->stream_id); + + dep_stream = nghttp2_session_get_stream_raw(session, 3); + + CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT == dep_stream->weight); + + dep_stream = open_stream(session, 3); + + /* Change weight */ + pri_spec.weight = 128; + + nghttp2_session_reprioritize_stream(session, stream, &pri_spec); + + CU_ASSERT(128 == stream->weight); + CU_ASSERT(dep_stream == stream->dep_prev); + + /* Test circular dependency; stream 1 is first removed and becomes + root. Then stream 3 depends on it. */ + nghttp2_priority_spec_init(&pri_spec, 1, 1, 0); + + nghttp2_session_reprioritize_stream(session, dep_stream, &pri_spec); + + CU_ASSERT(1 == dep_stream->weight); + CU_ASSERT(stream == dep_stream->dep_prev); + + /* Making priority to closed stream will result in default + priority */ + session->last_recv_stream_id = 9; + + nghttp2_priority_spec_init(&pri_spec, 5, 5, 0); + + nghttp2_session_reprioritize_stream(session, stream, &pri_spec); + + CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT == stream->weight); + + nghttp2_session_del(session); +} + +void test_nghttp2_session_reprioritize_stream_with_idle_stream_dep(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + nghttp2_stream *stream; + nghttp2_priority_spec pri_spec; + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.send_callback = block_count_send_callback; + + nghttp2_session_server_new(&session, &callbacks, NULL); + + stream = nghttp2_session_open_stream(session, 1, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, + NGHTTP2_STREAM_OPENING, NULL); + + session->pending_local_max_concurrent_stream = 1; + + nghttp2_priority_spec_init(&pri_spec, 101, 10, 0); + + nghttp2_session_reprioritize_stream(session, stream, &pri_spec); + + /* idle stream is not counteed to max concurrent streams */ + + CU_ASSERT(10 == stream->weight); + CU_ASSERT(101 == stream->dep_prev->stream_id); + + stream = nghttp2_session_get_stream_raw(session, 101); + + CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT == stream->weight); + + nghttp2_session_del(session); +} + +void test_nghttp2_submit_data(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + nghttp2_data_provider data_prd; + my_user_data ud; + nghttp2_frame *frame; + nghttp2_frame_hd hd; + nghttp2_active_outbound_item *aob; + nghttp2_bufs *framebufs; + nghttp2_buf *buf; + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.send_callback = block_count_send_callback; + + data_prd.read_callback = fixed_length_data_source_read_callback; + ud.data_source_length = NGHTTP2_DATA_PAYLOADLEN * 2; + CU_ASSERT(0 == nghttp2_session_client_new(&session, &callbacks, &ud)); + aob = &session->aob; + framebufs = &aob->framebufs; + + nghttp2_session_open_stream(session, 1, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, NGHTTP2_STREAM_OPENING, NULL); + CU_ASSERT( + 0 == nghttp2_submit_data(session, NGHTTP2_FLAG_END_STREAM, 1, &data_prd)); + + ud.block_count = 0; + CU_ASSERT(0 == nghttp2_session_send(session)); + frame = &aob->item->frame; + + buf = &framebufs->head->buf; + nghttp2_frame_unpack_frame_hd(&hd, buf->pos); + + CU_ASSERT(NGHTTP2_FLAG_NONE == hd.flags); + CU_ASSERT(NGHTTP2_FLAG_NONE == frame->hd.flags); + /* aux_data.data.flags has these flags */ + CU_ASSERT(NGHTTP2_FLAG_END_STREAM == aob->item->aux_data.data.flags); + + nghttp2_session_del(session); +} + +void test_nghttp2_submit_data_read_length_too_large(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + nghttp2_data_provider data_prd; + my_user_data ud; + nghttp2_frame *frame; + nghttp2_frame_hd hd; + nghttp2_active_outbound_item *aob; + nghttp2_bufs *framebufs; + nghttp2_buf *buf; + size_t payloadlen; + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.send_callback = block_count_send_callback; + callbacks.read_length_callback = too_large_data_source_length_callback; + + data_prd.read_callback = fixed_length_data_source_read_callback; + ud.data_source_length = NGHTTP2_DATA_PAYLOADLEN * 2; + CU_ASSERT(0 == nghttp2_session_client_new(&session, &callbacks, &ud)); + aob = &session->aob; + framebufs = &aob->framebufs; + + nghttp2_session_open_stream(session, 1, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, NGHTTP2_STREAM_OPENING, NULL); + CU_ASSERT( + 0 == nghttp2_submit_data(session, NGHTTP2_FLAG_END_STREAM, 1, &data_prd)); + + ud.block_count = 0; + CU_ASSERT(0 == nghttp2_session_send(session)); + frame = &aob->item->frame; + + buf = &framebufs->head->buf; + nghttp2_frame_unpack_frame_hd(&hd, buf->pos); + + CU_ASSERT(NGHTTP2_FLAG_NONE == hd.flags); + CU_ASSERT(NGHTTP2_FLAG_NONE == frame->hd.flags); + CU_ASSERT(16384 == hd.length) + /* aux_data.data.flags has these flags */ + CU_ASSERT(NGHTTP2_FLAG_END_STREAM == aob->item->aux_data.data.flags); + + nghttp2_session_del(session); + + /* Check that buffers are expanded */ + CU_ASSERT(0 == nghttp2_session_client_new(&session, &callbacks, &ud)); + + ud.data_source_length = NGHTTP2_MAX_FRAME_SIZE_MAX; + + session->remote_settings.max_frame_size = NGHTTP2_MAX_FRAME_SIZE_MAX; + + nghttp2_session_open_stream(session, 1, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, NGHTTP2_STREAM_OPENING, NULL); + CU_ASSERT( + 0 == nghttp2_submit_data(session, NGHTTP2_FLAG_END_STREAM, 1, &data_prd)); + + ud.block_count = 0; + CU_ASSERT(0 == nghttp2_session_send(session)); + + aob = &session->aob; + + frame = &aob->item->frame; + + framebufs = &aob->framebufs; + + buf = &framebufs->head->buf; + nghttp2_frame_unpack_frame_hd(&hd, buf->pos); + + payloadlen = nghttp2_min(NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE, + NGHTTP2_INITIAL_WINDOW_SIZE); + + CU_ASSERT(NGHTTP2_FRAME_HDLEN + 1 + payloadlen == + (size_t)nghttp2_buf_cap(buf)); + CU_ASSERT(NGHTTP2_FLAG_NONE == hd.flags); + CU_ASSERT(NGHTTP2_FLAG_NONE == frame->hd.flags); + CU_ASSERT(payloadlen == hd.length); + /* aux_data.data.flags has these flags */ + CU_ASSERT(NGHTTP2_FLAG_END_STREAM == aob->item->aux_data.data.flags); + + nghttp2_session_del(session); +} + +void test_nghttp2_submit_data_read_length_smallest(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + nghttp2_data_provider data_prd; + my_user_data ud; + nghttp2_frame *frame; + nghttp2_frame_hd hd; + nghttp2_active_outbound_item *aob; + nghttp2_bufs *framebufs; + nghttp2_buf *buf; + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.send_callback = block_count_send_callback; + callbacks.read_length_callback = smallest_length_data_source_length_callback; + + data_prd.read_callback = fixed_length_data_source_read_callback; + ud.data_source_length = NGHTTP2_DATA_PAYLOADLEN * 2; + CU_ASSERT(0 == nghttp2_session_client_new(&session, &callbacks, &ud)); + aob = &session->aob; + framebufs = &aob->framebufs; + + nghttp2_session_open_stream(session, 1, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, NGHTTP2_STREAM_OPENING, NULL); + CU_ASSERT( + 0 == nghttp2_submit_data(session, NGHTTP2_FLAG_END_STREAM, 1, &data_prd)); + + ud.block_count = 0; + CU_ASSERT(0 == nghttp2_session_send(session)); + frame = &aob->item->frame; + + buf = &framebufs->head->buf; + nghttp2_frame_unpack_frame_hd(&hd, buf->pos); + + CU_ASSERT(NGHTTP2_FLAG_NONE == hd.flags); + CU_ASSERT(NGHTTP2_FLAG_NONE == frame->hd.flags); + CU_ASSERT(1 == hd.length) + /* aux_data.data.flags has these flags */ + CU_ASSERT(NGHTTP2_FLAG_END_STREAM == aob->item->aux_data.data.flags); + + nghttp2_session_del(session); +} + +static ssize_t submit_data_twice_data_source_read_callback( + nghttp2_session *session _U_, int32_t stream_id _U_, uint8_t *buf _U_, + size_t len, uint32_t *data_flags, nghttp2_data_source *source _U_, + void *user_data _U_) { + *data_flags |= NGHTTP2_DATA_FLAG_EOF; + return nghttp2_min(len, 16); +} + +static int submit_data_twice_on_frame_send_callback(nghttp2_session *session, + const nghttp2_frame *frame, + void *user_data _U_) { + static int called = 0; + int rv; + nghttp2_data_provider data_prd; + + if (called == 0) { + called = 1; + + data_prd.read_callback = submit_data_twice_data_source_read_callback; + + rv = nghttp2_submit_data(session, NGHTTP2_FLAG_END_STREAM, + frame->hd.stream_id, &data_prd); + CU_ASSERT(0 == rv); + } + + return 0; +} + +void test_nghttp2_submit_data_twice(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + nghttp2_data_provider data_prd; + my_user_data ud; + accumulator acc; + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.send_callback = accumulator_send_callback; + callbacks.on_frame_send_callback = submit_data_twice_on_frame_send_callback; + + data_prd.read_callback = submit_data_twice_data_source_read_callback; + + acc.length = 0; + ud.acc = &acc; + + CU_ASSERT(0 == nghttp2_session_client_new(&session, &callbacks, &ud)); + + nghttp2_session_open_stream(session, 1, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, NGHTTP2_STREAM_OPENING, NULL); + + CU_ASSERT(0 == nghttp2_submit_data(session, NGHTTP2_FLAG_NONE, 1, &data_prd)); + + CU_ASSERT(0 == nghttp2_session_send(session)); + + /* We should have sent 2 DATA frame with 16 bytes payload each */ + CU_ASSERT(NGHTTP2_FRAME_HDLEN * 2 + 16 * 2 == acc.length); + + nghttp2_session_del(session); +} + +void test_nghttp2_submit_request_with_data(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + nghttp2_data_provider data_prd; + my_user_data ud; + nghttp2_outbound_item *item; + nghttp2_mem *mem; + + mem = nghttp2_mem_default(); + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.send_callback = null_send_callback; + + data_prd.read_callback = fixed_length_data_source_read_callback; + ud.data_source_length = 64 * 1024 - 1; + CU_ASSERT(0 == nghttp2_session_client_new(&session, &callbacks, &ud)); + CU_ASSERT(1 == nghttp2_submit_request(session, NULL, reqnv, ARRLEN(reqnv), + &data_prd, NULL)); + item = nghttp2_session_get_next_ob_item(session); + CU_ASSERT(ARRLEN(reqnv) == item->frame.headers.nvlen); + assert_nv_equal(reqnv, item->frame.headers.nva, item->frame.headers.nvlen, + mem); + CU_ASSERT(0 == nghttp2_session_send(session)); + CU_ASSERT(0 == ud.data_source_length); + + nghttp2_session_del(session); +} + +void test_nghttp2_submit_request_without_data(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + accumulator acc; + nghttp2_data_provider data_prd = {{-1}, NULL}; + nghttp2_outbound_item *item; + my_user_data ud; + nghttp2_frame frame; + nghttp2_hd_inflater inflater; + nva_out out; + nghttp2_bufs bufs; + nghttp2_mem *mem; + + mem = nghttp2_mem_default(); + frame_pack_bufs_init(&bufs); + + nva_out_init(&out); + acc.length = 0; + ud.acc = &acc; + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.send_callback = accumulator_send_callback; + CU_ASSERT(0 == nghttp2_session_client_new(&session, &callbacks, &ud)); + + nghttp2_hd_inflate_init(&inflater, mem); + CU_ASSERT(1 == nghttp2_submit_request(session, NULL, reqnv, ARRLEN(reqnv), + &data_prd, NULL)); + item = nghttp2_session_get_next_ob_item(session); + CU_ASSERT(ARRLEN(reqnv) == item->frame.headers.nvlen); + assert_nv_equal(reqnv, item->frame.headers.nva, item->frame.headers.nvlen, + mem); + CU_ASSERT(item->frame.hd.flags & NGHTTP2_FLAG_END_STREAM); + + CU_ASSERT(0 == nghttp2_session_send(session)); + CU_ASSERT(0 == unpack_frame(&frame, acc.buf, acc.length)); + + nghttp2_bufs_add(&bufs, acc.buf, acc.length); + inflate_hd(&inflater, &out, &bufs, NGHTTP2_FRAME_HDLEN, mem); + + CU_ASSERT(ARRLEN(reqnv) == out.nvlen); + assert_nv_equal(reqnv, out.nva, out.nvlen, mem); + nghttp2_frame_headers_free(&frame.headers, mem); + nva_out_reset(&out, mem); + + nghttp2_bufs_free(&bufs); + nghttp2_hd_inflate_free(&inflater); + nghttp2_session_del(session); +} + +void test_nghttp2_submit_response_with_data(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + nghttp2_data_provider data_prd; + my_user_data ud; + nghttp2_outbound_item *item; + nghttp2_mem *mem; + + mem = nghttp2_mem_default(); + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.send_callback = null_send_callback; + + data_prd.read_callback = fixed_length_data_source_read_callback; + ud.data_source_length = 64 * 1024 - 1; + CU_ASSERT(0 == nghttp2_session_server_new(&session, &callbacks, &ud)); + nghttp2_session_open_stream(session, 1, NGHTTP2_FLAG_END_STREAM, + &pri_spec_default, NGHTTP2_STREAM_OPENING, NULL); + CU_ASSERT(0 == nghttp2_submit_response(session, 1, resnv, ARRLEN(resnv), + &data_prd)); + item = nghttp2_session_get_next_ob_item(session); + CU_ASSERT(ARRLEN(resnv) == item->frame.headers.nvlen); + assert_nv_equal(resnv, item->frame.headers.nva, item->frame.headers.nvlen, + mem); + CU_ASSERT(0 == nghttp2_session_send(session)); + CU_ASSERT(0 == ud.data_source_length); + + nghttp2_session_del(session); +} + +void test_nghttp2_submit_response_without_data(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + accumulator acc; + nghttp2_data_provider data_prd = {{-1}, NULL}; + nghttp2_outbound_item *item; + my_user_data ud; + nghttp2_frame frame; + nghttp2_hd_inflater inflater; + nva_out out; + nghttp2_bufs bufs; + nghttp2_mem *mem; + + mem = nghttp2_mem_default(); + frame_pack_bufs_init(&bufs); + + nva_out_init(&out); + acc.length = 0; + ud.acc = &acc; + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.send_callback = accumulator_send_callback; + CU_ASSERT(0 == nghttp2_session_server_new(&session, &callbacks, &ud)); + + nghttp2_hd_inflate_init(&inflater, mem); + nghttp2_session_open_stream(session, 1, NGHTTP2_FLAG_END_STREAM, + &pri_spec_default, NGHTTP2_STREAM_OPENING, NULL); + CU_ASSERT(0 == nghttp2_submit_response(session, 1, resnv, ARRLEN(resnv), + &data_prd)); + item = nghttp2_session_get_next_ob_item(session); + CU_ASSERT(ARRLEN(resnv) == item->frame.headers.nvlen); + assert_nv_equal(resnv, item->frame.headers.nva, item->frame.headers.nvlen, + mem); + CU_ASSERT(item->frame.hd.flags & NGHTTP2_FLAG_END_STREAM); + + CU_ASSERT(0 == nghttp2_session_send(session)); + CU_ASSERT(0 == unpack_frame(&frame, acc.buf, acc.length)); + + nghttp2_bufs_add(&bufs, acc.buf, acc.length); + inflate_hd(&inflater, &out, &bufs, NGHTTP2_FRAME_HDLEN, mem); + + CU_ASSERT(ARRLEN(resnv) == out.nvlen); + assert_nv_equal(resnv, out.nva, out.nvlen, mem); + + nva_out_reset(&out, mem); + nghttp2_bufs_free(&bufs); + nghttp2_frame_headers_free(&frame.headers, mem); + nghttp2_hd_inflate_free(&inflater); + nghttp2_session_del(session); +} + +void test_nghttp2_submit_trailer(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + accumulator acc; + nghttp2_data_provider data_prd; + nghttp2_outbound_item *item; + my_user_data ud; + nghttp2_frame frame; + nghttp2_hd_inflater inflater; + nva_out out; + nghttp2_bufs bufs; + nghttp2_mem *mem; + + mem = nghttp2_mem_default(); + frame_pack_bufs_init(&bufs); + + data_prd.read_callback = no_end_stream_data_source_read_callback; + nva_out_init(&out); + acc.length = 0; + ud.acc = &acc; + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.send_callback = null_send_callback; + CU_ASSERT(0 == nghttp2_session_server_new(&session, &callbacks, &ud)); + + nghttp2_hd_inflate_init(&inflater, mem); + nghttp2_session_open_stream(session, 1, NGHTTP2_FLAG_END_STREAM, + &pri_spec_default, NGHTTP2_STREAM_OPENING, NULL); + CU_ASSERT(0 == nghttp2_submit_response(session, 1, resnv, ARRLEN(resnv), + &data_prd)); + CU_ASSERT(0 == nghttp2_session_send(session)); + + CU_ASSERT(0 == + nghttp2_submit_trailer(session, 1, trailernv, ARRLEN(trailernv))); + + session->callbacks.send_callback = accumulator_send_callback; + + item = nghttp2_session_get_next_ob_item(session); + CU_ASSERT(NGHTTP2_HEADERS == item->frame.hd.type); + CU_ASSERT(NGHTTP2_HCAT_HEADERS == item->frame.headers.cat); + CU_ASSERT(item->frame.hd.flags & NGHTTP2_FLAG_END_STREAM); + + CU_ASSERT(0 == nghttp2_session_send(session)); + CU_ASSERT(0 == unpack_frame(&frame, acc.buf, acc.length)); + + nghttp2_bufs_add(&bufs, acc.buf, acc.length); + inflate_hd(&inflater, &out, &bufs, NGHTTP2_FRAME_HDLEN, mem); + + CU_ASSERT(ARRLEN(trailernv) == out.nvlen); + assert_nv_equal(trailernv, out.nva, out.nvlen, mem); + + nva_out_reset(&out, mem); + nghttp2_bufs_free(&bufs); + nghttp2_frame_headers_free(&frame.headers, mem); + nghttp2_hd_inflate_free(&inflater); + nghttp2_session_del(session); +} + +void test_nghttp2_submit_headers_start_stream(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + nghttp2_outbound_item *item; + nghttp2_mem *mem; + + mem = nghttp2_mem_default(); + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + CU_ASSERT(0 == nghttp2_session_client_new(&session, &callbacks, NULL)); + CU_ASSERT(1 == nghttp2_submit_headers(session, NGHTTP2_FLAG_END_STREAM, -1, + NULL, reqnv, ARRLEN(reqnv), NULL)); + item = nghttp2_session_get_next_ob_item(session); + CU_ASSERT(ARRLEN(reqnv) == item->frame.headers.nvlen); + assert_nv_equal(reqnv, item->frame.headers.nva, item->frame.headers.nvlen, + mem); + CU_ASSERT((NGHTTP2_FLAG_END_HEADERS | NGHTTP2_FLAG_END_STREAM) == + item->frame.hd.flags); + CU_ASSERT(0 == (item->frame.hd.flags & NGHTTP2_FLAG_PRIORITY)); + + nghttp2_session_del(session); +} + +void test_nghttp2_submit_headers_reply(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + my_user_data ud; + nghttp2_outbound_item *item; + nghttp2_stream *stream; + nghttp2_mem *mem; + + mem = nghttp2_mem_default(); + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.send_callback = null_send_callback; + callbacks.on_frame_send_callback = on_frame_send_callback; + + CU_ASSERT(0 == nghttp2_session_server_new(&session, &callbacks, &ud)); + CU_ASSERT(0 == nghttp2_submit_headers(session, NGHTTP2_FLAG_END_STREAM, 1, + NULL, resnv, ARRLEN(resnv), NULL)); + item = nghttp2_session_get_next_ob_item(session); + CU_ASSERT(ARRLEN(resnv) == item->frame.headers.nvlen); + assert_nv_equal(resnv, item->frame.headers.nva, item->frame.headers.nvlen, + mem); + CU_ASSERT((NGHTTP2_FLAG_END_STREAM | NGHTTP2_FLAG_END_HEADERS) == + item->frame.hd.flags); + + ud.frame_send_cb_called = 0; + ud.sent_frame_type = 0; + /* The transimission will be canceled because the stream 1 is not + open. */ + CU_ASSERT(0 == nghttp2_session_send(session)); + CU_ASSERT(0 == ud.frame_send_cb_called); + + stream = nghttp2_session_open_stream(session, 1, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, + NGHTTP2_STREAM_OPENING, NULL); + + CU_ASSERT(0 == nghttp2_submit_headers(session, NGHTTP2_FLAG_END_STREAM, 1, + NULL, resnv, ARRLEN(resnv), NULL)); + CU_ASSERT(0 == nghttp2_session_send(session)); + CU_ASSERT(1 == ud.frame_send_cb_called); + CU_ASSERT(NGHTTP2_HEADERS == ud.sent_frame_type); + CU_ASSERT(stream->shut_flags & NGHTTP2_SHUT_WR); + + nghttp2_session_del(session); +} + +void test_nghttp2_submit_headers_push_reply(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + my_user_data ud; + nghttp2_stream *stream; + int foo; + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.send_callback = null_send_callback; + callbacks.on_frame_send_callback = on_frame_send_callback; + + CU_ASSERT(0 == nghttp2_session_server_new(&session, &callbacks, &ud)); + stream = nghttp2_session_open_stream(session, 2, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, + NGHTTP2_STREAM_RESERVED, NULL); + CU_ASSERT(0 == nghttp2_submit_headers(session, NGHTTP2_FLAG_NONE, 2, NULL, + resnv, ARRLEN(resnv), &foo)); + + ud.frame_send_cb_called = 0; + ud.sent_frame_type = 0; + CU_ASSERT(0 == nghttp2_session_send(session)); + CU_ASSERT(1 == ud.frame_send_cb_called); + CU_ASSERT(NGHTTP2_HEADERS == ud.sent_frame_type); + CU_ASSERT(NGHTTP2_STREAM_OPENED == stream->state); + CU_ASSERT(&foo == stream->stream_user_data); + + nghttp2_session_del(session); + + /* Sending HEADERS from client against stream in reserved state is + error */ + CU_ASSERT(0 == nghttp2_session_client_new(&session, &callbacks, &ud)); + nghttp2_session_open_stream(session, 2, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, NGHTTP2_STREAM_RESERVED, NULL); + CU_ASSERT(0 == nghttp2_submit_headers(session, NGHTTP2_FLAG_NONE, 2, NULL, + reqnv, ARRLEN(reqnv), NULL)); + + ud.frame_send_cb_called = 0; + ud.sent_frame_type = 0; + CU_ASSERT(0 == nghttp2_session_send(session)); + CU_ASSERT(0 == ud.frame_send_cb_called); + + nghttp2_session_del(session); +} + +void test_nghttp2_submit_headers(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + my_user_data ud; + nghttp2_outbound_item *item; + nghttp2_stream *stream; + accumulator acc; + nghttp2_frame frame; + nghttp2_hd_inflater inflater; + nva_out out; + nghttp2_bufs bufs; + nghttp2_mem *mem; + + mem = nghttp2_mem_default(); + frame_pack_bufs_init(&bufs); + + nva_out_init(&out); + acc.length = 0; + ud.acc = &acc; + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.send_callback = accumulator_send_callback; + callbacks.on_frame_send_callback = on_frame_send_callback; + + CU_ASSERT(0 == nghttp2_session_client_new(&session, &callbacks, &ud)); + + nghttp2_hd_inflate_init(&inflater, mem); + CU_ASSERT(0 == nghttp2_submit_headers(session, NGHTTP2_FLAG_END_STREAM, 1, + NULL, reqnv, ARRLEN(reqnv), NULL)); + item = nghttp2_session_get_next_ob_item(session); + CU_ASSERT(ARRLEN(reqnv) == item->frame.headers.nvlen); + assert_nv_equal(reqnv, item->frame.headers.nva, item->frame.headers.nvlen, + mem); + CU_ASSERT((NGHTTP2_FLAG_END_STREAM | NGHTTP2_FLAG_END_HEADERS) == + item->frame.hd.flags); + + ud.frame_send_cb_called = 0; + ud.sent_frame_type = 0; + /* The transimission will be canceled because the stream 1 is not + open. */ + CU_ASSERT(0 == nghttp2_session_send(session)); + CU_ASSERT(0 == ud.frame_send_cb_called); + + stream = nghttp2_session_open_stream(session, 1, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, + NGHTTP2_STREAM_OPENING, NULL); + + CU_ASSERT(0 == nghttp2_submit_headers(session, NGHTTP2_FLAG_END_STREAM, 1, + NULL, reqnv, ARRLEN(reqnv), NULL)); + CU_ASSERT(0 == nghttp2_session_send(session)); + CU_ASSERT(1 == ud.frame_send_cb_called); + CU_ASSERT(NGHTTP2_HEADERS == ud.sent_frame_type); + CU_ASSERT(stream->shut_flags & NGHTTP2_SHUT_WR); + + CU_ASSERT(0 == unpack_frame(&frame, acc.buf, acc.length)); + + nghttp2_bufs_add(&bufs, acc.buf, acc.length); + inflate_hd(&inflater, &out, &bufs, NGHTTP2_FRAME_HDLEN, mem); + + CU_ASSERT(ARRLEN(reqnv) == out.nvlen); + assert_nv_equal(reqnv, out.nva, out.nvlen, mem); + + nva_out_reset(&out, mem); + nghttp2_bufs_free(&bufs); + nghttp2_frame_headers_free(&frame.headers, mem); + + nghttp2_hd_inflate_free(&inflater); + nghttp2_session_del(session); +} + +void test_nghttp2_submit_headers_continuation(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + nghttp2_nv nv[] = { + MAKE_NV("h1", ""), MAKE_NV("h1", ""), MAKE_NV("h1", ""), + MAKE_NV("h1", ""), MAKE_NV("h1", ""), MAKE_NV("h1", ""), + MAKE_NV("h1", ""), + }; + nghttp2_outbound_item *item; + uint8_t data[4096]; + size_t i; + my_user_data ud; + + memset(data, '0', sizeof(data)); + for (i = 0; i < ARRLEN(nv); ++i) { + nv[i].valuelen = sizeof(data); + nv[i].value = data; + } + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.send_callback = null_send_callback; + callbacks.on_frame_send_callback = on_frame_send_callback; + + CU_ASSERT(0 == nghttp2_session_client_new(&session, &callbacks, &ud)); + CU_ASSERT(1 == nghttp2_submit_headers(session, NGHTTP2_FLAG_END_STREAM, -1, + NULL, nv, ARRLEN(nv), NULL)); + item = nghttp2_session_get_next_ob_item(session); + CU_ASSERT(NGHTTP2_HEADERS == item->frame.hd.type); + CU_ASSERT((NGHTTP2_FLAG_END_STREAM | NGHTTP2_FLAG_END_HEADERS) == + item->frame.hd.flags); + CU_ASSERT(0 == (item->frame.hd.flags & NGHTTP2_FLAG_PRIORITY)); + + ud.frame_send_cb_called = 0; + CU_ASSERT(0 == nghttp2_session_send(session)); + CU_ASSERT(1 == ud.frame_send_cb_called); + + nghttp2_session_del(session); +} + +void test_nghttp2_submit_priority(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + nghttp2_stream *stream; + my_user_data ud; + nghttp2_priority_spec pri_spec; + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.send_callback = null_send_callback; + callbacks.on_frame_send_callback = on_frame_send_callback; + + nghttp2_session_client_new(&session, &callbacks, &ud); + stream = nghttp2_session_open_stream(session, 1, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, + NGHTTP2_STREAM_OPENING, NULL); + + nghttp2_priority_spec_init(&pri_spec, 0, 3, 0); + + /* depends on stream 0 */ + CU_ASSERT(0 == + nghttp2_submit_priority(session, NGHTTP2_FLAG_NONE, 1, &pri_spec)); + CU_ASSERT(0 == nghttp2_session_send(session)); + CU_ASSERT(3 == stream->weight); + + /* submit against idle stream */ + CU_ASSERT(0 == + nghttp2_submit_priority(session, NGHTTP2_FLAG_NONE, 3, &pri_spec)); + + ud.frame_send_cb_called = 0; + CU_ASSERT(0 == nghttp2_session_send(session)); + CU_ASSERT(1 == ud.frame_send_cb_called); + + nghttp2_session_del(session); +} + +void test_nghttp2_submit_settings(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + my_user_data ud; + nghttp2_outbound_item *item; + nghttp2_frame *frame; + nghttp2_settings_entry iv[7]; + nghttp2_frame ack_frame; + const int32_t UNKNOWN_ID = 1000000007; + nghttp2_mem *mem; + + mem = nghttp2_mem_default(); + + iv[0].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS; + iv[0].value = 5; + + iv[1].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE; + iv[1].value = 16 * 1024; + + iv[2].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS; + iv[2].value = 50; + + iv[3].settings_id = NGHTTP2_SETTINGS_HEADER_TABLE_SIZE; + iv[3].value = 0; + + iv[4].settings_id = UNKNOWN_ID; + iv[4].value = 999; + + iv[5].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE; + iv[5].value = (uint32_t)NGHTTP2_MAX_WINDOW_SIZE + 1; + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.send_callback = null_send_callback; + callbacks.on_frame_send_callback = on_frame_send_callback; + nghttp2_session_server_new(&session, &callbacks, &ud); + + CU_ASSERT(NGHTTP2_ERR_INVALID_ARGUMENT == + nghttp2_submit_settings(session, NGHTTP2_FLAG_NONE, iv, 6)); + + /* Make sure that local settings are not changed */ + CU_ASSERT(NGHTTP2_INITIAL_MAX_CONCURRENT_STREAMS == + session->local_settings.max_concurrent_streams); + CU_ASSERT(NGHTTP2_INITIAL_WINDOW_SIZE == + session->local_settings.initial_window_size); + + /* Now sends without 6th one */ + CU_ASSERT(0 == nghttp2_submit_settings(session, NGHTTP2_FLAG_NONE, iv, 5)); + + item = nghttp2_session_get_next_ob_item(session); + + CU_ASSERT(NGHTTP2_SETTINGS == item->frame.hd.type); + + frame = &item->frame; + CU_ASSERT(5 == frame->settings.niv); + CU_ASSERT(5 == frame->settings.iv[0].value); + CU_ASSERT(NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS == + frame->settings.iv[0].settings_id); + + CU_ASSERT(16 * 1024 == frame->settings.iv[1].value); + CU_ASSERT(NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE == + frame->settings.iv[1].settings_id); + + CU_ASSERT(UNKNOWN_ID == frame->settings.iv[4].settings_id); + CU_ASSERT(999 == frame->settings.iv[4].value); + + ud.frame_send_cb_called = 0; + CU_ASSERT(0 == nghttp2_session_send(session)); + CU_ASSERT(1 == ud.frame_send_cb_called); + + CU_ASSERT(50 == session->pending_local_max_concurrent_stream); + + nghttp2_frame_settings_init(&ack_frame.settings, NGHTTP2_FLAG_ACK, NULL, 0); + CU_ASSERT(0 == nghttp2_session_on_settings_received(session, &ack_frame, 0)); + nghttp2_frame_settings_free(&ack_frame.settings, mem); + + CU_ASSERT(16 * 1024 == session->local_settings.initial_window_size); + CU_ASSERT(0 == session->hd_inflater.ctx.hd_table_bufsize_max); + CU_ASSERT(50 == session->local_settings.max_concurrent_streams); + CU_ASSERT(NGHTTP2_INITIAL_MAX_CONCURRENT_STREAMS == + session->pending_local_max_concurrent_stream); + + nghttp2_session_del(session); +} + +void test_nghttp2_submit_settings_update_local_window_size(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + nghttp2_outbound_item *item; + nghttp2_settings_entry iv[4]; + nghttp2_stream *stream; + nghttp2_frame ack_frame; + nghttp2_mem *mem; + + mem = nghttp2_mem_default(); + nghttp2_frame_settings_init(&ack_frame.settings, NGHTTP2_FLAG_ACK, NULL, 0); + + iv[0].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE; + iv[0].value = 16 * 1024; + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.send_callback = null_send_callback; + + nghttp2_session_server_new(&session, &callbacks, NULL); + + stream = nghttp2_session_open_stream(session, 1, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, NGHTTP2_STREAM_OPENED, + NULL); + stream->local_window_size = NGHTTP2_INITIAL_WINDOW_SIZE + 100; + stream->recv_window_size = 32768; + + nghttp2_session_open_stream(session, 3, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, NGHTTP2_STREAM_OPENED, NULL); + + CU_ASSERT(0 == nghttp2_submit_settings(session, NGHTTP2_FLAG_NONE, iv, 1)); + CU_ASSERT(0 == nghttp2_session_send(session)); + CU_ASSERT(0 == nghttp2_session_on_settings_received(session, &ack_frame, 0)); + + stream = nghttp2_session_get_stream(session, 1); + CU_ASSERT(0 == stream->recv_window_size); + CU_ASSERT(16 * 1024 + 100 == stream->local_window_size); + + stream = nghttp2_session_get_stream(session, 3); + CU_ASSERT(16 * 1024 == stream->local_window_size); + + item = nghttp2_session_get_next_ob_item(session); + CU_ASSERT(NGHTTP2_WINDOW_UPDATE == item->frame.hd.type); + CU_ASSERT(32768 == item->frame.window_update.window_size_increment); + + nghttp2_session_del(session); + + /* Check overflow case */ + iv[0].value = 128 * 1024; + nghttp2_session_server_new(&session, &callbacks, NULL); + stream = nghttp2_session_open_stream(session, 1, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, NGHTTP2_STREAM_OPENED, + NULL); + stream->local_window_size = NGHTTP2_MAX_WINDOW_SIZE; + + CU_ASSERT(0 == nghttp2_submit_settings(session, NGHTTP2_FLAG_NONE, iv, 1)); + CU_ASSERT(0 == nghttp2_session_send(session)); + CU_ASSERT(0 == nghttp2_session_on_settings_received(session, &ack_frame, 0)); + + item = nghttp2_session_get_next_ob_item(session); + CU_ASSERT(NGHTTP2_GOAWAY == item->frame.hd.type); + CU_ASSERT(NGHTTP2_FLOW_CONTROL_ERROR == item->frame.goaway.error_code); + + nghttp2_session_del(session); + nghttp2_frame_settings_free(&ack_frame.settings, mem); +} + +void test_nghttp2_submit_push_promise(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + my_user_data ud; + nghttp2_stream *stream; + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.send_callback = null_send_callback; + callbacks.on_frame_send_callback = on_frame_send_callback; + callbacks.on_frame_not_send_callback = on_frame_not_send_callback; + + CU_ASSERT(0 == nghttp2_session_server_new(&session, &callbacks, &ud)); + nghttp2_session_open_stream(session, 1, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, NGHTTP2_STREAM_OPENING, NULL); + CU_ASSERT(2 == nghttp2_submit_push_promise(session, NGHTTP2_FLAG_NONE, 1, + reqnv, ARRLEN(reqnv), &ud)); + + ud.frame_send_cb_called = 0; + ud.sent_frame_type = 0; + CU_ASSERT(0 == nghttp2_session_send(session)); + CU_ASSERT(1 == ud.frame_send_cb_called); + CU_ASSERT(NGHTTP2_PUSH_PROMISE == ud.sent_frame_type); + stream = nghttp2_session_get_stream(session, 2); + CU_ASSERT(NGHTTP2_STREAM_RESERVED == stream->state); + CU_ASSERT(&ud == nghttp2_session_get_stream_user_data(session, 2)); + + /* submit PUSH_PROMISE while associated stream is not opened */ + CU_ASSERT(4 == nghttp2_submit_push_promise(session, NGHTTP2_FLAG_NONE, 3, + reqnv, ARRLEN(reqnv), &ud)); + + ud.frame_not_send_cb_called = 0; + + CU_ASSERT(0 == nghttp2_session_send(session)); + CU_ASSERT(1 == ud.frame_not_send_cb_called); + CU_ASSERT(NGHTTP2_PUSH_PROMISE == ud.not_sent_frame_type); + + stream = nghttp2_session_get_stream(session, 4); + + CU_ASSERT(NULL == stream); + + nghttp2_session_del(session); +} + +void test_nghttp2_submit_window_update(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + my_user_data ud; + nghttp2_outbound_item *item; + nghttp2_stream *stream; + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.send_callback = null_send_callback; + + nghttp2_session_client_new(&session, &callbacks, &ud); + stream = nghttp2_session_open_stream(session, 2, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, NGHTTP2_STREAM_OPENED, + NULL); + stream->recv_window_size = 4096; + + CU_ASSERT(0 == + nghttp2_submit_window_update(session, NGHTTP2_FLAG_NONE, 2, 1024)); + item = nghttp2_session_get_next_ob_item(session); + CU_ASSERT(NGHTTP2_WINDOW_UPDATE == item->frame.hd.type); + CU_ASSERT(1024 == item->frame.window_update.window_size_increment); + CU_ASSERT(0 == nghttp2_session_send(session)); + CU_ASSERT(3072 == stream->recv_window_size); + + CU_ASSERT(0 == + nghttp2_submit_window_update(session, NGHTTP2_FLAG_NONE, 2, 4096)); + item = nghttp2_session_get_next_ob_item(session); + CU_ASSERT(NGHTTP2_WINDOW_UPDATE == item->frame.hd.type); + CU_ASSERT(4096 == item->frame.window_update.window_size_increment); + CU_ASSERT(0 == nghttp2_session_send(session)); + CU_ASSERT(0 == stream->recv_window_size); + + CU_ASSERT(0 == + nghttp2_submit_window_update(session, NGHTTP2_FLAG_NONE, 2, 4096)); + item = nghttp2_session_get_next_ob_item(session); + CU_ASSERT(NGHTTP2_WINDOW_UPDATE == item->frame.hd.type); + CU_ASSERT(4096 == item->frame.window_update.window_size_increment); + CU_ASSERT(0 == nghttp2_session_send(session)); + CU_ASSERT(0 == stream->recv_window_size); + + CU_ASSERT(0 == + nghttp2_submit_window_update(session, NGHTTP2_FLAG_NONE, 2, 0)); + /* It is ok if stream is closed or does not exist at the call + time */ + CU_ASSERT(0 == + nghttp2_submit_window_update(session, NGHTTP2_FLAG_NONE, 4, 4096)); + + nghttp2_session_del(session); +} + +void test_nghttp2_submit_window_update_local_window_size(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + nghttp2_outbound_item *item; + nghttp2_stream *stream; + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.send_callback = null_send_callback; + + nghttp2_session_client_new(&session, &callbacks, NULL); + stream = nghttp2_session_open_stream(session, 2, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, NGHTTP2_STREAM_OPENED, + NULL); + stream->recv_window_size = 4096; + + CU_ASSERT(0 == nghttp2_submit_window_update(session, NGHTTP2_FLAG_NONE, 2, + stream->recv_window_size + 1)); + CU_ASSERT(NGHTTP2_INITIAL_WINDOW_SIZE + 1 == stream->local_window_size); + CU_ASSERT(0 == stream->recv_window_size); + item = nghttp2_session_get_next_ob_item(session); + CU_ASSERT(NGHTTP2_WINDOW_UPDATE == item->frame.hd.type); + CU_ASSERT(4097 == item->frame.window_update.window_size_increment); + + CU_ASSERT(0 == nghttp2_session_send(session)); + + /* Let's decrement local window size */ + stream->recv_window_size = 4096; + CU_ASSERT(0 == nghttp2_submit_window_update(session, NGHTTP2_FLAG_NONE, 2, + -stream->local_window_size / 2)); + CU_ASSERT(32768 == stream->local_window_size); + CU_ASSERT(-28672 == stream->recv_window_size); + CU_ASSERT(32768 == stream->recv_reduction); + + item = nghttp2_session_get_next_ob_item(session); + CU_ASSERT(item == NULL); + + /* Increase local window size */ + CU_ASSERT(0 == + nghttp2_submit_window_update(session, NGHTTP2_FLAG_NONE, 2, 16384)); + CU_ASSERT(49152 == stream->local_window_size); + CU_ASSERT(-12288 == stream->recv_window_size); + CU_ASSERT(16384 == stream->recv_reduction); + CU_ASSERT(NULL == nghttp2_session_get_next_ob_item(session)); + + CU_ASSERT(NGHTTP2_ERR_FLOW_CONTROL == + nghttp2_submit_window_update(session, NGHTTP2_FLAG_NONE, 2, + NGHTTP2_MAX_WINDOW_SIZE)); + + CU_ASSERT(0 == nghttp2_session_send(session)); + + /* Check connection-level flow control */ + session->recv_window_size = 4096; + CU_ASSERT(0 == nghttp2_submit_window_update(session, NGHTTP2_FLAG_NONE, 0, + session->recv_window_size + 1)); + CU_ASSERT(NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE + 1 == + session->local_window_size); + CU_ASSERT(0 == session->recv_window_size); + item = nghttp2_session_get_next_ob_item(session); + CU_ASSERT(NGHTTP2_WINDOW_UPDATE == item->frame.hd.type); + CU_ASSERT(4097 == item->frame.window_update.window_size_increment); + + CU_ASSERT(0 == nghttp2_session_send(session)); + + /* Go decrement part */ + session->recv_window_size = 4096; + CU_ASSERT(0 == nghttp2_submit_window_update(session, NGHTTP2_FLAG_NONE, 0, + -session->local_window_size / 2)); + CU_ASSERT(32768 == session->local_window_size); + CU_ASSERT(-28672 == session->recv_window_size); + CU_ASSERT(32768 == session->recv_reduction); + item = nghttp2_session_get_next_ob_item(session); + CU_ASSERT(item == NULL); + + /* Increase local window size */ + CU_ASSERT(0 == + nghttp2_submit_window_update(session, NGHTTP2_FLAG_NONE, 0, 16384)); + CU_ASSERT(49152 == session->local_window_size); + CU_ASSERT(-12288 == session->recv_window_size); + CU_ASSERT(16384 == session->recv_reduction); + CU_ASSERT(NULL == nghttp2_session_get_next_ob_item(session)); + + CU_ASSERT(NGHTTP2_ERR_FLOW_CONTROL == + nghttp2_submit_window_update(session, NGHTTP2_FLAG_NONE, 0, + NGHTTP2_MAX_WINDOW_SIZE)); + + nghttp2_session_del(session); +} + +void test_nghttp2_submit_shutdown_notice(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + my_user_data ud; + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.send_callback = null_send_callback; + callbacks.on_frame_send_callback = on_frame_send_callback; + callbacks.on_frame_not_send_callback = on_frame_not_send_callback; + + nghttp2_session_server_new(&session, &callbacks, &ud); + + CU_ASSERT(0 == nghttp2_submit_shutdown_notice(session)); + + ud.frame_send_cb_called = 0; + + nghttp2_session_send(session); + + CU_ASSERT(1 == ud.frame_send_cb_called); + CU_ASSERT(NGHTTP2_GOAWAY == ud.sent_frame_type); + CU_ASSERT((1u << 31) - 1 == session->local_last_stream_id); + + /* After another GOAWAY, nghttp2_submit_shutdown_notice() is + noop. */ + CU_ASSERT(0 == nghttp2_session_terminate_session(session, NGHTTP2_NO_ERROR)); + + ud.frame_send_cb_called = 0; + + nghttp2_session_send(session); + + CU_ASSERT(1 == ud.frame_send_cb_called); + CU_ASSERT(NGHTTP2_GOAWAY == ud.sent_frame_type); + CU_ASSERT(0 == session->local_last_stream_id); + + CU_ASSERT(0 == nghttp2_submit_shutdown_notice(session)); + + ud.frame_send_cb_called = 0; + ud.frame_not_send_cb_called = 0; + + nghttp2_session_send(session); + + CU_ASSERT(0 == ud.frame_send_cb_called); + CU_ASSERT(0 == ud.frame_not_send_cb_called); + + nghttp2_session_del(session); + + /* Using nghttp2_submit_shutdown_notice() with client side session + is error */ + nghttp2_session_client_new(&session, &callbacks, NULL); + + CU_ASSERT(NGHTTP2_ERR_INVALID_STATE == + nghttp2_submit_shutdown_notice(session)); + + nghttp2_session_del(session); +} + +void test_nghttp2_submit_invalid_nv(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + nghttp2_nv empty_name_nv[] = {MAKE_NV("Version", "HTTP/1.1"), + MAKE_NV("", "empty name")}; + + /* Now invalid header name/value pair in HTTP/1.1 is accepted in + nghttp2 */ + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + + CU_ASSERT(0 == nghttp2_session_server_new(&session, &callbacks, NULL)); + + /* nghttp2_submit_request */ + CU_ASSERT(0 < nghttp2_submit_request(session, NULL, empty_name_nv, + ARRLEN(empty_name_nv), NULL, NULL)); + + /* nghttp2_submit_response */ + CU_ASSERT(0 == nghttp2_submit_response(session, 2, empty_name_nv, + ARRLEN(empty_name_nv), NULL)); + + /* nghttp2_submit_headers */ + CU_ASSERT(0 < nghttp2_submit_headers(session, NGHTTP2_FLAG_NONE, -1, NULL, + empty_name_nv, ARRLEN(empty_name_nv), + NULL)); + + /* nghttp2_submit_push_promise */ + open_stream(session, 1); + + CU_ASSERT(0 < nghttp2_submit_push_promise(session, NGHTTP2_FLAG_NONE, 1, + empty_name_nv, + ARRLEN(empty_name_nv), NULL)); + + nghttp2_session_del(session); +} + +void test_nghttp2_session_open_stream(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + nghttp2_stream *stream; + nghttp2_priority_spec pri_spec; + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + nghttp2_session_server_new(&session, &callbacks, NULL); + + nghttp2_priority_spec_init(&pri_spec, 0, 245, 0); + + stream = nghttp2_session_open_stream(session, 1, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec, NGHTTP2_STREAM_OPENED, NULL); + CU_ASSERT(1 == session->num_incoming_streams); + CU_ASSERT(0 == session->num_outgoing_streams); + CU_ASSERT(NGHTTP2_STREAM_OPENED == stream->state); + CU_ASSERT(245 == stream->weight); + CU_ASSERT(NULL == stream->dep_prev); + CU_ASSERT(NGHTTP2_SHUT_NONE == stream->shut_flags); + + stream = nghttp2_session_open_stream(session, 2, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, + NGHTTP2_STREAM_OPENING, NULL); + CU_ASSERT(1 == session->num_incoming_streams); + CU_ASSERT(1 == session->num_outgoing_streams); + CU_ASSERT(NULL == stream->dep_prev); + CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT == stream->weight); + CU_ASSERT(NGHTTP2_SHUT_NONE == stream->shut_flags); + + stream = nghttp2_session_open_stream(session, 4, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, + NGHTTP2_STREAM_RESERVED, NULL); + CU_ASSERT(1 == session->num_incoming_streams); + CU_ASSERT(1 == session->num_outgoing_streams); + CU_ASSERT(NULL == stream->dep_prev); + CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT == stream->weight); + CU_ASSERT(NGHTTP2_SHUT_RD == stream->shut_flags); + + nghttp2_priority_spec_init(&pri_spec, 1, 17, 1); + + stream = nghttp2_session_open_stream(session, 3, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec, NGHTTP2_STREAM_OPENED, NULL); + CU_ASSERT(17 == stream->weight); + CU_ASSERT(1 == stream->dep_prev->stream_id); + + /* Dependency to idle stream */ + nghttp2_priority_spec_init(&pri_spec, 1000000007, 240, 1); + + stream = nghttp2_session_open_stream(session, 5, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec, NGHTTP2_STREAM_OPENED, NULL); + CU_ASSERT(240 == stream->weight); + CU_ASSERT(1000000007 == stream->dep_prev->stream_id); + + stream = nghttp2_session_get_stream_raw(session, 1000000007); + + CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT == stream->weight); + CU_ASSERT(NULL != stream->root_next); + + /* Dependency to closed stream which is not in dependency tree */ + session->last_recv_stream_id = 7; + + nghttp2_priority_spec_init(&pri_spec, 7, 10, 0); + + stream = nghttp2_session_open_stream(session, 9, NGHTTP2_FLAG_NONE, &pri_spec, + NGHTTP2_STREAM_OPENED, NULL); + + CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT == stream->weight); + + nghttp2_session_del(session); + + nghttp2_session_client_new(&session, &callbacks, NULL); + stream = nghttp2_session_open_stream(session, 4, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, + NGHTTP2_STREAM_RESERVED, NULL); + CU_ASSERT(0 == session->num_incoming_streams); + CU_ASSERT(0 == session->num_outgoing_streams); + CU_ASSERT(NULL == stream->dep_prev); + CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT == stream->weight); + CU_ASSERT(NGHTTP2_SHUT_WR == stream->shut_flags); + + nghttp2_session_del(session); +} + +void test_nghttp2_session_open_stream_with_idle_stream_dep(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + nghttp2_stream *stream; + nghttp2_priority_spec pri_spec; + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + nghttp2_session_server_new(&session, &callbacks, NULL); + + /* Dependency to idle stream */ + nghttp2_priority_spec_init(&pri_spec, 101, 245, 0); + + stream = nghttp2_session_open_stream(session, 1, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec, NGHTTP2_STREAM_OPENED, NULL); + + CU_ASSERT(245 == stream->weight); + CU_ASSERT(101 == stream->dep_prev->stream_id); + + stream = nghttp2_session_get_stream_raw(session, 101); + + CU_ASSERT(NGHTTP2_STREAM_IDLE == stream->state); + CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT == stream->weight); + + nghttp2_priority_spec_init(&pri_spec, 211, 1, 0); + + /* stream 101 was already created as idle. */ + stream = nghttp2_session_open_stream(session, 101, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec, NGHTTP2_STREAM_OPENED, NULL); + + CU_ASSERT(1 == stream->weight); + CU_ASSERT(211 == stream->dep_prev->stream_id); + + stream = nghttp2_session_get_stream_raw(session, 211); + + CU_ASSERT(NGHTTP2_STREAM_IDLE == stream->state); + CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT == stream->weight); + + nghttp2_session_del(session); +} + +void test_nghttp2_session_get_next_ob_item(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + nghttp2_priority_spec pri_spec; + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.send_callback = null_send_callback; + + nghttp2_session_server_new(&session, &callbacks, NULL); + session->remote_settings.max_concurrent_streams = 2; + + CU_ASSERT(NULL == nghttp2_session_get_next_ob_item(session)); + nghttp2_submit_ping(session, NGHTTP2_FLAG_NONE, NULL); + CU_ASSERT(NGHTTP2_PING == + nghttp2_session_get_next_ob_item(session)->frame.hd.type); + + nghttp2_submit_request(session, NULL, NULL, 0, NULL, NULL); + CU_ASSERT(NGHTTP2_PING == + nghttp2_session_get_next_ob_item(session)->frame.hd.type); + + CU_ASSERT(0 == nghttp2_session_send(session)); + CU_ASSERT(NULL == nghttp2_session_get_next_ob_item(session)); + + /* Incoming stream does not affect the number of outgoing max + concurrent streams. */ + nghttp2_session_open_stream(session, 1, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, NGHTTP2_STREAM_OPENING, NULL); + + nghttp2_priority_spec_init(&pri_spec, 0, NGHTTP2_MAX_WEIGHT, 0); + + nghttp2_submit_request(session, &pri_spec, NULL, 0, NULL, NULL); + CU_ASSERT(NGHTTP2_HEADERS == + nghttp2_session_get_next_ob_item(session)->frame.hd.type); + CU_ASSERT(0 == nghttp2_session_send(session)); + + nghttp2_submit_request(session, &pri_spec, NULL, 0, NULL, NULL); + CU_ASSERT(NULL == nghttp2_session_get_next_ob_item(session)); + + session->remote_settings.max_concurrent_streams = 3; + + CU_ASSERT(NGHTTP2_HEADERS == + nghttp2_session_get_next_ob_item(session)->frame.hd.type); + + nghttp2_session_del(session); +} + +void test_nghttp2_session_pop_next_ob_item(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + nghttp2_outbound_item *item; + nghttp2_priority_spec pri_spec; + nghttp2_stream *stream; + nghttp2_mem *mem; + + mem = nghttp2_mem_default(); + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.send_callback = null_send_callback; + + nghttp2_session_server_new(&session, &callbacks, NULL); + session->remote_settings.max_concurrent_streams = 1; + + CU_ASSERT(NULL == nghttp2_session_pop_next_ob_item(session)); + + nghttp2_submit_ping(session, NGHTTP2_FLAG_NONE, NULL); + + nghttp2_priority_spec_init(&pri_spec, 0, 254, 0); + + nghttp2_submit_request(session, &pri_spec, NULL, 0, NULL, NULL); + + item = nghttp2_session_pop_next_ob_item(session); + CU_ASSERT(NGHTTP2_PING == item->frame.hd.type); + nghttp2_outbound_item_free(item, mem); + mem->free(item, NULL); + + item = nghttp2_session_pop_next_ob_item(session); + CU_ASSERT(NGHTTP2_HEADERS == item->frame.hd.type); + nghttp2_outbound_item_free(item, mem); + mem->free(item, NULL); + + CU_ASSERT(NULL == nghttp2_session_pop_next_ob_item(session)); + + /* Incoming stream does not affect the number of outgoing max + concurrent streams. */ + nghttp2_session_open_stream(session, 1, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, NGHTTP2_STREAM_OPENING, NULL); + /* In-flight outgoing stream */ + nghttp2_session_open_stream(session, 4, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, NGHTTP2_STREAM_OPENING, NULL); + + nghttp2_priority_spec_init(&pri_spec, 0, NGHTTP2_MAX_WEIGHT, 0); + + nghttp2_submit_request(session, &pri_spec, NULL, 0, NULL, NULL); + nghttp2_submit_response(session, 1, NULL, 0, NULL); + + item = nghttp2_session_pop_next_ob_item(session); + CU_ASSERT(NGHTTP2_HEADERS == item->frame.hd.type); + CU_ASSERT(1 == item->frame.hd.stream_id); + + stream = nghttp2_session_get_stream(session, 1); + + nghttp2_stream_detach_item(stream, session); + + nghttp2_outbound_item_free(item, mem); + mem->free(item, NULL); + + CU_ASSERT(NULL == nghttp2_session_pop_next_ob_item(session)); + + session->remote_settings.max_concurrent_streams = 2; + + item = nghttp2_session_pop_next_ob_item(session); + CU_ASSERT(NGHTTP2_HEADERS == item->frame.hd.type); + nghttp2_outbound_item_free(item, mem); + mem->free(item, NULL); + + nghttp2_session_del(session); + + /* Check that push reply HEADERS are queued into ob_ss_pq */ + nghttp2_session_server_new(&session, &callbacks, NULL); + session->remote_settings.max_concurrent_streams = 0; + nghttp2_session_open_stream(session, 2, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, NGHTTP2_STREAM_RESERVED, NULL); + CU_ASSERT(0 == nghttp2_submit_headers(session, NGHTTP2_FLAG_END_STREAM, 2, + NULL, NULL, 0, NULL)); + CU_ASSERT(NULL == nghttp2_session_pop_next_ob_item(session)); + CU_ASSERT(1 == nghttp2_outbound_queue_size(&session->ob_syn)); + nghttp2_session_del(session); +} + +void test_nghttp2_session_reply_fail(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + nghttp2_data_provider data_prd; + my_user_data ud; + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.send_callback = fail_send_callback; + + data_prd.read_callback = fixed_length_data_source_read_callback; + ud.data_source_length = 4 * 1024; + CU_ASSERT(0 == nghttp2_session_server_new(&session, &callbacks, &ud)); + nghttp2_session_open_stream(session, 1, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, NGHTTP2_STREAM_OPENING, NULL); + CU_ASSERT(0 == nghttp2_submit_response(session, 1, NULL, 0, &data_prd)); + CU_ASSERT(NGHTTP2_ERR_CALLBACK_FAILURE == nghttp2_session_send(session)); + nghttp2_session_del(session); +} + +void test_nghttp2_session_max_concurrent_streams(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + nghttp2_frame frame; + nghttp2_outbound_item *item; + nghttp2_mem *mem; + + mem = nghttp2_mem_default(); + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.send_callback = null_send_callback; + + nghttp2_session_server_new(&session, &callbacks, NULL); + nghttp2_session_open_stream(session, 1, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, NGHTTP2_STREAM_OPENED, NULL); + + /* Check un-ACKed SETTINGS_MAX_CONCURRENT_STREAMS */ + nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_END_HEADERS, 3, + NGHTTP2_HCAT_HEADERS, NULL, NULL, 0); + session->pending_local_max_concurrent_stream = 1; + + CU_ASSERT(NGHTTP2_ERR_IGN_HEADER_BLOCK == + nghttp2_session_on_request_headers_received(session, &frame)); + + item = nghttp2_outbound_queue_top(&session->ob_reg); + CU_ASSERT(NGHTTP2_RST_STREAM == item->frame.hd.type); + CU_ASSERT(NGHTTP2_REFUSED_STREAM == item->frame.rst_stream.error_code); + + CU_ASSERT(0 == nghttp2_session_send(session)); + + /* Check ACKed SETTINGS_MAX_CONCURRENT_STREAMS */ + session->local_settings.max_concurrent_streams = 1; + frame.hd.stream_id = 5; + + CU_ASSERT(NGHTTP2_ERR_IGN_HEADER_BLOCK == + nghttp2_session_on_request_headers_received(session, &frame)); + + item = nghttp2_outbound_queue_top(&session->ob_reg); + CU_ASSERT(NGHTTP2_GOAWAY == item->frame.hd.type); + CU_ASSERT(NGHTTP2_PROTOCOL_ERROR == item->frame.goaway.error_code); + + nghttp2_frame_headers_free(&frame.headers, mem); + nghttp2_session_del(session); +} + +/* + * Check that on_stream_close_callback is called when server pushed + * HEADERS have NGHTTP2_FLAG_END_STREAM. + */ +void test_nghttp2_session_stream_close_on_headers_push(void) { + /* nghttp2_session *session; */ + /* nghttp2_session_callbacks callbacks; */ + /* const char *nv[] = { NULL }; */ + /* my_user_data ud; */ + /* nghttp2_frame frame; */ + + /* memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); */ + /* callbacks.on_stream_close_callback = */ + /* no_stream_user_data_stream_close_callback; */ + /* ud.stream_close_cb_called = 0; */ + + /* nghttp2_session_client_new(&session, NGHTTP2_PROTO_SPDY2, &callbacks, &ud); + */ + /* nghttp2_session_open_stream(session, 1, NGHTTP2_CTRL_FLAG_NONE, 3, */ + /* NGHTTP2_STREAM_OPENING, NULL); */ + /* nghttp2_frame_syn_stream_init(&frame.syn_stream, NGHTTP2_PROTO_SPDY2, */ + /* NGHTTP2_CTRL_FLAG_FIN | */ + /* NGHTTP2_CTRL_FLAG_UNIDIRECTIONAL, */ + /* 2, 1, 3, dup_nv(nv)); */ + + /* CU_ASSERT(0 == nghttp2_session_on_request_headers_received(session, + * &frame)); */ + + /* nghttp2_frame_syn_stream_free(&frame.syn_stream); */ + /* nghttp2_session_del(session); */ +} + +void test_nghttp2_session_stop_data_with_rst_stream(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + my_user_data ud; + nghttp2_data_provider data_prd; + nghttp2_frame frame; + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.on_frame_send_callback = on_frame_send_callback; + callbacks.send_callback = block_count_send_callback; + data_prd.read_callback = fixed_length_data_source_read_callback; + + ud.frame_send_cb_called = 0; + ud.data_source_length = NGHTTP2_DATA_PAYLOADLEN * 4; + + nghttp2_session_server_new(&session, &callbacks, &ud); + nghttp2_session_open_stream(session, 1, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, NGHTTP2_STREAM_OPENING, NULL); + nghttp2_submit_response(session, 1, NULL, 0, &data_prd); + + ud.block_count = 2; + /* Sends response HEADERS + DATA[0] */ + CU_ASSERT(0 == nghttp2_session_send(session)); + CU_ASSERT(NGHTTP2_DATA == ud.sent_frame_type); + /* data for DATA[1] is read from data_prd but it is not sent */ + CU_ASSERT(ud.data_source_length == NGHTTP2_DATA_PAYLOADLEN * 2); + + nghttp2_frame_rst_stream_init(&frame.rst_stream, 1, NGHTTP2_CANCEL); + CU_ASSERT(0 == nghttp2_session_on_rst_stream_received(session, &frame)); + nghttp2_frame_rst_stream_free(&frame.rst_stream); + + /* Big enough number to send all DATA frames potentially. */ + ud.block_count = 100; + /* Nothing will be sent in the following call. */ + CU_ASSERT(0 == nghttp2_session_send(session)); + /* With RST_STREAM, stream is canceled and further DATA on that + stream are not sent. */ + CU_ASSERT(ud.data_source_length == NGHTTP2_DATA_PAYLOADLEN * 2); + + CU_ASSERT(NULL == nghttp2_session_get_stream(session, 1)); + + nghttp2_session_del(session); +} + +void test_nghttp2_session_defer_data(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + my_user_data ud; + nghttp2_data_provider data_prd; + nghttp2_outbound_item *item; + nghttp2_stream *stream; + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.on_frame_send_callback = on_frame_send_callback; + callbacks.send_callback = block_count_send_callback; + data_prd.read_callback = defer_data_source_read_callback; + + ud.frame_send_cb_called = 0; + ud.data_source_length = NGHTTP2_DATA_PAYLOADLEN * 4; + + nghttp2_session_server_new(&session, &callbacks, &ud); + stream = nghttp2_session_open_stream(session, 1, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, + NGHTTP2_STREAM_OPENING, NULL); + + session->remote_window_size = 1 << 20; + stream->remote_window_size = 1 << 20; + + nghttp2_submit_response(session, 1, NULL, 0, &data_prd); + + ud.block_count = 1; + /* Sends HEADERS reply */ + CU_ASSERT(0 == nghttp2_session_send(session)); + CU_ASSERT(NGHTTP2_HEADERS == ud.sent_frame_type); + /* No data is read */ + CU_ASSERT(ud.data_source_length == NGHTTP2_DATA_PAYLOADLEN * 4); + + ud.block_count = 1; + nghttp2_submit_ping(session, NGHTTP2_FLAG_NONE, NULL); + /* Sends PING */ + CU_ASSERT(0 == nghttp2_session_send(session)); + CU_ASSERT(NGHTTP2_PING == ud.sent_frame_type); + + /* Resume deferred DATA */ + CU_ASSERT(0 == nghttp2_session_resume_data(session, 1)); + item = (nghttp2_outbound_item *)nghttp2_pq_top(&session->ob_da_pq); + item->aux_data.data.data_prd.read_callback = + fixed_length_data_source_read_callback; + ud.block_count = 1; + /* Reads 2 DATA chunks */ + CU_ASSERT(0 == nghttp2_session_send(session)); + CU_ASSERT(ud.data_source_length == NGHTTP2_DATA_PAYLOADLEN * 2); + + /* Deferred again */ + item->aux_data.data.data_prd.read_callback = defer_data_source_read_callback; + /* This is needed since 16KiB block is already read and waiting to be + sent. No read_callback invocation. */ + ud.block_count = 1; + CU_ASSERT(0 == nghttp2_session_send(session)); + CU_ASSERT(ud.data_source_length == NGHTTP2_DATA_PAYLOADLEN * 2); + + /* Resume deferred DATA */ + CU_ASSERT(0 == nghttp2_session_resume_data(session, 1)); + item = (nghttp2_outbound_item *)nghttp2_pq_top(&session->ob_da_pq); + item->aux_data.data.data_prd.read_callback = + fixed_length_data_source_read_callback; + ud.block_count = 1; + /* Reads 2 16KiB blocks */ + CU_ASSERT(0 == nghttp2_session_send(session)); + CU_ASSERT(ud.data_source_length == 0); + + nghttp2_session_del(session); +} + +void test_nghttp2_session_flow_control(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + my_user_data ud; + nghttp2_data_provider data_prd; + nghttp2_frame frame; + nghttp2_stream *stream; + int32_t new_initial_window_size; + nghttp2_settings_entry iv[1]; + nghttp2_frame settings_frame; + nghttp2_mem *mem; + + mem = nghttp2_mem_default(); + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.send_callback = fixed_bytes_send_callback; + callbacks.on_frame_send_callback = on_frame_send_callback; + data_prd.read_callback = fixed_length_data_source_read_callback; + + ud.frame_send_cb_called = 0; + ud.data_source_length = 128 * 1024; + /* Use smaller emission count so that we can check outbound flow + control window calculation is correct. */ + ud.fixed_sendlen = 2 * 1024; + + /* Initial window size to 64KiB - 1*/ + nghttp2_session_client_new(&session, &callbacks, &ud); + /* Change it to 64KiB for easy calculation */ + session->remote_window_size = 64 * 1024; + session->remote_settings.initial_window_size = 64 * 1024; + + nghttp2_submit_request(session, NULL, NULL, 0, &data_prd, NULL); + + /* Sends 64KiB - 1 data */ + CU_ASSERT(0 == nghttp2_session_send(session)); + CU_ASSERT(64 * 1024 == ud.data_source_length); + + /* Back 32KiB in stream window */ + nghttp2_frame_window_update_init(&frame.window_update, NGHTTP2_FLAG_NONE, 1, + 32 * 1024); + nghttp2_session_on_window_update_received(session, &frame); + + /* Send nothing because of connection-level window */ + CU_ASSERT(0 == nghttp2_session_send(session)); + CU_ASSERT(64 * 1024 == ud.data_source_length); + + /* Back 32KiB in connection-level window */ + frame.hd.stream_id = 0; + nghttp2_session_on_window_update_received(session, &frame); + + /* Sends another 32KiB data */ + CU_ASSERT(0 == nghttp2_session_send(session)); + CU_ASSERT(32 * 1024 == ud.data_source_length); + + stream = nghttp2_session_get_stream(session, 1); + /* Change initial window size to 16KiB. The window_size becomes + negative. */ + new_initial_window_size = 16 * 1024; + stream->remote_window_size = + new_initial_window_size - (session->remote_settings.initial_window_size - + stream->remote_window_size); + session->remote_settings.initial_window_size = new_initial_window_size; + CU_ASSERT(-48 * 1024 == stream->remote_window_size); + + /* Back 48KiB to stream window */ + frame.hd.stream_id = 1; + frame.window_update.window_size_increment = 48 * 1024; + nghttp2_session_on_window_update_received(session, &frame); + + /* Nothing is sent because window_size is 0 */ + CU_ASSERT(0 == nghttp2_session_send(session)); + CU_ASSERT(32 * 1024 == ud.data_source_length); + + /* Back 16KiB in stream window */ + frame.hd.stream_id = 1; + frame.window_update.window_size_increment = 16 * 1024; + nghttp2_session_on_window_update_received(session, &frame); + + /* Back 24KiB in connection-level window */ + frame.hd.stream_id = 0; + frame.window_update.window_size_increment = 24 * 1024; + nghttp2_session_on_window_update_received(session, &frame); + + /* Sends another 16KiB data */ + CU_ASSERT(0 == nghttp2_session_send(session)); + CU_ASSERT(16 * 1024 == ud.data_source_length); + + /* Increase initial window size to 32KiB */ + iv[0].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE; + iv[0].value = 32 * 1024; + + nghttp2_frame_settings_init(&settings_frame.settings, NGHTTP2_FLAG_NONE, + dup_iv(iv, 1), 1); + nghttp2_session_on_settings_received(session, &settings_frame, 1); + nghttp2_frame_settings_free(&settings_frame.settings, mem); + + /* Sends another 8KiB data */ + CU_ASSERT(0 == nghttp2_session_send(session)); + CU_ASSERT(8 * 1024 == ud.data_source_length); + + /* Back 8KiB in connection-level window */ + frame.hd.stream_id = 0; + frame.window_update.window_size_increment = 8 * 1024; + nghttp2_session_on_window_update_received(session, &frame); + + /* Sends last 8KiB data */ + CU_ASSERT(0 == nghttp2_session_send(session)); + CU_ASSERT(0 == ud.data_source_length); + CU_ASSERT(nghttp2_session_get_stream(session, 1)->shut_flags & + NGHTTP2_SHUT_WR); + + nghttp2_frame_window_update_free(&frame.window_update); + nghttp2_session_del(session); +} + +void test_nghttp2_session_flow_control_data_recv(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + uint8_t data[64 * 1024 + 16]; + nghttp2_frame_hd hd; + nghttp2_outbound_item *item; + nghttp2_stream *stream; + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.send_callback = null_send_callback; + + /* Initial window size to 64KiB - 1*/ + nghttp2_session_client_new(&session, &callbacks, NULL); + + stream = nghttp2_session_open_stream(session, 1, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, NGHTTP2_STREAM_OPENED, + NULL); + + session->next_stream_id = 3; + + nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_WR); + + session->local_window_size = NGHTTP2_MAX_PAYLOADLEN; + stream->local_window_size = NGHTTP2_MAX_PAYLOADLEN; + + /* Create DATA frame */ + memset(data, 0, sizeof(data)); + nghttp2_frame_hd_init(&hd, NGHTTP2_MAX_PAYLOADLEN, NGHTTP2_DATA, + NGHTTP2_FLAG_END_STREAM, 1); + + nghttp2_frame_pack_frame_hd(data, &hd); + CU_ASSERT(NGHTTP2_MAX_PAYLOADLEN + NGHTTP2_FRAME_HDLEN == + nghttp2_session_mem_recv(session, data, NGHTTP2_MAX_PAYLOADLEN + + NGHTTP2_FRAME_HDLEN)); + + item = nghttp2_session_get_next_ob_item(session); + /* Since this is the last frame, stream-level WINDOW_UPDATE is not + issued, but connection-level is. */ + CU_ASSERT(NGHTTP2_WINDOW_UPDATE == item->frame.hd.type); + CU_ASSERT(0 == item->frame.hd.stream_id); + CU_ASSERT(NGHTTP2_MAX_PAYLOADLEN == + item->frame.window_update.window_size_increment); + + CU_ASSERT(0 == nghttp2_session_send(session)); + + /* Receive DATA for closed stream. They are still subject to under + connection-level flow control, since this situation arises when + RST_STREAM is issued by the remote, but the local side keeps + sending DATA frames. Without calculating connection-level window, + the subsequent flow control gets confused. */ + CU_ASSERT(NGHTTP2_MAX_PAYLOADLEN + NGHTTP2_FRAME_HDLEN == + nghttp2_session_mem_recv(session, data, NGHTTP2_MAX_PAYLOADLEN + + NGHTTP2_FRAME_HDLEN)); + + item = nghttp2_session_get_next_ob_item(session); + CU_ASSERT(NGHTTP2_WINDOW_UPDATE == item->frame.hd.type); + CU_ASSERT(0 == item->frame.hd.stream_id); + CU_ASSERT(NGHTTP2_MAX_PAYLOADLEN == + item->frame.window_update.window_size_increment); + + nghttp2_session_del(session); +} + +void test_nghttp2_session_flow_control_data_with_padding_recv(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + uint8_t data[1024]; + nghttp2_frame_hd hd; + nghttp2_stream *stream; + nghttp2_option *option; + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.send_callback = null_send_callback; + + nghttp2_option_new(&option); + /* Disable auto window update so that we can check padding is + consumed automatically */ + nghttp2_option_set_no_auto_window_update(option, 1); + + /* Initial window size to 64KiB - 1*/ + nghttp2_session_client_new2(&session, &callbacks, NULL, option); + + nghttp2_option_del(option); + + stream = nghttp2_session_open_stream(session, 1, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, NGHTTP2_STREAM_OPENED, + NULL); + + /* Create DATA frame */ + memset(data, 0, sizeof(data)); + nghttp2_frame_hd_init(&hd, 357, NGHTTP2_DATA, + NGHTTP2_FLAG_END_STREAM | NGHTTP2_FLAG_PADDED, 1); + + nghttp2_frame_pack_frame_hd(data, &hd); + /* Set Pad Length field, which itself is padding */ + data[NGHTTP2_FRAME_HDLEN] = 255; + + CU_ASSERT( + (ssize_t)(NGHTTP2_FRAME_HDLEN + hd.length) == + nghttp2_session_mem_recv(session, data, NGHTTP2_FRAME_HDLEN + hd.length)); + + CU_ASSERT((int32_t)hd.length == session->recv_window_size); + CU_ASSERT((int32_t)hd.length == stream->recv_window_size); + CU_ASSERT(256 == session->consumed_size); + CU_ASSERT(256 == stream->consumed_size); + + nghttp2_session_del(session); +} + +void test_nghttp2_session_data_read_temporal_failure(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + my_user_data ud; + nghttp2_data_provider data_prd; + nghttp2_frame frame; + nghttp2_stream *stream; + size_t data_size = 128 * 1024; + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.send_callback = null_send_callback; + callbacks.on_frame_send_callback = on_frame_send_callback; + data_prd.read_callback = fixed_length_data_source_read_callback; + + ud.data_source_length = data_size; + + /* Initial window size is 64KiB - 1 */ + nghttp2_session_client_new(&session, &callbacks, &ud); + nghttp2_submit_request(session, NULL, NULL, 0, &data_prd, NULL); + + /* Sends NGHTTP2_INITIAL_WINDOW_SIZE data, assuming, it is equal to + or smaller than NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE */ + CU_ASSERT(0 == nghttp2_session_send(session)); + CU_ASSERT(data_size - NGHTTP2_INITIAL_WINDOW_SIZE == ud.data_source_length); + + stream = nghttp2_session_get_stream(session, 1); + CU_ASSERT(nghttp2_stream_check_deferred_by_flow_control(stream)); + CU_ASSERT(NGHTTP2_DATA == stream->item->frame.hd.type); + + stream->item->aux_data.data.data_prd.read_callback = + temporal_failure_data_source_read_callback; + + /* Back NGHTTP2_INITIAL_WINDOW_SIZE to both connection-level and + stream-wise window */ + nghttp2_frame_window_update_init(&frame.window_update, NGHTTP2_FLAG_NONE, 1, + NGHTTP2_INITIAL_WINDOW_SIZE); + nghttp2_session_on_window_update_received(session, &frame); + frame.hd.stream_id = 0; + nghttp2_session_on_window_update_received(session, &frame); + nghttp2_frame_window_update_free(&frame.window_update); + + /* Sending data will fail (soft fail) and treated as stream error */ + ud.frame_send_cb_called = 0; + CU_ASSERT(0 == nghttp2_session_send(session)); + CU_ASSERT(data_size - NGHTTP2_INITIAL_WINDOW_SIZE == ud.data_source_length); + + CU_ASSERT(1 == ud.frame_send_cb_called); + CU_ASSERT(NGHTTP2_RST_STREAM == ud.sent_frame_type); + + data_prd.read_callback = fail_data_source_read_callback; + nghttp2_submit_request(session, NULL, NULL, 0, &data_prd, NULL); + /* Sending data will fail (hard fail) and session tear down */ + CU_ASSERT(NGHTTP2_ERR_CALLBACK_FAILURE == nghttp2_session_send(session)); + + nghttp2_session_del(session); +} + +void test_nghttp2_session_on_stream_close(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + my_user_data user_data; + nghttp2_stream *stream; + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.on_stream_close_callback = on_stream_close_callback; + user_data.stream_close_cb_called = 0; + + nghttp2_session_client_new(&session, &callbacks, &user_data); + stream = nghttp2_session_open_stream(session, 1, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, NGHTTP2_STREAM_OPENED, + &user_data); + CU_ASSERT(stream != NULL); + CU_ASSERT(nghttp2_session_close_stream(session, 1, NGHTTP2_NO_ERROR) == 0); + CU_ASSERT(user_data.stream_close_cb_called == 1); + nghttp2_session_del(session); +} + +void test_nghttp2_session_on_ctrl_not_send(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + my_user_data user_data; + nghttp2_stream *stream; + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.on_frame_not_send_callback = on_frame_not_send_callback; + callbacks.send_callback = null_send_callback; + user_data.frame_not_send_cb_called = 0; + user_data.not_sent_frame_type = 0; + user_data.not_sent_error = 0; + + nghttp2_session_server_new(&session, &callbacks, &user_data); + stream = nghttp2_session_open_stream(session, 1, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, + NGHTTP2_STREAM_OPENING, &user_data); + + /* Check response HEADERS */ + /* Send bogus stream ID */ + CU_ASSERT(0 == nghttp2_submit_headers(session, NGHTTP2_FLAG_END_STREAM, 3, + NULL, NULL, 0, NULL)); + CU_ASSERT(0 == nghttp2_session_send(session)); + CU_ASSERT(1 == user_data.frame_not_send_cb_called); + CU_ASSERT(NGHTTP2_HEADERS == user_data.not_sent_frame_type); + CU_ASSERT(NGHTTP2_ERR_STREAM_CLOSED == user_data.not_sent_error); + + user_data.frame_not_send_cb_called = 0; + /* Shutdown transmission */ + stream->shut_flags |= NGHTTP2_SHUT_WR; + CU_ASSERT(0 == nghttp2_submit_headers(session, NGHTTP2_FLAG_END_STREAM, 1, + NULL, NULL, 0, NULL)); + CU_ASSERT(0 == nghttp2_session_send(session)); + CU_ASSERT(1 == user_data.frame_not_send_cb_called); + CU_ASSERT(NGHTTP2_HEADERS == user_data.not_sent_frame_type); + CU_ASSERT(NGHTTP2_ERR_STREAM_SHUT_WR == user_data.not_sent_error); + + stream->shut_flags = NGHTTP2_SHUT_NONE; + user_data.frame_not_send_cb_called = 0; + /* Queue RST_STREAM */ + CU_ASSERT(0 == nghttp2_submit_headers(session, NGHTTP2_FLAG_END_STREAM, 1, + NULL, NULL, 0, NULL)); + CU_ASSERT(0 == nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, 1, + NGHTTP2_INTERNAL_ERROR)); + CU_ASSERT(0 == nghttp2_session_send(session)); + CU_ASSERT(1 == user_data.frame_not_send_cb_called); + CU_ASSERT(NGHTTP2_HEADERS == user_data.not_sent_frame_type); + CU_ASSERT(NGHTTP2_ERR_STREAM_CLOSING == user_data.not_sent_error); + + nghttp2_session_del(session); + + /* Check request HEADERS */ + user_data.frame_not_send_cb_called = 0; + CU_ASSERT(nghttp2_session_client_new(&session, &callbacks, &user_data) == 0); + /* Maximum Stream ID is reached */ + session->next_stream_id = (1u << 31) + 1; + CU_ASSERT(NGHTTP2_ERR_STREAM_ID_NOT_AVAILABLE == + nghttp2_submit_headers(session, NGHTTP2_FLAG_END_STREAM, -1, NULL, + NULL, 0, NULL)); + + user_data.frame_not_send_cb_called = 0; + /* GOAWAY received */ + session->goaway_flags |= NGHTTP2_GOAWAY_RECV; + session->next_stream_id = 9; + + CU_ASSERT(0 < nghttp2_submit_headers(session, NGHTTP2_FLAG_END_STREAM, -1, + NULL, NULL, 0, NULL)); + CU_ASSERT(0 == nghttp2_session_send(session)); + CU_ASSERT(1 == user_data.frame_not_send_cb_called); + CU_ASSERT(NGHTTP2_HEADERS == user_data.not_sent_frame_type); + CU_ASSERT(NGHTTP2_ERR_START_STREAM_NOT_ALLOWED == user_data.not_sent_error); + + nghttp2_session_del(session); +} + +void test_nghttp2_session_get_outbound_queue_size(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + CU_ASSERT(0 == nghttp2_session_client_new(&session, &callbacks, NULL)); + CU_ASSERT(0 == nghttp2_session_get_outbound_queue_size(session)); + + CU_ASSERT(0 == nghttp2_submit_ping(session, NGHTTP2_FLAG_NONE, NULL)); + CU_ASSERT(1 == nghttp2_session_get_outbound_queue_size(session)); + + CU_ASSERT(0 == nghttp2_submit_goaway(session, NGHTTP2_FLAG_NONE, 2, + NGHTTP2_NO_ERROR, NULL, 0)); + CU_ASSERT(2 == nghttp2_session_get_outbound_queue_size(session)); + + nghttp2_session_del(session); +} + +void test_nghttp2_session_get_effective_local_window_size(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + nghttp2_stream *stream; + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + CU_ASSERT(0 == nghttp2_session_client_new(&session, &callbacks, NULL)); + + stream = nghttp2_session_open_stream(session, 1, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, NGHTTP2_STREAM_OPENED, + NULL); + + CU_ASSERT(NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE == + nghttp2_session_get_effective_local_window_size(session)); + CU_ASSERT(0 == nghttp2_session_get_effective_recv_data_length(session)); + + CU_ASSERT(NGHTTP2_INITIAL_WINDOW_SIZE == + nghttp2_session_get_stream_effective_local_window_size(session, 1)); + CU_ASSERT(0 == + nghttp2_session_get_stream_effective_recv_data_length(session, 1)); + + /* Check connection flow control */ + session->recv_window_size = 100; + nghttp2_submit_window_update(session, NGHTTP2_FLAG_NONE, 0, 1100); + + CU_ASSERT(NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE + 1000 == + nghttp2_session_get_effective_local_window_size(session)); + CU_ASSERT(0 == nghttp2_session_get_effective_recv_data_length(session)); + + nghttp2_submit_window_update(session, NGHTTP2_FLAG_NONE, 0, -50); + /* Now session->recv_window_size = -50 */ + CU_ASSERT(NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE + 950 == + nghttp2_session_get_effective_local_window_size(session)); + CU_ASSERT(0 == nghttp2_session_get_effective_recv_data_length(session)); + + session->recv_window_size += 50; + /* Now session->recv_window_size = 0 */ + nghttp2_submit_window_update(session, NGHTTP2_FLAG_NONE, 0, 100); + CU_ASSERT(NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE + 1050 == + nghttp2_session_get_effective_local_window_size(session)); + CU_ASSERT(50 == nghttp2_session_get_effective_recv_data_length(session)); + + /* Check stream flow control */ + stream->recv_window_size = 100; + nghttp2_submit_window_update(session, NGHTTP2_FLAG_NONE, 1, 1100); + + CU_ASSERT(NGHTTP2_INITIAL_WINDOW_SIZE + 1000 == + nghttp2_session_get_stream_effective_local_window_size(session, 1)); + CU_ASSERT(0 == + nghttp2_session_get_stream_effective_recv_data_length(session, 1)); + + nghttp2_submit_window_update(session, NGHTTP2_FLAG_NONE, 1, -50); + /* Now stream->recv_window_size = -50 */ + CU_ASSERT(NGHTTP2_INITIAL_WINDOW_SIZE + 950 == + nghttp2_session_get_stream_effective_local_window_size(session, 1)); + CU_ASSERT(0 == + nghttp2_session_get_stream_effective_recv_data_length(session, 1)); + + stream->recv_window_size += 50; + /* Now stream->recv_window_size = 0 */ + nghttp2_submit_window_update(session, NGHTTP2_FLAG_NONE, 1, 100); + CU_ASSERT(NGHTTP2_INITIAL_WINDOW_SIZE + 1050 == + nghttp2_session_get_stream_effective_local_window_size(session, 1)); + CU_ASSERT(50 == + nghttp2_session_get_stream_effective_recv_data_length(session, 1)); + + nghttp2_session_del(session); +} + +void test_nghttp2_session_set_option(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + nghttp2_option *option; + + nghttp2_option_new(&option); + + nghttp2_option_set_no_auto_window_update(option, 1); + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + nghttp2_session_client_new2(&session, &callbacks, NULL, option); + + CU_ASSERT(session->opt_flags & NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE); + + nghttp2_session_del(session); + + nghttp2_option_set_peer_max_concurrent_streams(option, 100); + + nghttp2_session_client_new2(&session, &callbacks, NULL, option); + + CU_ASSERT(100 == session->remote_settings.max_concurrent_streams); + nghttp2_session_del(session); + + nghttp2_option_del(option); +} + +void test_nghttp2_session_data_backoff_by_high_pri_frame(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + my_user_data ud; + nghttp2_data_provider data_prd; + nghttp2_stream *stream; + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.send_callback = block_count_send_callback; + callbacks.on_frame_send_callback = on_frame_send_callback; + data_prd.read_callback = fixed_length_data_source_read_callback; + + ud.frame_send_cb_called = 0; + ud.data_source_length = NGHTTP2_DATA_PAYLOADLEN * 4; + + nghttp2_session_client_new(&session, &callbacks, &ud); + nghttp2_submit_request(session, NULL, NULL, 0, &data_prd, NULL); + + session->remote_window_size = 1 << 20; + + ud.block_count = 2; + /* Sends request HEADERS + DATA[0] */ + CU_ASSERT(0 == nghttp2_session_send(session)); + + stream = nghttp2_session_get_stream(session, 1); + stream->remote_window_size = 1 << 20; + + CU_ASSERT(NGHTTP2_DATA == ud.sent_frame_type); + /* data for DATA[1] is read from data_prd but it is not sent */ + CU_ASSERT(ud.data_source_length == NGHTTP2_DATA_PAYLOADLEN * 2); + + nghttp2_submit_ping(session, NGHTTP2_FLAG_NONE, NULL); + ud.block_count = 2; + /* Sends DATA[1] + PING, PING is interleaved in DATA sequence */ + CU_ASSERT(0 == nghttp2_session_send(session)); + CU_ASSERT(NGHTTP2_PING == ud.sent_frame_type); + /* data for DATA[2] is read from data_prd but it is not sent */ + CU_ASSERT(ud.data_source_length == NGHTTP2_DATA_PAYLOADLEN); + + ud.block_count = 2; + /* Sends DATA[2..3] */ + CU_ASSERT(0 == nghttp2_session_send(session)); + + CU_ASSERT(stream->shut_flags & NGHTTP2_SHUT_WR); + + nghttp2_session_del(session); +} + +static void check_session_recv_data_with_padding(nghttp2_bufs *bufs, + size_t datalen, + nghttp2_mem *mem) { + nghttp2_session *session; + my_user_data ud; + nghttp2_session_callbacks callbacks; + uint8_t *in; + size_t inlen; + + memset(&callbacks, 0, sizeof(callbacks)); + callbacks.on_frame_recv_callback = on_frame_recv_callback; + callbacks.on_data_chunk_recv_callback = on_data_chunk_recv_callback; + nghttp2_session_server_new(&session, &callbacks, &ud); + + nghttp2_session_open_stream(session, 1, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, NGHTTP2_STREAM_OPENING, NULL); + + inlen = nghttp2_bufs_remove(bufs, &in); + + ud.frame_recv_cb_called = 0; + ud.data_chunk_len = 0; + + CU_ASSERT((ssize_t)inlen == nghttp2_session_mem_recv(session, in, inlen)); + + CU_ASSERT(1 == ud.frame_recv_cb_called); + CU_ASSERT(datalen == ud.data_chunk_len); + + mem->free(in, NULL); + nghttp2_session_del(session); +} + +void test_nghttp2_session_pack_data_with_padding(void) { + nghttp2_session *session; + my_user_data ud; + nghttp2_session_callbacks callbacks; + nghttp2_data_provider data_prd; + nghttp2_frame *frame; + size_t datalen = 55; + nghttp2_mem *mem; + + mem = nghttp2_mem_default(); + + memset(&callbacks, 0, sizeof(callbacks)); + callbacks.send_callback = block_count_send_callback; + callbacks.on_frame_send_callback = on_frame_send_callback; + callbacks.select_padding_callback = select_padding_callback; + + data_prd.read_callback = fixed_length_data_source_read_callback; + + nghttp2_session_client_new(&session, &callbacks, &ud); + + ud.padlen = 63; + + nghttp2_submit_request(session, NULL, NULL, 0, &data_prd, NULL); + ud.block_count = 1; + ud.data_source_length = datalen; + /* Sends HEADERS */ + CU_ASSERT(0 == nghttp2_session_send(session)); + CU_ASSERT(NGHTTP2_HEADERS == ud.sent_frame_type); + + frame = &session->aob.item->frame; + + CU_ASSERT(ud.padlen == frame->data.padlen); + CU_ASSERT(frame->hd.flags & NGHTTP2_FLAG_PADDED); + + /* Check reception of this DATA frame */ + check_session_recv_data_with_padding(&session->aob.framebufs, datalen, mem); + + nghttp2_session_del(session); +} + +void test_nghttp2_session_pack_headers_with_padding(void) { + nghttp2_session *session, *sv_session; + accumulator acc; + my_user_data ud; + nghttp2_session_callbacks callbacks; + + memset(&callbacks, 0, sizeof(callbacks)); + callbacks.send_callback = accumulator_send_callback; + callbacks.on_frame_send_callback = on_frame_send_callback; + callbacks.select_padding_callback = select_padding_callback; + callbacks.on_frame_recv_callback = on_frame_recv_callback; + + acc.length = 0; + ud.acc = &acc; + + nghttp2_session_client_new(&session, &callbacks, &ud); + nghttp2_session_server_new(&sv_session, &callbacks, &ud); + + ud.padlen = 163; + + CU_ASSERT(1 == nghttp2_submit_request(session, NULL, reqnv, ARRLEN(reqnv), + NULL, NULL)); + CU_ASSERT(0 == nghttp2_session_send(session)); + + CU_ASSERT(acc.length < NGHTTP2_MAX_PAYLOADLEN); + ud.frame_recv_cb_called = 0; + CU_ASSERT((ssize_t)acc.length == + nghttp2_session_mem_recv(sv_session, acc.buf, acc.length)); + CU_ASSERT(1 == ud.frame_recv_cb_called); + CU_ASSERT(NULL == nghttp2_session_get_next_ob_item(sv_session)); + + nghttp2_session_del(sv_session); + nghttp2_session_del(session); +} + +void test_nghttp2_pack_settings_payload(void) { + nghttp2_settings_entry iv[2]; + uint8_t buf[64]; + ssize_t len; + nghttp2_settings_entry *resiv; + size_t resniv; + nghttp2_mem *mem; + + mem = nghttp2_mem_default(); + + iv[0].settings_id = NGHTTP2_SETTINGS_HEADER_TABLE_SIZE; + iv[0].value = 1023; + iv[1].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE; + iv[1].value = 4095; + + len = nghttp2_pack_settings_payload(buf, sizeof(buf), iv, 2); + CU_ASSERT(2 * NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH == len); + CU_ASSERT(0 == nghttp2_frame_unpack_settings_payload2(&resiv, &resniv, buf, + len, mem)); + CU_ASSERT(2 == resniv); + CU_ASSERT(NGHTTP2_SETTINGS_HEADER_TABLE_SIZE == resiv[0].settings_id); + CU_ASSERT(1023 == resiv[0].value); + CU_ASSERT(NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE == resiv[1].settings_id); + CU_ASSERT(4095 == resiv[1].value); + + mem->free(resiv, NULL); + + len = nghttp2_pack_settings_payload(buf, 9 /* too small */, iv, 2); + CU_ASSERT(NGHTTP2_ERR_INSUFF_BUFSIZE == len); +} + +#define check_stream_dep_sib(STREAM, DEP_PREV, DEP_NEXT, SIB_PREV, SIB_NEXT) \ + do { \ + CU_ASSERT(DEP_PREV == STREAM->dep_prev); \ + CU_ASSERT(DEP_NEXT == STREAM->dep_next); \ + CU_ASSERT(SIB_PREV == STREAM->sib_prev); \ + CU_ASSERT(SIB_NEXT == STREAM->sib_next); \ + } while (0) + +/* nghttp2_stream_dep_add() and its families functions should be + tested in nghttp2_stream_test.c, but it is easier to use + nghttp2_session_open_stream(). Therefore, we test them here. */ +void test_nghttp2_session_stream_dep_add(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + nghttp2_stream *a, *b, *c, *d, *e; + + memset(&callbacks, 0, sizeof(callbacks)); + + nghttp2_session_server_new(&session, &callbacks, NULL); + + a = open_stream(session, 1); + + c = open_stream_with_dep(session, 5, a); + b = open_stream_with_dep(session, 3, a); + d = open_stream_with_dep(session, 7, c); + + /* a + * | + * b--c + * | + * d + */ + + CU_ASSERT(4 == a->num_substreams); + CU_ASSERT(1 == b->num_substreams); + CU_ASSERT(2 == c->num_substreams); + CU_ASSERT(1 == d->num_substreams); + + CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT * 2 == a->sum_dep_weight); + CU_ASSERT(0 == b->sum_dep_weight); + CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT == c->sum_dep_weight); + CU_ASSERT(0 == d->sum_dep_weight); + + check_stream_dep_sib(a, NULL, b, NULL, NULL); + check_stream_dep_sib(b, a, NULL, NULL, c); + check_stream_dep_sib(c, NULL, d, b, NULL); + check_stream_dep_sib(d, c, NULL, NULL, NULL); + + CU_ASSERT(4 == session->roots.num_streams); + CU_ASSERT(a == session->roots.head); + CU_ASSERT(NULL == a->root_next); + + e = open_stream_with_dep_excl(session, 9, a); + + /* a + * | + * e + * | + * b--c + * | + * d + */ + + CU_ASSERT(5 == a->num_substreams); + CU_ASSERT(4 == e->num_substreams); + CU_ASSERT(1 == b->num_substreams); + CU_ASSERT(2 == c->num_substreams); + CU_ASSERT(1 == d->num_substreams); + + CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT == a->sum_dep_weight); + CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT * 2 == e->sum_dep_weight); + CU_ASSERT(0 == b->sum_dep_weight); + CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT == c->sum_dep_weight); + CU_ASSERT(0 == d->sum_dep_weight); + + check_stream_dep_sib(a, NULL, e, NULL, NULL); + check_stream_dep_sib(e, a, b, NULL, NULL); + check_stream_dep_sib(b, e, NULL, NULL, c); + check_stream_dep_sib(c, NULL, d, b, NULL); + check_stream_dep_sib(d, c, NULL, NULL, NULL); + + CU_ASSERT(5 == session->roots.num_streams); + CU_ASSERT(a == session->roots.head); + CU_ASSERT(NULL == a->root_next); + + nghttp2_session_del(session); +} + +void test_nghttp2_session_stream_dep_remove(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + nghttp2_stream *a, *b, *c, *d, *e, *f; + + memset(&callbacks, 0, sizeof(callbacks)); + + /* Remove root */ + nghttp2_session_server_new(&session, &callbacks, NULL); + + a = open_stream(session, 1); + b = open_stream_with_dep(session, 3, a); + c = open_stream_with_dep(session, 5, a); + d = open_stream_with_dep(session, 7, c); + + /* a + * | + * c--b + * | + * d + */ + + nghttp2_stream_dep_remove(a); + + /* becomes: + * b c + * | + * d + */ + + CU_ASSERT(1 == a->num_substreams); + CU_ASSERT(1 == b->num_substreams); + CU_ASSERT(2 == c->num_substreams); + CU_ASSERT(1 == d->num_substreams); + + CU_ASSERT(0 == a->sum_dep_weight); + CU_ASSERT(0 == b->sum_dep_weight); + CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT == c->sum_dep_weight); + CU_ASSERT(0 == d->sum_dep_weight); + + check_stream_dep_sib(a, NULL, NULL, NULL, NULL); + check_stream_dep_sib(b, NULL, NULL, NULL, NULL); + check_stream_dep_sib(c, NULL, d, NULL, NULL); + check_stream_dep_sib(d, c, NULL, NULL, NULL); + + CU_ASSERT(3 == session->roots.num_streams); + CU_ASSERT(b == session->roots.head); + CU_ASSERT(c == b->root_next); + CU_ASSERT(NULL == c->root_next); + + nghttp2_session_del(session); + + /* Remove left most stream */ + nghttp2_session_server_new(&session, &callbacks, NULL); + + a = open_stream(session, 1); + b = open_stream_with_dep(session, 3, a); + c = open_stream_with_dep(session, 5, a); + d = open_stream_with_dep(session, 7, c); + + /* a + * | + * c--b + * | + * d + */ + + nghttp2_stream_dep_remove(b); + + /* becomes: + * a + * | + * c + * | + * d + */ + + CU_ASSERT(3 == a->num_substreams); + CU_ASSERT(1 == b->num_substreams); + CU_ASSERT(2 == c->num_substreams); + CU_ASSERT(1 == d->num_substreams); + + CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT == a->sum_dep_weight); + CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT == c->sum_dep_weight); + CU_ASSERT(0 == d->sum_dep_weight); + CU_ASSERT(0 == b->sum_dep_weight); + + check_stream_dep_sib(a, NULL, c, NULL, NULL); + check_stream_dep_sib(b, NULL, NULL, NULL, NULL); + check_stream_dep_sib(c, a, d, NULL, NULL); + check_stream_dep_sib(d, c, NULL, NULL, NULL); + + CU_ASSERT(3 == session->roots.num_streams); + CU_ASSERT(a == session->roots.head); + CU_ASSERT(NULL == a->root_next); + + nghttp2_session_del(session); + + /* Remove right most stream */ + nghttp2_session_server_new(&session, &callbacks, NULL); + + a = open_stream(session, 1); + b = open_stream_with_dep(session, 3, a); + c = open_stream_with_dep(session, 5, a); + d = open_stream_with_dep(session, 7, c); + + /* a + * | + * c--b + * | + * d + */ + + nghttp2_stream_dep_remove(c); + + /* becomes: + * a + * | + * d--b + */ + + CU_ASSERT(3 == a->num_substreams); + CU_ASSERT(1 == b->num_substreams); + CU_ASSERT(1 == c->num_substreams); + CU_ASSERT(1 == d->num_substreams); + + CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT * 2 == a->sum_dep_weight); + CU_ASSERT(0 == b->sum_dep_weight); + CU_ASSERT(0 == d->sum_dep_weight); + CU_ASSERT(0 == c->sum_dep_weight); + + check_stream_dep_sib(a, NULL, d, NULL, NULL); + check_stream_dep_sib(b, NULL, NULL, d, NULL); + check_stream_dep_sib(c, NULL, NULL, NULL, NULL); + check_stream_dep_sib(d, a, NULL, NULL, b); + + nghttp2_session_del(session); + + /* Remove middle stream */ + nghttp2_session_server_new(&session, &callbacks, NULL); + + a = open_stream(session, 1); + b = open_stream_with_dep(session, 3, a); + c = open_stream_with_dep(session, 5, a); + d = open_stream_with_dep(session, 7, a); + e = open_stream_with_dep(session, 9, c); + f = open_stream_with_dep(session, 11, c); + + /* a + * | + * d--c--b + * | + * f--e + */ + + CU_ASSERT(6 == a->num_substreams); + CU_ASSERT(1 == b->num_substreams); + CU_ASSERT(3 == c->num_substreams); + CU_ASSERT(1 == d->num_substreams); + CU_ASSERT(1 == e->num_substreams); + CU_ASSERT(1 == f->num_substreams); + + CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT * 3 == a->sum_dep_weight); + CU_ASSERT(0 == b->sum_dep_weight); + CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT * 2 == c->sum_dep_weight); + CU_ASSERT(0 == d->sum_dep_weight); + CU_ASSERT(0 == e->sum_dep_weight); + CU_ASSERT(0 == f->sum_dep_weight); + + nghttp2_stream_dep_remove(c); + + /* becomes: + * a + * | + * d--f--e--b + */ + + CU_ASSERT(5 == a->num_substreams); + CU_ASSERT(1 == b->num_substreams); + CU_ASSERT(1 == c->num_substreams); + CU_ASSERT(1 == d->num_substreams); + CU_ASSERT(1 == e->num_substreams); + CU_ASSERT(1 == f->num_substreams); + + /* c's weight 16 is distributed evenly to e and f. Each weight of e + and f becomes 8. */ + CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT * 2 + 8 * 2 == a->sum_dep_weight); + CU_ASSERT(0 == b->sum_dep_weight); + CU_ASSERT(0 == c->sum_dep_weight); + CU_ASSERT(0 == d->sum_dep_weight); + CU_ASSERT(0 == e->sum_dep_weight); + CU_ASSERT(0 == f->sum_dep_weight); + + check_stream_dep_sib(a, NULL, d, NULL, NULL); + check_stream_dep_sib(b, NULL, NULL, e, NULL); + check_stream_dep_sib(c, NULL, NULL, NULL, NULL); + check_stream_dep_sib(e, NULL, NULL, f, b); + check_stream_dep_sib(f, NULL, NULL, d, e); + check_stream_dep_sib(d, a, NULL, NULL, f); + + nghttp2_session_del(session); +} + +void test_nghttp2_session_stream_dep_add_subtree(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + nghttp2_stream *a, *b, *c, *d, *e, *f; + + memset(&callbacks, 0, sizeof(callbacks)); + + /* dep_stream has dep_next */ + nghttp2_session_server_new(&session, &callbacks, NULL); + + a = open_stream(session, 1); + b = open_stream_with_dep(session, 3, a); + c = open_stream_with_dep(session, 5, a); + d = open_stream_with_dep(session, 7, c); + + e = open_stream(session, 9); + f = open_stream_with_dep(session, 11, e); + + /* a e + * | | + * c--b f + * | + * d + */ + + nghttp2_stream_dep_add_subtree(a, e, session); + + /* becomes + * a + * | + * e--c--b + * | | + * f d + */ + + CU_ASSERT(6 == a->num_substreams); + CU_ASSERT(1 == b->num_substreams); + CU_ASSERT(2 == c->num_substreams); + CU_ASSERT(1 == d->num_substreams); + CU_ASSERT(2 == e->num_substreams); + CU_ASSERT(1 == f->num_substreams); + + CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT * 3 == a->sum_dep_weight); + CU_ASSERT(0 == b->sum_dep_weight); + CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT == c->sum_dep_weight); + CU_ASSERT(0 == d->sum_dep_weight); + CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT == e->sum_dep_weight); + CU_ASSERT(0 == f->sum_dep_weight); + + check_stream_dep_sib(a, NULL, e, NULL, NULL); + check_stream_dep_sib(b, NULL, NULL, c, NULL); + check_stream_dep_sib(c, NULL, d, e, b); + check_stream_dep_sib(d, c, NULL, NULL, NULL); + check_stream_dep_sib(e, a, f, NULL, c); + check_stream_dep_sib(f, e, NULL, NULL, NULL); + + nghttp2_session_del(session); + + /* dep_stream has dep_next and now we insert subtree */ + nghttp2_session_server_new(&session, &callbacks, NULL); + + a = open_stream(session, 1); + b = open_stream_with_dep(session, 3, a); + c = open_stream_with_dep(session, 5, a); + d = open_stream_with_dep(session, 7, c); + + e = open_stream(session, 9); + f = open_stream_with_dep(session, 11, e); + + /* a e + * | | + * c--b f + * | + * d + */ + + nghttp2_stream_dep_insert_subtree(a, e, session); + + /* becomes + * a + * | + * e + * | + * f--c--b + * | + * d + */ + + CU_ASSERT(6 == a->num_substreams); + CU_ASSERT(1 == b->num_substreams); + CU_ASSERT(2 == c->num_substreams); + CU_ASSERT(1 == d->num_substreams); + CU_ASSERT(5 == e->num_substreams); + CU_ASSERT(1 == f->num_substreams); + + CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT == a->sum_dep_weight); + CU_ASSERT(0 == b->sum_dep_weight); + CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT == c->sum_dep_weight); + CU_ASSERT(0 == d->sum_dep_weight); + CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT * 3 == e->sum_dep_weight); + CU_ASSERT(0 == f->sum_dep_weight); + + check_stream_dep_sib(a, NULL, e, NULL, NULL); + check_stream_dep_sib(e, a, f, NULL, NULL); + check_stream_dep_sib(f, e, NULL, NULL, c); + check_stream_dep_sib(b, NULL, NULL, c, NULL); + check_stream_dep_sib(c, NULL, d, f, b); + check_stream_dep_sib(d, c, NULL, NULL, NULL); + + nghttp2_session_del(session); +} + +void test_nghttp2_session_stream_dep_remove_subtree(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + nghttp2_stream *a, *b, *c, *d, *e; + + memset(&callbacks, 0, sizeof(callbacks)); + + /* Remove left most stream */ + nghttp2_session_server_new(&session, &callbacks, NULL); + + a = open_stream(session, 1); + b = open_stream_with_dep(session, 3, a); + c = open_stream_with_dep(session, 5, a); + d = open_stream_with_dep(session, 7, c); + + /* a + * | + * c--b + * | + * d + */ + + nghttp2_stream_dep_remove_subtree(c); + + /* becomes + * a c + * | | + * b d + */ + + CU_ASSERT(2 == a->num_substreams); + CU_ASSERT(1 == b->num_substreams); + CU_ASSERT(2 == c->num_substreams); + CU_ASSERT(1 == d->num_substreams); + + CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT == a->sum_dep_weight); + CU_ASSERT(0 == b->sum_dep_weight); + CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT == c->sum_dep_weight); + CU_ASSERT(0 == d->sum_dep_weight); + + check_stream_dep_sib(a, NULL, b, NULL, NULL); + check_stream_dep_sib(b, a, NULL, NULL, NULL); + check_stream_dep_sib(c, NULL, d, NULL, NULL); + check_stream_dep_sib(d, c, NULL, NULL, NULL); + + nghttp2_session_del(session); + + /* Remove right most stream */ + nghttp2_session_server_new(&session, &callbacks, NULL); + + a = open_stream(session, 1); + b = open_stream_with_dep(session, 3, a); + c = open_stream_with_dep(session, 5, a); + d = open_stream_with_dep(session, 7, c); + + /* a + * | + * c--b + * | + * d + */ + + nghttp2_stream_dep_remove_subtree(b); + + /* becomes + * a b + * | + * c + * | + * d + */ + + CU_ASSERT(3 == a->num_substreams); + CU_ASSERT(1 == b->num_substreams); + CU_ASSERT(2 == c->num_substreams); + CU_ASSERT(1 == d->num_substreams); + + CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT == a->sum_dep_weight); + CU_ASSERT(0 == b->sum_dep_weight); + CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT == c->sum_dep_weight); + CU_ASSERT(0 == d->sum_dep_weight); + + check_stream_dep_sib(a, NULL, c, NULL, NULL); + check_stream_dep_sib(c, a, d, NULL, NULL); + check_stream_dep_sib(d, c, NULL, NULL, NULL); + check_stream_dep_sib(b, NULL, NULL, NULL, NULL); + + nghttp2_session_del(session); + + /* Remove middle stream */ + nghttp2_session_server_new(&session, &callbacks, NULL); + + a = open_stream(session, 1); + e = open_stream_with_dep(session, 9, a); + c = open_stream_with_dep(session, 5, a); + b = open_stream_with_dep(session, 3, a); + d = open_stream_with_dep(session, 7, c); + + /* a + * | + * b--c--e + * | + * d + */ + + nghttp2_stream_dep_remove_subtree(c); + + /* becomes + * a c + * | | + * b--e d + */ + + CU_ASSERT(3 == a->num_substreams); + CU_ASSERT(1 == b->num_substreams); + CU_ASSERT(1 == e->num_substreams); + CU_ASSERT(2 == c->num_substreams); + CU_ASSERT(1 == d->num_substreams); + + CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT * 2 == a->sum_dep_weight); + CU_ASSERT(0 == b->sum_dep_weight); + CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT == c->sum_dep_weight); + CU_ASSERT(0 == d->sum_dep_weight); + CU_ASSERT(0 == e->sum_dep_weight); + + check_stream_dep_sib(a, NULL, b, NULL, NULL); + check_stream_dep_sib(b, a, NULL, NULL, e); + check_stream_dep_sib(e, NULL, NULL, b, NULL); + check_stream_dep_sib(c, NULL, d, NULL, NULL); + check_stream_dep_sib(d, c, NULL, NULL, NULL); + + nghttp2_session_del(session); +} + +void test_nghttp2_session_stream_dep_all_your_stream_are_belong_to_us(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + nghttp2_stream *a, *b, *c, *d; + + memset(&callbacks, 0, sizeof(callbacks)); + + nghttp2_session_server_new(&session, &callbacks, NULL); + + a = open_stream(session, 1); + b = open_stream_with_dep(session, 3, a); + + c = open_stream(session, 5); + + /* a c + * | + * b + */ + + nghttp2_stream_dep_remove_subtree(c); + CU_ASSERT(0 == + nghttp2_stream_dep_all_your_stream_are_belong_to_us(c, session)); + + /* + * c + * | + * a + * | + * b + */ + + CU_ASSERT(3 == c->num_substreams); + CU_ASSERT(2 == a->num_substreams); + CU_ASSERT(1 == b->num_substreams); + + CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT == c->sum_dep_weight); + CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT == a->sum_dep_weight); + CU_ASSERT(0 == b->sum_dep_weight); + + check_stream_dep_sib(c, NULL, a, NULL, NULL); + check_stream_dep_sib(a, c, b, NULL, NULL); + check_stream_dep_sib(b, a, NULL, NULL, NULL); + + nghttp2_session_del(session); + + nghttp2_session_server_new(&session, &callbacks, NULL); + + a = open_stream(session, 1); + + b = open_stream(session, 3); + + c = open_stream(session, 5); + + /* + * a b c + */ + + nghttp2_stream_dep_remove_subtree(c); + CU_ASSERT(0 == + nghttp2_stream_dep_all_your_stream_are_belong_to_us(c, session)); + + /* + * c + * | + * b--a + */ + + CU_ASSERT(3 == c->num_substreams); + CU_ASSERT(1 == a->num_substreams); + CU_ASSERT(1 == b->num_substreams); + + CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT * 2 == c->sum_dep_weight); + CU_ASSERT(0 == b->sum_dep_weight); + CU_ASSERT(0 == a->sum_dep_weight); + + check_stream_dep_sib(c, NULL, b, NULL, NULL); + check_stream_dep_sib(b, c, NULL, NULL, a); + check_stream_dep_sib(a, NULL, NULL, b, NULL); + + nghttp2_session_del(session); + + nghttp2_session_server_new(&session, &callbacks, NULL); + + a = open_stream(session, 1); + b = open_stream_with_dep(session, 3, a); + + c = open_stream(session, 5); + d = open_stream_with_dep(session, 7, c); + + /* a c + * | | + * b d + */ + + nghttp2_stream_dep_remove_subtree(c); + CU_ASSERT(0 == + nghttp2_stream_dep_all_your_stream_are_belong_to_us(c, session)); + + /* + * c + * | + * a--d + * | + * b + */ + + CU_ASSERT(4 == c->num_substreams); + CU_ASSERT(1 == d->num_substreams); + CU_ASSERT(2 == a->num_substreams); + CU_ASSERT(1 == b->num_substreams); + + CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT * 2 == c->sum_dep_weight); + CU_ASSERT(0 == d->sum_dep_weight); + CU_ASSERT(NGHTTP2_DEFAULT_WEIGHT == a->sum_dep_weight); + CU_ASSERT(0 == b->sum_dep_weight); + + check_stream_dep_sib(c, NULL, a, NULL, NULL); + check_stream_dep_sib(d, NULL, NULL, a, NULL); + check_stream_dep_sib(a, c, b, NULL, d); + check_stream_dep_sib(b, a, NULL, NULL, NULL); + + nghttp2_session_del(session); +} + +void test_nghttp2_session_stream_attach_item(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + nghttp2_stream *a, *b, *c, *d; + nghttp2_outbound_item *da, *db, *dc, *dd; + nghttp2_mem *mem; + + mem = nghttp2_mem_default(); + + memset(&callbacks, 0, sizeof(callbacks)); + + nghttp2_session_server_new(&session, &callbacks, NULL); + + a = open_stream(session, 1); + b = open_stream_with_dep(session, 3, a); + c = open_stream_with_dep(session, 5, a); + d = open_stream_with_dep(session, 7, c); + + /* a + * | + * c--b + * | + * d + */ + + db = create_data_ob_item(mem); + + nghttp2_stream_attach_item(b, db, session); + + CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == a->dpri); + CU_ASSERT(NGHTTP2_STREAM_DPRI_TOP == b->dpri); + CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == c->dpri); + CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == d->dpri); + + CU_ASSERT(16 == b->effective_weight); + + CU_ASSERT(16 == a->sum_norest_weight); + + CU_ASSERT(1 == db->queued); + + dc = create_data_ob_item(mem); + + nghttp2_stream_attach_item(c, dc, session); + + CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == a->dpri); + CU_ASSERT(NGHTTP2_STREAM_DPRI_TOP == b->dpri); + CU_ASSERT(NGHTTP2_STREAM_DPRI_TOP == c->dpri); + CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == d->dpri); + + CU_ASSERT(16 * 16 / 32 == b->effective_weight); + CU_ASSERT(16 * 16 / 32 == c->effective_weight); + + CU_ASSERT(32 == a->sum_norest_weight); + + CU_ASSERT(1 == dc->queued); + + da = create_data_ob_item(mem); + + nghttp2_stream_attach_item(a, da, session); + + CU_ASSERT(NGHTTP2_STREAM_DPRI_TOP == a->dpri); + CU_ASSERT(NGHTTP2_STREAM_DPRI_REST == b->dpri); + CU_ASSERT(NGHTTP2_STREAM_DPRI_REST == c->dpri); + CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == d->dpri); + + CU_ASSERT(16 == a->effective_weight); + + CU_ASSERT(1 == da->queued); + + nghttp2_stream_detach_item(a, session); + + CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == a->dpri); + CU_ASSERT(NGHTTP2_STREAM_DPRI_TOP == b->dpri); + CU_ASSERT(NGHTTP2_STREAM_DPRI_TOP == c->dpri); + CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == d->dpri); + + CU_ASSERT(16 * 16 / 32 == b->effective_weight); + CU_ASSERT(16 * 16 / 32 == c->effective_weight); + + dd = create_data_ob_item(mem); + + nghttp2_stream_attach_item(d, dd, session); + + CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == a->dpri); + CU_ASSERT(NGHTTP2_STREAM_DPRI_TOP == b->dpri); + CU_ASSERT(NGHTTP2_STREAM_DPRI_TOP == c->dpri); + CU_ASSERT(NGHTTP2_STREAM_DPRI_REST == d->dpri); + + CU_ASSERT(16 * 16 / 32 == b->effective_weight); + CU_ASSERT(16 * 16 / 32 == c->effective_weight); + + CU_ASSERT(0 == dd->queued); + + nghttp2_stream_detach_item(c, session); + + CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == a->dpri); + CU_ASSERT(NGHTTP2_STREAM_DPRI_TOP == b->dpri); + CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == c->dpri); + CU_ASSERT(NGHTTP2_STREAM_DPRI_TOP == d->dpri); + + CU_ASSERT(16 * 16 / 32 == b->effective_weight); + CU_ASSERT(16 * 16 / 32 == d->effective_weight); + + CU_ASSERT(1 == dd->queued); + + nghttp2_stream_detach_item(b, session); + + CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == a->dpri); + CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == b->dpri); + CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == c->dpri); + CU_ASSERT(NGHTTP2_STREAM_DPRI_TOP == d->dpri); + + CU_ASSERT(16 * 16 / 16 == d->effective_weight); + + CU_ASSERT(1 == dd->queued); + + nghttp2_session_del(session); +} + +void test_nghttp2_session_stream_attach_item_subtree(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + nghttp2_stream *a, *b, *c, *d, *e, *f; + nghttp2_outbound_item *db, *dd, *de; + nghttp2_mem *mem; + + mem = nghttp2_mem_default(); + + memset(&callbacks, 0, sizeof(callbacks)); + + nghttp2_session_server_new(&session, &callbacks, NULL); + + a = open_stream(session, 1); + b = open_stream_with_dep(session, 3, a); + c = open_stream_with_dep(session, 5, a); + d = open_stream_with_dep(session, 7, c); + + e = open_stream(session, 9); + f = open_stream_with_dep(session, 11, e); + /* + * a e + * | | + * c--b f + * | + * d + */ + + de = create_data_ob_item(mem); + + nghttp2_stream_attach_item(e, de, session); + + db = create_data_ob_item(mem); + + nghttp2_stream_attach_item(b, db, session); + + CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == a->dpri); + CU_ASSERT(NGHTTP2_STREAM_DPRI_TOP == b->dpri); + CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == c->dpri); + CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == d->dpri); + CU_ASSERT(NGHTTP2_STREAM_DPRI_TOP == e->dpri); + CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == f->dpri); + + CU_ASSERT(16 == b->effective_weight); + CU_ASSERT(16 == e->effective_weight); + + /* Insert subtree e under a */ + + nghttp2_stream_dep_remove_subtree(e); + nghttp2_stream_dep_insert_subtree(a, e, session); + + /* + * a + * | + * e + * | + * f--c--b + * | + * d + */ + + CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == a->dpri); + CU_ASSERT(NGHTTP2_STREAM_DPRI_REST == b->dpri); + CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == c->dpri); + CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == d->dpri); + CU_ASSERT(NGHTTP2_STREAM_DPRI_TOP == e->dpri); + CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == f->dpri); + + CU_ASSERT(16 == e->effective_weight); + + /* Remove subtree b */ + + nghttp2_stream_dep_remove_subtree(b); + + nghttp2_stream_dep_make_root(b, session); + + /* + * a b + * | + * e + * | + * f--c + * | + * d + */ + + CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == a->dpri); + CU_ASSERT(NGHTTP2_STREAM_DPRI_TOP == b->dpri); + CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == c->dpri); + CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == d->dpri); + CU_ASSERT(NGHTTP2_STREAM_DPRI_TOP == e->dpri); + CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == f->dpri); + + CU_ASSERT(16 == b->effective_weight); + CU_ASSERT(16 == e->effective_weight); + + /* Remove subtree a */ + + nghttp2_stream_dep_remove_subtree(a); + + nghttp2_stream_dep_make_root(a, session); + + CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == a->dpri); + CU_ASSERT(NGHTTP2_STREAM_DPRI_TOP == b->dpri); + CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == c->dpri); + CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == d->dpri); + CU_ASSERT(NGHTTP2_STREAM_DPRI_TOP == e->dpri); + CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == f->dpri); + + /* Remove subtree c */ + + nghttp2_stream_dep_remove_subtree(c); + + nghttp2_stream_dep_make_root(c, session); + + /* + * a b c + * | | + * e d + * | + * f + */ + + CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == a->dpri); + CU_ASSERT(NGHTTP2_STREAM_DPRI_TOP == b->dpri); + CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == c->dpri); + CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == d->dpri); + CU_ASSERT(NGHTTP2_STREAM_DPRI_TOP == e->dpri); + CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == f->dpri); + + dd = create_data_ob_item(mem); + + nghttp2_stream_attach_item(d, dd, session); + + /* Add subtree c to a */ + + nghttp2_stream_dep_remove_subtree(c); + nghttp2_stream_dep_add_subtree(a, c, session); + + /* + * a b + * | + * c--e + * | | + * d f + */ + + CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == a->dpri); + CU_ASSERT(NGHTTP2_STREAM_DPRI_TOP == b->dpri); + CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == c->dpri); + CU_ASSERT(NGHTTP2_STREAM_DPRI_TOP == d->dpri); + CU_ASSERT(NGHTTP2_STREAM_DPRI_TOP == e->dpri); + CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == f->dpri); + + CU_ASSERT(16 == b->effective_weight); + CU_ASSERT(16 * 16 / 32 == e->effective_weight); + CU_ASSERT(16 * 16 / 32 == e->effective_weight); + + CU_ASSERT(32 == a->sum_norest_weight); + CU_ASSERT(16 == c->sum_norest_weight); + + /* Insert b under a */ + + nghttp2_stream_dep_remove_subtree(b); + nghttp2_stream_dep_insert_subtree(a, b, session); + + /* + * a + * | + * b + * | + * e--c + * | | + * f d + */ + + CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == a->dpri); + CU_ASSERT(NGHTTP2_STREAM_DPRI_TOP == b->dpri); + CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == c->dpri); + CU_ASSERT(NGHTTP2_STREAM_DPRI_REST == d->dpri); + CU_ASSERT(NGHTTP2_STREAM_DPRI_REST == e->dpri); + CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == f->dpri); + + CU_ASSERT(16 == b->effective_weight); + + CU_ASSERT(16 == a->sum_norest_weight); + CU_ASSERT(0 == b->sum_norest_weight); + + /* Remove subtree b */ + + nghttp2_stream_dep_remove_subtree(b); + nghttp2_stream_dep_make_root(b, session); + + /* + * b a + * | + * e--c + * | | + * f d + */ + + CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == a->dpri); + CU_ASSERT(NGHTTP2_STREAM_DPRI_TOP == b->dpri); + CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == c->dpri); + CU_ASSERT(NGHTTP2_STREAM_DPRI_REST == d->dpri); + CU_ASSERT(NGHTTP2_STREAM_DPRI_REST == e->dpri); + CU_ASSERT(NGHTTP2_STREAM_DPRI_NO_ITEM == f->dpri); + + CU_ASSERT(0 == a->sum_norest_weight); + CU_ASSERT(0 == b->sum_norest_weight); + + nghttp2_session_del(session); +} + +void test_nghttp2_session_keep_closed_stream(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + const size_t max_concurrent_streams = 5; + nghttp2_settings_entry iv = {NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, + max_concurrent_streams}; + size_t i; + + memset(&callbacks, 0, sizeof(callbacks)); + callbacks.send_callback = null_send_callback; + + nghttp2_session_server_new(&session, &callbacks, NULL); + + nghttp2_submit_settings(session, NGHTTP2_FLAG_NONE, &iv, 1); + + for (i = 0; i < max_concurrent_streams; ++i) { + open_stream(session, (int)i * 2 + 1); + } + + CU_ASSERT(0 == session->num_closed_streams); + + nghttp2_session_close_stream(session, 1, NGHTTP2_NO_ERROR); + + CU_ASSERT(1 == session->num_closed_streams); + CU_ASSERT(1 == session->closed_stream_tail->stream_id); + CU_ASSERT(session->closed_stream_tail == session->closed_stream_head); + + nghttp2_session_close_stream(session, 5, NGHTTP2_NO_ERROR); + + CU_ASSERT(2 == session->num_closed_streams); + CU_ASSERT(5 == session->closed_stream_tail->stream_id); + CU_ASSERT(1 == session->closed_stream_head->stream_id); + CU_ASSERT(session->closed_stream_head == + session->closed_stream_tail->closed_prev); + CU_ASSERT(NULL == session->closed_stream_tail->closed_next); + CU_ASSERT(session->closed_stream_tail == + session->closed_stream_head->closed_next); + CU_ASSERT(NULL == session->closed_stream_head->closed_prev); + + open_stream(session, 11); + + CU_ASSERT(1 == session->num_closed_streams); + CU_ASSERT(5 == session->closed_stream_tail->stream_id); + CU_ASSERT(session->closed_stream_tail == session->closed_stream_head); + CU_ASSERT(NULL == session->closed_stream_head->closed_prev); + CU_ASSERT(NULL == session->closed_stream_head->closed_next); + + open_stream(session, 13); + + CU_ASSERT(0 == session->num_closed_streams); + CU_ASSERT(NULL == session->closed_stream_tail); + CU_ASSERT(NULL == session->closed_stream_head); + + nghttp2_session_del(session); +} + +void test_nghttp2_session_keep_idle_stream(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + const size_t max_concurrent_streams = 1; + nghttp2_settings_entry iv = {NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, + max_concurrent_streams}; + int i; + + memset(&callbacks, 0, sizeof(callbacks)); + callbacks.send_callback = null_send_callback; + + nghttp2_session_server_new(&session, &callbacks, NULL); + + nghttp2_submit_settings(session, NGHTTP2_FLAG_NONE, &iv, 1); + + /* We at least allow 2 idle streams even if max concurrent streams + is very low. */ + for (i = 0; i < 2; ++i) { + nghttp2_session_open_stream(session, i * 2 + 1, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, NGHTTP2_STREAM_IDLE, NULL); + } + + CU_ASSERT(2 == session->num_idle_streams); + + CU_ASSERT(1 == session->idle_stream_head->stream_id); + CU_ASSERT(3 == session->idle_stream_tail->stream_id); + + nghttp2_session_open_stream(session, 5, NGHTTP2_FLAG_NONE, &pri_spec_default, + NGHTTP2_STREAM_IDLE, NULL); + + CU_ASSERT(2 == session->num_idle_streams); + + CU_ASSERT(3 == session->idle_stream_head->stream_id); + CU_ASSERT(5 == session->idle_stream_tail->stream_id); + + nghttp2_session_del(session); +} + +void test_nghttp2_session_detach_idle_stream(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + int i; + nghttp2_stream *stream; + + memset(&callbacks, 0, sizeof(callbacks)); + callbacks.send_callback = null_send_callback; + + nghttp2_session_server_new(&session, &callbacks, NULL); + + for (i = 1; i <= 3; ++i) { + nghttp2_session_open_stream(session, i, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, NGHTTP2_STREAM_IDLE, NULL); + } + + CU_ASSERT(3 == session->num_idle_streams); + + /* Detach middle stream */ + stream = nghttp2_session_get_stream_raw(session, 2); + + CU_ASSERT(session->idle_stream_head == stream->closed_prev); + CU_ASSERT(session->idle_stream_tail == stream->closed_next); + CU_ASSERT(stream == session->idle_stream_head->closed_next); + CU_ASSERT(stream == session->idle_stream_tail->closed_prev); + + nghttp2_session_detach_idle_stream(session, stream); + + CU_ASSERT(2 == session->num_idle_streams); + + CU_ASSERT(NULL == stream->closed_prev); + CU_ASSERT(NULL == stream->closed_next); + + CU_ASSERT(session->idle_stream_head == + session->idle_stream_tail->closed_prev); + CU_ASSERT(session->idle_stream_tail == + session->idle_stream_head->closed_next); + + /* Detach head stream */ + stream = session->idle_stream_head; + + nghttp2_session_detach_idle_stream(session, stream); + + CU_ASSERT(1 == session->num_idle_streams); + + CU_ASSERT(session->idle_stream_head == session->idle_stream_tail); + CU_ASSERT(NULL == session->idle_stream_head->closed_prev); + CU_ASSERT(NULL == session->idle_stream_head->closed_next); + + /* Detach last stream */ + + stream = session->idle_stream_head; + + nghttp2_session_detach_idle_stream(session, stream); + + CU_ASSERT(0 == session->num_idle_streams); + + CU_ASSERT(NULL == session->idle_stream_head); + CU_ASSERT(NULL == session->idle_stream_tail); + + for (i = 4; i <= 5; ++i) { + nghttp2_session_open_stream(session, i, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, NGHTTP2_STREAM_IDLE, NULL); + } + + CU_ASSERT(2 == session->num_idle_streams); + + /* Detach tail stream */ + + stream = session->idle_stream_tail; + + nghttp2_session_detach_idle_stream(session, stream); + + CU_ASSERT(1 == session->num_idle_streams); + + CU_ASSERT(session->idle_stream_head == session->idle_stream_tail); + CU_ASSERT(NULL == session->idle_stream_head->closed_prev); + CU_ASSERT(NULL == session->idle_stream_head->closed_next); + + nghttp2_session_del(session); +} + +void test_nghttp2_session_large_dep_tree(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + size_t i; + nghttp2_stream *dep_stream = NULL; + nghttp2_stream *root_stream; + int32_t stream_id; + + memset(&callbacks, 0, sizeof(callbacks)); + callbacks.send_callback = null_send_callback; + + nghttp2_session_server_new(&session, &callbacks, NULL); + + stream_id = 1; + for (i = 0; i < NGHTTP2_MAX_DEP_TREE_LENGTH; ++i) { + dep_stream = open_stream_with_dep(session, stream_id, dep_stream); + stream_id += 2; + } + + root_stream = nghttp2_session_get_stream(session, 1); + + /* Check that last dep_stream must be part of tree */ + CU_ASSERT(nghttp2_stream_dep_subtree_find(root_stream, dep_stream)); + + dep_stream = open_stream_with_dep(session, stream_id, dep_stream); + + /* We exceeded NGHTTP2_MAX_DEP_TREE_LENGTH limit. dep_stream is now + root node and has no descendants. */ + CU_ASSERT(!nghttp2_stream_dep_subtree_find(root_stream, dep_stream)); + CU_ASSERT(nghttp2_stream_in_dep_tree(dep_stream)); + + nghttp2_session_del(session); +} + +void test_nghttp2_session_graceful_shutdown(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + my_user_data ud; + + memset(&callbacks, 0, sizeof(callbacks)); + callbacks.send_callback = null_send_callback; + callbacks.on_frame_send_callback = on_frame_send_callback; + callbacks.on_stream_close_callback = on_stream_close_callback; + + nghttp2_session_server_new(&session, &callbacks, &ud); + + open_stream(session, 301); + open_stream(session, 302); + open_stream(session, 309); + open_stream(session, 311); + open_stream(session, 319); + + CU_ASSERT(0 == nghttp2_submit_shutdown_notice(session)); + + ud.frame_send_cb_called = 0; + + CU_ASSERT(0 == nghttp2_session_send(session)); + + CU_ASSERT(1 == ud.frame_send_cb_called); + CU_ASSERT((1u << 31) - 1 == session->local_last_stream_id); + + CU_ASSERT(0 == nghttp2_submit_goaway(session, NGHTTP2_FLAG_NONE, 311, + NGHTTP2_NO_ERROR, NULL, 0)); + + ud.frame_send_cb_called = 0; + ud.stream_close_cb_called = 0; + + CU_ASSERT(0 == nghttp2_session_send(session)); + + CU_ASSERT(1 == ud.frame_send_cb_called); + CU_ASSERT(311 == session->local_last_stream_id); + CU_ASSERT(1 == ud.stream_close_cb_called); + + CU_ASSERT(0 == + nghttp2_session_terminate_session2(session, 301, NGHTTP2_NO_ERROR)); + + ud.frame_send_cb_called = 0; + ud.stream_close_cb_called = 0; + + CU_ASSERT(0 == nghttp2_session_send(session)); + + CU_ASSERT(1 == ud.frame_send_cb_called); + CU_ASSERT(301 == session->local_last_stream_id); + CU_ASSERT(2 == ud.stream_close_cb_called); + + CU_ASSERT(NULL != nghttp2_session_get_stream(session, 301)); + CU_ASSERT(NULL != nghttp2_session_get_stream(session, 302)); + CU_ASSERT(NULL == nghttp2_session_get_stream(session, 309)); + CU_ASSERT(NULL == nghttp2_session_get_stream(session, 311)); + CU_ASSERT(NULL == nghttp2_session_get_stream(session, 319)); + + nghttp2_session_del(session); +} + +void test_nghttp2_session_on_header_temporal_failure(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + my_user_data ud; + nghttp2_bufs bufs; + nghttp2_buf *buf; + nghttp2_hd_deflater deflater; + nghttp2_nv nv[] = {MAKE_NV("alpha", "bravo"), MAKE_NV("charlie", "delta")}; + nghttp2_nv *nva; + size_t hdpos; + ssize_t rv; + nghttp2_frame frame; + nghttp2_frame_hd hd; + nghttp2_outbound_item *item; + nghttp2_mem *mem; + + mem = nghttp2_mem_default(); + memset(&callbacks, 0, sizeof(callbacks)); + callbacks.on_header_callback = temporal_failure_on_header_callback; + + nghttp2_session_server_new(&session, &callbacks, &ud); + + frame_pack_bufs_init(&bufs); + + nghttp2_hd_deflate_init(&deflater, mem); + + nghttp2_nv_array_copy(&nva, reqnv, ARRLEN(reqnv), mem); + + nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_END_STREAM, 1, + NGHTTP2_HCAT_REQUEST, NULL, nva, ARRLEN(reqnv)); + nghttp2_frame_pack_headers(&bufs, &frame.headers, &deflater); + nghttp2_frame_headers_free(&frame.headers, mem); + + /* We are going to create CONTINUATION. First serialize header + block, and then frame header. */ + hdpos = nghttp2_bufs_len(&bufs); + + buf = &bufs.head->buf; + buf->last += NGHTTP2_FRAME_HDLEN; + + nghttp2_hd_deflate_hd_bufs(&deflater, &bufs, &nv[1], 1); + + nghttp2_frame_hd_init(&hd, + nghttp2_bufs_len(&bufs) - hdpos - NGHTTP2_FRAME_HDLEN, + NGHTTP2_CONTINUATION, NGHTTP2_FLAG_END_HEADERS, 1); + + nghttp2_frame_pack_frame_hd(&buf->pos[hdpos], &hd); + + ud.header_cb_called = 0; + rv = nghttp2_session_mem_recv(session, buf->pos, nghttp2_bufs_len(&bufs)); + + CU_ASSERT(rv == nghttp2_bufs_len(&bufs)); + CU_ASSERT(1 == ud.header_cb_called); + + item = nghttp2_session_get_next_ob_item(session); + + CU_ASSERT(NGHTTP2_RST_STREAM == item->frame.hd.type); + CU_ASSERT(1 == item->frame.hd.stream_id); + + /* Make sure no header decompression error occurred */ + CU_ASSERT(NGHTTP2_GOAWAY_NONE == session->goaway_flags); + + nghttp2_hd_deflate_free(&deflater); + nghttp2_session_del(session); + + nghttp2_bufs_reset(&bufs); + + /* Check for PUSH_PROMISE */ + nghttp2_hd_deflate_init(&deflater, mem); + nghttp2_session_client_new(&session, &callbacks, &ud); + + nghttp2_session_open_stream(session, 1, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, NGHTTP2_STREAM_OPENING, NULL); + + rv = pack_push_promise(&bufs, &deflater, 1, NGHTTP2_FLAG_END_HEADERS, 2, + reqnv, ARRLEN(reqnv), mem); + CU_ASSERT(0 == rv); + + ud.header_cb_called = 0; + rv = nghttp2_session_mem_recv(session, bufs.head->buf.pos, + nghttp2_bufs_len(&bufs)); + CU_ASSERT(nghttp2_bufs_len(&bufs) == rv); + CU_ASSERT(1 == ud.header_cb_called); + + item = nghttp2_session_get_next_ob_item(session); + CU_ASSERT(NGHTTP2_RST_STREAM == item->frame.hd.type); + CU_ASSERT(2 == item->frame.hd.stream_id); + CU_ASSERT(NGHTTP2_INTERNAL_ERROR == item->frame.rst_stream.error_code); + + nghttp2_session_del(session); + nghttp2_hd_deflate_free(&deflater); + nghttp2_bufs_free(&bufs); +} + +void test_nghttp2_session_recv_client_magic(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + ssize_t rv; + nghttp2_frame ping_frame; + uint8_t buf[16]; + + /* enable global nghttp2_enable_strict_preface here */ + nghttp2_enable_strict_preface = 1; + + memset(&callbacks, 0, sizeof(callbacks)); + + /* Check success case */ + nghttp2_session_server_new(&session, &callbacks, NULL); + + rv = nghttp2_session_mem_recv(session, (const uint8_t *)NGHTTP2_CLIENT_MAGIC, + NGHTTP2_CLIENT_MAGIC_LEN); + + CU_ASSERT(rv == NGHTTP2_CLIENT_MAGIC_LEN); + CU_ASSERT(NGHTTP2_IB_READ_FIRST_SETTINGS == session->iframe.state); + + /* Receiving PING is error because we want SETTINGS. */ + nghttp2_frame_ping_init(&ping_frame.ping, NGHTTP2_FLAG_NONE, NULL); + + nghttp2_frame_pack_frame_hd(buf, &ping_frame.ping.hd); + + rv = nghttp2_session_mem_recv(session, buf, NGHTTP2_FRAME_HDLEN); + CU_ASSERT(NGHTTP2_FRAME_HDLEN == rv); + CU_ASSERT(NGHTTP2_IB_IGN_ALL == session->iframe.state); + CU_ASSERT(0 == session->iframe.payloadleft); + + nghttp2_frame_ping_free(&ping_frame.ping); + + nghttp2_session_del(session); + + /* Check bad case */ + nghttp2_session_server_new(&session, &callbacks, NULL); + + /* Feed magic with one byte less */ + rv = nghttp2_session_mem_recv(session, (const uint8_t *)NGHTTP2_CLIENT_MAGIC, + NGHTTP2_CLIENT_MAGIC_LEN - 1); + + CU_ASSERT(rv == NGHTTP2_CLIENT_MAGIC_LEN - 1); + CU_ASSERT(NGHTTP2_IB_READ_CLIENT_MAGIC == session->iframe.state); + CU_ASSERT(1 == session->iframe.payloadleft); + + rv = nghttp2_session_mem_recv(session, (const uint8_t *)"\0", 1); + + CU_ASSERT(NGHTTP2_ERR_BAD_CLIENT_MAGIC == rv); + + nghttp2_session_del(session); + + /* disable global nghttp2_enable_strict_preface here */ + nghttp2_enable_strict_preface = 0; +} + +void test_nghttp2_session_delete_data_item(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + nghttp2_stream *a; + nghttp2_data_provider prd; + + memset(&callbacks, 0, sizeof(callbacks)); + + nghttp2_session_server_new(&session, &callbacks, NULL); + + a = open_stream(session, 1); + open_stream_with_dep(session, 3, a); + + /* We don't care about these members, since we won't send data */ + prd.source.ptr = NULL; + prd.read_callback = fail_data_source_read_callback; + + /* This data item will be marked as TOP */ + CU_ASSERT(0 == nghttp2_submit_data(session, NGHTTP2_FLAG_NONE, 1, &prd)); + /* This data item will be marked as REST */ + CU_ASSERT(0 == nghttp2_submit_data(session, NGHTTP2_FLAG_NONE, 3, &prd)); + + nghttp2_session_del(session); +} + +void test_nghttp2_session_open_idle_stream(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + nghttp2_stream *stream; + nghttp2_stream *opened_stream; + nghttp2_priority_spec pri_spec; + nghttp2_frame frame; + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + + nghttp2_session_server_new(&session, &callbacks, NULL); + + nghttp2_priority_spec_init(&pri_spec, 0, 3, 0); + + nghttp2_frame_priority_init(&frame.priority, 1, &pri_spec); + + CU_ASSERT(0 == nghttp2_session_on_priority_received(session, &frame)); + + stream = nghttp2_session_get_stream_raw(session, 1); + + CU_ASSERT(NGHTTP2_STREAM_IDLE == stream->state); + CU_ASSERT(NULL == stream->closed_prev); + CU_ASSERT(NULL == stream->closed_next); + CU_ASSERT(1 == session->num_idle_streams); + CU_ASSERT(session->idle_stream_head == stream); + CU_ASSERT(session->idle_stream_tail == stream); + + opened_stream = nghttp2_session_open_stream( + session, 1, NGHTTP2_STREAM_FLAG_NONE, &pri_spec_default, + NGHTTP2_STREAM_OPENING, NULL); + + CU_ASSERT(stream == opened_stream); + CU_ASSERT(NGHTTP2_STREAM_OPENING == stream->state); + CU_ASSERT(0 == session->num_idle_streams); + CU_ASSERT(NULL == session->idle_stream_head); + CU_ASSERT(NULL == session->idle_stream_tail); + + nghttp2_frame_priority_free(&frame.priority); + + nghttp2_session_del(session); +} + +void test_nghttp2_session_cancel_reserved_remote(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + nghttp2_stream *stream; + nghttp2_frame frame; + nghttp2_nv *nva; + ssize_t nvlen; + nghttp2_hd_deflater deflater; + nghttp2_mem *mem; + nghttp2_bufs bufs; + ssize_t rv; + + mem = nghttp2_mem_default(); + frame_pack_bufs_init(&bufs); + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.send_callback = null_send_callback; + + nghttp2_session_client_new(&session, &callbacks, NULL); + + nghttp2_hd_deflate_init(&deflater, mem); + + stream = nghttp2_session_open_stream(session, 2, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, + NGHTTP2_STREAM_RESERVED, NULL); + + session->last_recv_stream_id = 2; + + nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, 2, NGHTTP2_CANCEL); + + CU_ASSERT(NGHTTP2_STREAM_CLOSING == stream->state); + + CU_ASSERT(0 == nghttp2_session_send(session)); + + nvlen = ARRLEN(resnv); + nghttp2_nv_array_copy(&nva, resnv, nvlen, mem); + + nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_END_HEADERS, 2, + NGHTTP2_HCAT_PUSH_RESPONSE, NULL, nva, nvlen); + rv = nghttp2_frame_pack_headers(&bufs, &frame.headers, &deflater); + + CU_ASSERT(0 == rv); + + rv = nghttp2_session_mem_recv(session, bufs.head->buf.pos, + nghttp2_buf_len(&bufs.head->buf)); + + CU_ASSERT(nghttp2_buf_len(&bufs.head->buf) == rv); + + /* stream is not dangling, so assign NULL */ + stream = NULL; + + /* No RST_STREAM or GOAWAY is generated since stream should be in + NGHTTP2_STREAM_CLOSING and push response should be ignored. */ + CU_ASSERT(0 == nghttp2_outbound_queue_size(&session->ob_reg)); + + /* Check that we can receive push response HEADERS while RST_STREAM + is just queued. */ + nghttp2_session_open_stream(session, 4, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, NGHTTP2_STREAM_RESERVED, NULL); + + session->last_recv_stream_id = 4; + + nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, 2, NGHTTP2_CANCEL); + + nghttp2_bufs_reset(&bufs); + + frame.hd.stream_id = 4; + rv = nghttp2_frame_pack_headers(&bufs, &frame.headers, &deflater); + + CU_ASSERT(0 == rv); + + rv = nghttp2_session_mem_recv(session, bufs.head->buf.pos, + nghttp2_buf_len(&bufs.head->buf)); + + CU_ASSERT(nghttp2_buf_len(&bufs.head->buf) == rv); + + CU_ASSERT(1 == nghttp2_outbound_queue_size(&session->ob_reg)); + + nghttp2_frame_headers_free(&frame.headers, mem); + + nghttp2_hd_deflate_free(&deflater); + + nghttp2_session_del(session); + + nghttp2_bufs_free(&bufs); +} + +void test_nghttp2_session_reset_pending_headers(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + nghttp2_stream *stream; + int32_t stream_id; + my_user_data ud; + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.send_callback = null_send_callback; + callbacks.on_frame_send_callback = on_frame_send_callback; + callbacks.on_frame_not_send_callback = on_frame_not_send_callback; + callbacks.on_stream_close_callback = on_stream_close_callback; + + nghttp2_session_client_new(&session, &callbacks, &ud); + + stream_id = nghttp2_submit_request(session, NULL, NULL, 0, NULL, NULL); + CU_ASSERT(stream_id >= 1); + + nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, stream_id, + NGHTTP2_CANCEL); + + session->remote_settings.max_concurrent_streams = 0; + + /* RST_STREAM cancels pending HEADERS and is not actually sent. */ + ud.frame_send_cb_called = 0; + CU_ASSERT(0 == nghttp2_session_send(session)); + + CU_ASSERT(0 == ud.frame_send_cb_called); + + stream = nghttp2_session_get_stream(session, stream_id); + + CU_ASSERT(NULL == stream); + + /* See HEADERS is not sent. on_stream_close is called just like + transmission failure. */ + session->remote_settings.max_concurrent_streams = 1; + + ud.frame_not_send_cb_called = 0; + ud.stream_close_error_code = 0; + CU_ASSERT(0 == nghttp2_session_send(session)); + + CU_ASSERT(1 == ud.frame_not_send_cb_called); + CU_ASSERT(NGHTTP2_HEADERS == ud.not_sent_frame_type); + CU_ASSERT(NGHTTP2_CANCEL == ud.stream_close_error_code); + + stream = nghttp2_session_get_stream(session, stream_id); + + CU_ASSERT(NULL == stream); + + nghttp2_session_del(session); +} + +void test_nghttp2_session_send_data_callback(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + nghttp2_data_provider data_prd; + my_user_data ud; + accumulator acc; + nghttp2_frame_hd hd; + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.send_callback = accumulator_send_callback; + callbacks.send_data_callback = send_data_callback; + + data_prd.read_callback = no_copy_data_source_read_callback; + + acc.length = 0; + ud.acc = &acc; + + ud.data_source_length = NGHTTP2_DATA_PAYLOADLEN * 2; + + nghttp2_session_client_new(&session, &callbacks, &ud); + + nghttp2_session_open_stream(session, 1, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, NGHTTP2_STREAM_OPENING, NULL); + + nghttp2_submit_data(session, NGHTTP2_FLAG_END_STREAM, 1, &data_prd); + + CU_ASSERT(0 == nghttp2_session_send(session)); + + CU_ASSERT((NGHTTP2_FRAME_HDLEN + NGHTTP2_DATA_PAYLOADLEN) * 2 == acc.length); + + nghttp2_frame_unpack_frame_hd(&hd, acc.buf); + + CU_ASSERT(16384 == hd.length); + CU_ASSERT(NGHTTP2_DATA == hd.type); + CU_ASSERT(NGHTTP2_FLAG_NONE == hd.flags); + + nghttp2_frame_unpack_frame_hd(&hd, acc.buf + NGHTTP2_FRAME_HDLEN + hd.length); + + CU_ASSERT(16384 == hd.length); + CU_ASSERT(NGHTTP2_DATA == hd.type); + CU_ASSERT(NGHTTP2_FLAG_END_STREAM == hd.flags); + + nghttp2_session_del(session); +} + +void test_nghttp2_session_on_begin_headers_temporal_failure(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + my_user_data ud; + nghttp2_bufs bufs; + nghttp2_mem *mem; + ssize_t rv; + nghttp2_hd_deflater deflater; + nghttp2_outbound_item *item; + + mem = nghttp2_mem_default(); + frame_pack_bufs_init(&bufs); + nghttp2_hd_deflate_init(&deflater, mem); + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.on_begin_headers_callback = + temporal_failure_on_begin_headers_callback; + callbacks.on_header_callback = on_header_callback; + callbacks.on_frame_recv_callback = on_frame_recv_callback; + callbacks.send_callback = null_send_callback; + nghttp2_session_server_new(&session, &callbacks, &ud); + + rv = pack_headers(&bufs, &deflater, 1, NGHTTP2_FLAG_END_HEADERS, reqnv, + ARRLEN(reqnv), mem); + CU_ASSERT(0 == rv); + + ud.header_cb_called = 0; + ud.frame_recv_cb_called = 0; + rv = nghttp2_session_mem_recv(session, bufs.head->buf.pos, + nghttp2_bufs_len(&bufs)); + CU_ASSERT(nghttp2_bufs_len(&bufs) == rv); + CU_ASSERT(0 == ud.header_cb_called); + CU_ASSERT(0 == ud.frame_recv_cb_called); + + item = nghttp2_session_get_next_ob_item(session); + CU_ASSERT(NGHTTP2_RST_STREAM == item->frame.hd.type); + CU_ASSERT(1 == item->frame.hd.stream_id); + CU_ASSERT(NGHTTP2_INTERNAL_ERROR == item->frame.rst_stream.error_code); + + nghttp2_session_del(session); + nghttp2_hd_deflate_free(&deflater); + + nghttp2_bufs_reset(&bufs); + /* check for PUSH_PROMISE */ + nghttp2_hd_deflate_init(&deflater, mem); + nghttp2_session_client_new(&session, &callbacks, &ud); + + nghttp2_session_open_stream(session, 1, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, NGHTTP2_STREAM_OPENING, NULL); + + rv = pack_push_promise(&bufs, &deflater, 1, NGHTTP2_FLAG_END_HEADERS, 2, + reqnv, ARRLEN(reqnv), mem); + CU_ASSERT(0 == rv); + + ud.header_cb_called = 0; + ud.frame_recv_cb_called = 0; + rv = nghttp2_session_mem_recv(session, bufs.head->buf.pos, + nghttp2_bufs_len(&bufs)); + CU_ASSERT(nghttp2_bufs_len(&bufs) == rv); + CU_ASSERT(0 == ud.header_cb_called); + CU_ASSERT(0 == ud.frame_recv_cb_called); + + item = nghttp2_session_get_next_ob_item(session); + CU_ASSERT(NGHTTP2_RST_STREAM == item->frame.hd.type); + CU_ASSERT(2 == item->frame.hd.stream_id); + CU_ASSERT(NGHTTP2_INTERNAL_ERROR == item->frame.rst_stream.error_code); + + nghttp2_session_del(session); + nghttp2_hd_deflate_free(&deflater); + nghttp2_bufs_free(&bufs); +} + +static void check_nghttp2_http_recv_headers_fail( + nghttp2_session *session, nghttp2_hd_deflater *deflater, int32_t stream_id, + int stream_state, const nghttp2_nv *nva, size_t nvlen) { + nghttp2_mem *mem; + ssize_t rv; + nghttp2_outbound_item *item; + nghttp2_bufs bufs; + my_user_data *ud; + + mem = nghttp2_mem_default(); + frame_pack_bufs_init(&bufs); + + ud = session->user_data; + + if (stream_state != -1) { + nghttp2_session_open_stream(session, stream_id, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, stream_state, NULL); + } + + rv = pack_headers(&bufs, deflater, stream_id, NGHTTP2_FLAG_END_HEADERS, nva, + nvlen, mem); + CU_ASSERT(0 == rv); + + ud->invalid_frame_recv_cb_called = 0; + + rv = nghttp2_session_mem_recv(session, bufs.head->buf.pos, + nghttp2_buf_len(&bufs.head->buf)); + + CU_ASSERT(nghttp2_buf_len(&bufs.head->buf) == rv); + + item = nghttp2_session_get_next_ob_item(session); + + CU_ASSERT(NGHTTP2_RST_STREAM == item->frame.hd.type); + CU_ASSERT(1 == ud->invalid_frame_recv_cb_called); + + CU_ASSERT(0 == nghttp2_session_send(session)); + + nghttp2_bufs_free(&bufs); +} + +static void check_nghttp2_http_recv_headers_ok( + nghttp2_session *session, nghttp2_hd_deflater *deflater, int32_t stream_id, + int stream_state, const nghttp2_nv *nva, size_t nvlen) { + nghttp2_mem *mem; + ssize_t rv; + nghttp2_bufs bufs; + my_user_data *ud; + + mem = nghttp2_mem_default(); + frame_pack_bufs_init(&bufs); + + ud = session->user_data; + + if (stream_state != -1) { + nghttp2_session_open_stream(session, stream_id, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, stream_state, NULL); + } + + rv = pack_headers(&bufs, deflater, stream_id, NGHTTP2_FLAG_END_HEADERS, nva, + nvlen, mem); + CU_ASSERT(0 == rv); + + ud->frame_recv_cb_called = 0; + + rv = nghttp2_session_mem_recv(session, bufs.head->buf.pos, + nghttp2_buf_len(&bufs.head->buf)); + + CU_ASSERT(nghttp2_buf_len(&bufs.head->buf) == rv); + CU_ASSERT(NULL == nghttp2_session_get_next_ob_item(session)); + CU_ASSERT(1 == ud->frame_recv_cb_called); + + nghttp2_bufs_free(&bufs); +} + +void test_nghttp2_http_mandatory_headers(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + nghttp2_hd_deflater deflater; + nghttp2_mem *mem; + my_user_data ud; + /* test case for response */ + const nghttp2_nv nostatus_resnv[] = {MAKE_NV("server", "foo")}; + const nghttp2_nv dupstatus_resnv[] = {MAKE_NV(":status", "200"), + MAKE_NV(":status", "200")}; + const nghttp2_nv badpseudo_resnv[] = {MAKE_NV(":status", "200"), + MAKE_NV(":scheme", "https")}; + const nghttp2_nv latepseudo_resnv[] = {MAKE_NV("server", "foo"), + MAKE_NV(":status", "200")}; + const nghttp2_nv badstatus_resnv[] = {MAKE_NV(":status", "2000")}; + const nghttp2_nv badcl_resnv[] = {MAKE_NV(":status", "200"), + MAKE_NV("content-length", "-1")}; + const nghttp2_nv dupcl_resnv[] = {MAKE_NV(":status", "200"), + MAKE_NV("content-length", "0"), + MAKE_NV("content-length", "0")}; + const nghttp2_nv badhd_resnv[] = {MAKE_NV(":status", "200"), + MAKE_NV("connection", "close")}; + + /* test case for request */ + const nghttp2_nv nopath_reqnv[] = {MAKE_NV(":scheme", "https"), + MAKE_NV(":method", "GET"), + MAKE_NV(":authority", "localhost")}; + const nghttp2_nv earlyconnect_reqnv[] = { + MAKE_NV(":method", "CONNECT"), MAKE_NV(":scheme", "https"), + MAKE_NV(":path", "/"), MAKE_NV(":authority", "localhost")}; + const nghttp2_nv lateconnect_reqnv[] = { + MAKE_NV(":scheme", "https"), MAKE_NV(":path", "/"), + MAKE_NV(":method", "CONNECT"), MAKE_NV(":authority", "localhost")}; + const nghttp2_nv duppath_reqnv[] = { + MAKE_NV(":scheme", "https"), MAKE_NV(":method", "GET"), + MAKE_NV(":authority", "localhost"), MAKE_NV(":path", "/"), + MAKE_NV(":path", "/")}; + const nghttp2_nv badcl_reqnv[] = { + MAKE_NV(":scheme", "https"), MAKE_NV(":method", "POST"), + MAKE_NV(":authority", "localhost"), MAKE_NV(":path", "/"), + MAKE_NV("content-length", "-1")}; + const nghttp2_nv dupcl_reqnv[] = { + MAKE_NV(":scheme", "https"), MAKE_NV(":method", "POST"), + MAKE_NV(":authority", "localhost"), MAKE_NV(":path", "/"), + MAKE_NV("content-length", "0"), MAKE_NV("content-length", "0")}; + const nghttp2_nv badhd_reqnv[] = { + MAKE_NV(":scheme", "https"), MAKE_NV(":method", "GET"), + MAKE_NV(":authority", "localhost"), MAKE_NV(":path", "/"), + MAKE_NV("connection", "close")}; + const nghttp2_nv badauthority_reqnv[] = { + MAKE_NV(":scheme", "https"), MAKE_NV(":method", "GET"), + MAKE_NV(":authority", "\x0d\x0alocalhost"), MAKE_NV(":path", "/")}; + const nghttp2_nv badhdbtw_reqnv[] = { + MAKE_NV(":scheme", "https"), MAKE_NV(":method", "GET"), + MAKE_NV("foo", "\x0d\x0a"), MAKE_NV(":authority", "localhost"), + MAKE_NV(":path", "/")}; + const nghttp2_nv asteriskget1_reqnv[] = { + MAKE_NV(":path", "*"), MAKE_NV(":scheme", "https"), + MAKE_NV(":authority", "localhost"), MAKE_NV(":method", "GET")}; + const nghttp2_nv asteriskget2_reqnv[] = { + MAKE_NV(":scheme", "https"), MAKE_NV(":authority", "localhost"), + MAKE_NV(":method", "GET"), MAKE_NV(":path", "*")}; + const nghttp2_nv asteriskoptions1_reqnv[] = { + MAKE_NV(":path", "*"), MAKE_NV(":scheme", "https"), + MAKE_NV(":authority", "localhost"), MAKE_NV(":method", "OPTIONS")}; + const nghttp2_nv asteriskoptions2_reqnv[] = { + MAKE_NV(":scheme", "https"), MAKE_NV(":authority", "localhost"), + MAKE_NV(":method", "OPTIONS"), MAKE_NV(":path", "*")}; + + mem = nghttp2_mem_default(); + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.send_callback = null_send_callback; + callbacks.on_frame_recv_callback = on_frame_recv_callback; + callbacks.on_invalid_frame_recv_callback = on_invalid_frame_recv_callback; + + nghttp2_session_client_new(&session, &callbacks, &ud); + + nghttp2_hd_deflate_init(&deflater, mem); + + /* response header lacks :status */ + check_nghttp2_http_recv_headers_fail(session, &deflater, 1, + NGHTTP2_STREAM_OPENING, nostatus_resnv, + ARRLEN(nostatus_resnv)); + + /* response header has 2 :status */ + check_nghttp2_http_recv_headers_fail(session, &deflater, 3, + NGHTTP2_STREAM_OPENING, dupstatus_resnv, + ARRLEN(dupstatus_resnv)); + + /* response header has bad pseudo header :scheme */ + check_nghttp2_http_recv_headers_fail(session, &deflater, 5, + NGHTTP2_STREAM_OPENING, badpseudo_resnv, + ARRLEN(badpseudo_resnv)); + + /* response header has :status after regular header field */ + check_nghttp2_http_recv_headers_fail(session, &deflater, 7, + NGHTTP2_STREAM_OPENING, latepseudo_resnv, + ARRLEN(latepseudo_resnv)); + + /* response header has bad status code */ + check_nghttp2_http_recv_headers_fail(session, &deflater, 9, + NGHTTP2_STREAM_OPENING, badstatus_resnv, + ARRLEN(badstatus_resnv)); + + /* response header has bad content-length */ + check_nghttp2_http_recv_headers_fail(session, &deflater, 11, + NGHTTP2_STREAM_OPENING, badcl_resnv, + ARRLEN(badcl_resnv)); + + /* response header has multiple content-length */ + check_nghttp2_http_recv_headers_fail(session, &deflater, 13, + NGHTTP2_STREAM_OPENING, dupcl_resnv, + ARRLEN(dupcl_resnv)); + + /* response header has disallowed header field */ + check_nghttp2_http_recv_headers_fail(session, &deflater, 15, + NGHTTP2_STREAM_OPENING, badhd_resnv, + ARRLEN(badhd_resnv)); + + nghttp2_hd_deflate_free(&deflater); + + nghttp2_session_del(session); + + /* check server side */ + nghttp2_session_server_new(&session, &callbacks, &ud); + + nghttp2_hd_deflate_init(&deflater, mem); + + /* request header has no :path */ + check_nghttp2_http_recv_headers_fail(session, &deflater, 1, -1, nopath_reqnv, + ARRLEN(nopath_reqnv)); + + /* request header has CONNECT method, but followed by :path */ + check_nghttp2_http_recv_headers_fail(session, &deflater, 3, -1, + earlyconnect_reqnv, + ARRLEN(earlyconnect_reqnv)); + + /* request header has CONNECT method following :path */ + check_nghttp2_http_recv_headers_fail( + session, &deflater, 5, -1, lateconnect_reqnv, ARRLEN(lateconnect_reqnv)); + + /* request header has multiple :path */ + check_nghttp2_http_recv_headers_fail(session, &deflater, 7, -1, duppath_reqnv, + ARRLEN(duppath_reqnv)); + + /* request header has bad content-length */ + check_nghttp2_http_recv_headers_fail(session, &deflater, 9, -1, badcl_reqnv, + ARRLEN(badcl_reqnv)); + + /* request header has multiple content-length */ + check_nghttp2_http_recv_headers_fail(session, &deflater, 11, -1, dupcl_reqnv, + ARRLEN(dupcl_reqnv)); + + /* request header has disallowed header field */ + check_nghttp2_http_recv_headers_fail(session, &deflater, 13, -1, badhd_reqnv, + ARRLEN(badhd_reqnv)); + + /* request header has :authority header field containing illegal + characters */ + check_nghttp2_http_recv_headers_fail(session, &deflater, 15, -1, + badauthority_reqnv, + ARRLEN(badauthority_reqnv)); + + /* request header has regular header field containing illegal + character before all mandatory header fields are seen. */ + check_nghttp2_http_recv_headers_fail(session, &deflater, 17, -1, + badhdbtw_reqnv, ARRLEN(badhdbtw_reqnv)); + + /* request header has "*" in :path header field while method is GET. + :path is received before :method */ + check_nghttp2_http_recv_headers_fail(session, &deflater, 19, -1, + asteriskget1_reqnv, + ARRLEN(asteriskget1_reqnv)); + + /* request header has "*" in :path header field while method is GET. + :method is received before :path */ + check_nghttp2_http_recv_headers_fail(session, &deflater, 21, -1, + asteriskget2_reqnv, + ARRLEN(asteriskget2_reqnv)); + + /* OPTIONS method can include "*" in :path header field. :path is + received before :method. */ + check_nghttp2_http_recv_headers_ok(session, &deflater, 23, -1, + asteriskoptions1_reqnv, + ARRLEN(asteriskoptions1_reqnv)); + + /* OPTIONS method can include "*" in :path header field. :method is + received before :path. */ + check_nghttp2_http_recv_headers_ok(session, &deflater, 25, -1, + asteriskoptions2_reqnv, + ARRLEN(asteriskoptions2_reqnv)); + + nghttp2_hd_deflate_free(&deflater); + + nghttp2_session_del(session); +} + +void test_nghttp2_http_content_length(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + nghttp2_hd_deflater deflater; + nghttp2_mem *mem; + nghttp2_bufs bufs; + ssize_t rv; + nghttp2_stream *stream; + const nghttp2_nv cl_resnv[] = {MAKE_NV(":status", "200"), + MAKE_NV("te", "trailers"), + MAKE_NV("content-length", "9000000000")}; + const nghttp2_nv cl_reqnv[] = { + MAKE_NV(":path", "/"), MAKE_NV(":method", "PUT"), + MAKE_NV(":scheme", "https"), MAKE_NV("te", "trailers"), + MAKE_NV("host", "localhost"), MAKE_NV("content-length", "9000000000")}; + + mem = nghttp2_mem_default(); + frame_pack_bufs_init(&bufs); + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.send_callback = null_send_callback; + + nghttp2_session_client_new(&session, &callbacks, NULL); + + nghttp2_hd_deflate_init(&deflater, mem); + + stream = nghttp2_session_open_stream(session, 1, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, + NGHTTP2_STREAM_OPENING, NULL); + + rv = pack_headers(&bufs, &deflater, 1, NGHTTP2_FLAG_END_HEADERS, cl_resnv, + ARRLEN(cl_resnv), mem); + CU_ASSERT(0 == rv); + + rv = nghttp2_session_mem_recv(session, bufs.head->buf.pos, + nghttp2_buf_len(&bufs.head->buf)); + + CU_ASSERT(nghttp2_buf_len(&bufs.head->buf) == rv); + CU_ASSERT(NULL == nghttp2_session_get_next_ob_item(session)); + CU_ASSERT(9000000000LL == stream->content_length); + CU_ASSERT(200 == stream->status_code); + + nghttp2_hd_deflate_free(&deflater); + + nghttp2_session_del(session); + + nghttp2_bufs_reset(&bufs); + + /* check server side */ + nghttp2_session_server_new(&session, &callbacks, NULL); + + nghttp2_hd_deflate_init(&deflater, mem); + + rv = pack_headers(&bufs, &deflater, 1, NGHTTP2_FLAG_END_HEADERS, cl_reqnv, + ARRLEN(cl_reqnv), mem); + CU_ASSERT(0 == rv); + + rv = nghttp2_session_mem_recv(session, bufs.head->buf.pos, + nghttp2_buf_len(&bufs.head->buf)); + + CU_ASSERT(nghttp2_buf_len(&bufs.head->buf) == rv); + + stream = nghttp2_session_get_stream(session, 1); + + CU_ASSERT(NULL == nghttp2_session_get_next_ob_item(session)); + CU_ASSERT(9000000000LL == stream->content_length); + + nghttp2_hd_deflate_free(&deflater); + + nghttp2_session_del(session); + + nghttp2_bufs_free(&bufs); +} + +void test_nghttp2_http_content_length_mismatch(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + nghttp2_hd_deflater deflater; + nghttp2_mem *mem; + nghttp2_bufs bufs; + ssize_t rv; + const nghttp2_nv cl_reqnv[] = { + MAKE_NV(":path", "/"), MAKE_NV(":method", "PUT"), + MAKE_NV(":authority", "localhost"), MAKE_NV(":scheme", "https"), + MAKE_NV("content-length", "20")}; + nghttp2_outbound_item *item; + nghttp2_frame_hd hd; + + mem = nghttp2_mem_default(); + frame_pack_bufs_init(&bufs); + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.send_callback = null_send_callback; + + nghttp2_session_server_new(&session, &callbacks, NULL); + + nghttp2_hd_deflate_init(&deflater, mem); + + /* header says content-length: 20, but HEADERS has END_STREAM flag set */ + rv = pack_headers(&bufs, &deflater, 1, + NGHTTP2_FLAG_END_HEADERS | NGHTTP2_FLAG_END_STREAM, + cl_reqnv, ARRLEN(cl_reqnv), mem); + CU_ASSERT(0 == rv); + + rv = nghttp2_session_mem_recv(session, bufs.head->buf.pos, + nghttp2_buf_len(&bufs.head->buf)); + + CU_ASSERT(nghttp2_buf_len(&bufs.head->buf) == rv); + + item = nghttp2_session_get_next_ob_item(session); + CU_ASSERT(NGHTTP2_RST_STREAM == item->frame.hd.type); + + CU_ASSERT(0 == nghttp2_session_send(session)); + + nghttp2_bufs_reset(&bufs); + + /* header says content-length: 20, but DATA has 0 byte */ + rv = pack_headers(&bufs, &deflater, 3, NGHTTP2_FLAG_END_HEADERS, cl_reqnv, + ARRLEN(cl_reqnv), mem); + CU_ASSERT(0 == rv); + + nghttp2_frame_hd_init(&hd, 0, NGHTTP2_DATA, NGHTTP2_FLAG_END_STREAM, 3); + nghttp2_frame_pack_frame_hd(bufs.head->buf.last, &hd); + bufs.head->buf.last += NGHTTP2_FRAME_HDLEN; + + rv = nghttp2_session_mem_recv(session, bufs.head->buf.pos, + nghttp2_buf_len(&bufs.head->buf)); + + CU_ASSERT(nghttp2_buf_len(&bufs.head->buf) == rv); + + item = nghttp2_session_get_next_ob_item(session); + CU_ASSERT(NGHTTP2_RST_STREAM == item->frame.hd.type); + + CU_ASSERT(0 == nghttp2_session_send(session)); + + nghttp2_bufs_reset(&bufs); + + /* header says content-length: 20, but DATA has 21 bytes */ + rv = pack_headers(&bufs, &deflater, 5, NGHTTP2_FLAG_END_HEADERS, cl_reqnv, + ARRLEN(cl_reqnv), mem); + CU_ASSERT(0 == rv); + + nghttp2_frame_hd_init(&hd, 21, NGHTTP2_DATA, NGHTTP2_FLAG_END_STREAM, 5); + nghttp2_frame_pack_frame_hd(bufs.head->buf.last, &hd); + bufs.head->buf.last += NGHTTP2_FRAME_HDLEN + 21; + + rv = nghttp2_session_mem_recv(session, bufs.head->buf.pos, + nghttp2_buf_len(&bufs.head->buf)); + + CU_ASSERT(nghttp2_buf_len(&bufs.head->buf) == rv); + + item = nghttp2_session_get_next_ob_item(session); + CU_ASSERT(NGHTTP2_RST_STREAM == item->frame.hd.type); + + CU_ASSERT(0 == nghttp2_session_send(session)); + + nghttp2_bufs_reset(&bufs); + + nghttp2_hd_deflate_free(&deflater); + + nghttp2_session_del(session); + + nghttp2_bufs_free(&bufs); +} + +void test_nghttp2_http_non_final_response(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + nghttp2_hd_deflater deflater; + nghttp2_mem *mem; + nghttp2_bufs bufs; + ssize_t rv; + const nghttp2_nv nonfinal_resnv[] = { + MAKE_NV(":status", "100"), + }; + nghttp2_outbound_item *item; + nghttp2_frame_hd hd; + + mem = nghttp2_mem_default(); + frame_pack_bufs_init(&bufs); + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.send_callback = null_send_callback; + + nghttp2_session_client_new(&session, &callbacks, NULL); + + nghttp2_hd_deflate_init(&deflater, mem); + + /* non-final HEADERS with END_STREAM is illegal */ + nghttp2_session_open_stream(session, 1, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, NGHTTP2_STREAM_OPENING, NULL); + + rv = pack_headers(&bufs, &deflater, 1, + NGHTTP2_FLAG_END_HEADERS | NGHTTP2_FLAG_END_STREAM, + nonfinal_resnv, ARRLEN(nonfinal_resnv), mem); + CU_ASSERT(0 == rv); + + rv = nghttp2_session_mem_recv(session, bufs.head->buf.pos, + nghttp2_buf_len(&bufs.head->buf)); + + CU_ASSERT(nghttp2_buf_len(&bufs.head->buf) == rv); + + item = nghttp2_session_get_next_ob_item(session); + CU_ASSERT(NGHTTP2_RST_STREAM == item->frame.hd.type); + + CU_ASSERT(0 == nghttp2_session_send(session)); + + nghttp2_bufs_reset(&bufs); + + /* non-final HEADERS followed by non-empty DATA is illegal */ + nghttp2_session_open_stream(session, 3, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, NGHTTP2_STREAM_OPENING, NULL); + + rv = pack_headers(&bufs, &deflater, 3, NGHTTP2_FLAG_END_HEADERS, + nonfinal_resnv, ARRLEN(nonfinal_resnv), mem); + CU_ASSERT(0 == rv); + + nghttp2_frame_hd_init(&hd, 10, NGHTTP2_DATA, NGHTTP2_FLAG_END_STREAM, 3); + nghttp2_frame_pack_frame_hd(bufs.head->buf.last, &hd); + bufs.head->buf.last += NGHTTP2_FRAME_HDLEN + 10; + + rv = nghttp2_session_mem_recv(session, bufs.head->buf.pos, + nghttp2_buf_len(&bufs.head->buf)); + + CU_ASSERT(nghttp2_buf_len(&bufs.head->buf) == rv); + + item = nghttp2_session_get_next_ob_item(session); + CU_ASSERT(NGHTTP2_RST_STREAM == item->frame.hd.type); + + CU_ASSERT(0 == nghttp2_session_send(session)); + + nghttp2_bufs_reset(&bufs); + + /* non-final HEADERS followed by empty DATA (without END_STREAM) is + ok */ + nghttp2_session_open_stream(session, 5, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, NGHTTP2_STREAM_OPENING, NULL); + + rv = pack_headers(&bufs, &deflater, 5, NGHTTP2_FLAG_END_HEADERS, + nonfinal_resnv, ARRLEN(nonfinal_resnv), mem); + CU_ASSERT(0 == rv); + + nghttp2_frame_hd_init(&hd, 0, NGHTTP2_DATA, NGHTTP2_FLAG_NONE, 5); + nghttp2_frame_pack_frame_hd(bufs.head->buf.last, &hd); + bufs.head->buf.last += NGHTTP2_FRAME_HDLEN; + + rv = nghttp2_session_mem_recv(session, bufs.head->buf.pos, + nghttp2_buf_len(&bufs.head->buf)); + + CU_ASSERT(nghttp2_buf_len(&bufs.head->buf) == rv); + + CU_ASSERT(NULL == nghttp2_session_get_next_ob_item(session)); + + nghttp2_bufs_reset(&bufs); + + /* non-final HEADERS followed by empty DATA (with END_STREAM) is + illegal */ + nghttp2_session_open_stream(session, 7, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, NGHTTP2_STREAM_OPENING, NULL); + + rv = pack_headers(&bufs, &deflater, 7, NGHTTP2_FLAG_END_HEADERS, + nonfinal_resnv, ARRLEN(nonfinal_resnv), mem); + CU_ASSERT(0 == rv); + + nghttp2_frame_hd_init(&hd, 0, NGHTTP2_DATA, NGHTTP2_FLAG_END_STREAM, 7); + nghttp2_frame_pack_frame_hd(bufs.head->buf.last, &hd); + bufs.head->buf.last += NGHTTP2_FRAME_HDLEN; + + rv = nghttp2_session_mem_recv(session, bufs.head->buf.pos, + nghttp2_buf_len(&bufs.head->buf)); + + CU_ASSERT(nghttp2_buf_len(&bufs.head->buf) == rv); + + item = nghttp2_session_get_next_ob_item(session); + + CU_ASSERT(NGHTTP2_RST_STREAM == item->frame.hd.type); + + CU_ASSERT(0 == nghttp2_session_send(session)); + + nghttp2_bufs_reset(&bufs); + + /* non-final HEADERS followed by final HEADERS is OK */ + nghttp2_session_open_stream(session, 9, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, NGHTTP2_STREAM_OPENING, NULL); + + rv = pack_headers(&bufs, &deflater, 9, NGHTTP2_FLAG_END_HEADERS, + nonfinal_resnv, ARRLEN(nonfinal_resnv), mem); + CU_ASSERT(0 == rv); + + rv = nghttp2_session_mem_recv(session, bufs.head->buf.pos, + nghttp2_buf_len(&bufs.head->buf)); + + CU_ASSERT(nghttp2_buf_len(&bufs.head->buf) == rv); + + nghttp2_bufs_reset(&bufs); + + rv = pack_headers(&bufs, &deflater, 9, NGHTTP2_FLAG_END_HEADERS, resnv, + ARRLEN(resnv), mem); + CU_ASSERT(0 == rv); + + rv = nghttp2_session_mem_recv(session, bufs.head->buf.pos, + nghttp2_buf_len(&bufs.head->buf)); + + CU_ASSERT(nghttp2_buf_len(&bufs.head->buf) == rv); + + CU_ASSERT(NULL == nghttp2_session_get_next_ob_item(session)); + + nghttp2_bufs_reset(&bufs); + + nghttp2_hd_deflate_free(&deflater); + + nghttp2_session_del(session); + + nghttp2_bufs_free(&bufs); +} + +void test_nghttp2_http_trailer_headers(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + nghttp2_hd_deflater deflater; + nghttp2_mem *mem; + nghttp2_bufs bufs; + ssize_t rv; + const nghttp2_nv trailer_reqnv[] = { + MAKE_NV("foo", "bar"), + }; + nghttp2_outbound_item *item; + + mem = nghttp2_mem_default(); + frame_pack_bufs_init(&bufs); + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.send_callback = null_send_callback; + + nghttp2_session_server_new(&session, &callbacks, NULL); + + nghttp2_hd_deflate_init(&deflater, mem); + + /* good trailer header */ + rv = pack_headers(&bufs, &deflater, 1, NGHTTP2_FLAG_END_HEADERS, reqnv, + ARRLEN(reqnv), mem); + CU_ASSERT(0 == rv); + + rv = nghttp2_session_mem_recv(session, bufs.head->buf.pos, + nghttp2_buf_len(&bufs.head->buf)); + + CU_ASSERT(nghttp2_buf_len(&bufs.head->buf) == rv); + + nghttp2_bufs_reset(&bufs); + + rv = pack_headers(&bufs, &deflater, 1, + NGHTTP2_FLAG_END_HEADERS | NGHTTP2_FLAG_END_STREAM, + trailer_reqnv, ARRLEN(trailer_reqnv), mem); + CU_ASSERT(0 == rv); + + rv = nghttp2_session_mem_recv(session, bufs.head->buf.pos, + nghttp2_buf_len(&bufs.head->buf)); + + CU_ASSERT(nghttp2_buf_len(&bufs.head->buf) == rv); + + CU_ASSERT(NULL == nghttp2_session_get_next_ob_item(session)); + + nghttp2_bufs_reset(&bufs); + + /* trailer header without END_STREAM is illegal */ + rv = pack_headers(&bufs, &deflater, 3, NGHTTP2_FLAG_END_HEADERS, reqnv, + ARRLEN(reqnv), mem); + CU_ASSERT(0 == rv); + + rv = nghttp2_session_mem_recv(session, bufs.head->buf.pos, + nghttp2_buf_len(&bufs.head->buf)); + + CU_ASSERT(nghttp2_buf_len(&bufs.head->buf) == rv); + + nghttp2_bufs_reset(&bufs); + + rv = pack_headers(&bufs, &deflater, 3, NGHTTP2_FLAG_END_HEADERS, + trailer_reqnv, ARRLEN(trailer_reqnv), mem); + CU_ASSERT(0 == rv); + + rv = nghttp2_session_mem_recv(session, bufs.head->buf.pos, + nghttp2_buf_len(&bufs.head->buf)); + + CU_ASSERT(nghttp2_buf_len(&bufs.head->buf) == rv); + + item = nghttp2_session_get_next_ob_item(session); + + CU_ASSERT(NGHTTP2_RST_STREAM == item->frame.hd.type); + + CU_ASSERT(0 == nghttp2_session_send(session)); + + nghttp2_bufs_reset(&bufs); + + /* trailer header including pseudo header field is illegal */ + rv = pack_headers(&bufs, &deflater, 5, NGHTTP2_FLAG_END_HEADERS, reqnv, + ARRLEN(reqnv), mem); + CU_ASSERT(0 == rv); + + rv = nghttp2_session_mem_recv(session, bufs.head->buf.pos, + nghttp2_buf_len(&bufs.head->buf)); + + CU_ASSERT(nghttp2_buf_len(&bufs.head->buf) == rv); + + nghttp2_bufs_reset(&bufs); + + rv = pack_headers(&bufs, &deflater, 5, NGHTTP2_FLAG_END_HEADERS, reqnv, + ARRLEN(reqnv), mem); + CU_ASSERT(0 == rv); + + rv = nghttp2_session_mem_recv(session, bufs.head->buf.pos, + nghttp2_buf_len(&bufs.head->buf)); + + CU_ASSERT(nghttp2_buf_len(&bufs.head->buf) == rv); + + item = nghttp2_session_get_next_ob_item(session); + + CU_ASSERT(NGHTTP2_RST_STREAM == item->frame.hd.type); + + CU_ASSERT(0 == nghttp2_session_send(session)); + + nghttp2_bufs_reset(&bufs); + + nghttp2_hd_deflate_free(&deflater); + + nghttp2_session_del(session); + + nghttp2_bufs_free(&bufs); +} + +void test_nghttp2_http_ignore_regular_header(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + nghttp2_hd_deflater deflater; + nghttp2_mem *mem; + nghttp2_bufs bufs; + ssize_t rv; + my_user_data ud; + const nghttp2_nv bad_reqnv[] = { + MAKE_NV(":authority", "localhost"), MAKE_NV(":scheme", "https"), + MAKE_NV(":path", "/"), MAKE_NV(":method", "GET"), + MAKE_NV("foo", "\x0zzz"), MAKE_NV("bar", "buzz"), + }; + const nghttp2_nv bad_ansnv[] = { + MAKE_NV(":authority", "localhost"), MAKE_NV(":scheme", "https"), + MAKE_NV(":path", "/"), MAKE_NV(":method", "GET"), MAKE_NV("bar", "buzz")}; + ssize_t proclen; + size_t i; + + mem = nghttp2_mem_default(); + frame_pack_bufs_init(&bufs); + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.send_callback = null_send_callback; + callbacks.on_header_callback = pause_on_header_callback; + + nghttp2_session_server_new(&session, &callbacks, &ud); + nghttp2_hd_deflate_init(&deflater, mem); + + rv = pack_headers(&bufs, &deflater, 1, + NGHTTP2_FLAG_END_HEADERS | NGHTTP2_FLAG_END_STREAM, + bad_reqnv, ARRLEN(bad_reqnv), mem); + + CU_ASSERT_FATAL(0 == rv); + + proclen = 0; + + for (i = 0; i < 4; ++i) { + rv = nghttp2_session_mem_recv(session, bufs.head->buf.pos + proclen, + nghttp2_buf_len(&bufs.head->buf) - proclen); + CU_ASSERT_FATAL(rv > 0); + proclen += rv; + CU_ASSERT(nghttp2_nv_equal(&bad_ansnv[i], &ud.nv)); + } + + rv = nghttp2_session_mem_recv(session, bufs.head->buf.pos + proclen, + nghttp2_buf_len(&bufs.head->buf) - proclen); + CU_ASSERT_FATAL(rv > 0); + /* header field "foo" must be ignored because it has illegal value. + So we have "bar" header field for 5th header. */ + CU_ASSERT(nghttp2_nv_equal(&bad_ansnv[4], &ud.nv)); + proclen += rv; + + CU_ASSERT(nghttp2_buf_len(&bufs.head->buf) == proclen); + + nghttp2_hd_deflate_free(&deflater); + nghttp2_session_del(session); + nghttp2_bufs_free(&bufs); +} + +void test_nghttp2_http_ignore_content_length(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + nghttp2_hd_deflater deflater; + nghttp2_mem *mem; + nghttp2_bufs bufs; + ssize_t rv; + const nghttp2_nv cl_resnv[] = {MAKE_NV(":status", "304"), + MAKE_NV("content-length", "20")}; + const nghttp2_nv conn_reqnv[] = {MAKE_NV(":authority", "localhost"), + MAKE_NV(":method", "CONNECT"), + MAKE_NV("content-length", "999999")}; + nghttp2_stream *stream; + + mem = nghttp2_mem_default(); + frame_pack_bufs_init(&bufs); + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.send_callback = null_send_callback; + + nghttp2_session_client_new(&session, &callbacks, NULL); + + nghttp2_hd_deflate_init(&deflater, mem); + + /* If status 304, content-length must be ignored */ + nghttp2_session_open_stream(session, 1, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, NGHTTP2_STREAM_OPENING, NULL); + + rv = pack_headers(&bufs, &deflater, 1, + NGHTTP2_FLAG_END_HEADERS | NGHTTP2_FLAG_END_STREAM, + cl_resnv, ARRLEN(cl_resnv), mem); + CU_ASSERT(0 == rv); + + rv = nghttp2_session_mem_recv(session, bufs.head->buf.pos, + nghttp2_buf_len(&bufs.head->buf)); + + CU_ASSERT(nghttp2_buf_len(&bufs.head->buf) == rv); + + CU_ASSERT(NULL == nghttp2_session_get_next_ob_item(session)); + + nghttp2_bufs_reset(&bufs); + + nghttp2_hd_deflate_free(&deflater); + nghttp2_session_del(session); + + /* If request method is CONNECT, content-length must be ignored */ + nghttp2_session_server_new(&session, &callbacks, NULL); + + nghttp2_hd_deflate_init(&deflater, mem); + + rv = pack_headers(&bufs, &deflater, 1, NGHTTP2_FLAG_END_HEADERS, conn_reqnv, + ARRLEN(conn_reqnv), mem); + + CU_ASSERT(0 == rv); + + rv = nghttp2_session_mem_recv(session, bufs.head->buf.pos, + nghttp2_buf_len(&bufs.head->buf)); + + CU_ASSERT(nghttp2_buf_len(&bufs.head->buf) == rv); + + CU_ASSERT(NULL == nghttp2_session_get_next_ob_item(session)); + + stream = nghttp2_session_get_stream(session, 1); + + CU_ASSERT(-1 == stream->content_length); + CU_ASSERT((stream->http_flags & NGHTTP2_HTTP_FLAG_METH_CONNECT) > 0); + + nghttp2_hd_deflate_free(&deflater); + nghttp2_session_del(session); + nghttp2_bufs_free(&bufs); +} + +void test_nghttp2_http_record_request_method(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + const nghttp2_nv conn_reqnv[] = {MAKE_NV(":method", "CONNECT"), + MAKE_NV(":authority", "localhost")}; + const nghttp2_nv conn_resnv[] = {MAKE_NV(":status", "200"), + MAKE_NV("content-length", "9999")}; + nghttp2_stream *stream; + ssize_t rv; + nghttp2_bufs bufs; + nghttp2_hd_deflater deflater; + nghttp2_mem *mem; + + mem = nghttp2_mem_default(); + frame_pack_bufs_init(&bufs); + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.send_callback = null_send_callback; + + nghttp2_session_client_new(&session, &callbacks, NULL); + + nghttp2_hd_deflate_init(&deflater, mem); + + CU_ASSERT(1 == nghttp2_submit_request(session, NULL, conn_reqnv, + ARRLEN(conn_reqnv), NULL, NULL)); + + CU_ASSERT(0 == nghttp2_session_send(session)); + + stream = nghttp2_session_get_stream(session, 1); + + CU_ASSERT(NGHTTP2_HTTP_FLAG_METH_CONNECT == stream->http_flags); + + rv = pack_headers(&bufs, &deflater, 1, NGHTTP2_FLAG_END_HEADERS, conn_resnv, + ARRLEN(conn_resnv), mem); + CU_ASSERT(0 == rv); + + rv = nghttp2_session_mem_recv(session, bufs.head->buf.pos, + nghttp2_buf_len(&bufs.head->buf)); + + CU_ASSERT(nghttp2_buf_len(&bufs.head->buf) == rv); + + CU_ASSERT((NGHTTP2_HTTP_FLAG_METH_CONNECT & stream->http_flags) > 0); + CU_ASSERT(-1 == stream->content_length); + + nghttp2_hd_deflate_free(&deflater); + nghttp2_session_del(session); + nghttp2_bufs_free(&bufs); +} + +void test_nghttp2_http_push_promise(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + nghttp2_hd_deflater deflater; + nghttp2_mem *mem; + nghttp2_bufs bufs; + ssize_t rv; + nghttp2_stream *stream; + const nghttp2_nv bad_reqnv[] = {MAKE_NV(":method", "GET")}; + nghttp2_outbound_item *item; + + mem = nghttp2_mem_default(); + frame_pack_bufs_init(&bufs); + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.send_callback = null_send_callback; + + /* good PUSH_PROMISE case */ + nghttp2_session_client_new(&session, &callbacks, NULL); + + nghttp2_hd_deflate_init(&deflater, mem); + + nghttp2_session_open_stream(session, 1, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, NGHTTP2_STREAM_OPENING, NULL); + + rv = pack_push_promise(&bufs, &deflater, 1, NGHTTP2_FLAG_END_HEADERS, 2, + reqnv, ARRLEN(reqnv), mem); + CU_ASSERT(0 == rv); + + rv = nghttp2_session_mem_recv(session, bufs.head->buf.pos, + nghttp2_buf_len(&bufs.head->buf)); + + CU_ASSERT(nghttp2_buf_len(&bufs.head->buf) == rv); + + CU_ASSERT(NULL == nghttp2_session_get_next_ob_item(session)); + + stream = nghttp2_session_get_stream(session, 2); + CU_ASSERT(NULL != stream); + + nghttp2_bufs_reset(&bufs); + + rv = pack_headers(&bufs, &deflater, 2, NGHTTP2_FLAG_END_HEADERS, resnv, + ARRLEN(resnv), mem); + + CU_ASSERT(0 == rv); + + rv = nghttp2_session_mem_recv(session, bufs.head->buf.pos, + nghttp2_buf_len(&bufs.head->buf)); + + CU_ASSERT(nghttp2_buf_len(&bufs.head->buf) == rv); + + CU_ASSERT(NULL == nghttp2_session_get_next_ob_item(session)); + + CU_ASSERT(200 == stream->status_code); + + nghttp2_bufs_reset(&bufs); + + /* PUSH_PROMISE lacks mandatory header */ + rv = pack_push_promise(&bufs, &deflater, 1, NGHTTP2_FLAG_END_HEADERS, 4, + bad_reqnv, ARRLEN(bad_reqnv), mem); + + CU_ASSERT(0 == rv); + + rv = nghttp2_session_mem_recv(session, bufs.head->buf.pos, + nghttp2_buf_len(&bufs.head->buf)); + + CU_ASSERT(nghttp2_buf_len(&bufs.head->buf) == rv); + + item = nghttp2_session_get_next_ob_item(session); + + CU_ASSERT(NGHTTP2_RST_STREAM == item->frame.hd.type); + CU_ASSERT(4 == item->frame.hd.stream_id); + + nghttp2_bufs_reset(&bufs); + + nghttp2_hd_deflate_free(&deflater); + nghttp2_session_del(session); + nghttp2_bufs_free(&bufs); +} diff --git a/tests/nghttp2_session_test.h b/tests/nghttp2_session_test.h new file mode 100644 index 0000000..e879c78 --- /dev/null +++ b/tests/nghttp2_session_test.h @@ -0,0 +1,138 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGHTTP2_SESSION_TEST_H +#define NGHTTP2_SESSION_TEST_H + +void test_nghttp2_session_recv(void); +void test_nghttp2_session_recv_invalid_stream_id(void); +void test_nghttp2_session_recv_invalid_frame(void); +void test_nghttp2_session_recv_eof(void); +void test_nghttp2_session_recv_data(void); +void test_nghttp2_session_recv_continuation(void); +void test_nghttp2_session_recv_headers_with_priority(void); +void test_nghttp2_session_recv_premature_headers(void); +void test_nghttp2_session_recv_unknown_frame(void); +void test_nghttp2_session_recv_unexpected_continuation(void); +void test_nghttp2_session_recv_settings_header_table_size(void); +void test_nghttp2_session_recv_too_large_frame_length(void); +void test_nghttp2_session_continue(void); +void test_nghttp2_session_add_frame(void); +void test_nghttp2_session_on_request_headers_received(void); +void test_nghttp2_session_on_response_headers_received(void); +void test_nghttp2_session_on_headers_received(void); +void test_nghttp2_session_on_push_response_headers_received(void); +void test_nghttp2_session_on_priority_received(void); +void test_nghttp2_session_on_rst_stream_received(void); +void test_nghttp2_session_on_settings_received(void); +void test_nghttp2_session_on_push_promise_received(void); +void test_nghttp2_session_on_ping_received(void); +void test_nghttp2_session_on_goaway_received(void); +void test_nghttp2_session_on_window_update_received(void); +void test_nghttp2_session_on_data_received(void); +void test_nghttp2_session_send_headers_start_stream(void); +void test_nghttp2_session_send_headers_reply(void); +void test_nghttp2_session_send_headers_frame_size_error(void); +void test_nghttp2_session_send_headers_push_reply(void); +void test_nghttp2_session_send_rst_stream(void); +void test_nghttp2_session_send_push_promise(void); +void test_nghttp2_session_is_my_stream_id(void); +void test_nghttp2_session_upgrade(void); +void test_nghttp2_session_reprioritize_stream(void); +void test_nghttp2_session_reprioritize_stream_with_idle_stream_dep(void); +void test_nghttp2_submit_data(void); +void test_nghttp2_submit_data_read_length_too_large(void); +void test_nghttp2_submit_data_read_length_smallest(void); +void test_nghttp2_submit_data_twice(void); +void test_nghttp2_submit_request_with_data(void); +void test_nghttp2_submit_request_without_data(void); +void test_nghttp2_submit_response_with_data(void); +void test_nghttp2_submit_response_without_data(void); +void test_nghttp2_submit_trailer(void); +void test_nghttp2_submit_headers_start_stream(void); +void test_nghttp2_submit_headers_reply(void); +void test_nghttp2_submit_headers_push_reply(void); +void test_nghttp2_submit_headers(void); +void test_nghttp2_submit_headers_continuation(void); +void test_nghttp2_submit_priority(void); +void test_nghttp2_submit_settings(void); +void test_nghttp2_submit_settings_update_local_window_size(void); +void test_nghttp2_submit_push_promise(void); +void test_nghttp2_submit_window_update(void); +void test_nghttp2_submit_window_update_local_window_size(void); +void test_nghttp2_submit_shutdown_notice(void); +void test_nghttp2_submit_invalid_nv(void); +void test_nghttp2_session_open_stream(void); +void test_nghttp2_session_open_stream_with_idle_stream_dep(void); +void test_nghttp2_session_get_next_ob_item(void); +void test_nghttp2_session_pop_next_ob_item(void); +void test_nghttp2_session_reply_fail(void); +void test_nghttp2_session_max_concurrent_streams(void); +void test_nghttp2_session_stream_close_on_headers_push(void); +void test_nghttp2_session_stop_data_with_rst_stream(void); +void test_nghttp2_session_defer_data(void); +void test_nghttp2_session_flow_control(void); +void test_nghttp2_session_flow_control_data_recv(void); +void test_nghttp2_session_flow_control_data_with_padding_recv(void); +void test_nghttp2_session_data_read_temporal_failure(void); +void test_nghttp2_session_on_stream_close(void); +void test_nghttp2_session_on_ctrl_not_send(void); +void test_nghttp2_session_get_outbound_queue_size(void); +void test_nghttp2_session_get_effective_local_window_size(void); +void test_nghttp2_session_set_option(void); +void test_nghttp2_session_data_backoff_by_high_pri_frame(void); +void test_nghttp2_session_pack_data_with_padding(void); +void test_nghttp2_session_pack_headers_with_padding(void); +void test_nghttp2_pack_settings_payload(void); +void test_nghttp2_session_stream_dep_add(void); +void test_nghttp2_session_stream_dep_remove(void); +void test_nghttp2_session_stream_dep_add_subtree(void); +void test_nghttp2_session_stream_dep_remove_subtree(void); +void test_nghttp2_session_stream_dep_all_your_stream_are_belong_to_us(void); +void test_nghttp2_session_stream_attach_item(void); +void test_nghttp2_session_stream_attach_item_subtree(void); +void test_nghttp2_session_keep_closed_stream(void); +void test_nghttp2_session_keep_idle_stream(void); +void test_nghttp2_session_detach_idle_stream(void); +void test_nghttp2_session_large_dep_tree(void); +void test_nghttp2_session_graceful_shutdown(void); +void test_nghttp2_session_on_header_temporal_failure(void); +void test_nghttp2_session_recv_client_magic(void); +void test_nghttp2_session_delete_data_item(void); +void test_nghttp2_session_open_idle_stream(void); +void test_nghttp2_session_cancel_reserved_remote(void); +void test_nghttp2_session_reset_pending_headers(void); +void test_nghttp2_session_send_data_callback(void); +void test_nghttp2_session_on_begin_headers_temporal_failure(void); +void test_nghttp2_http_mandatory_headers(void); +void test_nghttp2_http_content_length(void); +void test_nghttp2_http_content_length_mismatch(void); +void test_nghttp2_http_non_final_response(void); +void test_nghttp2_http_trailer_headers(void); +void test_nghttp2_http_ignore_regular_header(void); +void test_nghttp2_http_ignore_content_length(void); +void test_nghttp2_http_record_request_method(void); +void test_nghttp2_http_push_promise(void); + +#endif /* NGHTTP2_SESSION_TEST_H */ diff --git a/tests/nghttp2_stream_test.c b/tests/nghttp2_stream_test.c new file mode 100644 index 0000000..9b572d0 --- /dev/null +++ b/tests/nghttp2_stream_test.c @@ -0,0 +1,29 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "nghttp2_stream_test.h" + +#include + +#include "nghttp2_stream.h" diff --git a/tests/nghttp2_stream_test.h b/tests/nghttp2_stream_test.h new file mode 100644 index 0000000..508a8e1 --- /dev/null +++ b/tests/nghttp2_stream_test.h @@ -0,0 +1,28 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGHTTP2_STREAM_TEST_H +#define NGHTTP2_STREAM_TEST_H + +#endif /* NGHTTP2_STREAM_TEST_H */ diff --git a/tests/nghttp2_test_helper.c b/tests/nghttp2_test_helper.c new file mode 100644 index 0000000..a9cb10c --- /dev/null +++ b/tests/nghttp2_test_helper.c @@ -0,0 +1,303 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "nghttp2_test_helper.h" + +#include + +#include + +#include "nghttp2_helper.h" +#include "nghttp2_priority_spec.h" + +int unpack_framebuf(nghttp2_frame *frame, nghttp2_bufs *bufs) { + nghttp2_buf *buf; + + /* Assuming we have required data in first buffer. We don't decode + header block so, we don't mind its space */ + buf = &bufs->head->buf; + return unpack_frame(frame, buf->pos, nghttp2_buf_len(buf)); +} + +int unpack_frame(nghttp2_frame *frame, const uint8_t *in, size_t len) { + int rv = 0; + const uint8_t *payload = in + NGHTTP2_FRAME_HDLEN; + size_t payloadlen = len - NGHTTP2_FRAME_HDLEN; + size_t payloadoff; + nghttp2_mem *mem; + + mem = nghttp2_mem_default(); + + nghttp2_frame_unpack_frame_hd(&frame->hd, in); + switch (frame->hd.type) { + case NGHTTP2_HEADERS: + payloadoff = ((frame->hd.flags & NGHTTP2_FLAG_PADDED) > 0); + rv = nghttp2_frame_unpack_headers_payload( + &frame->headers, payload + payloadoff, payloadlen - payloadoff); + break; + case NGHTTP2_PRIORITY: + nghttp2_frame_unpack_priority_payload(&frame->priority, payload, + payloadlen); + break; + case NGHTTP2_RST_STREAM: + nghttp2_frame_unpack_rst_stream_payload(&frame->rst_stream, payload, + payloadlen); + break; + case NGHTTP2_SETTINGS: + rv = nghttp2_frame_unpack_settings_payload2( + &frame->settings.iv, &frame->settings.niv, payload, payloadlen, mem); + break; + case NGHTTP2_PUSH_PROMISE: + rv = nghttp2_frame_unpack_push_promise_payload(&frame->push_promise, + payload, payloadlen); + break; + case NGHTTP2_PING: + nghttp2_frame_unpack_ping_payload(&frame->ping, payload, payloadlen); + break; + case NGHTTP2_GOAWAY: + nghttp2_frame_unpack_goaway_payload2(&frame->goaway, payload, payloadlen, + mem); + break; + case NGHTTP2_WINDOW_UPDATE: + nghttp2_frame_unpack_window_update_payload(&frame->window_update, payload, + payloadlen); + break; + default: + /* Must not be reachable */ + assert(0); + } + return rv; +} + +int strmemeq(const char *a, const uint8_t *b, size_t bn) { + const uint8_t *c; + if (!a || !b) { + return 0; + } + c = b + bn; + for (; *a && b != c && *a == *b; ++a, ++b) + ; + return !*a && b == c; +} + +int nvnameeq(const char *a, nghttp2_nv *nv) { + return strmemeq(a, nv->name, nv->namelen); +} + +int nvvalueeq(const char *a, nghttp2_nv *nv) { + return strmemeq(a, nv->value, nv->valuelen); +} + +void nva_out_init(nva_out *out) { + memset(out->nva, 0, sizeof(out->nva)); + out->nvlen = 0; +} + +void nva_out_reset(nva_out *out, nghttp2_mem *mem) { + size_t i; + for (i = 0; i < out->nvlen; ++i) { + mem->free(out->nva[i].name, NULL); + mem->free(out->nva[i].value, NULL); + } + memset(out->nva, 0, sizeof(out->nva)); + out->nvlen = 0; +} + +void add_out(nva_out *out, nghttp2_nv *nv, nghttp2_mem *mem) { + nghttp2_nv *onv = &out->nva[out->nvlen]; + if (nv->namelen) { + onv->name = mem->malloc(nv->namelen, NULL); + memcpy(onv->name, nv->name, nv->namelen); + } else { + onv->name = NULL; + } + if (nv->valuelen) { + onv->value = mem->malloc(nv->valuelen, NULL); + memcpy(onv->value, nv->value, nv->valuelen); + } else { + onv->value = NULL; + } + onv->namelen = nv->namelen; + onv->valuelen = nv->valuelen; + + onv->flags = nv->flags; + + ++out->nvlen; +} + +ssize_t inflate_hd(nghttp2_hd_inflater *inflater, nva_out *out, + nghttp2_bufs *bufs, size_t offset, nghttp2_mem *mem) { + ssize_t rv; + nghttp2_nv nv; + int inflate_flags; + nghttp2_buf_chain *ci; + nghttp2_buf *buf; + nghttp2_buf bp; + int final; + size_t processed; + + processed = 0; + + for (ci = bufs->head; ci; ci = ci->next) { + buf = &ci->buf; + final = nghttp2_buf_len(buf) == 0 || ci->next == NULL; + bp = *buf; + + if (offset) { + ssize_t n; + + n = nghttp2_min((ssize_t)offset, nghttp2_buf_len(&bp)); + bp.pos += n; + offset -= n; + } + + for (;;) { + inflate_flags = 0; + rv = nghttp2_hd_inflate_hd(inflater, &nv, &inflate_flags, bp.pos, + nghttp2_buf_len(&bp), final); + + if (rv < 0) { + return rv; + } + + bp.pos += rv; + processed += rv; + + if (inflate_flags & NGHTTP2_HD_INFLATE_EMIT) { + if (out) { + add_out(out, &nv, mem); + } + } + if (inflate_flags & NGHTTP2_HD_INFLATE_FINAL) { + break; + } + } + } + + nghttp2_hd_inflate_end_headers(inflater); + + return processed; +} + +int pack_headers(nghttp2_bufs *bufs, nghttp2_hd_deflater *deflater, + int32_t stream_id, int flags, const nghttp2_nv *nva, + size_t nvlen, nghttp2_mem *mem) { + nghttp2_nv *dnva; + nghttp2_frame frame; + int rv; + + nghttp2_nv_array_copy(&dnva, nva, nvlen, mem); + + nghttp2_frame_headers_init(&frame.headers, flags, stream_id, + NGHTTP2_HCAT_HEADERS, NULL, dnva, nvlen); + rv = nghttp2_frame_pack_headers(bufs, &frame.headers, deflater); + + nghttp2_frame_headers_free(&frame.headers, mem); + + return rv; +} + +int pack_push_promise(nghttp2_bufs *bufs, nghttp2_hd_deflater *deflater, + int32_t stream_id, int flags, int32_t promised_stream_id, + const nghttp2_nv *nva, size_t nvlen, nghttp2_mem *mem) { + nghttp2_nv *dnva; + nghttp2_frame frame; + int rv; + + nghttp2_nv_array_copy(&dnva, nva, nvlen, mem); + + nghttp2_frame_push_promise_init(&frame.push_promise, flags, stream_id, + promised_stream_id, dnva, nvlen); + rv = nghttp2_frame_pack_push_promise(bufs, &frame.push_promise, deflater); + + nghttp2_frame_push_promise_free(&frame.push_promise, mem); + + return rv; +} + +int frame_pack_bufs_init(nghttp2_bufs *bufs) { + /* 1 for Pad Length */ + return nghttp2_bufs_init2(bufs, 4096, 16, NGHTTP2_FRAME_HDLEN + 1, + nghttp2_mem_default()); +} + +void bufs_large_init(nghttp2_bufs *bufs, size_t chunk_size) { + /* 1 for Pad Length */ + nghttp2_bufs_init2(bufs, chunk_size, 16, NGHTTP2_FRAME_HDLEN + 1, + nghttp2_mem_default()); +} + +static nghttp2_stream *open_stream_with_all(nghttp2_session *session, + int32_t stream_id, int32_t weight, + uint8_t exclusive, + nghttp2_stream *dep_stream) { + nghttp2_priority_spec pri_spec; + int32_t dep_stream_id; + + if (dep_stream) { + dep_stream_id = dep_stream->stream_id; + } else { + dep_stream_id = 0; + } + + nghttp2_priority_spec_init(&pri_spec, dep_stream_id, weight, exclusive); + + return nghttp2_session_open_stream(session, stream_id, + NGHTTP2_STREAM_FLAG_NONE, &pri_spec, + NGHTTP2_STREAM_OPENED, NULL); +} + +nghttp2_stream *open_stream(nghttp2_session *session, int32_t stream_id) { + return open_stream_with_all(session, stream_id, NGHTTP2_DEFAULT_WEIGHT, 0, + NULL); +} + +nghttp2_stream *open_stream_with_dep(nghttp2_session *session, + int32_t stream_id, + nghttp2_stream *dep_stream) { + return open_stream_with_all(session, stream_id, NGHTTP2_DEFAULT_WEIGHT, 0, + dep_stream); +} + +nghttp2_stream *open_stream_with_dep_weight(nghttp2_session *session, + int32_t stream_id, int32_t weight, + nghttp2_stream *dep_stream) { + return open_stream_with_all(session, stream_id, weight, 0, dep_stream); +} + +nghttp2_stream *open_stream_with_dep_excl(nghttp2_session *session, + int32_t stream_id, + nghttp2_stream *dep_stream) { + return open_stream_with_all(session, stream_id, NGHTTP2_DEFAULT_WEIGHT, 1, + dep_stream); +} + +nghttp2_outbound_item *create_data_ob_item(nghttp2_mem *mem) { + nghttp2_outbound_item *item; + + item = mem->malloc(sizeof(nghttp2_outbound_item), NULL); + memset(item, 0, sizeof(nghttp2_outbound_item)); + + return item; +} diff --git a/tests/nghttp2_test_helper.h b/tests/nghttp2_test_helper.h new file mode 100644 index 0000000..295c102 --- /dev/null +++ b/tests/nghttp2_test_helper.h @@ -0,0 +1,112 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGHTTP2_TEST_HELPER_H +#define NGHTTP2_TEST_HELPER_H + +#ifdef HAVE_CONFIG_H +#include +#endif /* HAVE_CONFIG_H */ + +#include "nghttp2_frame.h" +#include "nghttp2_hd.h" +#include "nghttp2_session.h" + +#define MAKE_NV(NAME, VALUE) \ + { \ + (uint8_t *)(NAME), (uint8_t *)(VALUE), sizeof((NAME)) - 1, \ + sizeof((VALUE)) - 1, NGHTTP2_NV_FLAG_NONE \ + } +#define ARRLEN(ARR) (sizeof(ARR) / sizeof(ARR[0])) + +#define assert_nv_equal(A, B, len, mem) \ + do { \ + size_t alloclen = sizeof(nghttp2_nv) * len; \ + const nghttp2_nv *sa = A, *sb = B; \ + nghttp2_nv *a = mem->malloc(alloclen, NULL); \ + nghttp2_nv *b = mem->malloc(alloclen, NULL); \ + ssize_t i_; \ + memcpy(a, sa, alloclen); \ + memcpy(b, sb, alloclen); \ + nghttp2_nv_array_sort(a, len); \ + nghttp2_nv_array_sort(b, len); \ + for (i_ = 0; i_ < (ssize_t)len; ++i_) { \ + CU_ASSERT(nghttp2_nv_equal(&a[i_], &b[i_])); \ + } \ + mem->free(b, NULL); \ + mem->free(a, NULL); \ + } while (0); + +int unpack_framebuf(nghttp2_frame *frame, nghttp2_bufs *bufs); + +int unpack_frame(nghttp2_frame *frame, const uint8_t *in, size_t len); + +int strmemeq(const char *a, const uint8_t *b, size_t bn); + +int nvnameeq(const char *a, nghttp2_nv *nv); + +int nvvalueeq(const char *a, nghttp2_nv *nv); + +typedef struct { + nghttp2_nv nva[256]; + size_t nvlen; +} nva_out; + +void nva_out_init(nva_out *out); +void nva_out_reset(nva_out *out, nghttp2_mem *mem); + +void add_out(nva_out *out, nghttp2_nv *nv, nghttp2_mem *mem); + +ssize_t inflate_hd(nghttp2_hd_inflater *inflater, nva_out *out, + nghttp2_bufs *bufs, size_t offset, nghttp2_mem *mem); + +int pack_headers(nghttp2_bufs *bufs, nghttp2_hd_deflater *deflater, + int32_t stream_id, int flags, const nghttp2_nv *nva, + size_t nvlen, nghttp2_mem *mem); + +int pack_push_promise(nghttp2_bufs *bufs, nghttp2_hd_deflater *deflater, + int32_t stream_id, int flags, int32_t promised_stream_id, + const nghttp2_nv *nva, size_t nvlen, nghttp2_mem *mem); + +int frame_pack_bufs_init(nghttp2_bufs *bufs); + +void bufs_large_init(nghttp2_bufs *bufs, size_t chunk_size); + +nghttp2_stream *open_stream(nghttp2_session *session, int32_t stream_id); + +nghttp2_stream *open_stream_with_dep(nghttp2_session *session, + int32_t stream_id, + nghttp2_stream *dep_stream); + +nghttp2_stream *open_stream_with_dep_weight(nghttp2_session *session, + int32_t stream_id, int32_t weight, + nghttp2_stream *dep_stream); + +nghttp2_stream *open_stream_with_dep_excl(nghttp2_session *session, + int32_t stream_id, + nghttp2_stream *dep_stream); + +nghttp2_outbound_item *create_data_ob_item(nghttp2_mem *mem); + +#endif /* NGHTTP2_TEST_HELPER_H */ diff --git a/tests/testdata/Makefile.am b/tests/testdata/Makefile.am new file mode 100644 index 0000000..ee38113 --- /dev/null +++ b/tests/testdata/Makefile.am @@ -0,0 +1,23 @@ +# nghttp2 - HTTP/2 C Library + +# Copyright (c) 2012 Tatsuhiro Tsujikawa + +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: + +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. + +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +EXTRA_DIST = cacert.pem index.html privkey.pem diff --git a/tests/testdata/Makefile.in b/tests/testdata/Makefile.in new file mode 100644 index 0000000..a40481f --- /dev/null +++ b/tests/testdata/Makefile.in @@ -0,0 +1,512 @@ +# Makefile.in generated by automake 1.14.1 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2013 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +# nghttp2 - HTTP/2 C Library + +# Copyright (c) 2012 Tatsuhiro Tsujikawa + +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: + +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +VPATH = @srcdir@ +am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)' +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +target_triplet = @target@ +subdir = tests/testdata +DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/ax_boost_asio.m4 \ + $(top_srcdir)/m4/ax_boost_base.m4 \ + $(top_srcdir)/m4/ax_boost_system.m4 \ + $(top_srcdir)/m4/ax_boost_thread.m4 \ + $(top_srcdir)/m4/ax_check_compile_flag.m4 \ + $(top_srcdir)/m4/ax_cxx_compile_stdcxx_11.m4 \ + $(top_srcdir)/m4/ax_have_epoll.m4 \ + $(top_srcdir)/m4/ax_python_devel.m4 \ + $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/libxml2.m4 \ + $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ + $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +SOURCES = +DIST_SOURCES = +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +APPLDFLAGS = @APPLDFLAGS@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +BOOST_ASIO_LIB = @BOOST_ASIO_LIB@ +BOOST_CPPFLAGS = @BOOST_CPPFLAGS@ +BOOST_LDFLAGS = @BOOST_LDFLAGS@ +BOOST_SYSTEM_LIB = @BOOST_SYSTEM_LIB@ +BOOST_THREAD_LIB = @BOOST_THREAD_LIB@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CUNIT_CFLAGS = @CUNIT_CFLAGS@ +CUNIT_LIBS = @CUNIT_LIBS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +CYTHON = @CYTHON@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DLLTOOL = @DLLTOOL@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +GREP = @GREP@ +HAVE_CXX11 = @HAVE_CXX11@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +JANSSON_CFLAGS = @JANSSON_CFLAGS@ +JANSSON_LIBS = @JANSSON_LIBS@ +JEMALLOC_LIBS = @JEMALLOC_LIBS@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBEVENT_OPENSSL_CFLAGS = @LIBEVENT_OPENSSL_CFLAGS@ +LIBEVENT_OPENSSL_LIBS = @LIBEVENT_OPENSSL_LIBS@ +LIBEV_CFLAGS = @LIBEV_CFLAGS@ +LIBEV_LIBS = @LIBEV_LIBS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBSPDYLAY_CFLAGS = @LIBSPDYLAY_CFLAGS@ +LIBSPDYLAY_LIBS = @LIBSPDYLAY_LIBS@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +LT_AGE = @LT_AGE@ +LT_CURRENT = @LT_CURRENT@ +LT_REVISION = @LT_REVISION@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MKDIR_P = @MKDIR_P@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OPENSSL_CFLAGS = @OPENSSL_CFLAGS@ +OPENSSL_LIBS = @OPENSSL_LIBS@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PACKAGE_VERSION_NUM = @PACKAGE_VERSION_NUM@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PKG_CONFIG = @PKG_CONFIG@ +PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ +PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ +PYTHON = @PYTHON@ +PYTHON_CPPFLAGS = @PYTHON_CPPFLAGS@ +PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@ +PYTHON_EXTRA_LDFLAGS = @PYTHON_EXTRA_LDFLAGS@ +PYTHON_EXTRA_LIBS = @PYTHON_EXTRA_LIBS@ +PYTHON_LDFLAGS = @PYTHON_LDFLAGS@ +PYTHON_PLATFORM = @PYTHON_PLATFORM@ +PYTHON_PREFIX = @PYTHON_PREFIX@ +PYTHON_SITE_PKG = @PYTHON_SITE_PKG@ +PYTHON_VERSION = @PYTHON_VERSION@ +RANLIB = @RANLIB@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +TESTLDADD = @TESTLDADD@ +VERSION = @VERSION@ +WARNCFLAGS = @WARNCFLAGS@ +XML2_CONFIG = @XML2_CONFIG@ +XML_CPPFLAGS = @XML_CPPFLAGS@ +XML_LIBS = @XML_LIBS@ +ZLIB_CFLAGS = @ZLIB_CFLAGS@ +ZLIB_LIBS = @ZLIB_LIBS@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +pkgpyexecdir = @pkgpyexecdir@ +pkgpythondir = @pkgpythondir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +pyexecdir = @pyexecdir@ +pythondir = @pythondir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target = @target@ +target_alias = @target_alias@ +target_cpu = @target_cpu@ +target_os = @target_os@ +target_vendor = @target_vendor@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ + +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +EXTRA_DIST = cacert.pem index.html privkey.pem +all: all-am + +.SUFFIXES: +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu tests/testdata/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu tests/testdata/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs +tags TAGS: + +ctags CTAGS: + +cscope cscopelist: + + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile +installdirs: +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic clean-libtool mostlyclean-am + +distclean: distclean-am + -rm -f Makefile +distclean-am: clean-am distclean-generic + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-generic mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: + +.MAKE: install-am install-strip + +.PHONY: all all-am check check-am clean clean-generic clean-libtool \ + cscopelist-am ctags-am distclean distclean-generic \ + distclean-libtool distdir dvi dvi-am html html-am info info-am \ + install install-am install-data install-data-am install-dvi \ + install-dvi-am install-exec install-exec-am install-html \ + install-html-am install-info install-info-am install-man \ + install-pdf install-pdf-am install-ps install-ps-am \ + install-strip installcheck installcheck-am installdirs \ + maintainer-clean maintainer-clean-generic mostlyclean \ + mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ + tags-am uninstall uninstall-am + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/tests/testdata/cacert.pem b/tests/testdata/cacert.pem new file mode 100644 index 0000000..e462790 --- /dev/null +++ b/tests/testdata/cacert.pem @@ -0,0 +1,14 @@ +-----BEGIN CERTIFICATE----- +MIICKTCCAdOgAwIBAgIJAIsolheWrwMZMA0GCSqGSIb3DQEBBQUAMHAxCzAJBgNV +BAYTAlVTMQswCQYDVQQIDAJDQTENMAsGA1UEBwwEQ2l0eTESMBAGA1UECgwJU3Bk +eSBUZXN0MRIwEAYDVQQDDAlsb2NhbGhvc3QxHTAbBgkqhkiG9w0BCQEWDnNwZHlA +bG9jYWxob3N0MB4XDTEyMDMwMTE5MTI0NVoXDTIzMDUxOTE5MTI0NVowcDELMAkG +A1UEBhMCVVMxCzAJBgNVBAgMAkNBMQ0wCwYDVQQHDARDaXR5MRIwEAYDVQQKDAlT +cGR5IFRlc3QxEjAQBgNVBAMMCWxvY2FsaG9zdDEdMBsGCSqGSIb3DQEJARYOc3Bk +eUBsb2NhbGhvc3QwXDANBgkqhkiG9w0BAQEFAANLADBIAkEAw/2MgzAdlJDm29qH +ZlAibgs9mH+8keOtsRrb4B1PiCcZoHvN9eCVZ4WnzT+0zhHF+nO3YfwVFVC3w7TF +7fLB3QIDAQABo1AwTjAdBgNVHQ4EFgQUVP2Jw9RX6BB76aV5x2qk5qsrAIQwHwYD +VR0jBBgwFoAUVP2Jw9RX6BB76aV5x2qk5qsrAIQwDAYDVR0TBAUwAwEB/zANBgkq +hkiG9w0BAQUFAANBAKd9M5FzQLEZW1KPe9/XNZlgxZ2g3EC5Krxo5I4Ul3MnIYS9 +u4K8t/iprhgOzjFH6+8LVk9v0Za+gU+K43CpUo4= +-----END CERTIFICATE----- diff --git a/tests/testdata/index.html b/tests/testdata/index.html new file mode 100644 index 0000000..cdd75fc --- /dev/null +++ b/tests/testdata/index.html @@ -0,0 +1 @@ +small diff --git a/tests/testdata/privkey.pem b/tests/testdata/privkey.pem new file mode 100644 index 0000000..0ab825b --- /dev/null +++ b/tests/testdata/privkey.pem @@ -0,0 +1,9 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIBOwIBAAJBAMP9jIMwHZSQ5tvah2ZQIm4LPZh/vJHjrbEa2+AdT4gnGaB7zfXg +lWeFp80/tM4Rxfpzt2H8FRVQt8O0xe3ywd0CAwEAAQJBAIQ8PGP/QNYOdlT8OsLj +aneJCgQsm1Rro7ONBbFO1WxslvA6+uJsx4Rs8zLiS8cyqmJ/lmGa7zhwYSOvFQPa +XgECIQDgIcgM/2C67peTm1diKKIoGVVKFCfdRi+Dje6mTl2TQQIhAN/bcFWbG73j +cUVlIsr9Wk1dJzjPPWKeyirF1qd/WbOdAiEApTsCOeLCssxV3jF02B5QfPNAFx6I +zO2C9Z7awque/IECIGCHW3VOoTPMs7dc2Rf3D810cclJdArmtf6juOAZRjDxAiBS +AC+H685IBJ99N5nCbF9NWYIVSkuiKVQ8POYVZX+0Jg== +-----END RSA PRIVATE KEY----- diff --git a/third-party/Makefile.am b/third-party/Makefile.am new file mode 100644 index 0000000..aa8832d --- /dev/null +++ b/third-party/Makefile.am @@ -0,0 +1,32 @@ +# nghttp2 - HTTP/2 C Library + +# Copyright (c) 2014 Tatsuhiro Tsujikawa + +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: + +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. + +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +AM_CPPFLAGS = -Wall @DEFS@ + +noinst_LTLIBRARIES = libhttp-parser.la +libhttp_parser_la_SOURCES = \ + http-parser/http_parser.c \ + http-parser/http_parser.h + + +dist_pkgdata_SCRIPTS = h2o/fetch-ocsp-response diff --git a/third-party/Makefile.in b/third-party/Makefile.in new file mode 100644 index 0000000..352a959 --- /dev/null +++ b/third-party/Makefile.in @@ -0,0 +1,751 @@ +# Makefile.in generated by automake 1.14.1 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2013 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +# nghttp2 - HTTP/2 C Library + +# Copyright (c) 2014 Tatsuhiro Tsujikawa + +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: + +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. + +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +VPATH = @srcdir@ +am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)' +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +target_triplet = @target@ +subdir = third-party +DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \ + $(dist_pkgdata_SCRIPTS) $(top_srcdir)/depcomp +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/ax_boost_asio.m4 \ + $(top_srcdir)/m4/ax_boost_base.m4 \ + $(top_srcdir)/m4/ax_boost_system.m4 \ + $(top_srcdir)/m4/ax_boost_thread.m4 \ + $(top_srcdir)/m4/ax_check_compile_flag.m4 \ + $(top_srcdir)/m4/ax_cxx_compile_stdcxx_11.m4 \ + $(top_srcdir)/m4/ax_have_epoll.m4 \ + $(top_srcdir)/m4/ax_python_devel.m4 \ + $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/libxml2.m4 \ + $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ + $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +LTLIBRARIES = $(noinst_LTLIBRARIES) +libhttp_parser_la_LIBADD = +am__dirstamp = $(am__leading_dot)dirstamp +am_libhttp_parser_la_OBJECTS = http-parser/http_parser.lo +libhttp_parser_la_OBJECTS = $(am_libhttp_parser_la_OBJECTS) +AM_V_lt = $(am__v_lt_@AM_V@) +am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) +am__v_lt_0 = --silent +am__v_lt_1 = +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +am__uninstall_files_from_dir = { \ + test -z "$$files" \ + || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ + || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ + $(am__cd) "$$dir" && rm -f $$files; }; \ + } +am__installdirs = "$(DESTDIR)$(pkgdatadir)" +SCRIPTS = $(dist_pkgdata_SCRIPTS) +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__depfiles_maybe = depfiles +am__mv = mv -f +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CFLAGS) $(CFLAGS) +AM_V_CC = $(am__v_CC_@AM_V@) +am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) +am__v_CC_0 = @echo " CC " $@; +am__v_CC_1 = +CCLD = $(CC) +LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CCLD = $(am__v_CCLD_@AM_V@) +am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) +am__v_CCLD_0 = @echo " CCLD " $@; +am__v_CCLD_1 = +SOURCES = $(libhttp_parser_la_SOURCES) +DIST_SOURCES = $(libhttp_parser_la_SOURCES) +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +ETAGS = etags +CTAGS = ctags +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +APPLDFLAGS = @APPLDFLAGS@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +BOOST_ASIO_LIB = @BOOST_ASIO_LIB@ +BOOST_CPPFLAGS = @BOOST_CPPFLAGS@ +BOOST_LDFLAGS = @BOOST_LDFLAGS@ +BOOST_SYSTEM_LIB = @BOOST_SYSTEM_LIB@ +BOOST_THREAD_LIB = @BOOST_THREAD_LIB@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CUNIT_CFLAGS = @CUNIT_CFLAGS@ +CUNIT_LIBS = @CUNIT_LIBS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +CYTHON = @CYTHON@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DLLTOOL = @DLLTOOL@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +GREP = @GREP@ +HAVE_CXX11 = @HAVE_CXX11@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +JANSSON_CFLAGS = @JANSSON_CFLAGS@ +JANSSON_LIBS = @JANSSON_LIBS@ +JEMALLOC_LIBS = @JEMALLOC_LIBS@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBEVENT_OPENSSL_CFLAGS = @LIBEVENT_OPENSSL_CFLAGS@ +LIBEVENT_OPENSSL_LIBS = @LIBEVENT_OPENSSL_LIBS@ +LIBEV_CFLAGS = @LIBEV_CFLAGS@ +LIBEV_LIBS = @LIBEV_LIBS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBSPDYLAY_CFLAGS = @LIBSPDYLAY_CFLAGS@ +LIBSPDYLAY_LIBS = @LIBSPDYLAY_LIBS@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +LT_AGE = @LT_AGE@ +LT_CURRENT = @LT_CURRENT@ +LT_REVISION = @LT_REVISION@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MKDIR_P = @MKDIR_P@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OPENSSL_CFLAGS = @OPENSSL_CFLAGS@ +OPENSSL_LIBS = @OPENSSL_LIBS@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PACKAGE_VERSION_NUM = @PACKAGE_VERSION_NUM@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PKG_CONFIG = @PKG_CONFIG@ +PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ +PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ +PYTHON = @PYTHON@ +PYTHON_CPPFLAGS = @PYTHON_CPPFLAGS@ +PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@ +PYTHON_EXTRA_LDFLAGS = @PYTHON_EXTRA_LDFLAGS@ +PYTHON_EXTRA_LIBS = @PYTHON_EXTRA_LIBS@ +PYTHON_LDFLAGS = @PYTHON_LDFLAGS@ +PYTHON_PLATFORM = @PYTHON_PLATFORM@ +PYTHON_PREFIX = @PYTHON_PREFIX@ +PYTHON_SITE_PKG = @PYTHON_SITE_PKG@ +PYTHON_VERSION = @PYTHON_VERSION@ +RANLIB = @RANLIB@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +TESTLDADD = @TESTLDADD@ +VERSION = @VERSION@ +WARNCFLAGS = @WARNCFLAGS@ +XML2_CONFIG = @XML2_CONFIG@ +XML_CPPFLAGS = @XML_CPPFLAGS@ +XML_LIBS = @XML_LIBS@ +ZLIB_CFLAGS = @ZLIB_CFLAGS@ +ZLIB_LIBS = @ZLIB_LIBS@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +pkgpyexecdir = @pkgpyexecdir@ +pkgpythondir = @pkgpythondir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +pyexecdir = @pyexecdir@ +pythondir = @pythondir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target = @target@ +target_alias = @target_alias@ +target_cpu = @target_cpu@ +target_os = @target_os@ +target_vendor = @target_vendor@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +AM_CPPFLAGS = -Wall @DEFS@ +noinst_LTLIBRARIES = libhttp-parser.la +libhttp_parser_la_SOURCES = \ + http-parser/http_parser.c \ + http-parser/http_parser.h + +dist_pkgdata_SCRIPTS = h2o/fetch-ocsp-response +all: all-am + +.SUFFIXES: +.SUFFIXES: .c .lo .o .obj +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu third-party/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu third-party/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): + +clean-noinstLTLIBRARIES: + -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES) + @list='$(noinst_LTLIBRARIES)'; \ + locs=`for p in $$list; do echo $$p; done | \ + sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ + sort -u`; \ + test -z "$$locs" || { \ + echo rm -f $${locs}; \ + rm -f $${locs}; \ + } +http-parser/$(am__dirstamp): + @$(MKDIR_P) http-parser + @: > http-parser/$(am__dirstamp) +http-parser/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) http-parser/$(DEPDIR) + @: > http-parser/$(DEPDIR)/$(am__dirstamp) +http-parser/http_parser.lo: http-parser/$(am__dirstamp) \ + http-parser/$(DEPDIR)/$(am__dirstamp) + +libhttp-parser.la: $(libhttp_parser_la_OBJECTS) $(libhttp_parser_la_DEPENDENCIES) $(EXTRA_libhttp_parser_la_DEPENDENCIES) + $(AM_V_CCLD)$(LINK) $(libhttp_parser_la_OBJECTS) $(libhttp_parser_la_LIBADD) $(LIBS) +install-dist_pkgdataSCRIPTS: $(dist_pkgdata_SCRIPTS) + @$(NORMAL_INSTALL) + @list='$(dist_pkgdata_SCRIPTS)'; test -n "$(pkgdatadir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(pkgdatadir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(pkgdatadir)" || exit 1; \ + fi; \ + for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + if test -f "$$d$$p"; then echo "$$d$$p"; echo "$$p"; else :; fi; \ + done | \ + sed -e 'p;s,.*/,,;n' \ + -e 'h;s|.*|.|' \ + -e 'p;x;s,.*/,,;$(transform)' | sed 'N;N;N;s,\n, ,g' | \ + $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1; } \ + { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ + if ($$2 == $$4) { files[d] = files[d] " " $$1; \ + if (++n[d] == $(am__install_max)) { \ + print "f", d, files[d]; n[d] = 0; files[d] = "" } } \ + else { print "f", d "/" $$4, $$1 } } \ + END { for (d in files) print "f", d, files[d] }' | \ + while read type dir files; do \ + if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ + test -z "$$files" || { \ + echo " $(INSTALL_SCRIPT) $$files '$(DESTDIR)$(pkgdatadir)$$dir'"; \ + $(INSTALL_SCRIPT) $$files "$(DESTDIR)$(pkgdatadir)$$dir" || exit $$?; \ + } \ + ; done + +uninstall-dist_pkgdataSCRIPTS: + @$(NORMAL_UNINSTALL) + @list='$(dist_pkgdata_SCRIPTS)'; test -n "$(pkgdatadir)" || exit 0; \ + files=`for p in $$list; do echo "$$p"; done | \ + sed -e 's,.*/,,;$(transform)'`; \ + dir='$(DESTDIR)$(pkgdatadir)'; $(am__uninstall_files_from_dir) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + -rm -f http-parser/*.$(OBJEXT) + -rm -f http-parser/*.lo + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@http-parser/$(DEPDIR)/http_parser.Plo@am__quote@ + +.c.o: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\ +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< + +.c.obj: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\ +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.c.lo: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\ +@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + -rm -rf http-parser/.libs http-parser/_libs + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-am +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + $(am__define_uniq_tagged_files); \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: ctags-am + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" +cscopelist: cscopelist-am + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(LTLIBRARIES) $(SCRIPTS) +installdirs: + for dir in "$(DESTDIR)$(pkgdatadir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + -rm -f http-parser/$(DEPDIR)/$(am__dirstamp) + -rm -f http-parser/$(am__dirstamp) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \ + mostlyclean-am + +distclean: distclean-am + -rm -rf http-parser/$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: install-dist_pkgdataSCRIPTS + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -rf http-parser/$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-dist_pkgdataSCRIPTS + +.MAKE: install-am install-strip + +.PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ + clean-libtool clean-noinstLTLIBRARIES cscopelist-am ctags \ + ctags-am distclean distclean-compile distclean-generic \ + distclean-libtool distclean-tags distdir dvi dvi-am html \ + html-am info info-am install install-am install-data \ + install-data-am install-dist_pkgdataSCRIPTS install-dvi \ + install-dvi-am install-exec install-exec-am install-html \ + install-html-am install-info install-info-am install-man \ + install-pdf install-pdf-am install-ps install-ps-am \ + install-strip installcheck installcheck-am installdirs \ + maintainer-clean maintainer-clean-generic mostlyclean \ + mostlyclean-compile mostlyclean-generic mostlyclean-libtool \ + pdf pdf-am ps ps-am tags tags-am uninstall uninstall-am \ + uninstall-dist_pkgdataSCRIPTS + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/third-party/h2o/fetch-ocsp-response b/third-party/h2o/fetch-ocsp-response new file mode 100755 index 0000000..9272dae --- /dev/null +++ b/third-party/h2o/fetch-ocsp-response @@ -0,0 +1,150 @@ +#! /bin/sh +exec perl -x $0 "$@" +#! perl + +# Copyright (c) 2015 DeNA Co., Ltd. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +# IN THE SOFTWARE. + +use strict; +use warnings; +use File::Temp qw(tempdir); +use Getopt::Long; + +# from sysexits.h +use constant EX_TEMPFAIL => 75; + +my ($issuer_fn, $opt_help); +my $openssl_cmd = 'openssl'; + +GetOptions( + "issuer=s" => \$issuer_fn, + "openssl=s", => \$openssl_cmd, + help => \$opt_help, +) or exit(1); +if ($opt_help) { + print << "EOT"; +Usage: $0 [] + +Options: + --issuer issuer certificate (if omitted, is extracted from the + certificate chain) + --openssl openssl command to use (default: "openssl") + --help prints this help + +The command issues an OCSP request for given server certificate, verifies the +response and prints the resulting DER. + +The command exits 0 if successful, or 75 (EX_TEMPFAIL) on temporary error. +Other exit codes may be returned in case of hard errors. + +EOT + exit(0); +} + +die "no certificate file\n" + if @ARGV == 0; +my $cert_fn = shift @ARGV; + +my $tempdir = tempdir(CLEANUP => 1); + +my $openssl_version = run_openssl("version"); +chomp $openssl_version; +print STDERR "fetch-ocsp-response (using $openssl_version)\n"; + +# obtain ocsp uri +my $ocsp_uri = run_openssl("x509 -in $cert_fn -noout -ocsp_uri"); +chomp $ocsp_uri; +die "failed to extract ocsp URI from $cert_fn\n" + if $ocsp_uri !~ m{^https?://}; +my($ocsp_host) = $ocsp_uri =~ m{^https?://([^/:]+)}; + +# save issuer certificate +if (! defined $issuer_fn) { + my $chain = read_file($cert_fn); + $chain =~ m{-----END CERTIFICATE-----.*?(-----BEGIN CERTIFICATE-----.*?-----END CERTIFICATE-----)}s + or die "--issuer option was not used, and failed to extract issuer certificate from the certificate\n"; + $issuer_fn = "$tempdir/issuer.crt"; + write_file($issuer_fn, "$1\n"); +} + +# obtain response (without verification) +print STDERR "sending OCSP request to $ocsp_uri\n"; +my $resp = run_openssl( + "ocsp -issuer $issuer_fn -cert $cert_fn -url $ocsp_uri" + . ($openssl_version =~ /^OpenSSL 1\./is ? " -header Host $ocsp_host" : "") + . " -noverify -respout $tempdir/resp.der " . join(' ', @ARGV), + 1, +); +print STDERR $resp; + +# verify the response +print STDERR "verifying the response signature\n"; +my $success; +for my $args ( + # try from exotic options + "-VAfile $issuer_fn", # for comodo + "-partial_chain -trusted_first -CAfile $issuer_fn", # these options are only available in OpenSSL >= 1.0.2 + "-CAfile $issuer_fn", # for OpenSSL <= 1.0.1 +) { + if (system("$openssl_cmd ocsp -respin $tempdir/resp.der $args > $tempdir/verify.out 2>&1") == 0) { + print STDERR "verify OK (used: $args)\n"; + $success = 1; + last; + } +} +if (! $success) { + print STDERR read_file("$tempdir/verify.out"); + tempfail("failed to verify the response\n"); +} + +# success +print read_file("$tempdir/resp.der"); +exit 0; + +sub run_openssl { + my ($args, $tempfail) = @_; + open my $fh, "-|", "$openssl_cmd $args" + or die "failed to invoke $openssl_cmd:$!"; + my $resp = do { local $/; <$fh> }; + close $fh + or ($tempfail ? \&tempfail : \&die)->("OpenSSL exitted abnormally: $openssl_cmd $args:$!"); + $resp; +} + +sub read_file { + my $fn = shift; + open my $fh, "<", $fn + or die "failed to open file:$fn:$!"; + local $/; + <$fh>; +} + +sub write_file { + my ($fn, $data) = @_; + open my $fh, ">", $fn + or die "failed to open file:$fn:$!"; + print $fh $data; + close $fh; +} + +sub tempfail { + print STDERR @_; + exit EX_TEMPFAIL; +} diff --git a/third-party/http-parser/http_parser.c b/third-party/http-parser/http_parser.c new file mode 100644 index 0000000..aa6310f --- /dev/null +++ b/third-party/http-parser/http_parser.c @@ -0,0 +1,2418 @@ +/* Based on src/http/ngx_http_parse.c from NGINX copyright Igor Sysoev + * + * Additional changes are licensed under the same terms as NGINX and + * copyright Joyent, Inc. and other Node contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +#include "http_parser.h" +#include +#include +#include +#include +#include +#include + +#ifndef ULLONG_MAX +# define ULLONG_MAX ((uint64_t) -1) /* 2^64-1 */ +#endif + +#ifndef MIN +# define MIN(a,b) ((a) < (b) ? (a) : (b)) +#endif + +#ifndef ARRAY_SIZE +# define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) +#endif + +#ifndef BIT_AT +# define BIT_AT(a, i) \ + (!!((unsigned int) (a)[(unsigned int) (i) >> 3] & \ + (1 << ((unsigned int) (i) & 7)))) +#endif + +#ifndef ELEM_AT +# define ELEM_AT(a, i, v) ((unsigned int) (i) < ARRAY_SIZE(a) ? (a)[(i)] : (v)) +#endif + +#define SET_ERRNO(e) \ +do { \ + parser->http_errno = (e); \ +} while(0) + +#define CURRENT_STATE() p_state +#define UPDATE_STATE(V) p_state = (enum state) (V); +#define RETURN(V) \ +do { \ + parser->state = CURRENT_STATE(); \ + return (V); \ +} while (0); +#define REEXECUTE() \ + goto reexecute; \ + + +#ifdef __GNUC__ +# define LIKELY(X) __builtin_expect(!!(X), 1) +# define UNLIKELY(X) __builtin_expect(!!(X), 0) +#else +# define LIKELY(X) (X) +# define UNLIKELY(X) (X) +#endif + + +/* Run the notify callback FOR, returning ER if it fails */ +#define CALLBACK_NOTIFY_(FOR, ER) \ +do { \ + assert(HTTP_PARSER_ERRNO(parser) == HPE_OK); \ + \ + if (LIKELY(settings->on_##FOR)) { \ + parser->state = CURRENT_STATE(); \ + if (UNLIKELY(0 != settings->on_##FOR(parser))) { \ + SET_ERRNO(HPE_CB_##FOR); \ + } \ + UPDATE_STATE(parser->state); \ + \ + /* We either errored above or got paused; get out */ \ + if (UNLIKELY(HTTP_PARSER_ERRNO(parser) != HPE_OK)) { \ + return (ER); \ + } \ + } \ +} while (0) + +/* Run the notify callback FOR and consume the current byte */ +#define CALLBACK_NOTIFY(FOR) CALLBACK_NOTIFY_(FOR, p - data + 1) + +/* Run the notify callback FOR and don't consume the current byte */ +#define CALLBACK_NOTIFY_NOADVANCE(FOR) CALLBACK_NOTIFY_(FOR, p - data) + +/* Run data callback FOR with LEN bytes, returning ER if it fails */ +#define CALLBACK_DATA_(FOR, LEN, ER) \ +do { \ + assert(HTTP_PARSER_ERRNO(parser) == HPE_OK); \ + \ + if (FOR##_mark) { \ + if (LIKELY(settings->on_##FOR)) { \ + parser->state = CURRENT_STATE(); \ + if (UNLIKELY(0 != \ + settings->on_##FOR(parser, FOR##_mark, (LEN)))) { \ + SET_ERRNO(HPE_CB_##FOR); \ + } \ + UPDATE_STATE(parser->state); \ + \ + /* We either errored above or got paused; get out */ \ + if (UNLIKELY(HTTP_PARSER_ERRNO(parser) != HPE_OK)) { \ + return (ER); \ + } \ + } \ + FOR##_mark = NULL; \ + } \ +} while (0) + +/* Run the data callback FOR and consume the current byte */ +#define CALLBACK_DATA(FOR) \ + CALLBACK_DATA_(FOR, p - FOR##_mark, p - data + 1) + +/* Run the data callback FOR and don't consume the current byte */ +#define CALLBACK_DATA_NOADVANCE(FOR) \ + CALLBACK_DATA_(FOR, p - FOR##_mark, p - data) + +/* Set the mark FOR; non-destructive if mark is already set */ +#define MARK(FOR) \ +do { \ + if (!FOR##_mark) { \ + FOR##_mark = p; \ + } \ +} while (0) + +/* Don't allow the total size of the HTTP headers (including the status + * line) to exceed HTTP_MAX_HEADER_SIZE. This check is here to protect + * embedders against denial-of-service attacks where the attacker feeds + * us a never-ending header that the embedder keeps buffering. + * + * This check is arguably the responsibility of embedders but we're doing + * it on the embedder's behalf because most won't bother and this way we + * make the web a little safer. HTTP_MAX_HEADER_SIZE is still far bigger + * than any reasonable request or response so this should never affect + * day-to-day operation. + */ +#define COUNT_HEADER_SIZE(V) \ +do { \ + parser->nread += (V); \ + if (UNLIKELY(parser->nread > (HTTP_MAX_HEADER_SIZE))) { \ + SET_ERRNO(HPE_HEADER_OVERFLOW); \ + goto error; \ + } \ +} while (0) + + +#define PROXY_CONNECTION "proxy-connection" +#define CONNECTION "connection" +#define CONTENT_LENGTH "content-length" +#define TRANSFER_ENCODING "transfer-encoding" +#define UPGRADE "upgrade" +#define CHUNKED "chunked" +#define KEEP_ALIVE "keep-alive" +#define CLOSE "close" + + +static const char *method_strings[] = + { +#define XX(num, name, string) #string, + HTTP_METHOD_MAP(XX) +#undef XX + }; + + +/* Tokens as defined by rfc 2616. Also lowercases them. + * token = 1* + * separators = "(" | ")" | "<" | ">" | "@" + * | "," | ";" | ":" | "\" | <"> + * | "/" | "[" | "]" | "?" | "=" + * | "{" | "}" | SP | HT + */ +static const char tokens[256] = { +/* 0 nul 1 soh 2 stx 3 etx 4 eot 5 enq 6 ack 7 bel */ + 0, 0, 0, 0, 0, 0, 0, 0, +/* 8 bs 9 ht 10 nl 11 vt 12 np 13 cr 14 so 15 si */ + 0, 0, 0, 0, 0, 0, 0, 0, +/* 16 dle 17 dc1 18 dc2 19 dc3 20 dc4 21 nak 22 syn 23 etb */ + 0, 0, 0, 0, 0, 0, 0, 0, +/* 24 can 25 em 26 sub 27 esc 28 fs 29 gs 30 rs 31 us */ + 0, 0, 0, 0, 0, 0, 0, 0, +/* 32 sp 33 ! 34 " 35 # 36 $ 37 % 38 & 39 ' */ + 0, '!', 0, '#', '$', '%', '&', '\'', +/* 40 ( 41 ) 42 * 43 + 44 , 45 - 46 . 47 / */ + 0, 0, '*', '+', 0, '-', '.', 0, +/* 48 0 49 1 50 2 51 3 52 4 53 5 54 6 55 7 */ + '0', '1', '2', '3', '4', '5', '6', '7', +/* 56 8 57 9 58 : 59 ; 60 < 61 = 62 > 63 ? */ + '8', '9', 0, 0, 0, 0, 0, 0, +/* 64 @ 65 A 66 B 67 C 68 D 69 E 70 F 71 G */ + 0, 'a', 'b', 'c', 'd', 'e', 'f', 'g', +/* 72 H 73 I 74 J 75 K 76 L 77 M 78 N 79 O */ + 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', +/* 80 P 81 Q 82 R 83 S 84 T 85 U 86 V 87 W */ + 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', +/* 88 X 89 Y 90 Z 91 [ 92 \ 93 ] 94 ^ 95 _ */ + 'x', 'y', 'z', 0, 0, 0, '^', '_', +/* 96 ` 97 a 98 b 99 c 100 d 101 e 102 f 103 g */ + '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g', +/* 104 h 105 i 106 j 107 k 108 l 109 m 110 n 111 o */ + 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', +/* 112 p 113 q 114 r 115 s 116 t 117 u 118 v 119 w */ + 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', +/* 120 x 121 y 122 z 123 { 124 | 125 } 126 ~ 127 del */ + 'x', 'y', 'z', 0, '|', 0, '~', 0 }; + + +static const int8_t unhex[256] = + {-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 + , 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,-1,-1,-1,-1,-1,-1 + ,-1,10,11,12,13,14,15,-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,10,11,12,13,14,15,-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 + }; + + +#if HTTP_PARSER_STRICT +# define T(v) 0 +#else +# define T(v) v +#endif + + +static const uint8_t normal_url_char[32] = { +/* 0 nul 1 soh 2 stx 3 etx 4 eot 5 enq 6 ack 7 bel */ + 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0, +/* 8 bs 9 ht 10 nl 11 vt 12 np 13 cr 14 so 15 si */ + 0 | T(2) | 0 | 0 | T(16) | 0 | 0 | 0, +/* 16 dle 17 dc1 18 dc2 19 dc3 20 dc4 21 nak 22 syn 23 etb */ + 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0, +/* 24 can 25 em 26 sub 27 esc 28 fs 29 gs 30 rs 31 us */ + 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0, +/* 32 sp 33 ! 34 " 35 # 36 $ 37 % 38 & 39 ' */ + 0 | 2 | 4 | 0 | 16 | 32 | 64 | 128, +/* 40 ( 41 ) 42 * 43 + 44 , 45 - 46 . 47 / */ + 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, +/* 48 0 49 1 50 2 51 3 52 4 53 5 54 6 55 7 */ + 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, +/* 56 8 57 9 58 : 59 ; 60 < 61 = 62 > 63 ? */ + 1 | 2 | 4 | 8 | 16 | 32 | 64 | 0, +/* 64 @ 65 A 66 B 67 C 68 D 69 E 70 F 71 G */ + 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, +/* 72 H 73 I 74 J 75 K 76 L 77 M 78 N 79 O */ + 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, +/* 80 P 81 Q 82 R 83 S 84 T 85 U 86 V 87 W */ + 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, +/* 88 X 89 Y 90 Z 91 [ 92 \ 93 ] 94 ^ 95 _ */ + 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, +/* 96 ` 97 a 98 b 99 c 100 d 101 e 102 f 103 g */ + 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, +/* 104 h 105 i 106 j 107 k 108 l 109 m 110 n 111 o */ + 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, +/* 112 p 113 q 114 r 115 s 116 t 117 u 118 v 119 w */ + 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, +/* 120 x 121 y 122 z 123 { 124 | 125 } 126 ~ 127 del */ + 1 | 2 | 4 | 8 | 16 | 32 | 64 | 0, }; + +#undef T + +enum state + { s_dead = 1 /* important that this is > 0 */ + + , s_start_req_or_res + , s_res_or_resp_H + , s_start_res + , s_res_H + , s_res_HT + , s_res_HTT + , s_res_HTTP + , s_res_first_http_major + , s_res_http_major + , s_res_first_http_minor + , s_res_http_minor + , s_res_first_status_code + , s_res_status_code + , s_res_status_start + , s_res_status + , s_res_line_almost_done + + , s_start_req + + , s_req_method + , s_req_spaces_before_url + , s_req_schema + , s_req_schema_slash + , s_req_schema_slash_slash + , s_req_server_start + , s_req_server + , s_req_server_with_at + , s_req_path + , s_req_query_string_start + , s_req_query_string + , s_req_fragment_start + , s_req_fragment + , s_req_http_start + , s_req_http_H + , s_req_http_HT + , s_req_http_HTT + , s_req_http_HTTP + , s_req_first_http_major + , s_req_http_major + , s_req_first_http_minor + , s_req_http_minor + , s_req_line_almost_done + + , s_header_field_start + , s_header_field + , s_header_value_discard_ws + , s_header_value_discard_ws_almost_done + , s_header_value_discard_lws + , s_header_value_start + , s_header_value + , s_header_value_lws + + , s_header_almost_done + + , s_chunk_size_start + , s_chunk_size + , s_chunk_parameters + , s_chunk_size_almost_done + + , s_headers_almost_done + , s_headers_done + + /* Important: 's_headers_done' must be the last 'header' state. All + * states beyond this must be 'body' states. It is used for overflow + * checking. See the PARSING_HEADER() macro. + */ + + , s_chunk_data + , s_chunk_data_almost_done + , s_chunk_data_done + + , s_body_identity + , s_body_identity_eof + + , s_message_done + }; + + +#define PARSING_HEADER(state) (state <= s_headers_done) + + +enum header_states + { h_general = 0 + , h_C + , h_CO + , h_CON + + , h_matching_connection + , h_matching_proxy_connection + , h_matching_content_length + , h_matching_transfer_encoding + , h_matching_upgrade + + , h_connection + , h_content_length + , h_transfer_encoding + , h_upgrade + + , h_matching_transfer_encoding_chunked + , h_matching_connection_token_start + , h_matching_connection_keep_alive + , h_matching_connection_close + , h_matching_connection_upgrade + , h_matching_connection_token + + , h_transfer_encoding_chunked + , h_connection_keep_alive + , h_connection_close + , h_connection_upgrade + }; + +enum http_host_state + { + s_http_host_dead = 1 + , s_http_userinfo_start + , s_http_userinfo + , s_http_host_start + , s_http_host_v6_start + , s_http_host + , s_http_host_v6 + , s_http_host_v6_end + , s_http_host_port_start + , s_http_host_port +}; + +/* Macros for character classes; depends on strict-mode */ +#define CR '\r' +#define LF '\n' +#define LOWER(c) (unsigned char)(c | 0x20) +#define IS_ALPHA(c) (LOWER(c) >= 'a' && LOWER(c) <= 'z') +#define IS_NUM(c) ((c) >= '0' && (c) <= '9') +#define IS_ALPHANUM(c) (IS_ALPHA(c) || IS_NUM(c)) +#define IS_HEX(c) (IS_NUM(c) || (LOWER(c) >= 'a' && LOWER(c) <= 'f')) +#define IS_MARK(c) ((c) == '-' || (c) == '_' || (c) == '.' || \ + (c) == '!' || (c) == '~' || (c) == '*' || (c) == '\'' || (c) == '(' || \ + (c) == ')') +#define IS_USERINFO_CHAR(c) (IS_ALPHANUM(c) || IS_MARK(c) || (c) == '%' || \ + (c) == ';' || (c) == ':' || (c) == '&' || (c) == '=' || (c) == '+' || \ + (c) == '$' || (c) == ',') + +#define STRICT_TOKEN(c) (tokens[(unsigned char)c]) + +#if HTTP_PARSER_STRICT +#define TOKEN(c) (tokens[(unsigned char)c]) +#define IS_URL_CHAR(c) (BIT_AT(normal_url_char, (unsigned char)c)) +#define IS_HOST_CHAR(c) (IS_ALPHANUM(c) || (c) == '.' || (c) == '-') +#else +#define TOKEN(c) ((c == ' ') ? ' ' : tokens[(unsigned char)c]) +#define IS_URL_CHAR(c) \ + (BIT_AT(normal_url_char, (unsigned char)c) || ((c) & 0x80)) +#define IS_HOST_CHAR(c) \ + (IS_ALPHANUM(c) || (c) == '.' || (c) == '-' || (c) == '_') +#endif + + +#define start_state (parser->type == HTTP_REQUEST ? s_start_req : s_start_res) + + +#if HTTP_PARSER_STRICT +# define STRICT_CHECK(cond) \ +do { \ + if (cond) { \ + SET_ERRNO(HPE_STRICT); \ + goto error; \ + } \ +} while (0) +# define NEW_MESSAGE() (http_should_keep_alive(parser) ? start_state : s_dead) +#else +# define STRICT_CHECK(cond) +# define NEW_MESSAGE() start_state +#endif + + +/* Map errno values to strings for human-readable output */ +#define HTTP_STRERROR_GEN(n, s) { "HPE_" #n, s }, +static struct { + const char *name; + const char *description; +} http_strerror_tab[] = { + HTTP_ERRNO_MAP(HTTP_STRERROR_GEN) +}; +#undef HTTP_STRERROR_GEN + +int http_message_needs_eof(const http_parser *parser); + +/* Our URL parser. + * + * This is designed to be shared by http_parser_execute() for URL validation, + * hence it has a state transition + byte-for-byte interface. In addition, it + * is meant to be embedded in http_parser_parse_url(), which does the dirty + * work of turning state transitions URL components for its API. + * + * This function should only be invoked with non-space characters. It is + * assumed that the caller cares about (and can detect) the transition between + * URL and non-URL states by looking for these. + */ +static enum state +parse_url_char(enum state s, const char ch) +{ + if (ch == ' ' || ch == '\r' || ch == '\n') { + return s_dead; + } + +#if HTTP_PARSER_STRICT + if (ch == '\t' || ch == '\f') { + return s_dead; + } +#endif + + switch (s) { + case s_req_spaces_before_url: + /* Proxied requests are followed by scheme of an absolute URI (alpha). + * All methods except CONNECT are followed by '/' or '*'. + */ + + if (ch == '/' || ch == '*') { + return s_req_path; + } + + if (IS_ALPHA(ch)) { + return s_req_schema; + } + + break; + + case s_req_schema: + if (IS_ALPHA(ch)) { + return s; + } + + if (ch == ':') { + return s_req_schema_slash; + } + + break; + + case s_req_schema_slash: + if (ch == '/') { + return s_req_schema_slash_slash; + } + + break; + + case s_req_schema_slash_slash: + if (ch == '/') { + return s_req_server_start; + } + + break; + + case s_req_server_with_at: + if (ch == '@') { + return s_dead; + } + + /* FALLTHROUGH */ + case s_req_server_start: + case s_req_server: + if (ch == '/') { + return s_req_path; + } + + if (ch == '?') { + return s_req_query_string_start; + } + + if (ch == '@') { + return s_req_server_with_at; + } + + if (IS_USERINFO_CHAR(ch) || ch == '[' || ch == ']') { + return s_req_server; + } + + break; + + case s_req_path: + if (IS_URL_CHAR(ch)) { + return s; + } + + switch (ch) { + case '?': + return s_req_query_string_start; + + case '#': + return s_req_fragment_start; + } + + break; + + case s_req_query_string_start: + case s_req_query_string: + if (IS_URL_CHAR(ch)) { + return s_req_query_string; + } + + switch (ch) { + case '?': + /* allow extra '?' in query string */ + return s_req_query_string; + + case '#': + return s_req_fragment_start; + } + + break; + + case s_req_fragment_start: + if (IS_URL_CHAR(ch)) { + return s_req_fragment; + } + + switch (ch) { + case '?': + return s_req_fragment; + + case '#': + return s; + } + + break; + + case s_req_fragment: + if (IS_URL_CHAR(ch)) { + return s; + } + + switch (ch) { + case '?': + case '#': + return s; + } + + break; + + default: + break; + } + + /* We should never fall out of the switch above unless there's an error */ + return s_dead; +} + +size_t http_parser_execute (http_parser *parser, + const http_parser_settings *settings, + const char *data, + size_t len) +{ + char c, ch; + int8_t unhex_val; + const char *p = data; + const char *header_field_mark = 0; + const char *header_value_mark = 0; + const char *url_mark = 0; + const char *body_mark = 0; + const char *status_mark = 0; + enum state p_state = (enum state) parser->state; + + /* We're in an error state. Don't bother doing anything. */ + if (HTTP_PARSER_ERRNO(parser) != HPE_OK) { + return 0; + } + + if (len == 0) { + switch (CURRENT_STATE()) { + case s_body_identity_eof: + /* Use of CALLBACK_NOTIFY() here would erroneously return 1 byte read if + * we got paused. + */ + CALLBACK_NOTIFY_NOADVANCE(message_complete); + return 0; + + case s_dead: + case s_start_req_or_res: + case s_start_res: + case s_start_req: + return 0; + + default: + SET_ERRNO(HPE_INVALID_EOF_STATE); + return 1; + } + } + + + if (CURRENT_STATE() == s_header_field) + header_field_mark = data; + if (CURRENT_STATE() == s_header_value) + header_value_mark = data; + switch (CURRENT_STATE()) { + case s_req_path: + case s_req_schema: + case s_req_schema_slash: + case s_req_schema_slash_slash: + case s_req_server_start: + case s_req_server: + case s_req_server_with_at: + case s_req_query_string_start: + case s_req_query_string: + case s_req_fragment_start: + case s_req_fragment: + url_mark = data; + break; + case s_res_status: + status_mark = data; + break; + default: + break; + } + + for (p=data; p != data + len; p++) { + ch = *p; + + if (PARSING_HEADER(CURRENT_STATE())) + COUNT_HEADER_SIZE(1); + +reexecute: + switch (CURRENT_STATE()) { + + case s_dead: + /* this state is used after a 'Connection: close' message + * the parser will error out if it reads another message + */ + if (LIKELY(ch == CR || ch == LF)) + break; + + SET_ERRNO(HPE_CLOSED_CONNECTION); + goto error; + + case s_start_req_or_res: + { + if (ch == CR || ch == LF) + break; + parser->flags = 0; + parser->content_length = ULLONG_MAX; + + if (ch == 'H') { + UPDATE_STATE(s_res_or_resp_H); + + CALLBACK_NOTIFY(message_begin); + } else { + parser->type = HTTP_REQUEST; + UPDATE_STATE(s_start_req); + REEXECUTE(); + } + + break; + } + + case s_res_or_resp_H: + if (ch == 'T') { + parser->type = HTTP_RESPONSE; + UPDATE_STATE(s_res_HT); + } else { + if (UNLIKELY(ch != 'E')) { + SET_ERRNO(HPE_INVALID_CONSTANT); + goto error; + } + + parser->type = HTTP_REQUEST; + parser->method = HTTP_HEAD; + parser->index = 2; + UPDATE_STATE(s_req_method); + } + break; + + case s_start_res: + { + parser->flags = 0; + parser->content_length = ULLONG_MAX; + + switch (ch) { + case 'H': + UPDATE_STATE(s_res_H); + break; + + case CR: + case LF: + break; + + default: + SET_ERRNO(HPE_INVALID_CONSTANT); + goto error; + } + + CALLBACK_NOTIFY(message_begin); + break; + } + + case s_res_H: + STRICT_CHECK(ch != 'T'); + UPDATE_STATE(s_res_HT); + break; + + case s_res_HT: + STRICT_CHECK(ch != 'T'); + UPDATE_STATE(s_res_HTT); + break; + + case s_res_HTT: + STRICT_CHECK(ch != 'P'); + UPDATE_STATE(s_res_HTTP); + break; + + case s_res_HTTP: + STRICT_CHECK(ch != '/'); + UPDATE_STATE(s_res_first_http_major); + break; + + case s_res_first_http_major: + if (UNLIKELY(ch < '0' || ch > '9')) { + SET_ERRNO(HPE_INVALID_VERSION); + goto error; + } + + parser->http_major = ch - '0'; + UPDATE_STATE(s_res_http_major); + break; + + /* major HTTP version or dot */ + case s_res_http_major: + { + if (ch == '.') { + UPDATE_STATE(s_res_first_http_minor); + break; + } + + if (!IS_NUM(ch)) { + SET_ERRNO(HPE_INVALID_VERSION); + goto error; + } + + parser->http_major *= 10; + parser->http_major += ch - '0'; + + if (UNLIKELY(parser->http_major > 999)) { + SET_ERRNO(HPE_INVALID_VERSION); + goto error; + } + + break; + } + + /* first digit of minor HTTP version */ + case s_res_first_http_minor: + if (UNLIKELY(!IS_NUM(ch))) { + SET_ERRNO(HPE_INVALID_VERSION); + goto error; + } + + parser->http_minor = ch - '0'; + UPDATE_STATE(s_res_http_minor); + break; + + /* minor HTTP version or end of request line */ + case s_res_http_minor: + { + if (ch == ' ') { + UPDATE_STATE(s_res_first_status_code); + break; + } + + if (UNLIKELY(!IS_NUM(ch))) { + SET_ERRNO(HPE_INVALID_VERSION); + goto error; + } + + parser->http_minor *= 10; + parser->http_minor += ch - '0'; + + if (UNLIKELY(parser->http_minor > 999)) { + SET_ERRNO(HPE_INVALID_VERSION); + goto error; + } + + break; + } + + case s_res_first_status_code: + { + if (!IS_NUM(ch)) { + if (ch == ' ') { + break; + } + + SET_ERRNO(HPE_INVALID_STATUS); + goto error; + } + parser->status_code = ch - '0'; + UPDATE_STATE(s_res_status_code); + break; + } + + case s_res_status_code: + { + if (!IS_NUM(ch)) { + switch (ch) { + case ' ': + UPDATE_STATE(s_res_status_start); + break; + case CR: + UPDATE_STATE(s_res_line_almost_done); + break; + case LF: + UPDATE_STATE(s_header_field_start); + break; + default: + SET_ERRNO(HPE_INVALID_STATUS); + goto error; + } + break; + } + + parser->status_code *= 10; + parser->status_code += ch - '0'; + + if (UNLIKELY(parser->status_code > 999)) { + SET_ERRNO(HPE_INVALID_STATUS); + goto error; + } + + break; + } + + case s_res_status_start: + { + if (ch == CR) { + UPDATE_STATE(s_res_line_almost_done); + break; + } + + if (ch == LF) { + UPDATE_STATE(s_header_field_start); + break; + } + + MARK(status); + UPDATE_STATE(s_res_status); + parser->index = 0; + break; + } + + case s_res_status: + if (ch == CR) { + UPDATE_STATE(s_res_line_almost_done); + CALLBACK_DATA(status); + break; + } + + if (ch == LF) { + UPDATE_STATE(s_header_field_start); + CALLBACK_DATA(status); + break; + } + + break; + + case s_res_line_almost_done: + STRICT_CHECK(ch != LF); + UPDATE_STATE(s_header_field_start); + break; + + case s_start_req: + { + if (ch == CR || ch == LF) + break; + parser->flags = 0; + parser->content_length = ULLONG_MAX; + + if (UNLIKELY(!IS_ALPHA(ch))) { + SET_ERRNO(HPE_INVALID_METHOD); + goto error; + } + + parser->method = (enum http_method) 0; + parser->index = 1; + switch (ch) { + case 'C': parser->method = HTTP_CONNECT; /* or COPY, CHECKOUT */ break; + case 'D': parser->method = HTTP_DELETE; break; + case 'G': parser->method = HTTP_GET; break; + case 'H': parser->method = HTTP_HEAD; break; + case 'L': parser->method = HTTP_LOCK; break; + case 'M': parser->method = HTTP_MKCOL; /* or MOVE, MKACTIVITY, MERGE, M-SEARCH, MKCALENDAR */ break; + case 'N': parser->method = HTTP_NOTIFY; break; + case 'O': parser->method = HTTP_OPTIONS; break; + case 'P': parser->method = HTTP_POST; + /* or PROPFIND|PROPPATCH|PUT|PATCH|PURGE */ + break; + case 'R': parser->method = HTTP_REPORT; break; + case 'S': parser->method = HTTP_SUBSCRIBE; /* or SEARCH */ break; + case 'T': parser->method = HTTP_TRACE; break; + case 'U': parser->method = HTTP_UNLOCK; /* or UNSUBSCRIBE */ break; + default: + SET_ERRNO(HPE_INVALID_METHOD); + goto error; + } + UPDATE_STATE(s_req_method); + + CALLBACK_NOTIFY(message_begin); + + break; + } + + case s_req_method: + { + const char *matcher; + if (UNLIKELY(ch == '\0')) { + SET_ERRNO(HPE_INVALID_METHOD); + goto error; + } + + matcher = method_strings[parser->method]; + if (ch == ' ' && matcher[parser->index] == '\0') { + UPDATE_STATE(s_req_spaces_before_url); + } else if (ch == matcher[parser->index]) { + ; /* nada */ + } else if (parser->method == HTTP_CONNECT) { + if (parser->index == 1 && ch == 'H') { + parser->method = HTTP_CHECKOUT; + } else if (parser->index == 2 && ch == 'P') { + parser->method = HTTP_COPY; + } else { + SET_ERRNO(HPE_INVALID_METHOD); + goto error; + } + } else if (parser->method == HTTP_MKCOL) { + if (parser->index == 1 && ch == 'O') { + parser->method = HTTP_MOVE; + } else if (parser->index == 1 && ch == 'E') { + parser->method = HTTP_MERGE; + } else if (parser->index == 1 && ch == '-') { + parser->method = HTTP_MSEARCH; + } else if (parser->index == 2 && ch == 'A') { + parser->method = HTTP_MKACTIVITY; + } else if (parser->index == 3 && ch == 'A') { + parser->method = HTTP_MKCALENDAR; + } else { + SET_ERRNO(HPE_INVALID_METHOD); + goto error; + } + } else if (parser->method == HTTP_SUBSCRIBE) { + if (parser->index == 1 && ch == 'E') { + parser->method = HTTP_SEARCH; + } else { + SET_ERRNO(HPE_INVALID_METHOD); + goto error; + } + } else if (parser->index == 1 && parser->method == HTTP_POST) { + if (ch == 'R') { + parser->method = HTTP_PROPFIND; /* or HTTP_PROPPATCH */ + } else if (ch == 'U') { + parser->method = HTTP_PUT; /* or HTTP_PURGE */ + } else if (ch == 'A') { + parser->method = HTTP_PATCH; + } else { + SET_ERRNO(HPE_INVALID_METHOD); + goto error; + } + } else if (parser->index == 2) { + if (parser->method == HTTP_PUT) { + if (ch == 'R') { + parser->method = HTTP_PURGE; + } else { + SET_ERRNO(HPE_INVALID_METHOD); + goto error; + } + } else if (parser->method == HTTP_UNLOCK) { + if (ch == 'S') { + parser->method = HTTP_UNSUBSCRIBE; + } else { + SET_ERRNO(HPE_INVALID_METHOD); + goto error; + } + } else { + SET_ERRNO(HPE_INVALID_METHOD); + goto error; + } + } else if (parser->index == 4 && parser->method == HTTP_PROPFIND && ch == 'P') { + parser->method = HTTP_PROPPATCH; + } else { + SET_ERRNO(HPE_INVALID_METHOD); + goto error; + } + + ++parser->index; + break; + } + + case s_req_spaces_before_url: + { + if (ch == ' ') break; + + MARK(url); + if (parser->method == HTTP_CONNECT) { + UPDATE_STATE(s_req_server_start); + } + + UPDATE_STATE(parse_url_char(CURRENT_STATE(), ch)); + if (UNLIKELY(CURRENT_STATE() == s_dead)) { + SET_ERRNO(HPE_INVALID_URL); + goto error; + } + + break; + } + + case s_req_schema: + case s_req_schema_slash: + case s_req_schema_slash_slash: + case s_req_server_start: + { + switch (ch) { + /* No whitespace allowed here */ + case ' ': + case CR: + case LF: + SET_ERRNO(HPE_INVALID_URL); + goto error; + default: + UPDATE_STATE(parse_url_char(CURRENT_STATE(), ch)); + if (UNLIKELY(CURRENT_STATE() == s_dead)) { + SET_ERRNO(HPE_INVALID_URL); + goto error; + } + } + + break; + } + + case s_req_server: + case s_req_server_with_at: + case s_req_path: + case s_req_query_string_start: + case s_req_query_string: + case s_req_fragment_start: + case s_req_fragment: + { + switch (ch) { + case ' ': + UPDATE_STATE(s_req_http_start); + CALLBACK_DATA(url); + break; + case CR: + case LF: + parser->http_major = 0; + parser->http_minor = 9; + UPDATE_STATE((ch == CR) ? + s_req_line_almost_done : + s_header_field_start); + CALLBACK_DATA(url); + break; + default: + UPDATE_STATE(parse_url_char(CURRENT_STATE(), ch)); + if (UNLIKELY(CURRENT_STATE() == s_dead)) { + SET_ERRNO(HPE_INVALID_URL); + goto error; + } + } + break; + } + + case s_req_http_start: + switch (ch) { + case 'H': + UPDATE_STATE(s_req_http_H); + break; + case ' ': + break; + default: + SET_ERRNO(HPE_INVALID_CONSTANT); + goto error; + } + break; + + case s_req_http_H: + STRICT_CHECK(ch != 'T'); + UPDATE_STATE(s_req_http_HT); + break; + + case s_req_http_HT: + STRICT_CHECK(ch != 'T'); + UPDATE_STATE(s_req_http_HTT); + break; + + case s_req_http_HTT: + STRICT_CHECK(ch != 'P'); + UPDATE_STATE(s_req_http_HTTP); + break; + + case s_req_http_HTTP: + STRICT_CHECK(ch != '/'); + UPDATE_STATE(s_req_first_http_major); + break; + + /* first digit of major HTTP version */ + case s_req_first_http_major: + if (UNLIKELY(ch < '1' || ch > '9')) { + SET_ERRNO(HPE_INVALID_VERSION); + goto error; + } + + parser->http_major = ch - '0'; + UPDATE_STATE(s_req_http_major); + break; + + /* major HTTP version or dot */ + case s_req_http_major: + { + if (ch == '.') { + UPDATE_STATE(s_req_first_http_minor); + break; + } + + if (UNLIKELY(!IS_NUM(ch))) { + SET_ERRNO(HPE_INVALID_VERSION); + goto error; + } + + parser->http_major *= 10; + parser->http_major += ch - '0'; + + if (UNLIKELY(parser->http_major > 999)) { + SET_ERRNO(HPE_INVALID_VERSION); + goto error; + } + + break; + } + + /* first digit of minor HTTP version */ + case s_req_first_http_minor: + if (UNLIKELY(!IS_NUM(ch))) { + SET_ERRNO(HPE_INVALID_VERSION); + goto error; + } + + parser->http_minor = ch - '0'; + UPDATE_STATE(s_req_http_minor); + break; + + /* minor HTTP version or end of request line */ + case s_req_http_minor: + { + if (ch == CR) { + UPDATE_STATE(s_req_line_almost_done); + break; + } + + if (ch == LF) { + UPDATE_STATE(s_header_field_start); + break; + } + + /* XXX allow spaces after digit? */ + + if (UNLIKELY(!IS_NUM(ch))) { + SET_ERRNO(HPE_INVALID_VERSION); + goto error; + } + + parser->http_minor *= 10; + parser->http_minor += ch - '0'; + + if (UNLIKELY(parser->http_minor > 999)) { + SET_ERRNO(HPE_INVALID_VERSION); + goto error; + } + + break; + } + + /* end of request line */ + case s_req_line_almost_done: + { + if (UNLIKELY(ch != LF)) { + SET_ERRNO(HPE_LF_EXPECTED); + goto error; + } + + UPDATE_STATE(s_header_field_start); + break; + } + + case s_header_field_start: + { + if (ch == CR) { + UPDATE_STATE(s_headers_almost_done); + break; + } + + if (ch == LF) { + /* they might be just sending \n instead of \r\n so this would be + * the second \n to denote the end of headers*/ + UPDATE_STATE(s_headers_almost_done); + REEXECUTE(); + } + + c = TOKEN(ch); + + if (UNLIKELY(!c)) { + SET_ERRNO(HPE_INVALID_HEADER_TOKEN); + goto error; + } + + MARK(header_field); + + parser->index = 0; + UPDATE_STATE(s_header_field); + + switch (c) { + case 'c': + parser->header_state = h_C; + break; + + case 'p': + parser->header_state = h_matching_proxy_connection; + break; + + case 't': + parser->header_state = h_matching_transfer_encoding; + break; + + case 'u': + parser->header_state = h_matching_upgrade; + break; + + default: + parser->header_state = h_general; + break; + } + break; + } + + case s_header_field: + { + const char* start = p; + for (; p != data + len; p++) { + ch = *p; + c = TOKEN(ch); + + if (!c) + break; + + switch (parser->header_state) { + case h_general: + break; + + case h_C: + parser->index++; + parser->header_state = (c == 'o' ? h_CO : h_general); + break; + + case h_CO: + parser->index++; + parser->header_state = (c == 'n' ? h_CON : h_general); + break; + + case h_CON: + parser->index++; + switch (c) { + case 'n': + parser->header_state = h_matching_connection; + break; + case 't': + parser->header_state = h_matching_content_length; + break; + default: + parser->header_state = h_general; + break; + } + break; + + /* connection */ + + case h_matching_connection: + parser->index++; + if (parser->index > sizeof(CONNECTION)-1 + || c != CONNECTION[parser->index]) { + parser->header_state = h_general; + } else if (parser->index == sizeof(CONNECTION)-2) { + parser->header_state = h_connection; + } + break; + + /* proxy-connection */ + + case h_matching_proxy_connection: + parser->index++; + if (parser->index > sizeof(PROXY_CONNECTION)-1 + || c != PROXY_CONNECTION[parser->index]) { + parser->header_state = h_general; + } else if (parser->index == sizeof(PROXY_CONNECTION)-2) { + parser->header_state = h_connection; + } + break; + + /* content-length */ + + case h_matching_content_length: + parser->index++; + if (parser->index > sizeof(CONTENT_LENGTH)-1 + || c != CONTENT_LENGTH[parser->index]) { + parser->header_state = h_general; + } else if (parser->index == sizeof(CONTENT_LENGTH)-2) { + parser->header_state = h_content_length; + } + break; + + /* transfer-encoding */ + + case h_matching_transfer_encoding: + parser->index++; + if (parser->index > sizeof(TRANSFER_ENCODING)-1 + || c != TRANSFER_ENCODING[parser->index]) { + parser->header_state = h_general; + } else if (parser->index == sizeof(TRANSFER_ENCODING)-2) { + parser->header_state = h_transfer_encoding; + } + break; + + /* upgrade */ + + case h_matching_upgrade: + parser->index++; + if (parser->index > sizeof(UPGRADE)-1 + || c != UPGRADE[parser->index]) { + parser->header_state = h_general; + } else if (parser->index == sizeof(UPGRADE)-2) { + parser->header_state = h_upgrade; + } + break; + + case h_connection: + case h_content_length: + case h_transfer_encoding: + case h_upgrade: + if (ch != ' ') parser->header_state = h_general; + break; + + default: + assert(0 && "Unknown header_state"); + break; + } + } + + COUNT_HEADER_SIZE(p - start); + + if (p == data + len) { + --p; + break; + } + + if (ch == ':') { + UPDATE_STATE(s_header_value_discard_ws); + CALLBACK_DATA(header_field); + break; + } + + SET_ERRNO(HPE_INVALID_HEADER_TOKEN); + goto error; + } + + case s_header_value_discard_ws: + if (ch == ' ' || ch == '\t') break; + + if (ch == CR) { + UPDATE_STATE(s_header_value_discard_ws_almost_done); + break; + } + + if (ch == LF) { + UPDATE_STATE(s_header_value_discard_lws); + break; + } + + /* FALLTHROUGH */ + + case s_header_value_start: + { + MARK(header_value); + + UPDATE_STATE(s_header_value); + parser->index = 0; + + c = LOWER(ch); + + switch (parser->header_state) { + case h_upgrade: + parser->flags |= F_UPGRADE; + parser->header_state = h_general; + break; + + case h_transfer_encoding: + /* looking for 'Transfer-Encoding: chunked' */ + if ('c' == c) { + parser->header_state = h_matching_transfer_encoding_chunked; + } else { + parser->header_state = h_general; + } + break; + + case h_content_length: + if (UNLIKELY(!IS_NUM(ch))) { + SET_ERRNO(HPE_INVALID_CONTENT_LENGTH); + goto error; + } + + parser->content_length = ch - '0'; + break; + + case h_connection: + /* looking for 'Connection: keep-alive' */ + if (c == 'k') { + parser->header_state = h_matching_connection_keep_alive; + /* looking for 'Connection: close' */ + } else if (c == 'c') { + parser->header_state = h_matching_connection_close; + } else if (c == 'u') { + parser->header_state = h_matching_connection_upgrade; + } else { + parser->header_state = h_matching_connection_token; + } + break; + + /* Multi-value `Connection` header */ + case h_matching_connection_token_start: + break; + + default: + parser->header_state = h_general; + break; + } + break; + } + + case s_header_value: + { + const char* start = p; + enum header_states h_state = (enum header_states) parser->header_state; + for (; p != data + len; p++) { + ch = *p; + if (ch == CR) { + UPDATE_STATE(s_header_almost_done); + parser->header_state = h_state; + CALLBACK_DATA(header_value); + break; + } + + if (ch == LF) { + UPDATE_STATE(s_header_almost_done); + COUNT_HEADER_SIZE(p - start); + parser->header_state = h_state; + CALLBACK_DATA_NOADVANCE(header_value); + REEXECUTE(); + } + + c = LOWER(ch); + + switch (h_state) { + case h_general: + { + const char* p_cr; + const char* p_lf; + size_t limit = data + len - p; + + limit = MIN(limit, HTTP_MAX_HEADER_SIZE); + + p_cr = (const char*) memchr(p, CR, limit); + p_lf = (const char*) memchr(p, LF, limit); + if (p_cr != NULL) { + if (p_lf != NULL && p_cr >= p_lf) + p = p_lf; + else + p = p_cr; + } else if (UNLIKELY(p_lf != NULL)) { + p = p_lf; + } else { + p = data + len; + } + --p; + + break; + } + + case h_connection: + case h_transfer_encoding: + assert(0 && "Shouldn't get here."); + break; + + case h_content_length: + { + uint64_t t; + + if (ch == ' ') break; + + if (UNLIKELY(!IS_NUM(ch))) { + SET_ERRNO(HPE_INVALID_CONTENT_LENGTH); + parser->header_state = h_state; + goto error; + } + + t = parser->content_length; + t *= 10; + t += ch - '0'; + + /* Overflow? Test against a conservative limit for simplicity. */ + if (UNLIKELY((ULLONG_MAX - 10) / 10 < parser->content_length)) { + SET_ERRNO(HPE_INVALID_CONTENT_LENGTH); + parser->header_state = h_state; + goto error; + } + + parser->content_length = t; + break; + } + + /* Transfer-Encoding: chunked */ + case h_matching_transfer_encoding_chunked: + parser->index++; + if (parser->index > sizeof(CHUNKED)-1 + || c != CHUNKED[parser->index]) { + h_state = h_general; + } else if (parser->index == sizeof(CHUNKED)-2) { + h_state = h_transfer_encoding_chunked; + } + break; + + case h_matching_connection_token_start: + /* looking for 'Connection: keep-alive' */ + if (c == 'k') { + h_state = h_matching_connection_keep_alive; + /* looking for 'Connection: close' */ + } else if (c == 'c') { + h_state = h_matching_connection_close; + } else if (c == 'u') { + h_state = h_matching_connection_upgrade; + } else if (STRICT_TOKEN(c)) { + h_state = h_matching_connection_token; + } else if (c == ' ' || c == '\t') { + /* Skip lws */ + } else { + h_state = h_general; + } + break; + + /* looking for 'Connection: keep-alive' */ + case h_matching_connection_keep_alive: + parser->index++; + if (parser->index > sizeof(KEEP_ALIVE)-1 + || c != KEEP_ALIVE[parser->index]) { + h_state = h_matching_connection_token; + } else if (parser->index == sizeof(KEEP_ALIVE)-2) { + h_state = h_connection_keep_alive; + } + break; + + /* looking for 'Connection: close' */ + case h_matching_connection_close: + parser->index++; + if (parser->index > sizeof(CLOSE)-1 || c != CLOSE[parser->index]) { + h_state = h_matching_connection_token; + } else if (parser->index == sizeof(CLOSE)-2) { + h_state = h_connection_close; + } + break; + + /* looking for 'Connection: upgrade' */ + case h_matching_connection_upgrade: + parser->index++; + if (parser->index > sizeof(UPGRADE) - 1 || + c != UPGRADE[parser->index]) { + h_state = h_matching_connection_token; + } else if (parser->index == sizeof(UPGRADE)-2) { + h_state = h_connection_upgrade; + } + break; + + case h_matching_connection_token: + if (ch == ',') { + h_state = h_matching_connection_token_start; + parser->index = 0; + } + break; + + case h_transfer_encoding_chunked: + if (ch != ' ') h_state = h_general; + break; + + case h_connection_keep_alive: + case h_connection_close: + case h_connection_upgrade: + if (ch == ',') { + if (h_state == h_connection_keep_alive) { + parser->flags |= F_CONNECTION_KEEP_ALIVE; + } else if (h_state == h_connection_close) { + parser->flags |= F_CONNECTION_CLOSE; + } else if (h_state == h_connection_upgrade) { + parser->flags |= F_CONNECTION_UPGRADE; + } + h_state = h_matching_connection_token_start; + parser->index = 0; + } else if (ch != ' ') { + h_state = h_matching_connection_token; + } + break; + + default: + UPDATE_STATE(s_header_value); + h_state = h_general; + break; + } + } + parser->header_state = h_state; + + COUNT_HEADER_SIZE(p - start); + + if (p == data + len) + --p; + break; + } + + case s_header_almost_done: + { + STRICT_CHECK(ch != LF); + + UPDATE_STATE(s_header_value_lws); + break; + } + + case s_header_value_lws: + { + if (ch == ' ' || ch == '\t') { + UPDATE_STATE(s_header_value_start); + REEXECUTE(); + } + + /* finished the header */ + switch (parser->header_state) { + case h_connection_keep_alive: + parser->flags |= F_CONNECTION_KEEP_ALIVE; + break; + case h_connection_close: + parser->flags |= F_CONNECTION_CLOSE; + break; + case h_transfer_encoding_chunked: + parser->flags |= F_CHUNKED; + break; + case h_connection_upgrade: + parser->flags |= F_CONNECTION_UPGRADE; + break; + default: + break; + } + + UPDATE_STATE(s_header_field_start); + REEXECUTE(); + } + + case s_header_value_discard_ws_almost_done: + { + STRICT_CHECK(ch != LF); + UPDATE_STATE(s_header_value_discard_lws); + break; + } + + case s_header_value_discard_lws: + { + if (ch == ' ' || ch == '\t') { + UPDATE_STATE(s_header_value_discard_ws); + break; + } else { + switch (parser->header_state) { + case h_connection_keep_alive: + parser->flags |= F_CONNECTION_KEEP_ALIVE; + break; + case h_connection_close: + parser->flags |= F_CONNECTION_CLOSE; + break; + case h_connection_upgrade: + parser->flags |= F_CONNECTION_UPGRADE; + break; + case h_transfer_encoding_chunked: + parser->flags |= F_CHUNKED; + break; + default: + break; + } + + /* header value was empty */ + MARK(header_value); + UPDATE_STATE(s_header_field_start); + CALLBACK_DATA_NOADVANCE(header_value); + REEXECUTE(); + } + } + + case s_headers_almost_done: + { + STRICT_CHECK(ch != LF); + + if (parser->flags & F_TRAILING) { + /* End of a chunked request */ + UPDATE_STATE(NEW_MESSAGE()); + CALLBACK_NOTIFY(message_complete); + break; + } + + UPDATE_STATE(s_headers_done); + + /* Set this here so that on_headers_complete() callbacks can see it */ + parser->upgrade = + ((parser->flags & (F_UPGRADE | F_CONNECTION_UPGRADE)) == + (F_UPGRADE | F_CONNECTION_UPGRADE) || + parser->method == HTTP_CONNECT); + + /* Here we call the headers_complete callback. This is somewhat + * different than other callbacks because if the user returns 1, we + * will interpret that as saying that this message has no body. This + * is needed for the annoying case of recieving a response to a HEAD + * request. + * + * We'd like to use CALLBACK_NOTIFY_NOADVANCE() here but we cannot, so + * we have to simulate it by handling a change in errno below. + */ + if (settings->on_headers_complete) { + switch (settings->on_headers_complete(parser)) { + case 0: + break; + + case 1: + parser->flags |= F_SKIPBODY; + break; + + default: + SET_ERRNO(HPE_CB_headers_complete); + RETURN(p - data); /* Error */ + } + } + + if (HTTP_PARSER_ERRNO(parser) != HPE_OK) { + RETURN(p - data); + } + + REEXECUTE(); + } + + case s_headers_done: + { + STRICT_CHECK(ch != LF); + + parser->nread = 0; + + /* Exit, the rest of the connect is in a different protocol. */ + if (parser->upgrade) { + UPDATE_STATE(NEW_MESSAGE()); + CALLBACK_NOTIFY(message_complete); + RETURN((p - data) + 1); + } + + if (parser->flags & F_SKIPBODY) { + UPDATE_STATE(NEW_MESSAGE()); + CALLBACK_NOTIFY(message_complete); + } else if (parser->flags & F_CHUNKED) { + /* chunked encoding - ignore Content-Length header */ + UPDATE_STATE(s_chunk_size_start); + } else { + if (parser->content_length == 0) { + /* Content-Length header given but zero: Content-Length: 0\r\n */ + UPDATE_STATE(NEW_MESSAGE()); + CALLBACK_NOTIFY(message_complete); + } else if (parser->content_length != ULLONG_MAX) { + /* Content-Length header given and non-zero */ + UPDATE_STATE(s_body_identity); + } else { + if (parser->type == HTTP_REQUEST || + !http_message_needs_eof(parser)) { + /* Assume content-length 0 - read the next */ + UPDATE_STATE(NEW_MESSAGE()); + CALLBACK_NOTIFY(message_complete); + } else { + /* Read body until EOF */ + UPDATE_STATE(s_body_identity_eof); + } + } + } + + break; + } + + case s_body_identity: + { + uint64_t to_read = MIN(parser->content_length, + (uint64_t) ((data + len) - p)); + + assert(parser->content_length != 0 + && parser->content_length != ULLONG_MAX); + + /* The difference between advancing content_length and p is because + * the latter will automaticaly advance on the next loop iteration. + * Further, if content_length ends up at 0, we want to see the last + * byte again for our message complete callback. + */ + MARK(body); + parser->content_length -= to_read; + p += to_read - 1; + + if (parser->content_length == 0) { + UPDATE_STATE(s_message_done); + + /* Mimic CALLBACK_DATA_NOADVANCE() but with one extra byte. + * + * The alternative to doing this is to wait for the next byte to + * trigger the data callback, just as in every other case. The + * problem with this is that this makes it difficult for the test + * harness to distinguish between complete-on-EOF and + * complete-on-length. It's not clear that this distinction is + * important for applications, but let's keep it for now. + */ + CALLBACK_DATA_(body, p - body_mark + 1, p - data); + REEXECUTE(); + } + + break; + } + + /* read until EOF */ + case s_body_identity_eof: + MARK(body); + p = data + len - 1; + + break; + + case s_message_done: + UPDATE_STATE(NEW_MESSAGE()); + CALLBACK_NOTIFY(message_complete); + break; + + case s_chunk_size_start: + { + assert(parser->nread == 1); + assert(parser->flags & F_CHUNKED); + + unhex_val = unhex[(unsigned char)ch]; + if (UNLIKELY(unhex_val == -1)) { + SET_ERRNO(HPE_INVALID_CHUNK_SIZE); + goto error; + } + + parser->content_length = unhex_val; + UPDATE_STATE(s_chunk_size); + break; + } + + case s_chunk_size: + { + uint64_t t; + + assert(parser->flags & F_CHUNKED); + + if (ch == CR) { + UPDATE_STATE(s_chunk_size_almost_done); + break; + } + + unhex_val = unhex[(unsigned char)ch]; + + if (unhex_val == -1) { + if (ch == ';' || ch == ' ') { + UPDATE_STATE(s_chunk_parameters); + break; + } + + SET_ERRNO(HPE_INVALID_CHUNK_SIZE); + goto error; + } + + t = parser->content_length; + t *= 16; + t += unhex_val; + + /* Overflow? Test against a conservative limit for simplicity. */ + if (UNLIKELY((ULLONG_MAX - 16) / 16 < parser->content_length)) { + SET_ERRNO(HPE_INVALID_CONTENT_LENGTH); + goto error; + } + + parser->content_length = t; + break; + } + + case s_chunk_parameters: + { + assert(parser->flags & F_CHUNKED); + /* just ignore this shit. TODO check for overflow */ + if (ch == CR) { + UPDATE_STATE(s_chunk_size_almost_done); + break; + } + break; + } + + case s_chunk_size_almost_done: + { + assert(parser->flags & F_CHUNKED); + STRICT_CHECK(ch != LF); + + parser->nread = 0; + + if (parser->content_length == 0) { + parser->flags |= F_TRAILING; + UPDATE_STATE(s_header_field_start); + } else { + UPDATE_STATE(s_chunk_data); + } + break; + } + + case s_chunk_data: + { + uint64_t to_read = MIN(parser->content_length, + (uint64_t) ((data + len) - p)); + + assert(parser->flags & F_CHUNKED); + assert(parser->content_length != 0 + && parser->content_length != ULLONG_MAX); + + /* See the explanation in s_body_identity for why the content + * length and data pointers are managed this way. + */ + MARK(body); + parser->content_length -= to_read; + p += to_read - 1; + + if (parser->content_length == 0) { + UPDATE_STATE(s_chunk_data_almost_done); + } + + break; + } + + case s_chunk_data_almost_done: + assert(parser->flags & F_CHUNKED); + assert(parser->content_length == 0); + STRICT_CHECK(ch != CR); + UPDATE_STATE(s_chunk_data_done); + CALLBACK_DATA(body); + break; + + case s_chunk_data_done: + assert(parser->flags & F_CHUNKED); + STRICT_CHECK(ch != LF); + parser->nread = 0; + UPDATE_STATE(s_chunk_size_start); + break; + + default: + assert(0 && "unhandled state"); + SET_ERRNO(HPE_INVALID_INTERNAL_STATE); + goto error; + } + } + + /* Run callbacks for any marks that we have leftover after we ran our of + * bytes. There should be at most one of these set, so it's OK to invoke + * them in series (unset marks will not result in callbacks). + * + * We use the NOADVANCE() variety of callbacks here because 'p' has already + * overflowed 'data' and this allows us to correct for the off-by-one that + * we'd otherwise have (since CALLBACK_DATA() is meant to be run with a 'p' + * value that's in-bounds). + */ + + assert(((header_field_mark ? 1 : 0) + + (header_value_mark ? 1 : 0) + + (url_mark ? 1 : 0) + + (body_mark ? 1 : 0) + + (status_mark ? 1 : 0)) <= 1); + + CALLBACK_DATA_NOADVANCE(header_field); + CALLBACK_DATA_NOADVANCE(header_value); + CALLBACK_DATA_NOADVANCE(url); + CALLBACK_DATA_NOADVANCE(body); + CALLBACK_DATA_NOADVANCE(status); + + RETURN(len); + +error: + if (HTTP_PARSER_ERRNO(parser) == HPE_OK) { + SET_ERRNO(HPE_UNKNOWN); + } + + RETURN(p - data); +} + + +/* Does the parser need to see an EOF to find the end of the message? */ +int +http_message_needs_eof (const http_parser *parser) +{ + if (parser->type == HTTP_REQUEST) { + return 0; + } + + /* See RFC 2616 section 4.4 */ + if (parser->status_code / 100 == 1 || /* 1xx e.g. Continue */ + parser->status_code == 204 || /* No Content */ + parser->status_code == 304 || /* Not Modified */ + parser->flags & F_SKIPBODY) { /* response to a HEAD request */ + return 0; + } + + if ((parser->flags & F_CHUNKED) || parser->content_length != ULLONG_MAX) { + return 0; + } + + return 1; +} + + +int +http_should_keep_alive (const http_parser *parser) +{ + if (parser->http_major > 0 && parser->http_minor > 0) { + /* HTTP/1.1 */ + if (parser->flags & F_CONNECTION_CLOSE) { + return 0; + } + } else { + /* HTTP/1.0 or earlier */ + if (!(parser->flags & F_CONNECTION_KEEP_ALIVE)) { + return 0; + } + } + + return !http_message_needs_eof(parser); +} + + +const char * +http_method_str (enum http_method m) +{ + return ELEM_AT(method_strings, m, ""); +} + + +void +http_parser_init (http_parser *parser, enum http_parser_type t) +{ + void *data = parser->data; /* preserve application data */ + memset(parser, 0, sizeof(*parser)); + parser->data = data; + parser->type = t; + parser->state = (t == HTTP_REQUEST ? s_start_req : (t == HTTP_RESPONSE ? s_start_res : s_start_req_or_res)); + parser->http_errno = HPE_OK; +} + +void +http_parser_settings_init(http_parser_settings *settings) +{ + memset(settings, 0, sizeof(*settings)); +} + +const char * +http_errno_name(enum http_errno err) { + assert(err < (sizeof(http_strerror_tab)/sizeof(http_strerror_tab[0]))); + return http_strerror_tab[err].name; +} + +const char * +http_errno_description(enum http_errno err) { + assert(err < (sizeof(http_strerror_tab)/sizeof(http_strerror_tab[0]))); + return http_strerror_tab[err].description; +} + +static enum http_host_state +http_parse_host_char(enum http_host_state s, const char ch) { + switch(s) { + case s_http_userinfo: + case s_http_userinfo_start: + if (ch == '@') { + return s_http_host_start; + } + + if (IS_USERINFO_CHAR(ch)) { + return s_http_userinfo; + } + break; + + case s_http_host_start: + if (ch == '[') { + return s_http_host_v6_start; + } + + if (IS_HOST_CHAR(ch)) { + return s_http_host; + } + + break; + + case s_http_host: + if (IS_HOST_CHAR(ch)) { + return s_http_host; + } + + /* FALLTHROUGH */ + case s_http_host_v6_end: + if (ch == ':') { + return s_http_host_port_start; + } + + break; + + case s_http_host_v6: + if (ch == ']') { + return s_http_host_v6_end; + } + + /* FALLTHROUGH */ + case s_http_host_v6_start: + if (IS_HEX(ch) || ch == ':' || ch == '.') { + return s_http_host_v6; + } + + break; + + case s_http_host_port: + case s_http_host_port_start: + if (IS_NUM(ch)) { + return s_http_host_port; + } + + break; + + default: + break; + } + return s_http_host_dead; +} + +static int +http_parse_host(const char * buf, struct http_parser_url *u, int found_at) { + enum http_host_state s; + + const char *p; + size_t buflen = u->field_data[UF_HOST].off + u->field_data[UF_HOST].len; + + u->field_data[UF_HOST].len = 0; + + s = found_at ? s_http_userinfo_start : s_http_host_start; + + for (p = buf + u->field_data[UF_HOST].off; p < buf + buflen; p++) { + enum http_host_state new_s = http_parse_host_char(s, *p); + + if (new_s == s_http_host_dead) { + return 1; + } + + switch(new_s) { + case s_http_host: + if (s != s_http_host) { + u->field_data[UF_HOST].off = p - buf; + } + u->field_data[UF_HOST].len++; + break; + + case s_http_host_v6: + if (s != s_http_host_v6) { + u->field_data[UF_HOST].off = p - buf; + } + u->field_data[UF_HOST].len++; + break; + + case s_http_host_port: + if (s != s_http_host_port) { + u->field_data[UF_PORT].off = p - buf; + u->field_data[UF_PORT].len = 0; + u->field_set |= (1 << UF_PORT); + } + u->field_data[UF_PORT].len++; + break; + + case s_http_userinfo: + if (s != s_http_userinfo) { + u->field_data[UF_USERINFO].off = p - buf ; + u->field_data[UF_USERINFO].len = 0; + u->field_set |= (1 << UF_USERINFO); + } + u->field_data[UF_USERINFO].len++; + break; + + default: + break; + } + s = new_s; + } + + /* Make sure we don't end somewhere unexpected */ + switch (s) { + case s_http_host_start: + case s_http_host_v6_start: + case s_http_host_v6: + case s_http_host_port_start: + case s_http_userinfo: + case s_http_userinfo_start: + return 1; + default: + break; + } + + return 0; +} + +int +http_parser_parse_url(const char *buf, size_t buflen, int is_connect, + struct http_parser_url *u) +{ + enum state s; + const char *p; + enum http_parser_url_fields uf, old_uf; + int found_at = 0; + + u->port = u->field_set = 0; + s = is_connect ? s_req_server_start : s_req_spaces_before_url; + old_uf = UF_MAX; + + for (p = buf; p < buf + buflen; p++) { + s = parse_url_char(s, *p); + + /* Figure out the next field that we're operating on */ + switch (s) { + case s_dead: + return 1; + + /* Skip delimeters */ + case s_req_schema_slash: + case s_req_schema_slash_slash: + case s_req_server_start: + case s_req_query_string_start: + case s_req_fragment_start: + continue; + + case s_req_schema: + uf = UF_SCHEMA; + break; + + case s_req_server_with_at: + found_at = 1; + + /* FALLTROUGH */ + case s_req_server: + uf = UF_HOST; + break; + + case s_req_path: + uf = UF_PATH; + break; + + case s_req_query_string: + uf = UF_QUERY; + break; + + case s_req_fragment: + uf = UF_FRAGMENT; + break; + + default: + assert(!"Unexpected state"); + return 1; + } + + /* Nothing's changed; soldier on */ + if (uf == old_uf) { + u->field_data[uf].len++; + continue; + } + + u->field_data[uf].off = p - buf; + u->field_data[uf].len = 1; + + u->field_set |= (1 << uf); + old_uf = uf; + } + + /* host must be present if there is a schema */ + /* parsing http:///toto will fail */ + if ((u->field_set & ((1 << UF_SCHEMA) | (1 << UF_HOST))) != 0) { + if (http_parse_host(buf, u, found_at) != 0) { + return 1; + } + } + + /* CONNECT requests can only contain "hostname:port" */ + if (is_connect && u->field_set != ((1 << UF_HOST)|(1 << UF_PORT))) { + return 1; + } + + if (u->field_set & (1 << UF_PORT)) { + /* Don't bother with endp; we've already validated the string */ + unsigned long v = strtoul(buf + u->field_data[UF_PORT].off, NULL, 10); + + /* Ports have a max value of 2^16 */ + if (v > 0xffff) { + return 1; + } + + u->port = (uint16_t) v; + } + + return 0; +} + +void +http_parser_pause(http_parser *parser, int paused) { + /* Users should only be pausing/unpausing a parser that is not in an error + * state. In non-debug builds, there's not much that we can do about this + * other than ignore it. + */ + if (HTTP_PARSER_ERRNO(parser) == HPE_OK || + HTTP_PARSER_ERRNO(parser) == HPE_PAUSED) { + SET_ERRNO((paused) ? HPE_PAUSED : HPE_OK); + } else { + assert(0 && "Attempting to pause parser in error state"); + } +} + +int +http_body_is_final(const struct http_parser *parser) { + return parser->state == s_message_done; +} + +unsigned long +http_parser_version(void) { + return HTTP_PARSER_VERSION_MAJOR * 0x10000 | + HTTP_PARSER_VERSION_MINOR * 0x00100 | + HTTP_PARSER_VERSION_PATCH * 0x00001; +} diff --git a/third-party/http-parser/http_parser.h b/third-party/http-parser/http_parser.h new file mode 100644 index 0000000..99c533a --- /dev/null +++ b/third-party/http-parser/http_parser.h @@ -0,0 +1,335 @@ +/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +#ifndef http_parser_h +#define http_parser_h +#ifdef __cplusplus +extern "C" { +#endif + +/* Also update SONAME in the Makefile whenever you change these. */ +#define HTTP_PARSER_VERSION_MAJOR 2 +#define HTTP_PARSER_VERSION_MINOR 4 +#define HTTP_PARSER_VERSION_PATCH 2 + +#include +#if defined(_WIN32) && !defined(__MINGW32__) && (!defined(_MSC_VER) || _MSC_VER<1600) +#include +#include +typedef __int8 int8_t; +typedef unsigned __int8 uint8_t; +typedef __int16 int16_t; +typedef unsigned __int16 uint16_t; +typedef __int32 int32_t; +typedef unsigned __int32 uint32_t; +typedef __int64 int64_t; +typedef unsigned __int64 uint64_t; +#else +#include +#endif + +/* Compile with -DHTTP_PARSER_STRICT=0 to make less checks, but run + * faster + */ +#ifndef HTTP_PARSER_STRICT +# define HTTP_PARSER_STRICT 1 +#endif + +/* Maximium header size allowed. If the macro is not defined + * before including this header then the default is used. To + * change the maximum header size, define the macro in the build + * environment (e.g. -DHTTP_MAX_HEADER_SIZE=). To remove + * the effective limit on the size of the header, define the macro + * to a very large number (e.g. -DHTTP_MAX_HEADER_SIZE=0x7fffffff) + */ +#ifndef HTTP_MAX_HEADER_SIZE +# define HTTP_MAX_HEADER_SIZE (80*1024) +#endif + +typedef struct http_parser http_parser; +typedef struct http_parser_settings http_parser_settings; + + +/* Callbacks should return non-zero to indicate an error. The parser will + * then halt execution. + * + * The one exception is on_headers_complete. In a HTTP_RESPONSE parser + * returning '1' from on_headers_complete will tell the parser that it + * should not expect a body. This is used when receiving a response to a + * HEAD request which may contain 'Content-Length' or 'Transfer-Encoding: + * chunked' headers that indicate the presence of a body. + * + * http_data_cb does not return data chunks. It will be called arbitrarily + * many times for each string. E.G. you might get 10 callbacks for "on_url" + * each providing just a few characters more data. + */ +typedef int (*http_data_cb) (http_parser*, const char *at, size_t length); +typedef int (*http_cb) (http_parser*); + + +/* Request Methods */ +#define HTTP_METHOD_MAP(XX) \ + XX(0, DELETE, DELETE) \ + XX(1, GET, GET) \ + XX(2, HEAD, HEAD) \ + XX(3, POST, POST) \ + XX(4, PUT, PUT) \ + /* pathological */ \ + XX(5, CONNECT, CONNECT) \ + XX(6, OPTIONS, OPTIONS) \ + XX(7, TRACE, TRACE) \ + /* webdav */ \ + XX(8, COPY, COPY) \ + XX(9, LOCK, LOCK) \ + XX(10, MKCOL, MKCOL) \ + XX(11, MOVE, MOVE) \ + XX(12, PROPFIND, PROPFIND) \ + XX(13, PROPPATCH, PROPPATCH) \ + XX(14, SEARCH, SEARCH) \ + XX(15, UNLOCK, UNLOCK) \ + /* subversion */ \ + XX(16, REPORT, REPORT) \ + XX(17, MKACTIVITY, MKACTIVITY) \ + XX(18, CHECKOUT, CHECKOUT) \ + XX(19, MERGE, MERGE) \ + /* upnp */ \ + XX(20, MSEARCH, M-SEARCH) \ + XX(21, NOTIFY, NOTIFY) \ + XX(22, SUBSCRIBE, SUBSCRIBE) \ + XX(23, UNSUBSCRIBE, UNSUBSCRIBE) \ + /* RFC-5789 */ \ + XX(24, PATCH, PATCH) \ + XX(25, PURGE, PURGE) \ + /* CalDAV */ \ + XX(26, MKCALENDAR, MKCALENDAR) \ + +enum http_method + { +#define XX(num, name, string) HTTP_##name = num, + HTTP_METHOD_MAP(XX) +#undef XX + }; + + +enum http_parser_type { HTTP_REQUEST, HTTP_RESPONSE, HTTP_BOTH }; + + +/* Flag values for http_parser.flags field */ +enum flags + { F_CHUNKED = 1 << 0 + , F_CONNECTION_KEEP_ALIVE = 1 << 1 + , F_CONNECTION_CLOSE = 1 << 2 + , F_CONNECTION_UPGRADE = 1 << 3 + , F_TRAILING = 1 << 4 + , F_UPGRADE = 1 << 5 + , F_SKIPBODY = 1 << 6 + }; + + +/* Map for errno-related constants + * + * The provided argument should be a macro that takes 2 arguments. + */ +#define HTTP_ERRNO_MAP(XX) \ + /* No error */ \ + XX(OK, "success") \ + \ + /* Callback-related errors */ \ + XX(CB_message_begin, "the on_message_begin callback failed") \ + XX(CB_url, "the on_url callback failed") \ + XX(CB_header_field, "the on_header_field callback failed") \ + XX(CB_header_value, "the on_header_value callback failed") \ + XX(CB_headers_complete, "the on_headers_complete callback failed") \ + XX(CB_body, "the on_body callback failed") \ + XX(CB_message_complete, "the on_message_complete callback failed") \ + XX(CB_status, "the on_status callback failed") \ + \ + /* Parsing-related errors */ \ + XX(INVALID_EOF_STATE, "stream ended at an unexpected time") \ + XX(HEADER_OVERFLOW, \ + "too many header bytes seen; overflow detected") \ + XX(CLOSED_CONNECTION, \ + "data received after completed connection: close message") \ + XX(INVALID_VERSION, "invalid HTTP version") \ + XX(INVALID_STATUS, "invalid HTTP status code") \ + XX(INVALID_METHOD, "invalid HTTP method") \ + XX(INVALID_URL, "invalid URL") \ + XX(INVALID_HOST, "invalid host") \ + XX(INVALID_PORT, "invalid port") \ + XX(INVALID_PATH, "invalid path") \ + XX(INVALID_QUERY_STRING, "invalid query string") \ + XX(INVALID_FRAGMENT, "invalid fragment") \ + XX(LF_EXPECTED, "LF character expected") \ + XX(INVALID_HEADER_TOKEN, "invalid character in header") \ + XX(INVALID_CONTENT_LENGTH, \ + "invalid character in content-length header") \ + XX(INVALID_CHUNK_SIZE, \ + "invalid character in chunk size header") \ + XX(INVALID_CONSTANT, "invalid constant string") \ + XX(INVALID_INTERNAL_STATE, "encountered unexpected internal state")\ + XX(STRICT, "strict mode assertion failed") \ + XX(PAUSED, "parser is paused") \ + XX(UNKNOWN, "an unknown error occurred") + + +/* Define HPE_* values for each errno value above */ +#define HTTP_ERRNO_GEN(n, s) HPE_##n, +enum http_errno { + HTTP_ERRNO_MAP(HTTP_ERRNO_GEN) +}; +#undef HTTP_ERRNO_GEN + + +/* Get an http_errno value from an http_parser */ +#define HTTP_PARSER_ERRNO(p) ((enum http_errno) (p)->http_errno) + + +struct http_parser { + /** PRIVATE **/ + unsigned int type : 2; /* enum http_parser_type */ + unsigned int flags : 7; /* F_* values from 'flags' enum; semi-public */ + unsigned int state : 7; /* enum state from http_parser.c */ + unsigned int header_state : 8; /* enum header_state from http_parser.c */ + unsigned int index : 8; /* index into current matcher */ + + uint32_t nread; /* # bytes read in various scenarios */ + uint64_t content_length; /* # bytes in body (0 if no Content-Length header) */ + + /** READ-ONLY **/ + unsigned short http_major; + unsigned short http_minor; + unsigned int status_code : 16; /* responses only */ + unsigned int method : 8; /* requests only */ + unsigned int http_errno : 7; + + /* 1 = Upgrade header was present and the parser has exited because of that. + * 0 = No upgrade header present. + * Should be checked when http_parser_execute() returns in addition to + * error checking. + */ + unsigned int upgrade : 1; + + /** PUBLIC **/ + void *data; /* A pointer to get hook to the "connection" or "socket" object */ +}; + + +struct http_parser_settings { + http_cb on_message_begin; + http_data_cb on_url; + http_data_cb on_status; + http_data_cb on_header_field; + http_data_cb on_header_value; + http_cb on_headers_complete; + http_data_cb on_body; + http_cb on_message_complete; +}; + + +enum http_parser_url_fields + { UF_SCHEMA = 0 + , UF_HOST = 1 + , UF_PORT = 2 + , UF_PATH = 3 + , UF_QUERY = 4 + , UF_FRAGMENT = 5 + , UF_USERINFO = 6 + , UF_MAX = 7 + }; + + +/* Result structure for http_parser_parse_url(). + * + * Callers should index into field_data[] with UF_* values iff field_set + * has the relevant (1 << UF_*) bit set. As a courtesy to clients (and + * because we probably have padding left over), we convert any port to + * a uint16_t. + */ +struct http_parser_url { + uint16_t field_set; /* Bitmask of (1 << UF_*) values */ + uint16_t port; /* Converted UF_PORT string */ + + struct { + uint16_t off; /* Offset into buffer in which field starts */ + uint16_t len; /* Length of run in buffer */ + } field_data[UF_MAX]; +}; + + +/* Returns the library version. Bits 16-23 contain the major version number, + * bits 8-15 the minor version number and bits 0-7 the patch level. + * Usage example: + * + * unsigned long version = http_parser_version(); + * unsigned major = (version >> 16) & 255; + * unsigned minor = (version >> 8) & 255; + * unsigned patch = version & 255; + * printf("http_parser v%u.%u.%u\n", major, minor, patch); + */ +unsigned long http_parser_version(void); + +void http_parser_init(http_parser *parser, enum http_parser_type type); + + +/* Initialize http_parser_settings members to 0 + */ +void http_parser_settings_init(http_parser_settings *settings); + + +/* Executes the parser. Returns number of parsed bytes. Sets + * `parser->http_errno` on error. */ +size_t http_parser_execute(http_parser *parser, + const http_parser_settings *settings, + const char *data, + size_t len); + + +/* If http_should_keep_alive() in the on_headers_complete or + * on_message_complete callback returns 0, then this should be + * the last message on the connection. + * If you are the server, respond with the "Connection: close" header. + * If you are the client, close the connection. + */ +int http_should_keep_alive(const http_parser *parser); + +/* Returns a string version of the HTTP method. */ +const char *http_method_str(enum http_method m); + +/* Return a string name of the given error */ +const char *http_errno_name(enum http_errno err); + +/* Return a string description of the given error */ +const char *http_errno_description(enum http_errno err); + +/* Parse a URL; return nonzero on failure */ +int http_parser_parse_url(const char *buf, size_t buflen, + int is_connect, + struct http_parser_url *u); + +/* Pause or un-pause the parser; a nonzero value pauses */ +void http_parser_pause(http_parser *parser, int paused); + +/* Checks if this is the final chunk of the body. */ +int http_body_is_final(const http_parser *parser); + +#ifdef __cplusplus +} +#endif +#endif -- 2.7.4